From 437fd90c0250dee670290f9b714253671a990160 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jos=C3=A9=20Pekkarinen?= Date: Wed, 18 May 2016 13:18:31 +0300 Subject: [PATCH] These changes are the raw update to qemu-2.6. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Collission happened in the following patches: migration: do cleanup operation after completion(738df5b9) Bug fix.(1750c932f86) kvmclock: add a new function to update env->tsc.(b52baab2) The code provided by the patches was already in the upstreamed version. Change-Id: I3cc11841a6a76ae20887b2e245710199e1ea7f9a Signed-off-by: José Pekkarinen --- qemu/CODING_STYLE | 13 +- qemu/HACKING | 55 + qemu/MAINTAINERS | 380 +- qemu/Makefile | 86 +- qemu/Makefile.objs | 47 +- qemu/Makefile.target | 18 +- qemu/README | 108 +- qemu/VERSION | 2 +- qemu/accel.c | 1 + qemu/aio-posix.c | 212 +- qemu/aio-win32.c | 13 +- qemu/arch_init.c | 9 +- qemu/async.c | 25 +- qemu/audio/alsaaudio.c | 1 + qemu/audio/audio.c | 16 +- qemu/audio/audio.h | 1 - qemu/audio/audio_pt_int.c | 1 + qemu/audio/audio_win_int.c | 1 + qemu/audio/coreaudio.c | 316 +- qemu/audio/dsoundaudio.c | 1 + qemu/audio/mixeng.c | 1 + qemu/audio/noaudio.c | 9 +- qemu/audio/ossaudio.c | 3 +- qemu/audio/paaudio.c | 1 + qemu/audio/sdlaudio.c | 1 + qemu/audio/spiceaudio.c | 5 +- qemu/audio/wavaudio.c | 3 +- qemu/audio/wavcapture.c | 1 + qemu/backends/baum.c | 30 +- qemu/backends/hostmem-file.c | 19 +- qemu/backends/hostmem-ram.c | 2 + qemu/backends/hostmem.c | 34 +- qemu/backends/msmouse.c | 16 +- qemu/backends/rng-egd.c | 74 +- qemu/backends/rng-random.c | 45 +- qemu/backends/rng.c | 55 +- qemu/backends/testdev.c | 13 +- qemu/backends/tpm.c | 2 + qemu/balloon.c | 12 + qemu/block.c | 1954 +++-- qemu/block/Makefile.objs | 6 +- qemu/block/accounting.c | 124 +- qemu/block/archipelago.c | 4 +- qemu/block/backup.c | 182 +- qemu/block/blkdebug.c | 150 +- qemu/block/blkreplay.c | 160 + qemu/block/blkverify.c | 77 +- qemu/block/block-backend.c | 1089 ++- qemu/block/bochs.c | 10 +- qemu/block/cloop.c | 12 +- qemu/block/commit.c | 15 +- qemu/block/crypto.c | 586 ++ qemu/block/curl.c | 83 +- qemu/block/dirty-bitmap.c | 387 + qemu/block/dmg.c | 22 +- qemu/block/gluster.c | 159 +- qemu/block/io.c | 452 +- qemu/block/iscsi.c | 253 +- qemu/block/linux-aio.c | 6 +- qemu/block/mirror.c | 465 +- qemu/block/nbd-client.c | 113 +- qemu/block/nbd-client.h | 12 +- qemu/block/nbd.c | 203 +- qemu/block/nfs.c | 71 +- qemu/block/null.c | 44 + qemu/block/parallels.c | 72 +- qemu/block/qapi.c | 237 +- qemu/block/qcow.c | 90 +- qemu/block/qcow2-cache.c | 85 +- qemu/block/qcow2-cluster.c | 150 +- qemu/block/qcow2-refcount.c | 584 +- qemu/block/qcow2-snapshot.c | 54 +- qemu/block/qcow2.c | 1032 ++- qemu/block/qcow2.h | 41 +- qemu/block/qed-check.c | 1 + qemu/block/qed-cluster.c | 1 + qemu/block/qed-gencb.c | 1 + qemu/block/qed-l2-cache.c | 1 + qemu/block/qed-table.c | 5 +- qemu/block/qed.c | 102 +- qemu/block/qed.h | 1 + qemu/block/quorum.c | 138 +- qemu/block/raw-aio.h | 2 + qemu/block/raw-posix.c | 484 +- qemu/block/raw-win32.c | 12 +- qemu/block/raw_bsd.c | 69 +- qemu/block/rbd.c | 52 +- qemu/block/sheepdog.c | 393 +- qemu/block/snapshot.c | 169 +- qemu/block/ssh.c | 15 +- qemu/block/stream.c | 50 +- qemu/block/throttle-groups.c | 58 +- qemu/block/vdi.c | 47 +- qemu/block/vhdx-endian.c | 1 + qemu/block/vhdx-log.c | 40 +- qemu/block/vhdx.c | 85 +- qemu/block/vmdk.c | 358 +- qemu/block/vpc.c | 306 +- qemu/block/vvfat.c | 44 +- qemu/block/win32-aio.c | 6 +- qemu/block/write-threshold.c | 3 +- qemu/blockdev-nbd.c | 161 +- qemu/blockdev.c | 2161 ++++-- qemu/blockjob.c | 225 +- qemu/bootdevice.c | 14 +- qemu/bsd-user/bsdload.c | 8 +- qemu/bsd-user/elfload.c | 18 +- qemu/bsd-user/i386/syscall.h | 161 - qemu/bsd-user/i386/target_syscall.h | 165 + qemu/bsd-user/main.c | 30 +- qemu/bsd-user/mmap.c | 7 +- qemu/bsd-user/qemu.h | 9 +- qemu/bsd-user/signal.c | 10 +- qemu/bsd-user/sparc/syscall.h | 9 - qemu/bsd-user/sparc/target_syscall.h | 14 + qemu/bsd-user/sparc64/syscall.h | 10 - qemu/bsd-user/sparc64/target_syscall.h | 15 + qemu/bsd-user/strace.c | 6 +- qemu/bsd-user/syscall.c | 14 +- qemu/bsd-user/uaccess.c | 6 +- qemu/bsd-user/x86_64/syscall.h | 116 - qemu/bsd-user/x86_64/target_syscall.h | 121 + qemu/bt-host.c | 2 +- qemu/bt-vhci.c | 1 + qemu/configure | 1059 ++- qemu/contrib/ivshmem-client/Makefile.objs | 1 + qemu/contrib/ivshmem-client/ivshmem-client.c | 446 ++ qemu/contrib/ivshmem-client/ivshmem-client.h | 212 + qemu/contrib/ivshmem-client/main.c | 241 + qemu/contrib/ivshmem-server/Makefile.objs | 1 + qemu/contrib/ivshmem-server/ivshmem-server.c | 457 ++ qemu/contrib/ivshmem-server/ivshmem-server.h | 166 + qemu/contrib/ivshmem-server/main.c | 273 + qemu/coroutine-gthread.c | 198 - qemu/coroutine-sigaltstack.c | 293 - qemu/coroutine-ucontext.c | 194 - qemu/coroutine-win32.c | 101 - qemu/cpu-exec-common.c | 82 + qemu/cpu-exec.c | 280 +- qemu/cpus.c | 407 +- qemu/cputlb.c | 182 +- qemu/crypto/Makefile.objs | 35 +- qemu/crypto/aes.c | 1 + qemu/crypto/afsplit.c | 158 + qemu/crypto/block-luks.c | 1329 ++++ qemu/crypto/block-luks.h | 28 + qemu/crypto/block-qcow.c | 174 + qemu/crypto/block-qcow.h | 28 + qemu/crypto/block.c | 261 + qemu/crypto/blockpriv.h | 92 + qemu/crypto/cipher-builtin.c | 237 +- qemu/crypto/cipher-gcrypt.c | 217 +- qemu/crypto/cipher-nettle.c | 330 +- qemu/crypto/cipher.c | 103 +- qemu/crypto/desrfb.c | 1 + qemu/crypto/hash.c | 23 +- qemu/crypto/init.c | 28 +- qemu/crypto/ivgen-essiv.c | 120 + qemu/crypto/ivgen-essiv.h | 28 + qemu/crypto/ivgen-plain.c | 61 + qemu/crypto/ivgen-plain.h | 28 + qemu/crypto/ivgen-plain64.c | 61 + qemu/crypto/ivgen-plain64.h | 28 + qemu/crypto/ivgen.c | 101 + qemu/crypto/ivgenpriv.h | 49 + qemu/crypto/pbkdf-gcrypt.c | 69 + qemu/crypto/pbkdf-nettle.c | 66 + qemu/crypto/pbkdf-stub.c | 43 + qemu/crypto/pbkdf.c | 110 + qemu/crypto/random-gcrypt.c | 33 + qemu/crypto/random-gnutls.c | 43 + qemu/crypto/random-stub.c | 31 + qemu/crypto/secret.c | 509 ++ qemu/crypto/tlscreds.c | 259 + qemu/crypto/tlscredsanon.c | 219 + qemu/crypto/tlscredspriv.h | 42 + qemu/crypto/tlscredsx509.c | 865 +++ qemu/crypto/tlssession.c | 576 ++ qemu/crypto/xts.c | 230 + qemu/default-configs/aarch64-linux-user.mak | 2 - qemu/default-configs/arm-softmmu.mak | 10 + qemu/default-configs/i386-softmmu.mak | 11 +- qemu/default-configs/mips-softmmu-common.mak | 34 + qemu/default-configs/mips-softmmu.mak | 31 +- qemu/default-configs/mips64-softmmu.mak | 31 +- qemu/default-configs/mips64el-softmmu.mak | 31 +- qemu/default-configs/mipsel-softmmu.mak | 31 +- qemu/default-configs/pci.mak | 3 +- qemu/default-configs/ppc64-softmmu.mak | 2 + qemu/default-configs/tilegx-linux-user.mak | 1 + qemu/default-configs/x86_64-softmmu.mak | 10 +- qemu/device-hotplug.c | 12 +- qemu/device_tree.c | 190 +- qemu/disas.c | 80 +- qemu/disas/Makefile.objs | 5 +- qemu/disas/alpha.c | 2 +- qemu/disas/arm-a64.cc | 5 +- qemu/disas/arm.c | 3 +- qemu/disas/cris.c | 3 +- qemu/disas/hppa.c | 1 + qemu/disas/i386.c | 8 +- qemu/disas/ia64.c | 4 +- qemu/disas/libvixl/Makefile.objs | 13 +- qemu/disas/libvixl/README | 3 +- qemu/disas/libvixl/a64/assembler-a64.h | 2353 ------ qemu/disas/libvixl/a64/constants-a64.h | 1213 ---- qemu/disas/libvixl/a64/cpu-a64.h | 83 - qemu/disas/libvixl/a64/decoder-a64.cc | 707 -- qemu/disas/libvixl/a64/decoder-a64.h | 239 - qemu/disas/libvixl/a64/disasm-a64.cc | 1954 ----- qemu/disas/libvixl/a64/disasm-a64.h | 176 - qemu/disas/libvixl/a64/instructions-a64.cc | 314 - qemu/disas/libvixl/a64/instructions-a64.h | 384 - qemu/disas/libvixl/code-buffer.h | 113 - qemu/disas/libvixl/globals.h | 85 - qemu/disas/libvixl/platform.h | 37 - qemu/disas/libvixl/utils.cc | 151 - qemu/disas/libvixl/utils.h | 211 - qemu/disas/libvixl/vixl/a64/assembler-a64.h | 4624 ++++++++++++ qemu/disas/libvixl/vixl/a64/constants-a64.h | 2116 ++++++ qemu/disas/libvixl/vixl/a64/cpu-a64.h | 83 + qemu/disas/libvixl/vixl/a64/decoder-a64.cc | 877 +++ qemu/disas/libvixl/vixl/a64/decoder-a64.h | 275 + qemu/disas/libvixl/vixl/a64/disasm-a64.cc | 3491 +++++++++ qemu/disas/libvixl/vixl/a64/disasm-a64.h | 177 + qemu/disas/libvixl/vixl/a64/instructions-a64.cc | 622 ++ qemu/disas/libvixl/vixl/a64/instructions-a64.h | 757 ++ qemu/disas/libvixl/vixl/code-buffer.h | 113 + qemu/disas/libvixl/vixl/compiler-intrinsics.cc | 144 + qemu/disas/libvixl/vixl/compiler-intrinsics.h | 155 + qemu/disas/libvixl/vixl/globals.h | 151 + qemu/disas/libvixl/vixl/invalset.h | 775 ++ qemu/disas/libvixl/vixl/platform.h | 37 + qemu/disas/libvixl/vixl/utils.cc | 142 + qemu/disas/libvixl/vixl/utils.h | 286 + qemu/disas/lm32.c | 2 +- qemu/disas/m68k.c | 5 +- qemu/disas/microblaze.c | 156 +- qemu/disas/mips.c | 7 + qemu/disas/moxie.c | 2 +- qemu/disas/ppc.c | 1 + qemu/disas/s390.c | 5 +- qemu/disas/sh4.c | 2 +- qemu/disas/sparc.c | 5 +- qemu/disas/tci.c | 1 + qemu/dma-helpers.c | 1 + qemu/docs/bitmaps.md | 163 +- qemu/docs/blkdebug.txt | 7 +- qemu/docs/build-system.txt | 507 ++ qemu/docs/libcacard.txt | 483 -- qemu/docs/memory.txt | 45 +- qemu/docs/migration.txt | 191 + qemu/docs/multiseat.txt | 2 +- qemu/docs/pci_expander_bridge.txt | 8 +- qemu/docs/qapi-code-gen.txt | 826 ++- qemu/docs/qcow2-cache.txt | 164 + qemu/docs/qmp-events.txt | 717 ++ qemu/docs/{qmp/README => qmp-intro.txt} | 0 qemu/docs/qmp-spec.txt | 341 + qemu/docs/qmp/qmp-events.txt | 664 -- qemu/docs/qmp/qmp-spec.txt | 336 - qemu/docs/rcu.txt | 2 +- qemu/docs/replay.txt | 197 + qemu/docs/specs/fw_cfg.txt | 158 +- qemu/docs/specs/ivshmem-spec.txt | 254 + qemu/docs/specs/ivshmem_device_spec.txt | 96 - qemu/docs/specs/parallels.txt | 228 + qemu/docs/specs/pci-ids.txt | 24 +- qemu/docs/specs/ppc-spapr-hcalls.txt | 4 +- qemu/docs/specs/ppc-spapr-hotplug.txt | 48 + qemu/docs/specs/qcow2.txt | 223 +- qemu/docs/specs/rocker.txt | 2 +- qemu/docs/specs/vhost-user.txt | 210 +- qemu/docs/throttle.txt | 252 + qemu/docs/tracing.txt | 58 +- qemu/docs/virtio-migration.txt | 106 + qemu/docs/win32-qemu-event.promela | 98 + qemu/docs/writing-qmp-commands.txt | 30 +- qemu/dump.c | 350 +- qemu/exec.c | 1019 ++- qemu/fpu/softfloat-macros.h | 44 +- qemu/fpu/softfloat-specialize.h | 4 +- qemu/fpu/softfloat.c | 383 +- qemu/fsdev/9p-iov-marshal.c | 314 + qemu/fsdev/9p-iov-marshal.h | 18 + qemu/fsdev/9p-marshal.c | 51 + qemu/fsdev/9p-marshal.h | 84 + qemu/fsdev/Makefile.objs | 2 +- qemu/fsdev/file-op-9p.h | 3 - qemu/fsdev/qemu-fsdev-dummy.c | 3 +- qemu/fsdev/qemu-fsdev-opts.c | 3 +- qemu/fsdev/qemu-fsdev.c | 11 +- qemu/fsdev/virtfs-proxy-helper.c | 27 +- qemu/fsdev/virtfs-proxy-helper.texi | 6 +- qemu/fsdev/virtio-9p-marshal.c | 323 - qemu/fsdev/virtio-9p-marshal.h | 90 - qemu/gdb-xml/power-vsx.xml | 44 + qemu/gdb-xml/s390-cr.xml | 26 + qemu/gdb-xml/s390-virt.xml | 18 + qemu/gdbstub.c | 74 +- qemu/hmp-commands-info.hx | 809 +++ qemu/hmp-commands.hx | 182 +- qemu/hmp.c | 339 +- qemu/hmp.h | 4 + qemu/hw/9pfs/9p-handle.c | 709 ++ qemu/hw/9pfs/9p-local.c | 1282 ++++ qemu/hw/9pfs/9p-posix-acl.c | 184 + qemu/hw/9pfs/9p-proxy.c | 1220 ++++ qemu/hw/9pfs/9p-proxy.h | 95 + qemu/hw/9pfs/9p-synth.c | 574 ++ qemu/hw/9pfs/9p-synth.h | 51 + qemu/hw/9pfs/9p-xattr-user.c | 127 + qemu/hw/9pfs/9p-xattr.c | 164 + qemu/hw/9pfs/9p-xattr.h | 120 + qemu/hw/9pfs/9p.c | 3380 +++++++++ qemu/hw/9pfs/9p.h | 324 + qemu/hw/9pfs/Makefile.objs | 14 +- qemu/hw/9pfs/codir.c | 5 +- qemu/hw/9pfs/cofile.c | 5 +- qemu/hw/9pfs/cofs.c | 5 +- qemu/hw/9pfs/coth.c | 42 + qemu/hw/9pfs/coth.h | 99 + qemu/hw/9pfs/coxattr.c | 5 +- qemu/hw/9pfs/virtio-9p-coth.c | 82 - qemu/hw/9pfs/virtio-9p-coth.h | 107 - qemu/hw/9pfs/virtio-9p-device.c | 212 +- qemu/hw/9pfs/virtio-9p-handle.c | 708 -- qemu/hw/9pfs/virtio-9p-local.c | 1280 ---- qemu/hw/9pfs/virtio-9p-posix-acl.c | 185 - qemu/hw/9pfs/virtio-9p-proxy.c | 1219 ---- qemu/hw/9pfs/virtio-9p-proxy.h | 95 - qemu/hw/9pfs/virtio-9p-synth.c | 573 -- qemu/hw/9pfs/virtio-9p-synth.h | 54 - qemu/hw/9pfs/virtio-9p-xattr-user.c | 128 - qemu/hw/9pfs/virtio-9p-xattr.c | 164 - qemu/hw/9pfs/virtio-9p-xattr.h | 120 - qemu/hw/9pfs/virtio-9p.c | 3300 --------- qemu/hw/9pfs/virtio-9p.h | 391 +- qemu/hw/Makefile.objs | 2 + qemu/hw/acpi/Makefile.objs | 5 +- qemu/hw/acpi/acpi_interface.c | 1 + qemu/hw/acpi/aml-build.c | 470 +- qemu/hw/acpi/bios-linker-loader.c | 97 +- qemu/hw/acpi/core.c | 45 +- qemu/hw/acpi/cpu_hotplug.c | 3 + qemu/hw/acpi/cpu_hotplug_acpi_table.c | 136 + qemu/hw/acpi/ich9.c | 59 +- qemu/hw/acpi/memory_hotplug.c | 16 +- qemu/hw/acpi/memory_hotplug_acpi_table.c | 262 + qemu/hw/acpi/nvdimm.c | 706 ++ qemu/hw/acpi/pcihp.c | 2 + qemu/hw/acpi/piix4.c | 2 + qemu/hw/acpi/tco.c | 1 + qemu/hw/alpha/dp264.c | 36 +- qemu/hw/alpha/pci.c | 7 +- qemu/hw/alpha/typhoon.c | 5 +- qemu/hw/arm/Makefile.objs | 6 +- qemu/hw/arm/allwinner-a10.c | 21 + qemu/hw/arm/armv7m.c | 17 +- qemu/hw/arm/ast2400.c | 140 + qemu/hw/arm/bcm2835_peripherals.c | 312 + qemu/hw/arm/bcm2836.c | 184 + qemu/hw/arm/boot.c | 235 +- qemu/hw/arm/collie.c | 14 +- qemu/hw/arm/cubieboard.c | 27 +- qemu/hw/arm/digic.c | 8 + qemu/hw/arm/digic_boards.c | 20 +- qemu/hw/arm/exynos4210.c | 25 +- qemu/hw/arm/exynos4_boards.c | 59 +- qemu/hw/arm/fsl-imx25.c | 313 + qemu/hw/arm/fsl-imx31.c | 287 + qemu/hw/arm/gumstix.c | 39 +- qemu/hw/arm/highbank.c | 121 +- qemu/hw/arm/imx25_pdk.c | 153 + qemu/hw/arm/integratorcp.c | 32 +- qemu/hw/arm/kzm.c | 218 +- qemu/hw/arm/mainstone.c | 17 +- qemu/hw/arm/musicpal.c | 19 +- qemu/hw/arm/netduino2.c | 22 +- qemu/hw/arm/nseries.c | 52 +- qemu/hw/arm/omap1.c | 55 +- qemu/hw/arm/omap2.c | 21 +- qemu/hw/arm/omap_sx1.c | 44 +- qemu/hw/arm/palm.c | 17 +- qemu/hw/arm/palmetto-bmc.c | 68 + qemu/hw/arm/pxa2xx.c | 28 +- qemu/hw/arm/pxa2xx_gpio.c | 1 + qemu/hw/arm/pxa2xx_pic.c | 3 + qemu/hw/arm/raspi.c | 172 + qemu/hw/arm/realview.c | 114 +- qemu/hw/arm/spitz.c | 87 +- qemu/hw/arm/stellaris.c | 91 +- qemu/hw/arm/stm32f205_soc.c | 23 +- qemu/hw/arm/strongarm.c | 9 +- qemu/hw/arm/sysbus-fdt.c | 323 +- qemu/hw/arm/tosa.c | 19 +- qemu/hw/arm/versatilepb.c | 59 +- qemu/hw/arm/vexpress.c | 36 +- qemu/hw/arm/virt-acpi-build.c | 216 +- qemu/hw/arm/virt.c | 680 +- qemu/hw/arm/xilinx_zynq.c | 120 +- qemu/hw/arm/xlnx-ep108.c | 93 +- qemu/hw/arm/xlnx-zynqmp.c | 173 +- qemu/hw/arm/z2.c | 16 +- qemu/hw/audio/ac97.c | 1 + qemu/hw/audio/adlib.c | 13 +- qemu/hw/audio/cs4231.c | 1 + qemu/hw/audio/cs4231a.c | 24 +- qemu/hw/audio/es1370.c | 28 +- qemu/hw/audio/fmopl.c | 7 +- qemu/hw/audio/gus.c | 31 +- qemu/hw/audio/gusemu.h | 1 - qemu/hw/audio/gusemu_hal.c | 1 + qemu/hw/audio/gusemu_mixer.c | 1 + qemu/hw/audio/hda-codec.c | 1 + qemu/hw/audio/intel-hda.c | 1 + qemu/hw/audio/lm4549.c | 2 +- qemu/hw/audio/marvell_88w8618.c | 1 + qemu/hw/audio/milkymist-ac97.c | 1 + qemu/hw/audio/pcspk.c | 1 + qemu/hw/audio/pl041.c | 1 + qemu/hw/audio/sb16.c | 43 +- qemu/hw/audio/wm8750.c | 1 + qemu/hw/block/block.c | 2 + qemu/hw/block/cdrom.c | 1 + qemu/hw/block/dataplane/virtio-blk.c | 237 +- qemu/hw/block/dataplane/virtio-blk.h | 1 + qemu/hw/block/ecc.c | 1 + qemu/hw/block/fdc.c | 467 +- qemu/hw/block/hd-geometry.c | 1 + qemu/hw/block/m25p80.c | 334 +- qemu/hw/block/nand.c | 14 +- qemu/hw/block/nvme.c | 55 +- qemu/hw/block/nvme.h | 1 + qemu/hw/block/onenand.c | 4 +- qemu/hw/block/pflash_cfi01.c | 20 +- qemu/hw/block/pflash_cfi02.c | 10 +- qemu/hw/block/tc58128.c | 1 + qemu/hw/block/virtio-blk.c | 158 +- qemu/hw/block/xen_blkif.h | 12 +- qemu/hw/block/xen_disk.c | 101 +- qemu/hw/bt/core.c | 1 + qemu/hw/bt/hci-csr.c | 5 +- qemu/hw/bt/hci.c | 23 +- qemu/hw/bt/hid.c | 1 + qemu/hw/bt/l2cap.c | 1 + qemu/hw/bt/sdp.c | 32 +- qemu/hw/char/Makefile.objs | 1 + qemu/hw/char/bcm2835_aux.c | 316 + qemu/hw/char/cadence_uart.c | 8 +- qemu/hw/char/debugcon.c | 2 + qemu/hw/char/digic-uart.c | 1 + qemu/hw/char/escc.c | 18 +- qemu/hw/char/etraxfs_ser.c | 7 +- qemu/hw/char/exynos4210_uart.c | 16 +- qemu/hw/char/grlib_apbuart.c | 1 + qemu/hw/char/imx_serial.c | 235 +- qemu/hw/char/ipoctal232.c | 1 + qemu/hw/char/lm32_juart.c | 1 + qemu/hw/char/lm32_uart.c | 1 + qemu/hw/char/mcf_uart.c | 3 +- qemu/hw/char/milkymist-uart.c | 1 + qemu/hw/char/omap_uart.c | 4 +- qemu/hw/char/parallel.c | 2 + qemu/hw/char/pl011.c | 1 + qemu/hw/char/sclpconsole-lm.c | 1 + qemu/hw/char/sclpconsole.c | 1 + qemu/hw/char/serial-isa.c | 2 + qemu/hw/char/serial-pci.c | 2 + qemu/hw/char/serial.c | 26 +- qemu/hw/char/sh_serial.c | 1 + qemu/hw/char/spapr_vty.c | 4 + qemu/hw/char/stm32f2xx_usart.c | 1 + qemu/hw/char/virtio-console.c | 1 + qemu/hw/char/virtio-serial-bus.c | 87 +- qemu/hw/char/xen_console.c | 26 +- qemu/hw/char/xilinx_uartlite.c | 1 + qemu/hw/core/empty_slot.c | 1 + qemu/hw/core/fw-path-provider.c | 1 + qemu/hw/core/hotplug.c | 1 + qemu/hw/core/irq.c | 1 + qemu/hw/core/loader.c | 131 +- qemu/hw/core/machine.c | 131 +- qemu/hw/core/nmi.c | 12 +- qemu/hw/core/null-machine.c | 17 +- qemu/hw/core/platform-bus.c | 26 +- qemu/hw/core/ptimer.c | 4 +- qemu/hw/core/qdev-properties-system.c | 58 +- qemu/hw/core/qdev-properties.c | 214 +- qemu/hw/core/qdev.c | 53 +- qemu/hw/core/stream.c | 1 + qemu/hw/core/sysbus.c | 1 + qemu/hw/cpu/Makefile.objs | 1 - qemu/hw/cpu/a15mpcore.c | 46 +- qemu/hw/cpu/a9mpcore.c | 13 + qemu/hw/cpu/arm11mpcore.c | 2 + qemu/hw/cpu/icc_bus.c | 118 - qemu/hw/cpu/realview_mpcore.c | 2 + qemu/hw/cris/axis_dev88.c | 23 +- qemu/hw/cris/boot.c | 6 +- qemu/hw/display/Makefile.objs | 7 +- qemu/hw/display/ads7846.c | 3 +- qemu/hw/display/bcm2835_fb.c | 425 ++ qemu/hw/display/blizzard.c | 1 + qemu/hw/display/cg3.c | 10 +- qemu/hw/display/cirrus_vga.c | 6 +- qemu/hw/display/exynos4210_fimd.c | 5 +- qemu/hw/display/framebuffer.c | 1 + qemu/hw/display/g364fb.c | 1 + qemu/hw/display/jazz_led.c | 7 +- qemu/hw/display/milkymist-tmu2.c | 5 +- qemu/hw/display/milkymist-vgafb.c | 1 + qemu/hw/display/omap_dss.c | 4 +- qemu/hw/display/omap_lcd_template.h | 4 +- qemu/hw/display/omap_lcdc.c | 4 +- qemu/hw/display/pl110.c | 1 + qemu/hw/display/pxa2xx_lcd.c | 9 +- qemu/hw/display/qxl-logger.c | 1 + qemu/hw/display/qxl-render.c | 3 +- qemu/hw/display/qxl.c | 12 +- qemu/hw/display/sm501.c | 7 +- qemu/hw/display/ssd0303.c | 1 + qemu/hw/display/ssd0323.c | 3 +- qemu/hw/display/tc6393xb.c | 4 +- qemu/hw/display/tcx.c | 33 +- qemu/hw/display/vga-isa-mm.c | 1 + qemu/hw/display/vga-isa.c | 1 + qemu/hw/display/vga-pci.c | 1 + qemu/hw/display/vga.c | 135 +- qemu/hw/display/vga_int.h | 1 - qemu/hw/display/virtio-gpu-3d.c | 606 ++ qemu/hw/display/virtio-gpu-pci.c | 5 +- qemu/hw/display/virtio-gpu.c | 226 +- qemu/hw/display/virtio-vga.c | 11 + qemu/hw/display/vmware_vga.c | 15 +- qemu/hw/display/xenfb.c | 58 +- qemu/hw/dma/Makefile.objs | 1 + qemu/hw/dma/bcm2835_dma.c | 409 ++ qemu/hw/dma/etraxfs_dma.c | 16 +- qemu/hw/dma/i82374.c | 62 +- qemu/hw/dma/i8257.c | 415 +- qemu/hw/dma/omap_dma.c | 7 +- qemu/hw/dma/pl080.c | 1 + qemu/hw/dma/pl330.c | 2 + qemu/hw/dma/puv3_dma.c | 1 + qemu/hw/dma/pxa2xx_dma.c | 4 +- qemu/hw/dma/rc4030.c | 3 +- qemu/hw/dma/soc_dma.c | 40 +- qemu/hw/dma/sparc32_dma.c | 1 + qemu/hw/dma/sun4m_iommu.c | 1 + qemu/hw/dma/xilinx_axidma.c | 14 +- qemu/hw/gpio/Makefile.objs | 2 + qemu/hw/gpio/gpio_key.c | 104 + qemu/hw/gpio/imx_gpio.c | 350 + qemu/hw/gpio/max7310.c | 1 + qemu/hw/gpio/mpc8xxx.c | 1 + qemu/hw/gpio/omap_gpio.c | 34 +- qemu/hw/gpio/pl061.c | 68 +- qemu/hw/gpio/puv3_gpio.c | 1 + qemu/hw/gpio/zaurus.c | 6 +- qemu/hw/i2c/Makefile.objs | 1 + qemu/hw/i2c/bitbang_i2c.c | 1 + qemu/hw/i2c/core.c | 1 + qemu/hw/i2c/exynos4210_i2c.c | 1 + qemu/hw/i2c/imx_i2c.c | 337 + qemu/hw/i2c/omap_i2c.c | 9 +- qemu/hw/i2c/pm_smbus.c | 1 + qemu/hw/i2c/smbus.c | 1 + qemu/hw/i2c/smbus_eeprom.c | 1 + qemu/hw/i2c/smbus_ich9.c | 1 + qemu/hw/i2c/versatile_i2c.c | 1 + qemu/hw/i386/Makefile.objs | 27 +- qemu/hw/i386/acpi-build.c | 1841 +++-- qemu/hw/i386/acpi-build.h | 4 +- qemu/hw/i386/acpi-dsdt-cpu-hotplug.dsl | 90 - qemu/hw/i386/acpi-dsdt-dbug.dsl | 41 - qemu/hw/i386/acpi-dsdt-hpet.dsl | 48 - qemu/hw/i386/acpi-dsdt-isa.dsl | 117 - qemu/hw/i386/acpi-dsdt-mem-hotplug.dsl | 171 - qemu/hw/i386/acpi-dsdt.dsl | 304 - qemu/hw/i386/acpi-dsdt.hex.generated | 2972 -------- qemu/hw/i386/intel_iommu.c | 166 +- qemu/hw/i386/intel_iommu_internal.h | 6 +- qemu/hw/i386/kvm/apic.c | 3 +- qemu/hw/i386/kvm/clock.c | 1 + qemu/hw/i386/kvm/i8254.c | 2 + qemu/hw/i386/kvm/i8259.c | 1 + qemu/hw/i386/kvm/ioapic.c | 11 + qemu/hw/i386/kvm/pci-assign.c | 116 +- qemu/hw/i386/kvmvapic.c | 16 +- qemu/hw/i386/multiboot.c | 6 +- qemu/hw/i386/pc.c | 580 +- qemu/hw/i386/pc_piix.c | 449 +- qemu/hw/i386/pc_q35.c | 304 +- qemu/hw/i386/pc_sysfw.c | 6 +- qemu/hw/i386/pci-assign-load-rom.c | 85 + qemu/hw/i386/q35-acpi-dsdt.dsl | 435 -- qemu/hw/i386/q35-acpi-dsdt.hex.generated | 7610 -------------------- qemu/hw/i386/smbios.c | 1102 --- qemu/hw/i386/xen/xen_apic.c | 7 +- qemu/hw/i386/xen/xen_platform.c | 17 +- qemu/hw/i386/xen/xen_pvdevice.c | 14 +- qemu/hw/ide/ahci.c | 381 +- qemu/hw/ide/ahci.h | 35 +- qemu/hw/ide/atapi.c | 222 +- qemu/hw/ide/cmd646.c | 2 + qemu/hw/ide/core.c | 364 +- qemu/hw/ide/ich.c | 11 +- qemu/hw/ide/internal.h | 54 +- qemu/hw/ide/isa.c | 1 + qemu/hw/ide/macio.c | 43 +- qemu/hw/ide/microdrive.c | 1 + qemu/hw/ide/mmio.c | 1 + qemu/hw/ide/pci.c | 29 +- qemu/hw/ide/piix.c | 16 +- qemu/hw/ide/qdev.c | 19 +- qemu/hw/ide/via.c | 1 + qemu/hw/input/Makefile.objs | 2 +- qemu/hw/input/adb.c | 21 +- qemu/hw/input/hid.c | 45 +- qemu/hw/input/lm832x.c | 1 + qemu/hw/input/milkymist-softusb.c | 6 +- qemu/hw/input/pckbd.c | 1 + qemu/hw/input/pl050.c | 1 + qemu/hw/input/ps2.c | 34 +- qemu/hw/input/pxa2xx_keypad.c | 1 + qemu/hw/input/stellaris_input.c | 7 +- qemu/hw/input/tsc2005.c | 4 +- qemu/hw/input/tsc210x.c | 12 +- qemu/hw/input/virtio-input-hid.c | 49 +- qemu/hw/input/virtio-input-host.c | 72 +- qemu/hw/input/virtio-input.c | 76 +- qemu/hw/input/vmmouse.c | 1 + qemu/hw/intc/Makefile.objs | 4 + qemu/hw/intc/allwinner-a10-pic.c | 1 + qemu/hw/intc/apic.c | 25 +- qemu/hw/intc/apic_common.c | 13 +- qemu/hw/intc/arm_gic.c | 393 +- qemu/hw/intc/arm_gic_common.c | 102 +- qemu/hw/intc/arm_gic_kvm.c | 148 +- qemu/hw/intc/arm_gicv2m.c | 4 +- qemu/hw/intc/arm_gicv3_common.c | 142 + qemu/hw/intc/arm_gicv3_kvm.c | 151 + qemu/hw/intc/armv7m_nvic.c | 25 +- qemu/hw/intc/aspeed_vic.c | 339 + qemu/hw/intc/bcm2835_ic.c | 237 + qemu/hw/intc/bcm2836_control.c | 304 + qemu/hw/intc/etraxfs_pic.c | 1 + qemu/hw/intc/exynos4210_combiner.c | 1 + qemu/hw/intc/exynos4210_gic.c | 1 + qemu/hw/intc/grlib_irqmp.c | 1 + qemu/hw/intc/heathrow_pic.c | 1 + qemu/hw/intc/i8259.c | 3 +- qemu/hw/intc/i8259_common.c | 1 + qemu/hw/intc/imx_avic.c | 89 +- qemu/hw/intc/ioapic.c | 89 +- qemu/hw/intc/ioapic_common.c | 57 + qemu/hw/intc/lm32_pic.c | 2 +- qemu/hw/intc/omap_intc.c | 11 +- qemu/hw/intc/openpic.c | 5 +- qemu/hw/intc/openpic_kvm.c | 5 +- qemu/hw/intc/pl190.c | 1 + qemu/hw/intc/puv3_intc.c | 1 + qemu/hw/intc/realview_gic.c | 2 + qemu/hw/intc/s390_flic.c | 1 + qemu/hw/intc/s390_flic_kvm.c | 3 + qemu/hw/intc/sh_intc.c | 3 + qemu/hw/intc/slavio_intctl.c | 1 + qemu/hw/intc/vgic_common.h | 35 + qemu/hw/intc/xics.c | 41 +- qemu/hw/intc/xics_kvm.c | 4 + qemu/hw/intc/xilinx_intc.c | 1 + qemu/hw/ipack/ipack.c | 2 + qemu/hw/ipack/tpci200.c | 2 +- qemu/hw/ipmi/Makefile.objs | 5 + qemu/hw/ipmi/ipmi.c | 151 + qemu/hw/ipmi/ipmi_bmc_extern.c | 519 ++ qemu/hw/ipmi/ipmi_bmc_sim.c | 1810 +++++ qemu/hw/ipmi/isa_ipmi_bt.c | 530 ++ qemu/hw/ipmi/isa_ipmi_kcs.c | 495 ++ qemu/hw/isa/apm.c | 1 + qemu/hw/isa/i82378.c | 9 +- qemu/hw/isa/isa-bus.c | 38 +- qemu/hw/isa/lpc_ich9.c | 61 +- qemu/hw/isa/pc87312.c | 10 +- qemu/hw/isa/piix4.c | 7 +- qemu/hw/isa/vt82c686.c | 6 +- qemu/hw/lm32/lm32_boards.c | 49 +- qemu/hw/lm32/lm32_hwsetup.h | 1 + qemu/hw/lm32/milkymist-hw.h | 3 +- qemu/hw/lm32/milkymist.c | 21 +- qemu/hw/m68k/an5206.c | 25 +- qemu/hw/m68k/dummy_m68k.c | 18 +- qemu/hw/m68k/mcf5206.c | 3 + qemu/hw/m68k/mcf5208.c | 23 +- qemu/hw/m68k/mcf_intc.c | 3 + qemu/hw/mem/Makefile.objs | 1 + qemu/hw/mem/nvdimm.c | 47 + qemu/hw/mem/pc-dimm.c | 61 +- qemu/hw/microblaze/boot.c | 8 +- qemu/hw/microblaze/petalogix_ml605_mmu.c | 26 +- qemu/hw/microblaze/petalogix_s3adsp1800_mmu.c | 24 +- qemu/hw/mips/Makefile.objs | 1 + qemu/hw/mips/addr.c | 1 + qemu/hw/mips/cps.c | 180 + qemu/hw/mips/cputimer.c | 34 +- qemu/hw/mips/gt64xxx_pci.c | 10 +- qemu/hw/mips/mips_fulong2e.c | 32 +- qemu/hw/mips/mips_int.c | 1 + qemu/hw/mips/mips_jazz.c | 66 +- qemu/hw/mips/mips_malta.c | 152 +- qemu/hw/mips/mips_mipssim.c | 21 +- qemu/hw/mips/mips_r4k.c | 27 +- qemu/hw/misc/Makefile.objs | 10 + qemu/hw/misc/a9scu.c | 1 + qemu/hw/misc/applesmc.c | 1 + qemu/hw/misc/arm11scu.c | 1 + qemu/hw/misc/arm_integrator_debug.c | 3 +- qemu/hw/misc/arm_l2x0.c | 1 + qemu/hw/misc/arm_sysctl.c | 4 +- qemu/hw/misc/bcm2835_mbox.c | 335 + qemu/hw/misc/bcm2835_property.c | 424 ++ qemu/hw/misc/cbus.c | 3 +- qemu/hw/misc/debugexit.c | 1 + qemu/hw/misc/eccmemctl.c | 1 + qemu/hw/misc/edu.c | 13 +- qemu/hw/misc/exynos4210_pmu.c | 1 + qemu/hw/misc/hyperv_testdev.c | 168 + qemu/hw/misc/imx25_ccm.c | 317 + qemu/hw/misc/imx31_ccm.c | 344 + qemu/hw/misc/imx6_ccm.c | 774 ++ qemu/hw/misc/imx_ccm.c | 309 +- qemu/hw/misc/ivshmem.c | 1334 ++-- qemu/hw/misc/macio/cuda.c | 480 +- qemu/hw/misc/macio/mac_dbdma.c | 78 +- qemu/hw/misc/macio/macio.c | 19 +- qemu/hw/misc/max111x.c | 3 +- qemu/hw/misc/milkymist-hpdmc.c | 1 + qemu/hw/misc/milkymist-pfpu.c | 1 + qemu/hw/misc/mips_cmgcr.c | 160 + qemu/hw/misc/mips_cpc.c | 177 + qemu/hw/misc/mips_itu.c | 521 ++ qemu/hw/misc/mst_fpga.c | 1 + qemu/hw/misc/omap_clk.c | 3 +- qemu/hw/misc/omap_gpmc.c | 4 +- qemu/hw/misc/omap_l4.c | 1 + qemu/hw/misc/omap_sdrc.c | 4 +- qemu/hw/misc/omap_tap.c | 1 + qemu/hw/misc/pc-testdev.c | 2 +- qemu/hw/misc/pci-testdev.c | 9 +- qemu/hw/misc/puv3_pm.c | 1 + qemu/hw/misc/pvpanic.c | 4 +- qemu/hw/misc/sga.c | 1 + qemu/hw/misc/slavio_misc.c | 1 + qemu/hw/misc/stm32f2xx_syscfg.c | 1 + qemu/hw/misc/tmp105.c | 14 +- qemu/hw/misc/vmport.c | 1 + qemu/hw/misc/zynq-xadc.c | 303 + qemu/hw/misc/zynq_slcr.c | 9 +- qemu/hw/moxie/moxiesim.c | 28 +- qemu/hw/net/Makefile.objs | 1 + qemu/hw/net/allwinner_emac.c | 1 + qemu/hw/net/cadence_gem.c | 21 +- qemu/hw/net/dp8393x.c | 4 +- qemu/hw/net/e1000.c | 499 +- qemu/hw/net/e1000_regs.h | 8 +- qemu/hw/net/eepro100.c | 18 +- qemu/hw/net/etraxfs_eth.c | 6 +- qemu/hw/net/fsl_etsec/etsec.c | 3 +- qemu/hw/net/fsl_etsec/miim.c | 1 + qemu/hw/net/fsl_etsec/registers.c | 1 + qemu/hw/net/fsl_etsec/registers.h | 1 - qemu/hw/net/fsl_etsec/rings.c | 5 +- qemu/hw/net/imx_fec.c | 711 ++ qemu/hw/net/lan9118.c | 21 +- qemu/hw/net/lance.c | 1 + qemu/hw/net/mcf_fec.c | 1 + qemu/hw/net/milkymist-minimac2.c | 6 +- qemu/hw/net/mipsnet.c | 1 + qemu/hw/net/ne2000-isa.c | 17 +- qemu/hw/net/ne2000.c | 25 +- qemu/hw/net/ne2000.h | 1 - qemu/hw/net/opencores_eth.c | 3 +- qemu/hw/net/pcnet-pci.c | 1 + qemu/hw/net/pcnet.c | 18 +- qemu/hw/net/rocker/qmp-norocker.c | 1 + qemu/hw/net/rocker/rocker.c | 59 +- qemu/hw/net/rocker/rocker_desc.c | 13 +- qemu/hw/net/rocker/rocker_fp.c | 8 +- qemu/hw/net/rocker/rocker_fp.h | 1 + qemu/hw/net/rocker/rocker_of_dpa.c | 23 +- qemu/hw/net/rocker/rocker_world.c | 8 +- qemu/hw/net/rocker/rocker_world.h | 1 + qemu/hw/net/rtl8139.c | 135 +- qemu/hw/net/smc91c111.c | 34 +- qemu/hw/net/spapr_llan.c | 323 +- qemu/hw/net/stellaris_enet.c | 13 +- qemu/hw/net/vhost_net.c | 140 +- qemu/hw/net/virtio-net.c | 223 +- qemu/hw/net/vmware_utils.h | 7 +- qemu/hw/net/vmxnet3.c | 205 +- qemu/hw/net/vmxnet3.h | 6 +- qemu/hw/net/vmxnet_debug.h | 139 +- qemu/hw/net/vmxnet_rx_pkt.c | 1 + qemu/hw/net/vmxnet_rx_pkt.h | 2 - qemu/hw/net/vmxnet_tx_pkt.c | 20 +- qemu/hw/net/vmxnet_tx_pkt.h | 2 - qemu/hw/net/xen_nic.c | 30 +- qemu/hw/net/xgmac.c | 1 + qemu/hw/net/xilinx_axienet.c | 2 + qemu/hw/net/xilinx_ethlite.c | 3 + qemu/hw/nvram/ds1225y.c | 1 + qemu/hw/nvram/eeprom93xx.c | 1 + qemu/hw/nvram/fw_cfg.c | 456 +- qemu/hw/nvram/mac_nvram.c | 9 +- qemu/hw/nvram/spapr_nvram.c | 4 + qemu/hw/openrisc/cputimer.c | 8 +- qemu/hw/openrisc/openrisc_sim.c | 26 +- qemu/hw/openrisc/pic_cpu.c | 1 + qemu/hw/pci-bridge/dec.c | 7 +- qemu/hw/pci-bridge/i82801b11.c | 15 +- qemu/hw/pci-bridge/ioh3420.c | 9 +- qemu/hw/pci-bridge/pci_bridge_dev.c | 12 +- qemu/hw/pci-bridge/pci_expander_bridge.c | 111 +- qemu/hw/pci-bridge/xio3130_downstream.c | 9 +- qemu/hw/pci-bridge/xio3130_upstream.c | 9 +- qemu/hw/pci-host/apb.c | 8 +- qemu/hw/pci-host/bonito.c | 41 +- qemu/hw/pci-host/gpex.c | 1 + qemu/hw/pci-host/grackle.c | 3 + qemu/hw/pci-host/pam.c | 1 + qemu/hw/pci-host/piix.c | 137 +- qemu/hw/pci-host/ppce500.c | 5 +- qemu/hw/pci-host/prep.c | 16 +- qemu/hw/pci-host/q35.c | 52 +- qemu/hw/pci-host/uninorth.c | 22 +- qemu/hw/pci-host/versatile.c | 12 + qemu/hw/pci/msi.c | 31 +- qemu/hw/pci/msix.c | 25 +- qemu/hw/pci/pci-stub.c | 1 + qemu/hw/pci/pci.c | 97 +- qemu/hw/pci/pci_bridge.c | 4 +- qemu/hw/pci/pci_host.c | 16 + qemu/hw/pci/pcie.c | 55 +- qemu/hw/pci/pcie_aer.c | 13 +- qemu/hw/pci/pcie_host.c | 1 + qemu/hw/pci/pcie_port.c | 1 + qemu/hw/pci/shpc.c | 4 +- qemu/hw/pci/slotid_cap.c | 1 + qemu/hw/pcmcia/pcmcia.c | 1 + qemu/hw/pcmcia/pxa2xx.c | 7 +- qemu/hw/ppc/Makefile.objs | 2 +- qemu/hw/ppc/e500.c | 13 +- qemu/hw/ppc/e500plat.c | 19 +- qemu/hw/ppc/mac.h | 6 +- qemu/hw/ppc/mac_newworld.c | 27 +- qemu/hw/ppc/mac_oldworld.c | 50 +- qemu/hw/ppc/mpc8544_guts.c | 3 + qemu/hw/ppc/mpc8544ds.c | 17 +- qemu/hw/ppc/ppc.c | 28 +- qemu/hw/ppc/ppc405_boards.c | 101 +- qemu/hw/ppc/ppc405_uc.c | 10 +- qemu/hw/ppc/ppc440_bamboo.c | 21 +- qemu/hw/ppc/ppc4xx_devs.c | 1 + qemu/hw/ppc/ppc4xx_pci.c | 1 + qemu/hw/ppc/ppc_booke.c | 5 +- qemu/hw/ppc/ppce500_spin.c | 1 + qemu/hw/ppc/prep.c | 73 +- qemu/hw/ppc/spapr.c | 1087 ++- qemu/hw/ppc/spapr_drc.c | 184 +- qemu/hw/ppc/spapr_events.c | 69 +- qemu/hw/ppc/spapr_hcall.c | 335 +- qemu/hw/ppc/spapr_iommu.c | 39 +- qemu/hw/ppc/spapr_pci.c | 264 +- qemu/hw/ppc/spapr_pci_vfio.c | 137 +- qemu/hw/ppc/spapr_rng.c | 191 + qemu/hw/ppc/spapr_rtas.c | 119 +- qemu/hw/ppc/spapr_rtc.c | 3 +- qemu/hw/ppc/spapr_vio.c | 21 +- qemu/hw/ppc/virtex_ml507.c | 17 +- qemu/hw/s390x/Makefile.objs | 4 +- qemu/hw/s390x/css.c | 282 +- qemu/hw/s390x/css.h | 15 + qemu/hw/s390x/event-facility.c | 55 +- qemu/hw/s390x/ipl.c | 129 +- qemu/hw/s390x/ipl.h | 30 +- qemu/hw/s390x/s390-pci-bus.c | 49 +- qemu/hw/s390x/s390-pci-bus.h | 5 + qemu/hw/s390x/s390-pci-inst.c | 53 +- qemu/hw/s390x/s390-skeys-kvm.c | 76 + qemu/hw/s390x/s390-skeys.c | 415 ++ qemu/hw/s390x/s390-virtio-bus.c | 763 -- qemu/hw/s390x/s390-virtio-bus.h | 186 - qemu/hw/s390x/s390-virtio-ccw.c | 286 +- qemu/hw/s390x/s390-virtio-hcall.c | 1 + qemu/hw/s390x/s390-virtio.c | 238 +- qemu/hw/s390x/s390-virtio.h | 4 +- qemu/hw/s390x/sclp.c | 273 +- qemu/hw/s390x/sclpcpu.c | 29 +- qemu/hw/s390x/sclpquiesce.c | 5 +- qemu/hw/s390x/virtio-ccw.c | 144 +- qemu/hw/s390x/virtio-ccw.h | 19 +- qemu/hw/scsi/Makefile.objs | 1 + qemu/hw/scsi/esp-pci.c | 2 + qemu/hw/scsi/esp.c | 2 + qemu/hw/scsi/lsi53c895a.c | 2 +- qemu/hw/scsi/megasas.c | 19 +- qemu/hw/scsi/mpi.h | 1153 +++ qemu/hw/scsi/mptconfig.c | 905 +++ qemu/hw/scsi/mptendian.c | 204 + qemu/hw/scsi/mptsas.c | 1442 ++++ qemu/hw/scsi/mptsas.h | 100 + qemu/hw/scsi/scsi-bus.c | 31 +- qemu/hw/scsi/scsi-disk.c | 166 +- qemu/hw/scsi/scsi-generic.c | 178 +- qemu/hw/scsi/spapr_vscsi.c | 4 +- qemu/hw/scsi/vhost-scsi.c | 23 +- qemu/hw/scsi/virtio-scsi-dataplane.c | 218 +- qemu/hw/scsi/virtio-scsi.c | 228 +- qemu/hw/scsi/vmw_pvscsi.c | 98 +- qemu/hw/sd/Makefile.objs | 2 +- qemu/hw/sd/core.c | 146 + qemu/hw/sd/milkymist-memcard.c | 3 +- qemu/hw/sd/omap_mmc.c | 9 +- qemu/hw/sd/pl181.c | 5 +- qemu/hw/sd/pxa2xx_mmci.c | 308 +- qemu/hw/sd/sd.c | 304 +- qemu/hw/sd/sdhci-internal.h | 232 + qemu/hw/sd/sdhci.c | 205 +- qemu/hw/sd/sdhci.h | 295 - qemu/hw/sd/ssi-sd.c | 5 +- qemu/hw/sh4/r2d.c | 25 +- qemu/hw/sh4/sh7750.c | 2 +- qemu/hw/sh4/sh7750_regnames.c | 1 + qemu/hw/sh4/sh_pci.c | 6 +- qemu/hw/sh4/shix.c | 25 +- qemu/hw/smbios/Makefile.objs | 1 + qemu/hw/smbios/smbios.c | 1112 +++ qemu/hw/sparc/leon3.c | 21 +- qemu/hw/sparc/sun4m.c | 249 +- qemu/hw/sparc64/sun4u.c | 192 +- qemu/hw/ssi/omap_spi.c | 4 +- qemu/hw/ssi/pl022.c | 3 +- qemu/hw/ssi/ssi.c | 3 +- qemu/hw/ssi/xilinx_spi.c | 3 +- qemu/hw/ssi/xilinx_spips.c | 49 +- qemu/hw/timer/Makefile.objs | 2 +- qemu/hw/timer/a9gtimer.c | 3 + qemu/hw/timer/allwinner-a10-pit.c | 1 + qemu/hw/timer/arm_mptimer.c | 7 +- qemu/hw/timer/arm_timer.c | 43 +- qemu/hw/timer/aspeed_timer.c | 449 ++ qemu/hw/timer/cadence_ttc.c | 1 + qemu/hw/timer/digic-timer.c | 1 + qemu/hw/timer/ds1338.c | 3 + qemu/hw/timer/etraxfs_timer.c | 1 + qemu/hw/timer/exynos4210_mct.c | 13 +- qemu/hw/timer/exynos4210_pwm.c | 13 +- qemu/hw/timer/exynos4210_rtc.c | 14 +- qemu/hw/timer/grlib_gptimer.c | 1 + qemu/hw/timer/hpet.c | 28 +- qemu/hw/timer/i8254.c | 5 +- qemu/hw/timer/i8254_common.c | 7 +- qemu/hw/timer/imx_epit.c | 144 +- qemu/hw/timer/imx_gpt.c | 204 +- qemu/hw/timer/lm32_timer.c | 1 + qemu/hw/timer/m48t59.c | 7 +- qemu/hw/timer/mc146818rtc.c | 18 +- qemu/hw/timer/milkymist-sysctl.c | 1 + qemu/hw/timer/omap_gptimer.c | 6 +- qemu/hw/timer/omap_synctimer.c | 4 +- qemu/hw/timer/pl031.c | 23 +- qemu/hw/timer/puv3_ost.c | 1 + qemu/hw/timer/pxa2xx_timer.c | 55 +- qemu/hw/timer/sh_timer.c | 1 + qemu/hw/timer/slavio_timer.c | 1 + qemu/hw/timer/stm32f2xx_timer.c | 1 + qemu/hw/timer/tusb6010.c | 816 --- qemu/hw/timer/twl92230.c | 2 + qemu/hw/timer/xilinx_timer.c | 1 + qemu/hw/tpm/tpm_passthrough.c | 33 +- qemu/hw/tpm/tpm_tis.c | 6 +- qemu/hw/tpm/tpm_util.c | 1 + qemu/hw/tricore/tricore_testboard.c | 39 +- qemu/hw/unicore32/puv3.c | 31 +- qemu/hw/usb/Makefile.objs | 7 +- qemu/hw/usb/bus.c | 23 +- qemu/hw/usb/ccid-card-emulated.c | 30 +- qemu/hw/usb/ccid-card-passthru.c | 17 +- qemu/hw/usb/combined-packet.c | 1 + qemu/hw/usb/core.c | 19 +- qemu/hw/usb/desc-msos.c | 1 + qemu/hw/usb/desc.c | 2 +- qemu/hw/usb/desc.h | 1 - qemu/hw/usb/dev-audio.c | 3 +- qemu/hw/usb/dev-bluetooth.c | 1 + qemu/hw/usb/dev-hid.c | 2 + qemu/hw/usb/dev-hub.c | 2 + qemu/hw/usb/dev-mtp.c | 324 +- qemu/hw/usb/dev-network.c | 15 +- qemu/hw/usb/dev-serial.c | 3 + qemu/hw/usb/dev-smartcard-reader.c | 2 + qemu/hw/usb/dev-storage.c | 45 +- qemu/hw/usb/dev-uas.c | 1 + qemu/hw/usb/dev-wacom.c | 1 + qemu/hw/usb/hcd-ehci-pci.c | 7 +- qemu/hw/usb/hcd-ehci-sysbus.c | 1 + qemu/hw/usb/hcd-ehci.c | 32 +- qemu/hw/usb/hcd-musb.c | 3 +- qemu/hw/usb/hcd-ohci.c | 129 +- qemu/hw/usb/hcd-uhci.c | 30 +- qemu/hw/usb/hcd-xhci.c | 13 +- qemu/hw/usb/host-legacy.c | 1 + qemu/hw/usb/host-libusb.c | 7 +- qemu/hw/usb/host-stub.c | 1 + qemu/hw/usb/libhw.c | 1 + qemu/hw/usb/quirks.c | 1 + qemu/hw/usb/redirect.c | 25 +- qemu/hw/usb/tusb6010.c | 817 +++ qemu/hw/vfio/Makefile.objs | 3 +- qemu/hw/vfio/amd-xgbe.c | 56 + qemu/hw/vfio/calxeda-xgmac.c | 1 + qemu/hw/vfio/common.c | 456 +- qemu/hw/vfio/pci-quirks.c | 1205 ++++ qemu/hw/vfio/pci.c | 2145 ++---- qemu/hw/vfio/pci.h | 162 + qemu/hw/vfio/platform.c | 250 +- qemu/hw/virtio/Makefile.objs | 1 - qemu/hw/virtio/dataplane/Makefile.objs | 1 - qemu/hw/virtio/dataplane/vring.c | 453 -- qemu/hw/virtio/vhost-backend.c | 147 +- qemu/hw/virtio/vhost-user.c | 624 +- qemu/hw/virtio/vhost.c | 213 +- qemu/hw/virtio/virtio-balloon.c | 124 +- qemu/hw/virtio/virtio-bus.c | 4 + qemu/hw/virtio/virtio-mmio.c | 1 + qemu/hw/virtio/virtio-pci.c | 398 +- qemu/hw/virtio/virtio-pci.h | 49 +- qemu/hw/virtio/virtio-rng.c | 19 +- qemu/hw/virtio/virtio.c | 562 +- qemu/hw/watchdog/watchdog.c | 17 +- qemu/hw/watchdog/wdt_diag288.c | 16 +- qemu/hw/watchdog/wdt_i6300esb.c | 13 +- qemu/hw/watchdog/wdt_ib700.c | 3 +- qemu/hw/xen/Makefile.objs | 2 +- qemu/hw/xen/xen-host-pci-device.c | 158 +- qemu/hw/xen/xen-host-pci-device.h | 7 +- qemu/hw/xen/xen_backend.c | 56 +- qemu/hw/xen/xen_devconfig.c | 1 + qemu/hw/xen/xen_pt.c | 253 +- qemu/hw/xen/xen_pt.h | 40 +- qemu/hw/xen/xen_pt_config_init.c | 356 +- qemu/hw/xen/xen_pt_graphics.c | 275 + qemu/hw/xen/xen_pt_msi.c | 124 +- qemu/hw/xenpv/Makefile.objs | 4 +- qemu/hw/xenpv/xen_domainbuild.c | 13 +- qemu/hw/xenpv/xen_machine_pv.c | 33 +- qemu/hw/xtensa/pic_cpu.c | 1 + qemu/hw/xtensa/sim.c | 29 +- qemu/hw/xtensa/xtfpga.c | 136 +- qemu/include/block/accounting.h | 31 +- qemu/include/block/aio.h | 70 +- qemu/include/block/block.h | 239 +- qemu/include/block/block_int.h | 135 +- qemu/include/block/blockjob.h | 98 +- qemu/include/block/coroutine.h | 219 - qemu/include/block/coroutine_int.h | 54 - qemu/include/block/dirty-bitmap.h | 44 + qemu/include/block/nbd.h | 30 +- qemu/include/block/qapi.h | 3 +- qemu/include/block/scsi.h | 2 +- qemu/include/block/snapshot.h | 25 +- qemu/include/block/throttle-groups.h | 6 +- qemu/include/block/write-threshold.h | 3 - qemu/include/config.h | 2 - qemu/include/crypto/afsplit.h | 135 + qemu/include/crypto/block.h | 232 + qemu/include/crypto/cipher.h | 65 +- qemu/include/crypto/hash.h | 35 +- qemu/include/crypto/init.h | 3 - qemu/include/crypto/ivgen.h | 206 + qemu/include/crypto/pbkdf.h | 152 + qemu/include/crypto/random.h | 44 + qemu/include/crypto/secret.h | 146 + qemu/include/crypto/tlscreds.h | 66 + qemu/include/crypto/tlscredsanon.h | 112 + qemu/include/crypto/tlscredsx509.h | 114 + qemu/include/crypto/tlssession.h | 322 + qemu/include/crypto/xts.h | 86 + qemu/include/elf.h | 11 +- qemu/include/exec/cpu-all.h | 59 +- qemu/include/exec/cpu-common.h | 6 +- qemu/include/exec/cpu-defs.h | 26 +- qemu/include/exec/cpu_ldst.h | 31 +- qemu/include/exec/cpu_ldst_template.h | 59 +- qemu/include/exec/cpu_ldst_useronly_template.h | 25 + qemu/include/exec/cputlb.h | 16 - qemu/include/exec/exec-all.h | 213 +- qemu/include/exec/gdbstub.h | 27 + qemu/include/exec/helper-head.h | 1 - qemu/include/exec/log.h | 60 + qemu/include/exec/memattrs.h | 4 +- qemu/include/exec/memory.h | 184 +- qemu/include/exec/ram_addr.h | 276 +- qemu/include/exec/softmmu-semi.h | 18 + qemu/include/exec/spinlock.h | 49 - qemu/include/exec/user/thunk.h | 1 - qemu/include/fpu/softfloat.h | 87 +- qemu/include/glib-compat.h | 69 + qemu/include/hw/acpi/acpi-defs.h | 9 + qemu/include/hw/acpi/acpi.h | 11 +- qemu/include/hw/acpi/aml-build.h | 111 +- qemu/include/hw/acpi/bios-linker-loader.h | 4 +- qemu/include/hw/acpi/cpu_hotplug.h | 10 + qemu/include/hw/acpi/ich9.h | 1 - qemu/include/hw/acpi/memory_hotplug.h | 9 + qemu/include/hw/acpi/pc-hotplug.h | 44 +- qemu/include/hw/acpi/pcihp.h | 2 - qemu/include/hw/acpi/piix4.h | 2 - qemu/include/hw/acpi/tco.h | 1 - qemu/include/hw/arm/allwinner-a10.h | 4 + qemu/include/hw/arm/arm.h | 32 +- qemu/include/hw/arm/ast2400.h | 35 + qemu/include/hw/arm/bcm2835_peripherals.h | 48 + qemu/include/hw/arm/bcm2836.h | 35 + qemu/include/hw/arm/fsl-imx25.h | 249 + qemu/include/hw/arm/fsl-imx31.h | 122 + qemu/include/hw/arm/imx.h | 34 - qemu/include/hw/arm/linux-boot-if.h | 43 + qemu/include/hw/arm/raspi_platform.h | 128 + qemu/include/hw/arm/virt-acpi-build.h | 3 +- qemu/include/hw/arm/virt.h | 8 +- qemu/include/hw/arm/xlnx-zynqmp.h | 29 +- qemu/include/hw/block/block.h | 1 - qemu/include/hw/block/fdc.h | 11 +- qemu/include/hw/boards.h | 101 +- qemu/include/hw/bt.h | 21 +- qemu/include/hw/char/bcm2835_aux.h | 33 + qemu/include/hw/char/digic-uart.h | 1 - qemu/include/hw/char/imx_serial.h | 102 + qemu/include/hw/compat.h | 153 +- qemu/include/hw/cpu/icc_bus.h | 82 - qemu/include/hw/display/bcm2835_fb.h | 47 + qemu/include/hw/dma/bcm2835_dma.h | 47 + qemu/include/hw/elf_ops.h | 45 +- qemu/include/hw/gpio/imx_gpio.h | 63 + qemu/include/hw/hotplug.h | 1 - qemu/include/hw/hw.h | 6 +- qemu/include/hw/i2c/imx_i2c.h | 87 + qemu/include/hw/i386/apic_internal.h | 104 +- qemu/include/hw/i386/ich9.h | 6 +- qemu/include/hw/i386/intel_iommu.h | 17 +- qemu/include/hw/i386/ioapic_internal.h | 7 + qemu/include/hw/i386/pc.h | 924 ++- qemu/include/hw/i386/smbios.h | 227 - qemu/include/hw/i386/topology.h | 35 +- qemu/include/hw/input/adb.h | 2 +- qemu/include/hw/intc/arm_gic_common.h | 16 +- qemu/include/hw/intc/arm_gicv3_common.h | 68 + qemu/include/hw/intc/aspeed_vic.h | 48 + qemu/include/hw/intc/bcm2835_ic.h | 33 + qemu/include/hw/intc/bcm2836_control.h | 51 + qemu/include/hw/intc/imx_avic.h | 55 + qemu/include/hw/ipmi/ipmi.h | 258 + qemu/include/hw/isa/apm.h | 1 - qemu/include/hw/isa/i8257.h | 42 + qemu/include/hw/isa/isa.h | 56 +- qemu/include/hw/loader.h | 61 +- qemu/include/hw/mem/nvdimm.h | 62 + qemu/include/hw/mem/pc-dimm.h | 2 +- qemu/include/hw/mips/cps.h | 46 + qemu/include/hw/misc/bcm2835_mbox.h | 38 + qemu/include/hw/misc/bcm2835_mbox_defs.h | 27 + qemu/include/hw/misc/bcm2835_property.h | 35 + qemu/include/hw/misc/imx25_ccm.h | 79 + qemu/include/hw/misc/imx31_ccm.h | 88 + qemu/include/hw/misc/imx6_ccm.h | 197 + qemu/include/hw/misc/imx_ccm.h | 64 + qemu/include/hw/misc/ivshmem.h | 25 + qemu/include/hw/misc/mips_cmgcr.h | 59 + qemu/include/hw/misc/mips_cpc.h | 47 + qemu/include/hw/misc/mips_itu.h | 72 + qemu/include/hw/misc/zynq-xadc.h | 46 + qemu/include/hw/net/imx_fec.h | 113 + qemu/include/hw/nvram/fw_cfg.h | 201 +- qemu/include/hw/nvram/fw_cfg_keys.h | 46 + qemu/include/hw/pci-host/q35.h | 1 - qemu/include/hw/pci-host/spapr.h | 67 +- qemu/include/hw/pci/msi.h | 2 +- qemu/include/hw/pci/msix.h | 16 +- qemu/include/hw/pci/pci-assign.h | 27 + qemu/include/hw/pci/pci.h | 19 +- qemu/include/hw/pci/pci_bridge.h | 2 +- qemu/include/hw/pci/pci_ids.h | 1 + qemu/include/hw/pci/pcie_aer.h | 3 +- qemu/include/hw/pci/shpc.h | 1 - qemu/include/hw/ppc/mac_dbdma.h | 1 + qemu/include/hw/ppc/spapr.h | 102 +- qemu/include/hw/ppc/spapr_drc.h | 31 +- qemu/include/hw/ppc/spapr_vio.h | 2 +- qemu/include/hw/ppc/xics.h | 5 +- qemu/include/hw/qdev-core.h | 20 +- qemu/include/hw/qdev-properties.h | 6 +- qemu/include/hw/s390x/event-facility.h | 12 +- qemu/include/hw/s390x/s390-virtio-ccw.h | 40 + qemu/include/hw/s390x/sclp.h | 33 + qemu/include/hw/s390x/storage-keys.h | 60 + qemu/include/hw/scsi/scsi.h | 5 +- qemu/include/hw/sd.h | 80 - qemu/include/hw/sd/sd.h | 145 + qemu/include/hw/sd/sdhci.h | 95 + qemu/include/hw/smbios/smbios.h | 269 + qemu/include/hw/ssi.h | 94 - qemu/include/hw/ssi/ssi.h | 96 + qemu/include/hw/ssi/xilinx_spips.h | 72 + qemu/include/hw/timer/aspeed_timer.h | 59 + qemu/include/hw/timer/digic-timer.h | 1 - qemu/include/hw/timer/hpet.h | 5 +- qemu/include/hw/timer/imx_epit.h | 80 + qemu/include/hw/timer/imx_gpt.h | 108 + qemu/include/hw/usb.h | 1 + qemu/include/hw/vfio/vfio-amd-xgbe.h | 51 + qemu/include/hw/vfio/vfio-common.h | 61 +- qemu/include/hw/vfio/vfio-platform.h | 4 +- qemu/include/hw/vfio/vfio.h | 6 +- qemu/include/hw/virtio/dataplane/vring-accessors.h | 75 - qemu/include/hw/virtio/dataplane/vring.h | 49 - qemu/include/hw/virtio/vhost-backend.h | 76 +- qemu/include/hw/virtio/vhost-scsi.h | 25 - qemu/include/hw/virtio/vhost.h | 16 +- qemu/include/hw/virtio/virtio-9p.h | 24 - qemu/include/hw/virtio/virtio-access.h | 30 +- qemu/include/hw/virtio/virtio-balloon.h | 2 +- qemu/include/hw/virtio/virtio-blk.h | 13 +- qemu/include/hw/virtio/virtio-bus.h | 8 + qemu/include/hw/virtio/virtio-gpu.h | 26 +- qemu/include/hw/virtio/virtio-input.h | 17 +- qemu/include/hw/virtio/virtio-net.h | 4 +- qemu/include/hw/virtio/virtio-scsi.h | 53 +- qemu/include/hw/virtio/virtio-serial.h | 2 +- qemu/include/hw/virtio/virtio.h | 35 +- qemu/include/hw/xen/xen.h | 11 +- qemu/include/hw/xen/xen_backend.h | 7 +- qemu/include/hw/xen/xen_common.h | 274 +- qemu/include/io/channel-buffer.h | 60 + qemu/include/io/channel-command.h | 91 + qemu/include/io/channel-file.h | 93 + qemu/include/io/channel-socket.h | 251 + qemu/include/io/channel-tls.h | 142 + qemu/include/io/channel-util.h | 52 + qemu/include/io/channel-watch.h | 90 + qemu/include/io/channel-websock.h | 108 + qemu/include/io/channel.h | 504 ++ qemu/include/io/task.h | 255 + qemu/include/libdecnumber/dconfig.h | 1 - qemu/include/libdecnumber/decContext.h | 3 - qemu/include/libdecnumber/decNumberLocal.h | 2 - qemu/include/migration/migration.h | 137 +- qemu/include/migration/postcopy-ram.h | 99 + qemu/include/migration/qemu-file.h | 29 +- qemu/include/migration/vmstate.h | 142 +- qemu/include/monitor/hmp-target.h | 49 + qemu/include/monitor/monitor.h | 7 +- qemu/include/monitor/qdev.h | 1 - qemu/include/net/checksum.h | 1 - qemu/include/net/eth.h | 6 +- qemu/include/net/filter.h | 79 + qemu/include/net/net.h | 6 +- qemu/include/net/queue.h | 20 +- qemu/include/net/vhost_net.h | 4 + qemu/include/qapi/error.h | 312 +- qemu/include/qapi/qmp-event.h | 1 - qemu/include/qapi/qmp/dispatch.h | 1 - qemu/include/qapi/qmp/json-lexer.h | 15 +- qemu/include/qapi/qmp/json-parser.h | 5 +- qemu/include/qapi/qmp/json-streamer.h | 14 +- qemu/include/qapi/qmp/qbool.h | 4 +- qemu/include/qapi/qmp/qdict.h | 5 +- qemu/include/qapi/qmp/qerror.h | 6 +- qemu/include/qapi/qmp/qfloat.h | 4 +- qemu/include/qapi/qmp/qint.h | 4 +- qemu/include/qapi/qmp/qjson.h | 2 - qemu/include/qapi/qmp/qlist.h | 3 +- qemu/include/qapi/qmp/qobject.h | 57 +- qemu/include/qapi/qmp/qstring.h | 4 +- qemu/include/qapi/visitor-impl.h | 75 +- qemu/include/qapi/visitor.h | 123 +- qemu/include/qemu-common.h | 441 +- qemu/include/qemu/atomic.h | 203 +- qemu/include/qemu/base64.h | 58 + qemu/include/qemu/bcd.h | 15 + qemu/include/qemu/bitmap.h | 3 - qemu/include/qemu/bitops.h | 2 - qemu/include/qemu/bswap.h | 30 +- qemu/include/qemu/buffer.h | 161 + qemu/include/qemu/compatfd.h | 1 - qemu/include/qemu/compiler.h | 40 +- qemu/include/qemu/config-file.h | 2 - qemu/include/qemu/coroutine.h | 217 + qemu/include/qemu/coroutine_int.h | 54 + qemu/include/qemu/cutils.h | 183 + qemu/include/qemu/error-report.h | 3 - qemu/include/qemu/event_notifier.h | 6 +- qemu/include/qemu/fprintf-fn.h | 2 - qemu/include/qemu/hbitmap.h | 3 - qemu/include/qemu/help_option.h | 22 + qemu/include/qemu/host-utils.h | 140 +- qemu/include/qemu/id.h | 13 + qemu/include/qemu/int128.h | 3 - qemu/include/qemu/iov.h | 64 +- qemu/include/qemu/log.h | 128 +- qemu/include/qemu/main-loop.h | 4 +- qemu/include/qemu/memfd.h | 24 + qemu/include/qemu/mmap-alloc.h | 12 + qemu/include/qemu/module.h | 5 +- qemu/include/qemu/option.h | 6 - qemu/include/qemu/osdep.h | 194 +- qemu/include/qemu/path.h | 7 + qemu/include/qemu/queue.h | 6 - qemu/include/qemu/range.h | 2 - qemu/include/qemu/rcu.h | 15 +- qemu/include/qemu/seqlock.h | 8 +- qemu/include/qemu/sockets.h | 86 +- qemu/include/qemu/thread-win32.h | 1 + qemu/include/qemu/thread.h | 2 - qemu/include/qemu/throttle.h | 56 +- qemu/include/qemu/timed-average.h | 63 + qemu/include/qemu/timer.h | 38 +- qemu/include/qemu/tls.h | 52 - qemu/include/qemu/typedefs.h | 27 +- qemu/include/qemu/unicode.h | 6 + qemu/include/qemu/xattr.h | 1 - qemu/include/qom/cpu.h | 157 +- qemu/include/qom/object.h | 147 +- qemu/include/qom/object_interfaces.h | 93 + qemu/include/standard-headers/asm-x86/hyperv.h | 366 + .../standard-headers/linux/input-event-codes.h | 805 +++ qemu/include/standard-headers/linux/input.h | 851 +-- qemu/include/standard-headers/linux/pci_regs.h | 424 +- qemu/include/standard-headers/linux/types.h | 5 +- .../standard-headers/linux/virtio_balloon.h | 3 +- qemu/include/standard-headers/linux/virtio_blk.h | 6 +- qemu/include/standard-headers/linux/virtio_gpu.h | 112 +- qemu/include/standard-headers/linux/virtio_ring.h | 3 +- qemu/include/sysemu/accel.h | 1 - qemu/include/sysemu/balloon.h | 2 + qemu/include/sysemu/block-backend.h | 45 +- qemu/include/sysemu/blockdev.h | 5 +- qemu/include/sysemu/char.h | 81 +- qemu/include/sysemu/cpus.h | 2 +- qemu/include/sysemu/device_tree.h | 53 +- qemu/include/sysemu/dma.h | 1 - qemu/include/sysemu/dump-arch.h | 9 +- qemu/include/sysemu/dump.h | 26 +- qemu/include/sysemu/hostmem.h | 1 - qemu/include/sysemu/kvm.h | 93 +- qemu/include/sysemu/memory_mapping.h | 5 +- qemu/include/sysemu/numa.h | 1 - qemu/include/sysemu/os-posix.h | 10 +- qemu/include/sysemu/os-win32.h | 130 +- qemu/include/sysemu/qtest.h | 1 - qemu/include/sysemu/replay.h | 136 + qemu/include/sysemu/rng.h | 31 +- qemu/include/sysemu/seccomp.h | 1 - qemu/include/sysemu/sysemu.h | 52 +- qemu/include/sysemu/tpm_backend.h | 1 - qemu/include/sysemu/watchdog.h | 10 + qemu/include/sysemu/xen-mapcache.h | 1 - qemu/include/ui/console.h | 73 +- qemu/include/ui/egl-context.h | 14 + qemu/include/ui/egl-helpers.h | 14 +- qemu/include/ui/gtk.h | 44 +- qemu/include/ui/input.h | 4 + qemu/include/ui/qemu-pixman.h | 2 - qemu/include/ui/qemu-spice.h | 5 +- qemu/include/ui/sdl2.h | 23 +- qemu/include/ui/shader.h | 4 +- qemu/include/ui/spice-display.h | 16 + qemu/io/Makefile.objs | 10 + qemu/io/channel-buffer.c | 249 + qemu/io/channel-command.c | 368 + qemu/io/channel-file.c | 225 + qemu/io/channel-socket.c | 772 ++ qemu/io/channel-tls.c | 395 + qemu/io/channel-util.c | 51 + qemu/io/channel-watch.c | 347 + qemu/io/channel-websock.c | 964 +++ qemu/io/channel.c | 307 + qemu/io/task.c | 161 + qemu/iohandler.c | 119 +- qemu/ioport.c | 27 +- qemu/iothread.c | 8 +- qemu/kvm-all.c | 219 +- qemu/kvm-stub.c | 12 +- qemu/libcacard/Makefile | 45 - qemu/libcacard/cac.c | 414 -- qemu/libcacard/cac.h | 31 - qemu/libcacard/card_7816.c | 757 -- qemu/libcacard/card_7816.h | 62 - qemu/libcacard/card_7816t.h | 165 - qemu/libcacard/event.c | 103 - qemu/libcacard/eventt.h | 29 - qemu/libcacard/libcacard.pc.in | 13 - qemu/libcacard/libcacard.syms | 77 - qemu/libcacard/link_test.c | 22 - qemu/libcacard/vcard.c | 325 - qemu/libcacard/vcard.h | 86 - qemu/libcacard/vcard_emul.h | 66 - qemu/libcacard/vcard_emul_nss.c | 1274 ---- qemu/libcacard/vcard_emul_type.c | 57 - qemu/libcacard/vcard_emul_type.h | 32 - qemu/libcacard/vcardt.c | 40 - qemu/libcacard/vcardt.h | 59 - qemu/libcacard/vcardt_internal.h | 6 - qemu/libcacard/vevent.h | 27 - qemu/libcacard/vreader.c | 578 -- qemu/libcacard/vreader.h | 55 - qemu/libcacard/vreadert.h | 24 - qemu/libcacard/vscard_common.h | 178 - qemu/libcacard/vscclient.c | 785 -- qemu/libdecnumber/decContext.c | 5 +- qemu/libdecnumber/decNumber.c | 13 +- qemu/libdecnumber/dpd/decimal128.c | 3 +- qemu/libdecnumber/dpd/decimal32.c | 3 +- qemu/libdecnumber/dpd/decimal64.c | 3 +- qemu/linux-headers/asm-arm/unistd.h | 450 ++ qemu/linux-headers/asm-arm64/kvm.h | 45 +- qemu/linux-headers/asm-arm64/unistd.h | 16 + qemu/linux-headers/asm-mips/unistd.h | 1063 +++ qemu/linux-headers/asm-powerpc/epapr_hcalls.h | 4 +- qemu/linux-headers/asm-powerpc/kvm.h | 9 + qemu/linux-headers/asm-powerpc/unistd.h | 394 + qemu/linux-headers/asm-s390/kvm.h | 13 +- qemu/linux-headers/asm-s390/unistd.h | 406 ++ qemu/linux-headers/asm-x86/hyperv.h | 251 +- qemu/linux-headers/asm-x86/kvm.h | 4 +- qemu/linux-headers/asm-x86/unistd.h | 15 + qemu/linux-headers/asm-x86/unistd_32.h | 381 + qemu/linux-headers/asm-x86/unistd_64.h | 334 + qemu/linux-headers/asm-x86/unistd_x32.h | 321 + qemu/linux-headers/linux/kvm.h | 59 +- qemu/linux-headers/linux/psci.h | 18 + qemu/linux-headers/linux/userfaultfd.h | 167 + qemu/linux-headers/linux/vfio.h | 101 +- qemu/linux-headers/linux/vhost.h | 6 + qemu/linux-user/aarch64/syscall.h | 13 - qemu/linux-user/aarch64/syscall_nr.h | 70 +- qemu/linux-user/aarch64/target_syscall.h | 18 + qemu/linux-user/alpha/syscall.h | 257 - qemu/linux-user/alpha/syscall_nr.h | 6 + qemu/linux-user/alpha/target_syscall.h | 262 + qemu/linux-user/arm/nwfpe/double_cpdo.c | 1 + qemu/linux-user/arm/nwfpe/extended_cpdo.c | 1 + qemu/linux-user/arm/nwfpe/fpa11.c | 2 +- qemu/linux-user/arm/nwfpe/fpa11.h | 5 +- qemu/linux-user/arm/nwfpe/fpa11_cpdo.c | 1 + qemu/linux-user/arm/nwfpe/fpa11_cpdt.c | 1 + qemu/linux-user/arm/nwfpe/fpa11_cprt.c | 1 + qemu/linux-user/arm/nwfpe/fpopcode.c | 1 + qemu/linux-user/arm/nwfpe/single_cpdo.c | 1 + qemu/linux-user/arm/syscall.h | 50 - qemu/linux-user/arm/syscall_nr.h | 12 + qemu/linux-user/arm/target_syscall.h | 54 + qemu/linux-user/cris/syscall_nr.h | 24 + .../cris/{syscall.h => target_syscall.h} | 0 qemu/linux-user/elfload.c | 201 +- qemu/linux-user/flatload.c | 8 +- qemu/linux-user/i386/syscall.h | 152 - qemu/linux-user/i386/syscall_nr.h | 29 +- qemu/linux-user/i386/target_syscall.h | 157 + qemu/linux-user/linuxload.c | 15 +- qemu/linux-user/m68k-sim.c | 9 +- qemu/linux-user/m68k/syscall.h | 25 - qemu/linux-user/m68k/syscall_nr.h | 29 +- qemu/linux-user/m68k/target_syscall.h | 29 + qemu/linux-user/main.c | 561 +- qemu/linux-user/microblaze/syscall_nr.h | 8 + .../microblaze/{syscall.h => target_syscall.h} | 0 qemu/linux-user/mips/syscall.h | 233 - qemu/linux-user/mips/syscall_nr.h | 12 + qemu/linux-user/mips/target_syscall.h | 237 + qemu/linux-user/mips64/syscall.h | 230 - qemu/linux-user/mips64/syscall_nr.h | 23 + qemu/linux-user/mips64/target_syscall.h | 234 + qemu/linux-user/mmap.c | 48 +- qemu/linux-user/openrisc/syscall.h | 29 - qemu/linux-user/openrisc/syscall_nr.h | 28 +- qemu/linux-user/openrisc/target_syscall.h | 34 + qemu/linux-user/ppc/syscall.h | 75 - qemu/linux-user/ppc/syscall_nr.h | 26 +- qemu/linux-user/ppc/target_syscall.h | 80 + qemu/linux-user/qemu.h | 16 +- qemu/linux-user/s390x/syscall.h | 29 - qemu/linux-user/s390x/syscall_nr.h | 30 + qemu/linux-user/s390x/target_syscall.h | 34 + qemu/linux-user/sh4/syscall.h | 17 - qemu/linux-user/sh4/syscall_nr.h | 2 +- qemu/linux-user/sh4/target_syscall.h | 22 + qemu/linux-user/signal.c | 420 +- qemu/linux-user/sparc/syscall.h | 20 - qemu/linux-user/sparc/syscall_nr.h | 16 +- qemu/linux-user/sparc/target_syscall.h | 25 + qemu/linux-user/sparc64/syscall.h | 21 - qemu/linux-user/sparc64/syscall_nr.h | 14 + qemu/linux-user/sparc64/target_syscall.h | 26 + qemu/linux-user/strace.c | 4 +- qemu/linux-user/syscall.c | 526 +- qemu/linux-user/syscall_defs.h | 64 +- qemu/linux-user/tilegx/syscall_nr.h | 328 + qemu/linux-user/tilegx/target_cpu.h | 35 + qemu/linux-user/tilegx/target_signal.h | 28 + qemu/linux-user/tilegx/target_structs.h | 46 + qemu/linux-user/tilegx/target_syscall.h | 43 + qemu/linux-user/tilegx/termbits.h | 274 + qemu/linux-user/uaccess.c | 4 +- qemu/linux-user/uname.c | 2 +- qemu/linux-user/unicore32/target_signal.h | 6 +- .../unicore32/{syscall.h => target_syscall.h} | 0 qemu/linux-user/vm86.c | 7 +- qemu/linux-user/x86_64/syscall.h | 102 - qemu/linux-user/x86_64/syscall_nr.h | 15 +- qemu/linux-user/x86_64/target_syscall.h | 107 + qemu/main-loop.c | 14 +- qemu/memory.c | 374 +- qemu/memory_mapping.c | 5 + qemu/migration/Makefile.objs | 2 +- qemu/migration/block.c | 106 +- qemu/migration/exec.c | 7 +- qemu/migration/fd.c | 6 +- qemu/migration/migration.c | 1016 ++- qemu/migration/postcopy-ram.c | 761 ++ qemu/migration/qemu-file-buf.c | 12 +- qemu/migration/qemu-file-stdio.c | 18 +- qemu/migration/qemu-file-unix.c | 129 +- qemu/migration/qemu-file.c | 91 +- qemu/migration/ram.c | 1330 +++- qemu/migration/rdma.c | 44 +- qemu/migration/savevm.c | 1226 +++- qemu/migration/tcp.c | 13 +- qemu/migration/unix.c | 6 +- qemu/migration/vmstate.c | 28 + qemu/migration/xbzrle.c | 3 +- qemu/module-common.c | 2 +- qemu/monitor.c | 1568 +--- qemu/nbd.c | 1489 ---- qemu/nbd/Makefile.objs | 1 + qemu/nbd/client.c | 745 ++ qemu/nbd/common.c | 96 + qemu/nbd/nbd-internal.h | 124 + qemu/nbd/server.c | 1290 ++++ qemu/net/Makefile.objs | 3 + qemu/net/checksum.c | 11 +- qemu/net/dump.c | 246 +- qemu/net/eth.c | 1 + qemu/net/filter-buffer.c | 212 + qemu/net/filter-mirror.c | 427 ++ qemu/net/filter.c | 275 + qemu/net/hub.c | 5 +- qemu/net/l2tpv3.c | 8 +- qemu/net/net.c | 298 +- qemu/net/netmap.c | 163 +- qemu/net/queue.c | 25 +- qemu/net/slirp.c | 119 +- qemu/net/socket.c | 26 +- qemu/net/tap-aix.c | 3 +- qemu/net/tap-bsd.c | 42 +- qemu/net/tap-haiku.c | 3 +- qemu/net/tap-linux.c | 8 +- qemu/net/tap-linux.h | 1 - qemu/net/tap-solaris.c | 4 +- qemu/net/tap-win32.c | 55 +- qemu/net/tap.c | 17 +- qemu/net/util.c | 3 +- qemu/net/util.h | 1 - qemu/net/vde.c | 6 +- qemu/net/vhost-user.c | 185 +- qemu/numa.c | 104 +- qemu/os-posix.c | 16 +- qemu/os-win32.c | 8 +- qemu/page_cache.c | 9 +- qemu/pc-bios/README | 6 +- qemu/pc-bios/bios-256k.bin | Bin 262144 -> 262144 bytes qemu/pc-bios/bios.bin | Bin 131072 -> 131072 bytes qemu/pc-bios/efi-e1000.rom | Bin 197120 -> 196608 bytes qemu/pc-bios/efi-eepro100.rom | Bin 197632 -> 197120 bytes qemu/pc-bios/efi-ne2k_pci.rom | Bin 195584 -> 195584 bytes qemu/pc-bios/efi-pcnet.rom | Bin 195584 -> 195584 bytes qemu/pc-bios/efi-rtl8139.rom | Bin 200192 -> 199168 bytes qemu/pc-bios/efi-virtio.rom | Bin 194048 -> 193024 bytes qemu/pc-bios/openbios-ppc | Bin 746588 -> 750684 bytes qemu/pc-bios/openbios-sparc32 | Bin 381512 -> 381584 bytes qemu/pc-bios/openbios-sparc64 | Bin 1616768 -> 1592280 bytes qemu/pc-bios/optionrom/optionrom.h | 3 +- qemu/pc-bios/q35-acpi-dsdt.aml | Bin 7344 -> 0 bytes qemu/pc-bios/s390-ccw.img | Bin 13784 -> 26424 bytes qemu/pc-bios/s390-ccw/Makefile | 5 +- qemu/pc-bios/s390-ccw/bootmap.c | 321 +- qemu/pc-bios/s390-ccw/bootmap.h | 215 +- qemu/pc-bios/s390-ccw/main.c | 87 +- qemu/pc-bios/s390-ccw/s390-ccw.h | 54 +- qemu/pc-bios/s390-ccw/scsi.h | 184 + qemu/pc-bios/s390-ccw/virtio-scsi.c | 342 + qemu/pc-bios/s390-ccw/virtio-scsi.h | 72 + qemu/pc-bios/s390-ccw/virtio.c | 478 +- qemu/pc-bios/s390-ccw/virtio.h | 226 +- qemu/pc-bios/s390-zipl.rom | Bin 3304 -> 0 bytes qemu/pc-bios/slof.bin | Bin 912720 -> 923832 bytes qemu/pc-bios/u-boot.e500 | Bin 330260 -> 347788 bytes qemu/pc-bios/vgabios-cirrus.bin | Bin 38400 -> 38400 bytes qemu/pc-bios/vgabios-qxl.bin | Bin 38400 -> 38912 bytes qemu/pc-bios/vgabios-stdvga.bin | Bin 38400 -> 38912 bytes qemu/pc-bios/vgabios-virtio.bin | Bin 38400 -> 38912 bytes qemu/pc-bios/vgabios-vmware.bin | Bin 38400 -> 38912 bytes qemu/pc-bios/vgabios.bin | Bin 38400 -> 38400 bytes qemu/po/de_DE.po | 36 +- qemu/po/fr_FR.po | 36 +- qemu/po/hu.po | 50 +- qemu/po/it.po | 38 +- qemu/po/messages.po | 50 +- qemu/po/tr.po | 50 +- qemu/po/zh_CN.po | 36 +- qemu/qapi-schema.json | 590 +- qemu/qapi/block-core.json | 605 +- qemu/qapi/block.json | 38 +- qemu/qapi/common.json | 21 +- qemu/qapi/crypto.json | 222 + qemu/qapi/event.json | 33 +- qemu/qapi/introspect.json | 279 + qemu/qapi/opts-visitor.c | 74 +- qemu/qapi/qapi-dealloc-visitor.c | 82 +- qemu/qapi/qapi-util.c | 3 +- qemu/qapi/qapi-visit-core.c | 278 +- qemu/qapi/qmp-dispatch.c | 5 +- qemu/qapi/qmp-event.c | 10 +- qemu/qapi/qmp-input-visitor.c | 134 +- qemu/qapi/qmp-output-visitor.c | 123 +- qemu/qapi/qmp-registry.c | 2 +- qemu/qapi/string-input-visitor.c | 69 +- qemu/qapi/string-output-visitor.c | 55 +- qemu/qdev-monitor.c | 119 +- qemu/qemu-bridge-helper.c | 13 +- qemu/qemu-char.c | 2083 +++--- qemu/qemu-coroutine-io.c | 91 - qemu/qemu-coroutine-lock.c | 186 - qemu/qemu-coroutine-sleep.c | 41 - qemu/qemu-coroutine.c | 146 - qemu/qemu-doc.texi | 182 +- qemu/qemu-ga.texi | 137 + qemu/qemu-img-cmds.hx | 44 +- qemu/qemu-img.c | 770 +- qemu/qemu-img.texi | 18 +- qemu/qemu-io-cmds.c | 363 +- qemu/qemu-io.c | 159 +- qemu/qemu-log.c | 176 - qemu/qemu-nbd.c | 548 +- qemu/qemu-nbd.texi | 99 +- qemu/qemu-options.hx | 483 +- qemu/qemu-seccomp.c | 19 +- qemu/qemu-tech.texi | 75 +- qemu/qemu-timer.c | 48 +- qemu/qemu.nsi | 3 + qemu/qga/channel-posix.c | 33 +- qemu/qga/channel-win32.c | 9 +- qemu/qga/commands-posix.c | 135 +- qemu/qga/commands-win32.c | 270 +- qemu/qga/commands.c | 435 +- qemu/qga/guest-agent-command-state.c | 5 +- qemu/qga/guest-agent-core.h | 2 + qemu/qga/installer/qemu-ga.wxs | 98 +- qemu/qga/main.c | 506 +- qemu/qga/qapi-schema.json | 107 +- qemu/qga/service-win32.c | 3 +- qemu/qga/vss-win32.c | 8 +- qemu/qga/vss-win32.h | 1 - qemu/qga/vss-win32/install.cpp | 3 +- qemu/qga/vss-win32/provider.cpp | 2 +- qemu/qga/vss-win32/requester.cpp | 11 +- qemu/qga/vss-win32/requester.h | 14 +- qemu/qga/vss-win32/vss-common.h | 1 - qemu/qjson.c | 2 +- qemu/qmp-commands.hx | 963 ++- qemu/qmp.c | 174 +- qemu/qobject/Makefile.objs | 2 +- qemu/qobject/json-lexer.c | 48 +- qemu/qobject/json-parser.c | 340 +- qemu/qobject/json-streamer.c | 92 +- qemu/qobject/qbool.c | 16 +- qemu/qobject/qdict.c | 55 +- qemu/qobject/qfloat.c | 16 +- qemu/qobject/qint.c | 16 +- qemu/qobject/qjson.c | 15 +- qemu/qobject/qlist.c | 15 +- qemu/qobject/qnull.c | 13 +- qemu/qobject/qobject.c | 35 + qemu/qobject/qstring.c | 16 +- qemu/qom/Makefile.objs | 7 +- qemu/qom/container.c | 2 +- qemu/qom/cpu.c | 22 +- qemu/qom/object.c | 570 +- qemu/qom/object_interfaces.c | 178 + qemu/qom/qom-qobject.c | 20 +- qemu/qtest.c | 2 + qemu/replay/Makefile.objs | 6 + qemu/replay/replay-char.c | 168 + qemu/replay/replay-events.c | 311 + qemu/replay/replay-input.c | 169 + qemu/replay/replay-internal.c | 207 + qemu/replay/replay-internal.h | 158 + qemu/replay/replay-time.c | 65 + qemu/replay/replay.c | 354 + qemu/roms/Makefile | 18 +- qemu/roms/SLOF/README | 36 + qemu/roms/SLOF/VERSION | 2 +- qemu/roms/SLOF/board-js2x/llfw/stage2.lds | 3 +- qemu/roms/SLOF/board-js2x/llfw/stage2_head.S | 2 - qemu/roms/SLOF/board-js2x/slof/Makefile | 1 + qemu/roms/SLOF/board-js2x/slof/dma-function.fs | 31 + qemu/roms/SLOF/board-js2x/slof/helper.fs | 11 + qemu/roms/SLOF/board-qemu/llfw/stage2.lds | 3 +- qemu/roms/SLOF/board-qemu/llfw/stage2_head.S | 2 - qemu/roms/SLOF/board-qemu/slof/Makefile | 5 + qemu/roms/SLOF/board-qemu/slof/OF.fs | 4 - qemu/roms/SLOF/board-qemu/slof/archsupport.fs | 39 + qemu/roms/SLOF/board-qemu/slof/helper.fs | 13 + .../SLOF/board-qemu/slof/pci-device_1234_1111.fs | 229 +- .../SLOF/board-qemu/slof/pci-device_1af4_1001.fs | 9 - .../SLOF/board-qemu/slof/pci-device_1af4_1009.fs | 9 - .../SLOF/board-qemu/slof/pci-device_1af4_1041.fs | 15 + .../SLOF/board-qemu/slof/pci-device_1af4_1042.fs | 15 + .../SLOF/board-qemu/slof/pci-device_1af4_1048.fs | 15 + .../SLOF/board-qemu/slof/pci-device_1af4_1050.fs | 15 + qemu/roms/SLOF/board-qemu/slof/qemu-vga.fs | 198 + qemu/roms/SLOF/board-qemu/slof/tree.fs | 2 + qemu/roms/SLOF/board-qemu/slof/virtio-block.fs | 3 +- qemu/roms/SLOF/board-qemu/slof/virtio-fs.fs | 3 +- qemu/roms/SLOF/board-qemu/slof/virtio-net.fs | 33 +- qemu/roms/SLOF/board-qemu/slof/virtio-scsi.fs | 3 +- qemu/roms/SLOF/board-qemu/slof/virtio.fs | 35 - .../SLOF/clients/net-snk/app/netapps/netboot.c | 58 +- qemu/roms/SLOF/clients/net-snk/app/netlib/bootp.c | 5 +- qemu/roms/SLOF/clients/net-snk/app/netlib/dhcp.c | 221 +- qemu/roms/SLOF/clients/net-snk/app/netlib/dhcp.h | 1 + qemu/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.c | 42 +- qemu/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.h | 11 +- qemu/roms/SLOF/clients/net-snk/app/netlib/dns.c | 4 +- qemu/roms/SLOF/clients/net-snk/app/netlib/dns.h | 2 +- .../SLOF/clients/net-snk/app/netlib/ethernet.c | 42 +- qemu/roms/SLOF/clients/net-snk/app/netlib/icmpv6.c | 9 +- qemu/roms/SLOF/clients/net-snk/app/netlib/ipv4.c | 131 +- qemu/roms/SLOF/clients/net-snk/app/netlib/ipv4.h | 6 +- qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.c | 123 +- qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.h | 28 +- qemu/roms/SLOF/clients/net-snk/app/netlib/ndp.c | 19 + qemu/roms/SLOF/clients/net-snk/app/netlib/ndp.h | 1 + qemu/roms/SLOF/clients/net-snk/app/netlib/tcp.c | 16 +- qemu/roms/SLOF/clients/net-snk/app/netlib/tftp.c | 57 +- qemu/roms/SLOF/clients/net-snk/app/netlib/udp.c | 26 +- qemu/roms/SLOF/clients/net-snk/app/netlib/udp.h | 2 +- qemu/roms/SLOF/clients/net-snk/client.lds | 4 +- qemu/roms/SLOF/clients/net-snk/kernel/entry.S | 2 +- qemu/roms/SLOF/clients/takeover/client.lds | 4 +- qemu/roms/SLOF/clients/takeover/entry.S | 2 +- qemu/roms/SLOF/clients/takeover/main.c | 2 +- qemu/roms/SLOF/include/byteorder.h | 22 +- qemu/roms/SLOF/include/helpers.h | 2 + qemu/roms/SLOF/include/ppc970/cache.h | 50 +- qemu/roms/SLOF/include/ppcp7/cache.h | 13 +- qemu/roms/SLOF/lib/libc/include/stdlib.h | 1 + qemu/roms/SLOF/lib/libc/stdio/vsnprintf.c | 180 +- qemu/roms/SLOF/lib/libc/stdlib/rand.c | 9 +- qemu/roms/SLOF/lib/libhvcall/Makefile | 2 +- qemu/roms/SLOF/lib/libhvcall/rfill.c | 38 + qemu/roms/SLOF/lib/libnvram/envvar.c | 40 +- qemu/roms/SLOF/lib/libnvram/libnvram.code | 75 +- qemu/roms/SLOF/lib/libnvram/nvram.c | 27 + qemu/roms/SLOF/lib/libnvram/nvram.h | 10 +- qemu/roms/SLOF/lib/libusb/usb-core.c | 37 +- qemu/roms/SLOF/lib/libusb/usb-core.h | 7 +- qemu/roms/SLOF/lib/libusb/usb-ehci.c | 4 +- qemu/roms/SLOF/lib/libusb/usb-hid.c | 85 +- qemu/roms/SLOF/lib/libusb/usb-hub.c | 4 +- qemu/roms/SLOF/lib/libusb/usb-ohci.c | 16 +- qemu/roms/SLOF/lib/libusb/usb-slof.c | 34 +- qemu/roms/SLOF/lib/libusb/usb-xhci.c | 236 +- qemu/roms/SLOF/lib/libusb/usb-xhci.h | 5 + qemu/roms/SLOF/lib/libvirtio/p9.c | 2 +- qemu/roms/SLOF/lib/libvirtio/p9.h | 2 +- qemu/roms/SLOF/lib/libvirtio/virtio-9p.c | 35 +- qemu/roms/SLOF/lib/libvirtio/virtio-9p.h | 2 +- qemu/roms/SLOF/lib/libvirtio/virtio-blk.c | 100 +- qemu/roms/SLOF/lib/libvirtio/virtio-blk.h | 2 +- qemu/roms/SLOF/lib/libvirtio/virtio-internal.h | 48 + qemu/roms/SLOF/lib/libvirtio/virtio-net.c | 200 +- qemu/roms/SLOF/lib/libvirtio/virtio-net.h | 15 +- qemu/roms/SLOF/lib/libvirtio/virtio-scsi.c | 174 +- qemu/roms/SLOF/lib/libvirtio/virtio.c | 457 +- qemu/roms/SLOF/lib/libvirtio/virtio.code | 23 +- qemu/roms/SLOF/lib/libvirtio/virtio.h | 58 +- qemu/roms/SLOF/lib/libvirtio/virtio.in | 2 + qemu/roms/SLOF/make.rules | 12 +- qemu/roms/SLOF/rtas/reloc.S | 2 +- qemu/roms/SLOF/rtas/rtas.lds | 3 +- qemu/roms/SLOF/rtas/rtas_entry.S | 2 +- qemu/roms/SLOF/slof/entry.S | 9 + qemu/roms/SLOF/slof/fs/archsupport.fs | 38 - qemu/roms/SLOF/slof/fs/base.fs | 2 - qemu/roms/SLOF/slof/fs/boot.fs | 8 +- qemu/roms/SLOF/slof/fs/client.fs | 12 + qemu/roms/SLOF/slof/fs/fbuffer.fs | 30 +- qemu/roms/SLOF/slof/fs/little-endian.fs | 6 + qemu/roms/SLOF/slof/fs/packages/disk-label.fs | 162 +- qemu/roms/SLOF/slof/fs/packages/fat-files.fs | 15 +- qemu/roms/SLOF/slof/fs/pci-config-bridge.fs | 20 - qemu/roms/SLOF/slof/fs/pci-scan.fs | 19 +- qemu/roms/SLOF/slof/fs/rmove.fs | 53 - qemu/roms/SLOF/slof/fs/root.fs | 1 - qemu/roms/SLOF/slof/fs/terminal.fs | 3 +- qemu/roms/SLOF/slof/helpers.c | 14 + qemu/roms/SLOF/slof/paflof.c | 9 + qemu/roms/SLOF/slof/ppc64.c | 19 +- qemu/roms/SLOF/slof/prim.code | 13 + qemu/roms/SLOF/slof/prim.in | 3 +- qemu/roms/config.ipxe.general.h | 4 - qemu/roms/config.seabios-128k | 2 + qemu/roms/ipxe/COPYING | 345 +- qemu/roms/ipxe/COPYING.GPLv2 | 339 + qemu/roms/ipxe/COPYING.UBDL | 59 + qemu/roms/ipxe/COPYRIGHTS | 12 - qemu/roms/ipxe/src/Makefile | 4 +- qemu/roms/ipxe/src/Makefile.housekeeping | 131 +- qemu/roms/ipxe/src/arch/i386/Makefile | 11 + qemu/roms/ipxe/src/arch/i386/core/basemem_packet.c | 6 +- qemu/roms/ipxe/src/arch/i386/core/cachedhcp.c | 6 +- qemu/roms/ipxe/src/arch/i386/core/gdbmach.c | 6 +- qemu/roms/ipxe/src/arch/i386/core/patch_cf.S | 6 +- qemu/roms/ipxe/src/arch/i386/core/pci_autoboot.c | 6 +- qemu/roms/ipxe/src/arch/i386/core/rdtsc_timer.c | 12 +- qemu/roms/ipxe/src/arch/i386/core/relocate.c | 2 +- qemu/roms/ipxe/src/arch/i386/core/runtime.c | 6 +- qemu/roms/ipxe/src/arch/i386/core/setjmp.S | 84 +- qemu/roms/ipxe/src/arch/i386/core/stack.S | 2 +- qemu/roms/ipxe/src/arch/i386/core/stack16.S | 2 +- qemu/roms/ipxe/src/arch/i386/core/timer2.c | 87 - qemu/roms/ipxe/src/arch/i386/core/virtaddr.S | 2 +- qemu/roms/ipxe/src/arch/i386/drivers/net/undi.c | 7 +- .../roms/ipxe/src/arch/i386/drivers/net/undiload.c | 6 +- .../roms/ipxe/src/arch/i386/drivers/net/undionly.c | 6 +- .../ipxe/src/arch/i386/drivers/net/undipreload.c | 6 +- .../ipxe/src/arch/i386/firmware/pcbios/basemem.c | 6 +- .../src/arch/i386/firmware/pcbios/bios_console.c | 12 +- .../src/arch/i386/firmware/pcbios/e820mangler.S | 6 +- .../ipxe/src/arch/i386/firmware/pcbios/fakee820.c | 6 +- .../ipxe/src/arch/i386/firmware/pcbios/hidemem.c | 6 +- .../ipxe/src/arch/i386/firmware/pcbios/memmap.c | 6 +- .../ipxe/src/arch/i386/firmware/pcbios/pnpbios.c | 6 +- .../roms/ipxe/src/arch/i386/hci/commands/pxe_cmd.c | 6 +- qemu/roms/ipxe/src/arch/i386/image/bootsector.c | 6 +- qemu/roms/ipxe/src/arch/i386/image/bzimage.c | 6 +- qemu/roms/ipxe/src/arch/i386/image/elfboot.c | 39 +- qemu/roms/ipxe/src/arch/i386/image/initrd.c | 6 +- qemu/roms/ipxe/src/arch/i386/image/multiboot.c | 6 +- qemu/roms/ipxe/src/arch/i386/image/pxe_image.c | 52 +- qemu/roms/ipxe/src/arch/i386/image/sdi.c | 6 +- qemu/roms/ipxe/src/arch/i386/include/basemem.h | 2 +- .../ipxe/src/arch/i386/include/basemem_packet.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/bios.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/biosint.h | 2 +- .../ipxe/src/arch/i386/include/bits/byteswap.h | 2 +- .../ipxe/src/arch/i386/include/bits/compiler.h | 5 +- qemu/roms/ipxe/src/arch/i386/include/bits/endian.h | 8 - .../roms/ipxe/src/arch/i386/include/bits/entropy.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/bits/hyperv.h | 72 + qemu/roms/ipxe/src/arch/i386/include/bits/nap.h | 2 +- .../roms/ipxe/src/arch/i386/include/bits/profile.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/bits/reboot.h | 2 +- .../roms/ipxe/src/arch/i386/include/bits/sanboot.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/bits/smbios.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/bits/stdint.h | 2 +- .../roms/ipxe/src/arch/i386/include/bits/strings.h | 48 +- qemu/roms/ipxe/src/arch/i386/include/bits/time.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/bits/timer.h | 2 +- .../roms/ipxe/src/arch/i386/include/bits/uaccess.h | 2 +- .../roms/ipxe/src/arch/i386/include/bits/umalloc.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/bootsector.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/bzimage.h | 2 +- .../src/arch/i386/include/efi/ipxe/dhcp_arch.h | 11 +- qemu/roms/ipxe/src/arch/i386/include/fakee820.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/initrd.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/int13.h | 2 +- .../ipxe/src/arch/i386/include/ipxe/bios_nap.h | 2 +- .../ipxe/src/arch/i386/include/ipxe/bios_reboot.h | 2 +- .../ipxe/src/arch/i386/include/ipxe/bios_sanboot.h | 2 +- .../ipxe/src/arch/i386/include/ipxe/bios_smbios.h | 2 +- .../ipxe/src/arch/i386/include/ipxe/bios_timer.h | 8 +- .../ipxe/src/arch/i386/include/ipxe/errno/pcbios.h | 2 +- .../ipxe/src/arch/i386/include/ipxe/guestrpc.h | 2 +- .../src/arch/i386/include/ipxe/memtop_umalloc.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/ipxe/msr.h | 2 +- .../ipxe/src/arch/i386/include/ipxe/rdtsc_timer.h | 2 +- .../ipxe/src/arch/i386/include/ipxe/rtc_entropy.h | 2 +- .../ipxe/src/arch/i386/include/ipxe/rtc_time.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/ipxe/timer2.h | 14 - qemu/roms/ipxe/src/arch/i386/include/ipxe/vesafb.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/ipxe/vmware.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/librm.h | 14 +- qemu/roms/ipxe/src/arch/i386/include/limits.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/memsizes.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/multiboot.h | 2 +- .../src/arch/i386/include/pcbios/ipxe/dhcp_arch.h | 11 +- qemu/roms/ipxe/src/arch/i386/include/pic8259.h | 73 - qemu/roms/ipxe/src/arch/i386/include/pnpbios.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/pxe.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/pxe_api.h | 6 +- qemu/roms/ipxe/src/arch/i386/include/pxe_call.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/pxe_error.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/pxe_types.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/realmode.h | 14 +- qemu/roms/ipxe/src/arch/i386/include/registers.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/rtc.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/sdi.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/setjmp.h | 46 +- qemu/roms/ipxe/src/arch/i386/include/undi.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/undiload.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/undinet.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/undipreload.h | 2 +- qemu/roms/ipxe/src/arch/i386/include/undirom.h | 2 +- .../roms/ipxe/src/arch/i386/interface/pcbios/apm.c | 6 +- .../ipxe/src/arch/i386/interface/pcbios/bios_nap.c | 2 +- .../src/arch/i386/interface/pcbios/bios_reboot.c | 6 +- .../src/arch/i386/interface/pcbios/bios_smbios.c | 6 +- .../src/arch/i386/interface/pcbios/bios_timer.c | 6 +- .../ipxe/src/arch/i386/interface/pcbios/biosint.c | 2 +- .../ipxe/src/arch/i386/interface/pcbios/int13.c | 6 +- .../ipxe/src/arch/i386/interface/pcbios/int13con.c | 284 + .../arch/i386/interface/pcbios/memtop_umalloc.c | 6 +- .../ipxe/src/arch/i386/interface/pcbios/pcibios.c | 12 +- .../src/arch/i386/interface/pcbios/rtc_entropy.c | 6 +- .../ipxe/src/arch/i386/interface/pcbios/rtc_time.c | 6 +- .../ipxe/src/arch/i386/interface/pcbios/vesafb.c | 6 +- .../ipxe/src/arch/i386/interface/pxe/pxe_call.c | 7 +- .../ipxe/src/arch/i386/interface/pxe/pxe_entry.S | 6 +- .../src/arch/i386/interface/pxe/pxe_exit_hook.c | 6 +- .../ipxe/src/arch/i386/interface/pxe/pxe_file.c | 6 +- .../ipxe/src/arch/i386/interface/pxe/pxe_loader.c | 6 +- .../ipxe/src/arch/i386/interface/pxe/pxe_preboot.c | 24 +- .../ipxe/src/arch/i386/interface/pxe/pxe_tftp.c | 43 +- .../ipxe/src/arch/i386/interface/pxe/pxe_udp.c | 130 +- .../ipxe/src/arch/i386/interface/pxe/pxe_undi.c | 6 +- .../arch/i386/interface/syslinux/comboot_call.c | 29 +- .../ipxe/src/arch/i386/interface/vmware/guestrpc.c | 6 +- .../src/arch/i386/interface/vmware/vmconsole.c | 6 +- .../ipxe/src/arch/i386/interface/vmware/vmware.c | 6 +- qemu/roms/ipxe/src/arch/i386/prefix/bootpart.S | 2 +- qemu/roms/ipxe/src/arch/i386/prefix/exeprefix.S | 6 +- qemu/roms/ipxe/src/arch/i386/prefix/hdprefix.S | 2 +- qemu/roms/ipxe/src/arch/i386/prefix/isaromprefix.S | 6 +- qemu/roms/ipxe/src/arch/i386/prefix/kkkpxeprefix.S | 6 +- qemu/roms/ipxe/src/arch/i386/prefix/kkpxeprefix.S | 5 +- qemu/roms/ipxe/src/arch/i386/prefix/kpxeprefix.S | 2 +- qemu/roms/ipxe/src/arch/i386/prefix/libprefix.S | 75 +- qemu/roms/ipxe/src/arch/i386/prefix/lkrnprefix.S | 2 +- qemu/roms/ipxe/src/arch/i386/prefix/mbr.S | 2 + qemu/roms/ipxe/src/arch/i386/prefix/mromprefix.S | 7 +- qemu/roms/ipxe/src/arch/i386/prefix/nbiprefix.S | 2 + qemu/roms/ipxe/src/arch/i386/prefix/nullprefix.S | 2 + qemu/roms/ipxe/src/arch/i386/prefix/pciromprefix.S | 6 +- qemu/roms/ipxe/src/arch/i386/prefix/pxeprefix.S | 2 +- qemu/roms/ipxe/src/arch/i386/prefix/romprefix.S | 31 +- qemu/roms/ipxe/src/arch/i386/prefix/undiloader.S | 2 +- qemu/roms/ipxe/src/arch/i386/prefix/unlzma.S | 942 +++ qemu/roms/ipxe/src/arch/i386/prefix/unlzma16.S | 9 + qemu/roms/ipxe/src/arch/i386/prefix/unnrv2b.S | 184 - qemu/roms/ipxe/src/arch/i386/prefix/unnrv2b16.S | 9 - qemu/roms/ipxe/src/arch/i386/prefix/usbdisk.S | 23 +- qemu/roms/ipxe/src/arch/i386/scripts/i386.lds | 9 + qemu/roms/ipxe/src/arch/i386/transitions/liba20.S | 6 +- qemu/roms/ipxe/src/arch/i386/transitions/libkir.S | 2 +- qemu/roms/ipxe/src/arch/i386/transitions/librm.S | 2 +- .../ipxe/src/arch/i386/transitions/librm_mgmt.c | 4 +- .../ipxe/src/arch/i386/transitions/librm_test.c | 7 +- qemu/roms/ipxe/src/arch/x86/Makefile | 5 + qemu/roms/ipxe/src/arch/x86/Makefile.efi | 2 +- qemu/roms/ipxe/src/arch/x86/core/cpuid.c | 6 +- qemu/roms/ipxe/src/arch/x86/core/cpuid_settings.c | 6 +- qemu/roms/ipxe/src/arch/x86/core/debugcon.c | 6 +- qemu/roms/ipxe/src/arch/x86/core/pcidirect.c | 6 +- .../ipxe/src/arch/{i386 => x86}/core/pic8259.c | 0 qemu/roms/ipxe/src/arch/x86/core/pit8254.c | 70 + qemu/roms/ipxe/src/arch/x86/core/vram_settings.c | 72 + qemu/roms/ipxe/src/arch/x86/core/x86_bigint.c | 6 +- qemu/roms/ipxe/src/arch/x86/core/x86_io.c | 6 +- qemu/roms/ipxe/src/arch/x86/core/x86_string.c | 90 +- qemu/roms/ipxe/src/arch/x86/core/x86_tcpip.c | 6 +- qemu/roms/ipxe/src/arch/x86/core/x86_uart.c | 69 + .../roms/ipxe/src/arch/x86/drivers/hyperv/hyperv.c | 597 ++ .../roms/ipxe/src/arch/x86/drivers/hyperv/hyperv.h | 57 + qemu/roms/ipxe/src/arch/x86/drivers/xen/hvm.c | 9 +- qemu/roms/ipxe/src/arch/x86/drivers/xen/hvm.h | 2 +- .../ipxe/src/arch/x86/hci/commands/cpuid_cmd.c | 6 +- qemu/roms/ipxe/src/arch/x86/include/bits/bigint.h | 2 +- qemu/roms/ipxe/src/arch/x86/include/bits/endian.h | 8 + qemu/roms/ipxe/src/arch/x86/include/bits/errfile.h | 6 +- qemu/roms/ipxe/src/arch/x86/include/bits/io.h | 2 +- qemu/roms/ipxe/src/arch/x86/include/bits/pci_io.h | 2 +- qemu/roms/ipxe/src/arch/x86/include/bits/string.h | 140 +- qemu/roms/ipxe/src/arch/x86/include/bits/tcpip.h | 2 +- qemu/roms/ipxe/src/arch/x86/include/bits/uart.h | 41 + qemu/roms/ipxe/src/arch/x86/include/bits/xen.h | 21 +- qemu/roms/ipxe/src/arch/x86/include/ipxe/cpuid.h | 5 +- .../src/arch/x86/include/ipxe/efi/efix86_nap.h | 2 +- qemu/roms/ipxe/src/arch/x86/include/ipxe/pcibios.h | 2 +- .../ipxe/src/arch/x86/include/ipxe/pcidirect.h | 2 +- qemu/roms/ipxe/src/arch/x86/include/ipxe/pit8254.h | 81 + qemu/roms/ipxe/src/arch/x86/include/ipxe/x86_io.h | 2 +- .../src/arch/x86/include/linux/ipxe/dhcp_arch.h | 11 +- qemu/roms/ipxe/src/arch/x86/include/pic8259.h | 70 + .../ipxe/src/arch/x86/interface/efi/efi_entropy.c | 223 + .../ipxe/src/arch/x86/interface/efi/efix86_nap.c | 6 +- qemu/roms/ipxe/src/arch/x86/prefix/efidrvprefix.c | 35 + qemu/roms/ipxe/src/arch/x86/prefix/efiprefix.c | 5 + qemu/roms/ipxe/src/arch/x86_64/Makefile | 1 + qemu/roms/ipxe/src/arch/x86_64/core/setjmp.S | 65 + .../ipxe/src/arch/x86_64/include/bits/byteswap.h | 2 +- .../ipxe/src/arch/x86_64/include/bits/compiler.h | 3 + .../ipxe/src/arch/x86_64/include/bits/endian.h | 6 - .../ipxe/src/arch/x86_64/include/bits/entropy.h | 2 +- .../ipxe/src/arch/x86_64/include/bits/hyperv.h | 75 + .../ipxe/src/arch/x86_64/include/bits/profile.h | 2 +- .../ipxe/src/arch/x86_64/include/bits/reboot.h | 2 +- .../ipxe/src/arch/x86_64/include/bits/sanboot.h | 2 +- .../ipxe/src/arch/x86_64/include/bits/strings.h | 40 +- qemu/roms/ipxe/src/arch/x86_64/include/bits/time.h | 2 +- .../src/arch/x86_64/include/efi/ipxe/dhcp_arch.h | 11 +- qemu/roms/ipxe/src/arch/x86_64/include/ipxe/msr.h | 2 +- qemu/roms/ipxe/src/arch/x86_64/include/setjmp.h | 34 + qemu/roms/ipxe/src/config/.gitignore | 1 - qemu/roms/ipxe/src/config/branding.h | 174 + qemu/roms/ipxe/src/config/colour.h | 2 +- qemu/roms/ipxe/src/config/config.c | 59 +- qemu/roms/ipxe/src/config/config_crypto.c | 76 + qemu/roms/ipxe/src/config/config_ethernet.c | 25 +- qemu/roms/ipxe/src/config/config_fc.c | 22 +- qemu/roms/ipxe/src/config/config_http.c | 45 + qemu/roms/ipxe/src/config/config_infiniband.c | 22 +- qemu/roms/ipxe/src/config/config_net80211.c | 16 +- qemu/roms/ipxe/src/config/config_romprefix.c | 22 +- qemu/roms/ipxe/src/config/config_route.c | 22 +- qemu/roms/ipxe/src/config/config_usb.c | 52 + qemu/roms/ipxe/src/config/console.h | 3 +- qemu/roms/ipxe/src/config/crypto.h | 35 +- qemu/roms/ipxe/src/config/defaults.h | 2 +- qemu/roms/ipxe/src/config/defaults/efi.h | 6 +- qemu/roms/ipxe/src/config/defaults/pcbios.h | 8 +- qemu/roms/ipxe/src/config/dhcp.h | 87 + qemu/roms/ipxe/src/config/entropy.h | 2 +- qemu/roms/ipxe/src/config/fault.h | 34 + qemu/roms/ipxe/src/config/general.h | 31 +- qemu/roms/ipxe/src/config/ioapi.h | 2 +- qemu/roms/ipxe/src/config/named.h | 2 +- qemu/roms/ipxe/src/config/nap.h | 2 +- .../ipxe/src/config/qemu/colour.h} | 0 .../ipxe/src/config/qemu/console.h} | 0 .../ipxe/src/config/qemu/crypto.h} | 0 qemu/roms/ipxe/src/config/qemu/general.h | 10 + .../ipxe/src/config/qemu/serial.h} | 0 .../ipxe/src/config/qemu/settings.h} | 0 .../ipxe/src/config/qemu/sideband.h} | 0 .../ipxe/src/config/qemu/usb.h} | 0 qemu/roms/ipxe/src/config/reboot.h | 2 +- qemu/roms/ipxe/src/config/sanboot.h | 2 +- qemu/roms/ipxe/src/config/serial.h | 5 - qemu/roms/ipxe/src/config/settings.h | 3 +- qemu/roms/ipxe/src/config/sideband.h | 2 +- qemu/roms/ipxe/src/config/time.h | 2 +- qemu/roms/ipxe/src/config/timer.h | 2 +- qemu/roms/ipxe/src/config/umalloc.h | 2 +- qemu/roms/ipxe/src/config/usb.h | 33 + qemu/roms/ipxe/src/config/vbox/general.h | 8 - .../ipxe/src/config/vbox/usb.h} | 0 qemu/roms/ipxe/src/core/acpi.c | 6 +- qemu/roms/ipxe/src/core/ansicol.c | 6 +- qemu/roms/ipxe/src/core/ansicoldef.c | 6 +- qemu/roms/ipxe/src/core/ansiesc.c | 6 +- qemu/roms/ipxe/src/core/asprintf.c | 2 +- qemu/roms/ipxe/src/core/assert.c | 6 +- qemu/roms/ipxe/src/core/base16.c | 96 +- qemu/roms/ipxe/src/core/base64.c | 106 +- qemu/roms/ipxe/src/core/basename.c | 6 +- qemu/roms/ipxe/src/core/bitmap.c | 6 +- qemu/roms/ipxe/src/core/blockdev.c | 6 +- qemu/roms/ipxe/src/core/blocktrans.c | 261 + qemu/roms/ipxe/src/core/console.c | 2 +- qemu/roms/ipxe/src/core/cpio.c | 6 +- qemu/roms/ipxe/src/core/ctype.c | 13 +- qemu/roms/ipxe/src/core/cwuri.c | 6 +- qemu/roms/ipxe/src/core/debug.c | 6 +- qemu/roms/ipxe/src/core/debug_md5.c | 6 +- qemu/roms/ipxe/src/core/device.c | 6 +- qemu/roms/ipxe/src/core/downloader.c | 112 +- qemu/roms/ipxe/src/core/edd.c | 6 +- qemu/roms/ipxe/src/core/errno.c | 2 +- qemu/roms/ipxe/src/core/exec.c | 6 +- qemu/roms/ipxe/src/core/fault.c | 82 + qemu/roms/ipxe/src/core/fbcon.c | 6 +- qemu/roms/ipxe/src/core/fnrec.c | 6 +- qemu/roms/ipxe/src/core/gdbserial.c | 80 +- qemu/roms/ipxe/src/core/gdbstub.c | 6 +- qemu/roms/ipxe/src/core/gdbudp.c | 6 +- qemu/roms/ipxe/src/core/getkey.c | 6 +- qemu/roms/ipxe/src/core/getopt.c | 6 +- qemu/roms/ipxe/src/core/image.c | 89 +- qemu/roms/ipxe/src/core/init.c | 6 +- qemu/roms/ipxe/src/core/interface.c | 31 +- qemu/roms/ipxe/src/core/iobuf.c | 36 +- qemu/roms/ipxe/src/core/isqrt.c | 6 +- qemu/roms/ipxe/src/core/job.c | 6 +- qemu/roms/ipxe/src/core/linebuf.c | 59 +- qemu/roms/ipxe/src/core/lineconsole.c | 6 +- qemu/roms/ipxe/src/core/list.c | 6 +- qemu/roms/ipxe/src/core/log.c | 6 +- qemu/roms/ipxe/src/core/main.c | 11 +- qemu/roms/ipxe/src/core/malloc.c | 63 +- qemu/roms/ipxe/src/core/memblock.c | 6 +- qemu/roms/ipxe/src/core/memmap_settings.c | 6 +- qemu/roms/ipxe/src/core/menu.c | 6 +- qemu/roms/ipxe/src/core/misc.c | 85 - qemu/roms/ipxe/src/core/monojob.c | 6 +- qemu/roms/ipxe/src/core/null_reboot.c | 6 +- qemu/roms/ipxe/src/core/null_sanboot.c | 6 +- qemu/roms/ipxe/src/core/null_time.c | 6 +- qemu/roms/ipxe/src/core/nvo.c | 6 +- qemu/roms/ipxe/src/core/open.c | 6 +- qemu/roms/ipxe/src/core/params.c | 6 +- qemu/roms/ipxe/src/core/parseopt.c | 9 +- qemu/roms/ipxe/src/core/pending.c | 6 +- qemu/roms/ipxe/src/core/pinger.c | 6 +- qemu/roms/ipxe/src/core/pixbuf.c | 6 +- qemu/roms/ipxe/src/core/pool.c | 114 + qemu/roms/ipxe/src/core/posix_io.c | 6 +- qemu/roms/ipxe/src/core/process.c | 6 +- qemu/roms/ipxe/src/core/profile.c | 6 +- qemu/roms/ipxe/src/core/random.c | 2 +- qemu/roms/ipxe/src/core/refcnt.c | 6 +- qemu/roms/ipxe/src/core/resolv.c | 6 +- qemu/roms/ipxe/src/core/serial.c | 349 +- qemu/roms/ipxe/src/core/serial_console.c | 42 - qemu/roms/ipxe/src/core/settings.c | 102 +- qemu/roms/ipxe/src/core/string.c | 648 +- qemu/roms/ipxe/src/core/stringextra.c | 188 - qemu/roms/ipxe/src/core/strtoull.c | 60 - qemu/roms/ipxe/src/core/time.c | 6 +- qemu/roms/ipxe/src/core/timer.c | 6 +- qemu/roms/ipxe/src/core/uart.c | 153 + qemu/roms/ipxe/src/core/uri.c | 15 +- qemu/roms/ipxe/src/core/uuid.c | 6 +- qemu/roms/ipxe/src/core/version.c | 7 +- qemu/roms/ipxe/src/core/vsprintf.c | 6 +- qemu/roms/ipxe/src/core/wchar.c | 6 +- qemu/roms/ipxe/src/core/xfer.c | 49 +- qemu/roms/ipxe/src/core/xferbuf.c | 262 +- qemu/roms/ipxe/src/crypto/aes.c | 808 +++ qemu/roms/ipxe/src/crypto/asn1.c | 6 +- qemu/roms/ipxe/src/crypto/axtls/aes.c | 457 -- qemu/roms/ipxe/src/crypto/axtls/bigint.h | 99 - qemu/roms/ipxe/src/crypto/axtls/bigint_impl.h | 131 - qemu/roms/ipxe/src/crypto/axtls/config.h | 13 - qemu/roms/ipxe/src/crypto/axtls/crypto.h | 229 - qemu/roms/ipxe/src/crypto/axtls/os_port.h | 54 - qemu/roms/ipxe/src/crypto/axtls_aes.c | 160 - qemu/roms/ipxe/src/crypto/bigint.c | 6 +- qemu/roms/ipxe/src/crypto/cbc.c | 6 +- qemu/roms/ipxe/src/crypto/certstore.c | 6 +- qemu/roms/ipxe/src/crypto/chap.c | 6 +- qemu/roms/ipxe/src/crypto/cms.c | 6 +- qemu/roms/ipxe/src/crypto/crypto_null.c | 6 +- qemu/roms/ipxe/src/crypto/deflate.c | 6 +- qemu/roms/ipxe/src/crypto/drbg.c | 6 +- qemu/roms/ipxe/src/crypto/ecb.c | 80 + qemu/roms/ipxe/src/crypto/entropy.c | 6 +- qemu/roms/ipxe/src/crypto/hash_df.c | 6 +- qemu/roms/ipxe/src/crypto/hmac.c | 6 +- qemu/roms/ipxe/src/crypto/hmac_drbg.c | 6 +- qemu/roms/ipxe/src/crypto/md5.c | 6 +- .../ipxe/src/crypto/mishmash/rsa_aes_cbc_sha1.c | 48 + .../ipxe/src/crypto/mishmash/rsa_aes_cbc_sha256.c | 48 + qemu/roms/ipxe/src/crypto/mishmash/rsa_md5.c | 51 + qemu/roms/ipxe/src/crypto/mishmash/rsa_sha1.c | 62 + qemu/roms/ipxe/src/crypto/mishmash/rsa_sha224.c | 62 + qemu/roms/ipxe/src/crypto/mishmash/rsa_sha256.c | 62 + qemu/roms/ipxe/src/crypto/mishmash/rsa_sha384.c | 62 + qemu/roms/ipxe/src/crypto/mishmash/rsa_sha512.c | 62 + qemu/roms/ipxe/src/crypto/null_entropy.c | 6 +- qemu/roms/ipxe/src/crypto/ocsp.c | 2 +- qemu/roms/ipxe/src/crypto/privkey.c | 6 +- qemu/roms/ipxe/src/crypto/random_nz.c | 6 +- qemu/roms/ipxe/src/crypto/rbg.c | 6 +- qemu/roms/ipxe/src/crypto/rootcert.c | 6 +- qemu/roms/ipxe/src/crypto/rsa.c | 78 +- qemu/roms/ipxe/src/crypto/sha1.c | 6 +- qemu/roms/ipxe/src/crypto/sha224.c | 82 + qemu/roms/ipxe/src/crypto/sha256.c | 63 +- qemu/roms/ipxe/src/crypto/sha384.c | 82 + qemu/roms/ipxe/src/crypto/sha512.c | 303 + qemu/roms/ipxe/src/crypto/sha512_224.c | 83 + qemu/roms/ipxe/src/crypto/sha512_256.c | 83 + qemu/roms/ipxe/src/crypto/x509.c | 15 +- qemu/roms/ipxe/src/drivers/bitbash/bitbash.c | 6 +- qemu/roms/ipxe/src/drivers/bitbash/i2c_bit.c | 6 +- qemu/roms/ipxe/src/drivers/bitbash/spi_bit.c | 6 +- qemu/roms/ipxe/src/drivers/block/ata.c | 6 +- qemu/roms/ipxe/src/drivers/block/scsi.c | 6 +- qemu/roms/ipxe/src/drivers/bus/cdc.c | 54 + qemu/roms/ipxe/src/drivers/bus/pci.c | 31 +- qemu/roms/ipxe/src/drivers/bus/pci_settings.c | 6 +- qemu/roms/ipxe/src/drivers/bus/pcibackup.c | 6 +- qemu/roms/ipxe/src/drivers/bus/pciextra.c | 12 +- qemu/roms/ipxe/src/drivers/bus/pcivpd.c | 6 +- qemu/roms/ipxe/src/drivers/bus/usb.c | 2128 ++++++ qemu/roms/ipxe/src/drivers/infiniband/arbel.c | 6 +- qemu/roms/ipxe/src/drivers/infiniband/arbel.h | 2 +- qemu/roms/ipxe/src/drivers/infiniband/linda.c | 6 +- qemu/roms/ipxe/src/drivers/infiniband/linda.h | 6 +- qemu/roms/ipxe/src/drivers/infiniband/qib7322.c | 6 +- qemu/roms/ipxe/src/drivers/infiniband/qib7322.h | 6 +- qemu/roms/ipxe/src/drivers/net/amd8111e.h | 6 +- qemu/roms/ipxe/src/drivers/net/ath/ath9k/ani.h | 2 +- .../ipxe/src/drivers/net/ath/ath9k/ath9k_ani.c | 16 +- .../src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c | 18 +- .../src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c | 12 +- qemu/roms/ipxe/src/drivers/net/atl1e.c | 2 +- qemu/roms/ipxe/src/drivers/net/davicom.c | 1 + qemu/roms/ipxe/src/drivers/net/dm96xx.c | 671 ++ qemu/roms/ipxe/src/drivers/net/dm96xx.h | 194 + qemu/roms/ipxe/src/drivers/net/dmfe.c | 2 +- qemu/roms/ipxe/src/drivers/net/ecm.c | 520 ++ qemu/roms/ipxe/src/drivers/net/ecm.h | 93 + qemu/roms/ipxe/src/drivers/net/eepro.c | 20 +- qemu/roms/ipxe/src/drivers/net/eepro100.c | 1 - qemu/roms/ipxe/src/drivers/net/efi/nii.c | 150 +- qemu/roms/ipxe/src/drivers/net/efi/nii.h | 2 +- qemu/roms/ipxe/src/drivers/net/efi/snp.c | 6 +- qemu/roms/ipxe/src/drivers/net/efi/snponly.c | 6 +- qemu/roms/ipxe/src/drivers/net/etherfabric.c | 2 +- qemu/roms/ipxe/src/drivers/net/forcedeth.c | 6 +- qemu/roms/ipxe/src/drivers/net/igbvf/igbvf_main.c | 2 +- qemu/roms/ipxe/src/drivers/net/intel.c | 148 +- qemu/roms/ipxe/src/drivers/net/intel.h | 123 +- qemu/roms/ipxe/src/drivers/net/intelvf.c | 340 + qemu/roms/ipxe/src/drivers/net/intelvf.h | 109 + qemu/roms/ipxe/src/drivers/net/intelx.c | 25 +- qemu/roms/ipxe/src/drivers/net/intelx.h | 2 +- qemu/roms/ipxe/src/drivers/net/intelxvf.c | 466 ++ qemu/roms/ipxe/src/drivers/net/intelxvf.h | 104 + qemu/roms/ipxe/src/drivers/net/ipoib.c | 90 +- qemu/roms/ipxe/src/drivers/net/legacy.c | 2 +- qemu/roms/ipxe/src/drivers/net/mii.c | 38 +- qemu/roms/ipxe/src/drivers/net/myson.c | 6 +- qemu/roms/ipxe/src/drivers/net/myson.h | 2 +- qemu/roms/ipxe/src/drivers/net/ncm.c | 672 ++ qemu/roms/ipxe/src/drivers/net/ncm.h | 173 + qemu/roms/ipxe/src/drivers/net/netfront.c | 16 +- qemu/roms/ipxe/src/drivers/net/netfront.h | 2 +- qemu/roms/ipxe/src/drivers/net/netvsc.c | 848 +++ qemu/roms/ipxe/src/drivers/net/netvsc.h | 365 + qemu/roms/ipxe/src/drivers/net/phantom/nx_bitops.h | 6 +- qemu/roms/ipxe/src/drivers/net/phantom/phantom.c | 6 +- qemu/roms/ipxe/src/drivers/net/phantom/phantom.h | 6 +- .../roms/ipxe/src/drivers/net/phantom/phantom_hw.h | 6 +- qemu/roms/ipxe/src/drivers/net/pnic.c | 14 +- qemu/roms/ipxe/src/drivers/net/prism2.c | 14 +- qemu/roms/ipxe/src/drivers/net/prism2_pci.c | 16 +- qemu/roms/ipxe/src/drivers/net/prism2_plx.c | 18 +- qemu/roms/ipxe/src/drivers/net/realtek.c | 11 +- qemu/roms/ipxe/src/drivers/net/realtek.h | 2 +- qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8180.c | 20 +- qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8185.c | 14 +- qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.c | 26 +- qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.h | 4 + qemu/roms/ipxe/src/drivers/net/skeleton.c | 6 +- qemu/roms/ipxe/src/drivers/net/skeleton.h | 2 +- qemu/roms/ipxe/src/drivers/net/smsc75xx.c | 1057 +++ qemu/roms/ipxe/src/drivers/net/smsc75xx.h | 309 + qemu/roms/ipxe/src/drivers/net/sundance.c | 2 +- qemu/roms/ipxe/src/drivers/net/tg3/tg3.c | 1 + qemu/roms/ipxe/src/drivers/net/tg3/tg3.h | 5 + qemu/roms/ipxe/src/drivers/net/tg3/tg3_hw.c | 1 + qemu/roms/ipxe/src/drivers/net/virtio-net.c | 14 +- qemu/roms/ipxe/src/drivers/net/vmxnet3.c | 6 +- qemu/roms/ipxe/src/drivers/net/vmxnet3.h | 6 +- qemu/roms/ipxe/src/drivers/net/vxge/vxge.c | 3 +- qemu/roms/ipxe/src/drivers/net/vxge/vxge_main.c | 2 +- qemu/roms/ipxe/src/drivers/net/w89c840.c | 2 + qemu/roms/ipxe/src/drivers/nvs/nvs.c | 6 +- qemu/roms/ipxe/src/drivers/nvs/nvsvpd.c | 6 +- qemu/roms/ipxe/src/drivers/nvs/spi.c | 6 +- qemu/roms/ipxe/src/drivers/nvs/threewire.c | 6 +- qemu/roms/ipxe/src/drivers/usb/ehci.c | 1994 +++++ qemu/roms/ipxe/src/drivers/usb/ehci.h | 544 ++ qemu/roms/ipxe/src/drivers/usb/uhci.c | 1577 ++++ qemu/roms/ipxe/src/drivers/usb/uhci.h | 350 + qemu/roms/ipxe/src/drivers/usb/usbhid.c | 151 + qemu/roms/ipxe/src/drivers/usb/usbhub.c | 547 ++ qemu/roms/ipxe/src/drivers/usb/usbhub.h | 279 + qemu/roms/ipxe/src/drivers/usb/usbkbd.c | 509 ++ qemu/roms/ipxe/src/drivers/usb/usbkbd.h | 154 + qemu/roms/ipxe/src/drivers/usb/usbnet.c | 284 + qemu/roms/ipxe/src/drivers/usb/xhci.c | 3321 +++++++++ qemu/roms/ipxe/src/drivers/usb/xhci.h | 1150 +++ qemu/roms/ipxe/src/hci/commands/autoboot_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/config_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/console_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/dhcp_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/fcmgmt_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/gdbstub_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/ifmgmt_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/image_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/image_trust_cmd.c | 9 +- qemu/roms/ipxe/src/hci/commands/ipstat_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/login_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/lotest_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/menu_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/neighbour_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/nvo_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/param_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/pci_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/ping_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/poweroff_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/profstat_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/reboot_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/route_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/sanboot_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/sync_cmd.c | 6 +- qemu/roms/ipxe/src/hci/commands/vlan_cmd.c | 6 +- qemu/roms/ipxe/src/hci/editstring.c | 6 +- qemu/roms/ipxe/src/hci/jumpscroll.c | 140 + qemu/roms/ipxe/src/hci/mucurses/alert.c | 2 + qemu/roms/ipxe/src/hci/mucurses/ansi_screen.c | 2 +- qemu/roms/ipxe/src/hci/mucurses/clear.c | 2 +- qemu/roms/ipxe/src/hci/mucurses/colour.c | 2 +- qemu/roms/ipxe/src/hci/mucurses/cursor.h | 2 +- qemu/roms/ipxe/src/hci/mucurses/edging.c | 2 + qemu/roms/ipxe/src/hci/mucurses/kb.c | 2 + qemu/roms/ipxe/src/hci/mucurses/mucurses.c | 2 +- qemu/roms/ipxe/src/hci/mucurses/mucurses.h | 2 +- qemu/roms/ipxe/src/hci/mucurses/print.c | 2 +- qemu/roms/ipxe/src/hci/mucurses/print_nadv.c | 2 + qemu/roms/ipxe/src/hci/mucurses/slk.c | 2 + qemu/roms/ipxe/src/hci/mucurses/widgets/editbox.c | 6 +- qemu/roms/ipxe/src/hci/mucurses/winattrs.c | 2 +- qemu/roms/ipxe/src/hci/mucurses/windows.c | 2 + qemu/roms/ipxe/src/hci/mucurses/wininit.c | 2 +- qemu/roms/ipxe/src/hci/readline.c | 6 +- qemu/roms/ipxe/src/hci/shell.c | 9 +- qemu/roms/ipxe/src/hci/strerror.c | 9 +- qemu/roms/ipxe/src/hci/tui/login_ui.c | 6 +- qemu/roms/ipxe/src/hci/tui/menu_ui.c | 102 +- qemu/roms/ipxe/src/hci/tui/settings_ui.c | 355 +- qemu/roms/ipxe/src/image/elf.c | 145 +- qemu/roms/ipxe/src/image/embedded.c | 2 +- qemu/roms/ipxe/src/image/png.c | 6 +- qemu/roms/ipxe/src/image/pnm.c | 6 +- qemu/roms/ipxe/src/image/script.c | 6 +- qemu/roms/ipxe/src/image/segment.c | 6 +- qemu/roms/ipxe/src/include/.gitignore | 1 - qemu/roms/ipxe/src/include/assert.h | 2 +- qemu/roms/ipxe/src/include/big_bswap.h | 35 - qemu/roms/ipxe/src/include/byteswap.h | 185 +- qemu/roms/ipxe/src/include/compiler.h | 203 +- qemu/roms/ipxe/src/include/ctype.h | 118 +- qemu/roms/ipxe/src/include/curses.h | 2 +- qemu/roms/ipxe/src/include/elf.h | 277 +- qemu/roms/ipxe/src/include/endian.h | 31 +- qemu/roms/ipxe/src/include/errno.h | 6 +- qemu/roms/ipxe/src/include/getopt.h | 2 +- qemu/roms/ipxe/src/include/hci/ifmgmt_cmd.h | 6 +- qemu/roms/ipxe/src/include/ipxe/acpi.h | 2 +- qemu/roms/ipxe/src/include/ipxe/aes.h | 44 +- qemu/roms/ipxe/src/include/ipxe/ansicol.h | 2 +- qemu/roms/ipxe/src/include/ipxe/ansiesc.h | 2 +- qemu/roms/ipxe/src/include/ipxe/aoe.h | 2 +- qemu/roms/ipxe/src/include/ipxe/api.h | 2 +- qemu/roms/ipxe/src/include/ipxe/arp.h | 6 +- qemu/roms/ipxe/src/include/ipxe/asn1.h | 55 +- qemu/roms/ipxe/src/include/ipxe/ata.h | 2 +- qemu/roms/ipxe/src/include/ipxe/base16.h | 35 +- qemu/roms/ipxe/src/include/ipxe/base64.h | 7 +- qemu/roms/ipxe/src/include/ipxe/bigint.h | 2 +- qemu/roms/ipxe/src/include/ipxe/bitbash.h | 2 +- qemu/roms/ipxe/src/include/ipxe/bitmap.h | 2 +- qemu/roms/ipxe/src/include/ipxe/bitops.h | 6 +- qemu/roms/ipxe/src/include/ipxe/blockdev.h | 2 +- qemu/roms/ipxe/src/include/ipxe/blocktrans.h | 38 + qemu/roms/ipxe/src/include/ipxe/bofm.h | 2 +- qemu/roms/ipxe/src/include/ipxe/cbc.h | 2 +- qemu/roms/ipxe/src/include/ipxe/cdc.h | 55 + qemu/roms/ipxe/src/include/ipxe/certstore.h | 2 +- qemu/roms/ipxe/src/include/ipxe/chap.h | 2 +- qemu/roms/ipxe/src/include/ipxe/cms.h | 2 +- qemu/roms/ipxe/src/include/ipxe/command.h | 2 +- qemu/roms/ipxe/src/include/ipxe/console.h | 2 +- qemu/roms/ipxe/src/include/ipxe/cpio.h | 2 +- qemu/roms/ipxe/src/include/ipxe/crc32.h | 2 +- qemu/roms/ipxe/src/include/ipxe/crypto.h | 2 +- qemu/roms/ipxe/src/include/ipxe/deflate.h | 2 +- qemu/roms/ipxe/src/include/ipxe/device.h | 33 +- qemu/roms/ipxe/src/include/ipxe/dhcp.h | 12 +- qemu/roms/ipxe/src/include/ipxe/dhcpopts.h | 2 +- qemu/roms/ipxe/src/include/ipxe/dhcppkt.h | 2 +- qemu/roms/ipxe/src/include/ipxe/dhcpv6.h | 2 +- qemu/roms/ipxe/src/include/ipxe/dns.h | 2 +- qemu/roms/ipxe/src/include/ipxe/downloader.h | 2 +- qemu/roms/ipxe/src/include/ipxe/drbg.h | 2 +- qemu/roms/ipxe/src/include/ipxe/ecb.h | 55 + qemu/roms/ipxe/src/include/ipxe/edd.h | 2 +- qemu/roms/ipxe/src/include/ipxe/editbox.h | 2 +- qemu/roms/ipxe/src/include/ipxe/editstring.h | 2 +- .../roms/ipxe/src/include/ipxe/efi/ProcessorBind.h | 2 +- qemu/roms/ipxe/src/include/ipxe/efi/Protocol/Rng.h | 158 + qemu/roms/ipxe/src/include/ipxe/efi/efi_autoboot.h | 2 +- qemu/roms/ipxe/src/include/ipxe/efi/efi_driver.h | 2 +- qemu/roms/ipxe/src/include/ipxe/efi/efi_entropy.h | 35 + qemu/roms/ipxe/src/include/ipxe/efi/efi_hii.h | 2 +- qemu/roms/ipxe/src/include/ipxe/efi/efi_pci.h | 2 +- qemu/roms/ipxe/src/include/ipxe/efi/efi_pci_api.h | 2 +- qemu/roms/ipxe/src/include/ipxe/efi/efi_reboot.h | 2 +- qemu/roms/ipxe/src/include/ipxe/efi/efi_smbios.h | 2 +- qemu/roms/ipxe/src/include/ipxe/efi/efi_snp.h | 27 +- qemu/roms/ipxe/src/include/ipxe/efi/efi_strings.h | 2 +- qemu/roms/ipxe/src/include/ipxe/efi/efi_time.h | 20 + qemu/roms/ipxe/src/include/ipxe/efi/efi_timer.h | 2 +- qemu/roms/ipxe/src/include/ipxe/efi/efi_uaccess.h | 2 +- qemu/roms/ipxe/src/include/ipxe/efi/efi_umalloc.h | 2 +- qemu/roms/ipxe/src/include/ipxe/efi/efi_utils.h | 2 +- qemu/roms/ipxe/src/include/ipxe/efi/efi_watchdog.h | 31 + qemu/roms/ipxe/src/include/ipxe/efi/efi_wrap.h | 2 +- qemu/roms/ipxe/src/include/ipxe/eisa.h | 2 +- qemu/roms/ipxe/src/include/ipxe/elf.h | 13 +- qemu/roms/ipxe/src/include/ipxe/eltorito.h | 2 +- qemu/roms/ipxe/src/include/ipxe/entropy.h | 3 +- qemu/roms/ipxe/src/include/ipxe/errfile.h | 37 +- qemu/roms/ipxe/src/include/ipxe/errno/efi.h | 2 +- qemu/roms/ipxe/src/include/ipxe/errno/linux.h | 2 +- qemu/roms/ipxe/src/include/ipxe/errortab.h | 2 +- qemu/roms/ipxe/src/include/ipxe/eth_slow.h | 2 +- qemu/roms/ipxe/src/include/ipxe/ethernet.h | 2 +- qemu/roms/ipxe/src/include/ipxe/fakedhcp.h | 2 +- qemu/roms/ipxe/src/include/ipxe/fault.h | 53 + qemu/roms/ipxe/src/include/ipxe/fbcon.h | 2 +- qemu/roms/ipxe/src/include/ipxe/fc.h | 2 +- qemu/roms/ipxe/src/include/ipxe/fcels.h | 2 +- qemu/roms/ipxe/src/include/ipxe/fcns.h | 2 +- qemu/roms/ipxe/src/include/ipxe/fcoe.h | 2 +- qemu/roms/ipxe/src/include/ipxe/fcp.h | 2 +- qemu/roms/ipxe/src/include/ipxe/features.h | 2 +- qemu/roms/ipxe/src/include/ipxe/fragment.h | 2 +- qemu/roms/ipxe/src/include/ipxe/ftp.h | 2 +- qemu/roms/ipxe/src/include/ipxe/gdbserial.h | 13 +- qemu/roms/ipxe/src/include/ipxe/gdbstub.h | 2 +- qemu/roms/ipxe/src/include/ipxe/gdbudp.h | 2 +- qemu/roms/ipxe/src/include/ipxe/hash_df.h | 2 +- qemu/roms/ipxe/src/include/ipxe/hidemem.h | 2 +- qemu/roms/ipxe/src/include/ipxe/hmac.h | 2 +- qemu/roms/ipxe/src/include/ipxe/hmac_drbg.h | 2 +- qemu/roms/ipxe/src/include/ipxe/http.h | 490 +- qemu/roms/ipxe/src/include/ipxe/hyperv.h | 232 + qemu/roms/ipxe/src/include/ipxe/i2c.h | 2 +- qemu/roms/ipxe/src/include/ipxe/ib_cm.h | 2 +- qemu/roms/ipxe/src/include/ipxe/ib_mad.h | 2 +- qemu/roms/ipxe/src/include/ipxe/ib_mcast.h | 2 +- qemu/roms/ipxe/src/include/ipxe/ib_mi.h | 2 +- qemu/roms/ipxe/src/include/ipxe/ib_packet.h | 2 +- qemu/roms/ipxe/src/include/ipxe/ib_pathrec.h | 2 +- qemu/roms/ipxe/src/include/ipxe/ib_sma.h | 2 +- qemu/roms/ipxe/src/include/ipxe/ib_smc.h | 2 +- qemu/roms/ipxe/src/include/ipxe/icmp.h | 2 +- qemu/roms/ipxe/src/include/ipxe/icmpv6.h | 14 +- qemu/roms/ipxe/src/include/ipxe/if_arp.h | 2 +- qemu/roms/ipxe/src/include/ipxe/if_ether.h | 2 +- qemu/roms/ipxe/src/include/ipxe/image.h | 3 +- qemu/roms/ipxe/src/include/ipxe/in.h | 40 +- qemu/roms/ipxe/src/include/ipxe/infiniband.h | 2 +- qemu/roms/ipxe/src/include/ipxe/init.h | 9 +- qemu/roms/ipxe/src/include/ipxe/interface.h | 7 +- qemu/roms/ipxe/src/include/ipxe/io.h | 2 +- qemu/roms/ipxe/src/include/ipxe/iobuf.h | 3 +- qemu/roms/ipxe/src/include/ipxe/ip.h | 2 +- qemu/roms/ipxe/src/include/ipxe/ipoib.h | 2 +- qemu/roms/ipxe/src/include/ipxe/ipstat.h | 2 +- qemu/roms/ipxe/src/include/ipxe/ipv6.h | 2 +- qemu/roms/ipxe/src/include/ipxe/isa_ids.h | 2 +- qemu/roms/ipxe/src/include/ipxe/isapnp.h | 6 +- qemu/roms/ipxe/src/include/ipxe/iscsi.h | 2 +- qemu/roms/ipxe/src/include/ipxe/iso9660.h | 2 +- qemu/roms/ipxe/src/include/ipxe/isqrt.h | 2 +- qemu/roms/ipxe/src/include/ipxe/job.h | 2 +- qemu/roms/ipxe/src/include/ipxe/jumpscroll.h | 50 + qemu/roms/ipxe/src/include/ipxe/keymap.h | 2 +- qemu/roms/ipxe/src/include/ipxe/keys.h | 4 +- qemu/roms/ipxe/src/include/ipxe/linebuf.h | 14 +- qemu/roms/ipxe/src/include/ipxe/lineconsole.h | 2 +- .../ipxe/src/include/ipxe/linux/linux_entropy.h | 12 +- qemu/roms/ipxe/src/include/ipxe/linux/linux_nap.h | 2 +- qemu/roms/ipxe/src/include/ipxe/linux/linux_pci.h | 2 +- .../ipxe/src/include/ipxe/linux/linux_smbios.h | 4 +- qemu/roms/ipxe/src/include/ipxe/linux/linux_time.h | 2 +- .../roms/ipxe/src/include/ipxe/linux/linux_timer.h | 2 +- .../ipxe/src/include/ipxe/linux/linux_uaccess.h | 126 +- .../ipxe/src/include/ipxe/linux/linux_umalloc.h | 6 +- qemu/roms/ipxe/src/include/ipxe/linux_compat.h | 2 +- qemu/roms/ipxe/src/include/ipxe/list.h | 2 +- qemu/roms/ipxe/src/include/ipxe/login_ui.h | 2 +- qemu/roms/ipxe/src/include/ipxe/malloc.h | 4 +- qemu/roms/ipxe/src/include/ipxe/mca.h | 2 +- qemu/roms/ipxe/src/include/ipxe/md5.h | 2 +- qemu/roms/ipxe/src/include/ipxe/memblock.h | 2 +- qemu/roms/ipxe/src/include/ipxe/menu.h | 2 +- qemu/roms/ipxe/src/include/ipxe/mii.h | 4 +- qemu/roms/ipxe/src/include/ipxe/monojob.h | 2 +- qemu/roms/ipxe/src/include/ipxe/mount.h | 2 +- qemu/roms/ipxe/src/include/ipxe/nap.h | 2 +- qemu/roms/ipxe/src/include/ipxe/ndp.h | 2 +- qemu/roms/ipxe/src/include/ipxe/neighbour.h | 2 +- qemu/roms/ipxe/src/include/ipxe/net80211_err.h | 2 +- qemu/roms/ipxe/src/include/ipxe/netdevice.h | 41 +- qemu/roms/ipxe/src/include/ipxe/nfs.h | 2 +- qemu/roms/ipxe/src/include/ipxe/nfs_open.h | 2 +- qemu/roms/ipxe/src/include/ipxe/nfs_uri.h | 2 +- qemu/roms/ipxe/src/include/ipxe/null_entropy.h | 2 +- qemu/roms/ipxe/src/include/ipxe/null_nap.h | 2 +- qemu/roms/ipxe/src/include/ipxe/null_reboot.h | 2 +- qemu/roms/ipxe/src/include/ipxe/null_sanboot.h | 2 +- qemu/roms/ipxe/src/include/ipxe/null_time.h | 2 +- qemu/roms/ipxe/src/include/ipxe/nvo.h | 2 +- qemu/roms/ipxe/src/include/ipxe/nvs.h | 2 +- qemu/roms/ipxe/src/include/ipxe/nvsvpd.h | 2 +- qemu/roms/ipxe/src/include/ipxe/ocsp.h | 2 +- qemu/roms/ipxe/src/include/ipxe/oncrpc.h | 2 +- qemu/roms/ipxe/src/include/ipxe/oncrpc_iob.h | 2 +- qemu/roms/ipxe/src/include/ipxe/open.h | 2 +- qemu/roms/ipxe/src/include/ipxe/params.h | 2 +- qemu/roms/ipxe/src/include/ipxe/parseopt.h | 2 +- qemu/roms/ipxe/src/include/ipxe/pccrc.h | 447 ++ qemu/roms/ipxe/src/include/ipxe/pccrd.h | 47 + qemu/roms/ipxe/src/include/ipxe/pccrr.h | 376 + qemu/roms/ipxe/src/include/ipxe/pci.h | 382 +- qemu/roms/ipxe/src/include/ipxe/pci_ids.h | 351 - qemu/roms/ipxe/src/include/ipxe/pci_io.h | 2 +- qemu/roms/ipxe/src/include/ipxe/pcibackup.h | 2 +- qemu/roms/ipxe/src/include/ipxe/pcivpd.h | 2 +- qemu/roms/ipxe/src/include/ipxe/peerblk.h | 144 + qemu/roms/ipxe/src/include/ipxe/peerdisc.h | 116 + qemu/roms/ipxe/src/include/ipxe/peermux.h | 73 + qemu/roms/ipxe/src/include/ipxe/pending.h | 2 +- qemu/roms/ipxe/src/include/ipxe/ping.h | 2 +- qemu/roms/ipxe/src/include/ipxe/pinger.h | 2 +- qemu/roms/ipxe/src/include/ipxe/pixbuf.h | 2 +- qemu/roms/ipxe/src/include/ipxe/png.h | 2 +- qemu/roms/ipxe/src/include/ipxe/pnm.h | 2 +- qemu/roms/ipxe/src/include/ipxe/pool.h | 127 + qemu/roms/ipxe/src/include/ipxe/portmap.h | 2 +- qemu/roms/ipxe/src/include/ipxe/posix_io.h | 2 +- qemu/roms/ipxe/src/include/ipxe/privkey.h | 2 +- qemu/roms/ipxe/src/include/ipxe/process.h | 2 +- qemu/roms/ipxe/src/include/ipxe/profile.h | 16 +- qemu/roms/ipxe/src/include/ipxe/random_nz.h | 2 +- qemu/roms/ipxe/src/include/ipxe/rarp.h | 2 +- qemu/roms/ipxe/src/include/ipxe/rbg.h | 2 +- qemu/roms/ipxe/src/include/ipxe/reboot.h | 2 +- qemu/roms/ipxe/src/include/ipxe/refcnt.h | 2 +- qemu/roms/ipxe/src/include/ipxe/resolv.h | 2 +- qemu/roms/ipxe/src/include/ipxe/retry.h | 36 +- qemu/roms/ipxe/src/include/ipxe/rndis.h | 370 + qemu/roms/ipxe/src/include/ipxe/rootcert.h | 2 +- qemu/roms/ipxe/src/include/ipxe/rotate.h | 22 +- qemu/roms/ipxe/src/include/ipxe/rsa.h | 3 +- qemu/roms/ipxe/src/include/ipxe/sanboot.h | 2 +- qemu/roms/ipxe/src/include/ipxe/script.h | 2 +- qemu/roms/ipxe/src/include/ipxe/scsi.h | 2 +- qemu/roms/ipxe/src/include/ipxe/segment.h | 2 +- qemu/roms/ipxe/src/include/ipxe/serial.h | 11 +- qemu/roms/ipxe/src/include/ipxe/settings.h | 3 +- qemu/roms/ipxe/src/include/ipxe/settings_ui.h | 2 +- qemu/roms/ipxe/src/include/ipxe/sha256.h | 17 +- qemu/roms/ipxe/src/include/ipxe/sha512.h | 98 + qemu/roms/ipxe/src/include/ipxe/shell.h | 2 +- qemu/roms/ipxe/src/include/ipxe/smbios.h | 2 +- qemu/roms/ipxe/src/include/ipxe/socket.h | 2 +- qemu/roms/ipxe/src/include/ipxe/spi.h | 2 +- qemu/roms/ipxe/src/include/ipxe/spi_bit.h | 2 +- qemu/roms/ipxe/src/include/ipxe/stp.h | 76 + qemu/roms/ipxe/src/include/ipxe/string.h | 14 + qemu/roms/ipxe/src/include/ipxe/syslog.h | 2 +- qemu/roms/ipxe/src/include/ipxe/tables.h | 2 +- qemu/roms/ipxe/src/include/ipxe/tcp.h | 53 +- qemu/roms/ipxe/src/include/ipxe/tcpip.h | 11 +- qemu/roms/ipxe/src/include/ipxe/test.h | 2 +- qemu/roms/ipxe/src/include/ipxe/tftp.h | 2 +- qemu/roms/ipxe/src/include/ipxe/time.h | 3 +- qemu/roms/ipxe/src/include/ipxe/timer.h | 2 +- qemu/roms/ipxe/src/include/ipxe/tls.h | 30 +- qemu/roms/ipxe/src/include/ipxe/uaccess.h | 2 +- qemu/roms/ipxe/src/include/ipxe/uart.h | 132 + qemu/roms/ipxe/src/include/ipxe/udp.h | 2 +- qemu/roms/ipxe/src/include/ipxe/umalloc.h | 2 +- qemu/roms/ipxe/src/include/ipxe/uri.h | 4 +- qemu/roms/ipxe/src/include/ipxe/usb.h | 1319 ++++ qemu/roms/ipxe/src/include/ipxe/usbhid.h | 106 + qemu/roms/ipxe/src/include/ipxe/usbnet.h | 62 + qemu/roms/ipxe/src/include/ipxe/uuid.h | 2 +- qemu/roms/ipxe/src/include/ipxe/validator.h | 2 +- qemu/roms/ipxe/src/include/ipxe/version.h | 2 +- qemu/roms/ipxe/src/include/ipxe/vlan.h | 2 +- qemu/roms/ipxe/src/include/ipxe/vmbus.h | 634 ++ qemu/roms/ipxe/src/include/ipxe/vsprintf.h | 2 +- qemu/roms/ipxe/src/include/ipxe/x509.h | 2 +- qemu/roms/ipxe/src/include/ipxe/xen.h | 2 +- qemu/roms/ipxe/src/include/ipxe/xenbus.h | 2 +- qemu/roms/ipxe/src/include/ipxe/xenevent.h | 2 +- qemu/roms/ipxe/src/include/ipxe/xengrant.h | 2 +- qemu/roms/ipxe/src/include/ipxe/xenmem.h | 2 +- qemu/roms/ipxe/src/include/ipxe/xenstore.h | 2 +- qemu/roms/ipxe/src/include/ipxe/xenver.h | 2 +- qemu/roms/ipxe/src/include/ipxe/xfer.h | 4 +- qemu/roms/ipxe/src/include/ipxe/xferbuf.h | 78 +- qemu/roms/ipxe/src/include/libgen.h | 2 +- qemu/roms/ipxe/src/include/little_bswap.h | 37 - qemu/roms/ipxe/src/include/nic.h | 19 +- qemu/roms/ipxe/src/include/readline/readline.h | 2 +- qemu/roms/ipxe/src/include/stdarg.h | 2 +- qemu/roms/ipxe/src/include/stddef.h | 40 +- qemu/roms/ipxe/src/include/stdint.h | 2 +- qemu/roms/ipxe/src/include/stdio.h | 2 +- qemu/roms/ipxe/src/include/stdlib.h | 30 +- qemu/roms/ipxe/src/include/string.h | 83 +- qemu/roms/ipxe/src/include/strings.h | 145 +- qemu/roms/ipxe/src/include/sys/time.h | 2 +- qemu/roms/ipxe/src/include/syslog.h | 2 +- qemu/roms/ipxe/src/include/time.h | 2 +- qemu/roms/ipxe/src/include/unistd.h | 2 +- qemu/roms/ipxe/src/include/usr/autoboot.h | 4 +- qemu/roms/ipxe/src/include/usr/dhcpmgmt.h | 2 +- qemu/roms/ipxe/src/include/usr/fcmgmt.h | 2 +- qemu/roms/ipxe/src/include/usr/ifmgmt.h | 2 +- qemu/roms/ipxe/src/include/usr/imgmgmt.h | 2 +- qemu/roms/ipxe/src/include/usr/imgtrust.h | 2 +- qemu/roms/ipxe/src/include/usr/ipstat.h | 2 +- qemu/roms/ipxe/src/include/usr/lotest.h | 2 +- qemu/roms/ipxe/src/include/usr/neighmgmt.h | 2 +- qemu/roms/ipxe/src/include/usr/pingmgmt.h | 2 +- qemu/roms/ipxe/src/include/usr/profstat.h | 2 +- qemu/roms/ipxe/src/include/usr/prompt.h | 2 +- qemu/roms/ipxe/src/include/usr/route.h | 2 +- qemu/roms/ipxe/src/include/usr/sync.h | 2 +- .../src/{arch/x86 => }/include/valgrind/memcheck.h | 0 .../src/{arch/x86 => }/include/valgrind/valgrind.h | 0 qemu/roms/ipxe/src/include/wchar.h | 2 +- qemu/roms/ipxe/src/interface/bofm/bofm.c | 6 +- qemu/roms/ipxe/src/interface/efi/efi_autoboot.c | 6 +- qemu/roms/ipxe/src/interface/efi/efi_bofm.c | 6 +- qemu/roms/ipxe/src/interface/efi/efi_debug.c | 8 +- qemu/roms/ipxe/src/interface/efi/efi_file.c | 6 +- qemu/roms/ipxe/src/interface/efi/efi_guid.c | 6 +- qemu/roms/ipxe/src/interface/efi/efi_hii.c | 6 +- qemu/roms/ipxe/src/interface/efi/efi_pci.c | 6 +- qemu/roms/ipxe/src/interface/efi/efi_reboot.c | 6 +- qemu/roms/ipxe/src/interface/efi/efi_snp.c | 181 +- qemu/roms/ipxe/src/interface/efi/efi_snp_hii.c | 9 +- qemu/roms/ipxe/src/interface/efi/efi_strings.c | 6 +- qemu/roms/ipxe/src/interface/efi/efi_time.c | 75 + qemu/roms/ipxe/src/interface/efi/efi_timer.c | 6 +- qemu/roms/ipxe/src/interface/efi/efi_uaccess.c | 6 +- qemu/roms/ipxe/src/interface/efi/efi_umalloc.c | 6 +- qemu/roms/ipxe/src/interface/efi/efi_watchdog.c | 82 + qemu/roms/ipxe/src/interface/efi/efi_wrap.c | 6 +- qemu/roms/ipxe/src/interface/hyperv/vmbus.c | 1333 ++++ qemu/roms/ipxe/src/interface/linux/linux_entropy.c | 6 +- qemu/roms/ipxe/src/interface/linux/linux_pci.c | 6 +- qemu/roms/ipxe/src/interface/linux/linux_time.c | 6 +- qemu/roms/ipxe/src/interface/linux/linux_uaccess.c | 1 - qemu/roms/ipxe/src/interface/smbios/smbios.c | 6 +- .../ipxe/src/interface/smbios/smbios_settings.c | 6 +- qemu/roms/ipxe/src/interface/xen/xenbus.c | 6 +- qemu/roms/ipxe/src/interface/xen/xengrant.c | 6 +- qemu/roms/ipxe/src/interface/xen/xenstore.c | 10 +- qemu/roms/ipxe/src/net/80211/net80211.c | 10 + qemu/roms/ipxe/src/net/80211/wpa.c | 1 + qemu/roms/ipxe/src/net/80211/wpa_ccmp.c | 2 +- qemu/roms/ipxe/src/net/80211/wpa_tkip.c | 2 +- qemu/roms/ipxe/src/net/aoe.c | 6 +- qemu/roms/ipxe/src/net/arp.c | 12 +- qemu/roms/ipxe/src/net/dhcpopts.c | 6 +- qemu/roms/ipxe/src/net/dhcppkt.c | 6 +- qemu/roms/ipxe/src/net/eth_slow.c | 6 +- qemu/roms/ipxe/src/net/ethernet.c | 50 +- qemu/roms/ipxe/src/net/fakedhcp.c | 6 +- qemu/roms/ipxe/src/net/fc.c | 12 +- qemu/roms/ipxe/src/net/fcels.c | 6 +- qemu/roms/ipxe/src/net/fcns.c | 6 +- qemu/roms/ipxe/src/net/fcoe.c | 6 +- qemu/roms/ipxe/src/net/fcp.c | 6 +- qemu/roms/ipxe/src/net/fragment.c | 6 +- qemu/roms/ipxe/src/net/icmp.c | 6 +- qemu/roms/ipxe/src/net/icmpv4.c | 6 +- qemu/roms/ipxe/src/net/icmpv6.c | 86 +- qemu/roms/ipxe/src/net/infiniband.c | 18 +- qemu/roms/ipxe/src/net/infiniband/ib_cm.c | 6 +- qemu/roms/ipxe/src/net/infiniband/ib_mcast.c | 12 +- qemu/roms/ipxe/src/net/infiniband/ib_mi.c | 6 +- qemu/roms/ipxe/src/net/infiniband/ib_packet.c | 6 +- qemu/roms/ipxe/src/net/infiniband/ib_pathrec.c | 6 +- qemu/roms/ipxe/src/net/infiniband/ib_sma.c | 6 +- qemu/roms/ipxe/src/net/infiniband/ib_smc.c | 6 +- qemu/roms/ipxe/src/net/infiniband/ib_srp.c | 2 +- qemu/roms/ipxe/src/net/iobpad.c | 6 +- qemu/roms/ipxe/src/net/ipv4.c | 130 +- qemu/roms/ipxe/src/net/ipv6.c | 22 +- qemu/roms/ipxe/src/net/neighbour.c | 12 +- qemu/roms/ipxe/src/net/netdev_settings.c | 10 +- qemu/roms/ipxe/src/net/netdevice.c | 74 +- qemu/roms/ipxe/src/net/nullnet.c | 6 +- qemu/roms/ipxe/src/net/pccrc.c | 818 +++ qemu/roms/ipxe/src/net/pccrd.c | 286 + qemu/roms/ipxe/src/net/peerblk.c | 1366 ++++ qemu/roms/ipxe/src/net/peerdisc.c | 551 ++ qemu/roms/ipxe/src/net/peerdist.c | 145 + qemu/roms/ipxe/src/net/peermux.c | 387 + qemu/roms/ipxe/src/net/ping.c | 6 +- qemu/roms/ipxe/src/net/rarp.c | 6 +- qemu/roms/ipxe/src/net/retry.c | 90 +- qemu/roms/ipxe/src/net/rndis.c | 1052 +++ qemu/roms/ipxe/src/net/socket.c | 6 +- qemu/roms/ipxe/src/net/stp.c | 152 + qemu/roms/ipxe/src/net/tcp.c | 245 +- qemu/roms/ipxe/src/net/tcp/http.c | 26 +- qemu/roms/ipxe/src/net/tcp/httpauth.c | 190 + qemu/roms/ipxe/src/net/tcp/httpbasic.c | 102 + qemu/roms/ipxe/src/net/tcp/httpblock.c | 134 + qemu/roms/ipxe/src/net/tcp/httpconn.c | 309 + qemu/roms/ipxe/src/net/tcp/httpcore.c | 2694 ++++--- qemu/roms/ipxe/src/net/tcp/httpdigest.c | 234 + qemu/roms/ipxe/src/net/tcp/https.c | 27 +- qemu/roms/ipxe/src/net/tcp/iscsi.c | 76 +- qemu/roms/ipxe/src/net/tcp/syslogs.c | 6 +- qemu/roms/ipxe/src/net/tcpip.c | 4 +- qemu/roms/ipxe/src/net/tls.c | 146 +- qemu/roms/ipxe/src/net/udp.c | 2 +- qemu/roms/ipxe/src/net/udp/dhcp.c | 133 +- qemu/roms/ipxe/src/net/udp/dhcpv6.c | 6 +- qemu/roms/ipxe/src/net/udp/dns.c | 6 +- qemu/roms/ipxe/src/net/udp/slam.c | 6 +- qemu/roms/ipxe/src/net/udp/syslog.c | 6 +- qemu/roms/ipxe/src/net/udp/tftp.c | 60 +- qemu/roms/ipxe/src/net/validator.c | 14 +- qemu/roms/ipxe/src/net/vlan.c | 10 +- qemu/roms/ipxe/src/tests/aes_cbc_test.c | 193 - qemu/roms/ipxe/src/tests/aes_test.c | 193 + qemu/roms/ipxe/src/tests/base16_test.c | 52 +- qemu/roms/ipxe/src/tests/base64_test.c | 52 +- qemu/roms/ipxe/src/tests/bigint_test.c | 6 +- qemu/roms/ipxe/src/tests/bofm_test.c | 6 +- qemu/roms/ipxe/src/tests/byteswap_test.c | 6 +- qemu/roms/ipxe/src/tests/cbc_test.c | 177 - qemu/roms/ipxe/src/tests/cbc_test.h | 57 - qemu/roms/ipxe/src/tests/cipher_test.c | 185 + qemu/roms/ipxe/src/tests/cipher_test.h | 111 + qemu/roms/ipxe/src/tests/cms_test.c | 7 +- qemu/roms/ipxe/src/tests/crc32_test.c | 6 +- qemu/roms/ipxe/src/tests/deflate_test.c | 6 +- qemu/roms/ipxe/src/tests/digest_test.c | 69 +- qemu/roms/ipxe/src/tests/digest_test.h | 120 +- qemu/roms/ipxe/src/tests/dns_test.c | 6 +- qemu/roms/ipxe/src/tests/entropy_sample.c | 6 +- qemu/roms/ipxe/src/tests/hash_df_test.c | 6 +- qemu/roms/ipxe/src/tests/hmac_drbg_test.c | 6 +- qemu/roms/ipxe/src/tests/ipv4_test.c | 154 + qemu/roms/ipxe/src/tests/ipv6_test.c | 6 +- qemu/roms/ipxe/src/tests/linebuf_test.c | 337 +- qemu/roms/ipxe/src/tests/list_test.c | 6 +- qemu/roms/ipxe/src/tests/math_test.c | 80 +- qemu/roms/ipxe/src/tests/md5_test.c | 82 +- qemu/roms/ipxe/src/tests/memcpy_test.c | 6 +- qemu/roms/ipxe/src/tests/memset_test.c | 157 + qemu/roms/ipxe/src/tests/ocsp_test.c | 7 +- qemu/roms/ipxe/src/tests/pccrc_test.c | 529 ++ qemu/roms/ipxe/src/tests/pixbuf_test.c | 13 +- qemu/roms/ipxe/src/tests/pixbuf_test.h | 2 +- qemu/roms/ipxe/src/tests/png_test.c | 6 +- qemu/roms/ipxe/src/tests/pnm_test.c | 6 +- qemu/roms/ipxe/src/tests/profile_test.c | 6 +- qemu/roms/ipxe/src/tests/pubkey_test.h | 2 +- qemu/roms/ipxe/src/tests/rsa_test.c | 6 +- qemu/roms/ipxe/src/tests/setjmp_test.c | 171 + qemu/roms/ipxe/src/tests/settings_test.c | 25 +- qemu/roms/ipxe/src/tests/sha1_test.c | 92 +- qemu/roms/ipxe/src/tests/sha256_test.c | 131 +- qemu/roms/ipxe/src/tests/sha512_test.c | 185 + qemu/roms/ipxe/src/tests/string_test.c | 142 +- qemu/roms/ipxe/src/tests/tcpip_test.c | 6 +- qemu/roms/ipxe/src/tests/test.c | 6 +- qemu/roms/ipxe/src/tests/tests.c | 15 +- qemu/roms/ipxe/src/tests/time_test.c | 6 +- qemu/roms/ipxe/src/tests/uri_test.c | 30 +- qemu/roms/ipxe/src/tests/vsprintf_test.c | 6 +- qemu/roms/ipxe/src/tests/x509_test.c | 7 +- qemu/roms/ipxe/src/usr/autoboot.c | 49 +- qemu/roms/ipxe/src/usr/dhcpmgmt.c | 6 +- qemu/roms/ipxe/src/usr/fcmgmt.c | 6 +- qemu/roms/ipxe/src/usr/ifmgmt.c | 9 +- qemu/roms/ipxe/src/usr/imgmgmt.c | 6 +- qemu/roms/ipxe/src/usr/imgtrust.c | 6 +- qemu/roms/ipxe/src/usr/ipstat.c | 6 +- qemu/roms/ipxe/src/usr/lotest.c | 6 +- qemu/roms/ipxe/src/usr/neighmgmt.c | 6 +- qemu/roms/ipxe/src/usr/pingmgmt.c | 6 +- qemu/roms/ipxe/src/usr/profstat.c | 6 +- qemu/roms/ipxe/src/usr/prompt.c | 6 +- qemu/roms/ipxe/src/usr/pxemenu.c | 6 +- qemu/roms/ipxe/src/usr/route.c | 10 +- qemu/roms/ipxe/src/usr/route_ipv4.c | 6 +- qemu/roms/ipxe/src/usr/route_ipv6.c | 6 +- qemu/roms/ipxe/src/usr/sync.c | 6 +- qemu/roms/ipxe/src/util/Option/ROM.pm | 20 + qemu/roms/ipxe/src/util/disrom.pl | 4 + qemu/roms/ipxe/src/util/elf2efi.c | 6 +- qemu/roms/ipxe/src/util/licence.pl | 13 + qemu/roms/ipxe/src/util/parserom.pl | 296 +- qemu/roms/ipxe/src/util/relicense.pl | 169 + qemu/roms/ipxe/src/util/zbin.c | 96 +- qemu/roms/openbios/Makefile.target | 2 +- qemu/roms/openbios/arch/ppc/qemu/init.c | 67 + qemu/roms/openbios/arch/ppc/qemu/methods.c | 36 +- qemu/roms/openbios/arch/ppc/qemu/qemu.fs | 45 + qemu/roms/openbios/arch/ppc/qemu/tree.fs | 8 + qemu/roms/openbios/arch/sparc64/call-client.S | 218 +- qemu/roms/openbios/arch/sparc64/context.c | 4 + qemu/roms/openbios/arch/sparc64/cpustate.h | 244 + qemu/roms/openbios/arch/sparc64/ldscript | 5 + qemu/roms/openbios/arch/sparc64/lib.c | 4 +- qemu/roms/openbios/arch/sparc64/vectors.S | 300 +- qemu/roms/openbios/config/scripts/switch-arch | 118 +- qemu/roms/openbios/drivers/cuda.c | 21 +- qemu/roms/openbios/drivers/escc.c | 112 +- qemu/roms/openbios/drivers/escc.h | 2 + qemu/roms/openbios/drivers/ide.c | 82 +- qemu/roms/openbios/drivers/ide.h | 2 +- qemu/roms/openbios/drivers/obio.c | 66 - qemu/roms/openbios/drivers/pci.c | 147 +- qemu/roms/openbios/drivers/pci.fs | 68 +- qemu/roms/openbios/drivers/pci.h | 9 + qemu/roms/openbios/drivers/sbus.c | 25 - qemu/roms/openbios/drivers/usbohci_private.h | 28 +- qemu/roms/openbios/drivers/vga.fs | 11 +- qemu/roms/openbios/forth/lib/string.fs | 14 + qemu/roms/openbios/forth/system/ciface.fs | 8 + qemu/roms/openbios/include/arch/ppc/types.h | 8 - qemu/roms/openbios/include/arch/sparc64/io.h | 2 +- qemu/roms/openbios/include/drivers/drivers.h | 1 + qemu/roms/openbios/include/libopenbios/bindings.h | 1 + qemu/roms/openbios/libopenbios/bindings.c | 8 + qemu/roms/openbios/libopenbios/bootinfo_load.c | 6 + qemu/roms/seabios/.version | 2 +- qemu/roms/seabios/Makefile | 28 +- qemu/roms/seabios/docs/Build_overview.md | 34 +- qemu/roms/seabios/docs/Contributing.md | 20 + qemu/roms/seabios/docs/Debugging.md | 27 +- qemu/roms/seabios/docs/Developer_Documentation.md | 11 +- qemu/roms/seabios/docs/Download.md | 6 +- qemu/roms/seabios/docs/Execution_and_code_flow.md | 2 +- qemu/roms/seabios/docs/Linking_overview.md | 12 +- qemu/roms/seabios/docs/Releases.md | 29 + qemu/roms/seabios/docs/Runtime_config.md | 191 + qemu/roms/seabios/docs/SeaBIOS.md | 2 + qemu/roms/seabios/docs/SeaVGABIOS.md | 39 + qemu/roms/seabios/scripts/buildversion.py | 134 + qemu/roms/seabios/scripts/buildversion.sh | 31 - qemu/roms/seabios/scripts/checkrom.py | 2 +- qemu/roms/seabios/scripts/checkstack.py | 270 +- qemu/roms/seabios/scripts/kconfig/lxdialog/util.c | 2 +- qemu/roms/seabios/scripts/layoutrom.py | 8 + qemu/roms/seabios/src/Kconfig | 59 +- qemu/roms/seabios/src/biosvar.h | 8 +- qemu/roms/seabios/src/block.c | 248 +- qemu/roms/seabios/src/block.h | 16 +- qemu/roms/seabios/src/bmp.c | 2 +- qemu/roms/seabios/src/boot.c | 22 +- qemu/roms/seabios/src/cdrom.c | 23 +- qemu/roms/seabios/src/clock.c | 57 +- qemu/roms/seabios/src/config.h | 3 + qemu/roms/seabios/src/disk.c | 5 +- qemu/roms/seabios/src/e820map.c | 152 + qemu/roms/seabios/src/e820map.h | 26 + qemu/roms/seabios/src/fw/biostables.c | 66 +- qemu/roms/seabios/src/fw/coreboot.c | 17 +- qemu/roms/seabios/src/fw/csm.c | 23 +- qemu/roms/seabios/src/fw/dev-q35.h | 3 + qemu/roms/seabios/src/fw/multiboot.c | 111 + qemu/roms/seabios/src/fw/paravirt.c | 93 +- qemu/roms/seabios/src/fw/paravirt.h | 26 +- qemu/roms/seabios/src/fw/pciinit.c | 24 +- qemu/roms/seabios/src/fw/shadow.c | 12 +- qemu/roms/seabios/src/fw/smbios.c | 2 +- qemu/roms/seabios/src/fw/smm.c | 8 +- qemu/roms/seabios/src/fw/smp.c | 3 - qemu/roms/seabios/src/fw/xen.c | 9 +- qemu/roms/seabios/src/hw/ahci.c | 36 +- qemu/roms/seabios/src/hw/ahci.h | 4 +- qemu/roms/seabios/src/hw/ata.c | 42 +- qemu/roms/seabios/src/hw/ata.h | 6 +- qemu/roms/seabios/src/hw/blockcmd.c | 139 +- qemu/roms/seabios/src/hw/blockcmd.h | 4 +- qemu/roms/seabios/src/hw/esp-scsi.c | 33 +- qemu/roms/seabios/src/hw/esp-scsi.h | 2 +- qemu/roms/seabios/src/hw/floppy.c | 2 +- qemu/roms/seabios/src/hw/lsi-scsi.c | 32 +- qemu/roms/seabios/src/hw/lsi-scsi.h | 2 +- qemu/roms/seabios/src/hw/megasas.c | 26 +- qemu/roms/seabios/src/hw/megasas.h | 2 +- qemu/roms/seabios/src/hw/pci.c | 11 +- qemu/roms/seabios/src/hw/pci.h | 2 +- qemu/roms/seabios/src/hw/pci_ids.h | 8 +- qemu/roms/seabios/src/hw/pic.c | 14 + qemu/roms/seabios/src/hw/pic.h | 4 + qemu/roms/seabios/src/hw/ps2port.c | 53 +- qemu/roms/seabios/src/hw/ps2port.h | 3 +- qemu/roms/seabios/src/hw/pvscsi.c | 64 +- qemu/roms/seabios/src/hw/pvscsi.h | 2 +- qemu/roms/seabios/src/hw/ramdisk.c | 17 +- qemu/roms/seabios/src/hw/rtc.c | 7 + qemu/roms/seabios/src/hw/sdcard.c | 415 +- qemu/roms/seabios/src/hw/timer.c | 25 +- qemu/roms/seabios/src/hw/tpm_drivers.c | 291 + qemu/roms/seabios/src/hw/tpm_drivers.h | 90 + qemu/roms/seabios/src/hw/usb-hid.h | 8 +- qemu/roms/seabios/src/hw/usb-msc.c | 14 +- qemu/roms/seabios/src/hw/usb-msc.h | 2 +- qemu/roms/seabios/src/hw/usb-uas.c | 6 +- qemu/roms/seabios/src/hw/usb-uas.h | 2 +- qemu/roms/seabios/src/hw/usb-xhci.c | 46 +- qemu/roms/seabios/src/hw/usb.c | 9 +- qemu/roms/seabios/src/hw/virtio-blk.c | 126 +- qemu/roms/seabios/src/hw/virtio-blk.h | 2 +- qemu/roms/seabios/src/hw/virtio-pci.c | 228 +- qemu/roms/seabios/src/hw/virtio-pci.h | 260 +- qemu/roms/seabios/src/hw/virtio-ring.c | 65 +- qemu/roms/seabios/src/hw/virtio-ring.h | 45 +- qemu/roms/seabios/src/hw/virtio-scsi.c | 98 +- qemu/roms/seabios/src/hw/virtio-scsi.h | 2 +- qemu/roms/seabios/src/list.h | 10 + qemu/roms/seabios/src/malloc.c | 296 +- qemu/roms/seabios/src/malloc.h | 13 +- qemu/roms/seabios/src/memmap.c | 144 - qemu/roms/seabios/src/memmap.h | 38 +- qemu/roms/seabios/src/misc.c | 2 +- qemu/roms/seabios/src/mouse.c | 3 +- qemu/roms/seabios/src/optionroms.c | 39 +- qemu/roms/seabios/src/output.c | 1 + qemu/roms/seabios/src/pmm.c | 16 +- qemu/roms/seabios/src/post.c | 60 +- qemu/roms/seabios/src/resume.c | 3 + qemu/roms/seabios/src/romlayout.S | 103 +- qemu/roms/seabios/src/sha1.c | 147 + qemu/roms/seabios/src/sha1.h | 8 + qemu/roms/seabios/src/stacks.c | 382 +- qemu/roms/seabios/src/stacks.h | 27 +- qemu/roms/seabios/src/std/acpi.h | 20 + qemu/roms/seabios/src/std/bda.h | 2 +- qemu/roms/seabios/src/std/multiboot.h | 260 + qemu/roms/seabios/src/std/smbios.h | 4 +- qemu/roms/seabios/src/string.c | 2 +- qemu/roms/seabios/src/string.h | 8 +- qemu/roms/seabios/src/system.c | 2 +- qemu/roms/seabios/src/tcgbios.c | 1480 ++++ qemu/roms/seabios/src/tcgbios.h | 375 + qemu/roms/seabios/src/types.h | 2 +- qemu/roms/seabios/src/util.h | 16 +- qemu/roms/seabios/src/version.c | 5 + qemu/roms/seabios/src/vgahooks.c | 2 +- qemu/roms/seabios/src/x86.h | 27 +- qemu/roms/seabios/vgasrc/Kconfig | 29 +- qemu/roms/seabios/vgasrc/geodevga.h | 2 +- qemu/roms/seabios/vgasrc/vgabios.c | 6 + qemu/roms/seabios/vgasrc/vgabios.h | 4 +- qemu/roms/seabios/vgasrc/vgaentry.S | 18 +- qemu/roms/seabios/vgasrc/vgafb.c | 17 +- qemu/roms/seabios/vgasrc/vgainit.c | 1 + qemu/roms/seabios/vgasrc/vgaversion.c | 6 + qemu/rules.mak | 24 +- qemu/scripts/acpi_extract.py | 367 - qemu/scripts/acpi_extract_preprocess.py | 51 - qemu/scripts/analyze-migration.py | 13 + qemu/scripts/checkpatch.pl | 615 +- qemu/scripts/clean-includes | 165 + qemu/scripts/cocci-macro-file.h | 119 + qemu/scripts/coverity-model.c | 26 + qemu/scripts/create_config | 9 + qemu/scripts/dump-guest-memory.py | 762 +- qemu/scripts/feature_to_c.sh | 2 +- qemu/scripts/get_maintainer.pl | 24 +- qemu/scripts/kvm/kvm_stat | 1211 ++-- qemu/scripts/ordereddict.py | 3 +- qemu/scripts/qapi-commands.py | 381 +- qemu/scripts/qapi-event.py | 286 +- qemu/scripts/qapi-introspect.py | 219 + qemu/scripts/qapi-types.py | 437 +- qemu/scripts/qapi-visit.py | 523 +- qemu/scripts/qapi.py | 1225 +++- qemu/scripts/qemu-gdb.py | 151 +- qemu/scripts/qemugdb/__init__.py | 28 + qemu/scripts/qemugdb/aio.py | 58 + qemu/scripts/qemugdb/coroutine.py | 119 + qemu/scripts/qemugdb/mtree.py | 82 + qemu/scripts/qmp/qemu-ga-client | 2 +- qemu/scripts/qmp/qmp | 4 +- qemu/scripts/qmp/qmp-shell | 60 +- qemu/scripts/qmp/qmp.py | 4 +- qemu/scripts/texi2pod.pl | 2 +- qemu/scripts/tracetool.py | 4 +- qemu/scripts/tracetool/__init__.py | 48 +- qemu/scripts/tracetool/backend/log.py | 44 + qemu/scripts/tracetool/backend/simple.py | 3 +- qemu/scripts/tracetool/backend/stderr.py | 47 - qemu/scripts/tracetool/format/events_c.py | 3 +- qemu/scripts/tracetool/format/events_h.py | 6 +- qemu/scripts/tracetool/format/h.py | 2 +- qemu/scripts/tracetool/format/tcg_h.py | 35 +- qemu/scripts/tracetool/format/tcg_helper_c.py | 46 +- qemu/scripts/tracetool/format/tcg_helper_h.py | 7 +- .../tracetool/format/tcg_helper_wrapper_h.py | 5 +- qemu/scripts/tracetool/format/ust_events_c.py | 2 + qemu/scripts/tracetool/transform.py | 4 +- qemu/scripts/tracetool/vcpu.py | 70 + qemu/scripts/update-acpi.sh | 4 - qemu/scripts/update-linux-headers.sh | 95 +- qemu/scripts/vmstate-static-checker.py | 1 + qemu/slirp/Makefile.objs | 6 +- qemu/slirp/arp_table.c | 1 + qemu/slirp/bootp.c | 15 +- qemu/slirp/cksum.c | 26 + qemu/slirp/dnssearch.c | 4 +- qemu/slirp/if.c | 34 +- qemu/slirp/if.h | 4 +- qemu/slirp/ip6.h | 141 + qemu/slirp/ip6_icmp.c | 424 ++ qemu/slirp/ip6_icmp.h | 213 + qemu/slirp/ip6_input.c | 78 + qemu/slirp/ip6_output.c | 40 + qemu/slirp/ip_icmp.c | 42 +- qemu/slirp/ip_icmp.h | 4 +- qemu/slirp/ip_input.c | 27 +- qemu/slirp/ip_output.c | 5 +- qemu/slirp/libslirp.h | 11 +- qemu/slirp/mbuf.c | 32 +- qemu/slirp/mbuf.h | 8 +- qemu/slirp/misc.c | 12 +- qemu/slirp/misc.h | 5 + qemu/slirp/ndp_table.c | 90 + qemu/slirp/sbuf.c | 5 +- qemu/slirp/slirp.c | 229 +- qemu/slirp/slirp.h | 111 +- qemu/slirp/socket.c | 301 +- qemu/slirp/socket.h | 78 +- qemu/slirp/tcp.h | 2 + qemu/slirp/tcp_input.c | 260 +- qemu/slirp/tcp_output.c | 54 +- qemu/slirp/tcp_subr.c | 176 +- qemu/slirp/tcp_timer.c | 4 +- qemu/slirp/tcpip.h | 40 +- qemu/slirp/tftp.c | 135 +- qemu/slirp/tftp.h | 7 +- qemu/slirp/udp.c | 90 +- qemu/slirp/udp.h | 10 +- qemu/slirp/udp6.c | 168 + qemu/softmmu_template.h | 30 +- qemu/spice-qemu-char.c | 58 +- qemu/stubs/Makefile.objs | 15 +- qemu/stubs/arch-query-cpu-def.c | 1 + qemu/stubs/bdrv-commit-all.c | 7 - qemu/stubs/bdrv-next-monitor-owned.c | 8 + qemu/stubs/blk-commit-all.c | 8 + qemu/stubs/blockdev-close-all-bdrv-states.c | 6 + qemu/stubs/chr-baum-init.c | 7 - qemu/stubs/chr-msmouse.c | 7 - qemu/stubs/chr-testdev.c | 7 - qemu/stubs/clock-warp.c | 3 +- qemu/stubs/cpu-get-clock.c | 1 + qemu/stubs/cpu-get-icount.c | 1 + qemu/stubs/cpus.c | 1 + qemu/stubs/dump.c | 1 + qemu/stubs/fd-register.c | 1 + qemu/stubs/fdset-add-fd.c | 1 + qemu/stubs/fdset-find-fd.c | 1 + qemu/stubs/fdset-get-fd.c | 1 + qemu/stubs/fdset-remove-fd.c | 1 + qemu/stubs/gdbstub.c | 3 +- qemu/stubs/get-fd.c | 2 + qemu/stubs/get-next-serial.c | 1 + qemu/stubs/get-vm-name.c | 1 + qemu/stubs/iohandler.c | 8 + qemu/stubs/iothread-lock.c | 1 + qemu/stubs/is-daemonized.c | 1 + qemu/stubs/kvm.c | 3 +- qemu/stubs/machine-init-done.c | 1 + qemu/stubs/migr-blocker.c | 1 + qemu/stubs/mon-is-qmp.c | 1 + qemu/stubs/mon-printf.c | 1 + qemu/stubs/monitor-init.c | 1 + qemu/stubs/notify-event.c | 1 + qemu/stubs/qemu-chr-open-spice.c | 14 - qemu/stubs/qmp_pc_dimm_device_list.c | 6 +- qemu/stubs/qtest.c | 6 + qemu/stubs/replay-user.c | 33 + qemu/stubs/replay.c | 69 + qemu/stubs/reset.c | 1 + qemu/stubs/runstate-check.c | 1 + qemu/stubs/set-fd-handler.c | 11 + qemu/stubs/slirp.c | 1 + qemu/stubs/sysbus.c | 1 + qemu/stubs/target-get-monitor-def.c | 29 + qemu/stubs/target-monitor-defs.c | 8 + qemu/stubs/uuid.c | 1 + qemu/stubs/vc-init.c | 7 - qemu/stubs/vhost.c | 7 + qemu/stubs/vm-stop.c | 1 + qemu/stubs/vmstate.c | 1 + qemu/target-alpha/cpu.c | 17 + qemu/target-alpha/cpu.h | 9 +- qemu/target-alpha/fpu_helper.c | 3 +- qemu/target-alpha/gdbstub.c | 6 +- qemu/target-alpha/helper.c | 67 +- qemu/target-alpha/helper.h | 4 +- qemu/target-alpha/int_helper.c | 52 +- qemu/target-alpha/machine.c | 5 +- qemu/target-alpha/mem_helper.c | 3 +- qemu/target-alpha/sys_helper.c | 25 +- qemu/target-alpha/translate.c | 297 +- qemu/target-alpha/vax_helper.c | 1 + qemu/target-arm/Makefile.objs | 3 +- qemu/target-arm/arch_dump.c | 337 + qemu/target-arm/arm-semi.c | 180 +- qemu/target-arm/arm_ldst.h | 8 +- qemu/target-arm/cpu-qom.h | 31 +- qemu/target-arm/cpu.c | 136 +- qemu/target-arm/cpu.h | 392 +- qemu/target-arm/cpu64.c | 15 +- qemu/target-arm/crypto_helper.c | 2 +- qemu/target-arm/gdbstub.c | 4 +- qemu/target-arm/gdbstub64.c | 2 +- qemu/target-arm/helper-a64.c | 105 +- qemu/target-arm/helper.c | 2629 +++++-- qemu/target-arm/helper.h | 9 +- qemu/target-arm/internals.h | 123 +- qemu/target-arm/iwmmxt_helper.c | 3 +- qemu/target-arm/kvm-consts.h | 1 - qemu/target-arm/kvm-stub.c | 1 + qemu/target-arm/kvm.c | 109 +- qemu/target-arm/kvm32.c | 89 +- qemu/target-arm/kvm64.c | 474 +- qemu/target-arm/kvm_arm.h | 61 +- qemu/target-arm/machine.c | 23 +- qemu/target-arm/monitor.c | 84 + qemu/target-arm/neon_helper.c | 3 +- qemu/target-arm/op_helper.c | 437 +- qemu/target-arm/psci.c | 1 + qemu/target-arm/translate-a64.c | 599 +- qemu/target-arm/translate.c | 1240 +++- qemu/target-arm/translate.h | 35 +- qemu/target-cris/cpu-qom.h | 4 + qemu/target-cris/cpu.c | 10 + qemu/target-cris/cpu.h | 21 +- qemu/target-cris/gdbstub.c | 2 +- qemu/target-cris/helper.c | 1 + qemu/target-cris/helper.h | 1 - qemu/target-cris/machine.c | 168 +- qemu/target-cris/mmu.c | 4 +- qemu/target-cris/op_helper.c | 6 +- qemu/target-cris/translate.c | 177 +- qemu/target-cris/translate_v10.c | 102 +- qemu/target-i386/Makefile.objs | 8 +- qemu/target-i386/arch_dump.c | 1 + qemu/target-i386/arch_memory_mapping.c | 1 + qemu/target-i386/bpt_helper.c | 327 + qemu/target-i386/cc_helper.c | 13 +- qemu/target-i386/cpu-qom.h | 8 +- qemu/target-i386/cpu.c | 473 +- qemu/target-i386/cpu.h | 226 +- qemu/target-i386/excp_helper.c | 31 +- qemu/target-i386/fpu_helper.c | 525 +- qemu/target-i386/gdbstub.c | 10 +- qemu/target-i386/helper.c | 429 +- qemu/target-i386/helper.h | 33 +- qemu/target-i386/hyperv.c | 140 + qemu/target-i386/hyperv.h | 42 + qemu/target-i386/int_helper.c | 43 +- qemu/target-i386/kvm-stub.c | 1 + qemu/target-i386/kvm.c | 514 +- qemu/target-i386/kvm_i386.h | 2 + qemu/target-i386/machine.c | 218 +- qemu/target-i386/mem_helper.c | 71 +- qemu/target-i386/misc_helper.c | 79 +- qemu/target-i386/monitor.c | 512 ++ qemu/target-i386/mpx_helper.c | 167 + qemu/target-i386/ops_sse.h | 380 +- qemu/target-i386/ops_sse_header.h | 80 +- qemu/target-i386/seg_helper.c | 671 +- qemu/target-i386/smm_helper.c | 10 +- qemu/target-i386/svm_helper.c | 1 + qemu/target-i386/translate.c | 3450 ++++----- qemu/target-lm32/cpu.c | 16 + qemu/target-lm32/cpu.h | 6 +- qemu/target-lm32/gdbstub.c | 2 +- qemu/target-lm32/helper.c | 4 +- qemu/target-lm32/lm32-semi.c | 5 +- qemu/target-lm32/machine.c | 1 + qemu/target-lm32/op_helper.c | 2 +- qemu/target-lm32/translate.c | 116 +- qemu/target-m68k/cpu.c | 18 +- qemu/target-m68k/cpu.h | 6 +- qemu/target-m68k/gdbstub.c | 2 +- qemu/target-m68k/helper.c | 1 + qemu/target-m68k/m68k-semi.c | 10 +- qemu/target-m68k/op_helper.c | 1 + qemu/target-m68k/translate.c | 123 +- qemu/target-microblaze/cpu.c | 12 +- qemu/target-microblaze/cpu.h | 6 +- qemu/target-microblaze/gdbstub.c | 2 +- qemu/target-microblaze/helper.c | 4 +- qemu/target-microblaze/mmu.c | 23 +- qemu/target-microblaze/op_helper.c | 14 +- qemu/target-microblaze/translate.c | 161 +- qemu/target-mips/cpu.c | 36 +- qemu/target-mips/cpu.h | 160 +- qemu/target-mips/dsp_helper.c | 1 + qemu/target-mips/gdbstub.c | 2 +- qemu/target-mips/helper.c | 62 +- qemu/target-mips/helper.h | 16 + qemu/target-mips/kvm.c | 395 +- qemu/target-mips/lmi_helper.c | 1 + qemu/target-mips/machine.c | 7 +- qemu/target-mips/mips-semi.c | 2 +- qemu/target-mips/msa_helper.c | 195 +- qemu/target-mips/op_helper.c | 458 +- qemu/target-mips/translate.c | 1548 ++-- qemu/target-mips/translate_init.c | 78 +- qemu/target-moxie/cpu.c | 16 + qemu/target-moxie/cpu.h | 6 +- qemu/target-moxie/helper.c | 7 +- qemu/target-moxie/machine.c | 1 + qemu/target-moxie/mmu.c | 5 +- qemu/target-moxie/translate.c | 105 +- qemu/target-openrisc/cpu.c | 9 + qemu/target-openrisc/cpu.h | 5 +- qemu/target-openrisc/exception.c | 1 + qemu/target-openrisc/exception_helper.c | 1 + qemu/target-openrisc/fpu_helper.c | 1 + qemu/target-openrisc/gdbstub.c | 2 +- qemu/target-openrisc/int_helper.c | 1 + qemu/target-openrisc/interrupt.c | 1 + qemu/target-openrisc/interrupt_helper.c | 1 + qemu/target-openrisc/machine.c | 1 + qemu/target-openrisc/mmu.c | 1 + qemu/target-openrisc/mmu_helper.c | 1 + qemu/target-openrisc/sys_helper.c | 1 + qemu/target-openrisc/translate.c | 138 +- qemu/target-ppc/Makefile.objs | 4 +- qemu/target-ppc/arch_dump.c | 7 +- qemu/target-ppc/cpu-models.c | 16 +- qemu/target-ppc/cpu-models.h | 6 +- qemu/target-ppc/cpu-qom.h | 4 +- qemu/target-ppc/cpu.h | 152 +- qemu/target-ppc/dfp_helper.c | 22 +- qemu/target-ppc/excp_helper.c | 60 +- qemu/target-ppc/fpu_helper.c | 23 +- qemu/target-ppc/gdbstub.c | 12 +- qemu/target-ppc/helper.h | 1 + qemu/target-ppc/int_helper.c | 20 +- qemu/target-ppc/kvm-stub.c | 1 + qemu/target-ppc/kvm.c | 537 +- qemu/target-ppc/kvm_ppc.c | 41 - qemu/target-ppc/kvm_ppc.h | 66 +- qemu/target-ppc/machine.c | 25 +- qemu/target-ppc/mem_helper.c | 7 +- qemu/target-ppc/mfrom_table_gen.c | 3 +- qemu/target-ppc/misc_helper.c | 1 + qemu/target-ppc/mmu-hash32.c | 82 +- qemu/target-ppc/mmu-hash32.h | 30 +- qemu/target-ppc/mmu-hash64.c | 363 +- qemu/target-ppc/mmu-hash64.h | 37 +- qemu/target-ppc/mmu_helper.c | 132 +- qemu/target-ppc/monitor.c | 147 + qemu/target-ppc/timebase_helper.c | 11 +- qemu/target-ppc/translate.c | 259 +- qemu/target-ppc/translate_init.c | 459 +- qemu/target-ppc/user_only_helper.c | 1 + qemu/target-s390x/arch_dump.c | 7 +- qemu/target-s390x/cc_helper.c | 3 +- qemu/target-s390x/cpu-qom.h | 5 +- qemu/target-s390x/cpu.c | 119 +- qemu/target-s390x/cpu.h | 85 +- qemu/target-s390x/fpu_helper.c | 1 + qemu/target-s390x/gdbstub.c | 123 +- qemu/target-s390x/helper.c | 52 +- qemu/target-s390x/int_helper.c | 1 + qemu/target-s390x/interrupt.c | 12 +- qemu/target-s390x/ioinst.c | 6 +- qemu/target-s390x/kvm.c | 79 +- qemu/target-s390x/machine.c | 5 +- qemu/target-s390x/mem_helper.c | 51 +- qemu/target-s390x/misc_helper.c | 20 +- qemu/target-s390x/mmu_helper.c | 33 +- qemu/target-s390x/translate.c | 137 +- qemu/target-sh4/Makefile.objs | 1 + qemu/target-sh4/cpu.c | 20 +- qemu/target-sh4/cpu.h | 7 +- qemu/target-sh4/gdbstub.c | 2 +- qemu/target-sh4/helper.c | 14 +- qemu/target-sh4/helper.h | 34 +- qemu/target-sh4/monitor.c | 53 + qemu/target-sh4/op_helper.c | 3 +- qemu/target-sh4/translate.c | 278 +- qemu/target-sparc/Makefile.objs | 2 +- qemu/target-sparc/cc_helper.c | 1 + qemu/target-sparc/cpu-qom.h | 4 + qemu/target-sparc/cpu.c | 19 + qemu/target-sparc/cpu.h | 20 +- qemu/target-sparc/fop_helper.c | 1 + qemu/target-sparc/gdbstub.c | 2 +- qemu/target-sparc/helper.c | 11 +- qemu/target-sparc/helper.h | 2 +- qemu/target-sparc/int32_helper.c | 2 + qemu/target-sparc/int64_helper.c | 7 +- qemu/target-sparc/ldst_helper.c | 13 +- qemu/target-sparc/machine.c | 371 +- qemu/target-sparc/mmu_helper.c | 3 +- qemu/target-sparc/monitor.c | 159 + qemu/target-sparc/translate.c | 438 +- qemu/target-sparc/vis_helper.c | 3 +- qemu/target-sparc/win_helper.c | 20 +- qemu/target-tilegx/Makefile.objs | 1 + qemu/target-tilegx/cpu.c | 187 + qemu/target-tilegx/cpu.h | 181 + qemu/target-tilegx/helper.c | 162 + qemu/target-tilegx/helper.h | 26 + qemu/target-tilegx/opcode_tilegx.h | 1406 ++++ qemu/target-tilegx/simd_helper.c | 166 + qemu/target-tilegx/spr_def_64.h | 216 + qemu/target-tilegx/translate.c | 2451 +++++++ qemu/target-tricore/Makefile.objs | 2 +- qemu/target-tricore/cpu.c | 8 + qemu/target-tricore/cpu.h | 12 +- qemu/target-tricore/fpu_helper.c | 217 + qemu/target-tricore/helper.c | 31 +- qemu/target-tricore/helper.h | 10 + qemu/target-tricore/op_helper.c | 161 +- qemu/target-tricore/translate.c | 632 +- qemu/target-tricore/tricore-opcodes.h | 18 + qemu/target-unicore32/cpu.c | 9 + qemu/target-unicore32/cpu.h | 5 +- qemu/target-unicore32/helper.c | 1 + qemu/target-unicore32/op_helper.c | 1 + qemu/target-unicore32/softmmu.c | 1 + qemu/target-unicore32/translate.c | 97 +- qemu/target-unicore32/ucf64_helper.c | 1 + qemu/target-xtensa/Makefile.objs | 1 + qemu/target-xtensa/core-dc232b.c | 1 + qemu/target-xtensa/core-dc233c.c | 1 + qemu/target-xtensa/core-fsf.c | 1 + qemu/target-xtensa/cpu.c | 9 + qemu/target-xtensa/cpu.h | 6 +- qemu/target-xtensa/gdbstub.c | 10 +- qemu/target-xtensa/helper.c | 13 +- qemu/target-xtensa/monitor.c | 35 + qemu/target-xtensa/op_helper.c | 25 +- qemu/target-xtensa/overlay_tool.h | 5 + qemu/target-xtensa/translate.c | 166 +- qemu/target-xtensa/xtensa-semi.c | 7 +- qemu/tcg-runtime.c | 2 +- qemu/tcg/README | 37 +- qemu/tcg/aarch64/tcg-target.c | 1868 ----- qemu/tcg/aarch64/tcg-target.h | 3 +- qemu/tcg/aarch64/tcg-target.inc.c | 1892 +++++ qemu/tcg/arm/tcg-target.c | 2128 ------ qemu/tcg/arm/tcg-target.inc.c | 2128 ++++++ qemu/tcg/i386/tcg-target.c | 2451 ------- qemu/tcg/i386/tcg-target.h | 3 +- qemu/tcg/i386/tcg-target.inc.c | 2463 +++++++ qemu/tcg/ia64/tcg-target.c | 2445 ------- qemu/tcg/ia64/tcg-target.h | 3 +- qemu/tcg/ia64/tcg-target.inc.c | 2453 +++++++ qemu/tcg/mips/tcg-target.c | 1824 ----- qemu/tcg/mips/tcg-target.h | 11 +- qemu/tcg/mips/tcg-target.inc.c | 1891 +++++ qemu/tcg/optimize.c | 261 +- qemu/tcg/ppc/tcg-target.c | 2722 ------- qemu/tcg/ppc/tcg-target.h | 3 +- qemu/tcg/ppc/tcg-target.inc.c | 2761 +++++++ qemu/tcg/s390/tcg-target.c | 2393 ------ qemu/tcg/s390/tcg-target.h | 3 +- qemu/tcg/s390/tcg-target.inc.c | 2409 +++++++ qemu/tcg/sparc/tcg-target.c | 1650 ----- qemu/tcg/sparc/tcg-target.h | 3 +- qemu/tcg/sparc/tcg-target.inc.c | 1652 +++++ qemu/tcg/tcg-be-ldst.h | 11 +- qemu/tcg/tcg-be-null.h | 3 +- qemu/tcg/tcg-common.c | 38 + qemu/tcg/tcg-op.c | 49 +- qemu/tcg/tcg-op.h | 66 +- qemu/tcg/tcg-opc.h | 23 +- qemu/tcg/tcg.c | 891 +-- qemu/tcg/tcg.h | 113 +- qemu/tcg/tci/README | 4 +- qemu/tcg/tci/tcg-target.c | 875 --- qemu/tcg/tci/tcg-target.h | 4 +- qemu/tcg/tci/tcg-target.inc.c | 879 +++ qemu/tci.c | 64 +- qemu/tests/.gitignore | 30 + qemu/tests/Makefile | 384 +- qemu/tests/ac97-test.c | 3 +- qemu/tests/acpi-test-data/pc/DSDT | Bin 3028 -> 5587 bytes qemu/tests/acpi-test-data/pc/DSDT.bridge | Bin 0 -> 7446 bytes qemu/tests/acpi-test-data/pc/SSDT | Bin 2486 -> 0 bytes qemu/tests/acpi-test-data/pc/SSDT.bridge | Bin 4345 -> 0 bytes qemu/tests/acpi-test-data/q35/DSDT | Bin 7666 -> 8357 bytes qemu/tests/acpi-test-data/q35/DSDT.bridge | Bin 0 -> 8374 bytes qemu/tests/acpi-test-data/q35/SSDT | Bin 691 -> 0 bytes qemu/tests/acpi-test-data/q35/SSDT.bridge | Bin 708 -> 0 bytes qemu/tests/ahci-test.c | 293 +- qemu/tests/bios-tables-test.c | 138 +- qemu/tests/boot-order-test.c | 6 +- qemu/tests/boot-sector.c | 118 + qemu/tests/boot-sector.h | 26 + qemu/tests/check-block.sh | 2 +- qemu/tests/check-qdict.c | 1 + qemu/tests/check-qfloat.c | 1 + qemu/tests/check-qint.c | 1 + qemu/tests/check-qjson.c | 26 + qemu/tests/check-qlist.c | 1 + qemu/tests/check-qom-interface.c | 1 + qemu/tests/check-qom-proplist.c | 237 +- qemu/tests/check-qstring.c | 1 + qemu/tests/crypto-tls-x509-helpers.c | 485 ++ qemu/tests/crypto-tls-x509-helpers.h | 133 + qemu/tests/device-introspect-test.c | 124 + qemu/tests/display-vga-test.c | 3 +- qemu/tests/drive_del-test.c | 24 +- qemu/tests/ds1338-test.c | 79 + qemu/tests/e1000-test.c | 3 +- qemu/tests/eepro100-test.c | 3 +- qemu/tests/endianness-test.c | 5 +- qemu/tests/es1370-test.c | 3 +- qemu/tests/fdc-test.c | 10 +- qemu/tests/fw_cfg-test.c | 9 +- qemu/tests/guest-debug/test-gdbstub.py | 176 + qemu/tests/hd-geo-test.c | 8 +- qemu/tests/i440fx-test.c | 10 +- qemu/tests/i82801b11-test.c | 3 +- qemu/tests/ide-test.c | 275 +- qemu/tests/image-fuzzer/runner.py | 14 +- qemu/tests/intel-hda-test.c | 3 +- qemu/tests/io-channel-helpers.c | 248 + qemu/tests/io-channel-helpers.h | 42 + qemu/tests/ioh3420-test.c | 3 +- qemu/tests/ipmi-bt-test.c | 433 ++ qemu/tests/ipmi-kcs-test.c | 293 + qemu/tests/ipoctal232-test.c | 3 +- qemu/tests/ivshmem-test.c | 518 ++ qemu/tests/libqos/ahci.c | 180 +- qemu/tests/libqos/ahci.h | 69 +- qemu/tests/libqos/fw_cfg.c | 1 + qemu/tests/libqos/fw_cfg.h | 2 - qemu/tests/libqos/i2c-imx.c | 208 + qemu/tests/libqos/i2c-omap.c | 3 +- qemu/tests/libqos/i2c.c | 1 + qemu/tests/libqos/i2c.h | 4 +- qemu/tests/libqos/libqos-pc.c | 1 + qemu/tests/libqos/libqos.c | 60 +- qemu/tests/libqos/libqos.h | 2 + qemu/tests/libqos/malloc-generic.c | 1 + qemu/tests/libqos/malloc-pc.c | 4 +- qemu/tests/libqos/malloc.c | 7 +- qemu/tests/libqos/malloc.h | 2 - qemu/tests/libqos/pci-pc.c | 9 +- qemu/tests/libqos/pci.c | 3 + qemu/tests/libqos/pci.h | 1 - qemu/tests/libqos/usb.c | 3 +- qemu/tests/libqos/virtio-mmio.c | 2 +- qemu/tests/libqos/virtio-pci.c | 2 +- qemu/tests/libqos/virtio.c | 1 + qemu/tests/libqtest.c | 144 +- qemu/tests/libqtest.h | 53 +- qemu/tests/m48t59-test.c | 5 +- qemu/tests/ne2000-test.c | 3 +- qemu/tests/nvme-test.c | 3 +- qemu/tests/pc-cpu-test.c | 3 +- qemu/tests/pcnet-test.c | 3 +- qemu/tests/pkix_asn1_tab.c | 1105 +++ qemu/tests/pvpanic-test.c | 3 +- qemu/tests/pxe-test.c | 68 + qemu/tests/q35-test.c | 3 +- qemu/tests/qapi-schema/alternate-any.err | 1 + .../{data-array-empty.exit => alternate-any.exit} | 0 qemu/tests/qapi-schema/alternate-any.json | 4 + .../{enum-empty.err => alternate-any.out} | 0 qemu/tests/qapi-schema/alternate-clash.err | 2 +- qemu/tests/qapi-schema/alternate-clash.json | 9 +- qemu/tests/qapi-schema/alternate-empty.err | 1 + ...ata-array-unknown.exit => alternate-empty.exit} | 0 qemu/tests/qapi-schema/alternate-empty.json | 2 + .../{enum-max-member.out => alternate-empty.out} | 0 qemu/tests/qapi-schema/alternate-good.exit | 1 - qemu/tests/qapi-schema/alternate-good.json | 9 - qemu/tests/qapi-schema/alternate-good.out | 6 - qemu/tests/qapi-schema/alternate-nested.json | 2 +- qemu/tests/qapi-schema/alternate-unknown.json | 2 +- qemu/tests/qapi-schema/args-alternate.err | 1 + .../{data-int.exit => args-alternate.exit} | 0 qemu/tests/qapi-schema/args-alternate.json | 3 + .../{enum-union-clash.out => args-alternate.out} | 0 qemu/tests/qapi-schema/args-any.err | 1 + .../{data-member-array-bad.exit => args-any.exit} | 0 qemu/tests/qapi-schema/args-any.json | 2 + .../qapi-schema/{event-max.out => args-any.out} | 0 qemu/tests/qapi-schema/args-array-empty.err | 1 + ...a-member-unknown.exit => args-array-empty.exit} | 0 ...data-array-empty.json => args-array-empty.json} | 0 ...at-union-base-star.out => args-array-empty.out} | 0 qemu/tests/qapi-schema/args-array-unknown.err | 1 + .../{data-unknown.exit => args-array-unknown.exit} | 0 ...-array-unknown.json => args-array-unknown.json} | 0 ...ion-branch-clash.out => args-array-unknown.out} | 0 qemu/tests/qapi-schema/args-int.err | 1 + .../{enum-max-member.exit => args-int.exit} | 0 .../qapi-schema/{data-int.json => args-int.json} | 0 ...{flat-union-reverse-define.err => args-int.out} | 0 qemu/tests/qapi-schema/args-invalid.err | 1 + .../{enum-union-clash.exit => args-invalid.exit} | 0 qemu/tests/qapi-schema/args-invalid.json | 2 + ...{nested-struct-returns.out => args-invalid.out} | 0 qemu/tests/qapi-schema/args-member-array-bad.err | 1 + .../{event-max.exit => args-member-array-bad.exit} | 0 ...r-array-bad.json => args-member-array-bad.json} | 0 .../{returns-int.err => args-member-array-bad.out} | 0 qemu/tests/qapi-schema/args-member-case.err | 1 + ...-union-base-star.exit => args-member-case.exit} | 0 qemu/tests/qapi-schema/args-member-case.json | 2 + ...type-bypass-no-gen.out => args-member-case.out} | 0 qemu/tests/qapi-schema/args-member-unknown.err | 1 + ...-branch-clash.exit => args-member-unknown.exit} | 0 ...ember-unknown.json => args-member-unknown.json} | 0 .../{type-bypass.err => args-member-unknown.out} | 0 qemu/tests/qapi-schema/args-name-clash.err | 1 + ...ed-struct-returns.exit => args-name-clash.exit} | 0 qemu/tests/qapi-schema/args-name-clash.json | 4 + .../{union-bad-branch.out => args-name-clash.out} | 0 qemu/tests/qapi-schema/args-union.err | 1 + .../{type-bypass-no-gen.exit => args-union.exit} | 0 qemu/tests/qapi-schema/args-union.json | 4 + .../qapi-schema/{union-max.out => args-union.out} | 0 qemu/tests/qapi-schema/args-unknown.err | 1 + .../{union-bad-branch.exit => args-unknown.exit} | 0 .../{data-unknown.json => args-unknown.json} | 0 qemu/tests/qapi-schema/args-unknown.out | 0 qemu/tests/qapi-schema/base-cycle-direct.err | 1 + .../{union-max.exit => base-cycle-direct.exit} | 0 qemu/tests/qapi-schema/base-cycle-direct.json | 2 + qemu/tests/qapi-schema/base-cycle-direct.out | 0 qemu/tests/qapi-schema/base-cycle-indirect.err | 1 + qemu/tests/qapi-schema/base-cycle-indirect.exit | 1 + qemu/tests/qapi-schema/base-cycle-indirect.json | 3 + qemu/tests/qapi-schema/base-cycle-indirect.out | 0 qemu/tests/qapi-schema/command-int.json | 3 +- qemu/tests/qapi-schema/comments.out | 7 +- qemu/tests/qapi-schema/data-array-empty.err | 1 - qemu/tests/qapi-schema/data-array-unknown.err | 1 - qemu/tests/qapi-schema/data-int.err | 1 - qemu/tests/qapi-schema/data-member-array-bad.err | 1 - qemu/tests/qapi-schema/data-member-array.exit | 1 - qemu/tests/qapi-schema/data-member-array.json | 4 - qemu/tests/qapi-schema/data-member-array.out | 5 - qemu/tests/qapi-schema/data-member-unknown.err | 1 - qemu/tests/qapi-schema/data-unknown.err | 1 - qemu/tests/qapi-schema/duplicate-key.err | 2 +- qemu/tests/qapi-schema/duplicate-key.json | 1 + qemu/tests/qapi-schema/empty.out | 6 +- qemu/tests/qapi-schema/enum-bad-prefix.err | 1 + qemu/tests/qapi-schema/enum-bad-prefix.exit | 1 + qemu/tests/qapi-schema/enum-bad-prefix.json | 2 + qemu/tests/qapi-schema/enum-bad-prefix.out | 0 qemu/tests/qapi-schema/enum-clash-member.err | 2 +- qemu/tests/qapi-schema/enum-clash-member.json | 2 +- qemu/tests/qapi-schema/enum-empty.exit | 1 - qemu/tests/qapi-schema/enum-empty.json | 2 - qemu/tests/qapi-schema/enum-empty.out | 3 - qemu/tests/qapi-schema/enum-max-member.err | 1 - qemu/tests/qapi-schema/enum-max-member.json | 3 - qemu/tests/qapi-schema/enum-member-case.err | 1 + qemu/tests/qapi-schema/enum-member-case.exit | 1 + qemu/tests/qapi-schema/enum-member-case.json | 3 + qemu/tests/qapi-schema/enum-member-case.out | 0 qemu/tests/qapi-schema/enum-union-clash.err | 1 - qemu/tests/qapi-schema/enum-union-clash.json | 4 - qemu/tests/qapi-schema/event-case.out | 7 +- qemu/tests/qapi-schema/event-max.err | 1 - qemu/tests/qapi-schema/event-max.json | 2 - qemu/tests/qapi-schema/flat-union-bad-base.err | 2 +- qemu/tests/qapi-schema/flat-union-bad-base.json | 5 +- qemu/tests/qapi-schema/flat-union-base-any.err | 1 + qemu/tests/qapi-schema/flat-union-base-any.exit | 1 + qemu/tests/qapi-schema/flat-union-base-any.json | 12 + qemu/tests/qapi-schema/flat-union-base-any.out | 0 qemu/tests/qapi-schema/flat-union-base-star.err | 1 - qemu/tests/qapi-schema/flat-union-base-star.json | 12 - qemu/tests/qapi-schema/flat-union-base-union.err | 2 +- qemu/tests/qapi-schema/flat-union-base-union.json | 5 +- qemu/tests/qapi-schema/flat-union-branch-clash.err | 1 - .../tests/qapi-schema/flat-union-branch-clash.json | 14 - qemu/tests/qapi-schema/flat-union-clash-member.err | 1 + .../tests/qapi-schema/flat-union-clash-member.exit | 1 + .../tests/qapi-schema/flat-union-clash-member.json | 15 + qemu/tests/qapi-schema/flat-union-clash-member.out | 0 qemu/tests/qapi-schema/flat-union-empty.err | 1 + qemu/tests/qapi-schema/flat-union-empty.exit | 1 + qemu/tests/qapi-schema/flat-union-empty.json | 4 + qemu/tests/qapi-schema/flat-union-empty.out | 0 qemu/tests/qapi-schema/flat-union-inline.err | 2 +- qemu/tests/qapi-schema/flat-union-inline.json | 4 +- qemu/tests/qapi-schema/flat-union-no-base.err | 2 +- .../qapi-schema/flat-union-reverse-define.exit | 1 - .../qapi-schema/flat-union-reverse-define.json | 17 - .../qapi-schema/flat-union-reverse-define.out | 9 - qemu/tests/qapi-schema/ident-with-escape.out | 10 +- qemu/tests/qapi-schema/include-non-file.err | 2 +- qemu/tests/qapi-schema/include-non-file.json | 2 +- qemu/tests/qapi-schema/include-relpath.out | 7 +- qemu/tests/qapi-schema/include-repetition.out | 7 +- qemu/tests/qapi-schema/include-simple.out | 7 +- qemu/tests/qapi-schema/indented-expr.out | 10 +- qemu/tests/qapi-schema/leading-comma-list.err | 1 + qemu/tests/qapi-schema/leading-comma-list.exit | 1 + qemu/tests/qapi-schema/leading-comma-list.json | 2 + qemu/tests/qapi-schema/leading-comma-list.out | 0 qemu/tests/qapi-schema/leading-comma-object.err | 1 + qemu/tests/qapi-schema/leading-comma-object.exit | 1 + qemu/tests/qapi-schema/leading-comma-object.json | 2 + qemu/tests/qapi-schema/leading-comma-object.out | 0 qemu/tests/qapi-schema/nested-struct-data.json | 3 +- qemu/tests/qapi-schema/nested-struct-returns.err | 1 - qemu/tests/qapi-schema/nested-struct-returns.json | 3 - qemu/tests/qapi-schema/qapi-schema-test.json | 100 +- qemu/tests/qapi-schema/qapi-schema-test.out | 266 +- qemu/tests/qapi-schema/reserved-command-q.err | 1 + qemu/tests/qapi-schema/reserved-command-q.exit | 1 + qemu/tests/qapi-schema/reserved-command-q.json | 5 + qemu/tests/qapi-schema/reserved-command-q.out | 0 qemu/tests/qapi-schema/reserved-enum-q.err | 1 + qemu/tests/qapi-schema/reserved-enum-q.exit | 1 + qemu/tests/qapi-schema/reserved-enum-q.json | 4 + qemu/tests/qapi-schema/reserved-enum-q.out | 0 qemu/tests/qapi-schema/reserved-member-has.err | 1 + qemu/tests/qapi-schema/reserved-member-has.exit | 1 + qemu/tests/qapi-schema/reserved-member-has.json | 5 + qemu/tests/qapi-schema/reserved-member-has.out | 0 qemu/tests/qapi-schema/reserved-member-q.err | 1 + qemu/tests/qapi-schema/reserved-member-q.exit | 1 + qemu/tests/qapi-schema/reserved-member-q.json | 4 + qemu/tests/qapi-schema/reserved-member-q.out | 0 qemu/tests/qapi-schema/reserved-member-u.err | 1 + qemu/tests/qapi-schema/reserved-member-u.exit | 1 + qemu/tests/qapi-schema/reserved-member-u.json | 7 + qemu/tests/qapi-schema/reserved-member-u.out | 0 .../qapi-schema/reserved-member-underscore.err | 1 + .../qapi-schema/reserved-member-underscore.exit | 1 + .../qapi-schema/reserved-member-underscore.json | 4 + .../qapi-schema/reserved-member-underscore.out | 0 qemu/tests/qapi-schema/reserved-type-kind.err | 1 + qemu/tests/qapi-schema/reserved-type-kind.exit | 1 + qemu/tests/qapi-schema/reserved-type-kind.json | 2 + qemu/tests/qapi-schema/reserved-type-kind.out | 0 qemu/tests/qapi-schema/reserved-type-list.err | 1 + qemu/tests/qapi-schema/reserved-type-list.exit | 1 + qemu/tests/qapi-schema/reserved-type-list.json | 5 + qemu/tests/qapi-schema/reserved-type-list.out | 0 qemu/tests/qapi-schema/returns-dict.err | 1 + qemu/tests/qapi-schema/returns-dict.exit | 1 + qemu/tests/qapi-schema/returns-dict.json | 2 + qemu/tests/qapi-schema/returns-dict.out | 0 qemu/tests/qapi-schema/returns-int.exit | 1 - qemu/tests/qapi-schema/returns-int.json | 3 - qemu/tests/qapi-schema/returns-int.out | 3 - qemu/tests/qapi-schema/returns-whitelist.err | 2 +- qemu/tests/qapi-schema/struct-base-clash-deep.err | 2 +- qemu/tests/qapi-schema/struct-base-clash-deep.json | 5 +- qemu/tests/qapi-schema/struct-base-clash.err | 2 +- qemu/tests/qapi-schema/struct-base-clash.json | 3 +- qemu/tests/qapi-schema/struct-data-invalid.err | 1 + qemu/tests/qapi-schema/struct-data-invalid.exit | 1 + qemu/tests/qapi-schema/struct-data-invalid.json | 2 + qemu/tests/qapi-schema/struct-data-invalid.out | 0 qemu/tests/qapi-schema/struct-member-invalid.err | 1 + qemu/tests/qapi-schema/struct-member-invalid.exit | 1 + qemu/tests/qapi-schema/struct-member-invalid.json | 2 + qemu/tests/qapi-schema/struct-member-invalid.out | 0 qemu/tests/qapi-schema/test-qapi.py | 47 +- qemu/tests/qapi-schema/type-bypass-no-gen.err | 1 - qemu/tests/qapi-schema/type-bypass-no-gen.json | 2 - qemu/tests/qapi-schema/type-bypass.exit | 1 - qemu/tests/qapi-schema/type-bypass.json | 2 - qemu/tests/qapi-schema/type-bypass.out | 3 - qemu/tests/qapi-schema/union-bad-branch.err | 1 - qemu/tests/qapi-schema/union-bad-branch.json | 8 - .../qapi-schema/union-base-no-discriminator.err | 2 +- qemu/tests/qapi-schema/union-branch-case.err | 1 + qemu/tests/qapi-schema/union-branch-case.exit | 1 + qemu/tests/qapi-schema/union-branch-case.json | 2 + qemu/tests/qapi-schema/union-branch-case.out | 0 qemu/tests/qapi-schema/union-clash-branches.err | 1 + qemu/tests/qapi-schema/union-clash-branches.exit | 1 + qemu/tests/qapi-schema/union-clash-branches.json | 5 + qemu/tests/qapi-schema/union-clash-branches.out | 0 qemu/tests/qapi-schema/union-empty.err | 1 + qemu/tests/qapi-schema/union-empty.exit | 1 + qemu/tests/qapi-schema/union-empty.json | 2 + qemu/tests/qapi-schema/union-empty.out | 0 qemu/tests/qapi-schema/union-invalid-base.err | 2 +- qemu/tests/qapi-schema/union-max.err | 1 - qemu/tests/qapi-schema/union-max.json | 3 - qemu/tests/qemu-iotests/001 | 1 - qemu/tests/qemu-iotests/002 | 1 - qemu/tests/qemu-iotests/003 | 1 - qemu/tests/qemu-iotests/004 | 1 - qemu/tests/qemu-iotests/005 | 1 - qemu/tests/qemu-iotests/007 | 1 - qemu/tests/qemu-iotests/008 | 1 - qemu/tests/qemu-iotests/009 | 1 - qemu/tests/qemu-iotests/010 | 1 - qemu/tests/qemu-iotests/011 | 1 - qemu/tests/qemu-iotests/012 | 1 - qemu/tests/qemu-iotests/013 | 1 - qemu/tests/qemu-iotests/014 | 1 - qemu/tests/qemu-iotests/015 | 1 - qemu/tests/qemu-iotests/017 | 1 - qemu/tests/qemu-iotests/017.out | 2 +- qemu/tests/qemu-iotests/018 | 9 +- qemu/tests/qemu-iotests/018.out | 2 +- qemu/tests/qemu-iotests/019 | 14 +- qemu/tests/qemu-iotests/019.out | 4 +- qemu/tests/qemu-iotests/020 | 8 +- qemu/tests/qemu-iotests/020.out | 4 +- qemu/tests/qemu-iotests/021 | 1 - qemu/tests/qemu-iotests/022 | 1 - qemu/tests/qemu-iotests/023 | 1 - qemu/tests/qemu-iotests/024 | 11 +- qemu/tests/qemu-iotests/024.out | 6 +- qemu/tests/qemu-iotests/025 | 1 - qemu/tests/qemu-iotests/026 | 19 +- qemu/tests/qemu-iotests/026.out | 80 +- qemu/tests/qemu-iotests/026.out.nocache | 222 +- qemu/tests/qemu-iotests/027 | 1 - qemu/tests/qemu-iotests/028 | 12 +- qemu/tests/qemu-iotests/028.out | 9 +- qemu/tests/qemu-iotests/029 | 1 - qemu/tests/qemu-iotests/030 | 26 +- qemu/tests/qemu-iotests/030.out | 4 +- qemu/tests/qemu-iotests/031 | 1 - qemu/tests/qemu-iotests/031.out | 17 +- qemu/tests/qemu-iotests/032 | 1 - qemu/tests/qemu-iotests/033 | 9 +- qemu/tests/qemu-iotests/033.out | 82 + qemu/tests/qemu-iotests/034 | 7 +- qemu/tests/qemu-iotests/034.out | 4 +- qemu/tests/qemu-iotests/035 | 1 - qemu/tests/qemu-iotests/036 | 3 +- qemu/tests/qemu-iotests/036.out | 21 +- qemu/tests/qemu-iotests/037 | 6 +- qemu/tests/qemu-iotests/037.out | 4 +- qemu/tests/qemu-iotests/038 | 6 +- qemu/tests/qemu-iotests/038.out | 4 +- qemu/tests/qemu-iotests/039 | 47 +- qemu/tests/qemu-iotests/039.out | 46 +- qemu/tests/qemu-iotests/040 | 30 + qemu/tests/qemu-iotests/040.out | 4 +- qemu/tests/qemu-iotests/041 | 128 +- qemu/tests/qemu-iotests/041.out | 4 +- qemu/tests/qemu-iotests/042 | 1 - qemu/tests/qemu-iotests/043 | 1 - qemu/tests/qemu-iotests/043.out | 14 +- qemu/tests/qemu-iotests/046 | 1 - qemu/tests/qemu-iotests/046.out | 2 +- qemu/tests/qemu-iotests/047 | 1 - qemu/tests/qemu-iotests/049 | 1 - qemu/tests/qemu-iotests/049.out | 39 +- qemu/tests/qemu-iotests/050 | 10 +- qemu/tests/qemu-iotests/050.out | 6 +- qemu/tests/qemu-iotests/051 | 147 +- qemu/tests/qemu-iotests/051.out | 249 +- qemu/tests/qemu-iotests/051.pc.out | 525 ++ qemu/tests/qemu-iotests/052 | 1 - qemu/tests/qemu-iotests/053 | 1 - qemu/tests/qemu-iotests/054 | 1 - qemu/tests/qemu-iotests/055 | 19 +- qemu/tests/qemu-iotests/056 | 25 + qemu/tests/qemu-iotests/056.out | 4 +- qemu/tests/qemu-iotests/058 | 13 +- qemu/tests/qemu-iotests/059 | 11 +- qemu/tests/qemu-iotests/059.out | 49 +- qemu/tests/qemu-iotests/060 | 1 - qemu/tests/qemu-iotests/060.out | 2 +- qemu/tests/qemu-iotests/061 | 7 +- qemu/tests/qemu-iotests/061.out | 69 +- qemu/tests/qemu-iotests/062 | 1 - qemu/tests/qemu-iotests/063 | 1 - qemu/tests/qemu-iotests/064 | 1 - qemu/tests/qemu-iotests/066 | 1 - qemu/tests/qemu-iotests/067 | 21 +- qemu/tests/qemu-iotests/067.out | 419 +- qemu/tests/qemu-iotests/068 | 15 +- qemu/tests/qemu-iotests/069 | 1 - qemu/tests/qemu-iotests/069.out | 4 +- qemu/tests/qemu-iotests/070 | 1 - qemu/tests/qemu-iotests/070.out | 5 +- qemu/tests/qemu-iotests/071 | 55 +- qemu/tests/qemu-iotests/071.out | 16 +- qemu/tests/qemu-iotests/072 | 1 - qemu/tests/qemu-iotests/073 | 1 - qemu/tests/qemu-iotests/073.out | 2 +- qemu/tests/qemu-iotests/075 | 1 - qemu/tests/qemu-iotests/075.out | 14 +- qemu/tests/qemu-iotests/076 | 1 - qemu/tests/qemu-iotests/076.out | 6 +- qemu/tests/qemu-iotests/077 | 25 +- qemu/tests/qemu-iotests/078 | 1 - qemu/tests/qemu-iotests/078.out | 12 +- qemu/tests/qemu-iotests/079 | 1 - qemu/tests/qemu-iotests/079.out | 18 +- qemu/tests/qemu-iotests/080 | 1 - qemu/tests/qemu-iotests/080.out | 36 +- qemu/tests/qemu-iotests/081 | 22 +- qemu/tests/qemu-iotests/081.out | 7 +- qemu/tests/qemu-iotests/082 | 1 - qemu/tests/qemu-iotests/082.out | 4 +- qemu/tests/qemu-iotests/083 | 14 +- qemu/tests/qemu-iotests/083.out | 44 +- qemu/tests/qemu-iotests/084 | 1 - qemu/tests/qemu-iotests/085 | 120 +- qemu/tests/qemu-iotests/085.out | 72 +- qemu/tests/qemu-iotests/086 | 3 +- qemu/tests/qemu-iotests/087 | 6 +- qemu/tests/qemu-iotests/087.out | 44 +- qemu/tests/qemu-iotests/088 | 1 - qemu/tests/qemu-iotests/088.out | 12 +- qemu/tests/qemu-iotests/089 | 1 - qemu/tests/qemu-iotests/090 | 1 - qemu/tests/qemu-iotests/092 | 1 - qemu/tests/qemu-iotests/092.out | 24 +- qemu/tests/qemu-iotests/093 | 65 +- qemu/tests/qemu-iotests/093.out | 4 +- qemu/tests/qemu-iotests/094 | 9 +- qemu/tests/qemu-iotests/095 | 10 +- qemu/tests/qemu-iotests/095.out | 8 +- qemu/tests/qemu-iotests/096 | 69 + qemu/tests/qemu-iotests/096.out | 5 + qemu/tests/qemu-iotests/097 | 1 - qemu/tests/qemu-iotests/097.out | 16 +- qemu/tests/qemu-iotests/098 | 1 - qemu/tests/qemu-iotests/098.out | 8 +- qemu/tests/qemu-iotests/099 | 1 - qemu/tests/qemu-iotests/100 | 1 - qemu/tests/qemu-iotests/101 | 1 - qemu/tests/qemu-iotests/102 | 1 - qemu/tests/qemu-iotests/103 | 1 - qemu/tests/qemu-iotests/103.out | 8 +- qemu/tests/qemu-iotests/104 | 1 - qemu/tests/qemu-iotests/105 | 1 - qemu/tests/qemu-iotests/107 | 1 - qemu/tests/qemu-iotests/108 | 1 - qemu/tests/qemu-iotests/109 | 1 - qemu/tests/qemu-iotests/109.out | 38 +- qemu/tests/qemu-iotests/110 | 1 - qemu/tests/qemu-iotests/110.out | 9 +- qemu/tests/qemu-iotests/111 | 1 - qemu/tests/qemu-iotests/112 | 110 +- qemu/tests/qemu-iotests/112.out | 75 +- qemu/tests/qemu-iotests/113 | 1 - qemu/tests/qemu-iotests/114 | 1 - qemu/tests/qemu-iotests/114.out | 4 +- qemu/tests/qemu-iotests/115 | 1 - qemu/tests/qemu-iotests/115.out | 2 +- qemu/tests/qemu-iotests/116 | 1 - qemu/tests/qemu-iotests/116.out | 14 +- qemu/tests/qemu-iotests/117 | 85 + qemu/tests/qemu-iotests/117.out | 14 + qemu/tests/qemu-iotests/118 | 677 ++ qemu/tests/qemu-iotests/118.out | 5 + qemu/tests/qemu-iotests/119 | 3 +- qemu/tests/qemu-iotests/120 | 3 +- qemu/tests/qemu-iotests/121 | 1 - qemu/tests/qemu-iotests/121.out | 4 +- qemu/tests/qemu-iotests/122 | 1 - qemu/tests/qemu-iotests/122.out | 12 +- qemu/tests/qemu-iotests/123 | 1 - qemu/tests/qemu-iotests/124 | 316 +- qemu/tests/qemu-iotests/124.out | 4 +- qemu/tests/qemu-iotests/128 | 10 +- qemu/tests/qemu-iotests/130 | 9 +- qemu/tests/qemu-iotests/130.out | 8 +- qemu/tests/qemu-iotests/131 | 1 - qemu/tests/qemu-iotests/131.out | 2 +- qemu/tests/qemu-iotests/133 | 89 + qemu/tests/qemu-iotests/133.out | 22 + qemu/tests/qemu-iotests/134 | 1 - qemu/tests/qemu-iotests/134.out | 18 - qemu/tests/qemu-iotests/135 | 1 - qemu/tests/qemu-iotests/136 | 349 + qemu/tests/qemu-iotests/136.out | 5 + qemu/tests/qemu-iotests/137 | 144 + qemu/tests/qemu-iotests/137.out | 46 + qemu/tests/qemu-iotests/138 | 72 + qemu/tests/qemu-iotests/138.out | 9 + qemu/tests/qemu-iotests/139 | 416 ++ qemu/tests/qemu-iotests/139.out | 5 + qemu/tests/qemu-iotests/140 | 95 + qemu/tests/qemu-iotests/140.out | 15 + qemu/tests/qemu-iotests/141 | 185 + qemu/tests/qemu-iotests/141.out | 59 + qemu/tests/qemu-iotests/142 | 358 + qemu/tests/qemu-iotests/142.out | 750 ++ qemu/tests/qemu-iotests/143 | 72 + qemu/tests/qemu-iotests/143.out | 7 + qemu/tests/qemu-iotests/144 | 114 + qemu/tests/qemu-iotests/144.out | 24 + qemu/tests/qemu-iotests/145 | 51 + qemu/tests/qemu-iotests/145.out | 5 + qemu/tests/qemu-iotests/146 | 165 + qemu/tests/qemu-iotests/146.out | 70 + qemu/tests/qemu-iotests/148 | 140 + qemu/tests/qemu-iotests/148.out | 5 + qemu/tests/qemu-iotests/149 | 519 ++ qemu/tests/qemu-iotests/149.out | 1880 +++++ qemu/tests/qemu-iotests/150 | 73 + qemu/tests/qemu-iotests/150.out | 11 + qemu/tests/qemu-iotests/152 | 62 + qemu/tests/qemu-iotests/152.out | 5 + qemu/tests/qemu-iotests/check | 34 +- qemu/tests/qemu-iotests/common | 11 +- qemu/tests/qemu-iotests/common.config | 63 +- qemu/tests/qemu-iotests/common.filter | 121 +- qemu/tests/qemu-iotests/common.qemu | 31 +- qemu/tests/qemu-iotests/common.rc | 76 +- qemu/tests/qemu-iotests/group | 19 + qemu/tests/qemu-iotests/iotests.py | 141 +- qemu/tests/qemu-iotests/qed.py | 2 +- .../sample_images/d2v-zerofilled.vhd.bz2 | Bin 0 -> 1021 bytes .../sample_images/hyperv2012r2-dynamic.vhd.bz2 | Bin 0 -> 214 bytes .../sample_images/virtualpc-dynamic.vhd.bz2 | Bin 0 -> 212 bytes qemu/tests/qemu-iotests/socket_scm_helper.c | 8 +- qemu/tests/qom-test.c | 25 +- qemu/tests/rcutorture.c | 5 +- qemu/tests/rtc-test.c | 5 +- qemu/tests/rtl8139-test.c | 5 +- qemu/tests/spapr-phb-test.c | 1 + qemu/tests/tcg/linux-test.c | 1 + qemu/tests/tcg/test-i386-fprem.c | 4 +- qemu/tests/tcg/testthread.c | 1 - qemu/tests/tco-test.c | 4 +- qemu/tests/test-aio.c | 89 +- qemu/tests/test-base64.c | 110 + qemu/tests/test-bitops.c | 3 +- qemu/tests/test-blockjob-txn.c | 251 + qemu/tests/test-coroutine.c | 5 +- qemu/tests/test-crypto-afsplit.c | 194 + qemu/tests/test-crypto-block.c | 363 + qemu/tests/test-crypto-cipher.c | 305 +- qemu/tests/test-crypto-hash.c | 6 + qemu/tests/test-crypto-ivgen.c | 174 + qemu/tests/test-crypto-pbkdf.c | 393 + qemu/tests/test-crypto-secret.c | 455 ++ qemu/tests/test-crypto-tlscredsx509.c | 730 ++ qemu/tests/test-crypto-tlssession.c | 534 ++ qemu/tests/test-crypto-xts.c | 423 ++ qemu/tests/test-cutils.c | 1388 +++- qemu/tests/test-filter-mirror.c | 93 + qemu/tests/test-filter-redirector.c | 221 + qemu/tests/test-hbitmap.c | 10 +- qemu/tests/test-int128.c | 3 +- qemu/tests/test-io-channel-buffer.c | 51 + qemu/tests/test-io-channel-command.c | 131 + qemu/tests/test-io-channel-file.c | 123 + qemu/tests/test-io-channel-socket.c | 568 ++ qemu/tests/test-io-channel-tls.c | 340 + qemu/tests/test-io-task.c | 270 + qemu/tests/test-iov.c | 1 + qemu/tests/test-logging.c | 141 + qemu/tests/test-mul64.c | 3 +- qemu/tests/test-netfilter.c | 201 + qemu/tests/test-opts-visitor.c | 14 +- qemu/tests/test-qdev-global-props.c | 20 +- qemu/tests/test-qemu-opts.c | 2 +- qemu/tests/test-qga.c | 869 +++ qemu/tests/test-qmp-commands.c | 43 +- qemu/tests/test-qmp-event.c | 12 +- qemu/tests/test-qmp-input-strict.c | 162 +- qemu/tests/test-qmp-input-visitor.c | 472 +- qemu/tests/test-qmp-output-visitor.c | 289 +- qemu/tests/test-rcu-list.c | 6 +- qemu/tests/test-rfifolock.c | 1 + qemu/tests/test-string-input-visitor.c | 37 +- qemu/tests/test-string-output-visitor.c | 42 +- qemu/tests/test-thread-pool.c | 6 +- qemu/tests/test-throttle.c | 118 +- qemu/tests/test-timed-average.c | 90 + qemu/tests/test-visitor-serialization.c | 148 +- qemu/tests/test-vmstate.c | 3 +- qemu/tests/test-write-threshold.c | 2 +- qemu/tests/test-x86-cpuid.c | 1 + qemu/tests/test-xbzrle.c | 9 +- qemu/tests/tmp105-test.c | 1 + qemu/tests/tpci200-test.c | 3 +- qemu/tests/usb-hcd-ehci-test.c | 4 +- qemu/tests/usb-hcd-ohci-test.c | 3 +- qemu/tests/usb-hcd-uhci-test.c | 3 +- qemu/tests/usb-hcd-xhci-test.c | 3 +- qemu/tests/vhost-user-bridge.c | 1418 ++++ qemu/tests/vhost-user-test.c | 576 +- qemu/tests/virtio-9p-test.c | 4 +- qemu/tests/virtio-balloon-test.c | 3 +- qemu/tests/virtio-blk-test.c | 5 +- qemu/tests/virtio-console-test.c | 3 +- qemu/tests/virtio-net-test.c | 240 +- qemu/tests/virtio-rng-test.c | 3 +- qemu/tests/virtio-scsi-test.c | 104 +- qemu/tests/virtio-serial-test.c | 3 +- qemu/tests/vmxnet3-test.c | 3 +- qemu/tests/wdt_ib700-test.c | 3 +- qemu/thread-pool.c | 4 +- qemu/thunk.c | 4 +- qemu/tpm.c | 16 +- qemu/trace-events | 449 +- qemu/trace/Makefile.objs | 48 +- qemu/trace/control-internal.h | 16 +- qemu/trace/control.c | 103 +- qemu/trace/control.h | 46 +- qemu/trace/event-internal.h | 2 - qemu/trace/ftrace.c | 5 +- qemu/trace/ftrace.h | 1 - qemu/trace/qmp.c | 2 +- qemu/trace/simple.c | 15 +- qemu/trace/simple.h | 7 +- qemu/translate-all.c | 660 +- qemu/translate-common.c | 57 + qemu/ui/Makefile.objs | 9 +- qemu/ui/cocoa.m | 500 +- qemu/ui/console-gl.c | 8 +- qemu/ui/console.c | 310 +- qemu/ui/curses.c | 65 +- qemu/ui/curses_keys.h | 8 +- qemu/ui/cursor.c | 1 + qemu/ui/egl-context.c | 35 + qemu/ui/egl-helpers.c | 137 +- qemu/ui/gtk-egl.c | 132 +- qemu/ui/gtk-gl-area.c | 224 + qemu/ui/gtk.c | 286 +- qemu/ui/input-keymap.c | 25 +- qemu/ui/input-legacy.c | 43 +- qemu/ui/input-linux.c | 507 ++ qemu/ui/input.c | 158 +- qemu/ui/keymaps.c | 3 +- qemu/ui/qemu-pixman.c | 1 + qemu/ui/sdl.c | 28 +- qemu/ui/sdl2-2d.c | 14 + qemu/ui/sdl2-gl.c | 136 + qemu/ui/sdl2-input.c | 1 + qemu/ui/sdl2.c | 33 +- qemu/ui/sdl_zoom.c | 4 +- qemu/ui/shader.c | 32 +- qemu/ui/spice-core.c | 53 +- qemu/ui/spice-display.c | 170 +- qemu/ui/spice-input.c | 7 +- qemu/ui/vnc-auth-sasl.c | 96 +- qemu/ui/vnc-auth-vencrypt.c | 93 +- qemu/ui/vnc-enc-hextile.c | 1 + qemu/ui/vnc-enc-tight.c | 3 +- qemu/ui/vnc-enc-zlib.c | 1 + qemu/ui/vnc-enc-zrle-template.c | 2 +- qemu/ui/vnc-enc-zrle.c | 1 + qemu/ui/vnc-enc-zywrle-template.c | 1 + qemu/ui/vnc-jobs.c | 53 +- qemu/ui/vnc-palette.c | 2 +- qemu/ui/vnc-palette.h | 2 - qemu/ui/vnc-tls.c | 474 -- qemu/ui/vnc-tls.h | 69 - qemu/ui/vnc-ws.c | 390 +- qemu/ui/vnc-ws.h | 73 +- qemu/ui/vnc.c | 1220 ++-- qemu/ui/vnc.h | 61 +- qemu/ui/x_keymap.c | 1 + qemu/user-exec.c | 7 +- qemu/util/Makefile.objs | 20 +- qemu/util/acl.c | 1 + qemu/util/base64.c | 60 + qemu/util/bitmap.c | 3 +- qemu/util/bitops.c | 1 + qemu/util/buffer.c | 172 + qemu/util/compatfd.c | 1 + qemu/util/coroutine-gthread.c | 199 + qemu/util/coroutine-sigaltstack.c | 290 + qemu/util/coroutine-ucontext.c | 192 + qemu/util/coroutine-win32.c | 102 + qemu/util/crc32c.c | 1 + qemu/util/cutils.c | 363 +- qemu/util/envlist.c | 1 + qemu/util/error.c | 216 +- qemu/util/event_notifier-posix.c | 14 +- qemu/util/event_notifier-win32.c | 2 + qemu/util/fifo8.c | 1 + qemu/util/getauxval.c | 2 +- qemu/util/hbitmap.c | 4 +- qemu/util/hexdump.c | 34 +- qemu/util/host-utils.c | 3 +- qemu/util/id.c | 39 + qemu/util/iov.c | 11 +- qemu/util/log.c | 313 + qemu/util/memfd.c | 162 + qemu/util/mmap-alloc.c | 110 + qemu/util/module.c | 2 +- qemu/util/notify.c | 1 + qemu/util/osdep.c | 29 +- qemu/util/oslib-posix.c | 133 +- qemu/util/oslib-win32.c | 298 +- qemu/util/path.c | 9 +- qemu/util/qemu-config.c | 10 +- qemu/util/qemu-coroutine-io.c | 90 + qemu/util/qemu-coroutine-lock.c | 187 + qemu/util/qemu-coroutine-sleep.c | 42 + qemu/util/qemu-coroutine.c | 147 + qemu/util/qemu-error.c | 12 +- qemu/util/qemu-openpty.c | 2 +- qemu/util/qemu-option.c | 57 +- qemu/util/qemu-progress.c | 6 +- qemu/util/qemu-sockets.c | 560 +- qemu/util/qemu-thread-posix.c | 24 +- qemu/util/qemu-thread-win32.c | 69 +- qemu/util/qemu-timer-common.c | 1 + qemu/util/rcu.c | 54 +- qemu/util/readline.c | 2 + qemu/util/rfifolock.c | 2 +- qemu/util/throttle.c | 137 +- qemu/util/timed-average.c | 231 + qemu/util/unicode.c | 3 +- qemu/util/uri.c | 3 +- qemu/vl.c | 725 +- qemu/xen-common-stub.c | 1 + qemu/xen-common.c | 11 +- qemu/xen-hvm-stub.c | 13 +- qemu/xen-hvm.c | 236 +- qemu/xen-mapcache.c | 14 +- 4154 files changed, 289394 insertions(+), 128032 deletions(-) create mode 100755 qemu/block/blkreplay.c create mode 100644 qemu/block/crypto.c create mode 100644 qemu/block/dirty-bitmap.c delete mode 100644 qemu/bsd-user/i386/syscall.h create mode 100644 qemu/bsd-user/i386/target_syscall.h delete mode 100644 qemu/bsd-user/sparc/syscall.h create mode 100644 qemu/bsd-user/sparc/target_syscall.h delete mode 100644 qemu/bsd-user/sparc64/syscall.h create mode 100644 qemu/bsd-user/sparc64/target_syscall.h delete mode 100644 qemu/bsd-user/x86_64/syscall.h create mode 100644 qemu/bsd-user/x86_64/target_syscall.h create mode 100644 qemu/contrib/ivshmem-client/Makefile.objs create mode 100644 qemu/contrib/ivshmem-client/ivshmem-client.c create mode 100644 qemu/contrib/ivshmem-client/ivshmem-client.h create mode 100644 qemu/contrib/ivshmem-client/main.c create mode 100644 qemu/contrib/ivshmem-server/Makefile.objs create mode 100644 qemu/contrib/ivshmem-server/ivshmem-server.c create mode 100644 qemu/contrib/ivshmem-server/ivshmem-server.h create mode 100644 qemu/contrib/ivshmem-server/main.c delete mode 100644 qemu/coroutine-gthread.c delete mode 100644 qemu/coroutine-sigaltstack.c delete mode 100644 qemu/coroutine-ucontext.c delete mode 100644 qemu/coroutine-win32.c create mode 100644 qemu/cpu-exec-common.c create mode 100644 qemu/crypto/afsplit.c create mode 100644 qemu/crypto/block-luks.c create mode 100644 qemu/crypto/block-luks.h create mode 100644 qemu/crypto/block-qcow.c create mode 100644 qemu/crypto/block-qcow.h create mode 100644 qemu/crypto/block.c create mode 100644 qemu/crypto/blockpriv.h create mode 100644 qemu/crypto/ivgen-essiv.c create mode 100644 qemu/crypto/ivgen-essiv.h create mode 100644 qemu/crypto/ivgen-plain.c create mode 100644 qemu/crypto/ivgen-plain.h create mode 100644 qemu/crypto/ivgen-plain64.c create mode 100644 qemu/crypto/ivgen-plain64.h create mode 100644 qemu/crypto/ivgen.c create mode 100644 qemu/crypto/ivgenpriv.h create mode 100644 qemu/crypto/pbkdf-gcrypt.c create mode 100644 qemu/crypto/pbkdf-nettle.c create mode 100644 qemu/crypto/pbkdf-stub.c create mode 100644 qemu/crypto/pbkdf.c create mode 100644 qemu/crypto/random-gcrypt.c create mode 100644 qemu/crypto/random-gnutls.c create mode 100644 qemu/crypto/random-stub.c create mode 100644 qemu/crypto/secret.c create mode 100644 qemu/crypto/tlscreds.c create mode 100644 qemu/crypto/tlscredsanon.c create mode 100644 qemu/crypto/tlscredspriv.h create mode 100644 qemu/crypto/tlscredsx509.c create mode 100644 qemu/crypto/tlssession.c create mode 100644 qemu/crypto/xts.c create mode 100644 qemu/default-configs/mips-softmmu-common.mak create mode 100644 qemu/default-configs/tilegx-linux-user.mak delete mode 100644 qemu/disas/libvixl/a64/assembler-a64.h delete mode 100644 qemu/disas/libvixl/a64/constants-a64.h delete mode 100644 qemu/disas/libvixl/a64/cpu-a64.h delete mode 100644 qemu/disas/libvixl/a64/decoder-a64.cc delete mode 100644 qemu/disas/libvixl/a64/decoder-a64.h delete mode 100644 qemu/disas/libvixl/a64/disasm-a64.cc delete mode 100644 qemu/disas/libvixl/a64/disasm-a64.h delete mode 100644 qemu/disas/libvixl/a64/instructions-a64.cc delete mode 100644 qemu/disas/libvixl/a64/instructions-a64.h delete mode 100644 qemu/disas/libvixl/code-buffer.h delete mode 100644 qemu/disas/libvixl/globals.h delete mode 100644 qemu/disas/libvixl/platform.h delete mode 100644 qemu/disas/libvixl/utils.cc delete mode 100644 qemu/disas/libvixl/utils.h create mode 100644 qemu/disas/libvixl/vixl/a64/assembler-a64.h create mode 100644 qemu/disas/libvixl/vixl/a64/constants-a64.h create mode 100644 qemu/disas/libvixl/vixl/a64/cpu-a64.h create mode 100644 qemu/disas/libvixl/vixl/a64/decoder-a64.cc create mode 100644 qemu/disas/libvixl/vixl/a64/decoder-a64.h create mode 100644 qemu/disas/libvixl/vixl/a64/disasm-a64.cc create mode 100644 qemu/disas/libvixl/vixl/a64/disasm-a64.h create mode 100644 qemu/disas/libvixl/vixl/a64/instructions-a64.cc create mode 100644 qemu/disas/libvixl/vixl/a64/instructions-a64.h create mode 100644 qemu/disas/libvixl/vixl/code-buffer.h create mode 100644 qemu/disas/libvixl/vixl/compiler-intrinsics.cc create mode 100644 qemu/disas/libvixl/vixl/compiler-intrinsics.h create mode 100644 qemu/disas/libvixl/vixl/globals.h create mode 100644 qemu/disas/libvixl/vixl/invalset.h create mode 100644 qemu/disas/libvixl/vixl/platform.h create mode 100644 qemu/disas/libvixl/vixl/utils.cc create mode 100644 qemu/disas/libvixl/vixl/utils.h create mode 100644 qemu/docs/build-system.txt delete mode 100644 qemu/docs/libcacard.txt create mode 100644 qemu/docs/qcow2-cache.txt create mode 100644 qemu/docs/qmp-events.txt rename qemu/docs/{qmp/README => qmp-intro.txt} (100%) create mode 100644 qemu/docs/qmp-spec.txt delete mode 100644 qemu/docs/qmp/qmp-events.txt delete mode 100644 qemu/docs/qmp/qmp-spec.txt create mode 100644 qemu/docs/replay.txt create mode 100644 qemu/docs/specs/ivshmem-spec.txt delete mode 100644 qemu/docs/specs/ivshmem_device_spec.txt create mode 100644 qemu/docs/specs/parallels.txt create mode 100644 qemu/docs/throttle.txt create mode 100644 qemu/docs/virtio-migration.txt create mode 100644 qemu/docs/win32-qemu-event.promela create mode 100644 qemu/fsdev/9p-iov-marshal.c create mode 100644 qemu/fsdev/9p-iov-marshal.h create mode 100644 qemu/fsdev/9p-marshal.c create mode 100644 qemu/fsdev/9p-marshal.h delete mode 100644 qemu/fsdev/virtio-9p-marshal.c delete mode 100644 qemu/fsdev/virtio-9p-marshal.h create mode 100644 qemu/gdb-xml/power-vsx.xml create mode 100644 qemu/gdb-xml/s390-cr.xml create mode 100644 qemu/gdb-xml/s390-virt.xml create mode 100644 qemu/hmp-commands-info.hx create mode 100644 qemu/hw/9pfs/9p-handle.c create mode 100644 qemu/hw/9pfs/9p-local.c create mode 100644 qemu/hw/9pfs/9p-posix-acl.c create mode 100644 qemu/hw/9pfs/9p-proxy.c create mode 100644 qemu/hw/9pfs/9p-proxy.h create mode 100644 qemu/hw/9pfs/9p-synth.c create mode 100644 qemu/hw/9pfs/9p-synth.h create mode 100644 qemu/hw/9pfs/9p-xattr-user.c create mode 100644 qemu/hw/9pfs/9p-xattr.c create mode 100644 qemu/hw/9pfs/9p-xattr.h create mode 100644 qemu/hw/9pfs/9p.c create mode 100644 qemu/hw/9pfs/9p.h create mode 100644 qemu/hw/9pfs/coth.c create mode 100644 qemu/hw/9pfs/coth.h delete mode 100644 qemu/hw/9pfs/virtio-9p-coth.c delete mode 100644 qemu/hw/9pfs/virtio-9p-coth.h delete mode 100644 qemu/hw/9pfs/virtio-9p-handle.c delete mode 100644 qemu/hw/9pfs/virtio-9p-local.c delete mode 100644 qemu/hw/9pfs/virtio-9p-posix-acl.c delete mode 100644 qemu/hw/9pfs/virtio-9p-proxy.c delete mode 100644 qemu/hw/9pfs/virtio-9p-proxy.h delete mode 100644 qemu/hw/9pfs/virtio-9p-synth.c delete mode 100644 qemu/hw/9pfs/virtio-9p-synth.h delete mode 100644 qemu/hw/9pfs/virtio-9p-xattr-user.c delete mode 100644 qemu/hw/9pfs/virtio-9p-xattr.c delete mode 100644 qemu/hw/9pfs/virtio-9p-xattr.h delete mode 100644 qemu/hw/9pfs/virtio-9p.c create mode 100644 qemu/hw/acpi/cpu_hotplug_acpi_table.c create mode 100644 qemu/hw/acpi/memory_hotplug_acpi_table.c create mode 100644 qemu/hw/acpi/nvdimm.c create mode 100644 qemu/hw/arm/ast2400.c create mode 100644 qemu/hw/arm/bcm2835_peripherals.c create mode 100644 qemu/hw/arm/bcm2836.c create mode 100644 qemu/hw/arm/fsl-imx25.c create mode 100644 qemu/hw/arm/fsl-imx31.c create mode 100644 qemu/hw/arm/imx25_pdk.c create mode 100644 qemu/hw/arm/palmetto-bmc.c create mode 100644 qemu/hw/arm/raspi.c create mode 100644 qemu/hw/char/bcm2835_aux.c delete mode 100644 qemu/hw/cpu/icc_bus.c create mode 100644 qemu/hw/display/bcm2835_fb.c create mode 100644 qemu/hw/display/virtio-gpu-3d.c create mode 100644 qemu/hw/dma/bcm2835_dma.c create mode 100644 qemu/hw/gpio/gpio_key.c create mode 100644 qemu/hw/gpio/imx_gpio.c create mode 100644 qemu/hw/i2c/imx_i2c.c delete mode 100644 qemu/hw/i386/acpi-dsdt-cpu-hotplug.dsl delete mode 100644 qemu/hw/i386/acpi-dsdt-dbug.dsl delete mode 100644 qemu/hw/i386/acpi-dsdt-hpet.dsl delete mode 100644 qemu/hw/i386/acpi-dsdt-isa.dsl delete mode 100644 qemu/hw/i386/acpi-dsdt-mem-hotplug.dsl delete mode 100644 qemu/hw/i386/acpi-dsdt.dsl delete mode 100644 qemu/hw/i386/acpi-dsdt.hex.generated create mode 100644 qemu/hw/i386/pci-assign-load-rom.c delete mode 100644 qemu/hw/i386/q35-acpi-dsdt.dsl delete mode 100644 qemu/hw/i386/q35-acpi-dsdt.hex.generated delete mode 100644 qemu/hw/i386/smbios.c create mode 100644 qemu/hw/intc/arm_gicv3_common.c create mode 100644 qemu/hw/intc/arm_gicv3_kvm.c create mode 100644 qemu/hw/intc/aspeed_vic.c create mode 100644 qemu/hw/intc/bcm2835_ic.c create mode 100644 qemu/hw/intc/bcm2836_control.c create mode 100644 qemu/hw/intc/vgic_common.h create mode 100644 qemu/hw/ipmi/Makefile.objs create mode 100644 qemu/hw/ipmi/ipmi.c create mode 100644 qemu/hw/ipmi/ipmi_bmc_extern.c create mode 100644 qemu/hw/ipmi/ipmi_bmc_sim.c create mode 100644 qemu/hw/ipmi/isa_ipmi_bt.c create mode 100644 qemu/hw/ipmi/isa_ipmi_kcs.c create mode 100644 qemu/hw/mem/nvdimm.c create mode 100644 qemu/hw/mips/cps.c create mode 100644 qemu/hw/misc/bcm2835_mbox.c create mode 100644 qemu/hw/misc/bcm2835_property.c create mode 100644 qemu/hw/misc/hyperv_testdev.c create mode 100644 qemu/hw/misc/imx25_ccm.c create mode 100644 qemu/hw/misc/imx31_ccm.c create mode 100644 qemu/hw/misc/imx6_ccm.c create mode 100644 qemu/hw/misc/mips_cmgcr.c create mode 100644 qemu/hw/misc/mips_cpc.c create mode 100644 qemu/hw/misc/mips_itu.c create mode 100644 qemu/hw/misc/zynq-xadc.c create mode 100644 qemu/hw/net/imx_fec.c create mode 100644 qemu/hw/ppc/spapr_rng.c create mode 100644 qemu/hw/s390x/s390-skeys-kvm.c create mode 100644 qemu/hw/s390x/s390-skeys.c delete mode 100644 qemu/hw/s390x/s390-virtio-bus.c delete mode 100644 qemu/hw/s390x/s390-virtio-bus.h create mode 100644 qemu/hw/scsi/mpi.h create mode 100644 qemu/hw/scsi/mptconfig.c create mode 100644 qemu/hw/scsi/mptendian.c create mode 100644 qemu/hw/scsi/mptsas.c create mode 100644 qemu/hw/scsi/mptsas.h create mode 100644 qemu/hw/sd/core.c create mode 100644 qemu/hw/sd/sdhci-internal.h delete mode 100644 qemu/hw/sd/sdhci.h create mode 100644 qemu/hw/smbios/Makefile.objs create mode 100644 qemu/hw/smbios/smbios.c create mode 100644 qemu/hw/timer/aspeed_timer.c delete mode 100644 qemu/hw/timer/tusb6010.c create mode 100644 qemu/hw/usb/tusb6010.c create mode 100644 qemu/hw/vfio/amd-xgbe.c create mode 100644 qemu/hw/vfio/pci-quirks.c create mode 100644 qemu/hw/vfio/pci.h delete mode 100644 qemu/hw/virtio/dataplane/Makefile.objs delete mode 100644 qemu/hw/virtio/dataplane/vring.c create mode 100644 qemu/hw/xen/xen_pt_graphics.c delete mode 100644 qemu/include/block/coroutine.h delete mode 100644 qemu/include/block/coroutine_int.h create mode 100644 qemu/include/block/dirty-bitmap.h delete mode 100644 qemu/include/config.h create mode 100644 qemu/include/crypto/afsplit.h create mode 100644 qemu/include/crypto/block.h create mode 100644 qemu/include/crypto/ivgen.h create mode 100644 qemu/include/crypto/pbkdf.h create mode 100644 qemu/include/crypto/random.h create mode 100644 qemu/include/crypto/secret.h create mode 100644 qemu/include/crypto/tlscreds.h create mode 100644 qemu/include/crypto/tlscredsanon.h create mode 100644 qemu/include/crypto/tlscredsx509.h create mode 100644 qemu/include/crypto/tlssession.h create mode 100644 qemu/include/crypto/xts.h create mode 100644 qemu/include/exec/log.h delete mode 100644 qemu/include/exec/spinlock.h create mode 100644 qemu/include/hw/arm/ast2400.h create mode 100644 qemu/include/hw/arm/bcm2835_peripherals.h create mode 100644 qemu/include/hw/arm/bcm2836.h create mode 100644 qemu/include/hw/arm/fsl-imx25.h create mode 100644 qemu/include/hw/arm/fsl-imx31.h delete mode 100644 qemu/include/hw/arm/imx.h create mode 100644 qemu/include/hw/arm/linux-boot-if.h create mode 100644 qemu/include/hw/arm/raspi_platform.h create mode 100644 qemu/include/hw/char/bcm2835_aux.h create mode 100644 qemu/include/hw/char/imx_serial.h delete mode 100644 qemu/include/hw/cpu/icc_bus.h create mode 100644 qemu/include/hw/display/bcm2835_fb.h create mode 100644 qemu/include/hw/dma/bcm2835_dma.h create mode 100644 qemu/include/hw/gpio/imx_gpio.h create mode 100644 qemu/include/hw/i2c/imx_i2c.h delete mode 100644 qemu/include/hw/i386/smbios.h create mode 100644 qemu/include/hw/intc/arm_gicv3_common.h create mode 100644 qemu/include/hw/intc/aspeed_vic.h create mode 100644 qemu/include/hw/intc/bcm2835_ic.h create mode 100644 qemu/include/hw/intc/bcm2836_control.h create mode 100644 qemu/include/hw/intc/imx_avic.h create mode 100644 qemu/include/hw/ipmi/ipmi.h create mode 100644 qemu/include/hw/isa/i8257.h create mode 100644 qemu/include/hw/mem/nvdimm.h create mode 100644 qemu/include/hw/mips/cps.h create mode 100644 qemu/include/hw/misc/bcm2835_mbox.h create mode 100644 qemu/include/hw/misc/bcm2835_mbox_defs.h create mode 100644 qemu/include/hw/misc/bcm2835_property.h create mode 100644 qemu/include/hw/misc/imx25_ccm.h create mode 100644 qemu/include/hw/misc/imx31_ccm.h create mode 100644 qemu/include/hw/misc/imx6_ccm.h create mode 100644 qemu/include/hw/misc/imx_ccm.h create mode 100644 qemu/include/hw/misc/ivshmem.h create mode 100644 qemu/include/hw/misc/mips_cmgcr.h create mode 100644 qemu/include/hw/misc/mips_cpc.h create mode 100644 qemu/include/hw/misc/mips_itu.h create mode 100644 qemu/include/hw/misc/zynq-xadc.h create mode 100644 qemu/include/hw/net/imx_fec.h create mode 100644 qemu/include/hw/nvram/fw_cfg_keys.h create mode 100644 qemu/include/hw/pci/pci-assign.h create mode 100644 qemu/include/hw/s390x/s390-virtio-ccw.h create mode 100644 qemu/include/hw/s390x/storage-keys.h delete mode 100644 qemu/include/hw/sd.h create mode 100644 qemu/include/hw/sd/sd.h create mode 100644 qemu/include/hw/sd/sdhci.h create mode 100644 qemu/include/hw/smbios/smbios.h delete mode 100644 qemu/include/hw/ssi.h create mode 100644 qemu/include/hw/ssi/ssi.h create mode 100644 qemu/include/hw/ssi/xilinx_spips.h create mode 100644 qemu/include/hw/timer/aspeed_timer.h create mode 100644 qemu/include/hw/timer/imx_epit.h create mode 100644 qemu/include/hw/timer/imx_gpt.h create mode 100644 qemu/include/hw/vfio/vfio-amd-xgbe.h delete mode 100644 qemu/include/hw/virtio/dataplane/vring-accessors.h delete mode 100644 qemu/include/hw/virtio/dataplane/vring.h delete mode 100644 qemu/include/hw/virtio/virtio-9p.h create mode 100644 qemu/include/io/channel-buffer.h create mode 100644 qemu/include/io/channel-command.h create mode 100644 qemu/include/io/channel-file.h create mode 100644 qemu/include/io/channel-socket.h create mode 100644 qemu/include/io/channel-tls.h create mode 100644 qemu/include/io/channel-util.h create mode 100644 qemu/include/io/channel-watch.h create mode 100644 qemu/include/io/channel-websock.h create mode 100644 qemu/include/io/channel.h create mode 100644 qemu/include/io/task.h create mode 100644 qemu/include/migration/postcopy-ram.h create mode 100644 qemu/include/monitor/hmp-target.h create mode 100644 qemu/include/net/filter.h create mode 100644 qemu/include/qemu/base64.h create mode 100644 qemu/include/qemu/bcd.h create mode 100644 qemu/include/qemu/buffer.h create mode 100644 qemu/include/qemu/coroutine.h create mode 100644 qemu/include/qemu/coroutine_int.h create mode 100644 qemu/include/qemu/cutils.h create mode 100644 qemu/include/qemu/help_option.h create mode 100644 qemu/include/qemu/id.h create mode 100644 qemu/include/qemu/memfd.h create mode 100644 qemu/include/qemu/mmap-alloc.h create mode 100644 qemu/include/qemu/path.h create mode 100644 qemu/include/qemu/timed-average.h delete mode 100644 qemu/include/qemu/tls.h create mode 100644 qemu/include/qemu/unicode.h create mode 100644 qemu/include/standard-headers/asm-x86/hyperv.h create mode 100644 qemu/include/standard-headers/linux/input-event-codes.h create mode 100644 qemu/include/sysemu/replay.h create mode 100644 qemu/include/ui/egl-context.h create mode 100644 qemu/io/Makefile.objs create mode 100644 qemu/io/channel-buffer.c create mode 100644 qemu/io/channel-command.c create mode 100644 qemu/io/channel-file.c create mode 100644 qemu/io/channel-socket.c create mode 100644 qemu/io/channel-tls.c create mode 100644 qemu/io/channel-util.c create mode 100644 qemu/io/channel-watch.c create mode 100644 qemu/io/channel-websock.c create mode 100644 qemu/io/channel.c create mode 100644 qemu/io/task.c delete mode 100644 qemu/libcacard/Makefile delete mode 100644 qemu/libcacard/cac.c delete mode 100644 qemu/libcacard/cac.h delete mode 100644 qemu/libcacard/card_7816.c delete mode 100644 qemu/libcacard/card_7816.h delete mode 100644 qemu/libcacard/card_7816t.h delete mode 100644 qemu/libcacard/event.c delete mode 100644 qemu/libcacard/eventt.h delete mode 100644 qemu/libcacard/libcacard.pc.in delete mode 100644 qemu/libcacard/libcacard.syms delete mode 100644 qemu/libcacard/link_test.c delete mode 100644 qemu/libcacard/vcard.c delete mode 100644 qemu/libcacard/vcard.h delete mode 100644 qemu/libcacard/vcard_emul.h delete mode 100644 qemu/libcacard/vcard_emul_nss.c delete mode 100644 qemu/libcacard/vcard_emul_type.c delete mode 100644 qemu/libcacard/vcard_emul_type.h delete mode 100644 qemu/libcacard/vcardt.c delete mode 100644 qemu/libcacard/vcardt.h delete mode 100644 qemu/libcacard/vcardt_internal.h delete mode 100644 qemu/libcacard/vevent.h delete mode 100644 qemu/libcacard/vreader.c delete mode 100644 qemu/libcacard/vreader.h delete mode 100644 qemu/libcacard/vreadert.h delete mode 100644 qemu/libcacard/vscard_common.h delete mode 100644 qemu/libcacard/vscclient.c create mode 100644 qemu/linux-headers/asm-arm/unistd.h create mode 100644 qemu/linux-headers/asm-arm64/unistd.h create mode 100644 qemu/linux-headers/asm-mips/unistd.h create mode 100644 qemu/linux-headers/asm-powerpc/unistd.h create mode 100644 qemu/linux-headers/asm-s390/unistd.h create mode 100644 qemu/linux-headers/asm-x86/unistd.h create mode 100644 qemu/linux-headers/asm-x86/unistd_32.h create mode 100644 qemu/linux-headers/asm-x86/unistd_64.h create mode 100644 qemu/linux-headers/asm-x86/unistd_x32.h create mode 100644 qemu/linux-headers/linux/userfaultfd.h delete mode 100644 qemu/linux-user/aarch64/syscall.h create mode 100644 qemu/linux-user/aarch64/target_syscall.h delete mode 100644 qemu/linux-user/alpha/syscall.h create mode 100644 qemu/linux-user/alpha/target_syscall.h delete mode 100644 qemu/linux-user/arm/syscall.h create mode 100644 qemu/linux-user/arm/target_syscall.h rename qemu/linux-user/cris/{syscall.h => target_syscall.h} (100%) delete mode 100644 qemu/linux-user/i386/syscall.h create mode 100644 qemu/linux-user/i386/target_syscall.h delete mode 100644 qemu/linux-user/m68k/syscall.h create mode 100644 qemu/linux-user/m68k/target_syscall.h rename qemu/linux-user/microblaze/{syscall.h => target_syscall.h} (100%) delete mode 100644 qemu/linux-user/mips/syscall.h create mode 100644 qemu/linux-user/mips/target_syscall.h delete mode 100644 qemu/linux-user/mips64/syscall.h create mode 100644 qemu/linux-user/mips64/target_syscall.h delete mode 100644 qemu/linux-user/openrisc/syscall.h create mode 100644 qemu/linux-user/openrisc/target_syscall.h delete mode 100644 qemu/linux-user/ppc/syscall.h create mode 100644 qemu/linux-user/ppc/target_syscall.h delete mode 100644 qemu/linux-user/s390x/syscall.h create mode 100644 qemu/linux-user/s390x/target_syscall.h delete mode 100644 qemu/linux-user/sh4/syscall.h create mode 100644 qemu/linux-user/sh4/target_syscall.h delete mode 100644 qemu/linux-user/sparc/syscall.h create mode 100644 qemu/linux-user/sparc/target_syscall.h delete mode 100644 qemu/linux-user/sparc64/syscall.h create mode 100644 qemu/linux-user/sparc64/target_syscall.h create mode 100644 qemu/linux-user/tilegx/syscall_nr.h create mode 100644 qemu/linux-user/tilegx/target_cpu.h create mode 100644 qemu/linux-user/tilegx/target_signal.h create mode 100644 qemu/linux-user/tilegx/target_structs.h create mode 100644 qemu/linux-user/tilegx/target_syscall.h create mode 100644 qemu/linux-user/tilegx/termbits.h rename qemu/linux-user/unicore32/{syscall.h => target_syscall.h} (100%) delete mode 100644 qemu/linux-user/x86_64/syscall.h create mode 100644 qemu/linux-user/x86_64/target_syscall.h create mode 100644 qemu/migration/postcopy-ram.c delete mode 100644 qemu/nbd.c create mode 100644 qemu/nbd/Makefile.objs create mode 100644 qemu/nbd/client.c create mode 100644 qemu/nbd/common.c create mode 100644 qemu/nbd/nbd-internal.h create mode 100644 qemu/nbd/server.c create mode 100644 qemu/net/filter-buffer.c create mode 100644 qemu/net/filter-mirror.c create mode 100644 qemu/net/filter.c delete mode 100644 qemu/pc-bios/q35-acpi-dsdt.aml create mode 100644 qemu/pc-bios/s390-ccw/scsi.h create mode 100644 qemu/pc-bios/s390-ccw/virtio-scsi.c create mode 100644 qemu/pc-bios/s390-ccw/virtio-scsi.h delete mode 100644 qemu/pc-bios/s390-zipl.rom create mode 100644 qemu/qapi/crypto.json create mode 100644 qemu/qapi/introspect.json delete mode 100644 qemu/qemu-coroutine-io.c delete mode 100644 qemu/qemu-coroutine-lock.c delete mode 100644 qemu/qemu-coroutine-sleep.c delete mode 100644 qemu/qemu-coroutine.c create mode 100644 qemu/qemu-ga.texi delete mode 100644 qemu/qemu-log.c create mode 100644 qemu/qobject/qobject.c create mode 100644 qemu/replay/Makefile.objs create mode 100755 qemu/replay/replay-char.c create mode 100644 qemu/replay/replay-events.c create mode 100644 qemu/replay/replay-input.c create mode 100644 qemu/replay/replay-internal.c create mode 100644 qemu/replay/replay-internal.h create mode 100644 qemu/replay/replay-time.c create mode 100644 qemu/replay/replay.c create mode 100644 qemu/roms/SLOF/board-js2x/slof/dma-function.fs create mode 100644 qemu/roms/SLOF/board-qemu/slof/archsupport.fs create mode 100644 qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1041.fs create mode 100644 qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1042.fs create mode 100644 qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1048.fs create mode 100644 qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1050.fs create mode 100644 qemu/roms/SLOF/board-qemu/slof/qemu-vga.fs delete mode 100644 qemu/roms/SLOF/board-qemu/slof/virtio.fs create mode 100644 qemu/roms/SLOF/lib/libhvcall/rfill.c create mode 100644 qemu/roms/SLOF/lib/libvirtio/virtio-internal.h delete mode 100644 qemu/roms/SLOF/slof/fs/archsupport.fs delete mode 100644 qemu/roms/SLOF/slof/fs/rmove.fs delete mode 100644 qemu/roms/config.ipxe.general.h create mode 100644 qemu/roms/ipxe/COPYING.GPLv2 create mode 100644 qemu/roms/ipxe/COPYING.UBDL delete mode 100644 qemu/roms/ipxe/COPYRIGHTS delete mode 100644 qemu/roms/ipxe/src/arch/i386/core/timer2.c delete mode 100644 qemu/roms/ipxe/src/arch/i386/include/bits/endian.h create mode 100644 qemu/roms/ipxe/src/arch/i386/include/bits/hyperv.h delete mode 100644 qemu/roms/ipxe/src/arch/i386/include/ipxe/timer2.h delete mode 100644 qemu/roms/ipxe/src/arch/i386/include/pic8259.h create mode 100644 qemu/roms/ipxe/src/arch/i386/interface/pcbios/int13con.c create mode 100644 qemu/roms/ipxe/src/arch/i386/prefix/unlzma.S create mode 100644 qemu/roms/ipxe/src/arch/i386/prefix/unlzma16.S delete mode 100644 qemu/roms/ipxe/src/arch/i386/prefix/unnrv2b.S delete mode 100644 qemu/roms/ipxe/src/arch/i386/prefix/unnrv2b16.S rename qemu/roms/ipxe/src/arch/{i386 => x86}/core/pic8259.c (100%) create mode 100644 qemu/roms/ipxe/src/arch/x86/core/pit8254.c create mode 100644 qemu/roms/ipxe/src/arch/x86/core/vram_settings.c create mode 100644 qemu/roms/ipxe/src/arch/x86/core/x86_uart.c create mode 100644 qemu/roms/ipxe/src/arch/x86/drivers/hyperv/hyperv.c create mode 100644 qemu/roms/ipxe/src/arch/x86/drivers/hyperv/hyperv.h create mode 100644 qemu/roms/ipxe/src/arch/x86/include/bits/endian.h create mode 100644 qemu/roms/ipxe/src/arch/x86/include/bits/uart.h create mode 100644 qemu/roms/ipxe/src/arch/x86/include/ipxe/pit8254.h create mode 100644 qemu/roms/ipxe/src/arch/x86/include/pic8259.h create mode 100644 qemu/roms/ipxe/src/arch/x86/interface/efi/efi_entropy.c create mode 100644 qemu/roms/ipxe/src/arch/x86_64/core/setjmp.S delete mode 100644 qemu/roms/ipxe/src/arch/x86_64/include/bits/endian.h create mode 100644 qemu/roms/ipxe/src/arch/x86_64/include/bits/hyperv.h create mode 100644 qemu/roms/ipxe/src/arch/x86_64/include/setjmp.h delete mode 100644 qemu/roms/ipxe/src/config/.gitignore create mode 100644 qemu/roms/ipxe/src/config/branding.h create mode 100644 qemu/roms/ipxe/src/config/config_crypto.c create mode 100644 qemu/roms/ipxe/src/config/config_http.c create mode 100644 qemu/roms/ipxe/src/config/config_usb.c create mode 100644 qemu/roms/ipxe/src/config/dhcp.h create mode 100644 qemu/roms/ipxe/src/config/fault.h rename qemu/{tests/qapi-schema/alternate-good.err => roms/ipxe/src/config/qemu/colour.h} (100%) rename qemu/{tests/qapi-schema/data-array-empty.out => roms/ipxe/src/config/qemu/console.h} (100%) rename qemu/{tests/qapi-schema/data-array-unknown.out => roms/ipxe/src/config/qemu/crypto.h} (100%) create mode 100644 qemu/roms/ipxe/src/config/qemu/general.h rename qemu/{tests/qapi-schema/data-int.out => roms/ipxe/src/config/qemu/serial.h} (100%) rename qemu/{tests/qapi-schema/data-member-array-bad.out => roms/ipxe/src/config/qemu/settings.h} (100%) rename qemu/{tests/qapi-schema/data-member-array.err => roms/ipxe/src/config/qemu/sideband.h} (100%) rename qemu/{tests/qapi-schema/data-member-unknown.out => roms/ipxe/src/config/qemu/usb.h} (100%) create mode 100644 qemu/roms/ipxe/src/config/usb.h rename qemu/{tests/qapi-schema/data-unknown.out => roms/ipxe/src/config/vbox/usb.h} (100%) create mode 100644 qemu/roms/ipxe/src/core/blocktrans.c create mode 100644 qemu/roms/ipxe/src/core/fault.c delete mode 100644 qemu/roms/ipxe/src/core/misc.c create mode 100644 qemu/roms/ipxe/src/core/pool.c delete mode 100644 qemu/roms/ipxe/src/core/serial_console.c delete mode 100644 qemu/roms/ipxe/src/core/strtoull.c create mode 100644 qemu/roms/ipxe/src/core/uart.c create mode 100644 qemu/roms/ipxe/src/crypto/aes.c delete mode 100644 qemu/roms/ipxe/src/crypto/axtls/aes.c delete mode 100644 qemu/roms/ipxe/src/crypto/axtls/bigint.h delete mode 100644 qemu/roms/ipxe/src/crypto/axtls/bigint_impl.h delete mode 100644 qemu/roms/ipxe/src/crypto/axtls/config.h delete mode 100644 qemu/roms/ipxe/src/crypto/axtls/crypto.h delete mode 100644 qemu/roms/ipxe/src/crypto/axtls/os_port.h delete mode 100644 qemu/roms/ipxe/src/crypto/axtls_aes.c create mode 100644 qemu/roms/ipxe/src/crypto/ecb.c create mode 100644 qemu/roms/ipxe/src/crypto/mishmash/rsa_aes_cbc_sha1.c create mode 100644 qemu/roms/ipxe/src/crypto/mishmash/rsa_aes_cbc_sha256.c create mode 100644 qemu/roms/ipxe/src/crypto/mishmash/rsa_md5.c create mode 100644 qemu/roms/ipxe/src/crypto/mishmash/rsa_sha1.c create mode 100644 qemu/roms/ipxe/src/crypto/mishmash/rsa_sha224.c create mode 100644 qemu/roms/ipxe/src/crypto/mishmash/rsa_sha256.c create mode 100644 qemu/roms/ipxe/src/crypto/mishmash/rsa_sha384.c create mode 100644 qemu/roms/ipxe/src/crypto/mishmash/rsa_sha512.c create mode 100644 qemu/roms/ipxe/src/crypto/sha224.c create mode 100644 qemu/roms/ipxe/src/crypto/sha384.c create mode 100644 qemu/roms/ipxe/src/crypto/sha512.c create mode 100644 qemu/roms/ipxe/src/crypto/sha512_224.c create mode 100644 qemu/roms/ipxe/src/crypto/sha512_256.c create mode 100644 qemu/roms/ipxe/src/drivers/bus/cdc.c create mode 100644 qemu/roms/ipxe/src/drivers/bus/usb.c create mode 100644 qemu/roms/ipxe/src/drivers/net/dm96xx.c create mode 100644 qemu/roms/ipxe/src/drivers/net/dm96xx.h create mode 100644 qemu/roms/ipxe/src/drivers/net/ecm.c create mode 100644 qemu/roms/ipxe/src/drivers/net/ecm.h create mode 100644 qemu/roms/ipxe/src/drivers/net/intelvf.c create mode 100644 qemu/roms/ipxe/src/drivers/net/intelvf.h create mode 100644 qemu/roms/ipxe/src/drivers/net/intelxvf.c create mode 100644 qemu/roms/ipxe/src/drivers/net/intelxvf.h create mode 100644 qemu/roms/ipxe/src/drivers/net/ncm.c create mode 100644 qemu/roms/ipxe/src/drivers/net/ncm.h create mode 100644 qemu/roms/ipxe/src/drivers/net/netvsc.c create mode 100644 qemu/roms/ipxe/src/drivers/net/netvsc.h create mode 100644 qemu/roms/ipxe/src/drivers/net/smsc75xx.c create mode 100644 qemu/roms/ipxe/src/drivers/net/smsc75xx.h create mode 100644 qemu/roms/ipxe/src/drivers/usb/ehci.c create mode 100644 qemu/roms/ipxe/src/drivers/usb/ehci.h create mode 100644 qemu/roms/ipxe/src/drivers/usb/uhci.c create mode 100644 qemu/roms/ipxe/src/drivers/usb/uhci.h create mode 100644 qemu/roms/ipxe/src/drivers/usb/usbhid.c create mode 100644 qemu/roms/ipxe/src/drivers/usb/usbhub.c create mode 100644 qemu/roms/ipxe/src/drivers/usb/usbhub.h create mode 100644 qemu/roms/ipxe/src/drivers/usb/usbkbd.c create mode 100644 qemu/roms/ipxe/src/drivers/usb/usbkbd.h create mode 100644 qemu/roms/ipxe/src/drivers/usb/usbnet.c create mode 100644 qemu/roms/ipxe/src/drivers/usb/xhci.c create mode 100644 qemu/roms/ipxe/src/drivers/usb/xhci.h create mode 100644 qemu/roms/ipxe/src/hci/jumpscroll.c delete mode 100644 qemu/roms/ipxe/src/include/.gitignore delete mode 100644 qemu/roms/ipxe/src/include/big_bswap.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/blocktrans.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/cdc.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/ecb.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/efi/Protocol/Rng.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/efi/efi_entropy.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/efi/efi_time.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/efi/efi_watchdog.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/fault.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/hyperv.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/jumpscroll.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/pccrc.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/pccrd.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/pccrr.h delete mode 100644 qemu/roms/ipxe/src/include/ipxe/pci_ids.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/peerblk.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/peerdisc.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/peermux.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/pool.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/rndis.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/sha512.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/stp.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/string.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/uart.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/usb.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/usbhid.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/usbnet.h create mode 100644 qemu/roms/ipxe/src/include/ipxe/vmbus.h delete mode 100644 qemu/roms/ipxe/src/include/little_bswap.h rename qemu/roms/ipxe/src/{arch/x86 => }/include/valgrind/memcheck.h (100%) rename qemu/roms/ipxe/src/{arch/x86 => }/include/valgrind/valgrind.h (100%) create mode 100644 qemu/roms/ipxe/src/interface/efi/efi_time.c create mode 100644 qemu/roms/ipxe/src/interface/efi/efi_watchdog.c create mode 100644 qemu/roms/ipxe/src/interface/hyperv/vmbus.c create mode 100644 qemu/roms/ipxe/src/net/pccrc.c create mode 100644 qemu/roms/ipxe/src/net/pccrd.c create mode 100644 qemu/roms/ipxe/src/net/peerblk.c create mode 100644 qemu/roms/ipxe/src/net/peerdisc.c create mode 100644 qemu/roms/ipxe/src/net/peerdist.c create mode 100644 qemu/roms/ipxe/src/net/peermux.c create mode 100644 qemu/roms/ipxe/src/net/rndis.c create mode 100644 qemu/roms/ipxe/src/net/stp.c create mode 100644 qemu/roms/ipxe/src/net/tcp/httpauth.c create mode 100644 qemu/roms/ipxe/src/net/tcp/httpbasic.c create mode 100644 qemu/roms/ipxe/src/net/tcp/httpblock.c create mode 100644 qemu/roms/ipxe/src/net/tcp/httpconn.c create mode 100644 qemu/roms/ipxe/src/net/tcp/httpdigest.c delete mode 100644 qemu/roms/ipxe/src/tests/aes_cbc_test.c create mode 100644 qemu/roms/ipxe/src/tests/aes_test.c delete mode 100644 qemu/roms/ipxe/src/tests/cbc_test.c delete mode 100644 qemu/roms/ipxe/src/tests/cbc_test.h create mode 100644 qemu/roms/ipxe/src/tests/cipher_test.c create mode 100644 qemu/roms/ipxe/src/tests/cipher_test.h create mode 100644 qemu/roms/ipxe/src/tests/ipv4_test.c create mode 100644 qemu/roms/ipxe/src/tests/memset_test.c create mode 100644 qemu/roms/ipxe/src/tests/pccrc_test.c create mode 100644 qemu/roms/ipxe/src/tests/setjmp_test.c create mode 100644 qemu/roms/ipxe/src/tests/sha512_test.c create mode 100755 qemu/roms/ipxe/src/util/relicense.pl create mode 100644 qemu/roms/openbios/arch/sparc64/cpustate.h create mode 100644 qemu/roms/seabios/docs/Contributing.md create mode 100644 qemu/roms/seabios/docs/Runtime_config.md create mode 100644 qemu/roms/seabios/docs/SeaVGABIOS.md create mode 100755 qemu/roms/seabios/scripts/buildversion.py delete mode 100755 qemu/roms/seabios/scripts/buildversion.sh create mode 100644 qemu/roms/seabios/src/e820map.c create mode 100644 qemu/roms/seabios/src/e820map.h create mode 100644 qemu/roms/seabios/src/fw/multiboot.c create mode 100644 qemu/roms/seabios/src/hw/tpm_drivers.c create mode 100644 qemu/roms/seabios/src/hw/tpm_drivers.h delete mode 100644 qemu/roms/seabios/src/memmap.c create mode 100644 qemu/roms/seabios/src/sha1.c create mode 100644 qemu/roms/seabios/src/sha1.h create mode 100644 qemu/roms/seabios/src/std/multiboot.h create mode 100644 qemu/roms/seabios/src/tcgbios.c create mode 100644 qemu/roms/seabios/src/tcgbios.h create mode 100644 qemu/roms/seabios/src/version.c create mode 100644 qemu/roms/seabios/vgasrc/vgaversion.c delete mode 100755 qemu/scripts/acpi_extract.py delete mode 100755 qemu/scripts/acpi_extract_preprocess.py create mode 100755 qemu/scripts/clean-includes create mode 100644 qemu/scripts/cocci-macro-file.h create mode 100644 qemu/scripts/qapi-introspect.py create mode 100644 qemu/scripts/qemugdb/__init__.py create mode 100644 qemu/scripts/qemugdb/aio.py create mode 100644 qemu/scripts/qemugdb/coroutine.py create mode 100644 qemu/scripts/qemugdb/mtree.py create mode 100644 qemu/scripts/tracetool/backend/log.py delete mode 100644 qemu/scripts/tracetool/backend/stderr.py create mode 100644 qemu/scripts/tracetool/vcpu.py delete mode 100644 qemu/scripts/update-acpi.sh create mode 100644 qemu/slirp/ip6.h create mode 100644 qemu/slirp/ip6_icmp.c create mode 100644 qemu/slirp/ip6_icmp.h create mode 100644 qemu/slirp/ip6_input.c create mode 100644 qemu/slirp/ip6_output.c create mode 100644 qemu/slirp/ndp_table.c create mode 100644 qemu/slirp/udp6.c delete mode 100644 qemu/stubs/bdrv-commit-all.c create mode 100644 qemu/stubs/bdrv-next-monitor-owned.c create mode 100644 qemu/stubs/blk-commit-all.c create mode 100644 qemu/stubs/blockdev-close-all-bdrv-states.c delete mode 100644 qemu/stubs/chr-baum-init.c delete mode 100644 qemu/stubs/chr-msmouse.c delete mode 100644 qemu/stubs/chr-testdev.c create mode 100644 qemu/stubs/iohandler.c delete mode 100644 qemu/stubs/qemu-chr-open-spice.c create mode 100644 qemu/stubs/replay-user.c create mode 100644 qemu/stubs/replay.c create mode 100644 qemu/stubs/target-get-monitor-def.c create mode 100644 qemu/stubs/target-monitor-defs.c delete mode 100644 qemu/stubs/vc-init.c create mode 100644 qemu/stubs/vhost.c create mode 100644 qemu/target-arm/arch_dump.c create mode 100644 qemu/target-arm/monitor.c create mode 100644 qemu/target-i386/bpt_helper.c create mode 100644 qemu/target-i386/hyperv.c create mode 100644 qemu/target-i386/hyperv.h create mode 100644 qemu/target-i386/monitor.c create mode 100644 qemu/target-i386/mpx_helper.c delete mode 100644 qemu/target-ppc/kvm_ppc.c create mode 100644 qemu/target-ppc/monitor.c create mode 100644 qemu/target-sh4/monitor.c create mode 100644 qemu/target-sparc/monitor.c create mode 100644 qemu/target-tilegx/Makefile.objs create mode 100644 qemu/target-tilegx/cpu.c create mode 100644 qemu/target-tilegx/cpu.h create mode 100644 qemu/target-tilegx/helper.c create mode 100644 qemu/target-tilegx/helper.h create mode 100644 qemu/target-tilegx/opcode_tilegx.h create mode 100644 qemu/target-tilegx/simd_helper.c create mode 100644 qemu/target-tilegx/spr_def_64.h create mode 100644 qemu/target-tilegx/translate.c create mode 100644 qemu/target-tricore/fpu_helper.c create mode 100644 qemu/target-xtensa/monitor.c delete mode 100644 qemu/tcg/aarch64/tcg-target.c create mode 100644 qemu/tcg/aarch64/tcg-target.inc.c delete mode 100644 qemu/tcg/arm/tcg-target.c create mode 100644 qemu/tcg/arm/tcg-target.inc.c delete mode 100644 qemu/tcg/i386/tcg-target.c create mode 100644 qemu/tcg/i386/tcg-target.inc.c delete mode 100644 qemu/tcg/ia64/tcg-target.c create mode 100644 qemu/tcg/ia64/tcg-target.inc.c delete mode 100644 qemu/tcg/mips/tcg-target.c create mode 100644 qemu/tcg/mips/tcg-target.inc.c delete mode 100644 qemu/tcg/ppc/tcg-target.c create mode 100644 qemu/tcg/ppc/tcg-target.inc.c delete mode 100644 qemu/tcg/s390/tcg-target.c create mode 100644 qemu/tcg/s390/tcg-target.inc.c delete mode 100644 qemu/tcg/sparc/tcg-target.c create mode 100644 qemu/tcg/sparc/tcg-target.inc.c create mode 100644 qemu/tcg/tcg-common.c delete mode 100644 qemu/tcg/tci/tcg-target.c create mode 100644 qemu/tcg/tci/tcg-target.inc.c create mode 100644 qemu/tests/acpi-test-data/pc/DSDT.bridge delete mode 100644 qemu/tests/acpi-test-data/pc/SSDT delete mode 100644 qemu/tests/acpi-test-data/pc/SSDT.bridge create mode 100644 qemu/tests/acpi-test-data/q35/DSDT.bridge delete mode 100644 qemu/tests/acpi-test-data/q35/SSDT delete mode 100644 qemu/tests/acpi-test-data/q35/SSDT.bridge create mode 100644 qemu/tests/boot-sector.c create mode 100644 qemu/tests/boot-sector.h create mode 100644 qemu/tests/crypto-tls-x509-helpers.c create mode 100644 qemu/tests/crypto-tls-x509-helpers.h create mode 100644 qemu/tests/device-introspect-test.c create mode 100644 qemu/tests/ds1338-test.c create mode 100644 qemu/tests/guest-debug/test-gdbstub.py create mode 100644 qemu/tests/io-channel-helpers.c create mode 100644 qemu/tests/io-channel-helpers.h create mode 100644 qemu/tests/ipmi-bt-test.c create mode 100644 qemu/tests/ipmi-kcs-test.c create mode 100644 qemu/tests/ivshmem-test.c create mode 100644 qemu/tests/libqos/i2c-imx.c create mode 100644 qemu/tests/pkix_asn1_tab.c create mode 100644 qemu/tests/pxe-test.c create mode 100644 qemu/tests/qapi-schema/alternate-any.err rename qemu/tests/qapi-schema/{data-array-empty.exit => alternate-any.exit} (100%) create mode 100644 qemu/tests/qapi-schema/alternate-any.json rename qemu/tests/qapi-schema/{enum-empty.err => alternate-any.out} (100%) create mode 100644 qemu/tests/qapi-schema/alternate-empty.err rename qemu/tests/qapi-schema/{data-array-unknown.exit => alternate-empty.exit} (100%) create mode 100644 qemu/tests/qapi-schema/alternate-empty.json rename qemu/tests/qapi-schema/{enum-max-member.out => alternate-empty.out} (100%) delete mode 100644 qemu/tests/qapi-schema/alternate-good.exit delete mode 100644 qemu/tests/qapi-schema/alternate-good.json delete mode 100644 qemu/tests/qapi-schema/alternate-good.out create mode 100644 qemu/tests/qapi-schema/args-alternate.err rename qemu/tests/qapi-schema/{data-int.exit => args-alternate.exit} (100%) create mode 100644 qemu/tests/qapi-schema/args-alternate.json rename qemu/tests/qapi-schema/{enum-union-clash.out => args-alternate.out} (100%) create mode 100644 qemu/tests/qapi-schema/args-any.err rename qemu/tests/qapi-schema/{data-member-array-bad.exit => args-any.exit} (100%) create mode 100644 qemu/tests/qapi-schema/args-any.json rename qemu/tests/qapi-schema/{event-max.out => args-any.out} (100%) create mode 100644 qemu/tests/qapi-schema/args-array-empty.err rename qemu/tests/qapi-schema/{data-member-unknown.exit => args-array-empty.exit} (100%) rename qemu/tests/qapi-schema/{data-array-empty.json => args-array-empty.json} (100%) rename qemu/tests/qapi-schema/{flat-union-base-star.out => args-array-empty.out} (100%) create mode 100644 qemu/tests/qapi-schema/args-array-unknown.err rename qemu/tests/qapi-schema/{data-unknown.exit => args-array-unknown.exit} (100%) rename qemu/tests/qapi-schema/{data-array-unknown.json => args-array-unknown.json} (100%) rename qemu/tests/qapi-schema/{flat-union-branch-clash.out => args-array-unknown.out} (100%) create mode 100644 qemu/tests/qapi-schema/args-int.err rename qemu/tests/qapi-schema/{enum-max-member.exit => args-int.exit} (100%) rename qemu/tests/qapi-schema/{data-int.json => args-int.json} (100%) rename qemu/tests/qapi-schema/{flat-union-reverse-define.err => args-int.out} (100%) create mode 100644 qemu/tests/qapi-schema/args-invalid.err rename qemu/tests/qapi-schema/{enum-union-clash.exit => args-invalid.exit} (100%) create mode 100644 qemu/tests/qapi-schema/args-invalid.json rename qemu/tests/qapi-schema/{nested-struct-returns.out => args-invalid.out} (100%) create mode 100644 qemu/tests/qapi-schema/args-member-array-bad.err rename qemu/tests/qapi-schema/{event-max.exit => args-member-array-bad.exit} (100%) rename qemu/tests/qapi-schema/{data-member-array-bad.json => args-member-array-bad.json} (100%) rename qemu/tests/qapi-schema/{returns-int.err => args-member-array-bad.out} (100%) create mode 100644 qemu/tests/qapi-schema/args-member-case.err rename qemu/tests/qapi-schema/{flat-union-base-star.exit => args-member-case.exit} (100%) create mode 100644 qemu/tests/qapi-schema/args-member-case.json rename qemu/tests/qapi-schema/{type-bypass-no-gen.out => args-member-case.out} (100%) create mode 100644 qemu/tests/qapi-schema/args-member-unknown.err rename qemu/tests/qapi-schema/{flat-union-branch-clash.exit => args-member-unknown.exit} (100%) rename qemu/tests/qapi-schema/{data-member-unknown.json => args-member-unknown.json} (100%) rename qemu/tests/qapi-schema/{type-bypass.err => args-member-unknown.out} (100%) create mode 100644 qemu/tests/qapi-schema/args-name-clash.err rename qemu/tests/qapi-schema/{nested-struct-returns.exit => args-name-clash.exit} (100%) create mode 100644 qemu/tests/qapi-schema/args-name-clash.json rename qemu/tests/qapi-schema/{union-bad-branch.out => args-name-clash.out} (100%) create mode 100644 qemu/tests/qapi-schema/args-union.err rename qemu/tests/qapi-schema/{type-bypass-no-gen.exit => args-union.exit} (100%) create mode 100644 qemu/tests/qapi-schema/args-union.json rename qemu/tests/qapi-schema/{union-max.out => args-union.out} (100%) create mode 100644 qemu/tests/qapi-schema/args-unknown.err rename qemu/tests/qapi-schema/{union-bad-branch.exit => args-unknown.exit} (100%) rename qemu/tests/qapi-schema/{data-unknown.json => args-unknown.json} (100%) create mode 100644 qemu/tests/qapi-schema/args-unknown.out create mode 100644 qemu/tests/qapi-schema/base-cycle-direct.err rename qemu/tests/qapi-schema/{union-max.exit => base-cycle-direct.exit} (100%) create mode 100644 qemu/tests/qapi-schema/base-cycle-direct.json create mode 100644 qemu/tests/qapi-schema/base-cycle-direct.out create mode 100644 qemu/tests/qapi-schema/base-cycle-indirect.err create mode 100644 qemu/tests/qapi-schema/base-cycle-indirect.exit create mode 100644 qemu/tests/qapi-schema/base-cycle-indirect.json create mode 100644 qemu/tests/qapi-schema/base-cycle-indirect.out delete mode 100644 qemu/tests/qapi-schema/data-array-empty.err delete mode 100644 qemu/tests/qapi-schema/data-array-unknown.err delete mode 100644 qemu/tests/qapi-schema/data-int.err delete mode 100644 qemu/tests/qapi-schema/data-member-array-bad.err delete mode 100644 qemu/tests/qapi-schema/data-member-array.exit delete mode 100644 qemu/tests/qapi-schema/data-member-array.json delete mode 100644 qemu/tests/qapi-schema/data-member-array.out delete mode 100644 qemu/tests/qapi-schema/data-member-unknown.err delete mode 100644 qemu/tests/qapi-schema/data-unknown.err create mode 100644 qemu/tests/qapi-schema/enum-bad-prefix.err create mode 100644 qemu/tests/qapi-schema/enum-bad-prefix.exit create mode 100644 qemu/tests/qapi-schema/enum-bad-prefix.json create mode 100644 qemu/tests/qapi-schema/enum-bad-prefix.out delete mode 100644 qemu/tests/qapi-schema/enum-empty.exit delete mode 100644 qemu/tests/qapi-schema/enum-empty.json delete mode 100644 qemu/tests/qapi-schema/enum-empty.out delete mode 100644 qemu/tests/qapi-schema/enum-max-member.err delete mode 100644 qemu/tests/qapi-schema/enum-max-member.json create mode 100644 qemu/tests/qapi-schema/enum-member-case.err create mode 100644 qemu/tests/qapi-schema/enum-member-case.exit create mode 100644 qemu/tests/qapi-schema/enum-member-case.json create mode 100644 qemu/tests/qapi-schema/enum-member-case.out delete mode 100644 qemu/tests/qapi-schema/enum-union-clash.err delete mode 100644 qemu/tests/qapi-schema/enum-union-clash.json delete mode 100644 qemu/tests/qapi-schema/event-max.err delete mode 100644 qemu/tests/qapi-schema/event-max.json create mode 100644 qemu/tests/qapi-schema/flat-union-base-any.err create mode 100644 qemu/tests/qapi-schema/flat-union-base-any.exit create mode 100644 qemu/tests/qapi-schema/flat-union-base-any.json create mode 100644 qemu/tests/qapi-schema/flat-union-base-any.out delete mode 100644 qemu/tests/qapi-schema/flat-union-base-star.err delete mode 100644 qemu/tests/qapi-schema/flat-union-base-star.json delete mode 100644 qemu/tests/qapi-schema/flat-union-branch-clash.err delete mode 100644 qemu/tests/qapi-schema/flat-union-branch-clash.json create mode 100644 qemu/tests/qapi-schema/flat-union-clash-member.err create mode 100644 qemu/tests/qapi-schema/flat-union-clash-member.exit create mode 100644 qemu/tests/qapi-schema/flat-union-clash-member.json create mode 100644 qemu/tests/qapi-schema/flat-union-clash-member.out create mode 100644 qemu/tests/qapi-schema/flat-union-empty.err create mode 100644 qemu/tests/qapi-schema/flat-union-empty.exit create mode 100644 qemu/tests/qapi-schema/flat-union-empty.json create mode 100644 qemu/tests/qapi-schema/flat-union-empty.out delete mode 100644 qemu/tests/qapi-schema/flat-union-reverse-define.exit delete mode 100644 qemu/tests/qapi-schema/flat-union-reverse-define.json delete mode 100644 qemu/tests/qapi-schema/flat-union-reverse-define.out create mode 100644 qemu/tests/qapi-schema/leading-comma-list.err create mode 100644 qemu/tests/qapi-schema/leading-comma-list.exit create mode 100644 qemu/tests/qapi-schema/leading-comma-list.json create mode 100644 qemu/tests/qapi-schema/leading-comma-list.out create mode 100644 qemu/tests/qapi-schema/leading-comma-object.err create mode 100644 qemu/tests/qapi-schema/leading-comma-object.exit create mode 100644 qemu/tests/qapi-schema/leading-comma-object.json create mode 100644 qemu/tests/qapi-schema/leading-comma-object.out delete mode 100644 qemu/tests/qapi-schema/nested-struct-returns.err delete mode 100644 qemu/tests/qapi-schema/nested-struct-returns.json create mode 100644 qemu/tests/qapi-schema/reserved-command-q.err create mode 100644 qemu/tests/qapi-schema/reserved-command-q.exit create mode 100644 qemu/tests/qapi-schema/reserved-command-q.json create mode 100644 qemu/tests/qapi-schema/reserved-command-q.out create mode 100644 qemu/tests/qapi-schema/reserved-enum-q.err create mode 100644 qemu/tests/qapi-schema/reserved-enum-q.exit create mode 100644 qemu/tests/qapi-schema/reserved-enum-q.json create mode 100644 qemu/tests/qapi-schema/reserved-enum-q.out create mode 100644 qemu/tests/qapi-schema/reserved-member-has.err create mode 100644 qemu/tests/qapi-schema/reserved-member-has.exit create mode 100644 qemu/tests/qapi-schema/reserved-member-has.json create mode 100644 qemu/tests/qapi-schema/reserved-member-has.out create mode 100644 qemu/tests/qapi-schema/reserved-member-q.err create mode 100644 qemu/tests/qapi-schema/reserved-member-q.exit create mode 100644 qemu/tests/qapi-schema/reserved-member-q.json create mode 100644 qemu/tests/qapi-schema/reserved-member-q.out create mode 100644 qemu/tests/qapi-schema/reserved-member-u.err create mode 100644 qemu/tests/qapi-schema/reserved-member-u.exit create mode 100644 qemu/tests/qapi-schema/reserved-member-u.json create mode 100644 qemu/tests/qapi-schema/reserved-member-u.out create mode 100644 qemu/tests/qapi-schema/reserved-member-underscore.err create mode 100644 qemu/tests/qapi-schema/reserved-member-underscore.exit create mode 100644 qemu/tests/qapi-schema/reserved-member-underscore.json create mode 100644 qemu/tests/qapi-schema/reserved-member-underscore.out create mode 100644 qemu/tests/qapi-schema/reserved-type-kind.err create mode 100644 qemu/tests/qapi-schema/reserved-type-kind.exit create mode 100644 qemu/tests/qapi-schema/reserved-type-kind.json create mode 100644 qemu/tests/qapi-schema/reserved-type-kind.out create mode 100644 qemu/tests/qapi-schema/reserved-type-list.err create mode 100644 qemu/tests/qapi-schema/reserved-type-list.exit create mode 100644 qemu/tests/qapi-schema/reserved-type-list.json create mode 100644 qemu/tests/qapi-schema/reserved-type-list.out create mode 100644 qemu/tests/qapi-schema/returns-dict.err create mode 100644 qemu/tests/qapi-schema/returns-dict.exit create mode 100644 qemu/tests/qapi-schema/returns-dict.json create mode 100644 qemu/tests/qapi-schema/returns-dict.out delete mode 100644 qemu/tests/qapi-schema/returns-int.exit delete mode 100644 qemu/tests/qapi-schema/returns-int.json delete mode 100644 qemu/tests/qapi-schema/returns-int.out create mode 100644 qemu/tests/qapi-schema/struct-data-invalid.err create mode 100644 qemu/tests/qapi-schema/struct-data-invalid.exit create mode 100644 qemu/tests/qapi-schema/struct-data-invalid.json create mode 100644 qemu/tests/qapi-schema/struct-data-invalid.out create mode 100644 qemu/tests/qapi-schema/struct-member-invalid.err create mode 100644 qemu/tests/qapi-schema/struct-member-invalid.exit create mode 100644 qemu/tests/qapi-schema/struct-member-invalid.json create mode 100644 qemu/tests/qapi-schema/struct-member-invalid.out delete mode 100644 qemu/tests/qapi-schema/type-bypass-no-gen.err delete mode 100644 qemu/tests/qapi-schema/type-bypass-no-gen.json delete mode 100644 qemu/tests/qapi-schema/type-bypass.exit delete mode 100644 qemu/tests/qapi-schema/type-bypass.json delete mode 100644 qemu/tests/qapi-schema/type-bypass.out delete mode 100644 qemu/tests/qapi-schema/union-bad-branch.err delete mode 100644 qemu/tests/qapi-schema/union-bad-branch.json create mode 100644 qemu/tests/qapi-schema/union-branch-case.err create mode 100644 qemu/tests/qapi-schema/union-branch-case.exit create mode 100644 qemu/tests/qapi-schema/union-branch-case.json create mode 100644 qemu/tests/qapi-schema/union-branch-case.out create mode 100644 qemu/tests/qapi-schema/union-clash-branches.err create mode 100644 qemu/tests/qapi-schema/union-clash-branches.exit create mode 100644 qemu/tests/qapi-schema/union-clash-branches.json create mode 100644 qemu/tests/qapi-schema/union-clash-branches.out create mode 100644 qemu/tests/qapi-schema/union-empty.err create mode 100644 qemu/tests/qapi-schema/union-empty.exit create mode 100644 qemu/tests/qapi-schema/union-empty.json create mode 100644 qemu/tests/qapi-schema/union-empty.out delete mode 100644 qemu/tests/qapi-schema/union-max.err delete mode 100644 qemu/tests/qapi-schema/union-max.json create mode 100644 qemu/tests/qemu-iotests/051.pc.out create mode 100644 qemu/tests/qemu-iotests/096 create mode 100644 qemu/tests/qemu-iotests/096.out create mode 100755 qemu/tests/qemu-iotests/117 create mode 100644 qemu/tests/qemu-iotests/117.out create mode 100755 qemu/tests/qemu-iotests/118 create mode 100644 qemu/tests/qemu-iotests/118.out create mode 100755 qemu/tests/qemu-iotests/133 create mode 100644 qemu/tests/qemu-iotests/133.out create mode 100644 qemu/tests/qemu-iotests/136 create mode 100644 qemu/tests/qemu-iotests/136.out create mode 100755 qemu/tests/qemu-iotests/137 create mode 100644 qemu/tests/qemu-iotests/137.out create mode 100755 qemu/tests/qemu-iotests/138 create mode 100644 qemu/tests/qemu-iotests/138.out create mode 100644 qemu/tests/qemu-iotests/139 create mode 100644 qemu/tests/qemu-iotests/139.out create mode 100755 qemu/tests/qemu-iotests/140 create mode 100644 qemu/tests/qemu-iotests/140.out create mode 100755 qemu/tests/qemu-iotests/141 create mode 100644 qemu/tests/qemu-iotests/141.out create mode 100755 qemu/tests/qemu-iotests/142 create mode 100644 qemu/tests/qemu-iotests/142.out create mode 100755 qemu/tests/qemu-iotests/143 create mode 100644 qemu/tests/qemu-iotests/143.out create mode 100755 qemu/tests/qemu-iotests/144 create mode 100644 qemu/tests/qemu-iotests/144.out create mode 100755 qemu/tests/qemu-iotests/145 create mode 100644 qemu/tests/qemu-iotests/145.out create mode 100755 qemu/tests/qemu-iotests/146 create mode 100644 qemu/tests/qemu-iotests/146.out create mode 100644 qemu/tests/qemu-iotests/148 create mode 100644 qemu/tests/qemu-iotests/148.out create mode 100755 qemu/tests/qemu-iotests/149 create mode 100644 qemu/tests/qemu-iotests/149.out create mode 100755 qemu/tests/qemu-iotests/150 create mode 100644 qemu/tests/qemu-iotests/150.out create mode 100644 qemu/tests/qemu-iotests/152 create mode 100644 qemu/tests/qemu-iotests/152.out create mode 100644 qemu/tests/qemu-iotests/sample_images/d2v-zerofilled.vhd.bz2 create mode 100644 qemu/tests/qemu-iotests/sample_images/hyperv2012r2-dynamic.vhd.bz2 create mode 100644 qemu/tests/qemu-iotests/sample_images/virtualpc-dynamic.vhd.bz2 create mode 100644 qemu/tests/test-base64.c create mode 100644 qemu/tests/test-blockjob-txn.c create mode 100644 qemu/tests/test-crypto-afsplit.c create mode 100644 qemu/tests/test-crypto-block.c create mode 100644 qemu/tests/test-crypto-ivgen.c create mode 100644 qemu/tests/test-crypto-pbkdf.c create mode 100644 qemu/tests/test-crypto-secret.c create mode 100644 qemu/tests/test-crypto-tlscredsx509.c create mode 100644 qemu/tests/test-crypto-tlssession.c create mode 100644 qemu/tests/test-crypto-xts.c create mode 100644 qemu/tests/test-filter-mirror.c create mode 100644 qemu/tests/test-filter-redirector.c create mode 100644 qemu/tests/test-io-channel-buffer.c create mode 100644 qemu/tests/test-io-channel-command.c create mode 100644 qemu/tests/test-io-channel-file.c create mode 100644 qemu/tests/test-io-channel-socket.c create mode 100644 qemu/tests/test-io-channel-tls.c create mode 100644 qemu/tests/test-io-task.c create mode 100644 qemu/tests/test-logging.c create mode 100644 qemu/tests/test-netfilter.c create mode 100644 qemu/tests/test-qga.c create mode 100644 qemu/tests/test-timed-average.c create mode 100644 qemu/tests/vhost-user-bridge.c create mode 100644 qemu/translate-common.c create mode 100644 qemu/ui/egl-context.c create mode 100644 qemu/ui/gtk-gl-area.c create mode 100644 qemu/ui/input-linux.c delete mode 100644 qemu/ui/vnc-tls.c delete mode 100644 qemu/ui/vnc-tls.h create mode 100644 qemu/util/base64.c create mode 100644 qemu/util/buffer.c create mode 100644 qemu/util/coroutine-gthread.c create mode 100644 qemu/util/coroutine-sigaltstack.c create mode 100644 qemu/util/coroutine-ucontext.c create mode 100644 qemu/util/coroutine-win32.c create mode 100644 qemu/util/log.c create mode 100644 qemu/util/memfd.c create mode 100644 qemu/util/mmap-alloc.c create mode 100644 qemu/util/qemu-coroutine-io.c create mode 100644 qemu/util/qemu-coroutine-lock.c create mode 100644 qemu/util/qemu-coroutine-sleep.c create mode 100644 qemu/util/qemu-coroutine.c create mode 100644 qemu/util/timed-average.c diff --git a/qemu/CODING_STYLE b/qemu/CODING_STYLE index d46cfa5f6..3c6978f83 100644 --- a/qemu/CODING_STYLE +++ b/qemu/CODING_STYLE @@ -87,10 +87,15 @@ Furthermore, it is the QEMU coding style. 5. Declarations -Mixed declarations (interleaving statements and declarations within blocks) -are not allowed; declarations should be at the beginning of blocks. In other -words, the code should not generate warnings if using GCC's --Wdeclaration-after-statement option. +Mixed declarations (interleaving statements and declarations within +blocks) are generally not allowed; declarations should be at the beginning +of blocks. + +Every now and then, an exception is made for declarations inside a +#ifdef or #ifndef block: if the code looks nicer, such declarations can +be placed at the top of the block even if there are statements above. +On the other hand, however, it's often best to move that #ifdef/#ifndef +block to a separate function altogether. 6. Conditional statements diff --git a/qemu/HACKING b/qemu/HACKING index 12fbc8afe..058aa8fd4 100644 --- a/qemu/HACKING +++ b/qemu/HACKING @@ -157,3 +157,58 @@ painful. These are: * you may assume that integers are 2s complement representation * you may assume that right shift of a signed integer duplicates the sign bit (ie it is an arithmetic shift, not a logical shift) + +7. Error handling and reporting + +7.1 Reporting errors to the human user + +Do not use printf(), fprintf() or monitor_printf(). Instead, use +error_report() or error_vreport() from error-report.h. This ensures the +error is reported in the right place (current monitor or stderr), and in +a uniform format. + +Use error_printf() & friends to print additional information. + +error_report() prints the current location. In certain common cases +like command line parsing, the current location is tracked +automatically. To manipulate it manually, use the loc_*() from +error-report.h. + +7.2 Propagating errors + +An error can't always be reported to the user right where it's detected, +but often needs to be propagated up the call chain to a place that can +handle it. This can be done in various ways. + +The most flexible one is Error objects. See error.h for usage +information. + +Use the simplest suitable method to communicate success / failure to +callers. Stick to common methods: non-negative on success / -1 on +error, non-negative / -errno, non-null / null, or Error objects. + +Example: when a function returns a non-null pointer on success, and it +can fail only in one way (as far as the caller is concerned), returning +null on failure is just fine, and certainly simpler and a lot easier on +the eyes than propagating an Error object through an Error ** parameter. + +Example: when a function's callers need to report details on failure +only the function really knows, use Error **, and set suitable errors. + +Do not report an error to the user when you're also returning an error +for somebody else to handle. Leave the reporting to the place that +consumes the error returned. + +7.3 Handling errors + +Calling exit() is fine when handling configuration errors during +startup. It's problematic during normal operation. In particular, +monitor commands should never exit(). + +Do not call exit() or abort() to handle an error that can be triggered +by the guest (e.g., some unimplemented corner case in guest code +translation or device emulation). Guests should not be able to +terminate QEMU. + +Note that &error_fatal is just another way to exit(1), and &error_abort +is just another way to abort(). diff --git a/qemu/MAINTAINERS b/qemu/MAINTAINERS index 978b7174f..81e7fac2f 100644 --- a/qemu/MAINTAINERS +++ b/qemu/MAINTAINERS @@ -52,6 +52,11 @@ General Project Administration ------------------------------ M: Peter Maydell +All patches CC here +L: qemu-devel@nongnu.org +F: * +F: */ + Responsible Disclosure, Reporting Security Issues ------------------------------ W: http://wiki.qemu.org/SecurityProcess @@ -62,14 +67,29 @@ Guest CPU cores (TCG): ---------------------- Overall L: qemu-devel@nongnu.org -S: Odd fixes +M: Paolo Bonzini +M: Peter Crosthwaite +M: Richard Henderson +S: Maintained F: cpu-exec.c +F: cpu-exec-common.c +F: cpus.c F: cputlb.c +F: exec.c F: softmmu_template.h -F: translate-all.c -F: include/exec/cpu_ldst.h -F: include/exec/cpu_ldst_template.h +F: translate-all.* +F: translate-common.c +F: include/exec/cpu*.h +F: include/exec/exec-all.h F: include/exec/helper*.h +F: include/exec/tb-hash.h + +FPU emulation +M: Aurelien Jarno +M: Peter Maydell +S: Odd Fixes +F: fpu/ +F: include/fpu/ Alpha M: Richard Henderson @@ -77,13 +97,18 @@ S: Maintained F: target-alpha/ F: hw/alpha/ F: tests/tcg/alpha/ +F: disas/alpha.c ARM M: Peter Maydell +L: qemu-arm@nongnu.org S: Maintained F: target-arm/ F: hw/arm/ F: hw/cpu/a*mpcore.c +F: disas/arm.c +F: disas/arm-a64.cc +F: disas/libvixl/ CRIS M: Edgar E. Iglesias @@ -91,6 +116,7 @@ S: Maintained F: target-cris/ F: hw/cris/ F: tests/tcg/cris/ +F: disas/cris.c LM32 M: Michael Walle @@ -114,6 +140,7 @@ M: Edgar E. Iglesias S: Maintained F: target-microblaze/ F: hw/microblaze/ +F: disas/microblaze.c MIPS M: Aurelien Jarno @@ -122,11 +149,13 @@ S: Maintained F: target-mips/ F: hw/mips/ F: tests/tcg/mips/ +F: disas/mips.c Moxie M: Anthony Green S: Maintained F: target-moxie/ +F: disas/moxie.c OpenRISC M: Jia Liu @@ -141,6 +170,7 @@ L: qemu-ppc@nongnu.org S: Maintained F: target-ppc/ F: hw/ppc/ +F: disas/ppc.c S390 M: Richard Henderson @@ -148,12 +178,14 @@ M: Alexander Graf S: Maintained F: target-s390x/ F: hw/s390x/ +F: disas/s390.c SH4 M: Aurelien Jarno S: Odd Fixes F: target-sh4/ F: hw/sh4/ +F: disas/sh4.c SPARC M: Blue Swirl @@ -162,6 +194,7 @@ S: Maintained F: target-sparc/ F: hw/sparc/ F: hw/sparc64/ +F: disas/sparc.c UniCore32 M: Guan Xuetao @@ -176,6 +209,7 @@ M: Eduardo Habkost S: Maintained F: target-i386/ F: hw/i386/ +F: disas/i386.c Xtensa M: Max Filippov @@ -200,9 +234,11 @@ L: kvm@vger.kernel.org S: Supported F: kvm-* F: */kvm.* +F: include/sysemu/kvm*.h ARM M: Peter Maydell +L: qemu-arm@nongnu.org S: Maintained F: target-arm/kvm.c @@ -222,9 +258,14 @@ M: Cornelia Huck M: Alexander Graf S: Maintained F: target-s390x/kvm.c +F: target-s390x/ioinst.[ch] +F: target-s390x/machine.c F: hw/intc/s390_flic.c F: hw/intc/s390_flic_kvm.c F: include/hw/s390x/s390_flic.h +F: gdb-xml/s390*.xml +T: git git://github.com/cohuck/qemu.git s390-next +T: git git://github.com/borntraeger/qemu.git s390-next X86 M: Paolo Bonzini @@ -237,11 +278,21 @@ Guest CPU Cores (Xen): ---------------------- X86 -M: Stefano Stabellini +M: Stefano Stabellini +M: Anthony Perard L: xen-devel@lists.xensource.com S: Supported F: xen-* F: */xen* +F: hw/char/xen_console.c +F: hw/display/xenfb.c +F: hw/net/xen_nic.c +F: hw/block/xen_* +F: hw/xen/ +F: hw/xenpv/ +F: hw/i386/xen/ +F: include/hw/xen/ +F: include/sysemu/xen-mapcache.h Hosts: ------ @@ -262,103 +313,163 @@ L: qemu-devel@nongnu.org M: Stefan Weil S: Maintained F: *win32* +F: qemu.nsi ARM Machines ------------ Allwinner-a10 -M: Li Guang +M: Beniamino Galvani +L: qemu-arm@nongnu.org S: Maintained -F: hw/*/allwinner-a10* -F: include/hw/*/allwinner-a10* +F: hw/*/allwinner* +F: include/hw/*/allwinner* F: hw/arm/cubieboard.c +ARM PrimeCell +M: Peter Maydell +L: qemu-arm@nongnu.org +S: Maintained +F: hw/char/pl011.c +F: hw/display/pl110* +F: hw/dma/pl080.c +F: hw/dma/pl330.c +F: hw/gpio/pl061.c +F: hw/input/pl050.c +F: hw/intc/pl190.c +F: hw/sd/pl181.c +F: hw/timer/pl031.c +F: include/hw/arm/primecell.h + +ARM cores +M: Peter Maydell +L: qemu-arm@nongnu.org +S: Maintained +F: hw/intc/arm* +F: hw/intc/gic_internal.h +F: hw/misc/a9scu.c +F: hw/misc/arm11scu.c +F: hw/timer/a9gtimer* +F: hw/timer/arm_* +F: include/hw/arm/arm.h +F: include/hw/intc/arm* +F: include/hw/misc/a9scu.h +F: include/hw/misc/arm11scu.h +F: include/hw/timer/a9gtimer.h +F: include/hw/timer/arm_mptimer.h + Exynos -M: Evgeny Voevodin -M: Maksim Kozlov M: Igor Mitsyanko -M: Dmitry Solodkiy +L: qemu-arm@nongnu.org S: Maintained F: hw/*/exynos* +F: include/hw/arm/exynos4210.h Calxeda Highbank M: Rob Herring +L: qemu-arm@nongnu.org S: Maintained F: hw/arm/highbank.c F: hw/net/xgmac.c Canon DIGIC M: Antony Pavlov +L: qemu-arm@nongnu.org S: Maintained F: include/hw/arm/digic.h F: hw/*/digic* Gumstix L: qemu-devel@nongnu.org +L: qemu-arm@nongnu.org S: Orphan F: hw/arm/gumstix.c i.MX31 M: Peter Chubb +L: qemu-arm@nongnu.org S: Odd fixes F: hw/*/imx* F: hw/arm/kzm.c +F: include/hw/arm/fsl-imx31.h Integrator CP M: Peter Maydell +L: qemu-arm@nongnu.org S: Maintained F: hw/arm/integratorcp.c -Mainstone -L: qemu-devel@nongnu.org -S: Orphan -F: hw/arm/mainstone.c - Musicpal M: Jan Kiszka +L: qemu-arm@nongnu.org S: Maintained F: hw/arm/musicpal.c nSeries M: Andrzej Zaborowski +L: qemu-arm@nongnu.org S: Maintained F: hw/arm/nseries.c Palm M: Andrzej Zaborowski +L: qemu-arm@nongnu.org S: Maintained F: hw/arm/palm.c Real View M: Peter Maydell +L: qemu-arm@nongnu.org S: Maintained F: hw/arm/realview* +F: hw/intc/realview_gic.c +F: include/hw/intc/realview_gic.h -Spitz +PXA2XX M: Andrzej Zaborowski +L: qemu-arm@nongnu.org S: Maintained +F: hw/arm/mainstone.c F: hw/arm/spitz.c +F: hw/arm/tosa.c +F: hw/arm/z2.c +F: hw/*/pxa2xx* +F: include/hw/arm/pxa.h Stellaris M: Peter Maydell +L: qemu-arm@nongnu.org S: Maintained F: hw/*/stellaris* Versatile PB M: Peter Maydell +L: qemu-arm@nongnu.org S: Maintained F: hw/*/versatile* Xilinx Zynq -M: Peter Crosthwaite +M: Alistair Francis +M: Peter Crosthwaite +L: qemu-arm@nongnu.org S: Maintained F: hw/arm/xilinx_zynq.c F: hw/misc/zynq_slcr.c F: hw/*/cadence_* F: hw/ssi/xilinx_spips.c +Xilinx ZynqMP +M: Alistair Francis +M: Peter Crosthwaite +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/xlnx-zynqmp.c +F: hw/arm/xlnx-ep108.c +F: include/hw/arm/xlnx-zynqmp.h + ARM ACPI Subsystem M: Shannon Zhao M: Shannon Zhao +L: qemu-arm@nongnu.org S: Maintained F: hw/arm/virt-acpi-build.c F: include/hw/arm/virt-acpi-build.h @@ -405,7 +516,7 @@ S: Maintained F: hw/microblaze/petalogix_s3adsp1800_mmu.c petalogix_ml605 -M: Peter Crosthwaite +M: Edgar E. Iglesias S: Maintained F: hw/microblaze/petalogix_ml605_mmu.c @@ -493,7 +604,7 @@ F: hw/ppc/prep.c F: hw/pci-host/prep.[hc] F: hw/isa/pc87312.[hc] -sPAPR (pseries) +sPAPR M: David Gibson M: Alexander Graf L: qemu-ppc@nongnu.org @@ -544,25 +655,18 @@ F: hw/*/grlib* S390 Machines ------------- -S390 Virtio -M: Alexander Graf -S: Maintained -F: hw/s390x/s390-*.c -X: hw/s390x/*pci*.[hc] - S390 Virtio-ccw M: Cornelia Huck M: Christian Borntraeger M: Alexander Graf S: Supported -F: hw/s390x/s390-virtio-ccw.c -F: hw/s390x/css.[hc] -F: hw/s390x/sclp*.[hc] -F: hw/s390x/ipl*.[hc] -F: hw/s390x/*pci*.[hc] +F: hw/char/sclp*.[hc] +F: hw/s390x/ F: include/hw/s390x/ F: pc-bios/s390-ccw/ -T: git git://github.com/cohuck/qemu virtio-ccw-upstr +F: hw/watchdog/wdt_diag288.c +T: git git://github.com/cohuck/qemu.git s390-next +T: git git://github.com/borntraeger/qemu.git s390-next UniCore32 Machines ------------- @@ -591,7 +695,32 @@ F: hw/acpi/piix4.c F: hw/acpi/ich9.c F: include/hw/acpi/ich9.h F: include/hw/acpi/piix.h +F: hw/misc/sga.c +PC Chipset +M: Michael S. Tsirkin +M: Paolo Bonzini +S: Support +F: hw/char/debugcon.c +F: hw/char/parallel.c +F: hw/char/serial* +F: hw/dma/i8257* +F: hw/i2c/pm_smbus.c +F: hw/intc/apic* +F: hw/intc/ioapic* +F: hw/intc/i8259* +F: hw/misc/debugexit.c +F: hw/misc/pc-testdev.c +F: hw/timer/hpet* +F: hw/timer/i8254* +F: hw/timer/mc146818rtc* + +Machine core +M: Eduardo Habkost +M: Marcel Apfelbaum +S: Supported +F: hw/core/machine.c +F: include/hw/boards.h Xtensa Machines --------------- @@ -624,6 +753,7 @@ F: hw/block/cdrom.c F: hw/block/hd-geometry.c F: tests/ide-test.c F: tests/ahci-test.c +F: tests/libqos/ahci* T: git git://github.com/jnsnow/qemu.git ide Floppy @@ -632,26 +762,38 @@ L: qemu-block@nongnu.org S: Supported F: hw/block/fdc.c F: include/hw/block/fdc.h +F: tests/fdc-test.c T: git git://github.com/jnsnow/qemu.git ide OMAP M: Peter Maydell S: Maintained F: hw/*/omap* +F: include/hw/arm/omap.h + +IPack +M: Alberto Garcia +S: Odd Fixes +F: hw/char/ipoctal232.c +F: hw/ipack/ PCI M: Michael S. Tsirkin S: Supported F: include/hw/pci/* +F: hw/misc/pci-testdev.c F: hw/pci/* +F: hw/pci-bridge/* -ACPI +ACPI/SMBIOS M: Michael S. Tsirkin M: Igor Mammedov S: Supported F: include/hw/acpi/* +F: include/hw/smbios/* F: hw/mem/* F: hw/acpi/* +F: hw/smbios/* F: hw/i386/acpi-build.[hc] F: hw/i386/*dsl F: hw/arm/virt-acpi-build.c @@ -670,6 +812,19 @@ M: Scott Wood L: qemu-ppc@nongnu.org S: Supported F: hw/ppc/e500* +F: hw/pci-host/ppce500.c +F: hw/net/fsl_etsec/ + +Character devices +M: Paolo Bonzini +S: Odd Fixes +F: hw/char/ + +Network devices +M: Jason Wang +S: Odd Fixes +F: hw/net/ +T: git git://github.com/jasowang/qemu.git net SCSI M: Paolo Bonzini @@ -683,16 +838,27 @@ S: Orphan F: hw/scsi/lsi53c895a.c SSI -M: Peter Crosthwaite +M: Peter Crosthwaite S: Maintained F: hw/ssi/* F: hw/block/m25p80.c +X: hw/ssi/xilinx_* + +Xilinx SPI +M: Alistair Francis +M: Peter Crosthwaite +S: Maintained +F: hw/ssi/xilinx_* USB M: Gerd Hoffmann S: Maintained F: hw/usb/* F: tests/usb-*-test.c +F: docs/usb2.txt +F: docs/usb-storage.txt +F: include/hw/usb.h +F: include/hw/usb/ USB (serial adapter) M: Gerd Hoffmann @@ -704,6 +870,7 @@ VFIO M: Alex Williamson S: Supported F: hw/vfio/* +F: include/hw/vfio/ vhost M: Michael S. Tsirkin @@ -715,9 +882,11 @@ M: Michael S. Tsirkin S: Supported F: hw/*/virtio* F: net/vhost-user.c +F: include/hw/virtio/ virtio-9p M: Aneesh Kumar K.V +M: Greg Kurz S: Supported F: hw/9pfs/ F: fsdev/ @@ -738,7 +907,8 @@ M: Cornelia Huck M: Christian Borntraeger S: Supported F: hw/s390x/virtio-ccw.[hc] -T: git git://github.com/cohuck/qemu virtio-ccw-upstr +T: git git://github.com/cohuck/qemu.git s390-next +T: git git://github.com/borntraeger/qemu.git s390-next virtio-input M: Gerd Hoffmann @@ -758,6 +928,7 @@ M: Amit Shah S: Supported F: hw/virtio/virtio-rng.c F: include/hw/virtio/virtio-rng.h +F: include/sysemu/rng*.h F: backends/rng*.c nvme @@ -775,8 +946,9 @@ F: hw/scsi/megasas.c F: hw/scsi/mfi.h Xilinx EDK -M: Peter Crosthwaite M: Edgar E. Iglesias +M: Alistair Francis +M: Peter Crosthwaite S: Maintained F: hw/*/xilinx_* F: include/hw/xilinx.h @@ -793,6 +965,13 @@ M: Jiri Pirko S: Maintained F: hw/net/rocker/ +NVDIMM +M: Xiao Guangrong +S: Maintained +F: hw/acpi/nvdimm.c +F: hw/mem/nvdimm.c +F: include/hw/mem/nvdimm.h + Subsystems ---------- Audio @@ -806,6 +985,7 @@ F: tests/intel-hda-test.c Block layer core M: Kevin Wolf +M: Max Reitz L: qemu-block@nongnu.org S: Supported F: block* @@ -819,6 +999,7 @@ T: git git://repo.or.cz/qemu/kevin.git block Block I/O path M: Stefan Hajnoczi +M: Fam Zheng L: qemu-block@nongnu.org S: Supported F: async.c @@ -835,7 +1016,7 @@ F: blockjob.c F: include/block/blockjob.h F: block/backup.c F: block/commit.c -F: block/stream.h +F: block/stream.c F: block/mirror.c T: git git://github.com/codyprime/qemu-kvm-jtc.git block @@ -847,7 +1028,7 @@ F: block/qapi.c F: qapi/block*.json T: git git://repo.or.cz/qemu/armbru.git block-next -Character Devices +Character device backends M: Paolo Bonzini S: Maintained F: qemu-char.c @@ -869,7 +1050,6 @@ M: Andreas Färber S: Supported F: qom/cpu.c F: include/qom/cpu.h -F: target-i386/cpu.c ICC Bus M: Igor Mammedov @@ -878,10 +1058,19 @@ F: include/hw/cpu/icc_bus.h F: hw/cpu/icc_bus.c Device Tree -M: Peter Crosthwaite +M: Peter Crosthwaite M: Alexander Graf S: Maintained -F: device_tree.[ch] +F: device_tree.c +F: include/sysemu/device_tree.h + +Error reporting +M: Markus Armbruster +S: Supported +F: include/qapi/error.h +F: include/qemu/error-report.h +F: util/error.c +F: util/qemu-error.c GDB stub L: qemu-devel@nongnu.org @@ -895,6 +1084,7 @@ S: Supported F: include/exec/ioport.h F: ioport.c F: include/exec/memory.h +F: include/exec/ram_addr.h F: memory.c F: include/exec/memory-internal.h F: exec.c @@ -903,6 +1093,7 @@ SPICE M: Gerd Hoffmann S: Supported F: include/ui/qemu-spice.h +F: include/ui/spice-display.h F: ui/spice-*.c F: audio/spiceaudio.c F: hw/display/qxl* @@ -911,6 +1102,7 @@ Graphics M: Gerd Hoffmann S: Odd Fixes F: ui/ +F: include/ui/ Cocoa graphics M: Andreas Färber @@ -934,12 +1126,12 @@ F: hmp.c F: hmp-commands.hx T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp -Network device layer -M: Stefan Hajnoczi +Network device backends M: Jason Wang S: Maintained F: net/ -T: git git://github.com/stefanha/qemu.git net +F: include/net/ +T: git git://github.com/jasowang/qemu.git net Netmap network backend M: Luigi Rizzo @@ -952,8 +1144,9 @@ F: net/netmap.c Network Block Device (NBD) M: Paolo Bonzini S: Odd Fixes -F: block/nbd.c -F: nbd.* +F: block/nbd* +F: nbd/ +F: include/block/nbd* F: qemu-nbd.c T: git git://github.com/bonzini/qemu.git nbd-next @@ -962,8 +1155,6 @@ M: Eduardo Habkost S: Maintained F: numa.c F: include/sysemu/numa.h -K: numa|NUMA -K: srat|SRAT T: git git://github.com/ehabkost/qemu.git numa QAPI @@ -972,7 +1163,12 @@ M: Michael Roth S: Supported F: qapi/ X: qapi/*.json +F: include/qapi/ +X: include/qapi/qmp/ +F: include/qapi/qmp/dispatch.h F: tests/qapi-schema/ +F: tests/test-*-visitor.c +F: tests/test-qmp-*.c F: scripts/qapi* F: docs/qapi* T: git git://repo.or.cz/qemu/armbru.git qapi-next @@ -989,6 +1185,14 @@ QObject M: Luiz Capitulino S: Maintained F: qobject/ +F: include/qapi/qmp/ +X: include/qapi/qmp/dispatch.h +F: tests/check-qdict.c +F: tests/check-qfloat.c +F: tests/check-qint.c +F: tests/check-qjson.c +F: tests/check-qlist.c +F: tests/check-qstring.c T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp QEMU Guest Agent @@ -1005,6 +1209,8 @@ F: include/qom/ X: include/qom/cpu.h F: qom/ X: qom/cpu.c +F: tests/check-qom-interface.c +F: tests/check-qom-proplist.c F: tests/qom-test.c QMP @@ -1013,14 +1219,17 @@ S: Supported F: qmp.c F: monitor.c F: qmp-commands.hx -F: docs/qmp/ +F: docs/*qmp-* F: scripts/qmp/ T: git git://repo.or.cz/qemu/armbru.git qapi-next SLIRP +M: Samuel Thibault M: Jan Kiszka S: Maintained F: slirp/ +F: net/slirp.c +F: include/net/slirp.h T: git git://git.kiszka.org/qemu.git queues/slirp Tracing @@ -1045,6 +1254,7 @@ F: include/migration/ F: migration/ F: scripts/vmstate-static-checker.py F: tests/vmstate-static-checker-data/ +F: docs/migration.txt Seccomp M: Eduardo Otubo @@ -1059,6 +1269,43 @@ F: crypto/ F: include/crypto/ F: tests/test-crypto-* +Coroutines +M: Stefan Hajnoczi +M: Kevin Wolf +F: util/*coroutine* +F: include/qemu/coroutine* +F: tests/test-coroutine.c + +Buffers +M: Daniel P. Berrange +S: Odd fixes +F: util/buffer.c +F: include/qemu/buffer.h + +I/O Channels +M: Daniel P. Berrange +S: Maintained +F: io/ +F: include/io/ +F: tests/test-io-* + +Sockets +M: Daniel P. Berrange +M: Gerd Hoffmann +M: Paolo Bonzini +S: Maintained +F: include/qemu/sockets.h +F: util/qemu-sockets.c + +Throttling infrastructure +M: Alberto Garcia +S: Supported +F: block/throttle-groups.c +F: include/block/throttle-groups.h +F: include/qemu/throttle.h +F: util/throttle.c +L: qemu-block@nongnu.org + Usermode Emulation ------------------ Overall @@ -1088,54 +1335,61 @@ AArch64 target M: Claudio Fontana M: Claudio Fontana S: Maintained +L: qemu-arm@nongnu.org F: tcg/aarch64/ +F: disas/arm-a64.cc +F: disas/libvixl/ ARM target M: Andrzej Zaborowski S: Maintained +L: qemu-arm@nongnu.org F: tcg/arm/ +F: disas/arm.c i386 target L: qemu-devel@nongnu.org S: Maintained F: tcg/i386/ +F: disas/i386.c IA64 target M: Aurelien Jarno S: Maintained F: tcg/ia64/ +F: disas/ia64.c MIPS target M: Aurelien Jarno S: Maintained F: tcg/mips/ +F: disas/mips.c PPC M: Vassili Karpov (malc) S: Maintained F: tcg/ppc/ - -PPC64 target -M: Vassili Karpov (malc) -S: Maintained -F: tcg/ppc64/ +F: disas/ppc.c S390 target M: Alexander Graf M: Richard Henderson S: Maintained F: tcg/s390/ +F: disas/s390.c SPARC target M: Blue Swirl S: Maintained F: tcg/sparc/ +F: disas/sparc.c TCI target M: Stefan Weil S: Maintained F: tcg/tci/ F: tci.c +F: disas/tci.c Stable branches --------------- @@ -1288,9 +1542,11 @@ F: block/dmg.c parallels M: Stefan Hajnoczi +M: Denis V. Lunev L: qemu-block@nongnu.org S: Supported F: block/parallels.c +F: docs/specs/parallels.txt qed M: Stefan Hajnoczi @@ -1311,6 +1567,7 @@ F: block/win32-aio.c qcow2 M: Kevin Wolf +M: Max Reitz L: qemu-block@nongnu.org S: Supported F: block/qcow2* @@ -1323,6 +1580,7 @@ F: block/qcow.c blkdebug M: Kevin Wolf +M: Max Reitz L: qemu-block@nongnu.org S: Supported F: block/blkdebug.c @@ -1344,3 +1602,17 @@ M: Stefan Hajnoczi L: qemu-block@nongnu.org S: Supported F: tests/image-fuzzer/ + +Build and test automation +------------------------- +M: Alex Bennée +L: qemu-devel@nongnu.org +S: Supported +F: .travis.yml + +Documentation +------------- +Build system architecture +M: Daniel P. Berrange +S: Odd Fixes +F: docs/build-system.txt diff --git a/qemu/Makefile b/qemu/Makefile index 340d9c8fa..1d076a9d8 100644 --- a/qemu/Makefile +++ b/qemu/Makefile @@ -52,6 +52,8 @@ endif GENERATED_HEADERS = config-host.h qemu-options.def GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h qapi-event.h GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c qapi-event.c +GENERATED_HEADERS += qmp-introspect.h +GENERATED_SOURCES += qmp-introspect.c GENERATED_HEADERS += trace/generated-events.h GENERATED_SOURCES += trace/generated-events.c @@ -88,7 +90,8 @@ LIBS+=-lz $(LIBS_TOOLS) HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF) ifdef BUILD_DOCS -DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 qmp-commands.txt +DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8 +DOCS+=qmp-commands.txt ifdef CONFIG_LINUX DOCS+=kvm_stat.1 endif @@ -148,18 +151,21 @@ dummy := $(call unnest-vars,, \ stub-obj-y \ util-obj-y \ qga-obj-y \ + ivshmem-client-obj-y \ + ivshmem-server-obj-y \ qga-vss-dll-obj-y \ block-obj-y \ block-obj-m \ + crypto-obj-y \ + crypto-aes-obj-y \ + qom-obj-y \ + io-obj-y \ common-obj-y \ common-obj-m) ifneq ($(wildcard config-host.mak),) include $(SRC_PATH)/tests/Makefile endif -ifeq ($(CONFIG_SMARTCARD_NSS),y) -include $(SRC_PATH)/libcacard/Makefile -endif all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules @@ -172,6 +178,8 @@ SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS)) SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES)) $(SOFTMMU_SUBDIR_RULES): $(block-obj-y) +$(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y) +$(SOFTMMU_SUBDIR_RULES): $(io-obj-y) $(SOFTMMU_SUBDIR_RULES): config-all-devices.mak subdir-%: @@ -196,7 +204,7 @@ subdir-dtc:dtc/libfdt dtc/tests dtc/%: mkdir -p $@ -$(SUBDIR_RULES): libqemuutil.a libqemustub.a $(common-obj-y) +$(SUBDIR_RULES): libqemuutil.a libqemustub.a $(common-obj-y) $(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY)) ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS)) romsubdir-%: @@ -226,13 +234,13 @@ util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)' qemu-img.o: qemu-img-cmds.h -qemu-img$(EXESUF): qemu-img.o $(block-obj-y) libqemuutil.a libqemustub.a -qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) libqemuutil.a libqemustub.a -qemu-io$(EXESUF): qemu-io.o $(block-obj-y) libqemuutil.a libqemustub.a +qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a +qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a +qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a -qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o +qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o libqemuutil.a libqemustub.a -fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/virtio-9p-marshal.o libqemuutil.a libqemustub.a +fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o libqemuutil.a libqemustub.a fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx @@ -263,7 +271,9 @@ $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py) qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \ $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \ - $(SRC_PATH)/qapi/event.json + $(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json \ + $(SRC_PATH)/qapi/crypto.json $(SRC_PATH)/qapi/rocker.json \ + $(SRC_PATH)/qapi/trace.json qapi-types.c qapi-types.h :\ $(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py) @@ -285,6 +295,11 @@ $(qapi-modules) $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py) $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \ $(gen-out-type) -o "." -m $<, \ " GEN $@") +qmp-introspect.h qmp-introspect.c :\ +$(qapi-modules) $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py) + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-introspect.py \ + $(gen-out-type) -o "." $<, \ + " GEN $@") QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h) $(qga-obj-y) qemu-ga.o: $(QGALIB_GEN) @@ -295,24 +310,30 @@ qemu-ga$(EXESUF): $(qga-obj-y) libqemuutil.a libqemustub.a ifdef QEMU_GA_MSI_ENABLED QEMU_GA_MSI=qemu-ga-$(ARCH).msi -msi: ${QEMU_GA_MSI} +msi: $(QEMU_GA_MSI) -$(QEMU_GA_MSI): qemu-ga.exe - -ifdef QEMU_GA_MSI_WITH_VSS -$(QEMU_GA_MSI): qga/vss-win32/qga-vss.dll -endif +$(QEMU_GA_MSI): qemu-ga.exe $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI): config-host.mak -$(QEMU_GA_MSI): qga/installer/qemu-ga.wxs - $(call quiet-command,QEMU_GA_VERSION="$(QEMU_GA_VERSION)" QEMU_GA_MANUFACTURER="$(QEMU_GA_MANUFACTURER)" QEMU_GA_DISTRO="$(QEMU_GA_DISTRO)" \ +$(QEMU_GA_MSI): $(SRC_PATH)/qga/installer/qemu-ga.wxs + $(call quiet-command,QEMU_GA_VERSION="$(QEMU_GA_VERSION)" QEMU_GA_MANUFACTURER="$(QEMU_GA_MANUFACTURER)" QEMU_GA_DISTRO="$(QEMU_GA_DISTRO)" BUILD_DIR="$(BUILD_DIR)" \ wixl -o $@ $(QEMU_GA_MSI_ARCH) $(QEMU_GA_MSI_WITH_VSS) $(QEMU_GA_MSI_MINGW_DLL_PATH) $<, " WIXL $@") else msi: - @echo MSI build not configured or dependency resolution failed (reconfigure with --enable-guest-agent-msi option) + @echo "MSI build not configured or dependency resolution failed (reconfigure with --enable-guest-agent-msi option)" endif +ifneq ($(EXESUF),) +.PHONY: qemu-ga +qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI) +endif + +ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) libqemuutil.a libqemustub.a + $(call LINK, $^) +ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) libqemuutil.a libqemustub.a + $(call LINK, $^) + clean: # avoid old build problems by removing potentially incorrect old files rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h @@ -344,7 +365,7 @@ qemu-%.tar.bz2: $(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)" distclean: clean - rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi + rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi rm -f config-all-devices.mak config-all-disas.mak config.status rm -f po/*.mo tests/qemu-iotests/common.env rm -f roms/seabios/config.mak roms/vgabios/config.mak @@ -370,7 +391,7 @@ bepo cz ifdef INSTALL_BLOBS BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \ vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \ -acpi-dsdt.aml q35-acpi-dsdt.aml \ +acpi-dsdt.aml \ ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \ pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \ pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \ @@ -379,7 +400,6 @@ efi-pcnet.rom efi-rtl8139.rom efi-virtio.rom \ qemu-icon.bmp qemu_logo_no_text.svg \ bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ multiboot.bin linuxboot.bin kvmvapic.bin \ -s390-zipl.rom \ s390-ccw.img \ spapr-rtas.bin slof.bin \ palcode-clipper \ @@ -400,6 +420,9 @@ ifneq ($(TOOLS),) $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8" $(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8" endif +ifneq (,$(findstring qemu-ga,$(TOOLS))) + $(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8" +endif endif ifdef CONFIG_VIRTFS $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1" @@ -420,7 +443,7 @@ endif install: all $(if $(BUILD_DOCS),install-doc) \ install-datadir install-localstatedir ifneq ($(TOOLS),) - $(call install-prog,$(TOOLS),$(DESTDIR)$(bindir)) + $(call install-prog,$(subst qemu-ga,qemu-ga$(EXESUF),$(TOOLS)),$(DESTDIR)$(bindir)) endif ifneq ($(CONFIG_MODULES),) $(INSTALL_DIR) "$(DESTDIR)$(qemu_moddir)" @@ -508,13 +531,16 @@ qemu-options.texi: $(SRC_PATH)/qemu-options.hx qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@") +qemu-monitor-info.texi: $(SRC_PATH)/hmp-commands-info.hx + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@") + qmp-commands.txt: $(SRC_PATH)/qmp-commands.hx $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -q < $< > $@," GEN $@") qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@") -qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi +qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi qemu-monitor-info.texi $(call quiet-command, \ perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu.pod && \ $(POD2MAN) --section=1 --center=" " --release=" " qemu.pod > $@, \ @@ -538,6 +564,12 @@ qemu-nbd.8: qemu-nbd.texi $(POD2MAN) --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \ " GEN $@") +qemu-ga.8: qemu-ga.texi + $(call quiet-command, \ + perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-ga.pod && \ + $(POD2MAN) --section=8 --center=" " --release=" " qemu-ga.pod > $@, \ + " GEN $@") + kvm_stat.1: scripts/kvm/kvm_stat.texi $(call quiet-command, \ perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< kvm_stat.pod && \ @@ -551,7 +583,8 @@ pdf: qemu-doc.pdf qemu-tech.pdf qemu-doc.dvi qemu-doc.html qemu-doc.info qemu-doc.pdf: \ qemu-img.texi qemu-nbd.texi qemu-options.texi \ - qemu-monitor.texi qemu-img-cmds.texi + qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \ + qemu-monitor-info.texi ifdef CONFIG_WIN32 @@ -601,6 +634,7 @@ endif # SIGNCODE $(if $(DLL_PATH),-DDLLDIR="$(DLL_PATH)") \ -DSRCDIR="$(SRC_PATH)" \ -DOUTFILE="$(INSTALLER)" \ + -DDISPLAYVERSION="$(VERSION)" \ $(SRC_PATH)/qemu.nsi rm -r ${INSTDIR} ifdef SIGNCODE diff --git a/qemu/Makefile.objs b/qemu/Makefile.objs index f094eff15..8f705f620 100644 --- a/qemu/Makefile.objs +++ b/qemu/Makefile.objs @@ -1,38 +1,38 @@ ####################################################################### # Common libraries for tools and emulators -stub-obj-y = stubs/ -util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o -util-obj-y += crypto/ +stub-obj-y = stubs/ crypto/ +util-obj-y = util/ qobject/ qapi/ +util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o ####################################################################### # block-obj-y is code used by both qemu system emulation and qemu-img block-obj-y = async.o thread-pool.o -block-obj-y += nbd.o block.o blockjob.o +block-obj-y += nbd/ +block-obj-y += block.o blockjob.o block-obj-y += main-loop.o iohandler.o qemu-timer.o block-obj-$(CONFIG_POSIX) += aio-posix.o block-obj-$(CONFIG_WIN32) += aio-win32.o block-obj-y += block/ block-obj-y += qemu-io-cmds.o -block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o -block-obj-y += qemu-coroutine-sleep.o -block-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o - block-obj-m = block/ +####################################################################### +# crypto-obj-y is code used by both qemu system emulation and qemu-img -###################################################################### -# smartcard +crypto-obj-y = crypto/ +crypto-aes-obj-y = crypto/ + +####################################################################### +# qom-obj-y is code used by both qemu system emulation and qemu-img + +qom-obj-y = qom/ -libcacard-y += libcacard/cac.o libcacard/event.o -libcacard-y += libcacard/vcard.o libcacard/vreader.o -libcacard-y += libcacard/vcard_emul_nss.o -libcacard-y += libcacard/vcard_emul_type.o -libcacard-y += libcacard/card_7816.o -libcacard-y += libcacard/vcardt.o -libcacard/vcard_emul_nss.o-cflags := $(NSS_CFLAGS) -libcacard/vcard_emul_nss.o-libs := $(NSS_LIBS) +####################################################################### +# io-obj-y is code used by both qemu system emulation and qemu-img + +io-obj-y = io/ ###################################################################### # Target independent part of system emulation. The long term path is to @@ -60,6 +60,8 @@ common-obj-y += audio/ common-obj-y += hw/ common-obj-y += accel.o +common-obj-y += replay/ + common-obj-y += ui/ common-obj-y += bt-host.o bt-vhci.o bt-host.o-cflags := $(BLUEZ_CFLAGS) @@ -75,20 +77,18 @@ common-obj-y += backends/ common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o -common-obj-$(CONFIG_SMARTCARD_NSS) += $(libcacard-y) - common-obj-$(CONFIG_FDT) += device_tree.o ###################################################################### # qapi common-obj-y += qmp-marshal.o +common-obj-y += qmp-introspect.o common-obj-y += qmp.o hmp.o endif ####################################################################### # Target-independent parts used in system and user emulation -common-obj-y += qemu-log.o common-obj-y += tcg-runtime.o common-obj-y += hw/ common-obj-y += qom/ @@ -111,3 +111,8 @@ target-obj-y += trace/ # by libqemuutil.a. These should be moved to a separate .json schema. qga-obj-y = qga/ qga-vss-dll-obj-y = qga/ + +###################################################################### +# contrib +ivshmem-client-obj-y = contrib/ivshmem-client/ +ivshmem-server-obj-y = contrib/ivshmem-server/ diff --git a/qemu/Makefile.target b/qemu/Makefile.target index 3e7aafd72..34ddb7e76 100644 --- a/qemu/Makefile.target +++ b/qemu/Makefile.target @@ -7,7 +7,7 @@ include config-target.mak include config-devices.mak include $(SRC_PATH)/rules.mak -$(call set-vpath, $(SRC_PATH)) +$(call set-vpath, $(SRC_PATH):$(BUILD_DIR)) ifdef CONFIG_LINUX QEMU_CFLAGS += -I../linux-headers endif @@ -85,8 +85,11 @@ all: $(PROGS) stap ######################################################### # cpu emulator library obj-y = exec.o translate-all.o cpu-exec.o +obj-y += translate-common.o +obj-y += cpu-exec-common.o obj-y += tcg/tcg.o tcg/tcg-op.o tcg/optimize.o obj-$(CONFIG_TCG_INTERPRETER) += tci.o +obj-y += tcg/tcg-common.o obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o obj-y += fpu/softfloat.o obj-y += target-$(TARGET_BASE_ARCH)/ @@ -151,7 +154,7 @@ else obj-y += hw/$(TARGET_BASE_ARCH)/ endif -GENERATED_HEADERS += hmp-commands.h qmp-commands-old.h +GENERATED_HEADERS += hmp-commands.h hmp-commands-info.h qmp-commands-old.h endif # CONFIG_SOFTMMU @@ -170,12 +173,20 @@ target-obj-y-save := $(target-obj-y) dummy := $(call unnest-vars,.., \ block-obj-y \ block-obj-m \ + crypto-obj-y \ + crypto-aes-obj-y \ + qom-obj-y \ + io-obj-y \ common-obj-y \ common-obj-m) target-obj-y := $(target-obj-y-save) all-obj-y += $(common-obj-y) all-obj-y += $(target-obj-y) +all-obj-y += $(qom-obj-y) all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) +all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y) +all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y) +all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y) $(QEMU_PROG_BUILD): config-devices.mak @@ -193,6 +204,9 @@ gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/scripts/feature_to_c.sh hmp-commands.h: $(SRC_PATH)/hmp-commands.hx $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@") +hmp-commands-info.h: $(SRC_PATH)/hmp-commands-info.hx + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@") + qmp-commands-old.h: $(SRC_PATH)/qmp-commands.hx $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@") diff --git a/qemu/README b/qemu/README index c7c990d89..f38193fc6 100644 --- a/qemu/README +++ b/qemu/README @@ -1,3 +1,107 @@ -Read the documentation in qemu-doc.html or on http://wiki.qemu-project.org + QEMU README + =========== -- QEMU team +QEMU is a generic and open source machine & userspace emulator and +virtualizer. + +QEMU is capable of emulating a complete machine in software without any +need for hardware virtualization support. By using dynamic translation, +it achieves very good performance. QEMU can also integrate with the Xen +and KVM hypervisors to provide emulated hardware while allowing the +hypervisor to manage the CPU. With hypervisor support, QEMU can achieve +near native performance for CPUs. When QEMU emulates CPUs directly it is +capable of running operating systems made for one machine (e.g. an ARMv7 +board) on a different machine (e.g. an x86_64 PC board). + +QEMU is also capable of providing userspace API virtualization for Linux +and BSD kernel interfaces. This allows binaries compiled against one +architecture ABI (e.g. the Linux PPC64 ABI) to be run on a host using a +different architecture ABI (e.g. the Linux x86_64 ABI). This does not +involve any hardware emulation, simply CPU and syscall emulation. + +QEMU aims to fit into a variety of use cases. It can be invoked directly +by users wishing to have full control over its behaviour and settings. +It also aims to facilitate integration into higher level management +layers, by providing a stable command line interface and monitor API. +It is commonly invoked indirectly via the libvirt library when using +open source applications such as oVirt, OpenStack and virt-manager. + +QEMU as a whole is released under the GNU General Public License, +version 2. For full licensing details, consult the LICENSE file. + + +Building +======== + +QEMU is multi-platform software intended to be buildable on all modern +Linux platforms, OS-X, Win32 (via the Mingw64 toolchain) and a variety +of other UNIX targets. The simple steps to build QEMU are: + + mkdir build + cd build + ../configure + make + +Complete details of the process for building and configuring QEMU for +all supported host platforms can be found in the qemu-tech.html file. +Additional information can also be found online via the QEMU website: + + http://qemu-project.org/Hosts/Linux + http://qemu-project.org/Hosts/W32 + + +Submitting patches +================== + +The QEMU source code is maintained under the GIT version control system. + + git clone git://git.qemu-project.org/qemu.git + +When submitting patches, the preferred approach is to use 'git +format-patch' and/or 'git send-email' to format & send the mail to the +qemu-devel@nongnu.org mailing list. All patches submitted must contain +a 'Signed-off-by' line from the author. Patches should follow the +guidelines set out in the HACKING and CODING_STYLE files. + +Additional information on submitting patches can be found online via +the QEMU website + + http://qemu-project.org/Contribute/SubmitAPatch + http://qemu-project.org/Contribute/TrivialPatches + + +Bug reporting +============= + +The QEMU project uses Launchpad as its primary upstream bug tracker. Bugs +found when running code built from QEMU git or upstream released sources +should be reported via: + + https://bugs.launchpad.net/qemu/ + +If using QEMU via an operating system vendor pre-built binary package, it +is preferable to report bugs to the vendor's own bug tracker first. If +the bug is also known to affect latest upstream code, it can also be +reported via launchpad. + +For additional information on bug reporting consult: + + http://qemu-project.org/Contribute/ReportABug + + +Contact +======= + +The QEMU community can be contacted in a number of ways, with the two +main methods being email and IRC + + - qemu-devel@nongnu.org + http://lists.nongnu.org/mailman/listinfo/qemu-devel + - #qemu on irc.oftc.net + +Information on additional methods of contacting the community can be +found online via the QEMU website: + + http://qemu-project.org/Contribute/StartHere + +-- End diff --git a/qemu/VERSION b/qemu/VERSION index 81b7c5124..e70b4523a 100644 --- a/qemu/VERSION +++ b/qemu/VERSION @@ -1 +1 @@ -2.4.0.1 +2.6.0 diff --git a/qemu/accel.c b/qemu/accel.c index 74e41daaa..0510b90f6 100644 --- a/qemu/accel.c +++ b/qemu/accel.c @@ -23,6 +23,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "sysemu/accel.h" #include "hw/boards.h" #include "qemu-common.h" diff --git a/qemu/aio-posix.c b/qemu/aio-posix.c index d4770336c..6006122e0 100644 --- a/qemu/aio-posix.c +++ b/qemu/aio-posix.c @@ -13,10 +13,14 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "block/block.h" #include "qemu/queue.h" #include "qemu/sockets.h" +#ifdef CONFIG_EPOLL_CREATE1 +#include +#endif struct AioHandler { @@ -25,9 +29,166 @@ struct AioHandler IOHandler *io_write; int deleted; void *opaque; + bool is_external; QLIST_ENTRY(AioHandler) node; }; +#ifdef CONFIG_EPOLL_CREATE1 + +/* The fd number threashold to switch to epoll */ +#define EPOLL_ENABLE_THRESHOLD 64 + +static void aio_epoll_disable(AioContext *ctx) +{ + ctx->epoll_available = false; + if (!ctx->epoll_enabled) { + return; + } + ctx->epoll_enabled = false; + close(ctx->epollfd); +} + +static inline int epoll_events_from_pfd(int pfd_events) +{ + return (pfd_events & G_IO_IN ? EPOLLIN : 0) | + (pfd_events & G_IO_OUT ? EPOLLOUT : 0) | + (pfd_events & G_IO_HUP ? EPOLLHUP : 0) | + (pfd_events & G_IO_ERR ? EPOLLERR : 0); +} + +static bool aio_epoll_try_enable(AioContext *ctx) +{ + AioHandler *node; + struct epoll_event event; + + QLIST_FOREACH(node, &ctx->aio_handlers, node) { + int r; + if (node->deleted || !node->pfd.events) { + continue; + } + event.events = epoll_events_from_pfd(node->pfd.events); + event.data.ptr = node; + r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, node->pfd.fd, &event); + if (r) { + return false; + } + } + ctx->epoll_enabled = true; + return true; +} + +static void aio_epoll_update(AioContext *ctx, AioHandler *node, bool is_new) +{ + struct epoll_event event; + int r; + + if (!ctx->epoll_enabled) { + return; + } + if (!node->pfd.events) { + r = epoll_ctl(ctx->epollfd, EPOLL_CTL_DEL, node->pfd.fd, &event); + if (r) { + aio_epoll_disable(ctx); + } + } else { + event.data.ptr = node; + event.events = epoll_events_from_pfd(node->pfd.events); + if (is_new) { + r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, node->pfd.fd, &event); + if (r) { + aio_epoll_disable(ctx); + } + } else { + r = epoll_ctl(ctx->epollfd, EPOLL_CTL_MOD, node->pfd.fd, &event); + if (r) { + aio_epoll_disable(ctx); + } + } + } +} + +static int aio_epoll(AioContext *ctx, GPollFD *pfds, + unsigned npfd, int64_t timeout) +{ + AioHandler *node; + int i, ret = 0; + struct epoll_event events[128]; + + assert(npfd == 1); + assert(pfds[0].fd == ctx->epollfd); + if (timeout > 0) { + ret = qemu_poll_ns(pfds, npfd, timeout); + } + if (timeout <= 0 || ret > 0) { + ret = epoll_wait(ctx->epollfd, events, + sizeof(events) / sizeof(events[0]), + timeout); + if (ret <= 0) { + goto out; + } + for (i = 0; i < ret; i++) { + int ev = events[i].events; + node = events[i].data.ptr; + node->pfd.revents = (ev & EPOLLIN ? G_IO_IN : 0) | + (ev & EPOLLOUT ? G_IO_OUT : 0) | + (ev & EPOLLHUP ? G_IO_HUP : 0) | + (ev & EPOLLERR ? G_IO_ERR : 0); + } + } +out: + return ret; +} + +static bool aio_epoll_enabled(AioContext *ctx) +{ + /* Fall back to ppoll when external clients are disabled. */ + return !aio_external_disabled(ctx) && ctx->epoll_enabled; +} + +static bool aio_epoll_check_poll(AioContext *ctx, GPollFD *pfds, + unsigned npfd, int64_t timeout) +{ + if (!ctx->epoll_available) { + return false; + } + if (aio_epoll_enabled(ctx)) { + return true; + } + if (npfd >= EPOLL_ENABLE_THRESHOLD) { + if (aio_epoll_try_enable(ctx)) { + return true; + } else { + aio_epoll_disable(ctx); + } + } + return false; +} + +#else + +static void aio_epoll_update(AioContext *ctx, AioHandler *node, bool is_new) +{ +} + +static int aio_epoll(AioContext *ctx, GPollFD *pfds, + unsigned npfd, int64_t timeout) +{ + assert(false); +} + +static bool aio_epoll_enabled(AioContext *ctx) +{ + return false; +} + +static bool aio_epoll_check_poll(AioContext *ctx, GPollFD *pfds, + unsigned npfd, int64_t timeout) +{ + return false; +} + +#endif + static AioHandler *find_aio_handler(AioContext *ctx, int fd) { AioHandler *node; @@ -43,11 +204,14 @@ static AioHandler *find_aio_handler(AioContext *ctx, int fd) void aio_set_fd_handler(AioContext *ctx, int fd, + bool is_external, IOHandler *io_read, IOHandler *io_write, void *opaque) { AioHandler *node; + bool is_new = false; + bool deleted = false; node = find_aio_handler(ctx, fd); @@ -66,7 +230,7 @@ void aio_set_fd_handler(AioContext *ctx, * releasing the walking_handlers lock. */ QLIST_REMOVE(node, node); - g_free(node); + deleted = true; } } } else { @@ -77,25 +241,32 @@ void aio_set_fd_handler(AioContext *ctx, QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node); g_source_add_poll(&ctx->source, &node->pfd); + is_new = true; } /* Update handler with latest information */ node->io_read = io_read; node->io_write = io_write; node->opaque = opaque; + node->is_external = is_external; node->pfd.events = (io_read ? G_IO_IN | G_IO_HUP | G_IO_ERR : 0); node->pfd.events |= (io_write ? G_IO_OUT | G_IO_ERR : 0); } + aio_epoll_update(ctx, node, is_new); aio_notify(ctx); + if (deleted) { + g_free(node); + } } void aio_set_event_notifier(AioContext *ctx, EventNotifier *notifier, + bool is_external, EventNotifierHandler *io_read) { aio_set_fd_handler(ctx, event_notifier_get_fd(notifier), - (IOHandler *)io_read, NULL, notifier); + is_external, (IOHandler *)io_read, NULL, notifier); } bool aio_prepare(AioContext *ctx) @@ -111,10 +282,12 @@ bool aio_pending(AioContext *ctx) int revents; revents = node->pfd.revents & node->pfd.events; - if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR) && node->io_read) { + if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR) && node->io_read && + aio_node_check(ctx, node->is_external)) { return true; } - if (revents & (G_IO_OUT | G_IO_ERR) && node->io_write) { + if (revents & (G_IO_OUT | G_IO_ERR) && node->io_write && + aio_node_check(ctx, node->is_external)) { return true; } } @@ -152,6 +325,7 @@ bool aio_dispatch(AioContext *ctx) if (!node->deleted && (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) && + aio_node_check(ctx, node->is_external) && node->io_read) { node->io_read(node->opaque); @@ -162,6 +336,7 @@ bool aio_dispatch(AioContext *ctx) } if (!node->deleted && (revents & (G_IO_OUT | G_IO_ERR)) && + aio_node_check(ctx, node->is_external) && node->io_write) { node->io_write(node->opaque); progress = true; @@ -257,7 +432,9 @@ bool aio_poll(AioContext *ctx, bool blocking) /* fill pollfds */ QLIST_FOREACH(node, &ctx->aio_handlers, node) { - if (!node->deleted && node->pfd.events) { + if (!node->deleted && node->pfd.events + && !aio_epoll_enabled(ctx) + && aio_node_check(ctx, node->is_external)) { add_pollfd(node); } } @@ -268,7 +445,17 @@ bool aio_poll(AioContext *ctx, bool blocking) if (timeout) { aio_context_release(ctx); } - ret = qemu_poll_ns((GPollFD *)pollfds, npfd, timeout); + if (aio_epoll_check_poll(ctx, pollfds, npfd, timeout)) { + AioHandler epoll_handler; + + epoll_handler.pfd.fd = ctx->epollfd; + epoll_handler.pfd.events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR; + npfd = 0; + add_pollfd(&epoll_handler); + ret = aio_epoll(ctx, pollfds, npfd, timeout); + } else { + ret = qemu_poll_ns(pollfds, npfd, timeout); + } if (blocking) { atomic_sub(&ctx->notify_me, 2); } @@ -297,3 +484,16 @@ bool aio_poll(AioContext *ctx, bool blocking) return progress; } + +void aio_context_setup(AioContext *ctx, Error **errp) +{ +#ifdef CONFIG_EPOLL_CREATE1 + assert(!ctx->epollfd); + ctx->epollfd = epoll_create1(EPOLL_CLOEXEC); + if (ctx->epollfd == -1) { + ctx->epoll_available = false; + } else { + ctx->epoll_available = true; + } +#endif +} diff --git a/qemu/aio-win32.c b/qemu/aio-win32.c index 50a686745..6aaa32a14 100644 --- a/qemu/aio-win32.c +++ b/qemu/aio-win32.c @@ -15,6 +15,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "block/block.h" #include "qemu/queue.h" @@ -28,11 +29,13 @@ struct AioHandler { GPollFD pfd; int deleted; void *opaque; + bool is_external; QLIST_ENTRY(AioHandler) node; }; void aio_set_fd_handler(AioContext *ctx, int fd, + bool is_external, IOHandler *io_read, IOHandler *io_write, void *opaque) @@ -86,6 +89,7 @@ void aio_set_fd_handler(AioContext *ctx, node->opaque = opaque; node->io_read = io_read; node->io_write = io_write; + node->is_external = is_external; event = event_notifier_get_handle(&ctx->notifier); WSAEventSelect(node->pfd.fd, event, @@ -98,6 +102,7 @@ void aio_set_fd_handler(AioContext *ctx, void aio_set_event_notifier(AioContext *ctx, EventNotifier *e, + bool is_external, EventNotifierHandler *io_notify) { AioHandler *node; @@ -133,6 +138,7 @@ void aio_set_event_notifier(AioContext *ctx, node->e = e; node->pfd.fd = (uintptr_t)event_notifier_get_handle(e); node->pfd.events = G_IO_IN; + node->is_external = is_external; QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node); g_source_add_poll(&ctx->source, &node->pfd); @@ -304,7 +310,8 @@ bool aio_poll(AioContext *ctx, bool blocking) /* fill fd sets */ count = 0; QLIST_FOREACH(node, &ctx->aio_handlers, node) { - if (!node->deleted && node->io_notify) { + if (!node->deleted && node->io_notify + && aio_node_check(ctx, node->is_external)) { events[count++] = event_notifier_get_handle(node->e); } } @@ -363,3 +370,7 @@ bool aio_poll(AioContext *ctx, bool blocking) aio_context_release(ctx); return progress; } + +void aio_context_setup(AioContext *ctx, Error **errp) +{ +} diff --git a/qemu/arch_init.c b/qemu/arch_init.c index 725c638ec..e3bb1b3ac 100644 --- a/qemu/arch_init.c +++ b/qemu/arch_init.c @@ -21,16 +21,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include +#include "qemu/osdep.h" #include "sysemu/sysemu.h" #include "sysemu/arch_init.h" #include "hw/pci/pci.h" #include "hw/audio/audio.h" -#include "hw/i386/smbios.h" +#include "hw/smbios/smbios.h" #include "qemu/config-file.h" #include "qemu/error-report.h" #include "qmp-commands.h" #include "hw/acpi/acpi.h" +#include "qemu/help_option.h" #ifdef TARGET_SPARC int graphic_width = 1024; @@ -258,9 +259,7 @@ void do_acpitable_option(const QemuOpts *opts) acpi_table_add(opts, &err); if (err) { - error_report("Wrong acpi table provided: %s", - error_get_pretty(err)); - error_free(err); + error_reportf_err(err, "Wrong acpi table provided: "); exit(1); } #endif diff --git a/qemu/async.c b/qemu/async.c index efce14b63..b4bf205a0 100644 --- a/qemu/async.c +++ b/qemu/async.c @@ -22,6 +22,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "block/aio.h" #include "block/thread-pool.h" @@ -59,6 +61,11 @@ QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque) return bh; } +void aio_bh_call(QEMUBH *bh) +{ + bh->cb(bh->opaque); +} + /* Multiple occurrences of aio_bh_poll cannot be called concurrently */ int aio_bh_poll(AioContext *ctx) { @@ -84,7 +91,7 @@ int aio_bh_poll(AioContext *ctx) ret = 1; } bh->idle = 0; - bh->cb(bh->opaque); + aio_bh_call(bh); } } @@ -247,7 +254,7 @@ aio_ctx_finalize(GSource *source) } qemu_mutex_unlock(&ctx->bh_lock); - aio_set_event_notifier(ctx, &ctx->notifier, NULL); + aio_set_event_notifier(ctx, &ctx->notifier, false, NULL); event_notifier_cleanup(&ctx->notifier); rfifolock_destroy(&ctx->lock); qemu_mutex_destroy(&ctx->bh_lock); @@ -320,15 +327,22 @@ AioContext *aio_context_new(Error **errp) { int ret; AioContext *ctx; + Error *local_err = NULL; + ctx = (AioContext *) g_source_new(&aio_source_funcs, sizeof(AioContext)); + aio_context_setup(ctx, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto fail; + } ret = event_notifier_init(&ctx->notifier, false); if (ret < 0) { - g_source_destroy(&ctx->source); error_setg_errno(errp, -ret, "Failed to initialize event notifier"); - return NULL; + goto fail; } g_source_set_can_recurse(&ctx->source, true); aio_set_event_notifier(ctx, &ctx->notifier, + false, (EventNotifierHandler *) event_notifier_dummy_cb); ctx->thread_pool = NULL; @@ -339,6 +353,9 @@ AioContext *aio_context_new(Error **errp) ctx->notify_dummy_bh = aio_bh_new(ctx, notify_dummy_bh, NULL); return ctx; +fail: + g_source_destroy(&ctx->source); + return NULL; } void aio_context_ref(AioContext *ctx) diff --git a/qemu/audio/alsaaudio.c b/qemu/audio/alsaaudio.c index 6315b2d74..3652a7b5f 100644 --- a/qemu/audio/alsaaudio.c +++ b/qemu/audio/alsaaudio.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include "qemu-common.h" #include "qemu/main-loop.h" diff --git a/qemu/audio/audio.c b/qemu/audio/audio.c index 5be4b15fc..e60c124de 100644 --- a/qemu/audio/audio.c +++ b/qemu/audio/audio.c @@ -21,11 +21,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "audio.h" #include "monitor/monitor.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" +#include "qemu/cutils.h" #define AUDIO_CAP "audio" #include "audio_int.h" @@ -1806,9 +1808,6 @@ static void audio_init (void) atexit (audio_atexit); s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s); - if (!s->ts) { - hw_error("Could not create audio timer\n"); - } audio_process_options ("AUDIO", audio_options); @@ -1859,12 +1858,8 @@ static void audio_init (void) if (!done) { done = !audio_driver_init (s, &no_audio_driver); - if (!done) { - hw_error("Could not initialize audio subsystem\n"); - } - else { - dolog ("warning: Using timer based audio emulation\n"); - } + assert(done); + dolog("warning: Using timer based audio emulation\n"); } if (conf.period.hertz <= 0) { @@ -1875,8 +1870,7 @@ static void audio_init (void) } conf.period.ticks = 1; } else { - conf.period.ticks = - muldiv64 (1, get_ticks_per_sec (), conf.period.hertz); + conf.period.ticks = NANOSECONDS_PER_SECOND / conf.period.hertz; } e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s); diff --git a/qemu/audio/audio.h b/qemu/audio/audio.h index e7ea39777..b41a97053 100644 --- a/qemu/audio/audio.h +++ b/qemu/audio/audio.h @@ -24,7 +24,6 @@ #ifndef QEMU_AUDIO_H #define QEMU_AUDIO_H -#include "config-host.h" #include "qemu/queue.h" typedef void (*audio_callback_fn) (void *opaque, int avail); diff --git a/qemu/audio/audio_pt_int.c b/qemu/audio/audio_pt_int.c index 9a9c306a9..21ff9c580 100644 --- a/qemu/audio/audio_pt_int.c +++ b/qemu/audio/audio_pt_int.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "audio.h" diff --git a/qemu/audio/audio_win_int.c b/qemu/audio/audio_win_int.c index e1324056a..6900008d0 100644 --- a/qemu/audio/audio_win_int.c +++ b/qemu/audio/audio_win_int.c @@ -1,5 +1,6 @@ /* public domain */ +#include "qemu/osdep.h" #include "qemu-common.h" #define AUDIO_CAP "win-int" diff --git a/qemu/audio/coreaudio.c b/qemu/audio/coreaudio.c index 6dfd63eb4..d4ad22459 100644 --- a/qemu/audio/coreaudio.c +++ b/qemu/audio/coreaudio.c @@ -22,8 +22,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include -#include /* strerror */ #include /* pthread_X */ #include "qemu-common.h" @@ -32,6 +32,10 @@ #define AUDIO_CAP "coreaudio" #include "audio_int.h" +#ifndef MAC_OS_X_VERSION_10_6 +#define MAC_OS_X_VERSION_10_6 1060 +#endif + static int isAtexit; typedef struct { @@ -45,11 +49,233 @@ typedef struct coreaudioVoiceOut { AudioDeviceID outputDeviceID; UInt32 audioDevicePropertyBufferFrameSize; AudioStreamBasicDescription outputStreamBasicDescription; + AudioDeviceIOProcID ioprocid; int live; int decr; int rpos; } coreaudioVoiceOut; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 +/* The APIs used here only become available from 10.6 */ + +static OSStatus coreaudio_get_voice(AudioDeviceID *id) +{ + UInt32 size = sizeof(*id); + AudioObjectPropertyAddress addr = { + kAudioHardwarePropertyDefaultOutputDevice, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + + return AudioObjectGetPropertyData(kAudioObjectSystemObject, + &addr, + 0, + NULL, + &size, + id); +} + +static OSStatus coreaudio_get_framesizerange(AudioDeviceID id, + AudioValueRange *framerange) +{ + UInt32 size = sizeof(*framerange); + AudioObjectPropertyAddress addr = { + kAudioDevicePropertyBufferFrameSizeRange, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + return AudioObjectGetPropertyData(id, + &addr, + 0, + NULL, + &size, + framerange); +} + +static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize) +{ + UInt32 size = sizeof(*framesize); + AudioObjectPropertyAddress addr = { + kAudioDevicePropertyBufferFrameSize, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + return AudioObjectGetPropertyData(id, + &addr, + 0, + NULL, + &size, + framesize); +} + +static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize) +{ + UInt32 size = sizeof(*framesize); + AudioObjectPropertyAddress addr = { + kAudioDevicePropertyBufferFrameSize, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + return AudioObjectSetPropertyData(id, + &addr, + 0, + NULL, + size, + framesize); +} + +static OSStatus coreaudio_get_streamformat(AudioDeviceID id, + AudioStreamBasicDescription *d) +{ + UInt32 size = sizeof(*d); + AudioObjectPropertyAddress addr = { + kAudioDevicePropertyStreamFormat, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + return AudioObjectGetPropertyData(id, + &addr, + 0, + NULL, + &size, + d); +} + +static OSStatus coreaudio_set_streamformat(AudioDeviceID id, + AudioStreamBasicDescription *d) +{ + UInt32 size = sizeof(*d); + AudioObjectPropertyAddress addr = { + kAudioDevicePropertyStreamFormat, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + return AudioObjectSetPropertyData(id, + &addr, + 0, + NULL, + size, + d); +} + +static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) +{ + UInt32 size = sizeof(*result); + AudioObjectPropertyAddress addr = { + kAudioDevicePropertyDeviceIsRunning, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + return AudioObjectGetPropertyData(id, + &addr, + 0, + NULL, + &size, + result); +} +#else +/* Legacy versions of functions using deprecated APIs */ + +static OSStatus coreaudio_get_voice(AudioDeviceID *id) +{ + UInt32 size = sizeof(*id); + + return AudioHardwareGetProperty( + kAudioHardwarePropertyDefaultOutputDevice, + &size, + id); +} + +static OSStatus coreaudio_get_framesizerange(AudioDeviceID id, + AudioValueRange *framerange) +{ + UInt32 size = sizeof(*framerange); + + return AudioDeviceGetProperty( + id, + 0, + 0, + kAudioDevicePropertyBufferFrameSizeRange, + &size, + framerange); +} + +static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize) +{ + UInt32 size = sizeof(*framesize); + + return AudioDeviceGetProperty( + id, + 0, + false, + kAudioDevicePropertyBufferFrameSize, + &size, + framesize); +} + +static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize) +{ + UInt32 size = sizeof(*framesize); + + return AudioDeviceSetProperty( + id, + NULL, + 0, + false, + kAudioDevicePropertyBufferFrameSize, + size, + framesize); +} + +static OSStatus coreaudio_get_streamformat(AudioDeviceID id, + AudioStreamBasicDescription *d) +{ + UInt32 size = sizeof(*d); + + return AudioDeviceGetProperty( + id, + 0, + false, + kAudioDevicePropertyStreamFormat, + &size, + d); +} + +static OSStatus coreaudio_set_streamformat(AudioDeviceID id, + AudioStreamBasicDescription *d) +{ + UInt32 size = sizeof(*d); + + return AudioDeviceSetProperty( + id, + 0, + 0, + 0, + kAudioDevicePropertyStreamFormat, + size, + d); +} + +static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) +{ + UInt32 size = sizeof(*result); + + return AudioDeviceGetProperty( + id, + 0, + 0, + kAudioDevicePropertyDeviceIsRunning, + &size, + result); +} +#endif + static void coreaudio_logstatus (OSStatus status) { const char *str = "BUG"; @@ -144,10 +370,7 @@ static inline UInt32 isPlaying (AudioDeviceID outputDeviceID) { OSStatus status; UInt32 result = 0; - UInt32 propertySize = sizeof(outputDeviceID); - status = AudioDeviceGetProperty( - outputDeviceID, 0, 0, - kAudioDevicePropertyDeviceIsRunning, &propertySize, &result); + status = coreaudio_get_isrunning(outputDeviceID, &result); if (status != kAudioHardwareNoError) { coreaudio_logerr(status, "Could not determine whether Device is playing\n"); @@ -288,7 +511,6 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, { OSStatus status; coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; - UInt32 propertySize; int err; const char *typ = "playback"; AudioValueRange frameRange; @@ -303,12 +525,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, audio_pcm_init_info (&hw->info, as); - /* open default output device */ - propertySize = sizeof(core->outputDeviceID); - status = AudioHardwareGetProperty( - kAudioHardwarePropertyDefaultOutputDevice, - &propertySize, - &core->outputDeviceID); + status = coreaudio_get_voice(&core->outputDeviceID); if (status != kAudioHardwareNoError) { coreaudio_logerr2 (status, typ, "Could not get default output Device\n"); @@ -320,14 +537,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, } /* get minimum and maximum buffer frame sizes */ - propertySize = sizeof(frameRange); - status = AudioDeviceGetProperty( - core->outputDeviceID, - 0, - 0, - kAudioDevicePropertyBufferFrameSizeRange, - &propertySize, - &frameRange); + status = coreaudio_get_framesizerange(core->outputDeviceID, + &frameRange); if (status != kAudioHardwareNoError) { coreaudio_logerr2 (status, typ, "Could not get device buffer frame range\n"); @@ -347,15 +558,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, } /* set Buffer Frame Size */ - propertySize = sizeof(core->audioDevicePropertyBufferFrameSize); - status = AudioDeviceSetProperty( - core->outputDeviceID, - NULL, - 0, - false, - kAudioDevicePropertyBufferFrameSize, - propertySize, - &core->audioDevicePropertyBufferFrameSize); + status = coreaudio_set_framesize(core->outputDeviceID, + &core->audioDevicePropertyBufferFrameSize); if (status != kAudioHardwareNoError) { coreaudio_logerr2 (status, typ, "Could not set device buffer frame size %" PRIu32 "\n", @@ -364,14 +568,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, } /* get Buffer Frame Size */ - propertySize = sizeof(core->audioDevicePropertyBufferFrameSize); - status = AudioDeviceGetProperty( - core->outputDeviceID, - 0, - false, - kAudioDevicePropertyBufferFrameSize, - &propertySize, - &core->audioDevicePropertyBufferFrameSize); + status = coreaudio_get_framesize(core->outputDeviceID, + &core->audioDevicePropertyBufferFrameSize); if (status != kAudioHardwareNoError) { coreaudio_logerr2 (status, typ, "Could not get device buffer frame size\n"); @@ -380,14 +578,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize; /* get StreamFormat */ - propertySize = sizeof(core->outputStreamBasicDescription); - status = AudioDeviceGetProperty( - core->outputDeviceID, - 0, - false, - kAudioDevicePropertyStreamFormat, - &propertySize, - &core->outputStreamBasicDescription); + status = coreaudio_get_streamformat(core->outputDeviceID, + &core->outputStreamBasicDescription); if (status != kAudioHardwareNoError) { coreaudio_logerr2 (status, typ, "Could not get Device Stream properties\n"); @@ -397,15 +589,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, /* set Samplerate */ core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; - propertySize = sizeof(core->outputStreamBasicDescription); - status = AudioDeviceSetProperty( - core->outputDeviceID, - 0, - 0, - 0, - kAudioDevicePropertyStreamFormat, - propertySize, - &core->outputStreamBasicDescription); + status = coreaudio_set_streamformat(core->outputDeviceID, + &core->outputStreamBasicDescription); if (status != kAudioHardwareNoError) { coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n", as->freq); @@ -414,8 +599,12 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, } /* set Callback */ - status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw); - if (status != kAudioHardwareNoError) { + core->ioprocid = NULL; + status = AudioDeviceCreateIOProcID(core->outputDeviceID, + audioDeviceIOProc, + hw, + &core->ioprocid); + if (status != kAudioHardwareNoError || core->ioprocid == NULL) { coreaudio_logerr2 (status, typ, "Could not set IOProc\n"); core->outputDeviceID = kAudioDeviceUnknown; return -1; @@ -423,10 +612,10 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, /* start Playback */ if (!isPlaying(core->outputDeviceID)) { - status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc); + status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); if (status != kAudioHardwareNoError) { coreaudio_logerr2 (status, typ, "Could not start playback\n"); - AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc); + AudioDeviceDestroyIOProcID(core->outputDeviceID, core->ioprocid); core->outputDeviceID = kAudioDeviceUnknown; return -1; } @@ -444,15 +633,15 @@ static void coreaudio_fini_out (HWVoiceOut *hw) if (!isAtexit) { /* stop playback */ if (isPlaying(core->outputDeviceID)) { - status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); + status = AudioDeviceStop(core->outputDeviceID, core->ioprocid); if (status != kAudioHardwareNoError) { coreaudio_logerr (status, "Could not stop playback\n"); } } /* remove callback */ - status = AudioDeviceRemoveIOProc(core->outputDeviceID, - audioDeviceIOProc); + status = AudioDeviceDestroyIOProcID(core->outputDeviceID, + core->ioprocid); if (status != kAudioHardwareNoError) { coreaudio_logerr (status, "Could not remove IOProc\n"); } @@ -475,7 +664,7 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) case VOICE_ENABLE: /* start playback */ if (!isPlaying(core->outputDeviceID)) { - status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc); + status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); if (status != kAudioHardwareNoError) { coreaudio_logerr (status, "Could not resume playback\n"); } @@ -486,7 +675,8 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) /* stop playback */ if (!isAtexit) { if (isPlaying(core->outputDeviceID)) { - status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); + status = AudioDeviceStop(core->outputDeviceID, + core->ioprocid); if (status != kAudioHardwareNoError) { coreaudio_logerr (status, "Could not pause playback\n"); } diff --git a/qemu/audio/dsoundaudio.c b/qemu/audio/dsoundaudio.c index e9472c105..516846eb8 100644 --- a/qemu/audio/dsoundaudio.c +++ b/qemu/audio/dsoundaudio.c @@ -26,6 +26,7 @@ * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "audio.h" diff --git a/qemu/audio/mixeng.c b/qemu/audio/mixeng.c index 0e4976f27..981b97a96 100644 --- a/qemu/audio/mixeng.c +++ b/qemu/audio/mixeng.c @@ -22,6 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "audio.h" diff --git a/qemu/audio/noaudio.c b/qemu/audio/noaudio.c index 50db1f344..b360c199a 100644 --- a/qemu/audio/noaudio.c +++ b/qemu/audio/noaudio.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "audio.h" #include "qemu/timer.h" @@ -48,8 +49,8 @@ static int no_run_out (HWVoiceOut *hw, int live) now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ticks = now - no->old_ticks; - bytes = muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ()); - bytes = audio_MIN (bytes, INT_MAX); + bytes = muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND); + bytes = audio_MIN(bytes, INT_MAX); samples = bytes >> hw->info.shift; no->old_ticks = now; @@ -60,7 +61,7 @@ static int no_run_out (HWVoiceOut *hw, int live) static int no_write (SWVoiceOut *sw, void *buf, int len) { - return audio_pcm_sw_write (sw, buf, len); + return audio_pcm_sw_write(sw, buf, len); } static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) @@ -105,7 +106,7 @@ static int no_run_in (HWVoiceIn *hw) int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); int64_t ticks = now - no->old_ticks; int64_t bytes = - muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ()); + muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND); no->old_ticks = now; bytes = audio_MIN (bytes, INT_MAX); diff --git a/qemu/audio/ossaudio.c b/qemu/audio/ossaudio.c index 7dbe3332d..349e9dd53 100644 --- a/qemu/audio/ossaudio.c +++ b/qemu/audio/ossaudio.c @@ -21,9 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include +#include "qemu/osdep.h" #include -#include #include #include #include "qemu-common.h" diff --git a/qemu/audio/paaudio.c b/qemu/audio/paaudio.c index fea607166..57678e72e 100644 --- a/qemu/audio/paaudio.c +++ b/qemu/audio/paaudio.c @@ -1,4 +1,5 @@ /* public domain */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "audio.h" diff --git a/qemu/audio/sdlaudio.c b/qemu/audio/sdlaudio.c index 1140f2ea0..db69fe141 100644 --- a/qemu/audio/sdlaudio.c +++ b/qemu/audio/sdlaudio.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include #include "qemu-common.h" diff --git a/qemu/audio/spiceaudio.c b/qemu/audio/spiceaudio.c index 42ae4a45f..dea71d37a 100644 --- a/qemu/audio/spiceaudio.c +++ b/qemu/audio/spiceaudio.c @@ -17,6 +17,7 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "qemu/error-report.h" #include "qemu/timer.h" @@ -103,11 +104,11 @@ static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate) now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ticks = now - rate->start_ticks; - bytes = muldiv64 (ticks, info->bytes_per_second, get_ticks_per_sec ()); + bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND); samples = (bytes - rate->bytes_sent) >> info->shift; if (samples < 0 || samples > 65536) { error_report("Resetting rate control (%" PRId64 " samples)", samples); - rate_start (rate); + rate_start(rate); samples = 0; } rate->bytes_sent += samples << info->shift; diff --git a/qemu/audio/wavaudio.c b/qemu/audio/wavaudio.c index c586020c5..345952e51 100644 --- a/qemu/audio/wavaudio.c +++ b/qemu/audio/wavaudio.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "qemu/timer.h" #include "audio.h" @@ -50,7 +51,7 @@ static int wav_run_out (HWVoiceOut *hw, int live) int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); int64_t ticks = now - wav->old_ticks; int64_t bytes = - muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ()); + muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND); if (bytes > INT_MAX) { samples = INT_MAX >> hw->info.shift; diff --git a/qemu/audio/wavcapture.c b/qemu/audio/wavcapture.c index 86e905627..8bfb9e765 100644 --- a/qemu/audio/wavcapture.c +++ b/qemu/audio/wavcapture.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/hw.h" #include "monitor/monitor.h" #include "qemu/error-report.h" diff --git a/qemu/backends/baum.c b/qemu/backends/baum.c index a69aafff4..c537141b2 100644 --- a/qemu/backends/baum.c +++ b/qemu/backends/baum.c @@ -21,6 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "sysemu/char.h" #include "qemu/timer.h" @@ -303,7 +305,7 @@ static int baum_eat_packet(BaumDriverState *baum, const uint8_t *buf, int len) return 0; cur++; } - DPRINTF("Dropped %d bytes!\n", cur - buf); + DPRINTF("Dropped %td bytes!\n", cur - buf); } #define EAT(c) do {\ @@ -335,7 +337,7 @@ static int baum_eat_packet(BaumDriverState *baum, const uint8_t *buf, int len) /* Allow 100ms to complete the DisplayData packet */ timer_mod(baum->cellCount_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - get_ticks_per_sec() / 10); + NANOSECONDS_PER_SECOND / 10); for (i = 0; i < baum->x * baum->y ; i++) { EAT(c); cells[i] = c; @@ -561,8 +563,12 @@ static void baum_close(struct CharDriverState *chr) g_free(baum); } -CharDriverState *chr_baum_init(void) +static CharDriverState *chr_baum_init(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, + Error **errp) { + ChardevCommon *common = backend->u.braille.data; BaumDriverState *baum; CharDriverState *chr; brlapi_handle_t *handle; @@ -573,8 +579,12 @@ CharDriverState *chr_baum_init(void) #endif int tty; + chr = qemu_chr_alloc(common, errp); + if (!chr) { + return NULL; + } baum = g_malloc0(sizeof(BaumDriverState)); - baum->chr = chr = qemu_chr_alloc(); + baum->chr = chr; chr->opaque = baum; chr->chr_write = baum_write; @@ -586,14 +596,16 @@ CharDriverState *chr_baum_init(void) baum->brlapi_fd = brlapi__openConnection(handle, NULL, NULL); if (baum->brlapi_fd == -1) { - brlapi_perror("baum_init: brlapi_openConnection"); + error_setg(errp, "brlapi__openConnection: %s", + brlapi_strerror(brlapi_error_location())); goto fail_handle; } baum->cellCount_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, baum_cellCount_timer_cb, baum); if (brlapi__getDisplaySize(handle, &baum->x, &baum->y) == -1) { - brlapi_perror("baum_init: brlapi_getDisplaySize"); + error_setg(errp, "brlapi__getDisplaySize: %s", + brlapi_strerror(brlapi_error_location())); goto fail; } @@ -609,7 +621,8 @@ CharDriverState *chr_baum_init(void) tty = BRLAPI_TTY_DEFAULT; if (brlapi__enterTtyMode(handle, tty, NULL) == -1) { - brlapi_perror("baum_init: brlapi_enterTtyMode"); + error_setg(errp, "brlapi__enterTtyMode: %s", + brlapi_strerror(brlapi_error_location())); goto fail; } @@ -629,7 +642,8 @@ fail_handle: static void register_types(void) { - register_char_driver("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL); + register_char_driver("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL, + chr_baum_init); } type_init(register_types); diff --git a/qemu/backends/hostmem-file.c b/qemu/backends/hostmem-file.c index 4b5536101..5c4b808c1 100644 --- a/qemu/backends/hostmem-file.c +++ b/qemu/backends/hostmem-file.c @@ -9,6 +9,8 @@ * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "sysemu/hostmem.h" #include "sysemu/sysemu.h" @@ -50,11 +52,14 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) error_setg(errp, "-mem-path not supported on this host"); #else if (!memory_region_size(&backend->mr)) { + gchar *path; backend->force_prealloc = mem_prealloc; + path = object_get_canonical_path(OBJECT(backend)); memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), - object_get_canonical_path(OBJECT(backend)), + path, backend->size, fb->share, fb->mem_path, errp); + g_free(path); } #endif } @@ -83,9 +88,7 @@ static void set_mem_path(Object *o, const char *str, Error **errp) error_setg(errp, "cannot change property value"); return; } - if (fb->mem_path) { - g_free(fb->mem_path); - } + g_free(fb->mem_path); fb->mem_path = g_strdup(str); } @@ -118,11 +121,19 @@ file_backend_instance_init(Object *o) set_mem_path, NULL); } +static void file_backend_instance_finalize(Object *o) +{ + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); + + g_free(fb->mem_path); +} + static const TypeInfo file_backend_info = { .name = TYPE_MEMORY_BACKEND_FILE, .parent = TYPE_MEMORY_BACKEND, .class_init = file_backend_class_init, .instance_init = file_backend_instance_init, + .instance_finalize = file_backend_instance_finalize, .instance_size = sizeof(HostMemoryBackendFile), }; diff --git a/qemu/backends/hostmem-ram.c b/qemu/backends/hostmem-ram.c index a67a13452..04a7ac362 100644 --- a/qemu/backends/hostmem-ram.c +++ b/qemu/backends/hostmem-ram.c @@ -9,7 +9,9 @@ * 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 "sysemu/hostmem.h" +#include "qapi/error.h" #include "qom/object_interfaces.h" #define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram" diff --git a/qemu/backends/hostmem.c b/qemu/backends/hostmem.c index 41ba2af52..6e28be11e 100644 --- a/qemu/backends/hostmem.c +++ b/qemu/backends/hostmem.c @@ -9,8 +9,10 @@ * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "sysemu/hostmem.h" #include "hw/boards.h" +#include "qapi/error.h" #include "qapi/visitor.h" #include "qapi-types.h" #include "qapi-visit.h" @@ -26,18 +28,18 @@ QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE); #endif static void -host_memory_backend_get_size(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +host_memory_backend_get_size(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); uint64_t value = backend->size; - visit_type_size(v, &value, name, errp); + visit_type_size(v, name, &value, errp); } static void -host_memory_backend_set_size(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +host_memory_backend_set_size(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); Error *local_err = NULL; @@ -48,7 +50,7 @@ host_memory_backend_set_size(Object *obj, Visitor *v, void *opaque, goto out; } - visit_type_size(v, &value, name, &local_err); + visit_type_size(v, name, &value, &local_err); if (local_err) { goto out; } @@ -63,8 +65,8 @@ out: } static void -host_memory_backend_get_host_nodes(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); uint16List *host_nodes = NULL; @@ -91,18 +93,18 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, void *opaque, node = &(*node)->next; } while (true); - visit_type_uint16List(v, &host_nodes, name, errp); + visit_type_uint16List(v, name, &host_nodes, errp); } static void -host_memory_backend_set_host_nodes(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { #ifdef CONFIG_NUMA HostMemoryBackend *backend = MEMORY_BACKEND(obj); uint16List *l = NULL; - visit_type_uint16List(v, &l, name, errp); + visit_type_uint16List(v, name, &l, errp); while (l) { bitmap_set(backend->host_nodes, l->value, 1); @@ -313,9 +315,11 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) assert(maxnode <= MAX_NODES); if (mbind(ptr, sz, backend->policy, maxnode ? backend->host_nodes : NULL, maxnode + 1, flags)) { - error_setg_errno(errp, errno, - "cannot bind memory to host NUMA nodes"); - return; + if (backend->policy != MPOL_DEFAULT || errno != ENOSYS) { + error_setg_errno(errp, errno, + "cannot bind memory to host NUMA nodes"); + return; + } } #endif /* Preallocate memory after the NUMA policy has been instantiated. diff --git a/qemu/backends/msmouse.c b/qemu/backends/msmouse.c index 0119110a4..8dea5a130 100644 --- a/qemu/backends/msmouse.c +++ b/qemu/backends/msmouse.c @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include +#include "qemu/osdep.h" #include "qemu-common.h" #include "sysemu/char.h" #include "ui/console.h" @@ -63,11 +63,18 @@ static void msmouse_chr_close (struct CharDriverState *chr) g_free (chr); } -CharDriverState *qemu_chr_open_msmouse(void) +static CharDriverState *qemu_chr_open_msmouse(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, + Error **errp) { + ChardevCommon *common = backend->u.msmouse.data; CharDriverState *chr; - chr = qemu_chr_alloc(); + chr = qemu_chr_alloc(common, errp); + if (!chr) { + return NULL; + } chr->chr_write = msmouse_chr_write; chr->chr_close = msmouse_chr_close; chr->explicit_be_open = true; @@ -79,7 +86,8 @@ CharDriverState *qemu_chr_open_msmouse(void) static void register_types(void) { - register_char_driver("msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL); + register_char_driver("msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL, + qemu_chr_open_msmouse); } type_init(register_types); diff --git a/qemu/backends/rng-egd.c b/qemu/backends/rng-egd.c index 6c1340963..7a1b9242d 100644 --- a/qemu/backends/rng-egd.c +++ b/qemu/backends/rng-egd.c @@ -10,8 +10,10 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "sysemu/rng.h" #include "sysemu/char.h" +#include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "hw/qdev.h" /* just for DEFINE_PROP_CHR */ @@ -24,33 +26,12 @@ typedef struct RngEgd CharDriverState *chr; char *chr_name; - - GSList *requests; } RngEgd; -typedef struct RngRequest -{ - EntropyReceiveFunc *receive_entropy; - uint8_t *data; - void *opaque; - size_t offset; - size_t size; -} RngRequest; - -static void rng_egd_request_entropy(RngBackend *b, size_t size, - EntropyReceiveFunc *receive_entropy, - void *opaque) +static void rng_egd_request_entropy(RngBackend *b, RngRequest *req) { RngEgd *s = RNG_EGD(b); - RngRequest *req; - - req = g_malloc(sizeof(*req)); - - req->offset = 0; - req->size = size; - req->receive_entropy = receive_entropy; - req->opaque = opaque; - req->data = g_malloc(req->size); + size_t size = req->size; while (size > 0) { uint8_t header[2]; @@ -64,24 +45,15 @@ static void rng_egd_request_entropy(RngBackend *b, size_t size, size -= len; } - - s->requests = g_slist_append(s->requests, req); -} - -static void rng_egd_free_request(RngRequest *req) -{ - g_free(req->data); - g_free(req); } static int rng_egd_chr_can_read(void *opaque) { RngEgd *s = RNG_EGD(opaque); - GSList *i; + RngRequest *req; int size = 0; - for (i = s->requests; i; i = i->next) { - RngRequest *req = i->data; + QSIMPLEQ_FOREACH(req, &s->parent.requests, next) { size += req->size - req->offset; } @@ -93,8 +65,8 @@ static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size) RngEgd *s = RNG_EGD(opaque); size_t buf_offset = 0; - while (size > 0 && s->requests) { - RngRequest *req = s->requests->data; + while (size > 0 && !QSIMPLEQ_EMPTY(&s->parent.requests)) { + RngRequest *req = QSIMPLEQ_FIRST(&s->parent.requests); int len = MIN(size, req->size - req->offset); memcpy(req->data + req->offset, buf + buf_offset, len); @@ -103,38 +75,13 @@ static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size) size -= len; if (req->offset == req->size) { - s->requests = g_slist_remove_link(s->requests, s->requests); - req->receive_entropy(req->opaque, req->data, req->size); - rng_egd_free_request(req); + rng_backend_finalize_request(&s->parent, req); } } } -static void rng_egd_free_requests(RngEgd *s) -{ - GSList *i; - - for (i = s->requests; i; i = i->next) { - rng_egd_free_request(i->data); - } - - g_slist_free(s->requests); - s->requests = NULL; -} - -static void rng_egd_cancel_requests(RngBackend *b) -{ - RngEgd *s = RNG_EGD(b); - - /* We simply delete the list of pending requests. If there is data in the - * queue waiting to be read, this is okay, because there will always be - * more data than we requested originally - */ - rng_egd_free_requests(s); -} - static void rng_egd_opened(RngBackend *b, Error **errp) { RngEgd *s = RNG_EGD(b); @@ -203,8 +150,6 @@ static void rng_egd_finalize(Object *obj) } g_free(s->chr_name); - - rng_egd_free_requests(s); } static void rng_egd_class_init(ObjectClass *klass, void *data) @@ -212,7 +157,6 @@ static void rng_egd_class_init(ObjectClass *klass, void *data) RngBackendClass *rbc = RNG_BACKEND_CLASS(klass); rbc->request_entropy = rng_egd_request_entropy; - rbc->cancel_requests = rng_egd_cancel_requests; rbc->opened = rng_egd_opened; } diff --git a/qemu/backends/rng-random.c b/qemu/backends/rng-random.c index 4e51f4609..2e44e2519 100644 --- a/qemu/backends/rng-random.c +++ b/qemu/backends/rng-random.c @@ -10,8 +10,10 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "sysemu/rng-random.h" #include "sysemu/rng.h" +#include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qemu/main-loop.h" @@ -21,10 +23,6 @@ struct RndRandom int fd; char *filename; - - EntropyReceiveFunc *receive_func; - void *opaque; - size_t size; }; /** @@ -37,36 +35,35 @@ struct RndRandom static void entropy_available(void *opaque) { RndRandom *s = RNG_RANDOM(opaque); - uint8_t buffer[s->size]; - ssize_t len; - len = read(s->fd, buffer, s->size); - if (len < 0 && errno == EAGAIN) { - return; - } - g_assert(len != -1); + while (!QSIMPLEQ_EMPTY(&s->parent.requests)) { + RngRequest *req = QSIMPLEQ_FIRST(&s->parent.requests); + ssize_t len; + + len = read(s->fd, req->data, req->size); + if (len < 0 && errno == EAGAIN) { + return; + } + g_assert(len != -1); - s->receive_func(s->opaque, buffer, len); - s->receive_func = NULL; + req->receive_entropy(req->opaque, req->data, len); + rng_backend_finalize_request(&s->parent, req); + } + + /* We've drained all requests, the fd handler can be reset. */ qemu_set_fd_handler(s->fd, NULL, NULL, NULL); } -static void rng_random_request_entropy(RngBackend *b, size_t size, - EntropyReceiveFunc *receive_entropy, - void *opaque) +static void rng_random_request_entropy(RngBackend *b, RngRequest *req) { RndRandom *s = RNG_RANDOM(b); - if (s->receive_func) { - s->receive_func(s->opaque, NULL, 0); + if (QSIMPLEQ_EMPTY(&s->parent.requests)) { + /* If there are no pending requests yet, we need to + * install our fd handler. */ + qemu_set_fd_handler(s->fd, entropy_available, NULL, s); } - - s->receive_func = receive_entropy; - s->opaque = opaque; - s->size = size; - - qemu_set_fd_handler(s->fd, entropy_available, NULL, s); } static void rng_random_opened(RngBackend *b, Error **errp) diff --git a/qemu/backends/rng.c b/qemu/backends/rng.c index 5065fdc15..398ebe4a7 100644 --- a/qemu/backends/rng.c +++ b/qemu/backends/rng.c @@ -10,7 +10,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "sysemu/rng.h" +#include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qom/object_interfaces.h" @@ -19,18 +21,20 @@ void rng_backend_request_entropy(RngBackend *s, size_t size, void *opaque) { RngBackendClass *k = RNG_BACKEND_GET_CLASS(s); + RngRequest *req; if (k->request_entropy) { - k->request_entropy(s, size, receive_entropy, opaque); - } -} + req = g_malloc(sizeof(*req)); -void rng_backend_cancel_requests(RngBackend *s) -{ - RngBackendClass *k = RNG_BACKEND_GET_CLASS(s); + req->offset = 0; + req->size = size; + req->receive_entropy = receive_entropy; + req->opaque = opaque; + req->data = g_malloc(req->size); - if (k->cancel_requests) { - k->cancel_requests(s); + k->request_entropy(s, req); + + QSIMPLEQ_INSERT_TAIL(&s->requests, req, next); } } @@ -72,14 +76,48 @@ static void rng_backend_prop_set_opened(Object *obj, bool value, Error **errp) s->opened = true; } +static void rng_backend_free_request(RngRequest *req) +{ + g_free(req->data); + g_free(req); +} + +static void rng_backend_free_requests(RngBackend *s) +{ + RngRequest *req, *next; + + QSIMPLEQ_FOREACH_SAFE(req, &s->requests, next, next) { + rng_backend_free_request(req); + } + + QSIMPLEQ_INIT(&s->requests); +} + +void rng_backend_finalize_request(RngBackend *s, RngRequest *req) +{ + QSIMPLEQ_REMOVE(&s->requests, req, RngRequest, next); + rng_backend_free_request(req); +} + static void rng_backend_init(Object *obj) { + RngBackend *s = RNG_BACKEND(obj); + + QSIMPLEQ_INIT(&s->requests); + object_property_add_bool(obj, "opened", rng_backend_prop_get_opened, rng_backend_prop_set_opened, NULL); } +static void rng_backend_finalize(Object *obj) +{ + RngBackend *s = RNG_BACKEND(obj); + + rng_backend_free_requests(s); +} + static void rng_backend_class_init(ObjectClass *oc, void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); @@ -92,6 +130,7 @@ static const TypeInfo rng_backend_info = { .parent = TYPE_OBJECT, .instance_size = sizeof(RngBackend), .instance_init = rng_backend_init, + .instance_finalize = rng_backend_finalize, .class_size = sizeof(RngBackendClass), .class_init = rng_backend_class_init, .abstract = true, diff --git a/qemu/backends/testdev.c b/qemu/backends/testdev.c index eba396aeb..3ab1c90c1 100644 --- a/qemu/backends/testdev.c +++ b/qemu/backends/testdev.c @@ -23,6 +23,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "sysemu/char.h" @@ -108,13 +109,16 @@ static void testdev_close(struct CharDriverState *chr) g_free(testdev); } -CharDriverState *chr_testdev_init(void) +static CharDriverState *chr_testdev_init(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, + Error **errp) { TestdevCharState *testdev; CharDriverState *chr; - testdev = g_malloc0(sizeof(TestdevCharState)); - testdev->chr = chr = g_malloc0(sizeof(CharDriverState)); + testdev = g_new0(TestdevCharState, 1); + testdev->chr = chr = g_new0(CharDriverState, 1); chr->opaque = testdev; chr->chr_write = testdev_write; @@ -125,7 +129,8 @@ CharDriverState *chr_testdev_init(void) static void register_types(void) { - register_char_driver("testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL); + register_char_driver("testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL, + chr_testdev_init); } type_init(register_types); diff --git a/qemu/backends/tpm.c b/qemu/backends/tpm.c index a512693b1..536f262bb 100644 --- a/qemu/backends/tpm.c +++ b/qemu/backends/tpm.c @@ -12,7 +12,9 @@ * Based on backends/rng.c by Anthony Liguori */ +#include "qemu/osdep.h" #include "sysemu/tpm_backend.h" +#include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "sysemu/tpm.h" #include "qemu/thread.h" diff --git a/qemu/balloon.c b/qemu/balloon.c index 5d69e8a00..f2ef50cf7 100644 --- a/qemu/balloon.c +++ b/qemu/balloon.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "exec/cpu-common.h" #include "sysemu/kvm.h" @@ -36,6 +37,17 @@ static QEMUBalloonEvent *balloon_event_fn; static QEMUBalloonStatus *balloon_stat_fn; static void *balloon_opaque; +static bool balloon_inhibited; + +bool qemu_balloon_is_inhibited(void) +{ + return balloon_inhibited; +} + +void qemu_balloon_inhibit(bool state) +{ + balloon_inhibited = state; +} static bool have_balloon(Error **errp) { diff --git a/qemu/block.c b/qemu/block.c index d088ee02f..d4939b49b 100644 --- a/qemu/block.c +++ b/qemu/block.c @@ -21,28 +21,28 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "config-host.h" -#include "qemu-common.h" +#include "qemu/osdep.h" #include "trace.h" #include "block/block_int.h" #include "block/blockjob.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "qapi/qmp/qerror.h" +#include "qapi/qmp/qbool.h" #include "qapi/qmp/qjson.h" #include "sysemu/block-backend.h" #include "sysemu/sysemu.h" #include "qemu/notify.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "block/qapi.h" #include "qmp-commands.h" #include "qemu/timer.h" #include "qapi-event.h" #include "block/throttle-groups.h" +#include "qemu/cutils.h" +#include "qemu/id.h" #ifdef CONFIG_BSD -#include -#include #include #include #ifndef __DragonFly__ @@ -54,44 +54,27 @@ #include #endif -/** - * A BdrvDirtyBitmap can be in three possible states: - * (1) successor is NULL and disabled is false: full r/w mode - * (2) successor is NULL and disabled is true: read only mode ("disabled") - * (3) successor is set: frozen mode. - * A frozen bitmap cannot be renamed, deleted, anonymized, cleared, set, - * or enabled. A frozen bitmap can only abdicate() or reclaim(). - */ -struct BdrvDirtyBitmap { - HBitmap *bitmap; /* Dirty sector bitmap implementation */ - BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */ - char *name; /* Optional non-empty unique ID */ - int64_t size; /* Size of the bitmap (Number of sectors) */ - bool disabled; /* Bitmap is read-only */ - QLIST_ENTRY(BdrvDirtyBitmap) list; -}; - #define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ -static QTAILQ_HEAD(, BlockDriverState) bdrv_states = - QTAILQ_HEAD_INITIALIZER(bdrv_states); - static QTAILQ_HEAD(, BlockDriverState) graph_bdrv_states = QTAILQ_HEAD_INITIALIZER(graph_bdrv_states); +static QTAILQ_HEAD(, BlockDriverState) all_bdrv_states = + QTAILQ_HEAD_INITIALIZER(all_bdrv_states); + static QLIST_HEAD(, BlockDriver) bdrv_drivers = QLIST_HEAD_INITIALIZER(bdrv_drivers); static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, const char *reference, QDict *options, int flags, BlockDriverState *parent, - const BdrvChildRole *child_role, - BlockDriver *drv, Error **errp); + const BdrvChildRole *child_role, Error **errp); -static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs); /* If non-zero, use only whitelisted block drivers */ static int use_bdrv_whitelist; +static void bdrv_close(BlockDriverState *bs); + #ifdef _WIN32 static int is_windows_drive_prefix(const char *filename) { @@ -242,10 +225,7 @@ void bdrv_register(BlockDriver *bdrv) BlockDriverState *bdrv_new_root(void) { - BlockDriverState *bs = bdrv_new(); - - QTAILQ_INSERT_TAIL(&bdrv_states, bs, device_list); - return bs; + return bdrv_new(); } BlockDriverState *bdrv_new(void) @@ -258,20 +238,15 @@ BlockDriverState *bdrv_new(void) for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { QLIST_INIT(&bs->op_blockers[i]); } - bdrv_iostatus_disable(bs); - notifier_list_init(&bs->close_notifiers); notifier_with_return_list_init(&bs->before_write_notifiers); qemu_co_queue_init(&bs->throttled_reqs[0]); qemu_co_queue_init(&bs->throttled_reqs[1]); bs->refcnt = 1; bs->aio_context = qemu_get_aio_context(); - return bs; -} + QTAILQ_INSERT_TAIL(&all_bdrv_states, bs, bs_list); -void bdrv_add_close_notifier(BlockDriverState *bs, Notifier *notify) -{ - notifier_list_add(&bs->close_notifiers, notify); + return bs; } BlockDriver *bdrv_find_format(const char *format_name) @@ -314,11 +289,9 @@ static int bdrv_is_whitelisted(BlockDriver *drv, bool read_only) return 0; } -BlockDriver *bdrv_find_whitelisted_format(const char *format_name, - bool read_only) +bool bdrv_uses_whitelist(void) { - BlockDriver *drv = bdrv_find_format(format_name); - return drv && bdrv_is_whitelisted(drv, read_only) ? drv : NULL; + return use_bdrv_whitelist; } typedef struct CreateCo { @@ -633,6 +606,20 @@ static int refresh_total_sectors(BlockDriverState *bs, int64_t hint) return 0; } +/** + * Combines a QDict of new block driver @options with any missing options taken + * from @old_options, so that leaving out an option defaults to its old value. + */ +static void bdrv_join_options(BlockDriverState *bs, QDict *options, + QDict *old_options) +{ + if (bs->drv && bs->drv->bdrv_join_options) { + bs->drv->bdrv_join_options(options, old_options); + } else { + qdict_join(options, old_options, false); + } +} + /** * Set open flags for a given discard mode * @@ -658,21 +645,23 @@ int bdrv_parse_discard_flags(const char *mode, int *flags) * * Return 0 on success, -1 if the cache mode was invalid. */ -int bdrv_parse_cache_flags(const char *mode, int *flags) +int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough) { *flags &= ~BDRV_O_CACHE_MASK; if (!strcmp(mode, "off") || !strcmp(mode, "none")) { - *flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; + *writethrough = false; + *flags |= BDRV_O_NOCACHE; } else if (!strcmp(mode, "directsync")) { + *writethrough = true; *flags |= BDRV_O_NOCACHE; } else if (!strcmp(mode, "writeback")) { - *flags |= BDRV_O_CACHE_WB; + *writethrough = false; } else if (!strcmp(mode, "unsafe")) { - *flags |= BDRV_O_CACHE_WB; + *writethrough = false; *flags |= BDRV_O_NO_FLUSH; } else if (!strcmp(mode, "writethrough")) { - /* this is the default */ + *writethrough = true; } else { return -1; } @@ -681,75 +670,101 @@ int bdrv_parse_cache_flags(const char *mode, int *flags) } /* - * Returns the flags that a temporary snapshot should get, based on the - * originally requested flags (the originally requested image will have flags - * like a backing file) + * Returns the options and flags that a temporary snapshot should get, based on + * the originally requested flags (the originally requested image will have + * flags like a backing file) */ -static int bdrv_temp_snapshot_flags(int flags) +static void bdrv_temp_snapshot_options(int *child_flags, QDict *child_options, + int parent_flags, QDict *parent_options) { - return (flags & ~BDRV_O_SNAPSHOT) | BDRV_O_TEMPORARY; + *child_flags = (parent_flags & ~BDRV_O_SNAPSHOT) | BDRV_O_TEMPORARY; + + /* For temporary files, unconditional cache=unsafe is fine */ + qdict_set_default_str(child_options, BDRV_OPT_CACHE_DIRECT, "off"); + qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on"); } /* - * Returns the flags that bs->file should get if a protocol driver is expected, - * based on the given flags for the parent BDS + * Returns the options and flags that bs->file should get if a protocol driver + * is expected, based on the given options and flags for the parent BDS */ -static int bdrv_inherited_flags(int flags) +static void bdrv_inherited_options(int *child_flags, QDict *child_options, + int parent_flags, QDict *parent_options) { + int flags = parent_flags; + /* Enable protocol handling, disable format probing for bs->file */ flags |= BDRV_O_PROTOCOL; + /* If the cache mode isn't explicitly set, inherit direct and no-flush from + * the parent. */ + qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT); + qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH); + /* Our block drivers take care to send flushes and respect unmap policy, - * so we can enable both unconditionally on lower layers. */ - flags |= BDRV_O_CACHE_WB | BDRV_O_UNMAP; + * so we can default to enable both on lower layers regardless of the + * corresponding parent options. */ + flags |= BDRV_O_UNMAP; /* Clear flags that only apply to the top layer */ - flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_COPY_ON_READ); + flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_COPY_ON_READ | + BDRV_O_NO_IO); - return flags; + *child_flags = flags; } const BdrvChildRole child_file = { - .inherit_flags = bdrv_inherited_flags, + .inherit_options = bdrv_inherited_options, }; /* - * Returns the flags that bs->file should get if the use of formats (and not - * only protocols) is permitted for it, based on the given flags for the parent - * BDS + * Returns the options and flags that bs->file should get if the use of formats + * (and not only protocols) is permitted for it, based on the given options and + * flags for the parent BDS */ -static int bdrv_inherited_fmt_flags(int parent_flags) +static void bdrv_inherited_fmt_options(int *child_flags, QDict *child_options, + int parent_flags, QDict *parent_options) { - int flags = child_file.inherit_flags(parent_flags); - return flags & ~BDRV_O_PROTOCOL; + child_file.inherit_options(child_flags, child_options, + parent_flags, parent_options); + + *child_flags &= ~(BDRV_O_PROTOCOL | BDRV_O_NO_IO); } const BdrvChildRole child_format = { - .inherit_flags = bdrv_inherited_fmt_flags, + .inherit_options = bdrv_inherited_fmt_options, }; /* - * Returns the flags that bs->backing_hd should get, based on the given flags - * for the parent BDS + * Returns the options and flags that bs->backing should get, based on the + * given options and flags for the parent BDS */ -static int bdrv_backing_flags(int flags) +static void bdrv_backing_options(int *child_flags, QDict *child_options, + int parent_flags, QDict *parent_options) { + int flags = parent_flags; + + /* The cache mode is inherited unmodified for backing files; except WCE, + * which is only applied on the top level (BlockBackend) */ + qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT); + qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH); + /* backing files always opened read-only */ flags &= ~(BDRV_O_RDWR | BDRV_O_COPY_ON_READ); /* snapshot=on is handled on the top layer */ flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_TEMPORARY); - return flags; + *child_flags = flags; } static const BdrvChildRole child_backing = { - .inherit_flags = bdrv_backing_flags, + .inherit_options = bdrv_backing_options, }; static int bdrv_open_flags(BlockDriverState *bs, int flags) { - int open_flags = flags | BDRV_O_CACHE_WB; + int open_flags = flags; /* * Clear flags that are internal to the block layer before opening the @@ -767,16 +782,46 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags) return open_flags; } +static void update_flags_from_options(int *flags, QemuOpts *opts) +{ + *flags &= ~BDRV_O_CACHE_MASK; + + assert(qemu_opt_find(opts, BDRV_OPT_CACHE_NO_FLUSH)); + if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) { + *flags |= BDRV_O_NO_FLUSH; + } + + assert(qemu_opt_find(opts, BDRV_OPT_CACHE_DIRECT)); + if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_DIRECT, false)) { + *flags |= BDRV_O_NOCACHE; + } +} + +static void update_options_from_flags(QDict *options, int flags) +{ + if (!qdict_haskey(options, BDRV_OPT_CACHE_DIRECT)) { + qdict_put(options, BDRV_OPT_CACHE_DIRECT, + qbool_from_bool(flags & BDRV_O_NOCACHE)); + } + if (!qdict_haskey(options, BDRV_OPT_CACHE_NO_FLUSH)) { + qdict_put(options, BDRV_OPT_CACHE_NO_FLUSH, + qbool_from_bool(flags & BDRV_O_NO_FLUSH)); + } +} + static void bdrv_assign_node_name(BlockDriverState *bs, const char *node_name, Error **errp) { - if (!node_name) { - return; - } + char *gen_node_name = NULL; - /* Check for empty string or invalid characters */ - if (!id_wellformed(node_name)) { + if (!node_name) { + node_name = gen_node_name = id_generate(ID_BLOCK); + } else if (!id_wellformed(node_name)) { + /* + * Check for empty string or invalid characters, but not if it is + * generated (generated names use characters not available to the user) + */ error_setg(errp, "Invalid node name"); return; } @@ -785,18 +830,20 @@ static void bdrv_assign_node_name(BlockDriverState *bs, if (blk_by_name(node_name)) { error_setg(errp, "node-name=%s is conflicting with a device id", node_name); - return; + goto out; } /* takes care of avoiding duplicates node names */ if (bdrv_find_node(node_name)) { error_setg(errp, "Duplicate node name"); - return; + goto out; } /* copy node name into the bs and insert it into the graph list */ pstrcpy(bs->node_name, sizeof(bs->node_name), node_name); QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list); +out: + g_free(gen_node_name); } static QemuOptsList bdrv_runtime_opts = { @@ -808,6 +855,21 @@ static QemuOptsList bdrv_runtime_opts = { .type = QEMU_OPT_STRING, .help = "Node name of the block device node", }, + { + .name = "driver", + .type = QEMU_OPT_STRING, + .help = "Block driver to use for the node", + }, + { + .name = BDRV_OPT_CACHE_DIRECT, + .type = QEMU_OPT_BOOL, + .help = "Bypass software writeback cache on the host", + }, + { + .name = BDRV_OPT_CACHE_NO_FLUSH, + .type = QEMU_OPT_BOOL, + .help = "Ignore flush requests", + }, { /* end of list */ } }, }; @@ -817,21 +879,34 @@ static QemuOptsList bdrv_runtime_opts = { * * Removes all processed options from *options. */ -static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, - QDict *options, int flags, BlockDriver *drv, Error **errp) +static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file, + QDict *options, Error **errp) { int ret, open_flags; const char *filename; + const char *driver_name = NULL; const char *node_name = NULL; QemuOpts *opts; + BlockDriver *drv; Error *local_err = NULL; - assert(drv != NULL); assert(bs->file == NULL); assert(options != NULL && bs->options != options); + opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail_opts; + } + + driver_name = qemu_opt_get(opts, "driver"); + drv = bdrv_find_format(driver_name); + assert(drv != NULL); + if (file != NULL) { - filename = file->filename; + filename = file->bs->filename; } else { filename = qdict_get_try_str(options, "filename"); } @@ -839,19 +914,13 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, if (drv->bdrv_needs_filename && !filename) { error_setg(errp, "The '%s' block driver requires a file name", drv->format_name); - return -EINVAL; - } - - trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name); - - opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort); - qemu_opts_absorb_qdict(opts, options, &local_err); - if (local_err) { - error_propagate(errp, local_err); ret = -EINVAL; goto fail_opts; } + trace_bdrv_open_common(bs, filename ?: "", bs->open_flags, + drv->format_name); + node_name = qemu_opt_get(opts, "node-name"); bdrv_assign_node_name(bs, node_name, &local_err); if (local_err) { @@ -860,11 +929,9 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, goto fail_opts; } - bs->guest_block_size = 512; bs->request_alignment = 512; bs->zero_beyond_eof = true; - open_flags = bdrv_open_flags(bs, flags); - bs->read_only = !(open_flags & BDRV_O_RDWR); + bs->read_only = !(bs->open_flags & BDRV_O_RDWR); if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) { error_setg(errp, @@ -877,7 +944,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, } assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */ - if (flags & BDRV_O_COPY_ON_READ) { + if (bs->open_flags & BDRV_O_COPY_ON_READ) { if (!bs->read_only) { bdrv_enable_copy_on_read(bs); } else { @@ -897,9 +964,11 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, bs->drv = drv; bs->opaque = g_malloc0(drv->instance_size); - bs->enable_write_cache = !!(flags & BDRV_O_CACHE_WB); + /* Apply cache mode options */ + update_flags_from_options(&bs->open_flags, opts); /* Open the image, either directly or using a protocol */ + open_flags = bdrv_open_flags(bs, bs->open_flags); if (drv->bdrv_file_open) { assert(file == NULL); assert(!drv->bdrv_needs_filename || filename != NULL); @@ -926,13 +995,6 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, goto free_and_fail; } - if (bs->encrypted) { - error_report("Encrypted images are deprecated"); - error_printf("Support for them will be removed in a future release.\n" - "You can use 'qemu-img convert' to convert your image" - " to an unencrypted one.\n"); - } - ret = refresh_total_sectors(bs, bs->total_sectors); if (ret < 0) { error_setg_errno(errp, -ret, "Could not refresh total sector count"); @@ -990,47 +1052,55 @@ static QDict *parse_json_filename(const char *filename, Error **errp) return options; } +static void parse_json_protocol(QDict *options, const char **pfilename, + Error **errp) +{ + QDict *json_options; + Error *local_err = NULL; + + /* Parse json: pseudo-protocol */ + if (!*pfilename || !g_str_has_prefix(*pfilename, "json:")) { + return; + } + + json_options = parse_json_filename(*pfilename, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* Options given in the filename have lower priority than options + * specified directly */ + qdict_join(options, json_options, false); + QDECREF(json_options); + *pfilename = NULL; +} + /* * Fills in default options for opening images and converts the legacy * filename/flags pair to option QDict entries. * The BDRV_O_PROTOCOL flag in *flags will be set or cleared accordingly if a * block driver has been specified explicitly. */ -static int bdrv_fill_options(QDict **options, const char **pfilename, - int *flags, BlockDriver *drv, Error **errp) +static int bdrv_fill_options(QDict **options, const char *filename, + int *flags, Error **errp) { - const char *filename = *pfilename; const char *drvname; bool protocol = *flags & BDRV_O_PROTOCOL; bool parse_filename = false; - BlockDriver *tmp_drv; + BlockDriver *drv = NULL; Error *local_err = NULL; - /* Parse json: pseudo-protocol */ - if (filename && g_str_has_prefix(filename, "json:")) { - QDict *json_options = parse_json_filename(filename, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return -EINVAL; - } - - /* Options given in the filename have lower priority than options - * specified directly */ - qdict_join(*options, json_options, false); - QDECREF(json_options); - *pfilename = filename = NULL; - } - drvname = qdict_get_try_str(*options, "driver"); - - /* If the user has explicitly specified the driver, this choice should - * override the BDRV_O_PROTOCOL flag */ - tmp_drv = drv; - if (!tmp_drv && drvname) { - tmp_drv = bdrv_find_format(drvname); - } - if (tmp_drv) { - protocol = tmp_drv->bdrv_file_open; + if (drvname) { + drv = bdrv_find_format(drvname); + if (!drv) { + error_setg(errp, "Unknown driver '%s'", drvname); + return -ENOENT; + } + /* If the user has explicitly specified the driver, this choice should + * override the BDRV_O_PROTOCOL flag */ + protocol = drv->bdrv_file_open; } if (protocol) { @@ -1039,6 +1109,9 @@ static int bdrv_fill_options(QDict **options, const char **pfilename, *flags &= ~BDRV_O_PROTOCOL; } + /* Translate cache options from flags into options */ + update_options_from_flags(*options, *flags); + /* Fetch the file name from the options QDict if necessary */ if (protocol && filename) { if (!qdict_haskey(*options, "filename")) { @@ -1054,33 +1127,18 @@ static int bdrv_fill_options(QDict **options, const char **pfilename, /* Find the right block driver */ filename = qdict_get_try_str(*options, "filename"); - if (drv) { - if (drvname) { - error_setg(errp, "Driver specified twice"); - return -EINVAL; - } - drvname = drv->format_name; - qdict_put(*options, "driver", qstring_from_str(drvname)); - } else { - if (!drvname && protocol) { - if (filename) { - drv = bdrv_find_protocol(filename, parse_filename, errp); - if (!drv) { - return -EINVAL; - } - - drvname = drv->format_name; - qdict_put(*options, "driver", qstring_from_str(drvname)); - } else { - error_setg(errp, "Must specify either driver or file"); - return -EINVAL; - } - } else if (drvname) { - drv = bdrv_find_format(drvname); + if (!drvname && protocol) { + if (filename) { + drv = bdrv_find_protocol(filename, parse_filename, errp); if (!drv) { - error_setg(errp, "Unknown driver '%s'", drvname); - return -ENOENT; + return -EINVAL; } + + drvname = drv->format_name; + qdict_put(*options, "driver", qstring_from_str(drvname)); + } else { + error_setg(errp, "Must specify either driver or file"); + return -EINVAL; } } @@ -1102,68 +1160,100 @@ static int bdrv_fill_options(QDict **options, const char **pfilename, return 0; } -static BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, - BlockDriverState *child_bs, - const BdrvChildRole *child_role) +BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, + const char *child_name, + const BdrvChildRole *child_role) { BdrvChild *child = g_new(BdrvChild, 1); *child = (BdrvChild) { .bs = child_bs, + .name = g_strdup(child_name), .role = child_role, }; - QLIST_INSERT_HEAD(&parent_bs->children, child, next); + QLIST_INSERT_HEAD(&child_bs->parents, child, next_parent); + + return child; +} +static BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, + BlockDriverState *child_bs, + const char *child_name, + const BdrvChildRole *child_role) +{ + BdrvChild *child = bdrv_root_attach_child(child_bs, child_name, child_role); + QLIST_INSERT_HEAD(&parent_bs->children, child, next); return child; } static void bdrv_detach_child(BdrvChild *child) { - QLIST_REMOVE(child, next); + if (child->next.le_prev) { + QLIST_REMOVE(child, next); + child->next.le_prev = NULL; + } + QLIST_REMOVE(child, next_parent); + g_free(child->name); g_free(child); } +void bdrv_root_unref_child(BdrvChild *child) +{ + BlockDriverState *child_bs; + + child_bs = child->bs; + bdrv_detach_child(child); + bdrv_unref(child_bs); +} + void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) { - BlockDriverState *child_bs = child->bs; + if (child == NULL) { + return; + } if (child->bs->inherits_from == parent) { child->bs->inherits_from = NULL; } - bdrv_detach_child(child); - bdrv_unref(child_bs); + bdrv_root_unref_child(child); } +/* + * Sets the backing file link of a BDS. A new reference is created; callers + * which don't need their own reference any more must call bdrv_unref(). + */ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd) { + if (backing_hd) { + bdrv_ref(backing_hd); + } - if (bs->backing_hd) { + if (bs->backing) { assert(bs->backing_blocker); - bdrv_op_unblock_all(bs->backing_hd, bs->backing_blocker); - bdrv_detach_child(bs->backing_child); + bdrv_op_unblock_all(bs->backing->bs, bs->backing_blocker); + bdrv_unref_child(bs, bs->backing); } else if (backing_hd) { error_setg(&bs->backing_blocker, "node is used as backing hd of '%s'", bdrv_get_device_or_node_name(bs)); } - bs->backing_hd = backing_hd; if (!backing_hd) { error_free(bs->backing_blocker); bs->backing_blocker = NULL; - bs->backing_child = NULL; + bs->backing = NULL; goto out; } - bs->backing_child = bdrv_attach_child(bs, backing_hd, &child_backing); + bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing); bs->open_flags &= ~BDRV_O_NO_BACKING; pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_hd->filename); pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_hd->drv ? backing_hd->drv->format_name : ""); - bdrv_op_block_all(bs->backing_hd, bs->backing_blocker); + bdrv_op_block_all(backing_hd, bs->backing_blocker); /* Otherwise we won't be able to commit due to check in bdrv_commit */ - bdrv_op_unblock(bs->backing_hd, BLOCK_OP_TYPE_COMMIT_TARGET, + bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_COMMIT_TARGET, bs->backing_blocker); out: bdrv_refresh_limits(bs, NULL); @@ -1172,30 +1262,43 @@ out: /* * Opens the backing file for a BlockDriverState if not yet open * - * options is a QDict of options to pass to the block drivers, or NULL for an - * empty set of options. The reference to the QDict is transferred to this - * function (even on failure), so if the caller intends to reuse the dictionary, - * it needs to use QINCREF() before calling bdrv_file_open. + * bdref_key specifies the key for the image's BlockdevRef in the options QDict. + * That QDict has to be flattened; therefore, if the BlockdevRef is a QDict + * itself, all options starting with "${bdref_key}." are considered part of the + * BlockdevRef. + * + * TODO Can this be unified with bdrv_open_image()? */ -int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) +int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, + const char *bdref_key, Error **errp) { char *backing_filename = g_malloc0(PATH_MAX); + char *bdref_key_dot; + const char *reference = NULL; int ret = 0; BlockDriverState *backing_hd; + QDict *options; + QDict *tmp_parent_options = NULL; Error *local_err = NULL; - if (bs->backing_hd != NULL) { - QDECREF(options); + if (bs->backing != NULL) { goto free_exit; } /* NULL means an empty set of options */ - if (options == NULL) { - options = qdict_new(); + if (parent_options == NULL) { + tmp_parent_options = qdict_new(); + parent_options = tmp_parent_options; } bs->open_flags &= ~BDRV_O_NO_BACKING; - if (qdict_haskey(options, "file.filename")) { + + bdref_key_dot = g_strdup_printf("%s.", bdref_key); + qdict_extract_subqdict(parent_options, &options, bdref_key_dot); + g_free(bdref_key_dot); + + reference = qdict_get_try_str(parent_options, bdref_key); + if (reference || qdict_haskey(options, "file.filename")) { backing_filename[0] = '\0'; } else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) { QDECREF(options); @@ -1218,31 +1321,31 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) goto free_exit; } - backing_hd = bdrv_new(); - if (bs->backing_format[0] != '\0' && !qdict_haskey(options, "driver")) { qdict_put(options, "driver", qstring_from_str(bs->backing_format)); } - assert(bs->backing_hd == NULL); + backing_hd = NULL; ret = bdrv_open_inherit(&backing_hd, *backing_filename ? backing_filename : NULL, - NULL, options, 0, bs, &child_backing, - NULL, &local_err); + reference, options, 0, bs, &child_backing, + errp); if (ret < 0) { - bdrv_unref(backing_hd); - backing_hd = NULL; bs->open_flags |= BDRV_O_NO_BACKING; - error_setg(errp, "Could not open backing file: %s", - error_get_pretty(local_err)); - error_free(local_err); + error_prepend(errp, "Could not open backing file: "); goto free_exit; } + /* Hook up the backing file link; drop our reference, bs owns the + * backing_hd reference now */ bdrv_set_backing_hd(bs, backing_hd); + bdrv_unref(backing_hd); + + qdict_del(parent_options, bdref_key); free_exit: g_free(backing_filename); + QDECREF(tmp_parent_options); return ret; } @@ -1291,59 +1394,25 @@ BdrvChild *bdrv_open_child(const char *filename, bs = NULL; ret = bdrv_open_inherit(&bs, filename, reference, image_options, 0, - parent, child_role, NULL, errp); + parent, child_role, errp); if (ret < 0) { goto done; } - c = bdrv_attach_child(parent, bs, child_role); + c = bdrv_attach_child(parent, bs, bdref_key, child_role); done: qdict_del(options, bdref_key); return c; } -/* - * This is a version of bdrv_open_child() that returns 0/-EINVAL instead of - * a BdrvChild object. - * - * If allow_none is true, no image will be opened if filename is false and no - * BlockdevRef is given. *pbs will remain unchanged and 0 will be returned. - * - * To conform with the behavior of bdrv_open(), *pbs has to be NULL. - */ -int bdrv_open_image(BlockDriverState **pbs, const char *filename, - QDict *options, const char *bdref_key, - BlockDriverState* parent, const BdrvChildRole *child_role, - bool allow_none, Error **errp) -{ - Error *local_err = NULL; - BdrvChild *c; - - assert(pbs); - assert(*pbs == NULL); - - c = bdrv_open_child(filename, options, bdref_key, parent, child_role, - allow_none, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return -EINVAL; - } - - if (c != NULL) { - *pbs = c->bs; - } - - return 0; -} - -int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp) +static int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, + QDict *snapshot_options, Error **errp) { /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */ char *tmp_filename = g_malloc0(PATH_MAX + 1); int64_t total_size; QemuOpts *opts = NULL; - QDict *snapshot_options; BlockDriverState *bs_snapshot; Error *local_err = NULL; int ret; @@ -1369,27 +1438,27 @@ int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp) opts = qemu_opts_create(bdrv_qcow2.create_opts, NULL, 0, &error_abort); qemu_opt_set_number(opts, BLOCK_OPT_SIZE, total_size, &error_abort); - ret = bdrv_create(&bdrv_qcow2, tmp_filename, opts, &local_err); + ret = bdrv_create(&bdrv_qcow2, tmp_filename, opts, errp); qemu_opts_del(opts); if (ret < 0) { - error_setg_errno(errp, -ret, "Could not create temporary overlay " - "'%s': %s", tmp_filename, - error_get_pretty(local_err)); - error_free(local_err); + error_prepend(errp, "Could not create temporary overlay '%s': ", + tmp_filename); goto out; } - /* Prepare a new options QDict for the temporary file */ - snapshot_options = qdict_new(); + /* Prepare options QDict for the temporary file */ qdict_put(snapshot_options, "file.driver", qstring_from_str("file")); qdict_put(snapshot_options, "file.filename", qstring_from_str(tmp_filename)); + qdict_put(snapshot_options, "driver", + qstring_from_str("qcow2")); bs_snapshot = bdrv_new(); ret = bdrv_open(&bs_snapshot, NULL, NULL, snapshot_options, - flags, &bdrv_qcow2, &local_err); + flags, &local_err); + snapshot_options = NULL; if (ret < 0) { error_propagate(errp, local_err); goto out; @@ -1398,6 +1467,7 @@ int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp) bdrv_append(bs_snapshot, bs); out: + QDECREF(snapshot_options); g_free(tmp_filename); return ret; } @@ -1420,13 +1490,16 @@ out: static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, const char *reference, QDict *options, int flags, BlockDriverState *parent, - const BdrvChildRole *child_role, - BlockDriver *drv, Error **errp) + const BdrvChildRole *child_role, Error **errp) { int ret; - BlockDriverState *file = NULL, *bs; + BdrvChild *file = NULL; + BlockDriverState *bs; + BlockDriver *drv = NULL; const char *drvname; + const char *backing; Error *local_err = NULL; + QDict *snapshot_options = NULL; int snapshot_flags = 0; assert(pbs); @@ -1453,6 +1526,13 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, if (!bs) { return -ENODEV; } + + if (bs->throttle_state) { + error_setg(errp, "Cannot reference an existing block device for " + "which I/O throttling is enabled"); + return -EINVAL; + } + bdrv_ref(bs); *pbs = bs; return 0; @@ -1469,22 +1549,34 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, options = qdict_new(); } + /* json: syntax counts as explicit options, as if in the QDict */ + parse_json_protocol(options, &filename, &local_err); + if (local_err) { + ret = -EINVAL; + goto fail; + } + + bs->explicit_options = qdict_clone_shallow(options); + if (child_role) { bs->inherits_from = parent; - flags = child_role->inherit_flags(parent->open_flags); + child_role->inherit_options(&flags, options, + parent->open_flags, parent->options); } - ret = bdrv_fill_options(&options, &filename, &flags, drv, &local_err); + ret = bdrv_fill_options(&options, filename, &flags, &local_err); if (local_err) { goto fail; } + bs->open_flags = flags; + bs->options = options; + options = qdict_clone_shallow(options); + /* Find the right image format driver */ - drv = NULL; drvname = qdict_get_try_str(options, "driver"); if (drvname) { drv = bdrv_find_format(drvname); - qdict_del(options, "driver"); if (!drv) { error_setg(errp, "Unknown driver: '%s'", drvname); ret = -EINVAL; @@ -1494,9 +1586,11 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, assert(drvname || !(flags & BDRV_O_PROTOCOL)); - bs->open_flags = flags; - bs->options = options; - options = qdict_clone_shallow(options); + backing = qdict_get_try_str(options, "backing"); + if (backing && *backing == '\0') { + flags |= BDRV_O_NO_BACKING; + qdict_del(options, "backing"); + } /* Open image file without format layer */ if ((flags & BDRV_O_PROTOCOL) == 0) { @@ -1504,15 +1598,18 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, flags |= BDRV_O_ALLOW_RDWR; } if (flags & BDRV_O_SNAPSHOT) { - snapshot_flags = bdrv_temp_snapshot_flags(flags); - flags = bdrv_backing_flags(flags); + snapshot_options = qdict_new(); + bdrv_temp_snapshot_options(&snapshot_flags, snapshot_options, + flags, options); + bdrv_backing_options(&flags, options, flags, options); } - assert(file == NULL); bs->open_flags = flags; - ret = bdrv_open_image(&file, filename, options, "file", - bs, &child_file, true, &local_err); - if (ret < 0) { + + file = bdrv_open_child(filename, options, "file", bs, + &child_file, true, &local_err); + if (local_err) { + ret = -EINVAL; goto fail; } } @@ -1520,10 +1617,23 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, /* Image format probing */ bs->probed = !drv; if (!drv && file) { - ret = find_image_format(file, filename, &drv, &local_err); + ret = find_image_format(file->bs, filename, &drv, &local_err); if (ret < 0) { goto fail; } + /* + * This option update would logically belong in bdrv_fill_options(), + * but we first need to open bs->file for the probing to work, while + * opening bs->file already requires the (mostly) final set of options + * so that cache mode etc. can be inherited. + * + * Adding the driver later is somewhat ugly, but it's not an option + * that would ever be inherited, so it's correct. We just need to make + * sure to update both bs->options (which has the full effective + * options for bs) and options (which has file.* already removed). + */ + qdict_put(bs->options, "driver", qstring_from_str(drv->format_name)); + qdict_put(options, "driver", qstring_from_str(drv->format_name)); } else if (!drv) { error_setg(errp, "Must specify either driver or file"); ret = -EINVAL; @@ -1537,22 +1647,19 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, assert(!(flags & BDRV_O_PROTOCOL) || !file); /* Open the image */ - ret = bdrv_open_common(bs, file, options, flags, drv, &local_err); + ret = bdrv_open_common(bs, file, options, &local_err); if (ret < 0) { goto fail; } if (file && (bs->file != file)) { - bdrv_unref(file); + bdrv_unref_child(bs, file); file = NULL; } /* If there is a backing file, use it */ if ((flags & BDRV_O_NO_BACKING) == 0) { - QDict *backing_options; - - qdict_extract_subqdict(options, &backing_options, "backing."); - ret = bdrv_open_backing_file(bs, backing_options, &local_err); + ret = bdrv_open_backing_file(bs, options, "backing", &local_err); if (ret < 0) { goto close_and_fail; } @@ -1560,15 +1667,6 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, bdrv_refresh_filename(bs); - /* For snapshot=on, create a temporary qcow2 overlay. bs points to the - * temporary snapshot afterwards. */ - if (snapshot_flags) { - ret = bdrv_append_temp_snapshot(bs, snapshot_flags, &local_err); - if (local_err) { - goto close_and_fail; - } - } - /* Check if any unknown options were used */ if (options && (qdict_size(options) != 0)) { const QDictEntry *entry = qdict_first(options); @@ -1576,9 +1674,9 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, error_setg(errp, "Block protocol '%s' doesn't support the option " "'%s'", drv->format_name, entry->key); } else { - error_setg(errp, "Block format '%s' used by device '%s' doesn't " - "support the option '%s'", drv->format_name, - bdrv_get_device_name(bs), entry->key); + error_setg(errp, + "Block format '%s' does not support the option '%s'", + drv->format_name, entry->key); } ret = -EINVAL; @@ -1600,12 +1698,26 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, QDECREF(options); *pbs = bs; + + /* For snapshot=on, create a temporary qcow2 overlay. bs points to the + * temporary snapshot afterwards. */ + if (snapshot_flags) { + ret = bdrv_append_temp_snapshot(bs, snapshot_flags, snapshot_options, + &local_err); + snapshot_options = NULL; + if (local_err) { + goto close_and_fail; + } + } + return 0; fail: if (file != NULL) { - bdrv_unref(file); + bdrv_unref_child(bs, file); } + QDECREF(snapshot_options); + QDECREF(bs->explicit_options); QDECREF(bs->options); QDECREF(options); bs->options = NULL; @@ -1627,6 +1739,7 @@ close_and_fail: } else { bdrv_unref(bs); } + QDECREF(snapshot_options); QDECREF(options); if (local_err) { error_propagate(errp, local_err); @@ -1635,11 +1748,10 @@ close_and_fail: } int bdrv_open(BlockDriverState **pbs, const char *filename, - const char *reference, QDict *options, int flags, - BlockDriver *drv, Error **errp) + const char *reference, QDict *options, int flags, Error **errp) { return bdrv_open_inherit(pbs, filename, reference, options, flags, NULL, - NULL, drv, errp); + NULL, errp); } typedef struct BlockReopenQueueEntry { @@ -1660,48 +1772,116 @@ typedef struct BlockReopenQueueEntry { * * bs is the BlockDriverState to add to the reopen queue. * + * options contains the changed options for the associated bs + * (the BlockReopenQueue takes ownership) + * * flags contains the open flags for the associated bs * * returns a pointer to bs_queue, which is either the newly allocated * bs_queue, or the existing bs_queue being used. * */ -BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, - BlockDriverState *bs, int flags) +static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, + BlockDriverState *bs, + QDict *options, + int flags, + const BdrvChildRole *role, + QDict *parent_options, + int parent_flags) { assert(bs != NULL); BlockReopenQueueEntry *bs_entry; BdrvChild *child; + QDict *old_options, *explicit_options; if (bs_queue == NULL) { bs_queue = g_new0(BlockReopenQueue, 1); QSIMPLEQ_INIT(bs_queue); } + if (!options) { + options = qdict_new(); + } + + /* + * Precedence of options: + * 1. Explicitly passed in options (highest) + * 2. Set in flags (only for top level) + * 3. Retained from explicitly set options of bs + * 4. Inherited from parent node + * 5. Retained from effective options of bs + */ + + if (!parent_options) { + /* + * Any setting represented by flags is always updated. If the + * corresponding QDict option is set, it takes precedence. Otherwise + * the flag is translated into a QDict option. The old setting of bs is + * not considered. + */ + update_options_from_flags(options, flags); + } + + /* Old explicitly set values (don't overwrite by inherited value) */ + old_options = qdict_clone_shallow(bs->explicit_options); + bdrv_join_options(bs, options, old_options); + QDECREF(old_options); + + explicit_options = qdict_clone_shallow(options); + + /* Inherit from parent node */ + if (parent_options) { + assert(!flags); + role->inherit_options(&flags, options, parent_flags, parent_options); + } + + /* Old values are used for options that aren't set yet */ + old_options = qdict_clone_shallow(bs->options); + bdrv_join_options(bs, options, old_options); + QDECREF(old_options); + /* bdrv_open() masks this flag out */ flags &= ~BDRV_O_PROTOCOL; QLIST_FOREACH(child, &bs->children, next) { - int child_flags; + QDict *new_child_options; + char *child_key_dot; + /* reopen can only change the options of block devices that were + * implicitly created and inherited options. For other (referenced) + * block devices, a syntax like "backing.foo" results in an error. */ if (child->bs->inherits_from != bs) { continue; } - child_flags = child->role->inherit_flags(flags); - bdrv_reopen_queue(bs_queue, child->bs, child_flags); + child_key_dot = g_strdup_printf("%s.", child->name); + qdict_extract_subqdict(options, &new_child_options, child_key_dot); + g_free(child_key_dot); + + bdrv_reopen_queue_child(bs_queue, child->bs, new_child_options, 0, + child->role, options, flags); } bs_entry = g_new0(BlockReopenQueueEntry, 1); QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry); bs_entry->state.bs = bs; + bs_entry->state.options = options; + bs_entry->state.explicit_options = explicit_options; bs_entry->state.flags = flags; return bs_queue; } +BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, + BlockDriverState *bs, + QDict *options, int flags) +{ + return bdrv_reopen_queue_child(bs_queue, bs, options, flags, + NULL, NULL, 0); +} + /* * Reopen multiple BlockDriverStates atomically & transactionally. * @@ -1748,7 +1928,10 @@ cleanup: QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { if (ret && bs_entry->prepared) { bdrv_reopen_abort(&bs_entry->state); + } else if (ret) { + QDECREF(bs_entry->state.explicit_options); } + QDECREF(bs_entry->state.options); g_free(bs_entry); } g_free(bs_queue); @@ -1761,7 +1944,7 @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp) { int ret = -1; Error *local_err = NULL; - BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, bdrv_flags); + BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags); ret = bdrv_reopen_multiple(queue, &local_err); if (local_err != NULL) { @@ -1794,16 +1977,41 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, int ret = -1; Error *local_err = NULL; BlockDriver *drv; + QemuOpts *opts; + const char *value; assert(reopen_state != NULL); assert(reopen_state->bs->drv != NULL); drv = reopen_state->bs->drv; - /* if we are to stay read-only, do not allow permission change - * to r/w */ - if (!(reopen_state->bs->open_flags & BDRV_O_ALLOW_RDWR) && - reopen_state->flags & BDRV_O_RDWR) { - error_setg(errp, "Node '%s' is read only", + /* Process generic block layer options */ + opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, reopen_state->options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto error; + } + + update_flags_from_options(&reopen_state->flags, opts); + + /* node-name and driver must be unchanged. Put them back into the QDict, so + * that they are checked at the end of this function. */ + value = qemu_opt_get(opts, "node-name"); + if (value) { + qdict_put(reopen_state->options, "node-name", qstring_from_str(value)); + } + + value = qemu_opt_get(opts, "driver"); + if (value) { + qdict_put(reopen_state->options, "driver", qstring_from_str(value)); + } + + /* if we are to stay read-only, do not allow permission change + * to r/w */ + if (!(reopen_state->bs->open_flags & BDRV_O_ALLOW_RDWR) && + reopen_state->flags & BDRV_O_RDWR) { + error_setg(errp, "Node '%s' is read only", bdrv_get_device_or_node_name(reopen_state->bs)); goto error; } @@ -1811,8 +2019,7 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, ret = bdrv_flush(reopen_state->bs); if (ret) { - error_set(errp, ERROR_CLASS_GENERIC_ERROR, "Error (%s) flushing drive", - strerror(-ret)); + error_setg_errno(errp, -ret, "Error flushing drive"); goto error; } @@ -1837,9 +2044,30 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, goto error; } + /* Options that are not handled are only okay if they are unchanged + * compared to the old state. It is expected that some options are only + * used for the initial open, but not reopen (e.g. filename) */ + if (qdict_size(reopen_state->options)) { + const QDictEntry *entry = qdict_first(reopen_state->options); + + do { + QString *new_obj = qobject_to_qstring(entry->value); + const char *new = qstring_get_str(new_obj); + const char *old = qdict_get_try_str(reopen_state->bs->options, + entry->key); + + if (!old || strcmp(new, old)) { + error_setg(errp, "Cannot change the option '%s'", entry->key); + ret = -EINVAL; + goto error; + } + } while ((entry = qdict_next(reopen_state->options, entry))); + } + ret = 0; error: + qemu_opts_del(opts); return ret; } @@ -1862,9 +2090,10 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state) } /* set BDS specific flags now */ + QDECREF(reopen_state->bs->explicit_options); + + reopen_state->bs->explicit_options = reopen_state->explicit_options; reopen_state->bs->open_flags = reopen_state->flags; - reopen_state->bs->enable_write_cache = !!(reopen_state->flags & - BDRV_O_CACHE_WB); reopen_state->bs->read_only = !(reopen_state->flags & BDRV_O_RDWR); bdrv_refresh_limits(reopen_state->bs, NULL); @@ -1885,30 +2114,44 @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state) if (drv->bdrv_reopen_abort) { drv->bdrv_reopen_abort(reopen_state); } + + QDECREF(reopen_state->explicit_options); } -void bdrv_close(BlockDriverState *bs) +static void bdrv_close(BlockDriverState *bs) { BdrvAioNotifier *ban, *ban_next; - if (bs->job) { - block_job_cancel_sync(bs->job); + assert(!bs->job); + + /* Disable I/O limits and drain all pending throttled requests */ + if (bs->throttle_state) { + bdrv_io_limits_disable(bs); } - bdrv_drain(bs); /* complete I/O */ + + bdrv_drained_begin(bs); /* complete I/O */ bdrv_flush(bs); bdrv_drain(bs); /* in case flush left pending I/O */ - notifier_list_notify(&bs->close_notifiers, bs); + + bdrv_release_named_dirty_bitmaps(bs); + assert(QLIST_EMPTY(&bs->dirty_bitmaps)); + + if (bs->blk) { + blk_dev_change_media_cb(bs->blk, false); + } if (bs->drv) { BdrvChild *child, *next; bs->drv->bdrv_close(bs); + bs->drv = NULL; - if (bs->backing_hd) { - BlockDriverState *backing_hd = bs->backing_hd; - bdrv_set_backing_hd(bs, NULL); - bdrv_unref(backing_hd); + bdrv_set_backing_hd(bs, NULL); + + if (bs->file != NULL) { + bdrv_unref_child(bs, bs->file); + bs->file = NULL; } QLIST_FOREACH_SAFE(child, &bs->children, next, next) { @@ -1922,7 +2165,6 @@ void bdrv_close(BlockDriverState *bs) g_free(bs->opaque); bs->opaque = NULL; - bs->drv = NULL; bs->copy_on_read = 0; bs->backing_file[0] = '\0'; bs->backing_format[0] = '\0'; @@ -1932,227 +2174,97 @@ void bdrv_close(BlockDriverState *bs) bs->sg = 0; bs->zero_beyond_eof = false; QDECREF(bs->options); + QDECREF(bs->explicit_options); bs->options = NULL; QDECREF(bs->full_open_options); bs->full_open_options = NULL; - - if (bs->file != NULL) { - bdrv_unref(bs->file); - bs->file = NULL; - } - } - - if (bs->blk) { - blk_dev_change_media_cb(bs->blk, false); - } - - /*throttling disk I/O limits*/ - if (bs->io_limits_enabled) { - bdrv_io_limits_disable(bs); } QLIST_FOREACH_SAFE(ban, &bs->aio_notifiers, list, ban_next) { g_free(ban); } QLIST_INIT(&bs->aio_notifiers); + bdrv_drained_end(bs); } void bdrv_close_all(void) { BlockDriverState *bs; + AioContext *aio_context; - QTAILQ_FOREACH(bs, &bdrv_states, device_list) { - AioContext *aio_context = bdrv_get_aio_context(bs); + /* Drop references from requests still in flight, such as canceled block + * jobs whose AIO context has not been polled yet */ + bdrv_drain_all(); - aio_context_acquire(aio_context); - bdrv_close(bs); - aio_context_release(aio_context); - } -} + blk_remove_all_bs(); + blockdev_close_all_bdrv_states(); -/* make a BlockDriverState anonymous by removing from bdrv_state and - * graph_bdrv_state list. - Also, NULL terminate the device_name to prevent double remove */ -void bdrv_make_anon(BlockDriverState *bs) -{ - /* - * Take care to remove bs from bdrv_states only when it's actually - * in it. Note that bs->device_list.tqe_prev is initially null, - * and gets set to non-null by QTAILQ_INSERT_TAIL(). Establish - * the useful invariant "bs in bdrv_states iff bs->tqe_prev" by - * resetting it to null on remove. - */ - if (bs->device_list.tqe_prev) { - QTAILQ_REMOVE(&bdrv_states, bs, device_list); - bs->device_list.tqe_prev = NULL; - } - if (bs->node_name[0] != '\0') { - QTAILQ_REMOVE(&graph_bdrv_states, bs, node_list); - } - bs->node_name[0] = '\0'; -} + /* Cancel all block jobs */ + while (!QTAILQ_EMPTY(&all_bdrv_states)) { + QTAILQ_FOREACH(bs, &all_bdrv_states, bs_list) { + aio_context = bdrv_get_aio_context(bs); -static void bdrv_rebind(BlockDriverState *bs) -{ - if (bs->drv && bs->drv->bdrv_rebind) { - bs->drv->bdrv_rebind(bs); + aio_context_acquire(aio_context); + if (bs->job) { + block_job_cancel_sync(bs->job); + aio_context_release(aio_context); + break; + } + aio_context_release(aio_context); + } + + /* All the remaining BlockDriverStates are referenced directly or + * indirectly from block jobs, so there needs to be at least one BDS + * directly used by a block job */ + assert(bs); } } +/* Fields that need to stay with the top-level BDS */ static void bdrv_move_feature_fields(BlockDriverState *bs_dest, BlockDriverState *bs_src) { /* move some fields that need to stay attached to the device */ - - /* dev info */ - bs_dest->guest_block_size = bs_src->guest_block_size; - bs_dest->copy_on_read = bs_src->copy_on_read; - - bs_dest->enable_write_cache = bs_src->enable_write_cache; - - /* i/o throttled req */ - bs_dest->throttle_state = bs_src->throttle_state, - bs_dest->io_limits_enabled = bs_src->io_limits_enabled; - bs_dest->pending_reqs[0] = bs_src->pending_reqs[0]; - bs_dest->pending_reqs[1] = bs_src->pending_reqs[1]; - bs_dest->throttled_reqs[0] = bs_src->throttled_reqs[0]; - bs_dest->throttled_reqs[1] = bs_src->throttled_reqs[1]; - memcpy(&bs_dest->round_robin, - &bs_src->round_robin, - sizeof(bs_dest->round_robin)); - memcpy(&bs_dest->throttle_timers, - &bs_src->throttle_timers, - sizeof(ThrottleTimers)); - - /* r/w error */ - bs_dest->on_read_error = bs_src->on_read_error; - bs_dest->on_write_error = bs_src->on_write_error; - - /* i/o status */ - bs_dest->iostatus_enabled = bs_src->iostatus_enabled; - bs_dest->iostatus = bs_src->iostatus; - - /* dirty bitmap */ - bs_dest->dirty_bitmaps = bs_src->dirty_bitmaps; - - /* reference count */ - bs_dest->refcnt = bs_src->refcnt; - - /* job */ - bs_dest->job = bs_src->job; - - /* keep the same entry in bdrv_states */ - bs_dest->device_list = bs_src->device_list; - bs_dest->blk = bs_src->blk; - - memcpy(bs_dest->op_blockers, bs_src->op_blockers, - sizeof(bs_dest->op_blockers)); } -/* - * Swap bs contents for two image chains while they are live, - * while keeping required fields on the BlockDriverState that is - * actually attached to a device. - * - * This will modify the BlockDriverState fields, and swap contents - * between bs_new and bs_old. Both bs_new and bs_old are modified. - * - * bs_new must not be attached to a BlockBackend. - * - * This function does not create any image files. - */ -void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old) +static void change_parent_backing_link(BlockDriverState *from, + BlockDriverState *to) { - BlockDriverState tmp; - BdrvChild *child; - - bdrv_drain(bs_new); - bdrv_drain(bs_old); + BdrvChild *c, *next; - /* The code needs to swap the node_name but simply swapping node_list won't - * work so first remove the nodes from the graph list, do the swap then - * insert them back if needed. - */ - if (bs_new->node_name[0] != '\0') { - QTAILQ_REMOVE(&graph_bdrv_states, bs_new, node_list); - } - if (bs_old->node_name[0] != '\0') { - QTAILQ_REMOVE(&graph_bdrv_states, bs_old, node_list); + if (from->blk) { + /* FIXME We bypass blk_set_bs(), so we need to make these updates + * manually. The root problem is not in this change function, but the + * existence of BlockDriverState.blk. */ + to->blk = from->blk; + from->blk = NULL; } - /* If the BlockDriverState is part of a throttling group acquire - * its lock since we're going to mess with the protected fields. - * Otherwise there's no need to worry since no one else can touch - * them. */ - if (bs_old->throttle_state) { - throttle_group_lock(bs_old); + QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { + assert(c->role != &child_backing); + c->bs = to; + QLIST_REMOVE(c, next_parent); + QLIST_INSERT_HEAD(&to->parents, c, next_parent); + bdrv_ref(to); + bdrv_unref(from); } +} - /* bs_new must be unattached and shouldn't have anything fancy enabled */ - assert(!bs_new->blk); - assert(QLIST_EMPTY(&bs_new->dirty_bitmaps)); - assert(bs_new->job == NULL); - assert(bs_new->io_limits_enabled == false); - assert(bs_new->throttle_state == NULL); - assert(!throttle_timers_are_initialized(&bs_new->throttle_timers)); - - tmp = *bs_new; - *bs_new = *bs_old; - *bs_old = tmp; +static void swap_feature_fields(BlockDriverState *bs_top, + BlockDriverState *bs_new) +{ + BlockDriverState tmp; - /* there are some fields that should not be swapped, move them back */ - bdrv_move_feature_fields(&tmp, bs_old); - bdrv_move_feature_fields(bs_old, bs_new); + bdrv_move_feature_fields(&tmp, bs_top); + bdrv_move_feature_fields(bs_top, bs_new); bdrv_move_feature_fields(bs_new, &tmp); - /* bs_new must remain unattached */ - assert(!bs_new->blk); - - /* Check a few fields that should remain attached to the device */ - assert(bs_new->job == NULL); - assert(bs_new->io_limits_enabled == false); - assert(bs_new->throttle_state == NULL); - assert(!throttle_timers_are_initialized(&bs_new->throttle_timers)); - - /* Release the ThrottleGroup lock */ - if (bs_old->throttle_state) { - throttle_group_unlock(bs_old); - } - - /* insert the nodes back into the graph node list if needed */ - if (bs_new->node_name[0] != '\0') { - QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs_new, node_list); - } - if (bs_old->node_name[0] != '\0') { - QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs_old, node_list); - } - - /* - * Update lh_first.le_prev for non-empty lists. - * - * The head of the op blocker list doesn't change because it is moved back - * in bdrv_move_feature_fields(). - */ - assert(QLIST_EMPTY(&bs_old->tracked_requests)); - assert(QLIST_EMPTY(&bs_new->tracked_requests)); - - QLIST_FIX_HEAD_PTR(&bs_new->children, next); - QLIST_FIX_HEAD_PTR(&bs_old->children, next); - - /* Update references in bs->opaque and children */ - QLIST_FOREACH(child, &bs_old->children, next) { - if (child->bs->inherits_from == bs_new) { - child->bs->inherits_from = bs_old; - } - } - QLIST_FOREACH(child, &bs_new->children, next) { - if (child->bs->inherits_from == bs_old) { - child->bs->inherits_from = bs_new; - } + assert(!bs_new->throttle_state); + if (bs_top->throttle_state) { + assert(bs_top->io_limits_enabled); + bdrv_io_limits_enable(bs_new, throttle_group_get_name(bs_top)); + bdrv_io_limits_disable(bs_top); } - - bdrv_rebind(bs_new); - bdrv_rebind(bs_old); } /* @@ -2165,14 +2277,59 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old) * bs_new must not be attached to a BlockBackend. * * This function does not create any image files. + * + * bdrv_append() takes ownership of a bs_new reference and unrefs it because + * that's what the callers commonly need. bs_new will be referenced by the old + * parents of bs_top after bdrv_append() returns. If the caller needs to keep a + * reference of its own, it must call bdrv_ref(). */ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top) { - bdrv_swap(bs_new, bs_top); + assert(!bdrv_requests_pending(bs_top)); + assert(!bdrv_requests_pending(bs_new)); + + bdrv_ref(bs_top); + change_parent_backing_link(bs_top, bs_new); + + /* Some fields always stay on top of the backing file chain */ + swap_feature_fields(bs_top, bs_new); - /* The contents of 'tmp' will become bs_top, as we are - * swapping bs_new and bs_top contents. */ - bdrv_set_backing_hd(bs_top, bs_new); + bdrv_set_backing_hd(bs_new, bs_top); + bdrv_unref(bs_top); + + /* bs_new is now referenced by its new parents, we don't need the + * additional reference any more. */ + bdrv_unref(bs_new); +} + +void bdrv_replace_in_backing_chain(BlockDriverState *old, BlockDriverState *new) +{ + assert(!bdrv_requests_pending(old)); + assert(!bdrv_requests_pending(new)); + + bdrv_ref(old); + + if (old->blk) { + /* As long as these fields aren't in BlockBackend, but in the top-level + * BlockDriverState, it's not possible for a BDS to have two BBs. + * + * We really want to copy the fields from old to new, but we go for a + * swap instead so that pointers aren't duplicated and cause trouble. + * (Also, bdrv_swap() used to do the same.) */ + assert(!new->blk); + swap_feature_fields(old, new); + } + change_parent_backing_link(old, new); + + /* Change backing files if a previously independent node is added to the + * chain. For active commit, we replace top by its own (indirect) backing + * file and don't do anything here so we don't build a loop. */ + if (new->backing == NULL && !bdrv_chain_contains(backing_bs(old), new)) { + bdrv_set_backing_hd(new, backing_bs(old)); + bdrv_set_backing_hd(old, NULL); + } + + bdrv_unref(old); } static void bdrv_delete(BlockDriverState *bs) @@ -2180,12 +2337,14 @@ static void bdrv_delete(BlockDriverState *bs) assert(!bs->job); assert(bdrv_op_blocker_is_empty(bs)); assert(!bs->refcnt); - assert(QLIST_EMPTY(&bs->dirty_bitmaps)); bdrv_close(bs); /* remove from list, if necessary */ - bdrv_make_anon(bs); + if (bs->node_name[0] != '\0') { + QTAILQ_REMOVE(&graph_bdrv_states, bs, node_list); + } + QTAILQ_REMOVE(&all_bdrv_states, bs, bs_list); g_free(bs); } @@ -2224,20 +2383,20 @@ int bdrv_commit(BlockDriverState *bs) if (!drv) return -ENOMEDIUM; - if (!bs->backing_hd) { + if (!bs->backing) { return -ENOTSUP; } if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, NULL) || - bdrv_op_is_blocked(bs->backing_hd, BLOCK_OP_TYPE_COMMIT_TARGET, NULL)) { + bdrv_op_is_blocked(bs->backing->bs, BLOCK_OP_TYPE_COMMIT_TARGET, NULL)) { return -EBUSY; } - ro = bs->backing_hd->read_only; - open_flags = bs->backing_hd->open_flags; + ro = bs->backing->bs->read_only; + open_flags = bs->backing->bs->open_flags; if (ro) { - if (bdrv_reopen(bs->backing_hd, open_flags | BDRV_O_RDWR, NULL)) { + if (bdrv_reopen(bs->backing->bs, open_flags | BDRV_O_RDWR, NULL)) { return -EACCES; } } @@ -2248,7 +2407,7 @@ int bdrv_commit(BlockDriverState *bs) goto ro_cleanup; } - backing_length = bdrv_getlength(bs->backing_hd); + backing_length = bdrv_getlength(bs->backing->bs); if (backing_length < 0) { ret = backing_length; goto ro_cleanup; @@ -2258,7 +2417,7 @@ int bdrv_commit(BlockDriverState *bs) * grow the backing file image if possible. If not possible, * we must return an error */ if (length > backing_length) { - ret = bdrv_truncate(bs->backing_hd, length); + ret = bdrv_truncate(bs->backing->bs, length); if (ret < 0) { goto ro_cleanup; } @@ -2267,7 +2426,7 @@ int bdrv_commit(BlockDriverState *bs) total_sectors = length >> BDRV_SECTOR_BITS; /* qemu_try_blockalign() for bs will choose an alignment that works for - * bs->backing_hd as well, so no need to compare the alignment manually. */ + * bs->backing->bs as well, so no need to compare the alignment manually. */ buf = qemu_try_blockalign(bs, COMMIT_BUF_SECTORS * BDRV_SECTOR_SIZE); if (buf == NULL) { ret = -ENOMEM; @@ -2285,7 +2444,7 @@ int bdrv_commit(BlockDriverState *bs) goto ro_cleanup; } - ret = bdrv_write(bs->backing_hd, sector, buf, n); + ret = bdrv_write(bs->backing->bs, sector, buf, n); if (ret < 0) { goto ro_cleanup; } @@ -2304,8 +2463,8 @@ int bdrv_commit(BlockDriverState *bs) * Make sure all data we wrote to the backing device is actually * stable on disk. */ - if (bs->backing_hd) { - bdrv_flush(bs->backing_hd); + if (bs->backing) { + bdrv_flush(bs->backing->bs); } ret = 0; @@ -2314,32 +2473,12 @@ ro_cleanup: if (ro) { /* ignoring error return here */ - bdrv_reopen(bs->backing_hd, open_flags & ~BDRV_O_RDWR, NULL); + bdrv_reopen(bs->backing->bs, open_flags & ~BDRV_O_RDWR, NULL); } return ret; } -int bdrv_commit_all(void) -{ - BlockDriverState *bs; - - QTAILQ_FOREACH(bs, &bdrv_states, device_list) { - AioContext *aio_context = bdrv_get_aio_context(bs); - - aio_context_acquire(aio_context); - if (bs->drv && bs->backing_hd) { - int ret = bdrv_commit(bs); - if (ret < 0) { - aio_context_release(aio_context); - return ret; - } - } - aio_context_release(aio_context); - } - return 0; -} - /* * Return values: * 0 - success @@ -2385,8 +2524,8 @@ int bdrv_change_backing_file(BlockDriverState *bs, BlockDriverState *bdrv_find_overlay(BlockDriverState *active, BlockDriverState *bs) { - while (active && bs != active->backing_hd) { - active = active->backing_hd; + while (active && bs != backing_bs(active)) { + active = backing_bs(active); } return active; @@ -2398,12 +2537,6 @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs) return bdrv_find_overlay(bs, NULL); } -typedef struct BlkIntermediateStates { - BlockDriverState *bs; - QSIMPLEQ_ENTRY(BlkIntermediateStates) entry; -} BlkIntermediateStates; - - /* * Drops images above 'base' up to and including 'top', and sets the image * above 'top' to have base as its backing file. @@ -2436,15 +2569,9 @@ typedef struct BlkIntermediateStates { int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top, BlockDriverState *base, const char *backing_file_str) { - BlockDriverState *intermediate; - BlockDriverState *base_bs = NULL; BlockDriverState *new_top_bs = NULL; - BlkIntermediateStates *intermediate_state, *next; int ret = -EIO; - QSIMPLEQ_HEAD(states_to_delete, BlkIntermediateStates) states_to_delete; - QSIMPLEQ_INIT(&states_to_delete); - if (!top->drv || !base->drv) { goto exit; } @@ -2456,55 +2583,29 @@ int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top, goto exit; } - /* special case of new_top_bs->backing_hd already pointing to base - nothing + /* special case of new_top_bs->backing->bs already pointing to base - nothing * to do, no intermediate images */ - if (new_top_bs->backing_hd == base) { + if (backing_bs(new_top_bs) == base) { ret = 0; goto exit; } - intermediate = top; - - /* now we will go down through the list, and add each BDS we find - * into our deletion queue, until we hit the 'base' - */ - while (intermediate) { - intermediate_state = g_new0(BlkIntermediateStates, 1); - intermediate_state->bs = intermediate; - QSIMPLEQ_INSERT_TAIL(&states_to_delete, intermediate_state, entry); - - if (intermediate->backing_hd == base) { - base_bs = intermediate->backing_hd; - break; - } - intermediate = intermediate->backing_hd; - } - if (base_bs == NULL) { - /* something went wrong, we did not end at the base. safely - * unravel everything, and exit with error */ + /* Make sure that base is in the backing chain of top */ + if (!bdrv_chain_contains(top, base)) { goto exit; } /* success - we can delete the intermediate states, and link top->base */ - backing_file_str = backing_file_str ? backing_file_str : base_bs->filename; + backing_file_str = backing_file_str ? backing_file_str : base->filename; ret = bdrv_change_backing_file(new_top_bs, backing_file_str, - base_bs->drv ? base_bs->drv->format_name : ""); + base->drv ? base->drv->format_name : ""); if (ret) { goto exit; } - bdrv_set_backing_hd(new_top_bs, base_bs); + bdrv_set_backing_hd(new_top_bs, base); - QSIMPLEQ_FOREACH_SAFE(intermediate_state, &states_to_delete, entry, next) { - /* so that bdrv_close() does not recursively close the chain */ - bdrv_set_backing_hd(intermediate_state->bs, NULL); - bdrv_unref(intermediate_state->bs); - } ret = 0; - exit: - QSIMPLEQ_FOREACH_SAFE(intermediate_state, &states_to_delete, entry, next) { - g_free(intermediate_state); - } return ret; } @@ -2547,7 +2648,7 @@ int64_t bdrv_get_allocated_file_size(BlockDriverState *bs) return drv->bdrv_get_allocated_file_size(bs); } if (bs->file) { - return bdrv_get_allocated_file_size(bs->file); + return bdrv_get_allocated_file_size(bs->file->bs); } return -ENOTSUP; } @@ -2591,82 +2692,6 @@ void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr) *nb_sectors_ptr = nb_sectors < 0 ? 0 : nb_sectors; } -void bdrv_set_on_error(BlockDriverState *bs, BlockdevOnError on_read_error, - BlockdevOnError on_write_error) -{ - bs->on_read_error = on_read_error; - bs->on_write_error = on_write_error; -} - -BlockdevOnError bdrv_get_on_error(BlockDriverState *bs, bool is_read) -{ - return is_read ? bs->on_read_error : bs->on_write_error; -} - -BlockErrorAction bdrv_get_error_action(BlockDriverState *bs, bool is_read, int error) -{ - BlockdevOnError on_err = is_read ? bs->on_read_error : bs->on_write_error; - - switch (on_err) { - case BLOCKDEV_ON_ERROR_ENOSPC: - return (error == ENOSPC) ? - BLOCK_ERROR_ACTION_STOP : BLOCK_ERROR_ACTION_REPORT; - case BLOCKDEV_ON_ERROR_STOP: - return BLOCK_ERROR_ACTION_STOP; - case BLOCKDEV_ON_ERROR_REPORT: - return BLOCK_ERROR_ACTION_REPORT; - case BLOCKDEV_ON_ERROR_IGNORE: - return BLOCK_ERROR_ACTION_IGNORE; - default: - abort(); - } -} - -static void send_qmp_error_event(BlockDriverState *bs, - BlockErrorAction action, - bool is_read, int error) -{ - IoOperationType optype; - - optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE; - qapi_event_send_block_io_error(bdrv_get_device_name(bs), optype, action, - bdrv_iostatus_is_enabled(bs), - error == ENOSPC, strerror(error), - &error_abort); -} - -/* This is done by device models because, while the block layer knows - * about the error, it does not know whether an operation comes from - * the device or the block layer (from a job, for example). - */ -void bdrv_error_action(BlockDriverState *bs, BlockErrorAction action, - bool is_read, int error) -{ - assert(error >= 0); - - if (action == BLOCK_ERROR_ACTION_STOP) { - /* First set the iostatus, so that "info block" returns an iostatus - * that matches the events raised so far (an additional error iostatus - * is fine, but not a lost one). - */ - bdrv_iostatus_set_err(bs, error); - - /* Then raise the request to stop the VM and the event. - * qemu_system_vmstop_request_prepare has two effects. First, - * it ensures that the STOP event always comes after the - * BLOCK_IO_ERROR event. Second, it ensures that even if management - * can observe the STOP event and do a "cont" before the STOP - * event is issued, the VM will not stop. In this case, vm_start() - * also ensures that the STOP/RESUME pair of events is emitted. - */ - qemu_system_vmstop_request_prepare(); - send_qmp_error_event(bs, action, is_read, error); - qemu_system_vmstop_request(RUN_STATE_IO_ERROR); - } else { - send_qmp_error_event(bs, action, is_read, error); - } -} - int bdrv_is_read_only(BlockDriverState *bs) { return bs->read_only; @@ -2677,44 +2702,29 @@ int bdrv_is_sg(BlockDriverState *bs) return bs->sg; } -int bdrv_enable_write_cache(BlockDriverState *bs) -{ - return bs->enable_write_cache; -} - -void bdrv_set_enable_write_cache(BlockDriverState *bs, bool wce) -{ - bs->enable_write_cache = wce; - - /* so a reopen() will preserve wce */ - if (wce) { - bs->open_flags |= BDRV_O_CACHE_WB; - } else { - bs->open_flags &= ~BDRV_O_CACHE_WB; - } -} - int bdrv_is_encrypted(BlockDriverState *bs) { - if (bs->backing_hd && bs->backing_hd->encrypted) + if (bs->backing && bs->backing->bs->encrypted) { return 1; + } return bs->encrypted; } int bdrv_key_required(BlockDriverState *bs) { - BlockDriverState *backing_hd = bs->backing_hd; + BdrvChild *backing = bs->backing; - if (backing_hd && backing_hd->encrypted && !backing_hd->valid_key) + if (backing && backing->bs->encrypted && !backing->bs->valid_key) { return 1; + } return (bs->encrypted && !bs->valid_key); } int bdrv_set_key(BlockDriverState *bs, const char *key) { int ret; - if (bs->backing_hd && bs->backing_hd->encrypted) { - ret = bdrv_set_key(bs->backing_hd, key); + if (bs->backing && bs->backing->bs->encrypted) { + ret = bdrv_set_key(bs->backing->bs, key); if (ret < 0) return ret; if (!bs->encrypted) @@ -2833,7 +2843,7 @@ BlockDeviceInfoList *bdrv_named_nodes_list(Error **errp) list = NULL; QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) { - BlockDeviceInfo *info = bdrv_block_device_info(bs, errp); + BlockDeviceInfo *info = bdrv_block_device_info(NULL, bs, errp); if (!info) { qapi_free_BlockDeviceInfoList(list); return NULL; @@ -2858,7 +2868,12 @@ BlockDriverState *bdrv_lookup_bs(const char *device, blk = blk_by_name(device); if (blk) { - return blk_bs(blk); + bs = blk_bs(blk); + if (!bs) { + error_setg(errp, "Device '%s' has no medium", device); + } + + return bs; } } @@ -2881,7 +2896,7 @@ BlockDriverState *bdrv_lookup_bs(const char *device, bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base) { while (top && top != base) { - top = top->backing_hd; + top = backing_bs(top); } return top != NULL; @@ -2895,12 +2910,23 @@ BlockDriverState *bdrv_next_node(BlockDriverState *bs) return QTAILQ_NEXT(bs, node_list); } +/* Iterates over all top-level BlockDriverStates, i.e. BDSs that are owned by + * the monitor or attached to a BlockBackend */ BlockDriverState *bdrv_next(BlockDriverState *bs) { - if (!bs) { - return QTAILQ_FIRST(&bdrv_states); + if (!bs || bs->blk) { + bs = blk_next_root_bs(bs); + if (bs) { + return bs; + } } - return QTAILQ_NEXT(bs, device_list); + + /* Ignore all BDSs that are attached to a BlockBackend here; they have been + * handled by the above block already */ + do { + bs = bdrv_next_monitor_owned(bs); + } while (bs && bs->blk); + return bs; } const char *bdrv_get_node_name(const BlockDriverState *bs) @@ -2939,7 +2965,7 @@ int bdrv_has_zero_init(BlockDriverState *bs) /* If BS is a copy on write image, it is initialized to the contents of the base image, which may not be zeroes. */ - if (bs->backing_hd) { + if (bs->backing) { return 0; } if (bs->drv->bdrv_has_zero_init) { @@ -2954,7 +2980,7 @@ bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs) { BlockDriverInfo bdi; - if (bs->backing_hd) { + if (bs->backing) { return false; } @@ -2969,7 +2995,7 @@ bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs) { BlockDriverInfo bdi; - if (bs->backing_hd || !(bs->open_flags & BDRV_O_UNMAP)) { + if (bs->backing || !(bs->open_flags & BDRV_O_UNMAP)) { return false; } @@ -2982,7 +3008,7 @@ bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs) const char *bdrv_get_encrypted_filename(BlockDriverState *bs) { - if (bs->backing_hd && bs->backing_hd->encrypted) + if (bs->backing && bs->backing->bs->encrypted) return bs->backing_file; else if (bs->encrypted) return bs->filename; @@ -3016,7 +3042,7 @@ ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs) return NULL; } -void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event) +void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event) { if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) { return; @@ -3029,7 +3055,7 @@ int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, const char *tag) { while (bs && bs->drv && !bs->drv->bdrv_debug_breakpoint) { - bs = bs->file; + bs = bs->file ? bs->file->bs : NULL; } if (bs && bs->drv && bs->drv->bdrv_debug_breakpoint) { @@ -3042,7 +3068,7 @@ int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag) { while (bs && bs->drv && !bs->drv->bdrv_debug_remove_breakpoint) { - bs = bs->file; + bs = bs->file ? bs->file->bs : NULL; } if (bs && bs->drv && bs->drv->bdrv_debug_remove_breakpoint) { @@ -3055,7 +3081,7 @@ int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag) int bdrv_debug_resume(BlockDriverState *bs, const char *tag) { while (bs && (!bs->drv || !bs->drv->bdrv_debug_resume)) { - bs = bs->file; + bs = bs->file ? bs->file->bs : NULL; } if (bs && bs->drv && bs->drv->bdrv_debug_resume) { @@ -3068,7 +3094,7 @@ int bdrv_debug_resume(BlockDriverState *bs, const char *tag) bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag) { while (bs && bs->drv && !bs->drv->bdrv_debug_is_suspended) { - bs = bs->file; + bs = bs->file ? bs->file->bs : NULL; } if (bs && bs->drv && bs->drv->bdrv_debug_is_suspended) { @@ -3107,13 +3133,13 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, is_protocol = path_has_protocol(backing_file); - for (curr_bs = bs; curr_bs->backing_hd; curr_bs = curr_bs->backing_hd) { + for (curr_bs = bs; curr_bs->backing; curr_bs = curr_bs->backing->bs) { /* If either of the filename paths is actually a protocol, then * compare unmodified paths; otherwise make paths relative */ if (is_protocol || path_has_protocol(curr_bs->backing_file)) { if (strcmp(backing_file, curr_bs->backing_file) == 0) { - retval = curr_bs->backing_hd; + retval = curr_bs->backing->bs; break; } } else { @@ -3137,7 +3163,7 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, } if (strcmp(backing_file_full, filename_full) == 0) { - retval = curr_bs->backing_hd; + retval = curr_bs->backing->bs; break; } } @@ -3155,11 +3181,11 @@ int bdrv_get_backing_file_depth(BlockDriverState *bs) return 0; } - if (!bs->backing_hd) { + if (!bs->backing) { return 0; } - return 1 + bdrv_get_backing_file_depth(bs->backing_hd); + return 1 + bdrv_get_backing_file_depth(bs->backing->bs); } void bdrv_init(void) @@ -3182,23 +3208,25 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) return; } - if (!(bs->open_flags & BDRV_O_INCOMING)) { + if (!(bs->open_flags & BDRV_O_INACTIVE)) { return; } - bs->open_flags &= ~BDRV_O_INCOMING; + bs->open_flags &= ~BDRV_O_INACTIVE; if (bs->drv->bdrv_invalidate_cache) { bs->drv->bdrv_invalidate_cache(bs, &local_err); } else if (bs->file) { - bdrv_invalidate_cache(bs->file, &local_err); + bdrv_invalidate_cache(bs->file->bs, &local_err); } if (local_err) { + bs->open_flags |= BDRV_O_INACTIVE; error_propagate(errp, local_err); return; } ret = refresh_total_sectors(bs, bs->total_sectors); if (ret < 0) { + bs->open_flags |= BDRV_O_INACTIVE; error_setg_errno(errp, -ret, "Could not refresh total sector count"); return; } @@ -3206,10 +3234,10 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) void bdrv_invalidate_cache_all(Error **errp) { - BlockDriverState *bs; + BlockDriverState *bs = NULL; Error *local_err = NULL; - QTAILQ_FOREACH(bs, &bdrv_states, device_list) { + while ((bs = bdrv_next(bs)) != NULL) { AioContext *aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); @@ -3222,21 +3250,63 @@ void bdrv_invalidate_cache_all(Error **errp) } } +static int bdrv_inactivate(BlockDriverState *bs) +{ + int ret; + + if (bs->drv->bdrv_inactivate) { + ret = bs->drv->bdrv_inactivate(bs); + if (ret < 0) { + return ret; + } + } + + bs->open_flags |= BDRV_O_INACTIVE; + return 0; +} + +int bdrv_inactivate_all(void) +{ + BlockDriverState *bs = NULL; + int ret; + + while ((bs = bdrv_next(bs)) != NULL) { + AioContext *aio_context = bdrv_get_aio_context(bs); + + aio_context_acquire(aio_context); + ret = bdrv_inactivate(bs); + aio_context_release(aio_context); + if (ret < 0) { + return ret; + } + } + + return 0; +} + /**************************************************************/ /* removable device support */ /** * Return TRUE if the media is present */ -int bdrv_is_inserted(BlockDriverState *bs) +bool bdrv_is_inserted(BlockDriverState *bs) { BlockDriver *drv = bs->drv; + BdrvChild *child; - if (!drv) - return 0; - if (!drv->bdrv_is_inserted) - return 1; - return drv->bdrv_is_inserted(bs); + if (!drv) { + return false; + } + if (drv->bdrv_is_inserted) { + return drv->bdrv_is_inserted(bs); + } + QLIST_FOREACH(child, &bs->children, next) { + if (!bdrv_is_inserted(child->bs)) { + return false; + } + } + return true; } /** @@ -3287,317 +3357,6 @@ void bdrv_lock_medium(BlockDriverState *bs, bool locked) } } -void bdrv_set_guest_block_size(BlockDriverState *bs, int align) -{ - bs->guest_block_size = align; -} - -BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name) -{ - BdrvDirtyBitmap *bm; - - assert(name); - QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { - if (bm->name && !strcmp(name, bm->name)) { - return bm; - } - } - return NULL; -} - -void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap) -{ - assert(!bdrv_dirty_bitmap_frozen(bitmap)); - g_free(bitmap->name); - bitmap->name = NULL; -} - -BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, - uint32_t granularity, - const char *name, - Error **errp) -{ - int64_t bitmap_size; - BdrvDirtyBitmap *bitmap; - uint32_t sector_granularity; - - assert((granularity & (granularity - 1)) == 0); - - if (name && bdrv_find_dirty_bitmap(bs, name)) { - error_setg(errp, "Bitmap already exists: %s", name); - return NULL; - } - sector_granularity = granularity >> BDRV_SECTOR_BITS; - assert(sector_granularity); - bitmap_size = bdrv_nb_sectors(bs); - if (bitmap_size < 0) { - error_setg_errno(errp, -bitmap_size, "could not get length of device"); - errno = -bitmap_size; - return NULL; - } - bitmap = g_new0(BdrvDirtyBitmap, 1); - bitmap->bitmap = hbitmap_alloc(bitmap_size, ctz32(sector_granularity)); - bitmap->size = bitmap_size; - bitmap->name = g_strdup(name); - bitmap->disabled = false; - QLIST_INSERT_HEAD(&bs->dirty_bitmaps, bitmap, list); - return bitmap; -} - -bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap) -{ - return bitmap->successor; -} - -bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) -{ - return !(bitmap->disabled || bitmap->successor); -} - -DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) -{ - if (bdrv_dirty_bitmap_frozen(bitmap)) { - return DIRTY_BITMAP_STATUS_FROZEN; - } else if (!bdrv_dirty_bitmap_enabled(bitmap)) { - return DIRTY_BITMAP_STATUS_DISABLED; - } else { - return DIRTY_BITMAP_STATUS_ACTIVE; - } -} - -/** - * Create a successor bitmap destined to replace this bitmap after an operation. - * Requires that the bitmap is not frozen and has no successor. - */ -int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, - BdrvDirtyBitmap *bitmap, Error **errp) -{ - uint64_t granularity; - BdrvDirtyBitmap *child; - - if (bdrv_dirty_bitmap_frozen(bitmap)) { - error_setg(errp, "Cannot create a successor for a bitmap that is " - "currently frozen"); - return -1; - } - assert(!bitmap->successor); - - /* Create an anonymous successor */ - granularity = bdrv_dirty_bitmap_granularity(bitmap); - child = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp); - if (!child) { - return -1; - } - - /* Successor will be on or off based on our current state. */ - child->disabled = bitmap->disabled; - - /* Install the successor and freeze the parent */ - bitmap->successor = child; - return 0; -} - -/** - * For a bitmap with a successor, yield our name to the successor, - * delete the old bitmap, and return a handle to the new bitmap. - */ -BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, - BdrvDirtyBitmap *bitmap, - Error **errp) -{ - char *name; - BdrvDirtyBitmap *successor = bitmap->successor; - - if (successor == NULL) { - error_setg(errp, "Cannot relinquish control if " - "there's no successor present"); - return NULL; - } - - name = bitmap->name; - bitmap->name = NULL; - successor->name = name; - bitmap->successor = NULL; - bdrv_release_dirty_bitmap(bs, bitmap); - - return successor; -} - -/** - * 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. - */ -BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, - BdrvDirtyBitmap *parent, - Error **errp) -{ - BdrvDirtyBitmap *successor = parent->successor; - - if (!successor) { - error_setg(errp, "Cannot reclaim a successor when none is present"); - return NULL; - } - - if (!hbitmap_merge(parent->bitmap, successor->bitmap)) { - error_setg(errp, "Merging of parent and successor bitmap failed"); - return NULL; - } - bdrv_release_dirty_bitmap(bs, successor); - parent->successor = NULL; - - return parent; -} - -/** - * Truncates _all_ bitmaps attached to a BDS. - */ -static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs) -{ - BdrvDirtyBitmap *bitmap; - uint64_t size = bdrv_nb_sectors(bs); - - QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { - assert(!bdrv_dirty_bitmap_frozen(bitmap)); - hbitmap_truncate(bitmap->bitmap, size); - bitmap->size = size; - } -} - -void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) -{ - BdrvDirtyBitmap *bm, *next; - QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { - if (bm == bitmap) { - assert(!bdrv_dirty_bitmap_frozen(bm)); - QLIST_REMOVE(bitmap, list); - hbitmap_free(bitmap->bitmap); - g_free(bitmap->name); - g_free(bitmap); - return; - } - } -} - -void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) -{ - assert(!bdrv_dirty_bitmap_frozen(bitmap)); - bitmap->disabled = true; -} - -void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap) -{ - assert(!bdrv_dirty_bitmap_frozen(bitmap)); - bitmap->disabled = false; -} - -BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) -{ - BdrvDirtyBitmap *bm; - BlockDirtyInfoList *list = NULL; - BlockDirtyInfoList **plist = &list; - - QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { - BlockDirtyInfo *info = g_new0(BlockDirtyInfo, 1); - BlockDirtyInfoList *entry = g_new0(BlockDirtyInfoList, 1); - info->count = bdrv_get_dirty_count(bm); - info->granularity = bdrv_dirty_bitmap_granularity(bm); - info->has_name = !!bm->name; - info->name = g_strdup(bm->name); - info->status = bdrv_dirty_bitmap_status(bm); - entry->value = info; - *plist = entry; - plist = &entry->next; - } - - return list; -} - -int bdrv_get_dirty(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, int64_t sector) -{ - if (bitmap) { - return hbitmap_get(bitmap->bitmap, sector); - } else { - return 0; - } -} - -/** - * Chooses a default granularity based on the existing cluster size, - * but clamped between [4K, 64K]. Defaults to 64K in the case that there - * is no cluster size information available. - */ -uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs) -{ - BlockDriverInfo bdi; - uint32_t granularity; - - if (bdrv_get_info(bs, &bdi) >= 0 && bdi.cluster_size > 0) { - granularity = MAX(4096, bdi.cluster_size); - granularity = MIN(65536, granularity); - } else { - granularity = 65536; - } - - return granularity; -} - -uint32_t bdrv_dirty_bitmap_granularity(BdrvDirtyBitmap *bitmap) -{ - return BDRV_SECTOR_SIZE << hbitmap_granularity(bitmap->bitmap); -} - -void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, HBitmapIter *hbi) -{ - hbitmap_iter_init(hbi, bitmap->bitmap, 0); -} - -void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, - int64_t cur_sector, int nr_sectors) -{ - assert(bdrv_dirty_bitmap_enabled(bitmap)); - hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors); -} - -void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, - int64_t cur_sector, int nr_sectors) -{ - assert(bdrv_dirty_bitmap_enabled(bitmap)); - hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors); -} - -void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap) -{ - assert(bdrv_dirty_bitmap_enabled(bitmap)); - hbitmap_reset_all(bitmap->bitmap); -} - -void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, - int nr_sectors) -{ - BdrvDirtyBitmap *bitmap; - QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { - if (!bdrv_dirty_bitmap_enabled(bitmap)) { - continue; - } - hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors); - } -} - -/** - * Advance an HBitmapIter to an arbitrary offset. - */ -void bdrv_set_dirty_iter(HBitmapIter *hbi, int64_t offset) -{ - assert(hbi->hb); - hbitmap_iter_init(hbi, hbi->hb, offset); -} - -int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap) -{ - return hbitmap_count(bitmap->bitmap); -} - /* Get a reference to bs */ void bdrv_ref(BlockDriverState *bs) { @@ -3630,9 +3389,9 @@ bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp) if (!QLIST_EMPTY(&bs->op_blockers[op])) { blocker = QLIST_FIRST(&bs->op_blockers[op]); if (errp) { - error_setg(errp, "Node '%s' is busy: %s", - bdrv_get_device_or_node_name(bs), - error_get_pretty(blocker->reason)); + *errp = error_copy(blocker->reason); + error_prepend(errp, "Node '%s' is busy: ", + bdrv_get_device_or_node_name(bs)); } return true; } @@ -3689,46 +3448,6 @@ bool bdrv_op_blocker_is_empty(BlockDriverState *bs) return true; } -void bdrv_iostatus_enable(BlockDriverState *bs) -{ - bs->iostatus_enabled = true; - bs->iostatus = BLOCK_DEVICE_IO_STATUS_OK; -} - -/* The I/O status is only enabled if the drive explicitly - * enables it _and_ the VM is configured to stop on errors */ -bool bdrv_iostatus_is_enabled(const BlockDriverState *bs) -{ - return (bs->iostatus_enabled && - (bs->on_write_error == BLOCKDEV_ON_ERROR_ENOSPC || - bs->on_write_error == BLOCKDEV_ON_ERROR_STOP || - bs->on_read_error == BLOCKDEV_ON_ERROR_STOP)); -} - -void bdrv_iostatus_disable(BlockDriverState *bs) -{ - bs->iostatus_enabled = false; -} - -void bdrv_iostatus_reset(BlockDriverState *bs) -{ - if (bdrv_iostatus_is_enabled(bs)) { - bs->iostatus = BLOCK_DEVICE_IO_STATUS_OK; - if (bs->job) { - block_job_iostatus_reset(bs->job); - } - } -} - -void bdrv_iostatus_set_err(BlockDriverState *bs, int error) -{ - assert(bdrv_iostatus_is_enabled(bs)); - if (bs->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { - bs->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE : - BLOCK_DEVICE_IO_STATUS_FAILED; - } -} - void bdrv_img_create(const char *filename, const char *fmt, const char *base_filename, const char *base_fmt, char *options, uint64_t img_size, int flags, @@ -3739,7 +3458,6 @@ void bdrv_img_create(const char *filename, const char *fmt, const char *backing_fmt, *backing_file; int64_t size; BlockDriver *drv, *proto_drv; - BlockDriver *backing_drv = NULL; Error *local_err = NULL; int ret = 0; @@ -3813,14 +3531,6 @@ void bdrv_img_create(const char *filename, const char *fmt, } backing_fmt = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT); - if (backing_fmt) { - backing_drv = bdrv_find_format(backing_fmt); - if (!backing_drv) { - error_setg(errp, "Unknown backing file format '%s'", - backing_fmt); - goto out; - } - } // The size for the image must always be specified, with one exception: // If we are using a backing file, we can obtain the size from there @@ -3831,6 +3541,7 @@ void bdrv_img_create(const char *filename, const char *fmt, char *full_backing = g_new0(char, PATH_MAX); int64_t size; int back_flags; + QDict *backing_options = NULL; bdrv_get_full_backing_filename_from_filename(filename, backing_file, full_backing, PATH_MAX, @@ -3841,12 +3552,18 @@ void bdrv_img_create(const char *filename, const char *fmt, } /* backing files always opened read-only */ - back_flags = - flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING); + back_flags = flags; + back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING); + + if (backing_fmt) { + backing_options = qdict_new(); + qdict_put(backing_options, "driver", + qstring_from_str(backing_fmt)); + } bs = NULL; - ret = bdrv_open(&bs, full_backing, NULL, NULL, back_flags, - backing_drv, &local_err); + ret = bdrv_open(&bs, full_backing, NULL, backing_options, + back_flags, &local_err); g_free(full_backing); if (ret < 0) { goto out; @@ -3869,7 +3586,7 @@ void bdrv_img_create(const char *filename, const char *fmt, } if (!quiet) { - printf("Formatting '%s', fmt=%s", filename, fmt); + printf("Formatting '%s', fmt=%s ", filename, fmt); qemu_opts_print(opts, " "); puts(""); } @@ -3915,17 +3632,17 @@ void bdrv_detach_aio_context(BlockDriverState *bs) baf->detach_aio_context(baf->opaque); } - if (bs->io_limits_enabled) { + if (bs->throttle_state) { throttle_timers_detach_aio_context(&bs->throttle_timers); } if (bs->drv->bdrv_detach_aio_context) { bs->drv->bdrv_detach_aio_context(bs); } if (bs->file) { - bdrv_detach_aio_context(bs->file); + bdrv_detach_aio_context(bs->file->bs); } - if (bs->backing_hd) { - bdrv_detach_aio_context(bs->backing_hd); + if (bs->backing) { + bdrv_detach_aio_context(bs->backing->bs); } bs->aio_context = NULL; @@ -3942,16 +3659,16 @@ void bdrv_attach_aio_context(BlockDriverState *bs, bs->aio_context = new_context; - if (bs->backing_hd) { - bdrv_attach_aio_context(bs->backing_hd, new_context); + if (bs->backing) { + bdrv_attach_aio_context(bs->backing->bs, new_context); } if (bs->file) { - bdrv_attach_aio_context(bs->file, new_context); + bdrv_attach_aio_context(bs->file->bs, new_context); } if (bs->drv->bdrv_attach_aio_context) { bs->drv->bdrv_attach_aio_context(bs, new_context); } - if (bs->io_limits_enabled) { + if (bs->throttle_state) { throttle_timers_attach_aio_context(&bs->throttle_timers, new_context); } @@ -4012,12 +3729,12 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs, } int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts, - BlockDriverAmendStatusCB *status_cb) + BlockDriverAmendStatusCB *status_cb, void *cb_opaque) { if (!bs->drv->bdrv_amend_options) { return -ENOTSUP; } - return bs->drv->bdrv_amend_options(bs, opts, status_cb); + return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque); } /* This function will be called by the bdrv_recurse_is_first_non_filter method @@ -4059,10 +3776,10 @@ bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs, */ bool bdrv_is_first_non_filter(BlockDriverState *candidate) { - BlockDriverState *bs; + BlockDriverState *bs = NULL; /* walk down the bs forest recursively */ - QTAILQ_FOREACH(bs, &bdrv_states, device_list) { + while ((bs = bdrv_next(bs)) != NULL) { bool perm; /* try to recurse in this top level bs */ @@ -4077,7 +3794,8 @@ bool bdrv_is_first_non_filter(BlockDriverState *candidate) return false; } -BlockDriverState *check_to_replace_node(const char *node_name, Error **errp) +BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs, + const char *node_name, Error **errp) { BlockDriverState *to_replace_bs = bdrv_find_node(node_name); AioContext *aio_context; @@ -4100,7 +3818,7 @@ BlockDriverState *check_to_replace_node(const char *node_name, Error **errp) * Another benefit is that this tests exclude backing files which are * blocked by the backing blockers. */ - if (!bdrv_is_first_non_filter(to_replace_bs)) { + if (!bdrv_recurse_is_first_non_filter(parent_bs, to_replace_bs)) { error_setg(errp, "Only top most non filter can be replaced"); to_replace_bs = NULL; goto out; @@ -4114,20 +3832,39 @@ out: static bool append_open_options(QDict *d, BlockDriverState *bs) { const QDictEntry *entry; + QemuOptDesc *desc; + BdrvChild *child; bool found_any = false; + const char *p; for (entry = qdict_first(bs->options); entry; entry = qdict_next(bs->options, entry)) { - /* Only take options for this level and exclude all non-driver-specific - * options */ - if (!strchr(qdict_entry_key(entry), '.') && - strcmp(qdict_entry_key(entry), "node-name")) - { - qobject_incref(qdict_entry_value(entry)); - qdict_put_obj(d, qdict_entry_key(entry), qdict_entry_value(entry)); - found_any = true; + /* Exclude options for children */ + QLIST_FOREACH(child, &bs->children, next) { + if (strstart(qdict_entry_key(entry), child->name, &p) + && (!*p || *p == '.')) + { + break; + } + } + if (child) { + continue; } + + /* And exclude all non-driver-specific options */ + for (desc = bdrv_runtime_opts.desc; desc->name; desc++) { + if (!strcmp(qdict_entry_key(entry), desc->name)) { + break; + } + } + if (desc->name) { + continue; + } + + qobject_incref(qdict_entry_value(entry)); + qdict_put_obj(d, qdict_entry_key(entry), qdict_entry_value(entry)); + found_any = true; } return found_any; @@ -4157,7 +3894,7 @@ void bdrv_refresh_filename(BlockDriverState *bs) /* This BDS's file name will most probably depend on its file's name, so * refresh that first */ if (bs->file) { - bdrv_refresh_filename(bs->file); + bdrv_refresh_filename(bs->file->bs); } if (drv->bdrv_refresh_filename) { @@ -4169,7 +3906,10 @@ void bdrv_refresh_filename(BlockDriverState *bs) bs->full_open_options = NULL; } - drv->bdrv_refresh_filename(bs); + opts = qdict_new(); + append_open_options(opts, bs); + drv->bdrv_refresh_filename(bs, opts); + QDECREF(opts); } else if (bs->file) { /* Try to reconstruct valid information from the underlying file */ bool has_open_options; @@ -4185,19 +3925,20 @@ void bdrv_refresh_filename(BlockDriverState *bs) /* If no specific options have been given for this BDS, the filename of * the underlying file should suffice for this one as well */ - if (bs->file->exact_filename[0] && !has_open_options) { - strcpy(bs->exact_filename, bs->file->exact_filename); + if (bs->file->bs->exact_filename[0] && !has_open_options) { + strcpy(bs->exact_filename, bs->file->bs->exact_filename); } /* Reconstructing the full options QDict is simple for most format block * drivers, as long as the full options are known for the underlying * file BDS. The full options QDict of that file BDS should somehow * contain a representation of the filename, therefore the following * suffices without querying the (exact_)filename of this BDS. */ - if (bs->file->full_open_options) { + if (bs->file->bs->full_open_options) { qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str(drv->format_name))); - QINCREF(bs->file->full_open_options); - qdict_put_obj(opts, "file", QOBJECT(bs->file->full_open_options)); + QINCREF(bs->file->bs->full_open_options); + qdict_put_obj(opts, "file", + QOBJECT(bs->file->bs->full_open_options)); bs->full_open_options = opts; } else { @@ -4240,14 +3981,3 @@ void bdrv_refresh_filename(BlockDriverState *bs) QDECREF(json); } } - -/* This accessor function purpose is to allow the device models to access the - * BlockAcctStats structure embedded inside a BlockDriverState without being - * aware of the BlockDriverState structure layout. - * It will go away when the BlockAcctStats structure will be moved inside - * the device models. - */ -BlockAcctStats *bdrv_get_stats(BlockDriverState *bs) -{ - return &bs->stats; -} diff --git a/qemu/block/Makefile.objs b/qemu/block/Makefile.objs index 58ef2ef3f..44a541622 100644 --- a/qemu/block/Makefile.objs +++ b/qemu/block/Makefile.objs @@ -4,7 +4,7 @@ block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o block-obj-y += qed-check.o block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o block-obj-y += quorum.o -block-obj-y += parallels.o blkdebug.o blkverify.o +block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o block-obj-y += block-backend.o snapshot.o qapi.o block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o block-obj-$(CONFIG_POSIX) += raw-posix.o @@ -20,9 +20,11 @@ block-obj-$(CONFIG_RBD) += rbd.o block-obj-$(CONFIG_GLUSTERFS) += gluster.o block-obj-$(CONFIG_ARCHIPELAGO) += archipelago.o block-obj-$(CONFIG_LIBSSH2) += ssh.o -block-obj-y += accounting.o +block-obj-y += accounting.o dirty-bitmap.o block-obj-y += write-threshold.o +block-obj-y += crypto.o + common-obj-y += stream.o common-obj-y += commit.o common-obj-y += backup.o diff --git a/qemu/block/accounting.c b/qemu/block/accounting.c index 01d594ffd..3f457c4e7 100644 --- a/qemu/block/accounting.c +++ b/qemu/block/accounting.c @@ -2,6 +2,7 @@ * QEMU System Emulator block accounting * * Copyright (c) 2011 Christoph Hellwig + * Copyright (c) 2015 Igalia, S.L. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,9 +23,58 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "block/accounting.h" #include "block/block_int.h" #include "qemu/timer.h" +#include "sysemu/qtest.h" + +static QEMUClockType clock_type = QEMU_CLOCK_REALTIME; +static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000; + +void block_acct_init(BlockAcctStats *stats, bool account_invalid, + bool account_failed) +{ + stats->account_invalid = account_invalid; + stats->account_failed = account_failed; + + if (qtest_enabled()) { + clock_type = QEMU_CLOCK_VIRTUAL; + } +} + +void block_acct_cleanup(BlockAcctStats *stats) +{ + BlockAcctTimedStats *s, *next; + QSLIST_FOREACH_SAFE(s, &stats->intervals, entries, next) { + g_free(s); + } +} + +void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length) +{ + BlockAcctTimedStats *s; + unsigned i; + + s = g_new0(BlockAcctTimedStats, 1); + s->interval_length = interval_length; + QSLIST_INSERT_HEAD(&stats->intervals, s, entries); + + for (i = 0; i < BLOCK_MAX_IOTYPE; i++) { + timed_average_init(&s->latency[i], clock_type, + (uint64_t) interval_length * NANOSECONDS_PER_SECOND); + } +} + +BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats, + BlockAcctTimedStats *s) +{ + if (s == NULL) { + return QSLIST_FIRST(&stats->intervals); + } else { + return QSLIST_NEXT(s, entries); + } +} void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie, int64_t bytes, enum BlockAcctType type) @@ -32,26 +82,69 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie, assert(type < BLOCK_MAX_IOTYPE); cookie->bytes = bytes; - cookie->start_time_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + cookie->start_time_ns = qemu_clock_get_ns(clock_type); cookie->type = type; } void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie) { + BlockAcctTimedStats *s; + int64_t time_ns = qemu_clock_get_ns(clock_type); + int64_t latency_ns = time_ns - cookie->start_time_ns; + + if (qtest_enabled()) { + latency_ns = qtest_latency_ns; + } + assert(cookie->type < BLOCK_MAX_IOTYPE); stats->nr_bytes[cookie->type] += cookie->bytes; stats->nr_ops[cookie->type]++; - stats->total_time_ns[cookie->type] += - qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - cookie->start_time_ns; + stats->total_time_ns[cookie->type] += latency_ns; + stats->last_access_time_ns = time_ns; + + QSLIST_FOREACH(s, &stats->intervals, entries) { + timed_average_account(&s->latency[cookie->type], latency_ns); + } } +void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie) +{ + assert(cookie->type < BLOCK_MAX_IOTYPE); + + stats->failed_ops[cookie->type]++; + + if (stats->account_failed) { + BlockAcctTimedStats *s; + int64_t time_ns = qemu_clock_get_ns(clock_type); + int64_t latency_ns = time_ns - cookie->start_time_ns; + + if (qtest_enabled()) { + latency_ns = qtest_latency_ns; + } -void block_acct_highest_sector(BlockAcctStats *stats, int64_t sector_num, - unsigned int nb_sectors) + stats->total_time_ns[cookie->type] += latency_ns; + stats->last_access_time_ns = time_ns; + + QSLIST_FOREACH(s, &stats->intervals, entries) { + timed_average_account(&s->latency[cookie->type], latency_ns); + } + } +} + +void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type) { - if (stats->wr_highest_sector < sector_num + nb_sectors - 1) { - stats->wr_highest_sector = sector_num + nb_sectors - 1; + assert(type < BLOCK_MAX_IOTYPE); + + /* block_acct_done() and block_acct_failed() update + * total_time_ns[], but this one does not. The reason is that + * invalid requests are accounted during their submission, + * therefore there's no actual I/O involved. */ + + stats->invalid_ops[type]++; + + if (stats->account_invalid) { + stats->last_access_time_ns = qemu_clock_get_ns(clock_type); } } @@ -61,3 +154,20 @@ void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type, assert(type < BLOCK_MAX_IOTYPE); stats->merged[type] += num_requests; } + +int64_t block_acct_idle_time_ns(BlockAcctStats *stats) +{ + return qemu_clock_get_ns(clock_type) - stats->last_access_time_ns; +} + +double block_acct_queue_depth(BlockAcctTimedStats *stats, + enum BlockAcctType type) +{ + uint64_t sum, elapsed; + + assert(type < BLOCK_MAX_IOTYPE); + + sum = timed_average_sum(&stats->latency[type], &elapsed); + + return (double) sum / elapsed; +} diff --git a/qemu/block/archipelago.c b/qemu/block/archipelago.c index 855655c6b..b9f5e69d4 100644 --- a/qemu/block/archipelago.c +++ b/qemu/block/archipelago.c @@ -50,7 +50,8 @@ * */ -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "qemu/cutils.h" #include "block/block_int.h" #include "qemu/error-report.h" #include "qemu/thread.h" @@ -59,7 +60,6 @@ #include "qapi/qmp/qjson.h" #include "qemu/atomic.h" -#include #include #include diff --git a/qemu/block/backup.c b/qemu/block/backup.c index 965654d52..491fd1406 100644 --- a/qemu/block/backup.c +++ b/qemu/block/backup.c @@ -11,21 +11,20 @@ * */ -#include -#include -#include +#include "qemu/osdep.h" #include "trace.h" #include "block/block.h" #include "block/block_int.h" #include "block/blockjob.h" +#include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qemu/ratelimit.h" +#include "qemu/cutils.h" +#include "sysemu/block-backend.h" +#include "qemu/bitmap.h" -#define BACKUP_CLUSTER_BITS 16 -#define BACKUP_CLUSTER_SIZE (1 << BACKUP_CLUSTER_BITS) -#define BACKUP_SECTORS_PER_CLUSTER (BACKUP_CLUSTER_SIZE / BDRV_SECTOR_SIZE) - +#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16) #define SLICE_TIME 100000000ULL /* ns */ typedef struct CowRequest { @@ -46,10 +45,17 @@ typedef struct BackupBlockJob { BlockdevOnError on_target_error; CoRwlock flush_rwlock; uint64_t sectors_read; - HBitmap *bitmap; + unsigned long *done_bitmap; + int64_t cluster_size; QLIST_HEAD(, CowRequest) inflight_reqs; } BackupBlockJob; +/* Size of a cluster in sectors, instead of bytes. */ +static inline int64_t cluster_size_sectors(BackupBlockJob *job) +{ + return job->cluster_size / BDRV_SECTOR_SIZE; +} + /* See if in-flight requests overlap and wait for them to complete */ static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job, int64_t start, @@ -89,7 +95,8 @@ static void cow_request_end(CowRequest *req) static int coroutine_fn backup_do_cow(BlockDriverState *bs, int64_t sector_num, int nb_sectors, - bool *error_is_read) + bool *error_is_read, + bool is_write_notifier) { BackupBlockJob *job = (BackupBlockJob *)bs->job; CowRequest cow_request; @@ -97,13 +104,14 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs, QEMUIOVector bounce_qiov; void *bounce_buffer = NULL; int ret = 0; + int64_t sectors_per_cluster = cluster_size_sectors(job); int64_t start, end; int n; qemu_co_rwlock_rdlock(&job->flush_rwlock); - start = sector_num / BACKUP_SECTORS_PER_CLUSTER; - end = DIV_ROUND_UP(sector_num + nb_sectors, BACKUP_SECTORS_PER_CLUSTER); + start = sector_num / sectors_per_cluster; + end = DIV_ROUND_UP(sector_num + nb_sectors, sectors_per_cluster); trace_backup_do_cow_enter(job, start, sector_num, nb_sectors); @@ -111,26 +119,32 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs, cow_request_begin(&cow_request, job, start, end); for (; start < end; start++) { - if (hbitmap_get(job->bitmap, start)) { + if (test_bit(start, job->done_bitmap)) { trace_backup_do_cow_skip(job, start); continue; /* already copied */ } trace_backup_do_cow_process(job, start); - n = MIN(BACKUP_SECTORS_PER_CLUSTER, + n = MIN(sectors_per_cluster, job->common.len / BDRV_SECTOR_SIZE - - start * BACKUP_SECTORS_PER_CLUSTER); + start * sectors_per_cluster); if (!bounce_buffer) { - bounce_buffer = qemu_blockalign(bs, BACKUP_CLUSTER_SIZE); + bounce_buffer = qemu_blockalign(bs, job->cluster_size); } iov.iov_base = bounce_buffer; iov.iov_len = n * BDRV_SECTOR_SIZE; qemu_iovec_init_external(&bounce_qiov, &iov, 1); - ret = bdrv_co_readv(bs, start * BACKUP_SECTORS_PER_CLUSTER, n, - &bounce_qiov); + if (is_write_notifier) { + ret = bdrv_co_readv_no_serialising(bs, + start * sectors_per_cluster, + n, &bounce_qiov); + } else { + ret = bdrv_co_readv(bs, start * sectors_per_cluster, n, + &bounce_qiov); + } if (ret < 0) { trace_backup_do_cow_read_fail(job, start, ret); if (error_is_read) { @@ -141,11 +155,11 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs, if (buffer_is_zero(iov.iov_base, iov.iov_len)) { ret = bdrv_co_write_zeroes(job->target, - start * BACKUP_SECTORS_PER_CLUSTER, + start * sectors_per_cluster, n, BDRV_REQ_MAY_UNMAP); } else { ret = bdrv_co_writev(job->target, - start * BACKUP_SECTORS_PER_CLUSTER, n, + start * sectors_per_cluster, n, &bounce_qiov); } if (ret < 0) { @@ -156,7 +170,7 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs, goto out; } - hbitmap_set(job->bitmap, start, 1); + set_bit(start, job->done_bitmap); /* Publish progress, guest I/O counts as progress too. Note that the * offset field is an opaque progress value, it is not a disk offset. @@ -190,7 +204,7 @@ static int coroutine_fn backup_before_write_notify( assert((req->offset & (BDRV_SECTOR_SIZE - 1)) == 0); assert((req->bytes & (BDRV_SECTOR_SIZE - 1)) == 0); - return backup_do_cow(req->bs, sector_num, nb_sectors, NULL); + return backup_do_cow(req->bs, sector_num, nb_sectors, NULL, true); } static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp) @@ -208,7 +222,41 @@ static void backup_iostatus_reset(BlockJob *job) { BackupBlockJob *s = container_of(job, BackupBlockJob, common); - bdrv_iostatus_reset(s->target); + if (s->target->blk) { + blk_iostatus_reset(s->target->blk); + } +} + +static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) +{ + BdrvDirtyBitmap *bm; + BlockDriverState *bs = job->common.bs; + + if (ret < 0 || block_job_is_cancelled(&job->common)) { + /* Merge the successor back into the parent, delete nothing. */ + bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL); + assert(bm); + } else { + /* Everything is fine, delete this bitmap and install the backup. */ + bm = bdrv_dirty_bitmap_abdicate(bs, job->sync_bitmap, NULL); + assert(bm); + } +} + +static void backup_commit(BlockJob *job) +{ + BackupBlockJob *s = container_of(job, BackupBlockJob, common); + if (s->sync_bitmap) { + backup_cleanup_sync_bitmap(s, 0); + } +} + +static void backup_abort(BlockJob *job) +{ + BackupBlockJob *s = container_of(job, BackupBlockJob, common); + if (s->sync_bitmap) { + backup_cleanup_sync_bitmap(s, -1); + } } static const BlockJobDriver backup_job_driver = { @@ -216,6 +264,8 @@ static const BlockJobDriver backup_job_driver = { .job_type = BLOCK_JOB_TYPE_BACKUP, .set_speed = backup_set_speed, .iostatus_reset = backup_iostatus_reset, + .commit = backup_commit, + .abort = backup_abort, }; static BlockErrorAction backup_error_action(BackupBlockJob *job, @@ -280,21 +330,22 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) int64_t cluster; int64_t end; int64_t last_cluster = -1; + int64_t sectors_per_cluster = cluster_size_sectors(job); BlockDriverState *bs = job->common.bs; HBitmapIter hbi; granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap); - clusters_per_iter = MAX((granularity / BACKUP_CLUSTER_SIZE), 1); + clusters_per_iter = MAX((granularity / job->cluster_size), 1); bdrv_dirty_iter_init(job->sync_bitmap, &hbi); /* Find the next dirty sector(s) */ while ((sector = hbitmap_iter_next(&hbi)) != -1) { - cluster = sector / BACKUP_SECTORS_PER_CLUSTER; + cluster = sector / sectors_per_cluster; /* Fake progress updates for any clusters we skipped */ if (cluster != last_cluster + 1) { job->common.offset += ((cluster - last_cluster - 1) * - BACKUP_CLUSTER_SIZE); + job->cluster_size); } for (end = cluster + clusters_per_iter; cluster < end; cluster++) { @@ -302,8 +353,9 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) if (yield_and_check(job)) { return ret; } - ret = backup_do_cow(bs, cluster * BACKUP_SECTORS_PER_CLUSTER, - BACKUP_SECTORS_PER_CLUSTER, &error_is_read); + ret = backup_do_cow(bs, cluster * sectors_per_cluster, + sectors_per_cluster, &error_is_read, + false); if ((ret < 0) && backup_error_action(job, error_is_read, -ret) == BLOCK_ERROR_ACTION_REPORT) { @@ -314,17 +366,17 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) /* If the bitmap granularity is smaller than the backup granularity, * we need to advance the iterator pointer to the next cluster. */ - if (granularity < BACKUP_CLUSTER_SIZE) { - bdrv_set_dirty_iter(&hbi, cluster * BACKUP_SECTORS_PER_CLUSTER); + if (granularity < job->cluster_size) { + bdrv_set_dirty_iter(&hbi, cluster * sectors_per_cluster); } last_cluster = cluster - 1; } /* Play some final catchup with the progress meter */ - end = DIV_ROUND_UP(job->common.len, BACKUP_CLUSTER_SIZE); + end = DIV_ROUND_UP(job->common.len, job->cluster_size); if (last_cluster + 1 < end) { - job->common.offset += ((end - last_cluster - 1) * BACKUP_CLUSTER_SIZE); + job->common.offset += ((end - last_cluster - 1) * job->cluster_size); } return ret; @@ -341,19 +393,21 @@ static void coroutine_fn backup_run(void *opaque) .notify = backup_before_write_notify, }; int64_t start, end; + int64_t sectors_per_cluster = cluster_size_sectors(job); int ret = 0; QLIST_INIT(&job->inflight_reqs); qemu_co_rwlock_init(&job->flush_rwlock); start = 0; - end = DIV_ROUND_UP(job->common.len, BACKUP_CLUSTER_SIZE); + end = DIV_ROUND_UP(job->common.len, job->cluster_size); - job->bitmap = hbitmap_alloc(end, 0); + job->done_bitmap = bitmap_new(end); - bdrv_set_enable_write_cache(target, true); - bdrv_set_on_error(target, on_target_error, on_target_error); - bdrv_iostatus_enable(target); + if (target->blk) { + blk_set_on_error(target->blk, on_target_error, on_target_error); + blk_iostatus_enable(target->blk); + } bdrv_add_before_write_notifier(bs, &before_write); @@ -382,7 +436,7 @@ static void coroutine_fn backup_run(void *opaque) /* Check to see if these blocks are already in the * backing file. */ - for (i = 0; i < BACKUP_SECTORS_PER_CLUSTER;) { + for (i = 0; i < sectors_per_cluster;) { /* bdrv_is_allocated() only returns true/false based * on the first set of sectors it comes across that * are are all in the same state. @@ -391,8 +445,8 @@ static void coroutine_fn backup_run(void *opaque) * needed but at some point that is always the case. */ alloced = bdrv_is_allocated(bs, - start * BACKUP_SECTORS_PER_CLUSTER + i, - BACKUP_SECTORS_PER_CLUSTER - i, &n); + start * sectors_per_cluster + i, + sectors_per_cluster - i, &n); i += n; if (alloced == 1 || n == 0) { @@ -407,8 +461,8 @@ static void coroutine_fn backup_run(void *opaque) } } /* FULL sync mode we copy the whole drive. */ - ret = backup_do_cow(bs, start * BACKUP_SECTORS_PER_CLUSTER, - BACKUP_SECTORS_PER_CLUSTER, &error_is_read); + ret = backup_do_cow(bs, start * sectors_per_cluster, + sectors_per_cluster, &error_is_read, false); if (ret < 0) { /* Depending on error action, fail now or retry cluster */ BlockErrorAction action = @@ -428,22 +482,11 @@ static void coroutine_fn backup_run(void *opaque) /* wait until pending backup_do_cow() calls have completed */ qemu_co_rwlock_wrlock(&job->flush_rwlock); qemu_co_rwlock_unlock(&job->flush_rwlock); + g_free(job->done_bitmap); - if (job->sync_bitmap) { - BdrvDirtyBitmap *bm; - if (ret < 0 || block_job_is_cancelled(&job->common)) { - /* Merge the successor back into the parent, delete nothing. */ - bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL); - assert(bm); - } else { - /* Everything is fine, delete this bitmap and install the backup. */ - bm = bdrv_dirty_bitmap_abdicate(bs, job->sync_bitmap, NULL); - assert(bm); - } + if (target->blk) { + blk_iostatus_disable(target->blk); } - hbitmap_free(job->bitmap); - - bdrv_iostatus_disable(target); bdrv_op_unblock_all(target, job->common.blocker); data = g_malloc(sizeof(*data)); @@ -457,9 +500,11 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, BlockdevOnError on_source_error, BlockdevOnError on_target_error, BlockCompletionFunc *cb, void *opaque, - Error **errp) + BlockJobTxn *txn, Error **errp) { int64_t len; + BlockDriverInfo bdi; + int ret; assert(bs); assert(target); @@ -472,7 +517,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, if ((on_source_error == BLOCKDEV_ON_ERROR_STOP || on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) && - !bdrv_iostatus_is_enabled(bs)) { + (!bs->blk || !blk_iostatus_is_enabled(bs->blk))) { error_setg(errp, QERR_INVALID_PARAMETER, "on-source-error"); return; } @@ -529,16 +574,35 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, goto error; } - bdrv_op_block_all(target, job->common.blocker); - job->on_source_error = on_source_error; job->on_target_error = on_target_error; job->target = target; job->sync_mode = sync_mode; job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ? sync_bitmap : NULL; + + /* If there is no backing file on the target, we cannot rely on COW if our + * backup cluster size is smaller than the target cluster size. Even for + * targets with a backing file, try to avoid COW if possible. */ + ret = bdrv_get_info(job->target, &bdi); + if (ret < 0 && !target->backing) { + error_setg_errno(errp, -ret, + "Couldn't determine the cluster size of the target image, " + "which has no backing file"); + error_append_hint(errp, + "Aborting, since this may create an unusable destination image\n"); + goto error; + } else if (ret < 0 && target->backing) { + /* Not fatal; just trudge on ahead. */ + job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT; + } else { + job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size); + } + + bdrv_op_block_all(target, job->common.blocker); job->common.len = len; job->common.co = qemu_coroutine_create(backup_run); + block_job_txn_add_job(txn, &job->common); qemu_coroutine_enter(job->common.co, job); return; diff --git a/qemu/block/blkdebug.c b/qemu/block/blkdebug.c index bc247f46f..20d25bda6 100644 --- a/qemu/block/blkdebug.c +++ b/qemu/block/blkdebug.c @@ -22,7 +22,9 @@ * THE SOFTWARE. */ -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/cutils.h" #include "qemu/config-file.h" #include "block/block_int.h" #include "qemu/module.h" @@ -30,12 +32,13 @@ #include "qapi/qmp/qdict.h" #include "qapi/qmp/qint.h" #include "qapi/qmp/qstring.h" +#include "sysemu/qtest.h" typedef struct BDRVBlkdebugState { int state; int new_state; - QLIST_HEAD(, BlkdebugRule) rules[BLKDBG_EVENT_MAX]; + QLIST_HEAD(, BlkdebugRule) rules[BLKDBG__MAX]; QSIMPLEQ_HEAD(, BlkdebugRule) active_rules; QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs; } BDRVBlkdebugState; @@ -63,7 +66,7 @@ enum { }; typedef struct BlkdebugRule { - BlkDebugEvent event; + BlkdebugEvent event; int action; int state; union { @@ -142,69 +145,12 @@ static QemuOptsList *config_groups[] = { NULL }; -static const char *event_names[BLKDBG_EVENT_MAX] = { - [BLKDBG_L1_UPDATE] = "l1_update", - [BLKDBG_L1_GROW_ALLOC_TABLE] = "l1_grow.alloc_table", - [BLKDBG_L1_GROW_WRITE_TABLE] = "l1_grow.write_table", - [BLKDBG_L1_GROW_ACTIVATE_TABLE] = "l1_grow.activate_table", - - [BLKDBG_L2_LOAD] = "l2_load", - [BLKDBG_L2_UPDATE] = "l2_update", - [BLKDBG_L2_UPDATE_COMPRESSED] = "l2_update_compressed", - [BLKDBG_L2_ALLOC_COW_READ] = "l2_alloc.cow_read", - [BLKDBG_L2_ALLOC_WRITE] = "l2_alloc.write", - - [BLKDBG_READ_AIO] = "read_aio", - [BLKDBG_READ_BACKING_AIO] = "read_backing_aio", - [BLKDBG_READ_COMPRESSED] = "read_compressed", - - [BLKDBG_WRITE_AIO] = "write_aio", - [BLKDBG_WRITE_COMPRESSED] = "write_compressed", - - [BLKDBG_VMSTATE_LOAD] = "vmstate_load", - [BLKDBG_VMSTATE_SAVE] = "vmstate_save", - - [BLKDBG_COW_READ] = "cow_read", - [BLKDBG_COW_WRITE] = "cow_write", - - [BLKDBG_REFTABLE_LOAD] = "reftable_load", - [BLKDBG_REFTABLE_GROW] = "reftable_grow", - [BLKDBG_REFTABLE_UPDATE] = "reftable_update", - - [BLKDBG_REFBLOCK_LOAD] = "refblock_load", - [BLKDBG_REFBLOCK_UPDATE] = "refblock_update", - [BLKDBG_REFBLOCK_UPDATE_PART] = "refblock_update_part", - [BLKDBG_REFBLOCK_ALLOC] = "refblock_alloc", - [BLKDBG_REFBLOCK_ALLOC_HOOKUP] = "refblock_alloc.hookup", - [BLKDBG_REFBLOCK_ALLOC_WRITE] = "refblock_alloc.write", - [BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS] = "refblock_alloc.write_blocks", - [BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE] = "refblock_alloc.write_table", - [BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE] = "refblock_alloc.switch_table", - - [BLKDBG_CLUSTER_ALLOC] = "cluster_alloc", - [BLKDBG_CLUSTER_ALLOC_BYTES] = "cluster_alloc_bytes", - [BLKDBG_CLUSTER_FREE] = "cluster_free", - - [BLKDBG_FLUSH_TO_OS] = "flush_to_os", - [BLKDBG_FLUSH_TO_DISK] = "flush_to_disk", - - [BLKDBG_PWRITEV_RMW_HEAD] = "pwritev_rmw.head", - [BLKDBG_PWRITEV_RMW_AFTER_HEAD] = "pwritev_rmw.after_head", - [BLKDBG_PWRITEV_RMW_TAIL] = "pwritev_rmw.tail", - [BLKDBG_PWRITEV_RMW_AFTER_TAIL] = "pwritev_rmw.after_tail", - [BLKDBG_PWRITEV] = "pwritev", - [BLKDBG_PWRITEV_ZERO] = "pwritev_zero", - [BLKDBG_PWRITEV_DONE] = "pwritev_done", - - [BLKDBG_EMPTY_IMAGE_PREPARE] = "empty_image_prepare", -}; - -static int get_event_by_name(const char *name, BlkDebugEvent *event) +static int get_event_by_name(const char *name, BlkdebugEvent *event) { int i; - for (i = 0; i < BLKDBG_EVENT_MAX; i++) { - if (!strcmp(event_names[i], name)) { + for (i = 0; i < BLKDBG__MAX; i++) { + if (!strcmp(BlkdebugEvent_lookup[i], name)) { *event = i; return 0; } @@ -223,7 +169,7 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp) struct add_rule_data *d = opaque; BDRVBlkdebugState *s = d->s; const char* event_name; - BlkDebugEvent event; + BlkdebugEvent event; struct BlkdebugRule *rule; /* Find the right event for the rule */ @@ -426,11 +372,11 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, /* Set initial state */ s->state = 1; - /* Open the backing file */ - assert(bs->file == NULL); - ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-image"), options, "image", - bs, &child_file, false, &local_err); - if (ret < 0) { + /* Open the image file */ + bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image", + bs, &child_file, false, &local_err); + if (local_err) { + ret = -EINVAL; error_propagate(errp, local_err); goto out; } @@ -449,7 +395,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, goto out; fail_unref: - bdrv_unref(bs->file); + bdrv_unref_child(bs, bs->file); out: qemu_opts_del(opts); return ret; @@ -510,7 +456,8 @@ static BlockAIOCB *blkdebug_aio_readv(BlockDriverState *bs, return inject_error(bs, cb, opaque, rule); } - return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque); + return bdrv_aio_readv(bs->file->bs, sector_num, qiov, nb_sectors, + cb, opaque); } static BlockAIOCB *blkdebug_aio_writev(BlockDriverState *bs, @@ -532,7 +479,8 @@ static BlockAIOCB *blkdebug_aio_writev(BlockDriverState *bs, return inject_error(bs, cb, opaque, rule); } - return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque); + return bdrv_aio_writev(bs->file->bs, sector_num, qiov, nb_sectors, + cb, opaque); } static BlockAIOCB *blkdebug_aio_flush(BlockDriverState *bs, @@ -551,7 +499,7 @@ static BlockAIOCB *blkdebug_aio_flush(BlockDriverState *bs, return inject_error(bs, cb, opaque, rule); } - return bdrv_aio_flush(bs->file, cb, opaque); + return bdrv_aio_flush(bs->file->bs, cb, opaque); } @@ -561,7 +509,7 @@ static void blkdebug_close(BlockDriverState *bs) BlkdebugRule *rule, *next; int i; - for (i = 0; i < BLKDBG_EVENT_MAX; i++) { + for (i = 0; i < BLKDBG__MAX; i++) { QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) { remove_rule(rule); } @@ -581,9 +529,13 @@ static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule) remove_rule(rule); QLIST_INSERT_HEAD(&s->suspended_reqs, &r, next); - printf("blkdebug: Suspended request '%s'\n", r.tag); + if (!qtest_enabled()) { + printf("blkdebug: Suspended request '%s'\n", r.tag); + } qemu_coroutine_yield(); - printf("blkdebug: Resuming request '%s'\n", r.tag); + if (!qtest_enabled()) { + printf("blkdebug: Resuming request '%s'\n", r.tag); + } QLIST_REMOVE(&r, next); g_free(r.tag); @@ -620,13 +572,13 @@ static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule, return injected; } -static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event) +static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event) { BDRVBlkdebugState *s = bs->opaque; struct BlkdebugRule *rule, *next; bool injected; - assert((int)event >= 0 && event < BLKDBG_EVENT_MAX); + assert((int)event >= 0 && event < BLKDBG__MAX); injected = false; s->new_state = s->state; @@ -641,7 +593,7 @@ static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event, { BDRVBlkdebugState *s = bs->opaque; struct BlkdebugRule *rule; - BlkDebugEvent blkdebug_event; + BlkdebugEvent blkdebug_event; if (get_event_by_name(event, &blkdebug_event) < 0) { return -ENOENT; @@ -683,7 +635,7 @@ static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs, BlkdebugRule *rule, *next; int i, ret = -ENOENT; - for (i = 0; i < BLKDBG_EVENT_MAX; i++) { + for (i = 0; i < BLKDBG__MAX; i++) { QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) { if (rule->action == ACTION_SUSPEND && !strcmp(rule->options.suspend.tag, tag)) { @@ -716,55 +668,50 @@ static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag) static int64_t blkdebug_getlength(BlockDriverState *bs) { - return bdrv_getlength(bs->file); + return bdrv_getlength(bs->file->bs); } static int blkdebug_truncate(BlockDriverState *bs, int64_t offset) { - return bdrv_truncate(bs->file, offset); + return bdrv_truncate(bs->file->bs, offset); } -static void blkdebug_refresh_filename(BlockDriverState *bs) +static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) { QDict *opts; const QDictEntry *e; bool force_json = false; - for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) { + for (e = qdict_first(options); e; e = qdict_next(options, e)) { if (strcmp(qdict_entry_key(e), "config") && - strcmp(qdict_entry_key(e), "x-image") && - strcmp(qdict_entry_key(e), "image") && - strncmp(qdict_entry_key(e), "image.", strlen("image."))) + strcmp(qdict_entry_key(e), "x-image")) { force_json = true; break; } } - if (force_json && !bs->file->full_open_options) { + if (force_json && !bs->file->bs->full_open_options) { /* The config file cannot be recreated, so creating a plain filename * is impossible */ return; } - if (!force_json && bs->file->exact_filename[0]) { + if (!force_json && bs->file->bs->exact_filename[0]) { snprintf(bs->exact_filename, sizeof(bs->exact_filename), "blkdebug:%s:%s", - qdict_get_try_str(bs->options, "config") ?: "", - bs->file->exact_filename); + qdict_get_try_str(options, "config") ?: "", + bs->file->bs->exact_filename); } opts = qdict_new(); qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkdebug"))); - QINCREF(bs->file->full_open_options); - qdict_put_obj(opts, "image", QOBJECT(bs->file->full_open_options)); + QINCREF(bs->file->bs->full_open_options); + qdict_put_obj(opts, "image", QOBJECT(bs->file->bs->full_open_options)); - for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) { - if (strcmp(qdict_entry_key(e), "x-image") && - strcmp(qdict_entry_key(e), "image") && - strncmp(qdict_entry_key(e), "image.", strlen("image."))) - { + for (e = qdict_first(options); e; e = qdict_next(options, e)) { + if (strcmp(qdict_entry_key(e), "x-image")) { qobject_incref(qdict_entry_value(e)); qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e)); } @@ -773,6 +720,12 @@ static void blkdebug_refresh_filename(BlockDriverState *bs) bs->full_open_options = opts; } +static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state, + BlockReopenQueue *queue, Error **errp) +{ + return 0; +} + static BlockDriver bdrv_blkdebug = { .format_name = "blkdebug", .protocol_name = "blkdebug", @@ -781,6 +734,7 @@ static BlockDriver bdrv_blkdebug = { .bdrv_parse_filename = blkdebug_parse_filename, .bdrv_file_open = blkdebug_open, .bdrv_close = blkdebug_close, + .bdrv_reopen_prepare = blkdebug_reopen_prepare, .bdrv_getlength = blkdebug_getlength, .bdrv_truncate = blkdebug_truncate, .bdrv_refresh_filename = blkdebug_refresh_filename, diff --git a/qemu/block/blkreplay.c b/qemu/block/blkreplay.c new file mode 100755 index 000000000..42f1813af --- /dev/null +++ b/qemu/block/blkreplay.c @@ -0,0 +1,160 @@ +/* + * Block protocol for record/replay + * + * Copyright (c) 2010-2016 Institute for System Programming + * of the Russian Academy of Sciences. + * + * 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 "block/block_int.h" +#include "sysemu/replay.h" +#include "qapi/error.h" + +typedef struct Request { + Coroutine *co; + QEMUBH *bh; +} Request; + +/* Next request id. + This counter is global, because requests from different + block devices should not get overlapping ids. */ +static uint64_t request_id; + +static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + Error *local_err = NULL; + int ret; + + /* Open the image file */ + bs->file = bdrv_open_child(NULL, options, "image", + bs, &child_file, false, &local_err); + if (local_err) { + ret = -EINVAL; + error_propagate(errp, local_err); + goto fail; + } + + ret = 0; +fail: + if (ret < 0) { + bdrv_unref_child(bs, bs->file); + } + return ret; +} + +static void blkreplay_close(BlockDriverState *bs) +{ +} + +static int64_t blkreplay_getlength(BlockDriverState *bs) +{ + return bdrv_getlength(bs->file->bs); +} + +/* This bh is used for synchronization of return from coroutines. + It continues yielded coroutine which then finishes its execution. + BH is called adjusted to some replay checkpoint, therefore + record and replay will always finish coroutines deterministically. +*/ +static void blkreplay_bh_cb(void *opaque) +{ + Request *req = opaque; + qemu_coroutine_enter(req->co, NULL); + qemu_bh_delete(req->bh); + g_free(req); +} + +static void block_request_create(uint64_t reqid, BlockDriverState *bs, + Coroutine *co) +{ + Request *req = g_new(Request, 1); + *req = (Request) { + .co = co, + .bh = aio_bh_new(bdrv_get_aio_context(bs), blkreplay_bh_cb, req), + }; + replay_block_event(req->bh, reqid); +} + +static int coroutine_fn blkreplay_co_readv(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) +{ + uint64_t reqid = request_id++; + int ret = bdrv_co_readv(bs->file->bs, sector_num, nb_sectors, qiov); + block_request_create(reqid, bs, qemu_coroutine_self()); + qemu_coroutine_yield(); + + return ret; +} + +static int coroutine_fn blkreplay_co_writev(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) +{ + uint64_t reqid = request_id++; + int ret = bdrv_co_writev(bs->file->bs, sector_num, nb_sectors, qiov); + block_request_create(reqid, bs, qemu_coroutine_self()); + qemu_coroutine_yield(); + + return ret; +} + +static int coroutine_fn blkreplay_co_write_zeroes(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, BdrvRequestFlags flags) +{ + uint64_t reqid = request_id++; + int ret = bdrv_co_write_zeroes(bs->file->bs, sector_num, nb_sectors, flags); + block_request_create(reqid, bs, qemu_coroutine_self()); + qemu_coroutine_yield(); + + return ret; +} + +static int coroutine_fn blkreplay_co_discard(BlockDriverState *bs, + int64_t sector_num, int nb_sectors) +{ + uint64_t reqid = request_id++; + int ret = bdrv_co_discard(bs->file->bs, sector_num, nb_sectors); + block_request_create(reqid, bs, qemu_coroutine_self()); + qemu_coroutine_yield(); + + return ret; +} + +static int coroutine_fn blkreplay_co_flush(BlockDriverState *bs) +{ + uint64_t reqid = request_id++; + int ret = bdrv_co_flush(bs->file->bs); + block_request_create(reqid, bs, qemu_coroutine_self()); + qemu_coroutine_yield(); + + return ret; +} + +static BlockDriver bdrv_blkreplay = { + .format_name = "blkreplay", + .protocol_name = "blkreplay", + .instance_size = 0, + + .bdrv_file_open = blkreplay_open, + .bdrv_close = blkreplay_close, + .bdrv_getlength = blkreplay_getlength, + + .bdrv_co_readv = blkreplay_co_readv, + .bdrv_co_writev = blkreplay_co_writev, + + .bdrv_co_write_zeroes = blkreplay_co_write_zeroes, + .bdrv_co_discard = blkreplay_co_discard, + .bdrv_co_flush = blkreplay_co_flush, +}; + +static void bdrv_blkreplay_init(void) +{ + bdrv_register(&bdrv_blkreplay); +} + +block_init(bdrv_blkreplay_init); diff --git a/qemu/block/blkverify.c b/qemu/block/blkverify.c index d277e6322..9414b7a84 100644 --- a/qemu/block/blkverify.c +++ b/qemu/block/blkverify.c @@ -7,14 +7,16 @@ * See the COPYING file in the top-level directory. */ -#include +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/sockets.h" /* for EINPROGRESS on Windows */ #include "block/block_int.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" +#include "qemu/cutils.h" typedef struct { - BlockDriverState *test_file; + BdrvChild *test_file; } BDRVBlkverifyState; typedef struct BlkverifyAIOCB BlkverifyAIOCB; @@ -123,26 +125,29 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, } /* Open the raw file */ - assert(bs->file == NULL); - ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-raw"), options, - "raw", bs, &child_file, false, &local_err); - if (ret < 0) { + bs->file = bdrv_open_child(qemu_opt_get(opts, "x-raw"), options, "raw", + bs, &child_file, false, &local_err); + if (local_err) { + ret = -EINVAL; error_propagate(errp, local_err); goto fail; } /* Open the test file */ - assert(s->test_file == NULL); - ret = bdrv_open_image(&s->test_file, qemu_opt_get(opts, "x-image"), options, - "test", bs, &child_format, false, &local_err); - if (ret < 0) { + s->test_file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, + "test", bs, &child_format, false, + &local_err); + if (local_err) { + ret = -EINVAL; error_propagate(errp, local_err); - s->test_file = NULL; goto fail; } ret = 0; fail: + if (ret < 0) { + bdrv_unref_child(bs, bs->file); + } qemu_opts_del(opts); return ret; } @@ -151,7 +156,7 @@ static void blkverify_close(BlockDriverState *bs) { BDRVBlkverifyState *s = bs->opaque; - bdrv_unref(s->test_file); + bdrv_unref_child(bs, s->test_file); s->test_file = NULL; } @@ -159,7 +164,7 @@ static int64_t blkverify_getlength(BlockDriverState *bs) { BDRVBlkverifyState *s = bs->opaque; - return bdrv_getlength(s->test_file); + return bdrv_getlength(s->test_file->bs); } static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write, @@ -238,13 +243,13 @@ static BlockAIOCB *blkverify_aio_readv(BlockDriverState *bs, nb_sectors, cb, opaque); acb->verify = blkverify_verify_readv; - acb->buf = qemu_blockalign(bs->file, qiov->size); + acb->buf = qemu_blockalign(bs->file->bs, qiov->size); qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov); qemu_iovec_clone(&acb->raw_qiov, qiov, acb->buf); - bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors, + bdrv_aio_readv(s->test_file->bs, sector_num, qiov, nb_sectors, blkverify_aio_cb, acb); - bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors, + bdrv_aio_readv(bs->file->bs, sector_num, &acb->raw_qiov, nb_sectors, blkverify_aio_cb, acb); return &acb->common; } @@ -257,9 +262,9 @@ static BlockAIOCB *blkverify_aio_writev(BlockDriverState *bs, BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov, nb_sectors, cb, opaque); - bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors, + bdrv_aio_writev(s->test_file->bs, sector_num, qiov, nb_sectors, blkverify_aio_cb, acb); - bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, + bdrv_aio_writev(bs->file->bs, sector_num, qiov, nb_sectors, blkverify_aio_cb, acb); return &acb->common; } @@ -271,7 +276,7 @@ static BlockAIOCB *blkverify_aio_flush(BlockDriverState *bs, BDRVBlkverifyState *s = bs->opaque; /* Only flush test file, the raw file is not important */ - return bdrv_aio_flush(s->test_file, cb, opaque); + return bdrv_aio_flush(s->test_file->bs, cb, opaque); } static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs, @@ -279,13 +284,13 @@ static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs, { BDRVBlkverifyState *s = bs->opaque; - bool perm = bdrv_recurse_is_first_non_filter(bs->file, candidate); + bool perm = bdrv_recurse_is_first_non_filter(bs->file->bs, candidate); if (perm) { return true; } - return bdrv_recurse_is_first_non_filter(s->test_file, candidate); + return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate); } /* Propagate AioContext changes to ->test_file */ @@ -293,7 +298,7 @@ static void blkverify_detach_aio_context(BlockDriverState *bs) { BDRVBlkverifyState *s = bs->opaque; - bdrv_detach_aio_context(s->test_file); + bdrv_detach_aio_context(s->test_file->bs); } static void blkverify_attach_aio_context(BlockDriverState *bs, @@ -301,32 +306,38 @@ static void blkverify_attach_aio_context(BlockDriverState *bs, { BDRVBlkverifyState *s = bs->opaque; - bdrv_attach_aio_context(s->test_file, new_context); + bdrv_attach_aio_context(s->test_file->bs, new_context); } -static void blkverify_refresh_filename(BlockDriverState *bs) +static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options) { BDRVBlkverifyState *s = bs->opaque; - /* bs->file has already been refreshed */ - bdrv_refresh_filename(s->test_file); + /* bs->file->bs has already been refreshed */ + bdrv_refresh_filename(s->test_file->bs); - if (bs->file->full_open_options && s->test_file->full_open_options) { + if (bs->file->bs->full_open_options + && s->test_file->bs->full_open_options) + { QDict *opts = qdict_new(); qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkverify"))); - QINCREF(bs->file->full_open_options); - qdict_put_obj(opts, "raw", QOBJECT(bs->file->full_open_options)); - QINCREF(s->test_file->full_open_options); - qdict_put_obj(opts, "test", QOBJECT(s->test_file->full_open_options)); + QINCREF(bs->file->bs->full_open_options); + qdict_put_obj(opts, "raw", QOBJECT(bs->file->bs->full_open_options)); + QINCREF(s->test_file->bs->full_open_options); + qdict_put_obj(opts, "test", + QOBJECT(s->test_file->bs->full_open_options)); bs->full_open_options = opts; } - if (bs->file->exact_filename[0] && s->test_file->exact_filename[0]) { + if (bs->file->bs->exact_filename[0] + && s->test_file->bs->exact_filename[0]) + { snprintf(bs->exact_filename, sizeof(bs->exact_filename), "blkverify:%s:%s", - bs->file->exact_filename, s->test_file->exact_filename); + bs->file->bs->exact_filename, + s->test_file->bs->exact_filename); } } diff --git a/qemu/block/block-backend.c b/qemu/block/block-backend.c index aee8a1202..16c9d5e0f 100644 --- a/qemu/block/block-backend.c +++ b/qemu/block/block-backend.c @@ -10,74 +10,105 @@ * or later. See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include "sysemu/block-backend.h" #include "block/block_int.h" +#include "block/blockjob.h" +#include "block/throttle-groups.h" #include "sysemu/blockdev.h" +#include "sysemu/sysemu.h" #include "qapi-event.h" +#include "qemu/id.h" /* Number of coroutines to reserve per attached device model */ #define COROUTINE_POOL_RESERVATION 64 +#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ + +static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb); + struct BlockBackend { char *name; int refcnt; - BlockDriverState *bs; + BdrvChild *root; DriveInfo *legacy_dinfo; /* null unless created by drive_new() */ - QTAILQ_ENTRY(BlockBackend) link; /* for blk_backends */ + QTAILQ_ENTRY(BlockBackend) link; /* for block_backends */ + QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */ void *dev; /* attached device model, if any */ /* TODO change to DeviceState when all users are qdevified */ const BlockDevOps *dev_ops; void *dev_opaque; + + /* the block size for which the guest device expects atomicity */ + int guest_block_size; + + /* If the BDS tree is removed, some of its options are stored here (which + * can be used to restore those options in the new BDS on insert) */ + BlockBackendRootState root_state; + + bool enable_write_cache; + + /* I/O stats (display with "info blockstats"). */ + BlockAcctStats stats; + + BlockdevOnError on_read_error, on_write_error; + bool iostatus_enabled; + BlockDeviceIoStatus iostatus; + + bool allow_write_beyond_eof; + + NotifierList remove_bs_notifiers, insert_bs_notifiers; }; typedef struct BlockBackendAIOCB { BlockAIOCB common; QEMUBH *bh; + BlockBackend *blk; int ret; } BlockBackendAIOCB; static const AIOCBInfo block_backend_aiocb_info = { + .get_aio_context = blk_aiocb_get_aio_context, .aiocb_size = sizeof(BlockBackendAIOCB), }; static void drive_info_del(DriveInfo *dinfo); -/* All the BlockBackends (except for hidden ones) */ -static QTAILQ_HEAD(, BlockBackend) blk_backends = - QTAILQ_HEAD_INITIALIZER(blk_backends); +/* All BlockBackends */ +static QTAILQ_HEAD(, BlockBackend) block_backends = + QTAILQ_HEAD_INITIALIZER(block_backends); + +/* All BlockBackends referenced by the monitor and which are iterated through by + * blk_next() */ +static QTAILQ_HEAD(, BlockBackend) monitor_block_backends = + QTAILQ_HEAD_INITIALIZER(monitor_block_backends); + +static void blk_root_inherit_options(int *child_flags, QDict *child_options, + int parent_flags, QDict *parent_options) +{ + /* We're not supposed to call this function for root nodes */ + abort(); +} + +static const BdrvChildRole child_root = { + .inherit_options = blk_root_inherit_options, +}; /* - * Create a new BlockBackend with @name, with a reference count of one. - * @name must not be null or empty. - * Fail if a BlockBackend with this name already exists. + * Create a new BlockBackend with a reference count of one. * Store an error through @errp on failure, unless it's null. * Return the new BlockBackend on success, null on failure. */ -BlockBackend *blk_new(const char *name, Error **errp) +BlockBackend *blk_new(Error **errp) { BlockBackend *blk; - assert(name && name[0]); - if (!id_wellformed(name)) { - error_setg(errp, "Invalid device name"); - return NULL; - } - if (blk_by_name(name)) { - error_setg(errp, "Device with id '%s' already exists", name); - return NULL; - } - if (bdrv_find_node(name)) { - error_setg(errp, - "Device name '%s' conflicts with an existing node name", - name); - return NULL; - } - blk = g_new0(BlockBackend, 1); - blk->name = g_strdup(name); blk->refcnt = 1; - QTAILQ_INSERT_TAIL(&blk_backends, blk, link); + notifier_list_init(&blk->remove_bs_notifiers); + notifier_list_init(&blk->insert_bs_notifiers); + QTAILQ_INSERT_TAIL(&block_backends, blk, link); return blk; } @@ -85,18 +116,18 @@ BlockBackend *blk_new(const char *name, Error **errp) * Create a new BlockBackend with a new BlockDriverState attached. * Otherwise just like blk_new(), which see. */ -BlockBackend *blk_new_with_bs(const char *name, Error **errp) +BlockBackend *blk_new_with_bs(Error **errp) { BlockBackend *blk; BlockDriverState *bs; - blk = blk_new(name, errp); + blk = blk_new(errp); if (!blk) { return NULL; } bs = bdrv_new_root(); - blk->bs = bs; + blk->root = bdrv_root_attach_child(bs, "root", &child_root); bs->blk = blk; return blk; } @@ -113,44 +144,46 @@ BlockBackend *blk_new_with_bs(const char *name, Error **errp) * though, so callers of this function have to be able to specify @filename and * @flags. */ -BlockBackend *blk_new_open(const char *name, const char *filename, - const char *reference, QDict *options, int flags, - Error **errp) +BlockBackend *blk_new_open(const char *filename, const char *reference, + QDict *options, int flags, Error **errp) { BlockBackend *blk; int ret; - blk = blk_new_with_bs(name, errp); + blk = blk_new_with_bs(errp); if (!blk) { QDECREF(options); return NULL; } - ret = bdrv_open(&blk->bs, filename, reference, options, flags, NULL, errp); + ret = bdrv_open(&blk->root->bs, filename, reference, options, flags, errp); if (ret < 0) { blk_unref(blk); return NULL; } + blk_set_enable_write_cache(blk, true); + return blk; } static void blk_delete(BlockBackend *blk) { assert(!blk->refcnt); + assert(!blk->name); assert(!blk->dev); - if (blk->bs) { - assert(blk->bs->blk == blk); - blk->bs->blk = NULL; - bdrv_unref(blk->bs); - blk->bs = NULL; + if (blk->root) { + blk_remove_bs(blk); } - /* Avoid double-remove after blk_hide_on_behalf_of_hmp_drive_del() */ - if (blk->name[0]) { - QTAILQ_REMOVE(&blk_backends, blk, link); + assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers)); + assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers)); + if (blk->root_state.throttle_state) { + g_free(blk->root_state.throttle_group); + throttle_group_unref(blk->root_state.throttle_state); } - g_free(blk->name); + QTAILQ_REMOVE(&block_backends, blk, link); drive_info_del(blk->legacy_dinfo); + block_acct_cleanup(&blk->stats); g_free(blk); } @@ -164,6 +197,11 @@ static void drive_info_del(DriveInfo *dinfo) g_free(dinfo); } +int blk_get_refcnt(BlockBackend *blk) +{ + return blk ? blk->refcnt : 0; +} + /* * Increment @blk's reference count. * @blk must not be null. @@ -189,7 +227,32 @@ void blk_unref(BlockBackend *blk) } /* - * Return the BlockBackend after @blk. + * Behaves similarly to blk_next() but iterates over all BlockBackends, even the + * ones which are hidden (i.e. are not referenced by the monitor). + */ +static BlockBackend *blk_all_next(BlockBackend *blk) +{ + return blk ? QTAILQ_NEXT(blk, link) + : QTAILQ_FIRST(&block_backends); +} + +void blk_remove_all_bs(void) +{ + BlockBackend *blk = NULL; + + while ((blk = blk_all_next(blk)) != NULL) { + AioContext *ctx = blk_get_aio_context(blk); + + aio_context_acquire(ctx); + if (blk->root) { + blk_remove_bs(blk); + } + aio_context_release(ctx); + } +} + +/* + * Return the monitor-owned BlockBackend after @blk. * If @blk is null, return the first one. * Else, return @blk's next sibling, which may be null. * @@ -200,17 +263,91 @@ void blk_unref(BlockBackend *blk) */ BlockBackend *blk_next(BlockBackend *blk) { - return blk ? QTAILQ_NEXT(blk, link) : QTAILQ_FIRST(&blk_backends); + return blk ? QTAILQ_NEXT(blk, monitor_link) + : QTAILQ_FIRST(&monitor_block_backends); +} + +/* + * Iterates over all BlockDriverStates which are attached to a BlockBackend. + * This function is for use by bdrv_next(). + * + * @bs must be NULL or a BDS that is attached to a BB. + */ +BlockDriverState *blk_next_root_bs(BlockDriverState *bs) +{ + BlockBackend *blk; + + if (bs) { + assert(bs->blk); + blk = bs->blk; + } else { + blk = NULL; + } + + do { + blk = blk_all_next(blk); + } while (blk && !blk->root); + + return blk ? blk->root->bs : NULL; +} + +/* + * Add a BlockBackend into the list of backends referenced by the monitor, with + * the given @name acting as the handle for the monitor. + * Strictly for use by blockdev.c. + * + * @name must not be null or empty. + * + * Returns true on success and false on failure. In the latter case, an Error + * object is returned through @errp. + */ +bool monitor_add_blk(BlockBackend *blk, const char *name, Error **errp) +{ + assert(!blk->name); + assert(name && name[0]); + + if (!id_wellformed(name)) { + error_setg(errp, "Invalid device name"); + return false; + } + if (blk_by_name(name)) { + error_setg(errp, "Device with id '%s' already exists", name); + return false; + } + if (bdrv_find_node(name)) { + error_setg(errp, + "Device name '%s' conflicts with an existing node name", + name); + return false; + } + + blk->name = g_strdup(name); + QTAILQ_INSERT_TAIL(&monitor_block_backends, blk, monitor_link); + return true; +} + +/* + * Remove a BlockBackend from the list of backends referenced by the monitor. + * Strictly for use by blockdev.c. + */ +void monitor_remove_blk(BlockBackend *blk) +{ + if (!blk->name) { + return; + } + + QTAILQ_REMOVE(&monitor_block_backends, blk, monitor_link); + g_free(blk->name); + blk->name = NULL; } /* * Return @blk's name, a non-null string. - * Wart: the name is empty iff @blk has been hidden with - * blk_hide_on_behalf_of_hmp_drive_del(). + * Returns an empty string iff @blk is not referenced by the monitor. */ const char *blk_name(BlockBackend *blk) { - return blk->name; + return blk->name ?: ""; } /* @@ -219,10 +356,10 @@ const char *blk_name(BlockBackend *blk) */ BlockBackend *blk_by_name(const char *name) { - BlockBackend *blk; + BlockBackend *blk = NULL; assert(name); - QTAILQ_FOREACH(blk, &blk_backends, link) { + while ((blk = blk_next(blk)) != NULL) { if (!strcmp(name, blk->name)) { return blk; } @@ -235,7 +372,7 @@ BlockBackend *blk_by_name(const char *name) */ BlockDriverState *blk_bs(BlockBackend *blk) { - return blk->bs; + return blk->root ? blk->root->bs : NULL; } /* @@ -263,9 +400,9 @@ DriveInfo *blk_set_legacy_dinfo(BlockBackend *blk, DriveInfo *dinfo) */ BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo) { - BlockBackend *blk; + BlockBackend *blk = NULL; - QTAILQ_FOREACH(blk, &blk_backends, link) { + while ((blk = blk_next(blk)) != NULL) { if (blk->legacy_dinfo == dinfo) { return blk; } @@ -274,21 +411,32 @@ BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo) } /* - * Hide @blk. - * @blk must not have been hidden already. - * Make attached BlockDriverState, if any, anonymous. - * Once hidden, @blk is invisible to all functions that don't receive - * it as argument. For example, blk_by_name() won't return it. - * Strictly for use by do_drive_del(). - * TODO get rid of it! + * Disassociates the currently associated BlockDriverState from @blk. */ -void blk_hide_on_behalf_of_hmp_drive_del(BlockBackend *blk) +void blk_remove_bs(BlockBackend *blk) { - QTAILQ_REMOVE(&blk_backends, blk, link); - blk->name[0] = 0; - if (blk->bs) { - bdrv_make_anon(blk->bs); - } + assert(blk->root->bs->blk == blk); + + notifier_list_notify(&blk->remove_bs_notifiers, blk); + + blk_update_root_state(blk); + + blk->root->bs->blk = NULL; + bdrv_root_unref_child(blk->root); + blk->root = NULL; +} + +/* + * Associates a new BlockDriverState with @blk. + */ +void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs) +{ + assert(!blk->root && !bs->blk); + bdrv_ref(bs); + blk->root = bdrv_root_attach_child(bs, "root", &child_root); + bs->blk = blk; + + notifier_list_notify(&blk->insert_bs_notifiers, blk); } /* @@ -303,7 +451,7 @@ int blk_attach_dev(BlockBackend *blk, void *dev) } blk_ref(blk); blk->dev = dev; - bdrv_iostatus_reset(blk->bs); + blk_iostatus_reset(blk); return 0; } @@ -330,7 +478,7 @@ void blk_detach_dev(BlockBackend *blk, void *dev) blk->dev = NULL; blk->dev_ops = NULL; blk->dev_opaque = NULL; - bdrv_set_guest_block_size(blk->bs, 512); + blk->guest_block_size = 512; blk_unref(blk); } @@ -364,18 +512,15 @@ void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, void blk_dev_change_media_cb(BlockBackend *blk, bool load) { if (blk->dev_ops && blk->dev_ops->change_media_cb) { - bool tray_was_closed = !blk_dev_is_tray_open(blk); + bool tray_was_open, tray_is_open; + tray_was_open = blk_dev_is_tray_open(blk); blk->dev_ops->change_media_cb(blk->dev_opaque, load); - if (tray_was_closed) { - /* tray open */ - qapi_event_send_device_tray_moved(blk_name(blk), - true, &error_abort); - } - if (load) { - /* tray close */ - qapi_event_send_device_tray_moved(blk_name(blk), - false, &error_abort); + tray_is_open = blk_dev_is_tray_open(blk); + + if (tray_was_open != tray_is_open) { + qapi_event_send_device_tray_moved(blk_name(blk), tray_is_open, + &error_abort); } } } @@ -389,6 +534,14 @@ bool blk_dev_has_removable_media(BlockBackend *blk) return !blk->dev || (blk->dev_ops && blk->dev_ops->change_media_cb); } +/* + * Does @blk's attached device model have a tray? + */ +bool blk_dev_has_tray(BlockBackend *blk) +{ + return blk->dev_ops && blk->dev_ops->is_tray_open; +} + /* * Notify @blk's attached device model of a media eject request. * If @force is true, the medium is about to be yanked out forcefully. @@ -405,7 +558,7 @@ void blk_dev_eject_request(BlockBackend *blk, bool force) */ bool blk_dev_is_tray_open(BlockBackend *blk) { - if (blk->dev_ops && blk->dev_ops->is_tray_open) { + if (blk_dev_has_tray(blk)) { return blk->dev_ops->is_tray_open(blk->dev_opaque); } return false; @@ -435,7 +588,53 @@ void blk_dev_resize_cb(BlockBackend *blk) void blk_iostatus_enable(BlockBackend *blk) { - bdrv_iostatus_enable(blk->bs); + blk->iostatus_enabled = true; + blk->iostatus = BLOCK_DEVICE_IO_STATUS_OK; +} + +/* The I/O status is only enabled if the drive explicitly + * enables it _and_ the VM is configured to stop on errors */ +bool blk_iostatus_is_enabled(const BlockBackend *blk) +{ + return (blk->iostatus_enabled && + (blk->on_write_error == BLOCKDEV_ON_ERROR_ENOSPC || + blk->on_write_error == BLOCKDEV_ON_ERROR_STOP || + blk->on_read_error == BLOCKDEV_ON_ERROR_STOP)); +} + +BlockDeviceIoStatus blk_iostatus(const BlockBackend *blk) +{ + return blk->iostatus; +} + +void blk_iostatus_disable(BlockBackend *blk) +{ + blk->iostatus_enabled = false; +} + +void blk_iostatus_reset(BlockBackend *blk) +{ + if (blk_iostatus_is_enabled(blk)) { + BlockDriverState *bs = blk_bs(blk); + blk->iostatus = BLOCK_DEVICE_IO_STATUS_OK; + if (bs && bs->job) { + block_job_iostatus_reset(bs->job); + } + } +} + +void blk_iostatus_set_err(BlockBackend *blk, int error) +{ + assert(blk_iostatus_is_enabled(blk)); + if (blk->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { + blk->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE : + BLOCK_DEVICE_IO_STATUS_FAILED; + } +} + +void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow) +{ + blk->allow_write_beyond_eof = allow; } static int blk_check_byte_request(BlockBackend *blk, int64_t offset, @@ -447,21 +646,23 @@ static int blk_check_byte_request(BlockBackend *blk, int64_t offset, return -EIO; } - if (!blk_is_inserted(blk)) { + if (!blk_is_available(blk)) { return -ENOMEDIUM; } - len = blk_getlength(blk); - if (len < 0) { - return len; - } - if (offset < 0) { return -EIO; } - if (offset > len || len - offset < size) { - return -EIO; + if (!blk->allow_write_beyond_eof) { + len = blk_getlength(blk); + if (len < 0) { + return len; + } + + if (offset > len || len - offset < size) { + return -EIO; + } } return 0; @@ -482,48 +683,144 @@ static int blk_check_request(BlockBackend *blk, int64_t sector_num, nb_sectors * BDRV_SECTOR_SIZE); } -int blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf, - int nb_sectors) +static int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, + unsigned int bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) { - int ret = blk_check_request(blk, sector_num, nb_sectors); + int ret = blk_check_byte_request(blk, offset, bytes); if (ret < 0) { return ret; } - return bdrv_read(blk->bs, sector_num, buf, nb_sectors); + return bdrv_co_do_preadv(blk_bs(blk), offset, bytes, qiov, flags); } -int blk_read_unthrottled(BlockBackend *blk, int64_t sector_num, uint8_t *buf, - int nb_sectors) +static int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, + unsigned int bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) { - int ret = blk_check_request(blk, sector_num, nb_sectors); + int ret; + + ret = blk_check_byte_request(blk, offset, bytes); if (ret < 0) { return ret; } - return bdrv_read_unthrottled(blk->bs, sector_num, buf, nb_sectors); + if (!blk->enable_write_cache) { + flags |= BDRV_REQ_FUA; + } + + return bdrv_co_do_pwritev(blk_bs(blk), offset, bytes, qiov, flags); } -int blk_write(BlockBackend *blk, int64_t sector_num, const uint8_t *buf, - int nb_sectors) +typedef struct BlkRwCo { + BlockBackend *blk; + int64_t offset; + QEMUIOVector *qiov; + int ret; + BdrvRequestFlags flags; +} BlkRwCo; + +static void blk_read_entry(void *opaque) { - int ret = blk_check_request(blk, sector_num, nb_sectors); - if (ret < 0) { - return ret; + BlkRwCo *rwco = opaque; + + rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, rwco->qiov->size, + rwco->qiov, rwco->flags); +} + +static void blk_write_entry(void *opaque) +{ + BlkRwCo *rwco = opaque; + + rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, rwco->qiov->size, + rwco->qiov, rwco->flags); +} + +static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, + int64_t bytes, CoroutineEntry co_entry, + BdrvRequestFlags flags) +{ + AioContext *aio_context; + QEMUIOVector qiov; + struct iovec iov; + Coroutine *co; + BlkRwCo rwco; + + iov = (struct iovec) { + .iov_base = buf, + .iov_len = bytes, + }; + qemu_iovec_init_external(&qiov, &iov, 1); + + rwco = (BlkRwCo) { + .blk = blk, + .offset = offset, + .qiov = &qiov, + .flags = flags, + .ret = NOT_DONE, + }; + + co = qemu_coroutine_create(co_entry); + qemu_coroutine_enter(co, &rwco); + + aio_context = blk_get_aio_context(blk); + while (rwco.ret == NOT_DONE) { + aio_poll(aio_context, true); } - return bdrv_write(blk->bs, sector_num, buf, nb_sectors); + return rwco.ret; } -int blk_write_zeroes(BlockBackend *blk, int64_t sector_num, - int nb_sectors, BdrvRequestFlags flags) +static int blk_rw(BlockBackend *blk, int64_t sector_num, uint8_t *buf, + int nb_sectors, CoroutineEntry co_entry, + BdrvRequestFlags flags) { - int ret = blk_check_request(blk, sector_num, nb_sectors); + if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { + return -EINVAL; + } + + return blk_prw(blk, sector_num << BDRV_SECTOR_BITS, buf, + nb_sectors << BDRV_SECTOR_BITS, co_entry, flags); +} + +int blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf, + int nb_sectors) +{ + return blk_rw(blk, sector_num, buf, nb_sectors, blk_read_entry, 0); +} + +int blk_read_unthrottled(BlockBackend *blk, int64_t sector_num, uint8_t *buf, + int nb_sectors) +{ + BlockDriverState *bs = blk_bs(blk); + bool enabled; + int ret; + + ret = blk_check_request(blk, sector_num, nb_sectors); if (ret < 0) { return ret; } - return bdrv_write_zeroes(blk->bs, sector_num, nb_sectors, flags); + enabled = bs->io_limits_enabled; + bs->io_limits_enabled = false; + ret = blk_read(blk, sector_num, buf, nb_sectors); + bs->io_limits_enabled = enabled; + return ret; +} + +int blk_write(BlockBackend *blk, int64_t sector_num, const uint8_t *buf, + int nb_sectors) +{ + return blk_rw(blk, sector_num, (uint8_t*) buf, nb_sectors, + blk_write_entry, 0); +} + +int blk_write_zeroes(BlockBackend *blk, int64_t sector_num, + int nb_sectors, BdrvRequestFlags flags) +{ + return blk_rw(blk, sector_num, NULL, nb_sectors, blk_write_entry, + flags | BDRV_REQ_ZERO_WRITE); } static void error_callback_bh(void *opaque) @@ -534,13 +831,15 @@ static void error_callback_bh(void *opaque) qemu_aio_unref(acb); } -static BlockAIOCB *abort_aio_request(BlockBackend *blk, BlockCompletionFunc *cb, - void *opaque, int ret) +BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, + BlockCompletionFunc *cb, + void *opaque, int ret) { struct BlockBackendAIOCB *acb; QEMUBH *bh; acb = blk_aio_get(&block_backend_aiocb_info, blk, cb, opaque); + acb->blk = blk; acb->ret = ret; bh = aio_bh_new(blk_get_aio_context(blk), error_callback_bh, acb); @@ -550,82 +849,182 @@ static BlockAIOCB *abort_aio_request(BlockBackend *blk, BlockCompletionFunc *cb, return &acb->common; } +typedef struct BlkAioEmAIOCB { + BlockAIOCB common; + BlkRwCo rwco; + int bytes; + bool has_returned; + QEMUBH* bh; +} BlkAioEmAIOCB; + +static const AIOCBInfo blk_aio_em_aiocb_info = { + .aiocb_size = sizeof(BlkAioEmAIOCB), +}; + +static void blk_aio_complete(BlkAioEmAIOCB *acb) +{ + if (acb->bh) { + assert(acb->has_returned); + qemu_bh_delete(acb->bh); + } + if (acb->has_returned) { + acb->common.cb(acb->common.opaque, acb->rwco.ret); + qemu_aio_unref(acb); + } +} + +static void blk_aio_complete_bh(void *opaque) +{ + blk_aio_complete(opaque); +} + +static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, int bytes, + QEMUIOVector *qiov, CoroutineEntry co_entry, + BdrvRequestFlags flags, + BlockCompletionFunc *cb, void *opaque) +{ + BlkAioEmAIOCB *acb; + Coroutine *co; + + acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque); + acb->rwco = (BlkRwCo) { + .blk = blk, + .offset = offset, + .qiov = qiov, + .flags = flags, + .ret = NOT_DONE, + }; + acb->bytes = bytes; + acb->bh = NULL; + acb->has_returned = false; + + co = qemu_coroutine_create(co_entry); + qemu_coroutine_enter(co, acb); + + acb->has_returned = true; + if (acb->rwco.ret != NOT_DONE) { + acb->bh = aio_bh_new(blk_get_aio_context(blk), blk_aio_complete_bh, acb); + qemu_bh_schedule(acb->bh); + } + + return &acb->common; +} + +static void blk_aio_read_entry(void *opaque) +{ + BlkAioEmAIOCB *acb = opaque; + BlkRwCo *rwco = &acb->rwco; + + assert(rwco->qiov->size == acb->bytes); + rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, acb->bytes, + rwco->qiov, rwco->flags); + blk_aio_complete(acb); +} + +static void blk_aio_write_entry(void *opaque) +{ + BlkAioEmAIOCB *acb = opaque; + BlkRwCo *rwco = &acb->rwco; + + assert(!rwco->qiov || rwco->qiov->size == acb->bytes); + rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, acb->bytes, + rwco->qiov, rwco->flags); + blk_aio_complete(acb); +} + BlockAIOCB *blk_aio_write_zeroes(BlockBackend *blk, int64_t sector_num, int nb_sectors, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque) { - int ret = blk_check_request(blk, sector_num, nb_sectors); - if (ret < 0) { - return abort_aio_request(blk, cb, opaque, ret); + if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { + return blk_abort_aio_request(blk, cb, opaque, -EINVAL); } - return bdrv_aio_write_zeroes(blk->bs, sector_num, nb_sectors, flags, - cb, opaque); + return blk_aio_prwv(blk, sector_num << BDRV_SECTOR_BITS, + nb_sectors << BDRV_SECTOR_BITS, NULL, + blk_aio_write_entry, flags | BDRV_REQ_ZERO_WRITE, + cb, opaque); } int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int count) { - int ret = blk_check_byte_request(blk, offset, count); + int ret = blk_prw(blk, offset, buf, count, blk_read_entry, 0); if (ret < 0) { return ret; } - - return bdrv_pread(blk->bs, offset, buf, count); + return count; } int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int count) { - int ret = blk_check_byte_request(blk, offset, count); + int ret = blk_prw(blk, offset, (void*) buf, count, blk_write_entry, 0); if (ret < 0) { return ret; } - - return bdrv_pwrite(blk->bs, offset, buf, count); + return count; } int64_t blk_getlength(BlockBackend *blk) { - return bdrv_getlength(blk->bs); + if (!blk_is_available(blk)) { + return -ENOMEDIUM; + } + + return bdrv_getlength(blk_bs(blk)); } void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr) { - bdrv_get_geometry(blk->bs, nb_sectors_ptr); + if (!blk_bs(blk)) { + *nb_sectors_ptr = 0; + } else { + bdrv_get_geometry(blk_bs(blk), nb_sectors_ptr); + } } int64_t blk_nb_sectors(BlockBackend *blk) { - return bdrv_nb_sectors(blk->bs); + if (!blk_is_available(blk)) { + return -ENOMEDIUM; + } + + return bdrv_nb_sectors(blk_bs(blk)); } BlockAIOCB *blk_aio_readv(BlockBackend *blk, int64_t sector_num, QEMUIOVector *iov, int nb_sectors, BlockCompletionFunc *cb, void *opaque) { - int ret = blk_check_request(blk, sector_num, nb_sectors); - if (ret < 0) { - return abort_aio_request(blk, cb, opaque, ret); + if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { + return blk_abort_aio_request(blk, cb, opaque, -EINVAL); } - return bdrv_aio_readv(blk->bs, sector_num, iov, nb_sectors, cb, opaque); + assert(nb_sectors << BDRV_SECTOR_BITS == iov->size); + return blk_aio_prwv(blk, sector_num << BDRV_SECTOR_BITS, iov->size, iov, + blk_aio_read_entry, 0, cb, opaque); } BlockAIOCB *blk_aio_writev(BlockBackend *blk, int64_t sector_num, QEMUIOVector *iov, int nb_sectors, BlockCompletionFunc *cb, void *opaque) { - int ret = blk_check_request(blk, sector_num, nb_sectors); - if (ret < 0) { - return abort_aio_request(blk, cb, opaque, ret); + if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { + return blk_abort_aio_request(blk, cb, opaque, -EINVAL); } - return bdrv_aio_writev(blk->bs, sector_num, iov, nb_sectors, cb, opaque); + assert(nb_sectors << BDRV_SECTOR_BITS == iov->size); + return blk_aio_prwv(blk, sector_num << BDRV_SECTOR_BITS, iov->size, iov, + blk_aio_write_entry, 0, cb, opaque); } BlockAIOCB *blk_aio_flush(BlockBackend *blk, BlockCompletionFunc *cb, void *opaque) { - return bdrv_aio_flush(blk->bs, cb, opaque); + if (!blk_is_available(blk)) { + return blk_abort_aio_request(blk, cb, opaque, -ENOMEDIUM); + } + + return bdrv_aio_flush(blk_bs(blk), cb, opaque); } BlockAIOCB *blk_aio_discard(BlockBackend *blk, @@ -634,10 +1033,10 @@ BlockAIOCB *blk_aio_discard(BlockBackend *blk, { int ret = blk_check_request(blk, sector_num, nb_sectors); if (ret < 0) { - return abort_aio_request(blk, cb, opaque, ret); + return blk_abort_aio_request(blk, cb, opaque, ret); } - return bdrv_aio_discard(blk->bs, sector_num, nb_sectors, cb, opaque); + return bdrv_aio_discard(blk_bs(blk), sector_num, nb_sectors, cb, opaque); } void blk_aio_cancel(BlockAIOCB *acb) @@ -661,18 +1060,26 @@ int blk_aio_multiwrite(BlockBackend *blk, BlockRequest *reqs, int num_reqs) } } - return bdrv_aio_multiwrite(blk->bs, reqs, num_reqs); + return bdrv_aio_multiwrite(blk_bs(blk), reqs, num_reqs); } int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) { - return bdrv_ioctl(blk->bs, req, buf); + if (!blk_is_available(blk)) { + return -ENOMEDIUM; + } + + return bdrv_ioctl(blk_bs(blk), req, buf); } BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, BlockCompletionFunc *cb, void *opaque) { - return bdrv_aio_ioctl(blk->bs, req, buf, cb, opaque); + if (!blk_is_available(blk)) { + return blk_abort_aio_request(blk, cb, opaque, -ENOMEDIUM); + } + + return bdrv_aio_ioctl(blk_bs(blk), req, buf, cb, opaque); } int blk_co_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors) @@ -682,27 +1089,32 @@ int blk_co_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors) return ret; } - return bdrv_co_discard(blk->bs, sector_num, nb_sectors); + return bdrv_co_discard(blk_bs(blk), sector_num, nb_sectors); } int blk_co_flush(BlockBackend *blk) { - return bdrv_co_flush(blk->bs); + if (!blk_is_available(blk)) { + return -ENOMEDIUM; + } + + return bdrv_co_flush(blk_bs(blk)); } int blk_flush(BlockBackend *blk) { - return bdrv_flush(blk->bs); -} + if (!blk_is_available(blk)) { + return -ENOMEDIUM; + } -int blk_flush_all(void) -{ - return bdrv_flush_all(); + return bdrv_flush(blk_bs(blk)); } void blk_drain(BlockBackend *blk) { - bdrv_drain(blk->bs); + if (blk_bs(blk)) { + bdrv_drain(blk_bs(blk)); + } } void blk_drain_all(void) @@ -710,119 +1122,273 @@ void blk_drain_all(void) bdrv_drain_all(); } +void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, + BlockdevOnError on_write_error) +{ + blk->on_read_error = on_read_error; + blk->on_write_error = on_write_error; +} + BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read) { - return bdrv_get_on_error(blk->bs, is_read); + return is_read ? blk->on_read_error : blk->on_write_error; } BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, int error) { - return bdrv_get_error_action(blk->bs, is_read, error); + BlockdevOnError on_err = blk_get_on_error(blk, is_read); + + switch (on_err) { + case BLOCKDEV_ON_ERROR_ENOSPC: + return (error == ENOSPC) ? + BLOCK_ERROR_ACTION_STOP : BLOCK_ERROR_ACTION_REPORT; + case BLOCKDEV_ON_ERROR_STOP: + return BLOCK_ERROR_ACTION_STOP; + case BLOCKDEV_ON_ERROR_REPORT: + return BLOCK_ERROR_ACTION_REPORT; + case BLOCKDEV_ON_ERROR_IGNORE: + return BLOCK_ERROR_ACTION_IGNORE; + default: + abort(); + } +} + +static void send_qmp_error_event(BlockBackend *blk, + BlockErrorAction action, + bool is_read, int error) +{ + IoOperationType optype; + + optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE; + qapi_event_send_block_io_error(blk_name(blk), optype, action, + blk_iostatus_is_enabled(blk), + error == ENOSPC, strerror(error), + &error_abort); } +/* This is done by device models because, while the block layer knows + * about the error, it does not know whether an operation comes from + * the device or the block layer (from a job, for example). + */ void blk_error_action(BlockBackend *blk, BlockErrorAction action, bool is_read, int error) { - bdrv_error_action(blk->bs, action, is_read, error); + assert(error >= 0); + + if (action == BLOCK_ERROR_ACTION_STOP) { + /* First set the iostatus, so that "info block" returns an iostatus + * that matches the events raised so far (an additional error iostatus + * is fine, but not a lost one). + */ + blk_iostatus_set_err(blk, error); + + /* Then raise the request to stop the VM and the event. + * qemu_system_vmstop_request_prepare has two effects. First, + * it ensures that the STOP event always comes after the + * BLOCK_IO_ERROR event. Second, it ensures that even if management + * can observe the STOP event and do a "cont" before the STOP + * event is issued, the VM will not stop. In this case, vm_start() + * also ensures that the STOP/RESUME pair of events is emitted. + */ + qemu_system_vmstop_request_prepare(); + send_qmp_error_event(blk, action, is_read, error); + qemu_system_vmstop_request(RUN_STATE_IO_ERROR); + } else { + send_qmp_error_event(blk, action, is_read, error); + } } int blk_is_read_only(BlockBackend *blk) { - return bdrv_is_read_only(blk->bs); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + return bdrv_is_read_only(bs); + } else { + return blk->root_state.read_only; + } } int blk_is_sg(BlockBackend *blk) { - return bdrv_is_sg(blk->bs); + BlockDriverState *bs = blk_bs(blk); + + if (!bs) { + return 0; + } + + return bdrv_is_sg(bs); } int blk_enable_write_cache(BlockBackend *blk) { - return bdrv_enable_write_cache(blk->bs); + return blk->enable_write_cache; } void blk_set_enable_write_cache(BlockBackend *blk, bool wce) { - bdrv_set_enable_write_cache(blk->bs, wce); + blk->enable_write_cache = wce; } void blk_invalidate_cache(BlockBackend *blk, Error **errp) { - bdrv_invalidate_cache(blk->bs, errp); + BlockDriverState *bs = blk_bs(blk); + + if (!bs) { + error_setg(errp, "Device '%s' has no medium", blk->name); + return; + } + + bdrv_invalidate_cache(bs, errp); +} + +bool blk_is_inserted(BlockBackend *blk) +{ + BlockDriverState *bs = blk_bs(blk); + + return bs && bdrv_is_inserted(bs); } -int blk_is_inserted(BlockBackend *blk) +bool blk_is_available(BlockBackend *blk) { - return bdrv_is_inserted(blk->bs); + return blk_is_inserted(blk) && !blk_dev_is_tray_open(blk); } void blk_lock_medium(BlockBackend *blk, bool locked) { - bdrv_lock_medium(blk->bs, locked); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_lock_medium(bs, locked); + } } void blk_eject(BlockBackend *blk, bool eject_flag) { - bdrv_eject(blk->bs, eject_flag); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_eject(bs, eject_flag); + } } int blk_get_flags(BlockBackend *blk) { - return bdrv_get_flags(blk->bs); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + return bdrv_get_flags(bs); + } else { + return blk->root_state.open_flags; + } } int blk_get_max_transfer_length(BlockBackend *blk) { - return blk->bs->bl.max_transfer_length; + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + return bs->bl.max_transfer_length; + } else { + return 0; + } +} + +int blk_get_max_iov(BlockBackend *blk) +{ + return blk->root->bs->bl.max_iov; } void blk_set_guest_block_size(BlockBackend *blk, int align) { - bdrv_set_guest_block_size(blk->bs, align); + blk->guest_block_size = align; +} + +void *blk_try_blockalign(BlockBackend *blk, size_t size) +{ + return qemu_try_blockalign(blk ? blk_bs(blk) : NULL, size); } void *blk_blockalign(BlockBackend *blk, size_t size) { - return qemu_blockalign(blk ? blk->bs : NULL, size); + return qemu_blockalign(blk ? blk_bs(blk) : NULL, size); } bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp) { - return bdrv_op_is_blocked(blk->bs, op, errp); + BlockDriverState *bs = blk_bs(blk); + + if (!bs) { + return false; + } + + return bdrv_op_is_blocked(bs, op, errp); } void blk_op_unblock(BlockBackend *blk, BlockOpType op, Error *reason) { - bdrv_op_unblock(blk->bs, op, reason); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_op_unblock(bs, op, reason); + } } void blk_op_block_all(BlockBackend *blk, Error *reason) { - bdrv_op_block_all(blk->bs, reason); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_op_block_all(bs, reason); + } } void blk_op_unblock_all(BlockBackend *blk, Error *reason) { - bdrv_op_unblock_all(blk->bs, reason); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_op_unblock_all(bs, reason); + } } AioContext *blk_get_aio_context(BlockBackend *blk) { - return bdrv_get_aio_context(blk->bs); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + return bdrv_get_aio_context(bs); + } else { + return qemu_get_aio_context(); + } +} + +static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb) +{ + BlockBackendAIOCB *blk_acb = DO_UPCAST(BlockBackendAIOCB, common, acb); + return blk_get_aio_context(blk_acb->blk); } void blk_set_aio_context(BlockBackend *blk, AioContext *new_context) { - bdrv_set_aio_context(blk->bs, new_context); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_set_aio_context(bs, new_context); + } } 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) { - bdrv_add_aio_context_notifier(blk->bs, attached_aio_context, - detach_aio_context, opaque); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_add_aio_context_notifier(bs, attached_aio_context, + detach_aio_context, opaque); + } } void blk_remove_aio_context_notifier(BlockBackend *blk, @@ -831,28 +1397,45 @@ void blk_remove_aio_context_notifier(BlockBackend *blk, void (*detach_aio_context)(void *), void *opaque) { - bdrv_remove_aio_context_notifier(blk->bs, attached_aio_context, - detach_aio_context, opaque); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_remove_aio_context_notifier(bs, attached_aio_context, + detach_aio_context, opaque); + } } -void blk_add_close_notifier(BlockBackend *blk, Notifier *notify) +void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify) { - bdrv_add_close_notifier(blk->bs, notify); + notifier_list_add(&blk->remove_bs_notifiers, notify); +} + +void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify) +{ + notifier_list_add(&blk->insert_bs_notifiers, notify); } void blk_io_plug(BlockBackend *blk) { - bdrv_io_plug(blk->bs); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_io_plug(bs); + } } void blk_io_unplug(BlockBackend *blk) { - bdrv_io_unplug(blk->bs); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_io_unplug(bs); + } } BlockAcctStats *blk_get_stats(BlockBackend *blk) { - return bdrv_get_stats(blk->bs); + return &blk->stats; } void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, @@ -864,12 +1447,13 @@ void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, int coroutine_fn blk_co_write_zeroes(BlockBackend *blk, int64_t sector_num, int nb_sectors, BdrvRequestFlags flags) { - int ret = blk_check_request(blk, sector_num, nb_sectors); - if (ret < 0) { - return ret; + if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { + return -EINVAL; } - return bdrv_co_write_zeroes(blk->bs, sector_num, nb_sectors, flags); + return blk_co_pwritev(blk, sector_num << BDRV_SECTOR_BITS, + nb_sectors << BDRV_SECTOR_BITS, NULL, + flags | BDRV_REQ_ZERO_WRITE); } int blk_write_compressed(BlockBackend *blk, int64_t sector_num, @@ -880,12 +1464,16 @@ int blk_write_compressed(BlockBackend *blk, int64_t sector_num, return ret; } - return bdrv_write_compressed(blk->bs, sector_num, buf, nb_sectors); + return bdrv_write_compressed(blk_bs(blk), sector_num, buf, nb_sectors); } int blk_truncate(BlockBackend *blk, int64_t offset) { - return bdrv_truncate(blk->bs, offset); + if (!blk_is_available(blk)) { + return -ENOMEDIUM; + } + + return bdrv_truncate(blk_bs(blk), offset); } int blk_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors) @@ -895,26 +1483,153 @@ int blk_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors) return ret; } - return bdrv_discard(blk->bs, sector_num, nb_sectors); + return bdrv_discard(blk_bs(blk), sector_num, nb_sectors); } int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, int64_t pos, int size) { - return bdrv_save_vmstate(blk->bs, buf, pos, size); + int ret; + + if (!blk_is_available(blk)) { + return -ENOMEDIUM; + } + + ret = bdrv_save_vmstate(blk_bs(blk), buf, pos, size); + if (ret < 0) { + return ret; + } + + if (ret == size && !blk->enable_write_cache) { + ret = bdrv_flush(blk_bs(blk)); + } + + return ret < 0 ? ret : size; } int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size) { - return bdrv_load_vmstate(blk->bs, buf, pos, size); + if (!blk_is_available(blk)) { + return -ENOMEDIUM; + } + + return bdrv_load_vmstate(blk_bs(blk), buf, pos, size); } int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz) { - return bdrv_probe_blocksizes(blk->bs, bsz); + if (!blk_is_available(blk)) { + return -ENOMEDIUM; + } + + return bdrv_probe_blocksizes(blk_bs(blk), bsz); } int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo) { - return bdrv_probe_geometry(blk->bs, geo); + if (!blk_is_available(blk)) { + return -ENOMEDIUM; + } + + return bdrv_probe_geometry(blk_bs(blk), geo); +} + +/* + * Updates the BlockBackendRootState object with data from the currently + * attached BlockDriverState. + */ +void blk_update_root_state(BlockBackend *blk) +{ + assert(blk->root); + + blk->root_state.open_flags = blk->root->bs->open_flags; + blk->root_state.read_only = blk->root->bs->read_only; + blk->root_state.detect_zeroes = blk->root->bs->detect_zeroes; + + if (blk->root_state.throttle_group) { + g_free(blk->root_state.throttle_group); + throttle_group_unref(blk->root_state.throttle_state); + } + if (blk->root->bs->throttle_state) { + const char *name = throttle_group_get_name(blk->root->bs); + blk->root_state.throttle_group = g_strdup(name); + blk->root_state.throttle_state = throttle_group_incref(name); + } else { + blk->root_state.throttle_group = NULL; + blk->root_state.throttle_state = NULL; + } +} + +/* + * Applies the information in the root state to the given BlockDriverState. This + * does not include the flags which have to be specified for bdrv_open(), use + * blk_get_open_flags_from_root_state() to inquire them. + */ +void blk_apply_root_state(BlockBackend *blk, BlockDriverState *bs) +{ + bs->detect_zeroes = blk->root_state.detect_zeroes; + if (blk->root_state.throttle_group) { + bdrv_io_limits_enable(bs, blk->root_state.throttle_group); + } +} + +/* + * Returns the flags to be used for bdrv_open() of a BlockDriverState which is + * supposed to inherit the root state. + */ +int blk_get_open_flags_from_root_state(BlockBackend *blk) +{ + int bs_flags; + + bs_flags = blk->root_state.read_only ? 0 : BDRV_O_RDWR; + bs_flags |= blk->root_state.open_flags & ~BDRV_O_RDWR; + + return bs_flags; +} + +BlockBackendRootState *blk_get_root_state(BlockBackend *blk) +{ + return &blk->root_state; +} + +int blk_commit_all(void) +{ + BlockBackend *blk = NULL; + + while ((blk = blk_all_next(blk)) != NULL) { + AioContext *aio_context = blk_get_aio_context(blk); + + aio_context_acquire(aio_context); + if (blk_is_inserted(blk) && blk->root->bs->backing) { + int ret = bdrv_commit(blk->root->bs); + if (ret < 0) { + aio_context_release(aio_context); + return ret; + } + } + aio_context_release(aio_context); + } + return 0; +} + +int blk_flush_all(void) +{ + BlockBackend *blk = NULL; + int result = 0; + + while ((blk = blk_all_next(blk)) != NULL) { + AioContext *aio_context = blk_get_aio_context(blk); + int ret; + + aio_context_acquire(aio_context); + if (blk_is_inserted(blk)) { + ret = blk_flush(blk); + if (ret < 0 && !result) { + result = ret; + } + } + aio_context_release(aio_context); + } + + return result; } diff --git a/qemu/block/bochs.c b/qemu/block/bochs.c index 199ac2b9a..af8b7abdf 100644 --- a/qemu/block/bochs.c +++ b/qemu/block/bochs.c @@ -22,6 +22,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "block/block_int.h" #include "qemu/module.h" @@ -103,7 +105,7 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, bs->read_only = 1; // no write support yet - ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); + ret = bdrv_pread(bs->file->bs, 0, &bochs, sizeof(bochs)); if (ret < 0) { return ret; } @@ -137,7 +139,7 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, return -ENOMEM; } - ret = bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap, + ret = bdrv_pread(bs->file->bs, le32_to_cpu(bochs.header), s->catalog_bitmap, s->catalog_size * 4); if (ret < 0) { goto fail; @@ -206,7 +208,7 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) (s->extent_blocks + s->bitmap_blocks)); /* read in bitmap for current extent */ - ret = bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8), + ret = bdrv_pread(bs->file->bs, bitmap_offset + (extent_offset / 8), &bitmap_entry, 1); if (ret < 0) { return ret; @@ -229,7 +231,7 @@ static int bochs_read(BlockDriverState *bs, int64_t sector_num, if (block_offset < 0) { return block_offset; } else if (block_offset > 0) { - ret = bdrv_pread(bs->file, block_offset, buf, 512); + ret = bdrv_pread(bs->file->bs, block_offset, buf, 512); if (ret < 0) { return ret; } diff --git a/qemu/block/cloop.c b/qemu/block/cloop.c index f328be06f..a84f14019 100644 --- a/qemu/block/cloop.c +++ b/qemu/block/cloop.c @@ -21,6 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "block/block_int.h" #include "qemu/module.h" @@ -66,7 +68,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, bs->read_only = 1; /* read header */ - ret = bdrv_pread(bs->file, 128, &s->block_size, 4); + ret = bdrv_pread(bs->file->bs, 128, &s->block_size, 4); if (ret < 0) { return ret; } @@ -92,7 +94,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - ret = bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4); + ret = bdrv_pread(bs->file->bs, 128 + 4, &s->n_blocks, 4); if (ret < 0) { return ret; } @@ -123,7 +125,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, return -ENOMEM; } - ret = bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size); + ret = bdrv_pread(bs->file->bs, 128 + 4 + 4, s->offsets, offsets_size); if (ret < 0) { goto fail; } @@ -203,8 +205,8 @@ static inline int cloop_read_block(BlockDriverState *bs, int block_num) int ret; uint32_t bytes = s->offsets[block_num + 1] - s->offsets[block_num]; - ret = bdrv_pread(bs->file, s->offsets[block_num], s->compressed_block, - bytes); + ret = bdrv_pread(bs->file->bs, s->offsets[block_num], + s->compressed_block, bytes); if (ret != bytes) { return -1; } diff --git a/qemu/block/commit.c b/qemu/block/commit.c index 7312a5bdc..cba0e8c1e 100644 --- a/qemu/block/commit.c +++ b/qemu/block/commit.c @@ -12,11 +12,14 @@ * */ +#include "qemu/osdep.h" #include "trace.h" #include "block/block_int.h" #include "block/blockjob.h" +#include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qemu/ratelimit.h" +#include "sysemu/block-backend.h" enum { /* @@ -213,7 +216,7 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base, if ((on_error == BLOCKDEV_ON_ERROR_STOP || on_error == BLOCKDEV_ON_ERROR_ENOSPC) && - !bdrv_iostatus_is_enabled(bs)) { + (!bs->blk || !blk_iostatus_is_enabled(bs->blk))) { error_setg(errp, "Invalid parameter combination"); return; } @@ -235,14 +238,14 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base, orig_overlay_flags = bdrv_get_flags(overlay_bs); /* convert base & overlay_bs to r/w, if necessary */ - if (!(orig_base_flags & BDRV_O_RDWR)) { - reopen_queue = bdrv_reopen_queue(reopen_queue, base, - orig_base_flags | BDRV_O_RDWR); - } if (!(orig_overlay_flags & BDRV_O_RDWR)) { - reopen_queue = bdrv_reopen_queue(reopen_queue, overlay_bs, + reopen_queue = bdrv_reopen_queue(reopen_queue, overlay_bs, NULL, orig_overlay_flags | BDRV_O_RDWR); } + if (!(orig_base_flags & BDRV_O_RDWR)) { + reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL, + orig_base_flags | BDRV_O_RDWR); + } if (reopen_queue) { bdrv_reopen_multiple(reopen_queue, &local_err); if (local_err != NULL) { diff --git a/qemu/block/crypto.c b/qemu/block/crypto.c new file mode 100644 index 000000000..1903e84fb --- /dev/null +++ b/qemu/block/crypto.c @@ -0,0 +1,586 @@ +/* + * QEMU block full disk encryption + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" + +#include "block/block_int.h" +#include "sysemu/block-backend.h" +#include "crypto/block.h" +#include "qapi/opts-visitor.h" +#include "qapi-visit.h" +#include "qapi/error.h" + +#define BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET "key-secret" +#define BLOCK_CRYPTO_OPT_LUKS_CIPHER_ALG "cipher-alg" +#define BLOCK_CRYPTO_OPT_LUKS_CIPHER_MODE "cipher-mode" +#define BLOCK_CRYPTO_OPT_LUKS_IVGEN_ALG "ivgen-alg" +#define BLOCK_CRYPTO_OPT_LUKS_IVGEN_HASH_ALG "ivgen-hash-alg" +#define BLOCK_CRYPTO_OPT_LUKS_HASH_ALG "hash-alg" + +typedef struct BlockCrypto BlockCrypto; + +struct BlockCrypto { + QCryptoBlock *block; +}; + + +static int block_crypto_probe_generic(QCryptoBlockFormat format, + const uint8_t *buf, + int buf_size, + const char *filename) +{ + if (qcrypto_block_has_format(format, buf, buf_size)) { + return 100; + } else { + return 0; + } +} + + +static ssize_t block_crypto_read_func(QCryptoBlock *block, + size_t offset, + uint8_t *buf, + size_t buflen, + Error **errp, + void *opaque) +{ + BlockDriverState *bs = opaque; + ssize_t ret; + + ret = bdrv_pread(bs->file->bs, offset, buf, buflen); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read encryption header"); + return ret; + } + return ret; +} + + +struct BlockCryptoCreateData { + const char *filename; + QemuOpts *opts; + BlockBackend *blk; + uint64_t size; +}; + + +static ssize_t block_crypto_write_func(QCryptoBlock *block, + size_t offset, + const uint8_t *buf, + size_t buflen, + Error **errp, + void *opaque) +{ + struct BlockCryptoCreateData *data = opaque; + ssize_t ret; + + ret = blk_pwrite(data->blk, offset, buf, buflen); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not write encryption header"); + return ret; + } + return ret; +} + + +static ssize_t block_crypto_init_func(QCryptoBlock *block, + size_t headerlen, + Error **errp, + void *opaque) +{ + struct BlockCryptoCreateData *data = opaque; + int ret; + + /* 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; +} + + +static QemuOptsList block_crypto_runtime_opts_luks = { + .name = "crypto", + .head = QTAILQ_HEAD_INITIALIZER(block_crypto_runtime_opts_luks.head), + .desc = { + { + .name = BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET, + .type = QEMU_OPT_STRING, + .help = "ID of the secret that provides the encryption key", + }, + { /* end of list */ } + }, +}; + + +static QemuOptsList block_crypto_create_opts_luks = { + .name = "crypto", + .head = QTAILQ_HEAD_INITIALIZER(block_crypto_create_opts_luks.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, + { + .name = BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET, + .type = QEMU_OPT_STRING, + .help = "ID of the secret that provides the encryption key", + }, + { + .name = BLOCK_CRYPTO_OPT_LUKS_CIPHER_ALG, + .type = QEMU_OPT_STRING, + .help = "Name of encryption cipher algorithm", + }, + { + .name = BLOCK_CRYPTO_OPT_LUKS_CIPHER_MODE, + .type = QEMU_OPT_STRING, + .help = "Name of encryption cipher mode", + }, + { + .name = BLOCK_CRYPTO_OPT_LUKS_IVGEN_ALG, + .type = QEMU_OPT_STRING, + .help = "Name of IV generator algorithm", + }, + { + .name = BLOCK_CRYPTO_OPT_LUKS_IVGEN_HASH_ALG, + .type = QEMU_OPT_STRING, + .help = "Name of IV generator hash algorithm", + }, + { + .name = BLOCK_CRYPTO_OPT_LUKS_HASH_ALG, + .type = QEMU_OPT_STRING, + .help = "Name of encryption hash algorithm", + }, + { /* end of list */ } + }, +}; + + +static QCryptoBlockOpenOptions * +block_crypto_open_opts_init(QCryptoBlockFormat format, + QemuOpts *opts, + Error **errp) +{ + OptsVisitor *ov; + QCryptoBlockOpenOptions *ret = NULL; + Error *local_err = NULL; + Error *end_err = NULL; + + ret = g_new0(QCryptoBlockOpenOptions, 1); + ret->format = format; + + ov = opts_visitor_new(opts); + + visit_start_struct(opts_get_visitor(ov), + NULL, NULL, 0, &local_err); + if (local_err) { + goto out; + } + + switch (format) { + case Q_CRYPTO_BLOCK_FORMAT_LUKS: + visit_type_QCryptoBlockOptionsLUKS_members( + opts_get_visitor(ov), &ret->u.luks, &local_err); + break; + + default: + error_setg(&local_err, "Unsupported block format %d", format); + break; + } + + visit_end_struct(opts_get_visitor(ov), &end_err); + error_propagate(&local_err, end_err); + + out: + if (local_err) { + error_propagate(errp, local_err); + qapi_free_QCryptoBlockOpenOptions(ret); + ret = NULL; + } + opts_visitor_cleanup(ov); + return ret; +} + + +static QCryptoBlockCreateOptions * +block_crypto_create_opts_init(QCryptoBlockFormat format, + QemuOpts *opts, + Error **errp) +{ + OptsVisitor *ov; + QCryptoBlockCreateOptions *ret = NULL; + Error *local_err = NULL; + Error *end_err = NULL; + + ret = g_new0(QCryptoBlockCreateOptions, 1); + ret->format = format; + + ov = opts_visitor_new(opts); + + visit_start_struct(opts_get_visitor(ov), + NULL, NULL, 0, &local_err); + if (local_err) { + goto out; + } + + switch (format) { + case Q_CRYPTO_BLOCK_FORMAT_LUKS: + visit_type_QCryptoBlockCreateOptionsLUKS_members( + opts_get_visitor(ov), &ret->u.luks, &local_err); + break; + + default: + error_setg(&local_err, "Unsupported block format %d", format); + break; + } + + visit_end_struct(opts_get_visitor(ov), &end_err); + error_propagate(&local_err, end_err); + + out: + if (local_err) { + error_propagate(errp, local_err); + qapi_free_QCryptoBlockCreateOptions(ret); + ret = NULL; + } + opts_visitor_cleanup(ov); + return ret; +} + + +static int block_crypto_open_generic(QCryptoBlockFormat format, + QemuOptsList *opts_spec, + BlockDriverState *bs, + QDict *options, + int flags, + Error **errp) +{ + BlockCrypto *crypto = bs->opaque; + QemuOpts *opts = NULL; + Error *local_err = NULL; + int ret = -EINVAL; + QCryptoBlockOpenOptions *open_opts = NULL; + unsigned int cflags = 0; + + opts = qemu_opts_create(opts_spec, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto cleanup; + } + + open_opts = block_crypto_open_opts_init(format, opts, errp); + if (!open_opts) { + goto cleanup; + } + + if (flags & BDRV_O_NO_IO) { + cflags |= QCRYPTO_BLOCK_OPEN_NO_IO; + } + crypto->block = qcrypto_block_open(open_opts, + block_crypto_read_func, + bs, + cflags, + errp); + + if (!crypto->block) { + ret = -EIO; + goto cleanup; + } + + bs->encrypted = 1; + bs->valid_key = 1; + + ret = 0; + cleanup: + qapi_free_QCryptoBlockOpenOptions(open_opts); + return ret; +} + + +static int block_crypto_create_generic(QCryptoBlockFormat format, + const char *filename, + QemuOpts *opts, + Error **errp) +{ + int ret = -EINVAL; + QCryptoBlockCreateOptions *create_opts = NULL; + 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, + }; + + create_opts = block_crypto_create_opts_init(format, opts, errp); + if (!create_opts) { + return -1; + } + + crypto = qcrypto_block_create(create_opts, + block_crypto_init_func, + block_crypto_write_func, + &data, + errp); + + if (!crypto) { + ret = -EIO; + goto cleanup; + } + + ret = 0; + cleanup: + qcrypto_block_free(crypto); + blk_unref(data.blk); + qapi_free_QCryptoBlockCreateOptions(create_opts); + return ret; +} + +static int block_crypto_truncate(BlockDriverState *bs, int64_t offset) +{ + BlockCrypto *crypto = bs->opaque; + size_t payload_offset = + qcrypto_block_get_payload_offset(crypto->block); + + offset += payload_offset; + + return bdrv_truncate(bs->file->bs, offset); +} + +static void block_crypto_close(BlockDriverState *bs) +{ + BlockCrypto *crypto = bs->opaque; + qcrypto_block_free(crypto->block); +} + + +#define BLOCK_CRYPTO_MAX_SECTORS 32 + +static coroutine_fn int +block_crypto_co_readv(BlockDriverState *bs, int64_t sector_num, + int remaining_sectors, QEMUIOVector *qiov) +{ + BlockCrypto *crypto = bs->opaque; + int cur_nr_sectors; /* number of sectors in current iteration */ + uint64_t bytes_done = 0; + uint8_t *cipher_data = NULL; + QEMUIOVector hd_qiov; + int ret = 0; + size_t payload_offset = + qcrypto_block_get_payload_offset(crypto->block) / 512; + + qemu_iovec_init(&hd_qiov, qiov->niov); + + /* Bounce buffer so we have a linear mem region for + * entire sector. XXX optimize so we avoid bounce + * buffer in case that qiov->niov == 1 + */ + cipher_data = + qemu_try_blockalign(bs->file->bs, MIN(BLOCK_CRYPTO_MAX_SECTORS * 512, + qiov->size)); + if (cipher_data == NULL) { + ret = -ENOMEM; + goto cleanup; + } + + while (remaining_sectors) { + cur_nr_sectors = remaining_sectors; + + if (cur_nr_sectors > BLOCK_CRYPTO_MAX_SECTORS) { + cur_nr_sectors = BLOCK_CRYPTO_MAX_SECTORS; + } + + qemu_iovec_reset(&hd_qiov); + qemu_iovec_add(&hd_qiov, cipher_data, cur_nr_sectors * 512); + + ret = bdrv_co_readv(bs->file->bs, + payload_offset + sector_num, + cur_nr_sectors, &hd_qiov); + if (ret < 0) { + goto cleanup; + } + + if (qcrypto_block_decrypt(crypto->block, + sector_num, + cipher_data, cur_nr_sectors * 512, + NULL) < 0) { + ret = -EIO; + goto cleanup; + } + + qemu_iovec_from_buf(qiov, bytes_done, + cipher_data, cur_nr_sectors * 512); + + remaining_sectors -= cur_nr_sectors; + sector_num += cur_nr_sectors; + bytes_done += cur_nr_sectors * 512; + } + + cleanup: + qemu_iovec_destroy(&hd_qiov); + qemu_vfree(cipher_data); + + return ret; +} + + +static coroutine_fn int +block_crypto_co_writev(BlockDriverState *bs, int64_t sector_num, + int remaining_sectors, QEMUIOVector *qiov) +{ + BlockCrypto *crypto = bs->opaque; + int cur_nr_sectors; /* number of sectors in current iteration */ + uint64_t bytes_done = 0; + uint8_t *cipher_data = NULL; + QEMUIOVector hd_qiov; + int ret = 0; + size_t payload_offset = + qcrypto_block_get_payload_offset(crypto->block) / 512; + + qemu_iovec_init(&hd_qiov, qiov->niov); + + /* Bounce buffer so we have a linear mem region for + * entire sector. XXX optimize so we avoid bounce + * buffer in case that qiov->niov == 1 + */ + cipher_data = + qemu_try_blockalign(bs->file->bs, MIN(BLOCK_CRYPTO_MAX_SECTORS * 512, + qiov->size)); + if (cipher_data == NULL) { + ret = -ENOMEM; + goto cleanup; + } + + while (remaining_sectors) { + cur_nr_sectors = remaining_sectors; + + if (cur_nr_sectors > BLOCK_CRYPTO_MAX_SECTORS) { + cur_nr_sectors = BLOCK_CRYPTO_MAX_SECTORS; + } + + qemu_iovec_to_buf(qiov, bytes_done, + cipher_data, cur_nr_sectors * 512); + + if (qcrypto_block_encrypt(crypto->block, + sector_num, + cipher_data, cur_nr_sectors * 512, + NULL) < 0) { + ret = -EIO; + goto cleanup; + } + + qemu_iovec_reset(&hd_qiov); + qemu_iovec_add(&hd_qiov, cipher_data, cur_nr_sectors * 512); + + ret = bdrv_co_writev(bs->file->bs, + payload_offset + sector_num, + cur_nr_sectors, &hd_qiov); + if (ret < 0) { + goto cleanup; + } + + remaining_sectors -= cur_nr_sectors; + sector_num += cur_nr_sectors; + bytes_done += cur_nr_sectors * 512; + } + + cleanup: + qemu_iovec_destroy(&hd_qiov); + qemu_vfree(cipher_data); + + return ret; +} + + +static int64_t block_crypto_getlength(BlockDriverState *bs) +{ + BlockCrypto *crypto = bs->opaque; + int64_t len = bdrv_getlength(bs->file->bs); + + ssize_t offset = qcrypto_block_get_payload_offset(crypto->block); + + len -= offset; + + return len; +} + + +static int block_crypto_probe_luks(const uint8_t *buf, + int buf_size, + const char *filename) { + return block_crypto_probe_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS, + buf, buf_size, filename); +} + +static int block_crypto_open_luks(BlockDriverState *bs, + QDict *options, + int flags, + Error **errp) +{ + return block_crypto_open_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS, + &block_crypto_runtime_opts_luks, + bs, options, flags, errp); +} + +static int block_crypto_create_luks(const char *filename, + QemuOpts *opts, + Error **errp) +{ + return block_crypto_create_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS, + filename, opts, errp); +} + +BlockDriver bdrv_crypto_luks = { + .format_name = "luks", + .instance_size = sizeof(BlockCrypto), + .bdrv_probe = block_crypto_probe_luks, + .bdrv_open = block_crypto_open_luks, + .bdrv_close = block_crypto_close, + .bdrv_create = block_crypto_create_luks, + .bdrv_truncate = block_crypto_truncate, + .create_opts = &block_crypto_create_opts_luks, + + .bdrv_co_readv = block_crypto_co_readv, + .bdrv_co_writev = block_crypto_co_writev, + .bdrv_getlength = block_crypto_getlength, +}; + +static void block_crypto_init(void) +{ + bdrv_register(&bdrv_crypto_luks); +} + +block_init(block_crypto_init); diff --git a/qemu/block/curl.c b/qemu/block/curl.c index 032cc8ae2..5a8f8b623 100644 --- a/qemu/block/curl.c +++ b/qemu/block/curl.c @@ -21,12 +21,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/error-report.h" #include "block/block_int.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qstring.h" +#include "crypto/secret.h" #include +#include "qemu/cutils.h" // #define DEBUG_CURL // #define DEBUG_VERBOSE @@ -77,6 +81,10 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle, #define CURL_BLOCK_OPT_SSLVERIFY "sslverify" #define CURL_BLOCK_OPT_TIMEOUT "timeout" #define CURL_BLOCK_OPT_COOKIE "cookie" +#define CURL_BLOCK_OPT_USERNAME "username" +#define CURL_BLOCK_OPT_PASSWORD_SECRET "password-secret" +#define CURL_BLOCK_OPT_PROXY_USERNAME "proxy-username" +#define CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET "proxy-password-secret" struct BDRVCURLState; @@ -119,6 +127,10 @@ typedef struct BDRVCURLState { char *cookie; bool accept_range; AioContext *aio_context; + char *username; + char *password; + char *proxyusername; + char *proxypassword; } BDRVCURLState; static void curl_clean_state(CURLState *s); @@ -154,18 +166,20 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, fd); switch (action) { case CURL_POLL_IN: - aio_set_fd_handler(s->aio_context, fd, curl_multi_read, - NULL, state); + aio_set_fd_handler(s->aio_context, fd, false, + curl_multi_read, NULL, state); break; case CURL_POLL_OUT: - aio_set_fd_handler(s->aio_context, fd, NULL, curl_multi_do, state); + aio_set_fd_handler(s->aio_context, fd, false, + NULL, curl_multi_do, state); break; case CURL_POLL_INOUT: - aio_set_fd_handler(s->aio_context, fd, curl_multi_read, - curl_multi_do, state); + aio_set_fd_handler(s->aio_context, fd, false, + curl_multi_read, curl_multi_do, state); break; case CURL_POLL_REMOVE: - aio_set_fd_handler(s->aio_context, fd, NULL, NULL, NULL); + aio_set_fd_handler(s->aio_context, fd, false, + NULL, NULL, NULL); break; } @@ -416,6 +430,21 @@ static CURLState *curl_init_state(BlockDriverState *bs, BDRVCURLState *s) curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg); curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1); + if (s->username) { + curl_easy_setopt(state->curl, CURLOPT_USERNAME, s->username); + } + if (s->password) { + curl_easy_setopt(state->curl, CURLOPT_PASSWORD, s->password); + } + if (s->proxyusername) { + curl_easy_setopt(state->curl, + CURLOPT_PROXYUSERNAME, s->proxyusername); + } + if (s->proxypassword) { + curl_easy_setopt(state->curl, + CURLOPT_PROXYPASSWORD, s->proxypassword); + } + /* Restrict supported protocols to avoid security issues in the more * obscure protocols. For example, do not allow POP3/SMTP/IMAP see * CVE-2013-0249. @@ -522,10 +551,31 @@ static QemuOptsList runtime_opts = { .type = QEMU_OPT_STRING, .help = "Pass the cookie or list of cookies with each request" }, + { + .name = CURL_BLOCK_OPT_USERNAME, + .type = QEMU_OPT_STRING, + .help = "Username for HTTP auth" + }, + { + .name = CURL_BLOCK_OPT_PASSWORD_SECRET, + .type = QEMU_OPT_STRING, + .help = "ID of secret used as password for HTTP auth", + }, + { + .name = CURL_BLOCK_OPT_PROXY_USERNAME, + .type = QEMU_OPT_STRING, + .help = "Username for HTTP proxy auth" + }, + { + .name = CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET, + .type = QEMU_OPT_STRING, + .help = "ID of secret used as password for HTTP proxy auth", + }, { /* end of list */ } }, }; + static int curl_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { @@ -536,6 +586,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, const char *file; const char *cookie; double d; + const char *secretid; static int inited = 0; @@ -577,6 +628,26 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, goto out_noclean; } + s->username = g_strdup(qemu_opt_get(opts, CURL_BLOCK_OPT_USERNAME)); + secretid = qemu_opt_get(opts, CURL_BLOCK_OPT_PASSWORD_SECRET); + + if (secretid) { + s->password = qcrypto_secret_lookup_as_utf8(secretid, errp); + if (!s->password) { + goto out_noclean; + } + } + + s->proxyusername = g_strdup( + qemu_opt_get(opts, CURL_BLOCK_OPT_PROXY_USERNAME)); + secretid = qemu_opt_get(opts, CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET); + if (secretid) { + s->proxypassword = qcrypto_secret_lookup_as_utf8(secretid, errp); + if (!s->proxypassword) { + goto out_noclean; + } + } + if (!inited) { curl_global_init(CURL_GLOBAL_ALL); inited = 1; diff --git a/qemu/block/dirty-bitmap.c b/qemu/block/dirty-bitmap.c new file mode 100644 index 000000000..4902ca557 --- /dev/null +++ b/qemu/block/dirty-bitmap.c @@ -0,0 +1,387 @@ +/* + * Block Dirty Bitmap + * + * Copyright (c) 2016 Red Hat. Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "trace.h" +#include "block/block_int.h" +#include "block/blockjob.h" + +/** + * A BdrvDirtyBitmap can be in three possible states: + * (1) successor is NULL and disabled is false: full r/w mode + * (2) successor is NULL and disabled is true: read only mode ("disabled") + * (3) successor is set: frozen mode. + * A frozen bitmap cannot be renamed, deleted, anonymized, cleared, set, + * or enabled. A frozen bitmap can only abdicate() or reclaim(). + */ +struct BdrvDirtyBitmap { + HBitmap *bitmap; /* Dirty sector bitmap implementation */ + BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */ + char *name; /* Optional non-empty unique ID */ + int64_t size; /* Size of the bitmap (Number of sectors) */ + bool disabled; /* Bitmap is read-only */ + QLIST_ENTRY(BdrvDirtyBitmap) list; +}; + +BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name) +{ + BdrvDirtyBitmap *bm; + + assert(name); + QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { + if (bm->name && !strcmp(name, bm->name)) { + return bm; + } + } + return NULL; +} + +void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap) +{ + assert(!bdrv_dirty_bitmap_frozen(bitmap)); + g_free(bitmap->name); + bitmap->name = NULL; +} + +BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, + uint32_t granularity, + const char *name, + Error **errp) +{ + int64_t bitmap_size; + BdrvDirtyBitmap *bitmap; + uint32_t sector_granularity; + + assert((granularity & (granularity - 1)) == 0); + + if (name && bdrv_find_dirty_bitmap(bs, name)) { + error_setg(errp, "Bitmap already exists: %s", name); + return NULL; + } + sector_granularity = granularity >> BDRV_SECTOR_BITS; + assert(sector_granularity); + bitmap_size = bdrv_nb_sectors(bs); + if (bitmap_size < 0) { + error_setg_errno(errp, -bitmap_size, "could not get length of device"); + errno = -bitmap_size; + return NULL; + } + bitmap = g_new0(BdrvDirtyBitmap, 1); + bitmap->bitmap = hbitmap_alloc(bitmap_size, ctz32(sector_granularity)); + bitmap->size = bitmap_size; + bitmap->name = g_strdup(name); + bitmap->disabled = false; + QLIST_INSERT_HEAD(&bs->dirty_bitmaps, bitmap, list); + return bitmap; +} + +bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap) +{ + return bitmap->successor; +} + +bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) +{ + return !(bitmap->disabled || bitmap->successor); +} + +DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) +{ + if (bdrv_dirty_bitmap_frozen(bitmap)) { + return DIRTY_BITMAP_STATUS_FROZEN; + } else if (!bdrv_dirty_bitmap_enabled(bitmap)) { + return DIRTY_BITMAP_STATUS_DISABLED; + } else { + return DIRTY_BITMAP_STATUS_ACTIVE; + } +} + +/** + * Create a successor bitmap destined to replace this bitmap after an operation. + * Requires that the bitmap is not frozen and has no successor. + */ +int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, Error **errp) +{ + uint64_t granularity; + BdrvDirtyBitmap *child; + + if (bdrv_dirty_bitmap_frozen(bitmap)) { + error_setg(errp, "Cannot create a successor for a bitmap that is " + "currently frozen"); + return -1; + } + assert(!bitmap->successor); + + /* Create an anonymous successor */ + granularity = bdrv_dirty_bitmap_granularity(bitmap); + child = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp); + if (!child) { + return -1; + } + + /* Successor will be on or off based on our current state. */ + child->disabled = bitmap->disabled; + + /* Install the successor and freeze the parent */ + bitmap->successor = child; + return 0; +} + +/** + * For a bitmap with a successor, yield our name to the successor, + * delete the old bitmap, and return a handle to the new bitmap. + */ +BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, + Error **errp) +{ + char *name; + BdrvDirtyBitmap *successor = bitmap->successor; + + if (successor == NULL) { + error_setg(errp, "Cannot relinquish control if " + "there's no successor present"); + return NULL; + } + + name = bitmap->name; + bitmap->name = NULL; + successor->name = name; + bitmap->successor = NULL; + bdrv_release_dirty_bitmap(bs, bitmap); + + return successor; +} + +/** + * 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. + */ +BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, + BdrvDirtyBitmap *parent, + Error **errp) +{ + BdrvDirtyBitmap *successor = parent->successor; + + if (!successor) { + error_setg(errp, "Cannot reclaim a successor when none is present"); + return NULL; + } + + if (!hbitmap_merge(parent->bitmap, successor->bitmap)) { + error_setg(errp, "Merging of parent and successor bitmap failed"); + return NULL; + } + bdrv_release_dirty_bitmap(bs, successor); + parent->successor = NULL; + + return parent; +} + +/** + * Truncates _all_ bitmaps attached to a BDS. + */ +void bdrv_dirty_bitmap_truncate(BlockDriverState *bs) +{ + BdrvDirtyBitmap *bitmap; + uint64_t size = bdrv_nb_sectors(bs); + + QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { + assert(!bdrv_dirty_bitmap_frozen(bitmap)); + hbitmap_truncate(bitmap->bitmap, size); + bitmap->size = size; + } +} + +static void bdrv_do_release_matching_dirty_bitmap(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, + bool only_named) +{ + BdrvDirtyBitmap *bm, *next; + QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { + if ((!bitmap || bm == bitmap) && (!only_named || bm->name)) { + assert(!bdrv_dirty_bitmap_frozen(bm)); + QLIST_REMOVE(bm, list); + hbitmap_free(bm->bitmap); + g_free(bm->name); + g_free(bm); + + if (bitmap) { + return; + } + } + } +} + +void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) +{ + bdrv_do_release_matching_dirty_bitmap(bs, bitmap, false); +} + +/** + * Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()). + * There must not be any frozen bitmaps attached. + */ +void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) +{ + bdrv_do_release_matching_dirty_bitmap(bs, NULL, true); +} + +void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) +{ + assert(!bdrv_dirty_bitmap_frozen(bitmap)); + bitmap->disabled = true; +} + +void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap) +{ + assert(!bdrv_dirty_bitmap_frozen(bitmap)); + bitmap->disabled = false; +} + +BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) +{ + BdrvDirtyBitmap *bm; + BlockDirtyInfoList *list = NULL; + BlockDirtyInfoList **plist = &list; + + QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { + BlockDirtyInfo *info = g_new0(BlockDirtyInfo, 1); + BlockDirtyInfoList *entry = g_new0(BlockDirtyInfoList, 1); + info->count = bdrv_get_dirty_count(bm); + info->granularity = bdrv_dirty_bitmap_granularity(bm); + info->has_name = !!bm->name; + info->name = g_strdup(bm->name); + info->status = bdrv_dirty_bitmap_status(bm); + entry->value = info; + *plist = entry; + plist = &entry->next; + } + + return list; +} + +int bdrv_get_dirty(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, + int64_t sector) +{ + if (bitmap) { + return hbitmap_get(bitmap->bitmap, sector); + } else { + return 0; + } +} + +/** + * Chooses a default granularity based on the existing cluster size, + * but clamped between [4K, 64K]. Defaults to 64K in the case that there + * is no cluster size information available. + */ +uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs) +{ + BlockDriverInfo bdi; + uint32_t granularity; + + if (bdrv_get_info(bs, &bdi) >= 0 && bdi.cluster_size > 0) { + granularity = MAX(4096, bdi.cluster_size); + granularity = MIN(65536, granularity); + } else { + granularity = 65536; + } + + return granularity; +} + +uint32_t bdrv_dirty_bitmap_granularity(BdrvDirtyBitmap *bitmap) +{ + return BDRV_SECTOR_SIZE << hbitmap_granularity(bitmap->bitmap); +} + +void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, HBitmapIter *hbi) +{ + hbitmap_iter_init(hbi, bitmap->bitmap, 0); +} + +void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, + int64_t cur_sector, int nr_sectors) +{ + assert(bdrv_dirty_bitmap_enabled(bitmap)); + hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors); +} + +void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, + int64_t cur_sector, int nr_sectors) +{ + assert(bdrv_dirty_bitmap_enabled(bitmap)); + hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors); +} + +void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) +{ + assert(bdrv_dirty_bitmap_enabled(bitmap)); + if (!out) { + hbitmap_reset_all(bitmap->bitmap); + } else { + HBitmap *backup = bitmap->bitmap; + bitmap->bitmap = hbitmap_alloc(bitmap->size, + hbitmap_granularity(backup)); + *out = backup; + } +} + +void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in) +{ + HBitmap *tmp = bitmap->bitmap; + assert(bdrv_dirty_bitmap_enabled(bitmap)); + bitmap->bitmap = in; + hbitmap_free(tmp); +} + +void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, + int nr_sectors) +{ + BdrvDirtyBitmap *bitmap; + QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { + if (!bdrv_dirty_bitmap_enabled(bitmap)) { + continue; + } + hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors); + } +} + +/** + * Advance an HBitmapIter to an arbitrary offset. + */ +void bdrv_set_dirty_iter(HBitmapIter *hbi, int64_t offset) +{ + assert(hbi->hb); + hbitmap_iter_init(hbi, hbi->hb, offset); +} + +int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap) +{ + return hbitmap_count(bitmap->bitmap); +} diff --git a/qemu/block/dmg.c b/qemu/block/dmg.c index 9f2528169..a496eb7c9 100644 --- a/qemu/block/dmg.c +++ b/qemu/block/dmg.c @@ -21,6 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "block/block_int.h" #include "qemu/bswap.h" @@ -85,7 +87,7 @@ static int read_uint64(BlockDriverState *bs, int64_t offset, uint64_t *result) uint64_t buffer; int ret; - ret = bdrv_pread(bs->file, offset, &buffer, 8); + ret = bdrv_pread(bs->file->bs, offset, &buffer, 8); if (ret < 0) { return ret; } @@ -99,7 +101,7 @@ static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result) uint32_t buffer; int ret; - ret = bdrv_pread(bs->file, offset, &buffer, 4); + ret = bdrv_pread(bs->file->bs, offset, &buffer, 4); if (ret < 0) { return ret; } @@ -354,7 +356,7 @@ static int dmg_read_resource_fork(BlockDriverState *bs, DmgHeaderState *ds, offset += 4; buffer = g_realloc(buffer, count); - ret = bdrv_pread(bs->file, offset, buffer, count); + ret = bdrv_pread(bs->file->bs, offset, buffer, count); if (ret < 0) { goto fail; } @@ -391,7 +393,7 @@ static int dmg_read_plist_xml(BlockDriverState *bs, DmgHeaderState *ds, buffer = g_malloc(info_length + 1); buffer[info_length] = '\0'; - ret = bdrv_pread(bs->file, info_begin, buffer, info_length); + ret = bdrv_pread(bs->file->bs, info_begin, buffer, info_length); if (ret != info_length) { ret = -EINVAL; goto fail; @@ -446,7 +448,7 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, ds.max_sectors_per_chunk = 1; /* locate the UDIF trailer */ - offset = dmg_find_koly_offset(bs->file, errp); + offset = dmg_find_koly_offset(bs->file->bs, errp); if (offset < 0) { ret = offset; goto fail; @@ -514,9 +516,9 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, } /* initialize zlib engine */ - s->compressed_chunk = qemu_try_blockalign(bs->file, + s->compressed_chunk = qemu_try_blockalign(bs->file->bs, ds.max_compressed_size + 1); - s->uncompressed_chunk = qemu_try_blockalign(bs->file, + s->uncompressed_chunk = qemu_try_blockalign(bs->file->bs, 512 * ds.max_sectors_per_chunk); if (s->compressed_chunk == NULL || s->uncompressed_chunk == NULL) { ret = -ENOMEM; @@ -592,7 +594,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) case 0x80000005: { /* zlib compressed */ /* we need to buffer, because only the chunk as whole can be * inflated. */ - ret = bdrv_pread(bs->file, s->offsets[chunk], + ret = bdrv_pread(bs->file->bs, s->offsets[chunk], s->compressed_chunk, s->lengths[chunk]); if (ret != s->lengths[chunk]) { return -1; @@ -616,7 +618,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) case 0x80000006: /* bzip2 compressed */ /* we need to buffer, because only the chunk as whole can be * inflated. */ - ret = bdrv_pread(bs->file, s->offsets[chunk], + ret = bdrv_pread(bs->file->bs, s->offsets[chunk], s->compressed_chunk, s->lengths[chunk]); if (ret != s->lengths[chunk]) { return -1; @@ -641,7 +643,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) break; #endif /* CONFIG_BZIP2 */ case 1: /* copy */ - ret = bdrv_pread(bs->file, s->offsets[chunk], + ret = bdrv_pread(bs->file->bs, s->offsets[chunk], s->uncompressed_chunk, s->lengths[chunk]); if (ret != s->lengths[chunk]) { return -1; diff --git a/qemu/block/gluster.c b/qemu/block/gluster.c index 1eb3a8c39..a8aaacf64 100644 --- a/qemu/block/gluster.c +++ b/qemu/block/gluster.c @@ -7,8 +7,10 @@ * See the COPYING file in the top-level directory. * */ +#include "qemu/osdep.h" #include #include "block/block_int.h" +#include "qapi/error.h" #include "qemu/uri.h" typedef struct GlusterAIOCB { @@ -245,7 +247,7 @@ static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg) if (!ret || ret == acb->size) { acb->ret = 0; /* Success */ } else if (ret < 0) { - acb->ret = ret; /* Read/Write failed */ + acb->ret = -errno; /* Read/Write failed */ } else { acb->ret = -EIO; /* Partial read/write - fail it */ } @@ -312,6 +314,23 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options, goto out; } +#ifdef CONFIG_GLUSTERFS_XLATOR_OPT + /* Without this, if fsync fails for a recoverable reason (for instance, + * ENOSPC), gluster will dump its cache, preventing retries. This means + * almost certain data loss. Not all gluster versions support the + * 'resync-failed-syncs-after-fsync' key value, but there is no way to + * discover during runtime if it is supported (this api returns success for + * unknown key/value pairs) */ + ret = glfs_set_xlator_option(s->glfs, "*-write-behind", + "resync-failed-syncs-after-fsync", + "on"); + if (ret < 0) { + error_setg_errno(errp, errno, "Unable to set xlator key/value pair"); + ret = -errno; + goto out; + } +#endif + qemu_gluster_parse_flags(bdrv_flags, &open_flags); s->fd = glfs_open(s->glfs, gconf->image, open_flags); @@ -364,6 +383,16 @@ static int qemu_gluster_reopen_prepare(BDRVReopenState *state, goto exit; } +#ifdef CONFIG_GLUSTERFS_XLATOR_OPT + ret = glfs_set_xlator_option(reop_s->glfs, "*-write-behind", + "resync-failed-syncs-after-fsync", "on"); + if (ret < 0) { + error_setg_errno(errp, errno, "Unable to set xlator key/value pair"); + ret = -errno; + goto exit; + } +#endif + reop_s->fd = glfs_open(reop_s->glfs, gconf->image, open_flags); if (reop_s->fd == NULL) { /* reops->glfs will be cleaned up in _abort */ @@ -429,28 +458,23 @@ static coroutine_fn int qemu_gluster_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, int nb_sectors, BdrvRequestFlags flags) { int ret; - GlusterAIOCB *acb = g_slice_new(GlusterAIOCB); + GlusterAIOCB acb; BDRVGlusterState *s = bs->opaque; off_t size = nb_sectors * BDRV_SECTOR_SIZE; off_t offset = sector_num * BDRV_SECTOR_SIZE; - acb->size = size; - acb->ret = 0; - acb->coroutine = qemu_coroutine_self(); - acb->aio_context = bdrv_get_aio_context(bs); + acb.size = size; + acb.ret = 0; + acb.coroutine = qemu_coroutine_self(); + acb.aio_context = bdrv_get_aio_context(bs); - ret = glfs_zerofill_async(s->fd, offset, size, &gluster_finish_aiocb, acb); + ret = glfs_zerofill_async(s->fd, offset, size, gluster_finish_aiocb, &acb); if (ret < 0) { - ret = -errno; - goto out; + return -errno; } qemu_coroutine_yield(); - ret = acb->ret; - -out: - g_slice_free(GlusterAIOCB, acb); - return ret; + return acb.ret; } static inline bool gluster_supports_zerofill(void) @@ -541,35 +565,30 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int write) { int ret; - GlusterAIOCB *acb = g_slice_new(GlusterAIOCB); + GlusterAIOCB acb; BDRVGlusterState *s = bs->opaque; size_t size = nb_sectors * BDRV_SECTOR_SIZE; off_t offset = sector_num * BDRV_SECTOR_SIZE; - acb->size = size; - acb->ret = 0; - acb->coroutine = qemu_coroutine_self(); - acb->aio_context = bdrv_get_aio_context(bs); + acb.size = size; + acb.ret = 0; + acb.coroutine = qemu_coroutine_self(); + acb.aio_context = bdrv_get_aio_context(bs); if (write) { ret = glfs_pwritev_async(s->fd, qiov->iov, qiov->niov, offset, 0, - &gluster_finish_aiocb, acb); + gluster_finish_aiocb, &acb); } else { ret = glfs_preadv_async(s->fd, qiov->iov, qiov->niov, offset, 0, - &gluster_finish_aiocb, acb); + gluster_finish_aiocb, &acb); } if (ret < 0) { - ret = -errno; - goto out; + return -errno; } qemu_coroutine_yield(); - ret = acb->ret; - -out: - g_slice_free(GlusterAIOCB, acb); - return ret; + return acb.ret; } static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset) @@ -597,28 +616,58 @@ static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs, return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1); } +static void qemu_gluster_close(BlockDriverState *bs) +{ + BDRVGlusterState *s = bs->opaque; + + if (s->fd) { + glfs_close(s->fd); + s->fd = NULL; + } + glfs_fini(s->glfs); +} + static coroutine_fn int qemu_gluster_co_flush_to_disk(BlockDriverState *bs) { int ret; - GlusterAIOCB *acb = g_slice_new(GlusterAIOCB); + GlusterAIOCB acb; BDRVGlusterState *s = bs->opaque; - acb->size = 0; - acb->ret = 0; - acb->coroutine = qemu_coroutine_self(); - acb->aio_context = bdrv_get_aio_context(bs); + acb.size = 0; + acb.ret = 0; + acb.coroutine = qemu_coroutine_self(); + acb.aio_context = bdrv_get_aio_context(bs); - ret = glfs_fsync_async(s->fd, &gluster_finish_aiocb, acb); + ret = glfs_fsync_async(s->fd, gluster_finish_aiocb, &acb); if (ret < 0) { ret = -errno; - goto out; + goto error; } qemu_coroutine_yield(); - ret = acb->ret; - -out: - g_slice_free(GlusterAIOCB, acb); + if (acb.ret < 0) { + ret = acb.ret; + goto error; + } + + return acb.ret; + +error: + /* Some versions of Gluster (3.5.6 -> 3.5.8?) will not retain its cache + * after a fsync failure, so we have no way of allowing the guest to safely + * continue. Gluster versions prior to 3.5.6 don't retain the cache + * either, but will invalidate the fd on error, so this is again our only + * option. + * + * The 'resync-failed-syncs-after-fsync' xlator option for the + * write-behind cache will cause later gluster versions to retain its + * cache after error, so long as the fd remains open. However, we + * currently have no way of knowing if this option is supported. + * + * TODO: Once gluster provides a way for us to determine if the option + * is supported, bypass the closure and setting drv to NULL. */ + qemu_gluster_close(bs); + bs->drv = NULL; return ret; } @@ -627,28 +676,23 @@ static coroutine_fn int qemu_gluster_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) { int ret; - GlusterAIOCB *acb = g_slice_new(GlusterAIOCB); + GlusterAIOCB acb; BDRVGlusterState *s = bs->opaque; size_t size = nb_sectors * BDRV_SECTOR_SIZE; off_t offset = sector_num * BDRV_SECTOR_SIZE; - acb->size = 0; - acb->ret = 0; - acb->coroutine = qemu_coroutine_self(); - acb->aio_context = bdrv_get_aio_context(bs); + acb.size = 0; + acb.ret = 0; + acb.coroutine = qemu_coroutine_self(); + acb.aio_context = bdrv_get_aio_context(bs); - ret = glfs_discard_async(s->fd, offset, size, &gluster_finish_aiocb, acb); + ret = glfs_discard_async(s->fd, offset, size, gluster_finish_aiocb, &acb); if (ret < 0) { - ret = -errno; - goto out; + return -errno; } qemu_coroutine_yield(); - ret = acb->ret; - -out: - g_slice_free(GlusterAIOCB, acb); - return ret; + return acb.ret; } #endif @@ -679,17 +723,6 @@ static int64_t qemu_gluster_allocated_file_size(BlockDriverState *bs) } } -static void qemu_gluster_close(BlockDriverState *bs) -{ - BDRVGlusterState *s = bs->opaque; - - if (s->fd) { - glfs_close(s->fd); - s->fd = NULL; - } - glfs_fini(s->glfs); -} - static int qemu_gluster_has_zero_init(BlockDriverState *bs) { /* GlusterFS volume could be backed by a block device */ diff --git a/qemu/block/io.c b/qemu/block/io.c index d4bc83b33..a7dbf85b1 100644 --- a/qemu/block/io.c +++ b/qemu/block/io.c @@ -22,10 +22,14 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "trace.h" +#include "sysemu/block-backend.h" #include "block/blockjob.h" #include "block/block_int.h" #include "block/throttle-groups.h" +#include "qemu/cutils.h" +#include "qapi/error.h" #include "qemu/error-report.h" #define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ @@ -42,12 +46,6 @@ static int coroutine_fn bdrv_co_readv_em(BlockDriverState *bs, static int coroutine_fn bdrv_co_writev_em(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *iov); -static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs, - int64_t offset, unsigned int bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags); -static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, - int64_t offset, unsigned int bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags); static BlockAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, @@ -156,38 +154,45 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) /* Take some limits from the children as a default */ if (bs->file) { - bdrv_refresh_limits(bs->file, &local_err); + bdrv_refresh_limits(bs->file->bs, &local_err); if (local_err) { error_propagate(errp, local_err); return; } - bs->bl.opt_transfer_length = bs->file->bl.opt_transfer_length; - bs->bl.max_transfer_length = bs->file->bl.max_transfer_length; - bs->bl.min_mem_alignment = bs->file->bl.min_mem_alignment; - bs->bl.opt_mem_alignment = bs->file->bl.opt_mem_alignment; + bs->bl.opt_transfer_length = bs->file->bs->bl.opt_transfer_length; + bs->bl.max_transfer_length = bs->file->bs->bl.max_transfer_length; + bs->bl.min_mem_alignment = bs->file->bs->bl.min_mem_alignment; + bs->bl.opt_mem_alignment = bs->file->bs->bl.opt_mem_alignment; + bs->bl.max_iov = bs->file->bs->bl.max_iov; } else { bs->bl.min_mem_alignment = 512; bs->bl.opt_mem_alignment = getpagesize(); + + /* Safe default since most protocols use readv()/writev()/etc */ + bs->bl.max_iov = IOV_MAX; } - if (bs->backing_hd) { - bdrv_refresh_limits(bs->backing_hd, &local_err); + if (bs->backing) { + bdrv_refresh_limits(bs->backing->bs, &local_err); if (local_err) { error_propagate(errp, local_err); return; } bs->bl.opt_transfer_length = MAX(bs->bl.opt_transfer_length, - bs->backing_hd->bl.opt_transfer_length); + bs->backing->bs->bl.opt_transfer_length); bs->bl.max_transfer_length = MIN_NON_ZERO(bs->bl.max_transfer_length, - bs->backing_hd->bl.max_transfer_length); + bs->backing->bs->bl.max_transfer_length); bs->bl.opt_mem_alignment = MAX(bs->bl.opt_mem_alignment, - bs->backing_hd->bl.opt_mem_alignment); + bs->backing->bs->bl.opt_mem_alignment); bs->bl.min_mem_alignment = MAX(bs->bl.min_mem_alignment, - bs->backing_hd->bl.min_mem_alignment); + bs->backing->bs->bl.min_mem_alignment); + bs->bl.max_iov = + MIN(bs->bl.max_iov, + bs->backing->bs->bl.max_iov); } /* Then let the driver override it */ @@ -213,8 +218,10 @@ void bdrv_disable_copy_on_read(BlockDriverState *bs) } /* Check if any requests are in-flight (including throttled requests) */ -static bool bdrv_requests_pending(BlockDriverState *bs) +bool bdrv_requests_pending(BlockDriverState *bs) { + BdrvChild *child; + if (!QLIST_EMPTY(&bs->tracked_requests)) { return true; } @@ -224,17 +231,72 @@ static bool bdrv_requests_pending(BlockDriverState *bs) if (!qemu_co_queue_empty(&bs->throttled_reqs[1])) { return true; } - if (bs->file && bdrv_requests_pending(bs->file)) { - return true; - } - if (bs->backing_hd && bdrv_requests_pending(bs->backing_hd)) { - return true; + + QLIST_FOREACH(child, &bs->children, next) { + if (bdrv_requests_pending(child->bs)) { + return true; + } } + return false; } +static void bdrv_drain_recurse(BlockDriverState *bs) +{ + BdrvChild *child; + + if (bs->drv && bs->drv->bdrv_drain) { + bs->drv->bdrv_drain(bs); + } + QLIST_FOREACH(child, &bs->children, next) { + bdrv_drain_recurse(child->bs); + } +} + +typedef struct { + Coroutine *co; + BlockDriverState *bs; + QEMUBH *bh; + bool done; +} BdrvCoDrainData; + +static void bdrv_co_drain_bh_cb(void *opaque) +{ + BdrvCoDrainData *data = opaque; + Coroutine *co = data->co; + + qemu_bh_delete(data->bh); + bdrv_drain(data->bs); + data->done = true; + qemu_coroutine_enter(co, NULL); +} + +void coroutine_fn bdrv_co_drain(BlockDriverState *bs) +{ + BdrvCoDrainData data; + + /* Calling bdrv_drain() from a BH ensures the current coroutine yields and + * other coroutines run if they were queued from + * qemu_co_queue_run_restart(). */ + + assert(qemu_in_coroutine()); + data = (BdrvCoDrainData) { + .co = qemu_coroutine_self(), + .bs = bs, + .done = false, + .bh = aio_bh_new(bdrv_get_aio_context(bs), bdrv_co_drain_bh_cb, &data), + }; + qemu_bh_schedule(data.bh); + + qemu_coroutine_yield(); + /* If we are resumed from some other event (such as an aio completion or a + * timer callback), it is a bug in the caller that should be fixed. */ + assert(data.done); +} + /* - * Wait for pending requests to complete on a single BlockDriverState subtree + * Wait for pending requests to complete on a single BlockDriverState subtree, + * and suspend block driver's internal I/O until next request arrives. * * Note that unlike bdrv_drain_all(), the caller must hold the BlockDriverState * AioContext. @@ -247,6 +309,11 @@ void bdrv_drain(BlockDriverState *bs) { bool busy = true; + bdrv_drain_recurse(bs); + if (qemu_in_coroutine()) { + bdrv_co_drain(bs); + return; + } while (busy) { /* Keep iterating */ bdrv_flush_io_queue(bs); @@ -275,6 +342,7 @@ void bdrv_drain_all(void) if (bs->job) { block_job_pause(bs->job); } + bdrv_drain_recurse(bs); aio_context_release(aio_context); if (!g_slist_find(aio_ctxs, aio_context)) { @@ -344,13 +412,14 @@ static void tracked_request_end(BdrvTrackedRequest *req) static void tracked_request_begin(BdrvTrackedRequest *req, BlockDriverState *bs, int64_t offset, - unsigned int bytes, bool is_write) + unsigned int bytes, + enum BdrvTrackedRequestType type) { *req = (BdrvTrackedRequest){ .bs = bs, .offset = offset, .bytes = bytes, - .is_write = is_write, + .type = type, .co = qemu_coroutine_self(), .serialising = false, .overlap_offset = offset, @@ -593,20 +662,6 @@ int bdrv_read(BlockDriverState *bs, int64_t sector_num, return bdrv_rw_co(bs, sector_num, buf, nb_sectors, false, 0); } -/* Just like bdrv_read(), but with I/O throttling temporarily disabled */ -int bdrv_read_unthrottled(BlockDriverState *bs, int64_t sector_num, - uint8_t *buf, int nb_sectors) -{ - bool enabled; - int ret; - - enabled = bs->io_limits_enabled; - bs->io_limits_enabled = false; - ret = bdrv_read(bs, sector_num, buf, nb_sectors); - bs->io_limits_enabled = enabled; - return ret; -} - /* Return < 0 if error. Important errors are: -EIO generic I/O error (may happen for all errors) -ENOMEDIUM No media inserted. @@ -637,6 +692,7 @@ int bdrv_write_zeroes(BlockDriverState *bs, int64_t sector_num, int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags) { int64_t target_sectors, ret, nb_sectors, sector_num = 0; + BlockDriverState *file; int n; target_sectors = bdrv_nb_sectors(bs); @@ -649,7 +705,7 @@ int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags) if (nb_sectors <= 0) { return 0; } - ret = bdrv_get_block_status(bs, sector_num, nb_sectors, &n); + ret = bdrv_get_block_status(bs, sector_num, nb_sectors, &n, &file); if (ret < 0) { error_report("error getting block status at sector %" PRId64 ": %s", sector_num, strerror(-ret)); @@ -736,9 +792,9 @@ int bdrv_pwrite_sync(BlockDriverState *bs, int64_t offset, return ret; } - /* No flush needed for cache modes that already do it */ - if (bs->enable_write_cache) { - bdrv_flush(bs); + ret = bdrv_flush(bs); + if (ret < 0) { + return ret; } return 0; @@ -833,6 +889,7 @@ static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs, assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0); assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0); assert(!qiov || bytes == qiov->size); + assert((bs->open_flags & BDRV_O_NO_IO) == 0); /* Handle Copy on Read and associated serialisation */ if (flags & BDRV_REQ_COPY_ON_READ) { @@ -844,7 +901,9 @@ static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs, mark_request_serialising(req, bdrv_get_cluster_size(bs)); } - wait_serialising_requests(req); + if (!(flags & BDRV_REQ_NO_SERIALISING)) { + wait_serialising_requests(req); + } if (flags & BDRV_REQ_COPY_ON_READ) { int pnum; @@ -908,7 +967,7 @@ out: /* * Handle a read request in coroutine context */ -static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs, +int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs, int64_t offset, unsigned int bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -932,7 +991,8 @@ static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs, return ret; } - if (bs->copy_on_read) { + /* Don't do copy-on-read if we read data before write operation */ + if (bs->copy_on_read && !(flags & BDRV_REQ_NO_SERIALISING)) { flags |= BDRV_REQ_COPY_ON_READ; } @@ -966,7 +1026,7 @@ static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs, bytes = ROUND_UP(bytes, align); } - tracked_request_begin(&req, bs, offset, bytes, false); + tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_READ); ret = bdrv_aligned_preadv(bs, &req, offset, bytes, align, use_local_qiov ? &local_qiov : qiov, flags); @@ -1001,6 +1061,15 @@ int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num, return bdrv_co_do_readv(bs, sector_num, nb_sectors, qiov, 0); } +int coroutine_fn bdrv_co_readv_no_serialising(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) +{ + trace_bdrv_co_readv_no_serialising(bs, sector_num, nb_sectors); + + return bdrv_co_do_readv(bs, sector_num, nb_sectors, qiov, + BDRV_REQ_NO_SERIALISING); +} + int coroutine_fn bdrv_co_copy_on_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { @@ -1107,6 +1176,7 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs, assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0); assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0); assert(!qiov || bytes == qiov->size); + assert((bs->open_flags & BDRV_O_NO_IO) == 0); waited = wait_serialising_requests(req); assert(!waited || !req->serialising); @@ -1127,21 +1197,30 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs, if (ret < 0) { /* Do nothing, write notifier decided to fail this request */ } else if (flags & BDRV_REQ_ZERO_WRITE) { - BLKDBG_EVENT(bs, BLKDBG_PWRITEV_ZERO); + bdrv_debug_event(bs, BLKDBG_PWRITEV_ZERO); ret = bdrv_co_do_write_zeroes(bs, sector_num, nb_sectors, flags); + } else if (drv->bdrv_co_writev_flags) { + bdrv_debug_event(bs, BLKDBG_PWRITEV); + ret = drv->bdrv_co_writev_flags(bs, sector_num, nb_sectors, qiov, + flags); } else { - BLKDBG_EVENT(bs, BLKDBG_PWRITEV); + assert(drv->supported_write_flags == 0); + bdrv_debug_event(bs, BLKDBG_PWRITEV); ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov); } - BLKDBG_EVENT(bs, BLKDBG_PWRITEV_DONE); + bdrv_debug_event(bs, BLKDBG_PWRITEV_DONE); - if (ret == 0 && !bs->enable_write_cache) { + if (ret == 0 && (flags & BDRV_REQ_FUA) && + !(drv->supported_write_flags & BDRV_REQ_FUA)) + { ret = bdrv_co_flush(bs); } bdrv_set_dirty(bs, sector_num, nb_sectors); - block_acct_highest_sector(&bs->stats, sector_num, nb_sectors); + if (bs->wr_highest_offset < offset + bytes) { + bs->wr_highest_offset = offset + bytes; + } if (ret >= 0) { bs->total_sectors = MAX(bs->total_sectors, sector_num + nb_sectors); @@ -1182,13 +1261,13 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs, /* RMW the unaligned part before head. */ mark_request_serialising(req, align); wait_serialising_requests(req); - BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_HEAD); + bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD); ret = bdrv_aligned_preadv(bs, req, offset & ~(align - 1), align, align, &local_qiov, 0); if (ret < 0) { goto fail; } - BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD); + bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD); memset(buf + head_padding_bytes, 0, zero_bytes); ret = bdrv_aligned_pwritev(bs, req, offset & ~(align - 1), align, @@ -1220,13 +1299,13 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs, /* RMW the unaligned part after tail. */ mark_request_serialising(req, align); wait_serialising_requests(req); - BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_TAIL); + bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); ret = bdrv_aligned_preadv(bs, req, offset, align, align, &local_qiov, 0); if (ret < 0) { goto fail; } - BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); + bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); memset(buf, 0, bytes); ret = bdrv_aligned_pwritev(bs, req, offset, align, @@ -1241,7 +1320,7 @@ fail: /* * Handle a write request in coroutine context */ -static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, +int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, int64_t offset, unsigned int bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -1260,6 +1339,7 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, if (bs->read_only) { return -EPERM; } + assert(!(bs->open_flags & BDRV_O_INACTIVE)); ret = bdrv_check_byte_request(bs, offset, bytes); if (ret < 0) { @@ -1276,7 +1356,7 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, * Pad qiov with the read parts and be sure to have a tracked request not * only for bdrv_aligned_pwritev, but also for the reads of the RMW cycle. */ - tracked_request_begin(&req, bs, offset, bytes, true); + tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_WRITE); if (!qiov) { ret = bdrv_co_do_zero_pwritev(bs, offset, bytes, flags, &req); @@ -1297,13 +1377,13 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, }; qemu_iovec_init_external(&head_qiov, &head_iov, 1); - BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_HEAD); + bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD); ret = bdrv_aligned_preadv(bs, &req, offset & ~(align - 1), align, align, &head_qiov, 0); if (ret < 0) { goto fail; } - BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD); + bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD); qemu_iovec_init(&local_qiov, qiov->niov + 2); qemu_iovec_add(&local_qiov, head_buf, offset & (align - 1)); @@ -1331,13 +1411,13 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, }; qemu_iovec_init_external(&tail_qiov, &tail_iov, 1); - BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_TAIL); + bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); ret = bdrv_aligned_preadv(bs, &req, (offset + bytes) & ~(align - 1), align, align, &tail_qiov, 0); if (ret < 0) { goto fail; } - BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); + bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); if (!use_local_qiov) { qemu_iovec_init(&local_qiov, qiov->niov + 1); @@ -1401,29 +1481,10 @@ int coroutine_fn bdrv_co_write_zeroes(BlockDriverState *bs, BDRV_REQ_ZERO_WRITE | flags); } -int bdrv_flush_all(void) -{ - BlockDriverState *bs = NULL; - int result = 0; - - while ((bs = bdrv_next(bs))) { - AioContext *aio_context = bdrv_get_aio_context(bs); - int ret; - - aio_context_acquire(aio_context); - ret = bdrv_flush(bs); - if (ret < 0 && !result) { - result = ret; - } - aio_context_release(aio_context); - } - - return result; -} - typedef struct BdrvCoGetBlockStatusData { BlockDriverState *bs; BlockDriverState *base; + BlockDriverState **file; int64_t sector_num; int nb_sectors; int *pnum; @@ -1445,10 +1506,14 @@ typedef struct BdrvCoGetBlockStatusData { * * 'nb_sectors' is the max value 'pnum' should be set to. If nb_sectors goes * beyond the end of the disk image it will be clamped. + * + * If returned value is positive and BDRV_BLOCK_OFFSET_VALID bit is set, 'file' + * points to the BDS which the sector range is allocated in. */ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int *pnum) + int nb_sectors, int *pnum, + BlockDriverState **file) { int64_t total_sectors; int64_t n; @@ -1478,7 +1543,9 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, return ret; } - ret = bs->drv->bdrv_co_get_block_status(bs, sector_num, nb_sectors, pnum); + *file = NULL; + ret = bs->drv->bdrv_co_get_block_status(bs, sector_num, nb_sectors, pnum, + file); if (ret < 0) { *pnum = 0; return ret; @@ -1486,8 +1553,8 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, if (ret & BDRV_BLOCK_RAW) { assert(ret & BDRV_BLOCK_OFFSET_VALID); - return bdrv_get_block_status(bs->file, ret >> BDRV_SECTOR_BITS, - *pnum, pnum); + return bdrv_get_block_status(bs->file->bs, ret >> BDRV_SECTOR_BITS, + *pnum, pnum, file); } if (ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ZERO)) { @@ -1495,8 +1562,8 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, } else { if (bdrv_unallocated_blocks_are_zero(bs)) { ret |= BDRV_BLOCK_ZERO; - } else if (bs->backing_hd) { - BlockDriverState *bs2 = bs->backing_hd; + } else if (bs->backing) { + BlockDriverState *bs2 = bs->backing->bs; int64_t nb_sectors2 = bdrv_nb_sectors(bs2); if (nb_sectors2 >= 0 && sector_num >= nb_sectors2) { ret |= BDRV_BLOCK_ZERO; @@ -1504,13 +1571,14 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, } } - if (bs->file && + if (*file && *file != bs && (ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO) && (ret & BDRV_BLOCK_OFFSET_VALID)) { + BlockDriverState *file2; int file_pnum; - ret2 = bdrv_co_get_block_status(bs->file, ret >> BDRV_SECTOR_BITS, - *pnum, &file_pnum); + ret2 = bdrv_co_get_block_status(*file, ret >> BDRV_SECTOR_BITS, + *pnum, &file_pnum, &file2); if (ret2 >= 0) { /* Ignore errors. This is just providing extra information, it * is useful but not necessary. @@ -1535,14 +1603,15 @@ static int64_t coroutine_fn bdrv_co_get_block_status_above(BlockDriverState *bs, BlockDriverState *base, int64_t sector_num, int nb_sectors, - int *pnum) + int *pnum, + BlockDriverState **file) { BlockDriverState *p; int64_t ret = 0; assert(bs != base); - for (p = bs; p != base; p = p->backing_hd) { - ret = bdrv_co_get_block_status(p, sector_num, nb_sectors, pnum); + for (p = bs; p != base; p = backing_bs(p)) { + ret = bdrv_co_get_block_status(p, sector_num, nb_sectors, pnum, file); if (ret < 0 || ret & BDRV_BLOCK_ALLOCATED) { break; } @@ -1561,7 +1630,8 @@ static void coroutine_fn bdrv_get_block_status_above_co_entry(void *opaque) data->ret = bdrv_co_get_block_status_above(data->bs, data->base, data->sector_num, data->nb_sectors, - data->pnum); + data->pnum, + data->file); data->done = true; } @@ -1573,12 +1643,14 @@ static void coroutine_fn bdrv_get_block_status_above_co_entry(void *opaque) int64_t bdrv_get_block_status_above(BlockDriverState *bs, BlockDriverState *base, int64_t sector_num, - int nb_sectors, int *pnum) + int nb_sectors, int *pnum, + BlockDriverState **file) { Coroutine *co; BdrvCoGetBlockStatusData data = { .bs = bs, .base = base, + .file = file, .sector_num = sector_num, .nb_sectors = nb_sectors, .pnum = pnum, @@ -1602,16 +1674,19 @@ int64_t bdrv_get_block_status_above(BlockDriverState *bs, int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int *pnum) + int nb_sectors, int *pnum, + BlockDriverState **file) { - return bdrv_get_block_status_above(bs, bs->backing_hd, - sector_num, nb_sectors, pnum); + return bdrv_get_block_status_above(bs, backing_bs(bs), + sector_num, nb_sectors, pnum, file); } int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum) { - int64_t ret = bdrv_get_block_status(bs, sector_num, nb_sectors, pnum); + BlockDriverState *file; + int64_t ret = bdrv_get_block_status(bs, sector_num, nb_sectors, pnum, + &file); if (ret < 0) { return ret; } @@ -1662,7 +1737,7 @@ int bdrv_is_allocated_above(BlockDriverState *top, n = pnum_inter; } - intermediate = intermediate->backing_hd; + intermediate = backing_bs(intermediate); } *pnum = n; @@ -1713,7 +1788,7 @@ int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) } else if (drv->bdrv_save_vmstate) { return drv->bdrv_save_vmstate(bs, qiov, pos); } else if (bs->file) { - return bdrv_writev_vmstate(bs->file, qiov, pos); + return bdrv_writev_vmstate(bs->file->bs, qiov, pos); } return -ENOTSUP; @@ -1728,7 +1803,7 @@ int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, if (drv->bdrv_load_vmstate) return drv->bdrv_load_vmstate(bs, buf, pos, size); if (bs->file) - return bdrv_load_vmstate(bs->file, buf, pos, size); + return bdrv_load_vmstate(bs->file->bs, buf, pos, size); return -ENOTSUP; } @@ -1849,7 +1924,8 @@ static int multiwrite_merge(BlockDriverState *bs, BlockRequest *reqs, merge = 1; } - if (reqs[outidx].qiov->niov + reqs[i].qiov->niov + 1 > IOV_MAX) { + if (reqs[outidx].qiov->niov + reqs[i].qiov->niov + 1 > + bs->bl.max_iov) { merge = 0; } @@ -1893,7 +1969,10 @@ static int multiwrite_merge(BlockDriverState *bs, BlockRequest *reqs, } } - block_acct_merge_done(&bs->stats, BLOCK_ACCT_WRITE, num_reqs - outidx - 1); + if (bs->blk) { + block_acct_merge_done(blk_get_stats(bs->blk), BLOCK_ACCT_WRITE, + num_reqs - outidx - 1); + } return outidx + 1; } @@ -2208,7 +2287,7 @@ void *qemu_aio_get(const AIOCBInfo *aiocb_info, BlockDriverState *bs, { BlockAIOCB *acb; - acb = g_slice_alloc(aiocb_info->aiocb_size); + acb = g_malloc(aiocb_info->aiocb_size); acb->aiocb_info = aiocb_info; acb->bs = bs; acb->cb = cb; @@ -2228,7 +2307,7 @@ void qemu_aio_unref(void *p) BlockAIOCB *acb = p; assert(acb->refcnt > 0); if (--acb->refcnt == 0) { - g_slice_free1(acb->aiocb_info->aiocb_size, acb); + g_free(acb); } } @@ -2298,18 +2377,27 @@ static void coroutine_fn bdrv_flush_co_entry(void *opaque) int coroutine_fn bdrv_co_flush(BlockDriverState *bs) { int ret; + BdrvTrackedRequest req; if (!bs || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs) || bdrv_is_sg(bs)) { return 0; } + tracked_request_begin(&req, bs, 0, 0, BDRV_TRACKED_FLUSH); + + /* Write back all layers by calling one driver function */ + if (bs->drv->bdrv_co_flush) { + ret = bs->drv->bdrv_co_flush(bs); + goto out; + } + /* Write back cached data to the OS even with cache=unsafe */ BLKDBG_EVENT(bs->file, BLKDBG_FLUSH_TO_OS); if (bs->drv->bdrv_co_flush_to_os) { ret = bs->drv->bdrv_co_flush_to_os(bs); if (ret < 0) { - return ret; + goto out; } } @@ -2349,14 +2437,17 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs) ret = 0; } if (ret < 0) { - return ret; + goto out; } /* Now flush the underlying protocol. It will also have BDRV_O_NO_FLUSH * in the case of cache=unsafe, so there are no useless flushes. */ flush_parent: - return bdrv_co_flush(bs->file); + ret = bs->file ? bdrv_co_flush(bs->file->bs) : 0; +out: + tracked_request_end(&req); + return ret; } int bdrv_flush(BlockDriverState *bs) @@ -2399,6 +2490,7 @@ static void coroutine_fn bdrv_discard_co_entry(void *opaque) int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) { + BdrvTrackedRequest req; int max_discard, ret; if (!bs->drv) { @@ -2411,6 +2503,7 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num, } else if (bs->read_only) { return -EPERM; } + assert(!(bs->open_flags & BDRV_O_INACTIVE)); /* Do nothing if disabled. */ if (!(bs->open_flags & BDRV_O_UNMAP)) { @@ -2421,6 +2514,8 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num, return 0; } + tracked_request_begin(&req, bs, sector_num, nb_sectors, + BDRV_TRACKED_DISCARD); bdrv_set_dirty(bs, sector_num, nb_sectors); max_discard = MIN_NON_ZERO(bs->bl.max_discard, BDRV_REQUEST_MAX_SECTORS); @@ -2454,20 +2549,24 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num, acb = bs->drv->bdrv_aio_discard(bs, sector_num, nb_sectors, bdrv_co_io_em_complete, &co); if (acb == NULL) { - return -EIO; + ret = -EIO; + goto out; } else { qemu_coroutine_yield(); ret = co.ret; } } if (ret && ret != -ENOTSUP) { - return ret; + goto out; } sector_num += num; nb_sectors -= num; } - return 0; + ret = 0; +out: + tracked_request_end(&req); + return ret; } int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) @@ -2496,26 +2595,110 @@ int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) return rwco.ret; } -/* needed for generic scsi interface */ +typedef struct { + CoroutineIOCompletion *co; + QEMUBH *bh; +} BdrvIoctlCompletionData; -int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) +static void bdrv_ioctl_bh_cb(void *opaque) +{ + BdrvIoctlCompletionData *data = opaque; + + bdrv_co_io_em_complete(data->co, -ENOTSUP); + qemu_bh_delete(data->bh); +} + +static int bdrv_co_do_ioctl(BlockDriverState *bs, int req, void *buf) { BlockDriver *drv = bs->drv; + BdrvTrackedRequest tracked_req; + CoroutineIOCompletion co = { + .coroutine = qemu_coroutine_self(), + }; + BlockAIOCB *acb; - if (drv && drv->bdrv_ioctl) - return drv->bdrv_ioctl(bs, req, buf); - return -ENOTSUP; + tracked_request_begin(&tracked_req, bs, 0, 0, BDRV_TRACKED_IOCTL); + if (!drv || !drv->bdrv_aio_ioctl) { + co.ret = -ENOTSUP; + goto out; + } + + acb = drv->bdrv_aio_ioctl(bs, req, buf, bdrv_co_io_em_complete, &co); + if (!acb) { + BdrvIoctlCompletionData *data = g_new(BdrvIoctlCompletionData, 1); + data->bh = aio_bh_new(bdrv_get_aio_context(bs), + bdrv_ioctl_bh_cb, data); + data->co = &co; + qemu_bh_schedule(data->bh); + } + qemu_coroutine_yield(); +out: + tracked_request_end(&tracked_req); + return co.ret; +} + +typedef struct { + BlockDriverState *bs; + int req; + void *buf; + int ret; +} BdrvIoctlCoData; + +static void coroutine_fn bdrv_co_ioctl_entry(void *opaque) +{ + BdrvIoctlCoData *data = opaque; + data->ret = bdrv_co_do_ioctl(data->bs, data->req, data->buf); +} + +/* needed for generic scsi interface */ +int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) +{ + BdrvIoctlCoData data = { + .bs = bs, + .req = req, + .buf = buf, + .ret = -EINPROGRESS, + }; + + if (qemu_in_coroutine()) { + /* Fast-path if already in coroutine context */ + bdrv_co_ioctl_entry(&data); + } else { + Coroutine *co = qemu_coroutine_create(bdrv_co_ioctl_entry); + + qemu_coroutine_enter(co, &data); + while (data.ret == -EINPROGRESS) { + aio_poll(bdrv_get_aio_context(bs), true); + } + } + return data.ret; +} + +static void coroutine_fn bdrv_co_aio_ioctl_entry(void *opaque) +{ + BlockAIOCBCoroutine *acb = opaque; + acb->req.error = bdrv_co_do_ioctl(acb->common.bs, + acb->req.req, acb->req.buf); + bdrv_co_complete(acb); } BlockAIOCB *bdrv_aio_ioctl(BlockDriverState *bs, unsigned long int req, void *buf, BlockCompletionFunc *cb, void *opaque) { - BlockDriver *drv = bs->drv; + BlockAIOCBCoroutine *acb = qemu_aio_get(&bdrv_em_co_aiocb_info, + bs, cb, opaque); + Coroutine *co; + + acb->need_bh = true; + acb->req.error = -EINPROGRESS; + acb->req.req = req; + acb->req.buf = buf; + co = qemu_coroutine_create(bdrv_co_aio_ioctl_entry); + qemu_coroutine_enter(co, acb); - if (drv && drv->bdrv_aio_ioctl) - return drv->bdrv_aio_ioctl(bs, req, buf, cb, opaque); - return NULL; + bdrv_co_maybe_schedule_bh(acb); + return &acb->common; } void *qemu_blockalign(BlockDriverState *bs, size_t size) @@ -2584,7 +2767,7 @@ void bdrv_io_plug(BlockDriverState *bs) if (drv && drv->bdrv_io_plug) { drv->bdrv_io_plug(bs); } else if (bs->file) { - bdrv_io_plug(bs->file); + bdrv_io_plug(bs->file->bs); } } @@ -2594,7 +2777,7 @@ void bdrv_io_unplug(BlockDriverState *bs) if (drv && drv->bdrv_io_unplug) { drv->bdrv_io_unplug(bs); } else if (bs->file) { - bdrv_io_unplug(bs->file); + bdrv_io_unplug(bs->file->bs); } } @@ -2604,7 +2787,24 @@ void bdrv_flush_io_queue(BlockDriverState *bs) if (drv && drv->bdrv_flush_io_queue) { drv->bdrv_flush_io_queue(bs); } else if (bs->file) { - bdrv_flush_io_queue(bs->file); + bdrv_flush_io_queue(bs->file->bs); } bdrv_start_throttled_reqs(bs); } + +void bdrv_drained_begin(BlockDriverState *bs) +{ + if (!bs->quiesce_counter++) { + aio_disable_external(bdrv_get_aio_context(bs)); + } + bdrv_drain(bs); +} + +void bdrv_drained_end(BlockDriverState *bs) +{ + assert(bs->quiesce_counter > 0); + if (--bs->quiesce_counter > 0) { + return; + } + aio_enable_external(bdrv_get_aio_context(bs)); +} diff --git a/qemu/block/iscsi.c b/qemu/block/iscsi.c index 50029168e..302baf84c 100644 --- a/qemu/block/iscsi.c +++ b/qemu/block/iscsi.c @@ -23,7 +23,7 @@ * THE SOFTWARE. */ -#include "config-host.h" +#include "qemu/osdep.h" #include #include @@ -39,6 +39,7 @@ #include "sysemu/sysemu.h" #include "qmp-commands.h" #include "qapi/qmp/qstring.h" +#include "crypto/secret.h" #include #include @@ -69,7 +70,6 @@ typedef struct IscsiLun { bool lbprz; bool dpofua; bool has_write_same; - bool force_next_flush; bool request_timed_out; } IscsiLun; @@ -83,7 +83,7 @@ typedef struct IscsiTask { QEMUBH *bh; IscsiLun *iscsilun; QEMUTimer retry_timer; - bool force_next_flush; + int err_code; } IscsiTask; typedef struct IscsiAIOCB { @@ -96,6 +96,7 @@ typedef struct IscsiAIOCB { int status; int64_t sector_num; int nb_sectors; + int ret; #ifdef __linux__ sg_io_hdr_t *ioh; #endif @@ -169,19 +170,70 @@ static inline unsigned exp_random(double mean) return -mean * log((double)rand() / RAND_MAX); } -/* SCSI_STATUS_TASK_SET_FULL and SCSI_STATUS_TIMEOUT were introduced - * in libiscsi 1.10.0 as part of an enum. The LIBISCSI_API_VERSION - * macro was introduced in 1.11.0. So use the API_VERSION macro as - * a hint that the macros are defined and define them ourselves - * otherwise to keep the required libiscsi version at 1.9.0 */ -#if !defined(LIBISCSI_API_VERSION) -#define QEMU_SCSI_STATUS_TASK_SET_FULL 0x28 -#define QEMU_SCSI_STATUS_TIMEOUT 0x0f000002 -#else -#define QEMU_SCSI_STATUS_TASK_SET_FULL SCSI_STATUS_TASK_SET_FULL -#define QEMU_SCSI_STATUS_TIMEOUT SCSI_STATUS_TIMEOUT +/* SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST was introduced in + * libiscsi 1.10.0, together with other constants we need. Use it as + * a hint that we have to define them ourselves if needed, to keep the + * minimum required libiscsi version at 1.9.0. We use an ASCQ macro for + * the test because SCSI_STATUS_* is an enum. + * + * To guard against future changes where SCSI_SENSE_ASCQ_* also becomes + * an enum, check against the LIBISCSI_API_VERSION macro, which was + * introduced in 1.11.0. If it is present, there is no need to define + * anything. + */ +#if !defined(SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST) && \ + !defined(LIBISCSI_API_VERSION) +#define SCSI_STATUS_TASK_SET_FULL 0x28 +#define SCSI_STATUS_TIMEOUT 0x0f000002 +#define SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST 0x2600 +#define SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR 0x1a00 #endif +static int iscsi_translate_sense(struct scsi_sense *sense) +{ + int ret; + + switch (sense->key) { + case SCSI_SENSE_NOT_READY: + return -EBUSY; + case SCSI_SENSE_DATA_PROTECTION: + return -EACCES; + case SCSI_SENSE_COMMAND_ABORTED: + return -ECANCELED; + case SCSI_SENSE_ILLEGAL_REQUEST: + /* Parse ASCQ */ + break; + default: + return -EIO; + } + switch (sense->ascq) { + case SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR: + case SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE: + case SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB: + case SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST: + ret = -EINVAL; + break; + case SCSI_SENSE_ASCQ_LBA_OUT_OF_RANGE: + ret = -ENOSPC; + break; + case SCSI_SENSE_ASCQ_LOGICAL_UNIT_NOT_SUPPORTED: + ret = -ENOTSUP; + break; + case SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT: + case SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_CLOSED: + case SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_OPEN: + ret = -ENOMEDIUM; + break; + case SCSI_SENSE_ASCQ_WRITE_PROTECTED: + ret = -EACCES; + break; + default: + ret = -EIO; + break; + } + return ret; +} + static void iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, void *command_data, void *opaque) @@ -203,11 +255,11 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, goto out; } if (status == SCSI_STATUS_BUSY || - status == QEMU_SCSI_STATUS_TIMEOUT || - status == QEMU_SCSI_STATUS_TASK_SET_FULL) { + status == SCSI_STATUS_TIMEOUT || + status == SCSI_STATUS_TASK_SET_FULL) { unsigned retry_time = exp_random(iscsi_retry_times[iTask->retries - 1]); - if (status == QEMU_SCSI_STATUS_TIMEOUT) { + if (status == SCSI_STATUS_TIMEOUT) { /* make sure the request is rescheduled AFTER the * reconnect is initiated */ retry_time = EVENT_INTERVAL * 2; @@ -226,9 +278,8 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, return; } } + iTask->err_code = iscsi_translate_sense(&task->sense); error_report("iSCSI Failure: %s", iscsi_get_error(iscsi)); - } else { - iTask->iscsilun->force_next_flush |= iTask->force_next_flush; } out: @@ -291,8 +342,8 @@ iscsi_set_events(IscsiLun *iscsilun) int ev = iscsi_which_events(iscsi); if (ev != iscsilun->events) { - aio_set_fd_handler(iscsilun->aio_context, - iscsi_get_fd(iscsi), + aio_set_fd_handler(iscsilun->aio_context, iscsi_get_fd(iscsi), + false, (ev & POLLIN) ? iscsi_process_read : NULL, (ev & POLLOUT) ? iscsi_process_write : NULL, iscsilun); @@ -397,15 +448,15 @@ static void iscsi_allocationmap_clear(IscsiLun *iscsilun, int64_t sector_num, } } -static int coroutine_fn iscsi_co_writev(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, - QEMUIOVector *iov) +static int coroutine_fn +iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors, + QEMUIOVector *iov, int flags) { IscsiLun *iscsilun = bs->opaque; struct IscsiTask iTask; uint64_t lba; uint32_t num_sectors; - int fua; + bool fua; if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { return -EINVAL; @@ -421,8 +472,7 @@ static int coroutine_fn iscsi_co_writev(BlockDriverState *bs, num_sectors = sector_qemu2lun(nb_sectors, iscsilun); iscsi_co_init_iscsitask(iscsilun, &iTask); retry: - fua = iscsilun->dpofua && !bs->enable_write_cache; - iTask.force_next_flush = !fua; + fua = iscsilun->dpofua && (flags & BDRV_REQ_FUA); if (iscsilun->use_16_for_rw) { iTask.task = iscsi_write16_task(iscsilun->iscsi, iscsilun->lun, lba, NULL, num_sectors * iscsilun->block_size, @@ -455,7 +505,7 @@ retry: } if (iTask.status != SCSI_STATUS_GOOD) { - return -EIO; + return iTask.err_code; } iscsi_allocationmap_set(iscsilun, sector_num, nb_sectors); @@ -463,6 +513,13 @@ retry: return 0; } +static int coroutine_fn +iscsi_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, + QEMUIOVector *iov) +{ + return iscsi_co_writev_flags(bs, sector_num, nb_sectors, iov, 0); +} + static bool iscsi_allocationmap_is_allocated(IscsiLun *iscsilun, int64_t sector_num, int nb_sectors) @@ -478,7 +535,8 @@ static bool iscsi_allocationmap_is_allocated(IscsiLun *iscsilun, static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int *pnum) + int nb_sectors, int *pnum, + BlockDriverState **file) { IscsiLun *iscsilun = bs->opaque; struct scsi_get_lba_status *lbas = NULL; @@ -570,6 +628,9 @@ out: if (iTask.task != NULL) { scsi_free_scsi_task(iTask.task); } + if (ret > 0 && ret & BDRV_BLOCK_OFFSET_VALID) { + *file = bs; + } return ret; } @@ -596,7 +657,8 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, !iscsi_allocationmap_is_allocated(iscsilun, sector_num, nb_sectors)) { int64_t ret; int pnum; - ret = iscsi_co_get_block_status(bs, sector_num, INT_MAX, &pnum); + BlockDriverState *file; + ret = iscsi_co_get_block_status(bs, sector_num, INT_MAX, &pnum, &file); if (ret < 0) { return ret; } @@ -644,7 +706,7 @@ retry: } if (iTask.status != SCSI_STATUS_GOOD) { - return -EIO; + return iTask.err_code; } return 0; @@ -655,11 +717,6 @@ static int coroutine_fn iscsi_co_flush(BlockDriverState *bs) IscsiLun *iscsilun = bs->opaque; struct IscsiTask iTask; - if (!iscsilun->force_next_flush) { - return 0; - } - iscsilun->force_next_flush = false; - iscsi_co_init_iscsitask(iscsilun, &iTask); retry: if (iscsi_synchronizecache10_task(iscsilun->iscsi, iscsilun->lun, 0, 0, 0, @@ -683,7 +740,7 @@ retry: } if (iTask.status != SCSI_STATUS_GOOD) { - return -EIO; + return iTask.err_code; } return 0; @@ -703,7 +760,7 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status, if (status < 0) { error_report("Failed to ioctl(SG_IO) to iSCSI lun. %s", iscsi_get_error(iscsi)); - acb->status = -EIO; + acb->status = iscsi_translate_sense(&acb->task->sense); } acb->ioh->driver_status = 0; @@ -726,6 +783,38 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status, iscsi_schedule_bh(acb); } +static void iscsi_ioctl_bh_completion(void *opaque) +{ + IscsiAIOCB *acb = opaque; + + qemu_bh_delete(acb->bh); + acb->common.cb(acb->common.opaque, acb->ret); + qemu_aio_unref(acb); +} + +static void iscsi_ioctl_handle_emulated(IscsiAIOCB *acb, int req, void *buf) +{ + BlockDriverState *bs = acb->common.bs; + IscsiLun *iscsilun = bs->opaque; + int ret = 0; + + switch (req) { + case SG_GET_VERSION_NUM: + *(int *)buf = 30000; + break; + case SG_GET_SCSI_ID: + ((struct sg_scsi_id *)buf)->scsi_type = iscsilun->type; + break; + default: + ret = -EINVAL; + } + assert(!acb->bh); + acb->bh = aio_bh_new(bdrv_get_aio_context(bs), + iscsi_ioctl_bh_completion, acb); + acb->ret = ret; + qemu_bh_schedule(acb->bh); +} + static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, unsigned long int req, void *buf, BlockCompletionFunc *cb, void *opaque) @@ -735,8 +824,6 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, struct iscsi_data data; IscsiAIOCB *acb; - assert(req == SG_IO); - acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); acb->iscsilun = iscsilun; @@ -745,6 +832,11 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, acb->buf = NULL; acb->ioh = buf; + if (req != SG_IO) { + iscsi_ioctl_handle_emulated(acb, req, buf); + return &acb->common; + } + acb->task = malloc(sizeof(struct scsi_task)); if (acb->task == NULL) { error_report("iSCSI: Failed to allocate task for scsi command. %s", @@ -809,38 +901,6 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, return &acb->common; } -static void ioctl_cb(void *opaque, int status) -{ - int *p_status = opaque; - *p_status = status; -} - -static int iscsi_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) -{ - IscsiLun *iscsilun = bs->opaque; - int status; - - switch (req) { - case SG_GET_VERSION_NUM: - *(int *)buf = 30000; - break; - case SG_GET_SCSI_ID: - ((struct sg_scsi_id *)buf)->scsi_type = iscsilun->type; - break; - case SG_IO: - status = -EINPROGRESS; - iscsi_aio_ioctl(bs, req, buf, ioctl_cb, &status); - - while (status == -EINPROGRESS) { - aio_poll(iscsilun->aio_context, true); - } - - return 0; - default: - return -1; - } - return 0; -} #endif static int64_t @@ -905,7 +965,7 @@ retry: } if (iTask.status != SCSI_STATUS_GOOD) { - return -EIO; + return iTask.err_code; } iscsi_allocationmap_clear(iscsilun, sector_num, nb_sectors); @@ -956,7 +1016,6 @@ coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, } iscsi_co_init_iscsitask(iscsilun, &iTask); - iTask.force_next_flush = true; retry: if (use_16_for_ws) { iTask.task = iscsi_writesame16_task(iscsilun->iscsi, iscsilun->lun, lba, @@ -999,7 +1058,7 @@ retry: } if (iTask.status != SCSI_STATUS_GOOD) { - return -EIO; + return iTask.err_code; } if (flags & BDRV_REQ_MAY_UNMAP) { @@ -1018,6 +1077,8 @@ static void parse_chap(struct iscsi_context *iscsi, const char *target, QemuOpts *opts; const char *user = NULL; const char *password = NULL; + const char *secretid; + char *secret = NULL; list = qemu_find_opts("iscsi"); if (!list) { @@ -1037,8 +1098,20 @@ static void parse_chap(struct iscsi_context *iscsi, const char *target, return; } + secretid = qemu_opt_get(opts, "password-secret"); password = qemu_opt_get(opts, "password"); - if (!password) { + if (secretid && password) { + error_setg(errp, "'password' and 'password-secret' properties are " + "mutually exclusive"); + return; + } + if (secretid) { + secret = qcrypto_secret_lookup_as_utf8(secretid, errp); + if (!secret) { + return; + } + password = secret; + } else if (!password) { error_setg(errp, "CHAP username specified but no password was given"); return; } @@ -1046,6 +1119,8 @@ static void parse_chap(struct iscsi_context *iscsi, const char *target, if (iscsi_set_initiator_username_pwd(iscsi, user, password)) { error_setg(errp, "Failed to set initiator username and password"); } + + g_free(secret); } static void parse_header_digest(struct iscsi_context *iscsi, const char *target, @@ -1186,8 +1261,13 @@ static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp) iscsilun->lbprz = !!rc16->lbprz; iscsilun->use_16_for_rw = (rc16->returned_lba > 0xffffffff); } + break; } - break; + if (task != NULL && task->status == SCSI_STATUS_CHECK_CONDITION + && task->sense.key == SCSI_SENSE_UNIT_ATTENTION) { + break; + } + /* Fall through and try READ CAPACITY(10) instead. */ case TYPE_ROM: task = iscsi_readcapacity10_sync(iscsilun->iscsi, iscsilun->lun, 0, 0); if (task != NULL && task->status == SCSI_STATUS_GOOD) { @@ -1213,7 +1293,11 @@ static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp) && retries-- > 0); if (task == NULL || task->status != SCSI_STATUS_GOOD) { - error_setg(errp, "iSCSI: failed to send readcapacity10 command."); + error_setg(errp, "iSCSI: failed to send readcapacity10/16 command"); + } else if (!iscsilun->block_size || + iscsilun->block_size % BDRV_SECTOR_SIZE) { + error_setg(errp, "iSCSI: the target returned an invalid " + "block size of %d.", iscsilun->block_size); } if (task) { scsi_free_scsi_task(task); @@ -1276,9 +1360,8 @@ static void iscsi_detach_aio_context(BlockDriverState *bs) { IscsiLun *iscsilun = bs->opaque; - aio_set_fd_handler(iscsilun->aio_context, - iscsi_get_fd(iscsilun->iscsi), - NULL, NULL, NULL); + aio_set_fd_handler(iscsilun->aio_context, iscsi_get_fd(iscsilun->iscsi), + false, NULL, NULL, NULL); iscsilun->events = 0; if (iscsilun->nop_timer) { @@ -1765,10 +1848,11 @@ static BlockDriver bdrv_iscsi = { .bdrv_co_write_zeroes = iscsi_co_write_zeroes, .bdrv_co_readv = iscsi_co_readv, .bdrv_co_writev = iscsi_co_writev, + .bdrv_co_writev_flags = iscsi_co_writev_flags, + .supported_write_flags = BDRV_REQ_FUA, .bdrv_co_flush_to_disk = iscsi_co_flush, #ifdef __linux__ - .bdrv_ioctl = iscsi_ioctl, .bdrv_aio_ioctl = iscsi_aio_ioctl, #endif @@ -1788,6 +1872,11 @@ static QemuOptsList qemu_iscsi_opts = { .name = "password", .type = QEMU_OPT_STRING, .help = "password for CHAP authentication to target", + },{ + .name = "password-secret", + .type = QEMU_OPT_STRING, + .help = "ID of the secret providing password for CHAP " + "authentication to target", },{ .name = "header-digest", .type = QEMU_OPT_STRING, diff --git a/qemu/block/linux-aio.c b/qemu/block/linux-aio.c index c991443c5..805757e02 100644 --- a/qemu/block/linux-aio.c +++ b/qemu/block/linux-aio.c @@ -7,6 +7,7 @@ * 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 "block/aio.h" #include "qemu/queue.h" @@ -287,7 +288,7 @@ void laio_detach_aio_context(void *s_, AioContext *old_context) { struct qemu_laio_state *s = s_; - aio_set_event_notifier(old_context, &s->e, NULL); + aio_set_event_notifier(old_context, &s->e, false, NULL); qemu_bh_delete(s->completion_bh); } @@ -296,7 +297,8 @@ void laio_attach_aio_context(void *s_, AioContext *new_context) struct qemu_laio_state *s = s_; s->completion_bh = aio_bh_new(new_context, qemu_laio_completion_bh, s); - aio_set_event_notifier(new_context, &s->e, qemu_laio_completion_cb); + aio_set_event_notifier(new_context, &s->e, false, + qemu_laio_completion_cb); } void *laio_init(void) diff --git a/qemu/block/mirror.c b/qemu/block/mirror.c index fc4d8f561..039f48125 100644 --- a/qemu/block/mirror.c +++ b/qemu/block/mirror.c @@ -11,12 +11,16 @@ * */ +#include "qemu/osdep.h" #include "trace.h" #include "block/blockjob.h" #include "block/block_int.h" +#include "sysemu/block-backend.h" +#include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qemu/ratelimit.h" #include "qemu/bitmap.h" +#include "qemu/error-report.h" #define SLICE_TIME 100000000ULL /* ns */ #define MAX_IN_FLIGHT 16 @@ -44,7 +48,6 @@ typedef struct MirrorBlockJob { BlockdevOnError on_source_error, on_target_error; bool synced; bool should_complete; - int64_t sector_num; int64_t granularity; size_t buf_size; int64_t bdev_length; @@ -60,6 +63,9 @@ typedef struct MirrorBlockJob { int sectors_in_flight; int ret; bool unmap; + bool waiting_for_io; + int target_cluster_sectors; + int max_iov; } MirrorBlockJob; typedef struct MirrorOp { @@ -102,7 +108,7 @@ static void mirror_iteration_done(MirrorOp *op, int ret) sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS; chunk_num = op->sector_num / sectors_per_chunk; - nb_chunks = op->nb_sectors / sectors_per_chunk; + nb_chunks = DIV_ROUND_UP(op->nb_sectors, sectors_per_chunk); bitmap_clear(s->in_flight_bitmap, chunk_num, nb_chunks); if (ret >= 0) { if (s->cow_bitmap) { @@ -112,13 +118,9 @@ static void mirror_iteration_done(MirrorOp *op, int ret) } qemu_iovec_destroy(&op->qiov); - g_slice_free(MirrorOp, op); + g_free(op); - /* Enter coroutine when it is not sleeping. The coroutine sleeps to - * rate-limit itself. The coroutine will eventually resume since there is - * a sleep timeout so don't wake it early. - */ - if (s->common.busy) { + if (s->waiting_for_io) { qemu_coroutine_enter(s->common.co, NULL); } } @@ -159,107 +161,97 @@ static void mirror_read_complete(void *opaque, int ret) mirror_write_complete, op); } -static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) +static inline void mirror_clip_sectors(MirrorBlockJob *s, + int64_t sector_num, + int *nb_sectors) { - BlockDriverState *source = s->common.bs; - int nb_sectors, sectors_per_chunk, nb_chunks; - int64_t end, sector_num, next_chunk, next_sector, hbitmap_next_sector; - uint64_t delay_ns = 0; - MirrorOp *op; - int pnum; - int64_t ret; - - s->sector_num = hbitmap_iter_next(&s->hbi); - if (s->sector_num < 0) { - bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi); - s->sector_num = hbitmap_iter_next(&s->hbi); - trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap)); - assert(s->sector_num >= 0); - } - - hbitmap_next_sector = s->sector_num; - sector_num = s->sector_num; - sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS; - end = s->bdev_length / BDRV_SECTOR_SIZE; - - /* Extend the QEMUIOVector to include all adjacent blocks that will - * be copied in this operation. - * - * We have to do this if we have no backing file yet in the destination, - * and the cluster size is very large. Then we need to do COW ourselves. - * The first time a cluster is copied, copy it entirely. Note that, - * because both the granularity and the cluster size are powers of two, - * the number of sectors to copy cannot exceed one cluster. - * - * We also want to extend the QEMUIOVector to include more adjacent - * dirty blocks if possible, to limit the number of I/O operations and - * run efficiently even with a small granularity. - */ - nb_chunks = 0; - nb_sectors = 0; - next_sector = sector_num; - next_chunk = sector_num / sectors_per_chunk; + *nb_sectors = MIN(*nb_sectors, + s->bdev_length / BDRV_SECTOR_SIZE - sector_num); +} - /* Wait for I/O to this cluster (from a previous iteration) to be done. */ - while (test_bit(next_chunk, s->in_flight_bitmap)) { - trace_mirror_yield_in_flight(s, sector_num, s->in_flight); - qemu_coroutine_yield(); +/* Round sector_num and/or nb_sectors to target cluster if COW is needed, and + * return the offset of the adjusted tail sector against original. */ +static int mirror_cow_align(MirrorBlockJob *s, + int64_t *sector_num, + int *nb_sectors) +{ + bool need_cow; + int ret = 0; + int chunk_sectors = s->granularity >> BDRV_SECTOR_BITS; + int64_t align_sector_num = *sector_num; + int align_nb_sectors = *nb_sectors; + int max_sectors = chunk_sectors * s->max_iov; + + need_cow = !test_bit(*sector_num / chunk_sectors, s->cow_bitmap); + need_cow |= !test_bit((*sector_num + *nb_sectors - 1) / chunk_sectors, + s->cow_bitmap); + if (need_cow) { + bdrv_round_to_clusters(s->target, *sector_num, *nb_sectors, + &align_sector_num, &align_nb_sectors); + } + + if (align_nb_sectors > max_sectors) { + align_nb_sectors = max_sectors; + if (need_cow) { + align_nb_sectors = QEMU_ALIGN_DOWN(align_nb_sectors, + s->target_cluster_sectors); + } } + /* Clipping may result in align_nb_sectors unaligned to chunk boundary, but + * that doesn't matter because it's already the end of source image. */ + mirror_clip_sectors(s, align_sector_num, &align_nb_sectors); - do { - int added_sectors, added_chunks; - - if (!bdrv_get_dirty(source, s->dirty_bitmap, next_sector) || - test_bit(next_chunk, s->in_flight_bitmap)) { - assert(nb_sectors > 0); - break; - } + ret = align_sector_num + align_nb_sectors - (*sector_num + *nb_sectors); + *sector_num = align_sector_num; + *nb_sectors = align_nb_sectors; + assert(ret >= 0); + return ret; +} - added_sectors = sectors_per_chunk; - if (s->cow_bitmap && !test_bit(next_chunk, s->cow_bitmap)) { - bdrv_round_to_clusters(s->target, - next_sector, added_sectors, - &next_sector, &added_sectors); +static inline void mirror_wait_for_io(MirrorBlockJob *s) +{ + assert(!s->waiting_for_io); + s->waiting_for_io = true; + qemu_coroutine_yield(); + s->waiting_for_io = false; +} - /* On the first iteration, the rounding may make us copy - * sectors before the first dirty one. - */ - if (next_sector < sector_num) { - assert(nb_sectors == 0); - sector_num = next_sector; - next_chunk = next_sector / sectors_per_chunk; - } - } +/* Submit async read while handling COW. + * Returns: nb_sectors if no alignment is necessary, or + * (new_end - sector_num) if tail is rounded up or down due to + * alignment or buffer limit. + */ +static int mirror_do_read(MirrorBlockJob *s, int64_t sector_num, + int nb_sectors) +{ + BlockDriverState *source = s->common.bs; + int sectors_per_chunk, nb_chunks; + int ret = nb_sectors; + MirrorOp *op; - added_sectors = MIN(added_sectors, end - (sector_num + nb_sectors)); - added_chunks = (added_sectors + sectors_per_chunk - 1) / sectors_per_chunk; + sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS; - /* When doing COW, it may happen that there is not enough space for - * a full cluster. Wait if that is the case. - */ - while (nb_chunks == 0 && s->buf_free_count < added_chunks) { - trace_mirror_yield_buf_busy(s, nb_chunks, s->in_flight); - qemu_coroutine_yield(); - } - if (s->buf_free_count < nb_chunks + added_chunks) { - trace_mirror_break_buf_busy(s, nb_chunks, s->in_flight); - break; - } + /* We can only handle as much as buf_size at a time. */ + nb_sectors = MIN(s->buf_size >> BDRV_SECTOR_BITS, nb_sectors); + assert(nb_sectors); - /* We have enough free space to copy these sectors. */ - bitmap_set(s->in_flight_bitmap, next_chunk, added_chunks); + if (s->cow_bitmap) { + ret += mirror_cow_align(s, §or_num, &nb_sectors); + } + assert(nb_sectors << BDRV_SECTOR_BITS <= s->buf_size); + /* The sector range must meet granularity because: + * 1) Caller passes in aligned values; + * 2) mirror_cow_align is used only when target cluster is larger. */ + assert(!(sector_num % sectors_per_chunk)); + nb_chunks = DIV_ROUND_UP(nb_sectors, sectors_per_chunk); - nb_sectors += added_sectors; - nb_chunks += added_chunks; - next_sector += added_sectors; - next_chunk += added_chunks; - if (!s->synced && s->common.speed) { - delay_ns = ratelimit_calculate_delay(&s->limit, added_sectors); - } - } while (delay_ns == 0 && next_sector < end); + while (s->buf_free_count < nb_chunks) { + trace_mirror_yield_in_flight(s, sector_num, s->in_flight); + mirror_wait_for_io(s); + } /* Allocate a MirrorOp that is used as an AIO callback. */ - op = g_slice_new(MirrorOp); + op = g_new(MirrorOp, 1); op->s = s; op->sector_num = sector_num; op->nb_sectors = nb_sectors; @@ -268,47 +260,158 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) * from s->buf_free. */ qemu_iovec_init(&op->qiov, nb_chunks); - next_sector = sector_num; while (nb_chunks-- > 0) { MirrorBuffer *buf = QSIMPLEQ_FIRST(&s->buf_free); - size_t remaining = (nb_sectors * BDRV_SECTOR_SIZE) - op->qiov.size; + size_t remaining = nb_sectors * BDRV_SECTOR_SIZE - op->qiov.size; QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next); s->buf_free_count--; qemu_iovec_add(&op->qiov, buf, MIN(s->granularity, remaining)); - - /* Advance the HBitmapIter in parallel, so that we do not examine - * the same sector twice. - */ - if (next_sector > hbitmap_next_sector - && bdrv_get_dirty(source, s->dirty_bitmap, next_sector)) { - hbitmap_next_sector = hbitmap_iter_next(&s->hbi); - } - - next_sector += sectors_per_chunk; } - bdrv_reset_dirty_bitmap(s->dirty_bitmap, sector_num, nb_sectors); - /* Copy the dirty cluster. */ s->in_flight++; s->sectors_in_flight += nb_sectors; trace_mirror_one_iteration(s, sector_num, nb_sectors); - ret = bdrv_get_block_status_above(source, NULL, sector_num, - nb_sectors, &pnum); - if (ret < 0 || pnum < nb_sectors || - (ret & BDRV_BLOCK_DATA && !(ret & BDRV_BLOCK_ZERO))) { - bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors, - mirror_read_complete, op); - } else if (ret & BDRV_BLOCK_ZERO) { + bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors, + mirror_read_complete, op); + return ret; +} + +static void mirror_do_zero_or_discard(MirrorBlockJob *s, + int64_t sector_num, + int nb_sectors, + bool is_discard) +{ + MirrorOp *op; + + /* Allocate a MirrorOp that is used as an AIO callback. The qiov is zeroed + * so the freeing in mirror_iteration_done is nop. */ + op = g_new0(MirrorOp, 1); + op->s = s; + op->sector_num = sector_num; + op->nb_sectors = nb_sectors; + + s->in_flight++; + s->sectors_in_flight += nb_sectors; + if (is_discard) { + bdrv_aio_discard(s->target, sector_num, op->nb_sectors, + mirror_write_complete, op); + } else { bdrv_aio_write_zeroes(s->target, sector_num, op->nb_sectors, s->unmap ? BDRV_REQ_MAY_UNMAP : 0, mirror_write_complete, op); - } else { - assert(!(ret & BDRV_BLOCK_DATA)); - bdrv_aio_discard(s->target, sector_num, op->nb_sectors, - mirror_write_complete, op); + } +} + +static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) +{ + BlockDriverState *source = s->common.bs; + int64_t sector_num, first_chunk; + uint64_t delay_ns = 0; + /* At least the first dirty chunk is mirrored in one iteration. */ + int nb_chunks = 1; + int64_t end = s->bdev_length / BDRV_SECTOR_SIZE; + int sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS; + + sector_num = hbitmap_iter_next(&s->hbi); + if (sector_num < 0) { + bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi); + sector_num = hbitmap_iter_next(&s->hbi); + trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap)); + assert(sector_num >= 0); + } + + first_chunk = sector_num / sectors_per_chunk; + while (test_bit(first_chunk, s->in_flight_bitmap)) { + trace_mirror_yield_in_flight(s, first_chunk, s->in_flight); + mirror_wait_for_io(s); + } + + /* Find the number of consective dirty chunks following the first dirty + * one, and wait for in flight requests in them. */ + while (nb_chunks * sectors_per_chunk < (s->buf_size >> BDRV_SECTOR_BITS)) { + int64_t hbitmap_next; + int64_t next_sector = sector_num + nb_chunks * sectors_per_chunk; + int64_t next_chunk = next_sector / sectors_per_chunk; + if (next_sector >= end || + !bdrv_get_dirty(source, s->dirty_bitmap, next_sector)) { + break; + } + if (test_bit(next_chunk, s->in_flight_bitmap)) { + break; + } + + hbitmap_next = hbitmap_iter_next(&s->hbi); + if (hbitmap_next > next_sector || hbitmap_next < 0) { + /* The bitmap iterator's cache is stale, refresh it */ + bdrv_set_dirty_iter(&s->hbi, next_sector); + hbitmap_next = hbitmap_iter_next(&s->hbi); + } + assert(hbitmap_next == next_sector); + nb_chunks++; + } + + /* Clear dirty bits before querying the block status, because + * calling bdrv_get_block_status_above could yield - if some blocks are + * marked dirty in this window, we need to know. + */ + bdrv_reset_dirty_bitmap(s->dirty_bitmap, sector_num, + nb_chunks * sectors_per_chunk); + bitmap_set(s->in_flight_bitmap, sector_num / sectors_per_chunk, nb_chunks); + while (nb_chunks > 0 && sector_num < end) { + int ret; + int io_sectors; + BlockDriverState *file; + enum MirrorMethod { + MIRROR_METHOD_COPY, + MIRROR_METHOD_ZERO, + MIRROR_METHOD_DISCARD + } mirror_method = MIRROR_METHOD_COPY; + + assert(!(sector_num % sectors_per_chunk)); + ret = bdrv_get_block_status_above(source, NULL, sector_num, + nb_chunks * sectors_per_chunk, + &io_sectors, &file); + if (ret < 0) { + io_sectors = nb_chunks * sectors_per_chunk; + } + + io_sectors -= io_sectors % sectors_per_chunk; + if (io_sectors < sectors_per_chunk) { + io_sectors = sectors_per_chunk; + } else if (ret >= 0 && !(ret & BDRV_BLOCK_DATA)) { + int64_t target_sector_num; + int target_nb_sectors; + bdrv_round_to_clusters(s->target, sector_num, io_sectors, + &target_sector_num, &target_nb_sectors); + if (target_sector_num == sector_num && + target_nb_sectors == io_sectors) { + mirror_method = ret & BDRV_BLOCK_ZERO ? + MIRROR_METHOD_ZERO : + MIRROR_METHOD_DISCARD; + } + } + + mirror_clip_sectors(s, sector_num, &io_sectors); + switch (mirror_method) { + case MIRROR_METHOD_COPY: + io_sectors = mirror_do_read(s, sector_num, io_sectors); + break; + case MIRROR_METHOD_ZERO: + mirror_do_zero_or_discard(s, sector_num, io_sectors, false); + break; + case MIRROR_METHOD_DISCARD: + mirror_do_zero_or_discard(s, sector_num, io_sectors, true); + break; + default: + abort(); + } + assert(io_sectors); + sector_num += io_sectors; + nb_chunks -= DIV_ROUND_UP(io_sectors, sectors_per_chunk); + delay_ns += ratelimit_calculate_delay(&s->limit, io_sectors); } return delay_ns; } @@ -333,7 +436,7 @@ static void mirror_free_init(MirrorBlockJob *s) static void mirror_drain(MirrorBlockJob *s) { while (s->in_flight > 0) { - qemu_coroutine_yield(); + mirror_wait_for_io(s); } } @@ -346,6 +449,11 @@ static void mirror_exit(BlockJob *job, void *opaque) MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); MirrorExitData *data = opaque; AioContext *replace_aio_context = NULL; + BlockDriverState *src = s->common.bs; + + /* Make sure that the source BDS doesn't go away before we called + * block_job_completed(). */ + bdrv_ref(src); if (s->to_replace) { replace_aio_context = bdrv_get_aio_context(s->to_replace); @@ -357,18 +465,22 @@ static void mirror_exit(BlockJob *job, void *opaque) if (s->to_replace) { to_replace = s->to_replace; } + + /* This was checked in mirror_start_job(), but meanwhile one of the + * nodes could have been newly attached to a BlockBackend. */ + if (to_replace->blk && s->target->blk) { + error_report("block job: Can't create node with two BlockBackends"); + data->ret = -EINVAL; + goto out; + } + if (bdrv_get_flags(s->target) != bdrv_get_flags(to_replace)) { bdrv_reopen(s->target, bdrv_get_flags(to_replace), NULL); } - bdrv_swap(s->target, to_replace); - if (s->common.driver->job_type == BLOCK_JOB_TYPE_COMMIT) { - /* drop the bs loop chain formed by the swap: break the loop then - * trigger the unref from the top one */ - BlockDriverState *p = s->base->backing_hd; - bdrv_set_backing_hd(s->base, NULL); - bdrv_unref(p); - } + bdrv_replace_in_backing_chain(to_replace, s->target); } + +out: if (s->to_replace) { bdrv_op_unblock_all(s->to_replace, s->replace_blocker); error_free(s->replace_blocker); @@ -378,9 +490,15 @@ static void mirror_exit(BlockJob *job, void *opaque) aio_context_release(replace_aio_context); } g_free(s->replaces); + bdrv_op_unblock_all(s->target, s->common.blocker); bdrv_unref(s->target); block_job_completed(&s->common, data->ret); g_free(data); + bdrv_drained_end(src); + if (qemu_get_aio_context() == bdrv_get_aio_context(src)) { + aio_enable_external(iohandler_get_aio_context()); + } + bdrv_unref(src); } static void coroutine_fn mirror_run(void *opaque) @@ -395,6 +513,7 @@ static void coroutine_fn mirror_run(void *opaque) checking for a NULL string */ int ret = 0; int n; + int target_cluster_size = BDRV_SECTOR_SIZE; if (block_job_is_cancelled(&s->common)) { goto immediate_exit; @@ -424,16 +543,16 @@ static void coroutine_fn mirror_run(void *opaque) */ bdrv_get_backing_filename(s->target, backing_filename, sizeof(backing_filename)); - if (backing_filename[0] && !s->target->backing_hd) { - ret = bdrv_get_info(s->target, &bdi); - if (ret < 0) { - goto immediate_exit; - } - if (s->granularity < bdi.cluster_size) { - s->buf_size = MAX(s->buf_size, bdi.cluster_size); - s->cow_bitmap = bitmap_new(length); - } + if (!bdrv_get_info(s->target, &bdi) && bdi.cluster_size) { + target_cluster_size = bdi.cluster_size; } + if (backing_filename[0] && !s->target->backing + && s->granularity < target_cluster_size) { + s->buf_size = MAX(s->buf_size, target_cluster_size); + s->cow_bitmap = bitmap_new(length); + } + s->target_cluster_sectors = target_cluster_size >> BDRV_SECTOR_BITS; + s->max_iov = MIN(s->common.bs->bl.max_iov, s->target->bl.max_iov); end = s->bdev_length / BDRV_SECTOR_SIZE; s->buf = qemu_try_blockalign(bs, s->buf_size); @@ -448,6 +567,8 @@ static void coroutine_fn mirror_run(void *opaque) if (!s->is_none_mode) { /* First part, loop on the sectors and initialize the dirty bitmap. */ BlockDriverState *base = s->base; + bool mark_all_dirty = s->base == NULL && !bdrv_has_zero_init(s->target); + for (sector_num = 0; sector_num < end; ) { /* Just to make sure we are not exceeding int limit. */ int nb_sectors = MIN(INT_MAX >> BDRV_SECTOR_BITS, @@ -470,7 +591,7 @@ static void coroutine_fn mirror_run(void *opaque) } assert(n > 0); - if (ret == 1) { + if (ret == 1 || mark_all_dirty) { bdrv_set_dirty_bitmap(s->dirty_bitmap, sector_num, n); } sector_num += n; @@ -506,7 +627,7 @@ static void coroutine_fn mirror_run(void *opaque) if (s->in_flight == MAX_IN_FLIGHT || s->buf_free_count == 0 || (cnt == 0 && s->in_flight > 0)) { trace_mirror_yield(s, s->in_flight, s->buf_free_count, cnt); - qemu_coroutine_yield(); + mirror_wait_for_io(s); continue; } else if (cnt != 0) { delay_ns = mirror_iteration(s); @@ -549,7 +670,7 @@ static void coroutine_fn mirror_run(void *opaque) * mirror_populate runs. */ trace_mirror_before_drain(s, cnt); - bdrv_drain(bs); + bdrv_co_drain(bs); cnt = bdrv_get_dirty_count(s->dirty_bitmap); } @@ -589,10 +710,21 @@ immediate_exit: g_free(s->cow_bitmap); g_free(s->in_flight_bitmap); bdrv_release_dirty_bitmap(bs, s->dirty_bitmap); - bdrv_iostatus_disable(s->target); + if (s->target->blk) { + blk_iostatus_disable(s->target->blk); + } data = g_malloc(sizeof(*data)); data->ret = ret; + /* Before we switch to target in mirror_exit, make sure data doesn't + * change. */ + bdrv_drained_begin(s->common.bs); + if (qemu_get_aio_context() == bdrv_get_aio_context(bs)) { + /* FIXME: virtio host notifiers run on iohandler_ctx, therefore the + * above bdrv_drained_end isn't enough to quiesce it. This is ugly, we + * need a block layer API change to achieve this. */ + aio_disable_external(iohandler_get_aio_context()); + } block_job_defer_to_main_loop(&s->common, mirror_exit, data); } @@ -611,7 +743,9 @@ static void mirror_iostatus_reset(BlockJob *job) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); - bdrv_iostatus_reset(s->target); + if (s->target->blk) { + blk_iostatus_reset(s->target->blk); + } } static void mirror_complete(BlockJob *job, Error **errp) @@ -620,14 +754,13 @@ static void mirror_complete(BlockJob *job, Error **errp) Error *local_err = NULL; int ret; - ret = bdrv_open_backing_file(s->target, NULL, &local_err); + ret = bdrv_open_backing_file(s->target, NULL, "backing", &local_err); if (ret < 0) { error_propagate(errp, local_err); return; } if (!s->synced) { - error_setg(errp, QERR_BLOCK_JOB_NOT_READY, - bdrv_get_device_name(job->bs)); + error_setg(errp, QERR_BLOCK_JOB_NOT_READY, job->id); return; } @@ -635,9 +768,9 @@ static void mirror_complete(BlockJob *job, Error **errp) if (s->replaces) { AioContext *replace_aio_context; - s->to_replace = check_to_replace_node(s->replaces, &local_err); + s->to_replace = bdrv_find_node(s->replaces); if (!s->to_replace) { - error_propagate(errp, local_err); + error_setg(errp, "Node name '%s' not found", s->replaces); return; } @@ -686,6 +819,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, bool is_none_mode, BlockDriverState *base) { MirrorBlockJob *s; + BlockDriverState *replaced_bs; if (granularity == 0) { granularity = bdrv_get_default_bitmap_granularity(target); @@ -695,7 +829,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, if ((on_source_error == BLOCKDEV_ON_ERROR_STOP || on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) && - !bdrv_iostatus_is_enabled(bs)) { + (!bs->blk || !blk_iostatus_is_enabled(bs->blk))) { error_setg(errp, QERR_INVALID_PARAMETER, "on-source-error"); return; } @@ -709,6 +843,21 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, buf_size = DEFAULT_MIRROR_BUF_SIZE; } + /* We can't support this case as long as the block layer can't handle + * multiple BlockBackends per BlockDriverState. */ + if (replaces) { + replaced_bs = bdrv_lookup_bs(replaces, replaces, errp); + if (replaced_bs == NULL) { + return; + } + } else { + replaced_bs = bs; + } + if (replaced_bs->blk && target->blk) { + error_setg(errp, "Can't create node with two BlockBackends"); + return; + } + s = block_job_create(driver, bs, speed, cb, opaque, errp); if (!s) { return; @@ -727,12 +876,16 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp); if (!s->dirty_bitmap) { g_free(s->replaces); - block_job_release(bs); + block_job_unref(&s->common); return; } - bdrv_set_enable_write_cache(s->target, true); - bdrv_set_on_error(s->target, on_target_error, on_target_error); - bdrv_iostatus_enable(s->target); + + bdrv_op_block_all(s->target, s->common.blocker); + + if (s->target->blk) { + blk_set_on_error(s->target->blk, on_target_error, on_target_error); + blk_iostatus_enable(s->target->blk); + } s->common.co = qemu_coroutine_create(mirror_run); trace_mirror_start(bs, s, s->common.co, opaque); qemu_coroutine_enter(s->common.co, s); @@ -755,7 +908,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target, return; } is_none_mode = mode == MIRROR_SYNC_MODE_NONE; - base = mode == MIRROR_SYNC_MODE_TOP ? bs->backing_hd : NULL; + base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL; mirror_start_job(bs, target, replaces, speed, granularity, buf_size, on_source_error, on_target_error, unmap, cb, opaque, errp, diff --git a/qemu/block/nbd-client.c b/qemu/block/nbd-client.c index e1bb9198c..878e879ac 100644 --- a/qemu/block/nbd-client.c +++ b/qemu/block/nbd-client.c @@ -26,8 +26,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "nbd-client.h" -#include "qemu/sockets.h" #define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs)) #define INDEX_TO_HANDLE(bs, index) ((index) ^ ((uint64_t)(intptr_t)bs)) @@ -47,13 +47,21 @@ static void nbd_teardown_connection(BlockDriverState *bs) { NbdClientSession *client = nbd_get_client_session(bs); + if (!client->ioc) { /* Already closed */ + return; + } + /* finish any pending coroutines */ - shutdown(client->sock, 2); + qio_channel_shutdown(client->ioc, + QIO_CHANNEL_SHUTDOWN_BOTH, + NULL); nbd_recv_coroutines_enter_all(client); nbd_client_detach_aio_context(bs); - closesocket(client->sock); - client->sock = -1; + object_unref(OBJECT(client->sioc)); + client->sioc = NULL; + object_unref(OBJECT(client->ioc)); + client->ioc = NULL; } static void nbd_reply_ready(void *opaque) @@ -63,12 +71,16 @@ static void nbd_reply_ready(void *opaque) uint64_t i; int ret; + if (!s->ioc) { /* Already closed */ + return; + } + if (s->reply.handle == 0) { /* No reply already in flight. Fetch a header. It is possible * that another thread has done the same thing in parallel, so * the socket is not readable anymore. */ - ret = nbd_receive_reply(s->sock, &s->reply); + ret = nbd_receive_reply(s->ioc, &s->reply); if (ret == -EAGAIN) { return; } @@ -119,32 +131,36 @@ static int nbd_co_send_request(BlockDriverState *bs, } } + g_assert(qemu_in_coroutine()); assert(i < MAX_NBD_REQUESTS); request->handle = INDEX_TO_HANDLE(s, i); + + if (!s->ioc) { + qemu_co_mutex_unlock(&s->send_mutex); + return -EPIPE; + } + s->send_coroutine = qemu_coroutine_self(); aio_context = bdrv_get_aio_context(bs); - aio_set_fd_handler(aio_context, s->sock, + aio_set_fd_handler(aio_context, s->sioc->fd, false, nbd_reply_ready, nbd_restart_write, bs); if (qiov) { - if (!s->is_unix) { - socket_set_cork(s->sock, 1); - } - rc = nbd_send_request(s->sock, request); + qio_channel_set_cork(s->ioc, true); + rc = nbd_send_request(s->ioc, request); if (rc >= 0) { - ret = qemu_co_sendv(s->sock, qiov->iov, qiov->niov, - offset, request->len); + ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov, + offset, request->len, 0); if (ret != request->len) { rc = -EIO; } } - if (!s->is_unix) { - socket_set_cork(s->sock, 0); - } + qio_channel_set_cork(s->ioc, false); } else { - rc = nbd_send_request(s->sock, request); + rc = nbd_send_request(s->ioc, request); } - aio_set_fd_handler(aio_context, s->sock, nbd_reply_ready, NULL, bs); + aio_set_fd_handler(aio_context, s->sioc->fd, false, + nbd_reply_ready, NULL, bs); s->send_coroutine = NULL; qemu_co_mutex_unlock(&s->send_mutex); return rc; @@ -160,12 +176,13 @@ static void nbd_co_receive_reply(NbdClientSession *s, * peek at the next reply and avoid yielding if it's ours? */ qemu_coroutine_yield(); *reply = s->reply; - if (reply->handle != request->handle) { + if (reply->handle != request->handle || + !s->ioc) { reply->error = EIO; } else { if (qiov && reply->error == 0) { - ret = qemu_co_recvv(s->sock, qiov->iov, qiov->niov, - offset, request->len); + ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov, + offset, request->len, 1); if (ret != request->len) { reply->error = EIO; } @@ -226,15 +243,15 @@ static int nbd_co_readv_1(BlockDriverState *bs, int64_t sector_num, static int nbd_co_writev_1(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, - int offset) + int offset, int *flags) { NbdClientSession *client = nbd_get_client_session(bs); struct nbd_request request = { .type = NBD_CMD_WRITE }; struct nbd_reply reply; ssize_t ret; - if (!bdrv_enable_write_cache(bs) && - (client->nbdflags & NBD_FLAG_SEND_FUA)) { + if ((*flags & BDRV_REQ_FUA) && (client->nbdflags & NBD_FLAG_SEND_FUA)) { + *flags &= ~BDRV_REQ_FUA; request.type |= NBD_CMD_FLAG_FUA; } @@ -274,12 +291,13 @@ int nbd_client_co_readv(BlockDriverState *bs, int64_t sector_num, } int nbd_client_co_writev(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) + int nb_sectors, QEMUIOVector *qiov, int *flags) { int offset = 0; int ret; while (nb_sectors > NBD_MAX_SECTORS) { - ret = nbd_co_writev_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset); + ret = nbd_co_writev_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset, + flags); if (ret < 0) { return ret; } @@ -287,7 +305,7 @@ int nbd_client_co_writev(BlockDriverState *bs, int64_t sector_num, sector_num += NBD_MAX_SECTORS; nb_sectors -= NBD_MAX_SECTORS; } - return nbd_co_writev_1(bs, sector_num, nb_sectors, qiov, offset); + return nbd_co_writev_1(bs, sector_num, nb_sectors, qiov, offset, flags); } int nbd_client_co_flush(BlockDriverState *bs) @@ -301,10 +319,6 @@ int nbd_client_co_flush(BlockDriverState *bs) return 0; } - if (client->nbdflags & NBD_FLAG_SEND_FUA) { - request.type |= NBD_CMD_FLAG_FUA; - } - request.from = 0; request.len = 0; @@ -348,14 +362,15 @@ int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num, void nbd_client_detach_aio_context(BlockDriverState *bs) { aio_set_fd_handler(bdrv_get_aio_context(bs), - nbd_get_client_session(bs)->sock, NULL, NULL, NULL); + nbd_get_client_session(bs)->sioc->fd, + false, NULL, NULL, NULL); } void nbd_client_attach_aio_context(BlockDriverState *bs, AioContext *new_context) { - aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sock, - nbd_reply_ready, NULL, bs); + aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sioc->fd, + false, nbd_reply_ready, NULL, bs); } void nbd_client_close(BlockDriverState *bs) @@ -367,16 +382,20 @@ void nbd_client_close(BlockDriverState *bs) .len = 0 }; - if (client->sock == -1) { + if (client->ioc == NULL) { return; } - nbd_send_request(client->sock, &request); + nbd_send_request(client->ioc, &request); nbd_teardown_connection(bs); } -int nbd_client_init(BlockDriverState *bs, int sock, const char *export, +int nbd_client_init(BlockDriverState *bs, + QIOChannelSocket *sioc, + const char *export, + QCryptoTLSCreds *tlscreds, + const char *hostname, Error **errp) { NbdClientSession *client = nbd_get_client_session(bs); @@ -384,22 +403,32 @@ int nbd_client_init(BlockDriverState *bs, int sock, const char *export, /* NBD handshake */ logout("session init %s\n", export); - qemu_set_block(sock); - ret = nbd_receive_negotiate(sock, export, - &client->nbdflags, &client->size, errp); + qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL); + + ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, + &client->nbdflags, + tlscreds, hostname, + &client->ioc, + &client->size, errp); if (ret < 0) { logout("Failed to negotiate with the NBD server\n"); - closesocket(sock); return ret; } qemu_co_mutex_init(&client->send_mutex); qemu_co_mutex_init(&client->free_sema); - client->sock = sock; + client->sioc = sioc; + object_ref(OBJECT(client->sioc)); + + if (!client->ioc) { + client->ioc = QIO_CHANNEL(sioc); + object_ref(OBJECT(client->ioc)); + } /* Now that we're connected, set the socket to be non-blocking and * kick the reply mechanism. */ - qemu_set_nonblock(sock); + qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL); + nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs)); logout("Established connection with NBD server\n"); diff --git a/qemu/block/nbd-client.h b/qemu/block/nbd-client.h index e8413408b..bc7aec079 100644 --- a/qemu/block/nbd-client.h +++ b/qemu/block/nbd-client.h @@ -4,6 +4,7 @@ #include "qemu-common.h" #include "block/nbd.h" #include "block/block_int.h" +#include "io/channel-socket.h" /* #define DEBUG_NBD */ @@ -17,7 +18,8 @@ #define MAX_NBD_REQUESTS 16 typedef struct NbdClientSession { - int sock; + QIOChannelSocket *sioc; /* The master data channel */ + QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */ uint32_t nbdflags; off_t size; @@ -34,7 +36,11 @@ typedef struct NbdClientSession { NbdClientSession *nbd_get_client_session(BlockDriverState *bs); -int nbd_client_init(BlockDriverState *bs, int sock, const char *export_name, +int nbd_client_init(BlockDriverState *bs, + QIOChannelSocket *sock, + const char *export_name, + QCryptoTLSCreds *tlscreds, + const char *hostname, Error **errp); void nbd_client_close(BlockDriverState *bs); @@ -42,7 +48,7 @@ int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors); int nbd_client_co_flush(BlockDriverState *bs); int nbd_client_co_writev(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov); + int nb_sectors, QEMUIOVector *qiov, int *flags); int nbd_client_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); diff --git a/qemu/block/nbd.c b/qemu/block/nbd.c index 217618612..f7ea3b360 100644 --- a/qemu/block/nbd.c +++ b/qemu/block/nbd.c @@ -26,24 +26,22 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "block/nbd-client.h" +#include "qapi/error.h" #include "qemu/uri.h" #include "block/block_int.h" #include "qemu/module.h" -#include "qemu/sockets.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qint.h" #include "qapi/qmp/qstring.h" - -#include -#include +#include "qemu/cutils.h" #define EN_OPTSTR ":exportname=" typedef struct BDRVNBDState { NbdClientSession client; - QemuOpts *socket_opts; } BDRVNBDState; static int nbd_parse_uri(const char *filename, QDict *options) @@ -190,10 +188,10 @@ out: g_free(file); } -static void nbd_config(BDRVNBDState *s, QDict *options, char **export, - Error **errp) +static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, char **export, + Error **errp) { - Error *local_err = NULL; + SocketAddress *saddr; if (qdict_haskey(options, "path") == qdict_haskey(options, "host")) { if (qdict_haskey(options, "path")) { @@ -201,28 +199,39 @@ static void nbd_config(BDRVNBDState *s, QDict *options, char **export, } else { error_setg(errp, "one of path and host must be specified."); } - return; + return NULL; } - s->client.is_unix = qdict_haskey(options, "path"); - s->socket_opts = qemu_opts_create(&socket_optslist, NULL, 0, - &error_abort); + saddr = g_new0(SocketAddress, 1); - qemu_opts_absorb_qdict(s->socket_opts, options, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; + if (qdict_haskey(options, "path")) { + UnixSocketAddress *q_unix; + saddr->type = SOCKET_ADDRESS_KIND_UNIX; + q_unix = saddr->u.q_unix.data = g_new0(UnixSocketAddress, 1); + q_unix->path = g_strdup(qdict_get_str(options, "path")); + qdict_del(options, "path"); + } else { + InetSocketAddress *inet; + saddr->type = SOCKET_ADDRESS_KIND_INET; + inet = saddr->u.inet.data = g_new0(InetSocketAddress, 1); + inet->host = g_strdup(qdict_get_str(options, "host")); + if (!qdict_get_try_str(options, "port")) { + inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT); + } else { + inet->port = g_strdup(qdict_get_str(options, "port")); + } + qdict_del(options, "host"); + qdict_del(options, "port"); } - if (!qemu_opt_get(s->socket_opts, "port")) { - qemu_opt_set_number(s->socket_opts, "port", NBD_DEFAULT_PORT, - &error_abort); - } + s->client.is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; *export = g_strdup(qdict_get_try_str(options, "export")); if (*export) { qdict_del(options, "export"); } + + return saddr; } NbdClientSession *nbd_get_client_session(BlockDriverState *bs) @@ -231,57 +240,113 @@ NbdClientSession *nbd_get_client_session(BlockDriverState *bs) return &s->client; } -static int nbd_establish_connection(BlockDriverState *bs, Error **errp) +static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr, + Error **errp) { - BDRVNBDState *s = bs->opaque; - int sock; + QIOChannelSocket *sioc; + Error *local_err = NULL; - if (s->client.is_unix) { - sock = unix_connect_opts(s->socket_opts, errp, NULL, NULL); - } else { - sock = inet_connect_opts(s->socket_opts, errp, NULL, NULL); - if (sock >= 0) { - socket_set_nodelay(sock); - } + sioc = qio_channel_socket_new(); + + qio_channel_socket_connect_sync(sioc, + saddr, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return NULL; } - /* Failed to establish connection */ - if (sock < 0) { - logout("Failed to establish connection to NBD server\n"); - return -EIO; + qio_channel_set_delay(QIO_CHANNEL(sioc), false); + + return sioc; +} + + +static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) +{ + Object *obj; + QCryptoTLSCreds *creds; + + obj = object_resolve_path_component( + object_get_objects_root(), id); + if (!obj) { + error_setg(errp, "No TLS credentials with id '%s'", + id); + return NULL; + } + creds = (QCryptoTLSCreds *) + object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS); + if (!creds) { + error_setg(errp, "Object with id '%s' is not TLS credentials", + id); + return NULL; } - return sock; + if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { + error_setg(errp, + "Expecting TLS credentials with a client endpoint"); + return NULL; + } + object_ref(obj); + return creds; } + static int nbd_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVNBDState *s = bs->opaque; char *export = NULL; - int result, sock; - Error *local_err = NULL; + QIOChannelSocket *sioc = NULL; + SocketAddress *saddr; + const char *tlscredsid; + QCryptoTLSCreds *tlscreds = NULL; + const char *hostname = NULL; + int ret = -EINVAL; /* Pop the config into our state object. Exit if invalid. */ - nbd_config(s, options, &export, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return -EINVAL; + saddr = nbd_config(s, options, &export, errp); + if (!saddr) { + goto error; + } + + tlscredsid = g_strdup(qdict_get_try_str(options, "tls-creds")); + if (tlscredsid) { + qdict_del(options, "tls-creds"); + tlscreds = nbd_get_tls_creds(tlscredsid, errp); + if (!tlscreds) { + goto error; + } + + if (saddr->type != SOCKET_ADDRESS_KIND_INET) { + error_setg(errp, "TLS only supported over IP sockets"); + goto error; + } + hostname = saddr->u.inet.data->host; } /* establish TCP connection, return error if it fails * TODO: Configurable retry-until-timeout behaviour. */ - sock = nbd_establish_connection(bs, errp); - if (sock < 0) { - g_free(export); - return sock; + sioc = nbd_establish_connection(saddr, errp); + if (!sioc) { + ret = -ECONNREFUSED; + goto error; } /* NBD handshake */ - result = nbd_client_init(bs, sock, export, errp); + ret = nbd_client_init(bs, sioc, export, + tlscreds, hostname, errp); + error: + if (sioc) { + object_unref(OBJECT(sioc)); + } + if (tlscreds) { + object_unref(OBJECT(tlscreds)); + } + qapi_free_SocketAddress(saddr); g_free(export); - return result; + return ret; } static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num, @@ -290,10 +355,29 @@ static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num, return nbd_client_co_readv(bs, sector_num, nb_sectors, qiov); } +static int nbd_co_writev_flags(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov, int flags) +{ + int ret; + + ret = nbd_client_co_writev(bs, sector_num, nb_sectors, qiov, &flags); + if (ret < 0) { + return ret; + } + + /* The flag wasn't sent to the server, so we need to emulate it with an + * explicit flush */ + if (flags & BDRV_REQ_FUA) { + ret = nbd_client_co_flush(bs); + } + + return ret; +} + static int nbd_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { - return nbd_client_co_writev(bs, sector_num, nb_sectors, qiov); + return nbd_co_writev_flags(bs, sector_num, nb_sectors, qiov, 0); } static int nbd_co_flush(BlockDriverState *bs) @@ -315,9 +399,6 @@ static int nbd_co_discard(BlockDriverState *bs, int64_t sector_num, static void nbd_close(BlockDriverState *bs) { - BDRVNBDState *s = bs->opaque; - - qemu_opts_del(s->socket_opts); nbd_client_close(bs); } @@ -339,13 +420,14 @@ static void nbd_attach_aio_context(BlockDriverState *bs, nbd_client_attach_aio_context(bs, new_context); } -static void nbd_refresh_filename(BlockDriverState *bs) +static void nbd_refresh_filename(BlockDriverState *bs, QDict *options) { QDict *opts = qdict_new(); - const char *path = qdict_get_try_str(bs->options, "path"); - const char *host = qdict_get_try_str(bs->options, "host"); - const char *port = qdict_get_try_str(bs->options, "port"); - const char *export = qdict_get_try_str(bs->options, "export"); + const char *path = qdict_get_try_str(options, "path"); + const char *host = qdict_get_try_str(options, "host"); + const char *port = qdict_get_try_str(options, "port"); + const char *export = qdict_get_try_str(options, "export"); + const char *tlscreds = qdict_get_try_str(options, "tls-creds"); qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("nbd"))); @@ -380,6 +462,9 @@ static void nbd_refresh_filename(BlockDriverState *bs) if (export) { qdict_put_obj(opts, "export", QOBJECT(qstring_from_str(export))); } + if (tlscreds) { + qdict_put_obj(opts, "tls-creds", QOBJECT(qstring_from_str(tlscreds))); + } bs->full_open_options = opts; } @@ -392,6 +477,8 @@ static BlockDriver bdrv_nbd = { .bdrv_file_open = nbd_open, .bdrv_co_readv = nbd_co_readv, .bdrv_co_writev = nbd_co_writev, + .bdrv_co_writev_flags = nbd_co_writev_flags, + .supported_write_flags = BDRV_REQ_FUA, .bdrv_close = nbd_close, .bdrv_co_flush_to_os = nbd_co_flush, .bdrv_co_discard = nbd_co_discard, @@ -410,6 +497,8 @@ static BlockDriver bdrv_nbd_tcp = { .bdrv_file_open = nbd_open, .bdrv_co_readv = nbd_co_readv, .bdrv_co_writev = nbd_co_writev, + .bdrv_co_writev_flags = nbd_co_writev_flags, + .supported_write_flags = BDRV_REQ_FUA, .bdrv_close = nbd_close, .bdrv_co_flush_to_os = nbd_co_flush, .bdrv_co_discard = nbd_co_discard, @@ -428,6 +517,8 @@ static BlockDriver bdrv_nbd_unix = { .bdrv_file_open = nbd_open, .bdrv_co_readv = nbd_co_readv, .bdrv_co_writev = nbd_co_writev, + .bdrv_co_writev_flags = nbd_co_writev_flags, + .supported_write_flags = BDRV_REQ_FUA, .bdrv_close = nbd_close, .bdrv_co_flush_to_os = nbd_co_flush, .bdrv_co_discard = nbd_co_discard, diff --git a/qemu/block/nfs.c b/qemu/block/nfs.c index c026ff688..9f51cc3f1 100644 --- a/qemu/block/nfs.c +++ b/qemu/block/nfs.c @@ -22,20 +22,23 @@ * THE SOFTWARE. */ -#include "config-host.h" +#include "qemu/osdep.h" #include #include "qemu-common.h" #include "qemu/config-file.h" #include "qemu/error-report.h" +#include "qapi/error.h" #include "block/block_int.h" #include "trace.h" #include "qemu/iov.h" #include "qemu/uri.h" +#include "qemu/cutils.h" #include "sysemu/sysemu.h" #include #define QEMU_NFS_MAX_READAHEAD_SIZE 1048576 +#define QEMU_NFS_MAX_DEBUG_LEVEL 2 typedef struct NFSClient { struct nfs_context *context; @@ -43,6 +46,7 @@ typedef struct NFSClient { int events; bool has_zero_init; AioContext *aio_context; + blkcnt_t st_blocks; } NFSClient; typedef struct NFSRPC { @@ -62,11 +66,10 @@ static void nfs_set_events(NFSClient *client) { int ev = nfs_which_events(client->context); if (ev != client->events) { - aio_set_fd_handler(client->aio_context, - nfs_get_fd(client->context), + aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context), + false, (ev & POLLIN) ? nfs_process_read : NULL, - (ev & POLLOUT) ? nfs_process_write : NULL, - client); + (ev & POLLOUT) ? nfs_process_write : NULL, client); } client->events = ev; @@ -241,9 +244,8 @@ static void nfs_detach_aio_context(BlockDriverState *bs) { NFSClient *client = bs->opaque; - aio_set_fd_handler(client->aio_context, - nfs_get_fd(client->context), - NULL, NULL, NULL); + aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context), + false, NULL, NULL, NULL); client->events = 0; } @@ -262,9 +264,8 @@ static void nfs_client_close(NFSClient *client) if (client->fh) { nfs_close(client->context, client->fh); } - aio_set_fd_handler(client->aio_context, - nfs_get_fd(client->context), - NULL, NULL, NULL); + aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context), + false, NULL, NULL, NULL); nfs_destroy_context(client->context); } memset(client, 0, sizeof(NFSClient)); @@ -335,6 +336,17 @@ static int64_t nfs_client_open(NFSClient *client, const char *filename, val = QEMU_NFS_MAX_READAHEAD_SIZE; } nfs_set_readahead(client->context, val); +#endif +#ifdef LIBNFS_FEATURE_DEBUG + } else if (!strcmp(qp->p[i].name, "debug")) { + /* limit the maximum debug level to avoid potential flooding + * of our log files. */ + if (val > QEMU_NFS_MAX_DEBUG_LEVEL) { + error_report("NFS Warning: Limiting NFS debug level" + " to %d", QEMU_NFS_MAX_DEBUG_LEVEL); + val = QEMU_NFS_MAX_DEBUG_LEVEL; + } + nfs_set_debug(client->context, val); #endif } else { error_setg(errp, "Unknown NFS parameter name: %s", @@ -374,6 +386,7 @@ static int64_t nfs_client_open(NFSClient *client, const char *filename, } ret = DIV_ROUND_UP(st.st_size, BDRV_SECTOR_SIZE); + client->st_blocks = st.st_blocks; client->has_zero_init = S_ISREG(st.st_mode); goto out; fail: @@ -464,6 +477,11 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) NFSRPC task = {0}; struct stat st; + if (bdrv_is_read_only(bs) && + !(bs->open_flags & BDRV_O_NOCACHE)) { + return client->st_blocks * 512; + } + task.st = &st; if (nfs_fstat_async(client->context, client->fh, nfs_co_generic_cb, &task) != 0) { @@ -475,7 +493,7 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) aio_poll(client->aio_context, true); } - return (task.ret < 0 ? task.ret : st.st_blocks * st.st_blksize); + return (task.ret < 0 ? task.ret : st.st_blocks * 512); } static int nfs_file_truncate(BlockDriverState *bs, int64_t offset) @@ -484,6 +502,34 @@ static int nfs_file_truncate(BlockDriverState *bs, int64_t offset) return nfs_ftruncate(client->context, client->fh, offset); } +/* Note that this will not re-establish a connection with the NFS server + * - it is effectively a NOP. */ +static int nfs_reopen_prepare(BDRVReopenState *state, + BlockReopenQueue *queue, Error **errp) +{ + NFSClient *client = state->bs->opaque; + struct stat st; + int ret = 0; + + if (state->flags & BDRV_O_RDWR && bdrv_is_read_only(state->bs)) { + error_setg(errp, "Cannot open a read-only mount as read-write"); + return -EACCES; + } + + /* Update cache for read-only reopens */ + if (!(state->flags & BDRV_O_RDWR)) { + ret = nfs_fstat(client->context, client->fh, &st); + if (ret < 0) { + error_setg(errp, "Failed to fstat file: %s", + nfs_get_error(client->context)); + return ret; + } + client->st_blocks = st.st_blocks; + } + + return 0; +} + static BlockDriver bdrv_nfs = { .format_name = "nfs", .protocol_name = "nfs", @@ -499,6 +545,7 @@ static BlockDriver bdrv_nfs = { .bdrv_file_open = nfs_file_open, .bdrv_close = nfs_file_close, .bdrv_create = nfs_file_create, + .bdrv_reopen_prepare = nfs_reopen_prepare, .bdrv_co_readv = nfs_co_readv, .bdrv_co_writev = nfs_co_writev, diff --git a/qemu/block/null.c b/qemu/block/null.c index 7d083233f..396500bab 100644 --- a/qemu/block/null.c +++ b/qemu/block/null.c @@ -10,13 +10,17 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "block/block_int.h" #define NULL_OPT_LATENCY "latency-ns" +#define NULL_OPT_ZEROES "read-zeroes" typedef struct { int64_t length; int64_t latency_ns; + bool read_zeroes; } BDRVNullState; static QemuOptsList runtime_opts = { @@ -39,6 +43,11 @@ static QemuOptsList runtime_opts = { .help = "nanoseconds (approximated) to wait " "before completing request", }, + { + .name = NULL_OPT_ZEROES, + .type = QEMU_OPT_BOOL, + .help = "return zeroes when read", + }, { /* end of list */ } }, }; @@ -60,6 +69,7 @@ static int null_file_open(BlockDriverState *bs, QDict *options, int flags, error_setg(errp, "latency-ns is invalid"); ret = -EINVAL; } + s->read_zeroes = qemu_opt_get_bool(opts, NULL_OPT_ZEROES, false); qemu_opts_del(opts); return ret; } @@ -89,6 +99,12 @@ static coroutine_fn int null_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { + BDRVNullState *s = bs->opaque; + + if (s->read_zeroes) { + qemu_iovec_memset(qiov, 0, 0, nb_sectors * BDRV_SECTOR_SIZE); + } + return null_co_common(bs); } @@ -158,6 +174,12 @@ static BlockAIOCB *null_aio_readv(BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque) { + BDRVNullState *s = bs->opaque; + + if (s->read_zeroes) { + qemu_iovec_memset(qiov, 0, 0, nb_sectors * BDRV_SECTOR_SIZE); + } + return null_aio_common(bs, cb, opaque); } @@ -183,6 +205,24 @@ static int null_reopen_prepare(BDRVReopenState *reopen_state, return 0; } +static int64_t coroutine_fn null_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, int *pnum, + BlockDriverState **file) +{ + BDRVNullState *s = bs->opaque; + off_t start = sector_num * BDRV_SECTOR_SIZE; + + *pnum = nb_sectors; + *file = bs; + + if (s->read_zeroes) { + return BDRV_BLOCK_OFFSET_VALID | start | BDRV_BLOCK_ZERO; + } else { + return BDRV_BLOCK_OFFSET_VALID | start; + } +} + static BlockDriver bdrv_null_co = { .format_name = "null-co", .protocol_name = "null-co", @@ -196,6 +236,8 @@ static BlockDriver bdrv_null_co = { .bdrv_co_writev = null_co_writev, .bdrv_co_flush_to_disk = null_co_flush, .bdrv_reopen_prepare = null_reopen_prepare, + + .bdrv_co_get_block_status = null_co_get_block_status, }; static BlockDriver bdrv_null_aio = { @@ -211,6 +253,8 @@ static BlockDriver bdrv_null_aio = { .bdrv_aio_writev = null_aio_writev, .bdrv_aio_flush = null_aio_flush, .bdrv_reopen_prepare = null_reopen_prepare, + + .bdrv_co_get_block_status = null_co_get_block_status, }; static void bdrv_null_init(void) diff --git a/qemu/block/parallels.c b/qemu/block/parallels.c index 046b56844..324ed43ac 100644 --- a/qemu/block/parallels.c +++ b/qemu/block/parallels.c @@ -27,8 +27,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "block/block_int.h" +#include "sysemu/block-backend.h" #include "qemu/module.h" #include "qemu/bitmap.h" #include "qapi/util.h" @@ -61,7 +64,7 @@ typedef struct ParallelsHeader { typedef enum ParallelsPreallocMode { PRL_PREALLOC_MODE_FALLOCATE = 0, PRL_PREALLOC_MODE_TRUNCATE = 1, - PRL_PREALLOC_MODE_MAX = 2, + PRL_PREALLOC_MODE__MAX = 2, } ParallelsPreallocMode; static const char *prealloc_mode_lookup[] = { @@ -202,13 +205,13 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, to_allocate = (sector_num + *pnum + s->tracks - 1) / s->tracks - idx; space = to_allocate * s->tracks; - if (s->data_end + space > bdrv_getlength(bs->file) >> BDRV_SECTOR_BITS) { + if (s->data_end + space > bdrv_getlength(bs->file->bs) >> BDRV_SECTOR_BITS) { int ret; space += s->prealloc_size; if (s->prealloc_mode == PRL_PREALLOC_MODE_FALLOCATE) { - ret = bdrv_write_zeroes(bs->file, s->data_end, space, 0); + ret = bdrv_write_zeroes(bs->file->bs, s->data_end, space, 0); } else { - ret = bdrv_truncate(bs->file, + ret = bdrv_truncate(bs->file->bs, (s->data_end + space) << BDRV_SECTOR_BITS); } if (ret < 0) { @@ -220,7 +223,7 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, s->bat_bitmap[idx + i] = cpu_to_le32(s->data_end / s->off_multiplier); s->data_end += s->tracks; bitmap_set(s->bat_dirty_bmap, - bat_entry_off(idx) / s->bat_dirty_block, 1); + bat_entry_off(idx + i) / s->bat_dirty_block, 1); } return bat2sect(s, idx) + sector_num % s->tracks; @@ -244,7 +247,8 @@ static coroutine_fn int parallels_co_flush_to_os(BlockDriverState *bs) if (off + to_write > s->header_size) { to_write = s->header_size - off; } - ret = bdrv_pwrite(bs->file, off, (uint8_t *)s->header + off, to_write); + ret = bdrv_pwrite(bs->file->bs, off, (uint8_t *)s->header + off, + to_write); if (ret < 0) { qemu_co_mutex_unlock(&s->lock); return ret; @@ -259,7 +263,7 @@ static coroutine_fn int parallels_co_flush_to_os(BlockDriverState *bs) static int64_t coroutine_fn parallels_co_get_block_status(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, int *pnum) + int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) { BDRVParallelsState *s = bs->opaque; int64_t offset; @@ -272,6 +276,7 @@ static int64_t coroutine_fn parallels_co_get_block_status(BlockDriverState *bs, return 0; } + *file = bs->file->bs; return (offset << BDRV_SECTOR_BITS) | BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; } @@ -303,7 +308,7 @@ static coroutine_fn int parallels_co_writev(BlockDriverState *bs, qemu_iovec_reset(&hd_qiov); qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes); - ret = bdrv_co_writev(bs->file, position, n, &hd_qiov); + ret = bdrv_co_writev(bs->file->bs, position, n, &hd_qiov); if (ret < 0) { break; } @@ -343,7 +348,7 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs, qemu_iovec_reset(&hd_qiov); qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes); - ret = bdrv_co_readv(bs->file, position, n, &hd_qiov); + ret = bdrv_co_readv(bs->file->bs, position, n, &hd_qiov); if (ret < 0) { break; } @@ -369,7 +374,7 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res, bool flush_bat = false; int cluster_size = s->tracks << BDRV_SECTOR_BITS; - size = bdrv_getlength(bs->file); + size = bdrv_getlength(bs->file->bs); if (size < 0) { res->check_errors++; return size; @@ -424,7 +429,7 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res, } if (flush_bat) { - ret = bdrv_pwrite_sync(bs->file, 0, s->header, s->header_size); + ret = bdrv_pwrite_sync(bs->file->bs, 0, s->header, s->header_size); if (ret < 0) { res->check_errors++; return ret; @@ -440,7 +445,7 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res, size - res->image_end_offset); res->leaks += count; if (fix & BDRV_FIX_LEAKS) { - ret = bdrv_truncate(bs->file, res->image_end_offset); + ret = bdrv_truncate(bs->file->bs, res->image_end_offset); if (ret < 0) { res->check_errors++; return ret; @@ -458,7 +463,7 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp) int64_t total_size, cl_size; uint8_t tmp[BDRV_SECTOR_SIZE]; Error *local_err = NULL; - BlockDriverState *file; + BlockBackend *file; uint32_t bat_entries, bat_sectors; ParallelsHeader header; int ret; @@ -474,14 +479,16 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp) return ret; } - file = NULL; - ret = bdrv_open(&file, filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err); - if (ret < 0) { + file = blk_new_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); + if (file == NULL) { error_propagate(errp, local_err); - return ret; + return -EIO; } - ret = bdrv_truncate(file, 0); + + blk_set_allow_write_beyond_eof(file, true); + + ret = blk_truncate(file, 0); if (ret < 0) { goto exit; } @@ -505,18 +512,18 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp) memset(tmp, 0, sizeof(tmp)); memcpy(tmp, &header, sizeof(header)); - ret = bdrv_pwrite(file, 0, tmp, BDRV_SECTOR_SIZE); + ret = blk_pwrite(file, 0, tmp, BDRV_SECTOR_SIZE); if (ret < 0) { goto exit; } - ret = bdrv_write_zeroes(file, 1, bat_sectors - 1, 0); + ret = blk_write_zeroes(file, 1, bat_sectors - 1, 0); if (ret < 0) { goto exit; } ret = 0; done: - bdrv_unref(file); + blk_unref(file); return ret; exit: @@ -546,12 +553,13 @@ static int parallels_probe(const uint8_t *buf, int buf_size, static int parallels_update_header(BlockDriverState *bs) { BDRVParallelsState *s = bs->opaque; - unsigned size = MAX(bdrv_opt_mem_align(bs->file), sizeof(ParallelsHeader)); + unsigned size = MAX(bdrv_opt_mem_align(bs->file->bs), + sizeof(ParallelsHeader)); if (size > s->header_size) { size = s->header_size; } - return bdrv_pwrite_sync(bs->file, 0, s->header, size); + return bdrv_pwrite_sync(bs->file->bs, 0, s->header, size); } static int parallels_open(BlockDriverState *bs, QDict *options, int flags, @@ -564,7 +572,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, Error *local_err = NULL; char *buf; - ret = bdrv_pread(bs->file, 0, &ph, sizeof(ph)); + ret = bdrv_pread(bs->file->bs, 0, &ph, sizeof(ph)); if (ret < 0) { goto fail; } @@ -603,8 +611,8 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, } size = bat_entry_off(s->bat_size); - s->header_size = ROUND_UP(size, bdrv_opt_mem_align(bs->file)); - s->header = qemu_try_blockalign(bs->file, s->header_size); + s->header_size = ROUND_UP(size, bdrv_opt_mem_align(bs->file->bs)); + s->header = qemu_try_blockalign(bs->file->bs, s->header_size); if (s->header == NULL) { ret = -ENOMEM; goto fail; @@ -619,7 +627,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, s->header_size = size; } - ret = bdrv_pread(bs->file, 0, s->header, s->header_size); + ret = bdrv_pread(bs->file->bs, 0, s->header, s->header_size); if (ret < 0) { goto fail; } @@ -658,13 +666,13 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, s->prealloc_size = MAX(s->tracks, s->prealloc_size >> BDRV_SECTOR_BITS); buf = qemu_opt_get_del(opts, PARALLELS_OPT_PREALLOC_MODE); s->prealloc_mode = qapi_enum_parse(prealloc_mode_lookup, buf, - PRL_PREALLOC_MODE_MAX, PRL_PREALLOC_MODE_FALLOCATE, &local_err); + PRL_PREALLOC_MODE__MAX, PRL_PREALLOC_MODE_FALLOCATE, &local_err); g_free(buf); if (local_err != NULL) { goto fail_options; } - if (!bdrv_has_zero_init(bs->file) || - bdrv_truncate(bs->file, bdrv_getlength(bs->file)) != 0) { + if (!bdrv_has_zero_init(bs->file->bs) || + bdrv_truncate(bs->file->bs, bdrv_getlength(bs->file->bs)) != 0) { s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE; } @@ -707,7 +715,7 @@ static void parallels_close(BlockDriverState *bs) } if (bs->open_flags & BDRV_O_RDWR) { - bdrv_truncate(bs->file, s->data_end << BDRV_SECTOR_BITS); + bdrv_truncate(bs->file->bs, s->data_end << BDRV_SECTOR_BITS); } g_free(s->bat_dirty_bmap); diff --git a/qemu/block/qapi.c b/qemu/block/qapi.c index 2ce509711..c5f6ba643 100644 --- a/qemu/block/qapi.c +++ b/qemu/block/qapi.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "block/qapi.h" #include "block/block_int.h" #include "block/throttle-groups.h" @@ -31,8 +32,10 @@ #include "qapi/qmp-output-visitor.h" #include "qapi/qmp/types.h" #include "sysemu/block-backend.h" +#include "qemu/cutils.h" -BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp) +BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, + BlockDriverState *bs, Error **errp) { ImageInfo **p_image_info; BlockDriverState *bs0; @@ -46,7 +49,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp) info->cache = g_new(BlockdevCacheInfo, 1); *info->cache = (BlockdevCacheInfo) { - .writeback = bdrv_enable_write_cache(bs), + .writeback = blk ? blk_enable_write_cache(blk) : true, .direct = !!(bs->open_flags & BDRV_O_NOCACHE), .no_flush = !!(bs->open_flags & BDRV_O_NO_FLUSH), }; @@ -64,7 +67,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp) info->backing_file_depth = bdrv_get_backing_file_depth(bs); info->detect_zeroes = bs->detect_zeroes; - if (bs->io_limits_enabled) { + if (bs->throttle_state) { ThrottleConfig cfg; throttle_group_get_config(bs, &cfg); @@ -91,6 +94,26 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp) info->has_iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max; info->iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max; + info->has_bps_max_length = info->has_bps_max; + info->bps_max_length = + cfg.buckets[THROTTLE_BPS_TOTAL].burst_length; + info->has_bps_rd_max_length = info->has_bps_rd_max; + info->bps_rd_max_length = + cfg.buckets[THROTTLE_BPS_READ].burst_length; + info->has_bps_wr_max_length = info->has_bps_wr_max; + info->bps_wr_max_length = + cfg.buckets[THROTTLE_BPS_WRITE].burst_length; + + info->has_iops_max_length = info->has_iops_max; + info->iops_max_length = + cfg.buckets[THROTTLE_OPS_TOTAL].burst_length; + info->has_iops_rd_max_length = info->has_iops_rd_max; + info->iops_rd_max_length = + cfg.buckets[THROTTLE_OPS_READ].burst_length; + info->has_iops_wr_max_length = info->has_iops_wr_max; + info->iops_wr_max_length = + cfg.buckets[THROTTLE_OPS_WRITE].burst_length; + info->has_iops_size = cfg.op_size; info->iops_size = cfg.op_size; @@ -110,8 +133,8 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp) qapi_free_BlockDeviceInfo(info); return NULL; } - if (bs0->drv && bs0->backing_hd) { - bs0 = bs0->backing_hd; + if (bs0->drv && bs0->backing) { + bs0 = bs0->backing->bs; (*p_image_info)->has_backing_image = true; p_image_info = &((*p_image_info)->backing_image); } else { @@ -210,11 +233,13 @@ void bdrv_query_image_info(BlockDriverState *bs, Error *err = NULL; ImageInfo *info; + aio_context_acquire(bdrv_get_aio_context(bs)); + size = bdrv_getlength(bs); if (size < 0) { error_setg_errno(errp, -size, "Can't get size of device '%s'", bdrv_get_device_name(bs)); - return; + goto out; } info = g_new0(ImageInfo, 1); @@ -245,15 +270,18 @@ void bdrv_query_image_info(BlockDriverState *bs, info->has_backing_filename = true; bdrv_get_full_backing_filename(bs, backing_filename2, PATH_MAX, &err); if (err) { - error_propagate(errp, err); - qapi_free_ImageInfo(info); + /* Can't reconstruct the full backing filename, so we must omit + * this field and apply a Best Effort to this query. */ g_free(backing_filename2); - return; + backing_filename2 = NULL; + error_free(err); + err = NULL; } - if (strcmp(backing_filename, backing_filename2) != 0) { - info->full_backing_filename = - g_strdup(backing_filename2); + /* Always report the full_backing_filename if present, even if it's the + * same as backing_filename. That they are same is useful info. */ + if (backing_filename2) { + info->full_backing_filename = g_strdup(backing_filename2); info->has_full_backing_filename = true; } @@ -279,10 +307,13 @@ void bdrv_query_image_info(BlockDriverState *bs, default: error_propagate(errp, err); qapi_free_ImageInfo(info); - return; + goto out; } *p_info = info; + +out: + aio_context_release(bdrv_get_aio_context(bs)); } /* @p_info will be set only on success. */ @@ -296,24 +327,24 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, info->locked = blk_dev_is_medium_locked(blk); info->removable = blk_dev_has_removable_media(blk); - if (blk_dev_has_removable_media(blk)) { + if (blk_dev_has_tray(blk)) { info->has_tray_open = true; info->tray_open = blk_dev_is_tray_open(blk); } - if (bdrv_iostatus_is_enabled(bs)) { + if (blk_iostatus_is_enabled(blk)) { info->has_io_status = true; - info->io_status = bs->iostatus; + info->io_status = blk_iostatus(blk); } - if (!QLIST_EMPTY(&bs->dirty_bitmaps)) { + if (bs && !QLIST_EMPTY(&bs->dirty_bitmaps)) { info->has_dirty_bitmaps = true; info->dirty_bitmaps = bdrv_query_dirty_bitmaps(bs); } - if (bs->drv) { + if (bs && bs->drv) { info->has_inserted = true; - info->inserted = bdrv_block_device_info(bs, errp); + info->inserted = bdrv_block_device_info(blk, bs, errp); if (info->inserted == NULL) { goto err; } @@ -326,45 +357,115 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, qapi_free_BlockInfo(info); } -static BlockStats *bdrv_query_stats(const BlockDriverState *bs, - bool query_backing) +static BlockStats *bdrv_query_stats(BlockBackend *blk, + const BlockDriverState *bs, + bool query_backing); + +static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) { - BlockStats *s; + BlockAcctStats *stats = blk_get_stats(blk); + BlockAcctTimedStats *ts = NULL; - s = g_malloc0(sizeof(*s)); + ds->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ]; + ds->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE]; + ds->rd_operations = stats->nr_ops[BLOCK_ACCT_READ]; + ds->wr_operations = stats->nr_ops[BLOCK_ACCT_WRITE]; - if (bdrv_get_device_name(bs)[0]) { - s->has_device = true; - s->device = g_strdup(bdrv_get_device_name(bs)); + ds->failed_rd_operations = stats->failed_ops[BLOCK_ACCT_READ]; + ds->failed_wr_operations = stats->failed_ops[BLOCK_ACCT_WRITE]; + ds->failed_flush_operations = stats->failed_ops[BLOCK_ACCT_FLUSH]; + + ds->invalid_rd_operations = stats->invalid_ops[BLOCK_ACCT_READ]; + ds->invalid_wr_operations = stats->invalid_ops[BLOCK_ACCT_WRITE]; + ds->invalid_flush_operations = + stats->invalid_ops[BLOCK_ACCT_FLUSH]; + + ds->rd_merged = stats->merged[BLOCK_ACCT_READ]; + ds->wr_merged = stats->merged[BLOCK_ACCT_WRITE]; + ds->flush_operations = stats->nr_ops[BLOCK_ACCT_FLUSH]; + ds->wr_total_time_ns = stats->total_time_ns[BLOCK_ACCT_WRITE]; + ds->rd_total_time_ns = stats->total_time_ns[BLOCK_ACCT_READ]; + ds->flush_total_time_ns = stats->total_time_ns[BLOCK_ACCT_FLUSH]; + + ds->has_idle_time_ns = stats->last_access_time_ns > 0; + if (ds->has_idle_time_ns) { + ds->idle_time_ns = block_acct_idle_time_ns(stats); } + ds->account_invalid = stats->account_invalid; + ds->account_failed = stats->account_failed; + + while ((ts = block_acct_interval_next(stats, ts))) { + BlockDeviceTimedStatsList *timed_stats = + g_malloc0(sizeof(*timed_stats)); + BlockDeviceTimedStats *dev_stats = g_malloc0(sizeof(*dev_stats)); + timed_stats->next = ds->timed_stats; + timed_stats->value = dev_stats; + ds->timed_stats = timed_stats; + + TimedAverage *rd = &ts->latency[BLOCK_ACCT_READ]; + TimedAverage *wr = &ts->latency[BLOCK_ACCT_WRITE]; + TimedAverage *fl = &ts->latency[BLOCK_ACCT_FLUSH]; + + dev_stats->interval_length = ts->interval_length; + + dev_stats->min_rd_latency_ns = timed_average_min(rd); + dev_stats->max_rd_latency_ns = timed_average_max(rd); + dev_stats->avg_rd_latency_ns = timed_average_avg(rd); + + dev_stats->min_wr_latency_ns = timed_average_min(wr); + dev_stats->max_wr_latency_ns = timed_average_max(wr); + dev_stats->avg_wr_latency_ns = timed_average_avg(wr); + + dev_stats->min_flush_latency_ns = timed_average_min(fl); + dev_stats->max_flush_latency_ns = timed_average_max(fl); + dev_stats->avg_flush_latency_ns = timed_average_avg(fl); + + dev_stats->avg_rd_queue_depth = + block_acct_queue_depth(ts, BLOCK_ACCT_READ); + dev_stats->avg_wr_queue_depth = + block_acct_queue_depth(ts, BLOCK_ACCT_WRITE); + } +} + +static void bdrv_query_bds_stats(BlockStats *s, const BlockDriverState *bs, + bool query_backing) +{ if (bdrv_get_node_name(bs)[0]) { s->has_node_name = true; s->node_name = g_strdup(bdrv_get_node_name(bs)); } - s->stats = g_malloc0(sizeof(*s->stats)); - s->stats->rd_bytes = bs->stats.nr_bytes[BLOCK_ACCT_READ]; - s->stats->wr_bytes = bs->stats.nr_bytes[BLOCK_ACCT_WRITE]; - s->stats->rd_operations = bs->stats.nr_ops[BLOCK_ACCT_READ]; - s->stats->wr_operations = bs->stats.nr_ops[BLOCK_ACCT_WRITE]; - s->stats->rd_merged = bs->stats.merged[BLOCK_ACCT_READ]; - s->stats->wr_merged = bs->stats.merged[BLOCK_ACCT_WRITE]; - s->stats->wr_highest_offset = - bs->stats.wr_highest_sector * BDRV_SECTOR_SIZE; - s->stats->flush_operations = bs->stats.nr_ops[BLOCK_ACCT_FLUSH]; - s->stats->wr_total_time_ns = bs->stats.total_time_ns[BLOCK_ACCT_WRITE]; - s->stats->rd_total_time_ns = bs->stats.total_time_ns[BLOCK_ACCT_READ]; - s->stats->flush_total_time_ns = bs->stats.total_time_ns[BLOCK_ACCT_FLUSH]; + s->stats->wr_highest_offset = bs->wr_highest_offset; if (bs->file) { s->has_parent = true; - s->parent = bdrv_query_stats(bs->file, query_backing); + s->parent = bdrv_query_stats(NULL, bs->file->bs, query_backing); } - if (query_backing && bs->backing_hd) { + if (query_backing && bs->backing) { s->has_backing = true; - s->backing = bdrv_query_stats(bs->backing_hd, query_backing); + s->backing = bdrv_query_stats(NULL, bs->backing->bs, query_backing); + } + +} + +static BlockStats *bdrv_query_stats(BlockBackend *blk, + const BlockDriverState *bs, + bool query_backing) +{ + BlockStats *s; + + s = g_malloc0(sizeof(*s)); + s->stats = g_malloc0(sizeof(*s->stats)); + + if (blk) { + s->has_device = true; + s->device = g_strdup(blk_name(blk)); + bdrv_query_blk_stats(s->stats, blk); + } + if (bs) { + bdrv_query_bds_stats(s, bs, query_backing); } return s; @@ -381,7 +482,9 @@ BlockInfoList *qmp_query_block(Error **errp) bdrv_query_info(blk, &info->value, &local_err); if (local_err) { error_propagate(errp, local_err); - goto err; + g_free(info); + qapi_free_BlockInfoList(head); + return NULL; } *p_next = info; @@ -389,10 +492,20 @@ BlockInfoList *qmp_query_block(Error **errp) } return head; +} - err: - qapi_free_BlockInfoList(head); - return NULL; +static bool next_query_bds(BlockBackend **blk, BlockDriverState **bs, + bool query_nodes) +{ + if (query_nodes) { + *bs = bdrv_next_node(*bs); + return !!*bs; + } + + *blk = blk_next(*blk); + *bs = *blk ? blk_bs(*blk) : NULL; + + return !!*blk; } BlockStatsList *qmp_query_blockstats(bool has_query_nodes, @@ -400,17 +513,19 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, Error **errp) { BlockStatsList *head = NULL, **p_next = &head; + BlockBackend *blk = NULL; BlockDriverState *bs = NULL; /* Just to be safe if query_nodes is not always initialized */ query_nodes = has_query_nodes && query_nodes; - while ((bs = query_nodes ? bdrv_next_node(bs) : bdrv_next(bs))) { + while (next_query_bds(&blk, &bs, query_nodes)) { BlockStatsList *info = g_malloc0(sizeof(*info)); - AioContext *ctx = bdrv_get_aio_context(bs); + AioContext *ctx = blk ? blk_get_aio_context(blk) + : bdrv_get_aio_context(bs); aio_context_acquire(ctx); - info->value = bdrv_query_stats(bs, !query_nodes); + info->value = bdrv_query_stats(blk, bs, !query_nodes); aio_context_release(ctx); *p_next = info; @@ -535,11 +650,10 @@ static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation, int i = 0; for (entry = qlist_first(list); entry; entry = qlist_next(entry), i++) { - qtype_code type = qobject_type(entry->value); + QType type = qobject_type(entry->value); bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST); - const char *format = composite ? "%*s[%i]:\n" : "%*s[%i]: "; - - func_fprintf(f, format, indentation * 4, "", i); + func_fprintf(f, "%*s[%i]:%c", indentation * 4, "", i, + composite ? '\n' : ' '); dump_qobject(func_fprintf, f, indentation + 1, entry->value); if (!composite) { func_fprintf(f, "\n"); @@ -553,10 +667,9 @@ static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation, const QDictEntry *entry; for (entry = qdict_first(dict); entry; entry = qdict_next(dict, entry)) { - qtype_code type = qobject_type(entry->value); + QType type = qobject_type(entry->value); bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST); - const char *format = composite ? "%*s%s:\n" : "%*s%s: "; - char key[strlen(entry->key) + 1]; + char *key = g_malloc(strlen(entry->key) + 1); int i; /* replace dashes with spaces in key (variable) names */ @@ -564,12 +677,13 @@ static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation, key[i] = entry->key[i] == '-' ? ' ' : entry->key[i]; } key[i] = 0; - - func_fprintf(f, format, indentation * 4, "", key); + func_fprintf(f, "%*s%s:%c", indentation * 4, "", key, + composite ? '\n' : ' '); dump_qobject(func_fprintf, f, indentation + 1, entry->value); if (!composite) { func_fprintf(f, "\n"); } + g_free(key); } } @@ -579,7 +693,7 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f, QmpOutputVisitor *ov = qmp_output_visitor_new(); QObject *obj, *data; - visit_type_ImageInfoSpecific(qmp_output_get_visitor(ov), &info_spec, NULL, + visit_type_ImageInfoSpecific(qmp_output_get_visitor(ov), NULL, &info_spec, &error_abort); obj = qmp_output_get_qobject(ov); assert(qobject_type(obj) == QTYPE_QDICT); @@ -623,7 +737,10 @@ void bdrv_image_info_dump(fprintf_function func_fprintf, void *f, if (info->has_backing_filename) { func_fprintf(f, "backing file: %s", info->backing_filename); - if (info->has_full_backing_filename) { + if (!info->has_full_backing_filename) { + func_fprintf(f, " (cannot determine actual path)"); + } else if (strcmp(info->backing_filename, + info->full_backing_filename) != 0) { func_fprintf(f, " (actual path: %s)", info->full_backing_filename); } func_fprintf(f, "\n"); diff --git a/qemu/block/qcow.c b/qemu/block/qcow.c index 01fba54ce..60ddb12ec 100644 --- a/qemu/block/qcow.c +++ b/qemu/block/qcow.c @@ -21,8 +21,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" +#include "qemu/error-report.h" #include "block/block_int.h" +#include "sysemu/block-backend.h" #include "qemu/module.h" #include #include "qapi/qmp/qerror.h" @@ -100,7 +104,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, int ret; QCowHeader header; - ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); + ret = bdrv_pread(bs->file->bs, 0, &header, sizeof(header)); if (ret < 0) { goto fail; } @@ -119,11 +123,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } if (header.version != QCOW_VERSION) { - char version[64]; - snprintf(version, sizeof(version), "QCOW version %" PRIu32, - header.version); - error_setg(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, - bdrv_get_device_or_node_name(bs), "qcow", version); + error_setg(errp, "Unsupported qcow version %" PRIu32, header.version); ret = -ENOTSUP; goto fail; } @@ -159,6 +159,14 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, } s->crypt_method_header = header.crypt_method; if (s->crypt_method_header) { + if (bdrv_uses_whitelist() && + s->crypt_method_header == QCOW_CRYPT_AES) { + error_report("qcow built-in AES encryption is deprecated"); + error_printf("Support for it will be removed in a future release.\n" + "You can use 'qemu-img convert' to switch to an\n" + "unencrypted qcow image, or a LUKS raw image.\n"); + } + bs->encrypted = 1; } s->cluster_bits = header.cluster_bits; @@ -193,7 +201,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, + ret = bdrv_pread(bs->file->bs, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)); if (ret < 0) { goto fail; @@ -205,7 +213,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, /* alloc L2 cache (max. 64k * 16 * 8 = 8 MB) */ s->l2_cache = - qemu_try_blockalign(bs->file, + qemu_try_blockalign(bs->file->bs, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t)); if (s->l2_cache == NULL) { error_setg(errp, "Could not allocate L2 table cache"); @@ -224,7 +232,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, ret = -EINVAL; goto fail; } - ret = bdrv_pread(bs->file, header.backing_file_offset, + ret = bdrv_pread(bs->file->bs, header.backing_file_offset, bs->backing_file, len); if (ret < 0) { goto fail; @@ -369,13 +377,13 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, if (!allocate) return 0; /* allocate a new l2 entry */ - l2_offset = bdrv_getlength(bs->file); + l2_offset = bdrv_getlength(bs->file->bs); /* round to cluster size */ l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); /* update the L1 entry */ s->l1_table[l1_index] = l2_offset; tmp = cpu_to_be64(l2_offset); - if (bdrv_pwrite_sync(bs->file, + if (bdrv_pwrite_sync(bs->file->bs, s->l1_table_offset + l1_index * sizeof(tmp), &tmp, sizeof(tmp)) < 0) return 0; @@ -405,11 +413,12 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, l2_table = s->l2_cache + (min_index << s->l2_bits); if (new_l2_table) { memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); - if (bdrv_pwrite_sync(bs->file, l2_offset, l2_table, + if (bdrv_pwrite_sync(bs->file->bs, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) < 0) return 0; } else { - if (bdrv_pread(bs->file, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) != + if (bdrv_pread(bs->file->bs, l2_offset, l2_table, + s->l2_size * sizeof(uint64_t)) != s->l2_size * sizeof(uint64_t)) return 0; } @@ -430,20 +439,21 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, overwritten */ if (decompress_cluster(bs, cluster_offset) < 0) return 0; - cluster_offset = bdrv_getlength(bs->file); + cluster_offset = bdrv_getlength(bs->file->bs); cluster_offset = (cluster_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); /* write the cluster content */ - if (bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache, s->cluster_size) != + if (bdrv_pwrite(bs->file->bs, cluster_offset, s->cluster_cache, + s->cluster_size) != s->cluster_size) return -1; } else { - cluster_offset = bdrv_getlength(bs->file); + cluster_offset = bdrv_getlength(bs->file->bs); if (allocate == 1) { /* round to cluster size */ cluster_offset = (cluster_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); - bdrv_truncate(bs->file, cluster_offset + s->cluster_size); + bdrv_truncate(bs->file->bs, cluster_offset + s->cluster_size); /* if encrypted, we must initialize the cluster content which won't be written */ if (bs->encrypted && @@ -463,7 +473,8 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, errno = EIO; return -1; } - if (bdrv_pwrite(bs->file, cluster_offset + i * 512, + if (bdrv_pwrite(bs->file->bs, + cluster_offset + i * 512, s->cluster_data, 512) != 512) return -1; } @@ -477,7 +488,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, /* update L2 table */ tmp = cpu_to_be64(cluster_offset); l2_table[l2_index] = tmp; - if (bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp), + if (bdrv_pwrite_sync(bs->file->bs, l2_offset + l2_index * sizeof(tmp), &tmp, sizeof(tmp)) < 0) return 0; } @@ -485,7 +496,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, } static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, int *pnum) + int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) { BDRVQcowState *s = bs->opaque; int index_in_cluster, n; @@ -506,6 +517,7 @@ static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs, return BDRV_BLOCK_DATA; } cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); + *file = bs->file->bs; return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | cluster_offset; } @@ -546,7 +558,7 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) if (s->cluster_cache_offset != coffset) { csize = cluster_offset >> (63 - s->cluster_bits); csize &= (s->cluster_size - 1); - ret = bdrv_pread(bs->file, coffset, s->cluster_data, csize); + ret = bdrv_pread(bs->file->bs, coffset, s->cluster_data, csize); if (ret != csize) return -1; if (decompress_buffer(s->cluster_cache, s->cluster_size, @@ -594,13 +606,13 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, } if (!cluster_offset) { - if (bs->backing_hd) { + if (bs->backing) { /* read from the base image */ hd_iov.iov_base = (void *)buf; hd_iov.iov_len = n * 512; qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_readv(bs->backing_hd, sector_num, + ret = bdrv_co_readv(bs->backing->bs, sector_num, n, &hd_qiov); qemu_co_mutex_lock(&s->lock); if (ret < 0) { @@ -625,7 +637,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, hd_iov.iov_len = n * 512; qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_readv(bs->file, + ret = bdrv_co_readv(bs->file->bs, (cluster_offset >> 9) + index_in_cluster, n, &hd_qiov); qemu_co_mutex_lock(&s->lock); @@ -727,7 +739,7 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, hd_iov.iov_len = n * 512; qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_writev(bs->file, + ret = bdrv_co_writev(bs->file->bs, (cluster_offset >> 9) + index_in_cluster, n, &hd_qiov); qemu_co_mutex_lock(&s->lock); @@ -775,7 +787,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp) int flags = 0; Error *local_err = NULL; int ret; - BlockDriverState *qcow_bs; + BlockBackend *qcow_blk; /* Read out options */ total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), @@ -791,15 +803,17 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp) goto cleanup; } - qcow_bs = NULL; - ret = bdrv_open(&qcow_bs, filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err); - if (ret < 0) { + qcow_blk = blk_new_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); + if (qcow_blk == NULL) { error_propagate(errp, local_err); + ret = -EIO; goto cleanup; } - ret = bdrv_truncate(qcow_bs, 0); + blk_set_allow_write_beyond_eof(qcow_blk, true); + + ret = blk_truncate(qcow_blk, 0); if (ret < 0) { goto exit; } @@ -839,13 +853,13 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp) } /* write all the data */ - ret = bdrv_pwrite(qcow_bs, 0, &header, sizeof(header)); + ret = blk_pwrite(qcow_blk, 0, &header, sizeof(header)); if (ret != sizeof(header)) { goto exit; } if (backing_file) { - ret = bdrv_pwrite(qcow_bs, sizeof(header), + ret = blk_pwrite(qcow_blk, sizeof(header), backing_file, backing_filename_len); if (ret != backing_filename_len) { goto exit; @@ -855,7 +869,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp) tmp = g_malloc0(BDRV_SECTOR_SIZE); for (i = 0; i < ((sizeof(uint64_t)*l1_size + BDRV_SECTOR_SIZE - 1)/ BDRV_SECTOR_SIZE); i++) { - ret = bdrv_pwrite(qcow_bs, header_size + + ret = blk_pwrite(qcow_blk, header_size + BDRV_SECTOR_SIZE*i, tmp, BDRV_SECTOR_SIZE); if (ret != BDRV_SECTOR_SIZE) { g_free(tmp); @@ -866,7 +880,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp) g_free(tmp); ret = 0; exit: - bdrv_unref(qcow_bs); + blk_unref(qcow_blk); cleanup: g_free(backing_file); return ret; @@ -879,10 +893,10 @@ static int qcow_make_empty(BlockDriverState *bs) int ret; memset(s->l1_table, 0, l1_length); - if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table, + if (bdrv_pwrite_sync(bs->file->bs, s->l1_table_offset, s->l1_table, l1_length) < 0) return -1; - ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length); + ret = bdrv_truncate(bs->file->bs, s->l1_table_offset + l1_length); if (ret < 0) return ret; @@ -962,7 +976,7 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num, } cluster_offset &= s->cluster_offset_mask; - ret = bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len); + ret = bdrv_pwrite(bs->file->bs, cluster_offset, out_buf, out_len); if (ret < 0) { goto fail; } diff --git a/qemu/block/qcow2-cache.c b/qemu/block/qcow2-cache.c index 53b8afc3d..0fe8edae4 100644 --- a/qemu/block/qcow2-cache.c +++ b/qemu/block/qcow2-cache.c @@ -22,6 +22,13 @@ * THE SOFTWARE. */ +/* Needed for CONFIG_MADVISE */ +#include "qemu/osdep.h" + +#if defined(CONFIG_MADVISE) || defined(CONFIG_POSIX_MADVISE) +#include +#endif + #include "block/block_int.h" #include "qemu-common.h" #include "qcow2.h" @@ -29,9 +36,9 @@ typedef struct Qcow2CachedTable { int64_t offset; - bool dirty; uint64_t lru_counter; int ref; + bool dirty; } Qcow2CachedTable; struct Qcow2Cache { @@ -41,34 +48,85 @@ struct Qcow2Cache { bool depends_on_flush; void *table_array; uint64_t lru_counter; + uint64_t cache_clean_lru_counter; }; static inline void *qcow2_cache_get_table_addr(BlockDriverState *bs, Qcow2Cache *c, int table) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; return (uint8_t *) c->table_array + (size_t) table * s->cluster_size; } static inline int qcow2_cache_get_table_idx(BlockDriverState *bs, Qcow2Cache *c, void *table) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; ptrdiff_t table_offset = (uint8_t *) table - (uint8_t *) c->table_array; int idx = table_offset / s->cluster_size; assert(idx >= 0 && idx < c->size && table_offset % s->cluster_size == 0); return idx; } +static void qcow2_cache_table_release(BlockDriverState *bs, Qcow2Cache *c, + int i, int num_tables) +{ +#if QEMU_MADV_DONTNEED != QEMU_MADV_INVALID + BDRVQcow2State *s = bs->opaque; + void *t = qcow2_cache_get_table_addr(bs, c, i); + int align = getpagesize(); + size_t mem_size = (size_t) s->cluster_size * num_tables; + size_t offset = QEMU_ALIGN_UP((uintptr_t) t, align) - (uintptr_t) t; + size_t length = QEMU_ALIGN_DOWN(mem_size - offset, align); + if (length > 0) { + qemu_madvise((uint8_t *) t + offset, length, QEMU_MADV_DONTNEED); + } +#endif +} + +static inline bool can_clean_entry(Qcow2Cache *c, int i) +{ + Qcow2CachedTable *t = &c->entries[i]; + return t->ref == 0 && !t->dirty && t->offset != 0 && + t->lru_counter <= c->cache_clean_lru_counter; +} + +void qcow2_cache_clean_unused(BlockDriverState *bs, Qcow2Cache *c) +{ + int i = 0; + while (i < c->size) { + int to_clean = 0; + + /* Skip the entries that we don't need to clean */ + while (i < c->size && !can_clean_entry(c, i)) { + i++; + } + + /* And count how many we can clean in a row */ + while (i < c->size && can_clean_entry(c, i)) { + c->entries[i].offset = 0; + c->entries[i].lru_counter = 0; + i++; + to_clean++; + } + + if (to_clean > 0) { + qcow2_cache_table_release(bs, c, i - to_clean, to_clean); + } + } + + c->cache_clean_lru_counter = c->lru_counter; +} + Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; Qcow2Cache *c; c = g_new0(Qcow2Cache, 1); c->size = num_tables; c->entries = g_try_new0(Qcow2CachedTable, num_tables); - c->table_array = qemu_try_blockalign(bs->file, + c->table_array = qemu_try_blockalign(bs->file->bs, (size_t) num_tables * s->cluster_size); if (!c->entries || !c->table_array) { @@ -113,7 +171,7 @@ static int qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c) static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int ret = 0; if (!c->entries[i].dirty || !c->entries[i].offset) { @@ -126,7 +184,7 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) if (c->depends) { ret = qcow2_cache_flush_dependency(bs, c); } else if (c->depends_on_flush) { - ret = bdrv_flush(bs->file); + ret = bdrv_flush(bs->file->bs); if (ret >= 0) { c->depends_on_flush = false; } @@ -157,7 +215,7 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); } - ret = bdrv_pwrite(bs->file, c->entries[i].offset, + ret = bdrv_pwrite(bs->file->bs, c->entries[i].offset, qcow2_cache_get_table_addr(bs, c, i), s->cluster_size); if (ret < 0) { return ret; @@ -170,7 +228,7 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int result = 0; int ret; int i; @@ -185,7 +243,7 @@ int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c) } if (result == 0) { - ret = bdrv_flush(bs->file); + ret = bdrv_flush(bs->file->bs); if (ret < 0) { result = ret; } @@ -237,6 +295,8 @@ int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c) c->entries[i].lru_counter = 0; } + qcow2_cache_table_release(bs, c, 0, c->size); + c->lru_counter = 0; return 0; @@ -245,7 +305,7 @@ int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c) static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, void **table, bool read_from_disk) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int i; int ret; int lookup_index; @@ -295,7 +355,8 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); } - ret = bdrv_pread(bs->file, offset, qcow2_cache_get_table_addr(bs, c, i), + ret = bdrv_pread(bs->file->bs, offset, + qcow2_cache_get_table_addr(bs, c, i), s->cluster_size); if (ret < 0) { return ret; diff --git a/qemu/block/qcow2-cluster.c b/qemu/block/qcow2-cluster.c index b43f186eb..31ecc1030 100644 --- a/qemu/block/qcow2-cluster.c +++ b/qemu/block/qcow2-cluster.c @@ -22,8 +22,10 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include +#include "qapi/error.h" #include "qemu-common.h" #include "block/block_int.h" #include "block/qcow2.h" @@ -32,7 +34,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, bool exact_size) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int new_l1_size2, ret, i; uint64_t *new_l1_table; int64_t old_l1_table_offset, old_l1_size; @@ -72,7 +74,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, #endif new_l1_size2 = sizeof(uint64_t) * new_l1_size; - new_l1_table = qemu_try_blockalign(bs->file, + new_l1_table = qemu_try_blockalign(bs->file->bs, align_offset(new_l1_size2, 512)); if (new_l1_table == NULL) { return -ENOMEM; @@ -105,7 +107,8 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE); for(i = 0; i < s->l1_size; i++) new_l1_table[i] = cpu_to_be64(new_l1_table[i]); - ret = bdrv_pwrite_sync(bs->file, new_l1_table_offset, new_l1_table, new_l1_size2); + ret = bdrv_pwrite_sync(bs->file->bs, new_l1_table_offset, + new_l1_table, new_l1_size2); if (ret < 0) goto fail; for(i = 0; i < s->l1_size; i++) @@ -115,7 +118,8 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ACTIVATE_TABLE); cpu_to_be32w((uint32_t*)data, new_l1_size); stq_be_p(data + 4, new_l1_table_offset); - ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_size), data,sizeof(data)); + ret = bdrv_pwrite_sync(bs->file->bs, offsetof(QCowHeader, l1_size), + data, sizeof(data)); if (ret < 0) { goto fail; } @@ -148,7 +152,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, static int l2_load(BlockDriverState *bs, uint64_t l2_offset, uint64_t **l2_table) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int ret; ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, (void**) l2_table); @@ -163,7 +167,7 @@ static int l2_load(BlockDriverState *bs, uint64_t l2_offset, #define L1_ENTRIES_PER_SECTOR (512 / 8) int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t buf[L1_ENTRIES_PER_SECTOR] = { 0 }; int l1_start_index; int i, ret; @@ -182,8 +186,9 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) } BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); - ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset + 8 * l1_start_index, - buf, sizeof(buf)); + ret = bdrv_pwrite_sync(bs->file->bs, + s->l1_table_offset + 8 * l1_start_index, + buf, sizeof(buf)); if (ret < 0) { return ret; } @@ -203,7 +208,7 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t old_l2_offset; uint64_t *l2_table = NULL; int64_t l2_offset; @@ -298,7 +303,7 @@ fail: * as contiguous. (This allows it, for example, to stop at the first compressed * cluster which may require a different handling) */ -static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size, +static int count_contiguous_clusters(int nb_clusters, int cluster_size, uint64_t *l2_table, uint64_t stop_flags) { int i; @@ -309,7 +314,7 @@ static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size, if (!offset) return 0; - assert(qcow2_get_cluster_type(first_entry) != QCOW2_CLUSTER_COMPRESSED); + assert(qcow2_get_cluster_type(first_entry) == QCOW2_CLUSTER_NORMAL); for (i = 0; i < nb_clusters; i++) { uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask; @@ -321,14 +326,16 @@ static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size, return i; } -static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_table) +static int count_contiguous_clusters_by_type(int nb_clusters, + uint64_t *l2_table, + int wanted_type) { int i; for (i = 0; i < nb_clusters; i++) { int type = qcow2_get_cluster_type(be64_to_cpu(l2_table[i])); - if (type != QCOW2_CLUSTER_UNALLOCATED) { + if (type != wanted_type) { break; } } @@ -339,7 +346,7 @@ static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_tab /* The crypt function is compatible with the linux cryptoloop algorithm for < 4 GB images. NOTE: out_buf == in_buf is supported */ -int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, +int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num, uint8_t *out_buf, const uint8_t *in_buf, int nb_sectors, bool enc, Error **errp) @@ -387,7 +394,7 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs, uint64_t cluster_offset, int n_start, int n_end) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QEMUIOVector qiov; struct iovec iov; int n, ret; @@ -440,7 +447,8 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs, } BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE); - ret = bdrv_co_writev(bs->file, (cluster_offset >> 9) + n_start, n, &qiov); + ret = bdrv_co_writev(bs->file->bs, (cluster_offset >> 9) + n_start, n, + &qiov); if (ret < 0) { goto out; } @@ -469,7 +477,7 @@ out: int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, int *num, uint64_t *cluster_offset) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; unsigned int l2_index; uint64_t l1_index, l2_offset, *l2_table; int l1_bits, c; @@ -495,10 +503,11 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, if (nb_needed > nb_available) { nb_needed = nb_available; } + assert(nb_needed <= INT_MAX); *cluster_offset = 0; - /* seek the the l2 offset in the l1 table */ + /* seek to the l2 offset in the l1 table */ l1_index = offset >> l1_bits; if (l1_index >= s->l1_size) { @@ -530,6 +539,8 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1); *cluster_offset = be64_to_cpu(l2_table[l2_index]); + + /* nb_needed <= INT_MAX, thus nb_clusters <= INT_MAX, too */ nb_clusters = size_to_clusters(s, nb_needed << 9); ret = qcow2_get_cluster_type(*cluster_offset); @@ -547,13 +558,14 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, ret = -EIO; goto fail; } - c = count_contiguous_clusters(nb_clusters, s->cluster_size, - &l2_table[l2_index], QCOW_OFLAG_ZERO); + c = count_contiguous_clusters_by_type(nb_clusters, &l2_table[l2_index], + QCOW2_CLUSTER_ZERO); *cluster_offset = 0; break; case QCOW2_CLUSTER_UNALLOCATED: /* how many empty clusters ? */ - c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]); + c = count_contiguous_clusters_by_type(nb_clusters, &l2_table[l2_index], + QCOW2_CLUSTER_UNALLOCATED); *cluster_offset = 0; break; case QCOW2_CLUSTER_NORMAL: @@ -606,13 +618,13 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, uint64_t **new_l2_table, int *new_l2_index) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; unsigned int l2_index; uint64_t l1_index, l2_offset; uint64_t *l2_table = NULL; int ret; - /* seek the the l2 offset in the l1 table */ + /* seek to the l2 offset in the l1 table */ l1_index = offset >> (s->l2_bits + s->cluster_bits); if (l1_index >= s->l1_size) { @@ -680,7 +692,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, uint64_t offset, int compressed_size) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int l2_index, ret; uint64_t *l2_table; int64_t cluster_offset; @@ -725,7 +737,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, static int perform_cow(BlockDriverState *bs, QCowL2Meta *m, Qcow2COWRegion *r) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int ret; if (r->nb_sectors == 0) { @@ -754,7 +766,7 @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m, Qcow2COWRegion *r) int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int i, j = 0, l2_index, ret; uint64_t *old_cluster, *l2_table; uint64_t cluster_offset = m->alloc_offset; @@ -814,7 +826,6 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) /* * If this was a COW, we need to decrease the refcount of the old cluster. - * Also flush bs->file to get the right order for L2 and refcount update. * * Don't discard clusters that reach a refcount of 0 (e.g. compressed * clusters), the next write will reuse them anyway. @@ -837,7 +848,7 @@ err: * write, but require COW to be performed (this includes yet unallocated space, * which must copy from the backing file) */ -static int count_cow_clusters(BDRVQcowState *s, int nb_clusters, +static int count_cow_clusters(BDRVQcow2State *s, int nb_clusters, uint64_t *l2_table, int l2_index) { int i; @@ -883,7 +894,7 @@ out: static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset, uint64_t *cur_bytes, QCowL2Meta **m) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowL2Meta *old_alloc; uint64_t bytes = *cur_bytes; @@ -956,11 +967,11 @@ static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset, static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int l2_index; uint64_t cluster_offset; uint64_t *l2_table; - unsigned int nb_clusters; + uint64_t nb_clusters; unsigned int keep_clusters; int ret; @@ -979,6 +990,7 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, l2_index = offset_to_l2_index(s, guest_offset); nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + assert(nb_clusters <= INT_MAX); /* Find L2 entry for the first involved cluster */ ret = get_cluster_table(bs, guest_offset, &l2_table, &l2_index); @@ -1061,9 +1073,9 @@ out: * restarted, but the whole request should not be failed. */ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, - uint64_t *host_offset, unsigned int *nb_clusters) + uint64_t *host_offset, uint64_t *nb_clusters) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset, *host_offset, *nb_clusters); @@ -1079,7 +1091,7 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, *host_offset = cluster_offset; return 0; } else { - int ret = qcow2_alloc_clusters_at(bs, *host_offset, *nb_clusters); + int64_t ret = qcow2_alloc_clusters_at(bs, *host_offset, *nb_clusters); if (ret < 0) { return ret; } @@ -1111,11 +1123,11 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int l2_index; uint64_t *l2_table; uint64_t entry; - unsigned int nb_clusters; + uint64_t nb_clusters; int ret; uint64_t alloc_cluster_offset; @@ -1133,6 +1145,7 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, l2_index = offset_to_l2_index(s, guest_offset); nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + assert(nb_clusters <= INT_MAX); /* Find L2 entry for the first involved cluster */ ret = get_cluster_table(bs, guest_offset, &l2_table, &l2_index); @@ -1263,7 +1276,7 @@ fail: int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, int *num, uint64_t *host_offset, QCowL2Meta **m) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t start, remaining; uint64_t cluster_offset; uint64_t cur_bytes; @@ -1397,7 +1410,7 @@ static int decompress_buffer(uint8_t *out_buf, int out_buf_size, int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int ret, csize, nb_csectors, sector_offset; uint64_t coffset; @@ -1407,7 +1420,8 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) sector_offset = coffset & 511; csize = nb_csectors * 512 - sector_offset; BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED); - ret = bdrv_read(bs->file, coffset >> 9, s->cluster_data, nb_csectors); + ret = bdrv_read(bs->file->bs, coffset >> 9, s->cluster_data, + nb_csectors); if (ret < 0) { return ret; } @@ -1426,9 +1440,10 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) * clusters. */ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, - unsigned int nb_clusters, enum qcow2_discard_type type, bool full_discard) + uint64_t nb_clusters, enum qcow2_discard_type type, + bool full_discard) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t *l2_table; int l2_index; int ret; @@ -1441,6 +1456,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, /* Limit nb_clusters to one L2 table */ nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + assert(nb_clusters <= INT_MAX); for (i = 0; i < nb_clusters; i++) { uint64_t old_l2_entry; @@ -1462,7 +1478,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, */ switch (qcow2_get_cluster_type(old_l2_entry)) { case QCOW2_CLUSTER_UNALLOCATED: - if (full_discard || !bs->backing_hd) { + if (full_discard || !bs->backing) { continue; } break; @@ -1501,9 +1517,9 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors, enum qcow2_discard_type type, bool full_discard) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t end_offset; - unsigned int nb_clusters; + uint64_t nb_clusters; int ret; end_offset = offset + (nb_sectors << BDRV_SECTOR_BITS); @@ -1545,9 +1561,9 @@ fail: * clusters. */ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, - unsigned int nb_clusters) + uint64_t nb_clusters) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t *l2_table; int l2_index; int ret; @@ -1560,6 +1576,7 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, /* Limit nb_clusters to one L2 table */ nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + assert(nb_clusters <= INT_MAX); for (i = 0; i < nb_clusters; i++) { uint64_t old_offset; @@ -1583,8 +1600,8 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors) { - BDRVQcowState *s = bs->opaque; - unsigned int nb_clusters; + BDRVQcow2State *s = bs->opaque; + uint64_t nb_clusters; int ret; /* The zero flag is only supported by version 3 and newer */ @@ -1626,9 +1643,10 @@ fail: static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, int l1_size, int64_t *visited_l1_entries, int64_t l1_entries, - BlockDriverAmendStatusCB *status_cb) + BlockDriverAmendStatusCB *status_cb, + void *cb_opaque) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; bool is_active_l1 = (l1_table == s->l1_table); uint64_t *l2_table = NULL; int ret; @@ -1637,7 +1655,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, if (!is_active_l1) { /* inactive L2 tables require a buffer to be stored in when loading * them from disk */ - l2_table = qemu_try_blockalign(bs->file, s->cluster_size); + l2_table = qemu_try_blockalign(bs->file->bs, s->cluster_size); if (l2_table == NULL) { return -ENOMEM; } @@ -1652,7 +1670,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, /* unallocated */ (*visited_l1_entries)++; if (status_cb) { - status_cb(bs, *visited_l1_entries, l1_entries); + status_cb(bs, *visited_l1_entries, l1_entries, cb_opaque); } continue; } @@ -1671,8 +1689,8 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, (void **)&l2_table); } else { /* load inactive L2 tables from disk */ - ret = bdrv_read(bs->file, l2_offset / BDRV_SECTOR_SIZE, - (void *)l2_table, s->cluster_sectors); + ret = bdrv_read(bs->file->bs, l2_offset / BDRV_SECTOR_SIZE, + (void *)l2_table, s->cluster_sectors); } if (ret < 0) { goto fail; @@ -1695,7 +1713,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, } if (!preallocated) { - if (!bs->backing_hd) { + if (!bs->backing) { /* not backed; therefore we can simply deallocate the * cluster */ l2_table[j] = 0; @@ -1746,7 +1764,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, goto fail; } - ret = bdrv_write_zeroes(bs->file, offset / BDRV_SECTOR_SIZE, + ret = bdrv_write_zeroes(bs->file->bs, offset / BDRV_SECTOR_SIZE, s->cluster_sectors, 0); if (ret < 0) { if (!preallocated) { @@ -1779,8 +1797,8 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, goto fail; } - ret = bdrv_write(bs->file, l2_offset / BDRV_SECTOR_SIZE, - (void *)l2_table, s->cluster_sectors); + ret = bdrv_write(bs->file->bs, l2_offset / BDRV_SECTOR_SIZE, + (void *)l2_table, s->cluster_sectors); if (ret < 0) { goto fail; } @@ -1789,7 +1807,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, (*visited_l1_entries)++; if (status_cb) { - status_cb(bs, *visited_l1_entries, l1_entries); + status_cb(bs, *visited_l1_entries, l1_entries, cb_opaque); } } @@ -1813,9 +1831,10 @@ fail: * qcow2 version which doesn't yet support metadata zero clusters. */ int qcow2_expand_zero_clusters(BlockDriverState *bs, - BlockDriverAmendStatusCB *status_cb) + BlockDriverAmendStatusCB *status_cb, + void *cb_opaque) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t *l1_table = NULL; int64_t l1_entries = 0, visited_l1_entries = 0; int ret; @@ -1830,7 +1849,7 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs, ret = expand_zero_clusters_in_l1(bs, s->l1_table, s->l1_size, &visited_l1_entries, l1_entries, - status_cb); + status_cb, cb_opaque); if (ret < 0) { goto fail; } @@ -1853,8 +1872,9 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs, l1_table = g_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE); - ret = bdrv_read(bs->file, s->snapshots[i].l1_table_offset / - BDRV_SECTOR_SIZE, (void *)l1_table, l1_sectors); + ret = bdrv_read(bs->file->bs, + s->snapshots[i].l1_table_offset / BDRV_SECTOR_SIZE, + (void *)l1_table, l1_sectors); if (ret < 0) { goto fail; } @@ -1865,7 +1885,7 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs, ret = expand_zero_clusters_in_l1(bs, l1_table, s->snapshots[i].l1_size, &visited_l1_entries, l1_entries, - status_cb); + status_cb, cb_opaque); if (ret < 0) { goto fail; } diff --git a/qemu/block/qcow2-refcount.c b/qemu/block/qcow2-refcount.c index b0ee42d81..ca6094ff5 100644 --- a/qemu/block/qcow2-refcount.c +++ b/qemu/block/qcow2-refcount.c @@ -22,6 +22,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "block/block_int.h" #include "block/qcow2.h" @@ -82,7 +84,7 @@ static Qcow2SetRefcountFunc *const set_refcount_funcs[] = { int qcow2_refcount_init(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; unsigned int refcount_table_size2, i; int ret; @@ -101,7 +103,7 @@ int qcow2_refcount_init(BlockDriverState *bs) goto fail; } BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_LOAD); - ret = bdrv_pread(bs->file, s->refcount_table_offset, + ret = bdrv_pread(bs->file->bs, s->refcount_table_offset, s->refcount_table, refcount_table_size2); if (ret < 0) { goto fail; @@ -116,7 +118,7 @@ int qcow2_refcount_init(BlockDriverState *bs) void qcow2_refcount_close(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; g_free(s->refcount_table); } @@ -214,7 +216,7 @@ static int load_refcount_block(BlockDriverState *bs, int64_t refcount_block_offset, void **refcount_block) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int ret; BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_LOAD); @@ -231,7 +233,7 @@ static int load_refcount_block(BlockDriverState *bs, int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, uint64_t *refcount) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t refcount_table_index, block_index; int64_t refcount_block_offset; int ret; @@ -274,7 +276,7 @@ int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, * Rounds the refcount table size up to avoid growing the table for each single * refcount block that is allocated. */ -static unsigned int next_refcount_table_size(BDRVQcowState *s, +static unsigned int next_refcount_table_size(BDRVQcow2State *s, unsigned int min_size) { unsigned int min_clusters = (min_size >> (s->cluster_bits - 3)) + 1; @@ -290,7 +292,7 @@ static unsigned int next_refcount_table_size(BDRVQcowState *s, /* Checks if two offsets are described by the same refcount block */ -static int in_same_refcount_block(BDRVQcowState *s, uint64_t offset_a, +static int in_same_refcount_block(BDRVQcow2State *s, uint64_t offset_a, uint64_t offset_b) { uint64_t block_a = offset_a >> (s->cluster_bits + s->refcount_block_bits); @@ -308,7 +310,7 @@ static int in_same_refcount_block(BDRVQcowState *s, uint64_t offset_a, static int alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index, void **refcount_block) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; unsigned int refcount_table_index; int ret; @@ -431,7 +433,7 @@ static int alloc_refcount_block(BlockDriverState *bs, if (refcount_table_index < s->refcount_table_size) { uint64_t data64 = cpu_to_be64(new_block); BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_HOOKUP); - ret = bdrv_pwrite_sync(bs->file, + ret = bdrv_pwrite_sync(bs->file->bs, s->refcount_table_offset + refcount_table_index * sizeof(uint64_t), &data64, sizeof(data64)); if (ret < 0) { @@ -535,7 +537,7 @@ static int alloc_refcount_block(BlockDriverState *bs, /* Write refcount blocks to disk */ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS); - ret = bdrv_pwrite_sync(bs->file, meta_offset, new_blocks, + ret = bdrv_pwrite_sync(bs->file->bs, meta_offset, new_blocks, blocks_clusters * s->cluster_size); g_free(new_blocks); new_blocks = NULL; @@ -549,7 +551,7 @@ static int alloc_refcount_block(BlockDriverState *bs, } BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE); - ret = bdrv_pwrite_sync(bs->file, table_offset, new_table, + ret = bdrv_pwrite_sync(bs->file->bs, table_offset, new_table, table_size * sizeof(uint64_t)); if (ret < 0) { goto fail_table; @@ -560,12 +562,16 @@ static int alloc_refcount_block(BlockDriverState *bs, } /* Hook up the new refcount table in the qcow2 header */ - uint8_t data[12]; - cpu_to_be64w((uint64_t*)data, table_offset); - cpu_to_be32w((uint32_t*)(data + 8), table_clusters); + struct QEMU_PACKED { + uint64_t d64; + uint32_t d32; + } data; + cpu_to_be64w(&data.d64, table_offset); + cpu_to_be32w(&data.d32, table_clusters); BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE); - ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, refcount_table_offset), - data, sizeof(data)); + ret = bdrv_pwrite_sync(bs->file->bs, + offsetof(QCowHeader, refcount_table_offset), + &data, sizeof(data)); if (ret < 0) { goto fail_table; } @@ -605,7 +611,7 @@ fail_block: void qcow2_process_discards(BlockDriverState *bs, int ret) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; Qcow2DiscardRegion *d, *next; QTAILQ_FOREACH_SAFE(d, &s->discards, next, next) { @@ -613,7 +619,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret) /* Discard is optional, ignore the return value */ if (ret >= 0) { - bdrv_discard(bs->file, + bdrv_discard(bs->file->bs, d->offset >> BDRV_SECTOR_BITS, d->bytes >> BDRV_SECTOR_BITS); } @@ -625,7 +631,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret) static void update_refcount_discard(BlockDriverState *bs, uint64_t offset, uint64_t length) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; Qcow2DiscardRegion *d, *p, *next; QTAILQ_FOREACH(d, &s->discards, next) { @@ -682,7 +688,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, bool decrease, enum qcow2_discard_type type) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t start, last, cluster_offset; void *refcount_block = NULL; int64_t old_table_index = -1; @@ -793,7 +799,7 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, uint64_t addend, bool decrease, enum qcow2_discard_type type) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int ret; ret = update_refcount(bs, cluster_index << s->cluster_bits, 1, addend, @@ -815,7 +821,7 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, /* return < 0 if error */ static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t i, nb_clusters, refcount; int ret; @@ -875,10 +881,10 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size) return offset; } -int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, - int nb_clusters) +int64_t qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, + int64_t nb_clusters) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t cluster_index, refcount; uint64_t i; int ret; @@ -916,7 +922,7 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, contiguous sectors. size must be <= cluster_size */ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t offset; size_t free_in_cluster; int ret; @@ -949,11 +955,17 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) if (!offset || ROUND_UP(offset, s->cluster_size) != new_cluster) { offset = new_cluster; + free_in_cluster = s->cluster_size; + } else { + free_in_cluster += s->cluster_size; } } assert(offset); ret = update_refcount(bs, offset, size, 1, false, QCOW2_DISCARD_NEVER); + if (ret < 0) { + offset = 0; + } } while (ret == -EAGAIN); if (ret < 0) { return ret; @@ -992,7 +1004,7 @@ void qcow2_free_clusters(BlockDriverState *bs, void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, int nb_clusters, enum qcow2_discard_type type) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; switch (qcow2_get_cluster_type(l2_entry)) { case QCOW2_CLUSTER_COMPRESSED: @@ -1036,7 +1048,7 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, int qcow2_update_snapshot_refcount(BlockDriverState *bs, int64_t l1_table_offset, int l1_size, int addend) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, refcount; bool l1_allocated = false; int64_t old_offset, old_l2_offset; @@ -1062,7 +1074,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, } l1_allocated = true; - ret = bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2); + ret = bdrv_pread(bs->file->bs, l1_table_offset, l1_table, l1_size2); if (ret < 0) { goto fail; } @@ -1215,7 +1227,8 @@ fail: cpu_to_be64s(&l1_table[i]); } - ret = bdrv_pwrite_sync(bs->file, l1_table_offset, l1_table, l1_size2); + ret = bdrv_pwrite_sync(bs->file->bs, l1_table_offset, + l1_table, l1_size2); for (i = 0; i < l1_size; i++) { be64_to_cpus(&l1_table[i]); @@ -1233,7 +1246,7 @@ fail: /* refcount checking functions */ -static size_t refcount_array_byte_size(BDRVQcowState *s, uint64_t entries) +static uint64_t refcount_array_byte_size(BDRVQcow2State *s, uint64_t entries) { /* This assertion holds because there is no way we can address more than * 2^(64 - 9) clusters at once (with cluster size 512 = 2^9, and because @@ -1256,10 +1269,10 @@ static size_t refcount_array_byte_size(BDRVQcowState *s, uint64_t entries) * refcount array buffer will be aligned to a cluster boundary, and the newly * allocated area will be zeroed. */ -static int realloc_refcount_array(BDRVQcowState *s, void **array, +static int realloc_refcount_array(BDRVQcow2State *s, void **array, int64_t *size, int64_t new_size) { - size_t old_byte_size, new_byte_size; + int64_t old_byte_size, new_byte_size; void *new_ptr; /* Round to clusters so the array can be directly written to disk */ @@ -1275,13 +1288,17 @@ static int realloc_refcount_array(BDRVQcowState *s, void **array, assert(new_byte_size > 0); + if (new_byte_size > SIZE_MAX) { + return -ENOMEM; + } + new_ptr = g_try_realloc(*array, new_byte_size); if (!new_ptr) { return -ENOMEM; } if (new_byte_size > old_byte_size) { - memset((void *)((uintptr_t)new_ptr + old_byte_size), 0, + memset((char *)new_ptr + old_byte_size, 0, new_byte_size - old_byte_size); } @@ -1294,7 +1311,7 @@ static int realloc_refcount_array(BDRVQcowState *s, void **array, /* * Increases the refcount for a range of clusters in a given refcount table. * This is used to construct a temporary refcount table out of L1 and L2 tables - * which can be compared the the refcount table saved in the image. + * which can be compared to the refcount table saved in the image. * * Modifies the number of errors in res. */ @@ -1304,7 +1321,7 @@ static int inc_refcounts(BlockDriverState *bs, int64_t *refcount_table_size, int64_t offset, int64_t size) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t start, last, cluster_offset, k, refcount; int ret; @@ -1330,6 +1347,9 @@ static int inc_refcounts(BlockDriverState *bs, if (refcount == s->refcount_max) { fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64 "\n", cluster_offset); + fprintf(stderr, "Use qemu-img amend to increase the refcount entry " + "width or qemu-img convert to create a clean copy if the " + "image cannot be opened for writing\n"); res->corruptions++; continue; } @@ -1357,7 +1377,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, int64_t *refcount_table_size, int64_t l2_offset, int flags) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t *l2_table, l2_entry; uint64_t next_contiguous_offset = 0; int i, l2_size, nb_csectors, ret; @@ -1366,7 +1386,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, l2_size = s->l2_size * sizeof(uint64_t); l2_table = g_malloc(l2_size); - ret = bdrv_pread(bs->file, l2_offset, l2_table, l2_size); + ret = bdrv_pread(bs->file->bs, l2_offset, l2_table, l2_size); if (ret < 0) { fprintf(stderr, "ERROR: I/O error in check_refcounts_l2\n"); res->check_errors++; @@ -1477,7 +1497,7 @@ static int check_refcounts_l1(BlockDriverState *bs, int64_t l1_table_offset, int l1_size, int flags) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t *l1_table = NULL, l2_offset, l1_size2; int i, ret; @@ -1498,7 +1518,7 @@ static int check_refcounts_l1(BlockDriverState *bs, res->check_errors++; goto fail; } - ret = bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2); + ret = bdrv_pread(bs->file->bs, l1_table_offset, l1_table, l1_size2); if (ret < 0) { fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n"); res->check_errors++; @@ -1554,7 +1574,7 @@ fail: static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t *l2_table = qemu_blockalign(bs, s->cluster_size); int ret; uint64_t refcount; @@ -1596,7 +1616,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, } } - ret = bdrv_pread(bs->file, l2_offset, l2_table, + ret = bdrv_pread(bs->file->bs, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)); if (ret < 0) { fprintf(stderr, "ERROR: Could not read L2 table: %s\n", @@ -1648,7 +1668,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, goto fail; } - ret = bdrv_pwrite(bs->file, l2_offset, l2_table, s->cluster_size); + ret = bdrv_pwrite(bs->file->bs, l2_offset, l2_table, + s->cluster_size); if (ret < 0) { fprintf(stderr, "ERROR: Could not write L2 table: %s\n", strerror(-ret)); @@ -1673,7 +1694,7 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix, bool *rebuild, void **refcount_table, int64_t *nb_clusters) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t i, size; int ret; @@ -1703,11 +1724,11 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, goto resize_fail; } - ret = bdrv_truncate(bs->file, offset + s->cluster_size); + ret = bdrv_truncate(bs->file->bs, offset + s->cluster_size); if (ret < 0) { goto resize_fail; } - size = bdrv_getlength(bs->file); + size = bdrv_getlength(bs->file->bs); if (size < 0) { ret = size; goto resize_fail; @@ -1776,7 +1797,7 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix, bool *rebuild, void **refcount_table, int64_t *nb_clusters) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t i; QCowSnapshot *sn; int ret; @@ -1840,7 +1861,7 @@ static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, int64_t *highest_cluster, void *refcount_table, int64_t nb_clusters) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t i; uint64_t refcount1, refcount2; int ret; @@ -1917,7 +1938,7 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs, int64_t *imrt_nb_clusters, int64_t *first_free_cluster) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t cluster = *first_free_cluster, i; bool first_gap = true; int contiguous_free_clusters; @@ -1987,7 +2008,7 @@ static int rebuild_refcount_structure(BlockDriverState *bs, void **refcount_table, int64_t *nb_clusters) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t first_free_cluster = 0, reftable_offset = -1, cluster = 0; int64_t refblock_offset, refblock_start, refblock_index; uint32_t reftable_size = 0; @@ -2081,7 +2102,7 @@ write_refblocks: on_disk_refblock = (void *)((char *) *refcount_table + refblock_index * s->cluster_size); - ret = bdrv_write(bs->file, refblock_offset / BDRV_SECTOR_SIZE, + ret = bdrv_write(bs->file->bs, refblock_offset / BDRV_SECTOR_SIZE, on_disk_refblock, s->cluster_sectors); if (ret < 0) { fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret)); @@ -2130,7 +2151,7 @@ write_refblocks: } assert(reftable_size < INT_MAX / sizeof(uint64_t)); - ret = bdrv_pwrite(bs->file, reftable_offset, on_disk_reftable, + ret = bdrv_pwrite(bs->file->bs, reftable_offset, on_disk_reftable, reftable_size * sizeof(uint64_t)); if (ret < 0) { fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret)); @@ -2142,8 +2163,8 @@ write_refblocks: reftable_offset); cpu_to_be32w(&reftable_offset_and_clusters.reftable_clusters, size_to_clusters(s, reftable_size * sizeof(uint64_t))); - ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, - refcount_table_offset), + ret = bdrv_pwrite_sync(bs->file->bs, offsetof(QCowHeader, + refcount_table_offset), &reftable_offset_and_clusters, sizeof(reftable_offset_and_clusters)); if (ret < 0) { @@ -2174,14 +2195,14 @@ fail: int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; BdrvCheckResult pre_compare_res; int64_t size, highest_cluster, nb_clusters; void *refcount_table = NULL; bool rebuild = false; int ret; - size = bdrv_getlength(bs->file); + size = bdrv_getlength(bs->file->bs); if (size < 0) { res->check_errors++; return size; @@ -2311,7 +2332,7 @@ fail: int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, int64_t size) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int chk = s->overlap_check & ~ign; int i, j; @@ -2390,7 +2411,7 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, return -ENOMEM; } - ret = bdrv_pread(bs->file, l1_ofs, l1, l1_sz2); + ret = bdrv_pread(bs->file->bs, l1_ofs, l1, l1_sz2); if (ret < 0) { g_free(l1); return ret; @@ -2451,3 +2472,450 @@ int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, return 0; } + +/* A pointer to a function of this type is given to walk_over_reftable(). That + * function will create refblocks and pass them to a RefblockFinishOp once they + * are completed (@refblock). @refblock_empty is set if the refblock is + * completely empty. + * + * Along with the refblock, a corresponding reftable entry is passed, in the + * reftable @reftable (which may be reallocated) at @reftable_index. + * + * @allocated should be set to true if a new cluster has been allocated. + */ +typedef int (RefblockFinishOp)(BlockDriverState *bs, uint64_t **reftable, + uint64_t reftable_index, uint64_t *reftable_size, + void *refblock, bool refblock_empty, + bool *allocated, Error **errp); + +/** + * This "operation" for walk_over_reftable() allocates the refblock on disk (if + * it is not empty) and inserts its offset into the new reftable. The size of + * this new reftable is increased as required. + */ +static int alloc_refblock(BlockDriverState *bs, uint64_t **reftable, + uint64_t reftable_index, uint64_t *reftable_size, + void *refblock, bool refblock_empty, bool *allocated, + Error **errp) +{ + BDRVQcow2State *s = bs->opaque; + int64_t offset; + + if (!refblock_empty && reftable_index >= *reftable_size) { + uint64_t *new_reftable; + uint64_t new_reftable_size; + + new_reftable_size = ROUND_UP(reftable_index + 1, + s->cluster_size / sizeof(uint64_t)); + if (new_reftable_size > QCOW_MAX_REFTABLE_SIZE / sizeof(uint64_t)) { + error_setg(errp, + "This operation would make the refcount table grow " + "beyond the maximum size supported by QEMU, aborting"); + return -ENOTSUP; + } + + new_reftable = g_try_realloc(*reftable, new_reftable_size * + sizeof(uint64_t)); + if (!new_reftable) { + error_setg(errp, "Failed to increase reftable buffer size"); + return -ENOMEM; + } + + memset(new_reftable + *reftable_size, 0, + (new_reftable_size - *reftable_size) * sizeof(uint64_t)); + + *reftable = new_reftable; + *reftable_size = new_reftable_size; + } + + if (!refblock_empty && !(*reftable)[reftable_index]) { + offset = qcow2_alloc_clusters(bs, s->cluster_size); + if (offset < 0) { + error_setg_errno(errp, -offset, "Failed to allocate refblock"); + return offset; + } + (*reftable)[reftable_index] = offset; + *allocated = true; + } + + return 0; +} + +/** + * This "operation" for walk_over_reftable() writes the refblock to disk at the + * offset specified by the new reftable's entry. It does not modify the new + * reftable or change any refcounts. + */ +static int flush_refblock(BlockDriverState *bs, uint64_t **reftable, + uint64_t reftable_index, uint64_t *reftable_size, + void *refblock, bool refblock_empty, bool *allocated, + Error **errp) +{ + BDRVQcow2State *s = bs->opaque; + int64_t offset; + int ret; + + if (reftable_index < *reftable_size && (*reftable)[reftable_index]) { + offset = (*reftable)[reftable_index]; + + ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size); + if (ret < 0) { + error_setg_errno(errp, -ret, "Overlap check failed"); + return ret; + } + + ret = bdrv_pwrite(bs->file->bs, offset, refblock, s->cluster_size); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to write refblock"); + return ret; + } + } else { + assert(refblock_empty); + } + + return 0; +} + +/** + * This function walks over the existing reftable and every referenced refblock; + * if @new_set_refcount is non-NULL, it is called for every refcount entry to + * create an equal new entry in the passed @new_refblock. Once that + * @new_refblock is completely filled, @operation will be called. + * + * @status_cb and @cb_opaque are used for the amend operation's status callback. + * @index is the index of the walk_over_reftable() calls and @total is the total + * number of walk_over_reftable() calls per amend operation. Both are used for + * calculating the parameters for the status callback. + * + * @allocated is set to true if a new cluster has been allocated. + */ +static int walk_over_reftable(BlockDriverState *bs, uint64_t **new_reftable, + uint64_t *new_reftable_index, + uint64_t *new_reftable_size, + void *new_refblock, int new_refblock_size, + int new_refcount_bits, + RefblockFinishOp *operation, bool *allocated, + Qcow2SetRefcountFunc *new_set_refcount, + BlockDriverAmendStatusCB *status_cb, + void *cb_opaque, int index, int total, + Error **errp) +{ + BDRVQcow2State *s = bs->opaque; + uint64_t reftable_index; + bool new_refblock_empty = true; + int refblock_index; + int new_refblock_index = 0; + int ret; + + for (reftable_index = 0; reftable_index < s->refcount_table_size; + reftable_index++) + { + uint64_t refblock_offset = s->refcount_table[reftable_index] + & REFT_OFFSET_MASK; + + status_cb(bs, (uint64_t)index * s->refcount_table_size + reftable_index, + (uint64_t)total * s->refcount_table_size, cb_opaque); + + if (refblock_offset) { + void *refblock; + + if (offset_into_cluster(s, refblock_offset)) { + qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#" + PRIx64 " unaligned (reftable index: %#" + PRIx64 ")", refblock_offset, + reftable_index); + error_setg(errp, + "Image is corrupt (unaligned refblock offset)"); + return -EIO; + } + + ret = qcow2_cache_get(bs, s->refcount_block_cache, refblock_offset, + &refblock); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to retrieve refblock"); + return ret; + } + + for (refblock_index = 0; refblock_index < s->refcount_block_size; + refblock_index++) + { + uint64_t refcount; + + if (new_refblock_index >= new_refblock_size) { + /* new_refblock is now complete */ + ret = operation(bs, new_reftable, *new_reftable_index, + new_reftable_size, new_refblock, + new_refblock_empty, allocated, errp); + if (ret < 0) { + qcow2_cache_put(bs, s->refcount_block_cache, &refblock); + return ret; + } + + (*new_reftable_index)++; + new_refblock_index = 0; + new_refblock_empty = true; + } + + refcount = s->get_refcount(refblock, refblock_index); + if (new_refcount_bits < 64 && refcount >> new_refcount_bits) { + uint64_t offset; + + qcow2_cache_put(bs, s->refcount_block_cache, &refblock); + + offset = ((reftable_index << s->refcount_block_bits) + + refblock_index) << s->cluster_bits; + + error_setg(errp, "Cannot decrease refcount entry width to " + "%i bits: Cluster at offset %#" PRIx64 " has a " + "refcount of %" PRIu64, new_refcount_bits, + offset, refcount); + return -EINVAL; + } + + if (new_set_refcount) { + new_set_refcount(new_refblock, new_refblock_index++, + refcount); + } else { + new_refblock_index++; + } + new_refblock_empty = new_refblock_empty && refcount == 0; + } + + qcow2_cache_put(bs, s->refcount_block_cache, &refblock); + } else { + /* No refblock means every refcount is 0 */ + for (refblock_index = 0; refblock_index < s->refcount_block_size; + refblock_index++) + { + if (new_refblock_index >= new_refblock_size) { + /* new_refblock is now complete */ + ret = operation(bs, new_reftable, *new_reftable_index, + new_reftable_size, new_refblock, + new_refblock_empty, allocated, errp); + if (ret < 0) { + return ret; + } + + (*new_reftable_index)++; + new_refblock_index = 0; + new_refblock_empty = true; + } + + if (new_set_refcount) { + new_set_refcount(new_refblock, new_refblock_index++, 0); + } else { + new_refblock_index++; + } + } + } + } + + if (new_refblock_index > 0) { + /* Complete the potentially existing partially filled final refblock */ + if (new_set_refcount) { + for (; new_refblock_index < new_refblock_size; + new_refblock_index++) + { + new_set_refcount(new_refblock, new_refblock_index, 0); + } + } + + ret = operation(bs, new_reftable, *new_reftable_index, + new_reftable_size, new_refblock, new_refblock_empty, + allocated, errp); + if (ret < 0) { + return ret; + } + + (*new_reftable_index)++; + } + + status_cb(bs, (uint64_t)(index + 1) * s->refcount_table_size, + (uint64_t)total * s->refcount_table_size, cb_opaque); + + return 0; +} + +int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, + BlockDriverAmendStatusCB *status_cb, + void *cb_opaque, Error **errp) +{ + BDRVQcow2State *s = bs->opaque; + Qcow2GetRefcountFunc *new_get_refcount; + Qcow2SetRefcountFunc *new_set_refcount; + void *new_refblock = qemu_blockalign(bs->file->bs, s->cluster_size); + uint64_t *new_reftable = NULL, new_reftable_size = 0; + uint64_t *old_reftable, old_reftable_size, old_reftable_offset; + uint64_t new_reftable_index = 0; + uint64_t i; + int64_t new_reftable_offset = 0, allocated_reftable_size = 0; + int new_refblock_size, new_refcount_bits = 1 << refcount_order; + int old_refcount_order; + int walk_index = 0; + int ret; + bool new_allocation; + + assert(s->qcow_version >= 3); + assert(refcount_order >= 0 && refcount_order <= 6); + + /* see qcow2_open() */ + new_refblock_size = 1 << (s->cluster_bits - (refcount_order - 3)); + + new_get_refcount = get_refcount_funcs[refcount_order]; + new_set_refcount = set_refcount_funcs[refcount_order]; + + + do { + int total_walks; + + new_allocation = false; + + /* At least we have to do this walk and the one which writes the + * refblocks; also, at least we have to do this loop here at least + * twice (normally), first to do the allocations, and second to + * determine that everything is correctly allocated, this then makes + * three walks in total */ + total_walks = MAX(walk_index + 2, 3); + + /* First, allocate the structures so they are present in the refcount + * structures */ + ret = walk_over_reftable(bs, &new_reftable, &new_reftable_index, + &new_reftable_size, NULL, new_refblock_size, + new_refcount_bits, &alloc_refblock, + &new_allocation, NULL, status_cb, cb_opaque, + walk_index++, total_walks, errp); + if (ret < 0) { + goto done; + } + + new_reftable_index = 0; + + if (new_allocation) { + if (new_reftable_offset) { + qcow2_free_clusters(bs, new_reftable_offset, + allocated_reftable_size * sizeof(uint64_t), + QCOW2_DISCARD_NEVER); + } + + new_reftable_offset = qcow2_alloc_clusters(bs, new_reftable_size * + sizeof(uint64_t)); + if (new_reftable_offset < 0) { + error_setg_errno(errp, -new_reftable_offset, + "Failed to allocate the new reftable"); + ret = new_reftable_offset; + goto done; + } + allocated_reftable_size = new_reftable_size; + } + } while (new_allocation); + + /* Second, write the new refblocks */ + ret = walk_over_reftable(bs, &new_reftable, &new_reftable_index, + &new_reftable_size, new_refblock, + new_refblock_size, new_refcount_bits, + &flush_refblock, &new_allocation, new_set_refcount, + status_cb, cb_opaque, walk_index, walk_index + 1, + errp); + if (ret < 0) { + goto done; + } + assert(!new_allocation); + + + /* Write the new reftable */ + ret = qcow2_pre_write_overlap_check(bs, 0, new_reftable_offset, + new_reftable_size * sizeof(uint64_t)); + if (ret < 0) { + error_setg_errno(errp, -ret, "Overlap check failed"); + goto done; + } + + for (i = 0; i < new_reftable_size; i++) { + cpu_to_be64s(&new_reftable[i]); + } + + ret = bdrv_pwrite(bs->file->bs, new_reftable_offset, new_reftable, + new_reftable_size * sizeof(uint64_t)); + + for (i = 0; i < new_reftable_size; i++) { + be64_to_cpus(&new_reftable[i]); + } + + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to write the new reftable"); + goto done; + } + + + /* Empty the refcount cache */ + ret = qcow2_cache_flush(bs, s->refcount_block_cache); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to flush the refblock cache"); + goto done; + } + + /* Update the image header to point to the new reftable; this only updates + * the fields which are relevant to qcow2_update_header(); other fields + * such as s->refcount_table or s->refcount_bits stay stale for now + * (because we have to restore everything if qcow2_update_header() fails) */ + old_refcount_order = s->refcount_order; + old_reftable_size = s->refcount_table_size; + old_reftable_offset = s->refcount_table_offset; + + s->refcount_order = refcount_order; + s->refcount_table_size = new_reftable_size; + s->refcount_table_offset = new_reftable_offset; + + ret = qcow2_update_header(bs); + if (ret < 0) { + s->refcount_order = old_refcount_order; + s->refcount_table_size = old_reftable_size; + s->refcount_table_offset = old_reftable_offset; + error_setg_errno(errp, -ret, "Failed to update the qcow2 header"); + goto done; + } + + /* Now update the rest of the in-memory information */ + old_reftable = s->refcount_table; + s->refcount_table = new_reftable; + + s->refcount_bits = 1 << refcount_order; + s->refcount_max = UINT64_C(1) << (s->refcount_bits - 1); + s->refcount_max += s->refcount_max - 1; + + s->refcount_block_bits = s->cluster_bits - (refcount_order - 3); + s->refcount_block_size = 1 << s->refcount_block_bits; + + s->get_refcount = new_get_refcount; + s->set_refcount = new_set_refcount; + + /* For cleaning up all old refblocks and the old reftable below the "done" + * label */ + new_reftable = old_reftable; + new_reftable_size = old_reftable_size; + new_reftable_offset = old_reftable_offset; + +done: + if (new_reftable) { + /* On success, new_reftable actually points to the old reftable (and + * new_reftable_size is the old reftable's size); but that is just + * fine */ + for (i = 0; i < new_reftable_size; i++) { + uint64_t offset = new_reftable[i] & REFT_OFFSET_MASK; + if (offset) { + qcow2_free_clusters(bs, offset, s->cluster_size, + QCOW2_DISCARD_OTHER); + } + } + g_free(new_reftable); + + if (new_reftable_offset > 0) { + qcow2_free_clusters(bs, new_reftable_offset, + new_reftable_size * sizeof(uint64_t), + QCOW2_DISCARD_OTHER); + } + } + + qemu_vfree(new_refblock); + return ret; +} diff --git a/qemu/block/qcow2-snapshot.c b/qemu/block/qcow2-snapshot.c index b6f58c13e..5f4a17e47 100644 --- a/qemu/block/qcow2-snapshot.c +++ b/qemu/block/qcow2-snapshot.c @@ -22,14 +22,16 @@ * THE SOFTWARE. */ -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "qapi/error.h" #include "block/block_int.h" #include "block/qcow2.h" #include "qemu/error-report.h" +#include "qemu/cutils.h" void qcow2_free_snapshots(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int i; for(i = 0; i < s->nb_snapshots; i++) { @@ -43,7 +45,7 @@ void qcow2_free_snapshots(BlockDriverState *bs) int qcow2_read_snapshots(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowSnapshotHeader h; QCowSnapshotExtraData extra; QCowSnapshot *sn; @@ -64,7 +66,7 @@ int qcow2_read_snapshots(BlockDriverState *bs) for(i = 0; i < s->nb_snapshots; i++) { /* Read statically sized part of the snapshot header */ offset = align_offset(offset, 8); - ret = bdrv_pread(bs->file, offset, &h, sizeof(h)); + ret = bdrv_pread(bs->file->bs, offset, &h, sizeof(h)); if (ret < 0) { goto fail; } @@ -83,7 +85,7 @@ int qcow2_read_snapshots(BlockDriverState *bs) name_size = be16_to_cpu(h.name_size); /* Read extra data */ - ret = bdrv_pread(bs->file, offset, &extra, + ret = bdrv_pread(bs->file->bs, offset, &extra, MIN(sizeof(extra), extra_data_size)); if (ret < 0) { goto fail; @@ -102,7 +104,7 @@ int qcow2_read_snapshots(BlockDriverState *bs) /* Read snapshot ID */ sn->id_str = g_malloc(id_str_size + 1); - ret = bdrv_pread(bs->file, offset, sn->id_str, id_str_size); + ret = bdrv_pread(bs->file->bs, offset, sn->id_str, id_str_size); if (ret < 0) { goto fail; } @@ -111,7 +113,7 @@ int qcow2_read_snapshots(BlockDriverState *bs) /* Read snapshot name */ sn->name = g_malloc(name_size + 1); - ret = bdrv_pread(bs->file, offset, sn->name, name_size); + ret = bdrv_pread(bs->file->bs, offset, sn->name, name_size); if (ret < 0) { goto fail; } @@ -136,7 +138,7 @@ fail: /* add at the end of the file a new list of snapshots */ static int qcow2_write_snapshots(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowSnapshot *sn; QCowSnapshotHeader h; QCowSnapshotExtraData extra; @@ -214,25 +216,25 @@ static int qcow2_write_snapshots(BlockDriverState *bs) h.name_size = cpu_to_be16(name_size); offset = align_offset(offset, 8); - ret = bdrv_pwrite(bs->file, offset, &h, sizeof(h)); + ret = bdrv_pwrite(bs->file->bs, offset, &h, sizeof(h)); if (ret < 0) { goto fail; } offset += sizeof(h); - ret = bdrv_pwrite(bs->file, offset, &extra, sizeof(extra)); + ret = bdrv_pwrite(bs->file->bs, offset, &extra, sizeof(extra)); if (ret < 0) { goto fail; } offset += sizeof(extra); - ret = bdrv_pwrite(bs->file, offset, sn->id_str, id_str_size); + ret = bdrv_pwrite(bs->file->bs, offset, sn->id_str, id_str_size); if (ret < 0) { goto fail; } offset += id_str_size; - ret = bdrv_pwrite(bs->file, offset, sn->name, name_size); + ret = bdrv_pwrite(bs->file->bs, offset, sn->name, name_size); if (ret < 0) { goto fail; } @@ -254,7 +256,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs) header_data.nb_snapshots = cpu_to_be32(s->nb_snapshots); header_data.snapshots_offset = cpu_to_be64(snapshots_offset); - ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), + ret = bdrv_pwrite_sync(bs->file->bs, offsetof(QCowHeader, nb_snapshots), &header_data, sizeof(header_data)); if (ret < 0) { goto fail; @@ -278,7 +280,7 @@ fail: static void find_new_snapshot_id(BlockDriverState *bs, char *id_str, int id_str_size) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowSnapshot *sn; int i; unsigned long id, id_max = 0; @@ -296,7 +298,7 @@ static int find_snapshot_by_id_and_name(BlockDriverState *bs, const char *id, const char *name) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int i; if (id && name) { @@ -338,7 +340,7 @@ static int find_snapshot_by_id_or_name(BlockDriverState *bs, /* if no id is provided, a new one is constructed */ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowSnapshot *new_snapshot_list = NULL; QCowSnapshot *old_snapshot_list = NULL; QCowSnapshot sn1, *sn = &sn1; @@ -396,7 +398,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) goto fail; } - ret = bdrv_pwrite(bs->file, sn->l1_table_offset, l1_table, + ret = bdrv_pwrite(bs->file->bs, sn->l1_table_offset, l1_table, s->l1_size * sizeof(uint64_t)); if (ret < 0) { goto fail; @@ -461,7 +463,7 @@ fail: /* copy the snapshot 'snapshot_name' into the current disk image */ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowSnapshot *sn; int i, snapshot_index; int cur_l1_bytes, sn_l1_bytes; @@ -509,7 +511,8 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) goto fail; } - ret = bdrv_pread(bs->file, sn->l1_table_offset, sn_l1_table, sn_l1_bytes); + ret = bdrv_pread(bs->file->bs, sn->l1_table_offset, + sn_l1_table, sn_l1_bytes); if (ret < 0) { goto fail; } @@ -526,7 +529,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) goto fail; } - ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset, sn_l1_table, + ret = bdrv_pwrite_sync(bs->file->bs, s->l1_table_offset, sn_l1_table, cur_l1_bytes); if (ret < 0) { goto fail; @@ -587,7 +590,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *name, Error **errp) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowSnapshot sn; int snapshot_index, ret; @@ -650,7 +653,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QEMUSnapshotInfo *sn_tab, *sn_info; QCowSnapshot *sn; int i; @@ -683,7 +686,7 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, Error **errp) { int i, snapshot_index; - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowSnapshot *sn; uint64_t *new_l1_table; int new_l1_bytes; @@ -706,13 +709,14 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, return -EFBIG; } new_l1_bytes = sn->l1_size * sizeof(uint64_t); - new_l1_table = qemu_try_blockalign(bs->file, + new_l1_table = qemu_try_blockalign(bs->file->bs, align_offset(new_l1_bytes, 512)); if (new_l1_table == NULL) { return -ENOMEM; } - ret = bdrv_pread(bs->file, sn->l1_table_offset, new_l1_table, new_l1_bytes); + ret = bdrv_pread(bs->file->bs, sn->l1_table_offset, + new_l1_table, new_l1_bytes); if (ret < 0) { error_setg(errp, "Failed to read l1 table for snapshot"); qemu_vfree(new_l1_table); diff --git a/qemu/block/qcow2.c b/qemu/block/qcow2.c index 76c331b38..470734be9 100644 --- a/qemu/block/qcow2.c +++ b/qemu/block/qcow2.c @@ -21,8 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "qemu-common.h" +#include "qemu/osdep.h" #include "block/block_int.h" +#include "sysemu/block-backend.h" #include "qemu/module.h" #include #include "block/qcow2.h" @@ -34,6 +35,7 @@ #include "qapi-event.h" #include "trace.h" #include "qemu/option_int.h" +#include "qemu/cutils.h" /* Differences with QCOW: @@ -85,7 +87,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, uint64_t end_offset, void **p_feature_table, Error **errp) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowExtension ext; uint64_t offset; int ret; @@ -104,7 +106,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, printf("attempting to read extended header in offset %lu\n", offset); #endif - ret = bdrv_pread(bs->file, offset, &ext, sizeof(ext)); + ret = bdrv_pread(bs->file->bs, offset, &ext, sizeof(ext)); if (ret < 0) { error_setg_errno(errp, -ret, "qcow2_read_extension: ERROR: " "pread fail from offset %" PRIu64, offset); @@ -132,7 +134,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, sizeof(bs->backing_format)); return 2; } - ret = bdrv_pread(bs->file, offset, bs->backing_format, ext.len); + ret = bdrv_pread(bs->file->bs, offset, bs->backing_format, ext.len); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR: ext_backing_format: " "Could not read format name"); @@ -148,7 +150,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, case QCOW2_EXT_MAGIC_FEATURE_TABLE: if (p_feature_table != NULL) { void* feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature)); - ret = bdrv_pread(bs->file, offset , feature_table, ext.len); + ret = bdrv_pread(bs->file->bs, offset , feature_table, ext.len); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR: ext_feature_table: " "Could not read table"); @@ -169,7 +171,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, uext->len = ext.len; QLIST_INSERT_HEAD(&s->unknown_header_ext, uext, next); - ret = bdrv_pread(bs->file, offset , uext->data, uext->len); + ret = bdrv_pread(bs->file->bs, offset , uext->data, uext->len); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR: unknown extension: " "Could not read data"); @@ -187,7 +189,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, static void cleanup_unknown_header_ext(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; Qcow2UnknownHeaderExtension *uext, *next; QLIST_FOREACH_SAFE(uext, &s->unknown_header_ext, next, next) { @@ -196,22 +198,8 @@ static void cleanup_unknown_header_ext(BlockDriverState *bs) } } -static void GCC_FMT_ATTR(3, 4) report_unsupported(BlockDriverState *bs, - Error **errp, const char *fmt, ...) -{ - char msg[64]; - va_list ap; - - va_start(ap, fmt); - vsnprintf(msg, sizeof(msg), fmt, ap); - va_end(ap); - - error_setg(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, - bdrv_get_device_or_node_name(bs), "qcow2", msg); -} - -static void report_unsupported_feature(BlockDriverState *bs, - Error **errp, Qcow2Feature *table, uint64_t mask) +static void report_unsupported_feature(Error **errp, Qcow2Feature *table, + uint64_t mask) { char *features = g_strdup(""); char *old; @@ -236,7 +224,7 @@ static void report_unsupported_feature(BlockDriverState *bs, g_free(old); } - report_unsupported(bs, errp, "%s", features); + error_setg(errp, "Unsupported qcow2 feature(s): %s", features); g_free(features); } @@ -249,7 +237,7 @@ static void report_unsupported_feature(BlockDriverState *bs, */ int qcow2_mark_dirty(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t val; int ret; @@ -260,12 +248,12 @@ int qcow2_mark_dirty(BlockDriverState *bs) } val = cpu_to_be64(s->incompatible_features | QCOW2_INCOMPAT_DIRTY); - ret = bdrv_pwrite(bs->file, offsetof(QCowHeader, incompatible_features), + ret = bdrv_pwrite(bs->file->bs, offsetof(QCowHeader, incompatible_features), &val, sizeof(val)); if (ret < 0) { return ret; } - ret = bdrv_flush(bs->file); + ret = bdrv_flush(bs->file->bs); if (ret < 0) { return ret; } @@ -282,7 +270,7 @@ int qcow2_mark_dirty(BlockDriverState *bs) */ static int qcow2_mark_clean(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { int ret; @@ -304,7 +292,7 @@ static int qcow2_mark_clean(BlockDriverState *bs) */ int qcow2_mark_corrupt(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; s->incompatible_features |= QCOW2_INCOMPAT_CORRUPT; return qcow2_update_header(bs); @@ -316,7 +304,7 @@ int qcow2_mark_corrupt(BlockDriverState *bs) */ int qcow2_mark_consistent(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) { int ret = bdrv_flush(bs); @@ -351,7 +339,7 @@ static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result, static int validate_table_offset(BlockDriverState *bs, uint64_t offset, uint64_t entries, size_t entry_len) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t size; /* Use signed INT64_MAX as the maximum even for uint64_t header fields, @@ -467,6 +455,11 @@ static QemuOptsList qcow2_runtime_opts = { .type = QEMU_OPT_SIZE, .help = "Maximum refcount block cache size", }, + { + .name = QCOW2_OPT_CACHE_CLEAN_INTERVAL, + .type = QEMU_OPT_NUMBER, + .help = "Clean unused cache entries after this time (in seconds)", + }, { /* end of list */ } }, }; @@ -482,11 +475,54 @@ static const char *overlap_bool_option_names[QCOW2_OL_MAX_BITNR] = { [QCOW2_OL_INACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L2, }; +static void cache_clean_timer_cb(void *opaque) +{ + BlockDriverState *bs = opaque; + BDRVQcow2State *s = bs->opaque; + qcow2_cache_clean_unused(bs, s->l2_table_cache); + qcow2_cache_clean_unused(bs, s->refcount_block_cache); + timer_mod(s->cache_clean_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + (int64_t) s->cache_clean_interval * 1000); +} + +static void cache_clean_timer_init(BlockDriverState *bs, AioContext *context) +{ + BDRVQcow2State *s = bs->opaque; + if (s->cache_clean_interval > 0) { + s->cache_clean_timer = aio_timer_new(context, QEMU_CLOCK_VIRTUAL, + SCALE_MS, cache_clean_timer_cb, + bs); + timer_mod(s->cache_clean_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + (int64_t) s->cache_clean_interval * 1000); + } +} + +static void cache_clean_timer_del(BlockDriverState *bs) +{ + BDRVQcow2State *s = bs->opaque; + if (s->cache_clean_timer) { + timer_del(s->cache_clean_timer); + timer_free(s->cache_clean_timer); + s->cache_clean_timer = NULL; + } +} + +static void qcow2_detach_aio_context(BlockDriverState *bs) +{ + cache_clean_timer_del(bs); +} + +static void qcow2_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) +{ + cache_clean_timer_init(bs, new_context); +} + static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, uint64_t *l2_cache_size, uint64_t *refcount_cache_size, Error **errp) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t combined_cache_size; bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set; @@ -541,22 +577,246 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, } } +typedef struct Qcow2ReopenState { + Qcow2Cache *l2_table_cache; + Qcow2Cache *refcount_block_cache; + bool use_lazy_refcounts; + int overlap_check; + bool discard_passthrough[QCOW2_DISCARD_MAX]; + uint64_t cache_clean_interval; +} Qcow2ReopenState; + +static int qcow2_update_options_prepare(BlockDriverState *bs, + Qcow2ReopenState *r, + QDict *options, int flags, + Error **errp) +{ + BDRVQcow2State *s = bs->opaque; + QemuOpts *opts = NULL; + const char *opt_overlap_check, *opt_overlap_check_template; + int overlap_check_template = 0; + uint64_t l2_cache_size, refcount_cache_size; + int i; + Error *local_err = NULL; + int ret; + + opts = qemu_opts_create(&qcow2_runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + /* get L2 table/refcount block cache size from command line options */ + read_cache_sizes(bs, opts, &l2_cache_size, &refcount_cache_size, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + l2_cache_size /= s->cluster_size; + if (l2_cache_size < MIN_L2_CACHE_SIZE) { + l2_cache_size = MIN_L2_CACHE_SIZE; + } + if (l2_cache_size > INT_MAX) { + error_setg(errp, "L2 cache size too big"); + ret = -EINVAL; + goto fail; + } + + refcount_cache_size /= s->cluster_size; + if (refcount_cache_size < MIN_REFCOUNT_CACHE_SIZE) { + refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE; + } + if (refcount_cache_size > INT_MAX) { + error_setg(errp, "Refcount cache size too big"); + ret = -EINVAL; + goto fail; + } + + /* alloc new L2 table/refcount block cache, flush old one */ + if (s->l2_table_cache) { + ret = qcow2_cache_flush(bs, s->l2_table_cache); + if (ret) { + error_setg_errno(errp, -ret, "Failed to flush the L2 table cache"); + goto fail; + } + } + + if (s->refcount_block_cache) { + ret = qcow2_cache_flush(bs, s->refcount_block_cache); + if (ret) { + error_setg_errno(errp, -ret, + "Failed to flush the refcount block cache"); + goto fail; + } + } + + r->l2_table_cache = qcow2_cache_create(bs, l2_cache_size); + r->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size); + if (r->l2_table_cache == NULL || r->refcount_block_cache == NULL) { + error_setg(errp, "Could not allocate metadata caches"); + ret = -ENOMEM; + goto fail; + } + + /* New interval for cache cleanup timer */ + r->cache_clean_interval = + qemu_opt_get_number(opts, QCOW2_OPT_CACHE_CLEAN_INTERVAL, + s->cache_clean_interval); + if (r->cache_clean_interval > UINT_MAX) { + error_setg(errp, "Cache clean interval too big"); + ret = -EINVAL; + goto fail; + } + + /* lazy-refcounts; flush if going from enabled to disabled */ + r->use_lazy_refcounts = qemu_opt_get_bool(opts, QCOW2_OPT_LAZY_REFCOUNTS, + (s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS)); + if (r->use_lazy_refcounts && s->qcow_version < 3) { + error_setg(errp, "Lazy refcounts require a qcow2 image with at least " + "qemu 1.1 compatibility level"); + ret = -EINVAL; + goto fail; + } + + if (s->use_lazy_refcounts && !r->use_lazy_refcounts) { + ret = qcow2_mark_clean(bs); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to disable lazy refcounts"); + goto fail; + } + } + + /* Overlap check options */ + opt_overlap_check = qemu_opt_get(opts, QCOW2_OPT_OVERLAP); + opt_overlap_check_template = qemu_opt_get(opts, QCOW2_OPT_OVERLAP_TEMPLATE); + if (opt_overlap_check_template && opt_overlap_check && + strcmp(opt_overlap_check_template, opt_overlap_check)) + { + error_setg(errp, "Conflicting values for qcow2 options '" + QCOW2_OPT_OVERLAP "' ('%s') and '" QCOW2_OPT_OVERLAP_TEMPLATE + "' ('%s')", opt_overlap_check, opt_overlap_check_template); + ret = -EINVAL; + goto fail; + } + if (!opt_overlap_check) { + opt_overlap_check = opt_overlap_check_template ?: "cached"; + } + + if (!strcmp(opt_overlap_check, "none")) { + overlap_check_template = 0; + } else if (!strcmp(opt_overlap_check, "constant")) { + overlap_check_template = QCOW2_OL_CONSTANT; + } else if (!strcmp(opt_overlap_check, "cached")) { + overlap_check_template = QCOW2_OL_CACHED; + } else if (!strcmp(opt_overlap_check, "all")) { + overlap_check_template = QCOW2_OL_ALL; + } else { + error_setg(errp, "Unsupported value '%s' for qcow2 option " + "'overlap-check'. Allowed are any of the following: " + "none, constant, cached, all", opt_overlap_check); + ret = -EINVAL; + goto fail; + } + + r->overlap_check = 0; + for (i = 0; i < QCOW2_OL_MAX_BITNR; i++) { + /* overlap-check defines a template bitmask, but every flag may be + * overwritten through the associated boolean option */ + r->overlap_check |= + qemu_opt_get_bool(opts, overlap_bool_option_names[i], + overlap_check_template & (1 << i)) << i; + } + + r->discard_passthrough[QCOW2_DISCARD_NEVER] = false; + r->discard_passthrough[QCOW2_DISCARD_ALWAYS] = true; + r->discard_passthrough[QCOW2_DISCARD_REQUEST] = + qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_REQUEST, + flags & BDRV_O_UNMAP); + r->discard_passthrough[QCOW2_DISCARD_SNAPSHOT] = + qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_SNAPSHOT, true); + r->discard_passthrough[QCOW2_DISCARD_OTHER] = + qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false); + + ret = 0; +fail: + qemu_opts_del(opts); + opts = NULL; + return ret; +} + +static void qcow2_update_options_commit(BlockDriverState *bs, + Qcow2ReopenState *r) +{ + BDRVQcow2State *s = bs->opaque; + int i; + + if (s->l2_table_cache) { + qcow2_cache_destroy(bs, s->l2_table_cache); + } + if (s->refcount_block_cache) { + qcow2_cache_destroy(bs, s->refcount_block_cache); + } + s->l2_table_cache = r->l2_table_cache; + s->refcount_block_cache = r->refcount_block_cache; + + s->overlap_check = r->overlap_check; + s->use_lazy_refcounts = r->use_lazy_refcounts; + + for (i = 0; i < QCOW2_DISCARD_MAX; i++) { + s->discard_passthrough[i] = r->discard_passthrough[i]; + } + + if (s->cache_clean_interval != r->cache_clean_interval) { + cache_clean_timer_del(bs); + s->cache_clean_interval = r->cache_clean_interval; + cache_clean_timer_init(bs, bdrv_get_aio_context(bs)); + } +} + +static void qcow2_update_options_abort(BlockDriverState *bs, + Qcow2ReopenState *r) +{ + if (r->l2_table_cache) { + qcow2_cache_destroy(bs, r->l2_table_cache); + } + if (r->refcount_block_cache) { + qcow2_cache_destroy(bs, r->refcount_block_cache); + } +} + +static int qcow2_update_options(BlockDriverState *bs, QDict *options, + int flags, Error **errp) +{ + Qcow2ReopenState r = {}; + int ret; + + ret = qcow2_update_options_prepare(bs, &r, options, flags, errp); + if (ret >= 0) { + qcow2_update_options_commit(bs, &r); + } else { + qcow2_update_options_abort(bs, &r); + } + + return ret; +} + static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; unsigned int len, i; int ret = 0; QCowHeader header; - QemuOpts *opts = NULL; Error *local_err = NULL; uint64_t ext_end; uint64_t l1_vm_state_index; - const char *opt_overlap_check, *opt_overlap_check_template; - int overlap_check_template = 0; - uint64_t l2_cache_size, refcount_cache_size; - ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); + ret = bdrv_pread(bs->file->bs, 0, &header, sizeof(header)); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read qcow2 header"); goto fail; @@ -581,7 +841,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } if (header.version < 2 || header.version > 3) { - report_unsupported(bs, errp, "QCOW version %" PRIu32, header.version); + error_setg(errp, "Unsupported qcow2 version %" PRIu32, header.version); ret = -ENOTSUP; goto fail; } @@ -631,7 +891,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, if (header.header_length > sizeof(header)) { s->unknown_header_fields_size = header.header_length - sizeof(header); s->unknown_header_fields = g_malloc(s->unknown_header_fields_size); - ret = bdrv_pread(bs->file, sizeof(header), s->unknown_header_fields, + ret = bdrv_pread(bs->file->bs, sizeof(header), s->unknown_header_fields, s->unknown_header_fields_size); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read unknown qcow2 header " @@ -661,7 +921,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, void *feature_table = NULL; qcow2_read_extensions(bs, header.header_length, ext_end, &feature_table, NULL); - report_unsupported_feature(bs, errp, feature_table, + report_unsupported_feature(errp, feature_table, s->incompatible_features & ~QCOW2_INCOMPAT_MASK); ret = -ENOTSUP; @@ -705,6 +965,14 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, } s->crypt_method_header = header.crypt_method; if (s->crypt_method_header) { + if (bdrv_uses_whitelist() && + s->crypt_method_header == QCOW_CRYPT_AES) { + error_report("qcow2 built-in AES encryption is deprecated"); + error_printf("Support for it will be removed in a future release.\n" + "You can use 'qemu-img convert' to switch to an\n" + "unencrypted qcow2 image, or a LUKS raw image.\n"); + } + bs->encrypted = 1; } @@ -784,14 +1052,14 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, if (s->l1_size > 0) { - s->l1_table = qemu_try_blockalign(bs->file, + s->l1_table = qemu_try_blockalign(bs->file->bs, align_offset(s->l1_size * sizeof(uint64_t), 512)); if (s->l1_table == NULL) { error_setg(errp, "Could not allocate L1 table"); ret = -ENOMEM; goto fail; } - ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, + ret = bdrv_pread(bs->file->bs, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read L1 table"); @@ -802,55 +1070,15 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, } } - /* get L2 table/refcount block cache size from command line options */ - opts = qemu_opts_create(&qcow2_runtime_opts, NULL, 0, &error_abort); - qemu_opts_absorb_qdict(opts, options, &local_err); - if (local_err) { - error_propagate(errp, local_err); - ret = -EINVAL; - goto fail; - } - - read_cache_sizes(bs, opts, &l2_cache_size, &refcount_cache_size, - &local_err); - if (local_err) { - error_propagate(errp, local_err); - ret = -EINVAL; - goto fail; - } - - l2_cache_size /= s->cluster_size; - if (l2_cache_size < MIN_L2_CACHE_SIZE) { - l2_cache_size = MIN_L2_CACHE_SIZE; - } - if (l2_cache_size > INT_MAX) { - error_setg(errp, "L2 cache size too big"); - ret = -EINVAL; - goto fail; - } - - refcount_cache_size /= s->cluster_size; - if (refcount_cache_size < MIN_REFCOUNT_CACHE_SIZE) { - refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE; - } - if (refcount_cache_size > INT_MAX) { - error_setg(errp, "Refcount cache size too big"); - ret = -EINVAL; - goto fail; - } - - /* alloc L2 table/refcount block cache */ - s->l2_table_cache = qcow2_cache_create(bs, l2_cache_size); - s->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size); - if (s->l2_table_cache == NULL || s->refcount_block_cache == NULL) { - error_setg(errp, "Could not allocate metadata caches"); - ret = -ENOMEM; + /* Parse driver-specific options */ + ret = qcow2_update_options(bs, options, flags, errp); + if (ret < 0) { goto fail; } s->cluster_cache = g_malloc(s->cluster_size); /* one more sector for decompressed data alignment */ - s->cluster_data = qemu_try_blockalign(bs->file, QCOW_MAX_CRYPT_CLUSTERS + s->cluster_data = qemu_try_blockalign(bs->file->bs, QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size + 512); if (s->cluster_data == NULL) { error_setg(errp, "Could not allocate temporary cluster buffer"); @@ -887,7 +1115,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, ret = -EINVAL; goto fail; } - ret = bdrv_pread(bs->file, header.backing_file_offset, + ret = bdrv_pread(bs->file->bs, header.backing_file_offset, bs->backing_file, len); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read backing file name"); @@ -908,7 +1136,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, } /* Clear unknown autoclear feature bits */ - if (!bs->read_only && !(flags & BDRV_O_INCOMING) && s->autoclear_features) { + if (!bs->read_only && !(flags & BDRV_O_INACTIVE) && s->autoclear_features) { s->autoclear_features = 0; ret = qcow2_update_header(bs); if (ret < 0) { @@ -921,7 +1149,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, qemu_co_mutex_init(&s->lock); /* Repair image if dirty */ - if (!(flags & (BDRV_O_CHECK | BDRV_O_INCOMING)) && !bs->read_only && + if (!(flags & (BDRV_O_CHECK | BDRV_O_INACTIVE)) && !bs->read_only && (s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) { BdrvCheckResult result = {0}; @@ -932,70 +1160,6 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, } } - /* Enable lazy_refcounts according to image and command line options */ - s->use_lazy_refcounts = qemu_opt_get_bool(opts, QCOW2_OPT_LAZY_REFCOUNTS, - (s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS)); - - s->discard_passthrough[QCOW2_DISCARD_NEVER] = false; - s->discard_passthrough[QCOW2_DISCARD_ALWAYS] = true; - s->discard_passthrough[QCOW2_DISCARD_REQUEST] = - qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_REQUEST, - flags & BDRV_O_UNMAP); - s->discard_passthrough[QCOW2_DISCARD_SNAPSHOT] = - qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_SNAPSHOT, true); - s->discard_passthrough[QCOW2_DISCARD_OTHER] = - qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false); - - opt_overlap_check = qemu_opt_get(opts, QCOW2_OPT_OVERLAP); - opt_overlap_check_template = qemu_opt_get(opts, QCOW2_OPT_OVERLAP_TEMPLATE); - if (opt_overlap_check_template && opt_overlap_check && - strcmp(opt_overlap_check_template, opt_overlap_check)) - { - error_setg(errp, "Conflicting values for qcow2 options '" - QCOW2_OPT_OVERLAP "' ('%s') and '" QCOW2_OPT_OVERLAP_TEMPLATE - "' ('%s')", opt_overlap_check, opt_overlap_check_template); - ret = -EINVAL; - goto fail; - } - if (!opt_overlap_check) { - opt_overlap_check = opt_overlap_check_template ?: "cached"; - } - - if (!strcmp(opt_overlap_check, "none")) { - overlap_check_template = 0; - } else if (!strcmp(opt_overlap_check, "constant")) { - overlap_check_template = QCOW2_OL_CONSTANT; - } else if (!strcmp(opt_overlap_check, "cached")) { - overlap_check_template = QCOW2_OL_CACHED; - } else if (!strcmp(opt_overlap_check, "all")) { - overlap_check_template = QCOW2_OL_ALL; - } else { - error_setg(errp, "Unsupported value '%s' for qcow2 option " - "'overlap-check'. Allowed are either of the following: " - "none, constant, cached, all", opt_overlap_check); - ret = -EINVAL; - goto fail; - } - - s->overlap_check = 0; - for (i = 0; i < QCOW2_OL_MAX_BITNR; i++) { - /* overlap-check defines a template bitmask, but every flag may be - * overwritten through the associated boolean option */ - s->overlap_check |= - qemu_opt_get_bool(opts, overlap_bool_option_names[i], - overlap_check_template & (1 << i)) << i; - } - - qemu_opts_del(opts); - opts = NULL; - - if (s->use_lazy_refcounts && s->qcow_version < 3) { - error_setg(errp, "Lazy refcounts require a qcow2 image with at least " - "qemu 1.1 compatibility level"); - ret = -EINVAL; - goto fail; - } - #ifdef DEBUG_ALLOC { BdrvCheckResult result = {0}; @@ -1005,7 +1169,6 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, return ret; fail: - qemu_opts_del(opts); g_free(s->unknown_header_fields); cleanup_unknown_header_ext(bs); qcow2_free_snapshots(bs); @@ -1013,6 +1176,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, qemu_vfree(s->l1_table); /* else pre-write overlap checks in cache_destroy may crash */ s->l1_table = NULL; + cache_clean_timer_del(bs); if (s->l2_table_cache) { qcow2_cache_destroy(bs, s->l2_table_cache); } @@ -1026,14 +1190,14 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; bs->bl.write_zeroes_alignment = s->cluster_sectors; } static int qcow2_set_key(BlockDriverState *bs, const char *key) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint8_t keybuf[16]; int len, i; Error *err = NULL; @@ -1066,32 +1230,104 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key) return 0; } -/* We have no actual commit/abort logic for qcow2, but we need to write out any - * unwritten data if we reopen read-only. */ static int qcow2_reopen_prepare(BDRVReopenState *state, BlockReopenQueue *queue, Error **errp) { + Qcow2ReopenState *r; int ret; + r = g_new0(Qcow2ReopenState, 1); + state->opaque = r; + + ret = qcow2_update_options_prepare(state->bs, r, state->options, + state->flags, errp); + if (ret < 0) { + goto fail; + } + + /* We need to write out any unwritten data if we reopen read-only. */ if ((state->flags & BDRV_O_RDWR) == 0) { ret = bdrv_flush(state->bs); if (ret < 0) { - return ret; + goto fail; } ret = qcow2_mark_clean(state->bs); if (ret < 0) { - return ret; + goto fail; } } return 0; + +fail: + qcow2_update_options_abort(state->bs, r); + g_free(r); + return ret; +} + +static void qcow2_reopen_commit(BDRVReopenState *state) +{ + qcow2_update_options_commit(state->bs, state->opaque); + g_free(state->opaque); +} + +static void qcow2_reopen_abort(BDRVReopenState *state) +{ + qcow2_update_options_abort(state->bs, state->opaque); + g_free(state->opaque); +} + +static void qcow2_join_options(QDict *options, QDict *old_options) +{ + bool has_new_overlap_template = + qdict_haskey(options, QCOW2_OPT_OVERLAP) || + qdict_haskey(options, QCOW2_OPT_OVERLAP_TEMPLATE); + bool has_new_total_cache_size = + qdict_haskey(options, QCOW2_OPT_CACHE_SIZE); + bool has_all_cache_options; + + /* New overlap template overrides all old overlap options */ + if (has_new_overlap_template) { + qdict_del(old_options, QCOW2_OPT_OVERLAP); + qdict_del(old_options, QCOW2_OPT_OVERLAP_TEMPLATE); + qdict_del(old_options, QCOW2_OPT_OVERLAP_MAIN_HEADER); + qdict_del(old_options, QCOW2_OPT_OVERLAP_ACTIVE_L1); + qdict_del(old_options, QCOW2_OPT_OVERLAP_ACTIVE_L2); + qdict_del(old_options, QCOW2_OPT_OVERLAP_REFCOUNT_TABLE); + qdict_del(old_options, QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK); + qdict_del(old_options, QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE); + qdict_del(old_options, QCOW2_OPT_OVERLAP_INACTIVE_L1); + qdict_del(old_options, QCOW2_OPT_OVERLAP_INACTIVE_L2); + } + + /* New total cache size overrides all old options */ + if (qdict_haskey(options, QCOW2_OPT_CACHE_SIZE)) { + qdict_del(old_options, QCOW2_OPT_L2_CACHE_SIZE); + qdict_del(old_options, QCOW2_OPT_REFCOUNT_CACHE_SIZE); + } + + qdict_join(options, old_options, false); + + /* + * If after merging all cache size options are set, an old total size is + * overwritten. Do keep all options, however, if all three are new. The + * resulting error message is what we want to happen. + */ + has_all_cache_options = + qdict_haskey(options, QCOW2_OPT_CACHE_SIZE) || + qdict_haskey(options, QCOW2_OPT_L2_CACHE_SIZE) || + qdict_haskey(options, QCOW2_OPT_REFCOUNT_CACHE_SIZE); + + if (has_all_cache_options && !has_new_total_cache_size) { + qdict_del(options, QCOW2_OPT_CACHE_SIZE); + } } static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, int *pnum) + int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t cluster_offset; int index_in_cluster, ret; int64_t status = 0; @@ -1108,6 +1344,7 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs, !s->cipher) { index_in_cluster = sector_num & (s->cluster_sectors - 1); cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); + *file = bs->file->bs; status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset; } if (ret == QCOW2_CLUSTER_ZERO) { @@ -1138,7 +1375,7 @@ int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, int remaining_sectors, QEMUIOVector *qiov) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int index_in_cluster, n1; int ret; int cur_nr_sectors; /* number of sectors in current iteration */ @@ -1175,9 +1412,9 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, switch (ret) { case QCOW2_CLUSTER_UNALLOCATED: - if (bs->backing_hd) { + if (bs->backing) { /* read from the base image */ - n1 = qcow2_backing_read1(bs->backing_hd, &hd_qiov, + n1 = qcow2_backing_read1(bs->backing->bs, &hd_qiov, sector_num, cur_nr_sectors); if (n1 > 0) { QEMUIOVector local_qiov; @@ -1188,7 +1425,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_readv(bs->backing_hd, sector_num, + ret = bdrv_co_readv(bs->backing->bs, sector_num, n1, &local_qiov); qemu_co_mutex_lock(&s->lock); @@ -1235,8 +1472,9 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, */ if (!cluster_data) { cluster_data = - qemu_try_blockalign(bs->file, QCOW_MAX_CRYPT_CLUSTERS - * s->cluster_size); + qemu_try_blockalign(bs->file->bs, + QCOW_MAX_CRYPT_CLUSTERS + * s->cluster_size); if (cluster_data == NULL) { ret = -ENOMEM; goto fail; @@ -1252,7 +1490,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_readv(bs->file, + ret = bdrv_co_readv(bs->file->bs, (cluster_offset >> 9) + index_in_cluster, cur_nr_sectors, &hd_qiov); qemu_co_mutex_lock(&s->lock); @@ -1300,7 +1538,7 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs, int remaining_sectors, QEMUIOVector *qiov) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int index_in_cluster; int ret; int cur_nr_sectors; /* number of sectors in current iteration */ @@ -1349,7 +1587,7 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs, Error *err = NULL; assert(s->cipher); if (!cluster_data) { - cluster_data = qemu_try_blockalign(bs->file, + cluster_data = qemu_try_blockalign(bs->file->bs, QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); if (cluster_data == NULL) { @@ -1386,7 +1624,7 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs, BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); trace_qcow2_writev_data(qemu_coroutine_self(), (cluster_offset >> 9) + index_in_cluster); - ret = bdrv_co_writev(bs->file, + ret = bdrv_co_writev(bs->file->bs, (cluster_offset >> 9) + index_in_cluster, cur_nr_sectors, &hd_qiov); qemu_co_mutex_lock(&s->lock); @@ -1444,33 +1682,44 @@ fail: return ret; } +static int qcow2_inactivate(BlockDriverState *bs) +{ + BDRVQcow2State *s = bs->opaque; + int ret, result = 0; + + ret = qcow2_cache_flush(bs, s->l2_table_cache); + if (ret) { + result = ret; + error_report("Failed to flush the L2 table cache: %s", + strerror(-ret)); + } + + ret = qcow2_cache_flush(bs, s->refcount_block_cache); + if (ret) { + result = ret; + error_report("Failed to flush the refcount block cache: %s", + strerror(-ret)); + } + + if (result == 0) { + qcow2_mark_clean(bs); + } + + return result; +} + static void qcow2_close(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; qemu_vfree(s->l1_table); /* else pre-write overlap checks in cache_destroy may crash */ s->l1_table = NULL; - if (!(bs->open_flags & BDRV_O_INCOMING)) { - int ret1, ret2; - - ret1 = qcow2_cache_flush(bs, s->l2_table_cache); - ret2 = qcow2_cache_flush(bs, s->refcount_block_cache); - - if (ret1) { - error_report("Failed to flush the L2 table cache: %s", - strerror(-ret1)); - } - if (ret2) { - error_report("Failed to flush the refcount block cache: %s", - strerror(-ret2)); - } - - if (!ret1 && !ret2) { - qcow2_mark_clean(bs); - } + if (!(s->flags & BDRV_O_INACTIVE)) { + qcow2_inactivate(bs); } + cache_clean_timer_del(bs); qcow2_cache_destroy(bs, s->l2_table_cache); qcow2_cache_destroy(bs, s->refcount_block_cache); @@ -1491,7 +1740,7 @@ static void qcow2_close(BlockDriverState *bs) static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int flags = s->flags; QCryptoCipher *cipher = NULL; QDict *options; @@ -1508,24 +1757,27 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp) qcow2_close(bs); - bdrv_invalidate_cache(bs->file, &local_err); + bdrv_invalidate_cache(bs->file->bs, &local_err); if (local_err) { error_propagate(errp, local_err); + bs->drv = NULL; return; } - memset(s, 0, sizeof(BDRVQcowState)); + memset(s, 0, sizeof(BDRVQcow2State)); options = qdict_clone_shallow(bs->options); + flags &= ~BDRV_O_INACTIVE; ret = qcow2_open(bs, options, flags, &local_err); QDECREF(options); if (local_err) { - error_setg(errp, "Could not reopen qcow2 layer: %s", - error_get_pretty(local_err)); - error_free(local_err); + error_propagate(errp, local_err); + error_prepend(errp, "Could not reopen qcow2 layer: "); + bs->drv = NULL; return; } else if (ret < 0) { error_setg_errno(errp, -ret, "Could not reopen qcow2 layer"); + bs->drv = NULL; return; } @@ -1561,7 +1813,7 @@ static size_t header_ext_add(char *buf, uint32_t magic, const void *s, */ int qcow2_update_header(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowHeader *header; char *buf; size_t buflen = s->cluster_size; @@ -1653,31 +1905,33 @@ int qcow2_update_header(BlockDriverState *bs) } /* Feature table */ - Qcow2Feature features[] = { - { - .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, - .bit = QCOW2_INCOMPAT_DIRTY_BITNR, - .name = "dirty bit", - }, - { - .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, - .bit = QCOW2_INCOMPAT_CORRUPT_BITNR, - .name = "corrupt bit", - }, - { - .type = QCOW2_FEAT_TYPE_COMPATIBLE, - .bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR, - .name = "lazy refcounts", - }, - }; + if (s->qcow_version >= 3) { + Qcow2Feature features[] = { + { + .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, + .bit = QCOW2_INCOMPAT_DIRTY_BITNR, + .name = "dirty bit", + }, + { + .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, + .bit = QCOW2_INCOMPAT_CORRUPT_BITNR, + .name = "corrupt bit", + }, + { + .type = QCOW2_FEAT_TYPE_COMPATIBLE, + .bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR, + .name = "lazy refcounts", + }, + }; - ret = header_ext_add(buf, QCOW2_EXT_MAGIC_FEATURE_TABLE, - features, sizeof(features), buflen); - if (ret < 0) { - goto fail; + ret = header_ext_add(buf, QCOW2_EXT_MAGIC_FEATURE_TABLE, + features, sizeof(features), buflen); + if (ret < 0) { + goto fail; + } + buf += ret; + buflen -= ret; } - buf += ret; - buflen -= ret; /* Keep unknown header extensions */ QLIST_FOREACH(uext, &s->unknown_header_ext, next) { @@ -1716,7 +1970,7 @@ int qcow2_update_header(BlockDriverState *bs) } /* Write the new header */ - ret = bdrv_pwrite(bs->file, 0, header, s->cluster_size); + ret = bdrv_pwrite(bs->file->bs, 0, header, s->cluster_size); if (ret < 0) { goto fail; } @@ -1730,7 +1984,11 @@ fail: static int qcow2_change_backing_file(BlockDriverState *bs, const char *backing_file, const char *backing_fmt) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; + + if (backing_file && strlen(backing_file) > 1023) { + return -EINVAL; + } pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: ""); pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: ""); @@ -1796,7 +2054,8 @@ static int preallocate(BlockDriverState *bs) if (host_offset != 0) { uint8_t buf[BDRV_SECTOR_SIZE]; memset(buf, 0, BDRV_SECTOR_SIZE); - ret = bdrv_write(bs->file, (host_offset >> BDRV_SECTOR_BITS) + num - 1, + ret = bdrv_write(bs->file->bs, + (host_offset >> BDRV_SECTOR_BITS) + num - 1, buf, 1); if (ret < 0) { return ret; @@ -1812,8 +2071,10 @@ static int qcow2_create2(const char *filename, int64_t total_size, QemuOpts *opts, int version, int refcount_order, Error **errp) { - /* Calculate cluster_bits */ int cluster_bits; + QDict *options; + + /* Calculate cluster_bits */ cluster_bits = ctz32(cluster_size); if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS || (1 << cluster_bits) != cluster_size) @@ -1835,7 +2096,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, * 2 GB for 64k clusters, and we don't want to have a 2 GB initial file * size for any qcow2 image. */ - BlockDriverState* bs; + BlockBackend *blk; QCowHeader *header; uint64_t* refcount_table; Error *local_err = NULL; @@ -1910,14 +2171,15 @@ static int qcow2_create2(const char *filename, int64_t total_size, return ret; } - bs = NULL; - ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, - NULL, &local_err); - if (ret < 0) { + blk = blk_new_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); + if (blk == NULL) { error_propagate(errp, local_err); - return ret; + return -EIO; } + blk_set_allow_write_beyond_eof(blk, true); + /* Write the header */ QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header)); header = g_malloc0(cluster_size); @@ -1945,7 +2207,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS); } - ret = bdrv_pwrite(bs, 0, header, cluster_size); + ret = blk_pwrite(blk, 0, header, cluster_size); g_free(header); if (ret < 0) { error_setg_errno(errp, -ret, "Could not write qcow2 header"); @@ -1955,7 +2217,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, /* Write a refcount table with one refcount block */ refcount_table = g_malloc0(2 * cluster_size); refcount_table[0] = cpu_to_be64(2 * cluster_size); - ret = bdrv_pwrite(bs, cluster_size, refcount_table, 2 * cluster_size); + ret = blk_pwrite(blk, cluster_size, refcount_table, 2 * cluster_size); g_free(refcount_table); if (ret < 0) { @@ -1963,23 +2225,25 @@ static int qcow2_create2(const char *filename, int64_t total_size, goto out; } - bdrv_unref(bs); - bs = NULL; + blk_unref(blk); + blk = NULL; /* * And now open the image and make it consistent first (i.e. increase the * refcount of the cluster that is occupied by the header and the refcount * table) */ - ret = bdrv_open(&bs, filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, - &bdrv_qcow2, &local_err); - if (ret < 0) { + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str("qcow2")); + blk = blk_new_open(filename, NULL, options, + BDRV_O_RDWR | BDRV_O_NO_FLUSH, &local_err); + if (blk == NULL) { error_propagate(errp, local_err); + ret = -EIO; goto out; } - ret = qcow2_alloc_clusters(bs, 3 * cluster_size); + ret = qcow2_alloc_clusters(blk_bs(blk), 3 * cluster_size); if (ret < 0) { error_setg_errno(errp, -ret, "Could not allocate clusters for qcow2 " "header and refcount table"); @@ -1990,8 +2254,15 @@ static int qcow2_create2(const char *filename, int64_t total_size, abort(); } + /* Create a full header (including things like feature table) */ + ret = qcow2_update_header(blk_bs(blk)); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not update qcow2 header"); + goto out; + } + /* Okay, now that we have a valid image, let's give it the right size */ - ret = bdrv_truncate(bs, total_size); + ret = blk_truncate(blk, total_size); if (ret < 0) { error_setg_errno(errp, -ret, "Could not resize image"); goto out; @@ -1999,7 +2270,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, /* Want a backing file? There you go.*/ if (backing_file) { - ret = bdrv_change_backing_file(bs, backing_file, backing_format); + ret = bdrv_change_backing_file(blk_bs(blk), backing_file, backing_format); if (ret < 0) { error_setg_errno(errp, -ret, "Could not assign backing file '%s' " "with format '%s'", backing_file, backing_format); @@ -2009,9 +2280,9 @@ static int qcow2_create2(const char *filename, int64_t total_size, /* And if we're supposed to preallocate metadata, do that now */ if (prealloc != PREALLOC_MODE_OFF) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = blk_bs(blk)->opaque; qemu_co_mutex_lock(&s->lock); - ret = preallocate(bs); + ret = preallocate(blk_bs(blk)); qemu_co_mutex_unlock(&s->lock); if (ret < 0) { error_setg_errno(errp, -ret, "Could not preallocate metadata"); @@ -2019,22 +2290,24 @@ static int qcow2_create2(const char *filename, int64_t total_size, } } - bdrv_unref(bs); - bs = NULL; + blk_unref(blk); + blk = NULL; /* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */ - ret = bdrv_open(&bs, filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING, - &bdrv_qcow2, &local_err); - if (local_err) { + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str("qcow2")); + blk = blk_new_open(filename, NULL, options, + BDRV_O_RDWR | BDRV_O_NO_BACKING, &local_err); + if (blk == NULL) { error_propagate(errp, local_err); + ret = -EIO; goto out; } ret = 0; out: - if (bs) { - bdrv_unref(bs); + if (blk) { + blk_unref(blk); } return ret; } @@ -2066,7 +2339,7 @@ static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp) DEFAULT_CLUSTER_SIZE); buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); prealloc = qapi_enum_parse(PreallocMode_lookup, buf, - PREALLOC_MODE_MAX, PREALLOC_MODE_OFF, + PREALLOC_MODE__MAX, PREALLOC_MODE_OFF, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -2142,7 +2415,7 @@ static coroutine_fn int qcow2_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, int nb_sectors, BdrvRequestFlags flags) { int ret; - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; /* Emulate misaligned zero writes */ if (sector_num % s->cluster_sectors || nb_sectors % s->cluster_sectors) { @@ -2162,7 +2435,7 @@ static coroutine_fn int qcow2_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) { int ret; - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; qemu_co_mutex_lock(&s->lock); ret = qcow2_discard_clusters(bs, sector_num << BDRV_SECTOR_BITS, @@ -2173,7 +2446,7 @@ static coroutine_fn int qcow2_co_discard(BlockDriverState *bs, static int qcow2_truncate(BlockDriverState *bs, int64_t offset) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t new_l1_size; int ret; @@ -2202,7 +2475,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset) /* write updated header.size */ offset = cpu_to_be64(offset); - ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size), + ret = bdrv_pwrite_sync(bs->file->bs, offsetof(QCowHeader, size), &offset, sizeof(uint64_t)); if (ret < 0) { return ret; @@ -2217,7 +2490,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset) static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; z_stream strm; int ret, out_len; uint8_t *out_buf; @@ -2226,8 +2499,8 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num, if (nb_sectors == 0) { /* align end of file to a sector boundary to ease reading with sector based I/Os */ - cluster_offset = bdrv_getlength(bs->file); - return bdrv_truncate(bs->file, cluster_offset); + cluster_offset = bdrv_getlength(bs->file->bs); + return bdrv_truncate(bs->file->bs, cluster_offset); } if (nb_sectors != s->cluster_sectors) { @@ -2294,7 +2567,7 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num, } BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED); - ret = bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len); + ret = bdrv_pwrite(bs->file->bs, cluster_offset, out_buf, out_len); if (ret < 0) { goto fail; } @@ -2308,7 +2581,7 @@ fail: static int make_completely_empty(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int ret, l1_clusters; int64_t offset; uint64_t *new_reftable = NULL; @@ -2343,7 +2616,7 @@ static int make_completely_empty(BlockDriverState *bs) /* After this call, neither the in-memory nor the on-disk refcount * information accurately describe the actual references */ - ret = bdrv_write_zeroes(bs->file, s->l1_table_offset / BDRV_SECTOR_SIZE, + ret = bdrv_write_zeroes(bs->file->bs, s->l1_table_offset / BDRV_SECTOR_SIZE, l1_clusters * s->cluster_sectors, 0); if (ret < 0) { goto fail_broken_refcounts; @@ -2357,7 +2630,7 @@ static int make_completely_empty(BlockDriverState *bs) * overwrite parts of the existing refcount and L1 table, which is not * an issue because the dirty flag is set, complete data loss is in fact * desired and partial data loss is consequently fine as well */ - ret = bdrv_write_zeroes(bs->file, s->cluster_size / BDRV_SECTOR_SIZE, + ret = bdrv_write_zeroes(bs->file->bs, s->cluster_size / BDRV_SECTOR_SIZE, (2 + l1_clusters) * s->cluster_size / BDRV_SECTOR_SIZE, 0); /* This call (even if it failed overall) may have overwritten on-disk @@ -2377,7 +2650,7 @@ static int make_completely_empty(BlockDriverState *bs) cpu_to_be64w(&l1_ofs_rt_ofs_cls.l1_offset, 3 * s->cluster_size); cpu_to_be64w(&l1_ofs_rt_ofs_cls.reftable_offset, s->cluster_size); cpu_to_be32w(&l1_ofs_rt_ofs_cls.reftable_clusters, 1); - ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_table_offset), + ret = bdrv_pwrite_sync(bs->file->bs, offsetof(QCowHeader, l1_table_offset), &l1_ofs_rt_ofs_cls, sizeof(l1_ofs_rt_ofs_cls)); if (ret < 0) { goto fail_broken_refcounts; @@ -2408,7 +2681,7 @@ static int make_completely_empty(BlockDriverState *bs) /* Enter the first refblock into the reftable */ rt_entry = cpu_to_be64(2 * s->cluster_size); - ret = bdrv_pwrite_sync(bs->file, s->cluster_size, + ret = bdrv_pwrite_sync(bs->file->bs, s->cluster_size, &rt_entry, sizeof(rt_entry)); if (ret < 0) { goto fail_broken_refcounts; @@ -2433,7 +2706,7 @@ static int make_completely_empty(BlockDriverState *bs) goto fail; } - ret = bdrv_truncate(bs->file, (3 + l1_clusters) * s->cluster_size); + ret = bdrv_truncate(bs->file->bs, (3 + l1_clusters) * s->cluster_size); if (ret < 0) { goto fail; } @@ -2456,7 +2729,7 @@ fail: static int qcow2_make_empty(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t start_sector; int sector_step = INT_MAX / BDRV_SECTOR_SIZE; int l1_clusters, ret = 0; @@ -2497,7 +2770,7 @@ static int qcow2_make_empty(BlockDriverState *bs) static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int ret; qemu_co_mutex_lock(&s->lock); @@ -2521,7 +2794,7 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs) static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; bdi->unallocated_blocks_are_zero = true; bdi->can_write_zeroes_with_unmap = (s->qcow_version >= 3); bdi->cluster_size = s->cluster_size; @@ -2531,22 +2804,20 @@ static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; ImageInfoSpecific *spec_info = g_new(ImageInfoSpecific, 1); *spec_info = (ImageInfoSpecific){ - .kind = IMAGE_INFO_SPECIFIC_KIND_QCOW2, - { - .qcow2 = g_new(ImageInfoSpecificQCow2, 1), - }, + .type = IMAGE_INFO_SPECIFIC_KIND_QCOW2, + .u.qcow2.data = g_new(ImageInfoSpecificQCow2, 1), }; if (s->qcow_version == 2) { - *spec_info->qcow2 = (ImageInfoSpecificQCow2){ + *spec_info->u.qcow2.data = (ImageInfoSpecificQCow2){ .compat = g_strdup("0.10"), .refcount_bits = s->refcount_bits, }; } else if (s->qcow_version == 3) { - *spec_info->qcow2 = (ImageInfoSpecificQCow2){ + *spec_info->u.qcow2.data = (ImageInfoSpecificQCow2){ .compat = g_strdup("1.1"), .lazy_refcounts = s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS, @@ -2556,6 +2827,10 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs) .has_corrupt = true, .refcount_bits = s->refcount_bits, }; + } else { + /* if this assertion fails, this probably means a new version was + * added without having it covered here */ + assert(false); } return spec_info; @@ -2564,11 +2839,11 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs) #if 0 static void dump_refcounts(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t nb_clusters, k, k1, size; int refcount; - size = bdrv_getlength(bs->file); + size = bdrv_getlength(bs->file->bs); nb_clusters = size_to_clusters(s, size); for(k = 0; k < nb_clusters;) { k1 = k; @@ -2585,7 +2860,7 @@ static void dump_refcounts(BlockDriverState *bs) static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t total_sectors = bs->total_sectors; bool zero_beyond_eof = bs->zero_beyond_eof; int ret; @@ -2606,7 +2881,7 @@ static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf, int64_t pos, int size) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; bool zero_beyond_eof = bs->zero_beyond_eof; int ret; @@ -2623,9 +2898,9 @@ static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf, * have to be removed. */ static int qcow2_downgrade(BlockDriverState *bs, int target_version, - BlockDriverAmendStatusCB *status_cb) + BlockDriverAmendStatusCB *status_cb, void *cb_opaque) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int current_version = s->qcow_version; int ret; @@ -2638,13 +2913,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, } if (s->refcount_order != 4) { - /* we would have to convert the image to a refcount_order == 4 image - * here; however, since qemu (at the time of writing this) does not - * support anything different than 4 anyway, there is no point in doing - * so right now; however, we should error out (if qemu supports this in - * the future and this code has not been adapted) */ - error_report("qcow2_downgrade: Image refcount orders other than 4 are " - "currently not supported."); + error_report("compat=0.10 requires refcount_bits=16"); return -ENOTSUP; } @@ -2672,7 +2941,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, /* clearing autoclear features is trivial */ s->autoclear_features = 0; - ret = qcow2_expand_zero_clusters(bs, status_cb); + ret = qcow2_expand_zero_clusters(bs, status_cb, cb_opaque); if (ret < 0) { return ret; } @@ -2686,10 +2955,81 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, return 0; } +typedef enum Qcow2AmendOperation { + /* This is the value Qcow2AmendHelperCBInfo::last_operation will be + * statically initialized to so that the helper CB can discern the first + * invocation from an operation change */ + QCOW2_NO_OPERATION = 0, + + QCOW2_CHANGING_REFCOUNT_ORDER, + QCOW2_DOWNGRADING, +} Qcow2AmendOperation; + +typedef struct Qcow2AmendHelperCBInfo { + /* The code coordinating the amend operations should only modify + * these four fields; the rest will be managed by the CB */ + BlockDriverAmendStatusCB *original_status_cb; + void *original_cb_opaque; + + Qcow2AmendOperation current_operation; + + /* Total number of operations to perform (only set once) */ + int total_operations; + + /* The following fields are managed by the CB */ + + /* Number of operations completed */ + int operations_completed; + + /* Cumulative offset of all completed operations */ + int64_t offset_completed; + + Qcow2AmendOperation last_operation; + int64_t last_work_size; +} Qcow2AmendHelperCBInfo; + +static void qcow2_amend_helper_cb(BlockDriverState *bs, + int64_t operation_offset, + int64_t operation_work_size, void *opaque) +{ + Qcow2AmendHelperCBInfo *info = opaque; + int64_t current_work_size; + int64_t projected_work_size; + + if (info->current_operation != info->last_operation) { + if (info->last_operation != QCOW2_NO_OPERATION) { + info->offset_completed += info->last_work_size; + info->operations_completed++; + } + + info->last_operation = info->current_operation; + } + + assert(info->total_operations > 0); + assert(info->operations_completed < info->total_operations); + + info->last_work_size = operation_work_size; + + current_work_size = info->offset_completed + operation_work_size; + + /* current_work_size is the total work size for (operations_completed + 1) + * operations (which includes this one), so multiply it by the number of + * operations not covered and divide it by the number of operations + * covered to get a projection for the operations not covered */ + projected_work_size = current_work_size * (info->total_operations - + info->operations_completed - 1) + / (info->operations_completed + 1); + + info->original_status_cb(bs, info->offset_completed + operation_offset, + current_work_size + projected_work_size, + info->original_cb_opaque); +} + static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, - BlockDriverAmendStatusCB *status_cb) + BlockDriverAmendStatusCB *status_cb, + void *cb_opaque) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int old_version = s->qcow_version, new_version = old_version; uint64_t new_size = 0; const char *backing_file = NULL, *backing_format = NULL; @@ -2697,8 +3037,10 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, const char *compat = NULL; uint64_t cluster_size = s->cluster_size; bool encrypt; + int refcount_bits = s->refcount_bits; int ret; QemuOptDesc *desc = opts->list->desc; + Qcow2AmendHelperCBInfo helper_cb_info; while (desc && desc->name) { if (!qemu_opt_find(opts, desc->name)) { @@ -2716,11 +3058,11 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, } else if (!strcmp(compat, "1.1")) { new_version = 3; } else { - fprintf(stderr, "Unknown compatibility level %s.\n", compat); + error_report("Unknown compatibility level %s", compat); return -EINVAL; } } else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) { - fprintf(stderr, "Cannot change preallocation mode.\n"); + error_report("Cannot change preallocation mode"); return -ENOTSUP; } else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) { new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0); @@ -2733,47 +3075,74 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, !!s->cipher); if (encrypt != !!s->cipher) { - fprintf(stderr, "Changing the encryption flag is not " - "supported.\n"); + error_report("Changing the encryption flag is not supported"); return -ENOTSUP; } } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) { cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE, cluster_size); if (cluster_size != s->cluster_size) { - fprintf(stderr, "Changing the cluster size is not " - "supported.\n"); + error_report("Changing the cluster size is not supported"); return -ENOTSUP; } } else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) { lazy_refcounts = qemu_opt_get_bool(opts, BLOCK_OPT_LAZY_REFCOUNTS, lazy_refcounts); } else if (!strcmp(desc->name, BLOCK_OPT_REFCOUNT_BITS)) { - error_report("Cannot change refcount entry width"); - return -ENOTSUP; + refcount_bits = qemu_opt_get_number(opts, BLOCK_OPT_REFCOUNT_BITS, + refcount_bits); + + if (refcount_bits <= 0 || refcount_bits > 64 || + !is_power_of_2(refcount_bits)) + { + error_report("Refcount width must be a power of two and may " + "not exceed 64 bits"); + return -EINVAL; + } } else { - /* if this assertion fails, this probably means a new option was + /* if this point is reached, this probably means a new option was * added without having it covered here */ - assert(false); + abort(); } desc++; } - if (new_version != old_version) { - if (new_version > old_version) { - /* Upgrade */ - s->qcow_version = new_version; - ret = qcow2_update_header(bs); - if (ret < 0) { - s->qcow_version = old_version; - return ret; - } - } else { - ret = qcow2_downgrade(bs, new_version, status_cb); - if (ret < 0) { - return ret; - } + helper_cb_info = (Qcow2AmendHelperCBInfo){ + .original_status_cb = status_cb, + .original_cb_opaque = cb_opaque, + .total_operations = (new_version < old_version) + + (s->refcount_bits != refcount_bits) + }; + + /* Upgrade first (some features may require compat=1.1) */ + if (new_version > old_version) { + s->qcow_version = new_version; + ret = qcow2_update_header(bs); + if (ret < 0) { + s->qcow_version = old_version; + return ret; + } + } + + if (s->refcount_bits != refcount_bits) { + int refcount_order = ctz32(refcount_bits); + Error *local_error = NULL; + + if (new_version < 3 && refcount_bits != 16) { + error_report("Different refcount widths than 16 bits require " + "compatibility level 1.1 or above (use compat=1.1 or " + "greater)"); + return -EINVAL; + } + + helper_cb_info.current_operation = QCOW2_CHANGING_REFCOUNT_ORDER; + ret = qcow2_change_refcount_order(bs, refcount_order, + &qcow2_amend_helper_cb, + &helper_cb_info, &local_error); + if (ret < 0) { + error_report_err(local_error); + return ret; } } @@ -2788,9 +3157,9 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, if (s->use_lazy_refcounts != lazy_refcounts) { if (lazy_refcounts) { - if (s->qcow_version < 3) { - fprintf(stderr, "Lazy refcounts only supported with compatibility " - "level 1.1 and above (use compat=1.1 or greater)\n"); + if (new_version < 3) { + error_report("Lazy refcounts only supported with compatibility " + "level 1.1 and above (use compat=1.1 or greater)"); return -EINVAL; } s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS; @@ -2824,6 +3193,16 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, } } + /* Downgrade last (so unsupported features can be removed before) */ + if (new_version < old_version) { + helper_cb_info.current_operation = QCOW2_DOWNGRADING; + ret = qcow2_downgrade(bs, new_version, &qcow2_amend_helper_cb, + &helper_cb_info); + if (ret < 0) { + return ret; + } + } + return 0; } @@ -2836,7 +3215,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, int64_t size, const char *message_format, ...) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; const char *node_name; char *message; va_list ap; @@ -2937,11 +3316,14 @@ static QemuOptsList qcow2_create_opts = { BlockDriver bdrv_qcow2 = { .format_name = "qcow2", - .instance_size = sizeof(BDRVQcowState), + .instance_size = sizeof(BDRVQcow2State), .bdrv_probe = qcow2_probe, .bdrv_open = qcow2_open, .bdrv_close = qcow2_close, .bdrv_reopen_prepare = qcow2_reopen_prepare, + .bdrv_reopen_commit = qcow2_reopen_commit, + .bdrv_reopen_abort = qcow2_reopen_abort, + .bdrv_join_options = qcow2_join_options, .bdrv_create = qcow2_create, .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_co_get_block_status = qcow2_co_get_block_status, @@ -2973,10 +3355,14 @@ BlockDriver bdrv_qcow2 = { .bdrv_refresh_limits = qcow2_refresh_limits, .bdrv_invalidate_cache = qcow2_invalidate_cache, + .bdrv_inactivate = qcow2_inactivate, .create_opts = &qcow2_create_opts, .bdrv_check = qcow2_check, .bdrv_amend_options = qcow2_amend_options, + + .bdrv_detach_aio_context = qcow2_detach_aio_context, + .bdrv_attach_aio_context = qcow2_attach_aio_context, }; static void bdrv_qcow2_init(void) diff --git a/qemu/block/qcow2.h b/qemu/block/qcow2.h index 72e132838..a063a3c1a 100644 --- a/qemu/block/qcow2.h +++ b/qemu/block/qcow2.h @@ -26,7 +26,7 @@ #define BLOCK_QCOW2_H #include "crypto/cipher.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" //#define DEBUG_ALLOC //#define DEBUG_ALLOC2 @@ -96,6 +96,7 @@ #define QCOW2_OPT_CACHE_SIZE "cache-size" #define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size" #define QCOW2_OPT_REFCOUNT_CACHE_SIZE "refcount-cache-size" +#define QCOW2_OPT_CACHE_CLEAN_INTERVAL "cache-clean-interval" typedef struct QCowHeader { uint32_t magic; @@ -221,7 +222,7 @@ typedef uint64_t Qcow2GetRefcountFunc(const void *refcount_array, typedef void Qcow2SetRefcountFunc(void *refcount_array, uint64_t index, uint64_t value); -typedef struct BDRVQcowState { +typedef struct BDRVQcow2State { int cluster_bits; int cluster_size; int cluster_sectors; @@ -239,6 +240,8 @@ typedef struct BDRVQcowState { Qcow2Cache* l2_table_cache; Qcow2Cache* refcount_block_cache; + QEMUTimer *cache_clean_timer; + unsigned cache_clean_interval; uint8_t *cluster_cache; uint8_t *cluster_data; @@ -290,9 +293,7 @@ typedef struct BDRVQcowState { * override) */ char *image_backing_file; char *image_backing_format; -} BDRVQcowState; - -struct QCowAIOCB; +} BDRVQcow2State; typedef struct Qcow2COWRegion { /** @@ -402,28 +403,28 @@ typedef enum QCow2MetadataOverlap { #define REFT_OFFSET_MASK 0xfffffffffffffe00ULL -static inline int64_t start_of_cluster(BDRVQcowState *s, int64_t offset) +static inline int64_t start_of_cluster(BDRVQcow2State *s, int64_t offset) { return offset & ~(s->cluster_size - 1); } -static inline int64_t offset_into_cluster(BDRVQcowState *s, int64_t offset) +static inline int64_t offset_into_cluster(BDRVQcow2State *s, int64_t offset) { return offset & (s->cluster_size - 1); } -static inline int size_to_clusters(BDRVQcowState *s, int64_t size) +static inline uint64_t size_to_clusters(BDRVQcow2State *s, uint64_t size) { return (size + (s->cluster_size - 1)) >> s->cluster_bits; } -static inline int64_t size_to_l1(BDRVQcowState *s, int64_t size) +static inline int64_t size_to_l1(BDRVQcow2State *s, int64_t size) { int shift = s->cluster_bits + s->l2_bits; return (size + (1ULL << shift) - 1) >> shift; } -static inline int offset_to_l2_index(BDRVQcowState *s, int64_t offset) +static inline int offset_to_l2_index(BDRVQcow2State *s, int64_t offset) { return (offset >> s->cluster_bits) & (s->l2_size - 1); } @@ -434,12 +435,12 @@ static inline int64_t align_offset(int64_t offset, int n) return offset; } -static inline int64_t qcow2_vm_state_offset(BDRVQcowState *s) +static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s) { return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); } -static inline uint64_t qcow2_max_refcount_clusters(BDRVQcowState *s) +static inline uint64_t qcow2_max_refcount_clusters(BDRVQcow2State *s) { return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits; } @@ -458,7 +459,7 @@ static inline int qcow2_get_cluster_type(uint64_t l2_entry) } /* Check whether refcounts are eager or lazy */ -static inline bool qcow2_need_accurate_refcounts(BDRVQcowState *s) +static inline bool qcow2_need_accurate_refcounts(BDRVQcow2State *s) { return !(s->incompatible_features & QCOW2_INCOMPAT_DIRTY); } @@ -506,8 +507,8 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, enum qcow2_discard_type type); int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size); -int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, - int nb_clusters); +int64_t qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, + int64_t nb_clusters); int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size); void qcow2_free_clusters(BlockDriverState *bs, int64_t offset, int64_t size, @@ -528,13 +529,17 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, int64_t size); +int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, + BlockDriverAmendStatusCB *status_cb, + void *cb_opaque, Error **errp); + /* qcow2-cluster.c functions */ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, bool exact_size); int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index); void qcow2_l2_cache_reset(BlockDriverState *bs); int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); -int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, +int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num, uint8_t *out_buf, const uint8_t *in_buf, int nb_sectors, bool enc, Error **errp); @@ -552,7 +557,8 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors); int qcow2_expand_zero_clusters(BlockDriverState *bs, - BlockDriverAmendStatusCB *status_cb); + BlockDriverAmendStatusCB *status_cb, + void *cb_opaque); /* qcow2-snapshot.c functions */ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); @@ -581,6 +587,7 @@ int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, Qcow2Cache *dependency); void qcow2_cache_depends_on_flush(Qcow2Cache *c); +void qcow2_cache_clean_unused(BlockDriverState *bs, Qcow2Cache *c); int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c); int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, diff --git a/qemu/block/qed-check.c b/qemu/block/qed-check.c index 36ecd290d..622f30897 100644 --- a/qemu/block/qed-check.c +++ b/qemu/block/qed-check.c @@ -11,6 +11,7 @@ * */ +#include "qemu/osdep.h" #include "qed.h" typedef struct { diff --git a/qemu/block/qed-cluster.c b/qemu/block/qed-cluster.c index f64b2af8f..c24e75616 100644 --- a/qemu/block/qed-cluster.c +++ b/qemu/block/qed-cluster.c @@ -12,6 +12,7 @@ * */ +#include "qemu/osdep.h" #include "qed.h" /** diff --git a/qemu/block/qed-gencb.c b/qemu/block/qed-gencb.c index b817a8bf5..faf8ecc84 100644 --- a/qemu/block/qed-gencb.c +++ b/qemu/block/qed-gencb.c @@ -11,6 +11,7 @@ * */ +#include "qemu/osdep.h" #include "qed.h" void *gencb_alloc(size_t len, BlockCompletionFunc *cb, void *opaque) diff --git a/qemu/block/qed-l2-cache.c b/qemu/block/qed-l2-cache.c index e9b2aae44..5cba79465 100644 --- a/qemu/block/qed-l2-cache.c +++ b/qemu/block/qed-l2-cache.c @@ -50,6 +50,7 @@ * table will be deleted in favor of the existing cache entry. */ +#include "qemu/osdep.h" #include "trace.h" #include "qed.h" diff --git a/qemu/block/qed-table.c b/qemu/block/qed-table.c index 513aa872c..802945f5e 100644 --- a/qemu/block/qed-table.c +++ b/qemu/block/qed-table.c @@ -12,6 +12,7 @@ * */ +#include "qemu/osdep.h" #include "trace.h" #include "qemu/sockets.h" /* for EINPROGRESS on Windows */ #include "qed.h" @@ -63,7 +64,7 @@ static void qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, read_table_cb->iov.iov_len = s->header.cluster_size * s->header.table_size, qemu_iovec_init_external(qiov, &read_table_cb->iov, 1); - bdrv_aio_readv(s->bs->file, offset / BDRV_SECTOR_SIZE, qiov, + bdrv_aio_readv(s->bs->file->bs, offset / BDRV_SECTOR_SIZE, qiov, qiov->size / BDRV_SECTOR_SIZE, qed_read_table_cb, read_table_cb); } @@ -152,7 +153,7 @@ static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, /* Adjust for offset into table */ offset += start * sizeof(uint64_t); - bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE, + bdrv_aio_writev(s->bs->file->bs, offset / BDRV_SECTOR_SIZE, &write_table_cb->qiov, write_table_cb->qiov.size / BDRV_SECTOR_SIZE, qed_write_table_cb, write_table_cb); diff --git a/qemu/block/qed.c b/qemu/block/qed.c index 954ed007c..0af52741d 100644 --- a/qemu/block/qed.c +++ b/qemu/block/qed.c @@ -12,11 +12,14 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/timer.h" #include "trace.h" #include "qed.h" #include "qapi/qmp/qerror.h" #include "migration/migration.h" +#include "sysemu/block-backend.h" static const AIOCBInfo qed_aiocb_info = { .aiocb_size = sizeof(QEDAIOCB), @@ -82,7 +85,7 @@ int qed_write_header_sync(BDRVQEDState *s) int ret; qed_header_cpu_to_le(&s->header, &le); - ret = bdrv_pwrite(s->bs->file, 0, &le, sizeof(le)); + ret = bdrv_pwrite(s->bs->file->bs, 0, &le, sizeof(le)); if (ret != sizeof(le)) { return ret; } @@ -119,7 +122,7 @@ static void qed_write_header_read_cb(void *opaque, int ret) /* Update header */ qed_header_cpu_to_le(&s->header, (QEDHeader *)write_header_cb->buf); - bdrv_aio_writev(s->bs->file, 0, &write_header_cb->qiov, + bdrv_aio_writev(s->bs->file->bs, 0, &write_header_cb->qiov, write_header_cb->nsectors, qed_write_header_cb, write_header_cb); } @@ -152,7 +155,7 @@ static void qed_write_header(BDRVQEDState *s, BlockCompletionFunc cb, write_header_cb->iov.iov_len = len; qemu_iovec_init_external(&write_header_cb->qiov, &write_header_cb->iov, 1); - bdrv_aio_readv(s->bs->file, 0, &write_header_cb->qiov, nsectors, + bdrv_aio_readv(s->bs->file->bs, 0, &write_header_cb->qiov, nsectors, qed_write_header_read_cb, write_header_cb); } @@ -344,7 +347,7 @@ static void qed_start_need_check_timer(BDRVQEDState *s) * migration. */ timer_mod(s->need_check_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - get_ticks_per_sec() * QED_NEED_CHECK_TIMEOUT); + NANOSECONDS_PER_SECOND * QED_NEED_CHECK_TIMEOUT); } /* It's okay to call this multiple times or when no timer is started */ @@ -354,12 +357,6 @@ static void qed_cancel_need_check_timer(BDRVQEDState *s) timer_del(s->need_check_timer); } -static void bdrv_qed_rebind(BlockDriverState *bs) -{ - BDRVQEDState *s = bs->opaque; - s->bs = bs; -} - static void bdrv_qed_detach_aio_context(BlockDriverState *bs) { BDRVQEDState *s = bs->opaque; @@ -392,7 +389,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, s->bs = bs; QSIMPLEQ_INIT(&s->allocating_write_reqs); - ret = bdrv_pread(bs->file, 0, &le_header, sizeof(le_header)); + ret = bdrv_pread(bs->file->bs, 0, &le_header, sizeof(le_header)); if (ret < 0) { return ret; } @@ -404,11 +401,8 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, } if (s->header.features & ~QED_FEATURE_MASK) { /* image uses unsupported feature bits */ - char buf[64]; - snprintf(buf, sizeof(buf), "%" PRIx64, - s->header.features & ~QED_FEATURE_MASK); - error_setg(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, - bdrv_get_device_or_node_name(bs), "QED", buf); + error_setg(errp, "Unsupported QED features: %" PRIx64, + s->header.features & ~QED_FEATURE_MASK); return -ENOTSUP; } if (!qed_is_cluster_size_valid(s->header.cluster_size)) { @@ -416,7 +410,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, } /* Round down file size to the last cluster */ - file_size = bdrv_getlength(bs->file); + file_size = bdrv_getlength(bs->file->bs); if (file_size < 0) { return file_size; } @@ -452,7 +446,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - ret = qed_read_string(bs->file, s->header.backing_filename_offset, + ret = qed_read_string(bs->file->bs, s->header.backing_filename_offset, s->header.backing_filename_size, bs->backing_file, sizeof(bs->backing_file)); if (ret < 0) { @@ -471,7 +465,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, * feature is no longer valid. */ if ((s->header.autoclear_features & ~QED_AUTOCLEAR_FEATURE_MASK) != 0 && - !bdrv_is_read_only(bs->file) && !(flags & BDRV_O_INCOMING)) { + !bdrv_is_read_only(bs->file->bs) && !(flags & BDRV_O_INACTIVE)) { s->header.autoclear_features &= QED_AUTOCLEAR_FEATURE_MASK; ret = qed_write_header_sync(s); @@ -480,7 +474,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, } /* From here on only known autoclear feature bits are valid */ - bdrv_flush(bs->file); + bdrv_flush(bs->file->bs); } s->l1_table = qed_alloc_table(s); @@ -498,8 +492,8 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, * potentially inconsistent images to be opened read-only. This can * aid data recovery from an otherwise inconsistent image. */ - if (!bdrv_is_read_only(bs->file) && - !(flags & BDRV_O_INCOMING)) { + if (!bdrv_is_read_only(bs->file->bs) && + !(flags & BDRV_O_INACTIVE)) { BdrvCheckResult result = {0}; ret = qed_check(s, &result, true); @@ -541,7 +535,7 @@ static void bdrv_qed_close(BlockDriverState *bs) bdrv_qed_detach_aio_context(bs); /* Ensure writes reach stable storage */ - bdrv_flush(bs->file); + bdrv_flush(bs->file->bs); /* Clean shutdown, no check required on next open */ if (s->header.features & QED_F_NEED_CHECK) { @@ -573,7 +567,7 @@ static int qed_create(const char *filename, uint32_t cluster_size, size_t l1_size = header.cluster_size * header.table_size; Error *local_err = NULL; int ret = 0; - BlockDriverState *bs; + BlockBackend *blk; ret = bdrv_create_file(filename, opts, &local_err); if (ret < 0) { @@ -581,17 +575,17 @@ static int qed_create(const char *filename, uint32_t cluster_size, return ret; } - bs = NULL; - ret = bdrv_open(&bs, filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, NULL, - &local_err); - if (ret < 0) { + blk = blk_new_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); + if (blk == NULL) { error_propagate(errp, local_err); - return ret; + return -EIO; } + blk_set_allow_write_beyond_eof(blk, true); + /* File must start empty and grow, check truncate is supported */ - ret = bdrv_truncate(bs, 0); + ret = blk_truncate(blk, 0); if (ret < 0) { goto out; } @@ -607,18 +601,18 @@ static int qed_create(const char *filename, uint32_t cluster_size, } qed_header_cpu_to_le(&header, &le_header); - ret = bdrv_pwrite(bs, 0, &le_header, sizeof(le_header)); + ret = blk_pwrite(blk, 0, &le_header, sizeof(le_header)); if (ret < 0) { goto out; } - ret = bdrv_pwrite(bs, sizeof(le_header), backing_file, - header.backing_filename_size); + ret = blk_pwrite(blk, sizeof(le_header), backing_file, + header.backing_filename_size); if (ret < 0) { goto out; } l1_table = g_malloc0(l1_size); - ret = bdrv_pwrite(bs, header.l1_table_offset, l1_table, l1_size); + ret = blk_pwrite(blk, header.l1_table_offset, l1_table, l1_size); if (ret < 0) { goto out; } @@ -626,7 +620,7 @@ static int qed_create(const char *filename, uint32_t cluster_size, ret = 0; /* success */ out: g_free(l1_table); - bdrv_unref(bs); + blk_unref(blk); return ret; } @@ -686,6 +680,7 @@ typedef struct { uint64_t pos; int64_t status; int *pnum; + BlockDriverState **file; } QEDIsAllocatedCB; static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t len) @@ -697,6 +692,7 @@ static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t l case QED_CLUSTER_FOUND: offset |= qed_offset_into_cluster(s, cb->pos); cb->status = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset; + *cb->file = cb->bs->file->bs; break; case QED_CLUSTER_ZERO: cb->status = BDRV_BLOCK_ZERO; @@ -718,7 +714,8 @@ static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t l static int64_t coroutine_fn bdrv_qed_co_get_block_status(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int *pnum) + int nb_sectors, int *pnum, + BlockDriverState **file) { BDRVQEDState *s = bs->opaque; size_t len = (size_t)nb_sectors * BDRV_SECTOR_SIZE; @@ -727,6 +724,7 @@ static int64_t coroutine_fn bdrv_qed_co_get_block_status(BlockDriverState *bs, .pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE, .status = BDRV_BLOCK_OFFSET_MASK, .pnum = pnum, + .file = file, }; QEDRequest request = { .l2_table = NULL }; @@ -772,8 +770,8 @@ static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos, /* If there is a backing file, get its length. Treat the absence of a * backing file like a zero length backing file. */ - if (s->bs->backing_hd) { - int64_t l = bdrv_getlength(s->bs->backing_hd); + if (s->bs->backing) { + int64_t l = bdrv_getlength(s->bs->backing->bs); if (l < 0) { cb(opaque, l); return; @@ -802,7 +800,7 @@ static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos, qemu_iovec_concat(*backing_qiov, qiov, 0, size); BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING_AIO); - bdrv_aio_readv(s->bs->backing_hd, pos / BDRV_SECTOR_SIZE, + bdrv_aio_readv(s->bs->backing->bs, pos / BDRV_SECTOR_SIZE, *backing_qiov, size / BDRV_SECTOR_SIZE, cb, opaque); } @@ -839,7 +837,7 @@ static void qed_copy_from_backing_file_write(void *opaque, int ret) } BLKDBG_EVENT(s->bs->file, BLKDBG_COW_WRITE); - bdrv_aio_writev(s->bs->file, copy_cb->offset / BDRV_SECTOR_SIZE, + bdrv_aio_writev(s->bs->file->bs, copy_cb->offset / BDRV_SECTOR_SIZE, ©_cb->qiov, copy_cb->qiov.size / BDRV_SECTOR_SIZE, qed_copy_from_backing_file_cb, copy_cb); } @@ -1055,7 +1053,7 @@ static void qed_aio_write_flush_before_l2_update(void *opaque, int ret) QEDAIOCB *acb = opaque; BDRVQEDState *s = acb_to_s(acb); - if (!bdrv_aio_flush(s->bs->file, qed_aio_write_l2_update_cb, opaque)) { + if (!bdrv_aio_flush(s->bs->file->bs, qed_aio_write_l2_update_cb, opaque)) { qed_aio_complete(acb, -EIO); } } @@ -1081,7 +1079,7 @@ static void qed_aio_write_main(void *opaque, int ret) if (acb->find_cluster_ret == QED_CLUSTER_FOUND) { next_fn = qed_aio_next_io; } else { - if (s->bs->backing_hd) { + if (s->bs->backing) { next_fn = qed_aio_write_flush_before_l2_update; } else { next_fn = qed_aio_write_l2_update_cb; @@ -1089,7 +1087,7 @@ static void qed_aio_write_main(void *opaque, int ret) } BLKDBG_EVENT(s->bs->file, BLKDBG_WRITE_AIO); - bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE, + bdrv_aio_writev(s->bs->file->bs, offset / BDRV_SECTOR_SIZE, &acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE, next_fn, acb); } @@ -1139,7 +1137,7 @@ static void qed_aio_write_prefill(void *opaque, int ret) static bool qed_should_set_need_check(BDRVQEDState *s) { /* The flush before L2 update path ensures consistency */ - if (s->bs->backing_hd) { + if (s->bs->backing) { return false; } @@ -1321,7 +1319,7 @@ static void qed_aio_read_data(void *opaque, int ret, } BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); - bdrv_aio_readv(bs->file, offset / BDRV_SECTOR_SIZE, + bdrv_aio_readv(bs->file->bs, offset / BDRV_SECTOR_SIZE, &acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE, qed_aio_next_io, acb); return; @@ -1443,7 +1441,7 @@ static int coroutine_fn bdrv_qed_co_write_zeroes(BlockDriverState *bs, struct iovec iov; /* Refuse if there are untouched backing file sectors */ - if (bs->backing_hd) { + if (bs->backing) { if (qed_offset_into_cluster(s, sector_num * BDRV_SECTOR_SIZE) != 0) { return -ENOTSUP; } @@ -1580,7 +1578,7 @@ static int bdrv_qed_change_backing_file(BlockDriverState *bs, } /* Write new header */ - ret = bdrv_pwrite_sync(bs->file, 0, buffer, buffer_len); + ret = bdrv_pwrite_sync(bs->file->bs, 0, buffer, buffer_len); g_free(buffer); if (ret == 0) { memcpy(&s->header, &new_header, sizeof(new_header)); @@ -1596,7 +1594,7 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp) bdrv_qed_close(bs); - bdrv_invalidate_cache(bs->file, &local_err); + bdrv_invalidate_cache(bs->file->bs, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -1605,9 +1603,8 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp) memset(s, 0, sizeof(BDRVQEDState)); ret = bdrv_qed_open(bs, NULL, bs->open_flags, &local_err); if (local_err) { - error_setg(errp, "Could not reopen qed layer: %s", - error_get_pretty(local_err)); - error_free(local_err); + error_propagate(errp, local_err); + error_prepend(errp, "Could not reopen qed layer: "); return; } else if (ret < 0) { error_setg_errno(errp, -ret, "Could not reopen qed layer"); @@ -1664,7 +1661,6 @@ static BlockDriver bdrv_qed = { .supports_backing = true, .bdrv_probe = bdrv_qed_probe, - .bdrv_rebind = bdrv_qed_rebind, .bdrv_open = bdrv_qed_open, .bdrv_close = bdrv_qed_close, .bdrv_reopen_prepare = bdrv_qed_reopen_prepare, diff --git a/qemu/block/qed.h b/qemu/block/qed.h index 615e676fc..22b319875 100644 --- a/qemu/block/qed.h +++ b/qemu/block/qed.h @@ -16,6 +16,7 @@ #define BLOCK_QED_H #include "block/block_int.h" +#include "qemu/cutils.h" /* The layout of a QED file is as follows: * diff --git a/qemu/block/quorum.c b/qemu/block/quorum.c index 2f6c45f76..da15465a9 100644 --- a/qemu/block/quorum.c +++ b/qemu/block/quorum.c @@ -13,6 +13,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "block/block_int.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qdict.h" @@ -64,7 +65,7 @@ typedef struct QuorumVotes { /* the following structure holds the state of one quorum instance */ typedef struct BDRVQuorumState { - BlockDriverState **bs; /* children BlockDriverStates */ + BdrvChild **children; /* children BlockDriverStates */ int num_children; /* children count */ int threshold; /* if less than threshold children reads gave the * same result a quorum error occurs. @@ -214,14 +215,16 @@ static QuorumAIOCB *quorum_aio_get(BDRVQuorumState *s, return acb; } -static void quorum_report_bad(QuorumAIOCB *acb, char *node_name, int ret) +static void quorum_report_bad(QuorumOpType type, uint64_t sector_num, + int nb_sectors, char *node_name, int ret) { const char *msg = NULL; if (ret < 0) { msg = strerror(-ret); } - qapi_event_send_quorum_report_bad(!!msg, msg, node_name, - acb->sector_num, acb->nb_sectors, &error_abort); + + qapi_event_send_quorum_report_bad(type, !!msg, msg, node_name, + sector_num, nb_sectors, &error_abort); } static void quorum_report_failure(QuorumAIOCB *acb) @@ -283,9 +286,19 @@ static void quorum_aio_cb(void *opaque, int ret) BDRVQuorumState *s = acb->common.bs->opaque; bool rewrite = false; + if (ret == 0) { + acb->success_count++; + } else { + QuorumOpType type; + type = acb->is_read ? QUORUM_OP_TYPE_READ : QUORUM_OP_TYPE_WRITE; + quorum_report_bad(type, acb->sector_num, acb->nb_sectors, + sacb->aiocb->bs->node_name, ret); + } + if (acb->is_read && s->read_pattern == QUORUM_READ_PATTERN_FIFO) { /* We try to read next child in FIFO order if we fail to read */ - if (ret < 0 && ++acb->child_iter < s->num_children) { + if (ret < 0 && (acb->child_iter + 1) < s->num_children) { + acb->child_iter++; read_fifo_child(acb); return; } @@ -300,11 +313,6 @@ static void quorum_aio_cb(void *opaque, int ret) sacb->ret = ret; acb->count++; - if (ret == 0) { - acb->success_count++; - } else { - quorum_report_bad(acb, sacb->aiocb->bs->node_name, ret); - } assert(acb->count <= s->num_children); assert(acb->success_count <= s->num_children); if (acb->count < s->num_children) { @@ -336,7 +344,9 @@ static void quorum_report_bad_versions(BDRVQuorumState *s, continue; } QLIST_FOREACH(item, &version->items, next) { - quorum_report_bad(acb, s->bs[item->index]->node_name, 0); + quorum_report_bad(QUORUM_OP_TYPE_READ, acb->sector_num, + acb->nb_sectors, + s->children[item->index]->bs->node_name, 0); } } } @@ -369,8 +379,9 @@ static bool quorum_rewrite_bad_versions(BDRVQuorumState *s, QuorumAIOCB *acb, continue; } QLIST_FOREACH(item, &version->items, next) { - bdrv_aio_writev(s->bs[item->index], acb->sector_num, acb->qiov, - acb->nb_sectors, quorum_rewrite_aio_cb, acb); + bdrv_aio_writev(s->children[item->index]->bs, acb->sector_num, + acb->qiov, acb->nb_sectors, quorum_rewrite_aio_cb, + acb); } } @@ -639,14 +650,15 @@ static BlockAIOCB *read_quorum_children(QuorumAIOCB *acb) int i; for (i = 0; i < s->num_children; i++) { - acb->qcrs[i].buf = qemu_blockalign(s->bs[i], acb->qiov->size); + acb->qcrs[i].buf = qemu_blockalign(s->children[i]->bs, acb->qiov->size); qemu_iovec_init(&acb->qcrs[i].qiov, acb->qiov->niov); qemu_iovec_clone(&acb->qcrs[i].qiov, acb->qiov, acb->qcrs[i].buf); } for (i = 0; i < s->num_children; i++) { - bdrv_aio_readv(s->bs[i], acb->sector_num, &acb->qcrs[i].qiov, - acb->nb_sectors, quorum_aio_cb, &acb->qcrs[i]); + acb->qcrs[i].aiocb = bdrv_aio_readv(s->children[i]->bs, acb->sector_num, + &acb->qcrs[i].qiov, acb->nb_sectors, + quorum_aio_cb, &acb->qcrs[i]); } return &acb->common; @@ -656,14 +668,15 @@ static BlockAIOCB *read_fifo_child(QuorumAIOCB *acb) { BDRVQuorumState *s = acb->common.bs->opaque; - acb->qcrs[acb->child_iter].buf = qemu_blockalign(s->bs[acb->child_iter], - acb->qiov->size); + acb->qcrs[acb->child_iter].buf = + qemu_blockalign(s->children[acb->child_iter]->bs, acb->qiov->size); qemu_iovec_init(&acb->qcrs[acb->child_iter].qiov, acb->qiov->niov); qemu_iovec_clone(&acb->qcrs[acb->child_iter].qiov, acb->qiov, acb->qcrs[acb->child_iter].buf); - bdrv_aio_readv(s->bs[acb->child_iter], acb->sector_num, - &acb->qcrs[acb->child_iter].qiov, acb->nb_sectors, - quorum_aio_cb, &acb->qcrs[acb->child_iter]); + acb->qcrs[acb->child_iter].aiocb = + bdrv_aio_readv(s->children[acb->child_iter]->bs, acb->sector_num, + &acb->qcrs[acb->child_iter].qiov, acb->nb_sectors, + quorum_aio_cb, &acb->qcrs[acb->child_iter]); return &acb->common; } @@ -702,8 +715,8 @@ static BlockAIOCB *quorum_aio_writev(BlockDriverState *bs, int i; for (i = 0; i < s->num_children; i++) { - acb->qcrs[i].aiocb = bdrv_aio_writev(s->bs[i], sector_num, qiov, - nb_sectors, &quorum_aio_cb, + acb->qcrs[i].aiocb = bdrv_aio_writev(s->children[i]->bs, sector_num, + qiov, nb_sectors, &quorum_aio_cb, &acb->qcrs[i]); } @@ -717,12 +730,12 @@ static int64_t quorum_getlength(BlockDriverState *bs) int i; /* check that all file have the same length */ - result = bdrv_getlength(s->bs[0]); + result = bdrv_getlength(s->children[0]->bs); if (result < 0) { return result; } for (i = 1; i < s->num_children; i++) { - int64_t value = bdrv_getlength(s->bs[i]); + int64_t value = bdrv_getlength(s->children[i]->bs); if (value < 0) { return value; } @@ -741,7 +754,7 @@ static void quorum_invalidate_cache(BlockDriverState *bs, Error **errp) int i; for (i = 0; i < s->num_children; i++) { - bdrv_invalidate_cache(s->bs[i], &local_err); + bdrv_invalidate_cache(s->children[i]->bs, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -757,19 +770,30 @@ static coroutine_fn int quorum_co_flush(BlockDriverState *bs) QuorumVoteValue result_value; int i; int result = 0; + int success_count = 0; QLIST_INIT(&error_votes.vote_list); error_votes.compare = quorum_64bits_compare; for (i = 0; i < s->num_children; i++) { - result = bdrv_co_flush(s->bs[i]); - result_value.l = result; - quorum_count_vote(&error_votes, &result_value, i); + result = bdrv_co_flush(s->children[i]->bs); + if (result) { + quorum_report_bad(QUORUM_OP_TYPE_FLUSH, 0, + bdrv_nb_sectors(s->children[i]->bs), + s->children[i]->bs->node_name, result); + result_value.l = result; + quorum_count_vote(&error_votes, &result_value, i); + } else { + success_count++; + } } - winner = quorum_get_vote_winner(&error_votes); - result = winner->value.l; - + if (success_count >= s->threshold) { + result = 0; + } else { + winner = quorum_get_vote_winner(&error_votes); + result = winner->value.l; + } quorum_free_vote_list(&error_votes); return result; @@ -782,7 +806,7 @@ static bool quorum_recurse_is_first_non_filter(BlockDriverState *bs, int i; for (i = 0; i < s->num_children; i++) { - bool perm = bdrv_recurse_is_first_non_filter(s->bs[i], + bool perm = bdrv_recurse_is_first_non_filter(s->children[i]->bs, candidate); if (perm) { return true; @@ -846,7 +870,7 @@ static int parse_read_pattern(const char *opt) return QUORUM_READ_PATTERN_QUORUM; } - for (i = 0; i < QUORUM_READ_PATTERN_MAX; i++) { + for (i = 0; i < QUORUM_READ_PATTERN__MAX; i++) { if (!strcmp(opt, QuorumReadPattern_lookup[i])) { return i; } @@ -889,6 +913,12 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, } s->threshold = qemu_opt_get_number(opts, QUORUM_OPT_VOTE_THRESHOLD, 0); + /* and validate it against s->num_children */ + ret = quorum_valid_threshold(s->threshold, s->num_children, &local_err); + if (ret < 0) { + goto exit; + } + ret = parse_read_pattern(qemu_opt_get(opts, QUORUM_OPT_READ_PATTERN)); if (ret < 0) { error_setg(&local_err, "Please set read-pattern as fifo or quorum"); @@ -897,12 +927,6 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, s->read_pattern = ret; if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) { - /* and validate it against s->num_children */ - ret = quorum_valid_threshold(s->threshold, s->num_children, &local_err); - if (ret < 0) { - goto exit; - } - /* is the driver in blkverify mode */ if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false) && s->num_children == 2 && s->threshold == 2) { @@ -922,8 +946,8 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, } } - /* allocate the children BlockDriverState array */ - s->bs = g_new0(BlockDriverState *, s->num_children); + /* allocate the children array */ + s->children = g_new0(BdrvChild *, s->num_children); opened = g_new0(bool, s->num_children); for (i = 0; i < s->num_children; i++) { @@ -931,9 +955,10 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, ret = snprintf(indexstr, 32, "children.%d", i); assert(ret < 32); - ret = bdrv_open_image(&s->bs[i], NULL, options, indexstr, bs, - &child_format, false, &local_err); - if (ret < 0) { + s->children[i] = bdrv_open_child(NULL, options, indexstr, bs, + &child_format, false, &local_err); + if (local_err) { + ret = -EINVAL; goto close_exit; } @@ -949,9 +974,9 @@ close_exit: if (!opened[i]) { continue; } - bdrv_unref(s->bs[i]); + bdrv_unref_child(bs, s->children[i]); } - g_free(s->bs); + g_free(s->children); g_free(opened); exit: qemu_opts_del(opts); @@ -968,10 +993,10 @@ static void quorum_close(BlockDriverState *bs) int i; for (i = 0; i < s->num_children; i++) { - bdrv_unref(s->bs[i]); + bdrv_unref_child(bs, s->children[i]); } - g_free(s->bs); + g_free(s->children); } static void quorum_detach_aio_context(BlockDriverState *bs) @@ -980,7 +1005,7 @@ static void quorum_detach_aio_context(BlockDriverState *bs) int i; for (i = 0; i < s->num_children; i++) { - bdrv_detach_aio_context(s->bs[i]); + bdrv_detach_aio_context(s->children[i]->bs); } } @@ -991,11 +1016,11 @@ static void quorum_attach_aio_context(BlockDriverState *bs, int i; for (i = 0; i < s->num_children; i++) { - bdrv_attach_aio_context(s->bs[i], new_context); + bdrv_attach_aio_context(s->children[i]->bs, new_context); } } -static void quorum_refresh_filename(BlockDriverState *bs) +static void quorum_refresh_filename(BlockDriverState *bs, QDict *options) { BDRVQuorumState *s = bs->opaque; QDict *opts; @@ -1003,16 +1028,17 @@ static void quorum_refresh_filename(BlockDriverState *bs) int i; for (i = 0; i < s->num_children; i++) { - bdrv_refresh_filename(s->bs[i]); - if (!s->bs[i]->full_open_options) { + bdrv_refresh_filename(s->children[i]->bs); + if (!s->children[i]->bs->full_open_options) { return; } } children = qlist_new(); for (i = 0; i < s->num_children; i++) { - QINCREF(s->bs[i]->full_open_options); - qlist_append_obj(children, QOBJECT(s->bs[i]->full_open_options)); + QINCREF(s->children[i]->bs->full_open_options); + qlist_append_obj(children, + QOBJECT(s->children[i]->bs->full_open_options)); } opts = qdict_new(); diff --git a/qemu/block/raw-aio.h b/qemu/block/raw-aio.h index 31d791fe6..811e37501 100644 --- a/qemu/block/raw-aio.h +++ b/qemu/block/raw-aio.h @@ -15,6 +15,8 @@ #ifndef QEMU_RAW_AIO_H #define QEMU_RAW_AIO_H +#include "qemu/iov.h" + /* AIO request types */ #define QEMU_AIO_READ 0x0001 #define QEMU_AIO_WRITE 0x0002 diff --git a/qemu/block/raw-posix.c b/qemu/block/raw-posix.c index 855febed5..906d5c941 100644 --- a/qemu/block/raw-posix.c +++ b/qemu/block/raw-posix.c @@ -21,7 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/timer.h" #include "qemu/log.h" @@ -43,6 +45,7 @@ #include #include //#include +#include #include #endif @@ -51,8 +54,6 @@ #include #endif #ifdef __linux__ -#include -#include #include #include #include @@ -127,11 +128,6 @@ do { \ #define FTYPE_FILE 0 #define FTYPE_CD 1 -#define FTYPE_FD 2 - -/* if the FD is not accessed during that time (in ns), we try to - reopen it to see if the disk has been changed */ -#define FD_OPEN_TIMEOUT (1000000000) #define MAX_BLOCKSIZE 4096 @@ -141,13 +137,6 @@ typedef struct BDRVRawState { int open_flags; size_t buf_align; -#if defined(__linux__) - /* linux floppy specific */ - int64_t fd_open_time; - int64_t fd_error_time; - int fd_got_error; - int fd_media_changed; -#endif #ifdef CONFIG_LINUX_AIO int use_aio; void *aio_ctx; @@ -512,14 +501,19 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, goto fail; } if (!s->use_aio && (bdrv_flags & BDRV_O_NATIVE_AIO)) { - error_printf("WARNING: aio=native was specified for '%s', but " - "it requires cache.direct=on, which was not " - "specified. Falling back to aio=threads.\n" - " This will become an error condition in " - "future QEMU versions.\n", - bs->filename); + error_setg(errp, "aio=native was specified, but it requires " + "cache.direct=on, which was not specified."); + ret = -EINVAL; + goto fail; } -#endif +#else + if (bdrv_flags & BDRV_O_NATIVE_AIO) { + error_setg(errp, "aio=native was specified, but is not supported " + "in this build."); + ret = -EINVAL; + goto fail; + } +#endif /* !defined(CONFIG_LINUX_AIO) */ s->has_discard = true; s->has_write_zeroes = true; @@ -626,7 +620,7 @@ static int raw_reopen_prepare(BDRVReopenState *state, } #endif - if (s->type == FTYPE_FD || s->type == FTYPE_CD) { + if (s->type == FTYPE_CD) { raw_s->open_flags |= O_NONBLOCK; } @@ -670,11 +664,17 @@ static int raw_reopen_prepare(BDRVReopenState *state, /* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */ if (raw_s->fd == -1) { - assert(!(raw_s->open_flags & O_CREAT)); - raw_s->fd = qemu_open(state->bs->filename, raw_s->open_flags); - if (raw_s->fd == -1) { - error_setg_errno(errp, errno, "Could not reopen file"); - ret = -1; + const char *normalized_filename = state->bs->filename; + ret = raw_normalize_devicepath(&normalized_filename); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not normalize device path"); + } else { + assert(!(raw_s->open_flags & O_CREAT)); + raw_s->fd = qemu_open(normalized_filename, raw_s->open_flags); + if (raw_s->fd == -1) { + error_setg_errno(errp, errno, "Could not reopen file"); + ret = -1; + } } } @@ -780,7 +780,6 @@ static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo) { BDRVRawState *s = bs->opaque; struct hd_geometry ioctl_geo = {0}; - uint32_t blksize; /* If DASD, get its geometry */ if (check_for_dasd(s->fd) < 0) { @@ -800,12 +799,6 @@ static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo) } geo->heads = ioctl_geo.heads; geo->sectors = ioctl_geo.sectors; - if (!probe_physical_blocksize(s->fd, &blksize)) { - /* overwrite cyls: HDIO_GETGEO result is incorrect for big drives */ - geo->cylinders = bdrv_nb_sectors(bs) / (blksize / BDRV_SECTOR_SIZE) - / (geo->heads * geo->sectors); - return 0; - } geo->cylinders = ioctl_geo.cylinders; return 0; @@ -1253,7 +1246,7 @@ static int aio_worker(void *arg) break; } - g_slice_free(RawPosixAIOData, aiocb); + g_free(aiocb); return ret; } @@ -1261,7 +1254,7 @@ static int paio_submit_co(BlockDriverState *bs, int fd, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, int type) { - RawPosixAIOData *acb = g_slice_new(RawPosixAIOData); + RawPosixAIOData *acb = g_new(RawPosixAIOData, 1); ThreadPool *pool; acb->bs = bs; @@ -1286,7 +1279,7 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, int fd, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockCompletionFunc *cb, void *opaque, int type) { - RawPosixAIOData *acb = g_slice_new(RawPosixAIOData); + RawPosixAIOData *acb = g_new(RawPosixAIOData, 1); ThreadPool *pool; acb->bs = bs; @@ -1633,7 +1626,7 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp) nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false); buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); prealloc = qapi_enum_parse(PreallocMode_lookup, buf, - PREALLOC_MODE_MAX, PREALLOC_MODE_OFF, + PREALLOC_MODE__MAX, PREALLOC_MODE_OFF, &local_err); g_free(buf); if (local_err) { @@ -1642,7 +1635,7 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp) goto out; } - fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, + fd = qemu_open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0644); if (fd < 0) { result = -errno; @@ -1827,7 +1820,8 @@ static int find_allocation(BlockDriverState *bs, off_t start, */ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int *pnum) + int nb_sectors, int *pnum, + BlockDriverState **file) { off_t start, data = 0, hole = 0; int64_t total_size; @@ -1869,6 +1863,7 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, *pnum = MIN(nb_sectors, (data - start) / BDRV_SECTOR_SIZE); ret = BDRV_BLOCK_ZERO; } + *file = bs; return ret | BDRV_BLOCK_OFFSET_VALID | start; } @@ -1972,36 +1967,51 @@ BlockDriver bdrv_file = { /* host device */ #if defined(__APPLE__) && defined(__MACH__) -static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator ); -static kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize ); - -kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator ) +static kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath, + CFIndex maxPathSize, int flags); +static char *FindEjectableOpticalMedia(io_iterator_t *mediaIterator) { - kern_return_t kernResult; + kern_return_t kernResult = KERN_FAILURE; mach_port_t masterPort; CFMutableDictionaryRef classesToMatch; + const char *matching_array[] = {kIODVDMediaClass, kIOCDMediaClass}; + char *mediaType = NULL; kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort ); if ( KERN_SUCCESS != kernResult ) { printf( "IOMasterPort returned %d\n", kernResult ); } - classesToMatch = IOServiceMatching( kIOCDMediaClass ); - if ( classesToMatch == NULL ) { - printf( "IOServiceMatching returned a NULL dictionary.\n" ); - } else { - CFDictionarySetValue( classesToMatch, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue ); - } - kernResult = IOServiceGetMatchingServices( masterPort, classesToMatch, mediaIterator ); - if ( KERN_SUCCESS != kernResult ) - { - printf( "IOServiceGetMatchingServices returned %d\n", kernResult ); - } + int index; + for (index = 0; index < ARRAY_SIZE(matching_array); index++) { + classesToMatch = IOServiceMatching(matching_array[index]); + if (classesToMatch == NULL) { + error_report("IOServiceMatching returned NULL for %s", + matching_array[index]); + continue; + } + CFDictionarySetValue(classesToMatch, CFSTR(kIOMediaEjectableKey), + kCFBooleanTrue); + kernResult = IOServiceGetMatchingServices(masterPort, classesToMatch, + mediaIterator); + if (kernResult != KERN_SUCCESS) { + error_report("Note: IOServiceGetMatchingServices returned %d", + kernResult); + continue; + } - return kernResult; + /* If a match was found, leave the loop */ + if (*mediaIterator != 0) { + DPRINTF("Matching using %s\n", matching_array[index]); + mediaType = g_strdup(matching_array[index]); + break; + } + } + return mediaType; } -kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize ) +kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath, + CFIndex maxPathSize, int flags) { io_object_t nextMedia; kern_return_t kernResult = KERN_FAILURE; @@ -2014,7 +2024,9 @@ kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex ma if ( bsdPathAsCFString ) { size_t devPathLength; strcpy( bsdPath, _PATH_DEV ); - strcat( bsdPath, "r" ); + if (flags & BDRV_O_NOCACHE) { + strcat(bsdPath, "r"); + } devPathLength = strlen( bsdPath ); if ( CFStringGetCString( bsdPathAsCFString, bsdPath + devPathLength, maxPathSize - devPathLength, kCFStringEncodingASCII ) ) { kernResult = KERN_SUCCESS; @@ -2027,7 +2039,46 @@ kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex ma return kernResult; } -#endif +/* Sets up a real cdrom for use in QEMU */ +static bool setup_cdrom(char *bsd_path, Error **errp) +{ + int index, num_of_test_partitions = 2, fd; + char test_partition[MAXPATHLEN]; + bool partition_found = false; + + /* look for a working partition */ + for (index = 0; index < num_of_test_partitions; index++) { + snprintf(test_partition, sizeof(test_partition), "%ss%d", bsd_path, + index); + fd = qemu_open(test_partition, O_RDONLY | O_BINARY | O_LARGEFILE); + if (fd >= 0) { + partition_found = true; + qemu_close(fd); + break; + } + } + + /* if a working partition on the device was not found */ + if (partition_found == false) { + error_setg(errp, "Failed to find a working partition on disc"); + } else { + DPRINTF("Using %s as optical disc\n", test_partition); + pstrcpy(bsd_path, MAXPATHLEN, test_partition); + } + return partition_found; +} + +/* Prints directions on mounting and unmounting a device */ +static void print_unmounting_directions(const char *file_name) +{ + error_report("If device %s is mounted on the desktop, unmount" + " it first before using it in QEMU", file_name); + error_report("Command to unmount device: diskutil unmountDisk %s", + file_name); + error_report("Command to mount device: diskutil mountDisk %s", file_name); +} + +#endif /* defined(__APPLE__) && defined(__MACH__) */ static int hdev_probe_device(const char *filename) { @@ -2118,33 +2169,57 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags, #if defined(__APPLE__) && defined(__MACH__) const char *filename = qdict_get_str(options, "filename"); + char bsd_path[MAXPATHLEN] = ""; + bool error_occurred = false; + + /* If using a real cdrom */ + if (strcmp(filename, "/dev/cdrom") == 0) { + char *mediaType = NULL; + kern_return_t ret_val; + io_iterator_t mediaIterator = 0; + + mediaType = FindEjectableOpticalMedia(&mediaIterator); + if (mediaType == NULL) { + error_setg(errp, "Please make sure your CD/DVD is in the optical" + " drive"); + error_occurred = true; + goto hdev_open_Mac_error; + } - if (strstart(filename, "/dev/cdrom", NULL)) { - kern_return_t kernResult; - io_iterator_t mediaIterator; - char bsdPath[ MAXPATHLEN ]; - int fd; - - kernResult = FindEjectableCDMedia( &mediaIterator ); - kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) ); - - if ( bsdPath[ 0 ] != '\0' ) { - strcat(bsdPath,"s0"); - /* some CDs don't have a partition 0 */ - fd = qemu_open(bsdPath, O_RDONLY | O_BINARY | O_LARGEFILE); - if (fd < 0) { - bsdPath[strlen(bsdPath)-1] = '1'; - } else { - qemu_close(fd); - } - filename = bsdPath; - qdict_put(options, "filename", qstring_from_str(filename)); + ret_val = GetBSDPath(mediaIterator, bsd_path, sizeof(bsd_path), flags); + if (ret_val != KERN_SUCCESS) { + error_setg(errp, "Could not get BSD path for optical drive"); + error_occurred = true; + goto hdev_open_Mac_error; + } + + /* If a real optical drive was not found */ + if (bsd_path[0] == '\0') { + error_setg(errp, "Failed to obtain bsd path for optical drive"); + error_occurred = true; + goto hdev_open_Mac_error; + } + + /* If using a cdrom disc and finding a partition on the disc failed */ + if (strncmp(mediaType, kIOCDMediaClass, 9) == 0 && + setup_cdrom(bsd_path, errp) == false) { + print_unmounting_directions(bsd_path); + error_occurred = true; + goto hdev_open_Mac_error; } - if ( mediaIterator ) - IOObjectRelease( mediaIterator ); + qdict_put(options, "filename", qstring_from_str(bsd_path)); + +hdev_open_Mac_error: + g_free(mediaType); + if (mediaIterator) { + IOObjectRelease(mediaIterator); + } + if (error_occurred) { + return -ENOENT; + } } -#endif +#endif /* defined(__APPLE__) && defined(__MACH__) */ s->type = FTYPE_FILE; @@ -2153,6 +2228,15 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags, if (local_err) { error_propagate(errp, local_err); } +#if defined(__APPLE__) && defined(__MACH__) + if (*bsd_path) { + filename = bsd_path; + } + /* if a physical device experienced an error while being opened */ + if (strncmp(filename, "/dev/", 5) == 0) { + print_unmounting_directions(filename); + } +#endif /* defined(__APPLE__) && defined(__MACH__) */ return ret; } @@ -2172,53 +2256,6 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags, } #if defined(__linux__) -/* Note: we do not have a reliable method to detect if the floppy is - present. The current method is to try to open the floppy at every - I/O and to keep it opened during a few hundreds of ms. */ -static int fd_open(BlockDriverState *bs) -{ - BDRVRawState *s = bs->opaque; - int last_media_present; - - if (s->type != FTYPE_FD) - return 0; - last_media_present = (s->fd >= 0); - if (s->fd >= 0 && - (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - s->fd_open_time) >= FD_OPEN_TIMEOUT) { - qemu_close(s->fd); - s->fd = -1; - DPRINTF("Floppy closed\n"); - } - if (s->fd < 0) { - if (s->fd_got_error && - (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - s->fd_error_time) < FD_OPEN_TIMEOUT) { - DPRINTF("No floppy (open delayed)\n"); - return -EIO; - } - s->fd = qemu_open(bs->filename, s->open_flags & ~O_NONBLOCK); - if (s->fd < 0) { - s->fd_error_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - s->fd_got_error = 1; - if (last_media_present) - s->fd_media_changed = 1; - DPRINTF("No floppy\n"); - return -EIO; - } - DPRINTF("Floppy opened\n"); - } - if (!last_media_present) - s->fd_media_changed = 1; - s->fd_open_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - s->fd_got_error = 0; - return 0; -} - -static int hdev_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) -{ - BDRVRawState *s = bs->opaque; - - return ioctl(s->fd, req, buf); -} static BlockAIOCB *hdev_aio_ioctl(BlockDriverState *bs, unsigned long int req, void *buf, @@ -2231,7 +2268,7 @@ static BlockAIOCB *hdev_aio_ioctl(BlockDriverState *bs, if (fd_open(bs) < 0) return NULL; - acb = g_slice_new(RawPosixAIOData); + acb = g_new(RawPosixAIOData, 1); acb->bs = bs; acb->aio_type = QEMU_AIO_IOCTL; acb->aio_fildes = s->fd; @@ -2241,8 +2278,8 @@ static BlockAIOCB *hdev_aio_ioctl(BlockDriverState *bs, pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); } +#endif /* linux */ -#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) static int fd_open(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; @@ -2252,14 +2289,6 @@ static int fd_open(BlockDriverState *bs) return 0; return -EIO; } -#else /* !linux && !FreeBSD */ - -static int fd_open(BlockDriverState *bs) -{ - return 0; -} - -#endif /* !linux && !FreeBSD */ static coroutine_fn BlockAIOCB *hdev_aio_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors, @@ -2303,17 +2332,22 @@ static int hdev_create(const char *filename, QemuOpts *opts, int64_t total_size = 0; bool has_prefix; - /* This function is used by all three protocol block drivers and therefore - * any of these three prefixes may be given. + /* This function is used by both protocol block drivers and therefore either + * of these prefixes may be given. * The return value has to be stored somewhere, otherwise this is an error * due to -Werror=unused-value. */ has_prefix = strstart(filename, "host_device:", &filename) || - strstart(filename, "host_cdrom:" , &filename) || - strstart(filename, "host_floppy:", &filename); + strstart(filename, "host_cdrom:" , &filename); (void)has_prefix; + ret = raw_normalize_devicepath(&filename); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not normalize device path"); + return ret; + } + /* Read out options */ total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), BDRV_SECTOR_SIZE); @@ -2379,160 +2413,10 @@ static BlockDriver bdrv_host_device = { /* generic scsi device */ #ifdef __linux__ - .bdrv_ioctl = hdev_ioctl, .bdrv_aio_ioctl = hdev_aio_ioctl, #endif }; -#ifdef __linux__ -static void floppy_parse_filename(const char *filename, QDict *options, - Error **errp) -{ - /* The prefix is optional, just as for "file". */ - strstart(filename, "host_floppy:", &filename); - - qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename))); -} - -static int floppy_open(BlockDriverState *bs, QDict *options, int flags, - Error **errp) -{ - BDRVRawState *s = bs->opaque; - Error *local_err = NULL; - int ret; - - s->type = FTYPE_FD; - - /* open will not fail even if no floppy is inserted, so add O_NONBLOCK */ - ret = raw_open_common(bs, options, flags, O_NONBLOCK, &local_err); - if (ret) { - if (local_err) { - error_propagate(errp, local_err); - } - return ret; - } - - /* close fd so that we can reopen it as needed */ - qemu_close(s->fd); - s->fd = -1; - s->fd_media_changed = 1; - - error_report("Host floppy pass-through is deprecated"); - error_printf("Support for it will be removed in a future release.\n"); - return 0; -} - -static int floppy_probe_device(const char *filename) -{ - int fd, ret; - int prio = 0; - struct floppy_struct fdparam; - struct stat st; - - if (strstart(filename, "/dev/fd", NULL) && - !strstart(filename, "/dev/fdset/", NULL) && - !strstart(filename, "/dev/fd/", NULL)) { - prio = 50; - } - - fd = qemu_open(filename, O_RDONLY | O_NONBLOCK); - if (fd < 0) { - goto out; - } - ret = fstat(fd, &st); - if (ret == -1 || !S_ISBLK(st.st_mode)) { - goto outc; - } - - /* Attempt to detect via a floppy specific ioctl */ - ret = ioctl(fd, FDGETPRM, &fdparam); - if (ret >= 0) - prio = 100; - -outc: - qemu_close(fd); -out: - return prio; -} - - -static int floppy_is_inserted(BlockDriverState *bs) -{ - return fd_open(bs) >= 0; -} - -static int floppy_media_changed(BlockDriverState *bs) -{ - BDRVRawState *s = bs->opaque; - int ret; - - /* - * XXX: we do not have a true media changed indication. - * It does not work if the floppy is changed without trying to read it. - */ - fd_open(bs); - ret = s->fd_media_changed; - s->fd_media_changed = 0; - DPRINTF("Floppy changed=%d\n", ret); - return ret; -} - -static void floppy_eject(BlockDriverState *bs, bool eject_flag) -{ - BDRVRawState *s = bs->opaque; - int fd; - - if (s->fd >= 0) { - qemu_close(s->fd); - s->fd = -1; - } - fd = qemu_open(bs->filename, s->open_flags | O_NONBLOCK); - if (fd >= 0) { - if (ioctl(fd, FDEJECT, 0) < 0) - perror("FDEJECT"); - qemu_close(fd); - } -} - -static BlockDriver bdrv_host_floppy = { - .format_name = "host_floppy", - .protocol_name = "host_floppy", - .instance_size = sizeof(BDRVRawState), - .bdrv_needs_filename = true, - .bdrv_probe_device = floppy_probe_device, - .bdrv_parse_filename = floppy_parse_filename, - .bdrv_file_open = floppy_open, - .bdrv_close = raw_close, - .bdrv_reopen_prepare = raw_reopen_prepare, - .bdrv_reopen_commit = raw_reopen_commit, - .bdrv_reopen_abort = raw_reopen_abort, - .bdrv_create = hdev_create, - .create_opts = &raw_create_opts, - - .bdrv_aio_readv = raw_aio_readv, - .bdrv_aio_writev = raw_aio_writev, - .bdrv_aio_flush = raw_aio_flush, - .bdrv_refresh_limits = raw_refresh_limits, - .bdrv_io_plug = raw_aio_plug, - .bdrv_io_unplug = raw_aio_unplug, - .bdrv_flush_io_queue = raw_aio_flush_io_queue, - - .bdrv_truncate = raw_truncate, - .bdrv_getlength = raw_getlength, - .has_variable_length = true, - .bdrv_get_allocated_file_size - = raw_get_allocated_file_size, - - .bdrv_detach_aio_context = raw_detach_aio_context, - .bdrv_attach_aio_context = raw_attach_aio_context, - - /* removable device support */ - .bdrv_is_inserted = floppy_is_inserted, - .bdrv_media_changed = floppy_media_changed, - .bdrv_eject = floppy_eject, -}; -#endif - #if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) static void cdrom_parse_filename(const char *filename, QDict *options, Error **errp) @@ -2588,15 +2472,13 @@ out: return prio; } -static int cdrom_is_inserted(BlockDriverState *bs) +static bool cdrom_is_inserted(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; int ret; ret = ioctl(s->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); - if (ret == CDS_DISC_OK) - return 1; - return 0; + return ret == CDS_DISC_OK; } static void cdrom_eject(BlockDriverState *bs, bool eject_flag) @@ -2663,7 +2545,6 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_lock_medium = cdrom_lock_medium, /* generic scsi device */ - .bdrv_ioctl = hdev_ioctl, .bdrv_aio_ioctl = hdev_aio_ioctl, }; #endif /* __linux__ */ @@ -2722,7 +2603,7 @@ static int cdrom_reopen(BlockDriverState *bs) return 0; } -static int cdrom_is_inserted(BlockDriverState *bs) +static bool cdrom_is_inserted(BlockDriverState *bs) { return raw_getlength(bs) > 0; } @@ -2810,7 +2691,6 @@ static void bdrv_file_init(void) bdrv_register(&bdrv_file); bdrv_register(&bdrv_host_device); #ifdef __linux__ - bdrv_register(&bdrv_host_floppy); bdrv_register(&bdrv_host_cdrom); #endif #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) diff --git a/qemu/block/raw-win32.c b/qemu/block/raw-win32.c index 68f2338ac..fd2389153 100644 --- a/qemu/block/raw-win32.c +++ b/qemu/block/raw-win32.c @@ -21,7 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/cutils.h" #include "qemu/timer.h" #include "block/block_int.h" #include "qemu/module.h" @@ -119,9 +121,9 @@ static int aio_worker(void *arg) case QEMU_AIO_WRITE: count = handle_aiocb_rw(aiocb); if (count == aiocb->aio_nbytes) { - count = 0; + ret = 0; } else { - count = -EINVAL; + ret = -EINVAL; } break; case QEMU_AIO_FLUSH: @@ -135,7 +137,7 @@ static int aio_worker(void *arg) break; } - g_slice_free(RawWin32AIOData, aiocb); + g_free(aiocb); return ret; } @@ -143,7 +145,7 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockCompletionFunc *cb, void *opaque, int type) { - RawWin32AIOData *acb = g_slice_new(RawWin32AIOData); + RawWin32AIOData *acb = g_new(RawWin32AIOData, 1); ThreadPool *pool; acb->bs = bs; diff --git a/qemu/block/raw_bsd.c b/qemu/block/raw_bsd.c index e3d2d0468..a6cc7e991 100644 --- a/qemu/block/raw_bsd.c +++ b/qemu/block/raw_bsd.c @@ -26,7 +26,9 @@ * IN THE SOFTWARE. */ +#include "qemu/osdep.h" #include "block/block_int.h" +#include "qapi/error.h" #include "qemu/option.h" static QemuOptsList raw_create_opts = { @@ -52,11 +54,12 @@ static int coroutine_fn raw_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); - return bdrv_co_readv(bs->file, sector_num, nb_sectors, qiov); + return bdrv_co_readv(bs->file->bs, sector_num, nb_sectors, qiov); } -static int coroutine_fn raw_co_writev(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) +static int coroutine_fn +raw_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors, + QEMUIOVector *qiov, int flags) { void *buf = NULL; BlockDriver *drv; @@ -75,7 +78,7 @@ static int coroutine_fn raw_co_writev(BlockDriverState *bs, int64_t sector_num, return 0; } - buf = qemu_try_blockalign(bs->file, 512); + buf = qemu_try_blockalign(bs->file->bs, 512); if (!buf) { ret = -ENOMEM; goto fail; @@ -102,7 +105,8 @@ static int coroutine_fn raw_co_writev(BlockDriverState *bs, int64_t sector_num, } BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); - ret = bdrv_co_writev(bs->file, sector_num, nb_sectors, qiov); + ret = bdrv_co_do_pwritev(bs->file->bs, sector_num * BDRV_SECTOR_SIZE, + nb_sectors * BDRV_SECTOR_SIZE, qiov, flags); fail: if (qiov == &local_qiov) { @@ -112,11 +116,20 @@ fail: return ret; } +static int coroutine_fn +raw_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, + QEMUIOVector *qiov) +{ + return raw_co_writev_flags(bs, sector_num, nb_sectors, qiov, 0); +} + static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int *pnum) + int nb_sectors, int *pnum, + BlockDriverState **file) { *pnum = nb_sectors; + *file = bs->file->bs; return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA | (sector_num << BDRV_SECTOR_BITS); } @@ -125,58 +138,48 @@ static int coroutine_fn raw_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, int nb_sectors, BdrvRequestFlags flags) { - return bdrv_co_write_zeroes(bs->file, sector_num, nb_sectors, flags); + return bdrv_co_write_zeroes(bs->file->bs, sector_num, nb_sectors, flags); } static int coroutine_fn raw_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) { - return bdrv_co_discard(bs->file, sector_num, nb_sectors); + return bdrv_co_discard(bs->file->bs, sector_num, nb_sectors); } static int64_t raw_getlength(BlockDriverState *bs) { - return bdrv_getlength(bs->file); + return bdrv_getlength(bs->file->bs); } static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { - return bdrv_get_info(bs->file, bdi); + return bdrv_get_info(bs->file->bs, bdi); } static void raw_refresh_limits(BlockDriverState *bs, Error **errp) { - bs->bl = bs->file->bl; + bs->bl = bs->file->bs->bl; } static int raw_truncate(BlockDriverState *bs, int64_t offset) { - return bdrv_truncate(bs->file, offset); -} - -static int raw_is_inserted(BlockDriverState *bs) -{ - return bdrv_is_inserted(bs->file); + return bdrv_truncate(bs->file->bs, offset); } static int raw_media_changed(BlockDriverState *bs) { - return bdrv_media_changed(bs->file); + return bdrv_media_changed(bs->file->bs); } static void raw_eject(BlockDriverState *bs, bool eject_flag) { - bdrv_eject(bs->file, eject_flag); + bdrv_eject(bs->file->bs, eject_flag); } static void raw_lock_medium(BlockDriverState *bs, bool locked) { - bdrv_lock_medium(bs->file, locked); -} - -static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) -{ - return bdrv_ioctl(bs->file, req, buf); + bdrv_lock_medium(bs->file->bs, locked); } static BlockAIOCB *raw_aio_ioctl(BlockDriverState *bs, @@ -184,12 +187,12 @@ static BlockAIOCB *raw_aio_ioctl(BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque) { - return bdrv_aio_ioctl(bs->file, req, buf, cb, opaque); + return bdrv_aio_ioctl(bs->file->bs, req, buf, cb, opaque); } static int raw_has_zero_init(BlockDriverState *bs) { - return bdrv_has_zero_init(bs->file); + return bdrv_has_zero_init(bs->file->bs); } static int raw_create(const char *filename, QemuOpts *opts, Error **errp) @@ -207,7 +210,7 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp) static int raw_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { - bs->sg = bs->file->sg; + bs->sg = bs->file->bs->sg; if (bs->probed && !bdrv_is_read_only(bs)) { fprintf(stderr, @@ -217,7 +220,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, "raw images, write operations on block 0 will be restricted.\n" " Specify the 'raw' format explicitly to remove the " "restrictions.\n", - bs->file->filename); + bs->file->bs->filename); } return 0; @@ -237,12 +240,12 @@ static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) static int raw_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) { - return bdrv_probe_blocksizes(bs->file, bsz); + return bdrv_probe_blocksizes(bs->file->bs, bsz); } static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) { - return bdrv_probe_geometry(bs->file, geo); + return bdrv_probe_geometry(bs->file->bs, geo); } BlockDriver bdrv_raw = { @@ -254,6 +257,8 @@ BlockDriver bdrv_raw = { .bdrv_create = &raw_create, .bdrv_co_readv = &raw_co_readv, .bdrv_co_writev = &raw_co_writev, + .bdrv_co_writev_flags = &raw_co_writev_flags, + .supported_write_flags = BDRV_REQ_FUA, .bdrv_co_write_zeroes = &raw_co_write_zeroes, .bdrv_co_discard = &raw_co_discard, .bdrv_co_get_block_status = &raw_co_get_block_status, @@ -264,11 +269,9 @@ BlockDriver bdrv_raw = { .bdrv_refresh_limits = &raw_refresh_limits, .bdrv_probe_blocksizes = &raw_probe_blocksizes, .bdrv_probe_geometry = &raw_probe_geometry, - .bdrv_is_inserted = &raw_is_inserted, .bdrv_media_changed = &raw_media_changed, .bdrv_eject = &raw_eject, .bdrv_lock_medium = &raw_lock_medium, - .bdrv_ioctl = &raw_ioctl, .bdrv_aio_ioctl = &raw_aio_ioctl, .create_opts = &raw_create_opts, .bdrv_has_zero_init = &raw_has_zero_init diff --git a/qemu/block/rbd.c b/qemu/block/rbd.c index a60a19d58..5bc5b3253 100644 --- a/qemu/block/rbd.c +++ b/qemu/block/rbd.c @@ -11,11 +11,13 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include +#include "qemu/osdep.h" -#include "qemu-common.h" +#include "qapi/error.h" #include "qemu/error-report.h" #include "block/block_int.h" +#include "crypto/secret.h" +#include "qemu/cutils.h" #include @@ -228,6 +230,27 @@ static char *qemu_rbd_parse_clientname(const char *conf, char *clientname) return NULL; } + +static int qemu_rbd_set_auth(rados_t cluster, const char *secretid, + Error **errp) +{ + if (secretid == 0) { + return 0; + } + + gchar *secret = qcrypto_secret_lookup_as_base64(secretid, + errp); + if (!secret) { + return -1; + } + + rados_conf_set(cluster, "key", secret); + g_free(secret); + + return 0; +} + + static int qemu_rbd_set_conf(rados_t cluster, const char *conf, bool only_read_conf_file, Error **errp) @@ -299,10 +322,13 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp) char conf[RBD_MAX_CONF_SIZE]; char clientname_buf[RBD_MAX_CONF_SIZE]; char *clientname; + const char *secretid; rados_t cluster; rados_ioctx_t io_ctx; int ret; + secretid = qemu_opt_get(opts, "password-secret"); + if (qemu_rbd_parsename(filename, pool, sizeof(pool), snap_buf, sizeof(snap_buf), name, sizeof(name), @@ -350,6 +376,11 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp) return -EIO; } + if (qemu_rbd_set_auth(cluster, secretid, errp) < 0) { + rados_shutdown(cluster); + return -EIO; + } + if (rados_connect(cluster) < 0) { error_setg(errp, "error connecting"); rados_shutdown(cluster); @@ -423,6 +454,11 @@ static QemuOptsList runtime_opts = { .type = QEMU_OPT_STRING, .help = "Specification of the rbd image", }, + { + .name = "password-secret", + .type = QEMU_OPT_STRING, + .help = "ID of secret providing the password", + }, { /* end of list */ } }, }; @@ -436,6 +472,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, char conf[RBD_MAX_CONF_SIZE]; char clientname_buf[RBD_MAX_CONF_SIZE]; char *clientname; + const char *secretid; QemuOpts *opts; Error *local_err = NULL; const char *filename; @@ -450,6 +487,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, } filename = qemu_opt_get(opts, "filename"); + secretid = qemu_opt_get(opts, "password-secret"); if (qemu_rbd_parsename(filename, pool, sizeof(pool), snap_buf, sizeof(snap_buf), @@ -488,6 +526,11 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, } } + if (qemu_rbd_set_auth(s->cluster, secretid, errp) < 0) { + r = -EIO; + goto failed_shutdown; + } + /* * Fallback to more conservative semantics if setting cache * options fails. Ignore errors from setting rbd_cache because the @@ -919,6 +962,11 @@ static QemuOptsList qemu_rbd_create_opts = { .type = QEMU_OPT_SIZE, .help = "RBD object size" }, + { + .name = "password-secret", + .type = QEMU_OPT_STRING, + .help = "ID of secret providing the password", + }, { /* end of list */ } } }; diff --git a/qemu/block/sheepdog.c b/qemu/block/sheepdog.c index 9585beb73..33e0a3382 100644 --- a/qemu/block/sheepdog.c +++ b/qemu/block/sheepdog.c @@ -12,12 +12,15 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/uri.h" #include "qemu/error-report.h" #include "qemu/sockets.h" #include "block/block_int.h" +#include "sysemu/block-backend.h" #include "qemu/bitops.h" +#include "qemu/cutils.h" #define SD_PROTO_VER 0x01 @@ -28,7 +31,6 @@ #define SD_OP_READ_OBJ 0x02 #define SD_OP_WRITE_OBJ 0x03 /* 0x04 is used internally by Sheepdog */ -#define SD_OP_DISCARD_OBJ 0x05 #define SD_OP_NEW_VDI 0x11 #define SD_OP_LOCK_VDI 0x12 @@ -284,6 +286,12 @@ static inline bool is_snapshot(struct SheepdogInode *inode) return !!inode->snap_ctime; } +static inline size_t count_data_objs(const struct SheepdogInode *inode) +{ + return DIV_ROUND_UP(inode->vdi_size, + (1UL << inode->block_size_shift)); +} + #undef DPRINTF #ifdef DEBUG_SDOG #define DPRINTF(fmt, args...) \ @@ -318,7 +326,7 @@ enum AIOCBState { AIOCB_DISCARD_OBJ, }; -#define AIOCBOverwrapping(x, y) \ +#define AIOCBOverlapping(x, y) \ (!(x->max_affect_data_idx < y->min_affect_data_idx \ || y->max_affect_data_idx < x->min_affect_data_idx)) @@ -342,6 +350,15 @@ struct SheepdogAIOCB { uint32_t min_affect_data_idx; uint32_t max_affect_data_idx; + /* + * The difference between affect_data_idx and dirty_data_idx: + * affect_data_idx represents range of index of all request types. + * dirty_data_idx represents range of index updated by COW requests. + * dirty_data_idx is used for updating an inode object. + */ + uint32_t min_dirty_data_idx; + uint32_t max_dirty_data_idx; + QLIST_ENTRY(SheepdogAIOCB) aiocb_siblings; }; @@ -351,9 +368,6 @@ typedef struct BDRVSheepdogState { SheepdogInode inode; - uint32_t min_dirty_data_idx; - uint32_t max_dirty_data_idx; - char name[SD_MAX_VDI_LEN]; bool is_snapshot; uint32_t cache_flags; @@ -373,10 +387,15 @@ typedef struct BDRVSheepdogState { QLIST_HEAD(inflight_aio_head, AIOReq) inflight_aio_head; QLIST_HEAD(failed_aio_head, AIOReq) failed_aio_head; - CoQueue overwrapping_queue; + CoQueue overlapping_queue; QLIST_HEAD(inflight_aiocb_head, SheepdogAIOCB) inflight_aiocb_head; } BDRVSheepdogState; +typedef struct BDRVSheepdogReopenState { + int fd; + int cache_flags; +} BDRVSheepdogReopenState; + static const char * sd_strerror(int err) { int i; @@ -556,6 +575,9 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov, acb->max_affect_data_idx = (acb->sector_num * BDRV_SECTOR_SIZE + acb->nb_sectors * BDRV_SECTOR_SIZE) / object_size; + acb->min_dirty_data_idx = UINT32_MAX; + acb->max_dirty_data_idx = 0; + return acb; } @@ -595,14 +617,13 @@ static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data, ret = qemu_co_send(sockfd, hdr, sizeof(*hdr)); if (ret != sizeof(*hdr)) { error_report("failed to send a req, %s", strerror(errno)); - ret = -socket_error(); - return ret; + return -errno; } ret = qemu_co_send(sockfd, data, *wlen); if (ret != *wlen) { - ret = -socket_error(); error_report("failed to send a req, %s", strerror(errno)); + return -errno; } return ret; @@ -638,14 +659,16 @@ static coroutine_fn void do_co_req(void *opaque) unsigned int *rlen = srco->rlen; co = qemu_coroutine_self(); - aio_set_fd_handler(srco->aio_context, sockfd, NULL, restart_co_req, co); + aio_set_fd_handler(srco->aio_context, sockfd, false, + NULL, restart_co_req, co); ret = send_co_req(sockfd, hdr, data, wlen); if (ret < 0) { goto out; } - aio_set_fd_handler(srco->aio_context, sockfd, restart_co_req, NULL, co); + aio_set_fd_handler(srco->aio_context, sockfd, false, + restart_co_req, NULL, co); ret = qemu_co_recv(sockfd, hdr, sizeof(*hdr)); if (ret != sizeof(*hdr)) { @@ -670,7 +693,8 @@ static coroutine_fn void do_co_req(void *opaque) out: /* there is at most one request for this sockfd, so it is safe to * set each handler to NULL. */ - aio_set_fd_handler(srco->aio_context, sockfd, NULL, NULL, NULL); + aio_set_fd_handler(srco->aio_context, sockfd, false, + NULL, NULL, NULL); srco->ret = ret; srco->finished = true; @@ -722,7 +746,8 @@ static coroutine_fn void reconnect_to_sdog(void *opaque) BDRVSheepdogState *s = opaque; AIOReq *aio_req, *next; - aio_set_fd_handler(s->aio_context, s->fd, NULL, NULL, NULL); + aio_set_fd_handler(s->aio_context, s->fd, false, NULL, + NULL, NULL); close(s->fd); s->fd = -1; @@ -819,8 +844,8 @@ static void coroutine_fn aio_read_response(void *opaque) */ if (rsp.result == SD_RES_SUCCESS) { s->inode.data_vdi_id[idx] = s->inode.vdi_id; - s->max_dirty_data_idx = MAX(idx, s->max_dirty_data_idx); - s->min_dirty_data_idx = MIN(idx, s->min_dirty_data_idx); + acb->max_dirty_data_idx = MAX(idx, acb->max_dirty_data_idx); + acb->min_dirty_data_idx = MIN(idx, acb->min_dirty_data_idx); } } break; @@ -847,10 +872,6 @@ static void coroutine_fn aio_read_response(void *opaque) rsp.result = SD_RES_SUCCESS; s->discard_supported = false; break; - case SD_RES_SUCCESS: - idx = data_oid_to_idx(aio_req->oid); - s->inode.data_vdi_id[idx] = 0; - break; default: break; } @@ -929,7 +950,8 @@ static int get_sheep_fd(BDRVSheepdogState *s, Error **errp) return fd; } - aio_set_fd_handler(s->aio_context, fd, co_read_response, NULL, s); + aio_set_fd_handler(s->aio_context, fd, false, + co_read_response, NULL, s); return fd; } @@ -1165,7 +1187,13 @@ static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, hdr.flags = SD_FLAG_CMD_WRITE | flags; break; case AIOCB_DISCARD_OBJ: - hdr.opcode = SD_OP_DISCARD_OBJ; + hdr.opcode = SD_OP_WRITE_OBJ; + hdr.flags = SD_FLAG_CMD_WRITE | flags; + s->inode.data_vdi_id[data_oid_to_idx(oid)] = 0; + offset = offsetof(SheepdogInode, + data_vdi_id[data_oid_to_idx(oid)]); + oid = vid_to_vdi_oid(s->inode.vdi_id); + wlen = datalen = sizeof(uint32_t); break; } @@ -1184,7 +1212,7 @@ static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, qemu_co_mutex_lock(&s->lock); s->co_send = qemu_coroutine_self(); - aio_set_fd_handler(s->aio_context, s->fd, + aio_set_fd_handler(s->aio_context, s->fd, false, co_read_response, co_write_request, s); socket_set_cork(s->fd, 1); @@ -1203,7 +1231,8 @@ static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, } out: socket_set_cork(s->fd, 0); - aio_set_fd_handler(s->aio_context, s->fd, co_read_response, NULL, s); + aio_set_fd_handler(s->aio_context, s->fd, false, + co_read_response, NULL, s); s->co_send = NULL; qemu_co_mutex_unlock(&s->lock); } @@ -1353,7 +1382,8 @@ static void sd_detach_aio_context(BlockDriverState *bs) { BDRVSheepdogState *s = bs->opaque; - aio_set_fd_handler(s->aio_context, s->fd, NULL, NULL, NULL); + aio_set_fd_handler(s->aio_context, s->fd, false, NULL, + NULL, NULL); } static void sd_attach_aio_context(BlockDriverState *bs, @@ -1362,7 +1392,8 @@ static void sd_attach_aio_context(BlockDriverState *bs, BDRVSheepdogState *s = bs->opaque; s->aio_context = new_context; - aio_set_fd_handler(new_context, s->fd, co_read_response, NULL, s); + aio_set_fd_handler(new_context, s->fd, false, + co_read_response, NULL, s); } /* TODO Convert to fine grained options */ @@ -1466,18 +1497,17 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags, } memcpy(&s->inode, buf, sizeof(s->inode)); - s->min_dirty_data_idx = UINT32_MAX; - s->max_dirty_data_idx = 0; bs->total_sectors = s->inode.vdi_size / BDRV_SECTOR_SIZE; pstrcpy(s->name, sizeof(s->name), vdi); qemu_co_mutex_init(&s->lock); - qemu_co_queue_init(&s->overwrapping_queue); + qemu_co_queue_init(&s->overlapping_queue); qemu_opts_del(opts); g_free(buf); return 0; out: - aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd, NULL, NULL, NULL); + aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd, + false, NULL, NULL, NULL); if (s->fd >= 0) { closesocket(s->fd); } @@ -1486,6 +1516,70 @@ out: return ret; } +static int sd_reopen_prepare(BDRVReopenState *state, BlockReopenQueue *queue, + Error **errp) +{ + BDRVSheepdogState *s = state->bs->opaque; + BDRVSheepdogReopenState *re_s; + int ret = 0; + + re_s = state->opaque = g_new0(BDRVSheepdogReopenState, 1); + + re_s->cache_flags = SD_FLAG_CMD_CACHE; + if (state->flags & BDRV_O_NOCACHE) { + re_s->cache_flags = SD_FLAG_CMD_DIRECT; + } + + re_s->fd = get_sheep_fd(s, errp); + if (re_s->fd < 0) { + ret = re_s->fd; + return ret; + } + + return ret; +} + +static void sd_reopen_commit(BDRVReopenState *state) +{ + BDRVSheepdogReopenState *re_s = state->opaque; + BDRVSheepdogState *s = state->bs->opaque; + + if (s->fd) { + aio_set_fd_handler(s->aio_context, s->fd, false, + NULL, NULL, NULL); + closesocket(s->fd); + } + + s->fd = re_s->fd; + s->cache_flags = re_s->cache_flags; + + g_free(state->opaque); + state->opaque = NULL; + + return; +} + +static void sd_reopen_abort(BDRVReopenState *state) +{ + BDRVSheepdogReopenState *re_s = state->opaque; + BDRVSheepdogState *s = state->bs->opaque; + + if (re_s == NULL) { + return; + } + + if (re_s->fd) { + aio_set_fd_handler(s->aio_context, re_s->fd, false, + NULL, NULL, NULL); + closesocket(re_s->fd); + } + + g_free(state->opaque); + state->opaque = NULL; + + return; +} + static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot, Error **errp) { @@ -1544,7 +1638,7 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot, static int sd_prealloc(const char *filename, Error **errp) { - BlockDriverState *bs = NULL; + BlockBackend *blk = NULL; BDRVSheepdogState *base = NULL; unsigned long buf_size; uint32_t idx, max_idx; @@ -1553,19 +1647,22 @@ static int sd_prealloc(const char *filename, Error **errp) void *buf = NULL; int ret; - ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, - NULL, errp); - if (ret < 0) { + blk = blk_new_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_PROTOCOL, errp); + if (blk == NULL) { + ret = -EIO; goto out_with_err_set; } - vdi_size = bdrv_getlength(bs); + blk_set_allow_write_beyond_eof(blk, true); + + vdi_size = blk_getlength(blk); if (vdi_size < 0) { ret = vdi_size; goto out; } - base = bs->opaque; + base = blk_bs(blk)->opaque; object_size = (UINT32_C(1) << base->inode.block_size_shift); buf_size = MIN(object_size, SD_DATA_OBJ_SIZE); buf = g_malloc0(buf_size); @@ -1577,23 +1674,24 @@ static int sd_prealloc(const char *filename, Error **errp) * The created image can be a cloned image, so we need to read * a data from the source image. */ - ret = bdrv_pread(bs, idx * buf_size, buf, buf_size); + ret = blk_pread(blk, idx * buf_size, buf, buf_size); if (ret < 0) { goto out; } - ret = bdrv_pwrite(bs, idx * buf_size, buf, buf_size); + ret = blk_pwrite(blk, idx * buf_size, buf, buf_size); if (ret < 0) { goto out; } } + ret = 0; out: if (ret < 0) { error_setg_errno(errp, -ret, "Can't pre-allocate"); } out_with_err_set: - if (bs) { - bdrv_unref(bs); + if (blk) { + blk_unref(blk); } g_free(buf); @@ -1733,7 +1831,7 @@ static int sd_create(const char *filename, QemuOpts *opts, } if (backing_file) { - BlockDriverState *bs; + BlockBackend *blk; BDRVSheepdogState *base; BlockDriver *drv; @@ -1745,23 +1843,23 @@ static int sd_create(const char *filename, QemuOpts *opts, goto out; } - bs = NULL; - ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_PROTOCOL, NULL, - errp); - if (ret < 0) { + blk = blk_new_open(backing_file, NULL, NULL, + BDRV_O_PROTOCOL, errp); + if (blk == NULL) { + ret = -EIO; goto out; } - base = bs->opaque; + base = blk_bs(blk)->opaque; if (!is_snapshot(&base->inode)) { error_setg(errp, "cannot clone from a non snapshot vdi"); - bdrv_unref(bs); + blk_unref(blk); ret = -EINVAL; goto out; } s->inode.vdi_id = base->inode.vdi_id; - bdrv_unref(bs); + blk_unref(blk); } s->aio_context = qemu_get_aio_context(); @@ -1776,8 +1874,7 @@ static int sd_create(const char *filename, QemuOpts *opts, fd = connect_to_sdog(s, &local_err); if (fd < 0) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); ret = -EIO; goto out; } @@ -1861,7 +1958,8 @@ static void sd_close(BlockDriverState *bs) error_report("%s, %s", sd_strerror(rsp->result), s->name); } - aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd, NULL, NULL, NULL); + aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd, + false, NULL, NULL, NULL); closesocket(s->fd); g_free(s->host_spec); } @@ -1923,16 +2021,16 @@ static void coroutine_fn sd_write_done(SheepdogAIOCB *acb) AIOReq *aio_req; uint32_t offset, data_len, mn, mx; - mn = s->min_dirty_data_idx; - mx = s->max_dirty_data_idx; + mn = acb->min_dirty_data_idx; + mx = acb->max_dirty_data_idx; if (mn <= mx) { /* we need to update the vdi object. */ offset = sizeof(s->inode) - sizeof(s->inode.data_vdi_id) + mn * sizeof(s->inode.data_vdi_id[0]); data_len = (mx - mn + 1) * sizeof(s->inode.data_vdi_id[0]); - s->min_dirty_data_idx = UINT32_MAX; - s->max_dirty_data_idx = 0; + acb->min_dirty_data_idx = UINT32_MAX; + acb->max_dirty_data_idx = 0; iov.iov_base = &s->inode; iov.iov_len = sizeof(s->inode); @@ -2141,7 +2239,9 @@ static int coroutine_fn sd_co_rw_vector(void *p) } aio_req = alloc_aio_req(s, acb, oid, len, offset, flags, create, - old_oid, done); + old_oid, + acb->aiocb_type == AIOCB_DISCARD_OBJ ? + 0 : done); QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings); add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov, @@ -2158,12 +2258,12 @@ out: return 1; } -static bool check_overwrapping_aiocb(BDRVSheepdogState *s, SheepdogAIOCB *aiocb) +static bool check_overlapping_aiocb(BDRVSheepdogState *s, SheepdogAIOCB *aiocb) { SheepdogAIOCB *cb; QLIST_FOREACH(cb, &s->inflight_aiocb_head, aiocb_siblings) { - if (AIOCBOverwrapping(aiocb, cb)) { + if (AIOCBOverlapping(aiocb, cb)) { return true; } } @@ -2192,15 +2292,15 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num, acb->aiocb_type = AIOCB_WRITE_UDATA; retry: - if (check_overwrapping_aiocb(s, acb)) { - qemu_co_queue_wait(&s->overwrapping_queue); + if (check_overlapping_aiocb(s, acb)) { + qemu_co_queue_wait(&s->overlapping_queue); goto retry; } ret = sd_co_rw_vector(acb); if (ret <= 0) { QLIST_REMOVE(acb, aiocb_siblings); - qemu_co_queue_restart_all(&s->overwrapping_queue); + qemu_co_queue_restart_all(&s->overlapping_queue); qemu_aio_unref(acb); return ret; } @@ -2208,7 +2308,7 @@ retry: qemu_coroutine_yield(); QLIST_REMOVE(acb, aiocb_siblings); - qemu_co_queue_restart_all(&s->overwrapping_queue); + qemu_co_queue_restart_all(&s->overlapping_queue); return acb->ret; } @@ -2225,15 +2325,15 @@ static coroutine_fn int sd_co_readv(BlockDriverState *bs, int64_t sector_num, acb->aio_done_func = sd_finish_aiocb; retry: - if (check_overwrapping_aiocb(s, acb)) { - qemu_co_queue_wait(&s->overwrapping_queue); + if (check_overlapping_aiocb(s, acb)) { + qemu_co_queue_wait(&s->overlapping_queue); goto retry; } ret = sd_co_rw_vector(acb); if (ret <= 0) { QLIST_REMOVE(acb, aiocb_siblings); - qemu_co_queue_restart_all(&s->overwrapping_queue); + qemu_co_queue_restart_all(&s->overlapping_queue); qemu_aio_unref(acb); return ret; } @@ -2241,7 +2341,7 @@ retry: qemu_coroutine_yield(); QLIST_REMOVE(acb, aiocb_siblings); - qemu_co_queue_restart_all(&s->overwrapping_queue); + qemu_co_queue_restart_all(&s->overlapping_queue); return acb->ret; } @@ -2318,9 +2418,8 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) ret = do_sd_create(s, &new_vid, 1, &local_err); if (ret < 0) { - error_report("failed to create inode for snapshot: %s", - error_get_pretty(local_err)); - error_free(local_err); + error_reportf_err(local_err, + "failed to create inode for snapshot: "); goto cleanup; } @@ -2391,13 +2490,131 @@ out: return ret; } +#define NR_BATCHED_DISCARD 128 + +static bool remove_objects(BDRVSheepdogState *s) +{ + int fd, i = 0, nr_objs = 0; + Error *local_err = NULL; + int ret = 0; + bool result = true; + SheepdogInode *inode = &s->inode; + + fd = connect_to_sdog(s, &local_err); + if (fd < 0) { + error_report_err(local_err); + return false; + } + + nr_objs = count_data_objs(inode); + while (i < nr_objs) { + int start_idx, nr_filled_idx; + + while (i < nr_objs && !inode->data_vdi_id[i]) { + i++; + } + start_idx = i; + + nr_filled_idx = 0; + while (i < nr_objs && nr_filled_idx < NR_BATCHED_DISCARD) { + if (inode->data_vdi_id[i]) { + inode->data_vdi_id[i] = 0; + nr_filled_idx++; + } + + i++; + } + + ret = write_object(fd, s->aio_context, + (char *)&inode->data_vdi_id[start_idx], + vid_to_vdi_oid(s->inode.vdi_id), inode->nr_copies, + (i - start_idx) * sizeof(uint32_t), + offsetof(struct SheepdogInode, + data_vdi_id[start_idx]), + false, s->cache_flags); + if (ret < 0) { + error_report("failed to discard snapshot inode."); + result = false; + goto out; + } + } + +out: + closesocket(fd); + return result; +} + static int sd_snapshot_delete(BlockDriverState *bs, const char *snapshot_id, const char *name, Error **errp) { - /* FIXME: Delete specified snapshot id. */ - return 0; + unsigned long snap_id = 0; + char snap_tag[SD_MAX_VDI_TAG_LEN]; + Error *local_err = NULL; + int fd, ret; + char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN]; + BDRVSheepdogState *s = bs->opaque; + unsigned int wlen = SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN, rlen = 0; + uint32_t vid; + SheepdogVdiReq hdr = { + .opcode = SD_OP_DEL_VDI, + .data_length = wlen, + .flags = SD_FLAG_CMD_WRITE, + }; + SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr; + + if (!remove_objects(s)) { + return -1; + } + + memset(buf, 0, sizeof(buf)); + memset(snap_tag, 0, sizeof(snap_tag)); + pstrcpy(buf, SD_MAX_VDI_LEN, s->name); + ret = qemu_strtoul(snapshot_id, NULL, 10, &snap_id); + if (ret || snap_id > UINT32_MAX) { + error_setg(errp, "Invalid snapshot ID: %s", + snapshot_id ? snapshot_id : ""); + return -EINVAL; + } + + if (snap_id) { + hdr.snapid = (uint32_t) snap_id; + } else { + pstrcpy(snap_tag, sizeof(snap_tag), snapshot_id); + pstrcpy(buf + SD_MAX_VDI_LEN, SD_MAX_VDI_TAG_LEN, snap_tag); + } + + ret = find_vdi_name(s, s->name, snap_id, snap_tag, &vid, true, + &local_err); + if (ret) { + return ret; + } + + fd = connect_to_sdog(s, &local_err); + if (fd < 0) { + error_report_err(local_err); + return -1; + } + + ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, + buf, &wlen, &rlen); + closesocket(fd); + if (ret) { + return ret; + } + + switch (rsp->result) { + case SD_RES_NO_VDI: + error_report("%s was already deleted", s->name); + case SD_RES_SUCCESS: + break; + default: + error_report("%s, %s", sd_strerror(rsp->result), s->name); + return -1; + } + + return ret; } static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) @@ -2577,28 +2794,36 @@ static coroutine_fn int sd_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) { SheepdogAIOCB *acb; - QEMUIOVector dummy; BDRVSheepdogState *s = bs->opaque; int ret; + QEMUIOVector discard_iov; + struct iovec iov; + uint32_t zero = 0; if (!s->discard_supported) { return 0; } - acb = sd_aio_setup(bs, &dummy, sector_num, nb_sectors); + memset(&discard_iov, 0, sizeof(discard_iov)); + memset(&iov, 0, sizeof(iov)); + iov.iov_base = &zero; + iov.iov_len = sizeof(zero); + discard_iov.iov = &iov; + discard_iov.niov = 1; + acb = sd_aio_setup(bs, &discard_iov, sector_num, nb_sectors); acb->aiocb_type = AIOCB_DISCARD_OBJ; acb->aio_done_func = sd_finish_aiocb; retry: - if (check_overwrapping_aiocb(s, acb)) { - qemu_co_queue_wait(&s->overwrapping_queue); + if (check_overlapping_aiocb(s, acb)) { + qemu_co_queue_wait(&s->overlapping_queue); goto retry; } ret = sd_co_rw_vector(acb); if (ret <= 0) { QLIST_REMOVE(acb, aiocb_siblings); - qemu_co_queue_restart_all(&s->overwrapping_queue); + qemu_co_queue_restart_all(&s->overlapping_queue); qemu_aio_unref(acb); return ret; } @@ -2606,14 +2831,14 @@ retry: qemu_coroutine_yield(); QLIST_REMOVE(acb, aiocb_siblings); - qemu_co_queue_restart_all(&s->overwrapping_queue); + qemu_co_queue_restart_all(&s->overlapping_queue); return acb->ret; } static coroutine_fn int64_t sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, - int *pnum) + int *pnum, BlockDriverState **file) { BDRVSheepdogState *s = bs->opaque; SheepdogInode *inode = &s->inode; @@ -2644,6 +2869,9 @@ sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, if (*pnum > nb_sectors) { *pnum = nb_sectors; } + if (ret > 0 && ret & BDRV_BLOCK_OFFSET_VALID) { + *file = bs; + } return ret; } @@ -2703,6 +2931,9 @@ static BlockDriver bdrv_sheepdog = { .instance_size = sizeof(BDRVSheepdogState), .bdrv_needs_filename = true, .bdrv_file_open = sd_open, + .bdrv_reopen_prepare = sd_reopen_prepare, + .bdrv_reopen_commit = sd_reopen_commit, + .bdrv_reopen_abort = sd_reopen_abort, .bdrv_close = sd_close, .bdrv_create = sd_create, .bdrv_has_zero_init = bdrv_has_zero_init_1, @@ -2736,6 +2967,9 @@ static BlockDriver bdrv_sheepdog_tcp = { .instance_size = sizeof(BDRVSheepdogState), .bdrv_needs_filename = true, .bdrv_file_open = sd_open, + .bdrv_reopen_prepare = sd_reopen_prepare, + .bdrv_reopen_commit = sd_reopen_commit, + .bdrv_reopen_abort = sd_reopen_abort, .bdrv_close = sd_close, .bdrv_create = sd_create, .bdrv_has_zero_init = bdrv_has_zero_init_1, @@ -2769,6 +3003,9 @@ static BlockDriver bdrv_sheepdog_unix = { .instance_size = sizeof(BDRVSheepdogState), .bdrv_needs_filename = true, .bdrv_file_open = sd_open, + .bdrv_reopen_prepare = sd_reopen_prepare, + .bdrv_reopen_commit = sd_reopen_commit, + .bdrv_reopen_abort = sd_reopen_abort, .bdrv_close = sd_close, .bdrv_create = sd_create, .bdrv_has_zero_init = bdrv_has_zero_init_1, diff --git a/qemu/block/snapshot.c b/qemu/block/snapshot.c index 49e143e99..e9d721df6 100644 --- a/qemu/block/snapshot.c +++ b/qemu/block/snapshot.c @@ -22,8 +22,10 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "block/snapshot.h" #include "block/block_int.h" +#include "qapi/error.h" #include "qapi/qmp/qerror.h" QemuOptsList internal_snapshot_opts = { @@ -149,7 +151,7 @@ int bdrv_can_snapshot(BlockDriverState *bs) if (!drv->bdrv_snapshot_create) { if (bs->file != NULL) { - return bdrv_can_snapshot(bs->file); + return bdrv_can_snapshot(bs->file->bs); } return 0; } @@ -168,7 +170,7 @@ int bdrv_snapshot_create(BlockDriverState *bs, return drv->bdrv_snapshot_create(bs, sn_info); } if (bs->file) { - return bdrv_snapshot_create(bs->file, sn_info); + return bdrv_snapshot_create(bs->file->bs, sn_info); } return -ENOTSUP; } @@ -188,10 +190,10 @@ int bdrv_snapshot_goto(BlockDriverState *bs, if (bs->file) { drv->bdrv_close(bs); - ret = bdrv_snapshot_goto(bs->file, snapshot_id); + ret = bdrv_snapshot_goto(bs->file->bs, snapshot_id); open_ret = drv->bdrv_open(bs, NULL, bs->open_flags, NULL); if (open_ret < 0) { - bdrv_unref(bs->file); + bdrv_unref(bs->file->bs); bs->drv = NULL; return open_ret; } @@ -229,6 +231,8 @@ int bdrv_snapshot_delete(BlockDriverState *bs, Error **errp) { BlockDriver *drv = bs->drv; + int ret; + if (!drv) { error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); return -ENOMEDIUM; @@ -239,23 +243,26 @@ int bdrv_snapshot_delete(BlockDriverState *bs, } /* drain all pending i/o before deleting snapshot */ - bdrv_drain(bs); + bdrv_drained_begin(bs); if (drv->bdrv_snapshot_delete) { - return drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp); - } - if (bs->file) { - return bdrv_snapshot_delete(bs->file, snapshot_id, name, errp); + ret = drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp); + } else if (bs->file) { + ret = bdrv_snapshot_delete(bs->file->bs, snapshot_id, name, errp); + } else { + error_setg(errp, "Block format '%s' used by device '%s' " + "does not support internal snapshot deletion", + drv->format_name, bdrv_get_device_name(bs)); + ret = -ENOTSUP; } - error_setg(errp, "Block format '%s' used by device '%s' " - "does not support internal snapshot deletion", - drv->format_name, bdrv_get_device_name(bs)); - return -ENOTSUP; + + bdrv_drained_end(bs); + return ret; } -void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs, - const char *id_or_name, - Error **errp) +int bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs, + const char *id_or_name, + Error **errp) { int ret; Error *local_err = NULL; @@ -270,6 +277,7 @@ void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs, if (ret < 0) { error_propagate(errp, local_err); } + return ret; } int bdrv_snapshot_list(BlockDriverState *bs, @@ -283,7 +291,7 @@ int bdrv_snapshot_list(BlockDriverState *bs, return drv->bdrv_snapshot_list(bs, psn_info); } if (bs->file) { - return bdrv_snapshot_list(bs->file, psn_info); + return bdrv_snapshot_list(bs->file->bs, psn_info); } return -ENOTSUP; } @@ -356,3 +364,130 @@ int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs, return ret; } + + +/* Group operations. All block drivers are involved. + * These functions will properly handle dataplane (take aio_context_acquire + * when appropriate for appropriate block drivers) */ + +bool bdrv_all_can_snapshot(BlockDriverState **first_bad_bs) +{ + bool ok = true; + BlockDriverState *bs = NULL; + + while (ok && (bs = bdrv_next(bs))) { + AioContext *ctx = bdrv_get_aio_context(bs); + + aio_context_acquire(ctx); + if (bdrv_is_inserted(bs) && !bdrv_is_read_only(bs)) { + ok = bdrv_can_snapshot(bs); + } + aio_context_release(ctx); + } + + *first_bad_bs = bs; + return ok; +} + +int bdrv_all_delete_snapshot(const char *name, BlockDriverState **first_bad_bs, + Error **err) +{ + int ret = 0; + BlockDriverState *bs = NULL; + QEMUSnapshotInfo sn1, *snapshot = &sn1; + + while (ret == 0 && (bs = bdrv_next(bs))) { + AioContext *ctx = bdrv_get_aio_context(bs); + + aio_context_acquire(ctx); + if (bdrv_can_snapshot(bs) && + bdrv_snapshot_find(bs, snapshot, name) >= 0) { + ret = bdrv_snapshot_delete_by_id_or_name(bs, name, err); + } + aio_context_release(ctx); + } + + *first_bad_bs = bs; + return ret; +} + + +int bdrv_all_goto_snapshot(const char *name, BlockDriverState **first_bad_bs) +{ + int err = 0; + BlockDriverState *bs = NULL; + + while (err == 0 && (bs = bdrv_next(bs))) { + AioContext *ctx = bdrv_get_aio_context(bs); + + aio_context_acquire(ctx); + if (bdrv_can_snapshot(bs)) { + err = bdrv_snapshot_goto(bs, name); + } + aio_context_release(ctx); + } + + *first_bad_bs = bs; + return err; +} + +int bdrv_all_find_snapshot(const char *name, BlockDriverState **first_bad_bs) +{ + QEMUSnapshotInfo sn; + int err = 0; + BlockDriverState *bs = NULL; + + while (err == 0 && (bs = bdrv_next(bs))) { + AioContext *ctx = bdrv_get_aio_context(bs); + + aio_context_acquire(ctx); + if (bdrv_can_snapshot(bs)) { + err = bdrv_snapshot_find(bs, &sn, name); + } + aio_context_release(ctx); + } + + *first_bad_bs = bs; + return err; +} + +int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn, + BlockDriverState *vm_state_bs, + uint64_t vm_state_size, + BlockDriverState **first_bad_bs) +{ + int err = 0; + BlockDriverState *bs = NULL; + + while (err == 0 && (bs = bdrv_next(bs))) { + AioContext *ctx = bdrv_get_aio_context(bs); + + aio_context_acquire(ctx); + if (bs == vm_state_bs) { + sn->vm_state_size = vm_state_size; + err = bdrv_snapshot_create(bs, sn); + } else if (bdrv_can_snapshot(bs)) { + sn->vm_state_size = 0; + err = bdrv_snapshot_create(bs, sn); + } + aio_context_release(ctx); + } + + *first_bad_bs = bs; + return err; +} + +BlockDriverState *bdrv_all_find_vmstate_bs(void) +{ + bool not_found = true; + BlockDriverState *bs = NULL; + + while (not_found && (bs = bdrv_next(bs))) { + AioContext *ctx = bdrv_get_aio_context(bs); + + aio_context_acquire(ctx); + not_found = !bdrv_can_snapshot(bs); + aio_context_release(ctx); + } + return bs; +} diff --git a/qemu/block/ssh.c b/qemu/block/ssh.c index 8d0673903..06928ed93 100644 --- a/qemu/block/ssh.c +++ b/qemu/block/ssh.c @@ -22,14 +22,13 @@ * THE SOFTWARE. */ -#include -#include -#include +#include "qemu/osdep.h" #include #include #include "block/block_int.h" +#include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/sockets.h" #include "qemu/uri.h" @@ -193,7 +192,7 @@ sftp_error_report(BDRVSSHState *s, const char *fs, ...) static int parse_uri(const char *filename, QDict *options, Error **errp) { URI *uri = NULL; - QueryParams *qp = NULL; + QueryParams *qp; int i; uri = uri_parse(filename); @@ -249,9 +248,6 @@ static int parse_uri(const char *filename, QDict *options, Error **errp) return 0; err: - if (qp) { - query_params_free(qp); - } if (uri) { uri_free(uri); } @@ -803,14 +799,15 @@ static coroutine_fn void set_fd_handler(BDRVSSHState *s, BlockDriverState *bs) rd_handler, wr_handler); aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock, - rd_handler, wr_handler, co); + false, rd_handler, wr_handler, co); } static coroutine_fn void clear_fd_handler(BDRVSSHState *s, BlockDriverState *bs) { DPRINTF("s->sock=%d", s->sock); - aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock, NULL, NULL, NULL); + aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock, + false, NULL, NULL, NULL); } /* A non-blocking call returned EAGAIN, so yield, ensuring the diff --git a/qemu/block/stream.c b/qemu/block/stream.c index ab0bd057f..332b9a183 100644 --- a/qemu/block/stream.c +++ b/qemu/block/stream.c @@ -11,11 +11,14 @@ * */ +#include "qemu/osdep.h" #include "trace.h" #include "block/block_int.h" #include "block/blockjob.h" +#include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qemu/ratelimit.h" +#include "sysemu/block-backend.h" enum { /* @@ -52,34 +55,6 @@ static int coroutine_fn stream_populate(BlockDriverState *bs, return bdrv_co_copy_on_readv(bs, sector_num, nb_sectors, &qiov); } -static void close_unused_images(BlockDriverState *top, BlockDriverState *base, - const char *base_id) -{ - BlockDriverState *intermediate; - intermediate = top->backing_hd; - - /* Must assign before bdrv_delete() to prevent traversing dangling pointer - * while we delete backing image instances. - */ - bdrv_set_backing_hd(top, base); - - while (intermediate) { - BlockDriverState *unused; - - /* reached base */ - if (intermediate == base) { - break; - } - - unused = intermediate; - intermediate = intermediate->backing_hd; - bdrv_set_backing_hd(unused, NULL); - bdrv_unref(unused); - } - - bdrv_refresh_limits(top, NULL); -} - typedef struct { int ret; bool reached_end; @@ -101,7 +76,7 @@ static void stream_complete(BlockJob *job, void *opaque) } } data->ret = bdrv_change_backing_file(job->bs, base_id, base_fmt); - close_unused_images(job->bs, base, base_id); + bdrv_set_backing_hd(job->bs, base); } g_free(s->backing_file_str); @@ -115,21 +90,21 @@ static void coroutine_fn stream_run(void *opaque) StreamCompleteData *data; BlockDriverState *bs = s->common.bs; BlockDriverState *base = s->base; - int64_t sector_num, end; + int64_t sector_num = 0; + int64_t end = -1; int error = 0; int ret = 0; int n = 0; void *buf; - if (!bs->backing_hd) { - block_job_completed(&s->common, 0); - return; + if (!bs->backing) { + goto out; } s->common.len = bdrv_getlength(bs); if (s->common.len < 0) { - block_job_completed(&s->common, s->common.len); - return; + ret = s->common.len; + goto out; } end = s->common.len >> BDRV_SECTOR_BITS; @@ -166,7 +141,7 @@ wait: } else if (ret >= 0) { /* Copy if allocated in the intermediate images. Limit to the * known-unallocated area [sector_num, sector_num+n). */ - ret = bdrv_is_allocated_above(bs->backing_hd, base, + ret = bdrv_is_allocated_above(backing_bs(bs), base, sector_num, n, &n); /* Finish early if end of backing file has been reached */ @@ -216,6 +191,7 @@ wait: qemu_vfree(buf); +out: /* Modify backing chain and close BDSes in main loop */ data = g_malloc(sizeof(*data)); data->ret = ret; @@ -250,7 +226,7 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base, if ((on_error == BLOCKDEV_ON_ERROR_STOP || on_error == BLOCKDEV_ON_ERROR_ENOSPC) && - !bdrv_iostatus_is_enabled(bs)) { + (!bs->blk || !blk_iostatus_is_enabled(bs->blk))) { error_setg(errp, QERR_INVALID_PARAMETER, "on-error"); return; } diff --git a/qemu/block/throttle-groups.c b/qemu/block/throttle-groups.c index 1abc6fcae..4920e0949 100644 --- a/qemu/block/throttle-groups.c +++ b/qemu/block/throttle-groups.c @@ -22,6 +22,7 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include "block/throttle-groups.h" #include "qemu/queue.h" #include "qemu/thread.h" @@ -33,8 +34,7 @@ * its own locking. * * This locking is however handled internally in this file, so it's - * mostly transparent to outside users (but see the documentation in - * throttle_groups_lock()). + * transparent to outside users. * * The whole ThrottleGroup structure is private and invisible to * outside users, that only use it through its ThrottleState. @@ -76,9 +76,9 @@ static QTAILQ_HEAD(, ThrottleGroup) throttle_groups = * created. * * @name: the name of the ThrottleGroup - * @ret: the ThrottleGroup + * @ret: the ThrottleState member of the ThrottleGroup */ -static ThrottleGroup *throttle_group_incref(const char *name) +ThrottleState *throttle_group_incref(const char *name) { ThrottleGroup *tg = NULL; ThrottleGroup *iter; @@ -108,7 +108,7 @@ static ThrottleGroup *throttle_group_incref(const char *name) qemu_mutex_unlock(&throttle_groups_lock); - return tg; + return &tg->ts; } /* Decrease the reference count of a ThrottleGroup. @@ -116,10 +116,12 @@ static ThrottleGroup *throttle_group_incref(const char *name) * When the reference count reaches zero the ThrottleGroup is * destroyed. * - * @tg: The ThrottleGroup to unref + * @ts: The ThrottleGroup to unref, given by its ThrottleState member */ -static void throttle_group_unref(ThrottleGroup *tg) +void throttle_group_unref(ThrottleState *ts) { + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); + qemu_mutex_lock(&throttle_groups_lock); if (--tg->refcount == 0) { QTAILQ_REMOVE(&throttle_groups, tg, list); @@ -401,7 +403,8 @@ static void write_timer_cb(void *opaque) void throttle_group_register_bs(BlockDriverState *bs, const char *groupname) { int i; - ThrottleGroup *tg = throttle_group_incref(groupname); + ThrottleState *ts = throttle_group_incref(groupname); + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); int clock_type = QEMU_CLOCK_REALTIME; if (qtest_enabled()) { @@ -409,7 +412,7 @@ void throttle_group_register_bs(BlockDriverState *bs, const char *groupname) clock_type = QEMU_CLOCK_VIRTUAL; } - bs->throttle_state = &tg->ts; + bs->throttle_state = ts; qemu_mutex_lock(&tg->lock); /* If the ThrottleGroup is new set this BlockDriverState as the token */ @@ -435,6 +438,9 @@ void throttle_group_register_bs(BlockDriverState *bs, const char *groupname) * list, destroying the timers and setting the throttle_state pointer * to NULL. * + * The BlockDriverState must not have pending throttled requests, so + * the caller has to drain them first. + * * The group will be destroyed if it's empty after this operation. * * @bs: the BlockDriverState to remove @@ -444,6 +450,10 @@ void throttle_group_unregister_bs(BlockDriverState *bs) ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); int i; + assert(bs->pending_reqs[0] == 0 && bs->pending_reqs[1] == 0); + assert(qemu_co_queue_empty(&bs->throttled_reqs[0])); + assert(qemu_co_queue_empty(&bs->throttled_reqs[1])); + qemu_mutex_lock(&tg->lock); for (i = 0; i < 2; i++) { if (tg->tokens[i] == bs) { @@ -461,38 +471,10 @@ void throttle_group_unregister_bs(BlockDriverState *bs) throttle_timers_destroy(&bs->throttle_timers); qemu_mutex_unlock(&tg->lock); - throttle_group_unref(tg); + throttle_group_unref(&tg->ts); bs->throttle_state = NULL; } -/* Acquire the lock of this throttling group. - * - * You won't normally need to use this. None of the functions from the - * ThrottleGroup API require you to acquire the lock since all of them - * deal with it internally. - * - * This should only be used in exceptional cases when you want to - * access the protected fields of a BlockDriverState directly - * (e.g. bdrv_swap()). - * - * @bs: a BlockDriverState that is member of the group - */ -void throttle_group_lock(BlockDriverState *bs) -{ - ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); - qemu_mutex_lock(&tg->lock); -} - -/* Release the lock of this throttling group. - * - * See the comments in throttle_group_lock(). - */ -void throttle_group_unlock(BlockDriverState *bs) -{ - ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); - qemu_mutex_unlock(&tg->lock); -} - static void throttle_groups_init(void) { qemu_mutex_init(&throttle_groups_lock); diff --git a/qemu/block/vdi.c b/qemu/block/vdi.c index 7642ef359..75d4819ed 100644 --- a/qemu/block/vdi.c +++ b/qemu/block/vdi.c @@ -49,11 +49,14 @@ * so this seems to be reasonable. */ -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "qapi/error.h" #include "block/block_int.h" +#include "sysemu/block-backend.h" #include "qemu/module.h" #include "migration/migration.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" +#include "qemu/cutils.h" #if defined(CONFIG_UUID) #include @@ -399,7 +402,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, logout("\n"); - ret = bdrv_read(bs->file, 0, (uint8_t *)&header, 1); + ret = bdrv_read(bs->file->bs, 0, (uint8_t *)&header, 1); if (ret < 0) { goto fail; } @@ -490,13 +493,14 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, bmap_size = header.blocks_in_image * sizeof(uint32_t); bmap_size = DIV_ROUND_UP(bmap_size, SECTOR_SIZE); - s->bmap = qemu_try_blockalign(bs->file, bmap_size * SECTOR_SIZE); + s->bmap = qemu_try_blockalign(bs->file->bs, bmap_size * SECTOR_SIZE); if (s->bmap == NULL) { ret = -ENOMEM; goto fail; } - ret = bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, bmap_size); + ret = bdrv_read(bs->file->bs, s->bmap_sector, (uint8_t *)s->bmap, + bmap_size); if (ret < 0) { goto fail_free_bmap; } @@ -525,7 +529,7 @@ static int vdi_reopen_prepare(BDRVReopenState *state, } static int64_t coroutine_fn vdi_co_get_block_status(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, int *pnum) + int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) { /* TODO: Check for too large sector_num (in bdrv_is_allocated or here). */ BDRVVdiState *s = (BDRVVdiState *)bs->opaque; @@ -549,6 +553,7 @@ static int64_t coroutine_fn vdi_co_get_block_status(BlockDriverState *bs, offset = s->header.offset_data + (uint64_t)bmap_entry * s->block_size + sector_in_block * SECTOR_SIZE; + *file = bs->file->bs; return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset; } @@ -585,7 +590,7 @@ static int vdi_co_read(BlockDriverState *bs, uint64_t offset = s->header.offset_data / SECTOR_SIZE + (uint64_t)bmap_entry * s->block_sectors + sector_in_block; - ret = bdrv_read(bs->file, offset, buf, n_sectors); + ret = bdrv_read(bs->file->bs, offset, buf, n_sectors); } logout("%u sectors read\n", n_sectors); @@ -653,7 +658,7 @@ static int vdi_co_write(BlockDriverState *bs, * acquire the lock and thus the padded cluster is written before * the other coroutines can write to the affected area. */ qemu_co_mutex_lock(&s->write_lock); - ret = bdrv_write(bs->file, offset, block, s->block_sectors); + ret = bdrv_write(bs->file->bs, offset, block, s->block_sectors); qemu_co_mutex_unlock(&s->write_lock); } else { uint64_t offset = s->header.offset_data / SECTOR_SIZE + @@ -669,7 +674,7 @@ static int vdi_co_write(BlockDriverState *bs, * that that write operation has returned (there may be other writes * in flight, but they do not concern this very operation). */ qemu_co_mutex_unlock(&s->write_lock); - ret = bdrv_write(bs->file, offset, buf, n_sectors); + ret = bdrv_write(bs->file->bs, offset, buf, n_sectors); } nb_sectors -= n_sectors; @@ -694,7 +699,7 @@ static int vdi_co_write(BlockDriverState *bs, assert(VDI_IS_ALLOCATED(bmap_first)); *header = s->header; vdi_header_to_le(header); - ret = bdrv_write(bs->file, 0, block, 1); + ret = bdrv_write(bs->file->bs, 0, block, 1); g_free(block); block = NULL; @@ -712,7 +717,7 @@ static int vdi_co_write(BlockDriverState *bs, base = ((uint8_t *)&s->bmap[0]) + bmap_first * SECTOR_SIZE; logout("will write %u block map sectors starting from entry %u\n", n_sectors, bmap_first); - ret = bdrv_write(bs->file, offset, base, n_sectors); + ret = bdrv_write(bs->file->bs, offset, base, n_sectors); } return ret; @@ -730,7 +735,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp) size_t bmap_size; int64_t offset = 0; Error *local_err = NULL; - BlockDriverState *bs = NULL; + BlockBackend *blk = NULL; uint32_t *bmap = NULL; logout("\n"); @@ -763,13 +768,17 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp) error_propagate(errp, local_err); goto exit; } - ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, - NULL, &local_err); - if (ret < 0) { + + blk = blk_new_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); + if (blk == NULL) { error_propagate(errp, local_err); + ret = -EIO; goto exit; } + blk_set_allow_write_beyond_eof(blk, true); + /* We need enough blocks to store the given disk size, so always round up. */ blocks = DIV_ROUND_UP(bytes, block_size); @@ -799,7 +808,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp) vdi_header_print(&header); #endif vdi_header_to_le(&header); - ret = bdrv_pwrite_sync(bs, offset, &header, sizeof(header)); + ret = blk_pwrite(blk, offset, &header, sizeof(header)); if (ret < 0) { error_setg(errp, "Error writing header to %s", filename); goto exit; @@ -820,7 +829,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp) bmap[i] = VDI_UNALLOCATED; } } - ret = bdrv_pwrite_sync(bs, offset, bmap, bmap_size); + ret = blk_pwrite(blk, offset, bmap, bmap_size); if (ret < 0) { error_setg(errp, "Error writing bmap to %s", filename); goto exit; @@ -829,7 +838,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp) } if (image_type == VDI_TYPE_STATIC) { - ret = bdrv_truncate(bs, offset + blocks * block_size); + ret = blk_truncate(blk, offset + blocks * block_size); if (ret < 0) { error_setg(errp, "Failed to statically allocate %s", filename); goto exit; @@ -837,7 +846,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp) } exit: - bdrv_unref(bs); + blk_unref(blk); g_free(bmap); return ret; } diff --git a/qemu/block/vhdx-endian.c b/qemu/block/vhdx-endian.c index 0640d3f4a..da33cd38e 100644 --- a/qemu/block/vhdx-endian.c +++ b/qemu/block/vhdx-endian.c @@ -15,6 +15,7 @@ * */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "block/block_int.h" #include "block/vhdx.h" diff --git a/qemu/block/vhdx-log.c b/qemu/block/vhdx-log.c index 47fec63c6..7ea7187fc 100644 --- a/qemu/block/vhdx-log.c +++ b/qemu/block/vhdx-log.c @@ -17,6 +17,8 @@ * See the COPYING.LIB file in the top-level directory. * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "block/block_int.h" #include "qemu/error-report.h" @@ -81,7 +83,7 @@ static int vhdx_log_peek_hdr(BlockDriverState *bs, VHDXLogEntries *log, offset = log->offset + read; - ret = bdrv_pread(bs->file, offset, hdr, sizeof(VHDXLogEntryHeader)); + ret = bdrv_pread(bs->file->bs, offset, hdr, sizeof(VHDXLogEntryHeader)); if (ret < 0) { goto exit; } @@ -141,7 +143,7 @@ static int vhdx_log_read_sectors(BlockDriverState *bs, VHDXLogEntries *log, } offset = log->offset + read; - ret = bdrv_pread(bs->file, offset, buffer, VHDX_LOG_SECTOR_SIZE); + ret = bdrv_pread(bs->file->bs, offset, buffer, VHDX_LOG_SECTOR_SIZE); if (ret < 0) { goto exit; } @@ -191,7 +193,8 @@ static int vhdx_log_write_sectors(BlockDriverState *bs, VHDXLogEntries *log, /* full */ break; } - ret = bdrv_pwrite(bs->file, offset, buffer_tmp, VHDX_LOG_SECTOR_SIZE); + ret = bdrv_pwrite(bs->file->bs, offset, buffer_tmp, + VHDX_LOG_SECTOR_SIZE); if (ret < 0) { goto exit; } @@ -353,7 +356,7 @@ static int vhdx_log_read_desc(BlockDriverState *bs, BDRVVHDXState *s, } desc_sectors = vhdx_compute_desc_sectors(hdr.descriptor_count); - desc_entries = qemu_try_blockalign(bs->file, + desc_entries = qemu_try_blockalign(bs->file->bs, desc_sectors * VHDX_LOG_SECTOR_SIZE); if (desc_entries == NULL) { ret = -ENOMEM; @@ -462,7 +465,7 @@ static int vhdx_log_flush_desc(BlockDriverState *bs, VHDXLogDescriptor *desc, /* count is only > 1 if we are writing zeroes */ for (i = 0; i < count; i++) { - ret = bdrv_pwrite_sync(bs->file, file_offset, buffer, + ret = bdrv_pwrite_sync(bs->file->bs, file_offset, buffer, VHDX_LOG_SECTOR_SIZE); if (ret < 0) { goto exit; @@ -509,7 +512,7 @@ static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s, /* if the log shows a FlushedFileOffset larger than our current file * size, then that means the file has been truncated / corrupted, and * we must refused to open it / use it */ - if (hdr_tmp.flushed_file_offset > bdrv_getlength(bs->file)) { + if (hdr_tmp.flushed_file_offset > bdrv_getlength(bs->file->bs)) { ret = -EINVAL; goto exit; } @@ -539,12 +542,12 @@ static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s, goto exit; } } - if (bdrv_getlength(bs->file) < desc_entries->hdr.last_file_offset) { + if (bdrv_getlength(bs->file->bs) < desc_entries->hdr.last_file_offset) { new_file_size = desc_entries->hdr.last_file_offset; if (new_file_size % (1024*1024)) { /* round up to nearest 1MB boundary */ new_file_size = ((new_file_size >> 20) + 1) << 20; - bdrv_truncate(bs->file, new_file_size); + bdrv_truncate(bs->file->bs, new_file_size); } } qemu_vfree(desc_entries); @@ -783,12 +786,13 @@ int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed, if (logs.valid) { if (bs->read_only) { ret = -EPERM; - error_setg_errno(errp, EPERM, - "VHDX image file '%s' opened read-only, but " - "contains a log that needs to be replayed. To " - "replay the log, execute:\n qemu-img check -r " - "all '%s'", - bs->filename, bs->filename); + error_setg(errp, + "VHDX image file '%s' opened read-only, but " + "contains a log that needs to be replayed", + bs->filename); + error_append_hint(errp, "To replay the log, run:\n" + "qemu-img check -r all '%s'\n", + bs->filename); goto exit; } /* now flush the log */ @@ -908,8 +912,8 @@ static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s, .sequence_number = s->log.sequence, .descriptor_count = sectors, .reserved = 0, - .flushed_file_offset = bdrv_getlength(bs->file), - .last_file_offset = bdrv_getlength(bs->file), + .flushed_file_offset = bdrv_getlength(bs->file->bs), + .last_file_offset = bdrv_getlength(bs->file->bs), }; new_hdr.log_guid = header->log_guid; @@ -940,7 +944,7 @@ static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s, if (i == 0 && leading_length) { /* partial sector at the front of the buffer */ - ret = bdrv_pread(bs->file, file_offset, merged_sector, + ret = bdrv_pread(bs->file->bs, file_offset, merged_sector, VHDX_LOG_SECTOR_SIZE); if (ret < 0) { goto exit; @@ -950,7 +954,7 @@ static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s, sector_write = merged_sector; } else if (i == sectors - 1 && trailing_length) { /* partial sector at the end of the buffer */ - ret = bdrv_pread(bs->file, + ret = bdrv_pread(bs->file->bs, file_offset, merged_sector + trailing_length, VHDX_LOG_SECTOR_SIZE - trailing_length); diff --git a/qemu/block/vhdx.c b/qemu/block/vhdx.c index 0776de717..2b7b33240 100644 --- a/qemu/block/vhdx.c +++ b/qemu/block/vhdx.c @@ -15,8 +15,11 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "block/block_int.h" +#include "sysemu/block-backend.h" #include "qemu/module.h" #include "qemu/crc32c.h" #include "block/vhdx.h" @@ -263,10 +266,10 @@ static void vhdx_region_unregister_all(BDRVVHDXState *s) static void vhdx_set_shift_bits(BDRVVHDXState *s) { - s->logical_sector_size_bits = 31 - clz32(s->logical_sector_size); - s->sectors_per_block_bits = 31 - clz32(s->sectors_per_block); - s->chunk_ratio_bits = 63 - clz64(s->chunk_ratio); - s->block_size_bits = 31 - clz32(s->block_size); + s->logical_sector_size_bits = ctz32(s->logical_sector_size); + s->sectors_per_block_bits = ctz32(s->sectors_per_block); + s->chunk_ratio_bits = ctz64(s->chunk_ratio); + s->block_size_bits = ctz32(s->block_size); } /* @@ -375,7 +378,7 @@ static int vhdx_update_header(BlockDriverState *bs, BDRVVHDXState *s, inactive_header->log_guid = *log_guid; } - ret = vhdx_write_header(bs->file, inactive_header, header_offset, true); + ret = vhdx_write_header(bs->file->bs, inactive_header, header_offset, true); if (ret < 0) { goto exit; } @@ -427,7 +430,8 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s, /* We have to read the whole VHDX_HEADER_SIZE instead of * sizeof(VHDXHeader), because the checksum is over the whole * region */ - ret = bdrv_pread(bs->file, VHDX_HEADER1_OFFSET, buffer, VHDX_HEADER_SIZE); + ret = bdrv_pread(bs->file->bs, VHDX_HEADER1_OFFSET, buffer, + VHDX_HEADER_SIZE); if (ret < 0) { goto fail; } @@ -443,7 +447,8 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s, } } - ret = bdrv_pread(bs->file, VHDX_HEADER2_OFFSET, buffer, VHDX_HEADER_SIZE); + ret = bdrv_pread(bs->file->bs, VHDX_HEADER2_OFFSET, buffer, + VHDX_HEADER_SIZE); if (ret < 0) { goto fail; } @@ -516,7 +521,7 @@ static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s) * whole block */ buffer = qemu_blockalign(bs, VHDX_HEADER_BLOCK_SIZE); - ret = bdrv_pread(bs->file, VHDX_REGION_TABLE_OFFSET, buffer, + ret = bdrv_pread(bs->file->bs, VHDX_REGION_TABLE_OFFSET, buffer, VHDX_HEADER_BLOCK_SIZE); if (ret < 0) { goto fail; @@ -629,7 +634,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) buffer = qemu_blockalign(bs, VHDX_METADATA_TABLE_MAX_SIZE); - ret = bdrv_pread(bs->file, s->metadata_rt.file_offset, buffer, + ret = bdrv_pread(bs->file->bs, s->metadata_rt.file_offset, buffer, VHDX_METADATA_TABLE_MAX_SIZE); if (ret < 0) { goto exit; @@ -732,7 +737,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) goto exit; } - ret = bdrv_pread(bs->file, + ret = bdrv_pread(bs->file->bs, s->metadata_entries.file_parameters_entry.offset + s->metadata_rt.file_offset, &s->params, @@ -767,7 +772,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) /* determine virtual disk size, logical sector size, * and phys sector size */ - ret = bdrv_pread(bs->file, + ret = bdrv_pread(bs->file->bs, s->metadata_entries.virtual_disk_size_entry.offset + s->metadata_rt.file_offset, &s->virtual_disk_size, @@ -775,7 +780,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) if (ret < 0) { goto exit; } - ret = bdrv_pread(bs->file, + ret = bdrv_pread(bs->file->bs, s->metadata_entries.logical_sector_size_entry.offset + s->metadata_rt.file_offset, &s->logical_sector_size, @@ -783,7 +788,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) if (ret < 0) { goto exit; } - ret = bdrv_pread(bs->file, + ret = bdrv_pread(bs->file->bs, s->metadata_entries.phys_sector_size_entry.offset + s->metadata_rt.file_offset, &s->physical_sector_size, @@ -854,14 +859,8 @@ static void vhdx_calc_bat_entries(BDRVVHDXState *s) { uint32_t data_blocks_cnt, bitmap_blocks_cnt; - data_blocks_cnt = s->virtual_disk_size >> s->block_size_bits; - if (s->virtual_disk_size - (data_blocks_cnt << s->block_size_bits)) { - data_blocks_cnt++; - } - bitmap_blocks_cnt = data_blocks_cnt >> s->chunk_ratio_bits; - if (data_blocks_cnt - (bitmap_blocks_cnt << s->chunk_ratio_bits)) { - bitmap_blocks_cnt++; - } + data_blocks_cnt = DIV_ROUND_UP(s->virtual_disk_size, s->block_size); + bitmap_blocks_cnt = DIV_ROUND_UP(data_blocks_cnt, s->chunk_ratio); if (s->parent_entries) { s->bat_entries = bitmap_blocks_cnt * (s->chunk_ratio + 1); @@ -906,7 +905,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, QLIST_INIT(&s->regions); /* validate the file signature */ - ret = bdrv_pread(bs->file, 0, &signature, sizeof(uint64_t)); + ret = bdrv_pread(bs->file->bs, 0, &signature, sizeof(uint64_t)); if (ret < 0) { goto fail; } @@ -959,13 +958,13 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, } /* s->bat is freed in vhdx_close() */ - s->bat = qemu_try_blockalign(bs->file, s->bat_rt.length); + s->bat = qemu_try_blockalign(bs->file->bs, s->bat_rt.length); if (s->bat == NULL) { ret = -ENOMEM; goto fail; } - ret = bdrv_pread(bs->file, s->bat_offset, s->bat, s->bat_rt.length); + ret = bdrv_pread(bs->file->bs, s->bat_offset, s->bat, s->bat_rt.length); if (ret < 0) { goto fail; } @@ -1118,7 +1117,7 @@ static coroutine_fn int vhdx_co_readv(BlockDriverState *bs, int64_t sector_num, break; case PAYLOAD_BLOCK_FULLY_PRESENT: qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_readv(bs->file, + ret = bdrv_co_readv(bs->file->bs, sinfo.file_offset >> BDRV_SECTOR_BITS, sinfo.sectors_avail, &hd_qiov); qemu_co_mutex_lock(&s->lock); @@ -1156,12 +1155,12 @@ exit: static int vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s, uint64_t *new_offset) { - *new_offset = bdrv_getlength(bs->file); + *new_offset = bdrv_getlength(bs->file->bs); /* per the spec, the address for a block is in units of 1MB */ *new_offset = ROUND_UP(*new_offset, 1024 * 1024); - return bdrv_truncate(bs->file, *new_offset + s->block_size); + return bdrv_truncate(bs->file->bs, *new_offset + s->block_size); } /* @@ -1260,7 +1259,7 @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, /* Queue another write of zero buffers if the underlying file * does not zero-fill on file extension */ - if (bdrv_has_zero_init(bs->file) == 0) { + if (bdrv_has_zero_init(bs->file->bs) == 0) { use_zero_buffers = true; /* zero fill the front, if any */ @@ -1327,7 +1326,7 @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, } /* block exists, so we can just overwrite it */ qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_writev(bs->file, + ret = bdrv_co_writev(bs->file->bs, sinfo.file_offset >> BDRV_SECTOR_BITS, sectors_to_write, &hd_qiov); qemu_co_mutex_lock(&s->lock); @@ -1454,7 +1453,7 @@ static int vhdx_create_new_metadata(BlockDriverState *bs, uint32_t offset = 0; void *buffer = NULL; void *entry_buffer; - VHDXMetadataTableHeader *md_table;; + VHDXMetadataTableHeader *md_table; VHDXMetadataTableEntry *md_table_entry; /* Metadata entries */ @@ -1775,7 +1774,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp) gunichar2 *creator = NULL; glong creator_items; - BlockDriverState *bs; + BlockBackend *blk; char *type = NULL; VHDXImageType image_type; Error *local_err = NULL; @@ -1840,14 +1839,16 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp) goto exit; } - bs = NULL; - ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, - NULL, &local_err); - if (ret < 0) { + blk = blk_new_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); + if (blk == NULL) { error_propagate(errp, local_err); + ret = -EIO; goto exit; } + blk_set_allow_write_beyond_eof(blk, true); + /* Create (A) */ /* The creator field is optional, but may be useful for @@ -1855,13 +1856,13 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp) creator = g_utf8_to_utf16("QEMU v" QEMU_VERSION, -1, NULL, &creator_items, NULL); signature = cpu_to_le64(VHDX_FILE_SIGNATURE); - ret = bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature)); + ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature)); if (ret < 0) { goto delete_and_exit; } if (creator) { - ret = bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET + sizeof(signature), - creator, creator_items * sizeof(gunichar2)); + ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET + sizeof(signature), + creator, creator_items * sizeof(gunichar2)); if (ret < 0) { goto delete_and_exit; } @@ -1869,13 +1870,13 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp) /* Creates (B),(C) */ - ret = vhdx_create_new_headers(bs, image_size, log_size); + ret = vhdx_create_new_headers(blk_bs(blk), image_size, log_size); if (ret < 0) { goto delete_and_exit; } /* Creates (D),(E),(G) explicitly. (F) created as by-product */ - ret = vhdx_create_new_region_table(bs, image_size, block_size, 512, + ret = vhdx_create_new_region_table(blk_bs(blk), image_size, block_size, 512, log_size, use_zero_blocks, image_type, &metadata_offset); if (ret < 0) { @@ -1883,7 +1884,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp) } /* Creates (H) */ - ret = vhdx_create_new_metadata(bs, image_size, block_size, 512, + ret = vhdx_create_new_metadata(blk_bs(blk), image_size, block_size, 512, metadata_offset, image_type); if (ret < 0) { goto delete_and_exit; @@ -1891,7 +1892,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp) delete_and_exit: - bdrv_unref(bs); + blk_unref(blk); exit: g_free(type); g_free(creator); diff --git a/qemu/block/vmdk.c b/qemu/block/vmdk.c index fbaab67c8..45f9d3c5b 100644 --- a/qemu/block/vmdk.c +++ b/qemu/block/vmdk.c @@ -23,12 +23,15 @@ * THE SOFTWARE. */ -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "qapi/error.h" #include "block/block_int.h" +#include "sysemu/block-backend.h" #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "migration/migration.h" +#include "qemu/cutils.h" #include #include @@ -87,7 +90,7 @@ typedef struct { #define L2_CACHE_SIZE 16 typedef struct VmdkExtent { - BlockDriverState *file; + BdrvChild *file; bool flat; bool compressed; bool has_marker; @@ -222,7 +225,7 @@ static void vmdk_free_extents(BlockDriverState *bs) g_free(e->l1_backup_table); g_free(e->type); if (e->file != bs->file) { - bdrv_unref(e->file); + bdrv_unref_child(bs, e->file); } } g_free(s->extents); @@ -241,15 +244,17 @@ static void vmdk_free_last_extent(BlockDriverState *bs) static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent) { - char desc[DESC_SIZE]; + char *desc; uint32_t cid = 0xffffffff; const char *p_name, *cid_str; size_t cid_str_size; BDRVVmdkState *s = bs->opaque; int ret; - ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE); + desc = g_malloc0(DESC_SIZE); + ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE); if (ret < 0) { + g_free(desc); return 0; } @@ -268,50 +273,55 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent) sscanf(p_name, "%" SCNx32, &cid); } + g_free(desc); return cid; } static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid) { - char desc[DESC_SIZE], tmp_desc[DESC_SIZE]; + char *desc, *tmp_desc; char *p_name, *tmp_str; BDRVVmdkState *s = bs->opaque; - int ret; + int ret = 0; - ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE); + desc = g_malloc0(DESC_SIZE); + tmp_desc = g_malloc0(DESC_SIZE); + ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE); if (ret < 0) { - return ret; + goto out; } desc[DESC_SIZE - 1] = '\0'; tmp_str = strstr(desc, "parentCID"); if (tmp_str == NULL) { - return -EINVAL; + ret = -EINVAL; + goto out; } - pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str); + pstrcpy(tmp_desc, DESC_SIZE, tmp_str); p_name = strstr(desc, "CID"); if (p_name != NULL) { p_name += sizeof("CID"); - snprintf(p_name, sizeof(desc) - (p_name - desc), "%" PRIx32 "\n", cid); - pstrcat(desc, sizeof(desc), tmp_desc); + snprintf(p_name, DESC_SIZE - (p_name - desc), "%" PRIx32 "\n", cid); + pstrcat(desc, DESC_SIZE, tmp_desc); } - ret = bdrv_pwrite_sync(bs->file, s->desc_offset, desc, DESC_SIZE); - if (ret < 0) { - return ret; - } + ret = bdrv_pwrite_sync(bs->file->bs, s->desc_offset, desc, DESC_SIZE); - return 0; +out: + g_free(desc); + g_free(tmp_desc); + return ret; } static int vmdk_is_cid_valid(BlockDriverState *bs) { BDRVVmdkState *s = bs->opaque; - BlockDriverState *p_bs = bs->backing_hd; uint32_t cur_pcid; - if (!s->cid_checked && p_bs) { + if (!s->cid_checked && bs->backing) { + BlockDriverState *p_bs = bs->backing->bs; + cur_pcid = vmdk_read_cid(p_bs, 0); if (s->parent_cid != cur_pcid) { /* CID not valid */ @@ -335,15 +345,16 @@ static int vmdk_reopen_prepare(BDRVReopenState *state, static int vmdk_parent_open(BlockDriverState *bs) { char *p_name; - char desc[DESC_SIZE + 1]; + char *desc; BDRVVmdkState *s = bs->opaque; int ret; - desc[DESC_SIZE] = '\0'; - ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE); + desc = g_malloc0(DESC_SIZE + 1); + ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE); if (ret < 0) { - return ret; + goto out; } + ret = 0; p_name = strstr(desc, "parentFileNameHint"); if (p_name != NULL) { @@ -352,22 +363,26 @@ static int vmdk_parent_open(BlockDriverState *bs) p_name += sizeof("parentFileNameHint") + 1; end_name = strchr(p_name, '\"'); if (end_name == NULL) { - return -EINVAL; + ret = -EINVAL; + goto out; } if ((end_name - p_name) > sizeof(bs->backing_file) - 1) { - return -EINVAL; + ret = -EINVAL; + goto out; } pstrcpy(bs->backing_file, end_name - p_name + 1, p_name); } - return 0; +out: + g_free(desc); + return ret; } /* Create and append extent to the extent array. Return the added VmdkExtent * address. return NULL if allocation failed. */ static int vmdk_add_extent(BlockDriverState *bs, - BlockDriverState *file, bool flat, int64_t sectors, + BdrvChild *file, bool flat, int64_t sectors, int64_t l1_offset, int64_t l1_backup_offset, uint32_t l1_size, int l2_size, uint64_t cluster_sectors, @@ -392,7 +407,7 @@ static int vmdk_add_extent(BlockDriverState *bs, return -EFBIG; } - nb_sectors = bdrv_nb_sectors(file); + nb_sectors = bdrv_nb_sectors(file->bs); if (nb_sectors < 0) { return nb_sectors; } @@ -439,14 +454,14 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent, return -ENOMEM; } - ret = bdrv_pread(extent->file, + ret = bdrv_pread(extent->file->bs, extent->l1_table_offset, extent->l1_table, l1_size); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read l1 table from extent '%s'", - extent->file->filename); + extent->file->bs->filename); goto fail_l1; } for (i = 0; i < extent->l1_size; i++) { @@ -459,14 +474,14 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent, ret = -ENOMEM; goto fail_l1; } - ret = bdrv_pread(extent->file, + ret = bdrv_pread(extent->file->bs, extent->l1_backup_table_offset, extent->l1_backup_table, l1_size); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read l1 backup table from extent '%s'", - extent->file->filename); + extent->file->bs->filename); goto fail_l1b; } for (i = 0; i < extent->l1_size; i++) { @@ -485,7 +500,7 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent, } static int vmdk_open_vmfs_sparse(BlockDriverState *bs, - BlockDriverState *file, + BdrvChild *file, int flags, Error **errp) { int ret; @@ -493,11 +508,11 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs, VMDK3Header header; VmdkExtent *extent; - ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header)); + ret = bdrv_pread(file->bs, sizeof(magic), &header, sizeof(header)); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read header from file '%s'", - file->filename); + file->bs->filename); return ret; } ret = vmdk_add_extent(bs, file, false, @@ -559,7 +574,7 @@ static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset, } static int vmdk_open_vmdk4(BlockDriverState *bs, - BlockDriverState *file, + BdrvChild *file, int flags, QDict *options, Error **errp) { int ret; @@ -569,18 +584,19 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, VmdkExtent *extent; BDRVVmdkState *s = bs->opaque; int64_t l1_backup_offset = 0; + bool compressed; - ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header)); + ret = bdrv_pread(file->bs, sizeof(magic), &header, sizeof(header)); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read header from file '%s'", - file->filename); + file->bs->filename); return -EINVAL; } if (header.capacity == 0) { uint64_t desc_offset = le64_to_cpu(header.desc_offset); if (desc_offset) { - char *buf = vmdk_read_desc(file, desc_offset << 9, errp); + char *buf = vmdk_read_desc(file->bs, desc_offset << 9, errp); if (!buf) { return -EINVAL; } @@ -620,8 +636,8 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, } QEMU_PACKED eos_marker; } QEMU_PACKED footer; - ret = bdrv_pread(file, - bs->file->total_sectors * 512 - 1536, + ret = bdrv_pread(file->bs, + bs->file->bs->total_sectors * 512 - 1536, &footer, sizeof(footer)); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to read footer"); @@ -643,14 +659,14 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, header = footer.header; } + compressed = + le16_to_cpu(header.compressAlgorithm) == VMDK4_COMPRESSION_DEFLATE; if (le32_to_cpu(header.version) > 3) { - char buf[64]; - snprintf(buf, sizeof(buf), "VMDK version %" PRId32, - le32_to_cpu(header.version)); - error_setg(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, - bdrv_get_device_or_node_name(bs), "vmdk", buf); + error_setg(errp, "Unsupported VMDK version %" PRIu32, + le32_to_cpu(header.version)); return -ENOTSUP; - } else if (le32_to_cpu(header.version) == 3 && (flags & BDRV_O_RDWR)) { + } else if (le32_to_cpu(header.version) == 3 && (flags & BDRV_O_RDWR) && + !compressed) { /* VMware KB 2064959 explains that version 3 added support for * persistent changed block tracking (CBT), and backup software can * read it as version=1 if it doesn't care about the changed area @@ -675,7 +691,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, if (le32_to_cpu(header.flags) & VMDK4_FLAG_RGD) { l1_backup_offset = le64_to_cpu(header.rgd_offset) << 9; } - if (bdrv_nb_sectors(file) < le64_to_cpu(header.grain_offset)) { + if (bdrv_nb_sectors(file->bs) < le64_to_cpu(header.grain_offset)) { error_setg(errp, "File truncated, expecting at least %" PRId64 " bytes", (int64_t)(le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE)); @@ -739,8 +755,7 @@ static int vmdk_parse_description(const char *desc, const char *opt_name, } /* Open an extent file and append to bs array */ -static int vmdk_open_sparse(BlockDriverState *bs, - BlockDriverState *file, int flags, +static int vmdk_open_sparse(BlockDriverState *bs, BdrvChild *file, int flags, char *buf, QDict *options, Error **errp) { uint32_t magic; @@ -760,6 +775,17 @@ static int vmdk_open_sparse(BlockDriverState *bs, } } +static const char *next_line(const char *s) +{ + while (*s) { + if (*s == '\n') { + return s + 1; + } + s++; + } + return s; +} + static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, const char *desc_file_path, QDict *options, Error **errp) @@ -769,16 +795,17 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, char access[11]; char type[11]; char fname[512]; - const char *p = desc; + const char *p, *np; int64_t sectors = 0; int64_t flat_offset; char *extent_path; - BlockDriverState *extent_file; + BdrvChild *extent_file; BDRVVmdkState *s = bs->opaque; VmdkExtent *extent; char extent_opt_prefix[32]; + Error *local_err = NULL; - while (*p) { + for (p = desc; *p; p = next_line(p)) { /* parse extent line in one of below formats: * * RW [size in sectors] FLAT "file-name.vmdk" OFFSET @@ -790,51 +817,48 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, matches = sscanf(p, "%10s %" SCNd64 " %10s \"%511[^\n\r\"]\" %" SCNd64, access, §ors, type, fname, &flat_offset); if (matches < 4 || strcmp(access, "RW")) { - goto next_line; + continue; } else if (!strcmp(type, "FLAT")) { if (matches != 5 || flat_offset < 0) { - error_setg(errp, "Invalid extent lines: \n%s", p); - return -EINVAL; + goto invalid; } } else if (!strcmp(type, "VMFS")) { if (matches == 4) { flat_offset = 0; } else { - error_setg(errp, "Invalid extent lines:\n%s", p); - return -EINVAL; + goto invalid; } } else if (matches != 4) { - error_setg(errp, "Invalid extent lines:\n%s", p); - return -EINVAL; + goto invalid; } if (sectors <= 0 || (strcmp(type, "FLAT") && strcmp(type, "SPARSE") && strcmp(type, "VMFS") && strcmp(type, "VMFSSPARSE")) || (strcmp(access, "RW"))) { - goto next_line; + continue; } if (!path_is_absolute(fname) && !path_has_protocol(fname) && !desc_file_path[0]) { error_setg(errp, "Cannot use relative extent paths with VMDK " - "descriptor file '%s'", bs->file->filename); + "descriptor file '%s'", bs->file->bs->filename); return -EINVAL; } extent_path = g_malloc0(PATH_MAX); path_combine(extent_path, PATH_MAX, desc_file_path, fname); - extent_file = NULL; ret = snprintf(extent_opt_prefix, 32, "extents.%d", s->num_extents); assert(ret < 32); - ret = bdrv_open_image(&extent_file, extent_path, options, - extent_opt_prefix, bs, &child_file, false, errp); + extent_file = bdrv_open_child(extent_path, options, extent_opt_prefix, + bs, &child_file, false, &local_err); g_free(extent_path); - if (ret) { - return ret; + if (local_err) { + error_propagate(errp, local_err); + return -EINVAL; } /* save to extents array */ @@ -844,13 +868,13 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, ret = vmdk_add_extent(bs, extent_file, true, sectors, 0, 0, 0, 0, 0, &extent, errp); if (ret < 0) { - bdrv_unref(extent_file); + bdrv_unref_child(bs, extent_file); return ret; } extent->flat_start_offset = flat_offset << 9; } else if (!strcmp(type, "SPARSE") || !strcmp(type, "VMFSSPARSE")) { /* SPARSE extent and VMFSSPARSE extent are both "COWD" sparse file*/ - char *buf = vmdk_read_desc(extent_file, 0, errp); + char *buf = vmdk_read_desc(extent_file->bs, 0, errp); if (!buf) { ret = -EINVAL; } else { @@ -859,27 +883,27 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, } g_free(buf); if (ret) { - bdrv_unref(extent_file); + bdrv_unref_child(bs, extent_file); return ret; } extent = &s->extents[s->num_extents - 1]; } else { error_setg(errp, "Unsupported extent type '%s'", type); - bdrv_unref(extent_file); + bdrv_unref_child(bs, extent_file); return -ENOTSUP; } extent->type = g_strdup(type); -next_line: - /* move to next line */ - while (*p) { - if (*p == '\n') { - p++; - break; - } - p++; - } } return 0; + +invalid: + np = next_line(p); + assert(np != p); + if (np[-1] == '\n') { + np--; + } + error_setg(errp, "Invalid extent line: %.*s", (int)(np - p), p); + return -EINVAL; } static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf, @@ -905,7 +929,8 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf, } s->create_type = g_strdup(ct); s->desc_offset = 0; - ret = vmdk_parse_extents(buf, bs, bs->file->exact_filename, options, errp); + ret = vmdk_parse_extents(buf, bs, bs->file->bs->exact_filename, options, + errp); exit: return ret; } @@ -918,7 +943,7 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, BDRVVmdkState *s = bs->opaque; uint32_t magic; - buf = vmdk_read_desc(bs->file, 0, errp); + buf = vmdk_read_desc(bs->file->bs, 0, errp); if (!buf) { return -EINVAL; } @@ -927,7 +952,8 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, switch (magic) { case VMDK3_MAGIC: case VMDK4_MAGIC: - ret = vmdk_open_sparse(bs, bs->file, flags, buf, options, errp); + ret = vmdk_open_sparse(bs, bs->file, flags, buf, options, + errp); s->desc_offset = 0x200; break; default: @@ -1004,7 +1030,7 @@ static int get_whole_cluster(BlockDriverState *bs, cluster_bytes = extent->cluster_sectors << BDRV_SECTOR_BITS; whole_grain = qemu_blockalign(bs, cluster_bytes); - if (!bs->backing_hd) { + if (!bs->backing) { memset(whole_grain, 0, skip_start_sector << BDRV_SECTOR_BITS); memset(whole_grain + (skip_end_sector << BDRV_SECTOR_BITS), 0, cluster_bytes - (skip_end_sector << BDRV_SECTOR_BITS)); @@ -1013,22 +1039,22 @@ static int get_whole_cluster(BlockDriverState *bs, assert(skip_end_sector <= extent->cluster_sectors); /* we will be here if it's first write on non-exist grain(cluster). * try to read from parent image, if exist */ - if (bs->backing_hd && !vmdk_is_cid_valid(bs)) { + if (bs->backing && !vmdk_is_cid_valid(bs)) { ret = VMDK_ERROR; goto exit; } /* Read backing data before skip range */ if (skip_start_sector > 0) { - if (bs->backing_hd) { - ret = bdrv_read(bs->backing_hd, sector_num, + if (bs->backing) { + ret = bdrv_read(bs->backing->bs, sector_num, whole_grain, skip_start_sector); if (ret < 0) { ret = VMDK_ERROR; goto exit; } } - ret = bdrv_write(extent->file, cluster_sector_num, whole_grain, + ret = bdrv_write(extent->file->bs, cluster_sector_num, whole_grain, skip_start_sector); if (ret < 0) { ret = VMDK_ERROR; @@ -1037,8 +1063,8 @@ static int get_whole_cluster(BlockDriverState *bs, } /* Read backing data after skip range */ if (skip_end_sector < extent->cluster_sectors) { - if (bs->backing_hd) { - ret = bdrv_read(bs->backing_hd, sector_num + skip_end_sector, + if (bs->backing) { + ret = bdrv_read(bs->backing->bs, sector_num + skip_end_sector, whole_grain + (skip_end_sector << BDRV_SECTOR_BITS), extent->cluster_sectors - skip_end_sector); if (ret < 0) { @@ -1046,7 +1072,7 @@ static int get_whole_cluster(BlockDriverState *bs, goto exit; } } - ret = bdrv_write(extent->file, cluster_sector_num + skip_end_sector, + ret = bdrv_write(extent->file->bs, cluster_sector_num + skip_end_sector, whole_grain + (skip_end_sector << BDRV_SECTOR_BITS), extent->cluster_sectors - skip_end_sector); if (ret < 0) { @@ -1066,7 +1092,7 @@ static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data, offset = cpu_to_le32(offset); /* update L2 table */ if (bdrv_pwrite_sync( - extent->file, + extent->file->bs, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(offset)), &offset, sizeof(offset)) < 0) { @@ -1076,7 +1102,7 @@ static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data, if (extent->l1_backup_table_offset != 0) { m_data->l2_offset = extent->l1_backup_table[m_data->l1_index]; if (bdrv_pwrite_sync( - extent->file, + extent->file->bs, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(offset)), &offset, sizeof(offset)) < 0) { @@ -1166,7 +1192,7 @@ static int get_cluster_offset(BlockDriverState *bs, } l2_table = extent->l2_cache + (min_index * extent->l2_size); if (bdrv_pread( - extent->file, + extent->file->bs, (int64_t)l2_offset * 512, l2_table, extent->l2_size * sizeof(uint32_t) @@ -1245,7 +1271,7 @@ static inline uint64_t vmdk_find_index_in_cluster(VmdkExtent *extent, } static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, int *pnum) + int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) { BDRVVmdkState *s = bs->opaque; int64_t index_in_cluster, n, ret; @@ -1262,6 +1288,7 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs, 0, 0); qemu_co_mutex_unlock(&s->lock); + index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num); switch (ret) { case VMDK_ERROR: ret = -EIO; @@ -1274,14 +1301,15 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs, break; case VMDK_OK: ret = BDRV_BLOCK_DATA; - if (extent->file == bs->file && !extent->compressed) { - ret |= BDRV_BLOCK_OFFSET_VALID | offset; + if (!extent->compressed) { + ret |= BDRV_BLOCK_OFFSET_VALID; + ret |= (offset + (index_in_cluster << BDRV_SECTOR_BITS)) + & BDRV_BLOCK_OFFSET_MASK; } - + *file = extent->file->bs; break; } - index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num); n = extent->cluster_sectors - index_in_cluster; if (n > nb_sectors) { n = nb_sectors; @@ -1320,12 +1348,16 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset, write_len = buf_len + sizeof(VmdkGrainMarker); } write_offset = cluster_offset + offset_in_cluster, - ret = bdrv_pwrite(extent->file, write_offset, write_buf, write_len); + ret = bdrv_pwrite(extent->file->bs, write_offset, write_buf, write_len); write_end_sector = DIV_ROUND_UP(write_offset + write_len, BDRV_SECTOR_SIZE); - extent->next_cluster_sector = MAX(extent->next_cluster_sector, - write_end_sector); + if (extent->compressed) { + extent->next_cluster_sector = write_end_sector; + } else { + extent->next_cluster_sector = MAX(extent->next_cluster_sector, + write_end_sector); + } if (ret != write_len) { ret = ret < 0 ? ret : -EIO; @@ -1351,7 +1383,7 @@ static int vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset, if (!extent->compressed) { - ret = bdrv_pread(extent->file, + ret = bdrv_pread(extent->file->bs, cluster_offset + offset_in_cluster, buf, nb_sectors * 512); if (ret == nb_sectors * 512) { @@ -1365,7 +1397,7 @@ static int vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset, buf_bytes = cluster_bytes * 2; cluster_buf = g_malloc(buf_bytes); uncomp_buf = g_malloc(cluster_bytes); - ret = bdrv_pread(extent->file, + ret = bdrv_pread(extent->file->bs, cluster_offset, cluster_buf, buf_bytes); if (ret < 0) { @@ -1427,11 +1459,11 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num, } if (ret != VMDK_OK) { /* if not allocated, try to read from parent image, if exist */ - if (bs->backing_hd && ret != VMDK_ZEROED) { + if (bs->backing && ret != VMDK_ZEROED) { if (!vmdk_is_cid_valid(bs)) { return -EINVAL; } - ret = bdrv_read(bs->backing_hd, sector_num, buf, n); + ret = bdrv_read(bs->backing->bs, sector_num, buf, n); if (ret < 0) { return ret; } @@ -1487,8 +1519,8 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, if (sector_num > bs->total_sectors) { error_report("Wrong offset: sector_num=0x%" PRIx64 - " total_sectors=0x%" PRIx64 "\n", - sector_num, bs->total_sectors); + " total_sectors=0x%" PRIx64, + sector_num, bs->total_sectors); return -EIO; } @@ -1617,7 +1649,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize, QemuOpts *opts, Error **errp) { int ret, i; - BlockDriverState *bs = NULL; + BlockBackend *blk = NULL; VMDK4Header header; Error *local_err = NULL; uint32_t tmp, magic, grains, gd_sectors, gt_size, gt_count; @@ -1630,16 +1662,18 @@ static int vmdk_create_extent(const char *filename, int64_t filesize, goto exit; } - assert(bs == NULL); - ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, - NULL, &local_err); - if (ret < 0) { + blk = blk_new_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); + if (blk == NULL) { error_propagate(errp, local_err); + ret = -EIO; goto exit; } + blk_set_allow_write_beyond_eof(blk, true); + if (flat) { - ret = bdrv_truncate(bs, filesize); + ret = blk_truncate(blk, filesize); if (ret < 0) { error_setg_errno(errp, -ret, "Could not truncate file"); } @@ -1647,7 +1681,13 @@ static int vmdk_create_extent(const char *filename, int64_t filesize, } magic = cpu_to_be32(VMDK4_MAGIC); memset(&header, 0, sizeof(header)); - header.version = zeroed_grain ? 2 : 1; + if (compress) { + header.version = 3; + } else if (zeroed_grain) { + header.version = 2; + } else { + header.version = 1; + } header.flags = VMDK4_FLAG_RGD | VMDK4_FLAG_NL_DETECT | (compress ? VMDK4_FLAG_COMPRESS | VMDK4_FLAG_MARKER : 0) | (zeroed_grain ? VMDK4_FLAG_ZERO_GRAIN : 0); @@ -1688,18 +1728,18 @@ static int vmdk_create_extent(const char *filename, int64_t filesize, header.check_bytes[3] = 0xa; /* write all the data */ - ret = bdrv_pwrite(bs, 0, &magic, sizeof(magic)); + ret = blk_pwrite(blk, 0, &magic, sizeof(magic)); if (ret < 0) { error_setg(errp, QERR_IO_ERROR); goto exit; } - ret = bdrv_pwrite(bs, sizeof(magic), &header, sizeof(header)); + ret = blk_pwrite(blk, sizeof(magic), &header, sizeof(header)); if (ret < 0) { error_setg(errp, QERR_IO_ERROR); goto exit; } - ret = bdrv_truncate(bs, le64_to_cpu(header.grain_offset) << 9); + ret = blk_truncate(blk, le64_to_cpu(header.grain_offset) << 9); if (ret < 0) { error_setg_errno(errp, -ret, "Could not truncate file"); goto exit; @@ -1712,8 +1752,8 @@ static int vmdk_create_extent(const char *filename, int64_t filesize, i < gt_count; i++, tmp += gt_size) { gd_buf[i] = cpu_to_le32(tmp); } - ret = bdrv_pwrite(bs, le64_to_cpu(header.rgd_offset) * BDRV_SECTOR_SIZE, - gd_buf, gd_buf_size); + ret = blk_pwrite(blk, le64_to_cpu(header.rgd_offset) * BDRV_SECTOR_SIZE, + gd_buf, gd_buf_size); if (ret < 0) { error_setg(errp, QERR_IO_ERROR); goto exit; @@ -1724,8 +1764,8 @@ static int vmdk_create_extent(const char *filename, int64_t filesize, i < gt_count; i++, tmp += gt_size) { gd_buf[i] = cpu_to_le32(tmp); } - ret = bdrv_pwrite(bs, le64_to_cpu(header.gd_offset) * BDRV_SECTOR_SIZE, - gd_buf, gd_buf_size); + ret = blk_pwrite(blk, le64_to_cpu(header.gd_offset) * BDRV_SECTOR_SIZE, + gd_buf, gd_buf_size); if (ret < 0) { error_setg(errp, QERR_IO_ERROR); goto exit; @@ -1733,8 +1773,8 @@ static int vmdk_create_extent(const char *filename, int64_t filesize, ret = 0; exit: - if (bs) { - bdrv_unref(bs); + if (blk) { + blk_unref(blk); } g_free(gd_buf); return ret; @@ -1783,7 +1823,7 @@ static int filename_decompose(const char *filename, char *path, char *prefix, static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp) { int idx = 0; - BlockDriverState *new_bs = NULL; + BlockBackend *new_blk = NULL; Error *local_err = NULL; char *desc = NULL; int64_t total_size = 0, filesize; @@ -1894,7 +1934,7 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp) goto exit; } if (backing_file) { - BlockDriverState *bs = NULL; + BlockBackend *blk; char *full_backing = g_new0(char, PATH_MAX); bdrv_get_full_backing_filename_from_filename(filename, backing_file, full_backing, PATH_MAX, @@ -1905,19 +1945,21 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp) ret = -ENOENT; goto exit; } - ret = bdrv_open(&bs, full_backing, NULL, NULL, BDRV_O_NO_BACKING, NULL, - errp); + + blk = blk_new_open(full_backing, NULL, NULL, + BDRV_O_NO_BACKING, errp); g_free(full_backing); - if (ret != 0) { + if (blk == NULL) { + ret = -EIO; goto exit; } - if (strcmp(bs->drv->format_name, "vmdk")) { - bdrv_unref(bs); + if (strcmp(blk_bs(blk)->drv->format_name, "vmdk")) { + blk_unref(blk); ret = -EINVAL; goto exit; } - parent_cid = vmdk_read_cid(bs, 0); - bdrv_unref(bs); + parent_cid = vmdk_read_cid(blk_bs(blk), 0); + blk_unref(blk); snprintf(parent_desc_line, BUF_SIZE, "parentFileNameHint=\"%s\"", backing_file); } @@ -1975,14 +2017,18 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp) goto exit; } } - assert(new_bs == NULL); - ret = bdrv_open(&new_bs, filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err); - if (ret < 0) { + + new_blk = blk_new_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); + if (new_blk == NULL) { error_propagate(errp, local_err); + ret = -EIO; goto exit; } - ret = bdrv_pwrite(new_bs, desc_offset, desc, desc_len); + + blk_set_allow_write_beyond_eof(new_blk, true); + + ret = blk_pwrite(new_blk, desc_offset, desc, desc_len); if (ret < 0) { error_setg_errno(errp, -ret, "Could not write description"); goto exit; @@ -1990,14 +2036,14 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp) /* bdrv_pwrite write padding zeros to align to sector, we don't need that * for description file */ if (desc_offset == 0) { - ret = bdrv_truncate(new_bs, desc_len); + ret = blk_truncate(new_blk, desc_len); if (ret < 0) { error_setg_errno(errp, -ret, "Could not truncate file"); } } exit: - if (new_bs) { - bdrv_unref(new_bs); + if (new_blk) { + blk_unref(new_blk); } g_free(adapter_type); g_free(backing_file); @@ -2032,7 +2078,7 @@ static coroutine_fn int vmdk_co_flush(BlockDriverState *bs) int ret = 0; for (i = 0; i < s->num_extents; i++) { - err = bdrv_co_flush(s->extents[i].file); + err = bdrv_co_flush(s->extents[i].file->bs); if (err < 0) { ret = err; } @@ -2047,7 +2093,7 @@ static int64_t vmdk_get_allocated_file_size(BlockDriverState *bs) int64_t r; BDRVVmdkState *s = bs->opaque; - ret = bdrv_get_allocated_file_size(bs->file); + ret = bdrv_get_allocated_file_size(bs->file->bs); if (ret < 0) { return ret; } @@ -2055,7 +2101,7 @@ static int64_t vmdk_get_allocated_file_size(BlockDriverState *bs) if (s->extents[i].file == bs->file) { continue; } - r = bdrv_get_allocated_file_size(s->extents[i].file); + r = bdrv_get_allocated_file_size(s->extents[i].file->bs); if (r < 0) { return r; } @@ -2073,7 +2119,7 @@ static int vmdk_has_zero_init(BlockDriverState *bs) * return 0. */ for (i = 0; i < s->num_extents; i++) { if (s->extents[i].flat) { - if (!bdrv_has_zero_init(s->extents[i].file)) { + if (!bdrv_has_zero_init(s->extents[i].file->bs)) { return 0; } } @@ -2086,7 +2132,7 @@ static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent) ImageInfo *info = g_new0(ImageInfo, 1); *info = (ImageInfo){ - .filename = g_strdup(extent->file->filename), + .filename = g_strdup(extent->file->bs->filename), .format = g_strdup(extent->type), .virtual_size = extent->sectors * BDRV_SECTOR_SIZE, .compressed = extent->compressed, @@ -2132,7 +2178,9 @@ static int vmdk_check(BlockDriverState *bs, BdrvCheckResult *result, PRId64 "\n", sector_num); break; } - if (ret == VMDK_OK && cluster_offset >= bdrv_getlength(extent->file)) { + if (ret == VMDK_OK && + cluster_offset >= bdrv_getlength(extent->file->bs)) + { fprintf(stderr, "ERROR: cluster offset for sector %" PRId64 " points after EOF\n", sector_num); @@ -2153,19 +2201,19 @@ static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs) ImageInfoList **next; *spec_info = (ImageInfoSpecific){ - .kind = IMAGE_INFO_SPECIFIC_KIND_VMDK, - { - .vmdk = g_new0(ImageInfoSpecificVmdk, 1), + .type = IMAGE_INFO_SPECIFIC_KIND_VMDK, + .u = { + .vmdk.data = g_new0(ImageInfoSpecificVmdk, 1), }, }; - *spec_info->vmdk = (ImageInfoSpecificVmdk) { + *spec_info->u.vmdk.data = (ImageInfoSpecificVmdk) { .create_type = g_strdup(s->create_type), .cid = s->cid, .parent_cid = s->parent_cid, }; - next = &spec_info->vmdk->extents; + next = &spec_info->u.vmdk.data->extents; for (i = 0; i < s->num_extents; i++) { *next = g_new0(ImageInfoList, 1); (*next)->value = vmdk_get_extent_info(&s->extents[i]); @@ -2208,7 +2256,7 @@ static void vmdk_detach_aio_context(BlockDriverState *bs) int i; for (i = 0; i < s->num_extents; i++) { - bdrv_detach_aio_context(s->extents[i].file); + bdrv_detach_aio_context(s->extents[i].file->bs); } } @@ -2219,7 +2267,7 @@ static void vmdk_attach_aio_context(BlockDriverState *bs, int i; for (i = 0; i < s->num_extents; i++) { - bdrv_attach_aio_context(s->extents[i].file, new_context); + bdrv_attach_aio_context(s->extents[i].file->bs, new_context); } } diff --git a/qemu/block/vpc.c b/qemu/block/vpc.c index 3e385d9fb..3e2ea698d 100644 --- a/qemu/block/vpc.c +++ b/qemu/block/vpc.c @@ -22,8 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "block/block_int.h" +#include "sysemu/block-backend.h" #include "qemu/module.h" #include "migration/migration.h" #if defined(CONFIG_UUID) @@ -42,28 +45,34 @@ enum vhd_type { VHD_DIFFERENCING = 4, }; -// Seconds since Jan 1, 2000 0:00:00 (UTC) +/* Seconds since Jan 1, 2000 0:00:00 (UTC) */ #define VHD_TIMESTAMP_BASE 946684800 -#define VHD_MAX_SECTORS (65535LL * 255 * 255) -#define VHD_MAX_GEOMETRY (65535LL * 16 * 255) +#define VHD_CHS_MAX_C 65535LL +#define VHD_CHS_MAX_H 16 +#define VHD_CHS_MAX_S 255 -// always big-endian +#define VHD_MAX_SECTORS 0xff000000 /* 2040 GiB max image size */ +#define VHD_MAX_GEOMETRY (VHD_CHS_MAX_C * VHD_CHS_MAX_H * VHD_CHS_MAX_S) + +#define VPC_OPT_FORCE_SIZE "force_size" + +/* always big-endian */ typedef struct vhd_footer { - char creator[8]; // "conectix" + char creator[8]; /* "conectix" */ uint32_t features; uint32_t version; - // Offset of next header structure, 0xFFFFFFFF if none + /* Offset of next header structure, 0xFFFFFFFF if none */ uint64_t data_offset; - // Seconds since Jan 1, 2000 0:00:00 (UTC) + /* Seconds since Jan 1, 2000 0:00:00 (UTC) */ uint32_t timestamp; - char creator_app[4]; // "vpc " + char creator_app[4]; /* e.g., "vpc " */ uint16_t major; uint16_t minor; - char creator_os[4]; // "Wi2k" + char creator_os[4]; /* "Wi2k" */ uint64_t orig_size; uint64_t current_size; @@ -74,29 +83,29 @@ typedef struct vhd_footer { uint32_t type; - // Checksum of the Hard Disk Footer ("one's complement of the sum of all - // the bytes in the footer without the checksum field") + /* Checksum of the Hard Disk Footer ("one's complement of the sum of all + the bytes in the footer without the checksum field") */ uint32_t checksum; - // UUID used to identify a parent hard disk (backing file) + /* UUID used to identify a parent hard disk (backing file) */ uint8_t uuid[16]; uint8_t in_saved_state; } QEMU_PACKED VHDFooter; typedef struct vhd_dyndisk_header { - char magic[8]; // "cxsparse" + char magic[8]; /* "cxsparse" */ - // Offset of next header structure, 0xFFFFFFFF if none + /* Offset of next header structure, 0xFFFFFFFF if none */ uint64_t data_offset; - // Offset of the Block Allocation Table (BAT) + /* Offset of the Block Allocation Table (BAT) */ uint64_t table_offset; uint32_t version; - uint32_t max_table_entries; // 32bit/entry + uint32_t max_table_entries; /* 32bit/entry */ - // 2 MB by default, must be a power of two + /* 2 MB by default, must be a power of two */ uint32_t block_size; uint32_t checksum; @@ -104,7 +113,7 @@ typedef struct vhd_dyndisk_header { uint32_t parent_timestamp; uint32_t reserved; - // Backing file name (in UTF-16) + /* Backing file name (in UTF-16) */ uint8_t parent_name[512]; struct { @@ -127,6 +136,8 @@ typedef struct BDRVVPCState { uint32_t block_size; uint32_t bitmap_size; + bool force_use_chs; + bool force_use_sz; #ifdef CACHE uint8_t *pageentry_u8; @@ -139,6 +150,22 @@ typedef struct BDRVVPCState { Error *migration_blocker; } BDRVVPCState; +#define VPC_OPT_SIZE_CALC "force_size_calc" +static QemuOptsList vpc_runtime_opts = { + .name = "vpc-runtime-opts", + .head = QTAILQ_HEAD_INITIALIZER(vpc_runtime_opts.head), + .desc = { + { + .name = VPC_OPT_SIZE_CALC, + .type = QEMU_OPT_STRING, + .help = "Force disk size calculation to use either CHS geometry, " + "or use the disk current_size specified in the VHD footer. " + "{chs, current_size}" + }, + { /* end of list */ } + } +}; + static uint32_t vpc_checksum(uint8_t* buf, size_t size) { uint32_t res = 0; @@ -158,6 +185,25 @@ static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } +static void vpc_parse_options(BlockDriverState *bs, QemuOpts *opts, + Error **errp) +{ + BDRVVPCState *s = bs->opaque; + const char *size_calc; + + size_calc = qemu_opt_get(opts, VPC_OPT_SIZE_CALC); + + if (!size_calc) { + /* no override, use autodetect only */ + } else if (!strcmp(size_calc, "current_size")) { + s->force_use_sz = true; + } else if (!strcmp(size_calc, "chs")) { + s->force_use_chs = true; + } else { + error_setg(errp, "Invalid size calculation mode: '%s'", size_calc); + } +} + static int vpc_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { @@ -165,6 +211,9 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, int i; VHDFooter *footer; VHDDynDiskHeader *dyndisk_header; + QemuOpts *opts = NULL; + Error *local_err = NULL; + bool use_chs; uint8_t buf[HEADER_SIZE]; uint32_t checksum; uint64_t computed_size; @@ -172,24 +221,42 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, int disk_type = VHD_DYNAMIC; int ret; - ret = bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE); + opts = qemu_opts_create(&vpc_runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + vpc_parse_options(bs, opts, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + ret = bdrv_pread(bs->file->bs, 0, s->footer_buf, HEADER_SIZE); if (ret < 0) { + error_setg(errp, "Unable to read VHD header"); goto fail; } footer = (VHDFooter *) s->footer_buf; if (strncmp(footer->creator, "conectix", 8)) { - int64_t offset = bdrv_getlength(bs->file); + int64_t offset = bdrv_getlength(bs->file->bs); if (offset < 0) { ret = offset; + error_setg(errp, "Invalid file size"); goto fail; } else if (offset < HEADER_SIZE) { ret = -EINVAL; + error_setg(errp, "File too small for a VHD header"); goto fail; } /* If a fixed disk, the footer is found only at the end of the file */ - ret = bdrv_pread(bs->file, offset-HEADER_SIZE, s->footer_buf, + ret = bdrv_pread(bs->file->bs, offset-HEADER_SIZE, s->footer_buf, HEADER_SIZE); if (ret < 0) { goto fail; @@ -211,36 +278,66 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, /* Write 'checksum' back to footer, or else will leave it with zero. */ footer->checksum = cpu_to_be32(checksum); - // The visible size of a image in Virtual PC depends on the geometry - // rather than on the size stored in the footer (the size in the footer - // is too large usually) + /* The visible size of a image in Virtual PC depends on the geometry + rather than on the size stored in the footer (the size in the footer + is too large usually) */ bs->total_sectors = (int64_t) be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl; - /* Images that have exactly the maximum geometry are probably bigger and - * would be truncated if we adhered to the geometry for them. Rely on - * footer->current_size for them. */ - if (bs->total_sectors == VHD_MAX_GEOMETRY) { + /* Microsoft Virtual PC and Microsoft Hyper-V produce and read + * VHD image sizes differently. VPC will rely on CHS geometry, + * while Hyper-V and disk2vhd use the size specified in the footer. + * + * We use a couple of approaches to try and determine the correct method: + * look at the Creator App field, and look for images that have CHS + * geometry that is the maximum value. + * + * If the CHS geometry is the maximum CHS geometry, then we assume that + * the size is the footer->current_size to avoid truncation. Otherwise, + * we follow the table based on footer->creator_app: + * + * Known creator apps: + * 'vpc ' : CHS Virtual PC (uses disk geometry) + * 'qemu' : CHS QEMU (uses disk geometry) + * 'qem2' : current_size QEMU (uses current_size) + * 'win ' : current_size Hyper-V + * 'd2v ' : current_size Disk2vhd + * 'tap\0' : current_size XenServer + * 'CTXS' : current_size XenConverter + * + * The user can override the table values via drive options, however + * even with an override we will still use current_size for images + * that have CHS geometry of the maximum size. + */ + use_chs = (!!strncmp(footer->creator_app, "win ", 4) && + !!strncmp(footer->creator_app, "qem2", 4) && + !!strncmp(footer->creator_app, "d2v ", 4) && + !!strncmp(footer->creator_app, "CTXS", 4) && + !!memcmp(footer->creator_app, "tap", 4)) || s->force_use_chs; + + if (!use_chs || bs->total_sectors == VHD_MAX_GEOMETRY || s->force_use_sz) { bs->total_sectors = be64_to_cpu(footer->current_size) / - BDRV_SECTOR_SIZE; + BDRV_SECTOR_SIZE; } - /* Allow a maximum disk size of approximately 2 TB */ - if (bs->total_sectors >= VHD_MAX_SECTORS) { + /* Allow a maximum disk size of 2040 GiB */ + if (bs->total_sectors > VHD_MAX_SECTORS) { ret = -EFBIG; goto fail; } if (disk_type == VHD_DYNAMIC) { - ret = bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, + ret = bdrv_pread(bs->file->bs, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE); if (ret < 0) { + error_setg(errp, "Error reading dynamic VHD header"); goto fail; } dyndisk_header = (VHDDynDiskHeader *) buf; if (strncmp(dyndisk_header->magic, "cxsparse", 8)) { + error_setg(errp, "Invalid header magic"); ret = -EINVAL; goto fail; } @@ -256,16 +353,14 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries); if ((bs->total_sectors * 512) / s->block_size > 0xffffffffU) { - ret = -EINVAL; - goto fail; - } - if (s->max_table_entries > (VHD_MAX_SECTORS * 512) / s->block_size) { + error_setg(errp, "Too many blocks"); ret = -EINVAL; goto fail; } computed_size = (uint64_t) s->max_table_entries * s->block_size; if (computed_size < bs->total_sectors * 512) { + error_setg(errp, "Page table too small"); ret = -EINVAL; goto fail; } @@ -280,16 +375,19 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, pagetable_size = (uint64_t) s->max_table_entries * 4; - s->pagetable = qemu_try_blockalign(bs->file, pagetable_size); + s->pagetable = qemu_try_blockalign(bs->file->bs, pagetable_size); if (s->pagetable == NULL) { + error_setg(errp, "Unable to allocate memory for page table"); ret = -ENOMEM; goto fail; } s->bat_offset = be64_to_cpu(dyndisk_header->table_offset); - ret = bdrv_pread(bs->file, s->bat_offset, s->pagetable, pagetable_size); + ret = bdrv_pread(bs->file->bs, s->bat_offset, s->pagetable, + pagetable_size); if (ret < 0) { + error_setg(errp, "Error reading pagetable"); goto fail; } @@ -308,7 +406,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, } } - if (s->free_data_block_offset > bdrv_getlength(bs->file)) { + if (s->free_data_block_offset > bdrv_getlength(bs->file->bs)) { error_setg(errp, "block-vpc: free_data_block_offset points after " "the end of file. The image has been truncated."); ret = -EINVAL; @@ -368,22 +466,22 @@ static inline int64_t get_sector_offset(BlockDriverState *bs, pageentry_index = (offset % s->block_size) / 512; if (pagetable_index >= s->max_table_entries || s->pagetable[pagetable_index] == 0xffffffff) - return -1; // not allocated + return -1; /* not allocated */ bitmap_offset = 512 * (uint64_t) s->pagetable[pagetable_index]; block_offset = bitmap_offset + s->bitmap_size + (512 * pageentry_index); - // We must ensure that we don't write to any sectors which are marked as - // unused in the bitmap. We get away with setting all bits in the block - // bitmap each time we write to a new block. This might cause Virtual PC to - // miss sparse read optimization, but it's not a problem in terms of - // correctness. + /* We must ensure that we don't write to any sectors which are marked as + unused in the bitmap. We get away with setting all bits in the block + bitmap each time we write to a new block. This might cause Virtual PC to + miss sparse read optimization, but it's not a problem in terms of + correctness. */ if (write && (s->last_bitmap_offset != bitmap_offset)) { uint8_t bitmap[s->bitmap_size]; s->last_bitmap_offset = bitmap_offset; memset(bitmap, 0xff, s->bitmap_size); - bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size); + bdrv_pwrite_sync(bs->file->bs, bitmap_offset, bitmap, s->bitmap_size); } return block_offset; @@ -401,7 +499,7 @@ static int rewrite_footer(BlockDriverState* bs) BDRVVPCState *s = bs->opaque; int64_t offset = s->free_data_block_offset; - ret = bdrv_pwrite_sync(bs->file, offset, s->footer_buf, HEADER_SIZE); + ret = bdrv_pwrite_sync(bs->file->bs, offset, s->footer_buf, HEADER_SIZE); if (ret < 0) return ret; @@ -423,35 +521,35 @@ static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num) int ret; uint8_t bitmap[s->bitmap_size]; - // Check if sector_num is valid + /* Check if sector_num is valid */ if ((sector_num < 0) || (sector_num > bs->total_sectors)) return -1; - // Write entry into in-memory BAT + /* Write entry into in-memory BAT */ index = (sector_num * 512) / s->block_size; if (s->pagetable[index] != 0xFFFFFFFF) return -1; s->pagetable[index] = s->free_data_block_offset / 512; - // Initialize the block's bitmap + /* Initialize the block's bitmap */ memset(bitmap, 0xff, s->bitmap_size); - ret = bdrv_pwrite_sync(bs->file, s->free_data_block_offset, bitmap, + ret = bdrv_pwrite_sync(bs->file->bs, s->free_data_block_offset, bitmap, s->bitmap_size); if (ret < 0) { return ret; } - // Write new footer (the old one will be overwritten) + /* Write new footer (the old one will be overwritten) */ s->free_data_block_offset += s->block_size + s->bitmap_size; ret = rewrite_footer(bs); if (ret < 0) goto fail; - // Write BAT entry to disk + /* Write BAT entry to disk */ bat_offset = s->bat_offset + (4 * index); bat_value = cpu_to_be32(s->pagetable[index]); - ret = bdrv_pwrite_sync(bs->file, bat_offset, &bat_value, 4); + ret = bdrv_pwrite_sync(bs->file->bs, bat_offset, &bat_value, 4); if (ret < 0) goto fail; @@ -485,7 +583,7 @@ static int vpc_read(BlockDriverState *bs, int64_t sector_num, VHDFooter *footer = (VHDFooter *) s->footer_buf; if (be32_to_cpu(footer->type) == VHD_FIXED) { - return bdrv_read(bs->file, sector_num, buf, nb_sectors); + return bdrv_read(bs->file->bs, sector_num, buf, nb_sectors); } while (nb_sectors > 0) { offset = get_sector_offset(bs, sector_num, 0); @@ -499,7 +597,7 @@ static int vpc_read(BlockDriverState *bs, int64_t sector_num, if (offset == -1) { memset(buf, 0, sectors * BDRV_SECTOR_SIZE); } else { - ret = bdrv_pread(bs->file, offset, buf, + ret = bdrv_pread(bs->file->bs, offset, buf, sectors * BDRV_SECTOR_SIZE); if (ret != sectors * BDRV_SECTOR_SIZE) { return -1; @@ -534,7 +632,7 @@ static int vpc_write(BlockDriverState *bs, int64_t sector_num, VHDFooter *footer = (VHDFooter *) s->footer_buf; if (be32_to_cpu(footer->type) == VHD_FIXED) { - return bdrv_write(bs->file, sector_num, buf, nb_sectors); + return bdrv_write(bs->file->bs, sector_num, buf, nb_sectors); } while (nb_sectors > 0) { offset = get_sector_offset(bs, sector_num, 1); @@ -551,7 +649,8 @@ static int vpc_write(BlockDriverState *bs, int64_t sector_num, return -1; } - ret = bdrv_pwrite(bs->file, offset, buf, sectors * BDRV_SECTOR_SIZE); + ret = bdrv_pwrite(bs->file->bs, offset, buf, + sectors * BDRV_SECTOR_SIZE); if (ret != sectors * BDRV_SECTOR_SIZE) { return -1; } @@ -576,7 +675,7 @@ static coroutine_fn int vpc_co_write(BlockDriverState *bs, int64_t sector_num, } static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, int *pnum) + int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) { BDRVVPCState *s = bs->opaque; VHDFooter *footer = (VHDFooter*) s->footer_buf; @@ -586,6 +685,7 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs, if (be32_to_cpu(footer->type) == VHD_FIXED) { *pnum = nb_sectors; + *file = bs->file->bs; return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA | (sector_num << BDRV_SECTOR_BITS); } @@ -607,6 +707,7 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs, /* *pnum can't be greater than one block for allocated * sectors since there is always a bitmap in between. */ if (allocated) { + *file = bs->file->bs; return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; } if (nb_sectors == 0) { @@ -626,7 +727,7 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs, * Note that the geometry doesn't always exactly match total_sectors but * may round it down. * - * Returns 0 on success, -EFBIG if the size is larger than ~2 TB. Override + * Returns 0 on success, -EFBIG if the size is larger than 2040 GiB. Override * the hardware EIDE and ATA-2 limit of 16 heads (max disk size of 127 GB) * and instead allow up to 255 heads. */ @@ -668,7 +769,7 @@ static int calculate_geometry(int64_t total_sectors, uint16_t* cyls, return 0; } -static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf, +static int create_dynamic_disk(BlockBackend *blk, uint8_t *buf, int64_t total_sectors) { VHDDynDiskHeader *dyndisk_header = @@ -678,34 +779,34 @@ static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf, int ret; int64_t offset = 0; - // Write the footer (twice: at the beginning and at the end) + /* Write the footer (twice: at the beginning and at the end) */ block_size = 0x200000; num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512); - ret = bdrv_pwrite_sync(bs, offset, buf, HEADER_SIZE); - if (ret) { + ret = blk_pwrite(blk, offset, buf, HEADER_SIZE); + if (ret < 0) { goto fail; } offset = 1536 + ((num_bat_entries * 4 + 511) & ~511); - ret = bdrv_pwrite_sync(bs, offset, buf, HEADER_SIZE); + ret = blk_pwrite(blk, offset, buf, HEADER_SIZE); if (ret < 0) { goto fail; } - // Write the initial BAT + /* Write the initial BAT */ offset = 3 * 512; memset(buf, 0xFF, 512); for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) { - ret = bdrv_pwrite_sync(bs, offset, buf, 512); + ret = blk_pwrite(blk, offset, buf, 512); if (ret < 0) { goto fail; } offset += 512; } - // Prepare the Dynamic Disk Header + /* Prepare the Dynamic Disk Header */ memset(buf, 0, 1024); memcpy(dyndisk_header->magic, "cxsparse", 8); @@ -722,10 +823,10 @@ static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf, dyndisk_header->checksum = cpu_to_be32(vpc_checksum(buf, 1024)); - // Write the header + /* Write the header */ offset = 512; - ret = bdrv_pwrite_sync(bs, offset, buf, 1024); + ret = blk_pwrite(blk, offset, buf, 1024); if (ret < 0) { goto fail; } @@ -734,7 +835,7 @@ static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf, return ret; } -static int create_fixed_disk(BlockDriverState *bs, uint8_t *buf, +static int create_fixed_disk(BlockBackend *blk, uint8_t *buf, int64_t total_size) { int ret; @@ -742,12 +843,12 @@ static int create_fixed_disk(BlockDriverState *bs, uint8_t *buf, /* Add footer to total size */ total_size += HEADER_SIZE; - ret = bdrv_truncate(bs, total_size); + ret = blk_truncate(blk, total_size); if (ret < 0) { return ret; } - ret = bdrv_pwrite_sync(bs, total_size - HEADER_SIZE, buf, HEADER_SIZE); + ret = blk_pwrite(blk, total_size - HEADER_SIZE, buf, HEADER_SIZE); if (ret < 0) { return ret; } @@ -768,8 +869,9 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) int64_t total_size; int disk_type; int ret = -EIO; + bool force_size; Error *local_err = NULL; - BlockDriverState *bs = NULL; + BlockBackend *blk = NULL; /* Read out options */ total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), @@ -781,6 +883,7 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) } 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; } @@ -788,36 +891,50 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) 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; } - ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, - NULL, &local_err); - if (ret < 0) { + + blk = blk_new_open(filename, NULL, NULL, + BDRV_O_RDWR | 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); + /* * Calculate matching total_size and geometry. Increase the number of * sectors requested until we get enough (or fail). This ensures that * qemu-img convert doesn't truncate images, but rather rounds up. * - * If the image size can't be represented by a spec conform CHS geometry, + * If the image size can't be represented by a spec conformant CHS geometry, * 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. */ - total_sectors = MIN(VHD_MAX_GEOMETRY, total_size / BDRV_SECTOR_SIZE); - for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) { - calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl); + if (force_size) { + /* This will force the use of total_size for sector count, below */ + cyls = VHD_CHS_MAX_C; + heads = VHD_CHS_MAX_H; + secs_per_cyl = VHD_CHS_MAX_S; + } else { + total_sectors = MIN(VHD_MAX_GEOMETRY, total_size / BDRV_SECTOR_SIZE); + for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) { + calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl); + } } if ((int64_t)cyls * heads * secs_per_cyl == VHD_MAX_GEOMETRY) { total_sectors = total_size / BDRV_SECTOR_SIZE; - /* Allow a maximum disk size of approximately 2 TB */ + /* 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; } @@ -830,8 +947,11 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) memset(buf, 0, 1024); memcpy(footer->creator, "conectix", 8); - /* TODO Check if "qemu" creator_app is ok for VPC */ - memcpy(footer->creator_app, "qemu", 4); + if (force_size) { + memcpy(footer->creator_app, "qem2", 4); + } else { + memcpy(footer->creator_app, "qemu", 4); + } memcpy(footer->creator_os, "Wi2k", 4); footer->features = cpu_to_be32(0x02); @@ -861,13 +981,16 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) footer->checksum = cpu_to_be32(vpc_checksum(buf, HEADER_SIZE)); if (disk_type == VHD_DYNAMIC) { - ret = create_dynamic_disk(bs, buf, total_sectors); + ret = create_dynamic_disk(blk, buf, total_sectors); } else { - ret = create_fixed_disk(bs, buf, total_size); + ret = create_fixed_disk(blk, buf, total_size); + } + if (ret < 0) { + error_setg(errp, "Unable to create or write VHD header"); } out: - bdrv_unref(bs); + blk_unref(blk); g_free(disk_type_param); return ret; } @@ -878,7 +1001,7 @@ static int vpc_has_zero_init(BlockDriverState *bs) VHDFooter *footer = (VHDFooter *) s->footer_buf; if (be32_to_cpu(footer->type) == VHD_FIXED) { - return bdrv_has_zero_init(bs->file); + return bdrv_has_zero_init(bs->file->bs); } else { return 1; } @@ -912,6 +1035,13 @@ static QemuOptsList vpc_create_opts = { "Type of virtual hard disk format. Supported formats are " "{dynamic (default) | fixed} " }, + { + .name = VPC_OPT_FORCE_SIZE, + .type = QEMU_OPT_BOOL, + .help = "Force disk size calculation to use the actual size " + "specified, rather than using the nearest CHS-based " + "calculation" + }, { /* end of list */ } } }; diff --git a/qemu/block/vvfat.c b/qemu/block/vvfat.c index 206869712..183fc4f04 100644 --- a/qemu/block/vvfat.c +++ b/qemu/block/vvfat.c @@ -22,15 +22,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include +#include "qemu/osdep.h" #include -#include "qemu-common.h" +#include "qapi/error.h" #include "block/block_int.h" #include "qemu/module.h" #include "migration/migration.h" #include "qapi/qmp/qint.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qstring.h" +#include "qemu/cutils.h" #ifndef S_IWGRP #define S_IWGRP 0 @@ -985,12 +986,6 @@ static BDRVVVFATState *vvv = NULL; static int enable_write_target(BDRVVVFATState *s, Error **errp); static int is_consistent(BDRVVVFATState *s); -static void vvfat_rebind(BlockDriverState *bs) -{ - BDRVVVFATState *s = bs->opaque; - s->bs = bs; -} - static QemuOptsList runtime_opts = { .name = "vvfat", .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), @@ -1114,6 +1109,8 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } memcpy(s->volume_label, label, label_length); + } else { + memcpy(s->volume_label, "QEMU VVFAT", 10); } if (floppy) { @@ -2288,12 +2285,17 @@ DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapp factor * (old_cluster_count - new_cluster_count)); for (c = first_cluster; !fat_eof(s, c); c = modified_fat_get(s, c)) { + direntry_t *first_direntry; void* direntry = array_get(&(s->directory), current_dir_index); int ret = vvfat_read(s->bs, cluster2sector(s, c), direntry, s->sectors_per_cluster); if (ret) return ret; - assert(!strncmp(s->directory.pointer, "QEMU", 4)); + + /* The first directory entry on the filesystem is the volume name */ + first_direntry = (direntry_t*) s->directory.pointer; + assert(!memcmp(first_direntry->name, s->volume_label, 11)); + current_dir_index += factor; } @@ -2890,7 +2892,7 @@ static coroutine_fn int vvfat_co_write(BlockDriverState *bs, int64_t sector_num, } static int64_t coroutine_fn vvfat_co_get_block_status(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, int* n) + int64_t sector_num, int nb_sectors, int *n, BlockDriverState **file) { BDRVVVFATState* s = bs->opaque; *n = s->sector_count - sector_num; @@ -2923,9 +2925,12 @@ static BlockDriver vvfat_write_target = { static int enable_write_target(BDRVVVFATState *s, Error **errp) { BlockDriver *bdrv_qcow = NULL; + BlockDriverState *backing; QemuOpts *opts = NULL; int ret; int size = sector2cluster(s, s->sector_count); + QDict *options; + s->used_clusters = calloc(size, 1); array_init(&(s->commits), sizeof(commit_t)); @@ -2956,9 +2961,10 @@ static int enable_write_target(BDRVVVFATState *s, Error **errp) } s->qcow = NULL; - ret = bdrv_open(&s->qcow, s->qcow_filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, - bdrv_qcow, errp); + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str("qcow")); + ret = bdrv_open(&s->qcow, s->qcow_filename, NULL, options, + BDRV_O_RDWR | BDRV_O_NO_FLUSH, errp); if (ret < 0) { goto err; } @@ -2967,10 +2973,13 @@ static int enable_write_target(BDRVVVFATState *s, Error **errp) unlink(s->qcow_filename); #endif - bdrv_set_backing_hd(s->bs, bdrv_new()); - s->bs->backing_hd->drv = &vvfat_write_target; - s->bs->backing_hd->opaque = g_new(void *, 1); - *(void**)s->bs->backing_hd->opaque = s; + backing = bdrv_new(); + bdrv_set_backing_hd(s->bs, backing); + bdrv_unref(backing); + + s->bs->backing->bs->drv = &vvfat_write_target; + s->bs->backing->bs->opaque = g_new(void *, 1); + *(void**)s->bs->backing->bs->opaque = s; return 0; @@ -3004,7 +3013,6 @@ static BlockDriver bdrv_vvfat = { .bdrv_parse_filename = vvfat_parse_filename, .bdrv_file_open = vvfat_open, .bdrv_close = vvfat_close, - .bdrv_rebind = vvfat_rebind, .bdrv_read = vvfat_co_read, .bdrv_write = vvfat_co_write, diff --git a/qemu/block/win32-aio.c b/qemu/block/win32-aio.c index 64e86827b..2d509a9a7 100644 --- a/qemu/block/win32-aio.c +++ b/qemu/block/win32-aio.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/timer.h" #include "block/block_int.h" @@ -174,7 +175,7 @@ int win32_aio_attach(QEMUWin32AIOState *aio, HANDLE hfile) void win32_aio_detach_aio_context(QEMUWin32AIOState *aio, AioContext *old_context) { - aio_set_event_notifier(old_context, &aio->e, NULL); + aio_set_event_notifier(old_context, &aio->e, false, NULL); aio->is_aio_context_attached = false; } @@ -182,7 +183,8 @@ void win32_aio_attach_aio_context(QEMUWin32AIOState *aio, AioContext *new_context) { aio->is_aio_context_attached = true; - aio_set_event_notifier(new_context, &aio->e, win32_aio_completion_cb); + aio_set_event_notifier(new_context, &aio->e, false, + win32_aio_completion_cb); } QEMUWin32AIOState *win32_aio_init(void) diff --git a/qemu/block/write-threshold.c b/qemu/block/write-threshold.c index a53c1f5e6..cc2ca7183 100644 --- a/qemu/block/write-threshold.c +++ b/qemu/block/write-threshold.c @@ -10,8 +10,9 @@ * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include "block/block_int.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "block/write-threshold.h" #include "qemu/notify.h" #include "qapi-event.h" diff --git a/qemu/blockdev-nbd.c b/qemu/blockdev-nbd.c index bcdd18b3f..12cae0ea7 100644 --- a/qemu/blockdev-nbd.c +++ b/qemu/blockdev-nbd.c @@ -9,6 +9,7 @@ * later. See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "sysemu/blockdev.h" #include "sysemu/block-backend.h" #include "hw/block/block.h" @@ -17,58 +18,128 @@ #include "qmp-commands.h" #include "trace.h" #include "block/nbd.h" -#include "qemu/sockets.h" +#include "io/channel-socket.h" -static int server_fd = -1; +typedef struct NBDServerData { + QIOChannelSocket *listen_ioc; + int watch; + QCryptoTLSCreds *tlscreds; +} NBDServerData; -static void nbd_accept(void *opaque) +static NBDServerData *nbd_server; + + +static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition, + gpointer opaque) { - struct sockaddr_in addr; - socklen_t addr_len = sizeof(addr); + QIOChannelSocket *cioc; + + if (!nbd_server) { + return FALSE; + } - int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len); - if (fd >= 0 && !nbd_client_new(NULL, fd, nbd_client_put)) { - shutdown(fd, 2); - close(fd); + cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), + NULL); + if (!cioc) { + return TRUE; } + + nbd_client_new(NULL, cioc, + nbd_server->tlscreds, NULL, + nbd_client_put); + object_unref(OBJECT(cioc)); + return TRUE; } -void qmp_nbd_server_start(SocketAddress *addr, Error **errp) + +static void nbd_server_free(NBDServerData *server) { - if (server_fd != -1) { - error_setg(errp, "NBD server already running"); + if (!server) { return; } - server_fd = socket_listen(addr, errp); - if (server_fd != -1) { - qemu_set_fd_handler(server_fd, nbd_accept, NULL, NULL); + if (server->watch != -1) { + g_source_remove(server->watch); + } + object_unref(OBJECT(server->listen_ioc)); + if (server->tlscreds) { + object_unref(OBJECT(server->tlscreds)); } + + g_free(server); } -/* - * Hook into the BlockBackend notifiers to close the export when the - * backend is closed. - */ -typedef struct NBDCloseNotifier { - Notifier n; - NBDExport *exp; - QTAILQ_ENTRY(NBDCloseNotifier) next; -} NBDCloseNotifier; +static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) +{ + Object *obj; + QCryptoTLSCreds *creds; + + obj = object_resolve_path_component( + object_get_objects_root(), id); + if (!obj) { + error_setg(errp, "No TLS credentials with id '%s'", + id); + return NULL; + } + creds = (QCryptoTLSCreds *) + object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS); + if (!creds) { + error_setg(errp, "Object with id '%s' is not TLS credentials", + id); + return NULL; + } -static QTAILQ_HEAD(, NBDCloseNotifier) close_notifiers = - QTAILQ_HEAD_INITIALIZER(close_notifiers); + if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + error_setg(errp, + "Expecting TLS credentials with a server endpoint"); + return NULL; + } + object_ref(obj); + return creds; +} -static void nbd_close_notifier(Notifier *n, void *data) + +void qmp_nbd_server_start(SocketAddress *addr, + bool has_tls_creds, const char *tls_creds, + Error **errp) { - NBDCloseNotifier *cn = DO_UPCAST(NBDCloseNotifier, n, n); + if (nbd_server) { + error_setg(errp, "NBD server already running"); + return; + } + + nbd_server = g_new0(NBDServerData, 1); + nbd_server->watch = -1; + nbd_server->listen_ioc = qio_channel_socket_new(); + if (qio_channel_socket_listen_sync( + nbd_server->listen_ioc, addr, errp) < 0) { + goto error; + } + + if (has_tls_creds) { + nbd_server->tlscreds = nbd_get_tls_creds(tls_creds, errp); + if (!nbd_server->tlscreds) { + goto error; + } + + if (addr->type != SOCKET_ADDRESS_KIND_INET) { + error_setg(errp, "TLS is only supported with IPv4/IPv6"); + goto error; + } + } - notifier_remove(&cn->n); - QTAILQ_REMOVE(&close_notifiers, cn, next); + nbd_server->watch = qio_channel_add_watch( + QIO_CHANNEL(nbd_server->listen_ioc), + G_IO_IN, + nbd_accept, + NULL, + NULL); - nbd_export_close(cn->exp); - nbd_export_put(cn->exp); - g_free(cn); + return; + + error: + nbd_server_free(nbd_server); + nbd_server = NULL; } void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, @@ -76,9 +147,8 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, { BlockBackend *blk; NBDExport *exp; - NBDCloseNotifier *n; - if (server_fd == -1) { + if (!nbd_server) { error_setg(errp, "NBD server not running"); return; } @@ -114,23 +184,16 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, nbd_export_set_name(exp, device); - n = g_new0(NBDCloseNotifier, 1); - n->n.notify = nbd_close_notifier; - n->exp = exp; - blk_add_close_notifier(blk, &n->n); - QTAILQ_INSERT_TAIL(&close_notifiers, n, next); + /* The list of named exports has a strong reference to this export now and + * our only way of accessing it is through nbd_export_find(), so we can drop + * the strong reference that is @exp. */ + nbd_export_put(exp); } void qmp_nbd_server_stop(Error **errp) { - while (!QTAILQ_EMPTY(&close_notifiers)) { - NBDCloseNotifier *cn = QTAILQ_FIRST(&close_notifiers); - nbd_close_notifier(&cn->n, nbd_export_get_blockdev(cn->exp)); - } + nbd_export_close_all(); - if (server_fd != -1) { - qemu_set_fd_handler(server_fd, NULL, NULL, NULL); - close(server_fd); - server_fd = -1; - } + nbd_server_free(nbd_server); + nbd_server = NULL; } diff --git a/qemu/blockdev.c b/qemu/blockdev.c index 62a4586cd..f1f520a26 100644 --- a/qemu/blockdev.c +++ b/qemu/blockdev.c @@ -30,6 +30,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/block/block.h" @@ -49,6 +50,11 @@ #include "qmp-commands.h" #include "trace.h" #include "sysemu/arch_init.h" +#include "qemu/cutils.h" +#include "qemu/help_option.h" + +static QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states = + QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states); static const char *const if_name[IF_COUNT] = { [IF_NONE] = "none", @@ -124,14 +130,16 @@ void blockdev_mark_auto_del(BlockBackend *blk) return; } - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); + if (bs) { + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); - if (bs->job) { - block_job_cancel(bs->job); - } + if (bs->job) { + block_job_cancel(bs->job); + } - aio_context_release(aio_context); + aio_context_release(aio_context); + } dinfo->auto_del = 1; } @@ -141,6 +149,7 @@ void blockdev_auto_del(BlockBackend *blk) DriveInfo *dinfo = blk_legacy_dinfo(blk); if (dinfo && dinfo->auto_del) { + monitor_remove_blk(blk); blk_unref(blk); } } @@ -229,8 +238,8 @@ bool drive_check_orphaned(void) dinfo->type != IF_NONE) { fprintf(stderr, "Warning: Orphaned drive without device: " "id=%s,file=%s,if=%s,bus=%d,unit=%d\n", - blk_name(blk), blk_bs(blk)->filename, if_name[dinfo->type], - dinfo->bus, dinfo->unit); + blk_name(blk), blk_bs(blk) ? blk_bs(blk)->filename : "", + if_name[dinfo->type], dinfo->bus, dinfo->unit); rs = true; } } @@ -281,32 +290,6 @@ typedef struct { BlockDriverState *bs; } BDRVPutRefBH; -static void bdrv_put_ref_bh(void *opaque) -{ - BDRVPutRefBH *s = opaque; - - bdrv_unref(s->bs); - qemu_bh_delete(s->bh); - g_free(s); -} - -/* - * Release a BDS reference in a BH - * - * It is not safe to use bdrv_unref() from a callback function when the callers - * still need the BlockDriverState. In such cases we schedule a BH to release - * the reference. - */ -static void bdrv_put_ref_bh_schedule(BlockDriverState *bs) -{ - BDRVPutRefBH *s; - - s = g_new(BDRVPutRefBH, 1); - s->bh = qemu_bh_new(bdrv_put_ref_bh, s); - s->bs = bs; - qemu_bh_schedule(s->bh); -} - static int parse_block_error_action(const char *buf, bool is_read, Error **errp) { if (!strcmp(buf, "ignore")) { @@ -324,43 +307,181 @@ static int parse_block_error_action(const char *buf, bool is_read, Error **errp) } } -static bool check_throttle_config(ThrottleConfig *cfg, Error **errp) +static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals, + Error **errp) { - if (throttle_conflicting(cfg)) { - error_setg(errp, "bps/iops/max total values and read/write values" - " cannot be used at the same time"); - return false; - } + const QListEntry *entry; + for (entry = qlist_first(intervals); entry; entry = qlist_next(entry)) { + switch (qobject_type(entry->value)) { + + case QTYPE_QSTRING: { + unsigned long long length; + 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); + } else { + error_setg(errp, "Invalid interval length: %s", str); + return false; + } + break; + } - if (!throttle_is_valid(cfg)) { - error_setg(errp, "bps/iops/maxs values must be 0 or greater"); - return false; - } + case QTYPE_QINT: { + int64_t length = qint_get_int(qobject_to_qint(entry->value)); + if (length > 0 && length <= UINT_MAX) { + block_acct_add_interval(stats, (unsigned) length); + } else { + error_setg(errp, "Invalid interval length: %" PRId64, length); + return false; + } + break; + } + default: + error_setg(errp, "The specification of stats-intervals is invalid"); + return false; + } + } return true; } typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType; +/* All parameters but @opts are optional and may be set to NULL. */ +static void extract_common_blockdev_options(QemuOpts *opts, int *bdrv_flags, + const char **throttling_group, ThrottleConfig *throttle_cfg, + BlockdevDetectZeroesOptions *detect_zeroes, Error **errp) +{ + const char *discard; + Error *local_error = NULL; + const char *aio; + + if (bdrv_flags) { + if (!qemu_opt_get_bool(opts, "read-only", false)) { + *bdrv_flags |= BDRV_O_RDWR; + } + if (qemu_opt_get_bool(opts, "copy-on-read", false)) { + *bdrv_flags |= BDRV_O_COPY_ON_READ; + } + + if ((discard = qemu_opt_get(opts, "discard")) != NULL) { + if (bdrv_parse_discard_flags(discard, bdrv_flags) != 0) { + error_setg(errp, "Invalid discard option"); + return; + } + } + + if ((aio = qemu_opt_get(opts, "aio")) != NULL) { + if (!strcmp(aio, "native")) { + *bdrv_flags |= BDRV_O_NATIVE_AIO; + } else if (!strcmp(aio, "threads")) { + /* this is the default */ + } else { + error_setg(errp, "invalid aio option"); + return; + } + } + } + + /* disk I/O throttling */ + if (throttling_group) { + *throttling_group = qemu_opt_get(opts, "throttling.group"); + } + + if (throttle_cfg) { + throttle_config_init(throttle_cfg); + throttle_cfg->buckets[THROTTLE_BPS_TOTAL].avg = + qemu_opt_get_number(opts, "throttling.bps-total", 0); + throttle_cfg->buckets[THROTTLE_BPS_READ].avg = + qemu_opt_get_number(opts, "throttling.bps-read", 0); + throttle_cfg->buckets[THROTTLE_BPS_WRITE].avg = + qemu_opt_get_number(opts, "throttling.bps-write", 0); + throttle_cfg->buckets[THROTTLE_OPS_TOTAL].avg = + qemu_opt_get_number(opts, "throttling.iops-total", 0); + throttle_cfg->buckets[THROTTLE_OPS_READ].avg = + qemu_opt_get_number(opts, "throttling.iops-read", 0); + throttle_cfg->buckets[THROTTLE_OPS_WRITE].avg = + qemu_opt_get_number(opts, "throttling.iops-write", 0); + + throttle_cfg->buckets[THROTTLE_BPS_TOTAL].max = + qemu_opt_get_number(opts, "throttling.bps-total-max", 0); + throttle_cfg->buckets[THROTTLE_BPS_READ].max = + qemu_opt_get_number(opts, "throttling.bps-read-max", 0); + throttle_cfg->buckets[THROTTLE_BPS_WRITE].max = + qemu_opt_get_number(opts, "throttling.bps-write-max", 0); + throttle_cfg->buckets[THROTTLE_OPS_TOTAL].max = + qemu_opt_get_number(opts, "throttling.iops-total-max", 0); + throttle_cfg->buckets[THROTTLE_OPS_READ].max = + qemu_opt_get_number(opts, "throttling.iops-read-max", 0); + throttle_cfg->buckets[THROTTLE_OPS_WRITE].max = + qemu_opt_get_number(opts, "throttling.iops-write-max", 0); + + throttle_cfg->buckets[THROTTLE_BPS_TOTAL].burst_length = + qemu_opt_get_number(opts, "throttling.bps-total-max-length", 1); + throttle_cfg->buckets[THROTTLE_BPS_READ].burst_length = + qemu_opt_get_number(opts, "throttling.bps-read-max-length", 1); + throttle_cfg->buckets[THROTTLE_BPS_WRITE].burst_length = + qemu_opt_get_number(opts, "throttling.bps-write-max-length", 1); + throttle_cfg->buckets[THROTTLE_OPS_TOTAL].burst_length = + qemu_opt_get_number(opts, "throttling.iops-total-max-length", 1); + throttle_cfg->buckets[THROTTLE_OPS_READ].burst_length = + qemu_opt_get_number(opts, "throttling.iops-read-max-length", 1); + throttle_cfg->buckets[THROTTLE_OPS_WRITE].burst_length = + qemu_opt_get_number(opts, "throttling.iops-write-max-length", 1); + + throttle_cfg->op_size = + qemu_opt_get_number(opts, "throttling.iops-size", 0); + + if (!throttle_is_valid(throttle_cfg, errp)) { + return; + } + } + + if (detect_zeroes) { + *detect_zeroes = + qapi_enum_parse(BlockdevDetectZeroesOptions_lookup, + qemu_opt_get(opts, "detect-zeroes"), + BLOCKDEV_DETECT_ZEROES_OPTIONS__MAX, + BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, + &local_error); + if (local_error) { + error_propagate(errp, local_error); + return; + } + + if (bdrv_flags && + *detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP && + !(*bdrv_flags & BDRV_O_UNMAP)) + { + error_setg(errp, "setting detect-zeroes to unmap is not allowed " + "without setting discard operation to unmap"); + return; + } + } +} + /* Takes the ownership of bs_opts */ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, Error **errp) { const char *buf; - int ro = 0; int bdrv_flags = 0; int on_read_error, on_write_error; + bool account_invalid, account_failed; + bool writethrough; BlockBackend *blk; BlockDriverState *bs; ThrottleConfig cfg; int snapshot = 0; - bool copy_on_read; Error *error = NULL; QemuOpts *opts; + QDict *interval_dict = NULL; + QList *interval_list = NULL; const char *id; - bool has_driver_specific_opts; - BlockdevDetectZeroesOptions detect_zeroes; - const char *throttling_group; + BlockdevDetectZeroesOptions detect_zeroes = + BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; + const char *throttling_group = NULL; /* Check common options by copying from bs_opts to opts, all other options * stay in bs_opts for processing by bdrv_open(). */ @@ -381,42 +502,29 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, qdict_del(bs_opts, "id"); } - has_driver_specific_opts = !!qdict_size(bs_opts); - /* extract parameters */ snapshot = qemu_opt_get_bool(opts, "snapshot", 0); - ro = qemu_opt_get_bool(opts, "read-only", 0); - copy_on_read = qemu_opt_get_bool(opts, "copy-on-read", false); - if ((buf = qemu_opt_get(opts, "discard")) != NULL) { - if (bdrv_parse_discard_flags(buf, &bdrv_flags) != 0) { - error_setg(errp, "invalid discard option"); - goto early_err; - } - } + account_invalid = qemu_opt_get_bool(opts, "stats-account-invalid", true); + account_failed = qemu_opt_get_bool(opts, "stats-account-failed", true); - if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true)) { - bdrv_flags |= BDRV_O_CACHE_WB; - } - if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_DIRECT, false)) { - bdrv_flags |= BDRV_O_NOCACHE; - } - if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) { - bdrv_flags |= BDRV_O_NO_FLUSH; + writethrough = !qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true); + + qdict_extract_subqdict(bs_opts, &interval_dict, "stats-intervals."); + qdict_array_split(interval_dict, &interval_list); + + if (qdict_size(interval_dict) != 0) { + error_setg(errp, "Invalid option stats-intervals.%s", + qdict_first(interval_dict)->key); + goto early_err; } -#ifdef CONFIG_LINUX_AIO - if ((buf = qemu_opt_get(opts, "aio")) != NULL) { - if (!strcmp(buf, "native")) { - bdrv_flags |= BDRV_O_NATIVE_AIO; - } else if (!strcmp(buf, "threads")) { - /* this is the default */ - } else { - error_setg(errp, "invalid aio option"); - goto early_err; - } + extract_common_blockdev_options(opts, &bdrv_flags, &throttling_group, &cfg, + &detect_zeroes, &error); + if (error) { + error_propagate(errp, error); + goto early_err; } -#endif if ((buf = qemu_opt_get(opts, "format")) != NULL) { if (is_help_option(buf)) { @@ -433,43 +541,6 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, qdict_put(bs_opts, "driver", qstring_from_str(buf)); } - /* disk I/O throttling */ - memset(&cfg, 0, sizeof(cfg)); - cfg.buckets[THROTTLE_BPS_TOTAL].avg = - qemu_opt_get_number(opts, "throttling.bps-total", 0); - cfg.buckets[THROTTLE_BPS_READ].avg = - qemu_opt_get_number(opts, "throttling.bps-read", 0); - cfg.buckets[THROTTLE_BPS_WRITE].avg = - qemu_opt_get_number(opts, "throttling.bps-write", 0); - cfg.buckets[THROTTLE_OPS_TOTAL].avg = - qemu_opt_get_number(opts, "throttling.iops-total", 0); - cfg.buckets[THROTTLE_OPS_READ].avg = - qemu_opt_get_number(opts, "throttling.iops-read", 0); - cfg.buckets[THROTTLE_OPS_WRITE].avg = - qemu_opt_get_number(opts, "throttling.iops-write", 0); - - cfg.buckets[THROTTLE_BPS_TOTAL].max = - qemu_opt_get_number(opts, "throttling.bps-total-max", 0); - cfg.buckets[THROTTLE_BPS_READ].max = - qemu_opt_get_number(opts, "throttling.bps-read-max", 0); - cfg.buckets[THROTTLE_BPS_WRITE].max = - qemu_opt_get_number(opts, "throttling.bps-write-max", 0); - cfg.buckets[THROTTLE_OPS_TOTAL].max = - qemu_opt_get_number(opts, "throttling.iops-total-max", 0); - cfg.buckets[THROTTLE_OPS_READ].max = - qemu_opt_get_number(opts, "throttling.iops-read-max", 0); - cfg.buckets[THROTTLE_OPS_WRITE].max = - qemu_opt_get_number(opts, "throttling.iops-write-max", 0); - - cfg.op_size = qemu_opt_get_number(opts, "throttling.iops-size", 0); - - throttling_group = qemu_opt_get(opts, "throttling.group"); - - if (!check_throttle_config(&cfg, &error)) { - error_propagate(errp, error); - goto early_err; - } - on_write_error = BLOCKDEV_ON_ERROR_ENOSPC; if ((buf = qemu_opt_get(opts, "werror")) != NULL) { on_write_error = parse_block_error_action(buf, 0, &error); @@ -488,34 +559,32 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, } } - detect_zeroes = - qapi_enum_parse(BlockdevDetectZeroesOptions_lookup, - qemu_opt_get(opts, "detect-zeroes"), - BLOCKDEV_DETECT_ZEROES_OPTIONS_MAX, - BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, - &error); - if (error) { - error_propagate(errp, error); - goto early_err; - } - - if (detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP && - !(bdrv_flags & BDRV_O_UNMAP)) { - error_setg(errp, "setting detect-zeroes to unmap is not allowed " - "without setting discard operation to unmap"); - goto early_err; + if (snapshot) { + bdrv_flags |= BDRV_O_SNAPSHOT; } /* init */ - if ((!file || !*file) && !has_driver_specific_opts) { - blk = blk_new_with_bs(qemu_opts_id(opts), errp); + if ((!file || !*file) && !qdict_size(bs_opts)) { + BlockBackendRootState *blk_rs; + + blk = blk_new(errp); if (!blk) { goto early_err; } - bs = blk_bs(blk); - bs->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0; - bs->read_only = ro; + blk_rs = blk_get_root_state(blk); + blk_rs->open_flags = bdrv_flags; + blk_rs->read_only = !(bdrv_flags & BDRV_O_RDWR); + blk_rs->detect_zeroes = detect_zeroes; + + if (throttle_enabled(&cfg)) { + if (!throttling_group) { + throttling_group = blk_name(blk); + } + blk_rs->throttle_group = g_strdup(throttling_group); + blk_rs->throttle_state = throttle_group_incref(throttling_group); + blk_rs->throttle_state->cfg = cfg; + } QDECREF(bs_opts); } else { @@ -523,58 +592,149 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, file = NULL; } - if (snapshot) { - /* always use cache=unsafe with snapshot */ - bdrv_flags &= ~BDRV_O_CACHE_MASK; - bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH); - } - - if (copy_on_read) { - bdrv_flags |= BDRV_O_COPY_ON_READ; - } + /* bdrv_open() defaults to the values in bdrv_flags (for compatibility + * with other callers) rather than what we want as the real defaults. + * Apply the defaults here instead. */ + qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off"); + qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off"); + assert((bdrv_flags & BDRV_O_CACHE_MASK) == 0); if (runstate_check(RUN_STATE_INMIGRATE)) { - bdrv_flags |= BDRV_O_INCOMING; + bdrv_flags |= BDRV_O_INACTIVE; } - bdrv_flags |= ro ? 0 : BDRV_O_RDWR; - - blk = blk_new_open(qemu_opts_id(opts), file, NULL, bs_opts, bdrv_flags, - errp); + blk = blk_new_open(file, NULL, bs_opts, bdrv_flags, errp); if (!blk) { goto err_no_bs_opts; } bs = blk_bs(blk); - } - bs->detect_zeroes = detect_zeroes; + bs->detect_zeroes = detect_zeroes; - bdrv_set_on_error(bs, on_read_error, on_write_error); + /* disk I/O throttling */ + if (throttle_enabled(&cfg)) { + if (!throttling_group) { + throttling_group = blk_name(blk); + } + bdrv_io_limits_enable(bs, throttling_group); + bdrv_set_io_limits(bs, &cfg); + } - /* disk I/O throttling */ - if (throttle_enabled(&cfg)) { - if (!throttling_group) { - throttling_group = blk_name(blk); + if (bdrv_key_required(bs)) { + autostart = 0; + } + + block_acct_init(blk_get_stats(blk), account_invalid, account_failed); + + if (!parse_stats_intervals(blk_get_stats(blk), interval_list, errp)) { + blk_unref(blk); + blk = NULL; + goto err_no_bs_opts; } - bdrv_io_limits_enable(bs, throttling_group); - bdrv_set_io_limits(bs, &cfg); } - if (bdrv_key_required(bs)) { - autostart = 0; + blk_set_enable_write_cache(blk, !writethrough); + blk_set_on_error(blk, on_read_error, on_write_error); + + if (!monitor_add_blk(blk, qemu_opts_id(opts), errp)) { + blk_unref(blk); + blk = NULL; + goto err_no_bs_opts; } err_no_bs_opts: qemu_opts_del(opts); + QDECREF(interval_dict); + QDECREF(interval_list); return blk; early_err: qemu_opts_del(opts); + QDECREF(interval_dict); + QDECREF(interval_list); err_no_opts: QDECREF(bs_opts); return NULL; } +static QemuOptsList qemu_root_bds_opts; + +/* Takes the ownership of bs_opts */ +static BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp) +{ + BlockDriverState *bs; + QemuOpts *opts; + Error *local_error = NULL; + BlockdevDetectZeroesOptions detect_zeroes; + int ret; + int bdrv_flags = 0; + + opts = qemu_opts_create(&qemu_root_bds_opts, NULL, 1, errp); + if (!opts) { + goto fail; + } + + qemu_opts_absorb_qdict(opts, bs_opts, &local_error); + if (local_error) { + error_propagate(errp, local_error); + goto fail; + } + + extract_common_blockdev_options(opts, &bdrv_flags, NULL, NULL, + &detect_zeroes, &local_error); + if (local_error) { + error_propagate(errp, local_error); + goto fail; + } + + /* bdrv_open() defaults to the values in bdrv_flags (for compatibility + * with other callers) rather than what we want as the real defaults. + * Apply the defaults here instead. */ + qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off"); + qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off"); + + if (runstate_check(RUN_STATE_INMIGRATE)) { + bdrv_flags |= BDRV_O_INACTIVE; + } + + bs = NULL; + ret = bdrv_open(&bs, NULL, NULL, bs_opts, bdrv_flags, errp); + if (ret < 0) { + goto fail_no_bs_opts; + } + + bs->detect_zeroes = detect_zeroes; + +fail_no_bs_opts: + qemu_opts_del(opts); + return bs; + +fail: + qemu_opts_del(opts); + QDECREF(bs_opts); + return NULL; +} + +void blockdev_close_all_bdrv_states(void) +{ + BlockDriverState *bs, *next_bs; + + QTAILQ_FOREACH_SAFE(bs, &monitor_bdrv_states, monitor_list, next_bs) { + AioContext *ctx = bdrv_get_aio_context(bs); + + aio_context_acquire(ctx); + bdrv_unref(bs); + aio_context_release(ctx); + } +} + +/* Iterates over the list of monitor-owned BlockDriverStates */ +BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs) +{ + return bs ? QTAILQ_NEXT(bs, monitor_list) + : QTAILQ_FIRST(&monitor_bdrv_states); +} + static void qemu_opt_rename(QemuOpts *opts, const char *from, const char *to, Error **errp) { @@ -737,8 +897,9 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) value = qemu_opt_get(all_opts, "cache"); if (value) { int flags = 0; + bool writethrough; - if (bdrv_parse_cache_flags(value, &flags) != 0) { + if (bdrv_parse_cache_mode(value, &flags, &writethrough) != 0) { error_report("invalid cache option"); return NULL; } @@ -746,7 +907,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) /* Specific options take precedence */ if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_WB)) { qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_WB, - !!(flags & BDRV_O_CACHE_WB), &error_abort); + !writethrough, &error_abort); } if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_DIRECT)) { qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_DIRECT, @@ -1031,14 +1192,28 @@ void hmp_commit(Monitor *mon, const QDict *qdict) int ret; if (!strcmp(device, "all")) { - ret = bdrv_commit_all(); + ret = blk_commit_all(); } else { + BlockDriverState *bs; + AioContext *aio_context; + blk = blk_by_name(device); if (!blk) { monitor_printf(mon, "Device '%s' not found\n", device); return; } - ret = bdrv_commit(blk_bs(blk)); + if (!blk_is_available(blk)) { + monitor_printf(mon, "Device '%s' has no medium\n", device); + return; + } + + bs = blk_bs(blk); + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + ret = bdrv_commit(bs); + + aio_context_release(aio_context); } if (ret < 0) { monitor_printf(mon, "'commit' error for '%s': %s\n", device, @@ -1046,16 +1221,13 @@ void hmp_commit(Monitor *mon, const QDict *qdict) } } -static void blockdev_do_action(int kind, void *data, Error **errp) +static void blockdev_do_action(TransactionAction *action, Error **errp) { - TransactionAction action; TransactionActionList list; - action.kind = kind; - action.data = data; - list.value = &action; + list.value = action; list.next = NULL; - qmp_transaction(&list, errp); + qmp_transaction(&list, false, NULL, errp); } void qmp_blockdev_snapshot_sync(bool has_device, const char *device, @@ -1066,7 +1238,7 @@ void qmp_blockdev_snapshot_sync(bool has_device, const char *device, bool has_format, const char *format, bool has_mode, NewImageMode mode, Error **errp) { - BlockdevSnapshot snapshot = { + BlockdevSnapshotSync snapshot = { .has_device = has_device, .device = (char *) device, .has_node_name = has_node_name, @@ -1079,8 +1251,25 @@ void qmp_blockdev_snapshot_sync(bool has_device, const char *device, .has_mode = has_mode, .mode = mode, }; - blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC, - &snapshot, errp); + TransactionAction action = { + .type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC, + .u.blockdev_snapshot_sync.data = &snapshot, + }; + blockdev_do_action(&action, errp); +} + +void qmp_blockdev_snapshot(const char *node, const char *overlay, + Error **errp) +{ + BlockdevSnapshot snapshot_data = { + .node = (char *) node, + .overlay = (char *) overlay + }; + TransactionAction action = { + .type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT, + .u.blockdev_snapshot.data = &snapshot_data, + }; + blockdev_do_action(&action, errp); } void qmp_blockdev_snapshot_internal_sync(const char *device, @@ -1091,9 +1280,11 @@ void qmp_blockdev_snapshot_internal_sync(const char *device, .device = (char *) device, .name = (char *) name }; - - blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC, - &snapshot, errp); + TransactionAction action = { + .type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC, + .u.blockdev_snapshot_internal_sync.data = &snapshot, + }; + blockdev_do_action(&action, errp); } SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, @@ -1117,7 +1308,9 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, "Device '%s' not found", device); return NULL; } - bs = blk_bs(blk); + + aio_context = blk_get_aio_context(blk); + aio_context_acquire(aio_context); if (!has_id) { id = NULL; @@ -1129,11 +1322,14 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, if (!id && !name) { error_setg(errp, "Name or id must be provided"); - return NULL; + goto out_aio_context; } - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); + if (!blk_is_available(blk)) { + error_setg(errp, "Device '%s' has no medium", device); + goto out_aio_context; + } + bs = blk_bs(blk); if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, errp)) { goto out_aio_context; @@ -1240,43 +1436,75 @@ static BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node, /* New and old BlockDriverState structs for atomic group operations */ -typedef struct BlkTransactionState BlkTransactionState; +typedef struct BlkActionState BlkActionState; -/* Only prepare() may fail. In a single transaction, only one of commit() or - abort() will be called, clean() will always be called if it present. */ -typedef struct BdrvActionOps { - /* Size of state struct, in bytes. */ +/** + * BlkActionOps: + * Table of operations that define an Action. + * + * @instance_size: Size of state struct, in bytes. + * @prepare: Prepare the work, must NOT be NULL. + * @commit: Commit the changes, can be NULL. + * @abort: Abort the changes on fail, can be NULL. + * @clean: Clean up resources after all transaction actions have called + * commit() or abort(). Can be NULL. + * + * Only prepare() may fail. In a single transaction, only one of commit() or + * abort() will be called. clean() will always be called if it is present. + */ +typedef struct BlkActionOps { size_t instance_size; - /* Prepare the work, must NOT be NULL. */ - void (*prepare)(BlkTransactionState *common, Error **errp); - /* Commit the changes, can be NULL. */ - void (*commit)(BlkTransactionState *common); - /* Abort the changes on fail, can be NULL. */ - void (*abort)(BlkTransactionState *common); - /* Clean up resource in the end, can be NULL. */ - void (*clean)(BlkTransactionState *common); -} BdrvActionOps; + void (*prepare)(BlkActionState *common, Error **errp); + void (*commit)(BlkActionState *common); + void (*abort)(BlkActionState *common); + void (*clean)(BlkActionState *common); +} BlkActionOps; -/* - * This structure must be arranged as first member in child type, assuming - * that compiler will also arrange it to the same address with parent instance. - * Later it will be used in free(). +/** + * BlkActionState: + * Describes one Action's state within a Transaction. + * + * @action: QAPI-defined enum identifying which Action to perform. + * @ops: Table of ActionOps this Action can perform. + * @block_job_txn: Transaction which this action belongs to. + * @entry: List membership for all Actions in this Transaction. + * + * This structure must be arranged as first member in a subclassed type, + * assuming that the compiler will also arrange it to the same offsets as the + * base class. */ -struct BlkTransactionState { +struct BlkActionState { TransactionAction *action; - const BdrvActionOps *ops; - QSIMPLEQ_ENTRY(BlkTransactionState) entry; + const BlkActionOps *ops; + BlockJobTxn *block_job_txn; + TransactionProperties *txn_props; + QSIMPLEQ_ENTRY(BlkActionState) entry; }; /* internal snapshot private data */ typedef struct InternalSnapshotState { - BlkTransactionState common; + BlkActionState common; BlockDriverState *bs; AioContext *aio_context; QEMUSnapshotInfo sn; + bool created; } InternalSnapshotState; -static void internal_snapshot_prepare(BlkTransactionState *common, + +static int action_check_completion_mode(BlkActionState *s, Error **errp) +{ + if (s->txn_props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) { + error_setg(errp, + "Action '%s' does not support Transaction property " + "completion-mode = %s", + TransactionActionKind_lookup[s->action->type], + ActionCompletionMode_lookup[s->txn_props->completion_mode]); + return -1; + } + return 0; +} + +static void internal_snapshot_prepare(BlkActionState *common, Error **errp) { Error *local_err = NULL; @@ -1291,9 +1519,9 @@ static void internal_snapshot_prepare(BlkTransactionState *common, InternalSnapshotState *state; int ret1; - g_assert(common->action->kind == + g_assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC); - internal = common->action->blockdev_snapshot_internal_sync; + internal = common->action->u.blockdev_snapshot_internal_sync.data; state = DO_UPCAST(InternalSnapshotState, common, common); /* 1. parse input */ @@ -1301,22 +1529,29 @@ static void internal_snapshot_prepare(BlkTransactionState *common, name = internal->name; /* 2. check for validation */ + if (action_check_completion_mode(common, errp) < 0) { + return; + } + blk = blk_by_name(device); if (!blk) { error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "Device '%s' not found", device); return; } - bs = blk_bs(blk); /* AioContext is released in .clean() */ - state->aio_context = bdrv_get_aio_context(bs); + state->aio_context = blk_get_aio_context(blk); aio_context_acquire(state->aio_context); - if (!bdrv_is_inserted(bs)) { + if (!blk_is_available(blk)) { error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); return; } + bs = blk_bs(blk); + + state->bs = bs; + bdrv_drained_begin(bs); if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) { return; @@ -1369,10 +1604,10 @@ static void internal_snapshot_prepare(BlkTransactionState *common, } /* 4. succeed, mark a snapshot is created */ - state->bs = bs; + state->created = true; } -static void internal_snapshot_abort(BlkTransactionState *common) +static void internal_snapshot_abort(BlkActionState *common) { InternalSnapshotState *state = DO_UPCAST(InternalSnapshotState, common, common); @@ -1380,106 +1615,96 @@ static void internal_snapshot_abort(BlkTransactionState *common) QEMUSnapshotInfo *sn = &state->sn; Error *local_error = NULL; - if (!bs) { + if (!state->created) { return; } if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) { - error_report("Failed to delete snapshot with id '%s' and name '%s' on " - "device '%s' in abort: %s", - sn->id_str, - sn->name, - bdrv_get_device_name(bs), - error_get_pretty(local_error)); - error_free(local_error); + error_reportf_err(local_error, + "Failed to delete snapshot with id '%s' and " + "name '%s' on device '%s' in abort: ", + sn->id_str, sn->name, + bdrv_get_device_name(bs)); } } -static void internal_snapshot_clean(BlkTransactionState *common) +static void internal_snapshot_clean(BlkActionState *common) { InternalSnapshotState *state = DO_UPCAST(InternalSnapshotState, common, common); if (state->aio_context) { + if (state->bs) { + bdrv_drained_end(state->bs); + } aio_context_release(state->aio_context); } } /* external snapshot private data */ typedef struct ExternalSnapshotState { - BlkTransactionState common; + BlkActionState common; BlockDriverState *old_bs; BlockDriverState *new_bs; AioContext *aio_context; } ExternalSnapshotState; -static void external_snapshot_prepare(BlkTransactionState *common, +static void external_snapshot_prepare(BlkActionState *common, Error **errp) { - BlockDriver *drv; - int flags, ret; + int flags = 0, ret; QDict *options = NULL; Error *local_err = NULL; - bool has_device = false; + /* Device and node name of the image to generate the snapshot from */ const char *device; - bool has_node_name = false; const char *node_name; - bool has_snapshot_node_name = false; - const char *snapshot_node_name; + /* Reference to the new image (for 'blockdev-snapshot') */ + const char *snapshot_ref; + /* File name of the new image (for 'blockdev-snapshot-sync') */ const char *new_image_file; - const char *format = "qcow2"; - enum NewImageMode mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; ExternalSnapshotState *state = DO_UPCAST(ExternalSnapshotState, common, common); TransactionAction *action = common->action; - /* get parameters */ - g_assert(action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC); - - has_device = action->blockdev_snapshot_sync->has_device; - device = action->blockdev_snapshot_sync->device; - has_node_name = action->blockdev_snapshot_sync->has_node_name; - node_name = action->blockdev_snapshot_sync->node_name; - has_snapshot_node_name = - action->blockdev_snapshot_sync->has_snapshot_node_name; - snapshot_node_name = action->blockdev_snapshot_sync->snapshot_node_name; - - new_image_file = action->blockdev_snapshot_sync->snapshot_file; - if (action->blockdev_snapshot_sync->has_format) { - format = action->blockdev_snapshot_sync->format; - } - if (action->blockdev_snapshot_sync->has_mode) { - mode = action->blockdev_snapshot_sync->mode; + /* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar + * purpose but a different set of parameters */ + switch (action->type) { + case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT: + { + BlockdevSnapshot *s = action->u.blockdev_snapshot.data; + device = s->node; + node_name = s->node; + new_image_file = NULL; + snapshot_ref = s->overlay; + } + break; + case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC: + { + BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync.data; + device = s->has_device ? s->device : NULL; + node_name = s->has_node_name ? s->node_name : NULL; + new_image_file = s->snapshot_file; + snapshot_ref = NULL; + } + break; + default: + g_assert_not_reached(); } /* start processing */ - drv = bdrv_find_format(format); - if (!drv) { - error_setg(errp, QERR_INVALID_BLOCK_FORMAT, format); - return; - } - - state->old_bs = bdrv_lookup_bs(has_device ? device : NULL, - has_node_name ? node_name : NULL, - &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - if (has_node_name && !has_snapshot_node_name) { - error_setg(errp, "New snapshot node name missing"); + if (action_check_completion_mode(common, errp) < 0) { return; } - if (has_snapshot_node_name && bdrv_find_node(snapshot_node_name)) { - error_setg(errp, "New snapshot node name already existing"); + state->old_bs = bdrv_lookup_bs(device, node_name, errp); + if (!state->old_bs) { return; } /* Acquire AioContext now so any threads operating on old_bs stop */ state->aio_context = bdrv_get_aio_context(state->old_bs); aio_context_acquire(state->aio_context); + bdrv_drained_begin(state->old_bs); if (!bdrv_is_inserted(state->old_bs)) { error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); @@ -1503,38 +1728,85 @@ static void external_snapshot_prepare(BlkTransactionState *common, return; } - flags = state->old_bs->open_flags; + if (action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC) { + BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync.data; + const char *format = s->has_format ? s->format : "qcow2"; + enum NewImageMode mode; + const char *snapshot_node_name = + s->has_snapshot_node_name ? s->snapshot_node_name : NULL; - /* create new image w/backing file */ - if (mode != NEW_IMAGE_MODE_EXISTING) { - bdrv_img_create(new_image_file, format, - state->old_bs->filename, - state->old_bs->drv->format_name, - NULL, -1, flags, &local_err, false); - if (local_err) { - error_propagate(errp, local_err); + if (node_name && !snapshot_node_name) { + error_setg(errp, "New snapshot node name missing"); + return; + } + + if (snapshot_node_name && + bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) { + error_setg(errp, "New snapshot node name already in use"); return; } - } - if (has_snapshot_node_name) { + flags = state->old_bs->open_flags; + flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_COPY_ON_READ); + + /* create new image w/backing file */ + mode = s->has_mode ? s->mode : NEW_IMAGE_MODE_ABSOLUTE_PATHS; + if (mode != NEW_IMAGE_MODE_EXISTING) { + int64_t size = bdrv_getlength(state->old_bs); + if (size < 0) { + error_setg_errno(errp, -size, "bdrv_getlength failed"); + return; + } + bdrv_img_create(new_image_file, format, + state->old_bs->filename, + state->old_bs->drv->format_name, + NULL, size, flags, &local_err, false); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + options = qdict_new(); - qdict_put(options, "node-name", - qstring_from_str(snapshot_node_name)); + if (s->has_snapshot_node_name) { + qdict_put(options, "node-name", + qstring_from_str(snapshot_node_name)); + } + qdict_put(options, "driver", qstring_from_str(format)); + + flags |= BDRV_O_NO_BACKING; } - /* TODO Inherit bs->options or only take explicit options with an - * extended QMP command? */ assert(state->new_bs == NULL); - ret = bdrv_open(&state->new_bs, new_image_file, NULL, options, - flags | BDRV_O_NO_BACKING, drv, &local_err); + ret = bdrv_open(&state->new_bs, new_image_file, snapshot_ref, options, + flags, errp); /* We will manually add the backing_hd field to the bs later */ if (ret != 0) { - error_propagate(errp, local_err); + return; + } + + if (state->new_bs->blk != NULL) { + error_setg(errp, "The snapshot is already in use by %s", + blk_name(state->new_bs->blk)); + return; + } + + if (bdrv_op_is_blocked(state->new_bs, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, + errp)) { + return; + } + + if (state->new_bs->backing != NULL) { + error_setg(errp, "The snapshot already has a backing image"); + return; + } + + if (!state->new_bs->drv->supports_backing) { + error_setg(errp, "The snapshot does not support backing images"); } } -static void external_snapshot_commit(BlkTransactionState *common) +static void external_snapshot_commit(BlkActionState *common) { ExternalSnapshotState *state = DO_UPCAST(ExternalSnapshotState, common, common); @@ -1546,41 +1818,59 @@ static void external_snapshot_commit(BlkTransactionState *common) /* We don't need (or want) to use the transactional * bdrv_reopen_multiple() across all the entries at once, because we * don't want to abort all of them if one of them fails the reopen */ - bdrv_reopen(state->new_bs, state->new_bs->open_flags & ~BDRV_O_RDWR, - NULL); - - aio_context_release(state->aio_context); + if (!state->old_bs->copy_on_read) { + bdrv_reopen(state->old_bs, state->old_bs->open_flags & ~BDRV_O_RDWR, + NULL); + } } -static void external_snapshot_abort(BlkTransactionState *common) +static void external_snapshot_abort(BlkActionState *common) { ExternalSnapshotState *state = DO_UPCAST(ExternalSnapshotState, common, common); if (state->new_bs) { bdrv_unref(state->new_bs); } +} + +static void external_snapshot_clean(BlkActionState *common) +{ + ExternalSnapshotState *state = + DO_UPCAST(ExternalSnapshotState, common, common); if (state->aio_context) { + bdrv_drained_end(state->old_bs); aio_context_release(state->aio_context); } } typedef struct DriveBackupState { - BlkTransactionState common; + BlkActionState common; BlockDriverState *bs; AioContext *aio_context; BlockJob *job; } DriveBackupState; -static void drive_backup_prepare(BlkTransactionState *common, Error **errp) +static void do_drive_backup(const char *device, const char *target, + bool has_format, const char *format, + enum MirrorSyncMode sync, + bool has_mode, enum NewImageMode mode, + bool has_speed, int64_t speed, + bool has_bitmap, const char *bitmap, + bool has_on_source_error, + BlockdevOnError on_source_error, + bool has_on_target_error, + BlockdevOnError on_target_error, + BlockJobTxn *txn, Error **errp); + +static void drive_backup_prepare(BlkActionState *common, Error **errp) { DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); - BlockDriverState *bs; BlockBackend *blk; DriveBackup *backup; Error *local_err = NULL; - assert(common->action->kind == TRANSACTION_ACTION_KIND_DRIVE_BACKUP); - backup = common->action->drive_backup; + assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP); + backup = common->action->u.drive_backup.data; blk = blk_by_name(backup->device); if (!blk) { @@ -1588,31 +1878,36 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp) "Device '%s' not found", backup->device); return; } - bs = blk_bs(blk); + + if (!blk_is_available(blk)) { + error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, backup->device); + return; + } /* AioContext is released in .clean() */ - state->aio_context = bdrv_get_aio_context(bs); + state->aio_context = blk_get_aio_context(blk); aio_context_acquire(state->aio_context); - - qmp_drive_backup(backup->device, backup->target, - backup->has_format, backup->format, - backup->sync, - backup->has_mode, backup->mode, - backup->has_speed, backup->speed, - backup->has_bitmap, backup->bitmap, - backup->has_on_source_error, backup->on_source_error, - backup->has_on_target_error, backup->on_target_error, - &local_err); + bdrv_drained_begin(blk_bs(blk)); + state->bs = blk_bs(blk); + + do_drive_backup(backup->device, backup->target, + backup->has_format, backup->format, + backup->sync, + backup->has_mode, backup->mode, + backup->has_speed, backup->speed, + backup->has_bitmap, backup->bitmap, + backup->has_on_source_error, backup->on_source_error, + backup->has_on_target_error, backup->on_target_error, + common->block_job_txn, &local_err); if (local_err) { error_propagate(errp, local_err); return; } - state->bs = bs; state->job = state->bs->job; } -static void drive_backup_abort(BlkTransactionState *common) +static void drive_backup_abort(BlkActionState *common) { DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); BlockDriverState *bs = state->bs; @@ -1623,72 +1918,85 @@ static void drive_backup_abort(BlkTransactionState *common) } } -static void drive_backup_clean(BlkTransactionState *common) +static void drive_backup_clean(BlkActionState *common) { DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); if (state->aio_context) { + bdrv_drained_end(state->bs); aio_context_release(state->aio_context); } } typedef struct BlockdevBackupState { - BlkTransactionState common; + BlkActionState common; BlockDriverState *bs; BlockJob *job; AioContext *aio_context; } BlockdevBackupState; -static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp) +static void do_blockdev_backup(const char *device, const char *target, + enum MirrorSyncMode sync, + bool has_speed, int64_t speed, + bool has_on_source_error, + BlockdevOnError on_source_error, + bool has_on_target_error, + BlockdevOnError on_target_error, + BlockJobTxn *txn, Error **errp); + +static void blockdev_backup_prepare(BlkActionState *common, Error **errp) { BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); BlockdevBackup *backup; - BlockDriverState *bs, *target; - BlockBackend *blk; + BlockBackend *blk, *target; Error *local_err = NULL; - assert(common->action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); - backup = common->action->blockdev_backup; + assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); + backup = common->action->u.blockdev_backup.data; blk = blk_by_name(backup->device); if (!blk) { error_setg(errp, "Device '%s' not found", backup->device); return; } - bs = blk_bs(blk); - blk = blk_by_name(backup->target); - if (!blk) { + if (!blk_is_available(blk)) { + error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, backup->device); + return; + } + + target = blk_by_name(backup->target); + if (!target) { error_setg(errp, "Device '%s' not found", backup->target); return; } - target = blk_bs(blk); /* AioContext is released in .clean() */ - state->aio_context = bdrv_get_aio_context(bs); - if (state->aio_context != bdrv_get_aio_context(target)) { + state->aio_context = blk_get_aio_context(blk); + if (state->aio_context != blk_get_aio_context(target)) { state->aio_context = NULL; error_setg(errp, "Backup between two IO threads is not implemented"); return; } aio_context_acquire(state->aio_context); - - qmp_blockdev_backup(backup->device, backup->target, - backup->sync, - backup->has_speed, backup->speed, - backup->has_on_source_error, backup->on_source_error, - backup->has_on_target_error, backup->on_target_error, - &local_err); + state->bs = blk_bs(blk); + bdrv_drained_begin(state->bs); + + do_blockdev_backup(backup->device, backup->target, + backup->sync, + backup->has_speed, backup->speed, + backup->has_on_source_error, backup->on_source_error, + backup->has_on_target_error, backup->on_target_error, + common->block_job_txn, &local_err); if (local_err) { error_propagate(errp, local_err); return; } - state->bs = bs; state->job = state->bs->job; } -static void blockdev_backup_abort(BlkTransactionState *common) +static void blockdev_backup_abort(BlkActionState *common) { BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); BlockDriverState *bs = state->bs; @@ -1699,31 +2007,148 @@ static void blockdev_backup_abort(BlkTransactionState *common) } } -static void blockdev_backup_clean(BlkTransactionState *common) +static void blockdev_backup_clean(BlkActionState *common) { BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); + if (state->aio_context) { + bdrv_drained_end(state->bs); + aio_context_release(state->aio_context); + } +} + +typedef struct BlockDirtyBitmapState { + BlkActionState common; + BdrvDirtyBitmap *bitmap; + BlockDriverState *bs; + AioContext *aio_context; + HBitmap *backup; + bool prepared; +} BlockDirtyBitmapState; + +static void block_dirty_bitmap_add_prepare(BlkActionState *common, + Error **errp) +{ + Error *local_err = NULL; + BlockDirtyBitmapAdd *action; + BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, + common, common); + + if (action_check_completion_mode(common, errp) < 0) { + return; + } + + action = common->action->u.block_dirty_bitmap_add.data; + /* AIO context taken and released within qmp_block_dirty_bitmap_add */ + qmp_block_dirty_bitmap_add(action->node, action->name, + action->has_granularity, action->granularity, + &local_err); + + if (!local_err) { + state->prepared = true; + } else { + error_propagate(errp, local_err); + } +} + +static void block_dirty_bitmap_add_abort(BlkActionState *common) +{ + BlockDirtyBitmapAdd *action; + BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, + common, common); + + action = common->action->u.block_dirty_bitmap_add.data; + /* Should not be able to fail: IF the bitmap was added via .prepare(), + * then the node reference and bitmap name must have been valid. + */ + if (state->prepared) { + qmp_block_dirty_bitmap_remove(action->node, action->name, &error_abort); + } +} + +static void block_dirty_bitmap_clear_prepare(BlkActionState *common, + Error **errp) +{ + BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, + common, common); + BlockDirtyBitmap *action; + + if (action_check_completion_mode(common, errp) < 0) { + return; + } + + action = common->action->u.block_dirty_bitmap_clear.data; + state->bitmap = block_dirty_bitmap_lookup(action->node, + action->name, + &state->bs, + &state->aio_context, + errp); + if (!state->bitmap) { + return; + } + + if (bdrv_dirty_bitmap_frozen(state->bitmap)) { + error_setg(errp, "Cannot modify a frozen bitmap"); + return; + } else if (!bdrv_dirty_bitmap_enabled(state->bitmap)) { + error_setg(errp, "Cannot clear a disabled bitmap"); + return; + } + + bdrv_clear_dirty_bitmap(state->bitmap, &state->backup); + /* AioContext is released in .clean() */ +} + +static void block_dirty_bitmap_clear_abort(BlkActionState *common) +{ + BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, + common, common); + + bdrv_undo_clear_dirty_bitmap(state->bitmap, state->backup); +} + +static void block_dirty_bitmap_clear_commit(BlkActionState *common) +{ + BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, + common, common); + + hbitmap_free(state->backup); +} + +static void block_dirty_bitmap_clear_clean(BlkActionState *common) +{ + BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, + common, common); + if (state->aio_context) { aio_context_release(state->aio_context); } } -static void abort_prepare(BlkTransactionState *common, Error **errp) +static void abort_prepare(BlkActionState *common, Error **errp) { error_setg(errp, "Transaction aborted using Abort action"); } -static void abort_commit(BlkTransactionState *common) +static void abort_commit(BlkActionState *common) { g_assert_not_reached(); /* this action never succeeds */ } -static const BdrvActionOps actions[] = { +static const BlkActionOps actions[] = { + [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT] = { + .instance_size = sizeof(ExternalSnapshotState), + .prepare = external_snapshot_prepare, + .commit = external_snapshot_commit, + .abort = external_snapshot_abort, + .clean = external_snapshot_clean, + }, [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC] = { .instance_size = sizeof(ExternalSnapshotState), .prepare = external_snapshot_prepare, .commit = external_snapshot_commit, .abort = external_snapshot_abort, + .clean = external_snapshot_clean, }, [TRANSACTION_ACTION_KIND_DRIVE_BACKUP] = { .instance_size = sizeof(DriveBackupState), @@ -1738,7 +2163,7 @@ static const BdrvActionOps actions[] = { .clean = blockdev_backup_clean, }, [TRANSACTION_ACTION_KIND_ABORT] = { - .instance_size = sizeof(BlkTransactionState), + .instance_size = sizeof(BlkActionState), .prepare = abort_prepare, .commit = abort_commit, }, @@ -1748,40 +2173,85 @@ static const BdrvActionOps actions[] = { .abort = internal_snapshot_abort, .clean = internal_snapshot_clean, }, + [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ADD] = { + .instance_size = sizeof(BlockDirtyBitmapState), + .prepare = block_dirty_bitmap_add_prepare, + .abort = block_dirty_bitmap_add_abort, + }, + [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_CLEAR] = { + .instance_size = sizeof(BlockDirtyBitmapState), + .prepare = block_dirty_bitmap_clear_prepare, + .commit = block_dirty_bitmap_clear_commit, + .abort = block_dirty_bitmap_clear_abort, + .clean = block_dirty_bitmap_clear_clean, + } }; +/** + * Allocate a TransactionProperties structure if necessary, and fill + * that structure with desired defaults if they are unset. + */ +static TransactionProperties *get_transaction_properties( + TransactionProperties *props) +{ + if (!props) { + props = g_new0(TransactionProperties, 1); + } + + if (!props->has_completion_mode) { + props->has_completion_mode = true; + props->completion_mode = ACTION_COMPLETION_MODE_INDIVIDUAL; + } + + return props; +} + /* * 'Atomic' group operations. The operations are performed as a set, and if * any fail then we roll back all operations in the group. */ -void qmp_transaction(TransactionActionList *dev_list, Error **errp) +void qmp_transaction(TransactionActionList *dev_list, + bool has_props, + struct TransactionProperties *props, + Error **errp) { TransactionActionList *dev_entry = dev_list; - BlkTransactionState *state, *next; + BlockJobTxn *block_job_txn = NULL; + BlkActionState *state, *next; Error *local_err = NULL; - QSIMPLEQ_HEAD(snap_bdrv_states, BlkTransactionState) snap_bdrv_states; + QSIMPLEQ_HEAD(snap_bdrv_states, BlkActionState) snap_bdrv_states; QSIMPLEQ_INIT(&snap_bdrv_states); + /* Does this transaction get canceled as a group on failure? + * If not, we don't really need to make a BlockJobTxn. + */ + props = get_transaction_properties(props); + if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) { + block_job_txn = block_job_txn_new(); + } + /* drain all i/o before any operations */ bdrv_drain_all(); /* We don't do anything in this loop that commits us to the operations */ while (NULL != dev_entry) { TransactionAction *dev_info = NULL; - const BdrvActionOps *ops; + const BlkActionOps *ops; dev_info = dev_entry->value; dev_entry = dev_entry->next; - assert(dev_info->kind < ARRAY_SIZE(actions)); + assert(dev_info->type < ARRAY_SIZE(actions)); - ops = &actions[dev_info->kind]; + ops = &actions[dev_info->type]; assert(ops->instance_size > 0); state = g_malloc0(ops->instance_size); state->ops = ops; state->action = dev_info; + state->block_job_txn = block_job_txn; + state->txn_props = props; QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, state, entry); state->ops->prepare(state, &local_err); @@ -1814,42 +2284,91 @@ exit: } g_free(state); } + if (!has_props) { + qapi_free_TransactionProperties(props); + } + block_job_txn_unref(block_job_txn); } +void qmp_eject(const char *device, bool has_force, bool force, Error **errp) +{ + Error *local_err = NULL; + + qmp_blockdev_open_tray(device, has_force, force, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + qmp_x_blockdev_remove_medium(device, errp); +} -static void eject_device(BlockBackend *blk, int force, Error **errp) +void qmp_block_passwd(bool has_device, const char *device, + bool has_node_name, const char *node_name, + const char *password, Error **errp) { - BlockDriverState *bs = blk_bs(blk); + Error *local_err = NULL; + BlockDriverState *bs; AioContext *aio_context; + bs = bdrv_lookup_bs(has_device ? device : NULL, + has_node_name ? node_name : NULL, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) { - goto out; + bdrv_add_key(bs, password, errp); + + aio_context_release(aio_context); +} + +void qmp_blockdev_open_tray(const char *device, bool has_force, bool force, + Error **errp) +{ + BlockBackend *blk; + bool locked; + + if (!has_force) { + force = false; } + + blk = blk_by_name(device); + if (!blk) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", device); + return; + } + if (!blk_dev_has_removable_media(blk)) { - error_setg(errp, "Device '%s' is not removable", - bdrv_get_device_name(bs)); - goto out; + error_setg(errp, "Device '%s' is not removable", device); + return; } - if (blk_dev_is_medium_locked(blk) && !blk_dev_is_tray_open(blk)) { - blk_dev_eject_request(blk, force); - if (!force) { - error_setg(errp, "Device '%s' is locked", - bdrv_get_device_name(bs)); - goto out; - } + if (!blk_dev_has_tray(blk)) { + /* Ignore this command on tray-less devices */ + return; } - bdrv_close(bs); + if (blk_dev_is_tray_open(blk)) { + return; + } -out: - aio_context_release(aio_context); + locked = blk_dev_is_medium_locked(blk); + if (locked) { + blk_dev_eject_request(blk, force); + } + + if (!locked || force) { + blk_dev_change_media_cb(blk, false); + } } -void qmp_eject(const char *device, bool has_force, bool force, Error **errp) +void qmp_blockdev_close_tray(const char *device, Error **errp) { BlockBackend *blk; @@ -1860,92 +2379,230 @@ void qmp_eject(const char *device, bool has_force, bool force, Error **errp) return; } - eject_device(blk, force, errp); + if (!blk_dev_has_removable_media(blk)) { + error_setg(errp, "Device '%s' is not removable", device); + return; + } + + if (!blk_dev_has_tray(blk)) { + /* Ignore this command on tray-less devices */ + return; + } + + if (!blk_dev_is_tray_open(blk)) { + return; + } + + blk_dev_change_media_cb(blk, true); } -void qmp_block_passwd(bool has_device, const char *device, - bool has_node_name, const char *node_name, - const char *password, Error **errp) +void qmp_x_blockdev_remove_medium(const char *device, Error **errp) { - Error *local_err = NULL; + BlockBackend *blk; BlockDriverState *bs; AioContext *aio_context; + bool has_device; - bs = bdrv_lookup_bs(has_device ? device : NULL, - has_node_name ? node_name : NULL, - &local_err); - if (local_err) { - error_propagate(errp, local_err); + blk = blk_by_name(device); + if (!blk) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", device); + return; + } + + /* For BBs without a device, we can exchange the BDS tree at will */ + has_device = blk_get_attached_dev(blk); + + if (has_device && !blk_dev_has_removable_media(blk)) { + error_setg(errp, "Device '%s' is not removable", device); + return; + } + + if (has_device && blk_dev_has_tray(blk) && !blk_dev_is_tray_open(blk)) { + error_setg(errp, "Tray of device '%s' is not open", device); + return; + } + + bs = blk_bs(blk); + if (!bs) { return; } aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - bdrv_add_key(bs, password, errp); + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) { + goto out; + } + blk_remove_bs(blk); + + if (!blk_dev_has_tray(blk)) { + /* For tray-less devices, blockdev-open-tray is a no-op (or may not be + * called at all); therefore, the medium needs to be ejected here. + * Do it after blk_remove_bs() so blk_is_inserted(blk) returns the @load + * value passed here (i.e. false). */ + blk_dev_change_media_cb(blk, false); + } + +out: aio_context_release(aio_context); } -/* Assumes AioContext is held */ -static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename, - int bdrv_flags, BlockDriver *drv, - const char *password, Error **errp) +static void qmp_blockdev_insert_anon_medium(const char *device, + BlockDriverState *bs, Error **errp) { - Error *local_err = NULL; - int ret; + BlockBackend *blk; + bool has_device; - ret = bdrv_open(&bs, filename, NULL, NULL, bdrv_flags, drv, &local_err); - if (ret < 0) { - error_propagate(errp, local_err); + blk = blk_by_name(device); + if (!blk) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", device); return; } - bdrv_add_key(bs, password, errp); + /* For BBs without a device, we can exchange the BDS tree at will */ + has_device = blk_get_attached_dev(blk); + + if (has_device && !blk_dev_has_removable_media(blk)) { + error_setg(errp, "Device '%s' is not removable", device); + return; + } + + if (has_device && blk_dev_has_tray(blk) && !blk_dev_is_tray_open(blk)) { + error_setg(errp, "Tray of device '%s' is not open", device); + return; + } + + if (blk_bs(blk)) { + error_setg(errp, "There already is a medium in device '%s'", device); + return; + } + + blk_insert_bs(blk, bs); + + if (!blk_dev_has_tray(blk)) { + /* For tray-less devices, blockdev-close-tray is a no-op (or may not be + * called at all); therefore, the medium needs to be pushed into the + * slot here. + * Do it after blk_insert_bs() so blk_is_inserted(blk) returns the @load + * value passed here (i.e. true). */ + blk_dev_change_media_cb(blk, true); + } } -void qmp_change_blockdev(const char *device, const char *filename, - const char *format, Error **errp) +void qmp_x_blockdev_insert_medium(const char *device, const char *node_name, + Error **errp) { - BlockBackend *blk; BlockDriverState *bs; - AioContext *aio_context; - BlockDriver *drv = NULL; - int bdrv_flags; + + bs = bdrv_find_node(node_name); + if (!bs) { + error_setg(errp, "Node '%s' not found", node_name); + return; + } + + if (bs->blk) { + error_setg(errp, "Node '%s' is already in use by '%s'", node_name, + blk_name(bs->blk)); + return; + } + + qmp_blockdev_insert_anon_medium(device, bs, errp); +} + +void qmp_blockdev_change_medium(const char *device, const char *filename, + bool has_format, const char *format, + bool has_read_only, + BlockdevChangeReadOnlyMode read_only, + Error **errp) +{ + BlockBackend *blk; + BlockDriverState *medium_bs = NULL; + int bdrv_flags, ret; + QDict *options = NULL; Error *err = NULL; blk = blk_by_name(device); if (!blk) { error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "Device '%s' not found", device); - return; + goto fail; } - bs = blk_bs(blk); - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); + if (blk_bs(blk)) { + blk_update_root_state(blk); + } - if (format) { - drv = bdrv_find_whitelisted_format(format, bs->read_only); - if (!drv) { - error_setg(errp, QERR_INVALID_BLOCK_FORMAT, format); - goto out; - } + bdrv_flags = blk_get_open_flags_from_root_state(blk); + bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | + BDRV_O_PROTOCOL); + + if (!has_read_only) { + read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN; + } + + switch (read_only) { + case BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN: + break; + + case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_ONLY: + bdrv_flags &= ~BDRV_O_RDWR; + break; + + case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_WRITE: + bdrv_flags |= BDRV_O_RDWR; + break; + + default: + abort(); + } + + if (has_format) { + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str(format)); } - eject_device(blk, 0, &err); + assert(!medium_bs); + ret = bdrv_open(&medium_bs, filename, NULL, options, bdrv_flags, errp); + if (ret < 0) { + goto fail; + } + + blk_apply_root_state(blk, medium_bs); + + bdrv_add_key(medium_bs, NULL, &err); if (err) { error_propagate(errp, err); - goto out; + goto fail; + } + + qmp_blockdev_open_tray(device, false, false, &err); + if (err) { + error_propagate(errp, err); + goto fail; } - bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR; - bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0; + qmp_x_blockdev_remove_medium(device, &err); + if (err) { + error_propagate(errp, err); + goto fail; + } + + qmp_blockdev_insert_anon_medium(device, medium_bs, &err); + if (err) { + error_propagate(errp, err); + goto fail; + } - qmp_bdrv_open_encrypted(bs, filename, bdrv_flags, drv, NULL, errp); + qmp_blockdev_close_tray(device, errp); -out: - aio_context_release(aio_context); +fail: + /* If the medium has been inserted, the device has its own reference, so + * ours must be relinquished; and if it has not been inserted successfully, + * the reference must be relinquished anyway */ + bdrv_unref(medium_bs); } /* throttling disk I/O limits */ @@ -1966,6 +2623,18 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, int64_t iops_rd_max, bool has_iops_wr_max, int64_t iops_wr_max, + bool has_bps_max_length, + int64_t bps_max_length, + bool has_bps_rd_max_length, + int64_t bps_rd_max_length, + bool has_bps_wr_max_length, + int64_t bps_wr_max_length, + bool has_iops_max_length, + int64_t iops_max_length, + bool has_iops_rd_max_length, + int64_t iops_rd_max_length, + bool has_iops_wr_max_length, + int64_t iops_wr_max_length, bool has_iops_size, int64_t iops_size, bool has_group, @@ -1982,9 +2651,24 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, "Device '%s' not found", device); return; } + + aio_context = blk_get_aio_context(blk); + aio_context_acquire(aio_context); + bs = blk_bs(blk); + if (!bs) { + error_setg(errp, "Device '%s' has no medium", device); + goto out; + } - memset(&cfg, 0, sizeof(cfg)); + /* The BlockBackend must be the only parent */ + assert(QLIST_FIRST(&bs->parents)); + if (QLIST_NEXT(QLIST_FIRST(&bs->parents), next_parent)) { + error_setg(errp, "Cannot throttle device with multiple parents"); + goto out; + } + + throttle_config_init(&cfg); cfg.buckets[THROTTLE_BPS_TOTAL].avg = bps; cfg.buckets[THROTTLE_BPS_READ].avg = bps_rd; cfg.buckets[THROTTLE_BPS_WRITE].avg = bps_wr; @@ -2012,32 +2696,49 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, cfg.buckets[THROTTLE_OPS_WRITE].max = iops_wr_max; } + if (has_bps_max_length) { + cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = bps_max_length; + } + if (has_bps_rd_max_length) { + cfg.buckets[THROTTLE_BPS_READ].burst_length = bps_rd_max_length; + } + if (has_bps_wr_max_length) { + cfg.buckets[THROTTLE_BPS_WRITE].burst_length = bps_wr_max_length; + } + if (has_iops_max_length) { + cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = iops_max_length; + } + if (has_iops_rd_max_length) { + cfg.buckets[THROTTLE_OPS_READ].burst_length = iops_rd_max_length; + } + if (has_iops_wr_max_length) { + cfg.buckets[THROTTLE_OPS_WRITE].burst_length = iops_wr_max_length; + } + if (has_iops_size) { cfg.op_size = iops_size; } - if (!check_throttle_config(&cfg, errp)) { - return; + if (!throttle_is_valid(&cfg, errp)) { + goto out; } - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - if (throttle_enabled(&cfg)) { /* Enable I/O limits if they're not enabled yet, otherwise * just update the throttling group. */ - if (!bs->io_limits_enabled) { + if (!bs->throttle_state) { bdrv_io_limits_enable(bs, has_group ? group : device); } else if (has_group) { bdrv_io_limits_update_group(bs, group); } /* Set the new throttling configuration */ bdrv_set_io_limits(bs, &cfg); - } else if (bs->io_limits_enabled) { + } else if (bs->throttle_state) { /* If all throttling settings are set to 0, disable I/O limits */ bdrv_io_limits_disable(bs); } +out: aio_context_release(aio_context); } @@ -2131,7 +2832,7 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name, goto out; } - bdrv_clear_dirty_bitmap(bitmap); + bdrv_clear_dirty_bitmap(bitmap, NULL); out: aio_context_release(aio_context); @@ -2145,12 +2846,20 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) AioContext *aio_context; Error *local_err = NULL; + bs = bdrv_find_node(id); + if (bs) { + qmp_x_blockdev_del(false, NULL, true, id, &local_err); + if (local_err) { + error_report_err(local_err); + } + return; + } + blk = blk_by_name(id); if (!blk) { error_report("Device '%s' not found", id); return; } - bs = blk_bs(blk); if (!blk_legacy_dinfo(blk)) { error_report("Deleting device added with blockdev-add" @@ -2158,27 +2867,30 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) return; } - aio_context = bdrv_get_aio_context(bs); + aio_context = blk_get_aio_context(blk); aio_context_acquire(aio_context); - if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) { - error_report_err(local_err); - aio_context_release(aio_context); - return; + bs = blk_bs(blk); + if (bs) { + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) { + error_report_err(local_err); + aio_context_release(aio_context); + return; + } + + blk_remove_bs(blk); } - bdrv_close(bs); + /* Make the BlockBackend and the attached BlockDriverState anonymous */ + monitor_remove_blk(blk); - /* if we have a device attached to this BlockDriverState - * then we need to make the drive anonymous until the device - * can be removed. If this is a drive with no device backing - * then we can just get rid of the block driver state right here. + /* If this BlockBackend has a device attached to it, its refcount will be + * decremented when the device is removed; otherwise we have to do so here. */ if (blk_get_attached_dev(blk)) { - blk_hide_on_behalf_of_hmp_drive_del(blk); /* Further I/O must not pause the guest */ - bdrv_set_on_error(bs, BLOCKDEV_ON_ERROR_REPORT, - BLOCKDEV_ON_ERROR_REPORT); + blk_set_on_error(blk, BLOCKDEV_ON_ERROR_REPORT, + BLOCKDEV_ON_ERROR_REPORT); } else { blk_unref(blk); } @@ -2272,8 +2984,6 @@ static void block_job_cb(void *opaque, int ret) } else { block_job_event_completed(bs->job, msg); } - - bdrv_put_ref_bh_schedule(bs); } void qmp_block_stream(const char *device, @@ -2300,11 +3010,16 @@ void qmp_block_stream(const char *device, "Device '%s' not found", device); return; } - bs = blk_bs(blk); - aio_context = bdrv_get_aio_context(bs); + aio_context = blk_get_aio_context(blk); aio_context_acquire(aio_context); + if (!blk_is_available(blk)) { + error_setg(errp, "Device '%s' has no medium", device); + goto out; + } + bs = blk_bs(blk); + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_STREAM, errp)) { goto out; } @@ -2375,11 +3090,16 @@ void qmp_block_commit(const char *device, "Device '%s' not found", device); return; } - bs = blk_bs(blk); - aio_context = bdrv_get_aio_context(bs); + aio_context = blk_get_aio_context(blk); aio_context_acquire(aio_context); + if (!blk_is_available(blk)) { + error_setg(errp, "Device '%s' has no medium", device); + goto out; + } + bs = blk_bs(blk); + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, errp)) { goto out; } @@ -2444,15 +3164,17 @@ out: aio_context_release(aio_context); } -void qmp_drive_backup(const char *device, const char *target, - bool has_format, const char *format, - enum MirrorSyncMode sync, - bool has_mode, enum NewImageMode mode, - bool has_speed, int64_t speed, - bool has_bitmap, const char *bitmap, - bool has_on_source_error, BlockdevOnError on_source_error, - bool has_on_target_error, BlockdevOnError on_target_error, - Error **errp) +static void do_drive_backup(const char *device, const char *target, + bool has_format, const char *format, + enum MirrorSyncMode sync, + bool has_mode, enum NewImageMode mode, + bool has_speed, int64_t speed, + bool has_bitmap, const char *bitmap, + bool has_on_source_error, + BlockdevOnError on_source_error, + bool has_on_target_error, + BlockdevOnError on_target_error, + BlockJobTxn *txn, Error **errp) { BlockBackend *blk; BlockDriverState *bs; @@ -2460,7 +3182,7 @@ void qmp_drive_backup(const char *device, const char *target, BlockDriverState *source = NULL; BdrvDirtyBitmap *bmap = NULL; AioContext *aio_context; - BlockDriver *drv = NULL; + QDict *options = NULL; Error *local_err = NULL; int flags; int64_t size; @@ -2485,28 +3207,21 @@ void qmp_drive_backup(const char *device, const char *target, "Device '%s' not found", device); return; } - bs = blk_bs(blk); - aio_context = bdrv_get_aio_context(bs); + aio_context = blk_get_aio_context(blk); aio_context_acquire(aio_context); /* Although backup_run has this check too, we need to use bs->drv below, so * do an early check redundantly. */ - if (!bdrv_is_inserted(bs)) { + if (!blk_is_available(blk)) { error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); goto out; } + bs = blk_bs(blk); if (!has_format) { format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name; } - if (format) { - drv = bdrv_find_format(format); - if (!drv) { - error_setg(errp, QERR_INVALID_BLOCK_FORMAT, format); - goto out; - } - } /* Early check to avoid creating target */ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { @@ -2518,7 +3233,7 @@ void qmp_drive_backup(const char *device, const char *target, /* See if we have a backing HD we can use to create our new image * on top of. */ if (sync == MIRROR_SYNC_MODE_TOP) { - source = bs->backing_hd; + source = backing_bs(bs); if (!source) { sync = MIRROR_SYNC_MODE_FULL; } @@ -2534,7 +3249,7 @@ void qmp_drive_backup(const char *device, const char *target, } if (mode != NEW_IMAGE_MODE_EXISTING) { - assert(format && drv); + assert(format); if (source) { bdrv_img_create(target, format, source->filename, source->drv->format_name, NULL, @@ -2550,8 +3265,13 @@ void qmp_drive_backup(const char *device, const char *target, goto out; } + if (format) { + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str(format)); + } + target_bs = NULL; - ret = bdrv_open(&target_bs, target, NULL, NULL, flags, drv, &local_err); + ret = bdrv_open(&target_bs, target, NULL, options, flags, &local_err); if (ret < 0) { error_propagate(errp, local_err); goto out; @@ -2563,13 +3283,14 @@ void qmp_drive_backup(const char *device, const char *target, bmap = bdrv_find_dirty_bitmap(bs, bitmap); if (!bmap) { error_setg(errp, "Bitmap '%s' could not be found", bitmap); + bdrv_unref(target_bs); goto out; } } backup_start(bs, target_bs, speed, sync, bmap, on_source_error, on_target_error, - block_job_cb, bs, &local_err); + block_job_cb, bs, txn, &local_err); if (local_err != NULL) { bdrv_unref(target_bs); error_propagate(errp, local_err); @@ -2580,21 +3301,39 @@ out: aio_context_release(aio_context); } +void qmp_drive_backup(const char *device, const char *target, + bool has_format, const char *format, + enum MirrorSyncMode sync, + bool has_mode, enum NewImageMode mode, + bool has_speed, int64_t speed, + bool has_bitmap, const char *bitmap, + bool has_on_source_error, BlockdevOnError on_source_error, + bool has_on_target_error, BlockdevOnError on_target_error, + Error **errp) +{ + return do_drive_backup(device, target, has_format, format, sync, + has_mode, mode, has_speed, speed, + has_bitmap, bitmap, + has_on_source_error, on_source_error, + has_on_target_error, on_target_error, + NULL, errp); +} + BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) { return bdrv_named_nodes_list(errp); } -void qmp_blockdev_backup(const char *device, const char *target, +void do_blockdev_backup(const char *device, const char *target, enum MirrorSyncMode sync, bool has_speed, int64_t speed, bool has_on_source_error, BlockdevOnError on_source_error, bool has_on_target_error, BlockdevOnError on_target_error, - Error **errp) + BlockJobTxn *txn, Error **errp) { - BlockBackend *blk; + BlockBackend *blk, *target_blk; BlockDriverState *bs; BlockDriverState *target_bs; Error *local_err = NULL; @@ -2615,22 +3354,32 @@ void qmp_blockdev_backup(const char *device, const char *target, error_setg(errp, "Device '%s' not found", device); return; } - bs = blk_bs(blk); - aio_context = bdrv_get_aio_context(bs); + aio_context = blk_get_aio_context(blk); aio_context_acquire(aio_context); - blk = blk_by_name(target); - if (!blk) { + if (!blk_is_available(blk)) { + error_setg(errp, "Device '%s' has no medium", device); + goto out; + } + bs = blk_bs(blk); + + target_blk = blk_by_name(target); + if (!target_blk) { error_setg(errp, "Device '%s' not found", target); goto out; } - target_bs = blk_bs(blk); + + if (!blk_is_available(target_blk)) { + error_setg(errp, "Device '%s' has no medium", target); + goto out; + } + target_bs = blk_bs(target_blk); bdrv_ref(target_bs); bdrv_set_aio_context(target_bs, aio_context); backup_start(bs, target_bs, speed, sync, NULL, on_source_error, - on_target_error, block_job_cb, bs, &local_err); + on_target_error, block_job_cb, bs, txn, &local_err); if (local_err != NULL) { bdrv_unref(target_bs); error_propagate(errp, local_err); @@ -2639,30 +3388,38 @@ out: aio_context_release(aio_context); } -void qmp_drive_mirror(const char *device, const char *target, - bool has_format, const char *format, - bool has_node_name, const char *node_name, - bool has_replaces, const char *replaces, - enum MirrorSyncMode sync, - bool has_mode, enum NewImageMode mode, - bool has_speed, int64_t speed, - bool has_granularity, uint32_t granularity, - bool has_buf_size, int64_t buf_size, - bool has_on_source_error, BlockdevOnError on_source_error, - bool has_on_target_error, BlockdevOnError on_target_error, - bool has_unmap, bool unmap, - Error **errp) +void qmp_blockdev_backup(const char *device, const char *target, + enum MirrorSyncMode sync, + bool has_speed, int64_t speed, + bool has_on_source_error, + BlockdevOnError on_source_error, + bool has_on_target_error, + BlockdevOnError on_target_error, + Error **errp) +{ + do_blockdev_backup(device, target, sync, has_speed, speed, + has_on_source_error, on_source_error, + has_on_target_error, on_target_error, + NULL, errp); +} + +/* Parameter check and block job starting for drive mirroring. + * Caller should hold @device and @target's aio context (must be the same). + **/ +static void blockdev_mirror_common(BlockDriverState *bs, + BlockDriverState *target, + bool has_replaces, const char *replaces, + enum MirrorSyncMode sync, + bool has_speed, int64_t speed, + bool has_granularity, uint32_t granularity, + bool has_buf_size, int64_t buf_size, + bool has_on_source_error, + BlockdevOnError on_source_error, + bool has_on_target_error, + BlockdevOnError on_target_error, + bool has_unmap, bool unmap, + Error **errp) { - BlockBackend *blk; - BlockDriverState *bs; - BlockDriverState *source, *target_bs; - AioContext *aio_context; - BlockDriver *drv = NULL; - Error *local_err = NULL; - QDict *options = NULL; - int flags; - int64_t size; - int ret; if (!has_speed) { speed = 0; @@ -2673,9 +3430,6 @@ void qmp_drive_mirror(const char *device, const char *target, if (!has_on_target_error) { on_target_error = BLOCKDEV_ON_ERROR_REPORT; } - if (!has_mode) { - mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; - } if (!has_granularity) { granularity = 0; } @@ -2697,39 +3451,80 @@ void qmp_drive_mirror(const char *device, const char *target, return; } + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) { + return; + } + if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_MIRROR_TARGET, errp)) { + return; + } + if (target->blk) { + error_setg(errp, "Cannot mirror to an attached block device"); + return; + } + + if (!bs->backing && sync == MIRROR_SYNC_MODE_TOP) { + sync = MIRROR_SYNC_MODE_FULL; + } + + /* pass the node name to replace to mirror start since it's loose coupling + * and will allow to check whether the node still exist at mirror completion + */ + mirror_start(bs, target, + has_replaces ? replaces : NULL, + speed, granularity, buf_size, sync, + on_source_error, on_target_error, unmap, + block_job_cb, bs, errp); +} + +void qmp_drive_mirror(const char *device, const char *target, + bool has_format, const char *format, + bool has_node_name, const char *node_name, + bool has_replaces, const char *replaces, + enum MirrorSyncMode sync, + bool has_mode, enum NewImageMode mode, + bool has_speed, int64_t speed, + bool has_granularity, uint32_t granularity, + bool has_buf_size, int64_t buf_size, + bool has_on_source_error, BlockdevOnError on_source_error, + bool has_on_target_error, BlockdevOnError on_target_error, + bool has_unmap, bool unmap, + Error **errp) +{ + BlockDriverState *bs; + BlockBackend *blk; + BlockDriverState *source, *target_bs; + AioContext *aio_context; + Error *local_err = NULL; + QDict *options = NULL; + int flags; + int64_t size; + int ret; + blk = blk_by_name(device); if (!blk) { error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "Device '%s' not found", device); return; } - bs = blk_bs(blk); - aio_context = bdrv_get_aio_context(bs); + aio_context = blk_get_aio_context(blk); aio_context_acquire(aio_context); - if (!bdrv_is_inserted(bs)) { + if (!blk_is_available(blk)) { error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); goto out; } + bs = blk_bs(blk); + if (!has_mode) { + mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; + } if (!has_format) { format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name; } - if (format) { - drv = bdrv_find_format(format); - if (!drv) { - error_setg(errp, QERR_INVALID_BLOCK_FORMAT, format); - goto out; - } - } - - if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR, errp)) { - goto out; - } flags = bs->open_flags | BDRV_O_RDWR; - source = bs->backing_hd; + source = backing_bs(bs); if (!source && sync == MIRROR_SYNC_MODE_TOP) { sync = MIRROR_SYNC_MODE_FULL; } @@ -2754,7 +3549,7 @@ void qmp_drive_mirror(const char *device, const char *target, goto out; } - to_replace_bs = check_to_replace_node(replaces, &local_err); + to_replace_bs = check_to_replace_node(bs, replaces, &local_err); if (!to_replace_bs) { error_propagate(errp, local_err); @@ -2777,7 +3572,7 @@ void qmp_drive_mirror(const char *device, const char *target, && mode != NEW_IMAGE_MODE_EXISTING) { /* create new image w/o backing file */ - assert(format && drv); + assert(format); bdrv_img_create(target, format, NULL, NULL, NULL, size, flags, &local_err, false); } else { @@ -2801,17 +3596,20 @@ void qmp_drive_mirror(const char *device, const char *target, goto out; } + options = qdict_new(); if (has_node_name) { - options = qdict_new(); qdict_put(options, "node-name", qstring_from_str(node_name)); } + if (format) { + qdict_put(options, "driver", qstring_from_str(format)); + } /* Mirroring takes care of copy-on-write using the source's backing * file. */ target_bs = NULL; ret = bdrv_open(&target_bs, target, NULL, options, - flags | BDRV_O_NO_BACKING, drv, &local_err); + flags | BDRV_O_NO_BACKING, &local_err); if (ret < 0) { error_propagate(errp, local_err); goto out; @@ -2819,22 +3617,78 @@ void qmp_drive_mirror(const char *device, const char *target, bdrv_set_aio_context(target_bs, aio_context); - /* pass the node name to replace to mirror start since it's loose coupling - * and will allow to check whether the node still exist at mirror completion - */ - mirror_start(bs, target_bs, - has_replaces ? replaces : NULL, - speed, granularity, buf_size, sync, - on_source_error, on_target_error, - unmap, - block_job_cb, bs, &local_err); - if (local_err != NULL) { + blockdev_mirror_common(bs, target_bs, + has_replaces, replaces, sync, + has_speed, speed, + has_granularity, granularity, + has_buf_size, buf_size, + has_on_source_error, on_source_error, + has_on_target_error, on_target_error, + has_unmap, unmap, + &local_err); + if (local_err) { + error_propagate(errp, local_err); bdrv_unref(target_bs); + } +out: + aio_context_release(aio_context); +} + +void qmp_blockdev_mirror(const char *device, const char *target, + bool has_replaces, const char *replaces, + MirrorSyncMode sync, + bool has_speed, int64_t speed, + bool has_granularity, uint32_t granularity, + bool has_buf_size, int64_t buf_size, + bool has_on_source_error, + BlockdevOnError on_source_error, + bool has_on_target_error, + BlockdevOnError on_target_error, + Error **errp) +{ + BlockDriverState *bs; + BlockBackend *blk; + BlockDriverState *target_bs; + AioContext *aio_context; + Error *local_err = NULL; + + blk = blk_by_name(device); + if (!blk) { + error_setg(errp, "Device '%s' not found", device); + return; + } + bs = blk_bs(blk); + + if (!bs) { + error_setg(errp, "Device '%s' has no media", device); + return; + } + + target_bs = bdrv_lookup_bs(target, target, errp); + if (!target_bs) { + return; + } + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + bdrv_ref(target_bs); + bdrv_set_aio_context(target_bs, aio_context); + + blockdev_mirror_common(bs, target_bs, + has_replaces, replaces, sync, + has_speed, speed, + has_granularity, granularity, + has_buf_size, buf_size, + has_on_source_error, on_source_error, + has_on_target_error, on_target_error, + true, true, + &local_err); + if (local_err) { error_propagate(errp, local_err); - goto out; + bdrv_unref(target_bs); } -out: aio_context_release(aio_context); } @@ -2845,17 +3699,22 @@ static BlockJob *find_block_job(const char *device, AioContext **aio_context, BlockBackend *blk; BlockDriverState *bs; + *aio_context = NULL; + blk = blk_by_name(device); if (!blk) { goto notfound; } - bs = blk_bs(blk); - *aio_context = bdrv_get_aio_context(bs); + *aio_context = blk_get_aio_context(blk); aio_context_acquire(*aio_context); + if (!blk_is_available(blk)) { + goto notfound; + } + bs = blk_bs(blk); + if (!bs->job) { - aio_context_release(*aio_context); goto notfound; } @@ -2864,7 +3723,10 @@ static BlockJob *find_block_job(const char *device, AioContext **aio_context, notfound: error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE, "No active block job on device '%s'", device); - *aio_context = NULL; + if (*aio_context) { + aio_context_release(*aio_context); + *aio_context = NULL; + } return NULL; } @@ -2971,11 +3833,16 @@ void qmp_change_backing_file(const char *device, "Device '%s' not found", device); return; } - bs = blk_bs(blk); - aio_context = bdrv_get_aio_context(bs); + aio_context = blk_get_aio_context(blk); aio_context_acquire(aio_context); + if (!blk_is_available(blk)) { + error_setg(errp, "Device '%s' has no medium", device); + goto out; + } + bs = blk_bs(blk); + image_bs = bdrv_lookup_bs(NULL, image_node_name, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -3039,20 +3906,46 @@ out: aio_context_release(aio_context); } +void hmp_drive_add_node(Monitor *mon, const char *optstr) +{ + QemuOpts *opts; + QDict *qdict; + Error *local_err = NULL; + + opts = qemu_opts_parse_noisily(&qemu_drive_opts, optstr, false); + if (!opts) { + return; + } + + qdict = qemu_opts_to_qdict(opts, NULL); + + if (!qdict_get_try_str(qdict, "node-name")) { + QDECREF(qdict); + error_report("'node-name' needs to be specified"); + goto out; + } + + BlockDriverState *bs = bds_tree_init(qdict, &local_err); + if (!bs) { + error_report_err(local_err); + goto out; + } + + QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list); + +out: + qemu_opts_del(opts); +} + void qmp_blockdev_add(BlockdevOptions *options, Error **errp) { QmpOutputVisitor *ov = qmp_output_visitor_new(); - BlockBackend *blk; + BlockDriverState *bs; + BlockBackend *blk = NULL; QObject *obj; QDict *qdict; Error *local_err = NULL; - /* Require an ID in the top level */ - if (!options->has_id) { - error_setg(errp, "Block device needs an ID"); - goto fail; - } - /* TODO Sort it out in raw-posix and drive_new(): Reject aio=native with * cache.direct=false instead of silently switching to aio=threads, except * when called from drive_new(). @@ -3068,8 +3961,8 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp) } } - visit_type_BlockdevOptions(qmp_output_get_visitor(ov), - &options, NULL, &local_err); + visit_type_BlockdevOptions(qmp_output_get_visitor(ov), NULL, &options, + &local_err); if (local_err) { error_propagate(errp, local_err); goto fail; @@ -3080,14 +3973,37 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp) qdict_flatten(qdict); - blk = blockdev_init(NULL, qdict, &local_err); - if (local_err) { - error_propagate(errp, local_err); - goto fail; + if (options->has_id) { + blk = blockdev_init(NULL, qdict, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto fail; + } + + bs = blk_bs(blk); + } else { + if (!qdict_get_try_str(qdict, "node-name")) { + error_setg(errp, "'id' and/or 'node-name' need to be specified for " + "the root node"); + goto fail; + } + + bs = bds_tree_init(qdict, errp); + if (!bs) { + goto fail; + } + + QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list); } - if (bdrv_key_required(blk_bs(blk))) { - blk_unref(blk); + if (bs && bdrv_key_required(bs)) { + if (blk) { + monitor_remove_blk(blk); + blk_unref(blk); + } else { + QTAILQ_REMOVE(&monitor_bdrv_states, bs, monitor_list); + bdrv_unref(bs); + } error_setg(errp, "blockdev-add doesn't support encrypted devices"); goto fail; } @@ -3096,6 +4012,86 @@ fail: qmp_output_visitor_cleanup(ov); } +void qmp_x_blockdev_del(bool has_id, const char *id, + bool has_node_name, const char *node_name, Error **errp) +{ + AioContext *aio_context; + BlockBackend *blk; + BlockDriverState *bs; + + if (has_id && has_node_name) { + error_setg(errp, "Only one of id and node-name must be specified"); + return; + } else if (!has_id && !has_node_name) { + error_setg(errp, "No block device specified"); + return; + } + + if (has_id) { + /* blk_by_name() never returns a BB that is not owned by the monitor */ + blk = blk_by_name(id); + if (!blk) { + error_setg(errp, "Cannot find block backend %s", id); + return; + } + if (blk_legacy_dinfo(blk)) { + error_setg(errp, "Deleting block backend added with drive-add" + " is not supported"); + return; + } + if (blk_get_refcnt(blk) > 1) { + error_setg(errp, "Block backend %s is in use", id); + return; + } + bs = blk_bs(blk); + aio_context = blk_get_aio_context(blk); + } else { + bs = bdrv_find_node(node_name); + if (!bs) { + error_setg(errp, "Cannot find node %s", node_name); + return; + } + blk = bs->blk; + if (blk) { + error_setg(errp, "Node %s is in use by %s", + node_name, blk_name(blk)); + return; + } + aio_context = bdrv_get_aio_context(bs); + } + + aio_context_acquire(aio_context); + + if (bs) { + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, errp)) { + goto out; + } + + if (!blk && !bs->monitor_list.tqe_prev) { + error_setg(errp, "Node %s is not owned by the monitor", + bs->node_name); + goto out; + } + + if (bs->refcnt > 1) { + error_setg(errp, "Block device %s is in use", + bdrv_get_device_or_node_name(bs)); + goto out; + } + } + + if (blk) { + monitor_remove_blk(blk); + blk_unref(blk); + } else { + QTAILQ_REMOVE(&monitor_bdrv_states, bs, monitor_list); + bdrv_unref(bs); + } + +out: + aio_context_release(aio_context); +} + BlockJobInfoList *qmp_query_block_jobs(Error **errp) { BlockJobInfoList *head = NULL, **p_next = &head; @@ -3131,22 +4127,14 @@ QemuOptsList qemu_common_drive_opts = { .name = "discard", .type = QEMU_OPT_STRING, .help = "discard operation (ignore/off, unmap/on)", - },{ - .name = BDRV_OPT_CACHE_WB, - .type = QEMU_OPT_BOOL, - .help = "enables writeback mode for any caches", - },{ - .name = BDRV_OPT_CACHE_DIRECT, - .type = QEMU_OPT_BOOL, - .help = "enables use of O_DIRECT (bypass the host page cache)", - },{ - .name = BDRV_OPT_CACHE_NO_FLUSH, - .type = QEMU_OPT_BOOL, - .help = "ignore any flush requests for the device", },{ .name = "aio", .type = QEMU_OPT_STRING, .help = "host AIO implementation (threads, native)", + },{ + .name = BDRV_OPT_CACHE_WB, + .type = QEMU_OPT_BOOL, + .help = "Enable writeback mode", },{ .name = "format", .type = QEMU_OPT_STRING, @@ -3211,6 +4199,30 @@ QemuOptsList qemu_common_drive_opts = { .name = "throttling.bps-write-max", .type = QEMU_OPT_NUMBER, .help = "total bytes write burst", + },{ + .name = "throttling.iops-total-max-length", + .type = QEMU_OPT_NUMBER, + .help = "length of the iops-total-max burst period, in seconds", + },{ + .name = "throttling.iops-read-max-length", + .type = QEMU_OPT_NUMBER, + .help = "length of the iops-read-max burst period, in seconds", + },{ + .name = "throttling.iops-write-max-length", + .type = QEMU_OPT_NUMBER, + .help = "length of the iops-write-max burst period, in seconds", + },{ + .name = "throttling.bps-total-max-length", + .type = QEMU_OPT_NUMBER, + .help = "length of the bps-total-max burst period, in seconds", + },{ + .name = "throttling.bps-read-max-length", + .type = QEMU_OPT_NUMBER, + .help = "length of the bps-read-max burst period, in seconds", + },{ + .name = "throttling.bps-write-max-length", + .type = QEMU_OPT_NUMBER, + .help = "length of the bps-write-max burst period, in seconds", },{ .name = "throttling.iops-size", .type = QEMU_OPT_NUMBER, @@ -3227,6 +4239,45 @@ QemuOptsList qemu_common_drive_opts = { .name = "detect-zeroes", .type = QEMU_OPT_STRING, .help = "try to optimize zero writes (off, on, unmap)", + },{ + .name = "stats-account-invalid", + .type = QEMU_OPT_BOOL, + .help = "whether to account for invalid I/O operations " + "in the statistics", + },{ + .name = "stats-account-failed", + .type = QEMU_OPT_BOOL, + .help = "whether to account for failed I/O operations " + "in the statistics", + }, + { /* end of list */ } + }, +}; + +static QemuOptsList qemu_root_bds_opts = { + .name = "root-bds", + .head = QTAILQ_HEAD_INITIALIZER(qemu_root_bds_opts.head), + .desc = { + { + .name = "discard", + .type = QEMU_OPT_STRING, + .help = "discard operation (ignore/off, unmap/on)", + },{ + .name = "aio", + .type = QEMU_OPT_STRING, + .help = "host AIO implementation (threads, native)", + },{ + .name = "read-only", + .type = QEMU_OPT_BOOL, + .help = "open drive file as read-only", + },{ + .name = "copy-on-read", + .type = QEMU_OPT_BOOL, + .help = "copy read data from backing file into image file", + },{ + .name = "detect-zeroes", + .type = QEMU_OPT_STRING, + .help = "try to optimize zero writes (off, on, unmap)", }, { /* end of list */ } }, diff --git a/qemu/blockjob.c b/qemu/blockjob.c index 62bb90663..9fc37ca96 100644 --- a/qemu/blockjob.c +++ b/qemu/blockjob.c @@ -23,19 +23,33 @@ * THE SOFTWARE. */ -#include "config-host.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "trace.h" #include "block/block.h" #include "block/blockjob.h" #include "block/block_int.h" +#include "sysemu/block-backend.h" #include "qapi/qmp/qerror.h" #include "qapi/qmp/qjson.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "qmp-commands.h" #include "qemu/timer.h" #include "qapi-event.h" +/* Transactional group of block jobs */ +struct BlockJobTxn { + + /* Is this txn being cancelled? */ + bool aborting; + + /* List of jobs */ + QLIST_HEAD(, BlockJob) jobs; + + /* Reference count */ + int refcnt; +}; + void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs, int64_t speed, BlockCompletionFunc *cb, void *opaque, Error **errp) @@ -54,10 +68,12 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs, bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker); job->driver = driver; + job->id = g_strdup(bdrv_get_device_name(bs)); job->bs = bs; job->cb = cb; job->opaque = opaque; job->busy = true; + job->refcnt = 1; bs->job = job; /* Only set speed when necessary to avoid NotSupported error */ @@ -66,7 +82,7 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs, block_job_set_speed(job, speed, &local_err); if (local_err) { - block_job_release(bs); + block_job_unref(job); error_propagate(errp, local_err); return NULL; } @@ -74,14 +90,101 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs, return job; } -void block_job_release(BlockDriverState *bs) +void block_job_ref(BlockJob *job) +{ + ++job->refcnt; +} + +void block_job_unref(BlockJob *job) { - BlockJob *job = bs->job; + if (--job->refcnt == 0) { + job->bs->job = NULL; + bdrv_op_unblock_all(job->bs, job->blocker); + bdrv_unref(job->bs); + error_free(job->blocker); + g_free(job->id); + g_free(job); + } +} - bs->job = NULL; - bdrv_op_unblock_all(bs, job->blocker); - error_free(job->blocker); - g_free(job); +static void block_job_completed_single(BlockJob *job) +{ + if (!job->ret) { + if (job->driver->commit) { + job->driver->commit(job); + } + } else { + if (job->driver->abort) { + job->driver->abort(job); + } + } + job->cb(job->opaque, job->ret); + if (job->txn) { + block_job_txn_unref(job->txn); + } + block_job_unref(job); +} + +static void block_job_completed_txn_abort(BlockJob *job) +{ + AioContext *ctx; + BlockJobTxn *txn = job->txn; + BlockJob *other_job, *next; + + if (txn->aborting) { + /* + * We are cancelled by another job, which will handle everything. + */ + return; + } + txn->aborting = true; + /* We are the first failed job. Cancel other jobs. */ + QLIST_FOREACH(other_job, &txn->jobs, txn_list) { + ctx = bdrv_get_aio_context(other_job->bs); + aio_context_acquire(ctx); + } + QLIST_FOREACH(other_job, &txn->jobs, txn_list) { + if (other_job == job || other_job->completed) { + /* Other jobs are "effectively" cancelled by us, set the status for + * them; this job, however, may or may not be cancelled, depending + * on the caller, so leave it. */ + if (other_job != job) { + other_job->cancelled = true; + } + continue; + } + block_job_cancel_sync(other_job); + assert(other_job->completed); + } + QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) { + ctx = bdrv_get_aio_context(other_job->bs); + block_job_completed_single(other_job); + aio_context_release(ctx); + } +} + +static void block_job_completed_txn_success(BlockJob *job) +{ + AioContext *ctx; + BlockJobTxn *txn = job->txn; + BlockJob *other_job, *next; + /* + * Successful completion, see if there are other running jobs in this + * txn. + */ + QLIST_FOREACH(other_job, &txn->jobs, txn_list) { + 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 = bdrv_get_aio_context(other_job->bs); + aio_context_acquire(ctx); + assert(other_job->ret == 0); + block_job_completed_single(other_job); + aio_context_release(ctx); + } } void block_job_completed(BlockJob *job, int ret) @@ -89,8 +192,16 @@ void block_job_completed(BlockJob *job, int ret) BlockDriverState *bs = job->bs; assert(bs->job == job); - job->cb(job->opaque, ret); - block_job_release(bs); + 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_completed_txn_abort(job); + } else { + block_job_completed_txn_success(job); + } } void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) @@ -113,8 +224,7 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) void block_job_complete(BlockJob *job, Error **errp) { if (job->pause_count || job->cancelled || !job->driver->complete) { - error_setg(errp, QERR_BLOCK_JOB_NOT_READY, - bdrv_get_device_name(job->bs)); + error_setg(errp, QERR_BLOCK_JOB_NOT_READY, job->id); return; } @@ -168,51 +278,31 @@ void block_job_iostatus_reset(BlockJob *job) } } -struct BlockFinishData { - BlockJob *job; - BlockCompletionFunc *cb; - void *opaque; - bool cancelled; - int ret; -}; - -static void block_job_finish_cb(void *opaque, int ret) -{ - struct BlockFinishData *data = opaque; - - data->cancelled = block_job_is_cancelled(data->job); - data->ret = ret; - data->cb(data->opaque, ret); -} - static int block_job_finish_sync(BlockJob *job, void (*finish)(BlockJob *, Error **errp), Error **errp) { - struct BlockFinishData data; BlockDriverState *bs = job->bs; Error *local_err = NULL; + int ret; assert(bs->job == job); - /* Set up our own callback to store the result and chain to - * the original callback. - */ - data.job = job; - data.cb = job->cb; - data.opaque = job->opaque; - data.ret = -EINPROGRESS; - job->cb = block_job_finish_cb; - job->opaque = &data; + block_job_ref(job); finish(job, &local_err); if (local_err) { error_propagate(errp, local_err); + block_job_unref(job); return -EBUSY; } - while (data.ret == -EINPROGRESS) { - aio_poll(bdrv_get_aio_context(bs), true); + while (!job->completed) { + aio_poll(job->deferred_to_main_loop ? qemu_get_aio_context() : + bdrv_get_aio_context(bs), + true); } - return (data.cancelled && data.ret == 0) ? -ECANCELED : data.ret; + ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret; + block_job_unref(job); + return ret; } /* A wrapper around block_job_cancel() taking an Error ** parameter so it may be @@ -269,7 +359,7 @@ BlockJobInfo *block_job_query(BlockJob *job) { BlockJobInfo *info = g_new0(BlockJobInfo, 1); info->type = g_strdup(BlockJobType_lookup[job->driver->job_type]); - info->device = g_strdup(bdrv_get_device_name(job->bs)); + info->device = g_strdup(job->id); info->len = job->len; info->busy = job->busy; info->paused = job->pause_count > 0; @@ -291,7 +381,7 @@ static void block_job_iostatus_set_err(BlockJob *job, int error) void block_job_event_cancelled(BlockJob *job) { qapi_event_send_block_job_cancelled(job->driver->job_type, - bdrv_get_device_name(job->bs), + job->id, job->len, job->offset, job->speed, @@ -301,7 +391,7 @@ void block_job_event_cancelled(BlockJob *job) void block_job_event_completed(BlockJob *job, const char *msg) { qapi_event_send_block_job_completed(job->driver->job_type, - bdrv_get_device_name(job->bs), + job->id, job->len, job->offset, job->speed, @@ -315,7 +405,7 @@ void block_job_event_ready(BlockJob *job) job->ready = true; qapi_event_send_block_job_ready(job->driver->job_type, - bdrv_get_device_name(job->bs), + job->id, job->len, job->offset, job->speed, &error_abort); @@ -344,7 +434,7 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs, default: abort(); } - qapi_event_send_block_job_error(bdrv_get_device_name(job->bs), + qapi_event_send_block_job_error(job->id, is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE, action, &error_abort); @@ -353,8 +443,8 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs, job->user_paused = true; block_job_pause(job); block_job_iostatus_set_err(job, error); - if (bs != job->bs) { - bdrv_iostatus_set_err(bs, error); + if (bs->blk && bs != job->bs) { + blk_iostatus_set_err(bs->blk, error); } } return action; @@ -382,6 +472,7 @@ static void block_job_defer_to_main_loop_bh(void *opaque) aio_context = bdrv_get_aio_context(data->job->bs); aio_context_acquire(aio_context); + data->job->deferred_to_main_loop = false; data->fn(data->job, data->opaque); aio_context_release(aio_context); @@ -401,6 +492,40 @@ void block_job_defer_to_main_loop(BlockJob *job, data->aio_context = bdrv_get_aio_context(job->bs); data->fn = fn; data->opaque = opaque; + job->deferred_to_main_loop = true; qemu_bh_schedule(data->bh); } + +BlockJobTxn *block_job_txn_new(void) +{ + BlockJobTxn *txn = g_new0(BlockJobTxn, 1); + QLIST_INIT(&txn->jobs); + txn->refcnt = 1; + return txn; +} + +static void block_job_txn_ref(BlockJobTxn *txn) +{ + txn->refcnt++; +} + +void block_job_txn_unref(BlockJobTxn *txn) +{ + if (txn && --txn->refcnt == 0) { + g_free(txn); + } +} + +void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job) +{ + if (!txn) { + return; + } + + assert(!job->txn); + job->txn = txn; + + QLIST_INSERT_HEAD(&txn->jobs, job, txn_list); + block_job_txn_ref(txn); +} diff --git a/qemu/bootdevice.c b/qemu/bootdevice.c index 3cdc0d7b2..2e83ff05e 100644 --- a/qemu/bootdevice.c +++ b/qemu/bootdevice.c @@ -22,6 +22,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "sysemu/sysemu.h" #include "qapi/visitor.h" #include "qemu/error-report.h" @@ -270,21 +272,21 @@ typedef struct { DeviceState *dev; } BootIndexProperty; -static void device_get_bootindex(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void device_get_bootindex(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { BootIndexProperty *prop = opaque; - visit_type_int32(v, prop->bootindex, name, errp); + visit_type_int32(v, name, prop->bootindex, errp); } -static void device_set_bootindex(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void device_set_bootindex(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { BootIndexProperty *prop = opaque; int32_t boot_index; Error *local_err = NULL; - visit_type_int32(v, &boot_index, name, &local_err); + visit_type_int32(v, name, &boot_index, &local_err); if (local_err) { goto out; } diff --git a/qemu/bsd-user/bsdload.c b/qemu/bsd-user/bsdload.c index 2abc7136e..94eec363b 100644 --- a/qemu/bsd-user/bsdload.c +++ b/qemu/bsd-user/bsdload.c @@ -1,12 +1,6 @@ /* Code for loading BSD executables. Mostly linux kernel code. */ -#include -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include "qemu.h" diff --git a/qemu/bsd-user/elfload.c b/qemu/bsd-user/elfload.c index 2bf57eb1f..898ee0547 100644 --- a/qemu/bsd-user/elfload.c +++ b/qemu/bsd-user/elfload.c @@ -1,16 +1,11 @@ /* This is the Linux kernel elf-loading code, ported into user space */ -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include -#include -#include #include "qemu.h" #include "disas/disas.h" +#include "qemu/path.h" #ifdef _ARCH_PPC64 #undef ARCH_DLINFO @@ -740,8 +735,7 @@ static void padzero(abi_ulong elf_bss, abi_ulong last_bss) size must be known */ if (qemu_real_host_page_size < qemu_host_page_size) { abi_ulong end_addr, end_addr1; - end_addr1 = (elf_bss + qemu_real_host_page_size - 1) & - ~(qemu_real_host_page_size - 1); + end_addr1 = REAL_HOST_PAGE_ALIGN(elf_bss); end_addr = HOST_PAGE_ALIGN(elf_bss); if (end_addr1 < end_addr) { mmap((void *)g2h(end_addr1), end_addr - end_addr1, @@ -1355,9 +1349,7 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, } } if (!bprm->p) { - if (elf_interpreter) { - free(elf_interpreter); - } + free(elf_interpreter); free (elf_phdata); close(bprm->fd); return -E2BIG; @@ -1371,7 +1363,6 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, info->mmap = 0; elf_entry = (abi_ulong) elf_ex.e_entry; -#if defined(CONFIG_USE_GUEST_BASE) /* * In case where user has not explicitly set the guest_base, we * probe here that should we set it automatically. @@ -1392,7 +1383,6 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, } } } -#endif /* CONFIG_USE_GUEST_BASE */ /* Do this so that we can load the interpreter, if need be. We will change some of these later */ diff --git a/qemu/bsd-user/i386/syscall.h b/qemu/bsd-user/i386/syscall.h deleted file mode 100644 index 9b34c61bb..000000000 --- a/qemu/bsd-user/i386/syscall.h +++ /dev/null @@ -1,161 +0,0 @@ -/* default linux values for the selectors */ -#define __USER_CS (0x23) -#define __USER_DS (0x2B) - -struct target_pt_regs { - long ebx; - long ecx; - long edx; - long esi; - long edi; - long ebp; - long eax; - int xds; - int xes; - long orig_eax; - long eip; - int xcs; - long eflags; - long esp; - int xss; -}; - -/* ioctls */ - -#define TARGET_LDT_ENTRIES 8192 -#define TARGET_LDT_ENTRY_SIZE 8 - -#define TARGET_GDT_ENTRIES 9 -#define TARGET_GDT_ENTRY_TLS_ENTRIES 3 -#define TARGET_GDT_ENTRY_TLS_MIN 6 -#define TARGET_GDT_ENTRY_TLS_MAX (TARGET_GDT_ENTRY_TLS_MIN + TARGET_GDT_ENTRY_TLS_ENTRIES - 1) - -struct target_modify_ldt_ldt_s { - unsigned int entry_number; - abi_ulong base_addr; - unsigned int limit; - unsigned int flags; -}; - -/* vm86 defines */ - -#define TARGET_BIOSSEG 0x0f000 - -#define TARGET_CPU_086 0 -#define TARGET_CPU_186 1 -#define TARGET_CPU_286 2 -#define TARGET_CPU_386 3 -#define TARGET_CPU_486 4 -#define TARGET_CPU_586 5 - -#define TARGET_VM86_SIGNAL 0 /* return due to signal */ -#define TARGET_VM86_UNKNOWN 1 /* unhandled GP fault - IO-instruction or similar */ -#define TARGET_VM86_INTx 2 /* int3/int x instruction (ARG = x) */ -#define TARGET_VM86_STI 3 /* sti/popf/iret instruction enabled virtual interrupts */ - -/* - * Additional return values when invoking new vm86() - */ -#define TARGET_VM86_PICRETURN 4 /* return due to pending PIC request */ -#define TARGET_VM86_TRAP 6 /* return due to DOS-debugger request */ - -/* - * function codes when invoking new vm86() - */ -#define TARGET_VM86_PLUS_INSTALL_CHECK 0 -#define TARGET_VM86_ENTER 1 -#define TARGET_VM86_ENTER_NO_BYPASS 2 -#define TARGET_VM86_REQUEST_IRQ 3 -#define TARGET_VM86_FREE_IRQ 4 -#define TARGET_VM86_GET_IRQ_BITS 5 -#define TARGET_VM86_GET_AND_RESET_IRQ 6 - -/* - * This is the stack-layout seen by the user space program when we have - * done a translation of "SAVE_ALL" from vm86 mode. The real kernel layout - * is 'kernel_vm86_regs' (see below). - */ - -struct target_vm86_regs { -/* - * normal regs, with special meaning for the segment descriptors.. - */ - abi_long ebx; - abi_long ecx; - abi_long edx; - abi_long esi; - abi_long edi; - abi_long ebp; - abi_long eax; - abi_long __null_ds; - abi_long __null_es; - abi_long __null_fs; - abi_long __null_gs; - abi_long orig_eax; - abi_long eip; - unsigned short cs, __csh; - abi_long eflags; - abi_long esp; - unsigned short ss, __ssh; -/* - * these are specific to v86 mode: - */ - unsigned short es, __esh; - unsigned short ds, __dsh; - unsigned short fs, __fsh; - unsigned short gs, __gsh; -}; - -struct target_revectored_struct { - abi_ulong __map[8]; /* 256 bits */ -}; - -struct target_vm86_struct { - struct target_vm86_regs regs; - abi_ulong flags; - abi_ulong screen_bitmap; - abi_ulong cpu_type; - struct target_revectored_struct int_revectored; - struct target_revectored_struct int21_revectored; -}; - -/* - * flags masks - */ -#define TARGET_VM86_SCREEN_BITMAP 0x0001 - -struct target_vm86plus_info_struct { - abi_ulong flags; -#define TARGET_force_return_for_pic (1 << 0) -#define TARGET_vm86dbg_active (1 << 1) /* for debugger */ -#define TARGET_vm86dbg_TFpendig (1 << 2) /* for debugger */ -#define TARGET_is_vm86pus (1 << 31) /* for vm86 internal use */ - unsigned char vm86dbg_intxxtab[32]; /* for debugger */ -}; - -struct target_vm86plus_struct { - struct target_vm86_regs regs; - abi_ulong flags; - abi_ulong screen_bitmap; - abi_ulong cpu_type; - struct target_revectored_struct int_revectored; - struct target_revectored_struct int21_revectored; - struct target_vm86plus_info_struct vm86plus; -}; - -/* FreeBSD sysarch(2) */ -#define TARGET_FREEBSD_I386_GET_LDT 0 -#define TARGET_FREEBSD_I386_SET_LDT 1 - /* I386_IOPL */ -#define TARGET_FREEBSD_I386_GET_IOPERM 3 -#define TARGET_FREEBSD_I386_SET_IOPERM 4 - /* xxxxx */ -#define TARGET_FREEBSD_I386_VM86 6 -#define TARGET_FREEBSD_I386_GET_FSBASE 7 -#define TARGET_FREEBSD_I386_SET_FSBASE 8 -#define TARGET_FREEBSD_I386_GET_GSBASE 9 -#define TARGET_FREEBSD_I386_SET_GSBASE 10 - - -#define UNAME_MACHINE "i386" - diff --git a/qemu/bsd-user/i386/target_syscall.h b/qemu/bsd-user/i386/target_syscall.h new file mode 100644 index 000000000..82d1c58ca --- /dev/null +++ b/qemu/bsd-user/i386/target_syscall.h @@ -0,0 +1,165 @@ +#ifndef TARGET_SYSCALL_H +#define TARGET_SYSCALL_H + +/* default linux values for the selectors */ +#define __USER_CS (0x23) +#define __USER_DS (0x2B) + +struct target_pt_regs { + long ebx; + long ecx; + long edx; + long esi; + long edi; + long ebp; + long eax; + int xds; + int xes; + long orig_eax; + long eip; + int xcs; + long eflags; + long esp; + int xss; +}; + +/* ioctls */ + +#define TARGET_LDT_ENTRIES 8192 +#define TARGET_LDT_ENTRY_SIZE 8 + +#define TARGET_GDT_ENTRIES 9 +#define TARGET_GDT_ENTRY_TLS_ENTRIES 3 +#define TARGET_GDT_ENTRY_TLS_MIN 6 +#define TARGET_GDT_ENTRY_TLS_MAX (TARGET_GDT_ENTRY_TLS_MIN + TARGET_GDT_ENTRY_TLS_ENTRIES - 1) + +struct target_modify_ldt_ldt_s { + unsigned int entry_number; + abi_ulong base_addr; + unsigned int limit; + unsigned int flags; +}; + +/* vm86 defines */ + +#define TARGET_BIOSSEG 0x0f000 + +#define TARGET_CPU_086 0 +#define TARGET_CPU_186 1 +#define TARGET_CPU_286 2 +#define TARGET_CPU_386 3 +#define TARGET_CPU_486 4 +#define TARGET_CPU_586 5 + +#define TARGET_VM86_SIGNAL 0 /* return due to signal */ +#define TARGET_VM86_UNKNOWN 1 /* unhandled GP fault - IO-instruction or similar */ +#define TARGET_VM86_INTx 2 /* int3/int x instruction (ARG = x) */ +#define TARGET_VM86_STI 3 /* sti/popf/iret instruction enabled virtual interrupts */ + +/* + * Additional return values when invoking new vm86() + */ +#define TARGET_VM86_PICRETURN 4 /* return due to pending PIC request */ +#define TARGET_VM86_TRAP 6 /* return due to DOS-debugger request */ + +/* + * function codes when invoking new vm86() + */ +#define TARGET_VM86_PLUS_INSTALL_CHECK 0 +#define TARGET_VM86_ENTER 1 +#define TARGET_VM86_ENTER_NO_BYPASS 2 +#define TARGET_VM86_REQUEST_IRQ 3 +#define TARGET_VM86_FREE_IRQ 4 +#define TARGET_VM86_GET_IRQ_BITS 5 +#define TARGET_VM86_GET_AND_RESET_IRQ 6 + +/* + * This is the stack-layout seen by the user space program when we have + * done a translation of "SAVE_ALL" from vm86 mode. The real kernel layout + * is 'kernel_vm86_regs' (see below). + */ + +struct target_vm86_regs { +/* + * normal regs, with special meaning for the segment descriptors.. + */ + abi_long ebx; + abi_long ecx; + abi_long edx; + abi_long esi; + abi_long edi; + abi_long ebp; + abi_long eax; + abi_long __null_ds; + abi_long __null_es; + abi_long __null_fs; + abi_long __null_gs; + abi_long orig_eax; + abi_long eip; + unsigned short cs, __csh; + abi_long eflags; + abi_long esp; + unsigned short ss, __ssh; +/* + * these are specific to v86 mode: + */ + unsigned short es, __esh; + unsigned short ds, __dsh; + unsigned short fs, __fsh; + unsigned short gs, __gsh; +}; + +struct target_revectored_struct { + abi_ulong __map[8]; /* 256 bits */ +}; + +struct target_vm86_struct { + struct target_vm86_regs regs; + abi_ulong flags; + abi_ulong screen_bitmap; + abi_ulong cpu_type; + struct target_revectored_struct int_revectored; + struct target_revectored_struct int21_revectored; +}; + +/* + * flags masks + */ +#define TARGET_VM86_SCREEN_BITMAP 0x0001 + +struct target_vm86plus_info_struct { + abi_ulong flags; +#define TARGET_force_return_for_pic (1 << 0) +#define TARGET_vm86dbg_active (1 << 1) /* for debugger */ +#define TARGET_vm86dbg_TFpendig (1 << 2) /* for debugger */ +#define TARGET_is_vm86pus (1 << 31) /* for vm86 internal use */ + unsigned char vm86dbg_intxxtab[32]; /* for debugger */ +}; + +struct target_vm86plus_struct { + struct target_vm86_regs regs; + abi_ulong flags; + abi_ulong screen_bitmap; + abi_ulong cpu_type; + struct target_revectored_struct int_revectored; + struct target_revectored_struct int21_revectored; + struct target_vm86plus_info_struct vm86plus; +}; + +/* FreeBSD sysarch(2) */ +#define TARGET_FREEBSD_I386_GET_LDT 0 +#define TARGET_FREEBSD_I386_SET_LDT 1 + /* I386_IOPL */ +#define TARGET_FREEBSD_I386_GET_IOPERM 3 +#define TARGET_FREEBSD_I386_SET_IOPERM 4 + /* xxxxx */ +#define TARGET_FREEBSD_I386_VM86 6 +#define TARGET_FREEBSD_I386_GET_FSBASE 7 +#define TARGET_FREEBSD_I386_SET_FSBASE 8 +#define TARGET_FREEBSD_I386_GET_GSBASE 9 +#define TARGET_FREEBSD_I386_SET_GSBASE 10 + + +#define UNAME_MACHINE "i386" + +#endif /* TARGET_SYSCALL_H */ diff --git a/qemu/bsd-user/main.c b/qemu/bsd-user/main.c index ee68daa39..27854c1f9 100644 --- a/qemu/bsd-user/main.c +++ b/qemu/bsd-user/main.c @@ -16,31 +16,25 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include -#include #include #include "qemu.h" -#include "qemu-common.h" +#include "qemu/path.h" +#include "qemu/help_option.h" /* For tb_lock */ #include "cpu.h" #include "tcg.h" #include "qemu/timer.h" #include "qemu/envlist.h" +#include "exec/log.h" int singlestep; -#if defined(CONFIG_USE_GUEST_BASE) unsigned long mmap_min_addr; unsigned long guest_base; int have_guest_base; unsigned long reserved_va; -#endif static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX; const char *qemu_uname_release; @@ -110,7 +104,7 @@ void cpu_list_unlock(void) uint64_t cpu_get_tsc(CPUX86State *env) { - return cpu_get_real_ticks(); + return cpu_get_host_ticks(); } static void write_dt(void *ptr, unsigned long addr, unsigned long limit, @@ -682,9 +676,7 @@ static void usage(void) "-drop-ld-preload drop LD_PRELOAD for target process\n" "-E var=value sets/modifies targets environment variable(s)\n" "-U var unsets targets environment variable(s)\n" -#if defined(CONFIG_USE_GUEST_BASE) "-B address set guest_base address to address\n" -#endif "-bsd type select emulated BSD type FreeBSD/NetBSD/OpenBSD (default)\n" "\n" "Debug options:\n" @@ -830,11 +822,9 @@ int main(int argc, char **argv) #endif exit(1); } -#if defined(CONFIG_USE_GUEST_BASE) } else if (!strcmp(r, "B")) { guest_base = strtol(argv[optind++], NULL, 0); have_guest_base = 1; -#endif } else if (!strcmp(r, "drop-ld-preload")) { (void) envlist_unsetenv(envlist, "LD_PRELOAD"); } else if (!strcmp(r, "bsd")) { @@ -923,7 +913,6 @@ int main(int argc, char **argv) target_environ = envlist_to_environ(envlist, NULL); envlist_free(envlist); -#if defined(CONFIG_USE_GUEST_BASE) /* * Now that page sizes are configured in cpu_init() we can do * proper page alignment for guest_base. @@ -945,12 +934,11 @@ int main(int argc, char **argv) unsigned long tmp; if (fscanf(fp, "%lu", &tmp) == 1) { mmap_min_addr = tmp; - qemu_log("host mmap_min_addr=0x%lx\n", mmap_min_addr); + qemu_log_mask(CPU_LOG_PAGE, "host mmap_min_addr=0x%lx\n", mmap_min_addr); } fclose(fp); } } -#endif /* CONFIG_USE_GUEST_BASE */ if (loader_exec(filename, argv+optind, target_environ, regs, info) != 0) { printf("Error loading %s\n", filename); @@ -963,10 +951,8 @@ int main(int argc, char **argv) free(target_environ); - if (qemu_log_enabled()) { -#if defined(CONFIG_USE_GUEST_BASE) + if (qemu_loglevel_mask(CPU_LOG_PAGE)) { qemu_log("guest_base 0x%lx\n", guest_base); -#endif log_page_dump(); qemu_log("start_brk 0x" TARGET_ABI_FMT_lx "\n", info->start_brk); @@ -986,12 +972,10 @@ int main(int argc, char **argv) syscall_init(); signal_init(); -#if defined(CONFIG_USE_GUEST_BASE) /* Now that we've loaded the binary, GUEST_BASE is fixed. Delay generating the prologue until now so that the prologue can take the real value of GUEST_BASE into account. */ tcg_prologue_init(&tcg_ctx); -#endif /* build Task State */ memset(ts, 0, sizeof(TaskState)); diff --git a/qemu/bsd-user/mmap.c b/qemu/bsd-user/mmap.c index 092bf7f89..6ab533470 100644 --- a/qemu/bsd-user/mmap.c +++ b/qemu/bsd-user/mmap.c @@ -16,12 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include #include "qemu.h" diff --git a/qemu/bsd-user/qemu.h b/qemu/bsd-user/qemu.h index 5362297fe..03b502ad3 100644 --- a/qemu/bsd-user/qemu.h +++ b/qemu/bsd-user/qemu.h @@ -17,15 +17,12 @@ #ifndef QEMU_H #define QEMU_H -#include -#include #include "cpu.h" #include "exec/cpu_ldst.h" #undef DEBUG_REMAP #ifdef DEBUG_REMAP -#include #endif /* DEBUG_REMAP */ #include "exec/user/abitypes.h" @@ -38,7 +35,7 @@ enum BSDType { extern enum BSDType bsd_type; #include "syscall_defs.h" -#include "syscall.h" +#include "target_syscall.h" #include "target_signal.h" #include "exec/gdbstub.h" @@ -101,9 +98,7 @@ typedef struct TaskState { void init_task_state(TaskState *ts); extern const char *qemu_uname_release; -#if defined(CONFIG_USE_GUEST_BASE) extern unsigned long mmap_min_addr; -#endif /* ??? See if we can avoid exposing so much of the loader internals. */ /* @@ -213,8 +208,6 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, abi_ulong new_addr); int target_msync(abi_ulong start, abi_ulong len, int flags); extern unsigned long last_brk; -void mmap_lock(void); -void mmap_unlock(void); void cpu_list_lock(void); void cpu_list_unlock(void); #if defined(CONFIG_USE_NPTL) diff --git a/qemu/bsd-user/signal.c b/qemu/bsd-user/signal.c index 445f69e83..f6f7aa242 100644 --- a/qemu/bsd-user/signal.c +++ b/qemu/bsd-user/signal.c @@ -16,19 +16,11 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ -#include -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include "qemu.h" #include "target_signal.h" -//#define DEBUG_SIGNAL - void signal_init(void) { } diff --git a/qemu/bsd-user/sparc/syscall.h b/qemu/bsd-user/sparc/syscall.h deleted file mode 100644 index 5a9bb7e54..000000000 --- a/qemu/bsd-user/sparc/syscall.h +++ /dev/null @@ -1,9 +0,0 @@ -struct target_pt_regs { - abi_ulong psr; - abi_ulong pc; - abi_ulong npc; - abi_ulong y; - abi_ulong u_regs[16]; -}; - -#define UNAME_MACHINE "sun4" diff --git a/qemu/bsd-user/sparc/target_syscall.h b/qemu/bsd-user/sparc/target_syscall.h new file mode 100644 index 000000000..c7eec6ba6 --- /dev/null +++ b/qemu/bsd-user/sparc/target_syscall.h @@ -0,0 +1,14 @@ +#ifndef TARGET_SYSCALL_H +#define TARGET_SYSCALL_H + +struct target_pt_regs { + abi_ulong psr; + abi_ulong pc; + abi_ulong npc; + abi_ulong y; + abi_ulong u_regs[16]; +}; + +#define UNAME_MACHINE "sun4" + +#endif /* TARGET_SYSCALL_H */ diff --git a/qemu/bsd-user/sparc64/syscall.h b/qemu/bsd-user/sparc64/syscall.h deleted file mode 100644 index 81a816de9..000000000 --- a/qemu/bsd-user/sparc64/syscall.h +++ /dev/null @@ -1,10 +0,0 @@ -struct target_pt_regs { - abi_ulong u_regs[16]; - abi_ulong tstate; - abi_ulong pc; - abi_ulong npc; - abi_ulong y; - abi_ulong fprs; -}; - -#define UNAME_MACHINE "sun4u" diff --git a/qemu/bsd-user/sparc64/target_syscall.h b/qemu/bsd-user/sparc64/target_syscall.h new file mode 100644 index 000000000..2f06100ae --- /dev/null +++ b/qemu/bsd-user/sparc64/target_syscall.h @@ -0,0 +1,15 @@ +#ifndef TARGET_SYSCALL_H +#define TARGET_SYSCALL_H + +struct target_pt_regs { + abi_ulong u_regs[16]; + abi_ulong tstate; + abi_ulong pc; + abi_ulong npc; + abi_ulong y; + abi_ulong fprs; +}; + +#define UNAME_MACHINE "sun4u" + +#endif /* TARGET_SYSCALL_H */ diff --git a/qemu/bsd-user/strace.c b/qemu/bsd-user/strace.c index e33dd4d48..fa66fe1ee 100644 --- a/qemu/bsd-user/strace.c +++ b/qemu/bsd-user/strace.c @@ -16,14 +16,10 @@ * along with this program; if not, see . */ -#include -#include +#include "qemu/osdep.h" #include -#include -#include #include #include -#include #include "qemu.h" diff --git a/qemu/bsd-user/syscall.c b/qemu/bsd-user/syscall.c index a4d1583fe..47cf865a3 100644 --- a/qemu/bsd-user/syscall.c +++ b/qemu/bsd-user/syscall.c @@ -16,17 +16,9 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "qemu/path.h" #include #include #include diff --git a/qemu/bsd-user/uaccess.c b/qemu/bsd-user/uaccess.c index 677f19c26..91e206793 100644 --- a/qemu/bsd-user/uaccess.c +++ b/qemu/bsd-user/uaccess.c @@ -1,6 +1,6 @@ /* User memory access */ -#include -#include +#include "qemu/osdep.h" +#include "qemu/cutils.h" #include "qemu.h" @@ -51,7 +51,7 @@ abi_long target_strlen(abi_ulong guest_addr1) ptr = lock_user(VERIFY_READ, guest_addr, max_len, 1); if (!ptr) return -TARGET_EFAULT; - len = qemu_strnlen((char *)ptr, max_len); + len = qemu_strnlen((const char *)ptr, max_len); unlock_user(ptr, guest_addr, 0); guest_addr += len; /* we don't allow wrapping or integer overflow */ diff --git a/qemu/bsd-user/x86_64/syscall.h b/qemu/bsd-user/x86_64/syscall.h deleted file mode 100644 index 630514a93..000000000 --- a/qemu/bsd-user/x86_64/syscall.h +++ /dev/null @@ -1,116 +0,0 @@ -#define __USER_CS (0x33) -#define __USER_DS (0x2B) - -struct target_pt_regs { - abi_ulong r15; - abi_ulong r14; - abi_ulong r13; - abi_ulong r12; - abi_ulong rbp; - abi_ulong rbx; -/* arguments: non interrupts/non tracing syscalls only save up to here */ - abi_ulong r11; - abi_ulong r10; - abi_ulong r9; - abi_ulong r8; - abi_ulong rax; - abi_ulong rcx; - abi_ulong rdx; - abi_ulong rsi; - abi_ulong rdi; - abi_ulong orig_rax; -/* end of arguments */ -/* cpu exception frame or undefined */ - abi_ulong rip; - abi_ulong cs; - abi_ulong eflags; - abi_ulong rsp; - abi_ulong ss; -/* top of stack page */ -}; - -/* Maximum number of LDT entries supported. */ -#define TARGET_LDT_ENTRIES 8192 -/* The size of each LDT entry. */ -#define TARGET_LDT_ENTRY_SIZE 8 - -#define TARGET_GDT_ENTRIES 16 -#define TARGET_GDT_ENTRY_TLS_ENTRIES 3 -#define TARGET_GDT_ENTRY_TLS_MIN 12 -#define TARGET_GDT_ENTRY_TLS_MAX 14 - -#if 0 // Redefine this -struct target_modify_ldt_ldt_s { - unsigned int entry_number; - abi_ulong base_addr; - unsigned int limit; - unsigned int seg_32bit:1; - unsigned int contents:2; - unsigned int read_exec_only:1; - unsigned int limit_in_pages:1; - unsigned int seg_not_present:1; - unsigned int useable:1; - unsigned int lm:1; -}; -#else -struct target_modify_ldt_ldt_s { - unsigned int entry_number; - abi_ulong base_addr; - unsigned int limit; - unsigned int flags; -}; -#endif - -struct target_ipc64_perm -{ - int key; - uint32_t uid; - uint32_t gid; - uint32_t cuid; - uint32_t cgid; - unsigned short mode; - unsigned short __pad1; - unsigned short seq; - unsigned short __pad2; - abi_ulong __unused1; - abi_ulong __unused2; -}; - -struct target_msqid64_ds { - struct target_ipc64_perm msg_perm; - unsigned int msg_stime; /* last msgsnd time */ - unsigned int msg_rtime; /* last msgrcv time */ - unsigned int msg_ctime; /* last change time */ - abi_ulong msg_cbytes; /* current number of bytes on queue */ - abi_ulong msg_qnum; /* number of messages in queue */ - abi_ulong msg_qbytes; /* max number of bytes on queue */ - unsigned int msg_lspid; /* pid of last msgsnd */ - unsigned int msg_lrpid; /* last receive pid */ - abi_ulong __unused4; - abi_ulong __unused5; -}; - -/* FreeBSD sysarch(2) */ -#define TARGET_FREEBSD_I386_GET_LDT 0 -#define TARGET_FREEBSD_I386_SET_LDT 1 - /* I386_IOPL */ -#define TARGET_FREEBSD_I386_GET_IOPERM 3 -#define TARGET_FREEBSD_I386_SET_IOPERM 4 - /* xxxxx */ -#define TARGET_FREEBSD_I386_GET_FSBASE 7 -#define TARGET_FREEBSD_I386_SET_FSBASE 8 -#define TARGET_FREEBSD_I386_GET_GSBASE 9 -#define TARGET_FREEBSD_I386_SET_GSBASE 10 - -#define TARGET_FREEBSD_AMD64_GET_FSBASE 128 -#define TARGET_FREEBSD_AMD64_SET_FSBASE 129 -#define TARGET_FREEBSD_AMD64_GET_GSBASE 130 -#define TARGET_FREEBSD_AMD64_SET_GSBASE 131 - - -#define UNAME_MACHINE "x86_64" - -#define TARGET_ARCH_SET_GS 0x1001 -#define TARGET_ARCH_SET_FS 0x1002 -#define TARGET_ARCH_GET_FS 0x1003 -#define TARGET_ARCH_GET_GS 0x1004 diff --git a/qemu/bsd-user/x86_64/target_syscall.h b/qemu/bsd-user/x86_64/target_syscall.h new file mode 100644 index 000000000..85a976697 --- /dev/null +++ b/qemu/bsd-user/x86_64/target_syscall.h @@ -0,0 +1,121 @@ +#ifndef TARGET_SYSCALL_H +#define TARGET_SYSCALL_H + +#define __USER_CS (0x33) +#define __USER_DS (0x2B) + +struct target_pt_regs { + abi_ulong r15; + abi_ulong r14; + abi_ulong r13; + abi_ulong r12; + abi_ulong rbp; + abi_ulong rbx; +/* arguments: non interrupts/non tracing syscalls only save up to here */ + abi_ulong r11; + abi_ulong r10; + abi_ulong r9; + abi_ulong r8; + abi_ulong rax; + abi_ulong rcx; + abi_ulong rdx; + abi_ulong rsi; + abi_ulong rdi; + abi_ulong orig_rax; +/* end of arguments */ +/* cpu exception frame or undefined */ + abi_ulong rip; + abi_ulong cs; + abi_ulong eflags; + abi_ulong rsp; + abi_ulong ss; +/* top of stack page */ +}; + +/* Maximum number of LDT entries supported. */ +#define TARGET_LDT_ENTRIES 8192 +/* The size of each LDT entry. */ +#define TARGET_LDT_ENTRY_SIZE 8 + +#define TARGET_GDT_ENTRIES 16 +#define TARGET_GDT_ENTRY_TLS_ENTRIES 3 +#define TARGET_GDT_ENTRY_TLS_MIN 12 +#define TARGET_GDT_ENTRY_TLS_MAX 14 + +#if 0 // Redefine this +struct target_modify_ldt_ldt_s { + unsigned int entry_number; + abi_ulong base_addr; + unsigned int limit; + unsigned int seg_32bit:1; + unsigned int contents:2; + unsigned int read_exec_only:1; + unsigned int limit_in_pages:1; + unsigned int seg_not_present:1; + unsigned int useable:1; + unsigned int lm:1; +}; +#else +struct target_modify_ldt_ldt_s { + unsigned int entry_number; + abi_ulong base_addr; + unsigned int limit; + unsigned int flags; +}; +#endif + +struct target_ipc64_perm +{ + int key; + uint32_t uid; + uint32_t gid; + uint32_t cuid; + uint32_t cgid; + unsigned short mode; + unsigned short __pad1; + unsigned short seq; + unsigned short __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_msqid64_ds { + struct target_ipc64_perm msg_perm; + unsigned int msg_stime; /* last msgsnd time */ + unsigned int msg_rtime; /* last msgrcv time */ + unsigned int msg_ctime; /* last change time */ + abi_ulong msg_cbytes; /* current number of bytes on queue */ + abi_ulong msg_qnum; /* number of messages in queue */ + abi_ulong msg_qbytes; /* max number of bytes on queue */ + unsigned int msg_lspid; /* pid of last msgsnd */ + unsigned int msg_lrpid; /* last receive pid */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +/* FreeBSD sysarch(2) */ +#define TARGET_FREEBSD_I386_GET_LDT 0 +#define TARGET_FREEBSD_I386_SET_LDT 1 + /* I386_IOPL */ +#define TARGET_FREEBSD_I386_GET_IOPERM 3 +#define TARGET_FREEBSD_I386_SET_IOPERM 4 + /* xxxxx */ +#define TARGET_FREEBSD_I386_GET_FSBASE 7 +#define TARGET_FREEBSD_I386_SET_FSBASE 8 +#define TARGET_FREEBSD_I386_GET_GSBASE 9 +#define TARGET_FREEBSD_I386_SET_GSBASE 10 + +#define TARGET_FREEBSD_AMD64_GET_FSBASE 128 +#define TARGET_FREEBSD_AMD64_SET_FSBASE 129 +#define TARGET_FREEBSD_AMD64_GET_GSBASE 130 +#define TARGET_FREEBSD_AMD64_SET_GSBASE 131 + + +#define UNAME_MACHINE "x86_64" + +#define TARGET_ARCH_SET_GS 0x1001 +#define TARGET_ARCH_SET_FS 0x1002 +#define TARGET_ARCH_GET_FS 0x1003 +#define TARGET_ARCH_GET_GS 0x1004 + +#endif /* TARGET_SYSCALL_H */ diff --git a/qemu/bt-host.c b/qemu/bt-host.c index 49205bf28..2f8f631c2 100644 --- a/qemu/bt-host.c +++ b/qemu/bt-host.c @@ -17,12 +17,12 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "sysemu/bt.h" #include "qemu/main-loop.h" #ifndef _WIN32 -# include # include # include # ifdef CONFIG_BLUEZ diff --git a/qemu/bt-vhci.c b/qemu/bt-vhci.c index e267c8ad1..9d277c32b 100644 --- a/qemu/bt-vhci.c +++ b/qemu/bt-vhci.c @@ -17,6 +17,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "sysemu/bt.h" #include "hw/bt.h" diff --git a/qemu/configure b/qemu/configure index cd219d8e3..c37fc5fe7 100755 --- a/qemu/configure +++ b/qemu/configure @@ -8,6 +8,9 @@ CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS +# Don't allow CCACHE, if present, to use cached results of compile tests! +export CCACHE_RECACHE=yes + # Temporary directory used for files created while # configure runs. Since it is in the build directory # we can safely blow away any previous version of it @@ -113,38 +116,6 @@ compile_prog() { do_cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags } -do_libtool() { - local mode=$1 - shift - # Run the compiler, capturing its output to the log. - echo $libtool $mode --tag=CC $cc "$@" >> config.log - $libtool $mode --tag=CC $cc "$@" >> config.log 2>&1 || return $? - # Test passed. If this is an --enable-werror build, rerun - # the test with -Werror and bail out if it fails. This - # makes warning-generating-errors in configure test code - # obvious to developers. - if test "$werror" != "yes"; then - return 0 - fi - # Don't bother rerunning the compile if we were already using -Werror - case "$*" in - *-Werror*) - return 0 - ;; - esac - echo $libtool $mode --tag=CC $cc -Werror "$@" >> config.log - $libtool $mode --tag=CC $cc -Werror "$@" >> config.log 2>&1 && return $? - error_exit "configure test passed without -Werror but failed with -Werror." \ - "This is probably a bug in the configure script. The failing command" \ - "will be at the bottom of config.log." \ - "You can run configure with --disable-werror to bypass this check." -} - -libtool_prog() { - do_libtool --mode=compile $QEMU_CFLAGS -c -fPIE -DPIE -o $TMPO $TMPC || return $? - do_libtool --mode=link $LDFLAGS -o $TMPA $TMPL -rpath /usr/local/lib -} - # symbolically link $1 to $2. Portable version of "ln -sf". symlink() { rm -rf "$2" @@ -242,12 +213,12 @@ vnc="yes" sparse="no" uuid="" vde="" -vnc_tls="" vnc_sasl="" vnc_jpeg="" vnc_png="" xen="" xen_ctrl_version="" +xen_pv_domain_build="no" xen_pci_passthrough="" linux_aio="" cap_ng="" @@ -262,6 +233,7 @@ rdma="" gprof="no" debug_tcg="no" debug="no" +fortify_source="" strip_opt="yes" tcg_interpreter="no" bigendian="no" @@ -293,21 +265,22 @@ cocoa="no" softmmu="yes" linux_user="no" bsd_user="no" -guest_base="yes" aix="no" blobs="yes" pkgversion="" pie="" zero_malloc="" qom_cast_debug="yes" -trace_backends="nop" +trace_backends="log" trace_file="trace" spice="" rbd="" -smartcard_nss="" +smartcard="" libusb="" usb_redir="" opengl="" +opengl_dmabuf="no" +avx2_opt="no" zlib="yes" lzo="" snappy="" @@ -325,19 +298,28 @@ coroutine="" coroutine_pool="" seccomp="" glusterfs="" +glusterfs_xlator_opt="no" glusterfs_discard="no" glusterfs_zerofill="no" archipelago="no" gtk="" gtkabi="" +gtk_gl="no" gnutls="" gnutls_hash="" +gnutls_rnd="" +nettle="" +nettle_kdf="no" +gcrypt="" +gcrypt_kdf="no" vte="" +virglrenderer="" tpm="yes" libssh2="" vhdx="" numa="" tcmalloc="no" +jemalloc="no" # parse CC options first for opt do @@ -390,7 +372,6 @@ as="${AS-${cross_prefix}as}" cpp="${CPP-$cc -E}" objcopy="${OBJCOPY-${cross_prefix}objcopy}" ld="${LD-${cross_prefix}ld}" -libtool="${LIBTOOL-${cross_prefix}libtool}" nm="${NM-${cross_prefix}nm}" strip="${STRIP-${cross_prefix}strip}" windres="${WINDRES-${cross_prefix}windres}" @@ -720,6 +701,8 @@ if test "$mingw32" = "yes" ; then QEMU_CFLAGS="-DWIN32_LEAN_AND_MEAN -DWINVER=0x501 $QEMU_CFLAGS" # enable C99/POSIX format strings (needs mingw32-runtime 3.15 or later) QEMU_CFLAGS="-D__USE_MINGW_ANSI_STDIO=1 $QEMU_CFLAGS" + # MinGW needs -mthreads for TLS and macro _MT. + QEMU_CFLAGS="-mthreads $QEMU_CFLAGS" LIBS="-lwinmm -lws2_32 -liphlpapi $LIBS" write_c_skeleton; if compile_prog "" "-liberty" ; then @@ -733,7 +716,7 @@ if test "$mingw32" = "yes" ; then sysconfdir="\${prefix}" local_statedir= confsuffix="" - libs_qga="-lws2_32 -lwinmm -lpowrprof -liphlpapi $libs_qga" + libs_qga="-lws2_32 -lwinmm -lpowrprof -liphlpapi -lnetapi32 $libs_qga" fi werror="" @@ -784,6 +767,9 @@ for opt do --enable-modules) modules="yes" ;; + --disable-modules) + modules="no" + ;; --cpu=*) ;; --target-list=*) target_list="$optarg" @@ -873,6 +859,7 @@ for opt do debug_tcg="yes" debug="yes" strip_opt="no" + fortify_source="no" ;; --enable-sparse) sparse="yes" ;; @@ -880,10 +867,6 @@ for opt do ;; --disable-strip) strip_opt="no" ;; - --disable-vnc-tls) vnc_tls="no" - ;; - --enable-vnc-tls) vnc_tls="yes" - ;; --disable-vnc-sasl) vnc_sasl="no" ;; --enable-vnc-sasl) vnc_sasl="yes" @@ -918,6 +901,10 @@ for opt do ;; --enable-xen-pci-passthrough) xen_pci_passthrough="yes" ;; + --disable-xen-pv-domain-build) xen_pv_domain_build="no" + ;; + --enable-xen-pv-domain-build) xen_pv_domain_build="yes" + ;; --disable-brlapi) brlapi="no" ;; --enable-brlapi) brlapi="yes" @@ -956,7 +943,6 @@ for opt do ;; --enable-cocoa) cocoa="yes" ; - sdl="no" ; audio_drv_list="coreaudio `echo $audio_drv_list | sed s,coreaudio,,g`" ;; --disable-system) softmmu="no" @@ -976,10 +962,6 @@ for opt do ;; --enable-bsd-user) bsd_user="yes" ;; - --enable-guest-base) guest_base="yes" - ;; - --disable-guest-base) guest_base="no" - ;; --enable-pie) pie="yes" ;; --disable-pie) pie="no" @@ -1046,9 +1028,9 @@ for opt do ;; --enable-xfsctl) xfs="yes" ;; - --disable-smartcard-nss) smartcard_nss="no" + --disable-smartcard) smartcard="no" ;; - --enable-smartcard-nss) smartcard_nss="yes" + --enable-smartcard) smartcard="yes" ;; --disable-libusb) libusb="no" ;; @@ -1119,6 +1101,14 @@ for opt do ;; --enable-gnutls) gnutls="yes" ;; + --disable-nettle) nettle="no" + ;; + --enable-nettle) nettle="yes" + ;; + --disable-gcrypt) gcrypt="no" + ;; + --enable-gcrypt) gcrypt="yes" + ;; --enable-rdma) rdma="yes" ;; --disable-rdma) rdma="no" @@ -1129,6 +1119,10 @@ for opt do ;; --enable-vte) vte="yes" ;; + --disable-virglrenderer) virglrenderer="no" + ;; + --enable-virglrenderer) virglrenderer="yes" + ;; --disable-tpm) tpm="no" ;; --enable-tpm) tpm="yes" @@ -1149,6 +1143,10 @@ for opt do ;; --enable-tcmalloc) tcmalloc="yes" ;; + --disable-jemalloc) jemalloc="no" + ;; + --enable-jemalloc) jemalloc="yes" + ;; *) echo "ERROR: unknown option $opt" echo "Try '$0 --help' for more information" @@ -1163,18 +1161,14 @@ fi # Note that if the Python conditional here evaluates True we will exit # with status 1 which is a shell 'false' value. -if ! $python -c 'import sys; sys.exit(sys.version_info < (2,4) or sys.version_info >= (3,))'; then - error_exit "Cannot use '$python', Python 2.4 or later is required." \ +if ! $python -c 'import sys; sys.exit(sys.version_info < (2,6) or sys.version_info >= (3,))'; then + error_exit "Cannot use '$python', Python 2.6 or later is required." \ "Note that Python 3 or later is not yet supported." \ "Use --python=/path/to/python to specify a supported Python." fi -# The -B switch was added in Python 2.6. -# If it is supplied, compiled files are not written. -# Use it for Python versions which support it. -if $python -B -c 'import sys; sys.exit(0)' 2>/dev/null; then - python="$python -B" -fi +# Suppress writing compiled files +python="$python -B" case "$cpu" in ppc) @@ -1315,7 +1309,6 @@ disabled with --disable-FEATURE, default is enabled if available: user supported user emulation targets linux-user all linux usermode emulation targets bsd-user all BSD usermode emulation targets - guest-base GUEST_BASE support for usermode emulation targets docs build documentation guest-agent build the QEMU Guest Agent guest-agent-msi build guest agent Windows MSI installation package @@ -1326,6 +1319,8 @@ disabled with --disable-FEATURE, default is enabled if available: sparse sparse checker gnutls GNUTLS cryptography support + nettle nettle cryptography support + gcrypt libgcrypt cryptography support sdl SDL UI --with-sdlabi select preferred SDL ABI 1.2 or 2.0 gtk gtk UI @@ -1333,7 +1328,6 @@ disabled with --disable-FEATURE, default is enabled if available: vte vte support for the gtk UI curses curses UI vnc VNC UI support - vnc-tls TLS encryption for VNC server vnc-sasl SASL encryption for VNC server vnc-jpeg JPEG lossy compression for VNC server vnc-png PNG compression for VNC server @@ -1358,7 +1352,7 @@ disabled with --disable-FEATURE, default is enabled if available: rbd rados block device (rbd) libiscsi iscsi support libnfs nfs support - smartcard-nss smartcard nss support + smartcard smartcard support (libcacard) libusb libusb (for usb passthrough) usb-redir usb network redirection support lzo support of lzo compression library @@ -1374,6 +1368,7 @@ disabled with --disable-FEATURE, default is enabled if available: vhdx support for the Microsoft VHDX image format numa libnuma support tcmalloc tcmalloc support + jemalloc jemalloc support NOTE: The object files are built at the place where configure is launched EOF @@ -1411,6 +1406,9 @@ if compile_object ; then else error_exit "\"$cc\" either does not exist or does not work" fi +if ! compile_prog ; then + error_exit "\"$cc\" cannot build an executable (is your linker broken?)" +fi # Check that the C++ compiler exists and works with the C compiler if has $cxx; then @@ -1471,6 +1469,16 @@ for flag in $gcc_flags; do done if test "$stack_protector" != "no"; then + cat > $TMPC << EOF +int main(int argc, char *argv[]) +{ + char arr[64], *p = arr, *c = argv[0]; + while (*c) { + *p++ = *c++; + } + return 0; +} +EOF gcc_flags="-fstack-protector-strong -fstack-protector-all" sp_on=0 for flag in $gcc_flags; do @@ -1479,7 +1487,6 @@ if test "$stack_protector" != "no"; then if do_cc $QEMU_CFLAGS -Werror $flag -c -o $TMPO $TMPC && compile_prog "-Werror $flag" ""; then QEMU_CFLAGS="$QEMU_CFLAGS $flag" - LIBTOOLFLAGS="$LIBTOOLFLAGS -Wc,$flag" sp_on=1 break fi @@ -1574,32 +1581,6 @@ EOF fi fi -# check for broken gcc and libtool in RHEL5 -if test -n "$libtool" -a "$pie" != "no" ; then - cat > $TMPC < + # are not in fact redundant. +cat > $TMPC << EOF +#include +#include +#undef localtime_r +int main(void) { localtime_r(NULL, NULL); return 0; } +EOF + if compile_prog "" "" ; then + localtime_r="yes" + else + localtime_r="no" + fi +fi + ########################################## # pkg-config probe @@ -1751,6 +1778,21 @@ EOF fi ########################################## +# avx2 optimization requirement check + +cat > $TMPC << EOF +static void bar(void) {} +static void *bar_ifunc(void) {return (void*) bar;} +static void foo(void) __attribute__((ifunc("bar_ifunc"))); +int main(void) { foo(); return 0; } +EOF +if compile_prog "-mavx2" "" ; then + if readelf --syms $TMPE |grep "IFUNC.*foo" >/dev/null 2>&1; then + avx2_opt="yes" + fi +fi + +######################################### # zlib check if test "$zlib" != "no" ; then @@ -1827,16 +1869,37 @@ fi # libseccomp check if test "$seccomp" != "no" ; then - if test "$cpu" = "i386" || test "$cpu" = "x86_64" && - $pkg_config --atleast-version=2.1.1 libseccomp; then + case "$cpu" in + i386|x86_64) + libseccomp_minver="2.1.0" + ;; + mips) + libseccomp_minver="2.2.0" + ;; + arm|aarch64) + libseccomp_minver="2.2.3" + ;; + *) + libseccomp_minver="" + ;; + esac + + if test "$libseccomp_minver" != "" && + $pkg_config --atleast-version=$libseccomp_minver libseccomp ; then libs_softmmu="$libs_softmmu `$pkg_config --libs libseccomp`" QEMU_CFLAGS="$QEMU_CFLAGS `$pkg_config --cflags libseccomp`" - seccomp="yes" + seccomp="yes" else - if test "$seccomp" = "yes"; then - feature_not_found "libseccomp" "Install libseccomp devel >= 2.1.1" - fi - seccomp="no" + if test "$seccomp" = "yes" ; then + if test "$libseccomp_minver" != "" ; then + feature_not_found "libseccomp" \ + "Install libseccomp devel >= $libseccomp_minver" + else + feature_not_found "libseccomp" \ + "libseccomp is not supported for host cpu $cpu" + fi + fi + seccomp="no" fi fi ########################################## @@ -1844,6 +1907,7 @@ fi if test "$xen" != "no" ; then xen_libs="-lxenstore -lxenctrl -lxenguest" + xen_stable_libs="-lxenforeignmemory -lxengnttab -lxenevtchn" # First we test whether Xen headers and libraries are available. # If no, we are done and there is no Xen support. @@ -1866,30 +1930,72 @@ EOF # Xen unstable elif cat > $TMPC < #include +#include +#include +#include #include #include #if !defined(HVM_MAX_VCPUS) # error HVM_MAX_VCPUS not defined #endif int main(void) { - xc_interface *xc; + xc_interface *xc = NULL; + xenforeignmemory_handle *xfmem; + xenevtchn_handle *xe; + xengnttab_handle *xg; + xen_domain_handle_t handle; + xs_daemon_open(); + xc = xc_interface_open(0, 0, 0); xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); - xc_gnttab_open(NULL, 0); xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0); xc_hvm_inject_msi(xc, 0, 0xf0000000, 0x00000000); - xc_hvm_create_ioreq_server(xc, 0, 0, NULL); + xc_hvm_create_ioreq_server(xc, 0, HVM_IOREQSRV_BUFIOREQ_ATOMIC, NULL); + xc_domain_create(xc, 0, handle, 0, NULL, NULL); + + xfmem = xenforeignmemory_open(0, 0); + xenforeignmemory_map(xfmem, 0, 0, 0, 0, 0); + + xe = xenevtchn_open(0, 0); + xenevtchn_fd(xe); + + xg = xengnttab_open(0, 0); + xengnttab_map_grant_ref(xg, 0, 0, 0); + + return 0; +} +EOF + compile_prog "" "$xen_libs $xen_stable_libs" + then + xen_ctrl_version=471 + xen=yes + elif + cat > $TMPC < +#include +int main(void) { + xc_interface *xc = NULL; + xen_domain_handle_t handle; + xc_domain_create(xc, 0, handle, 0, NULL, NULL); return 0; } EOF compile_prog "" "$xen_libs" then - xen_ctrl_version=450 + xen_ctrl_version=470 xen=yes + # Xen 4.6 elif cat > $TMPC < @@ -1907,127 +2013,89 @@ int main(void) { xc_gnttab_open(NULL, 0); xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0); xc_hvm_inject_msi(xc, 0, 0xf0000000, 0x00000000); + xc_hvm_create_ioreq_server(xc, 0, HVM_IOREQSRV_BUFIOREQ_ATOMIC, NULL); + xc_reserved_device_memory_map(xc, 0, 0, 0, 0, NULL, 0); return 0; } EOF compile_prog "" "$xen_libs" then - xen_ctrl_version=420 + xen_ctrl_version=460 xen=yes + # Xen 4.5 elif cat > $TMPC < -#include +#include #include #include #if !defined(HVM_MAX_VCPUS) # error HVM_MAX_VCPUS not defined #endif int main(void) { + xc_interface *xc; xs_daemon_open(); - xc_interface_open(0, 0, 0); + xc = xc_interface_open(0, 0, 0); xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); xc_gnttab_open(NULL, 0); xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0); + xc_hvm_inject_msi(xc, 0, 0xf0000000, 0x00000000); + xc_hvm_create_ioreq_server(xc, 0, 0, NULL); return 0; } EOF compile_prog "" "$xen_libs" then - xen_ctrl_version=410 + xen_ctrl_version=450 xen=yes - # Xen 4.0.0 elif cat > $TMPC < -#include +#include #include #include #if !defined(HVM_MAX_VCPUS) # error HVM_MAX_VCPUS not defined #endif int main(void) { - struct xen_add_to_physmap xatp = { - .domid = 0, .space = XENMAPSPACE_gmfn, .idx = 0, .gpfn = 0, - }; - xs_daemon_open(); - xc_interface_open(); - xc_gnttab_open(); - xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); - xc_memory_op(0, XENMEM_add_to_physmap, &xatp); - return 0; -} -EOF - compile_prog "" "$xen_libs" - then - xen_ctrl_version=400 - xen=yes - - # Xen 3.4.0 - elif - cat > $TMPC < -#include -int main(void) { - struct xen_add_to_physmap xatp = { - .domid = 0, .space = XENMAPSPACE_gmfn, .idx = 0, .gpfn = 0, - }; - xs_daemon_open(); - xc_interface_open(); - xc_gnttab_open(); - xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); - xc_memory_op(0, XENMEM_add_to_physmap, &xatp); - return 0; -} -EOF - compile_prog "" "$xen_libs" - then - xen_ctrl_version=340 - xen=yes - - # Xen 3.3.0 - elif - cat > $TMPC < -#include -int main(void) { + xc_interface *xc; xs_daemon_open(); - xc_interface_open(); - xc_gnttab_open(); + xc = xc_interface_open(0, 0, 0); xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); + xc_gnttab_open(NULL, 0); + xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0); + xc_hvm_inject_msi(xc, 0, 0xf0000000, 0x00000000); return 0; } EOF compile_prog "" "$xen_libs" then - xen_ctrl_version=330 + xen_ctrl_version=420 xen=yes - # Xen version unsupported else if test "$xen" = "yes" ; then - feature_not_found "xen (unsupported version)" "Install supported xen (e.g. 4.0, 3.4, 3.3)" + feature_not_found "xen (unsupported version)" \ + "Install a supported xen (xen 4.2 or newer)" fi xen=no fi if test "$xen" = yes; then + if test $xen_ctrl_version -ge 471 ; then + libs_softmmu="$xen_stable_libs $libs_softmmu" + fi libs_softmmu="$xen_libs $libs_softmmu" fi fi if test "$xen_pci_passthrough" != "no"; then - if test "$xen" = "yes" && test "$linux" = "yes" && - test "$xen_ctrl_version" -ge 340; then + if test "$xen" = "yes" && test "$linux" = "yes"; then xen_pci_passthrough=yes else if test "$xen_pci_passthrough" = "yes"; then - if test "$xen_ctrl_version" -lt 340; then - error_exit "User requested feature Xen PCI Passthrough" \ - "This feature does not work with Xen 3.3" - fi error_exit "User requested feature Xen PCI Passthrough" \ " but this feature requires /sys from Linux" fi @@ -2035,19 +2103,10 @@ if test "$xen_pci_passthrough" != "no"; then fi fi -########################################## -# libtool probe - -if ! has $libtool; then - libtool= -fi - -# MacOSX ships with a libtool which isn't the GNU one; weed this -# out by checking whether libtool supports the --version switch -if test -n "$libtool"; then - if ! "$libtool" --version >/dev/null 2>&1; then - libtool= - fi +if test "$xen_pv_domain_build" = "yes" && + test "$xen" != "yes"; then + error_exit "User requested Xen PV domain builder support" \ + "which requires Xen support." fi ########################################## @@ -2149,6 +2208,13 @@ if test "$gnutls" != "no"; then gnutls_hash="no" fi + # gnutls_rnd requires >= 2.11.0 + if $pkg_config --exists "gnutls >= 2.11.0"; then + gnutls_rnd="yes" + else + gnutls_rnd="no" + fi + if $pkg_config --exists 'gnutls >= 3.0'; then gnutls_gcrypt=no gnutls_nettle=yes @@ -2176,25 +2242,96 @@ if test "$gnutls" != "no"; then else gnutls="no" gnutls_hash="no" + gnutls_rnd="no" fi else gnutls_hash="no" + gnutls_rnd="no" +fi + + +# If user didn't give a --disable/enable-gcrypt flag, +# then mark as disabled if user requested nettle +# explicitly, or if gnutls links to nettle +if test -z "$gcrypt" +then + if test "$nettle" = "yes" || test "$gnutls_nettle" = "yes" + then + gcrypt="no" + fi +fi + +# If user didn't give a --disable/enable-nettle flag, +# then mark as disabled if user requested gcrypt +# explicitly, or if gnutls links to gcrypt +if test -z "$nettle" +then + if test "$gcrypt" = "yes" || test "$gnutls_gcrypt" = "yes" + then + nettle="no" + fi fi -if test "$gnutls_gcrypt" != "no"; then - if has "libgcrypt-config"; then +has_libgcrypt_config() { + if ! has "libgcrypt-config" + then + return 1 + fi + + if test -n "$cross_prefix" + then + host=`libgcrypt-config --host` + if test "$host-" != $cross_prefix + then + return 1 + fi + fi + + return 0 +} + +if test "$gcrypt" != "no"; then + if has_libgcrypt_config; then gcrypt_cflags=`libgcrypt-config --cflags` gcrypt_libs=`libgcrypt-config --libs` + # Debian has remove -lgpg-error from libgcrypt-config + # as it "spreads unnecessary dependencies" which in + # turn breaks static builds... + if test "$static" = "yes" + then + gcrypt_libs="$gcrypt_libs -lgpg-error" + fi libs_softmmu="$gcrypt_libs $libs_softmmu" libs_tools="$gcrypt_libs $libs_tools" QEMU_CFLAGS="$QEMU_CFLAGS $gcrypt_cflags" + gcrypt="yes" + if test -z "$nettle"; then + nettle="no" + fi + + cat > $TMPC << EOF +#include +int main(void) { + gcry_kdf_derive(NULL, 0, GCRY_KDF_PBKDF2, + GCRY_MD_SHA256, + NULL, 0, 0, 0, NULL); + return 0; +} +EOF + if compile_prog "$gcrypt_cflags" "$gcrypt_libs" ; then + gcrypt_kdf=yes + fi else - feature_not_found "gcrypt" "Install gcrypt devel" + if test "$gcrypt" = "yes"; then + feature_not_found "gcrypt" "Install gcrypt devel" + else + gcrypt="no" + fi fi fi -if test "$gnutls_nettle" != "no"; then +if test "$nettle" != "no"; then if $pkg_config --exists "nettle"; then nettle_cflags=`$pkg_config --cflags nettle` nettle_libs=`$pkg_config --libs nettle` @@ -2202,11 +2339,53 @@ if test "$gnutls_nettle" != "no"; then libs_softmmu="$nettle_libs $libs_softmmu" libs_tools="$nettle_libs $libs_tools" QEMU_CFLAGS="$QEMU_CFLAGS $nettle_cflags" + nettle="yes" + + cat > $TMPC << EOF +#include +int main(void) { + pbkdf2_hmac_sha256(8, NULL, 1000, 8, NULL, 8, NULL); + return 0; +} +EOF + if compile_prog "$nettle_cflags" "$nettle_libs" ; then + nettle_kdf=yes + fi else - feature_not_found "nettle" "Install nettle devel" + if test "$nettle" = "yes"; then + feature_not_found "nettle" "Install nettle devel" + else + nettle="no" + fi fi fi +if test "$gcrypt" = "yes" && test "$nettle" = "yes" +then + error_exit "Only one of gcrypt & nettle can be enabled" +fi + +########################################## +# libtasn1 - only for the TLS creds/session test suite + +tasn1=yes +tasn1_cflags="" +tasn1_libs="" +if $pkg_config --exists "libtasn1"; then + tasn1_cflags=`$pkg_config --cflags libtasn1` + tasn1_libs=`$pkg_config --libs libtasn1` +else + tasn1=no +fi + + +########################################## +# getifaddrs (for tests/test-io-channel-socket ) + +have_ifaddrs_h=yes +if ! check_include "ifaddrs.h" ; then + have_ifaddrs_h=no +fi ########################################## # VTE probe @@ -2287,9 +2466,7 @@ EOF if test "$_sdlversion" -lt 121 ; then sdl_too_old=yes else - if test "$cocoa" = "no" ; then - sdl=yes - fi + sdl=yes fi # static link with sdl ? (note: sdl.pc's --static --libs is broken) @@ -2353,28 +2530,6 @@ EOF fi fi -########################################## -# VNC TLS/WS detection -if test "$vnc" = "yes" -a "$vnc_tls" != "no" ; then - cat > $TMPC < -int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; } -EOF - vnc_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null` - vnc_tls_libs=`$pkg_config --libs gnutls 2> /dev/null` - if compile_prog "$vnc_tls_cflags" "$vnc_tls_libs" ; then - if test "$vnc_tls" != "no" ; then - vnc_tls=yes - fi - libs_softmmu="$vnc_tls_libs $libs_softmmu" - QEMU_CFLAGS="$QEMU_CFLAGS $vnc_tls_cflags" - else - if test "$vnc_tls" = "yes" ; then - feature_not_found "vnc-tls" "Install gnutls devel" - fi - vnc_tls=no - fi -fi ########################################## # VNC SASL detection @@ -2697,7 +2852,7 @@ fi # curses probe if test "$curses" != "no" ; then if test "$mingw32" = "yes" ; then - curses_list="-lpdcurses" + curses_list="$($pkg_config --libs ncurses 2>/dev/null):-lpdcurses" else curses_list="$($pkg_config --libs ncurses 2>/dev/null):-lncurses:-lcurses" fi @@ -2795,6 +2950,30 @@ for i in $glib_modules; do fi done +# Sanity check that the current size_t matches the +# size that glib thinks it should be. This catches +# problems on multi-arch where people try to build +# 32-bit QEMU while pointing at 64-bit glib headers +cat > $TMPC < +#include + +#define QEMU_BUILD_BUG_ON(x) \ + typedef char qemu_build_bug_on[(x)?-1:1] __attribute__((unused)); + +int main(void) { + QEMU_BUILD_BUG_ON(sizeof(size_t) != GLIB_SIZEOF_SIZE_T); + return 0; +} +EOF + +if ! compile_prog "-Werror $CFLAGS" "$LIBS" ; then + error_exit "sizeof(size_t) doesn't match GLIB_SIZEOF_SIZE_T."\ + "You probably need to set PKG_CONFIG_LIBDIR"\ + "to point to the right pkg-config files for your"\ + "build target" +fi + # g_test_trap_subprocess added in 2.38. Used by some tests. glib_subprocess=yes if ! $pkg_config --atleast-version=2.38 glib-2.0; then @@ -3149,28 +3328,17 @@ fi libs_softmmu="$libs_softmmu $fdt_libs" ########################################## -# opengl probe (for sdl2, milkymist-tmu2) - -# GLX probe, used by milkymist-tmu2 -# this is temporary, code will be switched to egl mid-term. -cat > $TMPC << EOF -#include -#include -#include -int main(void) { glBegin(0); glXQueryVersion(0,0,0); return 0; } -EOF -if compile_prog "" "-lGL -lX11" ; then - have_glx=yes -else - have_glx=no -fi +# opengl probe (for sdl2, gtk, milkymist-tmu2) if test "$opengl" != "no" ; then - opengl_pkgs="gl glesv2 epoxy egl" - if $pkg_config $opengl_pkgs x11 && test "$have_glx" = "yes"; then + opengl_pkgs="epoxy libdrm gbm" + if $pkg_config $opengl_pkgs x11; then opengl_cflags="$($pkg_config --cflags $opengl_pkgs) $x11_cflags" opengl_libs="$($pkg_config --libs $opengl_pkgs) $x11_libs" opengl=yes + if test "$gtk" = "yes" && $pkg_config --exists "$gtkpackage >= 3.16"; then + gtk_gl="yes" + fi else if test "$opengl" = "yes" ; then feature_not_found "opengl" "Please install opengl (mesa) devel pkgs: $opengl_pkgs" @@ -3181,6 +3349,18 @@ if test "$opengl" != "no" ; then fi fi +if test "$opengl" = "yes"; then + cat > $TMPC << EOF +#include +#ifndef EGL_MESA_image_dma_buf_export +# error mesa/epoxy lacks support for dmabufs (mesa 10.6+) +#endif +int main(void) { return 0; } +EOF + if compile_prog "" "" ; then + opengl_dmabuf=yes + fi +fi ########################################## # archipelago probe @@ -3221,6 +3401,9 @@ if test "$glusterfs" != "no" ; then glusterfs="yes" glusterfs_cflags=`$pkg_config --cflags glusterfs-api` glusterfs_libs=`$pkg_config --libs glusterfs-api` + if $pkg_config --atleast-version=4 glusterfs-api; then + glusterfs_xlator_opt="yes" + fi if $pkg_config --atleast-version=5 glusterfs-api; then glusterfs_discard="yes" fi @@ -3361,6 +3544,11 @@ EOF fi fi +if test "$tcmalloc" = "yes" && test "$jemalloc" = "yes" ; then + echo "ERROR: tcmalloc && jemalloc can't be used at the same time" + exit 1 +fi + ########################################## # tcmalloc probe @@ -3377,6 +3565,22 @@ EOF fi fi +########################################## +# jemalloc probe + +if test "$jemalloc" = "yes" ; then + cat > $TMPC << EOF +#include +int main(void) { malloc(1); return 0; } +EOF + + if compile_prog "" "-ljemalloc" ; then + LIBS="-ljemalloc $LIBS" + else + feature_not_found "jemalloc" "install jemalloc devel" + fi +fi + ########################################## # signalfd probe signalfd="no" @@ -3405,6 +3609,22 @@ if compile_prog "" "" ; then eventfd=yes fi +# check if memfd is supported +memfd=no +cat > $TMPC << EOF +#include + +int main(void) +{ + return memfd_create("foo", MFD_ALLOW_SEALING); +} +EOF +if compile_prog "" "" ; then + memfd=yes +fi + + + # check for fallocate fallocate=no cat > $TMPC << EOF @@ -3760,34 +3980,20 @@ EOF fi fi -# check for libcacard for smartcard support +# check for smartcard support smartcard_cflags="" -# TODO - what's the minimal nss version we support? -if test "$smartcard_nss" != "no"; then - cat > $TMPC << EOF -#include -int main(void) { PK11_FreeSlot(0); return 0; } -EOF - # FIXME: do not include $glib_* in here - nss_libs="$($pkg_config --libs nss 2>/dev/null) $glib_libs" - nss_cflags="$($pkg_config --cflags nss 2>/dev/null) $glib_cflags" - test_cflags="$nss_cflags" - # The header files in nss < 3.13.3 have a bug which causes them to - # emit a warning. If we're going to compile QEMU with -Werror, then - # test that the headers don't have this bug. Otherwise we would pass - # the configure test but fail to compile QEMU later. - if test "$werror" = "yes"; then - test_cflags="-Werror $test_cflags" - fi - if test -n "$libtool" && - $pkg_config --atleast-version=3.12.8 nss && \ - compile_prog "$test_cflags" "$nss_libs"; then - smartcard_nss="yes" +if test "$smartcard" != "no"; then + if $pkg_config libcacard; then + libcacard_cflags=$($pkg_config --cflags libcacard) + libcacard_libs=$($pkg_config --libs libcacard) + QEMU_CFLAGS="$QEMU_CFLAGS $libcacard_cflags" + libs_softmmu="$libs_softmmu $libcacard_libs" + smartcard="yes" else - if test "$smartcard_nss" = "yes"; then - feature_not_found "nss" "Install nss devel >= 3.12.8" + if test "$smartcard" = "yes"; then + feature_not_found "smartcard" "Install libcacard devel" fi - smartcard_nss="no" + smartcard="no" fi fi @@ -3845,6 +4051,7 @@ EOF guest_agent_with_vss="yes" QEMU_CFLAGS="$QEMU_CFLAGS $vss_win32_include" libs_qga="-lole32 -loleaut32 -lshlwapi -luuid -lstdc++ -Wl,--enable-stdcall-fixup $libs_qga" + qga_vss_provider="qga/vss-win32/qga-vss.dll qga/vss-win32/qga-vss.tlb" else if test "$vss_win32_sdk" != "" ; then echo "ERROR: Please download and install Microsoft VSS SDK:" @@ -3899,55 +4106,24 @@ EOF fi ########################################## -# Guest agent Window MSI package +# virgl renderer probe -if test "$guest_agent" != yes; then - if test "$guest_agent_msi" = yes; then - error_exit "MSI guest agent package requires guest agent enabled" - fi - guest_agent_msi=no -elif test "$mingw32" != "yes"; then - if test "$guest_agent_msi" = "yes"; then - error_exit "MSI guest agent package is available only for MinGW Windows cross-compilation" - fi - guest_agent_msi=no -elif ! has wixl; then - if test "$guest_agent_msi" = "yes"; then - error_exit "MSI guest agent package requires wixl tool installed ( usually from msitools package )" +if test "$virglrenderer" != "no" ; then + cat > $TMPC << EOF +#include +int main(void) { virgl_renderer_poll(); return 0; } +EOF + virgl_cflags=$($pkg_config --cflags virglrenderer 2>/dev/null) + virgl_libs=$($pkg_config --libs virglrenderer 2>/dev/null) + if $pkg_config virglrenderer >/dev/null 2>&1 && \ + compile_prog "$virgl_cflags" "$virgl_libs" ; then + virglrenderer="yes" + else + if test "$virglrenderer" = "yes" ; then + feature_not_found "virglrenderer" + fi + virglrenderer="no" fi - guest_agent_msi=no -fi - -if test "$guest_agent_msi" != "no"; then - if test "$guest_agent_with_vss" = "yes"; then - QEMU_GA_MSI_WITH_VSS="-D InstallVss" - fi - - if test "$QEMU_GA_MANUFACTURER" = ""; then - QEMU_GA_MANUFACTURER=QEMU - fi - - if test "$QEMU_GA_DISTRO" = ""; then - QEMU_GA_DISTRO=Linux - fi - - if test "$QEMU_GA_VERSION" = ""; then - QEMU_GA_VERSION=`cat $source_path/VERSION` - fi - - QEMU_GA_MSI_MINGW_DLL_PATH="-D Mingw_dlls=`$pkg_config --variable=prefix glib-2.0`/bin" - - case "$cpu" in - x86_64) - QEMU_GA_MSI_ARCH="-a x64 -D Arch=64" - ;; - i386) - QEMU_GA_MSI_ARCH="-D Arch=32" - ;; - *) - error_exit "CPU $cpu not supported for building installation package" - ;; - esac fi ########################################## @@ -4279,6 +4455,7 @@ fi # check if ccache is interfering with # semantic analysis of macros +unset CCACHE_CPP2 ccache_cpp2=no cat > $TMPC << EOF static const int Z = 1; @@ -4302,6 +4479,35 @@ if ! compile_object "-Werror"; then ccache_cpp2=yes fi +################################################# +# clang does not support glibc + FORTIFY_SOURCE. + +if test "$fortify_source" != "no"; then + if echo | $cc -dM -E - | grep __clang__ > /dev/null 2>&1 ; then + fortify_source="no"; + elif test -n "$cxx" && + echo | $cxx -dM -E - | grep __clang__ >/dev/null 2>&1 ; then + fortify_source="no"; + else + fortify_source="yes" + fi +fi + +########################################## +# check if struct fsxattr is available via linux/fs.h + +have_fsxattr=no +cat > $TMPC << EOF +#include +struct fsxattr foo; +int main(void) { + return 0; +} +EOF +if compile_prog "" "" ; then + have_fsxattr=yes +fi + ########################################## # End of CC checks # After here, no more $cc or $ld runs @@ -4309,8 +4515,10 @@ fi if test "$gcov" = "yes" ; then CFLAGS="-fprofile-arcs -ftest-coverage -g $CFLAGS" LDFLAGS="-fprofile-arcs -ftest-coverage $LDFLAGS" -elif test "$debug" = "no" ; then +elif test "$fortify_source" = "yes" ; then CFLAGS="-O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 $CFLAGS" +elif test "$debug" = "no"; then + CFLAGS="-O2 $CFLAGS" fi ########################################## @@ -4375,6 +4583,7 @@ if test "$want_tools" = "yes" ; then tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools" if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then tools="qemu-nbd\$(EXESUF) $tools" + tools="ivshmem-client\$(EXESUF) ivshmem-server\$(EXESUF) $tools" fi fi if test "$softmmu" = yes ; then @@ -4390,12 +4599,12 @@ if test "$softmmu" = yes ; then fi fi fi + +# Probe for guest agent support/options + if [ "$guest_agent" != "no" ]; then if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" -o "$mingw32" = "yes" ] ; then - tools="qemu-ga\$(EXESUF) $tools" - if [ "$mingw32" = "yes" -a "$guest_agent_with_vss" = "yes" ]; then - tools="qga/vss-win32/qga-vss.dll qga/vss-win32/qga-vss.tlb $tools" - fi + tools="qemu-ga $tools" guest_agent=yes elif [ "$guest_agent" != yes ]; then guest_agent=no @@ -4404,6 +4613,63 @@ if [ "$guest_agent" != "no" ]; then fi fi +# Guest agent Window MSI package + +if test "$guest_agent" != yes; then + if test "$guest_agent_msi" = yes; then + error_exit "MSI guest agent package requires guest agent enabled" + fi + guest_agent_msi=no +elif test "$mingw32" != "yes"; then + if test "$guest_agent_msi" = "yes"; then + error_exit "MSI guest agent package is available only for MinGW Windows cross-compilation" + fi + guest_agent_msi=no +elif ! has wixl; then + if test "$guest_agent_msi" = "yes"; then + error_exit "MSI guest agent package requires wixl tool installed ( usually from msitools package )" + fi + guest_agent_msi=no +else + # we support qemu-ga, mingw32, and wixl: default to MSI enabled if it wasn't + # disabled explicitly + if test "$guest_agent_msi" != "no"; then + guest_agent_msi=yes + fi +fi + +if test "$guest_agent_msi" = "yes"; then + if test "$guest_agent_with_vss" = "yes"; then + QEMU_GA_MSI_WITH_VSS="-D InstallVss" + fi + + if test "$QEMU_GA_MANUFACTURER" = ""; then + QEMU_GA_MANUFACTURER=QEMU + fi + + if test "$QEMU_GA_DISTRO" = ""; then + QEMU_GA_DISTRO=Linux + fi + + if test "$QEMU_GA_VERSION" = ""; then + QEMU_GA_VERSION=`cat $source_path/VERSION` + fi + + QEMU_GA_MSI_MINGW_DLL_PATH="-D Mingw_dlls=`$pkg_config --variable=prefix glib-2.0`/bin" + + case "$cpu" in + x86_64) + QEMU_GA_MSI_ARCH="-a x64 -D Arch=64" + ;; + i386) + QEMU_GA_MSI_ARCH="-D Arch=32" + ;; + *) + error_exit "CPU $cpu not supported for building installation package" + ;; + esac +fi + # Mac OS X ships with a broken assembler roms= if test \( "$cpu" = "i386" -o "$cpu" = "x86_64" \) -a \ @@ -4503,12 +4769,22 @@ fi echo "pixman $pixman" echo "SDL support $sdl" echo "GTK support $gtk" +echo "GTK GL support $gtk_gl" echo "GNUTLS support $gnutls" echo "GNUTLS hash $gnutls_hash" -echo "GNUTLS gcrypt $gnutls_gcrypt" -echo "GNUTLS nettle $gnutls_nettle ${gnutls_nettle+($nettle_version)}" +echo "GNUTLS rnd $gnutls_rnd" +echo "libgcrypt $gcrypt" +echo "libgcrypt kdf $gcrypt_kdf" +if test "$nettle" = "yes"; then + echo "nettle $nettle ($nettle_version)" +else + echo "nettle $nettle" +fi +echo "nettle kdf $nettle_kdf" +echo "libtasn1 $tasn1" echo "VTE support $vte" echo "curses support $curses" +echo "virgl support $virglrenderer" echo "curl support $curl" echo "mingw32 support $mingw32" echo "Audio drivers $audio_drv_list" @@ -4517,7 +4793,6 @@ echo "Block whitelist (ro) $block_drv_ro_whitelist" echo "VirtFS support $virtfs" echo "VNC support $vnc" if test "$vnc" = "yes" ; then - echo "VNC TLS support $vnc_tls" echo "VNC SASL support $vnc_sasl" echo "VNC JPEG support $vnc_jpeg" echo "VNC PNG support $vnc_png" @@ -4528,11 +4803,11 @@ fi echo "xen support $xen" if test "$xen" = "yes" ; then echo "xen ctrl version $xen_ctrl_version" + echo "pv dom build $xen_pv_domain_build" fi echo "brlapi support $brlapi" echo "bluez support $bluez" echo "Documentation $docs" -echo "GUEST_BASE $guest_base" echo "PIE $pie" echo "vde support $vde" echo "netmap support $netmap" @@ -4553,7 +4828,7 @@ echo "libcap-ng support $cap_ng" echo "vhost-net support $vhost_net" echo "vhost-scsi support $vhost_scsi" echo "Trace backends $trace_backends" -if test "$trace_backend" = "simple"; then +if have_backend "simple"; then echo "Trace output file $trace_file-" fi if test "$spice" = "yes"; then @@ -4563,15 +4838,17 @@ echo "spice support $spice" fi echo "rbd support $rbd" echo "xfsctl support $xfs" -echo "nss used $smartcard_nss" +echo "smartcard support $smartcard" echo "libusb $libusb" echo "usb net redir $usb_redir" echo "OpenGL support $opengl" +echo "OpenGL dmabufs $opengl_dmabuf" echo "libiscsi support $libiscsi" echo "libnfs support $libnfs" echo "build guest agent $guest_agent" echo "QGA VSS support $guest_agent_with_vss" echo "QGA w32 disk info $guest_agent_ntddscsi" +echo "QGA MSI support $guest_agent_msi" echo "seccomp support $seccomp" echo "coroutine backend $coroutine" echo "coroutine pool $coroutine_pool" @@ -4589,6 +4866,8 @@ echo "snappy support $snappy" echo "bzip2 support $bzip2" echo "NUMA host support $numa" echo "tcmalloc support $tcmalloc" +echo "jemalloc support $jemalloc" +echo "avx2 optimization $avx2_opt" if test "$sdl_too_old" = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -4646,12 +4925,13 @@ if test "$mingw32" = "yes" ; then echo "CONFIG_PRODUCTVERSION=$version_major,$version_minor,$version_subminor,$version_micro" >> $config_host_mak if test "$guest_agent_with_vss" = "yes" ; then echo "CONFIG_QGA_VSS=y" >> $config_host_mak + echo "QGA_VSS_PROVIDER=$qga_vss_provider" >> $config_host_mak echo "WIN_SDK=\"$win_sdk\"" >> $config_host_mak fi if test "$guest_agent_ntddscsi" = "yes" ; then echo "CONFIG_QGA_NTDDDISK=y" >> $config_host_mak fi - if test "$guest_agent_msi" != "no"; then + if test "$guest_agent_msi" = "yes"; then echo "QEMU_GA_MSI_ENABLED=yes" >> $config_host_mak echo "QEMU_GA_MSI_MINGW_DLL_PATH=${QEMU_GA_MSI_MINGW_DLL_PATH}" >> $config_host_mak echo "QEMU_GA_MSI_WITH_VSS=${QEMU_GA_MSI_WITH_VSS}" >> $config_host_mak @@ -4724,9 +5004,6 @@ echo "CONFIG_BDRV_RO_WHITELIST=$block_drv_ro_whitelist" >> $config_host_mak if test "$vnc" = "yes" ; then echo "CONFIG_VNC=y" >> $config_host_mak fi -if test "$vnc_tls" = "yes" ; then - echo "CONFIG_VNC_TLS=y" >> $config_host_mak -fi if test "$vnc_sasl" = "yes" ; then echo "CONFIG_VNC_SASL=y" >> $config_host_mak fi @@ -4785,6 +5062,9 @@ fi if test "$eventfd" = "yes" ; then echo "CONFIG_EVENTFD=y" >> $config_host_mak fi +if test "$memfd" = "yes" ; then + echo "CONFIG_MEMFD=y" >> $config_host_mak +fi if test "$fallocate" = "yes" ; then echo "CONFIG_FALLOCATE=y" >> $config_host_mak fi @@ -4862,6 +5142,10 @@ if test "$gtk" = "yes" ; then echo "CONFIG_GTK=y" >> $config_host_mak echo "CONFIG_GTKABI=$gtkabi" >> $config_host_mak echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak + echo "GTK_LIBS=$gtk_libs" >> $config_host_mak + if test "$gtk_gl" = "yes" ; then + echo "CONFIG_GTK_GL=y" >> $config_host_mak + fi fi if test "$gnutls" = "yes" ; then echo "CONFIG_GNUTLS=y" >> $config_host_mak @@ -4869,20 +5153,51 @@ fi if test "$gnutls_hash" = "yes" ; then echo "CONFIG_GNUTLS_HASH=y" >> $config_host_mak fi -if test "$gnutls_gcrypt" = "yes" ; then - echo "CONFIG_GNUTLS_GCRYPT=y" >> $config_host_mak +if test "$gnutls_rnd" = "yes" ; then + echo "CONFIG_GNUTLS_RND=y" >> $config_host_mak +fi +if test "$gcrypt" = "yes" ; then + echo "CONFIG_GCRYPT=y" >> $config_host_mak + if test "$gcrypt_kdf" = "yes" ; then + echo "CONFIG_GCRYPT_KDF=y" >> $config_host_mak + fi fi -if test "$gnutls_nettle" = "yes" ; then - echo "CONFIG_GNUTLS_NETTLE=y" >> $config_host_mak +if test "$nettle" = "yes" ; then + echo "CONFIG_NETTLE=y" >> $config_host_mak echo "CONFIG_NETTLE_VERSION_MAJOR=${nettle_version%%.*}" >> $config_host_mak + if test "$nettle_kdf" = "yes" ; then + echo "CONFIG_NETTLE_KDF=y" >> $config_host_mak + fi +fi +if test "$tasn1" = "yes" ; then + echo "CONFIG_TASN1=y" >> $config_host_mak +fi +if test "$have_ifaddrs_h" = "yes" ; then + echo "HAVE_IFADDRS_H=y" >> $config_host_mak +fi + +# Work around a system header bug with some kernel/XFS header +# versions where they both try to define 'struct fsxattr': +# xfs headers will not try to redefine structs from linux headers +# if this macro is set. +if test "$have_fsxattr" = "yes" ; then + echo "HAVE_FSXATTR=y" >> $config_host_mak fi if test "$vte" = "yes" ; then echo "CONFIG_VTE=y" >> $config_host_mak echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak fi +if test "$virglrenderer" = "yes" ; then + echo "CONFIG_VIRGL=y" >> $config_host_mak + echo "VIRGL_CFLAGS=$virgl_cflags" >> $config_host_mak + echo "VIRGL_LIBS=$virgl_libs" >> $config_host_mak +fi if test "$xen" = "yes" ; then echo "CONFIG_XEN_BACKEND=y" >> $config_host_mak echo "CONFIG_XEN_CTRL_INTERFACE_VERSION=$xen_ctrl_version" >> $config_host_mak + if test "$xen_pv_domain_build" = "yes" ; then + echo "CONFIG_XEN_PV_DOMAIN_BUILD=y" >> $config_host_mak + fi fi if test "$linux_aio" = "yes" ; then echo "CONFIG_LINUX_AIO=y" >> $config_host_mak @@ -4937,10 +5252,8 @@ if test "$spice" = "yes" ; then echo "CONFIG_SPICE=y" >> $config_host_mak fi -if test "$smartcard_nss" = "yes" ; then - echo "CONFIG_SMARTCARD_NSS=y" >> $config_host_mak - echo "NSS_LIBS=$nss_libs" >> $config_host_mak - echo "NSS_CFLAGS=$nss_cflags" >> $config_host_mak +if test "$smartcard" = "yes" ; then + echo "CONFIG_SMARTCARD=y" >> $config_host_mak fi if test "$libusb" = "yes" ; then @@ -4955,6 +5268,13 @@ if test "$opengl" = "yes" ; then echo "CONFIG_OPENGL=y" >> $config_host_mak echo "OPENGL_CFLAGS=$opengl_cflags" >> $config_host_mak echo "OPENGL_LIBS=$opengl_libs" >> $config_host_mak + if test "$opengl_dmabuf" = "yes" ; then + echo "CONFIG_OPENGL_DMABUF=y" >> $config_host_mak + fi +fi + +if test "$avx2_opt" = "yes" ; then + echo "CONFIG_AVX2_OPT=y" >> $config_host_mak fi if test "$lzo" = "yes" ; then @@ -4992,6 +5312,9 @@ fi if test "$zero_malloc" = "yes" ; then echo "CONFIG_ZERO_MALLOC=y" >> $config_host_mak fi +if test "$localtime_r" = "yes" ; then + echo "CONFIG_LOCALTIME_R=y" >> $config_host_mak +fi if test "$qom_cast_debug" = "yes" ; then echo "CONFIG_QOM_CAST_DEBUG=y" >> $config_host_mak fi @@ -5046,6 +5369,10 @@ if test "$glusterfs" = "yes" ; then echo "GLUSTERFS_LIBS=$glusterfs_libs" >> $config_host_mak fi +if test "$glusterfs_xlator_opt" = "yes" ; then + echo "CONFIG_GLUSTERFS_XLATOR_OPT=y" >> $config_host_mak +fi + if test "$glusterfs_discard" = "yes" ; then echo "CONFIG_GLUSTERFS_DISCARD=y" >> $config_host_mak fi @@ -5093,8 +5420,8 @@ if have_backend "simple"; then # Set the appropriate trace file. trace_file="\"$trace_file-\" FMT_pid" fi -if have_backend "stderr"; then - echo "CONFIG_TRACE_STDERR=y" >> $config_host_mak +if have_backend "log"; then + echo "CONFIG_TRACE_LOG=y" >> $config_host_mak fi if have_backend "ust"; then echo "CONFIG_TRACE_UST=y" >> $config_host_mak @@ -5149,13 +5476,8 @@ echo "MAKE=$make" >> $config_host_mak echo "INSTALL=$install" >> $config_host_mak echo "INSTALL_DIR=$install -d -m 0755" >> $config_host_mak echo "INSTALL_DATA=$install -c -m 0644" >> $config_host_mak -if test -n "$libtool"; then - echo "INSTALL_PROG=\$(LIBTOOL) --mode=install $install -c -m 0755" >> $config_host_mak - echo "INSTALL_LIB=\$(LIBTOOL) --mode=install $install -c -m 0644" >> $config_host_mak -else - echo "INSTALL_PROG=$install -c -m 0755" >> $config_host_mak - echo "INSTALL_LIB=$install -c -m 0644" >> $config_host_mak -fi +echo "INSTALL_PROG=$install -c -m 0755" >> $config_host_mak +echo "INSTALL_LIB=$install -c -m 0644" >> $config_host_mak echo "PYTHON=$python" >> $config_host_mak echo "CC=$cc" >> $config_host_mak if $iasl -h > /dev/null 2>&1; then @@ -5173,7 +5495,6 @@ echo "OBJCOPY=$objcopy" >> $config_host_mak echo "LD=$ld" >> $config_host_mak echo "NM=$nm" >> $config_host_mak echo "WINDRES=$windres" >> $config_host_mak -echo "LIBTOOL=$libtool" >> $config_host_mak echo "CFLAGS=$CFLAGS" >> $config_host_mak echo "CFLAGS_NOPIE=$CFLAGS_NOPIE" >> $config_host_mak echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak @@ -5192,13 +5513,14 @@ else fi echo "LDFLAGS=$LDFLAGS" >> $config_host_mak echo "LDFLAGS_NOPIE=$LDFLAGS_NOPIE" >> $config_host_mak -echo "LIBTOOLFLAGS=$LIBTOOLFLAGS" >> $config_host_mak echo "LIBS+=$LIBS" >> $config_host_mak echo "LIBS_TOOLS+=$libs_tools" >> $config_host_mak echo "EXESUF=$EXESUF" >> $config_host_mak echo "DSOSUF=$DSOSUF" >> $config_host_mak echo "LDFLAGS_SHARED=$LDFLAGS_SHARED" >> $config_host_mak echo "LIBS_QGA+=$libs_qga" >> $config_host_mak +echo "TASN1_LIBS=$tasn1_libs" >> $config_host_mak +echo "TASN1_CFLAGS=$tasn1_cflags" >> $config_host_mak echo "POD2MAN=$POD2MAN" >> $config_host_mak echo "TRANSLATE_OPT_CFLAGS=$TRANSLATE_OPT_CFLAGS" >> $config_host_mak if test "$gcov" = "yes" ; then @@ -5348,20 +5670,20 @@ case "$target_name" in ppc64) TARGET_BASE_ARCH=ppc TARGET_ABI_DIR=ppc - gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml" + gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml" ;; ppc64le) TARGET_ARCH=ppc64 TARGET_BASE_ARCH=ppc TARGET_ABI_DIR=ppc - gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml" + gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml" ;; ppc64abi32) TARGET_ARCH=ppc64 TARGET_BASE_ARCH=ppc TARGET_ABI_DIR=ppc echo "TARGET_ABI32=y" >> $config_target_mak - gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml" + gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml" ;; sh4|sh4eb) TARGET_ARCH=sh4 @@ -5379,7 +5701,9 @@ case "$target_name" in echo "TARGET_ABI32=y" >> $config_target_mak ;; s390x) - gdb_xml_files="s390x-core64.xml s390-acr.xml s390-fpr.xml s390-vx.xml" + gdb_xml_files="s390x-core64.xml s390-acr.xml s390-fpr.xml s390-vx.xml s390-cr.xml s390-virt.xml" + ;; + tilegx) ;; tricore) ;; @@ -5442,6 +5766,7 @@ case "$target_name" in echo "CONFIG_KVM=y" >> $config_target_mak if test "$vhost_net" = "yes" ; then echo "CONFIG_VHOST_NET=y" >> $config_target_mak + echo "CONFIG_VHOST_NET_TEST_$target_name=y" >> $config_host_mak fi fi esac @@ -5469,9 +5794,6 @@ fi if test "$target_user_only" = "yes" -a "$bflt" = "yes"; then echo "TARGET_HAS_BFLT=y" >> $config_target_mak fi -if test "$target_user_only" = "yes" -a "$guest_base" = "yes"; then - echo "CONFIG_USE_GUEST_BASE=y" >> $config_target_mak -fi if test "$target_bsd_user" = "yes" ; then echo "CONFIG_BSD_USER=y" >> $config_target_mak fi @@ -5481,91 +5803,76 @@ fi cflags="" ldflags="" +disas_config() { + echo "CONFIG_${1}_DIS=y" >> $config_target_mak + echo "CONFIG_${1}_DIS=y" >> config-all-disas.mak +} + for i in $ARCH $TARGET_BASE_ARCH ; do case "$i" in alpha) - echo "CONFIG_ALPHA_DIS=y" >> $config_target_mak - echo "CONFIG_ALPHA_DIS=y" >> config-all-disas.mak + disas_config "ALPHA" ;; aarch64) if test -n "${cxx}"; then - echo "CONFIG_ARM_A64_DIS=y" >> $config_target_mak - echo "CONFIG_ARM_A64_DIS=y" >> config-all-disas.mak + disas_config "ARM_A64" fi ;; arm) - echo "CONFIG_ARM_DIS=y" >> $config_target_mak - echo "CONFIG_ARM_DIS=y" >> config-all-disas.mak + disas_config "ARM" if test -n "${cxx}"; then - echo "CONFIG_ARM_A64_DIS=y" >> $config_target_mak - echo "CONFIG_ARM_A64_DIS=y" >> config-all-disas.mak + disas_config "ARM_A64" fi ;; cris) - echo "CONFIG_CRIS_DIS=y" >> $config_target_mak - echo "CONFIG_CRIS_DIS=y" >> config-all-disas.mak + disas_config "CRIS" ;; hppa) - echo "CONFIG_HPPA_DIS=y" >> $config_target_mak - echo "CONFIG_HPPA_DIS=y" >> config-all-disas.mak + disas_config "HPPA" ;; i386|x86_64|x32) - echo "CONFIG_I386_DIS=y" >> $config_target_mak - echo "CONFIG_I386_DIS=y" >> config-all-disas.mak + disas_config "I386" ;; ia64*) - echo "CONFIG_IA64_DIS=y" >> $config_target_mak - echo "CONFIG_IA64_DIS=y" >> config-all-disas.mak + disas_config "IA64" ;; lm32) - echo "CONFIG_LM32_DIS=y" >> $config_target_mak - echo "CONFIG_LM32_DIS=y" >> config-all-disas.mak + disas_config "LM32" ;; m68k) - echo "CONFIG_M68K_DIS=y" >> $config_target_mak - echo "CONFIG_M68K_DIS=y" >> config-all-disas.mak + disas_config "M68K" ;; microblaze*) - echo "CONFIG_MICROBLAZE_DIS=y" >> $config_target_mak - echo "CONFIG_MICROBLAZE_DIS=y" >> config-all-disas.mak + disas_config "MICROBLAZE" ;; mips*) - echo "CONFIG_MIPS_DIS=y" >> $config_target_mak - echo "CONFIG_MIPS_DIS=y" >> config-all-disas.mak + disas_config "MIPS" ;; moxie*) - echo "CONFIG_MOXIE_DIS=y" >> $config_target_mak - echo "CONFIG_MOXIE_DIS=y" >> config-all-disas.mak + disas_config "MOXIE" ;; or32) - echo "CONFIG_OPENRISC_DIS=y" >> $config_target_mak - echo "CONFIG_OPENRISC_DIS=y" >> config-all-disas.mak + disas_config "OPENRISC" ;; ppc*) - echo "CONFIG_PPC_DIS=y" >> $config_target_mak - echo "CONFIG_PPC_DIS=y" >> config-all-disas.mak + disas_config "PPC" ;; s390*) - echo "CONFIG_S390_DIS=y" >> $config_target_mak - echo "CONFIG_S390_DIS=y" >> config-all-disas.mak + disas_config "S390" ;; sh4) - echo "CONFIG_SH4_DIS=y" >> $config_target_mak - echo "CONFIG_SH4_DIS=y" >> config-all-disas.mak + disas_config "SH4" ;; sparc*) - echo "CONFIG_SPARC_DIS=y" >> $config_target_mak - echo "CONFIG_SPARC_DIS=y" >> config-all-disas.mak + disas_config "SPARC" ;; xtensa*) - echo "CONFIG_XTENSA_DIS=y" >> $config_target_mak - echo "CONFIG_XTENSA_DIS=y" >> config-all-disas.mak + disas_config "XTENSA" ;; esac done if test "$tcg_interpreter" = "yes" ; then - echo "CONFIG_TCI_DIS=y" >> $config_target_mak - echo "CONFIG_TCI_DIS=y" >> config-all-disas.mak + disas_config "TCI" fi case "$ARCH" in @@ -5600,10 +5907,6 @@ if [ "$pixman" = "internal" ]; then echo "config-host.h: subdir-pixman" >> $config_host_mak fi -if test "$rdma" = "yes" ; then -echo "CONFIG_RDMA=y" >> $config_host_mak -fi - if [ "$dtc_internal" = "yes" ]; then echo "config-host.h: subdir-dtc" >> $config_host_mak fi @@ -5689,7 +5992,7 @@ cat <config.status EOD printf "exec" >>config.status printf " '%s'" "$0" "$@" >>config.status -echo >>config.status +echo ' "$@"' >>config.status chmod +x config.status rm -r "$TMPDIR1" diff --git a/qemu/contrib/ivshmem-client/Makefile.objs b/qemu/contrib/ivshmem-client/Makefile.objs new file mode 100644 index 000000000..bfab2d20d --- /dev/null +++ b/qemu/contrib/ivshmem-client/Makefile.objs @@ -0,0 +1 @@ +ivshmem-client-obj-y = ivshmem-client.o main.o diff --git a/qemu/contrib/ivshmem-client/ivshmem-client.c b/qemu/contrib/ivshmem-client/ivshmem-client.c new file mode 100644 index 000000000..44ae3646e --- /dev/null +++ b/qemu/contrib/ivshmem-client/ivshmem-client.c @@ -0,0 +1,446 @@ +/* + * Copyright 6WIND S.A., 2014 + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" +#include +#include + +#include "qemu-common.h" +#include "qemu/queue.h" + +#include "ivshmem-client.h" + +/* log a message on stdout if verbose=1 */ +#define IVSHMEM_CLIENT_DEBUG(client, fmt, ...) do { \ + if ((client)->verbose) { \ + printf(fmt, ## __VA_ARGS__); \ + } \ + } while (0) + +/* read message from the unix socket */ +static int +ivshmem_client_read_one_msg(IvshmemClient *client, int64_t *index, int *fd) +{ + int ret; + struct msghdr msg; + struct iovec iov[1]; + union { + struct cmsghdr cmsg; + char control[CMSG_SPACE(sizeof(int))]; + } msg_control; + struct cmsghdr *cmsg; + + iov[0].iov_base = index; + iov[0].iov_len = sizeof(*index); + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = &msg_control; + msg.msg_controllen = sizeof(msg_control); + + ret = recvmsg(client->sock_fd, &msg, 0); + if (ret < sizeof(*index)) { + IVSHMEM_CLIENT_DEBUG(client, "cannot read message: %s\n", + strerror(errno)); + return -1; + } + if (ret == 0) { + IVSHMEM_CLIENT_DEBUG(client, "lost connection to server\n"); + return -1; + } + + *index = GINT64_FROM_LE(*index); + *fd = -1; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + + if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || + cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS) { + continue; + } + + memcpy(fd, CMSG_DATA(cmsg), sizeof(*fd)); + } + + return 0; +} + +/* free a peer when the server advertises a disconnection or when the + * client is freed */ +static void +ivshmem_client_free_peer(IvshmemClient *client, IvshmemClientPeer *peer) +{ + unsigned vector; + + QTAILQ_REMOVE(&client->peer_list, peer, next); + for (vector = 0; vector < peer->vectors_count; vector++) { + close(peer->vectors[vector]); + } + + g_free(peer); +} + +/* handle message coming from server (new peer, new vectors) */ +static int +ivshmem_client_handle_server_msg(IvshmemClient *client) +{ + IvshmemClientPeer *peer; + int64_t peer_id; + int ret, fd; + + ret = ivshmem_client_read_one_msg(client, &peer_id, &fd); + if (ret < 0) { + return -1; + } + + /* can return a peer or the local client */ + peer = ivshmem_client_search_peer(client, peer_id); + + /* delete peer */ + if (fd == -1) { + + if (peer == NULL || peer == &client->local) { + IVSHMEM_CLIENT_DEBUG(client, "receive delete for invalid " + "peer %" PRId64 "\n", peer_id); + return -1; + } + + IVSHMEM_CLIENT_DEBUG(client, "delete peer id = %" PRId64 "\n", peer_id); + ivshmem_client_free_peer(client, peer); + return 0; + } + + /* new peer */ + if (peer == NULL) { + peer = g_malloc0(sizeof(*peer)); + peer->id = peer_id; + peer->vectors_count = 0; + QTAILQ_INSERT_TAIL(&client->peer_list, peer, next); + IVSHMEM_CLIENT_DEBUG(client, "new peer id = %" PRId64 "\n", peer_id); + } + + /* new vector */ + IVSHMEM_CLIENT_DEBUG(client, " new vector %d (fd=%d) for peer id %" + PRId64 "\n", peer->vectors_count, fd, peer->id); + if (peer->vectors_count >= G_N_ELEMENTS(peer->vectors)) { + IVSHMEM_CLIENT_DEBUG(client, "Too many vectors received, failing"); + return -1; + } + + peer->vectors[peer->vectors_count] = fd; + peer->vectors_count++; + + return 0; +} + +/* init a new ivshmem client */ +int +ivshmem_client_init(IvshmemClient *client, const char *unix_sock_path, + IvshmemClientNotifCb notif_cb, void *notif_arg, + bool verbose) +{ + int ret; + unsigned i; + + memset(client, 0, sizeof(*client)); + + ret = snprintf(client->unix_sock_path, sizeof(client->unix_sock_path), + "%s", unix_sock_path); + + if (ret < 0 || ret >= sizeof(client->unix_sock_path)) { + IVSHMEM_CLIENT_DEBUG(client, "could not copy unix socket path\n"); + return -1; + } + + for (i = 0; i < IVSHMEM_CLIENT_MAX_VECTORS; i++) { + client->local.vectors[i] = -1; + } + + QTAILQ_INIT(&client->peer_list); + client->local.id = -1; + + client->notif_cb = notif_cb; + client->notif_arg = notif_arg; + client->verbose = verbose; + client->shm_fd = -1; + client->sock_fd = -1; + + return 0; +} + +/* create and connect to the unix socket */ +int +ivshmem_client_connect(IvshmemClient *client) +{ + struct sockaddr_un sun; + int fd, ret; + int64_t tmp; + + IVSHMEM_CLIENT_DEBUG(client, "connect to client %s\n", + client->unix_sock_path); + + client->sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (client->sock_fd < 0) { + IVSHMEM_CLIENT_DEBUG(client, "cannot create socket: %s\n", + strerror(errno)); + return -1; + } + + sun.sun_family = AF_UNIX; + ret = snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", + client->unix_sock_path); + if (ret < 0 || ret >= sizeof(sun.sun_path)) { + IVSHMEM_CLIENT_DEBUG(client, "could not copy unix socket path\n"); + goto err_close; + } + + if (connect(client->sock_fd, (struct sockaddr *)&sun, sizeof(sun)) < 0) { + IVSHMEM_CLIENT_DEBUG(client, "cannot connect to %s: %s\n", sun.sun_path, + strerror(errno)); + goto err_close; + } + + /* first, we expect a protocol version */ + if (ivshmem_client_read_one_msg(client, &tmp, &fd) < 0 || + (tmp != IVSHMEM_PROTOCOL_VERSION) || fd != -1) { + IVSHMEM_CLIENT_DEBUG(client, "cannot read from server\n"); + goto err_close; + } + + /* then, we expect our index + a fd == -1 */ + if (ivshmem_client_read_one_msg(client, &client->local.id, &fd) < 0 || + client->local.id < 0 || fd != -1) { + IVSHMEM_CLIENT_DEBUG(client, "cannot read from server (2)\n"); + goto err_close; + } + IVSHMEM_CLIENT_DEBUG(client, "our_id=%" PRId64 "\n", client->local.id); + + /* now, we expect shared mem fd + a -1 index, note that shm fd + * is not used */ + if (ivshmem_client_read_one_msg(client, &tmp, &fd) < 0 || + tmp != -1 || fd < 0) { + if (fd >= 0) { + close(fd); + } + IVSHMEM_CLIENT_DEBUG(client, "cannot read from server (3)\n"); + goto err_close; + } + client->shm_fd = fd; + IVSHMEM_CLIENT_DEBUG(client, "shm_fd=%d\n", fd); + + return 0; + +err_close: + close(client->sock_fd); + client->sock_fd = -1; + return -1; +} + +/* close connection to the server, and free all peer structures */ +void +ivshmem_client_close(IvshmemClient *client) +{ + IvshmemClientPeer *peer; + unsigned i; + + IVSHMEM_CLIENT_DEBUG(client, "close client\n"); + + while ((peer = QTAILQ_FIRST(&client->peer_list)) != NULL) { + ivshmem_client_free_peer(client, peer); + } + + close(client->shm_fd); + client->shm_fd = -1; + close(client->sock_fd); + client->sock_fd = -1; + client->local.id = -1; + for (i = 0; i < IVSHMEM_CLIENT_MAX_VECTORS; i++) { + close(client->local.vectors[i]); + client->local.vectors[i] = -1; + } + client->local.vectors_count = 0; +} + +/* get the fd_set according to the unix socket and peer list */ +void +ivshmem_client_get_fds(const IvshmemClient *client, fd_set *fds, int *maxfd) +{ + int fd; + unsigned vector; + + FD_SET(client->sock_fd, fds); + if (client->sock_fd >= *maxfd) { + *maxfd = client->sock_fd + 1; + } + + for (vector = 0; vector < client->local.vectors_count; vector++) { + fd = client->local.vectors[vector]; + FD_SET(fd, fds); + if (fd >= *maxfd) { + *maxfd = fd + 1; + } + } +} + +/* handle events from eventfd: just print a message on notification */ +static int +ivshmem_client_handle_event(IvshmemClient *client, const fd_set *cur, int maxfd) +{ + IvshmemClientPeer *peer; + uint64_t kick; + unsigned i; + int ret; + + peer = &client->local; + + for (i = 0; i < peer->vectors_count; i++) { + if (peer->vectors[i] >= maxfd || !FD_ISSET(peer->vectors[i], cur)) { + continue; + } + + ret = read(peer->vectors[i], &kick, sizeof(kick)); + if (ret < 0) { + return ret; + } + if (ret != sizeof(kick)) { + IVSHMEM_CLIENT_DEBUG(client, "invalid read size = %d\n", ret); + errno = EINVAL; + return -1; + } + IVSHMEM_CLIENT_DEBUG(client, "received event on fd %d vector %d: %" + PRIu64 "\n", peer->vectors[i], i, kick); + if (client->notif_cb != NULL) { + client->notif_cb(client, peer, i, client->notif_arg); + } + } + + return 0; +} + +/* read and handle new messages on the given fd_set */ +int +ivshmem_client_handle_fds(IvshmemClient *client, fd_set *fds, int maxfd) +{ + if (client->sock_fd < maxfd && FD_ISSET(client->sock_fd, fds) && + ivshmem_client_handle_server_msg(client) < 0 && errno != EINTR) { + IVSHMEM_CLIENT_DEBUG(client, "ivshmem_client_handle_server_msg() " + "failed\n"); + return -1; + } else if (ivshmem_client_handle_event(client, fds, maxfd) < 0 && + errno != EINTR) { + IVSHMEM_CLIENT_DEBUG(client, "ivshmem_client_handle_event() failed\n"); + return -1; + } + + return 0; +} + +/* send a notification on a vector of a peer */ +int +ivshmem_client_notify(const IvshmemClient *client, + const IvshmemClientPeer *peer, unsigned vector) +{ + uint64_t kick; + int fd; + + if (vector >= peer->vectors_count) { + IVSHMEM_CLIENT_DEBUG(client, "invalid vector %u on peer %" PRId64 "\n", + vector, peer->id); + return -1; + } + fd = peer->vectors[vector]; + IVSHMEM_CLIENT_DEBUG(client, "notify peer %" PRId64 + " on vector %d, fd %d\n", peer->id, vector, fd); + + kick = 1; + if (write(fd, &kick, sizeof(kick)) != sizeof(kick)) { + fprintf(stderr, "could not write to %d: %s\n", peer->vectors[vector], + strerror(errno)); + return -1; + } + return 0; +} + +/* send a notification to all vectors of a peer */ +int +ivshmem_client_notify_all_vects(const IvshmemClient *client, + const IvshmemClientPeer *peer) +{ + unsigned vector; + int ret = 0; + + for (vector = 0; vector < peer->vectors_count; vector++) { + if (ivshmem_client_notify(client, peer, vector) < 0) { + ret = -1; + } + } + + return ret; +} + +/* send a notification to all peers */ +int +ivshmem_client_notify_broadcast(const IvshmemClient *client) +{ + IvshmemClientPeer *peer; + int ret = 0; + + QTAILQ_FOREACH(peer, &client->peer_list, next) { + if (ivshmem_client_notify_all_vects(client, peer) < 0) { + ret = -1; + } + } + + return ret; +} + +/* lookup peer from its id */ +IvshmemClientPeer * +ivshmem_client_search_peer(IvshmemClient *client, int64_t peer_id) +{ + IvshmemClientPeer *peer; + + if (peer_id == client->local.id) { + return &client->local; + } + + QTAILQ_FOREACH(peer, &client->peer_list, next) { + if (peer->id == peer_id) { + return peer; + } + } + return NULL; +} + +/* dump our info, the list of peers their vectors on stdout */ +void +ivshmem_client_dump(const IvshmemClient *client) +{ + const IvshmemClientPeer *peer; + unsigned vector; + + /* dump local infos */ + peer = &client->local; + printf("our_id = %" PRId64 "\n", peer->id); + for (vector = 0; vector < peer->vectors_count; vector++) { + printf(" vector %d is enabled (fd=%d)\n", vector, + peer->vectors[vector]); + } + + /* dump peers */ + QTAILQ_FOREACH(peer, &client->peer_list, next) { + printf("peer_id = %" PRId64 "\n", peer->id); + + for (vector = 0; vector < peer->vectors_count; vector++) { + printf(" vector %d is enabled (fd=%d)\n", vector, + peer->vectors[vector]); + } + } +} diff --git a/qemu/contrib/ivshmem-client/ivshmem-client.h b/qemu/contrib/ivshmem-client/ivshmem-client.h new file mode 100644 index 000000000..54cde17d9 --- /dev/null +++ b/qemu/contrib/ivshmem-client/ivshmem-client.h @@ -0,0 +1,212 @@ +/* + * Copyright 6WIND S.A., 2014 + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#ifndef _IVSHMEM_CLIENT_H_ +#define _IVSHMEM_CLIENT_H_ + +/** + * This file provides helper to implement an ivshmem client. It is used + * on the host to ask QEMU to send an interrupt to an ivshmem PCI device in a + * guest. QEMU also implements an ivshmem client similar to this one, they both + * connect to an ivshmem server. + * + * A standalone ivshmem client based on this file is provided for debug/test + * purposes. + */ + +#include + +#include "qemu/queue.h" +#include "hw/misc/ivshmem.h" + +/** + * Maximum number of notification vectors supported by the client + */ +#define IVSHMEM_CLIENT_MAX_VECTORS 64 + +/** + * Structure storing a peer + * + * Each time a client connects to an ivshmem server, it is advertised to + * all connected clients through the unix socket. When our ivshmem + * client receives a notification, it creates a IvshmemClientPeer + * structure to store the infos of this peer. + * + * This structure is also used to store the information of our own + * client in (IvshmemClient)->local. + */ +typedef struct IvshmemClientPeer { + QTAILQ_ENTRY(IvshmemClientPeer) next; /**< next in list*/ + int64_t id; /**< the id of the peer */ + int vectors[IVSHMEM_CLIENT_MAX_VECTORS]; /**< one fd per vector */ + unsigned vectors_count; /**< number of vectors */ +} IvshmemClientPeer; +QTAILQ_HEAD(IvshmemClientPeerList, IvshmemClientPeer); + +typedef struct IvshmemClientPeerList IvshmemClientPeerList; +typedef struct IvshmemClient IvshmemClient; + +/** + * Typedef of callback function used when our IvshmemClient receives a + * notification from a peer. + */ +typedef void (*IvshmemClientNotifCb)( + const IvshmemClient *client, + const IvshmemClientPeer *peer, + unsigned vect, void *arg); + +/** + * Structure describing an ivshmem client + * + * This structure stores all information related to our client: the name + * of the server unix socket, the list of peers advertised by the + * server, our own client information, and a pointer the notification + * callback function used when we receive a notification from a peer. + */ +struct IvshmemClient { + char unix_sock_path[PATH_MAX]; /**< path to unix sock */ + int sock_fd; /**< unix sock filedesc */ + int shm_fd; /**< shm file descriptor */ + + IvshmemClientPeerList peer_list; /**< list of peers */ + IvshmemClientPeer local; /**< our own infos */ + + IvshmemClientNotifCb notif_cb; /**< notification callback */ + void *notif_arg; /**< notification argument */ + + bool verbose; /**< true to enable debug */ +}; + +/** + * Initialize an ivshmem client + * + * @client: A pointer to an uninitialized IvshmemClient structure + * @unix_sock_path: The pointer to the unix socket file name + * @notif_cb: If not NULL, the pointer to the function to be called when + * our IvshmemClient receives a notification from a peer + * @notif_arg: Opaque pointer given as-is to the notification callback + * function + * @verbose: True to enable debug + * + * Returns: 0 on success, or a negative value on error + */ +int ivshmem_client_init(IvshmemClient *client, const char *unix_sock_path, + IvshmemClientNotifCb notif_cb, void *notif_arg, + bool verbose); + +/** + * Connect to the server + * + * Connect to the server unix socket, and read the first initial + * messages sent by the server, giving the ID of the client and the file + * descriptor of the shared memory. + * + * @client: The ivshmem client + * + * Returns: 0 on success, or a negative value on error + */ +int ivshmem_client_connect(IvshmemClient *client); + +/** + * Close connection to the server and free all peer structures + * + * @client: The ivshmem client + */ +void ivshmem_client_close(IvshmemClient *client); + +/** + * Fill a fd_set with file descriptors to be monitored + * + * This function will fill a fd_set with all file descriptors + * that must be polled (unix server socket and peers eventfd). The + * function will not initialize the fd_set, it is up to the caller + * to do this. + * + * @client: The ivshmem client + * @fds: The fd_set to be updated + * @maxfd: Must be set to the max file descriptor + 1 in fd_set. This value is + * updated if this function adds a greater fd in fd_set. + */ +void ivshmem_client_get_fds(const IvshmemClient *client, fd_set *fds, + int *maxfd); + +/** + * Read and handle new messages + * + * Given a fd_set filled by select(), handle incoming messages from + * server or peers. + * + * @client: The ivshmem client + * @fds: The fd_set containing the file descriptors to be checked. Note + * that file descriptors that are not related to our client are + * ignored. + * @maxfd: The maximum fd in fd_set, plus one. + * + * Returns: 0 on success, or a negative value on error + */ +int ivshmem_client_handle_fds(IvshmemClient *client, fd_set *fds, int maxfd); + +/** + * Send a notification to a vector of a peer + * + * @client: The ivshmem client + * @peer: The peer to be notified + * @vector: The number of the vector + * + * Returns: 0 on success, or a negative value on error + */ +int ivshmem_client_notify(const IvshmemClient *client, + const IvshmemClientPeer *peer, unsigned vector); + +/** + * Send a notification to all vectors of a peer + * + * @client: The ivshmem client + * @peer: The peer to be notified + * + * Returns: 0 on success, or a negative value on error (at least one + * notification failed) + */ +int ivshmem_client_notify_all_vects(const IvshmemClient *client, + const IvshmemClientPeer *peer); + +/** + * Broadcat a notification to all vectors of all peers + * + * @client: The ivshmem client + * + * Returns: 0 on success, or a negative value on error (at least one + * notification failed) + */ +int ivshmem_client_notify_broadcast(const IvshmemClient *client); + +/** + * Search a peer from its identifier + * + * Return the peer structure from its peer_id. If the given peer_id is + * the local id, the function returns the local peer structure. + * + * @client: The ivshmem client + * @peer_id: The identifier of the peer structure + * + * Returns: The peer structure, or NULL if not found + */ +IvshmemClientPeer * +ivshmem_client_search_peer(IvshmemClient *client, int64_t peer_id); + +/** + * Dump information of this ivshmem client on stdout + * + * Dump the id and the vectors of the given ivshmem client and the list + * of its peers and their vectors on stdout. + * + * @client: The ivshmem client + */ +void ivshmem_client_dump(const IvshmemClient *client); + +#endif /* _IVSHMEM_CLIENT_H_ */ diff --git a/qemu/contrib/ivshmem-client/main.c b/qemu/contrib/ivshmem-client/main.c new file mode 100644 index 000000000..33ae1daa1 --- /dev/null +++ b/qemu/contrib/ivshmem-client/main.c @@ -0,0 +1,241 @@ +/* + * Copyright 6WIND S.A., 2014 + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" + +#include "ivshmem-client.h" + +#define IVSHMEM_CLIENT_DEFAULT_VERBOSE 0 +#define IVSHMEM_CLIENT_DEFAULT_UNIX_SOCK_PATH "/tmp/ivshmem_socket" + +typedef struct IvshmemClientArgs { + bool verbose; + const char *unix_sock_path; +} IvshmemClientArgs; + +/* show ivshmem_client_usage and exit with given error code */ +static void +ivshmem_client_usage(const char *name, int code) +{ + fprintf(stderr, "%s [opts]\n", name); + fprintf(stderr, " -h: show this help\n"); + fprintf(stderr, " -v: verbose mode\n"); + fprintf(stderr, " -S : path to the unix socket\n" + " to connect to.\n" + " default=%s\n", IVSHMEM_CLIENT_DEFAULT_UNIX_SOCK_PATH); + exit(code); +} + +/* parse the program arguments, exit on error */ +static void +ivshmem_client_parse_args(IvshmemClientArgs *args, int argc, char *argv[]) +{ + int c; + + while ((c = getopt(argc, argv, + "h" /* help */ + "v" /* verbose */ + "S:" /* unix_sock_path */ + )) != -1) { + + switch (c) { + case 'h': /* help */ + ivshmem_client_usage(argv[0], 0); + break; + + case 'v': /* verbose */ + args->verbose = 1; + break; + + case 'S': /* unix_sock_path */ + args->unix_sock_path = optarg; + break; + + default: + ivshmem_client_usage(argv[0], 1); + break; + } + } +} + +/* show command line help */ +static void +ivshmem_client_cmdline_help(void) +{ + printf("dump: dump peers (including us)\n" + "int : notify one vector on a peer\n" + "int all: notify all vectors of a peer\n" + "int all: notify all vectors of all peers (excepting us)\n"); +} + +/* read stdin and handle commands */ +static int +ivshmem_client_handle_stdin_command(IvshmemClient *client) +{ + IvshmemClientPeer *peer; + char buf[128]; + char *s, *token; + int ret; + int peer_id, vector; + + memset(buf, 0, sizeof(buf)); + ret = read(0, buf, sizeof(buf) - 1); + if (ret < 0) { + return -1; + } + + s = buf; + while ((token = strsep(&s, "\n\r;")) != NULL) { + if (!strcmp(token, "")) { + continue; + } + if (!strcmp(token, "?")) { + ivshmem_client_cmdline_help(); + } + if (!strcmp(token, "help")) { + ivshmem_client_cmdline_help(); + } else if (!strcmp(token, "dump")) { + ivshmem_client_dump(client); + } else if (!strcmp(token, "int all")) { + ivshmem_client_notify_broadcast(client); + } else if (sscanf(token, "int %d %d", &peer_id, &vector) == 2) { + peer = ivshmem_client_search_peer(client, peer_id); + if (peer == NULL) { + printf("cannot find peer_id = %d\n", peer_id); + continue; + } + ivshmem_client_notify(client, peer, vector); + } else if (sscanf(token, "int %d all", &peer_id) == 1) { + peer = ivshmem_client_search_peer(client, peer_id); + if (peer == NULL) { + printf("cannot find peer_id = %d\n", peer_id); + continue; + } + ivshmem_client_notify_all_vects(client, peer); + } else { + printf("invalid command, type help\n"); + } + } + + printf("cmd> "); + fflush(stdout); + return 0; +} + +/* listen on stdin (command line), on unix socket (notifications of new + * and dead peers), and on eventfd (IRQ request) */ +static int +ivshmem_client_poll_events(IvshmemClient *client) +{ + fd_set fds; + int ret, maxfd; + + while (1) { + + FD_ZERO(&fds); + FD_SET(0, &fds); /* add stdin in fd_set */ + maxfd = 1; + + ivshmem_client_get_fds(client, &fds, &maxfd); + + ret = select(maxfd, &fds, NULL, NULL, NULL); + if (ret < 0) { + if (errno == EINTR) { + continue; + } + + fprintf(stderr, "select error: %s\n", strerror(errno)); + break; + } + if (ret == 0) { + continue; + } + + if (FD_ISSET(0, &fds) && + ivshmem_client_handle_stdin_command(client) < 0 && errno != EINTR) { + fprintf(stderr, "ivshmem_client_handle_stdin_command() failed\n"); + break; + } + + if (ivshmem_client_handle_fds(client, &fds, maxfd) < 0) { + fprintf(stderr, "ivshmem_client_handle_fds() failed\n"); + break; + } + } + + return ret; +} + +/* callback when we receive a notification (just display it) */ +static void +ivshmem_client_notification_cb(const IvshmemClient *client, + const IvshmemClientPeer *peer, + unsigned vect, void *arg) +{ + (void)client; + (void)arg; + printf("receive notification from peer_id=%" PRId64 " vector=%u\n", + peer->id, vect); +} + +int +main(int argc, char *argv[]) +{ + struct sigaction sa; + IvshmemClient client; + IvshmemClientArgs args = { + .verbose = IVSHMEM_CLIENT_DEFAULT_VERBOSE, + .unix_sock_path = IVSHMEM_CLIENT_DEFAULT_UNIX_SOCK_PATH, + }; + + /* parse arguments, will exit on error */ + ivshmem_client_parse_args(&args, argc, argv); + + /* Ignore SIGPIPE, see this link for more info: + * http://www.mail-archive.com/libevent-users@monkey.org/msg01606.html */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + if (sigemptyset(&sa.sa_mask) == -1 || + sigaction(SIGPIPE, &sa, 0) == -1) { + perror("failed to ignore SIGPIPE; sigaction"); + return 1; + } + + ivshmem_client_cmdline_help(); + printf("cmd> "); + fflush(stdout); + + if (ivshmem_client_init(&client, args.unix_sock_path, + ivshmem_client_notification_cb, NULL, + args.verbose) < 0) { + fprintf(stderr, "cannot init client\n"); + return 1; + } + + while (1) { + if (ivshmem_client_connect(&client) < 0) { + fprintf(stderr, "cannot connect to server, retry in 1 second\n"); + sleep(1); + continue; + } + + fprintf(stdout, "listen on server socket %d\n", client.sock_fd); + + if (ivshmem_client_poll_events(&client) == 0) { + continue; + } + + /* disconnected from server, reset all peers */ + fprintf(stdout, "disconnected from server\n"); + + ivshmem_client_close(&client); + } + + return 0; +} diff --git a/qemu/contrib/ivshmem-server/Makefile.objs b/qemu/contrib/ivshmem-server/Makefile.objs new file mode 100644 index 000000000..c060dd369 --- /dev/null +++ b/qemu/contrib/ivshmem-server/Makefile.objs @@ -0,0 +1 @@ +ivshmem-server-obj-y = ivshmem-server.o main.o diff --git a/qemu/contrib/ivshmem-server/ivshmem-server.c b/qemu/contrib/ivshmem-server/ivshmem-server.c new file mode 100644 index 000000000..172db78b3 --- /dev/null +++ b/qemu/contrib/ivshmem-server/ivshmem-server.c @@ -0,0 +1,457 @@ +/* + * Copyright 6WIND S.A., 2014 + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/sockets.h" + +#include +#include +#include + +#include "ivshmem-server.h" + +/* log a message on stdout if verbose=1 */ +#define IVSHMEM_SERVER_DEBUG(server, fmt, ...) do { \ + if ((server)->verbose) { \ + printf(fmt, ## __VA_ARGS__); \ + } \ + } while (0) + +/** maximum size of a huge page, used by ivshmem_server_ftruncate() */ +#define IVSHMEM_SERVER_MAX_HUGEPAGE_SIZE (1024 * 1024 * 1024) + +/** default listen backlog (number of sockets not accepted) */ +#define IVSHMEM_SERVER_LISTEN_BACKLOG 10 + +/* send message to a client unix socket */ +static int +ivshmem_server_send_one_msg(int sock_fd, int64_t peer_id, int fd) +{ + int ret; + struct msghdr msg; + struct iovec iov[1]; + union { + struct cmsghdr cmsg; + char control[CMSG_SPACE(sizeof(int))]; + } msg_control; + struct cmsghdr *cmsg; + + peer_id = GINT64_TO_LE(peer_id); + iov[0].iov_base = &peer_id; + iov[0].iov_len = sizeof(peer_id); + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + /* if fd is specified, add it in a cmsg */ + if (fd >= 0) { + memset(&msg_control, 0, sizeof(msg_control)); + msg.msg_control = &msg_control; + msg.msg_controllen = sizeof(msg_control); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); + } + + ret = sendmsg(sock_fd, &msg, 0); + if (ret <= 0) { + return -1; + } + + return 0; +} + +/* free a peer when the server advertises a disconnection or when the + * server is freed */ +static void +ivshmem_server_free_peer(IvshmemServer *server, IvshmemServerPeer *peer) +{ + unsigned vector; + IvshmemServerPeer *other_peer; + + IVSHMEM_SERVER_DEBUG(server, "free peer %" PRId64 "\n", peer->id); + close(peer->sock_fd); + QTAILQ_REMOVE(&server->peer_list, peer, next); + + /* advertise the deletion to other peers */ + QTAILQ_FOREACH(other_peer, &server->peer_list, next) { + ivshmem_server_send_one_msg(other_peer->sock_fd, peer->id, -1); + } + + for (vector = 0; vector < peer->vectors_count; vector++) { + event_notifier_cleanup(&peer->vectors[vector]); + } + + g_free(peer); +} + +/* send the peer id and the shm_fd just after a new client connection */ +static int +ivshmem_server_send_initial_info(IvshmemServer *server, IvshmemServerPeer *peer) +{ + int ret; + + /* send our protocol version first */ + ret = ivshmem_server_send_one_msg(peer->sock_fd, IVSHMEM_PROTOCOL_VERSION, + -1); + if (ret < 0) { + IVSHMEM_SERVER_DEBUG(server, "cannot send version: %s\n", + strerror(errno)); + return -1; + } + + /* send the peer id to the client */ + ret = ivshmem_server_send_one_msg(peer->sock_fd, peer->id, -1); + if (ret < 0) { + IVSHMEM_SERVER_DEBUG(server, "cannot send peer id: %s\n", + strerror(errno)); + return -1; + } + + /* send the shm_fd */ + ret = ivshmem_server_send_one_msg(peer->sock_fd, -1, server->shm_fd); + if (ret < 0) { + IVSHMEM_SERVER_DEBUG(server, "cannot send shm fd: %s\n", + strerror(errno)); + return -1; + } + + return 0; +} + +/* handle message on listening unix socket (new client connection) */ +static int +ivshmem_server_handle_new_conn(IvshmemServer *server) +{ + IvshmemServerPeer *peer, *other_peer; + struct sockaddr_un unaddr; + socklen_t unaddr_len; + int newfd; + unsigned i; + + /* accept the incoming connection */ + unaddr_len = sizeof(unaddr); + newfd = qemu_accept(server->sock_fd, + (struct sockaddr *)&unaddr, &unaddr_len); + + if (newfd < 0) { + IVSHMEM_SERVER_DEBUG(server, "cannot accept() %s\n", strerror(errno)); + return -1; + } + + qemu_set_nonblock(newfd); + IVSHMEM_SERVER_DEBUG(server, "accept()=%d\n", newfd); + + /* allocate new structure for this peer */ + peer = g_malloc0(sizeof(*peer)); + peer->sock_fd = newfd; + + /* get an unused peer id */ + /* XXX: this could use id allocation such as Linux IDA, or simply + * a free-list */ + for (i = 0; i < G_MAXUINT16; i++) { + if (ivshmem_server_search_peer(server, server->cur_id) == NULL) { + break; + } + server->cur_id++; + } + if (i == G_MAXUINT16) { + IVSHMEM_SERVER_DEBUG(server, "cannot allocate new client id\n"); + close(newfd); + g_free(peer); + return -1; + } + peer->id = server->cur_id++; + + /* create eventfd, one per vector */ + peer->vectors_count = server->n_vectors; + for (i = 0; i < peer->vectors_count; i++) { + if (event_notifier_init(&peer->vectors[i], FALSE) < 0) { + IVSHMEM_SERVER_DEBUG(server, "cannot create eventfd\n"); + goto fail; + } + } + + /* send peer id and shm fd */ + if (ivshmem_server_send_initial_info(server, peer) < 0) { + IVSHMEM_SERVER_DEBUG(server, "cannot send initial info\n"); + goto fail; + } + + /* advertise the new peer to others */ + QTAILQ_FOREACH(other_peer, &server->peer_list, next) { + for (i = 0; i < peer->vectors_count; i++) { + ivshmem_server_send_one_msg(other_peer->sock_fd, peer->id, + peer->vectors[i].wfd); + } + } + + /* advertise the other peers to the new one */ + QTAILQ_FOREACH(other_peer, &server->peer_list, next) { + for (i = 0; i < peer->vectors_count; i++) { + ivshmem_server_send_one_msg(peer->sock_fd, other_peer->id, + other_peer->vectors[i].wfd); + } + } + + /* advertise the new peer to itself */ + for (i = 0; i < peer->vectors_count; i++) { + ivshmem_server_send_one_msg(peer->sock_fd, peer->id, + event_notifier_get_fd(&peer->vectors[i])); + } + + QTAILQ_INSERT_TAIL(&server->peer_list, peer, next); + IVSHMEM_SERVER_DEBUG(server, "new peer id = %" PRId64 "\n", + peer->id); + return 0; + +fail: + while (i--) { + event_notifier_cleanup(&peer->vectors[i]); + } + close(newfd); + g_free(peer); + return -1; +} + +/* Try to ftruncate a file to next power of 2 of shmsize. + * If it fails; all power of 2 above shmsize are tested until + * we reach the maximum huge page size. This is useful + * if the shm file is in a hugetlbfs that cannot be truncated to the + * shm_size value. */ +static int +ivshmem_server_ftruncate(int fd, unsigned shmsize) +{ + int ret; + struct stat mapstat; + + /* align shmsize to next power of 2 */ + shmsize = pow2ceil(shmsize); + + if (fstat(fd, &mapstat) != -1 && mapstat.st_size == shmsize) { + return 0; + } + + while (shmsize <= IVSHMEM_SERVER_MAX_HUGEPAGE_SIZE) { + ret = ftruncate(fd, shmsize); + if (ret == 0) { + return ret; + } + shmsize *= 2; + } + + return -1; +} + +/* Init a new ivshmem server */ +int +ivshmem_server_init(IvshmemServer *server, const char *unix_sock_path, + const char *shm_path, bool use_shm_open, + size_t shm_size, unsigned n_vectors, + bool verbose) +{ + int ret; + + memset(server, 0, sizeof(*server)); + server->verbose = verbose; + + ret = snprintf(server->unix_sock_path, sizeof(server->unix_sock_path), + "%s", unix_sock_path); + if (ret < 0 || ret >= sizeof(server->unix_sock_path)) { + IVSHMEM_SERVER_DEBUG(server, "could not copy unix socket path\n"); + return -1; + } + ret = snprintf(server->shm_path, sizeof(server->shm_path), + "%s", shm_path); + if (ret < 0 || ret >= sizeof(server->shm_path)) { + IVSHMEM_SERVER_DEBUG(server, "could not copy shm path\n"); + return -1; + } + + server->use_shm_open = use_shm_open; + server->shm_size = shm_size; + server->n_vectors = n_vectors; + + QTAILQ_INIT(&server->peer_list); + + return 0; +} + +/* open shm, create and bind to the unix socket */ +int +ivshmem_server_start(IvshmemServer *server) +{ + struct sockaddr_un sun; + int shm_fd, sock_fd, ret; + + /* open shm file */ + if (server->use_shm_open) { + IVSHMEM_SERVER_DEBUG(server, "Using POSIX shared memory: %s\n", + server->shm_path); + shm_fd = shm_open(server->shm_path, O_CREAT | O_RDWR, S_IRWXU); + } else { + gchar *filename = g_strdup_printf("%s/ivshmem.XXXXXX", server->shm_path); + IVSHMEM_SERVER_DEBUG(server, "Using file-backed shared memory: %s\n", + server->shm_path); + shm_fd = mkstemp(filename); + unlink(filename); + g_free(filename); + } + + if (shm_fd < 0) { + fprintf(stderr, "cannot open shm file %s: %s\n", server->shm_path, + strerror(errno)); + return -1; + } + if (ivshmem_server_ftruncate(shm_fd, server->shm_size) < 0) { + fprintf(stderr, "ftruncate(%s) failed: %s\n", server->shm_path, + strerror(errno)); + goto err_close_shm; + } + + IVSHMEM_SERVER_DEBUG(server, "create & bind socket %s\n", + server->unix_sock_path); + + /* create the unix listening socket */ + sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock_fd < 0) { + IVSHMEM_SERVER_DEBUG(server, "cannot create socket: %s\n", + strerror(errno)); + goto err_close_shm; + } + + sun.sun_family = AF_UNIX; + ret = snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", + server->unix_sock_path); + if (ret < 0 || ret >= sizeof(sun.sun_path)) { + IVSHMEM_SERVER_DEBUG(server, "could not copy unix socket path\n"); + goto err_close_sock; + } + if (bind(sock_fd, (struct sockaddr *)&sun, sizeof(sun)) < 0) { + IVSHMEM_SERVER_DEBUG(server, "cannot connect to %s: %s\n", sun.sun_path, + strerror(errno)); + goto err_close_sock; + } + + if (listen(sock_fd, IVSHMEM_SERVER_LISTEN_BACKLOG) < 0) { + IVSHMEM_SERVER_DEBUG(server, "listen() failed: %s\n", strerror(errno)); + goto err_close_sock; + } + + server->sock_fd = sock_fd; + server->shm_fd = shm_fd; + + return 0; + +err_close_sock: + close(sock_fd); +err_close_shm: + close(shm_fd); + return -1; +} + +/* close connections to clients, the unix socket and the shm fd */ +void +ivshmem_server_close(IvshmemServer *server) +{ + IvshmemServerPeer *peer, *npeer; + + IVSHMEM_SERVER_DEBUG(server, "close server\n"); + + QTAILQ_FOREACH_SAFE(peer, &server->peer_list, next, npeer) { + ivshmem_server_free_peer(server, peer); + } + + unlink(server->unix_sock_path); + close(server->sock_fd); + close(server->shm_fd); + server->sock_fd = -1; + server->shm_fd = -1; +} + +/* get the fd_set according to the unix socket and the peer list */ +void +ivshmem_server_get_fds(const IvshmemServer *server, fd_set *fds, int *maxfd) +{ + IvshmemServerPeer *peer; + + if (server->sock_fd == -1) { + return; + } + + FD_SET(server->sock_fd, fds); + if (server->sock_fd >= *maxfd) { + *maxfd = server->sock_fd + 1; + } + + QTAILQ_FOREACH(peer, &server->peer_list, next) { + FD_SET(peer->sock_fd, fds); + if (peer->sock_fd >= *maxfd) { + *maxfd = peer->sock_fd + 1; + } + } +} + +/* process incoming messages on the sockets in fd_set */ +int +ivshmem_server_handle_fds(IvshmemServer *server, fd_set *fds, int maxfd) +{ + IvshmemServerPeer *peer, *peer_next; + + if (server->sock_fd < maxfd && FD_ISSET(server->sock_fd, fds) && + ivshmem_server_handle_new_conn(server) < 0 && errno != EINTR) { + IVSHMEM_SERVER_DEBUG(server, "ivshmem_server_handle_new_conn() " + "failed\n"); + return -1; + } + + QTAILQ_FOREACH_SAFE(peer, &server->peer_list, next, peer_next) { + /* any message from a peer socket result in a close() */ + IVSHMEM_SERVER_DEBUG(server, "peer->sock_fd=%d\n", peer->sock_fd); + if (peer->sock_fd < maxfd && FD_ISSET(peer->sock_fd, fds)) { + ivshmem_server_free_peer(server, peer); + } + } + + return 0; +} + +/* lookup peer from its id */ +IvshmemServerPeer * +ivshmem_server_search_peer(IvshmemServer *server, int64_t peer_id) +{ + IvshmemServerPeer *peer; + + QTAILQ_FOREACH(peer, &server->peer_list, next) { + if (peer->id == peer_id) { + return peer; + } + } + return NULL; +} + +/* dump our info, the list of peers their vectors on stdout */ +void +ivshmem_server_dump(const IvshmemServer *server) +{ + const IvshmemServerPeer *peer; + unsigned vector; + + /* dump peers */ + QTAILQ_FOREACH(peer, &server->peer_list, next) { + printf("peer_id = %" PRId64 "\n", peer->id); + + for (vector = 0; vector < peer->vectors_count; vector++) { + printf(" vector %d is enabled (fd=%d)\n", vector, + event_notifier_get_fd(&peer->vectors[vector])); + } + } +} diff --git a/qemu/contrib/ivshmem-server/ivshmem-server.h b/qemu/contrib/ivshmem-server/ivshmem-server.h new file mode 100644 index 000000000..385163961 --- /dev/null +++ b/qemu/contrib/ivshmem-server/ivshmem-server.h @@ -0,0 +1,166 @@ +/* + * Copyright 6WIND S.A., 2014 + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#ifndef _IVSHMEM_SERVER_H_ +#define _IVSHMEM_SERVER_H_ + +/** + * The ivshmem server is a daemon that creates a unix socket in listen + * mode. The ivshmem clients (qemu or ivshmem-client) connect to this + * unix socket. For each client, the server will create some eventfd + * (see EVENTFD(2)), one per vector. These fd are transmitted to all + * clients using the SCM_RIGHTS cmsg message. Therefore, each client is + * able to send a notification to another client without beeing + * "profixied" by the server. + * + * We use this mechanism to send interruptions between guests. + * qemu is able to transform an event on a eventfd into a PCI MSI-x + * interruption in the guest. + * + * The ivshmem server is also able to share the file descriptor + * associated to the ivshmem shared memory. + */ + +#include + +#include "qemu/event_notifier.h" +#include "qemu/queue.h" +#include "hw/misc/ivshmem.h" + +/** + * Maximum number of notification vectors supported by the server + */ +#define IVSHMEM_SERVER_MAX_VECTORS 64 + +/** + * Structure storing a peer + * + * Each time a client connects to an ivshmem server, a new + * IvshmemServerPeer structure is created. This peer and all its + * vectors are advertised to all connected clients through the connected + * unix sockets. + */ +typedef struct IvshmemServerPeer { + QTAILQ_ENTRY(IvshmemServerPeer) next; /**< next in list*/ + int sock_fd; /**< connected unix sock */ + int64_t id; /**< the id of the peer */ + EventNotifier vectors[IVSHMEM_SERVER_MAX_VECTORS]; /**< one per vector */ + unsigned vectors_count; /**< number of vectors */ +} IvshmemServerPeer; +QTAILQ_HEAD(IvshmemServerPeerList, IvshmemServerPeer); + +typedef struct IvshmemServerPeerList IvshmemServerPeerList; + +/** + * Structure describing an ivshmem server + * + * This structure stores all information related to our server: the name + * of the server unix socket and the list of connected peers. + */ +typedef struct IvshmemServer { + char unix_sock_path[PATH_MAX]; /**< path to unix socket */ + int sock_fd; /**< unix sock file descriptor */ + char shm_path[PATH_MAX]; /**< path to shm */ + bool use_shm_open; + size_t shm_size; /**< size of shm */ + int shm_fd; /**< shm file descriptor */ + unsigned n_vectors; /**< number of vectors */ + uint16_t cur_id; /**< id to be given to next client */ + bool verbose; /**< true in verbose mode */ + IvshmemServerPeerList peer_list; /**< list of peers */ +} IvshmemServer; + +/** + * Initialize an ivshmem server + * + * @server: A pointer to an uninitialized IvshmemServer structure + * @unix_sock_path: The pointer to the unix socket file name + * @shm_path: Path to the shared memory. The path corresponds to a POSIX + * shm name or a hugetlbfs mount point. + * @shm_size: Size of shared memory + * @n_vectors: Number of interrupt vectors per client + * @verbose: True to enable verbose mode + * + * Returns: 0 on success, or a negative value on error + */ +int +ivshmem_server_init(IvshmemServer *server, const char *unix_sock_path, + const char *shm_path, bool use_shm_open, + size_t shm_size, unsigned n_vectors, + bool verbose); + +/** + * Open the shm, then create and bind to the unix socket + * + * @server: The pointer to the initialized IvshmemServer structure + * + * Returns: 0 on success, or a negative value on error + */ +int ivshmem_server_start(IvshmemServer *server); + +/** + * Close the server + * + * Close connections to all clients, close the unix socket and the + * shared memory file descriptor. The structure remains initialized, so + * it is possible to call ivshmem_server_start() again after a call to + * ivshmem_server_close(). + * + * @server: The ivshmem server + */ +void ivshmem_server_close(IvshmemServer *server); + +/** + * Fill a fd_set with file descriptors to be monitored + * + * This function will fill a fd_set with all file descriptors that must + * be polled (unix server socket and peers unix socket). The function + * will not initialize the fd_set, it is up to the caller to do it. + * + * @server: The ivshmem server + * @fds: The fd_set to be updated + * @maxfd: Must be set to the max file descriptor + 1 in fd_set. This value is + * updated if this function adds a greater fd in fd_set. + */ +void +ivshmem_server_get_fds(const IvshmemServer *server, fd_set *fds, int *maxfd); + +/** + * Read and handle new messages + * + * Given a fd_set (for instance filled by a call to select()), handle + * incoming messages from peers. + * + * @server: The ivshmem server + * @fds: The fd_set containing the file descriptors to be checked. Note that + * file descriptors that are not related to our server are ignored. + * @maxfd: The maximum fd in fd_set, plus one. + * + * Returns: 0 on success, or a negative value on error + */ +int ivshmem_server_handle_fds(IvshmemServer *server, fd_set *fds, int maxfd); + +/** + * Search a peer from its identifier + * + * @server: The ivshmem server + * @peer_id: The identifier of the peer structure + * + * Returns: The peer structure, or NULL if not found + */ +IvshmemServerPeer * +ivshmem_server_search_peer(IvshmemServer *server, int64_t peer_id); + +/** + * Dump information of this ivshmem server and its peers on stdout + * + * @server: The ivshmem server + */ +void ivshmem_server_dump(const IvshmemServer *server); + +#endif /* _IVSHMEM_SERVER_H_ */ diff --git a/qemu/contrib/ivshmem-server/main.c b/qemu/contrib/ivshmem-server/main.c new file mode 100644 index 000000000..45776d8af --- /dev/null +++ b/qemu/contrib/ivshmem-server/main.c @@ -0,0 +1,273 @@ +/* + * Copyright 6WIND S.A., 2014 + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/cutils.h" + +#include "ivshmem-server.h" + +#define IVSHMEM_SERVER_DEFAULT_VERBOSE 0 +#define IVSHMEM_SERVER_DEFAULT_FOREGROUND 0 +#define IVSHMEM_SERVER_DEFAULT_PID_FILE "/var/run/ivshmem-server.pid" +#define IVSHMEM_SERVER_DEFAULT_UNIX_SOCK_PATH "/tmp/ivshmem_socket" +#define IVSHMEM_SERVER_DEFAULT_SHM_PATH "ivshmem" +#define IVSHMEM_SERVER_DEFAULT_SHM_SIZE (4*1024*1024) +#define IVSHMEM_SERVER_DEFAULT_N_VECTORS 1 + +/* used to quit on signal SIGTERM */ +static int ivshmem_server_quit; + +/* arguments given by the user */ +typedef struct IvshmemServerArgs { + bool verbose; + bool foreground; + const char *pid_file; + const char *unix_socket_path; + const char *shm_path; + bool use_shm_open; + uint64_t shm_size; + unsigned n_vectors; +} IvshmemServerArgs; + +static void +ivshmem_server_usage(const char *progname) +{ + printf("Usage: %s [OPTION]...\n" + " -h: show this help\n" + " -v: verbose mode\n" + " -F: foreground mode (default is to daemonize)\n" + " -p : path to the PID file (used in daemon mode only)\n" + " default " IVSHMEM_SERVER_DEFAULT_PID_FILE "\n" + " -S : path to the unix socket to listen to\n" + " default " IVSHMEM_SERVER_DEFAULT_UNIX_SOCK_PATH "\n" + " -M : POSIX shared memory object to use\n" + " default " IVSHMEM_SERVER_DEFAULT_SHM_PATH "\n" + " -m : where to create shared memory\n" + " -l : size of shared memory in bytes\n" + " suffixes K, M and G can be used, e.g. 1K means 1024\n" + " default %u\n" + " -n : number of vectors\n" + " default %u\n", + progname, IVSHMEM_SERVER_DEFAULT_SHM_SIZE, + IVSHMEM_SERVER_DEFAULT_N_VECTORS); +} + +static void +ivshmem_server_help(const char *progname) +{ + fprintf(stderr, "Try '%s -h' for more information.\n", progname); +} + +/* parse the program arguments, exit on error */ +static void +ivshmem_server_parse_args(IvshmemServerArgs *args, int argc, char *argv[]) +{ + int c; + unsigned long long v; + Error *err = NULL; + + while ((c = getopt(argc, argv, "hvFp:S:m:M:l:n:")) != -1) { + + switch (c) { + case 'h': /* help */ + ivshmem_server_usage(argv[0]); + exit(0); + break; + + case 'v': /* verbose */ + args->verbose = 1; + break; + + case 'F': /* foreground */ + args->foreground = 1; + break; + + case 'p': /* pid file */ + args->pid_file = optarg; + break; + + case 'S': /* unix socket path */ + args->unix_socket_path = optarg; + break; + + case 'M': /* shm name */ + case 'm': /* dir name */ + args->shm_path = optarg; + args->use_shm_open = c == 'M'; + break; + + case 'l': /* shm size */ + parse_option_size("shm_size", optarg, &args->shm_size, &err); + if (err) { + error_report_err(err); + ivshmem_server_help(argv[0]); + exit(1); + } + break; + + case 'n': /* number of vectors */ + if (parse_uint_full(optarg, &v, 0) < 0) { + fprintf(stderr, "cannot parse n_vectors\n"); + ivshmem_server_help(argv[0]); + exit(1); + } + args->n_vectors = v; + break; + + default: + ivshmem_server_usage(argv[0]); + exit(1); + break; + } + } + + if (args->n_vectors > IVSHMEM_SERVER_MAX_VECTORS) { + fprintf(stderr, "too many requested vectors (max is %d)\n", + IVSHMEM_SERVER_MAX_VECTORS); + ivshmem_server_help(argv[0]); + exit(1); + } + + if (args->verbose == 1 && args->foreground == 0) { + fprintf(stderr, "cannot use verbose in daemon mode\n"); + ivshmem_server_help(argv[0]); + exit(1); + } +} + +/* wait for events on listening server unix socket and connected client + * sockets */ +static int +ivshmem_server_poll_events(IvshmemServer *server) +{ + fd_set fds; + int ret = 0, maxfd; + + while (!ivshmem_server_quit) { + + FD_ZERO(&fds); + maxfd = 0; + ivshmem_server_get_fds(server, &fds, &maxfd); + + ret = select(maxfd, &fds, NULL, NULL, NULL); + + if (ret < 0) { + if (errno == EINTR) { + continue; + } + + fprintf(stderr, "select error: %s\n", strerror(errno)); + break; + } + if (ret == 0) { + continue; + } + + if (ivshmem_server_handle_fds(server, &fds, maxfd) < 0) { + fprintf(stderr, "ivshmem_server_handle_fds() failed\n"); + break; + } + } + + return ret; +} + +static void +ivshmem_server_quit_cb(int signum) +{ + ivshmem_server_quit = 1; +} + +int +main(int argc, char *argv[]) +{ + IvshmemServer server; + struct sigaction sa, sa_quit; + IvshmemServerArgs args = { + .verbose = IVSHMEM_SERVER_DEFAULT_VERBOSE, + .foreground = IVSHMEM_SERVER_DEFAULT_FOREGROUND, + .pid_file = IVSHMEM_SERVER_DEFAULT_PID_FILE, + .unix_socket_path = IVSHMEM_SERVER_DEFAULT_UNIX_SOCK_PATH, + .shm_path = IVSHMEM_SERVER_DEFAULT_SHM_PATH, + .use_shm_open = true, + .shm_size = IVSHMEM_SERVER_DEFAULT_SHM_SIZE, + .n_vectors = IVSHMEM_SERVER_DEFAULT_N_VECTORS, + }; + int ret = 1; + + /* + * Do not remove this notice without adding proper error handling! + * Start with handling ivshmem_server_send_one_msg() failure. + */ + printf("*** Example code, do not use in production ***\n"); + + /* parse arguments, will exit on error */ + ivshmem_server_parse_args(&args, argc, argv); + + /* Ignore SIGPIPE, see this link for more info: + * http://www.mail-archive.com/libevent-users@monkey.org/msg01606.html */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + if (sigemptyset(&sa.sa_mask) == -1 || + sigaction(SIGPIPE, &sa, 0) == -1) { + perror("failed to ignore SIGPIPE; sigaction"); + goto err; + } + + sa_quit.sa_handler = ivshmem_server_quit_cb; + sa_quit.sa_flags = 0; + if (sigemptyset(&sa_quit.sa_mask) == -1 || + sigaction(SIGTERM, &sa_quit, 0) == -1) { + perror("failed to add SIGTERM handler; sigaction"); + goto err; + } + + /* init the ivshms structure */ + if (ivshmem_server_init(&server, args.unix_socket_path, + args.shm_path, args.use_shm_open, + args.shm_size, args.n_vectors, args.verbose) < 0) { + fprintf(stderr, "cannot init server\n"); + goto err; + } + + /* start the ivshmem server (open shm & unix socket) */ + if (ivshmem_server_start(&server) < 0) { + fprintf(stderr, "cannot bind\n"); + goto err; + } + + /* daemonize if asked to */ + if (!args.foreground) { + FILE *fp; + + if (qemu_daemon(1, 1) < 0) { + fprintf(stderr, "cannot daemonize: %s\n", strerror(errno)); + goto err_close; + } + + /* write pid file */ + fp = fopen(args.pid_file, "w"); + if (fp == NULL) { + fprintf(stderr, "cannot write pid file: %s\n", strerror(errno)); + goto err_close; + } + + fprintf(fp, "%d\n", (int) getpid()); + fclose(fp); + } + + ivshmem_server_poll_events(&server); + fprintf(stdout, "server disconnected\n"); + ret = 0; + +err_close: + ivshmem_server_close(&server); +err: + return ret; +} diff --git a/qemu/coroutine-gthread.c b/qemu/coroutine-gthread.c deleted file mode 100644 index 6bd6d6b22..000000000 --- a/qemu/coroutine-gthread.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * GThread coroutine initialization code - * - * Copyright (C) 2006 Anthony Liguori - * Copyright (C) 2011 Aneesh Kumar K.V - * - * 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.0 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 . - */ - -#include -#include "qemu-common.h" -#include "block/coroutine_int.h" - -typedef struct { - Coroutine base; - GThread *thread; - bool runnable; - bool free_on_thread_exit; - CoroutineAction action; -} CoroutineGThread; - -static CompatGMutex coroutine_lock; -static CompatGCond coroutine_cond; - -/* GLib 2.31 and beyond deprecated various parts of the thread API, - * but the new interfaces are not available in older GLib versions - * so we have to cope with both. - */ -#if GLIB_CHECK_VERSION(2, 31, 0) -/* Awkwardly, the GPrivate API doesn't provide a way to update the - * GDestroyNotify handler for the coroutine key dynamically. So instead - * we track whether or not the CoroutineGThread should be freed on - * thread exit / coroutine key update using the free_on_thread_exit - * field. - */ -static void coroutine_destroy_notify(gpointer data) -{ - CoroutineGThread *co = data; - if (co && co->free_on_thread_exit) { - g_free(co); - } -} - -static GPrivate coroutine_key = G_PRIVATE_INIT(coroutine_destroy_notify); - -static inline CoroutineGThread *get_coroutine_key(void) -{ - return g_private_get(&coroutine_key); -} - -static inline void set_coroutine_key(CoroutineGThread *co, - bool free_on_thread_exit) -{ - /* Unlike g_static_private_set() this does not call the GDestroyNotify - * if the previous value of the key was NULL. Fortunately we only need - * the GDestroyNotify in the non-NULL key case. - */ - co->free_on_thread_exit = free_on_thread_exit; - g_private_replace(&coroutine_key, co); -} - -static inline GThread *create_thread(GThreadFunc func, gpointer data) -{ - return g_thread_new("coroutine", func, data); -} - -#else - -/* Handle older GLib versions */ - -static GStaticPrivate coroutine_key = G_STATIC_PRIVATE_INIT; - -static inline CoroutineGThread *get_coroutine_key(void) -{ - return g_static_private_get(&coroutine_key); -} - -static inline void set_coroutine_key(CoroutineGThread *co, - bool free_on_thread_exit) -{ - g_static_private_set(&coroutine_key, co, - free_on_thread_exit ? (GDestroyNotify)g_free : NULL); -} - -static inline GThread *create_thread(GThreadFunc func, gpointer data) -{ - return g_thread_create_full(func, data, 0, TRUE, TRUE, - G_THREAD_PRIORITY_NORMAL, NULL); -} - -#endif - - -static void __attribute__((constructor)) coroutine_init(void) -{ -#if !GLIB_CHECK_VERSION(2, 31, 0) - if (!g_thread_supported()) { - g_thread_init(NULL); - } -#endif -} - -static void coroutine_wait_runnable_locked(CoroutineGThread *co) -{ - while (!co->runnable) { - g_cond_wait(&coroutine_cond, &coroutine_lock); - } -} - -static void coroutine_wait_runnable(CoroutineGThread *co) -{ - g_mutex_lock(&coroutine_lock); - coroutine_wait_runnable_locked(co); - g_mutex_unlock(&coroutine_lock); -} - -static gpointer coroutine_thread(gpointer opaque) -{ - CoroutineGThread *co = opaque; - - set_coroutine_key(co, false); - coroutine_wait_runnable(co); - co->base.entry(co->base.entry_arg); - qemu_coroutine_switch(&co->base, co->base.caller, COROUTINE_TERMINATE); - return NULL; -} - -Coroutine *qemu_coroutine_new(void) -{ - CoroutineGThread *co; - - co = g_malloc0(sizeof(*co)); - co->thread = create_thread(coroutine_thread, co); - if (!co->thread) { - g_free(co); - return NULL; - } - return &co->base; -} - -void qemu_coroutine_delete(Coroutine *co_) -{ - CoroutineGThread *co = DO_UPCAST(CoroutineGThread, base, co_); - - g_thread_join(co->thread); - g_free(co); -} - -CoroutineAction qemu_coroutine_switch(Coroutine *from_, - Coroutine *to_, - CoroutineAction action) -{ - CoroutineGThread *from = DO_UPCAST(CoroutineGThread, base, from_); - CoroutineGThread *to = DO_UPCAST(CoroutineGThread, base, to_); - - g_mutex_lock(&coroutine_lock); - from->runnable = false; - from->action = action; - to->runnable = true; - to->action = action; - g_cond_broadcast(&coroutine_cond); - - if (action != COROUTINE_TERMINATE) { - coroutine_wait_runnable_locked(from); - } - g_mutex_unlock(&coroutine_lock); - return from->action; -} - -Coroutine *qemu_coroutine_self(void) -{ - CoroutineGThread *co = get_coroutine_key(); - if (!co) { - co = g_malloc0(sizeof(*co)); - co->runnable = true; - set_coroutine_key(co, true); - } - - return &co->base; -} - -bool qemu_in_coroutine(void) -{ - CoroutineGThread *co = get_coroutine_key(); - - return co && co->base.caller; -} diff --git a/qemu/coroutine-sigaltstack.c b/qemu/coroutine-sigaltstack.c deleted file mode 100644 index 63519fffc..000000000 --- a/qemu/coroutine-sigaltstack.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * sigaltstack coroutine initialization code - * - * Copyright (C) 2006 Anthony Liguori - * Copyright (C) 2011 Kevin Wolf - * Copyright (C) 2012 Alex Barcelo -** This file is partly based on pth_mctx.c, from the GNU Portable Threads -** Copyright (c) 1999-2006 Ralf S. Engelschall - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -/* XXX Is there a nicer way to disable glibc's stack check for longjmp? */ -#ifdef _FORTIFY_SOURCE -#undef _FORTIFY_SOURCE -#endif -#include -#include -#include -#include -#include -#include "qemu-common.h" -#include "block/coroutine_int.h" - -typedef struct { - Coroutine base; - void *stack; - sigjmp_buf env; -} CoroutineUContext; - -/** - * Per-thread coroutine bookkeeping - */ -typedef struct { - /** Currently executing coroutine */ - Coroutine *current; - - /** The default coroutine */ - CoroutineUContext leader; - - /** Information for the signal handler (trampoline) */ - sigjmp_buf tr_reenter; - volatile sig_atomic_t tr_called; - void *tr_handler; -} CoroutineThreadState; - -static pthread_key_t thread_state_key; - -static CoroutineThreadState *coroutine_get_thread_state(void) -{ - CoroutineThreadState *s = pthread_getspecific(thread_state_key); - - if (!s) { - s = g_malloc0(sizeof(*s)); - s->current = &s->leader.base; - pthread_setspecific(thread_state_key, s); - } - return s; -} - -static void qemu_coroutine_thread_cleanup(void *opaque) -{ - CoroutineThreadState *s = opaque; - - g_free(s); -} - -static void __attribute__((constructor)) coroutine_init(void) -{ - int ret; - - ret = pthread_key_create(&thread_state_key, qemu_coroutine_thread_cleanup); - if (ret != 0) { - fprintf(stderr, "unable to create leader key: %s\n", strerror(errno)); - abort(); - } -} - -/* "boot" function - * This is what starts the coroutine, is called from the trampoline - * (from the signal handler when it is not signal handling, read ahead - * for more information). - */ -static void coroutine_bootstrap(CoroutineUContext *self, Coroutine *co) -{ - /* Initialize longjmp environment and switch back the caller */ - if (!sigsetjmp(self->env, 0)) { - siglongjmp(*(sigjmp_buf *)co->entry_arg, 1); - } - - while (true) { - co->entry(co->entry_arg); - qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); - } -} - -/* - * This is used as the signal handler. This is called with the brand new stack - * (thanks to sigaltstack). We have to return, given that this is a signal - * handler and the sigmask and some other things are changed. - */ -static void coroutine_trampoline(int signal) -{ - CoroutineUContext *self; - Coroutine *co; - CoroutineThreadState *coTS; - - /* Get the thread specific information */ - coTS = coroutine_get_thread_state(); - self = coTS->tr_handler; - coTS->tr_called = 1; - co = &self->base; - - /* - * Here we have to do a bit of a ping pong between the caller, given that - * this is a signal handler and we have to do a return "soon". Then the - * caller can reestablish everything and do a siglongjmp here again. - */ - if (!sigsetjmp(coTS->tr_reenter, 0)) { - return; - } - - /* - * Ok, the caller has siglongjmp'ed back to us, so now prepare - * us for the real machine state switching. We have to jump - * into another function here to get a new stack context for - * the auto variables (which have to be auto-variables - * because the start of the thread happens later). Else with - * PIC (i.e. Position Independent Code which is used when PTH - * is built as a shared library) most platforms would - * horrible core dump as experience showed. - */ - coroutine_bootstrap(self, co); -} - -Coroutine *qemu_coroutine_new(void) -{ - const size_t stack_size = 1 << 20; - CoroutineUContext *co; - CoroutineThreadState *coTS; - struct sigaction sa; - struct sigaction osa; - stack_t ss; - stack_t oss; - sigset_t sigs; - sigset_t osigs; - sigjmp_buf old_env; - - /* The way to manipulate stack is with the sigaltstack function. We - * prepare a stack, with it delivering a signal to ourselves and then - * put sigsetjmp/siglongjmp where needed. - * This has been done keeping coroutine-ucontext as a model and with the - * pth ideas (GNU Portable Threads). See coroutine-ucontext for the basics - * of the coroutines and see pth_mctx.c (from the pth project) for the - * sigaltstack way of manipulating stacks. - */ - - co = g_malloc0(sizeof(*co)); - co->stack = g_malloc(stack_size); - co->base.entry_arg = &old_env; /* stash away our jmp_buf */ - - coTS = coroutine_get_thread_state(); - coTS->tr_handler = co; - - /* - * Preserve the SIGUSR2 signal state, block SIGUSR2, - * and establish our signal handler. The signal will - * later transfer control onto the signal stack. - */ - sigemptyset(&sigs); - sigaddset(&sigs, SIGUSR2); - pthread_sigmask(SIG_BLOCK, &sigs, &osigs); - sa.sa_handler = coroutine_trampoline; - sigfillset(&sa.sa_mask); - sa.sa_flags = SA_ONSTACK; - if (sigaction(SIGUSR2, &sa, &osa) != 0) { - abort(); - } - - /* - * Set the new stack. - */ - ss.ss_sp = co->stack; - ss.ss_size = stack_size; - ss.ss_flags = 0; - if (sigaltstack(&ss, &oss) < 0) { - abort(); - } - - /* - * Now transfer control onto the signal stack and set it up. - * It will return immediately via "return" after the sigsetjmp() - * was performed. Be careful here with race conditions. The - * signal can be delivered the first time sigsuspend() is - * called. - */ - coTS->tr_called = 0; - pthread_kill(pthread_self(), SIGUSR2); - sigfillset(&sigs); - sigdelset(&sigs, SIGUSR2); - while (!coTS->tr_called) { - sigsuspend(&sigs); - } - - /* - * Inform the system that we are back off the signal stack by - * removing the alternative signal stack. Be careful here: It - * first has to be disabled, before it can be removed. - */ - sigaltstack(NULL, &ss); - ss.ss_flags = SS_DISABLE; - if (sigaltstack(&ss, NULL) < 0) { - abort(); - } - sigaltstack(NULL, &ss); - if (!(oss.ss_flags & SS_DISABLE)) { - sigaltstack(&oss, NULL); - } - - /* - * Restore the old SIGUSR2 signal handler and mask - */ - sigaction(SIGUSR2, &osa, NULL); - pthread_sigmask(SIG_SETMASK, &osigs, NULL); - - /* - * Now enter the trampoline again, but this time not as a signal - * handler. Instead we jump into it directly. The functionally - * redundant ping-pong pointer arithmetic is necessary to avoid - * type-conversion warnings related to the `volatile' qualifier and - * the fact that `jmp_buf' usually is an array type. - */ - if (!sigsetjmp(old_env, 0)) { - siglongjmp(coTS->tr_reenter, 1); - } - - /* - * Ok, we returned again, so now we're finished - */ - - return &co->base; -} - -void qemu_coroutine_delete(Coroutine *co_) -{ - CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_); - - g_free(co->stack); - g_free(co); -} - -CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, - CoroutineAction action) -{ - CoroutineUContext *from = DO_UPCAST(CoroutineUContext, base, from_); - CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, to_); - CoroutineThreadState *s = coroutine_get_thread_state(); - int ret; - - s->current = to_; - - ret = sigsetjmp(from->env, 0); - if (ret == 0) { - siglongjmp(to->env, action); - } - return ret; -} - -Coroutine *qemu_coroutine_self(void) -{ - CoroutineThreadState *s = coroutine_get_thread_state(); - - return s->current; -} - -bool qemu_in_coroutine(void) -{ - CoroutineThreadState *s = pthread_getspecific(thread_state_key); - - return s && s->current->caller; -} - diff --git a/qemu/coroutine-ucontext.c b/qemu/coroutine-ucontext.c deleted file mode 100644 index 259fcb48a..000000000 --- a/qemu/coroutine-ucontext.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * ucontext coroutine initialization code - * - * Copyright (C) 2006 Anthony Liguori - * Copyright (C) 2011 Kevin Wolf - * - * 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.0 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 . - */ - -/* XXX Is there a nicer way to disable glibc's stack check for longjmp? */ -#ifdef _FORTIFY_SOURCE -#undef _FORTIFY_SOURCE -#endif -#include -#include -#include -#include -#include "qemu-common.h" -#include "block/coroutine_int.h" - -#ifdef CONFIG_VALGRIND_H -#include -#endif - -typedef struct { - Coroutine base; - void *stack; - sigjmp_buf env; - -#ifdef CONFIG_VALGRIND_H - unsigned int valgrind_stack_id; -#endif - -} CoroutineUContext; - -/** - * Per-thread coroutine bookkeeping - */ -static __thread CoroutineUContext leader; -static __thread Coroutine *current; - -/* - * va_args to makecontext() must be type 'int', so passing - * the pointer we need may require several int args. This - * union is a quick hack to let us do that - */ -union cc_arg { - void *p; - int i[2]; -}; - -static void coroutine_trampoline(int i0, int i1) -{ - union cc_arg arg; - CoroutineUContext *self; - Coroutine *co; - - arg.i[0] = i0; - arg.i[1] = i1; - self = arg.p; - co = &self->base; - - /* Initialize longjmp environment and switch back the caller */ - if (!sigsetjmp(self->env, 0)) { - siglongjmp(*(sigjmp_buf *)co->entry_arg, 1); - } - - while (true) { - co->entry(co->entry_arg); - qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); - } -} - -Coroutine *qemu_coroutine_new(void) -{ - const size_t stack_size = 1 << 20; - CoroutineUContext *co; - ucontext_t old_uc, uc; - sigjmp_buf old_env; - union cc_arg arg = {0}; - - /* The ucontext functions preserve signal masks which incurs a - * system call overhead. sigsetjmp(buf, 0)/siglongjmp() does not - * preserve signal masks but only works on the current stack. - * Since we need a way to create and switch to a new stack, use - * the ucontext functions for that but sigsetjmp()/siglongjmp() for - * everything else. - */ - - if (getcontext(&uc) == -1) { - abort(); - } - - co = g_malloc0(sizeof(*co)); - co->stack = g_malloc(stack_size); - co->base.entry_arg = &old_env; /* stash away our jmp_buf */ - - uc.uc_link = &old_uc; - uc.uc_stack.ss_sp = co->stack; - uc.uc_stack.ss_size = stack_size; - uc.uc_stack.ss_flags = 0; - -#ifdef CONFIG_VALGRIND_H - co->valgrind_stack_id = - VALGRIND_STACK_REGISTER(co->stack, co->stack + stack_size); -#endif - - arg.p = co; - - makecontext(&uc, (void (*)(void))coroutine_trampoline, - 2, arg.i[0], arg.i[1]); - - /* swapcontext() in, siglongjmp() back out */ - if (!sigsetjmp(old_env, 0)) { - swapcontext(&old_uc, &uc); - } - return &co->base; -} - -#ifdef CONFIG_VALGRIND_H -#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE -/* Work around an unused variable in the valgrind.h macro... */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-but-set-variable" -#endif -static inline void valgrind_stack_deregister(CoroutineUContext *co) -{ - VALGRIND_STACK_DEREGISTER(co->valgrind_stack_id); -} -#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE -#pragma GCC diagnostic pop -#endif -#endif - -void qemu_coroutine_delete(Coroutine *co_) -{ - CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_); - -#ifdef CONFIG_VALGRIND_H - valgrind_stack_deregister(co); -#endif - - g_free(co->stack); - g_free(co); -} - -/* This function is marked noinline to prevent GCC from inlining it - * into coroutine_trampoline(). If we allow it to do that then it - * hoists the code to get the address of the TLS variable "current" - * out of the while() loop. This is an invalid transformation because - * the sigsetjmp() call may be called when running thread A but - * return in thread B, and so we might be in a different thread - * context each time round the loop. - */ -CoroutineAction __attribute__((noinline)) -qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, - CoroutineAction action) -{ - CoroutineUContext *from = DO_UPCAST(CoroutineUContext, base, from_); - CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, to_); - int ret; - - current = to_; - - ret = sigsetjmp(from->env, 0); - if (ret == 0) { - siglongjmp(to->env, action); - } - return ret; -} - -Coroutine *qemu_coroutine_self(void) -{ - if (!current) { - current = &leader.base; - } - return current; -} - -bool qemu_in_coroutine(void) -{ - return current && current->caller; -} diff --git a/qemu/coroutine-win32.c b/qemu/coroutine-win32.c deleted file mode 100644 index 17ace37de..000000000 --- a/qemu/coroutine-win32.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Win32 coroutine initialization code - * - * Copyright (c) 2011 Kevin Wolf - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu-common.h" -#include "block/coroutine_int.h" - -typedef struct -{ - Coroutine base; - - LPVOID fiber; - CoroutineAction action; -} CoroutineWin32; - -static __thread CoroutineWin32 leader; -static __thread Coroutine *current; - -/* This function is marked noinline to prevent GCC from inlining it - * into coroutine_trampoline(). If we allow it to do that then it - * hoists the code to get the address of the TLS variable "current" - * out of the while() loop. This is an invalid transformation because - * the SwitchToFiber() call may be called when running thread A but - * return in thread B, and so we might be in a different thread - * context each time round the loop. - */ -CoroutineAction __attribute__((noinline)) -qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, - CoroutineAction action) -{ - CoroutineWin32 *from = DO_UPCAST(CoroutineWin32, base, from_); - CoroutineWin32 *to = DO_UPCAST(CoroutineWin32, base, to_); - - current = to_; - - to->action = action; - SwitchToFiber(to->fiber); - return from->action; -} - -static void CALLBACK coroutine_trampoline(void *co_) -{ - Coroutine *co = co_; - - while (true) { - co->entry(co->entry_arg); - qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); - } -} - -Coroutine *qemu_coroutine_new(void) -{ - const size_t stack_size = 1 << 20; - CoroutineWin32 *co; - - co = g_malloc0(sizeof(*co)); - co->fiber = CreateFiber(stack_size, coroutine_trampoline, &co->base); - return &co->base; -} - -void qemu_coroutine_delete(Coroutine *co_) -{ - CoroutineWin32 *co = DO_UPCAST(CoroutineWin32, base, co_); - - DeleteFiber(co->fiber); - g_free(co); -} - -Coroutine *qemu_coroutine_self(void) -{ - if (!current) { - current = &leader.base; - leader.fiber = ConvertThreadToFiber(NULL); - } - return current; -} - -bool qemu_in_coroutine(void) -{ - return current && current->caller; -} diff --git a/qemu/cpu-exec-common.c b/qemu/cpu-exec-common.c new file mode 100644 index 000000000..1b1731cd8 --- /dev/null +++ b/qemu/cpu-exec-common.c @@ -0,0 +1,82 @@ +/* + * emulator main execution loop + * + * Copyright (c) 2003-2005 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 . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "sysemu/cpus.h" +#include "exec/memory-internal.h" + +bool exit_request; +CPUState *tcg_current_cpu; + +/* exit the current TB from a signal handler. The host registers are + restored in a state compatible with the CPU emulator + */ +#if defined(CONFIG_SOFTMMU) +void cpu_resume_from_signal(CPUState *cpu, void *puc) +{ + /* XXX: restore cpu registers saved in host registers */ + + cpu->exception_index = -1; + siglongjmp(cpu->jmp_env, 1); +} + +void cpu_reloading_memory_map(void) +{ + if (qemu_in_vcpu_thread()) { + /* The guest can in theory prolong the RCU critical section as long + * as it feels like. The major problem with this is that because it + * can do multiple reconfigurations of the memory map within the + * critical section, we could potentially accumulate an unbounded + * collection of memory data structures awaiting reclamation. + * + * Because the only thing we're currently protecting with RCU is the + * memory data structures, it's sufficient to break the critical section + * in this callback, which we know will get called every time the + * memory map is rearranged. + * + * (If we add anything else in the system that uses RCU to protect + * its data structures, we will need to implement some other mechanism + * to force TCG CPUs to exit the critical section, at which point this + * part of this callback might become unnecessary.) + * + * This pair matches cpu_exec's rcu_read_lock()/rcu_read_unlock(), which + * only protects cpu->as->dispatch. Since we know our caller is about + * to reload it, it's safe to split the critical section. + */ + rcu_read_unlock(); + rcu_read_lock(); + } +} +#endif + +void cpu_loop_exit(CPUState *cpu) +{ + cpu->current_tb = NULL; + siglongjmp(cpu->jmp_env, 1); +} + +void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) +{ + if (pc) { + cpu_restore_state(cpu, pc); + } + cpu->current_tb = NULL; + siglongjmp(cpu->jmp_env, 1); +} diff --git a/qemu/cpu-exec.c b/qemu/cpu-exec.c index 75694f3bb..bbfcbfb54 100644 --- a/qemu/cpu-exec.c +++ b/qemu/cpu-exec.c @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" +#include "qemu/osdep.h" #include "cpu.h" #include "trace.h" #include "disas/disas.h" @@ -25,9 +25,13 @@ #include "sysemu/qtest.h" #include "qemu/timer.h" #include "exec/address-spaces.h" -#include "exec/memory-internal.h" #include "qemu/rcu.h" #include "exec/tb-hash.h" +#include "exec/log.h" +#if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY) +#include "hw/i386/apic.h" +#endif +#include "sysemu/replay.h" /* -icount align implementation. */ @@ -128,57 +132,16 @@ static void init_delay_params(SyncClocks *sc, const CPUState *cpu) } #endif /* CONFIG USER ONLY */ -void cpu_loop_exit(CPUState *cpu) -{ - cpu->current_tb = NULL; - siglongjmp(cpu->jmp_env, 1); -} - -/* exit the current TB from a signal handler. The host registers are - restored in a state compatible with the CPU emulator - */ -#if defined(CONFIG_SOFTMMU) -void cpu_resume_from_signal(CPUState *cpu, void *puc) -{ - /* XXX: restore cpu registers saved in host registers */ - - cpu->exception_index = -1; - siglongjmp(cpu->jmp_env, 1); -} - -void cpu_reload_memory_map(CPUState *cpu) -{ - AddressSpaceDispatch *d; - - if (qemu_in_vcpu_thread()) { - /* Do not let the guest prolong the critical section as much as it - * as it desires. - * - * Currently, this is prevented by the I/O thread's periodinc kicking - * of the VCPU thread (iothread_requesting_mutex, qemu_cpu_kick_thread) - * but this will go away once TCG's execution moves out of the global - * mutex. - * - * This pair matches cpu_exec's rcu_read_lock()/rcu_read_unlock(), which - * only protects cpu->as->dispatch. Since we reload it below, we can - * split the critical section. - */ - rcu_read_unlock(); - rcu_read_lock(); - } - - /* The CPU and TLB are protected by the iothread lock. */ - d = atomic_rcu_read(&cpu->as->dispatch); - cpu->memory_dispatch = d; - tlb_flush(cpu, 1); -} -#endif - /* Execute a TB, and fix up the CPU state afterwards if necessary */ -static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, uint8_t *tb_ptr) +static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) { CPUArchState *env = cpu->env_ptr; uintptr_t next_tb; + uint8_t *tb_ptr = itb->tc_ptr; + + qemu_log_mask_and_addr(CPU_LOG_EXEC, itb->pc, + "Trace %p [" TARGET_FMT_lx "] %s\n", + itb->tc_ptr, itb->pc, lookup_symbol(itb->pc)); #if defined(DEBUG_DISAS) if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) { @@ -196,7 +159,7 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, uint8_t *tb_ptr) } #endif /* DEBUG_DISAS */ - cpu->can_do_io = 0; + cpu->can_do_io = !use_icount; next_tb = tcg_qemu_tb_exec(env, tb_ptr); cpu->can_do_io = 1; trace_exec_tb_exit((void *) (next_tb & ~TB_EXIT_MASK), @@ -209,6 +172,10 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, uint8_t *tb_ptr) */ CPUClass *cc = CPU_GET_CLASS(cpu); TranslationBlock *tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK); + qemu_log_mask_and_addr(CPU_LOG_EXEC, itb->pc, + "Stopped execution of TB chain before %p [" + TARGET_FMT_lx "] %s\n", + itb->tc_ptr, itb->pc, lookup_symbol(itb->pc)); if (cc->synchronize_from_tb) { cc->synchronize_from_tb(cpu, tb); } else { @@ -228,35 +195,32 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, uint8_t *tb_ptr) /* Execute the code without caching the generated code. An interpreter could be used if available. */ static void cpu_exec_nocache(CPUState *cpu, int max_cycles, - TranslationBlock *orig_tb) + TranslationBlock *orig_tb, bool ignore_icount) { TranslationBlock *tb; - target_ulong pc = orig_tb->pc; - target_ulong cs_base = orig_tb->cs_base; - uint64_t flags = orig_tb->flags; /* Should never happen. We only end up here when an existing TB is too long. */ if (max_cycles > CF_COUNT_MASK) max_cycles = CF_COUNT_MASK; - /* tb_gen_code can flush our orig_tb, invalidate it now */ - tb_phys_invalidate(orig_tb, -1); - tb = tb_gen_code(cpu, pc, cs_base, flags, - max_cycles | CF_NOCACHE); + tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, orig_tb->flags, + max_cycles | CF_NOCACHE + | (ignore_icount ? CF_IGNORE_ICOUNT : 0)); + tb->orig_tb = tcg_ctx.tb_ctx.tb_invalidated_flag ? NULL : orig_tb; cpu->current_tb = tb; /* execute the generated code */ trace_exec_tb_nocache(tb, tb->pc); - cpu_tb_exec(cpu, tb->tc_ptr); + cpu_tb_exec(cpu, tb); cpu->current_tb = NULL; tb_phys_invalidate(tb, -1); tb_free(tb); } -static TranslationBlock *tb_find_slow(CPUState *cpu, - target_ulong pc, - target_ulong cs_base, - uint64_t flags) +static TranslationBlock *tb_find_physical(CPUState *cpu, + target_ulong pc, + target_ulong cs_base, + uint64_t flags) { CPUArchState *env = (CPUArchState *)cpu->env_ptr; TranslationBlock *tb, **ptb1; @@ -273,8 +237,9 @@ static TranslationBlock *tb_find_slow(CPUState *cpu, ptb1 = &tcg_ctx.tb_ctx.tb_phys_hash[h]; for(;;) { tb = *ptb1; - if (!tb) - goto not_found; + if (!tb) { + return NULL; + } if (tb->pc == pc && tb->page_addr[0] == phys_page1 && tb->cs_base == cs_base && @@ -286,25 +251,59 @@ static TranslationBlock *tb_find_slow(CPUState *cpu, virt_page2 = (pc & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; phys_page2 = get_page_addr_code(env, virt_page2); - if (tb->page_addr[1] == phys_page2) - goto found; + if (tb->page_addr[1] == phys_page2) { + break; + } } else { - goto found; + break; } } ptb1 = &tb->phys_hash_next; } - not_found: - /* if no translated code available, then translate it now */ - tb = tb_gen_code(cpu, pc, cs_base, flags, 0); - found: - /* Move the last found TB to the head of the list */ - if (likely(*ptb1)) { - *ptb1 = tb->phys_hash_next; - tb->phys_hash_next = tcg_ctx.tb_ctx.tb_phys_hash[h]; - tcg_ctx.tb_ctx.tb_phys_hash[h] = tb; + /* Move the TB to the head of the list */ + *ptb1 = tb->phys_hash_next; + tb->phys_hash_next = tcg_ctx.tb_ctx.tb_phys_hash[h]; + tcg_ctx.tb_ctx.tb_phys_hash[h] = tb; + return tb; +} + +static TranslationBlock *tb_find_slow(CPUState *cpu, + target_ulong pc, + target_ulong cs_base, + uint64_t flags) +{ + TranslationBlock *tb; + + tb = tb_find_physical(cpu, pc, cs_base, flags); + if (tb) { + goto found; + } + +#ifdef CONFIG_USER_ONLY + /* mmap_lock is needed by tb_gen_code, and mmap_lock must be + * taken outside tb_lock. Since we're momentarily dropping + * tb_lock, there's a chance that our desired tb has been + * translated. + */ + tb_unlock(); + mmap_lock(); + tb_lock(); + tb = tb_find_physical(cpu, pc, cs_base, flags); + if (tb) { + mmap_unlock(); + goto found; } +#endif + + /* if no translated code available, then translate it now */ + tb = tb_gen_code(cpu, pc, cs_base, flags, 0); + +#ifdef CONFIG_USER_ONLY + mmap_unlock(); +#endif + +found: /* we add the TB in the virtual pc hash table */ cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb; return tb; @@ -345,8 +344,6 @@ static void cpu_handle_debug_exception(CPUState *cpu) /* main execution loop */ -volatile sig_atomic_t exit_request; - int cpu_exec(CPUState *cpu) { CPUClass *cc = CPU_GET_CLASS(cpu); @@ -356,34 +353,32 @@ int cpu_exec(CPUState *cpu) #endif int ret, interrupt_request; TranslationBlock *tb; - uint8_t *tc_ptr; uintptr_t next_tb; SyncClocks sc; - /* This must be volatile so it is not trashed by longjmp() */ - volatile bool have_tb_lock = false; + /* replay_interrupt may need current_cpu */ + current_cpu = cpu; if (cpu->halted) { +#if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY) + if ((cpu->interrupt_request & CPU_INTERRUPT_POLL) + && replay_interrupt()) { + apic_poll_irq(x86_cpu->apic_state); + cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL); + } +#endif if (!cpu_has_work(cpu)) { + current_cpu = NULL; return EXCP_HALTED; } cpu->halted = 0; } - current_cpu = cpu; - - /* As long as current_cpu is null, up to the assignment just above, - * requests by other threads to exit the execution loop are expected to - * be issued using the exit_request global. We must make sure that our - * evaluation of the global value is performed past the current_cpu - * value transition point, which requires a memory barrier as well as - * an instruction scheduling constraint on modern architectures. */ - smp_mb(); - + atomic_mb_set(&tcg_current_cpu, cpu); rcu_read_lock(); - if (unlikely(exit_request)) { + if (unlikely(atomic_mb_read(&exit_request))) { cpu->exit_request = 1; } @@ -421,10 +416,22 @@ int cpu_exec(CPUState *cpu) cpu->exception_index = -1; break; #else - cc->do_interrupt(cpu); - cpu->exception_index = -1; + if (replay_exception()) { + cc->do_interrupt(cpu); + cpu->exception_index = -1; + } else if (!replay_has_interrupt()) { + /* give a chance to iothread in replay mode */ + ret = EXCP_INTERRUPT; + break; + } #endif } + } else if (replay_has_exception() + && cpu->icount_decr.u16.low + cpu->icount_extra == 0) { + /* try to cause an exception pending in the log */ + cpu_exec_nocache(cpu, 1, tb_find_fast(cpu), true); + ret = -1; + break; } next_tb = 0; /* force lookup of first TB */ @@ -440,30 +447,40 @@ int cpu_exec(CPUState *cpu) cpu->exception_index = EXCP_DEBUG; cpu_loop_exit(cpu); } - if (interrupt_request & CPU_INTERRUPT_HALT) { + if (replay_mode == REPLAY_MODE_PLAY + && !replay_has_interrupt()) { + /* Do nothing */ + } else if (interrupt_request & CPU_INTERRUPT_HALT) { + replay_interrupt(); cpu->interrupt_request &= ~CPU_INTERRUPT_HALT; cpu->halted = 1; cpu->exception_index = EXCP_HLT; cpu_loop_exit(cpu); } #if defined(TARGET_I386) - if (interrupt_request & CPU_INTERRUPT_INIT) { + else if (interrupt_request & CPU_INTERRUPT_INIT) { + replay_interrupt(); cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0); do_cpu_init(x86_cpu); cpu->exception_index = EXCP_HALTED; cpu_loop_exit(cpu); } #else - if (interrupt_request & CPU_INTERRUPT_RESET) { + else if (interrupt_request & CPU_INTERRUPT_RESET) { + replay_interrupt(); cpu_reset(cpu); + cpu_loop_exit(cpu); } #endif /* The target hook has 3 exit conditions: False when the interrupt isn't processed, True when it is, and we should restart on a new TB, and via longjmp via cpu_loop_exit. */ - if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { - next_tb = 0; + else { + replay_interrupt(); + if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { + next_tb = 0; + } } /* Don't use the cached interrupt_request value, do_interrupt may have updated the EXITTB flag. */ @@ -474,13 +491,13 @@ int cpu_exec(CPUState *cpu) next_tb = 0; } } - if (unlikely(cpu->exit_request)) { + if (unlikely(cpu->exit_request + || replay_has_interrupt())) { cpu->exit_request = 0; cpu->exception_index = EXCP_INTERRUPT; cpu_loop_exit(cpu); } - spin_lock(&tcg_ctx.tb_ctx.tb_lock); - have_tb_lock = true; + tb_lock(); tb = tb_find_fast(cpu); /* Note: we do it here to avoid a gcc bug on Mac OS X when doing it in tb_find_slow */ @@ -491,31 +508,21 @@ int cpu_exec(CPUState *cpu) next_tb = 0; tcg_ctx.tb_ctx.tb_invalidated_flag = 0; } - if (qemu_loglevel_mask(CPU_LOG_EXEC)) { - qemu_log("Trace %p [" TARGET_FMT_lx "] %s\n", - tb->tc_ptr, tb->pc, lookup_symbol(tb->pc)); - } /* see if we can patch the calling TB. When the TB spans two pages, we cannot safely do a direct jump. */ - if (next_tb != 0 && tb->page_addr[1] == -1) { + if (next_tb != 0 && tb->page_addr[1] == -1 + && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) { tb_add_jump((TranslationBlock *)(next_tb & ~TB_EXIT_MASK), next_tb & TB_EXIT_MASK, tb); } - have_tb_lock = false; - spin_unlock(&tcg_ctx.tb_ctx.tb_lock); - - /* cpu_interrupt might be called while translating the - TB, but before it is linked into a potentially - infinite loop and becomes env->current_tb. Avoid - starting execution if there is a pending interrupt. */ - cpu->current_tb = tb; - barrier(); + tb_unlock(); if (likely(!cpu->exit_request)) { trace_exec_tb(tb, tb->pc); - tc_ptr = tb->tc_ptr; /* execute the generated code */ - next_tb = cpu_tb_exec(cpu, tc_ptr); + cpu->current_tb = tb; + next_tb = cpu_tb_exec(cpu, tb); + cpu->current_tb = NULL; switch (next_tb & TB_EXIT_MASK) { case TB_EXIT_REQUESTED: /* Something asked us to stop executing @@ -523,8 +530,12 @@ int cpu_exec(CPUState *cpu) * loop. Whatever requested the exit will also * have set something else (eg exit_request or * interrupt_request) which we will handle - * next time around the loop. + * next time around the loop. But we need to + * ensure the tcg_exit_req read in generated code + * comes before the next read of cpu->exit_request + * or cpu->interrupt_request. */ + smp_rmb(); next_tb = 0; break; case TB_EXIT_ICOUNT_EXPIRED: @@ -541,7 +552,7 @@ int cpu_exec(CPUState *cpu) if (insns_left > 0) { /* Execute remaining instructions. */ tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK); - cpu_exec_nocache(cpu, insns_left, tb); + cpu_exec_nocache(cpu, insns_left, tb, false); align_clocks(&sc, cpu); } cpu->exception_index = EXCP_INTERRUPT; @@ -554,7 +565,6 @@ int cpu_exec(CPUState *cpu) break; } } - cpu->current_tb = NULL; /* Try to align the host and virtual clocks if the guest is in advance */ align_clocks(&sc, cpu); @@ -562,19 +572,28 @@ int cpu_exec(CPUState *cpu) only be set by a memory fault) */ } /* for(;;) */ } else { - /* Reload env after longjmp - the compiler may have smashed all - * local variables as longjmp is marked 'noreturn'. */ +#if defined(__clang__) || !QEMU_GNUC_PREREQ(4, 6) + /* Some compilers wrongly smash all local variables after + * siglongjmp. There were bug reports for gcc 4.5.0 and clang. + * Reload essential local variables here for those compilers. + * Newer versions of gcc would complain about this code (-Wclobbered). */ cpu = current_cpu; cc = CPU_GET_CLASS(cpu); - cpu->can_do_io = 1; #ifdef TARGET_I386 x86_cpu = X86_CPU(cpu); env = &x86_cpu->env; #endif - if (have_tb_lock) { - spin_unlock(&tcg_ctx.tb_ctx.tb_lock); - have_tb_lock = false; - } +#else /* buggy compiler */ + /* Assert that the compiler does not smash local variables. */ + g_assert(cpu == current_cpu); + g_assert(cc == CPU_GET_CLASS(cpu)); +#ifdef TARGET_I386 + g_assert(x86_cpu == X86_CPU(cpu)); + g_assert(env == &x86_cpu->env); +#endif +#endif /* buggy compiler */ + cpu->can_do_io = 1; + tb_lock_reset(); } } /* for(;;) */ @@ -583,5 +602,8 @@ int cpu_exec(CPUState *cpu) /* fail safe : never use current_cpu outside cpu_exec() */ current_cpu = NULL; + + /* Does not need atomic_mb_set because a spurious wakeup is okay. */ + atomic_set(&tcg_current_cpu, NULL); return ret; } diff --git a/qemu/cpus.c b/qemu/cpus.c index a822ce3d8..cbeb1f613 100644 --- a/qemu/cpus.c +++ b/qemu/cpus.c @@ -23,12 +23,13 @@ */ /* Needed early for CONFIG_BSD etc. */ -#include "config-host.h" +#include "qemu/osdep.h" #include "monitor/monitor.h" #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "sysemu/sysemu.h" +#include "sysemu/block-backend.h" #include "exec/gdbstub.h" #include "sysemu/dma.h" #include "sysemu/kvm.h" @@ -42,6 +43,7 @@ #include "qemu/seqlock.h" #include "qapi-event.h" #include "hw/nmi.h" +#include "sysemu/replay.h" #ifndef _WIN32 #include "qemu/compatfd.h" @@ -69,6 +71,14 @@ static CPUState *next_cpu; int64_t max_delay; int64_t max_advance; +/* vcpu throttling controls */ +static QEMUTimer *throttle_timer; +static unsigned int throttle_percentage; + +#define CPU_THROTTLE_PCT_MIN 1 +#define CPU_THROTTLE_PCT_MAX 99 +#define CPU_THROTTLE_TIMESLICE_NS 10000000 + bool cpu_is_stopped(CPUState *cpu) { return cpu->stopped || !runstate_is_running(); @@ -145,7 +155,7 @@ int64_t cpu_get_icount_raw(void) icount = timers_state.qemu_icount; if (cpu) { - if (!cpu_can_do_io(cpu)) { + if (!cpu->can_do_io) { fprintf(stderr, "Bad icount read\n"); exit(1); } @@ -191,7 +201,7 @@ int64_t cpu_get_ticks(void) ticks = timers_state.cpu_ticks_offset; if (timers_state.cpu_ticks_enabled) { - ticks += cpu_get_real_ticks(); + ticks += cpu_get_host_ticks(); } if (timers_state.cpu_ticks_prev > ticks) { @@ -239,7 +249,7 @@ void cpu_enable_ticks(void) /* Here, the really thing protected by seqlock is cpu_clock_offset. */ seqlock_write_lock(&timers_state.vm_clock_seqlock); if (!timers_state.cpu_ticks_enabled) { - timers_state.cpu_ticks_offset -= cpu_get_real_ticks(); + timers_state.cpu_ticks_offset -= cpu_get_host_ticks(); timers_state.cpu_clock_offset -= get_clock(); timers_state.cpu_ticks_enabled = 1; } @@ -255,7 +265,7 @@ void cpu_disable_ticks(void) /* Here, the really thing protected by seqlock is cpu_clock_offset. */ seqlock_write_lock(&timers_state.vm_clock_seqlock); if (timers_state.cpu_ticks_enabled) { - timers_state.cpu_ticks_offset += cpu_get_real_ticks(); + timers_state.cpu_ticks_offset += cpu_get_host_ticks(); timers_state.cpu_clock_offset = cpu_get_clock_locked(); timers_state.cpu_ticks_enabled = 0; } @@ -266,7 +276,7 @@ void cpu_disable_ticks(void) fairly approximate, so ignore small variation. When the guest is idle real and virtual time will be aligned in the IO wait loop. */ -#define ICOUNT_WOBBLE (get_ticks_per_sec() / 10) +#define ICOUNT_WOBBLE (NANOSECONDS_PER_SECOND / 10) static void icount_adjust(void) { @@ -317,7 +327,7 @@ static void icount_adjust_vm(void *opaque) { timer_mod(icount_vm_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - get_ticks_per_sec() / 10); + NANOSECONDS_PER_SECOND / 10); icount_adjust(); } @@ -326,18 +336,27 @@ static int64_t qemu_icount_round(int64_t count) return (count + (1 << icount_time_shift) - 1) >> icount_time_shift; } -static void icount_warp_rt(void *opaque) +static void icount_warp_rt(void) { + unsigned seq; + int64_t warp_start; + /* The icount_warp_timer is rescheduled soon after vm_clock_warp_start * changes from -1 to another value, so the race here is okay. */ - if (atomic_read(&vm_clock_warp_start) == -1) { + do { + seq = seqlock_read_begin(&timers_state.vm_clock_seqlock); + warp_start = vm_clock_warp_start; + } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, seq)); + + if (warp_start == -1) { return; } seqlock_write_lock(&timers_state.vm_clock_seqlock); if (runstate_is_running()) { - int64_t clock = cpu_get_clock_locked(); + int64_t clock = REPLAY_CLOCK(REPLAY_CLOCK_VIRTUAL_RT, + cpu_get_clock_locked()); int64_t warp_delta; warp_delta = clock - vm_clock_warp_start; @@ -360,6 +379,14 @@ static void icount_warp_rt(void *opaque) } } +static void icount_timer_cb(void *opaque) +{ + /* No need for a checkpoint because the timer already synchronizes + * with CHECKPOINT_CLOCK_VIRTUAL_RT. + */ + icount_warp_rt(); +} + void qtest_clock_warp(int64_t dest) { int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); @@ -381,39 +408,34 @@ void qtest_clock_warp(int64_t dest) qemu_clock_notify(QEMU_CLOCK_VIRTUAL); } -void qemu_clock_warp(QEMUClockType type) +void qemu_start_warp_timer(void) { int64_t clock; int64_t deadline; - /* - * There are too many global variables to make the "warp" behavior - * applicable to other clocks. But a clock argument removes the - * need for if statements all over the place. + if (!use_icount) { + return; + } + + /* Nothing to do if the VM is stopped: QEMU_CLOCK_VIRTUAL timers + * do not fire, so computing the deadline does not make sense. */ - if (type != QEMU_CLOCK_VIRTUAL || !use_icount) { + if (!runstate_is_running()) { return; } - if (icount_sleep) { - /* - * If the CPUs have been sleeping, advance QEMU_CLOCK_VIRTUAL timer now. - * This ensures that the deadline for the timer is computed correctly - * below. - * This also makes sure that the insn counter is synchronized before - * the CPU starts running, in case the CPU is woken by an event other - * than the earliest QEMU_CLOCK_VIRTUAL timer. - */ - icount_warp_rt(NULL); - timer_del(icount_warp_timer); + /* warp clock deterministically in record/replay mode */ + if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_START)) { + return; } + if (!all_cpu_threads_idle()) { return; } if (qtest_enabled()) { /* When testing, qtest commands advance icount. */ - return; + return; } /* We want to use the earliest deadline from ALL vm_clocks */ @@ -469,6 +491,28 @@ void qemu_clock_warp(QEMUClockType type) } } +static void qemu_account_warp_timer(void) +{ + if (!use_icount || !icount_sleep) { + return; + } + + /* Nothing to do if the VM is stopped: QEMU_CLOCK_VIRTUAL timers + * do not fire, so computing the deadline does not make sense. + */ + if (!runstate_is_running()) { + return; + } + + /* warp clock deterministically in record/replay mode */ + if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_ACCOUNT)) { + return; + } + + timer_del(icount_warp_timer); + icount_warp_rt(); +} + static bool icount_state_needed(void *opaque) { return use_icount; @@ -505,10 +549,80 @@ static const VMStateDescription vmstate_timers = { } }; +static void cpu_throttle_thread(void *opaque) +{ + CPUState *cpu = opaque; + double pct; + double throttle_ratio; + long sleeptime_ns; + + if (!cpu_throttle_get_percentage()) { + return; + } + + pct = (double)cpu_throttle_get_percentage()/100; + throttle_ratio = pct / (1 - pct); + sleeptime_ns = (long)(throttle_ratio * CPU_THROTTLE_TIMESLICE_NS); + + qemu_mutex_unlock_iothread(); + atomic_set(&cpu->throttle_thread_scheduled, 0); + g_usleep(sleeptime_ns / 1000); /* Convert ns to us for usleep call */ + qemu_mutex_lock_iothread(); +} + +static void cpu_throttle_timer_tick(void *opaque) +{ + CPUState *cpu; + double pct; + + /* Stop the timer if needed */ + if (!cpu_throttle_get_percentage()) { + return; + } + CPU_FOREACH(cpu) { + if (!atomic_xchg(&cpu->throttle_thread_scheduled, 1)) { + async_run_on_cpu(cpu, cpu_throttle_thread, cpu); + } + } + + pct = (double)cpu_throttle_get_percentage()/100; + timer_mod(throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) + + CPU_THROTTLE_TIMESLICE_NS / (1-pct)); +} + +void cpu_throttle_set(int new_throttle_pct) +{ + /* Ensure throttle percentage is within valid range */ + new_throttle_pct = MIN(new_throttle_pct, CPU_THROTTLE_PCT_MAX); + new_throttle_pct = MAX(new_throttle_pct, CPU_THROTTLE_PCT_MIN); + + atomic_set(&throttle_percentage, new_throttle_pct); + + timer_mod(throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) + + CPU_THROTTLE_TIMESLICE_NS); +} + +void cpu_throttle_stop(void) +{ + atomic_set(&throttle_percentage, 0); +} + +bool cpu_throttle_active(void) +{ + return (cpu_throttle_get_percentage() != 0); +} + +int cpu_throttle_get_percentage(void) +{ + return atomic_read(&throttle_percentage); +} + void cpu_ticks_init(void) { seqlock_init(&timers_state.vm_clock_seqlock, NULL); vmstate_register(NULL, 0, &vmstate_timers, &timers_state); + throttle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT, + cpu_throttle_timer_tick, NULL); } void configure_icount(QemuOpts *opts, Error **errp) @@ -527,13 +641,13 @@ void configure_icount(QemuOpts *opts, Error **errp) icount_sleep = qemu_opt_get_bool(opts, "sleep", true); if (icount_sleep) { icount_warp_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT, - icount_warp_rt, NULL); + icount_timer_cb, NULL); } icount_align_option = qemu_opt_get_bool(opts, "align", false); if (icount_align_option && !icount_sleep) { - error_setg(errp, "align=on and sleep=no are incompatible"); + error_setg(errp, "align=on and sleep=off are incompatible"); } if (strcmp(option, "auto") != 0) { errno = 0; @@ -546,7 +660,7 @@ void configure_icount(QemuOpts *opts, Error **errp) } else if (icount_align_option) { error_setg(errp, "shift=auto and align=on are incompatible"); } else if (!icount_sleep) { - error_setg(errp, "shift=auto and sleep=no are incompatible"); + error_setg(errp, "shift=auto and sleep=off are incompatible"); } use_icount = 2; @@ -568,7 +682,7 @@ void configure_icount(QemuOpts *opts, Error **errp) icount_adjust_vm, NULL); timer_mod(icount_vm_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - get_ticks_per_sec() / 10); + NANOSECONDS_PER_SECOND / 10); } /***********************************************************/ @@ -616,15 +730,6 @@ void cpu_synchronize_all_post_init(void) } } -void cpu_clean_all_dirty(void) -{ - CPUState *cpu; - - CPU_FOREACH(cpu) { - cpu_clean_state(cpu); - } -} - static int do_vm_stop(RunState state) { int ret = 0; @@ -638,7 +743,7 @@ static int do_vm_stop(RunState state) } bdrv_drain_all(); - ret = bdrv_flush_all(); + ret = blk_flush_all(); return ret; } @@ -661,14 +766,6 @@ static void cpu_handle_guest_debug(CPUState *cpu) cpu->stopped = true; } -static void cpu_signal(int sig) -{ - if (current_cpu) { - cpu_exit(current_cpu); - } - exit_request = 1; -} - #ifdef CONFIG_LINUX static void sigbus_reraise(void) { @@ -781,29 +878,11 @@ static void qemu_kvm_init_cpu_signals(CPUState *cpu) } } -static void qemu_tcg_init_cpu_signals(void) -{ - sigset_t set; - struct sigaction sigact; - - memset(&sigact, 0, sizeof(sigact)); - sigact.sa_handler = cpu_signal; - sigaction(SIG_IPI, &sigact, NULL); - - sigemptyset(&set); - sigaddset(&set, SIG_IPI); - pthread_sigmask(SIG_UNBLOCK, &set, NULL); -} - #else /* _WIN32 */ static void qemu_kvm_init_cpu_signals(CPUState *cpu) { abort(); } - -static void qemu_tcg_init_cpu_signals(void) -{ -} #endif /* _WIN32 */ static QemuMutex qemu_global_mutex; @@ -812,9 +891,6 @@ static unsigned iothread_requesting_mutex; static QemuThread io_thread; -static QemuThread *tcg_cpu_thread; -static QemuCond *tcg_halt_cond; - /* cpu creation */ static QemuCond qemu_cpu_cond; /* system init */ @@ -845,6 +921,8 @@ void run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data) wi.func = func; wi.data = data; wi.free = false; + + qemu_mutex_lock(&cpu->work_mutex); if (cpu->queued_work_first == NULL) { cpu->queued_work_first = &wi; } else { @@ -853,9 +931,10 @@ void run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data) cpu->queued_work_last = &wi; wi.next = NULL; wi.done = false; + qemu_mutex_unlock(&cpu->work_mutex); qemu_cpu_kick(cpu); - while (!wi.done) { + while (!atomic_mb_read(&wi.done)) { CPUState *self_cpu = current_cpu; qemu_cond_wait(&qemu_work_cond, &qemu_global_mutex); @@ -876,6 +955,8 @@ void async_run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data) wi->func = func; wi->data = data; wi->free = true; + + qemu_mutex_lock(&cpu->work_mutex); if (cpu->queued_work_first == NULL) { cpu->queued_work_first = wi; } else { @@ -884,6 +965,7 @@ void async_run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data) cpu->queued_work_last = wi; wi->next = NULL; wi->done = false; + qemu_mutex_unlock(&cpu->work_mutex); qemu_cpu_kick(cpu); } @@ -896,15 +978,23 @@ static void flush_queued_work(CPUState *cpu) return; } - while ((wi = cpu->queued_work_first)) { + qemu_mutex_lock(&cpu->work_mutex); + while (cpu->queued_work_first != NULL) { + wi = cpu->queued_work_first; cpu->queued_work_first = wi->next; + if (!cpu->queued_work_first) { + cpu->queued_work_last = NULL; + } + qemu_mutex_unlock(&cpu->work_mutex); wi->func(wi->data); - wi->done = true; + qemu_mutex_lock(&cpu->work_mutex); if (wi->free) { g_free(wi); + } else { + atomic_mb_set(&wi->done, true); } } - cpu->queued_work_last = NULL; + qemu_mutex_unlock(&cpu->work_mutex); qemu_cond_broadcast(&qemu_work_cond); } @@ -913,21 +1003,16 @@ static void qemu_wait_io_event_common(CPUState *cpu) if (cpu->stop) { cpu->stop = false; cpu->stopped = true; - qemu_cond_signal(&qemu_pause_cond); + qemu_cond_broadcast(&qemu_pause_cond); } flush_queued_work(cpu); cpu->thread_kicked = false; } -static void qemu_tcg_wait_io_event(void) +static void qemu_tcg_wait_io_event(CPUState *cpu) { - CPUState *cpu; - while (all_cpu_threads_idle()) { - /* Start accounting real time to the virtual clock if the CPUs - are idle. */ - qemu_clock_warp(QEMU_CLOCK_VIRTUAL); - qemu_cond_wait(tcg_halt_cond, &qemu_global_mutex); + qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex); } while (iothread_requesting_mutex) { @@ -1041,7 +1126,6 @@ static void *qemu_tcg_cpu_thread_fn(void *arg) rcu_register_thread(); qemu_mutex_lock_iothread(); - qemu_tcg_init_cpu_signals(); qemu_thread_get_self(cpu->thread); CPU_FOREACH(cpu) { @@ -1053,7 +1137,7 @@ static void *qemu_tcg_cpu_thread_fn(void *arg) /* wait for initial kick-off after machine start */ while (first_cpu->stopped) { - qemu_cond_wait(tcg_halt_cond, &qemu_global_mutex); + qemu_cond_wait(first_cpu->halt_cond, &qemu_global_mutex); /* process any pending work */ CPU_FOREACH(cpu) { @@ -1062,7 +1146,7 @@ static void *qemu_tcg_cpu_thread_fn(void *arg) } /* process any pending work */ - exit_request = 1; + atomic_mb_set(&exit_request, 1); while (1) { tcg_exec_all(); @@ -1074,7 +1158,7 @@ static void *qemu_tcg_cpu_thread_fn(void *arg) qemu_clock_notify(QEMU_CLOCK_VIRTUAL); } } - qemu_tcg_wait_io_event(); + qemu_tcg_wait_io_event(QTAILQ_FIRST(&cpus)); } return NULL; @@ -1085,61 +1169,47 @@ static void qemu_cpu_kick_thread(CPUState *cpu) #ifndef _WIN32 int err; + if (cpu->thread_kicked) { + return; + } + cpu->thread_kicked = true; err = pthread_kill(cpu->thread->thread, SIG_IPI); if (err) { fprintf(stderr, "qemu:%s: %s", __func__, strerror(err)); exit(1); } #else /* _WIN32 */ - if (!qemu_cpu_is_self(cpu)) { - CONTEXT tcgContext; - - if (SuspendThread(cpu->hThread) == (DWORD)-1) { - fprintf(stderr, "qemu:%s: GetLastError:%lu\n", __func__, - GetLastError()); - exit(1); - } - - /* On multi-core systems, we are not sure that the thread is actually - * suspended until we can get the context. - */ - tcgContext.ContextFlags = CONTEXT_CONTROL; - while (GetThreadContext(cpu->hThread, &tcgContext) != 0) { - continue; - } - - cpu_signal(0); + abort(); +#endif +} - if (ResumeThread(cpu->hThread) == (DWORD)-1) { - fprintf(stderr, "qemu:%s: GetLastError:%lu\n", __func__, - GetLastError()); - exit(1); - } +static void qemu_cpu_kick_no_halt(void) +{ + CPUState *cpu; + /* Ensure whatever caused the exit has reached the CPU threads before + * writing exit_request. + */ + atomic_mb_set(&exit_request, 1); + cpu = atomic_mb_read(&tcg_current_cpu); + if (cpu) { + cpu_exit(cpu); } -#endif } void qemu_cpu_kick(CPUState *cpu) { qemu_cond_broadcast(cpu->halt_cond); - if (!tcg_enabled() && !cpu->thread_kicked) { + if (tcg_enabled()) { + qemu_cpu_kick_no_halt(); + } else { qemu_cpu_kick_thread(cpu); - cpu->thread_kicked = true; } } void qemu_cpu_kick_self(void) { -#ifndef _WIN32 assert(current_cpu); - - if (!current_cpu->thread_kicked) { - qemu_cpu_kick_thread(current_cpu); - current_cpu->thread_kicked = true; - } -#else - abort(); -#endif + qemu_cpu_kick_thread(current_cpu); } bool qemu_cpu_is_self(CPUState *cpu) @@ -1166,12 +1236,12 @@ void qemu_mutex_lock_iothread(void) * TCG code execution. */ if (!tcg_enabled() || qemu_in_vcpu_thread() || - !first_cpu || !first_cpu->thread) { + !first_cpu || !first_cpu->created) { qemu_mutex_lock(&qemu_global_mutex); atomic_dec(&iothread_requesting_mutex); } else { if (qemu_mutex_trylock(&qemu_global_mutex)) { - qemu_cpu_kick_thread(first_cpu); + qemu_cpu_kick_no_halt(); qemu_mutex_lock(&qemu_global_mutex); } atomic_dec(&iothread_requesting_mutex); @@ -1251,8 +1321,8 @@ void resume_all_vcpus(void) static void qemu_tcg_init_vcpu(CPUState *cpu) { char thread_name[VCPU_THREAD_NAME_SIZE]; - - tcg_cpu_address_space_init(cpu, cpu->as); + static QemuCond *tcg_halt_cond; + static QemuThread *tcg_cpu_thread; /* share a single thread for all cpus with TCG */ if (!tcg_cpu_thread) { @@ -1314,6 +1384,17 @@ void qemu_init_vcpu(CPUState *cpu) cpu->nr_cores = smp_cores; cpu->nr_threads = smp_threads; cpu->stopped = true; + + if (!cpu->as) { + /* If the target cpu hasn't set up any address spaces itself, + * give it the default one. + */ + AddressSpace *as = address_space_init_shareable(cpu->memory, + "cpu-memory"); + cpu->num_ases = 1; + cpu_address_space_init(cpu, as, 0); + } + if (kvm_enabled()) { qemu_kvm_start_vcpu(cpu); } else if (tcg_enabled()) { @@ -1329,7 +1410,7 @@ void cpu_stop_current(void) current_cpu->stop = false; current_cpu->stopped = true; cpu_exit(current_cpu); - qemu_cond_signal(&qemu_pause_cond); + qemu_cond_broadcast(&qemu_pause_cond); } } @@ -1357,9 +1438,33 @@ int vm_stop_force_state(RunState state) return vm_stop(state); } else { runstate_set(state); + + bdrv_drain_all(); /* Make sure to return an error if the flush in a previous vm_stop() * failed. */ - return bdrv_flush_all(); + return blk_flush_all(); + } +} + +static int64_t tcg_get_icount_limit(void) +{ + int64_t deadline; + + if (replay_mode != REPLAY_MODE_PLAY) { + deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL); + + /* Maintain prior (possibly buggy) behaviour where if no deadline + * was set (as there is no QEMU_CLOCK_VIRTUAL timer) or it is more than + * INT32_MAX nanoseconds ahead, we still use INT32_MAX + * nanoseconds. + */ + if ((deadline < 0) || (deadline > INT32_MAX)) { + deadline = INT32_MAX; + } + + return qemu_icount_round(deadline); + } else { + return replay_get_instructions(); } } @@ -1375,24 +1480,12 @@ static int tcg_cpu_exec(CPUState *cpu) #endif if (use_icount) { int64_t count; - int64_t deadline; int decr; timers_state.qemu_icount -= (cpu->icount_decr.u16.low + cpu->icount_extra); cpu->icount_decr.u16.low = 0; cpu->icount_extra = 0; - deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL); - - /* Maintain prior (possibly buggy) behaviour where if no deadline - * was set (as there is no QEMU_CLOCK_VIRTUAL timer) or it is more than - * INT32_MAX nanoseconds ahead, we still use INT32_MAX - * nanoseconds. - */ - if ((deadline < 0) || (deadline > INT32_MAX)) { - deadline = INT32_MAX; - } - - count = qemu_icount_round(deadline); + count = tcg_get_icount_limit(); timers_state.qemu_icount += count; decr = (count > 0xffff) ? 0xffff : count; count -= decr; @@ -1410,6 +1503,7 @@ static int tcg_cpu_exec(CPUState *cpu) + cpu->icount_extra); cpu->icount_decr.u32 = 0; cpu->icount_extra = 0; + replay_account_executed_instructions(); } return ret; } @@ -1419,7 +1513,7 @@ static void tcg_exec_all(void) int r; /* Account partial waits to QEMU_CLOCK_VIRTUAL. */ - qemu_clock_warp(QEMU_CLOCK_VIRTUAL); + qemu_account_warp_timer(); if (next_cpu == NULL) { next_cpu = first_cpu; @@ -1440,7 +1534,9 @@ static void tcg_exec_all(void) break; } } - exit_request = 0; + + /* Pairs with smp_wmb in qemu_cpu_kick. */ + atomic_mb_set(&exit_request, 0); } void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg) @@ -1485,22 +1581,23 @@ CpuInfoList *qmp_query_cpus(Error **errp) info->value->qom_path = object_get_canonical_path(OBJECT(cpu)); info->value->thread_id = cpu->thread_id; #if defined(TARGET_I386) - info->value->has_pc = true; - info->value->pc = env->eip + env->segs[R_CS].base; + info->value->arch = CPU_INFO_ARCH_X86; + info->value->u.x86.pc = env->eip + env->segs[R_CS].base; #elif defined(TARGET_PPC) - info->value->has_nip = true; - info->value->nip = env->nip; + info->value->arch = CPU_INFO_ARCH_PPC; + info->value->u.ppc.nip = env->nip; #elif defined(TARGET_SPARC) - info->value->has_pc = true; - info->value->pc = env->pc; - info->value->has_npc = true; - info->value->npc = env->npc; + info->value->arch = CPU_INFO_ARCH_SPARC; + info->value->u.q_sparc.pc = env->pc; + info->value->u.q_sparc.npc = env->npc; #elif defined(TARGET_MIPS) - info->value->has_PC = true; - info->value->PC = env->active_tc.PC; + info->value->arch = CPU_INFO_ARCH_MIPS; + info->value->u.q_mips.PC = env->active_tc.PC; #elif defined(TARGET_TRICORE) - info->value->has_PC = true; - info->value->PC = env->PC; + info->value->arch = CPU_INFO_ARCH_TRICORE; + info->value->u.tricore.PC = env->PC; +#else + info->value->arch = CPU_INFO_ARCH_OTHER; #endif /* XXX: waiting for the qapi to support GSList */ diff --git a/qemu/cputlb.c b/qemu/cputlb.c index a50608676..466663b56 100644 --- a/qemu/cputlb.c +++ b/qemu/cputlb.c @@ -17,7 +17,7 @@ * License along with this library; if not, see . */ -#include "config.h" +#include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" #include "exec/memory.h" @@ -30,8 +30,30 @@ #include "exec/ram_addr.h" #include "tcg/tcg.h" -//#define DEBUG_TLB -//#define DEBUG_TLB_CHECK +/* DEBUG defines, enable DEBUG_TLB_LOG to log to the CPU_LOG_MMU target */ +/* #define DEBUG_TLB */ +/* #define DEBUG_TLB_LOG */ + +#ifdef DEBUG_TLB +# define DEBUG_TLB_GATE 1 +# ifdef DEBUG_TLB_LOG +# define DEBUG_TLB_LOG_GATE 1 +# else +# define DEBUG_TLB_LOG_GATE 0 +# endif +#else +# define DEBUG_TLB_GATE 0 +# define DEBUG_TLB_LOG_GATE 0 +#endif + +#define tlb_debug(fmt, ...) do { \ + if (DEBUG_TLB_LOG_GATE) { \ + qemu_log_mask(CPU_LOG_MMU, "%s: " fmt, __func__, \ + ## __VA_ARGS__); \ + } else if (DEBUG_TLB_GATE) { \ + fprintf(stderr, "%s: " fmt, __func__, ## __VA_ARGS__); \ + } \ +} while (0) /* statistics */ int tlb_flush_count; @@ -52,9 +74,8 @@ void tlb_flush(CPUState *cpu, int flush_global) { CPUArchState *env = cpu->env_ptr; -#if defined(DEBUG_TLB) - printf("tlb_flush:\n"); -#endif + tlb_debug("(%d)\n", flush_global); + /* must reset current TB so that interrupts cannot modify the links while we are modifying them */ cpu->current_tb = NULL; @@ -69,6 +90,39 @@ void tlb_flush(CPUState *cpu, int flush_global) tlb_flush_count++; } +static inline void v_tlb_flush_by_mmuidx(CPUState *cpu, va_list argp) +{ + CPUArchState *env = cpu->env_ptr; + + tlb_debug("start\n"); + /* must reset current TB so that interrupts cannot modify the + links while we are modifying them */ + cpu->current_tb = NULL; + + for (;;) { + int mmu_idx = va_arg(argp, int); + + if (mmu_idx < 0) { + break; + } + + tlb_debug("%d\n", mmu_idx); + + memset(env->tlb_table[mmu_idx], -1, sizeof(env->tlb_table[0])); + memset(env->tlb_v_table[mmu_idx], -1, sizeof(env->tlb_v_table[0])); + } + + memset(cpu->tb_jmp_cache, 0, sizeof(cpu->tb_jmp_cache)); +} + +void tlb_flush_by_mmuidx(CPUState *cpu, ...) +{ + va_list argp; + va_start(argp, cpu); + v_tlb_flush_by_mmuidx(cpu, argp); + va_end(argp); +} + static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr) { if (addr == (tlb_entry->addr_read & @@ -87,16 +141,14 @@ void tlb_flush_page(CPUState *cpu, target_ulong addr) int i; int mmu_idx; -#if defined(DEBUG_TLB) - printf("tlb_flush_page: " TARGET_FMT_lx "\n", addr); -#endif + tlb_debug("page :" TARGET_FMT_lx "\n", addr); + /* Check if we need to flush due to large pages. */ if ((addr & env->tlb_flush_mask) == env->tlb_flush_addr) { -#if defined(DEBUG_TLB) - printf("tlb_flush_page: forced full flush (" - TARGET_FMT_lx "/" TARGET_FMT_lx ")\n", - env->tlb_flush_addr, env->tlb_flush_mask); -#endif + tlb_debug("forcing full flush (" + TARGET_FMT_lx "/" TARGET_FMT_lx ")\n", + env->tlb_flush_addr, env->tlb_flush_mask); + tlb_flush(cpu, 1); return; } @@ -121,6 +173,54 @@ void tlb_flush_page(CPUState *cpu, target_ulong addr) tb_flush_jmp_cache(cpu, addr); } +void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, ...) +{ + CPUArchState *env = cpu->env_ptr; + int i, k; + va_list argp; + + va_start(argp, addr); + + tlb_debug("addr "TARGET_FMT_lx"\n", addr); + + /* Check if we need to flush due to large pages. */ + if ((addr & env->tlb_flush_mask) == env->tlb_flush_addr) { + tlb_debug("forced full flush (" + TARGET_FMT_lx "/" TARGET_FMT_lx ")\n", + env->tlb_flush_addr, env->tlb_flush_mask); + + v_tlb_flush_by_mmuidx(cpu, argp); + va_end(argp); + return; + } + /* must reset current TB so that interrupts cannot modify the + links while we are modifying them */ + cpu->current_tb = NULL; + + addr &= TARGET_PAGE_MASK; + i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + + for (;;) { + int mmu_idx = va_arg(argp, int); + + if (mmu_idx < 0) { + break; + } + + tlb_debug("idx %d\n", mmu_idx); + + tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr); + + /* check whether there are vltb entries that need to be flushed */ + for (k = 0; k < CPU_VTLB_SIZE; k++) { + tlb_flush_entry(&env->tlb_v_table[mmu_idx][k], addr); + } + } + va_end(argp); + + tb_flush_jmp_cache(cpu, addr); +} + /* update the TLBs so that writes to code in the virtual page 'addr' can be detected */ void tlb_protect_code(ram_addr_t ram_addr) @@ -165,27 +265,24 @@ static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr) return ram_addr; } -void cpu_tlb_reset_dirty_all(ram_addr_t start1, ram_addr_t length) +void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length) { - CPUState *cpu; CPUArchState *env; - CPU_FOREACH(cpu) { - int mmu_idx; + int mmu_idx; - env = cpu->env_ptr; - for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { - unsigned int i; + env = cpu->env_ptr; + for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { + unsigned int i; - for (i = 0; i < CPU_TLB_SIZE; i++) { - tlb_reset_dirty_range(&env->tlb_table[mmu_idx][i], - start1, length); - } + for (i = 0; i < CPU_TLB_SIZE; i++) { + tlb_reset_dirty_range(&env->tlb_table[mmu_idx][i], + start1, length); + } - for (i = 0; i < CPU_VTLB_SIZE; i++) { - tlb_reset_dirty_range(&env->tlb_v_table[mmu_idx][i], - start1, length); - } + for (i = 0; i < CPU_VTLB_SIZE; i++) { + tlb_reset_dirty_range(&env->tlb_v_table[mmu_idx][i], + start1, length); } } } @@ -199,8 +296,9 @@ static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, target_ulong vaddr) /* update the TLB corresponding to virtual page vaddr so that it is no longer dirty */ -void tlb_set_dirty(CPUArchState *env, target_ulong vaddr) +void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) { + CPUArchState *env = cpu->env_ptr; int i; int mmu_idx; @@ -261,6 +359,7 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, CPUTLBEntry *te; hwaddr iotlb, xlat, sz; unsigned vidx = env->vtlb_index++ % CPU_VTLB_SIZE; + int asidx = cpu_asidx_from_attrs(cpu, attrs); assert(size >= TARGET_PAGE_SIZE); if (size != TARGET_PAGE_SIZE) { @@ -268,15 +367,12 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, } sz = size; - section = address_space_translate_for_iotlb(cpu, paddr, &xlat, &sz); + section = address_space_translate_for_iotlb(cpu, asidx, paddr, &xlat, &sz); assert(sz >= TARGET_PAGE_SIZE); -#if defined(DEBUG_TLB) - qemu_log_mask(CPU_LOG_MMU, - "tlb_set_page: vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx - " prot=%x idx=%d\n", - vaddr, paddr, prot, mmu_idx); -#endif + tlb_debug("vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx + " prot=%x idx=%d\n", + vaddr, paddr, prot, mmu_idx); address = vaddr; if (!memory_region_is_ram(section->mr) && !memory_region_is_romd(section->mr)) { @@ -320,8 +416,8 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, /* Write access calls the I/O callback. */ te->addr_write = address | TLB_MMIO; } else if (memory_region_is_ram(section->mr) - && cpu_physical_memory_is_clean(section->mr->ram_addr - + xlat)) { + && cpu_physical_memory_is_clean( + memory_region_get_ram_addr(section->mr) + xlat)) { te->addr_write = address | TLB_NOTDIRTY; } else { te->addr_write = address; @@ -353,15 +449,17 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr) void *p; MemoryRegion *mr; CPUState *cpu = ENV_GET_CPU(env1); + CPUIOTLBEntry *iotlbentry; page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - mmu_idx = cpu_mmu_index(env1); + mmu_idx = cpu_mmu_index(env1, true); if (unlikely(env1->tlb_table[mmu_idx][page_index].addr_code != (addr & TARGET_PAGE_MASK))) { cpu_ldub_code(env1, addr); } - pd = env1->iotlb[mmu_idx][page_index].addr & ~TARGET_PAGE_MASK; - mr = iotlb_to_region(cpu, pd); + iotlbentry = &env1->iotlb[mmu_idx][page_index]; + pd = iotlbentry->addr & ~TARGET_PAGE_MASK; + mr = iotlb_to_region(cpu, pd, iotlbentry->attrs); if (memory_region_is_unassigned(mr)) { CPUClass *cc = CPU_GET_CLASS(cpu); diff --git a/qemu/crypto/Makefile.objs b/qemu/crypto/Makefile.objs index b05013831..0737f4811 100644 --- a/qemu/crypto/Makefile.objs +++ b/qemu/crypto/Makefile.objs @@ -1,5 +1,30 @@ -util-obj-y += init.o -util-obj-y += hash.o -util-obj-y += aes.o -util-obj-y += desrfb.o -util-obj-y += cipher.o +crypto-obj-y = init.o +crypto-obj-y += hash.o +crypto-obj-y += aes.o +crypto-obj-y += desrfb.o +crypto-obj-y += cipher.o +crypto-obj-y += tlscreds.o +crypto-obj-y += tlscredsanon.o +crypto-obj-y += tlscredsx509.o +crypto-obj-y += tlssession.o +crypto-obj-y += secret.o +crypto-obj-$(CONFIG_GCRYPT) += random-gcrypt.o +crypto-obj-$(if $(CONFIG_GCRYPT),n,$(CONFIG_GNUTLS_RND)) += random-gnutls.o +crypto-obj-y += pbkdf.o +crypto-obj-$(CONFIG_NETTLE_KDF) += pbkdf-nettle.o +crypto-obj-$(if $(CONFIG_NETTLE_KDF),n,$(CONFIG_GCRYPT_KDF)) += pbkdf-gcrypt.o +crypto-obj-y += ivgen.o +crypto-obj-y += ivgen-essiv.o +crypto-obj-y += ivgen-plain.o +crypto-obj-y += ivgen-plain64.o +crypto-obj-y += afsplit.o +crypto-obj-y += xts.o +crypto-obj-y += block.o +crypto-obj-y += block-qcow.o +crypto-obj-y += block-luks.o + +# Let the userspace emulators avoid linking gnutls/etc +crypto-aes-obj-y = aes.o + +stub-obj-y += random-stub.o +stub-obj-y += pbkdf-stub.o diff --git a/qemu/crypto/aes.c b/qemu/crypto/aes.c index 244a388eb..3456eacd0 100644 --- a/qemu/crypto/aes.c +++ b/qemu/crypto/aes.c @@ -27,6 +27,7 @@ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "crypto/aes.h" diff --git a/qemu/crypto/afsplit.c b/qemu/crypto/afsplit.c new file mode 100644 index 000000000..8074913cd --- /dev/null +++ b/qemu/crypto/afsplit.c @@ -0,0 +1,158 @@ +/* + * QEMU Crypto anti forensic information splitter + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * Derived from cryptsetup package lib/luks1/af.c + * + * Copyright (C) 2004, Clemens Fruhwirth + * Copyright (C) 2009-2012, Red Hat, Inc. All rights reserved. + * + * This library 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 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 . + * + */ + +#include "qemu/osdep.h" +#include "crypto/afsplit.h" +#include "crypto/random.h" + + +static void qcrypto_afsplit_xor(size_t blocklen, + const uint8_t *in1, + const uint8_t *in2, + uint8_t *out) +{ + size_t i; + for (i = 0; i < blocklen; i++) { + out[i] = in1[i] ^ in2[i]; + } +} + + +static int qcrypto_afsplit_hash(QCryptoHashAlgorithm hash, + size_t blocklen, + uint8_t *block, + Error **errp) +{ + size_t digestlen = qcrypto_hash_digest_len(hash); + + size_t hashcount = blocklen / digestlen; + size_t finallen = blocklen % digestlen; + uint32_t i; + + if (finallen) { + hashcount++; + } else { + finallen = digestlen; + } + + for (i = 0; i < hashcount; i++) { + uint8_t *out = NULL; + size_t outlen = 0; + uint32_t iv = cpu_to_be32(i); + struct iovec in[] = { + { .iov_base = &iv, + .iov_len = sizeof(iv) }, + { .iov_base = block + (i * digestlen), + .iov_len = (i == (hashcount - 1)) ? finallen : digestlen }, + }; + + if (qcrypto_hash_bytesv(hash, + in, + G_N_ELEMENTS(in), + &out, &outlen, + errp) < 0) { + return -1; + } + + assert(outlen == digestlen); + memcpy(block + (i * digestlen), out, + (i == (hashcount - 1)) ? finallen : digestlen); + g_free(out); + } + + return 0; +} + + +int qcrypto_afsplit_encode(QCryptoHashAlgorithm hash, + size_t blocklen, + uint32_t stripes, + const uint8_t *in, + uint8_t *out, + Error **errp) +{ + uint8_t *block = g_new0(uint8_t, blocklen); + size_t i; + int ret = -1; + + for (i = 0; i < (stripes - 1); i++) { + if (qcrypto_random_bytes(out + (i * blocklen), blocklen, errp) < 0) { + goto cleanup; + } + + qcrypto_afsplit_xor(blocklen, + out + (i * blocklen), + block, + block); + if (qcrypto_afsplit_hash(hash, blocklen, block, + errp) < 0) { + goto cleanup; + } + } + qcrypto_afsplit_xor(blocklen, + in, + block, + out + (i * blocklen)); + ret = 0; + + cleanup: + g_free(block); + return ret; +} + + +int qcrypto_afsplit_decode(QCryptoHashAlgorithm hash, + size_t blocklen, + uint32_t stripes, + const uint8_t *in, + uint8_t *out, + Error **errp) +{ + uint8_t *block = g_new0(uint8_t, blocklen); + size_t i; + int ret = -1; + + for (i = 0; i < (stripes - 1); i++) { + qcrypto_afsplit_xor(blocklen, + in + (i * blocklen), + block, + block); + if (qcrypto_afsplit_hash(hash, blocklen, block, + errp) < 0) { + goto cleanup; + } + } + + qcrypto_afsplit_xor(blocklen, + in + (i * blocklen), + block, + out); + + ret = 0; + + cleanup: + g_free(block); + return ret; +} diff --git a/qemu/crypto/block-luks.c b/qemu/crypto/block-luks.c new file mode 100644 index 000000000..439f89230 --- /dev/null +++ b/qemu/crypto/block-luks.c @@ -0,0 +1,1329 @@ +/* + * QEMU Crypto block device encryption LUKS format + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" + +#include "crypto/block-luks.h" + +#include "crypto/hash.h" +#include "crypto/afsplit.h" +#include "crypto/pbkdf.h" +#include "crypto/secret.h" +#include "crypto/random.h" + +#ifdef CONFIG_UUID +#include +#endif + +#include "qemu/coroutine.h" + +/* + * Reference for the LUKS format implemented here is + * + * docs/on-disk-format.pdf + * + * in 'cryptsetup' package source code + * + * This file implements the 1.2.1 specification, dated + * Oct 16, 2011. + */ + +typedef struct QCryptoBlockLUKS QCryptoBlockLUKS; +typedef struct QCryptoBlockLUKSHeader QCryptoBlockLUKSHeader; +typedef struct QCryptoBlockLUKSKeySlot QCryptoBlockLUKSKeySlot; + + +/* The following constants are all defined by the LUKS spec */ +#define QCRYPTO_BLOCK_LUKS_VERSION 1 + +#define QCRYPTO_BLOCK_LUKS_MAGIC_LEN 6 +#define QCRYPTO_BLOCK_LUKS_CIPHER_NAME_LEN 32 +#define QCRYPTO_BLOCK_LUKS_CIPHER_MODE_LEN 32 +#define QCRYPTO_BLOCK_LUKS_HASH_SPEC_LEN 32 +#define QCRYPTO_BLOCK_LUKS_DIGEST_LEN 20 +#define QCRYPTO_BLOCK_LUKS_SALT_LEN 32 +#define QCRYPTO_BLOCK_LUKS_UUID_LEN 40 +#define QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS 8 +#define QCRYPTO_BLOCK_LUKS_STRIPES 4000 +#define QCRYPTO_BLOCK_LUKS_MIN_SLOT_KEY_ITERS 1000 +#define QCRYPTO_BLOCK_LUKS_MIN_MASTER_KEY_ITERS 1000 +#define QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET 4096 + +#define QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED 0x0000DEAD +#define QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED 0x00AC71F3 + +#define QCRYPTO_BLOCK_LUKS_SECTOR_SIZE 512LL + +static const char qcrypto_block_luks_magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN] = { + 'L', 'U', 'K', 'S', 0xBA, 0xBE +}; + +typedef struct QCryptoBlockLUKSNameMap QCryptoBlockLUKSNameMap; +struct QCryptoBlockLUKSNameMap { + const char *name; + int id; +}; + +typedef struct QCryptoBlockLUKSCipherSizeMap QCryptoBlockLUKSCipherSizeMap; +struct QCryptoBlockLUKSCipherSizeMap { + uint32_t key_bytes; + int id; +}; +typedef struct QCryptoBlockLUKSCipherNameMap QCryptoBlockLUKSCipherNameMap; +struct QCryptoBlockLUKSCipherNameMap { + const char *name; + const QCryptoBlockLUKSCipherSizeMap *sizes; +}; + + +static const QCryptoBlockLUKSCipherSizeMap +qcrypto_block_luks_cipher_size_map_aes[] = { + { 16, QCRYPTO_CIPHER_ALG_AES_128 }, + { 24, QCRYPTO_CIPHER_ALG_AES_192 }, + { 32, QCRYPTO_CIPHER_ALG_AES_256 }, + { 0, 0 }, +}; + +static const QCryptoBlockLUKSCipherSizeMap +qcrypto_block_luks_cipher_size_map_cast5[] = { + { 16, QCRYPTO_CIPHER_ALG_CAST5_128 }, + { 0, 0 }, +}; + +static const QCryptoBlockLUKSCipherSizeMap +qcrypto_block_luks_cipher_size_map_serpent[] = { + { 16, QCRYPTO_CIPHER_ALG_SERPENT_128 }, + { 24, QCRYPTO_CIPHER_ALG_SERPENT_192 }, + { 32, QCRYPTO_CIPHER_ALG_SERPENT_256 }, + { 0, 0 }, +}; + +static const QCryptoBlockLUKSCipherSizeMap +qcrypto_block_luks_cipher_size_map_twofish[] = { + { 16, QCRYPTO_CIPHER_ALG_TWOFISH_128 }, + { 24, QCRYPTO_CIPHER_ALG_TWOFISH_192 }, + { 32, QCRYPTO_CIPHER_ALG_TWOFISH_256 }, + { 0, 0 }, +}; + +static const QCryptoBlockLUKSCipherNameMap +qcrypto_block_luks_cipher_name_map[] = { + { "aes", qcrypto_block_luks_cipher_size_map_aes }, + { "cast5", qcrypto_block_luks_cipher_size_map_cast5 }, + { "serpent", qcrypto_block_luks_cipher_size_map_serpent }, + { "twofish", qcrypto_block_luks_cipher_size_map_twofish }, +}; + + +/* + * This struct is written to disk in big-endian format, + * but operated upon in native-endian format. + */ +struct QCryptoBlockLUKSKeySlot { + /* state of keyslot, enabled/disable */ + uint32_t active; + /* iterations for PBKDF2 */ + uint32_t iterations; + /* salt for PBKDF2 */ + uint8_t salt[QCRYPTO_BLOCK_LUKS_SALT_LEN]; + /* start sector of key material */ + uint32_t key_offset; + /* number of anti-forensic stripes */ + uint32_t stripes; +} QEMU_PACKED; + +QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSKeySlot) != 48); + + +/* + * This struct is written to disk in big-endian format, + * but operated upon in native-endian format. + */ +struct QCryptoBlockLUKSHeader { + /* 'L', 'U', 'K', 'S', '0xBA', '0xBE' */ + char magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN]; + + /* LUKS version, currently 1 */ + uint16_t version; + + /* cipher name specification (aes, etc) */ + char cipher_name[QCRYPTO_BLOCK_LUKS_CIPHER_NAME_LEN]; + + /* cipher mode specification (cbc-plain, xts-essiv:sha256, etc) */ + char cipher_mode[QCRYPTO_BLOCK_LUKS_CIPHER_MODE_LEN]; + + /* hash specification (sha256, etc) */ + char hash_spec[QCRYPTO_BLOCK_LUKS_HASH_SPEC_LEN]; + + /* start offset of the volume data (in 512 byte sectors) */ + uint32_t payload_offset; + + /* Number of key bytes */ + uint32_t key_bytes; + + /* master key checksum after PBKDF2 */ + uint8_t master_key_digest[QCRYPTO_BLOCK_LUKS_DIGEST_LEN]; + + /* salt for master key PBKDF2 */ + uint8_t master_key_salt[QCRYPTO_BLOCK_LUKS_SALT_LEN]; + + /* iterations for master key PBKDF2 */ + uint32_t master_key_iterations; + + /* UUID of the partition in standard ASCII representation */ + uint8_t uuid[QCRYPTO_BLOCK_LUKS_UUID_LEN]; + + /* key slots */ + QCryptoBlockLUKSKeySlot key_slots[QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS]; +} QEMU_PACKED; + +QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSHeader) != 592); + + +struct QCryptoBlockLUKS { + QCryptoBlockLUKSHeader header; +}; + + +static int qcrypto_block_luks_cipher_name_lookup(const char *name, + QCryptoCipherMode mode, + uint32_t key_bytes, + Error **errp) +{ + const QCryptoBlockLUKSCipherNameMap *map = + qcrypto_block_luks_cipher_name_map; + size_t maplen = G_N_ELEMENTS(qcrypto_block_luks_cipher_name_map); + size_t i, j; + + if (mode == QCRYPTO_CIPHER_MODE_XTS) { + key_bytes /= 2; + } + + for (i = 0; i < maplen; i++) { + if (!g_str_equal(map[i].name, name)) { + continue; + } + for (j = 0; j < map[i].sizes[j].key_bytes; j++) { + if (map[i].sizes[j].key_bytes == key_bytes) { + return map[i].sizes[j].id; + } + } + } + + error_setg(errp, "Algorithm %s with key size %d bytes not supported", + name, key_bytes); + return 0; +} + +static const char * +qcrypto_block_luks_cipher_alg_lookup(QCryptoCipherAlgorithm alg, + Error **errp) +{ + const QCryptoBlockLUKSCipherNameMap *map = + qcrypto_block_luks_cipher_name_map; + size_t maplen = G_N_ELEMENTS(qcrypto_block_luks_cipher_name_map); + size_t i, j; + for (i = 0; i < maplen; i++) { + for (j = 0; j < map[i].sizes[j].key_bytes; j++) { + if (map[i].sizes[j].id == alg) { + return map[i].name; + } + } + } + + error_setg(errp, "Algorithm '%s' not supported", + QCryptoCipherAlgorithm_lookup[alg]); + return NULL; +} + +/* XXX replace with qapi_enum_parse() in future, when we can + * make that function emit a more friendly error message */ +static int qcrypto_block_luks_name_lookup(const char *name, + const char *const *map, + size_t maplen, + const char *type, + Error **errp) +{ + size_t i; + for (i = 0; i < maplen; i++) { + if (g_str_equal(map[i], name)) { + return i; + } + } + + error_setg(errp, "%s %s not supported", type, name); + return 0; +} + +#define qcrypto_block_luks_cipher_mode_lookup(name, errp) \ + qcrypto_block_luks_name_lookup(name, \ + QCryptoCipherMode_lookup, \ + QCRYPTO_CIPHER_MODE__MAX, \ + "Cipher mode", \ + errp) + +#define qcrypto_block_luks_hash_name_lookup(name, errp) \ + qcrypto_block_luks_name_lookup(name, \ + QCryptoHashAlgorithm_lookup, \ + QCRYPTO_HASH_ALG__MAX, \ + "Hash algorithm", \ + errp) + +#define qcrypto_block_luks_ivgen_name_lookup(name, errp) \ + qcrypto_block_luks_name_lookup(name, \ + QCryptoIVGenAlgorithm_lookup, \ + QCRYPTO_IVGEN_ALG__MAX, \ + "IV generator", \ + errp) + + +static bool +qcrypto_block_luks_has_format(const uint8_t *buf, + size_t buf_size) +{ + const QCryptoBlockLUKSHeader *luks_header = (const void *)buf; + + if (buf_size >= offsetof(QCryptoBlockLUKSHeader, cipher_name) && + memcmp(luks_header->magic, qcrypto_block_luks_magic, + QCRYPTO_BLOCK_LUKS_MAGIC_LEN) == 0 && + be16_to_cpu(luks_header->version) == QCRYPTO_BLOCK_LUKS_VERSION) { + return true; + } else { + return false; + } +} + + +/** + * Deal with a quirk of dm-crypt usage of ESSIV. + * + * When calculating ESSIV IVs, the cipher length used by ESSIV + * may be different from the cipher length used for the block + * encryption, becauses dm-crypt uses the hash digest length + * as the key size. ie, if you have AES 128 as the block cipher + * and SHA 256 as ESSIV hash, then ESSIV will use AES 256 as + * the cipher since that gets a key length matching the digest + * size, not AES 128 with truncated digest as might be imagined + */ +static QCryptoCipherAlgorithm +qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher, + QCryptoHashAlgorithm hash, + Error **errp) +{ + size_t digestlen = qcrypto_hash_digest_len(hash); + size_t keylen = qcrypto_cipher_get_key_len(cipher); + if (digestlen == keylen) { + return cipher; + } + + switch (cipher) { + case QCRYPTO_CIPHER_ALG_AES_128: + case QCRYPTO_CIPHER_ALG_AES_192: + case QCRYPTO_CIPHER_ALG_AES_256: + if (digestlen == qcrypto_cipher_get_key_len( + QCRYPTO_CIPHER_ALG_AES_128)) { + return QCRYPTO_CIPHER_ALG_AES_128; + } else if (digestlen == qcrypto_cipher_get_key_len( + QCRYPTO_CIPHER_ALG_AES_192)) { + return QCRYPTO_CIPHER_ALG_AES_192; + } else if (digestlen == qcrypto_cipher_get_key_len( + QCRYPTO_CIPHER_ALG_AES_256)) { + return QCRYPTO_CIPHER_ALG_AES_256; + } else { + error_setg(errp, "No AES cipher with key size %zu available", + digestlen); + return 0; + } + break; + case QCRYPTO_CIPHER_ALG_SERPENT_128: + case QCRYPTO_CIPHER_ALG_SERPENT_192: + case QCRYPTO_CIPHER_ALG_SERPENT_256: + if (digestlen == qcrypto_cipher_get_key_len( + QCRYPTO_CIPHER_ALG_SERPENT_128)) { + return QCRYPTO_CIPHER_ALG_SERPENT_128; + } else if (digestlen == qcrypto_cipher_get_key_len( + QCRYPTO_CIPHER_ALG_SERPENT_192)) { + return QCRYPTO_CIPHER_ALG_SERPENT_192; + } else if (digestlen == qcrypto_cipher_get_key_len( + QCRYPTO_CIPHER_ALG_SERPENT_256)) { + return QCRYPTO_CIPHER_ALG_SERPENT_256; + } else { + error_setg(errp, "No Serpent cipher with key size %zu available", + digestlen); + return 0; + } + break; + case QCRYPTO_CIPHER_ALG_TWOFISH_128: + case QCRYPTO_CIPHER_ALG_TWOFISH_192: + case QCRYPTO_CIPHER_ALG_TWOFISH_256: + if (digestlen == qcrypto_cipher_get_key_len( + QCRYPTO_CIPHER_ALG_TWOFISH_128)) { + return QCRYPTO_CIPHER_ALG_TWOFISH_128; + } else if (digestlen == qcrypto_cipher_get_key_len( + QCRYPTO_CIPHER_ALG_TWOFISH_192)) { + return QCRYPTO_CIPHER_ALG_TWOFISH_192; + } else if (digestlen == qcrypto_cipher_get_key_len( + QCRYPTO_CIPHER_ALG_TWOFISH_256)) { + return QCRYPTO_CIPHER_ALG_TWOFISH_256; + } else { + error_setg(errp, "No Twofish cipher with key size %zu available", + digestlen); + return 0; + } + break; + default: + error_setg(errp, "Cipher %s not supported with essiv", + QCryptoCipherAlgorithm_lookup[cipher]); + return 0; + } +} + +/* + * Given a key slot, and user password, this will attempt to unlock + * the master encryption key from the key slot. + * + * Returns: + * 0 if the key slot is disabled, or key could not be decrypted + * with the provided password + * 1 if the key slot is enabled, and key decrypted successfully + * with the provided password + * -1 if a fatal error occurred loading the key + */ +static int +qcrypto_block_luks_load_key(QCryptoBlock *block, + QCryptoBlockLUKSKeySlot *slot, + const char *password, + QCryptoCipherAlgorithm cipheralg, + QCryptoCipherMode ciphermode, + QCryptoHashAlgorithm hash, + QCryptoIVGenAlgorithm ivalg, + QCryptoCipherAlgorithm ivcipheralg, + QCryptoHashAlgorithm ivhash, + uint8_t *masterkey, + size_t masterkeylen, + QCryptoBlockReadFunc readfunc, + void *opaque, + Error **errp) +{ + QCryptoBlockLUKS *luks = block->opaque; + uint8_t *splitkey; + size_t splitkeylen; + uint8_t *possiblekey; + int ret = -1; + ssize_t rv; + QCryptoCipher *cipher = NULL; + uint8_t keydigest[QCRYPTO_BLOCK_LUKS_DIGEST_LEN]; + QCryptoIVGen *ivgen = NULL; + size_t niv; + + if (slot->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED) { + return 0; + } + + splitkeylen = masterkeylen * slot->stripes; + splitkey = g_new0(uint8_t, splitkeylen); + possiblekey = g_new0(uint8_t, masterkeylen); + + /* + * The user password is used to generate a (possible) + * decryption key. This may or may not successfully + * decrypt the master key - we just blindly assume + * the key is correct and validate the results of + * decryption later. + */ + if (qcrypto_pbkdf2(hash, + (const uint8_t *)password, strlen(password), + slot->salt, QCRYPTO_BLOCK_LUKS_SALT_LEN, + slot->iterations, + possiblekey, masterkeylen, + errp) < 0) { + goto cleanup; + } + + /* + * We need to read the master key material from the + * LUKS key material header. What we're reading is + * not the raw master key, but rather the data after + * it has been passed through AFSplit and the result + * then encrypted. + */ + rv = readfunc(block, + slot->key_offset * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, + splitkey, splitkeylen, + errp, + opaque); + if (rv < 0) { + goto cleanup; + } + + + /* Setup the cipher/ivgen that we'll use to try to decrypt + * the split master key material */ + cipher = qcrypto_cipher_new(cipheralg, ciphermode, + possiblekey, masterkeylen, + errp); + if (!cipher) { + goto cleanup; + } + + niv = qcrypto_cipher_get_iv_len(cipheralg, + ciphermode); + ivgen = qcrypto_ivgen_new(ivalg, + ivcipheralg, + ivhash, + possiblekey, masterkeylen, + errp); + if (!ivgen) { + goto cleanup; + } + + + /* + * The master key needs to be decrypted in the same + * way that the block device payload will be decrypted + * later. In particular we'll be using the IV generator + * to reset the encryption cipher every time the master + * key crosses a sector boundary. + */ + if (qcrypto_block_decrypt_helper(cipher, + niv, + ivgen, + QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, + 0, + splitkey, + splitkeylen, + errp) < 0) { + goto cleanup; + } + + /* + * Now we've decrypted the split master key, join + * it back together to get the actual master key. + */ + if (qcrypto_afsplit_decode(hash, + masterkeylen, + slot->stripes, + splitkey, + masterkey, + errp) < 0) { + goto cleanup; + } + + + /* + * We still don't know that the masterkey we got is valid, + * because we just blindly assumed the user's password + * was correct. This is where we now verify it. We are + * creating a hash of the master key using PBKDF and + * then comparing that to the hash stored in the key slot + * header + */ + if (qcrypto_pbkdf2(hash, + masterkey, masterkeylen, + luks->header.master_key_salt, + QCRYPTO_BLOCK_LUKS_SALT_LEN, + luks->header.master_key_iterations, + keydigest, G_N_ELEMENTS(keydigest), + errp) < 0) { + goto cleanup; + } + + if (memcmp(keydigest, luks->header.master_key_digest, + QCRYPTO_BLOCK_LUKS_DIGEST_LEN) == 0) { + /* Success, we got the right master key */ + ret = 1; + goto cleanup; + } + + /* Fail, user's password was not valid for this key slot, + * tell caller to try another slot */ + ret = 0; + + cleanup: + qcrypto_ivgen_free(ivgen); + qcrypto_cipher_free(cipher); + g_free(splitkey); + g_free(possiblekey); + return ret; +} + + +/* + * Given a user password, this will iterate over all key + * slots and try to unlock each active key slot using the + * password until it successfully obtains a master key. + * + * Returns 0 if a key was loaded, -1 if no keys could be loaded + */ +static int +qcrypto_block_luks_find_key(QCryptoBlock *block, + const char *password, + QCryptoCipherAlgorithm cipheralg, + QCryptoCipherMode ciphermode, + QCryptoHashAlgorithm hash, + QCryptoIVGenAlgorithm ivalg, + QCryptoCipherAlgorithm ivcipheralg, + QCryptoHashAlgorithm ivhash, + uint8_t **masterkey, + size_t *masterkeylen, + QCryptoBlockReadFunc readfunc, + void *opaque, + Error **errp) +{ + QCryptoBlockLUKS *luks = block->opaque; + size_t i; + int rv; + + *masterkey = g_new0(uint8_t, luks->header.key_bytes); + *masterkeylen = luks->header.key_bytes; + + for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { + rv = qcrypto_block_luks_load_key(block, + &luks->header.key_slots[i], + password, + cipheralg, + ciphermode, + hash, + ivalg, + ivcipheralg, + ivhash, + *masterkey, + *masterkeylen, + readfunc, + opaque, + errp); + if (rv < 0) { + goto error; + } + if (rv == 1) { + return 0; + } + } + + error_setg(errp, "Invalid password, cannot unlock any keyslot"); + + error: + g_free(*masterkey); + *masterkey = NULL; + *masterkeylen = 0; + return -1; +} + + +static int +qcrypto_block_luks_open(QCryptoBlock *block, + QCryptoBlockOpenOptions *options, + QCryptoBlockReadFunc readfunc, + void *opaque, + unsigned int flags, + Error **errp) +{ + QCryptoBlockLUKS *luks; + Error *local_err = NULL; + int ret = 0; + size_t i; + ssize_t rv; + uint8_t *masterkey = NULL; + size_t masterkeylen; + char *ivgen_name, *ivhash_name; + QCryptoCipherMode ciphermode; + QCryptoCipherAlgorithm cipheralg; + QCryptoIVGenAlgorithm ivalg; + QCryptoCipherAlgorithm ivcipheralg; + QCryptoHashAlgorithm hash; + QCryptoHashAlgorithm ivhash; + char *password = NULL; + + if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) { + if (!options->u.luks.key_secret) { + error_setg(errp, "Parameter 'key-secret' is required for cipher"); + return -1; + } + password = qcrypto_secret_lookup_as_utf8( + options->u.luks.key_secret, errp); + if (!password) { + return -1; + } + } + + luks = g_new0(QCryptoBlockLUKS, 1); + block->opaque = luks; + + /* Read the entire LUKS header, minus the key material from + * the underlying device */ + rv = readfunc(block, 0, + (uint8_t *)&luks->header, + sizeof(luks->header), + errp, + opaque); + if (rv < 0) { + ret = rv; + goto fail; + } + + /* The header is always stored in big-endian format, so + * convert everything to native */ + be16_to_cpus(&luks->header.version); + be32_to_cpus(&luks->header.payload_offset); + be32_to_cpus(&luks->header.key_bytes); + be32_to_cpus(&luks->header.master_key_iterations); + + for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { + be32_to_cpus(&luks->header.key_slots[i].active); + be32_to_cpus(&luks->header.key_slots[i].iterations); + be32_to_cpus(&luks->header.key_slots[i].key_offset); + be32_to_cpus(&luks->header.key_slots[i].stripes); + } + + if (memcmp(luks->header.magic, qcrypto_block_luks_magic, + QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) { + error_setg(errp, "Volume is not in LUKS format"); + ret = -EINVAL; + goto fail; + } + if (luks->header.version != QCRYPTO_BLOCK_LUKS_VERSION) { + error_setg(errp, "LUKS version %" PRIu32 " is not supported", + luks->header.version); + ret = -ENOTSUP; + goto fail; + } + + /* + * The cipher_mode header contains a string that we have + * to further parse, of the format + * + * -[:] + * + * eg cbc-essiv:sha256, cbc-plain64 + */ + ivgen_name = strchr(luks->header.cipher_mode, '-'); + if (!ivgen_name) { + ret = -EINVAL; + error_setg(errp, "Unexpected cipher mode string format %s", + luks->header.cipher_mode); + goto fail; + } + *ivgen_name = '\0'; + ivgen_name++; + + ivhash_name = strchr(ivgen_name, ':'); + if (!ivhash_name) { + ivhash = 0; + } else { + *ivhash_name = '\0'; + ivhash_name++; + + ivhash = qcrypto_block_luks_hash_name_lookup(ivhash_name, + &local_err); + if (local_err) { + ret = -ENOTSUP; + error_propagate(errp, local_err); + goto fail; + } + } + + ciphermode = qcrypto_block_luks_cipher_mode_lookup(luks->header.cipher_mode, + &local_err); + if (local_err) { + ret = -ENOTSUP; + error_propagate(errp, local_err); + goto fail; + } + + cipheralg = qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name, + ciphermode, + luks->header.key_bytes, + &local_err); + if (local_err) { + ret = -ENOTSUP; + error_propagate(errp, local_err); + goto fail; + } + + hash = qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec, + &local_err); + if (local_err) { + ret = -ENOTSUP; + error_propagate(errp, local_err); + goto fail; + } + + ivalg = qcrypto_block_luks_ivgen_name_lookup(ivgen_name, + &local_err); + if (local_err) { + ret = -ENOTSUP; + error_propagate(errp, local_err); + goto fail; + } + + if (ivalg == QCRYPTO_IVGEN_ALG_ESSIV) { + ivcipheralg = qcrypto_block_luks_essiv_cipher(cipheralg, + ivhash, + &local_err); + if (local_err) { + ret = -ENOTSUP; + error_propagate(errp, local_err); + goto fail; + } + } else { + ivcipheralg = cipheralg; + } + + if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) { + /* Try to find which key slot our password is valid for + * and unlock the master key from that slot. + */ + if (qcrypto_block_luks_find_key(block, + password, + cipheralg, ciphermode, + hash, + ivalg, + ivcipheralg, + ivhash, + &masterkey, &masterkeylen, + readfunc, opaque, + errp) < 0) { + ret = -EACCES; + goto fail; + } + + /* We have a valid master key now, so can setup the + * block device payload decryption objects + */ + block->kdfhash = hash; + block->niv = qcrypto_cipher_get_iv_len(cipheralg, + ciphermode); + block->ivgen = qcrypto_ivgen_new(ivalg, + ivcipheralg, + ivhash, + masterkey, masterkeylen, + errp); + if (!block->ivgen) { + ret = -ENOTSUP; + goto fail; + } + + block->cipher = qcrypto_cipher_new(cipheralg, + ciphermode, + masterkey, masterkeylen, + errp); + if (!block->cipher) { + ret = -ENOTSUP; + goto fail; + } + } + + block->payload_offset = luks->header.payload_offset * + QCRYPTO_BLOCK_LUKS_SECTOR_SIZE; + + g_free(masterkey); + g_free(password); + + return 0; + + fail: + g_free(masterkey); + qcrypto_cipher_free(block->cipher); + qcrypto_ivgen_free(block->ivgen); + g_free(luks); + g_free(password); + return ret; +} + + +static int +qcrypto_block_luks_uuid_gen(uint8_t *uuidstr, Error **errp) +{ +#ifdef CONFIG_UUID + uuid_t uuid; + uuid_generate(uuid); + uuid_unparse(uuid, (char *)uuidstr); + return 0; +#else + error_setg(errp, "Unable to generate uuids on this platform"); + return -1; +#endif +} + +static int +qcrypto_block_luks_create(QCryptoBlock *block, + QCryptoBlockCreateOptions *options, + QCryptoBlockInitFunc initfunc, + QCryptoBlockWriteFunc writefunc, + void *opaque, + Error **errp) +{ + QCryptoBlockLUKS *luks; + QCryptoBlockCreateOptionsLUKS luks_opts; + Error *local_err = NULL; + uint8_t *masterkey = NULL; + uint8_t *slotkey = NULL; + uint8_t *splitkey = NULL; + size_t splitkeylen = 0; + size_t i; + QCryptoCipher *cipher = NULL; + QCryptoIVGen *ivgen = NULL; + char *password; + const char *cipher_alg; + const char *cipher_mode; + const char *ivgen_alg; + const char *ivgen_hash_alg = NULL; + const char *hash_alg; + char *cipher_mode_spec = NULL; + QCryptoCipherAlgorithm ivcipheralg = 0; + + memcpy(&luks_opts, &options->u.luks, sizeof(luks_opts)); + if (!luks_opts.has_cipher_alg) { + luks_opts.cipher_alg = QCRYPTO_CIPHER_ALG_AES_256; + } + if (!luks_opts.has_cipher_mode) { + luks_opts.cipher_mode = QCRYPTO_CIPHER_MODE_XTS; + } + if (!luks_opts.has_ivgen_alg) { + luks_opts.ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64; + } + if (!luks_opts.has_hash_alg) { + luks_opts.hash_alg = QCRYPTO_HASH_ALG_SHA256; + } + + if (!options->u.luks.key_secret) { + error_setg(errp, "Parameter 'key-secret' is required for cipher"); + return -1; + } + password = qcrypto_secret_lookup_as_utf8(luks_opts.key_secret, errp); + if (!password) { + return -1; + } + + luks = g_new0(QCryptoBlockLUKS, 1); + block->opaque = luks; + + memcpy(luks->header.magic, qcrypto_block_luks_magic, + QCRYPTO_BLOCK_LUKS_MAGIC_LEN); + + /* We populate the header in native endianness initially and + * then convert everything to big endian just before writing + * it out to disk + */ + luks->header.version = QCRYPTO_BLOCK_LUKS_VERSION; + if (qcrypto_block_luks_uuid_gen(luks->header.uuid, + errp) < 0) { + goto error; + } + + cipher_alg = qcrypto_block_luks_cipher_alg_lookup(luks_opts.cipher_alg, + errp); + if (!cipher_alg) { + goto error; + } + + cipher_mode = QCryptoCipherMode_lookup[luks_opts.cipher_mode]; + ivgen_alg = QCryptoIVGenAlgorithm_lookup[luks_opts.ivgen_alg]; + if (luks_opts.has_ivgen_hash_alg) { + ivgen_hash_alg = QCryptoHashAlgorithm_lookup[luks_opts.ivgen_hash_alg]; + cipher_mode_spec = g_strdup_printf("%s-%s:%s", cipher_mode, ivgen_alg, + ivgen_hash_alg); + } else { + cipher_mode_spec = g_strdup_printf("%s-%s", cipher_mode, ivgen_alg); + } + hash_alg = QCryptoHashAlgorithm_lookup[luks_opts.hash_alg]; + + + if (strlen(cipher_alg) >= QCRYPTO_BLOCK_LUKS_CIPHER_NAME_LEN) { + error_setg(errp, "Cipher name '%s' is too long for LUKS header", + cipher_alg); + goto error; + } + if (strlen(cipher_mode_spec) >= QCRYPTO_BLOCK_LUKS_CIPHER_MODE_LEN) { + error_setg(errp, "Cipher mode '%s' is too long for LUKS header", + cipher_mode_spec); + goto error; + } + if (strlen(hash_alg) >= QCRYPTO_BLOCK_LUKS_HASH_SPEC_LEN) { + error_setg(errp, "Hash name '%s' is too long for LUKS header", + hash_alg); + goto error; + } + + if (luks_opts.ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) { + ivcipheralg = qcrypto_block_luks_essiv_cipher(luks_opts.cipher_alg, + luks_opts.ivgen_hash_alg, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto error; + } + } else { + ivcipheralg = luks_opts.cipher_alg; + } + + strcpy(luks->header.cipher_name, cipher_alg); + strcpy(luks->header.cipher_mode, cipher_mode_spec); + strcpy(luks->header.hash_spec, hash_alg); + + luks->header.key_bytes = qcrypto_cipher_get_key_len(luks_opts.cipher_alg); + if (luks_opts.cipher_mode == QCRYPTO_CIPHER_MODE_XTS) { + luks->header.key_bytes *= 2; + } + + /* Generate the salt used for hashing the master key + * with PBKDF later + */ + if (qcrypto_random_bytes(luks->header.master_key_salt, + QCRYPTO_BLOCK_LUKS_SALT_LEN, + errp) < 0) { + goto error; + } + + /* Generate random master key */ + masterkey = g_new0(uint8_t, luks->header.key_bytes); + if (qcrypto_random_bytes(masterkey, + luks->header.key_bytes, errp) < 0) { + goto error; + } + + + /* Setup the block device payload encryption objects */ + block->cipher = qcrypto_cipher_new(luks_opts.cipher_alg, + luks_opts.cipher_mode, + masterkey, luks->header.key_bytes, + errp); + if (!block->cipher) { + goto error; + } + + block->kdfhash = luks_opts.hash_alg; + block->niv = qcrypto_cipher_get_iv_len(luks_opts.cipher_alg, + luks_opts.cipher_mode); + block->ivgen = qcrypto_ivgen_new(luks_opts.ivgen_alg, + ivcipheralg, + luks_opts.ivgen_hash_alg, + masterkey, luks->header.key_bytes, + errp); + + if (!block->ivgen) { + goto error; + } + + + /* Determine how many iterations we need to hash the master + * key, in order to have 1 second of compute time used + */ + luks->header.master_key_iterations = + qcrypto_pbkdf2_count_iters(luks_opts.hash_alg, + masterkey, luks->header.key_bytes, + luks->header.master_key_salt, + QCRYPTO_BLOCK_LUKS_SALT_LEN, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto error; + } + + /* Why /= 8 ? That matches cryptsetup, but there's no + * explanation why they chose /= 8... Probably so that + * if all 8 keyslots are active we only spend 1 second + * in total time to check all keys */ + luks->header.master_key_iterations /= 8; + luks->header.master_key_iterations = MAX( + luks->header.master_key_iterations, + QCRYPTO_BLOCK_LUKS_MIN_MASTER_KEY_ITERS); + + + /* Hash the master key, saving the result in the LUKS + * header. This hash is used when opening the encrypted + * device to verify that the user password unlocked a + * valid master key + */ + if (qcrypto_pbkdf2(luks_opts.hash_alg, + masterkey, luks->header.key_bytes, + luks->header.master_key_salt, + QCRYPTO_BLOCK_LUKS_SALT_LEN, + luks->header.master_key_iterations, + luks->header.master_key_digest, + QCRYPTO_BLOCK_LUKS_DIGEST_LEN, + errp) < 0) { + goto error; + } + + + /* Although LUKS has multiple key slots, we're just going + * to use the first key slot */ + splitkeylen = luks->header.key_bytes * QCRYPTO_BLOCK_LUKS_STRIPES; + for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { + luks->header.key_slots[i].active = i == 0 ? + QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED : + QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED; + luks->header.key_slots[i].stripes = QCRYPTO_BLOCK_LUKS_STRIPES; + + /* This calculation doesn't match that shown in the spec, + * but instead follows the cryptsetup implementation. + */ + luks->header.key_slots[i].key_offset = + (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET / + QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) + + (ROUND_UP(((splitkeylen + (QCRYPTO_BLOCK_LUKS_SECTOR_SIZE - 1)) / + QCRYPTO_BLOCK_LUKS_SECTOR_SIZE), + (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET / + QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) * i); + } + + if (qcrypto_random_bytes(luks->header.key_slots[0].salt, + QCRYPTO_BLOCK_LUKS_SALT_LEN, + errp) < 0) { + goto error; + } + + /* Again we determine how many iterations are required to + * hash the user password while consuming 1 second of compute + * time */ + luks->header.key_slots[0].iterations = + qcrypto_pbkdf2_count_iters(luks_opts.hash_alg, + (uint8_t *)password, strlen(password), + luks->header.key_slots[0].salt, + QCRYPTO_BLOCK_LUKS_SALT_LEN, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto error; + } + /* Why /= 2 ? That matches cryptsetup, but there's no + * explanation why they chose /= 2... */ + luks->header.key_slots[0].iterations /= 2; + luks->header.key_slots[0].iterations = MAX( + luks->header.key_slots[0].iterations, + QCRYPTO_BLOCK_LUKS_MIN_SLOT_KEY_ITERS); + + + /* Generate a key that we'll use to encrypt the master + * key, from the user's password + */ + slotkey = g_new0(uint8_t, luks->header.key_bytes); + if (qcrypto_pbkdf2(luks_opts.hash_alg, + (uint8_t *)password, strlen(password), + luks->header.key_slots[0].salt, + QCRYPTO_BLOCK_LUKS_SALT_LEN, + luks->header.key_slots[0].iterations, + slotkey, luks->header.key_bytes, + errp) < 0) { + goto error; + } + + + /* Setup the encryption objects needed to encrypt the + * master key material + */ + cipher = qcrypto_cipher_new(luks_opts.cipher_alg, + luks_opts.cipher_mode, + slotkey, luks->header.key_bytes, + errp); + if (!cipher) { + goto error; + } + + ivgen = qcrypto_ivgen_new(luks_opts.ivgen_alg, + ivcipheralg, + luks_opts.ivgen_hash_alg, + slotkey, luks->header.key_bytes, + errp); + if (!ivgen) { + goto error; + } + + /* Before storing the master key, we need to vastly + * increase its size, as protection against forensic + * disk data recovery */ + splitkey = g_new0(uint8_t, splitkeylen); + + if (qcrypto_afsplit_encode(luks_opts.hash_alg, + luks->header.key_bytes, + luks->header.key_slots[0].stripes, + masterkey, + splitkey, + errp) < 0) { + goto error; + } + + /* Now we encrypt the split master key with the key generated + * from the user's password, before storing it */ + if (qcrypto_block_encrypt_helper(cipher, block->niv, ivgen, + QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, + 0, + splitkey, + splitkeylen, + errp) < 0) { + goto error; + } + + + /* The total size of the LUKS headers is the partition header + key + * slot headers, rounded up to the nearest sector, combined with + * the size of each master key material region, also rounded up + * to the nearest sector */ + luks->header.payload_offset = + (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET / + QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) + + (ROUND_UP(((splitkeylen + (QCRYPTO_BLOCK_LUKS_SECTOR_SIZE - 1)) / + QCRYPTO_BLOCK_LUKS_SECTOR_SIZE), + (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET / + QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) * + QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS); + + block->payload_offset = luks->header.payload_offset * + QCRYPTO_BLOCK_LUKS_SECTOR_SIZE; + + /* Reserve header space to match payload offset */ + initfunc(block, block->payload_offset, &local_err, opaque); + if (local_err) { + error_propagate(errp, local_err); + goto error; + } + + /* Everything on disk uses Big Endian, so flip header fields + * before writing them */ + cpu_to_be16s(&luks->header.version); + cpu_to_be32s(&luks->header.payload_offset); + cpu_to_be32s(&luks->header.key_bytes); + cpu_to_be32s(&luks->header.master_key_iterations); + + for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { + cpu_to_be32s(&luks->header.key_slots[i].active); + cpu_to_be32s(&luks->header.key_slots[i].iterations); + cpu_to_be32s(&luks->header.key_slots[i].key_offset); + cpu_to_be32s(&luks->header.key_slots[i].stripes); + } + + + /* Write out the partition header and key slot headers */ + writefunc(block, 0, + (const uint8_t *)&luks->header, + sizeof(luks->header), + &local_err, + opaque); + + /* Delay checking local_err until we've byte-swapped */ + + /* Byte swap the header back to native, in case we need + * to read it again later */ + be16_to_cpus(&luks->header.version); + be32_to_cpus(&luks->header.payload_offset); + be32_to_cpus(&luks->header.key_bytes); + be32_to_cpus(&luks->header.master_key_iterations); + + for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { + be32_to_cpus(&luks->header.key_slots[i].active); + be32_to_cpus(&luks->header.key_slots[i].iterations); + be32_to_cpus(&luks->header.key_slots[i].key_offset); + be32_to_cpus(&luks->header.key_slots[i].stripes); + } + + if (local_err) { + error_propagate(errp, local_err); + goto error; + } + + /* Write out the master key material, starting at the + * sector immediately following the partition header. */ + if (writefunc(block, + luks->header.key_slots[0].key_offset * + QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, + splitkey, splitkeylen, + errp, + opaque) != splitkeylen) { + goto error; + } + + memset(masterkey, 0, luks->header.key_bytes); + g_free(masterkey); + memset(slotkey, 0, luks->header.key_bytes); + g_free(slotkey); + g_free(splitkey); + g_free(password); + g_free(cipher_mode_spec); + + qcrypto_ivgen_free(ivgen); + qcrypto_cipher_free(cipher); + + return 0; + + error: + if (masterkey) { + memset(masterkey, 0, luks->header.key_bytes); + } + g_free(masterkey); + if (slotkey) { + memset(slotkey, 0, luks->header.key_bytes); + } + g_free(slotkey); + g_free(splitkey); + g_free(password); + g_free(cipher_mode_spec); + + qcrypto_ivgen_free(ivgen); + qcrypto_cipher_free(cipher); + + g_free(luks); + return -1; +} + + +static void qcrypto_block_luks_cleanup(QCryptoBlock *block) +{ + g_free(block->opaque); +} + + +static int +qcrypto_block_luks_decrypt(QCryptoBlock *block, + uint64_t startsector, + uint8_t *buf, + size_t len, + Error **errp) +{ + return qcrypto_block_decrypt_helper(block->cipher, + block->niv, block->ivgen, + QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, + startsector, buf, len, errp); +} + + +static int +qcrypto_block_luks_encrypt(QCryptoBlock *block, + uint64_t startsector, + uint8_t *buf, + size_t len, + Error **errp) +{ + return qcrypto_block_encrypt_helper(block->cipher, + block->niv, block->ivgen, + QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, + startsector, buf, len, errp); +} + + +const QCryptoBlockDriver qcrypto_block_driver_luks = { + .open = qcrypto_block_luks_open, + .create = qcrypto_block_luks_create, + .cleanup = qcrypto_block_luks_cleanup, + .decrypt = qcrypto_block_luks_decrypt, + .encrypt = qcrypto_block_luks_encrypt, + .has_format = qcrypto_block_luks_has_format, +}; diff --git a/qemu/crypto/block-luks.h b/qemu/crypto/block-luks.h new file mode 100644 index 000000000..0934138aa --- /dev/null +++ b/qemu/crypto/block-luks.h @@ -0,0 +1,28 @@ +/* + * QEMU Crypto block device encryption LUKS format + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QCRYPTO_BLOCK_LUKS_H__ +#define QCRYPTO_BLOCK_LUKS_H__ + +#include "crypto/blockpriv.h" + +extern const QCryptoBlockDriver qcrypto_block_driver_luks; + +#endif /* QCRYPTO_BLOCK_LUKS_H__ */ diff --git a/qemu/crypto/block-qcow.c b/qemu/crypto/block-qcow.c new file mode 100644 index 000000000..be88c6f0e --- /dev/null +++ b/qemu/crypto/block-qcow.c @@ -0,0 +1,174 @@ +/* + * QEMU Crypto block device encryption QCow/QCow2 AES-CBC format + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +/* + * Note that the block encryption implemented in this file is broken + * by design. This exists only to allow data to be liberated from + * existing qcow[2] images and should not be used in any new areas. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" + +#include "crypto/block-qcow.h" +#include "crypto/secret.h" + +#define QCRYPTO_BLOCK_QCOW_SECTOR_SIZE 512 + + +static bool +qcrypto_block_qcow_has_format(const uint8_t *buf G_GNUC_UNUSED, + size_t buf_size G_GNUC_UNUSED) +{ + return false; +} + + +static int +qcrypto_block_qcow_init(QCryptoBlock *block, + const char *keysecret, + Error **errp) +{ + char *password; + int ret; + uint8_t keybuf[16]; + int len; + + memset(keybuf, 0, 16); + + password = qcrypto_secret_lookup_as_utf8(keysecret, errp); + if (!password) { + return -1; + } + + len = strlen(password); + memcpy(keybuf, password, MIN(len, sizeof(keybuf))); + g_free(password); + + block->niv = qcrypto_cipher_get_iv_len(QCRYPTO_CIPHER_ALG_AES_128, + QCRYPTO_CIPHER_MODE_CBC); + block->ivgen = qcrypto_ivgen_new(QCRYPTO_IVGEN_ALG_PLAIN64, + 0, 0, NULL, 0, errp); + if (!block->ivgen) { + ret = -ENOTSUP; + goto fail; + } + + block->cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_128, + QCRYPTO_CIPHER_MODE_CBC, + keybuf, G_N_ELEMENTS(keybuf), + errp); + if (!block->cipher) { + ret = -ENOTSUP; + goto fail; + } + + block->payload_offset = 0; + + return 0; + + fail: + qcrypto_cipher_free(block->cipher); + qcrypto_ivgen_free(block->ivgen); + return ret; +} + + +static int +qcrypto_block_qcow_open(QCryptoBlock *block, + QCryptoBlockOpenOptions *options, + QCryptoBlockReadFunc readfunc G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED, + unsigned int flags, + Error **errp) +{ + if (flags & QCRYPTO_BLOCK_OPEN_NO_IO) { + return 0; + } else { + if (!options->u.qcow.key_secret) { + error_setg(errp, + "Parameter 'key-secret' is required for cipher"); + return -1; + } + return qcrypto_block_qcow_init(block, + options->u.qcow.key_secret, errp); + } +} + + +static int +qcrypto_block_qcow_create(QCryptoBlock *block, + QCryptoBlockCreateOptions *options, + QCryptoBlockInitFunc initfunc G_GNUC_UNUSED, + QCryptoBlockWriteFunc writefunc G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED, + Error **errp) +{ + if (!options->u.qcow.key_secret) { + error_setg(errp, "Parameter 'key-secret' is required for cipher"); + return -1; + } + /* QCow2 has no special header, since everything is hardwired */ + return qcrypto_block_qcow_init(block, options->u.qcow.key_secret, errp); +} + + +static void +qcrypto_block_qcow_cleanup(QCryptoBlock *block) +{ +} + + +static int +qcrypto_block_qcow_decrypt(QCryptoBlock *block, + uint64_t startsector, + uint8_t *buf, + size_t len, + Error **errp) +{ + return qcrypto_block_decrypt_helper(block->cipher, + block->niv, block->ivgen, + QCRYPTO_BLOCK_QCOW_SECTOR_SIZE, + startsector, buf, len, errp); +} + + +static int +qcrypto_block_qcow_encrypt(QCryptoBlock *block, + uint64_t startsector, + uint8_t *buf, + size_t len, + Error **errp) +{ + return qcrypto_block_encrypt_helper(block->cipher, + block->niv, block->ivgen, + QCRYPTO_BLOCK_QCOW_SECTOR_SIZE, + startsector, buf, len, errp); +} + + +const QCryptoBlockDriver qcrypto_block_driver_qcow = { + .open = qcrypto_block_qcow_open, + .create = qcrypto_block_qcow_create, + .cleanup = qcrypto_block_qcow_cleanup, + .decrypt = qcrypto_block_qcow_decrypt, + .encrypt = qcrypto_block_qcow_encrypt, + .has_format = qcrypto_block_qcow_has_format, +}; diff --git a/qemu/crypto/block-qcow.h b/qemu/crypto/block-qcow.h new file mode 100644 index 000000000..569f83610 --- /dev/null +++ b/qemu/crypto/block-qcow.h @@ -0,0 +1,28 @@ +/* + * QEMU Crypto block device encryption QCow/QCow2 AES-CBC format + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QCRYPTO_BLOCK_QCOW_H__ +#define QCRYPTO_BLOCK_QCOW_H__ + +#include "crypto/blockpriv.h" + +extern const QCryptoBlockDriver qcrypto_block_driver_qcow; + +#endif /* QCRYPTO_BLOCK_QCOW_H__ */ diff --git a/qemu/crypto/block.c b/qemu/crypto/block.c new file mode 100644 index 000000000..da60eba85 --- /dev/null +++ b/qemu/crypto/block.c @@ -0,0 +1,261 @@ +/* + * QEMU Crypto block device encryption + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "crypto/blockpriv.h" +#include "crypto/block-qcow.h" +#include "crypto/block-luks.h" + +static const QCryptoBlockDriver *qcrypto_block_drivers[] = { + [Q_CRYPTO_BLOCK_FORMAT_QCOW] = &qcrypto_block_driver_qcow, + [Q_CRYPTO_BLOCK_FORMAT_LUKS] = &qcrypto_block_driver_luks, +}; + + +bool qcrypto_block_has_format(QCryptoBlockFormat format, + const uint8_t *buf, + size_t len) +{ + const QCryptoBlockDriver *driver; + + if (format >= G_N_ELEMENTS(qcrypto_block_drivers) || + !qcrypto_block_drivers[format]) { + return false; + } + + driver = qcrypto_block_drivers[format]; + + return driver->has_format(buf, len); +} + + +QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options, + QCryptoBlockReadFunc readfunc, + void *opaque, + unsigned int flags, + Error **errp) +{ + QCryptoBlock *block = g_new0(QCryptoBlock, 1); + + block->format = options->format; + + if (options->format >= G_N_ELEMENTS(qcrypto_block_drivers) || + !qcrypto_block_drivers[options->format]) { + error_setg(errp, "Unsupported block driver %d", options->format); + g_free(block); + return NULL; + } + + block->driver = qcrypto_block_drivers[options->format]; + + if (block->driver->open(block, options, + readfunc, opaque, flags, errp) < 0) { + g_free(block); + return NULL; + } + + return block; +} + + +QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, + QCryptoBlockInitFunc initfunc, + QCryptoBlockWriteFunc writefunc, + void *opaque, + Error **errp) +{ + QCryptoBlock *block = g_new0(QCryptoBlock, 1); + + block->format = options->format; + + if (options->format >= G_N_ELEMENTS(qcrypto_block_drivers) || + !qcrypto_block_drivers[options->format]) { + error_setg(errp, "Unsupported block driver %d", options->format); + g_free(block); + return NULL; + } + + block->driver = qcrypto_block_drivers[options->format]; + + if (block->driver->create(block, options, initfunc, + writefunc, opaque, errp) < 0) { + g_free(block); + return NULL; + } + + return block; +} + + +int qcrypto_block_decrypt(QCryptoBlock *block, + uint64_t startsector, + uint8_t *buf, + size_t len, + Error **errp) +{ + return block->driver->decrypt(block, startsector, buf, len, errp); +} + + +int qcrypto_block_encrypt(QCryptoBlock *block, + uint64_t startsector, + uint8_t *buf, + size_t len, + Error **errp) +{ + return block->driver->encrypt(block, startsector, buf, len, errp); +} + + +QCryptoCipher *qcrypto_block_get_cipher(QCryptoBlock *block) +{ + return block->cipher; +} + + +QCryptoIVGen *qcrypto_block_get_ivgen(QCryptoBlock *block) +{ + return block->ivgen; +} + + +QCryptoHashAlgorithm qcrypto_block_get_kdf_hash(QCryptoBlock *block) +{ + return block->kdfhash; +} + + +uint64_t qcrypto_block_get_payload_offset(QCryptoBlock *block) +{ + return block->payload_offset; +} + + +void qcrypto_block_free(QCryptoBlock *block) +{ + if (!block) { + return; + } + + block->driver->cleanup(block); + + qcrypto_cipher_free(block->cipher); + qcrypto_ivgen_free(block->ivgen); + g_free(block); +} + + +int qcrypto_block_decrypt_helper(QCryptoCipher *cipher, + size_t niv, + QCryptoIVGen *ivgen, + int sectorsize, + uint64_t startsector, + uint8_t *buf, + size_t len, + Error **errp) +{ + uint8_t *iv; + int ret = -1; + + iv = niv ? g_new0(uint8_t, niv) : NULL; + + while (len > 0) { + size_t nbytes; + if (niv) { + if (qcrypto_ivgen_calculate(ivgen, + startsector, + iv, niv, + errp) < 0) { + goto cleanup; + } + + if (qcrypto_cipher_setiv(cipher, + iv, niv, + errp) < 0) { + goto cleanup; + } + } + + nbytes = len > sectorsize ? sectorsize : len; + if (qcrypto_cipher_decrypt(cipher, buf, buf, + nbytes, errp) < 0) { + goto cleanup; + } + + startsector++; + buf += nbytes; + len -= nbytes; + } + + ret = 0; + cleanup: + g_free(iv); + return ret; +} + + +int qcrypto_block_encrypt_helper(QCryptoCipher *cipher, + size_t niv, + QCryptoIVGen *ivgen, + int sectorsize, + uint64_t startsector, + uint8_t *buf, + size_t len, + Error **errp) +{ + uint8_t *iv; + int ret = -1; + + iv = niv ? g_new0(uint8_t, niv) : NULL; + + while (len > 0) { + size_t nbytes; + if (niv) { + if (qcrypto_ivgen_calculate(ivgen, + startsector, + iv, niv, + errp) < 0) { + goto cleanup; + } + + if (qcrypto_cipher_setiv(cipher, + iv, niv, + errp) < 0) { + goto cleanup; + } + } + + nbytes = len > sectorsize ? sectorsize : len; + if (qcrypto_cipher_encrypt(cipher, buf, buf, + nbytes, errp) < 0) { + goto cleanup; + } + + startsector++; + buf += nbytes; + len -= nbytes; + } + + ret = 0; + cleanup: + g_free(iv); + return ret; +} diff --git a/qemu/crypto/blockpriv.h b/qemu/crypto/blockpriv.h new file mode 100644 index 000000000..62970859d --- /dev/null +++ b/qemu/crypto/blockpriv.h @@ -0,0 +1,92 @@ +/* + * QEMU Crypto block device encryption + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QCRYPTO_BLOCK_PRIV_H__ +#define QCRYPTO_BLOCK_PRIV_H__ + +#include "crypto/block.h" + +typedef struct QCryptoBlockDriver QCryptoBlockDriver; + +struct QCryptoBlock { + QCryptoBlockFormat format; + + const QCryptoBlockDriver *driver; + void *opaque; + + QCryptoCipher *cipher; + QCryptoIVGen *ivgen; + QCryptoHashAlgorithm kdfhash; + size_t niv; + uint64_t payload_offset; /* In bytes */ +}; + +struct QCryptoBlockDriver { + int (*open)(QCryptoBlock *block, + QCryptoBlockOpenOptions *options, + QCryptoBlockReadFunc readfunc, + void *opaque, + unsigned int flags, + Error **errp); + + int (*create)(QCryptoBlock *block, + QCryptoBlockCreateOptions *options, + QCryptoBlockInitFunc initfunc, + QCryptoBlockWriteFunc writefunc, + void *opaque, + Error **errp); + + void (*cleanup)(QCryptoBlock *block); + + int (*encrypt)(QCryptoBlock *block, + uint64_t startsector, + uint8_t *buf, + size_t len, + Error **errp); + int (*decrypt)(QCryptoBlock *block, + uint64_t startsector, + uint8_t *buf, + size_t len, + Error **errp); + + bool (*has_format)(const uint8_t *buf, + size_t buflen); +}; + + +int qcrypto_block_decrypt_helper(QCryptoCipher *cipher, + size_t niv, + QCryptoIVGen *ivgen, + int sectorsize, + uint64_t startsector, + uint8_t *buf, + size_t len, + Error **errp); + +int qcrypto_block_encrypt_helper(QCryptoCipher *cipher, + size_t niv, + QCryptoIVGen *ivgen, + int sectorsize, + uint64_t startsector, + uint8_t *buf, + size_t len, + Error **errp); + +#endif /* QCRYPTO_BLOCK_PRIV_H__ */ diff --git a/qemu/crypto/cipher-builtin.c b/qemu/crypto/cipher-builtin.c index 30f4853c8..88963f65c 100644 --- a/qemu/crypto/cipher-builtin.c +++ b/qemu/crypto/cipher-builtin.c @@ -18,15 +18,21 @@ * */ +#include "qemu/osdep.h" #include "crypto/aes.h" #include "crypto/desrfb.h" +#include "crypto/xts.h" +typedef struct QCryptoCipherBuiltinAESContext QCryptoCipherBuiltinAESContext; +struct QCryptoCipherBuiltinAESContext { + AES_KEY enc; + AES_KEY dec; +}; typedef struct QCryptoCipherBuiltinAES QCryptoCipherBuiltinAES; struct QCryptoCipherBuiltinAES { - AES_KEY encrypt_key; - AES_KEY decrypt_key; - uint8_t *iv; - size_t niv; + QCryptoCipherBuiltinAESContext key; + QCryptoCipherBuiltinAESContext key_tweak; + uint8_t iv[AES_BLOCK_SIZE]; }; typedef struct QCryptoCipherBuiltinDESRFB QCryptoCipherBuiltinDESRFB; struct QCryptoCipherBuiltinDESRFB { @@ -40,6 +46,7 @@ struct QCryptoCipherBuiltin { QCryptoCipherBuiltinAES aes; QCryptoCipherBuiltinDESRFB desrfb; } state; + size_t blocksize; void (*free)(QCryptoCipher *cipher); int (*setiv)(QCryptoCipher *cipher, const uint8_t *iv, size_t niv, @@ -61,12 +68,87 @@ static void qcrypto_cipher_free_aes(QCryptoCipher *cipher) { QCryptoCipherBuiltin *ctxt = cipher->opaque; - g_free(ctxt->state.aes.iv); g_free(ctxt); cipher->opaque = NULL; } +static void qcrypto_cipher_aes_ecb_encrypt(AES_KEY *key, + const void *in, + void *out, + size_t len) +{ + const uint8_t *inptr = in; + uint8_t *outptr = out; + while (len) { + if (len > AES_BLOCK_SIZE) { + AES_encrypt(inptr, outptr, key); + inptr += AES_BLOCK_SIZE; + outptr += AES_BLOCK_SIZE; + len -= AES_BLOCK_SIZE; + } else { + uint8_t tmp1[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE]; + memcpy(tmp1, inptr, len); + /* Fill with 0 to avoid valgrind uninitialized reads */ + memset(tmp1 + len, 0, sizeof(tmp1) - len); + AES_encrypt(tmp1, tmp2, key); + memcpy(outptr, tmp2, len); + len = 0; + } + } +} + + +static void qcrypto_cipher_aes_ecb_decrypt(AES_KEY *key, + const void *in, + void *out, + size_t len) +{ + const uint8_t *inptr = in; + uint8_t *outptr = out; + while (len) { + if (len > AES_BLOCK_SIZE) { + AES_decrypt(inptr, outptr, key); + inptr += AES_BLOCK_SIZE; + outptr += AES_BLOCK_SIZE; + len -= AES_BLOCK_SIZE; + } else { + uint8_t tmp1[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE]; + memcpy(tmp1, inptr, len); + /* Fill with 0 to avoid valgrind uninitialized reads */ + memset(tmp1 + len, 0, sizeof(tmp1) - len); + AES_decrypt(tmp1, tmp2, key); + memcpy(outptr, tmp2, len); + len = 0; + } + } +} + + +static void qcrypto_cipher_aes_xts_encrypt(const void *ctx, + size_t length, + uint8_t *dst, + const uint8_t *src) +{ + const QCryptoCipherBuiltinAESContext *aesctx = ctx; + + qcrypto_cipher_aes_ecb_encrypt((AES_KEY *)&aesctx->enc, + src, dst, length); +} + + +static void qcrypto_cipher_aes_xts_decrypt(const void *ctx, + size_t length, + uint8_t *dst, + const uint8_t *src) +{ + const QCryptoCipherBuiltinAESContext *aesctx = ctx; + + qcrypto_cipher_aes_ecb_decrypt((AES_KEY *)&aesctx->dec, + src, dst, length); +} + + static int qcrypto_cipher_encrypt_aes(QCryptoCipher *cipher, const void *in, void *out, @@ -75,29 +157,26 @@ static int qcrypto_cipher_encrypt_aes(QCryptoCipher *cipher, { QCryptoCipherBuiltin *ctxt = cipher->opaque; - if (cipher->mode == QCRYPTO_CIPHER_MODE_ECB) { - const uint8_t *inptr = in; - uint8_t *outptr = out; - while (len) { - if (len > AES_BLOCK_SIZE) { - AES_encrypt(inptr, outptr, &ctxt->state.aes.encrypt_key); - inptr += AES_BLOCK_SIZE; - outptr += AES_BLOCK_SIZE; - len -= AES_BLOCK_SIZE; - } else { - uint8_t tmp1[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE]; - memcpy(tmp1, inptr, len); - /* Fill with 0 to avoid valgrind uninitialized reads */ - memset(tmp1 + len, 0, sizeof(tmp1) - len); - AES_encrypt(tmp1, tmp2, &ctxt->state.aes.encrypt_key); - memcpy(outptr, tmp2, len); - len = 0; - } - } - } else { + switch (cipher->mode) { + case QCRYPTO_CIPHER_MODE_ECB: + qcrypto_cipher_aes_ecb_encrypt(&ctxt->state.aes.key.enc, + in, out, len); + break; + case QCRYPTO_CIPHER_MODE_CBC: AES_cbc_encrypt(in, out, len, - &ctxt->state.aes.encrypt_key, + &ctxt->state.aes.key.enc, ctxt->state.aes.iv, 1); + break; + case QCRYPTO_CIPHER_MODE_XTS: + xts_encrypt(&ctxt->state.aes.key, + &ctxt->state.aes.key_tweak, + qcrypto_cipher_aes_xts_encrypt, + qcrypto_cipher_aes_xts_decrypt, + ctxt->state.aes.iv, + len, out, in); + break; + default: + g_assert_not_reached(); } return 0; @@ -112,29 +191,26 @@ static int qcrypto_cipher_decrypt_aes(QCryptoCipher *cipher, { QCryptoCipherBuiltin *ctxt = cipher->opaque; - if (cipher->mode == QCRYPTO_CIPHER_MODE_ECB) { - const uint8_t *inptr = in; - uint8_t *outptr = out; - while (len) { - if (len > AES_BLOCK_SIZE) { - AES_decrypt(inptr, outptr, &ctxt->state.aes.decrypt_key); - inptr += AES_BLOCK_SIZE; - outptr += AES_BLOCK_SIZE; - len -= AES_BLOCK_SIZE; - } else { - uint8_t tmp1[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE]; - memcpy(tmp1, inptr, len); - /* Fill with 0 to avoid valgrind uninitialized reads */ - memset(tmp1 + len, 0, sizeof(tmp1) - len); - AES_decrypt(tmp1, tmp2, &ctxt->state.aes.decrypt_key); - memcpy(outptr, tmp2, len); - len = 0; - } - } - } else { + switch (cipher->mode) { + case QCRYPTO_CIPHER_MODE_ECB: + qcrypto_cipher_aes_ecb_decrypt(&ctxt->state.aes.key.dec, + in, out, len); + break; + case QCRYPTO_CIPHER_MODE_CBC: AES_cbc_encrypt(in, out, len, - &ctxt->state.aes.decrypt_key, + &ctxt->state.aes.key.dec, ctxt->state.aes.iv, 0); + break; + case QCRYPTO_CIPHER_MODE_XTS: + xts_decrypt(&ctxt->state.aes.key, + &ctxt->state.aes.key_tweak, + qcrypto_cipher_aes_xts_encrypt, + qcrypto_cipher_aes_xts_decrypt, + ctxt->state.aes.iv, + len, out, in); + break; + default: + g_assert_not_reached(); } return 0; @@ -145,15 +221,13 @@ static int qcrypto_cipher_setiv_aes(QCryptoCipher *cipher, Error **errp) { QCryptoCipherBuiltin *ctxt = cipher->opaque; - if (niv != 16) { - error_setg(errp, "IV must be 16 bytes not %zu", niv); + if (niv != AES_BLOCK_SIZE) { + error_setg(errp, "IV must be %d bytes not %zu", + AES_BLOCK_SIZE, niv); return -1; } - g_free(ctxt->state.aes.iv); - ctxt->state.aes.iv = g_new0(uint8_t, niv); - memcpy(ctxt->state.aes.iv, iv, niv); - ctxt->state.aes.niv = niv; + memcpy(ctxt->state.aes.iv, iv, AES_BLOCK_SIZE); return 0; } @@ -168,23 +242,49 @@ static int qcrypto_cipher_init_aes(QCryptoCipher *cipher, QCryptoCipherBuiltin *ctxt; if (cipher->mode != QCRYPTO_CIPHER_MODE_CBC && - cipher->mode != QCRYPTO_CIPHER_MODE_ECB) { + cipher->mode != QCRYPTO_CIPHER_MODE_ECB && + cipher->mode != QCRYPTO_CIPHER_MODE_XTS) { error_setg(errp, "Unsupported cipher mode %d", cipher->mode); return -1; } ctxt = g_new0(QCryptoCipherBuiltin, 1); - if (AES_set_encrypt_key(key, nkey * 8, &ctxt->state.aes.encrypt_key) != 0) { - error_setg(errp, "Failed to set encryption key"); - goto error; - } + if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) { + if (AES_set_encrypt_key(key, nkey * 4, &ctxt->state.aes.key.enc) != 0) { + error_setg(errp, "Failed to set encryption key"); + goto error; + } - if (AES_set_decrypt_key(key, nkey * 8, &ctxt->state.aes.decrypt_key) != 0) { - error_setg(errp, "Failed to set decryption key"); - goto error; + if (AES_set_decrypt_key(key, nkey * 4, &ctxt->state.aes.key.dec) != 0) { + error_setg(errp, "Failed to set decryption key"); + goto error; + } + + if (AES_set_encrypt_key(key + (nkey / 2), nkey * 4, + &ctxt->state.aes.key_tweak.enc) != 0) { + error_setg(errp, "Failed to set encryption key"); + goto error; + } + + if (AES_set_decrypt_key(key + (nkey / 2), nkey * 4, + &ctxt->state.aes.key_tweak.dec) != 0) { + error_setg(errp, "Failed to set decryption key"); + goto error; + } + } else { + if (AES_set_encrypt_key(key, nkey * 8, &ctxt->state.aes.key.enc) != 0) { + error_setg(errp, "Failed to set encryption key"); + goto error; + } + + if (AES_set_decrypt_key(key, nkey * 8, &ctxt->state.aes.key.dec) != 0) { + error_setg(errp, "Failed to set decryption key"); + goto error; + } } + ctxt->blocksize = AES_BLOCK_SIZE; ctxt->free = qcrypto_cipher_free_aes; ctxt->setiv = qcrypto_cipher_setiv_aes; ctxt->encrypt = qcrypto_cipher_encrypt_aes; @@ -286,6 +386,7 @@ static int qcrypto_cipher_init_des_rfb(QCryptoCipher *cipher, memcpy(ctxt->state.desrfb.key, key, nkey); ctxt->state.desrfb.nkey = nkey; + ctxt->blocksize = 8; ctxt->free = qcrypto_cipher_free_des_rfb; ctxt->setiv = qcrypto_cipher_setiv_des_rfb; ctxt->encrypt = qcrypto_cipher_encrypt_des_rfb; @@ -322,7 +423,7 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, cipher->alg = alg; cipher->mode = mode; - if (!qcrypto_cipher_validate_key_length(alg, nkey, errp)) { + if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) { goto error; } @@ -374,6 +475,12 @@ int qcrypto_cipher_encrypt(QCryptoCipher *cipher, { QCryptoCipherBuiltin *ctxt = cipher->opaque; + if (len % ctxt->blocksize) { + error_setg(errp, "Length %zu must be a multiple of block size %zu", + len, ctxt->blocksize); + return -1; + } + return ctxt->encrypt(cipher, in, out, len, errp); } @@ -386,6 +493,12 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher, { QCryptoCipherBuiltin *ctxt = cipher->opaque; + if (len % ctxt->blocksize) { + error_setg(errp, "Length %zu must be a multiple of block size %zu", + len, ctxt->blocksize); + return -1; + } + return ctxt->decrypt(cipher, in, out, len, errp); } diff --git a/qemu/crypto/cipher-gcrypt.c b/qemu/crypto/cipher-gcrypt.c index 8cfc56250..ede2f70df 100644 --- a/qemu/crypto/cipher-gcrypt.c +++ b/qemu/crypto/cipher-gcrypt.c @@ -18,6 +18,9 @@ * */ +#include "qemu/osdep.h" +#include "crypto/xts.h" + #include @@ -28,12 +31,25 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg) case QCRYPTO_CIPHER_ALG_AES_128: case QCRYPTO_CIPHER_ALG_AES_192: case QCRYPTO_CIPHER_ALG_AES_256: + case QCRYPTO_CIPHER_ALG_CAST5_128: + case QCRYPTO_CIPHER_ALG_SERPENT_128: + case QCRYPTO_CIPHER_ALG_SERPENT_192: + case QCRYPTO_CIPHER_ALG_SERPENT_256: + case QCRYPTO_CIPHER_ALG_TWOFISH_128: + case QCRYPTO_CIPHER_ALG_TWOFISH_256: return true; default: return false; } } +typedef struct QCryptoCipherGcrypt QCryptoCipherGcrypt; +struct QCryptoCipherGcrypt { + gcry_cipher_hd_t handle; + gcry_cipher_hd_t tweakhandle; + size_t blocksize; + uint8_t *iv; +}; QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, QCryptoCipherMode mode, @@ -41,12 +57,13 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, Error **errp) { QCryptoCipher *cipher; - gcry_cipher_hd_t handle; + QCryptoCipherGcrypt *ctx; gcry_error_t err; int gcryalg, gcrymode; switch (mode) { case QCRYPTO_CIPHER_MODE_ECB: + case QCRYPTO_CIPHER_MODE_XTS: gcrymode = GCRY_CIPHER_MODE_ECB; break; case QCRYPTO_CIPHER_MODE_CBC: @@ -57,7 +74,7 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, return NULL; } - if (!qcrypto_cipher_validate_key_length(alg, nkey, errp)) { + if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) { return NULL; } @@ -78,6 +95,30 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, gcryalg = GCRY_CIPHER_AES256; break; + case QCRYPTO_CIPHER_ALG_CAST5_128: + gcryalg = GCRY_CIPHER_CAST5; + break; + + case QCRYPTO_CIPHER_ALG_SERPENT_128: + gcryalg = GCRY_CIPHER_SERPENT128; + break; + + case QCRYPTO_CIPHER_ALG_SERPENT_192: + gcryalg = GCRY_CIPHER_SERPENT192; + break; + + case QCRYPTO_CIPHER_ALG_SERPENT_256: + gcryalg = GCRY_CIPHER_SERPENT256; + break; + + case QCRYPTO_CIPHER_ALG_TWOFISH_128: + gcryalg = GCRY_CIPHER_TWOFISH128; + break; + + case QCRYPTO_CIPHER_ALG_TWOFISH_256: + gcryalg = GCRY_CIPHER_TWOFISH; + break; + default: error_setg(errp, "Unsupported cipher algorithm %d", alg); return NULL; @@ -87,12 +128,22 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, cipher->alg = alg; cipher->mode = mode; - err = gcry_cipher_open(&handle, gcryalg, gcrymode, 0); + ctx = g_new0(QCryptoCipherGcrypt, 1); + + err = gcry_cipher_open(&ctx->handle, gcryalg, gcrymode, 0); if (err != 0) { error_setg(errp, "Cannot initialize cipher: %s", gcry_strerror(err)); goto error; } + if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) { + err = gcry_cipher_open(&ctx->tweakhandle, gcryalg, gcrymode, 0); + if (err != 0) { + error_setg(errp, "Cannot initialize cipher: %s", + gcry_strerror(err)); + goto error; + } + } if (cipher->alg == QCRYPTO_CIPHER_ALG_DES_RFB) { /* We're using standard DES cipher from gcrypt, so we need @@ -100,22 +151,59 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, * bizarre RFB variant of DES :-) */ uint8_t *rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey); - err = gcry_cipher_setkey(handle, rfbkey, nkey); + err = gcry_cipher_setkey(ctx->handle, rfbkey, nkey); g_free(rfbkey); + ctx->blocksize = 8; } else { - err = gcry_cipher_setkey(handle, key, nkey); + if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) { + nkey /= 2; + err = gcry_cipher_setkey(ctx->handle, key, nkey); + if (err != 0) { + error_setg(errp, "Cannot set key: %s", + gcry_strerror(err)); + goto error; + } + err = gcry_cipher_setkey(ctx->tweakhandle, key + nkey, nkey); + } else { + err = gcry_cipher_setkey(ctx->handle, key, nkey); + } + if (err != 0) { + error_setg(errp, "Cannot set key: %s", + gcry_strerror(err)); + goto error; + } + switch (cipher->alg) { + case QCRYPTO_CIPHER_ALG_AES_128: + case QCRYPTO_CIPHER_ALG_AES_192: + case QCRYPTO_CIPHER_ALG_AES_256: + case QCRYPTO_CIPHER_ALG_SERPENT_128: + case QCRYPTO_CIPHER_ALG_SERPENT_192: + case QCRYPTO_CIPHER_ALG_SERPENT_256: + case QCRYPTO_CIPHER_ALG_TWOFISH_128: + case QCRYPTO_CIPHER_ALG_TWOFISH_256: + ctx->blocksize = 16; + break; + case QCRYPTO_CIPHER_ALG_CAST5_128: + ctx->blocksize = 8; + break; + default: + g_assert_not_reached(); + } } - if (err != 0) { - error_setg(errp, "Cannot set key: %s", - gcry_strerror(err)); - goto error; + + if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) { + ctx->iv = g_new0(uint8_t, ctx->blocksize); } - cipher->opaque = handle; + cipher->opaque = ctx; return cipher; error: - gcry_cipher_close(handle); + gcry_cipher_close(ctx->handle); + if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) { + gcry_cipher_close(ctx->tweakhandle); + } + g_free(ctx); g_free(cipher); return NULL; } @@ -123,34 +211,72 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, void qcrypto_cipher_free(QCryptoCipher *cipher) { - gcry_cipher_hd_t handle; + QCryptoCipherGcrypt *ctx; if (!cipher) { return; } - handle = cipher->opaque; - gcry_cipher_close(handle); + ctx = cipher->opaque; + gcry_cipher_close(ctx->handle); + if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) { + gcry_cipher_close(ctx->tweakhandle); + } + g_free(ctx->iv); + g_free(ctx); g_free(cipher); } +static void qcrypto_gcrypt_xts_encrypt(const void *ctx, + size_t length, + uint8_t *dst, + const uint8_t *src) +{ + gcry_error_t err; + err = gcry_cipher_encrypt((gcry_cipher_hd_t)ctx, dst, length, src, length); + g_assert(err == 0); +} + +static void qcrypto_gcrypt_xts_decrypt(const void *ctx, + size_t length, + uint8_t *dst, + const uint8_t *src) +{ + gcry_error_t err; + err = gcry_cipher_decrypt((gcry_cipher_hd_t)ctx, dst, length, src, length); + g_assert(err == 0); +} + int qcrypto_cipher_encrypt(QCryptoCipher *cipher, const void *in, void *out, size_t len, Error **errp) { - gcry_cipher_hd_t handle = cipher->opaque; + QCryptoCipherGcrypt *ctx = cipher->opaque; gcry_error_t err; - err = gcry_cipher_encrypt(handle, - out, len, - in, len); - if (err != 0) { - error_setg(errp, "Cannot encrypt data: %s", - gcry_strerror(err)); + if (len % ctx->blocksize) { + error_setg(errp, "Length %zu must be a multiple of block size %zu", + len, ctx->blocksize); return -1; } + if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) { + xts_encrypt(ctx->handle, ctx->tweakhandle, + qcrypto_gcrypt_xts_encrypt, + qcrypto_gcrypt_xts_decrypt, + ctx->iv, len, out, in); + } else { + err = gcry_cipher_encrypt(ctx->handle, + out, len, + in, len); + if (err != 0) { + error_setg(errp, "Cannot encrypt data: %s", + gcry_strerror(err)); + return -1; + } + } + return 0; } @@ -161,18 +287,31 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher, size_t len, Error **errp) { - gcry_cipher_hd_t handle = cipher->opaque; + QCryptoCipherGcrypt *ctx = cipher->opaque; gcry_error_t err; - err = gcry_cipher_decrypt(handle, - out, len, - in, len); - if (err != 0) { - error_setg(errp, "Cannot decrypt data: %s", - gcry_strerror(err)); + if (len % ctx->blocksize) { + error_setg(errp, "Length %zu must be a multiple of block size %zu", + len, ctx->blocksize); return -1; } + if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) { + xts_decrypt(ctx->handle, ctx->tweakhandle, + qcrypto_gcrypt_xts_encrypt, + qcrypto_gcrypt_xts_decrypt, + ctx->iv, len, out, in); + } else { + err = gcry_cipher_decrypt(ctx->handle, + out, len, + in, len); + if (err != 0) { + error_setg(errp, "Cannot decrypt data: %s", + gcry_strerror(err)); + return -1; + } + } + return 0; } @@ -180,16 +319,26 @@ int qcrypto_cipher_setiv(QCryptoCipher *cipher, const uint8_t *iv, size_t niv, Error **errp) { - gcry_cipher_hd_t handle = cipher->opaque; + QCryptoCipherGcrypt *ctx = cipher->opaque; gcry_error_t err; - gcry_cipher_reset(handle); - err = gcry_cipher_setiv(handle, iv, niv); - if (err != 0) { - error_setg(errp, "Cannot set IV: %s", - gcry_strerror(err)); + if (niv != ctx->blocksize) { + error_setg(errp, "Expected IV size %zu not %zu", + ctx->blocksize, niv); return -1; } + if (ctx->iv) { + memcpy(ctx->iv, iv, niv); + } else { + gcry_cipher_reset(ctx->handle); + err = gcry_cipher_setiv(ctx->handle, iv, niv); + if (err != 0) { + error_setg(errp, "Cannot set IV: %s", + gcry_strerror(err)); + return -1; + } + } + return 0; } diff --git a/qemu/crypto/cipher-nettle.c b/qemu/crypto/cipher-nettle.c index b01cb1c85..70909fb7f 100644 --- a/qemu/crypto/cipher-nettle.c +++ b/qemu/crypto/cipher-nettle.c @@ -18,58 +18,177 @@ * */ +#include "qemu/osdep.h" +#include "crypto/xts.h" + #include #include #include #include +#include +#include +#include -#if CONFIG_NETTLE_VERSION_MAJOR < 3 -typedef nettle_crypt_func nettle_cipher_func; +typedef void (*QCryptoCipherNettleFuncWrapper)(const void *ctx, + size_t length, + uint8_t *dst, + const uint8_t *src); +#if CONFIG_NETTLE_VERSION_MAJOR < 3 +typedef nettle_crypt_func * QCryptoCipherNettleFuncNative; typedef void * cipher_ctx_t; typedef unsigned cipher_length_t; + +#define cast5_set_key cast128_set_key #else +typedef nettle_cipher_func * QCryptoCipherNettleFuncNative; typedef const void * cipher_ctx_t; typedef size_t cipher_length_t; #endif -static nettle_cipher_func aes_encrypt_wrapper; -static nettle_cipher_func aes_decrypt_wrapper; -static nettle_cipher_func des_encrypt_wrapper; -static nettle_cipher_func des_decrypt_wrapper; +typedef struct QCryptoNettleAES { + struct aes_ctx enc; + struct aes_ctx dec; +} QCryptoNettleAES; + +static void aes_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + const QCryptoNettleAES *aesctx = ctx; + aes_encrypt(&aesctx->enc, length, dst, src); +} + +static void aes_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + const QCryptoNettleAES *aesctx = ctx; + aes_decrypt(&aesctx->dec, length, dst, src); +} + +static void des_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + des_encrypt(ctx, length, dst, src); +} + +static void des_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + des_decrypt(ctx, length, dst, src); +} + +static void cast128_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + cast128_encrypt(ctx, length, dst, src); +} + +static void cast128_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + cast128_decrypt(ctx, length, dst, src); +} + +static void serpent_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + serpent_encrypt(ctx, length, dst, src); +} -static void aes_encrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length, +static void serpent_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + serpent_decrypt(ctx, length, dst, src); +} + +static void twofish_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + twofish_encrypt(ctx, length, dst, src); +} + +static void twofish_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + twofish_decrypt(ctx, length, dst, src); +} + +static void aes_encrypt_wrapper(const void *ctx, size_t length, uint8_t *dst, const uint8_t *src) { - aes_encrypt(ctx, length, dst, src); + const QCryptoNettleAES *aesctx = ctx; + aes_encrypt(&aesctx->enc, length, dst, src); } -static void aes_decrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length, +static void aes_decrypt_wrapper(const void *ctx, size_t length, uint8_t *dst, const uint8_t *src) { - aes_decrypt(ctx, length, dst, src); + const QCryptoNettleAES *aesctx = ctx; + aes_decrypt(&aesctx->dec, length, dst, src); } -static void des_encrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length, +static void des_encrypt_wrapper(const void *ctx, size_t length, uint8_t *dst, const uint8_t *src) { des_encrypt(ctx, length, dst, src); } -static void des_decrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length, +static void des_decrypt_wrapper(const void *ctx, size_t length, uint8_t *dst, const uint8_t *src) { des_decrypt(ctx, length, dst, src); } +static void cast128_encrypt_wrapper(const void *ctx, size_t length, + uint8_t *dst, const uint8_t *src) +{ + cast128_encrypt(ctx, length, dst, src); +} + +static void cast128_decrypt_wrapper(const void *ctx, size_t length, + uint8_t *dst, const uint8_t *src) +{ + cast128_decrypt(ctx, length, dst, src); +} + +static void serpent_encrypt_wrapper(const void *ctx, size_t length, + uint8_t *dst, const uint8_t *src) +{ + serpent_encrypt(ctx, length, dst, src); +} + +static void serpent_decrypt_wrapper(const void *ctx, size_t length, + uint8_t *dst, const uint8_t *src) +{ + serpent_decrypt(ctx, length, dst, src); +} + +static void twofish_encrypt_wrapper(const void *ctx, size_t length, + uint8_t *dst, const uint8_t *src) +{ + twofish_encrypt(ctx, length, dst, src); +} + +static void twofish_decrypt_wrapper(const void *ctx, size_t length, + uint8_t *dst, const uint8_t *src) +{ + twofish_decrypt(ctx, length, dst, src); +} + typedef struct QCryptoCipherNettle QCryptoCipherNettle; struct QCryptoCipherNettle { - void *ctx_encrypt; - void *ctx_decrypt; - nettle_cipher_func *alg_encrypt; - nettle_cipher_func *alg_decrypt; + /* Primary cipher context for all modes */ + void *ctx; + /* Second cipher context for XTS mode only */ + void *ctx_tweak; + /* Cipher callbacks for both contexts */ + QCryptoCipherNettleFuncNative alg_encrypt_native; + QCryptoCipherNettleFuncNative alg_decrypt_native; + QCryptoCipherNettleFuncWrapper alg_encrypt_wrapper; + QCryptoCipherNettleFuncWrapper alg_decrypt_wrapper; + uint8_t *iv; - size_t niv; + size_t blocksize; }; bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg) @@ -79,6 +198,13 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg) case QCRYPTO_CIPHER_ALG_AES_128: case QCRYPTO_CIPHER_ALG_AES_192: case QCRYPTO_CIPHER_ALG_AES_256: + case QCRYPTO_CIPHER_ALG_CAST5_128: + case QCRYPTO_CIPHER_ALG_SERPENT_128: + case QCRYPTO_CIPHER_ALG_SERPENT_192: + case QCRYPTO_CIPHER_ALG_SERPENT_256: + case QCRYPTO_CIPHER_ALG_TWOFISH_128: + case QCRYPTO_CIPHER_ALG_TWOFISH_192: + case QCRYPTO_CIPHER_ALG_TWOFISH_256: return true; default: return false; @@ -98,13 +224,14 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, switch (mode) { case QCRYPTO_CIPHER_MODE_ECB: case QCRYPTO_CIPHER_MODE_CBC: + case QCRYPTO_CIPHER_MODE_XTS: break; default: error_setg(errp, "Unsupported cipher mode %d", mode); return NULL; } - if (!qcrypto_cipher_validate_key_length(alg, nkey, errp)) { + if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) { return NULL; } @@ -116,38 +243,125 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, switch (alg) { case QCRYPTO_CIPHER_ALG_DES_RFB: - ctx->ctx_encrypt = g_new0(struct des_ctx, 1); - ctx->ctx_decrypt = NULL; /* 1 ctx can do both */ + ctx->ctx = g_new0(struct des_ctx, 1); rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey); - des_set_key(ctx->ctx_encrypt, rfbkey); + des_set_key(ctx->ctx, rfbkey); g_free(rfbkey); - ctx->alg_encrypt = des_encrypt_wrapper; - ctx->alg_decrypt = des_decrypt_wrapper; + ctx->alg_encrypt_native = des_encrypt_native; + ctx->alg_decrypt_native = des_decrypt_native; + ctx->alg_encrypt_wrapper = des_encrypt_wrapper; + ctx->alg_decrypt_wrapper = des_decrypt_wrapper; - ctx->niv = DES_BLOCK_SIZE; + ctx->blocksize = DES_BLOCK_SIZE; break; case QCRYPTO_CIPHER_ALG_AES_128: case QCRYPTO_CIPHER_ALG_AES_192: case QCRYPTO_CIPHER_ALG_AES_256: - ctx->ctx_encrypt = g_new0(struct aes_ctx, 1); - ctx->ctx_decrypt = g_new0(struct aes_ctx, 1); + ctx->ctx = g_new0(QCryptoNettleAES, 1); + + if (mode == QCRYPTO_CIPHER_MODE_XTS) { + ctx->ctx_tweak = g_new0(QCryptoNettleAES, 1); + + nkey /= 2; + aes_set_encrypt_key(&((QCryptoNettleAES *)ctx->ctx)->enc, + nkey, key); + aes_set_decrypt_key(&((QCryptoNettleAES *)ctx->ctx)->dec, + nkey, key); + + aes_set_encrypt_key(&((QCryptoNettleAES *)ctx->ctx_tweak)->enc, + nkey, key + nkey); + aes_set_decrypt_key(&((QCryptoNettleAES *)ctx->ctx_tweak)->dec, + nkey, key + nkey); + } else { + aes_set_encrypt_key(&((QCryptoNettleAES *)ctx->ctx)->enc, + nkey, key); + aes_set_decrypt_key(&((QCryptoNettleAES *)ctx->ctx)->dec, + nkey, key); + } + + ctx->alg_encrypt_native = aes_encrypt_native; + ctx->alg_decrypt_native = aes_decrypt_native; + ctx->alg_encrypt_wrapper = aes_encrypt_wrapper; + ctx->alg_decrypt_wrapper = aes_decrypt_wrapper; + + ctx->blocksize = AES_BLOCK_SIZE; + break; + + case QCRYPTO_CIPHER_ALG_CAST5_128: + ctx->ctx = g_new0(struct cast128_ctx, 1); + + if (mode == QCRYPTO_CIPHER_MODE_XTS) { + ctx->ctx_tweak = g_new0(struct cast128_ctx, 1); - aes_set_encrypt_key(ctx->ctx_encrypt, nkey, key); - aes_set_decrypt_key(ctx->ctx_decrypt, nkey, key); + nkey /= 2; + cast5_set_key(ctx->ctx, nkey, key); + cast5_set_key(ctx->ctx_tweak, nkey, key + nkey); + } else { + cast5_set_key(ctx->ctx, nkey, key); + } - ctx->alg_encrypt = aes_encrypt_wrapper; - ctx->alg_decrypt = aes_decrypt_wrapper; + ctx->alg_encrypt_native = cast128_encrypt_native; + ctx->alg_decrypt_native = cast128_decrypt_native; + ctx->alg_encrypt_wrapper = cast128_encrypt_wrapper; + ctx->alg_decrypt_wrapper = cast128_decrypt_wrapper; - ctx->niv = AES_BLOCK_SIZE; + ctx->blocksize = CAST128_BLOCK_SIZE; break; + + case QCRYPTO_CIPHER_ALG_SERPENT_128: + case QCRYPTO_CIPHER_ALG_SERPENT_192: + case QCRYPTO_CIPHER_ALG_SERPENT_256: + ctx->ctx = g_new0(struct serpent_ctx, 1); + + if (mode == QCRYPTO_CIPHER_MODE_XTS) { + ctx->ctx_tweak = g_new0(struct serpent_ctx, 1); + + nkey /= 2; + serpent_set_key(ctx->ctx, nkey, key); + serpent_set_key(ctx->ctx_tweak, nkey, key + nkey); + } else { + serpent_set_key(ctx->ctx, nkey, key); + } + + ctx->alg_encrypt_native = serpent_encrypt_native; + ctx->alg_decrypt_native = serpent_decrypt_native; + ctx->alg_encrypt_wrapper = serpent_encrypt_wrapper; + ctx->alg_decrypt_wrapper = serpent_decrypt_wrapper; + + ctx->blocksize = SERPENT_BLOCK_SIZE; + break; + + case QCRYPTO_CIPHER_ALG_TWOFISH_128: + case QCRYPTO_CIPHER_ALG_TWOFISH_192: + case QCRYPTO_CIPHER_ALG_TWOFISH_256: + ctx->ctx = g_new0(struct twofish_ctx, 1); + + if (mode == QCRYPTO_CIPHER_MODE_XTS) { + ctx->ctx_tweak = g_new0(struct twofish_ctx, 1); + + nkey /= 2; + twofish_set_key(ctx->ctx, nkey, key); + twofish_set_key(ctx->ctx_tweak, nkey, key + nkey); + } else { + twofish_set_key(ctx->ctx, nkey, key); + } + + ctx->alg_encrypt_native = twofish_encrypt_native; + ctx->alg_decrypt_native = twofish_decrypt_native; + ctx->alg_encrypt_wrapper = twofish_encrypt_wrapper; + ctx->alg_decrypt_wrapper = twofish_decrypt_wrapper; + + ctx->blocksize = TWOFISH_BLOCK_SIZE; + break; + default: error_setg(errp, "Unsupported cipher algorithm %d", alg); goto error; } - ctx->iv = g_new0(uint8_t, ctx->niv); + ctx->iv = g_new0(uint8_t, ctx->blocksize); cipher->opaque = ctx; return cipher; @@ -169,8 +383,8 @@ void qcrypto_cipher_free(QCryptoCipher *cipher) ctx = cipher->opaque; g_free(ctx->iv); - g_free(ctx->ctx_encrypt); - g_free(ctx->ctx_decrypt); + g_free(ctx->ctx); + g_free(ctx->ctx_tweak); g_free(ctx); g_free(cipher); } @@ -184,16 +398,29 @@ int qcrypto_cipher_encrypt(QCryptoCipher *cipher, { QCryptoCipherNettle *ctx = cipher->opaque; + if (len % ctx->blocksize) { + error_setg(errp, "Length %zu must be a multiple of block size %zu", + len, ctx->blocksize); + return -1; + } + switch (cipher->mode) { case QCRYPTO_CIPHER_MODE_ECB: - ctx->alg_encrypt(ctx->ctx_encrypt, len, out, in); + ctx->alg_encrypt_wrapper(ctx->ctx, len, out, in); break; case QCRYPTO_CIPHER_MODE_CBC: - cbc_encrypt(ctx->ctx_encrypt, ctx->alg_encrypt, - ctx->niv, ctx->iv, + cbc_encrypt(ctx->ctx, ctx->alg_encrypt_native, + ctx->blocksize, ctx->iv, len, out, in); break; + + case QCRYPTO_CIPHER_MODE_XTS: + xts_encrypt(ctx->ctx, ctx->ctx_tweak, + ctx->alg_encrypt_wrapper, ctx->alg_encrypt_wrapper, + ctx->iv, len, out, in); + break; + default: error_setg(errp, "Unsupported cipher algorithm %d", cipher->alg); @@ -211,17 +438,34 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher, { QCryptoCipherNettle *ctx = cipher->opaque; + if (len % ctx->blocksize) { + error_setg(errp, "Length %zu must be a multiple of block size %zu", + len, ctx->blocksize); + return -1; + } + switch (cipher->mode) { case QCRYPTO_CIPHER_MODE_ECB: - ctx->alg_decrypt(ctx->ctx_decrypt ? ctx->ctx_decrypt : ctx->ctx_encrypt, - len, out, in); + ctx->alg_decrypt_wrapper(ctx->ctx, len, out, in); break; case QCRYPTO_CIPHER_MODE_CBC: - cbc_decrypt(ctx->ctx_decrypt ? ctx->ctx_decrypt : ctx->ctx_encrypt, - ctx->alg_decrypt, ctx->niv, ctx->iv, + cbc_decrypt(ctx->ctx, ctx->alg_decrypt_native, + ctx->blocksize, ctx->iv, len, out, in); break; + + case QCRYPTO_CIPHER_MODE_XTS: + if (ctx->blocksize != XTS_BLOCK_SIZE) { + error_setg(errp, "Block size must be %d not %zu", + XTS_BLOCK_SIZE, ctx->blocksize); + return -1; + } + xts_decrypt(ctx->ctx, ctx->ctx_tweak, + ctx->alg_encrypt_wrapper, ctx->alg_decrypt_wrapper, + ctx->iv, len, out, in); + break; + default: error_setg(errp, "Unsupported cipher algorithm %d", cipher->alg); @@ -235,9 +479,9 @@ int qcrypto_cipher_setiv(QCryptoCipher *cipher, Error **errp) { QCryptoCipherNettle *ctx = cipher->opaque; - if (niv != ctx->niv) { + if (niv != ctx->blocksize) { error_setg(errp, "Expected IV size %zu not %zu", - ctx->niv, niv); + ctx->blocksize, niv); return -1; } memcpy(ctx->iv, iv, niv); diff --git a/qemu/crypto/cipher.c b/qemu/crypto/cipher.c index 024a00cb5..cafb45436 100644 --- a/qemu/crypto/cipher.c +++ b/qemu/crypto/cipher.c @@ -18,36 +18,119 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "crypto/cipher.h" -static size_t alg_key_len[QCRYPTO_CIPHER_ALG_LAST] = { +static size_t alg_key_len[QCRYPTO_CIPHER_ALG__MAX] = { [QCRYPTO_CIPHER_ALG_AES_128] = 16, [QCRYPTO_CIPHER_ALG_AES_192] = 24, [QCRYPTO_CIPHER_ALG_AES_256] = 32, [QCRYPTO_CIPHER_ALG_DES_RFB] = 8, + [QCRYPTO_CIPHER_ALG_CAST5_128] = 16, + [QCRYPTO_CIPHER_ALG_SERPENT_128] = 16, + [QCRYPTO_CIPHER_ALG_SERPENT_192] = 24, + [QCRYPTO_CIPHER_ALG_SERPENT_256] = 32, + [QCRYPTO_CIPHER_ALG_TWOFISH_128] = 16, + [QCRYPTO_CIPHER_ALG_TWOFISH_192] = 24, + [QCRYPTO_CIPHER_ALG_TWOFISH_256] = 32, }; +static size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX] = { + [QCRYPTO_CIPHER_ALG_AES_128] = 16, + [QCRYPTO_CIPHER_ALG_AES_192] = 16, + [QCRYPTO_CIPHER_ALG_AES_256] = 16, + [QCRYPTO_CIPHER_ALG_DES_RFB] = 8, + [QCRYPTO_CIPHER_ALG_CAST5_128] = 8, + [QCRYPTO_CIPHER_ALG_SERPENT_128] = 16, + [QCRYPTO_CIPHER_ALG_SERPENT_192] = 16, + [QCRYPTO_CIPHER_ALG_SERPENT_256] = 16, + [QCRYPTO_CIPHER_ALG_TWOFISH_128] = 16, + [QCRYPTO_CIPHER_ALG_TWOFISH_192] = 16, + [QCRYPTO_CIPHER_ALG_TWOFISH_256] = 16, +}; + +static bool mode_need_iv[QCRYPTO_CIPHER_MODE__MAX] = { + [QCRYPTO_CIPHER_MODE_ECB] = false, + [QCRYPTO_CIPHER_MODE_CBC] = true, + [QCRYPTO_CIPHER_MODE_XTS] = true, +}; + + +size_t qcrypto_cipher_get_block_len(QCryptoCipherAlgorithm alg) +{ + if (alg >= G_N_ELEMENTS(alg_key_len)) { + return 0; + } + return alg_block_len[alg]; +} + + +size_t qcrypto_cipher_get_key_len(QCryptoCipherAlgorithm alg) +{ + if (alg >= G_N_ELEMENTS(alg_key_len)) { + return 0; + } + return alg_key_len[alg]; +} + + +size_t qcrypto_cipher_get_iv_len(QCryptoCipherAlgorithm alg, + QCryptoCipherMode mode) +{ + if (alg >= G_N_ELEMENTS(alg_block_len)) { + return 0; + } + if (mode >= G_N_ELEMENTS(mode_need_iv)) { + return 0; + } + + if (mode_need_iv[mode]) { + return alg_block_len[alg]; + } + return 0; +} + + static bool qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg, + QCryptoCipherMode mode, size_t nkey, Error **errp) { - if ((unsigned)alg >= QCRYPTO_CIPHER_ALG_LAST) { + if ((unsigned)alg >= QCRYPTO_CIPHER_ALG__MAX) { error_setg(errp, "Cipher algorithm %d out of range", alg); return false; } - if (alg_key_len[alg] != nkey) { - error_setg(errp, "Cipher key length %zu should be %zu", - alg_key_len[alg], nkey); - return false; + if (mode == QCRYPTO_CIPHER_MODE_XTS) { + if (alg == QCRYPTO_CIPHER_ALG_DES_RFB) { + error_setg(errp, "XTS mode not compatible with DES-RFB"); + return false; + } + if (nkey % 2) { + error_setg(errp, "XTS cipher key length should be a multiple of 2"); + return false; + } + + if (alg_key_len[alg] != (nkey / 2)) { + error_setg(errp, "Cipher key length %zu should be %zu", + nkey, alg_key_len[alg] * 2); + return false; + } + } else { + if (alg_key_len[alg] != nkey) { + error_setg(errp, "Cipher key length %zu should be %zu", + nkey, alg_key_len[alg]); + return false; + } } return true; } -#if defined(CONFIG_GNUTLS_GCRYPT) || defined(CONFIG_GNUTLS_NETTLE) +#if defined(CONFIG_GCRYPT) || defined(CONFIG_NETTLE) static uint8_t * qcrypto_cipher_munge_des_rfb_key(const uint8_t *key, size_t nkey) @@ -63,11 +146,11 @@ qcrypto_cipher_munge_des_rfb_key(const uint8_t *key, } return ret; } -#endif /* CONFIG_GNUTLS_GCRYPT || CONFIG_GNUTLS_NETTLE */ +#endif /* CONFIG_GCRYPT || CONFIG_NETTLE */ -#ifdef CONFIG_GNUTLS_GCRYPT +#ifdef CONFIG_GCRYPT #include "crypto/cipher-gcrypt.c" -#elif defined CONFIG_GNUTLS_NETTLE +#elif defined CONFIG_NETTLE #include "crypto/cipher-nettle.c" #else #include "crypto/cipher-builtin.c" diff --git a/qemu/crypto/desrfb.c b/qemu/crypto/desrfb.c index fc20a30df..ec47dea3b 100644 --- a/qemu/crypto/desrfb.c +++ b/qemu/crypto/desrfb.c @@ -26,6 +26,7 @@ * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992. */ +#include "qemu/osdep.h" #include "crypto/desrfb.h" static void scrunch(unsigned char *, unsigned long *); diff --git a/qemu/crypto/hash.c b/qemu/crypto/hash.c index 81e74de86..b90af3495 100644 --- a/qemu/crypto/hash.c +++ b/qemu/crypto/hash.c @@ -18,13 +18,33 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "crypto/hash.h" #ifdef CONFIG_GNUTLS_HASH #include #include +#endif -static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG_LAST] = { + +static size_t qcrypto_hash_alg_size[QCRYPTO_HASH_ALG__MAX] = { + [QCRYPTO_HASH_ALG_MD5] = 16, + [QCRYPTO_HASH_ALG_SHA1] = 20, + [QCRYPTO_HASH_ALG_SHA256] = 32, +}; + +size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg) +{ + if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_size)) { + return 0; + } + return qcrypto_hash_alg_size[alg]; +} + + +#ifdef CONFIG_GNUTLS_HASH +static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = { [QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5, [QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1, [QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256, @@ -38,6 +58,7 @@ gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg) return false; } + int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, const struct iovec *iov, size_t niov, diff --git a/qemu/crypto/init.c b/qemu/crypto/init.c index 7447882c7..1e564d949 100644 --- a/qemu/crypto/init.c +++ b/qemu/crypto/init.c @@ -18,14 +18,17 @@ * */ +#include "qemu/osdep.h" #include "crypto/init.h" +#include "qapi/error.h" #include "qemu/thread.h" #ifdef CONFIG_GNUTLS #include #include +#endif -#ifdef CONFIG_GNUTLS_GCRYPT +#ifdef CONFIG_GCRYPT #include #endif @@ -37,6 +40,7 @@ * - When GNUTLS >= 2.12, we must not initialize gcrypt threading * because GNUTLS will do that itself * - When GNUTLS < 2.12 we must always initialize gcrypt threading + * - When GNUTLS is disabled we must always initialize gcrypt threading * * But.... * @@ -47,12 +51,15 @@ * * - gcrypt < 1.6.0 * AND - * - gnutls < 2.12 + * - gnutls < 2.12 + * OR + * - gnutls is disabled * */ -#if (defined(CONFIG_GNUTLS_GCRYPT) && \ - (!defined(GNUTLS_VERSION_NUMBER) || \ +#if (defined(CONFIG_GCRYPT) && \ + (!defined(CONFIG_GNUTLS) || \ + !defined(GNUTLS_VERSION_NUMBER) || \ (GNUTLS_VERSION_NUMBER < 0x020c00)) && \ (!defined(GCRYPT_VERSION_NUMBER) || \ (GCRYPT_VERSION_NUMBER < 0x010600))) @@ -113,6 +120,7 @@ static struct gcry_thread_cbs qcrypto_gcrypt_thread_impl = { int qcrypto_init(Error **errp) { +#ifdef CONFIG_GNUTLS int ret; ret = gnutls_global_init(); if (ret < 0) { @@ -125,8 +133,9 @@ int qcrypto_init(Error **errp) gnutls_global_set_log_level(10); gnutls_global_set_log_function(qcrypto_gnutls_log); #endif +#endif -#ifdef CONFIG_GNUTLS_GCRYPT +#ifdef CONFIG_GCRYPT if (!gcry_check_version(GCRYPT_VERSION)) { error_setg(errp, "Unable to initialize gcrypt"); return -1; @@ -139,12 +148,3 @@ int qcrypto_init(Error **errp) return 0; } - -#else /* ! CONFIG_GNUTLS */ - -int qcrypto_init(Error **errp G_GNUC_UNUSED) -{ - return 0; -} - -#endif /* ! CONFIG_GNUTLS */ diff --git a/qemu/crypto/ivgen-essiv.c b/qemu/crypto/ivgen-essiv.c new file mode 100644 index 000000000..634de6333 --- /dev/null +++ b/qemu/crypto/ivgen-essiv.c @@ -0,0 +1,120 @@ +/* + * QEMU Crypto block IV generator - essiv + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/bswap.h" +#include "crypto/ivgen-essiv.h" + +typedef struct QCryptoIVGenESSIV QCryptoIVGenESSIV; +struct QCryptoIVGenESSIV { + QCryptoCipher *cipher; +}; + +static int qcrypto_ivgen_essiv_init(QCryptoIVGen *ivgen, + const uint8_t *key, size_t nkey, + Error **errp) +{ + uint8_t *salt; + size_t nhash; + size_t nsalt; + QCryptoIVGenESSIV *essiv = g_new0(QCryptoIVGenESSIV, 1); + + /* Not necessarily the same as nkey */ + nsalt = qcrypto_cipher_get_key_len(ivgen->cipher); + + nhash = qcrypto_hash_digest_len(ivgen->hash); + /* Salt must be larger of hash size or key size */ + salt = g_new0(uint8_t, MAX(nhash, nsalt)); + + if (qcrypto_hash_bytes(ivgen->hash, (const gchar *)key, nkey, + &salt, &nhash, + errp) < 0) { + g_free(essiv); + return -1; + } + + /* Now potentially truncate salt to match cipher key len */ + essiv->cipher = qcrypto_cipher_new(ivgen->cipher, + QCRYPTO_CIPHER_MODE_ECB, + salt, MIN(nhash, nsalt), + errp); + if (!essiv->cipher) { + g_free(essiv); + g_free(salt); + return -1; + } + + g_free(salt); + ivgen->private = essiv; + + return 0; +} + +static int qcrypto_ivgen_essiv_calculate(QCryptoIVGen *ivgen, + uint64_t sector, + uint8_t *iv, size_t niv, + Error **errp) +{ + QCryptoIVGenESSIV *essiv = ivgen->private; + size_t ndata = qcrypto_cipher_get_block_len(ivgen->cipher); + uint8_t *data = g_new(uint8_t, ndata); + + sector = cpu_to_le64(sector); + memcpy(data, (uint8_t *)§or, ndata); + if (sizeof(sector) < ndata) { + memset(data + sizeof(sector), 0, ndata - sizeof(sector)); + } + + if (qcrypto_cipher_encrypt(essiv->cipher, + data, + data, + ndata, + errp) < 0) { + g_free(data); + return -1; + } + + if (ndata > niv) { + ndata = niv; + } + memcpy(iv, data, ndata); + if (ndata < niv) { + memset(iv + ndata, 0, niv - ndata); + } + g_free(data); + return 0; +} + +static void qcrypto_ivgen_essiv_cleanup(QCryptoIVGen *ivgen) +{ + QCryptoIVGenESSIV *essiv = ivgen->private; + + qcrypto_cipher_free(essiv->cipher); + g_free(essiv); +} + + +struct QCryptoIVGenDriver qcrypto_ivgen_essiv = { + .init = qcrypto_ivgen_essiv_init, + .calculate = qcrypto_ivgen_essiv_calculate, + .cleanup = qcrypto_ivgen_essiv_cleanup, +}; + diff --git a/qemu/crypto/ivgen-essiv.h b/qemu/crypto/ivgen-essiv.h new file mode 100644 index 000000000..4a00af849 --- /dev/null +++ b/qemu/crypto/ivgen-essiv.h @@ -0,0 +1,28 @@ +/* + * QEMU Crypto block IV generator - essiv + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "crypto/ivgenpriv.h" + +#ifndef QCRYPTO_IVGEN_ESSIV_H__ +#define QCRYPTO_IVGEN_ESSIV_H__ + +extern struct QCryptoIVGenDriver qcrypto_ivgen_essiv; + +#endif /* QCRYPTO_IVGEN_ESSIV_H__ */ diff --git a/qemu/crypto/ivgen-plain.c b/qemu/crypto/ivgen-plain.c new file mode 100644 index 000000000..9b9b4ad0b --- /dev/null +++ b/qemu/crypto/ivgen-plain.c @@ -0,0 +1,61 @@ +/* + * QEMU Crypto block IV generator - plain + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/bswap.h" +#include "crypto/ivgen-plain.h" + +static int qcrypto_ivgen_plain_init(QCryptoIVGen *ivgen, + const uint8_t *key, size_t nkey, + Error **errp) +{ + return 0; +} + +static int qcrypto_ivgen_plain_calculate(QCryptoIVGen *ivgen, + uint64_t sector, + uint8_t *iv, size_t niv, + Error **errp) +{ + size_t ivprefix; + uint32_t shortsector = cpu_to_le32((sector & 0xffffffff)); + ivprefix = sizeof(shortsector); + if (ivprefix > niv) { + ivprefix = niv; + } + memcpy(iv, &shortsector, ivprefix); + if (ivprefix < niv) { + memset(iv + ivprefix, 0, niv - ivprefix); + } + return 0; +} + +static void qcrypto_ivgen_plain_cleanup(QCryptoIVGen *ivgen) +{ +} + + +struct QCryptoIVGenDriver qcrypto_ivgen_plain = { + .init = qcrypto_ivgen_plain_init, + .calculate = qcrypto_ivgen_plain_calculate, + .cleanup = qcrypto_ivgen_plain_cleanup, +}; + diff --git a/qemu/crypto/ivgen-plain.h b/qemu/crypto/ivgen-plain.h new file mode 100644 index 000000000..0fe8835c3 --- /dev/null +++ b/qemu/crypto/ivgen-plain.h @@ -0,0 +1,28 @@ +/* + * QEMU Crypto block IV generator - plain + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "crypto/ivgenpriv.h" + +#ifndef QCRYPTO_IVGEN_PLAIN_H__ +#define QCRYPTO_IVGEN_PLAIN_H__ + +extern struct QCryptoIVGenDriver qcrypto_ivgen_plain; + +#endif /* QCRYPTO_IVGEN_PLAIN_H__ */ diff --git a/qemu/crypto/ivgen-plain64.c b/qemu/crypto/ivgen-plain64.c new file mode 100644 index 000000000..6c6b1b44c --- /dev/null +++ b/qemu/crypto/ivgen-plain64.c @@ -0,0 +1,61 @@ +/* + * QEMU Crypto block IV generator - plain + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/bswap.h" +#include "crypto/ivgen-plain.h" + +static int qcrypto_ivgen_plain_init(QCryptoIVGen *ivgen, + const uint8_t *key, size_t nkey, + Error **errp) +{ + return 0; +} + +static int qcrypto_ivgen_plain_calculate(QCryptoIVGen *ivgen, + uint64_t sector, + uint8_t *iv, size_t niv, + Error **errp) +{ + size_t ivprefix; + ivprefix = sizeof(sector); + sector = cpu_to_le64(sector); + if (ivprefix > niv) { + ivprefix = niv; + } + memcpy(iv, §or, ivprefix); + if (ivprefix < niv) { + memset(iv + ivprefix, 0, niv - ivprefix); + } + return 0; +} + +static void qcrypto_ivgen_plain_cleanup(QCryptoIVGen *ivgen) +{ +} + + +struct QCryptoIVGenDriver qcrypto_ivgen_plain64 = { + .init = qcrypto_ivgen_plain_init, + .calculate = qcrypto_ivgen_plain_calculate, + .cleanup = qcrypto_ivgen_plain_cleanup, +}; + diff --git a/qemu/crypto/ivgen-plain64.h b/qemu/crypto/ivgen-plain64.h new file mode 100644 index 000000000..c4104459b --- /dev/null +++ b/qemu/crypto/ivgen-plain64.h @@ -0,0 +1,28 @@ +/* + * QEMU Crypto block IV generator - plain64 + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "crypto/ivgenpriv.h" + +#ifndef QCRYPTO_IVGEN_PLAIN64_H__ +#define QCRYPTO_IVGEN_PLAIN64_H__ + +extern struct QCryptoIVGenDriver qcrypto_ivgen_plain64; + +#endif /* QCRYPTO_IVGEN_PLAIN64_H__ */ diff --git a/qemu/crypto/ivgen.c b/qemu/crypto/ivgen.c new file mode 100644 index 000000000..f66435112 --- /dev/null +++ b/qemu/crypto/ivgen.c @@ -0,0 +1,101 @@ +/* + * QEMU Crypto block IV generator + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" + +#include "crypto/ivgenpriv.h" +#include "crypto/ivgen-plain.h" +#include "crypto/ivgen-plain64.h" +#include "crypto/ivgen-essiv.h" + + +QCryptoIVGen *qcrypto_ivgen_new(QCryptoIVGenAlgorithm alg, + QCryptoCipherAlgorithm cipheralg, + QCryptoHashAlgorithm hash, + const uint8_t *key, size_t nkey, + Error **errp) +{ + QCryptoIVGen *ivgen = g_new0(QCryptoIVGen, 1); + + ivgen->algorithm = alg; + ivgen->cipher = cipheralg; + ivgen->hash = hash; + + switch (alg) { + case QCRYPTO_IVGEN_ALG_PLAIN: + ivgen->driver = &qcrypto_ivgen_plain; + break; + case QCRYPTO_IVGEN_ALG_PLAIN64: + ivgen->driver = &qcrypto_ivgen_plain64; + break; + case QCRYPTO_IVGEN_ALG_ESSIV: + ivgen->driver = &qcrypto_ivgen_essiv; + break; + default: + error_setg(errp, "Unknown block IV generator algorithm %d", alg); + g_free(ivgen); + return NULL; + } + + if (ivgen->driver->init(ivgen, key, nkey, errp) < 0) { + g_free(ivgen); + return NULL; + } + + return ivgen; +} + + +int qcrypto_ivgen_calculate(QCryptoIVGen *ivgen, + uint64_t sector, + uint8_t *iv, size_t niv, + Error **errp) +{ + return ivgen->driver->calculate(ivgen, sector, iv, niv, errp); +} + + +QCryptoIVGenAlgorithm qcrypto_ivgen_get_algorithm(QCryptoIVGen *ivgen) +{ + return ivgen->algorithm; +} + + +QCryptoCipherAlgorithm qcrypto_ivgen_get_cipher(QCryptoIVGen *ivgen) +{ + return ivgen->cipher; +} + + +QCryptoHashAlgorithm qcrypto_ivgen_get_hash(QCryptoIVGen *ivgen) +{ + return ivgen->hash; +} + + +void qcrypto_ivgen_free(QCryptoIVGen *ivgen) +{ + if (!ivgen) { + return; + } + ivgen->driver->cleanup(ivgen); + g_free(ivgen); +} diff --git a/qemu/crypto/ivgenpriv.h b/qemu/crypto/ivgenpriv.h new file mode 100644 index 000000000..7b87e02ea --- /dev/null +++ b/qemu/crypto/ivgenpriv.h @@ -0,0 +1,49 @@ +/* + * QEMU Crypto block IV generator + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QCRYPTO_IVGEN_PRIV_H__ +#define QCRYPTO_IVGEN_PRIV_H__ + +#include "crypto/ivgen.h" + +typedef struct QCryptoIVGenDriver QCryptoIVGenDriver; + +struct QCryptoIVGenDriver { + int (*init)(QCryptoIVGen *ivgen, + const uint8_t *key, size_t nkey, + Error **errp); + int (*calculate)(QCryptoIVGen *ivgen, + uint64_t sector, + uint8_t *iv, size_t niv, + Error **errp); + void (*cleanup)(QCryptoIVGen *ivgen); +}; + +struct QCryptoIVGen { + QCryptoIVGenDriver *driver; + void *private; + + QCryptoIVGenAlgorithm algorithm; + QCryptoCipherAlgorithm cipher; + QCryptoHashAlgorithm hash; +}; + + +#endif /* QCRYPTO_IVGEN_PRIV_H__ */ diff --git a/qemu/crypto/pbkdf-gcrypt.c b/qemu/crypto/pbkdf-gcrypt.c new file mode 100644 index 000000000..997b311d8 --- /dev/null +++ b/qemu/crypto/pbkdf-gcrypt.c @@ -0,0 +1,69 @@ +/* + * QEMU Crypto PBKDF support (Password-Based Key Derivation Function) + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "crypto/pbkdf.h" +#include "gcrypt.h" + +bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash) +{ + switch (hash) { + case QCRYPTO_HASH_ALG_MD5: + case QCRYPTO_HASH_ALG_SHA1: + case QCRYPTO_HASH_ALG_SHA256: + return true; + default: + return false; + } +} + +int qcrypto_pbkdf2(QCryptoHashAlgorithm hash, + const uint8_t *key, size_t nkey, + const uint8_t *salt, size_t nsalt, + unsigned int iterations, + uint8_t *out, size_t nout, + Error **errp) +{ + static const int hash_map[QCRYPTO_HASH_ALG__MAX] = { + [QCRYPTO_HASH_ALG_MD5] = GCRY_MD_MD5, + [QCRYPTO_HASH_ALG_SHA1] = GCRY_MD_SHA1, + [QCRYPTO_HASH_ALG_SHA256] = GCRY_MD_SHA256, + }; + int ret; + + if (hash >= G_N_ELEMENTS(hash_map) || + hash_map[hash] == GCRY_MD_NONE) { + error_setg(errp, "Unexpected hash algorithm %d", hash); + return -1; + } + + ret = gcry_kdf_derive(key, nkey, GCRY_KDF_PBKDF2, + hash_map[hash], + salt, nsalt, iterations, + nout, out); + if (ret != 0) { + error_setg(errp, "Cannot derive password: %s", + gcry_strerror(ret)); + return -1; + } + + return 0; +} diff --git a/qemu/crypto/pbkdf-nettle.c b/qemu/crypto/pbkdf-nettle.c new file mode 100644 index 000000000..db9fc1578 --- /dev/null +++ b/qemu/crypto/pbkdf-nettle.c @@ -0,0 +1,66 @@ +/* + * QEMU Crypto PBKDF support (Password-Based Key Derivation Function) + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "crypto/pbkdf.h" +#include "nettle/pbkdf2.h" + + +bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash) +{ + switch (hash) { + case QCRYPTO_HASH_ALG_SHA1: + case QCRYPTO_HASH_ALG_SHA256: + return true; + default: + return false; + } +} + +int qcrypto_pbkdf2(QCryptoHashAlgorithm hash, + const uint8_t *key, size_t nkey, + const uint8_t *salt, size_t nsalt, + unsigned int iterations, + uint8_t *out, size_t nout, + Error **errp) +{ + switch (hash) { + case QCRYPTO_HASH_ALG_SHA1: + pbkdf2_hmac_sha1(nkey, key, + iterations, + nsalt, salt, + nout, out); + break; + + case QCRYPTO_HASH_ALG_SHA256: + pbkdf2_hmac_sha256(nkey, key, + iterations, + nsalt, salt, + nout, out); + break; + + default: + error_setg_errno(errp, ENOSYS, + "PBKDF does not support hash algorithm %d", hash); + return -1; + } + return 0; +} diff --git a/qemu/crypto/pbkdf-stub.c b/qemu/crypto/pbkdf-stub.c new file mode 100644 index 000000000..266a5051b --- /dev/null +++ b/qemu/crypto/pbkdf-stub.c @@ -0,0 +1,43 @@ +/* + * QEMU Crypto PBKDF support (Password-Based Key Derivation Function) + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "crypto/pbkdf.h" + +bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash G_GNUC_UNUSED) +{ + return false; +} + +int qcrypto_pbkdf2(QCryptoHashAlgorithm hash G_GNUC_UNUSED, + const uint8_t *key G_GNUC_UNUSED, + size_t nkey G_GNUC_UNUSED, + const uint8_t *salt G_GNUC_UNUSED, + size_t nsalt G_GNUC_UNUSED, + unsigned int iterations G_GNUC_UNUSED, + uint8_t *out G_GNUC_UNUSED, + size_t nout G_GNUC_UNUSED, + Error **errp) +{ + error_setg_errno(errp, ENOSYS, + "No crypto library supporting PBKDF in this build"); + return -1; +} diff --git a/qemu/crypto/pbkdf.c b/qemu/crypto/pbkdf.c new file mode 100644 index 000000000..695cc35df --- /dev/null +++ b/qemu/crypto/pbkdf.c @@ -0,0 +1,110 @@ +/* + * QEMU Crypto PBKDF support (Password-Based Key Derivation Function) + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "crypto/pbkdf.h" +#ifndef _WIN32 +#include +#endif + + +static int qcrypto_pbkdf2_get_thread_cpu(unsigned long long *val_ms, + Error **errp) +{ +#ifdef _WIN32 + FILETIME creation_time, exit_time, kernel_time, user_time; + ULARGE_INTEGER thread_time; + + if (!GetThreadTimes(GetCurrentThread(), &creation_time, &exit_time, + &kernel_time, &user_time)) { + error_setg(errp, "Unable to get thread CPU usage"); + return -1; + } + + thread_time.LowPart = user_time.dwLowDateTime; + thread_time.HighPart = user_time.dwHighDateTime; + + /* QuadPart is units of 100ns and we want ms as unit */ + *val_ms = thread_time.QuadPart / 10000ll; + return 0; +#elif defined(RUSAGE_THREAD) + struct rusage ru; + if (getrusage(RUSAGE_THREAD, &ru) < 0) { + error_setg_errno(errp, errno, "Unable to get thread CPU usage"); + return -1; + } + + *val_ms = ((ru.ru_utime.tv_sec * 1000ll) + + (ru.ru_utime.tv_usec / 1000)); + return 0; +#else + *val_ms = 0; + error_setg(errp, "Unable to calculate thread CPU usage on this platform"); + return -1; +#endif +} + +int qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash, + const uint8_t *key, size_t nkey, + const uint8_t *salt, size_t nsalt, + Error **errp) +{ + uint8_t out[32]; + long long int iterations = (1 << 15); + unsigned long long delta_ms, start_ms, end_ms; + + while (1) { + if (qcrypto_pbkdf2_get_thread_cpu(&start_ms, errp) < 0) { + return -1; + } + if (qcrypto_pbkdf2(hash, + key, nkey, + salt, nsalt, + iterations, + out, sizeof(out), + errp) < 0) { + return -1; + } + if (qcrypto_pbkdf2_get_thread_cpu(&end_ms, errp) < 0) { + return -1; + } + + delta_ms = end_ms - start_ms; + + if (delta_ms > 500) { + break; + } else if (delta_ms < 100) { + iterations = iterations * 10; + } else { + iterations = (iterations * 1000 / delta_ms); + } + } + + iterations = iterations * 1000 / delta_ms; + + if (iterations > INT32_MAX) { + error_setg(errp, "Iterations %lld too large for a 32-bit int", + iterations); + return -1; + } + + return iterations; +} diff --git a/qemu/crypto/random-gcrypt.c b/qemu/crypto/random-gcrypt.c new file mode 100644 index 000000000..0de9a096d --- /dev/null +++ b/qemu/crypto/random-gcrypt.c @@ -0,0 +1,33 @@ +/* + * QEMU Crypto random number provider + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" + +#include "crypto/random.h" + +#include + +int qcrypto_random_bytes(uint8_t *buf, + size_t buflen, + Error **errp G_GNUC_UNUSED) +{ + gcry_randomize(buf, buflen, GCRY_STRONG_RANDOM); + return 0; +} diff --git a/qemu/crypto/random-gnutls.c b/qemu/crypto/random-gnutls.c new file mode 100644 index 000000000..04b45a8f8 --- /dev/null +++ b/qemu/crypto/random-gnutls.c @@ -0,0 +1,43 @@ +/* + * QEMU Crypto random number provider + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" + +#include "crypto/random.h" + +#include +#include + +int qcrypto_random_bytes(uint8_t *buf, + size_t buflen, + Error **errp) +{ + int ret; + + ret = gnutls_rnd(GNUTLS_RND_RANDOM, buf, buflen); + + if (ret < 0) { + error_setg(errp, "Cannot get random bytes: %s", + gnutls_strerror(ret)); + return -1; + } + + return 0; +} diff --git a/qemu/crypto/random-stub.c b/qemu/crypto/random-stub.c new file mode 100644 index 000000000..63bbf4147 --- /dev/null +++ b/qemu/crypto/random-stub.c @@ -0,0 +1,31 @@ +/* + * QEMU Crypto random number provider + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" + +#include "crypto/random.h" + +int qcrypto_random_bytes(uint8_t *buf G_GNUC_UNUSED, + size_t buflen G_GNUC_UNUSED, + Error **errp) +{ + error_setg(errp, "No random byte source provided in this build"); + return -1; +} diff --git a/qemu/crypto/secret.c b/qemu/crypto/secret.c new file mode 100644 index 000000000..285ab7a63 --- /dev/null +++ b/qemu/crypto/secret.c @@ -0,0 +1,509 @@ +/* + * QEMU crypto secret support + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "crypto/secret.h" +#include "crypto/cipher.h" +#include "qapi/error.h" +#include "qom/object_interfaces.h" +#include "qemu/base64.h" +#include "trace.h" + + +static void +qcrypto_secret_load_data(QCryptoSecret *secret, + uint8_t **output, + size_t *outputlen, + Error **errp) +{ + char *data = NULL; + size_t length = 0; + GError *gerr = NULL; + + *output = NULL; + *outputlen = 0; + + if (secret->file) { + if (secret->data) { + error_setg(errp, + "'file' and 'data' are mutually exclusive"); + return; + } + if (!g_file_get_contents(secret->file, &data, &length, &gerr)) { + error_setg(errp, + "Unable to read %s: %s", + secret->file, gerr->message); + g_error_free(gerr); + return; + } + *output = (uint8_t *)data; + *outputlen = length; + } else if (secret->data) { + *outputlen = strlen(secret->data); + *output = (uint8_t *)g_strdup(secret->data); + } else { + error_setg(errp, "Either 'file' or 'data' must be provided"); + } +} + + +static void qcrypto_secret_decrypt(QCryptoSecret *secret, + const uint8_t *input, + size_t inputlen, + uint8_t **output, + size_t *outputlen, + Error **errp) +{ + uint8_t *key = NULL, *ciphertext = NULL, *iv = NULL; + size_t keylen, ciphertextlen, ivlen; + QCryptoCipher *aes = NULL; + uint8_t *plaintext = NULL; + + *output = NULL; + *outputlen = 0; + + if (qcrypto_secret_lookup(secret->keyid, + &key, &keylen, + errp) < 0) { + goto cleanup; + } + + if (keylen != 32) { + error_setg(errp, "Key should be 32 bytes in length"); + goto cleanup; + } + + if (!secret->iv) { + error_setg(errp, "IV is required to decrypt secret"); + goto cleanup; + } + + iv = qbase64_decode(secret->iv, -1, &ivlen, errp); + if (!iv) { + goto cleanup; + } + if (ivlen != 16) { + error_setg(errp, "IV should be 16 bytes in length not %zu", + ivlen); + goto cleanup; + } + + aes = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_256, + QCRYPTO_CIPHER_MODE_CBC, + key, keylen, + errp); + if (!aes) { + goto cleanup; + } + + if (qcrypto_cipher_setiv(aes, iv, ivlen, errp) < 0) { + goto cleanup; + } + + if (secret->format == QCRYPTO_SECRET_FORMAT_BASE64) { + ciphertext = qbase64_decode((const gchar*)input, + inputlen, + &ciphertextlen, + errp); + if (!ciphertext) { + goto cleanup; + } + plaintext = g_new0(uint8_t, ciphertextlen + 1); + } else { + ciphertextlen = inputlen; + plaintext = g_new0(uint8_t, inputlen + 1); + } + if (qcrypto_cipher_decrypt(aes, + ciphertext ? ciphertext : input, + plaintext, + ciphertextlen, + errp) < 0) { + plaintext = NULL; + goto cleanup; + } + + if (plaintext[ciphertextlen - 1] > 16 || + plaintext[ciphertextlen - 1] > ciphertextlen) { + error_setg(errp, "Incorrect number of padding bytes (%d) " + "found on decrypted data", + (int)plaintext[ciphertextlen - 1]); + g_free(plaintext); + plaintext = NULL; + goto cleanup; + } + + /* Even though plaintext may contain arbitrary NUL + * ensure it is explicitly NUL terminated. + */ + ciphertextlen -= plaintext[ciphertextlen - 1]; + plaintext[ciphertextlen] = '\0'; + + *output = plaintext; + *outputlen = ciphertextlen; + + cleanup: + g_free(ciphertext); + g_free(iv); + g_free(key); + qcrypto_cipher_free(aes); +} + + +static void qcrypto_secret_decode(const uint8_t *input, + size_t inputlen, + uint8_t **output, + size_t *outputlen, + Error **errp) +{ + *output = qbase64_decode((const gchar*)input, + inputlen, + outputlen, + errp); +} + + +static void +qcrypto_secret_prop_set_loaded(Object *obj, + bool value, + Error **errp) +{ + QCryptoSecret *secret = QCRYPTO_SECRET(obj); + + if (value) { + Error *local_err = NULL; + uint8_t *input = NULL; + size_t inputlen = 0; + uint8_t *output = NULL; + size_t outputlen = 0; + + qcrypto_secret_load_data(secret, &input, &inputlen, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (secret->keyid) { + qcrypto_secret_decrypt(secret, input, inputlen, + &output, &outputlen, &local_err); + g_free(input); + if (local_err) { + error_propagate(errp, local_err); + return; + } + input = output; + inputlen = outputlen; + } else { + if (secret->format != QCRYPTO_SECRET_FORMAT_RAW) { + qcrypto_secret_decode(input, inputlen, + &output, &outputlen, &local_err); + g_free(input); + if (local_err) { + error_propagate(errp, local_err); + return; + } + input = output; + inputlen = outputlen; + } + } + + secret->rawdata = input; + secret->rawlen = inputlen; + } else { + g_free(secret->rawdata); + secret->rawlen = 0; + } +} + + +static bool +qcrypto_secret_prop_get_loaded(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoSecret *secret = QCRYPTO_SECRET(obj); + return secret->data != NULL; +} + + +static void +qcrypto_secret_prop_set_format(Object *obj, + int value, + Error **errp G_GNUC_UNUSED) +{ + QCryptoSecret *creds = QCRYPTO_SECRET(obj); + + creds->format = value; +} + + +static int +qcrypto_secret_prop_get_format(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoSecret *creds = QCRYPTO_SECRET(obj); + + return creds->format; +} + + +static void +qcrypto_secret_prop_set_data(Object *obj, + const char *value, + Error **errp) +{ + QCryptoSecret *secret = QCRYPTO_SECRET(obj); + + g_free(secret->data); + secret->data = g_strdup(value); +} + + +static char * +qcrypto_secret_prop_get_data(Object *obj, + Error **errp) +{ + QCryptoSecret *secret = QCRYPTO_SECRET(obj); + return g_strdup(secret->data); +} + + +static void +qcrypto_secret_prop_set_file(Object *obj, + const char *value, + Error **errp) +{ + QCryptoSecret *secret = QCRYPTO_SECRET(obj); + + g_free(secret->file); + secret->file = g_strdup(value); +} + + +static char * +qcrypto_secret_prop_get_file(Object *obj, + Error **errp) +{ + QCryptoSecret *secret = QCRYPTO_SECRET(obj); + return g_strdup(secret->file); +} + + +static void +qcrypto_secret_prop_set_iv(Object *obj, + const char *value, + Error **errp) +{ + QCryptoSecret *secret = QCRYPTO_SECRET(obj); + + g_free(secret->iv); + secret->iv = g_strdup(value); +} + + +static char * +qcrypto_secret_prop_get_iv(Object *obj, + Error **errp) +{ + QCryptoSecret *secret = QCRYPTO_SECRET(obj); + return g_strdup(secret->iv); +} + + +static void +qcrypto_secret_prop_set_keyid(Object *obj, + const char *value, + Error **errp) +{ + QCryptoSecret *secret = QCRYPTO_SECRET(obj); + + g_free(secret->keyid); + secret->keyid = g_strdup(value); +} + + +static char * +qcrypto_secret_prop_get_keyid(Object *obj, + Error **errp) +{ + QCryptoSecret *secret = QCRYPTO_SECRET(obj); + return g_strdup(secret->keyid); +} + + +static void +qcrypto_secret_complete(UserCreatable *uc, Error **errp) +{ + object_property_set_bool(OBJECT(uc), true, "loaded", errp); +} + + +static void +qcrypto_secret_finalize(Object *obj) +{ + QCryptoSecret *secret = QCRYPTO_SECRET(obj); + + g_free(secret->iv); + g_free(secret->file); + g_free(secret->keyid); + g_free(secret->rawdata); + g_free(secret->data); +} + +static void +qcrypto_secret_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = qcrypto_secret_complete; + + object_class_property_add_bool(oc, "loaded", + qcrypto_secret_prop_get_loaded, + qcrypto_secret_prop_set_loaded, + NULL); + object_class_property_add_enum(oc, "format", + "QCryptoSecretFormat", + QCryptoSecretFormat_lookup, + qcrypto_secret_prop_get_format, + qcrypto_secret_prop_set_format, + NULL); + object_class_property_add_str(oc, "data", + qcrypto_secret_prop_get_data, + qcrypto_secret_prop_set_data, + NULL); + object_class_property_add_str(oc, "file", + qcrypto_secret_prop_get_file, + qcrypto_secret_prop_set_file, + NULL); + object_class_property_add_str(oc, "keyid", + qcrypto_secret_prop_get_keyid, + qcrypto_secret_prop_set_keyid, + NULL); + object_class_property_add_str(oc, "iv", + qcrypto_secret_prop_get_iv, + qcrypto_secret_prop_set_iv, + NULL); +} + + +int qcrypto_secret_lookup(const char *secretid, + uint8_t **data, + size_t *datalen, + Error **errp) +{ + Object *obj; + QCryptoSecret *secret; + + obj = object_resolve_path_component( + object_get_objects_root(), secretid); + if (!obj) { + error_setg(errp, "No secret with id '%s'", secretid); + return -1; + } + + secret = (QCryptoSecret *) + object_dynamic_cast(obj, + TYPE_QCRYPTO_SECRET); + if (!secret) { + error_setg(errp, "Object with id '%s' is not a secret", + secretid); + return -1; + } + + if (!secret->rawdata) { + error_setg(errp, "Secret with id '%s' has no data", + secretid); + return -1; + } + + *data = g_new0(uint8_t, secret->rawlen + 1); + memcpy(*data, secret->rawdata, secret->rawlen); + (*data)[secret->rawlen] = '\0'; + *datalen = secret->rawlen; + + return 0; +} + + +char *qcrypto_secret_lookup_as_utf8(const char *secretid, + Error **errp) +{ + uint8_t *data; + size_t datalen; + + if (qcrypto_secret_lookup(secretid, + &data, + &datalen, + errp) < 0) { + return NULL; + } + + if (!g_utf8_validate((const gchar*)data, datalen, NULL)) { + error_setg(errp, + "Data from secret %s is not valid UTF-8", + secretid); + g_free(data); + return NULL; + } + + return (char *)data; +} + + +char *qcrypto_secret_lookup_as_base64(const char *secretid, + Error **errp) +{ + uint8_t *data; + size_t datalen; + char *ret; + + if (qcrypto_secret_lookup(secretid, + &data, + &datalen, + errp) < 0) { + return NULL; + } + + ret = g_base64_encode(data, datalen); + g_free(data); + return ret; +} + + +static const TypeInfo qcrypto_secret_info = { + .parent = TYPE_OBJECT, + .name = TYPE_QCRYPTO_SECRET, + .instance_size = sizeof(QCryptoSecret), + .instance_finalize = qcrypto_secret_finalize, + .class_size = sizeof(QCryptoSecretClass), + .class_init = qcrypto_secret_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + + +static void +qcrypto_secret_register_types(void) +{ + type_register_static(&qcrypto_secret_info); +} + + +type_init(qcrypto_secret_register_types); diff --git a/qemu/crypto/tlscreds.c b/qemu/crypto/tlscreds.c new file mode 100644 index 000000000..1620e126a --- /dev/null +++ b/qemu/crypto/tlscreds.c @@ -0,0 +1,259 @@ +/* + * QEMU crypto TLS credential support + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "crypto/tlscredspriv.h" +#include "trace.h" + +#define DH_BITS 2048 + +#ifdef CONFIG_GNUTLS +int +qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds, + const char *filename, + gnutls_dh_params_t *dh_params, + Error **errp) +{ + int ret; + + trace_qcrypto_tls_creds_load_dh(creds, filename ? filename : ""); + + if (filename == NULL) { + ret = gnutls_dh_params_init(dh_params); + if (ret < 0) { + error_setg(errp, "Unable to initialize DH parameters: %s", + gnutls_strerror(ret)); + return -1; + } + ret = gnutls_dh_params_generate2(*dh_params, DH_BITS); + if (ret < 0) { + gnutls_dh_params_deinit(*dh_params); + *dh_params = NULL; + error_setg(errp, "Unable to generate DH parameters: %s", + gnutls_strerror(ret)); + return -1; + } + } else { + GError *gerr = NULL; + gchar *contents; + gsize len; + gnutls_datum_t data; + if (!g_file_get_contents(filename, + &contents, + &len, + &gerr)) { + + error_setg(errp, "%s", gerr->message); + g_error_free(gerr); + return -1; + } + data.data = (unsigned char *)contents; + data.size = len; + ret = gnutls_dh_params_init(dh_params); + if (ret < 0) { + g_free(contents); + error_setg(errp, "Unable to initialize DH parameters: %s", + gnutls_strerror(ret)); + return -1; + } + ret = gnutls_dh_params_import_pkcs3(*dh_params, + &data, + GNUTLS_X509_FMT_PEM); + g_free(contents); + if (ret < 0) { + gnutls_dh_params_deinit(*dh_params); + *dh_params = NULL; + error_setg(errp, "Unable to load DH parameters from %s: %s", + filename, gnutls_strerror(ret)); + return -1; + } + } + + return 0; +} + + +int +qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds, + const char *filename, + bool required, + char **cred, + Error **errp) +{ + struct stat sb; + int ret = -1; + + if (!creds->dir) { + if (required) { + error_setg(errp, "Missing 'dir' property value"); + return -1; + } else { + return 0; + } + } + + *cred = g_strdup_printf("%s/%s", creds->dir, filename); + + if (stat(*cred, &sb) < 0) { + if (errno == ENOENT && !required) { + ret = 0; + } else { + error_setg_errno(errp, errno, + "Unable to access credentials %s", + *cred); + } + g_free(*cred); + *cred = NULL; + goto cleanup; + } + + ret = 0; + cleanup: + trace_qcrypto_tls_creds_get_path(creds, filename, + *cred ? *cred : ""); + return ret; +} + + +#endif /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_prop_set_verify(Object *obj, + bool value, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); + + creds->verifyPeer = value; +} + + +static bool +qcrypto_tls_creds_prop_get_verify(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); + + return creds->verifyPeer; +} + + +static void +qcrypto_tls_creds_prop_set_dir(Object *obj, + const char *value, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); + + creds->dir = g_strdup(value); +} + + +static char * +qcrypto_tls_creds_prop_get_dir(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); + + return g_strdup(creds->dir); +} + + +static void +qcrypto_tls_creds_prop_set_endpoint(Object *obj, + int value, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); + + creds->endpoint = value; +} + + +static int +qcrypto_tls_creds_prop_get_endpoint(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); + + return creds->endpoint; +} + + +static void +qcrypto_tls_creds_class_init(ObjectClass *oc, void *data) +{ + object_class_property_add_bool(oc, "verify-peer", + qcrypto_tls_creds_prop_get_verify, + qcrypto_tls_creds_prop_set_verify, + NULL); + object_class_property_add_str(oc, "dir", + qcrypto_tls_creds_prop_get_dir, + qcrypto_tls_creds_prop_set_dir, + NULL); + object_class_property_add_enum(oc, "endpoint", + "QCryptoTLSCredsEndpoint", + QCryptoTLSCredsEndpoint_lookup, + qcrypto_tls_creds_prop_get_endpoint, + qcrypto_tls_creds_prop_set_endpoint, + NULL); +} + + +static void +qcrypto_tls_creds_init(Object *obj) +{ + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); + + creds->verifyPeer = true; +} + + +static void +qcrypto_tls_creds_finalize(Object *obj) +{ + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); + + g_free(creds->dir); +} + + +static const TypeInfo qcrypto_tls_creds_info = { + .parent = TYPE_OBJECT, + .name = TYPE_QCRYPTO_TLS_CREDS, + .instance_size = sizeof(QCryptoTLSCreds), + .instance_init = qcrypto_tls_creds_init, + .instance_finalize = qcrypto_tls_creds_finalize, + .class_init = qcrypto_tls_creds_class_init, + .class_size = sizeof(QCryptoTLSCredsClass), + .abstract = true, +}; + + +static void +qcrypto_tls_creds_register_types(void) +{ + type_register_static(&qcrypto_tls_creds_info); +} + + +type_init(qcrypto_tls_creds_register_types); diff --git a/qemu/crypto/tlscredsanon.c b/qemu/crypto/tlscredsanon.c new file mode 100644 index 000000000..146422008 --- /dev/null +++ b/qemu/crypto/tlscredsanon.c @@ -0,0 +1,219 @@ +/* + * QEMU crypto TLS anonymous credential support + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "crypto/tlscredsanon.h" +#include "crypto/tlscredspriv.h" +#include "qapi/error.h" +#include "qom/object_interfaces.h" +#include "trace.h" + + +#ifdef CONFIG_GNUTLS + + +static int +qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds, + Error **errp) +{ + char *dhparams = NULL; + int ret; + int rv = -1; + + trace_qcrypto_tls_creds_anon_load(creds, + creds->parent_obj.dir ? creds->parent_obj.dir : ""); + + if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + if (qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_DH_PARAMS, + false, &dhparams, errp) < 0) { + goto cleanup; + } + + ret = gnutls_anon_allocate_server_credentials(&creds->data.server); + if (ret < 0) { + error_setg(errp, "Cannot allocate credentials: %s", + gnutls_strerror(ret)); + goto cleanup; + } + + if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams, + &creds->parent_obj.dh_params, + errp) < 0) { + goto cleanup; + } + + gnutls_anon_set_server_dh_params(creds->data.server, + creds->parent_obj.dh_params); + } else { + ret = gnutls_anon_allocate_client_credentials(&creds->data.client); + if (ret < 0) { + error_setg(errp, "Cannot allocate credentials: %s", + gnutls_strerror(ret)); + goto cleanup; + } + } + + rv = 0; + cleanup: + g_free(dhparams); + return rv; +} + + +static void +qcrypto_tls_creds_anon_unload(QCryptoTLSCredsAnon *creds) +{ + if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { + if (creds->data.client) { + gnutls_anon_free_client_credentials(creds->data.client); + creds->data.client = NULL; + } + } else { + if (creds->data.server) { + gnutls_anon_free_server_credentials(creds->data.server); + creds->data.server = NULL; + } + } + if (creds->parent_obj.dh_params) { + gnutls_dh_params_deinit(creds->parent_obj.dh_params); + creds->parent_obj.dh_params = NULL; + } +} + +#else /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds G_GNUC_UNUSED, + Error **errp) +{ + error_setg(errp, "TLS credentials support requires GNUTLS"); +} + + +static void +qcrypto_tls_creds_anon_unload(QCryptoTLSCredsAnon *creds G_GNUC_UNUSED) +{ + /* nada */ +} + + +#endif /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_anon_prop_set_loaded(Object *obj, + bool value, + Error **errp) +{ + QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(obj); + + if (value) { + qcrypto_tls_creds_anon_load(creds, errp); + } else { + qcrypto_tls_creds_anon_unload(creds); + } +} + + +#ifdef CONFIG_GNUTLS + + +static bool +qcrypto_tls_creds_anon_prop_get_loaded(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(obj); + + if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + return creds->data.server != NULL; + } else { + return creds->data.client != NULL; + } +} + + +#else /* ! CONFIG_GNUTLS */ + + +static bool +qcrypto_tls_creds_anon_prop_get_loaded(Object *obj G_GNUC_UNUSED, + Error **errp G_GNUC_UNUSED) +{ + return false; +} + + +#endif /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_anon_complete(UserCreatable *uc, Error **errp) +{ + object_property_set_bool(OBJECT(uc), true, "loaded", errp); +} + + +static void +qcrypto_tls_creds_anon_finalize(Object *obj) +{ + QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(obj); + + qcrypto_tls_creds_anon_unload(creds); +} + + +static void +qcrypto_tls_creds_anon_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = qcrypto_tls_creds_anon_complete; + + object_class_property_add_bool(oc, "loaded", + qcrypto_tls_creds_anon_prop_get_loaded, + qcrypto_tls_creds_anon_prop_set_loaded, + NULL); +} + + +static const TypeInfo qcrypto_tls_creds_anon_info = { + .parent = TYPE_QCRYPTO_TLS_CREDS, + .name = TYPE_QCRYPTO_TLS_CREDS_ANON, + .instance_size = sizeof(QCryptoTLSCredsAnon), + .instance_finalize = qcrypto_tls_creds_anon_finalize, + .class_size = sizeof(QCryptoTLSCredsAnonClass), + .class_init = qcrypto_tls_creds_anon_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + + +static void +qcrypto_tls_creds_anon_register_types(void) +{ + type_register_static(&qcrypto_tls_creds_anon_info); +} + + +type_init(qcrypto_tls_creds_anon_register_types); diff --git a/qemu/crypto/tlscredspriv.h b/qemu/crypto/tlscredspriv.h new file mode 100644 index 000000000..9222be4a9 --- /dev/null +++ b/qemu/crypto/tlscredspriv.h @@ -0,0 +1,42 @@ +/* + * QEMU crypto TLS credential support private helpers + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QCRYPTO_TLSCRED_PRIV_H__ +#define QCRYPTO_TLSCRED_PRIV_H__ + +#include "crypto/tlscreds.h" + +#ifdef CONFIG_GNUTLS + +int qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds, + const char *filename, + bool required, + char **cred, + Error **errp); + +int qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds, + const char *filename, + gnutls_dh_params_t *dh_params, + Error **errp); + +#endif + +#endif /* QCRYPTO_TLSCRED_PRIV_H__ */ + diff --git a/qemu/crypto/tlscredsx509.c b/qemu/crypto/tlscredsx509.c new file mode 100644 index 000000000..6a0179c2e --- /dev/null +++ b/qemu/crypto/tlscredsx509.c @@ -0,0 +1,865 @@ +/* + * QEMU crypto TLS x509 credential support + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "crypto/tlscredsx509.h" +#include "crypto/tlscredspriv.h" +#include "crypto/secret.h" +#include "qapi/error.h" +#include "qom/object_interfaces.h" +#include "trace.h" + + +#ifdef CONFIG_GNUTLS + +#include + + +static int +qcrypto_tls_creds_check_cert_times(gnutls_x509_crt_t cert, + const char *certFile, + bool isServer, + bool isCA, + Error **errp) +{ + time_t now = time(NULL); + + if (now == ((time_t)-1)) { + error_setg_errno(errp, errno, "cannot get current time"); + return -1; + } + + if (gnutls_x509_crt_get_expiration_time(cert) < now) { + error_setg(errp, + (isCA ? + "The CA certificate %s has expired" : + (isServer ? + "The server certificate %s has expired" : + "The client certificate %s has expired")), + certFile); + return -1; + } + + if (gnutls_x509_crt_get_activation_time(cert) > now) { + error_setg(errp, + (isCA ? + "The CA certificate %s is not yet active" : + (isServer ? + "The server certificate %s is not yet active" : + "The client certificate %s is not yet active")), + certFile); + return -1; + } + + return 0; +} + + +#if LIBGNUTLS_VERSION_NUMBER >= 2 +/* + * The gnutls_x509_crt_get_basic_constraints function isn't + * available in GNUTLS 1.0.x branches. This isn't critical + * though, since gnutls_certificate_verify_peers2 will do + * pretty much the same check at runtime, so we can just + * disable this code + */ +static int +qcrypto_tls_creds_check_cert_basic_constraints(QCryptoTLSCredsX509 *creds, + gnutls_x509_crt_t cert, + const char *certFile, + bool isServer, + bool isCA, + Error **errp) +{ + int status; + + status = gnutls_x509_crt_get_basic_constraints(cert, NULL, NULL, NULL); + trace_qcrypto_tls_creds_x509_check_basic_constraints( + creds, certFile, status); + + if (status > 0) { /* It is a CA cert */ + if (!isCA) { + error_setg(errp, isServer ? + "The certificate %s basic constraints show a CA, " + "but we need one for a server" : + "The certificate %s basic constraints show a CA, " + "but we need one for a client", + certFile); + return -1; + } + } else if (status == 0) { /* It is not a CA cert */ + if (isCA) { + error_setg(errp, + "The certificate %s basic constraints do not " + "show a CA", + certFile); + return -1; + } + } else if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + /* Missing basicConstraints */ + if (isCA) { + error_setg(errp, + "The certificate %s is missing basic constraints " + "for a CA", + certFile); + return -1; + } + } else { /* General error */ + error_setg(errp, + "Unable to query certificate %s basic constraints: %s", + certFile, gnutls_strerror(status)); + return -1; + } + + return 0; +} +#endif + + +static int +qcrypto_tls_creds_check_cert_key_usage(QCryptoTLSCredsX509 *creds, + gnutls_x509_crt_t cert, + const char *certFile, + bool isCA, + Error **errp) +{ + int status; + unsigned int usage = 0; + unsigned int critical = 0; + + status = gnutls_x509_crt_get_key_usage(cert, &usage, &critical); + trace_qcrypto_tls_creds_x509_check_key_usage( + creds, certFile, status, usage, critical); + + if (status < 0) { + if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + usage = isCA ? GNUTLS_KEY_KEY_CERT_SIGN : + GNUTLS_KEY_DIGITAL_SIGNATURE|GNUTLS_KEY_KEY_ENCIPHERMENT; + } else { + error_setg(errp, + "Unable to query certificate %s key usage: %s", + certFile, gnutls_strerror(status)); + return -1; + } + } + + if (isCA) { + if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) { + if (critical) { + error_setg(errp, + "Certificate %s usage does not permit " + "certificate signing", certFile); + return -1; + } + } + } else { + if (!(usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) { + if (critical) { + error_setg(errp, + "Certificate %s usage does not permit digital " + "signature", certFile); + return -1; + } + } + if (!(usage & GNUTLS_KEY_KEY_ENCIPHERMENT)) { + if (critical) { + error_setg(errp, + "Certificate %s usage does not permit key " + "encipherment", certFile); + return -1; + } + } + } + + return 0; +} + + +static int +qcrypto_tls_creds_check_cert_key_purpose(QCryptoTLSCredsX509 *creds, + gnutls_x509_crt_t cert, + const char *certFile, + bool isServer, + Error **errp) +{ + int status; + size_t i; + unsigned int purposeCritical; + unsigned int critical; + char *buffer = NULL; + size_t size; + bool allowClient = false, allowServer = false; + + critical = 0; + for (i = 0; ; i++) { + size = 0; + status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, + &size, NULL); + + if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + + /* If there is no data at all, then we must allow + client/server to pass */ + if (i == 0) { + allowServer = allowClient = true; + } + break; + } + if (status != GNUTLS_E_SHORT_MEMORY_BUFFER) { + error_setg(errp, + "Unable to query certificate %s key purpose: %s", + certFile, gnutls_strerror(status)); + return -1; + } + + buffer = g_new0(char, size); + + status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, + &size, &purposeCritical); + + if (status < 0) { + trace_qcrypto_tls_creds_x509_check_key_purpose( + creds, certFile, status, "", purposeCritical); + g_free(buffer); + error_setg(errp, + "Unable to query certificate %s key purpose: %s", + certFile, gnutls_strerror(status)); + return -1; + } + trace_qcrypto_tls_creds_x509_check_key_purpose( + creds, certFile, status, buffer, purposeCritical); + if (purposeCritical) { + critical = true; + } + + if (g_str_equal(buffer, GNUTLS_KP_TLS_WWW_SERVER)) { + allowServer = true; + } else if (g_str_equal(buffer, GNUTLS_KP_TLS_WWW_CLIENT)) { + allowClient = true; + } else if (g_str_equal(buffer, GNUTLS_KP_ANY)) { + allowServer = allowClient = true; + } + + g_free(buffer); + buffer = NULL; + } + + if (isServer) { + if (!allowServer) { + if (critical) { + error_setg(errp, + "Certificate %s purpose does not allow " + "use with a TLS server", certFile); + return -1; + } + } + } else { + if (!allowClient) { + if (critical) { + error_setg(errp, + "Certificate %s purpose does not allow use " + "with a TLS client", certFile); + return -1; + } + } + } + + return 0; +} + + +static int +qcrypto_tls_creds_check_cert(QCryptoTLSCredsX509 *creds, + gnutls_x509_crt_t cert, + const char *certFile, + bool isServer, + bool isCA, + Error **errp) +{ + if (qcrypto_tls_creds_check_cert_times(cert, certFile, + isServer, isCA, + errp) < 0) { + return -1; + } + +#if LIBGNUTLS_VERSION_NUMBER >= 2 + if (qcrypto_tls_creds_check_cert_basic_constraints(creds, + cert, certFile, + isServer, isCA, + errp) < 0) { + return -1; + } +#endif + + if (qcrypto_tls_creds_check_cert_key_usage(creds, + cert, certFile, + isCA, errp) < 0) { + return -1; + } + + if (!isCA && + qcrypto_tls_creds_check_cert_key_purpose(creds, + cert, certFile, + isServer, errp) < 0) { + return -1; + } + + return 0; +} + + +static int +qcrypto_tls_creds_check_cert_pair(gnutls_x509_crt_t cert, + const char *certFile, + gnutls_x509_crt_t *cacerts, + size_t ncacerts, + const char *cacertFile, + bool isServer, + Error **errp) +{ + unsigned int status; + + if (gnutls_x509_crt_list_verify(&cert, 1, + cacerts, ncacerts, + NULL, 0, + 0, &status) < 0) { + error_setg(errp, isServer ? + "Unable to verify server certificate %s against " + "CA certificate %s" : + "Unable to verify client certificate %s against " + "CA certificate %s", + certFile, cacertFile); + return -1; + } + + if (status != 0) { + const char *reason = "Invalid certificate"; + + if (status & GNUTLS_CERT_INVALID) { + reason = "The certificate is not trusted"; + } + + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { + reason = "The certificate hasn't got a known issuer"; + } + + if (status & GNUTLS_CERT_REVOKED) { + reason = "The certificate has been revoked"; + } + +#ifndef GNUTLS_1_0_COMPAT + if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { + reason = "The certificate uses an insecure algorithm"; + } +#endif + + error_setg(errp, + "Our own certificate %s failed validation against %s: %s", + certFile, cacertFile, reason); + return -1; + } + + return 0; +} + + +static gnutls_x509_crt_t +qcrypto_tls_creds_load_cert(QCryptoTLSCredsX509 *creds, + const char *certFile, + bool isServer, + Error **errp) +{ + gnutls_datum_t data; + gnutls_x509_crt_t cert = NULL; + char *buf = NULL; + gsize buflen; + GError *gerr; + int ret = -1; + + trace_qcrypto_tls_creds_x509_load_cert(creds, isServer, certFile); + + if (gnutls_x509_crt_init(&cert) < 0) { + error_setg(errp, "Unable to initialize certificate"); + goto cleanup; + } + + if (!g_file_get_contents(certFile, &buf, &buflen, &gerr)) { + error_setg(errp, "Cannot load CA cert list %s: %s", + certFile, gerr->message); + g_error_free(gerr); + goto cleanup; + } + + data.data = (unsigned char *)buf; + data.size = strlen(buf); + + if (gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_PEM) < 0) { + error_setg(errp, isServer ? + "Unable to import server certificate %s" : + "Unable to import client certificate %s", + certFile); + goto cleanup; + } + + ret = 0; + + cleanup: + if (ret != 0) { + gnutls_x509_crt_deinit(cert); + cert = NULL; + } + g_free(buf); + return cert; +} + + +static int +qcrypto_tls_creds_load_ca_cert_list(QCryptoTLSCredsX509 *creds, + const char *certFile, + gnutls_x509_crt_t *certs, + unsigned int certMax, + size_t *ncerts, + Error **errp) +{ + gnutls_datum_t data; + char *buf = NULL; + gsize buflen; + int ret = -1; + GError *gerr = NULL; + + *ncerts = 0; + trace_qcrypto_tls_creds_x509_load_cert_list(creds, certFile); + + if (!g_file_get_contents(certFile, &buf, &buflen, &gerr)) { + error_setg(errp, "Cannot load CA cert list %s: %s", + certFile, gerr->message); + g_error_free(gerr); + goto cleanup; + } + + data.data = (unsigned char *)buf; + data.size = strlen(buf); + + if (gnutls_x509_crt_list_import(certs, &certMax, &data, + GNUTLS_X509_FMT_PEM, 0) < 0) { + error_setg(errp, + "Unable to import CA certificate list %s", + certFile); + goto cleanup; + } + *ncerts = certMax; + + ret = 0; + + cleanup: + g_free(buf); + return ret; +} + + +#define MAX_CERTS 16 +static int +qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds, + bool isServer, + const char *cacertFile, + const char *certFile, + Error **errp) +{ + gnutls_x509_crt_t cert = NULL; + gnutls_x509_crt_t cacerts[MAX_CERTS]; + size_t ncacerts = 0; + size_t i; + int ret = -1; + + memset(cacerts, 0, sizeof(cacerts)); + if (certFile && + access(certFile, R_OK) == 0) { + cert = qcrypto_tls_creds_load_cert(creds, + certFile, isServer, + errp); + if (!cert) { + goto cleanup; + } + } + if (access(cacertFile, R_OK) == 0) { + if (qcrypto_tls_creds_load_ca_cert_list(creds, + cacertFile, cacerts, + MAX_CERTS, &ncacerts, + errp) < 0) { + goto cleanup; + } + } + + if (cert && + qcrypto_tls_creds_check_cert(creds, + cert, certFile, isServer, + false, errp) < 0) { + goto cleanup; + } + + for (i = 0; i < ncacerts; i++) { + if (qcrypto_tls_creds_check_cert(creds, + cacerts[i], cacertFile, + isServer, true, errp) < 0) { + goto cleanup; + } + } + + if (cert && ncacerts && + qcrypto_tls_creds_check_cert_pair(cert, certFile, cacerts, + ncacerts, cacertFile, + isServer, errp) < 0) { + goto cleanup; + } + + ret = 0; + + cleanup: + if (cert) { + gnutls_x509_crt_deinit(cert); + } + for (i = 0; i < ncacerts; i++) { + gnutls_x509_crt_deinit(cacerts[i]); + } + return ret; +} + + +static int +qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds, + Error **errp) +{ + char *cacert = NULL, *cacrl = NULL, *cert = NULL, + *key = NULL, *dhparams = NULL; + int ret; + int rv = -1; + + trace_qcrypto_tls_creds_x509_load(creds, + creds->parent_obj.dir ? creds->parent_obj.dir : ""); + + if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + if (qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_X509_CA_CERT, + true, &cacert, errp) < 0 || + qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_X509_CA_CRL, + false, &cacrl, errp) < 0 || + qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_X509_SERVER_CERT, + true, &cert, errp) < 0 || + qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_X509_SERVER_KEY, + true, &key, errp) < 0 || + qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_DH_PARAMS, + false, &dhparams, errp) < 0) { + goto cleanup; + } + } else { + if (qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_X509_CA_CERT, + true, &cacert, errp) < 0 || + qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_X509_CLIENT_CERT, + false, &cert, errp) < 0 || + qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_X509_CLIENT_KEY, + false, &key, errp) < 0) { + goto cleanup; + } + } + + if (creds->sanityCheck && + qcrypto_tls_creds_x509_sanity_check(creds, + creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, + cacert, cert, errp) < 0) { + goto cleanup; + } + + ret = gnutls_certificate_allocate_credentials(&creds->data); + if (ret < 0) { + error_setg(errp, "Cannot allocate credentials: '%s'", + gnutls_strerror(ret)); + goto cleanup; + } + + ret = gnutls_certificate_set_x509_trust_file(creds->data, + cacert, + GNUTLS_X509_FMT_PEM); + if (ret < 0) { + error_setg(errp, "Cannot load CA certificate '%s': %s", + cacert, gnutls_strerror(ret)); + goto cleanup; + } + + if (cert != NULL && key != NULL) { +#if GNUTLS_VERSION_NUMBER >= 0x030111 + char *password = NULL; + if (creds->passwordid) { + password = qcrypto_secret_lookup_as_utf8(creds->passwordid, + errp); + if (!password) { + goto cleanup; + } + } + ret = gnutls_certificate_set_x509_key_file2(creds->data, + cert, key, + GNUTLS_X509_FMT_PEM, + password, + 0); + g_free(password); +#else /* GNUTLS_VERSION_NUMBER < 0x030111 */ + if (creds->passwordid) { + error_setg(errp, "PKCS8 decryption requires GNUTLS >= 3.1.11"); + goto cleanup; + } + ret = gnutls_certificate_set_x509_key_file(creds->data, + cert, key, + GNUTLS_X509_FMT_PEM); +#endif /* GNUTLS_VERSION_NUMBER < 0x030111 */ + if (ret < 0) { + error_setg(errp, "Cannot load certificate '%s' & key '%s': %s", + cert, key, gnutls_strerror(ret)); + goto cleanup; + } + } + + if (cacrl != NULL) { + ret = gnutls_certificate_set_x509_crl_file(creds->data, + cacrl, + GNUTLS_X509_FMT_PEM); + if (ret < 0) { + error_setg(errp, "Cannot load CRL '%s': %s", + cacrl, gnutls_strerror(ret)); + goto cleanup; + } + } + + if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams, + &creds->parent_obj.dh_params, + errp) < 0) { + goto cleanup; + } + gnutls_certificate_set_dh_params(creds->data, + creds->parent_obj.dh_params); + } + + rv = 0; + cleanup: + g_free(cacert); + g_free(cacrl); + g_free(cert); + g_free(key); + g_free(dhparams); + return rv; +} + + +static void +qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds) +{ + if (creds->data) { + gnutls_certificate_free_credentials(creds->data); + creds->data = NULL; + } + if (creds->parent_obj.dh_params) { + gnutls_dh_params_deinit(creds->parent_obj.dh_params); + creds->parent_obj.dh_params = NULL; + } +} + + +#else /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED, + Error **errp) +{ + error_setg(errp, "TLS credentials support requires GNUTLS"); +} + + +static void +qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED) +{ + /* nada */ +} + + +#endif /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_x509_prop_set_loaded(Object *obj, + bool value, + Error **errp) +{ + QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); + + if (value) { + qcrypto_tls_creds_x509_load(creds, errp); + } else { + qcrypto_tls_creds_x509_unload(creds); + } +} + + +#ifdef CONFIG_GNUTLS + + +static bool +qcrypto_tls_creds_x509_prop_get_loaded(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); + + return creds->data != NULL; +} + + +#else /* ! CONFIG_GNUTLS */ + + +static bool +qcrypto_tls_creds_x509_prop_get_loaded(Object *obj G_GNUC_UNUSED, + Error **errp G_GNUC_UNUSED) +{ + return false; +} + + +#endif /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_x509_prop_set_sanity(Object *obj, + bool value, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); + + creds->sanityCheck = value; +} + + +static void +qcrypto_tls_creds_x509_prop_set_passwordid(Object *obj, + const char *value, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); + + creds->passwordid = g_strdup(value); +} + + +static char * +qcrypto_tls_creds_x509_prop_get_passwordid(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); + + return g_strdup(creds->passwordid); +} + + +static bool +qcrypto_tls_creds_x509_prop_get_sanity(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); + + return creds->sanityCheck; +} + + +static void +qcrypto_tls_creds_x509_complete(UserCreatable *uc, Error **errp) +{ + object_property_set_bool(OBJECT(uc), true, "loaded", errp); +} + + +static void +qcrypto_tls_creds_x509_init(Object *obj) +{ + QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); + + creds->sanityCheck = true; +} + + +static void +qcrypto_tls_creds_x509_finalize(Object *obj) +{ + QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); + + g_free(creds->passwordid); + qcrypto_tls_creds_x509_unload(creds); +} + + +static void +qcrypto_tls_creds_x509_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = qcrypto_tls_creds_x509_complete; + + object_class_property_add_bool(oc, "loaded", + qcrypto_tls_creds_x509_prop_get_loaded, + qcrypto_tls_creds_x509_prop_set_loaded, + NULL); + object_class_property_add_bool(oc, "sanity-check", + qcrypto_tls_creds_x509_prop_get_sanity, + qcrypto_tls_creds_x509_prop_set_sanity, + NULL); + object_class_property_add_str(oc, "passwordid", + qcrypto_tls_creds_x509_prop_get_passwordid, + qcrypto_tls_creds_x509_prop_set_passwordid, + NULL); +} + + +static const TypeInfo qcrypto_tls_creds_x509_info = { + .parent = TYPE_QCRYPTO_TLS_CREDS, + .name = TYPE_QCRYPTO_TLS_CREDS_X509, + .instance_size = sizeof(QCryptoTLSCredsX509), + .instance_init = qcrypto_tls_creds_x509_init, + .instance_finalize = qcrypto_tls_creds_x509_finalize, + .class_size = sizeof(QCryptoTLSCredsX509Class), + .class_init = qcrypto_tls_creds_x509_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + + +static void +qcrypto_tls_creds_x509_register_types(void) +{ + type_register_static(&qcrypto_tls_creds_x509_info); +} + + +type_init(qcrypto_tls_creds_x509_register_types); diff --git a/qemu/crypto/tlssession.c b/qemu/crypto/tlssession.c new file mode 100644 index 000000000..a543e5a57 --- /dev/null +++ b/qemu/crypto/tlssession.c @@ -0,0 +1,576 @@ +/* + * QEMU crypto TLS session support + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "crypto/tlssession.h" +#include "crypto/tlscredsanon.h" +#include "crypto/tlscredsx509.h" +#include "qapi/error.h" +#include "qemu/acl.h" +#include "trace.h" + +#ifdef CONFIG_GNUTLS + + +#include + + +struct QCryptoTLSSession { + QCryptoTLSCreds *creds; + gnutls_session_t handle; + char *hostname; + char *aclname; + bool handshakeComplete; + QCryptoTLSSessionWriteFunc writeFunc; + QCryptoTLSSessionReadFunc readFunc; + void *opaque; + char *peername; +}; + + +void +qcrypto_tls_session_free(QCryptoTLSSession *session) +{ + if (!session) { + return; + } + + gnutls_deinit(session->handle); + g_free(session->hostname); + g_free(session->peername); + g_free(session->aclname); + object_unref(OBJECT(session->creds)); + g_free(session); +} + + +static ssize_t +qcrypto_tls_session_push(void *opaque, const void *buf, size_t len) +{ + QCryptoTLSSession *session = opaque; + + if (!session->writeFunc) { + errno = EIO; + return -1; + }; + + return session->writeFunc(buf, len, session->opaque); +} + + +static ssize_t +qcrypto_tls_session_pull(void *opaque, void *buf, size_t len) +{ + QCryptoTLSSession *session = opaque; + + if (!session->readFunc) { + errno = EIO; + return -1; + }; + + return session->readFunc(buf, len, session->opaque); +} + + +QCryptoTLSSession * +qcrypto_tls_session_new(QCryptoTLSCreds *creds, + const char *hostname, + const char *aclname, + QCryptoTLSCredsEndpoint endpoint, + Error **errp) +{ + QCryptoTLSSession *session; + int ret; + + session = g_new0(QCryptoTLSSession, 1); + trace_qcrypto_tls_session_new( + session, creds, hostname ? hostname : "", + aclname ? aclname : "", endpoint); + + if (hostname) { + session->hostname = g_strdup(hostname); + } + if (aclname) { + session->aclname = g_strdup(aclname); + } + session->creds = creds; + object_ref(OBJECT(creds)); + + if (creds->endpoint != endpoint) { + error_setg(errp, "Credentials endpoint doesn't match session"); + goto error; + } + + if (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + ret = gnutls_init(&session->handle, GNUTLS_SERVER); + } else { + ret = gnutls_init(&session->handle, GNUTLS_CLIENT); + } + if (ret < 0) { + error_setg(errp, "Cannot initialize TLS session: %s", + gnutls_strerror(ret)); + goto error; + } + + if (object_dynamic_cast(OBJECT(creds), + TYPE_QCRYPTO_TLS_CREDS_ANON)) { + QCryptoTLSCredsAnon *acreds = QCRYPTO_TLS_CREDS_ANON(creds); + + ret = gnutls_priority_set_direct(session->handle, + "NORMAL:+ANON-DH", NULL); + if (ret < 0) { + error_setg(errp, "Unable to set TLS session priority: %s", + gnutls_strerror(ret)); + goto error; + } + if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + ret = gnutls_credentials_set(session->handle, + GNUTLS_CRD_ANON, + acreds->data.server); + } else { + ret = gnutls_credentials_set(session->handle, + GNUTLS_CRD_ANON, + acreds->data.client); + } + if (ret < 0) { + error_setg(errp, "Cannot set session credentials: %s", + gnutls_strerror(ret)); + goto error; + } + } else if (object_dynamic_cast(OBJECT(creds), + TYPE_QCRYPTO_TLS_CREDS_X509)) { + QCryptoTLSCredsX509 *tcreds = QCRYPTO_TLS_CREDS_X509(creds); + + ret = gnutls_set_default_priority(session->handle); + if (ret < 0) { + error_setg(errp, "Cannot set default TLS session priority: %s", + gnutls_strerror(ret)); + goto error; + } + ret = gnutls_credentials_set(session->handle, + GNUTLS_CRD_CERTIFICATE, + tcreds->data); + if (ret < 0) { + error_setg(errp, "Cannot set session credentials: %s", + gnutls_strerror(ret)); + goto error; + } + + if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + /* This requests, but does not enforce a client cert. + * The cert checking code later does enforcement */ + gnutls_certificate_server_set_request(session->handle, + GNUTLS_CERT_REQUEST); + } + } else { + error_setg(errp, "Unsupported TLS credentials type %s", + object_get_typename(OBJECT(creds))); + goto error; + } + + gnutls_transport_set_ptr(session->handle, session); + gnutls_transport_set_push_function(session->handle, + qcrypto_tls_session_push); + gnutls_transport_set_pull_function(session->handle, + qcrypto_tls_session_pull); + + return session; + + error: + qcrypto_tls_session_free(session); + return NULL; +} + +static int +qcrypto_tls_session_check_certificate(QCryptoTLSSession *session, + Error **errp) +{ + int ret; + unsigned int status; + const gnutls_datum_t *certs; + unsigned int nCerts, i; + time_t now; + gnutls_x509_crt_t cert = NULL; + + now = time(NULL); + if (now == ((time_t)-1)) { + error_setg_errno(errp, errno, "Cannot get current time"); + return -1; + } + + ret = gnutls_certificate_verify_peers2(session->handle, &status); + if (ret < 0) { + error_setg(errp, "Verify failed: %s", gnutls_strerror(ret)); + return -1; + } + + if (status != 0) { + const char *reason = "Invalid certificate"; + + if (status & GNUTLS_CERT_INVALID) { + reason = "The certificate is not trusted"; + } + + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { + reason = "The certificate hasn't got a known issuer"; + } + + if (status & GNUTLS_CERT_REVOKED) { + reason = "The certificate has been revoked"; + } + + if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { + reason = "The certificate uses an insecure algorithm"; + } + + error_setg(errp, "%s", reason); + return -1; + } + + certs = gnutls_certificate_get_peers(session->handle, &nCerts); + if (!certs) { + error_setg(errp, "No certificate peers"); + return -1; + } + + for (i = 0; i < nCerts; i++) { + ret = gnutls_x509_crt_init(&cert); + if (ret < 0) { + error_setg(errp, "Cannot initialize certificate: %s", + gnutls_strerror(ret)); + return -1; + } + + ret = gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER); + if (ret < 0) { + error_setg(errp, "Cannot import certificate: %s", + gnutls_strerror(ret)); + goto error; + } + + if (gnutls_x509_crt_get_expiration_time(cert) < now) { + error_setg(errp, "The certificate has expired"); + goto error; + } + + if (gnutls_x509_crt_get_activation_time(cert) > now) { + error_setg(errp, "The certificate is not yet activated"); + goto error; + } + + if (gnutls_x509_crt_get_activation_time(cert) > now) { + error_setg(errp, "The certificate is not yet activated"); + goto error; + } + + if (i == 0) { + size_t dnameSize = 1024; + session->peername = g_malloc(dnameSize); + requery: + ret = gnutls_x509_crt_get_dn(cert, session->peername, &dnameSize); + if (ret < 0) { + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { + session->peername = g_realloc(session->peername, + dnameSize); + goto requery; + } + error_setg(errp, "Cannot get client distinguished name: %s", + gnutls_strerror(ret)); + goto error; + } + if (session->aclname) { + qemu_acl *acl = qemu_acl_find(session->aclname); + int allow; + if (!acl) { + error_setg(errp, "Cannot find ACL %s", + session->aclname); + goto error; + } + + allow = qemu_acl_party_is_allowed(acl, session->peername); + + if (!allow) { + error_setg(errp, "TLS x509 ACL check for %s is denied", + session->peername); + goto error; + } + } + if (session->hostname) { + if (!gnutls_x509_crt_check_hostname(cert, session->hostname)) { + error_setg(errp, + "Certificate does not match the hostname %s", + session->hostname); + goto error; + } + } + } + + gnutls_x509_crt_deinit(cert); + } + + return 0; + + error: + gnutls_x509_crt_deinit(cert); + return -1; +} + + +int +qcrypto_tls_session_check_credentials(QCryptoTLSSession *session, + Error **errp) +{ + if (object_dynamic_cast(OBJECT(session->creds), + TYPE_QCRYPTO_TLS_CREDS_ANON)) { + return 0; + } else if (object_dynamic_cast(OBJECT(session->creds), + TYPE_QCRYPTO_TLS_CREDS_X509)) { + if (session->creds->verifyPeer) { + return qcrypto_tls_session_check_certificate(session, + errp); + } else { + return 0; + } + } else { + error_setg(errp, "Unexpected credential type %s", + object_get_typename(OBJECT(session->creds))); + return -1; + } +} + + +void +qcrypto_tls_session_set_callbacks(QCryptoTLSSession *session, + QCryptoTLSSessionWriteFunc writeFunc, + QCryptoTLSSessionReadFunc readFunc, + void *opaque) +{ + session->writeFunc = writeFunc; + session->readFunc = readFunc; + session->opaque = opaque; +} + + +ssize_t +qcrypto_tls_session_write(QCryptoTLSSession *session, + const char *buf, + size_t len) +{ + ssize_t ret = gnutls_record_send(session->handle, buf, len); + + if (ret < 0) { + switch (ret) { + case GNUTLS_E_AGAIN: + errno = EAGAIN; + break; + case GNUTLS_E_INTERRUPTED: + errno = EINTR; + break; + default: + errno = EIO; + break; + } + ret = -1; + } + + return ret; +} + + +ssize_t +qcrypto_tls_session_read(QCryptoTLSSession *session, + char *buf, + size_t len) +{ + ssize_t ret = gnutls_record_recv(session->handle, buf, len); + + if (ret < 0) { + switch (ret) { + case GNUTLS_E_AGAIN: + errno = EAGAIN; + break; + case GNUTLS_E_INTERRUPTED: + errno = EINTR; + break; + default: + errno = EIO; + break; + } + ret = -1; + } + + return ret; +} + + +int +qcrypto_tls_session_handshake(QCryptoTLSSession *session, + Error **errp) +{ + int ret = gnutls_handshake(session->handle); + if (ret == 0) { + session->handshakeComplete = true; + } else { + if (ret == GNUTLS_E_INTERRUPTED || + ret == GNUTLS_E_AGAIN) { + ret = 1; + } else { + error_setg(errp, "TLS handshake failed: %s", + gnutls_strerror(ret)); + ret = -1; + } + } + + return ret; +} + + +QCryptoTLSSessionHandshakeStatus +qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *session) +{ + if (session->handshakeComplete) { + return QCRYPTO_TLS_HANDSHAKE_COMPLETE; + } else if (gnutls_record_get_direction(session->handle) == 0) { + return QCRYPTO_TLS_HANDSHAKE_RECVING; + } else { + return QCRYPTO_TLS_HANDSHAKE_SENDING; + } +} + + +int +qcrypto_tls_session_get_key_size(QCryptoTLSSession *session, + Error **errp) +{ + gnutls_cipher_algorithm_t cipher; + int ssf; + + cipher = gnutls_cipher_get(session->handle); + ssf = gnutls_cipher_get_key_size(cipher); + if (!ssf) { + error_setg(errp, "Cannot get TLS cipher key size"); + return -1; + } + return ssf; +} + + +char * +qcrypto_tls_session_get_peer_name(QCryptoTLSSession *session) +{ + if (session->peername) { + return g_strdup(session->peername); + } + return NULL; +} + + +#else /* ! CONFIG_GNUTLS */ + + +QCryptoTLSSession * +qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED, + const char *hostname G_GNUC_UNUSED, + const char *aclname G_GNUC_UNUSED, + QCryptoTLSCredsEndpoint endpoint G_GNUC_UNUSED, + Error **errp) +{ + error_setg(errp, "TLS requires GNUTLS support"); + return NULL; +} + + +void +qcrypto_tls_session_free(QCryptoTLSSession *sess G_GNUC_UNUSED) +{ +} + + +int +qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess G_GNUC_UNUSED, + Error **errp) +{ + error_setg(errp, "TLS requires GNUTLS support"); + return -1; +} + + +void +qcrypto_tls_session_set_callbacks( + QCryptoTLSSession *sess G_GNUC_UNUSED, + QCryptoTLSSessionWriteFunc writeFunc G_GNUC_UNUSED, + QCryptoTLSSessionReadFunc readFunc G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ +} + + +ssize_t +qcrypto_tls_session_write(QCryptoTLSSession *sess, + const char *buf, + size_t len) +{ + errno = -EIO; + return -1; +} + + +ssize_t +qcrypto_tls_session_read(QCryptoTLSSession *sess, + char *buf, + size_t len) +{ + errno = -EIO; + return -1; +} + + +int +qcrypto_tls_session_handshake(QCryptoTLSSession *sess, + Error **errp) +{ + error_setg(errp, "TLS requires GNUTLS support"); + return -1; +} + + +QCryptoTLSSessionHandshakeStatus +qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess) +{ + return QCRYPTO_TLS_HANDSHAKE_COMPLETE; +} + + +int +qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, + Error **errp) +{ + error_setg(errp, "TLS requires GNUTLS support"); + return -1; +} + + +char * +qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess) +{ + return NULL; +} + +#endif diff --git a/qemu/crypto/xts.c b/qemu/crypto/xts.c new file mode 100644 index 000000000..95212341f --- /dev/null +++ b/qemu/crypto/xts.c @@ -0,0 +1,230 @@ +/* + * QEMU Crypto XTS cipher mode + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + * This code is originally derived from public domain / WTFPL code in + * LibTomCrypt crytographic library http://libtom.org. The XTS code + * was donated by Elliptic Semiconductor Inc (www.ellipticsemi.com) + * to the LibTom Projects + * + */ + +#include "qemu/osdep.h" +#include "crypto/xts.h" + +static void xts_mult_x(uint8_t *I) +{ + int x; + uint8_t t, tt; + + for (x = t = 0; x < 16; x++) { + tt = I[x] >> 7; + I[x] = ((I[x] << 1) | t) & 0xFF; + t = tt; + } + if (tt) { + I[0] ^= 0x87; + } +} + + +/** + * xts_tweak_uncrypt: + * @param ctxt: the cipher context + * @param func: the cipher function + * @src: buffer providing the cipher text of XTS_BLOCK_SIZE bytes + * @dst: buffer to output the plain text of XTS_BLOCK_SIZE bytes + * @iv: the initialization vector tweak of XTS_BLOCK_SIZE bytes + * + * Decrypt data with a tweak + */ +static void xts_tweak_decrypt(const void *ctx, + xts_cipher_func *func, + const uint8_t *src, + uint8_t *dst, + uint8_t *iv) +{ + unsigned long x; + + /* tweak encrypt block i */ + for (x = 0; x < XTS_BLOCK_SIZE; x++) { + dst[x] = src[x] ^ iv[x]; + } + + func(ctx, XTS_BLOCK_SIZE, dst, dst); + + for (x = 0; x < XTS_BLOCK_SIZE; x++) { + dst[x] = dst[x] ^ iv[x]; + } + + /* LFSR the tweak */ + xts_mult_x(iv); +} + + +void xts_decrypt(const void *datactx, + const void *tweakctx, + xts_cipher_func *encfunc, + xts_cipher_func *decfunc, + uint8_t *iv, + size_t length, + uint8_t *dst, + const uint8_t *src) +{ + uint8_t PP[XTS_BLOCK_SIZE], CC[XTS_BLOCK_SIZE], T[XTS_BLOCK_SIZE]; + unsigned long i, m, mo, lim; + + /* get number of blocks */ + m = length >> 4; + mo = length & 15; + + /* must have at least one full block */ + g_assert(m != 0); + + if (mo == 0) { + lim = m; + } else { + lim = m - 1; + } + + /* encrypt the iv */ + encfunc(tweakctx, XTS_BLOCK_SIZE, T, iv); + + for (i = 0; i < lim; i++) { + xts_tweak_decrypt(datactx, decfunc, src, dst, T); + + src += XTS_BLOCK_SIZE; + dst += XTS_BLOCK_SIZE; + } + + /* if length is not a multiple of XTS_BLOCK_SIZE then */ + if (mo > 0) { + memcpy(CC, T, XTS_BLOCK_SIZE); + xts_mult_x(CC); + + /* PP = tweak decrypt block m-1 */ + xts_tweak_decrypt(datactx, decfunc, src, PP, CC); + + /* Pm = first length % XTS_BLOCK_SIZE bytes of PP */ + for (i = 0; i < mo; i++) { + CC[i] = src[XTS_BLOCK_SIZE + i]; + dst[XTS_BLOCK_SIZE + i] = PP[i]; + } + for (; i < XTS_BLOCK_SIZE; i++) { + CC[i] = PP[i]; + } + + /* Pm-1 = Tweak uncrypt CC */ + xts_tweak_decrypt(datactx, decfunc, CC, dst, T); + } + + /* Decrypt the iv back */ + decfunc(tweakctx, XTS_BLOCK_SIZE, iv, T); +} + + +/** + * xts_tweak_crypt: + * @param ctxt: the cipher context + * @param func: the cipher function + * @src: buffer providing the plain text of XTS_BLOCK_SIZE bytes + * @dst: buffer to output the cipher text of XTS_BLOCK_SIZE bytes + * @iv: the initialization vector tweak of XTS_BLOCK_SIZE bytes + * + * Encrypt data with a tweak + */ +static void xts_tweak_encrypt(const void *ctx, + xts_cipher_func *func, + const uint8_t *src, + uint8_t *dst, + uint8_t *iv) +{ + unsigned long x; + + /* tweak encrypt block i */ + for (x = 0; x < XTS_BLOCK_SIZE; x++) { + dst[x] = src[x] ^ iv[x]; + } + + func(ctx, XTS_BLOCK_SIZE, dst, dst); + + for (x = 0; x < XTS_BLOCK_SIZE; x++) { + dst[x] = dst[x] ^ iv[x]; + } + + /* LFSR the tweak */ + xts_mult_x(iv); +} + + +void xts_encrypt(const void *datactx, + const void *tweakctx, + xts_cipher_func *encfunc, + xts_cipher_func *decfunc, + uint8_t *iv, + size_t length, + uint8_t *dst, + const uint8_t *src) +{ + uint8_t PP[XTS_BLOCK_SIZE], CC[XTS_BLOCK_SIZE], T[XTS_BLOCK_SIZE]; + unsigned long i, m, mo, lim; + + /* get number of blocks */ + m = length >> 4; + mo = length & 15; + + /* must have at least one full block */ + g_assert(m != 0); + + if (mo == 0) { + lim = m; + } else { + lim = m - 1; + } + + /* encrypt the iv */ + encfunc(tweakctx, XTS_BLOCK_SIZE, T, iv); + + for (i = 0; i < lim; i++) { + xts_tweak_encrypt(datactx, encfunc, src, dst, T); + + dst += XTS_BLOCK_SIZE; + src += XTS_BLOCK_SIZE; + } + + /* if length is not a multiple of XTS_BLOCK_SIZE then */ + if (mo > 0) { + /* CC = tweak encrypt block m-1 */ + xts_tweak_encrypt(datactx, encfunc, src, CC, T); + + /* Cm = first length % XTS_BLOCK_SIZE bytes of CC */ + for (i = 0; i < mo; i++) { + PP[i] = src[XTS_BLOCK_SIZE + i]; + dst[XTS_BLOCK_SIZE + i] = CC[i]; + } + + for (; i < XTS_BLOCK_SIZE; i++) { + PP[i] = CC[i]; + } + + /* Cm-1 = Tweak encrypt PP */ + xts_tweak_encrypt(datactx, encfunc, PP, dst, T); + } + + /* Decrypt the iv back */ + decfunc(tweakctx, XTS_BLOCK_SIZE, iv, T); +} diff --git a/qemu/default-configs/aarch64-linux-user.mak b/qemu/default-configs/aarch64-linux-user.mak index 3df7de5b8..0a5b08a00 100644 --- a/qemu/default-configs/aarch64-linux-user.mak +++ b/qemu/default-configs/aarch64-linux-user.mak @@ -1,3 +1 @@ # Default configuration for aarch64-linux-user - -CONFIG_GDBSTUB_XML=y diff --git a/qemu/default-configs/arm-softmmu.mak b/qemu/default-configs/arm-softmmu.mak index 74f1db3a0..c63cdd073 100644 --- a/qemu/default-configs/arm-softmmu.mak +++ b/qemu/default-configs/arm-softmmu.mak @@ -28,6 +28,7 @@ CONFIG_SSI_M25P80=y CONFIG_LAN9118=y CONFIG_SMC91C111=y CONFIG_ALLWINNER_EMAC=y +CONFIG_IMX_FEC=y CONFIG_DS1338=y CONFIG_PFLASH_CFI01=y CONFIG_PFLASH_CFI02=y @@ -78,6 +79,7 @@ CONFIG_TUSB6010=y CONFIG_IMX=y CONFIG_MAINSTONE=y CONFIG_NSERIES=y +CONFIG_RASPI=y CONFIG_REALVIEW=y CONFIG_ZAURUS=y CONFIG_ZYNQ=y @@ -98,7 +100,15 @@ CONFIG_ALLWINNER_A10_PIT=y CONFIG_ALLWINNER_A10_PIC=y CONFIG_ALLWINNER_A10=y +CONFIG_FSL_IMX31=y +CONFIG_FSL_IMX25=y + +CONFIG_IMX_I2C=y + CONFIG_XIO3130=y CONFIG_IOH3420=y CONFIG_I82801B11=y CONFIG_ACPI=y +CONFIG_SMBIOS=y +CONFIG_ASPEED_SOC=y +CONFIG_GPIO_KEY=y diff --git a/qemu/default-configs/i386-softmmu.mak b/qemu/default-configs/i386-softmmu.mak index 48b57623f..b177e5210 100644 --- a/qemu/default-configs/i386-softmmu.mak +++ b/qemu/default-configs/i386-softmmu.mak @@ -7,7 +7,13 @@ CONFIG_QXL=$(CONFIG_SPICE) CONFIG_VGA_ISA=y CONFIG_VGA_CIRRUS=y CONFIG_VMWARE_VGA=y +CONFIG_VIRTIO_VGA=y CONFIG_VMMOUSE=y +CONFIG_IPMI=y +CONFIG_IPMI_LOCAL=y +CONFIG_IPMI_EXTERN=y +CONFIG_ISA_IPMI_KCS=y +CONFIG_ISA_IPMI_BT=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y @@ -43,9 +49,12 @@ CONFIG_LPC_ICH9=y CONFIG_PCI_Q35=y CONFIG_APIC=y CONFIG_IOAPIC=y -CONFIG_ICC_BUS=y CONFIG_PVPANIC=y CONFIG_MEM_HOTPLUG=y +CONFIG_NVDIMM=y +CONFIG_ACPI_NVDIMM=y CONFIG_XIO3130=y CONFIG_IOH3420=y CONFIG_I82801B11=y +CONFIG_SMBIOS=y +CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM) diff --git a/qemu/default-configs/mips-softmmu-common.mak b/qemu/default-configs/mips-softmmu-common.mak new file mode 100644 index 000000000..0394514b9 --- /dev/null +++ b/qemu/default-configs/mips-softmmu-common.mak @@ -0,0 +1,34 @@ +# Common mips*-softmmu CONFIG defines + +include pci.mak +include sound.mak +include usb.mak +CONFIG_ESP=y +CONFIG_VGA_ISA=y +CONFIG_VGA_ISA_MM=y +CONFIG_VGA_CIRRUS=y +CONFIG_VMWARE_VGA=y +CONFIG_SERIAL=y +CONFIG_PARALLEL=y +CONFIG_I8254=y +CONFIG_PCSPK=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_ACPI=y +CONFIG_ACPI_X86=y +CONFIG_ACPI_MEMORY_HOTPLUG=y +CONFIG_ACPI_CPU_HOTPLUG=y +CONFIG_APM=y +CONFIG_I8257=y +CONFIG_PIIX4=y +CONFIG_IDE_ISA=y +CONFIG_IDE_PIIX=y +CONFIG_NE2000_ISA=y +CONFIG_MIPSNET=y +CONFIG_PFLASH_CFI01=y +CONFIG_I8259=y +CONFIG_MC146818RTC=y +CONFIG_ISA_TESTDEV=y +CONFIG_EMPTY_SLOT=y +CONFIG_MIPS_CPS=y +CONFIG_MIPS_ITU=y diff --git a/qemu/default-configs/mips-softmmu.mak b/qemu/default-configs/mips-softmmu.mak index 44467c37c..9fede6e00 100644 --- a/qemu/default-configs/mips-softmmu.mak +++ b/qemu/default-configs/mips-softmmu.mak @@ -1,32 +1,3 @@ # Default configuration for mips-softmmu -include pci.mak -include sound.mak -include usb.mak -CONFIG_ESP=y -CONFIG_VGA_ISA=y -CONFIG_VGA_ISA_MM=y -CONFIG_VGA_CIRRUS=y -CONFIG_VMWARE_VGA=y -CONFIG_SERIAL=y -CONFIG_PARALLEL=y -CONFIG_I8254=y -CONFIG_PCSPK=y -CONFIG_PCKBD=y -CONFIG_FDC=y -CONFIG_ACPI=y -CONFIG_ACPI_X86=y -CONFIG_ACPI_MEMORY_HOTPLUG=y -CONFIG_ACPI_CPU_HOTPLUG=y -CONFIG_APM=y -CONFIG_I8257=y -CONFIG_PIIX4=y -CONFIG_IDE_ISA=y -CONFIG_IDE_PIIX=y -CONFIG_NE2000_ISA=y -CONFIG_MIPSNET=y -CONFIG_PFLASH_CFI01=y -CONFIG_I8259=y -CONFIG_MC146818RTC=y -CONFIG_ISA_TESTDEV=y -CONFIG_EMPTY_SLOT=y +include mips-softmmu-common.mak diff --git a/qemu/default-configs/mips64-softmmu.mak b/qemu/default-configs/mips64-softmmu.mak index 66ed5f94c..bad749667 100644 --- a/qemu/default-configs/mips64-softmmu.mak +++ b/qemu/default-configs/mips64-softmmu.mak @@ -1,38 +1,9 @@ # Default configuration for mips64-softmmu -include pci.mak -include sound.mak -include usb.mak -CONFIG_ESP=y -CONFIG_VGA_ISA=y -CONFIG_VGA_ISA_MM=y -CONFIG_VGA_CIRRUS=y -CONFIG_VMWARE_VGA=y -CONFIG_SERIAL=y -CONFIG_PARALLEL=y -CONFIG_I8254=y -CONFIG_PCSPK=y -CONFIG_PCKBD=y -CONFIG_FDC=y -CONFIG_ACPI=y -CONFIG_ACPI_X86=y -CONFIG_ACPI_MEMORY_HOTPLUG=y -CONFIG_ACPI_CPU_HOTPLUG=y -CONFIG_APM=y -CONFIG_I8257=y -CONFIG_PIIX4=y -CONFIG_IDE_ISA=y -CONFIG_IDE_PIIX=y -CONFIG_NE2000_ISA=y +include mips-softmmu-common.mak CONFIG_RC4030=y CONFIG_DP8393X=y CONFIG_DS1225Y=y -CONFIG_MIPSNET=y -CONFIG_PFLASH_CFI01=y CONFIG_JAZZ=y CONFIG_G364FB=y -CONFIG_I8259=y CONFIG_JAZZ_LED=y -CONFIG_MC146818RTC=y -CONFIG_ISA_TESTDEV=y -CONFIG_EMPTY_SLOT=y diff --git a/qemu/default-configs/mips64el-softmmu.mak b/qemu/default-configs/mips64el-softmmu.mak index bfca2b2b7..485e218cf 100644 --- a/qemu/default-configs/mips64el-softmmu.mak +++ b/qemu/default-configs/mips64el-softmmu.mak @@ -1,41 +1,12 @@ # Default configuration for mips64el-softmmu -include pci.mak -include sound.mak -include usb.mak -CONFIG_ESP=y -CONFIG_VGA_ISA=y -CONFIG_VGA_ISA_MM=y -CONFIG_VGA_CIRRUS=y -CONFIG_VMWARE_VGA=y -CONFIG_SERIAL=y -CONFIG_PARALLEL=y -CONFIG_I8254=y -CONFIG_PCSPK=y -CONFIG_PCKBD=y -CONFIG_FDC=y -CONFIG_ACPI=y -CONFIG_ACPI_X86=y -CONFIG_ACPI_MEMORY_HOTPLUG=y -CONFIG_ACPI_CPU_HOTPLUG=y -CONFIG_APM=y -CONFIG_I8257=y -CONFIG_PIIX4=y -CONFIG_IDE_ISA=y -CONFIG_IDE_PIIX=y +include mips-softmmu-common.mak CONFIG_IDE_VIA=y -CONFIG_NE2000_ISA=y CONFIG_RC4030=y CONFIG_DP8393X=y CONFIG_DS1225Y=y -CONFIG_MIPSNET=y -CONFIG_PFLASH_CFI01=y CONFIG_FULONG=y CONFIG_JAZZ=y CONFIG_G364FB=y -CONFIG_I8259=y CONFIG_JAZZ_LED=y -CONFIG_MC146818RTC=y CONFIG_VT82C686=y -CONFIG_ISA_TESTDEV=y -CONFIG_EMPTY_SLOT=y diff --git a/qemu/default-configs/mipsel-softmmu.mak b/qemu/default-configs/mipsel-softmmu.mak index 0162ef024..a7f605948 100644 --- a/qemu/default-configs/mipsel-softmmu.mak +++ b/qemu/default-configs/mipsel-softmmu.mak @@ -1,32 +1,3 @@ # Default configuration for mipsel-softmmu -include pci.mak -include sound.mak -include usb.mak -CONFIG_ESP=y -CONFIG_VGA_ISA=y -CONFIG_VGA_ISA_MM=y -CONFIG_VGA_CIRRUS=y -CONFIG_VMWARE_VGA=y -CONFIG_SERIAL=y -CONFIG_PARALLEL=y -CONFIG_I8254=y -CONFIG_PCSPK=y -CONFIG_PCKBD=y -CONFIG_FDC=y -CONFIG_ACPI=y -CONFIG_ACPI_X86=y -CONFIG_ACPI_MEMORY_HOTPLUG=y -CONFIG_ACPI_CPU_HOTPLUG=y -CONFIG_APM=y -CONFIG_I8257=y -CONFIG_PIIX4=y -CONFIG_IDE_ISA=y -CONFIG_IDE_PIIX=y -CONFIG_NE2000_ISA=y -CONFIG_MIPSNET=y -CONFIG_PFLASH_CFI01=y -CONFIG_I8259=y -CONFIG_MC146818RTC=y -CONFIG_ISA_TESTDEV=y -CONFIG_EMPTY_SLOT=y +include mips-softmmu-common.mak diff --git a/qemu/default-configs/pci.mak b/qemu/default-configs/pci.mak index 7e10903ba..9c8bc68c4 100644 --- a/qemu/default-configs/pci.mak +++ b/qemu/default-configs/pci.mak @@ -15,6 +15,7 @@ CONFIG_ES1370=y CONFIG_LSI_SCSI_PCI=y CONFIG_VMW_PVSCSI_SCSI_PCI=y CONFIG_MEGASAS_SCSI_PCI=y +CONFIG_MPTSAS_SCSI_PCI=y CONFIG_RTL8139_PCI=y CONFIG_E1000_PCI=y CONFIG_VMXNET3_PCI=y @@ -35,5 +36,5 @@ CONFIG_SDHCI=y CONFIG_EDU=y CONFIG_VGA=y CONFIG_VGA_PCI=y -CONFIG_IVSHMEM=$(CONFIG_KVM) +CONFIG_IVSHMEM=$(CONFIG_EVENTFD) CONFIG_ROCKER=y diff --git a/qemu/default-configs/ppc64-softmmu.mak b/qemu/default-configs/ppc64-softmmu.mak index ab62cc7ff..bb71b23ee 100644 --- a/qemu/default-configs/ppc64-softmmu.mak +++ b/qemu/default-configs/ppc64-softmmu.mak @@ -3,6 +3,7 @@ include pci.mak include sound.mak include usb.mak +CONFIG_VIRTIO_VGA=y CONFIG_ISA_MMIO=y CONFIG_ESCC=y CONFIG_M48T59=y @@ -52,3 +53,4 @@ CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM)) # For PReP CONFIG_MC146818RTC=y CONFIG_ISA_TESTDEV=y +CONFIG_MEM_HOTPLUG=y diff --git a/qemu/default-configs/tilegx-linux-user.mak b/qemu/default-configs/tilegx-linux-user.mak new file mode 100644 index 000000000..3e47493af --- /dev/null +++ b/qemu/default-configs/tilegx-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for tilegx-linux-user diff --git a/qemu/default-configs/x86_64-softmmu.mak b/qemu/default-configs/x86_64-softmmu.mak index 4962ed70a..6e3b312c5 100644 --- a/qemu/default-configs/x86_64-softmmu.mak +++ b/qemu/default-configs/x86_64-softmmu.mak @@ -9,6 +9,11 @@ CONFIG_VGA_CIRRUS=y CONFIG_VMWARE_VGA=y CONFIG_VIRTIO_VGA=y CONFIG_VMMOUSE=y +CONFIG_IPMI=y +CONFIG_IPMI_LOCAL=y +CONFIG_IPMI_EXTERN=y +CONFIG_ISA_IPMI_KCS=y +CONFIG_ISA_IPMI_BT=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y @@ -44,9 +49,12 @@ CONFIG_LPC_ICH9=y CONFIG_PCI_Q35=y CONFIG_APIC=y CONFIG_IOAPIC=y -CONFIG_ICC_BUS=y CONFIG_PVPANIC=y CONFIG_MEM_HOTPLUG=y +CONFIG_NVDIMM=y +CONFIG_ACPI_NVDIMM=y CONFIG_XIO3130=y CONFIG_IOH3420=y CONFIG_I82801B11=y +CONFIG_SMBIOS=y +CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM) diff --git a/qemu/device-hotplug.c b/qemu/device-hotplug.c index 68b94967c..126f73c67 100644 --- a/qemu/device-hotplug.c +++ b/qemu/device-hotplug.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/boards.h" #include "sysemu/block-backend.h" @@ -29,6 +30,7 @@ #include "qemu/config-file.h" #include "sysemu/sysemu.h" #include "monitor/monitor.h" +#include "block/block_int.h" static DriveInfo *add_init_drive(const char *optstr) { @@ -54,6 +56,12 @@ void hmp_drive_add(Monitor *mon, const QDict *qdict) { DriveInfo *dinfo = NULL; const char *opts = qdict_get_str(qdict, "opts"); + bool node = qdict_get_try_bool(qdict, "node", false); + + if (node) { + hmp_drive_add_node(mon, opts); + return; + } dinfo = add_init_drive(opts); if (!dinfo) { @@ -76,6 +84,8 @@ void hmp_drive_add(Monitor *mon, const QDict *qdict) err: if (dinfo) { - blk_unref(blk_by_legacy_dinfo(dinfo)); + BlockBackend *blk = blk_by_legacy_dinfo(dinfo); + monitor_remove_blk(blk); + blk_unref(blk); } } diff --git a/qemu/device_tree.c b/qemu/device_tree.c index a9f5f8e59..ccba1fd4a 100644 --- a/qemu/device_tree.c +++ b/qemu/device_tree.c @@ -11,13 +11,13 @@ * */ -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" +#ifdef CONFIG_LINUX +#include +#endif + +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/error-report.h" #include "sysemu/device_tree.h" @@ -117,6 +117,102 @@ fail: return NULL; } +#ifdef CONFIG_LINUX + +#define SYSFS_DT_BASEDIR "/proc/device-tree" + +/** + * read_fstree: this function is inspired from dtc read_fstree + * @fdt: preallocated fdt blob buffer, to be populated + * @dirname: directory to scan under SYSFS_DT_BASEDIR + * the search is recursive and the tree is searched down to the + * leaves (property files). + * + * the function asserts in case of error + */ +static void read_fstree(void *fdt, const char *dirname) +{ + DIR *d; + struct dirent *de; + struct stat st; + const char *root_dir = SYSFS_DT_BASEDIR; + const char *parent_node; + + if (strstr(dirname, root_dir) != dirname) { + error_setg(&error_fatal, "%s: %s must be searched within %s", + __func__, dirname, root_dir); + } + parent_node = &dirname[strlen(SYSFS_DT_BASEDIR)]; + + d = opendir(dirname); + if (!d) { + error_setg(&error_fatal, "%s cannot open %s", __func__, dirname); + } + + while ((de = readdir(d)) != NULL) { + char *tmpnam; + + if (!g_strcmp0(de->d_name, ".") + || !g_strcmp0(de->d_name, "..")) { + continue; + } + + tmpnam = g_strdup_printf("%s/%s", dirname, de->d_name); + + if (lstat(tmpnam, &st) < 0) { + error_setg(&error_fatal, "%s cannot lstat %s", __func__, tmpnam); + } + + if (S_ISREG(st.st_mode)) { + gchar *val; + gsize len; + + if (!g_file_get_contents(tmpnam, &val, &len, NULL)) { + error_setg(&error_fatal, "%s not able to extract info from %s", + __func__, tmpnam); + } + + if (strlen(parent_node) > 0) { + qemu_fdt_setprop(fdt, parent_node, + de->d_name, val, len); + } else { + qemu_fdt_setprop(fdt, "/", de->d_name, val, len); + } + g_free(val); + } else if (S_ISDIR(st.st_mode)) { + char *node_name; + + node_name = g_strdup_printf("%s/%s", + parent_node, de->d_name); + qemu_fdt_add_subnode(fdt, node_name); + g_free(node_name); + read_fstree(fdt, tmpnam); + } + + g_free(tmpnam); + } + + closedir(d); +} + +/* load_device_tree_from_sysfs: extract the dt blob from host sysfs */ +void *load_device_tree_from_sysfs(void) +{ + void *host_fdt; + int host_fdt_size; + + host_fdt = create_device_tree(&host_fdt_size); + read_fstree(host_fdt, SYSFS_DT_BASEDIR); + if (fdt_check_header(host_fdt)) { + error_setg(&error_fatal, + "%s host device tree extracted into memory is invalid", + __func__); + } + return host_fdt; +} + +#endif /* CONFIG_LINUX */ + static int findnode_nofail(void *fdt, const char *node_path) { int offset; @@ -131,6 +227,60 @@ static int findnode_nofail(void *fdt, const char *node_path) return offset; } +char **qemu_fdt_node_path(void *fdt, const char *name, char *compat, + Error **errp) +{ + int offset, len, ret; + const char *iter_name; + unsigned int path_len = 16, n = 0; + GSList *path_list = NULL, *iter; + char **path_array; + + offset = fdt_node_offset_by_compatible(fdt, -1, compat); + + while (offset >= 0) { + iter_name = fdt_get_name(fdt, offset, &len); + if (!iter_name) { + offset = len; + break; + } + if (!strcmp(iter_name, name)) { + char *path; + + path = g_malloc(path_len); + while ((ret = fdt_get_path(fdt, offset, path, path_len)) + == -FDT_ERR_NOSPACE) { + path_len += 16; + path = g_realloc(path, path_len); + } + path_list = g_slist_prepend(path_list, path); + n++; + } + offset = fdt_node_offset_by_compatible(fdt, offset, compat); + } + + if (offset < 0 && offset != -FDT_ERR_NOTFOUND) { + error_setg(errp, "%s: abort parsing dt for %s/%s: %s", + __func__, name, compat, fdt_strerror(offset)); + for (iter = path_list; iter; iter = iter->next) { + g_free(iter->data); + } + g_slist_free(path_list); + return NULL; + } + + path_array = g_new(char *, n + 1); + path_array[n--] = NULL; + + for (iter = path_list; iter; iter = iter->next) { + path_array[n--] = iter->data; + } + + g_slist_free(path_list); + + return path_array; +} + int qemu_fdt_setprop(void *fdt, const char *node_path, const char *property, const void *val, int size) { @@ -184,31 +334,39 @@ int qemu_fdt_setprop_string(void *fdt, const char *node_path, } const void *qemu_fdt_getprop(void *fdt, const char *node_path, - const char *property, int *lenp) + const char *property, int *lenp, Error **errp) { int len; const void *r; + if (!lenp) { lenp = &len; } r = fdt_getprop(fdt, findnode_nofail(fdt, node_path), property, lenp); if (!r) { - error_report("%s: Couldn't get %s/%s: %s", __func__, - node_path, property, fdt_strerror(*lenp)); - exit(1); + error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__, + node_path, property, fdt_strerror(*lenp)); } return r; } uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path, - const char *property) + const char *property, int *lenp, Error **errp) { int len; - const uint32_t *p = qemu_fdt_getprop(fdt, node_path, property, &len); - if (len != 4) { - error_report("%s: %s/%s not 4 bytes long (not a cell?)", - __func__, node_path, property); - exit(1); + const uint32_t *p; + + if (!lenp) { + lenp = &len; + } + p = qemu_fdt_getprop(fdt, node_path, property, lenp, errp); + if (!p) { + return 0; + } else if (*lenp != 4) { + error_setg(errp, "%s: %s/%s not 4 bytes long (not a cell?)", + __func__, node_path, property); + *lenp = -EINVAL; + return 0; } return be32_to_cpu(*p); } diff --git a/qemu/disas.c b/qemu/disas.c index 69a606691..05a7a1260 100644 --- a/qemu/disas.c +++ b/qemu/disas.c @@ -1,9 +1,8 @@ /* General "disassemble this chunk" code. Used for debugging. */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "disas/bfd.h" #include "elf.h" -#include #include "cpu.h" #include "disas/disas.h" @@ -72,14 +71,6 @@ generic_print_address (bfd_vma addr, struct disassemble_info *info) (*info->fprintf_func) (info->stream, "0x%" PRIx64, addr); } -/* Print address in hex, truncated to the width of a target virtual address. */ -static void -generic_print_target_address(bfd_vma addr, struct disassemble_info *info) -{ - uint64_t mask = ~0ULL >> (64 - TARGET_VIRT_ADDR_SPACE_BITS); - generic_print_address(addr & mask, info); -} - /* Print address in hex, truncated to the width of a host virtual address. */ static void generic_print_host_address(bfd_vma addr, struct disassemble_info *info) @@ -201,7 +192,7 @@ void target_disas(FILE *out, CPUState *cpu, target_ulong code, s.info.read_memory_func = target_read_memory; s.info.buffer_vma = code; s.info.buffer_length = size; - s.info.print_address_func = generic_print_target_address; + s.info.print_address_func = generic_print_address; #ifdef TARGET_WORDS_BIGENDIAN s.info.endian = BFD_ENDIAN_BIG; @@ -222,11 +213,6 @@ void target_disas(FILE *out, CPUState *cpu, target_ulong code, s.info.mach = bfd_mach_i386_i386; } s.info.print_insn = print_insn_i386; -#elif defined(TARGET_SPARC) - s.info.print_insn = print_insn_sparc; -#ifdef TARGET_SPARC64 - s.info.mach = bfd_mach_sparc_v9b; -#endif #elif defined(TARGET_PPC) if ((flags >> 16) & 1) { s.info.endian = BFD_ENDIAN_LITTLE; @@ -243,29 +229,6 @@ void target_disas(FILE *out, CPUState *cpu, target_ulong code, } s.info.disassembler_options = (char *)"any"; s.info.print_insn = print_insn_ppc; -#elif defined(TARGET_M68K) - s.info.print_insn = print_insn_m68k; -#elif defined(TARGET_MIPS) -#ifdef TARGET_WORDS_BIGENDIAN - s.info.print_insn = print_insn_big_mips; -#else - s.info.print_insn = print_insn_little_mips; -#endif -#elif defined(TARGET_SH4) - s.info.mach = bfd_mach_sh4; - s.info.print_insn = print_insn_sh; -#elif defined(TARGET_ALPHA) - s.info.mach = bfd_mach_alpha_ev6; - s.info.print_insn = print_insn_alpha; -#elif defined(TARGET_S390X) - s.info.mach = bfd_mach_s390_64; - s.info.print_insn = print_insn_s390; -#elif defined(TARGET_MOXIE) - s.info.mach = bfd_arch_moxie; - s.info.print_insn = print_insn_moxie; -#elif defined(TARGET_LM32) - s.info.mach = bfd_mach_lm32; - s.info.print_insn = print_insn_lm32; #endif if (s.info.print_insn == NULL) { s.info.print_insn = print_insn_od_target; @@ -400,16 +363,6 @@ monitor_read_memory (bfd_vma memaddr, bfd_byte *myaddr, int length, return 0; } -static int GCC_FMT_ATTR(2, 3) -monitor_fprintf(FILE *stream, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - monitor_vprintf((Monitor *)stream, fmt, ap); - va_end(ap); - return 0; -} - /* Disassembler for the monitor. See target_disas for a description of flags. */ void monitor_disas(Monitor *mon, CPUState *cpu, @@ -424,7 +377,7 @@ void monitor_disas(Monitor *mon, CPUState *cpu, s.cpu = cpu; monitor_disas_is_physical = is_physical; s.info.read_memory_func = monitor_read_memory; - s.info.print_address_func = generic_print_target_address; + s.info.print_address_func = generic_print_address; s.info.buffer_vma = pc; @@ -447,13 +400,6 @@ void monitor_disas(Monitor *mon, CPUState *cpu, s.info.mach = bfd_mach_i386_i386; } s.info.print_insn = print_insn_i386; -#elif defined(TARGET_ALPHA) - s.info.print_insn = print_insn_alpha; -#elif defined(TARGET_SPARC) - s.info.print_insn = print_insn_sparc; -#ifdef TARGET_SPARC64 - s.info.mach = bfd_mach_sparc_v9b; -#endif #elif defined(TARGET_PPC) if (flags & 0xFFFF) { /* If we have a precise definition of the instruction set, use it. */ @@ -469,26 +415,6 @@ void monitor_disas(Monitor *mon, CPUState *cpu, s.info.endian = BFD_ENDIAN_LITTLE; } s.info.print_insn = print_insn_ppc; -#elif defined(TARGET_M68K) - s.info.print_insn = print_insn_m68k; -#elif defined(TARGET_MIPS) -#ifdef TARGET_WORDS_BIGENDIAN - s.info.print_insn = print_insn_big_mips; -#else - s.info.print_insn = print_insn_little_mips; -#endif -#elif defined(TARGET_SH4) - s.info.mach = bfd_mach_sh4; - s.info.print_insn = print_insn_sh; -#elif defined(TARGET_S390X) - s.info.mach = bfd_mach_s390_64; - s.info.print_insn = print_insn_s390; -#elif defined(TARGET_MOXIE) - s.info.mach = bfd_arch_moxie; - s.info.print_insn = print_insn_moxie; -#elif defined(TARGET_LM32) - s.info.mach = bfd_mach_lm32; - s.info.print_insn = print_insn_lm32; #endif if (!s.info.print_insn) { monitor_printf(mon, "0x" TARGET_FMT_lx diff --git a/qemu/disas/Makefile.objs b/qemu/disas/Makefile.objs index 8dae4daec..abeba8466 100644 --- a/qemu/disas/Makefile.objs +++ b/qemu/disas/Makefile.objs @@ -4,7 +4,10 @@ common-obj-$(CONFIG_ARM_DIS) += arm.o common-obj-$(CONFIG_ARM_A64_DIS) += arm-a64.o common-obj-$(CONFIG_ARM_A64_DIS) += libvixl/ libvixldir = $(SRC_PATH)/disas/libvixl -arm-a64.o-cflags := -I$(libvixldir) +# The -Wno-sign-compare is needed only for gcc 4.6, which complains about +# some signed-unsigned equality comparisons in libvixl which later gcc +# versions do not. +arm-a64.o-cflags := -I$(libvixldir) -Wno-sign-compare common-obj-$(CONFIG_CRIS_DIS) += cris.o common-obj-$(CONFIG_HPPA_DIS) += hppa.o common-obj-$(CONFIG_I386_DIS) += i386.o diff --git a/qemu/disas/alpha.c b/qemu/disas/alpha.c index a950b9cee..44d00a363 100644 --- a/qemu/disas/alpha.c +++ b/qemu/disas/alpha.c @@ -19,7 +19,7 @@ You should have received a copy of the GNU General Public License along with this file; see the file COPYING. If not, see . */ -#include +#include "qemu/osdep.h" #include "disas/bfd.h" /* MAX is redefined below, so remove any previous definition. */ diff --git a/qemu/disas/arm-a64.cc b/qemu/disas/arm-a64.cc index b57256b26..9280950ce 100644 --- a/qemu/disas/arm-a64.cc +++ b/qemu/disas/arm-a64.cc @@ -17,12 +17,13 @@ * along with this program. If not, see . */ -#include "a64/disasm-a64.h" - extern "C" { +#include "qemu/osdep.h" #include "disas/bfd.h" } +#include "vixl/a64/disasm-a64.h" + using namespace vixl; static Decoder *vixl_decoder = NULL; diff --git a/qemu/disas/arm.c b/qemu/disas/arm.c index 616524653..70da5298a 100644 --- a/qemu/disas/arm.c +++ b/qemu/disas/arm.c @@ -22,6 +22,7 @@ /* Start of qemu specific additions. Mostly this is stub definitions for things we don't care about. */ +#include "qemu/osdep.h" #include "disas/bfd.h" #define ATTRIBUTE_UNUSED __attribute__((unused)) #define ISSPACE(x) ((x) == ' ' || (x) == '\t' || (x) == '\n') @@ -1779,7 +1780,7 @@ print_insn_coprocessor (bfd_vma pc, struct disassemble_info *info, long given, /* Is ``imm'' a negative number? */ if (imm & 0x40) - imm |= (-1 << 7); + imm |= (~0u << 7); func (stream, "%d", imm); } diff --git a/qemu/disas/cris.c b/qemu/disas/cris.c index 1b76a09db..7f35bc0c6 100644 --- a/qemu/disas/cris.c +++ b/qemu/disas/cris.c @@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "disas/bfd.h" //#include "sysdep.h" @@ -2492,7 +2493,7 @@ print_with_operands (const struct cris_opcode *opcodep, = spec_reg_info ((insn >> 12) & 15, disdata->distype); if (sregp->name == NULL) - /* Should have been caught as a non-match eariler. */ + /* Should have been caught as a non-match earlier. */ *tp++ = '?'; else { diff --git a/qemu/disas/hppa.c b/qemu/disas/hppa.c index c7c8be66a..43facdc47 100644 --- a/qemu/disas/hppa.c +++ b/qemu/disas/hppa.c @@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, see . */ +#include "qemu/osdep.h" #include "disas/bfd.h" /* HP PA-RISC SOM object file format: definitions internal to BFD. diff --git a/qemu/disas/i386.c b/qemu/disas/i386.c index 00ceca9c5..c0e717abe 100644 --- a/qemu/disas/i386.c +++ b/qemu/disas/i386.c @@ -31,8 +31,10 @@ and the small letter tells about the operand size. Refer to the Intel manual for details. */ -#include +#include "qemu/osdep.h" #include "disas/bfd.h" +#include "qemu/cutils.h" + /* include/opcode/i386.h r1.78 */ /* opcode/i386.h -- Intel 80386 opcode macros @@ -153,8 +155,6 @@ /* opcodes/i386-dis.c r1.126 */ #include "qemu-common.h" -#include - static int fetch_data2(struct disassemble_info *, bfd_byte *); static int fetch_data(struct disassemble_info *, bfd_byte *); static void ckprefix (void); @@ -357,7 +357,7 @@ fetch_data(struct disassemble_info *info, bfd_byte *addr) #define Rd { OP_R, d_mode } #define Rm { OP_R, m_mode } #define Ib { OP_I, b_mode } -#define sIb { OP_sI, b_mode } /* sign extened byte */ +#define sIb { OP_sI, b_mode } /* sign extended byte */ #define Iv { OP_I, v_mode } #define Iq { OP_I, q_mode } #define Iv64 { OP_I64, v_mode } diff --git a/qemu/disas/ia64.c b/qemu/disas/ia64.c index a8fe26c41..140754c94 100644 --- a/qemu/disas/ia64.c +++ b/qemu/disas/ia64.c @@ -18,8 +18,7 @@ along with this file; see the file COPYING. If not, see . */ -#include -#include +#include "qemu/osdep.h" #include "disas/bfd.h" @@ -28,7 +27,6 @@ Free Software Foundation, Inc. Contributed by David Mosberger-Tang */ -#include typedef uint64_t ia64_insn; diff --git a/qemu/disas/libvixl/Makefile.objs b/qemu/disas/libvixl/Makefile.objs index 17e6565d1..bbe7695fd 100644 --- a/qemu/disas/libvixl/Makefile.objs +++ b/qemu/disas/libvixl/Makefile.objs @@ -1,8 +1,11 @@ -libvixl_OBJS = utils.o \ - a64/instructions-a64.o \ - a64/decoder-a64.o \ - a64/disasm-a64.o +libvixl_OBJS = vixl/utils.o \ + vixl/compiler-intrinsics.o \ + vixl/a64/instructions-a64.o \ + vixl/a64/decoder-a64.o \ + vixl/a64/disasm-a64.o -$(addprefix $(obj)/,$(libvixl_OBJS)): QEMU_CFLAGS := -I$(SRC_PATH)/disas/libvixl $(QEMU_CFLAGS) +# The -Wno-sign-compare is needed only for gcc 4.6, which complains about +# some signed-unsigned equality comparisons which later gcc versions do not. +$(addprefix $(obj)/,$(libvixl_OBJS)): QEMU_CFLAGS := -I$(SRC_PATH)/disas/libvixl $(QEMU_CFLAGS) -Wno-sign-compare common-obj-$(CONFIG_ARM_A64_DIS) += $(libvixl_OBJS) diff --git a/qemu/disas/libvixl/README b/qemu/disas/libvixl/README index 58db41c67..932a41adf 100644 --- a/qemu/disas/libvixl/README +++ b/qemu/disas/libvixl/README @@ -2,11 +2,10 @@ The code in this directory is a subset of libvixl: https://github.com/armvixl/vixl (specifically, it is the set of files needed for disassembly only, -taken from libvixl 1.7). +taken from libvixl 1.12). Bugfixes should preferably be sent upstream initially. The disassembler does not currently support the entire A64 instruction set. Notably: - * No Advanced SIMD support. * Limited support for system instructions. * A few miscellaneous integer and floating point instructions are missing. diff --git a/qemu/disas/libvixl/a64/assembler-a64.h b/qemu/disas/libvixl/a64/assembler-a64.h deleted file mode 100644 index 35aaf20f7..000000000 --- a/qemu/disas/libvixl/a64/assembler-a64.h +++ /dev/null @@ -1,2353 +0,0 @@ -// Copyright 2013, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef VIXL_A64_ASSEMBLER_A64_H_ -#define VIXL_A64_ASSEMBLER_A64_H_ - -#include -#include - -#include "globals.h" -#include "utils.h" -#include "code-buffer.h" -#include "a64/instructions-a64.h" - -namespace vixl { - -typedef uint64_t RegList; -static const int kRegListSizeInBits = sizeof(RegList) * 8; - - -// Registers. - -// Some CPURegister methods can return Register and FPRegister types, so we -// need to declare them in advance. -class Register; -class FPRegister; - - -class CPURegister { - public: - enum RegisterType { - // The kInvalid value is used to detect uninitialized static instances, - // which are always zero-initialized before any constructors are called. - kInvalid = 0, - kRegister, - kFPRegister, - kNoRegister - }; - - CPURegister() : code_(0), size_(0), type_(kNoRegister) { - VIXL_ASSERT(!IsValid()); - VIXL_ASSERT(IsNone()); - } - - CPURegister(unsigned code, unsigned size, RegisterType type) - : code_(code), size_(size), type_(type) { - VIXL_ASSERT(IsValidOrNone()); - } - - unsigned code() const { - VIXL_ASSERT(IsValid()); - return code_; - } - - RegisterType type() const { - VIXL_ASSERT(IsValidOrNone()); - return type_; - } - - RegList Bit() const { - VIXL_ASSERT(code_ < (sizeof(RegList) * 8)); - return IsValid() ? (static_cast(1) << code_) : 0; - } - - unsigned size() const { - VIXL_ASSERT(IsValid()); - return size_; - } - - int SizeInBytes() const { - VIXL_ASSERT(IsValid()); - VIXL_ASSERT(size() % 8 == 0); - return size_ / 8; - } - - int SizeInBits() const { - VIXL_ASSERT(IsValid()); - return size_; - } - - bool Is32Bits() const { - VIXL_ASSERT(IsValid()); - return size_ == 32; - } - - bool Is64Bits() const { - VIXL_ASSERT(IsValid()); - return size_ == 64; - } - - bool IsValid() const { - if (IsValidRegister() || IsValidFPRegister()) { - VIXL_ASSERT(!IsNone()); - return true; - } else { - VIXL_ASSERT(IsNone()); - return false; - } - } - - bool IsValidRegister() const { - return IsRegister() && - ((size_ == kWRegSize) || (size_ == kXRegSize)) && - ((code_ < kNumberOfRegisters) || (code_ == kSPRegInternalCode)); - } - - bool IsValidFPRegister() const { - return IsFPRegister() && - ((size_ == kSRegSize) || (size_ == kDRegSize)) && - (code_ < kNumberOfFPRegisters); - } - - bool IsNone() const { - // kNoRegister types should always have size 0 and code 0. - VIXL_ASSERT((type_ != kNoRegister) || (code_ == 0)); - VIXL_ASSERT((type_ != kNoRegister) || (size_ == 0)); - - return type_ == kNoRegister; - } - - bool Aliases(const CPURegister& other) const { - VIXL_ASSERT(IsValidOrNone() && other.IsValidOrNone()); - return (code_ == other.code_) && (type_ == other.type_); - } - - bool Is(const CPURegister& other) const { - VIXL_ASSERT(IsValidOrNone() && other.IsValidOrNone()); - return Aliases(other) && (size_ == other.size_); - } - - bool IsZero() const { - VIXL_ASSERT(IsValid()); - return IsRegister() && (code_ == kZeroRegCode); - } - - bool IsSP() const { - VIXL_ASSERT(IsValid()); - return IsRegister() && (code_ == kSPRegInternalCode); - } - - bool IsRegister() const { - return type_ == kRegister; - } - - bool IsFPRegister() const { - return type_ == kFPRegister; - } - - bool IsW() const { return IsValidRegister() && Is32Bits(); } - bool IsX() const { return IsValidRegister() && Is64Bits(); } - bool IsS() const { return IsValidFPRegister() && Is32Bits(); } - bool IsD() const { return IsValidFPRegister() && Is64Bits(); } - - const Register& W() const; - const Register& X() const; - const FPRegister& S() const; - const FPRegister& D() const; - - bool IsSameSizeAndType(const CPURegister& other) const { - return (size_ == other.size_) && (type_ == other.type_); - } - - protected: - unsigned code_; - unsigned size_; - RegisterType type_; - - private: - bool IsValidOrNone() const { - return IsValid() || IsNone(); - } -}; - - -class Register : public CPURegister { - public: - Register() : CPURegister() {} - explicit Register(const CPURegister& other) - : CPURegister(other.code(), other.size(), other.type()) { - VIXL_ASSERT(IsValidRegister()); - } - Register(unsigned code, unsigned size) - : CPURegister(code, size, kRegister) {} - - bool IsValid() const { - VIXL_ASSERT(IsRegister() || IsNone()); - return IsValidRegister(); - } - - static const Register& WRegFromCode(unsigned code); - static const Register& XRegFromCode(unsigned code); - - private: - static const Register wregisters[]; - static const Register xregisters[]; -}; - - -class FPRegister : public CPURegister { - public: - FPRegister() : CPURegister() {} - explicit FPRegister(const CPURegister& other) - : CPURegister(other.code(), other.size(), other.type()) { - VIXL_ASSERT(IsValidFPRegister()); - } - FPRegister(unsigned code, unsigned size) - : CPURegister(code, size, kFPRegister) {} - - bool IsValid() const { - VIXL_ASSERT(IsFPRegister() || IsNone()); - return IsValidFPRegister(); - } - - static const FPRegister& SRegFromCode(unsigned code); - static const FPRegister& DRegFromCode(unsigned code); - - private: - static const FPRegister sregisters[]; - static const FPRegister dregisters[]; -}; - - -// No*Reg is used to indicate an unused argument, or an error case. Note that -// these all compare equal (using the Is() method). The Register and FPRegister -// variants are provided for convenience. -const Register NoReg; -const FPRegister NoFPReg; -const CPURegister NoCPUReg; - - -#define DEFINE_REGISTERS(N) \ -const Register w##N(N, kWRegSize); \ -const Register x##N(N, kXRegSize); -REGISTER_CODE_LIST(DEFINE_REGISTERS) -#undef DEFINE_REGISTERS -const Register wsp(kSPRegInternalCode, kWRegSize); -const Register sp(kSPRegInternalCode, kXRegSize); - - -#define DEFINE_FPREGISTERS(N) \ -const FPRegister s##N(N, kSRegSize); \ -const FPRegister d##N(N, kDRegSize); -REGISTER_CODE_LIST(DEFINE_FPREGISTERS) -#undef DEFINE_FPREGISTERS - - -// Registers aliases. -const Register ip0 = x16; -const Register ip1 = x17; -const Register lr = x30; -const Register xzr = x31; -const Register wzr = w31; - - -// AreAliased returns true if any of the named registers overlap. Arguments -// set to NoReg are ignored. The system stack pointer may be specified. -bool AreAliased(const CPURegister& reg1, - const CPURegister& reg2, - const CPURegister& reg3 = NoReg, - const CPURegister& reg4 = NoReg, - const CPURegister& reg5 = NoReg, - const CPURegister& reg6 = NoReg, - const CPURegister& reg7 = NoReg, - const CPURegister& reg8 = NoReg); - - -// AreSameSizeAndType returns true if all of the specified registers have the -// same size, and are of the same type. The system stack pointer may be -// specified. Arguments set to NoReg are ignored, as are any subsequent -// arguments. At least one argument (reg1) must be valid (not NoCPUReg). -bool AreSameSizeAndType(const CPURegister& reg1, - const CPURegister& reg2, - const CPURegister& reg3 = NoCPUReg, - const CPURegister& reg4 = NoCPUReg, - const CPURegister& reg5 = NoCPUReg, - const CPURegister& reg6 = NoCPUReg, - const CPURegister& reg7 = NoCPUReg, - const CPURegister& reg8 = NoCPUReg); - - -// Lists of registers. -class CPURegList { - public: - explicit CPURegList(CPURegister reg1, - CPURegister reg2 = NoCPUReg, - CPURegister reg3 = NoCPUReg, - CPURegister reg4 = NoCPUReg) - : list_(reg1.Bit() | reg2.Bit() | reg3.Bit() | reg4.Bit()), - size_(reg1.size()), type_(reg1.type()) { - VIXL_ASSERT(AreSameSizeAndType(reg1, reg2, reg3, reg4)); - VIXL_ASSERT(IsValid()); - } - - CPURegList(CPURegister::RegisterType type, unsigned size, RegList list) - : list_(list), size_(size), type_(type) { - VIXL_ASSERT(IsValid()); - } - - CPURegList(CPURegister::RegisterType type, unsigned size, - unsigned first_reg, unsigned last_reg) - : size_(size), type_(type) { - VIXL_ASSERT(((type == CPURegister::kRegister) && - (last_reg < kNumberOfRegisters)) || - ((type == CPURegister::kFPRegister) && - (last_reg < kNumberOfFPRegisters))); - VIXL_ASSERT(last_reg >= first_reg); - list_ = (UINT64_C(1) << (last_reg + 1)) - 1; - list_ &= ~((UINT64_C(1) << first_reg) - 1); - VIXL_ASSERT(IsValid()); - } - - CPURegister::RegisterType type() const { - VIXL_ASSERT(IsValid()); - return type_; - } - - // Combine another CPURegList into this one. Registers that already exist in - // this list are left unchanged. The type and size of the registers in the - // 'other' list must match those in this list. - void Combine(const CPURegList& other) { - VIXL_ASSERT(IsValid()); - VIXL_ASSERT(other.type() == type_); - VIXL_ASSERT(other.RegisterSizeInBits() == size_); - list_ |= other.list(); - } - - // Remove every register in the other CPURegList from this one. Registers that - // do not exist in this list are ignored. The type and size of the registers - // in the 'other' list must match those in this list. - void Remove(const CPURegList& other) { - VIXL_ASSERT(IsValid()); - VIXL_ASSERT(other.type() == type_); - VIXL_ASSERT(other.RegisterSizeInBits() == size_); - list_ &= ~other.list(); - } - - // Variants of Combine and Remove which take a single register. - void Combine(const CPURegister& other) { - VIXL_ASSERT(other.type() == type_); - VIXL_ASSERT(other.size() == size_); - Combine(other.code()); - } - - void Remove(const CPURegister& other) { - VIXL_ASSERT(other.type() == type_); - VIXL_ASSERT(other.size() == size_); - Remove(other.code()); - } - - // Variants of Combine and Remove which take a single register by its code; - // the type and size of the register is inferred from this list. - void Combine(int code) { - VIXL_ASSERT(IsValid()); - VIXL_ASSERT(CPURegister(code, size_, type_).IsValid()); - list_ |= (UINT64_C(1) << code); - } - - void Remove(int code) { - VIXL_ASSERT(IsValid()); - VIXL_ASSERT(CPURegister(code, size_, type_).IsValid()); - list_ &= ~(UINT64_C(1) << code); - } - - static CPURegList Union(const CPURegList& list_1, const CPURegList& list_2) { - VIXL_ASSERT(list_1.type_ == list_2.type_); - VIXL_ASSERT(list_1.size_ == list_2.size_); - return CPURegList(list_1.type_, list_1.size_, list_1.list_ | list_2.list_); - } - static CPURegList Union(const CPURegList& list_1, - const CPURegList& list_2, - const CPURegList& list_3); - static CPURegList Union(const CPURegList& list_1, - const CPURegList& list_2, - const CPURegList& list_3, - const CPURegList& list_4); - - static CPURegList Intersection(const CPURegList& list_1, - const CPURegList& list_2) { - VIXL_ASSERT(list_1.type_ == list_2.type_); - VIXL_ASSERT(list_1.size_ == list_2.size_); - return CPURegList(list_1.type_, list_1.size_, list_1.list_ & list_2.list_); - } - static CPURegList Intersection(const CPURegList& list_1, - const CPURegList& list_2, - const CPURegList& list_3); - static CPURegList Intersection(const CPURegList& list_1, - const CPURegList& list_2, - const CPURegList& list_3, - const CPURegList& list_4); - - RegList list() const { - VIXL_ASSERT(IsValid()); - return list_; - } - - void set_list(RegList new_list) { - VIXL_ASSERT(IsValid()); - list_ = new_list; - } - - // Remove all callee-saved registers from the list. This can be useful when - // preparing registers for an AAPCS64 function call, for example. - void RemoveCalleeSaved(); - - CPURegister PopLowestIndex(); - CPURegister PopHighestIndex(); - - // AAPCS64 callee-saved registers. - static CPURegList GetCalleeSaved(unsigned size = kXRegSize); - static CPURegList GetCalleeSavedFP(unsigned size = kDRegSize); - - // AAPCS64 caller-saved registers. Note that this includes lr. - static CPURegList GetCallerSaved(unsigned size = kXRegSize); - static CPURegList GetCallerSavedFP(unsigned size = kDRegSize); - - bool IsEmpty() const { - VIXL_ASSERT(IsValid()); - return list_ == 0; - } - - bool IncludesAliasOf(const CPURegister& other) const { - VIXL_ASSERT(IsValid()); - return (type_ == other.type()) && ((other.Bit() & list_) != 0); - } - - bool IncludesAliasOf(int code) const { - VIXL_ASSERT(IsValid()); - return ((code & list_) != 0); - } - - int Count() const { - VIXL_ASSERT(IsValid()); - return CountSetBits(list_, kRegListSizeInBits); - } - - unsigned RegisterSizeInBits() const { - VIXL_ASSERT(IsValid()); - return size_; - } - - unsigned RegisterSizeInBytes() const { - int size_in_bits = RegisterSizeInBits(); - VIXL_ASSERT((size_in_bits % 8) == 0); - return size_in_bits / 8; - } - - unsigned TotalSizeInBytes() const { - VIXL_ASSERT(IsValid()); - return RegisterSizeInBytes() * Count(); - } - - private: - RegList list_; - unsigned size_; - CPURegister::RegisterType type_; - - bool IsValid() const; -}; - - -// AAPCS64 callee-saved registers. -extern const CPURegList kCalleeSaved; -extern const CPURegList kCalleeSavedFP; - - -// AAPCS64 caller-saved registers. Note that this includes lr. -extern const CPURegList kCallerSaved; -extern const CPURegList kCallerSavedFP; - - -// Operand. -class Operand { - public: - // # - // where is int64_t. - // This is allowed to be an implicit constructor because Operand is - // a wrapper class that doesn't normally perform any type conversion. - Operand(int64_t immediate); // NOLINT(runtime/explicit) - - // rm, { #} - // where is one of {LSL, LSR, ASR, ROR}. - // is uint6_t. - // This is allowed to be an implicit constructor because Operand is - // a wrapper class that doesn't normally perform any type conversion. - Operand(Register reg, - Shift shift = LSL, - unsigned shift_amount = 0); // NOLINT(runtime/explicit) - - // rm, { {#}} - // where is one of {UXTB, UXTH, UXTW, UXTX, SXTB, SXTH, SXTW, SXTX}. - // is uint2_t. - explicit Operand(Register reg, Extend extend, unsigned shift_amount = 0); - - bool IsImmediate() const; - bool IsShiftedRegister() const; - bool IsExtendedRegister() const; - bool IsZero() const; - - // This returns an LSL shift (<= 4) operand as an equivalent extend operand, - // which helps in the encoding of instructions that use the stack pointer. - Operand ToExtendedRegister() const; - - int64_t immediate() const { - VIXL_ASSERT(IsImmediate()); - return immediate_; - } - - Register reg() const { - VIXL_ASSERT(IsShiftedRegister() || IsExtendedRegister()); - return reg_; - } - - Shift shift() const { - VIXL_ASSERT(IsShiftedRegister()); - return shift_; - } - - Extend extend() const { - VIXL_ASSERT(IsExtendedRegister()); - return extend_; - } - - unsigned shift_amount() const { - VIXL_ASSERT(IsShiftedRegister() || IsExtendedRegister()); - return shift_amount_; - } - - private: - int64_t immediate_; - Register reg_; - Shift shift_; - Extend extend_; - unsigned shift_amount_; -}; - - -// MemOperand represents the addressing mode of a load or store instruction. -class MemOperand { - public: - explicit MemOperand(Register base, - int64_t offset = 0, - AddrMode addrmode = Offset); - explicit MemOperand(Register base, - Register regoffset, - Shift shift = LSL, - unsigned shift_amount = 0); - explicit MemOperand(Register base, - Register regoffset, - Extend extend, - unsigned shift_amount = 0); - explicit MemOperand(Register base, - const Operand& offset, - AddrMode addrmode = Offset); - - const Register& base() const { return base_; } - const Register& regoffset() const { return regoffset_; } - int64_t offset() const { return offset_; } - AddrMode addrmode() const { return addrmode_; } - Shift shift() const { return shift_; } - Extend extend() const { return extend_; } - unsigned shift_amount() const { return shift_amount_; } - bool IsImmediateOffset() const; - bool IsRegisterOffset() const; - bool IsPreIndex() const; - bool IsPostIndex() const; - - private: - Register base_; - Register regoffset_; - int64_t offset_; - AddrMode addrmode_; - Shift shift_; - Extend extend_; - unsigned shift_amount_; -}; - - -class Label { - public: - Label() : location_(kLocationUnbound) {} - ~Label() { - // If the label has been linked to, it needs to be bound to a target. - VIXL_ASSERT(!IsLinked() || IsBound()); - } - - bool IsBound() const { return location_ >= 0; } - bool IsLinked() const { return !links_.empty(); } - - ptrdiff_t location() const { return location_; } - - private: - // The list of linked instructions is stored in a stack-like structure. We - // don't use std::stack directly because it's slow for the common case where - // only one or two instructions refer to a label, and labels themselves are - // short-lived. This class behaves like std::stack, but the first few links - // are preallocated (configured by kPreallocatedLinks). - // - // If more than N links are required, this falls back to std::stack. - class LinksStack { - public: - LinksStack() : size_(0), links_extended_(NULL) {} - ~LinksStack() { - delete links_extended_; - } - - size_t size() const { - return size_; - } - - bool empty() const { - return size_ == 0; - } - - void push(ptrdiff_t value) { - if (size_ < kPreallocatedLinks) { - links_[size_] = value; - } else { - if (links_extended_ == NULL) { - links_extended_ = new std::stack(); - } - VIXL_ASSERT(size_ == (links_extended_->size() + kPreallocatedLinks)); - links_extended_->push(value); - } - size_++; - } - - ptrdiff_t top() const { - return (size_ <= kPreallocatedLinks) ? links_[size_ - 1] - : links_extended_->top(); - } - - void pop() { - size_--; - if (size_ >= kPreallocatedLinks) { - links_extended_->pop(); - VIXL_ASSERT(size_ == (links_extended_->size() + kPreallocatedLinks)); - } - } - - private: - static const size_t kPreallocatedLinks = 4; - - size_t size_; - ptrdiff_t links_[kPreallocatedLinks]; - std::stack * links_extended_; - }; - - void Bind(ptrdiff_t location) { - // Labels can only be bound once. - VIXL_ASSERT(!IsBound()); - location_ = location; - } - - void AddLink(ptrdiff_t instruction) { - // If a label is bound, the assembler already has the information it needs - // to write the instruction, so there is no need to add it to links_. - VIXL_ASSERT(!IsBound()); - links_.push(instruction); - } - - ptrdiff_t GetAndRemoveNextLink() { - VIXL_ASSERT(IsLinked()); - ptrdiff_t link = links_.top(); - links_.pop(); - return link; - } - - // The offsets of the instructions that have linked to this label. - LinksStack links_; - // The label location. - ptrdiff_t location_; - - static const ptrdiff_t kLocationUnbound = -1; - - // It is not safe to copy labels, so disable the copy constructor by declaring - // it private (without an implementation). - Label(const Label&); - - // The Assembler class is responsible for binding and linking labels, since - // the stored offsets need to be consistent with the Assembler's buffer. - friend class Assembler; -}; - - -// A literal is a 32-bit or 64-bit piece of data stored in the instruction -// stream and loaded through a pc relative load. The same literal can be -// referred to by multiple instructions but a literal can only reside at one -// place in memory. A literal can be used by a load before or after being -// placed in memory. -// -// Internally an offset of 0 is associated with a literal which has been -// neither used nor placed. Then two possibilities arise: -// 1) the label is placed, the offset (stored as offset + 1) is used to -// resolve any subsequent load using the label. -// 2) the label is not placed and offset is the offset of the last load using -// the literal (stored as -offset -1). If multiple loads refer to this -// literal then the last load holds the offset of the preceding load and -// all loads form a chain. Once the offset is placed all the loads in the -// chain are resolved and future loads fall back to possibility 1. -class RawLiteral { - public: - RawLiteral() : size_(0), offset_(0), raw_value_(0) {} - - size_t size() { - VIXL_STATIC_ASSERT(kDRegSizeInBytes == kXRegSizeInBytes); - VIXL_STATIC_ASSERT(kSRegSizeInBytes == kWRegSizeInBytes); - VIXL_ASSERT((size_ == kXRegSizeInBytes) || (size_ == kWRegSizeInBytes)); - return size_; - } - uint64_t raw_value64() { - VIXL_ASSERT(size_ == kXRegSizeInBytes); - return raw_value_; - } - uint32_t raw_value32() { - VIXL_ASSERT(size_ == kWRegSizeInBytes); - VIXL_ASSERT(is_uint32(raw_value_) || is_int32(raw_value_)); - return static_cast(raw_value_); - } - bool IsUsed() { return offset_ < 0; } - bool IsPlaced() { return offset_ > 0; } - - protected: - ptrdiff_t offset() { - VIXL_ASSERT(IsPlaced()); - return offset_ - 1; - } - void set_offset(ptrdiff_t offset) { - VIXL_ASSERT(offset >= 0); - VIXL_ASSERT(IsWordAligned(offset)); - VIXL_ASSERT(!IsPlaced()); - offset_ = offset + 1; - } - ptrdiff_t last_use() { - VIXL_ASSERT(IsUsed()); - return -offset_ - 1; - } - void set_last_use(ptrdiff_t offset) { - VIXL_ASSERT(offset >= 0); - VIXL_ASSERT(IsWordAligned(offset)); - VIXL_ASSERT(!IsPlaced()); - offset_ = -offset - 1; - } - - size_t size_; - ptrdiff_t offset_; - uint64_t raw_value_; - - friend class Assembler; -}; - - -template -class Literal : public RawLiteral { - public: - explicit Literal(T value) { - size_ = sizeof(value); - memcpy(&raw_value_, &value, sizeof(value)); - } -}; - - -// Control whether or not position-independent code should be emitted. -enum PositionIndependentCodeOption { - // All code generated will be position-independent; all branches and - // references to labels generated with the Label class will use PC-relative - // addressing. - PositionIndependentCode, - - // Allow VIXL to generate code that refers to absolute addresses. With this - // option, it will not be possible to copy the code buffer and run it from a - // different address; code must be generated in its final location. - PositionDependentCode, - - // Allow VIXL to assume that the bottom 12 bits of the address will be - // constant, but that the top 48 bits may change. This allows `adrp` to - // function in systems which copy code between pages, but otherwise maintain - // 4KB page alignment. - PageOffsetDependentCode -}; - - -// Control how scaled- and unscaled-offset loads and stores are generated. -enum LoadStoreScalingOption { - // Prefer scaled-immediate-offset instructions, but emit unscaled-offset, - // register-offset, pre-index or post-index instructions if necessary. - PreferScaledOffset, - - // Prefer unscaled-immediate-offset instructions, but emit scaled-offset, - // register-offset, pre-index or post-index instructions if necessary. - PreferUnscaledOffset, - - // Require scaled-immediate-offset instructions. - RequireScaledOffset, - - // Require unscaled-immediate-offset instructions. - RequireUnscaledOffset -}; - - -// Assembler. -class Assembler { - public: - Assembler(size_t capacity, - PositionIndependentCodeOption pic = PositionIndependentCode); - Assembler(byte* buffer, size_t capacity, - PositionIndependentCodeOption pic = PositionIndependentCode); - - // The destructor asserts that one of the following is true: - // * The Assembler object has not been used. - // * Nothing has been emitted since the last Reset() call. - // * Nothing has been emitted since the last FinalizeCode() call. - ~Assembler(); - - // System functions. - - // Start generating code from the beginning of the buffer, discarding any code - // and data that has already been emitted into the buffer. - void Reset(); - - // Finalize a code buffer of generated instructions. This function must be - // called before executing or copying code from the buffer. - void FinalizeCode(); - - // Label. - // Bind a label to the current PC. - void bind(Label* label); - - // Bind a label to a specified offset from the start of the buffer. - void BindToOffset(Label* label, ptrdiff_t offset); - - // Place a literal at the current PC. - void place(RawLiteral* literal); - - ptrdiff_t CursorOffset() const { - return buffer_->CursorOffset(); - } - - ptrdiff_t BufferEndOffset() const { - return static_cast(buffer_->capacity()); - } - - // Return the address of an offset in the buffer. - template - T GetOffsetAddress(ptrdiff_t offset) { - VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); - return buffer_->GetOffsetAddress(offset); - } - - // Return the address of a bound label. - template - T GetLabelAddress(const Label * label) { - VIXL_ASSERT(label->IsBound()); - VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); - return GetOffsetAddress(label->location()); - } - - // Return the address of the cursor. - template - T GetCursorAddress() { - VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); - return GetOffsetAddress(CursorOffset()); - } - - // Return the address of the start of the buffer. - template - T GetStartAddress() { - VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); - return GetOffsetAddress(0); - } - - // Instruction set functions. - - // Branch / Jump instructions. - // Branch to register. - void br(const Register& xn); - - // Branch with link to register. - void blr(const Register& xn); - - // Branch to register with return hint. - void ret(const Register& xn = lr); - - // Unconditional branch to label. - void b(Label* label); - - // Conditional branch to label. - void b(Label* label, Condition cond); - - // Unconditional branch to PC offset. - void b(int imm26); - - // Conditional branch to PC offset. - void b(int imm19, Condition cond); - - // Branch with link to label. - void bl(Label* label); - - // Branch with link to PC offset. - void bl(int imm26); - - // Compare and branch to label if zero. - void cbz(const Register& rt, Label* label); - - // Compare and branch to PC offset if zero. - void cbz(const Register& rt, int imm19); - - // Compare and branch to label if not zero. - void cbnz(const Register& rt, Label* label); - - // Compare and branch to PC offset if not zero. - void cbnz(const Register& rt, int imm19); - - // Test bit and branch to label if zero. - void tbz(const Register& rt, unsigned bit_pos, Label* label); - - // Test bit and branch to PC offset if zero. - void tbz(const Register& rt, unsigned bit_pos, int imm14); - - // Test bit and branch to label if not zero. - void tbnz(const Register& rt, unsigned bit_pos, Label* label); - - // Test bit and branch to PC offset if not zero. - void tbnz(const Register& rt, unsigned bit_pos, int imm14); - - // Address calculation instructions. - // Calculate a PC-relative address. Unlike for branches the offset in adr is - // unscaled (i.e. the result can be unaligned). - - // Calculate the address of a label. - void adr(const Register& rd, Label* label); - - // Calculate the address of a PC offset. - void adr(const Register& rd, int imm21); - - // Calculate the page address of a label. - void adrp(const Register& rd, Label* label); - - // Calculate the page address of a PC offset. - void adrp(const Register& rd, int imm21); - - // Data Processing instructions. - // Add. - void add(const Register& rd, - const Register& rn, - const Operand& operand); - - // Add and update status flags. - void adds(const Register& rd, - const Register& rn, - const Operand& operand); - - // Compare negative. - void cmn(const Register& rn, const Operand& operand); - - // Subtract. - void sub(const Register& rd, - const Register& rn, - const Operand& operand); - - // Subtract and update status flags. - void subs(const Register& rd, - const Register& rn, - const Operand& operand); - - // Compare. - void cmp(const Register& rn, const Operand& operand); - - // Negate. - void neg(const Register& rd, - const Operand& operand); - - // Negate and update status flags. - void negs(const Register& rd, - const Operand& operand); - - // Add with carry bit. - void adc(const Register& rd, - const Register& rn, - const Operand& operand); - - // Add with carry bit and update status flags. - void adcs(const Register& rd, - const Register& rn, - const Operand& operand); - - // Subtract with carry bit. - void sbc(const Register& rd, - const Register& rn, - const Operand& operand); - - // Subtract with carry bit and update status flags. - void sbcs(const Register& rd, - const Register& rn, - const Operand& operand); - - // Negate with carry bit. - void ngc(const Register& rd, - const Operand& operand); - - // Negate with carry bit and update status flags. - void ngcs(const Register& rd, - const Operand& operand); - - // Logical instructions. - // Bitwise and (A & B). - void and_(const Register& rd, - const Register& rn, - const Operand& operand); - - // Bitwise and (A & B) and update status flags. - void ands(const Register& rd, - const Register& rn, - const Operand& operand); - - // Bit test and set flags. - void tst(const Register& rn, const Operand& operand); - - // Bit clear (A & ~B). - void bic(const Register& rd, - const Register& rn, - const Operand& operand); - - // Bit clear (A & ~B) and update status flags. - void bics(const Register& rd, - const Register& rn, - const Operand& operand); - - // Bitwise or (A | B). - void orr(const Register& rd, const Register& rn, const Operand& operand); - - // Bitwise nor (A | ~B). - void orn(const Register& rd, const Register& rn, const Operand& operand); - - // Bitwise eor/xor (A ^ B). - void eor(const Register& rd, const Register& rn, const Operand& operand); - - // Bitwise enor/xnor (A ^ ~B). - void eon(const Register& rd, const Register& rn, const Operand& operand); - - // Logical shift left by variable. - void lslv(const Register& rd, const Register& rn, const Register& rm); - - // Logical shift right by variable. - void lsrv(const Register& rd, const Register& rn, const Register& rm); - - // Arithmetic shift right by variable. - void asrv(const Register& rd, const Register& rn, const Register& rm); - - // Rotate right by variable. - void rorv(const Register& rd, const Register& rn, const Register& rm); - - // Bitfield instructions. - // Bitfield move. - void bfm(const Register& rd, - const Register& rn, - unsigned immr, - unsigned imms); - - // Signed bitfield move. - void sbfm(const Register& rd, - const Register& rn, - unsigned immr, - unsigned imms); - - // Unsigned bitfield move. - void ubfm(const Register& rd, - const Register& rn, - unsigned immr, - unsigned imms); - - // Bfm aliases. - // Bitfield insert. - void bfi(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - VIXL_ASSERT(width >= 1); - VIXL_ASSERT(lsb + width <= rn.size()); - bfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); - } - - // Bitfield extract and insert low. - void bfxil(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - VIXL_ASSERT(width >= 1); - VIXL_ASSERT(lsb + width <= rn.size()); - bfm(rd, rn, lsb, lsb + width - 1); - } - - // Sbfm aliases. - // Arithmetic shift right. - void asr(const Register& rd, const Register& rn, unsigned shift) { - VIXL_ASSERT(shift < rd.size()); - sbfm(rd, rn, shift, rd.size() - 1); - } - - // Signed bitfield insert with zero at right. - void sbfiz(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - VIXL_ASSERT(width >= 1); - VIXL_ASSERT(lsb + width <= rn.size()); - sbfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); - } - - // Signed bitfield extract. - void sbfx(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - VIXL_ASSERT(width >= 1); - VIXL_ASSERT(lsb + width <= rn.size()); - sbfm(rd, rn, lsb, lsb + width - 1); - } - - // Signed extend byte. - void sxtb(const Register& rd, const Register& rn) { - sbfm(rd, rn, 0, 7); - } - - // Signed extend halfword. - void sxth(const Register& rd, const Register& rn) { - sbfm(rd, rn, 0, 15); - } - - // Signed extend word. - void sxtw(const Register& rd, const Register& rn) { - sbfm(rd, rn, 0, 31); - } - - // Ubfm aliases. - // Logical shift left. - void lsl(const Register& rd, const Register& rn, unsigned shift) { - unsigned reg_size = rd.size(); - VIXL_ASSERT(shift < reg_size); - ubfm(rd, rn, (reg_size - shift) % reg_size, reg_size - shift - 1); - } - - // Logical shift right. - void lsr(const Register& rd, const Register& rn, unsigned shift) { - VIXL_ASSERT(shift < rd.size()); - ubfm(rd, rn, shift, rd.size() - 1); - } - - // Unsigned bitfield insert with zero at right. - void ubfiz(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - VIXL_ASSERT(width >= 1); - VIXL_ASSERT(lsb + width <= rn.size()); - ubfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); - } - - // Unsigned bitfield extract. - void ubfx(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - VIXL_ASSERT(width >= 1); - VIXL_ASSERT(lsb + width <= rn.size()); - ubfm(rd, rn, lsb, lsb + width - 1); - } - - // Unsigned extend byte. - void uxtb(const Register& rd, const Register& rn) { - ubfm(rd, rn, 0, 7); - } - - // Unsigned extend halfword. - void uxth(const Register& rd, const Register& rn) { - ubfm(rd, rn, 0, 15); - } - - // Unsigned extend word. - void uxtw(const Register& rd, const Register& rn) { - ubfm(rd, rn, 0, 31); - } - - // Extract. - void extr(const Register& rd, - const Register& rn, - const Register& rm, - unsigned lsb); - - // Conditional select: rd = cond ? rn : rm. - void csel(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond); - - // Conditional select increment: rd = cond ? rn : rm + 1. - void csinc(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond); - - // Conditional select inversion: rd = cond ? rn : ~rm. - void csinv(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond); - - // Conditional select negation: rd = cond ? rn : -rm. - void csneg(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond); - - // Conditional set: rd = cond ? 1 : 0. - void cset(const Register& rd, Condition cond); - - // Conditional set mask: rd = cond ? -1 : 0. - void csetm(const Register& rd, Condition cond); - - // Conditional increment: rd = cond ? rn + 1 : rn. - void cinc(const Register& rd, const Register& rn, Condition cond); - - // Conditional invert: rd = cond ? ~rn : rn. - void cinv(const Register& rd, const Register& rn, Condition cond); - - // Conditional negate: rd = cond ? -rn : rn. - void cneg(const Register& rd, const Register& rn, Condition cond); - - // Rotate right. - void ror(const Register& rd, const Register& rs, unsigned shift) { - extr(rd, rs, rs, shift); - } - - // Conditional comparison. - // Conditional compare negative. - void ccmn(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond); - - // Conditional compare. - void ccmp(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond); - - // Multiply. - void mul(const Register& rd, const Register& rn, const Register& rm); - - // Negated multiply. - void mneg(const Register& rd, const Register& rn, const Register& rm); - - // Signed long multiply: 32 x 32 -> 64-bit. - void smull(const Register& rd, const Register& rn, const Register& rm); - - // Signed multiply high: 64 x 64 -> 64-bit <127:64>. - void smulh(const Register& xd, const Register& xn, const Register& xm); - - // Multiply and accumulate. - void madd(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Multiply and subtract. - void msub(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Signed long multiply and accumulate: 32 x 32 + 64 -> 64-bit. - void smaddl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Unsigned long multiply and accumulate: 32 x 32 + 64 -> 64-bit. - void umaddl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Signed long multiply and subtract: 64 - (32 x 32) -> 64-bit. - void smsubl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Unsigned long multiply and subtract: 64 - (32 x 32) -> 64-bit. - void umsubl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Signed integer divide. - void sdiv(const Register& rd, const Register& rn, const Register& rm); - - // Unsigned integer divide. - void udiv(const Register& rd, const Register& rn, const Register& rm); - - // Bit reverse. - void rbit(const Register& rd, const Register& rn); - - // Reverse bytes in 16-bit half words. - void rev16(const Register& rd, const Register& rn); - - // Reverse bytes in 32-bit words. - void rev32(const Register& rd, const Register& rn); - - // Reverse bytes. - void rev(const Register& rd, const Register& rn); - - // Count leading zeroes. - void clz(const Register& rd, const Register& rn); - - // Count leading sign bits. - void cls(const Register& rd, const Register& rn); - - // Memory instructions. - // Load integer or FP register. - void ldr(const CPURegister& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferScaledOffset); - - // Store integer or FP register. - void str(const CPURegister& rt, const MemOperand& dst, - LoadStoreScalingOption option = PreferScaledOffset); - - // Load word with sign extension. - void ldrsw(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferScaledOffset); - - // Load byte. - void ldrb(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferScaledOffset); - - // Store byte. - void strb(const Register& rt, const MemOperand& dst, - LoadStoreScalingOption option = PreferScaledOffset); - - // Load byte with sign extension. - void ldrsb(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferScaledOffset); - - // Load half-word. - void ldrh(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferScaledOffset); - - // Store half-word. - void strh(const Register& rt, const MemOperand& dst, - LoadStoreScalingOption option = PreferScaledOffset); - - // Load half-word with sign extension. - void ldrsh(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferScaledOffset); - - // Load integer or FP register (with unscaled offset). - void ldur(const CPURegister& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Store integer or FP register (with unscaled offset). - void stur(const CPURegister& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Load word with sign extension. - void ldursw(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Load byte (with unscaled offset). - void ldurb(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Store byte (with unscaled offset). - void sturb(const Register& rt, const MemOperand& dst, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Load byte with sign extension (and unscaled offset). - void ldursb(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Load half-word (with unscaled offset). - void ldurh(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Store half-word (with unscaled offset). - void sturh(const Register& rt, const MemOperand& dst, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Load half-word with sign extension (and unscaled offset). - void ldursh(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Load integer or FP register pair. - void ldp(const CPURegister& rt, const CPURegister& rt2, - const MemOperand& src); - - // Store integer or FP register pair. - void stp(const CPURegister& rt, const CPURegister& rt2, - const MemOperand& dst); - - // Load word pair with sign extension. - void ldpsw(const Register& rt, const Register& rt2, const MemOperand& src); - - // Load integer or FP register pair, non-temporal. - void ldnp(const CPURegister& rt, const CPURegister& rt2, - const MemOperand& src); - - // Store integer or FP register pair, non-temporal. - void stnp(const CPURegister& rt, const CPURegister& rt2, - const MemOperand& dst); - - // Load integer or FP register from literal pool. - void ldr(const CPURegister& rt, RawLiteral* literal); - - // Load word with sign extension from literal pool. - void ldrsw(const Register& rt, RawLiteral* literal); - - // Load integer or FP register from pc + imm19 << 2. - void ldr(const CPURegister& rt, int imm19); - - // Load word with sign extension from pc + imm19 << 2. - void ldrsw(const Register& rt, int imm19); - - // Store exclusive byte. - void stxrb(const Register& rs, const Register& rt, const MemOperand& dst); - - // Store exclusive half-word. - void stxrh(const Register& rs, const Register& rt, const MemOperand& dst); - - // Store exclusive register. - void stxr(const Register& rs, const Register& rt, const MemOperand& dst); - - // Load exclusive byte. - void ldxrb(const Register& rt, const MemOperand& src); - - // Load exclusive half-word. - void ldxrh(const Register& rt, const MemOperand& src); - - // Load exclusive register. - void ldxr(const Register& rt, const MemOperand& src); - - // Store exclusive register pair. - void stxp(const Register& rs, - const Register& rt, - const Register& rt2, - const MemOperand& dst); - - // Load exclusive register pair. - void ldxp(const Register& rt, const Register& rt2, const MemOperand& src); - - // Store-release exclusive byte. - void stlxrb(const Register& rs, const Register& rt, const MemOperand& dst); - - // Store-release exclusive half-word. - void stlxrh(const Register& rs, const Register& rt, const MemOperand& dst); - - // Store-release exclusive register. - void stlxr(const Register& rs, const Register& rt, const MemOperand& dst); - - // Load-acquire exclusive byte. - void ldaxrb(const Register& rt, const MemOperand& src); - - // Load-acquire exclusive half-word. - void ldaxrh(const Register& rt, const MemOperand& src); - - // Load-acquire exclusive register. - void ldaxr(const Register& rt, const MemOperand& src); - - // Store-release exclusive register pair. - void stlxp(const Register& rs, - const Register& rt, - const Register& rt2, - const MemOperand& dst); - - // Load-acquire exclusive register pair. - void ldaxp(const Register& rt, const Register& rt2, const MemOperand& src); - - // Store-release byte. - void stlrb(const Register& rt, const MemOperand& dst); - - // Store-release half-word. - void stlrh(const Register& rt, const MemOperand& dst); - - // Store-release register. - void stlr(const Register& rt, const MemOperand& dst); - - // Load-acquire byte. - void ldarb(const Register& rt, const MemOperand& src); - - // Load-acquire half-word. - void ldarh(const Register& rt, const MemOperand& src); - - // Load-acquire register. - void ldar(const Register& rt, const MemOperand& src); - - // Prefetch memory. - void prfm(PrefetchOperation op, const MemOperand& addr, - LoadStoreScalingOption option = PreferScaledOffset); - - // Prefetch memory (with unscaled offset). - void prfum(PrefetchOperation op, const MemOperand& addr, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Prefetch memory in the literal pool. - void prfm(PrefetchOperation op, RawLiteral* literal); - - // Prefetch from pc + imm19 << 2. - void prfm(PrefetchOperation op, int imm19); - - // Move instructions. The default shift of -1 indicates that the move - // instruction will calculate an appropriate 16-bit immediate and left shift - // that is equal to the 64-bit immediate argument. If an explicit left shift - // is specified (0, 16, 32 or 48), the immediate must be a 16-bit value. - // - // For movk, an explicit shift can be used to indicate which half word should - // be overwritten, eg. movk(x0, 0, 0) will overwrite the least-significant - // half word with zero, whereas movk(x0, 0, 48) will overwrite the - // most-significant. - - // Move immediate and keep. - void movk(const Register& rd, uint64_t imm, int shift = -1) { - MoveWide(rd, imm, shift, MOVK); - } - - // Move inverted immediate. - void movn(const Register& rd, uint64_t imm, int shift = -1) { - MoveWide(rd, imm, shift, MOVN); - } - - // Move immediate. - void movz(const Register& rd, uint64_t imm, int shift = -1) { - MoveWide(rd, imm, shift, MOVZ); - } - - // Misc instructions. - // Monitor debug-mode breakpoint. - void brk(int code); - - // Halting debug-mode breakpoint. - void hlt(int code); - - // Move register to register. - void mov(const Register& rd, const Register& rn); - - // Move inverted operand to register. - void mvn(const Register& rd, const Operand& operand); - - // System instructions. - // Move to register from system register. - void mrs(const Register& rt, SystemRegister sysreg); - - // Move from register to system register. - void msr(SystemRegister sysreg, const Register& rt); - - // System hint. - void hint(SystemHint code); - - // Clear exclusive monitor. - void clrex(int imm4 = 0xf); - - // Data memory barrier. - void dmb(BarrierDomain domain, BarrierType type); - - // Data synchronization barrier. - void dsb(BarrierDomain domain, BarrierType type); - - // Instruction synchronization barrier. - void isb(); - - // Alias for system instructions. - // No-op. - void nop() { - hint(NOP); - } - - // FP instructions. - // Move double precision immediate to FP register. - void fmov(const FPRegister& fd, double imm); - - // Move single precision immediate to FP register. - void fmov(const FPRegister& fd, float imm); - - // Move FP register to register. - void fmov(const Register& rd, const FPRegister& fn); - - // Move register to FP register. - void fmov(const FPRegister& fd, const Register& rn); - - // Move FP register to FP register. - void fmov(const FPRegister& fd, const FPRegister& fn); - - // FP add. - void fadd(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); - - // FP subtract. - void fsub(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); - - // FP multiply. - void fmul(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); - - // FP fused multiply and add. - void fmadd(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa); - - // FP fused multiply and subtract. - void fmsub(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa); - - // FP fused multiply, add and negate. - void fnmadd(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa); - - // FP fused multiply, subtract and negate. - void fnmsub(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa); - - // FP divide. - void fdiv(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); - - // FP maximum. - void fmax(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); - - // FP minimum. - void fmin(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); - - // FP maximum number. - void fmaxnm(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); - - // FP minimum number. - void fminnm(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); - - // FP absolute. - void fabs(const FPRegister& fd, const FPRegister& fn); - - // FP negate. - void fneg(const FPRegister& fd, const FPRegister& fn); - - // FP square root. - void fsqrt(const FPRegister& fd, const FPRegister& fn); - - // FP round to integer (nearest with ties to away). - void frinta(const FPRegister& fd, const FPRegister& fn); - - // FP round to integer (implicit rounding). - void frinti(const FPRegister& fd, const FPRegister& fn); - - // FP round to integer (toward minus infinity). - void frintm(const FPRegister& fd, const FPRegister& fn); - - // FP round to integer (nearest with ties to even). - void frintn(const FPRegister& fd, const FPRegister& fn); - - // FP round to integer (toward plus infinity). - void frintp(const FPRegister& fd, const FPRegister& fn); - - // FP round to integer (exact, implicit rounding). - void frintx(const FPRegister& fd, const FPRegister& fn); - - // FP round to integer (towards zero). - void frintz(const FPRegister& fd, const FPRegister& fn); - - // FP compare registers. - void fcmp(const FPRegister& fn, const FPRegister& fm); - - // FP compare immediate. - void fcmp(const FPRegister& fn, double value); - - // FP conditional compare. - void fccmp(const FPRegister& fn, - const FPRegister& fm, - StatusFlags nzcv, - Condition cond); - - // FP conditional select. - void fcsel(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - Condition cond); - - // Common FP Convert function. - void FPConvertToInt(const Register& rd, - const FPRegister& fn, - FPIntegerConvertOp op); - - // FP convert between single and double precision. - void fcvt(const FPRegister& fd, const FPRegister& fn); - - // Convert FP to signed integer (nearest with ties to away). - void fcvtas(const Register& rd, const FPRegister& fn); - - // Convert FP to unsigned integer (nearest with ties to away). - void fcvtau(const Register& rd, const FPRegister& fn); - - // Convert FP to signed integer (round towards -infinity). - void fcvtms(const Register& rd, const FPRegister& fn); - - // Convert FP to unsigned integer (round towards -infinity). - void fcvtmu(const Register& rd, const FPRegister& fn); - - // Convert FP to signed integer (nearest with ties to even). - void fcvtns(const Register& rd, const FPRegister& fn); - - // Convert FP to unsigned integer (nearest with ties to even). - void fcvtnu(const Register& rd, const FPRegister& fn); - - // Convert FP to signed integer (round towards zero). - void fcvtzs(const Register& rd, const FPRegister& fn); - - // Convert FP to unsigned integer (round towards zero). - void fcvtzu(const Register& rd, const FPRegister& fn); - - // Convert signed integer or fixed point to FP. - void scvtf(const FPRegister& fd, const Register& rn, unsigned fbits = 0); - - // Convert unsigned integer or fixed point to FP. - void ucvtf(const FPRegister& fd, const Register& rn, unsigned fbits = 0); - - // Emit generic instructions. - // Emit raw instructions into the instruction stream. - void dci(Instr raw_inst) { Emit(raw_inst); } - - // Emit 32 bits of data into the instruction stream. - void dc32(uint32_t data) { - VIXL_ASSERT(buffer_monitor_ > 0); - buffer_->Emit32(data); - } - - // Emit 64 bits of data into the instruction stream. - void dc64(uint64_t data) { - VIXL_ASSERT(buffer_monitor_ > 0); - buffer_->Emit64(data); - } - - // Copy a string into the instruction stream, including the terminating NULL - // character. The instruction pointer is then aligned correctly for - // subsequent instructions. - void EmitString(const char * string) { - VIXL_ASSERT(string != NULL); - VIXL_ASSERT(buffer_monitor_ > 0); - - buffer_->EmitString(string); - buffer_->Align(); - } - - // Code generation helpers. - - // Register encoding. - static Instr Rd(CPURegister rd) { - VIXL_ASSERT(rd.code() != kSPRegInternalCode); - return rd.code() << Rd_offset; - } - - static Instr Rn(CPURegister rn) { - VIXL_ASSERT(rn.code() != kSPRegInternalCode); - return rn.code() << Rn_offset; - } - - static Instr Rm(CPURegister rm) { - VIXL_ASSERT(rm.code() != kSPRegInternalCode); - return rm.code() << Rm_offset; - } - - static Instr Ra(CPURegister ra) { - VIXL_ASSERT(ra.code() != kSPRegInternalCode); - return ra.code() << Ra_offset; - } - - static Instr Rt(CPURegister rt) { - VIXL_ASSERT(rt.code() != kSPRegInternalCode); - return rt.code() << Rt_offset; - } - - static Instr Rt2(CPURegister rt2) { - VIXL_ASSERT(rt2.code() != kSPRegInternalCode); - return rt2.code() << Rt2_offset; - } - - static Instr Rs(CPURegister rs) { - VIXL_ASSERT(rs.code() != kSPRegInternalCode); - return rs.code() << Rs_offset; - } - - // These encoding functions allow the stack pointer to be encoded, and - // disallow the zero register. - static Instr RdSP(Register rd) { - VIXL_ASSERT(!rd.IsZero()); - return (rd.code() & kRegCodeMask) << Rd_offset; - } - - static Instr RnSP(Register rn) { - VIXL_ASSERT(!rn.IsZero()); - return (rn.code() & kRegCodeMask) << Rn_offset; - } - - // Flags encoding. - static Instr Flags(FlagsUpdate S) { - if (S == SetFlags) { - return 1 << FlagsUpdate_offset; - } else if (S == LeaveFlags) { - return 0 << FlagsUpdate_offset; - } - VIXL_UNREACHABLE(); - return 0; - } - - static Instr Cond(Condition cond) { - return cond << Condition_offset; - } - - // PC-relative address encoding. - static Instr ImmPCRelAddress(int imm21) { - VIXL_ASSERT(is_int21(imm21)); - Instr imm = static_cast(truncate_to_int21(imm21)); - Instr immhi = (imm >> ImmPCRelLo_width) << ImmPCRelHi_offset; - Instr immlo = imm << ImmPCRelLo_offset; - return (immhi & ImmPCRelHi_mask) | (immlo & ImmPCRelLo_mask); - } - - // Branch encoding. - static Instr ImmUncondBranch(int imm26) { - VIXL_ASSERT(is_int26(imm26)); - return truncate_to_int26(imm26) << ImmUncondBranch_offset; - } - - static Instr ImmCondBranch(int imm19) { - VIXL_ASSERT(is_int19(imm19)); - return truncate_to_int19(imm19) << ImmCondBranch_offset; - } - - static Instr ImmCmpBranch(int imm19) { - VIXL_ASSERT(is_int19(imm19)); - return truncate_to_int19(imm19) << ImmCmpBranch_offset; - } - - static Instr ImmTestBranch(int imm14) { - VIXL_ASSERT(is_int14(imm14)); - return truncate_to_int14(imm14) << ImmTestBranch_offset; - } - - static Instr ImmTestBranchBit(unsigned bit_pos) { - VIXL_ASSERT(is_uint6(bit_pos)); - // Subtract five from the shift offset, as we need bit 5 from bit_pos. - unsigned b5 = bit_pos << (ImmTestBranchBit5_offset - 5); - unsigned b40 = bit_pos << ImmTestBranchBit40_offset; - b5 &= ImmTestBranchBit5_mask; - b40 &= ImmTestBranchBit40_mask; - return b5 | b40; - } - - // Data Processing encoding. - static Instr SF(Register rd) { - return rd.Is64Bits() ? SixtyFourBits : ThirtyTwoBits; - } - - static Instr ImmAddSub(int64_t imm) { - VIXL_ASSERT(IsImmAddSub(imm)); - if (is_uint12(imm)) { // No shift required. - return imm << ImmAddSub_offset; - } else { - return ((imm >> 12) << ImmAddSub_offset) | (1 << ShiftAddSub_offset); - } - } - - static Instr ImmS(unsigned imms, unsigned reg_size) { - VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(imms)) || - ((reg_size == kWRegSize) && is_uint5(imms))); - USE(reg_size); - return imms << ImmS_offset; - } - - static Instr ImmR(unsigned immr, unsigned reg_size) { - VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || - ((reg_size == kWRegSize) && is_uint5(immr))); - USE(reg_size); - VIXL_ASSERT(is_uint6(immr)); - return immr << ImmR_offset; - } - - static Instr ImmSetBits(unsigned imms, unsigned reg_size) { - VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); - VIXL_ASSERT(is_uint6(imms)); - VIXL_ASSERT((reg_size == kXRegSize) || is_uint6(imms + 3)); - USE(reg_size); - return imms << ImmSetBits_offset; - } - - static Instr ImmRotate(unsigned immr, unsigned reg_size) { - VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); - VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || - ((reg_size == kWRegSize) && is_uint5(immr))); - USE(reg_size); - return immr << ImmRotate_offset; - } - - static Instr ImmLLiteral(int imm19) { - VIXL_ASSERT(is_int19(imm19)); - return truncate_to_int19(imm19) << ImmLLiteral_offset; - } - - static Instr BitN(unsigned bitn, unsigned reg_size) { - VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); - VIXL_ASSERT((reg_size == kXRegSize) || (bitn == 0)); - USE(reg_size); - return bitn << BitN_offset; - } - - static Instr ShiftDP(Shift shift) { - VIXL_ASSERT(shift == LSL || shift == LSR || shift == ASR || shift == ROR); - return shift << ShiftDP_offset; - } - - static Instr ImmDPShift(unsigned amount) { - VIXL_ASSERT(is_uint6(amount)); - return amount << ImmDPShift_offset; - } - - static Instr ExtendMode(Extend extend) { - return extend << ExtendMode_offset; - } - - static Instr ImmExtendShift(unsigned left_shift) { - VIXL_ASSERT(left_shift <= 4); - return left_shift << ImmExtendShift_offset; - } - - static Instr ImmCondCmp(unsigned imm) { - VIXL_ASSERT(is_uint5(imm)); - return imm << ImmCondCmp_offset; - } - - static Instr Nzcv(StatusFlags nzcv) { - return ((nzcv >> Flags_offset) & 0xf) << Nzcv_offset; - } - - // MemOperand offset encoding. - static Instr ImmLSUnsigned(int imm12) { - VIXL_ASSERT(is_uint12(imm12)); - return imm12 << ImmLSUnsigned_offset; - } - - static Instr ImmLS(int imm9) { - VIXL_ASSERT(is_int9(imm9)); - return truncate_to_int9(imm9) << ImmLS_offset; - } - - static Instr ImmLSPair(int imm7, LSDataSize size) { - VIXL_ASSERT(((imm7 >> size) << size) == imm7); - int scaled_imm7 = imm7 >> size; - VIXL_ASSERT(is_int7(scaled_imm7)); - return truncate_to_int7(scaled_imm7) << ImmLSPair_offset; - } - - static Instr ImmShiftLS(unsigned shift_amount) { - VIXL_ASSERT(is_uint1(shift_amount)); - return shift_amount << ImmShiftLS_offset; - } - - static Instr ImmPrefetchOperation(int imm5) { - VIXL_ASSERT(is_uint5(imm5)); - return imm5 << ImmPrefetchOperation_offset; - } - - static Instr ImmException(int imm16) { - VIXL_ASSERT(is_uint16(imm16)); - return imm16 << ImmException_offset; - } - - static Instr ImmSystemRegister(int imm15) { - VIXL_ASSERT(is_uint15(imm15)); - return imm15 << ImmSystemRegister_offset; - } - - static Instr ImmHint(int imm7) { - VIXL_ASSERT(is_uint7(imm7)); - return imm7 << ImmHint_offset; - } - - static Instr CRm(int imm4) { - VIXL_ASSERT(is_uint4(imm4)); - return imm4 << CRm_offset; - } - - static Instr ImmBarrierDomain(int imm2) { - VIXL_ASSERT(is_uint2(imm2)); - return imm2 << ImmBarrierDomain_offset; - } - - static Instr ImmBarrierType(int imm2) { - VIXL_ASSERT(is_uint2(imm2)); - return imm2 << ImmBarrierType_offset; - } - - static LSDataSize CalcLSDataSize(LoadStoreOp op) { - VIXL_ASSERT((SizeLS_offset + SizeLS_width) == (kInstructionSize * 8)); - return static_cast(op >> SizeLS_offset); - } - - // Move immediates encoding. - static Instr ImmMoveWide(uint64_t imm) { - VIXL_ASSERT(is_uint16(imm)); - return imm << ImmMoveWide_offset; - } - - static Instr ShiftMoveWide(int64_t shift) { - VIXL_ASSERT(is_uint2(shift)); - return shift << ShiftMoveWide_offset; - } - - // FP Immediates. - static Instr ImmFP32(float imm); - static Instr ImmFP64(double imm); - - // FP register type. - static Instr FPType(FPRegister fd) { - return fd.Is64Bits() ? FP64 : FP32; - } - - static Instr FPScale(unsigned scale) { - VIXL_ASSERT(is_uint6(scale)); - return scale << FPScale_offset; - } - - // Immediate field checking helpers. - static bool IsImmAddSub(int64_t immediate); - static bool IsImmConditionalCompare(int64_t immediate); - static bool IsImmFP32(float imm); - static bool IsImmFP64(double imm); - static bool IsImmLogical(uint64_t value, - unsigned width, - unsigned* n = NULL, - unsigned* imm_s = NULL, - unsigned* imm_r = NULL); - static bool IsImmLSPair(int64_t offset, LSDataSize size); - static bool IsImmLSScaled(int64_t offset, LSDataSize size); - static bool IsImmLSUnscaled(int64_t offset); - static bool IsImmMovn(uint64_t imm, unsigned reg_size); - static bool IsImmMovz(uint64_t imm, unsigned reg_size); - - // Size of the code generated since label to the current position. - size_t SizeOfCodeGeneratedSince(Label* label) const { - VIXL_ASSERT(label->IsBound()); - return buffer_->OffsetFrom(label->location()); - } - - size_t SizeOfCodeGenerated() const { - return buffer_->CursorOffset(); - } - - size_t BufferCapacity() const { return buffer_->capacity(); } - - size_t RemainingBufferSpace() const { return buffer_->RemainingBytes(); } - - void EnsureSpaceFor(size_t amount) { - if (buffer_->RemainingBytes() < amount) { - size_t capacity = buffer_->capacity(); - size_t size = buffer_->CursorOffset(); - do { - // TODO(all): refine. - capacity *= 2; - } while ((capacity - size) < amount); - buffer_->Grow(capacity); - } - } - -#ifdef VIXL_DEBUG - void AcquireBuffer() { - VIXL_ASSERT(buffer_monitor_ >= 0); - buffer_monitor_++; - } - - void ReleaseBuffer() { - buffer_monitor_--; - VIXL_ASSERT(buffer_monitor_ >= 0); - } -#endif - - PositionIndependentCodeOption pic() const { - return pic_; - } - - bool AllowPageOffsetDependentCode() const { - return (pic() == PageOffsetDependentCode) || - (pic() == PositionDependentCode); - } - - static const Register& AppropriateZeroRegFor(const CPURegister& reg) { - return reg.Is64Bits() ? xzr : wzr; - } - - - protected: - void LoadStore(const CPURegister& rt, - const MemOperand& addr, - LoadStoreOp op, - LoadStoreScalingOption option = PreferScaledOffset); - - void LoadStorePair(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& addr, - LoadStorePairOp op); - - void Prefetch(PrefetchOperation op, - const MemOperand& addr, - LoadStoreScalingOption option = PreferScaledOffset); - - // TODO(all): The third parameter should be passed by reference but gcc 4.8.2 - // reports a bogus uninitialised warning then. - void Logical(const Register& rd, - const Register& rn, - const Operand operand, - LogicalOp op); - void LogicalImmediate(const Register& rd, - const Register& rn, - unsigned n, - unsigned imm_s, - unsigned imm_r, - LogicalOp op); - - void ConditionalCompare(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond, - ConditionalCompareOp op); - - void AddSubWithCarry(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - AddSubWithCarryOp op); - - - // Functions for emulating operands not directly supported by the instruction - // set. - void EmitShift(const Register& rd, - const Register& rn, - Shift shift, - unsigned amount); - void EmitExtendShift(const Register& rd, - const Register& rn, - Extend extend, - unsigned left_shift); - - void AddSub(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - AddSubOp op); - - // Find an appropriate LoadStoreOp or LoadStorePairOp for the specified - // registers. Only simple loads are supported; sign- and zero-extension (such - // as in LDPSW_x or LDRB_w) are not supported. - static LoadStoreOp LoadOpFor(const CPURegister& rt); - static LoadStorePairOp LoadPairOpFor(const CPURegister& rt, - const CPURegister& rt2); - static LoadStoreOp StoreOpFor(const CPURegister& rt); - static LoadStorePairOp StorePairOpFor(const CPURegister& rt, - const CPURegister& rt2); - static LoadStorePairNonTemporalOp LoadPairNonTemporalOpFor( - const CPURegister& rt, const CPURegister& rt2); - static LoadStorePairNonTemporalOp StorePairNonTemporalOpFor( - const CPURegister& rt, const CPURegister& rt2); - static LoadLiteralOp LoadLiteralOpFor(const CPURegister& rt); - - - private: - // Instruction helpers. - void MoveWide(const Register& rd, - uint64_t imm, - int shift, - MoveWideImmediateOp mov_op); - void DataProcShiftedRegister(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - Instr op); - void DataProcExtendedRegister(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - Instr op); - void LoadStorePairNonTemporal(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& addr, - LoadStorePairNonTemporalOp op); - void LoadLiteral(const CPURegister& rt, uint64_t imm, LoadLiteralOp op); - void ConditionalSelect(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond, - ConditionalSelectOp op); - void DataProcessing1Source(const Register& rd, - const Register& rn, - DataProcessing1SourceOp op); - void DataProcessing3Source(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra, - DataProcessing3SourceOp op); - void FPDataProcessing1Source(const FPRegister& fd, - const FPRegister& fn, - FPDataProcessing1SourceOp op); - void FPDataProcessing2Source(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - FPDataProcessing2SourceOp op); - void FPDataProcessing3Source(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa, - FPDataProcessing3SourceOp op); - - // Encode the specified MemOperand for the specified access size and scaling - // preference. - Instr LoadStoreMemOperand(const MemOperand& addr, - LSDataSize size, - LoadStoreScalingOption option); - - // Link the current (not-yet-emitted) instruction to the specified label, then - // return an offset to be encoded in the instruction. If the label is not yet - // bound, an offset of 0 is returned. - ptrdiff_t LinkAndGetByteOffsetTo(Label * label); - ptrdiff_t LinkAndGetInstructionOffsetTo(Label * label); - ptrdiff_t LinkAndGetPageOffsetTo(Label * label); - - // A common implementation for the LinkAndGetOffsetTo helpers. - template - ptrdiff_t LinkAndGetOffsetTo(Label* label); - - // Literal load offset are in words (32-bit). - ptrdiff_t LinkAndGetWordOffsetTo(RawLiteral* literal); - - // Emit the instruction in buffer_. - void Emit(Instr instruction) { - VIXL_STATIC_ASSERT(sizeof(instruction) == kInstructionSize); - VIXL_ASSERT(buffer_monitor_ > 0); - buffer_->Emit32(instruction); - } - - // Buffer where the code is emitted. - CodeBuffer* buffer_; - PositionIndependentCodeOption pic_; - -#ifdef VIXL_DEBUG - int64_t buffer_monitor_; -#endif -}; - - -// All Assembler emits MUST acquire/release the underlying code buffer. The -// helper scope below will do so and optionally ensure the buffer is big enough -// to receive the emit. It is possible to request the scope not to perform any -// checks (kNoCheck) if for example it is known in advance the buffer size is -// adequate or there is some other size checking mechanism in place. -class CodeBufferCheckScope { - public: - // Tell whether or not the scope needs to ensure the associated CodeBuffer - // has enough space for the requested size. - enum CheckPolicy { - kNoCheck, - kCheck - }; - - // Tell whether or not the scope should assert the amount of code emitted - // within the scope is consistent with the requested amount. - enum AssertPolicy { - kNoAssert, // No assert required. - kExactSize, // The code emitted must be exactly size bytes. - kMaximumSize // The code emitted must be at most size bytes. - }; - - CodeBufferCheckScope(Assembler* assm, - size_t size, - CheckPolicy check_policy = kCheck, - AssertPolicy assert_policy = kMaximumSize) - : assm_(assm) { - if (check_policy == kCheck) assm->EnsureSpaceFor(size); -#ifdef VIXL_DEBUG - assm->bind(&start_); - size_ = size; - assert_policy_ = assert_policy; - assm->AcquireBuffer(); -#else - USE(assert_policy); -#endif - } - - // This is a shortcut for CodeBufferCheckScope(assm, 0, kNoCheck, kNoAssert). - explicit CodeBufferCheckScope(Assembler* assm) : assm_(assm) { -#ifdef VIXL_DEBUG - size_ = 0; - assert_policy_ = kNoAssert; - assm->AcquireBuffer(); -#endif - } - - ~CodeBufferCheckScope() { -#ifdef VIXL_DEBUG - assm_->ReleaseBuffer(); - switch (assert_policy_) { - case kNoAssert: break; - case kExactSize: - VIXL_ASSERT(assm_->SizeOfCodeGeneratedSince(&start_) == size_); - break; - case kMaximumSize: - VIXL_ASSERT(assm_->SizeOfCodeGeneratedSince(&start_) <= size_); - break; - default: - VIXL_UNREACHABLE(); - } -#endif - } - - protected: - Assembler* assm_; -#ifdef VIXL_DEBUG - Label start_; - size_t size_; - AssertPolicy assert_policy_; -#endif -}; - -} // namespace vixl - -#endif // VIXL_A64_ASSEMBLER_A64_H_ diff --git a/qemu/disas/libvixl/a64/constants-a64.h b/qemu/disas/libvixl/a64/constants-a64.h deleted file mode 100644 index bc1a2c4b9..000000000 --- a/qemu/disas/libvixl/a64/constants-a64.h +++ /dev/null @@ -1,1213 +0,0 @@ -// Copyright 2013, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef VIXL_A64_CONSTANTS_A64_H_ -#define VIXL_A64_CONSTANTS_A64_H_ - -namespace vixl { - -const unsigned kNumberOfRegisters = 32; -const unsigned kNumberOfFPRegisters = 32; - -#define REGISTER_CODE_LIST(R) \ -R(0) R(1) R(2) R(3) R(4) R(5) R(6) R(7) \ -R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15) \ -R(16) R(17) R(18) R(19) R(20) R(21) R(22) R(23) \ -R(24) R(25) R(26) R(27) R(28) R(29) R(30) R(31) - -#define INSTRUCTION_FIELDS_LIST(V_) \ -/* Register fields */ \ -V_(Rd, 4, 0, Bits) /* Destination register. */ \ -V_(Rn, 9, 5, Bits) /* First source register. */ \ -V_(Rm, 20, 16, Bits) /* Second source register. */ \ -V_(Ra, 14, 10, Bits) /* Third source register. */ \ -V_(Rt, 4, 0, Bits) /* Load/store register. */ \ -V_(Rt2, 14, 10, Bits) /* Load/store second register. */ \ -V_(Rs, 20, 16, Bits) /* Exclusive access status. */ \ - \ -/* Common bits */ \ -V_(SixtyFourBits, 31, 31, Bits) \ -V_(FlagsUpdate, 29, 29, Bits) \ - \ -/* PC relative addressing */ \ -V_(ImmPCRelHi, 23, 5, SignedBits) \ -V_(ImmPCRelLo, 30, 29, Bits) \ - \ -/* Add/subtract/logical shift register */ \ -V_(ShiftDP, 23, 22, Bits) \ -V_(ImmDPShift, 15, 10, Bits) \ - \ -/* Add/subtract immediate */ \ -V_(ImmAddSub, 21, 10, Bits) \ -V_(ShiftAddSub, 23, 22, Bits) \ - \ -/* Add/substract extend */ \ -V_(ImmExtendShift, 12, 10, Bits) \ -V_(ExtendMode, 15, 13, Bits) \ - \ -/* Move wide */ \ -V_(ImmMoveWide, 20, 5, Bits) \ -V_(ShiftMoveWide, 22, 21, Bits) \ - \ -/* Logical immediate, bitfield and extract */ \ -V_(BitN, 22, 22, Bits) \ -V_(ImmRotate, 21, 16, Bits) \ -V_(ImmSetBits, 15, 10, Bits) \ -V_(ImmR, 21, 16, Bits) \ -V_(ImmS, 15, 10, Bits) \ - \ -/* Test and branch immediate */ \ -V_(ImmTestBranch, 18, 5, SignedBits) \ -V_(ImmTestBranchBit40, 23, 19, Bits) \ -V_(ImmTestBranchBit5, 31, 31, Bits) \ - \ -/* Conditionals */ \ -V_(Condition, 15, 12, Bits) \ -V_(ConditionBranch, 3, 0, Bits) \ -V_(Nzcv, 3, 0, Bits) \ -V_(ImmCondCmp, 20, 16, Bits) \ -V_(ImmCondBranch, 23, 5, SignedBits) \ - \ -/* Floating point */ \ -V_(FPType, 23, 22, Bits) \ -V_(ImmFP, 20, 13, Bits) \ -V_(FPScale, 15, 10, Bits) \ - \ -/* Load Store */ \ -V_(ImmLS, 20, 12, SignedBits) \ -V_(ImmLSUnsigned, 21, 10, Bits) \ -V_(ImmLSPair, 21, 15, SignedBits) \ -V_(SizeLS, 31, 30, Bits) \ -V_(ImmShiftLS, 12, 12, Bits) \ -V_(ImmPrefetchOperation, 4, 0, Bits) \ -V_(PrefetchHint, 4, 3, Bits) \ -V_(PrefetchTarget, 2, 1, Bits) \ -V_(PrefetchStream, 0, 0, Bits) \ - \ -/* Other immediates */ \ -V_(ImmUncondBranch, 25, 0, SignedBits) \ -V_(ImmCmpBranch, 23, 5, SignedBits) \ -V_(ImmLLiteral, 23, 5, SignedBits) \ -V_(ImmException, 20, 5, Bits) \ -V_(ImmHint, 11, 5, Bits) \ -V_(ImmBarrierDomain, 11, 10, Bits) \ -V_(ImmBarrierType, 9, 8, Bits) \ - \ -/* System (MRS, MSR) */ \ -V_(ImmSystemRegister, 19, 5, Bits) \ -V_(SysO0, 19, 19, Bits) \ -V_(SysOp1, 18, 16, Bits) \ -V_(SysOp2, 7, 5, Bits) \ -V_(CRn, 15, 12, Bits) \ -V_(CRm, 11, 8, Bits) \ - \ -/* Load-/store-exclusive */ \ -V_(LdStXLoad, 22, 22, Bits) \ -V_(LdStXNotExclusive, 23, 23, Bits) \ -V_(LdStXAcquireRelease, 15, 15, Bits) \ -V_(LdStXSizeLog2, 31, 30, Bits) \ -V_(LdStXPair, 21, 21, Bits) \ - - -#define SYSTEM_REGISTER_FIELDS_LIST(V_, M_) \ -/* NZCV */ \ -V_(Flags, 31, 28, Bits) \ -V_(N, 31, 31, Bits) \ -V_(Z, 30, 30, Bits) \ -V_(C, 29, 29, Bits) \ -V_(V, 28, 28, Bits) \ -M_(NZCV, Flags_mask) \ - \ -/* FPCR */ \ -V_(AHP, 26, 26, Bits) \ -V_(DN, 25, 25, Bits) \ -V_(FZ, 24, 24, Bits) \ -V_(RMode, 23, 22, Bits) \ -M_(FPCR, AHP_mask | DN_mask | FZ_mask | RMode_mask) - - -// Fields offsets. -#define DECLARE_FIELDS_OFFSETS(Name, HighBit, LowBit, X) \ -const int Name##_offset = LowBit; \ -const int Name##_width = HighBit - LowBit + 1; \ -const uint32_t Name##_mask = ((1 << Name##_width) - 1) << LowBit; -#define NOTHING(A, B) -INSTRUCTION_FIELDS_LIST(DECLARE_FIELDS_OFFSETS) -SYSTEM_REGISTER_FIELDS_LIST(DECLARE_FIELDS_OFFSETS, NOTHING) -#undef NOTHING -#undef DECLARE_FIELDS_BITS - -// ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST), formed -// from ImmPCRelLo and ImmPCRelHi. -const int ImmPCRel_mask = ImmPCRelLo_mask | ImmPCRelHi_mask; - -// Condition codes. -enum Condition { - eq = 0, - ne = 1, - hs = 2, - lo = 3, - mi = 4, - pl = 5, - vs = 6, - vc = 7, - hi = 8, - ls = 9, - ge = 10, - lt = 11, - gt = 12, - le = 13, - al = 14, - nv = 15 // Behaves as always/al. -}; - -inline Condition InvertCondition(Condition cond) { - // Conditions al and nv behave identically, as "always true". They can't be - // inverted, because there is no "always false" condition. - VIXL_ASSERT((cond != al) && (cond != nv)); - return static_cast(cond ^ 1); -} - -enum FlagsUpdate { - SetFlags = 1, - LeaveFlags = 0 -}; - -enum StatusFlags { - NoFlag = 0, - - // Derive the flag combinations from the system register bit descriptions. - NFlag = N_mask, - ZFlag = Z_mask, - CFlag = C_mask, - VFlag = V_mask, - NZFlag = NFlag | ZFlag, - NCFlag = NFlag | CFlag, - NVFlag = NFlag | VFlag, - ZCFlag = ZFlag | CFlag, - ZVFlag = ZFlag | VFlag, - CVFlag = CFlag | VFlag, - NZCFlag = NFlag | ZFlag | CFlag, - NZVFlag = NFlag | ZFlag | VFlag, - NCVFlag = NFlag | CFlag | VFlag, - ZCVFlag = ZFlag | CFlag | VFlag, - NZCVFlag = NFlag | ZFlag | CFlag | VFlag, - - // Floating-point comparison results. - FPEqualFlag = ZCFlag, - FPLessThanFlag = NFlag, - FPGreaterThanFlag = CFlag, - FPUnorderedFlag = CVFlag -}; - -enum Shift { - NO_SHIFT = -1, - LSL = 0x0, - LSR = 0x1, - ASR = 0x2, - ROR = 0x3 -}; - -enum Extend { - NO_EXTEND = -1, - UXTB = 0, - UXTH = 1, - UXTW = 2, - UXTX = 3, - SXTB = 4, - SXTH = 5, - SXTW = 6, - SXTX = 7 -}; - -enum SystemHint { - NOP = 0, - YIELD = 1, - WFE = 2, - WFI = 3, - SEV = 4, - SEVL = 5 -}; - -enum BarrierDomain { - OuterShareable = 0, - NonShareable = 1, - InnerShareable = 2, - FullSystem = 3 -}; - -enum BarrierType { - BarrierOther = 0, - BarrierReads = 1, - BarrierWrites = 2, - BarrierAll = 3 -}; - -enum PrefetchOperation { - PLDL1KEEP = 0x00, - PLDL1STRM = 0x01, - PLDL2KEEP = 0x02, - PLDL2STRM = 0x03, - PLDL3KEEP = 0x04, - PLDL3STRM = 0x05, - - PLIL1KEEP = 0x08, - PLIL1STRM = 0x09, - PLIL2KEEP = 0x0a, - PLIL2STRM = 0x0b, - PLIL3KEEP = 0x0c, - PLIL3STRM = 0x0d, - - PSTL1KEEP = 0x10, - PSTL1STRM = 0x11, - PSTL2KEEP = 0x12, - PSTL2STRM = 0x13, - PSTL3KEEP = 0x14, - PSTL3STRM = 0x15 -}; - -// System/special register names. -// This information is not encoded as one field but as the concatenation of -// multiple fields (Op0<0>, Op1, Crn, Crm, Op2). -enum SystemRegister { - NZCV = ((0x1 << SysO0_offset) | - (0x3 << SysOp1_offset) | - (0x4 << CRn_offset) | - (0x2 << CRm_offset) | - (0x0 << SysOp2_offset)) >> ImmSystemRegister_offset, - FPCR = ((0x1 << SysO0_offset) | - (0x3 << SysOp1_offset) | - (0x4 << CRn_offset) | - (0x4 << CRm_offset) | - (0x0 << SysOp2_offset)) >> ImmSystemRegister_offset -}; - -// Instruction enumerations. -// -// These are the masks that define a class of instructions, and the list of -// instructions within each class. Each enumeration has a Fixed, FMask and -// Mask value. -// -// Fixed: The fixed bits in this instruction class. -// FMask: The mask used to extract the fixed bits in the class. -// Mask: The mask used to identify the instructions within a class. -// -// The enumerations can be used like this: -// -// VIXL_ASSERT(instr->Mask(PCRelAddressingFMask) == PCRelAddressingFixed); -// switch(instr->Mask(PCRelAddressingMask)) { -// case ADR: Format("adr 'Xd, 'AddrPCRelByte"); break; -// case ADRP: Format("adrp 'Xd, 'AddrPCRelPage"); break; -// default: printf("Unknown instruction\n"); -// } - - -// Generic fields. -enum GenericInstrField { - SixtyFourBits = 0x80000000, - ThirtyTwoBits = 0x00000000, - FP32 = 0x00000000, - FP64 = 0x00400000 -}; - -// PC relative addressing. -enum PCRelAddressingOp { - PCRelAddressingFixed = 0x10000000, - PCRelAddressingFMask = 0x1F000000, - PCRelAddressingMask = 0x9F000000, - ADR = PCRelAddressingFixed | 0x00000000, - ADRP = PCRelAddressingFixed | 0x80000000 -}; - -// Add/sub (immediate, shifted and extended.) -const int kSFOffset = 31; -enum AddSubOp { - AddSubOpMask = 0x60000000, - AddSubSetFlagsBit = 0x20000000, - ADD = 0x00000000, - ADDS = ADD | AddSubSetFlagsBit, - SUB = 0x40000000, - SUBS = SUB | AddSubSetFlagsBit -}; - -#define ADD_SUB_OP_LIST(V) \ - V(ADD), \ - V(ADDS), \ - V(SUB), \ - V(SUBS) - -enum AddSubImmediateOp { - AddSubImmediateFixed = 0x11000000, - AddSubImmediateFMask = 0x1F000000, - AddSubImmediateMask = 0xFF000000, - #define ADD_SUB_IMMEDIATE(A) \ - A##_w_imm = AddSubImmediateFixed | A, \ - A##_x_imm = AddSubImmediateFixed | A | SixtyFourBits - ADD_SUB_OP_LIST(ADD_SUB_IMMEDIATE) - #undef ADD_SUB_IMMEDIATE -}; - -enum AddSubShiftedOp { - AddSubShiftedFixed = 0x0B000000, - AddSubShiftedFMask = 0x1F200000, - AddSubShiftedMask = 0xFF200000, - #define ADD_SUB_SHIFTED(A) \ - A##_w_shift = AddSubShiftedFixed | A, \ - A##_x_shift = AddSubShiftedFixed | A | SixtyFourBits - ADD_SUB_OP_LIST(ADD_SUB_SHIFTED) - #undef ADD_SUB_SHIFTED -}; - -enum AddSubExtendedOp { - AddSubExtendedFixed = 0x0B200000, - AddSubExtendedFMask = 0x1F200000, - AddSubExtendedMask = 0xFFE00000, - #define ADD_SUB_EXTENDED(A) \ - A##_w_ext = AddSubExtendedFixed | A, \ - A##_x_ext = AddSubExtendedFixed | A | SixtyFourBits - ADD_SUB_OP_LIST(ADD_SUB_EXTENDED) - #undef ADD_SUB_EXTENDED -}; - -// Add/sub with carry. -enum AddSubWithCarryOp { - AddSubWithCarryFixed = 0x1A000000, - AddSubWithCarryFMask = 0x1FE00000, - AddSubWithCarryMask = 0xFFE0FC00, - ADC_w = AddSubWithCarryFixed | ADD, - ADC_x = AddSubWithCarryFixed | ADD | SixtyFourBits, - ADC = ADC_w, - ADCS_w = AddSubWithCarryFixed | ADDS, - ADCS_x = AddSubWithCarryFixed | ADDS | SixtyFourBits, - SBC_w = AddSubWithCarryFixed | SUB, - SBC_x = AddSubWithCarryFixed | SUB | SixtyFourBits, - SBC = SBC_w, - SBCS_w = AddSubWithCarryFixed | SUBS, - SBCS_x = AddSubWithCarryFixed | SUBS | SixtyFourBits -}; - - -// Logical (immediate and shifted register). -enum LogicalOp { - LogicalOpMask = 0x60200000, - NOT = 0x00200000, - AND = 0x00000000, - BIC = AND | NOT, - ORR = 0x20000000, - ORN = ORR | NOT, - EOR = 0x40000000, - EON = EOR | NOT, - ANDS = 0x60000000, - BICS = ANDS | NOT -}; - -// Logical immediate. -enum LogicalImmediateOp { - LogicalImmediateFixed = 0x12000000, - LogicalImmediateFMask = 0x1F800000, - LogicalImmediateMask = 0xFF800000, - AND_w_imm = LogicalImmediateFixed | AND, - AND_x_imm = LogicalImmediateFixed | AND | SixtyFourBits, - ORR_w_imm = LogicalImmediateFixed | ORR, - ORR_x_imm = LogicalImmediateFixed | ORR | SixtyFourBits, - EOR_w_imm = LogicalImmediateFixed | EOR, - EOR_x_imm = LogicalImmediateFixed | EOR | SixtyFourBits, - ANDS_w_imm = LogicalImmediateFixed | ANDS, - ANDS_x_imm = LogicalImmediateFixed | ANDS | SixtyFourBits -}; - -// Logical shifted register. -enum LogicalShiftedOp { - LogicalShiftedFixed = 0x0A000000, - LogicalShiftedFMask = 0x1F000000, - LogicalShiftedMask = 0xFF200000, - AND_w = LogicalShiftedFixed | AND, - AND_x = LogicalShiftedFixed | AND | SixtyFourBits, - AND_shift = AND_w, - BIC_w = LogicalShiftedFixed | BIC, - BIC_x = LogicalShiftedFixed | BIC | SixtyFourBits, - BIC_shift = BIC_w, - ORR_w = LogicalShiftedFixed | ORR, - ORR_x = LogicalShiftedFixed | ORR | SixtyFourBits, - ORR_shift = ORR_w, - ORN_w = LogicalShiftedFixed | ORN, - ORN_x = LogicalShiftedFixed | ORN | SixtyFourBits, - ORN_shift = ORN_w, - EOR_w = LogicalShiftedFixed | EOR, - EOR_x = LogicalShiftedFixed | EOR | SixtyFourBits, - EOR_shift = EOR_w, - EON_w = LogicalShiftedFixed | EON, - EON_x = LogicalShiftedFixed | EON | SixtyFourBits, - EON_shift = EON_w, - ANDS_w = LogicalShiftedFixed | ANDS, - ANDS_x = LogicalShiftedFixed | ANDS | SixtyFourBits, - ANDS_shift = ANDS_w, - BICS_w = LogicalShiftedFixed | BICS, - BICS_x = LogicalShiftedFixed | BICS | SixtyFourBits, - BICS_shift = BICS_w -}; - -// Move wide immediate. -enum MoveWideImmediateOp { - MoveWideImmediateFixed = 0x12800000, - MoveWideImmediateFMask = 0x1F800000, - MoveWideImmediateMask = 0xFF800000, - MOVN = 0x00000000, - MOVZ = 0x40000000, - MOVK = 0x60000000, - MOVN_w = MoveWideImmediateFixed | MOVN, - MOVN_x = MoveWideImmediateFixed | MOVN | SixtyFourBits, - MOVZ_w = MoveWideImmediateFixed | MOVZ, - MOVZ_x = MoveWideImmediateFixed | MOVZ | SixtyFourBits, - MOVK_w = MoveWideImmediateFixed | MOVK, - MOVK_x = MoveWideImmediateFixed | MOVK | SixtyFourBits -}; - -// Bitfield. -const int kBitfieldNOffset = 22; -enum BitfieldOp { - BitfieldFixed = 0x13000000, - BitfieldFMask = 0x1F800000, - BitfieldMask = 0xFF800000, - SBFM_w = BitfieldFixed | 0x00000000, - SBFM_x = BitfieldFixed | 0x80000000, - SBFM = SBFM_w, - BFM_w = BitfieldFixed | 0x20000000, - BFM_x = BitfieldFixed | 0xA0000000, - BFM = BFM_w, - UBFM_w = BitfieldFixed | 0x40000000, - UBFM_x = BitfieldFixed | 0xC0000000, - UBFM = UBFM_w - // Bitfield N field. -}; - -// Extract. -enum ExtractOp { - ExtractFixed = 0x13800000, - ExtractFMask = 0x1F800000, - ExtractMask = 0xFFA00000, - EXTR_w = ExtractFixed | 0x00000000, - EXTR_x = ExtractFixed | 0x80000000, - EXTR = EXTR_w -}; - -// Unconditional branch. -enum UnconditionalBranchOp { - UnconditionalBranchFixed = 0x14000000, - UnconditionalBranchFMask = 0x7C000000, - UnconditionalBranchMask = 0xFC000000, - B = UnconditionalBranchFixed | 0x00000000, - BL = UnconditionalBranchFixed | 0x80000000 -}; - -// Unconditional branch to register. -enum UnconditionalBranchToRegisterOp { - UnconditionalBranchToRegisterFixed = 0xD6000000, - UnconditionalBranchToRegisterFMask = 0xFE000000, - UnconditionalBranchToRegisterMask = 0xFFFFFC1F, - BR = UnconditionalBranchToRegisterFixed | 0x001F0000, - BLR = UnconditionalBranchToRegisterFixed | 0x003F0000, - RET = UnconditionalBranchToRegisterFixed | 0x005F0000 -}; - -// Compare and branch. -enum CompareBranchOp { - CompareBranchFixed = 0x34000000, - CompareBranchFMask = 0x7E000000, - CompareBranchMask = 0xFF000000, - CBZ_w = CompareBranchFixed | 0x00000000, - CBZ_x = CompareBranchFixed | 0x80000000, - CBZ = CBZ_w, - CBNZ_w = CompareBranchFixed | 0x01000000, - CBNZ_x = CompareBranchFixed | 0x81000000, - CBNZ = CBNZ_w -}; - -// Test and branch. -enum TestBranchOp { - TestBranchFixed = 0x36000000, - TestBranchFMask = 0x7E000000, - TestBranchMask = 0x7F000000, - TBZ = TestBranchFixed | 0x00000000, - TBNZ = TestBranchFixed | 0x01000000 -}; - -// Conditional branch. -enum ConditionalBranchOp { - ConditionalBranchFixed = 0x54000000, - ConditionalBranchFMask = 0xFE000000, - ConditionalBranchMask = 0xFF000010, - B_cond = ConditionalBranchFixed | 0x00000000 -}; - -// System. -// System instruction encoding is complicated because some instructions use op -// and CR fields to encode parameters. To handle this cleanly, the system -// instructions are split into more than one enum. - -enum SystemOp { - SystemFixed = 0xD5000000, - SystemFMask = 0xFFC00000 -}; - -enum SystemSysRegOp { - SystemSysRegFixed = 0xD5100000, - SystemSysRegFMask = 0xFFD00000, - SystemSysRegMask = 0xFFF00000, - MRS = SystemSysRegFixed | 0x00200000, - MSR = SystemSysRegFixed | 0x00000000 -}; - -enum SystemHintOp { - SystemHintFixed = 0xD503201F, - SystemHintFMask = 0xFFFFF01F, - SystemHintMask = 0xFFFFF01F, - HINT = SystemHintFixed | 0x00000000 -}; - -// Exception. -enum ExceptionOp { - ExceptionFixed = 0xD4000000, - ExceptionFMask = 0xFF000000, - ExceptionMask = 0xFFE0001F, - HLT = ExceptionFixed | 0x00400000, - BRK = ExceptionFixed | 0x00200000, - SVC = ExceptionFixed | 0x00000001, - HVC = ExceptionFixed | 0x00000002, - SMC = ExceptionFixed | 0x00000003, - DCPS1 = ExceptionFixed | 0x00A00001, - DCPS2 = ExceptionFixed | 0x00A00002, - DCPS3 = ExceptionFixed | 0x00A00003 -}; - -enum MemBarrierOp { - MemBarrierFixed = 0xD503309F, - MemBarrierFMask = 0xFFFFF09F, - MemBarrierMask = 0xFFFFF0FF, - DSB = MemBarrierFixed | 0x00000000, - DMB = MemBarrierFixed | 0x00000020, - ISB = MemBarrierFixed | 0x00000040 -}; - -enum SystemExclusiveMonitorOp { - SystemExclusiveMonitorFixed = 0xD503305F, - SystemExclusiveMonitorFMask = 0xFFFFF0FF, - SystemExclusiveMonitorMask = 0xFFFFF0FF, - CLREX = SystemExclusiveMonitorFixed -}; - -// Any load or store. -enum LoadStoreAnyOp { - LoadStoreAnyFMask = 0x0a000000, - LoadStoreAnyFixed = 0x08000000 -}; - -// Any load pair or store pair. -enum LoadStorePairAnyOp { - LoadStorePairAnyFMask = 0x3a000000, - LoadStorePairAnyFixed = 0x28000000 -}; - -#define LOAD_STORE_PAIR_OP_LIST(V) \ - V(STP, w, 0x00000000), \ - V(LDP, w, 0x00400000), \ - V(LDPSW, x, 0x40400000), \ - V(STP, x, 0x80000000), \ - V(LDP, x, 0x80400000), \ - V(STP, s, 0x04000000), \ - V(LDP, s, 0x04400000), \ - V(STP, d, 0x44000000), \ - V(LDP, d, 0x44400000) - -// Load/store pair (post, pre and offset.) -enum LoadStorePairOp { - LoadStorePairMask = 0xC4400000, - LoadStorePairLBit = 1 << 22, - #define LOAD_STORE_PAIR(A, B, C) \ - A##_##B = C - LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR) - #undef LOAD_STORE_PAIR -}; - -enum LoadStorePairPostIndexOp { - LoadStorePairPostIndexFixed = 0x28800000, - LoadStorePairPostIndexFMask = 0x3B800000, - LoadStorePairPostIndexMask = 0xFFC00000, - #define LOAD_STORE_PAIR_POST_INDEX(A, B, C) \ - A##_##B##_post = LoadStorePairPostIndexFixed | A##_##B - LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_POST_INDEX) - #undef LOAD_STORE_PAIR_POST_INDEX -}; - -enum LoadStorePairPreIndexOp { - LoadStorePairPreIndexFixed = 0x29800000, - LoadStorePairPreIndexFMask = 0x3B800000, - LoadStorePairPreIndexMask = 0xFFC00000, - #define LOAD_STORE_PAIR_PRE_INDEX(A, B, C) \ - A##_##B##_pre = LoadStorePairPreIndexFixed | A##_##B - LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_PRE_INDEX) - #undef LOAD_STORE_PAIR_PRE_INDEX -}; - -enum LoadStorePairOffsetOp { - LoadStorePairOffsetFixed = 0x29000000, - LoadStorePairOffsetFMask = 0x3B800000, - LoadStorePairOffsetMask = 0xFFC00000, - #define LOAD_STORE_PAIR_OFFSET(A, B, C) \ - A##_##B##_off = LoadStorePairOffsetFixed | A##_##B - LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_OFFSET) - #undef LOAD_STORE_PAIR_OFFSET -}; - -enum LoadStorePairNonTemporalOp { - LoadStorePairNonTemporalFixed = 0x28000000, - LoadStorePairNonTemporalFMask = 0x3B800000, - LoadStorePairNonTemporalMask = 0xFFC00000, - STNP_w = LoadStorePairNonTemporalFixed | STP_w, - LDNP_w = LoadStorePairNonTemporalFixed | LDP_w, - STNP_x = LoadStorePairNonTemporalFixed | STP_x, - LDNP_x = LoadStorePairNonTemporalFixed | LDP_x, - STNP_s = LoadStorePairNonTemporalFixed | STP_s, - LDNP_s = LoadStorePairNonTemporalFixed | LDP_s, - STNP_d = LoadStorePairNonTemporalFixed | STP_d, - LDNP_d = LoadStorePairNonTemporalFixed | LDP_d -}; - -// Load literal. -enum LoadLiteralOp { - LoadLiteralFixed = 0x18000000, - LoadLiteralFMask = 0x3B000000, - LoadLiteralMask = 0xFF000000, - LDR_w_lit = LoadLiteralFixed | 0x00000000, - LDR_x_lit = LoadLiteralFixed | 0x40000000, - LDRSW_x_lit = LoadLiteralFixed | 0x80000000, - PRFM_lit = LoadLiteralFixed | 0xC0000000, - LDR_s_lit = LoadLiteralFixed | 0x04000000, - LDR_d_lit = LoadLiteralFixed | 0x44000000 -}; - -#define LOAD_STORE_OP_LIST(V) \ - V(ST, RB, w, 0x00000000), \ - V(ST, RH, w, 0x40000000), \ - V(ST, R, w, 0x80000000), \ - V(ST, R, x, 0xC0000000), \ - V(LD, RB, w, 0x00400000), \ - V(LD, RH, w, 0x40400000), \ - V(LD, R, w, 0x80400000), \ - V(LD, R, x, 0xC0400000), \ - V(LD, RSB, x, 0x00800000), \ - V(LD, RSH, x, 0x40800000), \ - V(LD, RSW, x, 0x80800000), \ - V(LD, RSB, w, 0x00C00000), \ - V(LD, RSH, w, 0x40C00000), \ - V(ST, R, s, 0x84000000), \ - V(ST, R, d, 0xC4000000), \ - V(LD, R, s, 0x84400000), \ - V(LD, R, d, 0xC4400000) - - -// Load/store (post, pre, offset and unsigned.) -enum LoadStoreOp { - LoadStoreOpMask = 0xC4C00000, - #define LOAD_STORE(A, B, C, D) \ - A##B##_##C = D - LOAD_STORE_OP_LIST(LOAD_STORE), - #undef LOAD_STORE - PRFM = 0xC0800000 -}; - -// Load/store unscaled offset. -enum LoadStoreUnscaledOffsetOp { - LoadStoreUnscaledOffsetFixed = 0x38000000, - LoadStoreUnscaledOffsetFMask = 0x3B200C00, - LoadStoreUnscaledOffsetMask = 0xFFE00C00, - PRFUM = LoadStoreUnscaledOffsetFixed | PRFM, - #define LOAD_STORE_UNSCALED(A, B, C, D) \ - A##U##B##_##C = LoadStoreUnscaledOffsetFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_UNSCALED) - #undef LOAD_STORE_UNSCALED -}; - -// Load/store post index. -enum LoadStorePostIndex { - LoadStorePostIndexFixed = 0x38000400, - LoadStorePostIndexFMask = 0x3B200C00, - LoadStorePostIndexMask = 0xFFE00C00, - #define LOAD_STORE_POST_INDEX(A, B, C, D) \ - A##B##_##C##_post = LoadStorePostIndexFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_POST_INDEX) - #undef LOAD_STORE_POST_INDEX -}; - -// Load/store pre index. -enum LoadStorePreIndex { - LoadStorePreIndexFixed = 0x38000C00, - LoadStorePreIndexFMask = 0x3B200C00, - LoadStorePreIndexMask = 0xFFE00C00, - #define LOAD_STORE_PRE_INDEX(A, B, C, D) \ - A##B##_##C##_pre = LoadStorePreIndexFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_PRE_INDEX) - #undef LOAD_STORE_PRE_INDEX -}; - -// Load/store unsigned offset. -enum LoadStoreUnsignedOffset { - LoadStoreUnsignedOffsetFixed = 0x39000000, - LoadStoreUnsignedOffsetFMask = 0x3B000000, - LoadStoreUnsignedOffsetMask = 0xFFC00000, - PRFM_unsigned = LoadStoreUnsignedOffsetFixed | PRFM, - #define LOAD_STORE_UNSIGNED_OFFSET(A, B, C, D) \ - A##B##_##C##_unsigned = LoadStoreUnsignedOffsetFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_UNSIGNED_OFFSET) - #undef LOAD_STORE_UNSIGNED_OFFSET -}; - -// Load/store register offset. -enum LoadStoreRegisterOffset { - LoadStoreRegisterOffsetFixed = 0x38200800, - LoadStoreRegisterOffsetFMask = 0x3B200C00, - LoadStoreRegisterOffsetMask = 0xFFE00C00, - PRFM_reg = LoadStoreRegisterOffsetFixed | PRFM, - #define LOAD_STORE_REGISTER_OFFSET(A, B, C, D) \ - A##B##_##C##_reg = LoadStoreRegisterOffsetFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_REGISTER_OFFSET) - #undef LOAD_STORE_REGISTER_OFFSET -}; - -enum LoadStoreExclusive { - LoadStoreExclusiveFixed = 0x08000000, - LoadStoreExclusiveFMask = 0x3F000000, - LoadStoreExclusiveMask = 0xFFE08000, - STXRB_w = LoadStoreExclusiveFixed | 0x00000000, - STXRH_w = LoadStoreExclusiveFixed | 0x40000000, - STXR_w = LoadStoreExclusiveFixed | 0x80000000, - STXR_x = LoadStoreExclusiveFixed | 0xC0000000, - LDXRB_w = LoadStoreExclusiveFixed | 0x00400000, - LDXRH_w = LoadStoreExclusiveFixed | 0x40400000, - LDXR_w = LoadStoreExclusiveFixed | 0x80400000, - LDXR_x = LoadStoreExclusiveFixed | 0xC0400000, - STXP_w = LoadStoreExclusiveFixed | 0x80200000, - STXP_x = LoadStoreExclusiveFixed | 0xC0200000, - LDXP_w = LoadStoreExclusiveFixed | 0x80600000, - LDXP_x = LoadStoreExclusiveFixed | 0xC0600000, - STLXRB_w = LoadStoreExclusiveFixed | 0x00008000, - STLXRH_w = LoadStoreExclusiveFixed | 0x40008000, - STLXR_w = LoadStoreExclusiveFixed | 0x80008000, - STLXR_x = LoadStoreExclusiveFixed | 0xC0008000, - LDAXRB_w = LoadStoreExclusiveFixed | 0x00408000, - LDAXRH_w = LoadStoreExclusiveFixed | 0x40408000, - LDAXR_w = LoadStoreExclusiveFixed | 0x80408000, - LDAXR_x = LoadStoreExclusiveFixed | 0xC0408000, - STLXP_w = LoadStoreExclusiveFixed | 0x80208000, - STLXP_x = LoadStoreExclusiveFixed | 0xC0208000, - LDAXP_w = LoadStoreExclusiveFixed | 0x80608000, - LDAXP_x = LoadStoreExclusiveFixed | 0xC0608000, - STLRB_w = LoadStoreExclusiveFixed | 0x00808000, - STLRH_w = LoadStoreExclusiveFixed | 0x40808000, - STLR_w = LoadStoreExclusiveFixed | 0x80808000, - STLR_x = LoadStoreExclusiveFixed | 0xC0808000, - LDARB_w = LoadStoreExclusiveFixed | 0x00C08000, - LDARH_w = LoadStoreExclusiveFixed | 0x40C08000, - LDAR_w = LoadStoreExclusiveFixed | 0x80C08000, - LDAR_x = LoadStoreExclusiveFixed | 0xC0C08000 -}; - -// Conditional compare. -enum ConditionalCompareOp { - ConditionalCompareMask = 0x60000000, - CCMN = 0x20000000, - CCMP = 0x60000000 -}; - -// Conditional compare register. -enum ConditionalCompareRegisterOp { - ConditionalCompareRegisterFixed = 0x1A400000, - ConditionalCompareRegisterFMask = 0x1FE00800, - ConditionalCompareRegisterMask = 0xFFE00C10, - CCMN_w = ConditionalCompareRegisterFixed | CCMN, - CCMN_x = ConditionalCompareRegisterFixed | SixtyFourBits | CCMN, - CCMP_w = ConditionalCompareRegisterFixed | CCMP, - CCMP_x = ConditionalCompareRegisterFixed | SixtyFourBits | CCMP -}; - -// Conditional compare immediate. -enum ConditionalCompareImmediateOp { - ConditionalCompareImmediateFixed = 0x1A400800, - ConditionalCompareImmediateFMask = 0x1FE00800, - ConditionalCompareImmediateMask = 0xFFE00C10, - CCMN_w_imm = ConditionalCompareImmediateFixed | CCMN, - CCMN_x_imm = ConditionalCompareImmediateFixed | SixtyFourBits | CCMN, - CCMP_w_imm = ConditionalCompareImmediateFixed | CCMP, - CCMP_x_imm = ConditionalCompareImmediateFixed | SixtyFourBits | CCMP -}; - -// Conditional select. -enum ConditionalSelectOp { - ConditionalSelectFixed = 0x1A800000, - ConditionalSelectFMask = 0x1FE00000, - ConditionalSelectMask = 0xFFE00C00, - CSEL_w = ConditionalSelectFixed | 0x00000000, - CSEL_x = ConditionalSelectFixed | 0x80000000, - CSEL = CSEL_w, - CSINC_w = ConditionalSelectFixed | 0x00000400, - CSINC_x = ConditionalSelectFixed | 0x80000400, - CSINC = CSINC_w, - CSINV_w = ConditionalSelectFixed | 0x40000000, - CSINV_x = ConditionalSelectFixed | 0xC0000000, - CSINV = CSINV_w, - CSNEG_w = ConditionalSelectFixed | 0x40000400, - CSNEG_x = ConditionalSelectFixed | 0xC0000400, - CSNEG = CSNEG_w -}; - -// Data processing 1 source. -enum DataProcessing1SourceOp { - DataProcessing1SourceFixed = 0x5AC00000, - DataProcessing1SourceFMask = 0x5FE00000, - DataProcessing1SourceMask = 0xFFFFFC00, - RBIT = DataProcessing1SourceFixed | 0x00000000, - RBIT_w = RBIT, - RBIT_x = RBIT | SixtyFourBits, - REV16 = DataProcessing1SourceFixed | 0x00000400, - REV16_w = REV16, - REV16_x = REV16 | SixtyFourBits, - REV = DataProcessing1SourceFixed | 0x00000800, - REV_w = REV, - REV32_x = REV | SixtyFourBits, - REV_x = DataProcessing1SourceFixed | SixtyFourBits | 0x00000C00, - CLZ = DataProcessing1SourceFixed | 0x00001000, - CLZ_w = CLZ, - CLZ_x = CLZ | SixtyFourBits, - CLS = DataProcessing1SourceFixed | 0x00001400, - CLS_w = CLS, - CLS_x = CLS | SixtyFourBits -}; - -// Data processing 2 source. -enum DataProcessing2SourceOp { - DataProcessing2SourceFixed = 0x1AC00000, - DataProcessing2SourceFMask = 0x5FE00000, - DataProcessing2SourceMask = 0xFFE0FC00, - UDIV_w = DataProcessing2SourceFixed | 0x00000800, - UDIV_x = DataProcessing2SourceFixed | 0x80000800, - UDIV = UDIV_w, - SDIV_w = DataProcessing2SourceFixed | 0x00000C00, - SDIV_x = DataProcessing2SourceFixed | 0x80000C00, - SDIV = SDIV_w, - LSLV_w = DataProcessing2SourceFixed | 0x00002000, - LSLV_x = DataProcessing2SourceFixed | 0x80002000, - LSLV = LSLV_w, - LSRV_w = DataProcessing2SourceFixed | 0x00002400, - LSRV_x = DataProcessing2SourceFixed | 0x80002400, - LSRV = LSRV_w, - ASRV_w = DataProcessing2SourceFixed | 0x00002800, - ASRV_x = DataProcessing2SourceFixed | 0x80002800, - ASRV = ASRV_w, - RORV_w = DataProcessing2SourceFixed | 0x00002C00, - RORV_x = DataProcessing2SourceFixed | 0x80002C00, - RORV = RORV_w, - CRC32B = DataProcessing2SourceFixed | 0x00004000, - CRC32H = DataProcessing2SourceFixed | 0x00004400, - CRC32W = DataProcessing2SourceFixed | 0x00004800, - CRC32X = DataProcessing2SourceFixed | SixtyFourBits | 0x00004C00, - CRC32CB = DataProcessing2SourceFixed | 0x00005000, - CRC32CH = DataProcessing2SourceFixed | 0x00005400, - CRC32CW = DataProcessing2SourceFixed | 0x00005800, - CRC32CX = DataProcessing2SourceFixed | SixtyFourBits | 0x00005C00 -}; - -// Data processing 3 source. -enum DataProcessing3SourceOp { - DataProcessing3SourceFixed = 0x1B000000, - DataProcessing3SourceFMask = 0x1F000000, - DataProcessing3SourceMask = 0xFFE08000, - MADD_w = DataProcessing3SourceFixed | 0x00000000, - MADD_x = DataProcessing3SourceFixed | 0x80000000, - MADD = MADD_w, - MSUB_w = DataProcessing3SourceFixed | 0x00008000, - MSUB_x = DataProcessing3SourceFixed | 0x80008000, - MSUB = MSUB_w, - SMADDL_x = DataProcessing3SourceFixed | 0x80200000, - SMSUBL_x = DataProcessing3SourceFixed | 0x80208000, - SMULH_x = DataProcessing3SourceFixed | 0x80400000, - UMADDL_x = DataProcessing3SourceFixed | 0x80A00000, - UMSUBL_x = DataProcessing3SourceFixed | 0x80A08000, - UMULH_x = DataProcessing3SourceFixed | 0x80C00000 -}; - -// Floating point compare. -enum FPCompareOp { - FPCompareFixed = 0x1E202000, - FPCompareFMask = 0x5F203C00, - FPCompareMask = 0xFFE0FC1F, - FCMP_s = FPCompareFixed | 0x00000000, - FCMP_d = FPCompareFixed | FP64 | 0x00000000, - FCMP = FCMP_s, - FCMP_s_zero = FPCompareFixed | 0x00000008, - FCMP_d_zero = FPCompareFixed | FP64 | 0x00000008, - FCMP_zero = FCMP_s_zero, - FCMPE_s = FPCompareFixed | 0x00000010, - FCMPE_d = FPCompareFixed | FP64 | 0x00000010, - FCMPE_s_zero = FPCompareFixed | 0x00000018, - FCMPE_d_zero = FPCompareFixed | FP64 | 0x00000018 -}; - -// Floating point conditional compare. -enum FPConditionalCompareOp { - FPConditionalCompareFixed = 0x1E200400, - FPConditionalCompareFMask = 0x5F200C00, - FPConditionalCompareMask = 0xFFE00C10, - FCCMP_s = FPConditionalCompareFixed | 0x00000000, - FCCMP_d = FPConditionalCompareFixed | FP64 | 0x00000000, - FCCMP = FCCMP_s, - FCCMPE_s = FPConditionalCompareFixed | 0x00000010, - FCCMPE_d = FPConditionalCompareFixed | FP64 | 0x00000010, - FCCMPE = FCCMPE_s -}; - -// Floating point conditional select. -enum FPConditionalSelectOp { - FPConditionalSelectFixed = 0x1E200C00, - FPConditionalSelectFMask = 0x5F200C00, - FPConditionalSelectMask = 0xFFE00C00, - FCSEL_s = FPConditionalSelectFixed | 0x00000000, - FCSEL_d = FPConditionalSelectFixed | FP64 | 0x00000000, - FCSEL = FCSEL_s -}; - -// Floating point immediate. -enum FPImmediateOp { - FPImmediateFixed = 0x1E201000, - FPImmediateFMask = 0x5F201C00, - FPImmediateMask = 0xFFE01C00, - FMOV_s_imm = FPImmediateFixed | 0x00000000, - FMOV_d_imm = FPImmediateFixed | FP64 | 0x00000000 -}; - -// Floating point data processing 1 source. -enum FPDataProcessing1SourceOp { - FPDataProcessing1SourceFixed = 0x1E204000, - FPDataProcessing1SourceFMask = 0x5F207C00, - FPDataProcessing1SourceMask = 0xFFFFFC00, - FMOV_s = FPDataProcessing1SourceFixed | 0x00000000, - FMOV_d = FPDataProcessing1SourceFixed | FP64 | 0x00000000, - FMOV = FMOV_s, - FABS_s = FPDataProcessing1SourceFixed | 0x00008000, - FABS_d = FPDataProcessing1SourceFixed | FP64 | 0x00008000, - FABS = FABS_s, - FNEG_s = FPDataProcessing1SourceFixed | 0x00010000, - FNEG_d = FPDataProcessing1SourceFixed | FP64 | 0x00010000, - FNEG = FNEG_s, - FSQRT_s = FPDataProcessing1SourceFixed | 0x00018000, - FSQRT_d = FPDataProcessing1SourceFixed | FP64 | 0x00018000, - FSQRT = FSQRT_s, - FCVT_ds = FPDataProcessing1SourceFixed | 0x00028000, - FCVT_sd = FPDataProcessing1SourceFixed | FP64 | 0x00020000, - FRINTN_s = FPDataProcessing1SourceFixed | 0x00040000, - FRINTN_d = FPDataProcessing1SourceFixed | FP64 | 0x00040000, - FRINTN = FRINTN_s, - FRINTP_s = FPDataProcessing1SourceFixed | 0x00048000, - FRINTP_d = FPDataProcessing1SourceFixed | FP64 | 0x00048000, - FRINTP = FRINTP_s, - FRINTM_s = FPDataProcessing1SourceFixed | 0x00050000, - FRINTM_d = FPDataProcessing1SourceFixed | FP64 | 0x00050000, - FRINTM = FRINTM_s, - FRINTZ_s = FPDataProcessing1SourceFixed | 0x00058000, - FRINTZ_d = FPDataProcessing1SourceFixed | FP64 | 0x00058000, - FRINTZ = FRINTZ_s, - FRINTA_s = FPDataProcessing1SourceFixed | 0x00060000, - FRINTA_d = FPDataProcessing1SourceFixed | FP64 | 0x00060000, - FRINTA = FRINTA_s, - FRINTX_s = FPDataProcessing1SourceFixed | 0x00070000, - FRINTX_d = FPDataProcessing1SourceFixed | FP64 | 0x00070000, - FRINTX = FRINTX_s, - FRINTI_s = FPDataProcessing1SourceFixed | 0x00078000, - FRINTI_d = FPDataProcessing1SourceFixed | FP64 | 0x00078000, - FRINTI = FRINTI_s -}; - -// Floating point data processing 2 source. -enum FPDataProcessing2SourceOp { - FPDataProcessing2SourceFixed = 0x1E200800, - FPDataProcessing2SourceFMask = 0x5F200C00, - FPDataProcessing2SourceMask = 0xFFE0FC00, - FMUL = FPDataProcessing2SourceFixed | 0x00000000, - FMUL_s = FMUL, - FMUL_d = FMUL | FP64, - FDIV = FPDataProcessing2SourceFixed | 0x00001000, - FDIV_s = FDIV, - FDIV_d = FDIV | FP64, - FADD = FPDataProcessing2SourceFixed | 0x00002000, - FADD_s = FADD, - FADD_d = FADD | FP64, - FSUB = FPDataProcessing2SourceFixed | 0x00003000, - FSUB_s = FSUB, - FSUB_d = FSUB | FP64, - FMAX = FPDataProcessing2SourceFixed | 0x00004000, - FMAX_s = FMAX, - FMAX_d = FMAX | FP64, - FMIN = FPDataProcessing2SourceFixed | 0x00005000, - FMIN_s = FMIN, - FMIN_d = FMIN | FP64, - FMAXNM = FPDataProcessing2SourceFixed | 0x00006000, - FMAXNM_s = FMAXNM, - FMAXNM_d = FMAXNM | FP64, - FMINNM = FPDataProcessing2SourceFixed | 0x00007000, - FMINNM_s = FMINNM, - FMINNM_d = FMINNM | FP64, - FNMUL = FPDataProcessing2SourceFixed | 0x00008000, - FNMUL_s = FNMUL, - FNMUL_d = FNMUL | FP64 -}; - -// Floating point data processing 3 source. -enum FPDataProcessing3SourceOp { - FPDataProcessing3SourceFixed = 0x1F000000, - FPDataProcessing3SourceFMask = 0x5F000000, - FPDataProcessing3SourceMask = 0xFFE08000, - FMADD_s = FPDataProcessing3SourceFixed | 0x00000000, - FMSUB_s = FPDataProcessing3SourceFixed | 0x00008000, - FNMADD_s = FPDataProcessing3SourceFixed | 0x00200000, - FNMSUB_s = FPDataProcessing3SourceFixed | 0x00208000, - FMADD_d = FPDataProcessing3SourceFixed | 0x00400000, - FMSUB_d = FPDataProcessing3SourceFixed | 0x00408000, - FNMADD_d = FPDataProcessing3SourceFixed | 0x00600000, - FNMSUB_d = FPDataProcessing3SourceFixed | 0x00608000 -}; - -// Conversion between floating point and integer. -enum FPIntegerConvertOp { - FPIntegerConvertFixed = 0x1E200000, - FPIntegerConvertFMask = 0x5F20FC00, - FPIntegerConvertMask = 0xFFFFFC00, - FCVTNS = FPIntegerConvertFixed | 0x00000000, - FCVTNS_ws = FCVTNS, - FCVTNS_xs = FCVTNS | SixtyFourBits, - FCVTNS_wd = FCVTNS | FP64, - FCVTNS_xd = FCVTNS | SixtyFourBits | FP64, - FCVTNU = FPIntegerConvertFixed | 0x00010000, - FCVTNU_ws = FCVTNU, - FCVTNU_xs = FCVTNU | SixtyFourBits, - FCVTNU_wd = FCVTNU | FP64, - FCVTNU_xd = FCVTNU | SixtyFourBits | FP64, - FCVTPS = FPIntegerConvertFixed | 0x00080000, - FCVTPS_ws = FCVTPS, - FCVTPS_xs = FCVTPS | SixtyFourBits, - FCVTPS_wd = FCVTPS | FP64, - FCVTPS_xd = FCVTPS | SixtyFourBits | FP64, - FCVTPU = FPIntegerConvertFixed | 0x00090000, - FCVTPU_ws = FCVTPU, - FCVTPU_xs = FCVTPU | SixtyFourBits, - FCVTPU_wd = FCVTPU | FP64, - FCVTPU_xd = FCVTPU | SixtyFourBits | FP64, - FCVTMS = FPIntegerConvertFixed | 0x00100000, - FCVTMS_ws = FCVTMS, - FCVTMS_xs = FCVTMS | SixtyFourBits, - FCVTMS_wd = FCVTMS | FP64, - FCVTMS_xd = FCVTMS | SixtyFourBits | FP64, - FCVTMU = FPIntegerConvertFixed | 0x00110000, - FCVTMU_ws = FCVTMU, - FCVTMU_xs = FCVTMU | SixtyFourBits, - FCVTMU_wd = FCVTMU | FP64, - FCVTMU_xd = FCVTMU | SixtyFourBits | FP64, - FCVTZS = FPIntegerConvertFixed | 0x00180000, - FCVTZS_ws = FCVTZS, - FCVTZS_xs = FCVTZS | SixtyFourBits, - FCVTZS_wd = FCVTZS | FP64, - FCVTZS_xd = FCVTZS | SixtyFourBits | FP64, - FCVTZU = FPIntegerConvertFixed | 0x00190000, - FCVTZU_ws = FCVTZU, - FCVTZU_xs = FCVTZU | SixtyFourBits, - FCVTZU_wd = FCVTZU | FP64, - FCVTZU_xd = FCVTZU | SixtyFourBits | FP64, - SCVTF = FPIntegerConvertFixed | 0x00020000, - SCVTF_sw = SCVTF, - SCVTF_sx = SCVTF | SixtyFourBits, - SCVTF_dw = SCVTF | FP64, - SCVTF_dx = SCVTF | SixtyFourBits | FP64, - UCVTF = FPIntegerConvertFixed | 0x00030000, - UCVTF_sw = UCVTF, - UCVTF_sx = UCVTF | SixtyFourBits, - UCVTF_dw = UCVTF | FP64, - UCVTF_dx = UCVTF | SixtyFourBits | FP64, - FCVTAS = FPIntegerConvertFixed | 0x00040000, - FCVTAS_ws = FCVTAS, - FCVTAS_xs = FCVTAS | SixtyFourBits, - FCVTAS_wd = FCVTAS | FP64, - FCVTAS_xd = FCVTAS | SixtyFourBits | FP64, - FCVTAU = FPIntegerConvertFixed | 0x00050000, - FCVTAU_ws = FCVTAU, - FCVTAU_xs = FCVTAU | SixtyFourBits, - FCVTAU_wd = FCVTAU | FP64, - FCVTAU_xd = FCVTAU | SixtyFourBits | FP64, - FMOV_ws = FPIntegerConvertFixed | 0x00060000, - FMOV_sw = FPIntegerConvertFixed | 0x00070000, - FMOV_xd = FMOV_ws | SixtyFourBits | FP64, - FMOV_dx = FMOV_sw | SixtyFourBits | FP64 -}; - -// Conversion between fixed point and floating point. -enum FPFixedPointConvertOp { - FPFixedPointConvertFixed = 0x1E000000, - FPFixedPointConvertFMask = 0x5F200000, - FPFixedPointConvertMask = 0xFFFF0000, - FCVTZS_fixed = FPFixedPointConvertFixed | 0x00180000, - FCVTZS_ws_fixed = FCVTZS_fixed, - FCVTZS_xs_fixed = FCVTZS_fixed | SixtyFourBits, - FCVTZS_wd_fixed = FCVTZS_fixed | FP64, - FCVTZS_xd_fixed = FCVTZS_fixed | SixtyFourBits | FP64, - FCVTZU_fixed = FPFixedPointConvertFixed | 0x00190000, - FCVTZU_ws_fixed = FCVTZU_fixed, - FCVTZU_xs_fixed = FCVTZU_fixed | SixtyFourBits, - FCVTZU_wd_fixed = FCVTZU_fixed | FP64, - FCVTZU_xd_fixed = FCVTZU_fixed | SixtyFourBits | FP64, - SCVTF_fixed = FPFixedPointConvertFixed | 0x00020000, - SCVTF_sw_fixed = SCVTF_fixed, - SCVTF_sx_fixed = SCVTF_fixed | SixtyFourBits, - SCVTF_dw_fixed = SCVTF_fixed | FP64, - SCVTF_dx_fixed = SCVTF_fixed | SixtyFourBits | FP64, - UCVTF_fixed = FPFixedPointConvertFixed | 0x00030000, - UCVTF_sw_fixed = UCVTF_fixed, - UCVTF_sx_fixed = UCVTF_fixed | SixtyFourBits, - UCVTF_dw_fixed = UCVTF_fixed | FP64, - UCVTF_dx_fixed = UCVTF_fixed | SixtyFourBits | FP64 -}; - -// Unimplemented and unallocated instructions. These are defined to make fixed -// bit assertion easier. -enum UnimplementedOp { - UnimplementedFixed = 0x00000000, - UnimplementedFMask = 0x00000000 -}; - -enum UnallocatedOp { - UnallocatedFixed = 0x00000000, - UnallocatedFMask = 0x00000000 -}; - -} // namespace vixl - -#endif // VIXL_A64_CONSTANTS_A64_H_ diff --git a/qemu/disas/libvixl/a64/cpu-a64.h b/qemu/disas/libvixl/a64/cpu-a64.h deleted file mode 100644 index 59b7974a1..000000000 --- a/qemu/disas/libvixl/a64/cpu-a64.h +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2013, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef VIXL_CPU_A64_H -#define VIXL_CPU_A64_H - -#include "globals.h" -#include "instructions-a64.h" - -namespace vixl { - -class CPU { - public: - // Initialise CPU support. - static void SetUp(); - - // Ensures the data at a given address and with a given size is the same for - // the I and D caches. I and D caches are not automatically coherent on ARM - // so this operation is required before any dynamically generated code can - // safely run. - static void EnsureIAndDCacheCoherency(void *address, size_t length); - - // Handle tagged pointers. - template - static T SetPointerTag(T pointer, uint64_t tag) { - VIXL_ASSERT(is_uintn(kAddressTagWidth, tag)); - - // Use C-style casts to get static_cast behaviour for integral types (T), - // and reinterpret_cast behaviour for other types. - - uint64_t raw = (uint64_t)pointer; - VIXL_STATIC_ASSERT(sizeof(pointer) == sizeof(raw)); - - raw = (raw & ~kAddressTagMask) | (tag << kAddressTagOffset); - return (T)raw; - } - - template - static uint64_t GetPointerTag(T pointer) { - // Use C-style casts to get static_cast behaviour for integral types (T), - // and reinterpret_cast behaviour for other types. - - uint64_t raw = (uint64_t)pointer; - VIXL_STATIC_ASSERT(sizeof(pointer) == sizeof(raw)); - - return (raw & kAddressTagMask) >> kAddressTagOffset; - } - - private: - // Return the content of the cache type register. - static uint32_t GetCacheType(); - - // I and D cache line size in bytes. - static unsigned icache_line_size_; - static unsigned dcache_line_size_; -}; - -} // namespace vixl - -#endif // VIXL_CPU_A64_H diff --git a/qemu/disas/libvixl/a64/decoder-a64.cc b/qemu/disas/libvixl/a64/decoder-a64.cc deleted file mode 100644 index 82591ca30..000000000 --- a/qemu/disas/libvixl/a64/decoder-a64.cc +++ /dev/null @@ -1,707 +0,0 @@ -// Copyright 2013, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "globals.h" -#include "utils.h" -#include "a64/decoder-a64.h" - -namespace vixl { - -void Decoder::DecodeInstruction(const Instruction *instr) { - if (instr->Bits(28, 27) == 0) { - VisitUnallocated(instr); - } else { - switch (instr->Bits(27, 24)) { - // 0: PC relative addressing. - case 0x0: DecodePCRelAddressing(instr); break; - - // 1: Add/sub immediate. - case 0x1: DecodeAddSubImmediate(instr); break; - - // A: Logical shifted register. - // Add/sub with carry. - // Conditional compare register. - // Conditional compare immediate. - // Conditional select. - // Data processing 1 source. - // Data processing 2 source. - // B: Add/sub shifted register. - // Add/sub extended register. - // Data processing 3 source. - case 0xA: - case 0xB: DecodeDataProcessing(instr); break; - - // 2: Logical immediate. - // Move wide immediate. - case 0x2: DecodeLogical(instr); break; - - // 3: Bitfield. - // Extract. - case 0x3: DecodeBitfieldExtract(instr); break; - - // 4: Unconditional branch immediate. - // Exception generation. - // Compare and branch immediate. - // 5: Compare and branch immediate. - // Conditional branch. - // System. - // 6,7: Unconditional branch. - // Test and branch immediate. - case 0x4: - case 0x5: - case 0x6: - case 0x7: DecodeBranchSystemException(instr); break; - - // 8,9: Load/store register pair post-index. - // Load register literal. - // Load/store register unscaled immediate. - // Load/store register immediate post-index. - // Load/store register immediate pre-index. - // Load/store register offset. - // Load/store exclusive. - // C,D: Load/store register pair offset. - // Load/store register pair pre-index. - // Load/store register unsigned immediate. - // Advanced SIMD. - case 0x8: - case 0x9: - case 0xC: - case 0xD: DecodeLoadStore(instr); break; - - // E: FP fixed point conversion. - // FP integer conversion. - // FP data processing 1 source. - // FP compare. - // FP immediate. - // FP data processing 2 source. - // FP conditional compare. - // FP conditional select. - // Advanced SIMD. - // F: FP data processing 3 source. - // Advanced SIMD. - case 0xE: - case 0xF: DecodeFP(instr); break; - } - } -} - -void Decoder::AppendVisitor(DecoderVisitor* new_visitor) { - visitors_.push_back(new_visitor); -} - - -void Decoder::PrependVisitor(DecoderVisitor* new_visitor) { - visitors_.push_front(new_visitor); -} - - -void Decoder::InsertVisitorBefore(DecoderVisitor* new_visitor, - DecoderVisitor* registered_visitor) { - std::list::iterator it; - for (it = visitors_.begin(); it != visitors_.end(); it++) { - if (*it == registered_visitor) { - visitors_.insert(it, new_visitor); - return; - } - } - // We reached the end of the list. The last element must be - // registered_visitor. - VIXL_ASSERT(*it == registered_visitor); - visitors_.insert(it, new_visitor); -} - - -void Decoder::InsertVisitorAfter(DecoderVisitor* new_visitor, - DecoderVisitor* registered_visitor) { - std::list::iterator it; - for (it = visitors_.begin(); it != visitors_.end(); it++) { - if (*it == registered_visitor) { - it++; - visitors_.insert(it, new_visitor); - return; - } - } - // We reached the end of the list. The last element must be - // registered_visitor. - VIXL_ASSERT(*it == registered_visitor); - visitors_.push_back(new_visitor); -} - - -void Decoder::RemoveVisitor(DecoderVisitor* visitor) { - visitors_.remove(visitor); -} - - -void Decoder::DecodePCRelAddressing(const Instruction* instr) { - VIXL_ASSERT(instr->Bits(27, 24) == 0x0); - // We know bit 28 is set, as = 0 is filtered out at the top level - // decode. - VIXL_ASSERT(instr->Bit(28) == 0x1); - VisitPCRelAddressing(instr); -} - - -void Decoder::DecodeBranchSystemException(const Instruction* instr) { - VIXL_ASSERT((instr->Bits(27, 24) == 0x4) || - (instr->Bits(27, 24) == 0x5) || - (instr->Bits(27, 24) == 0x6) || - (instr->Bits(27, 24) == 0x7) ); - - switch (instr->Bits(31, 29)) { - case 0: - case 4: { - VisitUnconditionalBranch(instr); - break; - } - case 1: - case 5: { - if (instr->Bit(25) == 0) { - VisitCompareBranch(instr); - } else { - VisitTestBranch(instr); - } - break; - } - case 2: { - if (instr->Bit(25) == 0) { - if ((instr->Bit(24) == 0x1) || - (instr->Mask(0x01000010) == 0x00000010)) { - VisitUnallocated(instr); - } else { - VisitConditionalBranch(instr); - } - } else { - VisitUnallocated(instr); - } - break; - } - case 6: { - if (instr->Bit(25) == 0) { - if (instr->Bit(24) == 0) { - if ((instr->Bits(4, 2) != 0) || - (instr->Mask(0x00E0001D) == 0x00200001) || - (instr->Mask(0x00E0001D) == 0x00400001) || - (instr->Mask(0x00E0001E) == 0x00200002) || - (instr->Mask(0x00E0001E) == 0x00400002) || - (instr->Mask(0x00E0001C) == 0x00600000) || - (instr->Mask(0x00E0001C) == 0x00800000) || - (instr->Mask(0x00E0001F) == 0x00A00000) || - (instr->Mask(0x00C0001C) == 0x00C00000)) { - VisitUnallocated(instr); - } else { - VisitException(instr); - } - } else { - if (instr->Bits(23, 22) == 0) { - const Instr masked_003FF0E0 = instr->Mask(0x003FF0E0); - if ((instr->Bits(21, 19) == 0x4) || - (masked_003FF0E0 == 0x00033000) || - (masked_003FF0E0 == 0x003FF020) || - (masked_003FF0E0 == 0x003FF060) || - (masked_003FF0E0 == 0x003FF0E0) || - (instr->Mask(0x00388000) == 0x00008000) || - (instr->Mask(0x0038E000) == 0x00000000) || - (instr->Mask(0x0039E000) == 0x00002000) || - (instr->Mask(0x003AE000) == 0x00002000) || - (instr->Mask(0x003CE000) == 0x00042000) || - (instr->Mask(0x003FFFC0) == 0x000320C0) || - (instr->Mask(0x003FF100) == 0x00032100) || - (instr->Mask(0x003FF200) == 0x00032200) || - (instr->Mask(0x003FF400) == 0x00032400) || - (instr->Mask(0x003FF800) == 0x00032800) || - (instr->Mask(0x0038F000) == 0x00005000) || - (instr->Mask(0x0038E000) == 0x00006000)) { - VisitUnallocated(instr); - } else { - VisitSystem(instr); - } - } else { - VisitUnallocated(instr); - } - } - } else { - if ((instr->Bit(24) == 0x1) || - (instr->Bits(20, 16) != 0x1F) || - (instr->Bits(15, 10) != 0) || - (instr->Bits(4, 0) != 0) || - (instr->Bits(24, 21) == 0x3) || - (instr->Bits(24, 22) == 0x3)) { - VisitUnallocated(instr); - } else { - VisitUnconditionalBranchToRegister(instr); - } - } - break; - } - case 3: - case 7: { - VisitUnallocated(instr); - break; - } - } -} - - -void Decoder::DecodeLoadStore(const Instruction* instr) { - VIXL_ASSERT((instr->Bits(27, 24) == 0x8) || - (instr->Bits(27, 24) == 0x9) || - (instr->Bits(27, 24) == 0xC) || - (instr->Bits(27, 24) == 0xD) ); - - if (instr->Bit(24) == 0) { - if (instr->Bit(28) == 0) { - if (instr->Bit(29) == 0) { - if (instr->Bit(26) == 0) { - VisitLoadStoreExclusive(instr); - } else { - DecodeAdvSIMDLoadStore(instr); - } - } else { - if ((instr->Bits(31, 30) == 0x3) || - (instr->Mask(0xC4400000) == 0x40000000)) { - VisitUnallocated(instr); - } else { - if (instr->Bit(23) == 0) { - if (instr->Mask(0xC4400000) == 0xC0400000) { - VisitUnallocated(instr); - } else { - VisitLoadStorePairNonTemporal(instr); - } - } else { - VisitLoadStorePairPostIndex(instr); - } - } - } - } else { - if (instr->Bit(29) == 0) { - if (instr->Mask(0xC4000000) == 0xC4000000) { - VisitUnallocated(instr); - } else { - VisitLoadLiteral(instr); - } - } else { - if ((instr->Mask(0x84C00000) == 0x80C00000) || - (instr->Mask(0x44800000) == 0x44800000) || - (instr->Mask(0x84800000) == 0x84800000)) { - VisitUnallocated(instr); - } else { - if (instr->Bit(21) == 0) { - switch (instr->Bits(11, 10)) { - case 0: { - VisitLoadStoreUnscaledOffset(instr); - break; - } - case 1: { - if (instr->Mask(0xC4C00000) == 0xC0800000) { - VisitUnallocated(instr); - } else { - VisitLoadStorePostIndex(instr); - } - break; - } - case 2: { - // TODO: VisitLoadStoreRegisterOffsetUnpriv. - VisitUnimplemented(instr); - break; - } - case 3: { - if (instr->Mask(0xC4C00000) == 0xC0800000) { - VisitUnallocated(instr); - } else { - VisitLoadStorePreIndex(instr); - } - break; - } - } - } else { - if (instr->Bits(11, 10) == 0x2) { - if (instr->Bit(14) == 0) { - VisitUnallocated(instr); - } else { - VisitLoadStoreRegisterOffset(instr); - } - } else { - VisitUnallocated(instr); - } - } - } - } - } - } else { - if (instr->Bit(28) == 0) { - if (instr->Bit(29) == 0) { - VisitUnallocated(instr); - } else { - if ((instr->Bits(31, 30) == 0x3) || - (instr->Mask(0xC4400000) == 0x40000000)) { - VisitUnallocated(instr); - } else { - if (instr->Bit(23) == 0) { - VisitLoadStorePairOffset(instr); - } else { - VisitLoadStorePairPreIndex(instr); - } - } - } - } else { - if (instr->Bit(29) == 0) { - VisitUnallocated(instr); - } else { - if ((instr->Mask(0x84C00000) == 0x80C00000) || - (instr->Mask(0x44800000) == 0x44800000) || - (instr->Mask(0x84800000) == 0x84800000)) { - VisitUnallocated(instr); - } else { - VisitLoadStoreUnsignedOffset(instr); - } - } - } - } -} - - -void Decoder::DecodeLogical(const Instruction* instr) { - VIXL_ASSERT(instr->Bits(27, 24) == 0x2); - - if (instr->Mask(0x80400000) == 0x00400000) { - VisitUnallocated(instr); - } else { - if (instr->Bit(23) == 0) { - VisitLogicalImmediate(instr); - } else { - if (instr->Bits(30, 29) == 0x1) { - VisitUnallocated(instr); - } else { - VisitMoveWideImmediate(instr); - } - } - } -} - - -void Decoder::DecodeBitfieldExtract(const Instruction* instr) { - VIXL_ASSERT(instr->Bits(27, 24) == 0x3); - - if ((instr->Mask(0x80400000) == 0x80000000) || - (instr->Mask(0x80400000) == 0x00400000) || - (instr->Mask(0x80008000) == 0x00008000)) { - VisitUnallocated(instr); - } else if (instr->Bit(23) == 0) { - if ((instr->Mask(0x80200000) == 0x00200000) || - (instr->Mask(0x60000000) == 0x60000000)) { - VisitUnallocated(instr); - } else { - VisitBitfield(instr); - } - } else { - if ((instr->Mask(0x60200000) == 0x00200000) || - (instr->Mask(0x60000000) != 0x00000000)) { - VisitUnallocated(instr); - } else { - VisitExtract(instr); - } - } -} - - -void Decoder::DecodeAddSubImmediate(const Instruction* instr) { - VIXL_ASSERT(instr->Bits(27, 24) == 0x1); - if (instr->Bit(23) == 1) { - VisitUnallocated(instr); - } else { - VisitAddSubImmediate(instr); - } -} - - -void Decoder::DecodeDataProcessing(const Instruction* instr) { - VIXL_ASSERT((instr->Bits(27, 24) == 0xA) || - (instr->Bits(27, 24) == 0xB)); - - if (instr->Bit(24) == 0) { - if (instr->Bit(28) == 0) { - if (instr->Mask(0x80008000) == 0x00008000) { - VisitUnallocated(instr); - } else { - VisitLogicalShifted(instr); - } - } else { - switch (instr->Bits(23, 21)) { - case 0: { - if (instr->Mask(0x0000FC00) != 0) { - VisitUnallocated(instr); - } else { - VisitAddSubWithCarry(instr); - } - break; - } - case 2: { - if ((instr->Bit(29) == 0) || - (instr->Mask(0x00000410) != 0)) { - VisitUnallocated(instr); - } else { - if (instr->Bit(11) == 0) { - VisitConditionalCompareRegister(instr); - } else { - VisitConditionalCompareImmediate(instr); - } - } - break; - } - case 4: { - if (instr->Mask(0x20000800) != 0x00000000) { - VisitUnallocated(instr); - } else { - VisitConditionalSelect(instr); - } - break; - } - case 6: { - if (instr->Bit(29) == 0x1) { - VisitUnallocated(instr); - } else { - if (instr->Bit(30) == 0) { - if ((instr->Bit(15) == 0x1) || - (instr->Bits(15, 11) == 0) || - (instr->Bits(15, 12) == 0x1) || - (instr->Bits(15, 12) == 0x3) || - (instr->Bits(15, 13) == 0x3) || - (instr->Mask(0x8000EC00) == 0x00004C00) || - (instr->Mask(0x8000E800) == 0x80004000) || - (instr->Mask(0x8000E400) == 0x80004000)) { - VisitUnallocated(instr); - } else { - VisitDataProcessing2Source(instr); - } - } else { - if ((instr->Bit(13) == 1) || - (instr->Bits(20, 16) != 0) || - (instr->Bits(15, 14) != 0) || - (instr->Mask(0xA01FFC00) == 0x00000C00) || - (instr->Mask(0x201FF800) == 0x00001800)) { - VisitUnallocated(instr); - } else { - VisitDataProcessing1Source(instr); - } - } - break; - } - } - case 1: - case 3: - case 5: - case 7: VisitUnallocated(instr); break; - } - } - } else { - if (instr->Bit(28) == 0) { - if (instr->Bit(21) == 0) { - if ((instr->Bits(23, 22) == 0x3) || - (instr->Mask(0x80008000) == 0x00008000)) { - VisitUnallocated(instr); - } else { - VisitAddSubShifted(instr); - } - } else { - if ((instr->Mask(0x00C00000) != 0x00000000) || - (instr->Mask(0x00001400) == 0x00001400) || - (instr->Mask(0x00001800) == 0x00001800)) { - VisitUnallocated(instr); - } else { - VisitAddSubExtended(instr); - } - } - } else { - if ((instr->Bit(30) == 0x1) || - (instr->Bits(30, 29) == 0x1) || - (instr->Mask(0xE0600000) == 0x00200000) || - (instr->Mask(0xE0608000) == 0x00400000) || - (instr->Mask(0x60608000) == 0x00408000) || - (instr->Mask(0x60E00000) == 0x00E00000) || - (instr->Mask(0x60E00000) == 0x00800000) || - (instr->Mask(0x60E00000) == 0x00600000)) { - VisitUnallocated(instr); - } else { - VisitDataProcessing3Source(instr); - } - } - } -} - - -void Decoder::DecodeFP(const Instruction* instr) { - VIXL_ASSERT((instr->Bits(27, 24) == 0xE) || - (instr->Bits(27, 24) == 0xF)); - - if (instr->Bit(28) == 0) { - DecodeAdvSIMDDataProcessing(instr); - } else { - if (instr->Bit(29) == 1) { - VisitUnallocated(instr); - } else { - if (instr->Bits(31, 30) == 0x3) { - VisitUnallocated(instr); - } else if (instr->Bits(31, 30) == 0x1) { - DecodeAdvSIMDDataProcessing(instr); - } else { - if (instr->Bit(24) == 0) { - if (instr->Bit(21) == 0) { - if ((instr->Bit(23) == 1) || - (instr->Bit(18) == 1) || - (instr->Mask(0x80008000) == 0x00000000) || - (instr->Mask(0x000E0000) == 0x00000000) || - (instr->Mask(0x000E0000) == 0x000A0000) || - (instr->Mask(0x00160000) == 0x00000000) || - (instr->Mask(0x00160000) == 0x00120000)) { - VisitUnallocated(instr); - } else { - VisitFPFixedPointConvert(instr); - } - } else { - if (instr->Bits(15, 10) == 32) { - VisitUnallocated(instr); - } else if (instr->Bits(15, 10) == 0) { - if ((instr->Bits(23, 22) == 0x3) || - (instr->Mask(0x000E0000) == 0x000A0000) || - (instr->Mask(0x000E0000) == 0x000C0000) || - (instr->Mask(0x00160000) == 0x00120000) || - (instr->Mask(0x00160000) == 0x00140000) || - (instr->Mask(0x20C40000) == 0x00800000) || - (instr->Mask(0x20C60000) == 0x00840000) || - (instr->Mask(0xA0C60000) == 0x80060000) || - (instr->Mask(0xA0C60000) == 0x00860000) || - (instr->Mask(0xA0C60000) == 0x00460000) || - (instr->Mask(0xA0CE0000) == 0x80860000) || - (instr->Mask(0xA0CE0000) == 0x804E0000) || - (instr->Mask(0xA0CE0000) == 0x000E0000) || - (instr->Mask(0xA0D60000) == 0x00160000) || - (instr->Mask(0xA0D60000) == 0x80560000) || - (instr->Mask(0xA0D60000) == 0x80960000)) { - VisitUnallocated(instr); - } else { - VisitFPIntegerConvert(instr); - } - } else if (instr->Bits(14, 10) == 16) { - const Instr masked_A0DF8000 = instr->Mask(0xA0DF8000); - if ((instr->Mask(0x80180000) != 0) || - (masked_A0DF8000 == 0x00020000) || - (masked_A0DF8000 == 0x00030000) || - (masked_A0DF8000 == 0x00068000) || - (masked_A0DF8000 == 0x00428000) || - (masked_A0DF8000 == 0x00430000) || - (masked_A0DF8000 == 0x00468000) || - (instr->Mask(0xA0D80000) == 0x00800000) || - (instr->Mask(0xA0DE0000) == 0x00C00000) || - (instr->Mask(0xA0DF0000) == 0x00C30000) || - (instr->Mask(0xA0DC0000) == 0x00C40000)) { - VisitUnallocated(instr); - } else { - VisitFPDataProcessing1Source(instr); - } - } else if (instr->Bits(13, 10) == 8) { - if ((instr->Bits(15, 14) != 0) || - (instr->Bits(2, 0) != 0) || - (instr->Mask(0x80800000) != 0x00000000)) { - VisitUnallocated(instr); - } else { - VisitFPCompare(instr); - } - } else if (instr->Bits(12, 10) == 4) { - if ((instr->Bits(9, 5) != 0) || - (instr->Mask(0x80800000) != 0x00000000)) { - VisitUnallocated(instr); - } else { - VisitFPImmediate(instr); - } - } else { - if (instr->Mask(0x80800000) != 0x00000000) { - VisitUnallocated(instr); - } else { - switch (instr->Bits(11, 10)) { - case 1: { - VisitFPConditionalCompare(instr); - break; - } - case 2: { - if ((instr->Bits(15, 14) == 0x3) || - (instr->Mask(0x00009000) == 0x00009000) || - (instr->Mask(0x0000A000) == 0x0000A000)) { - VisitUnallocated(instr); - } else { - VisitFPDataProcessing2Source(instr); - } - break; - } - case 3: { - VisitFPConditionalSelect(instr); - break; - } - default: VIXL_UNREACHABLE(); - } - } - } - } - } else { - // Bit 30 == 1 has been handled earlier. - VIXL_ASSERT(instr->Bit(30) == 0); - if (instr->Mask(0xA0800000) != 0) { - VisitUnallocated(instr); - } else { - VisitFPDataProcessing3Source(instr); - } - } - } - } - } -} - - -void Decoder::DecodeAdvSIMDLoadStore(const Instruction* instr) { - // TODO: Implement Advanced SIMD load/store instruction decode. - VIXL_ASSERT(instr->Bits(29, 25) == 0x6); - VisitUnimplemented(instr); -} - - -void Decoder::DecodeAdvSIMDDataProcessing(const Instruction* instr) { - // TODO: Implement Advanced SIMD data processing instruction decode. - VIXL_ASSERT(instr->Bits(27, 25) == 0x7); - VisitUnimplemented(instr); -} - - -#define DEFINE_VISITOR_CALLERS(A) \ - void Decoder::Visit##A(const Instruction *instr) { \ - VIXL_ASSERT(instr->Mask(A##FMask) == A##Fixed); \ - std::list::iterator it; \ - for (it = visitors_.begin(); it != visitors_.end(); it++) { \ - (*it)->Visit##A(instr); \ - } \ - } -VISITOR_LIST(DEFINE_VISITOR_CALLERS) -#undef DEFINE_VISITOR_CALLERS -} // namespace vixl diff --git a/qemu/disas/libvixl/a64/decoder-a64.h b/qemu/disas/libvixl/a64/decoder-a64.h deleted file mode 100644 index fd08d6c1f..000000000 --- a/qemu/disas/libvixl/a64/decoder-a64.h +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright 2013, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef VIXL_A64_DECODER_A64_H_ -#define VIXL_A64_DECODER_A64_H_ - -#include - -#include "globals.h" -#include "a64/instructions-a64.h" - - -// List macro containing all visitors needed by the decoder class. - -#define VISITOR_LIST(V) \ - V(PCRelAddressing) \ - V(AddSubImmediate) \ - V(LogicalImmediate) \ - V(MoveWideImmediate) \ - V(Bitfield) \ - V(Extract) \ - V(UnconditionalBranch) \ - V(UnconditionalBranchToRegister) \ - V(CompareBranch) \ - V(TestBranch) \ - V(ConditionalBranch) \ - V(System) \ - V(Exception) \ - V(LoadStorePairPostIndex) \ - V(LoadStorePairOffset) \ - V(LoadStorePairPreIndex) \ - V(LoadStorePairNonTemporal) \ - V(LoadLiteral) \ - V(LoadStoreUnscaledOffset) \ - V(LoadStorePostIndex) \ - V(LoadStorePreIndex) \ - V(LoadStoreRegisterOffset) \ - V(LoadStoreUnsignedOffset) \ - V(LoadStoreExclusive) \ - V(LogicalShifted) \ - V(AddSubShifted) \ - V(AddSubExtended) \ - V(AddSubWithCarry) \ - V(ConditionalCompareRegister) \ - V(ConditionalCompareImmediate) \ - V(ConditionalSelect) \ - V(DataProcessing1Source) \ - V(DataProcessing2Source) \ - V(DataProcessing3Source) \ - V(FPCompare) \ - V(FPConditionalCompare) \ - V(FPConditionalSelect) \ - V(FPImmediate) \ - V(FPDataProcessing1Source) \ - V(FPDataProcessing2Source) \ - V(FPDataProcessing3Source) \ - V(FPIntegerConvert) \ - V(FPFixedPointConvert) \ - V(Unallocated) \ - V(Unimplemented) - -namespace vixl { - -// The Visitor interface. Disassembler and simulator (and other tools) -// must provide implementations for all of these functions. -class DecoderVisitor { - public: - enum VisitorConstness { - kConstVisitor, - kNonConstVisitor - }; - explicit DecoderVisitor(VisitorConstness constness = kConstVisitor) - : constness_(constness) {} - - virtual ~DecoderVisitor() {} - - #define DECLARE(A) virtual void Visit##A(const Instruction* instr) = 0; - VISITOR_LIST(DECLARE) - #undef DECLARE - - bool IsConstVisitor() const { return constness_ == kConstVisitor; } - Instruction* MutableInstruction(const Instruction* instr) { - VIXL_ASSERT(!IsConstVisitor()); - return const_cast(instr); - } - - private: - const VisitorConstness constness_; -}; - - -class Decoder { - public: - Decoder() {} - - // Top-level wrappers around the actual decoding function. - void Decode(const Instruction* instr) { - std::list::iterator it; - for (it = visitors_.begin(); it != visitors_.end(); it++) { - VIXL_ASSERT((*it)->IsConstVisitor()); - } - DecodeInstruction(instr); - } - void Decode(Instruction* instr) { - DecodeInstruction(const_cast(instr)); - } - - // Register a new visitor class with the decoder. - // Decode() will call the corresponding visitor method from all registered - // visitor classes when decoding reaches the leaf node of the instruction - // decode tree. - // Visitors are called in order. - // A visitor can be registered multiple times. - // - // d.AppendVisitor(V1); - // d.AppendVisitor(V2); - // d.PrependVisitor(V2); - // d.AppendVisitor(V3); - // - // d.Decode(i); - // - // will call in order visitor methods in V2, V1, V2, V3. - void AppendVisitor(DecoderVisitor* visitor); - void PrependVisitor(DecoderVisitor* visitor); - // These helpers register `new_visitor` before or after the first instance of - // `registered_visiter` in the list. - // So if - // V1, V2, V1, V2 - // are registered in this order in the decoder, calls to - // d.InsertVisitorAfter(V3, V1); - // d.InsertVisitorBefore(V4, V2); - // will yield the order - // V1, V3, V4, V2, V1, V2 - // - // For more complex modifications of the order of registered visitors, one can - // directly access and modify the list of visitors via the `visitors()' - // accessor. - void InsertVisitorBefore(DecoderVisitor* new_visitor, - DecoderVisitor* registered_visitor); - void InsertVisitorAfter(DecoderVisitor* new_visitor, - DecoderVisitor* registered_visitor); - - // Remove all instances of a previously registered visitor class from the list - // of visitors stored by the decoder. - void RemoveVisitor(DecoderVisitor* visitor); - - #define DECLARE(A) void Visit##A(const Instruction* instr); - VISITOR_LIST(DECLARE) - #undef DECLARE - - - std::list* visitors() { return &visitors_; } - - private: - // Decodes an instruction and calls the visitor functions registered with the - // Decoder class. - void DecodeInstruction(const Instruction* instr); - - // Decode the PC relative addressing instruction, and call the corresponding - // visitors. - // On entry, instruction bits 27:24 = 0x0. - void DecodePCRelAddressing(const Instruction* instr); - - // Decode the add/subtract immediate instruction, and call the correspoding - // visitors. - // On entry, instruction bits 27:24 = 0x1. - void DecodeAddSubImmediate(const Instruction* instr); - - // Decode the branch, system command, and exception generation parts of - // the instruction tree, and call the corresponding visitors. - // On entry, instruction bits 27:24 = {0x4, 0x5, 0x6, 0x7}. - void DecodeBranchSystemException(const Instruction* instr); - - // Decode the load and store parts of the instruction tree, and call - // the corresponding visitors. - // On entry, instruction bits 27:24 = {0x8, 0x9, 0xC, 0xD}. - void DecodeLoadStore(const Instruction* instr); - - // Decode the logical immediate and move wide immediate parts of the - // instruction tree, and call the corresponding visitors. - // On entry, instruction bits 27:24 = 0x2. - void DecodeLogical(const Instruction* instr); - - // Decode the bitfield and extraction parts of the instruction tree, - // and call the corresponding visitors. - // On entry, instruction bits 27:24 = 0x3. - void DecodeBitfieldExtract(const Instruction* instr); - - // Decode the data processing parts of the instruction tree, and call the - // corresponding visitors. - // On entry, instruction bits 27:24 = {0x1, 0xA, 0xB}. - void DecodeDataProcessing(const Instruction* instr); - - // Decode the floating point parts of the instruction tree, and call the - // corresponding visitors. - // On entry, instruction bits 27:24 = {0xE, 0xF}. - void DecodeFP(const Instruction* instr); - - // Decode the Advanced SIMD (NEON) load/store part of the instruction tree, - // and call the corresponding visitors. - // On entry, instruction bits 29:25 = 0x6. - void DecodeAdvSIMDLoadStore(const Instruction* instr); - - // Decode the Advanced SIMD (NEON) data processing part of the instruction - // tree, and call the corresponding visitors. - // On entry, instruction bits 27:25 = 0x7. - void DecodeAdvSIMDDataProcessing(const Instruction* instr); - - private: - // Visitors are registered in a list. - std::list visitors_; -}; - -} // namespace vixl - -#endif // VIXL_A64_DECODER_A64_H_ diff --git a/qemu/disas/libvixl/a64/disasm-a64.cc b/qemu/disas/libvixl/a64/disasm-a64.cc deleted file mode 100644 index f7bc2468b..000000000 --- a/qemu/disas/libvixl/a64/disasm-a64.cc +++ /dev/null @@ -1,1954 +0,0 @@ -// Copyright 2013, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include -#include "a64/disasm-a64.h" - -namespace vixl { - -Disassembler::Disassembler() { - buffer_size_ = 256; - buffer_ = reinterpret_cast(malloc(buffer_size_)); - buffer_pos_ = 0; - own_buffer_ = true; - code_address_offset_ = 0; -} - - -Disassembler::Disassembler(char* text_buffer, int buffer_size) { - buffer_size_ = buffer_size; - buffer_ = text_buffer; - buffer_pos_ = 0; - own_buffer_ = false; - code_address_offset_ = 0; -} - - -Disassembler::~Disassembler() { - if (own_buffer_) { - free(buffer_); - } -} - - -char* Disassembler::GetOutput() { - return buffer_; -} - - -void Disassembler::VisitAddSubImmediate(const Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - bool stack_op = (rd_is_zr || RnIsZROrSP(instr)) && - (instr->ImmAddSub() == 0) ? true : false; - const char *mnemonic = ""; - const char *form = "'Rds, 'Rns, 'IAddSub"; - const char *form_cmp = "'Rns, 'IAddSub"; - const char *form_mov = "'Rds, 'Rns"; - - switch (instr->Mask(AddSubImmediateMask)) { - case ADD_w_imm: - case ADD_x_imm: { - mnemonic = "add"; - if (stack_op) { - mnemonic = "mov"; - form = form_mov; - } - break; - } - case ADDS_w_imm: - case ADDS_x_imm: { - mnemonic = "adds"; - if (rd_is_zr) { - mnemonic = "cmn"; - form = form_cmp; - } - break; - } - case SUB_w_imm: - case SUB_x_imm: mnemonic = "sub"; break; - case SUBS_w_imm: - case SUBS_x_imm: { - mnemonic = "subs"; - if (rd_is_zr) { - mnemonic = "cmp"; - form = form_cmp; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitAddSubShifted(const Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - bool rn_is_zr = RnIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm'HDP"; - const char *form_cmp = "'Rn, 'Rm'HDP"; - const char *form_neg = "'Rd, 'Rm'HDP"; - - switch (instr->Mask(AddSubShiftedMask)) { - case ADD_w_shift: - case ADD_x_shift: mnemonic = "add"; break; - case ADDS_w_shift: - case ADDS_x_shift: { - mnemonic = "adds"; - if (rd_is_zr) { - mnemonic = "cmn"; - form = form_cmp; - } - break; - } - case SUB_w_shift: - case SUB_x_shift: { - mnemonic = "sub"; - if (rn_is_zr) { - mnemonic = "neg"; - form = form_neg; - } - break; - } - case SUBS_w_shift: - case SUBS_x_shift: { - mnemonic = "subs"; - if (rd_is_zr) { - mnemonic = "cmp"; - form = form_cmp; - } else if (rn_is_zr) { - mnemonic = "negs"; - form = form_neg; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitAddSubExtended(const Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - const char *mnemonic = ""; - Extend mode = static_cast(instr->ExtendMode()); - const char *form = ((mode == UXTX) || (mode == SXTX)) ? - "'Rds, 'Rns, 'Xm'Ext" : "'Rds, 'Rns, 'Wm'Ext"; - const char *form_cmp = ((mode == UXTX) || (mode == SXTX)) ? - "'Rns, 'Xm'Ext" : "'Rns, 'Wm'Ext"; - - switch (instr->Mask(AddSubExtendedMask)) { - case ADD_w_ext: - case ADD_x_ext: mnemonic = "add"; break; - case ADDS_w_ext: - case ADDS_x_ext: { - mnemonic = "adds"; - if (rd_is_zr) { - mnemonic = "cmn"; - form = form_cmp; - } - break; - } - case SUB_w_ext: - case SUB_x_ext: mnemonic = "sub"; break; - case SUBS_w_ext: - case SUBS_x_ext: { - mnemonic = "subs"; - if (rd_is_zr) { - mnemonic = "cmp"; - form = form_cmp; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitAddSubWithCarry(const Instruction* instr) { - bool rn_is_zr = RnIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm"; - const char *form_neg = "'Rd, 'Rm"; - - switch (instr->Mask(AddSubWithCarryMask)) { - case ADC_w: - case ADC_x: mnemonic = "adc"; break; - case ADCS_w: - case ADCS_x: mnemonic = "adcs"; break; - case SBC_w: - case SBC_x: { - mnemonic = "sbc"; - if (rn_is_zr) { - mnemonic = "ngc"; - form = form_neg; - } - break; - } - case SBCS_w: - case SBCS_x: { - mnemonic = "sbcs"; - if (rn_is_zr) { - mnemonic = "ngcs"; - form = form_neg; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLogicalImmediate(const Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - bool rn_is_zr = RnIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Rds, 'Rn, 'ITri"; - - if (instr->ImmLogical() == 0) { - // The immediate encoded in the instruction is not in the expected format. - Format(instr, "unallocated", "(LogicalImmediate)"); - return; - } - - switch (instr->Mask(LogicalImmediateMask)) { - case AND_w_imm: - case AND_x_imm: mnemonic = "and"; break; - case ORR_w_imm: - case ORR_x_imm: { - mnemonic = "orr"; - unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSize - : kWRegSize; - if (rn_is_zr && !IsMovzMovnImm(reg_size, instr->ImmLogical())) { - mnemonic = "mov"; - form = "'Rds, 'ITri"; - } - break; - } - case EOR_w_imm: - case EOR_x_imm: mnemonic = "eor"; break; - case ANDS_w_imm: - case ANDS_x_imm: { - mnemonic = "ands"; - if (rd_is_zr) { - mnemonic = "tst"; - form = "'Rn, 'ITri"; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -bool Disassembler::IsMovzMovnImm(unsigned reg_size, uint64_t value) { - VIXL_ASSERT((reg_size == kXRegSize) || - ((reg_size == kWRegSize) && (value <= 0xffffffff))); - - // Test for movz: 16 bits set at positions 0, 16, 32 or 48. - if (((value & UINT64_C(0xffffffffffff0000)) == 0) || - ((value & UINT64_C(0xffffffff0000ffff)) == 0) || - ((value & UINT64_C(0xffff0000ffffffff)) == 0) || - ((value & UINT64_C(0x0000ffffffffffff)) == 0)) { - return true; - } - - // Test for movn: NOT(16 bits set at positions 0, 16, 32 or 48). - if ((reg_size == kXRegSize) && - (((~value & UINT64_C(0xffffffffffff0000)) == 0) || - ((~value & UINT64_C(0xffffffff0000ffff)) == 0) || - ((~value & UINT64_C(0xffff0000ffffffff)) == 0) || - ((~value & UINT64_C(0x0000ffffffffffff)) == 0))) { - return true; - } - if ((reg_size == kWRegSize) && - (((value & 0xffff0000) == 0xffff0000) || - ((value & 0x0000ffff) == 0x0000ffff))) { - return true; - } - return false; -} - - -void Disassembler::VisitLogicalShifted(const Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - bool rn_is_zr = RnIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm'HLo"; - - switch (instr->Mask(LogicalShiftedMask)) { - case AND_w: - case AND_x: mnemonic = "and"; break; - case BIC_w: - case BIC_x: mnemonic = "bic"; break; - case EOR_w: - case EOR_x: mnemonic = "eor"; break; - case EON_w: - case EON_x: mnemonic = "eon"; break; - case BICS_w: - case BICS_x: mnemonic = "bics"; break; - case ANDS_w: - case ANDS_x: { - mnemonic = "ands"; - if (rd_is_zr) { - mnemonic = "tst"; - form = "'Rn, 'Rm'HLo"; - } - break; - } - case ORR_w: - case ORR_x: { - mnemonic = "orr"; - if (rn_is_zr && (instr->ImmDPShift() == 0) && (instr->ShiftDP() == LSL)) { - mnemonic = "mov"; - form = "'Rd, 'Rm"; - } - break; - } - case ORN_w: - case ORN_x: { - mnemonic = "orn"; - if (rn_is_zr) { - mnemonic = "mvn"; - form = "'Rd, 'Rm'HLo"; - } - break; - } - default: VIXL_UNREACHABLE(); - } - - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitConditionalCompareRegister(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rn, 'Rm, 'INzcv, 'Cond"; - - switch (instr->Mask(ConditionalCompareRegisterMask)) { - case CCMN_w: - case CCMN_x: mnemonic = "ccmn"; break; - case CCMP_w: - case CCMP_x: mnemonic = "ccmp"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitConditionalCompareImmediate(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rn, 'IP, 'INzcv, 'Cond"; - - switch (instr->Mask(ConditionalCompareImmediateMask)) { - case CCMN_w_imm: - case CCMN_x_imm: mnemonic = "ccmn"; break; - case CCMP_w_imm: - case CCMP_x_imm: mnemonic = "ccmp"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitConditionalSelect(const Instruction* instr) { - bool rnm_is_zr = (RnIsZROrSP(instr) && RmIsZROrSP(instr)); - bool rn_is_rm = (instr->Rn() == instr->Rm()); - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm, 'Cond"; - const char *form_test = "'Rd, 'CInv"; - const char *form_update = "'Rd, 'Rn, 'CInv"; - - Condition cond = static_cast(instr->Condition()); - bool invertible_cond = (cond != al) && (cond != nv); - - switch (instr->Mask(ConditionalSelectMask)) { - case CSEL_w: - case CSEL_x: mnemonic = "csel"; break; - case CSINC_w: - case CSINC_x: { - mnemonic = "csinc"; - if (rnm_is_zr && invertible_cond) { - mnemonic = "cset"; - form = form_test; - } else if (rn_is_rm && invertible_cond) { - mnemonic = "cinc"; - form = form_update; - } - break; - } - case CSINV_w: - case CSINV_x: { - mnemonic = "csinv"; - if (rnm_is_zr && invertible_cond) { - mnemonic = "csetm"; - form = form_test; - } else if (rn_is_rm && invertible_cond) { - mnemonic = "cinv"; - form = form_update; - } - break; - } - case CSNEG_w: - case CSNEG_x: { - mnemonic = "csneg"; - if (rn_is_rm && invertible_cond) { - mnemonic = "cneg"; - form = form_update; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitBitfield(const Instruction* instr) { - unsigned s = instr->ImmS(); - unsigned r = instr->ImmR(); - unsigned rd_size_minus_1 = - ((instr->SixtyFourBits() == 1) ? kXRegSize : kWRegSize) - 1; - const char *mnemonic = ""; - const char *form = ""; - const char *form_shift_right = "'Rd, 'Rn, 'IBr"; - const char *form_extend = "'Rd, 'Wn"; - const char *form_bfiz = "'Rd, 'Rn, 'IBZ-r, 'IBs+1"; - const char *form_bfx = "'Rd, 'Rn, 'IBr, 'IBs-r+1"; - const char *form_lsl = "'Rd, 'Rn, 'IBZ-r"; - - switch (instr->Mask(BitfieldMask)) { - case SBFM_w: - case SBFM_x: { - mnemonic = "sbfx"; - form = form_bfx; - if (r == 0) { - form = form_extend; - if (s == 7) { - mnemonic = "sxtb"; - } else if (s == 15) { - mnemonic = "sxth"; - } else if ((s == 31) && (instr->SixtyFourBits() == 1)) { - mnemonic = "sxtw"; - } else { - form = form_bfx; - } - } else if (s == rd_size_minus_1) { - mnemonic = "asr"; - form = form_shift_right; - } else if (s < r) { - mnemonic = "sbfiz"; - form = form_bfiz; - } - break; - } - case UBFM_w: - case UBFM_x: { - mnemonic = "ubfx"; - form = form_bfx; - if (r == 0) { - form = form_extend; - if (s == 7) { - mnemonic = "uxtb"; - } else if (s == 15) { - mnemonic = "uxth"; - } else { - form = form_bfx; - } - } - if (s == rd_size_minus_1) { - mnemonic = "lsr"; - form = form_shift_right; - } else if (r == s + 1) { - mnemonic = "lsl"; - form = form_lsl; - } else if (s < r) { - mnemonic = "ubfiz"; - form = form_bfiz; - } - break; - } - case BFM_w: - case BFM_x: { - mnemonic = "bfxil"; - form = form_bfx; - if (s < r) { - mnemonic = "bfi"; - form = form_bfiz; - } - } - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitExtract(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm, 'IExtract"; - - switch (instr->Mask(ExtractMask)) { - case EXTR_w: - case EXTR_x: { - if (instr->Rn() == instr->Rm()) { - mnemonic = "ror"; - form = "'Rd, 'Rn, 'IExtract"; - } else { - mnemonic = "extr"; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitPCRelAddressing(const Instruction* instr) { - switch (instr->Mask(PCRelAddressingMask)) { - case ADR: Format(instr, "adr", "'Xd, 'AddrPCRelByte"); break; - case ADRP: Format(instr, "adrp", "'Xd, 'AddrPCRelPage"); break; - default: Format(instr, "unimplemented", "(PCRelAddressing)"); - } -} - - -void Disassembler::VisitConditionalBranch(const Instruction* instr) { - switch (instr->Mask(ConditionalBranchMask)) { - case B_cond: Format(instr, "b.'CBrn", "'BImmCond"); break; - default: VIXL_UNREACHABLE(); - } -} - - -void Disassembler::VisitUnconditionalBranchToRegister( - const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Xn"; - - switch (instr->Mask(UnconditionalBranchToRegisterMask)) { - case BR: mnemonic = "br"; break; - case BLR: mnemonic = "blr"; break; - case RET: { - mnemonic = "ret"; - if (instr->Rn() == kLinkRegCode) { - form = NULL; - } - break; - } - default: form = "(UnconditionalBranchToRegister)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitUnconditionalBranch(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'BImmUncn"; - - switch (instr->Mask(UnconditionalBranchMask)) { - case B: mnemonic = "b"; break; - case BL: mnemonic = "bl"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitDataProcessing1Source(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn"; - - switch (instr->Mask(DataProcessing1SourceMask)) { - #define FORMAT(A, B) \ - case A##_w: \ - case A##_x: mnemonic = B; break; - FORMAT(RBIT, "rbit"); - FORMAT(REV16, "rev16"); - FORMAT(REV, "rev"); - FORMAT(CLZ, "clz"); - FORMAT(CLS, "cls"); - #undef FORMAT - case REV32_x: mnemonic = "rev32"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitDataProcessing2Source(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Rd, 'Rn, 'Rm"; - - switch (instr->Mask(DataProcessing2SourceMask)) { - #define FORMAT(A, B) \ - case A##_w: \ - case A##_x: mnemonic = B; break; - FORMAT(UDIV, "udiv"); - FORMAT(SDIV, "sdiv"); - FORMAT(LSLV, "lsl"); - FORMAT(LSRV, "lsr"); - FORMAT(ASRV, "asr"); - FORMAT(RORV, "ror"); - #undef FORMAT - default: form = "(DataProcessing2Source)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitDataProcessing3Source(const Instruction* instr) { - bool ra_is_zr = RaIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Xd, 'Wn, 'Wm, 'Xa"; - const char *form_rrr = "'Rd, 'Rn, 'Rm"; - const char *form_rrrr = "'Rd, 'Rn, 'Rm, 'Ra"; - const char *form_xww = "'Xd, 'Wn, 'Wm"; - const char *form_xxx = "'Xd, 'Xn, 'Xm"; - - switch (instr->Mask(DataProcessing3SourceMask)) { - case MADD_w: - case MADD_x: { - mnemonic = "madd"; - form = form_rrrr; - if (ra_is_zr) { - mnemonic = "mul"; - form = form_rrr; - } - break; - } - case MSUB_w: - case MSUB_x: { - mnemonic = "msub"; - form = form_rrrr; - if (ra_is_zr) { - mnemonic = "mneg"; - form = form_rrr; - } - break; - } - case SMADDL_x: { - mnemonic = "smaddl"; - if (ra_is_zr) { - mnemonic = "smull"; - form = form_xww; - } - break; - } - case SMSUBL_x: { - mnemonic = "smsubl"; - if (ra_is_zr) { - mnemonic = "smnegl"; - form = form_xww; - } - break; - } - case UMADDL_x: { - mnemonic = "umaddl"; - if (ra_is_zr) { - mnemonic = "umull"; - form = form_xww; - } - break; - } - case UMSUBL_x: { - mnemonic = "umsubl"; - if (ra_is_zr) { - mnemonic = "umnegl"; - form = form_xww; - } - break; - } - case SMULH_x: { - mnemonic = "smulh"; - form = form_xxx; - break; - } - case UMULH_x: { - mnemonic = "umulh"; - form = form_xxx; - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitCompareBranch(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rt, 'BImmCmpa"; - - switch (instr->Mask(CompareBranchMask)) { - case CBZ_w: - case CBZ_x: mnemonic = "cbz"; break; - case CBNZ_w: - case CBNZ_x: mnemonic = "cbnz"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitTestBranch(const Instruction* instr) { - const char *mnemonic = ""; - // If the top bit of the immediate is clear, the tested register is - // disassembled as Wt, otherwise Xt. As the top bit of the immediate is - // encoded in bit 31 of the instruction, we can reuse the Rt form, which - // uses bit 31 (normally "sf") to choose the register size. - const char *form = "'Rt, 'IS, 'BImmTest"; - - switch (instr->Mask(TestBranchMask)) { - case TBZ: mnemonic = "tbz"; break; - case TBNZ: mnemonic = "tbnz"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitMoveWideImmediate(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rd, 'IMoveImm"; - - // Print the shift separately for movk, to make it clear which half word will - // be overwritten. Movn and movz print the computed immediate, which includes - // shift calculation. - switch (instr->Mask(MoveWideImmediateMask)) { - case MOVN_w: - case MOVN_x: - if ((instr->ImmMoveWide()) || (instr->ShiftMoveWide() == 0)) { - if ((instr->SixtyFourBits() == 0) && (instr->ImmMoveWide() == 0xffff)) { - mnemonic = "movn"; - } else { - mnemonic = "mov"; - form = "'Rd, 'IMoveNeg"; - } - } else { - mnemonic = "movn"; - } - break; - case MOVZ_w: - case MOVZ_x: - if ((instr->ImmMoveWide()) || (instr->ShiftMoveWide() == 0)) - mnemonic = "mov"; - else - mnemonic = "movz"; - break; - case MOVK_w: - case MOVK_x: mnemonic = "movk"; form = "'Rd, 'IMoveLSL"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -#define LOAD_STORE_LIST(V) \ - V(STRB_w, "strb", "'Wt") \ - V(STRH_w, "strh", "'Wt") \ - V(STR_w, "str", "'Wt") \ - V(STR_x, "str", "'Xt") \ - V(LDRB_w, "ldrb", "'Wt") \ - V(LDRH_w, "ldrh", "'Wt") \ - V(LDR_w, "ldr", "'Wt") \ - V(LDR_x, "ldr", "'Xt") \ - V(LDRSB_x, "ldrsb", "'Xt") \ - V(LDRSH_x, "ldrsh", "'Xt") \ - V(LDRSW_x, "ldrsw", "'Xt") \ - V(LDRSB_w, "ldrsb", "'Wt") \ - V(LDRSH_w, "ldrsh", "'Wt") \ - V(STR_s, "str", "'St") \ - V(STR_d, "str", "'Dt") \ - V(LDR_s, "ldr", "'St") \ - V(LDR_d, "ldr", "'Dt") - -void Disassembler::VisitLoadStorePreIndex(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePreIndex)"; - - switch (instr->Mask(LoadStorePreIndexMask)) { - #define LS_PREINDEX(A, B, C) \ - case A##_pre: mnemonic = B; form = C ", ['Xns'ILS]!"; break; - LOAD_STORE_LIST(LS_PREINDEX) - #undef LS_PREINDEX - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStorePostIndex(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePostIndex)"; - - switch (instr->Mask(LoadStorePostIndexMask)) { - #define LS_POSTINDEX(A, B, C) \ - case A##_post: mnemonic = B; form = C ", ['Xns]'ILS"; break; - LOAD_STORE_LIST(LS_POSTINDEX) - #undef LS_POSTINDEX - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStoreUnsignedOffset(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStoreUnsignedOffset)"; - - switch (instr->Mask(LoadStoreUnsignedOffsetMask)) { - #define LS_UNSIGNEDOFFSET(A, B, C) \ - case A##_unsigned: mnemonic = B; form = C ", ['Xns'ILU]"; break; - LOAD_STORE_LIST(LS_UNSIGNEDOFFSET) - #undef LS_UNSIGNEDOFFSET - case PRFM_unsigned: mnemonic = "prfm"; form = "'PrefOp, ['Xns'ILU]"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStoreRegisterOffset(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStoreRegisterOffset)"; - - switch (instr->Mask(LoadStoreRegisterOffsetMask)) { - #define LS_REGISTEROFFSET(A, B, C) \ - case A##_reg: mnemonic = B; form = C ", ['Xns, 'Offsetreg]"; break; - LOAD_STORE_LIST(LS_REGISTEROFFSET) - #undef LS_REGISTEROFFSET - case PRFM_reg: mnemonic = "prfm"; form = "'PrefOp, ['Xns, 'Offsetreg]"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStoreUnscaledOffset(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Wt, ['Xns'ILS]"; - const char *form_x = "'Xt, ['Xns'ILS]"; - const char *form_s = "'St, ['Xns'ILS]"; - const char *form_d = "'Dt, ['Xns'ILS]"; - const char *form_prefetch = "'PrefOp, ['Xns'ILS]"; - - switch (instr->Mask(LoadStoreUnscaledOffsetMask)) { - case STURB_w: mnemonic = "sturb"; break; - case STURH_w: mnemonic = "sturh"; break; - case STUR_w: mnemonic = "stur"; break; - case STUR_x: mnemonic = "stur"; form = form_x; break; - case STUR_s: mnemonic = "stur"; form = form_s; break; - case STUR_d: mnemonic = "stur"; form = form_d; break; - case LDURB_w: mnemonic = "ldurb"; break; - case LDURH_w: mnemonic = "ldurh"; break; - case LDUR_w: mnemonic = "ldur"; break; - case LDUR_x: mnemonic = "ldur"; form = form_x; break; - case LDUR_s: mnemonic = "ldur"; form = form_s; break; - case LDUR_d: mnemonic = "ldur"; form = form_d; break; - case LDURSB_x: form = form_x; // Fall through. - case LDURSB_w: mnemonic = "ldursb"; break; - case LDURSH_x: form = form_x; // Fall through. - case LDURSH_w: mnemonic = "ldursh"; break; - case LDURSW_x: mnemonic = "ldursw"; form = form_x; break; - case PRFUM: mnemonic = "prfum"; form = form_prefetch; break; - default: form = "(LoadStoreUnscaledOffset)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadLiteral(const Instruction* instr) { - const char *mnemonic = "ldr"; - const char *form = "(LoadLiteral)"; - - switch (instr->Mask(LoadLiteralMask)) { - case LDR_w_lit: form = "'Wt, 'ILLiteral 'LValue"; break; - case LDR_x_lit: form = "'Xt, 'ILLiteral 'LValue"; break; - case LDR_s_lit: form = "'St, 'ILLiteral 'LValue"; break; - case LDR_d_lit: form = "'Dt, 'ILLiteral 'LValue"; break; - case LDRSW_x_lit: { - mnemonic = "ldrsw"; - form = "'Xt, 'ILLiteral 'LValue"; - break; - } - case PRFM_lit: { - mnemonic = "prfm"; - form = "'PrefOp, 'ILLiteral 'LValue"; - break; - } - default: mnemonic = "unimplemented"; - } - Format(instr, mnemonic, form); -} - - -#define LOAD_STORE_PAIR_LIST(V) \ - V(STP_w, "stp", "'Wt, 'Wt2", "4") \ - V(LDP_w, "ldp", "'Wt, 'Wt2", "4") \ - V(LDPSW_x, "ldpsw", "'Xt, 'Xt2", "4") \ - V(STP_x, "stp", "'Xt, 'Xt2", "8") \ - V(LDP_x, "ldp", "'Xt, 'Xt2", "8") \ - V(STP_s, "stp", "'St, 'St2", "4") \ - V(LDP_s, "ldp", "'St, 'St2", "4") \ - V(STP_d, "stp", "'Dt, 'Dt2", "8") \ - V(LDP_d, "ldp", "'Dt, 'Dt2", "8") - -void Disassembler::VisitLoadStorePairPostIndex(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePairPostIndex)"; - - switch (instr->Mask(LoadStorePairPostIndexMask)) { - #define LSP_POSTINDEX(A, B, C, D) \ - case A##_post: mnemonic = B; form = C ", ['Xns]'ILP" D; break; - LOAD_STORE_PAIR_LIST(LSP_POSTINDEX) - #undef LSP_POSTINDEX - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStorePairPreIndex(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePairPreIndex)"; - - switch (instr->Mask(LoadStorePairPreIndexMask)) { - #define LSP_PREINDEX(A, B, C, D) \ - case A##_pre: mnemonic = B; form = C ", ['Xns'ILP" D "]!"; break; - LOAD_STORE_PAIR_LIST(LSP_PREINDEX) - #undef LSP_PREINDEX - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStorePairOffset(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePairOffset)"; - - switch (instr->Mask(LoadStorePairOffsetMask)) { - #define LSP_OFFSET(A, B, C, D) \ - case A##_off: mnemonic = B; form = C ", ['Xns'ILP" D "]"; break; - LOAD_STORE_PAIR_LIST(LSP_OFFSET) - #undef LSP_OFFSET - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStorePairNonTemporal(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form; - - switch (instr->Mask(LoadStorePairNonTemporalMask)) { - case STNP_w: mnemonic = "stnp"; form = "'Wt, 'Wt2, ['Xns'ILP4]"; break; - case LDNP_w: mnemonic = "ldnp"; form = "'Wt, 'Wt2, ['Xns'ILP4]"; break; - case STNP_x: mnemonic = "stnp"; form = "'Xt, 'Xt2, ['Xns'ILP8]"; break; - case LDNP_x: mnemonic = "ldnp"; form = "'Xt, 'Xt2, ['Xns'ILP8]"; break; - case STNP_s: mnemonic = "stnp"; form = "'St, 'St2, ['Xns'ILP4]"; break; - case LDNP_s: mnemonic = "ldnp"; form = "'St, 'St2, ['Xns'ILP4]"; break; - case STNP_d: mnemonic = "stnp"; form = "'Dt, 'Dt2, ['Xns'ILP8]"; break; - case LDNP_d: mnemonic = "ldnp"; form = "'Dt, 'Dt2, ['Xns'ILP8]"; break; - default: form = "(LoadStorePairNonTemporal)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStoreExclusive(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form; - - switch (instr->Mask(LoadStoreExclusiveMask)) { - case STXRB_w: mnemonic = "stxrb"; form = "'Ws, 'Wt, ['Xns]"; break; - case STXRH_w: mnemonic = "stxrh"; form = "'Ws, 'Wt, ['Xns]"; break; - case STXR_w: mnemonic = "stxr"; form = "'Ws, 'Wt, ['Xns]"; break; - case STXR_x: mnemonic = "stxr"; form = "'Ws, 'Xt, ['Xns]"; break; - case LDXRB_w: mnemonic = "ldxrb"; form = "'Wt, ['Xns]"; break; - case LDXRH_w: mnemonic = "ldxrh"; form = "'Wt, ['Xns]"; break; - case LDXR_w: mnemonic = "ldxr"; form = "'Wt, ['Xns]"; break; - case LDXR_x: mnemonic = "ldxr"; form = "'Xt, ['Xns]"; break; - case STXP_w: mnemonic = "stxp"; form = "'Ws, 'Wt, 'Wt2, ['Xns]"; break; - case STXP_x: mnemonic = "stxp"; form = "'Ws, 'Xt, 'Xt2, ['Xns]"; break; - case LDXP_w: mnemonic = "ldxp"; form = "'Wt, 'Wt2, ['Xns]"; break; - case LDXP_x: mnemonic = "ldxp"; form = "'Xt, 'Xt2, ['Xns]"; break; - case STLXRB_w: mnemonic = "stlxrb"; form = "'Ws, 'Wt, ['Xns]"; break; - case STLXRH_w: mnemonic = "stlxrh"; form = "'Ws, 'Wt, ['Xns]"; break; - case STLXR_w: mnemonic = "stlxr"; form = "'Ws, 'Wt, ['Xns]"; break; - case STLXR_x: mnemonic = "stlxr"; form = "'Ws, 'Xt, ['Xns]"; break; - case LDAXRB_w: mnemonic = "ldaxrb"; form = "'Wt, ['Xns]"; break; - case LDAXRH_w: mnemonic = "ldaxrh"; form = "'Wt, ['Xns]"; break; - case LDAXR_w: mnemonic = "ldaxr"; form = "'Wt, ['Xns]"; break; - case LDAXR_x: mnemonic = "ldaxr"; form = "'Xt, ['Xns]"; break; - case STLXP_w: mnemonic = "stlxp"; form = "'Ws, 'Wt, 'Wt2, ['Xns]"; break; - case STLXP_x: mnemonic = "stlxp"; form = "'Ws, 'Xt, 'Xt2, ['Xns]"; break; - case LDAXP_w: mnemonic = "ldaxp"; form = "'Wt, 'Wt2, ['Xns]"; break; - case LDAXP_x: mnemonic = "ldaxp"; form = "'Xt, 'Xt2, ['Xns]"; break; - case STLRB_w: mnemonic = "stlrb"; form = "'Wt, ['Xns]"; break; - case STLRH_w: mnemonic = "stlrh"; form = "'Wt, ['Xns]"; break; - case STLR_w: mnemonic = "stlr"; form = "'Wt, ['Xns]"; break; - case STLR_x: mnemonic = "stlr"; form = "'Xt, ['Xns]"; break; - case LDARB_w: mnemonic = "ldarb"; form = "'Wt, ['Xns]"; break; - case LDARH_w: mnemonic = "ldarh"; form = "'Wt, ['Xns]"; break; - case LDAR_w: mnemonic = "ldar"; form = "'Wt, ['Xns]"; break; - case LDAR_x: mnemonic = "ldar"; form = "'Xt, ['Xns]"; break; - default: form = "(LoadStoreExclusive)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPCompare(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Fn, 'Fm"; - const char *form_zero = "'Fn, #0.0"; - - switch (instr->Mask(FPCompareMask)) { - case FCMP_s_zero: - case FCMP_d_zero: form = form_zero; // Fall through. - case FCMP_s: - case FCMP_d: mnemonic = "fcmp"; break; - default: form = "(FPCompare)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPConditionalCompare(const Instruction* instr) { - const char *mnemonic = "unmplemented"; - const char *form = "'Fn, 'Fm, 'INzcv, 'Cond"; - - switch (instr->Mask(FPConditionalCompareMask)) { - case FCCMP_s: - case FCCMP_d: mnemonic = "fccmp"; break; - case FCCMPE_s: - case FCCMPE_d: mnemonic = "fccmpe"; break; - default: form = "(FPConditionalCompare)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPConditionalSelect(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Fd, 'Fn, 'Fm, 'Cond"; - - switch (instr->Mask(FPConditionalSelectMask)) { - case FCSEL_s: - case FCSEL_d: mnemonic = "fcsel"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPDataProcessing1Source(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Fd, 'Fn"; - - switch (instr->Mask(FPDataProcessing1SourceMask)) { - #define FORMAT(A, B) \ - case A##_s: \ - case A##_d: mnemonic = B; break; - FORMAT(FMOV, "fmov"); - FORMAT(FABS, "fabs"); - FORMAT(FNEG, "fneg"); - FORMAT(FSQRT, "fsqrt"); - FORMAT(FRINTN, "frintn"); - FORMAT(FRINTP, "frintp"); - FORMAT(FRINTM, "frintm"); - FORMAT(FRINTZ, "frintz"); - FORMAT(FRINTA, "frinta"); - FORMAT(FRINTX, "frintx"); - FORMAT(FRINTI, "frinti"); - #undef FORMAT - case FCVT_ds: mnemonic = "fcvt"; form = "'Dd, 'Sn"; break; - case FCVT_sd: mnemonic = "fcvt"; form = "'Sd, 'Dn"; break; - default: form = "(FPDataProcessing1Source)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPDataProcessing2Source(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Fd, 'Fn, 'Fm"; - - switch (instr->Mask(FPDataProcessing2SourceMask)) { - #define FORMAT(A, B) \ - case A##_s: \ - case A##_d: mnemonic = B; break; - FORMAT(FMUL, "fmul"); - FORMAT(FDIV, "fdiv"); - FORMAT(FADD, "fadd"); - FORMAT(FSUB, "fsub"); - FORMAT(FMAX, "fmax"); - FORMAT(FMIN, "fmin"); - FORMAT(FMAXNM, "fmaxnm"); - FORMAT(FMINNM, "fminnm"); - FORMAT(FNMUL, "fnmul"); - #undef FORMAT - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPDataProcessing3Source(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Fd, 'Fn, 'Fm, 'Fa"; - - switch (instr->Mask(FPDataProcessing3SourceMask)) { - #define FORMAT(A, B) \ - case A##_s: \ - case A##_d: mnemonic = B; break; - FORMAT(FMADD, "fmadd"); - FORMAT(FMSUB, "fmsub"); - FORMAT(FNMADD, "fnmadd"); - FORMAT(FNMSUB, "fnmsub"); - #undef FORMAT - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPImmediate(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "(FPImmediate)"; - - switch (instr->Mask(FPImmediateMask)) { - case FMOV_s_imm: mnemonic = "fmov"; form = "'Sd, 'IFPSingle"; break; - case FMOV_d_imm: mnemonic = "fmov"; form = "'Dd, 'IFPDouble"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPIntegerConvert(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(FPIntegerConvert)"; - const char *form_rf = "'Rd, 'Fn"; - const char *form_fr = "'Fd, 'Rn"; - - switch (instr->Mask(FPIntegerConvertMask)) { - case FMOV_ws: - case FMOV_xd: mnemonic = "fmov"; form = form_rf; break; - case FMOV_sw: - case FMOV_dx: mnemonic = "fmov"; form = form_fr; break; - case FCVTAS_ws: - case FCVTAS_xs: - case FCVTAS_wd: - case FCVTAS_xd: mnemonic = "fcvtas"; form = form_rf; break; - case FCVTAU_ws: - case FCVTAU_xs: - case FCVTAU_wd: - case FCVTAU_xd: mnemonic = "fcvtau"; form = form_rf; break; - case FCVTMS_ws: - case FCVTMS_xs: - case FCVTMS_wd: - case FCVTMS_xd: mnemonic = "fcvtms"; form = form_rf; break; - case FCVTMU_ws: - case FCVTMU_xs: - case FCVTMU_wd: - case FCVTMU_xd: mnemonic = "fcvtmu"; form = form_rf; break; - case FCVTNS_ws: - case FCVTNS_xs: - case FCVTNS_wd: - case FCVTNS_xd: mnemonic = "fcvtns"; form = form_rf; break; - case FCVTNU_ws: - case FCVTNU_xs: - case FCVTNU_wd: - case FCVTNU_xd: mnemonic = "fcvtnu"; form = form_rf; break; - case FCVTZU_xd: - case FCVTZU_ws: - case FCVTZU_wd: - case FCVTZU_xs: mnemonic = "fcvtzu"; form = form_rf; break; - case FCVTZS_xd: - case FCVTZS_wd: - case FCVTZS_xs: - case FCVTZS_ws: mnemonic = "fcvtzs"; form = form_rf; break; - case SCVTF_sw: - case SCVTF_sx: - case SCVTF_dw: - case SCVTF_dx: mnemonic = "scvtf"; form = form_fr; break; - case UCVTF_sw: - case UCVTF_sx: - case UCVTF_dw: - case UCVTF_dx: mnemonic = "ucvtf"; form = form_fr; break; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPFixedPointConvert(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rd, 'Fn, 'IFPFBits"; - const char *form_fr = "'Fd, 'Rn, 'IFPFBits"; - - switch (instr->Mask(FPFixedPointConvertMask)) { - case FCVTZS_ws_fixed: - case FCVTZS_xs_fixed: - case FCVTZS_wd_fixed: - case FCVTZS_xd_fixed: mnemonic = "fcvtzs"; break; - case FCVTZU_ws_fixed: - case FCVTZU_xs_fixed: - case FCVTZU_wd_fixed: - case FCVTZU_xd_fixed: mnemonic = "fcvtzu"; break; - case SCVTF_sw_fixed: - case SCVTF_sx_fixed: - case SCVTF_dw_fixed: - case SCVTF_dx_fixed: mnemonic = "scvtf"; form = form_fr; break; - case UCVTF_sw_fixed: - case UCVTF_sx_fixed: - case UCVTF_dw_fixed: - case UCVTF_dx_fixed: mnemonic = "ucvtf"; form = form_fr; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitSystem(const Instruction* instr) { - // Some system instructions hijack their Op and Cp fields to represent a - // range of immediates instead of indicating a different instruction. This - // makes the decoding tricky. - const char *mnemonic = "unimplemented"; - const char *form = "(System)"; - - if (instr->Mask(SystemExclusiveMonitorFMask) == SystemExclusiveMonitorFixed) { - switch (instr->Mask(SystemExclusiveMonitorMask)) { - case CLREX: { - mnemonic = "clrex"; - form = (instr->CRm() == 0xf) ? NULL : "'IX"; - break; - } - } - } else if (instr->Mask(SystemSysRegFMask) == SystemSysRegFixed) { - switch (instr->Mask(SystemSysRegMask)) { - case MRS: { - mnemonic = "mrs"; - switch (instr->ImmSystemRegister()) { - case NZCV: form = "'Xt, nzcv"; break; - case FPCR: form = "'Xt, fpcr"; break; - default: form = "'Xt, (unknown)"; break; - } - break; - } - case MSR: { - mnemonic = "msr"; - switch (instr->ImmSystemRegister()) { - case NZCV: form = "nzcv, 'Xt"; break; - case FPCR: form = "fpcr, 'Xt"; break; - default: form = "(unknown), 'Xt"; break; - } - break; - } - } - } else if (instr->Mask(SystemHintFMask) == SystemHintFixed) { - switch (instr->ImmHint()) { - case NOP: { - mnemonic = "nop"; - form = NULL; - break; - } - } - } else if (instr->Mask(MemBarrierFMask) == MemBarrierFixed) { - switch (instr->Mask(MemBarrierMask)) { - case DMB: { - mnemonic = "dmb"; - form = "'M"; - break; - } - case DSB: { - mnemonic = "dsb"; - form = "'M"; - break; - } - case ISB: { - mnemonic = "isb"; - form = NULL; - break; - } - } - } - - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitException(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'IDebug"; - - switch (instr->Mask(ExceptionMask)) { - case HLT: mnemonic = "hlt"; break; - case BRK: mnemonic = "brk"; break; - case SVC: mnemonic = "svc"; break; - case HVC: mnemonic = "hvc"; break; - case SMC: mnemonic = "smc"; break; - case DCPS1: mnemonic = "dcps1"; form = "{'IDebug}"; break; - case DCPS2: mnemonic = "dcps2"; form = "{'IDebug}"; break; - case DCPS3: mnemonic = "dcps3"; form = "{'IDebug}"; break; - default: form = "(Exception)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitUnimplemented(const Instruction* instr) { - Format(instr, "unimplemented", "(Unimplemented)"); -} - - -void Disassembler::VisitUnallocated(const Instruction* instr) { - Format(instr, "unallocated", "(Unallocated)"); -} - - -void Disassembler::ProcessOutput(const Instruction* /*instr*/) { - // The base disasm does nothing more than disassembling into a buffer. -} - - -void Disassembler::AppendRegisterNameToOutput(const Instruction* instr, - const CPURegister& reg) { - USE(instr); - VIXL_ASSERT(reg.IsValid()); - char reg_char; - - if (reg.IsRegister()) { - reg_char = reg.Is64Bits() ? 'x' : 'w'; - } else { - VIXL_ASSERT(reg.IsFPRegister()); - reg_char = reg.Is64Bits() ? 'd' : 's'; - } - - if (reg.IsFPRegister() || !(reg.Aliases(sp) || reg.Aliases(xzr))) { - // A normal register: w0 - w30, x0 - x30, s0 - s31, d0 - d31. - AppendToOutput("%c%d", reg_char, reg.code()); - } else if (reg.Aliases(sp)) { - // Disassemble w31/x31 as stack pointer wsp/sp. - AppendToOutput("%s", reg.Is64Bits() ? "sp" : "wsp"); - } else { - // Disassemble w31/x31 as zero register wzr/xzr. - AppendToOutput("%czr", reg_char); - } -} - - -void Disassembler::AppendPCRelativeOffsetToOutput(const Instruction* instr, - int64_t offset) { - USE(instr); - char sign = (offset < 0) ? '-' : '+'; - AppendToOutput("#%c0x%" PRIx64, sign, std::abs(offset)); -} - - -void Disassembler::AppendAddressToOutput(const Instruction* instr, - const void* addr) { - USE(instr); - AppendToOutput("(addr 0x%" PRIxPTR ")", reinterpret_cast(addr)); -} - - -void Disassembler::AppendCodeAddressToOutput(const Instruction* instr, - const void* addr) { - AppendAddressToOutput(instr, addr); -} - - -void Disassembler::AppendDataAddressToOutput(const Instruction* instr, - const void* addr) { - AppendAddressToOutput(instr, addr); -} - - -void Disassembler::AppendCodeRelativeAddressToOutput(const Instruction* instr, - const void* addr) { - USE(instr); - int64_t rel_addr = CodeRelativeAddress(addr); - if (rel_addr >= 0) { - AppendToOutput("(addr 0x%" PRIx64 ")", rel_addr); - } else { - AppendToOutput("(addr -0x%" PRIx64 ")", -rel_addr); - } -} - - -void Disassembler::AppendCodeRelativeCodeAddressToOutput( - const Instruction* instr, const void* addr) { - AppendCodeRelativeAddressToOutput(instr, addr); -} - - -void Disassembler::AppendCodeRelativeDataAddressToOutput( - const Instruction* instr, const void* addr) { - AppendCodeRelativeAddressToOutput(instr, addr); -} - - -void Disassembler::MapCodeAddress(int64_t base_address, - const Instruction* instr_address) { - set_code_address_offset( - base_address - reinterpret_cast(instr_address)); -} -int64_t Disassembler::CodeRelativeAddress(const void* addr) { - return reinterpret_cast(addr) + code_address_offset(); -} - - -void Disassembler::Format(const Instruction* instr, const char* mnemonic, - const char* format) { - VIXL_ASSERT(mnemonic != NULL); - ResetOutput(); - Substitute(instr, mnemonic); - if (format != NULL) { - buffer_[buffer_pos_++] = ' '; - Substitute(instr, format); - } - buffer_[buffer_pos_] = 0; - ProcessOutput(instr); -} - - -void Disassembler::Substitute(const Instruction* instr, const char* string) { - char chr = *string++; - while (chr != '\0') { - if (chr == '\'') { - string += SubstituteField(instr, string); - } else { - buffer_[buffer_pos_++] = chr; - } - chr = *string++; - } -} - - -int Disassembler::SubstituteField(const Instruction* instr, - const char* format) { - switch (format[0]) { - case 'R': // Register. X or W, selected by sf bit. - case 'F': // FP Register. S or D, selected by type field. - case 'W': - case 'X': - case 'S': - case 'D': return SubstituteRegisterField(instr, format); - case 'I': return SubstituteImmediateField(instr, format); - case 'L': return SubstituteLiteralField(instr, format); - case 'H': return SubstituteShiftField(instr, format); - case 'P': return SubstitutePrefetchField(instr, format); - case 'C': return SubstituteConditionField(instr, format); - case 'E': return SubstituteExtendField(instr, format); - case 'A': return SubstitutePCRelAddressField(instr, format); - case 'B': return SubstituteBranchTargetField(instr, format); - case 'O': return SubstituteLSRegOffsetField(instr, format); - case 'M': return SubstituteBarrierField(instr, format); - default: { - VIXL_UNREACHABLE(); - return 1; - } - } -} - - -int Disassembler::SubstituteRegisterField(const Instruction* instr, - const char* format) { - unsigned reg_num = 0; - unsigned field_len = 2; - switch (format[1]) { - case 'd': reg_num = instr->Rd(); break; - case 'n': reg_num = instr->Rn(); break; - case 'm': reg_num = instr->Rm(); break; - case 'a': reg_num = instr->Ra(); break; - case 's': reg_num = instr->Rs(); break; - case 't': { - if (format[2] == '2') { - reg_num = instr->Rt2(); - field_len = 3; - } else { - reg_num = instr->Rt(); - } - break; - } - default: VIXL_UNREACHABLE(); - } - - // Increase field length for registers tagged as stack. - if (format[2] == 's') { - field_len = 3; - } - - CPURegister::RegisterType reg_type; - unsigned reg_size; - - if (format[0] == 'R') { - // Register type is R: use sf bit to choose X and W. - reg_type = CPURegister::kRegister; - reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; - } else if (format[0] == 'F') { - // Floating-point register: use type field to choose S or D. - reg_type = CPURegister::kFPRegister; - reg_size = ((instr->FPType() & 1) == 0) ? kSRegSize : kDRegSize; - } else { - // The register type is specified. - switch (format[0]) { - case 'W': - reg_type = CPURegister::kRegister; reg_size = kWRegSize; break; - case 'X': - reg_type = CPURegister::kRegister; reg_size = kXRegSize; break; - case 'S': - reg_type = CPURegister::kFPRegister; reg_size = kSRegSize; break; - case 'D': - reg_type = CPURegister::kFPRegister; reg_size = kDRegSize; break; - default: - VIXL_UNREACHABLE(); - reg_type = CPURegister::kRegister; - reg_size = kXRegSize; - } - } - - if ((reg_type == CPURegister::kRegister) && - (reg_num == kZeroRegCode) && (format[2] == 's')) { - reg_num = kSPRegInternalCode; - } - - AppendRegisterNameToOutput(instr, CPURegister(reg_num, reg_size, reg_type)); - - return field_len; -} - - -int Disassembler::SubstituteImmediateField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(format[0] == 'I'); - - switch (format[1]) { - case 'M': { // IMoveImm, IMoveNeg or IMoveLSL. - if (format[5] == 'L') { - AppendToOutput("#0x%" PRIx64, instr->ImmMoveWide()); - if (instr->ShiftMoveWide() > 0) { - AppendToOutput(", lsl #%" PRId64, 16 * instr->ShiftMoveWide()); - } - } else { - VIXL_ASSERT((format[5] == 'I') || (format[5] == 'N')); - uint64_t imm = instr->ImmMoveWide() << (16 * instr->ShiftMoveWide()); - if (format[5] == 'N') - imm = ~imm; - if (!instr->SixtyFourBits()) - imm &= UINT64_C(0xffffffff); - AppendToOutput("#0x%" PRIx64, imm); - } - return 8; - } - case 'L': { - switch (format[2]) { - case 'L': { // ILLiteral - Immediate Load Literal. - AppendToOutput("pc%+" PRId64, - instr->ImmLLiteral() << kLiteralEntrySizeLog2); - return 9; - } - case 'S': { // ILS - Immediate Load/Store. - if (instr->ImmLS() != 0) { - AppendToOutput(", #%" PRId64, instr->ImmLS()); - } - return 3; - } - case 'P': { // ILPx - Immediate Load/Store Pair, x = access size. - if (instr->ImmLSPair() != 0) { - // format[3] is the scale value. Convert to a number. - int scale = format[3] - 0x30; - AppendToOutput(", #%" PRId64, instr->ImmLSPair() * scale); - } - return 4; - } - case 'U': { // ILU - Immediate Load/Store Unsigned. - if (instr->ImmLSUnsigned() != 0) { - AppendToOutput(", #%" PRIu64, - instr->ImmLSUnsigned() << instr->SizeLS()); - } - return 3; - } - } - } - case 'C': { // ICondB - Immediate Conditional Branch. - int64_t offset = instr->ImmCondBranch() << 2; - AppendPCRelativeOffsetToOutput(instr, offset); - return 6; - } - case 'A': { // IAddSub. - VIXL_ASSERT(instr->ShiftAddSub() <= 1); - int64_t imm = instr->ImmAddSub() << (12 * instr->ShiftAddSub()); - AppendToOutput("#0x%" PRIx64 " (%" PRId64 ")", imm, imm); - return 7; - } - case 'F': { // IFPSingle, IFPDouble or IFPFBits. - if (format[3] == 'F') { // IFPFbits. - AppendToOutput("#%" PRId64, 64 - instr->FPScale()); - return 8; - } else { - AppendToOutput("#0x%" PRIx64 " (%.4f)", instr->ImmFP(), - format[3] == 'S' ? instr->ImmFP32() : instr->ImmFP64()); - return 9; - } - } - case 'T': { // ITri - Immediate Triangular Encoded. - AppendToOutput("#0x%" PRIx64, instr->ImmLogical()); - return 4; - } - case 'N': { // INzcv. - int nzcv = (instr->Nzcv() << Flags_offset); - AppendToOutput("#%c%c%c%c", ((nzcv & NFlag) == 0) ? 'n' : 'N', - ((nzcv & ZFlag) == 0) ? 'z' : 'Z', - ((nzcv & CFlag) == 0) ? 'c' : 'C', - ((nzcv & VFlag) == 0) ? 'v' : 'V'); - return 5; - } - case 'P': { // IP - Conditional compare. - AppendToOutput("#%" PRId64, instr->ImmCondCmp()); - return 2; - } - case 'B': { // Bitfields. - return SubstituteBitfieldImmediateField(instr, format); - } - case 'E': { // IExtract. - AppendToOutput("#%" PRId64, instr->ImmS()); - return 8; - } - case 'S': { // IS - Test and branch bit. - AppendToOutput("#%" PRId64, (instr->ImmTestBranchBit5() << 5) | - instr->ImmTestBranchBit40()); - return 2; - } - case 'D': { // IDebug - HLT and BRK instructions. - AppendToOutput("#0x%" PRIx64, instr->ImmException()); - return 6; - } - case 'X': { // IX - CLREX instruction. - AppendToOutput("#0x%" PRIx64, instr->CRm()); - return 2; - } - default: { - VIXL_UNIMPLEMENTED(); - return 0; - } - } -} - - -int Disassembler::SubstituteBitfieldImmediateField(const Instruction* instr, - const char* format) { - VIXL_ASSERT((format[0] == 'I') && (format[1] == 'B')); - unsigned r = instr->ImmR(); - unsigned s = instr->ImmS(); - - switch (format[2]) { - case 'r': { // IBr. - AppendToOutput("#%d", r); - return 3; - } - case 's': { // IBs+1 or IBs-r+1. - if (format[3] == '+') { - AppendToOutput("#%d", s + 1); - return 5; - } else { - VIXL_ASSERT(format[3] == '-'); - AppendToOutput("#%d", s - r + 1); - return 7; - } - } - case 'Z': { // IBZ-r. - VIXL_ASSERT((format[3] == '-') && (format[4] == 'r')); - unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSize : kWRegSize; - AppendToOutput("#%d", reg_size - r); - return 5; - } - default: { - VIXL_UNREACHABLE(); - return 0; - } - } -} - - -int Disassembler::SubstituteLiteralField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(strncmp(format, "LValue", 6) == 0); - USE(format); - - const void * address = instr->LiteralAddress(); - switch (instr->Mask(LoadLiteralMask)) { - case LDR_w_lit: - case LDR_x_lit: - case LDRSW_x_lit: - case LDR_s_lit: - case LDR_d_lit: - AppendCodeRelativeDataAddressToOutput(instr, address); - break; - case PRFM_lit: { - // Use the prefetch hint to decide how to print the address. - switch (instr->PrefetchHint()) { - case 0x0: // PLD: prefetch for load. - case 0x2: // PST: prepare for store. - AppendCodeRelativeDataAddressToOutput(instr, address); - break; - case 0x1: // PLI: preload instructions. - AppendCodeRelativeCodeAddressToOutput(instr, address); - break; - case 0x3: // Unallocated hint. - AppendCodeRelativeAddressToOutput(instr, address); - break; - } - break; - } - default: - VIXL_UNREACHABLE(); - } - - return 6; -} - - -int Disassembler::SubstituteShiftField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(format[0] == 'H'); - VIXL_ASSERT(instr->ShiftDP() <= 0x3); - - switch (format[1]) { - case 'D': { // HDP. - VIXL_ASSERT(instr->ShiftDP() != ROR); - } // Fall through. - case 'L': { // HLo. - if (instr->ImmDPShift() != 0) { - const char* shift_type[] = {"lsl", "lsr", "asr", "ror"}; - AppendToOutput(", %s #%" PRId64, shift_type[instr->ShiftDP()], - instr->ImmDPShift()); - } - return 3; - } - default: - VIXL_UNIMPLEMENTED(); - return 0; - } -} - - -int Disassembler::SubstituteConditionField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(format[0] == 'C'); - const char* condition_code[] = { "eq", "ne", "hs", "lo", - "mi", "pl", "vs", "vc", - "hi", "ls", "ge", "lt", - "gt", "le", "al", "nv" }; - int cond; - switch (format[1]) { - case 'B': cond = instr->ConditionBranch(); break; - case 'I': { - cond = InvertCondition(static_cast(instr->Condition())); - break; - } - default: cond = instr->Condition(); - } - AppendToOutput("%s", condition_code[cond]); - return 4; -} - - -int Disassembler::SubstitutePCRelAddressField(const Instruction* instr, - const char* format) { - VIXL_ASSERT((strcmp(format, "AddrPCRelByte") == 0) || // Used by `adr`. - (strcmp(format, "AddrPCRelPage") == 0)); // Used by `adrp`. - - int64_t offset = instr->ImmPCRel(); - - // Compute the target address based on the effective address (after applying - // code_address_offset). This is required for correct behaviour of adrp. - const Instruction* base = instr + code_address_offset(); - if (format[9] == 'P') { - offset *= kPageSize; - base = AlignDown(base, kPageSize); - } - // Strip code_address_offset before printing, so we can use the - // semantically-correct AppendCodeRelativeAddressToOutput. - const void* target = - reinterpret_cast(base + offset - code_address_offset()); - - AppendPCRelativeOffsetToOutput(instr, offset); - AppendToOutput(" "); - AppendCodeRelativeAddressToOutput(instr, target); - return 13; -} - - -int Disassembler::SubstituteBranchTargetField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(strncmp(format, "BImm", 4) == 0); - - int64_t offset = 0; - switch (format[5]) { - // BImmUncn - unconditional branch immediate. - case 'n': offset = instr->ImmUncondBranch(); break; - // BImmCond - conditional branch immediate. - case 'o': offset = instr->ImmCondBranch(); break; - // BImmCmpa - compare and branch immediate. - case 'm': offset = instr->ImmCmpBranch(); break; - // BImmTest - test and branch immediate. - case 'e': offset = instr->ImmTestBranch(); break; - default: VIXL_UNIMPLEMENTED(); - } - offset <<= kInstructionSizeLog2; - const void* target_address = reinterpret_cast(instr + offset); - VIXL_STATIC_ASSERT(sizeof(*instr) == 1); - - AppendPCRelativeOffsetToOutput(instr, offset); - AppendToOutput(" "); - AppendCodeRelativeCodeAddressToOutput(instr, target_address); - - return 8; -} - - -int Disassembler::SubstituteExtendField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(strncmp(format, "Ext", 3) == 0); - VIXL_ASSERT(instr->ExtendMode() <= 7); - USE(format); - - const char* extend_mode[] = { "uxtb", "uxth", "uxtw", "uxtx", - "sxtb", "sxth", "sxtw", "sxtx" }; - - // If rd or rn is SP, uxtw on 32-bit registers and uxtx on 64-bit - // registers becomes lsl. - if (((instr->Rd() == kZeroRegCode) || (instr->Rn() == kZeroRegCode)) && - (((instr->ExtendMode() == UXTW) && (instr->SixtyFourBits() == 0)) || - (instr->ExtendMode() == UXTX))) { - if (instr->ImmExtendShift() > 0) { - AppendToOutput(", lsl #%" PRId64, instr->ImmExtendShift()); - } - } else { - AppendToOutput(", %s", extend_mode[instr->ExtendMode()]); - if (instr->ImmExtendShift() > 0) { - AppendToOutput(" #%" PRId64, instr->ImmExtendShift()); - } - } - return 3; -} - - -int Disassembler::SubstituteLSRegOffsetField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(strncmp(format, "Offsetreg", 9) == 0); - const char* extend_mode[] = { "undefined", "undefined", "uxtw", "lsl", - "undefined", "undefined", "sxtw", "sxtx" }; - USE(format); - - unsigned shift = instr->ImmShiftLS(); - Extend ext = static_cast(instr->ExtendMode()); - char reg_type = ((ext == UXTW) || (ext == SXTW)) ? 'w' : 'x'; - - unsigned rm = instr->Rm(); - if (rm == kZeroRegCode) { - AppendToOutput("%czr", reg_type); - } else { - AppendToOutput("%c%d", reg_type, rm); - } - - // Extend mode UXTX is an alias for shift mode LSL here. - if (!((ext == UXTX) && (shift == 0))) { - AppendToOutput(", %s", extend_mode[ext]); - if (shift != 0) { - AppendToOutput(" #%" PRId64, instr->SizeLS()); - } - } - return 9; -} - - -int Disassembler::SubstitutePrefetchField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(format[0] == 'P'); - USE(format); - - static const char* hints[] = {"ld", "li", "st"}; - static const char* stream_options[] = {"keep", "strm"}; - - unsigned hint = instr->PrefetchHint(); - unsigned target = instr->PrefetchTarget() + 1; - unsigned stream = instr->PrefetchStream(); - - if ((hint >= (sizeof(hints) / sizeof(hints[0]))) || (target > 3)) { - // Unallocated prefetch operations. - int prefetch_mode = instr->ImmPrefetchOperation(); - AppendToOutput("#0b%c%c%c%c%c", - (prefetch_mode & (1 << 4)) ? '1' : '0', - (prefetch_mode & (1 << 3)) ? '1' : '0', - (prefetch_mode & (1 << 2)) ? '1' : '0', - (prefetch_mode & (1 << 1)) ? '1' : '0', - (prefetch_mode & (1 << 0)) ? '1' : '0'); - } else { - VIXL_ASSERT(stream < (sizeof(stream_options) / sizeof(stream_options[0]))); - AppendToOutput("p%sl%d%s", hints[hint], target, stream_options[stream]); - } - return 6; -} - -int Disassembler::SubstituteBarrierField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(format[0] == 'M'); - USE(format); - - static const char* options[4][4] = { - { "sy (0b0000)", "oshld", "oshst", "osh" }, - { "sy (0b0100)", "nshld", "nshst", "nsh" }, - { "sy (0b1000)", "ishld", "ishst", "ish" }, - { "sy (0b1100)", "ld", "st", "sy" } - }; - int domain = instr->ImmBarrierDomain(); - int type = instr->ImmBarrierType(); - - AppendToOutput("%s", options[domain][type]); - return 1; -} - -void Disassembler::ResetOutput() { - buffer_pos_ = 0; - buffer_[buffer_pos_] = 0; -} - - -void Disassembler::AppendToOutput(const char* format, ...) { - va_list args; - va_start(args, format); - buffer_pos_ += vsnprintf(&buffer_[buffer_pos_], buffer_size_, format, args); - va_end(args); -} - - -void PrintDisassembler::ProcessOutput(const Instruction* instr) { - fprintf(stream_, "0x%016" PRIx64 " %08" PRIx32 "\t\t%s\n", - reinterpret_cast(instr), - instr->InstructionBits(), - GetOutput()); -} -} // namespace vixl diff --git a/qemu/disas/libvixl/a64/disasm-a64.h b/qemu/disas/libvixl/a64/disasm-a64.h deleted file mode 100644 index ddfe98be1..000000000 --- a/qemu/disas/libvixl/a64/disasm-a64.h +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2013, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef VIXL_A64_DISASM_A64_H -#define VIXL_A64_DISASM_A64_H - -#include "globals.h" -#include "utils.h" -#include "instructions-a64.h" -#include "decoder-a64.h" -#include "assembler-a64.h" - -namespace vixl { - -class Disassembler: public DecoderVisitor { - public: - Disassembler(); - Disassembler(char* text_buffer, int buffer_size); - virtual ~Disassembler(); - char* GetOutput(); - - // Declare all Visitor functions. - #define DECLARE(A) virtual void Visit##A(const Instruction* instr); - VISITOR_LIST(DECLARE) - #undef DECLARE - - protected: - virtual void ProcessOutput(const Instruction* instr); - - // Default output functions. The functions below implement a default way of - // printing elements in the disassembly. A sub-class can override these to - // customize the disassembly output. - - // Prints the name of a register. - virtual void AppendRegisterNameToOutput(const Instruction* instr, - const CPURegister& reg); - - // Prints a PC-relative offset. This is used for example when disassembling - // branches to immediate offsets. - virtual void AppendPCRelativeOffsetToOutput(const Instruction* instr, - int64_t offset); - - // Prints an address, in the general case. It can be code or data. This is - // used for example to print the target address of an ADR instruction. - virtual void AppendCodeRelativeAddressToOutput(const Instruction* instr, - const void* addr); - - // Prints the address of some code. - // This is used for example to print the target address of a branch to an - // immediate offset. - // A sub-class can for example override this method to lookup the address and - // print an appropriate name. - virtual void AppendCodeRelativeCodeAddressToOutput(const Instruction* instr, - const void* addr); - - // Prints the address of some data. - // This is used for example to print the source address of a load literal - // instruction. - virtual void AppendCodeRelativeDataAddressToOutput(const Instruction* instr, - const void* addr); - - // Same as the above, but for addresses that are not relative to the code - // buffer. They are currently not used by VIXL. - virtual void AppendAddressToOutput(const Instruction* instr, - const void* addr); - virtual void AppendCodeAddressToOutput(const Instruction* instr, - const void* addr); - virtual void AppendDataAddressToOutput(const Instruction* instr, - const void* addr); - - public: - // Get/Set the offset that should be added to code addresses when printing - // code-relative addresses in the AppendCodeRelativeAddressToOutput() - // helpers. - // Below is an example of how a branch immediate instruction in memory at - // address 0xb010200 would disassemble with different offsets. - // Base address | Disassembly - // 0x0 | 0xb010200: b #+0xcc (addr 0xb0102cc) - // 0x10000 | 0xb000200: b #+0xcc (addr 0xb0002cc) - // 0xb010200 | 0x0: b #+0xcc (addr 0xcc) - void MapCodeAddress(int64_t base_address, const Instruction* instr_address); - int64_t CodeRelativeAddress(const void* instr); - - private: - void Format( - const Instruction* instr, const char* mnemonic, const char* format); - void Substitute(const Instruction* instr, const char* string); - int SubstituteField(const Instruction* instr, const char* format); - int SubstituteRegisterField(const Instruction* instr, const char* format); - int SubstituteImmediateField(const Instruction* instr, const char* format); - int SubstituteLiteralField(const Instruction* instr, const char* format); - int SubstituteBitfieldImmediateField( - const Instruction* instr, const char* format); - int SubstituteShiftField(const Instruction* instr, const char* format); - int SubstituteExtendField(const Instruction* instr, const char* format); - int SubstituteConditionField(const Instruction* instr, const char* format); - int SubstitutePCRelAddressField(const Instruction* instr, const char* format); - int SubstituteBranchTargetField(const Instruction* instr, const char* format); - int SubstituteLSRegOffsetField(const Instruction* instr, const char* format); - int SubstitutePrefetchField(const Instruction* instr, const char* format); - int SubstituteBarrierField(const Instruction* instr, const char* format); - - bool RdIsZROrSP(const Instruction* instr) const { - return (instr->Rd() == kZeroRegCode); - } - - bool RnIsZROrSP(const Instruction* instr) const { - return (instr->Rn() == kZeroRegCode); - } - - bool RmIsZROrSP(const Instruction* instr) const { - return (instr->Rm() == kZeroRegCode); - } - - bool RaIsZROrSP(const Instruction* instr) const { - return (instr->Ra() == kZeroRegCode); - } - - bool IsMovzMovnImm(unsigned reg_size, uint64_t value); - - int64_t code_address_offset() const { return code_address_offset_; } - - protected: - void ResetOutput(); - void AppendToOutput(const char* string, ...) PRINTF_CHECK(2, 3); - - void set_code_address_offset(int64_t code_address_offset) { - code_address_offset_ = code_address_offset; - } - - char* buffer_; - uint32_t buffer_pos_; - uint32_t buffer_size_; - bool own_buffer_; - - int64_t code_address_offset_; -}; - - -class PrintDisassembler: public Disassembler { - public: - explicit PrintDisassembler(FILE* stream) : stream_(stream) { } - virtual ~PrintDisassembler() { } - - protected: - virtual void ProcessOutput(const Instruction* instr); - - private: - FILE *stream_; -}; -} // namespace vixl - -#endif // VIXL_A64_DISASM_A64_H diff --git a/qemu/disas/libvixl/a64/instructions-a64.cc b/qemu/disas/libvixl/a64/instructions-a64.cc deleted file mode 100644 index b09188683..000000000 --- a/qemu/disas/libvixl/a64/instructions-a64.cc +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright 2013, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "a64/instructions-a64.h" -#include "a64/assembler-a64.h" - -namespace vixl { - - -// Floating-point infinity values. -const float kFP32PositiveInfinity = rawbits_to_float(0x7f800000); -const float kFP32NegativeInfinity = rawbits_to_float(0xff800000); -const double kFP64PositiveInfinity = - rawbits_to_double(UINT64_C(0x7ff0000000000000)); -const double kFP64NegativeInfinity = - rawbits_to_double(UINT64_C(0xfff0000000000000)); - - -// The default NaN values (for FPCR.DN=1). -const double kFP64DefaultNaN = rawbits_to_double(UINT64_C(0x7ff8000000000000)); -const float kFP32DefaultNaN = rawbits_to_float(0x7fc00000); - - -static uint64_t RotateRight(uint64_t value, - unsigned int rotate, - unsigned int width) { - VIXL_ASSERT(width <= 64); - rotate &= 63; - return ((value & ((UINT64_C(1) << rotate) - 1)) << - (width - rotate)) | (value >> rotate); -} - - -static uint64_t RepeatBitsAcrossReg(unsigned reg_size, - uint64_t value, - unsigned width) { - VIXL_ASSERT((width == 2) || (width == 4) || (width == 8) || (width == 16) || - (width == 32)); - VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); - uint64_t result = value & ((UINT64_C(1) << width) - 1); - for (unsigned i = width; i < reg_size; i *= 2) { - result |= (result << i); - } - return result; -} - - -bool Instruction::IsLoad() const { - if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) { - return false; - } - - if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) { - return Mask(LoadStorePairLBit) != 0; - } else { - LoadStoreOp op = static_cast(Mask(LoadStoreOpMask)); - switch (op) { - case LDRB_w: - case LDRH_w: - case LDR_w: - case LDR_x: - case LDRSB_w: - case LDRSB_x: - case LDRSH_w: - case LDRSH_x: - case LDRSW_x: - case LDR_s: - case LDR_d: return true; - default: return false; - } - } -} - - -bool Instruction::IsStore() const { - if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) { - return false; - } - - if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) { - return Mask(LoadStorePairLBit) == 0; - } else { - LoadStoreOp op = static_cast(Mask(LoadStoreOpMask)); - switch (op) { - case STRB_w: - case STRH_w: - case STR_w: - case STR_x: - case STR_s: - case STR_d: return true; - default: return false; - } - } -} - - -// Logical immediates can't encode zero, so a return value of zero is used to -// indicate a failure case. Specifically, where the constraints on imm_s are -// not met. -uint64_t Instruction::ImmLogical() const { - unsigned reg_size = SixtyFourBits() ? kXRegSize : kWRegSize; - int64_t n = BitN(); - int64_t imm_s = ImmSetBits(); - int64_t imm_r = ImmRotate(); - - // An integer is constructed from the n, imm_s and imm_r bits according to - // the following table: - // - // N imms immr size S R - // 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr) - // 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr) - // 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr) - // 0 110sss xxxrrr 8 UInt(sss) UInt(rrr) - // 0 1110ss xxxxrr 4 UInt(ss) UInt(rr) - // 0 11110s xxxxxr 2 UInt(s) UInt(r) - // (s bits must not be all set) - // - // A pattern is constructed of size bits, where the least significant S+1 - // bits are set. The pattern is rotated right by R, and repeated across a - // 32 or 64-bit value, depending on destination register width. - // - - if (n == 1) { - if (imm_s == 0x3F) { - return 0; - } - uint64_t bits = (UINT64_C(1) << (imm_s + 1)) - 1; - return RotateRight(bits, imm_r, 64); - } else { - if ((imm_s >> 1) == 0x1F) { - return 0; - } - for (int width = 0x20; width >= 0x2; width >>= 1) { - if ((imm_s & width) == 0) { - int mask = width - 1; - if ((imm_s & mask) == mask) { - return 0; - } - uint64_t bits = (UINT64_C(1) << ((imm_s & mask) + 1)) - 1; - return RepeatBitsAcrossReg(reg_size, - RotateRight(bits, imm_r & mask, width), - width); - } - } - } - VIXL_UNREACHABLE(); - return 0; -} - - -float Instruction::ImmFP32() const { - // ImmFP: abcdefgh (8 bits) - // Single: aBbb.bbbc.defg.h000.0000.0000.0000.0000 (32 bits) - // where B is b ^ 1 - uint32_t bits = ImmFP(); - uint32_t bit7 = (bits >> 7) & 0x1; - uint32_t bit6 = (bits >> 6) & 0x1; - uint32_t bit5_to_0 = bits & 0x3f; - uint32_t result = (bit7 << 31) | ((32 - bit6) << 25) | (bit5_to_0 << 19); - - return rawbits_to_float(result); -} - - -double Instruction::ImmFP64() const { - // ImmFP: abcdefgh (8 bits) - // Double: aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000 - // 0000.0000.0000.0000.0000.0000.0000.0000 (64 bits) - // where B is b ^ 1 - uint32_t bits = ImmFP(); - uint64_t bit7 = (bits >> 7) & 0x1; - uint64_t bit6 = (bits >> 6) & 0x1; - uint64_t bit5_to_0 = bits & 0x3f; - uint64_t result = (bit7 << 63) | ((256 - bit6) << 54) | (bit5_to_0 << 48); - - return rawbits_to_double(result); -} - - -LSDataSize CalcLSPairDataSize(LoadStorePairOp op) { - switch (op) { - case STP_x: - case LDP_x: - case STP_d: - case LDP_d: return LSDoubleWord; - default: return LSWord; - } -} - - -const Instruction* Instruction::ImmPCOffsetTarget() const { - const Instruction * base = this; - ptrdiff_t offset; - if (IsPCRelAddressing()) { - // ADR and ADRP. - offset = ImmPCRel(); - if (Mask(PCRelAddressingMask) == ADRP) { - base = AlignDown(base, kPageSize); - offset *= kPageSize; - } else { - VIXL_ASSERT(Mask(PCRelAddressingMask) == ADR); - } - } else { - // All PC-relative branches. - VIXL_ASSERT(BranchType() != UnknownBranchType); - // Relative branch offsets are instruction-size-aligned. - offset = ImmBranch() << kInstructionSizeLog2; - } - return base + offset; -} - - -inline int Instruction::ImmBranch() const { - switch (BranchType()) { - case CondBranchType: return ImmCondBranch(); - case UncondBranchType: return ImmUncondBranch(); - case CompareBranchType: return ImmCmpBranch(); - case TestBranchType: return ImmTestBranch(); - default: VIXL_UNREACHABLE(); - } - return 0; -} - - -void Instruction::SetImmPCOffsetTarget(const Instruction* target) { - if (IsPCRelAddressing()) { - SetPCRelImmTarget(target); - } else { - SetBranchImmTarget(target); - } -} - - -void Instruction::SetPCRelImmTarget(const Instruction* target) { - int32_t imm21; - if ((Mask(PCRelAddressingMask) == ADR)) { - imm21 = target - this; - } else { - VIXL_ASSERT(Mask(PCRelAddressingMask) == ADRP); - uintptr_t this_page = reinterpret_cast(this) / kPageSize; - uintptr_t target_page = reinterpret_cast(target) / kPageSize; - imm21 = target_page - this_page; - } - Instr imm = Assembler::ImmPCRelAddress(imm21); - - SetInstructionBits(Mask(~ImmPCRel_mask) | imm); -} - - -void Instruction::SetBranchImmTarget(const Instruction* target) { - VIXL_ASSERT(((target - this) & 3) == 0); - Instr branch_imm = 0; - uint32_t imm_mask = 0; - int offset = (target - this) >> kInstructionSizeLog2; - switch (BranchType()) { - case CondBranchType: { - branch_imm = Assembler::ImmCondBranch(offset); - imm_mask = ImmCondBranch_mask; - break; - } - case UncondBranchType: { - branch_imm = Assembler::ImmUncondBranch(offset); - imm_mask = ImmUncondBranch_mask; - break; - } - case CompareBranchType: { - branch_imm = Assembler::ImmCmpBranch(offset); - imm_mask = ImmCmpBranch_mask; - break; - } - case TestBranchType: { - branch_imm = Assembler::ImmTestBranch(offset); - imm_mask = ImmTestBranch_mask; - break; - } - default: VIXL_UNREACHABLE(); - } - SetInstructionBits(Mask(~imm_mask) | branch_imm); -} - - -void Instruction::SetImmLLiteral(const Instruction* source) { - VIXL_ASSERT(IsWordAligned(source)); - ptrdiff_t offset = (source - this) >> kLiteralEntrySizeLog2; - Instr imm = Assembler::ImmLLiteral(offset); - Instr mask = ImmLLiteral_mask; - - SetInstructionBits(Mask(~mask) | imm); -} -} // namespace vixl - diff --git a/qemu/disas/libvixl/a64/instructions-a64.h b/qemu/disas/libvixl/a64/instructions-a64.h deleted file mode 100644 index f1d883ccc..000000000 --- a/qemu/disas/libvixl/a64/instructions-a64.h +++ /dev/null @@ -1,384 +0,0 @@ -// Copyright 2013, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef VIXL_A64_INSTRUCTIONS_A64_H_ -#define VIXL_A64_INSTRUCTIONS_A64_H_ - -#include "globals.h" -#include "utils.h" -#include "a64/constants-a64.h" - -namespace vixl { -// ISA constants. -------------------------------------------------------------- - -typedef uint32_t Instr; -const unsigned kInstructionSize = 4; -const unsigned kInstructionSizeLog2 = 2; -const unsigned kLiteralEntrySize = 4; -const unsigned kLiteralEntrySizeLog2 = 2; -const unsigned kMaxLoadLiteralRange = 1 * MBytes; - -// This is the nominal page size (as used by the adrp instruction); the actual -// size of the memory pages allocated by the kernel is likely to differ. -const unsigned kPageSize = 4 * KBytes; -const unsigned kPageSizeLog2 = 12; - -const unsigned kWRegSize = 32; -const unsigned kWRegSizeLog2 = 5; -const unsigned kWRegSizeInBytes = kWRegSize / 8; -const unsigned kWRegSizeInBytesLog2 = kWRegSizeLog2 - 3; -const unsigned kXRegSize = 64; -const unsigned kXRegSizeLog2 = 6; -const unsigned kXRegSizeInBytes = kXRegSize / 8; -const unsigned kXRegSizeInBytesLog2 = kXRegSizeLog2 - 3; -const unsigned kSRegSize = 32; -const unsigned kSRegSizeLog2 = 5; -const unsigned kSRegSizeInBytes = kSRegSize / 8; -const unsigned kSRegSizeInBytesLog2 = kSRegSizeLog2 - 3; -const unsigned kDRegSize = 64; -const unsigned kDRegSizeLog2 = 6; -const unsigned kDRegSizeInBytes = kDRegSize / 8; -const unsigned kDRegSizeInBytesLog2 = kDRegSizeLog2 - 3; -const uint64_t kWRegMask = UINT64_C(0xffffffff); -const uint64_t kXRegMask = UINT64_C(0xffffffffffffffff); -const uint64_t kSRegMask = UINT64_C(0xffffffff); -const uint64_t kDRegMask = UINT64_C(0xffffffffffffffff); -const uint64_t kSSignMask = UINT64_C(0x80000000); -const uint64_t kDSignMask = UINT64_C(0x8000000000000000); -const uint64_t kWSignMask = UINT64_C(0x80000000); -const uint64_t kXSignMask = UINT64_C(0x8000000000000000); -const uint64_t kByteMask = UINT64_C(0xff); -const uint64_t kHalfWordMask = UINT64_C(0xffff); -const uint64_t kWordMask = UINT64_C(0xffffffff); -const uint64_t kXMaxUInt = UINT64_C(0xffffffffffffffff); -const uint64_t kWMaxUInt = UINT64_C(0xffffffff); -const int64_t kXMaxInt = INT64_C(0x7fffffffffffffff); -const int64_t kXMinInt = INT64_C(0x8000000000000000); -const int32_t kWMaxInt = INT32_C(0x7fffffff); -const int32_t kWMinInt = INT32_C(0x80000000); -const unsigned kLinkRegCode = 30; -const unsigned kZeroRegCode = 31; -const unsigned kSPRegInternalCode = 63; -const unsigned kRegCodeMask = 0x1f; - -const unsigned kAddressTagOffset = 56; -const unsigned kAddressTagWidth = 8; -const uint64_t kAddressTagMask = - ((UINT64_C(1) << kAddressTagWidth) - 1) << kAddressTagOffset; -VIXL_STATIC_ASSERT(kAddressTagMask == UINT64_C(0xff00000000000000)); - -// AArch64 floating-point specifics. These match IEEE-754. -const unsigned kDoubleMantissaBits = 52; -const unsigned kDoubleExponentBits = 11; -const unsigned kFloatMantissaBits = 23; -const unsigned kFloatExponentBits = 8; - -// Floating-point infinity values. -extern const float kFP32PositiveInfinity; -extern const float kFP32NegativeInfinity; -extern const double kFP64PositiveInfinity; -extern const double kFP64NegativeInfinity; - -// The default NaN values (for FPCR.DN=1). -extern const double kFP64DefaultNaN; -extern const float kFP32DefaultNaN; - - -enum LSDataSize { - LSByte = 0, - LSHalfword = 1, - LSWord = 2, - LSDoubleWord = 3 -}; - -LSDataSize CalcLSPairDataSize(LoadStorePairOp op); - -enum ImmBranchType { - UnknownBranchType = 0, - CondBranchType = 1, - UncondBranchType = 2, - CompareBranchType = 3, - TestBranchType = 4 -}; - -enum AddrMode { - Offset, - PreIndex, - PostIndex -}; - -enum FPRounding { - // The first four values are encodable directly by FPCR. - FPTieEven = 0x0, - FPPositiveInfinity = 0x1, - FPNegativeInfinity = 0x2, - FPZero = 0x3, - - // The final rounding mode is only available when explicitly specified by the - // instruction (such as with fcvta). It cannot be set in FPCR. - FPTieAway -}; - -enum Reg31Mode { - Reg31IsStackPointer, - Reg31IsZeroRegister -}; - -// Instructions. --------------------------------------------------------------- - -class Instruction { - public: - Instr InstructionBits() const { - return *(reinterpret_cast(this)); - } - - void SetInstructionBits(Instr new_instr) { - *(reinterpret_cast(this)) = new_instr; - } - - int Bit(int pos) const { - return (InstructionBits() >> pos) & 1; - } - - uint32_t Bits(int msb, int lsb) const { - return unsigned_bitextract_32(msb, lsb, InstructionBits()); - } - - int32_t SignedBits(int msb, int lsb) const { - int32_t bits = *(reinterpret_cast(this)); - return signed_bitextract_32(msb, lsb, bits); - } - - Instr Mask(uint32_t mask) const { - return InstructionBits() & mask; - } - - #define DEFINE_GETTER(Name, HighBit, LowBit, Func) \ - int64_t Name() const { return Func(HighBit, LowBit); } - INSTRUCTION_FIELDS_LIST(DEFINE_GETTER) - #undef DEFINE_GETTER - - // ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST), - // formed from ImmPCRelLo and ImmPCRelHi. - int ImmPCRel() const { - int const offset = ((ImmPCRelHi() << ImmPCRelLo_width) | ImmPCRelLo()); - int const width = ImmPCRelLo_width + ImmPCRelHi_width; - return signed_bitextract_32(width-1, 0, offset); - } - - uint64_t ImmLogical() const; - float ImmFP32() const; - double ImmFP64() const; - - LSDataSize SizeLSPair() const { - return CalcLSPairDataSize( - static_cast(Mask(LoadStorePairMask))); - } - - // Helpers. - bool IsCondBranchImm() const { - return Mask(ConditionalBranchFMask) == ConditionalBranchFixed; - } - - bool IsUncondBranchImm() const { - return Mask(UnconditionalBranchFMask) == UnconditionalBranchFixed; - } - - bool IsCompareBranch() const { - return Mask(CompareBranchFMask) == CompareBranchFixed; - } - - bool IsTestBranch() const { - return Mask(TestBranchFMask) == TestBranchFixed; - } - - bool IsPCRelAddressing() const { - return Mask(PCRelAddressingFMask) == PCRelAddressingFixed; - } - - bool IsLogicalImmediate() const { - return Mask(LogicalImmediateFMask) == LogicalImmediateFixed; - } - - bool IsAddSubImmediate() const { - return Mask(AddSubImmediateFMask) == AddSubImmediateFixed; - } - - bool IsAddSubExtended() const { - return Mask(AddSubExtendedFMask) == AddSubExtendedFixed; - } - - bool IsLoadOrStore() const { - return Mask(LoadStoreAnyFMask) == LoadStoreAnyFixed; - } - - bool IsLoad() const; - bool IsStore() const; - - bool IsLoadLiteral() const { - // This includes PRFM_lit. - return Mask(LoadLiteralFMask) == LoadLiteralFixed; - } - - bool IsMovn() const { - return (Mask(MoveWideImmediateMask) == MOVN_x) || - (Mask(MoveWideImmediateMask) == MOVN_w); - } - - // Indicate whether Rd can be the stack pointer or the zero register. This - // does not check that the instruction actually has an Rd field. - Reg31Mode RdMode() const { - // The following instructions use sp or wsp as Rd: - // Add/sub (immediate) when not setting the flags. - // Add/sub (extended) when not setting the flags. - // Logical (immediate) when not setting the flags. - // Otherwise, r31 is the zero register. - if (IsAddSubImmediate() || IsAddSubExtended()) { - if (Mask(AddSubSetFlagsBit)) { - return Reg31IsZeroRegister; - } else { - return Reg31IsStackPointer; - } - } - if (IsLogicalImmediate()) { - // Of the logical (immediate) instructions, only ANDS (and its aliases) - // can set the flags. The others can all write into sp. - // Note that some logical operations are not available to - // immediate-operand instructions, so we have to combine two masks here. - if (Mask(LogicalImmediateMask & LogicalOpMask) == ANDS) { - return Reg31IsZeroRegister; - } else { - return Reg31IsStackPointer; - } - } - return Reg31IsZeroRegister; - } - - // Indicate whether Rn can be the stack pointer or the zero register. This - // does not check that the instruction actually has an Rn field. - Reg31Mode RnMode() const { - // The following instructions use sp or wsp as Rn: - // All loads and stores. - // Add/sub (immediate). - // Add/sub (extended). - // Otherwise, r31 is the zero register. - if (IsLoadOrStore() || IsAddSubImmediate() || IsAddSubExtended()) { - return Reg31IsStackPointer; - } - return Reg31IsZeroRegister; - } - - ImmBranchType BranchType() const { - if (IsCondBranchImm()) { - return CondBranchType; - } else if (IsUncondBranchImm()) { - return UncondBranchType; - } else if (IsCompareBranch()) { - return CompareBranchType; - } else if (IsTestBranch()) { - return TestBranchType; - } else { - return UnknownBranchType; - } - } - - // Find the target of this instruction. 'this' may be a branch or a - // PC-relative addressing instruction. - const Instruction* ImmPCOffsetTarget() const; - - // Patch a PC-relative offset to refer to 'target'. 'this' may be a branch or - // a PC-relative addressing instruction. - void SetImmPCOffsetTarget(const Instruction* target); - // Patch a literal load instruction to load from 'source'. - void SetImmLLiteral(const Instruction* source); - - // Calculate the address of a literal referred to by a load-literal - // instruction, and return it as the specified type. - // - // The literal itself is safely mutable only if the backing buffer is safely - // mutable. - template - T LiteralAddress() const { - uint64_t base_raw = reinterpret_cast(this); - ptrdiff_t offset = ImmLLiteral() << kLiteralEntrySizeLog2; - uint64_t address_raw = base_raw + offset; - - // Cast the address using a C-style cast. A reinterpret_cast would be - // appropriate, but it can't cast one integral type to another. - T address = (T)(address_raw); - - // Assert that the address can be represented by the specified type. - VIXL_ASSERT((uint64_t)(address) == address_raw); - - return address; - } - - uint32_t Literal32() const { - uint32_t literal; - memcpy(&literal, LiteralAddress(), sizeof(literal)); - return literal; - } - - uint64_t Literal64() const { - uint64_t literal; - memcpy(&literal, LiteralAddress(), sizeof(literal)); - return literal; - } - - float LiteralFP32() const { - return rawbits_to_float(Literal32()); - } - - double LiteralFP64() const { - return rawbits_to_double(Literal64()); - } - - const Instruction* NextInstruction() const { - return this + kInstructionSize; - } - - const Instruction* InstructionAtOffset(int64_t offset) const { - VIXL_ASSERT(IsWordAligned(this + offset)); - return this + offset; - } - - template static Instruction* Cast(T src) { - return reinterpret_cast(src); - } - - template static const Instruction* CastConst(T src) { - return reinterpret_cast(src); - } - - private: - int ImmBranch() const; - - void SetPCRelImmTarget(const Instruction* target); - void SetBranchImmTarget(const Instruction* target); -}; -} // namespace vixl - -#endif // VIXL_A64_INSTRUCTIONS_A64_H_ diff --git a/qemu/disas/libvixl/code-buffer.h b/qemu/disas/libvixl/code-buffer.h deleted file mode 100644 index da6233dd8..000000000 --- a/qemu/disas/libvixl/code-buffer.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2014, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef VIXL_CODE_BUFFER_H -#define VIXL_CODE_BUFFER_H - -#include -#include "globals.h" - -namespace vixl { - -class CodeBuffer { - public: - explicit CodeBuffer(size_t capacity = 4 * KBytes); - CodeBuffer(void* buffer, size_t capacity); - ~CodeBuffer(); - - void Reset(); - - ptrdiff_t OffsetFrom(ptrdiff_t offset) const { - ptrdiff_t cursor_offset = cursor_ - buffer_; - VIXL_ASSERT((offset >= 0) && (offset <= cursor_offset)); - return cursor_offset - offset; - } - - ptrdiff_t CursorOffset() const { - return OffsetFrom(0); - } - - template - T GetOffsetAddress(ptrdiff_t offset) const { - VIXL_ASSERT((offset >= 0) && (offset <= (cursor_ - buffer_))); - return reinterpret_cast(buffer_ + offset); - } - - size_t RemainingBytes() const { - VIXL_ASSERT((cursor_ >= buffer_) && (cursor_ <= (buffer_ + capacity_))); - return (buffer_ + capacity_) - cursor_; - } - - // A code buffer can emit: - // * 32-bit data: instruction and constant. - // * 64-bit data: constant. - // * string: debug info. - void Emit32(uint32_t data) { Emit(data); } - - void Emit64(uint64_t data) { Emit(data); } - - void EmitString(const char* string); - - // Align to kInstructionSize. - void Align(); - - size_t capacity() const { return capacity_; } - - bool IsManaged() const { return managed_; } - - void Grow(size_t new_capacity); - - bool IsDirty() const { return dirty_; } - - void SetClean() { dirty_ = false; } - - private: - template - void Emit(T value) { - VIXL_ASSERT(RemainingBytes() >= sizeof(value)); - dirty_ = true; - memcpy(cursor_, &value, sizeof(value)); - cursor_ += sizeof(value); - } - - // Backing store of the buffer. - byte* buffer_; - // If true the backing store is allocated and deallocated by the buffer. The - // backing store can then grow on demand. If false the backing store is - // provided by the user and cannot be resized internally. - bool managed_; - // Pointer to the next location to be written. - byte* cursor_; - // True if there has been any write since the buffer was created or cleaned. - bool dirty_; - // Capacity in bytes of the backing store. - size_t capacity_; -}; - -} // namespace vixl - -#endif // VIXL_CODE_BUFFER_H - diff --git a/qemu/disas/libvixl/globals.h b/qemu/disas/libvixl/globals.h deleted file mode 100644 index 0c2493105..000000000 --- a/qemu/disas/libvixl/globals.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2013, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef VIXL_GLOBALS_H -#define VIXL_GLOBALS_H - -// Get standard C99 macros for integer types. -#ifndef __STDC_CONSTANT_MACROS -#define __STDC_CONSTANT_MACROS -#endif - -#ifndef __STDC_LIMIT_MACROS -#define __STDC_LIMIT_MACROS -#endif - -#ifndef __STDC_FORMAT_MACROS -#define __STDC_FORMAT_MACROS -#endif - -#include -#include - -#include -#include -#include -#include -#include -#include -#include "platform.h" - - -typedef uint8_t byte; - -const int KBytes = 1024; -const int MBytes = 1024 * KBytes; - -#define VIXL_ABORT() printf("in %s, line %i", __FILE__, __LINE__); abort() -#ifdef VIXL_DEBUG - #define VIXL_ASSERT(condition) assert(condition) - #define VIXL_CHECK(condition) VIXL_ASSERT(condition) - #define VIXL_UNIMPLEMENTED() printf("UNIMPLEMENTED\t"); VIXL_ABORT() - #define VIXL_UNREACHABLE() printf("UNREACHABLE\t"); VIXL_ABORT() -#else - #define VIXL_ASSERT(condition) ((void) 0) - #define VIXL_CHECK(condition) assert(condition) - #define VIXL_UNIMPLEMENTED() ((void) 0) - #define VIXL_UNREACHABLE() ((void) 0) -#endif -// This is not as powerful as template based assertions, but it is simple. -// It assumes that the descriptions are unique. If this starts being a problem, -// we can switch to a different implemention. -#define VIXL_CONCAT(a, b) a##b -#define VIXL_STATIC_ASSERT_LINE(line, condition) \ - typedef char VIXL_CONCAT(STATIC_ASSERT_LINE_, line)[(condition) ? 1 : -1] \ - __attribute__((unused)) -#define VIXL_STATIC_ASSERT(condition) VIXL_STATIC_ASSERT_LINE(__LINE__, condition) //NOLINT - -template inline void USE(T) {} - -#define VIXL_ALIGNMENT_EXCEPTION() printf("ALIGNMENT EXCEPTION\t"); VIXL_ABORT() - -#endif // VIXL_GLOBALS_H diff --git a/qemu/disas/libvixl/platform.h b/qemu/disas/libvixl/platform.h deleted file mode 100644 index de2b110cc..000000000 --- a/qemu/disas/libvixl/platform.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2013, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef PLATFORM_H -#define PLATFORM_H - -// Define platform specific functionalities. -#include - -namespace vixl { -inline void HostBreakpoint() { raise(SIGINT); } -} // namespace vixl - -#endif diff --git a/qemu/disas/libvixl/utils.cc b/qemu/disas/libvixl/utils.cc deleted file mode 100644 index 80b132a11..000000000 --- a/qemu/disas/libvixl/utils.cc +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2013, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "utils.h" -#include - -namespace vixl { - -uint32_t float_to_rawbits(float value) { - uint32_t bits = 0; - memcpy(&bits, &value, 4); - return bits; -} - - -uint64_t double_to_rawbits(double value) { - uint64_t bits = 0; - memcpy(&bits, &value, 8); - return bits; -} - - -float rawbits_to_float(uint32_t bits) { - float value = 0.0; - memcpy(&value, &bits, 4); - return value; -} - - -double rawbits_to_double(uint64_t bits) { - double value = 0.0; - memcpy(&value, &bits, 8); - return value; -} - - -int CountLeadingZeros(uint64_t value, int width) { - VIXL_ASSERT((width == 32) || (width == 64)); - int count = 0; - uint64_t bit_test = UINT64_C(1) << (width - 1); - while ((count < width) && ((bit_test & value) == 0)) { - count++; - bit_test >>= 1; - } - return count; -} - - -int CountLeadingSignBits(int64_t value, int width) { - VIXL_ASSERT((width == 32) || (width == 64)); - if (value >= 0) { - return CountLeadingZeros(value, width) - 1; - } else { - return CountLeadingZeros(~value, width) - 1; - } -} - - -int CountTrailingZeros(uint64_t value, int width) { - VIXL_ASSERT((width == 32) || (width == 64)); - int count = 0; - while ((count < width) && (((value >> count) & 1) == 0)) { - count++; - } - return count; -} - - -int CountSetBits(uint64_t value, int width) { - // TODO: Other widths could be added here, as the implementation already - // supports them. - VIXL_ASSERT((width == 32) || (width == 64)); - - // Mask out unused bits to ensure that they are not counted. - value &= (UINT64_C(0xffffffffffffffff) >> (64-width)); - - // Add up the set bits. - // The algorithm works by adding pairs of bit fields together iteratively, - // where the size of each bit field doubles each time. - // An example for an 8-bit value: - // Bits: h g f e d c b a - // \ | \ | \ | \ | - // value = h+g f+e d+c b+a - // \ | \ | - // value = h+g+f+e d+c+b+a - // \ | - // value = h+g+f+e+d+c+b+a - const uint64_t kMasks[] = { - UINT64_C(0x5555555555555555), - UINT64_C(0x3333333333333333), - UINT64_C(0x0f0f0f0f0f0f0f0f), - UINT64_C(0x00ff00ff00ff00ff), - UINT64_C(0x0000ffff0000ffff), - UINT64_C(0x00000000ffffffff), - }; - - for (unsigned i = 0; i < (sizeof(kMasks) / sizeof(kMasks[0])); i++) { - int shift = 1 << i; - value = ((value >> shift) & kMasks[i]) + (value & kMasks[i]); - } - - return value; -} - - -uint64_t LowestSetBit(uint64_t value) { - return value & -value; -} - - -bool IsPowerOf2(int64_t value) { - return (value != 0) && ((value & (value - 1)) == 0); -} - - -unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size) { - VIXL_ASSERT((reg_size % 8) == 0); - int count = 0; - for (unsigned i = 0; i < (reg_size / 16); i++) { - if ((imm & 0xffff) == 0) { - count++; - } - imm >>= 16; - } - return count; -} - -} // namespace vixl diff --git a/qemu/disas/libvixl/utils.h b/qemu/disas/libvixl/utils.h deleted file mode 100644 index b4406263a..000000000 --- a/qemu/disas/libvixl/utils.h +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright 2013, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef VIXL_UTILS_H -#define VIXL_UTILS_H - -#include -#include -#include "globals.h" - -namespace vixl { - -// Macros for compile-time format checking. -#if defined(__GNUC__) -#define PRINTF_CHECK(format_index, varargs_index) \ - __attribute__((format(printf, format_index, varargs_index))) -#else -#define PRINTF_CHECK(format_index, varargs_index) -#endif - -// Check number width. -inline bool is_intn(unsigned n, int64_t x) { - VIXL_ASSERT((0 < n) && (n < 64)); - int64_t limit = INT64_C(1) << (n - 1); - return (-limit <= x) && (x < limit); -} - -inline bool is_uintn(unsigned n, int64_t x) { - VIXL_ASSERT((0 < n) && (n < 64)); - return !(x >> n); -} - -inline unsigned truncate_to_intn(unsigned n, int64_t x) { - VIXL_ASSERT((0 < n) && (n < 64)); - return (x & ((INT64_C(1) << n) - 1)); -} - -#define INT_1_TO_63_LIST(V) \ -V(1) V(2) V(3) V(4) V(5) V(6) V(7) V(8) \ -V(9) V(10) V(11) V(12) V(13) V(14) V(15) V(16) \ -V(17) V(18) V(19) V(20) V(21) V(22) V(23) V(24) \ -V(25) V(26) V(27) V(28) V(29) V(30) V(31) V(32) \ -V(33) V(34) V(35) V(36) V(37) V(38) V(39) V(40) \ -V(41) V(42) V(43) V(44) V(45) V(46) V(47) V(48) \ -V(49) V(50) V(51) V(52) V(53) V(54) V(55) V(56) \ -V(57) V(58) V(59) V(60) V(61) V(62) V(63) - -#define DECLARE_IS_INT_N(N) \ -inline bool is_int##N(int64_t x) { return is_intn(N, x); } -#define DECLARE_IS_UINT_N(N) \ -inline bool is_uint##N(int64_t x) { return is_uintn(N, x); } -#define DECLARE_TRUNCATE_TO_INT_N(N) \ -inline int truncate_to_int##N(int x) { return truncate_to_intn(N, x); } -INT_1_TO_63_LIST(DECLARE_IS_INT_N) -INT_1_TO_63_LIST(DECLARE_IS_UINT_N) -INT_1_TO_63_LIST(DECLARE_TRUNCATE_TO_INT_N) -#undef DECLARE_IS_INT_N -#undef DECLARE_IS_UINT_N -#undef DECLARE_TRUNCATE_TO_INT_N - -// Bit field extraction. -inline uint32_t unsigned_bitextract_32(int msb, int lsb, uint32_t x) { - return (x >> lsb) & ((1 << (1 + msb - lsb)) - 1); -} - -inline uint64_t unsigned_bitextract_64(int msb, int lsb, uint64_t x) { - return (x >> lsb) & ((static_cast(1) << (1 + msb - lsb)) - 1); -} - -inline int32_t signed_bitextract_32(int msb, int lsb, int32_t x) { - return (x << (31 - msb)) >> (lsb + 31 - msb); -} - -inline int64_t signed_bitextract_64(int msb, int lsb, int64_t x) { - return (x << (63 - msb)) >> (lsb + 63 - msb); -} - -// Floating point representation. -uint32_t float_to_rawbits(float value); -uint64_t double_to_rawbits(double value); -float rawbits_to_float(uint32_t bits); -double rawbits_to_double(uint64_t bits); - - -// NaN tests. -inline bool IsSignallingNaN(double num) { - const uint64_t kFP64QuietNaNMask = UINT64_C(0x0008000000000000); - uint64_t raw = double_to_rawbits(num); - if (isnan(num) && ((raw & kFP64QuietNaNMask) == 0)) { - return true; - } - return false; -} - - -inline bool IsSignallingNaN(float num) { - const uint32_t kFP32QuietNaNMask = 0x00400000; - uint32_t raw = float_to_rawbits(num); - if (isnan(num) && ((raw & kFP32QuietNaNMask) == 0)) { - return true; - } - return false; -} - - -template -inline bool IsQuietNaN(T num) { - return isnan(num) && !IsSignallingNaN(num); -} - - -// Convert the NaN in 'num' to a quiet NaN. -inline double ToQuietNaN(double num) { - const uint64_t kFP64QuietNaNMask = UINT64_C(0x0008000000000000); - VIXL_ASSERT(isnan(num)); - return rawbits_to_double(double_to_rawbits(num) | kFP64QuietNaNMask); -} - - -inline float ToQuietNaN(float num) { - const uint32_t kFP32QuietNaNMask = 0x00400000; - VIXL_ASSERT(isnan(num)); - return rawbits_to_float(float_to_rawbits(num) | kFP32QuietNaNMask); -} - - -// Fused multiply-add. -inline double FusedMultiplyAdd(double op1, double op2, double a) { - return fma(op1, op2, a); -} - - -inline float FusedMultiplyAdd(float op1, float op2, float a) { - return fmaf(op1, op2, a); -} - - -// Bit counting. -int CountLeadingZeros(uint64_t value, int width); -int CountLeadingSignBits(int64_t value, int width); -int CountTrailingZeros(uint64_t value, int width); -int CountSetBits(uint64_t value, int width); -uint64_t LowestSetBit(uint64_t value); -bool IsPowerOf2(int64_t value); - -unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size); - -// Pointer alignment -// TODO: rename/refactor to make it specific to instructions. -template -bool IsWordAligned(T pointer) { - VIXL_ASSERT(sizeof(pointer) == sizeof(intptr_t)); // NOLINT(runtime/sizeof) - return ((intptr_t)(pointer) & 3) == 0; -} - -// Increment a pointer (up to 64 bits) until it has the specified alignment. -template -T AlignUp(T pointer, size_t alignment) { - // Use C-style casts to get static_cast behaviour for integral types (T), and - // reinterpret_cast behaviour for other types. - - uint64_t pointer_raw = (uint64_t)pointer; - VIXL_STATIC_ASSERT(sizeof(pointer) <= sizeof(pointer_raw)); - - size_t align_step = (alignment - pointer_raw) % alignment; - VIXL_ASSERT((pointer_raw + align_step) % alignment == 0); - - return (T)(pointer_raw + align_step); -} - -// Decrement a pointer (up to 64 bits) until it has the specified alignment. -template -T AlignDown(T pointer, size_t alignment) { - // Use C-style casts to get static_cast behaviour for integral types (T), and - // reinterpret_cast behaviour for other types. - - uint64_t pointer_raw = (uint64_t)pointer; - VIXL_STATIC_ASSERT(sizeof(pointer) <= sizeof(pointer_raw)); - - size_t align_step = pointer_raw % alignment; - VIXL_ASSERT((pointer_raw - align_step) % alignment == 0); - - return (T)(pointer_raw - align_step); -} - -} // namespace vixl - -#endif // VIXL_UTILS_H diff --git a/qemu/disas/libvixl/vixl/a64/assembler-a64.h b/qemu/disas/libvixl/vixl/a64/assembler-a64.h new file mode 100644 index 000000000..fda5ccc6c --- /dev/null +++ b/qemu/disas/libvixl/vixl/a64/assembler-a64.h @@ -0,0 +1,4624 @@ +// Copyright 2015, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_A64_ASSEMBLER_A64_H_ +#define VIXL_A64_ASSEMBLER_A64_H_ + + +#include "vixl/globals.h" +#include "vixl/invalset.h" +#include "vixl/utils.h" +#include "vixl/code-buffer.h" +#include "vixl/a64/instructions-a64.h" + +namespace vixl { + +typedef uint64_t RegList; +static const int kRegListSizeInBits = sizeof(RegList) * 8; + + +// Registers. + +// Some CPURegister methods can return Register or VRegister types, so we need +// to declare them in advance. +class Register; +class VRegister; + +class CPURegister { + public: + enum RegisterType { + // The kInvalid value is used to detect uninitialized static instances, + // which are always zero-initialized before any constructors are called. + kInvalid = 0, + kRegister, + kVRegister, + kFPRegister = kVRegister, + kNoRegister + }; + + CPURegister() : code_(0), size_(0), type_(kNoRegister) { + VIXL_ASSERT(!IsValid()); + VIXL_ASSERT(IsNone()); + } + + CPURegister(unsigned code, unsigned size, RegisterType type) + : code_(code), size_(size), type_(type) { + VIXL_ASSERT(IsValidOrNone()); + } + + unsigned code() const { + VIXL_ASSERT(IsValid()); + return code_; + } + + RegisterType type() const { + VIXL_ASSERT(IsValidOrNone()); + return type_; + } + + RegList Bit() const { + VIXL_ASSERT(code_ < (sizeof(RegList) * 8)); + return IsValid() ? (static_cast(1) << code_) : 0; + } + + unsigned size() const { + VIXL_ASSERT(IsValid()); + return size_; + } + + int SizeInBytes() const { + VIXL_ASSERT(IsValid()); + VIXL_ASSERT(size() % 8 == 0); + return size_ / 8; + } + + int SizeInBits() const { + VIXL_ASSERT(IsValid()); + return size_; + } + + bool Is8Bits() const { + VIXL_ASSERT(IsValid()); + return size_ == 8; + } + + bool Is16Bits() const { + VIXL_ASSERT(IsValid()); + return size_ == 16; + } + + bool Is32Bits() const { + VIXL_ASSERT(IsValid()); + return size_ == 32; + } + + bool Is64Bits() const { + VIXL_ASSERT(IsValid()); + return size_ == 64; + } + + bool Is128Bits() const { + VIXL_ASSERT(IsValid()); + return size_ == 128; + } + + bool IsValid() const { + if (IsValidRegister() || IsValidVRegister()) { + VIXL_ASSERT(!IsNone()); + return true; + } else { + // This assert is hit when the register has not been properly initialized. + // One cause for this can be an initialisation order fiasco. See + // https://isocpp.org/wiki/faq/ctors#static-init-order for some details. + VIXL_ASSERT(IsNone()); + return false; + } + } + + bool IsValidRegister() const { + return IsRegister() && + ((size_ == kWRegSize) || (size_ == kXRegSize)) && + ((code_ < kNumberOfRegisters) || (code_ == kSPRegInternalCode)); + } + + bool IsValidVRegister() const { + return IsVRegister() && + ((size_ == kBRegSize) || (size_ == kHRegSize) || + (size_ == kSRegSize) || (size_ == kDRegSize) || + (size_ == kQRegSize)) && + (code_ < kNumberOfVRegisters); + } + + bool IsValidFPRegister() const { + return IsFPRegister() && (code_ < kNumberOfVRegisters); + } + + bool IsNone() const { + // kNoRegister types should always have size 0 and code 0. + VIXL_ASSERT((type_ != kNoRegister) || (code_ == 0)); + VIXL_ASSERT((type_ != kNoRegister) || (size_ == 0)); + + return type_ == kNoRegister; + } + + bool Aliases(const CPURegister& other) const { + VIXL_ASSERT(IsValidOrNone() && other.IsValidOrNone()); + return (code_ == other.code_) && (type_ == other.type_); + } + + bool Is(const CPURegister& other) const { + VIXL_ASSERT(IsValidOrNone() && other.IsValidOrNone()); + return Aliases(other) && (size_ == other.size_); + } + + bool IsZero() const { + VIXL_ASSERT(IsValid()); + return IsRegister() && (code_ == kZeroRegCode); + } + + bool IsSP() const { + VIXL_ASSERT(IsValid()); + return IsRegister() && (code_ == kSPRegInternalCode); + } + + bool IsRegister() const { + return type_ == kRegister; + } + + bool IsVRegister() const { + return type_ == kVRegister; + } + + bool IsFPRegister() const { + return IsS() || IsD(); + } + + bool IsW() const { return IsValidRegister() && Is32Bits(); } + bool IsX() const { return IsValidRegister() && Is64Bits(); } + + // These assertions ensure that the size and type of the register are as + // described. They do not consider the number of lanes that make up a vector. + // So, for example, Is8B() implies IsD(), and Is1D() implies IsD, but IsD() + // does not imply Is1D() or Is8B(). + // Check the number of lanes, ie. the format of the vector, using methods such + // as Is8B(), Is1D(), etc. in the VRegister class. + bool IsV() const { return IsVRegister(); } + bool IsB() const { return IsV() && Is8Bits(); } + bool IsH() const { return IsV() && Is16Bits(); } + bool IsS() const { return IsV() && Is32Bits(); } + bool IsD() const { return IsV() && Is64Bits(); } + bool IsQ() const { return IsV() && Is128Bits(); } + + const Register& W() const; + const Register& X() const; + const VRegister& V() const; + const VRegister& B() const; + const VRegister& H() const; + const VRegister& S() const; + const VRegister& D() const; + const VRegister& Q() const; + + bool IsSameSizeAndType(const CPURegister& other) const { + return (size_ == other.size_) && (type_ == other.type_); + } + + protected: + unsigned code_; + unsigned size_; + RegisterType type_; + + private: + bool IsValidOrNone() const { + return IsValid() || IsNone(); + } +}; + + +class Register : public CPURegister { + public: + Register() : CPURegister() {} + explicit Register(const CPURegister& other) + : CPURegister(other.code(), other.size(), other.type()) { + VIXL_ASSERT(IsValidRegister()); + } + Register(unsigned code, unsigned size) + : CPURegister(code, size, kRegister) {} + + bool IsValid() const { + VIXL_ASSERT(IsRegister() || IsNone()); + return IsValidRegister(); + } + + static const Register& WRegFromCode(unsigned code); + static const Register& XRegFromCode(unsigned code); + + private: + static const Register wregisters[]; + static const Register xregisters[]; +}; + + +class VRegister : public CPURegister { + public: + VRegister() : CPURegister(), lanes_(1) {} + explicit VRegister(const CPURegister& other) + : CPURegister(other.code(), other.size(), other.type()), lanes_(1) { + VIXL_ASSERT(IsValidVRegister()); + VIXL_ASSERT(IsPowerOf2(lanes_) && (lanes_ <= 16)); + } + VRegister(unsigned code, unsigned size, unsigned lanes = 1) + : CPURegister(code, size, kVRegister), lanes_(lanes) { + VIXL_ASSERT(IsPowerOf2(lanes_) && (lanes_ <= 16)); + } + VRegister(unsigned code, VectorFormat format) + : CPURegister(code, RegisterSizeInBitsFromFormat(format), kVRegister), + lanes_(IsVectorFormat(format) ? LaneCountFromFormat(format) : 1) { + VIXL_ASSERT(IsPowerOf2(lanes_) && (lanes_ <= 16)); + } + + bool IsValid() const { + VIXL_ASSERT(IsVRegister() || IsNone()); + return IsValidVRegister(); + } + + static const VRegister& BRegFromCode(unsigned code); + static const VRegister& HRegFromCode(unsigned code); + static const VRegister& SRegFromCode(unsigned code); + static const VRegister& DRegFromCode(unsigned code); + static const VRegister& QRegFromCode(unsigned code); + static const VRegister& VRegFromCode(unsigned code); + + VRegister V8B() const { return VRegister(code_, kDRegSize, 8); } + VRegister V16B() const { return VRegister(code_, kQRegSize, 16); } + VRegister V4H() const { return VRegister(code_, kDRegSize, 4); } + VRegister V8H() const { return VRegister(code_, kQRegSize, 8); } + VRegister V2S() const { return VRegister(code_, kDRegSize, 2); } + VRegister V4S() const { return VRegister(code_, kQRegSize, 4); } + VRegister V2D() const { return VRegister(code_, kQRegSize, 2); } + VRegister V1D() const { return VRegister(code_, kDRegSize, 1); } + + bool Is8B() const { return (Is64Bits() && (lanes_ == 8)); } + bool Is16B() const { return (Is128Bits() && (lanes_ == 16)); } + bool Is4H() const { return (Is64Bits() && (lanes_ == 4)); } + bool Is8H() const { return (Is128Bits() && (lanes_ == 8)); } + bool Is2S() const { return (Is64Bits() && (lanes_ == 2)); } + bool Is4S() const { return (Is128Bits() && (lanes_ == 4)); } + bool Is1D() const { return (Is64Bits() && (lanes_ == 1)); } + bool Is2D() const { return (Is128Bits() && (lanes_ == 2)); } + + // For consistency, we assert the number of lanes of these scalar registers, + // even though there are no vectors of equivalent total size with which they + // could alias. + bool Is1B() const { + VIXL_ASSERT(!(Is8Bits() && IsVector())); + return Is8Bits(); + } + bool Is1H() const { + VIXL_ASSERT(!(Is16Bits() && IsVector())); + return Is16Bits(); + } + bool Is1S() const { + VIXL_ASSERT(!(Is32Bits() && IsVector())); + return Is32Bits(); + } + + bool IsLaneSizeB() const { return LaneSizeInBits() == kBRegSize; } + bool IsLaneSizeH() const { return LaneSizeInBits() == kHRegSize; } + bool IsLaneSizeS() const { return LaneSizeInBits() == kSRegSize; } + bool IsLaneSizeD() const { return LaneSizeInBits() == kDRegSize; } + + int lanes() const { + return lanes_; + } + + bool IsScalar() const { + return lanes_ == 1; + } + + bool IsVector() const { + return lanes_ > 1; + } + + bool IsSameFormat(const VRegister& other) const { + return (size_ == other.size_) && (lanes_ == other.lanes_); + } + + unsigned LaneSizeInBytes() const { + return SizeInBytes() / lanes_; + } + + unsigned LaneSizeInBits() const { + return LaneSizeInBytes() * 8; + } + + private: + static const VRegister bregisters[]; + static const VRegister hregisters[]; + static const VRegister sregisters[]; + static const VRegister dregisters[]; + static const VRegister qregisters[]; + static const VRegister vregisters[]; + int lanes_; +}; + + +// Backward compatibility for FPRegisters. +typedef VRegister FPRegister; + +// No*Reg is used to indicate an unused argument, or an error case. Note that +// these all compare equal (using the Is() method). The Register and VRegister +// variants are provided for convenience. +const Register NoReg; +const VRegister NoVReg; +const FPRegister NoFPReg; // For backward compatibility. +const CPURegister NoCPUReg; + + +#define DEFINE_REGISTERS(N) \ +const Register w##N(N, kWRegSize); \ +const Register x##N(N, kXRegSize); +REGISTER_CODE_LIST(DEFINE_REGISTERS) +#undef DEFINE_REGISTERS +const Register wsp(kSPRegInternalCode, kWRegSize); +const Register sp(kSPRegInternalCode, kXRegSize); + + +#define DEFINE_VREGISTERS(N) \ +const VRegister b##N(N, kBRegSize); \ +const VRegister h##N(N, kHRegSize); \ +const VRegister s##N(N, kSRegSize); \ +const VRegister d##N(N, kDRegSize); \ +const VRegister q##N(N, kQRegSize); \ +const VRegister v##N(N, kQRegSize); +REGISTER_CODE_LIST(DEFINE_VREGISTERS) +#undef DEFINE_VREGISTERS + + +// Registers aliases. +const Register ip0 = x16; +const Register ip1 = x17; +const Register lr = x30; +const Register xzr = x31; +const Register wzr = w31; + + +// AreAliased returns true if any of the named registers overlap. Arguments +// set to NoReg are ignored. The system stack pointer may be specified. +bool AreAliased(const CPURegister& reg1, + const CPURegister& reg2, + const CPURegister& reg3 = NoReg, + const CPURegister& reg4 = NoReg, + const CPURegister& reg5 = NoReg, + const CPURegister& reg6 = NoReg, + const CPURegister& reg7 = NoReg, + const CPURegister& reg8 = NoReg); + + +// AreSameSizeAndType returns true if all of the specified registers have the +// same size, and are of the same type. The system stack pointer may be +// specified. Arguments set to NoReg are ignored, as are any subsequent +// arguments. At least one argument (reg1) must be valid (not NoCPUReg). +bool AreSameSizeAndType(const CPURegister& reg1, + const CPURegister& reg2, + const CPURegister& reg3 = NoCPUReg, + const CPURegister& reg4 = NoCPUReg, + const CPURegister& reg5 = NoCPUReg, + const CPURegister& reg6 = NoCPUReg, + const CPURegister& reg7 = NoCPUReg, + const CPURegister& reg8 = NoCPUReg); + + +// AreSameFormat returns true if all of the specified VRegisters have the same +// vector format. Arguments set to NoReg are ignored, as are any subsequent +// arguments. At least one argument (reg1) must be valid (not NoVReg). +bool AreSameFormat(const VRegister& reg1, + const VRegister& reg2, + const VRegister& reg3 = NoVReg, + const VRegister& reg4 = NoVReg); + + +// AreConsecutive returns true if all of the specified VRegisters are +// consecutive in the register file. Arguments set to NoReg are ignored, as are +// any subsequent arguments. At least one argument (reg1) must be valid +// (not NoVReg). +bool AreConsecutive(const VRegister& reg1, + const VRegister& reg2, + const VRegister& reg3 = NoVReg, + const VRegister& reg4 = NoVReg); + + +// Lists of registers. +class CPURegList { + public: + explicit CPURegList(CPURegister reg1, + CPURegister reg2 = NoCPUReg, + CPURegister reg3 = NoCPUReg, + CPURegister reg4 = NoCPUReg) + : list_(reg1.Bit() | reg2.Bit() | reg3.Bit() | reg4.Bit()), + size_(reg1.size()), type_(reg1.type()) { + VIXL_ASSERT(AreSameSizeAndType(reg1, reg2, reg3, reg4)); + VIXL_ASSERT(IsValid()); + } + + CPURegList(CPURegister::RegisterType type, unsigned size, RegList list) + : list_(list), size_(size), type_(type) { + VIXL_ASSERT(IsValid()); + } + + CPURegList(CPURegister::RegisterType type, unsigned size, + unsigned first_reg, unsigned last_reg) + : size_(size), type_(type) { + VIXL_ASSERT(((type == CPURegister::kRegister) && + (last_reg < kNumberOfRegisters)) || + ((type == CPURegister::kVRegister) && + (last_reg < kNumberOfVRegisters))); + VIXL_ASSERT(last_reg >= first_reg); + list_ = (UINT64_C(1) << (last_reg + 1)) - 1; + list_ &= ~((UINT64_C(1) << first_reg) - 1); + VIXL_ASSERT(IsValid()); + } + + CPURegister::RegisterType type() const { + VIXL_ASSERT(IsValid()); + return type_; + } + + // Combine another CPURegList into this one. Registers that already exist in + // this list are left unchanged. The type and size of the registers in the + // 'other' list must match those in this list. + void Combine(const CPURegList& other) { + VIXL_ASSERT(IsValid()); + VIXL_ASSERT(other.type() == type_); + VIXL_ASSERT(other.RegisterSizeInBits() == size_); + list_ |= other.list(); + } + + // Remove every register in the other CPURegList from this one. Registers that + // do not exist in this list are ignored. The type and size of the registers + // in the 'other' list must match those in this list. + void Remove(const CPURegList& other) { + VIXL_ASSERT(IsValid()); + VIXL_ASSERT(other.type() == type_); + VIXL_ASSERT(other.RegisterSizeInBits() == size_); + list_ &= ~other.list(); + } + + // Variants of Combine and Remove which take a single register. + void Combine(const CPURegister& other) { + VIXL_ASSERT(other.type() == type_); + VIXL_ASSERT(other.size() == size_); + Combine(other.code()); + } + + void Remove(const CPURegister& other) { + VIXL_ASSERT(other.type() == type_); + VIXL_ASSERT(other.size() == size_); + Remove(other.code()); + } + + // Variants of Combine and Remove which take a single register by its code; + // the type and size of the register is inferred from this list. + void Combine(int code) { + VIXL_ASSERT(IsValid()); + VIXL_ASSERT(CPURegister(code, size_, type_).IsValid()); + list_ |= (UINT64_C(1) << code); + } + + void Remove(int code) { + VIXL_ASSERT(IsValid()); + VIXL_ASSERT(CPURegister(code, size_, type_).IsValid()); + list_ &= ~(UINT64_C(1) << code); + } + + static CPURegList Union(const CPURegList& list_1, const CPURegList& list_2) { + VIXL_ASSERT(list_1.type_ == list_2.type_); + VIXL_ASSERT(list_1.size_ == list_2.size_); + return CPURegList(list_1.type_, list_1.size_, list_1.list_ | list_2.list_); + } + static CPURegList Union(const CPURegList& list_1, + const CPURegList& list_2, + const CPURegList& list_3); + static CPURegList Union(const CPURegList& list_1, + const CPURegList& list_2, + const CPURegList& list_3, + const CPURegList& list_4); + + static CPURegList Intersection(const CPURegList& list_1, + const CPURegList& list_2) { + VIXL_ASSERT(list_1.type_ == list_2.type_); + VIXL_ASSERT(list_1.size_ == list_2.size_); + return CPURegList(list_1.type_, list_1.size_, list_1.list_ & list_2.list_); + } + static CPURegList Intersection(const CPURegList& list_1, + const CPURegList& list_2, + const CPURegList& list_3); + static CPURegList Intersection(const CPURegList& list_1, + const CPURegList& list_2, + const CPURegList& list_3, + const CPURegList& list_4); + + bool Overlaps(const CPURegList& other) const { + return (type_ == other.type_) && ((list_ & other.list_) != 0); + } + + RegList list() const { + VIXL_ASSERT(IsValid()); + return list_; + } + + void set_list(RegList new_list) { + VIXL_ASSERT(IsValid()); + list_ = new_list; + } + + // Remove all callee-saved registers from the list. This can be useful when + // preparing registers for an AAPCS64 function call, for example. + void RemoveCalleeSaved(); + + CPURegister PopLowestIndex(); + CPURegister PopHighestIndex(); + + // AAPCS64 callee-saved registers. + static CPURegList GetCalleeSaved(unsigned size = kXRegSize); + static CPURegList GetCalleeSavedV(unsigned size = kDRegSize); + + // AAPCS64 caller-saved registers. Note that this includes lr. + // TODO(all): Determine how we handle d8-d15 being callee-saved, but the top + // 64-bits being caller-saved. + static CPURegList GetCallerSaved(unsigned size = kXRegSize); + static CPURegList GetCallerSavedV(unsigned size = kDRegSize); + + bool IsEmpty() const { + VIXL_ASSERT(IsValid()); + return list_ == 0; + } + + bool IncludesAliasOf(const CPURegister& other) const { + VIXL_ASSERT(IsValid()); + return (type_ == other.type()) && ((other.Bit() & list_) != 0); + } + + bool IncludesAliasOf(int code) const { + VIXL_ASSERT(IsValid()); + return ((code & list_) != 0); + } + + int Count() const { + VIXL_ASSERT(IsValid()); + return CountSetBits(list_); + } + + unsigned RegisterSizeInBits() const { + VIXL_ASSERT(IsValid()); + return size_; + } + + unsigned RegisterSizeInBytes() const { + int size_in_bits = RegisterSizeInBits(); + VIXL_ASSERT((size_in_bits % 8) == 0); + return size_in_bits / 8; + } + + unsigned TotalSizeInBytes() const { + VIXL_ASSERT(IsValid()); + return RegisterSizeInBytes() * Count(); + } + + private: + RegList list_; + unsigned size_; + CPURegister::RegisterType type_; + + bool IsValid() const; +}; + + +// AAPCS64 callee-saved registers. +extern const CPURegList kCalleeSaved; +extern const CPURegList kCalleeSavedV; + + +// AAPCS64 caller-saved registers. Note that this includes lr. +extern const CPURegList kCallerSaved; +extern const CPURegList kCallerSavedV; + + +// Operand. +class Operand { + public: + // # + // where is int64_t. + // This is allowed to be an implicit constructor because Operand is + // a wrapper class that doesn't normally perform any type conversion. + Operand(int64_t immediate = 0); // NOLINT(runtime/explicit) + + // rm, { #} + // where is one of {LSL, LSR, ASR, ROR}. + // is uint6_t. + // This is allowed to be an implicit constructor because Operand is + // a wrapper class that doesn't normally perform any type conversion. + Operand(Register reg, + Shift shift = LSL, + unsigned shift_amount = 0); // NOLINT(runtime/explicit) + + // rm, { {#}} + // where is one of {UXTB, UXTH, UXTW, UXTX, SXTB, SXTH, SXTW, SXTX}. + // is uint2_t. + explicit Operand(Register reg, Extend extend, unsigned shift_amount = 0); + + bool IsImmediate() const; + bool IsShiftedRegister() const; + bool IsExtendedRegister() const; + bool IsZero() const; + + // This returns an LSL shift (<= 4) operand as an equivalent extend operand, + // which helps in the encoding of instructions that use the stack pointer. + Operand ToExtendedRegister() const; + + int64_t immediate() const { + VIXL_ASSERT(IsImmediate()); + return immediate_; + } + + Register reg() const { + VIXL_ASSERT(IsShiftedRegister() || IsExtendedRegister()); + return reg_; + } + + Shift shift() const { + VIXL_ASSERT(IsShiftedRegister()); + return shift_; + } + + Extend extend() const { + VIXL_ASSERT(IsExtendedRegister()); + return extend_; + } + + unsigned shift_amount() const { + VIXL_ASSERT(IsShiftedRegister() || IsExtendedRegister()); + return shift_amount_; + } + + private: + int64_t immediate_; + Register reg_; + Shift shift_; + Extend extend_; + unsigned shift_amount_; +}; + + +// MemOperand represents the addressing mode of a load or store instruction. +class MemOperand { + public: + explicit MemOperand(Register base, + int64_t offset = 0, + AddrMode addrmode = Offset); + MemOperand(Register base, + Register regoffset, + Shift shift = LSL, + unsigned shift_amount = 0); + MemOperand(Register base, + Register regoffset, + Extend extend, + unsigned shift_amount = 0); + MemOperand(Register base, + const Operand& offset, + AddrMode addrmode = Offset); + + const Register& base() const { return base_; } + const Register& regoffset() const { return regoffset_; } + int64_t offset() const { return offset_; } + AddrMode addrmode() const { return addrmode_; } + Shift shift() const { return shift_; } + Extend extend() const { return extend_; } + unsigned shift_amount() const { return shift_amount_; } + bool IsImmediateOffset() const; + bool IsRegisterOffset() const; + bool IsPreIndex() const; + bool IsPostIndex() const; + + void AddOffset(int64_t offset); + + private: + Register base_; + Register regoffset_; + int64_t offset_; + AddrMode addrmode_; + Shift shift_; + Extend extend_; + unsigned shift_amount_; +}; + + +class LabelTestHelper; // Forward declaration. + + +class Label { + public: + Label() : location_(kLocationUnbound) {} + ~Label() { + // If the label has been linked to, it needs to be bound to a target. + VIXL_ASSERT(!IsLinked() || IsBound()); + } + + bool IsBound() const { return location_ >= 0; } + bool IsLinked() const { return !links_.empty(); } + + ptrdiff_t location() const { return location_; } + + static const int kNPreallocatedLinks = 4; + static const ptrdiff_t kInvalidLinkKey = PTRDIFF_MAX; + static const size_t kReclaimFrom = 512; + static const size_t kReclaimFactor = 2; + + typedef InvalSet LinksSetBase; + typedef InvalSetIterator LabelLinksIteratorBase; + + private: + class LinksSet : public LinksSetBase { + public: + LinksSet() : LinksSetBase() {} + }; + + // Allows iterating over the links of a label. The behaviour is undefined if + // the list of links is modified in any way while iterating. + class LabelLinksIterator : public LabelLinksIteratorBase { + public: + explicit LabelLinksIterator(Label* label) + : LabelLinksIteratorBase(&label->links_) {} + }; + + void Bind(ptrdiff_t location) { + // Labels can only be bound once. + VIXL_ASSERT(!IsBound()); + location_ = location; + } + + void AddLink(ptrdiff_t instruction) { + // If a label is bound, the assembler already has the information it needs + // to write the instruction, so there is no need to add it to links_. + VIXL_ASSERT(!IsBound()); + links_.insert(instruction); + } + + void DeleteLink(ptrdiff_t instruction) { + links_.erase(instruction); + } + + void ClearAllLinks() { + links_.clear(); + } + + // TODO: The comment below considers average case complexity for our + // usual use-cases. The elements of interest are: + // - Branches to a label are emitted in order: branch instructions to a label + // are generated at an offset in the code generation buffer greater than any + // other branch to that same label already generated. As an example, this can + // be broken when an instruction is patched to become a branch. Note that the + // code will still work, but the complexity considerations below may locally + // not apply any more. + // - Veneers are generated in order: for multiple branches of the same type + // branching to the same unbound label going out of range, veneers are + // generated in growing order of the branch instruction offset from the start + // of the buffer. + // + // When creating a veneer for a branch going out of range, the link for this + // branch needs to be removed from this `links_`. Since all branches are + // tracked in one underlying InvalSet, the complexity for this deletion is the + // same as for finding the element, ie. O(n), where n is the number of links + // in the set. + // This could be reduced to O(1) by using the same trick as used when tracking + // branch information for veneers: split the container to use one set per type + // of branch. With that setup, when a veneer is created and the link needs to + // be deleted, if the two points above hold, it must be the minimum element of + // the set for its type of branch, and that minimum element will be accessible + // in O(1). + + // The offsets of the instructions that have linked to this label. + LinksSet links_; + // The label location. + ptrdiff_t location_; + + static const ptrdiff_t kLocationUnbound = -1; + + // It is not safe to copy labels, so disable the copy constructor and operator + // by declaring them private (without an implementation). + Label(const Label&); + void operator=(const Label&); + + // The Assembler class is responsible for binding and linking labels, since + // the stored offsets need to be consistent with the Assembler's buffer. + friend class Assembler; + // The MacroAssembler and VeneerPool handle resolution of branches to distant + // targets. + friend class MacroAssembler; + friend class VeneerPool; +}; + + +// Required InvalSet template specialisations. +#define INVAL_SET_TEMPLATE_PARAMETERS \ + ptrdiff_t, \ + Label::kNPreallocatedLinks, \ + ptrdiff_t, \ + Label::kInvalidLinkKey, \ + Label::kReclaimFrom, \ + Label::kReclaimFactor +template<> +inline ptrdiff_t InvalSet::Key( + const ptrdiff_t& element) { + return element; +} +template<> +inline void InvalSet::SetKey( + ptrdiff_t* element, ptrdiff_t key) { + *element = key; +} +#undef INVAL_SET_TEMPLATE_PARAMETERS + + +class Assembler; +class LiteralPool; + +// A literal is a 32-bit or 64-bit piece of data stored in the instruction +// stream and loaded through a pc relative load. The same literal can be +// referred to by multiple instructions but a literal can only reside at one +// place in memory. A literal can be used by a load before or after being +// placed in memory. +// +// Internally an offset of 0 is associated with a literal which has been +// neither used nor placed. Then two possibilities arise: +// 1) the label is placed, the offset (stored as offset + 1) is used to +// resolve any subsequent load using the label. +// 2) the label is not placed and offset is the offset of the last load using +// the literal (stored as -offset -1). If multiple loads refer to this +// literal then the last load holds the offset of the preceding load and +// all loads form a chain. Once the offset is placed all the loads in the +// chain are resolved and future loads fall back to possibility 1. +class RawLiteral { + public: + enum DeletionPolicy { + kDeletedOnPlacementByPool, + kDeletedOnPoolDestruction, + kManuallyDeleted + }; + + RawLiteral(size_t size, + LiteralPool* literal_pool, + DeletionPolicy deletion_policy = kManuallyDeleted); + + // The literal pool only sees and deletes `RawLiteral*` pointers, but they are + // actually pointing to `Literal` objects. + virtual ~RawLiteral() {} + + size_t size() { + VIXL_STATIC_ASSERT(kDRegSizeInBytes == kXRegSizeInBytes); + VIXL_STATIC_ASSERT(kSRegSizeInBytes == kWRegSizeInBytes); + VIXL_ASSERT((size_ == kXRegSizeInBytes) || + (size_ == kWRegSizeInBytes) || + (size_ == kQRegSizeInBytes)); + return size_; + } + uint64_t raw_value128_low64() { + VIXL_ASSERT(size_ == kQRegSizeInBytes); + return low64_; + } + uint64_t raw_value128_high64() { + VIXL_ASSERT(size_ == kQRegSizeInBytes); + return high64_; + } + uint64_t raw_value64() { + VIXL_ASSERT(size_ == kXRegSizeInBytes); + VIXL_ASSERT(high64_ == 0); + return low64_; + } + uint32_t raw_value32() { + VIXL_ASSERT(size_ == kWRegSizeInBytes); + VIXL_ASSERT(high64_ == 0); + VIXL_ASSERT(is_uint32(low64_) || is_int32(low64_)); + return static_cast(low64_); + } + bool IsUsed() { return offset_ < 0; } + bool IsPlaced() { return offset_ > 0; } + + LiteralPool* GetLiteralPool() const { + return literal_pool_; + } + + ptrdiff_t offset() { + VIXL_ASSERT(IsPlaced()); + return offset_ - 1; + } + + protected: + void set_offset(ptrdiff_t offset) { + VIXL_ASSERT(offset >= 0); + VIXL_ASSERT(IsWordAligned(offset)); + VIXL_ASSERT(!IsPlaced()); + offset_ = offset + 1; + } + ptrdiff_t last_use() { + VIXL_ASSERT(IsUsed()); + return -offset_ - 1; + } + void set_last_use(ptrdiff_t offset) { + VIXL_ASSERT(offset >= 0); + VIXL_ASSERT(IsWordAligned(offset)); + VIXL_ASSERT(!IsPlaced()); + offset_ = -offset - 1; + } + + size_t size_; + ptrdiff_t offset_; + uint64_t low64_; + uint64_t high64_; + + private: + LiteralPool* literal_pool_; + DeletionPolicy deletion_policy_; + + friend class Assembler; + friend class LiteralPool; +}; + + +template +class Literal : public RawLiteral { + public: + explicit Literal(T value, + LiteralPool* literal_pool = NULL, + RawLiteral::DeletionPolicy ownership = kManuallyDeleted) + : RawLiteral(sizeof(value), literal_pool, ownership) { + VIXL_STATIC_ASSERT(sizeof(value) <= kXRegSizeInBytes); + UpdateValue(value); + } + + Literal(T high64, T low64, + LiteralPool* literal_pool = NULL, + RawLiteral::DeletionPolicy ownership = kManuallyDeleted) + : RawLiteral(kQRegSizeInBytes, literal_pool, ownership) { + VIXL_STATIC_ASSERT(sizeof(low64) == (kQRegSizeInBytes / 2)); + UpdateValue(high64, low64); + } + + virtual ~Literal() {} + + // Update the value of this literal, if necessary by rewriting the value in + // the pool. + // If the literal has already been placed in a literal pool, the address of + // the start of the code buffer must be provided, as the literal only knows it + // offset from there. This also allows patching the value after the code has + // been moved in memory. + void UpdateValue(T new_value, uint8_t* code_buffer = NULL) { + VIXL_ASSERT(sizeof(new_value) == size_); + memcpy(&low64_, &new_value, sizeof(new_value)); + if (IsPlaced()) { + VIXL_ASSERT(code_buffer != NULL); + RewriteValueInCode(code_buffer); + } + } + + void UpdateValue(T high64, T low64, uint8_t* code_buffer = NULL) { + VIXL_ASSERT(sizeof(low64) == size_ / 2); + memcpy(&low64_, &low64, sizeof(low64)); + memcpy(&high64_, &high64, sizeof(high64)); + if (IsPlaced()) { + VIXL_ASSERT(code_buffer != NULL); + RewriteValueInCode(code_buffer); + } + } + + void UpdateValue(T new_value, const Assembler* assembler); + void UpdateValue(T high64, T low64, const Assembler* assembler); + + private: + void RewriteValueInCode(uint8_t* code_buffer) { + VIXL_ASSERT(IsPlaced()); + VIXL_STATIC_ASSERT(sizeof(T) <= kXRegSizeInBytes); + switch (size()) { + case kSRegSizeInBytes: + *reinterpret_cast(code_buffer + offset()) = raw_value32(); + break; + case kDRegSizeInBytes: + *reinterpret_cast(code_buffer + offset()) = raw_value64(); + break; + default: + VIXL_ASSERT(size() == kQRegSizeInBytes); + uint64_t* base_address = + reinterpret_cast(code_buffer + offset()); + *base_address = raw_value128_low64(); + *(base_address + 1) = raw_value128_high64(); + } + } +}; + + +// Control whether or not position-independent code should be emitted. +enum PositionIndependentCodeOption { + // All code generated will be position-independent; all branches and + // references to labels generated with the Label class will use PC-relative + // addressing. + PositionIndependentCode, + + // Allow VIXL to generate code that refers to absolute addresses. With this + // option, it will not be possible to copy the code buffer and run it from a + // different address; code must be generated in its final location. + PositionDependentCode, + + // Allow VIXL to assume that the bottom 12 bits of the address will be + // constant, but that the top 48 bits may change. This allows `adrp` to + // function in systems which copy code between pages, but otherwise maintain + // 4KB page alignment. + PageOffsetDependentCode +}; + + +// Control how scaled- and unscaled-offset loads and stores are generated. +enum LoadStoreScalingOption { + // Prefer scaled-immediate-offset instructions, but emit unscaled-offset, + // register-offset, pre-index or post-index instructions if necessary. + PreferScaledOffset, + + // Prefer unscaled-immediate-offset instructions, but emit scaled-offset, + // register-offset, pre-index or post-index instructions if necessary. + PreferUnscaledOffset, + + // Require scaled-immediate-offset instructions. + RequireScaledOffset, + + // Require unscaled-immediate-offset instructions. + RequireUnscaledOffset +}; + + +// Assembler. +class Assembler { + public: + Assembler(size_t capacity, + PositionIndependentCodeOption pic = PositionIndependentCode); + Assembler(byte* buffer, size_t capacity, + PositionIndependentCodeOption pic = PositionIndependentCode); + + // The destructor asserts that one of the following is true: + // * The Assembler object has not been used. + // * Nothing has been emitted since the last Reset() call. + // * Nothing has been emitted since the last FinalizeCode() call. + ~Assembler(); + + // System functions. + + // Start generating code from the beginning of the buffer, discarding any code + // and data that has already been emitted into the buffer. + void Reset(); + + // Finalize a code buffer of generated instructions. This function must be + // called before executing or copying code from the buffer. + void FinalizeCode(); + + // Label. + // Bind a label to the current PC. + void bind(Label* label); + + // Bind a label to a specified offset from the start of the buffer. + void BindToOffset(Label* label, ptrdiff_t offset); + + // Place a literal at the current PC. + void place(RawLiteral* literal); + + ptrdiff_t CursorOffset() const { + return buffer_->CursorOffset(); + } + + ptrdiff_t BufferEndOffset() const { + return static_cast(buffer_->capacity()); + } + + // Return the address of an offset in the buffer. + template + T GetOffsetAddress(ptrdiff_t offset) const { + VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); + return buffer_->GetOffsetAddress(offset); + } + + // Return the address of a bound label. + template + T GetLabelAddress(const Label * label) const { + VIXL_ASSERT(label->IsBound()); + VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); + return GetOffsetAddress(label->location()); + } + + // Return the address of the cursor. + template + T GetCursorAddress() const { + VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); + return GetOffsetAddress(CursorOffset()); + } + + // Return the address of the start of the buffer. + template + T GetStartAddress() const { + VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); + return GetOffsetAddress(0); + } + + Instruction* InstructionAt(ptrdiff_t instruction_offset) { + return GetOffsetAddress(instruction_offset); + } + + ptrdiff_t InstructionOffset(Instruction* instruction) { + VIXL_STATIC_ASSERT(sizeof(*instruction) == 1); + ptrdiff_t offset = instruction - GetStartAddress(); + VIXL_ASSERT((0 <= offset) && + (offset < static_cast(BufferCapacity()))); + return offset; + } + + // Instruction set functions. + + // Branch / Jump instructions. + // Branch to register. + void br(const Register& xn); + + // Branch with link to register. + void blr(const Register& xn); + + // Branch to register with return hint. + void ret(const Register& xn = lr); + + // Unconditional branch to label. + void b(Label* label); + + // Conditional branch to label. + void b(Label* label, Condition cond); + + // Unconditional branch to PC offset. + void b(int imm26); + + // Conditional branch to PC offset. + void b(int imm19, Condition cond); + + // Branch with link to label. + void bl(Label* label); + + // Branch with link to PC offset. + void bl(int imm26); + + // Compare and branch to label if zero. + void cbz(const Register& rt, Label* label); + + // Compare and branch to PC offset if zero. + void cbz(const Register& rt, int imm19); + + // Compare and branch to label if not zero. + void cbnz(const Register& rt, Label* label); + + // Compare and branch to PC offset if not zero. + void cbnz(const Register& rt, int imm19); + + // Table lookup from one register. + void tbl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Table lookup from two registers. + void tbl(const VRegister& vd, + const VRegister& vn, + const VRegister& vn2, + const VRegister& vm); + + // Table lookup from three registers. + void tbl(const VRegister& vd, + const VRegister& vn, + const VRegister& vn2, + const VRegister& vn3, + const VRegister& vm); + + // Table lookup from four registers. + void tbl(const VRegister& vd, + const VRegister& vn, + const VRegister& vn2, + const VRegister& vn3, + const VRegister& vn4, + const VRegister& vm); + + // Table lookup extension from one register. + void tbx(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Table lookup extension from two registers. + void tbx(const VRegister& vd, + const VRegister& vn, + const VRegister& vn2, + const VRegister& vm); + + // Table lookup extension from three registers. + void tbx(const VRegister& vd, + const VRegister& vn, + const VRegister& vn2, + const VRegister& vn3, + const VRegister& vm); + + // Table lookup extension from four registers. + void tbx(const VRegister& vd, + const VRegister& vn, + const VRegister& vn2, + const VRegister& vn3, + const VRegister& vn4, + const VRegister& vm); + + // Test bit and branch to label if zero. + void tbz(const Register& rt, unsigned bit_pos, Label* label); + + // Test bit and branch to PC offset if zero. + void tbz(const Register& rt, unsigned bit_pos, int imm14); + + // Test bit and branch to label if not zero. + void tbnz(const Register& rt, unsigned bit_pos, Label* label); + + // Test bit and branch to PC offset if not zero. + void tbnz(const Register& rt, unsigned bit_pos, int imm14); + + // Address calculation instructions. + // Calculate a PC-relative address. Unlike for branches the offset in adr is + // unscaled (i.e. the result can be unaligned). + + // Calculate the address of a label. + void adr(const Register& rd, Label* label); + + // Calculate the address of a PC offset. + void adr(const Register& rd, int imm21); + + // Calculate the page address of a label. + void adrp(const Register& rd, Label* label); + + // Calculate the page address of a PC offset. + void adrp(const Register& rd, int imm21); + + // Data Processing instructions. + // Add. + void add(const Register& rd, + const Register& rn, + const Operand& operand); + + // Add and update status flags. + void adds(const Register& rd, + const Register& rn, + const Operand& operand); + + // Compare negative. + void cmn(const Register& rn, const Operand& operand); + + // Subtract. + void sub(const Register& rd, + const Register& rn, + const Operand& operand); + + // Subtract and update status flags. + void subs(const Register& rd, + const Register& rn, + const Operand& operand); + + // Compare. + void cmp(const Register& rn, const Operand& operand); + + // Negate. + void neg(const Register& rd, + const Operand& operand); + + // Negate and update status flags. + void negs(const Register& rd, + const Operand& operand); + + // Add with carry bit. + void adc(const Register& rd, + const Register& rn, + const Operand& operand); + + // Add with carry bit and update status flags. + void adcs(const Register& rd, + const Register& rn, + const Operand& operand); + + // Subtract with carry bit. + void sbc(const Register& rd, + const Register& rn, + const Operand& operand); + + // Subtract with carry bit and update status flags. + void sbcs(const Register& rd, + const Register& rn, + const Operand& operand); + + // Negate with carry bit. + void ngc(const Register& rd, + const Operand& operand); + + // Negate with carry bit and update status flags. + void ngcs(const Register& rd, + const Operand& operand); + + // Logical instructions. + // Bitwise and (A & B). + void and_(const Register& rd, + const Register& rn, + const Operand& operand); + + // Bitwise and (A & B) and update status flags. + void ands(const Register& rd, + const Register& rn, + const Operand& operand); + + // Bit test and set flags. + void tst(const Register& rn, const Operand& operand); + + // Bit clear (A & ~B). + void bic(const Register& rd, + const Register& rn, + const Operand& operand); + + // Bit clear (A & ~B) and update status flags. + void bics(const Register& rd, + const Register& rn, + const Operand& operand); + + // Bitwise or (A | B). + void orr(const Register& rd, const Register& rn, const Operand& operand); + + // Bitwise nor (A | ~B). + void orn(const Register& rd, const Register& rn, const Operand& operand); + + // Bitwise eor/xor (A ^ B). + void eor(const Register& rd, const Register& rn, const Operand& operand); + + // Bitwise enor/xnor (A ^ ~B). + void eon(const Register& rd, const Register& rn, const Operand& operand); + + // Logical shift left by variable. + void lslv(const Register& rd, const Register& rn, const Register& rm); + + // Logical shift right by variable. + void lsrv(const Register& rd, const Register& rn, const Register& rm); + + // Arithmetic shift right by variable. + void asrv(const Register& rd, const Register& rn, const Register& rm); + + // Rotate right by variable. + void rorv(const Register& rd, const Register& rn, const Register& rm); + + // Bitfield instructions. + // Bitfield move. + void bfm(const Register& rd, + const Register& rn, + unsigned immr, + unsigned imms); + + // Signed bitfield move. + void sbfm(const Register& rd, + const Register& rn, + unsigned immr, + unsigned imms); + + // Unsigned bitfield move. + void ubfm(const Register& rd, + const Register& rn, + unsigned immr, + unsigned imms); + + // Bfm aliases. + // Bitfield insert. + void bfi(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + VIXL_ASSERT(width >= 1); + VIXL_ASSERT(lsb + width <= rn.size()); + bfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); + } + + // Bitfield extract and insert low. + void bfxil(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + VIXL_ASSERT(width >= 1); + VIXL_ASSERT(lsb + width <= rn.size()); + bfm(rd, rn, lsb, lsb + width - 1); + } + + // Sbfm aliases. + // Arithmetic shift right. + void asr(const Register& rd, const Register& rn, unsigned shift) { + VIXL_ASSERT(shift < rd.size()); + sbfm(rd, rn, shift, rd.size() - 1); + } + + // Signed bitfield insert with zero at right. + void sbfiz(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + VIXL_ASSERT(width >= 1); + VIXL_ASSERT(lsb + width <= rn.size()); + sbfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); + } + + // Signed bitfield extract. + void sbfx(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + VIXL_ASSERT(width >= 1); + VIXL_ASSERT(lsb + width <= rn.size()); + sbfm(rd, rn, lsb, lsb + width - 1); + } + + // Signed extend byte. + void sxtb(const Register& rd, const Register& rn) { + sbfm(rd, rn, 0, 7); + } + + // Signed extend halfword. + void sxth(const Register& rd, const Register& rn) { + sbfm(rd, rn, 0, 15); + } + + // Signed extend word. + void sxtw(const Register& rd, const Register& rn) { + sbfm(rd, rn, 0, 31); + } + + // Ubfm aliases. + // Logical shift left. + void lsl(const Register& rd, const Register& rn, unsigned shift) { + unsigned reg_size = rd.size(); + VIXL_ASSERT(shift < reg_size); + ubfm(rd, rn, (reg_size - shift) % reg_size, reg_size - shift - 1); + } + + // Logical shift right. + void lsr(const Register& rd, const Register& rn, unsigned shift) { + VIXL_ASSERT(shift < rd.size()); + ubfm(rd, rn, shift, rd.size() - 1); + } + + // Unsigned bitfield insert with zero at right. + void ubfiz(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + VIXL_ASSERT(width >= 1); + VIXL_ASSERT(lsb + width <= rn.size()); + ubfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); + } + + // Unsigned bitfield extract. + void ubfx(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + VIXL_ASSERT(width >= 1); + VIXL_ASSERT(lsb + width <= rn.size()); + ubfm(rd, rn, lsb, lsb + width - 1); + } + + // Unsigned extend byte. + void uxtb(const Register& rd, const Register& rn) { + ubfm(rd, rn, 0, 7); + } + + // Unsigned extend halfword. + void uxth(const Register& rd, const Register& rn) { + ubfm(rd, rn, 0, 15); + } + + // Unsigned extend word. + void uxtw(const Register& rd, const Register& rn) { + ubfm(rd, rn, 0, 31); + } + + // Extract. + void extr(const Register& rd, + const Register& rn, + const Register& rm, + unsigned lsb); + + // Conditional select: rd = cond ? rn : rm. + void csel(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond); + + // Conditional select increment: rd = cond ? rn : rm + 1. + void csinc(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond); + + // Conditional select inversion: rd = cond ? rn : ~rm. + void csinv(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond); + + // Conditional select negation: rd = cond ? rn : -rm. + void csneg(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond); + + // Conditional set: rd = cond ? 1 : 0. + void cset(const Register& rd, Condition cond); + + // Conditional set mask: rd = cond ? -1 : 0. + void csetm(const Register& rd, Condition cond); + + // Conditional increment: rd = cond ? rn + 1 : rn. + void cinc(const Register& rd, const Register& rn, Condition cond); + + // Conditional invert: rd = cond ? ~rn : rn. + void cinv(const Register& rd, const Register& rn, Condition cond); + + // Conditional negate: rd = cond ? -rn : rn. + void cneg(const Register& rd, const Register& rn, Condition cond); + + // Rotate right. + void ror(const Register& rd, const Register& rs, unsigned shift) { + extr(rd, rs, rs, shift); + } + + // Conditional comparison. + // Conditional compare negative. + void ccmn(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond); + + // Conditional compare. + void ccmp(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond); + + // CRC-32 checksum from byte. + void crc32b(const Register& rd, + const Register& rn, + const Register& rm); + + // CRC-32 checksum from half-word. + void crc32h(const Register& rd, + const Register& rn, + const Register& rm); + + // CRC-32 checksum from word. + void crc32w(const Register& rd, + const Register& rn, + const Register& rm); + + // CRC-32 checksum from double word. + void crc32x(const Register& rd, + const Register& rn, + const Register& rm); + + // CRC-32 C checksum from byte. + void crc32cb(const Register& rd, + const Register& rn, + const Register& rm); + + // CRC-32 C checksum from half-word. + void crc32ch(const Register& rd, + const Register& rn, + const Register& rm); + + // CRC-32 C checksum from word. + void crc32cw(const Register& rd, + const Register& rn, + const Register& rm); + + // CRC-32C checksum from double word. + void crc32cx(const Register& rd, + const Register& rn, + const Register& rm); + + // Multiply. + void mul(const Register& rd, const Register& rn, const Register& rm); + + // Negated multiply. + void mneg(const Register& rd, const Register& rn, const Register& rm); + + // Signed long multiply: 32 x 32 -> 64-bit. + void smull(const Register& rd, const Register& rn, const Register& rm); + + // Signed multiply high: 64 x 64 -> 64-bit <127:64>. + void smulh(const Register& xd, const Register& xn, const Register& xm); + + // Multiply and accumulate. + void madd(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Multiply and subtract. + void msub(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Signed long multiply and accumulate: 32 x 32 + 64 -> 64-bit. + void smaddl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Unsigned long multiply and accumulate: 32 x 32 + 64 -> 64-bit. + void umaddl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Unsigned long multiply: 32 x 32 -> 64-bit. + void umull(const Register& rd, + const Register& rn, + const Register& rm) { + umaddl(rd, rn, rm, xzr); + } + + // Unsigned multiply high: 64 x 64 -> 64-bit <127:64>. + void umulh(const Register& xd, + const Register& xn, + const Register& xm); + + // Signed long multiply and subtract: 64 - (32 x 32) -> 64-bit. + void smsubl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Unsigned long multiply and subtract: 64 - (32 x 32) -> 64-bit. + void umsubl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Signed integer divide. + void sdiv(const Register& rd, const Register& rn, const Register& rm); + + // Unsigned integer divide. + void udiv(const Register& rd, const Register& rn, const Register& rm); + + // Bit reverse. + void rbit(const Register& rd, const Register& rn); + + // Reverse bytes in 16-bit half words. + void rev16(const Register& rd, const Register& rn); + + // Reverse bytes in 32-bit words. + void rev32(const Register& rd, const Register& rn); + + // Reverse bytes. + void rev(const Register& rd, const Register& rn); + + // Count leading zeroes. + void clz(const Register& rd, const Register& rn); + + // Count leading sign bits. + void cls(const Register& rd, const Register& rn); + + // Memory instructions. + // Load integer or FP register. + void ldr(const CPURegister& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferScaledOffset); + + // Store integer or FP register. + void str(const CPURegister& rt, const MemOperand& dst, + LoadStoreScalingOption option = PreferScaledOffset); + + // Load word with sign extension. + void ldrsw(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferScaledOffset); + + // Load byte. + void ldrb(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferScaledOffset); + + // Store byte. + void strb(const Register& rt, const MemOperand& dst, + LoadStoreScalingOption option = PreferScaledOffset); + + // Load byte with sign extension. + void ldrsb(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferScaledOffset); + + // Load half-word. + void ldrh(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferScaledOffset); + + // Store half-word. + void strh(const Register& rt, const MemOperand& dst, + LoadStoreScalingOption option = PreferScaledOffset); + + // Load half-word with sign extension. + void ldrsh(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferScaledOffset); + + // Load integer or FP register (with unscaled offset). + void ldur(const CPURegister& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Store integer or FP register (with unscaled offset). + void stur(const CPURegister& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Load word with sign extension. + void ldursw(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Load byte (with unscaled offset). + void ldurb(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Store byte (with unscaled offset). + void sturb(const Register& rt, const MemOperand& dst, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Load byte with sign extension (and unscaled offset). + void ldursb(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Load half-word (with unscaled offset). + void ldurh(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Store half-word (with unscaled offset). + void sturh(const Register& rt, const MemOperand& dst, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Load half-word with sign extension (and unscaled offset). + void ldursh(const Register& rt, const MemOperand& src, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Load integer or FP register pair. + void ldp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& src); + + // Store integer or FP register pair. + void stp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& dst); + + // Load word pair with sign extension. + void ldpsw(const Register& rt, const Register& rt2, const MemOperand& src); + + // Load integer or FP register pair, non-temporal. + void ldnp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& src); + + // Store integer or FP register pair, non-temporal. + void stnp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& dst); + + // Load integer or FP register from literal pool. + void ldr(const CPURegister& rt, RawLiteral* literal); + + // Load word with sign extension from literal pool. + void ldrsw(const Register& rt, RawLiteral* literal); + + // Load integer or FP register from pc + imm19 << 2. + void ldr(const CPURegister& rt, int imm19); + + // Load word with sign extension from pc + imm19 << 2. + void ldrsw(const Register& rt, int imm19); + + // Store exclusive byte. + void stxrb(const Register& rs, const Register& rt, const MemOperand& dst); + + // Store exclusive half-word. + void stxrh(const Register& rs, const Register& rt, const MemOperand& dst); + + // Store exclusive register. + void stxr(const Register& rs, const Register& rt, const MemOperand& dst); + + // Load exclusive byte. + void ldxrb(const Register& rt, const MemOperand& src); + + // Load exclusive half-word. + void ldxrh(const Register& rt, const MemOperand& src); + + // Load exclusive register. + void ldxr(const Register& rt, const MemOperand& src); + + // Store exclusive register pair. + void stxp(const Register& rs, + const Register& rt, + const Register& rt2, + const MemOperand& dst); + + // Load exclusive register pair. + void ldxp(const Register& rt, const Register& rt2, const MemOperand& src); + + // Store-release exclusive byte. + void stlxrb(const Register& rs, const Register& rt, const MemOperand& dst); + + // Store-release exclusive half-word. + void stlxrh(const Register& rs, const Register& rt, const MemOperand& dst); + + // Store-release exclusive register. + void stlxr(const Register& rs, const Register& rt, const MemOperand& dst); + + // Load-acquire exclusive byte. + void ldaxrb(const Register& rt, const MemOperand& src); + + // Load-acquire exclusive half-word. + void ldaxrh(const Register& rt, const MemOperand& src); + + // Load-acquire exclusive register. + void ldaxr(const Register& rt, const MemOperand& src); + + // Store-release exclusive register pair. + void stlxp(const Register& rs, + const Register& rt, + const Register& rt2, + const MemOperand& dst); + + // Load-acquire exclusive register pair. + void ldaxp(const Register& rt, const Register& rt2, const MemOperand& src); + + // Store-release byte. + void stlrb(const Register& rt, const MemOperand& dst); + + // Store-release half-word. + void stlrh(const Register& rt, const MemOperand& dst); + + // Store-release register. + void stlr(const Register& rt, const MemOperand& dst); + + // Load-acquire byte. + void ldarb(const Register& rt, const MemOperand& src); + + // Load-acquire half-word. + void ldarh(const Register& rt, const MemOperand& src); + + // Load-acquire register. + void ldar(const Register& rt, const MemOperand& src); + + // Prefetch memory. + void prfm(PrefetchOperation op, const MemOperand& addr, + LoadStoreScalingOption option = PreferScaledOffset); + + // Prefetch memory (with unscaled offset). + void prfum(PrefetchOperation op, const MemOperand& addr, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Prefetch memory in the literal pool. + void prfm(PrefetchOperation op, RawLiteral* literal); + + // Prefetch from pc + imm19 << 2. + void prfm(PrefetchOperation op, int imm19); + + // Move instructions. The default shift of -1 indicates that the move + // instruction will calculate an appropriate 16-bit immediate and left shift + // that is equal to the 64-bit immediate argument. If an explicit left shift + // is specified (0, 16, 32 or 48), the immediate must be a 16-bit value. + // + // For movk, an explicit shift can be used to indicate which half word should + // be overwritten, eg. movk(x0, 0, 0) will overwrite the least-significant + // half word with zero, whereas movk(x0, 0, 48) will overwrite the + // most-significant. + + // Move immediate and keep. + void movk(const Register& rd, uint64_t imm, int shift = -1) { + MoveWide(rd, imm, shift, MOVK); + } + + // Move inverted immediate. + void movn(const Register& rd, uint64_t imm, int shift = -1) { + MoveWide(rd, imm, shift, MOVN); + } + + // Move immediate. + void movz(const Register& rd, uint64_t imm, int shift = -1) { + MoveWide(rd, imm, shift, MOVZ); + } + + // Misc instructions. + // Monitor debug-mode breakpoint. + void brk(int code); + + // Halting debug-mode breakpoint. + void hlt(int code); + + // Generate exception targeting EL1. + void svc(int code); + + // Move register to register. + void mov(const Register& rd, const Register& rn); + + // Move inverted operand to register. + void mvn(const Register& rd, const Operand& operand); + + // System instructions. + // Move to register from system register. + void mrs(const Register& rt, SystemRegister sysreg); + + // Move from register to system register. + void msr(SystemRegister sysreg, const Register& rt); + + // System instruction. + void sys(int op1, int crn, int crm, int op2, const Register& rt = xzr); + + // System instruction with pre-encoded op (op1:crn:crm:op2). + void sys(int op, const Register& rt = xzr); + + // System data cache operation. + void dc(DataCacheOp op, const Register& rt); + + // System instruction cache operation. + void ic(InstructionCacheOp op, const Register& rt); + + // System hint. + void hint(SystemHint code); + + // Clear exclusive monitor. + void clrex(int imm4 = 0xf); + + // Data memory barrier. + void dmb(BarrierDomain domain, BarrierType type); + + // Data synchronization barrier. + void dsb(BarrierDomain domain, BarrierType type); + + // Instruction synchronization barrier. + void isb(); + + // Alias for system instructions. + // No-op. + void nop() { + hint(NOP); + } + + // FP and NEON instructions. + // Move double precision immediate to FP register. + void fmov(const VRegister& vd, double imm); + + // Move single precision immediate to FP register. + void fmov(const VRegister& vd, float imm); + + // Move FP register to register. + void fmov(const Register& rd, const VRegister& fn); + + // Move register to FP register. + void fmov(const VRegister& vd, const Register& rn); + + // Move FP register to FP register. + void fmov(const VRegister& vd, const VRegister& fn); + + // Move 64-bit register to top half of 128-bit FP register. + void fmov(const VRegister& vd, int index, const Register& rn); + + // Move top half of 128-bit FP register to 64-bit register. + void fmov(const Register& rd, const VRegister& vn, int index); + + // FP add. + void fadd(const VRegister& vd, const VRegister& vn, const VRegister& vm); + + // FP subtract. + void fsub(const VRegister& vd, const VRegister& vn, const VRegister& vm); + + // FP multiply. + void fmul(const VRegister& vd, const VRegister& vn, const VRegister& vm); + + // FP fused multiply-add. + void fmadd(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + const VRegister& va); + + // FP fused multiply-subtract. + void fmsub(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + const VRegister& va); + + // FP fused multiply-add and negate. + void fnmadd(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + const VRegister& va); + + // FP fused multiply-subtract and negate. + void fnmsub(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + const VRegister& va); + + // FP multiply-negate scalar. + void fnmul(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // FP reciprocal exponent scalar. + void frecpx(const VRegister& vd, + const VRegister& vn); + + // FP divide. + void fdiv(const VRegister& vd, const VRegister& fn, const VRegister& vm); + + // FP maximum. + void fmax(const VRegister& vd, const VRegister& fn, const VRegister& vm); + + // FP minimum. + void fmin(const VRegister& vd, const VRegister& fn, const VRegister& vm); + + // FP maximum number. + void fmaxnm(const VRegister& vd, const VRegister& fn, const VRegister& vm); + + // FP minimum number. + void fminnm(const VRegister& vd, const VRegister& fn, const VRegister& vm); + + // FP absolute. + void fabs(const VRegister& vd, const VRegister& vn); + + // FP negate. + void fneg(const VRegister& vd, const VRegister& vn); + + // FP square root. + void fsqrt(const VRegister& vd, const VRegister& vn); + + // FP round to integer, nearest with ties to away. + void frinta(const VRegister& vd, const VRegister& vn); + + // FP round to integer, implicit rounding. + void frinti(const VRegister& vd, const VRegister& vn); + + // FP round to integer, toward minus infinity. + void frintm(const VRegister& vd, const VRegister& vn); + + // FP round to integer, nearest with ties to even. + void frintn(const VRegister& vd, const VRegister& vn); + + // FP round to integer, toward plus infinity. + void frintp(const VRegister& vd, const VRegister& vn); + + // FP round to integer, exact, implicit rounding. + void frintx(const VRegister& vd, const VRegister& vn); + + // FP round to integer, towards zero. + void frintz(const VRegister& vd, const VRegister& vn); + + void FPCompareMacro(const VRegister& vn, + double value, + FPTrapFlags trap); + + void FPCompareMacro(const VRegister& vn, + const VRegister& vm, + FPTrapFlags trap); + + // FP compare registers. + void fcmp(const VRegister& vn, const VRegister& vm); + + // FP compare immediate. + void fcmp(const VRegister& vn, double value); + + void FPCCompareMacro(const VRegister& vn, + const VRegister& vm, + StatusFlags nzcv, + Condition cond, + FPTrapFlags trap); + + // FP conditional compare. + void fccmp(const VRegister& vn, + const VRegister& vm, + StatusFlags nzcv, + Condition cond); + + // FP signaling compare registers. + void fcmpe(const VRegister& vn, const VRegister& vm); + + // FP signaling compare immediate. + void fcmpe(const VRegister& vn, double value); + + // FP conditional signaling compare. + void fccmpe(const VRegister& vn, + const VRegister& vm, + StatusFlags nzcv, + Condition cond); + + // FP conditional select. + void fcsel(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + Condition cond); + + // Common FP Convert functions. + void NEONFPConvertToInt(const Register& rd, + const VRegister& vn, + Instr op); + void NEONFPConvertToInt(const VRegister& vd, + const VRegister& vn, + Instr op); + + // FP convert between precisions. + void fcvt(const VRegister& vd, const VRegister& vn); + + // FP convert to higher precision. + void fcvtl(const VRegister& vd, const VRegister& vn); + + // FP convert to higher precision (second part). + void fcvtl2(const VRegister& vd, const VRegister& vn); + + // FP convert to lower precision. + void fcvtn(const VRegister& vd, const VRegister& vn); + + // FP convert to lower prevision (second part). + void fcvtn2(const VRegister& vd, const VRegister& vn); + + // FP convert to lower precision, rounding to odd. + void fcvtxn(const VRegister& vd, const VRegister& vn); + + // FP convert to lower precision, rounding to odd (second part). + void fcvtxn2(const VRegister& vd, const VRegister& vn); + + // FP convert to signed integer, nearest with ties to away. + void fcvtas(const Register& rd, const VRegister& vn); + + // FP convert to unsigned integer, nearest with ties to away. + void fcvtau(const Register& rd, const VRegister& vn); + + // FP convert to signed integer, nearest with ties to away. + void fcvtas(const VRegister& vd, const VRegister& vn); + + // FP convert to unsigned integer, nearest with ties to away. + void fcvtau(const VRegister& vd, const VRegister& vn); + + // FP convert to signed integer, round towards -infinity. + void fcvtms(const Register& rd, const VRegister& vn); + + // FP convert to unsigned integer, round towards -infinity. + void fcvtmu(const Register& rd, const VRegister& vn); + + // FP convert to signed integer, round towards -infinity. + void fcvtms(const VRegister& vd, const VRegister& vn); + + // FP convert to unsigned integer, round towards -infinity. + void fcvtmu(const VRegister& vd, const VRegister& vn); + + // FP convert to signed integer, nearest with ties to even. + void fcvtns(const Register& rd, const VRegister& vn); + + // FP convert to unsigned integer, nearest with ties to even. + void fcvtnu(const Register& rd, const VRegister& vn); + + // FP convert to signed integer, nearest with ties to even. + void fcvtns(const VRegister& rd, const VRegister& vn); + + // FP convert to unsigned integer, nearest with ties to even. + void fcvtnu(const VRegister& rd, const VRegister& vn); + + // FP convert to signed integer or fixed-point, round towards zero. + void fcvtzs(const Register& rd, const VRegister& vn, int fbits = 0); + + // FP convert to unsigned integer or fixed-point, round towards zero. + void fcvtzu(const Register& rd, const VRegister& vn, int fbits = 0); + + // FP convert to signed integer or fixed-point, round towards zero. + void fcvtzs(const VRegister& vd, const VRegister& vn, int fbits = 0); + + // FP convert to unsigned integer or fixed-point, round towards zero. + void fcvtzu(const VRegister& vd, const VRegister& vn, int fbits = 0); + + // FP convert to signed integer, round towards +infinity. + void fcvtps(const Register& rd, const VRegister& vn); + + // FP convert to unsigned integer, round towards +infinity. + void fcvtpu(const Register& rd, const VRegister& vn); + + // FP convert to signed integer, round towards +infinity. + void fcvtps(const VRegister& vd, const VRegister& vn); + + // FP convert to unsigned integer, round towards +infinity. + void fcvtpu(const VRegister& vd, const VRegister& vn); + + // Convert signed integer or fixed point to FP. + void scvtf(const VRegister& fd, const Register& rn, int fbits = 0); + + // Convert unsigned integer or fixed point to FP. + void ucvtf(const VRegister& fd, const Register& rn, int fbits = 0); + + // Convert signed integer or fixed-point to FP. + void scvtf(const VRegister& fd, const VRegister& vn, int fbits = 0); + + // Convert unsigned integer or fixed-point to FP. + void ucvtf(const VRegister& fd, const VRegister& vn, int fbits = 0); + + // Unsigned absolute difference. + void uabd(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed absolute difference. + void sabd(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned absolute difference and accumulate. + void uaba(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed absolute difference and accumulate. + void saba(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Add. + void add(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Subtract. + void sub(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned halving add. + void uhadd(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed halving add. + void shadd(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned rounding halving add. + void urhadd(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed rounding halving add. + void srhadd(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned halving sub. + void uhsub(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed halving sub. + void shsub(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned saturating add. + void uqadd(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed saturating add. + void sqadd(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned saturating subtract. + void uqsub(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed saturating subtract. + void sqsub(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Add pairwise. + void addp(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Add pair of elements scalar. + void addp(const VRegister& vd, + const VRegister& vn); + + // Multiply-add to accumulator. + void mla(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Multiply-subtract to accumulator. + void mls(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Multiply. + void mul(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Multiply by scalar element. + void mul(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Multiply-add by scalar element. + void mla(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Multiply-subtract by scalar element. + void mls(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Signed long multiply-add by scalar element. + void smlal(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Signed long multiply-add by scalar element (second part). + void smlal2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Unsigned long multiply-add by scalar element. + void umlal(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Unsigned long multiply-add by scalar element (second part). + void umlal2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Signed long multiply-sub by scalar element. + void smlsl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Signed long multiply-sub by scalar element (second part). + void smlsl2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Unsigned long multiply-sub by scalar element. + void umlsl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Unsigned long multiply-sub by scalar element (second part). + void umlsl2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Signed long multiply by scalar element. + void smull(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Signed long multiply by scalar element (second part). + void smull2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Unsigned long multiply by scalar element. + void umull(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Unsigned long multiply by scalar element (second part). + void umull2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Signed saturating double long multiply by element. + void sqdmull(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Signed saturating double long multiply by element (second part). + void sqdmull2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Signed saturating doubling long multiply-add by element. + void sqdmlal(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Signed saturating doubling long multiply-add by element (second part). + void sqdmlal2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Signed saturating doubling long multiply-sub by element. + void sqdmlsl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Signed saturating doubling long multiply-sub by element (second part). + void sqdmlsl2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Compare equal. + void cmeq(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Compare signed greater than or equal. + void cmge(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Compare signed greater than. + void cmgt(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Compare unsigned higher. + void cmhi(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Compare unsigned higher or same. + void cmhs(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Compare bitwise test bits nonzero. + void cmtst(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Compare bitwise to zero. + void cmeq(const VRegister& vd, + const VRegister& vn, + int value); + + // Compare signed greater than or equal to zero. + void cmge(const VRegister& vd, + const VRegister& vn, + int value); + + // Compare signed greater than zero. + void cmgt(const VRegister& vd, + const VRegister& vn, + int value); + + // Compare signed less than or equal to zero. + void cmle(const VRegister& vd, + const VRegister& vn, + int value); + + // Compare signed less than zero. + void cmlt(const VRegister& vd, + const VRegister& vn, + int value); + + // Signed shift left by register. + void sshl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned shift left by register. + void ushl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed saturating shift left by register. + void sqshl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned saturating shift left by register. + void uqshl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed rounding shift left by register. + void srshl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned rounding shift left by register. + void urshl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed saturating rounding shift left by register. + void sqrshl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned saturating rounding shift left by register. + void uqrshl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Bitwise and. + void and_(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Bitwise or. + void orr(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Bitwise or immediate. + void orr(const VRegister& vd, + const int imm8, + const int left_shift = 0); + + // Move register to register. + void mov(const VRegister& vd, + const VRegister& vn); + + // Bitwise orn. + void orn(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Bitwise eor. + void eor(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Bit clear immediate. + void bic(const VRegister& vd, + const int imm8, + const int left_shift = 0); + + // Bit clear. + void bic(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Bitwise insert if false. + void bif(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Bitwise insert if true. + void bit(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Bitwise select. + void bsl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Polynomial multiply. + void pmul(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Vector move immediate. + void movi(const VRegister& vd, + const uint64_t imm, + Shift shift = LSL, + const int shift_amount = 0); + + // Bitwise not. + void mvn(const VRegister& vd, + const VRegister& vn); + + // Vector move inverted immediate. + void mvni(const VRegister& vd, + const int imm8, + Shift shift = LSL, + const int shift_amount = 0); + + // Signed saturating accumulate of unsigned value. + void suqadd(const VRegister& vd, + const VRegister& vn); + + // Unsigned saturating accumulate of signed value. + void usqadd(const VRegister& vd, + const VRegister& vn); + + // Absolute value. + void abs(const VRegister& vd, + const VRegister& vn); + + // Signed saturating absolute value. + void sqabs(const VRegister& vd, + const VRegister& vn); + + // Negate. + void neg(const VRegister& vd, + const VRegister& vn); + + // Signed saturating negate. + void sqneg(const VRegister& vd, + const VRegister& vn); + + // Bitwise not. + void not_(const VRegister& vd, + const VRegister& vn); + + // Extract narrow. + void xtn(const VRegister& vd, + const VRegister& vn); + + // Extract narrow (second part). + void xtn2(const VRegister& vd, + const VRegister& vn); + + // Signed saturating extract narrow. + void sqxtn(const VRegister& vd, + const VRegister& vn); + + // Signed saturating extract narrow (second part). + void sqxtn2(const VRegister& vd, + const VRegister& vn); + + // Unsigned saturating extract narrow. + void uqxtn(const VRegister& vd, + const VRegister& vn); + + // Unsigned saturating extract narrow (second part). + void uqxtn2(const VRegister& vd, + const VRegister& vn); + + // Signed saturating extract unsigned narrow. + void sqxtun(const VRegister& vd, + const VRegister& vn); + + // Signed saturating extract unsigned narrow (second part). + void sqxtun2(const VRegister& vd, + const VRegister& vn); + + // Extract vector from pair of vectors. + void ext(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int index); + + // Duplicate vector element to vector or scalar. + void dup(const VRegister& vd, + const VRegister& vn, + int vn_index); + + // Move vector element to scalar. + void mov(const VRegister& vd, + const VRegister& vn, + int vn_index); + + // Duplicate general-purpose register to vector. + void dup(const VRegister& vd, + const Register& rn); + + // Insert vector element from another vector element. + void ins(const VRegister& vd, + int vd_index, + const VRegister& vn, + int vn_index); + + // Move vector element to another vector element. + void mov(const VRegister& vd, + int vd_index, + const VRegister& vn, + int vn_index); + + // Insert vector element from general-purpose register. + void ins(const VRegister& vd, + int vd_index, + const Register& rn); + + // Move general-purpose register to a vector element. + void mov(const VRegister& vd, + int vd_index, + const Register& rn); + + // Unsigned move vector element to general-purpose register. + void umov(const Register& rd, + const VRegister& vn, + int vn_index); + + // Move vector element to general-purpose register. + void mov(const Register& rd, + const VRegister& vn, + int vn_index); + + // Signed move vector element to general-purpose register. + void smov(const Register& rd, + const VRegister& vn, + int vn_index); + + // One-element structure load to one register. + void ld1(const VRegister& vt, + const MemOperand& src); + + // One-element structure load to two registers. + void ld1(const VRegister& vt, + const VRegister& vt2, + const MemOperand& src); + + // One-element structure load to three registers. + void ld1(const VRegister& vt, + const VRegister& vt2, + const VRegister& vt3, + const MemOperand& src); + + // One-element structure load to four registers. + void ld1(const VRegister& vt, + const VRegister& vt2, + const VRegister& vt3, + const VRegister& vt4, + const MemOperand& src); + + // One-element single structure load to one lane. + void ld1(const VRegister& vt, + int lane, + const MemOperand& src); + + // One-element single structure load to all lanes. + void ld1r(const VRegister& vt, + const MemOperand& src); + + // Two-element structure load. + void ld2(const VRegister& vt, + const VRegister& vt2, + const MemOperand& src); + + // Two-element single structure load to one lane. + void ld2(const VRegister& vt, + const VRegister& vt2, + int lane, + const MemOperand& src); + + // Two-element single structure load to all lanes. + void ld2r(const VRegister& vt, + const VRegister& vt2, + const MemOperand& src); + + // Three-element structure load. + void ld3(const VRegister& vt, + const VRegister& vt2, + const VRegister& vt3, + const MemOperand& src); + + // Three-element single structure load to one lane. + void ld3(const VRegister& vt, + const VRegister& vt2, + const VRegister& vt3, + int lane, + const MemOperand& src); + + // Three-element single structure load to all lanes. + void ld3r(const VRegister& vt, + const VRegister& vt2, + const VRegister& vt3, + const MemOperand& src); + + // Four-element structure load. + void ld4(const VRegister& vt, + const VRegister& vt2, + const VRegister& vt3, + const VRegister& vt4, + const MemOperand& src); + + // Four-element single structure load to one lane. + void ld4(const VRegister& vt, + const VRegister& vt2, + const VRegister& vt3, + const VRegister& vt4, + int lane, + const MemOperand& src); + + // Four-element single structure load to all lanes. + void ld4r(const VRegister& vt, + const VRegister& vt2, + const VRegister& vt3, + const VRegister& vt4, + const MemOperand& src); + + // Count leading sign bits. + void cls(const VRegister& vd, + const VRegister& vn); + + // Count leading zero bits (vector). + void clz(const VRegister& vd, + const VRegister& vn); + + // Population count per byte. + void cnt(const VRegister& vd, + const VRegister& vn); + + // Reverse bit order. + void rbit(const VRegister& vd, + const VRegister& vn); + + // Reverse elements in 16-bit halfwords. + void rev16(const VRegister& vd, + const VRegister& vn); + + // Reverse elements in 32-bit words. + void rev32(const VRegister& vd, + const VRegister& vn); + + // Reverse elements in 64-bit doublewords. + void rev64(const VRegister& vd, + const VRegister& vn); + + // Unsigned reciprocal square root estimate. + void ursqrte(const VRegister& vd, + const VRegister& vn); + + // Unsigned reciprocal estimate. + void urecpe(const VRegister& vd, + const VRegister& vn); + + // Signed pairwise long add. + void saddlp(const VRegister& vd, + const VRegister& vn); + + // Unsigned pairwise long add. + void uaddlp(const VRegister& vd, + const VRegister& vn); + + // Signed pairwise long add and accumulate. + void sadalp(const VRegister& vd, + const VRegister& vn); + + // Unsigned pairwise long add and accumulate. + void uadalp(const VRegister& vd, + const VRegister& vn); + + // Shift left by immediate. + void shl(const VRegister& vd, + const VRegister& vn, + int shift); + + // Signed saturating shift left by immediate. + void sqshl(const VRegister& vd, + const VRegister& vn, + int shift); + + // Signed saturating shift left unsigned by immediate. + void sqshlu(const VRegister& vd, + const VRegister& vn, + int shift); + + // Unsigned saturating shift left by immediate. + void uqshl(const VRegister& vd, + const VRegister& vn, + int shift); + + // Signed shift left long by immediate. + void sshll(const VRegister& vd, + const VRegister& vn, + int shift); + + // Signed shift left long by immediate (second part). + void sshll2(const VRegister& vd, + const VRegister& vn, + int shift); + + // Signed extend long. + void sxtl(const VRegister& vd, + const VRegister& vn); + + // Signed extend long (second part). + void sxtl2(const VRegister& vd, + const VRegister& vn); + + // Unsigned shift left long by immediate. + void ushll(const VRegister& vd, + const VRegister& vn, + int shift); + + // Unsigned shift left long by immediate (second part). + void ushll2(const VRegister& vd, + const VRegister& vn, + int shift); + + // Shift left long by element size. + void shll(const VRegister& vd, + const VRegister& vn, + int shift); + + // Shift left long by element size (second part). + void shll2(const VRegister& vd, + const VRegister& vn, + int shift); + + // Unsigned extend long. + void uxtl(const VRegister& vd, + const VRegister& vn); + + // Unsigned extend long (second part). + void uxtl2(const VRegister& vd, + const VRegister& vn); + + // Shift left by immediate and insert. + void sli(const VRegister& vd, + const VRegister& vn, + int shift); + + // Shift right by immediate and insert. + void sri(const VRegister& vd, + const VRegister& vn, + int shift); + + // Signed maximum. + void smax(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed pairwise maximum. + void smaxp(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Add across vector. + void addv(const VRegister& vd, + const VRegister& vn); + + // Signed add long across vector. + void saddlv(const VRegister& vd, + const VRegister& vn); + + // Unsigned add long across vector. + void uaddlv(const VRegister& vd, + const VRegister& vn); + + // FP maximum number across vector. + void fmaxnmv(const VRegister& vd, + const VRegister& vn); + + // FP maximum across vector. + void fmaxv(const VRegister& vd, + const VRegister& vn); + + // FP minimum number across vector. + void fminnmv(const VRegister& vd, + const VRegister& vn); + + // FP minimum across vector. + void fminv(const VRegister& vd, + const VRegister& vn); + + // Signed maximum across vector. + void smaxv(const VRegister& vd, + const VRegister& vn); + + // Signed minimum. + void smin(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed minimum pairwise. + void sminp(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed minimum across vector. + void sminv(const VRegister& vd, + const VRegister& vn); + + // One-element structure store from one register. + void st1(const VRegister& vt, + const MemOperand& src); + + // One-element structure store from two registers. + void st1(const VRegister& vt, + const VRegister& vt2, + const MemOperand& src); + + // One-element structure store from three registers. + void st1(const VRegister& vt, + const VRegister& vt2, + const VRegister& vt3, + const MemOperand& src); + + // One-element structure store from four registers. + void st1(const VRegister& vt, + const VRegister& vt2, + const VRegister& vt3, + const VRegister& vt4, + const MemOperand& src); + + // One-element single structure store from one lane. + void st1(const VRegister& vt, + int lane, + const MemOperand& src); + + // Two-element structure store from two registers. + void st2(const VRegister& vt, + const VRegister& vt2, + const MemOperand& src); + + // Two-element single structure store from two lanes. + void st2(const VRegister& vt, + const VRegister& vt2, + int lane, + const MemOperand& src); + + // Three-element structure store from three registers. + void st3(const VRegister& vt, + const VRegister& vt2, + const VRegister& vt3, + const MemOperand& src); + + // Three-element single structure store from three lanes. + void st3(const VRegister& vt, + const VRegister& vt2, + const VRegister& vt3, + int lane, + const MemOperand& src); + + // Four-element structure store from four registers. + void st4(const VRegister& vt, + const VRegister& vt2, + const VRegister& vt3, + const VRegister& vt4, + const MemOperand& src); + + // Four-element single structure store from four lanes. + void st4(const VRegister& vt, + const VRegister& vt2, + const VRegister& vt3, + const VRegister& vt4, + int lane, + const MemOperand& src); + + // Unsigned add long. + void uaddl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned add long (second part). + void uaddl2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned add wide. + void uaddw(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned add wide (second part). + void uaddw2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed add long. + void saddl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed add long (second part). + void saddl2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed add wide. + void saddw(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed add wide (second part). + void saddw2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned subtract long. + void usubl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned subtract long (second part). + void usubl2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned subtract wide. + void usubw(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned subtract wide (second part). + void usubw2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed subtract long. + void ssubl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed subtract long (second part). + void ssubl2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed integer subtract wide. + void ssubw(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed integer subtract wide (second part). + void ssubw2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned maximum. + void umax(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned pairwise maximum. + void umaxp(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned maximum across vector. + void umaxv(const VRegister& vd, + const VRegister& vn); + + // Unsigned minimum. + void umin(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned pairwise minimum. + void uminp(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned minimum across vector. + void uminv(const VRegister& vd, + const VRegister& vn); + + // Transpose vectors (primary). + void trn1(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Transpose vectors (secondary). + void trn2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unzip vectors (primary). + void uzp1(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unzip vectors (secondary). + void uzp2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Zip vectors (primary). + void zip1(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Zip vectors (secondary). + void zip2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed shift right by immediate. + void sshr(const VRegister& vd, + const VRegister& vn, + int shift); + + // Unsigned shift right by immediate. + void ushr(const VRegister& vd, + const VRegister& vn, + int shift); + + // Signed rounding shift right by immediate. + void srshr(const VRegister& vd, + const VRegister& vn, + int shift); + + // Unsigned rounding shift right by immediate. + void urshr(const VRegister& vd, + const VRegister& vn, + int shift); + + // Signed shift right by immediate and accumulate. + void ssra(const VRegister& vd, + const VRegister& vn, + int shift); + + // Unsigned shift right by immediate and accumulate. + void usra(const VRegister& vd, + const VRegister& vn, + int shift); + + // Signed rounding shift right by immediate and accumulate. + void srsra(const VRegister& vd, + const VRegister& vn, + int shift); + + // Unsigned rounding shift right by immediate and accumulate. + void ursra(const VRegister& vd, + const VRegister& vn, + int shift); + + // Shift right narrow by immediate. + void shrn(const VRegister& vd, + const VRegister& vn, + int shift); + + // Shift right narrow by immediate (second part). + void shrn2(const VRegister& vd, + const VRegister& vn, + int shift); + + // Rounding shift right narrow by immediate. + void rshrn(const VRegister& vd, + const VRegister& vn, + int shift); + + // Rounding shift right narrow by immediate (second part). + void rshrn2(const VRegister& vd, + const VRegister& vn, + int shift); + + // Unsigned saturating shift right narrow by immediate. + void uqshrn(const VRegister& vd, + const VRegister& vn, + int shift); + + // Unsigned saturating shift right narrow by immediate (second part). + void uqshrn2(const VRegister& vd, + const VRegister& vn, + int shift); + + // Unsigned saturating rounding shift right narrow by immediate. + void uqrshrn(const VRegister& vd, + const VRegister& vn, + int shift); + + // Unsigned saturating rounding shift right narrow by immediate (second part). + void uqrshrn2(const VRegister& vd, + const VRegister& vn, + int shift); + + // Signed saturating shift right narrow by immediate. + void sqshrn(const VRegister& vd, + const VRegister& vn, + int shift); + + // Signed saturating shift right narrow by immediate (second part). + void sqshrn2(const VRegister& vd, + const VRegister& vn, + int shift); + + // Signed saturating rounded shift right narrow by immediate. + void sqrshrn(const VRegister& vd, + const VRegister& vn, + int shift); + + // Signed saturating rounded shift right narrow by immediate (second part). + void sqrshrn2(const VRegister& vd, + const VRegister& vn, + int shift); + + // Signed saturating shift right unsigned narrow by immediate. + void sqshrun(const VRegister& vd, + const VRegister& vn, + int shift); + + // Signed saturating shift right unsigned narrow by immediate (second part). + void sqshrun2(const VRegister& vd, + const VRegister& vn, + int shift); + + // Signed sat rounded shift right unsigned narrow by immediate. + void sqrshrun(const VRegister& vd, + const VRegister& vn, + int shift); + + // Signed sat rounded shift right unsigned narrow by immediate (second part). + void sqrshrun2(const VRegister& vd, + const VRegister& vn, + int shift); + + // FP reciprocal step. + void frecps(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // FP reciprocal estimate. + void frecpe(const VRegister& vd, + const VRegister& vn); + + // FP reciprocal square root estimate. + void frsqrte(const VRegister& vd, + const VRegister& vn); + + // FP reciprocal square root step. + void frsqrts(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed absolute difference and accumulate long. + void sabal(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed absolute difference and accumulate long (second part). + void sabal2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned absolute difference and accumulate long. + void uabal(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned absolute difference and accumulate long (second part). + void uabal2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed absolute difference long. + void sabdl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed absolute difference long (second part). + void sabdl2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned absolute difference long. + void uabdl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned absolute difference long (second part). + void uabdl2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Polynomial multiply long. + void pmull(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Polynomial multiply long (second part). + void pmull2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed long multiply-add. + void smlal(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed long multiply-add (second part). + void smlal2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned long multiply-add. + void umlal(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned long multiply-add (second part). + void umlal2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed long multiply-sub. + void smlsl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed long multiply-sub (second part). + void smlsl2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned long multiply-sub. + void umlsl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned long multiply-sub (second part). + void umlsl2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed long multiply. + void smull(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed long multiply (second part). + void smull2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed saturating doubling long multiply-add. + void sqdmlal(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed saturating doubling long multiply-add (second part). + void sqdmlal2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed saturating doubling long multiply-subtract. + void sqdmlsl(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed saturating doubling long multiply-subtract (second part). + void sqdmlsl2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed saturating doubling long multiply. + void sqdmull(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed saturating doubling long multiply (second part). + void sqdmull2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed saturating doubling multiply returning high half. + void sqdmulh(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed saturating rounding doubling multiply returning high half. + void sqrdmulh(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Signed saturating doubling multiply element returning high half. + void sqdmulh(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Signed saturating rounding doubling multiply element returning high half. + void sqrdmulh(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // Unsigned long multiply long. + void umull(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Unsigned long multiply (second part). + void umull2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Add narrow returning high half. + void addhn(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Add narrow returning high half (second part). + void addhn2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Rounding add narrow returning high half. + void raddhn(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Rounding add narrow returning high half (second part). + void raddhn2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Subtract narrow returning high half. + void subhn(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Subtract narrow returning high half (second part). + void subhn2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Rounding subtract narrow returning high half. + void rsubhn(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // Rounding subtract narrow returning high half (second part). + void rsubhn2(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // FP vector multiply accumulate. + void fmla(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // FP vector multiply subtract. + void fmls(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // FP vector multiply extended. + void fmulx(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // FP absolute greater than or equal. + void facge(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // FP absolute greater than. + void facgt(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // FP multiply by element. + void fmul(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // FP fused multiply-add to accumulator by element. + void fmla(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // FP fused multiply-sub from accumulator by element. + void fmls(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // FP multiply extended by element. + void fmulx(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index); + + // FP compare equal. + void fcmeq(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // FP greater than. + void fcmgt(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // FP greater than or equal. + void fcmge(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // FP compare equal to zero. + void fcmeq(const VRegister& vd, + const VRegister& vn, + double imm); + + // FP greater than zero. + void fcmgt(const VRegister& vd, + const VRegister& vn, + double imm); + + // FP greater than or equal to zero. + void fcmge(const VRegister& vd, + const VRegister& vn, + double imm); + + // FP less than or equal to zero. + void fcmle(const VRegister& vd, + const VRegister& vn, + double imm); + + // FP less than to zero. + void fcmlt(const VRegister& vd, + const VRegister& vn, + double imm); + + // FP absolute difference. + void fabd(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // FP pairwise add vector. + void faddp(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // FP pairwise add scalar. + void faddp(const VRegister& vd, + const VRegister& vn); + + // FP pairwise maximum vector. + void fmaxp(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // FP pairwise maximum scalar. + void fmaxp(const VRegister& vd, + const VRegister& vn); + + // FP pairwise minimum vector. + void fminp(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // FP pairwise minimum scalar. + void fminp(const VRegister& vd, + const VRegister& vn); + + // FP pairwise maximum number vector. + void fmaxnmp(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // FP pairwise maximum number scalar. + void fmaxnmp(const VRegister& vd, + const VRegister& vn); + + // FP pairwise minimum number vector. + void fminnmp(const VRegister& vd, + const VRegister& vn, + const VRegister& vm); + + // FP pairwise minimum number scalar. + void fminnmp(const VRegister& vd, + const VRegister& vn); + + // Emit generic instructions. + // Emit raw instructions into the instruction stream. + void dci(Instr raw_inst) { Emit(raw_inst); } + + // Emit 32 bits of data into the instruction stream. + void dc32(uint32_t data) { + VIXL_ASSERT(buffer_monitor_ > 0); + buffer_->Emit32(data); + } + + // Emit 64 bits of data into the instruction stream. + void dc64(uint64_t data) { + VIXL_ASSERT(buffer_monitor_ > 0); + buffer_->Emit64(data); + } + + // Copy a string into the instruction stream, including the terminating NULL + // character. The instruction pointer is then aligned correctly for + // subsequent instructions. + void EmitString(const char * string) { + VIXL_ASSERT(string != NULL); + VIXL_ASSERT(buffer_monitor_ > 0); + + buffer_->EmitString(string); + buffer_->Align(); + } + + // Code generation helpers. + + // Register encoding. + static Instr Rd(CPURegister rd) { + VIXL_ASSERT(rd.code() != kSPRegInternalCode); + return rd.code() << Rd_offset; + } + + static Instr Rn(CPURegister rn) { + VIXL_ASSERT(rn.code() != kSPRegInternalCode); + return rn.code() << Rn_offset; + } + + static Instr Rm(CPURegister rm) { + VIXL_ASSERT(rm.code() != kSPRegInternalCode); + return rm.code() << Rm_offset; + } + + static Instr RmNot31(CPURegister rm) { + VIXL_ASSERT(rm.code() != kSPRegInternalCode); + VIXL_ASSERT(!rm.IsZero()); + return Rm(rm); + } + + static Instr Ra(CPURegister ra) { + VIXL_ASSERT(ra.code() != kSPRegInternalCode); + return ra.code() << Ra_offset; + } + + static Instr Rt(CPURegister rt) { + VIXL_ASSERT(rt.code() != kSPRegInternalCode); + return rt.code() << Rt_offset; + } + + static Instr Rt2(CPURegister rt2) { + VIXL_ASSERT(rt2.code() != kSPRegInternalCode); + return rt2.code() << Rt2_offset; + } + + static Instr Rs(CPURegister rs) { + VIXL_ASSERT(rs.code() != kSPRegInternalCode); + return rs.code() << Rs_offset; + } + + // These encoding functions allow the stack pointer to be encoded, and + // disallow the zero register. + static Instr RdSP(Register rd) { + VIXL_ASSERT(!rd.IsZero()); + return (rd.code() & kRegCodeMask) << Rd_offset; + } + + static Instr RnSP(Register rn) { + VIXL_ASSERT(!rn.IsZero()); + return (rn.code() & kRegCodeMask) << Rn_offset; + } + + // Flags encoding. + static Instr Flags(FlagsUpdate S) { + if (S == SetFlags) { + return 1 << FlagsUpdate_offset; + } else if (S == LeaveFlags) { + return 0 << FlagsUpdate_offset; + } + VIXL_UNREACHABLE(); + return 0; + } + + static Instr Cond(Condition cond) { + return cond << Condition_offset; + } + + // PC-relative address encoding. + static Instr ImmPCRelAddress(int imm21) { + VIXL_ASSERT(is_int21(imm21)); + Instr imm = static_cast(truncate_to_int21(imm21)); + Instr immhi = (imm >> ImmPCRelLo_width) << ImmPCRelHi_offset; + Instr immlo = imm << ImmPCRelLo_offset; + return (immhi & ImmPCRelHi_mask) | (immlo & ImmPCRelLo_mask); + } + + // Branch encoding. + static Instr ImmUncondBranch(int imm26) { + VIXL_ASSERT(is_int26(imm26)); + return truncate_to_int26(imm26) << ImmUncondBranch_offset; + } + + static Instr ImmCondBranch(int imm19) { + VIXL_ASSERT(is_int19(imm19)); + return truncate_to_int19(imm19) << ImmCondBranch_offset; + } + + static Instr ImmCmpBranch(int imm19) { + VIXL_ASSERT(is_int19(imm19)); + return truncate_to_int19(imm19) << ImmCmpBranch_offset; + } + + static Instr ImmTestBranch(int imm14) { + VIXL_ASSERT(is_int14(imm14)); + return truncate_to_int14(imm14) << ImmTestBranch_offset; + } + + static Instr ImmTestBranchBit(unsigned bit_pos) { + VIXL_ASSERT(is_uint6(bit_pos)); + // Subtract five from the shift offset, as we need bit 5 from bit_pos. + unsigned b5 = bit_pos << (ImmTestBranchBit5_offset - 5); + unsigned b40 = bit_pos << ImmTestBranchBit40_offset; + b5 &= ImmTestBranchBit5_mask; + b40 &= ImmTestBranchBit40_mask; + return b5 | b40; + } + + // Data Processing encoding. + static Instr SF(Register rd) { + return rd.Is64Bits() ? SixtyFourBits : ThirtyTwoBits; + } + + static Instr ImmAddSub(int imm) { + VIXL_ASSERT(IsImmAddSub(imm)); + if (is_uint12(imm)) { // No shift required. + imm <<= ImmAddSub_offset; + } else { + imm = ((imm >> 12) << ImmAddSub_offset) | (1 << ShiftAddSub_offset); + } + return imm; + } + + static Instr ImmS(unsigned imms, unsigned reg_size) { + VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(imms)) || + ((reg_size == kWRegSize) && is_uint5(imms))); + USE(reg_size); + return imms << ImmS_offset; + } + + static Instr ImmR(unsigned immr, unsigned reg_size) { + VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || + ((reg_size == kWRegSize) && is_uint5(immr))); + USE(reg_size); + VIXL_ASSERT(is_uint6(immr)); + return immr << ImmR_offset; + } + + static Instr ImmSetBits(unsigned imms, unsigned reg_size) { + VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); + VIXL_ASSERT(is_uint6(imms)); + VIXL_ASSERT((reg_size == kXRegSize) || is_uint6(imms + 3)); + USE(reg_size); + return imms << ImmSetBits_offset; + } + + static Instr ImmRotate(unsigned immr, unsigned reg_size) { + VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); + VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || + ((reg_size == kWRegSize) && is_uint5(immr))); + USE(reg_size); + return immr << ImmRotate_offset; + } + + static Instr ImmLLiteral(int imm19) { + VIXL_ASSERT(is_int19(imm19)); + return truncate_to_int19(imm19) << ImmLLiteral_offset; + } + + static Instr BitN(unsigned bitn, unsigned reg_size) { + VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); + VIXL_ASSERT((reg_size == kXRegSize) || (bitn == 0)); + USE(reg_size); + return bitn << BitN_offset; + } + + static Instr ShiftDP(Shift shift) { + VIXL_ASSERT(shift == LSL || shift == LSR || shift == ASR || shift == ROR); + return shift << ShiftDP_offset; + } + + static Instr ImmDPShift(unsigned amount) { + VIXL_ASSERT(is_uint6(amount)); + return amount << ImmDPShift_offset; + } + + static Instr ExtendMode(Extend extend) { + return extend << ExtendMode_offset; + } + + static Instr ImmExtendShift(unsigned left_shift) { + VIXL_ASSERT(left_shift <= 4); + return left_shift << ImmExtendShift_offset; + } + + static Instr ImmCondCmp(unsigned imm) { + VIXL_ASSERT(is_uint5(imm)); + return imm << ImmCondCmp_offset; + } + + static Instr Nzcv(StatusFlags nzcv) { + return ((nzcv >> Flags_offset) & 0xf) << Nzcv_offset; + } + + // MemOperand offset encoding. + static Instr ImmLSUnsigned(int imm12) { + VIXL_ASSERT(is_uint12(imm12)); + return imm12 << ImmLSUnsigned_offset; + } + + static Instr ImmLS(int imm9) { + VIXL_ASSERT(is_int9(imm9)); + return truncate_to_int9(imm9) << ImmLS_offset; + } + + static Instr ImmLSPair(int imm7, unsigned access_size) { + VIXL_ASSERT(((imm7 >> access_size) << access_size) == imm7); + int scaled_imm7 = imm7 >> access_size; + VIXL_ASSERT(is_int7(scaled_imm7)); + return truncate_to_int7(scaled_imm7) << ImmLSPair_offset; + } + + static Instr ImmShiftLS(unsigned shift_amount) { + VIXL_ASSERT(is_uint1(shift_amount)); + return shift_amount << ImmShiftLS_offset; + } + + static Instr ImmPrefetchOperation(int imm5) { + VIXL_ASSERT(is_uint5(imm5)); + return imm5 << ImmPrefetchOperation_offset; + } + + static Instr ImmException(int imm16) { + VIXL_ASSERT(is_uint16(imm16)); + return imm16 << ImmException_offset; + } + + static Instr ImmSystemRegister(int imm15) { + VIXL_ASSERT(is_uint15(imm15)); + return imm15 << ImmSystemRegister_offset; + } + + static Instr ImmHint(int imm7) { + VIXL_ASSERT(is_uint7(imm7)); + return imm7 << ImmHint_offset; + } + + static Instr CRm(int imm4) { + VIXL_ASSERT(is_uint4(imm4)); + return imm4 << CRm_offset; + } + + static Instr CRn(int imm4) { + VIXL_ASSERT(is_uint4(imm4)); + return imm4 << CRn_offset; + } + + static Instr SysOp(int imm14) { + VIXL_ASSERT(is_uint14(imm14)); + return imm14 << SysOp_offset; + } + + static Instr ImmSysOp1(int imm3) { + VIXL_ASSERT(is_uint3(imm3)); + return imm3 << SysOp1_offset; + } + + static Instr ImmSysOp2(int imm3) { + VIXL_ASSERT(is_uint3(imm3)); + return imm3 << SysOp2_offset; + } + + static Instr ImmBarrierDomain(int imm2) { + VIXL_ASSERT(is_uint2(imm2)); + return imm2 << ImmBarrierDomain_offset; + } + + static Instr ImmBarrierType(int imm2) { + VIXL_ASSERT(is_uint2(imm2)); + return imm2 << ImmBarrierType_offset; + } + + // Move immediates encoding. + static Instr ImmMoveWide(uint64_t imm) { + VIXL_ASSERT(is_uint16(imm)); + return static_cast(imm << ImmMoveWide_offset); + } + + static Instr ShiftMoveWide(int64_t shift) { + VIXL_ASSERT(is_uint2(shift)); + return static_cast(shift << ShiftMoveWide_offset); + } + + // FP Immediates. + static Instr ImmFP32(float imm); + static Instr ImmFP64(double imm); + + // FP register type. + static Instr FPType(FPRegister fd) { + return fd.Is64Bits() ? FP64 : FP32; + } + + static Instr FPScale(unsigned scale) { + VIXL_ASSERT(is_uint6(scale)); + return scale << FPScale_offset; + } + + // Immediate field checking helpers. + static bool IsImmAddSub(int64_t immediate); + static bool IsImmConditionalCompare(int64_t immediate); + static bool IsImmFP32(float imm); + static bool IsImmFP64(double imm); + static bool IsImmLogical(uint64_t value, + unsigned width, + unsigned* n = NULL, + unsigned* imm_s = NULL, + unsigned* imm_r = NULL); + static bool IsImmLSPair(int64_t offset, unsigned access_size); + static bool IsImmLSScaled(int64_t offset, unsigned access_size); + static bool IsImmLSUnscaled(int64_t offset); + static bool IsImmMovn(uint64_t imm, unsigned reg_size); + static bool IsImmMovz(uint64_t imm, unsigned reg_size); + + // Instruction bits for vector format in data processing operations. + static Instr VFormat(VRegister vd) { + if (vd.Is64Bits()) { + switch (vd.lanes()) { + case 2: return NEON_2S; + case 4: return NEON_4H; + case 8: return NEON_8B; + default: return 0xffffffff; + } + } else { + VIXL_ASSERT(vd.Is128Bits()); + switch (vd.lanes()) { + case 2: return NEON_2D; + case 4: return NEON_4S; + case 8: return NEON_8H; + case 16: return NEON_16B; + default: return 0xffffffff; + } + } + } + + // Instruction bits for vector format in floating point data processing + // operations. + static Instr FPFormat(VRegister vd) { + if (vd.lanes() == 1) { + // Floating point scalar formats. + VIXL_ASSERT(vd.Is32Bits() || vd.Is64Bits()); + return vd.Is64Bits() ? FP64 : FP32; + } + + // Two lane floating point vector formats. + if (vd.lanes() == 2) { + VIXL_ASSERT(vd.Is64Bits() || vd.Is128Bits()); + return vd.Is128Bits() ? NEON_FP_2D : NEON_FP_2S; + } + + // Four lane floating point vector format. + VIXL_ASSERT((vd.lanes() == 4) && vd.Is128Bits()); + return NEON_FP_4S; + } + + // Instruction bits for vector format in load and store operations. + static Instr LSVFormat(VRegister vd) { + if (vd.Is64Bits()) { + switch (vd.lanes()) { + case 1: return LS_NEON_1D; + case 2: return LS_NEON_2S; + case 4: return LS_NEON_4H; + case 8: return LS_NEON_8B; + default: return 0xffffffff; + } + } else { + VIXL_ASSERT(vd.Is128Bits()); + switch (vd.lanes()) { + case 2: return LS_NEON_2D; + case 4: return LS_NEON_4S; + case 8: return LS_NEON_8H; + case 16: return LS_NEON_16B; + default: return 0xffffffff; + } + } + } + + // Instruction bits for scalar format in data processing operations. + static Instr SFormat(VRegister vd) { + VIXL_ASSERT(vd.lanes() == 1); + switch (vd.SizeInBytes()) { + case 1: return NEON_B; + case 2: return NEON_H; + case 4: return NEON_S; + case 8: return NEON_D; + default: return 0xffffffff; + } + } + + static Instr ImmNEONHLM(int index, int num_bits) { + int h, l, m; + if (num_bits == 3) { + VIXL_ASSERT(is_uint3(index)); + h = (index >> 2) & 1; + l = (index >> 1) & 1; + m = (index >> 0) & 1; + } else if (num_bits == 2) { + VIXL_ASSERT(is_uint2(index)); + h = (index >> 1) & 1; + l = (index >> 0) & 1; + m = 0; + } else { + VIXL_ASSERT(is_uint1(index) && (num_bits == 1)); + h = (index >> 0) & 1; + l = 0; + m = 0; + } + return (h << NEONH_offset) | (l << NEONL_offset) | (m << NEONM_offset); + } + + static Instr ImmNEONExt(int imm4) { + VIXL_ASSERT(is_uint4(imm4)); + return imm4 << ImmNEONExt_offset; + } + + static Instr ImmNEON5(Instr format, int index) { + VIXL_ASSERT(is_uint4(index)); + int s = LaneSizeInBytesLog2FromFormat(static_cast(format)); + int imm5 = (index << (s + 1)) | (1 << s); + return imm5 << ImmNEON5_offset; + } + + static Instr ImmNEON4(Instr format, int index) { + VIXL_ASSERT(is_uint4(index)); + int s = LaneSizeInBytesLog2FromFormat(static_cast(format)); + int imm4 = index << s; + return imm4 << ImmNEON4_offset; + } + + static Instr ImmNEONabcdefgh(int imm8) { + VIXL_ASSERT(is_uint8(imm8)); + Instr instr; + instr = ((imm8 >> 5) & 7) << ImmNEONabc_offset; + instr |= (imm8 & 0x1f) << ImmNEONdefgh_offset; + return instr; + } + + static Instr NEONCmode(int cmode) { + VIXL_ASSERT(is_uint4(cmode)); + return cmode << NEONCmode_offset; + } + + static Instr NEONModImmOp(int op) { + VIXL_ASSERT(is_uint1(op)); + return op << NEONModImmOp_offset; + } + + // Size of the code generated since label to the current position. + size_t SizeOfCodeGeneratedSince(Label* label) const { + VIXL_ASSERT(label->IsBound()); + return buffer_->OffsetFrom(label->location()); + } + + size_t SizeOfCodeGenerated() const { + return buffer_->CursorOffset(); + } + + size_t BufferCapacity() const { return buffer_->capacity(); } + + size_t RemainingBufferSpace() const { return buffer_->RemainingBytes(); } + + void EnsureSpaceFor(size_t amount) { + if (buffer_->RemainingBytes() < amount) { + size_t capacity = buffer_->capacity(); + size_t size = buffer_->CursorOffset(); + do { + // TODO(all): refine. + capacity *= 2; + } while ((capacity - size) < amount); + buffer_->Grow(capacity); + } + } + +#ifdef VIXL_DEBUG + void AcquireBuffer() { + VIXL_ASSERT(buffer_monitor_ >= 0); + buffer_monitor_++; + } + + void ReleaseBuffer() { + buffer_monitor_--; + VIXL_ASSERT(buffer_monitor_ >= 0); + } +#endif + + PositionIndependentCodeOption pic() const { + return pic_; + } + + bool AllowPageOffsetDependentCode() const { + return (pic() == PageOffsetDependentCode) || + (pic() == PositionDependentCode); + } + + static const Register& AppropriateZeroRegFor(const CPURegister& reg) { + return reg.Is64Bits() ? xzr : wzr; + } + + + protected: + void LoadStore(const CPURegister& rt, + const MemOperand& addr, + LoadStoreOp op, + LoadStoreScalingOption option = PreferScaledOffset); + + void LoadStorePair(const CPURegister& rt, + const CPURegister& rt2, + const MemOperand& addr, + LoadStorePairOp op); + void LoadStoreStruct(const VRegister& vt, + const MemOperand& addr, + NEONLoadStoreMultiStructOp op); + void LoadStoreStruct1(const VRegister& vt, + int reg_count, + const MemOperand& addr); + void LoadStoreStructSingle(const VRegister& vt, + uint32_t lane, + const MemOperand& addr, + NEONLoadStoreSingleStructOp op); + void LoadStoreStructSingleAllLanes(const VRegister& vt, + const MemOperand& addr, + NEONLoadStoreSingleStructOp op); + void LoadStoreStructVerify(const VRegister& vt, + const MemOperand& addr, + Instr op); + + void Prefetch(PrefetchOperation op, + const MemOperand& addr, + LoadStoreScalingOption option = PreferScaledOffset); + + // TODO(all): The third parameter should be passed by reference but gcc 4.8.2 + // reports a bogus uninitialised warning then. + void Logical(const Register& rd, + const Register& rn, + const Operand operand, + LogicalOp op); + void LogicalImmediate(const Register& rd, + const Register& rn, + unsigned n, + unsigned imm_s, + unsigned imm_r, + LogicalOp op); + + void ConditionalCompare(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond, + ConditionalCompareOp op); + + void AddSubWithCarry(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + AddSubWithCarryOp op); + + + // Functions for emulating operands not directly supported by the instruction + // set. + void EmitShift(const Register& rd, + const Register& rn, + Shift shift, + unsigned amount); + void EmitExtendShift(const Register& rd, + const Register& rn, + Extend extend, + unsigned left_shift); + + void AddSub(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + AddSubOp op); + + void NEONTable(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + NEONTableOp op); + + // Find an appropriate LoadStoreOp or LoadStorePairOp for the specified + // registers. Only simple loads are supported; sign- and zero-extension (such + // as in LDPSW_x or LDRB_w) are not supported. + static LoadStoreOp LoadOpFor(const CPURegister& rt); + static LoadStorePairOp LoadPairOpFor(const CPURegister& rt, + const CPURegister& rt2); + static LoadStoreOp StoreOpFor(const CPURegister& rt); + static LoadStorePairOp StorePairOpFor(const CPURegister& rt, + const CPURegister& rt2); + static LoadStorePairNonTemporalOp LoadPairNonTemporalOpFor( + const CPURegister& rt, const CPURegister& rt2); + static LoadStorePairNonTemporalOp StorePairNonTemporalOpFor( + const CPURegister& rt, const CPURegister& rt2); + static LoadLiteralOp LoadLiteralOpFor(const CPURegister& rt); + + + private: + static uint32_t FP32ToImm8(float imm); + static uint32_t FP64ToImm8(double imm); + + // Instruction helpers. + void MoveWide(const Register& rd, + uint64_t imm, + int shift, + MoveWideImmediateOp mov_op); + void DataProcShiftedRegister(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + Instr op); + void DataProcExtendedRegister(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + Instr op); + void LoadStorePairNonTemporal(const CPURegister& rt, + const CPURegister& rt2, + const MemOperand& addr, + LoadStorePairNonTemporalOp op); + void LoadLiteral(const CPURegister& rt, uint64_t imm, LoadLiteralOp op); + void ConditionalSelect(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond, + ConditionalSelectOp op); + void DataProcessing1Source(const Register& rd, + const Register& rn, + DataProcessing1SourceOp op); + void DataProcessing3Source(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra, + DataProcessing3SourceOp op); + void FPDataProcessing1Source(const VRegister& fd, + const VRegister& fn, + FPDataProcessing1SourceOp op); + void FPDataProcessing3Source(const VRegister& fd, + const VRegister& fn, + const VRegister& fm, + const VRegister& fa, + FPDataProcessing3SourceOp op); + void NEONAcrossLanesL(const VRegister& vd, + const VRegister& vn, + NEONAcrossLanesOp op); + void NEONAcrossLanes(const VRegister& vd, + const VRegister& vn, + NEONAcrossLanesOp op); + void NEONModifiedImmShiftLsl(const VRegister& vd, + const int imm8, + const int left_shift, + NEONModifiedImmediateOp op); + void NEONModifiedImmShiftMsl(const VRegister& vd, + const int imm8, + const int shift_amount, + NEONModifiedImmediateOp op); + void NEONFP2Same(const VRegister& vd, + const VRegister& vn, + Instr vop); + void NEON3Same(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + NEON3SameOp vop); + void NEONFP3Same(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + Instr op); + void NEON3DifferentL(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + NEON3DifferentOp vop); + void NEON3DifferentW(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + NEON3DifferentOp vop); + void NEON3DifferentHN(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + NEON3DifferentOp vop); + void NEONFP2RegMisc(const VRegister& vd, + const VRegister& vn, + NEON2RegMiscOp vop, + double value = 0.0); + void NEON2RegMisc(const VRegister& vd, + const VRegister& vn, + NEON2RegMiscOp vop, + int value = 0); + void NEONFP2RegMisc(const VRegister& vd, + const VRegister& vn, + Instr op); + void NEONAddlp(const VRegister& vd, + const VRegister& vn, + NEON2RegMiscOp op); + void NEONPerm(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + NEONPermOp op); + void NEONFPByElement(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index, + NEONByIndexedElementOp op); + void NEONByElement(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index, + NEONByIndexedElementOp op); + void NEONByElementL(const VRegister& vd, + const VRegister& vn, + const VRegister& vm, + int vm_index, + NEONByIndexedElementOp op); + void NEONShiftImmediate(const VRegister& vd, + const VRegister& vn, + NEONShiftImmediateOp op, + int immh_immb); + void NEONShiftLeftImmediate(const VRegister& vd, + const VRegister& vn, + int shift, + NEONShiftImmediateOp op); + void NEONShiftRightImmediate(const VRegister& vd, + const VRegister& vn, + int shift, + NEONShiftImmediateOp op); + void NEONShiftImmediateL(const VRegister& vd, + const VRegister& vn, + int shift, + NEONShiftImmediateOp op); + void NEONShiftImmediateN(const VRegister& vd, + const VRegister& vn, + int shift, + NEONShiftImmediateOp op); + void NEONXtn(const VRegister& vd, + const VRegister& vn, + NEON2RegMiscOp vop); + + Instr LoadStoreStructAddrModeField(const MemOperand& addr); + + // Encode the specified MemOperand for the specified access size and scaling + // preference. + Instr LoadStoreMemOperand(const MemOperand& addr, + unsigned access_size, + LoadStoreScalingOption option); + + // Link the current (not-yet-emitted) instruction to the specified label, then + // return an offset to be encoded in the instruction. If the label is not yet + // bound, an offset of 0 is returned. + ptrdiff_t LinkAndGetByteOffsetTo(Label * label); + ptrdiff_t LinkAndGetInstructionOffsetTo(Label * label); + ptrdiff_t LinkAndGetPageOffsetTo(Label * label); + + // A common implementation for the LinkAndGetOffsetTo helpers. + template + ptrdiff_t LinkAndGetOffsetTo(Label* label); + + // Literal load offset are in words (32-bit). + ptrdiff_t LinkAndGetWordOffsetTo(RawLiteral* literal); + + // Emit the instruction in buffer_. + void Emit(Instr instruction) { + VIXL_STATIC_ASSERT(sizeof(instruction) == kInstructionSize); + VIXL_ASSERT(buffer_monitor_ > 0); + buffer_->Emit32(instruction); + } + + // Buffer where the code is emitted. + CodeBuffer* buffer_; + PositionIndependentCodeOption pic_; + +#ifdef VIXL_DEBUG + int64_t buffer_monitor_; +#endif +}; + + +// All Assembler emits MUST acquire/release the underlying code buffer. The +// helper scope below will do so and optionally ensure the buffer is big enough +// to receive the emit. It is possible to request the scope not to perform any +// checks (kNoCheck) if for example it is known in advance the buffer size is +// adequate or there is some other size checking mechanism in place. +class CodeBufferCheckScope { + public: + // Tell whether or not the scope needs to ensure the associated CodeBuffer + // has enough space for the requested size. + enum CheckPolicy { + kNoCheck, + kCheck + }; + + // Tell whether or not the scope should assert the amount of code emitted + // within the scope is consistent with the requested amount. + enum AssertPolicy { + kNoAssert, // No assert required. + kExactSize, // The code emitted must be exactly size bytes. + kMaximumSize // The code emitted must be at most size bytes. + }; + + CodeBufferCheckScope(Assembler* assm, + size_t size, + CheckPolicy check_policy = kCheck, + AssertPolicy assert_policy = kMaximumSize) + : assm_(assm) { + if (check_policy == kCheck) assm->EnsureSpaceFor(size); +#ifdef VIXL_DEBUG + assm->bind(&start_); + size_ = size; + assert_policy_ = assert_policy; + assm->AcquireBuffer(); +#else + USE(assert_policy); +#endif + } + + // This is a shortcut for CodeBufferCheckScope(assm, 0, kNoCheck, kNoAssert). + explicit CodeBufferCheckScope(Assembler* assm) : assm_(assm) { +#ifdef VIXL_DEBUG + size_ = 0; + assert_policy_ = kNoAssert; + assm->AcquireBuffer(); +#endif + } + + ~CodeBufferCheckScope() { +#ifdef VIXL_DEBUG + assm_->ReleaseBuffer(); + switch (assert_policy_) { + case kNoAssert: break; + case kExactSize: + VIXL_ASSERT(assm_->SizeOfCodeGeneratedSince(&start_) == size_); + break; + case kMaximumSize: + VIXL_ASSERT(assm_->SizeOfCodeGeneratedSince(&start_) <= size_); + break; + default: + VIXL_UNREACHABLE(); + } +#endif + } + + protected: + Assembler* assm_; +#ifdef VIXL_DEBUG + Label start_; + size_t size_; + AssertPolicy assert_policy_; +#endif +}; + + +template +void Literal::UpdateValue(T new_value, const Assembler* assembler) { + return UpdateValue(new_value, assembler->GetStartAddress()); +} + + +template +void Literal::UpdateValue(T high64, T low64, const Assembler* assembler) { + return UpdateValue(high64, low64, assembler->GetStartAddress()); +} + + +} // namespace vixl + +#endif // VIXL_A64_ASSEMBLER_A64_H_ diff --git a/qemu/disas/libvixl/vixl/a64/constants-a64.h b/qemu/disas/libvixl/vixl/a64/constants-a64.h new file mode 100644 index 000000000..2caa73af8 --- /dev/null +++ b/qemu/disas/libvixl/vixl/a64/constants-a64.h @@ -0,0 +1,2116 @@ +// Copyright 2015, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_A64_CONSTANTS_A64_H_ +#define VIXL_A64_CONSTANTS_A64_H_ + +namespace vixl { + +const unsigned kNumberOfRegisters = 32; +const unsigned kNumberOfVRegisters = 32; +const unsigned kNumberOfFPRegisters = kNumberOfVRegisters; +// Callee saved registers are x21-x30(lr). +const int kNumberOfCalleeSavedRegisters = 10; +const int kFirstCalleeSavedRegisterIndex = 21; +// Callee saved FP registers are d8-d15. +const int kNumberOfCalleeSavedFPRegisters = 8; +const int kFirstCalleeSavedFPRegisterIndex = 8; + +#define REGISTER_CODE_LIST(R) \ +R(0) R(1) R(2) R(3) R(4) R(5) R(6) R(7) \ +R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15) \ +R(16) R(17) R(18) R(19) R(20) R(21) R(22) R(23) \ +R(24) R(25) R(26) R(27) R(28) R(29) R(30) R(31) + +#define INSTRUCTION_FIELDS_LIST(V_) \ +/* Register fields */ \ +V_(Rd, 4, 0, Bits) /* Destination register. */ \ +V_(Rn, 9, 5, Bits) /* First source register. */ \ +V_(Rm, 20, 16, Bits) /* Second source register. */ \ +V_(Ra, 14, 10, Bits) /* Third source register. */ \ +V_(Rt, 4, 0, Bits) /* Load/store register. */ \ +V_(Rt2, 14, 10, Bits) /* Load/store second register. */ \ +V_(Rs, 20, 16, Bits) /* Exclusive access status. */ \ + \ +/* Common bits */ \ +V_(SixtyFourBits, 31, 31, Bits) \ +V_(FlagsUpdate, 29, 29, Bits) \ + \ +/* PC relative addressing */ \ +V_(ImmPCRelHi, 23, 5, SignedBits) \ +V_(ImmPCRelLo, 30, 29, Bits) \ + \ +/* Add/subtract/logical shift register */ \ +V_(ShiftDP, 23, 22, Bits) \ +V_(ImmDPShift, 15, 10, Bits) \ + \ +/* Add/subtract immediate */ \ +V_(ImmAddSub, 21, 10, Bits) \ +V_(ShiftAddSub, 23, 22, Bits) \ + \ +/* Add/substract extend */ \ +V_(ImmExtendShift, 12, 10, Bits) \ +V_(ExtendMode, 15, 13, Bits) \ + \ +/* Move wide */ \ +V_(ImmMoveWide, 20, 5, Bits) \ +V_(ShiftMoveWide, 22, 21, Bits) \ + \ +/* Logical immediate, bitfield and extract */ \ +V_(BitN, 22, 22, Bits) \ +V_(ImmRotate, 21, 16, Bits) \ +V_(ImmSetBits, 15, 10, Bits) \ +V_(ImmR, 21, 16, Bits) \ +V_(ImmS, 15, 10, Bits) \ + \ +/* Test and branch immediate */ \ +V_(ImmTestBranch, 18, 5, SignedBits) \ +V_(ImmTestBranchBit40, 23, 19, Bits) \ +V_(ImmTestBranchBit5, 31, 31, Bits) \ + \ +/* Conditionals */ \ +V_(Condition, 15, 12, Bits) \ +V_(ConditionBranch, 3, 0, Bits) \ +V_(Nzcv, 3, 0, Bits) \ +V_(ImmCondCmp, 20, 16, Bits) \ +V_(ImmCondBranch, 23, 5, SignedBits) \ + \ +/* Floating point */ \ +V_(FPType, 23, 22, Bits) \ +V_(ImmFP, 20, 13, Bits) \ +V_(FPScale, 15, 10, Bits) \ + \ +/* Load Store */ \ +V_(ImmLS, 20, 12, SignedBits) \ +V_(ImmLSUnsigned, 21, 10, Bits) \ +V_(ImmLSPair, 21, 15, SignedBits) \ +V_(ImmShiftLS, 12, 12, Bits) \ +V_(LSOpc, 23, 22, Bits) \ +V_(LSVector, 26, 26, Bits) \ +V_(LSSize, 31, 30, Bits) \ +V_(ImmPrefetchOperation, 4, 0, Bits) \ +V_(PrefetchHint, 4, 3, Bits) \ +V_(PrefetchTarget, 2, 1, Bits) \ +V_(PrefetchStream, 0, 0, Bits) \ + \ +/* Other immediates */ \ +V_(ImmUncondBranch, 25, 0, SignedBits) \ +V_(ImmCmpBranch, 23, 5, SignedBits) \ +V_(ImmLLiteral, 23, 5, SignedBits) \ +V_(ImmException, 20, 5, Bits) \ +V_(ImmHint, 11, 5, Bits) \ +V_(ImmBarrierDomain, 11, 10, Bits) \ +V_(ImmBarrierType, 9, 8, Bits) \ + \ +/* System (MRS, MSR, SYS) */ \ +V_(ImmSystemRegister, 19, 5, Bits) \ +V_(SysO0, 19, 19, Bits) \ +V_(SysOp, 18, 5, Bits) \ +V_(SysOp1, 18, 16, Bits) \ +V_(SysOp2, 7, 5, Bits) \ +V_(CRn, 15, 12, Bits) \ +V_(CRm, 11, 8, Bits) \ + \ +/* Load-/store-exclusive */ \ +V_(LdStXLoad, 22, 22, Bits) \ +V_(LdStXNotExclusive, 23, 23, Bits) \ +V_(LdStXAcquireRelease, 15, 15, Bits) \ +V_(LdStXSizeLog2, 31, 30, Bits) \ +V_(LdStXPair, 21, 21, Bits) \ + \ +/* NEON generic fields */ \ +V_(NEONQ, 30, 30, Bits) \ +V_(NEONSize, 23, 22, Bits) \ +V_(NEONLSSize, 11, 10, Bits) \ +V_(NEONS, 12, 12, Bits) \ +V_(NEONL, 21, 21, Bits) \ +V_(NEONM, 20, 20, Bits) \ +V_(NEONH, 11, 11, Bits) \ +V_(ImmNEONExt, 14, 11, Bits) \ +V_(ImmNEON5, 20, 16, Bits) \ +V_(ImmNEON4, 14, 11, Bits) \ + \ +/* NEON Modified Immediate fields */ \ +V_(ImmNEONabc, 18, 16, Bits) \ +V_(ImmNEONdefgh, 9, 5, Bits) \ +V_(NEONModImmOp, 29, 29, Bits) \ +V_(NEONCmode, 15, 12, Bits) \ + \ +/* NEON Shift Immediate fields */ \ +V_(ImmNEONImmhImmb, 22, 16, Bits) \ +V_(ImmNEONImmh, 22, 19, Bits) \ +V_(ImmNEONImmb, 18, 16, Bits) + +#define SYSTEM_REGISTER_FIELDS_LIST(V_, M_) \ +/* NZCV */ \ +V_(Flags, 31, 28, Bits) \ +V_(N, 31, 31, Bits) \ +V_(Z, 30, 30, Bits) \ +V_(C, 29, 29, Bits) \ +V_(V, 28, 28, Bits) \ +M_(NZCV, Flags_mask) \ +/* FPCR */ \ +V_(AHP, 26, 26, Bits) \ +V_(DN, 25, 25, Bits) \ +V_(FZ, 24, 24, Bits) \ +V_(RMode, 23, 22, Bits) \ +M_(FPCR, AHP_mask | DN_mask | FZ_mask | RMode_mask) + +// Fields offsets. +#define DECLARE_FIELDS_OFFSETS(Name, HighBit, LowBit, X) \ +const int Name##_offset = LowBit; \ +const int Name##_width = HighBit - LowBit + 1; \ +const uint32_t Name##_mask = ((1 << Name##_width) - 1) << LowBit; +#define NOTHING(A, B) +INSTRUCTION_FIELDS_LIST(DECLARE_FIELDS_OFFSETS) +SYSTEM_REGISTER_FIELDS_LIST(DECLARE_FIELDS_OFFSETS, NOTHING) +#undef NOTHING +#undef DECLARE_FIELDS_BITS + +// ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST), formed +// from ImmPCRelLo and ImmPCRelHi. +const int ImmPCRel_mask = ImmPCRelLo_mask | ImmPCRelHi_mask; + +// Condition codes. +enum Condition { + eq = 0, // Z set Equal. + ne = 1, // Z clear Not equal. + cs = 2, // C set Carry set. + cc = 3, // C clear Carry clear. + mi = 4, // N set Negative. + pl = 5, // N clear Positive or zero. + vs = 6, // V set Overflow. + vc = 7, // V clear No overflow. + hi = 8, // C set, Z clear Unsigned higher. + ls = 9, // C clear or Z set Unsigned lower or same. + ge = 10, // N == V Greater or equal. + lt = 11, // N != V Less than. + gt = 12, // Z clear, N == V Greater than. + le = 13, // Z set or N != V Less then or equal + al = 14, // Always. + nv = 15, // Behaves as always/al. + + // Aliases. + hs = cs, // C set Unsigned higher or same. + lo = cc // C clear Unsigned lower. +}; + +inline Condition InvertCondition(Condition cond) { + // Conditions al and nv behave identically, as "always true". They can't be + // inverted, because there is no "always false" condition. + VIXL_ASSERT((cond != al) && (cond != nv)); + return static_cast(cond ^ 1); +} + +enum FPTrapFlags { + EnableTrap = 1, + DisableTrap = 0 +}; + +enum FlagsUpdate { + SetFlags = 1, + LeaveFlags = 0 +}; + +enum StatusFlags { + NoFlag = 0, + + // Derive the flag combinations from the system register bit descriptions. + NFlag = N_mask, + ZFlag = Z_mask, + CFlag = C_mask, + VFlag = V_mask, + NZFlag = NFlag | ZFlag, + NCFlag = NFlag | CFlag, + NVFlag = NFlag | VFlag, + ZCFlag = ZFlag | CFlag, + ZVFlag = ZFlag | VFlag, + CVFlag = CFlag | VFlag, + NZCFlag = NFlag | ZFlag | CFlag, + NZVFlag = NFlag | ZFlag | VFlag, + NCVFlag = NFlag | CFlag | VFlag, + ZCVFlag = ZFlag | CFlag | VFlag, + NZCVFlag = NFlag | ZFlag | CFlag | VFlag, + + // Floating-point comparison results. + FPEqualFlag = ZCFlag, + FPLessThanFlag = NFlag, + FPGreaterThanFlag = CFlag, + FPUnorderedFlag = CVFlag +}; + +enum Shift { + NO_SHIFT = -1, + LSL = 0x0, + LSR = 0x1, + ASR = 0x2, + ROR = 0x3, + MSL = 0x4 +}; + +enum Extend { + NO_EXTEND = -1, + UXTB = 0, + UXTH = 1, + UXTW = 2, + UXTX = 3, + SXTB = 4, + SXTH = 5, + SXTW = 6, + SXTX = 7 +}; + +enum SystemHint { + NOP = 0, + YIELD = 1, + WFE = 2, + WFI = 3, + SEV = 4, + SEVL = 5 +}; + +enum BarrierDomain { + OuterShareable = 0, + NonShareable = 1, + InnerShareable = 2, + FullSystem = 3 +}; + +enum BarrierType { + BarrierOther = 0, + BarrierReads = 1, + BarrierWrites = 2, + BarrierAll = 3 +}; + +enum PrefetchOperation { + PLDL1KEEP = 0x00, + PLDL1STRM = 0x01, + PLDL2KEEP = 0x02, + PLDL2STRM = 0x03, + PLDL3KEEP = 0x04, + PLDL3STRM = 0x05, + + PLIL1KEEP = 0x08, + PLIL1STRM = 0x09, + PLIL2KEEP = 0x0a, + PLIL2STRM = 0x0b, + PLIL3KEEP = 0x0c, + PLIL3STRM = 0x0d, + + PSTL1KEEP = 0x10, + PSTL1STRM = 0x11, + PSTL2KEEP = 0x12, + PSTL2STRM = 0x13, + PSTL3KEEP = 0x14, + PSTL3STRM = 0x15 +}; + +// System/special register names. +// This information is not encoded as one field but as the concatenation of +// multiple fields (Op0<0>, Op1, Crn, Crm, Op2). +enum SystemRegister { + NZCV = ((0x1 << SysO0_offset) | + (0x3 << SysOp1_offset) | + (0x4 << CRn_offset) | + (0x2 << CRm_offset) | + (0x0 << SysOp2_offset)) >> ImmSystemRegister_offset, + FPCR = ((0x1 << SysO0_offset) | + (0x3 << SysOp1_offset) | + (0x4 << CRn_offset) | + (0x4 << CRm_offset) | + (0x0 << SysOp2_offset)) >> ImmSystemRegister_offset +}; + +enum InstructionCacheOp { + IVAU = ((0x3 << SysOp1_offset) | + (0x7 << CRn_offset) | + (0x5 << CRm_offset) | + (0x1 << SysOp2_offset)) >> SysOp_offset +}; + +enum DataCacheOp { + CVAC = ((0x3 << SysOp1_offset) | + (0x7 << CRn_offset) | + (0xa << CRm_offset) | + (0x1 << SysOp2_offset)) >> SysOp_offset, + CVAU = ((0x3 << SysOp1_offset) | + (0x7 << CRn_offset) | + (0xb << CRm_offset) | + (0x1 << SysOp2_offset)) >> SysOp_offset, + CIVAC = ((0x3 << SysOp1_offset) | + (0x7 << CRn_offset) | + (0xe << CRm_offset) | + (0x1 << SysOp2_offset)) >> SysOp_offset, + ZVA = ((0x3 << SysOp1_offset) | + (0x7 << CRn_offset) | + (0x4 << CRm_offset) | + (0x1 << SysOp2_offset)) >> SysOp_offset +}; + +// Instruction enumerations. +// +// These are the masks that define a class of instructions, and the list of +// instructions within each class. Each enumeration has a Fixed, FMask and +// Mask value. +// +// Fixed: The fixed bits in this instruction class. +// FMask: The mask used to extract the fixed bits in the class. +// Mask: The mask used to identify the instructions within a class. +// +// The enumerations can be used like this: +// +// VIXL_ASSERT(instr->Mask(PCRelAddressingFMask) == PCRelAddressingFixed); +// switch(instr->Mask(PCRelAddressingMask)) { +// case ADR: Format("adr 'Xd, 'AddrPCRelByte"); break; +// case ADRP: Format("adrp 'Xd, 'AddrPCRelPage"); break; +// default: printf("Unknown instruction\n"); +// } + + +// Generic fields. +enum GenericInstrField { + SixtyFourBits = 0x80000000, + ThirtyTwoBits = 0x00000000, + FP32 = 0x00000000, + FP64 = 0x00400000 +}; + +enum NEONFormatField { + NEONFormatFieldMask = 0x40C00000, + NEON_Q = 0x40000000, + NEON_8B = 0x00000000, + NEON_16B = NEON_8B | NEON_Q, + NEON_4H = 0x00400000, + NEON_8H = NEON_4H | NEON_Q, + NEON_2S = 0x00800000, + NEON_4S = NEON_2S | NEON_Q, + NEON_1D = 0x00C00000, + NEON_2D = 0x00C00000 | NEON_Q +}; + +enum NEONFPFormatField { + NEONFPFormatFieldMask = 0x40400000, + NEON_FP_2S = FP32, + NEON_FP_4S = FP32 | NEON_Q, + NEON_FP_2D = FP64 | NEON_Q +}; + +enum NEONLSFormatField { + NEONLSFormatFieldMask = 0x40000C00, + LS_NEON_8B = 0x00000000, + LS_NEON_16B = LS_NEON_8B | NEON_Q, + LS_NEON_4H = 0x00000400, + LS_NEON_8H = LS_NEON_4H | NEON_Q, + LS_NEON_2S = 0x00000800, + LS_NEON_4S = LS_NEON_2S | NEON_Q, + LS_NEON_1D = 0x00000C00, + LS_NEON_2D = LS_NEON_1D | NEON_Q +}; + +enum NEONScalarFormatField { + NEONScalarFormatFieldMask = 0x00C00000, + NEONScalar = 0x10000000, + NEON_B = 0x00000000, + NEON_H = 0x00400000, + NEON_S = 0x00800000, + NEON_D = 0x00C00000 +}; + +// PC relative addressing. +enum PCRelAddressingOp { + PCRelAddressingFixed = 0x10000000, + PCRelAddressingFMask = 0x1F000000, + PCRelAddressingMask = 0x9F000000, + ADR = PCRelAddressingFixed | 0x00000000, + ADRP = PCRelAddressingFixed | 0x80000000 +}; + +// Add/sub (immediate, shifted and extended.) +const int kSFOffset = 31; +enum AddSubOp { + AddSubOpMask = 0x60000000, + AddSubSetFlagsBit = 0x20000000, + ADD = 0x00000000, + ADDS = ADD | AddSubSetFlagsBit, + SUB = 0x40000000, + SUBS = SUB | AddSubSetFlagsBit +}; + +#define ADD_SUB_OP_LIST(V) \ + V(ADD), \ + V(ADDS), \ + V(SUB), \ + V(SUBS) + +enum AddSubImmediateOp { + AddSubImmediateFixed = 0x11000000, + AddSubImmediateFMask = 0x1F000000, + AddSubImmediateMask = 0xFF000000, + #define ADD_SUB_IMMEDIATE(A) \ + A##_w_imm = AddSubImmediateFixed | A, \ + A##_x_imm = AddSubImmediateFixed | A | SixtyFourBits + ADD_SUB_OP_LIST(ADD_SUB_IMMEDIATE) + #undef ADD_SUB_IMMEDIATE +}; + +enum AddSubShiftedOp { + AddSubShiftedFixed = 0x0B000000, + AddSubShiftedFMask = 0x1F200000, + AddSubShiftedMask = 0xFF200000, + #define ADD_SUB_SHIFTED(A) \ + A##_w_shift = AddSubShiftedFixed | A, \ + A##_x_shift = AddSubShiftedFixed | A | SixtyFourBits + ADD_SUB_OP_LIST(ADD_SUB_SHIFTED) + #undef ADD_SUB_SHIFTED +}; + +enum AddSubExtendedOp { + AddSubExtendedFixed = 0x0B200000, + AddSubExtendedFMask = 0x1F200000, + AddSubExtendedMask = 0xFFE00000, + #define ADD_SUB_EXTENDED(A) \ + A##_w_ext = AddSubExtendedFixed | A, \ + A##_x_ext = AddSubExtendedFixed | A | SixtyFourBits + ADD_SUB_OP_LIST(ADD_SUB_EXTENDED) + #undef ADD_SUB_EXTENDED +}; + +// Add/sub with carry. +enum AddSubWithCarryOp { + AddSubWithCarryFixed = 0x1A000000, + AddSubWithCarryFMask = 0x1FE00000, + AddSubWithCarryMask = 0xFFE0FC00, + ADC_w = AddSubWithCarryFixed | ADD, + ADC_x = AddSubWithCarryFixed | ADD | SixtyFourBits, + ADC = ADC_w, + ADCS_w = AddSubWithCarryFixed | ADDS, + ADCS_x = AddSubWithCarryFixed | ADDS | SixtyFourBits, + SBC_w = AddSubWithCarryFixed | SUB, + SBC_x = AddSubWithCarryFixed | SUB | SixtyFourBits, + SBC = SBC_w, + SBCS_w = AddSubWithCarryFixed | SUBS, + SBCS_x = AddSubWithCarryFixed | SUBS | SixtyFourBits +}; + + +// Logical (immediate and shifted register). +enum LogicalOp { + LogicalOpMask = 0x60200000, + NOT = 0x00200000, + AND = 0x00000000, + BIC = AND | NOT, + ORR = 0x20000000, + ORN = ORR | NOT, + EOR = 0x40000000, + EON = EOR | NOT, + ANDS = 0x60000000, + BICS = ANDS | NOT +}; + +// Logical immediate. +enum LogicalImmediateOp { + LogicalImmediateFixed = 0x12000000, + LogicalImmediateFMask = 0x1F800000, + LogicalImmediateMask = 0xFF800000, + AND_w_imm = LogicalImmediateFixed | AND, + AND_x_imm = LogicalImmediateFixed | AND | SixtyFourBits, + ORR_w_imm = LogicalImmediateFixed | ORR, + ORR_x_imm = LogicalImmediateFixed | ORR | SixtyFourBits, + EOR_w_imm = LogicalImmediateFixed | EOR, + EOR_x_imm = LogicalImmediateFixed | EOR | SixtyFourBits, + ANDS_w_imm = LogicalImmediateFixed | ANDS, + ANDS_x_imm = LogicalImmediateFixed | ANDS | SixtyFourBits +}; + +// Logical shifted register. +enum LogicalShiftedOp { + LogicalShiftedFixed = 0x0A000000, + LogicalShiftedFMask = 0x1F000000, + LogicalShiftedMask = 0xFF200000, + AND_w = LogicalShiftedFixed | AND, + AND_x = LogicalShiftedFixed | AND | SixtyFourBits, + AND_shift = AND_w, + BIC_w = LogicalShiftedFixed | BIC, + BIC_x = LogicalShiftedFixed | BIC | SixtyFourBits, + BIC_shift = BIC_w, + ORR_w = LogicalShiftedFixed | ORR, + ORR_x = LogicalShiftedFixed | ORR | SixtyFourBits, + ORR_shift = ORR_w, + ORN_w = LogicalShiftedFixed | ORN, + ORN_x = LogicalShiftedFixed | ORN | SixtyFourBits, + ORN_shift = ORN_w, + EOR_w = LogicalShiftedFixed | EOR, + EOR_x = LogicalShiftedFixed | EOR | SixtyFourBits, + EOR_shift = EOR_w, + EON_w = LogicalShiftedFixed | EON, + EON_x = LogicalShiftedFixed | EON | SixtyFourBits, + EON_shift = EON_w, + ANDS_w = LogicalShiftedFixed | ANDS, + ANDS_x = LogicalShiftedFixed | ANDS | SixtyFourBits, + ANDS_shift = ANDS_w, + BICS_w = LogicalShiftedFixed | BICS, + BICS_x = LogicalShiftedFixed | BICS | SixtyFourBits, + BICS_shift = BICS_w +}; + +// Move wide immediate. +enum MoveWideImmediateOp { + MoveWideImmediateFixed = 0x12800000, + MoveWideImmediateFMask = 0x1F800000, + MoveWideImmediateMask = 0xFF800000, + MOVN = 0x00000000, + MOVZ = 0x40000000, + MOVK = 0x60000000, + MOVN_w = MoveWideImmediateFixed | MOVN, + MOVN_x = MoveWideImmediateFixed | MOVN | SixtyFourBits, + MOVZ_w = MoveWideImmediateFixed | MOVZ, + MOVZ_x = MoveWideImmediateFixed | MOVZ | SixtyFourBits, + MOVK_w = MoveWideImmediateFixed | MOVK, + MOVK_x = MoveWideImmediateFixed | MOVK | SixtyFourBits +}; + +// Bitfield. +const int kBitfieldNOffset = 22; +enum BitfieldOp { + BitfieldFixed = 0x13000000, + BitfieldFMask = 0x1F800000, + BitfieldMask = 0xFF800000, + SBFM_w = BitfieldFixed | 0x00000000, + SBFM_x = BitfieldFixed | 0x80000000, + SBFM = SBFM_w, + BFM_w = BitfieldFixed | 0x20000000, + BFM_x = BitfieldFixed | 0xA0000000, + BFM = BFM_w, + UBFM_w = BitfieldFixed | 0x40000000, + UBFM_x = BitfieldFixed | 0xC0000000, + UBFM = UBFM_w + // Bitfield N field. +}; + +// Extract. +enum ExtractOp { + ExtractFixed = 0x13800000, + ExtractFMask = 0x1F800000, + ExtractMask = 0xFFA00000, + EXTR_w = ExtractFixed | 0x00000000, + EXTR_x = ExtractFixed | 0x80000000, + EXTR = EXTR_w +}; + +// Unconditional branch. +enum UnconditionalBranchOp { + UnconditionalBranchFixed = 0x14000000, + UnconditionalBranchFMask = 0x7C000000, + UnconditionalBranchMask = 0xFC000000, + B = UnconditionalBranchFixed | 0x00000000, + BL = UnconditionalBranchFixed | 0x80000000 +}; + +// Unconditional branch to register. +enum UnconditionalBranchToRegisterOp { + UnconditionalBranchToRegisterFixed = 0xD6000000, + UnconditionalBranchToRegisterFMask = 0xFE000000, + UnconditionalBranchToRegisterMask = 0xFFFFFC1F, + BR = UnconditionalBranchToRegisterFixed | 0x001F0000, + BLR = UnconditionalBranchToRegisterFixed | 0x003F0000, + RET = UnconditionalBranchToRegisterFixed | 0x005F0000 +}; + +// Compare and branch. +enum CompareBranchOp { + CompareBranchFixed = 0x34000000, + CompareBranchFMask = 0x7E000000, + CompareBranchMask = 0xFF000000, + CBZ_w = CompareBranchFixed | 0x00000000, + CBZ_x = CompareBranchFixed | 0x80000000, + CBZ = CBZ_w, + CBNZ_w = CompareBranchFixed | 0x01000000, + CBNZ_x = CompareBranchFixed | 0x81000000, + CBNZ = CBNZ_w +}; + +// Test and branch. +enum TestBranchOp { + TestBranchFixed = 0x36000000, + TestBranchFMask = 0x7E000000, + TestBranchMask = 0x7F000000, + TBZ = TestBranchFixed | 0x00000000, + TBNZ = TestBranchFixed | 0x01000000 +}; + +// Conditional branch. +enum ConditionalBranchOp { + ConditionalBranchFixed = 0x54000000, + ConditionalBranchFMask = 0xFE000000, + ConditionalBranchMask = 0xFF000010, + B_cond = ConditionalBranchFixed | 0x00000000 +}; + +// System. +// System instruction encoding is complicated because some instructions use op +// and CR fields to encode parameters. To handle this cleanly, the system +// instructions are split into more than one enum. + +enum SystemOp { + SystemFixed = 0xD5000000, + SystemFMask = 0xFFC00000 +}; + +enum SystemSysRegOp { + SystemSysRegFixed = 0xD5100000, + SystemSysRegFMask = 0xFFD00000, + SystemSysRegMask = 0xFFF00000, + MRS = SystemSysRegFixed | 0x00200000, + MSR = SystemSysRegFixed | 0x00000000 +}; + +enum SystemHintOp { + SystemHintFixed = 0xD503201F, + SystemHintFMask = 0xFFFFF01F, + SystemHintMask = 0xFFFFF01F, + HINT = SystemHintFixed | 0x00000000 +}; + +enum SystemSysOp { + SystemSysFixed = 0xD5080000, + SystemSysFMask = 0xFFF80000, + SystemSysMask = 0xFFF80000, + SYS = SystemSysFixed | 0x00000000 +}; + +// Exception. +enum ExceptionOp { + ExceptionFixed = 0xD4000000, + ExceptionFMask = 0xFF000000, + ExceptionMask = 0xFFE0001F, + HLT = ExceptionFixed | 0x00400000, + BRK = ExceptionFixed | 0x00200000, + SVC = ExceptionFixed | 0x00000001, + HVC = ExceptionFixed | 0x00000002, + SMC = ExceptionFixed | 0x00000003, + DCPS1 = ExceptionFixed | 0x00A00001, + DCPS2 = ExceptionFixed | 0x00A00002, + DCPS3 = ExceptionFixed | 0x00A00003 +}; + +enum MemBarrierOp { + MemBarrierFixed = 0xD503309F, + MemBarrierFMask = 0xFFFFF09F, + MemBarrierMask = 0xFFFFF0FF, + DSB = MemBarrierFixed | 0x00000000, + DMB = MemBarrierFixed | 0x00000020, + ISB = MemBarrierFixed | 0x00000040 +}; + +enum SystemExclusiveMonitorOp { + SystemExclusiveMonitorFixed = 0xD503305F, + SystemExclusiveMonitorFMask = 0xFFFFF0FF, + SystemExclusiveMonitorMask = 0xFFFFF0FF, + CLREX = SystemExclusiveMonitorFixed +}; + +// Any load or store. +enum LoadStoreAnyOp { + LoadStoreAnyFMask = 0x0a000000, + LoadStoreAnyFixed = 0x08000000 +}; + +// Any load pair or store pair. +enum LoadStorePairAnyOp { + LoadStorePairAnyFMask = 0x3a000000, + LoadStorePairAnyFixed = 0x28000000 +}; + +#define LOAD_STORE_PAIR_OP_LIST(V) \ + V(STP, w, 0x00000000), \ + V(LDP, w, 0x00400000), \ + V(LDPSW, x, 0x40400000), \ + V(STP, x, 0x80000000), \ + V(LDP, x, 0x80400000), \ + V(STP, s, 0x04000000), \ + V(LDP, s, 0x04400000), \ + V(STP, d, 0x44000000), \ + V(LDP, d, 0x44400000), \ + V(STP, q, 0x84000000), \ + V(LDP, q, 0x84400000) + +// Load/store pair (post, pre and offset.) +enum LoadStorePairOp { + LoadStorePairMask = 0xC4400000, + LoadStorePairLBit = 1 << 22, + #define LOAD_STORE_PAIR(A, B, C) \ + A##_##B = C + LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR) + #undef LOAD_STORE_PAIR +}; + +enum LoadStorePairPostIndexOp { + LoadStorePairPostIndexFixed = 0x28800000, + LoadStorePairPostIndexFMask = 0x3B800000, + LoadStorePairPostIndexMask = 0xFFC00000, + #define LOAD_STORE_PAIR_POST_INDEX(A, B, C) \ + A##_##B##_post = LoadStorePairPostIndexFixed | A##_##B + LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_POST_INDEX) + #undef LOAD_STORE_PAIR_POST_INDEX +}; + +enum LoadStorePairPreIndexOp { + LoadStorePairPreIndexFixed = 0x29800000, + LoadStorePairPreIndexFMask = 0x3B800000, + LoadStorePairPreIndexMask = 0xFFC00000, + #define LOAD_STORE_PAIR_PRE_INDEX(A, B, C) \ + A##_##B##_pre = LoadStorePairPreIndexFixed | A##_##B + LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_PRE_INDEX) + #undef LOAD_STORE_PAIR_PRE_INDEX +}; + +enum LoadStorePairOffsetOp { + LoadStorePairOffsetFixed = 0x29000000, + LoadStorePairOffsetFMask = 0x3B800000, + LoadStorePairOffsetMask = 0xFFC00000, + #define LOAD_STORE_PAIR_OFFSET(A, B, C) \ + A##_##B##_off = LoadStorePairOffsetFixed | A##_##B + LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_OFFSET) + #undef LOAD_STORE_PAIR_OFFSET +}; + +enum LoadStorePairNonTemporalOp { + LoadStorePairNonTemporalFixed = 0x28000000, + LoadStorePairNonTemporalFMask = 0x3B800000, + LoadStorePairNonTemporalMask = 0xFFC00000, + LoadStorePairNonTemporalLBit = 1 << 22, + STNP_w = LoadStorePairNonTemporalFixed | STP_w, + LDNP_w = LoadStorePairNonTemporalFixed | LDP_w, + STNP_x = LoadStorePairNonTemporalFixed | STP_x, + LDNP_x = LoadStorePairNonTemporalFixed | LDP_x, + STNP_s = LoadStorePairNonTemporalFixed | STP_s, + LDNP_s = LoadStorePairNonTemporalFixed | LDP_s, + STNP_d = LoadStorePairNonTemporalFixed | STP_d, + LDNP_d = LoadStorePairNonTemporalFixed | LDP_d, + STNP_q = LoadStorePairNonTemporalFixed | STP_q, + LDNP_q = LoadStorePairNonTemporalFixed | LDP_q +}; + +// Load literal. +enum LoadLiteralOp { + LoadLiteralFixed = 0x18000000, + LoadLiteralFMask = 0x3B000000, + LoadLiteralMask = 0xFF000000, + LDR_w_lit = LoadLiteralFixed | 0x00000000, + LDR_x_lit = LoadLiteralFixed | 0x40000000, + LDRSW_x_lit = LoadLiteralFixed | 0x80000000, + PRFM_lit = LoadLiteralFixed | 0xC0000000, + LDR_s_lit = LoadLiteralFixed | 0x04000000, + LDR_d_lit = LoadLiteralFixed | 0x44000000, + LDR_q_lit = LoadLiteralFixed | 0x84000000 +}; + +#define LOAD_STORE_OP_LIST(V) \ + V(ST, RB, w, 0x00000000), \ + V(ST, RH, w, 0x40000000), \ + V(ST, R, w, 0x80000000), \ + V(ST, R, x, 0xC0000000), \ + V(LD, RB, w, 0x00400000), \ + V(LD, RH, w, 0x40400000), \ + V(LD, R, w, 0x80400000), \ + V(LD, R, x, 0xC0400000), \ + V(LD, RSB, x, 0x00800000), \ + V(LD, RSH, x, 0x40800000), \ + V(LD, RSW, x, 0x80800000), \ + V(LD, RSB, w, 0x00C00000), \ + V(LD, RSH, w, 0x40C00000), \ + V(ST, R, b, 0x04000000), \ + V(ST, R, h, 0x44000000), \ + V(ST, R, s, 0x84000000), \ + V(ST, R, d, 0xC4000000), \ + V(ST, R, q, 0x04800000), \ + V(LD, R, b, 0x04400000), \ + V(LD, R, h, 0x44400000), \ + V(LD, R, s, 0x84400000), \ + V(LD, R, d, 0xC4400000), \ + V(LD, R, q, 0x04C00000) + +// Load/store (post, pre, offset and unsigned.) +enum LoadStoreOp { + LoadStoreMask = 0xC4C00000, + LoadStoreVMask = 0x04000000, + #define LOAD_STORE(A, B, C, D) \ + A##B##_##C = D + LOAD_STORE_OP_LIST(LOAD_STORE), + #undef LOAD_STORE + PRFM = 0xC0800000 +}; + +// Load/store unscaled offset. +enum LoadStoreUnscaledOffsetOp { + LoadStoreUnscaledOffsetFixed = 0x38000000, + LoadStoreUnscaledOffsetFMask = 0x3B200C00, + LoadStoreUnscaledOffsetMask = 0xFFE00C00, + PRFUM = LoadStoreUnscaledOffsetFixed | PRFM, + #define LOAD_STORE_UNSCALED(A, B, C, D) \ + A##U##B##_##C = LoadStoreUnscaledOffsetFixed | D + LOAD_STORE_OP_LIST(LOAD_STORE_UNSCALED) + #undef LOAD_STORE_UNSCALED +}; + +// Load/store post index. +enum LoadStorePostIndex { + LoadStorePostIndexFixed = 0x38000400, + LoadStorePostIndexFMask = 0x3B200C00, + LoadStorePostIndexMask = 0xFFE00C00, + #define LOAD_STORE_POST_INDEX(A, B, C, D) \ + A##B##_##C##_post = LoadStorePostIndexFixed | D + LOAD_STORE_OP_LIST(LOAD_STORE_POST_INDEX) + #undef LOAD_STORE_POST_INDEX +}; + +// Load/store pre index. +enum LoadStorePreIndex { + LoadStorePreIndexFixed = 0x38000C00, + LoadStorePreIndexFMask = 0x3B200C00, + LoadStorePreIndexMask = 0xFFE00C00, + #define LOAD_STORE_PRE_INDEX(A, B, C, D) \ + A##B##_##C##_pre = LoadStorePreIndexFixed | D + LOAD_STORE_OP_LIST(LOAD_STORE_PRE_INDEX) + #undef LOAD_STORE_PRE_INDEX +}; + +// Load/store unsigned offset. +enum LoadStoreUnsignedOffset { + LoadStoreUnsignedOffsetFixed = 0x39000000, + LoadStoreUnsignedOffsetFMask = 0x3B000000, + LoadStoreUnsignedOffsetMask = 0xFFC00000, + PRFM_unsigned = LoadStoreUnsignedOffsetFixed | PRFM, + #define LOAD_STORE_UNSIGNED_OFFSET(A, B, C, D) \ + A##B##_##C##_unsigned = LoadStoreUnsignedOffsetFixed | D + LOAD_STORE_OP_LIST(LOAD_STORE_UNSIGNED_OFFSET) + #undef LOAD_STORE_UNSIGNED_OFFSET +}; + +// Load/store register offset. +enum LoadStoreRegisterOffset { + LoadStoreRegisterOffsetFixed = 0x38200800, + LoadStoreRegisterOffsetFMask = 0x3B200C00, + LoadStoreRegisterOffsetMask = 0xFFE00C00, + PRFM_reg = LoadStoreRegisterOffsetFixed | PRFM, + #define LOAD_STORE_REGISTER_OFFSET(A, B, C, D) \ + A##B##_##C##_reg = LoadStoreRegisterOffsetFixed | D + LOAD_STORE_OP_LIST(LOAD_STORE_REGISTER_OFFSET) + #undef LOAD_STORE_REGISTER_OFFSET +}; + +enum LoadStoreExclusive { + LoadStoreExclusiveFixed = 0x08000000, + LoadStoreExclusiveFMask = 0x3F000000, + LoadStoreExclusiveMask = 0xFFE08000, + STXRB_w = LoadStoreExclusiveFixed | 0x00000000, + STXRH_w = LoadStoreExclusiveFixed | 0x40000000, + STXR_w = LoadStoreExclusiveFixed | 0x80000000, + STXR_x = LoadStoreExclusiveFixed | 0xC0000000, + LDXRB_w = LoadStoreExclusiveFixed | 0x00400000, + LDXRH_w = LoadStoreExclusiveFixed | 0x40400000, + LDXR_w = LoadStoreExclusiveFixed | 0x80400000, + LDXR_x = LoadStoreExclusiveFixed | 0xC0400000, + STXP_w = LoadStoreExclusiveFixed | 0x80200000, + STXP_x = LoadStoreExclusiveFixed | 0xC0200000, + LDXP_w = LoadStoreExclusiveFixed | 0x80600000, + LDXP_x = LoadStoreExclusiveFixed | 0xC0600000, + STLXRB_w = LoadStoreExclusiveFixed | 0x00008000, + STLXRH_w = LoadStoreExclusiveFixed | 0x40008000, + STLXR_w = LoadStoreExclusiveFixed | 0x80008000, + STLXR_x = LoadStoreExclusiveFixed | 0xC0008000, + LDAXRB_w = LoadStoreExclusiveFixed | 0x00408000, + LDAXRH_w = LoadStoreExclusiveFixed | 0x40408000, + LDAXR_w = LoadStoreExclusiveFixed | 0x80408000, + LDAXR_x = LoadStoreExclusiveFixed | 0xC0408000, + STLXP_w = LoadStoreExclusiveFixed | 0x80208000, + STLXP_x = LoadStoreExclusiveFixed | 0xC0208000, + LDAXP_w = LoadStoreExclusiveFixed | 0x80608000, + LDAXP_x = LoadStoreExclusiveFixed | 0xC0608000, + STLRB_w = LoadStoreExclusiveFixed | 0x00808000, + STLRH_w = LoadStoreExclusiveFixed | 0x40808000, + STLR_w = LoadStoreExclusiveFixed | 0x80808000, + STLR_x = LoadStoreExclusiveFixed | 0xC0808000, + LDARB_w = LoadStoreExclusiveFixed | 0x00C08000, + LDARH_w = LoadStoreExclusiveFixed | 0x40C08000, + LDAR_w = LoadStoreExclusiveFixed | 0x80C08000, + LDAR_x = LoadStoreExclusiveFixed | 0xC0C08000 +}; + +// Conditional compare. +enum ConditionalCompareOp { + ConditionalCompareMask = 0x60000000, + CCMN = 0x20000000, + CCMP = 0x60000000 +}; + +// Conditional compare register. +enum ConditionalCompareRegisterOp { + ConditionalCompareRegisterFixed = 0x1A400000, + ConditionalCompareRegisterFMask = 0x1FE00800, + ConditionalCompareRegisterMask = 0xFFE00C10, + CCMN_w = ConditionalCompareRegisterFixed | CCMN, + CCMN_x = ConditionalCompareRegisterFixed | SixtyFourBits | CCMN, + CCMP_w = ConditionalCompareRegisterFixed | CCMP, + CCMP_x = ConditionalCompareRegisterFixed | SixtyFourBits | CCMP +}; + +// Conditional compare immediate. +enum ConditionalCompareImmediateOp { + ConditionalCompareImmediateFixed = 0x1A400800, + ConditionalCompareImmediateFMask = 0x1FE00800, + ConditionalCompareImmediateMask = 0xFFE00C10, + CCMN_w_imm = ConditionalCompareImmediateFixed | CCMN, + CCMN_x_imm = ConditionalCompareImmediateFixed | SixtyFourBits | CCMN, + CCMP_w_imm = ConditionalCompareImmediateFixed | CCMP, + CCMP_x_imm = ConditionalCompareImmediateFixed | SixtyFourBits | CCMP +}; + +// Conditional select. +enum ConditionalSelectOp { + ConditionalSelectFixed = 0x1A800000, + ConditionalSelectFMask = 0x1FE00000, + ConditionalSelectMask = 0xFFE00C00, + CSEL_w = ConditionalSelectFixed | 0x00000000, + CSEL_x = ConditionalSelectFixed | 0x80000000, + CSEL = CSEL_w, + CSINC_w = ConditionalSelectFixed | 0x00000400, + CSINC_x = ConditionalSelectFixed | 0x80000400, + CSINC = CSINC_w, + CSINV_w = ConditionalSelectFixed | 0x40000000, + CSINV_x = ConditionalSelectFixed | 0xC0000000, + CSINV = CSINV_w, + CSNEG_w = ConditionalSelectFixed | 0x40000400, + CSNEG_x = ConditionalSelectFixed | 0xC0000400, + CSNEG = CSNEG_w +}; + +// Data processing 1 source. +enum DataProcessing1SourceOp { + DataProcessing1SourceFixed = 0x5AC00000, + DataProcessing1SourceFMask = 0x5FE00000, + DataProcessing1SourceMask = 0xFFFFFC00, + RBIT = DataProcessing1SourceFixed | 0x00000000, + RBIT_w = RBIT, + RBIT_x = RBIT | SixtyFourBits, + REV16 = DataProcessing1SourceFixed | 0x00000400, + REV16_w = REV16, + REV16_x = REV16 | SixtyFourBits, + REV = DataProcessing1SourceFixed | 0x00000800, + REV_w = REV, + REV32_x = REV | SixtyFourBits, + REV_x = DataProcessing1SourceFixed | SixtyFourBits | 0x00000C00, + CLZ = DataProcessing1SourceFixed | 0x00001000, + CLZ_w = CLZ, + CLZ_x = CLZ | SixtyFourBits, + CLS = DataProcessing1SourceFixed | 0x00001400, + CLS_w = CLS, + CLS_x = CLS | SixtyFourBits +}; + +// Data processing 2 source. +enum DataProcessing2SourceOp { + DataProcessing2SourceFixed = 0x1AC00000, + DataProcessing2SourceFMask = 0x5FE00000, + DataProcessing2SourceMask = 0xFFE0FC00, + UDIV_w = DataProcessing2SourceFixed | 0x00000800, + UDIV_x = DataProcessing2SourceFixed | 0x80000800, + UDIV = UDIV_w, + SDIV_w = DataProcessing2SourceFixed | 0x00000C00, + SDIV_x = DataProcessing2SourceFixed | 0x80000C00, + SDIV = SDIV_w, + LSLV_w = DataProcessing2SourceFixed | 0x00002000, + LSLV_x = DataProcessing2SourceFixed | 0x80002000, + LSLV = LSLV_w, + LSRV_w = DataProcessing2SourceFixed | 0x00002400, + LSRV_x = DataProcessing2SourceFixed | 0x80002400, + LSRV = LSRV_w, + ASRV_w = DataProcessing2SourceFixed | 0x00002800, + ASRV_x = DataProcessing2SourceFixed | 0x80002800, + ASRV = ASRV_w, + RORV_w = DataProcessing2SourceFixed | 0x00002C00, + RORV_x = DataProcessing2SourceFixed | 0x80002C00, + RORV = RORV_w, + CRC32B = DataProcessing2SourceFixed | 0x00004000, + CRC32H = DataProcessing2SourceFixed | 0x00004400, + CRC32W = DataProcessing2SourceFixed | 0x00004800, + CRC32X = DataProcessing2SourceFixed | SixtyFourBits | 0x00004C00, + CRC32CB = DataProcessing2SourceFixed | 0x00005000, + CRC32CH = DataProcessing2SourceFixed | 0x00005400, + CRC32CW = DataProcessing2SourceFixed | 0x00005800, + CRC32CX = DataProcessing2SourceFixed | SixtyFourBits | 0x00005C00 +}; + +// Data processing 3 source. +enum DataProcessing3SourceOp { + DataProcessing3SourceFixed = 0x1B000000, + DataProcessing3SourceFMask = 0x1F000000, + DataProcessing3SourceMask = 0xFFE08000, + MADD_w = DataProcessing3SourceFixed | 0x00000000, + MADD_x = DataProcessing3SourceFixed | 0x80000000, + MADD = MADD_w, + MSUB_w = DataProcessing3SourceFixed | 0x00008000, + MSUB_x = DataProcessing3SourceFixed | 0x80008000, + MSUB = MSUB_w, + SMADDL_x = DataProcessing3SourceFixed | 0x80200000, + SMSUBL_x = DataProcessing3SourceFixed | 0x80208000, + SMULH_x = DataProcessing3SourceFixed | 0x80400000, + UMADDL_x = DataProcessing3SourceFixed | 0x80A00000, + UMSUBL_x = DataProcessing3SourceFixed | 0x80A08000, + UMULH_x = DataProcessing3SourceFixed | 0x80C00000 +}; + +// Floating point compare. +enum FPCompareOp { + FPCompareFixed = 0x1E202000, + FPCompareFMask = 0x5F203C00, + FPCompareMask = 0xFFE0FC1F, + FCMP_s = FPCompareFixed | 0x00000000, + FCMP_d = FPCompareFixed | FP64 | 0x00000000, + FCMP = FCMP_s, + FCMP_s_zero = FPCompareFixed | 0x00000008, + FCMP_d_zero = FPCompareFixed | FP64 | 0x00000008, + FCMP_zero = FCMP_s_zero, + FCMPE_s = FPCompareFixed | 0x00000010, + FCMPE_d = FPCompareFixed | FP64 | 0x00000010, + FCMPE = FCMPE_s, + FCMPE_s_zero = FPCompareFixed | 0x00000018, + FCMPE_d_zero = FPCompareFixed | FP64 | 0x00000018, + FCMPE_zero = FCMPE_s_zero +}; + +// Floating point conditional compare. +enum FPConditionalCompareOp { + FPConditionalCompareFixed = 0x1E200400, + FPConditionalCompareFMask = 0x5F200C00, + FPConditionalCompareMask = 0xFFE00C10, + FCCMP_s = FPConditionalCompareFixed | 0x00000000, + FCCMP_d = FPConditionalCompareFixed | FP64 | 0x00000000, + FCCMP = FCCMP_s, + FCCMPE_s = FPConditionalCompareFixed | 0x00000010, + FCCMPE_d = FPConditionalCompareFixed | FP64 | 0x00000010, + FCCMPE = FCCMPE_s +}; + +// Floating point conditional select. +enum FPConditionalSelectOp { + FPConditionalSelectFixed = 0x1E200C00, + FPConditionalSelectFMask = 0x5F200C00, + FPConditionalSelectMask = 0xFFE00C00, + FCSEL_s = FPConditionalSelectFixed | 0x00000000, + FCSEL_d = FPConditionalSelectFixed | FP64 | 0x00000000, + FCSEL = FCSEL_s +}; + +// Floating point immediate. +enum FPImmediateOp { + FPImmediateFixed = 0x1E201000, + FPImmediateFMask = 0x5F201C00, + FPImmediateMask = 0xFFE01C00, + FMOV_s_imm = FPImmediateFixed | 0x00000000, + FMOV_d_imm = FPImmediateFixed | FP64 | 0x00000000 +}; + +// Floating point data processing 1 source. +enum FPDataProcessing1SourceOp { + FPDataProcessing1SourceFixed = 0x1E204000, + FPDataProcessing1SourceFMask = 0x5F207C00, + FPDataProcessing1SourceMask = 0xFFFFFC00, + FMOV_s = FPDataProcessing1SourceFixed | 0x00000000, + FMOV_d = FPDataProcessing1SourceFixed | FP64 | 0x00000000, + FMOV = FMOV_s, + FABS_s = FPDataProcessing1SourceFixed | 0x00008000, + FABS_d = FPDataProcessing1SourceFixed | FP64 | 0x00008000, + FABS = FABS_s, + FNEG_s = FPDataProcessing1SourceFixed | 0x00010000, + FNEG_d = FPDataProcessing1SourceFixed | FP64 | 0x00010000, + FNEG = FNEG_s, + FSQRT_s = FPDataProcessing1SourceFixed | 0x00018000, + FSQRT_d = FPDataProcessing1SourceFixed | FP64 | 0x00018000, + FSQRT = FSQRT_s, + FCVT_ds = FPDataProcessing1SourceFixed | 0x00028000, + FCVT_sd = FPDataProcessing1SourceFixed | FP64 | 0x00020000, + FCVT_hs = FPDataProcessing1SourceFixed | 0x00038000, + FCVT_hd = FPDataProcessing1SourceFixed | FP64 | 0x00038000, + FCVT_sh = FPDataProcessing1SourceFixed | 0x00C20000, + FCVT_dh = FPDataProcessing1SourceFixed | 0x00C28000, + FRINTN_s = FPDataProcessing1SourceFixed | 0x00040000, + FRINTN_d = FPDataProcessing1SourceFixed | FP64 | 0x00040000, + FRINTN = FRINTN_s, + FRINTP_s = FPDataProcessing1SourceFixed | 0x00048000, + FRINTP_d = FPDataProcessing1SourceFixed | FP64 | 0x00048000, + FRINTP = FRINTP_s, + FRINTM_s = FPDataProcessing1SourceFixed | 0x00050000, + FRINTM_d = FPDataProcessing1SourceFixed | FP64 | 0x00050000, + FRINTM = FRINTM_s, + FRINTZ_s = FPDataProcessing1SourceFixed | 0x00058000, + FRINTZ_d = FPDataProcessing1SourceFixed | FP64 | 0x00058000, + FRINTZ = FRINTZ_s, + FRINTA_s = FPDataProcessing1SourceFixed | 0x00060000, + FRINTA_d = FPDataProcessing1SourceFixed | FP64 | 0x00060000, + FRINTA = FRINTA_s, + FRINTX_s = FPDataProcessing1SourceFixed | 0x00070000, + FRINTX_d = FPDataProcessing1SourceFixed | FP64 | 0x00070000, + FRINTX = FRINTX_s, + FRINTI_s = FPDataProcessing1SourceFixed | 0x00078000, + FRINTI_d = FPDataProcessing1SourceFixed | FP64 | 0x00078000, + FRINTI = FRINTI_s +}; + +// Floating point data processing 2 source. +enum FPDataProcessing2SourceOp { + FPDataProcessing2SourceFixed = 0x1E200800, + FPDataProcessing2SourceFMask = 0x5F200C00, + FPDataProcessing2SourceMask = 0xFFE0FC00, + FMUL = FPDataProcessing2SourceFixed | 0x00000000, + FMUL_s = FMUL, + FMUL_d = FMUL | FP64, + FDIV = FPDataProcessing2SourceFixed | 0x00001000, + FDIV_s = FDIV, + FDIV_d = FDIV | FP64, + FADD = FPDataProcessing2SourceFixed | 0x00002000, + FADD_s = FADD, + FADD_d = FADD | FP64, + FSUB = FPDataProcessing2SourceFixed | 0x00003000, + FSUB_s = FSUB, + FSUB_d = FSUB | FP64, + FMAX = FPDataProcessing2SourceFixed | 0x00004000, + FMAX_s = FMAX, + FMAX_d = FMAX | FP64, + FMIN = FPDataProcessing2SourceFixed | 0x00005000, + FMIN_s = FMIN, + FMIN_d = FMIN | FP64, + FMAXNM = FPDataProcessing2SourceFixed | 0x00006000, + FMAXNM_s = FMAXNM, + FMAXNM_d = FMAXNM | FP64, + FMINNM = FPDataProcessing2SourceFixed | 0x00007000, + FMINNM_s = FMINNM, + FMINNM_d = FMINNM | FP64, + FNMUL = FPDataProcessing2SourceFixed | 0x00008000, + FNMUL_s = FNMUL, + FNMUL_d = FNMUL | FP64 +}; + +// Floating point data processing 3 source. +enum FPDataProcessing3SourceOp { + FPDataProcessing3SourceFixed = 0x1F000000, + FPDataProcessing3SourceFMask = 0x5F000000, + FPDataProcessing3SourceMask = 0xFFE08000, + FMADD_s = FPDataProcessing3SourceFixed | 0x00000000, + FMSUB_s = FPDataProcessing3SourceFixed | 0x00008000, + FNMADD_s = FPDataProcessing3SourceFixed | 0x00200000, + FNMSUB_s = FPDataProcessing3SourceFixed | 0x00208000, + FMADD_d = FPDataProcessing3SourceFixed | 0x00400000, + FMSUB_d = FPDataProcessing3SourceFixed | 0x00408000, + FNMADD_d = FPDataProcessing3SourceFixed | 0x00600000, + FNMSUB_d = FPDataProcessing3SourceFixed | 0x00608000 +}; + +// Conversion between floating point and integer. +enum FPIntegerConvertOp { + FPIntegerConvertFixed = 0x1E200000, + FPIntegerConvertFMask = 0x5F20FC00, + FPIntegerConvertMask = 0xFFFFFC00, + FCVTNS = FPIntegerConvertFixed | 0x00000000, + FCVTNS_ws = FCVTNS, + FCVTNS_xs = FCVTNS | SixtyFourBits, + FCVTNS_wd = FCVTNS | FP64, + FCVTNS_xd = FCVTNS | SixtyFourBits | FP64, + FCVTNU = FPIntegerConvertFixed | 0x00010000, + FCVTNU_ws = FCVTNU, + FCVTNU_xs = FCVTNU | SixtyFourBits, + FCVTNU_wd = FCVTNU | FP64, + FCVTNU_xd = FCVTNU | SixtyFourBits | FP64, + FCVTPS = FPIntegerConvertFixed | 0x00080000, + FCVTPS_ws = FCVTPS, + FCVTPS_xs = FCVTPS | SixtyFourBits, + FCVTPS_wd = FCVTPS | FP64, + FCVTPS_xd = FCVTPS | SixtyFourBits | FP64, + FCVTPU = FPIntegerConvertFixed | 0x00090000, + FCVTPU_ws = FCVTPU, + FCVTPU_xs = FCVTPU | SixtyFourBits, + FCVTPU_wd = FCVTPU | FP64, + FCVTPU_xd = FCVTPU | SixtyFourBits | FP64, + FCVTMS = FPIntegerConvertFixed | 0x00100000, + FCVTMS_ws = FCVTMS, + FCVTMS_xs = FCVTMS | SixtyFourBits, + FCVTMS_wd = FCVTMS | FP64, + FCVTMS_xd = FCVTMS | SixtyFourBits | FP64, + FCVTMU = FPIntegerConvertFixed | 0x00110000, + FCVTMU_ws = FCVTMU, + FCVTMU_xs = FCVTMU | SixtyFourBits, + FCVTMU_wd = FCVTMU | FP64, + FCVTMU_xd = FCVTMU | SixtyFourBits | FP64, + FCVTZS = FPIntegerConvertFixed | 0x00180000, + FCVTZS_ws = FCVTZS, + FCVTZS_xs = FCVTZS | SixtyFourBits, + FCVTZS_wd = FCVTZS | FP64, + FCVTZS_xd = FCVTZS | SixtyFourBits | FP64, + FCVTZU = FPIntegerConvertFixed | 0x00190000, + FCVTZU_ws = FCVTZU, + FCVTZU_xs = FCVTZU | SixtyFourBits, + FCVTZU_wd = FCVTZU | FP64, + FCVTZU_xd = FCVTZU | SixtyFourBits | FP64, + SCVTF = FPIntegerConvertFixed | 0x00020000, + SCVTF_sw = SCVTF, + SCVTF_sx = SCVTF | SixtyFourBits, + SCVTF_dw = SCVTF | FP64, + SCVTF_dx = SCVTF | SixtyFourBits | FP64, + UCVTF = FPIntegerConvertFixed | 0x00030000, + UCVTF_sw = UCVTF, + UCVTF_sx = UCVTF | SixtyFourBits, + UCVTF_dw = UCVTF | FP64, + UCVTF_dx = UCVTF | SixtyFourBits | FP64, + FCVTAS = FPIntegerConvertFixed | 0x00040000, + FCVTAS_ws = FCVTAS, + FCVTAS_xs = FCVTAS | SixtyFourBits, + FCVTAS_wd = FCVTAS | FP64, + FCVTAS_xd = FCVTAS | SixtyFourBits | FP64, + FCVTAU = FPIntegerConvertFixed | 0x00050000, + FCVTAU_ws = FCVTAU, + FCVTAU_xs = FCVTAU | SixtyFourBits, + FCVTAU_wd = FCVTAU | FP64, + FCVTAU_xd = FCVTAU | SixtyFourBits | FP64, + FMOV_ws = FPIntegerConvertFixed | 0x00060000, + FMOV_sw = FPIntegerConvertFixed | 0x00070000, + FMOV_xd = FMOV_ws | SixtyFourBits | FP64, + FMOV_dx = FMOV_sw | SixtyFourBits | FP64, + FMOV_d1_x = FPIntegerConvertFixed | SixtyFourBits | 0x008F0000, + FMOV_x_d1 = FPIntegerConvertFixed | SixtyFourBits | 0x008E0000 +}; + +// Conversion between fixed point and floating point. +enum FPFixedPointConvertOp { + FPFixedPointConvertFixed = 0x1E000000, + FPFixedPointConvertFMask = 0x5F200000, + FPFixedPointConvertMask = 0xFFFF0000, + FCVTZS_fixed = FPFixedPointConvertFixed | 0x00180000, + FCVTZS_ws_fixed = FCVTZS_fixed, + FCVTZS_xs_fixed = FCVTZS_fixed | SixtyFourBits, + FCVTZS_wd_fixed = FCVTZS_fixed | FP64, + FCVTZS_xd_fixed = FCVTZS_fixed | SixtyFourBits | FP64, + FCVTZU_fixed = FPFixedPointConvertFixed | 0x00190000, + FCVTZU_ws_fixed = FCVTZU_fixed, + FCVTZU_xs_fixed = FCVTZU_fixed | SixtyFourBits, + FCVTZU_wd_fixed = FCVTZU_fixed | FP64, + FCVTZU_xd_fixed = FCVTZU_fixed | SixtyFourBits | FP64, + SCVTF_fixed = FPFixedPointConvertFixed | 0x00020000, + SCVTF_sw_fixed = SCVTF_fixed, + SCVTF_sx_fixed = SCVTF_fixed | SixtyFourBits, + SCVTF_dw_fixed = SCVTF_fixed | FP64, + SCVTF_dx_fixed = SCVTF_fixed | SixtyFourBits | FP64, + UCVTF_fixed = FPFixedPointConvertFixed | 0x00030000, + UCVTF_sw_fixed = UCVTF_fixed, + UCVTF_sx_fixed = UCVTF_fixed | SixtyFourBits, + UCVTF_dw_fixed = UCVTF_fixed | FP64, + UCVTF_dx_fixed = UCVTF_fixed | SixtyFourBits | FP64 +}; + +// Crypto - two register SHA. +enum Crypto2RegSHAOp { + Crypto2RegSHAFixed = 0x5E280800, + Crypto2RegSHAFMask = 0xFF3E0C00 +}; + +// Crypto - three register SHA. +enum Crypto3RegSHAOp { + Crypto3RegSHAFixed = 0x5E000000, + Crypto3RegSHAFMask = 0xFF208C00 +}; + +// Crypto - AES. +enum CryptoAESOp { + CryptoAESFixed = 0x4E280800, + CryptoAESFMask = 0xFF3E0C00 +}; + +// NEON instructions with two register operands. +enum NEON2RegMiscOp { + NEON2RegMiscFixed = 0x0E200800, + NEON2RegMiscFMask = 0x9F3E0C00, + NEON2RegMiscMask = 0xBF3FFC00, + NEON2RegMiscUBit = 0x20000000, + NEON_REV64 = NEON2RegMiscFixed | 0x00000000, + NEON_REV32 = NEON2RegMiscFixed | 0x20000000, + NEON_REV16 = NEON2RegMiscFixed | 0x00001000, + NEON_SADDLP = NEON2RegMiscFixed | 0x00002000, + NEON_UADDLP = NEON_SADDLP | NEON2RegMiscUBit, + NEON_SUQADD = NEON2RegMiscFixed | 0x00003000, + NEON_USQADD = NEON_SUQADD | NEON2RegMiscUBit, + NEON_CLS = NEON2RegMiscFixed | 0x00004000, + NEON_CLZ = NEON2RegMiscFixed | 0x20004000, + NEON_CNT = NEON2RegMiscFixed | 0x00005000, + NEON_RBIT_NOT = NEON2RegMiscFixed | 0x20005000, + NEON_SADALP = NEON2RegMiscFixed | 0x00006000, + NEON_UADALP = NEON_SADALP | NEON2RegMiscUBit, + NEON_SQABS = NEON2RegMiscFixed | 0x00007000, + NEON_SQNEG = NEON2RegMiscFixed | 0x20007000, + NEON_CMGT_zero = NEON2RegMiscFixed | 0x00008000, + NEON_CMGE_zero = NEON2RegMiscFixed | 0x20008000, + NEON_CMEQ_zero = NEON2RegMiscFixed | 0x00009000, + NEON_CMLE_zero = NEON2RegMiscFixed | 0x20009000, + NEON_CMLT_zero = NEON2RegMiscFixed | 0x0000A000, + NEON_ABS = NEON2RegMiscFixed | 0x0000B000, + NEON_NEG = NEON2RegMiscFixed | 0x2000B000, + NEON_XTN = NEON2RegMiscFixed | 0x00012000, + NEON_SQXTUN = NEON2RegMiscFixed | 0x20012000, + NEON_SHLL = NEON2RegMiscFixed | 0x20013000, + NEON_SQXTN = NEON2RegMiscFixed | 0x00014000, + NEON_UQXTN = NEON_SQXTN | NEON2RegMiscUBit, + + NEON2RegMiscOpcode = 0x0001F000, + NEON_RBIT_NOT_opcode = NEON_RBIT_NOT & NEON2RegMiscOpcode, + NEON_NEG_opcode = NEON_NEG & NEON2RegMiscOpcode, + NEON_XTN_opcode = NEON_XTN & NEON2RegMiscOpcode, + NEON_UQXTN_opcode = NEON_UQXTN & NEON2RegMiscOpcode, + + // These instructions use only one bit of the size field. The other bit is + // used to distinguish between instructions. + NEON2RegMiscFPMask = NEON2RegMiscMask | 0x00800000, + NEON_FABS = NEON2RegMiscFixed | 0x0080F000, + NEON_FNEG = NEON2RegMiscFixed | 0x2080F000, + NEON_FCVTN = NEON2RegMiscFixed | 0x00016000, + NEON_FCVTXN = NEON2RegMiscFixed | 0x20016000, + NEON_FCVTL = NEON2RegMiscFixed | 0x00017000, + NEON_FRINTN = NEON2RegMiscFixed | 0x00018000, + NEON_FRINTA = NEON2RegMiscFixed | 0x20018000, + NEON_FRINTP = NEON2RegMiscFixed | 0x00818000, + NEON_FRINTM = NEON2RegMiscFixed | 0x00019000, + NEON_FRINTX = NEON2RegMiscFixed | 0x20019000, + NEON_FRINTZ = NEON2RegMiscFixed | 0x00819000, + NEON_FRINTI = NEON2RegMiscFixed | 0x20819000, + NEON_FCVTNS = NEON2RegMiscFixed | 0x0001A000, + NEON_FCVTNU = NEON_FCVTNS | NEON2RegMiscUBit, + NEON_FCVTPS = NEON2RegMiscFixed | 0x0081A000, + NEON_FCVTPU = NEON_FCVTPS | NEON2RegMiscUBit, + NEON_FCVTMS = NEON2RegMiscFixed | 0x0001B000, + NEON_FCVTMU = NEON_FCVTMS | NEON2RegMiscUBit, + NEON_FCVTZS = NEON2RegMiscFixed | 0x0081B000, + NEON_FCVTZU = NEON_FCVTZS | NEON2RegMiscUBit, + NEON_FCVTAS = NEON2RegMiscFixed | 0x0001C000, + NEON_FCVTAU = NEON_FCVTAS | NEON2RegMiscUBit, + NEON_FSQRT = NEON2RegMiscFixed | 0x2081F000, + NEON_SCVTF = NEON2RegMiscFixed | 0x0001D000, + NEON_UCVTF = NEON_SCVTF | NEON2RegMiscUBit, + NEON_URSQRTE = NEON2RegMiscFixed | 0x2081C000, + NEON_URECPE = NEON2RegMiscFixed | 0x0081C000, + NEON_FRSQRTE = NEON2RegMiscFixed | 0x2081D000, + NEON_FRECPE = NEON2RegMiscFixed | 0x0081D000, + NEON_FCMGT_zero = NEON2RegMiscFixed | 0x0080C000, + NEON_FCMGE_zero = NEON2RegMiscFixed | 0x2080C000, + NEON_FCMEQ_zero = NEON2RegMiscFixed | 0x0080D000, + NEON_FCMLE_zero = NEON2RegMiscFixed | 0x2080D000, + NEON_FCMLT_zero = NEON2RegMiscFixed | 0x0080E000, + + NEON_FCVTL_opcode = NEON_FCVTL & NEON2RegMiscOpcode, + NEON_FCVTN_opcode = NEON_FCVTN & NEON2RegMiscOpcode +}; + +// NEON instructions with three same-type operands. +enum NEON3SameOp { + NEON3SameFixed = 0x0E200400, + NEON3SameFMask = 0x9F200400, + NEON3SameMask = 0xBF20FC00, + NEON3SameUBit = 0x20000000, + NEON_ADD = NEON3SameFixed | 0x00008000, + NEON_ADDP = NEON3SameFixed | 0x0000B800, + NEON_SHADD = NEON3SameFixed | 0x00000000, + NEON_SHSUB = NEON3SameFixed | 0x00002000, + NEON_SRHADD = NEON3SameFixed | 0x00001000, + NEON_CMEQ = NEON3SameFixed | NEON3SameUBit | 0x00008800, + NEON_CMGE = NEON3SameFixed | 0x00003800, + NEON_CMGT = NEON3SameFixed | 0x00003000, + NEON_CMHI = NEON3SameFixed | NEON3SameUBit | NEON_CMGT, + NEON_CMHS = NEON3SameFixed | NEON3SameUBit | NEON_CMGE, + NEON_CMTST = NEON3SameFixed | 0x00008800, + NEON_MLA = NEON3SameFixed | 0x00009000, + NEON_MLS = NEON3SameFixed | 0x20009000, + NEON_MUL = NEON3SameFixed | 0x00009800, + NEON_PMUL = NEON3SameFixed | 0x20009800, + NEON_SRSHL = NEON3SameFixed | 0x00005000, + NEON_SQSHL = NEON3SameFixed | 0x00004800, + NEON_SQRSHL = NEON3SameFixed | 0x00005800, + NEON_SSHL = NEON3SameFixed | 0x00004000, + NEON_SMAX = NEON3SameFixed | 0x00006000, + NEON_SMAXP = NEON3SameFixed | 0x0000A000, + NEON_SMIN = NEON3SameFixed | 0x00006800, + NEON_SMINP = NEON3SameFixed | 0x0000A800, + NEON_SABD = NEON3SameFixed | 0x00007000, + NEON_SABA = NEON3SameFixed | 0x00007800, + NEON_UABD = NEON3SameFixed | NEON3SameUBit | NEON_SABD, + NEON_UABA = NEON3SameFixed | NEON3SameUBit | NEON_SABA, + NEON_SQADD = NEON3SameFixed | 0x00000800, + NEON_SQSUB = NEON3SameFixed | 0x00002800, + NEON_SUB = NEON3SameFixed | NEON3SameUBit | 0x00008000, + NEON_UHADD = NEON3SameFixed | NEON3SameUBit | NEON_SHADD, + NEON_UHSUB = NEON3SameFixed | NEON3SameUBit | NEON_SHSUB, + NEON_URHADD = NEON3SameFixed | NEON3SameUBit | NEON_SRHADD, + NEON_UMAX = NEON3SameFixed | NEON3SameUBit | NEON_SMAX, + NEON_UMAXP = NEON3SameFixed | NEON3SameUBit | NEON_SMAXP, + NEON_UMIN = NEON3SameFixed | NEON3SameUBit | NEON_SMIN, + NEON_UMINP = NEON3SameFixed | NEON3SameUBit | NEON_SMINP, + NEON_URSHL = NEON3SameFixed | NEON3SameUBit | NEON_SRSHL, + NEON_UQADD = NEON3SameFixed | NEON3SameUBit | NEON_SQADD, + NEON_UQRSHL = NEON3SameFixed | NEON3SameUBit | NEON_SQRSHL, + NEON_UQSHL = NEON3SameFixed | NEON3SameUBit | NEON_SQSHL, + NEON_UQSUB = NEON3SameFixed | NEON3SameUBit | NEON_SQSUB, + NEON_USHL = NEON3SameFixed | NEON3SameUBit | NEON_SSHL, + NEON_SQDMULH = NEON3SameFixed | 0x0000B000, + NEON_SQRDMULH = NEON3SameFixed | 0x2000B000, + + // NEON floating point instructions with three same-type operands. + NEON3SameFPFixed = NEON3SameFixed | 0x0000C000, + NEON3SameFPFMask = NEON3SameFMask | 0x0000C000, + NEON3SameFPMask = NEON3SameMask | 0x00800000, + NEON_FADD = NEON3SameFixed | 0x0000D000, + NEON_FSUB = NEON3SameFixed | 0x0080D000, + NEON_FMUL = NEON3SameFixed | 0x2000D800, + NEON_FDIV = NEON3SameFixed | 0x2000F800, + NEON_FMAX = NEON3SameFixed | 0x0000F000, + NEON_FMAXNM = NEON3SameFixed | 0x0000C000, + NEON_FMAXP = NEON3SameFixed | 0x2000F000, + NEON_FMAXNMP = NEON3SameFixed | 0x2000C000, + NEON_FMIN = NEON3SameFixed | 0x0080F000, + NEON_FMINNM = NEON3SameFixed | 0x0080C000, + NEON_FMINP = NEON3SameFixed | 0x2080F000, + NEON_FMINNMP = NEON3SameFixed | 0x2080C000, + NEON_FMLA = NEON3SameFixed | 0x0000C800, + NEON_FMLS = NEON3SameFixed | 0x0080C800, + NEON_FMULX = NEON3SameFixed | 0x0000D800, + NEON_FRECPS = NEON3SameFixed | 0x0000F800, + NEON_FRSQRTS = NEON3SameFixed | 0x0080F800, + NEON_FABD = NEON3SameFixed | 0x2080D000, + NEON_FADDP = NEON3SameFixed | 0x2000D000, + NEON_FCMEQ = NEON3SameFixed | 0x0000E000, + NEON_FCMGE = NEON3SameFixed | 0x2000E000, + NEON_FCMGT = NEON3SameFixed | 0x2080E000, + NEON_FACGE = NEON3SameFixed | 0x2000E800, + NEON_FACGT = NEON3SameFixed | 0x2080E800, + + // NEON logical instructions with three same-type operands. + NEON3SameLogicalFixed = NEON3SameFixed | 0x00001800, + NEON3SameLogicalFMask = NEON3SameFMask | 0x0000F800, + NEON3SameLogicalMask = 0xBFE0FC00, + NEON3SameLogicalFormatMask = NEON_Q, + NEON_AND = NEON3SameLogicalFixed | 0x00000000, + NEON_ORR = NEON3SameLogicalFixed | 0x00A00000, + NEON_ORN = NEON3SameLogicalFixed | 0x00C00000, + NEON_EOR = NEON3SameLogicalFixed | 0x20000000, + NEON_BIC = NEON3SameLogicalFixed | 0x00400000, + NEON_BIF = NEON3SameLogicalFixed | 0x20C00000, + NEON_BIT = NEON3SameLogicalFixed | 0x20800000, + NEON_BSL = NEON3SameLogicalFixed | 0x20400000 +}; + +// NEON instructions with three different-type operands. +enum NEON3DifferentOp { + NEON3DifferentFixed = 0x0E200000, + NEON3DifferentFMask = 0x9F200C00, + NEON3DifferentMask = 0xFF20FC00, + NEON_ADDHN = NEON3DifferentFixed | 0x00004000, + NEON_ADDHN2 = NEON_ADDHN | NEON_Q, + NEON_PMULL = NEON3DifferentFixed | 0x0000E000, + NEON_PMULL2 = NEON_PMULL | NEON_Q, + NEON_RADDHN = NEON3DifferentFixed | 0x20004000, + NEON_RADDHN2 = NEON_RADDHN | NEON_Q, + NEON_RSUBHN = NEON3DifferentFixed | 0x20006000, + NEON_RSUBHN2 = NEON_RSUBHN | NEON_Q, + NEON_SABAL = NEON3DifferentFixed | 0x00005000, + NEON_SABAL2 = NEON_SABAL | NEON_Q, + NEON_SABDL = NEON3DifferentFixed | 0x00007000, + NEON_SABDL2 = NEON_SABDL | NEON_Q, + NEON_SADDL = NEON3DifferentFixed | 0x00000000, + NEON_SADDL2 = NEON_SADDL | NEON_Q, + NEON_SADDW = NEON3DifferentFixed | 0x00001000, + NEON_SADDW2 = NEON_SADDW | NEON_Q, + NEON_SMLAL = NEON3DifferentFixed | 0x00008000, + NEON_SMLAL2 = NEON_SMLAL | NEON_Q, + NEON_SMLSL = NEON3DifferentFixed | 0x0000A000, + NEON_SMLSL2 = NEON_SMLSL | NEON_Q, + NEON_SMULL = NEON3DifferentFixed | 0x0000C000, + NEON_SMULL2 = NEON_SMULL | NEON_Q, + NEON_SSUBL = NEON3DifferentFixed | 0x00002000, + NEON_SSUBL2 = NEON_SSUBL | NEON_Q, + NEON_SSUBW = NEON3DifferentFixed | 0x00003000, + NEON_SSUBW2 = NEON_SSUBW | NEON_Q, + NEON_SQDMLAL = NEON3DifferentFixed | 0x00009000, + NEON_SQDMLAL2 = NEON_SQDMLAL | NEON_Q, + NEON_SQDMLSL = NEON3DifferentFixed | 0x0000B000, + NEON_SQDMLSL2 = NEON_SQDMLSL | NEON_Q, + NEON_SQDMULL = NEON3DifferentFixed | 0x0000D000, + NEON_SQDMULL2 = NEON_SQDMULL | NEON_Q, + NEON_SUBHN = NEON3DifferentFixed | 0x00006000, + NEON_SUBHN2 = NEON_SUBHN | NEON_Q, + NEON_UABAL = NEON_SABAL | NEON3SameUBit, + NEON_UABAL2 = NEON_UABAL | NEON_Q, + NEON_UABDL = NEON_SABDL | NEON3SameUBit, + NEON_UABDL2 = NEON_UABDL | NEON_Q, + NEON_UADDL = NEON_SADDL | NEON3SameUBit, + NEON_UADDL2 = NEON_UADDL | NEON_Q, + NEON_UADDW = NEON_SADDW | NEON3SameUBit, + NEON_UADDW2 = NEON_UADDW | NEON_Q, + NEON_UMLAL = NEON_SMLAL | NEON3SameUBit, + NEON_UMLAL2 = NEON_UMLAL | NEON_Q, + NEON_UMLSL = NEON_SMLSL | NEON3SameUBit, + NEON_UMLSL2 = NEON_UMLSL | NEON_Q, + NEON_UMULL = NEON_SMULL | NEON3SameUBit, + NEON_UMULL2 = NEON_UMULL | NEON_Q, + NEON_USUBL = NEON_SSUBL | NEON3SameUBit, + NEON_USUBL2 = NEON_USUBL | NEON_Q, + NEON_USUBW = NEON_SSUBW | NEON3SameUBit, + NEON_USUBW2 = NEON_USUBW | NEON_Q +}; + +// NEON instructions operating across vectors. +enum NEONAcrossLanesOp { + NEONAcrossLanesFixed = 0x0E300800, + NEONAcrossLanesFMask = 0x9F3E0C00, + NEONAcrossLanesMask = 0xBF3FFC00, + NEON_ADDV = NEONAcrossLanesFixed | 0x0001B000, + NEON_SADDLV = NEONAcrossLanesFixed | 0x00003000, + NEON_UADDLV = NEONAcrossLanesFixed | 0x20003000, + NEON_SMAXV = NEONAcrossLanesFixed | 0x0000A000, + NEON_SMINV = NEONAcrossLanesFixed | 0x0001A000, + NEON_UMAXV = NEONAcrossLanesFixed | 0x2000A000, + NEON_UMINV = NEONAcrossLanesFixed | 0x2001A000, + + // NEON floating point across instructions. + NEONAcrossLanesFPFixed = NEONAcrossLanesFixed | 0x0000C000, + NEONAcrossLanesFPFMask = NEONAcrossLanesFMask | 0x0000C000, + NEONAcrossLanesFPMask = NEONAcrossLanesMask | 0x00800000, + + NEON_FMAXV = NEONAcrossLanesFPFixed | 0x2000F000, + NEON_FMINV = NEONAcrossLanesFPFixed | 0x2080F000, + NEON_FMAXNMV = NEONAcrossLanesFPFixed | 0x2000C000, + NEON_FMINNMV = NEONAcrossLanesFPFixed | 0x2080C000 +}; + +// NEON instructions with indexed element operand. +enum NEONByIndexedElementOp { + NEONByIndexedElementFixed = 0x0F000000, + NEONByIndexedElementFMask = 0x9F000400, + NEONByIndexedElementMask = 0xBF00F400, + NEON_MUL_byelement = NEONByIndexedElementFixed | 0x00008000, + NEON_MLA_byelement = NEONByIndexedElementFixed | 0x20000000, + NEON_MLS_byelement = NEONByIndexedElementFixed | 0x20004000, + NEON_SMULL_byelement = NEONByIndexedElementFixed | 0x0000A000, + NEON_SMLAL_byelement = NEONByIndexedElementFixed | 0x00002000, + NEON_SMLSL_byelement = NEONByIndexedElementFixed | 0x00006000, + NEON_UMULL_byelement = NEONByIndexedElementFixed | 0x2000A000, + NEON_UMLAL_byelement = NEONByIndexedElementFixed | 0x20002000, + NEON_UMLSL_byelement = NEONByIndexedElementFixed | 0x20006000, + NEON_SQDMULL_byelement = NEONByIndexedElementFixed | 0x0000B000, + NEON_SQDMLAL_byelement = NEONByIndexedElementFixed | 0x00003000, + NEON_SQDMLSL_byelement = NEONByIndexedElementFixed | 0x00007000, + NEON_SQDMULH_byelement = NEONByIndexedElementFixed | 0x0000C000, + NEON_SQRDMULH_byelement = NEONByIndexedElementFixed | 0x0000D000, + + // Floating point instructions. + NEONByIndexedElementFPFixed = NEONByIndexedElementFixed | 0x00800000, + NEONByIndexedElementFPMask = NEONByIndexedElementMask | 0x00800000, + NEON_FMLA_byelement = NEONByIndexedElementFPFixed | 0x00001000, + NEON_FMLS_byelement = NEONByIndexedElementFPFixed | 0x00005000, + NEON_FMUL_byelement = NEONByIndexedElementFPFixed | 0x00009000, + NEON_FMULX_byelement = NEONByIndexedElementFPFixed | 0x20009000 +}; + +// NEON register copy. +enum NEONCopyOp { + NEONCopyFixed = 0x0E000400, + NEONCopyFMask = 0x9FE08400, + NEONCopyMask = 0x3FE08400, + NEONCopyInsElementMask = NEONCopyMask | 0x40000000, + NEONCopyInsGeneralMask = NEONCopyMask | 0x40007800, + NEONCopyDupElementMask = NEONCopyMask | 0x20007800, + NEONCopyDupGeneralMask = NEONCopyDupElementMask, + NEONCopyUmovMask = NEONCopyMask | 0x20007800, + NEONCopySmovMask = NEONCopyMask | 0x20007800, + NEON_INS_ELEMENT = NEONCopyFixed | 0x60000000, + NEON_INS_GENERAL = NEONCopyFixed | 0x40001800, + NEON_DUP_ELEMENT = NEONCopyFixed | 0x00000000, + NEON_DUP_GENERAL = NEONCopyFixed | 0x00000800, + NEON_SMOV = NEONCopyFixed | 0x00002800, + NEON_UMOV = NEONCopyFixed | 0x00003800 +}; + +// NEON extract. +enum NEONExtractOp { + NEONExtractFixed = 0x2E000000, + NEONExtractFMask = 0xBF208400, + NEONExtractMask = 0xBFE08400, + NEON_EXT = NEONExtractFixed | 0x00000000 +}; + +enum NEONLoadStoreMultiOp { + NEONLoadStoreMultiL = 0x00400000, + NEONLoadStoreMulti1_1v = 0x00007000, + NEONLoadStoreMulti1_2v = 0x0000A000, + NEONLoadStoreMulti1_3v = 0x00006000, + NEONLoadStoreMulti1_4v = 0x00002000, + NEONLoadStoreMulti2 = 0x00008000, + NEONLoadStoreMulti3 = 0x00004000, + NEONLoadStoreMulti4 = 0x00000000 +}; + +// NEON load/store multiple structures. +enum NEONLoadStoreMultiStructOp { + NEONLoadStoreMultiStructFixed = 0x0C000000, + NEONLoadStoreMultiStructFMask = 0xBFBF0000, + NEONLoadStoreMultiStructMask = 0xBFFFF000, + NEONLoadStoreMultiStructStore = NEONLoadStoreMultiStructFixed, + NEONLoadStoreMultiStructLoad = NEONLoadStoreMultiStructFixed | + NEONLoadStoreMultiL, + NEON_LD1_1v = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti1_1v, + NEON_LD1_2v = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti1_2v, + NEON_LD1_3v = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti1_3v, + NEON_LD1_4v = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti1_4v, + NEON_LD2 = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti2, + NEON_LD3 = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti3, + NEON_LD4 = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti4, + NEON_ST1_1v = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti1_1v, + NEON_ST1_2v = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti1_2v, + NEON_ST1_3v = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti1_3v, + NEON_ST1_4v = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti1_4v, + NEON_ST2 = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti2, + NEON_ST3 = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti3, + NEON_ST4 = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti4 +}; + +// NEON load/store multiple structures with post-index addressing. +enum NEONLoadStoreMultiStructPostIndexOp { + NEONLoadStoreMultiStructPostIndexFixed = 0x0C800000, + NEONLoadStoreMultiStructPostIndexFMask = 0xBFA00000, + NEONLoadStoreMultiStructPostIndexMask = 0xBFE0F000, + NEONLoadStoreMultiStructPostIndex = 0x00800000, + NEON_LD1_1v_post = NEON_LD1_1v | NEONLoadStoreMultiStructPostIndex, + NEON_LD1_2v_post = NEON_LD1_2v | NEONLoadStoreMultiStructPostIndex, + NEON_LD1_3v_post = NEON_LD1_3v | NEONLoadStoreMultiStructPostIndex, + NEON_LD1_4v_post = NEON_LD1_4v | NEONLoadStoreMultiStructPostIndex, + NEON_LD2_post = NEON_LD2 | NEONLoadStoreMultiStructPostIndex, + NEON_LD3_post = NEON_LD3 | NEONLoadStoreMultiStructPostIndex, + NEON_LD4_post = NEON_LD4 | NEONLoadStoreMultiStructPostIndex, + NEON_ST1_1v_post = NEON_ST1_1v | NEONLoadStoreMultiStructPostIndex, + NEON_ST1_2v_post = NEON_ST1_2v | NEONLoadStoreMultiStructPostIndex, + NEON_ST1_3v_post = NEON_ST1_3v | NEONLoadStoreMultiStructPostIndex, + NEON_ST1_4v_post = NEON_ST1_4v | NEONLoadStoreMultiStructPostIndex, + NEON_ST2_post = NEON_ST2 | NEONLoadStoreMultiStructPostIndex, + NEON_ST3_post = NEON_ST3 | NEONLoadStoreMultiStructPostIndex, + NEON_ST4_post = NEON_ST4 | NEONLoadStoreMultiStructPostIndex +}; + +enum NEONLoadStoreSingleOp { + NEONLoadStoreSingle1 = 0x00000000, + NEONLoadStoreSingle2 = 0x00200000, + NEONLoadStoreSingle3 = 0x00002000, + NEONLoadStoreSingle4 = 0x00202000, + NEONLoadStoreSingleL = 0x00400000, + NEONLoadStoreSingle_b = 0x00000000, + NEONLoadStoreSingle_h = 0x00004000, + NEONLoadStoreSingle_s = 0x00008000, + NEONLoadStoreSingle_d = 0x00008400, + NEONLoadStoreSingleAllLanes = 0x0000C000, + NEONLoadStoreSingleLenMask = 0x00202000 +}; + +// NEON load/store single structure. +enum NEONLoadStoreSingleStructOp { + NEONLoadStoreSingleStructFixed = 0x0D000000, + NEONLoadStoreSingleStructFMask = 0xBF9F0000, + NEONLoadStoreSingleStructMask = 0xBFFFE000, + NEONLoadStoreSingleStructStore = NEONLoadStoreSingleStructFixed, + NEONLoadStoreSingleStructLoad = NEONLoadStoreSingleStructFixed | + NEONLoadStoreSingleL, + NEONLoadStoreSingleStructLoad1 = NEONLoadStoreSingle1 | + NEONLoadStoreSingleStructLoad, + NEONLoadStoreSingleStructLoad2 = NEONLoadStoreSingle2 | + NEONLoadStoreSingleStructLoad, + NEONLoadStoreSingleStructLoad3 = NEONLoadStoreSingle3 | + NEONLoadStoreSingleStructLoad, + NEONLoadStoreSingleStructLoad4 = NEONLoadStoreSingle4 | + NEONLoadStoreSingleStructLoad, + NEONLoadStoreSingleStructStore1 = NEONLoadStoreSingle1 | + NEONLoadStoreSingleStructFixed, + NEONLoadStoreSingleStructStore2 = NEONLoadStoreSingle2 | + NEONLoadStoreSingleStructFixed, + NEONLoadStoreSingleStructStore3 = NEONLoadStoreSingle3 | + NEONLoadStoreSingleStructFixed, + NEONLoadStoreSingleStructStore4 = NEONLoadStoreSingle4 | + NEONLoadStoreSingleStructFixed, + NEON_LD1_b = NEONLoadStoreSingleStructLoad1 | NEONLoadStoreSingle_b, + NEON_LD1_h = NEONLoadStoreSingleStructLoad1 | NEONLoadStoreSingle_h, + NEON_LD1_s = NEONLoadStoreSingleStructLoad1 | NEONLoadStoreSingle_s, + NEON_LD1_d = NEONLoadStoreSingleStructLoad1 | NEONLoadStoreSingle_d, + NEON_LD1R = NEONLoadStoreSingleStructLoad1 | NEONLoadStoreSingleAllLanes, + NEON_ST1_b = NEONLoadStoreSingleStructStore1 | NEONLoadStoreSingle_b, + NEON_ST1_h = NEONLoadStoreSingleStructStore1 | NEONLoadStoreSingle_h, + NEON_ST1_s = NEONLoadStoreSingleStructStore1 | NEONLoadStoreSingle_s, + NEON_ST1_d = NEONLoadStoreSingleStructStore1 | NEONLoadStoreSingle_d, + + NEON_LD2_b = NEONLoadStoreSingleStructLoad2 | NEONLoadStoreSingle_b, + NEON_LD2_h = NEONLoadStoreSingleStructLoad2 | NEONLoadStoreSingle_h, + NEON_LD2_s = NEONLoadStoreSingleStructLoad2 | NEONLoadStoreSingle_s, + NEON_LD2_d = NEONLoadStoreSingleStructLoad2 | NEONLoadStoreSingle_d, + NEON_LD2R = NEONLoadStoreSingleStructLoad2 | NEONLoadStoreSingleAllLanes, + NEON_ST2_b = NEONLoadStoreSingleStructStore2 | NEONLoadStoreSingle_b, + NEON_ST2_h = NEONLoadStoreSingleStructStore2 | NEONLoadStoreSingle_h, + NEON_ST2_s = NEONLoadStoreSingleStructStore2 | NEONLoadStoreSingle_s, + NEON_ST2_d = NEONLoadStoreSingleStructStore2 | NEONLoadStoreSingle_d, + + NEON_LD3_b = NEONLoadStoreSingleStructLoad3 | NEONLoadStoreSingle_b, + NEON_LD3_h = NEONLoadStoreSingleStructLoad3 | NEONLoadStoreSingle_h, + NEON_LD3_s = NEONLoadStoreSingleStructLoad3 | NEONLoadStoreSingle_s, + NEON_LD3_d = NEONLoadStoreSingleStructLoad3 | NEONLoadStoreSingle_d, + NEON_LD3R = NEONLoadStoreSingleStructLoad3 | NEONLoadStoreSingleAllLanes, + NEON_ST3_b = NEONLoadStoreSingleStructStore3 | NEONLoadStoreSingle_b, + NEON_ST3_h = NEONLoadStoreSingleStructStore3 | NEONLoadStoreSingle_h, + NEON_ST3_s = NEONLoadStoreSingleStructStore3 | NEONLoadStoreSingle_s, + NEON_ST3_d = NEONLoadStoreSingleStructStore3 | NEONLoadStoreSingle_d, + + NEON_LD4_b = NEONLoadStoreSingleStructLoad4 | NEONLoadStoreSingle_b, + NEON_LD4_h = NEONLoadStoreSingleStructLoad4 | NEONLoadStoreSingle_h, + NEON_LD4_s = NEONLoadStoreSingleStructLoad4 | NEONLoadStoreSingle_s, + NEON_LD4_d = NEONLoadStoreSingleStructLoad4 | NEONLoadStoreSingle_d, + NEON_LD4R = NEONLoadStoreSingleStructLoad4 | NEONLoadStoreSingleAllLanes, + NEON_ST4_b = NEONLoadStoreSingleStructStore4 | NEONLoadStoreSingle_b, + NEON_ST4_h = NEONLoadStoreSingleStructStore4 | NEONLoadStoreSingle_h, + NEON_ST4_s = NEONLoadStoreSingleStructStore4 | NEONLoadStoreSingle_s, + NEON_ST4_d = NEONLoadStoreSingleStructStore4 | NEONLoadStoreSingle_d +}; + +// NEON load/store single structure with post-index addressing. +enum NEONLoadStoreSingleStructPostIndexOp { + NEONLoadStoreSingleStructPostIndexFixed = 0x0D800000, + NEONLoadStoreSingleStructPostIndexFMask = 0xBF800000, + NEONLoadStoreSingleStructPostIndexMask = 0xBFE0E000, + NEONLoadStoreSingleStructPostIndex = 0x00800000, + NEON_LD1_b_post = NEON_LD1_b | NEONLoadStoreSingleStructPostIndex, + NEON_LD1_h_post = NEON_LD1_h | NEONLoadStoreSingleStructPostIndex, + NEON_LD1_s_post = NEON_LD1_s | NEONLoadStoreSingleStructPostIndex, + NEON_LD1_d_post = NEON_LD1_d | NEONLoadStoreSingleStructPostIndex, + NEON_LD1R_post = NEON_LD1R | NEONLoadStoreSingleStructPostIndex, + NEON_ST1_b_post = NEON_ST1_b | NEONLoadStoreSingleStructPostIndex, + NEON_ST1_h_post = NEON_ST1_h | NEONLoadStoreSingleStructPostIndex, + NEON_ST1_s_post = NEON_ST1_s | NEONLoadStoreSingleStructPostIndex, + NEON_ST1_d_post = NEON_ST1_d | NEONLoadStoreSingleStructPostIndex, + + NEON_LD2_b_post = NEON_LD2_b | NEONLoadStoreSingleStructPostIndex, + NEON_LD2_h_post = NEON_LD2_h | NEONLoadStoreSingleStructPostIndex, + NEON_LD2_s_post = NEON_LD2_s | NEONLoadStoreSingleStructPostIndex, + NEON_LD2_d_post = NEON_LD2_d | NEONLoadStoreSingleStructPostIndex, + NEON_LD2R_post = NEON_LD2R | NEONLoadStoreSingleStructPostIndex, + NEON_ST2_b_post = NEON_ST2_b | NEONLoadStoreSingleStructPostIndex, + NEON_ST2_h_post = NEON_ST2_h | NEONLoadStoreSingleStructPostIndex, + NEON_ST2_s_post = NEON_ST2_s | NEONLoadStoreSingleStructPostIndex, + NEON_ST2_d_post = NEON_ST2_d | NEONLoadStoreSingleStructPostIndex, + + NEON_LD3_b_post = NEON_LD3_b | NEONLoadStoreSingleStructPostIndex, + NEON_LD3_h_post = NEON_LD3_h | NEONLoadStoreSingleStructPostIndex, + NEON_LD3_s_post = NEON_LD3_s | NEONLoadStoreSingleStructPostIndex, + NEON_LD3_d_post = NEON_LD3_d | NEONLoadStoreSingleStructPostIndex, + NEON_LD3R_post = NEON_LD3R | NEONLoadStoreSingleStructPostIndex, + NEON_ST3_b_post = NEON_ST3_b | NEONLoadStoreSingleStructPostIndex, + NEON_ST3_h_post = NEON_ST3_h | NEONLoadStoreSingleStructPostIndex, + NEON_ST3_s_post = NEON_ST3_s | NEONLoadStoreSingleStructPostIndex, + NEON_ST3_d_post = NEON_ST3_d | NEONLoadStoreSingleStructPostIndex, + + NEON_LD4_b_post = NEON_LD4_b | NEONLoadStoreSingleStructPostIndex, + NEON_LD4_h_post = NEON_LD4_h | NEONLoadStoreSingleStructPostIndex, + NEON_LD4_s_post = NEON_LD4_s | NEONLoadStoreSingleStructPostIndex, + NEON_LD4_d_post = NEON_LD4_d | NEONLoadStoreSingleStructPostIndex, + NEON_LD4R_post = NEON_LD4R | NEONLoadStoreSingleStructPostIndex, + NEON_ST4_b_post = NEON_ST4_b | NEONLoadStoreSingleStructPostIndex, + NEON_ST4_h_post = NEON_ST4_h | NEONLoadStoreSingleStructPostIndex, + NEON_ST4_s_post = NEON_ST4_s | NEONLoadStoreSingleStructPostIndex, + NEON_ST4_d_post = NEON_ST4_d | NEONLoadStoreSingleStructPostIndex +}; + +// NEON modified immediate. +enum NEONModifiedImmediateOp { + NEONModifiedImmediateFixed = 0x0F000400, + NEONModifiedImmediateFMask = 0x9FF80400, + NEONModifiedImmediateOpBit = 0x20000000, + NEONModifiedImmediate_MOVI = NEONModifiedImmediateFixed | 0x00000000, + NEONModifiedImmediate_MVNI = NEONModifiedImmediateFixed | 0x20000000, + NEONModifiedImmediate_ORR = NEONModifiedImmediateFixed | 0x00001000, + NEONModifiedImmediate_BIC = NEONModifiedImmediateFixed | 0x20001000 +}; + +// NEON shift immediate. +enum NEONShiftImmediateOp { + NEONShiftImmediateFixed = 0x0F000400, + NEONShiftImmediateFMask = 0x9F800400, + NEONShiftImmediateMask = 0xBF80FC00, + NEONShiftImmediateUBit = 0x20000000, + NEON_SHL = NEONShiftImmediateFixed | 0x00005000, + NEON_SSHLL = NEONShiftImmediateFixed | 0x0000A000, + NEON_USHLL = NEONShiftImmediateFixed | 0x2000A000, + NEON_SLI = NEONShiftImmediateFixed | 0x20005000, + NEON_SRI = NEONShiftImmediateFixed | 0x20004000, + NEON_SHRN = NEONShiftImmediateFixed | 0x00008000, + NEON_RSHRN = NEONShiftImmediateFixed | 0x00008800, + NEON_UQSHRN = NEONShiftImmediateFixed | 0x20009000, + NEON_UQRSHRN = NEONShiftImmediateFixed | 0x20009800, + NEON_SQSHRN = NEONShiftImmediateFixed | 0x00009000, + NEON_SQRSHRN = NEONShiftImmediateFixed | 0x00009800, + NEON_SQSHRUN = NEONShiftImmediateFixed | 0x20008000, + NEON_SQRSHRUN = NEONShiftImmediateFixed | 0x20008800, + NEON_SSHR = NEONShiftImmediateFixed | 0x00000000, + NEON_SRSHR = NEONShiftImmediateFixed | 0x00002000, + NEON_USHR = NEONShiftImmediateFixed | 0x20000000, + NEON_URSHR = NEONShiftImmediateFixed | 0x20002000, + NEON_SSRA = NEONShiftImmediateFixed | 0x00001000, + NEON_SRSRA = NEONShiftImmediateFixed | 0x00003000, + NEON_USRA = NEONShiftImmediateFixed | 0x20001000, + NEON_URSRA = NEONShiftImmediateFixed | 0x20003000, + NEON_SQSHLU = NEONShiftImmediateFixed | 0x20006000, + NEON_SCVTF_imm = NEONShiftImmediateFixed | 0x0000E000, + NEON_UCVTF_imm = NEONShiftImmediateFixed | 0x2000E000, + NEON_FCVTZS_imm = NEONShiftImmediateFixed | 0x0000F800, + NEON_FCVTZU_imm = NEONShiftImmediateFixed | 0x2000F800, + NEON_SQSHL_imm = NEONShiftImmediateFixed | 0x00007000, + NEON_UQSHL_imm = NEONShiftImmediateFixed | 0x20007000 +}; + +// NEON table. +enum NEONTableOp { + NEONTableFixed = 0x0E000000, + NEONTableFMask = 0xBF208C00, + NEONTableExt = 0x00001000, + NEONTableMask = 0xBF20FC00, + NEON_TBL_1v = NEONTableFixed | 0x00000000, + NEON_TBL_2v = NEONTableFixed | 0x00002000, + NEON_TBL_3v = NEONTableFixed | 0x00004000, + NEON_TBL_4v = NEONTableFixed | 0x00006000, + NEON_TBX_1v = NEON_TBL_1v | NEONTableExt, + NEON_TBX_2v = NEON_TBL_2v | NEONTableExt, + NEON_TBX_3v = NEON_TBL_3v | NEONTableExt, + NEON_TBX_4v = NEON_TBL_4v | NEONTableExt +}; + +// NEON perm. +enum NEONPermOp { + NEONPermFixed = 0x0E000800, + NEONPermFMask = 0xBF208C00, + NEONPermMask = 0x3F20FC00, + NEON_UZP1 = NEONPermFixed | 0x00001000, + NEON_TRN1 = NEONPermFixed | 0x00002000, + NEON_ZIP1 = NEONPermFixed | 0x00003000, + NEON_UZP2 = NEONPermFixed | 0x00005000, + NEON_TRN2 = NEONPermFixed | 0x00006000, + NEON_ZIP2 = NEONPermFixed | 0x00007000 +}; + +// NEON scalar instructions with two register operands. +enum NEONScalar2RegMiscOp { + NEONScalar2RegMiscFixed = 0x5E200800, + NEONScalar2RegMiscFMask = 0xDF3E0C00, + NEONScalar2RegMiscMask = NEON_Q | NEONScalar | NEON2RegMiscMask, + NEON_CMGT_zero_scalar = NEON_Q | NEONScalar | NEON_CMGT_zero, + NEON_CMEQ_zero_scalar = NEON_Q | NEONScalar | NEON_CMEQ_zero, + NEON_CMLT_zero_scalar = NEON_Q | NEONScalar | NEON_CMLT_zero, + NEON_CMGE_zero_scalar = NEON_Q | NEONScalar | NEON_CMGE_zero, + NEON_CMLE_zero_scalar = NEON_Q | NEONScalar | NEON_CMLE_zero, + NEON_ABS_scalar = NEON_Q | NEONScalar | NEON_ABS, + NEON_SQABS_scalar = NEON_Q | NEONScalar | NEON_SQABS, + NEON_NEG_scalar = NEON_Q | NEONScalar | NEON_NEG, + NEON_SQNEG_scalar = NEON_Q | NEONScalar | NEON_SQNEG, + NEON_SQXTN_scalar = NEON_Q | NEONScalar | NEON_SQXTN, + NEON_UQXTN_scalar = NEON_Q | NEONScalar | NEON_UQXTN, + NEON_SQXTUN_scalar = NEON_Q | NEONScalar | NEON_SQXTUN, + NEON_SUQADD_scalar = NEON_Q | NEONScalar | NEON_SUQADD, + NEON_USQADD_scalar = NEON_Q | NEONScalar | NEON_USQADD, + + NEONScalar2RegMiscOpcode = NEON2RegMiscOpcode, + NEON_NEG_scalar_opcode = NEON_NEG_scalar & NEONScalar2RegMiscOpcode, + + NEONScalar2RegMiscFPMask = NEONScalar2RegMiscMask | 0x00800000, + NEON_FRSQRTE_scalar = NEON_Q | NEONScalar | NEON_FRSQRTE, + NEON_FRECPE_scalar = NEON_Q | NEONScalar | NEON_FRECPE, + NEON_SCVTF_scalar = NEON_Q | NEONScalar | NEON_SCVTF, + NEON_UCVTF_scalar = NEON_Q | NEONScalar | NEON_UCVTF, + NEON_FCMGT_zero_scalar = NEON_Q | NEONScalar | NEON_FCMGT_zero, + NEON_FCMEQ_zero_scalar = NEON_Q | NEONScalar | NEON_FCMEQ_zero, + NEON_FCMLT_zero_scalar = NEON_Q | NEONScalar | NEON_FCMLT_zero, + NEON_FCMGE_zero_scalar = NEON_Q | NEONScalar | NEON_FCMGE_zero, + NEON_FCMLE_zero_scalar = NEON_Q | NEONScalar | NEON_FCMLE_zero, + NEON_FRECPX_scalar = NEONScalar2RegMiscFixed | 0x0081F000, + NEON_FCVTNS_scalar = NEON_Q | NEONScalar | NEON_FCVTNS, + NEON_FCVTNU_scalar = NEON_Q | NEONScalar | NEON_FCVTNU, + NEON_FCVTPS_scalar = NEON_Q | NEONScalar | NEON_FCVTPS, + NEON_FCVTPU_scalar = NEON_Q | NEONScalar | NEON_FCVTPU, + NEON_FCVTMS_scalar = NEON_Q | NEONScalar | NEON_FCVTMS, + NEON_FCVTMU_scalar = NEON_Q | NEONScalar | NEON_FCVTMU, + NEON_FCVTZS_scalar = NEON_Q | NEONScalar | NEON_FCVTZS, + NEON_FCVTZU_scalar = NEON_Q | NEONScalar | NEON_FCVTZU, + NEON_FCVTAS_scalar = NEON_Q | NEONScalar | NEON_FCVTAS, + NEON_FCVTAU_scalar = NEON_Q | NEONScalar | NEON_FCVTAU, + NEON_FCVTXN_scalar = NEON_Q | NEONScalar | NEON_FCVTXN +}; + +// NEON scalar instructions with three same-type operands. +enum NEONScalar3SameOp { + NEONScalar3SameFixed = 0x5E200400, + NEONScalar3SameFMask = 0xDF200400, + NEONScalar3SameMask = 0xFF20FC00, + NEON_ADD_scalar = NEON_Q | NEONScalar | NEON_ADD, + NEON_CMEQ_scalar = NEON_Q | NEONScalar | NEON_CMEQ, + NEON_CMGE_scalar = NEON_Q | NEONScalar | NEON_CMGE, + NEON_CMGT_scalar = NEON_Q | NEONScalar | NEON_CMGT, + NEON_CMHI_scalar = NEON_Q | NEONScalar | NEON_CMHI, + NEON_CMHS_scalar = NEON_Q | NEONScalar | NEON_CMHS, + NEON_CMTST_scalar = NEON_Q | NEONScalar | NEON_CMTST, + NEON_SUB_scalar = NEON_Q | NEONScalar | NEON_SUB, + NEON_UQADD_scalar = NEON_Q | NEONScalar | NEON_UQADD, + NEON_SQADD_scalar = NEON_Q | NEONScalar | NEON_SQADD, + NEON_UQSUB_scalar = NEON_Q | NEONScalar | NEON_UQSUB, + NEON_SQSUB_scalar = NEON_Q | NEONScalar | NEON_SQSUB, + NEON_USHL_scalar = NEON_Q | NEONScalar | NEON_USHL, + NEON_SSHL_scalar = NEON_Q | NEONScalar | NEON_SSHL, + NEON_UQSHL_scalar = NEON_Q | NEONScalar | NEON_UQSHL, + NEON_SQSHL_scalar = NEON_Q | NEONScalar | NEON_SQSHL, + NEON_URSHL_scalar = NEON_Q | NEONScalar | NEON_URSHL, + NEON_SRSHL_scalar = NEON_Q | NEONScalar | NEON_SRSHL, + NEON_UQRSHL_scalar = NEON_Q | NEONScalar | NEON_UQRSHL, + NEON_SQRSHL_scalar = NEON_Q | NEONScalar | NEON_SQRSHL, + NEON_SQDMULH_scalar = NEON_Q | NEONScalar | NEON_SQDMULH, + NEON_SQRDMULH_scalar = NEON_Q | NEONScalar | NEON_SQRDMULH, + + // NEON floating point scalar instructions with three same-type operands. + NEONScalar3SameFPFixed = NEONScalar3SameFixed | 0x0000C000, + NEONScalar3SameFPFMask = NEONScalar3SameFMask | 0x0000C000, + NEONScalar3SameFPMask = NEONScalar3SameMask | 0x00800000, + NEON_FACGE_scalar = NEON_Q | NEONScalar | NEON_FACGE, + NEON_FACGT_scalar = NEON_Q | NEONScalar | NEON_FACGT, + NEON_FCMEQ_scalar = NEON_Q | NEONScalar | NEON_FCMEQ, + NEON_FCMGE_scalar = NEON_Q | NEONScalar | NEON_FCMGE, + NEON_FCMGT_scalar = NEON_Q | NEONScalar | NEON_FCMGT, + NEON_FMULX_scalar = NEON_Q | NEONScalar | NEON_FMULX, + NEON_FRECPS_scalar = NEON_Q | NEONScalar | NEON_FRECPS, + NEON_FRSQRTS_scalar = NEON_Q | NEONScalar | NEON_FRSQRTS, + NEON_FABD_scalar = NEON_Q | NEONScalar | NEON_FABD +}; + +// NEON scalar instructions with three different-type operands. +enum NEONScalar3DiffOp { + NEONScalar3DiffFixed = 0x5E200000, + NEONScalar3DiffFMask = 0xDF200C00, + NEONScalar3DiffMask = NEON_Q | NEONScalar | NEON3DifferentMask, + NEON_SQDMLAL_scalar = NEON_Q | NEONScalar | NEON_SQDMLAL, + NEON_SQDMLSL_scalar = NEON_Q | NEONScalar | NEON_SQDMLSL, + NEON_SQDMULL_scalar = NEON_Q | NEONScalar | NEON_SQDMULL +}; + +// NEON scalar instructions with indexed element operand. +enum NEONScalarByIndexedElementOp { + NEONScalarByIndexedElementFixed = 0x5F000000, + NEONScalarByIndexedElementFMask = 0xDF000400, + NEONScalarByIndexedElementMask = 0xFF00F400, + NEON_SQDMLAL_byelement_scalar = NEON_Q | NEONScalar | NEON_SQDMLAL_byelement, + NEON_SQDMLSL_byelement_scalar = NEON_Q | NEONScalar | NEON_SQDMLSL_byelement, + NEON_SQDMULL_byelement_scalar = NEON_Q | NEONScalar | NEON_SQDMULL_byelement, + NEON_SQDMULH_byelement_scalar = NEON_Q | NEONScalar | NEON_SQDMULH_byelement, + NEON_SQRDMULH_byelement_scalar + = NEON_Q | NEONScalar | NEON_SQRDMULH_byelement, + + // Floating point instructions. + NEONScalarByIndexedElementFPFixed + = NEONScalarByIndexedElementFixed | 0x00800000, + NEONScalarByIndexedElementFPMask + = NEONScalarByIndexedElementMask | 0x00800000, + NEON_FMLA_byelement_scalar = NEON_Q | NEONScalar | NEON_FMLA_byelement, + NEON_FMLS_byelement_scalar = NEON_Q | NEONScalar | NEON_FMLS_byelement, + NEON_FMUL_byelement_scalar = NEON_Q | NEONScalar | NEON_FMUL_byelement, + NEON_FMULX_byelement_scalar = NEON_Q | NEONScalar | NEON_FMULX_byelement +}; + +// NEON scalar register copy. +enum NEONScalarCopyOp { + NEONScalarCopyFixed = 0x5E000400, + NEONScalarCopyFMask = 0xDFE08400, + NEONScalarCopyMask = 0xFFE0FC00, + NEON_DUP_ELEMENT_scalar = NEON_Q | NEONScalar | NEON_DUP_ELEMENT +}; + +// NEON scalar pairwise instructions. +enum NEONScalarPairwiseOp { + NEONScalarPairwiseFixed = 0x5E300800, + NEONScalarPairwiseFMask = 0xDF3E0C00, + NEONScalarPairwiseMask = 0xFFB1F800, + NEON_ADDP_scalar = NEONScalarPairwiseFixed | 0x0081B000, + NEON_FMAXNMP_scalar = NEONScalarPairwiseFixed | 0x2000C000, + NEON_FMINNMP_scalar = NEONScalarPairwiseFixed | 0x2080C000, + NEON_FADDP_scalar = NEONScalarPairwiseFixed | 0x2000D000, + NEON_FMAXP_scalar = NEONScalarPairwiseFixed | 0x2000F000, + NEON_FMINP_scalar = NEONScalarPairwiseFixed | 0x2080F000 +}; + +// NEON scalar shift immediate. +enum NEONScalarShiftImmediateOp { + NEONScalarShiftImmediateFixed = 0x5F000400, + NEONScalarShiftImmediateFMask = 0xDF800400, + NEONScalarShiftImmediateMask = 0xFF80FC00, + NEON_SHL_scalar = NEON_Q | NEONScalar | NEON_SHL, + NEON_SLI_scalar = NEON_Q | NEONScalar | NEON_SLI, + NEON_SRI_scalar = NEON_Q | NEONScalar | NEON_SRI, + NEON_SSHR_scalar = NEON_Q | NEONScalar | NEON_SSHR, + NEON_USHR_scalar = NEON_Q | NEONScalar | NEON_USHR, + NEON_SRSHR_scalar = NEON_Q | NEONScalar | NEON_SRSHR, + NEON_URSHR_scalar = NEON_Q | NEONScalar | NEON_URSHR, + NEON_SSRA_scalar = NEON_Q | NEONScalar | NEON_SSRA, + NEON_USRA_scalar = NEON_Q | NEONScalar | NEON_USRA, + NEON_SRSRA_scalar = NEON_Q | NEONScalar | NEON_SRSRA, + NEON_URSRA_scalar = NEON_Q | NEONScalar | NEON_URSRA, + NEON_UQSHRN_scalar = NEON_Q | NEONScalar | NEON_UQSHRN, + NEON_UQRSHRN_scalar = NEON_Q | NEONScalar | NEON_UQRSHRN, + NEON_SQSHRN_scalar = NEON_Q | NEONScalar | NEON_SQSHRN, + NEON_SQRSHRN_scalar = NEON_Q | NEONScalar | NEON_SQRSHRN, + NEON_SQSHRUN_scalar = NEON_Q | NEONScalar | NEON_SQSHRUN, + NEON_SQRSHRUN_scalar = NEON_Q | NEONScalar | NEON_SQRSHRUN, + NEON_SQSHLU_scalar = NEON_Q | NEONScalar | NEON_SQSHLU, + NEON_SQSHL_imm_scalar = NEON_Q | NEONScalar | NEON_SQSHL_imm, + NEON_UQSHL_imm_scalar = NEON_Q | NEONScalar | NEON_UQSHL_imm, + NEON_SCVTF_imm_scalar = NEON_Q | NEONScalar | NEON_SCVTF_imm, + NEON_UCVTF_imm_scalar = NEON_Q | NEONScalar | NEON_UCVTF_imm, + NEON_FCVTZS_imm_scalar = NEON_Q | NEONScalar | NEON_FCVTZS_imm, + NEON_FCVTZU_imm_scalar = NEON_Q | NEONScalar | NEON_FCVTZU_imm +}; + +// Unimplemented and unallocated instructions. These are defined to make fixed +// bit assertion easier. +enum UnimplementedOp { + UnimplementedFixed = 0x00000000, + UnimplementedFMask = 0x00000000 +}; + +enum UnallocatedOp { + UnallocatedFixed = 0x00000000, + UnallocatedFMask = 0x00000000 +}; + +} // namespace vixl + +#endif // VIXL_A64_CONSTANTS_A64_H_ diff --git a/qemu/disas/libvixl/vixl/a64/cpu-a64.h b/qemu/disas/libvixl/vixl/a64/cpu-a64.h new file mode 100644 index 000000000..cdf09a6af --- /dev/null +++ b/qemu/disas/libvixl/vixl/a64/cpu-a64.h @@ -0,0 +1,83 @@ +// Copyright 2014, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_CPU_A64_H +#define VIXL_CPU_A64_H + +#include "vixl/globals.h" +#include "vixl/a64/instructions-a64.h" + +namespace vixl { + +class CPU { + public: + // Initialise CPU support. + static void SetUp(); + + // Ensures the data at a given address and with a given size is the same for + // the I and D caches. I and D caches are not automatically coherent on ARM + // so this operation is required before any dynamically generated code can + // safely run. + static void EnsureIAndDCacheCoherency(void *address, size_t length); + + // Handle tagged pointers. + template + static T SetPointerTag(T pointer, uint64_t tag) { + VIXL_ASSERT(is_uintn(kAddressTagWidth, tag)); + + // Use C-style casts to get static_cast behaviour for integral types (T), + // and reinterpret_cast behaviour for other types. + + uint64_t raw = (uint64_t)pointer; + VIXL_STATIC_ASSERT(sizeof(pointer) == sizeof(raw)); + + raw = (raw & ~kAddressTagMask) | (tag << kAddressTagOffset); + return (T)raw; + } + + template + static uint64_t GetPointerTag(T pointer) { + // Use C-style casts to get static_cast behaviour for integral types (T), + // and reinterpret_cast behaviour for other types. + + uint64_t raw = (uint64_t)pointer; + VIXL_STATIC_ASSERT(sizeof(pointer) == sizeof(raw)); + + return (raw & kAddressTagMask) >> kAddressTagOffset; + } + + private: + // Return the content of the cache type register. + static uint32_t GetCacheType(); + + // I and D cache line size in bytes. + static unsigned icache_line_size_; + static unsigned dcache_line_size_; +}; + +} // namespace vixl + +#endif // VIXL_CPU_A64_H diff --git a/qemu/disas/libvixl/vixl/a64/decoder-a64.cc b/qemu/disas/libvixl/vixl/a64/decoder-a64.cc new file mode 100644 index 000000000..5ba2d3ce0 --- /dev/null +++ b/qemu/disas/libvixl/vixl/a64/decoder-a64.cc @@ -0,0 +1,877 @@ +// Copyright 2014, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "vixl/globals.h" +#include "vixl/utils.h" +#include "vixl/a64/decoder-a64.h" + +namespace vixl { + +void Decoder::DecodeInstruction(const Instruction *instr) { + if (instr->Bits(28, 27) == 0) { + VisitUnallocated(instr); + } else { + switch (instr->Bits(27, 24)) { + // 0: PC relative addressing. + case 0x0: DecodePCRelAddressing(instr); break; + + // 1: Add/sub immediate. + case 0x1: DecodeAddSubImmediate(instr); break; + + // A: Logical shifted register. + // Add/sub with carry. + // Conditional compare register. + // Conditional compare immediate. + // Conditional select. + // Data processing 1 source. + // Data processing 2 source. + // B: Add/sub shifted register. + // Add/sub extended register. + // Data processing 3 source. + case 0xA: + case 0xB: DecodeDataProcessing(instr); break; + + // 2: Logical immediate. + // Move wide immediate. + case 0x2: DecodeLogical(instr); break; + + // 3: Bitfield. + // Extract. + case 0x3: DecodeBitfieldExtract(instr); break; + + // 4: Unconditional branch immediate. + // Exception generation. + // Compare and branch immediate. + // 5: Compare and branch immediate. + // Conditional branch. + // System. + // 6,7: Unconditional branch. + // Test and branch immediate. + case 0x4: + case 0x5: + case 0x6: + case 0x7: DecodeBranchSystemException(instr); break; + + // 8,9: Load/store register pair post-index. + // Load register literal. + // Load/store register unscaled immediate. + // Load/store register immediate post-index. + // Load/store register immediate pre-index. + // Load/store register offset. + // Load/store exclusive. + // C,D: Load/store register pair offset. + // Load/store register pair pre-index. + // Load/store register unsigned immediate. + // Advanced SIMD. + case 0x8: + case 0x9: + case 0xC: + case 0xD: DecodeLoadStore(instr); break; + + // E: FP fixed point conversion. + // FP integer conversion. + // FP data processing 1 source. + // FP compare. + // FP immediate. + // FP data processing 2 source. + // FP conditional compare. + // FP conditional select. + // Advanced SIMD. + // F: FP data processing 3 source. + // Advanced SIMD. + case 0xE: + case 0xF: DecodeFP(instr); break; + } + } +} + +void Decoder::AppendVisitor(DecoderVisitor* new_visitor) { + visitors_.push_back(new_visitor); +} + + +void Decoder::PrependVisitor(DecoderVisitor* new_visitor) { + visitors_.push_front(new_visitor); +} + + +void Decoder::InsertVisitorBefore(DecoderVisitor* new_visitor, + DecoderVisitor* registered_visitor) { + std::list::iterator it; + for (it = visitors_.begin(); it != visitors_.end(); it++) { + if (*it == registered_visitor) { + visitors_.insert(it, new_visitor); + return; + } + } + // We reached the end of the list. The last element must be + // registered_visitor. + VIXL_ASSERT(*it == registered_visitor); + visitors_.insert(it, new_visitor); +} + + +void Decoder::InsertVisitorAfter(DecoderVisitor* new_visitor, + DecoderVisitor* registered_visitor) { + std::list::iterator it; + for (it = visitors_.begin(); it != visitors_.end(); it++) { + if (*it == registered_visitor) { + it++; + visitors_.insert(it, new_visitor); + return; + } + } + // We reached the end of the list. The last element must be + // registered_visitor. + VIXL_ASSERT(*it == registered_visitor); + visitors_.push_back(new_visitor); +} + + +void Decoder::RemoveVisitor(DecoderVisitor* visitor) { + visitors_.remove(visitor); +} + + +void Decoder::DecodePCRelAddressing(const Instruction* instr) { + VIXL_ASSERT(instr->Bits(27, 24) == 0x0); + // We know bit 28 is set, as = 0 is filtered out at the top level + // decode. + VIXL_ASSERT(instr->Bit(28) == 0x1); + VisitPCRelAddressing(instr); +} + + +void Decoder::DecodeBranchSystemException(const Instruction* instr) { + VIXL_ASSERT((instr->Bits(27, 24) == 0x4) || + (instr->Bits(27, 24) == 0x5) || + (instr->Bits(27, 24) == 0x6) || + (instr->Bits(27, 24) == 0x7) ); + + switch (instr->Bits(31, 29)) { + case 0: + case 4: { + VisitUnconditionalBranch(instr); + break; + } + case 1: + case 5: { + if (instr->Bit(25) == 0) { + VisitCompareBranch(instr); + } else { + VisitTestBranch(instr); + } + break; + } + case 2: { + if (instr->Bit(25) == 0) { + if ((instr->Bit(24) == 0x1) || + (instr->Mask(0x01000010) == 0x00000010)) { + VisitUnallocated(instr); + } else { + VisitConditionalBranch(instr); + } + } else { + VisitUnallocated(instr); + } + break; + } + case 6: { + if (instr->Bit(25) == 0) { + if (instr->Bit(24) == 0) { + if ((instr->Bits(4, 2) != 0) || + (instr->Mask(0x00E0001D) == 0x00200001) || + (instr->Mask(0x00E0001D) == 0x00400001) || + (instr->Mask(0x00E0001E) == 0x00200002) || + (instr->Mask(0x00E0001E) == 0x00400002) || + (instr->Mask(0x00E0001C) == 0x00600000) || + (instr->Mask(0x00E0001C) == 0x00800000) || + (instr->Mask(0x00E0001F) == 0x00A00000) || + (instr->Mask(0x00C0001C) == 0x00C00000)) { + VisitUnallocated(instr); + } else { + VisitException(instr); + } + } else { + if (instr->Bits(23, 22) == 0) { + const Instr masked_003FF0E0 = instr->Mask(0x003FF0E0); + if ((instr->Bits(21, 19) == 0x4) || + (masked_003FF0E0 == 0x00033000) || + (masked_003FF0E0 == 0x003FF020) || + (masked_003FF0E0 == 0x003FF060) || + (masked_003FF0E0 == 0x003FF0E0) || + (instr->Mask(0x00388000) == 0x00008000) || + (instr->Mask(0x0038E000) == 0x00000000) || + (instr->Mask(0x0039E000) == 0x00002000) || + (instr->Mask(0x003AE000) == 0x00002000) || + (instr->Mask(0x003CE000) == 0x00042000) || + (instr->Mask(0x003FFFC0) == 0x000320C0) || + (instr->Mask(0x003FF100) == 0x00032100) || + (instr->Mask(0x003FF200) == 0x00032200) || + (instr->Mask(0x003FF400) == 0x00032400) || + (instr->Mask(0x003FF800) == 0x00032800) || + (instr->Mask(0x0038F000) == 0x00005000) || + (instr->Mask(0x0038E000) == 0x00006000)) { + VisitUnallocated(instr); + } else { + VisitSystem(instr); + } + } else { + VisitUnallocated(instr); + } + } + } else { + if ((instr->Bit(24) == 0x1) || + (instr->Bits(20, 16) != 0x1F) || + (instr->Bits(15, 10) != 0) || + (instr->Bits(4, 0) != 0) || + (instr->Bits(24, 21) == 0x3) || + (instr->Bits(24, 22) == 0x3)) { + VisitUnallocated(instr); + } else { + VisitUnconditionalBranchToRegister(instr); + } + } + break; + } + case 3: + case 7: { + VisitUnallocated(instr); + break; + } + } +} + + +void Decoder::DecodeLoadStore(const Instruction* instr) { + VIXL_ASSERT((instr->Bits(27, 24) == 0x8) || + (instr->Bits(27, 24) == 0x9) || + (instr->Bits(27, 24) == 0xC) || + (instr->Bits(27, 24) == 0xD) ); + // TODO(all): rearrange the tree to integrate this branch. + if ((instr->Bit(28) == 0) && (instr->Bit(29) == 0) && (instr->Bit(26) == 1)) { + DecodeNEONLoadStore(instr); + return; + } + + if (instr->Bit(24) == 0) { + if (instr->Bit(28) == 0) { + if (instr->Bit(29) == 0) { + if (instr->Bit(26) == 0) { + VisitLoadStoreExclusive(instr); + } else { + VIXL_UNREACHABLE(); + } + } else { + if ((instr->Bits(31, 30) == 0x3) || + (instr->Mask(0xC4400000) == 0x40000000)) { + VisitUnallocated(instr); + } else { + if (instr->Bit(23) == 0) { + if (instr->Mask(0xC4400000) == 0xC0400000) { + VisitUnallocated(instr); + } else { + VisitLoadStorePairNonTemporal(instr); + } + } else { + VisitLoadStorePairPostIndex(instr); + } + } + } + } else { + if (instr->Bit(29) == 0) { + if (instr->Mask(0xC4000000) == 0xC4000000) { + VisitUnallocated(instr); + } else { + VisitLoadLiteral(instr); + } + } else { + if ((instr->Mask(0x84C00000) == 0x80C00000) || + (instr->Mask(0x44800000) == 0x44800000) || + (instr->Mask(0x84800000) == 0x84800000)) { + VisitUnallocated(instr); + } else { + if (instr->Bit(21) == 0) { + switch (instr->Bits(11, 10)) { + case 0: { + VisitLoadStoreUnscaledOffset(instr); + break; + } + case 1: { + if (instr->Mask(0xC4C00000) == 0xC0800000) { + VisitUnallocated(instr); + } else { + VisitLoadStorePostIndex(instr); + } + break; + } + case 2: { + // TODO: VisitLoadStoreRegisterOffsetUnpriv. + VisitUnimplemented(instr); + break; + } + case 3: { + if (instr->Mask(0xC4C00000) == 0xC0800000) { + VisitUnallocated(instr); + } else { + VisitLoadStorePreIndex(instr); + } + break; + } + } + } else { + if (instr->Bits(11, 10) == 0x2) { + if (instr->Bit(14) == 0) { + VisitUnallocated(instr); + } else { + VisitLoadStoreRegisterOffset(instr); + } + } else { + VisitUnallocated(instr); + } + } + } + } + } + } else { + if (instr->Bit(28) == 0) { + if (instr->Bit(29) == 0) { + VisitUnallocated(instr); + } else { + if ((instr->Bits(31, 30) == 0x3) || + (instr->Mask(0xC4400000) == 0x40000000)) { + VisitUnallocated(instr); + } else { + if (instr->Bit(23) == 0) { + VisitLoadStorePairOffset(instr); + } else { + VisitLoadStorePairPreIndex(instr); + } + } + } + } else { + if (instr->Bit(29) == 0) { + VisitUnallocated(instr); + } else { + if ((instr->Mask(0x84C00000) == 0x80C00000) || + (instr->Mask(0x44800000) == 0x44800000) || + (instr->Mask(0x84800000) == 0x84800000)) { + VisitUnallocated(instr); + } else { + VisitLoadStoreUnsignedOffset(instr); + } + } + } + } +} + + +void Decoder::DecodeLogical(const Instruction* instr) { + VIXL_ASSERT(instr->Bits(27, 24) == 0x2); + + if (instr->Mask(0x80400000) == 0x00400000) { + VisitUnallocated(instr); + } else { + if (instr->Bit(23) == 0) { + VisitLogicalImmediate(instr); + } else { + if (instr->Bits(30, 29) == 0x1) { + VisitUnallocated(instr); + } else { + VisitMoveWideImmediate(instr); + } + } + } +} + + +void Decoder::DecodeBitfieldExtract(const Instruction* instr) { + VIXL_ASSERT(instr->Bits(27, 24) == 0x3); + + if ((instr->Mask(0x80400000) == 0x80000000) || + (instr->Mask(0x80400000) == 0x00400000) || + (instr->Mask(0x80008000) == 0x00008000)) { + VisitUnallocated(instr); + } else if (instr->Bit(23) == 0) { + if ((instr->Mask(0x80200000) == 0x00200000) || + (instr->Mask(0x60000000) == 0x60000000)) { + VisitUnallocated(instr); + } else { + VisitBitfield(instr); + } + } else { + if ((instr->Mask(0x60200000) == 0x00200000) || + (instr->Mask(0x60000000) != 0x00000000)) { + VisitUnallocated(instr); + } else { + VisitExtract(instr); + } + } +} + + +void Decoder::DecodeAddSubImmediate(const Instruction* instr) { + VIXL_ASSERT(instr->Bits(27, 24) == 0x1); + if (instr->Bit(23) == 1) { + VisitUnallocated(instr); + } else { + VisitAddSubImmediate(instr); + } +} + + +void Decoder::DecodeDataProcessing(const Instruction* instr) { + VIXL_ASSERT((instr->Bits(27, 24) == 0xA) || + (instr->Bits(27, 24) == 0xB)); + + if (instr->Bit(24) == 0) { + if (instr->Bit(28) == 0) { + if (instr->Mask(0x80008000) == 0x00008000) { + VisitUnallocated(instr); + } else { + VisitLogicalShifted(instr); + } + } else { + switch (instr->Bits(23, 21)) { + case 0: { + if (instr->Mask(0x0000FC00) != 0) { + VisitUnallocated(instr); + } else { + VisitAddSubWithCarry(instr); + } + break; + } + case 2: { + if ((instr->Bit(29) == 0) || + (instr->Mask(0x00000410) != 0)) { + VisitUnallocated(instr); + } else { + if (instr->Bit(11) == 0) { + VisitConditionalCompareRegister(instr); + } else { + VisitConditionalCompareImmediate(instr); + } + } + break; + } + case 4: { + if (instr->Mask(0x20000800) != 0x00000000) { + VisitUnallocated(instr); + } else { + VisitConditionalSelect(instr); + } + break; + } + case 6: { + if (instr->Bit(29) == 0x1) { + VisitUnallocated(instr); + VIXL_FALLTHROUGH(); + } else { + if (instr->Bit(30) == 0) { + if ((instr->Bit(15) == 0x1) || + (instr->Bits(15, 11) == 0) || + (instr->Bits(15, 12) == 0x1) || + (instr->Bits(15, 12) == 0x3) || + (instr->Bits(15, 13) == 0x3) || + (instr->Mask(0x8000EC00) == 0x00004C00) || + (instr->Mask(0x8000E800) == 0x80004000) || + (instr->Mask(0x8000E400) == 0x80004000)) { + VisitUnallocated(instr); + } else { + VisitDataProcessing2Source(instr); + } + } else { + if ((instr->Bit(13) == 1) || + (instr->Bits(20, 16) != 0) || + (instr->Bits(15, 14) != 0) || + (instr->Mask(0xA01FFC00) == 0x00000C00) || + (instr->Mask(0x201FF800) == 0x00001800)) { + VisitUnallocated(instr); + } else { + VisitDataProcessing1Source(instr); + } + } + break; + } + } + case 1: + case 3: + case 5: + case 7: VisitUnallocated(instr); break; + } + } + } else { + if (instr->Bit(28) == 0) { + if (instr->Bit(21) == 0) { + if ((instr->Bits(23, 22) == 0x3) || + (instr->Mask(0x80008000) == 0x00008000)) { + VisitUnallocated(instr); + } else { + VisitAddSubShifted(instr); + } + } else { + if ((instr->Mask(0x00C00000) != 0x00000000) || + (instr->Mask(0x00001400) == 0x00001400) || + (instr->Mask(0x00001800) == 0x00001800)) { + VisitUnallocated(instr); + } else { + VisitAddSubExtended(instr); + } + } + } else { + if ((instr->Bit(30) == 0x1) || + (instr->Bits(30, 29) == 0x1) || + (instr->Mask(0xE0600000) == 0x00200000) || + (instr->Mask(0xE0608000) == 0x00400000) || + (instr->Mask(0x60608000) == 0x00408000) || + (instr->Mask(0x60E00000) == 0x00E00000) || + (instr->Mask(0x60E00000) == 0x00800000) || + (instr->Mask(0x60E00000) == 0x00600000)) { + VisitUnallocated(instr); + } else { + VisitDataProcessing3Source(instr); + } + } + } +} + + +void Decoder::DecodeFP(const Instruction* instr) { + VIXL_ASSERT((instr->Bits(27, 24) == 0xE) || + (instr->Bits(27, 24) == 0xF)); + if (instr->Bit(28) == 0) { + DecodeNEONVectorDataProcessing(instr); + } else { + if (instr->Bits(31, 30) == 0x3) { + VisitUnallocated(instr); + } else if (instr->Bits(31, 30) == 0x1) { + DecodeNEONScalarDataProcessing(instr); + } else { + if (instr->Bit(29) == 0) { + if (instr->Bit(24) == 0) { + if (instr->Bit(21) == 0) { + if ((instr->Bit(23) == 1) || + (instr->Bit(18) == 1) || + (instr->Mask(0x80008000) == 0x00000000) || + (instr->Mask(0x000E0000) == 0x00000000) || + (instr->Mask(0x000E0000) == 0x000A0000) || + (instr->Mask(0x00160000) == 0x00000000) || + (instr->Mask(0x00160000) == 0x00120000)) { + VisitUnallocated(instr); + } else { + VisitFPFixedPointConvert(instr); + } + } else { + if (instr->Bits(15, 10) == 32) { + VisitUnallocated(instr); + } else if (instr->Bits(15, 10) == 0) { + if ((instr->Bits(23, 22) == 0x3) || + (instr->Mask(0x000E0000) == 0x000A0000) || + (instr->Mask(0x000E0000) == 0x000C0000) || + (instr->Mask(0x00160000) == 0x00120000) || + (instr->Mask(0x00160000) == 0x00140000) || + (instr->Mask(0x20C40000) == 0x00800000) || + (instr->Mask(0x20C60000) == 0x00840000) || + (instr->Mask(0xA0C60000) == 0x80060000) || + (instr->Mask(0xA0C60000) == 0x00860000) || + (instr->Mask(0xA0C60000) == 0x00460000) || + (instr->Mask(0xA0CE0000) == 0x80860000) || + (instr->Mask(0xA0CE0000) == 0x804E0000) || + (instr->Mask(0xA0CE0000) == 0x000E0000) || + (instr->Mask(0xA0D60000) == 0x00160000) || + (instr->Mask(0xA0D60000) == 0x80560000) || + (instr->Mask(0xA0D60000) == 0x80960000)) { + VisitUnallocated(instr); + } else { + VisitFPIntegerConvert(instr); + } + } else if (instr->Bits(14, 10) == 16) { + const Instr masked_A0DF8000 = instr->Mask(0xA0DF8000); + if ((instr->Mask(0x80180000) != 0) || + (masked_A0DF8000 == 0x00020000) || + (masked_A0DF8000 == 0x00030000) || + (masked_A0DF8000 == 0x00068000) || + (masked_A0DF8000 == 0x00428000) || + (masked_A0DF8000 == 0x00430000) || + (masked_A0DF8000 == 0x00468000) || + (instr->Mask(0xA0D80000) == 0x00800000) || + (instr->Mask(0xA0DE0000) == 0x00C00000) || + (instr->Mask(0xA0DF0000) == 0x00C30000) || + (instr->Mask(0xA0DC0000) == 0x00C40000)) { + VisitUnallocated(instr); + } else { + VisitFPDataProcessing1Source(instr); + } + } else if (instr->Bits(13, 10) == 8) { + if ((instr->Bits(15, 14) != 0) || + (instr->Bits(2, 0) != 0) || + (instr->Mask(0x80800000) != 0x00000000)) { + VisitUnallocated(instr); + } else { + VisitFPCompare(instr); + } + } else if (instr->Bits(12, 10) == 4) { + if ((instr->Bits(9, 5) != 0) || + (instr->Mask(0x80800000) != 0x00000000)) { + VisitUnallocated(instr); + } else { + VisitFPImmediate(instr); + } + } else { + if (instr->Mask(0x80800000) != 0x00000000) { + VisitUnallocated(instr); + } else { + switch (instr->Bits(11, 10)) { + case 1: { + VisitFPConditionalCompare(instr); + break; + } + case 2: { + if ((instr->Bits(15, 14) == 0x3) || + (instr->Mask(0x00009000) == 0x00009000) || + (instr->Mask(0x0000A000) == 0x0000A000)) { + VisitUnallocated(instr); + } else { + VisitFPDataProcessing2Source(instr); + } + break; + } + case 3: { + VisitFPConditionalSelect(instr); + break; + } + default: VIXL_UNREACHABLE(); + } + } + } + } + } else { + // Bit 30 == 1 has been handled earlier. + VIXL_ASSERT(instr->Bit(30) == 0); + if (instr->Mask(0xA0800000) != 0) { + VisitUnallocated(instr); + } else { + VisitFPDataProcessing3Source(instr); + } + } + } else { + VisitUnallocated(instr); + } + } + } +} + + +void Decoder::DecodeNEONLoadStore(const Instruction* instr) { + VIXL_ASSERT(instr->Bits(29, 25) == 0x6); + if (instr->Bit(31) == 0) { + if ((instr->Bit(24) == 0) && (instr->Bit(21) == 1)) { + VisitUnallocated(instr); + return; + } + + if (instr->Bit(23) == 0) { + if (instr->Bits(20, 16) == 0) { + if (instr->Bit(24) == 0) { + VisitNEONLoadStoreMultiStruct(instr); + } else { + VisitNEONLoadStoreSingleStruct(instr); + } + } else { + VisitUnallocated(instr); + } + } else { + if (instr->Bit(24) == 0) { + VisitNEONLoadStoreMultiStructPostIndex(instr); + } else { + VisitNEONLoadStoreSingleStructPostIndex(instr); + } + } + } else { + VisitUnallocated(instr); + } +} + + +void Decoder::DecodeNEONVectorDataProcessing(const Instruction* instr) { + VIXL_ASSERT(instr->Bits(28, 25) == 0x7); + if (instr->Bit(31) == 0) { + if (instr->Bit(24) == 0) { + if (instr->Bit(21) == 0) { + if (instr->Bit(15) == 0) { + if (instr->Bit(10) == 0) { + if (instr->Bit(29) == 0) { + if (instr->Bit(11) == 0) { + VisitNEONTable(instr); + } else { + VisitNEONPerm(instr); + } + } else { + VisitNEONExtract(instr); + } + } else { + if (instr->Bits(23, 22) == 0) { + VisitNEONCopy(instr); + } else { + VisitUnallocated(instr); + } + } + } else { + VisitUnallocated(instr); + } + } else { + if (instr->Bit(10) == 0) { + if (instr->Bit(11) == 0) { + VisitNEON3Different(instr); + } else { + if (instr->Bits(18, 17) == 0) { + if (instr->Bit(20) == 0) { + if (instr->Bit(19) == 0) { + VisitNEON2RegMisc(instr); + } else { + if (instr->Bits(30, 29) == 0x2) { + VisitCryptoAES(instr); + } else { + VisitUnallocated(instr); + } + } + } else { + if (instr->Bit(19) == 0) { + VisitNEONAcrossLanes(instr); + } else { + VisitUnallocated(instr); + } + } + } else { + VisitUnallocated(instr); + } + } + } else { + VisitNEON3Same(instr); + } + } + } else { + if (instr->Bit(10) == 0) { + VisitNEONByIndexedElement(instr); + } else { + if (instr->Bit(23) == 0) { + if (instr->Bits(22, 19) == 0) { + VisitNEONModifiedImmediate(instr); + } else { + VisitNEONShiftImmediate(instr); + } + } else { + VisitUnallocated(instr); + } + } + } + } else { + VisitUnallocated(instr); + } +} + + +void Decoder::DecodeNEONScalarDataProcessing(const Instruction* instr) { + VIXL_ASSERT(instr->Bits(28, 25) == 0xF); + if (instr->Bit(24) == 0) { + if (instr->Bit(21) == 0) { + if (instr->Bit(15) == 0) { + if (instr->Bit(10) == 0) { + if (instr->Bit(29) == 0) { + if (instr->Bit(11) == 0) { + VisitCrypto3RegSHA(instr); + } else { + VisitUnallocated(instr); + } + } else { + VisitUnallocated(instr); + } + } else { + if (instr->Bits(23, 22) == 0) { + VisitNEONScalarCopy(instr); + } else { + VisitUnallocated(instr); + } + } + } else { + VisitUnallocated(instr); + } + } else { + if (instr->Bit(10) == 0) { + if (instr->Bit(11) == 0) { + VisitNEONScalar3Diff(instr); + } else { + if (instr->Bits(18, 17) == 0) { + if (instr->Bit(20) == 0) { + if (instr->Bit(19) == 0) { + VisitNEONScalar2RegMisc(instr); + } else { + if (instr->Bit(29) == 0) { + VisitCrypto2RegSHA(instr); + } else { + VisitUnallocated(instr); + } + } + } else { + if (instr->Bit(19) == 0) { + VisitNEONScalarPairwise(instr); + } else { + VisitUnallocated(instr); + } + } + } else { + VisitUnallocated(instr); + } + } + } else { + VisitNEONScalar3Same(instr); + } + } + } else { + if (instr->Bit(10) == 0) { + VisitNEONScalarByIndexedElement(instr); + } else { + if (instr->Bit(23) == 0) { + VisitNEONScalarShiftImmediate(instr); + } else { + VisitUnallocated(instr); + } + } + } +} + + +#define DEFINE_VISITOR_CALLERS(A) \ + void Decoder::Visit##A(const Instruction *instr) { \ + VIXL_ASSERT(instr->Mask(A##FMask) == A##Fixed); \ + std::list::iterator it; \ + for (it = visitors_.begin(); it != visitors_.end(); it++) { \ + (*it)->Visit##A(instr); \ + } \ + } +VISITOR_LIST(DEFINE_VISITOR_CALLERS) +#undef DEFINE_VISITOR_CALLERS +} // namespace vixl diff --git a/qemu/disas/libvixl/vixl/a64/decoder-a64.h b/qemu/disas/libvixl/vixl/a64/decoder-a64.h new file mode 100644 index 000000000..b3f04f68f --- /dev/null +++ b/qemu/disas/libvixl/vixl/a64/decoder-a64.h @@ -0,0 +1,275 @@ +// Copyright 2014, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_A64_DECODER_A64_H_ +#define VIXL_A64_DECODER_A64_H_ + +#include + +#include "vixl/globals.h" +#include "vixl/a64/instructions-a64.h" + + +// List macro containing all visitors needed by the decoder class. + +#define VISITOR_LIST_THAT_RETURN(V) \ + V(PCRelAddressing) \ + V(AddSubImmediate) \ + V(LogicalImmediate) \ + V(MoveWideImmediate) \ + V(Bitfield) \ + V(Extract) \ + V(UnconditionalBranch) \ + V(UnconditionalBranchToRegister) \ + V(CompareBranch) \ + V(TestBranch) \ + V(ConditionalBranch) \ + V(System) \ + V(Exception) \ + V(LoadStorePairPostIndex) \ + V(LoadStorePairOffset) \ + V(LoadStorePairPreIndex) \ + V(LoadStorePairNonTemporal) \ + V(LoadLiteral) \ + V(LoadStoreUnscaledOffset) \ + V(LoadStorePostIndex) \ + V(LoadStorePreIndex) \ + V(LoadStoreRegisterOffset) \ + V(LoadStoreUnsignedOffset) \ + V(LoadStoreExclusive) \ + V(LogicalShifted) \ + V(AddSubShifted) \ + V(AddSubExtended) \ + V(AddSubWithCarry) \ + V(ConditionalCompareRegister) \ + V(ConditionalCompareImmediate) \ + V(ConditionalSelect) \ + V(DataProcessing1Source) \ + V(DataProcessing2Source) \ + V(DataProcessing3Source) \ + V(FPCompare) \ + V(FPConditionalCompare) \ + V(FPConditionalSelect) \ + V(FPImmediate) \ + V(FPDataProcessing1Source) \ + V(FPDataProcessing2Source) \ + V(FPDataProcessing3Source) \ + V(FPIntegerConvert) \ + V(FPFixedPointConvert) \ + V(Crypto2RegSHA) \ + V(Crypto3RegSHA) \ + V(CryptoAES) \ + V(NEON2RegMisc) \ + V(NEON3Different) \ + V(NEON3Same) \ + V(NEONAcrossLanes) \ + V(NEONByIndexedElement) \ + V(NEONCopy) \ + V(NEONExtract) \ + V(NEONLoadStoreMultiStruct) \ + V(NEONLoadStoreMultiStructPostIndex) \ + V(NEONLoadStoreSingleStruct) \ + V(NEONLoadStoreSingleStructPostIndex) \ + V(NEONModifiedImmediate) \ + V(NEONScalar2RegMisc) \ + V(NEONScalar3Diff) \ + V(NEONScalar3Same) \ + V(NEONScalarByIndexedElement) \ + V(NEONScalarCopy) \ + V(NEONScalarPairwise) \ + V(NEONScalarShiftImmediate) \ + V(NEONShiftImmediate) \ + V(NEONTable) \ + V(NEONPerm) \ + +#define VISITOR_LIST_THAT_DONT_RETURN(V) \ + V(Unallocated) \ + V(Unimplemented) \ + +#define VISITOR_LIST(V) \ + VISITOR_LIST_THAT_RETURN(V) \ + VISITOR_LIST_THAT_DONT_RETURN(V) \ + +namespace vixl { + +// The Visitor interface. Disassembler and simulator (and other tools) +// must provide implementations for all of these functions. +class DecoderVisitor { + public: + enum VisitorConstness { + kConstVisitor, + kNonConstVisitor + }; + explicit DecoderVisitor(VisitorConstness constness = kConstVisitor) + : constness_(constness) {} + + virtual ~DecoderVisitor() {} + + #define DECLARE(A) virtual void Visit##A(const Instruction* instr) = 0; + VISITOR_LIST(DECLARE) + #undef DECLARE + + bool IsConstVisitor() const { return constness_ == kConstVisitor; } + Instruction* MutableInstruction(const Instruction* instr) { + VIXL_ASSERT(!IsConstVisitor()); + return const_cast(instr); + } + + private: + const VisitorConstness constness_; +}; + + +class Decoder { + public: + Decoder() {} + + // Top-level wrappers around the actual decoding function. + void Decode(const Instruction* instr) { + std::list::iterator it; + for (it = visitors_.begin(); it != visitors_.end(); it++) { + VIXL_ASSERT((*it)->IsConstVisitor()); + } + DecodeInstruction(instr); + } + void Decode(Instruction* instr) { + DecodeInstruction(const_cast(instr)); + } + + // Register a new visitor class with the decoder. + // Decode() will call the corresponding visitor method from all registered + // visitor classes when decoding reaches the leaf node of the instruction + // decode tree. + // Visitors are called in order. + // A visitor can be registered multiple times. + // + // d.AppendVisitor(V1); + // d.AppendVisitor(V2); + // d.PrependVisitor(V2); + // d.AppendVisitor(V3); + // + // d.Decode(i); + // + // will call in order visitor methods in V2, V1, V2, V3. + void AppendVisitor(DecoderVisitor* visitor); + void PrependVisitor(DecoderVisitor* visitor); + // These helpers register `new_visitor` before or after the first instance of + // `registered_visiter` in the list. + // So if + // V1, V2, V1, V2 + // are registered in this order in the decoder, calls to + // d.InsertVisitorAfter(V3, V1); + // d.InsertVisitorBefore(V4, V2); + // will yield the order + // V1, V3, V4, V2, V1, V2 + // + // For more complex modifications of the order of registered visitors, one can + // directly access and modify the list of visitors via the `visitors()' + // accessor. + void InsertVisitorBefore(DecoderVisitor* new_visitor, + DecoderVisitor* registered_visitor); + void InsertVisitorAfter(DecoderVisitor* new_visitor, + DecoderVisitor* registered_visitor); + + // Remove all instances of a previously registered visitor class from the list + // of visitors stored by the decoder. + void RemoveVisitor(DecoderVisitor* visitor); + + #define DECLARE(A) void Visit##A(const Instruction* instr); + VISITOR_LIST(DECLARE) + #undef DECLARE + + + std::list* visitors() { return &visitors_; } + + private: + // Decodes an instruction and calls the visitor functions registered with the + // Decoder class. + void DecodeInstruction(const Instruction* instr); + + // Decode the PC relative addressing instruction, and call the corresponding + // visitors. + // On entry, instruction bits 27:24 = 0x0. + void DecodePCRelAddressing(const Instruction* instr); + + // Decode the add/subtract immediate instruction, and call the correspoding + // visitors. + // On entry, instruction bits 27:24 = 0x1. + void DecodeAddSubImmediate(const Instruction* instr); + + // Decode the branch, system command, and exception generation parts of + // the instruction tree, and call the corresponding visitors. + // On entry, instruction bits 27:24 = {0x4, 0x5, 0x6, 0x7}. + void DecodeBranchSystemException(const Instruction* instr); + + // Decode the load and store parts of the instruction tree, and call + // the corresponding visitors. + // On entry, instruction bits 27:24 = {0x8, 0x9, 0xC, 0xD}. + void DecodeLoadStore(const Instruction* instr); + + // Decode the logical immediate and move wide immediate parts of the + // instruction tree, and call the corresponding visitors. + // On entry, instruction bits 27:24 = 0x2. + void DecodeLogical(const Instruction* instr); + + // Decode the bitfield and extraction parts of the instruction tree, + // and call the corresponding visitors. + // On entry, instruction bits 27:24 = 0x3. + void DecodeBitfieldExtract(const Instruction* instr); + + // Decode the data processing parts of the instruction tree, and call the + // corresponding visitors. + // On entry, instruction bits 27:24 = {0x1, 0xA, 0xB}. + void DecodeDataProcessing(const Instruction* instr); + + // Decode the floating point parts of the instruction tree, and call the + // corresponding visitors. + // On entry, instruction bits 27:24 = {0xE, 0xF}. + void DecodeFP(const Instruction* instr); + + // Decode the Advanced SIMD (NEON) load/store part of the instruction tree, + // and call the corresponding visitors. + // On entry, instruction bits 29:25 = 0x6. + void DecodeNEONLoadStore(const Instruction* instr); + + // Decode the Advanced SIMD (NEON) vector data processing part of the + // instruction tree, and call the corresponding visitors. + // On entry, instruction bits 28:25 = 0x7. + void DecodeNEONVectorDataProcessing(const Instruction* instr); + + // Decode the Advanced SIMD (NEON) scalar data processing part of the + // instruction tree, and call the corresponding visitors. + // On entry, instruction bits 28:25 = 0xF. + void DecodeNEONScalarDataProcessing(const Instruction* instr); + + private: + // Visitors are registered in a list. + std::list visitors_; +}; + +} // namespace vixl + +#endif // VIXL_A64_DECODER_A64_H_ diff --git a/qemu/disas/libvixl/vixl/a64/disasm-a64.cc b/qemu/disas/libvixl/vixl/a64/disasm-a64.cc new file mode 100644 index 000000000..7a58a5c08 --- /dev/null +++ b/qemu/disas/libvixl/vixl/a64/disasm-a64.cc @@ -0,0 +1,3491 @@ +// Copyright 2015, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include "vixl/a64/disasm-a64.h" + +namespace vixl { + +Disassembler::Disassembler() { + buffer_size_ = 256; + buffer_ = reinterpret_cast(malloc(buffer_size_)); + buffer_pos_ = 0; + own_buffer_ = true; + code_address_offset_ = 0; +} + + +Disassembler::Disassembler(char* text_buffer, int buffer_size) { + buffer_size_ = buffer_size; + buffer_ = text_buffer; + buffer_pos_ = 0; + own_buffer_ = false; + code_address_offset_ = 0; +} + + +Disassembler::~Disassembler() { + if (own_buffer_) { + free(buffer_); + } +} + + +char* Disassembler::GetOutput() { + return buffer_; +} + + +void Disassembler::VisitAddSubImmediate(const Instruction* instr) { + bool rd_is_zr = RdIsZROrSP(instr); + bool stack_op = (rd_is_zr || RnIsZROrSP(instr)) && + (instr->ImmAddSub() == 0) ? true : false; + const char *mnemonic = ""; + const char *form = "'Rds, 'Rns, 'IAddSub"; + const char *form_cmp = "'Rns, 'IAddSub"; + const char *form_mov = "'Rds, 'Rns"; + + switch (instr->Mask(AddSubImmediateMask)) { + case ADD_w_imm: + case ADD_x_imm: { + mnemonic = "add"; + if (stack_op) { + mnemonic = "mov"; + form = form_mov; + } + break; + } + case ADDS_w_imm: + case ADDS_x_imm: { + mnemonic = "adds"; + if (rd_is_zr) { + mnemonic = "cmn"; + form = form_cmp; + } + break; + } + case SUB_w_imm: + case SUB_x_imm: mnemonic = "sub"; break; + case SUBS_w_imm: + case SUBS_x_imm: { + mnemonic = "subs"; + if (rd_is_zr) { + mnemonic = "cmp"; + form = form_cmp; + } + break; + } + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitAddSubShifted(const Instruction* instr) { + bool rd_is_zr = RdIsZROrSP(instr); + bool rn_is_zr = RnIsZROrSP(instr); + const char *mnemonic = ""; + const char *form = "'Rd, 'Rn, 'Rm'NDP"; + const char *form_cmp = "'Rn, 'Rm'NDP"; + const char *form_neg = "'Rd, 'Rm'NDP"; + + switch (instr->Mask(AddSubShiftedMask)) { + case ADD_w_shift: + case ADD_x_shift: mnemonic = "add"; break; + case ADDS_w_shift: + case ADDS_x_shift: { + mnemonic = "adds"; + if (rd_is_zr) { + mnemonic = "cmn"; + form = form_cmp; + } + break; + } + case SUB_w_shift: + case SUB_x_shift: { + mnemonic = "sub"; + if (rn_is_zr) { + mnemonic = "neg"; + form = form_neg; + } + break; + } + case SUBS_w_shift: + case SUBS_x_shift: { + mnemonic = "subs"; + if (rd_is_zr) { + mnemonic = "cmp"; + form = form_cmp; + } else if (rn_is_zr) { + mnemonic = "negs"; + form = form_neg; + } + break; + } + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitAddSubExtended(const Instruction* instr) { + bool rd_is_zr = RdIsZROrSP(instr); + const char *mnemonic = ""; + Extend mode = static_cast(instr->ExtendMode()); + const char *form = ((mode == UXTX) || (mode == SXTX)) ? + "'Rds, 'Rns, 'Xm'Ext" : "'Rds, 'Rns, 'Wm'Ext"; + const char *form_cmp = ((mode == UXTX) || (mode == SXTX)) ? + "'Rns, 'Xm'Ext" : "'Rns, 'Wm'Ext"; + + switch (instr->Mask(AddSubExtendedMask)) { + case ADD_w_ext: + case ADD_x_ext: mnemonic = "add"; break; + case ADDS_w_ext: + case ADDS_x_ext: { + mnemonic = "adds"; + if (rd_is_zr) { + mnemonic = "cmn"; + form = form_cmp; + } + break; + } + case SUB_w_ext: + case SUB_x_ext: mnemonic = "sub"; break; + case SUBS_w_ext: + case SUBS_x_ext: { + mnemonic = "subs"; + if (rd_is_zr) { + mnemonic = "cmp"; + form = form_cmp; + } + break; + } + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitAddSubWithCarry(const Instruction* instr) { + bool rn_is_zr = RnIsZROrSP(instr); + const char *mnemonic = ""; + const char *form = "'Rd, 'Rn, 'Rm"; + const char *form_neg = "'Rd, 'Rm"; + + switch (instr->Mask(AddSubWithCarryMask)) { + case ADC_w: + case ADC_x: mnemonic = "adc"; break; + case ADCS_w: + case ADCS_x: mnemonic = "adcs"; break; + case SBC_w: + case SBC_x: { + mnemonic = "sbc"; + if (rn_is_zr) { + mnemonic = "ngc"; + form = form_neg; + } + break; + } + case SBCS_w: + case SBCS_x: { + mnemonic = "sbcs"; + if (rn_is_zr) { + mnemonic = "ngcs"; + form = form_neg; + } + break; + } + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLogicalImmediate(const Instruction* instr) { + bool rd_is_zr = RdIsZROrSP(instr); + bool rn_is_zr = RnIsZROrSP(instr); + const char *mnemonic = ""; + const char *form = "'Rds, 'Rn, 'ITri"; + + if (instr->ImmLogical() == 0) { + // The immediate encoded in the instruction is not in the expected format. + Format(instr, "unallocated", "(LogicalImmediate)"); + return; + } + + switch (instr->Mask(LogicalImmediateMask)) { + case AND_w_imm: + case AND_x_imm: mnemonic = "and"; break; + case ORR_w_imm: + case ORR_x_imm: { + mnemonic = "orr"; + unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSize + : kWRegSize; + if (rn_is_zr && !IsMovzMovnImm(reg_size, instr->ImmLogical())) { + mnemonic = "mov"; + form = "'Rds, 'ITri"; + } + break; + } + case EOR_w_imm: + case EOR_x_imm: mnemonic = "eor"; break; + case ANDS_w_imm: + case ANDS_x_imm: { + mnemonic = "ands"; + if (rd_is_zr) { + mnemonic = "tst"; + form = "'Rn, 'ITri"; + } + break; + } + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +bool Disassembler::IsMovzMovnImm(unsigned reg_size, uint64_t value) { + VIXL_ASSERT((reg_size == kXRegSize) || + ((reg_size == kWRegSize) && (value <= 0xffffffff))); + + // Test for movz: 16 bits set at positions 0, 16, 32 or 48. + if (((value & UINT64_C(0xffffffffffff0000)) == 0) || + ((value & UINT64_C(0xffffffff0000ffff)) == 0) || + ((value & UINT64_C(0xffff0000ffffffff)) == 0) || + ((value & UINT64_C(0x0000ffffffffffff)) == 0)) { + return true; + } + + // Test for movn: NOT(16 bits set at positions 0, 16, 32 or 48). + if ((reg_size == kXRegSize) && + (((~value & UINT64_C(0xffffffffffff0000)) == 0) || + ((~value & UINT64_C(0xffffffff0000ffff)) == 0) || + ((~value & UINT64_C(0xffff0000ffffffff)) == 0) || + ((~value & UINT64_C(0x0000ffffffffffff)) == 0))) { + return true; + } + if ((reg_size == kWRegSize) && + (((value & 0xffff0000) == 0xffff0000) || + ((value & 0x0000ffff) == 0x0000ffff))) { + return true; + } + return false; +} + + +void Disassembler::VisitLogicalShifted(const Instruction* instr) { + bool rd_is_zr = RdIsZROrSP(instr); + bool rn_is_zr = RnIsZROrSP(instr); + const char *mnemonic = ""; + const char *form = "'Rd, 'Rn, 'Rm'NLo"; + + switch (instr->Mask(LogicalShiftedMask)) { + case AND_w: + case AND_x: mnemonic = "and"; break; + case BIC_w: + case BIC_x: mnemonic = "bic"; break; + case EOR_w: + case EOR_x: mnemonic = "eor"; break; + case EON_w: + case EON_x: mnemonic = "eon"; break; + case BICS_w: + case BICS_x: mnemonic = "bics"; break; + case ANDS_w: + case ANDS_x: { + mnemonic = "ands"; + if (rd_is_zr) { + mnemonic = "tst"; + form = "'Rn, 'Rm'NLo"; + } + break; + } + case ORR_w: + case ORR_x: { + mnemonic = "orr"; + if (rn_is_zr && (instr->ImmDPShift() == 0) && (instr->ShiftDP() == LSL)) { + mnemonic = "mov"; + form = "'Rd, 'Rm"; + } + break; + } + case ORN_w: + case ORN_x: { + mnemonic = "orn"; + if (rn_is_zr) { + mnemonic = "mvn"; + form = "'Rd, 'Rm'NLo"; + } + break; + } + default: VIXL_UNREACHABLE(); + } + + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitConditionalCompareRegister(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rn, 'Rm, 'INzcv, 'Cond"; + + switch (instr->Mask(ConditionalCompareRegisterMask)) { + case CCMN_w: + case CCMN_x: mnemonic = "ccmn"; break; + case CCMP_w: + case CCMP_x: mnemonic = "ccmp"; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitConditionalCompareImmediate(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rn, 'IP, 'INzcv, 'Cond"; + + switch (instr->Mask(ConditionalCompareImmediateMask)) { + case CCMN_w_imm: + case CCMN_x_imm: mnemonic = "ccmn"; break; + case CCMP_w_imm: + case CCMP_x_imm: mnemonic = "ccmp"; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitConditionalSelect(const Instruction* instr) { + bool rnm_is_zr = (RnIsZROrSP(instr) && RmIsZROrSP(instr)); + bool rn_is_rm = (instr->Rn() == instr->Rm()); + const char *mnemonic = ""; + const char *form = "'Rd, 'Rn, 'Rm, 'Cond"; + const char *form_test = "'Rd, 'CInv"; + const char *form_update = "'Rd, 'Rn, 'CInv"; + + Condition cond = static_cast(instr->Condition()); + bool invertible_cond = (cond != al) && (cond != nv); + + switch (instr->Mask(ConditionalSelectMask)) { + case CSEL_w: + case CSEL_x: mnemonic = "csel"; break; + case CSINC_w: + case CSINC_x: { + mnemonic = "csinc"; + if (rnm_is_zr && invertible_cond) { + mnemonic = "cset"; + form = form_test; + } else if (rn_is_rm && invertible_cond) { + mnemonic = "cinc"; + form = form_update; + } + break; + } + case CSINV_w: + case CSINV_x: { + mnemonic = "csinv"; + if (rnm_is_zr && invertible_cond) { + mnemonic = "csetm"; + form = form_test; + } else if (rn_is_rm && invertible_cond) { + mnemonic = "cinv"; + form = form_update; + } + break; + } + case CSNEG_w: + case CSNEG_x: { + mnemonic = "csneg"; + if (rn_is_rm && invertible_cond) { + mnemonic = "cneg"; + form = form_update; + } + break; + } + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitBitfield(const Instruction* instr) { + unsigned s = instr->ImmS(); + unsigned r = instr->ImmR(); + unsigned rd_size_minus_1 = + ((instr->SixtyFourBits() == 1) ? kXRegSize : kWRegSize) - 1; + const char *mnemonic = ""; + const char *form = ""; + const char *form_shift_right = "'Rd, 'Rn, 'IBr"; + const char *form_extend = "'Rd, 'Wn"; + const char *form_bfiz = "'Rd, 'Rn, 'IBZ-r, 'IBs+1"; + const char *form_bfx = "'Rd, 'Rn, 'IBr, 'IBs-r+1"; + const char *form_lsl = "'Rd, 'Rn, 'IBZ-r"; + + switch (instr->Mask(BitfieldMask)) { + case SBFM_w: + case SBFM_x: { + mnemonic = "sbfx"; + form = form_bfx; + if (r == 0) { + form = form_extend; + if (s == 7) { + mnemonic = "sxtb"; + } else if (s == 15) { + mnemonic = "sxth"; + } else if ((s == 31) && (instr->SixtyFourBits() == 1)) { + mnemonic = "sxtw"; + } else { + form = form_bfx; + } + } else if (s == rd_size_minus_1) { + mnemonic = "asr"; + form = form_shift_right; + } else if (s < r) { + mnemonic = "sbfiz"; + form = form_bfiz; + } + break; + } + case UBFM_w: + case UBFM_x: { + mnemonic = "ubfx"; + form = form_bfx; + if (r == 0) { + form = form_extend; + if (s == 7) { + mnemonic = "uxtb"; + } else if (s == 15) { + mnemonic = "uxth"; + } else { + form = form_bfx; + } + } + if (s == rd_size_minus_1) { + mnemonic = "lsr"; + form = form_shift_right; + } else if (r == s + 1) { + mnemonic = "lsl"; + form = form_lsl; + } else if (s < r) { + mnemonic = "ubfiz"; + form = form_bfiz; + } + break; + } + case BFM_w: + case BFM_x: { + mnemonic = "bfxil"; + form = form_bfx; + if (s < r) { + mnemonic = "bfi"; + form = form_bfiz; + } + } + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitExtract(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rd, 'Rn, 'Rm, 'IExtract"; + + switch (instr->Mask(ExtractMask)) { + case EXTR_w: + case EXTR_x: { + if (instr->Rn() == instr->Rm()) { + mnemonic = "ror"; + form = "'Rd, 'Rn, 'IExtract"; + } else { + mnemonic = "extr"; + } + break; + } + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitPCRelAddressing(const Instruction* instr) { + switch (instr->Mask(PCRelAddressingMask)) { + case ADR: Format(instr, "adr", "'Xd, 'AddrPCRelByte"); break; + case ADRP: Format(instr, "adrp", "'Xd, 'AddrPCRelPage"); break; + default: Format(instr, "unimplemented", "(PCRelAddressing)"); + } +} + + +void Disassembler::VisitConditionalBranch(const Instruction* instr) { + switch (instr->Mask(ConditionalBranchMask)) { + case B_cond: Format(instr, "b.'CBrn", "'TImmCond"); break; + default: VIXL_UNREACHABLE(); + } +} + + +void Disassembler::VisitUnconditionalBranchToRegister( + const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Xn"; + + switch (instr->Mask(UnconditionalBranchToRegisterMask)) { + case BR: mnemonic = "br"; break; + case BLR: mnemonic = "blr"; break; + case RET: { + mnemonic = "ret"; + if (instr->Rn() == kLinkRegCode) { + form = NULL; + } + break; + } + default: form = "(UnconditionalBranchToRegister)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitUnconditionalBranch(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'TImmUncn"; + + switch (instr->Mask(UnconditionalBranchMask)) { + case B: mnemonic = "b"; break; + case BL: mnemonic = "bl"; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitDataProcessing1Source(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rd, 'Rn"; + + switch (instr->Mask(DataProcessing1SourceMask)) { + #define FORMAT(A, B) \ + case A##_w: \ + case A##_x: mnemonic = B; break; + FORMAT(RBIT, "rbit"); + FORMAT(REV16, "rev16"); + FORMAT(REV, "rev"); + FORMAT(CLZ, "clz"); + FORMAT(CLS, "cls"); + #undef FORMAT + case REV32_x: mnemonic = "rev32"; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitDataProcessing2Source(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Rd, 'Rn, 'Rm"; + const char *form_wwx = "'Wd, 'Wn, 'Xm"; + + switch (instr->Mask(DataProcessing2SourceMask)) { + #define FORMAT(A, B) \ + case A##_w: \ + case A##_x: mnemonic = B; break; + FORMAT(UDIV, "udiv"); + FORMAT(SDIV, "sdiv"); + FORMAT(LSLV, "lsl"); + FORMAT(LSRV, "lsr"); + FORMAT(ASRV, "asr"); + FORMAT(RORV, "ror"); + #undef FORMAT + case CRC32B: mnemonic = "crc32b"; break; + case CRC32H: mnemonic = "crc32h"; break; + case CRC32W: mnemonic = "crc32w"; break; + case CRC32X: mnemonic = "crc32x"; form = form_wwx; break; + case CRC32CB: mnemonic = "crc32cb"; break; + case CRC32CH: mnemonic = "crc32ch"; break; + case CRC32CW: mnemonic = "crc32cw"; break; + case CRC32CX: mnemonic = "crc32cx"; form = form_wwx; break; + default: form = "(DataProcessing2Source)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitDataProcessing3Source(const Instruction* instr) { + bool ra_is_zr = RaIsZROrSP(instr); + const char *mnemonic = ""; + const char *form = "'Xd, 'Wn, 'Wm, 'Xa"; + const char *form_rrr = "'Rd, 'Rn, 'Rm"; + const char *form_rrrr = "'Rd, 'Rn, 'Rm, 'Ra"; + const char *form_xww = "'Xd, 'Wn, 'Wm"; + const char *form_xxx = "'Xd, 'Xn, 'Xm"; + + switch (instr->Mask(DataProcessing3SourceMask)) { + case MADD_w: + case MADD_x: { + mnemonic = "madd"; + form = form_rrrr; + if (ra_is_zr) { + mnemonic = "mul"; + form = form_rrr; + } + break; + } + case MSUB_w: + case MSUB_x: { + mnemonic = "msub"; + form = form_rrrr; + if (ra_is_zr) { + mnemonic = "mneg"; + form = form_rrr; + } + break; + } + case SMADDL_x: { + mnemonic = "smaddl"; + if (ra_is_zr) { + mnemonic = "smull"; + form = form_xww; + } + break; + } + case SMSUBL_x: { + mnemonic = "smsubl"; + if (ra_is_zr) { + mnemonic = "smnegl"; + form = form_xww; + } + break; + } + case UMADDL_x: { + mnemonic = "umaddl"; + if (ra_is_zr) { + mnemonic = "umull"; + form = form_xww; + } + break; + } + case UMSUBL_x: { + mnemonic = "umsubl"; + if (ra_is_zr) { + mnemonic = "umnegl"; + form = form_xww; + } + break; + } + case SMULH_x: { + mnemonic = "smulh"; + form = form_xxx; + break; + } + case UMULH_x: { + mnemonic = "umulh"; + form = form_xxx; + break; + } + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitCompareBranch(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rt, 'TImmCmpa"; + + switch (instr->Mask(CompareBranchMask)) { + case CBZ_w: + case CBZ_x: mnemonic = "cbz"; break; + case CBNZ_w: + case CBNZ_x: mnemonic = "cbnz"; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitTestBranch(const Instruction* instr) { + const char *mnemonic = ""; + // If the top bit of the immediate is clear, the tested register is + // disassembled as Wt, otherwise Xt. As the top bit of the immediate is + // encoded in bit 31 of the instruction, we can reuse the Rt form, which + // uses bit 31 (normally "sf") to choose the register size. + const char *form = "'Rt, 'IS, 'TImmTest"; + + switch (instr->Mask(TestBranchMask)) { + case TBZ: mnemonic = "tbz"; break; + case TBNZ: mnemonic = "tbnz"; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitMoveWideImmediate(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rd, 'IMoveImm"; + + // Print the shift separately for movk, to make it clear which half word will + // be overwritten. Movn and movz print the computed immediate, which includes + // shift calculation. + switch (instr->Mask(MoveWideImmediateMask)) { + case MOVN_w: + case MOVN_x: + if ((instr->ImmMoveWide()) || (instr->ShiftMoveWide() == 0)) { + if ((instr->SixtyFourBits() == 0) && (instr->ImmMoveWide() == 0xffff)) { + mnemonic = "movn"; + } else { + mnemonic = "mov"; + form = "'Rd, 'IMoveNeg"; + } + } else { + mnemonic = "movn"; + } + break; + case MOVZ_w: + case MOVZ_x: + if ((instr->ImmMoveWide()) || (instr->ShiftMoveWide() == 0)) + mnemonic = "mov"; + else + mnemonic = "movz"; + break; + case MOVK_w: + case MOVK_x: mnemonic = "movk"; form = "'Rd, 'IMoveLSL"; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +#define LOAD_STORE_LIST(V) \ + V(STRB_w, "strb", "'Wt") \ + V(STRH_w, "strh", "'Wt") \ + V(STR_w, "str", "'Wt") \ + V(STR_x, "str", "'Xt") \ + V(LDRB_w, "ldrb", "'Wt") \ + V(LDRH_w, "ldrh", "'Wt") \ + V(LDR_w, "ldr", "'Wt") \ + V(LDR_x, "ldr", "'Xt") \ + V(LDRSB_x, "ldrsb", "'Xt") \ + V(LDRSH_x, "ldrsh", "'Xt") \ + V(LDRSW_x, "ldrsw", "'Xt") \ + V(LDRSB_w, "ldrsb", "'Wt") \ + V(LDRSH_w, "ldrsh", "'Wt") \ + V(STR_b, "str", "'Bt") \ + V(STR_h, "str", "'Ht") \ + V(STR_s, "str", "'St") \ + V(STR_d, "str", "'Dt") \ + V(LDR_b, "ldr", "'Bt") \ + V(LDR_h, "ldr", "'Ht") \ + V(LDR_s, "ldr", "'St") \ + V(LDR_d, "ldr", "'Dt") \ + V(STR_q, "str", "'Qt") \ + V(LDR_q, "ldr", "'Qt") + +void Disassembler::VisitLoadStorePreIndex(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStorePreIndex)"; + + switch (instr->Mask(LoadStorePreIndexMask)) { + #define LS_PREINDEX(A, B, C) \ + case A##_pre: mnemonic = B; form = C ", ['Xns'ILS]!"; break; + LOAD_STORE_LIST(LS_PREINDEX) + #undef LS_PREINDEX + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStorePostIndex(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStorePostIndex)"; + + switch (instr->Mask(LoadStorePostIndexMask)) { + #define LS_POSTINDEX(A, B, C) \ + case A##_post: mnemonic = B; form = C ", ['Xns]'ILS"; break; + LOAD_STORE_LIST(LS_POSTINDEX) + #undef LS_POSTINDEX + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStoreUnsignedOffset(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStoreUnsignedOffset)"; + + switch (instr->Mask(LoadStoreUnsignedOffsetMask)) { + #define LS_UNSIGNEDOFFSET(A, B, C) \ + case A##_unsigned: mnemonic = B; form = C ", ['Xns'ILU]"; break; + LOAD_STORE_LIST(LS_UNSIGNEDOFFSET) + #undef LS_UNSIGNEDOFFSET + case PRFM_unsigned: mnemonic = "prfm"; form = "'PrefOp, ['Xns'ILU]"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStoreRegisterOffset(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStoreRegisterOffset)"; + + switch (instr->Mask(LoadStoreRegisterOffsetMask)) { + #define LS_REGISTEROFFSET(A, B, C) \ + case A##_reg: mnemonic = B; form = C ", ['Xns, 'Offsetreg]"; break; + LOAD_STORE_LIST(LS_REGISTEROFFSET) + #undef LS_REGISTEROFFSET + case PRFM_reg: mnemonic = "prfm"; form = "'PrefOp, ['Xns, 'Offsetreg]"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStoreUnscaledOffset(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Wt, ['Xns'ILS]"; + const char *form_x = "'Xt, ['Xns'ILS]"; + const char *form_b = "'Bt, ['Xns'ILS]"; + const char *form_h = "'Ht, ['Xns'ILS]"; + const char *form_s = "'St, ['Xns'ILS]"; + const char *form_d = "'Dt, ['Xns'ILS]"; + const char *form_q = "'Qt, ['Xns'ILS]"; + const char *form_prefetch = "'PrefOp, ['Xns'ILS]"; + + switch (instr->Mask(LoadStoreUnscaledOffsetMask)) { + case STURB_w: mnemonic = "sturb"; break; + case STURH_w: mnemonic = "sturh"; break; + case STUR_w: mnemonic = "stur"; break; + case STUR_x: mnemonic = "stur"; form = form_x; break; + case STUR_b: mnemonic = "stur"; form = form_b; break; + case STUR_h: mnemonic = "stur"; form = form_h; break; + case STUR_s: mnemonic = "stur"; form = form_s; break; + case STUR_d: mnemonic = "stur"; form = form_d; break; + case STUR_q: mnemonic = "stur"; form = form_q; break; + case LDURB_w: mnemonic = "ldurb"; break; + case LDURH_w: mnemonic = "ldurh"; break; + case LDUR_w: mnemonic = "ldur"; break; + case LDUR_x: mnemonic = "ldur"; form = form_x; break; + case LDUR_b: mnemonic = "ldur"; form = form_b; break; + case LDUR_h: mnemonic = "ldur"; form = form_h; break; + case LDUR_s: mnemonic = "ldur"; form = form_s; break; + case LDUR_d: mnemonic = "ldur"; form = form_d; break; + case LDUR_q: mnemonic = "ldur"; form = form_q; break; + case LDURSB_x: form = form_x; VIXL_FALLTHROUGH(); + case LDURSB_w: mnemonic = "ldursb"; break; + case LDURSH_x: form = form_x; VIXL_FALLTHROUGH(); + case LDURSH_w: mnemonic = "ldursh"; break; + case LDURSW_x: mnemonic = "ldursw"; form = form_x; break; + case PRFUM: mnemonic = "prfum"; form = form_prefetch; break; + default: form = "(LoadStoreUnscaledOffset)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadLiteral(const Instruction* instr) { + const char *mnemonic = "ldr"; + const char *form = "(LoadLiteral)"; + + switch (instr->Mask(LoadLiteralMask)) { + case LDR_w_lit: form = "'Wt, 'ILLiteral 'LValue"; break; + case LDR_x_lit: form = "'Xt, 'ILLiteral 'LValue"; break; + case LDR_s_lit: form = "'St, 'ILLiteral 'LValue"; break; + case LDR_d_lit: form = "'Dt, 'ILLiteral 'LValue"; break; + case LDR_q_lit: form = "'Qt, 'ILLiteral 'LValue"; break; + case LDRSW_x_lit: { + mnemonic = "ldrsw"; + form = "'Xt, 'ILLiteral 'LValue"; + break; + } + case PRFM_lit: { + mnemonic = "prfm"; + form = "'PrefOp, 'ILLiteral 'LValue"; + break; + } + default: mnemonic = "unimplemented"; + } + Format(instr, mnemonic, form); +} + + +#define LOAD_STORE_PAIR_LIST(V) \ + V(STP_w, "stp", "'Wt, 'Wt2", "2") \ + V(LDP_w, "ldp", "'Wt, 'Wt2", "2") \ + V(LDPSW_x, "ldpsw", "'Xt, 'Xt2", "2") \ + V(STP_x, "stp", "'Xt, 'Xt2", "3") \ + V(LDP_x, "ldp", "'Xt, 'Xt2", "3") \ + V(STP_s, "stp", "'St, 'St2", "2") \ + V(LDP_s, "ldp", "'St, 'St2", "2") \ + V(STP_d, "stp", "'Dt, 'Dt2", "3") \ + V(LDP_d, "ldp", "'Dt, 'Dt2", "3") \ + V(LDP_q, "ldp", "'Qt, 'Qt2", "4") \ + V(STP_q, "stp", "'Qt, 'Qt2", "4") + +void Disassembler::VisitLoadStorePairPostIndex(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStorePairPostIndex)"; + + switch (instr->Mask(LoadStorePairPostIndexMask)) { + #define LSP_POSTINDEX(A, B, C, D) \ + case A##_post: mnemonic = B; form = C ", ['Xns]'ILP" D; break; + LOAD_STORE_PAIR_LIST(LSP_POSTINDEX) + #undef LSP_POSTINDEX + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStorePairPreIndex(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStorePairPreIndex)"; + + switch (instr->Mask(LoadStorePairPreIndexMask)) { + #define LSP_PREINDEX(A, B, C, D) \ + case A##_pre: mnemonic = B; form = C ", ['Xns'ILP" D "]!"; break; + LOAD_STORE_PAIR_LIST(LSP_PREINDEX) + #undef LSP_PREINDEX + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStorePairOffset(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStorePairOffset)"; + + switch (instr->Mask(LoadStorePairOffsetMask)) { + #define LSP_OFFSET(A, B, C, D) \ + case A##_off: mnemonic = B; form = C ", ['Xns'ILP" D "]"; break; + LOAD_STORE_PAIR_LIST(LSP_OFFSET) + #undef LSP_OFFSET + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStorePairNonTemporal(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form; + + switch (instr->Mask(LoadStorePairNonTemporalMask)) { + case STNP_w: mnemonic = "stnp"; form = "'Wt, 'Wt2, ['Xns'ILP2]"; break; + case LDNP_w: mnemonic = "ldnp"; form = "'Wt, 'Wt2, ['Xns'ILP2]"; break; + case STNP_x: mnemonic = "stnp"; form = "'Xt, 'Xt2, ['Xns'ILP3]"; break; + case LDNP_x: mnemonic = "ldnp"; form = "'Xt, 'Xt2, ['Xns'ILP3]"; break; + case STNP_s: mnemonic = "stnp"; form = "'St, 'St2, ['Xns'ILP2]"; break; + case LDNP_s: mnemonic = "ldnp"; form = "'St, 'St2, ['Xns'ILP2]"; break; + case STNP_d: mnemonic = "stnp"; form = "'Dt, 'Dt2, ['Xns'ILP3]"; break; + case LDNP_d: mnemonic = "ldnp"; form = "'Dt, 'Dt2, ['Xns'ILP3]"; break; + case STNP_q: mnemonic = "stnp"; form = "'Qt, 'Qt2, ['Xns'ILP4]"; break; + case LDNP_q: mnemonic = "ldnp"; form = "'Qt, 'Qt2, ['Xns'ILP4]"; break; + default: form = "(LoadStorePairNonTemporal)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStoreExclusive(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form; + + switch (instr->Mask(LoadStoreExclusiveMask)) { + case STXRB_w: mnemonic = "stxrb"; form = "'Ws, 'Wt, ['Xns]"; break; + case STXRH_w: mnemonic = "stxrh"; form = "'Ws, 'Wt, ['Xns]"; break; + case STXR_w: mnemonic = "stxr"; form = "'Ws, 'Wt, ['Xns]"; break; + case STXR_x: mnemonic = "stxr"; form = "'Ws, 'Xt, ['Xns]"; break; + case LDXRB_w: mnemonic = "ldxrb"; form = "'Wt, ['Xns]"; break; + case LDXRH_w: mnemonic = "ldxrh"; form = "'Wt, ['Xns]"; break; + case LDXR_w: mnemonic = "ldxr"; form = "'Wt, ['Xns]"; break; + case LDXR_x: mnemonic = "ldxr"; form = "'Xt, ['Xns]"; break; + case STXP_w: mnemonic = "stxp"; form = "'Ws, 'Wt, 'Wt2, ['Xns]"; break; + case STXP_x: mnemonic = "stxp"; form = "'Ws, 'Xt, 'Xt2, ['Xns]"; break; + case LDXP_w: mnemonic = "ldxp"; form = "'Wt, 'Wt2, ['Xns]"; break; + case LDXP_x: mnemonic = "ldxp"; form = "'Xt, 'Xt2, ['Xns]"; break; + case STLXRB_w: mnemonic = "stlxrb"; form = "'Ws, 'Wt, ['Xns]"; break; + case STLXRH_w: mnemonic = "stlxrh"; form = "'Ws, 'Wt, ['Xns]"; break; + case STLXR_w: mnemonic = "stlxr"; form = "'Ws, 'Wt, ['Xns]"; break; + case STLXR_x: mnemonic = "stlxr"; form = "'Ws, 'Xt, ['Xns]"; break; + case LDAXRB_w: mnemonic = "ldaxrb"; form = "'Wt, ['Xns]"; break; + case LDAXRH_w: mnemonic = "ldaxrh"; form = "'Wt, ['Xns]"; break; + case LDAXR_w: mnemonic = "ldaxr"; form = "'Wt, ['Xns]"; break; + case LDAXR_x: mnemonic = "ldaxr"; form = "'Xt, ['Xns]"; break; + case STLXP_w: mnemonic = "stlxp"; form = "'Ws, 'Wt, 'Wt2, ['Xns]"; break; + case STLXP_x: mnemonic = "stlxp"; form = "'Ws, 'Xt, 'Xt2, ['Xns]"; break; + case LDAXP_w: mnemonic = "ldaxp"; form = "'Wt, 'Wt2, ['Xns]"; break; + case LDAXP_x: mnemonic = "ldaxp"; form = "'Xt, 'Xt2, ['Xns]"; break; + case STLRB_w: mnemonic = "stlrb"; form = "'Wt, ['Xns]"; break; + case STLRH_w: mnemonic = "stlrh"; form = "'Wt, ['Xns]"; break; + case STLR_w: mnemonic = "stlr"; form = "'Wt, ['Xns]"; break; + case STLR_x: mnemonic = "stlr"; form = "'Xt, ['Xns]"; break; + case LDARB_w: mnemonic = "ldarb"; form = "'Wt, ['Xns]"; break; + case LDARH_w: mnemonic = "ldarh"; form = "'Wt, ['Xns]"; break; + case LDAR_w: mnemonic = "ldar"; form = "'Wt, ['Xns]"; break; + case LDAR_x: mnemonic = "ldar"; form = "'Xt, ['Xns]"; break; + default: form = "(LoadStoreExclusive)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPCompare(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Fn, 'Fm"; + const char *form_zero = "'Fn, #0.0"; + + switch (instr->Mask(FPCompareMask)) { + case FCMP_s_zero: + case FCMP_d_zero: form = form_zero; VIXL_FALLTHROUGH(); + case FCMP_s: + case FCMP_d: mnemonic = "fcmp"; break; + case FCMPE_s_zero: + case FCMPE_d_zero: form = form_zero; VIXL_FALLTHROUGH(); + case FCMPE_s: + case FCMPE_d: mnemonic = "fcmpe"; break; + default: form = "(FPCompare)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPConditionalCompare(const Instruction* instr) { + const char *mnemonic = "unmplemented"; + const char *form = "'Fn, 'Fm, 'INzcv, 'Cond"; + + switch (instr->Mask(FPConditionalCompareMask)) { + case FCCMP_s: + case FCCMP_d: mnemonic = "fccmp"; break; + case FCCMPE_s: + case FCCMPE_d: mnemonic = "fccmpe"; break; + default: form = "(FPConditionalCompare)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPConditionalSelect(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Fd, 'Fn, 'Fm, 'Cond"; + + switch (instr->Mask(FPConditionalSelectMask)) { + case FCSEL_s: + case FCSEL_d: mnemonic = "fcsel"; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPDataProcessing1Source(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Fd, 'Fn"; + + switch (instr->Mask(FPDataProcessing1SourceMask)) { + #define FORMAT(A, B) \ + case A##_s: \ + case A##_d: mnemonic = B; break; + FORMAT(FMOV, "fmov"); + FORMAT(FABS, "fabs"); + FORMAT(FNEG, "fneg"); + FORMAT(FSQRT, "fsqrt"); + FORMAT(FRINTN, "frintn"); + FORMAT(FRINTP, "frintp"); + FORMAT(FRINTM, "frintm"); + FORMAT(FRINTZ, "frintz"); + FORMAT(FRINTA, "frinta"); + FORMAT(FRINTX, "frintx"); + FORMAT(FRINTI, "frinti"); + #undef FORMAT + case FCVT_ds: mnemonic = "fcvt"; form = "'Dd, 'Sn"; break; + case FCVT_sd: mnemonic = "fcvt"; form = "'Sd, 'Dn"; break; + case FCVT_hs: mnemonic = "fcvt"; form = "'Hd, 'Sn"; break; + case FCVT_sh: mnemonic = "fcvt"; form = "'Sd, 'Hn"; break; + case FCVT_dh: mnemonic = "fcvt"; form = "'Dd, 'Hn"; break; + case FCVT_hd: mnemonic = "fcvt"; form = "'Hd, 'Dn"; break; + default: form = "(FPDataProcessing1Source)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPDataProcessing2Source(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Fd, 'Fn, 'Fm"; + + switch (instr->Mask(FPDataProcessing2SourceMask)) { + #define FORMAT(A, B) \ + case A##_s: \ + case A##_d: mnemonic = B; break; + FORMAT(FMUL, "fmul"); + FORMAT(FDIV, "fdiv"); + FORMAT(FADD, "fadd"); + FORMAT(FSUB, "fsub"); + FORMAT(FMAX, "fmax"); + FORMAT(FMIN, "fmin"); + FORMAT(FMAXNM, "fmaxnm"); + FORMAT(FMINNM, "fminnm"); + FORMAT(FNMUL, "fnmul"); + #undef FORMAT + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPDataProcessing3Source(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Fd, 'Fn, 'Fm, 'Fa"; + + switch (instr->Mask(FPDataProcessing3SourceMask)) { + #define FORMAT(A, B) \ + case A##_s: \ + case A##_d: mnemonic = B; break; + FORMAT(FMADD, "fmadd"); + FORMAT(FMSUB, "fmsub"); + FORMAT(FNMADD, "fnmadd"); + FORMAT(FNMSUB, "fnmsub"); + #undef FORMAT + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPImmediate(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "(FPImmediate)"; + + switch (instr->Mask(FPImmediateMask)) { + case FMOV_s_imm: mnemonic = "fmov"; form = "'Sd, 'IFPSingle"; break; + case FMOV_d_imm: mnemonic = "fmov"; form = "'Dd, 'IFPDouble"; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPIntegerConvert(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(FPIntegerConvert)"; + const char *form_rf = "'Rd, 'Fn"; + const char *form_fr = "'Fd, 'Rn"; + + switch (instr->Mask(FPIntegerConvertMask)) { + case FMOV_ws: + case FMOV_xd: mnemonic = "fmov"; form = form_rf; break; + case FMOV_sw: + case FMOV_dx: mnemonic = "fmov"; form = form_fr; break; + case FMOV_d1_x: mnemonic = "fmov"; form = "'Vd.D[1], 'Rn"; break; + case FMOV_x_d1: mnemonic = "fmov"; form = "'Rd, 'Vn.D[1]"; break; + case FCVTAS_ws: + case FCVTAS_xs: + case FCVTAS_wd: + case FCVTAS_xd: mnemonic = "fcvtas"; form = form_rf; break; + case FCVTAU_ws: + case FCVTAU_xs: + case FCVTAU_wd: + case FCVTAU_xd: mnemonic = "fcvtau"; form = form_rf; break; + case FCVTMS_ws: + case FCVTMS_xs: + case FCVTMS_wd: + case FCVTMS_xd: mnemonic = "fcvtms"; form = form_rf; break; + case FCVTMU_ws: + case FCVTMU_xs: + case FCVTMU_wd: + case FCVTMU_xd: mnemonic = "fcvtmu"; form = form_rf; break; + case FCVTNS_ws: + case FCVTNS_xs: + case FCVTNS_wd: + case FCVTNS_xd: mnemonic = "fcvtns"; form = form_rf; break; + case FCVTNU_ws: + case FCVTNU_xs: + case FCVTNU_wd: + case FCVTNU_xd: mnemonic = "fcvtnu"; form = form_rf; break; + case FCVTZU_xd: + case FCVTZU_ws: + case FCVTZU_wd: + case FCVTZU_xs: mnemonic = "fcvtzu"; form = form_rf; break; + case FCVTZS_xd: + case FCVTZS_wd: + case FCVTZS_xs: + case FCVTZS_ws: mnemonic = "fcvtzs"; form = form_rf; break; + case FCVTPU_xd: + case FCVTPU_ws: + case FCVTPU_wd: + case FCVTPU_xs: mnemonic = "fcvtpu"; form = form_rf; break; + case FCVTPS_xd: + case FCVTPS_wd: + case FCVTPS_xs: + case FCVTPS_ws: mnemonic = "fcvtps"; form = form_rf; break; + case SCVTF_sw: + case SCVTF_sx: + case SCVTF_dw: + case SCVTF_dx: mnemonic = "scvtf"; form = form_fr; break; + case UCVTF_sw: + case UCVTF_sx: + case UCVTF_dw: + case UCVTF_dx: mnemonic = "ucvtf"; form = form_fr; break; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPFixedPointConvert(const Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rd, 'Fn, 'IFPFBits"; + const char *form_fr = "'Fd, 'Rn, 'IFPFBits"; + + switch (instr->Mask(FPFixedPointConvertMask)) { + case FCVTZS_ws_fixed: + case FCVTZS_xs_fixed: + case FCVTZS_wd_fixed: + case FCVTZS_xd_fixed: mnemonic = "fcvtzs"; break; + case FCVTZU_ws_fixed: + case FCVTZU_xs_fixed: + case FCVTZU_wd_fixed: + case FCVTZU_xd_fixed: mnemonic = "fcvtzu"; break; + case SCVTF_sw_fixed: + case SCVTF_sx_fixed: + case SCVTF_dw_fixed: + case SCVTF_dx_fixed: mnemonic = "scvtf"; form = form_fr; break; + case UCVTF_sw_fixed: + case UCVTF_sx_fixed: + case UCVTF_dw_fixed: + case UCVTF_dx_fixed: mnemonic = "ucvtf"; form = form_fr; break; + default: VIXL_UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitSystem(const Instruction* instr) { + // Some system instructions hijack their Op and Cp fields to represent a + // range of immediates instead of indicating a different instruction. This + // makes the decoding tricky. + const char *mnemonic = "unimplemented"; + const char *form = "(System)"; + + if (instr->Mask(SystemExclusiveMonitorFMask) == SystemExclusiveMonitorFixed) { + switch (instr->Mask(SystemExclusiveMonitorMask)) { + case CLREX: { + mnemonic = "clrex"; + form = (instr->CRm() == 0xf) ? NULL : "'IX"; + break; + } + } + } else if (instr->Mask(SystemSysRegFMask) == SystemSysRegFixed) { + switch (instr->Mask(SystemSysRegMask)) { + case MRS: { + mnemonic = "mrs"; + switch (instr->ImmSystemRegister()) { + case NZCV: form = "'Xt, nzcv"; break; + case FPCR: form = "'Xt, fpcr"; break; + default: form = "'Xt, (unknown)"; break; + } + break; + } + case MSR: { + mnemonic = "msr"; + switch (instr->ImmSystemRegister()) { + case NZCV: form = "nzcv, 'Xt"; break; + case FPCR: form = "fpcr, 'Xt"; break; + default: form = "(unknown), 'Xt"; break; + } + break; + } + } + } else if (instr->Mask(SystemHintFMask) == SystemHintFixed) { + switch (instr->ImmHint()) { + case NOP: { + mnemonic = "nop"; + form = NULL; + break; + } + } + } else if (instr->Mask(MemBarrierFMask) == MemBarrierFixed) { + switch (instr->Mask(MemBarrierMask)) { + case DMB: { + mnemonic = "dmb"; + form = "'M"; + break; + } + case DSB: { + mnemonic = "dsb"; + form = "'M"; + break; + } + case ISB: { + mnemonic = "isb"; + form = NULL; + break; + } + } + } else if (instr->Mask(SystemSysFMask) == SystemSysFixed) { + switch (instr->SysOp()) { + case IVAU: + mnemonic = "ic"; + form = "ivau, 'Xt"; + break; + case CVAC: + mnemonic = "dc"; + form = "cvac, 'Xt"; + break; + case CVAU: + mnemonic = "dc"; + form = "cvau, 'Xt"; + break; + case CIVAC: + mnemonic = "dc"; + form = "civac, 'Xt"; + break; + case ZVA: + mnemonic = "dc"; + form = "zva, 'Xt"; + break; + default: + mnemonic = "sys"; + if (instr->Rt() == 31) { + form = "'G1, 'Kn, 'Km, 'G2"; + } else { + form = "'G1, 'Kn, 'Km, 'G2, 'Xt"; + } + break; + } + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitException(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'IDebug"; + + switch (instr->Mask(ExceptionMask)) { + case HLT: mnemonic = "hlt"; break; + case BRK: mnemonic = "brk"; break; + case SVC: mnemonic = "svc"; break; + case HVC: mnemonic = "hvc"; break; + case SMC: mnemonic = "smc"; break; + case DCPS1: mnemonic = "dcps1"; form = "{'IDebug}"; break; + case DCPS2: mnemonic = "dcps2"; form = "{'IDebug}"; break; + case DCPS3: mnemonic = "dcps3"; form = "{'IDebug}"; break; + default: form = "(Exception)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitCrypto2RegSHA(const Instruction* instr) { + VisitUnimplemented(instr); +} + + +void Disassembler::VisitCrypto3RegSHA(const Instruction* instr) { + VisitUnimplemented(instr); +} + + +void Disassembler::VisitCryptoAES(const Instruction* instr) { + VisitUnimplemented(instr); +} + + +void Disassembler::VisitNEON2RegMisc(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Vd.%s, 'Vn.%s"; + const char *form_cmp_zero = "'Vd.%s, 'Vn.%s, #0"; + const char *form_fcmp_zero = "'Vd.%s, 'Vn.%s, #0.0"; + NEONFormatDecoder nfd(instr); + + static const NEONFormatMap map_lp_ta = { + {23, 22, 30}, {NF_4H, NF_8H, NF_2S, NF_4S, NF_1D, NF_2D} + }; + + static const NEONFormatMap map_cvt_ta = { + {22}, {NF_4S, NF_2D} + }; + + static const NEONFormatMap map_cvt_tb = { + {22, 30}, {NF_4H, NF_8H, NF_2S, NF_4S} + }; + + if (instr->Mask(NEON2RegMiscOpcode) <= NEON_NEG_opcode) { + // These instructions all use a two bit size field, except NOT and RBIT, + // which use the field to encode the operation. + switch (instr->Mask(NEON2RegMiscMask)) { + case NEON_REV64: mnemonic = "rev64"; break; + case NEON_REV32: mnemonic = "rev32"; break; + case NEON_REV16: mnemonic = "rev16"; break; + case NEON_SADDLP: + mnemonic = "saddlp"; + nfd.SetFormatMap(0, &map_lp_ta); + break; + case NEON_UADDLP: + mnemonic = "uaddlp"; + nfd.SetFormatMap(0, &map_lp_ta); + break; + case NEON_SUQADD: mnemonic = "suqadd"; break; + case NEON_USQADD: mnemonic = "usqadd"; break; + case NEON_CLS: mnemonic = "cls"; break; + case NEON_CLZ: mnemonic = "clz"; break; + case NEON_CNT: mnemonic = "cnt"; break; + case NEON_SADALP: + mnemonic = "sadalp"; + nfd.SetFormatMap(0, &map_lp_ta); + break; + case NEON_UADALP: + mnemonic = "uadalp"; + nfd.SetFormatMap(0, &map_lp_ta); + break; + case NEON_SQABS: mnemonic = "sqabs"; break; + case NEON_SQNEG: mnemonic = "sqneg"; break; + case NEON_CMGT_zero: mnemonic = "cmgt"; form = form_cmp_zero; break; + case NEON_CMGE_zero: mnemonic = "cmge"; form = form_cmp_zero; break; + case NEON_CMEQ_zero: mnemonic = "cmeq"; form = form_cmp_zero; break; + case NEON_CMLE_zero: mnemonic = "cmle"; form = form_cmp_zero; break; + case NEON_CMLT_zero: mnemonic = "cmlt"; form = form_cmp_zero; break; + case NEON_ABS: mnemonic = "abs"; break; + case NEON_NEG: mnemonic = "neg"; break; + case NEON_RBIT_NOT: + switch (instr->FPType()) { + case 0: mnemonic = "mvn"; break; + case 1: mnemonic = "rbit"; break; + default: form = "(NEON2RegMisc)"; + } + nfd.SetFormatMaps(nfd.LogicalFormatMap()); + break; + } + } else { + // These instructions all use a one bit size field, except XTN, SQXTUN, + // SHLL, SQXTN and UQXTN, which use a two bit size field. + nfd.SetFormatMaps(nfd.FPFormatMap()); + switch (instr->Mask(NEON2RegMiscFPMask)) { + case NEON_FABS: mnemonic = "fabs"; break; + case NEON_FNEG: mnemonic = "fneg"; break; + case NEON_FCVTN: + mnemonic = instr->Mask(NEON_Q) ? "fcvtn2" : "fcvtn"; + nfd.SetFormatMap(0, &map_cvt_tb); + nfd.SetFormatMap(1, &map_cvt_ta); + break; + case NEON_FCVTXN: + mnemonic = instr->Mask(NEON_Q) ? "fcvtxn2" : "fcvtxn"; + nfd.SetFormatMap(0, &map_cvt_tb); + nfd.SetFormatMap(1, &map_cvt_ta); + break; + case NEON_FCVTL: + mnemonic = instr->Mask(NEON_Q) ? "fcvtl2" : "fcvtl"; + nfd.SetFormatMap(0, &map_cvt_ta); + nfd.SetFormatMap(1, &map_cvt_tb); + break; + case NEON_FRINTN: mnemonic = "frintn"; break; + case NEON_FRINTA: mnemonic = "frinta"; break; + case NEON_FRINTP: mnemonic = "frintp"; break; + case NEON_FRINTM: mnemonic = "frintm"; break; + case NEON_FRINTX: mnemonic = "frintx"; break; + case NEON_FRINTZ: mnemonic = "frintz"; break; + case NEON_FRINTI: mnemonic = "frinti"; break; + case NEON_FCVTNS: mnemonic = "fcvtns"; break; + case NEON_FCVTNU: mnemonic = "fcvtnu"; break; + case NEON_FCVTPS: mnemonic = "fcvtps"; break; + case NEON_FCVTPU: mnemonic = "fcvtpu"; break; + case NEON_FCVTMS: mnemonic = "fcvtms"; break; + case NEON_FCVTMU: mnemonic = "fcvtmu"; break; + case NEON_FCVTZS: mnemonic = "fcvtzs"; break; + case NEON_FCVTZU: mnemonic = "fcvtzu"; break; + case NEON_FCVTAS: mnemonic = "fcvtas"; break; + case NEON_FCVTAU: mnemonic = "fcvtau"; break; + case NEON_FSQRT: mnemonic = "fsqrt"; break; + case NEON_SCVTF: mnemonic = "scvtf"; break; + case NEON_UCVTF: mnemonic = "ucvtf"; break; + case NEON_URSQRTE: mnemonic = "ursqrte"; break; + case NEON_URECPE: mnemonic = "urecpe"; break; + case NEON_FRSQRTE: mnemonic = "frsqrte"; break; + case NEON_FRECPE: mnemonic = "frecpe"; break; + case NEON_FCMGT_zero: mnemonic = "fcmgt"; form = form_fcmp_zero; break; + case NEON_FCMGE_zero: mnemonic = "fcmge"; form = form_fcmp_zero; break; + case NEON_FCMEQ_zero: mnemonic = "fcmeq"; form = form_fcmp_zero; break; + case NEON_FCMLE_zero: mnemonic = "fcmle"; form = form_fcmp_zero; break; + case NEON_FCMLT_zero: mnemonic = "fcmlt"; form = form_fcmp_zero; break; + default: + if ((NEON_XTN_opcode <= instr->Mask(NEON2RegMiscOpcode)) && + (instr->Mask(NEON2RegMiscOpcode) <= NEON_UQXTN_opcode)) { + nfd.SetFormatMap(0, nfd.IntegerFormatMap()); + nfd.SetFormatMap(1, nfd.LongIntegerFormatMap()); + + switch (instr->Mask(NEON2RegMiscMask)) { + case NEON_XTN: mnemonic = "xtn"; break; + case NEON_SQXTN: mnemonic = "sqxtn"; break; + case NEON_UQXTN: mnemonic = "uqxtn"; break; + case NEON_SQXTUN: mnemonic = "sqxtun"; break; + case NEON_SHLL: + mnemonic = "shll"; + nfd.SetFormatMap(0, nfd.LongIntegerFormatMap()); + nfd.SetFormatMap(1, nfd.IntegerFormatMap()); + switch (instr->NEONSize()) { + case 0: form = "'Vd.%s, 'Vn.%s, #8"; break; + case 1: form = "'Vd.%s, 'Vn.%s, #16"; break; + case 2: form = "'Vd.%s, 'Vn.%s, #32"; break; + default: form = "(NEON2RegMisc)"; + } + } + Format(instr, nfd.Mnemonic(mnemonic), nfd.Substitute(form)); + return; + } else { + form = "(NEON2RegMisc)"; + } + } + } + Format(instr, mnemonic, nfd.Substitute(form)); +} + + +void Disassembler::VisitNEON3Same(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Vd.%s, 'Vn.%s, 'Vm.%s"; + NEONFormatDecoder nfd(instr); + + if (instr->Mask(NEON3SameLogicalFMask) == NEON3SameLogicalFixed) { + switch (instr->Mask(NEON3SameLogicalMask)) { + case NEON_AND: mnemonic = "and"; break; + case NEON_ORR: + mnemonic = "orr"; + if (instr->Rm() == instr->Rn()) { + mnemonic = "mov"; + form = "'Vd.%s, 'Vn.%s"; + } + break; + case NEON_ORN: mnemonic = "orn"; break; + case NEON_EOR: mnemonic = "eor"; break; + case NEON_BIC: mnemonic = "bic"; break; + case NEON_BIF: mnemonic = "bif"; break; + case NEON_BIT: mnemonic = "bit"; break; + case NEON_BSL: mnemonic = "bsl"; break; + default: form = "(NEON3Same)"; + } + nfd.SetFormatMaps(nfd.LogicalFormatMap()); + } else { + static const char *mnemonics[] = { + "shadd", "uhadd", "shadd", "uhadd", + "sqadd", "uqadd", "sqadd", "uqadd", + "srhadd", "urhadd", "srhadd", "urhadd", + NULL, NULL, NULL, NULL, // Handled by logical cases above. + "shsub", "uhsub", "shsub", "uhsub", + "sqsub", "uqsub", "sqsub", "uqsub", + "cmgt", "cmhi", "cmgt", "cmhi", + "cmge", "cmhs", "cmge", "cmhs", + "sshl", "ushl", "sshl", "ushl", + "sqshl", "uqshl", "sqshl", "uqshl", + "srshl", "urshl", "srshl", "urshl", + "sqrshl", "uqrshl", "sqrshl", "uqrshl", + "smax", "umax", "smax", "umax", + "smin", "umin", "smin", "umin", + "sabd", "uabd", "sabd", "uabd", + "saba", "uaba", "saba", "uaba", + "add", "sub", "add", "sub", + "cmtst", "cmeq", "cmtst", "cmeq", + "mla", "mls", "mla", "mls", + "mul", "pmul", "mul", "pmul", + "smaxp", "umaxp", "smaxp", "umaxp", + "sminp", "uminp", "sminp", "uminp", + "sqdmulh", "sqrdmulh", "sqdmulh", "sqrdmulh", + "addp", "unallocated", "addp", "unallocated", + "fmaxnm", "fmaxnmp", "fminnm", "fminnmp", + "fmla", "unallocated", "fmls", "unallocated", + "fadd", "faddp", "fsub", "fabd", + "fmulx", "fmul", "unallocated", "unallocated", + "fcmeq", "fcmge", "unallocated", "fcmgt", + "unallocated", "facge", "unallocated", "facgt", + "fmax", "fmaxp", "fmin", "fminp", + "frecps", "fdiv", "frsqrts", "unallocated"}; + + // Operation is determined by the opcode bits (15-11), the top bit of + // size (23) and the U bit (29). + unsigned index = (instr->Bits(15, 11) << 2) | (instr->Bit(23) << 1) | + instr->Bit(29); + VIXL_ASSERT(index < (sizeof(mnemonics) / sizeof(mnemonics[0]))); + mnemonic = mnemonics[index]; + // Assert that index is not one of the previously handled logical + // instructions. + VIXL_ASSERT(mnemonic != NULL); + + if (instr->Mask(NEON3SameFPFMask) == NEON3SameFPFixed) { + nfd.SetFormatMaps(nfd.FPFormatMap()); + } + } + Format(instr, mnemonic, nfd.Substitute(form)); +} + + +void Disassembler::VisitNEON3Different(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Vd.%s, 'Vn.%s, 'Vm.%s"; + + NEONFormatDecoder nfd(instr); + nfd.SetFormatMap(0, nfd.LongIntegerFormatMap()); + + // Ignore the Q bit. Appending a "2" suffix is handled later. + switch (instr->Mask(NEON3DifferentMask) & ~NEON_Q) { + case NEON_PMULL: mnemonic = "pmull"; break; + case NEON_SABAL: mnemonic = "sabal"; break; + case NEON_SABDL: mnemonic = "sabdl"; break; + case NEON_SADDL: mnemonic = "saddl"; break; + case NEON_SMLAL: mnemonic = "smlal"; break; + case NEON_SMLSL: mnemonic = "smlsl"; break; + case NEON_SMULL: mnemonic = "smull"; break; + case NEON_SSUBL: mnemonic = "ssubl"; break; + case NEON_SQDMLAL: mnemonic = "sqdmlal"; break; + case NEON_SQDMLSL: mnemonic = "sqdmlsl"; break; + case NEON_SQDMULL: mnemonic = "sqdmull"; break; + case NEON_UABAL: mnemonic = "uabal"; break; + case NEON_UABDL: mnemonic = "uabdl"; break; + case NEON_UADDL: mnemonic = "uaddl"; break; + case NEON_UMLAL: mnemonic = "umlal"; break; + case NEON_UMLSL: mnemonic = "umlsl"; break; + case NEON_UMULL: mnemonic = "umull"; break; + case NEON_USUBL: mnemonic = "usubl"; break; + case NEON_SADDW: + mnemonic = "saddw"; + nfd.SetFormatMap(1, nfd.LongIntegerFormatMap()); + break; + case NEON_SSUBW: + mnemonic = "ssubw"; + nfd.SetFormatMap(1, nfd.LongIntegerFormatMap()); + break; + case NEON_UADDW: + mnemonic = "uaddw"; + nfd.SetFormatMap(1, nfd.LongIntegerFormatMap()); + break; + case NEON_USUBW: + mnemonic = "usubw"; + nfd.SetFormatMap(1, nfd.LongIntegerFormatMap()); + break; + case NEON_ADDHN: + mnemonic = "addhn"; + nfd.SetFormatMaps(nfd.LongIntegerFormatMap()); + nfd.SetFormatMap(0, nfd.IntegerFormatMap()); + break; + case NEON_RADDHN: + mnemonic = "raddhn"; + nfd.SetFormatMaps(nfd.LongIntegerFormatMap()); + nfd.SetFormatMap(0, nfd.IntegerFormatMap()); + break; + case NEON_RSUBHN: + mnemonic = "rsubhn"; + nfd.SetFormatMaps(nfd.LongIntegerFormatMap()); + nfd.SetFormatMap(0, nfd.IntegerFormatMap()); + break; + case NEON_SUBHN: + mnemonic = "subhn"; + nfd.SetFormatMaps(nfd.LongIntegerFormatMap()); + nfd.SetFormatMap(0, nfd.IntegerFormatMap()); + break; + default: form = "(NEON3Different)"; + } + Format(instr, nfd.Mnemonic(mnemonic), nfd.Substitute(form)); +} + + +void Disassembler::VisitNEONAcrossLanes(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "%sd, 'Vn.%s"; + + NEONFormatDecoder nfd(instr, NEONFormatDecoder::ScalarFormatMap(), + NEONFormatDecoder::IntegerFormatMap()); + + if (instr->Mask(NEONAcrossLanesFPFMask) == NEONAcrossLanesFPFixed) { + nfd.SetFormatMap(0, nfd.FPScalarFormatMap()); + nfd.SetFormatMap(1, nfd.FPFormatMap()); + switch (instr->Mask(NEONAcrossLanesFPMask)) { + case NEON_FMAXV: mnemonic = "fmaxv"; break; + case NEON_FMINV: mnemonic = "fminv"; break; + case NEON_FMAXNMV: mnemonic = "fmaxnmv"; break; + case NEON_FMINNMV: mnemonic = "fminnmv"; break; + default: form = "(NEONAcrossLanes)"; break; + } + } else if (instr->Mask(NEONAcrossLanesFMask) == NEONAcrossLanesFixed) { + switch (instr->Mask(NEONAcrossLanesMask)) { + case NEON_ADDV: mnemonic = "addv"; break; + case NEON_SMAXV: mnemonic = "smaxv"; break; + case NEON_SMINV: mnemonic = "sminv"; break; + case NEON_UMAXV: mnemonic = "umaxv"; break; + case NEON_UMINV: mnemonic = "uminv"; break; + case NEON_SADDLV: + mnemonic = "saddlv"; + nfd.SetFormatMap(0, nfd.LongScalarFormatMap()); + break; + case NEON_UADDLV: + mnemonic = "uaddlv"; + nfd.SetFormatMap(0, nfd.LongScalarFormatMap()); + break; + default: form = "(NEONAcrossLanes)"; break; + } + } + Format(instr, mnemonic, nfd.Substitute(form, + NEONFormatDecoder::kPlaceholder, NEONFormatDecoder::kFormat)); +} + + +void Disassembler::VisitNEONByIndexedElement(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + bool l_instr = false; + bool fp_instr = false; + + const char *form = "'Vd.%s, 'Vn.%s, 'Ve.%s['IVByElemIndex]"; + + static const NEONFormatMap map_ta = { + {23, 22}, {NF_UNDEF, NF_4S, NF_2D} + }; + NEONFormatDecoder nfd(instr, &map_ta, + NEONFormatDecoder::IntegerFormatMap(), + NEONFormatDecoder::ScalarFormatMap()); + + switch (instr->Mask(NEONByIndexedElementMask)) { + case NEON_SMULL_byelement: mnemonic = "smull"; l_instr = true; break; + case NEON_UMULL_byelement: mnemonic = "umull"; l_instr = true; break; + case NEON_SMLAL_byelement: mnemonic = "smlal"; l_instr = true; break; + case NEON_UMLAL_byelement: mnemonic = "umlal"; l_instr = true; break; + case NEON_SMLSL_byelement: mnemonic = "smlsl"; l_instr = true; break; + case NEON_UMLSL_byelement: mnemonic = "umlsl"; l_instr = true; break; + case NEON_SQDMULL_byelement: mnemonic = "sqdmull"; l_instr = true; break; + case NEON_SQDMLAL_byelement: mnemonic = "sqdmlal"; l_instr = true; break; + case NEON_SQDMLSL_byelement: mnemonic = "sqdmlsl"; l_instr = true; break; + case NEON_MUL_byelement: mnemonic = "mul"; break; + case NEON_MLA_byelement: mnemonic = "mla"; break; + case NEON_MLS_byelement: mnemonic = "mls"; break; + case NEON_SQDMULH_byelement: mnemonic = "sqdmulh"; break; + case NEON_SQRDMULH_byelement: mnemonic = "sqrdmulh"; break; + default: + switch (instr->Mask(NEONByIndexedElementFPMask)) { + case NEON_FMUL_byelement: mnemonic = "fmul"; fp_instr = true; break; + case NEON_FMLA_byelement: mnemonic = "fmla"; fp_instr = true; break; + case NEON_FMLS_byelement: mnemonic = "fmls"; fp_instr = true; break; + case NEON_FMULX_byelement: mnemonic = "fmulx"; fp_instr = true; break; + } + } + + if (l_instr) { + Format(instr, nfd.Mnemonic(mnemonic), nfd.Substitute(form)); + } else if (fp_instr) { + nfd.SetFormatMap(0, nfd.FPFormatMap()); + Format(instr, mnemonic, nfd.Substitute(form)); + } else { + nfd.SetFormatMap(0, nfd.IntegerFormatMap()); + Format(instr, mnemonic, nfd.Substitute(form)); + } +} + + +void Disassembler::VisitNEONCopy(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(NEONCopy)"; + + NEONFormatDecoder nfd(instr, NEONFormatDecoder::TriangularFormatMap(), + NEONFormatDecoder::TriangularScalarFormatMap()); + + if (instr->Mask(NEONCopyInsElementMask) == NEON_INS_ELEMENT) { + mnemonic = "mov"; + nfd.SetFormatMap(0, nfd.TriangularScalarFormatMap()); + form = "'Vd.%s['IVInsIndex1], 'Vn.%s['IVInsIndex2]"; + } else if (instr->Mask(NEONCopyInsGeneralMask) == NEON_INS_GENERAL) { + mnemonic = "mov"; + nfd.SetFormatMap(0, nfd.TriangularScalarFormatMap()); + if (nfd.GetVectorFormat() == kFormatD) { + form = "'Vd.%s['IVInsIndex1], 'Xn"; + } else { + form = "'Vd.%s['IVInsIndex1], 'Wn"; + } + } else if (instr->Mask(NEONCopyUmovMask) == NEON_UMOV) { + if (instr->Mask(NEON_Q) || ((instr->ImmNEON5() & 7) == 4)) { + mnemonic = "mov"; + } else { + mnemonic = "umov"; + } + nfd.SetFormatMap(0, nfd.TriangularScalarFormatMap()); + if (nfd.GetVectorFormat() == kFormatD) { + form = "'Xd, 'Vn.%s['IVInsIndex1]"; + } else { + form = "'Wd, 'Vn.%s['IVInsIndex1]"; + } + } else if (instr->Mask(NEONCopySmovMask) == NEON_SMOV) { + mnemonic = "smov"; + nfd.SetFormatMap(0, nfd.TriangularScalarFormatMap()); + form = "'Rdq, 'Vn.%s['IVInsIndex1]"; + } else if (instr->Mask(NEONCopyDupElementMask) == NEON_DUP_ELEMENT) { + mnemonic = "dup"; + form = "'Vd.%s, 'Vn.%s['IVInsIndex1]"; + } else if (instr->Mask(NEONCopyDupGeneralMask) == NEON_DUP_GENERAL) { + mnemonic = "dup"; + if (nfd.GetVectorFormat() == kFormat2D) { + form = "'Vd.%s, 'Xn"; + } else { + form = "'Vd.%s, 'Wn"; + } + } + Format(instr, mnemonic, nfd.Substitute(form)); +} + + +void Disassembler::VisitNEONExtract(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(NEONExtract)"; + NEONFormatDecoder nfd(instr, NEONFormatDecoder::LogicalFormatMap()); + if (instr->Mask(NEONExtractMask) == NEON_EXT) { + mnemonic = "ext"; + form = "'Vd.%s, 'Vn.%s, 'Vm.%s, 'IVExtract"; + } + Format(instr, mnemonic, nfd.Substitute(form)); +} + + +void Disassembler::VisitNEONLoadStoreMultiStruct(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(NEONLoadStoreMultiStruct)"; + const char *form_1v = "{'Vt.%1$s}, ['Xns]"; + const char *form_2v = "{'Vt.%1$s, 'Vt2.%1$s}, ['Xns]"; + const char *form_3v = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s}, ['Xns]"; + const char *form_4v = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s, 'Vt4.%1$s}, ['Xns]"; + NEONFormatDecoder nfd(instr, NEONFormatDecoder::LoadStoreFormatMap()); + + switch (instr->Mask(NEONLoadStoreMultiStructMask)) { + case NEON_LD1_1v: mnemonic = "ld1"; form = form_1v; break; + case NEON_LD1_2v: mnemonic = "ld1"; form = form_2v; break; + case NEON_LD1_3v: mnemonic = "ld1"; form = form_3v; break; + case NEON_LD1_4v: mnemonic = "ld1"; form = form_4v; break; + case NEON_LD2: mnemonic = "ld2"; form = form_2v; break; + case NEON_LD3: mnemonic = "ld3"; form = form_3v; break; + case NEON_LD4: mnemonic = "ld4"; form = form_4v; break; + case NEON_ST1_1v: mnemonic = "st1"; form = form_1v; break; + case NEON_ST1_2v: mnemonic = "st1"; form = form_2v; break; + case NEON_ST1_3v: mnemonic = "st1"; form = form_3v; break; + case NEON_ST1_4v: mnemonic = "st1"; form = form_4v; break; + case NEON_ST2: mnemonic = "st2"; form = form_2v; break; + case NEON_ST3: mnemonic = "st3"; form = form_3v; break; + case NEON_ST4: mnemonic = "st4"; form = form_4v; break; + default: break; + } + + Format(instr, mnemonic, nfd.Substitute(form)); +} + + +void Disassembler::VisitNEONLoadStoreMultiStructPostIndex( + const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(NEONLoadStoreMultiStructPostIndex)"; + const char *form_1v = "{'Vt.%1$s}, ['Xns], 'Xmr1"; + const char *form_2v = "{'Vt.%1$s, 'Vt2.%1$s}, ['Xns], 'Xmr2"; + const char *form_3v = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s}, ['Xns], 'Xmr3"; + const char *form_4v = + "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s, 'Vt4.%1$s}, ['Xns], 'Xmr4"; + NEONFormatDecoder nfd(instr, NEONFormatDecoder::LoadStoreFormatMap()); + + switch (instr->Mask(NEONLoadStoreMultiStructPostIndexMask)) { + case NEON_LD1_1v_post: mnemonic = "ld1"; form = form_1v; break; + case NEON_LD1_2v_post: mnemonic = "ld1"; form = form_2v; break; + case NEON_LD1_3v_post: mnemonic = "ld1"; form = form_3v; break; + case NEON_LD1_4v_post: mnemonic = "ld1"; form = form_4v; break; + case NEON_LD2_post: mnemonic = "ld2"; form = form_2v; break; + case NEON_LD3_post: mnemonic = "ld3"; form = form_3v; break; + case NEON_LD4_post: mnemonic = "ld4"; form = form_4v; break; + case NEON_ST1_1v_post: mnemonic = "st1"; form = form_1v; break; + case NEON_ST1_2v_post: mnemonic = "st1"; form = form_2v; break; + case NEON_ST1_3v_post: mnemonic = "st1"; form = form_3v; break; + case NEON_ST1_4v_post: mnemonic = "st1"; form = form_4v; break; + case NEON_ST2_post: mnemonic = "st2"; form = form_2v; break; + case NEON_ST3_post: mnemonic = "st3"; form = form_3v; break; + case NEON_ST4_post: mnemonic = "st4"; form = form_4v; break; + default: break; + } + + Format(instr, mnemonic, nfd.Substitute(form)); +} + + +void Disassembler::VisitNEONLoadStoreSingleStruct(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(NEONLoadStoreSingleStruct)"; + + const char *form_1b = "{'Vt.b}['IVLSLane0], ['Xns]"; + const char *form_1h = "{'Vt.h}['IVLSLane1], ['Xns]"; + const char *form_1s = "{'Vt.s}['IVLSLane2], ['Xns]"; + const char *form_1d = "{'Vt.d}['IVLSLane3], ['Xns]"; + NEONFormatDecoder nfd(instr, NEONFormatDecoder::LoadStoreFormatMap()); + + switch (instr->Mask(NEONLoadStoreSingleStructMask)) { + case NEON_LD1_b: mnemonic = "ld1"; form = form_1b; break; + case NEON_LD1_h: mnemonic = "ld1"; form = form_1h; break; + case NEON_LD1_s: + mnemonic = "ld1"; + VIXL_STATIC_ASSERT((NEON_LD1_s | (1 << NEONLSSize_offset)) == NEON_LD1_d); + form = ((instr->NEONLSSize() & 1) == 0) ? form_1s : form_1d; + break; + case NEON_ST1_b: mnemonic = "st1"; form = form_1b; break; + case NEON_ST1_h: mnemonic = "st1"; form = form_1h; break; + case NEON_ST1_s: + mnemonic = "st1"; + VIXL_STATIC_ASSERT((NEON_ST1_s | (1 << NEONLSSize_offset)) == NEON_ST1_d); + form = ((instr->NEONLSSize() & 1) == 0) ? form_1s : form_1d; + break; + case NEON_LD1R: + mnemonic = "ld1r"; + form = "{'Vt.%s}, ['Xns]"; + break; + case NEON_LD2_b: + case NEON_ST2_b: + mnemonic = (instr->LdStXLoad() == 1) ? "ld2" : "st2"; + form = "{'Vt.b, 'Vt2.b}['IVLSLane0], ['Xns]"; + break; + case NEON_LD2_h: + case NEON_ST2_h: + mnemonic = (instr->LdStXLoad() == 1) ? "ld2" : "st2"; + form = "{'Vt.h, 'Vt2.h}['IVLSLane1], ['Xns]"; + break; + case NEON_LD2_s: + case NEON_ST2_s: + VIXL_STATIC_ASSERT((NEON_ST2_s | (1 << NEONLSSize_offset)) == NEON_ST2_d); + VIXL_STATIC_ASSERT((NEON_LD2_s | (1 << NEONLSSize_offset)) == NEON_LD2_d); + mnemonic = (instr->LdStXLoad() == 1) ? "ld2" : "st2"; + if ((instr->NEONLSSize() & 1) == 0) + form = "{'Vt.s, 'Vt2.s}['IVLSLane2], ['Xns]"; + else + form = "{'Vt.d, 'Vt2.d}['IVLSLane3], ['Xns]"; + break; + case NEON_LD2R: + mnemonic = "ld2r"; + form = "{'Vt.%s, 'Vt2.%s}, ['Xns]"; + break; + case NEON_LD3_b: + case NEON_ST3_b: + mnemonic = (instr->LdStXLoad() == 1) ? "ld3" : "st3"; + form = "{'Vt.b, 'Vt2.b, 'Vt3.b}['IVLSLane0], ['Xns]"; + break; + case NEON_LD3_h: + case NEON_ST3_h: + mnemonic = (instr->LdStXLoad() == 1) ? "ld3" : "st3"; + form = "{'Vt.h, 'Vt2.h, 'Vt3.h}['IVLSLane1], ['Xns]"; + break; + case NEON_LD3_s: + case NEON_ST3_s: + mnemonic = (instr->LdStXLoad() == 1) ? "ld3" : "st3"; + if ((instr->NEONLSSize() & 1) == 0) + form = "{'Vt.s, 'Vt2.s, 'Vt3.s}['IVLSLane2], ['Xns]"; + else + form = "{'Vt.d, 'Vt2.d, 'Vt3.d}['IVLSLane3], ['Xns]"; + break; + case NEON_LD3R: + mnemonic = "ld3r"; + form = "{'Vt.%s, 'Vt2.%s, 'Vt3.%s}, ['Xns]"; + break; + case NEON_LD4_b: + case NEON_ST4_b: + mnemonic = (instr->LdStXLoad() == 1) ? "ld4" : "st4"; + form = "{'Vt.b, 'Vt2.b, 'Vt3.b, 'Vt4.b}['IVLSLane0], ['Xns]"; + break; + case NEON_LD4_h: + case NEON_ST4_h: + mnemonic = (instr->LdStXLoad() == 1) ? "ld4" : "st4"; + form = "{'Vt.h, 'Vt2.h, 'Vt3.h, 'Vt4.h}['IVLSLane1], ['Xns]"; + break; + case NEON_LD4_s: + case NEON_ST4_s: + VIXL_STATIC_ASSERT((NEON_LD4_s | (1 << NEONLSSize_offset)) == NEON_LD4_d); + VIXL_STATIC_ASSERT((NEON_ST4_s | (1 << NEONLSSize_offset)) == NEON_ST4_d); + mnemonic = (instr->LdStXLoad() == 1) ? "ld4" : "st4"; + if ((instr->NEONLSSize() & 1) == 0) + form = "{'Vt.s, 'Vt2.s, 'Vt3.s, 'Vt4.s}['IVLSLane2], ['Xns]"; + else + form = "{'Vt.d, 'Vt2.d, 'Vt3.d, 'Vt4.d}['IVLSLane3], ['Xns]"; + break; + case NEON_LD4R: + mnemonic = "ld4r"; + form = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s, 'Vt4.%1$s}, ['Xns]"; + break; + default: break; + } + + Format(instr, mnemonic, nfd.Substitute(form)); +} + + +void Disassembler::VisitNEONLoadStoreSingleStructPostIndex( + const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(NEONLoadStoreSingleStructPostIndex)"; + + const char *form_1b = "{'Vt.b}['IVLSLane0], ['Xns], 'Xmb1"; + const char *form_1h = "{'Vt.h}['IVLSLane1], ['Xns], 'Xmb2"; + const char *form_1s = "{'Vt.s}['IVLSLane2], ['Xns], 'Xmb4"; + const char *form_1d = "{'Vt.d}['IVLSLane3], ['Xns], 'Xmb8"; + NEONFormatDecoder nfd(instr, NEONFormatDecoder::LoadStoreFormatMap()); + + switch (instr->Mask(NEONLoadStoreSingleStructPostIndexMask)) { + case NEON_LD1_b_post: mnemonic = "ld1"; form = form_1b; break; + case NEON_LD1_h_post: mnemonic = "ld1"; form = form_1h; break; + case NEON_LD1_s_post: + mnemonic = "ld1"; + VIXL_STATIC_ASSERT((NEON_LD1_s | (1 << NEONLSSize_offset)) == NEON_LD1_d); + form = ((instr->NEONLSSize() & 1) == 0) ? form_1s : form_1d; + break; + case NEON_ST1_b_post: mnemonic = "st1"; form = form_1b; break; + case NEON_ST1_h_post: mnemonic = "st1"; form = form_1h; break; + case NEON_ST1_s_post: + mnemonic = "st1"; + VIXL_STATIC_ASSERT((NEON_ST1_s | (1 << NEONLSSize_offset)) == NEON_ST1_d); + form = ((instr->NEONLSSize() & 1) == 0) ? form_1s : form_1d; + break; + case NEON_LD1R_post: + mnemonic = "ld1r"; + form = "{'Vt.%s}, ['Xns], 'Xmz1"; + break; + case NEON_LD2_b_post: + case NEON_ST2_b_post: + mnemonic = (instr->LdStXLoad() == 1) ? "ld2" : "st2"; + form = "{'Vt.b, 'Vt2.b}['IVLSLane0], ['Xns], 'Xmb2"; + break; + case NEON_ST2_h_post: + case NEON_LD2_h_post: + mnemonic = (instr->LdStXLoad() == 1) ? "ld2" : "st2"; + form = "{'Vt.h, 'Vt2.h}['IVLSLane1], ['Xns], 'Xmb4"; + break; + case NEON_LD2_s_post: + case NEON_ST2_s_post: + mnemonic = (instr->LdStXLoad() == 1) ? "ld2" : "st2"; + if ((instr->NEONLSSize() & 1) == 0) + form = "{'Vt.s, 'Vt2.s}['IVLSLane2], ['Xns], 'Xmb8"; + else + form = "{'Vt.d, 'Vt2.d}['IVLSLane3], ['Xns], 'Xmb16"; + break; + case NEON_LD2R_post: + mnemonic = "ld2r"; + form = "{'Vt.%s, 'Vt2.%s}, ['Xns], 'Xmz2"; + break; + case NEON_LD3_b_post: + case NEON_ST3_b_post: + mnemonic = (instr->LdStXLoad() == 1) ? "ld3" : "st3"; + form = "{'Vt.b, 'Vt2.b, 'Vt3.b}['IVLSLane0], ['Xns], 'Xmb3"; + break; + case NEON_LD3_h_post: + case NEON_ST3_h_post: + mnemonic = (instr->LdStXLoad() == 1) ? "ld3" : "st3"; + form = "{'Vt.h, 'Vt2.h, 'Vt3.h}['IVLSLane1], ['Xns], 'Xmb6"; + break; + case NEON_LD3_s_post: + case NEON_ST3_s_post: + mnemonic = (instr->LdStXLoad() == 1) ? "ld3" : "st3"; + if ((instr->NEONLSSize() & 1) == 0) + form = "{'Vt.s, 'Vt2.s, 'Vt3.s}['IVLSLane2], ['Xns], 'Xmb12"; + else + form = "{'Vt.d, 'Vt2.d, 'Vt3.d}['IVLSLane3], ['Xns], 'Xmr3"; + break; + case NEON_LD3R_post: + mnemonic = "ld3r"; + form = "{'Vt.%s, 'Vt2.%s, 'Vt3.%s}, ['Xns], 'Xmz3"; + break; + case NEON_LD4_b_post: + case NEON_ST4_b_post: + mnemonic = (instr->LdStXLoad() == 1) ? "ld4" : "st4"; + form = "{'Vt.b, 'Vt2.b, 'Vt3.b, 'Vt4.b}['IVLSLane0], ['Xns], 'Xmb4"; + break; + case NEON_LD4_h_post: + case NEON_ST4_h_post: + mnemonic = (instr->LdStXLoad()) == 1 ? "ld4" : "st4"; + form = "{'Vt.h, 'Vt2.h, 'Vt3.h, 'Vt4.h}['IVLSLane1], ['Xns], 'Xmb8"; + break; + case NEON_LD4_s_post: + case NEON_ST4_s_post: + mnemonic = (instr->LdStXLoad() == 1) ? "ld4" : "st4"; + if ((instr->NEONLSSize() & 1) == 0) + form = "{'Vt.s, 'Vt2.s, 'Vt3.s, 'Vt4.s}['IVLSLane2], ['Xns], 'Xmb16"; + else + form = "{'Vt.d, 'Vt2.d, 'Vt3.d, 'Vt4.d}['IVLSLane3], ['Xns], 'Xmb32"; + break; + case NEON_LD4R_post: + mnemonic = "ld4r"; + form = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s, 'Vt4.%1$s}, ['Xns], 'Xmz4"; + break; + default: break; + } + + Format(instr, mnemonic, nfd.Substitute(form)); +} + + +void Disassembler::VisitNEONModifiedImmediate(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Vt.%s, 'IVMIImm8, lsl 'IVMIShiftAmt1"; + + int cmode = instr->NEONCmode(); + int cmode_3 = (cmode >> 3) & 1; + int cmode_2 = (cmode >> 2) & 1; + int cmode_1 = (cmode >> 1) & 1; + int cmode_0 = cmode & 1; + int q = instr->NEONQ(); + int op = instr->NEONModImmOp(); + + static const NEONFormatMap map_b = { {30}, {NF_8B, NF_16B} }; + static const NEONFormatMap map_h = { {30}, {NF_4H, NF_8H} }; + static const NEONFormatMap map_s = { {30}, {NF_2S, NF_4S} }; + NEONFormatDecoder nfd(instr, &map_b); + + if (cmode_3 == 0) { + if (cmode_0 == 0) { + mnemonic = (op == 1) ? "mvni" : "movi"; + } else { // cmode<0> == '1'. + mnemonic = (op == 1) ? "bic" : "orr"; + } + nfd.SetFormatMap(0, &map_s); + } else { // cmode<3> == '1'. + if (cmode_2 == 0) { + if (cmode_0 == 0) { + mnemonic = (op == 1) ? "mvni" : "movi"; + } else { // cmode<0> == '1'. + mnemonic = (op == 1) ? "bic" : "orr"; + } + nfd.SetFormatMap(0, &map_h); + } else { // cmode<2> == '1'. + if (cmode_1 == 0) { + mnemonic = (op == 1) ? "mvni" : "movi"; + form = "'Vt.%s, 'IVMIImm8, msl 'IVMIShiftAmt2"; + nfd.SetFormatMap(0, &map_s); + } else { // cmode<1> == '1'. + if (cmode_0 == 0) { + mnemonic = "movi"; + if (op == 0) { + form = "'Vt.%s, 'IVMIImm8"; + } else { + form = (q == 0) ? "'Dd, 'IVMIImm" : "'Vt.2d, 'IVMIImm"; + } + } else { // cmode<0> == '1' + mnemonic = "fmov"; + if (op == 0) { + form = "'Vt.%s, 'IVMIImmFPSingle"; + nfd.SetFormatMap(0, &map_s); + } else { + if (q == 1) { + form = "'Vt.2d, 'IVMIImmFPDouble"; + } + } + } + } + } + } + Format(instr, mnemonic, nfd.Substitute(form)); +} + + +void Disassembler::VisitNEONScalar2RegMisc(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "%sd, %sn"; + const char *form_0 = "%sd, %sn, #0"; + const char *form_fp0 = "%sd, %sn, #0.0"; + + NEONFormatDecoder nfd(instr, NEONFormatDecoder::ScalarFormatMap()); + + if (instr->Mask(NEON2RegMiscOpcode) <= NEON_NEG_scalar_opcode) { + // These instructions all use a two bit size field, except NOT and RBIT, + // which use the field to encode the operation. + switch (instr->Mask(NEONScalar2RegMiscMask)) { + case NEON_CMGT_zero_scalar: mnemonic = "cmgt"; form = form_0; break; + case NEON_CMGE_zero_scalar: mnemonic = "cmge"; form = form_0; break; + case NEON_CMLE_zero_scalar: mnemonic = "cmle"; form = form_0; break; + case NEON_CMLT_zero_scalar: mnemonic = "cmlt"; form = form_0; break; + case NEON_CMEQ_zero_scalar: mnemonic = "cmeq"; form = form_0; break; + case NEON_NEG_scalar: mnemonic = "neg"; break; + case NEON_SQNEG_scalar: mnemonic = "sqneg"; break; + case NEON_ABS_scalar: mnemonic = "abs"; break; + case NEON_SQABS_scalar: mnemonic = "sqabs"; break; + case NEON_SUQADD_scalar: mnemonic = "suqadd"; break; + case NEON_USQADD_scalar: mnemonic = "usqadd"; break; + default: form = "(NEONScalar2RegMisc)"; + } + } else { + // These instructions all use a one bit size field, except SQXTUN, SQXTN + // and UQXTN, which use a two bit size field. + nfd.SetFormatMaps(nfd.FPScalarFormatMap()); + switch (instr->Mask(NEONScalar2RegMiscFPMask)) { + case NEON_FRSQRTE_scalar: mnemonic = "frsqrte"; break; + case NEON_FRECPE_scalar: mnemonic = "frecpe"; break; + case NEON_SCVTF_scalar: mnemonic = "scvtf"; break; + case NEON_UCVTF_scalar: mnemonic = "ucvtf"; break; + case NEON_FCMGT_zero_scalar: mnemonic = "fcmgt"; form = form_fp0; break; + case NEON_FCMGE_zero_scalar: mnemonic = "fcmge"; form = form_fp0; break; + case NEON_FCMLE_zero_scalar: mnemonic = "fcmle"; form = form_fp0; break; + case NEON_FCMLT_zero_scalar: mnemonic = "fcmlt"; form = form_fp0; break; + case NEON_FCMEQ_zero_scalar: mnemonic = "fcmeq"; form = form_fp0; break; + case NEON_FRECPX_scalar: mnemonic = "frecpx"; break; + case NEON_FCVTNS_scalar: mnemonic = "fcvtns"; break; + case NEON_FCVTNU_scalar: mnemonic = "fcvtnu"; break; + case NEON_FCVTPS_scalar: mnemonic = "fcvtps"; break; + case NEON_FCVTPU_scalar: mnemonic = "fcvtpu"; break; + case NEON_FCVTMS_scalar: mnemonic = "fcvtms"; break; + case NEON_FCVTMU_scalar: mnemonic = "fcvtmu"; break; + case NEON_FCVTZS_scalar: mnemonic = "fcvtzs"; break; + case NEON_FCVTZU_scalar: mnemonic = "fcvtzu"; break; + case NEON_FCVTAS_scalar: mnemonic = "fcvtas"; break; + case NEON_FCVTAU_scalar: mnemonic = "fcvtau"; break; + case NEON_FCVTXN_scalar: + nfd.SetFormatMap(0, nfd.LongScalarFormatMap()); + mnemonic = "fcvtxn"; + break; + default: + nfd.SetFormatMap(0, nfd.ScalarFormatMap()); + nfd.SetFormatMap(1, nfd.LongScalarFormatMap()); + switch (instr->Mask(NEONScalar2RegMiscMask)) { + case NEON_SQXTN_scalar: mnemonic = "sqxtn"; break; + case NEON_UQXTN_scalar: mnemonic = "uqxtn"; break; + case NEON_SQXTUN_scalar: mnemonic = "sqxtun"; break; + default: form = "(NEONScalar2RegMisc)"; + } + } + } + Format(instr, mnemonic, nfd.SubstitutePlaceholders(form)); +} + + +void Disassembler::VisitNEONScalar3Diff(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "%sd, %sn, %sm"; + NEONFormatDecoder nfd(instr, NEONFormatDecoder::LongScalarFormatMap(), + NEONFormatDecoder::ScalarFormatMap()); + + switch (instr->Mask(NEONScalar3DiffMask)) { + case NEON_SQDMLAL_scalar : mnemonic = "sqdmlal"; break; + case NEON_SQDMLSL_scalar : mnemonic = "sqdmlsl"; break; + case NEON_SQDMULL_scalar : mnemonic = "sqdmull"; break; + default: form = "(NEONScalar3Diff)"; + } + Format(instr, mnemonic, nfd.SubstitutePlaceholders(form)); +} + + +void Disassembler::VisitNEONScalar3Same(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "%sd, %sn, %sm"; + NEONFormatDecoder nfd(instr, NEONFormatDecoder::ScalarFormatMap()); + + if (instr->Mask(NEONScalar3SameFPFMask) == NEONScalar3SameFPFixed) { + nfd.SetFormatMaps(nfd.FPScalarFormatMap()); + switch (instr->Mask(NEONScalar3SameFPMask)) { + case NEON_FACGE_scalar: mnemonic = "facge"; break; + case NEON_FACGT_scalar: mnemonic = "facgt"; break; + case NEON_FCMEQ_scalar: mnemonic = "fcmeq"; break; + case NEON_FCMGE_scalar: mnemonic = "fcmge"; break; + case NEON_FCMGT_scalar: mnemonic = "fcmgt"; break; + case NEON_FMULX_scalar: mnemonic = "fmulx"; break; + case NEON_FRECPS_scalar: mnemonic = "frecps"; break; + case NEON_FRSQRTS_scalar: mnemonic = "frsqrts"; break; + case NEON_FABD_scalar: mnemonic = "fabd"; break; + default: form = "(NEONScalar3Same)"; + } + } else { + switch (instr->Mask(NEONScalar3SameMask)) { + case NEON_ADD_scalar: mnemonic = "add"; break; + case NEON_SUB_scalar: mnemonic = "sub"; break; + case NEON_CMEQ_scalar: mnemonic = "cmeq"; break; + case NEON_CMGE_scalar: mnemonic = "cmge"; break; + case NEON_CMGT_scalar: mnemonic = "cmgt"; break; + case NEON_CMHI_scalar: mnemonic = "cmhi"; break; + case NEON_CMHS_scalar: mnemonic = "cmhs"; break; + case NEON_CMTST_scalar: mnemonic = "cmtst"; break; + case NEON_UQADD_scalar: mnemonic = "uqadd"; break; + case NEON_SQADD_scalar: mnemonic = "sqadd"; break; + case NEON_UQSUB_scalar: mnemonic = "uqsub"; break; + case NEON_SQSUB_scalar: mnemonic = "sqsub"; break; + case NEON_USHL_scalar: mnemonic = "ushl"; break; + case NEON_SSHL_scalar: mnemonic = "sshl"; break; + case NEON_UQSHL_scalar: mnemonic = "uqshl"; break; + case NEON_SQSHL_scalar: mnemonic = "sqshl"; break; + case NEON_URSHL_scalar: mnemonic = "urshl"; break; + case NEON_SRSHL_scalar: mnemonic = "srshl"; break; + case NEON_UQRSHL_scalar: mnemonic = "uqrshl"; break; + case NEON_SQRSHL_scalar: mnemonic = "sqrshl"; break; + case NEON_SQDMULH_scalar: mnemonic = "sqdmulh"; break; + case NEON_SQRDMULH_scalar: mnemonic = "sqrdmulh"; break; + default: form = "(NEONScalar3Same)"; + } + } + Format(instr, mnemonic, nfd.SubstitutePlaceholders(form)); +} + + +void Disassembler::VisitNEONScalarByIndexedElement(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "%sd, %sn, 'Ve.%s['IVByElemIndex]"; + NEONFormatDecoder nfd(instr, NEONFormatDecoder::ScalarFormatMap()); + bool long_instr = false; + + switch (instr->Mask(NEONScalarByIndexedElementMask)) { + case NEON_SQDMULL_byelement_scalar: + mnemonic = "sqdmull"; + long_instr = true; + break; + case NEON_SQDMLAL_byelement_scalar: + mnemonic = "sqdmlal"; + long_instr = true; + break; + case NEON_SQDMLSL_byelement_scalar: + mnemonic = "sqdmlsl"; + long_instr = true; + break; + case NEON_SQDMULH_byelement_scalar: + mnemonic = "sqdmulh"; + break; + case NEON_SQRDMULH_byelement_scalar: + mnemonic = "sqrdmulh"; + break; + default: + nfd.SetFormatMap(0, nfd.FPScalarFormatMap()); + switch (instr->Mask(NEONScalarByIndexedElementFPMask)) { + case NEON_FMUL_byelement_scalar: mnemonic = "fmul"; break; + case NEON_FMLA_byelement_scalar: mnemonic = "fmla"; break; + case NEON_FMLS_byelement_scalar: mnemonic = "fmls"; break; + case NEON_FMULX_byelement_scalar: mnemonic = "fmulx"; break; + default: form = "(NEONScalarByIndexedElement)"; + } + } + + if (long_instr) { + nfd.SetFormatMap(0, nfd.LongScalarFormatMap()); + } + + Format(instr, mnemonic, nfd.Substitute( + form, nfd.kPlaceholder, nfd.kPlaceholder, nfd.kFormat)); +} + + +void Disassembler::VisitNEONScalarCopy(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(NEONScalarCopy)"; + + NEONFormatDecoder nfd(instr, NEONFormatDecoder::TriangularScalarFormatMap()); + + if (instr->Mask(NEONScalarCopyMask) == NEON_DUP_ELEMENT_scalar) { + mnemonic = "mov"; + form = "%sd, 'Vn.%s['IVInsIndex1]"; + } + + Format(instr, mnemonic, nfd.Substitute(form, nfd.kPlaceholder, nfd.kFormat)); +} + + +void Disassembler::VisitNEONScalarPairwise(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "%sd, 'Vn.%s"; + NEONFormatMap map = { {22}, {NF_2S, NF_2D} }; + NEONFormatDecoder nfd(instr, NEONFormatDecoder::FPScalarFormatMap(), &map); + + switch (instr->Mask(NEONScalarPairwiseMask)) { + case NEON_ADDP_scalar: mnemonic = "addp"; break; + case NEON_FADDP_scalar: mnemonic = "faddp"; break; + case NEON_FMAXP_scalar: mnemonic = "fmaxp"; break; + case NEON_FMAXNMP_scalar: mnemonic = "fmaxnmp"; break; + case NEON_FMINP_scalar: mnemonic = "fminp"; break; + case NEON_FMINNMP_scalar: mnemonic = "fminnmp"; break; + default: form = "(NEONScalarPairwise)"; + } + Format(instr, mnemonic, nfd.Substitute(form, + NEONFormatDecoder::kPlaceholder, NEONFormatDecoder::kFormat)); +} + + +void Disassembler::VisitNEONScalarShiftImmediate(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "%sd, %sn, 'Is1"; + const char *form_2 = "%sd, %sn, 'Is2"; + + static const NEONFormatMap map_shift = { + {22, 21, 20, 19}, + {NF_UNDEF, NF_B, NF_H, NF_H, NF_S, NF_S, NF_S, NF_S, + NF_D, NF_D, NF_D, NF_D, NF_D, NF_D, NF_D, NF_D} + }; + static const NEONFormatMap map_shift_narrow = { + {21, 20, 19}, + {NF_UNDEF, NF_H, NF_S, NF_S, NF_D, NF_D, NF_D, NF_D} + }; + NEONFormatDecoder nfd(instr, &map_shift); + + if (instr->ImmNEONImmh()) { // immh has to be non-zero. + switch (instr->Mask(NEONScalarShiftImmediateMask)) { + case NEON_FCVTZU_imm_scalar: mnemonic = "fcvtzu"; break; + case NEON_FCVTZS_imm_scalar: mnemonic = "fcvtzs"; break; + case NEON_SCVTF_imm_scalar: mnemonic = "scvtf"; break; + case NEON_UCVTF_imm_scalar: mnemonic = "ucvtf"; break; + case NEON_SRI_scalar: mnemonic = "sri"; break; + case NEON_SSHR_scalar: mnemonic = "sshr"; break; + case NEON_USHR_scalar: mnemonic = "ushr"; break; + case NEON_SRSHR_scalar: mnemonic = "srshr"; break; + case NEON_URSHR_scalar: mnemonic = "urshr"; break; + case NEON_SSRA_scalar: mnemonic = "ssra"; break; + case NEON_USRA_scalar: mnemonic = "usra"; break; + case NEON_SRSRA_scalar: mnemonic = "srsra"; break; + case NEON_URSRA_scalar: mnemonic = "ursra"; break; + case NEON_SHL_scalar: mnemonic = "shl"; form = form_2; break; + case NEON_SLI_scalar: mnemonic = "sli"; form = form_2; break; + case NEON_SQSHLU_scalar: mnemonic = "sqshlu"; form = form_2; break; + case NEON_SQSHL_imm_scalar: mnemonic = "sqshl"; form = form_2; break; + case NEON_UQSHL_imm_scalar: mnemonic = "uqshl"; form = form_2; break; + case NEON_UQSHRN_scalar: + mnemonic = "uqshrn"; + nfd.SetFormatMap(1, &map_shift_narrow); + break; + case NEON_UQRSHRN_scalar: + mnemonic = "uqrshrn"; + nfd.SetFormatMap(1, &map_shift_narrow); + break; + case NEON_SQSHRN_scalar: + mnemonic = "sqshrn"; + nfd.SetFormatMap(1, &map_shift_narrow); + break; + case NEON_SQRSHRN_scalar: + mnemonic = "sqrshrn"; + nfd.SetFormatMap(1, &map_shift_narrow); + break; + case NEON_SQSHRUN_scalar: + mnemonic = "sqshrun"; + nfd.SetFormatMap(1, &map_shift_narrow); + break; + case NEON_SQRSHRUN_scalar: + mnemonic = "sqrshrun"; + nfd.SetFormatMap(1, &map_shift_narrow); + break; + default: + form = "(NEONScalarShiftImmediate)"; + } + } else { + form = "(NEONScalarShiftImmediate)"; + } + Format(instr, mnemonic, nfd.SubstitutePlaceholders(form)); +} + + +void Disassembler::VisitNEONShiftImmediate(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Vd.%s, 'Vn.%s, 'Is1"; + const char *form_shift_2 = "'Vd.%s, 'Vn.%s, 'Is2"; + const char *form_xtl = "'Vd.%s, 'Vn.%s"; + + // 0001->8H, 001x->4S, 01xx->2D, all others undefined. + static const NEONFormatMap map_shift_ta = { + {22, 21, 20, 19}, + {NF_UNDEF, NF_8H, NF_4S, NF_4S, NF_2D, NF_2D, NF_2D, NF_2D} + }; + + // 00010->8B, 00011->16B, 001x0->4H, 001x1->8H, + // 01xx0->2S, 01xx1->4S, 1xxx1->2D, all others undefined. + static const NEONFormatMap map_shift_tb = { + {22, 21, 20, 19, 30}, + {NF_UNDEF, NF_UNDEF, NF_8B, NF_16B, NF_4H, NF_8H, NF_4H, NF_8H, + NF_2S, NF_4S, NF_2S, NF_4S, NF_2S, NF_4S, NF_2S, NF_4S, + NF_UNDEF, NF_2D, NF_UNDEF, NF_2D, NF_UNDEF, NF_2D, NF_UNDEF, NF_2D, + NF_UNDEF, NF_2D, NF_UNDEF, NF_2D, NF_UNDEF, NF_2D, NF_UNDEF, NF_2D} + }; + + NEONFormatDecoder nfd(instr, &map_shift_tb); + + if (instr->ImmNEONImmh()) { // immh has to be non-zero. + switch (instr->Mask(NEONShiftImmediateMask)) { + case NEON_SQSHLU: mnemonic = "sqshlu"; form = form_shift_2; break; + case NEON_SQSHL_imm: mnemonic = "sqshl"; form = form_shift_2; break; + case NEON_UQSHL_imm: mnemonic = "uqshl"; form = form_shift_2; break; + case NEON_SHL: mnemonic = "shl"; form = form_shift_2; break; + case NEON_SLI: mnemonic = "sli"; form = form_shift_2; break; + case NEON_SCVTF_imm: mnemonic = "scvtf"; break; + case NEON_UCVTF_imm: mnemonic = "ucvtf"; break; + case NEON_FCVTZU_imm: mnemonic = "fcvtzu"; break; + case NEON_FCVTZS_imm: mnemonic = "fcvtzs"; break; + case NEON_SRI: mnemonic = "sri"; break; + case NEON_SSHR: mnemonic = "sshr"; break; + case NEON_USHR: mnemonic = "ushr"; break; + case NEON_SRSHR: mnemonic = "srshr"; break; + case NEON_URSHR: mnemonic = "urshr"; break; + case NEON_SSRA: mnemonic = "ssra"; break; + case NEON_USRA: mnemonic = "usra"; break; + case NEON_SRSRA: mnemonic = "srsra"; break; + case NEON_URSRA: mnemonic = "ursra"; break; + case NEON_SHRN: + mnemonic = instr->Mask(NEON_Q) ? "shrn2" : "shrn"; + nfd.SetFormatMap(1, &map_shift_ta); + break; + case NEON_RSHRN: + mnemonic = instr->Mask(NEON_Q) ? "rshrn2" : "rshrn"; + nfd.SetFormatMap(1, &map_shift_ta); + break; + case NEON_UQSHRN: + mnemonic = instr->Mask(NEON_Q) ? "uqshrn2" : "uqshrn"; + nfd.SetFormatMap(1, &map_shift_ta); + break; + case NEON_UQRSHRN: + mnemonic = instr->Mask(NEON_Q) ? "uqrshrn2" : "uqrshrn"; + nfd.SetFormatMap(1, &map_shift_ta); + break; + case NEON_SQSHRN: + mnemonic = instr->Mask(NEON_Q) ? "sqshrn2" : "sqshrn"; + nfd.SetFormatMap(1, &map_shift_ta); + break; + case NEON_SQRSHRN: + mnemonic = instr->Mask(NEON_Q) ? "sqrshrn2" : "sqrshrn"; + nfd.SetFormatMap(1, &map_shift_ta); + break; + case NEON_SQSHRUN: + mnemonic = instr->Mask(NEON_Q) ? "sqshrun2" : "sqshrun"; + nfd.SetFormatMap(1, &map_shift_ta); + break; + case NEON_SQRSHRUN: + mnemonic = instr->Mask(NEON_Q) ? "sqrshrun2" : "sqrshrun"; + nfd.SetFormatMap(1, &map_shift_ta); + break; + case NEON_SSHLL: + nfd.SetFormatMap(0, &map_shift_ta); + if (instr->ImmNEONImmb() == 0 && + CountSetBits(instr->ImmNEONImmh(), 32) == 1) { // sxtl variant. + form = form_xtl; + mnemonic = instr->Mask(NEON_Q) ? "sxtl2" : "sxtl"; + } else { // sshll variant. + form = form_shift_2; + mnemonic = instr->Mask(NEON_Q) ? "sshll2" : "sshll"; + } + break; + case NEON_USHLL: + nfd.SetFormatMap(0, &map_shift_ta); + if (instr->ImmNEONImmb() == 0 && + CountSetBits(instr->ImmNEONImmh(), 32) == 1) { // uxtl variant. + form = form_xtl; + mnemonic = instr->Mask(NEON_Q) ? "uxtl2" : "uxtl"; + } else { // ushll variant. + form = form_shift_2; + mnemonic = instr->Mask(NEON_Q) ? "ushll2" : "ushll"; + } + break; + default: form = "(NEONShiftImmediate)"; + } + } else { + form = "(NEONShiftImmediate)"; + } + Format(instr, mnemonic, nfd.Substitute(form)); +} + + +void Disassembler::VisitNEONTable(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(NEONTable)"; + const char form_1v[] = "'Vd.%%s, {'Vn.16b}, 'Vm.%%s"; + const char form_2v[] = "'Vd.%%s, {'Vn.16b, v%d.16b}, 'Vm.%%s"; + const char form_3v[] = "'Vd.%%s, {'Vn.16b, v%d.16b, v%d.16b}, 'Vm.%%s"; + const char form_4v[] = + "'Vd.%%s, {'Vn.16b, v%d.16b, v%d.16b, v%d.16b}, 'Vm.%%s"; + static const NEONFormatMap map_b = { {30}, {NF_8B, NF_16B} }; + NEONFormatDecoder nfd(instr, &map_b); + + switch (instr->Mask(NEONTableMask)) { + case NEON_TBL_1v: mnemonic = "tbl"; form = form_1v; break; + case NEON_TBL_2v: mnemonic = "tbl"; form = form_2v; break; + case NEON_TBL_3v: mnemonic = "tbl"; form = form_3v; break; + case NEON_TBL_4v: mnemonic = "tbl"; form = form_4v; break; + case NEON_TBX_1v: mnemonic = "tbx"; form = form_1v; break; + case NEON_TBX_2v: mnemonic = "tbx"; form = form_2v; break; + case NEON_TBX_3v: mnemonic = "tbx"; form = form_3v; break; + case NEON_TBX_4v: mnemonic = "tbx"; form = form_4v; break; + default: break; + } + + char re_form[sizeof(form_4v) + 6]; + int reg_num = instr->Rn(); + snprintf(re_form, sizeof(re_form), form, + (reg_num + 1) % kNumberOfVRegisters, + (reg_num + 2) % kNumberOfVRegisters, + (reg_num + 3) % kNumberOfVRegisters); + + Format(instr, mnemonic, nfd.Substitute(re_form)); +} + + +void Disassembler::VisitNEONPerm(const Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Vd.%s, 'Vn.%s, 'Vm.%s"; + NEONFormatDecoder nfd(instr); + + switch (instr->Mask(NEONPermMask)) { + case NEON_TRN1: mnemonic = "trn1"; break; + case NEON_TRN2: mnemonic = "trn2"; break; + case NEON_UZP1: mnemonic = "uzp1"; break; + case NEON_UZP2: mnemonic = "uzp2"; break; + case NEON_ZIP1: mnemonic = "zip1"; break; + case NEON_ZIP2: mnemonic = "zip2"; break; + default: form = "(NEONPerm)"; + } + Format(instr, mnemonic, nfd.Substitute(form)); +} + + +void Disassembler::VisitUnimplemented(const Instruction* instr) { + Format(instr, "unimplemented", "(Unimplemented)"); +} + + +void Disassembler::VisitUnallocated(const Instruction* instr) { + Format(instr, "unallocated", "(Unallocated)"); +} + + +void Disassembler::ProcessOutput(const Instruction* /*instr*/) { + // The base disasm does nothing more than disassembling into a buffer. +} + + +void Disassembler::AppendRegisterNameToOutput(const Instruction* instr, + const CPURegister& reg) { + USE(instr); + VIXL_ASSERT(reg.IsValid()); + char reg_char; + + if (reg.IsRegister()) { + reg_char = reg.Is64Bits() ? 'x' : 'w'; + } else { + VIXL_ASSERT(reg.IsVRegister()); + switch (reg.SizeInBits()) { + case kBRegSize: reg_char = 'b'; break; + case kHRegSize: reg_char = 'h'; break; + case kSRegSize: reg_char = 's'; break; + case kDRegSize: reg_char = 'd'; break; + default: + VIXL_ASSERT(reg.Is128Bits()); + reg_char = 'q'; + } + } + + if (reg.IsVRegister() || !(reg.Aliases(sp) || reg.Aliases(xzr))) { + // A core or scalar/vector register: [wx]0 - 30, [bhsdq]0 - 31. + AppendToOutput("%c%d", reg_char, reg.code()); + } else if (reg.Aliases(sp)) { + // Disassemble w31/x31 as stack pointer wsp/sp. + AppendToOutput("%s", reg.Is64Bits() ? "sp" : "wsp"); + } else { + // Disassemble w31/x31 as zero register wzr/xzr. + AppendToOutput("%czr", reg_char); + } +} + + +void Disassembler::AppendPCRelativeOffsetToOutput(const Instruction* instr, + int64_t offset) { + USE(instr); + uint64_t abs_offset = offset; + char sign = (offset < 0) ? '-' : '+'; + if (offset < 0) { + abs_offset = -abs_offset; + } + AppendToOutput("#%c0x%" PRIx64, sign, abs_offset); +} + + +void Disassembler::AppendAddressToOutput(const Instruction* instr, + const void* addr) { + USE(instr); + AppendToOutput("(addr 0x%" PRIxPTR ")", reinterpret_cast(addr)); +} + + +void Disassembler::AppendCodeAddressToOutput(const Instruction* instr, + const void* addr) { + AppendAddressToOutput(instr, addr); +} + + +void Disassembler::AppendDataAddressToOutput(const Instruction* instr, + const void* addr) { + AppendAddressToOutput(instr, addr); +} + + +void Disassembler::AppendCodeRelativeAddressToOutput(const Instruction* instr, + const void* addr) { + USE(instr); + int64_t rel_addr = CodeRelativeAddress(addr); + if (rel_addr >= 0) { + AppendToOutput("(addr 0x%" PRIx64 ")", rel_addr); + } else { + AppendToOutput("(addr -0x%" PRIx64 ")", -rel_addr); + } +} + + +void Disassembler::AppendCodeRelativeCodeAddressToOutput( + const Instruction* instr, const void* addr) { + AppendCodeRelativeAddressToOutput(instr, addr); +} + + +void Disassembler::AppendCodeRelativeDataAddressToOutput( + const Instruction* instr, const void* addr) { + AppendCodeRelativeAddressToOutput(instr, addr); +} + + +void Disassembler::MapCodeAddress(int64_t base_address, + const Instruction* instr_address) { + set_code_address_offset( + base_address - reinterpret_cast(instr_address)); +} +int64_t Disassembler::CodeRelativeAddress(const void* addr) { + return reinterpret_cast(addr) + code_address_offset(); +} + + +void Disassembler::Format(const Instruction* instr, const char* mnemonic, + const char* format) { + VIXL_ASSERT(mnemonic != NULL); + ResetOutput(); + Substitute(instr, mnemonic); + if (format != NULL) { + VIXL_ASSERT(buffer_pos_ < buffer_size_); + buffer_[buffer_pos_++] = ' '; + Substitute(instr, format); + } + VIXL_ASSERT(buffer_pos_ < buffer_size_); + buffer_[buffer_pos_] = 0; + ProcessOutput(instr); +} + + +void Disassembler::Substitute(const Instruction* instr, const char* string) { + char chr = *string++; + while (chr != '\0') { + if (chr == '\'') { + string += SubstituteField(instr, string); + } else { + VIXL_ASSERT(buffer_pos_ < buffer_size_); + buffer_[buffer_pos_++] = chr; + } + chr = *string++; + } +} + + +int Disassembler::SubstituteField(const Instruction* instr, + const char* format) { + switch (format[0]) { + // NB. The remaining substitution prefix characters are: GJKUZ. + case 'R': // Register. X or W, selected by sf bit. + case 'F': // FP register. S or D, selected by type field. + case 'V': // Vector register, V, vector format. + case 'W': + case 'X': + case 'B': + case 'H': + case 'S': + case 'D': + case 'Q': return SubstituteRegisterField(instr, format); + case 'I': return SubstituteImmediateField(instr, format); + case 'L': return SubstituteLiteralField(instr, format); + case 'N': return SubstituteShiftField(instr, format); + case 'P': return SubstitutePrefetchField(instr, format); + case 'C': return SubstituteConditionField(instr, format); + case 'E': return SubstituteExtendField(instr, format); + case 'A': return SubstitutePCRelAddressField(instr, format); + case 'T': return SubstituteBranchTargetField(instr, format); + case 'O': return SubstituteLSRegOffsetField(instr, format); + case 'M': return SubstituteBarrierField(instr, format); + case 'K': return SubstituteCrField(instr, format); + case 'G': return SubstituteSysOpField(instr, format); + default: { + VIXL_UNREACHABLE(); + return 1; + } + } +} + + +int Disassembler::SubstituteRegisterField(const Instruction* instr, + const char* format) { + char reg_prefix = format[0]; + unsigned reg_num = 0; + unsigned field_len = 2; + + switch (format[1]) { + case 'd': + reg_num = instr->Rd(); + if (format[2] == 'q') { + reg_prefix = instr->NEONQ() ? 'X' : 'W'; + field_len = 3; + } + break; + case 'n': reg_num = instr->Rn(); break; + case 'm': + reg_num = instr->Rm(); + switch (format[2]) { + // Handle registers tagged with b (bytes), z (instruction), or + // r (registers), used for address updates in + // NEON load/store instructions. + case 'r': + case 'b': + case 'z': { + field_len = 3; + char* eimm; + int imm = static_cast(strtol(&format[3], &eimm, 10)); + field_len += eimm - &format[3]; + if (reg_num == 31) { + switch (format[2]) { + case 'z': + imm *= (1 << instr->NEONLSSize()); + break; + case 'r': + imm *= (instr->NEONQ() == 0) ? kDRegSizeInBytes + : kQRegSizeInBytes; + break; + case 'b': + break; + } + AppendToOutput("#%d", imm); + return field_len; + } + break; + } + } + break; + case 'e': + // This is register Rm, but using a 4-bit specifier. Used in NEON + // by-element instructions. + reg_num = (instr->Rm() & 0xf); + break; + case 'a': reg_num = instr->Ra(); break; + case 's': reg_num = instr->Rs(); break; + case 't': + reg_num = instr->Rt(); + if (format[0] == 'V') { + if ((format[2] >= '2') && (format[2] <= '4')) { + // Handle consecutive vector register specifiers Vt2, Vt3 and Vt4. + reg_num = (reg_num + format[2] - '1') % 32; + field_len = 3; + } + } else { + if (format[2] == '2') { + // Handle register specifier Rt2. + reg_num = instr->Rt2(); + field_len = 3; + } + } + break; + default: VIXL_UNREACHABLE(); + } + + // Increase field length for registers tagged as stack. + if (format[2] == 's') { + field_len = 3; + } + + CPURegister::RegisterType reg_type = CPURegister::kRegister; + unsigned reg_size = kXRegSize; + + if (reg_prefix == 'R') { + reg_prefix = instr->SixtyFourBits() ? 'X' : 'W'; + } else if (reg_prefix == 'F') { + reg_prefix = ((instr->FPType() & 1) == 0) ? 'S' : 'D'; + } + + switch (reg_prefix) { + case 'W': + reg_type = CPURegister::kRegister; reg_size = kWRegSize; break; + case 'X': + reg_type = CPURegister::kRegister; reg_size = kXRegSize; break; + case 'B': + reg_type = CPURegister::kVRegister; reg_size = kBRegSize; break; + case 'H': + reg_type = CPURegister::kVRegister; reg_size = kHRegSize; break; + case 'S': + reg_type = CPURegister::kVRegister; reg_size = kSRegSize; break; + case 'D': + reg_type = CPURegister::kVRegister; reg_size = kDRegSize; break; + case 'Q': + reg_type = CPURegister::kVRegister; reg_size = kQRegSize; break; + case 'V': + AppendToOutput("v%d", reg_num); + return field_len; + default: + VIXL_UNREACHABLE(); + } + + if ((reg_type == CPURegister::kRegister) && + (reg_num == kZeroRegCode) && (format[2] == 's')) { + reg_num = kSPRegInternalCode; + } + + AppendRegisterNameToOutput(instr, CPURegister(reg_num, reg_size, reg_type)); + + return field_len; +} + + +int Disassembler::SubstituteImmediateField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(format[0] == 'I'); + + switch (format[1]) { + case 'M': { // IMoveImm, IMoveNeg or IMoveLSL. + if (format[5] == 'L') { + AppendToOutput("#0x%" PRIx32, instr->ImmMoveWide()); + if (instr->ShiftMoveWide() > 0) { + AppendToOutput(", lsl #%" PRId32, 16 * instr->ShiftMoveWide()); + } + } else { + VIXL_ASSERT((format[5] == 'I') || (format[5] == 'N')); + uint64_t imm = static_cast(instr->ImmMoveWide()) << + (16 * instr->ShiftMoveWide()); + if (format[5] == 'N') + imm = ~imm; + if (!instr->SixtyFourBits()) + imm &= UINT64_C(0xffffffff); + AppendToOutput("#0x%" PRIx64, imm); + } + return 8; + } + case 'L': { + switch (format[2]) { + case 'L': { // ILLiteral - Immediate Load Literal. + AppendToOutput("pc%+" PRId32, + instr->ImmLLiteral() << kLiteralEntrySizeLog2); + return 9; + } + case 'S': { // ILS - Immediate Load/Store. + if (instr->ImmLS() != 0) { + AppendToOutput(", #%" PRId32, instr->ImmLS()); + } + return 3; + } + case 'P': { // ILPx - Immediate Load/Store Pair, x = access size. + if (instr->ImmLSPair() != 0) { + // format[3] is the scale value. Convert to a number. + int scale = 1 << (format[3] - '0'); + AppendToOutput(", #%" PRId32, instr->ImmLSPair() * scale); + } + return 4; + } + case 'U': { // ILU - Immediate Load/Store Unsigned. + if (instr->ImmLSUnsigned() != 0) { + int shift = instr->SizeLS(); + AppendToOutput(", #%" PRId32, instr->ImmLSUnsigned() << shift); + } + return 3; + } + } + } + case 'C': { // ICondB - Immediate Conditional Branch. + int64_t offset = instr->ImmCondBranch() << 2; + AppendPCRelativeOffsetToOutput(instr, offset); + return 6; + } + case 'A': { // IAddSub. + VIXL_ASSERT(instr->ShiftAddSub() <= 1); + int64_t imm = instr->ImmAddSub() << (12 * instr->ShiftAddSub()); + AppendToOutput("#0x%" PRIx64 " (%" PRId64 ")", imm, imm); + return 7; + } + case 'F': { // IFPSingle, IFPDouble or IFPFBits. + if (format[3] == 'F') { // IFPFbits. + AppendToOutput("#%" PRId32, 64 - instr->FPScale()); + return 8; + } else { + AppendToOutput("#0x%" PRIx32 " (%.4f)", instr->ImmFP(), + format[3] == 'S' ? instr->ImmFP32() : instr->ImmFP64()); + return 9; + } + } + case 'T': { // ITri - Immediate Triangular Encoded. + AppendToOutput("#0x%" PRIx64, instr->ImmLogical()); + return 4; + } + case 'N': { // INzcv. + int nzcv = (instr->Nzcv() << Flags_offset); + AppendToOutput("#%c%c%c%c", ((nzcv & NFlag) == 0) ? 'n' : 'N', + ((nzcv & ZFlag) == 0) ? 'z' : 'Z', + ((nzcv & CFlag) == 0) ? 'c' : 'C', + ((nzcv & VFlag) == 0) ? 'v' : 'V'); + return 5; + } + case 'P': { // IP - Conditional compare. + AppendToOutput("#%" PRId32, instr->ImmCondCmp()); + return 2; + } + case 'B': { // Bitfields. + return SubstituteBitfieldImmediateField(instr, format); + } + case 'E': { // IExtract. + AppendToOutput("#%" PRId32, instr->ImmS()); + return 8; + } + case 'S': { // IS - Test and branch bit. + AppendToOutput("#%" PRId32, (instr->ImmTestBranchBit5() << 5) | + instr->ImmTestBranchBit40()); + return 2; + } + case 's': { // Is - Shift (immediate). + switch (format[2]) { + case '1': { // Is1 - SSHR. + int shift = 16 << HighestSetBitPosition(instr->ImmNEONImmh()); + shift -= instr->ImmNEONImmhImmb(); + AppendToOutput("#%d", shift); + return 3; + } + case '2': { // Is2 - SLI. + int shift = instr->ImmNEONImmhImmb(); + shift -= 8 << HighestSetBitPosition(instr->ImmNEONImmh()); + AppendToOutput("#%d", shift); + return 3; + } + default: { + VIXL_UNIMPLEMENTED(); + return 0; + } + } + } + case 'D': { // IDebug - HLT and BRK instructions. + AppendToOutput("#0x%" PRIx32, instr->ImmException()); + return 6; + } + case 'V': { // Immediate Vector. + switch (format[2]) { + case 'E': { // IVExtract. + AppendToOutput("#%" PRId32, instr->ImmNEONExt()); + return 9; + } + case 'B': { // IVByElemIndex. + int vm_index = (instr->NEONH() << 1) | instr->NEONL(); + if (instr->NEONSize() == 1) { + vm_index = (vm_index << 1) | instr->NEONM(); + } + AppendToOutput("%d", vm_index); + return strlen("IVByElemIndex"); + } + case 'I': { // INS element. + if (strncmp(format, "IVInsIndex", strlen("IVInsIndex")) == 0) { + int rd_index, rn_index; + int imm5 = instr->ImmNEON5(); + int imm4 = instr->ImmNEON4(); + int tz = CountTrailingZeros(imm5, 32); + rd_index = imm5 >> (tz + 1); + rn_index = imm4 >> tz; + if (strncmp(format, "IVInsIndex1", strlen("IVInsIndex1")) == 0) { + AppendToOutput("%d", rd_index); + return strlen("IVInsIndex1"); + } else if (strncmp(format, "IVInsIndex2", + strlen("IVInsIndex2")) == 0) { + AppendToOutput("%d", rn_index); + return strlen("IVInsIndex2"); + } else { + VIXL_UNIMPLEMENTED(); + return 0; + } + } + VIXL_FALLTHROUGH(); + } + case 'L': { // IVLSLane[0123] - suffix indicates access size shift. + AppendToOutput("%d", instr->NEONLSIndex(format[8] - '0')); + return 9; + } + case 'M': { // Modified Immediate cases. + if (strncmp(format, + "IVMIImmFPSingle", + strlen("IVMIImmFPSingle")) == 0) { + AppendToOutput("#0x%" PRIx32 " (%.4f)", instr->ImmNEONabcdefgh(), + instr->ImmNEONFP32()); + return strlen("IVMIImmFPSingle"); + } else if (strncmp(format, + "IVMIImmFPDouble", + strlen("IVMIImmFPDouble")) == 0) { + AppendToOutput("#0x%" PRIx32 " (%.4f)", instr->ImmNEONabcdefgh(), + instr->ImmNEONFP64()); + return strlen("IVMIImmFPDouble"); + } else if (strncmp(format, "IVMIImm8", strlen("IVMIImm8")) == 0) { + uint64_t imm8 = instr->ImmNEONabcdefgh(); + AppendToOutput("#0x%" PRIx64, imm8); + return strlen("IVMIImm8"); + } else if (strncmp(format, "IVMIImm", strlen("IVMIImm")) == 0) { + uint64_t imm8 = instr->ImmNEONabcdefgh(); + uint64_t imm = 0; + for (int i = 0; i < 8; ++i) { + if (imm8 & (1 << i)) { + imm |= (UINT64_C(0xff) << (8 * i)); + } + } + AppendToOutput("#0x%" PRIx64, imm); + return strlen("IVMIImm"); + } else if (strncmp(format, "IVMIShiftAmt1", + strlen("IVMIShiftAmt1")) == 0) { + int cmode = instr->NEONCmode(); + int shift_amount = 8 * ((cmode >> 1) & 3); + AppendToOutput("#%d", shift_amount); + return strlen("IVMIShiftAmt1"); + } else if (strncmp(format, "IVMIShiftAmt2", + strlen("IVMIShiftAmt2")) == 0) { + int cmode = instr->NEONCmode(); + int shift_amount = 8 << (cmode & 1); + AppendToOutput("#%d", shift_amount); + return strlen("IVMIShiftAmt2"); + } else { + VIXL_UNIMPLEMENTED(); + return 0; + } + } + default: { + VIXL_UNIMPLEMENTED(); + return 0; + } + } + } + case 'X': { // IX - CLREX instruction. + AppendToOutput("#0x%" PRIx32, instr->CRm()); + return 2; + } + default: { + VIXL_UNIMPLEMENTED(); + return 0; + } + } +} + + +int Disassembler::SubstituteBitfieldImmediateField(const Instruction* instr, + const char* format) { + VIXL_ASSERT((format[0] == 'I') && (format[1] == 'B')); + unsigned r = instr->ImmR(); + unsigned s = instr->ImmS(); + + switch (format[2]) { + case 'r': { // IBr. + AppendToOutput("#%d", r); + return 3; + } + case 's': { // IBs+1 or IBs-r+1. + if (format[3] == '+') { + AppendToOutput("#%d", s + 1); + return 5; + } else { + VIXL_ASSERT(format[3] == '-'); + AppendToOutput("#%d", s - r + 1); + return 7; + } + } + case 'Z': { // IBZ-r. + VIXL_ASSERT((format[3] == '-') && (format[4] == 'r')); + unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSize : kWRegSize; + AppendToOutput("#%d", reg_size - r); + return 5; + } + default: { + VIXL_UNREACHABLE(); + return 0; + } + } +} + + +int Disassembler::SubstituteLiteralField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(strncmp(format, "LValue", 6) == 0); + USE(format); + + const void * address = instr->LiteralAddress(); + switch (instr->Mask(LoadLiteralMask)) { + case LDR_w_lit: + case LDR_x_lit: + case LDRSW_x_lit: + case LDR_s_lit: + case LDR_d_lit: + case LDR_q_lit: + AppendCodeRelativeDataAddressToOutput(instr, address); + break; + case PRFM_lit: { + // Use the prefetch hint to decide how to print the address. + switch (instr->PrefetchHint()) { + case 0x0: // PLD: prefetch for load. + case 0x2: // PST: prepare for store. + AppendCodeRelativeDataAddressToOutput(instr, address); + break; + case 0x1: // PLI: preload instructions. + AppendCodeRelativeCodeAddressToOutput(instr, address); + break; + case 0x3: // Unallocated hint. + AppendCodeRelativeAddressToOutput(instr, address); + break; + } + break; + } + default: + VIXL_UNREACHABLE(); + } + + return 6; +} + + +int Disassembler::SubstituteShiftField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(format[0] == 'N'); + VIXL_ASSERT(instr->ShiftDP() <= 0x3); + + switch (format[1]) { + case 'D': { // HDP. + VIXL_ASSERT(instr->ShiftDP() != ROR); + VIXL_FALLTHROUGH(); + } + case 'L': { // HLo. + if (instr->ImmDPShift() != 0) { + const char* shift_type[] = {"lsl", "lsr", "asr", "ror"}; + AppendToOutput(", %s #%" PRId32, shift_type[instr->ShiftDP()], + instr->ImmDPShift()); + } + return 3; + } + default: + VIXL_UNIMPLEMENTED(); + return 0; + } +} + + +int Disassembler::SubstituteConditionField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(format[0] == 'C'); + const char* condition_code[] = { "eq", "ne", "hs", "lo", + "mi", "pl", "vs", "vc", + "hi", "ls", "ge", "lt", + "gt", "le", "al", "nv" }; + int cond; + switch (format[1]) { + case 'B': cond = instr->ConditionBranch(); break; + case 'I': { + cond = InvertCondition(static_cast(instr->Condition())); + break; + } + default: cond = instr->Condition(); + } + AppendToOutput("%s", condition_code[cond]); + return 4; +} + + +int Disassembler::SubstitutePCRelAddressField(const Instruction* instr, + const char* format) { + VIXL_ASSERT((strcmp(format, "AddrPCRelByte") == 0) || // Used by `adr`. + (strcmp(format, "AddrPCRelPage") == 0)); // Used by `adrp`. + + int64_t offset = instr->ImmPCRel(); + + // Compute the target address based on the effective address (after applying + // code_address_offset). This is required for correct behaviour of adrp. + const Instruction* base = instr + code_address_offset(); + if (format[9] == 'P') { + offset *= kPageSize; + base = AlignDown(base, kPageSize); + } + // Strip code_address_offset before printing, so we can use the + // semantically-correct AppendCodeRelativeAddressToOutput. + const void* target = + reinterpret_cast(base + offset - code_address_offset()); + + AppendPCRelativeOffsetToOutput(instr, offset); + AppendToOutput(" "); + AppendCodeRelativeAddressToOutput(instr, target); + return 13; +} + + +int Disassembler::SubstituteBranchTargetField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(strncmp(format, "TImm", 4) == 0); + + int64_t offset = 0; + switch (format[5]) { + // BImmUncn - unconditional branch immediate. + case 'n': offset = instr->ImmUncondBranch(); break; + // BImmCond - conditional branch immediate. + case 'o': offset = instr->ImmCondBranch(); break; + // BImmCmpa - compare and branch immediate. + case 'm': offset = instr->ImmCmpBranch(); break; + // BImmTest - test and branch immediate. + case 'e': offset = instr->ImmTestBranch(); break; + default: VIXL_UNIMPLEMENTED(); + } + offset <<= kInstructionSizeLog2; + const void* target_address = reinterpret_cast(instr + offset); + VIXL_STATIC_ASSERT(sizeof(*instr) == 1); + + AppendPCRelativeOffsetToOutput(instr, offset); + AppendToOutput(" "); + AppendCodeRelativeCodeAddressToOutput(instr, target_address); + + return 8; +} + + +int Disassembler::SubstituteExtendField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(strncmp(format, "Ext", 3) == 0); + VIXL_ASSERT(instr->ExtendMode() <= 7); + USE(format); + + const char* extend_mode[] = { "uxtb", "uxth", "uxtw", "uxtx", + "sxtb", "sxth", "sxtw", "sxtx" }; + + // If rd or rn is SP, uxtw on 32-bit registers and uxtx on 64-bit + // registers becomes lsl. + if (((instr->Rd() == kZeroRegCode) || (instr->Rn() == kZeroRegCode)) && + (((instr->ExtendMode() == UXTW) && (instr->SixtyFourBits() == 0)) || + (instr->ExtendMode() == UXTX))) { + if (instr->ImmExtendShift() > 0) { + AppendToOutput(", lsl #%" PRId32, instr->ImmExtendShift()); + } + } else { + AppendToOutput(", %s", extend_mode[instr->ExtendMode()]); + if (instr->ImmExtendShift() > 0) { + AppendToOutput(" #%" PRId32, instr->ImmExtendShift()); + } + } + return 3; +} + + +int Disassembler::SubstituteLSRegOffsetField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(strncmp(format, "Offsetreg", 9) == 0); + const char* extend_mode[] = { "undefined", "undefined", "uxtw", "lsl", + "undefined", "undefined", "sxtw", "sxtx" }; + USE(format); + + unsigned shift = instr->ImmShiftLS(); + Extend ext = static_cast(instr->ExtendMode()); + char reg_type = ((ext == UXTW) || (ext == SXTW)) ? 'w' : 'x'; + + unsigned rm = instr->Rm(); + if (rm == kZeroRegCode) { + AppendToOutput("%czr", reg_type); + } else { + AppendToOutput("%c%d", reg_type, rm); + } + + // Extend mode UXTX is an alias for shift mode LSL here. + if (!((ext == UXTX) && (shift == 0))) { + AppendToOutput(", %s", extend_mode[ext]); + if (shift != 0) { + AppendToOutput(" #%d", instr->SizeLS()); + } + } + return 9; +} + + +int Disassembler::SubstitutePrefetchField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(format[0] == 'P'); + USE(format); + + static const char* hints[] = {"ld", "li", "st"}; + static const char* stream_options[] = {"keep", "strm"}; + + unsigned hint = instr->PrefetchHint(); + unsigned target = instr->PrefetchTarget() + 1; + unsigned stream = instr->PrefetchStream(); + + if ((hint >= (sizeof(hints) / sizeof(hints[0]))) || (target > 3)) { + // Unallocated prefetch operations. + int prefetch_mode = instr->ImmPrefetchOperation(); + AppendToOutput("#0b%c%c%c%c%c", + (prefetch_mode & (1 << 4)) ? '1' : '0', + (prefetch_mode & (1 << 3)) ? '1' : '0', + (prefetch_mode & (1 << 2)) ? '1' : '0', + (prefetch_mode & (1 << 1)) ? '1' : '0', + (prefetch_mode & (1 << 0)) ? '1' : '0'); + } else { + VIXL_ASSERT(stream < (sizeof(stream_options) / sizeof(stream_options[0]))); + AppendToOutput("p%sl%d%s", hints[hint], target, stream_options[stream]); + } + return 6; +} + +int Disassembler::SubstituteBarrierField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(format[0] == 'M'); + USE(format); + + static const char* options[4][4] = { + { "sy (0b0000)", "oshld", "oshst", "osh" }, + { "sy (0b0100)", "nshld", "nshst", "nsh" }, + { "sy (0b1000)", "ishld", "ishst", "ish" }, + { "sy (0b1100)", "ld", "st", "sy" } + }; + int domain = instr->ImmBarrierDomain(); + int type = instr->ImmBarrierType(); + + AppendToOutput("%s", options[domain][type]); + return 1; +} + +int Disassembler::SubstituteSysOpField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(format[0] == 'G'); + int op = -1; + switch (format[1]) { + case '1': op = instr->SysOp1(); break; + case '2': op = instr->SysOp2(); break; + default: + VIXL_UNREACHABLE(); + } + AppendToOutput("#%d", op); + return 2; +} + +int Disassembler::SubstituteCrField(const Instruction* instr, + const char* format) { + VIXL_ASSERT(format[0] == 'K'); + int cr = -1; + switch (format[1]) { + case 'n': cr = instr->CRn(); break; + case 'm': cr = instr->CRm(); break; + default: + VIXL_UNREACHABLE(); + } + AppendToOutput("C%d", cr); + return 2; +} + +void Disassembler::ResetOutput() { + buffer_pos_ = 0; + buffer_[buffer_pos_] = 0; +} + + +void Disassembler::AppendToOutput(const char* format, ...) { + va_list args; + va_start(args, format); + buffer_pos_ += vsnprintf(&buffer_[buffer_pos_], buffer_size_ - buffer_pos_, + format, args); + va_end(args); +} + + +void PrintDisassembler::ProcessOutput(const Instruction* instr) { + fprintf(stream_, "0x%016" PRIx64 " %08" PRIx32 "\t\t%s\n", + reinterpret_cast(instr), + instr->InstructionBits(), + GetOutput()); +} + +} // namespace vixl diff --git a/qemu/disas/libvixl/vixl/a64/disasm-a64.h b/qemu/disas/libvixl/vixl/a64/disasm-a64.h new file mode 100644 index 000000000..930df6ea6 --- /dev/null +++ b/qemu/disas/libvixl/vixl/a64/disasm-a64.h @@ -0,0 +1,177 @@ +// Copyright 2015, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_A64_DISASM_A64_H +#define VIXL_A64_DISASM_A64_H + +#include "vixl/globals.h" +#include "vixl/utils.h" +#include "vixl/a64/instructions-a64.h" +#include "vixl/a64/decoder-a64.h" +#include "vixl/a64/assembler-a64.h" + +namespace vixl { + +class Disassembler: public DecoderVisitor { + public: + Disassembler(); + Disassembler(char* text_buffer, int buffer_size); + virtual ~Disassembler(); + char* GetOutput(); + + // Declare all Visitor functions. + #define DECLARE(A) virtual void Visit##A(const Instruction* instr); + VISITOR_LIST(DECLARE) + #undef DECLARE + + protected: + virtual void ProcessOutput(const Instruction* instr); + + // Default output functions. The functions below implement a default way of + // printing elements in the disassembly. A sub-class can override these to + // customize the disassembly output. + + // Prints the name of a register. + // TODO: This currently doesn't allow renaming of V registers. + virtual void AppendRegisterNameToOutput(const Instruction* instr, + const CPURegister& reg); + + // Prints a PC-relative offset. This is used for example when disassembling + // branches to immediate offsets. + virtual void AppendPCRelativeOffsetToOutput(const Instruction* instr, + int64_t offset); + + // Prints an address, in the general case. It can be code or data. This is + // used for example to print the target address of an ADR instruction. + virtual void AppendCodeRelativeAddressToOutput(const Instruction* instr, + const void* addr); + + // Prints the address of some code. + // This is used for example to print the target address of a branch to an + // immediate offset. + // A sub-class can for example override this method to lookup the address and + // print an appropriate name. + virtual void AppendCodeRelativeCodeAddressToOutput(const Instruction* instr, + const void* addr); + + // Prints the address of some data. + // This is used for example to print the source address of a load literal + // instruction. + virtual void AppendCodeRelativeDataAddressToOutput(const Instruction* instr, + const void* addr); + + // Same as the above, but for addresses that are not relative to the code + // buffer. They are currently not used by VIXL. + virtual void AppendAddressToOutput(const Instruction* instr, + const void* addr); + virtual void AppendCodeAddressToOutput(const Instruction* instr, + const void* addr); + virtual void AppendDataAddressToOutput(const Instruction* instr, + const void* addr); + + public: + // Get/Set the offset that should be added to code addresses when printing + // code-relative addresses in the AppendCodeRelativeAddressToOutput() + // helpers. + // Below is an example of how a branch immediate instruction in memory at + // address 0xb010200 would disassemble with different offsets. + // Base address | Disassembly + // 0x0 | 0xb010200: b #+0xcc (addr 0xb0102cc) + // 0x10000 | 0xb000200: b #+0xcc (addr 0xb0002cc) + // 0xb010200 | 0x0: b #+0xcc (addr 0xcc) + void MapCodeAddress(int64_t base_address, const Instruction* instr_address); + int64_t CodeRelativeAddress(const void* instr); + + private: + void Format( + const Instruction* instr, const char* mnemonic, const char* format); + void Substitute(const Instruction* instr, const char* string); + int SubstituteField(const Instruction* instr, const char* format); + int SubstituteRegisterField(const Instruction* instr, const char* format); + int SubstituteImmediateField(const Instruction* instr, const char* format); + int SubstituteLiteralField(const Instruction* instr, const char* format); + int SubstituteBitfieldImmediateField( + const Instruction* instr, const char* format); + int SubstituteShiftField(const Instruction* instr, const char* format); + int SubstituteExtendField(const Instruction* instr, const char* format); + int SubstituteConditionField(const Instruction* instr, const char* format); + int SubstitutePCRelAddressField(const Instruction* instr, const char* format); + int SubstituteBranchTargetField(const Instruction* instr, const char* format); + int SubstituteLSRegOffsetField(const Instruction* instr, const char* format); + int SubstitutePrefetchField(const Instruction* instr, const char* format); + int SubstituteBarrierField(const Instruction* instr, const char* format); + int SubstituteSysOpField(const Instruction* instr, const char* format); + int SubstituteCrField(const Instruction* instr, const char* format); + bool RdIsZROrSP(const Instruction* instr) const { + return (instr->Rd() == kZeroRegCode); + } + + bool RnIsZROrSP(const Instruction* instr) const { + return (instr->Rn() == kZeroRegCode); + } + + bool RmIsZROrSP(const Instruction* instr) const { + return (instr->Rm() == kZeroRegCode); + } + + bool RaIsZROrSP(const Instruction* instr) const { + return (instr->Ra() == kZeroRegCode); + } + + bool IsMovzMovnImm(unsigned reg_size, uint64_t value); + + int64_t code_address_offset() const { return code_address_offset_; } + + protected: + void ResetOutput(); + void AppendToOutput(const char* string, ...) PRINTF_CHECK(2, 3); + + void set_code_address_offset(int64_t code_address_offset) { + code_address_offset_ = code_address_offset; + } + + char* buffer_; + uint32_t buffer_pos_; + uint32_t buffer_size_; + bool own_buffer_; + + int64_t code_address_offset_; +}; + + +class PrintDisassembler: public Disassembler { + public: + explicit PrintDisassembler(FILE* stream) : stream_(stream) { } + + protected: + virtual void ProcessOutput(const Instruction* instr); + + private: + FILE *stream_; +}; +} // namespace vixl + +#endif // VIXL_A64_DISASM_A64_H diff --git a/qemu/disas/libvixl/vixl/a64/instructions-a64.cc b/qemu/disas/libvixl/vixl/a64/instructions-a64.cc new file mode 100644 index 000000000..33992f88a --- /dev/null +++ b/qemu/disas/libvixl/vixl/a64/instructions-a64.cc @@ -0,0 +1,622 @@ +// Copyright 2015, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "vixl/a64/instructions-a64.h" +#include "vixl/a64/assembler-a64.h" + +namespace vixl { + + +// Floating-point infinity values. +const float16 kFP16PositiveInfinity = 0x7c00; +const float16 kFP16NegativeInfinity = 0xfc00; +const float kFP32PositiveInfinity = rawbits_to_float(0x7f800000); +const float kFP32NegativeInfinity = rawbits_to_float(0xff800000); +const double kFP64PositiveInfinity = + rawbits_to_double(UINT64_C(0x7ff0000000000000)); +const double kFP64NegativeInfinity = + rawbits_to_double(UINT64_C(0xfff0000000000000)); + + +// The default NaN values (for FPCR.DN=1). +const double kFP64DefaultNaN = rawbits_to_double(UINT64_C(0x7ff8000000000000)); +const float kFP32DefaultNaN = rawbits_to_float(0x7fc00000); +const float16 kFP16DefaultNaN = 0x7e00; + + +static uint64_t RotateRight(uint64_t value, + unsigned int rotate, + unsigned int width) { + VIXL_ASSERT(width <= 64); + rotate &= 63; + return ((value & ((UINT64_C(1) << rotate) - 1)) << + (width - rotate)) | (value >> rotate); +} + + +static uint64_t RepeatBitsAcrossReg(unsigned reg_size, + uint64_t value, + unsigned width) { + VIXL_ASSERT((width == 2) || (width == 4) || (width == 8) || (width == 16) || + (width == 32)); + VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); + uint64_t result = value & ((UINT64_C(1) << width) - 1); + for (unsigned i = width; i < reg_size; i *= 2) { + result |= (result << i); + } + return result; +} + + +bool Instruction::IsLoad() const { + if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) { + return false; + } + + if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) { + return Mask(LoadStorePairLBit) != 0; + } else { + LoadStoreOp op = static_cast(Mask(LoadStoreMask)); + switch (op) { + case LDRB_w: + case LDRH_w: + case LDR_w: + case LDR_x: + case LDRSB_w: + case LDRSB_x: + case LDRSH_w: + case LDRSH_x: + case LDRSW_x: + case LDR_b: + case LDR_h: + case LDR_s: + case LDR_d: + case LDR_q: return true; + default: return false; + } + } +} + + +bool Instruction::IsStore() const { + if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) { + return false; + } + + if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) { + return Mask(LoadStorePairLBit) == 0; + } else { + LoadStoreOp op = static_cast(Mask(LoadStoreMask)); + switch (op) { + case STRB_w: + case STRH_w: + case STR_w: + case STR_x: + case STR_b: + case STR_h: + case STR_s: + case STR_d: + case STR_q: return true; + default: return false; + } + } +} + + +// Logical immediates can't encode zero, so a return value of zero is used to +// indicate a failure case. Specifically, where the constraints on imm_s are +// not met. +uint64_t Instruction::ImmLogical() const { + unsigned reg_size = SixtyFourBits() ? kXRegSize : kWRegSize; + int32_t n = BitN(); + int32_t imm_s = ImmSetBits(); + int32_t imm_r = ImmRotate(); + + // An integer is constructed from the n, imm_s and imm_r bits according to + // the following table: + // + // N imms immr size S R + // 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr) + // 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr) + // 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr) + // 0 110sss xxxrrr 8 UInt(sss) UInt(rrr) + // 0 1110ss xxxxrr 4 UInt(ss) UInt(rr) + // 0 11110s xxxxxr 2 UInt(s) UInt(r) + // (s bits must not be all set) + // + // A pattern is constructed of size bits, where the least significant S+1 + // bits are set. The pattern is rotated right by R, and repeated across a + // 32 or 64-bit value, depending on destination register width. + // + + if (n == 1) { + if (imm_s == 0x3f) { + return 0; + } + uint64_t bits = (UINT64_C(1) << (imm_s + 1)) - 1; + return RotateRight(bits, imm_r, 64); + } else { + if ((imm_s >> 1) == 0x1f) { + return 0; + } + for (int width = 0x20; width >= 0x2; width >>= 1) { + if ((imm_s & width) == 0) { + int mask = width - 1; + if ((imm_s & mask) == mask) { + return 0; + } + uint64_t bits = (UINT64_C(1) << ((imm_s & mask) + 1)) - 1; + return RepeatBitsAcrossReg(reg_size, + RotateRight(bits, imm_r & mask, width), + width); + } + } + } + VIXL_UNREACHABLE(); + return 0; +} + + +uint32_t Instruction::ImmNEONabcdefgh() const { + return ImmNEONabc() << 5 | ImmNEONdefgh(); +} + + +float Instruction::Imm8ToFP32(uint32_t imm8) { + // Imm8: abcdefgh (8 bits) + // Single: aBbb.bbbc.defg.h000.0000.0000.0000.0000 (32 bits) + // where B is b ^ 1 + uint32_t bits = imm8; + uint32_t bit7 = (bits >> 7) & 0x1; + uint32_t bit6 = (bits >> 6) & 0x1; + uint32_t bit5_to_0 = bits & 0x3f; + uint32_t result = (bit7 << 31) | ((32 - bit6) << 25) | (bit5_to_0 << 19); + + return rawbits_to_float(result); +} + + +float Instruction::ImmFP32() const { + return Imm8ToFP32(ImmFP()); +} + + +double Instruction::Imm8ToFP64(uint32_t imm8) { + // Imm8: abcdefgh (8 bits) + // Double: aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000 + // 0000.0000.0000.0000.0000.0000.0000.0000 (64 bits) + // where B is b ^ 1 + uint32_t bits = imm8; + uint64_t bit7 = (bits >> 7) & 0x1; + uint64_t bit6 = (bits >> 6) & 0x1; + uint64_t bit5_to_0 = bits & 0x3f; + uint64_t result = (bit7 << 63) | ((256 - bit6) << 54) | (bit5_to_0 << 48); + + return rawbits_to_double(result); +} + + +double Instruction::ImmFP64() const { + return Imm8ToFP64(ImmFP()); +} + + +float Instruction::ImmNEONFP32() const { + return Imm8ToFP32(ImmNEONabcdefgh()); +} + + +double Instruction::ImmNEONFP64() const { + return Imm8ToFP64(ImmNEONabcdefgh()); +} + + +unsigned CalcLSDataSize(LoadStoreOp op) { + VIXL_ASSERT((LSSize_offset + LSSize_width) == (kInstructionSize * 8)); + unsigned size = static_cast(op) >> LSSize_offset; + if ((op & LSVector_mask) != 0) { + // Vector register memory operations encode the access size in the "size" + // and "opc" fields. + if ((size == 0) && ((op & LSOpc_mask) >> LSOpc_offset) >= 2) { + size = kQRegSizeInBytesLog2; + } + } + return size; +} + + +unsigned CalcLSPairDataSize(LoadStorePairOp op) { + VIXL_STATIC_ASSERT(kXRegSizeInBytes == kDRegSizeInBytes); + VIXL_STATIC_ASSERT(kWRegSizeInBytes == kSRegSizeInBytes); + switch (op) { + case STP_q: + case LDP_q: return kQRegSizeInBytesLog2; + case STP_x: + case LDP_x: + case STP_d: + case LDP_d: return kXRegSizeInBytesLog2; + default: return kWRegSizeInBytesLog2; + } +} + + +int Instruction::ImmBranchRangeBitwidth(ImmBranchType branch_type) { + switch (branch_type) { + case UncondBranchType: + return ImmUncondBranch_width; + case CondBranchType: + return ImmCondBranch_width; + case CompareBranchType: + return ImmCmpBranch_width; + case TestBranchType: + return ImmTestBranch_width; + default: + VIXL_UNREACHABLE(); + return 0; + } +} + + +int32_t Instruction::ImmBranchForwardRange(ImmBranchType branch_type) { + int32_t encoded_max = 1 << (ImmBranchRangeBitwidth(branch_type) - 1); + return encoded_max * kInstructionSize; +} + + +bool Instruction::IsValidImmPCOffset(ImmBranchType branch_type, + int64_t offset) { + return is_intn(ImmBranchRangeBitwidth(branch_type), offset); +} + + +const Instruction* Instruction::ImmPCOffsetTarget() const { + const Instruction * base = this; + ptrdiff_t offset; + if (IsPCRelAddressing()) { + // ADR and ADRP. + offset = ImmPCRel(); + if (Mask(PCRelAddressingMask) == ADRP) { + base = AlignDown(base, kPageSize); + offset *= kPageSize; + } else { + VIXL_ASSERT(Mask(PCRelAddressingMask) == ADR); + } + } else { + // All PC-relative branches. + VIXL_ASSERT(BranchType() != UnknownBranchType); + // Relative branch offsets are instruction-size-aligned. + offset = ImmBranch() << kInstructionSizeLog2; + } + return base + offset; +} + + +int Instruction::ImmBranch() const { + switch (BranchType()) { + case CondBranchType: return ImmCondBranch(); + case UncondBranchType: return ImmUncondBranch(); + case CompareBranchType: return ImmCmpBranch(); + case TestBranchType: return ImmTestBranch(); + default: VIXL_UNREACHABLE(); + } + return 0; +} + + +void Instruction::SetImmPCOffsetTarget(const Instruction* target) { + if (IsPCRelAddressing()) { + SetPCRelImmTarget(target); + } else { + SetBranchImmTarget(target); + } +} + + +void Instruction::SetPCRelImmTarget(const Instruction* target) { + ptrdiff_t imm21; + if ((Mask(PCRelAddressingMask) == ADR)) { + imm21 = target - this; + } else { + VIXL_ASSERT(Mask(PCRelAddressingMask) == ADRP); + uintptr_t this_page = reinterpret_cast(this) / kPageSize; + uintptr_t target_page = reinterpret_cast(target) / kPageSize; + imm21 = target_page - this_page; + } + Instr imm = Assembler::ImmPCRelAddress(static_cast(imm21)); + + SetInstructionBits(Mask(~ImmPCRel_mask) | imm); +} + + +void Instruction::SetBranchImmTarget(const Instruction* target) { + VIXL_ASSERT(((target - this) & 3) == 0); + Instr branch_imm = 0; + uint32_t imm_mask = 0; + int offset = static_cast((target - this) >> kInstructionSizeLog2); + switch (BranchType()) { + case CondBranchType: { + branch_imm = Assembler::ImmCondBranch(offset); + imm_mask = ImmCondBranch_mask; + break; + } + case UncondBranchType: { + branch_imm = Assembler::ImmUncondBranch(offset); + imm_mask = ImmUncondBranch_mask; + break; + } + case CompareBranchType: { + branch_imm = Assembler::ImmCmpBranch(offset); + imm_mask = ImmCmpBranch_mask; + break; + } + case TestBranchType: { + branch_imm = Assembler::ImmTestBranch(offset); + imm_mask = ImmTestBranch_mask; + break; + } + default: VIXL_UNREACHABLE(); + } + SetInstructionBits(Mask(~imm_mask) | branch_imm); +} + + +void Instruction::SetImmLLiteral(const Instruction* source) { + VIXL_ASSERT(IsWordAligned(source)); + ptrdiff_t offset = (source - this) >> kLiteralEntrySizeLog2; + Instr imm = Assembler::ImmLLiteral(static_cast(offset)); + Instr mask = ImmLLiteral_mask; + + SetInstructionBits(Mask(~mask) | imm); +} + + +VectorFormat VectorFormatHalfWidth(const VectorFormat vform) { + VIXL_ASSERT(vform == kFormat8H || vform == kFormat4S || vform == kFormat2D || + vform == kFormatH || vform == kFormatS || vform == kFormatD); + switch (vform) { + case kFormat8H: return kFormat8B; + case kFormat4S: return kFormat4H; + case kFormat2D: return kFormat2S; + case kFormatH: return kFormatB; + case kFormatS: return kFormatH; + case kFormatD: return kFormatS; + default: VIXL_UNREACHABLE(); return kFormatUndefined; + } +} + + +VectorFormat VectorFormatDoubleWidth(const VectorFormat vform) { + VIXL_ASSERT(vform == kFormat8B || vform == kFormat4H || vform == kFormat2S || + vform == kFormatB || vform == kFormatH || vform == kFormatS); + switch (vform) { + case kFormat8B: return kFormat8H; + case kFormat4H: return kFormat4S; + case kFormat2S: return kFormat2D; + case kFormatB: return kFormatH; + case kFormatH: return kFormatS; + case kFormatS: return kFormatD; + default: VIXL_UNREACHABLE(); return kFormatUndefined; + } +} + + +VectorFormat VectorFormatFillQ(const VectorFormat vform) { + switch (vform) { + case kFormatB: + case kFormat8B: + case kFormat16B: return kFormat16B; + case kFormatH: + case kFormat4H: + case kFormat8H: return kFormat8H; + case kFormatS: + case kFormat2S: + case kFormat4S: return kFormat4S; + case kFormatD: + case kFormat1D: + case kFormat2D: return kFormat2D; + default: VIXL_UNREACHABLE(); return kFormatUndefined; + } +} + +VectorFormat VectorFormatHalfWidthDoubleLanes(const VectorFormat vform) { + switch (vform) { + case kFormat4H: return kFormat8B; + case kFormat8H: return kFormat16B; + case kFormat2S: return kFormat4H; + case kFormat4S: return kFormat8H; + case kFormat1D: return kFormat2S; + case kFormat2D: return kFormat4S; + default: VIXL_UNREACHABLE(); return kFormatUndefined; + } +} + +VectorFormat VectorFormatDoubleLanes(const VectorFormat vform) { + VIXL_ASSERT(vform == kFormat8B || vform == kFormat4H || vform == kFormat2S); + switch (vform) { + case kFormat8B: return kFormat16B; + case kFormat4H: return kFormat8H; + case kFormat2S: return kFormat4S; + default: VIXL_UNREACHABLE(); return kFormatUndefined; + } +} + + +VectorFormat VectorFormatHalfLanes(const VectorFormat vform) { + VIXL_ASSERT(vform == kFormat16B || vform == kFormat8H || vform == kFormat4S); + switch (vform) { + case kFormat16B: return kFormat8B; + case kFormat8H: return kFormat4H; + case kFormat4S: return kFormat2S; + default: VIXL_UNREACHABLE(); return kFormatUndefined; + } +} + + +VectorFormat ScalarFormatFromLaneSize(int laneSize) { + switch (laneSize) { + case 8: return kFormatB; + case 16: return kFormatH; + case 32: return kFormatS; + case 64: return kFormatD; + default: VIXL_UNREACHABLE(); return kFormatUndefined; + } +} + + +unsigned RegisterSizeInBitsFromFormat(VectorFormat vform) { + VIXL_ASSERT(vform != kFormatUndefined); + switch (vform) { + case kFormatB: return kBRegSize; + case kFormatH: return kHRegSize; + case kFormatS: return kSRegSize; + case kFormatD: return kDRegSize; + case kFormat8B: + case kFormat4H: + case kFormat2S: + case kFormat1D: return kDRegSize; + default: return kQRegSize; + } +} + + +unsigned RegisterSizeInBytesFromFormat(VectorFormat vform) { + return RegisterSizeInBitsFromFormat(vform) / 8; +} + + +unsigned LaneSizeInBitsFromFormat(VectorFormat vform) { + VIXL_ASSERT(vform != kFormatUndefined); + switch (vform) { + case kFormatB: + case kFormat8B: + case kFormat16B: return 8; + case kFormatH: + case kFormat4H: + case kFormat8H: return 16; + case kFormatS: + case kFormat2S: + case kFormat4S: return 32; + case kFormatD: + case kFormat1D: + case kFormat2D: return 64; + default: VIXL_UNREACHABLE(); return 0; + } +} + + +int LaneSizeInBytesFromFormat(VectorFormat vform) { + return LaneSizeInBitsFromFormat(vform) / 8; +} + + +int LaneSizeInBytesLog2FromFormat(VectorFormat vform) { + VIXL_ASSERT(vform != kFormatUndefined); + switch (vform) { + case kFormatB: + case kFormat8B: + case kFormat16B: return 0; + case kFormatH: + case kFormat4H: + case kFormat8H: return 1; + case kFormatS: + case kFormat2S: + case kFormat4S: return 2; + case kFormatD: + case kFormat1D: + case kFormat2D: return 3; + default: VIXL_UNREACHABLE(); return 0; + } +} + + +int LaneCountFromFormat(VectorFormat vform) { + VIXL_ASSERT(vform != kFormatUndefined); + switch (vform) { + case kFormat16B: return 16; + case kFormat8B: + case kFormat8H: return 8; + case kFormat4H: + case kFormat4S: return 4; + case kFormat2S: + case kFormat2D: return 2; + case kFormat1D: + case kFormatB: + case kFormatH: + case kFormatS: + case kFormatD: return 1; + default: VIXL_UNREACHABLE(); return 0; + } +} + + +int MaxLaneCountFromFormat(VectorFormat vform) { + VIXL_ASSERT(vform != kFormatUndefined); + switch (vform) { + case kFormatB: + case kFormat8B: + case kFormat16B: return 16; + case kFormatH: + case kFormat4H: + case kFormat8H: return 8; + case kFormatS: + case kFormat2S: + case kFormat4S: return 4; + case kFormatD: + case kFormat1D: + case kFormat2D: return 2; + default: VIXL_UNREACHABLE(); return 0; + } +} + + +// Does 'vform' indicate a vector format or a scalar format? +bool IsVectorFormat(VectorFormat vform) { + VIXL_ASSERT(vform != kFormatUndefined); + switch (vform) { + case kFormatB: + case kFormatH: + case kFormatS: + case kFormatD: return false; + default: return true; + } +} + + +int64_t MaxIntFromFormat(VectorFormat vform) { + return INT64_MAX >> (64 - LaneSizeInBitsFromFormat(vform)); +} + + +int64_t MinIntFromFormat(VectorFormat vform) { + return INT64_MIN >> (64 - LaneSizeInBitsFromFormat(vform)); +} + + +uint64_t MaxUintFromFormat(VectorFormat vform) { + return UINT64_MAX >> (64 - LaneSizeInBitsFromFormat(vform)); +} +} // namespace vixl + diff --git a/qemu/disas/libvixl/vixl/a64/instructions-a64.h b/qemu/disas/libvixl/vixl/a64/instructions-a64.h new file mode 100644 index 000000000..7e0dbae36 --- /dev/null +++ b/qemu/disas/libvixl/vixl/a64/instructions-a64.h @@ -0,0 +1,757 @@ +// Copyright 2015, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_A64_INSTRUCTIONS_A64_H_ +#define VIXL_A64_INSTRUCTIONS_A64_H_ + +#include "vixl/globals.h" +#include "vixl/utils.h" +#include "vixl/a64/constants-a64.h" + +namespace vixl { +// ISA constants. -------------------------------------------------------------- + +typedef uint32_t Instr; +const unsigned kInstructionSize = 4; +const unsigned kInstructionSizeLog2 = 2; +const unsigned kLiteralEntrySize = 4; +const unsigned kLiteralEntrySizeLog2 = 2; +const unsigned kMaxLoadLiteralRange = 1 * MBytes; + +// This is the nominal page size (as used by the adrp instruction); the actual +// size of the memory pages allocated by the kernel is likely to differ. +const unsigned kPageSize = 4 * KBytes; +const unsigned kPageSizeLog2 = 12; + +const unsigned kBRegSize = 8; +const unsigned kBRegSizeLog2 = 3; +const unsigned kBRegSizeInBytes = kBRegSize / 8; +const unsigned kBRegSizeInBytesLog2 = kBRegSizeLog2 - 3; +const unsigned kHRegSize = 16; +const unsigned kHRegSizeLog2 = 4; +const unsigned kHRegSizeInBytes = kHRegSize / 8; +const unsigned kHRegSizeInBytesLog2 = kHRegSizeLog2 - 3; +const unsigned kWRegSize = 32; +const unsigned kWRegSizeLog2 = 5; +const unsigned kWRegSizeInBytes = kWRegSize / 8; +const unsigned kWRegSizeInBytesLog2 = kWRegSizeLog2 - 3; +const unsigned kXRegSize = 64; +const unsigned kXRegSizeLog2 = 6; +const unsigned kXRegSizeInBytes = kXRegSize / 8; +const unsigned kXRegSizeInBytesLog2 = kXRegSizeLog2 - 3; +const unsigned kSRegSize = 32; +const unsigned kSRegSizeLog2 = 5; +const unsigned kSRegSizeInBytes = kSRegSize / 8; +const unsigned kSRegSizeInBytesLog2 = kSRegSizeLog2 - 3; +const unsigned kDRegSize = 64; +const unsigned kDRegSizeLog2 = 6; +const unsigned kDRegSizeInBytes = kDRegSize / 8; +const unsigned kDRegSizeInBytesLog2 = kDRegSizeLog2 - 3; +const unsigned kQRegSize = 128; +const unsigned kQRegSizeLog2 = 7; +const unsigned kQRegSizeInBytes = kQRegSize / 8; +const unsigned kQRegSizeInBytesLog2 = kQRegSizeLog2 - 3; +const uint64_t kWRegMask = UINT64_C(0xffffffff); +const uint64_t kXRegMask = UINT64_C(0xffffffffffffffff); +const uint64_t kSRegMask = UINT64_C(0xffffffff); +const uint64_t kDRegMask = UINT64_C(0xffffffffffffffff); +const uint64_t kSSignMask = UINT64_C(0x80000000); +const uint64_t kDSignMask = UINT64_C(0x8000000000000000); +const uint64_t kWSignMask = UINT64_C(0x80000000); +const uint64_t kXSignMask = UINT64_C(0x8000000000000000); +const uint64_t kByteMask = UINT64_C(0xff); +const uint64_t kHalfWordMask = UINT64_C(0xffff); +const uint64_t kWordMask = UINT64_C(0xffffffff); +const uint64_t kXMaxUInt = UINT64_C(0xffffffffffffffff); +const uint64_t kWMaxUInt = UINT64_C(0xffffffff); +const int64_t kXMaxInt = INT64_C(0x7fffffffffffffff); +const int64_t kXMinInt = INT64_C(0x8000000000000000); +const int32_t kWMaxInt = INT32_C(0x7fffffff); +const int32_t kWMinInt = INT32_C(0x80000000); +const unsigned kLinkRegCode = 30; +const unsigned kZeroRegCode = 31; +const unsigned kSPRegInternalCode = 63; +const unsigned kRegCodeMask = 0x1f; + +const unsigned kAddressTagOffset = 56; +const unsigned kAddressTagWidth = 8; +const uint64_t kAddressTagMask = + ((UINT64_C(1) << kAddressTagWidth) - 1) << kAddressTagOffset; +VIXL_STATIC_ASSERT(kAddressTagMask == UINT64_C(0xff00000000000000)); + +// AArch64 floating-point specifics. These match IEEE-754. +const unsigned kDoubleMantissaBits = 52; +const unsigned kDoubleExponentBits = 11; +const unsigned kFloatMantissaBits = 23; +const unsigned kFloatExponentBits = 8; +const unsigned kFloat16MantissaBits = 10; +const unsigned kFloat16ExponentBits = 5; + +// Floating-point infinity values. +extern const float16 kFP16PositiveInfinity; +extern const float16 kFP16NegativeInfinity; +extern const float kFP32PositiveInfinity; +extern const float kFP32NegativeInfinity; +extern const double kFP64PositiveInfinity; +extern const double kFP64NegativeInfinity; + +// The default NaN values (for FPCR.DN=1). +extern const float16 kFP16DefaultNaN; +extern const float kFP32DefaultNaN; +extern const double kFP64DefaultNaN; + +unsigned CalcLSDataSize(LoadStoreOp op); +unsigned CalcLSPairDataSize(LoadStorePairOp op); + +enum ImmBranchType { + UnknownBranchType = 0, + CondBranchType = 1, + UncondBranchType = 2, + CompareBranchType = 3, + TestBranchType = 4 +}; + +enum AddrMode { + Offset, + PreIndex, + PostIndex +}; + +enum FPRounding { + // The first four values are encodable directly by FPCR. + FPTieEven = 0x0, + FPPositiveInfinity = 0x1, + FPNegativeInfinity = 0x2, + FPZero = 0x3, + + // The final rounding modes are only available when explicitly specified by + // the instruction (such as with fcvta). It cannot be set in FPCR. + FPTieAway, + FPRoundOdd +}; + +enum Reg31Mode { + Reg31IsStackPointer, + Reg31IsZeroRegister +}; + +// Instructions. --------------------------------------------------------------- + +class Instruction { + public: + Instr InstructionBits() const { + return *(reinterpret_cast(this)); + } + + void SetInstructionBits(Instr new_instr) { + *(reinterpret_cast(this)) = new_instr; + } + + int Bit(int pos) const { + return (InstructionBits() >> pos) & 1; + } + + uint32_t Bits(int msb, int lsb) const { + return unsigned_bitextract_32(msb, lsb, InstructionBits()); + } + + int32_t SignedBits(int msb, int lsb) const { + int32_t bits = *(reinterpret_cast(this)); + return signed_bitextract_32(msb, lsb, bits); + } + + Instr Mask(uint32_t mask) const { + return InstructionBits() & mask; + } + + #define DEFINE_GETTER(Name, HighBit, LowBit, Func) \ + int32_t Name() const { return Func(HighBit, LowBit); } + INSTRUCTION_FIELDS_LIST(DEFINE_GETTER) + #undef DEFINE_GETTER + + // ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST), + // formed from ImmPCRelLo and ImmPCRelHi. + int ImmPCRel() const { + int offset = + static_cast((ImmPCRelHi() << ImmPCRelLo_width) | ImmPCRelLo()); + int width = ImmPCRelLo_width + ImmPCRelHi_width; + return signed_bitextract_32(width - 1, 0, offset); + } + + uint64_t ImmLogical() const; + unsigned ImmNEONabcdefgh() const; + float ImmFP32() const; + double ImmFP64() const; + float ImmNEONFP32() const; + double ImmNEONFP64() const; + + unsigned SizeLS() const { + return CalcLSDataSize(static_cast(Mask(LoadStoreMask))); + } + + unsigned SizeLSPair() const { + return CalcLSPairDataSize( + static_cast(Mask(LoadStorePairMask))); + } + + int NEONLSIndex(int access_size_shift) const { + int64_t q = NEONQ(); + int64_t s = NEONS(); + int64_t size = NEONLSSize(); + int64_t index = (q << 3) | (s << 2) | size; + return static_cast(index >> access_size_shift); + } + + // Helpers. + bool IsCondBranchImm() const { + return Mask(ConditionalBranchFMask) == ConditionalBranchFixed; + } + + bool IsUncondBranchImm() const { + return Mask(UnconditionalBranchFMask) == UnconditionalBranchFixed; + } + + bool IsCompareBranch() const { + return Mask(CompareBranchFMask) == CompareBranchFixed; + } + + bool IsTestBranch() const { + return Mask(TestBranchFMask) == TestBranchFixed; + } + + bool IsImmBranch() const { + return BranchType() != UnknownBranchType; + } + + bool IsPCRelAddressing() const { + return Mask(PCRelAddressingFMask) == PCRelAddressingFixed; + } + + bool IsLogicalImmediate() const { + return Mask(LogicalImmediateFMask) == LogicalImmediateFixed; + } + + bool IsAddSubImmediate() const { + return Mask(AddSubImmediateFMask) == AddSubImmediateFixed; + } + + bool IsAddSubExtended() const { + return Mask(AddSubExtendedFMask) == AddSubExtendedFixed; + } + + bool IsLoadOrStore() const { + return Mask(LoadStoreAnyFMask) == LoadStoreAnyFixed; + } + + bool IsLoad() const; + bool IsStore() const; + + bool IsLoadLiteral() const { + // This includes PRFM_lit. + return Mask(LoadLiteralFMask) == LoadLiteralFixed; + } + + bool IsMovn() const { + return (Mask(MoveWideImmediateMask) == MOVN_x) || + (Mask(MoveWideImmediateMask) == MOVN_w); + } + + static int ImmBranchRangeBitwidth(ImmBranchType branch_type); + static int32_t ImmBranchForwardRange(ImmBranchType branch_type); + static bool IsValidImmPCOffset(ImmBranchType branch_type, int64_t offset); + + // Indicate whether Rd can be the stack pointer or the zero register. This + // does not check that the instruction actually has an Rd field. + Reg31Mode RdMode() const { + // The following instructions use sp or wsp as Rd: + // Add/sub (immediate) when not setting the flags. + // Add/sub (extended) when not setting the flags. + // Logical (immediate) when not setting the flags. + // Otherwise, r31 is the zero register. + if (IsAddSubImmediate() || IsAddSubExtended()) { + if (Mask(AddSubSetFlagsBit)) { + return Reg31IsZeroRegister; + } else { + return Reg31IsStackPointer; + } + } + if (IsLogicalImmediate()) { + // Of the logical (immediate) instructions, only ANDS (and its aliases) + // can set the flags. The others can all write into sp. + // Note that some logical operations are not available to + // immediate-operand instructions, so we have to combine two masks here. + if (Mask(LogicalImmediateMask & LogicalOpMask) == ANDS) { + return Reg31IsZeroRegister; + } else { + return Reg31IsStackPointer; + } + } + return Reg31IsZeroRegister; + } + + // Indicate whether Rn can be the stack pointer or the zero register. This + // does not check that the instruction actually has an Rn field. + Reg31Mode RnMode() const { + // The following instructions use sp or wsp as Rn: + // All loads and stores. + // Add/sub (immediate). + // Add/sub (extended). + // Otherwise, r31 is the zero register. + if (IsLoadOrStore() || IsAddSubImmediate() || IsAddSubExtended()) { + return Reg31IsStackPointer; + } + return Reg31IsZeroRegister; + } + + ImmBranchType BranchType() const { + if (IsCondBranchImm()) { + return CondBranchType; + } else if (IsUncondBranchImm()) { + return UncondBranchType; + } else if (IsCompareBranch()) { + return CompareBranchType; + } else if (IsTestBranch()) { + return TestBranchType; + } else { + return UnknownBranchType; + } + } + + // Find the target of this instruction. 'this' may be a branch or a + // PC-relative addressing instruction. + const Instruction* ImmPCOffsetTarget() const; + + // Patch a PC-relative offset to refer to 'target'. 'this' may be a branch or + // a PC-relative addressing instruction. + void SetImmPCOffsetTarget(const Instruction* target); + // Patch a literal load instruction to load from 'source'. + void SetImmLLiteral(const Instruction* source); + + // The range of a load literal instruction, expressed as 'instr +- range'. + // The range is actually the 'positive' range; the branch instruction can + // target [instr - range - kInstructionSize, instr + range]. + static const int kLoadLiteralImmBitwidth = 19; + static const int kLoadLiteralRange = + (1 << kLoadLiteralImmBitwidth) / 2 - kInstructionSize; + + // Calculate the address of a literal referred to by a load-literal + // instruction, and return it as the specified type. + // + // The literal itself is safely mutable only if the backing buffer is safely + // mutable. + template + T LiteralAddress() const { + uint64_t base_raw = reinterpret_cast(this); + int64_t offset = ImmLLiteral() << kLiteralEntrySizeLog2; + uint64_t address_raw = base_raw + offset; + + // Cast the address using a C-style cast. A reinterpret_cast would be + // appropriate, but it can't cast one integral type to another. + T address = (T)(address_raw); + + // Assert that the address can be represented by the specified type. + VIXL_ASSERT((uint64_t)(address) == address_raw); + + return address; + } + + uint32_t Literal32() const { + uint32_t literal; + memcpy(&literal, LiteralAddress(), sizeof(literal)); + return literal; + } + + uint64_t Literal64() const { + uint64_t literal; + memcpy(&literal, LiteralAddress(), sizeof(literal)); + return literal; + } + + float LiteralFP32() const { + return rawbits_to_float(Literal32()); + } + + double LiteralFP64() const { + return rawbits_to_double(Literal64()); + } + + const Instruction* NextInstruction() const { + return this + kInstructionSize; + } + + const Instruction* InstructionAtOffset(int64_t offset) const { + VIXL_ASSERT(IsWordAligned(this + offset)); + return this + offset; + } + + template static Instruction* Cast(T src) { + return reinterpret_cast(src); + } + + template static const Instruction* CastConst(T src) { + return reinterpret_cast(src); + } + + private: + int ImmBranch() const; + + static float Imm8ToFP32(uint32_t imm8); + static double Imm8ToFP64(uint32_t imm8); + + void SetPCRelImmTarget(const Instruction* target); + void SetBranchImmTarget(const Instruction* target); +}; + + +// Functions for handling NEON vector format information. +enum VectorFormat { + kFormatUndefined = 0xffffffff, + kFormat8B = NEON_8B, + kFormat16B = NEON_16B, + kFormat4H = NEON_4H, + kFormat8H = NEON_8H, + kFormat2S = NEON_2S, + kFormat4S = NEON_4S, + kFormat1D = NEON_1D, + kFormat2D = NEON_2D, + + // Scalar formats. We add the scalar bit to distinguish between scalar and + // vector enumerations; the bit is always set in the encoding of scalar ops + // and always clear for vector ops. Although kFormatD and kFormat1D appear + // to be the same, their meaning is subtly different. The first is a scalar + // operation, the second a vector operation that only affects one lane. + kFormatB = NEON_B | NEONScalar, + kFormatH = NEON_H | NEONScalar, + kFormatS = NEON_S | NEONScalar, + kFormatD = NEON_D | NEONScalar +}; + +VectorFormat VectorFormatHalfWidth(const VectorFormat vform); +VectorFormat VectorFormatDoubleWidth(const VectorFormat vform); +VectorFormat VectorFormatDoubleLanes(const VectorFormat vform); +VectorFormat VectorFormatHalfLanes(const VectorFormat vform); +VectorFormat ScalarFormatFromLaneSize(int lanesize); +VectorFormat VectorFormatHalfWidthDoubleLanes(const VectorFormat vform); +VectorFormat VectorFormatFillQ(const VectorFormat vform); +unsigned RegisterSizeInBitsFromFormat(VectorFormat vform); +unsigned RegisterSizeInBytesFromFormat(VectorFormat vform); +// TODO: Make the return types of these functions consistent. +unsigned LaneSizeInBitsFromFormat(VectorFormat vform); +int LaneSizeInBytesFromFormat(VectorFormat vform); +int LaneSizeInBytesLog2FromFormat(VectorFormat vform); +int LaneCountFromFormat(VectorFormat vform); +int MaxLaneCountFromFormat(VectorFormat vform); +bool IsVectorFormat(VectorFormat vform); +int64_t MaxIntFromFormat(VectorFormat vform); +int64_t MinIntFromFormat(VectorFormat vform); +uint64_t MaxUintFromFormat(VectorFormat vform); + + +enum NEONFormat { + NF_UNDEF = 0, + NF_8B = 1, + NF_16B = 2, + NF_4H = 3, + NF_8H = 4, + NF_2S = 5, + NF_4S = 6, + NF_1D = 7, + NF_2D = 8, + NF_B = 9, + NF_H = 10, + NF_S = 11, + NF_D = 12 +}; + +static const unsigned kNEONFormatMaxBits = 6; + +struct NEONFormatMap { + // The bit positions in the instruction to consider. + uint8_t bits[kNEONFormatMaxBits]; + + // Mapping from concatenated bits to format. + NEONFormat map[1 << kNEONFormatMaxBits]; +}; + +class NEONFormatDecoder { + public: + enum SubstitutionMode { + kPlaceholder, + kFormat + }; + + // Construct a format decoder with increasingly specific format maps for each + // subsitution. If no format map is specified, the default is the integer + // format map. + explicit NEONFormatDecoder(const Instruction* instr) { + instrbits_ = instr->InstructionBits(); + SetFormatMaps(IntegerFormatMap()); + } + NEONFormatDecoder(const Instruction* instr, + const NEONFormatMap* format) { + instrbits_ = instr->InstructionBits(); + SetFormatMaps(format); + } + NEONFormatDecoder(const Instruction* instr, + const NEONFormatMap* format0, + const NEONFormatMap* format1) { + instrbits_ = instr->InstructionBits(); + SetFormatMaps(format0, format1); + } + NEONFormatDecoder(const Instruction* instr, + const NEONFormatMap* format0, + const NEONFormatMap* format1, + const NEONFormatMap* format2) { + instrbits_ = instr->InstructionBits(); + SetFormatMaps(format0, format1, format2); + } + + // Set the format mapping for all or individual substitutions. + void SetFormatMaps(const NEONFormatMap* format0, + const NEONFormatMap* format1 = NULL, + const NEONFormatMap* format2 = NULL) { + VIXL_ASSERT(format0 != NULL); + formats_[0] = format0; + formats_[1] = (format1 == NULL) ? formats_[0] : format1; + formats_[2] = (format2 == NULL) ? formats_[1] : format2; + } + void SetFormatMap(unsigned index, const NEONFormatMap* format) { + VIXL_ASSERT(index <= (sizeof(formats_) / sizeof(formats_[0]))); + VIXL_ASSERT(format != NULL); + formats_[index] = format; + } + + // Substitute %s in the input string with the placeholder string for each + // register, ie. "'B", "'H", etc. + const char* SubstitutePlaceholders(const char* string) { + return Substitute(string, kPlaceholder, kPlaceholder, kPlaceholder); + } + + // Substitute %s in the input string with a new string based on the + // substitution mode. + const char* Substitute(const char* string, + SubstitutionMode mode0 = kFormat, + SubstitutionMode mode1 = kFormat, + SubstitutionMode mode2 = kFormat) { + snprintf(form_buffer_, sizeof(form_buffer_), string, + GetSubstitute(0, mode0), + GetSubstitute(1, mode1), + GetSubstitute(2, mode2)); + return form_buffer_; + } + + // Append a "2" to a mnemonic string based of the state of the Q bit. + const char* Mnemonic(const char* mnemonic) { + if ((instrbits_ & NEON_Q) != 0) { + snprintf(mne_buffer_, sizeof(mne_buffer_), "%s2", mnemonic); + return mne_buffer_; + } + return mnemonic; + } + + VectorFormat GetVectorFormat(int format_index = 0) { + return GetVectorFormat(formats_[format_index]); + } + + VectorFormat GetVectorFormat(const NEONFormatMap* format_map) { + static const VectorFormat vform[] = { + kFormatUndefined, + kFormat8B, kFormat16B, kFormat4H, kFormat8H, + kFormat2S, kFormat4S, kFormat1D, kFormat2D, + kFormatB, kFormatH, kFormatS, kFormatD + }; + VIXL_ASSERT(GetNEONFormat(format_map) < (sizeof(vform) / sizeof(vform[0]))); + return vform[GetNEONFormat(format_map)]; + } + + // Built in mappings for common cases. + + // The integer format map uses three bits (Q, size<1:0>) to encode the + // "standard" set of NEON integer vector formats. + static const NEONFormatMap* IntegerFormatMap() { + static const NEONFormatMap map = { + {23, 22, 30}, + {NF_8B, NF_16B, NF_4H, NF_8H, NF_2S, NF_4S, NF_UNDEF, NF_2D} + }; + return ↦ + } + + // The long integer format map uses two bits (size<1:0>) to encode the + // long set of NEON integer vector formats. These are used in narrow, wide + // and long operations. + static const NEONFormatMap* LongIntegerFormatMap() { + static const NEONFormatMap map = { + {23, 22}, {NF_8H, NF_4S, NF_2D} + }; + return ↦ + } + + // The FP format map uses two bits (Q, size<0>) to encode the NEON FP vector + // formats: NF_2S, NF_4S, NF_2D. + static const NEONFormatMap* FPFormatMap() { + // The FP format map assumes two bits (Q, size<0>) are used to encode the + // NEON FP vector formats: NF_2S, NF_4S, NF_2D. + static const NEONFormatMap map = { + {22, 30}, {NF_2S, NF_4S, NF_UNDEF, NF_2D} + }; + return ↦ + } + + // The load/store format map uses three bits (Q, 11, 10) to encode the + // set of NEON vector formats. + static const NEONFormatMap* LoadStoreFormatMap() { + static const NEONFormatMap map = { + {11, 10, 30}, + {NF_8B, NF_16B, NF_4H, NF_8H, NF_2S, NF_4S, NF_1D, NF_2D} + }; + return ↦ + } + + // The logical format map uses one bit (Q) to encode the NEON vector format: + // NF_8B, NF_16B. + static const NEONFormatMap* LogicalFormatMap() { + static const NEONFormatMap map = { + {30}, {NF_8B, NF_16B} + }; + return ↦ + } + + // The triangular format map uses between two and five bits to encode the NEON + // vector format: + // xxx10->8B, xxx11->16B, xx100->4H, xx101->8H + // x1000->2S, x1001->4S, 10001->2D, all others undefined. + static const NEONFormatMap* TriangularFormatMap() { + static const NEONFormatMap map = { + {19, 18, 17, 16, 30}, + {NF_UNDEF, NF_UNDEF, NF_8B, NF_16B, NF_4H, NF_8H, NF_8B, NF_16B, NF_2S, + NF_4S, NF_8B, NF_16B, NF_4H, NF_8H, NF_8B, NF_16B, NF_UNDEF, NF_2D, + NF_8B, NF_16B, NF_4H, NF_8H, NF_8B, NF_16B, NF_2S, NF_4S, NF_8B, NF_16B, + NF_4H, NF_8H, NF_8B, NF_16B} + }; + return ↦ + } + + // The scalar format map uses two bits (size<1:0>) to encode the NEON scalar + // formats: NF_B, NF_H, NF_S, NF_D. + static const NEONFormatMap* ScalarFormatMap() { + static const NEONFormatMap map = { + {23, 22}, {NF_B, NF_H, NF_S, NF_D} + }; + return ↦ + } + + // The long scalar format map uses two bits (size<1:0>) to encode the longer + // NEON scalar formats: NF_H, NF_S, NF_D. + static const NEONFormatMap* LongScalarFormatMap() { + static const NEONFormatMap map = { + {23, 22}, {NF_H, NF_S, NF_D} + }; + return ↦ + } + + // The FP scalar format map assumes one bit (size<0>) is used to encode the + // NEON FP scalar formats: NF_S, NF_D. + static const NEONFormatMap* FPScalarFormatMap() { + static const NEONFormatMap map = { + {22}, {NF_S, NF_D} + }; + return ↦ + } + + // The triangular scalar format map uses between one and four bits to encode + // the NEON FP scalar formats: + // xxx1->B, xx10->H, x100->S, 1000->D, all others undefined. + static const NEONFormatMap* TriangularScalarFormatMap() { + static const NEONFormatMap map = { + {19, 18, 17, 16}, + {NF_UNDEF, NF_B, NF_H, NF_B, NF_S, NF_B, NF_H, NF_B, + NF_D, NF_B, NF_H, NF_B, NF_S, NF_B, NF_H, NF_B} + }; + return ↦ + } + + private: + // Get a pointer to a string that represents the format or placeholder for + // the specified substitution index, based on the format map and instruction. + const char* GetSubstitute(int index, SubstitutionMode mode) { + if (mode == kFormat) { + return NEONFormatAsString(GetNEONFormat(formats_[index])); + } + VIXL_ASSERT(mode == kPlaceholder); + return NEONFormatAsPlaceholder(GetNEONFormat(formats_[index])); + } + + // Get the NEONFormat enumerated value for bits obtained from the + // instruction based on the specified format mapping. + NEONFormat GetNEONFormat(const NEONFormatMap* format_map) { + return format_map->map[PickBits(format_map->bits)]; + } + + // Convert a NEONFormat into a string. + static const char* NEONFormatAsString(NEONFormat format) { + static const char* formats[] = { + "undefined", + "8b", "16b", "4h", "8h", "2s", "4s", "1d", "2d", + "b", "h", "s", "d" + }; + VIXL_ASSERT(format < (sizeof(formats) / sizeof(formats[0]))); + return formats[format]; + } + + // Convert a NEONFormat into a register placeholder string. + static const char* NEONFormatAsPlaceholder(NEONFormat format) { + VIXL_ASSERT((format == NF_B) || (format == NF_H) || + (format == NF_S) || (format == NF_D) || + (format == NF_UNDEF)); + static const char* formats[] = { + "undefined", + "undefined", "undefined", "undefined", "undefined", + "undefined", "undefined", "undefined", "undefined", + "'B", "'H", "'S", "'D" + }; + return formats[format]; + } + + // Select bits from instrbits_ defined by the bits array, concatenate them, + // and return the value. + uint8_t PickBits(const uint8_t bits[]) { + uint8_t result = 0; + for (unsigned b = 0; b < kNEONFormatMaxBits; b++) { + if (bits[b] == 0) break; + result <<= 1; + result |= ((instrbits_ & (1 << bits[b])) == 0) ? 0 : 1; + } + return result; + } + + Instr instrbits_; + const NEONFormatMap* formats_[3]; + char form_buffer_[64]; + char mne_buffer_[16]; +}; +} // namespace vixl + +#endif // VIXL_A64_INSTRUCTIONS_A64_H_ diff --git a/qemu/disas/libvixl/vixl/code-buffer.h b/qemu/disas/libvixl/vixl/code-buffer.h new file mode 100644 index 000000000..f93ebb6b8 --- /dev/null +++ b/qemu/disas/libvixl/vixl/code-buffer.h @@ -0,0 +1,113 @@ +// Copyright 2014, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_CODE_BUFFER_H +#define VIXL_CODE_BUFFER_H + +#include +#include "vixl/globals.h" + +namespace vixl { + +class CodeBuffer { + public: + explicit CodeBuffer(size_t capacity = 4 * KBytes); + CodeBuffer(void* buffer, size_t capacity); + ~CodeBuffer(); + + void Reset(); + + ptrdiff_t OffsetFrom(ptrdiff_t offset) const { + ptrdiff_t cursor_offset = cursor_ - buffer_; + VIXL_ASSERT((offset >= 0) && (offset <= cursor_offset)); + return cursor_offset - offset; + } + + ptrdiff_t CursorOffset() const { + return OffsetFrom(0); + } + + template + T GetOffsetAddress(ptrdiff_t offset) const { + VIXL_ASSERT((offset >= 0) && (offset <= (cursor_ - buffer_))); + return reinterpret_cast(buffer_ + offset); + } + + size_t RemainingBytes() const { + VIXL_ASSERT((cursor_ >= buffer_) && (cursor_ <= (buffer_ + capacity_))); + return (buffer_ + capacity_) - cursor_; + } + + // A code buffer can emit: + // * 32-bit data: instruction and constant. + // * 64-bit data: constant. + // * string: debug info. + void Emit32(uint32_t data) { Emit(data); } + + void Emit64(uint64_t data) { Emit(data); } + + void EmitString(const char* string); + + // Align to kInstructionSize. + void Align(); + + size_t capacity() const { return capacity_; } + + bool IsManaged() const { return managed_; } + + void Grow(size_t new_capacity); + + bool IsDirty() const { return dirty_; } + + void SetClean() { dirty_ = false; } + + private: + template + void Emit(T value) { + VIXL_ASSERT(RemainingBytes() >= sizeof(value)); + dirty_ = true; + memcpy(cursor_, &value, sizeof(value)); + cursor_ += sizeof(value); + } + + // Backing store of the buffer. + byte* buffer_; + // If true the backing store is allocated and deallocated by the buffer. The + // backing store can then grow on demand. If false the backing store is + // provided by the user and cannot be resized internally. + bool managed_; + // Pointer to the next location to be written. + byte* cursor_; + // True if there has been any write since the buffer was created or cleaned. + bool dirty_; + // Capacity in bytes of the backing store. + size_t capacity_; +}; + +} // namespace vixl + +#endif // VIXL_CODE_BUFFER_H + diff --git a/qemu/disas/libvixl/vixl/compiler-intrinsics.cc b/qemu/disas/libvixl/vixl/compiler-intrinsics.cc new file mode 100644 index 000000000..fd551faeb --- /dev/null +++ b/qemu/disas/libvixl/vixl/compiler-intrinsics.cc @@ -0,0 +1,144 @@ +// Copyright 2015, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "compiler-intrinsics.h" + +namespace vixl { + + +int CountLeadingSignBitsFallBack(int64_t value, int width) { + VIXL_ASSERT(IsPowerOf2(width) && (width <= 64)); + if (value >= 0) { + return CountLeadingZeros(value, width) - 1; + } else { + return CountLeadingZeros(~value, width) - 1; + } +} + + +int CountLeadingZerosFallBack(uint64_t value, int width) { + VIXL_ASSERT(IsPowerOf2(width) && (width <= 64)); + if (value == 0) { + return width; + } + int count = 0; + value = value << (64 - width); + if ((value & UINT64_C(0xffffffff00000000)) == 0) { + count += 32; + value = value << 32; + } + if ((value & UINT64_C(0xffff000000000000)) == 0) { + count += 16; + value = value << 16; + } + if ((value & UINT64_C(0xff00000000000000)) == 0) { + count += 8; + value = value << 8; + } + if ((value & UINT64_C(0xf000000000000000)) == 0) { + count += 4; + value = value << 4; + } + if ((value & UINT64_C(0xc000000000000000)) == 0) { + count += 2; + value = value << 2; + } + if ((value & UINT64_C(0x8000000000000000)) == 0) { + count += 1; + } + count += (value == 0); + return count; +} + + +int CountSetBitsFallBack(uint64_t value, int width) { + VIXL_ASSERT(IsPowerOf2(width) && (width <= 64)); + + // Mask out unused bits to ensure that they are not counted. + value &= (UINT64_C(0xffffffffffffffff) >> (64 - width)); + + // Add up the set bits. + // The algorithm works by adding pairs of bit fields together iteratively, + // where the size of each bit field doubles each time. + // An example for an 8-bit value: + // Bits: h g f e d c b a + // \ | \ | \ | \ | + // value = h+g f+e d+c b+a + // \ | \ | + // value = h+g+f+e d+c+b+a + // \ | + // value = h+g+f+e+d+c+b+a + const uint64_t kMasks[] = { + UINT64_C(0x5555555555555555), + UINT64_C(0x3333333333333333), + UINT64_C(0x0f0f0f0f0f0f0f0f), + UINT64_C(0x00ff00ff00ff00ff), + UINT64_C(0x0000ffff0000ffff), + UINT64_C(0x00000000ffffffff), + }; + + for (unsigned i = 0; i < (sizeof(kMasks) / sizeof(kMasks[0])); i++) { + int shift = 1 << i; + value = ((value >> shift) & kMasks[i]) + (value & kMasks[i]); + } + + return static_cast(value); +} + + +int CountTrailingZerosFallBack(uint64_t value, int width) { + VIXL_ASSERT(IsPowerOf2(width) && (width <= 64)); + int count = 0; + value = value << (64 - width); + if ((value & UINT64_C(0xffffffff)) == 0) { + count += 32; + value = value >> 32; + } + if ((value & 0xffff) == 0) { + count += 16; + value = value >> 16; + } + if ((value & 0xff) == 0) { + count += 8; + value = value >> 8; + } + if ((value & 0xf) == 0) { + count += 4; + value = value >> 4; + } + if ((value & 0x3) == 0) { + count += 2; + value = value >> 2; + } + if ((value & 0x1) == 0) { + count += 1; + } + count += (value == 0); + return count - (64 - width); +} + + +} // namespace vixl diff --git a/qemu/disas/libvixl/vixl/compiler-intrinsics.h b/qemu/disas/libvixl/vixl/compiler-intrinsics.h new file mode 100644 index 000000000..9431beddb --- /dev/null +++ b/qemu/disas/libvixl/vixl/compiler-intrinsics.h @@ -0,0 +1,155 @@ +// Copyright 2015, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +#ifndef VIXL_COMPILER_INTRINSICS_H +#define VIXL_COMPILER_INTRINSICS_H + +#include "globals.h" + +namespace vixl { + +// Helper to check whether the version of GCC used is greater than the specified +// requirement. +#define MAJOR 1000000 +#define MINOR 1000 +#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) +#define GCC_VERSION_OR_NEWER(major, minor, patchlevel) \ + ((__GNUC__ * MAJOR + __GNUC_MINOR__ * MINOR + __GNUC_PATCHLEVEL__) >= \ + ((major) * MAJOR + (minor) * MINOR + (patchlevel))) +#elif defined(__GNUC__) && defined(__GNUC_MINOR__) +#define GCC_VERSION_OR_NEWER(major, minor, patchlevel) \ + ((__GNUC__ * MAJOR + __GNUC_MINOR__ * MINOR) >= \ + ((major) * MAJOR + (minor) * MINOR + (patchlevel))) +#else +#define GCC_VERSION_OR_NEWER(major, minor, patchlevel) 0 +#endif + + +#if defined(__clang__) && !defined(VIXL_NO_COMPILER_BUILTINS) + +#define COMPILER_HAS_BUILTIN_CLRSB (__has_builtin(__builtin_clrsb)) +#define COMPILER_HAS_BUILTIN_CLZ (__has_builtin(__builtin_clz)) +#define COMPILER_HAS_BUILTIN_CTZ (__has_builtin(__builtin_ctz)) +#define COMPILER_HAS_BUILTIN_FFS (__has_builtin(__builtin_ffs)) +#define COMPILER_HAS_BUILTIN_POPCOUNT (__has_builtin(__builtin_popcount)) + +#elif defined(__GNUC__) && !defined(VIXL_NO_COMPILER_BUILTINS) +// The documentation for these builtins is available at: +// https://gcc.gnu.org/onlinedocs/gcc-$MAJOR.$MINOR.$PATCHLEVEL/gcc//Other-Builtins.html + +# define COMPILER_HAS_BUILTIN_CLRSB (GCC_VERSION_OR_NEWER(4, 7, 0)) +# define COMPILER_HAS_BUILTIN_CLZ (GCC_VERSION_OR_NEWER(3, 4, 0)) +# define COMPILER_HAS_BUILTIN_CTZ (GCC_VERSION_OR_NEWER(3, 4, 0)) +# define COMPILER_HAS_BUILTIN_FFS (GCC_VERSION_OR_NEWER(3, 4, 0)) +# define COMPILER_HAS_BUILTIN_POPCOUNT (GCC_VERSION_OR_NEWER(3, 4, 0)) + +#else +// One can define VIXL_NO_COMPILER_BUILTINS to force using the manually +// implemented C++ methods. + +#define COMPILER_HAS_BUILTIN_BSWAP false +#define COMPILER_HAS_BUILTIN_CLRSB false +#define COMPILER_HAS_BUILTIN_CLZ false +#define COMPILER_HAS_BUILTIN_CTZ false +#define COMPILER_HAS_BUILTIN_FFS false +#define COMPILER_HAS_BUILTIN_POPCOUNT false + +#endif + + +template +inline bool IsPowerOf2(V value) { + return (value != 0) && ((value & (value - 1)) == 0); +} + + +// Declaration of fallback functions. +int CountLeadingSignBitsFallBack(int64_t value, int width); +int CountLeadingZerosFallBack(uint64_t value, int width); +int CountSetBitsFallBack(uint64_t value, int width); +int CountTrailingZerosFallBack(uint64_t value, int width); + + +// Implementation of intrinsics functions. +// TODO: The implementations could be improved for sizes different from 32bit +// and 64bit: we could mask the values and call the appropriate builtin. + +template +inline int CountLeadingSignBits(V value, int width = (sizeof(V) * 8)) { +#if COMPILER_HAS_BUILTIN_CLRSB + if (width == 32) { + return __builtin_clrsb(value); + } else if (width == 64) { + return __builtin_clrsbll(value); + } +#endif + return CountLeadingSignBitsFallBack(value, width); +} + + +template +inline int CountLeadingZeros(V value, int width = (sizeof(V) * 8)) { +#if COMPILER_HAS_BUILTIN_CLZ + if (width == 32) { + return (value == 0) ? 32 : __builtin_clz(static_cast(value)); + } else if (width == 64) { + return (value == 0) ? 64 : __builtin_clzll(value); + } +#endif + return CountLeadingZerosFallBack(value, width); +} + + +template +inline int CountSetBits(V value, int width = (sizeof(V) * 8)) { +#if COMPILER_HAS_BUILTIN_POPCOUNT + if (width == 32) { + return __builtin_popcount(static_cast(value)); + } else if (width == 64) { + return __builtin_popcountll(value); + } +#endif + return CountSetBitsFallBack(value, width); +} + + +template +inline int CountTrailingZeros(V value, int width = (sizeof(V) * 8)) { +#if COMPILER_HAS_BUILTIN_CTZ + if (width == 32) { + return (value == 0) ? 32 : __builtin_ctz(static_cast(value)); + } else if (width == 64) { + return (value == 0) ? 64 : __builtin_ctzll(value); + } +#endif + return CountTrailingZerosFallBack(value, width); +} + +} // namespace vixl + +#endif // VIXL_COMPILER_INTRINSICS_H + diff --git a/qemu/disas/libvixl/vixl/globals.h b/qemu/disas/libvixl/vixl/globals.h new file mode 100644 index 000000000..61dc9f7f7 --- /dev/null +++ b/qemu/disas/libvixl/vixl/globals.h @@ -0,0 +1,151 @@ +// Copyright 2015, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_GLOBALS_H +#define VIXL_GLOBALS_H + +// Get standard C99 macros for integer types. +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif + +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif + +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include "vixl/platform.h" + + +typedef uint8_t byte; + +// Type for half-precision (16 bit) floating point numbers. +typedef uint16_t float16; + +const int KBytes = 1024; +const int MBytes = 1024 * KBytes; + +#define VIXL_ABORT() \ + do { printf("in %s, line %i", __FILE__, __LINE__); abort(); } while (false) +#ifdef VIXL_DEBUG + #define VIXL_ASSERT(condition) assert(condition) + #define VIXL_CHECK(condition) VIXL_ASSERT(condition) + #define VIXL_UNIMPLEMENTED() \ + do { fprintf(stderr, "UNIMPLEMENTED\t"); VIXL_ABORT(); } while (false) + #define VIXL_UNREACHABLE() \ + do { fprintf(stderr, "UNREACHABLE\t"); VIXL_ABORT(); } while (false) +#else + #define VIXL_ASSERT(condition) ((void) 0) + #define VIXL_CHECK(condition) assert(condition) + #define VIXL_UNIMPLEMENTED() ((void) 0) + #define VIXL_UNREACHABLE() ((void) 0) +#endif +// This is not as powerful as template based assertions, but it is simple. +// It assumes that the descriptions are unique. If this starts being a problem, +// we can switch to a different implemention. +#define VIXL_CONCAT(a, b) a##b +#define VIXL_STATIC_ASSERT_LINE(line, condition) \ + typedef char VIXL_CONCAT(STATIC_ASSERT_LINE_, line)[(condition) ? 1 : -1] \ + __attribute__((unused)) +#define VIXL_STATIC_ASSERT(condition) \ + VIXL_STATIC_ASSERT_LINE(__LINE__, condition) + +template +inline void USE(T1) {} + +template +inline void USE(T1, T2) {} + +template +inline void USE(T1, T2, T3) {} + +template +inline void USE(T1, T2, T3, T4) {} + +#define VIXL_ALIGNMENT_EXCEPTION() \ + do { fprintf(stderr, "ALIGNMENT EXCEPTION\t"); VIXL_ABORT(); } while (0) + +// The clang::fallthrough attribute is used along with the Wimplicit-fallthrough +// argument to annotate intentional fall-through between switch labels. +// For more information please refer to: +// http://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough +#ifndef __has_warning + #define __has_warning(x) 0 +#endif + +// Note: This option is only available for Clang. And will only be enabled for +// C++11(201103L). +#if __has_warning("-Wimplicit-fallthrough") && __cplusplus >= 201103L + #define VIXL_FALLTHROUGH() [[clang::fallthrough]] //NOLINT +#else + #define VIXL_FALLTHROUGH() do {} while (0) +#endif + +#if __cplusplus >= 201103L + #define VIXL_NO_RETURN [[noreturn]] //NOLINT +#else + #define VIXL_NO_RETURN __attribute__((noreturn)) +#endif + +// Some functions might only be marked as "noreturn" for the DEBUG build. This +// macro should be used for such cases (for more details see what +// VIXL_UNREACHABLE expands to). +#ifdef VIXL_DEBUG + #define VIXL_DEBUG_NO_RETURN VIXL_NO_RETURN +#else + #define VIXL_DEBUG_NO_RETURN +#endif + +#ifdef VIXL_INCLUDE_SIMULATOR +#ifndef VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE + #define VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE 1 +#endif +#else +#ifndef VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE + #define VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE 0 +#endif +#if VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE + #warning "Generating Simulator instructions without Simulator support." +#endif +#endif + +#ifdef USE_SIMULATOR + #error "Please see the release notes for USE_SIMULATOR." +#endif + +#endif // VIXL_GLOBALS_H diff --git a/qemu/disas/libvixl/vixl/invalset.h b/qemu/disas/libvixl/vixl/invalset.h new file mode 100644 index 000000000..ffdc0237b --- /dev/null +++ b/qemu/disas/libvixl/vixl/invalset.h @@ -0,0 +1,775 @@ +// Copyright 2015, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_INVALSET_H_ +#define VIXL_INVALSET_H_ + +#include + +#include +#include + +#include "vixl/globals.h" + +namespace vixl { + +// We define a custom data structure template and its iterator as `std` +// containers do not fit the performance requirements for some of our use cases. +// +// The structure behaves like an iterable unordered set with special properties +// and restrictions. "InvalSet" stands for "Invalidatable Set". +// +// Restrictions and requirements: +// - Adding an element already present in the set is illegal. In debug mode, +// this is checked at insertion time. +// - The templated class `ElementType` must provide comparison operators so that +// `std::sort()` can be used. +// - A key must be available to represent invalid elements. +// - Elements with an invalid key must compare higher or equal to any other +// element. +// +// Use cases and performance considerations: +// Our use cases present two specificities that allow us to design this +// structure to provide fast insertion *and* fast search and deletion +// operations: +// - Elements are (generally) inserted in order (sorted according to their key). +// - A key is available to mark elements as invalid (deleted). +// The backing `std::vector` allows for fast insertions. When +// searching for an element we ensure the elements are sorted (this is generally +// the case) and perform a binary search. When deleting an element we do not +// free the associated memory immediately. Instead, an element to be deleted is +// marked with the 'invalid' key. Other methods of the container take care of +// ignoring entries marked as invalid. +// To avoid the overhead of the `std::vector` container when only few entries +// are used, a number of elements are preallocated. + +// 'ElementType' and 'KeyType' are respectively the types of the elements and +// their key. The structure only reclaims memory when safe to do so, if the +// number of elements that can be reclaimed is greater than `RECLAIM_FROM` and +// greater than ` / RECLAIM_FACTOR. +#define TEMPLATE_INVALSET_P_DECL \ + class ElementType, \ + unsigned N_PREALLOCATED_ELEMENTS, \ + class KeyType, \ + KeyType INVALID_KEY, \ + size_t RECLAIM_FROM, \ + unsigned RECLAIM_FACTOR + +#define TEMPLATE_INVALSET_P_DEF \ +ElementType, N_PREALLOCATED_ELEMENTS, \ +KeyType, INVALID_KEY, RECLAIM_FROM, RECLAIM_FACTOR + +template class InvalSetIterator; // Forward declaration. + +template class InvalSet { + public: + InvalSet(); + ~InvalSet(); + + static const size_t kNPreallocatedElements = N_PREALLOCATED_ELEMENTS; + static const KeyType kInvalidKey = INVALID_KEY; + + // It is illegal to insert an element already present in the set. + void insert(const ElementType& element); + + // Looks for the specified element in the set and - if found - deletes it. + void erase(const ElementType& element); + + // This indicates the number of (valid) elements stored in this set. + size_t size() const; + + // Returns true if no elements are stored in the set. + // Note that this does not mean the the backing storage is empty: it can still + // contain invalid elements. + bool empty() const; + + void clear(); + + const ElementType min_element(); + + // This returns the key of the minimum element in the set. + KeyType min_element_key(); + + static bool IsValid(const ElementType& element); + static KeyType Key(const ElementType& element); + static void SetKey(ElementType* element, KeyType key); + + protected: + // Returns a pointer to the element in vector_ if it was found, or NULL + // otherwise. + ElementType* Search(const ElementType& element); + + // The argument *must* point to an element stored in *this* set. + // This function is not allowed to move elements in the backing vector + // storage. + void EraseInternal(ElementType* element); + + // The elements in the range searched must be sorted. + ElementType* BinarySearch(const ElementType& element, + ElementType* start, + ElementType* end) const; + + // Sort the elements. + enum SortType { + // The 'hard' version guarantees that invalid elements are moved to the end + // of the container. + kHardSort, + // The 'soft' version only guarantees that the elements will be sorted. + // Invalid elements may still be present anywhere in the set. + kSoftSort + }; + void Sort(SortType sort_type); + + // Delete the elements that have an invalid key. The complexity is linear + // with the size of the vector. + void Clean(); + + const ElementType Front() const; + const ElementType Back() const; + + // Delete invalid trailing elements and return the last valid element in the + // set. + const ElementType CleanBack(); + + // Returns a pointer to the start or end of the backing storage. + const ElementType* StorageBegin() const; + const ElementType* StorageEnd() const; + ElementType* StorageBegin(); + ElementType* StorageEnd(); + + // Returns the index of the element within the backing storage. The element + // must belong to the backing storage. + size_t ElementIndex(const ElementType* element) const; + + // Returns the element at the specified index in the backing storage. + const ElementType* ElementAt(size_t index) const; + ElementType* ElementAt(size_t index); + + static const ElementType* FirstValidElement(const ElementType* from, + const ElementType* end); + + void CacheMinElement(); + const ElementType CachedMinElement() const; + + bool ShouldReclaimMemory() const; + void ReclaimMemory(); + + bool IsUsingVector() const { return vector_ != NULL; } + void set_sorted(bool sorted) { sorted_ = sorted; } + + // We cache some data commonly required by users to improve performance. + // We cannot cache pointers to elements as we do not control the backing + // storage. + bool valid_cached_min_; + size_t cached_min_index_; // Valid iff `valid_cached_min_` is true. + KeyType cached_min_key_; // Valid iff `valid_cached_min_` is true. + + // Indicates whether the elements are sorted. + bool sorted_; + + // This represents the number of (valid) elements in this set. + size_t size_; + + // The backing storage is either the array of preallocated elements or the + // vector. The structure starts by using the preallocated elements, and + // transitions (permanently) to using the vector once more than + // kNPreallocatedElements are used. + // Elements are only invalidated when using the vector. The preallocated + // storage always only contains valid elements. + ElementType preallocated_[kNPreallocatedElements]; + std::vector* vector_; + +#ifdef VIXL_DEBUG + // Iterators acquire and release this monitor. While a set is acquired, + // certain operations are illegal to ensure that the iterator will + // correctly iterate over the elements in the set. + int monitor_; + int monitor() const { return monitor_; } + void Acquire() { monitor_++; } + void Release() { + monitor_--; + VIXL_ASSERT(monitor_ >= 0); + } +#endif + + friend class InvalSetIterator >; + typedef ElementType _ElementType; + typedef KeyType _KeyType; +}; + + +template class InvalSetIterator { + private: + // Redefine types to mirror the associated set types. + typedef typename S::_ElementType ElementType; + typedef typename S::_KeyType KeyType; + + public: + explicit InvalSetIterator(S* inval_set); + ~InvalSetIterator(); + + ElementType* Current() const; + void Advance(); + bool Done() const; + + // Mark this iterator as 'done'. + void Finish(); + + // Delete the current element and advance the iterator to point to the next + // element. + void DeleteCurrentAndAdvance(); + + static bool IsValid(const ElementType& element); + static KeyType Key(const ElementType& element); + + protected: + void MoveToValidElement(); + + // Indicates if the iterator is looking at the vector or at the preallocated + // elements. + const bool using_vector_; + // Used when looking at the preallocated elements, or in debug mode when using + // the vector to track how many times the iterator has advanced. + size_t index_; + typename std::vector::iterator iterator_; + S* inval_set_; +}; + + +template +InvalSet::InvalSet() + : valid_cached_min_(false), + sorted_(true), size_(0), vector_(NULL) { +#ifdef VIXL_DEBUG + monitor_ = 0; +#endif +} + + +template +InvalSet::~InvalSet() { + VIXL_ASSERT(monitor_ == 0); + delete vector_; +} + + +template +void InvalSet::insert(const ElementType& element) { + VIXL_ASSERT(monitor() == 0); + VIXL_ASSERT(IsValid(element)); + VIXL_ASSERT(Search(element) == NULL); + set_sorted(empty() || (sorted_ && (element > CleanBack()))); + if (IsUsingVector()) { + vector_->push_back(element); + } else { + if (size_ < kNPreallocatedElements) { + preallocated_[size_] = element; + } else { + // Transition to using the vector. + vector_ = new std::vector(preallocated_, + preallocated_ + size_); + vector_->push_back(element); + } + } + size_++; + + if (valid_cached_min_ && (element < min_element())) { + cached_min_index_ = IsUsingVector() ? vector_->size() - 1 : size_ - 1; + cached_min_key_ = Key(element); + valid_cached_min_ = true; + } + + if (ShouldReclaimMemory()) { + ReclaimMemory(); + } +} + + +template +void InvalSet::erase(const ElementType& element) { + VIXL_ASSERT(monitor() == 0); + VIXL_ASSERT(IsValid(element)); + ElementType* local_element = Search(element); + if (local_element != NULL) { + EraseInternal(local_element); + } +} + + +template +ElementType* InvalSet::Search( + const ElementType& element) { + VIXL_ASSERT(monitor() == 0); + if (empty()) { + return NULL; + } + if (ShouldReclaimMemory()) { + ReclaimMemory(); + } + if (!sorted_) { + Sort(kHardSort); + } + if (!valid_cached_min_) { + CacheMinElement(); + } + return BinarySearch(element, ElementAt(cached_min_index_), StorageEnd()); +} + + +template +size_t InvalSet::size() const { + return size_; +} + + +template +bool InvalSet::empty() const { + return size_ == 0; +} + + +template +void InvalSet::clear() { + VIXL_ASSERT(monitor() == 0); + size_ = 0; + if (IsUsingVector()) { + vector_->clear(); + } + set_sorted(true); + valid_cached_min_ = false; +} + + +template +const ElementType InvalSet::min_element() { + VIXL_ASSERT(monitor() == 0); + VIXL_ASSERT(!empty()); + CacheMinElement(); + return *ElementAt(cached_min_index_); +} + + +template +KeyType InvalSet::min_element_key() { + VIXL_ASSERT(monitor() == 0); + if (valid_cached_min_) { + return cached_min_key_; + } else { + return Key(min_element()); + } +} + + +template +bool InvalSet::IsValid(const ElementType& element) { + return Key(element) != kInvalidKey; +} + + +template +void InvalSet::EraseInternal(ElementType* element) { + // Note that this function must be safe even while an iterator has acquired + // this set. + VIXL_ASSERT(element != NULL); + size_t deleted_index = ElementIndex(element); + if (IsUsingVector()) { + VIXL_ASSERT((&(vector_->front()) <= element) && + (element <= &(vector_->back()))); + SetKey(element, kInvalidKey); + } else { + VIXL_ASSERT((preallocated_ <= element) && + (element < (preallocated_ + kNPreallocatedElements))); + ElementType* end = preallocated_ + kNPreallocatedElements; + size_t copy_size = sizeof(*element) * (end - element - 1); + memmove(element, element + 1, copy_size); + } + size_--; + + if (valid_cached_min_ && + (deleted_index == cached_min_index_)) { + if (sorted_ && !empty()) { + const ElementType* min = FirstValidElement(element, StorageEnd()); + cached_min_index_ = ElementIndex(min); + cached_min_key_ = Key(*min); + valid_cached_min_ = true; + } else { + valid_cached_min_ = false; + } + } +} + + +template +ElementType* InvalSet::BinarySearch( + const ElementType& element, ElementType* start, ElementType* end) const { + if (start == end) { + return NULL; + } + VIXL_ASSERT(sorted_); + VIXL_ASSERT(start < end); + VIXL_ASSERT(!empty()); + + // Perform a binary search through the elements while ignoring invalid + // elements. + ElementType* elements = start; + size_t low = 0; + size_t high = (end - start) - 1; + while (low < high) { + // Find valid bounds. + while (!IsValid(elements[low]) && (low < high)) ++low; + while (!IsValid(elements[high]) && (low < high)) --high; + VIXL_ASSERT(low <= high); + // Avoid overflow when computing the middle index. + size_t middle = low / 2 + high / 2 + (low & high & 1); + if ((middle == low) || (middle == high)) { + break; + } + while (!IsValid(elements[middle]) && (middle < high - 1)) ++middle; + while (!IsValid(elements[middle]) && (low + 1 < middle)) --middle; + if (!IsValid(elements[middle])) { + break; + } + if (elements[middle] < element) { + low = middle; + } else { + high = middle; + } + } + + if (elements[low] == element) return &elements[low]; + if (elements[high] == element) return &elements[high]; + return NULL; +} + + +template +void InvalSet::Sort(SortType sort_type) { + VIXL_ASSERT(monitor() == 0); + if (sort_type == kSoftSort) { + if (sorted_) { + return; + } + } + if (empty()) { + return; + } + + Clean(); + std::sort(StorageBegin(), StorageEnd()); + + set_sorted(true); + cached_min_index_ = 0; + cached_min_key_ = Key(Front()); + valid_cached_min_ = true; +} + + +template +void InvalSet::Clean() { + VIXL_ASSERT(monitor() == 0); + if (empty() || !IsUsingVector()) { + return; + } + // Manually iterate through the vector storage to discard invalid elements. + ElementType* start = &(vector_->front()); + ElementType* end = start + vector_->size(); + ElementType* c = start; + ElementType* first_invalid; + ElementType* first_valid; + ElementType* next_invalid; + + while (c < end && IsValid(*c)) { c++; } + first_invalid = c; + + while (c < end) { + while (c < end && !IsValid(*c)) { c++; } + first_valid = c; + while (c < end && IsValid(*c)) { c++; } + next_invalid = c; + + ptrdiff_t n_moved_elements = (next_invalid - first_valid); + memmove(first_invalid, first_valid, n_moved_elements * sizeof(*c)); + first_invalid = first_invalid + n_moved_elements; + c = next_invalid; + } + + // Delete the trailing invalid elements. + vector_->erase(vector_->begin() + (first_invalid - start), vector_->end()); + VIXL_ASSERT(vector_->size() == size_); + + if (sorted_) { + valid_cached_min_ = true; + cached_min_index_ = 0; + cached_min_key_ = Key(*ElementAt(0)); + } else { + valid_cached_min_ = false; + } +} + + +template +const ElementType InvalSet::Front() const { + VIXL_ASSERT(!empty()); + return IsUsingVector() ? vector_->front() : preallocated_[0]; +} + + +template +const ElementType InvalSet::Back() const { + VIXL_ASSERT(!empty()); + return IsUsingVector() ? vector_->back() : preallocated_[size_ - 1]; +} + + +template +const ElementType InvalSet::CleanBack() { + VIXL_ASSERT(monitor() == 0); + if (IsUsingVector()) { + // Delete the invalid trailing elements. + typename std::vector::reverse_iterator it = vector_->rbegin(); + while (!IsValid(*it)) { + it++; + } + vector_->erase(it.base(), vector_->end()); + } + return Back(); +} + + +template +const ElementType* InvalSet::StorageBegin() const { + return IsUsingVector() ? &(vector_->front()) : preallocated_; +} + + +template +const ElementType* InvalSet::StorageEnd() const { + return IsUsingVector() ? &(vector_->back()) + 1 : preallocated_ + size_; +} + + +template +ElementType* InvalSet::StorageBegin() { + return IsUsingVector() ? &(vector_->front()) : preallocated_; +} + + +template +ElementType* InvalSet::StorageEnd() { + return IsUsingVector() ? &(vector_->back()) + 1 : preallocated_ + size_; +} + + +template +size_t InvalSet::ElementIndex( + const ElementType* element) const { + VIXL_ASSERT((StorageBegin() <= element) && (element < StorageEnd())); + return element - StorageBegin(); +} + + +template +const ElementType* InvalSet::ElementAt( + size_t index) const { + VIXL_ASSERT( + (IsUsingVector() && (index < vector_->size())) || (index < size_)); + return StorageBegin() + index; +} + +template +ElementType* InvalSet::ElementAt(size_t index) { + VIXL_ASSERT( + (IsUsingVector() && (index < vector_->size())) || (index < size_)); + return StorageBegin() + index; +} + +template +const ElementType* InvalSet::FirstValidElement( + const ElementType* from, const ElementType* end) { + while ((from < end) && !IsValid(*from)) { + from++; + } + return from; +} + + +template +void InvalSet::CacheMinElement() { + VIXL_ASSERT(monitor() == 0); + VIXL_ASSERT(!empty()); + + if (valid_cached_min_) { + return; + } + + if (sorted_) { + const ElementType* min = FirstValidElement(StorageBegin(), StorageEnd()); + cached_min_index_ = ElementIndex(min); + cached_min_key_ = Key(*min); + valid_cached_min_ = true; + } else { + Sort(kHardSort); + } + VIXL_ASSERT(valid_cached_min_); +} + + +template +bool InvalSet::ShouldReclaimMemory() const { + if (!IsUsingVector()) { + return false; + } + size_t n_invalid_elements = vector_->size() - size_; + return (n_invalid_elements > RECLAIM_FROM) && + (n_invalid_elements > vector_->size() / RECLAIM_FACTOR); +} + + +template +void InvalSet::ReclaimMemory() { + VIXL_ASSERT(monitor() == 0); + Clean(); +} + + +template +InvalSetIterator::InvalSetIterator(S* inval_set) + : using_vector_((inval_set != NULL) && inval_set->IsUsingVector()), + index_(0), + inval_set_(inval_set) { + if (inval_set != NULL) { + inval_set->Sort(S::kSoftSort); +#ifdef VIXL_DEBUG + inval_set->Acquire(); +#endif + if (using_vector_) { + iterator_ = typename std::vector::iterator( + inval_set_->vector_->begin()); + } + MoveToValidElement(); + } +} + + +template +InvalSetIterator::~InvalSetIterator() { +#ifdef VIXL_DEBUG + if (inval_set_ != NULL) { + inval_set_->Release(); + } +#endif +} + + +template +typename S::_ElementType* InvalSetIterator::Current() const { + VIXL_ASSERT(!Done()); + if (using_vector_) { + return &(*iterator_); + } else { + return &(inval_set_->preallocated_[index_]); + } +} + + +template +void InvalSetIterator::Advance() { + VIXL_ASSERT(!Done()); + if (using_vector_) { + iterator_++; +#ifdef VIXL_DEBUG + index_++; +#endif + MoveToValidElement(); + } else { + index_++; + } +} + + +template +bool InvalSetIterator::Done() const { + if (using_vector_) { + bool done = (iterator_ == inval_set_->vector_->end()); + VIXL_ASSERT(done == (index_ == inval_set_->size())); + return done; + } else { + return index_ == inval_set_->size(); + } +} + + +template +void InvalSetIterator::Finish() { + VIXL_ASSERT(inval_set_->sorted_); + if (using_vector_) { + iterator_ = inval_set_->vector_->end(); + } + index_ = inval_set_->size(); +} + + +template +void InvalSetIterator::DeleteCurrentAndAdvance() { + if (using_vector_) { + inval_set_->EraseInternal(&(*iterator_)); + MoveToValidElement(); + } else { + inval_set_->EraseInternal(inval_set_->preallocated_ + index_); + } +} + + +template +bool InvalSetIterator::IsValid(const ElementType& element) { + return S::IsValid(element); +} + + +template +typename S::_KeyType InvalSetIterator::Key(const ElementType& element) { + return S::Key(element); +} + + +template +void InvalSetIterator::MoveToValidElement() { + if (using_vector_) { + while ((iterator_ != inval_set_->vector_->end()) && !IsValid(*iterator_)) { + iterator_++; + } + } else { + VIXL_ASSERT(inval_set_->empty() || IsValid(inval_set_->preallocated_[0])); + // Nothing to do. + } +} + +#undef TEMPLATE_INVALSET_P_DECL +#undef TEMPLATE_INVALSET_P_DEF + +} // namespace vixl + +#endif // VIXL_INVALSET_H_ diff --git a/qemu/disas/libvixl/vixl/platform.h b/qemu/disas/libvixl/vixl/platform.h new file mode 100644 index 000000000..ab588f07f --- /dev/null +++ b/qemu/disas/libvixl/vixl/platform.h @@ -0,0 +1,37 @@ +// Copyright 2014, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef PLATFORM_H +#define PLATFORM_H + +// Define platform specific functionalities. +#include + +namespace vixl { +inline void HostBreakpoint() { raise(SIGINT); } +} // namespace vixl + +#endif diff --git a/qemu/disas/libvixl/vixl/utils.cc b/qemu/disas/libvixl/vixl/utils.cc new file mode 100644 index 000000000..3b8bd75fb --- /dev/null +++ b/qemu/disas/libvixl/vixl/utils.cc @@ -0,0 +1,142 @@ +// Copyright 2015, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "vixl/utils.h" +#include + +namespace vixl { + +uint32_t float_to_rawbits(float value) { + uint32_t bits = 0; + memcpy(&bits, &value, 4); + return bits; +} + + +uint64_t double_to_rawbits(double value) { + uint64_t bits = 0; + memcpy(&bits, &value, 8); + return bits; +} + + +float rawbits_to_float(uint32_t bits) { + float value = 0.0; + memcpy(&value, &bits, 4); + return value; +} + + +double rawbits_to_double(uint64_t bits) { + double value = 0.0; + memcpy(&value, &bits, 8); + return value; +} + + +uint32_t float_sign(float val) { + uint32_t rawbits = float_to_rawbits(val); + return unsigned_bitextract_32(31, 31, rawbits); +} + + +uint32_t float_exp(float val) { + uint32_t rawbits = float_to_rawbits(val); + return unsigned_bitextract_32(30, 23, rawbits); +} + + +uint32_t float_mantissa(float val) { + uint32_t rawbits = float_to_rawbits(val); + return unsigned_bitextract_32(22, 0, rawbits); +} + + +uint32_t double_sign(double val) { + uint64_t rawbits = double_to_rawbits(val); + return static_cast(unsigned_bitextract_64(63, 63, rawbits)); +} + + +uint32_t double_exp(double val) { + uint64_t rawbits = double_to_rawbits(val); + return static_cast(unsigned_bitextract_64(62, 52, rawbits)); +} + + +uint64_t double_mantissa(double val) { + uint64_t rawbits = double_to_rawbits(val); + return unsigned_bitextract_64(51, 0, rawbits); +} + + +float float_pack(uint32_t sign, uint32_t exp, uint32_t mantissa) { + uint32_t bits = (sign << 31) | (exp << 23) | mantissa; + return rawbits_to_float(bits); +} + + +double double_pack(uint64_t sign, uint64_t exp, uint64_t mantissa) { + uint64_t bits = (sign << 63) | (exp << 52) | mantissa; + return rawbits_to_double(bits); +} + + +int float16classify(float16 value) { + uint16_t exponent_max = (1 << 5) - 1; + uint16_t exponent_mask = exponent_max << 10; + uint16_t mantissa_mask = (1 << 10) - 1; + + uint16_t exponent = (value & exponent_mask) >> 10; + uint16_t mantissa = value & mantissa_mask; + if (exponent == 0) { + if (mantissa == 0) { + return FP_ZERO; + } + return FP_SUBNORMAL; + } else if (exponent == exponent_max) { + if (mantissa == 0) { + return FP_INFINITE; + } + return FP_NAN; + } + return FP_NORMAL; +} + + +unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size) { + VIXL_ASSERT((reg_size % 8) == 0); + int count = 0; + for (unsigned i = 0; i < (reg_size / 16); i++) { + if ((imm & 0xffff) == 0) { + count++; + } + imm >>= 16; + } + return count; +} + +} // namespace vixl diff --git a/qemu/disas/libvixl/vixl/utils.h b/qemu/disas/libvixl/vixl/utils.h new file mode 100644 index 000000000..5ab134e24 --- /dev/null +++ b/qemu/disas/libvixl/vixl/utils.h @@ -0,0 +1,286 @@ +// Copyright 2015, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_UTILS_H +#define VIXL_UTILS_H + +#include +#include +#include "vixl/globals.h" +#include "vixl/compiler-intrinsics.h" + +namespace vixl { + +// Macros for compile-time format checking. +#if GCC_VERSION_OR_NEWER(4, 4, 0) +#define PRINTF_CHECK(format_index, varargs_index) \ + __attribute__((format(gnu_printf, format_index, varargs_index))) +#else +#define PRINTF_CHECK(format_index, varargs_index) +#endif + +// Check number width. +inline bool is_intn(unsigned n, int64_t x) { + VIXL_ASSERT((0 < n) && (n < 64)); + int64_t limit = INT64_C(1) << (n - 1); + return (-limit <= x) && (x < limit); +} + +inline bool is_uintn(unsigned n, int64_t x) { + VIXL_ASSERT((0 < n) && (n < 64)); + return !(x >> n); +} + +inline uint32_t truncate_to_intn(unsigned n, int64_t x) { + VIXL_ASSERT((0 < n) && (n < 64)); + return static_cast(x & ((INT64_C(1) << n) - 1)); +} + +#define INT_1_TO_63_LIST(V) \ +V(1) V(2) V(3) V(4) V(5) V(6) V(7) V(8) \ +V(9) V(10) V(11) V(12) V(13) V(14) V(15) V(16) \ +V(17) V(18) V(19) V(20) V(21) V(22) V(23) V(24) \ +V(25) V(26) V(27) V(28) V(29) V(30) V(31) V(32) \ +V(33) V(34) V(35) V(36) V(37) V(38) V(39) V(40) \ +V(41) V(42) V(43) V(44) V(45) V(46) V(47) V(48) \ +V(49) V(50) V(51) V(52) V(53) V(54) V(55) V(56) \ +V(57) V(58) V(59) V(60) V(61) V(62) V(63) + +#define DECLARE_IS_INT_N(N) \ +inline bool is_int##N(int64_t x) { return is_intn(N, x); } +#define DECLARE_IS_UINT_N(N) \ +inline bool is_uint##N(int64_t x) { return is_uintn(N, x); } +#define DECLARE_TRUNCATE_TO_INT_N(N) \ +inline uint32_t truncate_to_int##N(int x) { return truncate_to_intn(N, x); } +INT_1_TO_63_LIST(DECLARE_IS_INT_N) +INT_1_TO_63_LIST(DECLARE_IS_UINT_N) +INT_1_TO_63_LIST(DECLARE_TRUNCATE_TO_INT_N) +#undef DECLARE_IS_INT_N +#undef DECLARE_IS_UINT_N +#undef DECLARE_TRUNCATE_TO_INT_N + +// Bit field extraction. +inline uint32_t unsigned_bitextract_32(int msb, int lsb, uint32_t x) { + return (x >> lsb) & ((1 << (1 + msb - lsb)) - 1); +} + +inline uint64_t unsigned_bitextract_64(int msb, int lsb, uint64_t x) { + return (x >> lsb) & ((static_cast(1) << (1 + msb - lsb)) - 1); +} + +inline int32_t signed_bitextract_32(int msb, int lsb, int32_t x) { + return (x << (31 - msb)) >> (lsb + 31 - msb); +} + +inline int64_t signed_bitextract_64(int msb, int lsb, int64_t x) { + return (x << (63 - msb)) >> (lsb + 63 - msb); +} + +// Floating point representation. +uint32_t float_to_rawbits(float value); +uint64_t double_to_rawbits(double value); +float rawbits_to_float(uint32_t bits); +double rawbits_to_double(uint64_t bits); + +uint32_t float_sign(float val); +uint32_t float_exp(float val); +uint32_t float_mantissa(float val); +uint32_t double_sign(double val); +uint32_t double_exp(double val); +uint64_t double_mantissa(double val); + +float float_pack(uint32_t sign, uint32_t exp, uint32_t mantissa); +double double_pack(uint64_t sign, uint64_t exp, uint64_t mantissa); + +// An fpclassify() function for 16-bit half-precision floats. +int float16classify(float16 value); + +// NaN tests. +inline bool IsSignallingNaN(double num) { + const uint64_t kFP64QuietNaNMask = UINT64_C(0x0008000000000000); + uint64_t raw = double_to_rawbits(num); + if (std::isnan(num) && ((raw & kFP64QuietNaNMask) == 0)) { + return true; + } + return false; +} + + +inline bool IsSignallingNaN(float num) { + const uint32_t kFP32QuietNaNMask = 0x00400000; + uint32_t raw = float_to_rawbits(num); + if (std::isnan(num) && ((raw & kFP32QuietNaNMask) == 0)) { + return true; + } + return false; +} + + +inline bool IsSignallingNaN(float16 num) { + const uint16_t kFP16QuietNaNMask = 0x0200; + return (float16classify(num) == FP_NAN) && + ((num & kFP16QuietNaNMask) == 0); +} + + +template +inline bool IsQuietNaN(T num) { + return std::isnan(num) && !IsSignallingNaN(num); +} + + +// Convert the NaN in 'num' to a quiet NaN. +inline double ToQuietNaN(double num) { + const uint64_t kFP64QuietNaNMask = UINT64_C(0x0008000000000000); + VIXL_ASSERT(std::isnan(num)); + return rawbits_to_double(double_to_rawbits(num) | kFP64QuietNaNMask); +} + + +inline float ToQuietNaN(float num) { + const uint32_t kFP32QuietNaNMask = 0x00400000; + VIXL_ASSERT(std::isnan(num)); + return rawbits_to_float(float_to_rawbits(num) | kFP32QuietNaNMask); +} + + +// Fused multiply-add. +inline double FusedMultiplyAdd(double op1, double op2, double a) { + return fma(op1, op2, a); +} + + +inline float FusedMultiplyAdd(float op1, float op2, float a) { + return fmaf(op1, op2, a); +} + + +inline uint64_t LowestSetBit(uint64_t value) { + return value & -value; +} + + +template +inline int HighestSetBitPosition(T value) { + VIXL_ASSERT(value != 0); + return (sizeof(value) * 8 - 1) - CountLeadingZeros(value); +} + + +template +inline int WhichPowerOf2(V value) { + VIXL_ASSERT(IsPowerOf2(value)); + return CountTrailingZeros(value); +} + + +unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size); + + +template +T ReverseBits(T value) { + VIXL_ASSERT((sizeof(value) == 1) || (sizeof(value) == 2) || + (sizeof(value) == 4) || (sizeof(value) == 8)); + T result = 0; + for (unsigned i = 0; i < (sizeof(value) * 8); i++) { + result = (result << 1) | (value & 1); + value >>= 1; + } + return result; +} + + +template +T ReverseBytes(T value, int block_bytes_log2) { + VIXL_ASSERT((sizeof(value) == 4) || (sizeof(value) == 8)); + VIXL_ASSERT((1U << block_bytes_log2) <= sizeof(value)); + // Split the 64-bit value into an 8-bit array, where b[0] is the least + // significant byte, and b[7] is the most significant. + uint8_t bytes[8]; + uint64_t mask = UINT64_C(0xff00000000000000); + for (int i = 7; i >= 0; i--) { + bytes[i] = (static_cast(value) & mask) >> (i * 8); + mask >>= 8; + } + + // Permutation tables for REV instructions. + // permute_table[0] is used by REV16_x, REV16_w + // permute_table[1] is used by REV32_x, REV_w + // permute_table[2] is used by REV_x + VIXL_ASSERT((0 < block_bytes_log2) && (block_bytes_log2 < 4)); + static const uint8_t permute_table[3][8] = { {6, 7, 4, 5, 2, 3, 0, 1}, + {4, 5, 6, 7, 0, 1, 2, 3}, + {0, 1, 2, 3, 4, 5, 6, 7} }; + T result = 0; + for (int i = 0; i < 8; i++) { + result <<= 8; + result |= bytes[permute_table[block_bytes_log2 - 1][i]]; + } + return result; +} + + +// Pointer alignment +// TODO: rename/refactor to make it specific to instructions. +template +bool IsWordAligned(T pointer) { + VIXL_ASSERT(sizeof(pointer) == sizeof(intptr_t)); // NOLINT(runtime/sizeof) + return ((intptr_t)(pointer) & 3) == 0; +} + +// Increment a pointer (up to 64 bits) until it has the specified alignment. +template +T AlignUp(T pointer, size_t alignment) { + // Use C-style casts to get static_cast behaviour for integral types (T), and + // reinterpret_cast behaviour for other types. + + uint64_t pointer_raw = (uint64_t)pointer; + VIXL_STATIC_ASSERT(sizeof(pointer) <= sizeof(pointer_raw)); + + size_t align_step = (alignment - pointer_raw) % alignment; + VIXL_ASSERT((pointer_raw + align_step) % alignment == 0); + + return (T)(pointer_raw + align_step); +} + +// Decrement a pointer (up to 64 bits) until it has the specified alignment. +template +T AlignDown(T pointer, size_t alignment) { + // Use C-style casts to get static_cast behaviour for integral types (T), and + // reinterpret_cast behaviour for other types. + + uint64_t pointer_raw = (uint64_t)pointer; + VIXL_STATIC_ASSERT(sizeof(pointer) <= sizeof(pointer_raw)); + + size_t align_step = pointer_raw % alignment; + VIXL_ASSERT((pointer_raw - align_step) % alignment == 0); + + return (T)(pointer_raw - align_step); +} + +} // namespace vixl + +#endif // VIXL_UTILS_H diff --git a/qemu/disas/lm32.c b/qemu/disas/lm32.c index 1718c86e1..fcc2cde23 100644 --- a/qemu/disas/lm32.c +++ b/qemu/disas/lm32.c @@ -18,7 +18,7 @@ * */ -#include +#include "qemu/osdep.h" #include "disas/bfd.h" typedef enum { diff --git a/qemu/disas/m68k.c b/qemu/disas/m68k.c index cc0db96ca..8f74ae115 100644 --- a/qemu/disas/m68k.c +++ b/qemu/disas/m68k.c @@ -1,9 +1,8 @@ /* This file is composed of several different files from the upstream sourceware.org CVS. Original file boundaries marked with **** */ -#include +#include "qemu/osdep.h" #include -#include #include "disas/bfd.h" @@ -616,8 +615,6 @@ static const char *const reg_half_names[] = /* Maximum length of an instruction. */ #define MAXLEN 22 -#include - struct private { /* Points to first byte not fetched. */ diff --git a/qemu/disas/microblaze.c b/qemu/disas/microblaze.c index c14ab89b7..91b30acbe 100644 --- a/qemu/disas/microblaze.c +++ b/qemu/disas/microblaze.c @@ -33,13 +33,10 @@ along with this program; if not, see . */ */ -#include +#include "qemu/osdep.h" #define STATIC_TABLE #define DEFINE_TABLE -#define TRUE 1 -#define FALSE 0 - #ifndef MICROBLAZE_OPC #define MICROBLAZE_OPC /* Assembler instructions for Xilinx's microblaze processor @@ -582,7 +579,6 @@ static const char pvr_register_prefix[] = "rpvr"; #endif /* MICROBLAZE_OPC */ #include "disas/bfd.h" -#include #define get_field_rd(instr) get_field(instr, RD_MASK, RD_LOW) #define get_field_r1(instr) get_field(instr, RA_MASK, RA_LOW) @@ -604,25 +600,6 @@ char * get_field_special (long instr, struct op_code_struct * op); unsigned long read_insn_microblaze (bfd_vma memaddr, struct disassemble_info *info, struct op_code_struct **opr); -enum microblaze_instr get_insn_microblaze (long inst, - bfd_boolean *isunsignedimm, - enum microblaze_instr_type *insn_type, - short *delay_slots); -short get_delay_slots_microblaze (long inst); -enum microblaze_instr microblaze_decode_insn (long insn, - int *rd, - int *ra, - int *rb, - int *imm); -unsigned long -microblaze_get_target_address (long inst, - bfd_boolean immfound, - int immval, - long pcval, - long r1val, - long r2val, - bfd_boolean *targetvalid, - bfd_boolean *unconditionalbranch); static char * get_field (long instr, long mask, unsigned short low) @@ -968,134 +945,3 @@ print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info) /* Say how many bytes we consumed? */ return 4; } - -enum microblaze_instr -get_insn_microblaze (long inst, - bfd_boolean *isunsignedimm, - enum microblaze_instr_type *insn_type, - short *delay_slots) -{ - struct op_code_struct * op; - *isunsignedimm = FALSE; - - /* Just a linear search of the table. */ - for (op = opcodes; op->name != 0; op ++) - if (op->bit_sequence == (inst & op->opcode_mask)) - break; - - if (op->name == 0) - return invalid_inst; - else { - *isunsignedimm = (op->inst_type == INST_TYPE_RD_R1_UNSIGNED_IMM); - *insn_type = op->instr_type; - *delay_slots = op->delay_slots; - return op->instr; - } -} - -short -get_delay_slots_microblaze (long inst) -{ - bfd_boolean isunsignedimm; - enum microblaze_instr_type insn_type; - enum microblaze_instr op; - short delay_slots; - - op = get_insn_microblaze( inst, &isunsignedimm, &insn_type, &delay_slots); - if (op == invalid_inst) - return 0; - else - return delay_slots; -} - -enum microblaze_instr -microblaze_decode_insn (long insn, - int *rd, - int *ra, - int *rb, - int *imm) -{ - enum microblaze_instr op; - bfd_boolean t1; - enum microblaze_instr_type t2; - short t3; - - op = get_insn_microblaze(insn, &t1, &t2, &t3); - *rd = (insn & RD_MASK) >> RD_LOW; - *ra = (insn & RA_MASK) >> RA_LOW; - *rb = (insn & RB_MASK) >> RB_LOW; - t3 = (insn & IMM_MASK) >> IMM_LOW; - *imm = (int) t3; - return (op); -} - -unsigned long -microblaze_get_target_address (long inst, - bfd_boolean immfound, - int immval, - long pcval, - long r1val, - long r2val, - bfd_boolean *targetvalid, - bfd_boolean *unconditionalbranch) -{ - struct op_code_struct * op; - long targetaddr = 0; - - *unconditionalbranch = FALSE; - /* Just a linear search of the table. */ - for (op = opcodes; op->name != 0; op ++) - if (op->bit_sequence == (inst & op->opcode_mask)) - break; - - if (op->name == 0) { - *targetvalid = FALSE; - } else if (op->instr_type == branch_inst) { - switch (op->inst_type) { - case INST_TYPE_R2: - *unconditionalbranch = TRUE; - /* fallthru */ - case INST_TYPE_RD_R2: - case INST_TYPE_R1_R2: - targetaddr = r2val; - *targetvalid = TRUE; - if (op->inst_offset_type == INST_PC_OFFSET) - targetaddr += pcval; - break; - case INST_TYPE_IMM: - *unconditionalbranch = TRUE; - /* fallthru */ - case INST_TYPE_RD_IMM: - case INST_TYPE_R1_IMM: - if (immfound) { - targetaddr = (immval << 16) & 0xffff0000; - targetaddr |= (get_int_field_imm(inst) & 0x0000ffff); - } else { - targetaddr = get_int_field_imm(inst); - if (targetaddr & 0x8000) - targetaddr |= 0xFFFF0000; - } - if (op->inst_offset_type == INST_PC_OFFSET) - targetaddr += pcval; - *targetvalid = TRUE; - break; - default: - *targetvalid = FALSE; - break; - } - } else if (op->instr_type == return_inst) { - if (immfound) { - targetaddr = (immval << 16) & 0xffff0000; - targetaddr |= (get_int_field_imm(inst) & 0x0000ffff); - } else { - targetaddr = get_int_field_imm(inst); - if (targetaddr & 0x8000) - targetaddr |= 0xFFFF0000; - } - targetaddr += r1val; - *targetvalid = TRUE; - } else { - *targetvalid = FALSE; - } - return targetaddr; -} diff --git a/qemu/disas/mips.c b/qemu/disas/mips.c index 01336a838..249931b73 100644 --- a/qemu/disas/mips.c +++ b/qemu/disas/mips.c @@ -19,6 +19,7 @@ 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 . */ +#include "qemu/osdep.h" #include "disas/bfd.h" /* mips.h. Mips opcode list for GDB, the GNU debugger. @@ -1404,6 +1405,10 @@ const struct mips_opcode mips_builtin_opcodes[] = {"cmp.sor.d", "D,S,T", 0x46a00019, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, {"cmp.sune.d", "D,S,T", 0x46a0001a, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, {"cmp.sne.d", "D,S,T", 0x46a0001b, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"dvp", "", 0x41600024, 0xffffffff, TRAP, 0, I32R6}, +{"dvp", "t", 0x41600024, 0xffe0ffff, TRAP|WR_t, 0, I32R6}, +{"evp", "", 0x41600004, 0xffffffff, TRAP, 0, I32R6}, +{"evp", "t", 0x41600004, 0xffe0ffff, TRAP|WR_t, 0, I32R6}, /* MSA */ {"sll.b", "+d,+e,+f", 0x7800000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, @@ -2420,9 +2425,11 @@ const struct mips_opcode mips_builtin_opcodes[] = {"hibernate","", 0x42000023, 0xffffffff, 0, 0, V1 }, {"ins", "t,r,+A,+B", 0x7c000004, 0xfc00003f, WR_t|RD_s, 0, I33 }, {"jr", "s", 0x00000008, 0xfc1fffff, UBD|RD_s, 0, I1 }, +{"jr", "s", 0x00000009, 0xfc1fffff, UBD|RD_s, 0, I32R6 }, /* jalr */ /* jr.hb is officially MIPS{32,64}R2, but it works on R1 as jr with the same hazard barrier effect. */ {"jr.hb", "s", 0x00000408, 0xfc1fffff, UBD|RD_s, 0, I32 }, +{"jr.hb", "s", 0x00000409, 0xfc1fffff, UBD|RD_s, 0, I32R6 }, /* jalr.hb */ {"j", "s", 0x00000008, 0xfc1fffff, UBD|RD_s, 0, I1 }, /* jr */ /* SVR4 PIC code requires special handling for j, so it must be a macro. */ diff --git a/qemu/disas/moxie.c b/qemu/disas/moxie.c index 4c5f18047..70b49ed74 100644 --- a/qemu/disas/moxie.c +++ b/qemu/disas/moxie.c @@ -14,7 +14,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, see . */ -#include +#include "qemu/osdep.h" #define STATIC_TABLE #define DEFINE_TABLE diff --git a/qemu/disas/ppc.c b/qemu/disas/ppc.c index 99c4cbc3a..478332ba3 100644 --- a/qemu/disas/ppc.c +++ b/qemu/disas/ppc.c @@ -18,6 +18,7 @@ the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this file; see the file COPYING. If not, see . */ +#include "qemu/osdep.h" #include "disas/bfd.h" #define BFD_DEFAULT_TARGET_SIZE 64 diff --git a/qemu/disas/s390.c b/qemu/disas/s390.c index 974460c81..1f167d2ea 100644 --- a/qemu/disas/s390.c +++ b/qemu/disas/s390.c @@ -20,6 +20,7 @@ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "disas/bfd.h" @@ -613,7 +614,7 @@ static const struct s390_operand s390_operands[] = names of the instruction format that you can find in the principals of operation. 2) the last part of the definition (y in INSTR_x_y) gives you an idea - which operands the binary represenation of the instruction has. + which operands the binary representation of the instruction has. The meanings of the letters in y are: a - access register c - control register @@ -627,7 +628,7 @@ static const struct s390_operand s390_operands[] = m - mode field, 4 bit 0 - operand skipped. The order of the letters reflects the layout of the format in - storage and not the order of the paramaters of the instructions. + storage and not the order of the parameters of the instructions. The use of the letters is not a 100% match with the PoP but it is quite close. diff --git a/qemu/disas/sh4.c b/qemu/disas/sh4.c index 020f5ebb7..8b0415dfe 100644 --- a/qemu/disas/sh4.c +++ b/qemu/disas/sh4.c @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, see . */ -#include +#include "qemu/osdep.h" #include "disas/bfd.h" #define DEFINE_TABLE diff --git a/qemu/disas/sparc.c b/qemu/disas/sparc.c index f4e356573..64bba8df2 100644 --- a/qemu/disas/sparc.c +++ b/qemu/disas/sparc.c @@ -26,7 +26,7 @@ along with GAS or GDB; see the file COPYING. If not, see . */ -#include +#include "qemu/osdep.h" #include "disas/bfd.h" /* The SPARC opcode table (and other related data) is defined in @@ -2622,8 +2622,7 @@ build_hash_table (const sparc_opcode **opcode_table, memset (hash_table, 0, HASH_SIZE * sizeof (hash_table[0])); memset (hash_count, 0, HASH_SIZE * sizeof (hash_count[0])); - if (hash_buf != NULL) - free (hash_buf); + free(hash_buf); hash_buf = malloc (sizeof (* hash_buf) * num_opcodes); for (i = num_opcodes - 1; i >= 0; --i) { diff --git a/qemu/disas/tci.c b/qemu/disas/tci.c index d7b954e62..1cdf5eeaf 100644 --- a/qemu/disas/tci.c +++ b/qemu/disas/tci.c @@ -17,6 +17,7 @@ * along with this program. If not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "disas/bfd.h" #include "tcg/tcg.h" diff --git a/qemu/dma-helpers.c b/qemu/dma-helpers.c index 4faec5d0c..4ad0bca67 100644 --- a/qemu/dma-helpers.c +++ b/qemu/dma-helpers.c @@ -7,6 +7,7 @@ * (GNU GPL), version 2 or later. */ +#include "qemu/osdep.h" #include "sysemu/block-backend.h" #include "sysemu/dma.h" #include "trace.h" diff --git a/qemu/docs/bitmaps.md b/qemu/docs/bitmaps.md index fa87f077f..a2e8d5116 100644 --- a/qemu/docs/bitmaps.md +++ b/qemu/docs/bitmaps.md @@ -19,12 +19,20 @@ which is included at the end of this document. * A dirty bitmap's name is unique to the node, but bitmaps attached to different nodes can share the same name. +* Dirty bitmaps created for internal use by QEMU may be anonymous and have no + name, but any user-created bitmaps may not be. There can be any number of + anonymous bitmaps per node. + +* The name of a user-created bitmap must not be empty (""). + ## Bitmap Modes * A Bitmap can be "frozen," which means that it is currently in-use by a backup operation and cannot be deleted, renamed, written to, reset, etc. +* The normal operating mode for a bitmap is "active." + ## Basic QMP Usage ### Supported Commands ### @@ -97,11 +105,7 @@ which is included at the end of this document. } ``` -## Transactions (Not yet implemented) - -* Transactional commands are forthcoming in a future version, - and are not yet available for use. This section serves as - documentation of intent for their design and usage. +## Transactions ### Justification @@ -323,6 +327,155 @@ full backup as a backing image. "event": "BLOCK_JOB_COMPLETED" } ``` +### Partial Transactional Failures + +* Sometimes, a transaction will succeed in launching and return success, + but then later the backup jobs themselves may fail. It is possible that + a management application may have to deal with a partial backup failure + after a successful transaction. + +* If multiple backup jobs are specified in a single transaction, when one of + them fails, it will not interact with the other backup jobs in any way. + +* The job(s) that succeeded will clear the dirty bitmap associated with the + operation, but the job(s) that failed will not. It is not "safe" to delete + any incremental backups that were created successfully in this scenario, + even though others failed. + +#### Example + +* QMP example highlighting two backup jobs: + + ```json + { "execute": "transaction", + "arguments": { + "actions": [ + { "type": "drive-backup", + "data": { "device": "drive0", "bitmap": "bitmap0", + "format": "qcow2", "mode": "existing", + "sync": "incremental", "target": "d0-incr-1.qcow2" } }, + { "type": "drive-backup", + "data": { "device": "drive1", "bitmap": "bitmap1", + "format": "qcow2", "mode": "existing", + "sync": "incremental", "target": "d1-incr-1.qcow2" } }, + ] + } + } + ``` + +* QMP example response, highlighting one success and one failure: + * Acknowledgement that the Transaction was accepted and jobs were launched: + ```json + { "return": {} } + ``` + + * Later, QEMU sends notice that the first job was completed: + ```json + { "timestamp": { "seconds": 1447192343, "microseconds": 615698 }, + "data": { "device": "drive0", "type": "backup", + "speed": 0, "len": 67108864, "offset": 67108864 }, + "event": "BLOCK_JOB_COMPLETED" + } + ``` + + * Later yet, QEMU sends notice that the second job has failed: + ```json + { "timestamp": { "seconds": 1447192399, "microseconds": 683015 }, + "data": { "device": "drive1", "action": "report", + "operation": "read" }, + "event": "BLOCK_JOB_ERROR" } + ``` + + ```json + { "timestamp": { "seconds": 1447192399, "microseconds": 685853 }, + "data": { "speed": 0, "offset": 0, "len": 67108864, + "error": "Input/output error", + "device": "drive1", "type": "backup" }, + "event": "BLOCK_JOB_COMPLETED" } + +* In the above example, "d0-incr-1.qcow2" is valid and must be kept, + but "d1-incr-1.qcow2" is invalid and should be deleted. If a VM-wide + incremental backup of all drives at a point-in-time is to be made, + new backups for both drives will need to be made, taking into account + that a new incremental backup for drive0 needs to be based on top of + "d0-incr-1.qcow2." + +### Grouped Completion Mode + +* While jobs launched by transactions normally complete or fail on their own, + it is possible to instruct them to complete or fail together as a group. + +* QMP transactions take an optional properties structure that can affect + the semantics of the transaction. + +* The "completion-mode" transaction property can be either "individual" + which is the default, legacy behavior described above, or "grouped," + a new behavior detailed below. + +* Delayed Completion: In grouped completion mode, no jobs will report + success until all jobs are ready to report success. + +* Grouped failure: If any job fails in grouped completion mode, all remaining + jobs will be cancelled. Any incremental backups will restore their dirty + bitmap objects as if no backup command was ever issued. + + * Regardless of if QEMU reports a particular incremental backup job as + CANCELLED or as an ERROR, the in-memory bitmap will be restored. + +#### Example + +* Here's the same example scenario from above with the new property: + + ```json + { "execute": "transaction", + "arguments": { + "actions": [ + { "type": "drive-backup", + "data": { "device": "drive0", "bitmap": "bitmap0", + "format": "qcow2", "mode": "existing", + "sync": "incremental", "target": "d0-incr-1.qcow2" } }, + { "type": "drive-backup", + "data": { "device": "drive1", "bitmap": "bitmap1", + "format": "qcow2", "mode": "existing", + "sync": "incremental", "target": "d1-incr-1.qcow2" } }, + ], + "properties": { + "completion-mode": "grouped" + } + } + } + ``` + +* QMP example response, highlighting a failure for drive2: + * Acknowledgement that the Transaction was accepted and jobs were launched: + ```json + { "return": {} } + ``` + + * Later, QEMU sends notice that the second job has errored out, + but that the first job was also cancelled: + ```json + { "timestamp": { "seconds": 1447193702, "microseconds": 632377 }, + "data": { "device": "drive1", "action": "report", + "operation": "read" }, + "event": "BLOCK_JOB_ERROR" } + ``` + + ```json + { "timestamp": { "seconds": 1447193702, "microseconds": 640074 }, + "data": { "speed": 0, "offset": 0, "len": 67108864, + "error": "Input/output error", + "device": "drive1", "type": "backup" }, + "event": "BLOCK_JOB_COMPLETED" } + ``` + + ```json + { "timestamp": { "seconds": 1447193702, "microseconds": 640163 }, + "data": { "device": "drive0", "type": "backup", "speed": 0, + "len": 67108864, "offset": 16777216 }, + "event": "BLOCK_JOB_CANCELLED" } + ``` + #ram (0xe0000000-0xffffffff) | - +---- vga-window: alias@0xa0000-0xbfffff ---> #pci (0xa0000-0xbffff) + +---- vga-window: alias@0xa0000-0xbffff ---> #pci (0xa0000-0xbffff) | (prio 1) | +---- pci-hole: alias@0xe0000000-0xffffffff ---> #pci (0xe0000000-0xffffffff) @@ -273,8 +297,9 @@ various constraints can be supplied to control how these callbacks are called: - .valid.min_access_size, .valid.max_access_size define the access sizes (in bytes) which the device accepts; accesses outside this range will have device and bus specific behaviour (ignored, or machine check) - - .valid.aligned specifies that the device only accepts naturally aligned - accesses. Unaligned accesses invoke device and bus specific behaviour. + - .valid.unaligned specifies that the *device being modelled* supports + unaligned accesses; if false, unaligned accesses will invoke the + appropriate bus or CPU specific behaviour. - .impl.min_access_size, .impl.max_access_size define the access sizes (in bytes) supported by the *implementation*; other access sizes will be emulated using the ones available. For example a 4-byte write will be @@ -282,5 +307,5 @@ various constraints can be supplied to control how these callbacks are called: - .impl.unaligned specifies that the *implementation* supports unaligned accesses; if false, unaligned accesses will be emulated by two aligned accesses. - - .old_mmio can be used to ease porting from code using + - .old_mmio eases the porting of code that was formerly using cpu_register_io_memory(). It should not be used in new code. diff --git a/qemu/docs/migration.txt b/qemu/docs/migration.txt index f6df4beb2..90209ab29 100644 --- a/qemu/docs/migration.txt +++ b/qemu/docs/migration.txt @@ -291,3 +291,194 @@ save/send this state when we are in the middle of a pio operation (that is what ide_drive_pio_state_needed() checks). If DRQ_STAT is not enabled, the values on that fields are garbage and don't need to be sent. + += Return path = + +In most migration scenarios there is only a single data path that runs +from the source VM to the destination, typically along a single fd (although +possibly with another fd or similar for some fast way of throwing pages across). + +However, some uses need two way communication; in particular the Postcopy +destination needs to be able to request pages on demand from the source. + +For these scenarios there is a 'return path' from the destination to the source; +qemu_file_get_return_path(QEMUFile* fwdpath) gives the QEMUFile* for the return +path. + + Source side + Forward path - written by migration thread + Return path - opened by main thread, read by return-path thread + + Destination side + Forward path - read by main thread + Return path - opened by main thread, written by main thread AND postcopy + thread (protected by rp_mutex) + += Postcopy = +'Postcopy' migration is a way to deal with migrations that refuse to converge +(or take too long to converge) its plus side is that there is an upper bound on +the amount of migration traffic and time it takes, the down side is that during +the postcopy phase, a failure of *either* side or the network connection causes +the guest to be lost. + +In postcopy the destination CPUs are started before all the memory has been +transferred, and accesses to pages that are yet to be transferred cause +a fault that's translated by QEMU into a request to the source QEMU. + +Postcopy can be combined with precopy (i.e. normal migration) so that if precopy +doesn't finish in a given time the switch is made to postcopy. + +=== Enabling postcopy === + +To enable postcopy, issue this command on the monitor prior to the +start of migration: + +migrate_set_capability postcopy-ram on + +The normal commands are then used to start a migration, which is still +started in precopy mode. Issuing: + +migrate_start_postcopy + +will now cause the transition from precopy to postcopy. +It can be issued immediately after migration is started or any +time later on. Issuing it after the end of a migration is harmless. + +Note: During the postcopy phase, the bandwidth limits set using +migrate_set_speed is ignored (to avoid delaying requested pages that +the destination is waiting for). + +=== Postcopy device transfer === + +Loading of device data may cause the device emulation to access guest RAM +that may trigger faults that have to be resolved by the source, as such +the migration stream has to be able to respond with page data *during* the +device load, and hence the device data has to be read from the stream completely +before the device load begins to free the stream up. This is achieved by +'packaging' the device data into a blob that's read in one go. + +Source behaviour + +Until postcopy is entered the migration stream is identical to normal +precopy, except for the addition of a 'postcopy advise' command at +the beginning, to tell the destination that postcopy might happen. +When postcopy starts the source sends the page discard data and then +forms the 'package' containing: + + Command: 'postcopy listen' + The device state + A series of sections, identical to the precopy streams device state stream + containing everything except postcopiable devices (i.e. RAM) + Command: 'postcopy run' + +The 'package' is sent as the data part of a Command: 'CMD_PACKAGED', and the +contents are formatted in the same way as the main migration stream. + +During postcopy the source scans the list of dirty pages and sends them +to the destination without being requested (in much the same way as precopy), +however when a page request is received from the destination, the dirty page +scanning restarts from the requested location. This causes requested pages +to be sent quickly, and also causes pages directly after the requested page +to be sent quickly in the hope that those pages are likely to be used +by the destination soon. + +Destination behaviour + +Initially the destination looks the same as precopy, with a single thread +reading the migration stream; the 'postcopy advise' and 'discard' commands +are processed to change the way RAM is managed, but don't affect the stream +processing. + +------------------------------------------------------------------------------ + 1 2 3 4 5 6 7 +main -----DISCARD-CMD_PACKAGED ( LISTEN DEVICE DEVICE DEVICE RUN ) +thread | | + | (page request) + | \___ + v \ +listen thread: --- page -- page -- page -- page -- page -- + + a b c +------------------------------------------------------------------------------ + +On receipt of CMD_PACKAGED (1) + All the data associated with the package - the ( ... ) section in the +diagram - is read into memory (into a QEMUSizedBuffer), and the main thread +recurses into qemu_loadvm_state_main to process the contents of the package (2) +which contains commands (3,6) and devices (4...) + +On receipt of 'postcopy listen' - 3 -(i.e. the 1st command in the package) +a new thread (a) is started that takes over servicing the migration stream, +while the main thread carries on loading the package. It loads normal +background page data (b) but if during a device load a fault happens (5) the +returned page (c) is loaded by the listen thread allowing the main threads +device load to carry on. + +The last thing in the CMD_PACKAGED is a 'RUN' command (6) letting the destination +CPUs start running. +At the end of the CMD_PACKAGED (7) the main thread returns to normal running behaviour +and is no longer used by migration, while the listen thread carries +on servicing page data until the end of migration. + +=== Postcopy states === + +Postcopy moves through a series of states (see postcopy_state) from +ADVISE->DISCARD->LISTEN->RUNNING->END + + Advise: Set at the start of migration if postcopy is enabled, even + if it hasn't had the start command; here the destination + checks that its OS has the support needed for postcopy, and performs + setup to ensure the RAM mappings are suitable for later postcopy. + The destination will fail early in migration at this point if the + required OS support is not present. + (Triggered by reception of POSTCOPY_ADVISE command) + + Discard: Entered on receipt of the first 'discard' command; prior to + the first Discard being performed, hugepages are switched off + (using madvise) to ensure that no new huge pages are created + during the postcopy phase, and to cause any huge pages that + have discards on them to be broken. + + Listen: The first command in the package, POSTCOPY_LISTEN, switches + the destination state to Listen, and starts a new thread + (the 'listen thread') which takes over the job of receiving + pages off the migration stream, while the main thread carries + on processing the blob. With this thread able to process page + reception, the destination now 'sensitises' the RAM to detect + any access to missing pages (on Linux using the 'userfault' + system). + + Running: POSTCOPY_RUN causes the destination to synchronise all + state and start the CPUs and IO devices running. The main + thread now finishes processing the migration package and + now carries on as it would for normal precopy migration + (although it can't do the cleanup it would do as it + finishes a normal migration). + + End: The listen thread can now quit, and perform the cleanup of migration + state, the migration is now complete. + +=== Source side page maps === + +The source side keeps two bitmaps during postcopy; 'the migration bitmap' +and 'unsent map'. The 'migration bitmap' is basically the same as in +the precopy case, and holds a bit to indicate that page is 'dirty' - +i.e. needs sending. During the precopy phase this is updated as the CPU +dirties pages, however during postcopy the CPUs are stopped and nothing +should dirty anything any more. + +The 'unsent map' is used for the transition to postcopy. It is a bitmap that +has a bit cleared whenever a page is sent to the destination, however during +the transition to postcopy mode it is combined with the migration bitmap +to form a set of pages that: + a) Have been sent but then redirtied (which must be discarded) + b) Have not yet been sent - which also must be discarded to cause any + transparent huge pages built during precopy to be broken. + +Note that the contents of the unsentmap are sacrificed during the calculation +of the discard set and thus aren't valid once in postcopy. The dirtymap +is still valid and is used to ensure that no page is sent more than once. Any +request for a page that has already been sent is ignored. Duplicate requests +such as this can happen as a page is sent at about the same time the +destination accesses it. + diff --git a/qemu/docs/multiseat.txt b/qemu/docs/multiseat.txt index ebf244693..807518c8a 100644 --- a/qemu/docs/multiseat.txt +++ b/qemu/docs/multiseat.txt @@ -135,7 +135,7 @@ configuration: TAG+="seat", ENV{ID_AUTOSEAT}="1" Patch with this rule has been submitted to upstream udev/systemd, was -accepted and and should be included in the next systemd release (222). +accepted and should be included in the next systemd release (222). So, if your guest has this or a newer version, multiseat will work just fine without any manual guest configuration. diff --git a/qemu/docs/pci_expander_bridge.txt b/qemu/docs/pci_expander_bridge.txt index d7913fb4a..36750273b 100644 --- a/qemu/docs/pci_expander_bridge.txt +++ b/qemu/docs/pci_expander_bridge.txt @@ -23,9 +23,9 @@ A detailed command line would be: -m 2G -object memory-backend-ram,size=1024M,policy=bind,host-nodes=0,id=ram-node0 -numa node,nodeid=0,cpus=0,memdev=ram-node0 -object memory-backend-ram,size=1024M,policy=bind,host-nodes=1,id=ram-node1 -numa node,nodeid=1,cpus=1,memdev=ram-node1 --device pxb,id=bridge1,bus=pci.0,numa_node=1,bus_nr=4 -netdev user,id=nd-device e1000,bus=bridge1,addr=0x4,netdev=nd --device pxb,id=bridge2,bus=pci.0,numa_node=0,bus_nr=8,bus=pci.0 -device e1000,bus=bridge2,addr=0x3 --device pxb,id=bridge3,bus=pci.0,bus_nr=40,bus=pci.0 -drive if=none,id=drive0,file=[img] -device virtio-blk-pci,drive=drive0,scsi=off,bus=bridge3,addr=1 +-device pxb,id=bridge1,bus=pci.0,numa_node=1,bus_nr=4 -netdev user,id=nd -device e1000,bus=bridge1,addr=0x4,netdev=nd +-device pxb,id=bridge2,bus=pci.0,numa_node=0,bus_nr=8 -device e1000,bus=bridge2,addr=0x3 +-device pxb,id=bridge3,bus=pci.0,bus_nr=40 -drive if=none,id=drive0,file=[img] -device virtio-blk-pci,drive=drive0,scsi=off,bus=bridge3,addr=1 Here you have: - 2 NUMA nodes for the guest, 0 and 1. (both mapped to the same NUMA node in host, but you can and should put it in different host NUMA nodes) @@ -43,7 +43,7 @@ Implementation ============== The PXB is composed by: - HostBridge (TYPE_PXB_HOST) - The host bridge allows to register and query the PXB's rPCI root bus in QEMU. + The host bridge allows to register and query the PXB's PCI root bus in QEMU. - PXBDev(TYPE_PXB_DEVICE) It is a regular PCI Device that resides on the piix host-bridge bus and its bus uses the same PCI domain. However, the bus behind is exposed through ACPI as a primary PCI bus and starts a new PCI hierarchy. diff --git a/qemu/docs/qapi-code-gen.txt b/qemu/docs/qapi-code-gen.txt index 61b5be47f..0e4bafff0 100644 --- a/qemu/docs/qapi-code-gen.txt +++ b/qemu/docs/qapi-code-gen.txt @@ -1,7 +1,7 @@ = How to use the QAPI code generator = Copyright IBM Corp. 2011 -Copyright (C) 2012-2015 Red Hat, Inc. +Copyright (C) 2012-2016 Red Hat, Inc. This work is licensed under the terms of the GNU GPL, version 2 or later. See the COPYING file in the top-level directory. @@ -52,7 +52,7 @@ schema. The documentation is delimited between two lines of ##, then the first line names the expression, an optional overview is provided, then individual documentation about each member of 'data' is provided, and finally, a 'Since: x.y.z' tag lists the release that introduced -the expression. Optional fields are tagged with the phrase +the expression. Optional members are tagged with the phrase '#optional', often with their default value; and extensions added after the expression was first released are also given a '(since x.y.z)' comment. For example: @@ -106,27 +106,28 @@ Types, commands, and events share a common namespace. Therefore, generally speaking, type definitions should always use CamelCase for user-defined type names, while built-in types are lowercase. Type definitions should not end in 'Kind', as this namespace is used for -creating implicit C enums for visiting union types. Command names, -and field names within a type, should be all lower case with words +creating implicit C enums for visiting union types, or in 'List', as +this namespace is used for creating array types. Command names, +and member names within a type, should be all lower case with words separated by a hyphen. However, some existing older commands and complex types use underscore; when extending such expressions, consistency is preferred over blindly avoiding underscore. Event -names should be ALL_CAPS with words separated by underscore. The -special string '**' appears for some commands that manually perform -their own type checking rather than relying on the type-safe code -produced by the qapi code generators. +names should be ALL_CAPS with words separated by underscore. Member +names cannot start with 'has-' or 'has_', as this is reserved for +tracking optional members. -Any name (command, event, type, field, or enum value) beginning with +Any name (command, event, type, member, or enum value) beginning with "x-" is marked experimental, and may be withdrawn or changed -incompatibly in a future release. Downstream vendors may add -extensions; such extensions should begin with a prefix matching -"__RFQDN_" (for the reverse-fully-qualified-domain-name of the -vendor), even if the rest of the name uses dash (example: -__com.redhat_drive-mirror). Other than downstream extensions (with -leading underscore and the use of dots), all names should begin with a -letter, and contain only ASCII letters, digits, dash, and underscore. -It is okay to reuse names that match C keywords; the generator will -rename a field named "default" in the QAPI to "q_default" in the +incompatibly in a future release. All names must begin with a letter, +and contain only ASCII letters, digits, dash, and underscore. There +are two exceptions: enum values may start with a digit, and any +extensions added by downstream vendors should start with a prefix +matching "__RFQDN_" (for the reverse-fully-qualified-domain-name of +the vendor), even if the rest of the name uses dash (example: +__com.redhat_drive-mirror). Names beginning with 'q_' are reserved +for the generator: QMP names that resemble C keywords or other +problematic strings will be munged in C to use this prefix. For +example, a member named "default" in qapi becomes "q_default" in the generated C code. In the rest of this document, usage lines are given for each @@ -140,17 +141,26 @@ must have a value that forms a struct name. === Built-in Types === -The following types are built-in to the parser: - 'str' - arbitrary UTF-8 string - 'int' - 64-bit signed integer (although the C code may place further - restrictions on acceptable range) - 'number' - floating point number - 'bool' - JSON value of true or false - 'int8', 'int16', 'int32', 'int64' - like 'int', but enforce maximum - bit size - 'uint8', 'uint16', 'uint32', 'uint64' - unsigned counterparts - 'size' - like 'uint64', but allows scaled suffix from command line - visitor +The following types are predefined, and map to C as follows: + + Schema C JSON + str char * any JSON string, UTF-8 + number double any JSON number + int int64_t a JSON number without fractional part + that fits into the C integer type + int8 int8_t likewise + int16 int16_t likewise + int32 int32_t likewise + int64 int64_t likewise + uint8 uint8_t likewise + uint16 uint16_t likewise + uint32 uint32_t likewise + uint64 uint64_t likewise + size uint64_t like uint64_t, except StringInputVisitor + accepts size suffixes + bool bool JSON true or false + any QObject * any JSON value + QType QType JSON string matching enum QType values === Includes === @@ -163,7 +173,7 @@ The QAPI schema definitions can be modularized using the 'include' directive: The directive is evaluated recursively, and include paths are relative to the file using the directive. Multiple includes of the same file are -safe. No other keys should appear in the expression, and the include +idempotent. No other keys should appear in the expression, and the include value should be a string. As a matter of style, it is a good idea to have all files be @@ -177,11 +187,11 @@ prevent incomplete include files. Usage: { 'struct': STRING, 'data': DICT, '*base': STRUCT-NAME } -A struct is a dictionary containing a single 'data' key whose -value is a dictionary. This corresponds to a struct in C or an Object -in JSON. Each value of the 'data' dictionary must be the name of a -type, or a one-element array containing a type name. An example of a -struct is: +A struct is a dictionary containing a single 'data' key whose value is +a dictionary; the dictionary may be empty. This corresponds to a +struct in C or an Object in JSON. Each value of the 'data' dictionary +must be the name of a type, or a one-element array containing a type +name. An example of a struct is: { 'struct': 'MyType', 'data': { 'member1': 'str', 'member2': 'int', '*member3': 'str' } } @@ -207,17 +217,18 @@ and must continue to work). On output structures (only mentioned in the 'returns' side of a command), changing from mandatory to optional is in general unsafe (older clients may be -expecting the field, and could crash if it is missing), although it can be done -if the only way that the optional argument will be omitted is when it is -triggered by the presence of a new input flag to the command that older clients -don't know to send. Changing from optional to mandatory is safe. +expecting the member, and could crash if it is missing), although it +can be done if the only way that the optional argument will be omitted +is when it is triggered by the presence of a new input flag to the +command that older clients don't know to send. Changing from optional +to mandatory is safe. A structure that is used in both input and output of various commands must consider the backwards compatibility constraints of both directions of use. A struct definition can specify another struct as its base. -In this case, the fields of the base type are included as top-level fields +In this case, the members of the base type are included as top-level members of the new struct's dictionary in the Client JSON Protocol wire format. An example definition is: @@ -227,7 +238,7 @@ format. An example definition is: 'data': { '*backing': 'str' } } An example BlockdevOptionsGenericCOWFormat object on the wire could use -both fields like this: +both members like this: { "file": "/some/place/my-image", "backing": "/some/place/my-backing-file" } @@ -236,6 +247,7 @@ both fields like this: === Enumeration types === Usage: { 'enum': STRING, 'data': ARRAY-OF-STRING } + { 'enum': STRING, '*prefix': STRING, 'data': ARRAY-OF-STRING } An enumeration type is a dictionary containing a single 'data' key whose value is a list of strings. An example enumeration is: @@ -247,6 +259,13 @@ useful. The list of strings should be lower case; if an enum name represents multiple words, use '-' between words. The string 'max' is not allowed as an enum value, and values should not be repeated. +The enum constants will be named by using a heuristic to turn the +type name into a set of underscore separated words. For the example +above, 'MyEnum' will turn into 'MY_ENUM' giving a constant name +of 'MY_ENUM_VALUE1' for the first value. If the default heuristic +does not result in a desirable name, the optional 'prefix' member +can be used when defining the enum. + The enumeration values are passed as strings over the Client JSON Protocol, but are encoded as C enum integral values in generated code. While the C code starts numbering at 0, it is better to use explicit @@ -257,42 +276,43 @@ converting between strings and enum values. Since the wire format always passes by name, it is acceptable to reorder or add new enumeration members in any location without breaking clients of Client JSON Protocol; however, removing enum values would break -compatibility. For any struct that has a field that will only contain -a finite set of string values, using an enum type for that field is -better than open-coding the field to be type 'str'. +compatibility. For any struct that has a member that will only contain +a finite set of string values, using an enum type for that member is +better than open-coding the member to be type 'str'. === Union types === Usage: { 'union': STRING, 'data': DICT } -or: { 'union': STRING, 'data': DICT, 'base': STRUCT-NAME, +or: { 'union': STRING, 'data': DICT, 'base': STRUCT-NAME-OR-DICT, 'discriminator': ENUM-MEMBER-OF-BASE } Union types are used to let the user choose between several different variants for an object. There are two flavors: simple (no -discriminator or base), flat (both discriminator and base). A union +discriminator or base), and flat (both discriminator and base). A union type is defined using a data dictionary as explained in the following -paragraphs. +paragraphs. The data dictionary for either type of union must not +be empty. A simple union type defines a mapping from automatic discriminator values to data types like in this example: - { 'struct': 'FileOptions', 'data': { 'filename': 'str' } } - { 'struct': 'Qcow2Options', - 'data': { 'backing-file': 'str', 'lazy-refcounts': 'bool' } } + { 'struct': 'BlockdevOptionsFile', 'data': { 'filename': 'str' } } + { 'struct': 'BlockdevOptionsQcow2', + 'data': { 'backing': 'str', '*lazy-refcounts': 'bool' } } - { 'union': 'BlockdevOptions', - 'data': { 'file': 'FileOptions', - 'qcow2': 'Qcow2Options' } } + { 'union': 'BlockdevOptionsSimple', + 'data': { 'file': 'BlockdevOptionsFile', + 'qcow2': 'BlockdevOptionsQcow2' } } In the Client JSON Protocol, a simple union is represented by a -dictionary that contains the 'type' field as a discriminator, and a -'data' field that is of the specified data type corresponding to the +dictionary that contains the 'type' member as a discriminator, and a +'data' member that is of the specified data type corresponding to the discriminator value, as in these examples: - { "type": "file", "data" : { "filename": "/some/place/my-image" } } - { "type": "qcow2", "data" : { "backing-file": "/some/place/my-image", - "lazy-refcounts": true } } + { "type": "file", "data": { "filename": "/some/place/my-image" } } + { "type": "qcow2", "data": { "backing": "/some/place/my-image", + "lazy-refcounts": true } } The generated C code uses a struct containing a union. Additionally, an implicit C enum 'NameKind' is created, corresponding to the union @@ -300,43 +320,43 @@ an implicit C enum 'NameKind' is created, corresponding to the union the union can be named 'max', as this would collide with the implicit enum. The value for each branch can be of any type. - -A flat union definition specifies a struct as its base, and -avoids nesting on the wire. All branches of the union must be -complex types, and the top-level fields of the union dictionary on -the wire will be combination of fields from both the base type and the -appropriate branch type (when merging two dictionaries, there must be -no keys in common). The 'discriminator' field must be the name of an -enum-typed member of the base struct. +A flat union definition avoids nesting on the wire, and specifies a +set of common members that occur in all variants of the union. The +'base' key must specifiy either a type name (the type must be a +struct, not a union), or a dictionary representing an anonymous type. +All branches of the union must be complex types, and the top-level +members of the union dictionary on the wire will be combination of +members from both the base type and the appropriate branch type (when +merging two dictionaries, there must be no keys in common). The +'discriminator' member must be the name of a non-optional enum-typed +member of the base struct. The following example enhances the above simple union example by -adding a common field 'readonly', renaming the discriminator to -something more applicable, and reducing the number of {} required on -the wire: +adding an optional common member 'read-only', renaming the +discriminator to something more applicable than the simple union's +default of 'type', and reducing the number of {} required on the wire: - { 'enum': 'BlockdevDriver', 'data': [ 'raw', 'qcow2' ] } - { 'struct': 'BlockdevCommonOptions', - 'data': { 'driver': 'BlockdevDriver', 'readonly': 'bool' } } + { 'enum': 'BlockdevDriver', 'data': [ 'file', 'qcow2' ] } { 'union': 'BlockdevOptions', - 'base': 'BlockdevCommonOptions', + 'base': { 'driver': 'BlockdevDriver', '*read-only': 'bool' }, 'discriminator': 'driver', - 'data': { 'file': 'FileOptions', - 'qcow2': 'Qcow2Options' } } + 'data': { 'file': 'BlockdevOptionsFile', + 'qcow2': 'BlockdevOptionsQcow2' } } Resulting in these JSON objects: - { "driver": "file", "readonly": true, + { "driver": "file", "read-only": true, "filename": "/some/place/my-image" } - { "driver": "qcow2", "readonly": false, - "backing-file": "/some/place/my-image", "lazy-refcounts": true } + { "driver": "qcow2", "read-only": false, + "backing": "/some/place/my-image", "lazy-refcounts": true } Notice that in a flat union, the discriminator name is controlled by the user, but because it must map to a base member with enum type, the code generator can ensure that branches exist for all values of the enum (although the order of the keys need not match the declaration of the enum). In the resulting generated C data types, a flat union is -represented as a struct with the base member fields included directly, -and then a union of structures for each branch of the struct. +represented as a struct with the base members included directly, and +then a union of structures for each branch of the struct. A simple union can always be re-written as a flat union where the base class has a single member named 'type', and where each branch of the @@ -347,10 +367,9 @@ union has a struct with a single member named 'data'. That is, is identical on the wire to: { 'enum': 'Enum', 'data': ['one', 'two'] } - { 'struct': 'Base', 'data': { 'type': 'Enum' } } { 'struct': 'Branch1', 'data': { 'data': 'str' } } { 'struct': 'Branch2', 'data': { 'data': 'int' } } - { 'union': 'Flat': 'base': 'Base', 'discriminator': 'type', + { 'union': 'Flat': 'base': { 'type': 'Enum' }, 'discriminator': 'type', 'data': { 'one': 'Branch1', 'two': 'Branch2' } } @@ -363,13 +382,10 @@ data types (string, integer, number, or object, but currently not array) on the wire. The definition is similar to a simple union type, where each branch of the union names a QAPI type. For example: - { 'alternate': 'BlockRef', + { 'alternate': 'BlockdevRef', 'data': { 'definition': 'BlockdevOptions', 'reference': 'str' } } -Just like for a simple union, an implicit C enum 'NameKind' is created -to enumerate the branches for the alternate 'Name'. - Unlike a union, the discriminator string is never passed on the wire for the Client JSON Protocol. Instead, the value's JSON type serves as an implicit discriminator, which in turn means that an alternate @@ -387,14 +403,14 @@ following example objects: { "file": "my_existing_block_device_id" } { "file": { "driver": "file", - "readonly": false, + "read-only": false, "filename": "/tmp/mydisk.qcow2" } } === Commands === Usage: { 'command': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT, - '*returns': TYPE-NAME-OR-DICT, + '*returns': TYPE-NAME, '*gen': false, '*success-response': false } Commands are defined by using a dictionary containing several members, @@ -405,25 +421,23 @@ Client JSON Protocol command exchange. The 'data' argument maps to the "arguments" dictionary passed in as part of a Client JSON Protocol command. The 'data' member is optional and defaults to {} (an empty dictionary). If present, it must be the -string name of a complex type, a one-element array containing the name -of a complex type, or a dictionary that declares an anonymous type -with the same semantics as a 'struct' expression, with one exception -noted below when 'gen' is used. +string name of a complex type, or a dictionary that declares an +anonymous type with the same semantics as a 'struct' expression, with +one exception noted below when 'gen' is used. -The 'returns' member describes what will appear in the "return" field +The 'returns' member describes what will appear in the "return" member of a Client JSON Protocol reply on successful completion of a command. The member is optional from the command declaration; if absent, the -"return" field will be an empty dictionary. If 'returns' is present, +"return" member will be an empty dictionary. If 'returns' is present, it must be the string name of a complex or built-in type, a one-element array containing the name of a complex or built-in type, -or a dictionary that declares an anonymous type with the same -semantics as a 'struct' expression, with one exception noted below -when 'gen' is used. Although it is permitted to have the 'returns' -member name a built-in type or an array of built-in types, any command -that does this cannot be extended to return additional information in -the future; thus, new commands should strongly consider returning a -dictionary-based type or an array of dictionaries, even if the -dictionary only contains one field at the present. +with one exception noted below when 'gen' is used. Although it is +permitted to have the 'returns' member name a built-in type or an +array of built-in types, any command that does this cannot be extended +to return additional information in the future; thus, new commands +should strongly consider returning a dictionary-based type or an array +of dictionaries, even if the dictionary only contains one member at the +present. All commands in Client JSON Protocol use a dictionary to report failure, with no way to specify that in QAPI. Where the error return @@ -448,17 +462,14 @@ which would validate this Client JSON Protocol transaction: <= { "return": [ { "value": "one" }, { } ] } In rare cases, QAPI cannot express a type-safe representation of a -corresponding Client JSON Protocol command. In these cases, if the -command expression includes the key 'gen' with boolean value false, -then the 'data' or 'returns' member that intends to bypass generated -type-safety and do its own manual validation should use an inline -dictionary definition, with a value of '**' rather than a valid type -name for the keys that the generated code will not validate. Please -try to avoid adding new commands that rely on this, and instead use -type-safe unions. For an example of bypass usage: +corresponding Client JSON Protocol command. You then have to suppress +generation of a marshalling function by including a key 'gen' with +boolean value false, and instead write your own function. Please try +to avoid adding new commands that rely on this, and instead use +type-safe unions. For an example of this usage: { 'command': 'netdev_add', - 'data': {'type': 'str', 'id': 'str', '*props': '**'}, + 'data': {'type': 'str', 'id': 'str'}, 'gen': false } Normally, the QAPI schema is used to describe synchronous exchanges, @@ -468,7 +479,7 @@ response is not possible (although the command will still return a normal dictionary error on failure). When a successful reply is not possible, the command expression should include the optional key 'success-response' with boolean value false. So far, only QGA makes -use of this field. +use of this member. === Events === @@ -495,34 +506,255 @@ Resulting in this JSON object: "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } +== Client JSON Protocol introspection == + +Clients of a Client JSON Protocol commonly need to figure out what +exactly the server (QEMU) supports. + +For this purpose, QMP provides introspection via command +query-qmp-schema. QGA currently doesn't support introspection. + +While Client JSON Protocol wire compatibility should be maintained +between qemu versions, we cannot make the same guarantees for +introspection stability. For example, one version of qemu may provide +a non-variant optional member of a struct, and a later version rework +the member to instead be non-optional and associated with a variant. +Likewise, one version of qemu may list a member with open-ended type +'str', and a later version could convert it to a finite set of strings +via an enum type; or a member may be converted from a specific type to +an alternate that represents a choice between the original type and +something else. + +query-qmp-schema returns a JSON array of SchemaInfo objects. These +objects together describe the wire ABI, as defined in the QAPI schema. +There is no specified order to the SchemaInfo objects returned; a +client must search for a particular name throughout the entire array +to learn more about that name, but is at least guaranteed that there +will be no collisions between type, command, and event names. + +However, the SchemaInfo can't reflect all the rules and restrictions +that apply to QMP. It's interface introspection (figuring out what's +there), not interface specification. The specification is in the QAPI +schema. To understand how QMP is to be used, you need to study the +QAPI schema. + +Like any other command, query-qmp-schema is itself defined in the QAPI +schema, along with the SchemaInfo type. This text attempts to give an +overview how things work. For details you need to consult the QAPI +schema. + +SchemaInfo objects have common members "name" and "meta-type", and +additional variant members depending on the value of meta-type. + +Each SchemaInfo object describes a wire ABI entity of a certain +meta-type: a command, event or one of several kinds of type. + +SchemaInfo for commands and events have the same name as in the QAPI +schema. + +Command and event names are part of the wire ABI, but type names are +not. Therefore, the SchemaInfo for types have auto-generated +meaningless names. For readability, the examples in this section use +meaningful type names instead. + +To examine a type, start with a command or event using it, then follow +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". + +If the command takes no arguments, "arg-type" names an object type +without members. Likewise, if the command returns nothing, "ret-type" +names an object type without members. + +Example: the SchemaInfo for command query-qmp-schema + + { "name": "query-qmp-schema", "meta-type": "command", + "arg-type": "q_empty", "ret-type": "SchemaInfoList" } + + Type "q_empty" is an automatic object type without members, and type + "SchemaInfoList" is the array of SchemaInfo type. + +The SchemaInfo for an event has meta-type "event", and variant member +"arg-type". On the wire, a "data" member that the server passes in an +event conforms to the object type named by "arg-type". + +If the event carries no additional information, "arg-type" names an +object type without members. The event may not have a data member on +the wire then. + +Each command or event defined with dictionary-valued 'data' in the +QAPI schema implicitly defines an object type. + +Example: the SchemaInfo for EVENT_C from section Events + + { "name": "EVENT_C", "meta-type": "event", + "arg-type": "q_obj-EVENT_C-arg" } + + Type "q_obj-EVENT_C-arg" is an implicitly defined object type with + the two members from the event's definition. + +The SchemaInfo for struct and union types has meta-type "object". + +The SchemaInfo for a struct type has variant member "members". + +The SchemaInfo for a union type additionally has variant members "tag" +and "variants". + +"members" is a JSON array describing the object's common members, if +any. Each element is a JSON object with members "name" (the member's +name), "type" (the name of its type), and optionally "default". The +member is optional if "default" is present. Currently, "default" can +only have value null. Other values are reserved for future +extensions. The "members" array is in no particular order; clients +must search the entire object when learning whether a particular +member is supported. + +Example: the SchemaInfo for MyType from section Struct types + + { "name": "MyType", "meta-type": "object", + "members": [ + { "name": "member1", "type": "str" }, + { "name": "member2", "type": "int" }, + { "name": "member3", "type": "str", "default": null } ] } + +"tag" is the name of the common member serving as type tag. +"variants" is a JSON array describing the object's variant members. +Each element is a JSON object with members "case" (the value of type +tag this element applies to) and "type" (the name of an object type +that provides the variant members for this type tag value). The +"variants" array is in no particular order, and is not guaranteed to +list cases in the same order as the corresponding "tag" enum type. + +Example: the SchemaInfo for flat union BlockdevOptions from section +Union types + + { "name": "BlockdevOptions", "meta-type": "object", + "members": [ + { "name": "driver", "type": "BlockdevDriver" }, + { "name": "read-only", "type": "bool", "default": null } ], + "tag": "driver", + "variants": [ + { "case": "file", "type": "BlockdevOptionsFile" }, + { "case": "qcow2", "type": "BlockdevOptionsQcow2" } ] } + +Note that base types are "flattened": its members are included in the +"members" array. + +A simple union implicitly defines an enumeration type for its implicit +discriminator (called "type" on the wire, see section Union types). + +A simple union implicitly defines an object type for each of its +variants. + +Example: the SchemaInfo for simple union BlockdevOptionsSimple from section +Union types + + { "name": "BlockdevOptionsSimple", "meta-type": "object", + "members": [ + { "name": "type", "type": "BlockdevOptionsSimpleKind" } ], + "tag": "type", + "variants": [ + { "case": "file", "type": "q_obj-BlockdevOptionsFile-wrapper" }, + { "case": "qcow2", "type": "q_obj-BlockdevOptionsQcow2-wrapper" } ] } + + Enumeration type "BlockdevOptionsSimpleKind" and the object types + "q_obj-BlockdevOptionsFile-wrapper", "q_obj-BlockdevOptionsQcow2-wrapper" + are implicitly defined. + +The SchemaInfo for an alternate type has meta-type "alternate", and +variant member "members". "members" is a JSON array. Each element is +a JSON object with member "type", which names a type. Values of the +alternate type conform to exactly one of its member types. There is +no guarantee on the order in which "members" will be listed. + +Example: the SchemaInfo for BlockdevRef from section Alternate types + + { "name": "BlockdevRef", "meta-type": "alternate", + "members": [ + { "type": "BlockdevOptions" }, + { "type": "str" } ] } + +The SchemaInfo for an array type has meta-type "array", and variant +member "element-type", which names the array's element type. Array +types are implicitly defined. For convenience, the array's name may +resemble the element type; however, clients should examine member +"element-type" instead of making assumptions based on parsing member +"name". + +Example: the SchemaInfo for ['str'] + + { "name": "[str]", "meta-type": "array", + "element-type": "str" } + +The SchemaInfo for an enumeration type has meta-type "enum" and +variant member "values". The values are listed in no particular +order; clients must search the entire enum when learning whether a +particular value is supported. + +Example: the SchemaInfo for MyEnum from section Enumeration types + + { "name": "MyEnum", "meta-type": "enum", + "values": [ "value1", "value2", "value3" ] } + +The SchemaInfo for a built-in type has the same name as the type in +the QAPI schema (see section Built-in Types), with one exception +detailed below. It has variant member "json-type" that shows how +values of this type are encoded on the wire. + +Example: the SchemaInfo for str + + { "name": "str", "meta-type": "builtin", "json-type": "string" } + +The QAPI schema supports a number of integer types that only differ in +how they map to C. They are identical as far as SchemaInfo is +concerned. Therefore, they get all mapped to a single type "int" in +SchemaInfo. + +As explained above, type names are not part of the wire ABI. Not even +the names of built-in types. Clients should examine member +"json-type" instead of hard-coding names of built-in types. + + == Code generation == -Schemas are fed into 3 scripts to generate all the code/files that, paired -with the core QAPI libraries, comprise everything required to take JSON -commands read in by a Client JSON Protocol server, unmarshal the arguments into -the underlying C types, call into the corresponding C function, and map the -response back to a Client JSON Protocol response to be returned to the user. +Schemas are fed into five scripts to generate all the code/files that, +paired with the core QAPI libraries, comprise everything required to +take JSON commands read in by a Client JSON Protocol server, unmarshal +the arguments into the underlying C types, call into the corresponding +C function, map the response back to a Client JSON Protocol response +to be returned to the user, and introspect the commands. -As an example, we'll use the following schema, which describes a single -complex user-defined type (which will produce a C struct, along with a list -node structure that can be used to chain together a list of such types in -case we want to accept/return a list of this type with a command), and a -command which takes that type as a parameter and returns the same type: +As an example, we'll use the following schema, which describes a +single complex user-defined type, along with command which takes a +list of that type as a parameter, and returns a single element of that +type. The user is responsible for writing the implementation of +qmp_my_command(); everything else is produced by the generator. $ cat example-schema.json { 'struct': 'UserDefOne', - 'data': { 'integer': 'int', 'string': 'str' } } + 'data': { 'integer': 'int', '*string': 'str' } } { 'command': 'my-command', - 'data': {'arg1': 'UserDefOne'}, + 'data': { 'arg1': ['UserDefOne'] }, 'returns': 'UserDefOne' } { 'event': 'MY_EVENT' } +For a more thorough look at generated code, the testsuite includes +tests/qapi-schema/qapi-schema-tests.json that covers more examples of +what the generator will accept, and compiles the resulting C code as +part of 'make check-unit'. + === scripts/qapi-types.py === -Used to generate the C types defined by a schema. The following files are -created: +Used to generate the C types defined by a schema, along with +supporting code. The following files are created: $(prefix)qapi-types.h - C types corresponding to types defined in the schema you pass in @@ -537,77 +769,73 @@ Example: $ python scripts/qapi-types.py --output-dir="qapi-generated" \ --prefix="example-" example-schema.json + $ cat qapi-generated/example-qapi-types.h +[Uninteresting stuff omitted...] + + #ifndef EXAMPLE_QAPI_TYPES_H + #define EXAMPLE_QAPI_TYPES_H + +[Built-in types omitted...] + + typedef struct UserDefOne UserDefOne; + + typedef struct UserDefOneList UserDefOneList; + + struct UserDefOne { + int64_t integer; + bool has_string; + char *string; + }; + + void qapi_free_UserDefOne(UserDefOne *obj); + + struct UserDefOneList { + UserDefOneList *next; + UserDefOne *value; + }; + + void qapi_free_UserDefOneList(UserDefOneList *obj); + + #endif $ cat qapi-generated/example-qapi-types.c [Uninteresting stuff omitted...] - void qapi_free_UserDefOneList(UserDefOneList *obj) + void qapi_free_UserDefOne(UserDefOne *obj) { - QapiDeallocVisitor *md; + QapiDeallocVisitor *qdv; Visitor *v; if (!obj) { return; } - md = qapi_dealloc_visitor_new(); - v = qapi_dealloc_get_visitor(md); - visit_type_UserDefOneList(v, &obj, NULL, NULL); - qapi_dealloc_visitor_cleanup(md); + qdv = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(qdv); + visit_type_UserDefOne(v, NULL, &obj, NULL); + qapi_dealloc_visitor_cleanup(qdv); } - void qapi_free_UserDefOne(UserDefOne *obj) + void qapi_free_UserDefOneList(UserDefOneList *obj) { - QapiDeallocVisitor *md; + QapiDeallocVisitor *qdv; Visitor *v; if (!obj) { return; } - md = qapi_dealloc_visitor_new(); - v = qapi_dealloc_get_visitor(md); - visit_type_UserDefOne(v, &obj, NULL, NULL); - qapi_dealloc_visitor_cleanup(md); + qdv = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(qdv); + visit_type_UserDefOneList(v, NULL, &obj, NULL); + qapi_dealloc_visitor_cleanup(qdv); } - $ cat qapi-generated/example-qapi-types.h -[Uninteresting stuff omitted...] - - #ifndef EXAMPLE_QAPI_TYPES_H - #define EXAMPLE_QAPI_TYPES_H - -[Built-in types omitted...] - - typedef struct UserDefOne UserDefOne; - - typedef struct UserDefOneList - { - union { - UserDefOne *value; - uint64_t padding; - }; - struct UserDefOneList *next; - } UserDefOneList; - -[Functions on built-in types omitted...] - - struct UserDefOne - { - int64_t integer; - char *string; - }; - - void qapi_free_UserDefOneList(UserDefOneList *obj); - void qapi_free_UserDefOne(UserDefOne *obj); - - #endif - === scripts/qapi-visit.py === -Used to generate the visitor functions used to walk through and convert -a QObject (as provided by QMP) to a native C data structure and -vice-versa, as well as the visitor function used to dealloc a complex -schema-defined C type. +Used to generate the visitor functions used to walk through and +convert between a native QAPI C data structure and some other format +(such as QObject); the generated functions are named visit_type_FOO() +and visit_type_FOO_members(). The following files are generated: @@ -624,79 +852,90 @@ Example: $ python scripts/qapi-visit.py --output-dir="qapi-generated" --prefix="example-" example-schema.json + $ cat qapi-generated/example-qapi-visit.h +[Uninteresting stuff omitted...] + + #ifndef EXAMPLE_QAPI_VISIT_H + #define EXAMPLE_QAPI_VISIT_H + +[Visitors for built-in types omitted...] + + void visit_type_UserDefOne_members(Visitor *v, UserDefOne *obj, Error **errp); + void visit_type_UserDefOne(Visitor *v, const char *name, UserDefOne **obj, Error **errp); + void visit_type_UserDefOneList(Visitor *v, const char *name, UserDefOneList **obj, Error **errp); + + #endif $ cat qapi-generated/example-qapi-visit.c [Uninteresting stuff omitted...] - static void visit_type_UserDefOne_fields(Visitor *m, UserDefOne **obj, Error **errp) + void visit_type_UserDefOne_members(Visitor *v, UserDefOne *obj, Error **errp) { Error *err = NULL; - visit_type_int(m, &(*obj)->integer, "integer", &err); + + visit_type_int(v, "integer", &obj->integer, &err); if (err) { goto out; } - visit_type_str(m, &(*obj)->string, "string", &err); - if (err) { - goto out; + if (visit_optional(v, "string", &obj->has_string)) { + visit_type_str(v, "string", &obj->string, &err); + if (err) { + goto out; + } } out: error_propagate(errp, err); } - void visit_type_UserDefOne(Visitor *m, UserDefOne **obj, const char *name, Error **errp) + void visit_type_UserDefOne(Visitor *v, const char *name, UserDefOne **obj, Error **errp) { Error *err = NULL; - visit_start_struct(m, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), &err); - if (!err) { - if (*obj) { - visit_type_UserDefOne_fields(m, obj, errp); - } - visit_end_struct(m, &err); + visit_start_struct(v, name, (void **)obj, sizeof(UserDefOne), &err); + if (err) { + goto out; } + if (!*obj) { + goto out_obj; + } + visit_type_UserDefOne_members(v, *obj, &err); + error_propagate(errp, err); + err = NULL; + out_obj: + visit_end_struct(v, &err); + out: error_propagate(errp, err); } - void visit_type_UserDefOneList(Visitor *m, UserDefOneList **obj, const char *name, Error **errp) + void visit_type_UserDefOneList(Visitor *v, const char *name, UserDefOneList **obj, Error **errp) { Error *err = NULL; GenericList *i, **prev; - visit_start_list(m, name, &err); + visit_start_list(v, name, &err); if (err) { goto out; } for (prev = (GenericList **)obj; - !err && (i = visit_next_list(m, prev, &err)) != NULL; + !err && (i = visit_next_list(v, prev, sizeof(**obj))) != NULL; prev = &i) { UserDefOneList *native_i = (UserDefOneList *)i; - visit_type_UserDefOne(m, &native_i->value, NULL, &err); + visit_type_UserDefOne(v, NULL, &native_i->value, &err); } - error_propagate(errp, err); - err = NULL; - visit_end_list(m, &err); + visit_end_list(v); out: error_propagate(errp, err); } - $ cat qapi-generated/example-qapi-visit.h -[Uninteresting stuff omitted...] - - #ifndef EXAMPLE_QAPI_VISIT_H - #define EXAMPLE_QAPI_VISIT_H - -[Visitors for built-in types omitted...] - - void visit_type_UserDefOne(Visitor *m, UserDefOne **obj, const char *name, Error **errp); - void visit_type_UserDefOneList(Visitor *m, UserDefOneList **obj, const char *name, Error **errp); - - #endif === scripts/qapi-commands.py === -Used to generate the marshaling/dispatch functions for the commands defined -in the schema. The following files are generated: +Used to generate the marshaling/dispatch functions for the commands +defined in the schema. The generated code implements +qmp_marshal_COMMAND() (mentioned in qmp-commands.hx, and registered +automatically), and declares qmp_COMMAND() that the user must +implement. The following files are generated: $(prefix)qmp-marshal.c: command marshal/dispatch functions for each QMP command defined in the schema. Functions @@ -714,88 +953,88 @@ Example: $ python scripts/qapi-commands.py --output-dir="qapi-generated" --prefix="example-" example-schema.json + $ cat qapi-generated/example-qmp-commands.h +[Uninteresting stuff omitted...] + + #ifndef EXAMPLE_QMP_COMMANDS_H + #define EXAMPLE_QMP_COMMANDS_H + + #include "example-qapi-types.h" + #include "qapi/qmp/qdict.h" + #include "qapi/error.h" + + UserDefOne *qmp_my_command(UserDefOneList *arg1, Error **errp); + + #endif $ cat qapi-generated/example-qmp-marshal.c [Uninteresting stuff omitted...] - static void qmp_marshal_output_my_command(UserDefOne *ret_in, QObject **ret_out, Error **errp) + static void qmp_marshal_output_UserDefOne(UserDefOne *ret_in, QObject **ret_out, Error **errp) { - Error *local_err = NULL; - QmpOutputVisitor *mo = qmp_output_visitor_new(); - QapiDeallocVisitor *md; + Error *err = NULL; + QmpOutputVisitor *qov = qmp_output_visitor_new(); + QapiDeallocVisitor *qdv; Visitor *v; - v = qmp_output_get_visitor(mo); - visit_type_UserDefOne(v, &ret_in, "unused", &local_err); - if (local_err) { + v = qmp_output_get_visitor(qov); + visit_type_UserDefOne(v, "unused", &ret_in, &err); + if (err) { goto out; } - *ret_out = qmp_output_get_qobject(mo); + *ret_out = qmp_output_get_qobject(qov); out: - error_propagate(errp, local_err); - qmp_output_visitor_cleanup(mo); - md = qapi_dealloc_visitor_new(); - v = qapi_dealloc_get_visitor(md); - visit_type_UserDefOne(v, &ret_in, "unused", NULL); - qapi_dealloc_visitor_cleanup(md); + error_propagate(errp, err); + qmp_output_visitor_cleanup(qov); + qdv = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(qdv); + visit_type_UserDefOne(v, "unused", &ret_in, NULL); + qapi_dealloc_visitor_cleanup(qdv); } - static void qmp_marshal_input_my_command(QDict *args, QObject **ret, Error **errp) + static void qmp_marshal_my_command(QDict *args, QObject **ret, Error **errp) { - Error *local_err = NULL; - UserDefOne *retval = NULL; - QmpInputVisitor *mi = qmp_input_visitor_new_strict(QOBJECT(args)); - QapiDeallocVisitor *md; + Error *err = NULL; + UserDefOne *retval; + QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args)); + QapiDeallocVisitor *qdv; Visitor *v; - UserDefOne *arg1 = NULL; + UserDefOneList *arg1 = NULL; - v = qmp_input_get_visitor(mi); - visit_type_UserDefOne(v, &arg1, "arg1", &local_err); - if (local_err) { + v = qmp_input_get_visitor(qiv); + visit_type_UserDefOneList(v, "arg1", &arg1, &err); + if (err) { goto out; } - retval = qmp_my_command(arg1, &local_err); - if (local_err) { + retval = qmp_my_command(arg1, &err); + if (err) { goto out; } - qmp_marshal_output_my_command(retval, ret, &local_err); + qmp_marshal_output_UserDefOne(retval, ret, &err); out: - error_propagate(errp, local_err); - qmp_input_visitor_cleanup(mi); - md = qapi_dealloc_visitor_new(); - v = qapi_dealloc_get_visitor(md); - visit_type_UserDefOne(v, &arg1, "arg1", NULL); - qapi_dealloc_visitor_cleanup(md); - return; + error_propagate(errp, err); + qmp_input_visitor_cleanup(qiv); + qdv = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(qdv); + visit_type_UserDefOneList(v, "arg1", &arg1, NULL); + qapi_dealloc_visitor_cleanup(qdv); } static void qmp_init_marshal(void) { - qmp_register_command("my-command", qmp_marshal_input_my_command, QCO_NO_OPTIONS); + qmp_register_command("my-command", qmp_marshal_my_command, QCO_NO_OPTIONS); } qapi_init(qmp_init_marshal); - $ cat qapi-generated/example-qmp-commands.h -[Uninteresting stuff omitted...] - - #ifndef EXAMPLE_QMP_COMMANDS_H - #define EXAMPLE_QMP_COMMANDS_H - - #include "example-qapi-types.h" - #include "qapi/qmp/qdict.h" - #include "qapi/error.h" - - UserDefOne *qmp_my_command(UserDefOne *arg1, Error **errp); - - #endif === scripts/qapi-event.py === -Used to generate the event-related C code defined by a schema. The -following files are created: +Used to generate the event-related C code defined by a schema, with +implementations for qapi_event_send_FOO(). The following files are +created: $(prefix)qapi-event.h - Function prototypes for each event type, plus an enumeration of all event names @@ -805,13 +1044,34 @@ Example: $ python scripts/qapi-event.py --output-dir="qapi-generated" --prefix="example-" example-schema.json + $ cat qapi-generated/example-qapi-event.h +[Uninteresting stuff omitted...] + + #ifndef EXAMPLE_QAPI_EVENT_H + #define EXAMPLE_QAPI_EVENT_H + + #include "qapi/error.h" + #include "qapi/qmp/qdict.h" + #include "example-qapi-types.h" + + + void qapi_event_send_my_event(Error **errp); + + typedef enum example_QAPIEvent { + EXAMPLE_QAPI_EVENT_MY_EVENT = 0, + EXAMPLE_QAPI_EVENT__MAX = 1, + } example_QAPIEvent; + + extern const char *const example_QAPIEvent_lookup[]; + + #endif $ cat qapi-generated/example-qapi-event.c [Uninteresting stuff omitted...] void qapi_event_send_my_event(Error **errp) { QDict *qmp; - Error *local_err = NULL; + Error *err = NULL; QMPEventFuncEmit emit; emit = qmp_event_get_func_emit(); if (!emit) { @@ -820,34 +1080,48 @@ Example: qmp = qmp_event_build_dict("MY_EVENT"); - emit(EXAMPLE_QAPI_EVENT_MY_EVENT, qmp, &local_err); + emit(EXAMPLE_QAPI_EVENT_MY_EVENT, qmp, &err); - error_propagate(errp, local_err); + error_propagate(errp, err); QDECREF(qmp); } - const char *EXAMPLE_QAPIEvent_lookup[] = { - "MY_EVENT", - NULL, + const char *const example_QAPIEvent_lookup[] = { + [EXAMPLE_QAPI_EVENT_MY_EVENT] = "MY_EVENT", + [EXAMPLE_QAPI_EVENT__MAX] = NULL, }; - $ cat qapi-generated/example-qapi-event.h -[Uninteresting stuff omitted...] - #ifndef EXAMPLE_QAPI_EVENT_H - #define EXAMPLE_QAPI_EVENT_H +=== scripts/qapi-introspect.py === - #include "qapi/error.h" - #include "qapi/qmp/qdict.h" - #include "example-qapi-types.h" +Used to generate the introspection C code for a schema. The following +files are created: +$(prefix)qmp-introspect.c - Defines a string holding a JSON + description of the schema. +$(prefix)qmp-introspect.h - Declares the above string. - void qapi_event_send_my_event(Error **errp); +Example: - extern const char *EXAMPLE_QAPIEvent_lookup[]; - typedef enum EXAMPLE_QAPIEvent - { - EXAMPLE_QAPI_EVENT_MY_EVENT = 0, - EXAMPLE_QAPI_EVENT_MAX = 1, - } EXAMPLE_QAPIEvent; + $ python scripts/qapi-introspect.py --output-dir="qapi-generated" + --prefix="example-" example-schema.json + $ cat qapi-generated/example-qmp-introspect.h +[Uninteresting stuff omitted...] + + #ifndef EXAMPLE_QMP_INTROSPECT_H + #define EXAMPLE_QMP_INTROSPECT_H + + extern const char example_qmp_schema_json[]; #endif + $ cat qapi-generated/example-qmp-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\"}]"; diff --git a/qemu/docs/qcow2-cache.txt b/qemu/docs/qcow2-cache.txt new file mode 100644 index 000000000..5bb06072d --- /dev/null +++ b/qemu/docs/qcow2-cache.txt @@ -0,0 +1,164 @@ +qcow2 L2/refcount cache configuration +===================================== +Copyright (C) 2015 Igalia, S.L. +Author: Alberto Garcia + +This work is licensed under the terms of the GNU GPL, version 2 or +later. See the COPYING file in the top-level directory. + +Introduction +------------ +The QEMU qcow2 driver has two caches that can improve the I/O +performance significantly. However, setting the right cache sizes is +not a straightforward operation. + +This document attempts to give an overview of the L2 and refcount +caches, and how to configure them. + +Please refer to the docs/specs/qcow2.txt file for an in-depth +technical description of the qcow2 file format. + + +Clusters +-------- +A qcow2 file is organized in units of constant size called clusters. + +The cluster size is configurable, but it must be a power of two and +its value 512 bytes or higher. QEMU currently defaults to 64 KB +clusters, and it does not support sizes larger than 2MB. + +The 'qemu-img create' command supports specifying the size using the +cluster_size option: + + qemu-img create -f qcow2 -o cluster_size=128K hd.qcow2 4G + + +The L2 tables +------------- +The qcow2 format uses a two-level structure to map the virtual disk as +seen by the guest to the disk image in the host. These structures are +called the L1 and L2 tables. + +There is one single L1 table per disk image. The table is small and is +always kept in memory. + +There can be many L2 tables, depending on how much space has been +allocated in the image. Each table is one cluster in size. In order to +read or write data from the virtual disk, QEMU needs to read its +corresponding L2 table to find out where that data is located. Since +reading the table for each I/O operation can be expensive, QEMU keeps +an L2 cache in memory to speed up disk access. + +The size of the L2 cache can be configured, and setting the right +value can improve the I/O performance significantly. + + +The refcount blocks +------------------- +The qcow2 format also mantains a reference count for each cluster. +Reference counts are used for cluster allocation and internal +snapshots. The data is stored in a two-level structure similar to the +L1/L2 tables described above. + +The second level structures are called refcount blocks, are also one +cluster in size and the number is also variable and dependent on the +amount of allocated space. + +Each block contains a number of refcount entries. Their size (in bits) +is a power of two and must not be higher than 64. It defaults to 16 +bits, but a different value can be set using the refcount_bits option: + + qemu-img create -f qcow2 -o refcount_bits=8 hd.qcow2 4G + +QEMU keeps a refcount cache to speed up I/O much like the +aforementioned L2 cache, and its size can also be configured. + + +Choosing the right cache sizes +------------------------------ +In order to choose the cache sizes we need to know how they relate to +the amount of allocated space. + +The amount of virtual disk that can be mapped by the L2 and refcount +caches (in bytes) is: + + disk_size = l2_cache_size * cluster_size / 8 + disk_size = refcount_cache_size * cluster_size * 8 / refcount_bits + +With the default values for cluster_size (64KB) and refcount_bits +(16), that is + + disk_size = l2_cache_size * 8192 + disk_size = refcount_cache_size * 32768 + +So in order to cover n GB of disk space with the default values we +need: + + l2_cache_size = disk_size_GB * 131072 + refcount_cache_size = disk_size_GB * 32768 + +QEMU has a default L2 cache of 1MB (1048576 bytes) and a refcount +cache of 256KB (262144 bytes), so using the formulas we've just seen +we have + + 1048576 / 131072 = 8 GB of virtual disk covered by that cache + 262144 / 32768 = 8 GB + + +How to configure the cache sizes +-------------------------------- +Cache sizes can be configured using the -drive option in the +command-line, or the 'blockdev-add' QMP command. + +There are three options available, and all of them take bytes: + +"l2-cache-size": maximum size of the L2 table cache +"refcount-cache-size": maximum size of the refcount block cache +"cache-size": maximum size of both caches combined + +There are two things that need to be taken into account: + + - Both caches must have a size that is a multiple of the cluster + size. + + - If you only set one of the options above, QEMU will automatically + adjust the others so that the L2 cache is 4 times bigger than the + refcount cache. + +This means that these options are equivalent: + + -drive file=hd.qcow2,l2-cache-size=2097152 + -drive file=hd.qcow2,refcount-cache-size=524288 + -drive file=hd.qcow2,cache-size=2621440 + +The reason for this 1/4 ratio is to ensure that both caches cover the +same amount of disk space. Note however that this is only valid with +the default value of refcount_bits (16). If you are using a different +value you might want to calculate both cache sizes yourself since QEMU +will always use the same 1/4 ratio. + +It's also worth mentioning that there's no strict need for both caches +to cover the same amount of disk space. The refcount cache is used +much less often than the L2 cache, so it's perfectly reasonable to +keep it small. + + +Reducing the memory usage +------------------------- +It is possible to clean unused cache entries in order to reduce the +memory usage during periods of low I/O activity. + +The parameter "cache-clean-interval" defines an interval (in seconds). +All cache entries that haven't been accessed during that interval are +removed from memory. + +This example removes all unused cache entries every 15 minutes: + + -drive file=hd.qcow2,cache-clean-interval=900 + +If unset, the default value for this parameter is 0 and it disables +this feature. + +Note that this functionality currently relies on the MADV_DONTNEED +argument for madvise() to actually free the memory, so it is not +useful in systems that don't follow that behavior. diff --git a/qemu/docs/qmp-events.txt b/qemu/docs/qmp-events.txt new file mode 100644 index 000000000..fa7574d67 --- /dev/null +++ b/qemu/docs/qmp-events.txt @@ -0,0 +1,717 @@ + QEMU Machine Protocol Events + ============================ + +ACPI_DEVICE_OST +--------------- + +Emitted when guest executes ACPI _OST method. + + - data: ACPIOSTInfo type as described in qapi-schema.json + +{ "event": "ACPI_DEVICE_OST", + "data": { "device": "d1", "slot": "0", "slot-type": "DIMM", "source": 1, "status": 0 } } + +BALLOON_CHANGE +-------------- + +Emitted when the guest changes the actual BALLOON level. This +value is equivalent to the 'actual' field return by the +'query-balloon' command + +Data: + +- "actual": actual level of the guest memory balloon in bytes (json-number) + +Example: + +{ "event": "BALLOON_CHANGE", + "data": { "actual": 944766976 }, + "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } + +Note: this event is rate-limited. + +BLOCK_IMAGE_CORRUPTED +--------------------- + +Emitted when a disk image is being marked corrupt. The image can be +identified by its device or node name. The 'device' field is always +present for compatibility reasons, but it can be empty ("") if the +image does not have a device name associated. + +Data: + +- "device": Device name (json-string) +- "node-name": Node name (json-string, optional) +- "msg": Informative message (e.g., reason for the corruption) + (json-string) +- "offset": If the corruption resulted from an image access, this + is the host's access offset into the image + (json-int, optional) +- "size": If the corruption resulted from an image access, this + is the access size (json-int, optional) + +Example: + +{ "event": "BLOCK_IMAGE_CORRUPTED", + "data": { "device": "ide0-hd0", "node-name": "node0", + "msg": "Prevented active L1 table overwrite", "offset": 196608, + "size": 65536 }, + "timestamp": { "seconds": 1378126126, "microseconds": 966463 } } + +BLOCK_IO_ERROR +-------------- + +Emitted when a disk I/O error occurs. + +Data: + +- "device": device name (json-string) +- "operation": I/O operation (json-string, "read" or "write") +- "action": action that has been taken, it's one of the following (json-string): + "ignore": error has been ignored + "report": error has been reported to the device + "stop": the VM is going to stop because of the error + +Example: + +{ "event": "BLOCK_IO_ERROR", + "data": { "device": "ide0-hd1", + "operation": "write", + "action": "stop" }, + "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } + +Note: If action is "stop", a STOP event will eventually follow the +BLOCK_IO_ERROR event. + +BLOCK_JOB_CANCELLED +------------------- + +Emitted when a block job has been cancelled. + +Data: + +- "type": Job type (json-string; "stream" for image streaming + "commit" for block commit) +- "device": Device name (json-string) +- "len": Maximum progress value (json-int) +- "offset": Current progress value (json-int) + On success this is equal to len. + On failure this is less than len. +- "speed": Rate limit, bytes per second (json-int) + +Example: + +{ "event": "BLOCK_JOB_CANCELLED", + "data": { "type": "stream", "device": "virtio-disk0", + "len": 10737418240, "offset": 134217728, + "speed": 0 }, + "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } + +BLOCK_JOB_COMPLETED +------------------- + +Emitted when a block job has completed. + +Data: + +- "type": Job type (json-string; "stream" for image streaming + "commit" for block commit) +- "device": Device name (json-string) +- "len": Maximum progress value (json-int) +- "offset": Current progress value (json-int) + On success this is equal to len. + On failure this is less than len. +- "speed": Rate limit, bytes per second (json-int) +- "error": Error message (json-string, optional) + Only present on failure. This field contains a human-readable + error message. There are no semantics other than that streaming + has failed and clients should not try to interpret the error + string. + +Example: + +{ "event": "BLOCK_JOB_COMPLETED", + "data": { "type": "stream", "device": "virtio-disk0", + "len": 10737418240, "offset": 10737418240, + "speed": 0 }, + "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } + +BLOCK_JOB_ERROR +--------------- + +Emitted when a block job encounters an error. + +Data: + +- "device": device name (json-string) +- "operation": I/O operation (json-string, "read" or "write") +- "action": action that has been taken, it's one of the following (json-string): + "ignore": error has been ignored, the job may fail later + "report": error will be reported and the job canceled + "stop": error caused job to be paused + +Example: + +{ "event": "BLOCK_JOB_ERROR", + "data": { "device": "ide0-hd1", + "operation": "write", + "action": "stop" }, + "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } + +BLOCK_JOB_READY +--------------- + +Emitted when a block job is ready to complete. + +Data: + +- "type": Job type (json-string; "stream" for image streaming + "commit" for block commit) +- "device": Device name (json-string) +- "len": Maximum progress value (json-int) +- "offset": Current progress value (json-int) + On success this is equal to len. + On failure this is less than len. +- "speed": Rate limit, bytes per second (json-int) + +Example: + +{ "event": "BLOCK_JOB_READY", + "data": { "device": "drive0", "type": "mirror", "speed": 0, + "len": 2097152, "offset": 2097152 } + "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } + +Note: The "ready to complete" status is always reset by a BLOCK_JOB_ERROR +event. + +DEVICE_DELETED +-------------- + +Emitted whenever the device removal completion is acknowledged +by the guest. +At this point, it's safe to reuse the specified device ID. +Device removal can be initiated by the guest or by HMP/QMP commands. + +Data: + +- "device": device name (json-string, optional) +- "path": device path (json-string) + +{ "event": "DEVICE_DELETED", + "data": { "device": "virtio-net-pci-0", + "path": "/machine/peripheral/virtio-net-pci-0" }, + "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } + +DEVICE_TRAY_MOVED +----------------- + +It's emitted whenever the tray of a removable device is moved by the guest +or by HMP/QMP commands. + +Data: + +- "device": device name (json-string) +- "tray-open": true if the tray has been opened or false if it has been closed + (json-bool) + +{ "event": "DEVICE_TRAY_MOVED", + "data": { "device": "ide1-cd0", + "tray-open": true + }, + "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } + +DUMP_COMPLETED +-------------- + +Emitted when the guest has finished one memory dump. + +Data: + +- "result": DumpQueryResult type described in qapi-schema.json +- "error": Error message when dump failed. This is only a + human-readable string provided when dump failed. It should not be + parsed in any way (json-string, optional) + +Example: + +{ "event": "DUMP_COMPLETED", + "data": {"result": {"total": 1090650112, "status": "completed", + "completed": 1090650112} } } + +GUEST_PANICKED +-------------- + +Emitted when guest OS panic is detected. + +Data: + +- "action": Action that has been taken (json-string, currently always "pause"). + +Example: + +{ "event": "GUEST_PANICKED", + "data": { "action": "pause" } } + +MEM_UNPLUG_ERROR +-------------------- +Emitted when memory hot unplug error occurs. + +Data: + +- "device": device name (json-string) +- "msg": Informative message (e.g., reason for the error) (json-string) + +Example: + +{ "event": "MEM_UNPLUG_ERROR" + "data": { "device": "dimm1", + "msg": "acpi: device unplug for unsupported device" + }, + "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } + +NIC_RX_FILTER_CHANGED +--------------------- + +The event is emitted once until the query command is executed, +the first event will always be emitted. + +Data: + +- "name": net client name (json-string) +- "path": device path (json-string) + +{ "event": "NIC_RX_FILTER_CHANGED", + "data": { "name": "vnet0", + "path": "/machine/peripheral/vnet0/virtio-backend" }, + "timestamp": { "seconds": 1368697518, "microseconds": 326866 } } +} + +POWERDOWN +--------- + +Emitted when the Virtual Machine is powered down through the power +control system, such as via ACPI. + +Data: None. + +Example: + +{ "event": "POWERDOWN", + "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } + +QUORUM_FAILURE +-------------- + +Emitted by the Quorum block driver if it fails to establish a quorum. + +Data: + +- "reference": device name if defined else node name. +- "sector-num": Number of the first sector of the failed read operation. +- "sectors-count": Failed read operation sector count. + +Example: + +{ "event": "QUORUM_FAILURE", + "data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 }, + "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } + +Note: this event is rate-limited. + +QUORUM_REPORT_BAD +----------------- + +Emitted to report a corruption of a Quorum file. + +Data: + +- "type": Quorum operation type +- "error": Error message (json-string, optional) + Only present on failure. This field contains a human-readable + error message. There are no semantics other than that the + block layer reported an error and clients should not try to + interpret the error string. +- "node-name": The graph node name of the block driver state. +- "sector-num": Number of the first sector of the failed read operation. +- "sectors-count": Failed read operation sector count. + +Example: + +Read operation: +{ "event": "QUORUM_REPORT_BAD", + "data": { "node-name": "node0", "sector-num": 345435, "sectors-count": 5, + "type": "read" }, + "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } + +Flush operation: +{ "event": "QUORUM_REPORT_BAD", + "data": { "node-name": "node0", "sector-num": 0, "sectors-count": 2097120, + "type": "flush", "error": "Broken pipe" }, + "timestamp": { "seconds": 1456406829, "microseconds": 291763 } } + +Note: this event is rate-limited. + +RESET +----- + +Emitted when the Virtual Machine is reset. + +Data: None. + +Example: + +{ "event": "RESET", + "timestamp": { "seconds": 1267041653, "microseconds": 9518 } } + +RESUME +------ + +Emitted when the Virtual Machine resumes execution. + +Data: None. + +Example: + +{ "event": "RESUME", + "timestamp": { "seconds": 1271770767, "microseconds": 582542 } } + +RTC_CHANGE +---------- + +Emitted when the guest changes the RTC time. + +Data: + +- "offset": Offset between base RTC clock (as specified by -rtc base), and +new RTC clock value (json-number) + +Example: + +{ "event": "RTC_CHANGE", + "data": { "offset": 78 }, + "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } + +Note: this event is rate-limited. + +SHUTDOWN +-------- + +Emitted when the Virtual Machine has shut down, indicating that qemu +is about to exit. + +Data: None. + +Example: + +{ "event": "SHUTDOWN", + "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } + +Note: If the command-line option "-no-shutdown" has been specified, a STOP +event will eventually follow the SHUTDOWN event. + +SPICE_CONNECTED +--------------- + +Emitted when a SPICE client connects. + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") +- "client": Client information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + +Example: + +{ "timestamp": {"seconds": 1290688046, "microseconds": 388707}, + "event": "SPICE_CONNECTED", + "data": { + "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"}, + "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"} +}} + +SPICE_DISCONNECTED +------------------ + +Emitted when a SPICE client disconnects. + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") +- "client": Client information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + +Example: + +{ "timestamp": {"seconds": 1290688046, "microseconds": 388707}, + "event": "SPICE_DISCONNECTED", + "data": { + "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"}, + "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"} +}} + +SPICE_INITIALIZED +----------------- + +Emitted after initial handshake and authentication takes place (if any) +and the SPICE channel is up and running + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "auth": authentication method (json-string, optional) +- "client": Client information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "connection-id": spice connection id. All channels with the same id + belong to the same spice session (json-int) + - "channel-type": channel type. "1" is the main control channel, filter for + this one if you want track spice sessions only (json-int) + - "channel-id": channel id. Usually "0", might be different needed when + multiple channels of the same type exist, such as multiple + display channels in a multihead setup (json-int) + - "tls": whevener the channel is encrypted (json-bool) + +Example: + +{ "timestamp": {"seconds": 1290688046, "microseconds": 417172}, + "event": "SPICE_INITIALIZED", + "data": {"server": {"auth": "spice", "port": "5921", + "family": "ipv4", "host": "127.0.0.1"}, + "client": {"port": "49004", "family": "ipv4", "channel-type": 3, + "connection-id": 1804289383, "host": "127.0.0.1", + "channel-id": 0, "tls": true} +}} + +SPICE_MIGRATE_COMPLETED +----------------------- + +Emitted when SPICE migration has completed + +Data: None. + +Example: + +{ "timestamp": {"seconds": 1290688046, "microseconds": 417172}, + "event": "SPICE_MIGRATE_COMPLETED" } + +MIGRATION +--------- + +Emitted when a migration event happens + +Data: None. + + - "status": migration status + See MigrationStatus in ~/qapi-schema.json for possible values + +Example: + +{"timestamp": {"seconds": 1432121972, "microseconds": 744001}, + "event": "MIGRATION", "data": {"status": "completed"}} + +MIGRATION_PASS +-------------- + +Emitted from the source side of a migration at the start of each pass +(when it syncs the dirty bitmap) + +Data: None. + + - "pass": An incrementing count (starting at 1 on the first pass) + +Example: +{"timestamp": {"seconds": 1449669631, "microseconds": 239225}, + "event": "MIGRATION_PASS", "data": {"pass": 2}} + +STOP +---- + +Emitted when the Virtual Machine is stopped. + +Data: None. + +Example: + +{ "event": "STOP", + "timestamp": { "seconds": 1267041730, "microseconds": 281295 } } + +SUSPEND +------- + +Emitted when guest enters S3 state. + +Data: None. + +Example: + +{ "event": "SUSPEND", + "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } + +SUSPEND_DISK +------------ + +Emitted when the guest makes a request to enter S4 state. + +Data: None. + +Example: + +{ "event": "SUSPEND_DISK", + "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } + +Note: QEMU shuts down when entering S4 state. + +VNC_CONNECTED +------------- + +Emitted when a VNC client establishes a connection. + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "service": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "auth": authentication method (json-string, optional) +- "client": Client information (json-object) + - "host": IP address (json-string) + - "service": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + +Example: + +{ "event": "VNC_CONNECTED", + "data": { + "server": { "auth": "sasl", "family": "ipv4", + "service": "5901", "host": "0.0.0.0" }, + "client": { "family": "ipv4", "service": "58425", + "host": "127.0.0.1" } }, + "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } + + +Note: This event is emitted before any authentication takes place, thus +the authentication ID is not provided. + +VNC_DISCONNECTED +---------------- + +Emitted when the connection is closed. + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "service": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "auth": authentication method (json-string, optional) +- "client": Client information (json-object) + - "host": IP address (json-string) + - "service": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "x509_dname": TLS dname (json-string, optional) + - "sasl_username": SASL username (json-string, optional) + +Example: + +{ "event": "VNC_DISCONNECTED", + "data": { + "server": { "auth": "sasl", "family": "ipv4", + "service": "5901", "host": "0.0.0.0" }, + "client": { "family": "ipv4", "service": "58425", + "host": "127.0.0.1", "sasl_username": "luiz" } }, + "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } + +VNC_INITIALIZED +--------------- + +Emitted after authentication takes place (if any) and the VNC session is +made active. + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "service": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "auth": authentication method (json-string, optional) +- "client": Client information (json-object) + - "host": IP address (json-string) + - "service": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "x509_dname": TLS dname (json-string, optional) + - "sasl_username": SASL username (json-string, optional) + +Example: + +{ "event": "VNC_INITIALIZED", + "data": { + "server": { "auth": "sasl", "family": "ipv4", + "service": "5901", "host": "0.0.0.0"}, + "client": { "family": "ipv4", "service": "46089", + "host": "127.0.0.1", "sasl_username": "luiz" } }, + "timestamp": { "seconds": 1263475302, "microseconds": 150772 } } + +VSERPORT_CHANGE +--------------- + +Emitted when the guest opens or closes a virtio-serial port. + +Data: + +- "id": device identifier of the virtio-serial port (json-string) +- "open": true if the guest has opened the virtio-serial port (json-bool) + +Example: + +{ "event": "VSERPORT_CHANGE", + "data": { "id": "channel0", "open": true }, + "timestamp": { "seconds": 1401385907, "microseconds": 422329 } } + +Note: this event is rate-limited separately for each "id". + +WAKEUP +------ + +Emitted when the guest has woken up from S3 and is running. + +Data: None. + +Example: + +{ "event": "WAKEUP", + "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } + +WATCHDOG +-------- + +Emitted when the watchdog device's timer is expired. + +Data: + +- "action": Action that has been taken, it's one of the following (json-string): + "reset", "shutdown", "poweroff", "pause", "debug", or "none" + +Example: + +{ "event": "WATCHDOG", + "data": { "action": "reset" }, + "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } + +Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is +followed respectively by the RESET, SHUTDOWN, or STOP events. + +Note: this event is rate-limited. diff --git a/qemu/docs/qmp/README b/qemu/docs/qmp-intro.txt similarity index 100% rename from qemu/docs/qmp/README rename to qemu/docs/qmp-intro.txt diff --git a/qemu/docs/qmp-spec.txt b/qemu/docs/qmp-spec.txt new file mode 100644 index 000000000..f8b535601 --- /dev/null +++ b/qemu/docs/qmp-spec.txt @@ -0,0 +1,341 @@ + QEMU Machine Protocol Specification + +0. About This Document +====================== + +Copyright (C) 2009-2016 Red Hat, Inc. + +This work is licensed under the terms of the GNU GPL, version 2 or +later. See the COPYING file in the top-level directory. + +1. Introduction +=============== + +This document specifies the QEMU Machine Protocol (QMP), a JSON-based +protocol which is available for applications to operate QEMU at the +machine-level. It is also in use by the QEMU Guest Agent (QGA), which +is available for host applications to interact with the guest +operating system. + +2. Protocol Specification +========================= + +This section details the protocol format. For the purpose of this document +"Client" is any application which is using QMP to communicate with QEMU and +"Server" is QEMU itself. + +JSON data structures, when mentioned in this document, are always in the +following format: + + json-DATA-STRUCTURE-NAME + +Where DATA-STRUCTURE-NAME is any valid JSON data structure, as defined +by the JSON standard: + +http://www.ietf.org/rfc/rfc7159.txt + +The protocol is always encoded in UTF-8 except for synchronization +bytes (documented below); although thanks to json-string escape +sequences, the server will reply using only the strict ASCII subset. + +For convenience, json-object members mentioned in this document will +be in a certain order. However, in real protocol usage they can be in +ANY order, thus no particular order should be assumed. On the other +hand, use of json-array elements presumes that preserving order is +important unless specifically documented otherwise. Repeating a key +within a json-object gives unpredictable results. + +Also for convenience, the server will accept an extension of +'single-quoted' strings in place of the usual "double-quoted" +json-string, and both input forms of strings understand an additional +escape sequence of "\'" for a single quote. The server will only use +double quoting on output. + +2.1 General Definitions +----------------------- + +2.1.1 All interactions transmitted by the Server are json-objects, always + terminating with CRLF + +2.1.2 All json-objects members are mandatory when not specified otherwise + +2.2 Server Greeting +------------------- + +Right when connected the Server will issue a greeting message, which signals +that the connection has been successfully established and that the Server is +ready for capabilities negotiation (for more information refer to section +'4. Capabilities Negotiation'). + +The greeting message format is: + +{ "QMP": { "version": json-object, "capabilities": json-array } } + + Where, + +- The "version" member contains the Server's version information (the format + is the same of the query-version command) +- The "capabilities" member specify the availability of features beyond the + baseline specification; the order of elements in this array has no + particular significance, so a client must search the entire array + when looking for a particular capability + +2.2.1 Capabilities +------------------ + +As of the date this document was last revised, no server or client +capability strings have been defined. + + +2.3 Issuing Commands +-------------------- + +The format for command execution is: + +{ "execute": json-string, "arguments": json-object, "id": json-value } + + Where, + +- The "execute" member identifies the command to be executed by the Server +- The "arguments" member is used to pass any arguments required for the + execution of the command, it is optional when no arguments are + 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 + +2.4 Commands Responses +---------------------- + +There are two possible responses which the Server will issue as the result +of a command execution: success or error. + +2.4.1 success +------------- + +The format of a success response is: + +{ "return": json-value, "id": json-value } + + Where, + +- The "return" member contains the data returned by the command, which + is defined on a per-command basis (usually a json-object or + json-array of json-objects, but sometimes a json-number, json-string, + or json-array of json-strings); it is an empty json-object if the + command does not return data +- The "id" member contains the transaction identification associated + with the command execution if issued by the Client + +2.4.2 error +----------- + +The format of an error response is: + +{ "error": { "class": json-string, "desc": json-string }, "id": json-value } + + Where, + +- The "class" member contains the error class name (eg. "GenericError") +- The "desc" member is a human-readable error message. Clients should + not attempt to parse this message. +- The "id" member contains the transaction identification associated with + the command execution if issued by the Client + +NOTE: Some errors can occur before the Server is able to read the "id" member, +in these cases the "id" member will not be part of the error response, even +if provided by the client. + +2.5 Asynchronous events +----------------------- + +As a result of state changes, the Server may send messages unilaterally +to the Client at any time, when not in the middle of any other +response. They are called "asynchronous events". + +The format of asynchronous events is: + +{ "event": json-string, "data": json-object, + "timestamp": { "seconds": json-number, "microseconds": json-number } } + + Where, + +- The "event" member contains the event's name +- The "data" member contains event specific data, which is defined in a + per-event basis, it is optional +- The "timestamp" member contains the exact time of when the event + occurred in the Server. It is a fixed json-object with time in + seconds and microseconds relative to the Unix Epoch (1 Jan 1970); if + there is a failure to retrieve host time, both members of the + timestamp will be set to -1. + +For a listing of supported asynchronous events, please, refer to the +qmp-events.txt file. + +Some events are rate-limited to at most one per second. If additional +"similar" events arrive within one second, all but the last one are +dropped, and the last one is delayed. "Similar" normally means same +event type. See qmp-events.txt for details. + +2.6 QGA Synchronization +----------------------- + +When using QGA, an additional synchronization feature is built into +the protocol. If the Client sends a raw 0xFF sentinel byte (not valid +JSON), then the Server will reset its state and discard all pending +data prior to the sentinel. Conversely, if the Client makes use of +the 'guest-sync-delimited' command, the Server will send a raw 0xFF +sentinel byte prior to its response, to aid the Client in discarding +any data prior to the sentinel. + + +3. QMP Examples +=============== + +This section provides some examples of real QMP usage, in all of them +"C" stands for "Client" and "S" stands for "Server". + +3.1 Server greeting +------------------- + +S: { "QMP": { "version": { "qemu": { "micro": 50, "minor": 6, "major": 1 }, + "package": ""}, "capabilities": []}} + +3.2 Client QMP negotiation +-------------------------- +C: { "execute": "qmp_capabilities" } +S: { "return": {}} + +3.3 Simple 'stop' execution +--------------------------- + +C: { "execute": "stop" } +S: { "return": {} } + +3.4 KVM information +------------------- + +C: { "execute": "query-kvm", "id": "example" } +S: { "return": { "enabled": true, "present": true }, "id": "example"} + +3.5 Parsing error +------------------ + +C: { "execute": } +S: { "error": { "class": "GenericError", "desc": "Invalid JSON syntax" } } + +3.6 Powerdown event +------------------- + +S: { "timestamp": { "seconds": 1258551470, "microseconds": 802384 }, + "event": "POWERDOWN" } + +4. Capabilities Negotiation +=========================== + +When a Client successfully establishes a connection, the Server is in +Capabilities Negotiation mode. + +In this mode only the qmp_capabilities command is allowed to run, all +other commands will return the CommandNotFound error. Asynchronous +messages are not delivered either. + +Clients should use the qmp_capabilities command to enable capabilities +advertised in the Server's greeting (section '2.2 Server Greeting') they +support. + +When the qmp_capabilities command is issued, and if it does not return an +error, the Server enters in Command mode where capabilities changes take +effect, all commands (except qmp_capabilities) are allowed and asynchronous +messages are delivered. + +5 Compatibility Considerations +============================== + +All protocol changes or new features which modify the protocol format in an +incompatible way are disabled by default and will be advertised by the +capabilities array (section '2.2 Server Greeting'). Thus, Clients can check +that array and enable the capabilities they support. + +The QMP Server performs a type check on the arguments to a command. It +generates an error if a value does not have the expected type for its +key, or if it does not understand a key that the Client included. The +strictness of the Server catches wrong assumptions of Clients about +the Server's schema. Clients can assume that, when such validation +errors occur, they will be reported before the command generated any +side effect. + +However, Clients must not assume any particular: + +- Length of json-arrays +- Size of json-objects; in particular, future versions of QEMU may add + new keys and Clients should be able to ignore them. +- Order of json-object members or json-array elements +- Amount of errors generated by a command, that is, new errors can be added + to any existing command in newer versions of the Server + +Any command or member name beginning with "x-" is deemed experimental, +and may be withdrawn or changed in an incompatible manner in a future +release. + +Of course, the Server does guarantee to send valid JSON. But apart from +this, a Client should be "conservative in what they send, and liberal in +what they accept". + +6. Downstream extension of QMP +============================== + +We recommend that downstream consumers of QEMU do *not* modify QMP. +Management tools should be able to support both upstream and downstream +versions of QMP without special logic, and downstream extensions are +inherently at odds with that. + +However, we recognize that it is sometimes impossible for downstreams to +avoid modifying QMP. Both upstream and downstream need to take care to +preserve long-term compatibility and interoperability. + +To help with that, QMP reserves JSON object member names beginning with +'__' (double underscore) for downstream use ("downstream names"). This +means upstream will never use any downstream names for its commands, +arguments, errors, asynchronous events, and so forth. + +Any new names downstream wishes to add must begin with '__'. To +ensure compatibility with other downstreams, it is strongly +recommended that you prefix your downstream names with '__RFQDN_' where +RFQDN is a valid, reverse fully qualified domain name which you +control. For example, a qemu-kvm specific monitor command would be: + + (qemu) __org.linux-kvm_enable_irqchip + +Downstream must not change the server greeting (section 2.2) other than +to offer additional capabilities. But see below for why even that is +discouraged. + +Section '5 Compatibility Considerations' applies to downstream as well +as to upstream, obviously. It follows that downstream must behave +exactly like upstream for any input not containing members with +downstream names ("downstream members"), except it may add members +with downstream names to its output. + +Thus, a client should not be able to distinguish downstream from +upstream as long as it doesn't send input with downstream members, and +properly ignores any downstream members in the output it receives. + +Advice on downstream modifications: + +1. Introducing new commands is okay. If you want to extend an existing + command, consider introducing a new one with the new behaviour + instead. + +2. Introducing new asynchronous messages is okay. If you want to extend + an existing message, consider adding a new one instead. + +3. Introducing new errors for use in new commands is okay. Adding new + errors to existing commands counts as extension, so 1. applies. + +4. New capabilities are strongly discouraged. Capabilities are for + evolving the basic protocol, and multiple diverging basic protocol + dialects are most undesirable. diff --git a/qemu/docs/qmp/qmp-events.txt b/qemu/docs/qmp/qmp-events.txt deleted file mode 100644 index d92cc4833..000000000 --- a/qemu/docs/qmp/qmp-events.txt +++ /dev/null @@ -1,664 +0,0 @@ - QEMU Machine Protocol Events - ============================ - -ACPI_DEVICE_OST ---------------- - -Emitted when guest executes ACPI _OST method. - - - data: ACPIOSTInfo type as described in qapi-schema.json - -{ "event": "ACPI_DEVICE_OST", - "data": { "device": "d1", "slot": "0", "slot-type": "DIMM", "source": 1, "status": 0 } } - -BALLOON_CHANGE --------------- - -Emitted when the guest changes the actual BALLOON level. This -value is equivalent to the 'actual' field return by the -'query-balloon' command - -Data: - -- "actual": actual level of the guest memory balloon in bytes (json-number) - -Example: - -{ "event": "BALLOON_CHANGE", - "data": { "actual": 944766976 }, - "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } - -BLOCK_IMAGE_CORRUPTED ---------------------- - -Emitted when a disk image is being marked corrupt. The image can be -identified by its device or node name. The 'device' field is always -present for compatibility reasons, but it can be empty ("") if the -image does not have a device name associated. - -Data: - -- "device": Device name (json-string) -- "node-name": Node name (json-string, optional) -- "msg": Informative message (e.g., reason for the corruption) - (json-string) -- "offset": If the corruption resulted from an image access, this - is the host's access offset into the image - (json-int, optional) -- "size": If the corruption resulted from an image access, this - is the access size (json-int, optional) - -Example: - -{ "event": "BLOCK_IMAGE_CORRUPTED", - "data": { "device": "ide0-hd0", "node-name": "node0", - "msg": "Prevented active L1 table overwrite", "offset": 196608, - "size": 65536 }, - "timestamp": { "seconds": 1378126126, "microseconds": 966463 } } - -BLOCK_IO_ERROR --------------- - -Emitted when a disk I/O error occurs. - -Data: - -- "device": device name (json-string) -- "operation": I/O operation (json-string, "read" or "write") -- "action": action that has been taken, it's one of the following (json-string): - "ignore": error has been ignored - "report": error has been reported to the device - "stop": the VM is going to stop because of the error - -Example: - -{ "event": "BLOCK_IO_ERROR", - "data": { "device": "ide0-hd1", - "operation": "write", - "action": "stop" }, - "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } - -Note: If action is "stop", a STOP event will eventually follow the -BLOCK_IO_ERROR event. - -BLOCK_JOB_CANCELLED -------------------- - -Emitted when a block job has been cancelled. - -Data: - -- "type": Job type (json-string; "stream" for image streaming - "commit" for block commit) -- "device": Device name (json-string) -- "len": Maximum progress value (json-int) -- "offset": Current progress value (json-int) - On success this is equal to len. - On failure this is less than len. -- "speed": Rate limit, bytes per second (json-int) - -Example: - -{ "event": "BLOCK_JOB_CANCELLED", - "data": { "type": "stream", "device": "virtio-disk0", - "len": 10737418240, "offset": 134217728, - "speed": 0 }, - "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } - -BLOCK_JOB_COMPLETED -------------------- - -Emitted when a block job has completed. - -Data: - -- "type": Job type (json-string; "stream" for image streaming - "commit" for block commit) -- "device": Device name (json-string) -- "len": Maximum progress value (json-int) -- "offset": Current progress value (json-int) - On success this is equal to len. - On failure this is less than len. -- "speed": Rate limit, bytes per second (json-int) -- "error": Error message (json-string, optional) - Only present on failure. This field contains a human-readable - error message. There are no semantics other than that streaming - has failed and clients should not try to interpret the error - string. - -Example: - -{ "event": "BLOCK_JOB_COMPLETED", - "data": { "type": "stream", "device": "virtio-disk0", - "len": 10737418240, "offset": 10737418240, - "speed": 0 }, - "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } - -BLOCK_JOB_ERROR ---------------- - -Emitted when a block job encounters an error. - -Data: - -- "device": device name (json-string) -- "operation": I/O operation (json-string, "read" or "write") -- "action": action that has been taken, it's one of the following (json-string): - "ignore": error has been ignored, the job may fail later - "report": error will be reported and the job canceled - "stop": error caused job to be paused - -Example: - -{ "event": "BLOCK_JOB_ERROR", - "data": { "device": "ide0-hd1", - "operation": "write", - "action": "stop" }, - "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } - -BLOCK_JOB_READY ---------------- - -Emitted when a block job is ready to complete. - -Data: - -- "type": Job type (json-string; "stream" for image streaming - "commit" for block commit) -- "device": Device name (json-string) -- "len": Maximum progress value (json-int) -- "offset": Current progress value (json-int) - On success this is equal to len. - On failure this is less than len. -- "speed": Rate limit, bytes per second (json-int) - -Example: - -{ "event": "BLOCK_JOB_READY", - "data": { "device": "drive0", "type": "mirror", "speed": 0, - "len": 2097152, "offset": 2097152 } - "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } - -Note: The "ready to complete" status is always reset by a BLOCK_JOB_ERROR -event. - -DEVICE_DELETED --------------- - -Emitted whenever the device removal completion is acknowledged -by the guest. -At this point, it's safe to reuse the specified device ID. -Device removal can be initiated by the guest or by HMP/QMP commands. - -Data: - -- "device": device name (json-string, optional) -- "path": device path (json-string) - -{ "event": "DEVICE_DELETED", - "data": { "device": "virtio-net-pci-0", - "path": "/machine/peripheral/virtio-net-pci-0" }, - "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } - -DEVICE_TRAY_MOVED ------------------ - -It's emitted whenever the tray of a removable device is moved by the guest -or by HMP/QMP commands. - -Data: - -- "device": device name (json-string) -- "tray-open": true if the tray has been opened or false if it has been closed - (json-bool) - -{ "event": "DEVICE_TRAY_MOVED", - "data": { "device": "ide1-cd0", - "tray-open": true - }, - "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } - -GUEST_PANICKED --------------- - -Emitted when guest OS panic is detected. - -Data: - -- "action": Action that has been taken (json-string, currently always "pause"). - -Example: - -{ "event": "GUEST_PANICKED", - "data": { "action": "pause" } } - -MEM_UNPLUG_ERROR --------------------- -Emitted when memory hot unplug error occurs. - -Data: - -- "device": device name (json-string) -- "msg": Informative message (e.g., reason for the error) (json-string) - -Example: - -{ "event": "MEM_UNPLUG_ERROR" - "data": { "device": "dimm1", - "msg": "acpi: device unplug for unsupported device" - }, - "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } - -NIC_RX_FILTER_CHANGED ---------------------- - -The event is emitted once until the query command is executed, -the first event will always be emitted. - -Data: - -- "name": net client name (json-string) -- "path": device path (json-string) - -{ "event": "NIC_RX_FILTER_CHANGED", - "data": { "name": "vnet0", - "path": "/machine/peripheral/vnet0/virtio-backend" }, - "timestamp": { "seconds": 1368697518, "microseconds": 326866 } } -} - -POWERDOWN ---------- - -Emitted when the Virtual Machine is powered down through the power -control system, such as via ACPI. - -Data: None. - -Example: - -{ "event": "POWERDOWN", - "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } - -QUORUM_FAILURE --------------- - -Emitted by the Quorum block driver if it fails to establish a quorum. - -Data: - -- "reference": device name if defined else node name. -- "sector-num": Number of the first sector of the failed read operation. -- "sectors-count": Failed read operation sector count. - -Example: - -{ "event": "QUORUM_FAILURE", - "data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 }, - "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } - -QUORUM_REPORT_BAD ------------------ - -Emitted to report a corruption of a Quorum file. - -Data: - -- "error": Error message (json-string, optional) - Only present on failure. This field contains a human-readable - error message. There are no semantics other than that the - block layer reported an error and clients should not try to - interpret the error string. -- "node-name": The graph node name of the block driver state. -- "sector-num": Number of the first sector of the failed read operation. -- "sectors-count": Failed read operation sector count. - -Example: - -{ "event": "QUORUM_REPORT_BAD", - "data": { "node-name": "1.raw", "sector-num": 345435, "sectors-count": 5 }, - "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } - -RESET ------ - -Emitted when the Virtual Machine is reset. - -Data: None. - -Example: - -{ "event": "RESET", - "timestamp": { "seconds": 1267041653, "microseconds": 9518 } } - -RESUME ------- - -Emitted when the Virtual Machine resumes execution. - -Data: None. - -Example: - -{ "event": "RESUME", - "timestamp": { "seconds": 1271770767, "microseconds": 582542 } } - -RTC_CHANGE ----------- - -Emitted when the guest changes the RTC time. - -Data: - -- "offset": Offset between base RTC clock (as specified by -rtc base), and -new RTC clock value (json-number) - -Example: - -{ "event": "RTC_CHANGE", - "data": { "offset": 78 }, - "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } - -SHUTDOWN --------- - -Emitted when the Virtual Machine has shut down, indicating that qemu -is about to exit. - -Data: None. - -Example: - -{ "event": "SHUTDOWN", - "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } - -Note: If the command-line option "-no-shutdown" has been specified, a STOP -event will eventually follow the SHUTDOWN event. - -SPICE_CONNECTED ---------------- - -Emitted when a SPICE client connects. - -Data: - -- "server": Server information (json-object) - - "host": IP address (json-string) - - "port": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") -- "client": Client information (json-object) - - "host": IP address (json-string) - - "port": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - -Example: - -{ "timestamp": {"seconds": 1290688046, "microseconds": 388707}, - "event": "SPICE_CONNECTED", - "data": { - "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"}, - "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"} -}} - -SPICE_DISCONNECTED ------------------- - -Emitted when a SPICE client disconnects. - -Data: - -- "server": Server information (json-object) - - "host": IP address (json-string) - - "port": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") -- "client": Client information (json-object) - - "host": IP address (json-string) - - "port": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - -Example: - -{ "timestamp": {"seconds": 1290688046, "microseconds": 388707}, - "event": "SPICE_DISCONNECTED", - "data": { - "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"}, - "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"} -}} - -SPICE_INITIALIZED ------------------ - -Emitted after initial handshake and authentication takes place (if any) -and the SPICE channel is up and running - -Data: - -- "server": Server information (json-object) - - "host": IP address (json-string) - - "port": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - - "auth": authentication method (json-string, optional) -- "client": Client information (json-object) - - "host": IP address (json-string) - - "port": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - - "connection-id": spice connection id. All channels with the same id - belong to the same spice session (json-int) - - "channel-type": channel type. "1" is the main control channel, filter for - this one if you want track spice sessions only (json-int) - - "channel-id": channel id. Usually "0", might be different needed when - multiple channels of the same type exist, such as multiple - display channels in a multihead setup (json-int) - - "tls": whevener the channel is encrypted (json-bool) - -Example: - -{ "timestamp": {"seconds": 1290688046, "microseconds": 417172}, - "event": "SPICE_INITIALIZED", - "data": {"server": {"auth": "spice", "port": "5921", - "family": "ipv4", "host": "127.0.0.1"}, - "client": {"port": "49004", "family": "ipv4", "channel-type": 3, - "connection-id": 1804289383, "host": "127.0.0.1", - "channel-id": 0, "tls": true} -}} - -SPICE_MIGRATE_COMPLETED ------------------------ - -Emitted when SPICE migration has completed - -Data: None. - -Example: - -{ "timestamp": {"seconds": 1290688046, "microseconds": 417172}, - "event": "SPICE_MIGRATE_COMPLETED" } - -MIGRATION ---------- - -Emitted when a migration event happens - -Data: None. - - - "status": migration status - See MigrationStatus in ~/qapi-schema.json for possible values - -Example: - -{"timestamp": {"seconds": 1432121972, "microseconds": 744001}, - "event": "MIGRATION", "data": {"status": "completed"}} - -STOP ----- - -Emitted when the Virtual Machine is stopped. - -Data: None. - -Example: - -{ "event": "STOP", - "timestamp": { "seconds": 1267041730, "microseconds": 281295 } } - -SUSPEND -------- - -Emitted when guest enters S3 state. - -Data: None. - -Example: - -{ "event": "SUSPEND", - "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } - -SUSPEND_DISK ------------- - -Emitted when the guest makes a request to enter S4 state. - -Data: None. - -Example: - -{ "event": "SUSPEND_DISK", - "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } - -Note: QEMU shuts down when entering S4 state. - -VNC_CONNECTED -------------- - -Emitted when a VNC client establishes a connection. - -Data: - -- "server": Server information (json-object) - - "host": IP address (json-string) - - "service": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - - "auth": authentication method (json-string, optional) -- "client": Client information (json-object) - - "host": IP address (json-string) - - "service": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - -Example: - -{ "event": "VNC_CONNECTED", - "data": { - "server": { "auth": "sasl", "family": "ipv4", - "service": "5901", "host": "0.0.0.0" }, - "client": { "family": "ipv4", "service": "58425", - "host": "127.0.0.1" } }, - "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } - - -Note: This event is emitted before any authentication takes place, thus -the authentication ID is not provided. - -VNC_DISCONNECTED ----------------- - -Emitted when the connection is closed. - -Data: - -- "server": Server information (json-object) - - "host": IP address (json-string) - - "service": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - - "auth": authentication method (json-string, optional) -- "client": Client information (json-object) - - "host": IP address (json-string) - - "service": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - - "x509_dname": TLS dname (json-string, optional) - - "sasl_username": SASL username (json-string, optional) - -Example: - -{ "event": "VNC_DISCONNECTED", - "data": { - "server": { "auth": "sasl", "family": "ipv4", - "service": "5901", "host": "0.0.0.0" }, - "client": { "family": "ipv4", "service": "58425", - "host": "127.0.0.1", "sasl_username": "luiz" } }, - "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } - -VNC_INITIALIZED ---------------- - -Emitted after authentication takes place (if any) and the VNC session is -made active. - -Data: - -- "server": Server information (json-object) - - "host": IP address (json-string) - - "service": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - - "auth": authentication method (json-string, optional) -- "client": Client information (json-object) - - "host": IP address (json-string) - - "service": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - - "x509_dname": TLS dname (json-string, optional) - - "sasl_username": SASL username (json-string, optional) - -Example: - -{ "event": "VNC_INITIALIZED", - "data": { - "server": { "auth": "sasl", "family": "ipv4", - "service": "5901", "host": "0.0.0.0"}, - "client": { "family": "ipv4", "service": "46089", - "host": "127.0.0.1", "sasl_username": "luiz" } }, - "timestamp": { "seconds": 1263475302, "microseconds": 150772 } } - -VSERPORT_CHANGE ---------------- - -Emitted when the guest opens or closes a virtio-serial port. - -Data: - -- "id": device identifier of the virtio-serial port (json-string) -- "open": true if the guest has opened the virtio-serial port (json-bool) - -Example: - -{ "event": "VSERPORT_CHANGE", - "data": { "id": "channel0", "open": true }, - "timestamp": { "seconds": 1401385907, "microseconds": 422329 } } - -WAKEUP ------- - -Emitted when the guest has woken up from S3 and is running. - -Data: None. - -Example: - -{ "event": "WAKEUP", - "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } - -WATCHDOG --------- - -Emitted when the watchdog device's timer is expired. - -Data: - -- "action": Action that has been taken, it's one of the following (json-string): - "reset", "shutdown", "poweroff", "pause", "debug", or "none" - -Example: - -{ "event": "WATCHDOG", - "data": { "action": "reset" }, - "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } - -Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is -followed respectively by the RESET, SHUTDOWN, or STOP events. diff --git a/qemu/docs/qmp/qmp-spec.txt b/qemu/docs/qmp/qmp-spec.txt deleted file mode 100644 index 4c28cd943..000000000 --- a/qemu/docs/qmp/qmp-spec.txt +++ /dev/null @@ -1,336 +0,0 @@ - QEMU Machine Protocol Specification - -0. About This Document -====================== - -Copyright (C) 2009-2015 Red Hat, Inc. - -This work is licensed under the terms of the GNU GPL, version 2 or -later. See the COPYING file in the top-level directory. - -1. Introduction -=============== - -This document specifies the QEMU Machine Protocol (QMP), a JSON-based -protocol which is available for applications to operate QEMU at the -machine-level. It is also in use by the QEMU Guest Agent (QGA), which -is available for host applications to interact with the guest -operating system. - -2. Protocol Specification -========================= - -This section details the protocol format. For the purpose of this document -"Client" is any application which is using QMP to communicate with QEMU and -"Server" is QEMU itself. - -JSON data structures, when mentioned in this document, are always in the -following format: - - json-DATA-STRUCTURE-NAME - -Where DATA-STRUCTURE-NAME is any valid JSON data structure, as defined -by the JSON standard: - -http://www.ietf.org/rfc/rfc7159.txt - -The protocol is always encoded in UTF-8 except for synchronization -bytes (documented below); although thanks to json-string escape -sequences, the server will reply using only the strict ASCII subset. - -For convenience, json-object members mentioned in this document will -be in a certain order. However, in real protocol usage they can be in -ANY order, thus no particular order should be assumed. On the other -hand, use of json-array elements presumes that preserving order is -important unless specifically documented otherwise. Repeating a key -within a json-object gives unpredictable results. - -Also for convenience, the server will accept an extension of -'single-quoted' strings in place of the usual "double-quoted" -json-string, and both input forms of strings understand an additional -escape sequence of "\'" for a single quote. The server will only use -double quoting on output. - -2.1 General Definitions ------------------------ - -2.1.1 All interactions transmitted by the Server are json-objects, always - terminating with CRLF - -2.1.2 All json-objects members are mandatory when not specified otherwise - -2.2 Server Greeting -------------------- - -Right when connected the Server will issue a greeting message, which signals -that the connection has been successfully established and that the Server is -ready for capabilities negotiation (for more information refer to section -'4. Capabilities Negotiation'). - -The greeting message format is: - -{ "QMP": { "version": json-object, "capabilities": json-array } } - - Where, - -- The "version" member contains the Server's version information (the format - is the same of the query-version command) -- The "capabilities" member specify the availability of features beyond the - baseline specification; the order of elements in this array has no - particular significance, so a client must search the entire array - when looking for a particular capability - -2.2.1 Capabilities ------------------- - -As of the date this document was last revised, no server or client -capability strings have been defined. - - -2.3 Issuing Commands --------------------- - -The format for command execution is: - -{ "execute": json-string, "arguments": json-object, "id": json-value } - - Where, - -- The "execute" member identifies the command to be executed by the Server -- The "arguments" member is used to pass any arguments required for the - execution of the command, it is optional when no arguments are - 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 - -2.4 Commands Responses ----------------------- - -There are two possible responses which the Server will issue as the result -of a command execution: success or error. - -2.4.1 success -------------- - -The format of a success response is: - -{ "return": json-value, "id": json-value } - - Where, - -- The "return" member contains the data returned by the command, which - is defined on a per-command basis (usually a json-object or - json-array of json-objects, but sometimes a json-number, json-string, - or json-array of json-strings); it is an empty json-object if the - command does not return data -- The "id" member contains the transaction identification associated - with the command execution if issued by the Client - -2.4.2 error ------------ - -The format of an error response is: - -{ "error": { "class": json-string, "desc": json-string }, "id": json-value } - - Where, - -- The "class" member contains the error class name (eg. "GenericError") -- The "desc" member is a human-readable error message. Clients should - not attempt to parse this message. -- The "id" member contains the transaction identification associated with - the command execution if issued by the Client - -NOTE: Some errors can occur before the Server is able to read the "id" member, -in these cases the "id" member will not be part of the error response, even -if provided by the client. - -2.5 Asynchronous events ------------------------ - -As a result of state changes, the Server may send messages unilaterally -to the Client at any time, when not in the middle of any other -response. They are called "asynchronous events". - -The format of asynchronous events is: - -{ "event": json-string, "data": json-object, - "timestamp": { "seconds": json-number, "microseconds": json-number } } - - Where, - -- The "event" member contains the event's name -- The "data" member contains event specific data, which is defined in a - per-event basis, it is optional -- The "timestamp" member contains the exact time of when the event - occurred in the Server. It is a fixed json-object with time in - seconds and microseconds relative to the Unix Epoch (1 Jan 1970); if - there is a failure to retrieve host time, both members of the - timestamp will be set to -1. - -For a listing of supported asynchronous events, please, refer to the -qmp-events.txt file. - -2.5 QGA Synchronization ------------------------ - -When using QGA, an additional synchronization feature is built into -the protocol. If the Client sends a raw 0xFF sentinel byte (not valid -JSON), then the Server will reset its state and discard all pending -data prior to the sentinel. Conversely, if the Client makes use of -the 'guest-sync-delimited' command, the Server will send a raw 0xFF -sentinel byte prior to its response, to aid the Client in discarding -any data prior to the sentinel. - - -3. QMP Examples -=============== - -This section provides some examples of real QMP usage, in all of them -"C" stands for "Client" and "S" stands for "Server". - -3.1 Server greeting -------------------- - -S: { "QMP": { "version": { "qemu": { "micro": 50, "minor": 6, "major": 1 }, - "package": ""}, "capabilities": []}} - -3.2 Client QMP negotiation --------------------------- -C: { "execute": "qmp_capabilities" } -S: { "return": {}} - -3.3 Simple 'stop' execution ---------------------------- - -C: { "execute": "stop" } -S: { "return": {} } - -3.4 KVM information -------------------- - -C: { "execute": "query-kvm", "id": "example" } -S: { "return": { "enabled": true, "present": true }, "id": "example"} - -3.5 Parsing error ------------------- - -C: { "execute": } -S: { "error": { "class": "GenericError", "desc": "Invalid JSON syntax" } } - -3.6 Powerdown event -------------------- - -S: { "timestamp": { "seconds": 1258551470, "microseconds": 802384 }, - "event": "POWERDOWN" } - -4. Capabilities Negotiation -=========================== - -When a Client successfully establishes a connection, the Server is in -Capabilities Negotiation mode. - -In this mode only the qmp_capabilities command is allowed to run, all -other commands will return the CommandNotFound error. Asynchronous -messages are not delivered either. - -Clients should use the qmp_capabilities command to enable capabilities -advertised in the Server's greeting (section '2.2 Server Greeting') they -support. - -When the qmp_capabilities command is issued, and if it does not return an -error, the Server enters in Command mode where capabilities changes take -effect, all commands (except qmp_capabilities) are allowed and asynchronous -messages are delivered. - -5 Compatibility Considerations -============================== - -All protocol changes or new features which modify the protocol format in an -incompatible way are disabled by default and will be advertised by the -capabilities array (section '2.2 Server Greeting'). Thus, Clients can check -that array and enable the capabilities they support. - -The QMP Server performs a type check on the arguments to a command. It -generates an error if a value does not have the expected type for its -key, or if it does not understand a key that the Client included. The -strictness of the Server catches wrong assumptions of Clients about -the Server's schema. Clients can assume that, when such validation -errors occur, they will be reported before the command generated any -side effect. - -However, Clients must not assume any particular: - -- Length of json-arrays -- Size of json-objects; in particular, future versions of QEMU may add - new keys and Clients should be able to ignore them. -- Order of json-object members or json-array elements -- Amount of errors generated by a command, that is, new errors can be added - to any existing command in newer versions of the Server - -Any command or field name beginning with "x-" is deemed experimental, -and may be withdrawn or changed in an incompatible manner in a future -release. - -Of course, the Server does guarantee to send valid JSON. But apart from -this, a Client should be "conservative in what they send, and liberal in -what they accept". - -6. Downstream extension of QMP -============================== - -We recommend that downstream consumers of QEMU do *not* modify QMP. -Management tools should be able to support both upstream and downstream -versions of QMP without special logic, and downstream extensions are -inherently at odds with that. - -However, we recognize that it is sometimes impossible for downstreams to -avoid modifying QMP. Both upstream and downstream need to take care to -preserve long-term compatibility and interoperability. - -To help with that, QMP reserves JSON object member names beginning with -'__' (double underscore) for downstream use ("downstream names"). This -means upstream will never use any downstream names for its commands, -arguments, errors, asynchronous events, and so forth. - -Any new names downstream wishes to add must begin with '__'. To -ensure compatibility with other downstreams, it is strongly -recommended that you prefix your downstream names with '__RFQDN_' where -RFQDN is a valid, reverse fully qualified domain name which you -control. For example, a qemu-kvm specific monitor command would be: - - (qemu) __org.linux-kvm_enable_irqchip - -Downstream must not change the server greeting (section 2.2) other than -to offer additional capabilities. But see below for why even that is -discouraged. - -Section '5 Compatibility Considerations' applies to downstream as well -as to upstream, obviously. It follows that downstream must behave -exactly like upstream for any input not containing members with -downstream names ("downstream members"), except it may add members -with downstream names to its output. - -Thus, a client should not be able to distinguish downstream from -upstream as long as it doesn't send input with downstream members, and -properly ignores any downstream members in the output it receives. - -Advice on downstream modifications: - -1. Introducing new commands is okay. If you want to extend an existing - command, consider introducing a new one with the new behaviour - instead. - -2. Introducing new asynchronous messages is okay. If you want to extend - an existing message, consider adding a new one instead. - -3. Introducing new errors for use in new commands is okay. Adding new - errors to existing commands counts as extension, so 1. applies. - -4. New capabilities are strongly discouraged. Capabilities are for - evolving the basic protocol, and multiple diverging basic protocol - dialects are most undesirable. diff --git a/qemu/docs/rcu.txt b/qemu/docs/rcu.txt index 21ecb8106..2f70954e8 100644 --- a/qemu/docs/rcu.txt +++ b/qemu/docs/rcu.txt @@ -128,7 +128,7 @@ The core RCU API is small: the callback function is g_free, in particular, g_free_rcu can be used. In the above case, one could have written simply: - g_free_rcu(foo_reclaim, rcu); + g_free_rcu(&foo, rcu); typeof(*p) atomic_rcu_read(p); diff --git a/qemu/docs/replay.txt b/qemu/docs/replay.txt new file mode 100644 index 000000000..779c6c059 --- /dev/null +++ b/qemu/docs/replay.txt @@ -0,0 +1,197 @@ +Copyright (c) 2010-2015 Institute for System Programming + of the Russian Academy of Sciences. + +This work is licensed under the terms of the GNU GPL, version 2 or later. +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. + +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. +Execution replaying reads the log and replays all non-deterministic events +including external input, hardware clocks, and interrupts. + +Deterministic replay has the following features: + * Deterministically replays whole system execution and all contents of + the memory, state of the hardware devices, clocks, and screen of the VM. + * Writes execution log into the file for later replaying for multiple times + on different machines. + * Supports i386, x86_64, and ARM hardware platforms. + * Performs deterministic replay of all operations with keyboard and mouse + 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, + 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: +http://www.computer.org/csdl/proceedings/csmr/2012/4666/00/4666a553-abs.html +http://dl.acm.org/citation.cfm?id=2786805.2803179 + +Modifications of qemu include: + * wrappers for clock and time functions to save their return values in the log + * 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) + * adding internal checkpoints for cpu and io synchronization + +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. + +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 +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 +can be replayed by simulating the behavior of virtual machine starting from +initial state. + +We had to solve three tasks to implement deterministic replay: recording +non-deterministic events, replaying non-deterministic events, and checking +that there is no divergence between record and replay modes. + +We changed several parts of QEMU to make event log recording and replaying. +Devices' models that have non-deterministic input from external devices were +changed to write every external event into the execution log immediately. +E.g. network packets are written into the log when they arrive into the virtual +network adapter. + +All non-deterministic events are coming from these devices. But to +replay them we need to know at which moments they occur. We specify +these moments by counting the number of instructions executed between +every pair of consecutive events. + +Instruction counting +-------------------- + +QEMU should work in icount mode to use record/replay feature. icount was +designed to allow deterministic execution in absence of external inputs +of the virtual machine. We also use icount to control the occurrence of the +non-deterministic events. The number of instructions elapsed from the last event +is written to the log while recording the execution. In replay mode we +can predict when to inject that event using the instruction counter. + +Timers +------ + +Timers are used to execute callbacks from different subsystems of QEMU +at the specified moments of time. There are several kinds of timers: + * Real time clock. Based on host time and used only for callbacks that + do not change the virtual machine state. For this reason real time + clock and timers does not affect deterministic replay at all. + * Virtual clock. These timers run only during the emulation. In icount + mode virtual clock value is calculated using executed instructions counter. + That is why it is completely deterministic and does not have to be recorded. + * Host clock. This clock is used by device models that simulate real time + sources (e.g. real time clock chip). Host clock is the one of the sources + of non-determinism. Host clock read operations should be logged to + make the execution deterministic. + * Virtual real time clock. This clock is similar to real time clock but + it is used only for increasing virtual clock while virtual machine is + sleeping. Due to its nature it is also non-deterministic as the host clock + and has to be logged too. + +Checkpoints +----------- + +Replaying of the execution of virtual machine is bound by sources of +non-determinism. These are inputs from clock and peripheral devices, +and QEMU thread scheduling. Thread scheduling affect on processing events +from timers, asynchronous input-output, and bottom halves. + +Invocations of timers are coupled with clock reads and changing the state +of the virtual machine. Reads produce non-deterministic data taken from +host clock. And VM state changes should preserve their order. Their relative +order in replay mode must replicate the order of callbacks in record mode. +To preserve this order we use checkpoints. When a specific clock is processed +in record mode we save to the log special "checkpoint" event. +Checkpoints here do not refer to virtual machine snapshots. They are just +record/replay events used for synchronization. + +QEMU in replay mode will try to invoke timers processing in random moment +of time. That's why we do not process a group of timers until the checkpoint +event will be read from the log. Such an event allows synchronizing CPU +execution and timer events. + +Two other checkpoints govern the "warping" of the virtual clock. +While the virtual machine is idle, the virtual clock increments at +1 ns per *real time* nanosecond. This is done by setting up a timer +(called the warp timer) on the virtual real time clock, so that the +timer fires at the next deadline of the virtual clock; the virtual clock +is then incremented (which is called "warping" the virtual clock) as +soon as the timer fires or the CPUs need to go out of the idle state. +Two functions are used for this purpose; because these actions change +virtual machine state and must be deterministic, each of them creates a +checkpoint. qemu_start_warp_timer checks if the CPUs are idle and if so +starts accounting real time to virtual clock. qemu_account_warp_timer +is called when the CPUs get an interrupt or when the warp timer fires, +and it warps the virtual clock by the amount of real time that has passed +since qemu_start_warp_timer. + +Bottom halves +------------- + +Disk I/O events are completely deterministic in our model, because +in both record and replay modes we start virtual machine from the same +disk state. But callbacks that virtual disk controller uses for reading and +writing the disk may occur at different moments of time in record and replay +modes. + +Reading and writing requests are created by CPU thread of QEMU. Later these +requests proceed to block layer which creates "bottom halves". Bottom +halves consist of callback and its parameters. They are processed when +main loop locks the global mutex. These locks are not synchronized with +replaying process because main loop also processes the events that do not +affect the virtual machine state (like user interaction with monitor). + +That is why we had to implement saving and replaying bottom halves callbacks +synchronously to the CPU execution. When the callback is about to execute +it is added to the queue in the replay module. This queue is written to the +log when its callbacks are executed. In replay mode callbacks are not processed +until the corresponding event is read from the events log file. + +Sometimes the block layer uses asynchronous callbacks for its internal purposes +(like reading or writing VM snapshots or disk image cluster tables). In this +case bottom halves are not marked as "replayable" and do not saved +into the log. + +Block devices +------------- + +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 driver=blkreplay,if=none,image=img-direct,id=img-blkreplay + -device ide-hd,drive=img-blkreplay + +blkreplay driver should be inserted between disk image and virtual driver +controller. Therefore all disk requests may be recorded and replayed. + +All block completion operations are added to the queue in the coroutines. +Queue is flushed at checkpoints and information about processed requests +is recorded to the log. In replay phase the queue is matched with +events read from the log. Therefore block devices requests are processed +deterministically. diff --git a/qemu/docs/specs/fw_cfg.txt b/qemu/docs/specs/fw_cfg.txt index 74351dd18..7a5f8c782 100644 --- a/qemu/docs/specs/fw_cfg.txt +++ b/qemu/docs/specs/fw_cfg.txt @@ -76,6 +76,22 @@ increasing address order, similar to memcpy(). Selector Register IOport: 0x510 Data Register IOport: 0x511 +DMA Address IOport: 0x514 + +=== ARM Register Locations === + +Selector Register address: Base + 8 (2 bytes) +Data Register address: Base + 0 (8 bytes) +DMA Address address: Base + 16 (8 bytes) + +== ACPI Interface == + +The fw_cfg device is defined with ACPI ID "QEMU0002". Since we expect +ACPI tables to be passed into the guest through the fw_cfg device itself, +the guest-side firmware can not use ACPI to find fw_cfg. However, once the +firmware is finished setting up ACPI tables and hands control over to the +guest kernel, the latter can use the fw_cfg ACPI node for a more accurate +inventory of in-use IOport or MMIO regions. == Firmware Configuration Items == @@ -86,11 +102,15 @@ by selecting the "signature" item using key 0x0000 (FW_CFG_SIGNATURE), and reading four bytes from the data register. If the fw_cfg device is present, the four bytes read will contain the characters "QEMU". -=== Revision (Key 0x0001, FW_CFG_ID) === +If the DMA interface is available, then reading the DMA Address +Register returns 0x51454d5520434647 ("QEMU CFG" in big-endian format). + +=== Revision / feature bitmap (Key 0x0001, FW_CFG_ID) === -A 32-bit little-endian unsigned int, this item is used as an interface -revision number, and is currently set to 1 by QEMU when fw_cfg is -initialized. +A 32-bit little-endian unsigned int, this item is used to check for enabled +features. + - Bit 0: traditional interface. Always set. + - Bit 1: DMA interface. === File Directory (Key 0x0019, FW_CFG_FILE_DIR) === @@ -132,79 +152,56 @@ Selector Reg. Range Usage In practice, the number of allowed firmware configuration items is given by the value of FW_CFG_MAX_ENTRY (see fw_cfg.h). -= Host-side API = - -The following functions are available to the QEMU programmer for adding -data to a fw_cfg device during guest initialization (see fw_cfg.h for -each function's complete prototype): - -== fw_cfg_add_bytes() == - -Given a selector key value, starting pointer, and size, create an item -as a raw "blob" of the given size, available by selecting the given key. -The data referenced by the starting pointer is only linked, NOT copied, -into the data structure of the fw_cfg device. - -== fw_cfg_add_string() == += Guest-side DMA Interface = -Instead of a starting pointer and size, this function accepts a pointer -to a NUL-terminated ascii string, and inserts a newly allocated copy of -the string (including the NUL terminator) into the fw_cfg device data -structure. +If bit 1 of the feature bitmap is set, the DMA interface is present. This does +not replace the existing fw_cfg interface, it is an add-on. This interface +can be used through the 64-bit wide address register. -== fw_cfg_add_iXX() == +The address register is in big-endian format. The value for the register is 0 +at startup and after an operation. A write to the least significant half (at +offset 4) triggers an operation. This means that operations with 32-bit +addresses can be triggered with just one write, whereas operations with +64-bit addresses can be triggered with one 64-bit write or two 32-bit writes, +starting with the most significant half (at offset 0). -Insert an XX-bit item, where XX may be 16, 32, or 64. These functions -will convert a 16-, 32-, or 64-bit integer to little-endian, then add -a dynamically allocated copy of the appropriately sized item to fw_cfg -under the given selector key value. +In this register, the physical address of a FWCfgDmaAccess structure in RAM +should be written. This is the format of the FWCfgDmaAccess structure: -== fw_cfg_add_file() == +typedef struct FWCfgDmaAccess { + uint32_t control; + uint32_t length; + uint64_t address; +} FWCfgDmaAccess; -Given a filename (i.e., fw_cfg item name), starting pointer, and size, -create an item as a raw "blob" of the given size. Unlike fw_cfg_add_bytes() -above, the next available selector key (above 0x0020, FW_CFG_FILE_FIRST) -will be used, and a new entry will be added to the file directory structure -(at key 0x0019), containing the item name, blob size, and automatically -assigned selector key value. The data referenced by the starting pointer -is only linked, NOT copied, into the fw_cfg data structure. +The fields of the structure are in big endian mode, and the field at the lowest +address is the "control" field. -== fw_cfg_add_file_callback() == +The "control" field has the following bits: + - Bit 0: Error + - Bit 1: Read + - Bit 2: Skip + - Bit 3: Select. The upper 16 bits are the selected index. -Like fw_cfg_add_file(), but additionally sets pointers to a callback -function (and opaque argument), which will be executed host-side by -QEMU each time a byte is read by the guest from this particular item. +When an operation is triggered, if the "control" field has bit 3 set, the +upper 16 bits are interpreted as an index of a firmware configuration item. +This has the same effect as writing the selector register. -NOTE: The callback function is given the opaque argument set by -fw_cfg_add_file_callback(), but also the current data offset, -allowing it the option of only acting upon specific offset values -(e.g., 0, before the first data byte of the selected item is -returned to the guest). +If the "control" field has bit 1 set, a read operation will be performed. +"length" bytes for the current selector and offset will be copied into the +physical RAM address specified by the "address" field. -== fw_cfg_modify_file() == +If the "control" field has bit 2 set (and not bit 1), a skip operation will be +performed. The offset for the current selector will be advanced "length" bytes. -Given a filename (i.e., fw_cfg item name), starting pointer, and size, -completely replace the configuration item referenced by the given item -name with the new given blob. If an existing blob is found, its -callback information is removed, and a pointer to the old data is -returned to allow the caller to free it, helping avoid memory leaks. -If a configuration item does not already exist under the given item -name, a new item will be created as with fw_cfg_add_file(), and NULL -is returned to the caller. In any case, the data referenced by the -starting pointer is only linked, NOT copied, into the fw_cfg data -structure. +To check the result, read the "control" field: + error bit set -> something went wrong. + all bits cleared -> transfer finished successfully. + otherwise -> transfer still in progress (doesn't happen + today due to implementation not being async, + but may in the future). -== fw_cfg_add_callback() == - -Like fw_cfg_add_bytes(), but additionally sets pointers to a callback -function (and opaque argument), which will be executed host-side by -QEMU each time a guest-side write operation to this particular item -completes fully overwriting the item's data. - -NOTE: This function is deprecated, and will be completely removed -starting with QEMU v2.4. - -== Externally Provided Items == += Externally Provided Items = As of v2.4, "file" fw_cfg items (i.e., items with selector keys above FW_CFG_FILE_FIRST, and with a corresponding entry in the fw_cfg file @@ -213,14 +210,27 @@ the following syntax: -fw_cfg [name=],file= -where is the fw_cfg item name, and is the location -on the host file system of a file containing the data to be inserted. +Or + + -fw_cfg [name=],string= + +See QEMU man page for more documentation. + +Using item_name with plain ASCII characters only is recommended. + +Item names beginning with "opt/" are reserved for users. QEMU will +never create entries with such names unless explicitly ordered by the +user. + +To avoid clashes among different users, it is strongly recommended +that you use names beginning with opt/RFQDN/, where RFQDN is a reverse +fully qualified domain name you control. For instance, if SeaBIOS +wanted to define additional names, the prefix "opt/org.seabios/" would +be appropriate. -NOTE: Users *SHOULD* choose item names beginning with the prefix "opt/" -when using the "-fw_cfg" command line option, to avoid conflicting with -item names used internally by QEMU. For instance: +For historical reasons, "opt/ovmf/" is reserved for OVMF firmware. - -fw_cfg name=opt/my_item_name,file=./my_blob.bin +Prefix "opt/org.qemu/" is reserved for QEMU itself. -Similarly, QEMU developers *SHOULD NOT* use item names prefixed with -"opt/" when inserting items programmatically, e.g. via fw_cfg_add_file(). +Use of names not beginning with "opt/" is potentially dangerous and +entirely unsupported. QEMU will warn if you try. diff --git a/qemu/docs/specs/ivshmem-spec.txt b/qemu/docs/specs/ivshmem-spec.txt new file mode 100644 index 000000000..a1f549979 --- /dev/null +++ b/qemu/docs/specs/ivshmem-spec.txt @@ -0,0 +1,254 @@ += Device Specification for Inter-VM shared memory device = + +The Inter-VM shared memory device (ivshmem) is designed to share a +memory region between multiple QEMU processes running different guests +and the host. In order for all guests to be able to pick up the +shared memory area, it is modeled by QEMU as a PCI device exposing +said memory to the guest as a PCI BAR. + +The device can use a shared memory object on the host directly, or it +can obtain one from an ivshmem server. + +In the latter case, the device can additionally interrupt its peers, and +get interrupted by its peers. + + +== Configuring the ivshmem PCI device == + +There are two basic configurations: + +- Just shared memory: -device ivshmem-plain,memdev=HMB,... + + This uses host memory backend HMB. It should have option "share" + set. + +- Shared memory plus interrupts: -device ivshmem,chardev=CHR,vectors=N,... + + An ivshmem server must already be running on the host. The device + connects to the server's UNIX domain socket via character device + CHR. + + Each peer gets assigned a unique ID by the server. IDs must be + between 0 and 65535. + + Interrupts are message-signaled (MSI-X). vectors=N configures the + number of vectors to use. + +For more details on ivshmem device properties, see The QEMU Emulator +User Documentation (qemu-doc.*). + + +== The ivshmem PCI device's guest interface == + +The device has vendor ID 1af4, device ID 1110, revision 1. Before +QEMU 2.6.0, it had revision 0. + +=== PCI BARs === + +The ivshmem PCI device has two or three BARs: + +- BAR0 holds device registers (256 Byte MMIO) +- BAR1 holds MSI-X table and PBA (only ivshmem-doorbell) +- BAR2 maps the shared memory object + +There are two ways to use this device: + +- If you only need the shared memory part, BAR2 suffices. This way, + you have access to the shared memory in the guest and can use it as + you see fit. Memnic, for example, uses ivshmem this way from guest + user space (see http://dpdk.org/browse/memnic). + +- If you additionally need the capability for peers to interrupt each + other, you need BAR0 and BAR1. You will most likely want to write a + kernel driver to handle interrupts. Requires the device to be + configured for interrupts, obviously. + +Before QEMU 2.6.0, BAR2 can initially be invalid if the device is +configured for interrupts. It becomes safely accessible only after +the ivshmem server provided the shared memory. These devices have PCI +revision 0 rather than 1. Guest software should wait for the +IVPosition register (described below) to become non-negative before +accessing BAR2. + +Revision 0 of the device is not capable to tell guest software whether +it is configured for interrupts. + +=== PCI device registers === + +BAR 0 contains the following registers: + + Offset Size Access On reset Function + 0 4 read/write 0 Interrupt Mask + bit 0: peer interrupt (rev 0) + reserved (rev 1) + bit 1..31: reserved + 4 4 read/write 0 Interrupt Status + bit 0: peer interrupt (rev 0) + reserved (rev 1) + bit 1..31: reserved + 8 4 read-only 0 or ID IVPosition + 12 4 write-only N/A Doorbell + bit 0..15: vector + bit 16..31: peer ID + 16 240 none N/A reserved + +Software should only access the registers as specified in column +"Access". Reserved bits should be ignored on read, and preserved on +write. + +In revision 0 of the device, Interrupt Status and Mask Register +together control the legacy INTx interrupt when the device has no +MSI-X capability: INTx is asserted when the bit-wise AND of Status and +Mask is non-zero and the device has no MSI-X capability. Interrupt +Status Register bit 0 becomes 1 when an interrupt request from a peer +is received. Reading the register clears it. + +IVPosition Register: if the device is not configured for interrupts, +this is zero. Else, it is the device's ID (between 0 and 65535). + +Before QEMU 2.6.0, the register may read -1 for a short while after +reset. These devices have PCI revision 0 rather than 1. + +There is no good way for software to find out whether the device is +configured for interrupts. A positive IVPosition means interrupts, +but zero could be either. + +Doorbell Register: writing this register requests to interrupt a peer. +The written value's high 16 bits are the ID of the peer to interrupt, +and its low 16 bits select an interrupt vector. + +If the device is not configured for interrupts, the write is ignored. + +If the interrupt hasn't completed setup, the write is ignored. The +device is not capable to tell guest software whether setup is +complete. Interrupts can regress to this state on migration. + +If the peer with the requested ID isn't connected, or it has fewer +interrupt vectors connected, the write is ignored. The device is not +capable to tell guest software what peers are connected, or how many +interrupt vectors are connected. + +The peer's interrupt for this vector then becomes pending. There is +no way for software to clear the pending bit, and a polling mode of +operation is therefore impossible. + +If the peer is a revision 0 device without MSI-X capability, its +Interrupt Status register is set to 1. This asserts INTx unless +masked by the Interrupt Mask register. The device is not capable to +communicate the interrupt vector to guest software then. + +With multiple MSI-X vectors, different vectors can be used to indicate +different events have occurred. The semantics of interrupt vectors +are left to the application. + + +== Interrupt infrastructure == + +When configured for interrupts, the peers share eventfd objects in +addition to shared memory. The shared resources are managed by an +ivshmem server. + +=== The ivshmem server === + +The server listens on a UNIX domain socket. + +For each new client that connects to the server, the server +- picks an ID, +- creates eventfd file descriptors for the interrupt vectors, +- sends the ID and the file descriptor for the shared memory to the + new client, +- sends connect notifications for the new client to the other clients + (these contain file descriptors for sending interrupts), +- sends connect notifications for the other clients to the new client, + and +- sends interrupt setup messages to the new client (these contain file + descriptors for receiving interrupts). + +The first client to connect to the server receives ID zero. + +When a client disconnects from the server, the server sends disconnect +notifications to the other clients. + +The next section describes the protocol in detail. + +If the server terminates without sending disconnect notifications for +its connected clients, the clients can elect to continue. They can +communicate with each other normally, but won't receive disconnect +notification on disconnect, and no new clients can connect. There is +no way for the clients to connect to a restarted server. The device +is not capable to tell guest software whether the server is still up. + +Example server code is in contrib/ivshmem-server/. Not to be used in +production. It assumes all clients use the same number of interrupt +vectors. + +A standalone client is in contrib/ivshmem-client/. It can be useful +for debugging. + +=== The ivshmem Client-Server Protocol === + +An ivshmem device configured for interrupts connects to an ivshmem +server. This section details the protocol between the two. + +The connection is one-way: the server sends messages to the client. +Each message consists of a single 8 byte little-endian signed number, +and may be accompanied by a file descriptor via SCM_RIGHTS. Both +client and server close the connection on error. + +Note: QEMU currently doesn't close the connection right on error, but +only when the character device is destroyed. + +On connect, the server sends the following messages in order: + +1. The protocol version number, currently zero. The client should + close the connection on receipt of versions it can't handle. + +2. The client's ID. This is unique among all clients of this server. + IDs must be between 0 and 65535, because the Doorbell register + provides only 16 bits for them. + +3. The number -1, accompanied by the file descriptor for the shared + memory. + +4. Connect notifications for existing other clients, if any. This is + a peer ID (number between 0 and 65535 other than the client's ID), + repeated N times. Each repetition is accompanied by one file + descriptor. These are for interrupting the peer with that ID using + vector 0,..,N-1, in order. If the client is configured for fewer + vectors, it closes the extra file descriptors. If it is configured + for more, the extra vectors remain unconnected. + +5. Interrupt setup. This is the client's own ID, repeated N times. + Each repetition is accompanied by one file descriptor. These are + for receiving interrupts from peers using vector 0,..,N-1, in + order. If the client is configured for fewer vectors, it closes + the extra file descriptors. If it is configured for more, the + extra vectors remain unconnected. + +From then on, the server sends these kinds of messages: + +6. Connection / disconnection notification. This is a peer ID. + + - If the number comes with a file descriptor, it's a connection + notification, exactly like in step 4. + + - Else, it's a disconnection notification for the peer with that ID. + +Known bugs: + +* The protocol changed incompatibly in QEMU 2.5. Before, messages + were native endian long, and there was no version number. + +* The protocol is poorly designed. + +=== The ivshmem Client-Client Protocol === + +An ivshmem device configured for interrupts receives eventfd file +descriptors for interrupting peers and getting interrupted by peers +from the server, as explained in the previous section. + +To interrupt a peer, the device writes the 8-byte integer 1 in native +byte order to the respective file descriptor. + +To receive an interrupt, the device reads and discards as many 8-byte +integers as it can. diff --git a/qemu/docs/specs/ivshmem_device_spec.txt b/qemu/docs/specs/ivshmem_device_spec.txt deleted file mode 100644 index 667a8628f..000000000 --- a/qemu/docs/specs/ivshmem_device_spec.txt +++ /dev/null @@ -1,96 +0,0 @@ - -Device Specification for Inter-VM shared memory device ------------------------------------------------------- - -The Inter-VM shared memory device is designed to share a region of memory to -userspace in multiple virtual guests. The memory region does not belong to any -guest, but is a POSIX memory object on the host. Optionally, the device may -support sending interrupts to other guests sharing the same memory region. - - -The Inter-VM PCI device ------------------------ - -*BARs* - -The device supports three BARs. BAR0 is a 1 Kbyte MMIO region to support -registers. BAR1 is used for MSI-X when it is enabled in the device. BAR2 is -used to map the shared memory object from the host. The size of BAR2 is -specified when the guest is started and must be a power of 2 in size. - -*Registers* - -The device currently supports 4 registers of 32-bits each. Registers -are used for synchronization between guests sharing the same memory object when -interrupts are supported (this requires using the shared memory server). - -The server assigns each VM an ID number and sends this ID number to the QEMU -process when the guest starts. - -enum ivshmem_registers { - IntrMask = 0, - IntrStatus = 4, - IVPosition = 8, - Doorbell = 12 -}; - -The first two registers are the interrupt mask and status registers. Mask and -status are only used with pin-based interrupts. They are unused with MSI -interrupts. - -Status Register: The status register is set to 1 when an interrupt occurs. - -Mask Register: The mask register is bitwise ANDed with the interrupt status -and the result will raise an interrupt if it is non-zero. However, since 1 is -the only value the status will be set to, it is only the first bit of the mask -that has any effect. Therefore interrupts can be masked by setting the first -bit to 0 and unmasked by setting the first bit to 1. - -IVPosition Register: The IVPosition register is read-only and reports the -guest's ID number. The guest IDs are non-negative integers. When using the -server, since the server is a separate process, the VM ID will only be set when -the device is ready (shared memory is received from the server and accessible via -the device). If the device is not ready, the IVPosition will return -1. -Applications should ensure that they have a valid VM ID before accessing the -shared memory. - -Doorbell Register: To interrupt another guest, a guest must write to the -Doorbell register. The doorbell register is 32-bits, logically divided into -two 16-bit fields. The high 16-bits are the guest ID to interrupt and the low -16-bits are the interrupt vector to trigger. The semantics of the value -written to the doorbell depends on whether the device is using MSI or a regular -pin-based interrupt. In short, MSI uses vectors while regular interrupts set the -status register. - -Regular Interrupts - -If regular interrupts are used (due to either a guest not supporting MSI or the -user specifying not to use them on startup) then the value written to the lower -16-bits of the Doorbell register results is arbitrary and will trigger an -interrupt in the destination guest. - -Message Signalled Interrupts - -A ivshmem device may support multiple MSI vectors. If so, the lower 16-bits -written to the Doorbell register must be between 0 and the maximum number of -vectors the guest supports. The lower 16 bits written to the doorbell is the -MSI vector that will be raised in the destination guest. The number of MSI -vectors is configurable but it is set when the VM is started. - -The important thing to remember with MSI is that it is only a signal, no status -is set (since MSI interrupts are not shared). All information other than the -interrupt itself should be communicated via the shared memory region. Devices -supporting multiple MSI vectors can use different vectors to indicate different -events have occurred. The semantics of interrupt vectors are left to the -user's discretion. - - -Usage in the Guest ------------------- - -The shared memory device is intended to be used with the provided UIO driver. -Very little configuration is needed. The guest should map BAR0 to access the -registers (an array of 32-bit ints allows simple writing) and map BAR2 to -access the shared memory region itself. The size of the shared memory region -is specified when the guest (or shared memory server) is started. A guest may -map the whole shared memory region or only part of it. diff --git a/qemu/docs/specs/parallels.txt b/qemu/docs/specs/parallels.txt new file mode 100644 index 000000000..b4fe2295f --- /dev/null +++ b/qemu/docs/specs/parallels.txt @@ -0,0 +1,228 @@ += License = + +Copyright (c) 2015 Denis Lunev +Copyright (c) 2015 Vladimir Sementsov-Ogievskiy + +This work is licensed under the terms of the GNU GPL, version 2 or later. +See the COPYING file in the top-level directory. + += Parallels Expandable Image File Format = + +A Parallels expandable image file consists of three consecutive parts: + * header + * BAT + * data area + +All numbers in a Parallels expandable image are stored in little-endian byte +order. + + +== Definitions == + + Sector A 512-byte data chunk. + + Cluster A data chunk of the size specified in the image header. + Currently, the default size is 1MiB (2048 sectors). In previous + versions, cluster sizes of 63 sectors, 256 and 252 kilobytes were + used. + + BAT Block Allocation Table, an entity that contains information for + guest-to-host I/O data address translation. + + +== Header == + +The header is placed at the start of an image and contains the following +fields: + +Bytes: + 0 - 15: magic + Must contain "WithoutFreeSpace" or "WithouFreSpacExt". + + 16 - 19: version + Must be 2. + + 20 - 23: heads + Disk geometry parameter for guest. + + 24 - 27: cylinders + Disk geometry parameter for guest. + + 28 - 31: tracks + Cluster size, in sectors. + + 32 - 35: nb_bat_entries + Disk size, in clusters (BAT size). + + 36 - 43: nb_sectors + Disk size, in sectors. + + For "WithoutFreeSpace" images: + Only the lowest 4 bytes are used. The highest 4 bytes must be + cleared in this case. + + For "WithouFreSpacExt" images, there are no such + restrictions. + + 44 - 47: in_use + Set to 0x746F6E59 when the image is opened by software in R/W + mode; set to 0x312e3276 when the image is closed. + + A zero in this field means that the image was opened by an old + version of the software that doesn't support Format Extension + (see below). + + Other values are not allowed. + + 48 - 51: data_off + An offset, in sectors, from the start of the file to the start of + the data area. + + For "WithoutFreeSpace" images: + - If data_off is zero, the offset is calculated as the end of BAT + table plus some padding to ensure sector size alignment. + - If data_off is non-zero, the offset should be aligned to sector + size. However it is recommended to align it to cluster size for + newly created images. + + For "WithouFreSpacExt" images: + data_off must be non-zero and aligned to cluster size. + + 52 - 55: flags + Miscellaneous flags. + + Bit 0: Empty Image bit. If set, the image should be + considered clear. + + Bits 2-31: Unused. + + 56 - 63: ext_off + Format Extension offset, an offset, in sectors, from the start of + the file to the start of the Format Extension Cluster. + + ext_off must meet the same requirements as cluster offsets + defined by BAT entries (see below). + + +== BAT == + +BAT is placed immediately after the image header. In the file, BAT is a +contiguous array of 32-bit unsigned little-endian integers with +(bat_entries * 4) bytes size. + +Each BAT entry contains an offset from the start of the file to the +corresponding cluster. The offset set in clusters for "WithouFreSpacExt" images +and in sectors for "WithoutFreeSpace" images. + +If a BAT entry is zero, the corresponding cluster is not allocated and should +be considered as filled with zeroes. + +Cluster offsets specified by BAT entries must meet the following requirements: + - the value must not be lower than data offset (provided by header.data_off + or calculated as specified above), + - the value must be lower than the desired file size, + - the value must be unique among all BAT entries, + - the result of (cluster offset - data offset) must be aligned to cluster + size. + + +== Data Area == + +The data area is an area from the data offset (provided by header.data_off or +calculated as specified above) to the end of the file. It represents a +contiguous array of clusters. Most of them are allocated by the BAT, some may +be allocated by the ext_off field in the header while other may be allocated by +extensions. All clusters allocated by ext_off and extensions should meet the +same requirements as clusters specified by BAT entries. + + +== Format Extension == + +The Format Extension is an area 1 cluster in size that provides additional +format features. This cluster is addressed by the ext_off field in the header. +The format of the Format Extension area is the following: + + 0 - 7: magic + Must be 0xAB234CEF23DCEA87 + + 8 - 23: m_CheckSum + The MD5 checksum of the entire Header Extension cluster except + the first 24 bytes. + + The above are followed by feature sections or "extensions". The last + extension must be "End of features" (see below). + +Each feature section has the following format: + + 0 - 7: magic + The identifier of the feature: + 0x0000000000000000 - End of features + 0x20385FAE252CB34A - Dirty bitmap + + 8 - 15: flags + External flags for extension: + + Bit 0: NECESSARY + If the software cannot load the extension (due to an + unknown magic number or error), the file should not be + changed. If this flag is unset and there is an error on + loading the extension, said extension should be dropped. + + Bit 1: TRANSIT + If there is an unknown extension with this flag set, + said extension should be left as is. + + If neither NECESSARY nor TRANSIT are set, the extension should be + dropped. + + 16 - 19: data_size + The size of the following feature data, in bytes. + + 20 - 23: unused32 + Align header to 8 bytes boundary. + + variable: data (data_size bytes) + + The above is followed by padding to the next 8 bytes boundary, then the + next extension starts. + + The last extension must be "End of features" with all the fields set to 0. + + +=== Dirty bitmaps feature === + +This feature provides a way of storing dirty bitmaps in the image. The fields +of its data area are: + + 0 - 7: size + The bitmap size, should be equal to disk size in sectors. + + 8 - 23: id + An identifier for backup consistency checking. + + 24 - 27: granularity + Bitmap granularity, in sectors. I.e., the number of sectors + corresponding to one bit of the bitmap. Granularity must be + a power of 2. + + 28 - 31: l1_size + The number of entries in the L1 table of the bitmap. + + variable: l1 (64 * l1_size bytes) + L1 offset table (in bytes) + +A dirty bitmap is stored using a one-level structure for the mapping to host +clusters - an L1 table. + +Given an offset in bytes into the bitmap data, the offset in bytes into the +image file can be obtained as follows: + + offset = l1_table[offset / cluster_size] + (offset % cluster_size) + +If an L1 table entry is 0, the corresponding cluster of the bitmap is assumed +to be zero. + +If an L1 table entry is 1, the corresponding cluster of the bitmap is assumed +to have all bits set. + +If an L1 table entry is not 0 or 1, it allocates a cluster from the data area. diff --git a/qemu/docs/specs/pci-ids.txt b/qemu/docs/specs/pci-ids.txt index 0adcb89aa..fd27c677d 100644 --- a/qemu/docs/specs/pci-ids.txt +++ b/qemu/docs/specs/pci-ids.txt @@ -15,13 +15,23 @@ The 1000 -> 10ff device ID range is used as follows for virtio-pci devices. Note that this allocation separate from the virtio device IDs, which are maintained as part of the virtio specification. -1af4:1000 network device -1af4:1001 block device -1af4:1002 balloon device -1af4:1003 console device -1af4:1004 SCSI host bus adapter device -1af4:1005 entropy generator device -1af4:1009 9p filesystem device +1af4:1000 network device (legacy) +1af4:1001 block device (legacy) +1af4:1002 balloon device (legacy) +1af4:1003 console device (legacy) +1af4:1004 SCSI host bus adapter device (legacy) +1af4:1005 entropy generator device (legacy) +1af4:1009 9p filesystem device (legacy) + +1af4:1041 network device (modern) +1af4:1042 block device (modern) +1af4:1043 console device (modern) +1af4:1044 entropy generator device (modern) +1af4:1045 balloon device (modern) +1af4:1048 SCSI host bus adapter device (modern) +1af4:1049 9p filesystem device (modern) +1af4:1050 virtio gpu device (modern) +1af4:1052 virtio input device (modern) 1af4:10f0 Available for experimental usage without registration. Must get to official ID when the code leaves the test lab (i.e. when seeking diff --git a/qemu/docs/specs/ppc-spapr-hcalls.txt b/qemu/docs/specs/ppc-spapr-hcalls.txt index 667b3fa00..5bd8eab78 100644 --- a/qemu/docs/specs/ppc-spapr-hcalls.txt +++ b/qemu/docs/specs/ppc-spapr-hcalls.txt @@ -41,8 +41,8 @@ When the guest runs in "real mode" (in powerpc lingua this means with MMU disabled, ie guest effective == guest physical), it only has access to a subset of memory and no IOs. -PAPR provides a set of hypervisor calls to perform cachable or -non-cachable accesses to any guest physical addresses that the +PAPR provides a set of hypervisor calls to perform cacheable or +non-cacheable accesses to any guest physical addresses that the guest can use in order to access IO devices while in real mode. This is typically used by the firmware running in the guest. diff --git a/qemu/docs/specs/ppc-spapr-hotplug.txt b/qemu/docs/specs/ppc-spapr-hotplug.txt index 46e07196b..631b0cada 100644 --- a/qemu/docs/specs/ppc-spapr-hotplug.txt +++ b/qemu/docs/specs/ppc-spapr-hotplug.txt @@ -302,4 +302,52 @@ consisting of , and . pseries guests use this property to note the maximum allowed CPUs for the guest. +== ibm,dynamic-reconfiguration-memory == + +ibm,dynamic-reconfiguration-memory is a device tree node that represents +dynamically reconfigurable logical memory blocks (LMB). This node +is generated only when the guest advertises the support for it via +ibm,client-architecture-support call. Memory that is not dynamically +reconfigurable is represented by /memory nodes. The properties of this +node that are of interest to the sPAPR memory hotplug implementation +in QEMU are described here. + +ibm,lmb-size + +This 64bit integer defines the size of each dynamically reconfigurable LMB. + +ibm,associativity-lookup-arrays + +This property defines a lookup array in which the NUMA associativity +information for each LMB can be found. It is a property encoded array +that begins with an integer M, the number of associativity lists followed +by an integer N, the number of entries per associativity list and terminated +by M associativity lists each of length N integers. + +This property provides the same information as given by ibm,associativity +property in a /memory node. Each assigned LMB has an index value between +0 and M-1 which is used as an index into this table to select which +associativity list to use for the LMB. This index value for each LMB +is defined in ibm,dynamic-memory property. + +ibm,dynamic-memory + +This property describes the dynamically reconfigurable memory. It is a +property encoded array that has an integer N, the number of LMBs followed +by N LMB list entires. + +Each LMB list entry consists of the following elements: + +- Logical address of the start of the LMB encoded as a 64bit integer. This + corresponds to reg property in /memory node. +- DRC index of the LMB that corresponds to ibm,my-drc-index property + in a /memory node. +- Four bytes reserved for expansion. +- Associativity list index for the LMB that is used as an index into + ibm,associativity-lookup-arrays property described earlier. This + is used to retrieve the right associativity list to be used for this + LMB. +- A 32bit flags word. The bit at bit position 0x00000008 defines whether + the LMB is assigned to the the partition as of boot time. + [1] http://thread.gmane.org/gmane.linux.ports.ppc.embedded/75350/focus=106867 diff --git a/qemu/docs/specs/qcow2.txt b/qemu/docs/specs/qcow2.txt index 121dfc8cc..80cdfd0e9 100644 --- a/qemu/docs/specs/qcow2.txt +++ b/qemu/docs/specs/qcow2.txt @@ -103,7 +103,18 @@ in the description of a field. write to an image with unknown auto-clear features if it clears the respective bits from this field first. - Bits 0-63: Reserved (set to 0) + Bit 0: Bitmaps extension bit + This bit indicates consistency for the bitmaps + extension data. + + It is an error if this bit is set without the + bitmaps extension present. + + If the bitmaps extension is present but this + bit is unset, the bitmaps extension data must be + considered inconsistent. + + Bits 1-63: Reserved (set to 0) 96 - 99: refcount_order Describes the width of a reference count block entry (width @@ -123,6 +134,7 @@ be stored. Each extension has a structure like the following: 0x00000000 - End of the header extension area 0xE2792ACA - Backing file format name 0x6803f857 - Feature name table + 0x23852875 - Bitmaps extension other - Unknown header extension, can be safely ignored @@ -166,6 +178,36 @@ the header extension data. Each entry look like this: terminated if it has full length) +== Bitmaps extension == + +The bitmaps extension is an optional header extension. It provides the ability +to store bitmaps related to a virtual disk. For now, there is only one bitmap +type: the dirty tracking bitmap, which tracks virtual disk changes from some +point in time. + +The data of the extension should be considered consistent only if the +corresponding auto-clear feature bit is set, see autoclear_features above. + +The fields of the bitmaps extension are: + + Byte 0 - 3: nb_bitmaps + The number of bitmaps contained in the image. Must be + greater than or equal to 1. + + Note: Qemu currently only supports up to 65535 bitmaps per + image. + + 4 - 7: Reserved, must be zero. + + 8 - 15: bitmap_directory_size + Size of the bitmap directory in bytes. It is the cumulative + size of all (nb_bitmaps) bitmap headers. + + 16 - 23: bitmap_directory_offset + Offset into the image file at which the bitmap directory + starts. Must be aligned to a cluster boundary. + + == Host cluster management == qcow2 manages the allocation of host clusters by maintaining a reference count @@ -257,7 +299,7 @@ L2 table entry: 63: 0 for a cluster that is unused or requires COW, 1 if its refcount is exactly one. This information is only accurate - in L2 tables that are reachable from the the active L1 + in L2 tables that are reachable from the active L1 table. Standard Cluster Descriptor: @@ -360,3 +402,180 @@ Snapshot table entry: variable: Padding to round up the snapshot table entry size to the next multiple of 8. + + +== Bitmaps == + +As mentioned above, the bitmaps extension provides the ability to store bitmaps +related to a virtual disk. This section describes how these bitmaps are stored. + +All stored bitmaps are related to the virtual disk stored in the same image, so +each bitmap size is equal to the virtual disk size. + +Each bit of the bitmap is responsible for strictly defined range of the virtual +disk. For bit number bit_nr the corresponding range (in bytes) will be: + + [bit_nr * bitmap_granularity .. (bit_nr + 1) * bitmap_granularity - 1] + +Granularity is a property of the concrete bitmap, see below. + + +=== Bitmap directory === + +Each bitmap saved in the image is described in a bitmap directory entry. The +bitmap directory is a contiguous area in the image file, whose starting offset +and length are given by the header extension fields bitmap_directory_offset and +bitmap_directory_size. The entries of the bitmap directory have variable +length, depending on the lengths of the bitmap name and extra data. These +entries are also called bitmap headers. + +Structure of a bitmap directory entry: + + Byte 0 - 7: bitmap_table_offset + Offset into the image file at which the bitmap table + (described below) for the bitmap starts. Must be aligned to + a cluster boundary. + + 8 - 11: bitmap_table_size + Number of entries in the bitmap table of the bitmap. + + 12 - 15: flags + Bit + 0: in_use + The bitmap was not saved correctly and may be + inconsistent. + + 1: auto + The bitmap must reflect all changes of the virtual + disk by any application that would write to this qcow2 + file (including writes, snapshot switching, etc.). The + type of this bitmap must be 'dirty tracking bitmap'. + + 2: extra_data_compatible + This flags is meaningful when the extra data is + unknown to the software (currently any extra data is + unknown to Qemu). + If it is set, the bitmap may be used as expected, extra + data must be left as is. + If it is not set, the bitmap must not be used, but + both it and its extra data be left as is. + + Bits 3 - 31 are reserved and must be 0. + + 16: type + This field describes the sort of the bitmap. + Values: + 1: Dirty tracking bitmap + + Values 0, 2 - 255 are reserved. + + 17: granularity_bits + Granularity bits. Valid values: 0 - 63. + + Note: Qemu currently doesn't support granularity_bits + greater than 31. + + Granularity is calculated as + granularity = 1 << granularity_bits + + A bitmap's granularity is how many bytes of the image + accounts for one bit of the bitmap. + + 18 - 19: name_size + Size of the bitmap name. Must be non-zero. + + Note: Qemu currently doesn't support values greater than + 1023. + + 20 - 23: extra_data_size + Size of type-specific extra data. + + For now, as no extra data is defined, extra_data_size is + reserved and should be zero. If it is non-zero the + behavior is defined by extra_data_compatible flag. + + variable: extra_data + Extra data for the bitmap, occupying extra_data_size bytes. + Extra data must never contain references to clusters or in + some other way allocate additional clusters. + + variable: name + The name of the bitmap (not null terminated), occupying + name_size bytes. Must be unique among all bitmap names + within the bitmaps extension. + + variable: Padding to round up the bitmap directory entry size to the + next multiple of 8. All bytes of the padding must be zero. + + +=== Bitmap table === + +Each bitmap is stored using a one-level structure (as opposed to two-level +structures like for refcounts and guest clusters mapping) for the mapping of +bitmap data to host clusters. This structure is called the bitmap table. + +Each bitmap table has a variable size (stored in the bitmap directory entry) +and may use multiple clusters, however, it must be contiguous in the image +file. + +Structure of a bitmap table entry: + + Bit 0: Reserved and must be zero if bits 9 - 55 are non-zero. + If bits 9 - 55 are zero: + 0: Cluster should be read as all zeros. + 1: Cluster should be read as all ones. + + 1 - 8: Reserved and must be zero. + + 9 - 55: Bits 9 - 55 of the host cluster offset. Must be aligned to + a cluster boundary. If the offset is 0, the cluster is + unallocated; in that case, bit 0 determines how this + cluster should be treated during reads. + + 56 - 63: Reserved and must be zero. + + +=== Bitmap data === + +As noted above, bitmap data is stored in separate clusters, described by the +bitmap table. Given an offset (in bytes) into the bitmap data, the offset into +the image file can be obtained as follows: + + image_offset(bitmap_data_offset) = + bitmap_table[bitmap_data_offset / cluster_size] + + (bitmap_data_offset % cluster_size) + +This offset is not defined if bits 9 - 55 of bitmap table entry are zero (see +above). + +Given an offset byte_nr into the virtual disk and the bitmap's granularity, the +bit offset into the image file to the corresponding bit of the bitmap can be +calculated like this: + + bit_offset(byte_nr) = + image_offset(byte_nr / granularity / 8) * 8 + + (byte_nr / granularity) % 8 + +If the size of the bitmap data is not a multiple of the cluster size then the +last cluster of the bitmap data contains some unused tail bits. These bits must +be zero. + + +=== Dirty tracking bitmaps === + +Bitmaps with 'type' field equal to one are dirty tracking bitmaps. + +When the virtual disk is in use dirty tracking bitmap may be 'enabled' or +'disabled'. While the bitmap is 'enabled', all writes to the virtual disk +should be reflected in the bitmap. A set bit in the bitmap means that the +corresponding range of the virtual disk (see above) was written to while the +bitmap was 'enabled'. An unset bit means that this range was not written to. + +The software doesn't have to sync the bitmap in the image file with its +representation in RAM after each write. Flag 'in_use' should be set while the +bitmap is not synced. + +In the image file the 'enabled' state is reflected by the 'auto' flag. If this +flag is set, the software must consider the bitmap as 'enabled' and start +tracking virtual disk changes to this bitmap from the first write to the +virtual disk. If this flag is not set then the bitmap is disabled. diff --git a/qemu/docs/specs/rocker.txt b/qemu/docs/specs/rocker.txt index 1c743515c..d2a82624f 100644 --- a/qemu/docs/specs/rocker.txt +++ b/qemu/docs/specs/rocker.txt @@ -297,7 +297,7 @@ but not fired. If only partial credits are returned, the interrupt remains masked but the device generates an interrupt, signaling the driver that more outstanding work is available. -(* this masking is unrelated to to the MSI-X interrupt mask register) +(* this masking is unrelated to the MSI-X interrupt mask register) Endianness ---------- diff --git a/qemu/docs/specs/vhost-user.txt b/qemu/docs/specs/vhost-user.txt index 650bb1818..777c49cfe 100644 --- a/qemu/docs/specs/vhost-user.txt +++ b/qemu/docs/specs/vhost-user.txt @@ -87,6 +87,14 @@ Depending on the request type, payload can be: User address: a 64-bit user address mmap offset: 64-bit offset where region starts in the mapped memory +* Log description + --------------------------- + | log size | log offset | + --------------------------- + log size: size of area used for logging + log offset: offset from start of supplied file descriptor + where logging starts (i.e. where guest address 0 would be logged) + In QEMU the vhost-user message is implemented with the following struct: typedef struct VhostUserMsg { @@ -98,6 +106,7 @@ typedef struct VhostUserMsg { struct vhost_vring_state state; struct vhost_vring_addr addr; VhostUserMemory memory; + VhostUserLog log; }; } QEMU_PACKED VhostUserMsg; @@ -113,12 +122,15 @@ message replies. Most of the requests don't require replies. Here is a list of the ones that do: * VHOST_GET_FEATURES + * VHOST_GET_PROTOCOL_FEATURES * VHOST_GET_VRING_BASE + * VHOST_SET_LOG_BASE (if VHOST_USER_PROTOCOL_F_LOG_SHMFD) There are several messages that the master sends with file descriptors passed in the ancillary data: * VHOST_SET_MEM_TABLE + * VHOST_SET_LOG_BASE (if VHOST_USER_PROTOCOL_F_LOG_SHMFD) * VHOST_SET_LOG_FD * VHOST_SET_VRING_KICK * VHOST_SET_VRING_CALL @@ -127,6 +139,122 @@ in the ancillary data: If Master is unable to send the full message or receives a wrong reply it will close the connection. An optional reconnection mechanism can be implemented. +Any protocol extensions are gated by protocol feature bits, +which allows full backwards compatibility on both master +and slave. +As older slaves don't support negotiating protocol features, +a feature bit was dedicated for this purpose: +#define VHOST_USER_F_PROTOCOL_FEATURES 30 + +Starting and stopping rings +---------------------- +Client must only process each ring when it is started. + +Client must only pass data between the ring and the +backend, when the ring is enabled. + +If ring is started but disabled, client must process the +ring without talking to the backend. + +For example, for a networking device, in the disabled state +client must not supply any new RX packets, but must process +and discard any TX packets. + +If VHOST_USER_F_PROTOCOL_FEATURES has not been negotiated, the ring is initialized +in an enabled state. + +If VHOST_USER_F_PROTOCOL_FEATURES has been negotiated, the ring is initialized +in a disabled state. Client must not pass data to/from the backend until ring is enabled by +VHOST_USER_SET_VRING_ENABLE with parameter 1, or after it has been disabled by +VHOST_USER_SET_VRING_ENABLE with parameter 0. + +Each ring is initialized in a stopped state, client must not process it until +ring is started, or after it has been stopped. + +Client must start ring upon receiving a kick (that is, detecting that file +descriptor is readable) on the descriptor specified by +VHOST_USER_SET_VRING_KICK, and stop ring upon receiving +VHOST_USER_GET_VRING_BASE. + +While processing the rings (whether they are enabled or not), client must +support changing some configuration aspects on the fly. + +Multiple queue support +---------------------- + +Multiple queue is treated as a protocol extension, hence the slave has to +implement protocol features first. The multiple queues feature is supported +only when the protocol feature VHOST_USER_PROTOCOL_F_MQ (bit 0) is set. + +The max number of queues the slave supports can be queried with message +VHOST_USER_GET_PROTOCOL_FEATURES. Master should stop when the number of +requested queues is bigger than that. + +As all queues share one connection, the master uses a unique index for each +queue in the sent message to identify a specified queue. One queue pair +is enabled initially. More queues are enabled dynamically, by sending +message VHOST_USER_SET_VRING_ENABLE. + +Migration +--------- + +During live migration, the master may need to track the modifications +the slave makes to the memory mapped regions. The client should mark +the dirty pages in a log. Once it complies to this logging, it may +declare the VHOST_F_LOG_ALL vhost feature. + +To start/stop logging of data/used ring writes, server may send messages +VHOST_USER_SET_FEATURES with VHOST_F_LOG_ALL and VHOST_USER_SET_VRING_ADDR with +VHOST_VRING_F_LOG in ring's flags set to 1/0, respectively. + +All the modifications to memory pointed by vring "descriptor" should +be marked. Modifications to "used" vring should be marked if +VHOST_VRING_F_LOG is part of ring's flags. + +Dirty pages are of size: +#define VHOST_LOG_PAGE 0x1000 + +The log memory fd is provided in the ancillary data of +VHOST_USER_SET_LOG_BASE message when the slave has +VHOST_USER_PROTOCOL_F_LOG_SHMFD protocol feature. + +The size of the log is supplied as part of VhostUserMsg +which should be large enough to cover all known guest +addresses. Log starts at the supplied offset in the +supplied file descriptor. +The log covers from address 0 to the maximum of guest +regions. In pseudo-code, to mark page at "addr" as dirty: + +page = addr / VHOST_LOG_PAGE +log[page / 8] |= 1 << page % 8 + +Where addr is the guest physical address. + +Use atomic operations, as the log may be concurrently manipulated. + +Note that when logging modifications to the used ring (when VHOST_VRING_F_LOG +is set for this ring), log_guest_addr should be used to calculate the log +offset: the write to first byte of the used ring is logged at this offset from +log start. Also note that this value might be outside the legal guest physical +address range (i.e. does not have to be covered by the VhostUserMemory table), +but the bit offset of the last byte of the ring must fall within +the size supplied by VhostUserLog. + +VHOST_USER_SET_LOG_FD is an optional message with an eventfd in +ancillary data, it may be used to inform the master that the log has +been modified. + +Once the source has finished migration, rings will be stopped by +the source. No further update must be done before rings are +restarted. + +Protocol features +----------------- + +#define VHOST_USER_PROTOCOL_F_MQ 0 +#define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1 +#define VHOST_USER_PROTOCOL_F_RARP 2 + Message types ------------- @@ -138,6 +266,8 @@ Message types Slave payload: u64 Get from the underlying vhost implementation the features bitmask. + Feature bit VHOST_USER_F_PROTOCOL_FEATURES signals slave support for + VHOST_USER_GET_PROTOCOL_FEATURES and VHOST_USER_SET_PROTOCOL_FEATURES. * VHOST_USER_SET_FEATURES @@ -146,6 +276,33 @@ Message types Master payload: u64 Enable features in the underlying vhost implementation using a bitmask. + Feature bit VHOST_USER_F_PROTOCOL_FEATURES signals slave support for + VHOST_USER_GET_PROTOCOL_FEATURES and VHOST_USER_SET_PROTOCOL_FEATURES. + + * VHOST_USER_GET_PROTOCOL_FEATURES + + Id: 15 + Equivalent ioctl: VHOST_GET_FEATURES + Master payload: N/A + Slave payload: u64 + + Get the protocol feature bitmask from the underlying vhost implementation. + Only legal if feature bit VHOST_USER_F_PROTOCOL_FEATURES is present in + VHOST_USER_GET_FEATURES. + Note: slave that reported VHOST_USER_F_PROTOCOL_FEATURES must support + this message even before VHOST_USER_SET_FEATURES was called. + + * VHOST_USER_SET_PROTOCOL_FEATURES + + Id: 16 + Ioctl: VHOST_SET_FEATURES + Master payload: u64 + + Enable protocol features in the underlying vhost implementation. + Only legal if feature bit VHOST_USER_F_PROTOCOL_FEATURES is present in + VHOST_USER_GET_FEATURES. + Note: slave that reported VHOST_USER_F_PROTOCOL_FEATURES must support + this message even before VHOST_USER_SET_FEATURES was called. * VHOST_USER_SET_OWNER @@ -160,11 +317,13 @@ Message types * VHOST_USER_RESET_OWNER Id: 4 - Equivalent ioctl: VHOST_RESET_OWNER Master payload: N/A - Issued when a new connection is about to be closed. The Master will no - longer own this connection (and will usually close it). + This is no longer used. Used to be sent to request disabling + all rings, but some clients interpreted it to also discard + connection state (this interpretation would lead to bugs). + It is recommended that clients either ignore this message, + or use it to disable all rings. * VHOST_USER_SET_MEM_TABLE @@ -182,8 +341,14 @@ Message types Id: 6 Equivalent ioctl: VHOST_SET_LOG_BASE Master payload: u64 + Slave payload: N/A + + Sets logging shared memory space. + When slave has VHOST_USER_PROTOCOL_F_LOG_SHMFD protocol + feature, the log memory fd is provided in the ancillary data of + VHOST_USER_SET_LOG_BASE message, the size and offset of shared + memory area provided in the message. - Sets the logging base address. * VHOST_USER_SET_LOG_FD @@ -199,7 +364,7 @@ Message types Equivalent ioctl: VHOST_SET_VRING_NUM Master payload: vring state description - Sets the number of vrings for this owner. + Set the size of the queue. * VHOST_USER_SET_VRING_ADDR @@ -264,3 +429,38 @@ Message types Bits (0-7) of the payload contain the vring index. Bit 8 is the invalid FD flag. This flag is set when there is no file descriptor in the ancillary data. + + * VHOST_USER_GET_QUEUE_NUM + + Id: 17 + Equivalent ioctl: N/A + Master payload: N/A + Slave payload: u64 + + Query how many queues the backend supports. This request should be + sent only when VHOST_USER_PROTOCOL_F_MQ is set in queried protocol + features by VHOST_USER_GET_PROTOCOL_FEATURES. + + * VHOST_USER_SET_VRING_ENABLE + + Id: 18 + Equivalent ioctl: N/A + Master payload: vring state description + + Signal slave to enable or disable corresponding vring. + This request should be sent only when VHOST_USER_F_PROTOCOL_FEATURES + has been negotiated. + + * VHOST_USER_SEND_RARP + + Id: 19 + Equivalent ioctl: N/A + Master payload: u64 + + Ask vhost user backend to broadcast a fake RARP to notify the migration + is terminated for guest that does not support GUEST_ANNOUNCE. + Only legal if feature bit VHOST_USER_F_PROTOCOL_FEATURES is present in + VHOST_USER_GET_FEATURES and protocol feature bit VHOST_USER_PROTOCOL_F_RARP + is present in VHOST_USER_GET_PROTOCOL_FEATURES. + The first 6 bytes of the payload contain the mac address of the guest to + allow the vhost user backend to construct and broadcast the fake RARP. diff --git a/qemu/docs/throttle.txt b/qemu/docs/throttle.txt new file mode 100644 index 000000000..28204e46c --- /dev/null +++ b/qemu/docs/throttle.txt @@ -0,0 +1,252 @@ +The QEMU throttling infrastructure +================================== +Copyright (C) 2016 Igalia, S.L. +Author: Alberto Garcia + +This work is licensed under the terms of the GNU GPL, version 2 or +later. See the COPYING file in the top-level directory. + +Introduction +------------ +QEMU includes a throttling module that can be used to set limits to +I/O operations. The code itself is generic and independent of the I/O +units, but it is currenly used to limit the number of bytes per second +and operations per second (IOPS) when performing disk I/O. + +This document explains how to use the throttling code in QEMU, and how +it works internally. The implementation is in throttle.c. + + +Using throttling to limit disk I/O +---------------------------------- +Two aspects of the disk I/O can be limited: the number of bytes per +second and the number of operations per second (IOPS). For each one of +them the user can set a global limit or separate limits for read and +write operations. This gives us a total of six different parameters. + +I/O limits can be set using the throttling.* parameters of -drive, or +using the QMP 'block_set_io_throttle' command. These are the names of +the parameters for both cases: + +|-----------------------+-----------------------| +| -drive | block_set_io_throttle | +|-----------------------+-----------------------| +| throttling.iops-total | iops | +| throttling.iops-read | iops_rd | +| throttling.iops-write | iops_wr | +| throttling.bps-total | bps | +| throttling.bps-read | bps_rd | +| throttling.bps-write | bps_wr | +|-----------------------+-----------------------| + +It is possible to set limits for both IOPS and bps and the same time, +and for each case we can decide whether to have separate read and +write limits or not, but note that if iops-total is set then neither +iops-read nor iops-write can be set. The same applies to bps-total and +bps-read/write. + +The default value of these parameters is 0, and it means 'unlimited'. + +In its most basic usage, the user can add a drive to QEMU with a limit +of 100 IOPS with the following -drive line: + + -drive file=hd0.qcow2,throttling.iops-total=100 + +We can do the same using QMP. In this case all these parameters are +mandatory, so we must set to 0 the ones that we don't want to limit: + + { "execute": "block_set_io_throttle", + "arguments": { + "device": "virtio0", + "iops": 100, + "iops_rd": 0, + "iops_wr": 0, + "bps": 0, + "bps_rd": 0, + "bps_wr": 0 + } + } + + +I/O bursts +---------- +In addition to the basic limits we have just seen, QEMU allows the +user to do bursts of I/O for a configurable amount of time. A burst is +an amount of I/O that can exceed the basic limit. Bursts are useful to +allow better performance when there are peaks of activity (the OS +boots, a service needs to be restarted) while keeping the average +limits lower the rest of the time. + +Two parameters control bursts: their length and the maximum amount of +I/O they allow. These two can be configured separately for each one of +the six basic parameters described in the previous section, but in +this section we'll use 'iops-total' as an example. + +The I/O limit during bursts is set using 'iops-total-max', and the +maximum length (in seconds) is set with 'iops-total-max-length'. So if +we want to configure a drive with a basic limit of 100 IOPS and allow +bursts of 2000 IOPS for 60 seconds, we would do it like this (the line +is split for clarity): + + -drive file=hd0.qcow2, + throttling.iops-total=100, + throttling.iops-total-max=2000, + throttling.iops-total-max-length=60 + +Or, with QMP: + + { "execute": "block_set_io_throttle", + "arguments": { + "device": "virtio0", + "iops": 100, + "iops_rd": 0, + "iops_wr": 0, + "bps": 0, + "bps_rd": 0, + "bps_wr": 0, + "iops_max": 2000, + "iops_max_length": 60, + } + } + +With this, the user can perform I/O on hd0.qcow2 at a rate of 2000 +IOPS for 1 minute before it's throttled down to 100 IOPS. + +The user will be able to do bursts again if there's a sufficiently +long period of time with unused I/O (see below for details). + +The default value for 'iops-total-max' is 0 and it means that bursts +are not allowed. 'iops-total-max-length' can only be set if +'iops-total-max' is set as well, and its default value is 1 second. + +Here's the complete list of parameters for configuring bursts: + +|----------------------------------+-----------------------| +| -drive | block_set_io_throttle | +|----------------------------------+-----------------------| +| throttling.iops-total-max | iops_max | +| throttling.iops-total-max-length | iops_max_length | +| throttling.iops-read-max | iops_rd_max | +| throttling.iops-read-max-length | iops_rd_max_length | +| throttling.iops-write-max | iops_wr_max | +| throttling.iops-write-max-length | iops_wr_max_length | +| throttling.bps-total-max | bps_max | +| throttling.bps-total-max-length | bps_max_length | +| throttling.bps-read-max | bps_rd_max | +| throttling.bps-read-max-length | bps_rd_max_length | +| throttling.bps-write-max | bps_wr_max | +| throttling.bps-write-max-length | bps_wr_max_length | +|----------------------------------+-----------------------| + + +Controlling the size of I/O operations +-------------------------------------- +When applying IOPS limits all I/O operations are treated equally +regardless of their size. This means that the user can take advantage +of this in order to circumvent the limits and submit one huge I/O +request instead of several smaller ones. + +QEMU provides a setting called throttling.iops-size to prevent this +from happening. This setting specifies the size (in bytes) of an I/O +request for accounting purposes. Larger requests will be counted +proportionally to this size. + +For example, if iops-size is set to 4096 then an 8KB request will be +counted as two, and a 6KB request will be counted as one and a +half. This only applies to requests larger than iops-size: smaller +requests will be always counted as one, no matter their size. + +The default value of iops-size is 0 and it means that the size of the +requests is never taken into account when applying IOPS limits. + + +Applying I/O limits to groups of disks +-------------------------------------- +In all the examples so far we have seen how to apply limits to the I/O +performed on individual drives, but QEMU allows grouping drives so +they all share the same limits. + +The way it works is that each drive with I/O limits is assigned to a +group named using the throttling.group parameter. If this parameter is +not specified, then the device name (i.e. 'virtio0', 'ide0-hd0') will +be used as the group name. + +Limits set using the throttling.* parameters discussed earlier in this +document apply to the combined I/O of all members of a group. + +Consider this example: + + -drive file=hd1.qcow2,throttling.iops-total=6000,throttling.group=foo + -drive file=hd2.qcow2,throttling.iops-total=6000,throttling.group=foo + -drive file=hd3.qcow2,throttling.iops-total=3000,throttling.group=bar + -drive file=hd4.qcow2,throttling.iops-total=6000,throttling.group=foo + -drive file=hd5.qcow2,throttling.iops-total=3000,throttling.group=bar + -drive file=hd6.qcow2,throttling.iops-total=5000 + +Here hd1, hd2 and hd4 are all members of a group named 'foo' with a +combined IOPS limit of 6000, and hd3 and hd5 are members of 'bar'. hd6 +is left alone (technically it is part of a 1-member group). + +Limits are applied in a round-robin fashion so if there are concurrent +I/O requests on several drives of the same group they will be +distributed evenly. + +When I/O limits are applied to an existing drive using the QMP command +'block_set_io_throttle', the following things need to be taken into +account: + + - I/O limits are shared within the same group, so new values will + affect all members and overwrite the previous settings. In other + words: if different limits are applied to members of the same + group, the last one wins. + + - If 'group' is unset it is assumed to be the current group of that + drive. If the drive is not in a group yet, it will be added to a + group named after the device name. + + - If 'group' is set then the drive will be moved to that group if + it was member of a different one. In this case the limits + specified in the parameters will be applied to the new group + only. + + - I/O limits can be disabled by setting all of them to 0. In this + case the device will be removed from its group and the rest of + its members will not be affected. The 'group' parameter is + ignored. + + +The Leaky Bucket algorithm +-------------------------- +I/O limits in QEMU are implemented using the leaky bucket algorithm +(specifically the "Leaky bucket as a meter" variant). + +This algorithm uses the analogy of a bucket that leaks water +constantly. The water that gets into the bucket represents the I/O +that has been performed, and no more I/O is allowed once the bucket is +full. + +To see the way this corresponds to the throttling parameters in QEMU, +consider the following values: + + iops-total=100 + iops-total-max=2000 + iops-total-max-length=60 + + - Water leaks from the bucket at a rate of 100 IOPS. + - Water can be added to the bucket at a rate of 2000 IOPS. + - The size of the bucket is 2000 x 60 = 120000 + - If 'iops-total-max-length' is unset then the bucket size is 100. + +The bucket is initially empty, therefore water can be added until it's +full at a rate of 2000 IOPS (the burst rate). Once the bucket is full +we can only add as much water as it leaks, therefore the I/O rate is +reduced to 100 IOPS. If we add less water than it leaks then the +bucket will start to empty, allowing for bursts again. + +Note that since water is leaking from the bucket even during bursts, +it will take a bit more than 60 seconds at 2000 IOPS to fill it +up. After those 60 seconds the bucket will have leaked 60 x 100 = +6000, allowing for 3 more seconds of I/O at 2000 IOPS. + +Also, due to the way the algorithm works, longer burst can be done at +a lower I/O rate, e.g. 1000 IOPS during 120 seconds. diff --git a/qemu/docs/tracing.txt b/qemu/docs/tracing.txt index 7117c5e7d..0bd6b9cf9 100644 --- a/qemu/docs/tracing.txt +++ b/qemu/docs/tracing.txt @@ -157,9 +157,9 @@ performance penalty. Note that regardless of the selected trace backend, events with the "disable" property will be generated with the "nop" backend. -=== Stderr === +=== Log === -The "stderr" backend sends trace events directly to standard error. This +The "log" backend sends trace events directly to standard error. This effectively turns trace events into debug printfs. This is the simplest backend and can be used together with existing code that @@ -172,9 +172,6 @@ source tree. It may not be as powerful as platform-specific or third-party trace backends but it is portable. This is the recommended trace backend unless you have specific needs for more advanced backends. -The "simple" backend currently does not capture string arguments, it simply -records the char* pointer value instead of the string that is pointed to. - === Ftrace === The "ftrace" backend writes trace data to ftrace marker. This effectively @@ -258,11 +255,11 @@ is generated to make use in scripts more convenient. This step can also be performed manually after a build in order to change the binary name in the .stp probes: - scripts/tracetool --dtrace --stap \ - --binary path/to/qemu-binary \ - --target-type system \ - --target-name x86_64 \ - qemu.stp + scripts/tracetool.py --backends=dtrace --format=stap \ + --binary path/to/qemu-binary \ + --target-type system \ + --target-name x86_64 \ + qemu.stp == Trace event properties == @@ -347,3 +344,44 @@ This will immediately call: and will generate the TCG code to call: void trace_foo(uint8_t a1, uint32_t a2); + +=== "vcpu" === + +Identifies events that trace vCPU-specific information. It implicitly adds a +"CPUState*" argument, and extends the tracing print format to show the vCPU +information. If used together with the "tcg" property, it adds a second +"TCGv_env" argument that must point to the per-target global TCG register that +points to the vCPU when guest code is executed (usually the "cpu_env" variable). + +The following example events: + + foo(uint32_t a) "a=%x" + vcpu bar(uint32_t a) "a=%x" + tcg vcpu baz(uint32_t a) "a=%x", "a=%x" + +Can be used as: + + #include "trace-tcg.h" + + CPUArchState *env; + TCGv_ptr cpu_env; + + void some_disassembly_func(...) + { + /* trace emitted at this point */ + trace_foo(0xd1); + /* trace emitted at this point */ + trace_bar(ENV_GET_CPU(env), 0xd2); + /* trace emitted at this point (env) and when guest code is executed (cpu_env) */ + trace_baz_tcg(ENV_GET_CPU(env), cpu_env, 0xd3); + } + +If the translating vCPU has address 0xc1 and code is later executed by vCPU +0xc2, this would be an example output: + + // at guest code translation + foo a=0xd1 + bar cpu=0xc1 a=0xd2 + baz_trans cpu=0xc1 a=0xd3 + // at guest code execution + baz_exec cpu=0xc2 a=0xd3 diff --git a/qemu/docs/virtio-migration.txt b/qemu/docs/virtio-migration.txt new file mode 100644 index 000000000..cf66458b9 --- /dev/null +++ b/qemu/docs/virtio-migration.txt @@ -0,0 +1,106 @@ +Virtio devices and migration +============================ + +Copyright 2015 IBM Corp. + +This work is licensed under the terms of the GNU GPL, version 2 or later. See +the COPYING file in the top-level directory. + +Saving and restoring the state of virtio devices is a bit of a twisty maze, +for several reasons: +- state is distributed between several parts: + - virtio core, for common fields like features, number of queues, ... + - virtio transport (pci, ccw, ...), for the different proxy devices and + transport specific state (msix vectors, indicators, ...) + - virtio device (net, blk, ...), for the different device types and their + state (mac address, request queue, ...) +- most fields are saved via the stream interface; subsequently, subsections + have been added to make cross-version migration possible + +This file attempts to document the current procedure and point out some +caveats. + + +Save state procedure +==================== + +virtio core virtio transport virtio device +----------- ---------------- ------------- + + save() function registered + via register_savevm() +virtio_save() <---------- + ------> save_config() + - save proxy device + - save transport-specific + device fields +- save common device + fields +- save common virtqueue + fields + ------> save_queue() + - save transport-specific + virtqueue fields + ------> save_device() + - save device-specific + fields +- save subsections + - device endianness, + if changed from + default endianness + - 64 bit features, if + any high feature bit + is set + - virtio-1 virtqueue + fields, if VERSION_1 + is set + + +Load state procedure +==================== + +virtio core virtio transport virtio device +----------- ---------------- ------------- + + load() function registered + via register_savevm() +virtio_load() <---------- + ------> load_config() + - load proxy device + - load transport-specific + device fields +- load common device + fields +- load common virtqueue + fields + ------> load_queue() + - load transport-specific + virtqueue fields +- notify guest + ------> load_device() + - load device-specific + fields +- load subsections + - device endianness + - 64 bit features + - virtio-1 virtqueue + fields +- sanitize endianness +- sanitize features +- virtqueue index sanity + check + - feature-dependent setup + + +Implications of this setup +========================== + +Devices need to be careful in their state processing during load: The +load_device() procedure is invoked by the core before subsections have +been loaded. Any code that depends on information transmitted in subsections +therefore has to be invoked in the device's load() function _after_ +virtio_load() returned (like e.g. code depending on features). + +Any extension of the state being migrated should be done in subsections +added to the core for compatibility reasons. If transport or device specific +state is added, core needs to invoke a callback from the new subsection. diff --git a/qemu/docs/win32-qemu-event.promela b/qemu/docs/win32-qemu-event.promela new file mode 100644 index 000000000..c446a7155 --- /dev/null +++ b/qemu/docs/win32-qemu-event.promela @@ -0,0 +1,98 @@ +/* + * This model describes the implementation of QemuEvent in + * util/qemu-thread-win32.c. + * + * Author: Paolo Bonzini + * + * This file is in the public domain. If you really want a license, + * the WTFPL will do. + * + * To verify it: + * spin -a docs/event.promela + * gcc -O2 pan.c -DSAFETY + * ./a.out + */ + +bool event; +int value; + +/* Primitives for a Win32 event */ +#define RAW_RESET event = false +#define RAW_SET event = true +#define RAW_WAIT do :: event -> break; od + +#if 0 +/* Basic sanity checking: test the Win32 event primitives */ +#define RESET RAW_RESET +#define SET RAW_SET +#define WAIT RAW_WAIT +#else +/* Full model: layer a userspace-only fast path on top of the RAW_* + * primitives. SET/RESET/WAIT have exactly the same semantics as + * RAW_SET/RAW_RESET/RAW_WAIT, but try to avoid invoking them. + */ +#define EV_SET 0 +#define EV_FREE 1 +#define EV_BUSY -1 + +int state = EV_FREE; + +int xchg_result; +#define SET if :: state != EV_SET -> \ + atomic { /* xchg_result=xchg(state, EV_SET) */ \ + xchg_result = state; \ + state = EV_SET; \ + } \ + if :: xchg_result == EV_BUSY -> RAW_SET; \ + :: else -> skip; \ + fi; \ + :: else -> skip; \ + fi + +#define RESET if :: state == EV_SET -> atomic { state = state | EV_FREE; } \ + :: else -> skip; \ + fi + +int tmp1, tmp2; +#define WAIT tmp1 = state; \ + if :: tmp1 != EV_SET -> \ + if :: tmp1 == EV_FREE -> \ + RAW_RESET; \ + atomic { /* tmp2=cas(state, EV_FREE, EV_BUSY) */ \ + tmp2 = state; \ + if :: tmp2 == EV_FREE -> state = EV_BUSY; \ + :: else -> skip; \ + fi; \ + } \ + if :: tmp2 == EV_SET -> tmp1 = EV_SET; \ + :: else -> tmp1 = EV_BUSY; \ + fi; \ + :: else -> skip; \ + fi; \ + assert(tmp1 != EV_FREE); \ + if :: tmp1 == EV_BUSY -> RAW_WAIT; \ + :: else -> skip; \ + fi; \ + :: else -> skip; \ + fi +#endif + +active proctype waiter() +{ + if + :: !value -> + RESET; + if + :: !value -> WAIT; + :: else -> skip; + fi; + :: else -> skip; + fi; + assert(value); +} + +active proctype notifier() +{ + value = true; + SET; +} diff --git a/qemu/docs/writing-qmp-commands.txt b/qemu/docs/writing-qmp-commands.txt index ab1fdd36b..59aa77ae2 100644 --- a/qemu/docs/writing-qmp-commands.txt +++ b/qemu/docs/writing-qmp-commands.txt @@ -122,12 +122,12 @@ There are a few things to be noticed: Now a little hack is needed. As we're still using the old QMP server we need to add the new command to its internal dispatch table. This step won't be required in the near future. Open the qmp-commands.hx file and add the -following in the botton: +following at the bottom: { .name = "hello-world", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_hello_world, + .mhandler.cmd_new = qmp_marshal_hello_world, }, You're done. Now build qemu, run it as suggested in the "Testing" section, @@ -179,7 +179,7 @@ The last step is to update the qmp-commands.hx file: { .name = "hello-world", .args_type = "message:s?", - .mhandler.cmd_new = qmp_marshal_input_hello_world, + .mhandler.cmd_new = qmp_marshal_hello_world, }, Notice that the "args_type" member got our "message" argument. The character @@ -210,7 +210,7 @@ if you don't see these strings, then something went wrong. === Errors === QMP commands should use the error interface exported by the error.h header -file. Basically, errors are set by calling the error_set() function. +file. Basically, most errors are set by calling the error_setg() function. Let's say we don't accept the string "message" to contain the word "love". If it does contain it, we want the "hello-world" command to return an error: @@ -219,8 +219,7 @@ void qmp_hello_world(bool has_message, const char *message, Error **errp) { if (has_message) { if (strstr(message, "love")) { - error_set(errp, ERROR_CLASS_GENERIC_ERROR, - "the word 'love' is not allowed"); + error_setg(errp, "the word 'love' is not allowed"); return; } printf("%s\n", message); @@ -229,10 +228,8 @@ void qmp_hello_world(bool has_message, const char *message, Error **errp) } } -The first argument to the error_set() function is the Error pointer to pointer, -which is passed to all QMP functions. The second argument is a ErrorClass -value, which should be ERROR_CLASS_GENERIC_ERROR most of the time (more -details about error classes are given below). The third argument is a human +The first argument to the error_setg() function is the Error pointer +to pointer, which is passed to all QMP functions. The next argument is a human description of the error, this is a free-form printf-like string. Let's test the example above. Build qemu, run it as defined in the "Testing" @@ -249,8 +246,9 @@ The QMP server's response should be: } } -As a general rule, all QMP errors should use ERROR_CLASS_GENERIC_ERROR. There -are two exceptions to this rule: +As a general rule, all QMP errors should use ERROR_CLASS_GENERIC_ERROR +(done by default when using error_setg()). There are two exceptions to +this rule: 1. A non-generic ErrorClass value exists* for the failure you want to report (eg. DeviceNotFound) @@ -259,8 +257,8 @@ are two exceptions to this rule: want to report, hence you have to add a new ErrorClass value so that they can check for it -If the failure you want to report doesn't fall in one of the two cases above, -just report ERROR_CLASS_GENERIC_ERROR. +If the failure you want to report falls into one of the two cases above, +use error_set() with a second argument of an ErrorClass value. * All existing ErrorClass values are defined in the qapi-schema.json file @@ -461,7 +459,7 @@ The last step is to add the correspoding entry in the qmp-commands.hx file: { .name = "query-alarm-clock", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_alarm_clock, + .mhandler.cmd_new = qmp_marshal_query_alarm_clock, }, Time to test the new command. Build qemu, run it as described in the "Testing" @@ -607,7 +605,7 @@ To test this you have to add the corresponding qmp-commands.hx entry: { .name = "query-alarm-methods", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_alarm_methods, + .mhandler.cmd_new = qmp_marshal_query_alarm_methods, }, Now Build qemu, run it as explained in the "Testing" section and try our new diff --git a/qemu/dump.c b/qemu/dump.c index 78b7d843c..9726f1f47 100644 --- a/qemu/dump.c +++ b/qemu/dump.c @@ -11,7 +11,8 @@ * */ -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "qemu/cutils.h" #include "elf.h" #include "cpu.h" #include "exec/cpu-all.h" @@ -22,9 +23,9 @@ #include "sysemu/sysemu.h" #include "sysemu/memory_mapping.h" #include "sysemu/cpus.h" -#include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qmp-commands.h" +#include "qapi-event.h" #include #ifdef CONFIG_LZO @@ -82,12 +83,6 @@ static int dump_cleanup(DumpState *s) return 0; } -static void dump_error(DumpState *s, const char *reason, Error **errp) -{ - dump_cleanup(s); - error_setg(errp, "%s", reason); -} - static int fd_write_vmcore(const void *buf, size_t size, void *opaque) { DumpState *s = opaque; @@ -128,7 +123,7 @@ static void write_elf64_header(DumpState *s, Error **errp) ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s); if (ret < 0) { - dump_error(s, "dump: failed to write elf header", errp); + error_setg(errp, "dump: failed to write elf header"); } } @@ -159,7 +154,7 @@ static void write_elf32_header(DumpState *s, Error **errp) ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s); if (ret < 0) { - dump_error(s, "dump: failed to write elf header", errp); + error_setg(errp, "dump: failed to write elf header"); } } @@ -182,7 +177,7 @@ static void write_elf64_load(DumpState *s, MemoryMapping *memory_mapping, ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s); if (ret < 0) { - dump_error(s, "dump: failed to write program header table", errp); + error_setg(errp, "dump: failed to write program header table"); } } @@ -205,7 +200,7 @@ static void write_elf32_load(DumpState *s, MemoryMapping *memory_mapping, ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s); if (ret < 0) { - dump_error(s, "dump: failed to write program header table", errp); + error_setg(errp, "dump: failed to write program header table"); } } @@ -225,7 +220,7 @@ static void write_elf64_note(DumpState *s, Error **errp) ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s); if (ret < 0) { - dump_error(s, "dump: failed to write program header table", errp); + error_setg(errp, "dump: failed to write program header table"); } } @@ -245,7 +240,7 @@ static void write_elf64_notes(WriteCoreDumpFunction f, DumpState *s, id = cpu_index(cpu); ret = cpu_write_elf64_note(f, cpu, id, s); if (ret < 0) { - dump_error(s, "dump: failed to write elf notes", errp); + error_setg(errp, "dump: failed to write elf notes"); return; } } @@ -253,7 +248,7 @@ static void write_elf64_notes(WriteCoreDumpFunction f, DumpState *s, CPU_FOREACH(cpu) { ret = cpu_write_elf64_qemunote(f, cpu, s); if (ret < 0) { - dump_error(s, "dump: failed to write CPU status", errp); + error_setg(errp, "dump: failed to write CPU status"); return; } } @@ -275,7 +270,7 @@ static void write_elf32_note(DumpState *s, Error **errp) ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s); if (ret < 0) { - dump_error(s, "dump: failed to write program header table", errp); + error_setg(errp, "dump: failed to write program header table"); } } @@ -290,7 +285,7 @@ static void write_elf32_notes(WriteCoreDumpFunction f, DumpState *s, id = cpu_index(cpu); ret = cpu_write_elf32_note(f, cpu, id, s); if (ret < 0) { - dump_error(s, "dump: failed to write elf notes", errp); + error_setg(errp, "dump: failed to write elf notes"); return; } } @@ -298,7 +293,7 @@ static void write_elf32_notes(WriteCoreDumpFunction f, DumpState *s, CPU_FOREACH(cpu) { ret = cpu_write_elf32_qemunote(f, cpu, s); if (ret < 0) { - dump_error(s, "dump: failed to write CPU status", errp); + error_setg(errp, "dump: failed to write CPU status"); return; } } @@ -326,7 +321,7 @@ static void write_elf_section(DumpState *s, int type, Error **errp) ret = fd_write_vmcore(&shdr, shdr_size, s); if (ret < 0) { - dump_error(s, "dump: failed to write section header table", errp); + error_setg(errp, "dump: failed to write section header table"); } } @@ -336,7 +331,9 @@ static void write_data(DumpState *s, void *buf, int length, Error **errp) ret = fd_write_vmcore(buf, length, s); if (ret < 0) { - dump_error(s, "dump: failed to save memory", errp); + error_setg(errp, "dump: failed to save memory"); + } else { + s->written_size += length; } } @@ -347,18 +344,18 @@ static void write_memory(DumpState *s, GuestPhysBlock *block, ram_addr_t start, int64_t i; Error *local_err = NULL; - for (i = 0; i < size / TARGET_PAGE_SIZE; i++) { - write_data(s, block->host_addr + start + i * TARGET_PAGE_SIZE, - TARGET_PAGE_SIZE, &local_err); + for (i = 0; i < size / s->dump_info.page_size; i++) { + write_data(s, block->host_addr + start + i * s->dump_info.page_size, + s->dump_info.page_size, &local_err); if (local_err) { error_propagate(errp, local_err); return; } } - if ((size % TARGET_PAGE_SIZE) != 0) { - write_data(s, block->host_addr + start + i * TARGET_PAGE_SIZE, - size % TARGET_PAGE_SIZE, &local_err); + if ((size % s->dump_info.page_size) != 0) { + write_data(s, block->host_addr + start + i * s->dump_info.page_size, + size % s->dump_info.page_size, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -568,11 +565,6 @@ static void dump_begin(DumpState *s, Error **errp) } } -static void dump_completed(DumpState *s) -{ - dump_cleanup(s); -} - static int get_next_block(DumpState *s, GuestPhysBlock *block) { while (1) { @@ -624,8 +616,6 @@ static void dump_iterate(DumpState *s, Error **errp) } } while (!get_next_block(s, block)); - - dump_completed(s); } static void create_vmcore(DumpState *s, Error **errp) @@ -737,7 +727,7 @@ static void create_header32(DumpState *s, Error **errp) strncpy(dh->signature, KDUMP_SIGNATURE, strlen(KDUMP_SIGNATURE)); dh->header_version = cpu_to_dump32(s, 6); - block_size = TARGET_PAGE_SIZE; + block_size = s->dump_info.page_size; dh->block_size = cpu_to_dump32(s, block_size); sub_hdr_size = sizeof(struct KdumpSubHeader32) + s->note_size; sub_hdr_size = DIV_ROUND_UP(sub_hdr_size, block_size); @@ -765,7 +755,7 @@ static void create_header32(DumpState *s, Error **errp) dh->status = cpu_to_dump32(s, status); if (write_buffer(s->fd, 0, dh, size) < 0) { - dump_error(s, "dump: failed to write disk dump header", errp); + error_setg(errp, "dump: failed to write disk dump header"); goto out; } @@ -775,7 +765,7 @@ static void create_header32(DumpState *s, Error **errp) /* 64bit max_mapnr_64 */ kh->max_mapnr_64 = cpu_to_dump64(s, s->max_mapnr); - kh->phys_base = cpu_to_dump32(s, PHYS_BASE); + kh->phys_base = cpu_to_dump32(s, s->dump_info.phys_base); kh->dump_level = cpu_to_dump32(s, DUMP_LEVEL); offset_note = DISKDUMP_HEADER_BLOCKS * block_size + size; @@ -784,7 +774,7 @@ static void create_header32(DumpState *s, Error **errp) if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS * block_size, kh, size) < 0) { - dump_error(s, "dump: failed to write kdump sub header", errp); + error_setg(errp, "dump: failed to write kdump sub header"); goto out; } @@ -800,7 +790,7 @@ static void create_header32(DumpState *s, Error **errp) } if (write_buffer(s->fd, offset_note, s->note_buf, s->note_size) < 0) { - dump_error(s, "dump: failed to write notes", errp); + error_setg(errp, "dump: failed to write notes"); goto out; } @@ -837,7 +827,7 @@ static void create_header64(DumpState *s, Error **errp) strncpy(dh->signature, KDUMP_SIGNATURE, strlen(KDUMP_SIGNATURE)); dh->header_version = cpu_to_dump32(s, 6); - block_size = TARGET_PAGE_SIZE; + block_size = s->dump_info.page_size; dh->block_size = cpu_to_dump32(s, block_size); sub_hdr_size = sizeof(struct KdumpSubHeader64) + s->note_size; sub_hdr_size = DIV_ROUND_UP(sub_hdr_size, block_size); @@ -865,7 +855,7 @@ static void create_header64(DumpState *s, Error **errp) dh->status = cpu_to_dump32(s, status); if (write_buffer(s->fd, 0, dh, size) < 0) { - dump_error(s, "dump: failed to write disk dump header", errp); + error_setg(errp, "dump: failed to write disk dump header"); goto out; } @@ -875,7 +865,7 @@ static void create_header64(DumpState *s, Error **errp) /* 64bit max_mapnr_64 */ kh->max_mapnr_64 = cpu_to_dump64(s, s->max_mapnr); - kh->phys_base = cpu_to_dump64(s, PHYS_BASE); + kh->phys_base = cpu_to_dump64(s, s->dump_info.phys_base); kh->dump_level = cpu_to_dump32(s, DUMP_LEVEL); offset_note = DISKDUMP_HEADER_BLOCKS * block_size + size; @@ -884,7 +874,7 @@ static void create_header64(DumpState *s, Error **errp) if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS * block_size, kh, size) < 0) { - dump_error(s, "dump: failed to write kdump sub header", errp); + error_setg(errp, "dump: failed to write kdump sub header"); goto out; } @@ -901,7 +891,7 @@ static void create_header64(DumpState *s, Error **errp) if (write_buffer(s->fd, offset_note, s->note_buf, s->note_size) < 0) { - dump_error(s, "dump: failed to write notes", errp); + error_setg(errp, "dump: failed to write notes"); goto out; } @@ -933,6 +923,11 @@ static void write_dump_header(DumpState *s, Error **errp) } } +static size_t dump_bitmap_get_bufsize(DumpState *s) +{ + return s->dump_info.page_size; +} + /* * set dump_bitmap sequencely. the bit before last_pfn is not allowed to be * rewritten, so if need to set the first bit, set last_pfn and pfn to 0. @@ -946,6 +941,8 @@ static int set_dump_bitmap(uint64_t last_pfn, uint64_t pfn, bool value, off_t old_offset, new_offset; off_t offset_bitmap1, offset_bitmap2; uint32_t byte, bit; + size_t bitmap_bufsize = dump_bitmap_get_bufsize(s); + size_t bits_per_buf = bitmap_bufsize * CHAR_BIT; /* should not set the previous place */ assert(last_pfn <= pfn); @@ -956,14 +953,14 @@ static int set_dump_bitmap(uint64_t last_pfn, uint64_t pfn, bool value, * making new_offset be bigger than old_offset can also sync remained data * into vmcore. */ - old_offset = BUFSIZE_BITMAP * (last_pfn / PFN_BUFBITMAP); - new_offset = BUFSIZE_BITMAP * (pfn / PFN_BUFBITMAP); + old_offset = bitmap_bufsize * (last_pfn / bits_per_buf); + new_offset = bitmap_bufsize * (pfn / bits_per_buf); while (old_offset < new_offset) { /* calculate the offset and write dump_bitmap */ offset_bitmap1 = s->offset_dump_bitmap + old_offset; if (write_buffer(s->fd, offset_bitmap1, buf, - BUFSIZE_BITMAP) < 0) { + bitmap_bufsize) < 0) { return -1; } @@ -971,17 +968,17 @@ static int set_dump_bitmap(uint64_t last_pfn, uint64_t pfn, bool value, offset_bitmap2 = s->offset_dump_bitmap + s->len_dump_bitmap + old_offset; if (write_buffer(s->fd, offset_bitmap2, buf, - BUFSIZE_BITMAP) < 0) { + bitmap_bufsize) < 0) { return -1; } - memset(buf, 0, BUFSIZE_BITMAP); - old_offset += BUFSIZE_BITMAP; + memset(buf, 0, bitmap_bufsize); + old_offset += bitmap_bufsize; } /* get the exact place of the bit in the buf, and set it */ - byte = (pfn % PFN_BUFBITMAP) / CHAR_BIT; - bit = (pfn % PFN_BUFBITMAP) % CHAR_BIT; + byte = (pfn % bits_per_buf) / CHAR_BIT; + bit = (pfn % bits_per_buf) % CHAR_BIT; if (value) { buf[byte] |= 1u << bit; } else { @@ -991,6 +988,20 @@ static int set_dump_bitmap(uint64_t last_pfn, uint64_t pfn, bool value, return 0; } +static uint64_t dump_paddr_to_pfn(DumpState *s, uint64_t addr) +{ + int target_page_shift = ctz32(s->dump_info.page_size); + + return (addr >> target_page_shift) - ARCH_PFN_OFFSET; +} + +static uint64_t dump_pfn_to_paddr(DumpState *s, uint64_t pfn) +{ + int target_page_shift = ctz32(s->dump_info.page_size); + + return (pfn + ARCH_PFN_OFFSET) << target_page_shift; +} + /* * exam every page and return the page frame number and the address of the page. * bufptr can be NULL. note: the blocks here is supposed to reflect guest-phys @@ -1001,16 +1012,16 @@ static bool get_next_page(GuestPhysBlock **blockptr, uint64_t *pfnptr, uint8_t **bufptr, DumpState *s) { GuestPhysBlock *block = *blockptr; - hwaddr addr; + hwaddr addr, target_page_mask = ~((hwaddr)s->dump_info.page_size - 1); uint8_t *buf; /* block == NULL means the start of the iteration */ if (!block) { block = QTAILQ_FIRST(&s->guest_phys_blocks.head); *blockptr = block; - assert((block->target_start & ~TARGET_PAGE_MASK) == 0); - assert((block->target_end & ~TARGET_PAGE_MASK) == 0); - *pfnptr = paddr_to_pfn(block->target_start); + assert((block->target_start & ~target_page_mask) == 0); + assert((block->target_end & ~target_page_mask) == 0); + *pfnptr = dump_paddr_to_pfn(s, block->target_start); if (bufptr) { *bufptr = block->host_addr; } @@ -1018,10 +1029,10 @@ static bool get_next_page(GuestPhysBlock **blockptr, uint64_t *pfnptr, } *pfnptr = *pfnptr + 1; - addr = pfn_to_paddr(*pfnptr); + addr = dump_pfn_to_paddr(s, *pfnptr); if ((addr >= block->target_start) && - (addr + TARGET_PAGE_SIZE <= block->target_end)) { + (addr + s->dump_info.page_size <= block->target_end)) { buf = block->host_addr + (addr - block->target_start); } else { /* the next page is in the next block */ @@ -1030,9 +1041,9 @@ static bool get_next_page(GuestPhysBlock **blockptr, uint64_t *pfnptr, if (!block) { return false; } - assert((block->target_start & ~TARGET_PAGE_MASK) == 0); - assert((block->target_end & ~TARGET_PAGE_MASK) == 0); - *pfnptr = paddr_to_pfn(block->target_start); + assert((block->target_start & ~target_page_mask) == 0); + assert((block->target_end & ~target_page_mask) == 0); + *pfnptr = dump_paddr_to_pfn(s, block->target_start); buf = block->host_addr; } @@ -1050,9 +1061,11 @@ static void write_dump_bitmap(DumpState *s, Error **errp) void *dump_bitmap_buf; size_t num_dumpable; GuestPhysBlock *block_iter = NULL; + size_t bitmap_bufsize = dump_bitmap_get_bufsize(s); + size_t bits_per_buf = bitmap_bufsize * CHAR_BIT; /* dump_bitmap_buf is used to store dump_bitmap temporarily */ - dump_bitmap_buf = g_malloc0(BUFSIZE_BITMAP); + dump_bitmap_buf = g_malloc0(bitmap_bufsize); num_dumpable = 0; last_pfn = 0; @@ -1064,7 +1077,7 @@ static void write_dump_bitmap(DumpState *s, Error **errp) while (get_next_page(&block_iter, &pfn, NULL, s)) { ret = set_dump_bitmap(last_pfn, pfn, true, dump_bitmap_buf, s); if (ret < 0) { - dump_error(s, "dump: failed to set dump_bitmap", errp); + error_setg(errp, "dump: failed to set dump_bitmap"); goto out; } @@ -1074,14 +1087,14 @@ static void write_dump_bitmap(DumpState *s, Error **errp) /* * set_dump_bitmap will always leave the recently set bit un-sync. Here we - * set last_pfn + PFN_BUFBITMAP to 0 and those set but un-sync bit will be - * synchronized into vmcore. + * set the remaining bits from last_pfn to the end of the bitmap buffer to + * 0. With those set, the un-sync bit will be synchronized into the vmcore. */ if (num_dumpable > 0) { - ret = set_dump_bitmap(last_pfn, last_pfn + PFN_BUFBITMAP, false, + ret = set_dump_bitmap(last_pfn, last_pfn + bits_per_buf, false, dump_bitmap_buf, s); if (ret < 0) { - dump_error(s, "dump: failed to sync dump_bitmap", errp); + error_setg(errp, "dump: failed to sync dump_bitmap"); goto out; } } @@ -1098,8 +1111,8 @@ static void prepare_data_cache(DataCache *data_cache, DumpState *s, { data_cache->fd = s->fd; data_cache->data_size = 0; - data_cache->buf_size = BUFSIZE_DATA_CACHE; - data_cache->buf = g_malloc0(BUFSIZE_DATA_CACHE); + data_cache->buf_size = 4 * dump_bitmap_get_bufsize(s); + data_cache->buf = g_malloc0(data_cache->buf_size); data_cache->offset = offset; } @@ -1193,7 +1206,7 @@ static void write_dump_pages(DumpState *s, Error **errp) prepare_data_cache(&page_data, s, offset_data); /* prepare buffer to store compressed data */ - len_buf_out = get_len_buf_out(TARGET_PAGE_SIZE, s->flag_compress); + len_buf_out = get_len_buf_out(s->dump_info.page_size, s->flag_compress); assert(len_buf_out != 0); #ifdef CONFIG_LZO @@ -1206,19 +1219,19 @@ static void write_dump_pages(DumpState *s, Error **errp) * init zero page's page_desc and page_data, because every zero page * uses the same page_data */ - pd_zero.size = cpu_to_dump32(s, TARGET_PAGE_SIZE); + pd_zero.size = cpu_to_dump32(s, s->dump_info.page_size); pd_zero.flags = cpu_to_dump32(s, 0); pd_zero.offset = cpu_to_dump64(s, offset_data); pd_zero.page_flags = cpu_to_dump64(s, 0); - buf = g_malloc0(TARGET_PAGE_SIZE); - ret = write_cache(&page_data, buf, TARGET_PAGE_SIZE, false); + buf = g_malloc0(s->dump_info.page_size); + ret = write_cache(&page_data, buf, s->dump_info.page_size, false); g_free(buf); if (ret < 0) { - dump_error(s, "dump: failed to write page data (zero page)", errp); + error_setg(errp, "dump: failed to write page data (zero page)"); goto out; } - offset_data += TARGET_PAGE_SIZE; + offset_data += s->dump_info.page_size; /* * dump memory to vmcore page by page. zero page will all be resided in the @@ -1226,11 +1239,11 @@ static void write_dump_pages(DumpState *s, Error **errp) */ while (get_next_page(&block_iter, &pfn_iter, &buf, s)) { /* check zero page */ - if (is_zero_page(buf, TARGET_PAGE_SIZE)) { + if (is_zero_page(buf, s->dump_info.page_size)) { ret = write_cache(&page_desc, &pd_zero, sizeof(PageDescriptor), false); if (ret < 0) { - dump_error(s, "dump: failed to write page desc", errp); + error_setg(errp, "dump: failed to write page desc"); goto out; } } else { @@ -1248,56 +1261,57 @@ static void write_dump_pages(DumpState *s, Error **errp) size_out = len_buf_out; if ((s->flag_compress & DUMP_DH_COMPRESSED_ZLIB) && (compress2(buf_out, (uLongf *)&size_out, buf, - TARGET_PAGE_SIZE, Z_BEST_SPEED) == Z_OK) && - (size_out < TARGET_PAGE_SIZE)) { + s->dump_info.page_size, Z_BEST_SPEED) == Z_OK) && + (size_out < s->dump_info.page_size)) { pd.flags = cpu_to_dump32(s, DUMP_DH_COMPRESSED_ZLIB); pd.size = cpu_to_dump32(s, size_out); ret = write_cache(&page_data, buf_out, size_out, false); if (ret < 0) { - dump_error(s, "dump: failed to write page data", errp); + error_setg(errp, "dump: failed to write page data"); goto out; } #ifdef CONFIG_LZO } else if ((s->flag_compress & DUMP_DH_COMPRESSED_LZO) && - (lzo1x_1_compress(buf, TARGET_PAGE_SIZE, buf_out, + (lzo1x_1_compress(buf, s->dump_info.page_size, buf_out, (lzo_uint *)&size_out, wrkmem) == LZO_E_OK) && - (size_out < TARGET_PAGE_SIZE)) { + (size_out < s->dump_info.page_size)) { pd.flags = cpu_to_dump32(s, DUMP_DH_COMPRESSED_LZO); pd.size = cpu_to_dump32(s, size_out); ret = write_cache(&page_data, buf_out, size_out, false); if (ret < 0) { - dump_error(s, "dump: failed to write page data", errp); + error_setg(errp, "dump: failed to write page data"); goto out; } #endif #ifdef CONFIG_SNAPPY } else if ((s->flag_compress & DUMP_DH_COMPRESSED_SNAPPY) && - (snappy_compress((char *)buf, TARGET_PAGE_SIZE, + (snappy_compress((char *)buf, s->dump_info.page_size, (char *)buf_out, &size_out) == SNAPPY_OK) && - (size_out < TARGET_PAGE_SIZE)) { + (size_out < s->dump_info.page_size)) { pd.flags = cpu_to_dump32(s, DUMP_DH_COMPRESSED_SNAPPY); pd.size = cpu_to_dump32(s, size_out); ret = write_cache(&page_data, buf_out, size_out, false); if (ret < 0) { - dump_error(s, "dump: failed to write page data", errp); + error_setg(errp, "dump: failed to write page data"); goto out; } #endif } else { /* * fall back to save in plaintext, size_out should be - * assigned TARGET_PAGE_SIZE + * assigned the target's page size */ pd.flags = cpu_to_dump32(s, 0); - size_out = TARGET_PAGE_SIZE; + size_out = s->dump_info.page_size; pd.size = cpu_to_dump32(s, size_out); - ret = write_cache(&page_data, buf, TARGET_PAGE_SIZE, false); + ret = write_cache(&page_data, buf, + s->dump_info.page_size, false); if (ret < 0) { - dump_error(s, "dump: failed to write page data", errp); + error_setg(errp, "dump: failed to write page data"); goto out; } } @@ -1309,20 +1323,21 @@ static void write_dump_pages(DumpState *s, Error **errp) ret = write_cache(&page_desc, &pd, sizeof(PageDescriptor), false); if (ret < 0) { - dump_error(s, "dump: failed to write page desc", errp); + error_setg(errp, "dump: failed to write page desc"); goto out; } } + s->written_size += s->dump_info.page_size; } ret = write_cache(&page_desc, NULL, 0, true); if (ret < 0) { - dump_error(s, "dump: failed to sync cache for page_desc", errp); + error_setg(errp, "dump: failed to sync cache for page_desc"); goto out; } ret = write_cache(&page_data, NULL, 0, true); if (ret < 0) { - dump_error(s, "dump: failed to sync cache for page_data", errp); + error_setg(errp, "dump: failed to sync cache for page_data"); goto out; } @@ -1366,7 +1381,7 @@ static void create_kdump_vmcore(DumpState *s, Error **errp) ret = write_start_flat_header(s->fd); if (ret < 0) { - dump_error(s, "dump: failed to write start flat header", errp); + error_setg(errp, "dump: failed to write start flat header"); return; } @@ -1390,11 +1405,9 @@ static void create_kdump_vmcore(DumpState *s, Error **errp) ret = write_end_flat_header(s->fd); if (ret < 0) { - dump_error(s, "dump: failed to write end flat header", errp); + error_setg(errp, "dump: failed to write end flat header"); return; } - - dump_completed(s); } static ram_addr_t get_start_block(DumpState *s) @@ -1430,7 +1443,45 @@ static void get_max_mapnr(DumpState *s) GuestPhysBlock *last_block; last_block = QTAILQ_LAST(&s->guest_phys_blocks.head, GuestPhysBlockHead); - s->max_mapnr = paddr_to_pfn(last_block->target_end); + s->max_mapnr = dump_paddr_to_pfn(s, last_block->target_end); +} + +static DumpState dump_state_global = { .status = DUMP_STATUS_NONE }; + +static void dump_state_prepare(DumpState *s) +{ + /* zero the struct, setting status to active */ + *s = (DumpState) { .status = DUMP_STATUS_ACTIVE }; +} + +bool dump_in_progress(void) +{ + DumpState *state = &dump_state_global; + return (atomic_read(&state->status) == DUMP_STATUS_ACTIVE); +} + +/* calculate total size of memory to be dumped (taking filter into + * acoount.) */ +static int64_t dump_calculate_size(DumpState *s) +{ + GuestPhysBlock *block; + int64_t size = 0, total = 0, left = 0, right = 0; + + QTAILQ_FOREACH(block, &s->guest_phys_blocks.head, next) { + if (s->has_filter) { + /* calculate the overlapped region. */ + left = MAX(s->begin, block->target_start); + right = MIN(s->begin + s->length, block->target_end); + size = right - left; + size = size > 0 ? size : 0; + } else { + /* count the whole region in */ + size = (block->target_end - block->target_start); + } + total += size; + } + + return total; } static void dump_init(DumpState *s, int fd, bool has_format, @@ -1442,6 +1493,10 @@ static void dump_init(DumpState *s, int fd, bool has_format, Error *err = NULL; int ret; + s->has_format = has_format; + s->format = format; + s->written_size = 0; + /* kdump-compressed is conflict with paging and filter */ if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) { assert(!paging && !has_filter); @@ -1472,6 +1527,10 @@ static void dump_init(DumpState *s, int fd, bool has_format, guest_phys_blocks_init(&s->guest_phys_blocks); guest_phys_blocks_append(&s->guest_phys_blocks); + s->total_size = dump_calculate_size(s); +#ifdef DEBUG_DUMP_GUEST_MEMORY + fprintf(stderr, "DUMP: total memory to dump: %lu\n", s->total_size); +#endif s->start = get_start_block(s); if (s->start == -1) { @@ -1489,6 +1548,10 @@ static void dump_init(DumpState *s, int fd, bool has_format, goto cleanup; } + if (!s->dump_info.page_size) { + s->dump_info.page_size = TARGET_PAGE_SIZE; + } + s->note_size = cpu_get_note_size(s->dump_info.d_class, s->dump_info.d_machine, nr_cpus); if (s->note_size < 0) { @@ -1512,8 +1575,9 @@ static void dump_init(DumpState *s, int fd, bool has_format, get_max_mapnr(s); uint64_t tmp; - tmp = DIV_ROUND_UP(DIV_ROUND_UP(s->max_mapnr, CHAR_BIT), TARGET_PAGE_SIZE); - s->len_dump_bitmap = tmp * TARGET_PAGE_SIZE; + tmp = DIV_ROUND_UP(DIV_ROUND_UP(s->max_mapnr, CHAR_BIT), + s->dump_info.page_size); + s->len_dump_bitmap = tmp * s->dump_info.page_size; /* init for kdump-compressed format */ if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) { @@ -1595,8 +1659,60 @@ cleanup: dump_cleanup(s); } -void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin, - int64_t begin, bool has_length, +/* this operation might be time consuming. */ +static void dump_process(DumpState *s, Error **errp) +{ + Error *local_err = NULL; + DumpQueryResult *result = NULL; + + if (s->has_format && s->format != DUMP_GUEST_MEMORY_FORMAT_ELF) { + create_kdump_vmcore(s, &local_err); + } else { + create_vmcore(s, &local_err); + } + + /* make sure status is written after written_size updates */ + smp_wmb(); + atomic_set(&s->status, + (local_err ? DUMP_STATUS_FAILED : DUMP_STATUS_COMPLETED)); + + /* send DUMP_COMPLETED message (unconditionally) */ + result = qmp_query_dump(NULL); + /* should never fail */ + assert(result); + qapi_event_send_dump_completed(result, !!local_err, (local_err ? \ + error_get_pretty(local_err) : NULL), + &error_abort); + qapi_free_DumpQueryResult(result); + + error_propagate(errp, local_err); + dump_cleanup(s); +} + +static void *dump_thread(void *data) +{ + Error *err = NULL; + DumpState *s = (DumpState *)data; + dump_process(s, &err); + error_free(err); + return NULL; +} + +DumpQueryResult *qmp_query_dump(Error **errp) +{ + DumpQueryResult *result = g_new(DumpQueryResult, 1); + DumpState *state = &dump_state_global; + result->status = atomic_read(&state->status); + /* make sure we are reading status and written_size in order */ + smp_rmb(); + result->completed = state->written_size; + result->total = state->total_size; + return result; +} + +void qmp_dump_guest_memory(bool paging, const char *file, + bool has_detach, bool detach, + bool has_begin, int64_t begin, bool has_length, int64_t length, bool has_format, DumpGuestMemoryFormat format, Error **errp) { @@ -1604,6 +1720,19 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin, int fd = -1; DumpState *s; Error *local_err = NULL; + bool detach_p = false; + + if (runstate_check(RUN_STATE_INMIGRATE)) { + error_setg(errp, "Dump not allowed during incoming migration."); + return; + } + + /* if there is a dump in background, we should wait until the dump + * finished */ + if (dump_in_progress()) { + error_setg(errp, "There is a dump in process, please wait."); + return; + } /* * kdump-compressed format need the whole memory dumped, so paging or @@ -1623,6 +1752,9 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin, error_setg(errp, QERR_MISSING_PARAMETER, "begin"); return; } + if (has_detach) { + detach_p = detach; + } /* check whether lzo/snappy is supported */ #ifndef CONFIG_LZO @@ -1661,23 +1793,25 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin, return; } - s = g_malloc0(sizeof(DumpState)); + s = &dump_state_global; + dump_state_prepare(s); dump_init(s, fd, has_format, format, paging, has_begin, begin, length, &local_err); if (local_err) { - g_free(s); error_propagate(errp, local_err); + atomic_set(&s->status, DUMP_STATUS_FAILED); return; } - if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) { - create_kdump_vmcore(s, errp); + if (detach_p) { + /* detached dump */ + qemu_thread_create(&s->dump_thread, "dump_thread", dump_thread, + s, QEMU_THREAD_DETACHED); } else { - create_vmcore(s, errp); + /* sync dump */ + dump_process(s, errp); } - - g_free(s); } DumpGuestMemoryCapability *qmp_query_dump_guest_memory_capability(Error **errp) diff --git a/qemu/exec.c b/qemu/exec.c index 0a4a0c5af..c4f903618 100644 --- a/qemu/exec.c +++ b/qemu/exec.c @@ -16,13 +16,13 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" +#include "qemu/osdep.h" +#include "qapi/error.h" #ifndef _WIN32 -#include #include #endif -#include "qemu-common.h" +#include "qemu/cutils.h" #include "cpu.h" #include "tcg.h" #include "hw/hw.h" @@ -30,7 +30,6 @@ #include "hw/boards.h" #endif #include "hw/qdev.h" -#include "qemu/osdep.h" #include "sysemu/kvm.h" #include "sysemu/sysemu.h" #include "hw/xen/xen.h" @@ -49,13 +48,17 @@ #include "exec/cpu-all.h" #include "qemu/rcu_queue.h" #include "qemu/main-loop.h" -#include "exec/cputlb.h" #include "translate-all.h" +#include "sysemu/replay.h" #include "exec/memory-internal.h" #include "exec/ram_addr.h" +#include "exec/log.h" #include "qemu/range.h" +#ifndef _WIN32 +#include "qemu/mmap-alloc.h" +#endif //#define DEBUG_SUBPAGE @@ -90,7 +93,7 @@ static MemoryRegion io_mem_unassigned; struct CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus); /* current CPU in the current thread. It is only valid inside cpu_exec() */ -DEFINE_TLS(CPUState *, current_cpu); +__thread CPUState *current_cpu; /* 0 = Do not count executed instructions. 1 = Precise instruction counting. 2 = Adaptive rate instruction counting. */ @@ -133,6 +136,7 @@ typedef struct PhysPageMap { struct AddressSpaceDispatch { struct rcu_head rcu; + MemoryRegionSection *mru_section; /* This is a multi-level map on the physical address space. * The bottom level has pointers to MemoryRegionSections. */ @@ -159,6 +163,21 @@ static void memory_map_init(void); static void tcg_commit(MemoryListener *listener); static MemoryRegion io_mem_watch; + +/** + * CPUAddressSpace: all the information a CPU needs about an AddressSpace + * @cpu: the CPU whose AddressSpace this is + * @as: the AddressSpace itself + * @memory_dispatch: its dispatch pointer (cached, RCU protected) + * @tcg_as_listener: listener for tracking changes to the AddressSpace + */ +struct CPUAddressSpace { + CPUState *cpu; + AddressSpace *as; + struct AddressSpaceDispatch *memory_dispatch; + MemoryListener tcg_as_listener; +}; + #endif #if !defined(CONFIG_USER_ONLY) @@ -290,6 +309,17 @@ static void phys_page_compact_all(AddressSpaceDispatch *d, int nodes_nb) } } +static inline bool section_covers_addr(const MemoryRegionSection *section, + hwaddr addr) +{ + /* Memory topology clips a memory region to [0, 2^64); size.hi > 0 means + * the section must cover the entire address space. + */ + return section->size.hi || + range_covers_byte(section->offset_within_address_space, + section->size.lo, addr); +} + static MemoryRegionSection *phys_page_find(PhysPageEntry lp, hwaddr addr, Node *nodes, MemoryRegionSection *sections) { @@ -305,9 +335,7 @@ static MemoryRegionSection *phys_page_find(PhysPageEntry lp, hwaddr addr, lp = p[(index >> (i * P_L2_BITS)) & (P_L2_SIZE - 1)]; } - if (sections[lp.ptr].size.hi || - range_covers_byte(sections[lp.ptr].offset_within_address_space, - sections[lp.ptr].size.lo, addr)) { + if (section_covers_addr(§ions[lp.ptr], addr)) { return §ions[lp.ptr]; } else { return §ions[PHYS_SECTION_UNASSIGNED]; @@ -325,14 +353,25 @@ static MemoryRegionSection *address_space_lookup_region(AddressSpaceDispatch *d, hwaddr addr, bool resolve_subpage) { - MemoryRegionSection *section; + MemoryRegionSection *section = atomic_read(&d->mru_section); subpage_t *subpage; + bool update; - section = phys_page_find(d->phys_map, addr, d->map.nodes, d->map.sections); + if (section && section != &d->map.sections[PHYS_SECTION_UNASSIGNED] && + section_covers_addr(section, addr)) { + update = false; + } else { + section = phys_page_find(d->phys_map, addr, d->map.nodes, + d->map.sections); + update = true; + } if (resolve_subpage && section->mr->subpage) { subpage = container_of(section->mr, subpage_t, iomem); section = &d->map.sections[subpage->sub_section[SUBPAGE_IDX(addr)]]; } + if (update) { + atomic_set(&d->mru_section, section); + } return section; } @@ -372,18 +411,6 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x return section; } -static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) -{ - if (memory_region_is_ram(mr)) { - return !(is_write && mr->readonly); - } - if (memory_region_is_romd(mr)) { - return !is_write; - } - - return false; -} - /* Called from RCU critical section */ MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr, hwaddr *xlat, hwaddr *plen, @@ -425,12 +452,13 @@ MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr, /* Called from RCU critical section */ MemoryRegionSection * -address_space_translate_for_iotlb(CPUState *cpu, hwaddr addr, +address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr, hwaddr *xlat, hwaddr *plen) { MemoryRegionSection *section; - section = address_space_translate_internal(cpu->memory_dispatch, - addr, xlat, plen, false); + AddressSpaceDispatch *d = cpu->cpu_ases[asidx].memory_dispatch; + + section = address_space_translate_internal(d, addr, xlat, plen, false); assert(!section->mr->iommu_ops); return section; @@ -478,6 +506,24 @@ static const VMStateDescription vmstate_cpu_common_exception_index = { } }; +static bool cpu_common_crash_occurred_needed(void *opaque) +{ + CPUState *cpu = opaque; + + return cpu->crash_occurred; +} + +static const VMStateDescription vmstate_cpu_common_crash_occurred = { + .name = "cpu_common/crash_occurred", + .version_id = 1, + .minimum_version_id = 1, + .needed = cpu_common_crash_occurred_needed, + .fields = (VMStateField[]) { + VMSTATE_BOOL(crash_occurred, CPUState), + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_cpu_common = { .name = "cpu_common", .version_id = 1, @@ -491,6 +537,7 @@ const VMStateDescription vmstate_cpu_common = { }, .subsections = (const VMStateDescription*[]) { &vmstate_cpu_common_exception_index, + &vmstate_cpu_common_crash_occurred, NULL } }; @@ -511,18 +558,38 @@ CPUState *qemu_get_cpu(int index) } #if !defined(CONFIG_USER_ONLY) -void tcg_cpu_address_space_init(CPUState *cpu, AddressSpace *as) +void cpu_address_space_init(CPUState *cpu, AddressSpace *as, int asidx) { - /* We only support one address space per cpu at the moment. */ - assert(cpu->as == as); + CPUAddressSpace *newas; - if (cpu->tcg_as_listener) { - memory_listener_unregister(cpu->tcg_as_listener); - } else { - cpu->tcg_as_listener = g_new0(MemoryListener, 1); + /* Target code should have set num_ases before calling us */ + assert(asidx < cpu->num_ases); + + if (asidx == 0) { + /* address space 0 gets the convenience alias */ + cpu->as = as; + } + + /* KVM cannot currently support multiple address spaces. */ + assert(asidx == 0 || !kvm_enabled()); + + if (!cpu->cpu_ases) { + cpu->cpu_ases = g_new0(CPUAddressSpace, cpu->num_ases); + } + + newas = &cpu->cpu_ases[asidx]; + newas->cpu = cpu; + newas->as = as; + if (tcg_enabled()) { + newas->tcg_as_listener.commit = tcg_commit; + memory_listener_register(&newas->tcg_as_listener, as); } - cpu->tcg_as_listener->commit = tcg_commit; - memory_listener_register(cpu->tcg_as_listener, as); +} + +AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx) +{ + /* Return the AddressSpace corresponding to the specified index */ + return cpu->cpu_ases[asidx].as; } #endif @@ -577,10 +644,25 @@ void cpu_exec_init(CPUState *cpu, Error **errp) int cpu_index; Error *local_err = NULL; + cpu->as = NULL; + cpu->num_ases = 0; + #ifndef CONFIG_USER_ONLY - cpu->as = &address_space_memory; cpu->thread_id = qemu_get_thread_id(); - cpu_reload_memory_map(cpu); + + /* This is a softmmu CPU object, so create a property for it + * so users can wire up its memory. (This can't go in qom/cpu.c + * because that file is compiled only once for both user-mode + * and system builds.) The default if no link is set up is to use + * the system address space. + */ + object_property_add_link(OBJECT(cpu), "memory", TYPE_MEMORY_REGION, + (Object **)&cpu->memory, + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + &error_abort); + cpu->memory = system_memory; + object_ref(OBJECT(cpu->memory)); #endif #if defined(CONFIG_USER_ONLY) @@ -601,12 +683,6 @@ void cpu_exec_init(CPUState *cpu, Error **errp) if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { vmstate_register(NULL, cpu_index, &vmstate_cpu_common, cpu); } -#if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY) - register_savevm(NULL, "cpu", cpu_index, CPU_SAVE_VERSION, - cpu_save, cpu_load, cpu->env_ptr); - assert(cc->vmsd == NULL); - assert(qdev_get_vmsd(DEVICE(cpu)) == NULL); -#endif if (cc->vmsd != NULL) { vmstate_register(NULL, cpu_index, cc->vmsd, cpu); } @@ -620,9 +696,11 @@ static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) #else static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) { - hwaddr phys = cpu_get_phys_page_debug(cpu, pc); + MemTxAttrs attrs; + hwaddr phys = cpu_get_phys_page_attrs_debug(cpu, pc, &attrs); + int asidx = cpu_asidx_from_attrs(cpu, attrs); if (phys != -1) { - tb_invalidate_phys_addr(cpu->as, + tb_invalidate_phys_addr(cpu->cpu_ases[asidx].as, phys | (pc & ~TARGET_PAGE_MASK)); } } @@ -831,7 +909,7 @@ void cpu_abort(CPUState *cpu, const char *fmt, ...) vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); cpu_dump_state(cpu, stderr, fprintf, CPU_DUMP_FPU | CPU_DUMP_CCOP); - if (qemu_log_enabled()) { + if (qemu_log_separate()) { qemu_log("qemu: fatal: "); qemu_log_vprintf(fmt, ap2); qemu_log("\n"); @@ -841,6 +919,7 @@ void cpu_abort(CPUState *cpu, const char *fmt, ...) } va_end(ap2); va_end(ap); + replay_finish(); #if defined(CONFIG_USER_ONLY) { struct sigaction act; @@ -860,7 +939,7 @@ static RAMBlock *qemu_get_ram_block(ram_addr_t addr) block = atomic_rcu_read(&ram_list.mru_block); if (block && addr - block->offset < block->max_length) { - goto found; + return block; } QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { if (addr - block->offset < block->max_length) { @@ -894,6 +973,7 @@ found: static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length) { + CPUState *cpu; ram_addr_t start1; RAMBlock *block; ram_addr_t end; @@ -905,7 +985,9 @@ static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length) block = qemu_get_ram_block(start); assert(block == qemu_get_ram_block(end - 1)); start1 = (uintptr_t)ramblock_ptr(block, start - block->offset); - cpu_tlb_reset_dirty_all(start1, length); + CPU_FOREACH(cpu) { + tlb_reset_dirty(cpu, start1, length); + } rcu_read_unlock(); } @@ -914,8 +996,9 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, ram_addr_t length, unsigned client) { + DirtyMemoryBlocks *blocks; unsigned long end, page; - bool dirty; + bool dirty = false; if (length == 0) { return false; @@ -923,8 +1006,22 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; page = start >> TARGET_PAGE_BITS; - dirty = bitmap_test_and_clear_atomic(ram_list.dirty_memory[client], - page, end - page); + + rcu_read_lock(); + + blocks = atomic_rcu_read(&ram_list.dirty_memory[client]); + + while (page < end) { + unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE; + unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE; + unsigned long num = MIN(end - page, DIRTY_MEMORY_BLOCK_SIZE - offset); + + dirty |= bitmap_test_and_clear_atomic(blocks->blocks[idx], + offset, num); + page += num; + } + + rcu_read_unlock(); if (dirty && tcg_enabled()) { tlb_reset_dirty_range_all(start, length); @@ -1018,9 +1115,11 @@ static uint16_t phys_section_add(PhysPageMap *map, static void phys_section_destroy(MemoryRegion *mr) { + bool have_sub_page = mr->subpage; + memory_region_unref(mr); - if (mr->subpage) { + if (have_sub_page) { subpage_t *subpage = container_of(mr, subpage_t, iomem); object_unref(OBJECT(&subpage->iomem)); g_free(subpage); @@ -1130,87 +1229,83 @@ void qemu_mutex_unlock_ramlist(void) } #ifdef __linux__ - -#include - -#define HUGETLBFS_MAGIC 0x958458f6 - -static long gethugepagesize(const char *path, Error **errp) -{ - struct statfs fs; - int ret; - - do { - ret = statfs(path, &fs); - } while (ret != 0 && errno == EINTR); - - if (ret != 0) { - error_setg_errno(errp, errno, "failed to get page size of file %s", - path); - return 0; - } - - if (fs.f_type != HUGETLBFS_MAGIC) - fprintf(stderr, "Warning: path not on HugeTLBFS: %s\n", path); - - return fs.f_bsize; -} - static void *file_ram_alloc(RAMBlock *block, ram_addr_t memory, const char *path, Error **errp) { + bool unlink_on_error = false; char *filename; char *sanitized_name; char *c; - void *area = NULL; - int fd; - uint64_t hpagesize; - Error *local_err = NULL; - - hpagesize = gethugepagesize(path, &local_err); - if (local_err) { - error_propagate(errp, local_err); - goto error; - } - block->mr->align = hpagesize; - - if (memory < hpagesize) { - error_setg(errp, "memory size 0x" RAM_ADDR_FMT " must be equal to " - "or larger than huge page size 0x%" PRIx64, - memory, hpagesize); - goto error; - } + void *area; + int fd = -1; + int64_t page_size; if (kvm_enabled() && !kvm_has_sync_mmu()) { error_setg(errp, "host lacks kvm mmu notifiers, -mem-path unsupported"); - goto error; + return NULL; } - /* Make name safe to use with mkstemp by replacing '/' with '_'. */ - sanitized_name = g_strdup(memory_region_name(block->mr)); - for (c = sanitized_name; *c != '\0'; c++) { - if (*c == '/') - *c = '_'; + for (;;) { + fd = open(path, O_RDWR); + if (fd >= 0) { + /* @path names an existing file, use it */ + break; + } + if (errno == ENOENT) { + /* @path names a file that doesn't exist, create it */ + fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0644); + if (fd >= 0) { + unlink_on_error = true; + break; + } + } else if (errno == EISDIR) { + /* @path names a directory, create a file there */ + /* Make name safe to use with mkstemp by replacing '/' with '_'. */ + sanitized_name = g_strdup(memory_region_name(block->mr)); + for (c = sanitized_name; *c != '\0'; c++) { + if (*c == '/') { + *c = '_'; + } + } + + filename = g_strdup_printf("%s/qemu_back_mem.%s.XXXXXX", path, + sanitized_name); + g_free(sanitized_name); + + fd = mkstemp(filename); + if (fd >= 0) { + unlink(filename); + g_free(filename); + break; + } + g_free(filename); + } + if (errno != EEXIST && errno != EINTR) { + error_setg_errno(errp, errno, + "can't open backing store %s for guest RAM", + path); + goto error; + } + /* + * Try again on EINTR and EEXIST. The latter happens when + * something else creates the file between our two open(). + */ } - filename = g_strdup_printf("%s/qemu_back_mem.%s.XXXXXX", path, - sanitized_name); - g_free(sanitized_name); + page_size = qemu_fd_getpagesize(fd); + block->mr->align = page_size; - fd = mkstemp(filename); - if (fd < 0) { - error_setg_errno(errp, errno, - "unable to create backing store for hugepages"); - g_free(filename); + if (memory < page_size) { + error_setg(errp, "memory size 0x" RAM_ADDR_FMT " must be equal to " + "or larger than page size 0x%" PRIx64, + memory, page_size); goto error; } - unlink(filename); - g_free(filename); - memory = (memory+hpagesize-1) & ~(hpagesize-1); + memory = ROUND_UP(memory, page_size); /* * ftruncate is not supported by hugetlbfs in older @@ -1222,13 +1317,10 @@ static void *file_ram_alloc(RAMBlock *block, perror("ftruncate"); } - area = mmap(0, memory, PROT_READ | PROT_WRITE, - (block->flags & RAM_SHARED ? MAP_SHARED : MAP_PRIVATE), - fd, 0); + area = qemu_ram_mmap(fd, memory, page_size, block->flags & RAM_SHARED); if (area == MAP_FAILED) { error_setg_errno(errp, errno, - "unable to map backing store for hugepages"); - close(fd); + "unable to map backing store for guest RAM"); goto error; } @@ -1240,9 +1332,11 @@ static void *file_ram_alloc(RAMBlock *block, return area; error: - if (mem_prealloc) { - error_report("%s", error_get_pretty(*errp)); - exit(1); + if (unlink_on_error) { + unlink(path); + } + if (fd != -1) { + close(fd); } return NULL; } @@ -1329,6 +1423,11 @@ static RAMBlock *find_ram_block(ram_addr_t addr) return NULL; } +const char *qemu_ram_get_idstr(RAMBlock *rb) +{ + return rb->idstr; +} + /* Called with iothread lock held. */ void qemu_ram_set_idstr(ram_addr_t addr, const char *name, DeviceState *dev) { @@ -1399,7 +1498,7 @@ int qemu_ram_resize(ram_addr_t base, ram_addr_t newsize, Error **errp) assert(block); - newsize = TARGET_PAGE_ALIGN(newsize); + newsize = HOST_PAGE_ALIGN(newsize); if (block->used_length == newsize) { return 0; @@ -1432,11 +1531,53 @@ int qemu_ram_resize(ram_addr_t base, ram_addr_t newsize, Error **errp) return 0; } -static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp) +/* Called with ram_list.mutex held */ +static void dirty_memory_extend(ram_addr_t old_ram_size, + ram_addr_t new_ram_size) +{ + ram_addr_t old_num_blocks = DIV_ROUND_UP(old_ram_size, + DIRTY_MEMORY_BLOCK_SIZE); + ram_addr_t new_num_blocks = DIV_ROUND_UP(new_ram_size, + DIRTY_MEMORY_BLOCK_SIZE); + int i; + + /* Only need to extend if block count increased */ + if (new_num_blocks <= old_num_blocks) { + return; + } + + for (i = 0; i < DIRTY_MEMORY_NUM; i++) { + DirtyMemoryBlocks *old_blocks; + DirtyMemoryBlocks *new_blocks; + int j; + + old_blocks = atomic_rcu_read(&ram_list.dirty_memory[i]); + new_blocks = g_malloc(sizeof(*new_blocks) + + sizeof(new_blocks->blocks[0]) * new_num_blocks); + + if (old_num_blocks) { + memcpy(new_blocks->blocks, old_blocks->blocks, + old_num_blocks * sizeof(old_blocks->blocks[0])); + } + + for (j = old_num_blocks; j < new_num_blocks; j++) { + new_blocks->blocks[j] = bitmap_new(DIRTY_MEMORY_BLOCK_SIZE); + } + + atomic_rcu_set(&ram_list.dirty_memory[i], new_blocks); + + if (old_blocks) { + g_free_rcu(old_blocks, rcu); + } + } +} + +static void ram_block_add(RAMBlock *new_block, Error **errp) { RAMBlock *block; RAMBlock *last_block = NULL; ram_addr_t old_ram_size, new_ram_size; + Error *err = NULL; old_ram_size = last_ram_offset() >> TARGET_PAGE_BITS; @@ -1446,7 +1587,12 @@ static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp) if (!new_block->host) { if (xen_enabled()) { xen_ram_alloc(new_block->offset, new_block->max_length, - new_block->mr); + new_block->mr, &err); + if (err) { + error_propagate(errp, err); + qemu_mutex_unlock_ramlist(); + return; + } } else { new_block->host = phys_mem_alloc(new_block->max_length, &new_block->mr->align); @@ -1455,7 +1601,7 @@ static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp) "cannot set up guest memory '%s'", memory_region_name(new_block->mr)); qemu_mutex_unlock_ramlist(); - return -1; + return; } memory_try_enable_merging(new_block->host, new_block->max_length); } @@ -1465,6 +1611,7 @@ static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp) (new_block->offset + new_block->max_length) >> TARGET_PAGE_BITS); if (new_ram_size > old_ram_size) { migration_bitmap_extend(old_ram_size, new_ram_size); + dirty_memory_extend(old_ram_size, new_ram_size); } /* Keep the list sorted from biggest to smallest block. Unlike QTAILQ, * QLIST (which has an RCU-friendly variant) does not have insertion at @@ -1490,18 +1637,6 @@ static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp) ram_list.version++; qemu_mutex_unlock_ramlist(); - new_ram_size = last_ram_offset() >> TARGET_PAGE_BITS; - - if (new_ram_size > old_ram_size) { - int i; - - /* ram_list.dirty_memory[] is protected by the iothread lock. */ - for (i = 0; i < DIRTY_MEMORY_NUM; i++) { - ram_list.dirty_memory[i] = - bitmap_zero_extend(ram_list.dirty_memory[i], - old_ram_size, new_ram_size); - } - } cpu_physical_memory_set_dirty_range(new_block->offset, new_block->used_length, DIRTY_CLIENTS_ALL); @@ -1514,22 +1649,19 @@ static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp) kvm_setup_guest_memory(new_block->host, new_block->max_length); } } - - return new_block->offset; } #ifdef __linux__ -ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, - bool share, const char *mem_path, - Error **errp) +RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, + bool share, const char *mem_path, + Error **errp) { RAMBlock *new_block; - ram_addr_t addr; Error *local_err = NULL; if (xen_enabled()) { error_setg(errp, "-mem-path not supported with Xen"); - return -1; + return NULL; } if (phys_mem_alloc != qemu_anon_ram_alloc) { @@ -1540,10 +1672,10 @@ ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, */ error_setg(errp, "-mem-path not supported with this accelerator"); - return -1; + return NULL; } - size = TARGET_PAGE_ALIGN(size); + size = HOST_PAGE_ALIGN(size); new_block = g_malloc0(sizeof(*new_block)); new_block->mr = mr; new_block->used_length = size; @@ -1553,33 +1685,32 @@ ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, mem_path, errp); if (!new_block->host) { g_free(new_block); - return -1; + return NULL; } - addr = ram_block_add(new_block, &local_err); + ram_block_add(new_block, &local_err); if (local_err) { g_free(new_block); error_propagate(errp, local_err); - return -1; + return NULL; } - return addr; + return new_block; } #endif static -ram_addr_t qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, - void (*resized)(const char*, - uint64_t length, - void *host), - void *host, bool resizeable, - MemoryRegion *mr, Error **errp) +RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, + void (*resized)(const char*, + uint64_t length, + void *host), + void *host, bool resizeable, + MemoryRegion *mr, Error **errp) { RAMBlock *new_block; - ram_addr_t addr; Error *local_err = NULL; - size = TARGET_PAGE_ALIGN(size); - max_size = TARGET_PAGE_ALIGN(max_size); + size = HOST_PAGE_ALIGN(size); + max_size = HOST_PAGE_ALIGN(max_size); new_block = g_malloc0(sizeof(*new_block)); new_block->mr = mr; new_block->resized = resized; @@ -1594,27 +1725,27 @@ ram_addr_t qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, if (resizeable) { new_block->flags |= RAM_RESIZEABLE; } - addr = ram_block_add(new_block, &local_err); + ram_block_add(new_block, &local_err); if (local_err) { g_free(new_block); error_propagate(errp, local_err); - return -1; + return NULL; } - return addr; + return new_block; } -ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, +RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, MemoryRegion *mr, Error **errp) { return qemu_ram_alloc_internal(size, size, NULL, host, false, mr, errp); } -ram_addr_t qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr, Error **errp) +RAMBlock *qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr, Error **errp) { return qemu_ram_alloc_internal(size, size, NULL, NULL, false, mr, errp); } -ram_addr_t qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t maxsz, +RAMBlock *qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t maxsz, void (*resized)(const char*, uint64_t length, void *host), @@ -1623,25 +1754,6 @@ ram_addr_t qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t maxsz, return qemu_ram_alloc_internal(size, maxsz, resized, NULL, true, mr, errp); } -void qemu_ram_free_from_ptr(ram_addr_t addr) -{ - RAMBlock *block; - - qemu_mutex_lock_ramlist(); - QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { - if (addr == block->offset) { - QLIST_REMOVE_RCU(block, next); - ram_list.mru_block = NULL; - /* Write list before version */ - smp_wmb(); - ram_list.version++; - g_free_rcu(block, rcu); - break; - } - } - qemu_mutex_unlock_ramlist(); -} - static void reclaim_ramblock(RAMBlock *block) { if (block->flags & RAM_PREALLOC) { @@ -1650,7 +1762,7 @@ static void reclaim_ramblock(RAMBlock *block) xen_invalidate_map_cache_entry(block->host); #ifndef _WIN32 } else if (block->fd >= 0) { - munmap(block->host, block->max_length); + qemu_ram_munmap(block->host, block->max_length); close(block->fd); #endif } else { @@ -1659,22 +1771,19 @@ static void reclaim_ramblock(RAMBlock *block) g_free(block); } -void qemu_ram_free(ram_addr_t addr) +void qemu_ram_free(RAMBlock *block) { - RAMBlock *block; + if (!block) { + return; + } qemu_mutex_lock_ramlist(); - QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { - if (addr == block->offset) { - QLIST_REMOVE_RCU(block, next); - ram_list.mru_block = NULL; - /* Write list before version */ - smp_wmb(); - ram_list.version++; - call_rcu(block, reclaim_ramblock, rcu); - break; - } - } + QLIST_REMOVE_RCU(block, next); + ram_list.mru_block = NULL; + /* Write list before version */ + smp_wmb(); + ram_list.version++; + call_rcu(block, reclaim_ramblock, rcu); qemu_mutex_unlock_ramlist(); } @@ -1739,6 +1848,16 @@ int qemu_get_ram_fd(ram_addr_t addr) return fd; } +void qemu_set_ram_fd(ram_addr_t addr, int fd) +{ + RAMBlock *block; + + rcu_read_lock(); + block = qemu_get_ram_block(addr); + block->fd = fd; + rcu_read_unlock(); +} + void *qemu_get_ram_block_host_ptr(ram_addr_t addr) { RAMBlock *block; @@ -1756,19 +1875,15 @@ void *qemu_get_ram_block_host_ptr(ram_addr_t addr) * or address_space_rw instead. For local memory (e.g. video ram) that the * device owns, use memory_region_get_ram_ptr. * - * By the time this function returns, the returned pointer is not protected - * by RCU anymore. If the caller is not within an RCU critical section and - * does not hold the iothread lock, it must have other means of protecting the - * pointer, such as a reference to the region that includes the incoming - * ram_addr_t. + * Called within RCU critical section. */ -void *qemu_get_ram_ptr(ram_addr_t addr) +void *qemu_get_ram_ptr(RAMBlock *ram_block, ram_addr_t addr) { - RAMBlock *block; - void *ptr; + RAMBlock *block = ram_block; - rcu_read_lock(); - block = qemu_get_ram_block(addr); + if (block == NULL) { + block = qemu_get_ram_block(addr); + } if (xen_enabled() && block->host == NULL) { /* We need to check if the requested address is in the RAM @@ -1776,56 +1891,59 @@ void *qemu_get_ram_ptr(ram_addr_t addr) * In that case just map until the end of the page. */ if (block->offset == 0) { - ptr = xen_map_cache(addr, 0, 0); - goto unlock; + return xen_map_cache(addr, 0, 0); } block->host = xen_map_cache(block->offset, block->max_length, 1); } - ptr = ramblock_ptr(block, addr - block->offset); - -unlock: - rcu_read_unlock(); - return ptr; + return ramblock_ptr(block, addr - block->offset); } /* Return a host pointer to guest's ram. Similar to qemu_get_ram_ptr * but takes a size argument. * - * By the time this function returns, the returned pointer is not protected - * by RCU anymore. If the caller is not within an RCU critical section and - * does not hold the iothread lock, it must have other means of protecting the - * pointer, such as a reference to the region that includes the incoming - * ram_addr_t. + * Called within RCU critical section. */ -static void *qemu_ram_ptr_length(ram_addr_t addr, hwaddr *size) +static void *qemu_ram_ptr_length(RAMBlock *ram_block, ram_addr_t addr, + hwaddr *size) { - void *ptr; + RAMBlock *block = ram_block; + ram_addr_t offset_inside_block; if (*size == 0) { return NULL; } - if (xen_enabled()) { - return xen_map_cache(addr, *size, 1); - } else { - RAMBlock *block; - rcu_read_lock(); - QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { - if (addr - block->offset < block->max_length) { - if (addr - block->offset + *size > block->max_length) - *size = block->max_length - addr + block->offset; - ptr = ramblock_ptr(block, addr - block->offset); - rcu_read_unlock(); - return ptr; - } + + if (block == NULL) { + block = qemu_get_ram_block(addr); + } + offset_inside_block = addr - block->offset; + *size = MIN(*size, block->max_length - offset_inside_block); + + if (xen_enabled() && block->host == NULL) { + /* We need to check if the requested address is in the RAM + * because we don't want to map the entire memory in QEMU. + * In that case just map the requested area. + */ + if (block->offset == 0) { + return xen_map_cache(addr, *size, 1); } - fprintf(stderr, "Bad ram offset %" PRIx64 "\n", (uint64_t)addr); - abort(); + block->host = xen_map_cache(block->offset, block->max_length, 1); } + + return ramblock_ptr(block, offset_inside_block); } -/* Some of the softmmu routines need to translate from a host pointer - * (typically a TLB entry) back to a ram offset. +/* + * Translates a host ptr back to a RAMBlock, a ram_addr and an offset + * in that RAMBlock. + * + * ptr: Host pointer to look up + * round_offset: If true round the result offset down to a page boundary + * *ram_addr: set to result ram_addr + * *offset: set to result offset within the RAMBlock + * + * Returns: RAMBlock (or NULL if not found) * * By the time this function returns, the returned pointer is not protected * by RCU anymore. If the caller is not within an RCU critical section and @@ -1833,18 +1951,22 @@ static void *qemu_ram_ptr_length(ram_addr_t addr, hwaddr *size) * pointer, such as a reference to the region that includes the incoming * ram_addr_t. */ -MemoryRegion *qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr) +RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset, + ram_addr_t *ram_addr, + ram_addr_t *offset) { RAMBlock *block; uint8_t *host = ptr; - MemoryRegion *mr; if (xen_enabled()) { rcu_read_lock(); *ram_addr = xen_ram_addr_from_mapcache(ptr); - mr = qemu_get_ram_block(*ram_addr)->mr; + block = qemu_get_ram_block(*ram_addr); + if (block) { + *offset = (host - block->host); + } rcu_read_unlock(); - return mr; + return block; } rcu_read_lock(); @@ -1867,12 +1989,52 @@ MemoryRegion *qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr) return NULL; found: - *ram_addr = block->offset + (host - block->host); - mr = block->mr; + *offset = (host - block->host); + if (round_offset) { + *offset &= TARGET_PAGE_MASK; + } + *ram_addr = block->offset + *offset; rcu_read_unlock(); - return mr; + return block; +} + +/* + * Finds the named RAMBlock + * + * name: The name of RAMBlock to find + * + * Returns: RAMBlock (or NULL if not found) + */ +RAMBlock *qemu_ram_block_by_name(const char *name) +{ + RAMBlock *block; + + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + if (!strcmp(name, block->idstr)) { + return block; + } + } + + return NULL; +} + +/* Some of the softmmu routines need to translate from a host pointer + (typically a TLB entry) back to a ram offset. */ +MemoryRegion *qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr) +{ + RAMBlock *block; + ram_addr_t offset; /* Not used */ + + block = qemu_ram_block_from_host(ptr, false, ram_addr, &offset); + + if (!block) { + return NULL; + } + + return block->mr; } +/* Called within RCU critical section. */ static void notdirty_mem_write(void *opaque, hwaddr ram_addr, uint64_t val, unsigned size) { @@ -1881,13 +2043,13 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr, } switch (size) { case 1: - stb_p(qemu_get_ram_ptr(ram_addr), val); + stb_p(qemu_get_ram_ptr(NULL, ram_addr), val); break; case 2: - stw_p(qemu_get_ram_ptr(ram_addr), val); + stw_p(qemu_get_ram_ptr(NULL, ram_addr), val); break; case 4: - stl_p(qemu_get_ram_ptr(ram_addr), val); + stl_p(qemu_get_ram_ptr(NULL, ram_addr), val); break; default: abort(); @@ -1900,8 +2062,7 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr, /* we remove the notdirty callback only if the code has been flushed */ if (!cpu_physical_memory_is_clean(ram_addr)) { - CPUArchState *env = current_cpu->env_ptr; - tlb_set_dirty(env, current_cpu->mem_io_vaddr); + tlb_set_dirty(current_cpu, current_cpu->mem_io_vaddr); } } @@ -1921,6 +2082,7 @@ static const MemoryRegionOps notdirty_mem_ops = { static void check_watchpoint(int offset, int len, MemTxAttrs attrs, int flags) { CPUState *cpu = current_cpu; + CPUClass *cc = CPU_GET_CLASS(cpu); CPUArchState *env = cpu->env_ptr; target_ulong pc, cs_base; target_ulong vaddr; @@ -1946,6 +2108,11 @@ static void check_watchpoint(int offset, int len, MemTxAttrs attrs, int flags) wp->hitaddr = vaddr; wp->hitattrs = attrs; if (!cpu->watchpoint_hit) { + if (wp->flags & BP_CPU && + !cc->debug_check_watchpoint(cpu, wp)) { + wp->flags &= ~BP_WATCHPOINT_HIT; + continue; + } cpu->watchpoint_hit = wp; tb_check_watchpoint(cpu); if (wp->flags & BP_STOP_BEFORE_ACCESS) { @@ -1971,17 +2138,19 @@ static MemTxResult watch_mem_read(void *opaque, hwaddr addr, uint64_t *pdata, { MemTxResult res; uint64_t data; + int asidx = cpu_asidx_from_attrs(current_cpu, attrs); + AddressSpace *as = current_cpu->cpu_ases[asidx].as; check_watchpoint(addr & ~TARGET_PAGE_MASK, size, attrs, BP_MEM_READ); switch (size) { case 1: - data = address_space_ldub(&address_space_memory, addr, attrs, &res); + data = address_space_ldub(as, addr, attrs, &res); break; case 2: - data = address_space_lduw(&address_space_memory, addr, attrs, &res); + data = address_space_lduw(as, addr, attrs, &res); break; case 4: - data = address_space_ldl(&address_space_memory, addr, attrs, &res); + data = address_space_ldl(as, addr, attrs, &res); break; default: abort(); } @@ -1994,17 +2163,19 @@ static MemTxResult watch_mem_write(void *opaque, hwaddr addr, MemTxAttrs attrs) { MemTxResult res; + int asidx = cpu_asidx_from_attrs(current_cpu, attrs); + AddressSpace *as = current_cpu->cpu_ases[asidx].as; check_watchpoint(addr & ~TARGET_PAGE_MASK, size, attrs, BP_MEM_WRITE); switch (size) { case 1: - address_space_stb(&address_space_memory, addr, val, attrs, &res); + address_space_stb(as, addr, val, attrs, &res); break; case 2: - address_space_stw(&address_space_memory, addr, val, attrs, &res); + address_space_stw(as, addr, val, attrs, &res); break; case 4: - address_space_stl(&address_space_memory, addr, val, attrs, &res); + address_space_stl(as, addr, val, attrs, &res); break; default: abort(); } @@ -2161,9 +2332,11 @@ static uint16_t dummy_section(PhysPageMap *map, AddressSpace *as, return phys_section_add(map, §ion); } -MemoryRegion *iotlb_to_region(CPUState *cpu, hwaddr index) +MemoryRegion *iotlb_to_region(CPUState *cpu, hwaddr index, MemTxAttrs attrs) { - AddressSpaceDispatch *d = atomic_rcu_read(&cpu->memory_dispatch); + int asidx = cpu_asidx_from_attrs(cpu, attrs); + CPUAddressSpace *cpuas = &cpu->cpu_ases[asidx]; + AddressSpaceDispatch *d = atomic_rcu_read(&cpuas->memory_dispatch); MemoryRegionSection *sections = d->map.sections; return sections[index & ~TARGET_PAGE_MASK].mr; @@ -2222,19 +2395,20 @@ static void mem_commit(MemoryListener *listener) static void tcg_commit(MemoryListener *listener) { - CPUState *cpu; + CPUAddressSpace *cpuas; + AddressSpaceDispatch *d; /* since each CPU stores ram addresses in its TLB cache, we must reset the modified entries */ - /* XXX: slow ! */ - CPU_FOREACH(cpu) { - /* FIXME: Disentangle the cpu.h circular files deps so we can - directly get the right CPU from listener. */ - if (cpu->tcg_as_listener != listener) { - continue; - } - cpu_reload_memory_map(cpu); - } + cpuas = container_of(listener, CPUAddressSpace, tcg_as_listener); + cpu_reloading_memory_map(); + /* The CPU and TLB are protected by the iothread lock. + * We reload the dispatch pointer now because cpu_reloading_memory_map() + * may have split the RCU critical section. + */ + d = atomic_rcu_read(&cpuas->as->dispatch); + cpuas->memory_dispatch = d; + tlb_flush(cpuas->cpu, 1); } void address_space_init_dispatch(AddressSpace *as) @@ -2374,9 +2548,7 @@ static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr) if (l > access_size_max) { l = access_size_max; } - if (l & (l - 1)) { - l = 1 << (qemu_fls(l) - 1); - } + l = pow2floor(l); return l; } @@ -2404,101 +2576,58 @@ static bool prepare_mmio_access(MemoryRegion *mr) return release_lock; } -MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, - uint8_t *buf, int len, bool is_write) +/* Called within RCU critical section. */ +static MemTxResult address_space_write_continue(AddressSpace *as, hwaddr addr, + MemTxAttrs attrs, + const uint8_t *buf, + int len, hwaddr addr1, + hwaddr l, MemoryRegion *mr) { - hwaddr l; uint8_t *ptr; uint64_t val; - hwaddr addr1; - MemoryRegion *mr; MemTxResult result = MEMTX_OK; bool release_lock = false; - rcu_read_lock(); - while (len > 0) { - l = len; - mr = address_space_translate(as, addr, &addr1, &l, is_write); - - if (is_write) { - if (!memory_access_is_direct(mr, is_write)) { - release_lock |= prepare_mmio_access(mr); - l = memory_access_size(mr, l, addr1); - /* XXX: could force current_cpu to NULL to avoid - potential bugs */ - switch (l) { - case 8: - /* 64 bit write access */ - val = ldq_p(buf); - result |= memory_region_dispatch_write(mr, addr1, val, 8, - attrs); - break; - case 4: - /* 32 bit write access */ - val = ldl_p(buf); - result |= memory_region_dispatch_write(mr, addr1, val, 4, - attrs); - break; - case 2: - /* 16 bit write access */ - val = lduw_p(buf); - result |= memory_region_dispatch_write(mr, addr1, val, 2, - attrs); - break; - case 1: - /* 8 bit write access */ - val = ldub_p(buf); - result |= memory_region_dispatch_write(mr, addr1, val, 1, - attrs); - break; - default: - abort(); - } - } else { - addr1 += memory_region_get_ram_addr(mr); - /* RAM case */ - ptr = qemu_get_ram_ptr(addr1); - memcpy(ptr, buf, l); - invalidate_and_set_dirty(mr, addr1, l); + for (;;) { + if (!memory_access_is_direct(mr, true)) { + release_lock |= prepare_mmio_access(mr); + l = memory_access_size(mr, l, addr1); + /* XXX: could force current_cpu to NULL to avoid + potential bugs */ + switch (l) { + case 8: + /* 64 bit write access */ + val = ldq_p(buf); + result |= memory_region_dispatch_write(mr, addr1, val, 8, + attrs); + break; + case 4: + /* 32 bit write access */ + val = ldl_p(buf); + result |= memory_region_dispatch_write(mr, addr1, val, 4, + attrs); + break; + case 2: + /* 16 bit write access */ + val = lduw_p(buf); + result |= memory_region_dispatch_write(mr, addr1, val, 2, + attrs); + break; + case 1: + /* 8 bit write access */ + val = ldub_p(buf); + result |= memory_region_dispatch_write(mr, addr1, val, 1, + attrs); + break; + default: + abort(); } } else { - if (!memory_access_is_direct(mr, is_write)) { - /* I/O case */ - release_lock |= prepare_mmio_access(mr); - l = memory_access_size(mr, l, addr1); - switch (l) { - case 8: - /* 64 bit read access */ - result |= memory_region_dispatch_read(mr, addr1, &val, 8, - attrs); - stq_p(buf, val); - break; - case 4: - /* 32 bit read access */ - result |= memory_region_dispatch_read(mr, addr1, &val, 4, - attrs); - stl_p(buf, val); - break; - case 2: - /* 16 bit read access */ - result |= memory_region_dispatch_read(mr, addr1, &val, 2, - attrs); - stw_p(buf, val); - break; - case 1: - /* 8 bit read access */ - result |= memory_region_dispatch_read(mr, addr1, &val, 1, - attrs); - stb_p(buf, val); - break; - default: - abort(); - } - } else { - /* RAM case */ - ptr = qemu_get_ram_ptr(mr->ram_addr + addr1); - memcpy(buf, ptr, l); - } + addr1 += memory_region_get_ram_addr(mr); + /* RAM case */ + ptr = qemu_get_ram_ptr(mr->ram_block, addr1); + memcpy(ptr, buf, l); + invalidate_and_set_dirty(mr, addr1, l); } if (release_lock) { @@ -2509,8 +2638,14 @@ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, len -= l; buf += l; addr += l; + + if (!len) { + break; + } + + l = len; + mr = address_space_translate(as, addr, &addr1, &l, true); } - rcu_read_unlock(); return result; } @@ -2518,15 +2653,123 @@ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, MemTxResult address_space_write(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, const uint8_t *buf, int len) { - return address_space_rw(as, addr, attrs, (uint8_t *)buf, len, true); + hwaddr l; + hwaddr addr1; + MemoryRegion *mr; + MemTxResult result = MEMTX_OK; + + if (len > 0) { + rcu_read_lock(); + l = len; + mr = address_space_translate(as, addr, &addr1, &l, true); + result = address_space_write_continue(as, addr, attrs, buf, len, + addr1, l, mr); + rcu_read_unlock(); + } + + return result; } -MemTxResult address_space_read(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, - uint8_t *buf, int len) +/* Called within RCU critical section. */ +MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr, + MemTxAttrs attrs, uint8_t *buf, + int len, hwaddr addr1, hwaddr l, + MemoryRegion *mr) { - return address_space_rw(as, addr, attrs, buf, len, false); + uint8_t *ptr; + uint64_t val; + MemTxResult result = MEMTX_OK; + bool release_lock = false; + + for (;;) { + if (!memory_access_is_direct(mr, false)) { + /* I/O case */ + release_lock |= prepare_mmio_access(mr); + l = memory_access_size(mr, l, addr1); + switch (l) { + case 8: + /* 64 bit read access */ + result |= memory_region_dispatch_read(mr, addr1, &val, 8, + attrs); + stq_p(buf, val); + break; + case 4: + /* 32 bit read access */ + result |= memory_region_dispatch_read(mr, addr1, &val, 4, + attrs); + stl_p(buf, val); + break; + case 2: + /* 16 bit read access */ + result |= memory_region_dispatch_read(mr, addr1, &val, 2, + attrs); + stw_p(buf, val); + break; + case 1: + /* 8 bit read access */ + result |= memory_region_dispatch_read(mr, addr1, &val, 1, + attrs); + stb_p(buf, val); + break; + default: + abort(); + } + } else { + /* RAM case */ + ptr = qemu_get_ram_ptr(mr->ram_block, + memory_region_get_ram_addr(mr) + addr1); + memcpy(buf, ptr, l); + } + + if (release_lock) { + qemu_mutex_unlock_iothread(); + release_lock = false; + } + + len -= l; + buf += l; + addr += l; + + if (!len) { + break; + } + + l = len; + mr = address_space_translate(as, addr, &addr1, &l, false); + } + + return result; +} + +MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr, + MemTxAttrs attrs, uint8_t *buf, int len) +{ + hwaddr l; + hwaddr addr1; + MemoryRegion *mr; + MemTxResult result = MEMTX_OK; + + if (len > 0) { + rcu_read_lock(); + l = len; + mr = address_space_translate(as, addr, &addr1, &l, false); + result = address_space_read_continue(as, addr, attrs, buf, len, + addr1, l, mr); + rcu_read_unlock(); + } + + return result; } +MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, + uint8_t *buf, int len, bool is_write) +{ + if (is_write) { + return address_space_write(as, addr, attrs, (uint8_t *)buf, len); + } else { + return address_space_read(as, addr, attrs, (uint8_t *)buf, len); + } +} void cpu_physical_memory_rw(hwaddr addr, uint8_t *buf, int len, int is_write) @@ -2559,7 +2802,7 @@ static inline void cpu_physical_memory_write_rom_internal(AddressSpace *as, } else { addr1 += memory_region_get_ram_addr(mr); /* ROM/RAM case */ - ptr = qemu_get_ram_ptr(addr1); + ptr = qemu_get_ram_ptr(mr->ram_block, addr1); switch (type) { case WRITE_DATA: memcpy(ptr, buf, l); @@ -2652,8 +2895,8 @@ void cpu_register_map_client(QEMUBH *bh) void cpu_exec_init_all(void) { qemu_mutex_init(&ram_list.mutex); - memory_map_init(); io_mem_init(); + memory_map_init(); qemu_mutex_init(&map_client_list_lock); } @@ -2718,6 +2961,7 @@ void *address_space_map(AddressSpace *as, hwaddr l, xlat, base; MemoryRegion *mr, *this_mr; ram_addr_t raddr; + void *ptr; if (len == 0) { return NULL; @@ -2769,9 +3013,11 @@ void *address_space_map(AddressSpace *as, } memory_region_ref(mr); - rcu_read_unlock(); *plen = done; - return qemu_ram_ptr_length(raddr + base, plen); + ptr = qemu_ram_ptr_length(mr->ram_block, raddr + base, plen); + rcu_read_unlock(); + + return ptr; } /* Unmaps a memory region previously mapped by address_space_map(). @@ -2852,7 +3098,8 @@ static inline uint32_t address_space_ldl_internal(AddressSpace *as, hwaddr addr, #endif } else { /* RAM case */ - ptr = qemu_get_ram_ptr((memory_region_get_ram_addr(mr) + ptr = qemu_get_ram_ptr(mr->ram_block, + (memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK) + addr1); switch (endian) { @@ -2947,7 +3194,8 @@ static inline uint64_t address_space_ldq_internal(AddressSpace *as, hwaddr addr, #endif } else { /* RAM case */ - ptr = qemu_get_ram_ptr((memory_region_get_ram_addr(mr) + ptr = qemu_get_ram_ptr(mr->ram_block, + (memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK) + addr1); switch (endian) { @@ -3062,7 +3310,8 @@ static inline uint32_t address_space_lduw_internal(AddressSpace *as, #endif } else { /* RAM case */ - ptr = qemu_get_ram_ptr((memory_region_get_ram_addr(mr) + ptr = qemu_get_ram_ptr(mr->ram_block, + (memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK) + addr1); switch (endian) { @@ -3147,7 +3396,7 @@ void address_space_stl_notdirty(AddressSpace *as, hwaddr addr, uint32_t val, r = memory_region_dispatch_write(mr, addr1, val, 4, attrs); } else { addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK; - ptr = qemu_get_ram_ptr(addr1); + ptr = qemu_get_ram_ptr(mr->ram_block, addr1); stl_p(ptr, val); dirty_log_mask = memory_region_get_dirty_log_mask(mr); @@ -3202,7 +3451,7 @@ static inline void address_space_stl_internal(AddressSpace *as, } else { /* RAM case */ addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK; - ptr = qemu_get_ram_ptr(addr1); + ptr = qemu_get_ram_ptr(mr->ram_block, addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: stl_le_p(ptr, val); @@ -3312,7 +3561,7 @@ static inline void address_space_stw_internal(AddressSpace *as, } else { /* RAM case */ addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK; - ptr = qemu_get_ram_ptr(addr1); + ptr = qemu_get_ram_ptr(mr->ram_block, addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: stw_le_p(ptr, val); @@ -3429,8 +3678,12 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, target_ulong page; while (len > 0) { + int asidx; + MemTxAttrs attrs; + page = addr & TARGET_PAGE_MASK; - phys_addr = cpu_get_phys_page_debug(cpu, page); + phys_addr = cpu_get_phys_page_attrs_debug(cpu, page, &attrs); + asidx = cpu_asidx_from_attrs(cpu, attrs); /* if no physical page mapped, return an error */ if (phys_addr == -1) return -1; @@ -3439,9 +3692,11 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, l = len; phys_addr += (addr & ~TARGET_PAGE_MASK); if (is_write) { - cpu_physical_memory_write_rom(cpu->as, phys_addr, buf, l); + cpu_physical_memory_write_rom(cpu->cpu_ases[asidx].as, + phys_addr, buf, l); } else { - address_space_rw(cpu->as, phys_addr, MEMTXATTRS_UNSPECIFIED, + address_space_rw(cpu->cpu_ases[asidx].as, phys_addr, + MEMTXATTRS_UNSPECIFIED, buf, l, 0); } len -= l; @@ -3450,6 +3705,16 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, } return 0; } + +/* + * Allows code that needs to deal with migration bitmaps etc to still be built + * target independent. + */ +size_t qemu_target_page_bits(void) +{ + return TARGET_PAGE_BITS; +} + #endif /* diff --git a/qemu/fpu/softfloat-macros.h b/qemu/fpu/softfloat-macros.h index 5e030cd8e..9cc6158cb 100644 --- a/qemu/fpu/softfloat-macros.h +++ b/qemu/fpu/softfloat-macros.h @@ -99,7 +99,7 @@ this code that are retained. | The result is stored in the location pointed to by `zPtr'. *----------------------------------------------------------------------------*/ -static inline void shift32RightJamming(uint32_t a, int_fast16_t count, uint32_t *zPtr) +static inline void shift32RightJamming(uint32_t a, int count, uint32_t *zPtr) { uint32_t z; @@ -125,7 +125,7 @@ static inline void shift32RightJamming(uint32_t a, int_fast16_t count, uint32_t | The result is stored in the location pointed to by `zPtr'. *----------------------------------------------------------------------------*/ -static inline void shift64RightJamming(uint64_t a, int_fast16_t count, uint64_t *zPtr) +static inline void shift64RightJamming(uint64_t a, int count, uint64_t *zPtr) { uint64_t z; @@ -161,10 +161,10 @@ static inline void shift64RightJamming(uint64_t a, int_fast16_t count, uint64_t static inline void shift64ExtraRightJamming( - uint64_t a0, uint64_t a1, int_fast16_t count, uint64_t *z0Ptr, uint64_t *z1Ptr) + uint64_t a0, uint64_t a1, int count, uint64_t *z0Ptr, uint64_t *z1Ptr) { uint64_t z0, z1; - int8 negCount = ( - count ) & 63; + int8_t negCount = ( - count ) & 63; if ( count == 0 ) { z1 = a1; @@ -198,10 +198,10 @@ static inline void static inline void shift128Right( - uint64_t a0, uint64_t a1, int_fast16_t count, uint64_t *z0Ptr, uint64_t *z1Ptr) + uint64_t a0, uint64_t a1, int count, uint64_t *z0Ptr, uint64_t *z1Ptr) { uint64_t z0, z1; - int8 negCount = ( - count ) & 63; + int8_t negCount = ( - count ) & 63; if ( count == 0 ) { z1 = a1; @@ -233,10 +233,10 @@ static inline void static inline void shift128RightJamming( - uint64_t a0, uint64_t a1, int_fast16_t count, uint64_t *z0Ptr, uint64_t *z1Ptr) + uint64_t a0, uint64_t a1, int count, uint64_t *z0Ptr, uint64_t *z1Ptr) { uint64_t z0, z1; - int8 negCount = ( - count ) & 63; + int8_t negCount = ( - count ) & 63; if ( count == 0 ) { z1 = a1; @@ -287,14 +287,14 @@ static inline void uint64_t a0, uint64_t a1, uint64_t a2, - int_fast16_t count, + int count, uint64_t *z0Ptr, uint64_t *z1Ptr, uint64_t *z2Ptr ) { uint64_t z0, z1, z2; - int8 negCount = ( - count ) & 63; + int8_t negCount = ( - count ) & 63; if ( count == 0 ) { z2 = a2; @@ -342,7 +342,7 @@ static inline void static inline void shortShift128Left( - uint64_t a0, uint64_t a1, int_fast16_t count, uint64_t *z0Ptr, uint64_t *z1Ptr) + uint64_t a0, uint64_t a1, int count, uint64_t *z0Ptr, uint64_t *z1Ptr) { *z1Ptr = a1<>27 ) & 15; @@ -669,7 +669,7 @@ static uint32_t estimateSqrt32(int_fast16_t aExp, uint32_t a) | `a'. If `a' is zero, 32 is returned. *----------------------------------------------------------------------------*/ -static int8 countLeadingZeros32( uint32_t a ) +static int8_t countLeadingZeros32( uint32_t a ) { #if SOFTFLOAT_GNUC_PREREQ(3, 4) if (a) { @@ -678,7 +678,7 @@ static int8 countLeadingZeros32( uint32_t a ) return 32; } #else - static const int8 countLeadingZerosHigh[] = { + static const int8_t countLeadingZerosHigh[] = { 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -696,7 +696,7 @@ static int8 countLeadingZeros32( uint32_t a ) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - int8 shiftCount; + int8_t shiftCount; shiftCount = 0; if ( a < 0x10000 ) { @@ -717,7 +717,7 @@ static int8 countLeadingZeros32( uint32_t a ) | `a'. If `a' is zero, 64 is returned. *----------------------------------------------------------------------------*/ -static int8 countLeadingZeros64( uint64_t a ) +static int8_t countLeadingZeros64( uint64_t a ) { #if SOFTFLOAT_GNUC_PREREQ(3, 4) if (a) { @@ -726,7 +726,7 @@ static int8 countLeadingZeros64( uint64_t a ) return 64; } #else - int8 shiftCount; + int8_t shiftCount; shiftCount = 0; if ( a < ( (uint64_t) 1 )<<32 ) { diff --git a/qemu/fpu/softfloat-specialize.h b/qemu/fpu/softfloat-specialize.h index 6dd41d897..a4cbdad45 100644 --- a/qemu/fpu/softfloat-specialize.h +++ b/qemu/fpu/softfloat-specialize.h @@ -113,7 +113,7 @@ const float16 float16_default_nan = const_float16(0xFE00); #if defined(TARGET_SPARC) const float32 float32_default_nan = const_float32(0x7FFFFFFF); #elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \ - defined(TARGET_XTENSA) || defined(TARGET_S390X) + defined(TARGET_XTENSA) || defined(TARGET_S390X) || defined(TARGET_TRICORE) const float32 float32_default_nan = const_float32(0x7FC00000); #elif SNAN_BIT_IS_ONE const float32 float32_default_nan = const_float32(0x7FBFFFFF); @@ -174,7 +174,7 @@ const float128 float128_default_nan | should be simply `float_exception_flags |= flags;'. *----------------------------------------------------------------------------*/ -void float_raise(int8 flags, float_status *status) +void float_raise(int8_t flags, float_status *status) { status->float_exception_flags |= flags; } diff --git a/qemu/fpu/softfloat.c b/qemu/fpu/softfloat.c index f1170fe50..166c48e43 100644 --- a/qemu/fpu/softfloat.c +++ b/qemu/fpu/softfloat.c @@ -82,12 +82,11 @@ this code that are retained. /* softfloat (and in particular the code in softfloat-specialize.h) is * target-dependent and needs the TARGET_* macros. */ -#include "config.h" +#include "qemu/osdep.h" #include "fpu/softfloat.h" /* We only need stdlib for abort() */ -#include /*---------------------------------------------------------------------------- | Primitive arithmetic functions, including multi-word arithmetic, and @@ -119,7 +118,7 @@ static inline uint32_t extractFloat16Frac(float16 a) | Returns the exponent bits of the half-precision floating-point value `a'. *----------------------------------------------------------------------------*/ -static inline int_fast16_t extractFloat16Exp(float16 a) +static inline int extractFloat16Exp(float16 a) { return (float16_val(a) >> 10) & 0x1f; } @@ -144,11 +143,11 @@ static inline flag extractFloat16Sign(float16 a) | positive or negative integer is returned. *----------------------------------------------------------------------------*/ -static int32 roundAndPackInt32(flag zSign, uint64_t absZ, float_status *status) +static int32_t roundAndPackInt32(flag zSign, uint64_t absZ, float_status *status) { - int8 roundingMode; + int8_t roundingMode; flag roundNearestEven; - int8 roundIncrement, roundBits; + int8_t roundIncrement, roundBits; int32_t z; roundingMode = status->float_rounding_mode; @@ -198,10 +197,10 @@ static int32 roundAndPackInt32(flag zSign, uint64_t absZ, float_status *status) | returned. *----------------------------------------------------------------------------*/ -static int64 roundAndPackInt64(flag zSign, uint64_t absZ0, uint64_t absZ1, +static int64_t roundAndPackInt64(flag zSign, uint64_t absZ0, uint64_t absZ1, float_status *status) { - int8 roundingMode; + int8_t roundingMode; flag roundNearestEven, increment; int64_t z; @@ -255,10 +254,10 @@ static int64 roundAndPackInt64(flag zSign, uint64_t absZ0, uint64_t absZ1, | exception is raised and the largest unsigned integer is returned. *----------------------------------------------------------------------------*/ -static int64 roundAndPackUint64(flag zSign, uint64_t absZ0, +static int64_t roundAndPackUint64(flag zSign, uint64_t absZ0, uint64_t absZ1, float_status *status) { - int8 roundingMode; + int8_t roundingMode; flag roundNearestEven, increment; roundingMode = status->float_rounding_mode; @@ -315,7 +314,7 @@ static inline uint32_t extractFloat32Frac( float32 a ) | Returns the exponent bits of the single-precision floating-point value `a'. *----------------------------------------------------------------------------*/ -static inline int_fast16_t extractFloat32Exp(float32 a) +static inline int extractFloat32Exp(float32 a) { return ( float32_val(a)>>23 ) & 0xFF; @@ -356,9 +355,9 @@ float32 float32_squash_input_denormal(float32 a, float_status *status) *----------------------------------------------------------------------------*/ static void - normalizeFloat32Subnormal(uint32_t aSig, int_fast16_t *zExpPtr, uint32_t *zSigPtr) + normalizeFloat32Subnormal(uint32_t aSig, int *zExpPtr, uint32_t *zSigPtr) { - int8 shiftCount; + int8_t shiftCount; shiftCount = countLeadingZeros32( aSig ) - 8; *zSigPtr = aSig<float_rounding_mode; @@ -482,10 +481,10 @@ static float32 roundAndPackFloat32(flag zSign, int_fast16_t zExp, uint32_t zSig, *----------------------------------------------------------------------------*/ static float32 - normalizeRoundAndPackFloat32(flag zSign, int_fast16_t zExp, uint32_t zSig, + normalizeRoundAndPackFloat32(flag zSign, int zExp, uint32_t zSig, float_status *status) { - int8 shiftCount; + int8_t shiftCount; shiftCount = countLeadingZeros32( zSig ) - 1; return roundAndPackFloat32(zSign, zExp - shiftCount, zSig<>52 ) & 0x7FF; @@ -549,9 +548,9 @@ float64 float64_squash_input_denormal(float64 a, float_status *status) *----------------------------------------------------------------------------*/ static void - normalizeFloat64Subnormal(uint64_t aSig, int_fast16_t *zExpPtr, uint64_t *zSigPtr) + normalizeFloat64Subnormal(uint64_t aSig, int *zExpPtr, uint64_t *zSigPtr) { - int8 shiftCount; + int8_t shiftCount; shiftCount = countLeadingZeros64( aSig ) - 11; *zSigPtr = aSig<float_rounding_mode; @@ -674,10 +673,10 @@ static float64 roundAndPackFloat64(flag zSign, int_fast16_t zExp, uint64_t zSig, *----------------------------------------------------------------------------*/ static float64 - normalizeRoundAndPackFloat64(flag zSign, int_fast16_t zExp, uint64_t zSig, + normalizeRoundAndPackFloat64(flag zSign, int zExp, uint64_t zSig, float_status *status) { - int8 shiftCount; + int8_t shiftCount; shiftCount = countLeadingZeros64( zSig ) - 1; return roundAndPackFloat64(zSign, zExp - shiftCount, zSig<float_rounding_mode; roundNearestEven = ( roundingMode == float_round_nearest_even ); @@ -974,12 +973,12 @@ static floatx80 roundAndPackFloatx80(int8 roundingPrecision, flag zSign, | normalized. *----------------------------------------------------------------------------*/ -static floatx80 normalizeRoundAndPackFloatx80(int8 roundingPrecision, - flag zSign, int32 zExp, +static floatx80 normalizeRoundAndPackFloatx80(int8_t roundingPrecision, + flag zSign, int32_t zExp, uint64_t zSig0, uint64_t zSig1, float_status *status) { - int8 shiftCount; + int8_t shiftCount; if ( zSig0 == 0 ) { zSig0 = zSig1; @@ -1023,7 +1022,7 @@ static inline uint64_t extractFloat128Frac0( float128 a ) | `a'. *----------------------------------------------------------------------------*/ -static inline int32 extractFloat128Exp( float128 a ) +static inline int32_t extractFloat128Exp( float128 a ) { return ( a.high>>48 ) & 0x7FFF; @@ -1055,12 +1054,12 @@ static void normalizeFloat128Subnormal( uint64_t aSig0, uint64_t aSig1, - int32 *zExpPtr, + int32_t *zExpPtr, uint64_t *zSig0Ptr, uint64_t *zSig1Ptr ) { - int8 shiftCount; + int8_t shiftCount; if ( aSig0 == 0 ) { shiftCount = countLeadingZeros64( aSig1 ) - 15; @@ -1096,7 +1095,7 @@ static void *----------------------------------------------------------------------------*/ static inline float128 - packFloat128( flag zSign, int32 zExp, uint64_t zSig0, uint64_t zSig1 ) + packFloat128( flag zSign, int32_t zExp, uint64_t zSig0, uint64_t zSig1 ) { float128 z; @@ -1127,11 +1126,11 @@ static inline float128 | overflow follows the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -static float128 roundAndPackFloat128(flag zSign, int32 zExp, +static float128 roundAndPackFloat128(flag zSign, int32_t zExp, uint64_t zSig0, uint64_t zSig1, uint64_t zSig2, float_status *status) { - int8 roundingMode; + int8_t roundingMode; flag roundNearestEven, increment, isTiny; roundingMode = status->float_rounding_mode; @@ -1245,11 +1244,11 @@ static float128 roundAndPackFloat128(flag zSign, int32 zExp, | point exponent. *----------------------------------------------------------------------------*/ -static float128 normalizeRoundAndPackFloat128(flag zSign, int32 zExp, +static float128 normalizeRoundAndPackFloat128(flag zSign, int32_t zExp, uint64_t zSig0, uint64_t zSig1, float_status *status) { - int8 shiftCount; + int8_t shiftCount; uint64_t zSig2; if ( zSig0 == 0 ) { @@ -1296,8 +1295,8 @@ float32 int32_to_float32(int32_t a, float_status *status) float64 int32_to_float64(int32_t a, float_status *status) { flag zSign; - uint32 absA; - int8 shiftCount; + uint32_t absA; + int8_t shiftCount; uint64_t zSig; if ( a == 0 ) return float64_zero; @@ -1319,8 +1318,8 @@ float64 int32_to_float64(int32_t a, float_status *status) floatx80 int32_to_floatx80(int32_t a, float_status *status) { flag zSign; - uint32 absA; - int8 shiftCount; + uint32_t absA; + int8_t shiftCount; uint64_t zSig; if ( a == 0 ) return packFloatx80( 0, 0, 0 ); @@ -1341,8 +1340,8 @@ floatx80 int32_to_floatx80(int32_t a, float_status *status) float128 int32_to_float128(int32_t a, float_status *status) { flag zSign; - uint32 absA; - int8 shiftCount; + uint32_t absA; + int8_t shiftCount; uint64_t zSig0; if ( a == 0 ) return packFloat128( 0, 0, 0, 0 ); @@ -1363,8 +1362,8 @@ float128 int32_to_float128(int32_t a, float_status *status) float32 int64_to_float32(int64_t a, float_status *status) { flag zSign; - uint64 absA; - int8 shiftCount; + uint64_t absA; + int8_t shiftCount; if ( a == 0 ) return float32_zero; zSign = ( a < 0 ); @@ -1414,8 +1413,8 @@ float64 int64_to_float64(int64_t a, float_status *status) floatx80 int64_to_floatx80(int64_t a, float_status *status) { flag zSign; - uint64 absA; - int8 shiftCount; + uint64_t absA; + int8_t shiftCount; if ( a == 0 ) return packFloatx80( 0, 0, 0 ); zSign = ( a < 0 ); @@ -1434,9 +1433,9 @@ floatx80 int64_to_floatx80(int64_t a, float_status *status) float128 int64_to_float128(int64_t a, float_status *status) { flag zSign; - uint64 absA; - int8 shiftCount; - int32 zExp; + uint64_t absA; + int8_t shiftCount; + int32_t zExp; uint64_t zSig0, zSig1; if ( a == 0 ) return packFloat128( 0, 0, 0, 0 ); @@ -1541,10 +1540,11 @@ float128 uint64_to_float128(uint64_t a, float_status *status) | largest integer with the same sign as `a' is returned. *----------------------------------------------------------------------------*/ -int32 float32_to_int32(float32 a, float_status *status) +int32_t float32_to_int32(float32 a, float_status *status) { flag aSign; - int_fast16_t aExp, shiftCount; + int aExp; + int shiftCount; uint32_t aSig; uint64_t aSig64; @@ -1572,10 +1572,11 @@ int32 float32_to_int32(float32 a, float_status *status) | returned. *----------------------------------------------------------------------------*/ -int32 float32_to_int32_round_to_zero(float32 a, float_status *status) +int32_t float32_to_int32_round_to_zero(float32 a, float_status *status) { flag aSign; - int_fast16_t aExp, shiftCount; + int aExp; + int shiftCount; uint32_t aSig; int32_t z; a = float32_squash_input_denormal(a, status); @@ -1617,12 +1618,13 @@ int32 float32_to_int32_round_to_zero(float32 a, float_status *status) | returned. *----------------------------------------------------------------------------*/ -int_fast16_t float32_to_int16_round_to_zero(float32 a, float_status *status) +int16_t float32_to_int16_round_to_zero(float32 a, float_status *status) { flag aSign; - int_fast16_t aExp, shiftCount; + int aExp; + int shiftCount; uint32_t aSig; - int32 z; + int32_t z; aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -1666,10 +1668,11 @@ int_fast16_t float32_to_int16_round_to_zero(float32 a, float_status *status) | largest integer with the same sign as `a' is returned. *----------------------------------------------------------------------------*/ -int64 float32_to_int64(float32 a, float_status *status) +int64_t float32_to_int64(float32 a, float_status *status) { flag aSign; - int_fast16_t aExp, shiftCount; + int aExp; + int shiftCount; uint32_t aSig; uint64_t aSig64, aSigExtra; a = float32_squash_input_denormal(a, status); @@ -1705,10 +1708,11 @@ int64 float32_to_int64(float32 a, float_status *status) | raise the inexact exception flag. *----------------------------------------------------------------------------*/ -uint64 float32_to_uint64(float32 a, float_status *status) +uint64_t float32_to_uint64(float32 a, float_status *status) { flag aSign; - int_fast16_t aExp, shiftCount; + int aExp; + int shiftCount; uint32_t aSig; uint64_t aSig64, aSigExtra; a = float32_squash_input_denormal(a, status); @@ -1750,7 +1754,7 @@ uint64 float32_to_uint64(float32 a, float_status *status) | not round to zero will raise the inexact flag. *----------------------------------------------------------------------------*/ -uint64 float32_to_uint64_round_to_zero(float32 a, float_status *status) +uint64_t float32_to_uint64_round_to_zero(float32 a, float_status *status) { signed char current_rounding_mode = status->float_rounding_mode; set_float_rounding_mode(float_round_to_zero, status); @@ -1769,13 +1773,14 @@ uint64 float32_to_uint64_round_to_zero(float32 a, float_status *status) | returned. *----------------------------------------------------------------------------*/ -int64 float32_to_int64_round_to_zero(float32 a, float_status *status) +int64_t float32_to_int64_round_to_zero(float32 a, float_status *status) { flag aSign; - int_fast16_t aExp, shiftCount; + int aExp; + int shiftCount; uint32_t aSig; uint64_t aSig64; - int64 z; + int64_t z; a = float32_squash_input_denormal(a, status); aSig = extractFloat32Frac( a ); @@ -1818,7 +1823,7 @@ int64 float32_to_int64_round_to_zero(float32 a, float_status *status) float64 float32_to_float64(float32 a, float_status *status) { flag aSign; - int_fast16_t aExp; + int aExp; uint32_t aSig; a = float32_squash_input_denormal(a, status); @@ -1850,7 +1855,7 @@ float64 float32_to_float64(float32 a, float_status *status) floatx80 float32_to_floatx80(float32 a, float_status *status) { flag aSign; - int_fast16_t aExp; + int aExp; uint32_t aSig; a = float32_squash_input_denormal(a, status); @@ -1882,7 +1887,7 @@ floatx80 float32_to_floatx80(float32 a, float_status *status) float128 float32_to_float128(float32 a, float_status *status) { flag aSign; - int_fast16_t aExp; + int aExp; uint32_t aSig; a = float32_squash_input_denormal(a, status); @@ -1914,7 +1919,7 @@ float128 float32_to_float128(float32 a, float_status *status) float32 float32_round_to_int(float32 a, float_status *status) { flag aSign; - int_fast16_t aExp; + int aExp; uint32_t lastBitMask, roundBitsMask; uint32_t z; a = float32_squash_input_denormal(a, status); @@ -1996,9 +2001,9 @@ float32 float32_round_to_int(float32 a, float_status *status) static float32 addFloat32Sigs(float32 a, float32 b, flag zSign, float_status *status) { - int_fast16_t aExp, bExp, zExp; + int aExp, bExp, zExp; uint32_t aSig, bSig, zSig; - int_fast16_t expDiff; + int expDiff; aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -2082,9 +2087,9 @@ static float32 addFloat32Sigs(float32 a, float32 b, flag zSign, static float32 subFloat32Sigs(float32 a, float32 b, flag zSign, float_status *status) { - int_fast16_t aExp, bExp, zExp; + int aExp, bExp, zExp; uint32_t aSig, bSig, zSig; - int_fast16_t expDiff; + int expDiff; aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -2208,7 +2213,7 @@ float32 float32_sub(float32 a, float32 b, float_status *status) float32 float32_mul(float32 a, float32 b, float_status *status) { flag aSign, bSign, zSign; - int_fast16_t aExp, bExp, zExp; + int aExp, bExp, zExp; uint32_t aSig, bSig; uint64_t zSig64; uint32_t zSig; @@ -2273,7 +2278,7 @@ float32 float32_mul(float32 a, float32 b, float_status *status) float32 float32_div(float32 a, float32 b, float_status *status) { flag aSign, bSign, zSign; - int_fast16_t aExp, bExp, zExp; + int aExp, bExp, zExp; uint32_t aSig, bSig, zSig; a = float32_squash_input_denormal(a, status); b = float32_squash_input_denormal(b, status); @@ -2343,7 +2348,7 @@ float32 float32_div(float32 a, float32 b, float_status *status) float32 float32_rem(float32 a, float32 b, float_status *status) { flag aSign, zSign; - int_fast16_t aExp, bExp, expDiff; + int aExp, bExp, expDiff; uint32_t aSig, bSig; uint32_t q; uint64_t aSig64, bSig64, q64; @@ -2451,7 +2456,7 @@ float32 float32_muladd(float32 a, float32 b, float32 c, int flags, float_status *status) { flag aSign, bSign, cSign, zSign; - int_fast16_t aExp, bExp, cExp, pExp, zExp, expDiff; + int aExp, bExp, cExp, pExp, zExp, expDiff; uint32_t aSig, bSig, cSig; flag pInf, pZero, pSign; uint64_t pSig64, cSig64, zSig64; @@ -2671,7 +2676,7 @@ float32 float32_muladd(float32 a, float32 b, float32 c, int flags, float32 float32_sqrt(float32 a, float_status *status) { flag aSign; - int_fast16_t aExp, zExp; + int aExp, zExp; uint32_t aSig, zSig; uint64_t rem, term; a = float32_squash_input_denormal(a, status); @@ -2759,7 +2764,7 @@ static const float64 float32_exp2_coefficients[15] = float32 float32_exp2(float32 a, float_status *status) { flag aSign; - int_fast16_t aExp; + int aExp; uint32_t aSig; float64 r, x, xn; int i; @@ -2809,7 +2814,7 @@ float32 float32_exp2(float32 a, float_status *status) float32 float32_log2(float32 a, float_status *status) { flag aSign, zSign; - int_fast16_t aExp; + int aExp; uint32_t aSig, zSig, i; a = float32_squash_input_denormal(a, status); @@ -3073,10 +3078,11 @@ int float32_unordered_quiet(float32 a, float32 b, float_status *status) | largest integer with the same sign as `a' is returned. *----------------------------------------------------------------------------*/ -int32 float64_to_int32(float64 a, float_status *status) +int32_t float64_to_int32(float64 a, float_status *status) { flag aSign; - int_fast16_t aExp, shiftCount; + int aExp; + int shiftCount; uint64_t aSig; a = float64_squash_input_denormal(a, status); @@ -3101,10 +3107,11 @@ int32 float64_to_int32(float64 a, float_status *status) | returned. *----------------------------------------------------------------------------*/ -int32 float64_to_int32_round_to_zero(float64 a, float_status *status) +int32_t float64_to_int32_round_to_zero(float64 a, float_status *status) { flag aSign; - int_fast16_t aExp, shiftCount; + int aExp; + int shiftCount; uint64_t aSig, savedASig; int32_t z; a = float64_squash_input_denormal(a, status); @@ -3150,12 +3157,13 @@ int32 float64_to_int32_round_to_zero(float64 a, float_status *status) | returned. *----------------------------------------------------------------------------*/ -int_fast16_t float64_to_int16_round_to_zero(float64 a, float_status *status) +int16_t float64_to_int16_round_to_zero(float64 a, float_status *status) { flag aSign; - int_fast16_t aExp, shiftCount; + int aExp; + int shiftCount; uint64_t aSig, savedASig; - int32 z; + int32_t z; aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -3201,10 +3209,11 @@ int_fast16_t float64_to_int16_round_to_zero(float64 a, float_status *status) | largest integer with the same sign as `a' is returned. *----------------------------------------------------------------------------*/ -int64 float64_to_int64(float64 a, float_status *status) +int64_t float64_to_int64(float64 a, float_status *status) { flag aSign; - int_fast16_t aExp, shiftCount; + int aExp; + int shiftCount; uint64_t aSig, aSigExtra; a = float64_squash_input_denormal(a, status); @@ -3244,12 +3253,13 @@ int64 float64_to_int64(float64 a, float_status *status) | returned. *----------------------------------------------------------------------------*/ -int64 float64_to_int64_round_to_zero(float64 a, float_status *status) +int64_t float64_to_int64_round_to_zero(float64 a, float_status *status) { flag aSign; - int_fast16_t aExp, shiftCount; + int aExp; + int shiftCount; uint64_t aSig; - int64 z; + int64_t z; a = float64_squash_input_denormal(a, status); aSig = extractFloat64Frac( a ); @@ -3299,7 +3309,7 @@ int64 float64_to_int64_round_to_zero(float64 a, float_status *status) float32 float64_to_float32(float64 a, float_status *status) { flag aSign; - int_fast16_t aExp; + int aExp; uint64_t aSig; uint32_t zSig; a = float64_squash_input_denormal(a, status); @@ -3334,7 +3344,7 @@ float32 float64_to_float32(float64 a, float_status *status) | than the desired result exponent whenever `zSig' is a complete, normalized | significand. *----------------------------------------------------------------------------*/ -static float16 packFloat16(flag zSign, int_fast16_t zExp, uint16_t zSig) +static float16 packFloat16(flag zSign, int zExp, uint16_t zSig) { return make_float16( (((uint32_t)zSign) << 15) + (((uint32_t)zExp) << 10) + zSig); @@ -3368,7 +3378,7 @@ static float16 packFloat16(flag zSign, int_fast16_t zExp, uint16_t zSig) | Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -static float32 roundAndPackFloat16(flag zSign, int_fast16_t zExp, +static float16 roundAndPackFloat16(flag zSign, int zExp, uint32_t zSig, flag ieee, float_status *status) { @@ -3455,7 +3465,7 @@ static float32 roundAndPackFloat16(flag zSign, int_fast16_t zExp, return packFloat16(zSign, zExp, zSig >> 13); } -static void normalizeFloat16Subnormal(uint32_t aSig, int_fast16_t *zExpPtr, +static void normalizeFloat16Subnormal(uint32_t aSig, int *zExpPtr, uint32_t *zSigPtr) { int8_t shiftCount = countLeadingZeros32(aSig) - 21; @@ -3469,7 +3479,7 @@ static void normalizeFloat16Subnormal(uint32_t aSig, int_fast16_t *zExpPtr, float32 float16_to_float32(float16 a, flag ieee, float_status *status) { flag aSign; - int_fast16_t aExp; + int aExp; uint32_t aSig; aSign = extractFloat16Sign(a); @@ -3496,7 +3506,7 @@ float32 float16_to_float32(float16 a, flag ieee, float_status *status) float16 float32_to_float16(float32 a, flag ieee, float_status *status) { flag aSign; - int_fast16_t aExp; + int aExp; uint32_t aSig; a = float32_squash_input_denormal(a, status); @@ -3540,7 +3550,7 @@ float16 float32_to_float16(float32 a, flag ieee, float_status *status) float64 float16_to_float64(float16 a, flag ieee, float_status *status) { flag aSign; - int_fast16_t aExp; + int aExp; uint32_t aSig; aSign = extractFloat16Sign(a); @@ -3568,7 +3578,7 @@ float64 float16_to_float64(float16 a, flag ieee, float_status *status) float16 float64_to_float16(float64 a, flag ieee, float_status *status) { flag aSign; - int_fast16_t aExp; + int aExp; uint64_t aSig; uint32_t zSig; @@ -3622,7 +3632,7 @@ float16 float64_to_float16(float64 a, flag ieee, float_status *status) floatx80 float64_to_floatx80(float64 a, float_status *status) { flag aSign; - int_fast16_t aExp; + int aExp; uint64_t aSig; a = float64_squash_input_denormal(a, status); @@ -3655,7 +3665,7 @@ floatx80 float64_to_floatx80(float64 a, float_status *status) float128 float64_to_float128(float64 a, float_status *status) { flag aSign; - int_fast16_t aExp; + int aExp; uint64_t aSig, zSig0, zSig1; a = float64_squash_input_denormal(a, status); @@ -3688,7 +3698,7 @@ float128 float64_to_float128(float64 a, float_status *status) float64 float64_round_to_int(float64 a, float_status *status) { flag aSign; - int_fast16_t aExp; + int aExp; uint64_t lastBitMask, roundBitsMask; uint64_t z; a = float64_squash_input_denormal(a, status); @@ -3782,9 +3792,9 @@ float64 float64_trunc_to_int(float64 a, float_status *status) static float64 addFloat64Sigs(float64 a, float64 b, flag zSign, float_status *status) { - int_fast16_t aExp, bExp, zExp; + int aExp, bExp, zExp; uint64_t aSig, bSig, zSig; - int_fast16_t expDiff; + int expDiff; aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -3868,9 +3878,9 @@ static float64 addFloat64Sigs(float64 a, float64 b, flag zSign, static float64 subFloat64Sigs(float64 a, float64 b, flag zSign, float_status *status) { - int_fast16_t aExp, bExp, zExp; + int aExp, bExp, zExp; uint64_t aSig, bSig, zSig; - int_fast16_t expDiff; + int expDiff; aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -3994,7 +4004,7 @@ float64 float64_sub(float64 a, float64 b, float_status *status) float64 float64_mul(float64 a, float64 b, float_status *status) { flag aSign, bSign, zSign; - int_fast16_t aExp, bExp, zExp; + int aExp, bExp, zExp; uint64_t aSig, bSig, zSig0, zSig1; a = float64_squash_input_denormal(a, status); @@ -4057,7 +4067,7 @@ float64 float64_mul(float64 a, float64 b, float_status *status) float64 float64_div(float64 a, float64 b, float_status *status) { flag aSign, bSign, zSign; - int_fast16_t aExp, bExp, zExp; + int aExp, bExp, zExp; uint64_t aSig, bSig, zSig; uint64_t rem0, rem1; uint64_t term0, term1; @@ -4135,7 +4145,7 @@ float64 float64_div(float64 a, float64 b, float_status *status) float64 float64_rem(float64 a, float64 b, float_status *status) { flag aSign, zSign; - int_fast16_t aExp, bExp, expDiff; + int aExp, bExp, expDiff; uint64_t aSig, bSig; uint64_t q, alternateASig; int64_t sigMean; @@ -4229,7 +4239,7 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags, float_status *status) { flag aSign, bSign, cSign, zSign; - int_fast16_t aExp, bExp, cExp, pExp, zExp, expDiff; + int aExp, bExp, cExp, pExp, zExp, expDiff; uint64_t aSig, bSig, cSig; flag pInf, pZero, pSign; uint64_t pSig0, pSig1, cSig0, cSig1, zSig0, zSig1; @@ -4470,7 +4480,7 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags, float64 float64_sqrt(float64 a, float_status *status) { flag aSign; - int_fast16_t aExp, zExp; + int aExp, zExp; uint64_t aSig, zSig, doubleZSig; uint64_t rem0, rem1, term0, term1; a = float64_squash_input_denormal(a, status); @@ -4523,7 +4533,7 @@ float64 float64_sqrt(float64 a, float_status *status) float64 float64_log2(float64 a, float_status *status) { flag aSign, zSign; - int_fast16_t aExp; + int aExp; uint64_t aSig, aSig0, aSig1, zSig, i; a = float64_squash_input_denormal(a, status); @@ -4790,10 +4800,10 @@ int float64_unordered_quiet(float64 a, float64 b, float_status *status) | overflows, the largest integer with the same sign as `a' is returned. *----------------------------------------------------------------------------*/ -int32 floatx80_to_int32(floatx80 a, float_status *status) +int32_t floatx80_to_int32(floatx80 a, float_status *status) { flag aSign; - int32 aExp, shiftCount; + int32_t aExp, shiftCount; uint64_t aSig; aSig = extractFloatx80Frac( a ); @@ -4817,10 +4827,10 @@ int32 floatx80_to_int32(floatx80 a, float_status *status) | sign as `a' is returned. *----------------------------------------------------------------------------*/ -int32 floatx80_to_int32_round_to_zero(floatx80 a, float_status *status) +int32_t floatx80_to_int32_round_to_zero(floatx80 a, float_status *status) { flag aSign; - int32 aExp, shiftCount; + int32_t aExp, shiftCount; uint64_t aSig, savedASig; int32_t z; @@ -4864,10 +4874,10 @@ int32 floatx80_to_int32_round_to_zero(floatx80 a, float_status *status) | overflows, the largest integer with the same sign as `a' is returned. *----------------------------------------------------------------------------*/ -int64 floatx80_to_int64(floatx80 a, float_status *status) +int64_t floatx80_to_int64(floatx80 a, float_status *status) { flag aSign; - int32 aExp, shiftCount; + int32_t aExp, shiftCount; uint64_t aSig, aSigExtra; aSig = extractFloatx80Frac( a ); @@ -4904,12 +4914,12 @@ int64 floatx80_to_int64(floatx80 a, float_status *status) | sign as `a' is returned. *----------------------------------------------------------------------------*/ -int64 floatx80_to_int64_round_to_zero(floatx80 a, float_status *status) +int64_t floatx80_to_int64_round_to_zero(floatx80 a, float_status *status) { flag aSign; - int32 aExp, shiftCount; + int32_t aExp, shiftCount; uint64_t aSig; - int64 z; + int64_t z; aSig = extractFloatx80Frac( a ); aExp = extractFloatx80Exp( a ); @@ -4950,7 +4960,7 @@ int64 floatx80_to_int64_round_to_zero(floatx80 a, float_status *status) float32 floatx80_to_float32(floatx80 a, float_status *status) { flag aSign; - int32 aExp; + int32_t aExp; uint64_t aSig; aSig = extractFloatx80Frac( a ); @@ -4978,7 +4988,7 @@ float32 floatx80_to_float32(floatx80 a, float_status *status) float64 floatx80_to_float64(floatx80 a, float_status *status) { flag aSign; - int32 aExp; + int32_t aExp; uint64_t aSig, zSig; aSig = extractFloatx80Frac( a ); @@ -5006,7 +5016,7 @@ float64 floatx80_to_float64(floatx80 a, float_status *status) float128 floatx80_to_float128(floatx80 a, float_status *status) { flag aSign; - int_fast16_t aExp; + int aExp; uint64_t aSig, zSig0, zSig1; aSig = extractFloatx80Frac( a ); @@ -5030,7 +5040,7 @@ float128 floatx80_to_float128(floatx80 a, float_status *status) floatx80 floatx80_round_to_int(floatx80 a, float_status *status) { flag aSign; - int32 aExp; + int32_t aExp; uint64_t lastBitMask, roundBitsMask; floatx80 z; @@ -5125,9 +5135,9 @@ floatx80 floatx80_round_to_int(floatx80 a, float_status *status) static floatx80 addFloatx80Sigs(floatx80 a, floatx80 b, flag zSign, float_status *status) { - int32 aExp, bExp, zExp; + int32_t aExp, bExp, zExp; uint64_t aSig, bSig, zSig0, zSig1; - int32 expDiff; + int32_t expDiff; aSig = extractFloatx80Frac( a ); aExp = extractFloatx80Exp( a ); @@ -5194,9 +5204,9 @@ static floatx80 addFloatx80Sigs(floatx80 a, floatx80 b, flag zSign, static floatx80 subFloatx80Sigs(floatx80 a, floatx80 b, flag zSign, float_status *status) { - int32 aExp, bExp, zExp; + int32_t aExp, bExp, zExp; uint64_t aSig, bSig, zSig0, zSig1; - int32 expDiff; + int32_t expDiff; floatx80 z; aSig = extractFloatx80Frac( a ); @@ -5305,7 +5315,7 @@ floatx80 floatx80_sub(floatx80 a, floatx80 b, float_status *status) floatx80 floatx80_mul(floatx80 a, floatx80 b, float_status *status) { flag aSign, bSign, zSign; - int32 aExp, bExp, zExp; + int32_t aExp, bExp, zExp; uint64_t aSig, bSig, zSig0, zSig1; floatx80 z; @@ -5364,7 +5374,7 @@ floatx80 floatx80_mul(floatx80 a, floatx80 b, float_status *status) floatx80 floatx80_div(floatx80 a, floatx80 b, float_status *status) { flag aSign, bSign, zSign; - int32 aExp, bExp, zExp; + int32_t aExp, bExp, zExp; uint64_t aSig, bSig, zSig0, zSig1; uint64_t rem0, rem1, rem2, term0, term1, term2; floatx80 z; @@ -5448,7 +5458,7 @@ floatx80 floatx80_div(floatx80 a, floatx80 b, float_status *status) floatx80 floatx80_rem(floatx80 a, floatx80 b, float_status *status) { flag aSign, zSign; - int32 aExp, bExp, expDiff; + int32_t aExp, bExp, expDiff; uint64_t aSig0, aSig1, bSig; uint64_t q, term0, term1, alternateASig0, alternateASig1; floatx80 z; @@ -5546,7 +5556,7 @@ floatx80 floatx80_rem(floatx80 a, floatx80 b, float_status *status) floatx80 floatx80_sqrt(floatx80 a, float_status *status) { flag aSign; - int32 aExp, zExp; + int32_t aExp, zExp; uint64_t aSig0, aSig1, zSig0, zSig1, doubleZSig0; uint64_t rem0, rem1, rem2, rem3, term0, term1, term2, term3; floatx80 z; @@ -5854,10 +5864,10 @@ int floatx80_unordered_quiet(floatx80 a, floatx80 b, float_status *status) | largest integer with the same sign as `a' is returned. *----------------------------------------------------------------------------*/ -int32 float128_to_int32(float128 a, float_status *status) +int32_t float128_to_int32(float128 a, float_status *status) { flag aSign; - int32 aExp, shiftCount; + int32_t aExp, shiftCount; uint64_t aSig0, aSig1; aSig1 = extractFloat128Frac1( a ); @@ -5883,10 +5893,10 @@ int32 float128_to_int32(float128 a, float_status *status) | returned. *----------------------------------------------------------------------------*/ -int32 float128_to_int32_round_to_zero(float128 a, float_status *status) +int32_t float128_to_int32_round_to_zero(float128 a, float_status *status) { flag aSign; - int32 aExp, shiftCount; + int32_t aExp, shiftCount; uint64_t aSig0, aSig1, savedASig; int32_t z; @@ -5933,10 +5943,10 @@ int32 float128_to_int32_round_to_zero(float128 a, float_status *status) | largest integer with the same sign as `a' is returned. *----------------------------------------------------------------------------*/ -int64 float128_to_int64(float128 a, float_status *status) +int64_t float128_to_int64(float128 a, float_status *status) { flag aSign; - int32 aExp, shiftCount; + int32_t aExp, shiftCount; uint64_t aSig0, aSig1; aSig1 = extractFloat128Frac1( a ); @@ -5976,12 +5986,12 @@ int64 float128_to_int64(float128 a, float_status *status) | returned. *----------------------------------------------------------------------------*/ -int64 float128_to_int64_round_to_zero(float128 a, float_status *status) +int64_t float128_to_int64_round_to_zero(float128 a, float_status *status) { flag aSign; - int32 aExp, shiftCount; + int32_t aExp, shiftCount; uint64_t aSig0, aSig1; - int64 z; + int64_t z; aSig1 = extractFloat128Frac1( a ); aSig0 = extractFloat128Frac0( a ); @@ -6039,7 +6049,7 @@ int64 float128_to_int64_round_to_zero(float128 a, float_status *status) float32 float128_to_float32(float128 a, float_status *status) { flag aSign; - int32 aExp; + int32_t aExp; uint64_t aSig0, aSig1; uint32_t zSig; @@ -6074,7 +6084,7 @@ float32 float128_to_float32(float128 a, float_status *status) float64 float128_to_float64(float128 a, float_status *status) { flag aSign; - int32 aExp; + int32_t aExp; uint64_t aSig0, aSig1; aSig1 = extractFloat128Frac1( a ); @@ -6107,7 +6117,7 @@ float64 float128_to_float64(float128 a, float_status *status) floatx80 float128_to_floatx80(float128 a, float_status *status) { flag aSign; - int32 aExp; + int32_t aExp; uint64_t aSig0, aSig1; aSig1 = extractFloat128Frac1( a ); @@ -6142,7 +6152,7 @@ floatx80 float128_to_floatx80(float128 a, float_status *status) float128 float128_round_to_int(float128 a, float_status *status) { flag aSign; - int32 aExp; + int32_t aExp; uint64_t lastBitMask, roundBitsMask; float128 z; @@ -6281,9 +6291,9 @@ float128 float128_round_to_int(float128 a, float_status *status) static float128 addFloat128Sigs(float128 a, float128 b, flag zSign, float_status *status) { - int32 aExp, bExp, zExp; + int32_t aExp, bExp, zExp; uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; - int32 expDiff; + int32_t expDiff; aSig1 = extractFloat128Frac1( a ); aSig0 = extractFloat128Frac0( a ); @@ -6372,9 +6382,9 @@ static float128 addFloat128Sigs(float128 a, float128 b, flag zSign, static float128 subFloat128Sigs(float128 a, float128 b, flag zSign, float_status *status) { - int32 aExp, bExp, zExp; + int32_t aExp, bExp, zExp; uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1; - int32 expDiff; + int32_t expDiff; float128 z; aSig1 = extractFloat128Frac1( a ); @@ -6503,7 +6513,7 @@ float128 float128_sub(float128 a, float128 b, float_status *status) float128 float128_mul(float128 a, float128 b, float_status *status) { flag aSign, bSign, zSign; - int32 aExp, bExp, zExp; + int32_t aExp, bExp, zExp; uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2, zSig3; float128 z; @@ -6569,7 +6579,7 @@ float128 float128_mul(float128 a, float128 b, float_status *status) float128 float128_div(float128 a, float128 b, float_status *status) { flag aSign, bSign, zSign; - int32 aExp, bExp, zExp; + int32_t aExp, bExp, zExp; uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; uint64_t rem0, rem1, rem2, rem3, term0, term1, term2, term3; float128 z; @@ -6659,7 +6669,7 @@ float128 float128_div(float128 a, float128 b, float_status *status) float128 float128_rem(float128 a, float128 b, float_status *status) { flag aSign, zSign; - int32 aExp, bExp, expDiff; + int32_t aExp, bExp, expDiff; uint64_t aSig0, aSig1, bSig0, bSig1, q, term0, term1, term2; uint64_t allZero, alternateASig0, alternateASig1, sigMean1; int64_t sigMean0; @@ -6769,7 +6779,7 @@ float128 float128_rem(float128 a, float128 b, float_status *status) float128 float128_sqrt(float128 a, float_status *status) { flag aSign; - int32 aExp, zExp; + int32_t aExp, zExp; uint64_t aSig0, aSig1, zSig0, zSig1, zSig2, doubleZSig0; uint64_t rem0, rem1, rem2, rem3, term0, term1, term2, term3; float128 z; @@ -7080,10 +7090,10 @@ float64 uint32_to_float64(uint32_t a, float_status *status) return int64_to_float64(a, status); } -uint32 float32_to_uint32(float32 a, float_status *status) +uint32_t float32_to_uint32(float32 a, float_status *status) { int64_t v; - uint32 res; + uint32_t res; int old_exc_flags = get_float_exception_flags(status); v = float32_to_int64(a, status); @@ -7099,10 +7109,10 @@ uint32 float32_to_uint32(float32 a, float_status *status) return res; } -uint32 float32_to_uint32_round_to_zero(float32 a, float_status *status) +uint32_t float32_to_uint32_round_to_zero(float32 a, float_status *status) { int64_t v; - uint32 res; + uint32_t res; int old_exc_flags = get_float_exception_flags(status); v = float32_to_int64_round_to_zero(a, status); @@ -7118,10 +7128,10 @@ uint32 float32_to_uint32_round_to_zero(float32 a, float_status *status) return res; } -int_fast16_t float32_to_int16(float32 a, float_status *status) +int16_t float32_to_int16(float32 a, float_status *status) { int32_t v; - int_fast16_t res; + int16_t res; int old_exc_flags = get_float_exception_flags(status); v = float32_to_int32(a, status); @@ -7138,10 +7148,10 @@ int_fast16_t float32_to_int16(float32 a, float_status *status) return res; } -uint_fast16_t float32_to_uint16(float32 a, float_status *status) +uint16_t float32_to_uint16(float32 a, float_status *status) { int32_t v; - uint_fast16_t res; + uint16_t res; int old_exc_flags = get_float_exception_flags(status); v = float32_to_int32(a, status); @@ -7158,10 +7168,10 @@ uint_fast16_t float32_to_uint16(float32 a, float_status *status) return res; } -uint_fast16_t float32_to_uint16_round_to_zero(float32 a, float_status *status) +uint16_t float32_to_uint16_round_to_zero(float32 a, float_status *status) { int64_t v; - uint_fast16_t res; + uint16_t res; int old_exc_flags = get_float_exception_flags(status); v = float32_to_int64_round_to_zero(a, status); @@ -7177,10 +7187,10 @@ uint_fast16_t float32_to_uint16_round_to_zero(float32 a, float_status *status) return res; } -uint32 float64_to_uint32(float64 a, float_status *status) +uint32_t float64_to_uint32(float64 a, float_status *status) { uint64_t v; - uint32 res; + uint32_t res; int old_exc_flags = get_float_exception_flags(status); v = float64_to_uint64(a, status); @@ -7194,10 +7204,10 @@ uint32 float64_to_uint32(float64 a, float_status *status) return res; } -uint32 float64_to_uint32_round_to_zero(float64 a, float_status *status) +uint32_t float64_to_uint32_round_to_zero(float64 a, float_status *status) { uint64_t v; - uint32 res; + uint32_t res; int old_exc_flags = get_float_exception_flags(status); v = float64_to_uint64_round_to_zero(a, status); @@ -7211,10 +7221,10 @@ uint32 float64_to_uint32_round_to_zero(float64 a, float_status *status) return res; } -int_fast16_t float64_to_int16(float64 a, float_status *status) +int16_t float64_to_int16(float64 a, float_status *status) { int64_t v; - int_fast16_t res; + int16_t res; int old_exc_flags = get_float_exception_flags(status); v = float64_to_int32(a, status); @@ -7231,10 +7241,10 @@ int_fast16_t float64_to_int16(float64 a, float_status *status) return res; } -uint_fast16_t float64_to_uint16(float64 a, float_status *status) +uint16_t float64_to_uint16(float64 a, float_status *status) { int64_t v; - uint_fast16_t res; + uint16_t res; int old_exc_flags = get_float_exception_flags(status); v = float64_to_int32(a, status); @@ -7251,10 +7261,10 @@ uint_fast16_t float64_to_uint16(float64 a, float_status *status) return res; } -uint_fast16_t float64_to_uint16_round_to_zero(float64 a, float_status *status) +uint16_t float64_to_uint16_round_to_zero(float64 a, float_status *status) { int64_t v; - uint_fast16_t res; + uint16_t res; int old_exc_flags = get_float_exception_flags(status); v = float64_to_int64_round_to_zero(a, status); @@ -7285,7 +7295,8 @@ uint_fast16_t float64_to_uint16_round_to_zero(float64 a, float_status *status) uint64_t float64_to_uint64(float64 a, float_status *status) { flag aSign; - int_fast16_t aExp, shiftCount; + int aExp; + int shiftCount; uint64_t aSig, aSigExtra; a = float64_squash_input_denormal(a, status); diff --git a/qemu/fsdev/9p-iov-marshal.c b/qemu/fsdev/9p-iov-marshal.c new file mode 100644 index 000000000..fb40bdf0d --- /dev/null +++ b/qemu/fsdev/9p-iov-marshal.c @@ -0,0 +1,314 @@ +/* + * 9p backend + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Anthony Liguori + * + * 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 +#include +#include +#include + +#include "9p-iov-marshal.h" +#include "qemu/bswap.h" + +static ssize_t v9fs_packunpack(void *addr, struct iovec *sg, int sg_count, + size_t offset, size_t size, int pack) +{ + int i = 0; + size_t copied = 0; + size_t req_size = size; + + + for (i = 0; size && i < sg_count; i++) { + size_t len; + if (offset >= sg[i].iov_len) { + /* skip this sg */ + offset -= sg[i].iov_len; + continue; + } else { + len = MIN(sg[i].iov_len - offset, size); + if (pack) { + memcpy(sg[i].iov_base + offset, addr, len); + } else { + memcpy(addr, sg[i].iov_base + offset, len); + } + size -= len; + copied += len; + addr += len; + if (size) { + offset = 0; + continue; + } + } + } + if (copied < req_size) { + /* + * We copied less that requested size. error out + */ + return -ENOBUFS; + } + return copied; +} + +static ssize_t v9fs_unpack(void *dst, struct iovec *out_sg, int out_num, + size_t offset, size_t size) +{ + return v9fs_packunpack(dst, out_sg, out_num, offset, size, 0); +} + +ssize_t v9fs_pack(struct iovec *in_sg, int in_num, size_t offset, + const void *src, size_t size) +{ + return v9fs_packunpack((void *)src, in_sg, in_num, offset, size, 1); +} + +ssize_t v9fs_iov_vunmarshal(struct iovec *out_sg, int out_num, size_t offset, + int bswap, const char *fmt, va_list ap) +{ + int i; + ssize_t copied = 0; + size_t old_offset = offset; + + for (i = 0; fmt[i]; i++) { + switch (fmt[i]) { + case 'b': { + uint8_t *valp = va_arg(ap, uint8_t *); + copied = v9fs_unpack(valp, out_sg, out_num, offset, sizeof(*valp)); + break; + } + case 'w': { + uint16_t val, *valp; + valp = va_arg(ap, uint16_t *); + copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val)); + if (bswap) { + *valp = le16_to_cpu(val); + } else { + *valp = val; + } + break; + } + case 'd': { + uint32_t val, *valp; + valp = va_arg(ap, uint32_t *); + copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val)); + if (bswap) { + *valp = le32_to_cpu(val); + } else { + *valp = val; + } + break; + } + case 'q': { + uint64_t val, *valp; + valp = va_arg(ap, uint64_t *); + copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val)); + if (bswap) { + *valp = le64_to_cpu(val); + } else { + *valp = val; + } + break; + } + case 's': { + V9fsString *str = va_arg(ap, V9fsString *); + copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap, + "w", &str->size); + if (copied > 0) { + offset += copied; + str->data = g_malloc(str->size + 1); + copied = v9fs_unpack(str->data, out_sg, out_num, offset, + str->size); + if (copied > 0) { + str->data[str->size] = 0; + } else { + v9fs_string_free(str); + } + } + break; + } + case 'Q': { + V9fsQID *qidp = va_arg(ap, V9fsQID *); + copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap, + "bdq", &qidp->type, &qidp->version, + &qidp->path); + break; + } + case 'S': { + V9fsStat *statp = va_arg(ap, V9fsStat *); + copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap, + "wwdQdddqsssssddd", + &statp->size, &statp->type, + &statp->dev, &statp->qid, + &statp->mode, &statp->atime, + &statp->mtime, &statp->length, + &statp->name, &statp->uid, + &statp->gid, &statp->muid, + &statp->extension, + &statp->n_uid, &statp->n_gid, + &statp->n_muid); + break; + } + case 'I': { + V9fsIattr *iattr = va_arg(ap, V9fsIattr *); + copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap, + "ddddqqqqq", + &iattr->valid, &iattr->mode, + &iattr->uid, &iattr->gid, + &iattr->size, &iattr->atime_sec, + &iattr->atime_nsec, + &iattr->mtime_sec, + &iattr->mtime_nsec); + break; + } + default: + break; + } + if (copied < 0) { + return copied; + } + offset += copied; + } + + return offset - old_offset; +} + +ssize_t v9fs_iov_unmarshal(struct iovec *out_sg, int out_num, size_t offset, + int bswap, const char *fmt, ...) +{ + ssize_t ret; + va_list ap; + + va_start(ap, fmt); + ret = v9fs_iov_vunmarshal(out_sg, out_num, offset, bswap, fmt, ap); + va_end(ap); + + return ret; +} + +ssize_t v9fs_iov_vmarshal(struct iovec *in_sg, int in_num, size_t offset, + int bswap, const char *fmt, va_list ap) +{ + int i; + ssize_t copied = 0; + size_t old_offset = offset; + + for (i = 0; fmt[i]; i++) { + switch (fmt[i]) { + case 'b': { + uint8_t val = va_arg(ap, int); + copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val)); + break; + } + case 'w': { + uint16_t val; + if (bswap) { + cpu_to_le16w(&val, va_arg(ap, int)); + } else { + val = va_arg(ap, int); + } + copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val)); + break; + } + case 'd': { + uint32_t val; + if (bswap) { + cpu_to_le32w(&val, va_arg(ap, uint32_t)); + } else { + val = va_arg(ap, uint32_t); + } + copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val)); + break; + } + case 'q': { + uint64_t val; + if (bswap) { + cpu_to_le64w(&val, va_arg(ap, uint64_t)); + } else { + val = va_arg(ap, uint64_t); + } + copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val)); + break; + } + case 's': { + V9fsString *str = va_arg(ap, V9fsString *); + copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap, + "w", str->size); + if (copied > 0) { + offset += copied; + copied = v9fs_pack(in_sg, in_num, offset, str->data, str->size); + } + break; + } + case 'Q': { + V9fsQID *qidp = va_arg(ap, V9fsQID *); + copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap, "bdq", + qidp->type, qidp->version, + qidp->path); + break; + } + case 'S': { + V9fsStat *statp = va_arg(ap, V9fsStat *); + copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap, + "wwdQdddqsssssddd", + statp->size, statp->type, statp->dev, + &statp->qid, statp->mode, statp->atime, + statp->mtime, statp->length, + &statp->name, + &statp->uid, &statp->gid, &statp->muid, + &statp->extension, statp->n_uid, + statp->n_gid, statp->n_muid); + break; + } + case 'A': { + V9fsStatDotl *statp = va_arg(ap, V9fsStatDotl *); + copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap, + "qQdddqqqqqqqqqqqqqqq", + statp->st_result_mask, + &statp->qid, statp->st_mode, + statp->st_uid, statp->st_gid, + statp->st_nlink, statp->st_rdev, + statp->st_size, statp->st_blksize, + statp->st_blocks, statp->st_atime_sec, + statp->st_atime_nsec, + statp->st_mtime_sec, + statp->st_mtime_nsec, + statp->st_ctime_sec, + statp->st_ctime_nsec, + statp->st_btime_sec, + statp->st_btime_nsec, statp->st_gen, + statp->st_data_version); + break; + } + default: + break; + } + if (copied < 0) { + return copied; + } + offset += copied; + } + + return offset - old_offset; +} + +ssize_t v9fs_iov_marshal(struct iovec *in_sg, int in_num, size_t offset, + int bswap, const char *fmt, ...) +{ + ssize_t ret; + va_list ap; + + va_start(ap, fmt); + ret = v9fs_iov_vmarshal(in_sg, in_num, offset, bswap, fmt, ap); + va_end(ap); + + return ret; +} diff --git a/qemu/fsdev/9p-iov-marshal.h b/qemu/fsdev/9p-iov-marshal.h new file mode 100644 index 000000000..6bccbfb41 --- /dev/null +++ b/qemu/fsdev/9p-iov-marshal.h @@ -0,0 +1,18 @@ +#ifndef _QEMU_9P_IOV_MARSHAL_H +#define _QEMU_9P_IOV_MARSHAL_H + +#include "9p-marshal.h" + + +ssize_t v9fs_pack(struct iovec *in_sg, int in_num, size_t offset, + const void *src, size_t size); +ssize_t v9fs_iov_unmarshal(struct iovec *out_sg, int out_num, size_t offset, + int bswap, const char *fmt, ...); +ssize_t v9fs_iov_marshal(struct iovec *in_sg, int in_num, size_t offset, + int bswap, const char *fmt, ...); + +ssize_t v9fs_iov_vunmarshal(struct iovec *out_sg, int out_num, size_t offset, + int bswap, const char *fmt, va_list ap); +ssize_t v9fs_iov_vmarshal(struct iovec *in_sg, int in_num, size_t offset, + int bswap, const char *fmt, va_list ap); +#endif diff --git a/qemu/fsdev/9p-marshal.c b/qemu/fsdev/9p-marshal.c new file mode 100644 index 000000000..183d3667c --- /dev/null +++ b/qemu/fsdev/9p-marshal.c @@ -0,0 +1,51 @@ +/* + * 9p backend + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Anthony Liguori + * + * 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 +#include +#include +#include +#include + +#include "9p-marshal.h" + +void v9fs_string_free(V9fsString *str) +{ + g_free(str->data); + str->data = NULL; + str->size = 0; +} + +void v9fs_string_null(V9fsString *str) +{ + v9fs_string_free(str); +} + +void GCC_FMT_ATTR(2, 3) +v9fs_string_sprintf(V9fsString *str, const char *fmt, ...) +{ + va_list ap; + + v9fs_string_free(str); + + va_start(ap, fmt); + str->size = g_vasprintf(&str->data, fmt, ap); + va_end(ap); +} + +void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs) +{ + v9fs_string_free(lhs); + v9fs_string_sprintf(lhs, "%s", rhs->data); +} diff --git a/qemu/fsdev/9p-marshal.h b/qemu/fsdev/9p-marshal.h new file mode 100644 index 000000000..e91b24e9c --- /dev/null +++ b/qemu/fsdev/9p-marshal.h @@ -0,0 +1,84 @@ +#ifndef _QEMU_9P_MARSHAL_H +#define _QEMU_9P_MARSHAL_H + +typedef struct V9fsString +{ + uint16_t size; + char *data; +} V9fsString; + +typedef struct V9fsQID +{ + int8_t type; + int32_t version; + int64_t path; +} V9fsQID; + +typedef struct V9fsStat +{ + int16_t size; + int16_t type; + int32_t dev; + V9fsQID qid; + int32_t mode; + int32_t atime; + int32_t mtime; + int64_t length; + V9fsString name; + V9fsString uid; + V9fsString gid; + V9fsString muid; + /* 9p2000.u */ + V9fsString extension; + int32_t n_uid; + int32_t n_gid; + int32_t n_muid; +} V9fsStat; + +typedef struct V9fsIattr +{ + int32_t valid; + int32_t mode; + int32_t uid; + int32_t gid; + int64_t size; + int64_t atime_sec; + int64_t atime_nsec; + int64_t mtime_sec; + int64_t mtime_nsec; +} V9fsIattr; + +typedef struct V9fsStatDotl { + uint64_t st_result_mask; + V9fsQID qid; + uint32_t st_mode; + uint32_t st_uid; + uint32_t st_gid; + uint64_t st_nlink; + uint64_t st_rdev; + uint64_t st_size; + uint64_t st_blksize; + uint64_t st_blocks; + uint64_t st_atime_sec; + uint64_t st_atime_nsec; + uint64_t st_mtime_sec; + uint64_t st_mtime_nsec; + uint64_t st_ctime_sec; + uint64_t st_ctime_nsec; + uint64_t st_btime_sec; + uint64_t st_btime_nsec; + uint64_t st_gen; + uint64_t st_data_version; +} V9fsStatDotl; + +static inline void v9fs_string_init(V9fsString *str) +{ + str->data = NULL; + str->size = 0; +} +extern void v9fs_string_free(V9fsString *str); +extern void v9fs_string_null(V9fsString *str); +extern void v9fs_string_sprintf(V9fsString *str, const char *fmt, ...); +extern void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs); + +#endif diff --git a/qemu/fsdev/Makefile.objs b/qemu/fsdev/Makefile.objs index c27dad3f6..1b120a4a7 100644 --- a/qemu/fsdev/Makefile.objs +++ b/qemu/fsdev/Makefile.objs @@ -1,7 +1,7 @@ ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy) # Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add. # only pull in the actual virtio-9p device if we also enabled virtio. -common-obj-y = qemu-fsdev.o virtio-9p-marshal.o +common-obj-y = qemu-fsdev.o 9p-marshal.o 9p-iov-marshal.o else common-obj-y = qemu-fsdev-dummy.o endif diff --git a/qemu/fsdev/file-op-9p.h b/qemu/fsdev/file-op-9p.h index 956fda091..b8c26024a 100644 --- a/qemu/fsdev/file-op-9p.h +++ b/qemu/fsdev/file-op-9p.h @@ -12,11 +12,8 @@ */ #ifndef _FILEOP_H #define _FILEOP_H -#include #include -#include #include -#include #include #include diff --git a/qemu/fsdev/qemu-fsdev-dummy.c b/qemu/fsdev/qemu-fsdev-dummy.c index 7dc2630a7..7622e86c1 100644 --- a/qemu/fsdev/qemu-fsdev-dummy.c +++ b/qemu/fsdev/qemu-fsdev-dummy.c @@ -10,8 +10,7 @@ * the COPYING file in the top-level directory. * */ -#include -#include +#include "qemu/osdep.h" #include "qemu-fsdev.h" #include "qemu/config-file.h" #include "qemu/module.h" diff --git a/qemu/fsdev/qemu-fsdev-opts.c b/qemu/fsdev/qemu-fsdev-opts.c index 6311c7a7e..88a4ac325 100644 --- a/qemu/fsdev/qemu-fsdev-opts.c +++ b/qemu/fsdev/qemu-fsdev-opts.c @@ -5,6 +5,7 @@ * later. See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "qemu/config-file.h" #include "qemu/option.h" #include "qemu/module.h" @@ -82,4 +83,4 @@ static void fsdev_register_config(void) qemu_add_opts(&qemu_fsdev_opts); qemu_add_opts(&qemu_virtfs_opts); } -machine_init(fsdev_register_config); +opts_init(fsdev_register_config); diff --git a/qemu/fsdev/qemu-fsdev.c b/qemu/fsdev/qemu-fsdev.c index ccfec139a..bf7f0b07f 100644 --- a/qemu/fsdev/qemu-fsdev.c +++ b/qemu/fsdev/qemu-fsdev.c @@ -10,13 +10,12 @@ * the COPYING file in the top-level directory. * */ -#include -#include +#include "qemu/osdep.h" #include "qemu-fsdev.h" #include "qemu/queue.h" -#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/config-file.h" +#include "qemu/error-report.h" static QTAILQ_HEAD(FsDriverEntry_head, FsDriverListEntry) fsdriver_entries = QTAILQ_HEAD_INITIALIZER(fsdriver_entries); @@ -40,7 +39,7 @@ int qemu_fsdev_add(QemuOpts *opts) bool ro = qemu_opt_get_bool(opts, "readonly", 0); if (!fsdev_id) { - fprintf(stderr, "fsdev: No id specified\n"); + error_report("fsdev: No id specified"); return -1; } @@ -52,11 +51,11 @@ int qemu_fsdev_add(QemuOpts *opts) } if (i == ARRAY_SIZE(FsDrivers)) { - fprintf(stderr, "fsdev: fsdriver %s not found\n", fsdriver); + error_report("fsdev: fsdriver %s not found", fsdriver); return -1; } } else { - fprintf(stderr, "fsdev: No fsdriver specified\n"); + error_report("fsdev: No fsdriver specified"); return -1; } diff --git a/qemu/fsdev/virtfs-proxy-helper.c b/qemu/fsdev/virtfs-proxy-helper.c index 9097d15c9..54f7ad1c4 100644 --- a/qemu/fsdev/virtfs-proxy-helper.c +++ b/qemu/fsdev/virtfs-proxy-helper.c @@ -9,6 +9,7 @@ * the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include #include #include @@ -23,9 +24,9 @@ #include "qemu-common.h" #include "qemu/sockets.h" #include "qemu/xattr.h" -#include "virtio-9p-marshal.h" -#include "hw/9pfs/virtio-9p-proxy.h" -#include "fsdev/virtio-9p-marshal.h" +#include "9p-iov-marshal.h" +#include "hw/9pfs/9p-proxy.h" +#include "fsdev/9p-iov-marshal.h" #define PROGNAME "virtfs-proxy-helper" @@ -1128,10 +1129,19 @@ int main(int argc, char **argv) } } + if (chdir("/") < 0) { + do_perror("chdir"); + goto error; + } + if (chroot(rpath) < 0) { + do_perror("chroot"); + goto error; + } + get_version = false; #ifdef FS_IOC_GETVERSION /* check whether underlying FS support IOC_GETVERSION */ - retval = statfs(rpath, &st_fs); + retval = statfs("/", &st_fs); if (!retval) { switch (st_fs.f_type) { case EXT2_SUPER_MAGIC: @@ -1144,16 +1154,7 @@ int main(int argc, char **argv) } #endif - if (chdir("/") < 0) { - do_perror("chdir"); - goto error; - } - if (chroot(rpath) < 0) { - do_perror("chroot"); - goto error; - } umask(0); - if (init_capabilities() < 0) { goto error; } diff --git a/qemu/fsdev/virtfs-proxy-helper.texi b/qemu/fsdev/virtfs-proxy-helper.texi index e60e3b946..6eb2d5096 100644 --- a/qemu/fsdev/virtfs-proxy-helper.texi +++ b/qemu/fsdev/virtfs-proxy-helper.texi @@ -1,6 +1,6 @@ @example @c man begin SYNOPSIS -usage: virtfs-proxy-helper options +@command{virtfs-proxy-helper} @var{options} @c man end @end example @@ -28,8 +28,8 @@ QEMU and proxy helper communicate using this socket. QEMU proxy fs driver sends filesystem request to proxy helper and receives the response from it. -Proxy helper is designed so that it can drop the root privilege with -retaining capbilities needed for doing filesystem operations only. +The proxy helper is designed so that it can drop root privileges except +for the capabilities needed for doing filesystem operations. @end table @c man end diff --git a/qemu/fsdev/virtio-9p-marshal.c b/qemu/fsdev/virtio-9p-marshal.c deleted file mode 100644 index 20f308b76..000000000 --- a/qemu/fsdev/virtio-9p-marshal.c +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Virtio 9p backend - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "qemu/compiler.h" -#include "virtio-9p-marshal.h" -#include "qemu/bswap.h" - -void v9fs_string_free(V9fsString *str) -{ - g_free(str->data); - str->data = NULL; - str->size = 0; -} - -void v9fs_string_null(V9fsString *str) -{ - v9fs_string_free(str); -} - -void GCC_FMT_ATTR(2, 3) -v9fs_string_sprintf(V9fsString *str, const char *fmt, ...) -{ - va_list ap; - - v9fs_string_free(str); - - va_start(ap, fmt); - str->size = g_vasprintf(&str->data, fmt, ap); - va_end(ap); -} - -void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs) -{ - v9fs_string_free(lhs); - v9fs_string_sprintf(lhs, "%s", rhs->data); -} - - -static ssize_t v9fs_packunpack(void *addr, struct iovec *sg, int sg_count, - size_t offset, size_t size, int pack) -{ - int i = 0; - size_t copied = 0; - size_t req_size = size; - - - for (i = 0; size && i < sg_count; i++) { - size_t len; - if (offset >= sg[i].iov_len) { - /* skip this sg */ - offset -= sg[i].iov_len; - continue; - } else { - len = MIN(sg[i].iov_len - offset, size); - if (pack) { - memcpy(sg[i].iov_base + offset, addr, len); - } else { - memcpy(addr, sg[i].iov_base + offset, len); - } - size -= len; - copied += len; - addr += len; - if (size) { - offset = 0; - continue; - } - } - } - if (copied < req_size) { - /* - * We copied less that requested size. error out - */ - return -ENOBUFS; - } - return copied; -} - -static ssize_t v9fs_unpack(void *dst, struct iovec *out_sg, int out_num, - size_t offset, size_t size) -{ - return v9fs_packunpack(dst, out_sg, out_num, offset, size, 0); -} - -ssize_t v9fs_pack(struct iovec *in_sg, int in_num, size_t offset, - const void *src, size_t size) -{ - return v9fs_packunpack((void *)src, in_sg, in_num, offset, size, 1); -} - -ssize_t v9fs_unmarshal(struct iovec *out_sg, int out_num, size_t offset, - int bswap, const char *fmt, ...) -{ - int i; - va_list ap; - ssize_t copied = 0; - size_t old_offset = offset; - - va_start(ap, fmt); - for (i = 0; fmt[i]; i++) { - switch (fmt[i]) { - case 'b': { - uint8_t *valp = va_arg(ap, uint8_t *); - copied = v9fs_unpack(valp, out_sg, out_num, offset, sizeof(*valp)); - break; - } - case 'w': { - uint16_t val, *valp; - valp = va_arg(ap, uint16_t *); - copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val)); - if (bswap) { - *valp = le16_to_cpu(val); - } else { - *valp = val; - } - break; - } - case 'd': { - uint32_t val, *valp; - valp = va_arg(ap, uint32_t *); - copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val)); - if (bswap) { - *valp = le32_to_cpu(val); - } else { - *valp = val; - } - break; - } - case 'q': { - uint64_t val, *valp; - valp = va_arg(ap, uint64_t *); - copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val)); - if (bswap) { - *valp = le64_to_cpu(val); - } else { - *valp = val; - } - break; - } - case 's': { - V9fsString *str = va_arg(ap, V9fsString *); - copied = v9fs_unmarshal(out_sg, out_num, offset, bswap, - "w", &str->size); - if (copied > 0) { - offset += copied; - str->data = g_malloc(str->size + 1); - copied = v9fs_unpack(str->data, out_sg, out_num, offset, - str->size); - if (copied > 0) { - str->data[str->size] = 0; - } else { - v9fs_string_free(str); - } - } - break; - } - case 'Q': { - V9fsQID *qidp = va_arg(ap, V9fsQID *); - copied = v9fs_unmarshal(out_sg, out_num, offset, bswap, "bdq", - &qidp->type, &qidp->version, &qidp->path); - break; - } - case 'S': { - V9fsStat *statp = va_arg(ap, V9fsStat *); - copied = v9fs_unmarshal(out_sg, out_num, offset, bswap, - "wwdQdddqsssssddd", - &statp->size, &statp->type, &statp->dev, - &statp->qid, &statp->mode, &statp->atime, - &statp->mtime, &statp->length, - &statp->name, &statp->uid, &statp->gid, - &statp->muid, &statp->extension, - &statp->n_uid, &statp->n_gid, - &statp->n_muid); - break; - } - case 'I': { - V9fsIattr *iattr = va_arg(ap, V9fsIattr *); - copied = v9fs_unmarshal(out_sg, out_num, offset, bswap, - "ddddqqqqq", - &iattr->valid, &iattr->mode, - &iattr->uid, &iattr->gid, &iattr->size, - &iattr->atime_sec, &iattr->atime_nsec, - &iattr->mtime_sec, &iattr->mtime_nsec); - break; - } - default: - break; - } - if (copied < 0) { - va_end(ap); - return copied; - } - offset += copied; - } - va_end(ap); - - return offset - old_offset; -} - -ssize_t v9fs_marshal(struct iovec *in_sg, int in_num, size_t offset, - int bswap, const char *fmt, ...) -{ - int i; - va_list ap; - ssize_t copied = 0; - size_t old_offset = offset; - - va_start(ap, fmt); - for (i = 0; fmt[i]; i++) { - switch (fmt[i]) { - case 'b': { - uint8_t val = va_arg(ap, int); - copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val)); - break; - } - case 'w': { - uint16_t val; - if (bswap) { - cpu_to_le16w(&val, va_arg(ap, int)); - } else { - val = va_arg(ap, int); - } - copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val)); - break; - } - case 'd': { - uint32_t val; - if (bswap) { - cpu_to_le32w(&val, va_arg(ap, uint32_t)); - } else { - val = va_arg(ap, uint32_t); - } - copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val)); - break; - } - case 'q': { - uint64_t val; - if (bswap) { - cpu_to_le64w(&val, va_arg(ap, uint64_t)); - } else { - val = va_arg(ap, uint64_t); - } - copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val)); - break; - } - case 's': { - V9fsString *str = va_arg(ap, V9fsString *); - copied = v9fs_marshal(in_sg, in_num, offset, bswap, - "w", str->size); - if (copied > 0) { - offset += copied; - copied = v9fs_pack(in_sg, in_num, offset, str->data, str->size); - } - break; - } - case 'Q': { - V9fsQID *qidp = va_arg(ap, V9fsQID *); - copied = v9fs_marshal(in_sg, in_num, offset, bswap, "bdq", - qidp->type, qidp->version, qidp->path); - break; - } - case 'S': { - V9fsStat *statp = va_arg(ap, V9fsStat *); - copied = v9fs_marshal(in_sg, in_num, offset, bswap, - "wwdQdddqsssssddd", - statp->size, statp->type, statp->dev, - &statp->qid, statp->mode, statp->atime, - statp->mtime, statp->length, &statp->name, - &statp->uid, &statp->gid, &statp->muid, - &statp->extension, statp->n_uid, - statp->n_gid, statp->n_muid); - break; - } - case 'A': { - V9fsStatDotl *statp = va_arg(ap, V9fsStatDotl *); - copied = v9fs_marshal(in_sg, in_num, offset, bswap, - "qQdddqqqqqqqqqqqqqqq", - statp->st_result_mask, - &statp->qid, statp->st_mode, - statp->st_uid, statp->st_gid, - statp->st_nlink, statp->st_rdev, - statp->st_size, statp->st_blksize, - statp->st_blocks, statp->st_atime_sec, - statp->st_atime_nsec, statp->st_mtime_sec, - statp->st_mtime_nsec, statp->st_ctime_sec, - statp->st_ctime_nsec, statp->st_btime_sec, - statp->st_btime_nsec, statp->st_gen, - statp->st_data_version); - break; - } - default: - break; - } - if (copied < 0) { - va_end(ap); - return copied; - } - offset += copied; - } - va_end(ap); - - return offset - old_offset; -} diff --git a/qemu/fsdev/virtio-9p-marshal.h b/qemu/fsdev/virtio-9p-marshal.h deleted file mode 100644 index 5df65a835..000000000 --- a/qemu/fsdev/virtio-9p-marshal.h +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef _QEMU_VIRTIO_9P_MARSHAL_H -#define _QEMU_VIRTIO_9P_MARSHAL_H - -typedef struct V9fsString -{ - uint16_t size; - char *data; -} V9fsString; - -typedef struct V9fsQID -{ - int8_t type; - int32_t version; - int64_t path; -} V9fsQID; - -typedef struct V9fsStat -{ - int16_t size; - int16_t type; - int32_t dev; - V9fsQID qid; - int32_t mode; - int32_t atime; - int32_t mtime; - int64_t length; - V9fsString name; - V9fsString uid; - V9fsString gid; - V9fsString muid; - /* 9p2000.u */ - V9fsString extension; - int32_t n_uid; - int32_t n_gid; - int32_t n_muid; -} V9fsStat; - -typedef struct V9fsIattr -{ - int32_t valid; - int32_t mode; - int32_t uid; - int32_t gid; - int64_t size; - int64_t atime_sec; - int64_t atime_nsec; - int64_t mtime_sec; - int64_t mtime_nsec; -} V9fsIattr; - -typedef struct V9fsStatDotl { - uint64_t st_result_mask; - V9fsQID qid; - uint32_t st_mode; - uint32_t st_uid; - uint32_t st_gid; - uint64_t st_nlink; - uint64_t st_rdev; - uint64_t st_size; - uint64_t st_blksize; - uint64_t st_blocks; - uint64_t st_atime_sec; - uint64_t st_atime_nsec; - uint64_t st_mtime_sec; - uint64_t st_mtime_nsec; - uint64_t st_ctime_sec; - uint64_t st_ctime_nsec; - uint64_t st_btime_sec; - uint64_t st_btime_nsec; - uint64_t st_gen; - uint64_t st_data_version; -} V9fsStatDotl; - -static inline void v9fs_string_init(V9fsString *str) -{ - str->data = NULL; - str->size = 0; -} -extern void v9fs_string_free(V9fsString *str); -extern void v9fs_string_null(V9fsString *str); -extern void v9fs_string_sprintf(V9fsString *str, const char *fmt, ...); -extern void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs); - -ssize_t v9fs_pack(struct iovec *in_sg, int in_num, size_t offset, - const void *src, size_t size); -ssize_t v9fs_unmarshal(struct iovec *out_sg, int out_num, size_t offset, - int bswap, const char *fmt, ...); -ssize_t v9fs_marshal(struct iovec *in_sg, int in_num, size_t offset, - int bswap, const char *fmt, ...); -#endif diff --git a/qemu/gdb-xml/power-vsx.xml b/qemu/gdb-xml/power-vsx.xml new file mode 100644 index 000000000..fd290e970 --- /dev/null +++ b/qemu/gdb-xml/power-vsx.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qemu/gdb-xml/s390-cr.xml b/qemu/gdb-xml/s390-cr.xml new file mode 100644 index 000000000..5246beaab --- /dev/null +++ b/qemu/gdb-xml/s390-cr.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/qemu/gdb-xml/s390-virt.xml b/qemu/gdb-xml/s390-virt.xml new file mode 100644 index 000000000..e2e9a7ad3 --- /dev/null +++ b/qemu/gdb-xml/s390-virt.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/qemu/gdbstub.c b/qemu/gdbstub.c index ffe7e6efb..0e431fd4d 100644 --- a/qemu/gdbstub.c +++ b/qemu/gdbstub.c @@ -16,17 +16,11 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" -#include "qemu-common.h" -#ifdef CONFIG_USER_ONLY -#include -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/cutils.h" +#ifdef CONFIG_USER_ONLY #include "qemu.h" #else #include "monitor/monitor.h" @@ -540,13 +534,20 @@ static const char *get_feature_xml(const char *p, const char **newp, GDBRegisterState *r; CPUState *cpu = first_cpu; - snprintf(target_xml, sizeof(target_xml), - "" - "" - "" - "", - cc->gdb_core_xml_file); - + pstrcat(target_xml, sizeof(target_xml), + "" + "" + ""); + if (cc->gdb_arch_name) { + gchar *arch = cc->gdb_arch_name(cpu); + pstrcat(target_xml, sizeof(target_xml), ""); + pstrcat(target_xml, sizeof(target_xml), arch); + pstrcat(target_xml, sizeof(target_xml), ""); + g_free(arch); + } + pstrcat(target_xml, sizeof(target_xml), "gdb_core_xml_file); + pstrcat(target_xml, sizeof(target_xml), "\"/>"); for (r = cpu->gdb_regs; r; r = r->next) { pstrcat(target_xml, sizeof(target_xml), "xml); @@ -956,6 +957,13 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) if (*p == ',') p++; len = strtoull(p, NULL, 16); + + /* memtohex() doubles the required space */ + if (len > MAX_PACKET_LENGTH / 2) { + put_packet (s, "E22"); + break; + } + if (target_memory_rw_debug(s->g_cpu, addr, mem_buf, len, false) != 0) { put_packet (s, "E14"); } else { @@ -970,6 +978,12 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) len = strtoull(p, (char **)&p, 16); if (*p == ':') p++; + + /* hextomem() reads 2*len bytes */ + if (len > strlen(p) / 2) { + put_packet (s, "E22"); + break; + } hextomem(mem_buf, p, len); if (target_memory_rw_debug(s->g_cpu, addr, mem_buf, len, true) != 0) { @@ -1107,7 +1121,8 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) cpu = find_cpu(thread); if (cpu != NULL) { cpu_synchronize_state(cpu); - len = snprintf((char *)mem_buf, sizeof(mem_buf), + /* memtohex() doubles the required space */ + len = snprintf((char *)mem_buf, sizeof(buf) / 2, "CPU#%d [%s]", cpu->cpu_index, cpu->halted ? "halted " : "running"); memtohex(buf, mem_buf, len); @@ -1136,8 +1151,8 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet(s, "E01"); break; } - hextomem(mem_buf, p + 5, len); len = len / 2; + hextomem(mem_buf, p + 5, len); mem_buf[len++] = 0; qemu_chr_be_write(s->mon_chr, mem_buf, len); put_packet(s, "OK"); @@ -1301,9 +1316,8 @@ send_packet: %x - target_ulong argument printed in hex. %lx - 64-bit argument printed in hex. %s - string pointer (target_ulong) and length (int) pair. */ -void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) +void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va) { - va_list va; char *p; char *p_end; target_ulong addr; @@ -1317,7 +1331,6 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) #ifndef CONFIG_USER_ONLY vm_stop(RUN_STATE_DEBUG); #endif - va_start(va, fmt); p = s->syscall_buf; p_end = &s->syscall_buf[sizeof(s->syscall_buf)]; *(p++) = 'F'; @@ -1351,7 +1364,6 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) } } *p = 0; - va_end(va); #ifdef CONFIG_USER_ONLY put_packet(s, s->syscall_buf); gdb_handlesig(s->c_cpu, 0); @@ -1362,10 +1374,19 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) is still in the running state, which can cause packets to be dropped and state transition 'T' packets to be sent while the syscall is still being processed. */ - cpu_exit(s->c_cpu); + qemu_cpu_kick(s->c_cpu); #endif } +void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + gdb_do_syscallv(cb, fmt, va); + va_end(va); +} + static void gdb_read_byte(GDBState *s, int ch) { int i, csum; @@ -1712,6 +1733,7 @@ int gdbserver_start(const char *device) char gdbstub_device_name[128]; CharDriverState *chr = NULL; CharDriverState *mon_chr; + ChardevCommon common = { 0 }; if (!device) return -1; @@ -1731,7 +1753,7 @@ int gdbserver_start(const char *device) sigaction(SIGINT, &act, NULL); } #endif - chr = qemu_chr_new("gdb", device, NULL); + chr = qemu_chr_new_noreplay("gdb", device, NULL); if (!chr) return -1; @@ -1748,7 +1770,7 @@ int gdbserver_start(const char *device) qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL); /* Initialize a monitor terminal for gdb */ - mon_chr = qemu_chr_alloc(); + mon_chr = qemu_chr_alloc(&common, &error_abort); mon_chr->chr_write = gdb_monitor_write; monitor_init(mon_chr, 0); } else { diff --git a/qemu/hmp-commands-info.hx b/qemu/hmp-commands-info.hx new file mode 100644 index 000000000..52539c310 --- /dev/null +++ b/qemu/hmp-commands-info.hx @@ -0,0 +1,809 @@ +HXCOMM Use DEFHEADING() to define headings in both help text and texi +HXCOMM Text between STEXI and ETEXI are copied to texi version and +HXCOMM discarded from C version +HXCOMM DEF(command, args, callback, arg_string, help) is used to construct +HXCOMM monitor info commands +HXCOMM HXCOMM can be used for comments, discarded from both texi and C + +STEXI +@table @option +@item info @var{subcommand} +@findex info +Show various information about the system state. +@table @option +ETEXI + + { + .name = "version", + .args_type = "", + .params = "", + .help = "show the version of QEMU", + .mhandler.cmd = hmp_info_version, + }, + +STEXI +@item info version +@findex version +Show the version of QEMU. +ETEXI + + { + .name = "network", + .args_type = "", + .params = "", + .help = "show the network state", + .mhandler.cmd = hmp_info_network, + }, + +STEXI +@item info network +@findex network +Show the network state. +ETEXI + + { + .name = "chardev", + .args_type = "", + .params = "", + .help = "show the character devices", + .mhandler.cmd = hmp_info_chardev, + }, + +STEXI +@item info chardev +@findex chardev +Show the character devices. +ETEXI + + { + .name = "block", + .args_type = "nodes:-n,verbose:-v,device:B?", + .params = "[-n] [-v] [device]", + .help = "show info of one block device or all block devices " + "(-n: show named nodes; -v: show details)", + .mhandler.cmd = hmp_info_block, + }, + +STEXI +@item info block +@findex block +Show info of one block device or all block devices. +ETEXI + + { + .name = "blockstats", + .args_type = "", + .params = "", + .help = "show block device statistics", + .mhandler.cmd = hmp_info_blockstats, + }, + +STEXI +@item info blockstats +@findex blockstats +Show block device statistics. +ETEXI + + { + .name = "block-jobs", + .args_type = "", + .params = "", + .help = "show progress of ongoing block device operations", + .mhandler.cmd = hmp_info_block_jobs, + }, + +STEXI +@item info block-jobs +@findex block-jobs +Show progress of ongoing block device operations. +ETEXI + + { + .name = "registers", + .args_type = "", + .params = "", + .help = "show the cpu registers", + .mhandler.cmd = hmp_info_registers, + }, + +STEXI +@item info registers +@findex registers +Show the cpu registers. +ETEXI + +#if defined(TARGET_I386) + { + .name = "lapic", + .args_type = "", + .params = "", + .help = "show local apic state", + .mhandler.cmd = hmp_info_local_apic, + }, +#endif + +STEXI +@item info lapic +@findex lapic +Show local APIC state +ETEXI + +#if defined(TARGET_I386) + { + .name = "ioapic", + .args_type = "", + .params = "", + .help = "show io apic state", + .mhandler.cmd = hmp_info_io_apic, + }, +#endif + +STEXI +@item info ioapic +@findex ioapic +Show io APIC state +ETEXI + + { + .name = "cpus", + .args_type = "", + .params = "", + .help = "show infos for each CPU", + .mhandler.cmd = hmp_info_cpus, + }, + +STEXI +@item info cpus +@findex cpus +Show infos for each CPU. +ETEXI + + { + .name = "history", + .args_type = "", + .params = "", + .help = "show the command line history", + .mhandler.cmd = hmp_info_history, + }, + +STEXI +@item info history +@findex history +Show the command line history. +ETEXI + +#if defined(TARGET_I386) || defined(TARGET_PPC) || defined(TARGET_MIPS) || \ + defined(TARGET_LM32) || (defined(TARGET_SPARC) && !defined(TARGET_SPARC64)) + { + .name = "irq", + .args_type = "", + .params = "", + .help = "show the interrupts statistics (if available)", +#ifdef TARGET_SPARC + .mhandler.cmd = sun4m_hmp_info_irq, +#elif defined(TARGET_LM32) + .mhandler.cmd = lm32_hmp_info_irq, +#else + .mhandler.cmd = hmp_info_irq, +#endif + }, + +STEXI +@item info irq +@findex irq +Show the interrupts statistics (if available). +ETEXI + + { + .name = "pic", + .args_type = "", + .params = "", + .help = "show i8259 (PIC) state", +#ifdef TARGET_SPARC + .mhandler.cmd = sun4m_hmp_info_pic, +#elif defined(TARGET_LM32) + .mhandler.cmd = lm32_hmp_info_pic, +#else + .mhandler.cmd = hmp_info_pic, +#endif + }, +#endif + +STEXI +@item info pic +@findex pic +Show i8259 (PIC) state. +ETEXI + + { + .name = "pci", + .args_type = "", + .params = "", + .help = "show PCI info", + .mhandler.cmd = hmp_info_pci, + }, + +STEXI +@item info pci +@findex pci +Show PCI information. +ETEXI + +#if defined(TARGET_I386) || defined(TARGET_SH4) || defined(TARGET_SPARC) || \ + defined(TARGET_PPC) || defined(TARGET_XTENSA) + { + .name = "tlb", + .args_type = "", + .params = "", + .help = "show virtual to physical memory mappings", + .mhandler.cmd = hmp_info_tlb, + }, +#endif + +STEXI +@item info tlb +@findex tlb +Show virtual to physical memory mappings. +ETEXI + +#if defined(TARGET_I386) + { + .name = "mem", + .args_type = "", + .params = "", + .help = "show the active virtual memory mappings", + .mhandler.cmd = hmp_info_mem, + }, +#endif + +STEXI +@item info mem +@findex mem +Show the active virtual memory mappings. +ETEXI + + { + .name = "mtree", + .args_type = "", + .params = "", + .help = "show memory tree", + .mhandler.cmd = hmp_info_mtree, + }, + +STEXI +@item info mtree +@findex mtree +Show memory tree. +ETEXI + + { + .name = "jit", + .args_type = "", + .params = "", + .help = "show dynamic compiler info", + .mhandler.cmd = hmp_info_jit, + }, + +STEXI +@item info jit +@findex jit +Show dynamic compiler info. +ETEXI + + { + .name = "opcount", + .args_type = "", + .params = "", + .help = "show dynamic compiler opcode counters", + .mhandler.cmd = hmp_info_opcount, + }, + +STEXI +@item info opcount +@findex opcount +Show dynamic compiler opcode counters +ETEXI + + { + .name = "kvm", + .args_type = "", + .params = "", + .help = "show KVM information", + .mhandler.cmd = hmp_info_kvm, + }, + +STEXI +@item info kvm +@findex kvm +Show KVM information. +ETEXI + + { + .name = "numa", + .args_type = "", + .params = "", + .help = "show NUMA information", + .mhandler.cmd = hmp_info_numa, + }, + +STEXI +@item info numa +@findex numa +Show NUMA information. +ETEXI + + { + .name = "usb", + .args_type = "", + .params = "", + .help = "show guest USB devices", + .mhandler.cmd = hmp_info_usb, + }, + +STEXI +@item info usb +@findex usb +Show guest USB devices. +ETEXI + + { + .name = "usbhost", + .args_type = "", + .params = "", + .help = "show host USB devices", + .mhandler.cmd = hmp_info_usbhost, + }, + +STEXI +@item info usbhost +@findex usbhost +Show host USB devices. +ETEXI + + { + .name = "profile", + .args_type = "", + .params = "", + .help = "show profiling information", + .mhandler.cmd = hmp_info_profile, + }, + +STEXI +@item info profile +@findex profile +Show profiling information. +ETEXI + + { + .name = "capture", + .args_type = "", + .params = "", + .help = "show capture information", + .mhandler.cmd = hmp_info_capture, + }, + +STEXI +@item info capture +@findex capture +Show capture information. +ETEXI + + { + .name = "snapshots", + .args_type = "", + .params = "", + .help = "show the currently saved VM snapshots", + .mhandler.cmd = hmp_info_snapshots, + }, + +STEXI +@item info snapshots +@findex snapshots +Show the currently saved VM snapshots. +ETEXI + + { + .name = "status", + .args_type = "", + .params = "", + .help = "show the current VM status (running|paused)", + .mhandler.cmd = hmp_info_status, + }, + +STEXI +@item info status +@findex status +Show the current VM status (running|paused). +ETEXI + + { + .name = "mice", + .args_type = "", + .params = "", + .help = "show which guest mouse is receiving events", + .mhandler.cmd = hmp_info_mice, + }, + +STEXI +@item info mice +@findex mice +Show which guest mouse is receiving events. +ETEXI + + { + .name = "vnc", + .args_type = "", + .params = "", + .help = "show the vnc server status", + .mhandler.cmd = hmp_info_vnc, + }, + +STEXI +@item info vnc +@findex vnc +Show the vnc server status. +ETEXI + +#if defined(CONFIG_SPICE) + { + .name = "spice", + .args_type = "", + .params = "", + .help = "show the spice server status", + .mhandler.cmd = hmp_info_spice, + }, +#endif + +STEXI +@item info spice +@findex spice +Show the spice server status. +ETEXI + + { + .name = "name", + .args_type = "", + .params = "", + .help = "show the current VM name", + .mhandler.cmd = hmp_info_name, + }, + +STEXI +@item info name +@findex name +Show the current VM name. +ETEXI + + { + .name = "uuid", + .args_type = "", + .params = "", + .help = "show the current VM UUID", + .mhandler.cmd = hmp_info_uuid, + }, + +STEXI +@item info uuid +@findex uuid +Show the current VM UUID. +ETEXI + + { + .name = "cpustats", + .args_type = "", + .params = "", + .help = "show CPU statistics", + .mhandler.cmd = hmp_info_cpustats, + }, + +STEXI +@item info cpustats +@findex cpustats +Show CPU statistics. +ETEXI + +#if defined(CONFIG_SLIRP) + { + .name = "usernet", + .args_type = "", + .params = "", + .help = "show user network stack connection states", + .mhandler.cmd = hmp_info_usernet, + }, +#endif + +STEXI +@item info usernet +@findex usernet +Show user network stack connection states. +ETEXI + + { + .name = "migrate", + .args_type = "", + .params = "", + .help = "show migration status", + .mhandler.cmd = hmp_info_migrate, + }, + +STEXI +@item info migrate +@findex migrate +Show migration status. +ETEXI + + { + .name = "migrate_capabilities", + .args_type = "", + .params = "", + .help = "show current migration capabilities", + .mhandler.cmd = hmp_info_migrate_capabilities, + }, + +STEXI +@item info migrate_capabilities +@findex migrate_capabilities +Show current migration capabilities. +ETEXI + + { + .name = "migrate_parameters", + .args_type = "", + .params = "", + .help = "show current migration parameters", + .mhandler.cmd = hmp_info_migrate_parameters, + }, + +STEXI +@item info migrate_parameters +@findex migrate_parameters +Show current migration parameters. +ETEXI + + { + .name = "migrate_cache_size", + .args_type = "", + .params = "", + .help = "show current migration xbzrle cache size", + .mhandler.cmd = hmp_info_migrate_cache_size, + }, + +STEXI +@item info migrate_cache_size +@findex migrate_cache_size +Show current migration xbzrle cache size. +ETEXI + + { + .name = "balloon", + .args_type = "", + .params = "", + .help = "show balloon information", + .mhandler.cmd = hmp_info_balloon, + }, + +STEXI +@item info balloon +@findex balloon +Show balloon information. +ETEXI + + { + .name = "qtree", + .args_type = "", + .params = "", + .help = "show device tree", + .mhandler.cmd = hmp_info_qtree, + }, + +STEXI +@item info qtree +@findex qtree +Show device tree. +ETEXI + + { + .name = "qdm", + .args_type = "", + .params = "", + .help = "show qdev device model list", + .mhandler.cmd = hmp_info_qdm, + }, + +STEXI +@item info qdm +@findex qdm +Show qdev device model list. +ETEXI + + { + .name = "qom-tree", + .args_type = "path:s?", + .params = "[path]", + .help = "show QOM composition tree", + .mhandler.cmd = hmp_info_qom_tree, + }, + +STEXI +@item info qom-tree +@findex qom-tree +Show QOM composition tree. +ETEXI + + { + .name = "roms", + .args_type = "", + .params = "", + .help = "show roms", + .mhandler.cmd = hmp_info_roms, + }, + +STEXI +@item info roms +@findex roms +Show roms. +ETEXI + + { + .name = "trace-events", + .args_type = "", + .params = "", + .help = "show available trace-events & their state", + .mhandler.cmd = hmp_info_trace_events, + }, + +STEXI +@item info trace-events +@findex trace-events +Show available trace-events & their state. +ETEXI + + { + .name = "tpm", + .args_type = "", + .params = "", + .help = "show the TPM device", + .mhandler.cmd = hmp_info_tpm, + }, + +STEXI +@item info tpm +@findex tpm +Show the TPM device. +ETEXI + + { + .name = "memdev", + .args_type = "", + .params = "", + .help = "show memory backends", + .mhandler.cmd = hmp_info_memdev, + }, + +STEXI +@item info memdev +@findex memdev +Show memory backends +ETEXI + + { + .name = "memory-devices", + .args_type = "", + .params = "", + .help = "show memory devices", + .mhandler.cmd = hmp_info_memory_devices, + }, + +STEXI +@item info memory-devices +@findex memory-devices +Show memory devices. +ETEXI + + { + .name = "iothreads", + .args_type = "", + .params = "", + .help = "show iothreads", + .mhandler.cmd = hmp_info_iothreads, + }, + +STEXI +@item info iothreads +@findex iothreads +Show iothread's identifiers. +ETEXI + + { + .name = "rocker", + .args_type = "name:s", + .params = "name", + .help = "Show rocker switch", + .mhandler.cmd = hmp_rocker, + }, + +STEXI +@item info rocker @var{name} +@findex rocker +Show rocker switch. +ETEXI + + { + .name = "rocker-ports", + .args_type = "name:s", + .params = "name", + .help = "Show rocker ports", + .mhandler.cmd = hmp_rocker_ports, + }, + +STEXI +@item info rocker_ports @var{name}-ports +@findex ocker-ports +Show rocker ports. +ETEXI + + { + .name = "rocker-of-dpa-flows", + .args_type = "name:s,tbl_id:i?", + .params = "name [tbl_id]", + .help = "Show rocker OF-DPA flow tables", + .mhandler.cmd = hmp_rocker_of_dpa_flows, + }, + +STEXI +@item info rocker_of_dpa_flows @var{name} [@var{tbl_id}] +@findex rocker-of-dpa-flows +Show rocker OF-DPA flow tables. +ETEXI + + { + .name = "rocker-of-dpa-groups", + .args_type = "name:s,type:i?", + .params = "name [type]", + .help = "Show rocker OF-DPA groups", + .mhandler.cmd = hmp_rocker_of_dpa_groups, + }, + +STEXI +@item info rocker-of-dpa-groups @var{name} [@var{type}] +@findex rocker-of-dpa-groups +Show rocker OF-DPA groups. +ETEXI + +#if defined(TARGET_S390X) + { + .name = "skeys", + .args_type = "addr:l", + .params = "address", + .help = "Display the value of a storage key", + .mhandler.cmd = hmp_info_skeys, + }, +#endif + +STEXI +@item info skeys @var{address} +@findex skeys +Display the value of a storage key (s390 only) +ETEXI + + { + .name = "dump", + .args_type = "", + .params = "", + .help = "Display the latest dump status", + .mhandler.cmd = hmp_info_dump, + }, + +STEXI +@item info dump +@findex dump +Display the latest dump status. +ETEXI + +STEXI +@end table +ETEXI + +STEXI +@end table +ETEXI diff --git a/qemu/hmp-commands.hx b/qemu/hmp-commands.hx index d3b7932ff..4f4f60a0d 100644 --- a/qemu/hmp-commands.hx +++ b/qemu/hmp-commands.hx @@ -194,8 +194,8 @@ ETEXI { .name = "change", - .args_type = "device:B,target:F,arg:s?", - .params = "device filename [format]", + .args_type = "device:B,target:F,arg:s?,read-only-mode:s?", + .params = "device filename [format [read-only-mode]]", .help = "change a removable medium, optional format", .mhandler.cmd = hmp_change, }, @@ -206,7 +206,7 @@ STEXI Change the configuration of a device. @table @option -@item change @var{diskdevice} @var{filename} [@var{format}] +@item change @var{diskdevice} @var{filename} [@var{format} [@var{read-only-mode}]] Change the medium for a removable disk device to point to @var{filename}. eg @example @@ -215,6 +215,20 @@ Change the medium for a removable disk device to point to @var{filename}. eg @var{format} is optional. +@var{read-only-mode} may be used to change the read-only status of the device. +It accepts the following values: + +@table @var +@item retain +Retains the current status; this is the default. + +@item read-only +Makes the device read-only. + +@item read-write +Makes the device writable. +@end table + @item change vnc @var{display},@var{options} Change the configuration of the VNC server. The valid syntax for @var{display} and @var{options} are described at @ref{sec_invocation}. eg @@ -271,6 +285,7 @@ ETEXI .params = "name on|off", .help = "changes status of a specific trace event", .mhandler.cmd = hmp_trace_event, + .command_completion = trace_event_completion, }, STEXI @@ -675,7 +690,8 @@ ETEXI STEXI @item device_del @var{id} @findex device_del -Remove device @var{id}. +Remove device @var{id}. @var{id} may be a short ID +or a QOM object path. ETEXI { @@ -1003,6 +1019,23 @@ STEXI @item migrate_set_parameter @var{parameter} @var{value} @findex migrate_set_parameter Set the parameter @var{parameter} for migration. +ETEXI + + { + .name = "migrate_start_postcopy", + .args_type = "", + .params = "", + .help = "Followup to a migration command to switch the migration" + " to postcopy mode. The postcopy-ram capability must " + "be set before the original migration command.", + .mhandler.cmd = hmp_migrate_start_postcopy, + }, + +STEXI +@item migrate_start_postcopy +@findex migrate_start_postcopy +Switch in-progress migration to postcopy mode. Ignored after the end of +migration (or once already in postcopy). ETEXI { @@ -1023,10 +1056,11 @@ ETEXI { .name = "dump-guest-memory", - .args_type = "paging:-p,zlib:-z,lzo:-l,snappy:-s,filename:F,begin:i?,length:i?", - .params = "[-p] [-z|-l|-s] filename [begin length]", + .args_type = "paging:-p,detach:-d,zlib:-z,lzo:-l,snappy:-s,filename:F,begin:i?,length:i?", + .params = "[-p] [-d] [-z|-l|-s] filename [begin length]", .help = "dump guest memory into file 'filename'.\n\t\t\t" "-p: do paging to get guest's memory mapping.\n\t\t\t" + "-d: return immediately (do not wait for completion).\n\t\t\t" "-z: dump in kdump-compressed format, with zlib compression.\n\t\t\t" "-l: dump in kdump-compressed format, with lzo compression.\n\t\t\t" "-s: dump in kdump-compressed format, with snappy compression.\n\t\t\t" @@ -1053,6 +1087,22 @@ gdb. Without -z|-l|-s, the dump format is ELF. together with begin. ETEXI +#if defined(TARGET_S390X) + { + .name = "dump-skeys", + .args_type = "filename:F", + .params = "", + .help = "Save guest storage keys into file 'filename'.\n", + .mhandler.cmd = hmp_dump_skeys, + }, +#endif + +STEXI +@item dump-skeys @var{filename} +@findex dump-skeys +Save guest storage keys to a file. +ETEXI + { .name = "snapshot_blkdev", .args_type = "reuse:-n,device:B,snapshot-file:s?,format:s?", @@ -1151,8 +1201,8 @@ ETEXI { .name = "drive_add", - .args_type = "pci_addr:s,opts:s", - .params = "[[:]:]\n" + .args_type = "node:-n,pci_addr:s,opts:s", + .params = "[-n] [[:]:]\n" "[file=file][,if=type][,bus=n]\n" "[,unit=m][,media=d][,index=i]\n" "[,cyls=c,heads=h,secs=s[,trans=t]]\n" @@ -1706,122 +1756,6 @@ ETEXI .sub_table = info_cmds, }, -STEXI -@item info @var{subcommand} -@findex info -Show various information about the system state. - -@table @option -@item info version -show the version of QEMU -@item info network -show the various VLANs and the associated devices -@item info chardev -show the character devices -@item info block -show the block devices -@item info blockstats -show block device statistics -@item info registers -show the cpu registers -@item info cpus -show infos for each CPU -@item info history -show the command line history -@item info irq -show the interrupts statistics (if available) -@item info pic -show i8259 (PIC) state -@item info pci -show emulated PCI device info -@item info tlb -show virtual to physical memory mappings (i386, SH4, SPARC, PPC, and Xtensa only) -@item info mem -show the active virtual memory mappings (i386 only) -@item info jit -show dynamic compiler info -@item info numa -show NUMA information -@item info kvm -show KVM information -@item info usb -show USB devices plugged on the virtual USB hub -@item info usbhost -show all USB host devices -@item info profile -show profiling information -@item info capture -show information about active capturing -@item info snapshots -show list of VM snapshots -@item info status -show the current VM status (running|paused) -@item info mice -show which guest mouse is receiving events -@item info vnc -show the vnc server status -@item info name -show the current VM name -@item info uuid -show the current VM UUID -@item info cpustats -show CPU statistics -@item info usernet -show user network stack connection states -@item info migrate -show migration status -@item info migrate_capabilities -show current migration capabilities -@item info migrate_parameters -show current migration parameters -@item info migrate_cache_size -show current migration XBZRLE cache size -@item info balloon -show balloon information -@item info qtree -show device tree -@item info qdm -show qdev device model list -@item info qom-tree -show object composition tree -@item info roms -show roms -@item info tpm -show the TPM device -@item info memory-devices -show the memory devices -@end table -ETEXI - -STEXI -@item info trace-events -show available trace events and their state -ETEXI - -STEXI -@item rocker @var{name} -@findex rocker -Show Rocker(s) -ETEXI - -STEXI -@item rocker_ports @var{name} -@findex rocker_ports -Show Rocker ports -ETEXI - -STEXI -@item rocker_of_dpa_flows @var{name} [@var{tbl_id}] -@findex rocker_of_dpa_flows -Show Rocker OF-DPA flow tables -ETEXI - -STEXI -@item rocker_of_dpa_groups @var{name} [@var{type}] -@findex rocker_of_dpa_groups -Show Rocker OF-DPA groups -ETEXI - STEXI @end table ETEXI diff --git a/qemu/hmp.c b/qemu/hmp.c index dcc66f159..d51023667 100644 --- a/qemu/hmp.c +++ b/qemu/hmp.c @@ -13,6 +13,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hmp.h" #include "net/net.h" #include "net/eth.h" @@ -27,10 +28,13 @@ #include "qapi/opts-visitor.h" #include "qapi/qmp/qerror.h" #include "qapi/string-output-visitor.h" +#include "qapi/util.h" #include "qapi-visit.h" +#include "qom/object_interfaces.h" #include "ui/console.h" #include "block/qapi.h" #include "qemu-io.h" +#include "qemu/cutils.h" #ifdef CONFIG_SPICE #include @@ -40,8 +44,7 @@ static void hmp_handle_error(Monitor *mon, Error **errp) { assert(errp); if (*errp) { - monitor_printf(mon, "%s\n", error_get_pretty(*errp)); - error_free(*errp); + error_report_err(*errp); } } @@ -232,6 +235,11 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) info->xbzrle_cache->overflow); } + if (info->has_x_cpu_throttle_percentage) { + monitor_printf(mon, "cpu throttle percentage: %" PRIu64 "\n", + info->x_cpu_throttle_percentage); + } + qapi_free_MigrationInfo(info); qapi_free_MigrationCapabilityStatusList(caps); } @@ -272,6 +280,12 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, " %s: %" PRId64, MigrationParameter_lookup[MIGRATION_PARAMETER_DECOMPRESS_THREADS], params->decompress_threads); + monitor_printf(mon, " %s: %" PRId64, + MigrationParameter_lookup[MIGRATION_PARAMETER_X_CPU_THROTTLE_INITIAL], + params->x_cpu_throttle_initial); + monitor_printf(mon, " %s: %" PRId64, + MigrationParameter_lookup[MIGRATION_PARAMETER_X_CPU_THROTTLE_INCREMENT], + params->x_cpu_throttle_increment); monitor_printf(mon, "\n"); } @@ -299,17 +313,27 @@ void hmp_info_cpus(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%c CPU #%" PRId64 ":", active, cpu->value->CPU); - if (cpu->value->has_pc) { - monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->pc); - } - if (cpu->value->has_nip) { - monitor_printf(mon, " nip=0x%016" PRIx64, cpu->value->nip); - } - if (cpu->value->has_npc) { - monitor_printf(mon, " npc=0x%016" PRIx64, cpu->value->npc); - } - if (cpu->value->has_PC) { - monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->PC); + switch (cpu->value->arch) { + case CPU_INFO_ARCH_X86: + monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->u.x86.pc); + break; + case CPU_INFO_ARCH_PPC: + monitor_printf(mon, " nip=0x%016" PRIx64, cpu->value->u.ppc.nip); + break; + case CPU_INFO_ARCH_SPARC: + monitor_printf(mon, " pc=0x%016" PRIx64, + cpu->value->u.q_sparc.pc); + monitor_printf(mon, " npc=0x%016" PRIx64, + cpu->value->u.q_sparc.npc); + break; + case CPU_INFO_ARCH_MIPS: + monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.q_mips.PC); + break; + case CPU_INFO_ARCH_TRICORE: + monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.tricore.PC); + break; + default: + break; } if (cpu->value->halted) { @@ -510,6 +534,7 @@ void hmp_info_blockstats(Monitor *mon, const QDict *qdict) " flush_total_time_ns=%" PRId64 " rd_merged=%" PRId64 " wr_merged=%" PRId64 + " idle_time_ns=%" PRId64 "\n", stats->value->stats->rd_bytes, stats->value->stats->wr_bytes, @@ -520,7 +545,8 @@ void hmp_info_blockstats(Monitor *mon, const QDict *qdict) stats->value->stats->rd_total_time_ns, stats->value->stats->flush_total_time_ns, stats->value->stats->rd_merged, - stats->value->stats->wr_merged); + stats->value->stats->wr_merged, + stats->value->stats->idle_time_ns); } qapi_free_BlockStatsList(stats_list); @@ -534,8 +560,7 @@ void hmp_info_vnc(Monitor *mon, const QDict *qdict) info = qmp_query_vnc(&err); if (err) { - monitor_printf(mon, "%s\n", error_get_pretty(err)); - error_free(err); + error_report_err(err); return; } @@ -558,8 +583,8 @@ void hmp_info_vnc(Monitor *mon, const QDict *qdict) for (client = info->clients; client; client = client->next) { monitor_printf(mon, "Client:\n"); monitor_printf(mon, " address: %s:%s\n", - client->value->base->host, - client->value->base->service); + client->value->host, + client->value->service); monitor_printf(mon, " x509_dname: %s\n", client->value->x509_dname ? client->value->x509_dname : "none"); @@ -627,7 +652,7 @@ void hmp_info_spice(Monitor *mon, const QDict *qdict) for (chan = info->channels; chan; chan = chan->next) { monitor_printf(mon, "Channel:\n"); monitor_printf(mon, " address: %s:%s%s\n", - chan->value->base->host, chan->value->base->port, + chan->value->host, chan->value->port, chan->value->tls ? " [tls]" : ""); monitor_printf(mon, " session: %" PRId64 "\n", chan->value->connection_id); @@ -657,8 +682,7 @@ void hmp_info_balloon(Monitor *mon, const QDict *qdict) info = qmp_query_balloon(&err); if (err) { - monitor_printf(mon, "%s\n", error_get_pretty(err)); - error_free(err); + error_report_err(err); return; } @@ -830,18 +854,18 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict) c, TpmModel_lookup[ti->model]); monitor_printf(mon, " \\ %s: type=%s", - ti->id, TpmTypeOptionsKind_lookup[ti->options->kind]); + ti->id, TpmTypeOptionsKind_lookup[ti->options->type]); - switch (ti->options->kind) { + switch (ti->options->type) { case TPM_TYPE_OPTIONS_KIND_PASSTHROUGH: - tpo = ti->options->passthrough; + tpo = ti->options->u.passthrough.data; monitor_printf(mon, "%s%s%s%s", tpo->has_path ? ",path=" : "", tpo->has_path ? tpo->path : "", tpo->has_cancel_path ? ",cancel-path=" : "", tpo->has_cancel_path ? tpo->cancel_path : ""); break; - case TPM_TYPE_OPTIONS_KIND_MAX: + case TPM_TYPE_OPTIONS_KIND__MAX: break; } monitor_printf(mon, "\n"); @@ -926,8 +950,7 @@ void hmp_ringbuf_read(Monitor *mon, const QDict *qdict) data = qmp_ringbuf_read(chardev, size, false, 0, &err); if (err) { - monitor_printf(mon, "%s\n", error_get_pretty(err)); - error_free(err); + error_report_err(err); return; } @@ -1020,8 +1043,7 @@ void hmp_balloon(Monitor *mon, const QDict *qdict) qmp_balloon(value, &err); if (err) { - monitor_printf(mon, "balloon: %s\n", error_get_pretty(err)); - error_free(err); + error_report_err(err); } } @@ -1169,8 +1191,7 @@ void hmp_migrate_set_cache_size(Monitor *mon, const QDict *qdict) qmp_migrate_set_cache_size(value, &err); if (err) { - monitor_printf(mon, "%s\n", error_get_pretty(err)); - error_free(err); + error_report_err(err); return; } } @@ -1189,7 +1210,7 @@ void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict) MigrationCapabilityStatusList *caps = g_malloc0(sizeof(*caps)); int i; - for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) { + for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { if (strcmp(cap, MigrationCapability_lookup[i]) == 0) { caps->value = g_malloc0(sizeof(*caps->value)); caps->value->capability = i; @@ -1200,16 +1221,14 @@ void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict) } } - if (i == MIGRATION_CAPABILITY_MAX) { + if (i == MIGRATION_CAPABILITY__MAX) { error_setg(&err, QERR_INVALID_PARAMETER, cap); } qapi_free_MigrationCapabilityStatusList(caps); if (err) { - monitor_printf(mon, "migrate_set_capability: %s\n", - error_get_pretty(err)); - error_free(err); + error_report_err(err); } } @@ -1221,9 +1240,11 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) bool has_compress_level = false; bool has_compress_threads = false; bool has_decompress_threads = false; + bool has_x_cpu_throttle_initial = false; + bool has_x_cpu_throttle_increment = false; int i; - for (i = 0; i < MIGRATION_PARAMETER_MAX; i++) { + for (i = 0; i < MIGRATION_PARAMETER__MAX; i++) { if (strcmp(param, MigrationParameter_lookup[i]) == 0) { switch (i) { case MIGRATION_PARAMETER_COMPRESS_LEVEL: @@ -1235,23 +1256,29 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) case MIGRATION_PARAMETER_DECOMPRESS_THREADS: has_decompress_threads = true; break; + case MIGRATION_PARAMETER_X_CPU_THROTTLE_INITIAL: + has_x_cpu_throttle_initial = true; + break; + case MIGRATION_PARAMETER_X_CPU_THROTTLE_INCREMENT: + has_x_cpu_throttle_increment = true; + break; } qmp_migrate_set_parameters(has_compress_level, value, has_compress_threads, value, has_decompress_threads, value, + has_x_cpu_throttle_initial, value, + has_x_cpu_throttle_increment, value, &err); break; } } - if (i == MIGRATION_PARAMETER_MAX) { + if (i == MIGRATION_PARAMETER__MAX) { error_setg(&err, QERR_INVALID_PARAMETER, param); } if (err) { - monitor_printf(mon, "migrate_set_parameter: %s\n", - error_get_pretty(err)); - error_free(err); + error_report_err(err); } } @@ -1272,6 +1299,13 @@ void hmp_client_migrate_info(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, &err); } +void hmp_migrate_start_postcopy(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + qmp_migrate_start_postcopy(&err); + hmp_handle_error(mon, &err); +} + void hmp_set_password(Monitor *mon, const QDict *qdict) { const char *protocol = qdict_get_str(qdict, "protocol"); @@ -1315,24 +1349,46 @@ void hmp_change(Monitor *mon, const QDict *qdict) const char *device = qdict_get_str(qdict, "device"); const char *target = qdict_get_str(qdict, "target"); const char *arg = qdict_get_try_str(qdict, "arg"); + const char *read_only = qdict_get_try_str(qdict, "read-only-mode"); + BlockdevChangeReadOnlyMode read_only_mode = 0; Error *err = NULL; - if (strcmp(device, "vnc") == 0 && - (strcmp(target, "passwd") == 0 || - strcmp(target, "password") == 0)) { - if (!arg) { - monitor_read_password(mon, hmp_change_read_arg, NULL); + if (strcmp(device, "vnc") == 0) { + if (read_only) { + monitor_printf(mon, + "Parameter 'read-only-mode' is invalid for VNC\n"); return; } - } + if (strcmp(target, "passwd") == 0 || + strcmp(target, "password") == 0) { + if (!arg) { + monitor_read_password(mon, hmp_change_read_arg, NULL); + return; + } + } + qmp_change("vnc", target, !!arg, arg, &err); + } else { + if (read_only) { + read_only_mode = + qapi_enum_parse(BlockdevChangeReadOnlyMode_lookup, + read_only, BLOCKDEV_CHANGE_READ_ONLY_MODE__MAX, + BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN, &err); + if (err) { + hmp_handle_error(mon, &err); + return; + } + } - qmp_change(device, target, !!arg, arg, &err); - if (err && - error_get_class(err) == ERROR_CLASS_DEVICE_ENCRYPTED) { - error_free(err); - monitor_read_block_device_key(mon, device, NULL, NULL); - return; + qmp_blockdev_change_medium(device, target, !!arg, arg, + !!read_only, read_only_mode, &err); + if (err && + error_get_class(err) == ERROR_CLASS_DEVICE_ENCRYPTED) { + error_free(err); + monitor_read_block_device_key(mon, device, NULL, NULL); + return; + } } + hmp_handle_error(mon, &err); } @@ -1359,6 +1415,18 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict) 0, false, 0, + false, /* no burst length via HMP */ + 0, + false, + 0, + false, + 0, + false, + 0, + false, + 0, + false, + 0, false, /* No default I/O size */ 0, false, @@ -1483,8 +1551,7 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) qmp_migrate(uri, !!blk, blk, !!inc, inc, false, false, &err); if (err) { - monitor_printf(mon, "migrate: %s\n", error_get_pretty(err)); - error_free(err); + error_report_err(err); return; } @@ -1533,8 +1600,10 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) const char *file = qdict_get_str(qdict, "filename"); bool has_begin = qdict_haskey(qdict, "begin"); bool has_length = qdict_haskey(qdict, "length"); + bool has_detach = qdict_haskey(qdict, "detach"); int64_t begin = 0; int64_t length = 0; + bool detach = false; enum DumpGuestMemoryFormat dump_format = DUMP_GUEST_MEMORY_FORMAT_ELF; char *prot; @@ -1562,11 +1631,14 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) if (has_length) { length = qdict_get_int(qdict, "length"); } + if (has_detach) { + detach = qdict_get_bool(qdict, "detach"); + } prot = g_strconcat("file:", file, NULL); - qmp_dump_guest_memory(paging, prot, has_begin, begin, has_length, length, - true, dump_format, &err); + qmp_dump_guest_memory(paging, prot, true, detach, has_begin, begin, + has_length, length, true, dump_format, &err); hmp_handle_error(mon, &err); g_free(prot); } @@ -1602,58 +1674,27 @@ void hmp_netdev_del(Monitor *mon, const QDict *qdict) void hmp_object_add(Monitor *mon, const QDict *qdict) { Error *err = NULL; - Error *err_end = NULL; QemuOpts *opts; - char *type = NULL; - char *id = NULL; - void *dummy = NULL; OptsVisitor *ov; - QDict *pdict; + Object *obj = NULL; opts = qemu_opts_from_qdict(qemu_find_opts("object"), qdict, &err); if (err) { - goto out; + hmp_handle_error(mon, &err); + return; } ov = opts_visitor_new(opts); - pdict = qdict_clone_shallow(qdict); - - visit_start_struct(opts_get_visitor(ov), &dummy, NULL, NULL, 0, &err); - if (err) { - goto out_clean; - } - - qdict_del(pdict, "qom-type"); - visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err); - if (err) { - goto out_end; - } + obj = user_creatable_add(qdict, opts_get_visitor(ov), &err); + opts_visitor_cleanup(ov); + qemu_opts_del(opts); - qdict_del(pdict, "id"); - visit_type_str(opts_get_visitor(ov), &id, "id", &err); if (err) { - goto out_end; + hmp_handle_error(mon, &err); } - - object_add(type, id, pdict, opts_get_visitor(ov), &err); - -out_end: - visit_end_struct(opts_get_visitor(ov), &err_end); - if (!err && err_end) { - qmp_object_del(id, NULL); + if (obj) { + object_unref(obj); } - error_propagate(&err, err_end); -out_clean: - opts_visitor_cleanup(ov); - - QDECREF(pdict); - qemu_opts_del(opts); - g_free(id); - g_free(type); - g_free(dummy); - -out: - hmp_handle_error(mon, &err); } void hmp_getfd(Monitor *mon, const QDict *qdict) @@ -1681,21 +1722,18 @@ void hmp_sendkey(Monitor *mon, const QDict *qdict) int has_hold_time = qdict_haskey(qdict, "hold-time"); int hold_time = qdict_get_try_int(qdict, "hold-time", -1); Error *err = NULL; - char keyname_buf[16]; char *separator; int keyname_len; while (1) { separator = strchr(keys, '-'); keyname_len = separator ? separator - keys : strlen(keys); - pstrcpy(keyname_buf, sizeof(keyname_buf), keys); /* Be compatible with old interface, convert user inputted "<" */ - if (!strncmp(keyname_buf, "<", 1) && keyname_len == 1) { - pstrcpy(keyname_buf, sizeof(keyname_buf), "less"); + if (keys[0] == '<' && keyname_len == 1) { + keys = "less"; keyname_len = 4; } - keyname_buf[keyname_len] = 0; keylist = g_malloc0(sizeof(*keylist)); keylist->value = g_malloc0(sizeof(*keylist->value)); @@ -1708,21 +1746,22 @@ void hmp_sendkey(Monitor *mon, const QDict *qdict) } tmp = keylist; - if (strstart(keyname_buf, "0x", NULL)) { + if (strstart(keys, "0x", NULL)) { char *endp; - int value = strtoul(keyname_buf, &endp, 0); - if (*endp != '\0') { + int value = strtoul(keys, &endp, 0); + assert(endp <= keys + keyname_len); + if (endp != keys + keyname_len) { goto err_out; } - keylist->value->kind = KEY_VALUE_KIND_NUMBER; - keylist->value->number = value; + keylist->value->type = KEY_VALUE_KIND_NUMBER; + keylist->value->u.number.data = value; } else { - int idx = index_from_key(keyname_buf); - if (idx == Q_KEY_CODE_MAX) { + int idx = index_from_key(keys, keyname_len); + if (idx == Q_KEY_CODE__MAX) { goto err_out; } - keylist->value->kind = KEY_VALUE_KIND_QCODE; - keylist->value->qcode = idx; + keylist->value->type = KEY_VALUE_KIND_QCODE; + keylist->value->u.qcode.data = idx; } if (!separator) { @@ -1739,7 +1778,7 @@ out: return; err_out: - monitor_printf(mon, "invalid parameter: %s\n", keyname_buf); + monitor_printf(mon, "invalid parameter: %.*s\n", keyname_len, keys); goto out; } @@ -1772,7 +1811,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) goto exit; } - qmp_nbd_server_start(addr, &local_err); + qmp_nbd_server_start(addr, false, NULL, &local_err); qapi_free_SocketAddress(addr); if (local_err != NULL) { goto exit; @@ -1883,7 +1922,7 @@ void hmp_object_del(Monitor *mon, const QDict *qdict) const char *id = qdict_get_str(qdict, "id"); Error *err = NULL; - qmp_object_del(id, &err); + user_creatable_del(id, &err); hmp_handle_error(mon, &err); } @@ -1899,8 +1938,8 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict) while (m) { ov = string_output_visitor_new(false); - visit_type_uint16List(string_output_get_visitor(ov), - &m->value->host_nodes, NULL, NULL); + visit_type_uint16List(string_output_get_visitor(ov), NULL, + &m->value->host_nodes, NULL); monitor_printf(mon, "memory backend: %d\n", i); monitor_printf(mon, " size: %" PRId64 "\n", m->value->size); monitor_printf(mon, " merge: %s\n", @@ -1937,12 +1976,12 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) value = info->value; if (value) { - switch (value->kind) { + switch (value->type) { case MEMORY_DEVICE_INFO_KIND_DIMM: - di = value->dimm; + di = value->u.dimm.data; monitor_printf(mon, "Memory device [%s]: \"%s\"\n", - MemoryDeviceInfoKind_lookup[value->kind], + MemoryDeviceInfoKind_lookup[value->type], di->id ? di->id : ""); monitor_printf(mon, " addr: 0x%" PRIx64 "\n", di->addr); monitor_printf(mon, " slot: %" PRId64 "\n", di->slot); @@ -1963,6 +2002,19 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) qapi_free_MemoryDeviceInfoList(info_list); } +void hmp_info_iothreads(Monitor *mon, const QDict *qdict) +{ + IOThreadInfoList *info_list = qmp_query_iothreads(NULL); + IOThreadInfoList *info; + + for (info = info_list; info; info = info->next) { + monitor_printf(mon, "%s: thread_id=%" PRId64 "\n", + info->value->id, info->value->thread_id); + } + + qapi_free_IOThreadInfoList(info_list); +} + void hmp_qom_list(Monitor *mon, const QDict *qdict) { const char *path = qdict_get_try_str(qdict, "path"); @@ -2015,11 +2067,11 @@ void hmp_rocker(Monitor *mon, const QDict *qdict) { const char *name = qdict_get_str(qdict, "name"); RockerSwitch *rocker; - Error *errp = NULL; + Error *err = NULL; - rocker = qmp_query_rocker(name, &errp); - if (errp != NULL) { - hmp_handle_error(mon, &errp); + rocker = qmp_query_rocker(name, &err); + if (err != NULL) { + hmp_handle_error(mon, &err); return; } @@ -2034,11 +2086,11 @@ void hmp_rocker_ports(Monitor *mon, const QDict *qdict) { RockerPortList *list, *port; const char *name = qdict_get_str(qdict, "name"); - Error *errp = NULL; + Error *err = NULL; - list = qmp_query_rocker_ports(name, &errp); - if (errp != NULL) { - hmp_handle_error(mon, &errp); + list = qmp_query_rocker_ports(name, &err); + if (err != NULL) { + hmp_handle_error(mon, &err); return; } @@ -2063,11 +2115,11 @@ void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict) RockerOfDpaFlowList *list, *info; const char *name = qdict_get_str(qdict, "name"); uint32_t tbl_id = qdict_get_try_int(qdict, "tbl_id", -1); - Error *errp = NULL; + Error *err = NULL; - list = qmp_query_rocker_of_dpa_flows(name, tbl_id != -1, tbl_id, &errp); - if (errp != NULL) { - hmp_handle_error(mon, &errp); + list = qmp_query_rocker_of_dpa_flows(name, tbl_id != -1, tbl_id, &err); + if (err != NULL) { + hmp_handle_error(mon, &err); return; } @@ -2213,12 +2265,12 @@ void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict) RockerOfDpaGroupList *list, *g; const char *name = qdict_get_str(qdict, "name"); uint8_t type = qdict_get_try_int(qdict, "type", 9); - Error *errp = NULL; + Error *err = NULL; bool set = false; - list = qmp_query_rocker_of_dpa_groups(name, type != 9, type, &errp); - if (errp != NULL) { - hmp_handle_error(mon, &errp); + list = qmp_query_rocker_of_dpa_groups(name, type != 9, type, &err); + if (err != NULL) { + hmp_handle_error(mon, &err); return; } @@ -2312,3 +2364,20 @@ void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict) qapi_free_RockerOfDpaGroupList(list); } + +void hmp_info_dump(Monitor *mon, const QDict *qdict) +{ + DumpQueryResult *result = qmp_query_dump(NULL); + + assert(result && result->status < DUMP_STATUS__MAX); + monitor_printf(mon, "Status: %s\n", DumpStatus_lookup[result->status]); + + if (result->status == DUMP_STATUS_ACTIVE) { + float percent = 0; + assert(result->total != 0); + percent = 100.0 * result->completed / result->total; + monitor_printf(mon, "Finished: %.2f %%\n", percent); + } + + qapi_free_DumpQueryResult(result); +} diff --git a/qemu/hmp.h b/qemu/hmp.h index 0cf4f2a3d..093d65f5a 100644 --- a/qemu/hmp.h +++ b/qemu/hmp.h @@ -39,6 +39,7 @@ void hmp_info_balloon(Monitor *mon, const QDict *qdict); void hmp_info_pci(Monitor *mon, const QDict *qdict); void hmp_info_block_jobs(Monitor *mon, const QDict *qdict); void hmp_info_tpm(Monitor *mon, const QDict *qdict); +void hmp_info_iothreads(Monitor *mon, const QDict *qdict); void hmp_quit(Monitor *mon, const QDict *qdict); void hmp_stop(Monitor *mon, const QDict *qdict); void hmp_system_reset(Monitor *mon, const QDict *qdict); @@ -68,6 +69,7 @@ void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict); void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict); void hmp_migrate_set_cache_size(Monitor *mon, const QDict *qdict); void hmp_client_migrate_info(Monitor *mon, const QDict *qdict); +void hmp_migrate_start_postcopy(Monitor *mon, const QDict *qdict); void hmp_set_password(Monitor *mon, const QDict *qdict); void hmp_expire_password(Monitor *mon, const QDict *qdict); void hmp_eject(Monitor *mon, const QDict *qdict); @@ -113,6 +115,7 @@ void set_link_completion(ReadLineState *rs, int nb_args, const char *str); void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str); void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str); void ringbuf_write_completion(ReadLineState *rs, int nb_args, const char *str); +void trace_event_completion(ReadLineState *rs, int nb_args, const char *str); void watchdog_action_completion(ReadLineState *rs, int nb_args, const char *str); void migrate_set_capability_completion(ReadLineState *rs, int nb_args, @@ -128,5 +131,6 @@ void hmp_rocker(Monitor *mon, const QDict *qdict); void hmp_rocker_ports(Monitor *mon, const QDict *qdict); void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict); void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict); +void hmp_info_dump(Monitor *mon, const QDict *qdict); #endif diff --git a/qemu/hw/9pfs/9p-handle.c b/qemu/hw/9pfs/9p-handle.c new file mode 100644 index 000000000..894041488 --- /dev/null +++ b/qemu/hw/9pfs/9p-handle.c @@ -0,0 +1,709 @@ +/* + * 9p handle callback + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Aneesh Kumar K.V + * + * 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 "9p.h" +#include "9p-xattr.h" +#include +#include +#include +#include +#include +#include "qemu/xattr.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include +#ifdef CONFIG_LINUX_MAGIC_H +#include +#endif +#include + +#ifndef XFS_SUPER_MAGIC +#define XFS_SUPER_MAGIC 0x58465342 +#endif +#ifndef EXT2_SUPER_MAGIC +#define EXT2_SUPER_MAGIC 0xEF53 +#endif +#ifndef REISERFS_SUPER_MAGIC +#define REISERFS_SUPER_MAGIC 0x52654973 +#endif +#ifndef BTRFS_SUPER_MAGIC +#define BTRFS_SUPER_MAGIC 0x9123683E +#endif + +struct handle_data { + int mountfd; + int handle_bytes; +}; + +static inline int name_to_handle(int dirfd, const char *name, + struct file_handle *fh, int *mnt_id, int flags) +{ + return name_to_handle_at(dirfd, name, fh, mnt_id, flags); +} + +static inline int open_by_handle(int mountfd, const char *fh, int flags) +{ + return open_by_handle_at(mountfd, (struct file_handle *)fh, flags); +} + +static int handle_update_file_cred(int dirfd, const char *name, FsCred *credp) +{ + int fd, ret; + fd = openat(dirfd, name, O_NONBLOCK | O_NOFOLLOW); + if (fd < 0) { + return fd; + } + ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH); + if (ret < 0) { + goto err_out; + } + ret = fchmod(fd, credp->fc_mode & 07777); +err_out: + close(fd); + return ret; +} + + +static int handle_lstat(FsContext *fs_ctx, V9fsPath *fs_path, + struct stat *stbuf) +{ + int fd, ret; + struct handle_data *data = (struct handle_data *)fs_ctx->private; + + fd = open_by_handle(data->mountfd, fs_path->data, O_PATH); + if (fd < 0) { + return fd; + } + ret = fstatat(fd, "", stbuf, AT_EMPTY_PATH); + close(fd); + return ret; +} + +static ssize_t handle_readlink(FsContext *fs_ctx, V9fsPath *fs_path, + char *buf, size_t bufsz) +{ + int fd, ret; + struct handle_data *data = (struct handle_data *)fs_ctx->private; + + fd = open_by_handle(data->mountfd, fs_path->data, O_PATH); + if (fd < 0) { + return fd; + } + ret = readlinkat(fd, "", buf, bufsz); + close(fd); + return ret; +} + +static int handle_close(FsContext *ctx, V9fsFidOpenState *fs) +{ + return close(fs->fd); +} + +static int handle_closedir(FsContext *ctx, V9fsFidOpenState *fs) +{ + return closedir(fs->dir); +} + +static int handle_open(FsContext *ctx, V9fsPath *fs_path, + int flags, V9fsFidOpenState *fs) +{ + struct handle_data *data = (struct handle_data *)ctx->private; + + fs->fd = open_by_handle(data->mountfd, fs_path->data, flags); + return fs->fd; +} + +static int handle_opendir(FsContext *ctx, + V9fsPath *fs_path, V9fsFidOpenState *fs) +{ + int ret; + ret = handle_open(ctx, fs_path, O_DIRECTORY, fs); + if (ret < 0) { + return -1; + } + fs->dir = fdopendir(ret); + if (!fs->dir) { + return -1; + } + return 0; +} + +static void handle_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) +{ + rewinddir(fs->dir); +} + +static off_t handle_telldir(FsContext *ctx, V9fsFidOpenState *fs) +{ + return telldir(fs->dir); +} + +static int handle_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, + struct dirent *entry, + struct dirent **result) +{ + return readdir_r(fs->dir, entry, result); +} + +static void handle_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) +{ + seekdir(fs->dir, off); +} + +static ssize_t handle_preadv(FsContext *ctx, V9fsFidOpenState *fs, + const struct iovec *iov, + int iovcnt, off_t offset) +{ +#ifdef CONFIG_PREADV + return preadv(fs->fd, iov, iovcnt, offset); +#else + int err = lseek(fs->fd, offset, SEEK_SET); + if (err == -1) { + return err; + } else { + return readv(fs->fd, iov, iovcnt); + } +#endif +} + +static ssize_t handle_pwritev(FsContext *ctx, V9fsFidOpenState *fs, + const struct iovec *iov, + int iovcnt, off_t offset) +{ + ssize_t ret; +#ifdef CONFIG_PREADV + ret = pwritev(fs->fd, iov, iovcnt, offset); +#else + int err = lseek(fs->fd, offset, SEEK_SET); + if (err == -1) { + return err; + } else { + ret = writev(fs->fd, iov, iovcnt); + } +#endif +#ifdef CONFIG_SYNC_FILE_RANGE + if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { + /* + * Initiate a writeback. This is not a data integrity sync. + * We want to ensure that we don't leave dirty pages in the cache + * after write when writeout=immediate is sepcified. + */ + sync_file_range(fs->fd, offset, ret, + SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); + } +#endif + return ret; +} + +static int handle_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) +{ + int fd, ret; + struct handle_data *data = (struct handle_data *)fs_ctx->private; + + fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); + if (fd < 0) { + return fd; + } + ret = fchmod(fd, credp->fc_mode); + close(fd); + return ret; +} + +static int handle_mknod(FsContext *fs_ctx, V9fsPath *dir_path, + const char *name, FsCred *credp) +{ + int dirfd, ret; + struct handle_data *data = (struct handle_data *)fs_ctx->private; + + dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); + if (dirfd < 0) { + return dirfd; + } + ret = mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev); + if (!ret) { + ret = handle_update_file_cred(dirfd, name, credp); + } + close(dirfd); + return ret; +} + +static int handle_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, + const char *name, FsCred *credp) +{ + int dirfd, ret; + struct handle_data *data = (struct handle_data *)fs_ctx->private; + + dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); + if (dirfd < 0) { + return dirfd; + } + ret = mkdirat(dirfd, name, credp->fc_mode); + if (!ret) { + ret = handle_update_file_cred(dirfd, name, credp); + } + close(dirfd); + return ret; +} + +static int handle_fstat(FsContext *fs_ctx, int fid_type, + V9fsFidOpenState *fs, struct stat *stbuf) +{ + int fd; + + if (fid_type == P9_FID_DIR) { + fd = dirfd(fs->dir); + } else { + fd = fs->fd; + } + return fstat(fd, stbuf); +} + +static int handle_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, + int flags, FsCred *credp, V9fsFidOpenState *fs) +{ + int ret; + int dirfd, fd; + struct handle_data *data = (struct handle_data *)fs_ctx->private; + + dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); + if (dirfd < 0) { + return dirfd; + } + fd = openat(dirfd, name, flags | O_NOFOLLOW, credp->fc_mode); + if (fd >= 0) { + ret = handle_update_file_cred(dirfd, name, credp); + if (ret < 0) { + close(fd); + fd = ret; + } else { + fs->fd = fd; + } + } + close(dirfd); + return fd; +} + + +static int handle_symlink(FsContext *fs_ctx, const char *oldpath, + V9fsPath *dir_path, const char *name, FsCred *credp) +{ + int fd, dirfd, ret; + struct handle_data *data = (struct handle_data *)fs_ctx->private; + + dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); + if (dirfd < 0) { + return dirfd; + } + ret = symlinkat(oldpath, dirfd, name); + if (!ret) { + fd = openat(dirfd, name, O_PATH | O_NOFOLLOW); + if (fd < 0) { + ret = fd; + goto err_out; + } + ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH); + close(fd); + } +err_out: + close(dirfd); + return ret; +} + +static int handle_link(FsContext *ctx, V9fsPath *oldpath, + V9fsPath *dirpath, const char *name) +{ + int oldfd, newdirfd, ret; + struct handle_data *data = (struct handle_data *)ctx->private; + + oldfd = open_by_handle(data->mountfd, oldpath->data, O_PATH); + if (oldfd < 0) { + return oldfd; + } + newdirfd = open_by_handle(data->mountfd, dirpath->data, O_PATH); + if (newdirfd < 0) { + close(oldfd); + return newdirfd; + } + ret = linkat(oldfd, "", newdirfd, name, AT_EMPTY_PATH); + close(newdirfd); + close(oldfd); + return ret; +} + +static int handle_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) +{ + int fd, ret; + struct handle_data *data = (struct handle_data *)ctx->private; + + fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK | O_WRONLY); + if (fd < 0) { + return fd; + } + ret = ftruncate(fd, size); + close(fd); + return ret; +} + +static int handle_rename(FsContext *ctx, const char *oldpath, + const char *newpath) +{ + errno = EOPNOTSUPP; + return -1; +} + +static int handle_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) +{ + int fd, ret; + struct handle_data *data = (struct handle_data *)fs_ctx->private; + + fd = open_by_handle(data->mountfd, fs_path->data, O_PATH); + if (fd < 0) { + return fd; + } + ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH); + close(fd); + return ret; +} + +static int handle_utimensat(FsContext *ctx, V9fsPath *fs_path, + const struct timespec *buf) +{ + int ret; +#ifdef CONFIG_UTIMENSAT + int fd; + struct handle_data *data = (struct handle_data *)ctx->private; + + fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); + if (fd < 0) { + return fd; + } + ret = futimens(fd, buf); + close(fd); +#else + ret = -1; + errno = ENOSYS; +#endif + return ret; +} + +static int handle_remove(FsContext *ctx, const char *path) +{ + errno = EOPNOTSUPP; + return -1; +} + +static int handle_fsync(FsContext *ctx, int fid_type, + V9fsFidOpenState *fs, int datasync) +{ + int fd; + + if (fid_type == P9_FID_DIR) { + fd = dirfd(fs->dir); + } else { + fd = fs->fd; + } + + if (datasync) { + return qemu_fdatasync(fd); + } else { + return fsync(fd); + } +} + +static int handle_statfs(FsContext *ctx, V9fsPath *fs_path, + struct statfs *stbuf) +{ + int fd, ret; + struct handle_data *data = (struct handle_data *)ctx->private; + + fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); + if (fd < 0) { + return fd; + } + ret = fstatfs(fd, stbuf); + close(fd); + return ret; +} + +static ssize_t handle_lgetxattr(FsContext *ctx, V9fsPath *fs_path, + const char *name, void *value, size_t size) +{ + int fd, ret; + struct handle_data *data = (struct handle_data *)ctx->private; + + fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); + if (fd < 0) { + return fd; + } + ret = fgetxattr(fd, name, value, size); + close(fd); + return ret; +} + +static ssize_t handle_llistxattr(FsContext *ctx, V9fsPath *fs_path, + void *value, size_t size) +{ + int fd, ret; + struct handle_data *data = (struct handle_data *)ctx->private; + + fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); + if (fd < 0) { + return fd; + } + ret = flistxattr(fd, value, size); + close(fd); + return ret; +} + +static int handle_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, + void *value, size_t size, int flags) +{ + int fd, ret; + struct handle_data *data = (struct handle_data *)ctx->private; + + fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); + if (fd < 0) { + return fd; + } + ret = fsetxattr(fd, name, value, size, flags); + close(fd); + return ret; +} + +static int handle_lremovexattr(FsContext *ctx, V9fsPath *fs_path, + const char *name) +{ + int fd, ret; + struct handle_data *data = (struct handle_data *)ctx->private; + + fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); + if (fd < 0) { + return fd; + } + ret = fremovexattr(fd, name); + close(fd); + return ret; +} + +static int handle_name_to_path(FsContext *ctx, V9fsPath *dir_path, + const char *name, V9fsPath *target) +{ + char *buffer; + struct file_handle *fh; + int dirfd, ret, mnt_id; + struct handle_data *data = (struct handle_data *)ctx->private; + + /* "." and ".." are not allowed */ + if (!strcmp(name, ".") || !strcmp(name, "..")) { + errno = EINVAL; + return -1; + + } + if (dir_path) { + dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); + } else { + /* relative to export root */ + buffer = rpath(ctx, "."); + dirfd = open(buffer, O_DIRECTORY); + g_free(buffer); + } + if (dirfd < 0) { + return dirfd; + } + fh = g_malloc(sizeof(struct file_handle) + data->handle_bytes); + fh->handle_bytes = data->handle_bytes; + /* add a "./" at the beginning of the path */ + buffer = g_strdup_printf("./%s", name); + /* flag = 0 imply don't follow symlink */ + ret = name_to_handle(dirfd, buffer, fh, &mnt_id, 0); + if (!ret) { + target->data = (char *)fh; + target->size = sizeof(struct file_handle) + data->handle_bytes; + } else { + g_free(fh); + } + close(dirfd); + g_free(buffer); + return ret; +} + +static int handle_renameat(FsContext *ctx, V9fsPath *olddir, + const char *old_name, V9fsPath *newdir, + const char *new_name) +{ + int olddirfd, newdirfd, ret; + struct handle_data *data = (struct handle_data *)ctx->private; + + olddirfd = open_by_handle(data->mountfd, olddir->data, O_PATH); + if (olddirfd < 0) { + return olddirfd; + } + newdirfd = open_by_handle(data->mountfd, newdir->data, O_PATH); + if (newdirfd < 0) { + close(olddirfd); + return newdirfd; + } + ret = renameat(olddirfd, old_name, newdirfd, new_name); + close(newdirfd); + close(olddirfd); + return ret; +} + +static int handle_unlinkat(FsContext *ctx, V9fsPath *dir, + const char *name, int flags) +{ + int dirfd, ret; + struct handle_data *data = (struct handle_data *)ctx->private; + int rflags; + + dirfd = open_by_handle(data->mountfd, dir->data, O_PATH); + if (dirfd < 0) { + return dirfd; + } + + rflags = 0; + if (flags & P9_DOTL_AT_REMOVEDIR) { + rflags |= AT_REMOVEDIR; + } + + ret = unlinkat(dirfd, name, rflags); + + close(dirfd); + return ret; +} + +static int handle_ioc_getversion(FsContext *ctx, V9fsPath *path, + mode_t st_mode, uint64_t *st_gen) +{ +#ifdef FS_IOC_GETVERSION + int err; + V9fsFidOpenState fid_open; + + /* + * Do not try to open special files like device nodes, fifos etc + * We can get fd for regular files and directories only + */ + if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { + errno = ENOTTY; + return -1; + } + err = handle_open(ctx, path, O_RDONLY, &fid_open); + if (err < 0) { + return err; + } + err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen); + handle_close(ctx, &fid_open); + return err; +#else + errno = ENOTTY; + return -1; +#endif +} + +static int handle_init(FsContext *ctx) +{ + int ret, mnt_id; + struct statfs stbuf; + struct file_handle fh; + struct handle_data *data = g_malloc(sizeof(struct handle_data)); + + data->mountfd = open(ctx->fs_root, O_DIRECTORY); + if (data->mountfd < 0) { + ret = data->mountfd; + goto err_out; + } + ret = statfs(ctx->fs_root, &stbuf); + if (!ret) { + switch (stbuf.f_type) { + case EXT2_SUPER_MAGIC: + case BTRFS_SUPER_MAGIC: + case REISERFS_SUPER_MAGIC: + case XFS_SUPER_MAGIC: + ctx->exops.get_st_gen = handle_ioc_getversion; + break; + } + } + memset(&fh, 0, sizeof(struct file_handle)); + ret = name_to_handle(data->mountfd, ".", &fh, &mnt_id, 0); + if (ret && errno == EOVERFLOW) { + data->handle_bytes = fh.handle_bytes; + ctx->private = data; + ret = 0; + goto out; + } + /* we got 0 byte handle ? */ + ret = -1; + close(data->mountfd); +err_out: + g_free(data); +out: + return ret; +} + +static int handle_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse) +{ + const char *sec_model = qemu_opt_get(opts, "security_model"); + const char *path = qemu_opt_get(opts, "path"); + + if (sec_model) { + error_report("Invalid argument security_model specified with handle fsdriver"); + return -1; + } + + if (!path) { + error_report("fsdev: No path specified"); + return -1; + } + fse->path = g_strdup(path); + return 0; + +} + +FileOperations handle_ops = { + .parse_opts = handle_parse_opts, + .init = handle_init, + .lstat = handle_lstat, + .readlink = handle_readlink, + .close = handle_close, + .closedir = handle_closedir, + .open = handle_open, + .opendir = handle_opendir, + .rewinddir = handle_rewinddir, + .telldir = handle_telldir, + .readdir_r = handle_readdir_r, + .seekdir = handle_seekdir, + .preadv = handle_preadv, + .pwritev = handle_pwritev, + .chmod = handle_chmod, + .mknod = handle_mknod, + .mkdir = handle_mkdir, + .fstat = handle_fstat, + .open2 = handle_open2, + .symlink = handle_symlink, + .link = handle_link, + .truncate = handle_truncate, + .rename = handle_rename, + .chown = handle_chown, + .utimensat = handle_utimensat, + .remove = handle_remove, + .fsync = handle_fsync, + .statfs = handle_statfs, + .lgetxattr = handle_lgetxattr, + .llistxattr = handle_llistxattr, + .lsetxattr = handle_lsetxattr, + .lremovexattr = handle_lremovexattr, + .name_to_path = handle_name_to_path, + .renameat = handle_renameat, + .unlinkat = handle_unlinkat, +}; diff --git a/qemu/hw/9pfs/9p-local.c b/qemu/hw/9pfs/9p-local.c new file mode 100644 index 000000000..16f45f485 --- /dev/null +++ b/qemu/hw/9pfs/9p-local.c @@ -0,0 +1,1282 @@ +/* + * 9p Posix callback + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Anthony Liguori + * + * 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 "9p.h" +#include "9p-xattr.h" +#include "fsdev/qemu-fsdev.h" /* local_ops */ +#include +#include +#include +#include +#include +#include "qemu/xattr.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include +#include +#ifdef CONFIG_LINUX_MAGIC_H +#include +#endif +#include + +#ifndef XFS_SUPER_MAGIC +#define XFS_SUPER_MAGIC 0x58465342 +#endif +#ifndef EXT2_SUPER_MAGIC +#define EXT2_SUPER_MAGIC 0xEF53 +#endif +#ifndef REISERFS_SUPER_MAGIC +#define REISERFS_SUPER_MAGIC 0x52654973 +#endif +#ifndef BTRFS_SUPER_MAGIC +#define BTRFS_SUPER_MAGIC 0x9123683E +#endif + +#define VIRTFS_META_DIR ".virtfs_metadata" + +static char *local_mapped_attr_path(FsContext *ctx, const char *path) +{ + int dirlen; + const char *name = strrchr(path, '/'); + if (name) { + dirlen = name - path; + ++name; + } else { + name = path; + dirlen = 0; + } + return g_strdup_printf("%s/%.*s/%s/%s", ctx->fs_root, + dirlen, path, VIRTFS_META_DIR, name); +} + +static FILE *local_fopen(const char *path, const char *mode) +{ + int fd, o_mode = 0; + FILE *fp; + int flags = O_NOFOLLOW; + /* + * only supports two modes + */ + if (mode[0] == 'r') { + flags |= O_RDONLY; + } else if (mode[0] == 'w') { + flags |= O_WRONLY | O_TRUNC | O_CREAT; + o_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + } else { + return NULL; + } + fd = open(path, flags, o_mode); + if (fd == -1) { + return NULL; + } + fp = fdopen(fd, mode); + if (!fp) { + close(fd); + } + return fp; +} + +#define ATTR_MAX 100 +static void local_mapped_file_attr(FsContext *ctx, const char *path, + struct stat *stbuf) +{ + FILE *fp; + char buf[ATTR_MAX]; + char *attr_path; + + attr_path = local_mapped_attr_path(ctx, path); + fp = local_fopen(attr_path, "r"); + g_free(attr_path); + if (!fp) { + return; + } + memset(buf, 0, ATTR_MAX); + while (fgets(buf, ATTR_MAX, fp)) { + if (!strncmp(buf, "virtfs.uid", 10)) { + stbuf->st_uid = atoi(buf+11); + } else if (!strncmp(buf, "virtfs.gid", 10)) { + stbuf->st_gid = atoi(buf+11); + } else if (!strncmp(buf, "virtfs.mode", 11)) { + stbuf->st_mode = atoi(buf+12); + } else if (!strncmp(buf, "virtfs.rdev", 11)) { + stbuf->st_rdev = atoi(buf+12); + } + memset(buf, 0, ATTR_MAX); + } + fclose(fp); +} + +static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf) +{ + int err; + char *buffer; + char *path = fs_path->data; + + buffer = rpath(fs_ctx, path); + err = lstat(buffer, stbuf); + if (err) { + goto err_out; + } + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { + /* Actual credentials are part of extended attrs */ + uid_t tmp_uid; + gid_t tmp_gid; + mode_t tmp_mode; + dev_t tmp_dev; + if (getxattr(buffer, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) { + stbuf->st_uid = le32_to_cpu(tmp_uid); + } + if (getxattr(buffer, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) { + stbuf->st_gid = le32_to_cpu(tmp_gid); + } + if (getxattr(buffer, "user.virtfs.mode", + &tmp_mode, sizeof(mode_t)) > 0) { + stbuf->st_mode = le32_to_cpu(tmp_mode); + } + if (getxattr(buffer, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) { + stbuf->st_rdev = le64_to_cpu(tmp_dev); + } + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { + local_mapped_file_attr(fs_ctx, path, stbuf); + } + +err_out: + g_free(buffer); + return err; +} + +static int local_create_mapped_attr_dir(FsContext *ctx, const char *path) +{ + int err; + char *attr_dir; + char *tmp_path = g_strdup(path); + + attr_dir = g_strdup_printf("%s/%s/%s", + ctx->fs_root, dirname(tmp_path), VIRTFS_META_DIR); + + err = mkdir(attr_dir, 0700); + if (err < 0 && errno == EEXIST) { + err = 0; + } + g_free(attr_dir); + g_free(tmp_path); + return err; +} + +static int local_set_mapped_file_attr(FsContext *ctx, + const char *path, FsCred *credp) +{ + FILE *fp; + int ret = 0; + char buf[ATTR_MAX]; + char *attr_path; + int uid = -1, gid = -1, mode = -1, rdev = -1; + + attr_path = local_mapped_attr_path(ctx, path); + fp = local_fopen(attr_path, "r"); + if (!fp) { + goto create_map_file; + } + memset(buf, 0, ATTR_MAX); + while (fgets(buf, ATTR_MAX, fp)) { + if (!strncmp(buf, "virtfs.uid", 10)) { + uid = atoi(buf+11); + } else if (!strncmp(buf, "virtfs.gid", 10)) { + gid = atoi(buf+11); + } else if (!strncmp(buf, "virtfs.mode", 11)) { + mode = atoi(buf+12); + } else if (!strncmp(buf, "virtfs.rdev", 11)) { + rdev = atoi(buf+12); + } + memset(buf, 0, ATTR_MAX); + } + fclose(fp); + goto update_map_file; + +create_map_file: + ret = local_create_mapped_attr_dir(ctx, path); + if (ret < 0) { + goto err_out; + } + +update_map_file: + fp = local_fopen(attr_path, "w"); + if (!fp) { + ret = -1; + goto err_out; + } + + if (credp->fc_uid != -1) { + uid = credp->fc_uid; + } + if (credp->fc_gid != -1) { + gid = credp->fc_gid; + } + if (credp->fc_mode != -1) { + mode = credp->fc_mode; + } + if (credp->fc_rdev != -1) { + rdev = credp->fc_rdev; + } + + + if (uid != -1) { + fprintf(fp, "virtfs.uid=%d\n", uid); + } + if (gid != -1) { + fprintf(fp, "virtfs.gid=%d\n", gid); + } + if (mode != -1) { + fprintf(fp, "virtfs.mode=%d\n", mode); + } + if (rdev != -1) { + fprintf(fp, "virtfs.rdev=%d\n", rdev); + } + fclose(fp); + +err_out: + g_free(attr_path); + return ret; +} + +static int local_set_xattr(const char *path, FsCred *credp) +{ + int err; + + if (credp->fc_uid != -1) { + uint32_t tmp_uid = cpu_to_le32(credp->fc_uid); + err = setxattr(path, "user.virtfs.uid", &tmp_uid, sizeof(uid_t), 0); + if (err) { + return err; + } + } + if (credp->fc_gid != -1) { + uint32_t tmp_gid = cpu_to_le32(credp->fc_gid); + err = setxattr(path, "user.virtfs.gid", &tmp_gid, sizeof(gid_t), 0); + if (err) { + return err; + } + } + if (credp->fc_mode != -1) { + uint32_t tmp_mode = cpu_to_le32(credp->fc_mode); + err = setxattr(path, "user.virtfs.mode", &tmp_mode, sizeof(mode_t), 0); + if (err) { + return err; + } + } + if (credp->fc_rdev != -1) { + uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev); + err = setxattr(path, "user.virtfs.rdev", &tmp_rdev, sizeof(dev_t), 0); + if (err) { + return err; + } + } + return 0; +} + +static int local_post_create_passthrough(FsContext *fs_ctx, const char *path, + FsCred *credp) +{ + char *buffer; + + buffer = rpath(fs_ctx, path); + if (lchown(buffer, credp->fc_uid, credp->fc_gid) < 0) { + /* + * If we fail to change ownership and if we are + * using security model none. Ignore the error + */ + if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { + goto err; + } + } + + if (chmod(buffer, credp->fc_mode & 07777) < 0) { + goto err; + } + + g_free(buffer); + return 0; +err: + g_free(buffer); + return -1; +} + +static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, + char *buf, size_t bufsz) +{ + ssize_t tsize = -1; + char *buffer; + char *path = fs_path->data; + + if ((fs_ctx->export_flags & V9FS_SM_MAPPED) || + (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) { + int fd; + buffer = rpath(fs_ctx, path); + fd = open(buffer, O_RDONLY | O_NOFOLLOW); + g_free(buffer); + if (fd == -1) { + return -1; + } + do { + tsize = read(fd, (void *)buf, bufsz); + } while (tsize == -1 && errno == EINTR); + close(fd); + } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || + (fs_ctx->export_flags & V9FS_SM_NONE)) { + buffer = rpath(fs_ctx, path); + tsize = readlink(buffer, buf, bufsz); + g_free(buffer); + } + return tsize; +} + +static int local_close(FsContext *ctx, V9fsFidOpenState *fs) +{ + return close(fs->fd); +} + +static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs) +{ + return closedir(fs->dir); +} + +static int local_open(FsContext *ctx, V9fsPath *fs_path, + int flags, V9fsFidOpenState *fs) +{ + char *buffer; + char *path = fs_path->data; + + buffer = rpath(ctx, path); + fs->fd = open(buffer, flags | O_NOFOLLOW); + g_free(buffer); + return fs->fd; +} + +static int local_opendir(FsContext *ctx, + V9fsPath *fs_path, V9fsFidOpenState *fs) +{ + char *buffer; + char *path = fs_path->data; + + buffer = rpath(ctx, path); + fs->dir = opendir(buffer); + g_free(buffer); + if (!fs->dir) { + return -1; + } + return 0; +} + +static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) +{ + rewinddir(fs->dir); +} + +static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) +{ + return telldir(fs->dir); +} + +static int local_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, + struct dirent *entry, + struct dirent **result) +{ + int ret; + +again: + ret = readdir_r(fs->dir, entry, result); + if (ctx->export_flags & V9FS_SM_MAPPED) { + entry->d_type = DT_UNKNOWN; + } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { + if (!ret && *result != NULL && + !strcmp(entry->d_name, VIRTFS_META_DIR)) { + /* skp the meta data directory */ + goto again; + } + entry->d_type = DT_UNKNOWN; + } + return ret; +} + +static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) +{ + seekdir(fs->dir, off); +} + +static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs, + const struct iovec *iov, + int iovcnt, off_t offset) +{ +#ifdef CONFIG_PREADV + return preadv(fs->fd, iov, iovcnt, offset); +#else + int err = lseek(fs->fd, offset, SEEK_SET); + if (err == -1) { + return err; + } else { + return readv(fs->fd, iov, iovcnt); + } +#endif +} + +static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs, + const struct iovec *iov, + int iovcnt, off_t offset) +{ + ssize_t ret +; +#ifdef CONFIG_PREADV + ret = pwritev(fs->fd, iov, iovcnt, offset); +#else + int err = lseek(fs->fd, offset, SEEK_SET); + if (err == -1) { + return err; + } else { + ret = writev(fs->fd, iov, iovcnt); + } +#endif +#ifdef CONFIG_SYNC_FILE_RANGE + if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { + /* + * Initiate a writeback. This is not a data integrity sync. + * We want to ensure that we don't leave dirty pages in the cache + * after write when writeout=immediate is sepcified. + */ + sync_file_range(fs->fd, offset, ret, + SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); + } +#endif + return ret; +} + +static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) +{ + char *buffer; + int ret = -1; + char *path = fs_path->data; + + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { + buffer = rpath(fs_ctx, path); + ret = local_set_xattr(buffer, credp); + g_free(buffer); + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { + return local_set_mapped_file_attr(fs_ctx, path, credp); + } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || + (fs_ctx->export_flags & V9FS_SM_NONE)) { + buffer = rpath(fs_ctx, path); + ret = chmod(buffer, credp->fc_mode); + g_free(buffer); + } + return ret; +} + +static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, + const char *name, FsCred *credp) +{ + char *path; + int err = -1; + int serrno = 0; + V9fsString fullname; + char *buffer = NULL; + + v9fs_string_init(&fullname); + v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); + path = fullname.data; + + /* Determine the security model */ + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { + buffer = rpath(fs_ctx, path); + err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0); + if (err == -1) { + goto out; + } + err = local_set_xattr(buffer, credp); + if (err == -1) { + serrno = errno; + goto err_end; + } + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { + + buffer = rpath(fs_ctx, path); + err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0); + if (err == -1) { + goto out; + } + err = local_set_mapped_file_attr(fs_ctx, path, credp); + if (err == -1) { + serrno = errno; + goto err_end; + } + } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || + (fs_ctx->export_flags & V9FS_SM_NONE)) { + buffer = rpath(fs_ctx, path); + err = mknod(buffer, credp->fc_mode, credp->fc_rdev); + if (err == -1) { + goto out; + } + err = local_post_create_passthrough(fs_ctx, path, credp); + if (err == -1) { + serrno = errno; + goto err_end; + } + } + goto out; + +err_end: + remove(buffer); + errno = serrno; +out: + g_free(buffer); + v9fs_string_free(&fullname); + return err; +} + +static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, + const char *name, FsCred *credp) +{ + char *path; + int err = -1; + int serrno = 0; + V9fsString fullname; + char *buffer = NULL; + + v9fs_string_init(&fullname); + v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); + path = fullname.data; + + /* Determine the security model */ + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { + buffer = rpath(fs_ctx, path); + err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS); + if (err == -1) { + goto out; + } + credp->fc_mode = credp->fc_mode|S_IFDIR; + err = local_set_xattr(buffer, credp); + if (err == -1) { + serrno = errno; + goto err_end; + } + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { + buffer = rpath(fs_ctx, path); + err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS); + if (err == -1) { + goto out; + } + credp->fc_mode = credp->fc_mode|S_IFDIR; + err = local_set_mapped_file_attr(fs_ctx, path, credp); + if (err == -1) { + serrno = errno; + goto err_end; + } + } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || + (fs_ctx->export_flags & V9FS_SM_NONE)) { + buffer = rpath(fs_ctx, path); + err = mkdir(buffer, credp->fc_mode); + if (err == -1) { + goto out; + } + err = local_post_create_passthrough(fs_ctx, path, credp); + if (err == -1) { + serrno = errno; + goto err_end; + } + } + goto out; + +err_end: + remove(buffer); + errno = serrno; +out: + g_free(buffer); + v9fs_string_free(&fullname); + return err; +} + +static int local_fstat(FsContext *fs_ctx, int fid_type, + V9fsFidOpenState *fs, struct stat *stbuf) +{ + int err, fd; + + if (fid_type == P9_FID_DIR) { + fd = dirfd(fs->dir); + } else { + fd = fs->fd; + } + + err = fstat(fd, stbuf); + if (err) { + return err; + } + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { + /* Actual credentials are part of extended attrs */ + uid_t tmp_uid; + gid_t tmp_gid; + mode_t tmp_mode; + dev_t tmp_dev; + + if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) { + stbuf->st_uid = le32_to_cpu(tmp_uid); + } + if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) { + stbuf->st_gid = le32_to_cpu(tmp_gid); + } + if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) { + stbuf->st_mode = le32_to_cpu(tmp_mode); + } + if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) { + stbuf->st_rdev = le64_to_cpu(tmp_dev); + } + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { + errno = EOPNOTSUPP; + return -1; + } + return err; +} + +static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, + int flags, FsCred *credp, V9fsFidOpenState *fs) +{ + char *path; + int fd = -1; + int err = -1; + int serrno = 0; + V9fsString fullname; + char *buffer = NULL; + + /* + * Mark all the open to not follow symlinks + */ + flags |= O_NOFOLLOW; + + v9fs_string_init(&fullname); + v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); + path = fullname.data; + + /* Determine the security model */ + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { + buffer = rpath(fs_ctx, path); + fd = open(buffer, flags, SM_LOCAL_MODE_BITS); + if (fd == -1) { + err = fd; + goto out; + } + credp->fc_mode = credp->fc_mode|S_IFREG; + /* Set cleint credentials in xattr */ + err = local_set_xattr(buffer, credp); + if (err == -1) { + serrno = errno; + goto err_end; + } + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { + buffer = rpath(fs_ctx, path); + fd = open(buffer, flags, SM_LOCAL_MODE_BITS); + if (fd == -1) { + err = fd; + goto out; + } + credp->fc_mode = credp->fc_mode|S_IFREG; + /* Set client credentials in .virtfs_metadata directory files */ + err = local_set_mapped_file_attr(fs_ctx, path, credp); + if (err == -1) { + serrno = errno; + goto err_end; + } + } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || + (fs_ctx->export_flags & V9FS_SM_NONE)) { + buffer = rpath(fs_ctx, path); + fd = open(buffer, flags, credp->fc_mode); + if (fd == -1) { + err = fd; + goto out; + } + err = local_post_create_passthrough(fs_ctx, path, credp); + if (err == -1) { + serrno = errno; + goto err_end; + } + } + err = fd; + fs->fd = fd; + goto out; + +err_end: + close(fd); + remove(buffer); + errno = serrno; +out: + g_free(buffer); + v9fs_string_free(&fullname); + return err; +} + + +static int local_symlink(FsContext *fs_ctx, const char *oldpath, + V9fsPath *dir_path, const char *name, FsCred *credp) +{ + int err = -1; + int serrno = 0; + char *newpath; + V9fsString fullname; + char *buffer = NULL; + + v9fs_string_init(&fullname); + v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); + newpath = fullname.data; + + /* Determine the security model */ + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { + int fd; + ssize_t oldpath_size, write_size; + buffer = rpath(fs_ctx, newpath); + fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS); + if (fd == -1) { + err = fd; + goto out; + } + /* Write the oldpath (target) to the file. */ + oldpath_size = strlen(oldpath); + do { + write_size = write(fd, (void *)oldpath, oldpath_size); + } while (write_size == -1 && errno == EINTR); + + if (write_size != oldpath_size) { + serrno = errno; + close(fd); + err = -1; + goto err_end; + } + close(fd); + /* Set cleint credentials in symlink's xattr */ + credp->fc_mode = credp->fc_mode|S_IFLNK; + err = local_set_xattr(buffer, credp); + if (err == -1) { + serrno = errno; + goto err_end; + } + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { + int fd; + ssize_t oldpath_size, write_size; + buffer = rpath(fs_ctx, newpath); + fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS); + if (fd == -1) { + err = fd; + goto out; + } + /* Write the oldpath (target) to the file. */ + oldpath_size = strlen(oldpath); + do { + write_size = write(fd, (void *)oldpath, oldpath_size); + } while (write_size == -1 && errno == EINTR); + + if (write_size != oldpath_size) { + serrno = errno; + close(fd); + err = -1; + goto err_end; + } + close(fd); + /* Set cleint credentials in symlink's xattr */ + credp->fc_mode = credp->fc_mode|S_IFLNK; + err = local_set_mapped_file_attr(fs_ctx, newpath, credp); + if (err == -1) { + serrno = errno; + goto err_end; + } + } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || + (fs_ctx->export_flags & V9FS_SM_NONE)) { + buffer = rpath(fs_ctx, newpath); + err = symlink(oldpath, buffer); + if (err) { + goto out; + } + err = lchown(buffer, credp->fc_uid, credp->fc_gid); + if (err == -1) { + /* + * If we fail to change ownership and if we are + * using security model none. Ignore the error + */ + if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { + serrno = errno; + goto err_end; + } else + err = 0; + } + } + goto out; + +err_end: + remove(buffer); + errno = serrno; +out: + g_free(buffer); + v9fs_string_free(&fullname); + return err; +} + +static int local_link(FsContext *ctx, V9fsPath *oldpath, + V9fsPath *dirpath, const char *name) +{ + int ret; + V9fsString newpath; + char *buffer, *buffer1; + + v9fs_string_init(&newpath); + v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name); + + buffer = rpath(ctx, oldpath->data); + buffer1 = rpath(ctx, newpath.data); + ret = link(buffer, buffer1); + g_free(buffer); + g_free(buffer1); + + /* now link the virtfs_metadata files */ + if (!ret && (ctx->export_flags & V9FS_SM_MAPPED_FILE)) { + /* Link the .virtfs_metadata files. Create the metada directory */ + ret = local_create_mapped_attr_dir(ctx, newpath.data); + if (ret < 0) { + goto err_out; + } + buffer = local_mapped_attr_path(ctx, oldpath->data); + buffer1 = local_mapped_attr_path(ctx, newpath.data); + ret = link(buffer, buffer1); + g_free(buffer); + g_free(buffer1); + if (ret < 0 && errno != ENOENT) { + goto err_out; + } + } +err_out: + v9fs_string_free(&newpath); + return ret; +} + +static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) +{ + char *buffer; + int ret; + char *path = fs_path->data; + + buffer = rpath(ctx, path); + ret = truncate(buffer, size); + g_free(buffer); + return ret; +} + +static int local_rename(FsContext *ctx, const char *oldpath, + const char *newpath) +{ + int err; + char *buffer, *buffer1; + + if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { + err = local_create_mapped_attr_dir(ctx, newpath); + if (err < 0) { + return err; + } + /* rename the .virtfs_metadata files */ + buffer = local_mapped_attr_path(ctx, oldpath); + buffer1 = local_mapped_attr_path(ctx, newpath); + err = rename(buffer, buffer1); + g_free(buffer); + g_free(buffer1); + if (err < 0 && errno != ENOENT) { + return err; + } + } + + buffer = rpath(ctx, oldpath); + buffer1 = rpath(ctx, newpath); + err = rename(buffer, buffer1); + g_free(buffer); + g_free(buffer1); + return err; +} + +static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) +{ + char *buffer; + int ret = -1; + char *path = fs_path->data; + + if ((credp->fc_uid == -1 && credp->fc_gid == -1) || + (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || + (fs_ctx->export_flags & V9FS_SM_NONE)) { + buffer = rpath(fs_ctx, path); + ret = lchown(buffer, credp->fc_uid, credp->fc_gid); + g_free(buffer); + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) { + buffer = rpath(fs_ctx, path); + ret = local_set_xattr(buffer, credp); + g_free(buffer); + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { + return local_set_mapped_file_attr(fs_ctx, path, credp); + } + return ret; +} + +static int local_utimensat(FsContext *s, V9fsPath *fs_path, + const struct timespec *buf) +{ + char *buffer; + int ret; + char *path = fs_path->data; + + buffer = rpath(s, path); + ret = qemu_utimens(buffer, buf); + g_free(buffer); + return ret; +} + +static int local_remove(FsContext *ctx, const char *path) +{ + int err; + struct stat stbuf; + char *buffer; + + if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { + buffer = rpath(ctx, path); + err = lstat(buffer, &stbuf); + g_free(buffer); + if (err) { + goto err_out; + } + /* + * If directory remove .virtfs_metadata contained in the + * directory + */ + if (S_ISDIR(stbuf.st_mode)) { + buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root, + path, VIRTFS_META_DIR); + err = remove(buffer); + g_free(buffer); + if (err < 0 && errno != ENOENT) { + /* + * We didn't had the .virtfs_metadata file. May be file created + * in non-mapped mode ?. Ignore ENOENT. + */ + goto err_out; + } + } + /* + * Now remove the name from parent directory + * .virtfs_metadata directory + */ + buffer = local_mapped_attr_path(ctx, path); + err = remove(buffer); + g_free(buffer); + if (err < 0 && errno != ENOENT) { + /* + * We didn't had the .virtfs_metadata file. May be file created + * in non-mapped mode ?. Ignore ENOENT. + */ + goto err_out; + } + } + + buffer = rpath(ctx, path); + err = remove(buffer); + g_free(buffer); +err_out: + return err; +} + +static int local_fsync(FsContext *ctx, int fid_type, + V9fsFidOpenState *fs, int datasync) +{ + int fd; + + if (fid_type == P9_FID_DIR) { + fd = dirfd(fs->dir); + } else { + fd = fs->fd; + } + + if (datasync) { + return qemu_fdatasync(fd); + } else { + return fsync(fd); + } +} + +static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf) +{ + char *buffer; + int ret; + char *path = fs_path->data; + + buffer = rpath(s, path); + ret = statfs(buffer, stbuf); + g_free(buffer); + return ret; +} + +static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path, + const char *name, void *value, size_t size) +{ + char *path = fs_path->data; + + return v9fs_get_xattr(ctx, path, name, value, size); +} + +static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path, + void *value, size_t size) +{ + char *path = fs_path->data; + + return v9fs_list_xattr(ctx, path, value, size); +} + +static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, + void *value, size_t size, int flags) +{ + char *path = fs_path->data; + + return v9fs_set_xattr(ctx, path, name, value, size, flags); +} + +static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path, + const char *name) +{ + char *path = fs_path->data; + + return v9fs_remove_xattr(ctx, path, name); +} + +static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, + const char *name, V9fsPath *target) +{ + if (dir_path) { + v9fs_string_sprintf((V9fsString *)target, "%s/%s", + dir_path->data, name); + } else { + v9fs_string_sprintf((V9fsString *)target, "%s", name); + } + /* Bump the size for including terminating NULL */ + target->size++; + return 0; +} + +static int local_renameat(FsContext *ctx, V9fsPath *olddir, + const char *old_name, V9fsPath *newdir, + const char *new_name) +{ + int ret; + V9fsString old_full_name, new_full_name; + + v9fs_string_init(&old_full_name); + v9fs_string_init(&new_full_name); + + v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name); + v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name); + + ret = local_rename(ctx, old_full_name.data, new_full_name.data); + v9fs_string_free(&old_full_name); + v9fs_string_free(&new_full_name); + return ret; +} + +static int local_unlinkat(FsContext *ctx, V9fsPath *dir, + const char *name, int flags) +{ + int ret; + V9fsString fullname; + char *buffer; + + v9fs_string_init(&fullname); + + v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name); + if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { + if (flags == AT_REMOVEDIR) { + /* + * If directory remove .virtfs_metadata contained in the + * directory + */ + buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root, + fullname.data, VIRTFS_META_DIR); + ret = remove(buffer); + g_free(buffer); + if (ret < 0 && errno != ENOENT) { + /* + * We didn't had the .virtfs_metadata file. May be file created + * in non-mapped mode ?. Ignore ENOENT. + */ + goto err_out; + } + } + /* + * Now remove the name from parent directory + * .virtfs_metadata directory. + */ + buffer = local_mapped_attr_path(ctx, fullname.data); + ret = remove(buffer); + g_free(buffer); + if (ret < 0 && errno != ENOENT) { + /* + * We didn't had the .virtfs_metadata file. May be file created + * in non-mapped mode ?. Ignore ENOENT. + */ + goto err_out; + } + } + /* Remove the name finally */ + buffer = rpath(ctx, fullname.data); + ret = remove(buffer); + g_free(buffer); + +err_out: + v9fs_string_free(&fullname); + return ret; +} + +static int local_ioc_getversion(FsContext *ctx, V9fsPath *path, + mode_t st_mode, uint64_t *st_gen) +{ +#ifdef FS_IOC_GETVERSION + int err; + V9fsFidOpenState fid_open; + + /* + * Do not try to open special files like device nodes, fifos etc + * We can get fd for regular files and directories only + */ + if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { + errno = ENOTTY; + return -1; + } + err = local_open(ctx, path, O_RDONLY, &fid_open); + if (err < 0) { + return err; + } + err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen); + local_close(ctx, &fid_open); + return err; +#else + errno = ENOTTY; + return -1; +#endif +} + +static int local_init(FsContext *ctx) +{ + int err = 0; + struct statfs stbuf; + + if (ctx->export_flags & V9FS_SM_PASSTHROUGH) { + ctx->xops = passthrough_xattr_ops; + } else if (ctx->export_flags & V9FS_SM_MAPPED) { + ctx->xops = mapped_xattr_ops; + } else if (ctx->export_flags & V9FS_SM_NONE) { + ctx->xops = none_xattr_ops; + } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { + /* + * xattr operation for mapped-file and passthrough + * remain same. + */ + ctx->xops = passthrough_xattr_ops; + } + ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; +#ifdef FS_IOC_GETVERSION + /* + * use ioc_getversion only if the iocl is definied + */ + err = statfs(ctx->fs_root, &stbuf); + if (!err) { + switch (stbuf.f_type) { + case EXT2_SUPER_MAGIC: + case BTRFS_SUPER_MAGIC: + case REISERFS_SUPER_MAGIC: + case XFS_SUPER_MAGIC: + ctx->exops.get_st_gen = local_ioc_getversion; + break; + } + } +#endif + return err; +} + +static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse) +{ + const char *sec_model = qemu_opt_get(opts, "security_model"); + const char *path = qemu_opt_get(opts, "path"); + + if (!sec_model) { + error_report("Security model not specified, local fs needs security model"); + error_printf("valid options are:" + "\tsecurity_model=[passthrough|mapped-xattr|mapped-file|none]\n"); + return -1; + } + + if (!strcmp(sec_model, "passthrough")) { + fse->export_flags |= V9FS_SM_PASSTHROUGH; + } else if (!strcmp(sec_model, "mapped") || + !strcmp(sec_model, "mapped-xattr")) { + fse->export_flags |= V9FS_SM_MAPPED; + } else if (!strcmp(sec_model, "none")) { + fse->export_flags |= V9FS_SM_NONE; + } else if (!strcmp(sec_model, "mapped-file")) { + fse->export_flags |= V9FS_SM_MAPPED_FILE; + } else { + error_report("Invalid security model %s specified", sec_model); + error_printf("valid options are:" + "\t[passthrough|mapped-xattr|mapped-file|none]\n"); + return -1; + } + + if (!path) { + error_report("fsdev: No path specified"); + return -1; + } + fse->path = g_strdup(path); + + return 0; +} + +FileOperations local_ops = { + .parse_opts = local_parse_opts, + .init = local_init, + .lstat = local_lstat, + .readlink = local_readlink, + .close = local_close, + .closedir = local_closedir, + .open = local_open, + .opendir = local_opendir, + .rewinddir = local_rewinddir, + .telldir = local_telldir, + .readdir_r = local_readdir_r, + .seekdir = local_seekdir, + .preadv = local_preadv, + .pwritev = local_pwritev, + .chmod = local_chmod, + .mknod = local_mknod, + .mkdir = local_mkdir, + .fstat = local_fstat, + .open2 = local_open2, + .symlink = local_symlink, + .link = local_link, + .truncate = local_truncate, + .rename = local_rename, + .chown = local_chown, + .utimensat = local_utimensat, + .remove = local_remove, + .fsync = local_fsync, + .statfs = local_statfs, + .lgetxattr = local_lgetxattr, + .llistxattr = local_llistxattr, + .lsetxattr = local_lsetxattr, + .lremovexattr = local_lremovexattr, + .name_to_path = local_name_to_path, + .renameat = local_renameat, + .unlinkat = local_unlinkat, +}; diff --git a/qemu/hw/9pfs/9p-posix-acl.c b/qemu/hw/9pfs/9p-posix-acl.c new file mode 100644 index 000000000..ec003181c --- /dev/null +++ b/qemu/hw/9pfs/9p-posix-acl.c @@ -0,0 +1,184 @@ +/* + * 9p system.posix* xattr callback + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Aneesh Kumar K.V + * + * 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 "qemu/xattr.h" +#include "9p.h" +#include "fsdev/file-op-9p.h" +#include "9p-xattr.h" + +#define MAP_ACL_ACCESS "user.virtfs.system.posix_acl_access" +#define MAP_ACL_DEFAULT "user.virtfs.system.posix_acl_default" +#define ACL_ACCESS "system.posix_acl_access" +#define ACL_DEFAULT "system.posix_acl_default" + +static ssize_t mp_pacl_getxattr(FsContext *ctx, const char *path, + const char *name, void *value, size_t size) +{ + char *buffer; + ssize_t ret; + + buffer = rpath(ctx, path); + ret = lgetxattr(buffer, MAP_ACL_ACCESS, value, size); + g_free(buffer); + return ret; +} + +static ssize_t mp_pacl_listxattr(FsContext *ctx, const char *path, + char *name, void *value, size_t osize) +{ + ssize_t len = sizeof(ACL_ACCESS); + + if (!value) { + return len; + } + + if (osize < len) { + errno = ERANGE; + return -1; + } + + /* len includes the trailing NUL */ + memcpy(value, ACL_ACCESS, len); + return 0; +} + +static int mp_pacl_setxattr(FsContext *ctx, const char *path, const char *name, + void *value, size_t size, int flags) +{ + char *buffer; + int ret; + + buffer = rpath(ctx, path); + ret = lsetxattr(buffer, MAP_ACL_ACCESS, value, size, flags); + g_free(buffer); + return ret; +} + +static int mp_pacl_removexattr(FsContext *ctx, + const char *path, const char *name) +{ + int ret; + char *buffer; + + buffer = rpath(ctx, path); + ret = lremovexattr(buffer, MAP_ACL_ACCESS); + if (ret == -1 && errno == ENODATA) { + /* + * We don't get ENODATA error when trying to remove a + * posix acl that is not present. So don't throw the error + * even in case of mapped security model + */ + errno = 0; + ret = 0; + } + g_free(buffer); + return ret; +} + +static ssize_t mp_dacl_getxattr(FsContext *ctx, const char *path, + const char *name, void *value, size_t size) +{ + char *buffer; + ssize_t ret; + + buffer = rpath(ctx, path); + ret = lgetxattr(buffer, MAP_ACL_DEFAULT, value, size); + g_free(buffer); + return ret; +} + +static ssize_t mp_dacl_listxattr(FsContext *ctx, const char *path, + char *name, void *value, size_t osize) +{ + ssize_t len = sizeof(ACL_DEFAULT); + + if (!value) { + return len; + } + + if (osize < len) { + errno = ERANGE; + return -1; + } + + /* len includes the trailing NUL */ + memcpy(value, ACL_DEFAULT, len); + return 0; +} + +static int mp_dacl_setxattr(FsContext *ctx, const char *path, const char *name, + void *value, size_t size, int flags) +{ + char *buffer; + int ret; + + buffer = rpath(ctx, path); + ret = lsetxattr(buffer, MAP_ACL_DEFAULT, value, size, flags); + g_free(buffer); + return ret; +} + +static int mp_dacl_removexattr(FsContext *ctx, + const char *path, const char *name) +{ + int ret; + char *buffer; + + buffer = rpath(ctx, path); + ret = lremovexattr(buffer, MAP_ACL_DEFAULT); + if (ret == -1 && errno == ENODATA) { + /* + * We don't get ENODATA error when trying to remove a + * posix acl that is not present. So don't throw the error + * even in case of mapped security model + */ + errno = 0; + ret = 0; + } + g_free(buffer); + return ret; +} + + +XattrOperations mapped_pacl_xattr = { + .name = "system.posix_acl_access", + .getxattr = mp_pacl_getxattr, + .setxattr = mp_pacl_setxattr, + .listxattr = mp_pacl_listxattr, + .removexattr = mp_pacl_removexattr, +}; + +XattrOperations mapped_dacl_xattr = { + .name = "system.posix_acl_default", + .getxattr = mp_dacl_getxattr, + .setxattr = mp_dacl_setxattr, + .listxattr = mp_dacl_listxattr, + .removexattr = mp_dacl_removexattr, +}; + +XattrOperations passthrough_acl_xattr = { + .name = "system.posix_acl_", + .getxattr = pt_getxattr, + .setxattr = pt_setxattr, + .listxattr = pt_listxattr, + .removexattr = pt_removexattr, +}; + +XattrOperations none_acl_xattr = { + .name = "system.posix_acl_", + .getxattr = notsup_getxattr, + .setxattr = notsup_setxattr, + .listxattr = notsup_listxattr, + .removexattr = notsup_removexattr, +}; diff --git a/qemu/hw/9pfs/9p-proxy.c b/qemu/hw/9pfs/9p-proxy.c new file mode 100644 index 000000000..00a4eb2a7 --- /dev/null +++ b/qemu/hw/9pfs/9p-proxy.c @@ -0,0 +1,1220 @@ +/* + * 9p Proxy callback + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * M. Mohan Kumar + * + * 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 +#include +#include "9p.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "fsdev/qemu-fsdev.h" +#include "9p-proxy.h" + +typedef struct V9fsProxy { + int sockfd; + QemuMutex mutex; + struct iovec in_iovec; + struct iovec out_iovec; +} V9fsProxy; + +/* + * Return received file descriptor on success in *status. + * errno is also returned on *status (which will be < 0) + * return < 0 on transport error. + */ +static int v9fs_receivefd(int sockfd, int *status) +{ + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg; + int retval, data, fd; + union MsgControl msg_control; + + iov.iov_base = &data; + iov.iov_len = sizeof(data); + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &msg_control; + msg.msg_controllen = sizeof(msg_control); + + do { + retval = recvmsg(sockfd, &msg, 0); + } while (retval < 0 && errno == EINTR); + if (retval <= 0) { + return retval; + } + /* + * data is set to V9FS_FD_VALID, if ancillary data is sent. If this + * request doesn't need ancillary data (fd) or an error occurred, + * data is set to negative errno value. + */ + if (data != V9FS_FD_VALID) { + *status = data; + return 0; + } + /* + * File descriptor (fd) is sent in the ancillary data. Check if we + * indeed received it. One of the reasons to fail to receive it is if + * we exceeded the maximum number of file descriptors! + */ + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || + cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS) { + continue; + } + fd = *((int *)CMSG_DATA(cmsg)); + *status = fd; + return 0; + } + *status = -ENFILE; /* Ancillary data sent but not received */ + return 0; +} + +static ssize_t socket_read(int sockfd, void *buff, size_t size) +{ + ssize_t retval, total = 0; + + while (size) { + retval = read(sockfd, buff, size); + if (retval == 0) { + return -EIO; + } + if (retval < 0) { + if (errno == EINTR) { + continue; + } + return -errno; + } + size -= retval; + buff += retval; + total += retval; + } + return total; +} + +/* Converts proxy_statfs to VFS statfs structure */ +static void prstatfs_to_statfs(struct statfs *stfs, ProxyStatFS *prstfs) +{ + memset(stfs, 0, sizeof(*stfs)); + stfs->f_type = prstfs->f_type; + stfs->f_bsize = prstfs->f_bsize; + stfs->f_blocks = prstfs->f_blocks; + stfs->f_bfree = prstfs->f_bfree; + stfs->f_bavail = prstfs->f_bavail; + stfs->f_files = prstfs->f_files; + stfs->f_ffree = prstfs->f_ffree; + stfs->f_fsid.__val[0] = prstfs->f_fsid[0] & 0xFFFFFFFFU; + stfs->f_fsid.__val[1] = prstfs->f_fsid[1] >> 32 & 0xFFFFFFFFU; + stfs->f_namelen = prstfs->f_namelen; + stfs->f_frsize = prstfs->f_frsize; +} + +/* Converts proxy_stat structure to VFS stat structure */ +static void prstat_to_stat(struct stat *stbuf, ProxyStat *prstat) +{ + memset(stbuf, 0, sizeof(*stbuf)); + stbuf->st_dev = prstat->st_dev; + stbuf->st_ino = prstat->st_ino; + stbuf->st_nlink = prstat->st_nlink; + stbuf->st_mode = prstat->st_mode; + stbuf->st_uid = prstat->st_uid; + stbuf->st_gid = prstat->st_gid; + stbuf->st_rdev = prstat->st_rdev; + stbuf->st_size = prstat->st_size; + stbuf->st_blksize = prstat->st_blksize; + stbuf->st_blocks = prstat->st_blocks; + stbuf->st_atim.tv_sec = prstat->st_atim_sec; + stbuf->st_atim.tv_nsec = prstat->st_atim_nsec; + stbuf->st_mtime = prstat->st_mtim_sec; + stbuf->st_mtim.tv_nsec = prstat->st_mtim_nsec; + stbuf->st_ctime = prstat->st_ctim_sec; + stbuf->st_ctim.tv_nsec = prstat->st_ctim_nsec; +} + +/* + * Response contains two parts + * {header, data} + * header.type == T_ERROR, data -> -errno + * header.type == T_SUCCESS, data -> response + * size of errno/response is given by header.size + * returns < 0, on transport error. response is + * valid only if status >= 0. + */ +static int v9fs_receive_response(V9fsProxy *proxy, int type, + int *status, void *response) +{ + int retval; + ProxyHeader header; + struct iovec *reply = &proxy->in_iovec; + + *status = 0; + reply->iov_len = 0; + retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ); + if (retval < 0) { + return retval; + } + reply->iov_len = PROXY_HDR_SZ; + proxy_unmarshal(reply, 0, "dd", &header.type, &header.size); + /* + * if response size > PROXY_MAX_IO_SZ, read the response but ignore it and + * return -ENOBUFS + */ + if (header.size > PROXY_MAX_IO_SZ) { + int count; + while (header.size > 0) { + count = MIN(PROXY_MAX_IO_SZ, header.size); + count = socket_read(proxy->sockfd, reply->iov_base, count); + if (count < 0) { + return count; + } + header.size -= count; + } + *status = -ENOBUFS; + return 0; + } + + retval = socket_read(proxy->sockfd, + reply->iov_base + PROXY_HDR_SZ, header.size); + if (retval < 0) { + return retval; + } + reply->iov_len += header.size; + /* there was an error during processing request */ + if (header.type == T_ERROR) { + int ret; + ret = proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status); + if (ret < 0) { + *status = ret; + } + return 0; + } + + switch (type) { + case T_LSTAT: { + ProxyStat prstat; + retval = proxy_unmarshal(reply, PROXY_HDR_SZ, + "qqqdddqqqqqqqqqq", &prstat.st_dev, + &prstat.st_ino, &prstat.st_nlink, + &prstat.st_mode, &prstat.st_uid, + &prstat.st_gid, &prstat.st_rdev, + &prstat.st_size, &prstat.st_blksize, + &prstat.st_blocks, + &prstat.st_atim_sec, &prstat.st_atim_nsec, + &prstat.st_mtim_sec, &prstat.st_mtim_nsec, + &prstat.st_ctim_sec, &prstat.st_ctim_nsec); + prstat_to_stat(response, &prstat); + break; + } + case T_STATFS: { + ProxyStatFS prstfs; + retval = proxy_unmarshal(reply, PROXY_HDR_SZ, + "qqqqqqqqqqq", &prstfs.f_type, + &prstfs.f_bsize, &prstfs.f_blocks, + &prstfs.f_bfree, &prstfs.f_bavail, + &prstfs.f_files, &prstfs.f_ffree, + &prstfs.f_fsid[0], &prstfs.f_fsid[1], + &prstfs.f_namelen, &prstfs.f_frsize); + prstatfs_to_statfs(response, &prstfs); + break; + } + case T_READLINK: { + V9fsString target; + v9fs_string_init(&target); + retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &target); + strcpy(response, target.data); + v9fs_string_free(&target); + break; + } + case T_LGETXATTR: + case T_LLISTXATTR: { + V9fsString xattr; + v9fs_string_init(&xattr); + retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &xattr); + memcpy(response, xattr.data, xattr.size); + v9fs_string_free(&xattr); + break; + } + case T_GETVERSION: + proxy_unmarshal(reply, PROXY_HDR_SZ, "q", response); + break; + default: + return -1; + } + if (retval < 0) { + *status = retval; + } + return 0; +} + +/* + * return < 0 on transport error. + * *status is valid only if return >= 0 + */ +static int v9fs_receive_status(V9fsProxy *proxy, + struct iovec *reply, int *status) +{ + int retval; + ProxyHeader header; + + *status = 0; + reply->iov_len = 0; + retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ); + if (retval < 0) { + return retval; + } + reply->iov_len = PROXY_HDR_SZ; + proxy_unmarshal(reply, 0, "dd", &header.type, &header.size); + if (header.size != sizeof(int)) { + *status = -ENOBUFS; + return 0; + } + retval = socket_read(proxy->sockfd, + reply->iov_base + PROXY_HDR_SZ, header.size); + if (retval < 0) { + return retval; + } + reply->iov_len += header.size; + proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status); + return 0; +} + +/* + * Proxy->header and proxy->request written to socket by QEMU process. + * This request read by proxy helper process + * returns 0 on success and -errno on error + */ +static int v9fs_request(V9fsProxy *proxy, int type, + void *response, const char *fmt, ...) +{ + dev_t rdev; + va_list ap; + int size = 0; + int retval = 0; + uint64_t offset; + ProxyHeader header = { 0, 0}; + struct timespec spec[2]; + int flags, mode, uid, gid; + V9fsString *name, *value; + V9fsString *path, *oldpath; + struct iovec *iovec = NULL, *reply = NULL; + + qemu_mutex_lock(&proxy->mutex); + + if (proxy->sockfd == -1) { + retval = -EIO; + goto err_out; + } + iovec = &proxy->out_iovec; + reply = &proxy->in_iovec; + va_start(ap, fmt); + switch (type) { + case T_OPEN: + path = va_arg(ap, V9fsString *); + flags = va_arg(ap, int); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, flags); + if (retval > 0) { + header.size = retval; + header.type = T_OPEN; + } + break; + case T_CREATE: + path = va_arg(ap, V9fsString *); + flags = va_arg(ap, int); + mode = va_arg(ap, int); + uid = va_arg(ap, int); + gid = va_arg(ap, int); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sdddd", path, + flags, mode, uid, gid); + if (retval > 0) { + header.size = retval; + header.type = T_CREATE; + } + break; + case T_MKNOD: + path = va_arg(ap, V9fsString *); + mode = va_arg(ap, int); + rdev = va_arg(ap, long int); + uid = va_arg(ap, int); + gid = va_arg(ap, int); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddsdq", + uid, gid, path, mode, rdev); + if (retval > 0) { + header.size = retval; + header.type = T_MKNOD; + } + break; + case T_MKDIR: + path = va_arg(ap, V9fsString *); + mode = va_arg(ap, int); + uid = va_arg(ap, int); + gid = va_arg(ap, int); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddsd", + uid, gid, path, mode); + if (retval > 0) { + header.size = retval; + header.type = T_MKDIR; + } + break; + case T_SYMLINK: + oldpath = va_arg(ap, V9fsString *); + path = va_arg(ap, V9fsString *); + uid = va_arg(ap, int); + gid = va_arg(ap, int); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddss", + uid, gid, oldpath, path); + if (retval > 0) { + header.size = retval; + header.type = T_SYMLINK; + } + break; + case T_LINK: + oldpath = va_arg(ap, V9fsString *); + path = va_arg(ap, V9fsString *); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", + oldpath, path); + if (retval > 0) { + header.size = retval; + header.type = T_LINK; + } + break; + case T_LSTAT: + path = va_arg(ap, V9fsString *); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); + if (retval > 0) { + header.size = retval; + header.type = T_LSTAT; + } + break; + case T_READLINK: + path = va_arg(ap, V9fsString *); + size = va_arg(ap, int); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, size); + if (retval > 0) { + header.size = retval; + header.type = T_READLINK; + } + break; + case T_STATFS: + path = va_arg(ap, V9fsString *); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); + if (retval > 0) { + header.size = retval; + header.type = T_STATFS; + } + break; + case T_CHMOD: + path = va_arg(ap, V9fsString *); + mode = va_arg(ap, int); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, mode); + if (retval > 0) { + header.size = retval; + header.type = T_CHMOD; + } + break; + case T_CHOWN: + path = va_arg(ap, V9fsString *); + uid = va_arg(ap, int); + gid = va_arg(ap, int); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sdd", path, uid, gid); + if (retval > 0) { + header.size = retval; + header.type = T_CHOWN; + } + break; + case T_TRUNCATE: + path = va_arg(ap, V9fsString *); + offset = va_arg(ap, uint64_t); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sq", path, offset); + if (retval > 0) { + header.size = retval; + header.type = T_TRUNCATE; + } + break; + case T_UTIME: + path = va_arg(ap, V9fsString *); + spec[0].tv_sec = va_arg(ap, long); + spec[0].tv_nsec = va_arg(ap, long); + spec[1].tv_sec = va_arg(ap, long); + spec[1].tv_nsec = va_arg(ap, long); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sqqqq", path, + spec[0].tv_sec, spec[1].tv_nsec, + spec[1].tv_sec, spec[1].tv_nsec); + if (retval > 0) { + header.size = retval; + header.type = T_UTIME; + } + break; + case T_RENAME: + oldpath = va_arg(ap, V9fsString *); + path = va_arg(ap, V9fsString *); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", oldpath, path); + if (retval > 0) { + header.size = retval; + header.type = T_RENAME; + } + break; + case T_REMOVE: + path = va_arg(ap, V9fsString *); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); + if (retval > 0) { + header.size = retval; + header.type = T_REMOVE; + } + break; + case T_LGETXATTR: + size = va_arg(ap, int); + path = va_arg(ap, V9fsString *); + name = va_arg(ap, V9fsString *); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, + "dss", size, path, name); + if (retval > 0) { + header.size = retval; + header.type = T_LGETXATTR; + } + break; + case T_LLISTXATTR: + size = va_arg(ap, int); + path = va_arg(ap, V9fsString *); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ds", size, path); + if (retval > 0) { + header.size = retval; + header.type = T_LLISTXATTR; + } + break; + case T_LSETXATTR: + path = va_arg(ap, V9fsString *); + name = va_arg(ap, V9fsString *); + value = va_arg(ap, V9fsString *); + size = va_arg(ap, int); + flags = va_arg(ap, int); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sssdd", + path, name, value, size, flags); + if (retval > 0) { + header.size = retval; + header.type = T_LSETXATTR; + } + break; + case T_LREMOVEXATTR: + path = va_arg(ap, V9fsString *); + name = va_arg(ap, V9fsString *); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", path, name); + if (retval > 0) { + header.size = retval; + header.type = T_LREMOVEXATTR; + } + break; + case T_GETVERSION: + path = va_arg(ap, V9fsString *); + retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); + if (retval > 0) { + header.size = retval; + header.type = T_GETVERSION; + } + break; + default: + error_report("Invalid type %d", type); + retval = -EINVAL; + break; + } + va_end(ap); + + if (retval < 0) { + goto err_out; + } + + /* marshal the header details */ + proxy_marshal(iovec, 0, "dd", header.type, header.size); + header.size += PROXY_HDR_SZ; + + retval = qemu_write_full(proxy->sockfd, iovec->iov_base, header.size); + if (retval != header.size) { + goto close_error; + } + + switch (type) { + case T_OPEN: + case T_CREATE: + /* + * A file descriptor is returned as response for + * T_OPEN,T_CREATE on success + */ + if (v9fs_receivefd(proxy->sockfd, &retval) < 0) { + goto close_error; + } + break; + case T_MKNOD: + case T_MKDIR: + case T_SYMLINK: + case T_LINK: + case T_CHMOD: + case T_CHOWN: + case T_RENAME: + case T_TRUNCATE: + case T_UTIME: + case T_REMOVE: + case T_LSETXATTR: + case T_LREMOVEXATTR: + if (v9fs_receive_status(proxy, reply, &retval) < 0) { + goto close_error; + } + break; + case T_LSTAT: + case T_READLINK: + case T_STATFS: + case T_GETVERSION: + if (v9fs_receive_response(proxy, type, &retval, response) < 0) { + goto close_error; + } + break; + case T_LGETXATTR: + case T_LLISTXATTR: + if (!size) { + if (v9fs_receive_status(proxy, reply, &retval) < 0) { + goto close_error; + } + } else { + if (v9fs_receive_response(proxy, type, &retval, response) < 0) { + goto close_error; + } + } + break; + } + +err_out: + qemu_mutex_unlock(&proxy->mutex); + return retval; + +close_error: + close(proxy->sockfd); + proxy->sockfd = -1; + qemu_mutex_unlock(&proxy->mutex); + return -EIO; +} + +static int proxy_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf) +{ + int retval; + retval = v9fs_request(fs_ctx->private, T_LSTAT, stbuf, "s", fs_path); + if (retval < 0) { + errno = -retval; + return -1; + } + return retval; +} + +static ssize_t proxy_readlink(FsContext *fs_ctx, V9fsPath *fs_path, + char *buf, size_t bufsz) +{ + int retval; + retval = v9fs_request(fs_ctx->private, T_READLINK, buf, "sd", + fs_path, bufsz); + if (retval < 0) { + errno = -retval; + return -1; + } + return strlen(buf); +} + +static int proxy_close(FsContext *ctx, V9fsFidOpenState *fs) +{ + return close(fs->fd); +} + +static int proxy_closedir(FsContext *ctx, V9fsFidOpenState *fs) +{ + return closedir(fs->dir); +} + +static int proxy_open(FsContext *ctx, V9fsPath *fs_path, + int flags, V9fsFidOpenState *fs) +{ + fs->fd = v9fs_request(ctx->private, T_OPEN, NULL, "sd", fs_path, flags); + if (fs->fd < 0) { + errno = -fs->fd; + fs->fd = -1; + } + return fs->fd; +} + +static int proxy_opendir(FsContext *ctx, + V9fsPath *fs_path, V9fsFidOpenState *fs) +{ + int serrno, fd; + + fs->dir = NULL; + fd = v9fs_request(ctx->private, T_OPEN, NULL, "sd", fs_path, O_DIRECTORY); + if (fd < 0) { + errno = -fd; + return -1; + } + fs->dir = fdopendir(fd); + if (!fs->dir) { + serrno = errno; + close(fd); + errno = serrno; + return -1; + } + return 0; +} + +static void proxy_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) +{ + rewinddir(fs->dir); +} + +static off_t proxy_telldir(FsContext *ctx, V9fsFidOpenState *fs) +{ + return telldir(fs->dir); +} + +static int proxy_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, + struct dirent *entry, + struct dirent **result) +{ + return readdir_r(fs->dir, entry, result); +} + +static void proxy_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) +{ + seekdir(fs->dir, off); +} + +static ssize_t proxy_preadv(FsContext *ctx, V9fsFidOpenState *fs, + const struct iovec *iov, + int iovcnt, off_t offset) +{ + ssize_t ret; +#ifdef CONFIG_PREADV + ret = preadv(fs->fd, iov, iovcnt, offset); +#else + ret = lseek(fs->fd, offset, SEEK_SET); + if (ret >= 0) { + ret = readv(fs->fd, iov, iovcnt); + } +#endif + return ret; +} + +static ssize_t proxy_pwritev(FsContext *ctx, V9fsFidOpenState *fs, + const struct iovec *iov, + int iovcnt, off_t offset) +{ + ssize_t ret; + +#ifdef CONFIG_PREADV + ret = pwritev(fs->fd, iov, iovcnt, offset); +#else + ret = lseek(fs->fd, offset, SEEK_SET); + if (ret >= 0) { + ret = writev(fs->fd, iov, iovcnt); + } +#endif +#ifdef CONFIG_SYNC_FILE_RANGE + if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { + /* + * Initiate a writeback. This is not a data integrity sync. + * We want to ensure that we don't leave dirty pages in the cache + * after write when writeout=immediate is sepcified. + */ + sync_file_range(fs->fd, offset, ret, + SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); + } +#endif + return ret; +} + +static int proxy_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) +{ + int retval; + retval = v9fs_request(fs_ctx->private, T_CHMOD, NULL, "sd", + fs_path, credp->fc_mode); + if (retval < 0) { + errno = -retval; + } + return retval; +} + +static int proxy_mknod(FsContext *fs_ctx, V9fsPath *dir_path, + const char *name, FsCred *credp) +{ + int retval; + V9fsString fullname; + + v9fs_string_init(&fullname); + v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); + + retval = v9fs_request(fs_ctx->private, T_MKNOD, NULL, "sdqdd", + &fullname, credp->fc_mode, credp->fc_rdev, + credp->fc_uid, credp->fc_gid); + v9fs_string_free(&fullname); + if (retval < 0) { + errno = -retval; + retval = -1; + } + return retval; +} + +static int proxy_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, + const char *name, FsCred *credp) +{ + int retval; + V9fsString fullname; + + v9fs_string_init(&fullname); + v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); + + retval = v9fs_request(fs_ctx->private, T_MKDIR, NULL, "sddd", &fullname, + credp->fc_mode, credp->fc_uid, credp->fc_gid); + v9fs_string_free(&fullname); + if (retval < 0) { + errno = -retval; + retval = -1; + } + v9fs_string_free(&fullname); + return retval; +} + +static int proxy_fstat(FsContext *fs_ctx, int fid_type, + V9fsFidOpenState *fs, struct stat *stbuf) +{ + int fd; + + if (fid_type == P9_FID_DIR) { + fd = dirfd(fs->dir); + } else { + fd = fs->fd; + } + return fstat(fd, stbuf); +} + +static int proxy_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, + int flags, FsCred *credp, V9fsFidOpenState *fs) +{ + V9fsString fullname; + + v9fs_string_init(&fullname); + v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); + + fs->fd = v9fs_request(fs_ctx->private, T_CREATE, NULL, "sdddd", + &fullname, flags, credp->fc_mode, + credp->fc_uid, credp->fc_gid); + v9fs_string_free(&fullname); + if (fs->fd < 0) { + errno = -fs->fd; + fs->fd = -1; + } + return fs->fd; +} + +static int proxy_symlink(FsContext *fs_ctx, const char *oldpath, + V9fsPath *dir_path, const char *name, FsCred *credp) +{ + int retval; + V9fsString fullname, target; + + v9fs_string_init(&fullname); + v9fs_string_init(&target); + + v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); + v9fs_string_sprintf(&target, "%s", oldpath); + + retval = v9fs_request(fs_ctx->private, T_SYMLINK, NULL, "ssdd", + &target, &fullname, credp->fc_uid, credp->fc_gid); + v9fs_string_free(&fullname); + v9fs_string_free(&target); + if (retval < 0) { + errno = -retval; + retval = -1; + } + return retval; +} + +static int proxy_link(FsContext *ctx, V9fsPath *oldpath, + V9fsPath *dirpath, const char *name) +{ + int retval; + V9fsString newpath; + + v9fs_string_init(&newpath); + v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name); + + retval = v9fs_request(ctx->private, T_LINK, NULL, "ss", oldpath, &newpath); + v9fs_string_free(&newpath); + if (retval < 0) { + errno = -retval; + retval = -1; + } + return retval; +} + +static int proxy_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) +{ + int retval; + + retval = v9fs_request(ctx->private, T_TRUNCATE, NULL, "sq", fs_path, size); + if (retval < 0) { + errno = -retval; + return -1; + } + return 0; +} + +static int proxy_rename(FsContext *ctx, const char *oldpath, + const char *newpath) +{ + int retval; + V9fsString oldname, newname; + + v9fs_string_init(&oldname); + v9fs_string_init(&newname); + + v9fs_string_sprintf(&oldname, "%s", oldpath); + v9fs_string_sprintf(&newname, "%s", newpath); + retval = v9fs_request(ctx->private, T_RENAME, NULL, "ss", + &oldname, &newname); + v9fs_string_free(&oldname); + v9fs_string_free(&newname); + if (retval < 0) { + errno = -retval; + } + return retval; +} + +static int proxy_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) +{ + int retval; + retval = v9fs_request(fs_ctx->private, T_CHOWN, NULL, "sdd", + fs_path, credp->fc_uid, credp->fc_gid); + if (retval < 0) { + errno = -retval; + } + return retval; +} + +static int proxy_utimensat(FsContext *s, V9fsPath *fs_path, + const struct timespec *buf) +{ + int retval; + retval = v9fs_request(s->private, T_UTIME, NULL, "sqqqq", + fs_path, + buf[0].tv_sec, buf[0].tv_nsec, + buf[1].tv_sec, buf[1].tv_nsec); + if (retval < 0) { + errno = -retval; + } + return retval; +} + +static int proxy_remove(FsContext *ctx, const char *path) +{ + int retval; + V9fsString name; + v9fs_string_init(&name); + v9fs_string_sprintf(&name, "%s", path); + retval = v9fs_request(ctx->private, T_REMOVE, NULL, "s", &name); + v9fs_string_free(&name); + if (retval < 0) { + errno = -retval; + } + return retval; +} + +static int proxy_fsync(FsContext *ctx, int fid_type, + V9fsFidOpenState *fs, int datasync) +{ + int fd; + + if (fid_type == P9_FID_DIR) { + fd = dirfd(fs->dir); + } else { + fd = fs->fd; + } + + if (datasync) { + return qemu_fdatasync(fd); + } else { + return fsync(fd); + } +} + +static int proxy_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf) +{ + int retval; + retval = v9fs_request(s->private, T_STATFS, stbuf, "s", fs_path); + if (retval < 0) { + errno = -retval; + return -1; + } + return retval; +} + +static ssize_t proxy_lgetxattr(FsContext *ctx, V9fsPath *fs_path, + const char *name, void *value, size_t size) +{ + int retval; + V9fsString xname; + + v9fs_string_init(&xname); + v9fs_string_sprintf(&xname, "%s", name); + retval = v9fs_request(ctx->private, T_LGETXATTR, value, "dss", size, + fs_path, &xname); + v9fs_string_free(&xname); + if (retval < 0) { + errno = -retval; + } + return retval; +} + +static ssize_t proxy_llistxattr(FsContext *ctx, V9fsPath *fs_path, + void *value, size_t size) +{ + int retval; + retval = v9fs_request(ctx->private, T_LLISTXATTR, value, "ds", size, + fs_path); + if (retval < 0) { + errno = -retval; + } + return retval; +} + +static int proxy_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, + void *value, size_t size, int flags) +{ + int retval; + V9fsString xname, xvalue; + + v9fs_string_init(&xname); + v9fs_string_sprintf(&xname, "%s", name); + + v9fs_string_init(&xvalue); + xvalue.size = size; + xvalue.data = g_malloc(size); + memcpy(xvalue.data, value, size); + + retval = v9fs_request(ctx->private, T_LSETXATTR, value, "sssdd", + fs_path, &xname, &xvalue, size, flags); + v9fs_string_free(&xname); + v9fs_string_free(&xvalue); + if (retval < 0) { + errno = -retval; + } + return retval; +} + +static int proxy_lremovexattr(FsContext *ctx, V9fsPath *fs_path, + const char *name) +{ + int retval; + V9fsString xname; + + v9fs_string_init(&xname); + v9fs_string_sprintf(&xname, "%s", name); + retval = v9fs_request(ctx->private, T_LREMOVEXATTR, NULL, "ss", + fs_path, &xname); + v9fs_string_free(&xname); + if (retval < 0) { + errno = -retval; + } + return retval; +} + +static int proxy_name_to_path(FsContext *ctx, V9fsPath *dir_path, + const char *name, V9fsPath *target) +{ + if (dir_path) { + v9fs_string_sprintf((V9fsString *)target, "%s/%s", + dir_path->data, name); + } else { + v9fs_string_sprintf((V9fsString *)target, "%s", name); + } + /* Bump the size for including terminating NULL */ + target->size++; + return 0; +} + +static int proxy_renameat(FsContext *ctx, V9fsPath *olddir, + const char *old_name, V9fsPath *newdir, + const char *new_name) +{ + int ret; + V9fsString old_full_name, new_full_name; + + v9fs_string_init(&old_full_name); + v9fs_string_init(&new_full_name); + + v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name); + v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name); + + ret = proxy_rename(ctx, old_full_name.data, new_full_name.data); + v9fs_string_free(&old_full_name); + v9fs_string_free(&new_full_name); + return ret; +} + +static int proxy_unlinkat(FsContext *ctx, V9fsPath *dir, + const char *name, int flags) +{ + int ret; + V9fsString fullname; + v9fs_string_init(&fullname); + + v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name); + ret = proxy_remove(ctx, fullname.data); + v9fs_string_free(&fullname); + + return ret; +} + +static int proxy_ioc_getversion(FsContext *fs_ctx, V9fsPath *path, + mode_t st_mode, uint64_t *st_gen) +{ + int err; + + /* Do not try to open special files like device nodes, fifos etc + * we can get fd for regular files and directories only + */ + if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { + errno = ENOTTY; + return -1; + } + err = v9fs_request(fs_ctx->private, T_GETVERSION, st_gen, "s", path); + if (err < 0) { + errno = -err; + err = -1; + } + return err; +} + +static int connect_namedsocket(const char *path) +{ + int sockfd, size; + struct sockaddr_un helper; + + if (strlen(path) >= sizeof(helper.sun_path)) { + error_report("Socket name too long"); + return -1; + } + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sockfd < 0) { + error_report("Failed to create socket: %s", strerror(errno)); + return -1; + } + strcpy(helper.sun_path, path); + helper.sun_family = AF_UNIX; + size = strlen(helper.sun_path) + sizeof(helper.sun_family); + if (connect(sockfd, (struct sockaddr *)&helper, size) < 0) { + error_report("Failed to connect to %s: %s", path, strerror(errno)); + close(sockfd); + return -1; + } + + /* remove the socket for security reasons */ + unlink(path); + return sockfd; +} + +static int proxy_parse_opts(QemuOpts *opts, struct FsDriverEntry *fs) +{ + const char *socket = qemu_opt_get(opts, "socket"); + const char *sock_fd = qemu_opt_get(opts, "sock_fd"); + + if (!socket && !sock_fd) { + error_report("Must specify either socket or sock_fd"); + return -1; + } + if (socket && sock_fd) { + error_report("Both socket and sock_fd options specified"); + return -1; + } + if (socket) { + fs->path = g_strdup(socket); + fs->export_flags = V9FS_PROXY_SOCK_NAME; + } else { + fs->path = g_strdup(sock_fd); + fs->export_flags = V9FS_PROXY_SOCK_FD; + } + return 0; +} + +static int proxy_init(FsContext *ctx) +{ + V9fsProxy *proxy = g_malloc(sizeof(V9fsProxy)); + int sock_id; + + if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) { + sock_id = connect_namedsocket(ctx->fs_root); + } else { + sock_id = atoi(ctx->fs_root); + if (sock_id < 0) { + error_report("Socket descriptor not initialized"); + } + } + if (sock_id < 0) { + g_free(proxy); + return -1; + } + g_free(ctx->fs_root); + ctx->fs_root = NULL; + + proxy->in_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); + proxy->in_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; + proxy->out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); + proxy->out_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; + + ctx->private = proxy; + proxy->sockfd = sock_id; + qemu_mutex_init(&proxy->mutex); + + ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; + ctx->exops.get_st_gen = proxy_ioc_getversion; + return 0; +} + +FileOperations proxy_ops = { + .parse_opts = proxy_parse_opts, + .init = proxy_init, + .lstat = proxy_lstat, + .readlink = proxy_readlink, + .close = proxy_close, + .closedir = proxy_closedir, + .open = proxy_open, + .opendir = proxy_opendir, + .rewinddir = proxy_rewinddir, + .telldir = proxy_telldir, + .readdir_r = proxy_readdir_r, + .seekdir = proxy_seekdir, + .preadv = proxy_preadv, + .pwritev = proxy_pwritev, + .chmod = proxy_chmod, + .mknod = proxy_mknod, + .mkdir = proxy_mkdir, + .fstat = proxy_fstat, + .open2 = proxy_open2, + .symlink = proxy_symlink, + .link = proxy_link, + .truncate = proxy_truncate, + .rename = proxy_rename, + .chown = proxy_chown, + .utimensat = proxy_utimensat, + .remove = proxy_remove, + .fsync = proxy_fsync, + .statfs = proxy_statfs, + .lgetxattr = proxy_lgetxattr, + .llistxattr = proxy_llistxattr, + .lsetxattr = proxy_lsetxattr, + .lremovexattr = proxy_lremovexattr, + .name_to_path = proxy_name_to_path, + .renameat = proxy_renameat, + .unlinkat = proxy_unlinkat, +}; diff --git a/qemu/hw/9pfs/9p-proxy.h b/qemu/hw/9pfs/9p-proxy.h new file mode 100644 index 000000000..ba9ca203d --- /dev/null +++ b/qemu/hw/9pfs/9p-proxy.h @@ -0,0 +1,95 @@ +/* + * 9p Proxy callback + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * M. Mohan Kumar + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ +#ifndef _QEMU_9P_PROXY_H +#define _QEMU_9P_PROXY_H + +#define PROXY_MAX_IO_SZ (64 * 1024) +#define V9FS_FD_VALID INT_MAX + +/* + * proxy iovec only support one element and + * marsha/unmarshal doesn't do little endian conversion. + */ +#define proxy_unmarshal(in_sg, offset, fmt, args...) \ + v9fs_iov_unmarshal(in_sg, 1, offset, 0, fmt, ##args) +#define proxy_marshal(out_sg, offset, fmt, args...) \ + v9fs_iov_marshal(out_sg, 1, offset, 0, fmt, ##args) + +union MsgControl { + struct cmsghdr cmsg; + char control[CMSG_SPACE(sizeof(int))]; +}; + +typedef struct { + uint32_t type; + uint32_t size; +} ProxyHeader; + +#define PROXY_HDR_SZ (sizeof(ProxyHeader)) + +enum { + T_SUCCESS = 0, + T_ERROR, + T_OPEN, + T_CREATE, + T_MKNOD, + T_MKDIR, + T_SYMLINK, + T_LINK, + T_LSTAT, + T_READLINK, + T_STATFS, + T_CHMOD, + T_CHOWN, + T_TRUNCATE, + T_UTIME, + T_RENAME, + T_REMOVE, + T_LGETXATTR, + T_LLISTXATTR, + T_LSETXATTR, + T_LREMOVEXATTR, + T_GETVERSION, +}; + +typedef struct { + uint64_t st_dev; + uint64_t st_ino; + uint64_t st_nlink; + uint32_t st_mode; + uint32_t st_uid; + uint32_t st_gid; + uint64_t st_rdev; + uint64_t st_size; + uint64_t st_blksize; + uint64_t st_blocks; + uint64_t st_atim_sec; + uint64_t st_atim_nsec; + uint64_t st_mtim_sec; + uint64_t st_mtim_nsec; + uint64_t st_ctim_sec; + uint64_t st_ctim_nsec; +} ProxyStat; + +typedef struct { + uint64_t f_type; + uint64_t f_bsize; + uint64_t f_blocks; + uint64_t f_bfree; + uint64_t f_bavail; + uint64_t f_files; + uint64_t f_ffree; + uint64_t f_fsid[2]; + uint64_t f_namelen; + uint64_t f_frsize; +} ProxyStatFS; +#endif diff --git a/qemu/hw/9pfs/9p-synth.c b/qemu/hw/9pfs/9p-synth.c new file mode 100644 index 000000000..f1475dfd6 --- /dev/null +++ b/qemu/hw/9pfs/9p-synth.c @@ -0,0 +1,574 @@ +/* + * Virtio 9p synthetic file system support + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Malahal Naineni + * Aneesh Kumar K.V + * + * 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 "hw/virtio/virtio.h" +#include "9p.h" +#include "9p-xattr.h" +#include "fsdev/qemu-fsdev.h" +#include "9p-synth.h" +#include "qemu/rcu.h" +#include "qemu/rcu_queue.h" +#include "qemu/cutils.h" + +/* Root node for synth file system */ +static V9fsSynthNode v9fs_synth_root = { + .name = "/", + .actual_attr = { + .mode = 0555 | S_IFDIR, + .nlink = 1, + }, + .attr = &v9fs_synth_root.actual_attr, +}; + +static QemuMutex v9fs_synth_mutex; +static int v9fs_synth_node_count; +/* set to 1 when the synth fs is ready */ +static int v9fs_synth_fs; + +static V9fsSynthNode *v9fs_add_dir_node(V9fsSynthNode *parent, int mode, + const char *name, + V9fsSynthNodeAttr *attr, int inode) +{ + V9fsSynthNode *node; + + /* Add directory type and remove write bits */ + mode = ((mode & 0777) | S_IFDIR) & ~(S_IWUSR | S_IWGRP | S_IWOTH); + node = g_malloc0(sizeof(V9fsSynthNode)); + if (attr) { + /* We are adding .. or . entries */ + node->attr = attr; + node->attr->nlink++; + } else { + node->attr = &node->actual_attr; + node->attr->inode = inode; + node->attr->nlink = 1; + /* We don't allow write to directories */ + node->attr->mode = mode; + node->attr->write = NULL; + node->attr->read = NULL; + } + node->private = node; + pstrcpy(node->name, sizeof(node->name), name); + QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling); + return node; +} + +int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode, + const char *name, V9fsSynthNode **result) +{ + int ret; + V9fsSynthNode *node, *tmp; + + if (!v9fs_synth_fs) { + return EAGAIN; + } + if (!name || (strlen(name) >= NAME_MAX)) { + return EINVAL; + } + if (!parent) { + parent = &v9fs_synth_root; + } + qemu_mutex_lock(&v9fs_synth_mutex); + QLIST_FOREACH(tmp, &parent->child, sibling) { + if (!strcmp(tmp->name, name)) { + ret = EEXIST; + goto err_out; + } + } + /* Add the name */ + node = v9fs_add_dir_node(parent, mode, name, NULL, v9fs_synth_node_count++); + v9fs_add_dir_node(node, parent->attr->mode, "..", + parent->attr, parent->attr->inode); + v9fs_add_dir_node(node, node->attr->mode, ".", + node->attr, node->attr->inode); + *result = node; + ret = 0; +err_out: + qemu_mutex_unlock(&v9fs_synth_mutex); + return ret; +} + +int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, + const char *name, v9fs_synth_read read, + v9fs_synth_write write, void *arg) +{ + int ret; + V9fsSynthNode *node, *tmp; + + if (!v9fs_synth_fs) { + return EAGAIN; + } + if (!name || (strlen(name) >= NAME_MAX)) { + return EINVAL; + } + if (!parent) { + parent = &v9fs_synth_root; + } + + qemu_mutex_lock(&v9fs_synth_mutex); + QLIST_FOREACH(tmp, &parent->child, sibling) { + if (!strcmp(tmp->name, name)) { + ret = EEXIST; + goto err_out; + } + } + /* Add file type and remove write bits */ + mode = ((mode & 0777) | S_IFREG); + node = g_malloc0(sizeof(V9fsSynthNode)); + node->attr = &node->actual_attr; + node->attr->inode = v9fs_synth_node_count++; + node->attr->nlink = 1; + node->attr->read = read; + node->attr->write = write; + node->attr->mode = mode; + node->private = arg; + pstrcpy(node->name, sizeof(node->name), name); + QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling); + ret = 0; +err_out: + qemu_mutex_unlock(&v9fs_synth_mutex); + return ret; +} + +static void v9fs_synth_fill_statbuf(V9fsSynthNode *node, struct stat *stbuf) +{ + stbuf->st_dev = 0; + stbuf->st_ino = node->attr->inode; + stbuf->st_mode = node->attr->mode; + stbuf->st_nlink = node->attr->nlink; + stbuf->st_uid = 0; + stbuf->st_gid = 0; + stbuf->st_rdev = 0; + stbuf->st_size = 0; + stbuf->st_blksize = 0; + stbuf->st_blocks = 0; + stbuf->st_atime = 0; + stbuf->st_mtime = 0; + stbuf->st_ctime = 0; +} + +static int v9fs_synth_lstat(FsContext *fs_ctx, + V9fsPath *fs_path, struct stat *stbuf) +{ + V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data; + + v9fs_synth_fill_statbuf(node, stbuf); + return 0; +} + +static int v9fs_synth_fstat(FsContext *fs_ctx, int fid_type, + V9fsFidOpenState *fs, struct stat *stbuf) +{ + V9fsSynthOpenState *synth_open = fs->private; + v9fs_synth_fill_statbuf(synth_open->node, stbuf); + return 0; +} + +static int v9fs_synth_opendir(FsContext *ctx, + V9fsPath *fs_path, V9fsFidOpenState *fs) +{ + V9fsSynthOpenState *synth_open; + V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data; + + synth_open = g_malloc(sizeof(*synth_open)); + synth_open->node = node; + node->open_count++; + fs->private = synth_open; + return 0; +} + +static int v9fs_synth_closedir(FsContext *ctx, V9fsFidOpenState *fs) +{ + V9fsSynthOpenState *synth_open = fs->private; + V9fsSynthNode *node = synth_open->node; + + node->open_count--; + g_free(synth_open); + fs->private = NULL; + return 0; +} + +static off_t v9fs_synth_telldir(FsContext *ctx, V9fsFidOpenState *fs) +{ + V9fsSynthOpenState *synth_open = fs->private; + return synth_open->offset; +} + +static void v9fs_synth_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) +{ + V9fsSynthOpenState *synth_open = fs->private; + synth_open->offset = off; +} + +static void v9fs_synth_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) +{ + v9fs_synth_seekdir(ctx, fs, 0); +} + +static void v9fs_synth_direntry(V9fsSynthNode *node, + struct dirent *entry, off_t off) +{ + strcpy(entry->d_name, node->name); + entry->d_ino = node->attr->inode; + entry->d_off = off + 1; +} + +static int v9fs_synth_get_dentry(V9fsSynthNode *dir, struct dirent *entry, + struct dirent **result, off_t off) +{ + int i = 0; + V9fsSynthNode *node; + + rcu_read_lock(); + QLIST_FOREACH(node, &dir->child, sibling) { + /* This is the off child of the directory */ + if (i == off) { + break; + } + i++; + } + rcu_read_unlock(); + if (!node) { + /* end of directory */ + *result = NULL; + return 0; + } + v9fs_synth_direntry(node, entry, off); + *result = entry; + return 0; +} + +static int v9fs_synth_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, + struct dirent *entry, struct dirent **result) +{ + int ret; + V9fsSynthOpenState *synth_open = fs->private; + V9fsSynthNode *node = synth_open->node; + ret = v9fs_synth_get_dentry(node, entry, result, synth_open->offset); + if (!ret && *result != NULL) { + synth_open->offset++; + } + return ret; +} + +static int v9fs_synth_open(FsContext *ctx, V9fsPath *fs_path, + int flags, V9fsFidOpenState *fs) +{ + V9fsSynthOpenState *synth_open; + V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data; + + synth_open = g_malloc(sizeof(*synth_open)); + synth_open->node = node; + node->open_count++; + fs->private = synth_open; + return 0; +} + +static int v9fs_synth_open2(FsContext *fs_ctx, V9fsPath *dir_path, + const char *name, int flags, + FsCred *credp, V9fsFidOpenState *fs) +{ + errno = ENOSYS; + return -1; +} + +static int v9fs_synth_close(FsContext *ctx, V9fsFidOpenState *fs) +{ + V9fsSynthOpenState *synth_open = fs->private; + V9fsSynthNode *node = synth_open->node; + + node->open_count--; + g_free(synth_open); + fs->private = NULL; + return 0; +} + +static ssize_t v9fs_synth_pwritev(FsContext *ctx, V9fsFidOpenState *fs, + const struct iovec *iov, + int iovcnt, off_t offset) +{ + int i, count = 0, wcount; + V9fsSynthOpenState *synth_open = fs->private; + V9fsSynthNode *node = synth_open->node; + if (!node->attr->write) { + errno = EPERM; + return -1; + } + for (i = 0; i < iovcnt; i++) { + wcount = node->attr->write(iov[i].iov_base, iov[i].iov_len, + offset, node->private); + offset += wcount; + count += wcount; + /* If we wrote less than requested. we are done */ + if (wcount < iov[i].iov_len) { + break; + } + } + return count; +} + +static ssize_t v9fs_synth_preadv(FsContext *ctx, V9fsFidOpenState *fs, + const struct iovec *iov, + int iovcnt, off_t offset) +{ + int i, count = 0, rcount; + V9fsSynthOpenState *synth_open = fs->private; + V9fsSynthNode *node = synth_open->node; + if (!node->attr->read) { + errno = EPERM; + return -1; + } + for (i = 0; i < iovcnt; i++) { + rcount = node->attr->read(iov[i].iov_base, iov[i].iov_len, + offset, node->private); + offset += rcount; + count += rcount; + /* If we read less than requested. we are done */ + if (rcount < iov[i].iov_len) { + break; + } + } + return count; +} + +static int v9fs_synth_truncate(FsContext *ctx, V9fsPath *path, off_t offset) +{ + errno = ENOSYS; + return -1; +} + +static int v9fs_synth_chmod(FsContext *fs_ctx, V9fsPath *path, FsCred *credp) +{ + errno = EPERM; + return -1; +} + +static int v9fs_synth_mknod(FsContext *fs_ctx, V9fsPath *path, + const char *buf, FsCred *credp) +{ + errno = EPERM; + return -1; +} + +static int v9fs_synth_mkdir(FsContext *fs_ctx, V9fsPath *path, + const char *buf, FsCred *credp) +{ + errno = EPERM; + return -1; +} + +static ssize_t v9fs_synth_readlink(FsContext *fs_ctx, V9fsPath *path, + char *buf, size_t bufsz) +{ + errno = ENOSYS; + return -1; +} + +static int v9fs_synth_symlink(FsContext *fs_ctx, const char *oldpath, + V9fsPath *newpath, const char *buf, FsCred *credp) +{ + errno = EPERM; + return -1; +} + +static int v9fs_synth_link(FsContext *fs_ctx, V9fsPath *oldpath, + V9fsPath *newpath, const char *buf) +{ + errno = EPERM; + return -1; +} + +static int v9fs_synth_rename(FsContext *ctx, const char *oldpath, + const char *newpath) +{ + errno = EPERM; + return -1; +} + +static int v9fs_synth_chown(FsContext *fs_ctx, V9fsPath *path, FsCred *credp) +{ + errno = EPERM; + return -1; +} + +static int v9fs_synth_utimensat(FsContext *fs_ctx, V9fsPath *path, + const struct timespec *buf) +{ + errno = EPERM; + return 0; +} + +static int v9fs_synth_remove(FsContext *ctx, const char *path) +{ + errno = EPERM; + return -1; +} + +static int v9fs_synth_fsync(FsContext *ctx, int fid_type, + V9fsFidOpenState *fs, int datasync) +{ + errno = ENOSYS; + return 0; +} + +static int v9fs_synth_statfs(FsContext *s, V9fsPath *fs_path, + struct statfs *stbuf) +{ + stbuf->f_type = 0xABCD; + stbuf->f_bsize = 512; + stbuf->f_blocks = 0; + stbuf->f_files = v9fs_synth_node_count; + stbuf->f_namelen = NAME_MAX; + return 0; +} + +static ssize_t v9fs_synth_lgetxattr(FsContext *ctx, V9fsPath *path, + const char *name, void *value, size_t size) +{ + errno = ENOTSUP; + return -1; +} + +static ssize_t v9fs_synth_llistxattr(FsContext *ctx, V9fsPath *path, + void *value, size_t size) +{ + errno = ENOTSUP; + return -1; +} + +static int v9fs_synth_lsetxattr(FsContext *ctx, V9fsPath *path, + const char *name, void *value, + size_t size, int flags) +{ + errno = ENOTSUP; + return -1; +} + +static int v9fs_synth_lremovexattr(FsContext *ctx, + V9fsPath *path, const char *name) +{ + errno = ENOTSUP; + return -1; +} + +static int v9fs_synth_name_to_path(FsContext *ctx, V9fsPath *dir_path, + const char *name, V9fsPath *target) +{ + V9fsSynthNode *node; + V9fsSynthNode *dir_node; + + /* "." and ".." are not allowed */ + if (!strcmp(name, ".") || !strcmp(name, "..")) { + errno = EINVAL; + return -1; + + } + if (!dir_path) { + dir_node = &v9fs_synth_root; + } else { + dir_node = *(V9fsSynthNode **)dir_path->data; + } + if (!strcmp(name, "/")) { + node = dir_node; + goto out; + } + /* search for the name in the childern */ + rcu_read_lock(); + QLIST_FOREACH(node, &dir_node->child, sibling) { + if (!strcmp(node->name, name)) { + break; + } + } + rcu_read_unlock(); + + if (!node) { + errno = ENOENT; + return -1; + } +out: + /* Copy the node pointer to fid */ + target->data = g_malloc(sizeof(void *)); + memcpy(target->data, &node, sizeof(void *)); + target->size = sizeof(void *); + return 0; +} + +static int v9fs_synth_renameat(FsContext *ctx, V9fsPath *olddir, + const char *old_name, V9fsPath *newdir, + const char *new_name) +{ + errno = EPERM; + return -1; +} + +static int v9fs_synth_unlinkat(FsContext *ctx, V9fsPath *dir, + const char *name, int flags) +{ + errno = EPERM; + return -1; +} + +static int v9fs_synth_init(FsContext *ctx) +{ + QLIST_INIT(&v9fs_synth_root.child); + qemu_mutex_init(&v9fs_synth_mutex); + + /* Add "." and ".." entries for root */ + v9fs_add_dir_node(&v9fs_synth_root, v9fs_synth_root.attr->mode, + "..", v9fs_synth_root.attr, v9fs_synth_root.attr->inode); + v9fs_add_dir_node(&v9fs_synth_root, v9fs_synth_root.attr->mode, + ".", v9fs_synth_root.attr, v9fs_synth_root.attr->inode); + + /* Mark the subsystem is ready for use */ + v9fs_synth_fs = 1; + return 0; +} + +FileOperations synth_ops = { + .init = v9fs_synth_init, + .lstat = v9fs_synth_lstat, + .readlink = v9fs_synth_readlink, + .close = v9fs_synth_close, + .closedir = v9fs_synth_closedir, + .open = v9fs_synth_open, + .opendir = v9fs_synth_opendir, + .rewinddir = v9fs_synth_rewinddir, + .telldir = v9fs_synth_telldir, + .readdir_r = v9fs_synth_readdir_r, + .seekdir = v9fs_synth_seekdir, + .preadv = v9fs_synth_preadv, + .pwritev = v9fs_synth_pwritev, + .chmod = v9fs_synth_chmod, + .mknod = v9fs_synth_mknod, + .mkdir = v9fs_synth_mkdir, + .fstat = v9fs_synth_fstat, + .open2 = v9fs_synth_open2, + .symlink = v9fs_synth_symlink, + .link = v9fs_synth_link, + .truncate = v9fs_synth_truncate, + .rename = v9fs_synth_rename, + .chown = v9fs_synth_chown, + .utimensat = v9fs_synth_utimensat, + .remove = v9fs_synth_remove, + .fsync = v9fs_synth_fsync, + .statfs = v9fs_synth_statfs, + .lgetxattr = v9fs_synth_lgetxattr, + .llistxattr = v9fs_synth_llistxattr, + .lsetxattr = v9fs_synth_lsetxattr, + .lremovexattr = v9fs_synth_lremovexattr, + .name_to_path = v9fs_synth_name_to_path, + .renameat = v9fs_synth_renameat, + .unlinkat = v9fs_synth_unlinkat, +}; diff --git a/qemu/hw/9pfs/9p-synth.h b/qemu/hw/9pfs/9p-synth.h new file mode 100644 index 000000000..82962512a --- /dev/null +++ b/qemu/hw/9pfs/9p-synth.h @@ -0,0 +1,51 @@ +/* + * 9p + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Aneesh Kumar K.V + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ +#ifndef HW_9PFS_SYNTH_H +#define HW_9PFS_SYNTH_H 1 + + +typedef struct V9fsSynthNode V9fsSynthNode; +typedef ssize_t (*v9fs_synth_read)(void *buf, int len, off_t offset, + void *arg); +typedef ssize_t (*v9fs_synth_write)(void *buf, int len, off_t offset, + void *arg); +typedef struct V9fsSynthNodeAttr { + int mode; + int inode; + int nlink; + v9fs_synth_read read; + v9fs_synth_write write; +} V9fsSynthNodeAttr; + +struct V9fsSynthNode { + QLIST_HEAD(, V9fsSynthNode) child; + QLIST_ENTRY(V9fsSynthNode) sibling; + char name[NAME_MAX]; + V9fsSynthNodeAttr *attr; + V9fsSynthNodeAttr actual_attr; + void *private; + int open_count; +}; + +typedef struct V9fsSynthOpenState { + off_t offset; + V9fsSynthNode *node; +} V9fsSynthOpenState; + +extern int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode, + const char *name, V9fsSynthNode **result); +extern int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, + const char *name, v9fs_synth_read read, + v9fs_synth_write write, void *arg); + +#endif diff --git a/qemu/hw/9pfs/9p-xattr-user.c b/qemu/hw/9pfs/9p-xattr-user.c new file mode 100644 index 000000000..f87530c8b --- /dev/null +++ b/qemu/hw/9pfs/9p-xattr-user.c @@ -0,0 +1,127 @@ +/* + * 9p user. xattr callback + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Aneesh Kumar K.V + * + * 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 "9p.h" +#include "fsdev/file-op-9p.h" +#include "9p-xattr.h" + + +static ssize_t mp_user_getxattr(FsContext *ctx, const char *path, + const char *name, void *value, size_t size) +{ + char *buffer; + ssize_t ret; + + if (strncmp(name, "user.virtfs.", 12) == 0) { + /* + * Don't allow fetch of user.virtfs namesapce + * in case of mapped security + */ + errno = ENOATTR; + return -1; + } + buffer = rpath(ctx, path); + ret = lgetxattr(buffer, name, value, size); + g_free(buffer); + return ret; +} + +static ssize_t mp_user_listxattr(FsContext *ctx, const char *path, + char *name, void *value, size_t size) +{ + int name_size = strlen(name) + 1; + if (strncmp(name, "user.virtfs.", 12) == 0) { + + /* check if it is a mapped posix acl */ + if (strncmp(name, "user.virtfs.system.posix_acl_", 29) == 0) { + /* adjust the name and size */ + name += 12; + name_size -= 12; + } else { + /* + * Don't allow fetch of user.virtfs namesapce + * in case of mapped security + */ + return 0; + } + } + if (!value) { + return name_size; + } + + if (size < name_size) { + errno = ERANGE; + return -1; + } + + /* name_size includes the trailing NUL. */ + memcpy(value, name, name_size); + return name_size; +} + +static int mp_user_setxattr(FsContext *ctx, const char *path, const char *name, + void *value, size_t size, int flags) +{ + char *buffer; + int ret; + + if (strncmp(name, "user.virtfs.", 12) == 0) { + /* + * Don't allow fetch of user.virtfs namesapce + * in case of mapped security + */ + errno = EACCES; + return -1; + } + buffer = rpath(ctx, path); + ret = lsetxattr(buffer, name, value, size, flags); + g_free(buffer); + return ret; +} + +static int mp_user_removexattr(FsContext *ctx, + const char *path, const char *name) +{ + char *buffer; + int ret; + + if (strncmp(name, "user.virtfs.", 12) == 0) { + /* + * Don't allow fetch of user.virtfs namesapce + * in case of mapped security + */ + errno = EACCES; + return -1; + } + buffer = rpath(ctx, path); + ret = lremovexattr(buffer, name); + g_free(buffer); + return ret; +} + +XattrOperations mapped_user_xattr = { + .name = "user.", + .getxattr = mp_user_getxattr, + .setxattr = mp_user_setxattr, + .listxattr = mp_user_listxattr, + .removexattr = mp_user_removexattr, +}; + +XattrOperations passthrough_user_xattr = { + .name = "user.", + .getxattr = pt_getxattr, + .setxattr = pt_setxattr, + .listxattr = pt_listxattr, + .removexattr = pt_removexattr, +}; diff --git a/qemu/hw/9pfs/9p-xattr.c b/qemu/hw/9pfs/9p-xattr.c new file mode 100644 index 000000000..5d8595ed9 --- /dev/null +++ b/qemu/hw/9pfs/9p-xattr.c @@ -0,0 +1,164 @@ +/* + * 9p xattr callback + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Aneesh Kumar K.V + * + * 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 "9p.h" +#include "fsdev/file-op-9p.h" +#include "9p-xattr.h" + + +static XattrOperations *get_xattr_operations(XattrOperations **h, + const char *name) +{ + XattrOperations *xops; + for (xops = *(h)++; xops != NULL; xops = *(h)++) { + if (!strncmp(name, xops->name, strlen(xops->name))) { + return xops; + } + } + return NULL; +} + +ssize_t v9fs_get_xattr(FsContext *ctx, const char *path, + const char *name, void *value, size_t size) +{ + XattrOperations *xops = get_xattr_operations(ctx->xops, name); + if (xops) { + return xops->getxattr(ctx, path, name, value, size); + } + errno = EOPNOTSUPP; + return -1; +} + +ssize_t pt_listxattr(FsContext *ctx, const char *path, + char *name, void *value, size_t size) +{ + int name_size = strlen(name) + 1; + if (!value) { + return name_size; + } + + if (size < name_size) { + errno = ERANGE; + return -1; + } + + /* no need for strncpy: name_size is strlen(name)+1 */ + memcpy(value, name, name_size); + return name_size; +} + + +/* + * Get the list and pass to each layer to find out whether + * to send the data or not + */ +ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, + void *value, size_t vsize) +{ + ssize_t size = 0; + char *buffer; + void *ovalue = value; + XattrOperations *xops; + char *orig_value, *orig_value_start; + ssize_t xattr_len, parsed_len = 0, attr_len; + + /* Get the actual len */ + buffer = rpath(ctx, path); + xattr_len = llistxattr(buffer, value, 0); + if (xattr_len <= 0) { + g_free(buffer); + return xattr_len; + } + + /* Now fetch the xattr and find the actual size */ + orig_value = g_malloc(xattr_len); + xattr_len = llistxattr(buffer, orig_value, xattr_len); + g_free(buffer); + + /* store the orig pointer */ + orig_value_start = orig_value; + while (xattr_len > parsed_len) { + xops = get_xattr_operations(ctx->xops, orig_value); + if (!xops) { + goto next_entry; + } + + if (!value) { + size += xops->listxattr(ctx, path, orig_value, value, vsize); + } else { + size = xops->listxattr(ctx, path, orig_value, value, vsize); + if (size < 0) { + goto err_out; + } + value += size; + vsize -= size; + } +next_entry: + /* Got the next entry */ + attr_len = strlen(orig_value) + 1; + parsed_len += attr_len; + orig_value += attr_len; + } + if (value) { + size = value - ovalue; + } + +err_out: + g_free(orig_value_start); + return size; +} + +int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name, + void *value, size_t size, int flags) +{ + XattrOperations *xops = get_xattr_operations(ctx->xops, name); + if (xops) { + return xops->setxattr(ctx, path, name, value, size, flags); + } + errno = EOPNOTSUPP; + return -1; + +} + +int v9fs_remove_xattr(FsContext *ctx, + const char *path, const char *name) +{ + XattrOperations *xops = get_xattr_operations(ctx->xops, name); + if (xops) { + return xops->removexattr(ctx, path, name); + } + errno = EOPNOTSUPP; + return -1; + +} + +XattrOperations *mapped_xattr_ops[] = { + &mapped_user_xattr, + &mapped_pacl_xattr, + &mapped_dacl_xattr, + NULL, +}; + +XattrOperations *passthrough_xattr_ops[] = { + &passthrough_user_xattr, + &passthrough_acl_xattr, + NULL, +}; + +/* for .user none model should be same as passthrough */ +XattrOperations *none_xattr_ops[] = { + &passthrough_user_xattr, + &none_acl_xattr, + NULL, +}; diff --git a/qemu/hw/9pfs/9p-xattr.h b/qemu/hw/9pfs/9p-xattr.h new file mode 100644 index 000000000..4d39a2026 --- /dev/null +++ b/qemu/hw/9pfs/9p-xattr.h @@ -0,0 +1,120 @@ +/* + * 9p + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Aneesh Kumar K.V + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ +#ifndef _QEMU_9P_XATTR_H +#define _QEMU_9P_XATTR_H + +#include "qemu/xattr.h" + +typedef struct xattr_operations +{ + const char *name; + ssize_t (*getxattr)(FsContext *ctx, const char *path, + const char *name, void *value, size_t size); + ssize_t (*listxattr)(FsContext *ctx, const char *path, + char *name, void *value, size_t size); + int (*setxattr)(FsContext *ctx, const char *path, const char *name, + void *value, size_t size, int flags); + int (*removexattr)(FsContext *ctx, + const char *path, const char *name); +} XattrOperations; + + +extern XattrOperations mapped_user_xattr; +extern XattrOperations passthrough_user_xattr; + +extern XattrOperations mapped_pacl_xattr; +extern XattrOperations mapped_dacl_xattr; +extern XattrOperations passthrough_acl_xattr; +extern XattrOperations none_acl_xattr; + +extern XattrOperations *mapped_xattr_ops[]; +extern XattrOperations *passthrough_xattr_ops[]; +extern XattrOperations *none_xattr_ops[]; + +ssize_t v9fs_get_xattr(FsContext *ctx, const char *path, const char *name, + void *value, size_t size); +ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, void *value, + size_t vsize); +int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name, + void *value, size_t size, int flags); +int v9fs_remove_xattr(FsContext *ctx, const char *path, const char *name); +ssize_t pt_listxattr(FsContext *ctx, const char *path, char *name, void *value, + size_t size); + +static inline ssize_t pt_getxattr(FsContext *ctx, const char *path, + const char *name, void *value, size_t size) +{ + char *buffer; + ssize_t ret; + + buffer = rpath(ctx, path); + ret = lgetxattr(buffer, name, value, size); + g_free(buffer); + return ret; +} + +static inline int pt_setxattr(FsContext *ctx, const char *path, + const char *name, void *value, + size_t size, int flags) +{ + char *buffer; + int ret; + + buffer = rpath(ctx, path); + ret = lsetxattr(buffer, name, value, size, flags); + g_free(buffer); + return ret; +} + +static inline int pt_removexattr(FsContext *ctx, + const char *path, const char *name) +{ + char *buffer; + int ret; + + buffer = rpath(ctx, path); + ret = lremovexattr(path, name); + g_free(buffer); + return ret; +} + +static inline ssize_t notsup_getxattr(FsContext *ctx, const char *path, + const char *name, void *value, + size_t size) +{ + errno = ENOTSUP; + return -1; +} + +static inline int notsup_setxattr(FsContext *ctx, const char *path, + const char *name, void *value, + size_t size, int flags) +{ + errno = ENOTSUP; + return -1; +} + +static inline ssize_t notsup_listxattr(FsContext *ctx, const char *path, + char *name, void *value, size_t size) +{ + return 0; +} + +static inline int notsup_removexattr(FsContext *ctx, + const char *path, const char *name) +{ + errno = ENOTSUP; + return -1; +} + +#endif diff --git a/qemu/hw/9pfs/9p.c b/qemu/hw/9pfs/9p.c new file mode 100644 index 000000000..f5e30125f --- /dev/null +++ b/qemu/hw/9pfs/9p.c @@ -0,0 +1,3380 @@ +/* + * Virtio 9p backend + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Anthony Liguori + * + * 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 "hw/virtio/virtio.h" +#include "hw/i386/pc.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/iov.h" +#include "qemu/sockets.h" +#include "virtio-9p.h" +#include "fsdev/qemu-fsdev.h" +#include "9p-xattr.h" +#include "coth.h" +#include "trace.h" +#include "migration/migration.h" + +int open_fd_hw; +int total_open_fd; +static int open_fd_rc; + +enum { + Oread = 0x00, + Owrite = 0x01, + Ordwr = 0x02, + Oexec = 0x03, + Oexcl = 0x04, + Otrunc = 0x10, + Orexec = 0x20, + Orclose = 0x40, + Oappend = 0x80, +}; + +ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) +{ + ssize_t ret; + va_list ap; + + va_start(ap, fmt); + ret = virtio_pdu_vmarshal(pdu, offset, fmt, ap); + va_end(ap); + + return ret; +} + +ssize_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) +{ + ssize_t ret; + va_list ap; + + va_start(ap, fmt); + ret = virtio_pdu_vunmarshal(pdu, offset, fmt, ap); + va_end(ap); + + return ret; +} + +static void pdu_push_and_notify(V9fsPDU *pdu) +{ + virtio_9p_push_and_notify(pdu); +} + +static int omode_to_uflags(int8_t mode) +{ + int ret = 0; + + switch (mode & 3) { + case Oread: + ret = O_RDONLY; + break; + case Ordwr: + ret = O_RDWR; + break; + case Owrite: + ret = O_WRONLY; + break; + case Oexec: + ret = O_RDONLY; + break; + } + + if (mode & Otrunc) { + ret |= O_TRUNC; + } + + if (mode & Oappend) { + ret |= O_APPEND; + } + + if (mode & Oexcl) { + ret |= O_EXCL; + } + + return ret; +} + +struct dotl_openflag_map { + int dotl_flag; + int open_flag; +}; + +static int dotl_to_open_flags(int flags) +{ + int i; + /* + * We have same bits for P9_DOTL_READONLY, P9_DOTL_WRONLY + * and P9_DOTL_NOACCESS + */ + int oflags = flags & O_ACCMODE; + + struct dotl_openflag_map dotl_oflag_map[] = { + { P9_DOTL_CREATE, O_CREAT }, + { P9_DOTL_EXCL, O_EXCL }, + { P9_DOTL_NOCTTY , O_NOCTTY }, + { P9_DOTL_TRUNC, O_TRUNC }, + { P9_DOTL_APPEND, O_APPEND }, + { P9_DOTL_NONBLOCK, O_NONBLOCK } , + { P9_DOTL_DSYNC, O_DSYNC }, + { P9_DOTL_FASYNC, FASYNC }, + { P9_DOTL_DIRECT, O_DIRECT }, + { P9_DOTL_LARGEFILE, O_LARGEFILE }, + { P9_DOTL_DIRECTORY, O_DIRECTORY }, + { P9_DOTL_NOFOLLOW, O_NOFOLLOW }, + { P9_DOTL_NOATIME, O_NOATIME }, + { P9_DOTL_SYNC, O_SYNC }, + }; + + for (i = 0; i < ARRAY_SIZE(dotl_oflag_map); i++) { + if (flags & dotl_oflag_map[i].dotl_flag) { + oflags |= dotl_oflag_map[i].open_flag; + } + } + + return oflags; +} + +void cred_init(FsCred *credp) +{ + credp->fc_uid = -1; + credp->fc_gid = -1; + credp->fc_mode = -1; + credp->fc_rdev = -1; +} + +static int get_dotl_openflags(V9fsState *s, int oflags) +{ + int flags; + /* + * Filter the client open flags + */ + flags = dotl_to_open_flags(oflags); + flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT); + /* + * Ignore direct disk access hint until the server supports it. + */ + flags &= ~O_DIRECT; + return flags; +} + +void v9fs_path_init(V9fsPath *path) +{ + path->data = NULL; + path->size = 0; +} + +void v9fs_path_free(V9fsPath *path) +{ + g_free(path->data); + path->data = NULL; + path->size = 0; +} + +void v9fs_path_copy(V9fsPath *lhs, V9fsPath *rhs) +{ + v9fs_path_free(lhs); + lhs->data = g_malloc(rhs->size); + memcpy(lhs->data, rhs->data, rhs->size); + lhs->size = rhs->size; +} + +int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath, + const char *name, V9fsPath *path) +{ + int err; + err = s->ops->name_to_path(&s->ctx, dirpath, name, path); + if (err < 0) { + err = -errno; + } + return err; +} + +/* + * Return TRUE if s1 is an ancestor of s2. + * + * E.g. "a/b" is an ancestor of "a/b/c" but not of "a/bc/d". + * As a special case, We treat s1 as ancestor of s2 if they are same! + */ +static int v9fs_path_is_ancestor(V9fsPath *s1, V9fsPath *s2) +{ + if (!strncmp(s1->data, s2->data, s1->size - 1)) { + if (s2->data[s1->size - 1] == '\0' || s2->data[s1->size - 1] == '/') { + return 1; + } + } + return 0; +} + +static size_t v9fs_string_size(V9fsString *str) +{ + return str->size; +} + +/* + * returns 0 if fid got re-opened, 1 if not, < 0 on error */ +static int v9fs_reopen_fid(V9fsPDU *pdu, V9fsFidState *f) +{ + int err = 1; + if (f->fid_type == P9_FID_FILE) { + if (f->fs.fd == -1) { + do { + err = v9fs_co_open(pdu, f, f->open_flags); + } while (err == -EINTR && !pdu->cancelled); + } + } else if (f->fid_type == P9_FID_DIR) { + if (f->fs.dir == NULL) { + do { + err = v9fs_co_opendir(pdu, f); + } while (err == -EINTR && !pdu->cancelled); + } + } + return err; +} + +static V9fsFidState *get_fid(V9fsPDU *pdu, int32_t fid) +{ + int err; + V9fsFidState *f; + V9fsState *s = pdu->s; + + for (f = s->fid_list; f; f = f->next) { + BUG_ON(f->clunked); + if (f->fid == fid) { + /* + * Update the fid ref upfront so that + * we don't get reclaimed when we yield + * in open later. + */ + f->ref++; + /* + * check whether we need to reopen the + * file. We might have closed the fd + * while trying to free up some file + * descriptors. + */ + err = v9fs_reopen_fid(pdu, f); + if (err < 0) { + f->ref--; + return NULL; + } + /* + * Mark the fid as referenced so that the LRU + * reclaim won't close the file descriptor + */ + f->flags |= FID_REFERENCED; + return f; + } + } + return NULL; +} + +static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid) +{ + V9fsFidState *f; + + for (f = s->fid_list; f; f = f->next) { + /* If fid is already there return NULL */ + BUG_ON(f->clunked); + if (f->fid == fid) { + return NULL; + } + } + f = g_malloc0(sizeof(V9fsFidState)); + f->fid = fid; + f->fid_type = P9_FID_NONE; + f->ref = 1; + /* + * Mark the fid as referenced so that the LRU + * reclaim won't close the file descriptor + */ + f->flags |= FID_REFERENCED; + f->next = s->fid_list; + s->fid_list = f; + + return f; +} + +static int v9fs_xattr_fid_clunk(V9fsPDU *pdu, V9fsFidState *fidp) +{ + int retval = 0; + + if (fidp->fs.xattr.copied_len == -1) { + /* getxattr/listxattr fid */ + goto free_value; + } + /* + * if this is fid for setxattr. clunk should + * result in setxattr localcall + */ + if (fidp->fs.xattr.len != fidp->fs.xattr.copied_len) { + /* clunk after partial write */ + retval = -EINVAL; + goto free_out; + } + if (fidp->fs.xattr.len) { + retval = v9fs_co_lsetxattr(pdu, &fidp->path, &fidp->fs.xattr.name, + fidp->fs.xattr.value, + fidp->fs.xattr.len, + fidp->fs.xattr.flags); + } else { + retval = v9fs_co_lremovexattr(pdu, &fidp->path, &fidp->fs.xattr.name); + } +free_out: + v9fs_string_free(&fidp->fs.xattr.name); +free_value: + g_free(fidp->fs.xattr.value); + return retval; +} + +static int free_fid(V9fsPDU *pdu, V9fsFidState *fidp) +{ + int retval = 0; + + if (fidp->fid_type == P9_FID_FILE) { + /* If we reclaimed the fd no need to close */ + if (fidp->fs.fd != -1) { + retval = v9fs_co_close(pdu, &fidp->fs); + } + } else if (fidp->fid_type == P9_FID_DIR) { + if (fidp->fs.dir != NULL) { + retval = v9fs_co_closedir(pdu, &fidp->fs); + } + } else if (fidp->fid_type == P9_FID_XATTR) { + retval = v9fs_xattr_fid_clunk(pdu, fidp); + } + v9fs_path_free(&fidp->path); + g_free(fidp); + return retval; +} + +static int put_fid(V9fsPDU *pdu, V9fsFidState *fidp) +{ + BUG_ON(!fidp->ref); + fidp->ref--; + /* + * Don't free the fid if it is in reclaim list + */ + if (!fidp->ref && fidp->clunked) { + if (fidp->fid == pdu->s->root_fid) { + /* + * if the clunked fid is root fid then we + * have unmounted the fs on the client side. + * delete the migration blocker. Ideally, this + * should be hooked to transport close notification + */ + if (pdu->s->migration_blocker) { + migrate_del_blocker(pdu->s->migration_blocker); + error_free(pdu->s->migration_blocker); + pdu->s->migration_blocker = NULL; + } + } + return free_fid(pdu, fidp); + } + return 0; +} + +static V9fsFidState *clunk_fid(V9fsState *s, int32_t fid) +{ + V9fsFidState **fidpp, *fidp; + + for (fidpp = &s->fid_list; *fidpp; fidpp = &(*fidpp)->next) { + if ((*fidpp)->fid == fid) { + break; + } + } + if (*fidpp == NULL) { + return NULL; + } + fidp = *fidpp; + *fidpp = fidp->next; + fidp->clunked = 1; + return fidp; +} + +void v9fs_reclaim_fd(V9fsPDU *pdu) +{ + int reclaim_count = 0; + V9fsState *s = pdu->s; + V9fsFidState *f, *reclaim_list = NULL; + + for (f = s->fid_list; f; f = f->next) { + /* + * Unlink fids cannot be reclaimed. Check + * for them and skip them. Also skip fids + * currently being operated on. + */ + if (f->ref || f->flags & FID_NON_RECLAIMABLE) { + continue; + } + /* + * if it is a recently referenced fid + * we leave the fid untouched and clear the + * reference bit. We come back to it later + * in the next iteration. (a simple LRU without + * moving list elements around) + */ + if (f->flags & FID_REFERENCED) { + f->flags &= ~FID_REFERENCED; + continue; + } + /* + * Add fids to reclaim list. + */ + if (f->fid_type == P9_FID_FILE) { + if (f->fs.fd != -1) { + /* + * Up the reference count so that + * a clunk request won't free this fid + */ + f->ref++; + f->rclm_lst = reclaim_list; + reclaim_list = f; + f->fs_reclaim.fd = f->fs.fd; + f->fs.fd = -1; + reclaim_count++; + } + } else if (f->fid_type == P9_FID_DIR) { + if (f->fs.dir != NULL) { + /* + * Up the reference count so that + * a clunk request won't free this fid + */ + f->ref++; + f->rclm_lst = reclaim_list; + reclaim_list = f; + f->fs_reclaim.dir = f->fs.dir; + f->fs.dir = NULL; + reclaim_count++; + } + } + if (reclaim_count >= open_fd_rc) { + break; + } + } + /* + * Now close the fid in reclaim list. Free them if they + * are already clunked. + */ + while (reclaim_list) { + f = reclaim_list; + reclaim_list = f->rclm_lst; + if (f->fid_type == P9_FID_FILE) { + v9fs_co_close(pdu, &f->fs_reclaim); + } else if (f->fid_type == P9_FID_DIR) { + v9fs_co_closedir(pdu, &f->fs_reclaim); + } + f->rclm_lst = NULL; + /* + * Now drop the fid reference, free it + * if clunked. + */ + put_fid(pdu, f); + } +} + +static int v9fs_mark_fids_unreclaim(V9fsPDU *pdu, V9fsPath *path) +{ + int err; + V9fsState *s = pdu->s; + V9fsFidState *fidp, head_fid; + + head_fid.next = s->fid_list; + for (fidp = s->fid_list; fidp; fidp = fidp->next) { + if (fidp->path.size != path->size) { + continue; + } + if (!memcmp(fidp->path.data, path->data, path->size)) { + /* Mark the fid non reclaimable. */ + fidp->flags |= FID_NON_RECLAIMABLE; + + /* reopen the file/dir if already closed */ + err = v9fs_reopen_fid(pdu, fidp); + if (err < 0) { + return -1; + } + /* + * Go back to head of fid list because + * the list could have got updated when + * switched to the worker thread + */ + if (err == 0) { + fidp = &head_fid; + } + } + } + return 0; +} + +static void virtfs_reset(V9fsPDU *pdu) +{ + V9fsState *s = pdu->s; + V9fsFidState *fidp = NULL; + + /* Free all fids */ + while (s->fid_list) { + fidp = s->fid_list; + s->fid_list = fidp->next; + + if (fidp->ref) { + fidp->clunked = 1; + } else { + free_fid(pdu, fidp); + } + } + if (fidp) { + /* One or more unclunked fids found... */ + error_report("9pfs:%s: One or more uncluncked fids " + "found during reset", __func__); + } +} + +#define P9_QID_TYPE_DIR 0x80 +#define P9_QID_TYPE_SYMLINK 0x02 + +#define P9_STAT_MODE_DIR 0x80000000 +#define P9_STAT_MODE_APPEND 0x40000000 +#define P9_STAT_MODE_EXCL 0x20000000 +#define P9_STAT_MODE_MOUNT 0x10000000 +#define P9_STAT_MODE_AUTH 0x08000000 +#define P9_STAT_MODE_TMP 0x04000000 +#define P9_STAT_MODE_SYMLINK 0x02000000 +#define P9_STAT_MODE_LINK 0x01000000 +#define P9_STAT_MODE_DEVICE 0x00800000 +#define P9_STAT_MODE_NAMED_PIPE 0x00200000 +#define P9_STAT_MODE_SOCKET 0x00100000 +#define P9_STAT_MODE_SETUID 0x00080000 +#define P9_STAT_MODE_SETGID 0x00040000 +#define P9_STAT_MODE_SETVTX 0x00010000 + +#define P9_STAT_MODE_TYPE_BITS (P9_STAT_MODE_DIR | \ + P9_STAT_MODE_SYMLINK | \ + P9_STAT_MODE_LINK | \ + P9_STAT_MODE_DEVICE | \ + P9_STAT_MODE_NAMED_PIPE | \ + P9_STAT_MODE_SOCKET) + +/* This is the algorithm from ufs in spfs */ +static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp) +{ + size_t size; + + memset(&qidp->path, 0, sizeof(qidp->path)); + size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path)); + memcpy(&qidp->path, &stbuf->st_ino, size); + qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8); + qidp->type = 0; + if (S_ISDIR(stbuf->st_mode)) { + qidp->type |= P9_QID_TYPE_DIR; + } + if (S_ISLNK(stbuf->st_mode)) { + qidp->type |= P9_QID_TYPE_SYMLINK; + } +} + +static int fid_to_qid(V9fsPDU *pdu, V9fsFidState *fidp, V9fsQID *qidp) +{ + struct stat stbuf; + int err; + + err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); + if (err < 0) { + return err; + } + stat_to_qid(&stbuf, qidp); + return 0; +} + +V9fsPDU *pdu_alloc(V9fsState *s) +{ + V9fsPDU *pdu = NULL; + + if (!QLIST_EMPTY(&s->free_list)) { + pdu = QLIST_FIRST(&s->free_list); + QLIST_REMOVE(pdu, next); + QLIST_INSERT_HEAD(&s->active_list, pdu, next); + } + return pdu; +} + +void pdu_free(V9fsPDU *pdu) +{ + if (pdu) { + V9fsState *s = pdu->s; + /* + * Cancelled pdu are added back to the freelist + * by flush request . + */ + if (!pdu->cancelled) { + QLIST_REMOVE(pdu, next); + QLIST_INSERT_HEAD(&s->free_list, pdu, next); + } + } +} + +/* + * We don't do error checking for pdu_marshal/unmarshal here + * because we always expect to have enough space to encode + * error details + */ +static void pdu_complete(V9fsPDU *pdu, ssize_t len) +{ + int8_t id = pdu->id + 1; /* Response */ + V9fsState *s = pdu->s; + + if (len < 0) { + int err = -len; + len = 7; + + if (s->proto_version != V9FS_PROTO_2000L) { + V9fsString str; + + str.data = strerror(err); + str.size = strlen(str.data); + + len += pdu_marshal(pdu, len, "s", &str); + id = P9_RERROR; + } + + len += pdu_marshal(pdu, len, "d", err); + + if (s->proto_version == V9FS_PROTO_2000L) { + id = P9_RLERROR; + } + trace_v9fs_rerror(pdu->tag, pdu->id, err); /* Trace ERROR */ + } + + /* fill out the header */ + pdu_marshal(pdu, 0, "dbw", (int32_t)len, id, pdu->tag); + + /* keep these in sync */ + pdu->size = len; + pdu->id = id; + + pdu_push_and_notify(pdu); + + /* Now wakeup anybody waiting in flush for this request */ + qemu_co_queue_next(&pdu->complete); + + pdu_free(pdu); +} + +static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension) +{ + mode_t ret; + + ret = mode & 0777; + if (mode & P9_STAT_MODE_DIR) { + ret |= S_IFDIR; + } + + if (mode & P9_STAT_MODE_SYMLINK) { + ret |= S_IFLNK; + } + if (mode & P9_STAT_MODE_SOCKET) { + ret |= S_IFSOCK; + } + if (mode & P9_STAT_MODE_NAMED_PIPE) { + ret |= S_IFIFO; + } + if (mode & P9_STAT_MODE_DEVICE) { + if (extension->size && extension->data[0] == 'c') { + ret |= S_IFCHR; + } else { + ret |= S_IFBLK; + } + } + + if (!(ret&~0777)) { + ret |= S_IFREG; + } + + if (mode & P9_STAT_MODE_SETUID) { + ret |= S_ISUID; + } + if (mode & P9_STAT_MODE_SETGID) { + ret |= S_ISGID; + } + if (mode & P9_STAT_MODE_SETVTX) { + ret |= S_ISVTX; + } + + return ret; +} + +static int donttouch_stat(V9fsStat *stat) +{ + if (stat->type == -1 && + stat->dev == -1 && + stat->qid.type == -1 && + stat->qid.version == -1 && + stat->qid.path == -1 && + stat->mode == -1 && + stat->atime == -1 && + stat->mtime == -1 && + stat->length == -1 && + !stat->name.size && + !stat->uid.size && + !stat->gid.size && + !stat->muid.size && + stat->n_uid == -1 && + stat->n_gid == -1 && + stat->n_muid == -1) { + return 1; + } + + return 0; +} + +static void v9fs_stat_init(V9fsStat *stat) +{ + v9fs_string_init(&stat->name); + v9fs_string_init(&stat->uid); + v9fs_string_init(&stat->gid); + v9fs_string_init(&stat->muid); + v9fs_string_init(&stat->extension); +} + +static void v9fs_stat_free(V9fsStat *stat) +{ + v9fs_string_free(&stat->name); + v9fs_string_free(&stat->uid); + v9fs_string_free(&stat->gid); + v9fs_string_free(&stat->muid); + v9fs_string_free(&stat->extension); +} + +static uint32_t stat_to_v9mode(const struct stat *stbuf) +{ + uint32_t mode; + + mode = stbuf->st_mode & 0777; + if (S_ISDIR(stbuf->st_mode)) { + mode |= P9_STAT_MODE_DIR; + } + + if (S_ISLNK(stbuf->st_mode)) { + mode |= P9_STAT_MODE_SYMLINK; + } + + if (S_ISSOCK(stbuf->st_mode)) { + mode |= P9_STAT_MODE_SOCKET; + } + + if (S_ISFIFO(stbuf->st_mode)) { + mode |= P9_STAT_MODE_NAMED_PIPE; + } + + if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) { + mode |= P9_STAT_MODE_DEVICE; + } + + if (stbuf->st_mode & S_ISUID) { + mode |= P9_STAT_MODE_SETUID; + } + + if (stbuf->st_mode & S_ISGID) { + mode |= P9_STAT_MODE_SETGID; + } + + if (stbuf->st_mode & S_ISVTX) { + mode |= P9_STAT_MODE_SETVTX; + } + + return mode; +} + +static int stat_to_v9stat(V9fsPDU *pdu, V9fsPath *name, + const struct stat *stbuf, + V9fsStat *v9stat) +{ + int err; + const char *str; + + memset(v9stat, 0, sizeof(*v9stat)); + + stat_to_qid(stbuf, &v9stat->qid); + v9stat->mode = stat_to_v9mode(stbuf); + v9stat->atime = stbuf->st_atime; + v9stat->mtime = stbuf->st_mtime; + v9stat->length = stbuf->st_size; + + v9fs_string_null(&v9stat->uid); + v9fs_string_null(&v9stat->gid); + v9fs_string_null(&v9stat->muid); + + v9stat->n_uid = stbuf->st_uid; + v9stat->n_gid = stbuf->st_gid; + v9stat->n_muid = 0; + + v9fs_string_null(&v9stat->extension); + + if (v9stat->mode & P9_STAT_MODE_SYMLINK) { + err = v9fs_co_readlink(pdu, name, &v9stat->extension); + if (err < 0) { + return err; + } + } else if (v9stat->mode & P9_STAT_MODE_DEVICE) { + v9fs_string_sprintf(&v9stat->extension, "%c %u %u", + S_ISCHR(stbuf->st_mode) ? 'c' : 'b', + major(stbuf->st_rdev), minor(stbuf->st_rdev)); + } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) { + v9fs_string_sprintf(&v9stat->extension, "%s %lu", + "HARDLINKCOUNT", (unsigned long)stbuf->st_nlink); + } + + str = strrchr(name->data, '/'); + if (str) { + str += 1; + } else { + str = name->data; + } + + v9fs_string_sprintf(&v9stat->name, "%s", str); + + v9stat->size = 61 + + v9fs_string_size(&v9stat->name) + + v9fs_string_size(&v9stat->uid) + + v9fs_string_size(&v9stat->gid) + + v9fs_string_size(&v9stat->muid) + + v9fs_string_size(&v9stat->extension); + return 0; +} + +#define P9_STATS_MODE 0x00000001ULL +#define P9_STATS_NLINK 0x00000002ULL +#define P9_STATS_UID 0x00000004ULL +#define P9_STATS_GID 0x00000008ULL +#define P9_STATS_RDEV 0x00000010ULL +#define P9_STATS_ATIME 0x00000020ULL +#define P9_STATS_MTIME 0x00000040ULL +#define P9_STATS_CTIME 0x00000080ULL +#define P9_STATS_INO 0x00000100ULL +#define P9_STATS_SIZE 0x00000200ULL +#define P9_STATS_BLOCKS 0x00000400ULL + +#define P9_STATS_BTIME 0x00000800ULL +#define P9_STATS_GEN 0x00001000ULL +#define P9_STATS_DATA_VERSION 0x00002000ULL + +#define P9_STATS_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */ +#define P9_STATS_ALL 0x00003fffULL /* Mask for All fields above */ + + +static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf, + V9fsStatDotl *v9lstat) +{ + memset(v9lstat, 0, sizeof(*v9lstat)); + + v9lstat->st_mode = stbuf->st_mode; + v9lstat->st_nlink = stbuf->st_nlink; + v9lstat->st_uid = stbuf->st_uid; + v9lstat->st_gid = stbuf->st_gid; + v9lstat->st_rdev = stbuf->st_rdev; + v9lstat->st_size = stbuf->st_size; + v9lstat->st_blksize = stbuf->st_blksize; + v9lstat->st_blocks = stbuf->st_blocks; + v9lstat->st_atime_sec = stbuf->st_atime; + v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec; + v9lstat->st_mtime_sec = stbuf->st_mtime; + v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec; + v9lstat->st_ctime_sec = stbuf->st_ctime; + v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec; + /* Currently we only support BASIC fields in stat */ + v9lstat->st_result_mask = P9_STATS_BASIC; + + stat_to_qid(stbuf, &v9lstat->qid); +} + +static void print_sg(struct iovec *sg, int cnt) +{ + int i; + + printf("sg[%d]: {", cnt); + for (i = 0; i < cnt; i++) { + if (i) { + printf(", "); + } + printf("(%p, %zd)", sg[i].iov_base, sg[i].iov_len); + } + printf("}\n"); +} + +/* Will call this only for path name based fid */ +static void v9fs_fix_path(V9fsPath *dst, V9fsPath *src, int len) +{ + V9fsPath str; + v9fs_path_init(&str); + v9fs_path_copy(&str, dst); + v9fs_string_sprintf((V9fsString *)dst, "%s%s", src->data, str.data+len); + v9fs_path_free(&str); + /* +1 to include terminating NULL */ + dst->size++; +} + +static inline bool is_ro_export(FsContext *ctx) +{ + return ctx->export_flags & V9FS_RDONLY; +} + +static void v9fs_version(void *opaque) +{ + ssize_t err; + V9fsPDU *pdu = opaque; + V9fsState *s = pdu->s; + V9fsString version; + size_t offset = 7; + + v9fs_string_init(&version); + err = pdu_unmarshal(pdu, offset, "ds", &s->msize, &version); + if (err < 0) { + offset = err; + goto out; + } + trace_v9fs_version(pdu->tag, pdu->id, s->msize, version.data); + + virtfs_reset(pdu); + + if (!strcmp(version.data, "9P2000.u")) { + s->proto_version = V9FS_PROTO_2000U; + } else if (!strcmp(version.data, "9P2000.L")) { + s->proto_version = V9FS_PROTO_2000L; + } else { + v9fs_string_sprintf(&version, "unknown"); + } + + err = pdu_marshal(pdu, offset, "ds", s->msize, &version); + if (err < 0) { + offset = err; + goto out; + } + offset += err; + trace_v9fs_version_return(pdu->tag, pdu->id, s->msize, version.data); +out: + pdu_complete(pdu, offset); + v9fs_string_free(&version); +} + +static void v9fs_attach(void *opaque) +{ + V9fsPDU *pdu = opaque; + V9fsState *s = pdu->s; + int32_t fid, afid, n_uname; + V9fsString uname, aname; + V9fsFidState *fidp; + size_t offset = 7; + V9fsQID qid; + ssize_t err; + + v9fs_string_init(&uname); + v9fs_string_init(&aname); + err = pdu_unmarshal(pdu, offset, "ddssd", &fid, + &afid, &uname, &aname, &n_uname); + if (err < 0) { + goto out_nofid; + } + trace_v9fs_attach(pdu->tag, pdu->id, fid, afid, uname.data, aname.data); + + fidp = alloc_fid(s, fid); + if (fidp == NULL) { + err = -EINVAL; + goto out_nofid; + } + fidp->uid = n_uname; + err = v9fs_co_name_to_path(pdu, NULL, "/", &fidp->path); + if (err < 0) { + err = -EINVAL; + clunk_fid(s, fid); + goto out; + } + err = fid_to_qid(pdu, fidp, &qid); + if (err < 0) { + err = -EINVAL; + clunk_fid(s, fid); + goto out; + } + err = pdu_marshal(pdu, offset, "Q", &qid); + if (err < 0) { + clunk_fid(s, fid); + goto out; + } + err += offset; + trace_v9fs_attach_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path); + /* + * disable migration if we haven't done already. + * attach could get called multiple times for the same export. + */ + if (!s->migration_blocker) { + s->root_fid = fid; + error_setg(&s->migration_blocker, + "Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'", + s->ctx.fs_root ? s->ctx.fs_root : "NULL", s->tag); + migrate_add_blocker(s->migration_blocker); + } +out: + put_fid(pdu, fidp); +out_nofid: + pdu_complete(pdu, err); + v9fs_string_free(&uname); + v9fs_string_free(&aname); +} + +static void v9fs_stat(void *opaque) +{ + int32_t fid; + V9fsStat v9stat; + ssize_t err = 0; + size_t offset = 7; + struct stat stbuf; + V9fsFidState *fidp; + V9fsPDU *pdu = opaque; + + err = pdu_unmarshal(pdu, offset, "d", &fid); + if (err < 0) { + goto out_nofid; + } + trace_v9fs_stat(pdu->tag, pdu->id, fid); + + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -ENOENT; + goto out_nofid; + } + err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); + if (err < 0) { + goto out; + } + err = stat_to_v9stat(pdu, &fidp->path, &stbuf, &v9stat); + if (err < 0) { + goto out; + } + err = pdu_marshal(pdu, offset, "wS", 0, &v9stat); + if (err < 0) { + v9fs_stat_free(&v9stat); + goto out; + } + trace_v9fs_stat_return(pdu->tag, pdu->id, v9stat.mode, + v9stat.atime, v9stat.mtime, v9stat.length); + err += offset; + v9fs_stat_free(&v9stat); +out: + put_fid(pdu, fidp); +out_nofid: + pdu_complete(pdu, err); +} + +static void v9fs_getattr(void *opaque) +{ + int32_t fid; + size_t offset = 7; + ssize_t retval = 0; + struct stat stbuf; + V9fsFidState *fidp; + uint64_t request_mask; + V9fsStatDotl v9stat_dotl; + V9fsPDU *pdu = opaque; + V9fsState *s = pdu->s; + + retval = pdu_unmarshal(pdu, offset, "dq", &fid, &request_mask); + if (retval < 0) { + goto out_nofid; + } + trace_v9fs_getattr(pdu->tag, pdu->id, fid, request_mask); + + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + retval = -ENOENT; + goto out_nofid; + } + /* + * Currently we only support BASIC fields in stat, so there is no + * need to look at request_mask. + */ + retval = v9fs_co_lstat(pdu, &fidp->path, &stbuf); + if (retval < 0) { + goto out; + } + stat_to_v9stat_dotl(s, &stbuf, &v9stat_dotl); + + /* fill st_gen if requested and supported by underlying fs */ + if (request_mask & P9_STATS_GEN) { + retval = v9fs_co_st_gen(pdu, &fidp->path, stbuf.st_mode, &v9stat_dotl); + switch (retval) { + case 0: + /* we have valid st_gen: update result mask */ + v9stat_dotl.st_result_mask |= P9_STATS_GEN; + break; + case -EINTR: + /* request cancelled, e.g. by Tflush */ + goto out; + default: + /* failed to get st_gen: not fatal, ignore */ + break; + } + } + retval = pdu_marshal(pdu, offset, "A", &v9stat_dotl); + if (retval < 0) { + goto out; + } + retval += offset; + trace_v9fs_getattr_return(pdu->tag, pdu->id, v9stat_dotl.st_result_mask, + v9stat_dotl.st_mode, v9stat_dotl.st_uid, + v9stat_dotl.st_gid); +out: + put_fid(pdu, fidp); +out_nofid: + pdu_complete(pdu, retval); +} + +/* Attribute flags */ +#define P9_ATTR_MODE (1 << 0) +#define P9_ATTR_UID (1 << 1) +#define P9_ATTR_GID (1 << 2) +#define P9_ATTR_SIZE (1 << 3) +#define P9_ATTR_ATIME (1 << 4) +#define P9_ATTR_MTIME (1 << 5) +#define P9_ATTR_CTIME (1 << 6) +#define P9_ATTR_ATIME_SET (1 << 7) +#define P9_ATTR_MTIME_SET (1 << 8) + +#define P9_ATTR_MASK 127 + +static void v9fs_setattr(void *opaque) +{ + int err = 0; + int32_t fid; + V9fsFidState *fidp; + size_t offset = 7; + V9fsIattr v9iattr; + V9fsPDU *pdu = opaque; + + err = pdu_unmarshal(pdu, offset, "dI", &fid, &v9iattr); + if (err < 0) { + goto out_nofid; + } + + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -EINVAL; + goto out_nofid; + } + if (v9iattr.valid & P9_ATTR_MODE) { + err = v9fs_co_chmod(pdu, &fidp->path, v9iattr.mode); + if (err < 0) { + goto out; + } + } + if (v9iattr.valid & (P9_ATTR_ATIME | P9_ATTR_MTIME)) { + struct timespec times[2]; + if (v9iattr.valid & P9_ATTR_ATIME) { + if (v9iattr.valid & P9_ATTR_ATIME_SET) { + times[0].tv_sec = v9iattr.atime_sec; + times[0].tv_nsec = v9iattr.atime_nsec; + } else { + times[0].tv_nsec = UTIME_NOW; + } + } else { + times[0].tv_nsec = UTIME_OMIT; + } + if (v9iattr.valid & P9_ATTR_MTIME) { + if (v9iattr.valid & P9_ATTR_MTIME_SET) { + times[1].tv_sec = v9iattr.mtime_sec; + times[1].tv_nsec = v9iattr.mtime_nsec; + } else { + times[1].tv_nsec = UTIME_NOW; + } + } else { + times[1].tv_nsec = UTIME_OMIT; + } + err = v9fs_co_utimensat(pdu, &fidp->path, times); + if (err < 0) { + goto out; + } + } + /* + * If the only valid entry in iattr is ctime we can call + * chown(-1,-1) to update the ctime of the file + */ + if ((v9iattr.valid & (P9_ATTR_UID | P9_ATTR_GID)) || + ((v9iattr.valid & P9_ATTR_CTIME) + && !((v9iattr.valid & P9_ATTR_MASK) & ~P9_ATTR_CTIME))) { + if (!(v9iattr.valid & P9_ATTR_UID)) { + v9iattr.uid = -1; + } + if (!(v9iattr.valid & P9_ATTR_GID)) { + v9iattr.gid = -1; + } + err = v9fs_co_chown(pdu, &fidp->path, v9iattr.uid, + v9iattr.gid); + if (err < 0) { + goto out; + } + } + if (v9iattr.valid & (P9_ATTR_SIZE)) { + err = v9fs_co_truncate(pdu, &fidp->path, v9iattr.size); + if (err < 0) { + goto out; + } + } + err = offset; +out: + put_fid(pdu, fidp); +out_nofid: + pdu_complete(pdu, err); +} + +static int v9fs_walk_marshal(V9fsPDU *pdu, uint16_t nwnames, V9fsQID *qids) +{ + int i; + ssize_t err; + size_t offset = 7; + + err = pdu_marshal(pdu, offset, "w", nwnames); + if (err < 0) { + return err; + } + offset += err; + for (i = 0; i < nwnames; i++) { + err = pdu_marshal(pdu, offset, "Q", &qids[i]); + if (err < 0) { + return err; + } + offset += err; + } + return offset; +} + +static void v9fs_walk(void *opaque) +{ + int name_idx; + V9fsQID *qids = NULL; + int i, err = 0; + V9fsPath dpath, path; + uint16_t nwnames; + struct stat stbuf; + size_t offset = 7; + int32_t fid, newfid; + V9fsString *wnames = NULL; + V9fsFidState *fidp; + V9fsFidState *newfidp = NULL; + V9fsPDU *pdu = opaque; + V9fsState *s = pdu->s; + + err = pdu_unmarshal(pdu, offset, "ddw", &fid, &newfid, &nwnames); + if (err < 0) { + pdu_complete(pdu, err); + return ; + } + offset += err; + + trace_v9fs_walk(pdu->tag, pdu->id, fid, newfid, nwnames); + + if (nwnames && nwnames <= P9_MAXWELEM) { + wnames = g_malloc0(sizeof(wnames[0]) * nwnames); + qids = g_malloc0(sizeof(qids[0]) * nwnames); + for (i = 0; i < nwnames; i++) { + err = pdu_unmarshal(pdu, offset, "s", &wnames[i]); + if (err < 0) { + goto out_nofid; + } + offset += err; + } + } else if (nwnames > P9_MAXWELEM) { + err = -EINVAL; + goto out_nofid; + } + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -ENOENT; + goto out_nofid; + } + v9fs_path_init(&dpath); + v9fs_path_init(&path); + /* + * Both dpath and path initially poin to fidp. + * Needed to handle request with nwnames == 0 + */ + v9fs_path_copy(&dpath, &fidp->path); + v9fs_path_copy(&path, &fidp->path); + for (name_idx = 0; name_idx < nwnames; name_idx++) { + err = v9fs_co_name_to_path(pdu, &dpath, wnames[name_idx].data, &path); + if (err < 0) { + goto out; + } + err = v9fs_co_lstat(pdu, &path, &stbuf); + if (err < 0) { + goto out; + } + stat_to_qid(&stbuf, &qids[name_idx]); + v9fs_path_copy(&dpath, &path); + } + if (fid == newfid) { + BUG_ON(fidp->fid_type != P9_FID_NONE); + v9fs_path_copy(&fidp->path, &path); + } else { + newfidp = alloc_fid(s, newfid); + if (newfidp == NULL) { + err = -EINVAL; + goto out; + } + newfidp->uid = fidp->uid; + v9fs_path_copy(&newfidp->path, &path); + } + err = v9fs_walk_marshal(pdu, nwnames, qids); + trace_v9fs_walk_return(pdu->tag, pdu->id, nwnames, qids); +out: + put_fid(pdu, fidp); + if (newfidp) { + put_fid(pdu, newfidp); + } + v9fs_path_free(&dpath); + v9fs_path_free(&path); +out_nofid: + pdu_complete(pdu, err); + if (nwnames && nwnames <= P9_MAXWELEM) { + for (name_idx = 0; name_idx < nwnames; name_idx++) { + v9fs_string_free(&wnames[name_idx]); + } + g_free(wnames); + g_free(qids); + } +} + +static int32_t get_iounit(V9fsPDU *pdu, V9fsPath *path) +{ + struct statfs stbuf; + int32_t iounit = 0; + V9fsState *s = pdu->s; + + /* + * iounit should be multiples of f_bsize (host filesystem block size + * and as well as less than (client msize - P9_IOHDRSZ)) + */ + if (!v9fs_co_statfs(pdu, path, &stbuf)) { + iounit = stbuf.f_bsize; + iounit *= (s->msize - P9_IOHDRSZ)/stbuf.f_bsize; + } + if (!iounit) { + iounit = s->msize - P9_IOHDRSZ; + } + return iounit; +} + +static void v9fs_open(void *opaque) +{ + int flags; + int32_t fid; + int32_t mode; + V9fsQID qid; + int iounit = 0; + ssize_t err = 0; + size_t offset = 7; + struct stat stbuf; + V9fsFidState *fidp; + V9fsPDU *pdu = opaque; + V9fsState *s = pdu->s; + + if (s->proto_version == V9FS_PROTO_2000L) { + err = pdu_unmarshal(pdu, offset, "dd", &fid, &mode); + } else { + uint8_t modebyte; + err = pdu_unmarshal(pdu, offset, "db", &fid, &modebyte); + mode = modebyte; + } + if (err < 0) { + goto out_nofid; + } + trace_v9fs_open(pdu->tag, pdu->id, fid, mode); + + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -ENOENT; + goto out_nofid; + } + BUG_ON(fidp->fid_type != P9_FID_NONE); + + err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); + if (err < 0) { + goto out; + } + stat_to_qid(&stbuf, &qid); + if (S_ISDIR(stbuf.st_mode)) { + err = v9fs_co_opendir(pdu, fidp); + if (err < 0) { + goto out; + } + fidp->fid_type = P9_FID_DIR; + err = pdu_marshal(pdu, offset, "Qd", &qid, 0); + if (err < 0) { + goto out; + } + err += offset; + } else { + if (s->proto_version == V9FS_PROTO_2000L) { + flags = get_dotl_openflags(s, mode); + } else { + flags = omode_to_uflags(mode); + } + if (is_ro_export(&s->ctx)) { + if (mode & O_WRONLY || mode & O_RDWR || + mode & O_APPEND || mode & O_TRUNC) { + err = -EROFS; + goto out; + } + } + err = v9fs_co_open(pdu, fidp, flags); + if (err < 0) { + goto out; + } + fidp->fid_type = P9_FID_FILE; + fidp->open_flags = flags; + if (flags & O_EXCL) { + /* + * We let the host file system do O_EXCL check + * We should not reclaim such fd + */ + fidp->flags |= FID_NON_RECLAIMABLE; + } + iounit = get_iounit(pdu, &fidp->path); + err = pdu_marshal(pdu, offset, "Qd", &qid, iounit); + if (err < 0) { + goto out; + } + err += offset; + } + trace_v9fs_open_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path, iounit); +out: + put_fid(pdu, fidp); +out_nofid: + pdu_complete(pdu, err); +} + +static void v9fs_lcreate(void *opaque) +{ + int32_t dfid, flags, mode; + gid_t gid; + ssize_t err = 0; + ssize_t offset = 7; + V9fsString name; + V9fsFidState *fidp; + struct stat stbuf; + V9fsQID qid; + int32_t iounit; + V9fsPDU *pdu = opaque; + + v9fs_string_init(&name); + err = pdu_unmarshal(pdu, offset, "dsddd", &dfid, + &name, &flags, &mode, &gid); + if (err < 0) { + goto out_nofid; + } + trace_v9fs_lcreate(pdu->tag, pdu->id, dfid, flags, mode, gid); + + fidp = get_fid(pdu, dfid); + if (fidp == NULL) { + err = -ENOENT; + goto out_nofid; + } + + flags = get_dotl_openflags(pdu->s, flags); + err = v9fs_co_open2(pdu, fidp, &name, gid, + flags | O_CREAT, mode, &stbuf); + if (err < 0) { + goto out; + } + fidp->fid_type = P9_FID_FILE; + fidp->open_flags = flags; + if (flags & O_EXCL) { + /* + * We let the host file system do O_EXCL check + * We should not reclaim such fd + */ + fidp->flags |= FID_NON_RECLAIMABLE; + } + iounit = get_iounit(pdu, &fidp->path); + stat_to_qid(&stbuf, &qid); + err = pdu_marshal(pdu, offset, "Qd", &qid, iounit); + if (err < 0) { + goto out; + } + err += offset; + trace_v9fs_lcreate_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path, iounit); +out: + put_fid(pdu, fidp); +out_nofid: + pdu_complete(pdu, err); + v9fs_string_free(&name); +} + +static void v9fs_fsync(void *opaque) +{ + int err; + int32_t fid; + int datasync; + size_t offset = 7; + V9fsFidState *fidp; + V9fsPDU *pdu = opaque; + + err = pdu_unmarshal(pdu, offset, "dd", &fid, &datasync); + if (err < 0) { + goto out_nofid; + } + trace_v9fs_fsync(pdu->tag, pdu->id, fid, datasync); + + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -ENOENT; + goto out_nofid; + } + err = v9fs_co_fsync(pdu, fidp, datasync); + if (!err) { + err = offset; + } + put_fid(pdu, fidp); +out_nofid: + pdu_complete(pdu, err); +} + +static void v9fs_clunk(void *opaque) +{ + int err; + int32_t fid; + size_t offset = 7; + V9fsFidState *fidp; + V9fsPDU *pdu = opaque; + V9fsState *s = pdu->s; + + err = pdu_unmarshal(pdu, offset, "d", &fid); + if (err < 0) { + goto out_nofid; + } + trace_v9fs_clunk(pdu->tag, pdu->id, fid); + + fidp = clunk_fid(s, fid); + if (fidp == NULL) { + err = -ENOENT; + goto out_nofid; + } + /* + * Bump the ref so that put_fid will + * free the fid. + */ + fidp->ref++; + err = put_fid(pdu, fidp); + if (!err) { + err = offset; + } +out_nofid: + pdu_complete(pdu, err); +} + +static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, + uint64_t off, uint32_t max_count) +{ + ssize_t err; + size_t offset = 7; + int read_count; + int64_t xattr_len; + V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); + VirtQueueElement *elem = v->elems[pdu->idx]; + + xattr_len = fidp->fs.xattr.len; + read_count = xattr_len - off; + if (read_count > max_count) { + read_count = max_count; + } else if (read_count < 0) { + /* + * read beyond XATTR value + */ + read_count = 0; + } + err = pdu_marshal(pdu, offset, "d", read_count); + if (err < 0) { + return err; + } + offset += err; + + err = v9fs_pack(elem->in_sg, elem->in_num, offset, + ((char *)fidp->fs.xattr.value) + off, + read_count); + if (err < 0) { + return err; + } + offset += err; + return offset; +} + +static int v9fs_do_readdir_with_stat(V9fsPDU *pdu, + V9fsFidState *fidp, uint32_t max_count) +{ + V9fsPath path; + V9fsStat v9stat; + int len, err = 0; + int32_t count = 0; + struct stat stbuf; + off_t saved_dir_pos; + struct dirent *dent, *result; + + /* save the directory position */ + saved_dir_pos = v9fs_co_telldir(pdu, fidp); + if (saved_dir_pos < 0) { + return saved_dir_pos; + } + + dent = g_malloc(sizeof(struct dirent)); + + while (1) { + v9fs_path_init(&path); + err = v9fs_co_readdir_r(pdu, fidp, dent, &result); + if (err || !result) { + break; + } + err = v9fs_co_name_to_path(pdu, &fidp->path, dent->d_name, &path); + if (err < 0) { + goto out; + } + err = v9fs_co_lstat(pdu, &path, &stbuf); + if (err < 0) { + goto out; + } + err = stat_to_v9stat(pdu, &path, &stbuf, &v9stat); + if (err < 0) { + goto out; + } + /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */ + len = pdu_marshal(pdu, 11 + count, "S", &v9stat); + if ((len != (v9stat.size + 2)) || ((count + len) > max_count)) { + /* Ran out of buffer. Set dir back to old position and return */ + v9fs_co_seekdir(pdu, fidp, saved_dir_pos); + v9fs_stat_free(&v9stat); + v9fs_path_free(&path); + g_free(dent); + return count; + } + count += len; + v9fs_stat_free(&v9stat); + v9fs_path_free(&path); + saved_dir_pos = dent->d_off; + } +out: + g_free(dent); + v9fs_path_free(&path); + if (err < 0) { + return err; + } + return count; +} + +/* + * Create a QEMUIOVector for a sub-region of PDU iovecs + * + * @qiov: uninitialized QEMUIOVector + * @skip: number of bytes to skip from beginning of PDU + * @size: number of bytes to include + * @is_write: true - write, false - read + * + * The resulting QEMUIOVector has heap-allocated iovecs and must be cleaned up + * with qemu_iovec_destroy(). + */ +static void v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu, + size_t skip, size_t size, + bool is_write) +{ + QEMUIOVector elem; + struct iovec *iov; + unsigned int niov; + + virtio_init_iov_from_pdu(pdu, &iov, &niov, is_write); + + qemu_iovec_init_external(&elem, iov, niov); + qemu_iovec_init(qiov, niov); + qemu_iovec_concat(qiov, &elem, skip, size); +} + +static void v9fs_read(void *opaque) +{ + int32_t fid; + uint64_t off; + ssize_t err = 0; + int32_t count = 0; + size_t offset = 7; + uint32_t max_count; + V9fsFidState *fidp; + V9fsPDU *pdu = opaque; + V9fsState *s = pdu->s; + + err = pdu_unmarshal(pdu, offset, "dqd", &fid, &off, &max_count); + if (err < 0) { + goto out_nofid; + } + trace_v9fs_read(pdu->tag, pdu->id, fid, off, max_count); + + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -EINVAL; + goto out_nofid; + } + if (fidp->fid_type == P9_FID_DIR) { + + if (off == 0) { + v9fs_co_rewinddir(pdu, fidp); + } + count = v9fs_do_readdir_with_stat(pdu, fidp, max_count); + if (count < 0) { + err = count; + goto out; + } + err = pdu_marshal(pdu, offset, "d", count); + if (err < 0) { + goto out; + } + err += offset + count; + } else if (fidp->fid_type == P9_FID_FILE) { + QEMUIOVector qiov_full; + QEMUIOVector qiov; + int32_t len; + + v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset + 4, max_count, false); + qemu_iovec_init(&qiov, qiov_full.niov); + do { + qemu_iovec_reset(&qiov); + qemu_iovec_concat(&qiov, &qiov_full, count, qiov_full.size - count); + if (0) { + print_sg(qiov.iov, qiov.niov); + } + /* Loop in case of EINTR */ + do { + len = v9fs_co_preadv(pdu, fidp, qiov.iov, qiov.niov, off); + if (len >= 0) { + off += len; + count += len; + } + } while (len == -EINTR && !pdu->cancelled); + if (len < 0) { + /* IO error return the error */ + err = len; + goto out; + } + } while (count < max_count && len > 0); + err = pdu_marshal(pdu, offset, "d", count); + if (err < 0) { + goto out; + } + err += offset + count; + qemu_iovec_destroy(&qiov); + qemu_iovec_destroy(&qiov_full); + } else if (fidp->fid_type == P9_FID_XATTR) { + err = v9fs_xattr_read(s, pdu, fidp, off, max_count); + } else { + err = -EINVAL; + } + trace_v9fs_read_return(pdu->tag, pdu->id, count, err); +out: + put_fid(pdu, fidp); +out_nofid: + pdu_complete(pdu, err); +} + +static size_t v9fs_readdir_data_size(V9fsString *name) +{ + /* + * Size of each dirent on the wire: size of qid (13) + size of offset (8) + * size of type (1) + size of name.size (2) + strlen(name.data) + */ + return 24 + v9fs_string_size(name); +} + +static int v9fs_do_readdir(V9fsPDU *pdu, + V9fsFidState *fidp, int32_t max_count) +{ + size_t size; + V9fsQID qid; + V9fsString name; + int len, err = 0; + int32_t count = 0; + off_t saved_dir_pos; + struct dirent *dent, *result; + + /* save the directory position */ + saved_dir_pos = v9fs_co_telldir(pdu, fidp); + if (saved_dir_pos < 0) { + return saved_dir_pos; + } + + dent = g_malloc(sizeof(struct dirent)); + + while (1) { + err = v9fs_co_readdir_r(pdu, fidp, dent, &result); + if (err || !result) { + break; + } + v9fs_string_init(&name); + v9fs_string_sprintf(&name, "%s", dent->d_name); + if ((count + v9fs_readdir_data_size(&name)) > max_count) { + /* Ran out of buffer. Set dir back to old position and return */ + v9fs_co_seekdir(pdu, fidp, saved_dir_pos); + v9fs_string_free(&name); + g_free(dent); + return count; + } + /* + * Fill up just the path field of qid because the client uses + * only that. To fill the entire qid structure we will have + * to stat each dirent found, which is expensive + */ + size = MIN(sizeof(dent->d_ino), sizeof(qid.path)); + memcpy(&qid.path, &dent->d_ino, size); + /* Fill the other fields with dummy values */ + qid.type = 0; + qid.version = 0; + + /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */ + len = pdu_marshal(pdu, 11 + count, "Qqbs", + &qid, dent->d_off, + dent->d_type, &name); + if (len < 0) { + v9fs_co_seekdir(pdu, fidp, saved_dir_pos); + v9fs_string_free(&name); + g_free(dent); + return len; + } + count += len; + v9fs_string_free(&name); + saved_dir_pos = dent->d_off; + } + g_free(dent); + if (err < 0) { + return err; + } + return count; +} + +static void v9fs_readdir(void *opaque) +{ + int32_t fid; + V9fsFidState *fidp; + ssize_t retval = 0; + size_t offset = 7; + uint64_t initial_offset; + int32_t count; + uint32_t max_count; + V9fsPDU *pdu = opaque; + + retval = pdu_unmarshal(pdu, offset, "dqd", &fid, + &initial_offset, &max_count); + if (retval < 0) { + goto out_nofid; + } + trace_v9fs_readdir(pdu->tag, pdu->id, fid, initial_offset, max_count); + + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + retval = -EINVAL; + goto out_nofid; + } + if (!fidp->fs.dir) { + retval = -EINVAL; + goto out; + } + if (initial_offset == 0) { + v9fs_co_rewinddir(pdu, fidp); + } else { + v9fs_co_seekdir(pdu, fidp, initial_offset); + } + count = v9fs_do_readdir(pdu, fidp, max_count); + if (count < 0) { + retval = count; + goto out; + } + retval = pdu_marshal(pdu, offset, "d", count); + if (retval < 0) { + goto out; + } + retval += count + offset; + trace_v9fs_readdir_return(pdu->tag, pdu->id, count, retval); +out: + put_fid(pdu, fidp); +out_nofid: + pdu_complete(pdu, retval); +} + +static int v9fs_xattr_write(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, + uint64_t off, uint32_t count, + struct iovec *sg, int cnt) +{ + int i, to_copy; + ssize_t err = 0; + int write_count; + int64_t xattr_len; + size_t offset = 7; + + + xattr_len = fidp->fs.xattr.len; + write_count = xattr_len - off; + if (write_count > count) { + write_count = count; + } else if (write_count < 0) { + /* + * write beyond XATTR value len specified in + * xattrcreate + */ + err = -ENOSPC; + goto out; + } + err = pdu_marshal(pdu, offset, "d", write_count); + if (err < 0) { + return err; + } + err += offset; + fidp->fs.xattr.copied_len += write_count; + /* + * Now copy the content from sg list + */ + for (i = 0; i < cnt; i++) { + if (write_count > sg[i].iov_len) { + to_copy = sg[i].iov_len; + } else { + to_copy = write_count; + } + memcpy((char *)fidp->fs.xattr.value + off, sg[i].iov_base, to_copy); + /* updating vs->off since we are not using below */ + off += to_copy; + write_count -= to_copy; + } +out: + return err; +} + +static void v9fs_write(void *opaque) +{ + ssize_t err; + int32_t fid; + uint64_t off; + uint32_t count; + int32_t len = 0; + int32_t total = 0; + size_t offset = 7; + V9fsFidState *fidp; + V9fsPDU *pdu = opaque; + V9fsState *s = pdu->s; + QEMUIOVector qiov_full; + QEMUIOVector qiov; + + err = pdu_unmarshal(pdu, offset, "dqd", &fid, &off, &count); + if (err < 0) { + pdu_complete(pdu, err); + return; + } + offset += err; + v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset, count, true); + trace_v9fs_write(pdu->tag, pdu->id, fid, off, count, qiov_full.niov); + + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -EINVAL; + goto out_nofid; + } + if (fidp->fid_type == P9_FID_FILE) { + if (fidp->fs.fd == -1) { + err = -EINVAL; + goto out; + } + } else if (fidp->fid_type == P9_FID_XATTR) { + /* + * setxattr operation + */ + err = v9fs_xattr_write(s, pdu, fidp, off, count, + qiov_full.iov, qiov_full.niov); + goto out; + } else { + err = -EINVAL; + goto out; + } + qemu_iovec_init(&qiov, qiov_full.niov); + do { + qemu_iovec_reset(&qiov); + qemu_iovec_concat(&qiov, &qiov_full, total, qiov_full.size - total); + if (0) { + print_sg(qiov.iov, qiov.niov); + } + /* Loop in case of EINTR */ + do { + len = v9fs_co_pwritev(pdu, fidp, qiov.iov, qiov.niov, off); + if (len >= 0) { + off += len; + total += len; + } + } while (len == -EINTR && !pdu->cancelled); + if (len < 0) { + /* IO error return the error */ + err = len; + goto out_qiov; + } + } while (total < count && len > 0); + + offset = 7; + err = pdu_marshal(pdu, offset, "d", total); + if (err < 0) { + goto out; + } + err += offset; + trace_v9fs_write_return(pdu->tag, pdu->id, total, err); +out_qiov: + qemu_iovec_destroy(&qiov); +out: + put_fid(pdu, fidp); +out_nofid: + qemu_iovec_destroy(&qiov_full); + pdu_complete(pdu, err); +} + +static void v9fs_create(void *opaque) +{ + int32_t fid; + int err = 0; + size_t offset = 7; + V9fsFidState *fidp; + V9fsQID qid; + int32_t perm; + int8_t mode; + V9fsPath path; + struct stat stbuf; + V9fsString name; + V9fsString extension; + int iounit; + V9fsPDU *pdu = opaque; + + v9fs_path_init(&path); + v9fs_string_init(&name); + v9fs_string_init(&extension); + err = pdu_unmarshal(pdu, offset, "dsdbs", &fid, &name, + &perm, &mode, &extension); + if (err < 0) { + goto out_nofid; + } + trace_v9fs_create(pdu->tag, pdu->id, fid, name.data, perm, mode); + + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -EINVAL; + goto out_nofid; + } + if (perm & P9_STAT_MODE_DIR) { + err = v9fs_co_mkdir(pdu, fidp, &name, perm & 0777, + fidp->uid, -1, &stbuf); + if (err < 0) { + goto out; + } + err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); + if (err < 0) { + goto out; + } + v9fs_path_copy(&fidp->path, &path); + err = v9fs_co_opendir(pdu, fidp); + if (err < 0) { + goto out; + } + fidp->fid_type = P9_FID_DIR; + } else if (perm & P9_STAT_MODE_SYMLINK) { + err = v9fs_co_symlink(pdu, fidp, &name, + extension.data, -1 , &stbuf); + if (err < 0) { + goto out; + } + err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); + if (err < 0) { + goto out; + } + v9fs_path_copy(&fidp->path, &path); + } else if (perm & P9_STAT_MODE_LINK) { + int32_t ofid = atoi(extension.data); + V9fsFidState *ofidp = get_fid(pdu, ofid); + if (ofidp == NULL) { + err = -EINVAL; + goto out; + } + err = v9fs_co_link(pdu, ofidp, fidp, &name); + put_fid(pdu, ofidp); + if (err < 0) { + goto out; + } + err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); + if (err < 0) { + fidp->fid_type = P9_FID_NONE; + goto out; + } + v9fs_path_copy(&fidp->path, &path); + err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); + if (err < 0) { + fidp->fid_type = P9_FID_NONE; + goto out; + } + } else if (perm & P9_STAT_MODE_DEVICE) { + char ctype; + uint32_t major, minor; + mode_t nmode = 0; + + if (sscanf(extension.data, "%c %u %u", &ctype, &major, &minor) != 3) { + err = -errno; + goto out; + } + + switch (ctype) { + case 'c': + nmode = S_IFCHR; + break; + case 'b': + nmode = S_IFBLK; + break; + default: + err = -EIO; + goto out; + } + + nmode |= perm & 0777; + err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, + makedev(major, minor), nmode, &stbuf); + if (err < 0) { + goto out; + } + err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); + if (err < 0) { + goto out; + } + v9fs_path_copy(&fidp->path, &path); + } else if (perm & P9_STAT_MODE_NAMED_PIPE) { + err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, + 0, S_IFIFO | (perm & 0777), &stbuf); + if (err < 0) { + goto out; + } + err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); + if (err < 0) { + goto out; + } + v9fs_path_copy(&fidp->path, &path); + } else if (perm & P9_STAT_MODE_SOCKET) { + err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, + 0, S_IFSOCK | (perm & 0777), &stbuf); + if (err < 0) { + goto out; + } + err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); + if (err < 0) { + goto out; + } + v9fs_path_copy(&fidp->path, &path); + } else { + err = v9fs_co_open2(pdu, fidp, &name, -1, + omode_to_uflags(mode)|O_CREAT, perm, &stbuf); + if (err < 0) { + goto out; + } + fidp->fid_type = P9_FID_FILE; + fidp->open_flags = omode_to_uflags(mode); + if (fidp->open_flags & O_EXCL) { + /* + * We let the host file system do O_EXCL check + * We should not reclaim such fd + */ + fidp->flags |= FID_NON_RECLAIMABLE; + } + } + iounit = get_iounit(pdu, &fidp->path); + stat_to_qid(&stbuf, &qid); + err = pdu_marshal(pdu, offset, "Qd", &qid, iounit); + if (err < 0) { + goto out; + } + err += offset; + trace_v9fs_create_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path, iounit); +out: + put_fid(pdu, fidp); +out_nofid: + pdu_complete(pdu, err); + v9fs_string_free(&name); + v9fs_string_free(&extension); + v9fs_path_free(&path); +} + +static void v9fs_symlink(void *opaque) +{ + V9fsPDU *pdu = opaque; + V9fsString name; + V9fsString symname; + V9fsFidState *dfidp; + V9fsQID qid; + struct stat stbuf; + int32_t dfid; + int err = 0; + gid_t gid; + size_t offset = 7; + + v9fs_string_init(&name); + v9fs_string_init(&symname); + err = pdu_unmarshal(pdu, offset, "dssd", &dfid, &name, &symname, &gid); + if (err < 0) { + goto out_nofid; + } + trace_v9fs_symlink(pdu->tag, pdu->id, dfid, name.data, symname.data, gid); + + dfidp = get_fid(pdu, dfid); + if (dfidp == NULL) { + err = -EINVAL; + goto out_nofid; + } + err = v9fs_co_symlink(pdu, dfidp, &name, symname.data, gid, &stbuf); + if (err < 0) { + goto out; + } + stat_to_qid(&stbuf, &qid); + err = pdu_marshal(pdu, offset, "Q", &qid); + if (err < 0) { + goto out; + } + err += offset; + trace_v9fs_symlink_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path); +out: + put_fid(pdu, dfidp); +out_nofid: + pdu_complete(pdu, err); + v9fs_string_free(&name); + v9fs_string_free(&symname); +} + +static void v9fs_flush(void *opaque) +{ + ssize_t err; + int16_t tag; + size_t offset = 7; + V9fsPDU *cancel_pdu; + V9fsPDU *pdu = opaque; + V9fsState *s = pdu->s; + + err = pdu_unmarshal(pdu, offset, "w", &tag); + if (err < 0) { + pdu_complete(pdu, err); + return; + } + trace_v9fs_flush(pdu->tag, pdu->id, tag); + + QLIST_FOREACH(cancel_pdu, &s->active_list, next) { + if (cancel_pdu->tag == tag) { + break; + } + } + if (cancel_pdu) { + cancel_pdu->cancelled = 1; + /* + * Wait for pdu to complete. + */ + qemu_co_queue_wait(&cancel_pdu->complete); + cancel_pdu->cancelled = 0; + pdu_free(cancel_pdu); + } + pdu_complete(pdu, 7); +} + +static void v9fs_link(void *opaque) +{ + V9fsPDU *pdu = opaque; + int32_t dfid, oldfid; + V9fsFidState *dfidp, *oldfidp; + V9fsString name; + size_t offset = 7; + int err = 0; + + v9fs_string_init(&name); + err = pdu_unmarshal(pdu, offset, "dds", &dfid, &oldfid, &name); + if (err < 0) { + goto out_nofid; + } + trace_v9fs_link(pdu->tag, pdu->id, dfid, oldfid, name.data); + + dfidp = get_fid(pdu, dfid); + if (dfidp == NULL) { + err = -ENOENT; + goto out_nofid; + } + + oldfidp = get_fid(pdu, oldfid); + if (oldfidp == NULL) { + err = -ENOENT; + goto out; + } + err = v9fs_co_link(pdu, oldfidp, dfidp, &name); + if (!err) { + err = offset; + } +out: + put_fid(pdu, dfidp); +out_nofid: + v9fs_string_free(&name); + pdu_complete(pdu, err); +} + +/* Only works with path name based fid */ +static void v9fs_remove(void *opaque) +{ + int32_t fid; + int err = 0; + size_t offset = 7; + V9fsFidState *fidp; + V9fsPDU *pdu = opaque; + + err = pdu_unmarshal(pdu, offset, "d", &fid); + if (err < 0) { + goto out_nofid; + } + trace_v9fs_remove(pdu->tag, pdu->id, fid); + + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -EINVAL; + goto out_nofid; + } + /* if fs driver is not path based, return EOPNOTSUPP */ + if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) { + err = -EOPNOTSUPP; + goto out_err; + } + /* + * IF the file is unlinked, we cannot reopen + * the file later. So don't reclaim fd + */ + err = v9fs_mark_fids_unreclaim(pdu, &fidp->path); + if (err < 0) { + goto out_err; + } + err = v9fs_co_remove(pdu, &fidp->path); + if (!err) { + err = offset; + } +out_err: + /* For TREMOVE we need to clunk the fid even on failed remove */ + clunk_fid(pdu->s, fidp->fid); + put_fid(pdu, fidp); +out_nofid: + pdu_complete(pdu, err); +} + +static void v9fs_unlinkat(void *opaque) +{ + int err = 0; + V9fsString name; + int32_t dfid, flags; + size_t offset = 7; + V9fsPath path; + V9fsFidState *dfidp; + V9fsPDU *pdu = opaque; + + v9fs_string_init(&name); + err = pdu_unmarshal(pdu, offset, "dsd", &dfid, &name, &flags); + if (err < 0) { + goto out_nofid; + } + dfidp = get_fid(pdu, dfid); + if (dfidp == NULL) { + err = -EINVAL; + goto out_nofid; + } + /* + * IF the file is unlinked, we cannot reopen + * the file later. So don't reclaim fd + */ + v9fs_path_init(&path); + err = v9fs_co_name_to_path(pdu, &dfidp->path, name.data, &path); + if (err < 0) { + goto out_err; + } + err = v9fs_mark_fids_unreclaim(pdu, &path); + if (err < 0) { + goto out_err; + } + err = v9fs_co_unlinkat(pdu, &dfidp->path, &name, flags); + if (!err) { + err = offset; + } +out_err: + put_fid(pdu, dfidp); + v9fs_path_free(&path); +out_nofid: + pdu_complete(pdu, err); + v9fs_string_free(&name); +} + + +/* Only works with path name based fid */ +static int v9fs_complete_rename(V9fsPDU *pdu, V9fsFidState *fidp, + int32_t newdirfid, V9fsString *name) +{ + char *end; + int err = 0; + V9fsPath new_path; + V9fsFidState *tfidp; + V9fsState *s = pdu->s; + V9fsFidState *dirfidp = NULL; + char *old_name, *new_name; + + v9fs_path_init(&new_path); + if (newdirfid != -1) { + dirfidp = get_fid(pdu, newdirfid); + if (dirfidp == NULL) { + err = -ENOENT; + goto out_nofid; + } + BUG_ON(dirfidp->fid_type != P9_FID_NONE); + v9fs_co_name_to_path(pdu, &dirfidp->path, name->data, &new_path); + } else { + old_name = fidp->path.data; + end = strrchr(old_name, '/'); + if (end) { + end++; + } else { + end = old_name; + } + new_name = g_malloc0(end - old_name + name->size + 1); + strncat(new_name, old_name, end - old_name); + strncat(new_name + (end - old_name), name->data, name->size); + v9fs_co_name_to_path(pdu, NULL, new_name, &new_path); + g_free(new_name); + } + err = v9fs_co_rename(pdu, &fidp->path, &new_path); + if (err < 0) { + goto out; + } + /* + * Fixup fid's pointing to the old name to + * start pointing to the new name + */ + for (tfidp = s->fid_list; tfidp; tfidp = tfidp->next) { + if (v9fs_path_is_ancestor(&fidp->path, &tfidp->path)) { + /* replace the name */ + v9fs_fix_path(&tfidp->path, &new_path, strlen(fidp->path.data)); + } + } +out: + if (dirfidp) { + put_fid(pdu, dirfidp); + } + v9fs_path_free(&new_path); +out_nofid: + return err; +} + +/* Only works with path name based fid */ +static void v9fs_rename(void *opaque) +{ + int32_t fid; + ssize_t err = 0; + size_t offset = 7; + V9fsString name; + int32_t newdirfid; + V9fsFidState *fidp; + V9fsPDU *pdu = opaque; + V9fsState *s = pdu->s; + + v9fs_string_init(&name); + err = pdu_unmarshal(pdu, offset, "dds", &fid, &newdirfid, &name); + if (err < 0) { + goto out_nofid; + } + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -ENOENT; + goto out_nofid; + } + BUG_ON(fidp->fid_type != P9_FID_NONE); + /* if fs driver is not path based, return EOPNOTSUPP */ + if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) { + err = -EOPNOTSUPP; + goto out; + } + v9fs_path_write_lock(s); + err = v9fs_complete_rename(pdu, fidp, newdirfid, &name); + v9fs_path_unlock(s); + if (!err) { + err = offset; + } +out: + put_fid(pdu, fidp); +out_nofid: + pdu_complete(pdu, err); + v9fs_string_free(&name); +} + +static void v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir, + V9fsString *old_name, V9fsPath *newdir, + V9fsString *new_name) +{ + V9fsFidState *tfidp; + V9fsPath oldpath, newpath; + V9fsState *s = pdu->s; + + + v9fs_path_init(&oldpath); + v9fs_path_init(&newpath); + v9fs_co_name_to_path(pdu, olddir, old_name->data, &oldpath); + v9fs_co_name_to_path(pdu, newdir, new_name->data, &newpath); + + /* + * Fixup fid's pointing to the old name to + * start pointing to the new name + */ + for (tfidp = s->fid_list; tfidp; tfidp = tfidp->next) { + if (v9fs_path_is_ancestor(&oldpath, &tfidp->path)) { + /* replace the name */ + v9fs_fix_path(&tfidp->path, &newpath, strlen(oldpath.data)); + } + } + v9fs_path_free(&oldpath); + v9fs_path_free(&newpath); +} + +static int v9fs_complete_renameat(V9fsPDU *pdu, int32_t olddirfid, + V9fsString *old_name, int32_t newdirfid, + V9fsString *new_name) +{ + int err = 0; + V9fsState *s = pdu->s; + V9fsFidState *newdirfidp = NULL, *olddirfidp = NULL; + + olddirfidp = get_fid(pdu, olddirfid); + if (olddirfidp == NULL) { + err = -ENOENT; + goto out; + } + if (newdirfid != -1) { + newdirfidp = get_fid(pdu, newdirfid); + if (newdirfidp == NULL) { + err = -ENOENT; + goto out; + } + } else { + newdirfidp = get_fid(pdu, olddirfid); + } + + err = v9fs_co_renameat(pdu, &olddirfidp->path, old_name, + &newdirfidp->path, new_name); + if (err < 0) { + goto out; + } + if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { + /* Only for path based fid we need to do the below fixup */ + v9fs_fix_fid_paths(pdu, &olddirfidp->path, old_name, + &newdirfidp->path, new_name); + } +out: + if (olddirfidp) { + put_fid(pdu, olddirfidp); + } + if (newdirfidp) { + put_fid(pdu, newdirfidp); + } + return err; +} + +static void v9fs_renameat(void *opaque) +{ + ssize_t err = 0; + size_t offset = 7; + V9fsPDU *pdu = opaque; + V9fsState *s = pdu->s; + int32_t olddirfid, newdirfid; + V9fsString old_name, new_name; + + v9fs_string_init(&old_name); + v9fs_string_init(&new_name); + err = pdu_unmarshal(pdu, offset, "dsds", &olddirfid, + &old_name, &newdirfid, &new_name); + if (err < 0) { + goto out_err; + } + + v9fs_path_write_lock(s); + err = v9fs_complete_renameat(pdu, olddirfid, + &old_name, newdirfid, &new_name); + v9fs_path_unlock(s); + if (!err) { + err = offset; + } + +out_err: + pdu_complete(pdu, err); + v9fs_string_free(&old_name); + v9fs_string_free(&new_name); +} + +static void v9fs_wstat(void *opaque) +{ + int32_t fid; + int err = 0; + int16_t unused; + V9fsStat v9stat; + size_t offset = 7; + struct stat stbuf; + V9fsFidState *fidp; + V9fsPDU *pdu = opaque; + + v9fs_stat_init(&v9stat); + err = pdu_unmarshal(pdu, offset, "dwS", &fid, &unused, &v9stat); + if (err < 0) { + goto out_nofid; + } + trace_v9fs_wstat(pdu->tag, pdu->id, fid, + v9stat.mode, v9stat.atime, v9stat.mtime); + + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -EINVAL; + goto out_nofid; + } + /* do we need to sync the file? */ + if (donttouch_stat(&v9stat)) { + err = v9fs_co_fsync(pdu, fidp, 0); + goto out; + } + if (v9stat.mode != -1) { + uint32_t v9_mode; + err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); + if (err < 0) { + goto out; + } + v9_mode = stat_to_v9mode(&stbuf); + if ((v9stat.mode & P9_STAT_MODE_TYPE_BITS) != + (v9_mode & P9_STAT_MODE_TYPE_BITS)) { + /* Attempting to change the type */ + err = -EIO; + goto out; + } + err = v9fs_co_chmod(pdu, &fidp->path, + v9mode_to_mode(v9stat.mode, + &v9stat.extension)); + if (err < 0) { + goto out; + } + } + if (v9stat.mtime != -1 || v9stat.atime != -1) { + struct timespec times[2]; + if (v9stat.atime != -1) { + times[0].tv_sec = v9stat.atime; + times[0].tv_nsec = 0; + } else { + times[0].tv_nsec = UTIME_OMIT; + } + if (v9stat.mtime != -1) { + times[1].tv_sec = v9stat.mtime; + times[1].tv_nsec = 0; + } else { + times[1].tv_nsec = UTIME_OMIT; + } + err = v9fs_co_utimensat(pdu, &fidp->path, times); + if (err < 0) { + goto out; + } + } + if (v9stat.n_gid != -1 || v9stat.n_uid != -1) { + err = v9fs_co_chown(pdu, &fidp->path, v9stat.n_uid, v9stat.n_gid); + if (err < 0) { + goto out; + } + } + if (v9stat.name.size != 0) { + err = v9fs_complete_rename(pdu, fidp, -1, &v9stat.name); + if (err < 0) { + goto out; + } + } + if (v9stat.length != -1) { + err = v9fs_co_truncate(pdu, &fidp->path, v9stat.length); + if (err < 0) { + goto out; + } + } + err = offset; +out: + put_fid(pdu, fidp); +out_nofid: + v9fs_stat_free(&v9stat); + pdu_complete(pdu, err); +} + +static int v9fs_fill_statfs(V9fsState *s, V9fsPDU *pdu, struct statfs *stbuf) +{ + uint32_t f_type; + uint32_t f_bsize; + uint64_t f_blocks; + uint64_t f_bfree; + uint64_t f_bavail; + uint64_t f_files; + uint64_t f_ffree; + uint64_t fsid_val; + uint32_t f_namelen; + size_t offset = 7; + int32_t bsize_factor; + + /* + * compute bsize factor based on host file system block size + * and client msize + */ + bsize_factor = (s->msize - P9_IOHDRSZ)/stbuf->f_bsize; + if (!bsize_factor) { + bsize_factor = 1; + } + f_type = stbuf->f_type; + f_bsize = stbuf->f_bsize; + f_bsize *= bsize_factor; + /* + * f_bsize is adjusted(multiplied) by bsize factor, so we need to + * adjust(divide) the number of blocks, free blocks and available + * blocks by bsize factor + */ + f_blocks = stbuf->f_blocks/bsize_factor; + f_bfree = stbuf->f_bfree/bsize_factor; + f_bavail = stbuf->f_bavail/bsize_factor; + f_files = stbuf->f_files; + f_ffree = stbuf->f_ffree; + fsid_val = (unsigned int) stbuf->f_fsid.__val[0] | + (unsigned long long)stbuf->f_fsid.__val[1] << 32; + f_namelen = stbuf->f_namelen; + + return pdu_marshal(pdu, offset, "ddqqqqqqd", + f_type, f_bsize, f_blocks, f_bfree, + f_bavail, f_files, f_ffree, + fsid_val, f_namelen); +} + +static void v9fs_statfs(void *opaque) +{ + int32_t fid; + ssize_t retval = 0; + size_t offset = 7; + V9fsFidState *fidp; + struct statfs stbuf; + V9fsPDU *pdu = opaque; + V9fsState *s = pdu->s; + + retval = pdu_unmarshal(pdu, offset, "d", &fid); + if (retval < 0) { + goto out_nofid; + } + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + retval = -ENOENT; + goto out_nofid; + } + retval = v9fs_co_statfs(pdu, &fidp->path, &stbuf); + if (retval < 0) { + goto out; + } + retval = v9fs_fill_statfs(s, pdu, &stbuf); + if (retval < 0) { + goto out; + } + retval += offset; +out: + put_fid(pdu, fidp); +out_nofid: + pdu_complete(pdu, retval); +} + +static void v9fs_mknod(void *opaque) +{ + + int mode; + gid_t gid; + int32_t fid; + V9fsQID qid; + int err = 0; + int major, minor; + size_t offset = 7; + V9fsString name; + struct stat stbuf; + V9fsFidState *fidp; + V9fsPDU *pdu = opaque; + + v9fs_string_init(&name); + err = pdu_unmarshal(pdu, offset, "dsdddd", &fid, &name, &mode, + &major, &minor, &gid); + if (err < 0) { + goto out_nofid; + } + trace_v9fs_mknod(pdu->tag, pdu->id, fid, mode, major, minor); + + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -ENOENT; + goto out_nofid; + } + err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, gid, + makedev(major, minor), mode, &stbuf); + if (err < 0) { + goto out; + } + stat_to_qid(&stbuf, &qid); + err = pdu_marshal(pdu, offset, "Q", &qid); + if (err < 0) { + goto out; + } + err += offset; + trace_v9fs_mknod_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path); +out: + put_fid(pdu, fidp); +out_nofid: + pdu_complete(pdu, err); + v9fs_string_free(&name); +} + +/* + * Implement posix byte range locking code + * Server side handling of locking code is very simple, because 9p server in + * QEMU can handle only one client. And most of the lock handling + * (like conflict, merging) etc is done by the VFS layer itself, so no need to + * do any thing in * qemu 9p server side lock code path. + * So when a TLOCK request comes, always return success + */ +static void v9fs_lock(void *opaque) +{ + int8_t status; + V9fsFlock flock; + size_t offset = 7; + struct stat stbuf; + V9fsFidState *fidp; + int32_t fid, err = 0; + V9fsPDU *pdu = opaque; + + status = P9_LOCK_ERROR; + v9fs_string_init(&flock.client_id); + err = pdu_unmarshal(pdu, offset, "dbdqqds", &fid, &flock.type, + &flock.flags, &flock.start, &flock.length, + &flock.proc_id, &flock.client_id); + if (err < 0) { + goto out_nofid; + } + trace_v9fs_lock(pdu->tag, pdu->id, fid, + flock.type, flock.start, flock.length); + + + /* We support only block flag now (that too ignored currently) */ + if (flock.flags & ~P9_LOCK_FLAGS_BLOCK) { + err = -EINVAL; + goto out_nofid; + } + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -ENOENT; + goto out_nofid; + } + err = v9fs_co_fstat(pdu, fidp, &stbuf); + if (err < 0) { + goto out; + } + status = P9_LOCK_SUCCESS; +out: + put_fid(pdu, fidp); +out_nofid: + err = pdu_marshal(pdu, offset, "b", status); + if (err > 0) { + err += offset; + } + trace_v9fs_lock_return(pdu->tag, pdu->id, status); + pdu_complete(pdu, err); + v9fs_string_free(&flock.client_id); +} + +/* + * When a TGETLOCK request comes, always return success because all lock + * handling is done by client's VFS layer. + */ +static void v9fs_getlock(void *opaque) +{ + size_t offset = 7; + struct stat stbuf; + V9fsFidState *fidp; + V9fsGetlock glock; + int32_t fid, err = 0; + V9fsPDU *pdu = opaque; + + v9fs_string_init(&glock.client_id); + err = pdu_unmarshal(pdu, offset, "dbqqds", &fid, &glock.type, + &glock.start, &glock.length, &glock.proc_id, + &glock.client_id); + if (err < 0) { + goto out_nofid; + } + trace_v9fs_getlock(pdu->tag, pdu->id, fid, + glock.type, glock.start, glock.length); + + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -ENOENT; + goto out_nofid; + } + err = v9fs_co_fstat(pdu, fidp, &stbuf); + if (err < 0) { + goto out; + } + glock.type = P9_LOCK_TYPE_UNLCK; + err = pdu_marshal(pdu, offset, "bqqds", glock.type, + glock.start, glock.length, glock.proc_id, + &glock.client_id); + if (err < 0) { + goto out; + } + err += offset; + trace_v9fs_getlock_return(pdu->tag, pdu->id, glock.type, glock.start, + glock.length, glock.proc_id); +out: + put_fid(pdu, fidp); +out_nofid: + pdu_complete(pdu, err); + v9fs_string_free(&glock.client_id); +} + +static void v9fs_mkdir(void *opaque) +{ + V9fsPDU *pdu = opaque; + size_t offset = 7; + int32_t fid; + struct stat stbuf; + V9fsQID qid; + V9fsString name; + V9fsFidState *fidp; + gid_t gid; + int mode; + int err = 0; + + v9fs_string_init(&name); + err = pdu_unmarshal(pdu, offset, "dsdd", &fid, &name, &mode, &gid); + if (err < 0) { + goto out_nofid; + } + trace_v9fs_mkdir(pdu->tag, pdu->id, fid, name.data, mode, gid); + + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -ENOENT; + goto out_nofid; + } + err = v9fs_co_mkdir(pdu, fidp, &name, mode, fidp->uid, gid, &stbuf); + if (err < 0) { + goto out; + } + stat_to_qid(&stbuf, &qid); + err = pdu_marshal(pdu, offset, "Q", &qid); + if (err < 0) { + goto out; + } + err += offset; + trace_v9fs_mkdir_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path, err); +out: + put_fid(pdu, fidp); +out_nofid: + pdu_complete(pdu, err); + v9fs_string_free(&name); +} + +static void v9fs_xattrwalk(void *opaque) +{ + int64_t size; + V9fsString name; + ssize_t err = 0; + size_t offset = 7; + int32_t fid, newfid; + V9fsFidState *file_fidp; + V9fsFidState *xattr_fidp = NULL; + V9fsPDU *pdu = opaque; + V9fsState *s = pdu->s; + + v9fs_string_init(&name); + err = pdu_unmarshal(pdu, offset, "dds", &fid, &newfid, &name); + if (err < 0) { + goto out_nofid; + } + trace_v9fs_xattrwalk(pdu->tag, pdu->id, fid, newfid, name.data); + + file_fidp = get_fid(pdu, fid); + if (file_fidp == NULL) { + err = -ENOENT; + goto out_nofid; + } + xattr_fidp = alloc_fid(s, newfid); + if (xattr_fidp == NULL) { + err = -EINVAL; + goto out; + } + v9fs_path_copy(&xattr_fidp->path, &file_fidp->path); + if (name.data == NULL) { + /* + * listxattr request. Get the size first + */ + size = v9fs_co_llistxattr(pdu, &xattr_fidp->path, NULL, 0); + if (size < 0) { + err = size; + clunk_fid(s, xattr_fidp->fid); + goto out; + } + /* + * Read the xattr value + */ + xattr_fidp->fs.xattr.len = size; + xattr_fidp->fid_type = P9_FID_XATTR; + xattr_fidp->fs.xattr.copied_len = -1; + if (size) { + xattr_fidp->fs.xattr.value = g_malloc(size); + err = v9fs_co_llistxattr(pdu, &xattr_fidp->path, + xattr_fidp->fs.xattr.value, + xattr_fidp->fs.xattr.len); + if (err < 0) { + clunk_fid(s, xattr_fidp->fid); + goto out; + } + } + err = pdu_marshal(pdu, offset, "q", size); + if (err < 0) { + goto out; + } + err += offset; + } else { + /* + * specific xattr fid. We check for xattr + * presence also collect the xattr size + */ + size = v9fs_co_lgetxattr(pdu, &xattr_fidp->path, + &name, NULL, 0); + if (size < 0) { + err = size; + clunk_fid(s, xattr_fidp->fid); + goto out; + } + /* + * Read the xattr value + */ + xattr_fidp->fs.xattr.len = size; + xattr_fidp->fid_type = P9_FID_XATTR; + xattr_fidp->fs.xattr.copied_len = -1; + if (size) { + xattr_fidp->fs.xattr.value = g_malloc(size); + err = v9fs_co_lgetxattr(pdu, &xattr_fidp->path, + &name, xattr_fidp->fs.xattr.value, + xattr_fidp->fs.xattr.len); + if (err < 0) { + clunk_fid(s, xattr_fidp->fid); + goto out; + } + } + err = pdu_marshal(pdu, offset, "q", size); + if (err < 0) { + goto out; + } + err += offset; + } + trace_v9fs_xattrwalk_return(pdu->tag, pdu->id, size); +out: + put_fid(pdu, file_fidp); + if (xattr_fidp) { + put_fid(pdu, xattr_fidp); + } +out_nofid: + pdu_complete(pdu, err); + v9fs_string_free(&name); +} + +static void v9fs_xattrcreate(void *opaque) +{ + int flags; + int32_t fid; + int64_t size; + ssize_t err = 0; + V9fsString name; + size_t offset = 7; + V9fsFidState *file_fidp; + V9fsFidState *xattr_fidp; + V9fsPDU *pdu = opaque; + + v9fs_string_init(&name); + err = pdu_unmarshal(pdu, offset, "dsqd", &fid, &name, &size, &flags); + if (err < 0) { + goto out_nofid; + } + trace_v9fs_xattrcreate(pdu->tag, pdu->id, fid, name.data, size, flags); + + file_fidp = get_fid(pdu, fid); + if (file_fidp == NULL) { + err = -EINVAL; + goto out_nofid; + } + /* Make the file fid point to xattr */ + xattr_fidp = file_fidp; + xattr_fidp->fid_type = P9_FID_XATTR; + xattr_fidp->fs.xattr.copied_len = 0; + xattr_fidp->fs.xattr.len = size; + xattr_fidp->fs.xattr.flags = flags; + v9fs_string_init(&xattr_fidp->fs.xattr.name); + v9fs_string_copy(&xattr_fidp->fs.xattr.name, &name); + xattr_fidp->fs.xattr.value = g_malloc(size); + err = offset; + put_fid(pdu, file_fidp); +out_nofid: + pdu_complete(pdu, err); + v9fs_string_free(&name); +} + +static void v9fs_readlink(void *opaque) +{ + V9fsPDU *pdu = opaque; + size_t offset = 7; + V9fsString target; + int32_t fid; + int err = 0; + V9fsFidState *fidp; + + err = pdu_unmarshal(pdu, offset, "d", &fid); + if (err < 0) { + goto out_nofid; + } + trace_v9fs_readlink(pdu->tag, pdu->id, fid); + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -ENOENT; + goto out_nofid; + } + + v9fs_string_init(&target); + err = v9fs_co_readlink(pdu, &fidp->path, &target); + if (err < 0) { + goto out; + } + err = pdu_marshal(pdu, offset, "s", &target); + if (err < 0) { + v9fs_string_free(&target); + goto out; + } + err += offset; + trace_v9fs_readlink_return(pdu->tag, pdu->id, target.data); + v9fs_string_free(&target); +out: + put_fid(pdu, fidp); +out_nofid: + pdu_complete(pdu, err); +} + +static CoroutineEntry *pdu_co_handlers[] = { + [P9_TREADDIR] = v9fs_readdir, + [P9_TSTATFS] = v9fs_statfs, + [P9_TGETATTR] = v9fs_getattr, + [P9_TSETATTR] = v9fs_setattr, + [P9_TXATTRWALK] = v9fs_xattrwalk, + [P9_TXATTRCREATE] = v9fs_xattrcreate, + [P9_TMKNOD] = v9fs_mknod, + [P9_TRENAME] = v9fs_rename, + [P9_TLOCK] = v9fs_lock, + [P9_TGETLOCK] = v9fs_getlock, + [P9_TRENAMEAT] = v9fs_renameat, + [P9_TREADLINK] = v9fs_readlink, + [P9_TUNLINKAT] = v9fs_unlinkat, + [P9_TMKDIR] = v9fs_mkdir, + [P9_TVERSION] = v9fs_version, + [P9_TLOPEN] = v9fs_open, + [P9_TATTACH] = v9fs_attach, + [P9_TSTAT] = v9fs_stat, + [P9_TWALK] = v9fs_walk, + [P9_TCLUNK] = v9fs_clunk, + [P9_TFSYNC] = v9fs_fsync, + [P9_TOPEN] = v9fs_open, + [P9_TREAD] = v9fs_read, +#if 0 + [P9_TAUTH] = v9fs_auth, +#endif + [P9_TFLUSH] = v9fs_flush, + [P9_TLINK] = v9fs_link, + [P9_TSYMLINK] = v9fs_symlink, + [P9_TCREATE] = v9fs_create, + [P9_TLCREATE] = v9fs_lcreate, + [P9_TWRITE] = v9fs_write, + [P9_TWSTAT] = v9fs_wstat, + [P9_TREMOVE] = v9fs_remove, +}; + +static void v9fs_op_not_supp(void *opaque) +{ + V9fsPDU *pdu = opaque; + pdu_complete(pdu, -EOPNOTSUPP); +} + +static void v9fs_fs_ro(void *opaque) +{ + V9fsPDU *pdu = opaque; + pdu_complete(pdu, -EROFS); +} + +static inline bool is_read_only_op(V9fsPDU *pdu) +{ + switch (pdu->id) { + case P9_TREADDIR: + case P9_TSTATFS: + case P9_TGETATTR: + case P9_TXATTRWALK: + case P9_TLOCK: + case P9_TGETLOCK: + case P9_TREADLINK: + case P9_TVERSION: + case P9_TLOPEN: + case P9_TATTACH: + case P9_TSTAT: + case P9_TWALK: + case P9_TCLUNK: + case P9_TFSYNC: + case P9_TOPEN: + case P9_TREAD: + case P9_TAUTH: + case P9_TFLUSH: + return 1; + default: + return 0; + } +} + +void pdu_submit(V9fsPDU *pdu) +{ + Coroutine *co; + CoroutineEntry *handler; + V9fsState *s = pdu->s; + + if (pdu->id >= ARRAY_SIZE(pdu_co_handlers) || + (pdu_co_handlers[pdu->id] == NULL)) { + handler = v9fs_op_not_supp; + } else { + handler = pdu_co_handlers[pdu->id]; + } + + if (is_ro_export(&s->ctx) && !is_read_only_op(pdu)) { + handler = v9fs_fs_ro; + } + co = qemu_coroutine_create(handler); + qemu_coroutine_enter(co, pdu); +} + +/* Returns 0 on success, 1 on failure. */ +int v9fs_device_realize_common(V9fsState *s, Error **errp) +{ + V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); + int i, len; + struct stat stat; + FsDriverEntry *fse; + V9fsPath path; + int rc = 1; + + /* initialize pdu allocator */ + QLIST_INIT(&s->free_list); + QLIST_INIT(&s->active_list); + for (i = 0; i < (MAX_REQ - 1); i++) { + QLIST_INSERT_HEAD(&s->free_list, &v->pdus[i], next); + v->pdus[i].s = s; + v->pdus[i].idx = i; + } + + v9fs_path_init(&path); + + fse = get_fsdev_fsentry(s->fsconf.fsdev_id); + + if (!fse) { + /* We don't have a fsdev identified by fsdev_id */ + error_setg(errp, "9pfs device couldn't find fsdev with the " + "id = %s", + s->fsconf.fsdev_id ? s->fsconf.fsdev_id : "NULL"); + goto out; + } + + if (!s->fsconf.tag) { + /* we haven't specified a mount_tag */ + error_setg(errp, "fsdev with id %s needs mount_tag arguments", + s->fsconf.fsdev_id); + goto out; + } + + s->ctx.export_flags = fse->export_flags; + s->ctx.fs_root = g_strdup(fse->path); + s->ctx.exops.get_st_gen = NULL; + len = strlen(s->fsconf.tag); + if (len > MAX_TAG_LEN - 1) { + error_setg(errp, "mount tag '%s' (%d bytes) is longer than " + "maximum (%d bytes)", s->fsconf.tag, len, MAX_TAG_LEN - 1); + goto out; + } + + s->tag = g_strdup(s->fsconf.tag); + s->ctx.uid = -1; + + s->ops = fse->ops; + + s->fid_list = NULL; + qemu_co_rwlock_init(&s->rename_lock); + + if (s->ops->init(&s->ctx) < 0) { + error_setg(errp, "9pfs Failed to initialize fs-driver with id:%s" + " and export path:%s", s->fsconf.fsdev_id, s->ctx.fs_root); + goto out; + } + + /* + * Check details of export path, We need to use fs driver + * call back to do that. Since we are in the init path, we don't + * use co-routines here. + */ + if (s->ops->name_to_path(&s->ctx, NULL, "/", &path) < 0) { + error_setg(errp, + "error in converting name to path %s", strerror(errno)); + goto out; + } + if (s->ops->lstat(&s->ctx, &path, &stat)) { + error_setg(errp, "share path %s does not exist", fse->path); + goto out; + } else if (!S_ISDIR(stat.st_mode)) { + error_setg(errp, "share path %s is not a directory", fse->path); + goto out; + } + v9fs_path_free(&path); + + rc = 0; +out: + if (rc) { + g_free(s->ctx.fs_root); + g_free(s->tag); + v9fs_path_free(&path); + } + return rc; +} + +void v9fs_device_unrealize_common(V9fsState *s, Error **errp) +{ + g_free(s->ctx.fs_root); + g_free(s->tag); +} + +static void __attribute__((__constructor__)) v9fs_set_fd_limit(void) +{ + struct rlimit rlim; + if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { + error_report("Failed to get the resource limit"); + exit(1); + } + open_fd_hw = rlim.rlim_cur - MIN(400, rlim.rlim_cur/3); + open_fd_rc = rlim.rlim_cur/2; +} diff --git a/qemu/hw/9pfs/9p.h b/qemu/hw/9pfs/9p.h new file mode 100644 index 000000000..1a19418a8 --- /dev/null +++ b/qemu/hw/9pfs/9p.h @@ -0,0 +1,324 @@ +#ifndef _QEMU_9P_H +#define _QEMU_9P_H + +#include +#include +#include +#include +#include "standard-headers/linux/virtio_9p.h" +#include "hw/virtio/virtio.h" +#include "fsdev/file-op-9p.h" +#include "fsdev/9p-iov-marshal.h" +#include "qemu/thread.h" +#include "qemu/coroutine.h" + +enum { + P9_TLERROR = 6, + P9_RLERROR, + P9_TSTATFS = 8, + P9_RSTATFS, + P9_TLOPEN = 12, + P9_RLOPEN, + P9_TLCREATE = 14, + P9_RLCREATE, + P9_TSYMLINK = 16, + P9_RSYMLINK, + P9_TMKNOD = 18, + P9_RMKNOD, + P9_TRENAME = 20, + P9_RRENAME, + P9_TREADLINK = 22, + P9_RREADLINK, + P9_TGETATTR = 24, + P9_RGETATTR, + P9_TSETATTR = 26, + P9_RSETATTR, + P9_TXATTRWALK = 30, + P9_RXATTRWALK, + P9_TXATTRCREATE = 32, + P9_RXATTRCREATE, + P9_TREADDIR = 40, + P9_RREADDIR, + P9_TFSYNC = 50, + P9_RFSYNC, + P9_TLOCK = 52, + P9_RLOCK, + P9_TGETLOCK = 54, + P9_RGETLOCK, + P9_TLINK = 70, + P9_RLINK, + P9_TMKDIR = 72, + P9_RMKDIR, + P9_TRENAMEAT = 74, + P9_RRENAMEAT, + P9_TUNLINKAT = 76, + P9_RUNLINKAT, + P9_TVERSION = 100, + P9_RVERSION, + P9_TAUTH = 102, + P9_RAUTH, + P9_TATTACH = 104, + P9_RATTACH, + P9_TERROR = 106, + P9_RERROR, + P9_TFLUSH = 108, + P9_RFLUSH, + P9_TWALK = 110, + P9_RWALK, + P9_TOPEN = 112, + P9_ROPEN, + P9_TCREATE = 114, + P9_RCREATE, + P9_TREAD = 116, + P9_RREAD, + P9_TWRITE = 118, + P9_RWRITE, + P9_TCLUNK = 120, + P9_RCLUNK, + P9_TREMOVE = 122, + P9_RREMOVE, + P9_TSTAT = 124, + P9_RSTAT, + P9_TWSTAT = 126, + P9_RWSTAT, +}; + + +/* qid.types */ +enum { + P9_QTDIR = 0x80, + P9_QTAPPEND = 0x40, + P9_QTEXCL = 0x20, + P9_QTMOUNT = 0x10, + P9_QTAUTH = 0x08, + P9_QTTMP = 0x04, + P9_QTSYMLINK = 0x02, + P9_QTLINK = 0x01, + P9_QTFILE = 0x00, +}; + +enum p9_proto_version { + V9FS_PROTO_2000U = 0x01, + V9FS_PROTO_2000L = 0x02, +}; + +#define P9_NOTAG (u16)(~0) +#define P9_NOFID (u32)(~0) +#define P9_MAXWELEM 16 + +#define FID_REFERENCED 0x1 +#define FID_NON_RECLAIMABLE 0x2 +static inline char *rpath(FsContext *ctx, const char *path) +{ + return g_strdup_printf("%s/%s", ctx->fs_root, path); +} + +/* + * ample room for Twrite/Rread header + * size[4] Tread/Twrite tag[2] fid[4] offset[8] count[4] + */ +#define P9_IOHDRSZ 24 + +typedef struct V9fsPDU V9fsPDU; +struct V9fsState; + +struct V9fsPDU +{ + uint32_t size; + uint16_t tag; + uint8_t id; + uint8_t cancelled; + CoQueue complete; + struct V9fsState *s; + QLIST_ENTRY(V9fsPDU) next; + uint32_t idx; +}; + + +/* FIXME + * 1) change user needs to set groups and stuff + */ + +#define MAX_REQ 128 +#define MAX_TAG_LEN 32 + +#define BUG_ON(cond) assert(!(cond)) + +typedef struct V9fsFidState V9fsFidState; + +enum { + P9_FID_NONE = 0, + P9_FID_FILE, + P9_FID_DIR, + P9_FID_XATTR, +}; + +typedef struct V9fsConf +{ + /* tag name for the device */ + char *tag; + char *fsdev_id; +} V9fsConf; + +typedef struct V9fsXattr +{ + int64_t copied_len; + int64_t len; + void *value; + V9fsString name; + int flags; +} V9fsXattr; + +/* + * Filled by fs driver on open and other + * calls. + */ +union V9fsFidOpenState { + int fd; + DIR *dir; + V9fsXattr xattr; + /* + * private pointer for fs drivers, that + * have its own internal representation of + * open files. + */ + void *private; +}; + +struct V9fsFidState +{ + int fid_type; + int32_t fid; + V9fsPath path; + V9fsFidOpenState fs; + V9fsFidOpenState fs_reclaim; + int flags; + int open_flags; + uid_t uid; + int ref; + int clunked; + V9fsFidState *next; + V9fsFidState *rclm_lst; +}; + +typedef struct V9fsState +{ + QLIST_HEAD(, V9fsPDU) free_list; + QLIST_HEAD(, V9fsPDU) active_list; + V9fsFidState *fid_list; + FileOperations *ops; + FsContext ctx; + char *tag; + enum p9_proto_version proto_version; + int32_t msize; + /* + * lock ensuring atomic path update + * on rename. + */ + CoRwlock rename_lock; + int32_t root_fid; + Error *migration_blocker; + V9fsConf fsconf; +} V9fsState; + +/* 9p2000.L open flags */ +#define P9_DOTL_RDONLY 00000000 +#define P9_DOTL_WRONLY 00000001 +#define P9_DOTL_RDWR 00000002 +#define P9_DOTL_NOACCESS 00000003 +#define P9_DOTL_CREATE 00000100 +#define P9_DOTL_EXCL 00000200 +#define P9_DOTL_NOCTTY 00000400 +#define P9_DOTL_TRUNC 00001000 +#define P9_DOTL_APPEND 00002000 +#define P9_DOTL_NONBLOCK 00004000 +#define P9_DOTL_DSYNC 00010000 +#define P9_DOTL_FASYNC 00020000 +#define P9_DOTL_DIRECT 00040000 +#define P9_DOTL_LARGEFILE 00100000 +#define P9_DOTL_DIRECTORY 00200000 +#define P9_DOTL_NOFOLLOW 00400000 +#define P9_DOTL_NOATIME 01000000 +#define P9_DOTL_CLOEXEC 02000000 +#define P9_DOTL_SYNC 04000000 + +/* 9p2000.L at flags */ +#define P9_DOTL_AT_REMOVEDIR 0x200 + +/* 9P2000.L lock type */ +#define P9_LOCK_TYPE_RDLCK 0 +#define P9_LOCK_TYPE_WRLCK 1 +#define P9_LOCK_TYPE_UNLCK 2 + +#define P9_LOCK_SUCCESS 0 +#define P9_LOCK_BLOCKED 1 +#define P9_LOCK_ERROR 2 +#define P9_LOCK_GRACE 3 + +#define P9_LOCK_FLAGS_BLOCK 1 +#define P9_LOCK_FLAGS_RECLAIM 2 + +typedef struct V9fsFlock +{ + uint8_t type; + uint32_t flags; + uint64_t start; /* absolute offset */ + uint64_t length; + uint32_t proc_id; + V9fsString client_id; +} V9fsFlock; + +typedef struct V9fsGetlock +{ + uint8_t type; + uint64_t start; /* absolute offset */ + uint64_t length; + uint32_t proc_id; + V9fsString client_id; +} V9fsGetlock; + +extern int open_fd_hw; +extern int total_open_fd; + +static inline void v9fs_path_write_lock(V9fsState *s) +{ + if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { + qemu_co_rwlock_wrlock(&s->rename_lock); + } +} + +static inline void v9fs_path_read_lock(V9fsState *s) +{ + if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { + qemu_co_rwlock_rdlock(&s->rename_lock); + } +} + +static inline void v9fs_path_unlock(V9fsState *s) +{ + if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { + qemu_co_rwlock_unlock(&s->rename_lock); + } +} + +static inline uint8_t v9fs_request_cancelled(V9fsPDU *pdu) +{ + return pdu->cancelled; +} + +extern void v9fs_reclaim_fd(V9fsPDU *pdu); +extern void v9fs_path_init(V9fsPath *path); +extern void v9fs_path_free(V9fsPath *path); +extern void v9fs_path_copy(V9fsPath *lhs, V9fsPath *rhs); +extern int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath, + const char *name, V9fsPath *path); +extern int v9fs_device_realize_common(V9fsState *s, Error **errp); +extern void v9fs_device_unrealize_common(V9fsState *s, Error **errp); + +ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...); +ssize_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...); +V9fsPDU *pdu_alloc(V9fsState *s); +void pdu_free(V9fsPDU *pdu); +void pdu_submit(V9fsPDU *pdu); + +#endif diff --git a/qemu/hw/9pfs/Makefile.objs b/qemu/hw/9pfs/Makefile.objs index 1e9b595cb..da0ae0cfd 100644 --- a/qemu/hw/9pfs/Makefile.objs +++ b/qemu/hw/9pfs/Makefile.objs @@ -1,9 +1,9 @@ -common-obj-y = virtio-9p.o -common-obj-y += virtio-9p-local.o virtio-9p-xattr.o -common-obj-y += virtio-9p-xattr-user.o virtio-9p-posix-acl.o -common-obj-y += virtio-9p-coth.o cofs.o codir.o cofile.o -common-obj-y += coxattr.o virtio-9p-synth.o -common-obj-$(CONFIG_OPEN_BY_HANDLE) += virtio-9p-handle.o -common-obj-y += virtio-9p-proxy.o +common-obj-y = 9p.o +common-obj-y += 9p-local.o 9p-xattr.o +common-obj-y += 9p-xattr-user.o 9p-posix-acl.o +common-obj-y += coth.o cofs.o codir.o cofile.o +common-obj-y += coxattr.o 9p-synth.o +common-obj-$(CONFIG_OPEN_BY_HANDLE) += 9p-handle.o +common-obj-y += 9p-proxy.o obj-y += virtio-9p-device.o diff --git a/qemu/hw/9pfs/codir.c b/qemu/hw/9pfs/codir.c index 65ad3298b..91df7f7a7 100644 --- a/qemu/hw/9pfs/codir.c +++ b/qemu/hw/9pfs/codir.c @@ -12,10 +12,11 @@ * */ +#include "qemu/osdep.h" #include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" -#include "block/coroutine.h" -#include "virtio-9p-coth.h" +#include "qemu/coroutine.h" +#include "coth.h" int v9fs_co_readdir_r(V9fsPDU *pdu, V9fsFidState *fidp, struct dirent *dent, struct dirent **result) diff --git a/qemu/hw/9pfs/cofile.c b/qemu/hw/9pfs/cofile.c index 2efebf357..293483e0c 100644 --- a/qemu/hw/9pfs/cofile.c +++ b/qemu/hw/9pfs/cofile.c @@ -12,10 +12,11 @@ * */ +#include "qemu/osdep.h" #include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" -#include "block/coroutine.h" -#include "virtio-9p-coth.h" +#include "qemu/coroutine.h" +#include "coth.h" int v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t st_mode, V9fsStatDotl *v9stat) diff --git a/qemu/hw/9pfs/cofs.c b/qemu/hw/9pfs/cofs.c index 42ee614e2..18c81cb3d 100644 --- a/qemu/hw/9pfs/cofs.c +++ b/qemu/hw/9pfs/cofs.c @@ -12,10 +12,11 @@ * */ +#include "qemu/osdep.h" #include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" -#include "block/coroutine.h" -#include "virtio-9p-coth.h" +#include "qemu/coroutine.h" +#include "coth.h" static ssize_t __readlink(V9fsState *s, V9fsPath *path, V9fsString *buf) { diff --git a/qemu/hw/9pfs/coth.c b/qemu/hw/9pfs/coth.c new file mode 100644 index 000000000..464293ef2 --- /dev/null +++ b/qemu/hw/9pfs/coth.c @@ -0,0 +1,42 @@ +/* + * 9p backend + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Harsh Prateek Bora + * Venkateswararao Jujjuri(JV) + * + * 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 "qemu-common.h" +#include "block/thread-pool.h" +#include "qemu/coroutine.h" +#include "qemu/main-loop.h" +#include "coth.h" + +/* Called from QEMU I/O thread. */ +static void coroutine_enter_cb(void *opaque, int ret) +{ + Coroutine *co = opaque; + qemu_coroutine_enter(co, NULL); +} + +/* Called from worker thread. */ +static int coroutine_enter_func(void *arg) +{ + Coroutine *co = arg; + qemu_coroutine_enter(co, NULL); + return 0; +} + +void co_run_in_worker_bh(void *opaque) +{ + Coroutine *co = opaque; + thread_pool_submit_aio(aio_get_thread_pool(qemu_get_aio_context()), + coroutine_enter_func, co, coroutine_enter_cb, co); +} diff --git a/qemu/hw/9pfs/coth.h b/qemu/hw/9pfs/coth.h new file mode 100644 index 000000000..209fc6a9a --- /dev/null +++ b/qemu/hw/9pfs/coth.h @@ -0,0 +1,99 @@ +/* + * 9p backend + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Harsh Prateek Bora + * Venkateswararao Jujjuri(JV) + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef _QEMU_9P_COTH_H +#define _QEMU_9P_COTH_H + +#include "qemu/thread.h" +#include "qemu/coroutine.h" +#include "virtio-9p.h" + +/* + * we want to use bottom half because we want to make sure the below + * sequence of events. + * + * 1. Yield the coroutine in the QEMU thread. + * 2. Submit the coroutine to a worker thread. + * 3. Enter the coroutine in the worker thread. + * we cannot swap step 1 and 2, because that would imply worker thread + * can enter coroutine while step1 is still running + */ +#define v9fs_co_run_in_worker(code_block) \ + do { \ + QEMUBH *co_bh; \ + co_bh = qemu_bh_new(co_run_in_worker_bh, \ + qemu_coroutine_self()); \ + qemu_bh_schedule(co_bh); \ + /* \ + * yield in qemu thread and re-enter back \ + * in worker thread \ + */ \ + qemu_coroutine_yield(); \ + qemu_bh_delete(co_bh); \ + code_block; \ + /* re-enter back to qemu thread */ \ + qemu_coroutine_yield(); \ + } while (0) + +extern void co_run_in_worker_bh(void *); +extern int v9fs_init_worker_threads(void); +extern int v9fs_co_readlink(V9fsPDU *, V9fsPath *, V9fsString *); +extern int v9fs_co_readdir_r(V9fsPDU *, V9fsFidState *, + struct dirent *, struct dirent **result); +extern off_t v9fs_co_telldir(V9fsPDU *, V9fsFidState *); +extern void v9fs_co_seekdir(V9fsPDU *, V9fsFidState *, off_t); +extern void v9fs_co_rewinddir(V9fsPDU *, V9fsFidState *); +extern int v9fs_co_statfs(V9fsPDU *, V9fsPath *, struct statfs *); +extern int v9fs_co_lstat(V9fsPDU *, V9fsPath *, struct stat *); +extern int v9fs_co_chmod(V9fsPDU *, V9fsPath *, mode_t); +extern int v9fs_co_utimensat(V9fsPDU *, V9fsPath *, struct timespec [2]); +extern int v9fs_co_chown(V9fsPDU *, V9fsPath *, uid_t, gid_t); +extern int v9fs_co_truncate(V9fsPDU *, V9fsPath *, off_t); +extern int v9fs_co_llistxattr(V9fsPDU *, V9fsPath *, void *, size_t); +extern int v9fs_co_lgetxattr(V9fsPDU *, V9fsPath *, + V9fsString *, void *, size_t); +extern int v9fs_co_mknod(V9fsPDU *, V9fsFidState *, V9fsString *, uid_t, + gid_t, dev_t, mode_t, struct stat *); +extern int v9fs_co_mkdir(V9fsPDU *, V9fsFidState *, V9fsString *, + mode_t, uid_t, gid_t, struct stat *); +extern int v9fs_co_remove(V9fsPDU *, V9fsPath *); +extern int v9fs_co_rename(V9fsPDU *, V9fsPath *, V9fsPath *); +extern int v9fs_co_unlinkat(V9fsPDU *, V9fsPath *, V9fsString *, int flags); +extern int v9fs_co_renameat(V9fsPDU *, V9fsPath *, V9fsString *, + V9fsPath *, V9fsString *); +extern int v9fs_co_fstat(V9fsPDU *, V9fsFidState *, struct stat *); +extern int v9fs_co_opendir(V9fsPDU *, V9fsFidState *); +extern int v9fs_co_open(V9fsPDU *, V9fsFidState *, int); +extern int v9fs_co_open2(V9fsPDU *, V9fsFidState *, V9fsString *, + gid_t, int, int, struct stat *); +extern int v9fs_co_lsetxattr(V9fsPDU *, V9fsPath *, V9fsString *, + void *, size_t, int); +extern int v9fs_co_lremovexattr(V9fsPDU *, V9fsPath *, V9fsString *); +extern int v9fs_co_closedir(V9fsPDU *, V9fsFidOpenState *); +extern int v9fs_co_close(V9fsPDU *, V9fsFidOpenState *); +extern int v9fs_co_fsync(V9fsPDU *, V9fsFidState *, int); +extern int v9fs_co_symlink(V9fsPDU *, V9fsFidState *, V9fsString *, + const char *, gid_t, struct stat *); +extern int v9fs_co_link(V9fsPDU *, V9fsFidState *, + V9fsFidState *, V9fsString *); +extern int v9fs_co_pwritev(V9fsPDU *, V9fsFidState *, + struct iovec *, int, int64_t); +extern int v9fs_co_preadv(V9fsPDU *, V9fsFidState *, + struct iovec *, int, int64_t); +extern int v9fs_co_name_to_path(V9fsPDU *, V9fsPath *, + const char *, V9fsPath *); +extern int v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t, + V9fsStatDotl *v9stat); + +#endif diff --git a/qemu/hw/9pfs/coxattr.c b/qemu/hw/9pfs/coxattr.c index 18ee08df0..6ad96ea9f 100644 --- a/qemu/hw/9pfs/coxattr.c +++ b/qemu/hw/9pfs/coxattr.c @@ -12,10 +12,11 @@ * */ +#include "qemu/osdep.h" #include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" -#include "block/coroutine.h" -#include "virtio-9p-coth.h" +#include "qemu/coroutine.h" +#include "coth.h" int v9fs_co_llistxattr(V9fsPDU *pdu, V9fsPath *path, void *value, size_t size) { diff --git a/qemu/hw/9pfs/virtio-9p-coth.c b/qemu/hw/9pfs/virtio-9p-coth.c deleted file mode 100644 index 8185c533c..000000000 --- a/qemu/hw/9pfs/virtio-9p-coth.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Virtio 9p backend - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Harsh Prateek Bora - * Venkateswararao Jujjuri(JV) - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include "fsdev/qemu-fsdev.h" -#include "qemu/thread.h" -#include "qemu/event_notifier.h" -#include "block/coroutine.h" -#include "virtio-9p-coth.h" - -/* v9fs glib thread pool */ -static V9fsThPool v9fs_pool; - -void co_run_in_worker_bh(void *opaque) -{ - Coroutine *co = opaque; - g_thread_pool_push(v9fs_pool.pool, co, NULL); -} - -static void v9fs_qemu_process_req_done(EventNotifier *e) -{ - Coroutine *co; - - event_notifier_test_and_clear(e); - - while ((co = g_async_queue_try_pop(v9fs_pool.completed)) != NULL) { - qemu_coroutine_enter(co, NULL); - } -} - -static void v9fs_thread_routine(gpointer data, gpointer user_data) -{ - Coroutine *co = data; - - qemu_coroutine_enter(co, NULL); - - g_async_queue_push(v9fs_pool.completed, co); - - event_notifier_set(&v9fs_pool.e); -} - -int v9fs_init_worker_threads(void) -{ - int ret = 0; - V9fsThPool *p = &v9fs_pool; - sigset_t set, oldset; - - sigfillset(&set); - /* Leave signal handling to the iothread. */ - pthread_sigmask(SIG_SETMASK, &set, &oldset); - - p->pool = g_thread_pool_new(v9fs_thread_routine, p, -1, FALSE, NULL); - if (!p->pool) { - ret = -1; - goto err_out; - } - p->completed = g_async_queue_new(); - if (!p->completed) { - /* - * We are going to terminate. - * So don't worry about cleanup - */ - ret = -1; - goto err_out; - } - event_notifier_init(&p->e, 0); - - event_notifier_set_handler(&p->e, v9fs_qemu_process_req_done); -err_out: - pthread_sigmask(SIG_SETMASK, &oldset, NULL); - return ret; -} diff --git a/qemu/hw/9pfs/virtio-9p-coth.h b/qemu/hw/9pfs/virtio-9p-coth.h deleted file mode 100644 index 4f51b250d..000000000 --- a/qemu/hw/9pfs/virtio-9p-coth.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Virtio 9p backend - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Harsh Prateek Bora - * Venkateswararao Jujjuri(JV) - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#ifndef _QEMU_VIRTIO_9P_COTH_H -#define _QEMU_VIRTIO_9P_COTH_H - -#include "qemu/thread.h" -#include "block/coroutine.h" -#include "virtio-9p.h" -#include - -typedef struct V9fsThPool { - EventNotifier e; - - GThreadPool *pool; - GAsyncQueue *completed; -} V9fsThPool; - -/* - * we want to use bottom half because we want to make sure the below - * sequence of events. - * - * 1. Yield the coroutine in the QEMU thread. - * 2. Submit the coroutine to a worker thread. - * 3. Enter the coroutine in the worker thread. - * we cannot swap step 1 and 2, because that would imply worker thread - * can enter coroutine while step1 is still running - */ -#define v9fs_co_run_in_worker(code_block) \ - do { \ - QEMUBH *co_bh; \ - co_bh = qemu_bh_new(co_run_in_worker_bh, \ - qemu_coroutine_self()); \ - qemu_bh_schedule(co_bh); \ - /* \ - * yield in qemu thread and re-enter back \ - * in glib worker thread \ - */ \ - qemu_coroutine_yield(); \ - qemu_bh_delete(co_bh); \ - code_block; \ - /* re-enter back to qemu thread */ \ - qemu_coroutine_yield(); \ - } while (0) - -extern void co_run_in_worker_bh(void *); -extern int v9fs_init_worker_threads(void); -extern int v9fs_co_readlink(V9fsPDU *, V9fsPath *, V9fsString *); -extern int v9fs_co_readdir_r(V9fsPDU *, V9fsFidState *, - struct dirent *, struct dirent **result); -extern off_t v9fs_co_telldir(V9fsPDU *, V9fsFidState *); -extern void v9fs_co_seekdir(V9fsPDU *, V9fsFidState *, off_t); -extern void v9fs_co_rewinddir(V9fsPDU *, V9fsFidState *); -extern int v9fs_co_statfs(V9fsPDU *, V9fsPath *, struct statfs *); -extern int v9fs_co_lstat(V9fsPDU *, V9fsPath *, struct stat *); -extern int v9fs_co_chmod(V9fsPDU *, V9fsPath *, mode_t); -extern int v9fs_co_utimensat(V9fsPDU *, V9fsPath *, struct timespec [2]); -extern int v9fs_co_chown(V9fsPDU *, V9fsPath *, uid_t, gid_t); -extern int v9fs_co_truncate(V9fsPDU *, V9fsPath *, off_t); -extern int v9fs_co_llistxattr(V9fsPDU *, V9fsPath *, void *, size_t); -extern int v9fs_co_lgetxattr(V9fsPDU *, V9fsPath *, - V9fsString *, void *, size_t); -extern int v9fs_co_mknod(V9fsPDU *, V9fsFidState *, V9fsString *, uid_t, - gid_t, dev_t, mode_t, struct stat *); -extern int v9fs_co_mkdir(V9fsPDU *, V9fsFidState *, V9fsString *, - mode_t, uid_t, gid_t, struct stat *); -extern int v9fs_co_remove(V9fsPDU *, V9fsPath *); -extern int v9fs_co_rename(V9fsPDU *, V9fsPath *, V9fsPath *); -extern int v9fs_co_unlinkat(V9fsPDU *, V9fsPath *, V9fsString *, int flags); -extern int v9fs_co_renameat(V9fsPDU *, V9fsPath *, V9fsString *, - V9fsPath *, V9fsString *); -extern int v9fs_co_fstat(V9fsPDU *, V9fsFidState *, struct stat *); -extern int v9fs_co_opendir(V9fsPDU *, V9fsFidState *); -extern int v9fs_co_open(V9fsPDU *, V9fsFidState *, int); -extern int v9fs_co_open2(V9fsPDU *, V9fsFidState *, V9fsString *, - gid_t, int, int, struct stat *); -extern int v9fs_co_lsetxattr(V9fsPDU *, V9fsPath *, V9fsString *, - void *, size_t, int); -extern int v9fs_co_lremovexattr(V9fsPDU *, V9fsPath *, V9fsString *); -extern int v9fs_co_closedir(V9fsPDU *, V9fsFidOpenState *); -extern int v9fs_co_close(V9fsPDU *, V9fsFidOpenState *); -extern int v9fs_co_fsync(V9fsPDU *, V9fsFidState *, int); -extern int v9fs_co_symlink(V9fsPDU *, V9fsFidState *, V9fsString *, - const char *, gid_t, struct stat *); -extern int v9fs_co_link(V9fsPDU *, V9fsFidState *, - V9fsFidState *, V9fsString *); -extern int v9fs_co_pwritev(V9fsPDU *, V9fsFidState *, - struct iovec *, int, int64_t); -extern int v9fs_co_preadv(V9fsPDU *, V9fsFidState *, - struct iovec *, int, int64_t); -extern int v9fs_co_name_to_path(V9fsPDU *, V9fsPath *, - const char *, V9fsPath *); -extern int v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t, - V9fsStatDotl *v9stat); - -#endif diff --git a/qemu/hw/9pfs/virtio-9p-device.c b/qemu/hw/9pfs/virtio-9p-device.c index 93a407c45..a38850ee8 100644 --- a/qemu/hw/9pfs/virtio-9p-device.c +++ b/qemu/hw/9pfs/virtio-9p-device.c @@ -11,15 +11,70 @@ * */ +#include "qemu/osdep.h" #include "hw/virtio/virtio.h" -#include "hw/virtio/virtio-9p.h" #include "hw/i386/pc.h" #include "qemu/sockets.h" #include "virtio-9p.h" #include "fsdev/qemu-fsdev.h" -#include "virtio-9p-xattr.h" -#include "virtio-9p-coth.h" +#include "9p-xattr.h" +#include "coth.h" #include "hw/virtio/virtio-access.h" +#include "qemu/iov.h" + +void virtio_9p_push_and_notify(V9fsPDU *pdu) +{ + V9fsState *s = pdu->s; + V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); + VirtQueueElement *elem = v->elems[pdu->idx]; + + /* push onto queue and notify */ + virtqueue_push(v->vq, elem, pdu->size); + g_free(elem); + v->elems[pdu->idx] = NULL; + + /* FIXME: we should batch these completions */ + virtio_notify(VIRTIO_DEVICE(v), v->vq); +} + +static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq) +{ + V9fsVirtioState *v = (V9fsVirtioState *)vdev; + V9fsState *s = &v->state; + V9fsPDU *pdu; + ssize_t len; + + while ((pdu = pdu_alloc(s))) { + struct { + uint32_t size_le; + uint8_t id; + uint16_t tag_le; + } QEMU_PACKED out; + VirtQueueElement *elem; + + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!elem) { + pdu_free(pdu); + break; + } + + BUG_ON(elem->out_num == 0 || elem->in_num == 0); + QEMU_BUILD_BUG_ON(sizeof out != 7); + + v->elems[pdu->idx] = elem; + len = iov_to_buf(elem->out_sg, elem->out_num, 0, + &out, sizeof out); + BUG_ON(len != sizeof out); + + pdu->size = le32_to_cpu(out.size_le); + + pdu->id = out.id; + pdu->tag = le16_to_cpu(out.tag_le); + + qemu_co_queue_init(&pdu->complete); + pdu_submit(pdu); + } +} static uint64_t virtio_9p_get_features(VirtIODevice *vdev, uint64_t features, Error **errp) @@ -32,117 +87,99 @@ static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config) { int len; struct virtio_9p_config *cfg; - V9fsState *s = VIRTIO_9P(vdev); + V9fsVirtioState *v = VIRTIO_9P(vdev); + V9fsState *s = &v->state; len = strlen(s->tag); cfg = g_malloc0(sizeof(struct virtio_9p_config) + len); virtio_stw_p(vdev, &cfg->tag_len, len); /* We don't copy the terminating null to config space */ memcpy(cfg->tag, s->tag, len); - memcpy(config, cfg, s->config_size); + memcpy(config, cfg, v->config_size); g_free(cfg); } +static void virtio_9p_save(QEMUFile *f, void *opaque) +{ + virtio_save(VIRTIO_DEVICE(opaque), f); +} + +static int virtio_9p_load(QEMUFile *f, void *opaque, int version_id) +{ + return virtio_load(VIRTIO_DEVICE(opaque), f, version_id); +} + static void virtio_9p_device_realize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); - V9fsState *s = VIRTIO_9P(dev); - int i, len; - struct stat stat; - FsDriverEntry *fse; - V9fsPath path; - - virtio_init(vdev, "virtio-9p", VIRTIO_ID_9P, - sizeof(struct virtio_9p_config) + MAX_TAG_LEN); - - /* initialize pdu allocator */ - QLIST_INIT(&s->free_list); - QLIST_INIT(&s->active_list); - for (i = 0; i < (MAX_REQ - 1); i++) { - QLIST_INSERT_HEAD(&s->free_list, &s->pdus[i], next); - } + V9fsVirtioState *v = VIRTIO_9P(dev); + V9fsState *s = &v->state; - s->vq = virtio_add_queue(vdev, MAX_REQ, handle_9p_output); + if (v9fs_device_realize_common(s, errp)) { + goto out; + } - v9fs_path_init(&path); + v->config_size = sizeof(struct virtio_9p_config) + strlen(s->fsconf.tag); + virtio_init(vdev, "virtio-9p", VIRTIO_ID_9P, v->config_size); + v->vq = virtio_add_queue(vdev, MAX_REQ, handle_9p_output); + register_savevm(dev, "virtio-9p", -1, 1, virtio_9p_save, virtio_9p_load, v); - fse = get_fsdev_fsentry(s->fsconf.fsdev_id); +out: + return; +} - if (!fse) { - /* We don't have a fsdev identified by fsdev_id */ - error_setg(errp, "Virtio-9p device couldn't find fsdev with the " - "id = %s", - s->fsconf.fsdev_id ? s->fsconf.fsdev_id : "NULL"); - goto out; - } +static void virtio_9p_device_unrealize(DeviceState *dev, Error **errp) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + V9fsVirtioState *v = VIRTIO_9P(dev); + V9fsState *s = &v->state; - if (!s->fsconf.tag) { - /* we haven't specified a mount_tag */ - error_setg(errp, "fsdev with id %s needs mount_tag arguments", - s->fsconf.fsdev_id); - goto out; - } + virtio_cleanup(vdev); + unregister_savevm(dev, "virtio-9p", v); + v9fs_device_unrealize_common(s, errp); +} - s->ctx.export_flags = fse->export_flags; - s->ctx.fs_root = g_strdup(fse->path); - s->ctx.exops.get_st_gen = NULL; - len = strlen(s->fsconf.tag); - if (len > MAX_TAG_LEN - 1) { - error_setg(errp, "mount tag '%s' (%d bytes) is longer than " - "maximum (%d bytes)", s->fsconf.tag, len, MAX_TAG_LEN - 1); - goto out; - } +ssize_t virtio_pdu_vmarshal(V9fsPDU *pdu, size_t offset, + const char *fmt, va_list ap) +{ + V9fsState *s = pdu->s; + V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); + VirtQueueElement *elem = v->elems[pdu->idx]; - s->tag = g_strdup(s->fsconf.tag); - s->ctx.uid = -1; + return v9fs_iov_vmarshal(elem->in_sg, elem->in_num, offset, 1, fmt, ap); +} - s->ops = fse->ops; - s->config_size = sizeof(struct virtio_9p_config) + len; - s->fid_list = NULL; - qemu_co_rwlock_init(&s->rename_lock); +ssize_t virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset, + const char *fmt, va_list ap) +{ + V9fsState *s = pdu->s; + V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); + VirtQueueElement *elem = v->elems[pdu->idx]; - if (s->ops->init(&s->ctx) < 0) { - error_setg(errp, "Virtio-9p Failed to initialize fs-driver with id:%s" - " and export path:%s", s->fsconf.fsdev_id, s->ctx.fs_root); - goto out; - } - if (v9fs_init_worker_threads() < 0) { - error_setg(errp, "worker thread initialization failed"); - goto out; - } + return v9fs_iov_vunmarshal(elem->out_sg, elem->out_num, offset, 1, fmt, ap); +} - /* - * Check details of export path, We need to use fs driver - * call back to do that. Since we are in the init path, we don't - * use co-routines here. - */ - if (s->ops->name_to_path(&s->ctx, NULL, "/", &path) < 0) { - error_setg(errp, - "error in converting name to path %s", strerror(errno)); - goto out; - } - if (s->ops->lstat(&s->ctx, &path, &stat)) { - error_setg(errp, "share path %s does not exist", fse->path); - goto out; - } else if (!S_ISDIR(stat.st_mode)) { - error_setg(errp, "share path %s is not a directory", fse->path); - goto out; +void virtio_init_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, + unsigned int *pniov, bool is_write) +{ + V9fsState *s = pdu->s; + V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); + VirtQueueElement *elem = v->elems[pdu->idx]; + + if (is_write) { + *piov = elem->out_sg; + *pniov = elem->out_num; + } else { + *piov = elem->in_sg; + *pniov = elem->in_num; } - v9fs_path_free(&path); - - return; -out: - g_free(s->ctx.fs_root); - g_free(s->tag); - virtio_cleanup(vdev); - v9fs_path_free(&path); } /* virtio-9p device */ static Property virtio_9p_properties[] = { - DEFINE_PROP_STRING("mount_tag", V9fsState, fsconf.tag), - DEFINE_PROP_STRING("fsdev", V9fsState, fsconf.fsdev_id), + DEFINE_PROP_STRING("mount_tag", V9fsVirtioState, state.fsconf.tag), + DEFINE_PROP_STRING("fsdev", V9fsVirtioState, state.fsconf.fsdev_id), DEFINE_PROP_END_OF_LIST(), }; @@ -154,6 +191,7 @@ static void virtio_9p_class_init(ObjectClass *klass, void *data) dc->props = virtio_9p_properties; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); vdc->realize = virtio_9p_device_realize; + vdc->unrealize = virtio_9p_device_unrealize; vdc->get_features = virtio_9p_get_features; vdc->get_config = virtio_9p_get_config; } @@ -161,7 +199,7 @@ static void virtio_9p_class_init(ObjectClass *klass, void *data) static const TypeInfo virtio_device_info = { .name = TYPE_VIRTIO_9P, .parent = TYPE_VIRTIO_DEVICE, - .instance_size = sizeof(V9fsState), + .instance_size = sizeof(V9fsVirtioState), .class_init = virtio_9p_class_init, }; diff --git a/qemu/hw/9pfs/virtio-9p-handle.c b/qemu/hw/9pfs/virtio-9p-handle.c deleted file mode 100644 index 13eabb98a..000000000 --- a/qemu/hw/9pfs/virtio-9p-handle.c +++ /dev/null @@ -1,708 +0,0 @@ -/* - * Virtio 9p handle callback - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Aneesh Kumar K.V - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include "hw/virtio/virtio.h" -#include "virtio-9p.h" -#include "virtio-9p-xattr.h" -#include -#include -#include -#include -#include -#include "qemu/xattr.h" -#include -#include -#ifdef CONFIG_LINUX_MAGIC_H -#include -#endif -#include - -#ifndef XFS_SUPER_MAGIC -#define XFS_SUPER_MAGIC 0x58465342 -#endif -#ifndef EXT2_SUPER_MAGIC -#define EXT2_SUPER_MAGIC 0xEF53 -#endif -#ifndef REISERFS_SUPER_MAGIC -#define REISERFS_SUPER_MAGIC 0x52654973 -#endif -#ifndef BTRFS_SUPER_MAGIC -#define BTRFS_SUPER_MAGIC 0x9123683E -#endif - -struct handle_data { - int mountfd; - int handle_bytes; -}; - -static inline int name_to_handle(int dirfd, const char *name, - struct file_handle *fh, int *mnt_id, int flags) -{ - return name_to_handle_at(dirfd, name, fh, mnt_id, flags); -} - -static inline int open_by_handle(int mountfd, const char *fh, int flags) -{ - return open_by_handle_at(mountfd, (struct file_handle *)fh, flags); -} - -static int handle_update_file_cred(int dirfd, const char *name, FsCred *credp) -{ - int fd, ret; - fd = openat(dirfd, name, O_NONBLOCK | O_NOFOLLOW); - if (fd < 0) { - return fd; - } - ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH); - if (ret < 0) { - goto err_out; - } - ret = fchmod(fd, credp->fc_mode & 07777); -err_out: - close(fd); - return ret; -} - - -static int handle_lstat(FsContext *fs_ctx, V9fsPath *fs_path, - struct stat *stbuf) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)fs_ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_PATH); - if (fd < 0) { - return fd; - } - ret = fstatat(fd, "", stbuf, AT_EMPTY_PATH); - close(fd); - return ret; -} - -static ssize_t handle_readlink(FsContext *fs_ctx, V9fsPath *fs_path, - char *buf, size_t bufsz) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)fs_ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_PATH); - if (fd < 0) { - return fd; - } - ret = readlinkat(fd, "", buf, bufsz); - close(fd); - return ret; -} - -static int handle_close(FsContext *ctx, V9fsFidOpenState *fs) -{ - return close(fs->fd); -} - -static int handle_closedir(FsContext *ctx, V9fsFidOpenState *fs) -{ - return closedir(fs->dir); -} - -static int handle_open(FsContext *ctx, V9fsPath *fs_path, - int flags, V9fsFidOpenState *fs) -{ - struct handle_data *data = (struct handle_data *)ctx->private; - - fs->fd = open_by_handle(data->mountfd, fs_path->data, flags); - return fs->fd; -} - -static int handle_opendir(FsContext *ctx, - V9fsPath *fs_path, V9fsFidOpenState *fs) -{ - int ret; - ret = handle_open(ctx, fs_path, O_DIRECTORY, fs); - if (ret < 0) { - return -1; - } - fs->dir = fdopendir(ret); - if (!fs->dir) { - return -1; - } - return 0; -} - -static void handle_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) -{ - rewinddir(fs->dir); -} - -static off_t handle_telldir(FsContext *ctx, V9fsFidOpenState *fs) -{ - return telldir(fs->dir); -} - -static int handle_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, - struct dirent *entry, - struct dirent **result) -{ - return readdir_r(fs->dir, entry, result); -} - -static void handle_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) -{ - seekdir(fs->dir, off); -} - -static ssize_t handle_preadv(FsContext *ctx, V9fsFidOpenState *fs, - const struct iovec *iov, - int iovcnt, off_t offset) -{ -#ifdef CONFIG_PREADV - return preadv(fs->fd, iov, iovcnt, offset); -#else - int err = lseek(fs->fd, offset, SEEK_SET); - if (err == -1) { - return err; - } else { - return readv(fs->fd, iov, iovcnt); - } -#endif -} - -static ssize_t handle_pwritev(FsContext *ctx, V9fsFidOpenState *fs, - const struct iovec *iov, - int iovcnt, off_t offset) -{ - ssize_t ret; -#ifdef CONFIG_PREADV - ret = pwritev(fs->fd, iov, iovcnt, offset); -#else - int err = lseek(fs->fd, offset, SEEK_SET); - if (err == -1) { - return err; - } else { - ret = writev(fs->fd, iov, iovcnt); - } -#endif -#ifdef CONFIG_SYNC_FILE_RANGE - if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { - /* - * Initiate a writeback. This is not a data integrity sync. - * We want to ensure that we don't leave dirty pages in the cache - * after write when writeout=immediate is sepcified. - */ - sync_file_range(fs->fd, offset, ret, - SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); - } -#endif - return ret; -} - -static int handle_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)fs_ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); - if (fd < 0) { - return fd; - } - ret = fchmod(fd, credp->fc_mode); - close(fd); - return ret; -} - -static int handle_mknod(FsContext *fs_ctx, V9fsPath *dir_path, - const char *name, FsCred *credp) -{ - int dirfd, ret; - struct handle_data *data = (struct handle_data *)fs_ctx->private; - - dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); - if (dirfd < 0) { - return dirfd; - } - ret = mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev); - if (!ret) { - ret = handle_update_file_cred(dirfd, name, credp); - } - close(dirfd); - return ret; -} - -static int handle_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, - const char *name, FsCred *credp) -{ - int dirfd, ret; - struct handle_data *data = (struct handle_data *)fs_ctx->private; - - dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); - if (dirfd < 0) { - return dirfd; - } - ret = mkdirat(dirfd, name, credp->fc_mode); - if (!ret) { - ret = handle_update_file_cred(dirfd, name, credp); - } - close(dirfd); - return ret; -} - -static int handle_fstat(FsContext *fs_ctx, int fid_type, - V9fsFidOpenState *fs, struct stat *stbuf) -{ - int fd; - - if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir); - } else { - fd = fs->fd; - } - return fstat(fd, stbuf); -} - -static int handle_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, - int flags, FsCred *credp, V9fsFidOpenState *fs) -{ - int ret; - int dirfd, fd; - struct handle_data *data = (struct handle_data *)fs_ctx->private; - - dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); - if (dirfd < 0) { - return dirfd; - } - fd = openat(dirfd, name, flags | O_NOFOLLOW, credp->fc_mode); - if (fd >= 0) { - ret = handle_update_file_cred(dirfd, name, credp); - if (ret < 0) { - close(fd); - fd = ret; - } else { - fs->fd = fd; - } - } - close(dirfd); - return fd; -} - - -static int handle_symlink(FsContext *fs_ctx, const char *oldpath, - V9fsPath *dir_path, const char *name, FsCred *credp) -{ - int fd, dirfd, ret; - struct handle_data *data = (struct handle_data *)fs_ctx->private; - - dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); - if (dirfd < 0) { - return dirfd; - } - ret = symlinkat(oldpath, dirfd, name); - if (!ret) { - fd = openat(dirfd, name, O_PATH | O_NOFOLLOW); - if (fd < 0) { - ret = fd; - goto err_out; - } - ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH); - close(fd); - } -err_out: - close(dirfd); - return ret; -} - -static int handle_link(FsContext *ctx, V9fsPath *oldpath, - V9fsPath *dirpath, const char *name) -{ - int oldfd, newdirfd, ret; - struct handle_data *data = (struct handle_data *)ctx->private; - - oldfd = open_by_handle(data->mountfd, oldpath->data, O_PATH); - if (oldfd < 0) { - return oldfd; - } - newdirfd = open_by_handle(data->mountfd, dirpath->data, O_PATH); - if (newdirfd < 0) { - close(oldfd); - return newdirfd; - } - ret = linkat(oldfd, "", newdirfd, name, AT_EMPTY_PATH); - close(newdirfd); - close(oldfd); - return ret; -} - -static int handle_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK | O_WRONLY); - if (fd < 0) { - return fd; - } - ret = ftruncate(fd, size); - close(fd); - return ret; -} - -static int handle_rename(FsContext *ctx, const char *oldpath, - const char *newpath) -{ - errno = EOPNOTSUPP; - return -1; -} - -static int handle_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)fs_ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_PATH); - if (fd < 0) { - return fd; - } - ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH); - close(fd); - return ret; -} - -static int handle_utimensat(FsContext *ctx, V9fsPath *fs_path, - const struct timespec *buf) -{ - int ret; -#ifdef CONFIG_UTIMENSAT - int fd; - struct handle_data *data = (struct handle_data *)ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); - if (fd < 0) { - return fd; - } - ret = futimens(fd, buf); - close(fd); -#else - ret = -1; - errno = ENOSYS; -#endif - return ret; -} - -static int handle_remove(FsContext *ctx, const char *path) -{ - errno = EOPNOTSUPP; - return -1; -} - -static int handle_fsync(FsContext *ctx, int fid_type, - V9fsFidOpenState *fs, int datasync) -{ - int fd; - - if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir); - } else { - fd = fs->fd; - } - - if (datasync) { - return qemu_fdatasync(fd); - } else { - return fsync(fd); - } -} - -static int handle_statfs(FsContext *ctx, V9fsPath *fs_path, - struct statfs *stbuf) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); - if (fd < 0) { - return fd; - } - ret = fstatfs(fd, stbuf); - close(fd); - return ret; -} - -static ssize_t handle_lgetxattr(FsContext *ctx, V9fsPath *fs_path, - const char *name, void *value, size_t size) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); - if (fd < 0) { - return fd; - } - ret = fgetxattr(fd, name, value, size); - close(fd); - return ret; -} - -static ssize_t handle_llistxattr(FsContext *ctx, V9fsPath *fs_path, - void *value, size_t size) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); - if (fd < 0) { - return fd; - } - ret = flistxattr(fd, value, size); - close(fd); - return ret; -} - -static int handle_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, - void *value, size_t size, int flags) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); - if (fd < 0) { - return fd; - } - ret = fsetxattr(fd, name, value, size, flags); - close(fd); - return ret; -} - -static int handle_lremovexattr(FsContext *ctx, V9fsPath *fs_path, - const char *name) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); - if (fd < 0) { - return fd; - } - ret = fremovexattr(fd, name); - close(fd); - return ret; -} - -static int handle_name_to_path(FsContext *ctx, V9fsPath *dir_path, - const char *name, V9fsPath *target) -{ - char *buffer; - struct file_handle *fh; - int dirfd, ret, mnt_id; - struct handle_data *data = (struct handle_data *)ctx->private; - - /* "." and ".." are not allowed */ - if (!strcmp(name, ".") || !strcmp(name, "..")) { - errno = EINVAL; - return -1; - - } - if (dir_path) { - dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); - } else { - /* relative to export root */ - buffer = rpath(ctx, "."); - dirfd = open(buffer, O_DIRECTORY); - g_free(buffer); - } - if (dirfd < 0) { - return dirfd; - } - fh = g_malloc(sizeof(struct file_handle) + data->handle_bytes); - fh->handle_bytes = data->handle_bytes; - /* add a "./" at the beginning of the path */ - buffer = g_strdup_printf("./%s", name); - /* flag = 0 imply don't follow symlink */ - ret = name_to_handle(dirfd, buffer, fh, &mnt_id, 0); - if (!ret) { - target->data = (char *)fh; - target->size = sizeof(struct file_handle) + data->handle_bytes; - } else { - g_free(fh); - } - close(dirfd); - g_free(buffer); - return ret; -} - -static int handle_renameat(FsContext *ctx, V9fsPath *olddir, - const char *old_name, V9fsPath *newdir, - const char *new_name) -{ - int olddirfd, newdirfd, ret; - struct handle_data *data = (struct handle_data *)ctx->private; - - olddirfd = open_by_handle(data->mountfd, olddir->data, O_PATH); - if (olddirfd < 0) { - return olddirfd; - } - newdirfd = open_by_handle(data->mountfd, newdir->data, O_PATH); - if (newdirfd < 0) { - close(olddirfd); - return newdirfd; - } - ret = renameat(olddirfd, old_name, newdirfd, new_name); - close(newdirfd); - close(olddirfd); - return ret; -} - -static int handle_unlinkat(FsContext *ctx, V9fsPath *dir, - const char *name, int flags) -{ - int dirfd, ret; - struct handle_data *data = (struct handle_data *)ctx->private; - int rflags; - - dirfd = open_by_handle(data->mountfd, dir->data, O_PATH); - if (dirfd < 0) { - return dirfd; - } - - rflags = 0; - if (flags & P9_DOTL_AT_REMOVEDIR) { - rflags |= AT_REMOVEDIR; - } - - ret = unlinkat(dirfd, name, rflags); - - close(dirfd); - return ret; -} - -static int handle_ioc_getversion(FsContext *ctx, V9fsPath *path, - mode_t st_mode, uint64_t *st_gen) -{ -#ifdef FS_IOC_GETVERSION - int err; - V9fsFidOpenState fid_open; - - /* - * Do not try to open special files like device nodes, fifos etc - * We can get fd for regular files and directories only - */ - if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { - errno = ENOTTY; - return -1; - } - err = handle_open(ctx, path, O_RDONLY, &fid_open); - if (err < 0) { - return err; - } - err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen); - handle_close(ctx, &fid_open); - return err; -#else - errno = ENOTTY; - return -1; -#endif -} - -static int handle_init(FsContext *ctx) -{ - int ret, mnt_id; - struct statfs stbuf; - struct file_handle fh; - struct handle_data *data = g_malloc(sizeof(struct handle_data)); - - data->mountfd = open(ctx->fs_root, O_DIRECTORY); - if (data->mountfd < 0) { - ret = data->mountfd; - goto err_out; - } - ret = statfs(ctx->fs_root, &stbuf); - if (!ret) { - switch (stbuf.f_type) { - case EXT2_SUPER_MAGIC: - case BTRFS_SUPER_MAGIC: - case REISERFS_SUPER_MAGIC: - case XFS_SUPER_MAGIC: - ctx->exops.get_st_gen = handle_ioc_getversion; - break; - } - } - memset(&fh, 0, sizeof(struct file_handle)); - ret = name_to_handle(data->mountfd, ".", &fh, &mnt_id, 0); - if (ret && errno == EOVERFLOW) { - data->handle_bytes = fh.handle_bytes; - ctx->private = data; - ret = 0; - goto out; - } - /* we got 0 byte handle ? */ - ret = -1; - close(data->mountfd); -err_out: - g_free(data); -out: - return ret; -} - -static int handle_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse) -{ - const char *sec_model = qemu_opt_get(opts, "security_model"); - const char *path = qemu_opt_get(opts, "path"); - - if (sec_model) { - fprintf(stderr, "Invalid argument security_model specified with handle fsdriver\n"); - return -1; - } - - if (!path) { - fprintf(stderr, "fsdev: No path specified.\n"); - return -1; - } - fse->path = g_strdup(path); - return 0; - -} - -FileOperations handle_ops = { - .parse_opts = handle_parse_opts, - .init = handle_init, - .lstat = handle_lstat, - .readlink = handle_readlink, - .close = handle_close, - .closedir = handle_closedir, - .open = handle_open, - .opendir = handle_opendir, - .rewinddir = handle_rewinddir, - .telldir = handle_telldir, - .readdir_r = handle_readdir_r, - .seekdir = handle_seekdir, - .preadv = handle_preadv, - .pwritev = handle_pwritev, - .chmod = handle_chmod, - .mknod = handle_mknod, - .mkdir = handle_mkdir, - .fstat = handle_fstat, - .open2 = handle_open2, - .symlink = handle_symlink, - .link = handle_link, - .truncate = handle_truncate, - .rename = handle_rename, - .chown = handle_chown, - .utimensat = handle_utimensat, - .remove = handle_remove, - .fsync = handle_fsync, - .statfs = handle_statfs, - .lgetxattr = handle_lgetxattr, - .llistxattr = handle_llistxattr, - .lsetxattr = handle_lsetxattr, - .lremovexattr = handle_lremovexattr, - .name_to_path = handle_name_to_path, - .renameat = handle_renameat, - .unlinkat = handle_unlinkat, -}; diff --git a/qemu/hw/9pfs/virtio-9p-local.c b/qemu/hw/9pfs/virtio-9p-local.c deleted file mode 100644 index f1f2e2573..000000000 --- a/qemu/hw/9pfs/virtio-9p-local.c +++ /dev/null @@ -1,1280 +0,0 @@ -/* - * Virtio 9p Posix callback - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include "hw/virtio/virtio.h" -#include "virtio-9p.h" -#include "virtio-9p-xattr.h" -#include "fsdev/qemu-fsdev.h" /* local_ops */ -#include -#include -#include -#include -#include -#include "qemu/xattr.h" -#include -#include -#ifdef CONFIG_LINUX_MAGIC_H -#include -#endif -#include - -#ifndef XFS_SUPER_MAGIC -#define XFS_SUPER_MAGIC 0x58465342 -#endif -#ifndef EXT2_SUPER_MAGIC -#define EXT2_SUPER_MAGIC 0xEF53 -#endif -#ifndef REISERFS_SUPER_MAGIC -#define REISERFS_SUPER_MAGIC 0x52654973 -#endif -#ifndef BTRFS_SUPER_MAGIC -#define BTRFS_SUPER_MAGIC 0x9123683E -#endif - -#define VIRTFS_META_DIR ".virtfs_metadata" - -static char *local_mapped_attr_path(FsContext *ctx, const char *path) -{ - int dirlen; - const char *name = strrchr(path, '/'); - if (name) { - dirlen = name - path; - ++name; - } else { - name = path; - dirlen = 0; - } - return g_strdup_printf("%s/%.*s/%s/%s", ctx->fs_root, - dirlen, path, VIRTFS_META_DIR, name); -} - -static FILE *local_fopen(const char *path, const char *mode) -{ - int fd, o_mode = 0; - FILE *fp; - int flags = O_NOFOLLOW; - /* - * only supports two modes - */ - if (mode[0] == 'r') { - flags |= O_RDONLY; - } else if (mode[0] == 'w') { - flags |= O_WRONLY | O_TRUNC | O_CREAT; - o_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; - } else { - return NULL; - } - fd = open(path, flags, o_mode); - if (fd == -1) { - return NULL; - } - fp = fdopen(fd, mode); - if (!fp) { - close(fd); - } - return fp; -} - -#define ATTR_MAX 100 -static void local_mapped_file_attr(FsContext *ctx, const char *path, - struct stat *stbuf) -{ - FILE *fp; - char buf[ATTR_MAX]; - char *attr_path; - - attr_path = local_mapped_attr_path(ctx, path); - fp = local_fopen(attr_path, "r"); - g_free(attr_path); - if (!fp) { - return; - } - memset(buf, 0, ATTR_MAX); - while (fgets(buf, ATTR_MAX, fp)) { - if (!strncmp(buf, "virtfs.uid", 10)) { - stbuf->st_uid = atoi(buf+11); - } else if (!strncmp(buf, "virtfs.gid", 10)) { - stbuf->st_gid = atoi(buf+11); - } else if (!strncmp(buf, "virtfs.mode", 11)) { - stbuf->st_mode = atoi(buf+12); - } else if (!strncmp(buf, "virtfs.rdev", 11)) { - stbuf->st_rdev = atoi(buf+12); - } - memset(buf, 0, ATTR_MAX); - } - fclose(fp); -} - -static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf) -{ - int err; - char *buffer; - char *path = fs_path->data; - - buffer = rpath(fs_ctx, path); - err = lstat(buffer, stbuf); - if (err) { - goto err_out; - } - if (fs_ctx->export_flags & V9FS_SM_MAPPED) { - /* Actual credentials are part of extended attrs */ - uid_t tmp_uid; - gid_t tmp_gid; - mode_t tmp_mode; - dev_t tmp_dev; - if (getxattr(buffer, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) { - stbuf->st_uid = le32_to_cpu(tmp_uid); - } - if (getxattr(buffer, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) { - stbuf->st_gid = le32_to_cpu(tmp_gid); - } - if (getxattr(buffer, "user.virtfs.mode", - &tmp_mode, sizeof(mode_t)) > 0) { - stbuf->st_mode = le32_to_cpu(tmp_mode); - } - if (getxattr(buffer, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) { - stbuf->st_rdev = le64_to_cpu(tmp_dev); - } - } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - local_mapped_file_attr(fs_ctx, path, stbuf); - } - -err_out: - g_free(buffer); - return err; -} - -static int local_create_mapped_attr_dir(FsContext *ctx, const char *path) -{ - int err; - char *attr_dir; - char *tmp_path = g_strdup(path); - - attr_dir = g_strdup_printf("%s/%s/%s", - ctx->fs_root, dirname(tmp_path), VIRTFS_META_DIR); - - err = mkdir(attr_dir, 0700); - if (err < 0 && errno == EEXIST) { - err = 0; - } - g_free(attr_dir); - g_free(tmp_path); - return err; -} - -static int local_set_mapped_file_attr(FsContext *ctx, - const char *path, FsCred *credp) -{ - FILE *fp; - int ret = 0; - char buf[ATTR_MAX]; - char *attr_path; - int uid = -1, gid = -1, mode = -1, rdev = -1; - - attr_path = local_mapped_attr_path(ctx, path); - fp = local_fopen(attr_path, "r"); - if (!fp) { - goto create_map_file; - } - memset(buf, 0, ATTR_MAX); - while (fgets(buf, ATTR_MAX, fp)) { - if (!strncmp(buf, "virtfs.uid", 10)) { - uid = atoi(buf+11); - } else if (!strncmp(buf, "virtfs.gid", 10)) { - gid = atoi(buf+11); - } else if (!strncmp(buf, "virtfs.mode", 11)) { - mode = atoi(buf+12); - } else if (!strncmp(buf, "virtfs.rdev", 11)) { - rdev = atoi(buf+12); - } - memset(buf, 0, ATTR_MAX); - } - fclose(fp); - goto update_map_file; - -create_map_file: - ret = local_create_mapped_attr_dir(ctx, path); - if (ret < 0) { - goto err_out; - } - -update_map_file: - fp = local_fopen(attr_path, "w"); - if (!fp) { - ret = -1; - goto err_out; - } - - if (credp->fc_uid != -1) { - uid = credp->fc_uid; - } - if (credp->fc_gid != -1) { - gid = credp->fc_gid; - } - if (credp->fc_mode != -1) { - mode = credp->fc_mode; - } - if (credp->fc_rdev != -1) { - rdev = credp->fc_rdev; - } - - - if (uid != -1) { - fprintf(fp, "virtfs.uid=%d\n", uid); - } - if (gid != -1) { - fprintf(fp, "virtfs.gid=%d\n", gid); - } - if (mode != -1) { - fprintf(fp, "virtfs.mode=%d\n", mode); - } - if (rdev != -1) { - fprintf(fp, "virtfs.rdev=%d\n", rdev); - } - fclose(fp); - -err_out: - g_free(attr_path); - return ret; -} - -static int local_set_xattr(const char *path, FsCred *credp) -{ - int err; - - if (credp->fc_uid != -1) { - uint32_t tmp_uid = cpu_to_le32(credp->fc_uid); - err = setxattr(path, "user.virtfs.uid", &tmp_uid, sizeof(uid_t), 0); - if (err) { - return err; - } - } - if (credp->fc_gid != -1) { - uint32_t tmp_gid = cpu_to_le32(credp->fc_gid); - err = setxattr(path, "user.virtfs.gid", &tmp_gid, sizeof(gid_t), 0); - if (err) { - return err; - } - } - if (credp->fc_mode != -1) { - uint32_t tmp_mode = cpu_to_le32(credp->fc_mode); - err = setxattr(path, "user.virtfs.mode", &tmp_mode, sizeof(mode_t), 0); - if (err) { - return err; - } - } - if (credp->fc_rdev != -1) { - uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev); - err = setxattr(path, "user.virtfs.rdev", &tmp_rdev, sizeof(dev_t), 0); - if (err) { - return err; - } - } - return 0; -} - -static int local_post_create_passthrough(FsContext *fs_ctx, const char *path, - FsCred *credp) -{ - char *buffer; - - buffer = rpath(fs_ctx, path); - if (lchown(buffer, credp->fc_uid, credp->fc_gid) < 0) { - /* - * If we fail to change ownership and if we are - * using security model none. Ignore the error - */ - if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { - goto err; - } - } - - if (chmod(buffer, credp->fc_mode & 07777) < 0) { - goto err; - } - - g_free(buffer); - return 0; -err: - g_free(buffer); - return -1; -} - -static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, - char *buf, size_t bufsz) -{ - ssize_t tsize = -1; - char *buffer; - char *path = fs_path->data; - - if ((fs_ctx->export_flags & V9FS_SM_MAPPED) || - (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) { - int fd; - buffer = rpath(fs_ctx, path); - fd = open(buffer, O_RDONLY | O_NOFOLLOW); - g_free(buffer); - if (fd == -1) { - return -1; - } - do { - tsize = read(fd, (void *)buf, bufsz); - } while (tsize == -1 && errno == EINTR); - close(fd); - } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || - (fs_ctx->export_flags & V9FS_SM_NONE)) { - buffer = rpath(fs_ctx, path); - tsize = readlink(buffer, buf, bufsz); - g_free(buffer); - } - return tsize; -} - -static int local_close(FsContext *ctx, V9fsFidOpenState *fs) -{ - return close(fs->fd); -} - -static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs) -{ - return closedir(fs->dir); -} - -static int local_open(FsContext *ctx, V9fsPath *fs_path, - int flags, V9fsFidOpenState *fs) -{ - char *buffer; - char *path = fs_path->data; - - buffer = rpath(ctx, path); - fs->fd = open(buffer, flags | O_NOFOLLOW); - g_free(buffer); - return fs->fd; -} - -static int local_opendir(FsContext *ctx, - V9fsPath *fs_path, V9fsFidOpenState *fs) -{ - char *buffer; - char *path = fs_path->data; - - buffer = rpath(ctx, path); - fs->dir = opendir(buffer); - g_free(buffer); - if (!fs->dir) { - return -1; - } - return 0; -} - -static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) -{ - rewinddir(fs->dir); -} - -static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) -{ - return telldir(fs->dir); -} - -static int local_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, - struct dirent *entry, - struct dirent **result) -{ - int ret; - -again: - ret = readdir_r(fs->dir, entry, result); - if (ctx->export_flags & V9FS_SM_MAPPED) { - entry->d_type = DT_UNKNOWN; - } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { - if (!ret && *result != NULL && - !strcmp(entry->d_name, VIRTFS_META_DIR)) { - /* skp the meta data directory */ - goto again; - } - entry->d_type = DT_UNKNOWN; - } - return ret; -} - -static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) -{ - seekdir(fs->dir, off); -} - -static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs, - const struct iovec *iov, - int iovcnt, off_t offset) -{ -#ifdef CONFIG_PREADV - return preadv(fs->fd, iov, iovcnt, offset); -#else - int err = lseek(fs->fd, offset, SEEK_SET); - if (err == -1) { - return err; - } else { - return readv(fs->fd, iov, iovcnt); - } -#endif -} - -static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs, - const struct iovec *iov, - int iovcnt, off_t offset) -{ - ssize_t ret -; -#ifdef CONFIG_PREADV - ret = pwritev(fs->fd, iov, iovcnt, offset); -#else - int err = lseek(fs->fd, offset, SEEK_SET); - if (err == -1) { - return err; - } else { - ret = writev(fs->fd, iov, iovcnt); - } -#endif -#ifdef CONFIG_SYNC_FILE_RANGE - if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { - /* - * Initiate a writeback. This is not a data integrity sync. - * We want to ensure that we don't leave dirty pages in the cache - * after write when writeout=immediate is sepcified. - */ - sync_file_range(fs->fd, offset, ret, - SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); - } -#endif - return ret; -} - -static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) -{ - char *buffer; - int ret = -1; - char *path = fs_path->data; - - if (fs_ctx->export_flags & V9FS_SM_MAPPED) { - buffer = rpath(fs_ctx, path); - ret = local_set_xattr(buffer, credp); - g_free(buffer); - } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - return local_set_mapped_file_attr(fs_ctx, path, credp); - } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || - (fs_ctx->export_flags & V9FS_SM_NONE)) { - buffer = rpath(fs_ctx, path); - ret = chmod(buffer, credp->fc_mode); - g_free(buffer); - } - return ret; -} - -static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, - const char *name, FsCred *credp) -{ - char *path; - int err = -1; - int serrno = 0; - V9fsString fullname; - char *buffer = NULL; - - v9fs_string_init(&fullname); - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - path = fullname.data; - - /* Determine the security model */ - if (fs_ctx->export_flags & V9FS_SM_MAPPED) { - buffer = rpath(fs_ctx, path); - err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0); - if (err == -1) { - goto out; - } - err = local_set_xattr(buffer, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - - buffer = rpath(fs_ctx, path); - err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0); - if (err == -1) { - goto out; - } - err = local_set_mapped_file_attr(fs_ctx, path, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || - (fs_ctx->export_flags & V9FS_SM_NONE)) { - buffer = rpath(fs_ctx, path); - err = mknod(buffer, credp->fc_mode, credp->fc_rdev); - if (err == -1) { - goto out; - } - err = local_post_create_passthrough(fs_ctx, path, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } - goto out; - -err_end: - remove(buffer); - errno = serrno; -out: - g_free(buffer); - v9fs_string_free(&fullname); - return err; -} - -static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, - const char *name, FsCred *credp) -{ - char *path; - int err = -1; - int serrno = 0; - V9fsString fullname; - char *buffer = NULL; - - v9fs_string_init(&fullname); - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - path = fullname.data; - - /* Determine the security model */ - if (fs_ctx->export_flags & V9FS_SM_MAPPED) { - buffer = rpath(fs_ctx, path); - err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS); - if (err == -1) { - goto out; - } - credp->fc_mode = credp->fc_mode|S_IFDIR; - err = local_set_xattr(buffer, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - buffer = rpath(fs_ctx, path); - err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS); - if (err == -1) { - goto out; - } - credp->fc_mode = credp->fc_mode|S_IFDIR; - err = local_set_mapped_file_attr(fs_ctx, path, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || - (fs_ctx->export_flags & V9FS_SM_NONE)) { - buffer = rpath(fs_ctx, path); - err = mkdir(buffer, credp->fc_mode); - if (err == -1) { - goto out; - } - err = local_post_create_passthrough(fs_ctx, path, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } - goto out; - -err_end: - remove(buffer); - errno = serrno; -out: - g_free(buffer); - v9fs_string_free(&fullname); - return err; -} - -static int local_fstat(FsContext *fs_ctx, int fid_type, - V9fsFidOpenState *fs, struct stat *stbuf) -{ - int err, fd; - - if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir); - } else { - fd = fs->fd; - } - - err = fstat(fd, stbuf); - if (err) { - return err; - } - if (fs_ctx->export_flags & V9FS_SM_MAPPED) { - /* Actual credentials are part of extended attrs */ - uid_t tmp_uid; - gid_t tmp_gid; - mode_t tmp_mode; - dev_t tmp_dev; - - if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) { - stbuf->st_uid = le32_to_cpu(tmp_uid); - } - if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) { - stbuf->st_gid = le32_to_cpu(tmp_gid); - } - if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) { - stbuf->st_mode = le32_to_cpu(tmp_mode); - } - if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) { - stbuf->st_rdev = le64_to_cpu(tmp_dev); - } - } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - errno = EOPNOTSUPP; - return -1; - } - return err; -} - -static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, - int flags, FsCred *credp, V9fsFidOpenState *fs) -{ - char *path; - int fd = -1; - int err = -1; - int serrno = 0; - V9fsString fullname; - char *buffer = NULL; - - /* - * Mark all the open to not follow symlinks - */ - flags |= O_NOFOLLOW; - - v9fs_string_init(&fullname); - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - path = fullname.data; - - /* Determine the security model */ - if (fs_ctx->export_flags & V9FS_SM_MAPPED) { - buffer = rpath(fs_ctx, path); - fd = open(buffer, flags, SM_LOCAL_MODE_BITS); - if (fd == -1) { - err = fd; - goto out; - } - credp->fc_mode = credp->fc_mode|S_IFREG; - /* Set cleint credentials in xattr */ - err = local_set_xattr(buffer, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - buffer = rpath(fs_ctx, path); - fd = open(buffer, flags, SM_LOCAL_MODE_BITS); - if (fd == -1) { - err = fd; - goto out; - } - credp->fc_mode = credp->fc_mode|S_IFREG; - /* Set client credentials in .virtfs_metadata directory files */ - err = local_set_mapped_file_attr(fs_ctx, path, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || - (fs_ctx->export_flags & V9FS_SM_NONE)) { - buffer = rpath(fs_ctx, path); - fd = open(buffer, flags, credp->fc_mode); - if (fd == -1) { - err = fd; - goto out; - } - err = local_post_create_passthrough(fs_ctx, path, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } - err = fd; - fs->fd = fd; - goto out; - -err_end: - close(fd); - remove(buffer); - errno = serrno; -out: - g_free(buffer); - v9fs_string_free(&fullname); - return err; -} - - -static int local_symlink(FsContext *fs_ctx, const char *oldpath, - V9fsPath *dir_path, const char *name, FsCred *credp) -{ - int err = -1; - int serrno = 0; - char *newpath; - V9fsString fullname; - char *buffer = NULL; - - v9fs_string_init(&fullname); - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - newpath = fullname.data; - - /* Determine the security model */ - if (fs_ctx->export_flags & V9FS_SM_MAPPED) { - int fd; - ssize_t oldpath_size, write_size; - buffer = rpath(fs_ctx, newpath); - fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS); - if (fd == -1) { - err = fd; - goto out; - } - /* Write the oldpath (target) to the file. */ - oldpath_size = strlen(oldpath); - do { - write_size = write(fd, (void *)oldpath, oldpath_size); - } while (write_size == -1 && errno == EINTR); - - if (write_size != oldpath_size) { - serrno = errno; - close(fd); - err = -1; - goto err_end; - } - close(fd); - /* Set cleint credentials in symlink's xattr */ - credp->fc_mode = credp->fc_mode|S_IFLNK; - err = local_set_xattr(buffer, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - int fd; - ssize_t oldpath_size, write_size; - buffer = rpath(fs_ctx, newpath); - fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS); - if (fd == -1) { - err = fd; - goto out; - } - /* Write the oldpath (target) to the file. */ - oldpath_size = strlen(oldpath); - do { - write_size = write(fd, (void *)oldpath, oldpath_size); - } while (write_size == -1 && errno == EINTR); - - if (write_size != oldpath_size) { - serrno = errno; - close(fd); - err = -1; - goto err_end; - } - close(fd); - /* Set cleint credentials in symlink's xattr */ - credp->fc_mode = credp->fc_mode|S_IFLNK; - err = local_set_mapped_file_attr(fs_ctx, newpath, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || - (fs_ctx->export_flags & V9FS_SM_NONE)) { - buffer = rpath(fs_ctx, newpath); - err = symlink(oldpath, buffer); - if (err) { - goto out; - } - err = lchown(buffer, credp->fc_uid, credp->fc_gid); - if (err == -1) { - /* - * If we fail to change ownership and if we are - * using security model none. Ignore the error - */ - if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { - serrno = errno; - goto err_end; - } else - err = 0; - } - } - goto out; - -err_end: - remove(buffer); - errno = serrno; -out: - g_free(buffer); - v9fs_string_free(&fullname); - return err; -} - -static int local_link(FsContext *ctx, V9fsPath *oldpath, - V9fsPath *dirpath, const char *name) -{ - int ret; - V9fsString newpath; - char *buffer, *buffer1; - - v9fs_string_init(&newpath); - v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name); - - buffer = rpath(ctx, oldpath->data); - buffer1 = rpath(ctx, newpath.data); - ret = link(buffer, buffer1); - g_free(buffer); - g_free(buffer1); - - /* now link the virtfs_metadata files */ - if (!ret && (ctx->export_flags & V9FS_SM_MAPPED_FILE)) { - /* Link the .virtfs_metadata files. Create the metada directory */ - ret = local_create_mapped_attr_dir(ctx, newpath.data); - if (ret < 0) { - goto err_out; - } - buffer = local_mapped_attr_path(ctx, oldpath->data); - buffer1 = local_mapped_attr_path(ctx, newpath.data); - ret = link(buffer, buffer1); - g_free(buffer); - g_free(buffer1); - if (ret < 0 && errno != ENOENT) { - goto err_out; - } - } -err_out: - v9fs_string_free(&newpath); - return ret; -} - -static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) -{ - char *buffer; - int ret; - char *path = fs_path->data; - - buffer = rpath(ctx, path); - ret = truncate(buffer, size); - g_free(buffer); - return ret; -} - -static int local_rename(FsContext *ctx, const char *oldpath, - const char *newpath) -{ - int err; - char *buffer, *buffer1; - - if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { - err = local_create_mapped_attr_dir(ctx, newpath); - if (err < 0) { - return err; - } - /* rename the .virtfs_metadata files */ - buffer = local_mapped_attr_path(ctx, oldpath); - buffer1 = local_mapped_attr_path(ctx, newpath); - err = rename(buffer, buffer1); - g_free(buffer); - g_free(buffer1); - if (err < 0 && errno != ENOENT) { - return err; - } - } - - buffer = rpath(ctx, oldpath); - buffer1 = rpath(ctx, newpath); - err = rename(buffer, buffer1); - g_free(buffer); - g_free(buffer1); - return err; -} - -static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) -{ - char *buffer; - int ret = -1; - char *path = fs_path->data; - - if ((credp->fc_uid == -1 && credp->fc_gid == -1) || - (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || - (fs_ctx->export_flags & V9FS_SM_NONE)) { - buffer = rpath(fs_ctx, path); - ret = lchown(buffer, credp->fc_uid, credp->fc_gid); - g_free(buffer); - } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) { - buffer = rpath(fs_ctx, path); - ret = local_set_xattr(buffer, credp); - g_free(buffer); - } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - return local_set_mapped_file_attr(fs_ctx, path, credp); - } - return ret; -} - -static int local_utimensat(FsContext *s, V9fsPath *fs_path, - const struct timespec *buf) -{ - char *buffer; - int ret; - char *path = fs_path->data; - - buffer = rpath(s, path); - ret = qemu_utimens(buffer, buf); - g_free(buffer); - return ret; -} - -static int local_remove(FsContext *ctx, const char *path) -{ - int err; - struct stat stbuf; - char *buffer; - - if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { - buffer = rpath(ctx, path); - err = lstat(buffer, &stbuf); - g_free(buffer); - if (err) { - goto err_out; - } - /* - * If directory remove .virtfs_metadata contained in the - * directory - */ - if (S_ISDIR(stbuf.st_mode)) { - buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root, - path, VIRTFS_META_DIR); - err = remove(buffer); - g_free(buffer); - if (err < 0 && errno != ENOENT) { - /* - * We didn't had the .virtfs_metadata file. May be file created - * in non-mapped mode ?. Ignore ENOENT. - */ - goto err_out; - } - } - /* - * Now remove the name from parent directory - * .virtfs_metadata directory - */ - buffer = local_mapped_attr_path(ctx, path); - err = remove(buffer); - g_free(buffer); - if (err < 0 && errno != ENOENT) { - /* - * We didn't had the .virtfs_metadata file. May be file created - * in non-mapped mode ?. Ignore ENOENT. - */ - goto err_out; - } - } - - buffer = rpath(ctx, path); - err = remove(buffer); - g_free(buffer); -err_out: - return err; -} - -static int local_fsync(FsContext *ctx, int fid_type, - V9fsFidOpenState *fs, int datasync) -{ - int fd; - - if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir); - } else { - fd = fs->fd; - } - - if (datasync) { - return qemu_fdatasync(fd); - } else { - return fsync(fd); - } -} - -static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf) -{ - char *buffer; - int ret; - char *path = fs_path->data; - - buffer = rpath(s, path); - ret = statfs(buffer, stbuf); - g_free(buffer); - return ret; -} - -static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path, - const char *name, void *value, size_t size) -{ - char *path = fs_path->data; - - return v9fs_get_xattr(ctx, path, name, value, size); -} - -static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path, - void *value, size_t size) -{ - char *path = fs_path->data; - - return v9fs_list_xattr(ctx, path, value, size); -} - -static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, - void *value, size_t size, int flags) -{ - char *path = fs_path->data; - - return v9fs_set_xattr(ctx, path, name, value, size, flags); -} - -static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path, - const char *name) -{ - char *path = fs_path->data; - - return v9fs_remove_xattr(ctx, path, name); -} - -static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, - const char *name, V9fsPath *target) -{ - if (dir_path) { - v9fs_string_sprintf((V9fsString *)target, "%s/%s", - dir_path->data, name); - } else { - v9fs_string_sprintf((V9fsString *)target, "%s", name); - } - /* Bump the size for including terminating NULL */ - target->size++; - return 0; -} - -static int local_renameat(FsContext *ctx, V9fsPath *olddir, - const char *old_name, V9fsPath *newdir, - const char *new_name) -{ - int ret; - V9fsString old_full_name, new_full_name; - - v9fs_string_init(&old_full_name); - v9fs_string_init(&new_full_name); - - v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name); - v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name); - - ret = local_rename(ctx, old_full_name.data, new_full_name.data); - v9fs_string_free(&old_full_name); - v9fs_string_free(&new_full_name); - return ret; -} - -static int local_unlinkat(FsContext *ctx, V9fsPath *dir, - const char *name, int flags) -{ - int ret; - V9fsString fullname; - char *buffer; - - v9fs_string_init(&fullname); - - v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name); - if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { - if (flags == AT_REMOVEDIR) { - /* - * If directory remove .virtfs_metadata contained in the - * directory - */ - buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root, - fullname.data, VIRTFS_META_DIR); - ret = remove(buffer); - g_free(buffer); - if (ret < 0 && errno != ENOENT) { - /* - * We didn't had the .virtfs_metadata file. May be file created - * in non-mapped mode ?. Ignore ENOENT. - */ - goto err_out; - } - } - /* - * Now remove the name from parent directory - * .virtfs_metadata directory. - */ - buffer = local_mapped_attr_path(ctx, fullname.data); - ret = remove(buffer); - g_free(buffer); - if (ret < 0 && errno != ENOENT) { - /* - * We didn't had the .virtfs_metadata file. May be file created - * in non-mapped mode ?. Ignore ENOENT. - */ - goto err_out; - } - } - /* Remove the name finally */ - buffer = rpath(ctx, fullname.data); - ret = remove(buffer); - g_free(buffer); - -err_out: - v9fs_string_free(&fullname); - return ret; -} - -static int local_ioc_getversion(FsContext *ctx, V9fsPath *path, - mode_t st_mode, uint64_t *st_gen) -{ -#ifdef FS_IOC_GETVERSION - int err; - V9fsFidOpenState fid_open; - - /* - * Do not try to open special files like device nodes, fifos etc - * We can get fd for regular files and directories only - */ - if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { - errno = ENOTTY; - return -1; - } - err = local_open(ctx, path, O_RDONLY, &fid_open); - if (err < 0) { - return err; - } - err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen); - local_close(ctx, &fid_open); - return err; -#else - errno = ENOTTY; - return -1; -#endif -} - -static int local_init(FsContext *ctx) -{ - int err = 0; - struct statfs stbuf; - - if (ctx->export_flags & V9FS_SM_PASSTHROUGH) { - ctx->xops = passthrough_xattr_ops; - } else if (ctx->export_flags & V9FS_SM_MAPPED) { - ctx->xops = mapped_xattr_ops; - } else if (ctx->export_flags & V9FS_SM_NONE) { - ctx->xops = none_xattr_ops; - } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { - /* - * xattr operation for mapped-file and passthrough - * remain same. - */ - ctx->xops = passthrough_xattr_ops; - } - ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; -#ifdef FS_IOC_GETVERSION - /* - * use ioc_getversion only if the iocl is definied - */ - err = statfs(ctx->fs_root, &stbuf); - if (!err) { - switch (stbuf.f_type) { - case EXT2_SUPER_MAGIC: - case BTRFS_SUPER_MAGIC: - case REISERFS_SUPER_MAGIC: - case XFS_SUPER_MAGIC: - ctx->exops.get_st_gen = local_ioc_getversion; - break; - } - } -#endif - return err; -} - -static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse) -{ - const char *sec_model = qemu_opt_get(opts, "security_model"); - const char *path = qemu_opt_get(opts, "path"); - - if (!sec_model) { - fprintf(stderr, "security model not specified, " - "local fs needs security model\nvalid options are:" - "\tsecurity_model=[passthrough|mapped|none]\n"); - return -1; - } - - if (!strcmp(sec_model, "passthrough")) { - fse->export_flags |= V9FS_SM_PASSTHROUGH; - } else if (!strcmp(sec_model, "mapped") || - !strcmp(sec_model, "mapped-xattr")) { - fse->export_flags |= V9FS_SM_MAPPED; - } else if (!strcmp(sec_model, "none")) { - fse->export_flags |= V9FS_SM_NONE; - } else if (!strcmp(sec_model, "mapped-file")) { - fse->export_flags |= V9FS_SM_MAPPED_FILE; - } else { - fprintf(stderr, "Invalid security model %s specified, valid options are" - "\n\t [passthrough|mapped-xattr|mapped-file|none]\n", - sec_model); - return -1; - } - - if (!path) { - fprintf(stderr, "fsdev: No path specified.\n"); - return -1; - } - fse->path = g_strdup(path); - - return 0; -} - -FileOperations local_ops = { - .parse_opts = local_parse_opts, - .init = local_init, - .lstat = local_lstat, - .readlink = local_readlink, - .close = local_close, - .closedir = local_closedir, - .open = local_open, - .opendir = local_opendir, - .rewinddir = local_rewinddir, - .telldir = local_telldir, - .readdir_r = local_readdir_r, - .seekdir = local_seekdir, - .preadv = local_preadv, - .pwritev = local_pwritev, - .chmod = local_chmod, - .mknod = local_mknod, - .mkdir = local_mkdir, - .fstat = local_fstat, - .open2 = local_open2, - .symlink = local_symlink, - .link = local_link, - .truncate = local_truncate, - .rename = local_rename, - .chown = local_chown, - .utimensat = local_utimensat, - .remove = local_remove, - .fsync = local_fsync, - .statfs = local_statfs, - .lgetxattr = local_lgetxattr, - .llistxattr = local_llistxattr, - .lsetxattr = local_lsetxattr, - .lremovexattr = local_lremovexattr, - .name_to_path = local_name_to_path, - .renameat = local_renameat, - .unlinkat = local_unlinkat, -}; diff --git a/qemu/hw/9pfs/virtio-9p-posix-acl.c b/qemu/hw/9pfs/virtio-9p-posix-acl.c deleted file mode 100644 index 09dad071e..000000000 --- a/qemu/hw/9pfs/virtio-9p-posix-acl.c +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Virtio 9p system.posix* xattr callback - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Aneesh Kumar K.V - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include -#include "qemu/xattr.h" -#include "hw/virtio/virtio.h" -#include "virtio-9p.h" -#include "fsdev/file-op-9p.h" -#include "virtio-9p-xattr.h" - -#define MAP_ACL_ACCESS "user.virtfs.system.posix_acl_access" -#define MAP_ACL_DEFAULT "user.virtfs.system.posix_acl_default" -#define ACL_ACCESS "system.posix_acl_access" -#define ACL_DEFAULT "system.posix_acl_default" - -static ssize_t mp_pacl_getxattr(FsContext *ctx, const char *path, - const char *name, void *value, size_t size) -{ - char *buffer; - ssize_t ret; - - buffer = rpath(ctx, path); - ret = lgetxattr(buffer, MAP_ACL_ACCESS, value, size); - g_free(buffer); - return ret; -} - -static ssize_t mp_pacl_listxattr(FsContext *ctx, const char *path, - char *name, void *value, size_t osize) -{ - ssize_t len = sizeof(ACL_ACCESS); - - if (!value) { - return len; - } - - if (osize < len) { - errno = ERANGE; - return -1; - } - - /* len includes the trailing NUL */ - memcpy(value, ACL_ACCESS, len); - return 0; -} - -static int mp_pacl_setxattr(FsContext *ctx, const char *path, const char *name, - void *value, size_t size, int flags) -{ - char *buffer; - int ret; - - buffer = rpath(ctx, path); - ret = lsetxattr(buffer, MAP_ACL_ACCESS, value, size, flags); - g_free(buffer); - return ret; -} - -static int mp_pacl_removexattr(FsContext *ctx, - const char *path, const char *name) -{ - int ret; - char *buffer; - - buffer = rpath(ctx, path); - ret = lremovexattr(buffer, MAP_ACL_ACCESS); - if (ret == -1 && errno == ENODATA) { - /* - * We don't get ENODATA error when trying to remove a - * posix acl that is not present. So don't throw the error - * even in case of mapped security model - */ - errno = 0; - ret = 0; - } - g_free(buffer); - return ret; -} - -static ssize_t mp_dacl_getxattr(FsContext *ctx, const char *path, - const char *name, void *value, size_t size) -{ - char *buffer; - ssize_t ret; - - buffer = rpath(ctx, path); - ret = lgetxattr(buffer, MAP_ACL_DEFAULT, value, size); - g_free(buffer); - return ret; -} - -static ssize_t mp_dacl_listxattr(FsContext *ctx, const char *path, - char *name, void *value, size_t osize) -{ - ssize_t len = sizeof(ACL_DEFAULT); - - if (!value) { - return len; - } - - if (osize < len) { - errno = ERANGE; - return -1; - } - - /* len includes the trailing NUL */ - memcpy(value, ACL_DEFAULT, len); - return 0; -} - -static int mp_dacl_setxattr(FsContext *ctx, const char *path, const char *name, - void *value, size_t size, int flags) -{ - char *buffer; - int ret; - - buffer = rpath(ctx, path); - ret = lsetxattr(buffer, MAP_ACL_DEFAULT, value, size, flags); - g_free(buffer); - return ret; -} - -static int mp_dacl_removexattr(FsContext *ctx, - const char *path, const char *name) -{ - int ret; - char *buffer; - - buffer = rpath(ctx, path); - ret = lremovexattr(buffer, MAP_ACL_DEFAULT); - if (ret == -1 && errno == ENODATA) { - /* - * We don't get ENODATA error when trying to remove a - * posix acl that is not present. So don't throw the error - * even in case of mapped security model - */ - errno = 0; - ret = 0; - } - g_free(buffer); - return ret; -} - - -XattrOperations mapped_pacl_xattr = { - .name = "system.posix_acl_access", - .getxattr = mp_pacl_getxattr, - .setxattr = mp_pacl_setxattr, - .listxattr = mp_pacl_listxattr, - .removexattr = mp_pacl_removexattr, -}; - -XattrOperations mapped_dacl_xattr = { - .name = "system.posix_acl_default", - .getxattr = mp_dacl_getxattr, - .setxattr = mp_dacl_setxattr, - .listxattr = mp_dacl_listxattr, - .removexattr = mp_dacl_removexattr, -}; - -XattrOperations passthrough_acl_xattr = { - .name = "system.posix_acl_", - .getxattr = pt_getxattr, - .setxattr = pt_setxattr, - .listxattr = pt_listxattr, - .removexattr = pt_removexattr, -}; - -XattrOperations none_acl_xattr = { - .name = "system.posix_acl_", - .getxattr = notsup_getxattr, - .setxattr = notsup_setxattr, - .listxattr = notsup_listxattr, - .removexattr = notsup_removexattr, -}; diff --git a/qemu/hw/9pfs/virtio-9p-proxy.c b/qemu/hw/9pfs/virtio-9p-proxy.c deleted file mode 100644 index 1bc7881f0..000000000 --- a/qemu/hw/9pfs/virtio-9p-proxy.c +++ /dev/null @@ -1,1219 +0,0 @@ -/* - * Virtio 9p Proxy callback - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * M. Mohan Kumar - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - */ -#include -#include -#include "hw/virtio/virtio.h" -#include "virtio-9p.h" -#include "qemu/error-report.h" -#include "fsdev/qemu-fsdev.h" -#include "virtio-9p-proxy.h" - -typedef struct V9fsProxy { - int sockfd; - QemuMutex mutex; - struct iovec in_iovec; - struct iovec out_iovec; -} V9fsProxy; - -/* - * Return received file descriptor on success in *status. - * errno is also returned on *status (which will be < 0) - * return < 0 on transport error. - */ -static int v9fs_receivefd(int sockfd, int *status) -{ - struct iovec iov; - struct msghdr msg; - struct cmsghdr *cmsg; - int retval, data, fd; - union MsgControl msg_control; - - iov.iov_base = &data; - iov.iov_len = sizeof(data); - - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = &msg_control; - msg.msg_controllen = sizeof(msg_control); - - do { - retval = recvmsg(sockfd, &msg, 0); - } while (retval < 0 && errno == EINTR); - if (retval <= 0) { - return retval; - } - /* - * data is set to V9FS_FD_VALID, if ancillary data is sent. If this - * request doesn't need ancillary data (fd) or an error occurred, - * data is set to negative errno value. - */ - if (data != V9FS_FD_VALID) { - *status = data; - return 0; - } - /* - * File descriptor (fd) is sent in the ancillary data. Check if we - * indeed received it. One of the reasons to fail to receive it is if - * we exceeded the maximum number of file descriptors! - */ - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || - cmsg->cmsg_level != SOL_SOCKET || - cmsg->cmsg_type != SCM_RIGHTS) { - continue; - } - fd = *((int *)CMSG_DATA(cmsg)); - *status = fd; - return 0; - } - *status = -ENFILE; /* Ancillary data sent but not received */ - return 0; -} - -static ssize_t socket_read(int sockfd, void *buff, size_t size) -{ - ssize_t retval, total = 0; - - while (size) { - retval = read(sockfd, buff, size); - if (retval == 0) { - return -EIO; - } - if (retval < 0) { - if (errno == EINTR) { - continue; - } - return -errno; - } - size -= retval; - buff += retval; - total += retval; - } - return total; -} - -/* Converts proxy_statfs to VFS statfs structure */ -static void prstatfs_to_statfs(struct statfs *stfs, ProxyStatFS *prstfs) -{ - memset(stfs, 0, sizeof(*stfs)); - stfs->f_type = prstfs->f_type; - stfs->f_bsize = prstfs->f_bsize; - stfs->f_blocks = prstfs->f_blocks; - stfs->f_bfree = prstfs->f_bfree; - stfs->f_bavail = prstfs->f_bavail; - stfs->f_files = prstfs->f_files; - stfs->f_ffree = prstfs->f_ffree; - stfs->f_fsid.__val[0] = prstfs->f_fsid[0] & 0xFFFFFFFFU; - stfs->f_fsid.__val[1] = prstfs->f_fsid[1] >> 32 & 0xFFFFFFFFU; - stfs->f_namelen = prstfs->f_namelen; - stfs->f_frsize = prstfs->f_frsize; -} - -/* Converts proxy_stat structure to VFS stat structure */ -static void prstat_to_stat(struct stat *stbuf, ProxyStat *prstat) -{ - memset(stbuf, 0, sizeof(*stbuf)); - stbuf->st_dev = prstat->st_dev; - stbuf->st_ino = prstat->st_ino; - stbuf->st_nlink = prstat->st_nlink; - stbuf->st_mode = prstat->st_mode; - stbuf->st_uid = prstat->st_uid; - stbuf->st_gid = prstat->st_gid; - stbuf->st_rdev = prstat->st_rdev; - stbuf->st_size = prstat->st_size; - stbuf->st_blksize = prstat->st_blksize; - stbuf->st_blocks = prstat->st_blocks; - stbuf->st_atim.tv_sec = prstat->st_atim_sec; - stbuf->st_atim.tv_nsec = prstat->st_atim_nsec; - stbuf->st_mtime = prstat->st_mtim_sec; - stbuf->st_mtim.tv_nsec = prstat->st_mtim_nsec; - stbuf->st_ctime = prstat->st_ctim_sec; - stbuf->st_ctim.tv_nsec = prstat->st_ctim_nsec; -} - -/* - * Response contains two parts - * {header, data} - * header.type == T_ERROR, data -> -errno - * header.type == T_SUCCESS, data -> response - * size of errno/response is given by header.size - * returns < 0, on transport error. response is - * valid only if status >= 0. - */ -static int v9fs_receive_response(V9fsProxy *proxy, int type, - int *status, void *response) -{ - int retval; - ProxyHeader header; - struct iovec *reply = &proxy->in_iovec; - - *status = 0; - reply->iov_len = 0; - retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ); - if (retval < 0) { - return retval; - } - reply->iov_len = PROXY_HDR_SZ; - proxy_unmarshal(reply, 0, "dd", &header.type, &header.size); - /* - * if response size > PROXY_MAX_IO_SZ, read the response but ignore it and - * return -ENOBUFS - */ - if (header.size > PROXY_MAX_IO_SZ) { - int count; - while (header.size > 0) { - count = MIN(PROXY_MAX_IO_SZ, header.size); - count = socket_read(proxy->sockfd, reply->iov_base, count); - if (count < 0) { - return count; - } - header.size -= count; - } - *status = -ENOBUFS; - return 0; - } - - retval = socket_read(proxy->sockfd, - reply->iov_base + PROXY_HDR_SZ, header.size); - if (retval < 0) { - return retval; - } - reply->iov_len += header.size; - /* there was an error during processing request */ - if (header.type == T_ERROR) { - int ret; - ret = proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status); - if (ret < 0) { - *status = ret; - } - return 0; - } - - switch (type) { - case T_LSTAT: { - ProxyStat prstat; - retval = proxy_unmarshal(reply, PROXY_HDR_SZ, - "qqqdddqqqqqqqqqq", &prstat.st_dev, - &prstat.st_ino, &prstat.st_nlink, - &prstat.st_mode, &prstat.st_uid, - &prstat.st_gid, &prstat.st_rdev, - &prstat.st_size, &prstat.st_blksize, - &prstat.st_blocks, - &prstat.st_atim_sec, &prstat.st_atim_nsec, - &prstat.st_mtim_sec, &prstat.st_mtim_nsec, - &prstat.st_ctim_sec, &prstat.st_ctim_nsec); - prstat_to_stat(response, &prstat); - break; - } - case T_STATFS: { - ProxyStatFS prstfs; - retval = proxy_unmarshal(reply, PROXY_HDR_SZ, - "qqqqqqqqqqq", &prstfs.f_type, - &prstfs.f_bsize, &prstfs.f_blocks, - &prstfs.f_bfree, &prstfs.f_bavail, - &prstfs.f_files, &prstfs.f_ffree, - &prstfs.f_fsid[0], &prstfs.f_fsid[1], - &prstfs.f_namelen, &prstfs.f_frsize); - prstatfs_to_statfs(response, &prstfs); - break; - } - case T_READLINK: { - V9fsString target; - v9fs_string_init(&target); - retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &target); - strcpy(response, target.data); - v9fs_string_free(&target); - break; - } - case T_LGETXATTR: - case T_LLISTXATTR: { - V9fsString xattr; - v9fs_string_init(&xattr); - retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &xattr); - memcpy(response, xattr.data, xattr.size); - v9fs_string_free(&xattr); - break; - } - case T_GETVERSION: - proxy_unmarshal(reply, PROXY_HDR_SZ, "q", response); - break; - default: - return -1; - } - if (retval < 0) { - *status = retval; - } - return 0; -} - -/* - * return < 0 on transport error. - * *status is valid only if return >= 0 - */ -static int v9fs_receive_status(V9fsProxy *proxy, - struct iovec *reply, int *status) -{ - int retval; - ProxyHeader header; - - *status = 0; - reply->iov_len = 0; - retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ); - if (retval < 0) { - return retval; - } - reply->iov_len = PROXY_HDR_SZ; - proxy_unmarshal(reply, 0, "dd", &header.type, &header.size); - if (header.size != sizeof(int)) { - *status = -ENOBUFS; - return 0; - } - retval = socket_read(proxy->sockfd, - reply->iov_base + PROXY_HDR_SZ, header.size); - if (retval < 0) { - return retval; - } - reply->iov_len += header.size; - proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status); - return 0; -} - -/* - * Proxy->header and proxy->request written to socket by QEMU process. - * This request read by proxy helper process - * returns 0 on success and -errno on error - */ -static int v9fs_request(V9fsProxy *proxy, int type, - void *response, const char *fmt, ...) -{ - dev_t rdev; - va_list ap; - int size = 0; - int retval = 0; - uint64_t offset; - ProxyHeader header = { 0, 0}; - struct timespec spec[2]; - int flags, mode, uid, gid; - V9fsString *name, *value; - V9fsString *path, *oldpath; - struct iovec *iovec = NULL, *reply = NULL; - - qemu_mutex_lock(&proxy->mutex); - - if (proxy->sockfd == -1) { - retval = -EIO; - goto err_out; - } - iovec = &proxy->out_iovec; - reply = &proxy->in_iovec; - va_start(ap, fmt); - switch (type) { - case T_OPEN: - path = va_arg(ap, V9fsString *); - flags = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, flags); - if (retval > 0) { - header.size = retval; - header.type = T_OPEN; - } - break; - case T_CREATE: - path = va_arg(ap, V9fsString *); - flags = va_arg(ap, int); - mode = va_arg(ap, int); - uid = va_arg(ap, int); - gid = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sdddd", path, - flags, mode, uid, gid); - if (retval > 0) { - header.size = retval; - header.type = T_CREATE; - } - break; - case T_MKNOD: - path = va_arg(ap, V9fsString *); - mode = va_arg(ap, int); - rdev = va_arg(ap, long int); - uid = va_arg(ap, int); - gid = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddsdq", - uid, gid, path, mode, rdev); - if (retval > 0) { - header.size = retval; - header.type = T_MKNOD; - } - break; - case T_MKDIR: - path = va_arg(ap, V9fsString *); - mode = va_arg(ap, int); - uid = va_arg(ap, int); - gid = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddsd", - uid, gid, path, mode); - if (retval > 0) { - header.size = retval; - header.type = T_MKDIR; - } - break; - case T_SYMLINK: - oldpath = va_arg(ap, V9fsString *); - path = va_arg(ap, V9fsString *); - uid = va_arg(ap, int); - gid = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddss", - uid, gid, oldpath, path); - if (retval > 0) { - header.size = retval; - header.type = T_SYMLINK; - } - break; - case T_LINK: - oldpath = va_arg(ap, V9fsString *); - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", - oldpath, path); - if (retval > 0) { - header.size = retval; - header.type = T_LINK; - } - break; - case T_LSTAT: - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); - if (retval > 0) { - header.size = retval; - header.type = T_LSTAT; - } - break; - case T_READLINK: - path = va_arg(ap, V9fsString *); - size = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, size); - if (retval > 0) { - header.size = retval; - header.type = T_READLINK; - } - break; - case T_STATFS: - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); - if (retval > 0) { - header.size = retval; - header.type = T_STATFS; - } - break; - case T_CHMOD: - path = va_arg(ap, V9fsString *); - mode = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, mode); - if (retval > 0) { - header.size = retval; - header.type = T_CHMOD; - } - break; - case T_CHOWN: - path = va_arg(ap, V9fsString *); - uid = va_arg(ap, int); - gid = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sdd", path, uid, gid); - if (retval > 0) { - header.size = retval; - header.type = T_CHOWN; - } - break; - case T_TRUNCATE: - path = va_arg(ap, V9fsString *); - offset = va_arg(ap, uint64_t); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sq", path, offset); - if (retval > 0) { - header.size = retval; - header.type = T_TRUNCATE; - } - break; - case T_UTIME: - path = va_arg(ap, V9fsString *); - spec[0].tv_sec = va_arg(ap, long); - spec[0].tv_nsec = va_arg(ap, long); - spec[1].tv_sec = va_arg(ap, long); - spec[1].tv_nsec = va_arg(ap, long); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sqqqq", path, - spec[0].tv_sec, spec[1].tv_nsec, - spec[1].tv_sec, spec[1].tv_nsec); - if (retval > 0) { - header.size = retval; - header.type = T_UTIME; - } - break; - case T_RENAME: - oldpath = va_arg(ap, V9fsString *); - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", oldpath, path); - if (retval > 0) { - header.size = retval; - header.type = T_RENAME; - } - break; - case T_REMOVE: - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); - if (retval > 0) { - header.size = retval; - header.type = T_REMOVE; - } - break; - case T_LGETXATTR: - size = va_arg(ap, int); - path = va_arg(ap, V9fsString *); - name = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, - "dss", size, path, name); - if (retval > 0) { - header.size = retval; - header.type = T_LGETXATTR; - } - break; - case T_LLISTXATTR: - size = va_arg(ap, int); - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ds", size, path); - if (retval > 0) { - header.size = retval; - header.type = T_LLISTXATTR; - } - break; - case T_LSETXATTR: - path = va_arg(ap, V9fsString *); - name = va_arg(ap, V9fsString *); - value = va_arg(ap, V9fsString *); - size = va_arg(ap, int); - flags = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sssdd", - path, name, value, size, flags); - if (retval > 0) { - header.size = retval; - header.type = T_LSETXATTR; - } - break; - case T_LREMOVEXATTR: - path = va_arg(ap, V9fsString *); - name = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", path, name); - if (retval > 0) { - header.size = retval; - header.type = T_LREMOVEXATTR; - } - break; - case T_GETVERSION: - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); - if (retval > 0) { - header.size = retval; - header.type = T_GETVERSION; - } - break; - default: - error_report("Invalid type %d", type); - retval = -EINVAL; - break; - } - va_end(ap); - - if (retval < 0) { - goto err_out; - } - - /* marshal the header details */ - proxy_marshal(iovec, 0, "dd", header.type, header.size); - header.size += PROXY_HDR_SZ; - - retval = qemu_write_full(proxy->sockfd, iovec->iov_base, header.size); - if (retval != header.size) { - goto close_error; - } - - switch (type) { - case T_OPEN: - case T_CREATE: - /* - * A file descriptor is returned as response for - * T_OPEN,T_CREATE on success - */ - if (v9fs_receivefd(proxy->sockfd, &retval) < 0) { - goto close_error; - } - break; - case T_MKNOD: - case T_MKDIR: - case T_SYMLINK: - case T_LINK: - case T_CHMOD: - case T_CHOWN: - case T_RENAME: - case T_TRUNCATE: - case T_UTIME: - case T_REMOVE: - case T_LSETXATTR: - case T_LREMOVEXATTR: - if (v9fs_receive_status(proxy, reply, &retval) < 0) { - goto close_error; - } - break; - case T_LSTAT: - case T_READLINK: - case T_STATFS: - case T_GETVERSION: - if (v9fs_receive_response(proxy, type, &retval, response) < 0) { - goto close_error; - } - break; - case T_LGETXATTR: - case T_LLISTXATTR: - if (!size) { - if (v9fs_receive_status(proxy, reply, &retval) < 0) { - goto close_error; - } - } else { - if (v9fs_receive_response(proxy, type, &retval, response) < 0) { - goto close_error; - } - } - break; - } - -err_out: - qemu_mutex_unlock(&proxy->mutex); - return retval; - -close_error: - close(proxy->sockfd); - proxy->sockfd = -1; - qemu_mutex_unlock(&proxy->mutex); - return -EIO; -} - -static int proxy_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf) -{ - int retval; - retval = v9fs_request(fs_ctx->private, T_LSTAT, stbuf, "s", fs_path); - if (retval < 0) { - errno = -retval; - return -1; - } - return retval; -} - -static ssize_t proxy_readlink(FsContext *fs_ctx, V9fsPath *fs_path, - char *buf, size_t bufsz) -{ - int retval; - retval = v9fs_request(fs_ctx->private, T_READLINK, buf, "sd", - fs_path, bufsz); - if (retval < 0) { - errno = -retval; - return -1; - } - return strlen(buf); -} - -static int proxy_close(FsContext *ctx, V9fsFidOpenState *fs) -{ - return close(fs->fd); -} - -static int proxy_closedir(FsContext *ctx, V9fsFidOpenState *fs) -{ - return closedir(fs->dir); -} - -static int proxy_open(FsContext *ctx, V9fsPath *fs_path, - int flags, V9fsFidOpenState *fs) -{ - fs->fd = v9fs_request(ctx->private, T_OPEN, NULL, "sd", fs_path, flags); - if (fs->fd < 0) { - errno = -fs->fd; - fs->fd = -1; - } - return fs->fd; -} - -static int proxy_opendir(FsContext *ctx, - V9fsPath *fs_path, V9fsFidOpenState *fs) -{ - int serrno, fd; - - fs->dir = NULL; - fd = v9fs_request(ctx->private, T_OPEN, NULL, "sd", fs_path, O_DIRECTORY); - if (fd < 0) { - errno = -fd; - return -1; - } - fs->dir = fdopendir(fd); - if (!fs->dir) { - serrno = errno; - close(fd); - errno = serrno; - return -1; - } - return 0; -} - -static void proxy_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) -{ - rewinddir(fs->dir); -} - -static off_t proxy_telldir(FsContext *ctx, V9fsFidOpenState *fs) -{ - return telldir(fs->dir); -} - -static int proxy_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, - struct dirent *entry, - struct dirent **result) -{ - return readdir_r(fs->dir, entry, result); -} - -static void proxy_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) -{ - seekdir(fs->dir, off); -} - -static ssize_t proxy_preadv(FsContext *ctx, V9fsFidOpenState *fs, - const struct iovec *iov, - int iovcnt, off_t offset) -{ - ssize_t ret; -#ifdef CONFIG_PREADV - ret = preadv(fs->fd, iov, iovcnt, offset); -#else - ret = lseek(fs->fd, offset, SEEK_SET); - if (ret >= 0) { - ret = readv(fs->fd, iov, iovcnt); - } -#endif - return ret; -} - -static ssize_t proxy_pwritev(FsContext *ctx, V9fsFidOpenState *fs, - const struct iovec *iov, - int iovcnt, off_t offset) -{ - ssize_t ret; - -#ifdef CONFIG_PREADV - ret = pwritev(fs->fd, iov, iovcnt, offset); -#else - ret = lseek(fs->fd, offset, SEEK_SET); - if (ret >= 0) { - ret = writev(fs->fd, iov, iovcnt); - } -#endif -#ifdef CONFIG_SYNC_FILE_RANGE - if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { - /* - * Initiate a writeback. This is not a data integrity sync. - * We want to ensure that we don't leave dirty pages in the cache - * after write when writeout=immediate is sepcified. - */ - sync_file_range(fs->fd, offset, ret, - SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); - } -#endif - return ret; -} - -static int proxy_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) -{ - int retval; - retval = v9fs_request(fs_ctx->private, T_CHMOD, NULL, "sd", - fs_path, credp->fc_mode); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_mknod(FsContext *fs_ctx, V9fsPath *dir_path, - const char *name, FsCred *credp) -{ - int retval; - V9fsString fullname; - - v9fs_string_init(&fullname); - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - - retval = v9fs_request(fs_ctx->private, T_MKNOD, NULL, "sdqdd", - &fullname, credp->fc_mode, credp->fc_rdev, - credp->fc_uid, credp->fc_gid); - v9fs_string_free(&fullname); - if (retval < 0) { - errno = -retval; - retval = -1; - } - return retval; -} - -static int proxy_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, - const char *name, FsCred *credp) -{ - int retval; - V9fsString fullname; - - v9fs_string_init(&fullname); - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - - retval = v9fs_request(fs_ctx->private, T_MKDIR, NULL, "sddd", &fullname, - credp->fc_mode, credp->fc_uid, credp->fc_gid); - v9fs_string_free(&fullname); - if (retval < 0) { - errno = -retval; - retval = -1; - } - v9fs_string_free(&fullname); - return retval; -} - -static int proxy_fstat(FsContext *fs_ctx, int fid_type, - V9fsFidOpenState *fs, struct stat *stbuf) -{ - int fd; - - if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir); - } else { - fd = fs->fd; - } - return fstat(fd, stbuf); -} - -static int proxy_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, - int flags, FsCred *credp, V9fsFidOpenState *fs) -{ - V9fsString fullname; - - v9fs_string_init(&fullname); - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - - fs->fd = v9fs_request(fs_ctx->private, T_CREATE, NULL, "sdddd", - &fullname, flags, credp->fc_mode, - credp->fc_uid, credp->fc_gid); - v9fs_string_free(&fullname); - if (fs->fd < 0) { - errno = -fs->fd; - fs->fd = -1; - } - return fs->fd; -} - -static int proxy_symlink(FsContext *fs_ctx, const char *oldpath, - V9fsPath *dir_path, const char *name, FsCred *credp) -{ - int retval; - V9fsString fullname, target; - - v9fs_string_init(&fullname); - v9fs_string_init(&target); - - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - v9fs_string_sprintf(&target, "%s", oldpath); - - retval = v9fs_request(fs_ctx->private, T_SYMLINK, NULL, "ssdd", - &target, &fullname, credp->fc_uid, credp->fc_gid); - v9fs_string_free(&fullname); - v9fs_string_free(&target); - if (retval < 0) { - errno = -retval; - retval = -1; - } - return retval; -} - -static int proxy_link(FsContext *ctx, V9fsPath *oldpath, - V9fsPath *dirpath, const char *name) -{ - int retval; - V9fsString newpath; - - v9fs_string_init(&newpath); - v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name); - - retval = v9fs_request(ctx->private, T_LINK, NULL, "ss", oldpath, &newpath); - v9fs_string_free(&newpath); - if (retval < 0) { - errno = -retval; - retval = -1; - } - return retval; -} - -static int proxy_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) -{ - int retval; - - retval = v9fs_request(ctx->private, T_TRUNCATE, NULL, "sq", fs_path, size); - if (retval < 0) { - errno = -retval; - return -1; - } - return 0; -} - -static int proxy_rename(FsContext *ctx, const char *oldpath, - const char *newpath) -{ - int retval; - V9fsString oldname, newname; - - v9fs_string_init(&oldname); - v9fs_string_init(&newname); - - v9fs_string_sprintf(&oldname, "%s", oldpath); - v9fs_string_sprintf(&newname, "%s", newpath); - retval = v9fs_request(ctx->private, T_RENAME, NULL, "ss", - &oldname, &newname); - v9fs_string_free(&oldname); - v9fs_string_free(&newname); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) -{ - int retval; - retval = v9fs_request(fs_ctx->private, T_CHOWN, NULL, "sdd", - fs_path, credp->fc_uid, credp->fc_gid); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_utimensat(FsContext *s, V9fsPath *fs_path, - const struct timespec *buf) -{ - int retval; - retval = v9fs_request(s->private, T_UTIME, NULL, "sqqqq", - fs_path, - buf[0].tv_sec, buf[0].tv_nsec, - buf[1].tv_sec, buf[1].tv_nsec); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_remove(FsContext *ctx, const char *path) -{ - int retval; - V9fsString name; - v9fs_string_init(&name); - v9fs_string_sprintf(&name, "%s", path); - retval = v9fs_request(ctx->private, T_REMOVE, NULL, "s", &name); - v9fs_string_free(&name); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_fsync(FsContext *ctx, int fid_type, - V9fsFidOpenState *fs, int datasync) -{ - int fd; - - if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir); - } else { - fd = fs->fd; - } - - if (datasync) { - return qemu_fdatasync(fd); - } else { - return fsync(fd); - } -} - -static int proxy_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf) -{ - int retval; - retval = v9fs_request(s->private, T_STATFS, stbuf, "s", fs_path); - if (retval < 0) { - errno = -retval; - return -1; - } - return retval; -} - -static ssize_t proxy_lgetxattr(FsContext *ctx, V9fsPath *fs_path, - const char *name, void *value, size_t size) -{ - int retval; - V9fsString xname; - - v9fs_string_init(&xname); - v9fs_string_sprintf(&xname, "%s", name); - retval = v9fs_request(ctx->private, T_LGETXATTR, value, "dss", size, - fs_path, &xname); - v9fs_string_free(&xname); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static ssize_t proxy_llistxattr(FsContext *ctx, V9fsPath *fs_path, - void *value, size_t size) -{ - int retval; - retval = v9fs_request(ctx->private, T_LLISTXATTR, value, "ds", size, - fs_path); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, - void *value, size_t size, int flags) -{ - int retval; - V9fsString xname, xvalue; - - v9fs_string_init(&xname); - v9fs_string_sprintf(&xname, "%s", name); - - v9fs_string_init(&xvalue); - xvalue.size = size; - xvalue.data = g_malloc(size); - memcpy(xvalue.data, value, size); - - retval = v9fs_request(ctx->private, T_LSETXATTR, value, "sssdd", - fs_path, &xname, &xvalue, size, flags); - v9fs_string_free(&xname); - v9fs_string_free(&xvalue); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_lremovexattr(FsContext *ctx, V9fsPath *fs_path, - const char *name) -{ - int retval; - V9fsString xname; - - v9fs_string_init(&xname); - v9fs_string_sprintf(&xname, "%s", name); - retval = v9fs_request(ctx->private, T_LREMOVEXATTR, NULL, "ss", - fs_path, &xname); - v9fs_string_free(&xname); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_name_to_path(FsContext *ctx, V9fsPath *dir_path, - const char *name, V9fsPath *target) -{ - if (dir_path) { - v9fs_string_sprintf((V9fsString *)target, "%s/%s", - dir_path->data, name); - } else { - v9fs_string_sprintf((V9fsString *)target, "%s", name); - } - /* Bump the size for including terminating NULL */ - target->size++; - return 0; -} - -static int proxy_renameat(FsContext *ctx, V9fsPath *olddir, - const char *old_name, V9fsPath *newdir, - const char *new_name) -{ - int ret; - V9fsString old_full_name, new_full_name; - - v9fs_string_init(&old_full_name); - v9fs_string_init(&new_full_name); - - v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name); - v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name); - - ret = proxy_rename(ctx, old_full_name.data, new_full_name.data); - v9fs_string_free(&old_full_name); - v9fs_string_free(&new_full_name); - return ret; -} - -static int proxy_unlinkat(FsContext *ctx, V9fsPath *dir, - const char *name, int flags) -{ - int ret; - V9fsString fullname; - v9fs_string_init(&fullname); - - v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name); - ret = proxy_remove(ctx, fullname.data); - v9fs_string_free(&fullname); - - return ret; -} - -static int proxy_ioc_getversion(FsContext *fs_ctx, V9fsPath *path, - mode_t st_mode, uint64_t *st_gen) -{ - int err; - - /* Do not try to open special files like device nodes, fifos etc - * we can get fd for regular files and directories only - */ - if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { - errno = ENOTTY; - return -1; - } - err = v9fs_request(fs_ctx->private, T_GETVERSION, st_gen, "s", path); - if (err < 0) { - errno = -err; - err = -1; - } - return err; -} - -static int connect_namedsocket(const char *path) -{ - int sockfd, size; - struct sockaddr_un helper; - - if (strlen(path) >= sizeof(helper.sun_path)) { - fprintf(stderr, "Socket name too large\n"); - return -1; - } - sockfd = socket(AF_UNIX, SOCK_STREAM, 0); - if (sockfd < 0) { - fprintf(stderr, "failed to create socket: %s\n", strerror(errno)); - return -1; - } - strcpy(helper.sun_path, path); - helper.sun_family = AF_UNIX; - size = strlen(helper.sun_path) + sizeof(helper.sun_family); - if (connect(sockfd, (struct sockaddr *)&helper, size) < 0) { - fprintf(stderr, "failed to connect to %s: %s\n", path, strerror(errno)); - close(sockfd); - return -1; - } - - /* remove the socket for security reasons */ - unlink(path); - return sockfd; -} - -static int proxy_parse_opts(QemuOpts *opts, struct FsDriverEntry *fs) -{ - const char *socket = qemu_opt_get(opts, "socket"); - const char *sock_fd = qemu_opt_get(opts, "sock_fd"); - - if (!socket && !sock_fd) { - fprintf(stderr, "socket and sock_fd none of the option specified\n"); - return -1; - } - if (socket && sock_fd) { - fprintf(stderr, "Both socket and sock_fd options specified\n"); - return -1; - } - if (socket) { - fs->path = g_strdup(socket); - fs->export_flags = V9FS_PROXY_SOCK_NAME; - } else { - fs->path = g_strdup(sock_fd); - fs->export_flags = V9FS_PROXY_SOCK_FD; - } - return 0; -} - -static int proxy_init(FsContext *ctx) -{ - V9fsProxy *proxy = g_malloc(sizeof(V9fsProxy)); - int sock_id; - - if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) { - sock_id = connect_namedsocket(ctx->fs_root); - } else { - sock_id = atoi(ctx->fs_root); - if (sock_id < 0) { - fprintf(stderr, "socket descriptor not initialized\n"); - } - } - if (sock_id < 0) { - g_free(proxy); - return -1; - } - g_free(ctx->fs_root); - ctx->fs_root = NULL; - - proxy->in_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); - proxy->in_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; - proxy->out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); - proxy->out_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; - - ctx->private = proxy; - proxy->sockfd = sock_id; - qemu_mutex_init(&proxy->mutex); - - ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; - ctx->exops.get_st_gen = proxy_ioc_getversion; - return 0; -} - -FileOperations proxy_ops = { - .parse_opts = proxy_parse_opts, - .init = proxy_init, - .lstat = proxy_lstat, - .readlink = proxy_readlink, - .close = proxy_close, - .closedir = proxy_closedir, - .open = proxy_open, - .opendir = proxy_opendir, - .rewinddir = proxy_rewinddir, - .telldir = proxy_telldir, - .readdir_r = proxy_readdir_r, - .seekdir = proxy_seekdir, - .preadv = proxy_preadv, - .pwritev = proxy_pwritev, - .chmod = proxy_chmod, - .mknod = proxy_mknod, - .mkdir = proxy_mkdir, - .fstat = proxy_fstat, - .open2 = proxy_open2, - .symlink = proxy_symlink, - .link = proxy_link, - .truncate = proxy_truncate, - .rename = proxy_rename, - .chown = proxy_chown, - .utimensat = proxy_utimensat, - .remove = proxy_remove, - .fsync = proxy_fsync, - .statfs = proxy_statfs, - .lgetxattr = proxy_lgetxattr, - .llistxattr = proxy_llistxattr, - .lsetxattr = proxy_lsetxattr, - .lremovexattr = proxy_lremovexattr, - .name_to_path = proxy_name_to_path, - .renameat = proxy_renameat, - .unlinkat = proxy_unlinkat, -}; diff --git a/qemu/hw/9pfs/virtio-9p-proxy.h b/qemu/hw/9pfs/virtio-9p-proxy.h deleted file mode 100644 index 005c1ad75..000000000 --- a/qemu/hw/9pfs/virtio-9p-proxy.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Virtio 9p Proxy callback - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * M. Mohan Kumar - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - */ -#ifndef _QEMU_VIRTIO_9P_PROXY_H -#define _QEMU_VIRTIO_9P_PROXY_H - -#define PROXY_MAX_IO_SZ (64 * 1024) -#define V9FS_FD_VALID INT_MAX - -/* - * proxy iovec only support one element and - * marsha/unmarshal doesn't do little endian conversion. - */ -#define proxy_unmarshal(in_sg, offset, fmt, args...) \ - v9fs_unmarshal(in_sg, 1, offset, 0, fmt, ##args) -#define proxy_marshal(out_sg, offset, fmt, args...) \ - v9fs_marshal(out_sg, 1, offset, 0, fmt, ##args) - -union MsgControl { - struct cmsghdr cmsg; - char control[CMSG_SPACE(sizeof(int))]; -}; - -typedef struct { - uint32_t type; - uint32_t size; -} ProxyHeader; - -#define PROXY_HDR_SZ (sizeof(ProxyHeader)) - -enum { - T_SUCCESS = 0, - T_ERROR, - T_OPEN, - T_CREATE, - T_MKNOD, - T_MKDIR, - T_SYMLINK, - T_LINK, - T_LSTAT, - T_READLINK, - T_STATFS, - T_CHMOD, - T_CHOWN, - T_TRUNCATE, - T_UTIME, - T_RENAME, - T_REMOVE, - T_LGETXATTR, - T_LLISTXATTR, - T_LSETXATTR, - T_LREMOVEXATTR, - T_GETVERSION, -}; - -typedef struct { - uint64_t st_dev; - uint64_t st_ino; - uint64_t st_nlink; - uint32_t st_mode; - uint32_t st_uid; - uint32_t st_gid; - uint64_t st_rdev; - uint64_t st_size; - uint64_t st_blksize; - uint64_t st_blocks; - uint64_t st_atim_sec; - uint64_t st_atim_nsec; - uint64_t st_mtim_sec; - uint64_t st_mtim_nsec; - uint64_t st_ctim_sec; - uint64_t st_ctim_nsec; -} ProxyStat; - -typedef struct { - uint64_t f_type; - uint64_t f_bsize; - uint64_t f_blocks; - uint64_t f_bfree; - uint64_t f_bavail; - uint64_t f_files; - uint64_t f_ffree; - uint64_t f_fsid[2]; - uint64_t f_namelen; - uint64_t f_frsize; -} ProxyStatFS; -#endif diff --git a/qemu/hw/9pfs/virtio-9p-synth.c b/qemu/hw/9pfs/virtio-9p-synth.c deleted file mode 100644 index a0ab9a86a..000000000 --- a/qemu/hw/9pfs/virtio-9p-synth.c +++ /dev/null @@ -1,573 +0,0 @@ -/* - * Virtio 9p synthetic file system support - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Malahal Naineni - * Aneesh Kumar K.V - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include "hw/virtio/virtio.h" -#include "virtio-9p.h" -#include "virtio-9p-xattr.h" -#include "fsdev/qemu-fsdev.h" -#include "virtio-9p-synth.h" -#include "qemu/rcu.h" -#include "qemu/rcu_queue.h" -#include - -/* Root node for synth file system */ -static V9fsSynthNode v9fs_synth_root = { - .name = "/", - .actual_attr = { - .mode = 0555 | S_IFDIR, - .nlink = 1, - }, - .attr = &v9fs_synth_root.actual_attr, -}; - -static QemuMutex v9fs_synth_mutex; -static int v9fs_synth_node_count; -/* set to 1 when the synth fs is ready */ -static int v9fs_synth_fs; - -static V9fsSynthNode *v9fs_add_dir_node(V9fsSynthNode *parent, int mode, - const char *name, - V9fsSynthNodeAttr *attr, int inode) -{ - V9fsSynthNode *node; - - /* Add directory type and remove write bits */ - mode = ((mode & 0777) | S_IFDIR) & ~(S_IWUSR | S_IWGRP | S_IWOTH); - node = g_malloc0(sizeof(V9fsSynthNode)); - if (attr) { - /* We are adding .. or . entries */ - node->attr = attr; - node->attr->nlink++; - } else { - node->attr = &node->actual_attr; - node->attr->inode = inode; - node->attr->nlink = 1; - /* We don't allow write to directories */ - node->attr->mode = mode; - node->attr->write = NULL; - node->attr->read = NULL; - } - node->private = node; - pstrcpy(node->name, sizeof(node->name), name); - QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling); - return node; -} - -int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode, - const char *name, V9fsSynthNode **result) -{ - int ret; - V9fsSynthNode *node, *tmp; - - if (!v9fs_synth_fs) { - return EAGAIN; - } - if (!name || (strlen(name) >= NAME_MAX)) { - return EINVAL; - } - if (!parent) { - parent = &v9fs_synth_root; - } - qemu_mutex_lock(&v9fs_synth_mutex); - QLIST_FOREACH(tmp, &parent->child, sibling) { - if (!strcmp(tmp->name, name)) { - ret = EEXIST; - goto err_out; - } - } - /* Add the name */ - node = v9fs_add_dir_node(parent, mode, name, NULL, v9fs_synth_node_count++); - v9fs_add_dir_node(node, parent->attr->mode, "..", - parent->attr, parent->attr->inode); - v9fs_add_dir_node(node, node->attr->mode, ".", - node->attr, node->attr->inode); - *result = node; - ret = 0; -err_out: - qemu_mutex_unlock(&v9fs_synth_mutex); - return ret; -} - -int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, - const char *name, v9fs_synth_read read, - v9fs_synth_write write, void *arg) -{ - int ret; - V9fsSynthNode *node, *tmp; - - if (!v9fs_synth_fs) { - return EAGAIN; - } - if (!name || (strlen(name) >= NAME_MAX)) { - return EINVAL; - } - if (!parent) { - parent = &v9fs_synth_root; - } - - qemu_mutex_lock(&v9fs_synth_mutex); - QLIST_FOREACH(tmp, &parent->child, sibling) { - if (!strcmp(tmp->name, name)) { - ret = EEXIST; - goto err_out; - } - } - /* Add file type and remove write bits */ - mode = ((mode & 0777) | S_IFREG); - node = g_malloc0(sizeof(V9fsSynthNode)); - node->attr = &node->actual_attr; - node->attr->inode = v9fs_synth_node_count++; - node->attr->nlink = 1; - node->attr->read = read; - node->attr->write = write; - node->attr->mode = mode; - node->private = arg; - pstrcpy(node->name, sizeof(node->name), name); - QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling); - ret = 0; -err_out: - qemu_mutex_unlock(&v9fs_synth_mutex); - return ret; -} - -static void v9fs_synth_fill_statbuf(V9fsSynthNode *node, struct stat *stbuf) -{ - stbuf->st_dev = 0; - stbuf->st_ino = node->attr->inode; - stbuf->st_mode = node->attr->mode; - stbuf->st_nlink = node->attr->nlink; - stbuf->st_uid = 0; - stbuf->st_gid = 0; - stbuf->st_rdev = 0; - stbuf->st_size = 0; - stbuf->st_blksize = 0; - stbuf->st_blocks = 0; - stbuf->st_atime = 0; - stbuf->st_mtime = 0; - stbuf->st_ctime = 0; -} - -static int v9fs_synth_lstat(FsContext *fs_ctx, - V9fsPath *fs_path, struct stat *stbuf) -{ - V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data; - - v9fs_synth_fill_statbuf(node, stbuf); - return 0; -} - -static int v9fs_synth_fstat(FsContext *fs_ctx, int fid_type, - V9fsFidOpenState *fs, struct stat *stbuf) -{ - V9fsSynthOpenState *synth_open = fs->private; - v9fs_synth_fill_statbuf(synth_open->node, stbuf); - return 0; -} - -static int v9fs_synth_opendir(FsContext *ctx, - V9fsPath *fs_path, V9fsFidOpenState *fs) -{ - V9fsSynthOpenState *synth_open; - V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data; - - synth_open = g_malloc(sizeof(*synth_open)); - synth_open->node = node; - node->open_count++; - fs->private = synth_open; - return 0; -} - -static int v9fs_synth_closedir(FsContext *ctx, V9fsFidOpenState *fs) -{ - V9fsSynthOpenState *synth_open = fs->private; - V9fsSynthNode *node = synth_open->node; - - node->open_count--; - g_free(synth_open); - fs->private = NULL; - return 0; -} - -static off_t v9fs_synth_telldir(FsContext *ctx, V9fsFidOpenState *fs) -{ - V9fsSynthOpenState *synth_open = fs->private; - return synth_open->offset; -} - -static void v9fs_synth_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) -{ - V9fsSynthOpenState *synth_open = fs->private; - synth_open->offset = off; -} - -static void v9fs_synth_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) -{ - v9fs_synth_seekdir(ctx, fs, 0); -} - -static void v9fs_synth_direntry(V9fsSynthNode *node, - struct dirent *entry, off_t off) -{ - strcpy(entry->d_name, node->name); - entry->d_ino = node->attr->inode; - entry->d_off = off + 1; -} - -static int v9fs_synth_get_dentry(V9fsSynthNode *dir, struct dirent *entry, - struct dirent **result, off_t off) -{ - int i = 0; - V9fsSynthNode *node; - - rcu_read_lock(); - QLIST_FOREACH(node, &dir->child, sibling) { - /* This is the off child of the directory */ - if (i == off) { - break; - } - i++; - } - rcu_read_unlock(); - if (!node) { - /* end of directory */ - *result = NULL; - return 0; - } - v9fs_synth_direntry(node, entry, off); - *result = entry; - return 0; -} - -static int v9fs_synth_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, - struct dirent *entry, struct dirent **result) -{ - int ret; - V9fsSynthOpenState *synth_open = fs->private; - V9fsSynthNode *node = synth_open->node; - ret = v9fs_synth_get_dentry(node, entry, result, synth_open->offset); - if (!ret && *result != NULL) { - synth_open->offset++; - } - return ret; -} - -static int v9fs_synth_open(FsContext *ctx, V9fsPath *fs_path, - int flags, V9fsFidOpenState *fs) -{ - V9fsSynthOpenState *synth_open; - V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data; - - synth_open = g_malloc(sizeof(*synth_open)); - synth_open->node = node; - node->open_count++; - fs->private = synth_open; - return 0; -} - -static int v9fs_synth_open2(FsContext *fs_ctx, V9fsPath *dir_path, - const char *name, int flags, - FsCred *credp, V9fsFidOpenState *fs) -{ - errno = ENOSYS; - return -1; -} - -static int v9fs_synth_close(FsContext *ctx, V9fsFidOpenState *fs) -{ - V9fsSynthOpenState *synth_open = fs->private; - V9fsSynthNode *node = synth_open->node; - - node->open_count--; - g_free(synth_open); - fs->private = NULL; - return 0; -} - -static ssize_t v9fs_synth_pwritev(FsContext *ctx, V9fsFidOpenState *fs, - const struct iovec *iov, - int iovcnt, off_t offset) -{ - int i, count = 0, wcount; - V9fsSynthOpenState *synth_open = fs->private; - V9fsSynthNode *node = synth_open->node; - if (!node->attr->write) { - errno = EPERM; - return -1; - } - for (i = 0; i < iovcnt; i++) { - wcount = node->attr->write(iov[i].iov_base, iov[i].iov_len, - offset, node->private); - offset += wcount; - count += wcount; - /* If we wrote less than requested. we are done */ - if (wcount < iov[i].iov_len) { - break; - } - } - return count; -} - -static ssize_t v9fs_synth_preadv(FsContext *ctx, V9fsFidOpenState *fs, - const struct iovec *iov, - int iovcnt, off_t offset) -{ - int i, count = 0, rcount; - V9fsSynthOpenState *synth_open = fs->private; - V9fsSynthNode *node = synth_open->node; - if (!node->attr->read) { - errno = EPERM; - return -1; - } - for (i = 0; i < iovcnt; i++) { - rcount = node->attr->read(iov[i].iov_base, iov[i].iov_len, - offset, node->private); - offset += rcount; - count += rcount; - /* If we read less than requested. we are done */ - if (rcount < iov[i].iov_len) { - break; - } - } - return count; -} - -static int v9fs_synth_truncate(FsContext *ctx, V9fsPath *path, off_t offset) -{ - errno = ENOSYS; - return -1; -} - -static int v9fs_synth_chmod(FsContext *fs_ctx, V9fsPath *path, FsCred *credp) -{ - errno = EPERM; - return -1; -} - -static int v9fs_synth_mknod(FsContext *fs_ctx, V9fsPath *path, - const char *buf, FsCred *credp) -{ - errno = EPERM; - return -1; -} - -static int v9fs_synth_mkdir(FsContext *fs_ctx, V9fsPath *path, - const char *buf, FsCred *credp) -{ - errno = EPERM; - return -1; -} - -static ssize_t v9fs_synth_readlink(FsContext *fs_ctx, V9fsPath *path, - char *buf, size_t bufsz) -{ - errno = ENOSYS; - return -1; -} - -static int v9fs_synth_symlink(FsContext *fs_ctx, const char *oldpath, - V9fsPath *newpath, const char *buf, FsCred *credp) -{ - errno = EPERM; - return -1; -} - -static int v9fs_synth_link(FsContext *fs_ctx, V9fsPath *oldpath, - V9fsPath *newpath, const char *buf) -{ - errno = EPERM; - return -1; -} - -static int v9fs_synth_rename(FsContext *ctx, const char *oldpath, - const char *newpath) -{ - errno = EPERM; - return -1; -} - -static int v9fs_synth_chown(FsContext *fs_ctx, V9fsPath *path, FsCred *credp) -{ - errno = EPERM; - return -1; -} - -static int v9fs_synth_utimensat(FsContext *fs_ctx, V9fsPath *path, - const struct timespec *buf) -{ - errno = EPERM; - return 0; -} - -static int v9fs_synth_remove(FsContext *ctx, const char *path) -{ - errno = EPERM; - return -1; -} - -static int v9fs_synth_fsync(FsContext *ctx, int fid_type, - V9fsFidOpenState *fs, int datasync) -{ - errno = ENOSYS; - return 0; -} - -static int v9fs_synth_statfs(FsContext *s, V9fsPath *fs_path, - struct statfs *stbuf) -{ - stbuf->f_type = 0xABCD; - stbuf->f_bsize = 512; - stbuf->f_blocks = 0; - stbuf->f_files = v9fs_synth_node_count; - stbuf->f_namelen = NAME_MAX; - return 0; -} - -static ssize_t v9fs_synth_lgetxattr(FsContext *ctx, V9fsPath *path, - const char *name, void *value, size_t size) -{ - errno = ENOTSUP; - return -1; -} - -static ssize_t v9fs_synth_llistxattr(FsContext *ctx, V9fsPath *path, - void *value, size_t size) -{ - errno = ENOTSUP; - return -1; -} - -static int v9fs_synth_lsetxattr(FsContext *ctx, V9fsPath *path, - const char *name, void *value, - size_t size, int flags) -{ - errno = ENOTSUP; - return -1; -} - -static int v9fs_synth_lremovexattr(FsContext *ctx, - V9fsPath *path, const char *name) -{ - errno = ENOTSUP; - return -1; -} - -static int v9fs_synth_name_to_path(FsContext *ctx, V9fsPath *dir_path, - const char *name, V9fsPath *target) -{ - V9fsSynthNode *node; - V9fsSynthNode *dir_node; - - /* "." and ".." are not allowed */ - if (!strcmp(name, ".") || !strcmp(name, "..")) { - errno = EINVAL; - return -1; - - } - if (!dir_path) { - dir_node = &v9fs_synth_root; - } else { - dir_node = *(V9fsSynthNode **)dir_path->data; - } - if (!strcmp(name, "/")) { - node = dir_node; - goto out; - } - /* search for the name in the childern */ - rcu_read_lock(); - QLIST_FOREACH(node, &dir_node->child, sibling) { - if (!strcmp(node->name, name)) { - break; - } - } - rcu_read_unlock(); - - if (!node) { - errno = ENOENT; - return -1; - } -out: - /* Copy the node pointer to fid */ - target->data = g_malloc(sizeof(void *)); - memcpy(target->data, &node, sizeof(void *)); - target->size = sizeof(void *); - return 0; -} - -static int v9fs_synth_renameat(FsContext *ctx, V9fsPath *olddir, - const char *old_name, V9fsPath *newdir, - const char *new_name) -{ - errno = EPERM; - return -1; -} - -static int v9fs_synth_unlinkat(FsContext *ctx, V9fsPath *dir, - const char *name, int flags) -{ - errno = EPERM; - return -1; -} - -static int v9fs_synth_init(FsContext *ctx) -{ - QLIST_INIT(&v9fs_synth_root.child); - qemu_mutex_init(&v9fs_synth_mutex); - - /* Add "." and ".." entries for root */ - v9fs_add_dir_node(&v9fs_synth_root, v9fs_synth_root.attr->mode, - "..", v9fs_synth_root.attr, v9fs_synth_root.attr->inode); - v9fs_add_dir_node(&v9fs_synth_root, v9fs_synth_root.attr->mode, - ".", v9fs_synth_root.attr, v9fs_synth_root.attr->inode); - - /* Mark the subsystem is ready for use */ - v9fs_synth_fs = 1; - return 0; -} - -FileOperations synth_ops = { - .init = v9fs_synth_init, - .lstat = v9fs_synth_lstat, - .readlink = v9fs_synth_readlink, - .close = v9fs_synth_close, - .closedir = v9fs_synth_closedir, - .open = v9fs_synth_open, - .opendir = v9fs_synth_opendir, - .rewinddir = v9fs_synth_rewinddir, - .telldir = v9fs_synth_telldir, - .readdir_r = v9fs_synth_readdir_r, - .seekdir = v9fs_synth_seekdir, - .preadv = v9fs_synth_preadv, - .pwritev = v9fs_synth_pwritev, - .chmod = v9fs_synth_chmod, - .mknod = v9fs_synth_mknod, - .mkdir = v9fs_synth_mkdir, - .fstat = v9fs_synth_fstat, - .open2 = v9fs_synth_open2, - .symlink = v9fs_synth_symlink, - .link = v9fs_synth_link, - .truncate = v9fs_synth_truncate, - .rename = v9fs_synth_rename, - .chown = v9fs_synth_chown, - .utimensat = v9fs_synth_utimensat, - .remove = v9fs_synth_remove, - .fsync = v9fs_synth_fsync, - .statfs = v9fs_synth_statfs, - .lgetxattr = v9fs_synth_lgetxattr, - .llistxattr = v9fs_synth_llistxattr, - .lsetxattr = v9fs_synth_lsetxattr, - .lremovexattr = v9fs_synth_lremovexattr, - .name_to_path = v9fs_synth_name_to_path, - .renameat = v9fs_synth_renameat, - .unlinkat = v9fs_synth_unlinkat, -}; diff --git a/qemu/hw/9pfs/virtio-9p-synth.h b/qemu/hw/9pfs/virtio-9p-synth.h deleted file mode 100644 index ab05a8e78..000000000 --- a/qemu/hw/9pfs/virtio-9p-synth.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Virtio 9p - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Aneesh Kumar K.V - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ -#ifndef HW_9PFS_VIRTIO9P_SYNTH_H -#define HW_9PFS_VIRTIO9P_SYNTH_H 1 - -#include -#include -#include - -typedef struct V9fsSynthNode V9fsSynthNode; -typedef ssize_t (*v9fs_synth_read)(void *buf, int len, off_t offset, - void *arg); -typedef ssize_t (*v9fs_synth_write)(void *buf, int len, off_t offset, - void *arg); -typedef struct V9fsSynthNodeAttr { - int mode; - int inode; - int nlink; - v9fs_synth_read read; - v9fs_synth_write write; -} V9fsSynthNodeAttr; - -struct V9fsSynthNode { - QLIST_HEAD(, V9fsSynthNode) child; - QLIST_ENTRY(V9fsSynthNode) sibling; - char name[NAME_MAX]; - V9fsSynthNodeAttr *attr; - V9fsSynthNodeAttr actual_attr; - void *private; - int open_count; -}; - -typedef struct V9fsSynthOpenState { - off_t offset; - V9fsSynthNode *node; -} V9fsSynthOpenState; - -extern int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode, - const char *name, V9fsSynthNode **result); -extern int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, - const char *name, v9fs_synth_read read, - v9fs_synth_write write, void *arg); - -#endif diff --git a/qemu/hw/9pfs/virtio-9p-xattr-user.c b/qemu/hw/9pfs/virtio-9p-xattr-user.c deleted file mode 100644 index 46133e06d..000000000 --- a/qemu/hw/9pfs/virtio-9p-xattr-user.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Virtio 9p user. xattr callback - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Aneesh Kumar K.V - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include -#include "hw/virtio/virtio.h" -#include "virtio-9p.h" -#include "fsdev/file-op-9p.h" -#include "virtio-9p-xattr.h" - - -static ssize_t mp_user_getxattr(FsContext *ctx, const char *path, - const char *name, void *value, size_t size) -{ - char *buffer; - ssize_t ret; - - if (strncmp(name, "user.virtfs.", 12) == 0) { - /* - * Don't allow fetch of user.virtfs namesapce - * in case of mapped security - */ - errno = ENOATTR; - return -1; - } - buffer = rpath(ctx, path); - ret = lgetxattr(buffer, name, value, size); - g_free(buffer); - return ret; -} - -static ssize_t mp_user_listxattr(FsContext *ctx, const char *path, - char *name, void *value, size_t size) -{ - int name_size = strlen(name) + 1; - if (strncmp(name, "user.virtfs.", 12) == 0) { - - /* check if it is a mapped posix acl */ - if (strncmp(name, "user.virtfs.system.posix_acl_", 29) == 0) { - /* adjust the name and size */ - name += 12; - name_size -= 12; - } else { - /* - * Don't allow fetch of user.virtfs namesapce - * in case of mapped security - */ - return 0; - } - } - if (!value) { - return name_size; - } - - if (size < name_size) { - errno = ERANGE; - return -1; - } - - /* name_size includes the trailing NUL. */ - memcpy(value, name, name_size); - return name_size; -} - -static int mp_user_setxattr(FsContext *ctx, const char *path, const char *name, - void *value, size_t size, int flags) -{ - char *buffer; - int ret; - - if (strncmp(name, "user.virtfs.", 12) == 0) { - /* - * Don't allow fetch of user.virtfs namesapce - * in case of mapped security - */ - errno = EACCES; - return -1; - } - buffer = rpath(ctx, path); - ret = lsetxattr(buffer, name, value, size, flags); - g_free(buffer); - return ret; -} - -static int mp_user_removexattr(FsContext *ctx, - const char *path, const char *name) -{ - char *buffer; - int ret; - - if (strncmp(name, "user.virtfs.", 12) == 0) { - /* - * Don't allow fetch of user.virtfs namesapce - * in case of mapped security - */ - errno = EACCES; - return -1; - } - buffer = rpath(ctx, path); - ret = lremovexattr(buffer, name); - g_free(buffer); - return ret; -} - -XattrOperations mapped_user_xattr = { - .name = "user.", - .getxattr = mp_user_getxattr, - .setxattr = mp_user_setxattr, - .listxattr = mp_user_listxattr, - .removexattr = mp_user_removexattr, -}; - -XattrOperations passthrough_user_xattr = { - .name = "user.", - .getxattr = pt_getxattr, - .setxattr = pt_setxattr, - .listxattr = pt_listxattr, - .removexattr = pt_removexattr, -}; diff --git a/qemu/hw/9pfs/virtio-9p-xattr.c b/qemu/hw/9pfs/virtio-9p-xattr.c deleted file mode 100644 index 07183887c..000000000 --- a/qemu/hw/9pfs/virtio-9p-xattr.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Virtio 9p xattr callback - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Aneesh Kumar K.V - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include "hw/virtio/virtio.h" -#include "virtio-9p.h" -#include "fsdev/file-op-9p.h" -#include "virtio-9p-xattr.h" - - -static XattrOperations *get_xattr_operations(XattrOperations **h, - const char *name) -{ - XattrOperations *xops; - for (xops = *(h)++; xops != NULL; xops = *(h)++) { - if (!strncmp(name, xops->name, strlen(xops->name))) { - return xops; - } - } - return NULL; -} - -ssize_t v9fs_get_xattr(FsContext *ctx, const char *path, - const char *name, void *value, size_t size) -{ - XattrOperations *xops = get_xattr_operations(ctx->xops, name); - if (xops) { - return xops->getxattr(ctx, path, name, value, size); - } - errno = EOPNOTSUPP; - return -1; -} - -ssize_t pt_listxattr(FsContext *ctx, const char *path, - char *name, void *value, size_t size) -{ - int name_size = strlen(name) + 1; - if (!value) { - return name_size; - } - - if (size < name_size) { - errno = ERANGE; - return -1; - } - - /* no need for strncpy: name_size is strlen(name)+1 */ - memcpy(value, name, name_size); - return name_size; -} - - -/* - * Get the list and pass to each layer to find out whether - * to send the data or not - */ -ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, - void *value, size_t vsize) -{ - ssize_t size = 0; - char *buffer; - void *ovalue = value; - XattrOperations *xops; - char *orig_value, *orig_value_start; - ssize_t xattr_len, parsed_len = 0, attr_len; - - /* Get the actual len */ - buffer = rpath(ctx, path); - xattr_len = llistxattr(buffer, value, 0); - if (xattr_len <= 0) { - g_free(buffer); - return xattr_len; - } - - /* Now fetch the xattr and find the actual size */ - orig_value = g_malloc(xattr_len); - xattr_len = llistxattr(buffer, orig_value, xattr_len); - g_free(buffer); - - /* store the orig pointer */ - orig_value_start = orig_value; - while (xattr_len > parsed_len) { - xops = get_xattr_operations(ctx->xops, orig_value); - if (!xops) { - goto next_entry; - } - - if (!value) { - size += xops->listxattr(ctx, path, orig_value, value, vsize); - } else { - size = xops->listxattr(ctx, path, orig_value, value, vsize); - if (size < 0) { - goto err_out; - } - value += size; - vsize -= size; - } -next_entry: - /* Got the next entry */ - attr_len = strlen(orig_value) + 1; - parsed_len += attr_len; - orig_value += attr_len; - } - if (value) { - size = value - ovalue; - } - -err_out: - g_free(orig_value_start); - return size; -} - -int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name, - void *value, size_t size, int flags) -{ - XattrOperations *xops = get_xattr_operations(ctx->xops, name); - if (xops) { - return xops->setxattr(ctx, path, name, value, size, flags); - } - errno = EOPNOTSUPP; - return -1; - -} - -int v9fs_remove_xattr(FsContext *ctx, - const char *path, const char *name) -{ - XattrOperations *xops = get_xattr_operations(ctx->xops, name); - if (xops) { - return xops->removexattr(ctx, path, name); - } - errno = EOPNOTSUPP; - return -1; - -} - -XattrOperations *mapped_xattr_ops[] = { - &mapped_user_xattr, - &mapped_pacl_xattr, - &mapped_dacl_xattr, - NULL, -}; - -XattrOperations *passthrough_xattr_ops[] = { - &passthrough_user_xattr, - &passthrough_acl_xattr, - NULL, -}; - -/* for .user none model should be same as passthrough */ -XattrOperations *none_xattr_ops[] = { - &passthrough_user_xattr, - &none_acl_xattr, - NULL, -}; diff --git a/qemu/hw/9pfs/virtio-9p-xattr.h b/qemu/hw/9pfs/virtio-9p-xattr.h deleted file mode 100644 index 327b32b5a..000000000 --- a/qemu/hw/9pfs/virtio-9p-xattr.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Virtio 9p - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Aneesh Kumar K.V - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ -#ifndef _QEMU_VIRTIO_9P_XATTR_H -#define _QEMU_VIRTIO_9P_XATTR_H - -#include "qemu/xattr.h" - -typedef struct xattr_operations -{ - const char *name; - ssize_t (*getxattr)(FsContext *ctx, const char *path, - const char *name, void *value, size_t size); - ssize_t (*listxattr)(FsContext *ctx, const char *path, - char *name, void *value, size_t size); - int (*setxattr)(FsContext *ctx, const char *path, const char *name, - void *value, size_t size, int flags); - int (*removexattr)(FsContext *ctx, - const char *path, const char *name); -} XattrOperations; - - -extern XattrOperations mapped_user_xattr; -extern XattrOperations passthrough_user_xattr; - -extern XattrOperations mapped_pacl_xattr; -extern XattrOperations mapped_dacl_xattr; -extern XattrOperations passthrough_acl_xattr; -extern XattrOperations none_acl_xattr; - -extern XattrOperations *mapped_xattr_ops[]; -extern XattrOperations *passthrough_xattr_ops[]; -extern XattrOperations *none_xattr_ops[]; - -ssize_t v9fs_get_xattr(FsContext *ctx, const char *path, const char *name, - void *value, size_t size); -ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, void *value, - size_t vsize); -int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name, - void *value, size_t size, int flags); -int v9fs_remove_xattr(FsContext *ctx, const char *path, const char *name); -ssize_t pt_listxattr(FsContext *ctx, const char *path, char *name, void *value, - size_t size); - -static inline ssize_t pt_getxattr(FsContext *ctx, const char *path, - const char *name, void *value, size_t size) -{ - char *buffer; - ssize_t ret; - - buffer = rpath(ctx, path); - ret = lgetxattr(buffer, name, value, size); - g_free(buffer); - return ret; -} - -static inline int pt_setxattr(FsContext *ctx, const char *path, - const char *name, void *value, - size_t size, int flags) -{ - char *buffer; - int ret; - - buffer = rpath(ctx, path); - ret = lsetxattr(buffer, name, value, size, flags); - g_free(buffer); - return ret; -} - -static inline int pt_removexattr(FsContext *ctx, - const char *path, const char *name) -{ - char *buffer; - int ret; - - buffer = rpath(ctx, path); - ret = lremovexattr(path, name); - g_free(buffer); - return ret; -} - -static inline ssize_t notsup_getxattr(FsContext *ctx, const char *path, - const char *name, void *value, - size_t size) -{ - errno = ENOTSUP; - return -1; -} - -static inline int notsup_setxattr(FsContext *ctx, const char *path, - const char *name, void *value, - size_t size, int flags) -{ - errno = ENOTSUP; - return -1; -} - -static inline ssize_t notsup_listxattr(FsContext *ctx, const char *path, - char *name, void *value, size_t size) -{ - return 0; -} - -static inline int notsup_removexattr(FsContext *ctx, - const char *path, const char *name) -{ - errno = ENOTSUP; - return -1; -} - -#endif diff --git a/qemu/hw/9pfs/virtio-9p.c b/qemu/hw/9pfs/virtio-9p.c deleted file mode 100644 index f972731f5..000000000 --- a/qemu/hw/9pfs/virtio-9p.c +++ /dev/null @@ -1,3300 +0,0 @@ -/* - * Virtio 9p backend - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include "hw/virtio/virtio.h" -#include "hw/i386/pc.h" -#include "qemu/error-report.h" -#include "qemu/iov.h" -#include "qemu/sockets.h" -#include "virtio-9p.h" -#include "fsdev/qemu-fsdev.h" -#include "virtio-9p-xattr.h" -#include "virtio-9p-coth.h" -#include "trace.h" -#include "migration/migration.h" - -int open_fd_hw; -int total_open_fd; -static int open_fd_rc; - -enum { - Oread = 0x00, - Owrite = 0x01, - Ordwr = 0x02, - Oexec = 0x03, - Oexcl = 0x04, - Otrunc = 0x10, - Orexec = 0x20, - Orclose = 0x40, - Oappend = 0x80, -}; - -static int omode_to_uflags(int8_t mode) -{ - int ret = 0; - - switch (mode & 3) { - case Oread: - ret = O_RDONLY; - break; - case Ordwr: - ret = O_RDWR; - break; - case Owrite: - ret = O_WRONLY; - break; - case Oexec: - ret = O_RDONLY; - break; - } - - if (mode & Otrunc) { - ret |= O_TRUNC; - } - - if (mode & Oappend) { - ret |= O_APPEND; - } - - if (mode & Oexcl) { - ret |= O_EXCL; - } - - return ret; -} - -struct dotl_openflag_map { - int dotl_flag; - int open_flag; -}; - -static int dotl_to_open_flags(int flags) -{ - int i; - /* - * We have same bits for P9_DOTL_READONLY, P9_DOTL_WRONLY - * and P9_DOTL_NOACCESS - */ - int oflags = flags & O_ACCMODE; - - struct dotl_openflag_map dotl_oflag_map[] = { - { P9_DOTL_CREATE, O_CREAT }, - { P9_DOTL_EXCL, O_EXCL }, - { P9_DOTL_NOCTTY , O_NOCTTY }, - { P9_DOTL_TRUNC, O_TRUNC }, - { P9_DOTL_APPEND, O_APPEND }, - { P9_DOTL_NONBLOCK, O_NONBLOCK } , - { P9_DOTL_DSYNC, O_DSYNC }, - { P9_DOTL_FASYNC, FASYNC }, - { P9_DOTL_DIRECT, O_DIRECT }, - { P9_DOTL_LARGEFILE, O_LARGEFILE }, - { P9_DOTL_DIRECTORY, O_DIRECTORY }, - { P9_DOTL_NOFOLLOW, O_NOFOLLOW }, - { P9_DOTL_NOATIME, O_NOATIME }, - { P9_DOTL_SYNC, O_SYNC }, - }; - - for (i = 0; i < ARRAY_SIZE(dotl_oflag_map); i++) { - if (flags & dotl_oflag_map[i].dotl_flag) { - oflags |= dotl_oflag_map[i].open_flag; - } - } - - return oflags; -} - -void cred_init(FsCred *credp) -{ - credp->fc_uid = -1; - credp->fc_gid = -1; - credp->fc_mode = -1; - credp->fc_rdev = -1; -} - -static int get_dotl_openflags(V9fsState *s, int oflags) -{ - int flags; - /* - * Filter the client open flags - */ - flags = dotl_to_open_flags(oflags); - flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT); - /* - * Ignore direct disk access hint until the server supports it. - */ - flags &= ~O_DIRECT; - return flags; -} - -void v9fs_path_init(V9fsPath *path) -{ - path->data = NULL; - path->size = 0; -} - -void v9fs_path_free(V9fsPath *path) -{ - g_free(path->data); - path->data = NULL; - path->size = 0; -} - -void v9fs_path_copy(V9fsPath *lhs, V9fsPath *rhs) -{ - v9fs_path_free(lhs); - lhs->data = g_malloc(rhs->size); - memcpy(lhs->data, rhs->data, rhs->size); - lhs->size = rhs->size; -} - -int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath, - const char *name, V9fsPath *path) -{ - int err; - err = s->ops->name_to_path(&s->ctx, dirpath, name, path); - if (err < 0) { - err = -errno; - } - return err; -} - -/* - * Return TRUE if s1 is an ancestor of s2. - * - * E.g. "a/b" is an ancestor of "a/b/c" but not of "a/bc/d". - * As a special case, We treat s1 as ancestor of s2 if they are same! - */ -static int v9fs_path_is_ancestor(V9fsPath *s1, V9fsPath *s2) -{ - if (!strncmp(s1->data, s2->data, s1->size - 1)) { - if (s2->data[s1->size - 1] == '\0' || s2->data[s1->size - 1] == '/') { - return 1; - } - } - return 0; -} - -static size_t v9fs_string_size(V9fsString *str) -{ - return str->size; -} - -/* - * returns 0 if fid got re-opened, 1 if not, < 0 on error */ -static int v9fs_reopen_fid(V9fsPDU *pdu, V9fsFidState *f) -{ - int err = 1; - if (f->fid_type == P9_FID_FILE) { - if (f->fs.fd == -1) { - do { - err = v9fs_co_open(pdu, f, f->open_flags); - } while (err == -EINTR && !pdu->cancelled); - } - } else if (f->fid_type == P9_FID_DIR) { - if (f->fs.dir == NULL) { - do { - err = v9fs_co_opendir(pdu, f); - } while (err == -EINTR && !pdu->cancelled); - } - } - return err; -} - -static V9fsFidState *get_fid(V9fsPDU *pdu, int32_t fid) -{ - int err; - V9fsFidState *f; - V9fsState *s = pdu->s; - - for (f = s->fid_list; f; f = f->next) { - BUG_ON(f->clunked); - if (f->fid == fid) { - /* - * Update the fid ref upfront so that - * we don't get reclaimed when we yield - * in open later. - */ - f->ref++; - /* - * check whether we need to reopen the - * file. We might have closed the fd - * while trying to free up some file - * descriptors. - */ - err = v9fs_reopen_fid(pdu, f); - if (err < 0) { - f->ref--; - return NULL; - } - /* - * Mark the fid as referenced so that the LRU - * reclaim won't close the file descriptor - */ - f->flags |= FID_REFERENCED; - return f; - } - } - return NULL; -} - -static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid) -{ - V9fsFidState *f; - - for (f = s->fid_list; f; f = f->next) { - /* If fid is already there return NULL */ - BUG_ON(f->clunked); - if (f->fid == fid) { - return NULL; - } - } - f = g_malloc0(sizeof(V9fsFidState)); - f->fid = fid; - f->fid_type = P9_FID_NONE; - f->ref = 1; - /* - * Mark the fid as referenced so that the LRU - * reclaim won't close the file descriptor - */ - f->flags |= FID_REFERENCED; - f->next = s->fid_list; - s->fid_list = f; - - return f; -} - -static int v9fs_xattr_fid_clunk(V9fsPDU *pdu, V9fsFidState *fidp) -{ - int retval = 0; - - if (fidp->fs.xattr.copied_len == -1) { - /* getxattr/listxattr fid */ - goto free_value; - } - /* - * if this is fid for setxattr. clunk should - * result in setxattr localcall - */ - if (fidp->fs.xattr.len != fidp->fs.xattr.copied_len) { - /* clunk after partial write */ - retval = -EINVAL; - goto free_out; - } - if (fidp->fs.xattr.len) { - retval = v9fs_co_lsetxattr(pdu, &fidp->path, &fidp->fs.xattr.name, - fidp->fs.xattr.value, - fidp->fs.xattr.len, - fidp->fs.xattr.flags); - } else { - retval = v9fs_co_lremovexattr(pdu, &fidp->path, &fidp->fs.xattr.name); - } -free_out: - v9fs_string_free(&fidp->fs.xattr.name); -free_value: - g_free(fidp->fs.xattr.value); - return retval; -} - -static int free_fid(V9fsPDU *pdu, V9fsFidState *fidp) -{ - int retval = 0; - - if (fidp->fid_type == P9_FID_FILE) { - /* If we reclaimed the fd no need to close */ - if (fidp->fs.fd != -1) { - retval = v9fs_co_close(pdu, &fidp->fs); - } - } else if (fidp->fid_type == P9_FID_DIR) { - if (fidp->fs.dir != NULL) { - retval = v9fs_co_closedir(pdu, &fidp->fs); - } - } else if (fidp->fid_type == P9_FID_XATTR) { - retval = v9fs_xattr_fid_clunk(pdu, fidp); - } - v9fs_path_free(&fidp->path); - g_free(fidp); - return retval; -} - -static int put_fid(V9fsPDU *pdu, V9fsFidState *fidp) -{ - BUG_ON(!fidp->ref); - fidp->ref--; - /* - * Don't free the fid if it is in reclaim list - */ - if (!fidp->ref && fidp->clunked) { - if (fidp->fid == pdu->s->root_fid) { - /* - * if the clunked fid is root fid then we - * have unmounted the fs on the client side. - * delete the migration blocker. Ideally, this - * should be hooked to transport close notification - */ - if (pdu->s->migration_blocker) { - migrate_del_blocker(pdu->s->migration_blocker); - error_free(pdu->s->migration_blocker); - pdu->s->migration_blocker = NULL; - } - } - return free_fid(pdu, fidp); - } - return 0; -} - -static V9fsFidState *clunk_fid(V9fsState *s, int32_t fid) -{ - V9fsFidState **fidpp, *fidp; - - for (fidpp = &s->fid_list; *fidpp; fidpp = &(*fidpp)->next) { - if ((*fidpp)->fid == fid) { - break; - } - } - if (*fidpp == NULL) { - return NULL; - } - fidp = *fidpp; - *fidpp = fidp->next; - fidp->clunked = 1; - return fidp; -} - -void v9fs_reclaim_fd(V9fsPDU *pdu) -{ - int reclaim_count = 0; - V9fsState *s = pdu->s; - V9fsFidState *f, *reclaim_list = NULL; - - for (f = s->fid_list; f; f = f->next) { - /* - * Unlink fids cannot be reclaimed. Check - * for them and skip them. Also skip fids - * currently being operated on. - */ - if (f->ref || f->flags & FID_NON_RECLAIMABLE) { - continue; - } - /* - * if it is a recently referenced fid - * we leave the fid untouched and clear the - * reference bit. We come back to it later - * in the next iteration. (a simple LRU without - * moving list elements around) - */ - if (f->flags & FID_REFERENCED) { - f->flags &= ~FID_REFERENCED; - continue; - } - /* - * Add fids to reclaim list. - */ - if (f->fid_type == P9_FID_FILE) { - if (f->fs.fd != -1) { - /* - * Up the reference count so that - * a clunk request won't free this fid - */ - f->ref++; - f->rclm_lst = reclaim_list; - reclaim_list = f; - f->fs_reclaim.fd = f->fs.fd; - f->fs.fd = -1; - reclaim_count++; - } - } else if (f->fid_type == P9_FID_DIR) { - if (f->fs.dir != NULL) { - /* - * Up the reference count so that - * a clunk request won't free this fid - */ - f->ref++; - f->rclm_lst = reclaim_list; - reclaim_list = f; - f->fs_reclaim.dir = f->fs.dir; - f->fs.dir = NULL; - reclaim_count++; - } - } - if (reclaim_count >= open_fd_rc) { - break; - } - } - /* - * Now close the fid in reclaim list. Free them if they - * are already clunked. - */ - while (reclaim_list) { - f = reclaim_list; - reclaim_list = f->rclm_lst; - if (f->fid_type == P9_FID_FILE) { - v9fs_co_close(pdu, &f->fs_reclaim); - } else if (f->fid_type == P9_FID_DIR) { - v9fs_co_closedir(pdu, &f->fs_reclaim); - } - f->rclm_lst = NULL; - /* - * Now drop the fid reference, free it - * if clunked. - */ - put_fid(pdu, f); - } -} - -static int v9fs_mark_fids_unreclaim(V9fsPDU *pdu, V9fsPath *path) -{ - int err; - V9fsState *s = pdu->s; - V9fsFidState *fidp, head_fid; - - head_fid.next = s->fid_list; - for (fidp = s->fid_list; fidp; fidp = fidp->next) { - if (fidp->path.size != path->size) { - continue; - } - if (!memcmp(fidp->path.data, path->data, path->size)) { - /* Mark the fid non reclaimable. */ - fidp->flags |= FID_NON_RECLAIMABLE; - - /* reopen the file/dir if already closed */ - err = v9fs_reopen_fid(pdu, fidp); - if (err < 0) { - return -1; - } - /* - * Go back to head of fid list because - * the list could have got updated when - * switched to the worker thread - */ - if (err == 0) { - fidp = &head_fid; - } - } - } - return 0; -} - -static void virtfs_reset(V9fsPDU *pdu) -{ - V9fsState *s = pdu->s; - V9fsFidState *fidp = NULL; - - /* Free all fids */ - while (s->fid_list) { - fidp = s->fid_list; - s->fid_list = fidp->next; - - if (fidp->ref) { - fidp->clunked = 1; - } else { - free_fid(pdu, fidp); - } - } - if (fidp) { - /* One or more unclunked fids found... */ - error_report("9pfs:%s: One or more uncluncked fids " - "found during reset", __func__); - } -} - -#define P9_QID_TYPE_DIR 0x80 -#define P9_QID_TYPE_SYMLINK 0x02 - -#define P9_STAT_MODE_DIR 0x80000000 -#define P9_STAT_MODE_APPEND 0x40000000 -#define P9_STAT_MODE_EXCL 0x20000000 -#define P9_STAT_MODE_MOUNT 0x10000000 -#define P9_STAT_MODE_AUTH 0x08000000 -#define P9_STAT_MODE_TMP 0x04000000 -#define P9_STAT_MODE_SYMLINK 0x02000000 -#define P9_STAT_MODE_LINK 0x01000000 -#define P9_STAT_MODE_DEVICE 0x00800000 -#define P9_STAT_MODE_NAMED_PIPE 0x00200000 -#define P9_STAT_MODE_SOCKET 0x00100000 -#define P9_STAT_MODE_SETUID 0x00080000 -#define P9_STAT_MODE_SETGID 0x00040000 -#define P9_STAT_MODE_SETVTX 0x00010000 - -#define P9_STAT_MODE_TYPE_BITS (P9_STAT_MODE_DIR | \ - P9_STAT_MODE_SYMLINK | \ - P9_STAT_MODE_LINK | \ - P9_STAT_MODE_DEVICE | \ - P9_STAT_MODE_NAMED_PIPE | \ - P9_STAT_MODE_SOCKET) - -/* This is the algorithm from ufs in spfs */ -static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp) -{ - size_t size; - - memset(&qidp->path, 0, sizeof(qidp->path)); - size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path)); - memcpy(&qidp->path, &stbuf->st_ino, size); - qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8); - qidp->type = 0; - if (S_ISDIR(stbuf->st_mode)) { - qidp->type |= P9_QID_TYPE_DIR; - } - if (S_ISLNK(stbuf->st_mode)) { - qidp->type |= P9_QID_TYPE_SYMLINK; - } -} - -static int fid_to_qid(V9fsPDU *pdu, V9fsFidState *fidp, V9fsQID *qidp) -{ - struct stat stbuf; - int err; - - err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); - if (err < 0) { - return err; - } - stat_to_qid(&stbuf, qidp); - return 0; -} - -static V9fsPDU *alloc_pdu(V9fsState *s) -{ - V9fsPDU *pdu = NULL; - - if (!QLIST_EMPTY(&s->free_list)) { - pdu = QLIST_FIRST(&s->free_list); - QLIST_REMOVE(pdu, next); - QLIST_INSERT_HEAD(&s->active_list, pdu, next); - } - return pdu; -} - -static void free_pdu(V9fsState *s, V9fsPDU *pdu) -{ - if (pdu) { - /* - * Cancelled pdu are added back to the freelist - * by flush request . - */ - if (!pdu->cancelled) { - QLIST_REMOVE(pdu, next); - QLIST_INSERT_HEAD(&s->free_list, pdu, next); - } - } -} - -/* - * We don't do error checking for pdu_marshal/unmarshal here - * because we always expect to have enough space to encode - * error details - */ -static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len) -{ - int8_t id = pdu->id + 1; /* Response */ - - if (len < 0) { - int err = -len; - len = 7; - - if (s->proto_version != V9FS_PROTO_2000L) { - V9fsString str; - - str.data = strerror(err); - str.size = strlen(str.data); - - len += pdu_marshal(pdu, len, "s", &str); - id = P9_RERROR; - } - - len += pdu_marshal(pdu, len, "d", err); - - if (s->proto_version == V9FS_PROTO_2000L) { - id = P9_RLERROR; - } - trace_v9fs_rerror(pdu->tag, pdu->id, err); /* Trace ERROR */ - } - - /* fill out the header */ - pdu_marshal(pdu, 0, "dbw", (int32_t)len, id, pdu->tag); - - /* keep these in sync */ - pdu->size = len; - pdu->id = id; - - /* push onto queue and notify */ - virtqueue_push(s->vq, &pdu->elem, len); - - /* FIXME: we should batch these completions */ - virtio_notify(VIRTIO_DEVICE(s), s->vq); - - /* Now wakeup anybody waiting in flush for this request */ - qemu_co_queue_next(&pdu->complete); - - free_pdu(s, pdu); -} - -static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension) -{ - mode_t ret; - - ret = mode & 0777; - if (mode & P9_STAT_MODE_DIR) { - ret |= S_IFDIR; - } - - if (mode & P9_STAT_MODE_SYMLINK) { - ret |= S_IFLNK; - } - if (mode & P9_STAT_MODE_SOCKET) { - ret |= S_IFSOCK; - } - if (mode & P9_STAT_MODE_NAMED_PIPE) { - ret |= S_IFIFO; - } - if (mode & P9_STAT_MODE_DEVICE) { - if (extension->size && extension->data[0] == 'c') { - ret |= S_IFCHR; - } else { - ret |= S_IFBLK; - } - } - - if (!(ret&~0777)) { - ret |= S_IFREG; - } - - if (mode & P9_STAT_MODE_SETUID) { - ret |= S_ISUID; - } - if (mode & P9_STAT_MODE_SETGID) { - ret |= S_ISGID; - } - if (mode & P9_STAT_MODE_SETVTX) { - ret |= S_ISVTX; - } - - return ret; -} - -static int donttouch_stat(V9fsStat *stat) -{ - if (stat->type == -1 && - stat->dev == -1 && - stat->qid.type == -1 && - stat->qid.version == -1 && - stat->qid.path == -1 && - stat->mode == -1 && - stat->atime == -1 && - stat->mtime == -1 && - stat->length == -1 && - !stat->name.size && - !stat->uid.size && - !stat->gid.size && - !stat->muid.size && - stat->n_uid == -1 && - stat->n_gid == -1 && - stat->n_muid == -1) { - return 1; - } - - return 0; -} - -static void v9fs_stat_init(V9fsStat *stat) -{ - v9fs_string_init(&stat->name); - v9fs_string_init(&stat->uid); - v9fs_string_init(&stat->gid); - v9fs_string_init(&stat->muid); - v9fs_string_init(&stat->extension); -} - -static void v9fs_stat_free(V9fsStat *stat) -{ - v9fs_string_free(&stat->name); - v9fs_string_free(&stat->uid); - v9fs_string_free(&stat->gid); - v9fs_string_free(&stat->muid); - v9fs_string_free(&stat->extension); -} - -static uint32_t stat_to_v9mode(const struct stat *stbuf) -{ - uint32_t mode; - - mode = stbuf->st_mode & 0777; - if (S_ISDIR(stbuf->st_mode)) { - mode |= P9_STAT_MODE_DIR; - } - - if (S_ISLNK(stbuf->st_mode)) { - mode |= P9_STAT_MODE_SYMLINK; - } - - if (S_ISSOCK(stbuf->st_mode)) { - mode |= P9_STAT_MODE_SOCKET; - } - - if (S_ISFIFO(stbuf->st_mode)) { - mode |= P9_STAT_MODE_NAMED_PIPE; - } - - if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) { - mode |= P9_STAT_MODE_DEVICE; - } - - if (stbuf->st_mode & S_ISUID) { - mode |= P9_STAT_MODE_SETUID; - } - - if (stbuf->st_mode & S_ISGID) { - mode |= P9_STAT_MODE_SETGID; - } - - if (stbuf->st_mode & S_ISVTX) { - mode |= P9_STAT_MODE_SETVTX; - } - - return mode; -} - -static int stat_to_v9stat(V9fsPDU *pdu, V9fsPath *name, - const struct stat *stbuf, - V9fsStat *v9stat) -{ - int err; - const char *str; - - memset(v9stat, 0, sizeof(*v9stat)); - - stat_to_qid(stbuf, &v9stat->qid); - v9stat->mode = stat_to_v9mode(stbuf); - v9stat->atime = stbuf->st_atime; - v9stat->mtime = stbuf->st_mtime; - v9stat->length = stbuf->st_size; - - v9fs_string_null(&v9stat->uid); - v9fs_string_null(&v9stat->gid); - v9fs_string_null(&v9stat->muid); - - v9stat->n_uid = stbuf->st_uid; - v9stat->n_gid = stbuf->st_gid; - v9stat->n_muid = 0; - - v9fs_string_null(&v9stat->extension); - - if (v9stat->mode & P9_STAT_MODE_SYMLINK) { - err = v9fs_co_readlink(pdu, name, &v9stat->extension); - if (err < 0) { - return err; - } - } else if (v9stat->mode & P9_STAT_MODE_DEVICE) { - v9fs_string_sprintf(&v9stat->extension, "%c %u %u", - S_ISCHR(stbuf->st_mode) ? 'c' : 'b', - major(stbuf->st_rdev), minor(stbuf->st_rdev)); - } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) { - v9fs_string_sprintf(&v9stat->extension, "%s %lu", - "HARDLINKCOUNT", (unsigned long)stbuf->st_nlink); - } - - str = strrchr(name->data, '/'); - if (str) { - str += 1; - } else { - str = name->data; - } - - v9fs_string_sprintf(&v9stat->name, "%s", str); - - v9stat->size = 61 + - v9fs_string_size(&v9stat->name) + - v9fs_string_size(&v9stat->uid) + - v9fs_string_size(&v9stat->gid) + - v9fs_string_size(&v9stat->muid) + - v9fs_string_size(&v9stat->extension); - return 0; -} - -#define P9_STATS_MODE 0x00000001ULL -#define P9_STATS_NLINK 0x00000002ULL -#define P9_STATS_UID 0x00000004ULL -#define P9_STATS_GID 0x00000008ULL -#define P9_STATS_RDEV 0x00000010ULL -#define P9_STATS_ATIME 0x00000020ULL -#define P9_STATS_MTIME 0x00000040ULL -#define P9_STATS_CTIME 0x00000080ULL -#define P9_STATS_INO 0x00000100ULL -#define P9_STATS_SIZE 0x00000200ULL -#define P9_STATS_BLOCKS 0x00000400ULL - -#define P9_STATS_BTIME 0x00000800ULL -#define P9_STATS_GEN 0x00001000ULL -#define P9_STATS_DATA_VERSION 0x00002000ULL - -#define P9_STATS_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */ -#define P9_STATS_ALL 0x00003fffULL /* Mask for All fields above */ - - -static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf, - V9fsStatDotl *v9lstat) -{ - memset(v9lstat, 0, sizeof(*v9lstat)); - - v9lstat->st_mode = stbuf->st_mode; - v9lstat->st_nlink = stbuf->st_nlink; - v9lstat->st_uid = stbuf->st_uid; - v9lstat->st_gid = stbuf->st_gid; - v9lstat->st_rdev = stbuf->st_rdev; - v9lstat->st_size = stbuf->st_size; - v9lstat->st_blksize = stbuf->st_blksize; - v9lstat->st_blocks = stbuf->st_blocks; - v9lstat->st_atime_sec = stbuf->st_atime; - v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec; - v9lstat->st_mtime_sec = stbuf->st_mtime; - v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec; - v9lstat->st_ctime_sec = stbuf->st_ctime; - v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec; - /* Currently we only support BASIC fields in stat */ - v9lstat->st_result_mask = P9_STATS_BASIC; - - stat_to_qid(stbuf, &v9lstat->qid); -} - -static void print_sg(struct iovec *sg, int cnt) -{ - int i; - - printf("sg[%d]: {", cnt); - for (i = 0; i < cnt; i++) { - if (i) { - printf(", "); - } - printf("(%p, %zd)", sg[i].iov_base, sg[i].iov_len); - } - printf("}\n"); -} - -/* Will call this only for path name based fid */ -static void v9fs_fix_path(V9fsPath *dst, V9fsPath *src, int len) -{ - V9fsPath str; - v9fs_path_init(&str); - v9fs_path_copy(&str, dst); - v9fs_string_sprintf((V9fsString *)dst, "%s%s", src->data, str.data+len); - v9fs_path_free(&str); - /* +1 to include terminating NULL */ - dst->size++; -} - -static inline bool is_ro_export(FsContext *ctx) -{ - return ctx->export_flags & V9FS_RDONLY; -} - -static void v9fs_version(void *opaque) -{ - ssize_t err; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - V9fsString version; - size_t offset = 7; - - v9fs_string_init(&version); - err = pdu_unmarshal(pdu, offset, "ds", &s->msize, &version); - if (err < 0) { - offset = err; - goto out; - } - trace_v9fs_version(pdu->tag, pdu->id, s->msize, version.data); - - virtfs_reset(pdu); - - if (!strcmp(version.data, "9P2000.u")) { - s->proto_version = V9FS_PROTO_2000U; - } else if (!strcmp(version.data, "9P2000.L")) { - s->proto_version = V9FS_PROTO_2000L; - } else { - v9fs_string_sprintf(&version, "unknown"); - } - - err = pdu_marshal(pdu, offset, "ds", s->msize, &version); - if (err < 0) { - offset = err; - goto out; - } - offset += err; - trace_v9fs_version_return(pdu->tag, pdu->id, s->msize, version.data); -out: - complete_pdu(s, pdu, offset); - v9fs_string_free(&version); -} - -static void v9fs_attach(void *opaque) -{ - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - int32_t fid, afid, n_uname; - V9fsString uname, aname; - V9fsFidState *fidp; - size_t offset = 7; - V9fsQID qid; - ssize_t err; - - v9fs_string_init(&uname); - v9fs_string_init(&aname); - err = pdu_unmarshal(pdu, offset, "ddssd", &fid, - &afid, &uname, &aname, &n_uname); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_attach(pdu->tag, pdu->id, fid, afid, uname.data, aname.data); - - fidp = alloc_fid(s, fid); - if (fidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - fidp->uid = n_uname; - err = v9fs_co_name_to_path(pdu, NULL, "/", &fidp->path); - if (err < 0) { - err = -EINVAL; - clunk_fid(s, fid); - goto out; - } - err = fid_to_qid(pdu, fidp, &qid); - if (err < 0) { - err = -EINVAL; - clunk_fid(s, fid); - goto out; - } - err = pdu_marshal(pdu, offset, "Q", &qid); - if (err < 0) { - clunk_fid(s, fid); - goto out; - } - err += offset; - trace_v9fs_attach_return(pdu->tag, pdu->id, - qid.type, qid.version, qid.path); - /* - * disable migration if we haven't done already. - * attach could get called multiple times for the same export. - */ - if (!s->migration_blocker) { - s->root_fid = fid; - error_setg(&s->migration_blocker, - "Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'", - s->ctx.fs_root ? s->ctx.fs_root : "NULL", s->tag); - migrate_add_blocker(s->migration_blocker); - } -out: - put_fid(pdu, fidp); -out_nofid: - complete_pdu(s, pdu, err); - v9fs_string_free(&uname); - v9fs_string_free(&aname); -} - -static void v9fs_stat(void *opaque) -{ - int32_t fid; - V9fsStat v9stat; - ssize_t err = 0; - size_t offset = 7; - struct stat stbuf; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - err = pdu_unmarshal(pdu, offset, "d", &fid); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_stat(pdu->tag, pdu->id, fid); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); - if (err < 0) { - goto out; - } - err = stat_to_v9stat(pdu, &fidp->path, &stbuf, &v9stat); - if (err < 0) { - goto out; - } - err = pdu_marshal(pdu, offset, "wS", 0, &v9stat); - if (err < 0) { - v9fs_stat_free(&v9stat); - goto out; - } - trace_v9fs_stat_return(pdu->tag, pdu->id, v9stat.mode, - v9stat.atime, v9stat.mtime, v9stat.length); - err += offset; - v9fs_stat_free(&v9stat); -out: - put_fid(pdu, fidp); -out_nofid: - complete_pdu(s, pdu, err); -} - -static void v9fs_getattr(void *opaque) -{ - int32_t fid; - size_t offset = 7; - ssize_t retval = 0; - struct stat stbuf; - V9fsFidState *fidp; - uint64_t request_mask; - V9fsStatDotl v9stat_dotl; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - retval = pdu_unmarshal(pdu, offset, "dq", &fid, &request_mask); - if (retval < 0) { - goto out_nofid; - } - trace_v9fs_getattr(pdu->tag, pdu->id, fid, request_mask); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - retval = -ENOENT; - goto out_nofid; - } - /* - * Currently we only support BASIC fields in stat, so there is no - * need to look at request_mask. - */ - retval = v9fs_co_lstat(pdu, &fidp->path, &stbuf); - if (retval < 0) { - goto out; - } - stat_to_v9stat_dotl(s, &stbuf, &v9stat_dotl); - - /* fill st_gen if requested and supported by underlying fs */ - if (request_mask & P9_STATS_GEN) { - retval = v9fs_co_st_gen(pdu, &fidp->path, stbuf.st_mode, &v9stat_dotl); - switch (retval) { - case 0: - /* we have valid st_gen: update result mask */ - v9stat_dotl.st_result_mask |= P9_STATS_GEN; - break; - case -EINTR: - /* request cancelled, e.g. by Tflush */ - goto out; - default: - /* failed to get st_gen: not fatal, ignore */ - break; - } - } - retval = pdu_marshal(pdu, offset, "A", &v9stat_dotl); - if (retval < 0) { - goto out; - } - retval += offset; - trace_v9fs_getattr_return(pdu->tag, pdu->id, v9stat_dotl.st_result_mask, - v9stat_dotl.st_mode, v9stat_dotl.st_uid, - v9stat_dotl.st_gid); -out: - put_fid(pdu, fidp); -out_nofid: - complete_pdu(s, pdu, retval); -} - -/* Attribute flags */ -#define P9_ATTR_MODE (1 << 0) -#define P9_ATTR_UID (1 << 1) -#define P9_ATTR_GID (1 << 2) -#define P9_ATTR_SIZE (1 << 3) -#define P9_ATTR_ATIME (1 << 4) -#define P9_ATTR_MTIME (1 << 5) -#define P9_ATTR_CTIME (1 << 6) -#define P9_ATTR_ATIME_SET (1 << 7) -#define P9_ATTR_MTIME_SET (1 << 8) - -#define P9_ATTR_MASK 127 - -static void v9fs_setattr(void *opaque) -{ - int err = 0; - int32_t fid; - V9fsFidState *fidp; - size_t offset = 7; - V9fsIattr v9iattr; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - err = pdu_unmarshal(pdu, offset, "dI", &fid, &v9iattr); - if (err < 0) { - goto out_nofid; - } - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - if (v9iattr.valid & P9_ATTR_MODE) { - err = v9fs_co_chmod(pdu, &fidp->path, v9iattr.mode); - if (err < 0) { - goto out; - } - } - if (v9iattr.valid & (P9_ATTR_ATIME | P9_ATTR_MTIME)) { - struct timespec times[2]; - if (v9iattr.valid & P9_ATTR_ATIME) { - if (v9iattr.valid & P9_ATTR_ATIME_SET) { - times[0].tv_sec = v9iattr.atime_sec; - times[0].tv_nsec = v9iattr.atime_nsec; - } else { - times[0].tv_nsec = UTIME_NOW; - } - } else { - times[0].tv_nsec = UTIME_OMIT; - } - if (v9iattr.valid & P9_ATTR_MTIME) { - if (v9iattr.valid & P9_ATTR_MTIME_SET) { - times[1].tv_sec = v9iattr.mtime_sec; - times[1].tv_nsec = v9iattr.mtime_nsec; - } else { - times[1].tv_nsec = UTIME_NOW; - } - } else { - times[1].tv_nsec = UTIME_OMIT; - } - err = v9fs_co_utimensat(pdu, &fidp->path, times); - if (err < 0) { - goto out; - } - } - /* - * If the only valid entry in iattr is ctime we can call - * chown(-1,-1) to update the ctime of the file - */ - if ((v9iattr.valid & (P9_ATTR_UID | P9_ATTR_GID)) || - ((v9iattr.valid & P9_ATTR_CTIME) - && !((v9iattr.valid & P9_ATTR_MASK) & ~P9_ATTR_CTIME))) { - if (!(v9iattr.valid & P9_ATTR_UID)) { - v9iattr.uid = -1; - } - if (!(v9iattr.valid & P9_ATTR_GID)) { - v9iattr.gid = -1; - } - err = v9fs_co_chown(pdu, &fidp->path, v9iattr.uid, - v9iattr.gid); - if (err < 0) { - goto out; - } - } - if (v9iattr.valid & (P9_ATTR_SIZE)) { - err = v9fs_co_truncate(pdu, &fidp->path, v9iattr.size); - if (err < 0) { - goto out; - } - } - err = offset; -out: - put_fid(pdu, fidp); -out_nofid: - complete_pdu(s, pdu, err); -} - -static int v9fs_walk_marshal(V9fsPDU *pdu, uint16_t nwnames, V9fsQID *qids) -{ - int i; - ssize_t err; - size_t offset = 7; - - err = pdu_marshal(pdu, offset, "w", nwnames); - if (err < 0) { - return err; - } - offset += err; - for (i = 0; i < nwnames; i++) { - err = pdu_marshal(pdu, offset, "Q", &qids[i]); - if (err < 0) { - return err; - } - offset += err; - } - return offset; -} - -static void v9fs_walk(void *opaque) -{ - int name_idx; - V9fsQID *qids = NULL; - int i, err = 0; - V9fsPath dpath, path; - uint16_t nwnames; - struct stat stbuf; - size_t offset = 7; - int32_t fid, newfid; - V9fsString *wnames = NULL; - V9fsFidState *fidp; - V9fsFidState *newfidp = NULL; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - err = pdu_unmarshal(pdu, offset, "ddw", &fid, &newfid, &nwnames); - if (err < 0) { - complete_pdu(s, pdu, err); - return ; - } - offset += err; - - trace_v9fs_walk(pdu->tag, pdu->id, fid, newfid, nwnames); - - if (nwnames && nwnames <= P9_MAXWELEM) { - wnames = g_malloc0(sizeof(wnames[0]) * nwnames); - qids = g_malloc0(sizeof(qids[0]) * nwnames); - for (i = 0; i < nwnames; i++) { - err = pdu_unmarshal(pdu, offset, "s", &wnames[i]); - if (err < 0) { - goto out_nofid; - } - offset += err; - } - } else if (nwnames > P9_MAXWELEM) { - err = -EINVAL; - goto out_nofid; - } - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - v9fs_path_init(&dpath); - v9fs_path_init(&path); - /* - * Both dpath and path initially poin to fidp. - * Needed to handle request with nwnames == 0 - */ - v9fs_path_copy(&dpath, &fidp->path); - v9fs_path_copy(&path, &fidp->path); - for (name_idx = 0; name_idx < nwnames; name_idx++) { - err = v9fs_co_name_to_path(pdu, &dpath, wnames[name_idx].data, &path); - if (err < 0) { - goto out; - } - err = v9fs_co_lstat(pdu, &path, &stbuf); - if (err < 0) { - goto out; - } - stat_to_qid(&stbuf, &qids[name_idx]); - v9fs_path_copy(&dpath, &path); - } - if (fid == newfid) { - BUG_ON(fidp->fid_type != P9_FID_NONE); - v9fs_path_copy(&fidp->path, &path); - } else { - newfidp = alloc_fid(s, newfid); - if (newfidp == NULL) { - err = -EINVAL; - goto out; - } - newfidp->uid = fidp->uid; - v9fs_path_copy(&newfidp->path, &path); - } - err = v9fs_walk_marshal(pdu, nwnames, qids); - trace_v9fs_walk_return(pdu->tag, pdu->id, nwnames, qids); -out: - put_fid(pdu, fidp); - if (newfidp) { - put_fid(pdu, newfidp); - } - v9fs_path_free(&dpath); - v9fs_path_free(&path); -out_nofid: - complete_pdu(s, pdu, err); - if (nwnames && nwnames <= P9_MAXWELEM) { - for (name_idx = 0; name_idx < nwnames; name_idx++) { - v9fs_string_free(&wnames[name_idx]); - } - g_free(wnames); - g_free(qids); - } -} - -static int32_t get_iounit(V9fsPDU *pdu, V9fsPath *path) -{ - struct statfs stbuf; - int32_t iounit = 0; - V9fsState *s = pdu->s; - - /* - * iounit should be multiples of f_bsize (host filesystem block size - * and as well as less than (client msize - P9_IOHDRSZ)) - */ - if (!v9fs_co_statfs(pdu, path, &stbuf)) { - iounit = stbuf.f_bsize; - iounit *= (s->msize - P9_IOHDRSZ)/stbuf.f_bsize; - } - if (!iounit) { - iounit = s->msize - P9_IOHDRSZ; - } - return iounit; -} - -static void v9fs_open(void *opaque) -{ - int flags; - int32_t fid; - int32_t mode; - V9fsQID qid; - int iounit = 0; - ssize_t err = 0; - size_t offset = 7; - struct stat stbuf; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - if (s->proto_version == V9FS_PROTO_2000L) { - err = pdu_unmarshal(pdu, offset, "dd", &fid, &mode); - } else { - uint8_t modebyte; - err = pdu_unmarshal(pdu, offset, "db", &fid, &modebyte); - mode = modebyte; - } - if (err < 0) { - goto out_nofid; - } - trace_v9fs_open(pdu->tag, pdu->id, fid, mode); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - BUG_ON(fidp->fid_type != P9_FID_NONE); - - err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); - if (err < 0) { - goto out; - } - stat_to_qid(&stbuf, &qid); - if (S_ISDIR(stbuf.st_mode)) { - err = v9fs_co_opendir(pdu, fidp); - if (err < 0) { - goto out; - } - fidp->fid_type = P9_FID_DIR; - err = pdu_marshal(pdu, offset, "Qd", &qid, 0); - if (err < 0) { - goto out; - } - err += offset; - } else { - if (s->proto_version == V9FS_PROTO_2000L) { - flags = get_dotl_openflags(s, mode); - } else { - flags = omode_to_uflags(mode); - } - if (is_ro_export(&s->ctx)) { - if (mode & O_WRONLY || mode & O_RDWR || - mode & O_APPEND || mode & O_TRUNC) { - err = -EROFS; - goto out; - } - } - err = v9fs_co_open(pdu, fidp, flags); - if (err < 0) { - goto out; - } - fidp->fid_type = P9_FID_FILE; - fidp->open_flags = flags; - if (flags & O_EXCL) { - /* - * We let the host file system do O_EXCL check - * We should not reclaim such fd - */ - fidp->flags |= FID_NON_RECLAIMABLE; - } - iounit = get_iounit(pdu, &fidp->path); - err = pdu_marshal(pdu, offset, "Qd", &qid, iounit); - if (err < 0) { - goto out; - } - err += offset; - } - trace_v9fs_open_return(pdu->tag, pdu->id, - qid.type, qid.version, qid.path, iounit); -out: - put_fid(pdu, fidp); -out_nofid: - complete_pdu(s, pdu, err); -} - -static void v9fs_lcreate(void *opaque) -{ - int32_t dfid, flags, mode; - gid_t gid; - ssize_t err = 0; - ssize_t offset = 7; - V9fsString name; - V9fsFidState *fidp; - struct stat stbuf; - V9fsQID qid; - int32_t iounit; - V9fsPDU *pdu = opaque; - - v9fs_string_init(&name); - err = pdu_unmarshal(pdu, offset, "dsddd", &dfid, - &name, &flags, &mode, &gid); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_lcreate(pdu->tag, pdu->id, dfid, flags, mode, gid); - - fidp = get_fid(pdu, dfid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - - flags = get_dotl_openflags(pdu->s, flags); - err = v9fs_co_open2(pdu, fidp, &name, gid, - flags | O_CREAT, mode, &stbuf); - if (err < 0) { - goto out; - } - fidp->fid_type = P9_FID_FILE; - fidp->open_flags = flags; - if (flags & O_EXCL) { - /* - * We let the host file system do O_EXCL check - * We should not reclaim such fd - */ - fidp->flags |= FID_NON_RECLAIMABLE; - } - iounit = get_iounit(pdu, &fidp->path); - stat_to_qid(&stbuf, &qid); - err = pdu_marshal(pdu, offset, "Qd", &qid, iounit); - if (err < 0) { - goto out; - } - err += offset; - trace_v9fs_lcreate_return(pdu->tag, pdu->id, - qid.type, qid.version, qid.path, iounit); -out: - put_fid(pdu, fidp); -out_nofid: - complete_pdu(pdu->s, pdu, err); - v9fs_string_free(&name); -} - -static void v9fs_fsync(void *opaque) -{ - int err; - int32_t fid; - int datasync; - size_t offset = 7; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - err = pdu_unmarshal(pdu, offset, "dd", &fid, &datasync); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_fsync(pdu->tag, pdu->id, fid, datasync); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - err = v9fs_co_fsync(pdu, fidp, datasync); - if (!err) { - err = offset; - } - put_fid(pdu, fidp); -out_nofid: - complete_pdu(s, pdu, err); -} - -static void v9fs_clunk(void *opaque) -{ - int err; - int32_t fid; - size_t offset = 7; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - err = pdu_unmarshal(pdu, offset, "d", &fid); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_clunk(pdu->tag, pdu->id, fid); - - fidp = clunk_fid(s, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - /* - * Bump the ref so that put_fid will - * free the fid. - */ - fidp->ref++; - err = put_fid(pdu, fidp); - if (!err) { - err = offset; - } -out_nofid: - complete_pdu(s, pdu, err); -} - -static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, - uint64_t off, uint32_t max_count) -{ - ssize_t err; - size_t offset = 7; - int read_count; - int64_t xattr_len; - - xattr_len = fidp->fs.xattr.len; - read_count = xattr_len - off; - if (read_count > max_count) { - read_count = max_count; - } else if (read_count < 0) { - /* - * read beyond XATTR value - */ - read_count = 0; - } - err = pdu_marshal(pdu, offset, "d", read_count); - if (err < 0) { - return err; - } - offset += err; - err = v9fs_pack(pdu->elem.in_sg, pdu->elem.in_num, offset, - ((char *)fidp->fs.xattr.value) + off, - read_count); - if (err < 0) { - return err; - } - offset += err; - return offset; -} - -static int v9fs_do_readdir_with_stat(V9fsPDU *pdu, - V9fsFidState *fidp, uint32_t max_count) -{ - V9fsPath path; - V9fsStat v9stat; - int len, err = 0; - int32_t count = 0; - struct stat stbuf; - off_t saved_dir_pos; - struct dirent *dent, *result; - - /* save the directory position */ - saved_dir_pos = v9fs_co_telldir(pdu, fidp); - if (saved_dir_pos < 0) { - return saved_dir_pos; - } - - dent = g_malloc(sizeof(struct dirent)); - - while (1) { - v9fs_path_init(&path); - err = v9fs_co_readdir_r(pdu, fidp, dent, &result); - if (err || !result) { - break; - } - err = v9fs_co_name_to_path(pdu, &fidp->path, dent->d_name, &path); - if (err < 0) { - goto out; - } - err = v9fs_co_lstat(pdu, &path, &stbuf); - if (err < 0) { - goto out; - } - err = stat_to_v9stat(pdu, &path, &stbuf, &v9stat); - if (err < 0) { - goto out; - } - /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */ - len = pdu_marshal(pdu, 11 + count, "S", &v9stat); - if ((len != (v9stat.size + 2)) || ((count + len) > max_count)) { - /* Ran out of buffer. Set dir back to old position and return */ - v9fs_co_seekdir(pdu, fidp, saved_dir_pos); - v9fs_stat_free(&v9stat); - v9fs_path_free(&path); - g_free(dent); - return count; - } - count += len; - v9fs_stat_free(&v9stat); - v9fs_path_free(&path); - saved_dir_pos = dent->d_off; - } -out: - g_free(dent); - v9fs_path_free(&path); - if (err < 0) { - return err; - } - return count; -} - -/* - * Create a QEMUIOVector for a sub-region of PDU iovecs - * - * @qiov: uninitialized QEMUIOVector - * @skip: number of bytes to skip from beginning of PDU - * @size: number of bytes to include - * @is_write: true - write, false - read - * - * The resulting QEMUIOVector has heap-allocated iovecs and must be cleaned up - * with qemu_iovec_destroy(). - */ -static void v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu, - size_t skip, size_t size, - bool is_write) -{ - QEMUIOVector elem; - struct iovec *iov; - unsigned int niov; - - if (is_write) { - iov = pdu->elem.out_sg; - niov = pdu->elem.out_num; - } else { - iov = pdu->elem.in_sg; - niov = pdu->elem.in_num; - } - - qemu_iovec_init_external(&elem, iov, niov); - qemu_iovec_init(qiov, niov); - qemu_iovec_concat(qiov, &elem, skip, size); -} - -static void v9fs_read(void *opaque) -{ - int32_t fid; - uint64_t off; - ssize_t err = 0; - int32_t count = 0; - size_t offset = 7; - uint32_t max_count; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - err = pdu_unmarshal(pdu, offset, "dqd", &fid, &off, &max_count); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_read(pdu->tag, pdu->id, fid, off, max_count); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - if (fidp->fid_type == P9_FID_DIR) { - - if (off == 0) { - v9fs_co_rewinddir(pdu, fidp); - } - count = v9fs_do_readdir_with_stat(pdu, fidp, max_count); - if (count < 0) { - err = count; - goto out; - } - err = pdu_marshal(pdu, offset, "d", count); - if (err < 0) { - goto out; - } - err += offset + count; - } else if (fidp->fid_type == P9_FID_FILE) { - QEMUIOVector qiov_full; - QEMUIOVector qiov; - int32_t len; - - v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset + 4, max_count, false); - qemu_iovec_init(&qiov, qiov_full.niov); - do { - qemu_iovec_reset(&qiov); - qemu_iovec_concat(&qiov, &qiov_full, count, qiov_full.size - count); - if (0) { - print_sg(qiov.iov, qiov.niov); - } - /* Loop in case of EINTR */ - do { - len = v9fs_co_preadv(pdu, fidp, qiov.iov, qiov.niov, off); - if (len >= 0) { - off += len; - count += len; - } - } while (len == -EINTR && !pdu->cancelled); - if (len < 0) { - /* IO error return the error */ - err = len; - goto out; - } - } while (count < max_count && len > 0); - err = pdu_marshal(pdu, offset, "d", count); - if (err < 0) { - goto out; - } - err += offset + count; - qemu_iovec_destroy(&qiov); - qemu_iovec_destroy(&qiov_full); - } else if (fidp->fid_type == P9_FID_XATTR) { - err = v9fs_xattr_read(s, pdu, fidp, off, max_count); - } else { - err = -EINVAL; - } - trace_v9fs_read_return(pdu->tag, pdu->id, count, err); -out: - put_fid(pdu, fidp); -out_nofid: - complete_pdu(s, pdu, err); -} - -static size_t v9fs_readdir_data_size(V9fsString *name) -{ - /* - * Size of each dirent on the wire: size of qid (13) + size of offset (8) - * size of type (1) + size of name.size (2) + strlen(name.data) - */ - return 24 + v9fs_string_size(name); -} - -static int v9fs_do_readdir(V9fsPDU *pdu, - V9fsFidState *fidp, int32_t max_count) -{ - size_t size; - V9fsQID qid; - V9fsString name; - int len, err = 0; - int32_t count = 0; - off_t saved_dir_pos; - struct dirent *dent, *result; - - /* save the directory position */ - saved_dir_pos = v9fs_co_telldir(pdu, fidp); - if (saved_dir_pos < 0) { - return saved_dir_pos; - } - - dent = g_malloc(sizeof(struct dirent)); - - while (1) { - err = v9fs_co_readdir_r(pdu, fidp, dent, &result); - if (err || !result) { - break; - } - v9fs_string_init(&name); - v9fs_string_sprintf(&name, "%s", dent->d_name); - if ((count + v9fs_readdir_data_size(&name)) > max_count) { - /* Ran out of buffer. Set dir back to old position and return */ - v9fs_co_seekdir(pdu, fidp, saved_dir_pos); - v9fs_string_free(&name); - g_free(dent); - return count; - } - /* - * Fill up just the path field of qid because the client uses - * only that. To fill the entire qid structure we will have - * to stat each dirent found, which is expensive - */ - size = MIN(sizeof(dent->d_ino), sizeof(qid.path)); - memcpy(&qid.path, &dent->d_ino, size); - /* Fill the other fields with dummy values */ - qid.type = 0; - qid.version = 0; - - /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */ - len = pdu_marshal(pdu, 11 + count, "Qqbs", - &qid, dent->d_off, - dent->d_type, &name); - if (len < 0) { - v9fs_co_seekdir(pdu, fidp, saved_dir_pos); - v9fs_string_free(&name); - g_free(dent); - return len; - } - count += len; - v9fs_string_free(&name); - saved_dir_pos = dent->d_off; - } - g_free(dent); - if (err < 0) { - return err; - } - return count; -} - -static void v9fs_readdir(void *opaque) -{ - int32_t fid; - V9fsFidState *fidp; - ssize_t retval = 0; - size_t offset = 7; - uint64_t initial_offset; - int32_t count; - uint32_t max_count; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - retval = pdu_unmarshal(pdu, offset, "dqd", &fid, - &initial_offset, &max_count); - if (retval < 0) { - goto out_nofid; - } - trace_v9fs_readdir(pdu->tag, pdu->id, fid, initial_offset, max_count); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - retval = -EINVAL; - goto out_nofid; - } - if (!fidp->fs.dir) { - retval = -EINVAL; - goto out; - } - if (initial_offset == 0) { - v9fs_co_rewinddir(pdu, fidp); - } else { - v9fs_co_seekdir(pdu, fidp, initial_offset); - } - count = v9fs_do_readdir(pdu, fidp, max_count); - if (count < 0) { - retval = count; - goto out; - } - retval = pdu_marshal(pdu, offset, "d", count); - if (retval < 0) { - goto out; - } - retval += count + offset; - trace_v9fs_readdir_return(pdu->tag, pdu->id, count, retval); -out: - put_fid(pdu, fidp); -out_nofid: - complete_pdu(s, pdu, retval); -} - -static int v9fs_xattr_write(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, - uint64_t off, uint32_t count, - struct iovec *sg, int cnt) -{ - int i, to_copy; - ssize_t err = 0; - int write_count; - int64_t xattr_len; - size_t offset = 7; - - - xattr_len = fidp->fs.xattr.len; - write_count = xattr_len - off; - if (write_count > count) { - write_count = count; - } else if (write_count < 0) { - /* - * write beyond XATTR value len specified in - * xattrcreate - */ - err = -ENOSPC; - goto out; - } - err = pdu_marshal(pdu, offset, "d", write_count); - if (err < 0) { - return err; - } - err += offset; - fidp->fs.xattr.copied_len += write_count; - /* - * Now copy the content from sg list - */ - for (i = 0; i < cnt; i++) { - if (write_count > sg[i].iov_len) { - to_copy = sg[i].iov_len; - } else { - to_copy = write_count; - } - memcpy((char *)fidp->fs.xattr.value + off, sg[i].iov_base, to_copy); - /* updating vs->off since we are not using below */ - off += to_copy; - write_count -= to_copy; - } -out: - return err; -} - -static void v9fs_write(void *opaque) -{ - ssize_t err; - int32_t fid; - uint64_t off; - uint32_t count; - int32_t len = 0; - int32_t total = 0; - size_t offset = 7; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - QEMUIOVector qiov_full; - QEMUIOVector qiov; - - err = pdu_unmarshal(pdu, offset, "dqd", &fid, &off, &count); - if (err < 0) { - complete_pdu(s, pdu, err); - return; - } - offset += err; - v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset, count, true); - trace_v9fs_write(pdu->tag, pdu->id, fid, off, count, qiov_full.niov); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - if (fidp->fid_type == P9_FID_FILE) { - if (fidp->fs.fd == -1) { - err = -EINVAL; - goto out; - } - } else if (fidp->fid_type == P9_FID_XATTR) { - /* - * setxattr operation - */ - err = v9fs_xattr_write(s, pdu, fidp, off, count, - qiov_full.iov, qiov_full.niov); - goto out; - } else { - err = -EINVAL; - goto out; - } - qemu_iovec_init(&qiov, qiov_full.niov); - do { - qemu_iovec_reset(&qiov); - qemu_iovec_concat(&qiov, &qiov_full, total, qiov_full.size - total); - if (0) { - print_sg(qiov.iov, qiov.niov); - } - /* Loop in case of EINTR */ - do { - len = v9fs_co_pwritev(pdu, fidp, qiov.iov, qiov.niov, off); - if (len >= 0) { - off += len; - total += len; - } - } while (len == -EINTR && !pdu->cancelled); - if (len < 0) { - /* IO error return the error */ - err = len; - goto out_qiov; - } - } while (total < count && len > 0); - - offset = 7; - err = pdu_marshal(pdu, offset, "d", total); - if (err < 0) { - goto out; - } - err += offset; - trace_v9fs_write_return(pdu->tag, pdu->id, total, err); -out_qiov: - qemu_iovec_destroy(&qiov); -out: - put_fid(pdu, fidp); -out_nofid: - qemu_iovec_destroy(&qiov_full); - complete_pdu(s, pdu, err); -} - -static void v9fs_create(void *opaque) -{ - int32_t fid; - int err = 0; - size_t offset = 7; - V9fsFidState *fidp; - V9fsQID qid; - int32_t perm; - int8_t mode; - V9fsPath path; - struct stat stbuf; - V9fsString name; - V9fsString extension; - int iounit; - V9fsPDU *pdu = opaque; - - v9fs_path_init(&path); - v9fs_string_init(&name); - v9fs_string_init(&extension); - err = pdu_unmarshal(pdu, offset, "dsdbs", &fid, &name, - &perm, &mode, &extension); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_create(pdu->tag, pdu->id, fid, name.data, perm, mode); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - if (perm & P9_STAT_MODE_DIR) { - err = v9fs_co_mkdir(pdu, fidp, &name, perm & 0777, - fidp->uid, -1, &stbuf); - if (err < 0) { - goto out; - } - err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); - if (err < 0) { - goto out; - } - v9fs_path_copy(&fidp->path, &path); - err = v9fs_co_opendir(pdu, fidp); - if (err < 0) { - goto out; - } - fidp->fid_type = P9_FID_DIR; - } else if (perm & P9_STAT_MODE_SYMLINK) { - err = v9fs_co_symlink(pdu, fidp, &name, - extension.data, -1 , &stbuf); - if (err < 0) { - goto out; - } - err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); - if (err < 0) { - goto out; - } - v9fs_path_copy(&fidp->path, &path); - } else if (perm & P9_STAT_MODE_LINK) { - int32_t ofid = atoi(extension.data); - V9fsFidState *ofidp = get_fid(pdu, ofid); - if (ofidp == NULL) { - err = -EINVAL; - goto out; - } - err = v9fs_co_link(pdu, ofidp, fidp, &name); - put_fid(pdu, ofidp); - if (err < 0) { - goto out; - } - err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); - if (err < 0) { - fidp->fid_type = P9_FID_NONE; - goto out; - } - v9fs_path_copy(&fidp->path, &path); - err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); - if (err < 0) { - fidp->fid_type = P9_FID_NONE; - goto out; - } - } else if (perm & P9_STAT_MODE_DEVICE) { - char ctype; - uint32_t major, minor; - mode_t nmode = 0; - - if (sscanf(extension.data, "%c %u %u", &ctype, &major, &minor) != 3) { - err = -errno; - goto out; - } - - switch (ctype) { - case 'c': - nmode = S_IFCHR; - break; - case 'b': - nmode = S_IFBLK; - break; - default: - err = -EIO; - goto out; - } - - nmode |= perm & 0777; - err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, - makedev(major, minor), nmode, &stbuf); - if (err < 0) { - goto out; - } - err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); - if (err < 0) { - goto out; - } - v9fs_path_copy(&fidp->path, &path); - } else if (perm & P9_STAT_MODE_NAMED_PIPE) { - err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, - 0, S_IFIFO | (perm & 0777), &stbuf); - if (err < 0) { - goto out; - } - err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); - if (err < 0) { - goto out; - } - v9fs_path_copy(&fidp->path, &path); - } else if (perm & P9_STAT_MODE_SOCKET) { - err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, - 0, S_IFSOCK | (perm & 0777), &stbuf); - if (err < 0) { - goto out; - } - err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); - if (err < 0) { - goto out; - } - v9fs_path_copy(&fidp->path, &path); - } else { - err = v9fs_co_open2(pdu, fidp, &name, -1, - omode_to_uflags(mode)|O_CREAT, perm, &stbuf); - if (err < 0) { - goto out; - } - fidp->fid_type = P9_FID_FILE; - fidp->open_flags = omode_to_uflags(mode); - if (fidp->open_flags & O_EXCL) { - /* - * We let the host file system do O_EXCL check - * We should not reclaim such fd - */ - fidp->flags |= FID_NON_RECLAIMABLE; - } - } - iounit = get_iounit(pdu, &fidp->path); - stat_to_qid(&stbuf, &qid); - err = pdu_marshal(pdu, offset, "Qd", &qid, iounit); - if (err < 0) { - goto out; - } - err += offset; - trace_v9fs_create_return(pdu->tag, pdu->id, - qid.type, qid.version, qid.path, iounit); -out: - put_fid(pdu, fidp); -out_nofid: - complete_pdu(pdu->s, pdu, err); - v9fs_string_free(&name); - v9fs_string_free(&extension); - v9fs_path_free(&path); -} - -static void v9fs_symlink(void *opaque) -{ - V9fsPDU *pdu = opaque; - V9fsString name; - V9fsString symname; - V9fsFidState *dfidp; - V9fsQID qid; - struct stat stbuf; - int32_t dfid; - int err = 0; - gid_t gid; - size_t offset = 7; - - v9fs_string_init(&name); - v9fs_string_init(&symname); - err = pdu_unmarshal(pdu, offset, "dssd", &dfid, &name, &symname, &gid); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_symlink(pdu->tag, pdu->id, dfid, name.data, symname.data, gid); - - dfidp = get_fid(pdu, dfid); - if (dfidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - err = v9fs_co_symlink(pdu, dfidp, &name, symname.data, gid, &stbuf); - if (err < 0) { - goto out; - } - stat_to_qid(&stbuf, &qid); - err = pdu_marshal(pdu, offset, "Q", &qid); - if (err < 0) { - goto out; - } - err += offset; - trace_v9fs_symlink_return(pdu->tag, pdu->id, - qid.type, qid.version, qid.path); -out: - put_fid(pdu, dfidp); -out_nofid: - complete_pdu(pdu->s, pdu, err); - v9fs_string_free(&name); - v9fs_string_free(&symname); -} - -static void v9fs_flush(void *opaque) -{ - ssize_t err; - int16_t tag; - size_t offset = 7; - V9fsPDU *cancel_pdu; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - err = pdu_unmarshal(pdu, offset, "w", &tag); - if (err < 0) { - complete_pdu(s, pdu, err); - return; - } - trace_v9fs_flush(pdu->tag, pdu->id, tag); - - QLIST_FOREACH(cancel_pdu, &s->active_list, next) { - if (cancel_pdu->tag == tag) { - break; - } - } - if (cancel_pdu) { - cancel_pdu->cancelled = 1; - /* - * Wait for pdu to complete. - */ - qemu_co_queue_wait(&cancel_pdu->complete); - cancel_pdu->cancelled = 0; - free_pdu(pdu->s, cancel_pdu); - } - complete_pdu(s, pdu, 7); -} - -static void v9fs_link(void *opaque) -{ - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - int32_t dfid, oldfid; - V9fsFidState *dfidp, *oldfidp; - V9fsString name; - size_t offset = 7; - int err = 0; - - v9fs_string_init(&name); - err = pdu_unmarshal(pdu, offset, "dds", &dfid, &oldfid, &name); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_link(pdu->tag, pdu->id, dfid, oldfid, name.data); - - dfidp = get_fid(pdu, dfid); - if (dfidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - - oldfidp = get_fid(pdu, oldfid); - if (oldfidp == NULL) { - err = -ENOENT; - goto out; - } - err = v9fs_co_link(pdu, oldfidp, dfidp, &name); - if (!err) { - err = offset; - } -out: - put_fid(pdu, dfidp); -out_nofid: - v9fs_string_free(&name); - complete_pdu(s, pdu, err); -} - -/* Only works with path name based fid */ -static void v9fs_remove(void *opaque) -{ - int32_t fid; - int err = 0; - size_t offset = 7; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - - err = pdu_unmarshal(pdu, offset, "d", &fid); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_remove(pdu->tag, pdu->id, fid); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - /* if fs driver is not path based, return EOPNOTSUPP */ - if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) { - err = -EOPNOTSUPP; - goto out_err; - } - /* - * IF the file is unlinked, we cannot reopen - * the file later. So don't reclaim fd - */ - err = v9fs_mark_fids_unreclaim(pdu, &fidp->path); - if (err < 0) { - goto out_err; - } - err = v9fs_co_remove(pdu, &fidp->path); - if (!err) { - err = offset; - } -out_err: - /* For TREMOVE we need to clunk the fid even on failed remove */ - clunk_fid(pdu->s, fidp->fid); - put_fid(pdu, fidp); -out_nofid: - complete_pdu(pdu->s, pdu, err); -} - -static void v9fs_unlinkat(void *opaque) -{ - int err = 0; - V9fsString name; - int32_t dfid, flags; - size_t offset = 7; - V9fsPath path; - V9fsFidState *dfidp; - V9fsPDU *pdu = opaque; - - v9fs_string_init(&name); - err = pdu_unmarshal(pdu, offset, "dsd", &dfid, &name, &flags); - if (err < 0) { - goto out_nofid; - } - dfidp = get_fid(pdu, dfid); - if (dfidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - /* - * IF the file is unlinked, we cannot reopen - * the file later. So don't reclaim fd - */ - v9fs_path_init(&path); - err = v9fs_co_name_to_path(pdu, &dfidp->path, name.data, &path); - if (err < 0) { - goto out_err; - } - err = v9fs_mark_fids_unreclaim(pdu, &path); - if (err < 0) { - goto out_err; - } - err = v9fs_co_unlinkat(pdu, &dfidp->path, &name, flags); - if (!err) { - err = offset; - } -out_err: - put_fid(pdu, dfidp); - v9fs_path_free(&path); -out_nofid: - complete_pdu(pdu->s, pdu, err); - v9fs_string_free(&name); -} - - -/* Only works with path name based fid */ -static int v9fs_complete_rename(V9fsPDU *pdu, V9fsFidState *fidp, - int32_t newdirfid, V9fsString *name) -{ - char *end; - int err = 0; - V9fsPath new_path; - V9fsFidState *tfidp; - V9fsState *s = pdu->s; - V9fsFidState *dirfidp = NULL; - char *old_name, *new_name; - - v9fs_path_init(&new_path); - if (newdirfid != -1) { - dirfidp = get_fid(pdu, newdirfid); - if (dirfidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - BUG_ON(dirfidp->fid_type != P9_FID_NONE); - v9fs_co_name_to_path(pdu, &dirfidp->path, name->data, &new_path); - } else { - old_name = fidp->path.data; - end = strrchr(old_name, '/'); - if (end) { - end++; - } else { - end = old_name; - } - new_name = g_malloc0(end - old_name + name->size + 1); - strncat(new_name, old_name, end - old_name); - strncat(new_name + (end - old_name), name->data, name->size); - v9fs_co_name_to_path(pdu, NULL, new_name, &new_path); - g_free(new_name); - } - err = v9fs_co_rename(pdu, &fidp->path, &new_path); - if (err < 0) { - goto out; - } - /* - * Fixup fid's pointing to the old name to - * start pointing to the new name - */ - for (tfidp = s->fid_list; tfidp; tfidp = tfidp->next) { - if (v9fs_path_is_ancestor(&fidp->path, &tfidp->path)) { - /* replace the name */ - v9fs_fix_path(&tfidp->path, &new_path, strlen(fidp->path.data)); - } - } -out: - if (dirfidp) { - put_fid(pdu, dirfidp); - } - v9fs_path_free(&new_path); -out_nofid: - return err; -} - -/* Only works with path name based fid */ -static void v9fs_rename(void *opaque) -{ - int32_t fid; - ssize_t err = 0; - size_t offset = 7; - V9fsString name; - int32_t newdirfid; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - v9fs_string_init(&name); - err = pdu_unmarshal(pdu, offset, "dds", &fid, &newdirfid, &name); - if (err < 0) { - goto out_nofid; - } - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - BUG_ON(fidp->fid_type != P9_FID_NONE); - /* if fs driver is not path based, return EOPNOTSUPP */ - if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) { - err = -EOPNOTSUPP; - goto out; - } - v9fs_path_write_lock(s); - err = v9fs_complete_rename(pdu, fidp, newdirfid, &name); - v9fs_path_unlock(s); - if (!err) { - err = offset; - } -out: - put_fid(pdu, fidp); -out_nofid: - complete_pdu(s, pdu, err); - v9fs_string_free(&name); -} - -static void v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir, - V9fsString *old_name, V9fsPath *newdir, - V9fsString *new_name) -{ - V9fsFidState *tfidp; - V9fsPath oldpath, newpath; - V9fsState *s = pdu->s; - - - v9fs_path_init(&oldpath); - v9fs_path_init(&newpath); - v9fs_co_name_to_path(pdu, olddir, old_name->data, &oldpath); - v9fs_co_name_to_path(pdu, newdir, new_name->data, &newpath); - - /* - * Fixup fid's pointing to the old name to - * start pointing to the new name - */ - for (tfidp = s->fid_list; tfidp; tfidp = tfidp->next) { - if (v9fs_path_is_ancestor(&oldpath, &tfidp->path)) { - /* replace the name */ - v9fs_fix_path(&tfidp->path, &newpath, strlen(oldpath.data)); - } - } - v9fs_path_free(&oldpath); - v9fs_path_free(&newpath); -} - -static int v9fs_complete_renameat(V9fsPDU *pdu, int32_t olddirfid, - V9fsString *old_name, int32_t newdirfid, - V9fsString *new_name) -{ - int err = 0; - V9fsState *s = pdu->s; - V9fsFidState *newdirfidp = NULL, *olddirfidp = NULL; - - olddirfidp = get_fid(pdu, olddirfid); - if (olddirfidp == NULL) { - err = -ENOENT; - goto out; - } - if (newdirfid != -1) { - newdirfidp = get_fid(pdu, newdirfid); - if (newdirfidp == NULL) { - err = -ENOENT; - goto out; - } - } else { - newdirfidp = get_fid(pdu, olddirfid); - } - - err = v9fs_co_renameat(pdu, &olddirfidp->path, old_name, - &newdirfidp->path, new_name); - if (err < 0) { - goto out; - } - if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { - /* Only for path based fid we need to do the below fixup */ - v9fs_fix_fid_paths(pdu, &olddirfidp->path, old_name, - &newdirfidp->path, new_name); - } -out: - if (olddirfidp) { - put_fid(pdu, olddirfidp); - } - if (newdirfidp) { - put_fid(pdu, newdirfidp); - } - return err; -} - -static void v9fs_renameat(void *opaque) -{ - ssize_t err = 0; - size_t offset = 7; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - int32_t olddirfid, newdirfid; - V9fsString old_name, new_name; - - v9fs_string_init(&old_name); - v9fs_string_init(&new_name); - err = pdu_unmarshal(pdu, offset, "dsds", &olddirfid, - &old_name, &newdirfid, &new_name); - if (err < 0) { - goto out_err; - } - - v9fs_path_write_lock(s); - err = v9fs_complete_renameat(pdu, olddirfid, - &old_name, newdirfid, &new_name); - v9fs_path_unlock(s); - if (!err) { - err = offset; - } - -out_err: - complete_pdu(s, pdu, err); - v9fs_string_free(&old_name); - v9fs_string_free(&new_name); -} - -static void v9fs_wstat(void *opaque) -{ - int32_t fid; - int err = 0; - int16_t unused; - V9fsStat v9stat; - size_t offset = 7; - struct stat stbuf; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - v9fs_stat_init(&v9stat); - err = pdu_unmarshal(pdu, offset, "dwS", &fid, &unused, &v9stat); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_wstat(pdu->tag, pdu->id, fid, - v9stat.mode, v9stat.atime, v9stat.mtime); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - /* do we need to sync the file? */ - if (donttouch_stat(&v9stat)) { - err = v9fs_co_fsync(pdu, fidp, 0); - goto out; - } - if (v9stat.mode != -1) { - uint32_t v9_mode; - err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); - if (err < 0) { - goto out; - } - v9_mode = stat_to_v9mode(&stbuf); - if ((v9stat.mode & P9_STAT_MODE_TYPE_BITS) != - (v9_mode & P9_STAT_MODE_TYPE_BITS)) { - /* Attempting to change the type */ - err = -EIO; - goto out; - } - err = v9fs_co_chmod(pdu, &fidp->path, - v9mode_to_mode(v9stat.mode, - &v9stat.extension)); - if (err < 0) { - goto out; - } - } - if (v9stat.mtime != -1 || v9stat.atime != -1) { - struct timespec times[2]; - if (v9stat.atime != -1) { - times[0].tv_sec = v9stat.atime; - times[0].tv_nsec = 0; - } else { - times[0].tv_nsec = UTIME_OMIT; - } - if (v9stat.mtime != -1) { - times[1].tv_sec = v9stat.mtime; - times[1].tv_nsec = 0; - } else { - times[1].tv_nsec = UTIME_OMIT; - } - err = v9fs_co_utimensat(pdu, &fidp->path, times); - if (err < 0) { - goto out; - } - } - if (v9stat.n_gid != -1 || v9stat.n_uid != -1) { - err = v9fs_co_chown(pdu, &fidp->path, v9stat.n_uid, v9stat.n_gid); - if (err < 0) { - goto out; - } - } - if (v9stat.name.size != 0) { - err = v9fs_complete_rename(pdu, fidp, -1, &v9stat.name); - if (err < 0) { - goto out; - } - } - if (v9stat.length != -1) { - err = v9fs_co_truncate(pdu, &fidp->path, v9stat.length); - if (err < 0) { - goto out; - } - } - err = offset; -out: - put_fid(pdu, fidp); -out_nofid: - v9fs_stat_free(&v9stat); - complete_pdu(s, pdu, err); -} - -static int v9fs_fill_statfs(V9fsState *s, V9fsPDU *pdu, struct statfs *stbuf) -{ - uint32_t f_type; - uint32_t f_bsize; - uint64_t f_blocks; - uint64_t f_bfree; - uint64_t f_bavail; - uint64_t f_files; - uint64_t f_ffree; - uint64_t fsid_val; - uint32_t f_namelen; - size_t offset = 7; - int32_t bsize_factor; - - /* - * compute bsize factor based on host file system block size - * and client msize - */ - bsize_factor = (s->msize - P9_IOHDRSZ)/stbuf->f_bsize; - if (!bsize_factor) { - bsize_factor = 1; - } - f_type = stbuf->f_type; - f_bsize = stbuf->f_bsize; - f_bsize *= bsize_factor; - /* - * f_bsize is adjusted(multiplied) by bsize factor, so we need to - * adjust(divide) the number of blocks, free blocks and available - * blocks by bsize factor - */ - f_blocks = stbuf->f_blocks/bsize_factor; - f_bfree = stbuf->f_bfree/bsize_factor; - f_bavail = stbuf->f_bavail/bsize_factor; - f_files = stbuf->f_files; - f_ffree = stbuf->f_ffree; - fsid_val = (unsigned int) stbuf->f_fsid.__val[0] | - (unsigned long long)stbuf->f_fsid.__val[1] << 32; - f_namelen = stbuf->f_namelen; - - return pdu_marshal(pdu, offset, "ddqqqqqqd", - f_type, f_bsize, f_blocks, f_bfree, - f_bavail, f_files, f_ffree, - fsid_val, f_namelen); -} - -static void v9fs_statfs(void *opaque) -{ - int32_t fid; - ssize_t retval = 0; - size_t offset = 7; - V9fsFidState *fidp; - struct statfs stbuf; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - retval = pdu_unmarshal(pdu, offset, "d", &fid); - if (retval < 0) { - goto out_nofid; - } - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - retval = -ENOENT; - goto out_nofid; - } - retval = v9fs_co_statfs(pdu, &fidp->path, &stbuf); - if (retval < 0) { - goto out; - } - retval = v9fs_fill_statfs(s, pdu, &stbuf); - if (retval < 0) { - goto out; - } - retval += offset; -out: - put_fid(pdu, fidp); -out_nofid: - complete_pdu(s, pdu, retval); -} - -static void v9fs_mknod(void *opaque) -{ - - int mode; - gid_t gid; - int32_t fid; - V9fsQID qid; - int err = 0; - int major, minor; - size_t offset = 7; - V9fsString name; - struct stat stbuf; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - v9fs_string_init(&name); - err = pdu_unmarshal(pdu, offset, "dsdddd", &fid, &name, &mode, - &major, &minor, &gid); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_mknod(pdu->tag, pdu->id, fid, mode, major, minor); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, gid, - makedev(major, minor), mode, &stbuf); - if (err < 0) { - goto out; - } - stat_to_qid(&stbuf, &qid); - err = pdu_marshal(pdu, offset, "Q", &qid); - if (err < 0) { - goto out; - } - err += offset; - trace_v9fs_mknod_return(pdu->tag, pdu->id, - qid.type, qid.version, qid.path); -out: - put_fid(pdu, fidp); -out_nofid: - complete_pdu(s, pdu, err); - v9fs_string_free(&name); -} - -/* - * Implement posix byte range locking code - * Server side handling of locking code is very simple, because 9p server in - * QEMU can handle only one client. And most of the lock handling - * (like conflict, merging) etc is done by the VFS layer itself, so no need to - * do any thing in * qemu 9p server side lock code path. - * So when a TLOCK request comes, always return success - */ -static void v9fs_lock(void *opaque) -{ - int8_t status; - V9fsFlock flock; - size_t offset = 7; - struct stat stbuf; - V9fsFidState *fidp; - int32_t fid, err = 0; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - status = P9_LOCK_ERROR; - v9fs_string_init(&flock.client_id); - err = pdu_unmarshal(pdu, offset, "dbdqqds", &fid, &flock.type, - &flock.flags, &flock.start, &flock.length, - &flock.proc_id, &flock.client_id); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_lock(pdu->tag, pdu->id, fid, - flock.type, flock.start, flock.length); - - - /* We support only block flag now (that too ignored currently) */ - if (flock.flags & ~P9_LOCK_FLAGS_BLOCK) { - err = -EINVAL; - goto out_nofid; - } - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - err = v9fs_co_fstat(pdu, fidp, &stbuf); - if (err < 0) { - goto out; - } - status = P9_LOCK_SUCCESS; -out: - put_fid(pdu, fidp); -out_nofid: - err = pdu_marshal(pdu, offset, "b", status); - if (err > 0) { - err += offset; - } - trace_v9fs_lock_return(pdu->tag, pdu->id, status); - complete_pdu(s, pdu, err); - v9fs_string_free(&flock.client_id); -} - -/* - * When a TGETLOCK request comes, always return success because all lock - * handling is done by client's VFS layer. - */ -static void v9fs_getlock(void *opaque) -{ - size_t offset = 7; - struct stat stbuf; - V9fsFidState *fidp; - V9fsGetlock glock; - int32_t fid, err = 0; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - v9fs_string_init(&glock.client_id); - err = pdu_unmarshal(pdu, offset, "dbqqds", &fid, &glock.type, - &glock.start, &glock.length, &glock.proc_id, - &glock.client_id); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_getlock(pdu->tag, pdu->id, fid, - glock.type, glock.start, glock.length); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - err = v9fs_co_fstat(pdu, fidp, &stbuf); - if (err < 0) { - goto out; - } - glock.type = P9_LOCK_TYPE_UNLCK; - err = pdu_marshal(pdu, offset, "bqqds", glock.type, - glock.start, glock.length, glock.proc_id, - &glock.client_id); - if (err < 0) { - goto out; - } - err += offset; - trace_v9fs_getlock_return(pdu->tag, pdu->id, glock.type, glock.start, - glock.length, glock.proc_id); -out: - put_fid(pdu, fidp); -out_nofid: - complete_pdu(s, pdu, err); - v9fs_string_free(&glock.client_id); -} - -static void v9fs_mkdir(void *opaque) -{ - V9fsPDU *pdu = opaque; - size_t offset = 7; - int32_t fid; - struct stat stbuf; - V9fsQID qid; - V9fsString name; - V9fsFidState *fidp; - gid_t gid; - int mode; - int err = 0; - - v9fs_string_init(&name); - err = pdu_unmarshal(pdu, offset, "dsdd", &fid, &name, &mode, &gid); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_mkdir(pdu->tag, pdu->id, fid, name.data, mode, gid); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - err = v9fs_co_mkdir(pdu, fidp, &name, mode, fidp->uid, gid, &stbuf); - if (err < 0) { - goto out; - } - stat_to_qid(&stbuf, &qid); - err = pdu_marshal(pdu, offset, "Q", &qid); - if (err < 0) { - goto out; - } - err += offset; - trace_v9fs_mkdir_return(pdu->tag, pdu->id, - qid.type, qid.version, qid.path, err); -out: - put_fid(pdu, fidp); -out_nofid: - complete_pdu(pdu->s, pdu, err); - v9fs_string_free(&name); -} - -static void v9fs_xattrwalk(void *opaque) -{ - int64_t size; - V9fsString name; - ssize_t err = 0; - size_t offset = 7; - int32_t fid, newfid; - V9fsFidState *file_fidp; - V9fsFidState *xattr_fidp = NULL; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - v9fs_string_init(&name); - err = pdu_unmarshal(pdu, offset, "dds", &fid, &newfid, &name); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_xattrwalk(pdu->tag, pdu->id, fid, newfid, name.data); - - file_fidp = get_fid(pdu, fid); - if (file_fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - xattr_fidp = alloc_fid(s, newfid); - if (xattr_fidp == NULL) { - err = -EINVAL; - goto out; - } - v9fs_path_copy(&xattr_fidp->path, &file_fidp->path); - if (name.data == NULL) { - /* - * listxattr request. Get the size first - */ - size = v9fs_co_llistxattr(pdu, &xattr_fidp->path, NULL, 0); - if (size < 0) { - err = size; - clunk_fid(s, xattr_fidp->fid); - goto out; - } - /* - * Read the xattr value - */ - xattr_fidp->fs.xattr.len = size; - xattr_fidp->fid_type = P9_FID_XATTR; - xattr_fidp->fs.xattr.copied_len = -1; - if (size) { - xattr_fidp->fs.xattr.value = g_malloc(size); - err = v9fs_co_llistxattr(pdu, &xattr_fidp->path, - xattr_fidp->fs.xattr.value, - xattr_fidp->fs.xattr.len); - if (err < 0) { - clunk_fid(s, xattr_fidp->fid); - goto out; - } - } - err = pdu_marshal(pdu, offset, "q", size); - if (err < 0) { - goto out; - } - err += offset; - } else { - /* - * specific xattr fid. We check for xattr - * presence also collect the xattr size - */ - size = v9fs_co_lgetxattr(pdu, &xattr_fidp->path, - &name, NULL, 0); - if (size < 0) { - err = size; - clunk_fid(s, xattr_fidp->fid); - goto out; - } - /* - * Read the xattr value - */ - xattr_fidp->fs.xattr.len = size; - xattr_fidp->fid_type = P9_FID_XATTR; - xattr_fidp->fs.xattr.copied_len = -1; - if (size) { - xattr_fidp->fs.xattr.value = g_malloc(size); - err = v9fs_co_lgetxattr(pdu, &xattr_fidp->path, - &name, xattr_fidp->fs.xattr.value, - xattr_fidp->fs.xattr.len); - if (err < 0) { - clunk_fid(s, xattr_fidp->fid); - goto out; - } - } - err = pdu_marshal(pdu, offset, "q", size); - if (err < 0) { - goto out; - } - err += offset; - } - trace_v9fs_xattrwalk_return(pdu->tag, pdu->id, size); -out: - put_fid(pdu, file_fidp); - if (xattr_fidp) { - put_fid(pdu, xattr_fidp); - } -out_nofid: - complete_pdu(s, pdu, err); - v9fs_string_free(&name); -} - -static void v9fs_xattrcreate(void *opaque) -{ - int flags; - int32_t fid; - int64_t size; - ssize_t err = 0; - V9fsString name; - size_t offset = 7; - V9fsFidState *file_fidp; - V9fsFidState *xattr_fidp; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - v9fs_string_init(&name); - err = pdu_unmarshal(pdu, offset, "dsqd", &fid, &name, &size, &flags); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_xattrcreate(pdu->tag, pdu->id, fid, name.data, size, flags); - - file_fidp = get_fid(pdu, fid); - if (file_fidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - /* Make the file fid point to xattr */ - xattr_fidp = file_fidp; - xattr_fidp->fid_type = P9_FID_XATTR; - xattr_fidp->fs.xattr.copied_len = 0; - xattr_fidp->fs.xattr.len = size; - xattr_fidp->fs.xattr.flags = flags; - v9fs_string_init(&xattr_fidp->fs.xattr.name); - v9fs_string_copy(&xattr_fidp->fs.xattr.name, &name); - xattr_fidp->fs.xattr.value = g_malloc(size); - err = offset; - put_fid(pdu, file_fidp); -out_nofid: - complete_pdu(s, pdu, err); - v9fs_string_free(&name); -} - -static void v9fs_readlink(void *opaque) -{ - V9fsPDU *pdu = opaque; - size_t offset = 7; - V9fsString target; - int32_t fid; - int err = 0; - V9fsFidState *fidp; - - err = pdu_unmarshal(pdu, offset, "d", &fid); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_readlink(pdu->tag, pdu->id, fid); - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - - v9fs_string_init(&target); - err = v9fs_co_readlink(pdu, &fidp->path, &target); - if (err < 0) { - goto out; - } - err = pdu_marshal(pdu, offset, "s", &target); - if (err < 0) { - v9fs_string_free(&target); - goto out; - } - err += offset; - trace_v9fs_readlink_return(pdu->tag, pdu->id, target.data); - v9fs_string_free(&target); -out: - put_fid(pdu, fidp); -out_nofid: - complete_pdu(pdu->s, pdu, err); -} - -static CoroutineEntry *pdu_co_handlers[] = { - [P9_TREADDIR] = v9fs_readdir, - [P9_TSTATFS] = v9fs_statfs, - [P9_TGETATTR] = v9fs_getattr, - [P9_TSETATTR] = v9fs_setattr, - [P9_TXATTRWALK] = v9fs_xattrwalk, - [P9_TXATTRCREATE] = v9fs_xattrcreate, - [P9_TMKNOD] = v9fs_mknod, - [P9_TRENAME] = v9fs_rename, - [P9_TLOCK] = v9fs_lock, - [P9_TGETLOCK] = v9fs_getlock, - [P9_TRENAMEAT] = v9fs_renameat, - [P9_TREADLINK] = v9fs_readlink, - [P9_TUNLINKAT] = v9fs_unlinkat, - [P9_TMKDIR] = v9fs_mkdir, - [P9_TVERSION] = v9fs_version, - [P9_TLOPEN] = v9fs_open, - [P9_TATTACH] = v9fs_attach, - [P9_TSTAT] = v9fs_stat, - [P9_TWALK] = v9fs_walk, - [P9_TCLUNK] = v9fs_clunk, - [P9_TFSYNC] = v9fs_fsync, - [P9_TOPEN] = v9fs_open, - [P9_TREAD] = v9fs_read, -#if 0 - [P9_TAUTH] = v9fs_auth, -#endif - [P9_TFLUSH] = v9fs_flush, - [P9_TLINK] = v9fs_link, - [P9_TSYMLINK] = v9fs_symlink, - [P9_TCREATE] = v9fs_create, - [P9_TLCREATE] = v9fs_lcreate, - [P9_TWRITE] = v9fs_write, - [P9_TWSTAT] = v9fs_wstat, - [P9_TREMOVE] = v9fs_remove, -}; - -static void v9fs_op_not_supp(void *opaque) -{ - V9fsPDU *pdu = opaque; - complete_pdu(pdu->s, pdu, -EOPNOTSUPP); -} - -static void v9fs_fs_ro(void *opaque) -{ - V9fsPDU *pdu = opaque; - complete_pdu(pdu->s, pdu, -EROFS); -} - -static inline bool is_read_only_op(V9fsPDU *pdu) -{ - switch (pdu->id) { - case P9_TREADDIR: - case P9_TSTATFS: - case P9_TGETATTR: - case P9_TXATTRWALK: - case P9_TLOCK: - case P9_TGETLOCK: - case P9_TREADLINK: - case P9_TVERSION: - case P9_TLOPEN: - case P9_TATTACH: - case P9_TSTAT: - case P9_TWALK: - case P9_TCLUNK: - case P9_TFSYNC: - case P9_TOPEN: - case P9_TREAD: - case P9_TAUTH: - case P9_TFLUSH: - return 1; - default: - return 0; - } -} - -static void submit_pdu(V9fsState *s, V9fsPDU *pdu) -{ - Coroutine *co; - CoroutineEntry *handler; - - if (pdu->id >= ARRAY_SIZE(pdu_co_handlers) || - (pdu_co_handlers[pdu->id] == NULL)) { - handler = v9fs_op_not_supp; - } else { - handler = pdu_co_handlers[pdu->id]; - } - - if (is_ro_export(&s->ctx) && !is_read_only_op(pdu)) { - handler = v9fs_fs_ro; - } - co = qemu_coroutine_create(handler); - qemu_coroutine_enter(co, pdu); -} - -void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq) -{ - V9fsState *s = (V9fsState *)vdev; - V9fsPDU *pdu; - ssize_t len; - - while ((pdu = alloc_pdu(s)) && - (len = virtqueue_pop(vq, &pdu->elem)) != 0) { - struct { - uint32_t size_le; - uint8_t id; - uint16_t tag_le; - } QEMU_PACKED out; - int len; - - pdu->s = s; - BUG_ON(pdu->elem.out_num == 0 || pdu->elem.in_num == 0); - QEMU_BUILD_BUG_ON(sizeof out != 7); - - len = iov_to_buf(pdu->elem.out_sg, pdu->elem.out_num, 0, - &out, sizeof out); - BUG_ON(len != sizeof out); - - pdu->size = le32_to_cpu(out.size_le); - - pdu->id = out.id; - pdu->tag = le16_to_cpu(out.tag_le); - - qemu_co_queue_init(&pdu->complete); - submit_pdu(s, pdu); - } - free_pdu(s, pdu); -} - -static void __attribute__((__constructor__)) virtio_9p_set_fd_limit(void) -{ - struct rlimit rlim; - if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { - fprintf(stderr, "Failed to get the resource limit\n"); - exit(1); - } - open_fd_hw = rlim.rlim_cur - MIN(400, rlim.rlim_cur/3); - open_fd_rc = rlim.rlim_cur/2; -} diff --git a/qemu/hw/9pfs/virtio-9p.h b/qemu/hw/9pfs/virtio-9p.h index 2e7d48857..7f6d88553 100644 --- a/qemu/hw/9pfs/virtio-9p.h +++ b/qemu/hw/9pfs/virtio-9p.h @@ -1,394 +1,31 @@ #ifndef _QEMU_VIRTIO_9P_H #define _QEMU_VIRTIO_9P_H -#include -#include -#include -#include -#include -#include #include "standard-headers/linux/virtio_9p.h" #include "hw/virtio/virtio.h" -#include "hw/virtio/virtio-9p.h" -#include "fsdev/file-op-9p.h" -#include "fsdev/virtio-9p-marshal.h" -#include "qemu/thread.h" -#include "block/coroutine.h" +#include "9p.h" -enum { - P9_TLERROR = 6, - P9_RLERROR, - P9_TSTATFS = 8, - P9_RSTATFS, - P9_TLOPEN = 12, - P9_RLOPEN, - P9_TLCREATE = 14, - P9_RLCREATE, - P9_TSYMLINK = 16, - P9_RSYMLINK, - P9_TMKNOD = 18, - P9_RMKNOD, - P9_TRENAME = 20, - P9_RRENAME, - P9_TREADLINK = 22, - P9_RREADLINK, - P9_TGETATTR = 24, - P9_RGETATTR, - P9_TSETATTR = 26, - P9_RSETATTR, - P9_TXATTRWALK = 30, - P9_RXATTRWALK, - P9_TXATTRCREATE = 32, - P9_RXATTRCREATE, - P9_TREADDIR = 40, - P9_RREADDIR, - P9_TFSYNC = 50, - P9_RFSYNC, - P9_TLOCK = 52, - P9_RLOCK, - P9_TGETLOCK = 54, - P9_RGETLOCK, - P9_TLINK = 70, - P9_RLINK, - P9_TMKDIR = 72, - P9_RMKDIR, - P9_TRENAMEAT = 74, - P9_RRENAMEAT, - P9_TUNLINKAT = 76, - P9_RUNLINKAT, - P9_TVERSION = 100, - P9_RVERSION, - P9_TAUTH = 102, - P9_RAUTH, - P9_TATTACH = 104, - P9_RATTACH, - P9_TERROR = 106, - P9_RERROR, - P9_TFLUSH = 108, - P9_RFLUSH, - P9_TWALK = 110, - P9_RWALK, - P9_TOPEN = 112, - P9_ROPEN, - P9_TCREATE = 114, - P9_RCREATE, - P9_TREAD = 116, - P9_RREAD, - P9_TWRITE = 118, - P9_RWRITE, - P9_TCLUNK = 120, - P9_RCLUNK, - P9_TREMOVE = 122, - P9_RREMOVE, - P9_TSTAT = 124, - P9_RSTAT, - P9_TWSTAT = 126, - P9_RWSTAT, -}; - - -/* qid.types */ -enum { - P9_QTDIR = 0x80, - P9_QTAPPEND = 0x40, - P9_QTEXCL = 0x20, - P9_QTMOUNT = 0x10, - P9_QTAUTH = 0x08, - P9_QTTMP = 0x04, - P9_QTSYMLINK = 0x02, - P9_QTLINK = 0x01, - P9_QTFILE = 0x00, -}; - -enum p9_proto_version { - V9FS_PROTO_2000U = 0x01, - V9FS_PROTO_2000L = 0x02, -}; - -#define P9_NOTAG (u16)(~0) -#define P9_NOFID (u32)(~0) -#define P9_MAXWELEM 16 - -#define FID_REFERENCED 0x1 -#define FID_NON_RECLAIMABLE 0x2 -static inline char *rpath(FsContext *ctx, const char *path) -{ - return g_strdup_printf("%s/%s", ctx->fs_root, path); -} - -/* - * ample room for Twrite/Rread header - * size[4] Tread/Twrite tag[2] fid[4] offset[8] count[4] - */ -#define P9_IOHDRSZ 24 - -typedef struct V9fsPDU V9fsPDU; -struct V9fsState; - -struct V9fsPDU -{ - uint32_t size; - uint16_t tag; - uint8_t id; - uint8_t cancelled; - CoQueue complete; - VirtQueueElement elem; - struct V9fsState *s; - QLIST_ENTRY(V9fsPDU) next; -}; - - -/* FIXME - * 1) change user needs to set groups and stuff - */ - -#define MAX_REQ 128 -#define MAX_TAG_LEN 32 - -#define BUG_ON(cond) assert(!(cond)) - -typedef struct V9fsFidState V9fsFidState; - -enum { - P9_FID_NONE = 0, - P9_FID_FILE, - P9_FID_DIR, - P9_FID_XATTR, -}; - -typedef struct V9fsXattr -{ - int64_t copied_len; - int64_t len; - void *value; - V9fsString name; - int flags; -} V9fsXattr; - -/* - * Filled by fs driver on open and other - * calls. - */ -union V9fsFidOpenState { - int fd; - DIR *dir; - V9fsXattr xattr; - /* - * private pointer for fs drivers, that - * have its own internal representation of - * open files. - */ - void *private; -}; - -struct V9fsFidState -{ - int fid_type; - int32_t fid; - V9fsPath path; - V9fsFidOpenState fs; - V9fsFidOpenState fs_reclaim; - int flags; - int open_flags; - uid_t uid; - int ref; - int clunked; - V9fsFidState *next; - V9fsFidState *rclm_lst; -}; - -typedef struct V9fsState +typedef struct V9fsVirtioState { VirtIODevice parent_obj; VirtQueue *vq; - V9fsPDU pdus[MAX_REQ]; - QLIST_HEAD(, V9fsPDU) free_list; - QLIST_HEAD(, V9fsPDU) active_list; - V9fsFidState *fid_list; - FileOperations *ops; - FsContext ctx; - char *tag; size_t config_size; - enum p9_proto_version proto_version; - int32_t msize; - /* - * lock ensuring atomic path update - * on rename. - */ - CoRwlock rename_lock; - int32_t root_fid; - Error *migration_blocker; - V9fsConf fsconf; -} V9fsState; - -typedef struct V9fsStatState { - V9fsPDU *pdu; - size_t offset; - V9fsStat v9stat; - V9fsFidState *fidp; - struct stat stbuf; -} V9fsStatState; - -typedef struct V9fsOpenState { - V9fsPDU *pdu; - size_t offset; - int32_t mode; - V9fsFidState *fidp; - V9fsQID qid; - struct stat stbuf; - int iounit; -} V9fsOpenState; - -typedef struct V9fsReadState { - V9fsPDU *pdu; - size_t offset; - int32_t count; - int32_t total; - int64_t off; - V9fsFidState *fidp; - struct iovec iov[128]; /* FIXME: bad, bad, bad */ - struct iovec *sg; - off_t dir_pos; - struct dirent *dent; - struct stat stbuf; - V9fsString name; - V9fsStat v9stat; - int32_t len; - int32_t cnt; - int32_t max_count; -} V9fsReadState; - -typedef struct V9fsWriteState { - V9fsPDU *pdu; - size_t offset; - int32_t len; - int32_t count; - int32_t total; - int64_t off; - V9fsFidState *fidp; - struct iovec iov[128]; /* FIXME: bad, bad, bad */ - struct iovec *sg; - int cnt; -} V9fsWriteState; - -typedef struct V9fsMkState { - V9fsPDU *pdu; - size_t offset; - V9fsQID qid; - struct stat stbuf; - V9fsString name; - V9fsString fullname; -} V9fsMkState; - -/* 9p2000.L open flags */ -#define P9_DOTL_RDONLY 00000000 -#define P9_DOTL_WRONLY 00000001 -#define P9_DOTL_RDWR 00000002 -#define P9_DOTL_NOACCESS 00000003 -#define P9_DOTL_CREATE 00000100 -#define P9_DOTL_EXCL 00000200 -#define P9_DOTL_NOCTTY 00000400 -#define P9_DOTL_TRUNC 00001000 -#define P9_DOTL_APPEND 00002000 -#define P9_DOTL_NONBLOCK 00004000 -#define P9_DOTL_DSYNC 00010000 -#define P9_DOTL_FASYNC 00020000 -#define P9_DOTL_DIRECT 00040000 -#define P9_DOTL_LARGEFILE 00100000 -#define P9_DOTL_DIRECTORY 00200000 -#define P9_DOTL_NOFOLLOW 00400000 -#define P9_DOTL_NOATIME 01000000 -#define P9_DOTL_CLOEXEC 02000000 -#define P9_DOTL_SYNC 04000000 - -/* 9p2000.L at flags */ -#define P9_DOTL_AT_REMOVEDIR 0x200 - -/* 9P2000.L lock type */ -#define P9_LOCK_TYPE_RDLCK 0 -#define P9_LOCK_TYPE_WRLCK 1 -#define P9_LOCK_TYPE_UNLCK 2 - -#define P9_LOCK_SUCCESS 0 -#define P9_LOCK_BLOCKED 1 -#define P9_LOCK_ERROR 2 -#define P9_LOCK_GRACE 3 - -#define P9_LOCK_FLAGS_BLOCK 1 -#define P9_LOCK_FLAGS_RECLAIM 2 - -typedef struct V9fsFlock -{ - uint8_t type; - uint32_t flags; - uint64_t start; /* absolute offset */ - uint64_t length; - uint32_t proc_id; - V9fsString client_id; -} V9fsFlock; - -typedef struct V9fsGetlock -{ - uint8_t type; - uint64_t start; /* absolute offset */ - uint64_t length; - uint32_t proc_id; - V9fsString client_id; -} V9fsGetlock; - -extern int open_fd_hw; -extern int total_open_fd; - -size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count, - size_t offset, size_t size, int pack); - -static inline size_t do_pdu_unpack(void *dst, struct iovec *sg, int sg_count, - size_t offset, size_t size) -{ - return pdu_packunpack(dst, sg, sg_count, offset, size, 0); -} - -static inline void v9fs_path_write_lock(V9fsState *s) -{ - if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { - qemu_co_rwlock_wrlock(&s->rename_lock); - } -} - -static inline void v9fs_path_read_lock(V9fsState *s) -{ - if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { - qemu_co_rwlock_rdlock(&s->rename_lock); - } -} - -static inline void v9fs_path_unlock(V9fsState *s) -{ - if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { - qemu_co_rwlock_unlock(&s->rename_lock); - } -} - -static inline uint8_t v9fs_request_cancelled(V9fsPDU *pdu) -{ - return pdu->cancelled; -} + V9fsPDU pdus[MAX_REQ]; + VirtQueueElement *elems[MAX_REQ]; + V9fsState state; +} V9fsVirtioState; -extern void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq); -extern void v9fs_reclaim_fd(V9fsPDU *pdu); -extern void v9fs_path_init(V9fsPath *path); -extern void v9fs_path_free(V9fsPath *path); -extern void v9fs_path_copy(V9fsPath *lhs, V9fsPath *rhs); -extern int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath, - const char *name, V9fsPath *path); +extern void virtio_9p_push_and_notify(V9fsPDU *pdu); -#define pdu_marshal(pdu, offset, fmt, args...) \ - v9fs_marshal(pdu->elem.in_sg, pdu->elem.in_num, offset, 1, fmt, ##args) -#define pdu_unmarshal(pdu, offset, fmt, args...) \ - v9fs_unmarshal(pdu->elem.out_sg, pdu->elem.out_num, offset, 1, fmt, ##args) +ssize_t virtio_pdu_vmarshal(V9fsPDU *pdu, size_t offset, + const char *fmt, va_list ap); +ssize_t virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset, + const char *fmt, va_list ap); +void virtio_init_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, + unsigned int *pniov, bool is_write); #define TYPE_VIRTIO_9P "virtio-9p-device" #define VIRTIO_9P(obj) \ - OBJECT_CHECK(V9fsState, (obj), TYPE_VIRTIO_9P) + OBJECT_CHECK(V9fsVirtioState, (obj), TYPE_VIRTIO_9P) #endif diff --git a/qemu/hw/Makefile.objs b/qemu/hw/Makefile.objs index 73afa41b3..4a07ed434 100644 --- a/qemu/hw/Makefile.objs +++ b/qemu/hw/Makefile.objs @@ -13,6 +13,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += ide/ devices-dirs-$(CONFIG_SOFTMMU) += input/ devices-dirs-$(CONFIG_SOFTMMU) += intc/ devices-dirs-$(CONFIG_IPACK) += ipack/ +devices-dirs-$(CONFIG_IPMI) += ipmi/ devices-dirs-$(CONFIG_SOFTMMU) += isa/ devices-dirs-$(CONFIG_SOFTMMU) += misc/ devices-dirs-$(CONFIG_SOFTMMU) += net/ @@ -31,6 +32,7 @@ devices-dirs-$(CONFIG_VIRTIO) += virtio/ devices-dirs-$(CONFIG_SOFTMMU) += watchdog/ devices-dirs-$(CONFIG_SOFTMMU) += xen/ devices-dirs-$(CONFIG_MEM_HOTPLUG) += mem/ +devices-dirs-$(CONFIG_SMBIOS) += smbios/ devices-dirs-y += core/ common-obj-y += $(devices-dirs-y) obj-y += $(devices-dirs-y) diff --git a/qemu/hw/acpi/Makefile.objs b/qemu/hw/acpi/Makefile.objs index 7d3230c2a..faee86c5c 100644 --- a/qemu/hw/acpi/Makefile.objs +++ b/qemu/hw/acpi/Makefile.objs @@ -1,7 +1,8 @@ common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o pcihp.o common-obj-$(CONFIG_ACPI_X86_ICH) += ich9.o tco.o -common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o -common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o +common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o cpu_hotplug_acpi_table.o +common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o memory_hotplug_acpi_table.o +obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o common-obj-$(CONFIG_ACPI) += acpi_interface.o common-obj-$(CONFIG_ACPI) += bios-linker-loader.o common-obj-$(CONFIG_ACPI) += aml-build.o diff --git a/qemu/hw/acpi/acpi_interface.c b/qemu/hw/acpi/acpi_interface.c index c181bb226..d82131326 100644 --- a/qemu/hw/acpi/acpi_interface.c +++ b/qemu/hw/acpi/acpi_interface.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/acpi/acpi_dev_interface.h" #include "qemu/module.h" diff --git a/qemu/hw/acpi/aml-build.c b/qemu/hw/acpi/aml-build.c index 0d4b3247b..ab89ca638 100644 --- a/qemu/hw/acpi/aml-build.c +++ b/qemu/hw/acpi/aml-build.c @@ -19,12 +19,8 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include -#include -#include -#include -#include -#include #include "hw/acpi/aml-build.h" #include "qemu/bswap.h" #include "qemu/bitops.h" @@ -262,6 +258,34 @@ static void build_append_int(GArray *table, uint64_t value) } } +/* + * Build NAME(XXXX, 0x00000000) where 0x00000000 is encoded as a dword, + * and return the offset to 0x00000000 for runtime patching. + * + * Warning: runtime patching is best avoided. Only use this as + * a replacement for DataTableRegion (for guests that don't + * support it). + */ +int +build_append_named_dword(GArray *array, const char *name_format, ...) +{ + int offset; + va_list ap; + + build_append_byte(array, 0x08); /* NameOp */ + va_start(ap, name_format); + build_append_namestringv(array, name_format, ap); + va_end(ap); + + build_append_byte(array, 0x0C); /* DWordPrefix */ + + offset = array->len; + build_append_int_noprefix(array, 0x00000000, 4); + assert(array->len == offset + 4); + + return offset; +} + static GPtrArray *alloc_list; static Aml *aml_alloc(void) @@ -427,6 +451,41 @@ Aml *aml_arg(int pos) return var; } +/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToInteger */ +Aml *aml_to_integer(Aml *arg) +{ + Aml *var = aml_opcode(0x99 /* ToIntegerOp */); + aml_append(var, arg); + build_append_byte(var->buf, 0x00 /* NullNameOp */); + return var; +} + +/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToHexString */ +Aml *aml_to_hexstring(Aml *src, Aml *dst) +{ + Aml *var = aml_opcode(0x98 /* ToHexStringOp */); + aml_append(var, src); + if (dst) { + aml_append(var, dst); + } else { + build_append_byte(var->buf, 0x00 /* NullNameOp */); + } + return var; +} + +/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToBuffer */ +Aml *aml_to_buffer(Aml *src, Aml *dst) +{ + Aml *var = aml_opcode(0x96 /* ToBufferOp */); + aml_append(var, src); + if (dst) { + aml_append(var, dst); + } else { + build_append_byte(var->buf, 0x00 /* NullNameOp */); + } + return var; +} + /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefStore */ Aml *aml_store(Aml *val, Aml *target) { @@ -436,44 +495,64 @@ Aml *aml_store(Aml *val, Aml *target) return var; } -/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAnd */ -Aml *aml_and(Aml *arg1, Aml *arg2) +/** + * build_opcode_2arg_dst: + * @op: 1-byte opcode + * @arg1: 1st operand + * @arg2: 2nd operand + * @dst: optional target to store to, set to NULL if it's not required + * + * An internal helper to compose AML terms that have + * "Op Operand Operand Target" + * pattern. + * + * Returns: The newly allocated and composed according to patter Aml object. + */ +static Aml * +build_opcode_2arg_dst(uint8_t op, Aml *arg1, Aml *arg2, Aml *dst) { - Aml *var = aml_opcode(0x7B /* AndOp */); + Aml *var = aml_opcode(op); aml_append(var, arg1); aml_append(var, arg2); - build_append_byte(var->buf, 0x00 /* NullNameOp */); + if (dst) { + aml_append(var, dst); + } else { + build_append_byte(var->buf, 0x00 /* NullNameOp */); + } return var; } +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAnd */ +Aml *aml_and(Aml *arg1, Aml *arg2, Aml *dst) +{ + return build_opcode_2arg_dst(0x7B /* AndOp */, arg1, arg2, dst); +} + /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefOr */ -Aml *aml_or(Aml *arg1, Aml *arg2) +Aml *aml_or(Aml *arg1, Aml *arg2, Aml *dst) { - Aml *var = aml_opcode(0x7D /* OrOp */); + return build_opcode_2arg_dst(0x7D /* OrOp */, arg1, arg2, dst); +} + +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLOr */ +Aml *aml_lor(Aml *arg1, Aml *arg2) +{ + Aml *var = aml_opcode(0x91 /* LOrOp */); aml_append(var, arg1); aml_append(var, arg2); - build_append_byte(var->buf, 0x00 /* NullNameOp */); return var; } /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefShiftLeft */ Aml *aml_shiftleft(Aml *arg1, Aml *count) { - Aml *var = aml_opcode(0x79 /* ShiftLeftOp */); - aml_append(var, arg1); - aml_append(var, count); - build_append_byte(var->buf, 0x00); /* NullNameOp */ - return var; + return build_opcode_2arg_dst(0x79 /* ShiftLeftOp */, arg1, count, NULL); } /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefShiftRight */ -Aml *aml_shiftright(Aml *arg1, Aml *count) +Aml *aml_shiftright(Aml *arg1, Aml *count, Aml *dst) { - Aml *var = aml_opcode(0x7A /* ShiftRightOp */); - aml_append(var, arg1); - aml_append(var, count); - build_append_byte(var->buf, 0x00); /* NullNameOp */ - return var; + return build_opcode_2arg_dst(0x7A /* ShiftRightOp */, arg1, count, dst); } /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLLess */ @@ -486,13 +565,15 @@ Aml *aml_lless(Aml *arg1, Aml *arg2) } /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAdd */ -Aml *aml_add(Aml *arg1, Aml *arg2) +Aml *aml_add(Aml *arg1, Aml *arg2, Aml *dst) { - Aml *var = aml_opcode(0x72 /* AddOp */); - aml_append(var, arg1); - aml_append(var, arg2); - build_append_byte(var->buf, 0x00 /* NullNameOp */); - return var; + return build_opcode_2arg_dst(0x72 /* AddOp */, arg1, arg2, dst); +} + +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefSubtract */ +Aml *aml_subtract(Aml *arg1, Aml *arg2, Aml *dst) +{ + return build_opcode_2arg_dst(0x74 /* SubtractOp */, arg1, arg2, dst); } /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefIncrement */ @@ -503,14 +584,18 @@ Aml *aml_increment(Aml *arg) return var; } +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefDecrement */ +Aml *aml_decrement(Aml *arg) +{ + Aml *var = aml_opcode(0x76 /* DecrementOp */); + aml_append(var, arg); + return var; +} + /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefIndex */ Aml *aml_index(Aml *arg1, Aml *idx) { - Aml *var = aml_opcode(0x88 /* IndexOp */); - aml_append(var, arg1); - aml_append(var, idx); - build_append_byte(var->buf, 0x00 /* NullNameOp */); - return var; + return build_opcode_2arg_dst(0x88 /* IndexOp */, arg1, idx, NULL); } /* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefNotify */ @@ -522,6 +607,14 @@ Aml *aml_notify(Aml *arg1, Aml *arg2) return var; } +/* helper to call method with 1 argument */ +Aml *aml_call0(const char *method) +{ + Aml *var = aml_alloc(); + build_append_namestring(var->buf, "%s", method); + return var; +} + /* helper to call method with 1 argument */ Aml *aml_call1(const char *method, Aml *arg1) { @@ -564,6 +657,94 @@ Aml *aml_call4(const char *method, Aml *arg1, Aml *arg2, Aml *arg3, Aml *arg4) return var; } +/* + * ACPI 5.0: 6.4.3.8.1 GPIO Connection Descriptor + * Type 1, Large Item Name 0xC + */ + +static Aml *aml_gpio_connection(AmlGpioConnectionType type, + AmlConsumerAndProducer con_and_pro, + uint8_t flags, AmlPinConfig pin_config, + uint16_t output_drive, + uint16_t debounce_timeout, + const uint32_t pin_list[], uint32_t pin_count, + const char *resource_source_name, + const uint8_t *vendor_data, + uint16_t vendor_data_len) +{ + Aml *var = aml_alloc(); + const uint16_t min_desc_len = 0x16; + uint16_t resource_source_name_len, length; + uint16_t pin_table_offset, resource_source_name_offset, vendor_data_offset; + uint32_t i; + + assert(resource_source_name); + resource_source_name_len = strlen(resource_source_name) + 1; + length = min_desc_len + resource_source_name_len + vendor_data_len; + pin_table_offset = min_desc_len + 1; + resource_source_name_offset = pin_table_offset + pin_count * 2; + vendor_data_offset = resource_source_name_offset + resource_source_name_len; + + build_append_byte(var->buf, 0x8C); /* GPIO Connection Descriptor */ + build_append_int_noprefix(var->buf, length, 2); /* Length */ + build_append_byte(var->buf, 1); /* Revision ID */ + build_append_byte(var->buf, type); /* GPIO Connection Type */ + /* General Flags (2 bytes) */ + build_append_int_noprefix(var->buf, con_and_pro, 2); + /* Interrupt and IO Flags (2 bytes) */ + build_append_int_noprefix(var->buf, flags, 2); + /* Pin Configuration 0 = Default 1 = Pull-up 2 = Pull-down 3 = No Pull */ + build_append_byte(var->buf, pin_config); + /* Output Drive Strength (2 bytes) */ + build_append_int_noprefix(var->buf, output_drive, 2); + /* Debounce Timeout (2 bytes) */ + build_append_int_noprefix(var->buf, debounce_timeout, 2); + /* Pin Table Offset (2 bytes) */ + build_append_int_noprefix(var->buf, pin_table_offset, 2); + build_append_byte(var->buf, 0); /* Resource Source Index */ + /* Resource Source Name Offset (2 bytes) */ + build_append_int_noprefix(var->buf, resource_source_name_offset, 2); + /* Vendor Data Offset (2 bytes) */ + build_append_int_noprefix(var->buf, vendor_data_offset, 2); + /* Vendor Data Length (2 bytes) */ + build_append_int_noprefix(var->buf, vendor_data_len, 2); + /* Pin Number (2n bytes)*/ + for (i = 0; i < pin_count; i++) { + build_append_int_noprefix(var->buf, pin_list[i], 2); + } + + /* Resource Source Name */ + build_append_namestring(var->buf, "%s", resource_source_name); + build_append_byte(var->buf, '\0'); + + /* Vendor-defined Data */ + if (vendor_data != NULL) { + g_array_append_vals(var->buf, vendor_data, vendor_data_len); + } + + return var; +} + +/* + * ACPI 5.0: 19.5.53 + * GpioInt(GPIO Interrupt Connection Resource Descriptor Macro) + */ +Aml *aml_gpio_int(AmlConsumerAndProducer con_and_pro, + AmlLevelAndEdge edge_level, + AmlActiveHighAndLow active_level, AmlShared shared, + AmlPinConfig pin_config, uint16_t debounce_timeout, + const uint32_t pin_list[], uint32_t pin_count, + const char *resource_source_name, + const uint8_t *vendor_data, uint16_t vendor_data_len) +{ + uint8_t flags = edge_level | (active_level << 1) | (shared << 3); + + return aml_gpio_connection(AML_INTERRUPT_CONNECTION, con_and_pro, flags, + pin_config, 0, debounce_timeout, pin_list, + pin_count, resource_source_name, vendor_data, + vendor_data_len); +} + /* * ACPI 1.0b: 6.4.3.4 32-Bit Fixed Location Memory Range Descriptor * (Type 1, Large Item Name 0x6) @@ -598,23 +779,27 @@ Aml *aml_memory32_fixed(uint32_t addr, uint32_t size, Aml *aml_interrupt(AmlConsumerAndProducer con_and_pro, AmlLevelAndEdge level_and_edge, AmlActiveHighAndLow high_and_low, AmlShared shared, - uint32_t irq) + uint32_t *irq_list, uint8_t irq_count) { + int i; Aml *var = aml_alloc(); uint8_t irq_flags = con_and_pro | (level_and_edge << 1) | (high_and_low << 2) | (shared << 3); + const int header_bytes_in_len = 2; + uint16_t len = header_bytes_in_len + irq_count * sizeof(uint32_t); + + assert(irq_count > 0); build_append_byte(var->buf, 0x89); /* Extended irq descriptor */ - build_append_byte(var->buf, 6); /* Length, bits[7:0] minimum value = 6 */ - build_append_byte(var->buf, 0); /* Length, bits[15:8] minimum value = 0 */ + build_append_byte(var->buf, len & 0xFF); /* Length, bits[7:0] */ + build_append_byte(var->buf, len >> 8); /* Length, bits[15:8] */ build_append_byte(var->buf, irq_flags); /* Interrupt Vector Information. */ - build_append_byte(var->buf, 0x01); /* Interrupt table length = 1 */ + build_append_byte(var->buf, irq_count); /* Interrupt table length */ - /* Interrupt Number */ - build_append_byte(var->buf, extract32(irq, 0, 8)); /* bits[7:0] */ - build_append_byte(var->buf, extract32(irq, 8, 8)); /* bits[15:8] */ - build_append_byte(var->buf, extract32(irq, 16, 8)); /* bits[23:16] */ - build_append_byte(var->buf, extract32(irq, 24, 8)); /* bits[31:24] */ + /* Interrupt Number List */ + for (i = 0; i < irq_count; i++) { + build_append_int_noprefix(var->buf, irq_list[i], 4); + } return var; } @@ -672,6 +857,26 @@ Aml *aml_equal(Aml *arg1, Aml *arg2) return var; } +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLGreater */ +Aml *aml_lgreater(Aml *arg1, Aml *arg2) +{ + Aml *var = aml_opcode(0x94 /* LGreaterOp */); + aml_append(var, arg1); + aml_append(var, arg2); + return var; +} + +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLGreaterEqual */ +Aml *aml_lgreater_equal(Aml *arg1, Aml *arg2) +{ + /* LGreaterEqualOp := LNotOp LLessOp */ + Aml *var = aml_opcode(0x92 /* LNotOp */); + build_append_byte(var->buf, 0x95 /* LLessOp */); + aml_append(var, arg1); + aml_append(var, arg2); + return var; +} + /* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefIfElse */ Aml *aml_if(Aml *predicate) { @@ -696,11 +901,24 @@ Aml *aml_while(Aml *predicate) } /* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefMethod */ -Aml *aml_method(const char *name, int arg_count) +Aml *aml_method(const char *name, int arg_count, AmlSerializeFlag sflag) { Aml *var = aml_bundle(0x14 /* MethodOp */, AML_PACKAGE); + int methodflags; + + /* + * MethodFlags: + * bit 0-2: ArgCount (0-7) + * bit 3: SerializeFlag + * 0: NotSerialized + * 1: Serialized + * bit 4-7: reserved (must be 0) + */ + assert(arg_count < 8); + methodflags = arg_count | (sflag << 3); + build_append_namestring(var->buf, "%s", name); - build_append_byte(var->buf, arg_count); /* MethodFlags: ArgCount */ + build_append_byte(var->buf, methodflags); /* MethodFlags: ArgCount */ return var; } @@ -752,14 +970,14 @@ Aml *aml_package(uint8_t num_elements) /* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefOpRegion */ Aml *aml_operation_region(const char *name, AmlRegionSpace rs, - uint32_t offset, uint32_t len) + Aml *offset, uint32_t len) { Aml *var = aml_alloc(); build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */ build_append_byte(var->buf, 0x80); /* OpRegionOp */ build_append_namestring(var->buf, "%s", name); build_append_byte(var->buf, rs); - build_append_int(var->buf, offset); + aml_append(var, offset); build_append_int(var->buf, len); return var; } @@ -784,27 +1002,57 @@ Aml *aml_reserved_field(unsigned length) } /* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefField */ -Aml *aml_field(const char *name, AmlAccessType type, AmlUpdateRule rule) +Aml *aml_field(const char *name, AmlAccessType type, AmlLockRule lock, + AmlUpdateRule rule) { Aml *var = aml_bundle(0x81 /* FieldOp */, AML_EXT_PACKAGE); uint8_t flags = rule << 5 | type; + flags |= lock << 4; /* LockRule at 4 bit offset */ + build_append_namestring(var->buf, "%s", name); build_append_byte(var->buf, flags); return var; } -/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateDWordField */ -Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name) +static +Aml *create_field_common(int opcode, Aml *srcbuf, Aml *index, const char *name) { - Aml *var = aml_alloc(); - build_append_byte(var->buf, 0x8A); /* CreateDWordFieldOp */ + Aml *var = aml_opcode(opcode); aml_append(var, srcbuf); aml_append(var, index); build_append_namestring(var->buf, "%s", name); return var; } +/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateField */ +Aml *aml_create_field(Aml *srcbuf, Aml *bit_index, Aml *num_bits, + const char *name) +{ + Aml *var = aml_alloc(); + build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */ + build_append_byte(var->buf, 0x13); /* CreateFieldOp */ + aml_append(var, srcbuf); + aml_append(var, bit_index); + aml_append(var, num_bits); + build_append_namestring(var->buf, "%s", name); + return var; +} + +/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateDWordField */ +Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name) +{ + return create_field_common(0x8A /* CreateDWordFieldOp */, + srcbuf, index, name); +} + +/* ACPI 2.0a: 17.2.4.2 Named Objects Encoding: DefCreateQWordField */ +Aml *aml_create_qword_field(Aml *srcbuf, Aml *index, const char *name) +{ + return create_field_common(0x8F /* CreateQWordFieldOp */, + srcbuf, index, name); +} + /* ACPI 1.0b: 16.2.3 Data Objects Encoding: String */ Aml *aml_string(const char *name_format, ...) { @@ -1065,6 +1313,30 @@ Aml *aml_qword_memory(AmlDecode dec, AmlMinFixed min_fixed, addr_trans, len, flags); } +/* ACPI 1.0b: 6.4.2.2 DMA Format/6.4.2.2.1 ASL Macro for DMA Descriptor */ +Aml *aml_dma(AmlDmaType typ, AmlDmaBusMaster bm, AmlTransferSize sz, + uint8_t channel) +{ + Aml *var = aml_alloc(); + uint8_t flags = sz | bm << 2 | typ << 5; + + assert(channel < 8); + build_append_byte(var->buf, 0x2A); /* Byte 0: DMA Descriptor */ + build_append_byte(var->buf, 1U << channel); /* Byte 1: _DMA - DmaChannel */ + build_append_byte(var->buf, flags); /* Byte 2 */ + return var; +} + +/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefSleep */ +Aml *aml_sleep(uint64_t msec) +{ + Aml *var = aml_alloc(); + build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */ + build_append_byte(var->buf, 0x22); /* SleepOp */ + aml_append(var, aml_int(msec)); + return var; +} + static uint8_t Hex2Byte(const char *src) { int hi, lo; @@ -1135,23 +1407,100 @@ Aml *aml_unicode(const char *str) return var; } +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefDerefOf */ +Aml *aml_derefof(Aml *arg) +{ + Aml *var = aml_opcode(0x83 /* DerefOfOp */); + aml_append(var, arg); + return var; +} + +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefSizeOf */ +Aml *aml_sizeof(Aml *arg) +{ + Aml *var = aml_opcode(0x87 /* SizeOfOp */); + aml_append(var, arg); + return var; +} + +/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefMutex */ +Aml *aml_mutex(const char *name, uint8_t sync_level) +{ + Aml *var = aml_alloc(); + build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */ + build_append_byte(var->buf, 0x01); /* MutexOp */ + build_append_namestring(var->buf, "%s", name); + assert(!(sync_level & 0xF0)); + build_append_byte(var->buf, sync_level); + return var; +} + +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAcquire */ +Aml *aml_acquire(Aml *mutex, uint16_t timeout) +{ + Aml *var = aml_alloc(); + build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */ + build_append_byte(var->buf, 0x23); /* AcquireOp */ + aml_append(var, mutex); + build_append_int_noprefix(var->buf, timeout, sizeof(timeout)); + return var; +} + +/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefRelease */ +Aml *aml_release(Aml *mutex) +{ + Aml *var = aml_alloc(); + build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */ + build_append_byte(var->buf, 0x27); /* ReleaseOp */ + aml_append(var, mutex); + return var; +} + +/* ACPI 1.0b: 16.2.5.1 Name Space Modifier Objects Encoding: DefAlias */ +Aml *aml_alias(const char *source_object, const char *alias_object) +{ + Aml *var = aml_opcode(0x06 /* AliasOp */); + aml_append(var, aml_name("%s", source_object)); + aml_append(var, aml_name("%s", alias_object)); + return var; +} + +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefConcat */ +Aml *aml_concatenate(Aml *source1, Aml *source2, Aml *target) +{ + return build_opcode_2arg_dst(0x73 /* ConcatOp */, source1, source2, + target); +} + void build_header(GArray *linker, GArray *table_data, - AcpiTableHeader *h, const char *sig, int len, uint8_t rev) + AcpiTableHeader *h, const char *sig, int len, uint8_t rev, + const char *oem_id, const char *oem_table_id) { memcpy(&h->signature, sig, 4); h->length = cpu_to_le32(len); h->revision = rev; - memcpy(h->oem_id, ACPI_BUILD_APPNAME6, 6); - memcpy(h->oem_table_id, ACPI_BUILD_APPNAME4, 4); - memcpy(h->oem_table_id + 4, sig, 4); + + if (oem_id) { + strncpy((char *)h->oem_id, oem_id, sizeof h->oem_id); + } else { + memcpy(h->oem_id, ACPI_BUILD_APPNAME6, 6); + } + + if (oem_table_id) { + strncpy((char *)h->oem_table_id, oem_table_id, sizeof(h->oem_table_id)); + } else { + memcpy(h->oem_table_id, ACPI_BUILD_APPNAME4, 4); + memcpy(h->oem_table_id + 4, sig, 4); + } + h->oem_revision = cpu_to_le32(1); memcpy(h->asl_compiler_id, ACPI_BUILD_APPNAME4, 4); h->asl_compiler_revision = cpu_to_le32(1); h->checksum = 0; /* Checksum to be filled in by Guest linker */ bios_linker_loader_add_checksum(linker, ACPI_BUILD_TABLE_FILE, - table_data->data, h, len, &h->checksum); + table_data, h, len, &h->checksum); } void *acpi_data_push(GArray *table_data, unsigned size) @@ -1163,9 +1512,7 @@ void *acpi_data_push(GArray *table_data, unsigned size) unsigned acpi_data_len(GArray *table) { -#if GLIB_CHECK_VERSION(2, 22, 0) assert(g_array_get_element_size(table) == 1); -#endif return table->len; } @@ -1194,7 +1541,8 @@ void acpi_build_tables_cleanup(AcpiBuildTables *tables, bool mfre) /* Build rsdt table */ void -build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets) +build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets, + const char *oem_id, const char *oem_table_id) { AcpiRsdtDescriptorRev1 *rsdt; size_t rsdt_len; @@ -1213,5 +1561,5 @@ build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets) sizeof(uint32_t)); } build_header(linker, table_data, - (void *)rsdt, "RSDT", rsdt_len, 1); + (void *)rsdt, "RSDT", rsdt_len, 1, oem_id, oem_table_id); } diff --git a/qemu/hw/acpi/bios-linker-loader.c b/qemu/hw/acpi/bios-linker-loader.c index d9382f826..5153ab151 100644 --- a/qemu/hw/acpi/bios-linker-loader.c +++ b/qemu/hw/acpi/bios-linker-loader.c @@ -18,12 +18,20 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/acpi/bios-linker-loader.h" #include "hw/nvram/fw_cfg.h" #include "qemu/bswap.h" +/* + * Linker/loader is a paravirtualized interface that passes commands to guest. + * The commands can be used to request guest to + * - allocate memory chunks and initialize them from QEMU FW CFG files + * - link allocated chunks by storing pointer to one chunk into another + * - calculate ACPI checksum of part of the chunk and store into same chunk + */ #define BIOS_LINKER_LOADER_FILESZ FW_CFG_MAX_FILE_PATH struct BiosLinkerLoaderEntry { @@ -87,6 +95,12 @@ enum { BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG = 0x2, }; +/* + * bios_linker_loader_init: allocate a new linker file blob array. + * + * After initialization, linker commands can be added, and will + * be stored in the array. + */ GArray *bios_linker_loader_init(void) { return g_array_new(false, true /* clear */, 1); @@ -98,6 +112,16 @@ void *bios_linker_loader_cleanup(GArray *linker) return g_array_free(linker, false); } +/* + * bios_linker_loader_alloc: ask guest to load file into guest memory. + * + * @linker: linker file blob array + * @file: file to be loaded + * @alloc_align: required minimal alignment in bytes. Must be a power of 2. + * @alloc_fseg: request allocation in FSEG zone (useful for the RSDP ACPI table) + * + * Note: this command must precede any other linker command using this file. + */ void bios_linker_loader_alloc(GArray *linker, const char *file, uint32_t alloc_align, @@ -105,35 +129,90 @@ void bios_linker_loader_alloc(GArray *linker, { BiosLinkerLoaderEntry entry; + assert(!(alloc_align & (alloc_align - 1))); + memset(&entry, 0, sizeof entry); strncpy(entry.alloc.file, file, sizeof entry.alloc.file - 1); entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ALLOCATE); entry.alloc.align = cpu_to_le32(alloc_align); - entry.alloc.zone = cpu_to_le32(alloc_fseg ? - BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG : - BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH); + entry.alloc.zone = alloc_fseg ? BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG : + BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH; /* Alloc entries must come first, so prepend them */ g_array_prepend_vals(linker, &entry, sizeof entry); } +/* + * bios_linker_loader_add_checksum: ask guest to add checksum of file data + * into (same) file at the specified pointer. + * + * Checksum calculation simply sums -X for each byte X in the range + * using 8-bit math (i.e. ACPI checksum). + * + * @linker: linker file blob array + * @file: file that includes the checksum to be calculated + * and the data to be checksummed + * @table: @file blob contents + * @start, @size: range of data to checksum + * @checksum: location of the checksum to be patched within file blob + * + * Notes: + * - checksum byte initial value must have been pushed into @table + * and reside at address @checksum. + * - @size bytes must have been pushed into @table and reside at address + * @start. + * - Guest calculates checksum of specified range of data, result is added to + * initial value at @checksum into copy of @file in Guest memory. + * - Range might include the checksum itself. + * - To avoid confusion, caller must always put 0x0 at @checksum. + * - @file must be loaded into Guest memory using bios_linker_loader_alloc + */ void bios_linker_loader_add_checksum(GArray *linker, const char *file, - void *table, + GArray *table, void *start, unsigned size, uint8_t *checksum) { BiosLinkerLoaderEntry entry; + ptrdiff_t checksum_offset = (gchar *)checksum - table->data; + ptrdiff_t start_offset = (gchar *)start - table->data; + + assert(checksum_offset >= 0); + assert(start_offset >= 0); + assert(checksum_offset + 1 <= table->len); + assert(start_offset + size <= table->len); + assert(*checksum == 0x0); memset(&entry, 0, sizeof entry); strncpy(entry.cksum.file, file, sizeof entry.cksum.file - 1); entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM); - entry.cksum.offset = cpu_to_le32(checksum - (uint8_t *)table); - entry.cksum.start = cpu_to_le32((uint8_t *)start - (uint8_t *)table); + entry.cksum.offset = cpu_to_le32(checksum_offset); + entry.cksum.start = cpu_to_le32(start_offset); entry.cksum.length = cpu_to_le32(size); g_array_append_vals(linker, &entry, sizeof entry); } +/* + * bios_linker_loader_add_pointer: ask guest to add address of source file + * into destination file at the specified pointer. + * + * @linker: linker file blob array + * @dest_file: destination file that must be changed + * @src_file: source file who's address must be taken + * @table: @dest_file blob contents array + * @pointer: location of the pointer to be patched within destination file blob + * @pointer_size: size of pointer to be patched, in bytes + * + * Notes: + * - @pointer_size bytes must have been pushed into @table + * and reside at address @pointer. + * - Guest address is added to initial value at @pointer + * into copy of @dest_file in Guest memory. + * e.g. to get start of src_file in guest memory, put 0x0 there + * to get address of a field at offset 0x10 in src_file, put 0x10 there + * - Both @dest_file and @src_file must be + * loaded into Guest memory using bios_linker_loader_alloc + */ void bios_linker_loader_add_pointer(GArray *linker, const char *dest_file, const char *src_file, @@ -141,7 +220,10 @@ void bios_linker_loader_add_pointer(GArray *linker, uint8_t pointer_size) { BiosLinkerLoaderEntry entry; - size_t offset = (gchar *)pointer - table->data; + ptrdiff_t offset = (gchar *)pointer - table->data; + + assert(offset >= 0); + assert(offset + pointer_size <= table->len); memset(&entry, 0, sizeof entry); strncpy(entry.pointer.dest_file, dest_file, @@ -149,7 +231,6 @@ void bios_linker_loader_add_pointer(GArray *linker, strncpy(entry.pointer.src_file, src_file, sizeof entry.pointer.src_file - 1); entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_POINTER); - assert(table->len >= offset + pointer_size); entry.pointer.offset = cpu_to_le32(offset); entry.pointer.size = pointer_size; assert(pointer_size == 1 || pointer_size == 2 || diff --git a/qemu/hw/acpi/core.c b/qemu/hw/acpi/core.c index fe6215af4..6a2f45214 100644 --- a/qemu/hw/acpi/core.c +++ b/qemu/hw/acpi/core.c @@ -18,6 +18,7 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "sysemu/sysemu.h" #include "hw/hw.h" #include "hw/i386/pc.h" @@ -25,7 +26,6 @@ #include "hw/nvram/fw_cfg.h" #include "qemu/config-file.h" #include "qapi/opts-visitor.h" -#include "qapi/dealloc-visitor.h" #include "qapi-visit.h" #include "qapi-event.h" @@ -67,7 +67,7 @@ static void acpi_register_config(void) qemu_add_opts(&qemu_acpi_opts); } -machine_init(acpi_register_config); +opts_init(acpi_register_config); static int acpi_checksum(const uint8_t *data, int len) { @@ -242,7 +242,7 @@ void acpi_table_add(const QemuOpts *opts, Error **errp) OptsVisitor *ov; ov = opts_visitor_new(opts); - visit_type_AcpiTableOptions(opts_get_visitor(ov), &hdrs, NULL, &err); + visit_type_AcpiTableOptions(opts_get_visitor(ov), NULL, &hdrs, &err); opts_visitor_cleanup(ov); } @@ -296,15 +296,7 @@ void acpi_table_add(const QemuOpts *opts, Error **errp) out: g_free(blob); g_strfreev(pathnames); - - if (hdrs != NULL) { - QapiDeallocVisitor *dv; - - dv = qapi_dealloc_visitor_new(); - visit_type_AcpiTableOptions(qapi_dealloc_get_visitor(dv), &hdrs, NULL, - NULL); - qapi_dealloc_visitor_cleanup(dv); - } + qapi_free_AcpiTableOptions(hdrs); error_propagate(errp, err); } @@ -349,6 +341,22 @@ uint8_t *acpi_table_next(uint8_t *current) } } +int acpi_get_slic_oem(AcpiSlicOem *oem) +{ + uint8_t *u; + + for (u = acpi_table_first(); u; u = acpi_table_next(u)) { + struct acpi_table_header *hdr = (void *)(u - sizeof(hdr->_length)); + + if (memcmp(hdr->sig, "SLIC", 4) == 0) { + oem->id = hdr->oem_id; + oem->table_id = hdr->oem_table_id; + return 0; + } + } + return -1; +} + static void acpi_notify_wakeup(Notifier *notifier, void *data) { ACPIREGS *ar = container_of(notifier, ACPIREGS, wakeup); @@ -381,7 +389,7 @@ uint16_t acpi_pm1_evt_get_sts(ACPIREGS *ar) acpi_pm_tmr_update function uses ns for setting the timer. */ int64_t d = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); if (d >= muldiv64(ar->tmr.overflow_time, - get_ticks_per_sec(), PM_TIMER_FREQUENCY)) { + NANOSECONDS_PER_SECOND, PM_TIMER_FREQUENCY)) { ar->pm1.evt.sts |= ACPI_BITMASK_TIMER_STATUS; } return ar->pm1.evt.sts; @@ -475,7 +483,7 @@ void acpi_pm_tmr_update(ACPIREGS *ar, bool enable) /* schedule a timer interruption if needed */ if (enable) { - expire_time = muldiv64(ar->tmr.overflow_time, get_ticks_per_sec(), + expire_time = muldiv64(ar->tmr.overflow_time, NANOSECONDS_PER_SECOND, PM_TIMER_FREQUENCY); timer_mod(ar->tmr.timer, expire_time); } else { @@ -528,7 +536,6 @@ void acpi_pm_tmr_init(ACPIREGS *ar, acpi_update_sci_fn update_sci, ar->tmr.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, acpi_pm_tmr_timer, ar); memory_region_init_io(&ar->tmr.io, memory_region_owner(parent), &acpi_pm_tmr_ops, ar, "acpi-tmr", 4); - memory_region_clear_global_locking(&ar->tmr.io); memory_region_add_subregion(parent, 8, &ar->tmr.io); } @@ -625,8 +632,12 @@ void acpi_pm1_cnt_reset(ACPIREGS *ar) void acpi_gpe_init(ACPIREGS *ar, uint8_t len) { ar->gpe.len = len; - ar->gpe.sts = g_malloc0(len / 2); - ar->gpe.en = g_malloc0(len / 2); + /* Only first len / 2 bytes are ever used, + * but the caller in ich9.c migrates full len bytes. + * TODO: fix ich9.c and drop the extra allocation. + */ + ar->gpe.sts = g_malloc0(len); + ar->gpe.en = g_malloc0(len); } void acpi_gpe_reset(ACPIREGS *ar) diff --git a/qemu/hw/acpi/cpu_hotplug.c b/qemu/hw/acpi/cpu_hotplug.c index f5b9972f2..4d86743fd 100644 --- a/qemu/hw/acpi/cpu_hotplug.c +++ b/qemu/hw/acpi/cpu_hotplug.c @@ -9,8 +9,11 @@ * 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 "hw/hw.h" #include "hw/acpi/cpu_hotplug.h" +#include "qapi/error.h" +#include "qom/cpu.h" static uint64_t cpu_status_read(void *opaque, hwaddr addr, unsigned int size) { diff --git a/qemu/hw/acpi/cpu_hotplug_acpi_table.c b/qemu/hw/acpi/cpu_hotplug_acpi_table.c new file mode 100644 index 000000000..97bb1092a --- /dev/null +++ b/qemu/hw/acpi/cpu_hotplug_acpi_table.c @@ -0,0 +1,136 @@ +/* + * 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 . + */ + +#include "qemu/osdep.h" +#include "hw/acpi/cpu_hotplug.h" + +void build_cpu_hotplug_aml(Aml *ctx) +{ + Aml *method; + Aml *if_ctx; + Aml *else_ctx; + Aml *sb_scope = aml_scope("_SB"); + uint8_t madt_tmpl[8] = {0x00, 0x08, 0x00, 0x00, 0x00, 0, 0, 0}; + Aml *cpu_id = aml_arg(0); + Aml *cpu_on = aml_local(0); + Aml *madt = aml_local(1); + Aml *cpus_map = aml_name(CPU_ON_BITMAP); + Aml *zero = aml_int(0); + Aml *one = aml_int(1); + + /* + * _MAT method - creates an madt apic buffer + * cpu_id = Arg0 = Processor ID = Local APIC ID + * cpu_on = Local0 = CPON flag for this cpu + * madt = Local1 = Buffer (in madt apic form) to return + */ + method = aml_method(CPU_MAT_METHOD, 1, AML_NOTSERIALIZED); + aml_append(method, + aml_store(aml_derefof(aml_index(cpus_map, cpu_id)), cpu_on)); + aml_append(method, + aml_store(aml_buffer(sizeof(madt_tmpl), madt_tmpl), madt)); + /* Update the processor id, lapic id, and enable/disable status */ + aml_append(method, aml_store(cpu_id, aml_index(madt, aml_int(2)))); + aml_append(method, aml_store(cpu_id, aml_index(madt, aml_int(3)))); + aml_append(method, aml_store(cpu_on, aml_index(madt, aml_int(4)))); + aml_append(method, aml_return(madt)); + aml_append(sb_scope, method); + + /* + * _STA method - return ON status of cpu + * cpu_id = Arg0 = Processor ID = Local APIC ID + * cpu_on = Local0 = CPON flag for this cpu + */ + method = aml_method(CPU_STATUS_METHOD, 1, AML_NOTSERIALIZED); + aml_append(method, + aml_store(aml_derefof(aml_index(cpus_map, cpu_id)), cpu_on)); + if_ctx = aml_if(cpu_on); + { + aml_append(if_ctx, aml_return(aml_int(0xF))); + } + aml_append(method, if_ctx); + else_ctx = aml_else(); + { + aml_append(else_ctx, aml_return(zero)); + } + aml_append(method, else_ctx); + aml_append(sb_scope, method); + + method = aml_method(CPU_EJECT_METHOD, 2, AML_NOTSERIALIZED); + aml_append(method, aml_sleep(200)); + aml_append(sb_scope, method); + + method = aml_method(CPU_SCAN_METHOD, 0, AML_NOTSERIALIZED); + { + Aml *while_ctx, *if_ctx2, *else_ctx2; + Aml *bus_check_evt = aml_int(1); + Aml *remove_evt = aml_int(3); + Aml *status_map = aml_local(5); /* Local5 = active cpu bitmap */ + Aml *byte = aml_local(2); /* Local2 = last read byte from bitmap */ + Aml *idx = aml_local(0); /* Processor ID / APIC ID iterator */ + Aml *is_cpu_on = aml_local(1); /* Local1 = CPON flag for cpu */ + Aml *status = aml_local(3); /* Local3 = active state for cpu */ + + aml_append(method, aml_store(aml_name(CPU_STATUS_MAP), status_map)); + aml_append(method, aml_store(zero, byte)); + aml_append(method, aml_store(zero, idx)); + + /* While (idx < SizeOf(CPON)) */ + while_ctx = aml_while(aml_lless(idx, aml_sizeof(cpus_map))); + aml_append(while_ctx, + aml_store(aml_derefof(aml_index(cpus_map, idx)), is_cpu_on)); + + if_ctx = aml_if(aml_and(idx, aml_int(0x07), NULL)); + { + /* Shift down previously read bitmap byte */ + aml_append(if_ctx, aml_shiftright(byte, one, byte)); + } + aml_append(while_ctx, if_ctx); + + else_ctx = aml_else(); + { + /* Read next byte from cpu bitmap */ + aml_append(else_ctx, aml_store(aml_derefof(aml_index(status_map, + aml_shiftright(idx, aml_int(3), NULL))), byte)); + } + aml_append(while_ctx, else_ctx); + + aml_append(while_ctx, aml_store(aml_and(byte, one, NULL), status)); + if_ctx = aml_if(aml_lnot(aml_equal(is_cpu_on, status))); + { + /* State change - update CPON with new state */ + aml_append(if_ctx, aml_store(status, aml_index(cpus_map, idx))); + if_ctx2 = aml_if(aml_equal(status, one)); + { + aml_append(if_ctx2, + aml_call2(AML_NOTIFY_METHOD, idx, bus_check_evt)); + } + aml_append(if_ctx, if_ctx2); + else_ctx2 = aml_else(); + { + aml_append(else_ctx2, + aml_call2(AML_NOTIFY_METHOD, idx, remove_evt)); + } + } + aml_append(if_ctx, else_ctx2); + aml_append(while_ctx, if_ctx); + + aml_append(while_ctx, aml_increment(idx)); /* go to next cpu */ + aml_append(method, while_ctx); + } + aml_append(sb_scope, method); + + aml_append(ctx, sb_scope); +} diff --git a/qemu/hw/acpi/ich9.c b/qemu/hw/acpi/ich9.c index 1c7fcfa9d..27e978f5f 100644 --- a/qemu/hw/acpi/ich9.c +++ b/qemu/hw/acpi/ich9.c @@ -23,7 +23,9 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/hw.h" +#include "qapi/error.h" #include "qapi/visitor.h" #include "hw/i386/pc.h" #include "hw/pci/pci.h" @@ -239,7 +241,7 @@ static void pm_powerdown_req(Notifier *n, void *opaque) } void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, - bool smm_enabled, bool enable_tco, + bool smm_enabled, qemu_irq sci_irq) { memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm", ICH9_PMIO_SIZE); @@ -263,10 +265,8 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, pm->smm_enabled = smm_enabled; - pm->enable_tco = enable_tco; - if (pm->enable_tco) { - acpi_pm_tco_init(&pm->tco_regs, &pm->io); - } + pm->enable_tco = true; + acpi_pm_tco_init(&pm->tco_regs, &pm->io); pm->irq = sci_irq; qemu_register_reset(pm_reset, pm); @@ -282,14 +282,13 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, } } -static void ich9_pm_get_gpe0_blk(Object *obj, Visitor *v, - void *opaque, const char *name, - Error **errp) +static void ich9_pm_get_gpe0_blk(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { ICH9LPCPMRegs *pm = opaque; uint32_t value = pm->pm_io_base + ICH9_PMIO_GPE0_STS; - visit_type_uint32(v, &value, name, errp); + visit_type_uint32(v, name, &value, errp); } static bool ich9_pm_get_memory_hotplug_support(Object *obj, Error **errp) @@ -307,25 +306,23 @@ static void ich9_pm_set_memory_hotplug_support(Object *obj, bool value, s->pm.acpi_memory_hotplug.is_enabled = value; } -static void ich9_pm_get_disable_s3(Object *obj, Visitor *v, - void *opaque, const char *name, - Error **errp) +static void ich9_pm_get_disable_s3(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { ICH9LPCPMRegs *pm = opaque; uint8_t value = pm->disable_s3; - visit_type_uint8(v, &value, name, errp); + visit_type_uint8(v, name, &value, errp); } -static void ich9_pm_set_disable_s3(Object *obj, Visitor *v, - void *opaque, const char *name, - Error **errp) +static void ich9_pm_set_disable_s3(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { ICH9LPCPMRegs *pm = opaque; Error *local_err = NULL; uint8_t value; - visit_type_uint8(v, &value, name, &local_err); + visit_type_uint8(v, name, &value, &local_err); if (local_err) { goto out; } @@ -334,25 +331,23 @@ out: error_propagate(errp, local_err); } -static void ich9_pm_get_disable_s4(Object *obj, Visitor *v, - void *opaque, const char *name, - Error **errp) +static void ich9_pm_get_disable_s4(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { ICH9LPCPMRegs *pm = opaque; uint8_t value = pm->disable_s4; - visit_type_uint8(v, &value, name, errp); + visit_type_uint8(v, name, &value, errp); } -static void ich9_pm_set_disable_s4(Object *obj, Visitor *v, - void *opaque, const char *name, - Error **errp) +static void ich9_pm_set_disable_s4(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { ICH9LPCPMRegs *pm = opaque; Error *local_err = NULL; uint8_t value; - visit_type_uint8(v, &value, name, &local_err); + visit_type_uint8(v, name, &value, &local_err); if (local_err) { goto out; } @@ -361,25 +356,23 @@ out: error_propagate(errp, local_err); } -static void ich9_pm_get_s4_val(Object *obj, Visitor *v, - void *opaque, const char *name, - Error **errp) +static void ich9_pm_get_s4_val(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { ICH9LPCPMRegs *pm = opaque; uint8_t value = pm->s4_val; - visit_type_uint8(v, &value, name, errp); + visit_type_uint8(v, name, &value, errp); } -static void ich9_pm_set_s4_val(Object *obj, Visitor *v, - void *opaque, const char *name, - Error **errp) +static void ich9_pm_set_s4_val(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { ICH9LPCPMRegs *pm = opaque; Error *local_err = NULL; uint8_t value; - visit_type_uint8(v, &value, name, &local_err); + visit_type_uint8(v, name, &value, &local_err); if (local_err) { goto out; } diff --git a/qemu/hw/acpi/memory_hotplug.c b/qemu/hw/acpi/memory_hotplug.c index 2ff0d5ce1..f65a3a21e 100644 --- a/qemu/hw/acpi/memory_hotplug.c +++ b/qemu/hw/acpi/memory_hotplug.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/acpi/memory_hotplug.h" #include "hw/acpi/pc-hotplug.h" #include "hw/mem/pc-dimm.h" @@ -155,6 +156,7 @@ static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data, qapi_event_send_mem_unplug_error(dev->id, error_get_pretty(local_err), &error_abort); + error_free(local_err); break; } trace_mhp_acpi_pc_dimm_deleted(mem_st->selector); @@ -230,6 +232,11 @@ void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st, DeviceState *dev, Error **errp) { MemStatus *mdev; + DeviceClass *dc = DEVICE_GET_CLASS(dev); + + if (!dc->hotpluggable) { + return; + } mdev = acpi_memory_slot_status(mem_st, dev, errp); if (!mdev) { @@ -238,11 +245,12 @@ void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st, mdev->dimm = dev; mdev->is_enabled = true; - mdev->is_inserting = true; + if (dev->hotplugged) { + mdev->is_inserting = true; - /* do ACPI magic */ - acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS); - return; + /* do ACPI magic */ + acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS); + } } void acpi_memory_unplug_request_cb(ACPIREGS *ar, qemu_irq irq, diff --git a/qemu/hw/acpi/memory_hotplug_acpi_table.c b/qemu/hw/acpi/memory_hotplug_acpi_table.c new file mode 100644 index 000000000..c75660215 --- /dev/null +++ b/qemu/hw/acpi/memory_hotplug_acpi_table.c @@ -0,0 +1,262 @@ +/* + * Memory hotplug AML code of DSDT ACPI table + * + * Copyright (C) 2015 Red Hat Inc + * + * Author: Igor Mammedov + * + * 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 "hw/acpi/memory_hotplug.h" +#include "include/hw/acpi/pc-hotplug.h" +#include "hw/boards.h" + +void build_memory_hotplug_aml(Aml *ctx, uint32_t nr_mem, + uint16_t io_base, uint16_t io_len) +{ + Aml *ifctx; + Aml *method; + Aml *pci_scope; + Aml *mem_ctrl_dev; + + /* scope for memory hotplug controller device node */ + pci_scope = aml_scope("_SB.PCI0"); + mem_ctrl_dev = aml_device(MEMORY_HOTPLUG_DEVICE); + { + Aml *one = aml_int(1); + Aml *zero = aml_int(0); + Aml *ret_val = aml_local(0); + Aml *slot_arg0 = aml_arg(0); + Aml *slots_nr = aml_name(MEMORY_SLOTS_NUMBER); + Aml *ctrl_lock = aml_name(MEMORY_SLOT_LOCK); + Aml *slot_selector = aml_name(MEMORY_SLOT_SLECTOR); + + aml_append(mem_ctrl_dev, aml_name_decl("_HID", aml_string("PNP0A06"))); + aml_append(mem_ctrl_dev, + aml_name_decl("_UID", aml_string("Memory hotplug resources"))); + + method = aml_method("_STA", 0, AML_NOTSERIALIZED); + ifctx = aml_if(aml_equal(slots_nr, zero)); + { + aml_append(ifctx, aml_return(zero)); + } + aml_append(method, ifctx); + /* present, functioning, decoding, not shown in UI */ + aml_append(method, aml_return(aml_int(0xB))); + aml_append(mem_ctrl_dev, method); + + aml_append(mem_ctrl_dev, aml_mutex(MEMORY_SLOT_LOCK, 0)); + + method = aml_method(MEMORY_SLOT_SCAN_METHOD, 0, AML_NOTSERIALIZED); + { + Aml *else_ctx; + Aml *while_ctx; + Aml *idx = aml_local(0); + Aml *eject_req = aml_int(3); + Aml *dev_chk = aml_int(1); + + ifctx = aml_if(aml_equal(slots_nr, zero)); + { + aml_append(ifctx, aml_return(zero)); + } + aml_append(method, ifctx); + + aml_append(method, aml_store(zero, idx)); + aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); + /* build AML that: + * loops over all slots and Notifies DIMMs with + * Device Check or Eject Request notifications if + * slot has corresponding status bit set and clears + * slot status. + */ + while_ctx = aml_while(aml_lless(idx, slots_nr)); + { + Aml *ins_evt = aml_name(MEMORY_SLOT_INSERT_EVENT); + Aml *rm_evt = aml_name(MEMORY_SLOT_REMOVE_EVENT); + + aml_append(while_ctx, aml_store(idx, slot_selector)); + ifctx = aml_if(aml_equal(ins_evt, one)); + { + aml_append(ifctx, + aml_call2(MEMORY_SLOT_NOTIFY_METHOD, + idx, dev_chk)); + aml_append(ifctx, aml_store(one, ins_evt)); + } + aml_append(while_ctx, ifctx); + + else_ctx = aml_else(); + ifctx = aml_if(aml_equal(rm_evt, one)); + { + aml_append(ifctx, + aml_call2(MEMORY_SLOT_NOTIFY_METHOD, + idx, eject_req)); + aml_append(ifctx, aml_store(one, rm_evt)); + } + aml_append(else_ctx, ifctx); + aml_append(while_ctx, else_ctx); + + aml_append(while_ctx, aml_add(idx, one, idx)); + } + aml_append(method, while_ctx); + aml_append(method, aml_release(ctrl_lock)); + aml_append(method, aml_return(one)); + } + aml_append(mem_ctrl_dev, method); + + method = aml_method(MEMORY_SLOT_STATUS_METHOD, 1, AML_NOTSERIALIZED); + { + Aml *slot_enabled = aml_name(MEMORY_SLOT_ENABLED); + + aml_append(method, aml_store(zero, ret_val)); + aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); + aml_append(method, + aml_store(aml_to_integer(slot_arg0), slot_selector)); + + ifctx = aml_if(aml_equal(slot_enabled, one)); + { + aml_append(ifctx, aml_store(aml_int(0xF), ret_val)); + } + aml_append(method, ifctx); + + aml_append(method, aml_release(ctrl_lock)); + aml_append(method, aml_return(ret_val)); + } + aml_append(mem_ctrl_dev, method); + + method = aml_method(MEMORY_SLOT_CRS_METHOD, 1, AML_SERIALIZED); + { + Aml *mr64 = aml_name("MR64"); + Aml *mr32 = aml_name("MR32"); + Aml *crs_tmpl = aml_resource_template(); + Aml *minl = aml_name("MINL"); + Aml *minh = aml_name("MINH"); + Aml *maxl = aml_name("MAXL"); + Aml *maxh = aml_name("MAXH"); + Aml *lenl = aml_name("LENL"); + Aml *lenh = aml_name("LENH"); + + aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); + aml_append(method, aml_store(aml_to_integer(slot_arg0), + slot_selector)); + + aml_append(crs_tmpl, + aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, + AML_CACHEABLE, AML_READ_WRITE, + 0, 0x0, 0xFFFFFFFFFFFFFFFEULL, 0, + 0xFFFFFFFFFFFFFFFFULL)); + aml_append(method, aml_name_decl("MR64", crs_tmpl)); + aml_append(method, + aml_create_dword_field(mr64, aml_int(14), "MINL")); + aml_append(method, + aml_create_dword_field(mr64, aml_int(18), "MINH")); + aml_append(method, + aml_create_dword_field(mr64, aml_int(38), "LENL")); + aml_append(method, + aml_create_dword_field(mr64, aml_int(42), "LENH")); + aml_append(method, + aml_create_dword_field(mr64, aml_int(22), "MAXL")); + aml_append(method, + aml_create_dword_field(mr64, aml_int(26), "MAXH")); + + aml_append(method, + aml_store(aml_name(MEMORY_SLOT_ADDR_HIGH), minh)); + aml_append(method, + aml_store(aml_name(MEMORY_SLOT_ADDR_LOW), minl)); + aml_append(method, + aml_store(aml_name(MEMORY_SLOT_SIZE_HIGH), lenh)); + aml_append(method, + aml_store(aml_name(MEMORY_SLOT_SIZE_LOW), lenl)); + + /* 64-bit math: MAX = MIN + LEN - 1 */ + aml_append(method, aml_add(minl, lenl, maxl)); + aml_append(method, aml_add(minh, lenh, maxh)); + ifctx = aml_if(aml_lless(maxl, minl)); + { + aml_append(ifctx, aml_add(maxh, one, maxh)); + } + aml_append(method, ifctx); + ifctx = aml_if(aml_lless(maxl, one)); + { + aml_append(ifctx, aml_subtract(maxh, one, maxh)); + } + aml_append(method, ifctx); + aml_append(method, aml_subtract(maxl, one, maxl)); + + /* return 32-bit _CRS if addr/size is in low mem */ + /* TODO: remove it since all hotplugged DIMMs are in high mem */ + ifctx = aml_if(aml_equal(maxh, zero)); + { + crs_tmpl = aml_resource_template(); + aml_append(crs_tmpl, + aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, + AML_MAX_FIXED, AML_CACHEABLE, + AML_READ_WRITE, + 0, 0x0, 0xFFFFFFFE, 0, + 0xFFFFFFFF)); + aml_append(ifctx, aml_name_decl("MR32", crs_tmpl)); + aml_append(ifctx, + aml_create_dword_field(mr32, aml_int(10), "MIN")); + aml_append(ifctx, + aml_create_dword_field(mr32, aml_int(14), "MAX")); + aml_append(ifctx, + aml_create_dword_field(mr32, aml_int(22), "LEN")); + aml_append(ifctx, aml_store(minl, aml_name("MIN"))); + aml_append(ifctx, aml_store(maxl, aml_name("MAX"))); + aml_append(ifctx, aml_store(lenl, aml_name("LEN"))); + + aml_append(ifctx, aml_release(ctrl_lock)); + aml_append(ifctx, aml_return(mr32)); + } + aml_append(method, ifctx); + + aml_append(method, aml_release(ctrl_lock)); + aml_append(method, aml_return(mr64)); + } + aml_append(mem_ctrl_dev, method); + + method = aml_method(MEMORY_SLOT_PROXIMITY_METHOD, 1, + AML_NOTSERIALIZED); + { + Aml *proximity = aml_name(MEMORY_SLOT_PROXIMITY); + + aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); + aml_append(method, aml_store(aml_to_integer(slot_arg0), + slot_selector)); + aml_append(method, aml_store(proximity, ret_val)); + aml_append(method, aml_release(ctrl_lock)); + aml_append(method, aml_return(ret_val)); + } + aml_append(mem_ctrl_dev, method); + + method = aml_method(MEMORY_SLOT_OST_METHOD, 4, AML_NOTSERIALIZED); + { + Aml *ost_evt = aml_name(MEMORY_SLOT_OST_EVENT); + Aml *ost_status = aml_name(MEMORY_SLOT_OST_STATUS); + + aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); + aml_append(method, aml_store(aml_to_integer(slot_arg0), + slot_selector)); + aml_append(method, aml_store(aml_arg(1), ost_evt)); + aml_append(method, aml_store(aml_arg(2), ost_status)); + aml_append(method, aml_release(ctrl_lock)); + } + aml_append(mem_ctrl_dev, method); + + method = aml_method(MEMORY_SLOT_EJECT_METHOD, 2, AML_NOTSERIALIZED); + { + Aml *eject = aml_name(MEMORY_SLOT_EJECT); + + aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); + aml_append(method, aml_store(aml_to_integer(slot_arg0), + slot_selector)); + aml_append(method, aml_store(one, eject)); + aml_append(method, aml_release(ctrl_lock)); + } + aml_append(mem_ctrl_dev, method); + } + aml_append(pci_scope, mem_ctrl_dev); + aml_append(ctx, pci_scope); +} diff --git a/qemu/hw/acpi/nvdimm.c b/qemu/hw/acpi/nvdimm.c new file mode 100644 index 000000000..9531340e5 --- /dev/null +++ b/qemu/hw/acpi/nvdimm.c @@ -0,0 +1,706 @@ +/* + * NVDIMM ACPI Implementation + * + * Copyright(C) 2015 Intel Corporation. + * + * Author: + * Xiao Guangrong + * + * NFIT is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT) + * and the DSM specification can be found at: + * http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf + * + * Currently, it only supports PMEM Virtualization. + * + * 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 + */ + +#include "qemu/osdep.h" +#include "hw/acpi/acpi.h" +#include "hw/acpi/aml-build.h" +#include "hw/acpi/bios-linker-loader.h" +#include "hw/nvram/fw_cfg.h" +#include "hw/mem/nvdimm.h" + +static int nvdimm_plugged_device_list(Object *obj, void *opaque) +{ + GSList **list = opaque; + + if (object_dynamic_cast(obj, TYPE_NVDIMM)) { + DeviceState *dev = DEVICE(obj); + + if (dev->realized) { /* only realized NVDIMMs matter */ + *list = g_slist_append(*list, DEVICE(obj)); + } + } + + object_child_foreach(obj, nvdimm_plugged_device_list, opaque); + return 0; +} + +/* + * inquire plugged NVDIMM devices and link them into the list which is + * returned to the caller. + * + * Note: it is the caller's responsibility to free the list to avoid + * memory leak. + */ +static GSList *nvdimm_get_plugged_device_list(void) +{ + GSList *list = NULL; + + object_child_foreach(qdev_get_machine(), nvdimm_plugged_device_list, + &list); + return list; +} + +#define NVDIMM_UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ + { (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ + (b) & 0xff, ((b) >> 8) & 0xff, (c) & 0xff, ((c) >> 8) & 0xff, \ + (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) } + +/* + * define Byte Addressable Persistent Memory (PM) Region according to + * ACPI 6.0: 5.2.25.1 System Physical Address Range Structure. + */ +static const uint8_t nvdimm_nfit_spa_uuid[] = + NVDIMM_UUID_LE(0x66f0d379, 0xb4f3, 0x4074, 0xac, 0x43, 0x0d, 0x33, + 0x18, 0xb7, 0x8c, 0xdb); + +/* + * NVDIMM Firmware Interface Table + * @signature: "NFIT" + * + * It provides information that allows OSPM to enumerate NVDIMM present in + * the platform and associate system physical address ranges created by the + * NVDIMMs. + * + * It is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT) + */ +struct NvdimmNfitHeader { + ACPI_TABLE_HEADER_DEF + uint32_t reserved; +} QEMU_PACKED; +typedef struct NvdimmNfitHeader NvdimmNfitHeader; + +/* + * define NFIT structures according to ACPI 6.0: 5.2.25 NVDIMM Firmware + * Interface Table (NFIT). + */ + +/* + * System Physical Address Range Structure + * + * It describes the system physical address ranges occupied by NVDIMMs and + * the types of the regions. + */ +struct NvdimmNfitSpa { + uint16_t type; + uint16_t length; + uint16_t spa_index; + uint16_t flags; + uint32_t reserved; + uint32_t proximity_domain; + uint8_t type_guid[16]; + uint64_t spa_base; + uint64_t spa_length; + uint64_t mem_attr; +} QEMU_PACKED; +typedef struct NvdimmNfitSpa NvdimmNfitSpa; + +/* + * Memory Device to System Physical Address Range Mapping Structure + * + * It enables identifying each NVDIMM region and the corresponding SPA + * describing the memory interleave + */ +struct NvdimmNfitMemDev { + uint16_t type; + uint16_t length; + uint32_t nfit_handle; + uint16_t phys_id; + uint16_t region_id; + uint16_t spa_index; + uint16_t dcr_index; + uint64_t region_len; + uint64_t region_offset; + uint64_t region_dpa; + uint16_t interleave_index; + uint16_t interleave_ways; + uint16_t flags; + uint16_t reserved; +} QEMU_PACKED; +typedef struct NvdimmNfitMemDev NvdimmNfitMemDev; + +/* + * NVDIMM Control Region Structure + * + * It describes the NVDIMM and if applicable, Block Control Window. + */ +struct NvdimmNfitControlRegion { + uint16_t type; + uint16_t length; + uint16_t dcr_index; + uint16_t vendor_id; + uint16_t device_id; + uint16_t revision_id; + uint16_t sub_vendor_id; + uint16_t sub_device_id; + uint16_t sub_revision_id; + uint8_t reserved[6]; + uint32_t serial_number; + uint16_t fic; + uint16_t num_bcw; + uint64_t bcw_size; + uint64_t cmd_offset; + uint64_t cmd_size; + uint64_t status_offset; + uint64_t status_size; + uint16_t flags; + uint8_t reserved2[6]; +} QEMU_PACKED; +typedef struct NvdimmNfitControlRegion NvdimmNfitControlRegion; + +/* + * Module serial number is a unique number for each device. We use the + * slot id of NVDIMM device to generate this number so that each device + * associates with a different number. + * + * 0x123456 is a magic number we arbitrarily chose. + */ +static uint32_t nvdimm_slot_to_sn(int slot) +{ + return 0x123456 + slot; +} + +/* + * handle is used to uniquely associate nfit_memdev structure with NVDIMM + * ACPI device - nfit_memdev.nfit_handle matches with the value returned + * by ACPI device _ADR method. + * + * We generate the handle with the slot id of NVDIMM device and reserve + * 0 for NVDIMM root device. + */ +static uint32_t nvdimm_slot_to_handle(int slot) +{ + return slot + 1; +} + +/* + * index uniquely identifies the structure, 0 is reserved which indicates + * that the structure is not valid or the associated structure is not + * present. + * + * Each NVDIMM device needs two indexes, one for nfit_spa and another for + * nfit_dc which are generated by the slot id of NVDIMM device. + */ +static uint16_t nvdimm_slot_to_spa_index(int slot) +{ + return (slot + 1) << 1; +} + +/* See the comments of nvdimm_slot_to_spa_index(). */ +static uint32_t nvdimm_slot_to_dcr_index(int slot) +{ + return nvdimm_slot_to_spa_index(slot) + 1; +} + +/* ACPI 6.0: 5.2.25.1 System Physical Address Range Structure */ +static void +nvdimm_build_structure_spa(GArray *structures, DeviceState *dev) +{ + NvdimmNfitSpa *nfit_spa; + uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP, + NULL); + uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP, + NULL); + uint32_t node = object_property_get_int(OBJECT(dev), PC_DIMM_NODE_PROP, + NULL); + int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, + NULL); + + nfit_spa = acpi_data_push(structures, sizeof(*nfit_spa)); + + nfit_spa->type = cpu_to_le16(0 /* System Physical Address Range + Structure */); + nfit_spa->length = cpu_to_le16(sizeof(*nfit_spa)); + nfit_spa->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot)); + + /* + * Control region is strict as all the device info, such as SN, index, + * is associated with slot id. + */ + nfit_spa->flags = cpu_to_le16(1 /* Control region is strictly for + management during hot add/online + operation */ | + 2 /* Data in Proximity Domain field is + valid*/); + + /* NUMA node. */ + nfit_spa->proximity_domain = cpu_to_le32(node); + /* the region reported as PMEM. */ + memcpy(nfit_spa->type_guid, nvdimm_nfit_spa_uuid, + sizeof(nvdimm_nfit_spa_uuid)); + + nfit_spa->spa_base = cpu_to_le64(addr); + nfit_spa->spa_length = cpu_to_le64(size); + + /* It is the PMEM and can be cached as writeback. */ + nfit_spa->mem_attr = cpu_to_le64(0x8ULL /* EFI_MEMORY_WB */ | + 0x8000ULL /* EFI_MEMORY_NV */); +} + +/* + * ACPI 6.0: 5.2.25.2 Memory Device to System Physical Address Range Mapping + * Structure + */ +static void +nvdimm_build_structure_memdev(GArray *structures, DeviceState *dev) +{ + NvdimmNfitMemDev *nfit_memdev; + uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP, + NULL); + uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP, + NULL); + int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, + NULL); + uint32_t handle = nvdimm_slot_to_handle(slot); + + nfit_memdev = acpi_data_push(structures, sizeof(*nfit_memdev)); + + nfit_memdev->type = cpu_to_le16(1 /* Memory Device to System Address + Range Map Structure*/); + nfit_memdev->length = cpu_to_le16(sizeof(*nfit_memdev)); + nfit_memdev->nfit_handle = cpu_to_le32(handle); + + /* + * associate memory device with System Physical Address Range + * Structure. + */ + nfit_memdev->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot)); + /* associate memory device with Control Region Structure. */ + nfit_memdev->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot)); + + /* The memory region on the device. */ + nfit_memdev->region_len = cpu_to_le64(size); + nfit_memdev->region_dpa = cpu_to_le64(addr); + + /* Only one interleave for PMEM. */ + nfit_memdev->interleave_ways = cpu_to_le16(1); +} + +/* + * ACPI 6.0: 5.2.25.5 NVDIMM Control Region Structure. + */ +static void nvdimm_build_structure_dcr(GArray *structures, DeviceState *dev) +{ + NvdimmNfitControlRegion *nfit_dcr; + int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, + NULL); + uint32_t sn = nvdimm_slot_to_sn(slot); + + nfit_dcr = acpi_data_push(structures, sizeof(*nfit_dcr)); + + nfit_dcr->type = cpu_to_le16(4 /* NVDIMM Control Region Structure */); + nfit_dcr->length = cpu_to_le16(sizeof(*nfit_dcr)); + nfit_dcr->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot)); + + /* vendor: Intel. */ + nfit_dcr->vendor_id = cpu_to_le16(0x8086); + nfit_dcr->device_id = cpu_to_le16(1); + + /* The _DSM method is following Intel's DSM specification. */ + nfit_dcr->revision_id = cpu_to_le16(1 /* Current Revision supported + in ACPI 6.0 is 1. */); + nfit_dcr->serial_number = cpu_to_le32(sn); + nfit_dcr->fic = cpu_to_le16(0x201 /* Format Interface Code. See Chapter + 2: NVDIMM Device Specific Method + (DSM) in DSM Spec Rev1.*/); +} + +static GArray *nvdimm_build_device_structure(GSList *device_list) +{ + GArray *structures = g_array_new(false, true /* clear */, 1); + + for (; device_list; device_list = device_list->next) { + DeviceState *dev = device_list->data; + + /* build System Physical Address Range Structure. */ + nvdimm_build_structure_spa(structures, dev); + + /* + * build Memory Device to System Physical Address Range Mapping + * Structure. + */ + nvdimm_build_structure_memdev(structures, dev); + + /* build NVDIMM Control Region Structure. */ + nvdimm_build_structure_dcr(structures, dev); + } + + return structures; +} + +static void nvdimm_build_nfit(GSList *device_list, GArray *table_offsets, + GArray *table_data, GArray *linker) +{ + GArray *structures = nvdimm_build_device_structure(device_list); + unsigned int header; + + acpi_add_table(table_offsets, table_data); + + /* NFIT header. */ + header = table_data->len; + acpi_data_push(table_data, sizeof(NvdimmNfitHeader)); + /* NVDIMM device structures. */ + g_array_append_vals(table_data, structures->data, structures->len); + + build_header(linker, table_data, + (void *)(table_data->data + header), "NFIT", + sizeof(NvdimmNfitHeader) + structures->len, 1, NULL, NULL); + g_array_free(structures, true); +} + +struct NvdimmDsmIn { + uint32_t handle; + uint32_t revision; + uint32_t function; + /* the remaining size in the page is used by arg3. */ + union { + uint8_t arg3[0]; + }; +} QEMU_PACKED; +typedef struct NvdimmDsmIn NvdimmDsmIn; + +struct NvdimmDsmOut { + /* the size of buffer filled by QEMU. */ + uint32_t len; + uint8_t data[0]; +} QEMU_PACKED; +typedef struct NvdimmDsmOut NvdimmDsmOut; + +struct NvdimmDsmFunc0Out { + /* the size of buffer filled by QEMU. */ + uint32_t len; + uint32_t supported_func; +} QEMU_PACKED; +typedef struct NvdimmDsmFunc0Out NvdimmDsmFunc0Out; + +struct NvdimmDsmFuncNoPayloadOut { + /* the size of buffer filled by QEMU. */ + uint32_t len; + uint32_t func_ret_status; +} QEMU_PACKED; +typedef struct NvdimmDsmFuncNoPayloadOut NvdimmDsmFuncNoPayloadOut; + +static uint64_t +nvdimm_dsm_read(void *opaque, hwaddr addr, unsigned size) +{ + nvdimm_debug("BUG: we never read _DSM IO Port.\n"); + return 0; +} + +static void +nvdimm_dsm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + NvdimmDsmIn *in; + hwaddr dsm_mem_addr = val; + + nvdimm_debug("dsm memory address %#" HWADDR_PRIx ".\n", dsm_mem_addr); + + /* + * The DSM memory is mapped to guest address space so an evil guest + * can change its content while we are doing DSM emulation. Avoid + * this by copying DSM memory to QEMU local memory. + */ + in = g_malloc(TARGET_PAGE_SIZE); + cpu_physical_memory_read(dsm_mem_addr, in, TARGET_PAGE_SIZE); + + le32_to_cpus(&in->revision); + le32_to_cpus(&in->function); + le32_to_cpus(&in->handle); + + nvdimm_debug("Revision %#x Handler %#x Function %#x.\n", in->revision, + in->handle, in->function); + + /* + * function 0 is called to inquire which functions are supported by + * OSPM + */ + if (in->function == 0) { + NvdimmDsmFunc0Out func0 = { + .len = cpu_to_le32(sizeof(func0)), + /* No function supported other than function 0 */ + .supported_func = cpu_to_le32(0), + }; + cpu_physical_memory_write(dsm_mem_addr, &func0, sizeof func0); + } else { + /* No function except function 0 is supported yet. */ + NvdimmDsmFuncNoPayloadOut out = { + .len = cpu_to_le32(sizeof(out)), + .func_ret_status = cpu_to_le32(1) /* Not Supported */, + }; + cpu_physical_memory_write(dsm_mem_addr, &out, sizeof(out)); + } + + g_free(in); +} + +static const MemoryRegionOps nvdimm_dsm_ops = { + .read = nvdimm_dsm_read, + .write = nvdimm_dsm_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +void nvdimm_init_acpi_state(AcpiNVDIMMState *state, MemoryRegion *io, + FWCfgState *fw_cfg, Object *owner) +{ + memory_region_init_io(&state->io_mr, owner, &nvdimm_dsm_ops, state, + "nvdimm-acpi-io", NVDIMM_ACPI_IO_LEN); + memory_region_add_subregion(io, NVDIMM_ACPI_IO_BASE, &state->io_mr); + + state->dsm_mem = g_array_new(false, true /* clear */, 1); + acpi_data_push(state->dsm_mem, TARGET_PAGE_SIZE); + fw_cfg_add_file(fw_cfg, NVDIMM_DSM_MEM_FILE, state->dsm_mem->data, + state->dsm_mem->len); +} + +#define NVDIMM_COMMON_DSM "NCAL" +#define NVDIMM_ACPI_MEM_ADDR "MEMA" + +static void nvdimm_build_common_dsm(Aml *dev) +{ + Aml *method, *ifctx, *function, *dsm_mem, *unpatched, *result_size; + uint8_t byte_list[1]; + + method = aml_method(NVDIMM_COMMON_DSM, 4, AML_SERIALIZED); + function = aml_arg(2); + dsm_mem = aml_name(NVDIMM_ACPI_MEM_ADDR); + + /* + * do not support any method if DSM memory address has not been + * patched. + */ + unpatched = aml_if(aml_equal(dsm_mem, aml_int(0x0))); + + /* + * function 0 is called to inquire what functions are supported by + * OSPM + */ + ifctx = aml_if(aml_equal(function, aml_int(0))); + byte_list[0] = 0 /* No function Supported */; + aml_append(ifctx, aml_return(aml_buffer(1, byte_list))); + aml_append(unpatched, ifctx); + + /* No function is supported yet. */ + byte_list[0] = 1 /* Not Supported */; + aml_append(unpatched, aml_return(aml_buffer(1, byte_list))); + aml_append(method, unpatched); + + /* + * The HDLE indicates the DSM function is issued from which device, + * it is not used at this time as no function is supported yet. + * Currently we make it always be 0 for all the devices and will set + * the appropriate value once real function is implemented. + */ + aml_append(method, aml_store(aml_int(0x0), aml_name("HDLE"))); + aml_append(method, aml_store(aml_arg(1), aml_name("REVS"))); + aml_append(method, aml_store(aml_arg(2), aml_name("FUNC"))); + + /* + * tell QEMU about the real address of DSM memory, then QEMU + * gets the control and fills the result in DSM memory. + */ + aml_append(method, aml_store(dsm_mem, aml_name("NTFI"))); + + result_size = aml_local(1); + aml_append(method, aml_store(aml_name("RLEN"), result_size)); + aml_append(method, aml_store(aml_shiftleft(result_size, aml_int(3)), + result_size)); + aml_append(method, aml_create_field(aml_name("ODAT"), aml_int(0), + result_size, "OBUF")); + aml_append(method, aml_concatenate(aml_buffer(0, NULL), aml_name("OBUF"), + aml_arg(6))); + aml_append(method, aml_return(aml_arg(6))); + aml_append(dev, method); +} + +static void nvdimm_build_device_dsm(Aml *dev) +{ + Aml *method; + + method = aml_method("_DSM", 4, AML_NOTSERIALIZED); + aml_append(method, aml_return(aml_call4(NVDIMM_COMMON_DSM, aml_arg(0), + aml_arg(1), aml_arg(2), aml_arg(3)))); + aml_append(dev, method); +} + +static void nvdimm_build_nvdimm_devices(GSList *device_list, Aml *root_dev) +{ + for (; device_list; device_list = device_list->next) { + DeviceState *dev = device_list->data; + int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, + NULL); + uint32_t handle = nvdimm_slot_to_handle(slot); + Aml *nvdimm_dev; + + nvdimm_dev = aml_device("NV%02X", slot); + + /* + * ACPI 6.0: 9.20 NVDIMM Devices: + * + * _ADR object that is used to supply OSPM with unique address + * of the NVDIMM device. This is done by returning the NFIT Device + * handle that is used to identify the associated entries in ACPI + * table NFIT or _FIT. + */ + aml_append(nvdimm_dev, aml_name_decl("_ADR", aml_int(handle))); + + nvdimm_build_device_dsm(nvdimm_dev); + aml_append(root_dev, nvdimm_dev); + } +} + +static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets, + GArray *table_data, GArray *linker) +{ + Aml *ssdt, *sb_scope, *dev, *field; + int mem_addr_offset, nvdimm_ssdt; + + acpi_add_table(table_offsets, table_data); + + ssdt = init_aml_allocator(); + acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader)); + + sb_scope = aml_scope("\\_SB"); + + dev = aml_device("NVDR"); + + /* + * ACPI 6.0: 9.20 NVDIMM Devices: + * + * The ACPI Name Space device uses _HID of ACPI0012 to identify the root + * NVDIMM interface device. Platform firmware is required to contain one + * such device in _SB scope if NVDIMMs support is exposed by platform to + * OSPM. + * For each NVDIMM present or intended to be supported by platform, + * platform firmware also exposes an ACPI Namespace Device under the + * root device. + */ + aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0012"))); + + /* map DSM memory and IO into ACPI namespace. */ + aml_append(dev, aml_operation_region("NPIO", AML_SYSTEM_IO, + aml_int(NVDIMM_ACPI_IO_BASE), NVDIMM_ACPI_IO_LEN)); + aml_append(dev, aml_operation_region("NRAM", AML_SYSTEM_MEMORY, + aml_name(NVDIMM_ACPI_MEM_ADDR), TARGET_PAGE_SIZE)); + + /* + * DSM notifier: + * NTFI: write the address of DSM memory and notify QEMU to emulate + * the access. + * + * It is the IO port so that accessing them will cause VM-exit, the + * control will be transferred to QEMU. + */ + field = aml_field("NPIO", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE); + aml_append(field, aml_named_field("NTFI", + sizeof(uint32_t) * BITS_PER_BYTE)); + aml_append(dev, field); + + /* + * DSM input: + * HDLE: store device's handle, it's zero if the _DSM call happens + * on NVDIMM Root Device. + * REVS: store the Arg1 of _DSM call. + * FUNC: store the Arg2 of _DSM call. + * ARG3: store the Arg3 of _DSM call. + * + * They are RAM mapping on host so that these accesses never cause + * VM-EXIT. + */ + field = aml_field("NRAM", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE); + aml_append(field, aml_named_field("HDLE", + sizeof(typeof_field(NvdimmDsmIn, handle)) * BITS_PER_BYTE)); + aml_append(field, aml_named_field("REVS", + sizeof(typeof_field(NvdimmDsmIn, revision)) * BITS_PER_BYTE)); + aml_append(field, aml_named_field("FUNC", + sizeof(typeof_field(NvdimmDsmIn, function)) * BITS_PER_BYTE)); + aml_append(field, aml_named_field("ARG3", + (TARGET_PAGE_SIZE - offsetof(NvdimmDsmIn, arg3)) * + BITS_PER_BYTE)); + aml_append(dev, field); + + /* + * DSM output: + * RLEN: the size of the buffer filled by QEMU. + * ODAT: the buffer QEMU uses to store the result. + * + * Since the page is reused by both input and out, the input data + * will be lost after storing new result into ODAT so we should fetch + * all the input data before writing the result. + */ + field = aml_field("NRAM", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE); + aml_append(field, aml_named_field("RLEN", + sizeof(typeof_field(NvdimmDsmOut, len)) * BITS_PER_BYTE)); + aml_append(field, aml_named_field("ODAT", + (TARGET_PAGE_SIZE - offsetof(NvdimmDsmOut, data)) * + BITS_PER_BYTE)); + aml_append(dev, field); + + nvdimm_build_common_dsm(dev); + nvdimm_build_device_dsm(dev); + + nvdimm_build_nvdimm_devices(device_list, dev); + + aml_append(sb_scope, dev); + aml_append(ssdt, sb_scope); + + nvdimm_ssdt = table_data->len; + + /* copy AML table into ACPI tables blob and patch header there */ + g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len); + mem_addr_offset = build_append_named_dword(table_data, + NVDIMM_ACPI_MEM_ADDR); + + bios_linker_loader_alloc(linker, NVDIMM_DSM_MEM_FILE, TARGET_PAGE_SIZE, + false /* high memory */); + bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, + NVDIMM_DSM_MEM_FILE, table_data, + table_data->data + mem_addr_offset, + sizeof(uint32_t)); + build_header(linker, table_data, + (void *)(table_data->data + nvdimm_ssdt), + "SSDT", table_data->len - nvdimm_ssdt, 1, NULL, "NVDIMM"); + free_aml_allocator(); +} + +void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data, + GArray *linker) +{ + GSList *device_list; + + /* no NVDIMM device is plugged. */ + device_list = nvdimm_get_plugged_device_list(); + if (!device_list) { + return; + } + nvdimm_build_nfit(device_list, table_offsets, table_data, linker); + nvdimm_build_ssdt(device_list, table_offsets, table_data, linker); + g_slist_free(device_list); +} diff --git a/qemu/hw/acpi/pcihp.c b/qemu/hw/acpi/pcihp.c index fbbc4dde4..71f4c4e14 100644 --- a/qemu/hw/acpi/pcihp.c +++ b/qemu/hw/acpi/pcihp.c @@ -24,6 +24,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/acpi/pcihp.h" #include "hw/hw.h" @@ -34,6 +35,7 @@ #include "exec/ioport.h" #include "exec/address-spaces.h" #include "hw/pci/pci_bus.h" +#include "qapi/error.h" #include "qom/qom-qobject.h" #include "qapi/qmp/qint.h" diff --git a/qemu/hw/acpi/piix4.c b/qemu/hw/acpi/piix4.c index 2cd2fee89..16abdf162 100644 --- a/qemu/hw/acpi/piix4.c +++ b/qemu/hw/acpi/piix4.c @@ -18,6 +18,7 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/isa/apm.h" @@ -25,6 +26,7 @@ #include "hw/pci/pci.h" #include "hw/acpi/acpi.h" #include "sysemu/sysemu.h" +#include "qapi/error.h" #include "qemu/range.h" #include "exec/ioport.h" #include "hw/nvram/fw_cfg.h" diff --git a/qemu/hw/acpi/tco.c b/qemu/hw/acpi/tco.c index 7a026c255..8ce7daf23 100644 --- a/qemu/hw/acpi/tco.c +++ b/qemu/hw/acpi/tco.c @@ -6,6 +6,7 @@ * 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/watchdog.h" #include "hw/i386/ich9.h" diff --git a/qemu/hw/alpha/dp264.c b/qemu/hw/alpha/dp264.c index f86e7bb83..f1267b544 100644 --- a/qemu/hw/alpha/dp264.c +++ b/qemu/hw/alpha/dp264.c @@ -6,16 +6,21 @@ * that we need to emulate as well. */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "elf.h" #include "hw/loader.h" #include "hw/boards.h" #include "alpha_sys.h" +#include "qemu/error-report.h" #include "sysemu/sysemu.h" #include "hw/timer/mc146818rtc.h" #include "hw/ide.h" #include "hw/timer/i8254.h" #include "hw/char/serial.h" +#include "qemu/cutils.h" #define MAX_IDE_BUS 2 @@ -104,14 +109,14 @@ static void clipper_init(MachineState *machine) palcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name ? bios_name : "palcode-clipper"); if (palcode_filename == NULL) { - hw_error("no palcode provided\n"); + error_report("no palcode provided"); exit(1); } size = load_elf(palcode_filename, cpu_alpha_superpage_to_phys, NULL, &palcode_entry, &palcode_low, &palcode_high, - 0, EM_ALPHA, 0); + 0, EM_ALPHA, 0, 0); if (size < 0) { - hw_error("could not load palcode '%s'\n", palcode_filename); + error_report("could not load palcode '%s'", palcode_filename); exit(1); } g_free(palcode_filename); @@ -129,9 +134,9 @@ static void clipper_init(MachineState *machine) size = load_elf(kernel_filename, cpu_alpha_superpage_to_phys, NULL, &kernel_entry, &kernel_low, &kernel_high, - 0, EM_ALPHA, 0); + 0, EM_ALPHA, 0, 0); if (size < 0) { - hw_error("could not load kernel '%s'\n", kernel_filename); + error_report("could not load kernel '%s'", kernel_filename); exit(1); } @@ -148,8 +153,8 @@ static void clipper_init(MachineState *machine) initrd_size = get_image_size(initrd_filename); if (initrd_size < 0) { - hw_error("could not load initial ram disk '%s'\n", - initrd_filename); + error_report("could not load initial ram disk '%s'", + initrd_filename); exit(1); } @@ -168,17 +173,12 @@ static void clipper_init(MachineState *machine) } } -static QEMUMachine clipper_machine = { - .name = "clipper", - .desc = "Alpha DP264/CLIPPER", - .init = clipper_init, - .max_cpus = 4, - .is_default = 1, -}; - -static void clipper_machine_init(void) +static void clipper_machine_init(MachineClass *mc) { - qemu_register_machine(&clipper_machine); + mc->desc = "Alpha DP264/CLIPPER"; + mc->init = clipper_init; + mc->max_cpus = 4; + mc->is_default = 1; } -machine_init(clipper_machine_init); +DEFINE_MACHINE("clipper", clipper_machine_init) diff --git a/qemu/hw/alpha/pci.c b/qemu/hw/alpha/pci.c index d839dd556..5baa0eaf1 100644 --- a/qemu/hw/alpha/pci.c +++ b/qemu/hw/alpha/pci.c @@ -6,10 +6,13 @@ * ??? Sparse memory access not implemented. */ -#include "config.h" +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "alpha_sys.h" #include "qemu/log.h" #include "sysemu/sysemu.h" +#include "trace.h" /* Fallback for unassigned PCI I/O operations. Avoids MCHK. */ @@ -73,7 +76,7 @@ static uint64_t iack_read(void *opaque, hwaddr addr, unsigned size) static void special_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - qemu_log("pci: special write cycle"); + trace_alpha_pci_iack_write(); } const MemoryRegionOps alpha_pci_iack_ops = { diff --git a/qemu/hw/alpha/typhoon.c b/qemu/hw/alpha/typhoon.c index 421162e1d..97721b535 100644 --- a/qemu/hw/alpha/typhoon.c +++ b/qemu/hw/alpha/typhoon.c @@ -6,6 +6,8 @@ * This work is licensed under the GNU GPL license version 2 or later. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "hw/hw.h" #include "hw/devices.h" @@ -920,7 +922,8 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, { qemu_irq *isa_irqs; - *isa_bus = isa_bus_new(NULL, get_system_memory(), &s->pchip.reg_io); + *isa_bus = isa_bus_new(NULL, get_system_memory(), &s->pchip.reg_io, + &error_abort); isa_irqs = i8259_init(*isa_bus, qemu_allocate_irq(typhoon_set_isa_irq, s, 0)); isa_bus_irqs(*isa_bus, isa_irqs); diff --git a/qemu/hw/arm/Makefile.objs b/qemu/hw/arm/Makefile.objs index cf346c1d0..954c9fe15 100644 --- a/qemu/hw/arm/Makefile.objs +++ b/qemu/hw/arm/Makefile.objs @@ -1,6 +1,6 @@ obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o obj-$(CONFIG_DIGIC) += digic_boards.o -obj-y += integratorcp.o kzm.o mainstone.o musicpal.o nseries.o +obj-y += integratorcp.o mainstone.o musicpal.o nseries.o obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o obj-$(CONFIG_ACPI) += virt-acpi-build.o @@ -11,5 +11,9 @@ obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o obj-$(CONFIG_DIGIC) += digic.o obj-y += omap1.o omap2.o strongarm.o obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o +obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o +obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o +obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o +obj-$(CONFIG_ASPEED_SOC) += ast2400.o palmetto-bmc.o diff --git a/qemu/hw/arm/allwinner-a10.c b/qemu/hw/arm/allwinner-a10.c index ff249af33..ca15d1c8c 100644 --- a/qemu/hw/arm/allwinner-a10.c +++ b/qemu/hw/arm/allwinner-a10.c @@ -15,6 +15,10 @@ * for more details. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "hw/devices.h" #include "hw/arm/allwinner-a10.h" @@ -39,6 +43,9 @@ static void aw_a10_init(Object *obj) qemu_check_nic_model(&nd_table[0], TYPE_AW_EMAC); qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]); } + + object_initialize(&s->sata, sizeof(s->sata), TYPE_ALLWINNER_AHCI); + qdev_set_parent_bus(DEVICE(&s->sata), sysbus_get_default()); } static void aw_a10_realize(DeviceState *dev, Error **errp) @@ -93,6 +100,14 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(sysbusdev, 0, AW_A10_EMAC_BASE); sysbus_connect_irq(sysbusdev, 0, s->irq[55]); + object_property_set_bool(OBJECT(&s->sata), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sata), 0, AW_A10_SATA_BASE); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->sata), 0, s->irq[56]); + /* FIXME use a qdev chardev prop instead of serial_hds[] */ serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1], 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN); @@ -103,6 +118,12 @@ static void aw_a10_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = aw_a10_realize; + + /* + * Reason: creates an ARM CPU, thus use after free(), see + * arm_cpu_class_init() + */ + dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo aw_a10_type_info = { diff --git a/qemu/hw/arm/armv7m.c b/qemu/hw/arm/armv7m.c index c6eab6de3..bb2a22d96 100644 --- a/qemu/hw/arm/armv7m.c +++ b/qemu/hw/arm/armv7m.c @@ -7,6 +7,10 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "hw/arm/arm.h" #include "hw/loader.h" @@ -166,17 +170,15 @@ static void armv7m_reset(void *opaque) mem_size is in bytes. Returns the NVIC array. */ -qemu_irq *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, +DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, const char *kernel_filename, const char *cpu_model) { ARMCPU *cpu; CPUARMState *env; DeviceState *nvic; - qemu_irq *pic = g_new(qemu_irq, num_irq); int image_size; uint64_t entry; uint64_t lowaddr; - int i; int big_endian; MemoryRegion *hack = g_new(MemoryRegion, 1); @@ -198,9 +200,6 @@ qemu_irq *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, qdev_init_nofail(nvic); sysbus_connect_irq(SYS_BUS_DEVICE(nvic), 0, qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ)); - for (i = 0; i < num_irq; i++) { - pic[i] = qdev_get_gpio_in(nvic, i); - } #ifdef TARGET_WORDS_BIGENDIAN big_endian = 1; @@ -215,7 +214,7 @@ qemu_irq *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, if (kernel_filename) { image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr, - NULL, big_endian, ELF_MACHINE, 1); + NULL, big_endian, EM_ARM, 1, 0); if (image_size < 0) { image_size = load_image_targphys(kernel_filename, 0, mem_size); lowaddr = 0; @@ -229,12 +228,12 @@ qemu_irq *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, /* Hack to map an additional page of ram at the top of the address space. This stops qemu complaining about executing code outside RAM when returning from an exception. */ - memory_region_init_ram(hack, NULL, "armv7m.hack", 0x1000, &error_abort); + memory_region_init_ram(hack, NULL, "armv7m.hack", 0x1000, &error_fatal); vmstate_register_ram_global(hack); memory_region_add_subregion(system_memory, 0xfffff000, hack); qemu_register_reset(armv7m_reset, cpu); - return pic; + return nvic; } static Property bitband_properties[] = { diff --git a/qemu/hw/arm/ast2400.c b/qemu/hw/arm/ast2400.c new file mode 100644 index 000000000..03f993863 --- /dev/null +++ b/qemu/hw/arm/ast2400.c @@ -0,0 +1,140 @@ +/* + * AST2400 SoC + * + * Andrew Jeffery + * Jeremy Kerr + * + * Copyright 2016 IBM Corp. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" +#include "exec/address-spaces.h" +#include "hw/arm/ast2400.h" +#include "hw/char/serial.h" + +#define AST2400_UART_5_BASE 0x00184000 +#define AST2400_IOMEM_SIZE 0x00200000 +#define AST2400_IOMEM_BASE 0x1E600000 +#define AST2400_VIC_BASE 0x1E6C0000 +#define AST2400_TIMER_BASE 0x1E782000 + +static const int uart_irqs[] = { 9, 32, 33, 34, 10 }; +static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, }; + +/* + * IO handlers: simply catch any reads/writes to IO addresses that aren't + * handled by a device mapping. + */ + +static uint64_t ast2400_io_read(void *p, hwaddr offset, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " [%u]\n", + __func__, offset, size); + return 0; +} + +static void ast2400_io_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " <- 0x%" PRIx64 " [%u]\n", + __func__, offset, value, size); +} + +static const MemoryRegionOps ast2400_io_ops = { + .read = ast2400_io_read, + .write = ast2400_io_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void ast2400_init(Object *obj) +{ + AST2400State *s = AST2400(obj); + + s->cpu = cpu_arm_init("arm926"); + + object_initialize(&s->vic, sizeof(s->vic), TYPE_ASPEED_VIC); + object_property_add_child(obj, "vic", OBJECT(&s->vic), NULL); + qdev_set_parent_bus(DEVICE(&s->vic), sysbus_get_default()); + + object_initialize(&s->timerctrl, sizeof(s->timerctrl), TYPE_ASPEED_TIMER); + object_property_add_child(obj, "timerctrl", OBJECT(&s->timerctrl), NULL); + qdev_set_parent_bus(DEVICE(&s->timerctrl), sysbus_get_default()); +} + +static void ast2400_realize(DeviceState *dev, Error **errp) +{ + int i; + AST2400State *s = AST2400(dev); + Error *err = NULL; + + /* IO space */ + memory_region_init_io(&s->iomem, NULL, &ast2400_io_ops, NULL, + "ast2400.io", AST2400_IOMEM_SIZE); + memory_region_add_subregion_overlap(get_system_memory(), AST2400_IOMEM_BASE, + &s->iomem, -1); + + /* VIC */ + object_property_set_bool(OBJECT(&s->vic), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->vic), 0, AST2400_VIC_BASE); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 0, + qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 1, + qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_FIQ)); + + /* Timer */ + object_property_set_bool(OBJECT(&s->timerctrl), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0, AST2400_TIMER_BASE); + for (i = 0; i < ARRAY_SIZE(timer_irqs); i++) { + qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->vic), timer_irqs[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq); + } + + /* UART - attach an 8250 to the IO space as our UART5 */ + if (serial_hds[0]) { + qemu_irq uart5 = qdev_get_gpio_in(DEVICE(&s->vic), uart_irqs[4]); + serial_mm_init(&s->iomem, AST2400_UART_5_BASE, 2, + uart5, 38400, serial_hds[0], DEVICE_LITTLE_ENDIAN); + } +} + +static void ast2400_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = ast2400_realize; + + /* + * Reason: creates an ARM CPU, thus use after free(), see + * arm_cpu_class_init() + */ + dc->cannot_destroy_with_object_finalize_yet = true; +} + +static const TypeInfo ast2400_type_info = { + .name = TYPE_AST2400, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AST2400State), + .instance_init = ast2400_init, + .class_init = ast2400_class_init, +}; + +static void ast2400_register_types(void) +{ + type_register_static(&ast2400_type_info); +} + +type_init(ast2400_register_types) diff --git a/qemu/hw/arm/bcm2835_peripherals.c b/qemu/hw/arm/bcm2835_peripherals.c new file mode 100644 index 000000000..234d51843 --- /dev/null +++ b/qemu/hw/arm/bcm2835_peripherals.c @@ -0,0 +1,312 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous + * + * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * This code is licensed under the GNU GPLv2 and later. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/arm/bcm2835_peripherals.h" +#include "hw/misc/bcm2835_mbox_defs.h" +#include "hw/arm/raspi_platform.h" +#include "sysemu/char.h" + +/* Peripheral base address on the VC (GPU) system bus */ +#define BCM2835_VC_PERI_BASE 0x7e000000 + +/* Capabilities for SD controller: no DMA, high-speed, default clocks etc. */ +#define BCM2835_SDHC_CAPAREG 0x52034b4 + +static void bcm2835_peripherals_init(Object *obj) +{ + BCM2835PeripheralState *s = BCM2835_PERIPHERALS(obj); + + /* Memory region for peripheral devices, which we export to our parent */ + memory_region_init(&s->peri_mr, obj,"bcm2835-peripherals", 0x1000000); + object_property_add_child(obj, "peripheral-io", OBJECT(&s->peri_mr), NULL); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->peri_mr); + + /* Internal memory region for peripheral bus addresses (not exported) */ + memory_region_init(&s->gpu_bus_mr, obj, "bcm2835-gpu", (uint64_t)1 << 32); + object_property_add_child(obj, "gpu-bus", OBJECT(&s->gpu_bus_mr), NULL); + + /* Internal memory region for request/response communication with + * mailbox-addressable peripherals (not exported) + */ + memory_region_init(&s->mbox_mr, obj, "bcm2835-mbox", + MBOX_CHAN_COUNT << MBOX_AS_CHAN_SHIFT); + + /* Interrupt Controller */ + object_initialize(&s->ic, sizeof(s->ic), TYPE_BCM2835_IC); + object_property_add_child(obj, "ic", OBJECT(&s->ic), NULL); + qdev_set_parent_bus(DEVICE(&s->ic), sysbus_get_default()); + + /* UART0 */ + s->uart0 = SYS_BUS_DEVICE(object_new("pl011")); + object_property_add_child(obj, "uart0", OBJECT(s->uart0), NULL); + qdev_set_parent_bus(DEVICE(s->uart0), sysbus_get_default()); + + /* AUX / UART1 */ + object_initialize(&s->aux, sizeof(s->aux), TYPE_BCM2835_AUX); + object_property_add_child(obj, "aux", OBJECT(&s->aux), NULL); + qdev_set_parent_bus(DEVICE(&s->aux), sysbus_get_default()); + + /* Mailboxes */ + object_initialize(&s->mboxes, sizeof(s->mboxes), TYPE_BCM2835_MBOX); + object_property_add_child(obj, "mbox", OBJECT(&s->mboxes), NULL); + qdev_set_parent_bus(DEVICE(&s->mboxes), sysbus_get_default()); + + object_property_add_const_link(OBJECT(&s->mboxes), "mbox-mr", + OBJECT(&s->mbox_mr), &error_abort); + + /* Framebuffer */ + object_initialize(&s->fb, sizeof(s->fb), TYPE_BCM2835_FB); + object_property_add_child(obj, "fb", OBJECT(&s->fb), NULL); + object_property_add_alias(obj, "vcram-size", OBJECT(&s->fb), "vcram-size", + &error_abort); + qdev_set_parent_bus(DEVICE(&s->fb), sysbus_get_default()); + + object_property_add_const_link(OBJECT(&s->fb), "dma-mr", + OBJECT(&s->gpu_bus_mr), &error_abort); + + /* Property channel */ + object_initialize(&s->property, sizeof(s->property), TYPE_BCM2835_PROPERTY); + object_property_add_child(obj, "property", OBJECT(&s->property), NULL); + object_property_add_alias(obj, "board-rev", OBJECT(&s->property), + "board-rev", &error_abort); + qdev_set_parent_bus(DEVICE(&s->property), sysbus_get_default()); + + object_property_add_const_link(OBJECT(&s->property), "fb", + OBJECT(&s->fb), &error_abort); + object_property_add_const_link(OBJECT(&s->property), "dma-mr", + OBJECT(&s->gpu_bus_mr), &error_abort); + + /* Extended Mass Media Controller */ + object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI); + object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL); + qdev_set_parent_bus(DEVICE(&s->sdhci), sysbus_get_default()); + + /* DMA Channels */ + object_initialize(&s->dma, sizeof(s->dma), TYPE_BCM2835_DMA); + object_property_add_child(obj, "dma", OBJECT(&s->dma), NULL); + qdev_set_parent_bus(DEVICE(&s->dma), sysbus_get_default()); + + object_property_add_const_link(OBJECT(&s->dma), "dma-mr", + OBJECT(&s->gpu_bus_mr), &error_abort); +} + +static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) +{ + BCM2835PeripheralState *s = BCM2835_PERIPHERALS(dev); + Object *obj; + MemoryRegion *ram; + Error *err = NULL; + uint32_t ram_size, vcram_size; + CharDriverState *chr; + int n; + + obj = object_property_get_link(OBJECT(dev), "ram", &err); + if (obj == NULL) { + error_setg(errp, "%s: required ram link not found: %s", + __func__, error_get_pretty(err)); + return; + } + + ram = MEMORY_REGION(obj); + ram_size = memory_region_size(ram); + + /* Map peripherals and RAM into the GPU address space. */ + memory_region_init_alias(&s->peri_mr_alias, OBJECT(s), + "bcm2835-peripherals", &s->peri_mr, 0, + memory_region_size(&s->peri_mr)); + + memory_region_add_subregion_overlap(&s->gpu_bus_mr, BCM2835_VC_PERI_BASE, + &s->peri_mr_alias, 1); + + /* RAM is aliased four times (different cache configurations) on the GPU */ + for (n = 0; n < 4; n++) { + memory_region_init_alias(&s->ram_alias[n], OBJECT(s), + "bcm2835-gpu-ram-alias[*]", ram, 0, ram_size); + memory_region_add_subregion_overlap(&s->gpu_bus_mr, (hwaddr)n << 30, + &s->ram_alias[n], 0); + } + + /* Interrupt Controller */ + object_property_set_bool(OBJECT(&s->ic), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->peri_mr, ARMCTRL_IC_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->ic), 0)); + sysbus_pass_irq(SYS_BUS_DEVICE(s), SYS_BUS_DEVICE(&s->ic)); + + /* UART0 */ + object_property_set_bool(OBJECT(s->uart0), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->peri_mr, UART0_OFFSET, + sysbus_mmio_get_region(s->uart0, 0)); + sysbus_connect_irq(s->uart0, 0, + qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, + INTERRUPT_UART)); + + /* AUX / UART1 */ + /* TODO: don't call qemu_char_get_next_serial() here, instead set + * chardev properties for each uart at the board level, once pl011 + * (uart0) has been updated to avoid qemu_char_get_next_serial() + */ + chr = qemu_char_get_next_serial(); + if (chr == NULL) { + chr = qemu_chr_new("bcm2835.uart1", "null", NULL); + } + qdev_prop_set_chr(DEVICE(&s->aux), "chardev", chr); + + object_property_set_bool(OBJECT(&s->aux), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->peri_mr, UART1_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->aux), 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->aux), 0, + qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, + INTERRUPT_AUX)); + + /* Mailboxes */ + object_property_set_bool(OBJECT(&s->mboxes), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->peri_mr, ARMCTRL_0_SBM_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mboxes), 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->mboxes), 0, + qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_ARM_IRQ, + INTERRUPT_ARM_MAILBOX)); + + /* Framebuffer */ + vcram_size = (uint32_t)object_property_get_int(OBJECT(s), "vcram-size", + &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_int(OBJECT(&s->fb), ram_size - vcram_size, + "vcram-base", &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->fb), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->mbox_mr, MBOX_CHAN_FB << MBOX_AS_CHAN_SHIFT, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->fb), 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->fb), 0, + qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_FB)); + + /* Property channel */ + object_property_set_bool(OBJECT(&s->property), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->mbox_mr, + MBOX_CHAN_PROPERTY << MBOX_AS_CHAN_SHIFT, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->property), 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->property), 0, + qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_PROPERTY)); + + /* Extended Mass Media Controller */ + object_property_set_int(OBJECT(&s->sdhci), BCM2835_SDHC_CAPAREG, "capareg", + &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->sdhci), true, "pending-insert-quirk", + &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->sdhci), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->peri_mr, EMMC_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->sdhci), 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, + qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, + INTERRUPT_ARASANSDIO)); + object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->sdhci), "sd-bus", + &err); + if (err) { + error_propagate(errp, err); + return; + } + + /* DMA Channels */ + object_property_set_bool(OBJECT(&s->dma), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->peri_mr, DMA_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 0)); + memory_region_add_subregion(&s->peri_mr, DMA15_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 1)); + + for (n = 0; n <= 12; n++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma), n, + qdev_get_gpio_in_named(DEVICE(&s->ic), + BCM2835_IC_GPU_IRQ, + INTERRUPT_DMA0 + n)); + } +} + +static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = bcm2835_peripherals_realize; + /* Reason: realize() method uses qemu_char_get_next_serial() */ + dc->cannot_instantiate_with_device_add_yet = true; +} + +static const TypeInfo bcm2835_peripherals_type_info = { + .name = TYPE_BCM2835_PERIPHERALS, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835PeripheralState), + .instance_init = bcm2835_peripherals_init, + .class_init = bcm2835_peripherals_class_init, +}; + +static void bcm2835_peripherals_register_types(void) +{ + type_register_static(&bcm2835_peripherals_type_info); +} + +type_init(bcm2835_peripherals_register_types) diff --git a/qemu/hw/arm/bcm2836.c b/qemu/hw/arm/bcm2836.c new file mode 100644 index 000000000..8451190a1 --- /dev/null +++ b/qemu/hw/arm/bcm2836.c @@ -0,0 +1,184 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous + * + * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * This code is licensed under the GNU GPLv2 and later. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" +#include "hw/arm/bcm2836.h" +#include "hw/arm/raspi_platform.h" +#include "hw/sysbus.h" +#include "exec/address-spaces.h" + +/* Peripheral base address seen by the CPU */ +#define BCM2836_PERI_BASE 0x3F000000 + +/* "QA7" (Pi2) interrupt controller and mailboxes etc. */ +#define BCM2836_CONTROL_BASE 0x40000000 + +static void bcm2836_init(Object *obj) +{ + BCM2836State *s = BCM2836(obj); + int n; + + for (n = 0; n < BCM2836_NCPUS; n++) { + object_initialize(&s->cpus[n], sizeof(s->cpus[n]), + "cortex-a15-" TYPE_ARM_CPU); + 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); + qdev_set_parent_bus(DEVICE(&s->control), sysbus_get_default()); + + object_initialize(&s->peripherals, sizeof(s->peripherals), + TYPE_BCM2835_PERIPHERALS); + object_property_add_child(obj, "peripherals", OBJECT(&s->peripherals), + &error_abort); + object_property_add_alias(obj, "board-rev", OBJECT(&s->peripherals), + "board-rev", &error_abort); + object_property_add_alias(obj, "vcram-size", OBJECT(&s->peripherals), + "vcram-size", &error_abort); + qdev_set_parent_bus(DEVICE(&s->peripherals), sysbus_get_default()); +} + +static void bcm2836_realize(DeviceState *dev, Error **errp) +{ + BCM2836State *s = BCM2836(dev); + Object *obj; + Error *err = NULL; + int n; + + /* common peripherals from bcm2835 */ + + obj = object_property_get_link(OBJECT(dev), "ram", &err); + if (obj == NULL) { + error_setg(errp, "%s: required ram link not found: %s", + __func__, error_get_pretty(err)); + return; + } + + object_property_add_const_link(OBJECT(&s->peripherals), "ram", obj, &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->peripherals), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->peripherals), + "sd-bus", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->peripherals), 0, + BCM2836_PERI_BASE, 1); + + /* bcm2836 interrupt controller (and mailboxes, etc.) */ + object_property_set_bool(OBJECT(&s->control), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->control), 0, BCM2836_CONTROL_BASE); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 0, + qdev_get_gpio_in_named(DEVICE(&s->control), "gpu-irq", 0)); + 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; + + /* set periphbase/CBAR value for CPU-local registers */ + object_property_set_int(OBJECT(&s->cpus[n]), + BCM2836_PERI_BASE + MCORE_OFFSET, + "reset-cbar", &err); + if (err) { + error_propagate(errp, err); + return; + } + + /* start powered off if not enabled */ + object_property_set_bool(OBJECT(&s->cpus[n]), n >= s->enabled_cpus, + "start-powered-off", &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->cpus[n]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + /* Connect irq/fiq outputs from the interrupt controller. */ + qdev_connect_gpio_out_named(DEVICE(&s->control), "irq", n, + qdev_get_gpio_in(DEVICE(&s->cpus[n]), ARM_CPU_IRQ)); + qdev_connect_gpio_out_named(DEVICE(&s->control), "fiq", n, + qdev_get_gpio_in(DEVICE(&s->cpus[n]), ARM_CPU_FIQ)); + + /* Connect timers from the CPU to the interrupt controller */ + qdev_connect_gpio_out(DEVICE(&s->cpus[n]), GTIMER_PHYS, + qdev_get_gpio_in_named(DEVICE(&s->control), "cntpnsirq", n)); + qdev_connect_gpio_out(DEVICE(&s->cpus[n]), GTIMER_VIRT, + qdev_get_gpio_in_named(DEVICE(&s->control), "cntvirq", n)); + qdev_connect_gpio_out(DEVICE(&s->cpus[n]), GTIMER_HYP, + qdev_get_gpio_in_named(DEVICE(&s->control), "cnthpirq", n)); + qdev_connect_gpio_out(DEVICE(&s->cpus[n]), GTIMER_SEC, + qdev_get_gpio_in_named(DEVICE(&s->control), "cntpsirq", n)); + } +} + +static Property bcm2836_props[] = { + DEFINE_PROP_UINT32("enabled-cpus", BCM2836State, enabled_cpus, BCM2836_NCPUS), + DEFINE_PROP_END_OF_LIST() +}; + +static void bcm2836_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->props = bcm2836_props; + dc->realize = bcm2836_realize; + + /* + * Reason: creates an ARM CPU, thus use after free(), see + * arm_cpu_class_init() + */ + dc->cannot_destroy_with_object_finalize_yet = true; +} + +static const TypeInfo bcm2836_type_info = { + .name = TYPE_BCM2836, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2836State), + .instance_init = bcm2836_init, + .class_init = bcm2836_class_init, +}; + +static void bcm2836_register_types(void) +{ + type_register_static(&bcm2836_type_info); +} + +type_init(bcm2836_register_types) diff --git a/qemu/hw/arm/boot.c b/qemu/hw/arm/boot.c index 5b969cda1..587694557 100644 --- a/qemu/hw/arm/boot.c +++ b/qemu/hw/arm/boot.c @@ -7,9 +7,12 @@ * This code is licensed under the GPL. */ -#include "config.h" +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "hw/arm/arm.h" +#include "hw/arm/linux-boot-if.h" +#include "sysemu/kvm.h" #include "sysemu/sysemu.h" #include "hw/boards.h" #include "hw/loader.h" @@ -27,14 +30,15 @@ #define KERNEL64_LOAD_ADDR 0x00080000 typedef enum { - FIXUP_NONE = 0, /* do nothing */ - FIXUP_TERMINATOR, /* end of insns */ - FIXUP_BOARDID, /* overwrite with board ID number */ - FIXUP_ARGPTR, /* overwrite with pointer to kernel args */ - FIXUP_ENTRYPOINT, /* overwrite with kernel entry point */ - FIXUP_GIC_CPU_IF, /* overwrite with GIC CPU interface address */ - FIXUP_BOOTREG, /* overwrite with boot register address */ - FIXUP_DSB, /* overwrite with correct DSB insn for cpu */ + FIXUP_NONE = 0, /* do nothing */ + FIXUP_TERMINATOR, /* end of insns */ + FIXUP_BOARDID, /* overwrite with board ID number */ + FIXUP_BOARD_SETUP, /* overwrite with board specific setup code address */ + FIXUP_ARGPTR, /* overwrite with pointer to kernel args */ + FIXUP_ENTRYPOINT, /* overwrite with kernel entry point */ + FIXUP_GIC_CPU_IF, /* overwrite with GIC CPU interface address */ + FIXUP_BOOTREG, /* overwrite with boot register address */ + FIXUP_DSB, /* overwrite with correct DSB insn for cpu */ FIXUP_MAX, } FixupType; @@ -57,8 +61,17 @@ static const ARMInsnFixup bootloader_aarch64[] = { { 0, FIXUP_TERMINATOR } }; -/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */ +/* A very small bootloader: call the board-setup code (if needed), + * set r0-r2, then jump to the kernel. + * If we're not calling boot setup code then we don't copy across + * the first BOOTLOADER_NO_BOARD_SETUP_OFFSET insns in this array. + */ + static const ARMInsnFixup bootloader[] = { + { 0xe28fe004 }, /* add lr, pc, #4 */ + { 0xe51ff004 }, /* ldr pc, [pc, #-4] */ + { 0, FIXUP_BOARD_SETUP }, +#define BOOTLOADER_NO_BOARD_SETUP_OFFSET 3 { 0xe3a00000 }, /* mov r0, #0 */ { 0xe59f1004 }, /* ldr r1, [pc, #4] */ { 0xe59f2004 }, /* ldr r2, [pc, #4] */ @@ -130,6 +143,7 @@ static void write_bootloader(const char *name, hwaddr addr, case FIXUP_NONE: break; case FIXUP_BOARDID: + case FIXUP_BOARD_SETUP: case FIXUP_ARGPTR: case FIXUP_ENTRYPOINT: case FIXUP_GIC_CPU_IF: @@ -165,6 +179,57 @@ static void default_write_secondary(ARMCPU *cpu, smpboot, fixupcontext); } +void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu, + const struct arm_boot_info *info, + hwaddr mvbar_addr) +{ + int n; + uint32_t mvbar_blob[] = { + /* mvbar_addr: secure monitor vectors + * Default unimplemented and unused vectors to spin. Makes it + * easier to debug (as opposed to the CPU running away). + */ + 0xeafffffe, /* (spin) */ + 0xeafffffe, /* (spin) */ + 0xe1b0f00e, /* movs pc, lr ;SMC exception return */ + 0xeafffffe, /* (spin) */ + 0xeafffffe, /* (spin) */ + 0xeafffffe, /* (spin) */ + 0xeafffffe, /* (spin) */ + 0xeafffffe, /* (spin) */ + }; + uint32_t board_setup_blob[] = { + /* board setup addr */ + 0xe3a00e00 + (mvbar_addr >> 4), /* mov r0, #mvbar_addr */ + 0xee0c0f30, /* mcr p15, 0, r0, c12, c0, 1 ;set MVBAR */ + 0xee110f11, /* mrc p15, 0, r0, c1 , c1, 0 ;read SCR */ + 0xe3800031, /* orr r0, #0x31 ;enable AW, FW, NS */ + 0xee010f11, /* mcr p15, 0, r0, c1, c1, 0 ;write SCR */ + 0xe1a0100e, /* mov r1, lr ;save LR across SMC */ + 0xe1600070, /* smc #0 ;call monitor to flush SCR */ + 0xe1a0f001, /* mov pc, r1 ;return */ + }; + + /* check that mvbar_addr is correctly aligned and relocatable (using MOV) */ + assert((mvbar_addr & 0x1f) == 0 && (mvbar_addr >> 4) < 0x100); + + /* check that these blobs don't overlap */ + assert((mvbar_addr + sizeof(mvbar_blob) <= info->board_setup_addr) + || (info->board_setup_addr + sizeof(board_setup_blob) <= mvbar_addr)); + + for (n = 0; n < ARRAY_SIZE(mvbar_blob); n++) { + mvbar_blob[n] = tswap32(mvbar_blob[n]); + } + rom_add_blob_fixed("board-setup-mvbar", mvbar_blob, sizeof(mvbar_blob), + mvbar_addr); + + for (n = 0; n < ARRAY_SIZE(board_setup_blob); n++) { + board_setup_blob[n] = tswap32(board_setup_blob[n]); + } + rom_add_blob_fixed("board-setup", board_setup_blob, + sizeof(board_setup_blob), info->board_setup_addr); +} + static void default_reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info) { @@ -373,8 +438,10 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo, return 0; } - acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells"); - scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells"); + acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells", + NULL, &error_fatal); + scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells", + NULL, &error_fatal); if (acells == 0 || scells == 0) { fprintf(stderr, "dtb file invalid (#address-cells or #size-cells 0)\n"); goto fail; @@ -452,9 +519,34 @@ static void do_cpu_reset(void *opaque) cpu_reset(cs); if (info) { if (!info->is_linux) { + int i; /* Jump to the entry point. */ uint64_t entry = info->entry; + switch (info->endianness) { + case ARM_ENDIANNESS_LE: + env->cp15.sctlr_el[1] &= ~SCTLR_E0E; + for (i = 1; i < 4; ++i) { + env->cp15.sctlr_el[i] &= ~SCTLR_EE; + } + env->uncached_cpsr &= ~CPSR_E; + break; + case ARM_ENDIANNESS_BE8: + env->cp15.sctlr_el[1] |= SCTLR_E0E; + for (i = 1; i < 4; ++i) { + env->cp15.sctlr_el[i] |= SCTLR_EE; + } + env->uncached_cpsr |= CPSR_E; + break; + case ARM_ENDIANNESS_BE32: + env->cp15.sctlr_el[1] |= SCTLR_B; + break; + case ARM_ENDIANNESS_UNKNOWN: + break; /* Board's decision */ + default: + g_assert_not_reached(); + } + if (!env->aarch64) { env->thumb = info->entry & 1; entry &= 0xfffffffe; @@ -475,7 +567,9 @@ static void do_cpu_reset(void *opaque) * adjust. */ if (env->aarch64) { + env->cp15.scr_el3 |= SCR_RW; if (arm_feature(env, ARM_FEATURE_EL2)) { + env->cp15.hcr_el2 |= HCR_RW; env->pstate = PSTATE_MODE_EL2h; } else { env->pstate = PSTATE_MODE_EL1h; @@ -483,7 +577,8 @@ static void do_cpu_reset(void *opaque) } /* Set to non-secure if not a secure boot */ - if (!info->secure_boot) { + if (!info->secure_boot && + (cs != first_cpu || !info->secure_board_setup)) { /* Linux expects non-secure state */ env->cp15.scr_el3 |= SCR_NS; } @@ -555,6 +650,76 @@ static void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key, fw_cfg_add_bytes(fw_cfg, data_key, data, size); } +static int do_arm_linux_init(Object *obj, void *opaque) +{ + if (object_dynamic_cast(obj, TYPE_ARM_LINUX_BOOT_IF)) { + ARMLinuxBootIf *albif = ARM_LINUX_BOOT_IF(obj); + ARMLinuxBootIfClass *albifc = ARM_LINUX_BOOT_IF_GET_CLASS(obj); + struct arm_boot_info *info = opaque; + + if (albifc->arm_linux_init) { + albifc->arm_linux_init(albif, info->secure_boot); + } + } + return 0; +} + +static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, + int elf_machine) +{ + bool elf_is64; + union { + Elf32_Ehdr h32; + Elf64_Ehdr h64; + } elf_header; + int data_swab = 0; + bool big_endian; + uint64_t ret = -1; + Error *err = NULL; + + + load_elf_hdr(info->kernel_filename, &elf_header, &elf_is64, &err); + if (err) { + return ret; + } + + if (elf_is64) { + big_endian = elf_header.h64.e_ident[EI_DATA] == ELFDATA2MSB; + info->endianness = big_endian ? ARM_ENDIANNESS_BE8 + : ARM_ENDIANNESS_LE; + } else { + big_endian = elf_header.h32.e_ident[EI_DATA] == ELFDATA2MSB; + if (big_endian) { + if (bswap32(elf_header.h32.e_flags) & EF_ARM_BE8) { + info->endianness = ARM_ENDIANNESS_BE8; + } else { + info->endianness = ARM_ENDIANNESS_BE32; + /* In BE32, the CPU has a different view of the per-byte + * address map than the rest of the system. BE32 ELF files + * are organised such that they can be programmed through + * the CPU's per-word byte-reversed view of the world. QEMU + * however loads ELF files independently of the CPU. So + * tell the ELF loader to byte reverse the data for us. + */ + data_swab = 2; + } + } else { + info->endianness = ARM_ENDIANNESS_LE; + } + } + + ret = load_elf(info->kernel_filename, NULL, NULL, + pentry, lowaddr, highaddr, big_endian, elf_machine, + 1, data_swab); + if (ret <= 0) { + /* The header loaded but the image didn't */ + exit(1); + } + + return ret; +} + static void arm_load_kernel_notify(Notifier *notifier, void *data) { CPUState *cs; @@ -564,7 +729,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) uint64_t elf_entry, elf_low_addr, elf_high_addr; int elf_machine; hwaddr entry, kernel_load_offset; - int big_endian; static const ARMInsnFixup *primary_loader; ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier, notifier, notifier); @@ -572,6 +736,12 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) struct arm_boot_info *info = container_of(n, struct arm_boot_info, load_kernel_notifier); + /* The board code is not supposed to set secure_board_setup unless + * running its code in secure mode is actually possible, and KVM + * doesn't support secure. + */ + assert(!(info->secure_board_setup && kvm_enabled())); + /* Load the kernel. */ if (!info->kernel_filename || info->firmware_loaded) { @@ -625,6 +795,9 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) elf_machine = EM_AARCH64; } else { primary_loader = bootloader; + if (!info->write_board_setup) { + primary_loader += BOOTLOADER_NO_BOARD_SETUP_OFFSET; + } kernel_load_offset = KERNEL_LOAD_ADDR; elf_machine = EM_ARM; } @@ -641,12 +814,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) if (info->nb_cpus == 0) info->nb_cpus = 1; -#ifdef TARGET_WORDS_BIGENDIAN - big_endian = 1; -#else - big_endian = 0; -#endif - /* We want to put the initrd far enough into RAM that when the * kernel is uncompressed it will not clobber the initrd. However * on boards without much RAM we must ensure that we still leave @@ -661,9 +828,8 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) MIN(info->ram_size / 2, 128 * 1024 * 1024); /* Assume that raw images are linux kernels, and ELF images are not. */ - kernel_size = load_elf(info->kernel_filename, NULL, NULL, &elf_entry, - &elf_low_addr, &elf_high_addr, big_endian, - elf_machine, 1); + kernel_size = arm_load_elf(info, &elf_entry, &elf_low_addr, + &elf_high_addr, elf_machine); if (kernel_size > 0 && have_dtb(info)) { /* If there is still some room left at the base of RAM, try and put * the DTB there like we do for images loaded with -bios or -pflash. @@ -730,6 +896,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) info->initrd_size = initrd_size; fixupcontext[FIXUP_BOARDID] = info->board_id; + fixupcontext[FIXUP_BOARD_SETUP] = info->board_setup_addr; /* for device tree boot, we pass the DTB directly in r2. Otherwise * we point to the kernel args. @@ -778,6 +945,15 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) if (info->nb_cpus > 1) { info->write_secondary_boot(cpu, info); } + if (info->write_board_setup) { + info->write_board_setup(cpu, info); + } + + /* Notify devices which need to fake up firmware initialization + * that we're doing a direct kernel boot. + */ + object_child_foreach_recursive(object_get_root(), + do_arm_linux_init, info); } info->is_linux = is_linux; @@ -803,3 +979,16 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) qemu_register_reset(do_cpu_reset, ARM_CPU(cs)); } } + +static const TypeInfo arm_linux_boot_if_info = { + .name = TYPE_ARM_LINUX_BOOT_IF, + .parent = TYPE_INTERFACE, + .class_size = sizeof(ARMLinuxBootIfClass), +}; + +static void arm_linux_boot_register_types(void) +{ + type_register_static(&arm_linux_boot_if_info); +} + +type_init(arm_linux_boot_register_types) diff --git a/qemu/hw/arm/collie.c b/qemu/hw/arm/collie.c index 6c9b82fc5..8bb308a42 100644 --- a/qemu/hw/arm/collie.c +++ b/qemu/hw/arm/collie.c @@ -8,6 +8,7 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "hw/boards.h" @@ -58,15 +59,10 @@ static void collie_init(MachineState *machine) arm_load_kernel(s->cpu, &collie_binfo); } -static QEMUMachine collie_machine = { - .name = "collie", - .desc = "Collie PDA (SA-1110)", - .init = collie_init, -}; - -static void collie_machine_init(void) +static void collie_machine_init(MachineClass *mc) { - qemu_register_machine(&collie_machine); + mc->desc = "Sharp SL-5500 (Collie) PDA (SA-1110)"; + mc->init = collie_init; } -machine_init(collie_machine_init) +DEFINE_MACHINE("collie", collie_machine_init) diff --git a/qemu/hw/arm/cubieboard.c b/qemu/hw/arm/cubieboard.c index 1582250eb..fbd78ed01 100644 --- a/qemu/hw/arm/cubieboard.c +++ b/qemu/hw/arm/cubieboard.c @@ -15,6 +15,10 @@ * for more details. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "hw/devices.h" #include "hw/boards.h" @@ -39,27 +43,26 @@ static void cubieboard_init(MachineState *machine) object_property_set_int(OBJECT(&s->a10->emac), 1, "phy-addr", &err); if (err != NULL) { - error_report("Couldn't set phy address: %s", error_get_pretty(err)); + error_reportf_err(err, "Couldn't set phy address: "); exit(1); } object_property_set_int(OBJECT(&s->a10->timer), 32768, "clk0-freq", &err); if (err != NULL) { - error_report("Couldn't set clk0 frequency: %s", error_get_pretty(err)); + error_reportf_err(err, "Couldn't set clk0 frequency: "); exit(1); } object_property_set_int(OBJECT(&s->a10->timer), 24000000, "clk1-freq", &err); if (err != NULL) { - error_report("Couldn't set clk1 frequency: %s", error_get_pretty(err)); + error_reportf_err(err, "Couldn't set clk1 frequency: "); exit(1); } object_property_set_bool(OBJECT(s->a10), true, "realized", &err); if (err != NULL) { - error_report("Couldn't realize Allwinner A10: %s", - error_get_pretty(err)); + error_reportf_err(err, "Couldn't realize Allwinner A10: "); exit(1); } @@ -74,16 +77,10 @@ static void cubieboard_init(MachineState *machine) arm_load_kernel(&s->a10->cpu, &cubieboard_binfo); } -static QEMUMachine cubieboard_machine = { - .name = "cubieboard", - .desc = "cubietech cubieboard", - .init = cubieboard_init, -}; - - -static void cubieboard_machine_init(void) +static void cubieboard_machine_init(MachineClass *mc) { - qemu_register_machine(&cubieboard_machine); + mc->desc = "cubietech cubieboard"; + mc->init = cubieboard_init; } -machine_init(cubieboard_machine_init) +DEFINE_MACHINE("cubieboard", cubieboard_machine_init) diff --git a/qemu/hw/arm/digic.c b/qemu/hw/arm/digic.c index ec8c33060..e0f973032 100644 --- a/qemu/hw/arm/digic.c +++ b/qemu/hw/arm/digic.c @@ -20,6 +20,8 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/arm/digic.h" #define DIGIC4_TIMER_BASE(n) (0xc0210000 + (n) * 0x100) @@ -97,6 +99,12 @@ static void digic_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = digic_realize; + + /* + * Reason: creates an ARM CPU, thus use after free(), see + * arm_cpu_class_init() + */ + dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo digic_type_info = { diff --git a/qemu/hw/arm/digic_boards.c b/qemu/hw/arm/digic_boards.c index f8ba9e595..520c8e9ff 100644 --- a/qemu/hw/arm/digic_boards.c +++ b/qemu/hw/arm/digic_boards.c @@ -23,6 +23,10 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/boards.h" #include "exec/address-spaces.h" #include "qemu/error-report.h" @@ -64,8 +68,7 @@ static void digic4_board_init(DigicBoard *board) s->digic = DIGIC(object_new(TYPE_DIGIC)); object_property_set_bool(OBJECT(s->digic), true, "realized", &err); if (err != NULL) { - error_report("Couldn't realize DIGIC SoC: %s", - error_get_pretty(err)); + error_reportf_err(err, "Couldn't realize DIGIC SoC: "); exit(1); } @@ -148,15 +151,10 @@ static void canon_a1100_init(MachineState *machine) digic4_board_init(&digic4_board_canon_a1100); } -static QEMUMachine canon_a1100 = { - .name = "canon-a1100", - .desc = "Canon PowerShot A1100 IS", - .init = &canon_a1100_init, -}; - -static void digic_register_machines(void) +static void canon_a1100_machine_init(MachineClass *mc) { - qemu_register_machine(&canon_a1100); + mc->desc = "Canon PowerShot A1100 IS"; + mc->init = &canon_a1100_init; } -machine_init(digic_register_machines) +DEFINE_MACHINE("canon-a1100", canon_a1100_machine_init) diff --git a/qemu/hw/arm/exynos4210.c b/qemu/hw/arm/exynos4210.c index c55fab813..be3c96d21 100644 --- a/qemu/hw/arm/exynos4210.c +++ b/qemu/hw/arm/exynos4210.c @@ -21,6 +21,10 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/boards.h" #include "sysemu/sysemu.h" #include "hw/sysbus.h" @@ -150,27 +154,18 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, for (n = 0; n < EXYNOS4210_NCPUS; n++) { Object *cpuobj = object_new(object_class_get_name(cpu_oc)); - Error *err = NULL; /* By default A9 CPUs have EL3 enabled. This board does not currently * support EL3 so the CPU EL3 property is disabled before realization. */ if (object_property_find(cpuobj, "has_el3", NULL)) { - object_property_set_bool(cpuobj, false, "has_el3", &err); - if (err) { - error_report_err(err); - exit(1); - } + object_property_set_bool(cpuobj, false, "has_el3", &error_fatal); } s->cpu[n] = ARM_CPU(cpuobj); object_property_set_int(cpuobj, EXYNOS4210_SMP_PRIVATE_BASE_ADDR, "reset-cbar", &error_abort); - object_property_set_bool(cpuobj, true, "realized", &err); - if (err) { - error_report_err(err); - exit(1); - } + object_property_set_bool(cpuobj, true, "realized", &error_fatal); } /*** IRQs ***/ @@ -259,7 +254,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, /* Internal ROM */ memory_region_init_ram(&s->irom_mem, NULL, "exynos4210.irom", - EXYNOS4210_IROM_SIZE, &error_abort); + EXYNOS4210_IROM_SIZE, &error_fatal); vmstate_register_ram_global(&s->irom_mem); memory_region_set_readonly(&s->irom_mem, true); memory_region_add_subregion(system_mem, EXYNOS4210_IROM_BASE_ADDR, @@ -275,7 +270,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, /* Internal RAM */ memory_region_init_ram(&s->iram_mem, NULL, "exynos4210.iram", - EXYNOS4210_IRAM_SIZE, &error_abort); + EXYNOS4210_IRAM_SIZE, &error_fatal); vmstate_register_ram_global(&s->iram_mem); memory_region_add_subregion(system_mem, EXYNOS4210_IRAM_BASE_ADDR, &s->iram_mem); @@ -284,14 +279,14 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, mem_size = ram_size; if (mem_size > EXYNOS4210_DRAM_MAX_SIZE) { memory_region_init_ram(&s->dram1_mem, NULL, "exynos4210.dram1", - mem_size - EXYNOS4210_DRAM_MAX_SIZE, &error_abort); + mem_size - EXYNOS4210_DRAM_MAX_SIZE, &error_fatal); vmstate_register_ram_global(&s->dram1_mem); memory_region_add_subregion(system_mem, EXYNOS4210_DRAM1_BASE_ADDR, &s->dram1_mem); mem_size = EXYNOS4210_DRAM_MAX_SIZE; } memory_region_init_ram(&s->dram0_mem, NULL, "exynos4210.dram0", mem_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(&s->dram0_mem); memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR, &s->dram0_mem); diff --git a/qemu/hw/arm/exynos4_boards.c b/qemu/hw/arm/exynos4_boards.c index d644db1ef..0efa19405 100644 --- a/qemu/hw/arm/exynos4_boards.c +++ b/qemu/hw/arm/exynos4_boards.c @@ -21,6 +21,9 @@ * */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "sysemu/sysemu.h" #include "sysemu/qtest.h" #include "hw/sysbus.h" @@ -74,8 +77,6 @@ static struct arm_boot_info exynos4_board_binfo = { .write_secondary_boot = exynos4210_write_secondary, }; -static QEMUMachine exynos4_machines[EXYNOS4_NUM_OF_BOARDS]; - static void lan9215_init(uint32_t base, qemu_irq irq) { DeviceState *dev; @@ -97,11 +98,12 @@ static void lan9215_init(uint32_t base, qemu_irq irq) static Exynos4210State *exynos4_boards_init_common(MachineState *machine, Exynos4BoardType board_type) { + MachineClass *mc = MACHINE_GET_CLASS(machine); + if (smp_cpus != EXYNOS4210_NCPUS && !qtest_enabled()) { fprintf(stderr, "%s board supports only %d CPU cores. Ignoring smp_cpus" " value.\n", - exynos4_machines[board_type].name, - exynos4_machines[board_type].max_cpus); + mc->name, EXYNOS4210_NCPUS); } exynos4_board_binfo.ram_size = exynos4_board_ram_size[board_type]; @@ -145,25 +147,40 @@ static void smdkc210_init(MachineState *machine) arm_load_kernel(ARM_CPU(first_cpu), &exynos4_board_binfo); } -static QEMUMachine exynos4_machines[EXYNOS4_NUM_OF_BOARDS] = { - [EXYNOS4_BOARD_NURI] = { - .name = "nuri", - .desc = "Samsung NURI board (Exynos4210)", - .init = nuri_init, - .max_cpus = EXYNOS4210_NCPUS, - }, - [EXYNOS4_BOARD_SMDKC210] = { - .name = "smdkc210", - .desc = "Samsung SMDKC210 board (Exynos4210)", - .init = smdkc210_init, - .max_cpus = EXYNOS4210_NCPUS, - }, +static void nuri_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Samsung NURI board (Exynos4210)"; + mc->init = nuri_init; + mc->max_cpus = EXYNOS4210_NCPUS; +} + +static const TypeInfo nuri_type = { + .name = MACHINE_TYPE_NAME("nuri"), + .parent = TYPE_MACHINE, + .class_init = nuri_class_init, +}; + +static void smdkc210_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Samsung SMDKC210 board (Exynos4210)"; + mc->init = smdkc210_init; + mc->max_cpus = EXYNOS4210_NCPUS; +} + +static const TypeInfo smdkc210_type = { + .name = MACHINE_TYPE_NAME("smdkc210"), + .parent = TYPE_MACHINE, + .class_init = smdkc210_class_init, }; -static void exynos4_machine_init(void) +static void exynos4_machines_init(void) { - qemu_register_machine(&exynos4_machines[EXYNOS4_BOARD_NURI]); - qemu_register_machine(&exynos4_machines[EXYNOS4_BOARD_SMDKC210]); + type_register_static(&nuri_type); + type_register_static(&smdkc210_type); } -machine_init(exynos4_machine_init); +type_init(exynos4_machines_init) diff --git a/qemu/hw/arm/fsl-imx25.c b/qemu/hw/arm/fsl-imx25.c new file mode 100644 index 000000000..2f878b935 --- /dev/null +++ b/qemu/hw/arm/fsl-imx25.c @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2013 Jean-Christophe Dubois + * + * i.MX25 SOC emulation. + * + * Based on hw/arm/xlnx-zynqmp.c + * + * Copyright (C) 2015 Xilinx Inc + * Written by Peter Crosthwaite + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" +#include "hw/arm/fsl-imx25.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "hw/boards.h" +#include "sysemu/char.h" + +static void fsl_imx25_init(Object *obj) +{ + FslIMX25State *s = FSL_IMX25(obj); + int i; + + object_initialize(&s->cpu, sizeof(s->cpu), "arm926-" TYPE_ARM_CPU); + + object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC); + qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default()); + + object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX25_CCM); + qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default()); + + for (i = 0; i < FSL_IMX25_NUM_UARTS; i++) { + object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL); + qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default()); + } + + for (i = 0; i < FSL_IMX25_NUM_GPTS; i++) { + object_initialize(&s->gpt[i], sizeof(s->gpt[i]), TYPE_IMX_GPT); + qdev_set_parent_bus(DEVICE(&s->gpt[i]), sysbus_get_default()); + } + + for (i = 0; i < FSL_IMX25_NUM_EPITS; i++) { + object_initialize(&s->epit[i], sizeof(s->epit[i]), TYPE_IMX_EPIT); + qdev_set_parent_bus(DEVICE(&s->epit[i]), sysbus_get_default()); + } + + object_initialize(&s->fec, sizeof(s->fec), TYPE_IMX_FEC); + qdev_set_parent_bus(DEVICE(&s->fec), sysbus_get_default()); + + for (i = 0; i < FSL_IMX25_NUM_I2CS; i++) { + object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C); + qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default()); + } + + for (i = 0; i < FSL_IMX25_NUM_GPIOS; i++) { + object_initialize(&s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO); + qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus_get_default()); + } +} + +static void fsl_imx25_realize(DeviceState *dev, Error **errp) +{ + FslIMX25State *s = FSL_IMX25(dev); + uint8_t i; + Error *err = NULL; + + object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->avic), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->avic), 0, FSL_IMX25_AVIC_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->avic), 0, + qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->avic), 1, + qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ)); + + object_property_set_bool(OBJECT(&s->ccm), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, FSL_IMX25_CCM_ADDR); + + /* Initialize all UARTs */ + for (i = 0; i < FSL_IMX25_NUM_UARTS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } serial_table[FSL_IMX25_NUM_UARTS] = { + { FSL_IMX25_UART1_ADDR, FSL_IMX25_UART1_IRQ }, + { FSL_IMX25_UART2_ADDR, FSL_IMX25_UART2_IRQ }, + { FSL_IMX25_UART3_ADDR, FSL_IMX25_UART3_IRQ }, + { FSL_IMX25_UART4_ADDR, FSL_IMX25_UART4_IRQ }, + { FSL_IMX25_UART5_ADDR, FSL_IMX25_UART5_IRQ } + }; + + if (i < MAX_SERIAL_PORTS) { + CharDriverState *chr; + + chr = serial_hds[i]; + + if (!chr) { + char label[20]; + snprintf(label, sizeof(label), "imx31.uart%d", i); + chr = qemu_chr_new(label, "null", NULL); + } + + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr); + } + + object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + serial_table[i].irq)); + } + + /* Initialize all GPT timers */ + for (i = 0; i < FSL_IMX25_NUM_GPTS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } gpt_table[FSL_IMX25_NUM_GPTS] = { + { FSL_IMX25_GPT1_ADDR, FSL_IMX25_GPT1_IRQ }, + { FSL_IMX25_GPT2_ADDR, FSL_IMX25_GPT2_IRQ }, + { FSL_IMX25_GPT3_ADDR, FSL_IMX25_GPT3_IRQ }, + { FSL_IMX25_GPT4_ADDR, FSL_IMX25_GPT4_IRQ } + }; + + s->gpt[i].ccm = IMX_CCM(&s->ccm); + + object_property_set_bool(OBJECT(&s->gpt[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, gpt_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + gpt_table[i].irq)); + } + + /* Initialize all EPIT timers */ + for (i = 0; i < FSL_IMX25_NUM_EPITS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } epit_table[FSL_IMX25_NUM_EPITS] = { + { FSL_IMX25_EPIT1_ADDR, FSL_IMX25_EPIT1_IRQ }, + { FSL_IMX25_EPIT2_ADDR, FSL_IMX25_EPIT2_IRQ } + }; + + s->epit[i].ccm = IMX_CCM(&s->ccm); + + object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->epit[i]), 0, epit_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->epit[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + epit_table[i].irq)); + } + + qdev_set_nic_properties(DEVICE(&s->fec), &nd_table[0]); + object_property_set_bool(OBJECT(&s->fec), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->fec), 0, FSL_IMX25_FEC_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->fec), 0, + qdev_get_gpio_in(DEVICE(&s->avic), FSL_IMX25_FEC_IRQ)); + + + /* Initialize all I2C */ + for (i = 0; i < FSL_IMX25_NUM_I2CS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } i2c_table[FSL_IMX25_NUM_I2CS] = { + { FSL_IMX25_I2C1_ADDR, FSL_IMX25_I2C1_IRQ }, + { FSL_IMX25_I2C2_ADDR, FSL_IMX25_I2C2_IRQ }, + { FSL_IMX25_I2C3_ADDR, FSL_IMX25_I2C3_IRQ } + }; + + object_property_set_bool(OBJECT(&s->i2c[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, i2c_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + i2c_table[i].irq)); + } + + /* Initialize all GPIOs */ + for (i = 0; i < FSL_IMX25_NUM_GPIOS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } gpio_table[FSL_IMX25_NUM_GPIOS] = { + { FSL_IMX25_GPIO1_ADDR, FSL_IMX25_GPIO1_IRQ }, + { FSL_IMX25_GPIO2_ADDR, FSL_IMX25_GPIO2_IRQ }, + { FSL_IMX25_GPIO3_ADDR, FSL_IMX25_GPIO3_IRQ }, + { FSL_IMX25_GPIO4_ADDR, FSL_IMX25_GPIO4_IRQ } + }; + + object_property_set_bool(OBJECT(&s->gpio[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr); + /* Connect GPIO IRQ to PIC */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + gpio_table[i].irq)); + } + + /* initialize 2 x 16 KB ROM */ + memory_region_init_rom_device(&s->rom[0], NULL, NULL, NULL, + "imx25.rom0", FSL_IMX25_ROM0_SIZE, &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(get_system_memory(), FSL_IMX25_ROM0_ADDR, + &s->rom[0]); + memory_region_init_rom_device(&s->rom[1], NULL, NULL, NULL, + "imx25.rom1", FSL_IMX25_ROM1_SIZE, &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(get_system_memory(), FSL_IMX25_ROM1_ADDR, + &s->rom[1]); + + /* initialize internal RAM (128 KB) */ + memory_region_init_ram(&s->iram, NULL, "imx25.iram", FSL_IMX25_IRAM_SIZE, + &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(get_system_memory(), FSL_IMX25_IRAM_ADDR, + &s->iram); + vmstate_register_ram_global(&s->iram); + + /* internal RAM (128 KB) is aliased over 128 MB - 128 KB */ + memory_region_init_alias(&s->iram_alias, NULL, "imx25.iram_alias", + &s->iram, 0, FSL_IMX25_IRAM_ALIAS_SIZE); + memory_region_add_subregion(get_system_memory(), FSL_IMX25_IRAM_ALIAS_ADDR, + &s->iram_alias); +} + +static void fsl_imx25_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = fsl_imx25_realize; + + /* + * Reason: creates an ARM CPU, thus use after free(), see + * arm_cpu_class_init() + */ + dc->cannot_destroy_with_object_finalize_yet = true; + dc->desc = "i.MX25 SOC"; +} + +static const TypeInfo fsl_imx25_type_info = { + .name = TYPE_FSL_IMX25, + .parent = TYPE_DEVICE, + .instance_size = sizeof(FslIMX25State), + .instance_init = fsl_imx25_init, + .class_init = fsl_imx25_class_init, +}; + +static void fsl_imx25_register_types(void) +{ + type_register_static(&fsl_imx25_type_info); +} + +type_init(fsl_imx25_register_types) diff --git a/qemu/hw/arm/fsl-imx31.c b/qemu/hw/arm/fsl-imx31.c new file mode 100644 index 000000000..31a3a8791 --- /dev/null +++ b/qemu/hw/arm/fsl-imx31.c @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2013 Jean-Christophe Dubois + * + * i.MX31 SOC emulation. + * + * Based on hw/arm/fsl-imx31.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" +#include "hw/arm/fsl-imx31.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "hw/boards.h" +#include "sysemu/char.h" + +static void fsl_imx31_init(Object *obj) +{ + FslIMX31State *s = FSL_IMX31(obj); + int i; + + object_initialize(&s->cpu, sizeof(s->cpu), "arm1136-" TYPE_ARM_CPU); + + object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC); + qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default()); + + object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX31_CCM); + qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default()); + + for (i = 0; i < FSL_IMX31_NUM_UARTS; i++) { + object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL); + qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default()); + } + + object_initialize(&s->gpt, sizeof(s->gpt), TYPE_IMX_GPT); + qdev_set_parent_bus(DEVICE(&s->gpt), sysbus_get_default()); + + for (i = 0; i < FSL_IMX31_NUM_EPITS; i++) { + object_initialize(&s->epit[i], sizeof(s->epit[i]), TYPE_IMX_EPIT); + qdev_set_parent_bus(DEVICE(&s->epit[i]), sysbus_get_default()); + } + + for (i = 0; i < FSL_IMX31_NUM_I2CS; i++) { + object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C); + qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default()); + } + + for (i = 0; i < FSL_IMX31_NUM_GPIOS; i++) { + object_initialize(&s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO); + qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus_get_default()); + } +} + +static void fsl_imx31_realize(DeviceState *dev, Error **errp) +{ + FslIMX31State *s = FSL_IMX31(dev); + uint16_t i; + Error *err = NULL; + + object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->avic), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->avic), 0, FSL_IMX31_AVIC_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->avic), 0, + qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->avic), 1, + qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ)); + + object_property_set_bool(OBJECT(&s->ccm), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, FSL_IMX31_CCM_ADDR); + + /* Initialize all UARTS */ + for (i = 0; i < FSL_IMX31_NUM_UARTS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } serial_table[FSL_IMX31_NUM_UARTS] = { + { FSL_IMX31_UART1_ADDR, FSL_IMX31_UART1_IRQ }, + { FSL_IMX31_UART2_ADDR, FSL_IMX31_UART2_IRQ }, + }; + + if (i < MAX_SERIAL_PORTS) { + CharDriverState *chr; + + chr = serial_hds[i]; + + if (!chr) { + char label[20]; + snprintf(label, sizeof(label), "imx31.uart%d", i); + chr = qemu_chr_new(label, "null", NULL); + } + + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr); + } + + object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + serial_table[i].irq)); + } + + s->gpt.ccm = IMX_CCM(&s->ccm); + + object_property_set_bool(OBJECT(&s->gpt), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt), 0, FSL_IMX31_GPT_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt), 0, + qdev_get_gpio_in(DEVICE(&s->avic), FSL_IMX31_GPT_IRQ)); + + /* Initialize all EPIT timers */ + for (i = 0; i < FSL_IMX31_NUM_EPITS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } epit_table[FSL_IMX31_NUM_EPITS] = { + { FSL_IMX31_EPIT1_ADDR, FSL_IMX31_EPIT1_IRQ }, + { FSL_IMX31_EPIT2_ADDR, FSL_IMX31_EPIT2_IRQ }, + }; + + s->epit[i].ccm = IMX_CCM(&s->ccm); + + object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->epit[i]), 0, epit_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->epit[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + epit_table[i].irq)); + } + + /* Initialize all I2C */ + for (i = 0; i < FSL_IMX31_NUM_I2CS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } i2c_table[FSL_IMX31_NUM_I2CS] = { + { FSL_IMX31_I2C1_ADDR, FSL_IMX31_I2C1_IRQ }, + { FSL_IMX31_I2C2_ADDR, FSL_IMX31_I2C2_IRQ }, + { FSL_IMX31_I2C3_ADDR, FSL_IMX31_I2C3_IRQ } + }; + + /* Initialize the I2C */ + object_property_set_bool(OBJECT(&s->i2c[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + /* Map I2C memory */ + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, i2c_table[i].addr); + /* Connect I2C IRQ to PIC */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + i2c_table[i].irq)); + } + + /* Initialize all GPIOs */ + for (i = 0; i < FSL_IMX31_NUM_GPIOS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } gpio_table[FSL_IMX31_NUM_GPIOS] = { + { FSL_IMX31_GPIO1_ADDR, FSL_IMX31_GPIO1_IRQ }, + { FSL_IMX31_GPIO2_ADDR, FSL_IMX31_GPIO2_IRQ }, + { FSL_IMX31_GPIO3_ADDR, FSL_IMX31_GPIO3_IRQ } + }; + + object_property_set_bool(OBJECT(&s->gpio[i]), false, "has-edge-sel", + &error_abort); + object_property_set_bool(OBJECT(&s->gpio[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr); + /* Connect GPIO IRQ to PIC */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + gpio_table[i].irq)); + } + + /* On a real system, the first 16k is a `secure boot rom' */ + memory_region_init_rom_device(&s->secure_rom, NULL, NULL, NULL, + "imx31.secure_rom", + FSL_IMX31_SECURE_ROM_SIZE, &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(get_system_memory(), FSL_IMX31_SECURE_ROM_ADDR, + &s->secure_rom); + + /* There is also a 16k ROM */ + memory_region_init_rom_device(&s->rom, NULL, NULL, NULL, "imx31.rom", + FSL_IMX31_ROM_SIZE, &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(get_system_memory(), FSL_IMX31_ROM_ADDR, + &s->rom); + + /* initialize internal RAM (16 KB) */ + memory_region_init_ram(&s->iram, NULL, "imx31.iram", FSL_IMX31_IRAM_SIZE, + &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(get_system_memory(), FSL_IMX31_IRAM_ADDR, + &s->iram); + vmstate_register_ram_global(&s->iram); + + /* internal RAM (16 KB) is aliased over 256 MB - 16 KB */ + memory_region_init_alias(&s->iram_alias, NULL, "imx31.iram_alias", + &s->iram, 0, FSL_IMX31_IRAM_ALIAS_SIZE); + memory_region_add_subregion(get_system_memory(), FSL_IMX31_IRAM_ALIAS_ADDR, + &s->iram_alias); +} + +static void fsl_imx31_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = fsl_imx31_realize; + + /* + * Reason: creates an ARM CPU, thus use after free(), see + * arm_cpu_class_init() + */ + dc->cannot_destroy_with_object_finalize_yet = true; + dc->desc = "i.MX31 SOC"; +} + +static const TypeInfo fsl_imx31_type_info = { + .name = TYPE_FSL_IMX31, + .parent = TYPE_DEVICE, + .instance_size = sizeof(FslIMX31State), + .instance_init = fsl_imx31_init, + .class_init = fsl_imx31_class_init, +}; + +static void fsl_imx31_register_types(void) +{ + type_register_static(&fsl_imx31_type_info); +} + +type_init(fsl_imx31_register_types) diff --git a/qemu/hw/arm/gumstix.c b/qemu/hw/arm/gumstix.c index 8103278b1..d59d9ba4e 100644 --- a/qemu/hw/arm/gumstix.c +++ b/qemu/hw/arm/gumstix.c @@ -34,6 +34,7 @@ * # qemu-system-arm -M verdex -pflash flash -monitor null -nographic -m 289 */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/arm/pxa.h" #include "net/net.h" @@ -121,22 +122,38 @@ static void verdex_init(MachineState *machine) qdev_get_gpio_in(cpu->gpio, 99)); } -static QEMUMachine connex_machine = { - .name = "connex", - .desc = "Gumstix Connex (PXA255)", - .init = connex_init, +static void connex_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Gumstix Connex (PXA255)"; + mc->init = connex_init; +} + +static const TypeInfo connex_type = { + .name = MACHINE_TYPE_NAME("connex"), + .parent = TYPE_MACHINE, + .class_init = connex_class_init, }; -static QEMUMachine verdex_machine = { - .name = "verdex", - .desc = "Gumstix Verdex (PXA270)", - .init = verdex_init, +static void verdex_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Gumstix Verdex (PXA270)"; + mc->init = verdex_init; +} + +static const TypeInfo verdex_type = { + .name = MACHINE_TYPE_NAME("verdex"), + .parent = TYPE_MACHINE, + .class_init = verdex_class_init, }; static void gumstix_machine_init(void) { - qemu_register_machine(&connex_machine); - qemu_register_machine(&verdex_machine); + type_register_static(&connex_type); + type_register_static(&verdex_type); } -machine_init(gumstix_machine_init); +type_init(gumstix_machine_init) diff --git a/qemu/hw/arm/highbank.c b/qemu/hw/arm/highbank.c index f8353a787..d9930c0d3 100644 --- a/qemu/hw/arm/highbank.c +++ b/qemu/hw/arm/highbank.c @@ -17,11 +17,14 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/sysbus.h" #include "hw/arm/arm.h" #include "hw/devices.h" #include "hw/loader.h" #include "net/net.h" +#include "sysemu/kvm.h" #include "sysemu/sysemu.h" #include "hw/boards.h" #include "sysemu/block-backend.h" @@ -32,10 +35,19 @@ #define SMP_BOOT_REG 0x40 #define MPCORE_PERIPHBASE 0xfff10000 +#define MVBAR_ADDR 0x200 +#define BOARD_SETUP_ADDR (MVBAR_ADDR + 8 * sizeof(uint32_t)) + #define NIRQ_GIC 160 /* Board init. */ +static void hb_write_board_setup(ARMCPU *cpu, + const struct arm_boot_info *info) +{ + arm_write_secure_board_setup_dummy_smc(cpu, info, MVBAR_ADDR); +} + static void hb_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info) { int n; @@ -223,52 +235,37 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) MemoryRegion *sysmem; char *sysboot_filename; - if (!cpu_model) { - switch (machine_id) { - case CALXEDA_HIGHBANK: - cpu_model = "cortex-a9"; - break; - case CALXEDA_MIDWAY: - cpu_model = "cortex-a15"; - break; - } + switch (machine_id) { + case CALXEDA_HIGHBANK: + cpu_model = "cortex-a9"; + break; + case CALXEDA_MIDWAY: + cpu_model = "cortex-a15"; + break; } for (n = 0; n < smp_cpus; n++) { ObjectClass *oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model); Object *cpuobj; ARMCPU *cpu; - Error *err = NULL; - - if (!oc) { - error_report("Unable to find CPU definition"); - exit(1); - } cpuobj = object_new(object_class_get_name(oc)); cpu = ARM_CPU(cpuobj); - /* By default A9 and A15 CPUs have EL3 enabled. This board does not - * currently support EL3 so the CPU EL3 property is disabled before - * realization. - */ - if (object_property_find(cpuobj, "has_el3", NULL)) { - object_property_set_bool(cpuobj, false, "has_el3", &err); - if (err) { - error_report_err(err); - exit(1); - } + object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_SMC, + "psci-conduit", &error_abort); + + if (n) { + /* Secondary CPUs start in PSCI powered-down state */ + object_property_set_bool(cpuobj, true, + "start-powered-off", &error_abort); } if (object_property_find(cpuobj, "reset-cbar", NULL)) { object_property_set_int(cpuobj, MPCORE_PERIPHBASE, "reset-cbar", &error_abort); } - object_property_set_bool(cpuobj, true, "realized", &err); - if (err) { - error_report_err(err); - exit(1); - } + object_property_set_bool(cpuobj, true, "realized", &error_fatal); cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ); cpu_fiq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_FIQ); } @@ -281,17 +278,19 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) sysram = g_new(MemoryRegion, 1); memory_region_init_ram(sysram, NULL, "highbank.sysram", 0x8000, - &error_abort); + &error_fatal); memory_region_add_subregion(sysmem, 0xfff88000, sysram); if (bios_name != NULL) { sysboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (sysboot_filename != NULL) { if (load_image_targphys(sysboot_filename, 0xfff88000, 0x8000) < 0) { - hw_error("Unable to load %s\n", bios_name); + error_report("Unable to load %s", bios_name); + exit(1); } g_free(sysboot_filename); } else { - hw_error("Unable to find %s\n", bios_name); + error_report("Unable to find %s", bios_name); + exit(1); } } @@ -378,6 +377,16 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) highbank_binfo.loader_start = 0; highbank_binfo.write_secondary_boot = hb_write_secondary; highbank_binfo.secondary_cpu_reset_hook = hb_reset_secondary; + if (!kvm_enabled()) { + highbank_binfo.board_setup_addr = BOARD_SETUP_ADDR; + highbank_binfo.write_board_setup = hb_write_board_setup; + highbank_binfo.secure_board_setup = true; + } else { + error_report("WARNING: cannot load built-in Monitor support " + "if KVM is enabled. Some guests (such as Linux) " + "may not boot."); + } + arm_load_kernel(ARM_CPU(first_cpu), &highbank_binfo); } @@ -391,26 +400,42 @@ static void midway_init(MachineState *machine) calxeda_init(machine, CALXEDA_MIDWAY); } -static QEMUMachine highbank_machine = { - .name = "highbank", - .desc = "Calxeda Highbank (ECX-1000)", - .init = highbank_init, - .block_default_type = IF_SCSI, - .max_cpus = 4, +static void highbank_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Calxeda Highbank (ECX-1000)"; + mc->init = highbank_init; + mc->block_default_type = IF_SCSI; + mc->max_cpus = 4; +} + +static const TypeInfo highbank_type = { + .name = MACHINE_TYPE_NAME("highbank"), + .parent = TYPE_MACHINE, + .class_init = highbank_class_init, }; -static QEMUMachine midway_machine = { - .name = "midway", - .desc = "Calxeda Midway (ECX-2000)", - .init = midway_init, - .block_default_type = IF_SCSI, - .max_cpus = 4, +static void midway_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Calxeda Midway (ECX-2000)"; + mc->init = midway_init; + mc->block_default_type = IF_SCSI; + mc->max_cpus = 4; +} + +static const TypeInfo midway_type = { + .name = MACHINE_TYPE_NAME("midway"), + .parent = TYPE_MACHINE, + .class_init = midway_class_init, }; static void calxeda_machines_init(void) { - qemu_register_machine(&highbank_machine); - qemu_register_machine(&midway_machine); + type_register_static(&highbank_type); + type_register_static(&midway_type); } -machine_init(calxeda_machines_init); +type_init(calxeda_machines_init) diff --git a/qemu/hw/arm/imx25_pdk.c b/qemu/hw/arm/imx25_pdk.c new file mode 100644 index 000000000..025b60843 --- /dev/null +++ b/qemu/hw/arm/imx25_pdk.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2013 Jean-Christophe Dubois + * + * PDK Board System emulation. + * + * Based on hw/arm/kzm.c + * + * Copyright (c) 2008 OKL and 2011 NICTA + * Written by Hans at OK-Labs + * Updated by Peter Chubb. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" +#include "hw/arm/fsl-imx25.h" +#include "hw/boards.h" +#include "qemu/error-report.h" +#include "exec/address-spaces.h" +#include "sysemu/qtest.h" +#include "hw/i2c/i2c.h" + +/* Memory map for PDK Emulation Baseboard: + * 0x00000000-0x7fffffff See i.MX25 SOC fr support + * 0x80000000-0x87ffffff RAM + Alias EMULATED + * 0x90000000-0x9fffffff RAM + Alias EMULATED + * 0xa0000000-0xa7ffffff Flash IGNORED + * 0xa8000000-0xafffffff Flash IGNORED + * 0xb0000000-0xb1ffffff SRAM IGNORED + * 0xb2000000-0xb3ffffff SRAM IGNORED + * 0xb4000000-0xb5ffffff CS4 IGNORED + * 0xb6000000-0xb8000fff Reserved IGNORED + * 0xb8001000-0xb8001fff SDRAM CTRL reg IGNORED + * 0xb8002000-0xb8002fff WEIM CTRL reg IGNORED + * 0xb8003000-0xb8003fff M3IF CTRL reg IGNORED + * 0xb8004000-0xb8004fff EMI CTRL reg IGNORED + * 0xb8005000-0xbaffffff Reserved IGNORED + * 0xbb000000-0xbb000fff NAND flash area buf IGNORED + * 0xbb001000-0xbb0011ff NAND flash reserved IGNORED + * 0xbb001200-0xbb001dff Reserved IGNORED + * 0xbb001e00-0xbb001fff NAN flash CTRL reg IGNORED + * 0xbb012000-0xbfffffff Reserved IGNORED + * 0xc0000000-0xffffffff Reserved IGNORED + */ + +typedef struct IMX25PDK { + FslIMX25State soc; + MemoryRegion ram; + MemoryRegion ram_alias; +} IMX25PDK; + +static struct arm_boot_info imx25_pdk_binfo; + +static void imx25_pdk_init(MachineState *machine) +{ + IMX25PDK *s = g_new0(IMX25PDK, 1); + unsigned int ram_size; + unsigned int alias_offset; + int i; + + object_initialize(&s->soc, sizeof(s->soc), TYPE_FSL_IMX25); + object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), + &error_abort); + + object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_fatal); + + /* We need to initialize our memory */ + if (machine->ram_size > (FSL_IMX25_SDRAM0_SIZE + FSL_IMX25_SDRAM1_SIZE)) { + error_report("WARNING: RAM size " RAM_ADDR_FMT " above max supported, " + "reduced to %x", machine->ram_size, + FSL_IMX25_SDRAM0_SIZE + FSL_IMX25_SDRAM1_SIZE); + machine->ram_size = FSL_IMX25_SDRAM0_SIZE + FSL_IMX25_SDRAM1_SIZE; + } + + memory_region_allocate_system_memory(&s->ram, NULL, "imx25.ram", + machine->ram_size); + memory_region_add_subregion(get_system_memory(), FSL_IMX25_SDRAM0_ADDR, + &s->ram); + + /* initialize the alias memory if any */ + for (i = 0, ram_size = machine->ram_size, alias_offset = 0; + (i < 2) && ram_size; i++) { + unsigned int size; + static const struct { + hwaddr addr; + unsigned int size; + } ram[2] = { + { FSL_IMX25_SDRAM0_ADDR, FSL_IMX25_SDRAM0_SIZE }, + { FSL_IMX25_SDRAM1_ADDR, FSL_IMX25_SDRAM1_SIZE }, + }; + + size = MIN(ram_size, ram[i].size); + + ram_size -= size; + + if (size < ram[i].size) { + memory_region_init_alias(&s->ram_alias, NULL, "ram.alias", + &s->ram, alias_offset, ram[i].size - size); + memory_region_add_subregion(get_system_memory(), + ram[i].addr + size, &s->ram_alias); + } + + alias_offset += ram[i].size; + } + + imx25_pdk_binfo.ram_size = machine->ram_size; + imx25_pdk_binfo.kernel_filename = machine->kernel_filename; + imx25_pdk_binfo.kernel_cmdline = machine->kernel_cmdline; + imx25_pdk_binfo.initrd_filename = machine->initrd_filename; + imx25_pdk_binfo.loader_start = FSL_IMX25_SDRAM0_ADDR; + imx25_pdk_binfo.board_id = 1771, + imx25_pdk_binfo.nb_cpus = 1; + + /* + * We test explicitly for qtest here as it is not done (yet?) in + * arm_load_kernel(). Without this the "make check" command would + * fail. + */ + if (!qtest_enabled()) { + arm_load_kernel(&s->soc.cpu, &imx25_pdk_binfo); + } else { + /* + * This I2C device doesn't exist on the real board. + * We add it here (only on qtest usage) to be able to do a bit + * of simple qtest. See "make check" for details. + */ + i2c_create_slave((I2CBus *)qdev_get_child_bus(DEVICE(&s->soc.i2c[0]), + "i2c"), + "ds1338", 0x68); + } +} + +static void imx25_pdk_machine_init(MachineClass *mc) +{ + mc->desc = "ARM i.MX25 PDK board (ARM926)"; + mc->init = imx25_pdk_init; +} + +DEFINE_MACHINE("imx25-pdk", imx25_pdk_machine_init) diff --git a/qemu/hw/arm/integratorcp.c b/qemu/hw/arm/integratorcp.c index 0fbbf997e..e31bca6e7 100644 --- a/qemu/hw/arm/integratorcp.c +++ b/qemu/hw/arm/integratorcp.c @@ -7,6 +7,10 @@ * This code is licensed under the GPL */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "hw/devices.h" #include "hw/boards.h" @@ -266,7 +270,7 @@ static int integratorcm_init(SysBusDevice *dev) s->cm_refcnt_offset = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24, 1000); memory_region_init_ram(&s->flash, OBJECT(s), "integrator.flash", 0x100000, - &error_abort); + &error_fatal); vmstate_register_ram_global(&s->flash); memory_region_init_io(&s->iomem, OBJECT(s), &integratorcm_ops, s, @@ -533,7 +537,6 @@ static void integratorcp_init(MachineState *machine) qemu_irq pic[32]; DeviceState *dev, *sic, *icp; int i; - Error *err = NULL; if (!cpu_model) { cpu_model = "arm926"; @@ -552,18 +555,10 @@ static void integratorcp_init(MachineState *machine) * realization. */ if (object_property_find(cpuobj, "has_el3", NULL)) { - object_property_set_bool(cpuobj, false, "has_el3", &err); - if (err) { - error_report_err(err); - exit(1); - } + object_property_set_bool(cpuobj, false, "has_el3", &error_fatal); } - object_property_set_bool(cpuobj, true, "realized", &err); - if (err) { - error_report_err(err); - exit(1); - } + object_property_set_bool(cpuobj, true, "realized", &error_fatal); cpu = ARM_CPU(cpuobj); @@ -619,18 +614,13 @@ static void integratorcp_init(MachineState *machine) arm_load_kernel(cpu, &integrator_binfo); } -static QEMUMachine integratorcp_machine = { - .name = "integratorcp", - .desc = "ARM Integrator/CP (ARM926EJ-S)", - .init = integratorcp_init, -}; - -static void integratorcp_machine_init(void) +static void integratorcp_machine_init(MachineClass *mc) { - qemu_register_machine(&integratorcp_machine); + mc->desc = "ARM Integrator/CP (ARM926EJ-S)"; + mc->init = integratorcp_init; } -machine_init(integratorcp_machine_init); +DEFINE_MACHINE("integratorcp", integratorcp_machine_init) static Property core_properties[] = { DEFINE_PROP_UINT32("memsz", IntegratorCMState, memsz, 0), diff --git a/qemu/hw/arm/kzm.c b/qemu/hw/arm/kzm.c index 5be0369a5..2c96ee33b 100644 --- a/qemu/hw/arm/kzm.c +++ b/qemu/hw/arm/kzm.c @@ -13,141 +13,135 @@ * i.MX31 SoC */ -#include "hw/sysbus.h" +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" +#include "hw/arm/fsl-imx31.h" +#include "hw/boards.h" +#include "qemu/error-report.h" #include "exec/address-spaces.h" -#include "hw/hw.h" -#include "hw/arm/arm.h" -#include "hw/devices.h" #include "net/net.h" -#include "sysemu/sysemu.h" -#include "hw/boards.h" +#include "hw/devices.h" #include "hw/char/serial.h" -#include "hw/arm/imx.h" - - /* Memory map for Kzm Emulation Baseboard: - * 0x00000000-0x00003fff 16k secure ROM IGNORED - * 0x00004000-0x00407fff Reserved IGNORED - * 0x00404000-0x00407fff ROM IGNORED - * 0x00408000-0x0fffffff Reserved IGNORED - * 0x10000000-0x1fffbfff RAM aliasing IGNORED - * 0x1fffc000-0x1fffffff RAM EMULATED - * 0x20000000-0x2fffffff Reserved IGNORED - * 0x30000000-0x7fffffff I.MX31 Internal Register Space - * 0x43f00000 IO_AREA0 - * 0x43f90000 UART1 EMULATED - * 0x43f94000 UART2 EMULATED - * 0x68000000 AVIC EMULATED - * 0x53f80000 CCM EMULATED - * 0x53f94000 PIT 1 EMULATED - * 0x53f98000 PIT 2 EMULATED - * 0x53f90000 GPT EMULATED - * 0x80000000-0x87ffffff RAM EMULATED - * 0x88000000-0x8fffffff RAM Aliasing EMULATED - * 0xa0000000-0xafffffff NAND Flash IGNORED - * 0xb0000000-0xb3ffffff Unavailable IGNORED - * 0xb4000000-0xb4000fff 8-bit free space IGNORED - * 0xb4001000-0xb400100f Board control IGNORED - * 0xb4001003 DIP switch - * 0xb4001010-0xb400101f 7-segment LED IGNORED - * 0xb4001020-0xb400102f LED IGNORED - * 0xb4001030-0xb400103f LED IGNORED - * 0xb4001040-0xb400104f FPGA, UART EMULATED - * 0xb4001050-0xb400105f FPGA, UART EMULATED - * 0xb4001060-0xb40fffff FPGA IGNORED - * 0xb6000000-0xb61fffff LAN controller EMULATED - * 0xb6200000-0xb62fffff FPGA NAND Controller IGNORED - * 0xb6300000-0xb7ffffff Free IGNORED - * 0xb8000000-0xb8004fff Memory control registers IGNORED - * 0xc0000000-0xc3ffffff PCMCIA/CF IGNORED - * 0xc4000000-0xffffffff Reserved IGNORED - */ - -#define KZM_RAMADDRESS (0x80000000) -#define KZM_FPGA (0xb4001040) +#include "sysemu/qtest.h" + +/* Memory map for Kzm Emulation Baseboard: + * 0x00000000-0x7fffffff See i.MX31 SOC for support + * 0x80000000-0x8fffffff RAM EMULATED + * 0x90000000-0x9fffffff RAM EMULATED + * 0xa0000000-0xafffffff Flash IGNORED + * 0xb0000000-0xb3ffffff Unavailable IGNORED + * 0xb4000000-0xb4000fff 8-bit free space IGNORED + * 0xb4001000-0xb400100f Board control IGNORED + * 0xb4001003 DIP switch + * 0xb4001010-0xb400101f 7-segment LED IGNORED + * 0xb4001020-0xb400102f LED IGNORED + * 0xb4001030-0xb400103f LED IGNORED + * 0xb4001040-0xb400104f FPGA, UART EMULATED + * 0xb4001050-0xb400105f FPGA, UART EMULATED + * 0xb4001060-0xb40fffff FPGA IGNORED + * 0xb6000000-0xb61fffff LAN controller EMULATED + * 0xb6200000-0xb62fffff FPGA NAND Controller IGNORED + * 0xb6300000-0xb7ffffff Free IGNORED + * 0xb8000000-0xb8004fff Memory control registers IGNORED + * 0xc0000000-0xc3ffffff PCMCIA/CF IGNORED + * 0xc4000000-0xffffffff Reserved IGNORED + */ + +typedef struct IMX31KZM { + FslIMX31State soc; + MemoryRegion ram; + MemoryRegion ram_alias; +} IMX31KZM; + +#define KZM_RAM_ADDR (FSL_IMX31_SDRAM0_ADDR) +#define KZM_FPGA_ADDR (FSL_IMX31_CS4_ADDR + 0x1040) +#define KZM_LAN9118_ADDR (FSL_IMX31_CS5_ADDR) static struct arm_boot_info kzm_binfo = { - .loader_start = KZM_RAMADDRESS, + .loader_start = KZM_RAM_ADDR, .board_id = 1722, }; static void kzm_init(MachineState *machine) { - ram_addr_t ram_size = machine->ram_size; - const char *cpu_model = machine->cpu_model; - const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; - const char *initrd_filename = machine->initrd_filename; - ARMCPU *cpu; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - MemoryRegion *sram = g_new(MemoryRegion, 1); - MemoryRegion *ram_alias = g_new(MemoryRegion, 1); - DeviceState *dev; - DeviceState *ccm; - - if (!cpu_model) { - cpu_model = "arm1136"; + IMX31KZM *s = g_new0(IMX31KZM, 1); + unsigned int ram_size; + unsigned int alias_offset; + unsigned int i; + + object_initialize(&s->soc, sizeof(s->soc), TYPE_FSL_IMX31); + object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), + &error_abort); + + object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_fatal); + + /* Check the amount of memory is compatible with the SOC */ + if (machine->ram_size > (FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE)) { + error_report("WARNING: RAM size " RAM_ADDR_FMT " above max supported, " + "reduced to %x", machine->ram_size, + FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE); + machine->ram_size = FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE; } - cpu = cpu_arm_init(cpu_model); - if (!cpu) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); + memory_region_allocate_system_memory(&s->ram, NULL, "kzm.ram", + machine->ram_size); + memory_region_add_subregion(get_system_memory(), FSL_IMX31_SDRAM0_ADDR, + &s->ram); + + /* initialize the alias memory if any */ + for (i = 0, ram_size = machine->ram_size, alias_offset = 0; + (i < 2) && ram_size; i++) { + unsigned int size; + static const struct { + hwaddr addr; + unsigned int size; + } ram[2] = { + { FSL_IMX31_SDRAM0_ADDR, FSL_IMX31_SDRAM0_SIZE }, + { FSL_IMX31_SDRAM1_ADDR, FSL_IMX31_SDRAM1_SIZE }, + }; + + size = MIN(ram_size, ram[i].size); + + ram_size -= size; + + if (size < ram[i].size) { + memory_region_init_alias(&s->ram_alias, NULL, "ram.alias", + &s->ram, alias_offset, ram[i].size - size); + memory_region_add_subregion(get_system_memory(), + ram[i].addr + size, &s->ram_alias); + } + + alias_offset += ram[i].size; } - /* On a real system, the first 16k is a `secure boot rom' */ - - memory_region_allocate_system_memory(ram, NULL, "kzm.ram", ram_size); - memory_region_add_subregion(address_space_mem, KZM_RAMADDRESS, ram); - - memory_region_init_alias(ram_alias, NULL, "ram.alias", ram, 0, ram_size); - memory_region_add_subregion(address_space_mem, 0x88000000, ram_alias); - - memory_region_init_ram(sram, NULL, "kzm.sram", 0x4000, &error_abort); - memory_region_add_subregion(address_space_mem, 0x1FFFC000, sram); - - dev = sysbus_create_varargs("imx_avic", 0x68000000, - qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ), - qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_FIQ), - NULL); - - imx_serial_create(0, 0x43f90000, qdev_get_gpio_in(dev, 45)); - imx_serial_create(1, 0x43f94000, qdev_get_gpio_in(dev, 32)); - - ccm = sysbus_create_simple("imx_ccm", 0x53f80000, NULL); - - imx_timerp_create(0x53f94000, qdev_get_gpio_in(dev, 28), ccm); - imx_timerp_create(0x53f98000, qdev_get_gpio_in(dev, 27), ccm); - imx_timerg_create(0x53f90000, qdev_get_gpio_in(dev, 29), ccm); - if (nd_table[0].used) { - lan9118_init(&nd_table[0], 0xb6000000, qdev_get_gpio_in(dev, 52)); + lan9118_init(&nd_table[0], KZM_LAN9118_ADDR, + qdev_get_gpio_in(DEVICE(&s->soc.avic), 52)); } if (serial_hds[2]) { /* touchscreen */ - serial_mm_init(address_space_mem, KZM_FPGA+0x10, 0, - qdev_get_gpio_in(dev, 52), - 14745600, serial_hds[2], - DEVICE_NATIVE_ENDIAN); + serial_mm_init(get_system_memory(), KZM_FPGA_ADDR+0x10, 0, + qdev_get_gpio_in(DEVICE(&s->soc.avic), 52), + 14745600, serial_hds[2], DEVICE_NATIVE_ENDIAN); } - kzm_binfo.ram_size = ram_size; - kzm_binfo.kernel_filename = kernel_filename; - kzm_binfo.kernel_cmdline = kernel_cmdline; - kzm_binfo.initrd_filename = initrd_filename; + kzm_binfo.ram_size = machine->ram_size; + kzm_binfo.kernel_filename = machine->kernel_filename; + kzm_binfo.kernel_cmdline = machine->kernel_cmdline; + kzm_binfo.initrd_filename = machine->initrd_filename; kzm_binfo.nb_cpus = 1; - arm_load_kernel(cpu, &kzm_binfo); -} -static QEMUMachine kzm_machine = { - .name = "kzm", - .desc = "ARM KZM Emulation Baseboard (ARM1136)", - .init = kzm_init, -}; + if (!qtest_enabled()) { + arm_load_kernel(&s->soc.cpu, &kzm_binfo); + } +} -static void kzm_machine_init(void) +static void kzm_machine_init(MachineClass *mc) { - qemu_register_machine(&kzm_machine); + mc->desc = "ARM KZM Emulation Baseboard (ARM1136)"; + mc->init = kzm_init; } -machine_init(kzm_machine_init) +DEFINE_MACHINE("kzm", kzm_machine_init) diff --git a/qemu/hw/arm/mainstone.c b/qemu/hw/arm/mainstone.c index 0da02a67e..454acc5d2 100644 --- a/qemu/hw/arm/mainstone.c +++ b/qemu/hw/arm/mainstone.c @@ -11,6 +11,8 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "hw/arm/pxa.h" #include "hw/arm/arm.h" @@ -124,7 +126,7 @@ static void mainstone_common_init(MemoryRegion *address_space_mem, /* Setup CPU & memory */ mpu = pxa270_init(address_space_mem, mainstone_binfo.ram_size, cpu_model); memory_region_init_ram(rom, NULL, "mainstone.rom", MAINSTONE_ROM, - &error_abort); + &error_fatal); vmstate_register_ram_global(rom); memory_region_set_readonly(rom, true); memory_region_add_subregion(address_space_mem, 0, rom); @@ -188,15 +190,10 @@ static void mainstone_init(MachineState *machine) mainstone_common_init(get_system_memory(), machine, mainstone, 0x196); } -static QEMUMachine mainstone2_machine = { - .name = "mainstone", - .desc = "Mainstone II (PXA27x)", - .init = mainstone_init, -}; - -static void mainstone_machine_init(void) +static void mainstone2_machine_init(MachineClass *mc) { - qemu_register_machine(&mainstone2_machine); + mc->desc = "Mainstone II (PXA27x)"; + mc->init = mainstone_init; } -machine_init(mainstone_machine_init); +DEFINE_MACHINE("mainstone", mainstone2_machine_init) diff --git a/qemu/hw/arm/musicpal.c b/qemu/hw/arm/musicpal.c index 42f66b33e..7a4cc07dd 100644 --- a/qemu/hw/arm/musicpal.c +++ b/qemu/hw/arm/musicpal.c @@ -9,6 +9,10 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "hw/arm/arm.h" #include "hw/devices.h" @@ -1599,7 +1603,7 @@ static void musicpal_init(MachineState *machine) memory_region_add_subregion(address_space_mem, 0, ram); memory_region_init_ram(sram, NULL, "musicpal.sram", MP_SRAM_SIZE, - &error_abort); + &error_fatal); vmstate_register_ram_global(sram); memory_region_add_subregion(address_space_mem, MP_SRAM_BASE, sram); @@ -1709,18 +1713,13 @@ static void musicpal_init(MachineState *machine) arm_load_kernel(cpu, &musicpal_binfo); } -static QEMUMachine musicpal_machine = { - .name = "musicpal", - .desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)", - .init = musicpal_init, -}; - -static void musicpal_machine_init(void) +static void musicpal_machine_init(MachineClass *mc) { - qemu_register_machine(&musicpal_machine); + mc->desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)"; + mc->init = musicpal_init; } -machine_init(musicpal_machine_init); +DEFINE_MACHINE("musicpal", musicpal_machine_init) static void mv88w8618_wlan_class_init(ObjectClass *klass, void *data) { diff --git a/qemu/hw/arm/netduino2.c b/qemu/hw/arm/netduino2.c index 8f26780ef..23d792837 100644 --- a/qemu/hw/arm/netduino2.c +++ b/qemu/hw/arm/netduino2.c @@ -22,6 +22,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/boards.h" #include "qemu/error-report.h" #include "hw/arm/stm32f205_soc.h" @@ -29,29 +31,19 @@ static void netduino2_init(MachineState *machine) { DeviceState *dev; - Error *err = NULL; dev = qdev_create(NULL, TYPE_STM32F205_SOC); if (machine->kernel_filename) { qdev_prop_set_string(dev, "kernel-filename", machine->kernel_filename); } qdev_prop_set_string(dev, "cpu-model", "cortex-m3"); - object_property_set_bool(OBJECT(dev), true, "realized", &err); - if (err != NULL) { - error_report("%s", error_get_pretty(err)); - exit(1); - } + object_property_set_bool(OBJECT(dev), true, "realized", &error_fatal); } -static QEMUMachine netduino2_machine = { - .name = "netduino2", - .desc = "Netduino 2 Machine", - .init = netduino2_init, -}; - -static void netduino2_machine_init(void) +static void netduino2_machine_init(MachineClass *mc) { - qemu_register_machine(&netduino2_machine); + mc->desc = "Netduino 2 Machine"; + mc->init = netduino2_init; } -machine_init(netduino2_machine_init); +DEFINE_MACHINE("netduino2", netduino2_machine_init) diff --git a/qemu/hw/arm/nseries.c b/qemu/hw/arm/nseries.c index a659e8525..538250555 100644 --- a/qemu/hw/arm/nseries.c +++ b/qemu/hw/arm/nseries.c @@ -18,7 +18,9 @@ * with this program; if not, see . */ -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/cutils.h" #include "sysemu/sysemu.h" #include "hw/arm/omap.h" #include "hw/arm/arm.h" @@ -172,8 +174,8 @@ static void n8x0_nand_setup(struct n800_s *s) qdev_prop_set_int32(s->nand, "shift", 1); dinfo = drive_get(IF_MTD, 0, 0); if (dinfo) { - qdev_prop_set_drive_nofail(s->nand, "drive", - blk_by_legacy_dinfo(dinfo)); + qdev_prop_set_drive(s->nand, "drive", blk_by_legacy_dinfo(dinfo), + &error_fatal); } qdev_init_nofail(s->nand); sysbus_connect_irq(SYS_BUS_DEVICE(s->nand), 0, @@ -1275,7 +1277,7 @@ static int n8x0_atag_setup(void *p, int model) strcpy((void *) w, "hw-build"); /* char component[12] */ w += 6; strcpy((void *) w, "QEMU "); - pstrcat((void *) w, 12, qemu_get_version()); /* char version[12] */ + pstrcat((void *) w, 12, qemu_hw_version()); /* char version[12] */ w += 6; tag = (model == 810) ? "1.1.10-qemu" : "1.1.6-qemu"; @@ -1413,24 +1415,40 @@ static void n810_init(MachineState *machine) n8x0_init(machine, &n810_binfo, 810); } -static QEMUMachine n800_machine = { - .name = "n800", - .desc = "Nokia N800 tablet aka. RX-34 (OMAP2420)", - .init = n800_init, - .default_boot_order = "", +static void n800_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Nokia N800 tablet aka. RX-34 (OMAP2420)"; + mc->init = n800_init; + mc->default_boot_order = ""; +} + +static const TypeInfo n800_type = { + .name = MACHINE_TYPE_NAME("n800"), + .parent = TYPE_MACHINE, + .class_init = n800_class_init, }; -static QEMUMachine n810_machine = { - .name = "n810", - .desc = "Nokia N810 tablet aka. RX-44 (OMAP2420)", - .init = n810_init, - .default_boot_order = "", +static void n810_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Nokia N810 tablet aka. RX-44 (OMAP2420)"; + mc->init = n810_init; + mc->default_boot_order = ""; +} + +static const TypeInfo n810_type = { + .name = MACHINE_TYPE_NAME("n810"), + .parent = TYPE_MACHINE, + .class_init = n810_class_init, }; static void nseries_machine_init(void) { - qemu_register_machine(&n800_machine); - qemu_register_machine(&n810_machine); + type_register_static(&n800_type); + type_register_static(&n810_type); } -machine_init(nseries_machine_init); +type_init(nseries_machine_init) diff --git a/qemu/hw/arm/omap1.c b/qemu/hw/arm/omap1.c index de2b28925..b3cf0ec69 100644 --- a/qemu/hw/arm/omap1.c +++ b/qemu/hw/arm/omap1.c @@ -17,6 +17,10 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/boards.h" #include "hw/hw.h" #include "hw/arm/arm.h" @@ -27,6 +31,8 @@ #include "sysemu/blockdev.h" #include "qemu/range.h" #include "hw/sysbus.h" +#include "qemu/cutils.h" +#include "qemu/bcd.h" /* Should signal the TCMI/GPMC */ uint32_t omap_badwidth_read8(void *opaque, hwaddr addr) @@ -106,7 +112,7 @@ static inline uint32_t omap_timer_read(struct omap_mpu_timer_s *timer) if (timer->st && timer->enable && timer->rate) return timer->val - muldiv64(distance >> (timer->ptv + 1), - timer->rate, get_ticks_per_sec()); + timer->rate, NANOSECONDS_PER_SECOND); else return timer->val; } @@ -124,7 +130,7 @@ static inline void omap_timer_update(struct omap_mpu_timer_s *timer) if (timer->enable && timer->st && timer->rate) { timer->val = timer->reset_val; /* Should skip this on clk enable */ expires = muldiv64((uint64_t) timer->val << (timer->ptv + 1), - get_ticks_per_sec(), timer->rate); + NANOSECONDS_PER_SECOND, timer->rate); /* If timer expiry would be sooner than in about 1 ms and * auto-reload isn't set, then fire immediately. This is a hack @@ -132,10 +138,11 @@ static inline void omap_timer_update(struct omap_mpu_timer_s *timer) * sets the interval to a very low value and polls the status bit * in a busy loop when it wants to sleep just a couple of CPU * ticks. */ - if (expires > (get_ticks_per_sec() >> 10) || timer->ar) + if (expires > (NANOSECONDS_PER_SECOND >> 10) || timer->ar) { timer_mod(timer->timer, timer->time + expires); - else + } else { qemu_bh_schedule(timer->tick); + } } else timer_del(timer->timer); } @@ -258,8 +265,7 @@ static struct omap_mpu_timer_s *omap_mpu_timer_init(MemoryRegion *system_memory, hwaddr base, qemu_irq irq, omap_clk clk) { - struct omap_mpu_timer_s *s = (struct omap_mpu_timer_s *) - g_malloc0(sizeof(struct omap_mpu_timer_s)); + struct omap_mpu_timer_s *s = g_new0(struct omap_mpu_timer_s, 1); s->irq = irq; s->clk = clk; @@ -388,8 +394,7 @@ static struct omap_watchdog_timer_s *omap_wd_timer_init(MemoryRegion *memory, hwaddr base, qemu_irq irq, omap_clk clk) { - struct omap_watchdog_timer_s *s = (struct omap_watchdog_timer_s *) - g_malloc0(sizeof(struct omap_watchdog_timer_s)); + struct omap_watchdog_timer_s *s = g_new0(struct omap_watchdog_timer_s, 1); s->timer.irq = irq; s->timer.clk = clk; @@ -495,8 +500,7 @@ static struct omap_32khz_timer_s *omap_os_timer_init(MemoryRegion *memory, hwaddr base, qemu_irq irq, omap_clk clk) { - struct omap_32khz_timer_s *s = (struct omap_32khz_timer_s *) - g_malloc0(sizeof(struct omap_32khz_timer_s)); + struct omap_32khz_timer_s *s = g_new0(struct omap_32khz_timer_s, 1); s->timer.irq = irq; s->timer.clk = clk; @@ -615,14 +619,14 @@ static void omap_ulpd_pm_write(void *opaque, hwaddr addr, now -= s->ulpd_gauge_start; /* 32-kHz ticks */ - ticks = muldiv64(now, 32768, get_ticks_per_sec()); + ticks = muldiv64(now, 32768, NANOSECONDS_PER_SECOND); s->ulpd_pm_regs[0x00 >> 2] = (ticks >> 0) & 0xffff; s->ulpd_pm_regs[0x04 >> 2] = (ticks >> 16) & 0xffff; if (ticks >> 32) /* OVERFLOW_32K */ s->ulpd_pm_regs[0x14 >> 2] |= 1 << 2; /* High frequency ticks */ - ticks = muldiv64(now, 12000000, get_ticks_per_sec()); + ticks = muldiv64(now, 12000000, NANOSECONDS_PER_SECOND); s->ulpd_pm_regs[0x08 >> 2] = (ticks >> 0) & 0xffff; s->ulpd_pm_regs[0x0c >> 2] = (ticks >> 16) & 0xffff; if (ticks >> 32) /* OVERFLOW_HI_FREQ */ @@ -1236,8 +1240,7 @@ static struct omap_tipb_bridge_s *omap_tipb_bridge_init( MemoryRegion *memory, hwaddr base, qemu_irq abort_irq, omap_clk clk) { - struct omap_tipb_bridge_s *s = (struct omap_tipb_bridge_s *) - g_malloc0(sizeof(struct omap_tipb_bridge_s)); + struct omap_tipb_bridge_s *s = g_new0(struct omap_tipb_bridge_s, 1); s->abort = abort_irq; omap_tipb_bridge_reset(s); @@ -2099,8 +2102,7 @@ static struct omap_mpuio_s *omap_mpuio_init(MemoryRegion *memory, qemu_irq kbd_int, qemu_irq gpio_int, qemu_irq wakeup, omap_clk clk) { - struct omap_mpuio_s *s = (struct omap_mpuio_s *) - g_malloc0(sizeof(struct omap_mpuio_s)); + struct omap_mpuio_s *s = g_new0(struct omap_mpuio_s, 1); s->irq = gpio_int; s->kbd_irq = kbd_int; @@ -2292,8 +2294,7 @@ static struct omap_uwire_s *omap_uwire_init(MemoryRegion *system_memory, qemu_irq dma, omap_clk clk) { - struct omap_uwire_s *s = (struct omap_uwire_s *) - g_malloc0(sizeof(struct omap_uwire_s)); + struct omap_uwire_s *s = g_new0(struct omap_uwire_s, 1); s->txirq = txirq; s->rxirq = rxirq; @@ -2932,8 +2933,7 @@ static struct omap_rtc_s *omap_rtc_init(MemoryRegion *system_memory, qemu_irq timerirq, qemu_irq alarmirq, omap_clk clk) { - struct omap_rtc_s *s = (struct omap_rtc_s *) - g_malloc0(sizeof(struct omap_rtc_s)); + struct omap_rtc_s *s = g_new0(struct omap_rtc_s, 1); s->irq = timerirq; s->alarm = alarmirq; @@ -3032,7 +3032,7 @@ static void omap_mcbsp_source_tick(void *opaque) omap_mcbsp_rx_newdata(s); timer_mod(s->source_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - get_ticks_per_sec()); + NANOSECONDS_PER_SECOND); } static void omap_mcbsp_rx_start(struct omap_mcbsp_s *s) @@ -3078,7 +3078,7 @@ static void omap_mcbsp_sink_tick(void *opaque) omap_mcbsp_tx_newdata(s); timer_mod(s->sink_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - get_ticks_per_sec()); + NANOSECONDS_PER_SECOND); } static void omap_mcbsp_tx_start(struct omap_mcbsp_s *s) @@ -3468,8 +3468,7 @@ static struct omap_mcbsp_s *omap_mcbsp_init(MemoryRegion *system_memory, qemu_irq txirq, qemu_irq rxirq, qemu_irq *dma, omap_clk clk) { - struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) - g_malloc0(sizeof(struct omap_mcbsp_s)); + struct omap_mcbsp_s *s = g_new0(struct omap_mcbsp_s, 1); s->txirq = txirq; s->rxirq = rxirq; @@ -3648,8 +3647,7 @@ static void omap_lpg_clk_update(void *opaque, int line, int on) static struct omap_lpg_s *omap_lpg_init(MemoryRegion *system_memory, hwaddr base, omap_clk clk) { - struct omap_lpg_s *s = (struct omap_lpg_s *) - g_malloc0(sizeof(struct omap_lpg_s)); + struct omap_lpg_s *s = g_new0(struct omap_lpg_s, 1); s->tm = timer_new_ms(QEMU_CLOCK_VIRTUAL, omap_lpg_tick, s); @@ -3853,8 +3851,7 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory, const char *core) { int i; - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) - g_malloc0(sizeof(struct omap_mpu_state_s)); + struct omap_mpu_state_s *s = g_new0(struct omap_mpu_state_s, 1); qemu_irq dma_irqs[6]; DriveInfo *dinfo; SysBusDevice *busdev; @@ -3882,7 +3879,7 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory, s->sdram_size); memory_region_add_subregion(system_memory, OMAP_EMIFF_BASE, &s->emiff_ram); memory_region_init_ram(&s->imif_ram, NULL, "omap1.sram", s->sram_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(&s->imif_ram); memory_region_add_subregion(system_memory, OMAP_IMIF_BASE, &s->imif_ram); diff --git a/qemu/hw/arm/omap2.c b/qemu/hw/arm/omap2.c index e39b31729..3a0d77714 100644 --- a/qemu/hw/arm/omap2.c +++ b/qemu/hw/arm/omap2.c @@ -18,6 +18,10 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/boards.h" @@ -596,8 +600,7 @@ static const MemoryRegionOps omap_eac_ops = { static struct omap_eac_s *omap_eac_init(struct omap_target_agent_s *ta, qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk) { - struct omap_eac_s *s = (struct omap_eac_s *) - g_malloc0(sizeof(struct omap_eac_s)); + struct omap_eac_s *s = g_new0(struct omap_eac_s, 1); s->irq = irq; s->codec.rxdrq = *drq ++; @@ -788,8 +791,7 @@ static struct omap_sti_s *omap_sti_init(struct omap_target_agent_s *ta, hwaddr channel_base, qemu_irq irq, omap_clk clk, CharDriverState *chr) { - struct omap_sti_s *s = (struct omap_sti_s *) - g_malloc0(sizeof(struct omap_sti_s)); + struct omap_sti_s *s = g_new0(struct omap_sti_s, 1); s->irq = irq; omap_sti_reset(s); @@ -1806,8 +1808,7 @@ static struct omap_prcm_s *omap_prcm_init(struct omap_target_agent_s *ta, qemu_irq mpu_int, qemu_irq dsp_int, qemu_irq iva_int, struct omap_mpu_state_s *mpu) { - struct omap_prcm_s *s = (struct omap_prcm_s *) - g_malloc0(sizeof(struct omap_prcm_s)); + struct omap_prcm_s *s = g_new0(struct omap_prcm_s, 1); s->irq[0] = mpu_int; s->irq[1] = dsp_int; @@ -2185,8 +2186,7 @@ static void omap_sysctl_reset(struct omap_sysctl_s *s) static struct omap_sysctl_s *omap_sysctl_init(struct omap_target_agent_s *ta, omap_clk iclk, struct omap_mpu_state_s *mpu) { - struct omap_sysctl_s *s = (struct omap_sysctl_s *) - g_malloc0(sizeof(struct omap_sysctl_s)); + struct omap_sysctl_s *s = g_new0(struct omap_sysctl_s, 1); s->mpu = mpu; omap_sysctl_reset(s); @@ -2248,8 +2248,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, unsigned long sdram_size, const char *core) { - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) - g_malloc0(sizeof(struct omap_mpu_state_s)); + struct omap_mpu_state_s *s = g_new0(struct omap_mpu_state_s, 1); qemu_irq dma_irqs[4]; DriveInfo *dinfo; int i; @@ -2276,7 +2275,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, s->sdram_size); memory_region_add_subregion(sysmem, OMAP2_Q2_BASE, &s->sdram); memory_region_init_ram(&s->sram, NULL, "omap2.sram", s->sram_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(&s->sram); memory_region_add_subregion(sysmem, OMAP2_SRAM_BASE, &s->sram); diff --git a/qemu/hw/arm/omap_sx1.c b/qemu/hw/arm/omap_sx1.c index 4b0f7f9c4..5d74026cb 100644 --- a/qemu/hw/arm/omap_sx1.c +++ b/qemu/hw/arm/omap_sx1.c @@ -25,6 +25,8 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "ui/console.h" #include "hw/arm/omap.h" @@ -122,7 +124,7 @@ static void sx1_init(MachineState *machine, const int version) /* External Flash (EMIFS) */ memory_region_init_ram(flash, NULL, "omap_sx1.flash0-0", flash_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(flash); memory_region_set_readonly(flash, true); memory_region_add_subregion(address_space, OMAP_CS0_BASE, flash); @@ -166,7 +168,7 @@ static void sx1_init(MachineState *machine, const int version) (dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) { MemoryRegion *flash_1 = g_new(MemoryRegion, 1); memory_region_init_ram(flash_1, NULL, "omap_sx1.flash1-0", flash1_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(flash_1); memory_region_set_readonly(flash_1, true); memory_region_add_subregion(address_space, OMAP_CS1_BASE, flash_1); @@ -217,22 +219,38 @@ static void sx1_init_v2(MachineState *machine) sx1_init(machine, 2); } -static QEMUMachine sx1_machine_v2 = { - .name = "sx1", - .desc = "Siemens SX1 (OMAP310) V2", - .init = sx1_init_v2, +static void sx1_machine_v2_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Siemens SX1 (OMAP310) V2"; + mc->init = sx1_init_v2; +} + +static const TypeInfo sx1_machine_v2_type = { + .name = MACHINE_TYPE_NAME("sx1"), + .parent = TYPE_MACHINE, + .class_init = sx1_machine_v2_class_init, }; -static QEMUMachine sx1_machine_v1 = { - .name = "sx1-v1", - .desc = "Siemens SX1 (OMAP310) V1", - .init = sx1_init_v1, +static void sx1_machine_v1_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Siemens SX1 (OMAP310) V1"; + mc->init = sx1_init_v1; +} + +static const TypeInfo sx1_machine_v1_type = { + .name = MACHINE_TYPE_NAME("sx1-v1"), + .parent = TYPE_MACHINE, + .class_init = sx1_machine_v1_class_init, }; static void sx1_machine_init(void) { - qemu_register_machine(&sx1_machine_v2); - qemu_register_machine(&sx1_machine_v1); + type_register_static(&sx1_machine_v1_type); + type_register_static(&sx1_machine_v2_type); } -machine_init(sx1_machine_init); +type_init(sx1_machine_init) diff --git a/qemu/hw/arm/palm.c b/qemu/hw/arm/palm.c index 7f1cfb8f6..7f460732e 100644 --- a/qemu/hw/arm/palm.c +++ b/qemu/hw/arm/palm.c @@ -16,6 +16,8 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "audio/audio.h" #include "sysemu/sysemu.h" @@ -213,7 +215,7 @@ static void palmte_init(MachineState *machine) /* External Flash (EMIFS) */ memory_region_init_ram(flash, NULL, "palmte.flash", flash_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(flash); memory_region_set_readonly(flash, true); memory_region_add_subregion(address_space_mem, OMAP_CS0_BASE, flash); @@ -269,15 +271,10 @@ static void palmte_init(MachineState *machine) arm_load_kernel(mpu->cpu, &palmte_binfo); } -static QEMUMachine palmte_machine = { - .name = "cheetah", - .desc = "Palm Tungsten|E aka. Cheetah PDA (OMAP310)", - .init = palmte_init, -}; - -static void palmte_machine_init(void) +static void palmte_machine_init(MachineClass *mc) { - qemu_register_machine(&palmte_machine); + mc->desc = "Palm Tungsten|E aka. Cheetah PDA (OMAP310)"; + mc->init = palmte_init; } -machine_init(palmte_machine_init); +DEFINE_MACHINE("cheetah", palmte_machine_init) diff --git a/qemu/hw/arm/palmetto-bmc.c b/qemu/hw/arm/palmetto-bmc.c new file mode 100644 index 000000000..89ebd92b9 --- /dev/null +++ b/qemu/hw/arm/palmetto-bmc.c @@ -0,0 +1,68 @@ +/* + * OpenPOWER Palmetto BMC + * + * Andrew Jeffery + * + * Copyright 2016 IBM Corp. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" +#include "exec/address-spaces.h" +#include "hw/arm/arm.h" +#include "hw/arm/ast2400.h" +#include "hw/boards.h" + +static struct arm_boot_info palmetto_bmc_binfo = { + .loader_start = AST2400_SDRAM_BASE, + .board_id = 0, + .nb_cpus = 1, +}; + +typedef struct PalmettoBMCState { + AST2400State soc; + MemoryRegion ram; +} PalmettoBMCState; + +static void palmetto_bmc_init(MachineState *machine) +{ + PalmettoBMCState *bmc; + + bmc = g_new0(PalmettoBMCState, 1); + object_initialize(&bmc->soc, (sizeof(bmc->soc)), TYPE_AST2400); + object_property_add_child(OBJECT(machine), "soc", OBJECT(&bmc->soc), + &error_abort); + + memory_region_allocate_system_memory(&bmc->ram, NULL, "ram", ram_size); + memory_region_add_subregion(get_system_memory(), AST2400_SDRAM_BASE, + &bmc->ram); + object_property_add_const_link(OBJECT(&bmc->soc), "ram", OBJECT(&bmc->ram), + &error_abort); + object_property_set_bool(OBJECT(&bmc->soc), true, "realized", + &error_abort); + + palmetto_bmc_binfo.kernel_filename = machine->kernel_filename; + palmetto_bmc_binfo.initrd_filename = machine->initrd_filename; + palmetto_bmc_binfo.kernel_cmdline = machine->kernel_cmdline; + palmetto_bmc_binfo.ram_size = ram_size; + arm_load_kernel(ARM_CPU(first_cpu), &palmetto_bmc_binfo); +} + +static void palmetto_bmc_machine_init(MachineClass *mc) +{ + mc->desc = "OpenPOWER Palmetto BMC"; + mc->init = palmetto_bmc_init; + mc->max_cpus = 1; + mc->no_sdcard = 1; + mc->no_floppy = 1; + mc->no_cdrom = 1; + mc->no_sdcard = 1; + mc->no_parallel = 1; +} + +DEFINE_MACHINE("palmetto-bmc", palmetto_bmc_machine_init); diff --git a/qemu/hw/arm/pxa2xx.c b/qemu/hw/arm/pxa2xx.c index ec353f79c..1a8c36033 100644 --- a/qemu/hw/arm/pxa2xx.c +++ b/qemu/hw/arm/pxa2xx.c @@ -7,15 +7,20 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "hw/arm/pxa.h" #include "sysemu/sysemu.h" #include "hw/char/serial.h" #include "hw/i2c/i2c.h" -#include "hw/ssi.h" +#include "hw/ssi/ssi.h" #include "sysemu/char.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" +#include "qemu/cutils.h" static struct { hwaddr io_base; @@ -1731,8 +1736,7 @@ static PXA2xxI2SState *pxa2xx_i2s_init(MemoryRegion *sysmem, hwaddr base, qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma) { - PXA2xxI2SState *s = (PXA2xxI2SState *) - g_malloc0(sizeof(PXA2xxI2SState)); + PXA2xxI2SState *s = g_new0(PXA2xxI2SState, 1); s->irq = irq; s->rx_dma = rx_dma; @@ -1959,7 +1963,7 @@ static void pxa2xx_fir_instance_init(Object *obj) PXA2xxFIrState *s = PXA2XX_FIR(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->iomem, NULL, &pxa2xx_fir_ops, s, + memory_region_init_io(&s->iomem, obj, &pxa2xx_fir_ops, s, "pxa2xx-fir", 0x1000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); @@ -2061,7 +2065,7 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, PXA2xxState *s; int i; DriveInfo *dinfo; - s = (PXA2xxState *) g_malloc0(sizeof(PXA2xxState)); + s = g_new0(PXA2xxState, 1); if (revision && strncmp(revision, "pxa27", 5)) { fprintf(stderr, "Machine requires a PXA27x processor.\n"); @@ -2079,11 +2083,11 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, /* SDRAM & Internal Memory Storage */ memory_region_init_ram(&s->sdram, NULL, "pxa270.sdram", sdram_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(&s->sdram); memory_region_add_subregion(address_space, PXA2XX_SDRAM_BASE, &s->sdram); memory_region_init_ram(&s->internal, NULL, "pxa270.internal", 0x40000, - &error_abort); + &error_fatal); vmstate_register_ram_global(&s->internal); memory_region_add_subregion(address_space, PXA2XX_INTERNAL_BASE, &s->internal); @@ -2157,7 +2161,7 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, vmstate_register(NULL, 0, &vmstate_pxa2xx_pm, s); for (i = 0; pxa27x_ssp[i].io_base; i ++); - s->ssp = (SSIBus **)g_malloc0(sizeof(SSIBus *) * i); + s->ssp = g_new0(SSIBus *, i); for (i = 0; pxa27x_ssp[i].io_base; i ++) { DeviceState *dev; dev = sysbus_create_simple(TYPE_PXA2XX_SSP, pxa27x_ssp[i].io_base, @@ -2202,7 +2206,7 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) int i; DriveInfo *dinfo; - s = (PXA2xxState *) g_malloc0(sizeof(PXA2xxState)); + s = g_new0(PXA2xxState, 1); s->cpu = cpu_arm_init("pxa255"); if (s->cpu == NULL) { @@ -2213,11 +2217,11 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) /* SDRAM & Internal Memory Storage */ memory_region_init_ram(&s->sdram, NULL, "pxa255.sdram", sdram_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(&s->sdram); memory_region_add_subregion(address_space, PXA2XX_SDRAM_BASE, &s->sdram); memory_region_init_ram(&s->internal, NULL, "pxa255.internal", - PXA2XX_INTERNAL_SIZE, &error_abort); + PXA2XX_INTERNAL_SIZE, &error_fatal); vmstate_register_ram_global(&s->internal); memory_region_add_subregion(address_space, PXA2XX_INTERNAL_BASE, &s->internal); @@ -2290,7 +2294,7 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) vmstate_register(NULL, 0, &vmstate_pxa2xx_pm, s); for (i = 0; pxa255_ssp[i].io_base; i ++); - s->ssp = (SSIBus **)g_malloc0(sizeof(SSIBus *) * i); + s->ssp = g_new0(SSIBus *, i); for (i = 0; pxa255_ssp[i].io_base; i ++) { DeviceState *dev; dev = sysbus_create_simple(TYPE_PXA2XX_SSP, pxa255_ssp[i].io_base, diff --git a/qemu/hw/arm/pxa2xx_gpio.c b/qemu/hw/arm/pxa2xx_gpio.c index c89c8045c..67e7e7094 100644 --- a/qemu/hw/arm/pxa2xx_gpio.c +++ b/qemu/hw/arm/pxa2xx_gpio.c @@ -7,6 +7,7 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "hw/arm/pxa.h" diff --git a/qemu/hw/arm/pxa2xx_pic.c b/qemu/hw/arm/pxa2xx_pic.c index d41ac9341..7e51532cd 100644 --- a/qemu/hw/arm/pxa2xx_pic.c +++ b/qemu/hw/arm/pxa2xx_pic.c @@ -8,6 +8,9 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/arm/pxa.h" #include "hw/sysbus.h" diff --git a/qemu/hw/arm/raspi.c b/qemu/hw/arm/raspi.c new file mode 100644 index 000000000..2b295f14c --- /dev/null +++ b/qemu/hw/arm/raspi.c @@ -0,0 +1,172 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous + * + * Rasperry Pi 2 emulation Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * This code is licensed under the GNU GPLv2 and later. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" +#include "hw/arm/bcm2836.h" +#include "qemu/error-report.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/arm/arm.h" +#include "sysemu/sysemu.h" + +#define SMPBOOT_ADDR 0x300 /* this should leave enough space for ATAGS */ +#define MVBAR_ADDR 0x400 /* secure vectors */ +#define BOARDSETUP_ADDR (MVBAR_ADDR + 0x20) /* board setup code */ +#define FIRMWARE_ADDR 0x8000 /* Pi loads kernel.img here by default */ + +/* Table of Linux board IDs for different Pi versions */ +static const int raspi_boardid[] = {[1] = 0xc42, [2] = 0xc43}; + +typedef struct RasPiState { + BCM2836State soc; + MemoryRegion ram; +} RasPiState; + +static void write_smpboot(ARMCPU *cpu, const struct arm_boot_info *info) +{ + static const uint32_t smpboot[] = { + 0xe1a0e00f, /* mov lr, pc */ + 0xe3a0fe00 + (BOARDSETUP_ADDR >> 4), /* mov pc, BOARDSETUP_ADDR */ + 0xee100fb0, /* mrc p15, 0, r0, c0, c0, 5;get core ID */ + 0xe7e10050, /* ubfx r0, r0, #0, #2 ;extract LSB */ + 0xe59f5014, /* ldr r5, =0x400000CC ;load mbox base */ + 0xe320f001, /* 1: yield */ + 0xe7953200, /* ldr r3, [r5, r0, lsl #4] ;read mbox for our core*/ + 0xe3530000, /* cmp r3, #0 ;spin while zero */ + 0x0afffffb, /* beq 1b */ + 0xe7853200, /* str r3, [r5, r0, lsl #4] ;clear mbox */ + 0xe12fff13, /* bx r3 ;jump to target */ + 0x400000cc, /* (constant: mailbox 3 read/clear base) */ + }; + + /* check that we don't overrun board setup vectors */ + QEMU_BUILD_BUG_ON(SMPBOOT_ADDR + sizeof(smpboot) > MVBAR_ADDR); + /* check that board setup address is correctly relocated */ + QEMU_BUILD_BUG_ON((BOARDSETUP_ADDR & 0xf) != 0 + || (BOARDSETUP_ADDR >> 4) >= 0x100); + + rom_add_blob_fixed("raspi_smpboot", smpboot, sizeof(smpboot), + info->smp_loader_start); +} + +static void write_board_setup(ARMCPU *cpu, const struct arm_boot_info *info) +{ + arm_write_secure_board_setup_dummy_smc(cpu, info, MVBAR_ADDR); +} + +static void reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info) +{ + CPUState *cs = CPU(cpu); + cpu_set_pc(cs, info->smp_loader_start); +} + +static void setup_boot(MachineState *machine, int version, size_t ram_size) +{ + static struct arm_boot_info binfo; + int r; + + 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; + + /* Pi2 requires SMP setup */ + if (version == 2) { + binfo.smp_loader_start = SMPBOOT_ADDR; + binfo.write_secondary_boot = write_smpboot; + binfo.secondary_cpu_reset_hook = reset_secondary; + } + + /* If the user specified a "firmware" image (e.g. UEFI), we bypass + * the normal Linux boot process + */ + if (machine->firmware) { + /* load the firmware image (typically kernel.img) */ + r = load_image_targphys(machine->firmware, FIRMWARE_ADDR, + ram_size - FIRMWARE_ADDR); + if (r < 0) { + error_report("Failed to load firmware from %s", machine->firmware); + exit(1); + } + + binfo.entry = FIRMWARE_ADDR; + binfo.firmware_loaded = true; + } else { + binfo.kernel_filename = machine->kernel_filename; + binfo.kernel_cmdline = machine->kernel_cmdline; + binfo.initrd_filename = machine->initrd_filename; + } + + arm_load_kernel(ARM_CPU(first_cpu), &binfo); +} + +static void raspi2_init(MachineState *machine) +{ + RasPiState *s = g_new0(RasPiState, 1); + uint32_t vcram_size; + DriveInfo *di; + BlockBackend *blk; + BusState *bus; + DeviceState *carddev; + + object_initialize(&s->soc, sizeof(s->soc), TYPE_BCM2836); + object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), + &error_abort); + + /* Allocate and map RAM */ + memory_region_allocate_system_memory(&s->ram, OBJECT(machine), "ram", + machine->ram_size); + /* FIXME: Remove when we have custom CPU address space support */ + memory_region_add_subregion_overlap(get_system_memory(), 0, &s->ram, 0); + + /* Setup the SOC */ + object_property_add_const_link(OBJECT(&s->soc), "ram", OBJECT(&s->ram), + &error_abort); + object_property_set_int(OBJECT(&s->soc), smp_cpus, "enabled-cpus", + &error_abort); + object_property_set_int(OBJECT(&s->soc), 0xa21041, "board-rev", + &error_abort); + object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_abort); + + /* Create and plug in the SD cards */ + di = drive_get_next(IF_SD); + blk = di ? blk_by_legacy_dinfo(di) : NULL; + bus = qdev_get_child_bus(DEVICE(&s->soc), "sd-bus"); + if (bus == NULL) { + error_report("No SD bus found in SOC object"); + exit(1); + } + carddev = qdev_create(bus, TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal); + + vcram_size = object_property_get_int(OBJECT(&s->soc), "vcram-size", + &error_abort); + setup_boot(machine, 2, machine->ram_size - vcram_size); +} + +static void raspi2_machine_init(MachineClass *mc) +{ + mc->desc = "Raspberry Pi 2"; + mc->init = raspi2_init; + mc->block_default_type = IF_SD; + mc->no_parallel = 1; + mc->no_floppy = 1; + mc->no_cdrom = 1; + mc->max_cpus = BCM2836_NCPUS; + mc->default_ram_size = 1024 * 1024 * 1024; +}; +DEFINE_MACHINE("raspi2", raspi2_machine_init) diff --git a/qemu/hw/arm/realview.c b/qemu/hw/arm/realview.c index ef2788d3e..3222b360e 100644 --- a/qemu/hw/arm/realview.c +++ b/qemu/hw/arm/realview.c @@ -7,6 +7,10 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "hw/arm/arm.h" #include "hw/arm/primecell.h" @@ -99,33 +103,21 @@ static void realview_init(MachineState *machine, for (n = 0; n < smp_cpus; n++) { Object *cpuobj = object_new(object_class_get_name(cpu_oc)); - Error *err = NULL; /* By default A9,A15 and ARM1176 CPUs have EL3 enabled. This board * does not currently support EL3 so the CPU EL3 property is disabled * before realization. */ if (object_property_find(cpuobj, "has_el3", NULL)) { - object_property_set_bool(cpuobj, false, "has_el3", &err); - if (err) { - error_report_err(err); - exit(1); - } + object_property_set_bool(cpuobj, false, "has_el3", &error_fatal); } if (is_pb && is_mpcore) { - object_property_set_int(cpuobj, periphbase, "reset-cbar", &err); - if (err) { - error_report_err(err); - exit(1); - } + object_property_set_int(cpuobj, periphbase, "reset-cbar", + &error_fatal); } - object_property_set_bool(cpuobj, true, "realized", &err); - if (err) { - error_report_err(err); - exit(1); - } + object_property_set_bool(cpuobj, true, "realized", &error_fatal); cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpuobj), ARM_CPU_IRQ); } @@ -151,13 +143,13 @@ static void realview_init(MachineState *machine, low_ram_size = ram_size - 0x20000000; ram_size = 0x20000000; memory_region_init_ram(ram_lo, NULL, "realview.lowmem", low_ram_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(ram_lo); memory_region_add_subregion(sysmem, 0x20000000, ram_lo); } memory_region_init_ram(ram_hi, NULL, "realview.highmem", ram_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(ram_hi); low_ram_size = ram_size; if (low_ram_size > 0x10000000) @@ -353,7 +345,7 @@ static void realview_init(MachineState *machine, BootROM happens to be in ROM/flash or in memory that isn't clobbered until after Linux boots the secondary CPUs. */ memory_region_init_ram(ram_hack, NULL, "realview.hack", 0x1000, - &error_abort); + &error_fatal); vmstate_register_ram_global(ram_hack); memory_region_add_subregion(sysmem, SMP_BOOT_ADDR, ram_hack); @@ -399,41 +391,73 @@ static void realview_pbx_a9_init(MachineState *machine) realview_init(machine, BOARD_PBX_A9); } -static QEMUMachine realview_eb_machine = { - .name = "realview-eb", - .desc = "ARM RealView Emulation Baseboard (ARM926EJ-S)", - .init = realview_eb_init, - .block_default_type = IF_SCSI, +static void realview_eb_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "ARM RealView Emulation Baseboard (ARM926EJ-S)"; + mc->init = realview_eb_init; + mc->block_default_type = IF_SCSI; +} + +static const TypeInfo realview_eb_type = { + .name = MACHINE_TYPE_NAME("realview-eb"), + .parent = TYPE_MACHINE, + .class_init = realview_eb_class_init, }; -static QEMUMachine realview_eb_mpcore_machine = { - .name = "realview-eb-mpcore", - .desc = "ARM RealView Emulation Baseboard (ARM11MPCore)", - .init = realview_eb_mpcore_init, - .block_default_type = IF_SCSI, - .max_cpus = 4, +static void realview_eb_mpcore_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "ARM RealView Emulation Baseboard (ARM11MPCore)"; + mc->init = realview_eb_mpcore_init; + mc->block_default_type = IF_SCSI; + mc->max_cpus = 4; +} + +static const TypeInfo realview_eb_mpcore_type = { + .name = MACHINE_TYPE_NAME("realview-eb-mpcore"), + .parent = TYPE_MACHINE, + .class_init = realview_eb_mpcore_class_init, }; -static QEMUMachine realview_pb_a8_machine = { - .name = "realview-pb-a8", - .desc = "ARM RealView Platform Baseboard for Cortex-A8", - .init = realview_pb_a8_init, +static void realview_pb_a8_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "ARM RealView Platform Baseboard for Cortex-A8"; + mc->init = realview_pb_a8_init; +} + +static const TypeInfo realview_pb_a8_type = { + .name = MACHINE_TYPE_NAME("realview-pb-a8"), + .parent = TYPE_MACHINE, + .class_init = realview_pb_a8_class_init, }; -static QEMUMachine realview_pbx_a9_machine = { - .name = "realview-pbx-a9", - .desc = "ARM RealView Platform Baseboard Explore for Cortex-A9", - .init = realview_pbx_a9_init, - .block_default_type = IF_SCSI, - .max_cpus = 4, +static void realview_pbx_a9_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "ARM RealView Platform Baseboard Explore for Cortex-A9"; + mc->init = realview_pbx_a9_init; + mc->block_default_type = IF_SCSI; + mc->max_cpus = 4; +} + +static const TypeInfo realview_pbx_a9_type = { + .name = MACHINE_TYPE_NAME("realview-pbx-a9"), + .parent = TYPE_MACHINE, + .class_init = realview_pbx_a9_class_init, }; static void realview_machine_init(void) { - qemu_register_machine(&realview_eb_machine); - qemu_register_machine(&realview_eb_mpcore_machine); - qemu_register_machine(&realview_pb_a8_machine); - qemu_register_machine(&realview_pbx_a9_machine); + type_register_static(&realview_eb_type); + type_register_static(&realview_eb_mpcore_type); + type_register_static(&realview_pb_a8_type); + type_register_static(&realview_pbx_a9_type); } -machine_init(realview_machine_init); +type_init(realview_machine_init) diff --git a/qemu/hw/arm/spitz.c b/qemu/hw/arm/spitz.c index 5bf032a63..bf61d63b5 100644 --- a/qemu/hw/arm/spitz.c +++ b/qemu/hw/arm/spitz.c @@ -10,13 +10,15 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "hw/arm/pxa.h" #include "hw/arm/arm.h" #include "sysemu/sysemu.h" #include "hw/pcmcia.h" #include "hw/i2c/i2c.h" -#include "hw/ssi.h" +#include "hw/ssi/ssi.h" #include "hw/block/flash.h" #include "qemu/timer.h" #include "hw/devices.h" @@ -403,7 +405,7 @@ static void spitz_keyboard_tick(void *opaque) } timer_mod(s->kbdtimer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - get_ticks_per_sec() / 32); + NANOSECONDS_PER_SECOND / 32); } static void spitz_keyboard_pre_map(SpitzKeyboardState *s) @@ -913,7 +915,7 @@ static void spitz_common_init(MachineState *machine, sl_flash_register(mpu, (model == spitz) ? FLASH_128M : FLASH_1024M); - memory_region_init_ram(rom, NULL, "spitz.rom", SPITZ_ROM, &error_abort); + memory_region_init_ram(rom, NULL, "spitz.rom", SPITZ_ROM, &error_fatal); vmstate_register_ram_global(rom); memory_region_set_readonly(rom, true); memory_region_add_subregion(address_space_mem, 0, rom); @@ -972,39 +974,71 @@ static void terrier_init(MachineState *machine) spitz_common_init(machine, terrier, 0x33f); } -static QEMUMachine akitapda_machine = { - .name = "akita", - .desc = "Akita PDA (PXA270)", - .init = akita_init, +static void akitapda_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Sharp SL-C1000 (Akita) PDA (PXA270)"; + mc->init = akita_init; +} + +static const TypeInfo akitapda_type = { + .name = MACHINE_TYPE_NAME("akita"), + .parent = TYPE_MACHINE, + .class_init = akitapda_class_init, }; -static QEMUMachine spitzpda_machine = { - .name = "spitz", - .desc = "Spitz PDA (PXA270)", - .init = spitz_init, +static void spitzpda_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Sharp SL-C3000 (Spitz) PDA (PXA270)"; + mc->init = spitz_init; +} + +static const TypeInfo spitzpda_type = { + .name = MACHINE_TYPE_NAME("spitz"), + .parent = TYPE_MACHINE, + .class_init = spitzpda_class_init, }; -static QEMUMachine borzoipda_machine = { - .name = "borzoi", - .desc = "Borzoi PDA (PXA270)", - .init = borzoi_init, +static void borzoipda_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Sharp SL-C3100 (Borzoi) PDA (PXA270)"; + mc->init = borzoi_init; +} + +static const TypeInfo borzoipda_type = { + .name = MACHINE_TYPE_NAME("borzoi"), + .parent = TYPE_MACHINE, + .class_init = borzoipda_class_init, }; -static QEMUMachine terrierpda_machine = { - .name = "terrier", - .desc = "Terrier PDA (PXA270)", - .init = terrier_init, +static void terrierpda_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Sharp SL-C3200 (Terrier) PDA (PXA270)"; + mc->init = terrier_init; +} + +static const TypeInfo terrierpda_type = { + .name = MACHINE_TYPE_NAME("terrier"), + .parent = TYPE_MACHINE, + .class_init = terrierpda_class_init, }; static void spitz_machine_init(void) { - qemu_register_machine(&akitapda_machine); - qemu_register_machine(&spitzpda_machine); - qemu_register_machine(&borzoipda_machine); - qemu_register_machine(&terrierpda_machine); + type_register_static(&akitapda_type); + type_register_static(&spitzpda_type); + type_register_static(&borzoipda_type); + type_register_static(&terrierpda_type); } -machine_init(spitz_machine_init); +type_init(spitz_machine_init) static bool is_version_0(void *opaque, int version_id) { @@ -1060,10 +1094,6 @@ static VMStateDescription vmstate_spitz_kbd = { }, }; -static Property spitz_keyboard_properties[] = { - DEFINE_PROP_END_OF_LIST(), -}; - static void spitz_keyboard_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1071,7 +1101,6 @@ static void spitz_keyboard_class_init(ObjectClass *klass, void *data) k->init = spitz_keyboard_init; dc->vmsd = &vmstate_spitz_kbd; - dc->props = spitz_keyboard_properties; } static const TypeInfo spitz_keyboard_info = { diff --git a/qemu/hw/arm/stellaris.c b/qemu/hw/arm/stellaris.c index cb515ec76..c1766f856 100644 --- a/qemu/hw/arm/stellaris.c +++ b/qemu/hw/arm/stellaris.c @@ -7,8 +7,10 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/sysbus.h" -#include "hw/ssi.h" +#include "hw/ssi/ssi.h" #include "hw/arm/arm.h" #include "hw/devices.h" #include "qemu/timer.h" @@ -16,6 +18,7 @@ #include "net/net.h" #include "hw/boards.h" #include "exec/address-spaces.h" +#include "sysemu/sysemu.h" #define GPIO_A 0 #define GPIO_B 1 @@ -98,7 +101,7 @@ static void gptm_reload(gptm_state *s, int n, int reset) tick += (int64_t)count * system_clock_scale; } else if (s->config == 1) { /* 32-bit RTC. 1Hz tick. */ - tick += get_ticks_per_sec(); + tick += NANOSECONDS_PER_SECOND; } else if (s->mode[n] == 0xa) { /* PWM mode. Not implemented. */ } else { @@ -675,7 +678,7 @@ static int stellaris_sys_init(uint32_t base, qemu_irq irq, { ssys_state *s; - s = (ssys_state *)g_malloc0(sizeof(ssys_state)); + s = g_new0(ssys_state, 1); s->irq = irq; s->board = board; /* Most devices come preprogrammed with a MAC address in the user data. */ @@ -1176,6 +1179,14 @@ static int stellaris_adc_init(SysBusDevice *sbd) return 0; } +static +void do_sys_reset(void *opaque, int n, int level) +{ + if (level) { + qemu_system_reset_request(); + } +} + /* Board init. */ static stellaris_board_info stellaris_boards[] = { { "LM3S811EVB", @@ -1210,8 +1221,7 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model, 0x40024000, 0x40025000, 0x40026000}; static const int gpio_irq[7] = {0, 1, 2, 3, 4, 30, 31}; - qemu_irq *pic; - DeviceState *gpio_dev[7]; + DeviceState *gpio_dev[7], *nvic; qemu_irq gpio_in[7][8]; qemu_irq gpio_out[7][8]; qemu_irq adc; @@ -1231,22 +1241,29 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model, /* Flash programming is done via the SCU, so pretend it is ROM. */ memory_region_init_ram(flash, NULL, "stellaris.flash", flash_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(flash); memory_region_set_readonly(flash, true); memory_region_add_subregion(system_memory, 0, flash); memory_region_init_ram(sram, NULL, "stellaris.sram", sram_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(sram); memory_region_add_subregion(system_memory, 0x20000000, sram); - pic = armv7m_init(system_memory, flash_size, NUM_IRQ_LINES, + nvic = armv7m_init(system_memory, flash_size, NUM_IRQ_LINES, kernel_filename, cpu_model); + qdev_connect_gpio_out_named(nvic, "SYSRESETREQ", 0, + qemu_allocate_irq(&do_sys_reset, NULL, 0)); + if (board->dc1 & (1 << 16)) { dev = sysbus_create_varargs(TYPE_STELLARIS_ADC, 0x40038000, - pic[14], pic[15], pic[16], pic[17], NULL); + qdev_get_gpio_in(nvic, 14), + qdev_get_gpio_in(nvic, 15), + qdev_get_gpio_in(nvic, 16), + qdev_get_gpio_in(nvic, 17), + NULL); adc = qdev_get_gpio_in(dev, 0); } else { adc = NULL; @@ -1255,19 +1272,21 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model, if (board->dc2 & (0x10000 << i)) { dev = sysbus_create_simple(TYPE_STELLARIS_GPTM, 0x40030000 + i * 0x1000, - pic[timer_irq[i]]); + qdev_get_gpio_in(nvic, timer_irq[i])); /* TODO: This is incorrect, but we get away with it because the ADC output is only ever pulsed. */ qdev_connect_gpio_out(dev, 0, adc); } } - stellaris_sys_init(0x400fe000, pic[28], board, nd_table[0].macaddr.a); + stellaris_sys_init(0x400fe000, qdev_get_gpio_in(nvic, 28), + board, nd_table[0].macaddr.a); for (i = 0; i < 7; i++) { if (board->dc4 & (1 << i)) { gpio_dev[i] = sysbus_create_simple("pl061_luminary", gpio_addr[i], - pic[gpio_irq[i]]); + qdev_get_gpio_in(nvic, + gpio_irq[i])); for (j = 0; j < 8; j++) { gpio_in[i][j] = qdev_get_gpio_in(gpio_dev[i], j); gpio_out[i][j] = NULL; @@ -1276,7 +1295,8 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model, } if (board->dc2 & (1 << 12)) { - dev = sysbus_create_simple(TYPE_STELLARIS_I2C, 0x40020000, pic[8]); + dev = sysbus_create_simple(TYPE_STELLARIS_I2C, 0x40020000, + qdev_get_gpio_in(nvic, 8)); i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); if (board->peripherals & BP_OLED_I2C) { i2c_create_slave(i2c, "ssd0303", 0x3d); @@ -1286,11 +1306,12 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model, for (i = 0; i < 4; i++) { if (board->dc2 & (1 << i)) { sysbus_create_simple("pl011_luminary", 0x4000c000 + i * 0x1000, - pic[uart_irq[i]]); + qdev_get_gpio_in(nvic, uart_irq[i])); } } if (board->dc2 & (1 << 4)) { - dev = sysbus_create_simple("pl022", 0x40008000, pic[7]); + dev = sysbus_create_simple("pl022", 0x40008000, + qdev_get_gpio_in(nvic, 7)); if (board->peripherals & BP_OLED_SSI) { void *bus; DeviceState *sddev; @@ -1326,7 +1347,7 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model, qdev_set_nic_properties(enet, &nd_table[0]); qdev_init_nofail(enet); sysbus_mmio_map(SYS_BUS_DEVICE(enet), 0, 0x40048000); - sysbus_connect_irq(SYS_BUS_DEVICE(enet), 0, pic[42]); + sysbus_connect_irq(SYS_BUS_DEVICE(enet), 0, qdev_get_gpio_in(nvic, 42)); } if (board->peripherals & BP_GAMEPAD) { qemu_irq gpad_irq[5]; @@ -1366,25 +1387,41 @@ static void lm3s6965evb_init(MachineState *machine) stellaris_init(kernel_filename, cpu_model, &stellaris_boards[1]); } -static QEMUMachine lm3s811evb_machine = { - .name = "lm3s811evb", - .desc = "Stellaris LM3S811EVB", - .init = lm3s811evb_init, +static void lm3s811evb_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Stellaris LM3S811EVB"; + mc->init = lm3s811evb_init; +} + +static const TypeInfo lm3s811evb_type = { + .name = MACHINE_TYPE_NAME("lm3s811evb"), + .parent = TYPE_MACHINE, + .class_init = lm3s811evb_class_init, }; -static QEMUMachine lm3s6965evb_machine = { - .name = "lm3s6965evb", - .desc = "Stellaris LM3S6965EVB", - .init = lm3s6965evb_init, +static void lm3s6965evb_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Stellaris LM3S6965EVB"; + mc->init = lm3s6965evb_init; +} + +static const TypeInfo lm3s6965evb_type = { + .name = MACHINE_TYPE_NAME("lm3s6965evb"), + .parent = TYPE_MACHINE, + .class_init = lm3s6965evb_class_init, }; static void stellaris_machine_init(void) { - qemu_register_machine(&lm3s811evb_machine); - qemu_register_machine(&lm3s6965evb_machine); + type_register_static(&lm3s811evb_type); + type_register_static(&lm3s6965evb_type); } -machine_init(stellaris_machine_init); +type_init(stellaris_machine_init) static void stellaris_i2c_class_init(ObjectClass *klass, void *data) { diff --git a/qemu/hw/arm/stm32f205_soc.c b/qemu/hw/arm/stm32f205_soc.c index 0f3bdc77b..a5ea1e237 100644 --- a/qemu/hw/arm/stm32f205_soc.c +++ b/qemu/hw/arm/stm32f205_soc.c @@ -22,6 +22,10 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/arm/arm.h" #include "exec/address-spaces.h" #include "hw/arm/stm32f205_soc.h" @@ -59,9 +63,8 @@ static void stm32f205_soc_initfn(Object *obj) static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) { STM32F205State *s = STM32F205_SOC(dev_soc); - DeviceState *syscfgdev, *usartdev, *timerdev; + DeviceState *syscfgdev, *usartdev, *timerdev, *nvic; SysBusDevice *syscfgbusdev, *usartbusdev, *timerbusdev; - qemu_irq *pic; Error *err = NULL; int i; @@ -71,7 +74,7 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) MemoryRegion *flash_alias = g_new(MemoryRegion, 1); memory_region_init_ram(flash, NULL, "STM32F205.flash", FLASH_SIZE, - &error_abort); + &error_fatal); memory_region_init_alias(flash_alias, NULL, "STM32F205.flash.alias", flash, 0, FLASH_SIZE); @@ -84,12 +87,12 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) memory_region_add_subregion(system_memory, 0, flash_alias); memory_region_init_ram(sram, NULL, "STM32F205.sram", SRAM_SIZE, - &error_abort); + &error_fatal); vmstate_register_ram_global(sram); memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, sram); - pic = armv7m_init(get_system_memory(), FLASH_SIZE, 96, - s->kernel_filename, s->cpu_model); + nvic = armv7m_init(get_system_memory(), FLASH_SIZE, 96, + s->kernel_filename, s->cpu_model); /* System configuration controller */ syscfgdev = DEVICE(&s->syscfg); @@ -100,7 +103,7 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) } syscfgbusdev = SYS_BUS_DEVICE(syscfgdev); sysbus_mmio_map(syscfgbusdev, 0, 0x40013800); - sysbus_connect_irq(syscfgbusdev, 0, pic[71]); + sysbus_connect_irq(syscfgbusdev, 0, qdev_get_gpio_in(nvic, 71)); /* Attach UART (uses USART registers) and USART controllers */ for (i = 0; i < STM_NUM_USARTS; i++) { @@ -112,7 +115,8 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) } usartbusdev = SYS_BUS_DEVICE(usartdev); sysbus_mmio_map(usartbusdev, 0, usart_addr[i]); - sysbus_connect_irq(usartbusdev, 0, pic[usart_irq[i]]); + sysbus_connect_irq(usartbusdev, 0, + qdev_get_gpio_in(nvic, usart_irq[i])); } /* Timer 2 to 5 */ @@ -126,7 +130,8 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) } timerbusdev = SYS_BUS_DEVICE(timerdev); sysbus_mmio_map(timerbusdev, 0, timer_addr[i]); - sysbus_connect_irq(timerbusdev, 0, pic[timer_irq[i]]); + sysbus_connect_irq(timerbusdev, 0, + qdev_get_gpio_in(nvic, timer_irq[i])); } } diff --git a/qemu/hw/arm/strongarm.c b/qemu/hw/arm/strongarm.c index da9fc1d51..1eeb1ab39 100644 --- a/qemu/hw/arm/strongarm.c +++ b/qemu/hw/arm/strongarm.c @@ -27,6 +27,8 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" +#include "cpu.h" #include "hw/boards.h" #include "hw/sysbus.h" #include "strongarm.h" @@ -34,7 +36,8 @@ #include "hw/arm/arm.h" #include "sysemu/char.h" #include "sysemu/sysemu.h" -#include "hw/ssi.h" +#include "hw/ssi/ssi.h" +#include "qemu/cutils.h" //#define DEBUG @@ -1023,7 +1026,7 @@ static void strongarm_uart_update_parameters(StrongARMUARTState *s) ssp.parity = parity; ssp.data_bits = data_bits; ssp.stop_bits = stop_bits; - s->char_transmit_time = (get_ticks_per_sec() / speed) * frame_size; + s->char_transmit_time = (NANOSECONDS_PER_SECOND / speed) * frame_size; if (s->chr) { qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); } @@ -1588,7 +1591,7 @@ StrongARMState *sa1110_init(MemoryRegion *sysmem, StrongARMState *s; int i; - s = g_malloc0(sizeof(StrongARMState)); + s = g_new0(StrongARMState, 1); if (!rev) { rev = "sa1110-b5"; diff --git a/qemu/hw/arm/sysbus-fdt.c b/qemu/hw/arm/sysbus-fdt.c index 9d28797c8..5debb3348 100644 --- a/qemu/hw/arm/sysbus-fdt.c +++ b/qemu/hw/arm/sysbus-fdt.c @@ -21,6 +21,13 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include +#include "qemu-common.h" +#ifdef CONFIG_LINUX +#include +#endif #include "hw/arm/sysbus-fdt.h" #include "qemu/error-report.h" #include "sysemu/device_tree.h" @@ -28,6 +35,7 @@ #include "sysemu/sysemu.h" #include "hw/vfio/vfio-platform.h" #include "hw/vfio/vfio-calxeda-xgmac.h" +#include "hw/vfio/vfio-amd-xgbe.h" #include "hw/arm/fdt.h" /* @@ -56,6 +64,146 @@ typedef struct NodeCreationPair { int (*add_fdt_node_fn)(SysBusDevice *sbdev, void *opaque); } NodeCreationPair; +/* helpers */ + +typedef struct HostProperty { + const char *name; + bool optional; +} HostProperty; + +#ifdef CONFIG_LINUX + +/** + * copy_properties_from_host + * + * copies properties listed in an array from host device tree to + * guest device tree. If a non optional property is not found, the + * function asserts. An optional property is ignored if not found + * in the host device tree. + * @props: array of HostProperty to copy + * @nb_props: number of properties in the array + * @host_dt: host device tree blob + * @guest_dt: guest device tree blob + * @node_path: host dt node path where the property is supposed to be + found + * @nodename: guest node name the properties should be added to + */ +static void copy_properties_from_host(HostProperty *props, int nb_props, + void *host_fdt, void *guest_fdt, + char *node_path, char *nodename) +{ + int i, prop_len; + const void *r; + Error *err = NULL; + + for (i = 0; i < nb_props; i++) { + r = qemu_fdt_getprop(host_fdt, node_path, + props[i].name, + &prop_len, + props[i].optional ? &err : &error_fatal); + if (r) { + qemu_fdt_setprop(guest_fdt, nodename, + props[i].name, r, prop_len); + } else { + if (prop_len != -FDT_ERR_NOTFOUND) { + /* optional property not returned although property exists */ + error_report_err(err); + } else { + error_free(err); + } + } + } +} + +/* clock properties whose values are copied/pasted from host */ +static HostProperty clock_copied_properties[] = { + {"compatible", false}, + {"#clock-cells", false}, + {"clock-frequency", true}, + {"clock-output-names", true}, +}; + +/** + * fdt_build_clock_node + * + * Build a guest clock node, used as a dependency from a passthrough'ed + * device. Most information are retrieved from the host clock node. + * Also check the host clock is a fixed one. + * + * @host_fdt: host device tree blob from which info are retrieved + * @guest_fdt: guest device tree blob where the clock node is added + * @host_phandle: phandle of the clock in host device tree + * @guest_phandle: phandle to assign to the guest node + */ +static void fdt_build_clock_node(void *host_fdt, void *guest_fdt, + uint32_t host_phandle, + uint32_t guest_phandle) +{ + char *node_path = NULL; + char *nodename; + const void *r; + int ret, node_offset, prop_len, path_len = 16; + + node_offset = fdt_node_offset_by_phandle(host_fdt, host_phandle); + if (node_offset <= 0) { + error_setg(&error_fatal, + "not able to locate clock handle %d in host device tree", + host_phandle); + } + node_path = g_malloc(path_len); + while ((ret = fdt_get_path(host_fdt, node_offset, node_path, path_len)) + == -FDT_ERR_NOSPACE) { + path_len += 16; + node_path = g_realloc(node_path, path_len); + } + if (ret < 0) { + error_setg(&error_fatal, + "not able to retrieve node path for clock handle %d", + host_phandle); + } + + r = qemu_fdt_getprop(host_fdt, node_path, "compatible", &prop_len, + &error_fatal); + if (strcmp(r, "fixed-clock")) { + error_setg(&error_fatal, + "clock handle %d is not a fixed clock", host_phandle); + } + + nodename = strrchr(node_path, '/'); + qemu_fdt_add_subnode(guest_fdt, nodename); + + copy_properties_from_host(clock_copied_properties, + ARRAY_SIZE(clock_copied_properties), + host_fdt, guest_fdt, + node_path, nodename); + + qemu_fdt_setprop_cell(guest_fdt, nodename, "phandle", guest_phandle); + + g_free(node_path); +} + +/** + * sysfs_to_dt_name: convert the name found in sysfs into the node name + * for instance e0900000.xgmac is converted into xgmac@e0900000 + * @sysfs_name: directory name in sysfs + * + * returns the device tree name upon success or NULL in case the sysfs name + * does not match the expected format + */ +static char *sysfs_to_dt_name(const char *sysfs_name) +{ + gchar **substrings = g_strsplit(sysfs_name, ".", 2); + char *dt_name = NULL; + + if (!substrings || !substrings[0] || !substrings[1]) { + goto out; + } + dt_name = g_strdup_printf("%s@%s", substrings[1], substrings[0]); +out: + g_strfreev(substrings); + return dt_name; +} + /* Device Specific Code */ /** @@ -70,7 +218,7 @@ static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque) PlatformBusDevice *pbus = data->pbus; void *fdt = data->fdt; const char *parent_node = data->pbus_node_name; - int compat_str_len, i, ret = -1; + int compat_str_len, i; char *nodename; uint32_t *irq_attr, *reg_attr; uint64_t mmio_base, irq_number; @@ -93,14 +241,10 @@ static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque) mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i); reg_attr[2 * i] = cpu_to_be32(mmio_base); reg_attr[2 * i + 1] = cpu_to_be32( - memory_region_size(&vdev->regions[i]->mem)); - } - ret = qemu_fdt_setprop(fdt, nodename, "reg", reg_attr, - vbasedev->num_regions * 2 * sizeof(uint32_t)); - if (ret) { - error_report("could not set reg property of node %s", nodename); - goto fail_reg; + memory_region_size(vdev->regions[i]->mem)); } + qemu_fdt_setprop(fdt, nodename, "reg", reg_attr, + vbasedev->num_regions * 2 * sizeof(uint32_t)); irq_attr = g_new(uint32_t, vbasedev->num_irqs * 3); for (i = 0; i < vbasedev->num_irqs; i++) { @@ -110,22 +254,173 @@ static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque) irq_attr[3 * i + 1] = cpu_to_be32(irq_number); irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI); } - ret = qemu_fdt_setprop(fdt, nodename, "interrupts", + qemu_fdt_setprop(fdt, nodename, "interrupts", irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t)); - if (ret) { - error_report("could not set interrupts property of node %s", - nodename); + g_free(irq_attr); + g_free(reg_attr); + g_free(nodename); + return 0; +} + +/* AMD xgbe properties whose values are copied/pasted from host */ +static HostProperty amd_xgbe_copied_properties[] = { + {"compatible", false}, + {"dma-coherent", true}, + {"amd,per-channel-interrupt", true}, + {"phy-mode", false}, + {"mac-address", true}, + {"amd,speed-set", false}, + {"amd,serdes-blwc", true}, + {"amd,serdes-cdr-rate", true}, + {"amd,serdes-pq-skew", true}, + {"amd,serdes-tx-amp", true}, + {"amd,serdes-dfe-tap-config", true}, + {"amd,serdes-dfe-tap-enable", true}, + {"clock-names", false}, +}; + +/** + * add_amd_xgbe_fdt_node + * + * Generates the combined xgbe/phy node following kernel >=4.2 + * binding documentation: + * Documentation/devicetree/bindings/net/amd-xgbe.txt: + * Also 2 clock nodes are created (dma and ptp) + * + * Asserts in case of error + */ +static int add_amd_xgbe_fdt_node(SysBusDevice *sbdev, void *opaque) +{ + PlatformBusFDTData *data = opaque; + PlatformBusDevice *pbus = data->pbus; + VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); + VFIODevice *vbasedev = &vdev->vbasedev; + VFIOINTp *intp; + const char *parent_node = data->pbus_node_name; + char **node_path, *nodename, *dt_name; + void *guest_fdt = data->fdt, *host_fdt; + const void *r; + int i, prop_len; + uint32_t *irq_attr, *reg_attr, *host_clock_phandles; + uint64_t mmio_base, irq_number; + uint32_t guest_clock_phandles[2]; + + host_fdt = load_device_tree_from_sysfs(); + + dt_name = sysfs_to_dt_name(vbasedev->name); + if (!dt_name) { + error_setg(&error_fatal, "%s incorrect sysfs device name %s", + __func__, vbasedev->name); + } + node_path = qemu_fdt_node_path(host_fdt, dt_name, vdev->compat, + &error_fatal); + if (!node_path || !node_path[0]) { + error_setg(&error_fatal, "%s unable to retrieve node path for %s/%s", + __func__, dt_name, vdev->compat); } + + if (node_path[1]) { + error_setg(&error_fatal, "%s more than one node matching %s/%s!", + __func__, dt_name, vdev->compat); + } + + g_free(dt_name); + + if (vbasedev->num_regions != 5) { + error_setg(&error_fatal, "%s Does the host dt node combine XGBE/PHY?", + __func__); + } + + /* generate nodes for DMA_CLK and PTP_CLK */ + r = qemu_fdt_getprop(host_fdt, node_path[0], "clocks", + &prop_len, &error_fatal); + if (prop_len != 8) { + error_setg(&error_fatal, "%s clocks property should contain 2 handles", + __func__); + } + host_clock_phandles = (uint32_t *)r; + guest_clock_phandles[0] = qemu_fdt_alloc_phandle(guest_fdt); + guest_clock_phandles[1] = qemu_fdt_alloc_phandle(guest_fdt); + + /** + * clock handles fetched from host dt are in be32 layout whereas + * rest of the code uses cpu layout. Also guest clock handles are + * in cpu layout. + */ + fdt_build_clock_node(host_fdt, guest_fdt, + be32_to_cpu(host_clock_phandles[0]), + guest_clock_phandles[0]); + + fdt_build_clock_node(host_fdt, guest_fdt, + be32_to_cpu(host_clock_phandles[1]), + guest_clock_phandles[1]); + + /* combined XGBE/PHY node */ + mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0); + nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, + vbasedev->name, mmio_base); + qemu_fdt_add_subnode(guest_fdt, nodename); + + copy_properties_from_host(amd_xgbe_copied_properties, + ARRAY_SIZE(amd_xgbe_copied_properties), + host_fdt, guest_fdt, + node_path[0], nodename); + + qemu_fdt_setprop_cells(guest_fdt, nodename, "clocks", + guest_clock_phandles[0], + guest_clock_phandles[1]); + + reg_attr = g_new(uint32_t, vbasedev->num_regions * 2); + for (i = 0; i < vbasedev->num_regions; i++) { + mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i); + reg_attr[2 * i] = cpu_to_be32(mmio_base); + reg_attr[2 * i + 1] = cpu_to_be32( + memory_region_size(vdev->regions[i]->mem)); + } + qemu_fdt_setprop(guest_fdt, nodename, "reg", reg_attr, + vbasedev->num_regions * 2 * sizeof(uint32_t)); + + irq_attr = g_new(uint32_t, vbasedev->num_irqs * 3); + for (i = 0; i < vbasedev->num_irqs; i++) { + irq_number = platform_bus_get_irqn(pbus, sbdev , i) + + data->irq_start; + irq_attr[3 * i] = cpu_to_be32(GIC_FDT_IRQ_TYPE_SPI); + irq_attr[3 * i + 1] = cpu_to_be32(irq_number); + /* + * General device interrupt and PCS auto-negotiation interrupts are + * level-sensitive while the 4 per-channel interrupts are edge + * sensitive + */ + QLIST_FOREACH(intp, &vdev->intp_list, next) { + if (intp->pin == i) { + break; + } + } + if (intp->flags & VFIO_IRQ_INFO_AUTOMASKED) { + irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI); + } else { + irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); + } + } + qemu_fdt_setprop(guest_fdt, nodename, "interrupts", + irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t)); + + g_free(host_fdt); + g_strfreev(node_path); g_free(irq_attr); -fail_reg: g_free(reg_attr); g_free(nodename); - return ret; + return 0; } +#endif /* CONFIG_LINUX */ + /* list of supported dynamic sysbus devices */ static const NodeCreationPair add_fdt_node_functions[] = { +#ifdef CONFIG_LINUX {TYPE_VFIO_CALXEDA_XGMAC, add_calxeda_midway_xgmac_fdt_node}, + {TYPE_VFIO_AMD_XGBE, add_amd_xgbe_fdt_node}, +#endif {"", NULL}, /* last element */ }; diff --git a/qemu/hw/arm/tosa.c b/qemu/hw/arm/tosa.c index 73572ebe0..4e9494f94 100644 --- a/qemu/hw/arm/tosa.c +++ b/qemu/hw/arm/tosa.c @@ -11,6 +11,8 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "hw/arm/pxa.h" #include "hw/arm/arm.h" @@ -19,7 +21,7 @@ #include "hw/pcmcia.h" #include "hw/boards.h" #include "hw/i2c/i2c.h" -#include "hw/ssi.h" +#include "hw/ssi/ssi.h" #include "sysemu/block-backend.h" #include "hw/sysbus.h" #include "exec/address-spaces.h" @@ -227,7 +229,7 @@ static void tosa_init(MachineState *machine) mpu = pxa255_init(address_space_mem, tosa_binfo.ram_size); - memory_region_init_ram(rom, NULL, "tosa.rom", TOSA_ROM, &error_abort); + memory_region_init_ram(rom, NULL, "tosa.rom", TOSA_ROM, &error_fatal); vmstate_register_ram_global(rom); memory_region_set_readonly(rom, true); memory_region_add_subregion(address_space_mem, 0, rom); @@ -252,18 +254,13 @@ static void tosa_init(MachineState *machine) sl_bootparam_write(SL_PXA_PARAM_BASE); } -static QEMUMachine tosapda_machine = { - .name = "tosa", - .desc = "Tosa PDA (PXA255)", - .init = tosa_init, -}; - -static void tosapda_machine_init(void) +static void tosapda_machine_init(MachineClass *mc) { - qemu_register_machine(&tosapda_machine); + mc->desc = "Sharp SL-6000 (Tosa) PDA (PXA255)"; + mc->init = tosa_init; } -machine_init(tosapda_machine_init); +DEFINE_MACHINE("tosa", tosapda_machine_init) static void tosa_dac_class_init(ObjectClass *klass, void *data) { diff --git a/qemu/hw/arm/versatilepb.c b/qemu/hw/arm/versatilepb.c index 6c69f4eaa..e5a80c2d2 100644 --- a/qemu/hw/arm/versatilepb.c +++ b/qemu/hw/arm/versatilepb.c @@ -7,6 +7,10 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "hw/arm/arm.h" #include "hw/devices.h" @@ -192,7 +196,6 @@ static void versatile_init(MachineState *machine, int board_id) int n; int done_smc = 0; DriveInfo *dinfo; - Error *err = NULL; if (!machine->cpu_model) { machine->cpu_model = "arm926"; @@ -211,18 +214,10 @@ static void versatile_init(MachineState *machine, int board_id) * realization. */ if (object_property_find(cpuobj, "has_el3", NULL)) { - object_property_set_bool(cpuobj, false, "has_el3", &err); - if (err) { - error_report_err(err); - exit(1); - } + object_property_set_bool(cpuobj, false, "has_el3", &error_fatal); } - object_property_set_bool(cpuobj, true, "realized", &err); - if (err) { - error_report_err(err); - exit(1); - } + object_property_set_bool(cpuobj, true, "realized", &error_fatal); cpu = ARM_CPU(cpuobj); @@ -391,27 +386,43 @@ static void vab_init(MachineState *machine) versatile_init(machine, 0x25e); } -static QEMUMachine versatilepb_machine = { - .name = "versatilepb", - .desc = "ARM Versatile/PB (ARM926EJ-S)", - .init = vpb_init, - .block_default_type = IF_SCSI, +static void versatilepb_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "ARM Versatile/PB (ARM926EJ-S)"; + mc->init = vpb_init; + mc->block_default_type = IF_SCSI; +} + +static const TypeInfo versatilepb_type = { + .name = MACHINE_TYPE_NAME("versatilepb"), + .parent = TYPE_MACHINE, + .class_init = versatilepb_class_init, }; -static QEMUMachine versatileab_machine = { - .name = "versatileab", - .desc = "ARM Versatile/AB (ARM926EJ-S)", - .init = vab_init, - .block_default_type = IF_SCSI, +static void versatileab_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "ARM Versatile/AB (ARM926EJ-S)"; + mc->init = vab_init; + mc->block_default_type = IF_SCSI; +} + +static const TypeInfo versatileab_type = { + .name = MACHINE_TYPE_NAME("versatileab"), + .parent = TYPE_MACHINE, + .class_init = versatileab_class_init, }; static void versatile_machine_init(void) { - qemu_register_machine(&versatilepb_machine); - qemu_register_machine(&versatileab_machine); + type_register_static(&versatilepb_type); + type_register_static(&versatileab_type); } -machine_init(versatile_machine_init); +type_init(versatile_machine_init) static void vpb_sic_class_init(ObjectClass *klass, void *data) { diff --git a/qemu/hw/arm/vexpress.c b/qemu/hw/arm/vexpress.c index da217884e..70b3e701e 100644 --- a/qemu/hw/arm/vexpress.c +++ b/qemu/hw/arm/vexpress.c @@ -21,6 +21,10 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "hw/arm/arm.h" #include "hw/arm/primecell.h" @@ -168,8 +172,8 @@ typedef struct { } VexpressMachineState; #define TYPE_VEXPRESS_MACHINE "vexpress" -#define TYPE_VEXPRESS_A9_MACHINE "vexpress-a9" -#define TYPE_VEXPRESS_A15_MACHINE "vexpress-a15" +#define TYPE_VEXPRESS_A9_MACHINE MACHINE_TYPE_NAME("vexpress-a9") +#define TYPE_VEXPRESS_A15_MACHINE MACHINE_TYPE_NAME("vexpress-a15") #define VEXPRESS_MACHINE(obj) \ OBJECT_CHECK(VexpressMachineState, (obj), TYPE_VEXPRESS_MACHINE) #define VEXPRESS_MACHINE_GET_CLASS(obj) \ @@ -211,7 +215,6 @@ static void init_cpus(const char *cpu_model, const char *privdev, /* Create the actual CPUs */ for (n = 0; n < smp_cpus; n++) { Object *cpuobj = object_new(object_class_get_name(cpu_oc)); - Error *err = NULL; if (!secure) { object_property_set_bool(cpuobj, false, "has_el3", NULL); @@ -221,11 +224,7 @@ static void init_cpus(const char *cpu_model, const char *privdev, object_property_set_int(cpuobj, periphbase, "reset-cbar", &error_abort); } - object_property_set_bool(cpuobj, true, "realized", &err); - if (err) { - error_report_err(err); - exit(1); - } + object_property_set_bool(cpuobj, true, "realized", &error_fatal); } /* Create the private peripheral devices (including the GIC); @@ -391,7 +390,7 @@ static void a15_daughterboard_init(const VexpressMachineState *vms, /* 0x2b0a0000: PL341 dynamic memory controller: not modelled */ /* 0x2e000000: system SRAM */ memory_region_init_ram(sram, NULL, "vexpress.a15sram", 0x10000, - &error_abort); + &error_fatal); vmstate_register_ram_global(sram); memory_region_add_subregion(sysmem, 0x2e000000, sram); @@ -482,8 +481,10 @@ static void vexpress_modify_dtb(const struct arm_boot_info *info, void *fdt) uint32_t acells, scells, intc; const VEDBoardInfo *daughterboard = (const VEDBoardInfo *)info; - acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells"); - scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells"); + acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells", + NULL, &error_fatal); + scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells", + NULL, &error_fatal); intc = find_int_controller(fdt); if (!intc) { /* Not fatal, we just won't provide virtio. This will @@ -541,7 +542,7 @@ static void vexpress_common_init(MachineState *machine) { VexpressMachineState *vms = VEXPRESS_MACHINE(machine); VexpressMachineClass *vmc = VEXPRESS_MACHINE_GET_CLASS(machine); - VEDBoardInfo *daughterboard = vmc->daughterboard;; + VEDBoardInfo *daughterboard = vmc->daughterboard; DeviceState *dev, *sysctl, *pl041; qemu_irq pic[64]; uint32_t sys_id; @@ -671,13 +672,13 @@ static void vexpress_common_init(MachineState *machine) sram_size = 0x2000000; memory_region_init_ram(sram, NULL, "vexpress.sram", sram_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(sram); memory_region_add_subregion(sysmem, map[VE_SRAM], sram); vram_size = 0x800000; memory_region_init_ram(vram, NULL, "vexpress.vram", vram_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(vram); memory_region_add_subregion(sysmem, map[VE_VIDEORAM], vram); @@ -747,7 +748,6 @@ static void vexpress_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - mc->name = TYPE_VEXPRESS_MACHINE; mc->desc = "ARM Versatile Express"; mc->init = vexpress_common_init; mc->block_default_type = IF_SCSI; @@ -759,10 +759,9 @@ static void vexpress_a9_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); VexpressMachineClass *vmc = VEXPRESS_MACHINE_CLASS(oc); - mc->name = TYPE_VEXPRESS_A9_MACHINE; mc->desc = "ARM Versatile Express for Cortex-A9"; - vmc->daughterboard = &a9_daughterboard;; + vmc->daughterboard = &a9_daughterboard; } static void vexpress_a15_class_init(ObjectClass *oc, void *data) @@ -770,7 +769,6 @@ static void vexpress_a15_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); VexpressMachineClass *vmc = VEXPRESS_MACHINE_CLASS(oc); - mc->name = TYPE_VEXPRESS_A15_MACHINE; mc->desc = "ARM Versatile Express for Cortex-A15"; vmc->daughterboard = &a15_daughterboard; @@ -805,4 +803,4 @@ static void vexpress_machine_init(void) type_register_static(&vexpress_a15_info); } -machine_init(vexpress_machine_init); +type_init(vexpress_machine_init); diff --git a/qemu/hw/arm/virt-acpi-build.c b/qemu/hw/arm/virt-acpi-build.c index f36514031..f51fe396c 100644 --- a/qemu/hw/arm/virt-acpi-build.c +++ b/qemu/hw/arm/virt-acpi-build.c @@ -26,6 +26,8 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "hw/arm/virt-acpi-build.h" #include "qemu/bitmap.h" @@ -43,20 +45,7 @@ #include "hw/pci/pci.h" #define ARM_SPI_BASE 32 - -typedef struct VirtAcpiCpuInfo { - DECLARE_BITMAP(found_cpus, VIRT_ACPI_CPU_ID_LIMIT); -} VirtAcpiCpuInfo; - -static void virt_acpi_get_cpu_info(VirtAcpiCpuInfo *cpuinfo) -{ - CPUState *cpu; - - memset(cpuinfo->found_cpus, 0, sizeof cpuinfo->found_cpus); - CPU_FOREACH(cpu) { - set_bit(cpu->cpu_index, cpuinfo->found_cpus); - } -} +#define ACPI_POWER_BUTTON_DEVICE "PWRB" static void acpi_dsdt_add_cpus(Aml *scope, int smp_cpus) { @@ -71,7 +60,7 @@ static void acpi_dsdt_add_cpus(Aml *scope, int smp_cpus) } static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap, - int uart_irq) + uint32_t uart_irq) { Aml *dev = aml_device("COM0"); aml_append(dev, aml_name_decl("_HID", aml_string("ARMH0011"))); @@ -82,7 +71,7 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap, uart_memmap->size, AML_READ_WRITE)); aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, - AML_EXCLUSIVE, uart_irq)); + AML_EXCLUSIVE, &uart_irq, 1)); aml_append(dev, aml_name_decl("_CRS", crs)); /* The _ADR entry is used to link this device to the UART described @@ -93,19 +82,16 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap, aml_append(scope, dev); } -static void acpi_dsdt_add_rtc(Aml *scope, const MemMapEntry *rtc_memmap, - int rtc_irq) +static void acpi_dsdt_add_fw_cfg(Aml *scope, const MemMapEntry *fw_cfg_memmap) { - Aml *dev = aml_device("RTC0"); - aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0013"))); - aml_append(dev, aml_name_decl("_UID", aml_int(0))); + Aml *dev = aml_device("FWCF"); + aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002"))); + /* device present, functioning, decoding, not shown in UI */ + aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); Aml *crs = aml_resource_template(); - aml_append(crs, aml_memory32_fixed(rtc_memmap->base, - rtc_memmap->size, AML_READ_WRITE)); - aml_append(crs, - aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, - AML_EXCLUSIVE, rtc_irq)); + aml_append(crs, aml_memory32_fixed(fw_cfg_memmap->base, + fw_cfg_memmap->size, AML_READ_WRITE)); aml_append(dev, aml_name_decl("_CRS", crs)); aml_append(scope, dev); } @@ -114,7 +100,7 @@ static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap) { Aml *dev, *crs; hwaddr base = flash_memmap->base; - hwaddr size = flash_memmap->size; + hwaddr size = flash_memmap->size / 2; dev = aml_device("FLS0"); aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015"))); @@ -136,14 +122,14 @@ static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap) static void acpi_dsdt_add_virtio(Aml *scope, const MemMapEntry *virtio_mmio_memmap, - int mmio_irq, int num) + uint32_t mmio_irq, int num) { hwaddr base = virtio_mmio_memmap->base; hwaddr size = virtio_mmio_memmap->size; - int irq = mmio_irq; int i; for (i = 0; i < num; i++) { + uint32_t irq = mmio_irq + i; Aml *dev = aml_device("VR%02u", i); aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0005"))); aml_append(dev, aml_name_decl("_UID", aml_int(i))); @@ -152,14 +138,15 @@ static void acpi_dsdt_add_virtio(Aml *scope, aml_append(crs, aml_memory32_fixed(base, size, AML_READ_WRITE)); aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, - AML_EXCLUSIVE, irq + i)); + AML_EXCLUSIVE, &irq, 1)); aml_append(dev, aml_name_decl("_CRS", crs)); aml_append(scope, dev); base += size; } } -static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq) +static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, + uint32_t irq, bool use_highmem) { Aml *method, *crs, *ifctx, *UUID, *ifctx1, *elsectx, *buf; int i, bus_no; @@ -179,6 +166,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq) aml_append(dev, aml_name_decl("_ADR", aml_int(0))); aml_append(dev, aml_name_decl("_UID", aml_string("PCI0"))); aml_append(dev, aml_name_decl("_STR", aml_unicode("PCIe 0 Device"))); + aml_append(dev, aml_name_decl("_CCA", aml_int(1))); /* Declare the PCI Routing Table. */ Aml *rt_pkg = aml_package(nr_pcie_buses * PCI_NUM_PINS); @@ -197,29 +185,30 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq) /* Create GSI link device */ for (i = 0; i < PCI_NUM_PINS; i++) { + uint32_t irqs = irq + i; Aml *dev_gsi = aml_device("GSI%d", i); aml_append(dev_gsi, aml_name_decl("_HID", aml_string("PNP0C0F"))); aml_append(dev_gsi, aml_name_decl("_UID", aml_int(0))); crs = aml_resource_template(); aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, - AML_EXCLUSIVE, irq + i)); + AML_EXCLUSIVE, &irqs, 1)); aml_append(dev_gsi, aml_name_decl("_PRS", crs)); crs = aml_resource_template(); aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, - AML_EXCLUSIVE, irq + i)); + AML_EXCLUSIVE, &irqs, 1)); aml_append(dev_gsi, aml_name_decl("_CRS", crs)); - method = aml_method("_SRS", 1); + method = aml_method("_SRS", 1, AML_NOTSERIALIZED); aml_append(dev_gsi, method); aml_append(dev, dev_gsi); } - method = aml_method("_CBA", 0); + method = aml_method("_CBA", 0, AML_NOTSERIALIZED); aml_append(method, aml_return(aml_int(base_ecam))); aml_append(dev, method); - method = aml_method("_CRS", 0); + method = aml_method("_CRS", 0, AML_NOTSERIALIZED); Aml *rbuf = aml_resource_template(); aml_append(rbuf, aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, @@ -234,6 +223,17 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq) AML_ENTIRE_RANGE, 0x0000, 0x0000, size_pio - 1, base_pio, size_pio)); + if (use_highmem) { + hwaddr base_mmio_high = memmap[VIRT_PCIE_MMIO_HIGH].base; + hwaddr size_mmio_high = memmap[VIRT_PCIE_MMIO_HIGH].size; + + aml_append(rbuf, + aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, + AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000, + base_mmio_high, base_mmio_high, 0x0000, + size_mmio_high)); + } + aml_append(method, aml_name_decl("RBUF", rbuf)); aml_append(method, aml_return(rbuf)); aml_append(dev, method); @@ -241,7 +241,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq) /* Declare an _OSC (OS Control Handoff) method */ aml_append(dev, aml_name_decl("SUPP", aml_int(0))); aml_append(dev, aml_name_decl("CTRL", aml_int(0))); - method = aml_method("_OSC", 4); + method = aml_method("_OSC", 4, AML_NOTSERIALIZED); aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1")); @@ -259,16 +259,16 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq) aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3")); aml_append(ifctx, aml_store(aml_name("CDW2"), aml_name("SUPP"))); aml_append(ifctx, aml_store(aml_name("CDW3"), aml_name("CTRL"))); - aml_append(ifctx, aml_store(aml_and(aml_name("CTRL"), aml_int(0x1D)), + aml_append(ifctx, aml_store(aml_and(aml_name("CTRL"), aml_int(0x1D), NULL), aml_name("CTRL"))); ifctx1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1)))); - aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x08)), + aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x08), NULL), aml_name("CDW1"))); aml_append(ifctx, ifctx1); ifctx1 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), aml_name("CTRL")))); - aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x10)), + aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x10), NULL), aml_name("CDW1"))); aml_append(ifctx, ifctx1); @@ -277,13 +277,13 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq) aml_append(method, ifctx); elsectx = aml_else(); - aml_append(elsectx, aml_store(aml_or(aml_name("CDW1"), aml_int(4)), + aml_append(elsectx, aml_store(aml_or(aml_name("CDW1"), aml_int(4), NULL), aml_name("CDW1"))); aml_append(elsectx, aml_return(aml_arg(3))); aml_append(method, elsectx); aml_append(dev, method); - method = aml_method("_DSM", 4); + method = aml_method("_DSM", 4, AML_NOTSERIALIZED); /* PCI Firmware Specification 3.0 * 4.6.1. _DSM for PCI Express Slot Information @@ -310,6 +310,46 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq) aml_append(scope, dev); } +static void acpi_dsdt_add_gpio(Aml *scope, const MemMapEntry *gpio_memmap, + uint32_t gpio_irq) +{ + Aml *dev = aml_device("GPO0"); + aml_append(dev, aml_name_decl("_HID", aml_string("ARMH0061"))); + aml_append(dev, aml_name_decl("_ADR", aml_int(0))); + aml_append(dev, aml_name_decl("_UID", aml_int(0))); + + Aml *crs = aml_resource_template(); + aml_append(crs, aml_memory32_fixed(gpio_memmap->base, gpio_memmap->size, + AML_READ_WRITE)); + aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, + AML_EXCLUSIVE, &gpio_irq, 1)); + aml_append(dev, aml_name_decl("_CRS", crs)); + + Aml *aei = aml_resource_template(); + /* Pin 3 for power button */ + const uint32_t pin_list[1] = {3}; + aml_append(aei, aml_gpio_int(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH, + AML_EXCLUSIVE, AML_PULL_UP, 0, pin_list, 1, + "GPO0", NULL, 0)); + aml_append(dev, aml_name_decl("_AEI", aei)); + + /* _E03 is handle for power button */ + Aml *method = aml_method("_E03", 0, AML_NOTSERIALIZED); + aml_append(method, aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE), + aml_int(0x80))); + aml_append(dev, method); + aml_append(scope, dev); +} + +static void acpi_dsdt_add_power_button(Aml *scope) +{ + Aml *dev = aml_device(ACPI_POWER_BUTTON_DEVICE); + aml_append(dev, aml_name_decl("_HID", aml_string("PNP0C0C"))); + aml_append(dev, aml_name_decl("_ADR", aml_int(0))); + aml_append(dev, aml_name_decl("_UID", aml_int(0))); + aml_append(scope, dev); +} + /* RSDP */ static GArray * build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt) @@ -334,7 +374,8 @@ build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt) rsdp->checksum = 0; /* Checksum to be filled by Guest linker */ bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE, - rsdp, rsdp, sizeof *rsdp, &rsdp->checksum); + rsdp_table, rsdp, sizeof *rsdp, + &rsdp->checksum); return rsdp_table; } @@ -368,7 +409,8 @@ build_spcr(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) spcr->pci_device_id = 0xffff; /* PCI Device ID: not a PCI device */ spcr->pci_vendor_id = 0xffff; /* PCI Vendor ID: not a PCI device */ - build_header(linker, table_data, (void *)spcr, "SPCR", sizeof(*spcr), 2); + build_header(linker, table_data, (void *)spcr, "SPCR", sizeof(*spcr), 2, + NULL, NULL); } static void @@ -387,7 +429,7 @@ build_mcfg(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) mcfg->allocation[0].end_bus_number = (memmap[VIRT_PCIE_ECAM].size / PCIE_MMCFG_SIZE_MIN) - 1; - build_header(linker, table_data, (void *)mcfg, "MCFG", len, 1); + build_header(linker, table_data, (void *)mcfg, "MCFG", len, 1, NULL, NULL); } /* GTDT */ @@ -403,7 +445,7 @@ build_gtdt(GArray *table_data, GArray *linker) gtdt->secure_el1_flags = ACPI_EDGE_SENSITIVE; gtdt->non_secure_el1_interrupt = ARCH_TIMER_NS_EL1_IRQ + 16; - gtdt->non_secure_el1_flags = ACPI_EDGE_SENSITIVE; + gtdt->non_secure_el1_flags = ACPI_EDGE_SENSITIVE | ACPI_GTDT_ALWAYS_ON; gtdt->virtual_timer_interrupt = ARCH_TIMER_VIRT_IRQ + 16; gtdt->virtual_timer_flags = ACPI_EDGE_SENSITIVE; @@ -413,13 +455,12 @@ build_gtdt(GArray *table_data, GArray *linker) build_header(linker, table_data, (void *)(table_data->data + gtdt_start), "GTDT", - table_data->len - gtdt_start, 2); + table_data->len - gtdt_start, 2, NULL, NULL); } /* MADT */ static void -build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info, - VirtAcpiCpuInfo *cpuinfo) +build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) { int madt_start = table_data->len; const MemMapEntry *memmap = guest_info->memmap; @@ -431,37 +472,49 @@ build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info, madt = acpi_data_push(table_data, sizeof *madt); + gicd = acpi_data_push(table_data, sizeof *gicd); + gicd->type = ACPI_APIC_GENERIC_DISTRIBUTOR; + gicd->length = sizeof(*gicd); + gicd->base_address = memmap[VIRT_GIC_DIST].base; + for (i = 0; i < guest_info->smp_cpus; i++) { AcpiMadtGenericInterrupt *gicc = acpi_data_push(table_data, sizeof *gicc); + ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(i)); + gicc->type = ACPI_APIC_GENERIC_INTERRUPT; gicc->length = sizeof(*gicc); - gicc->base_address = memmap[VIRT_GIC_CPU].base; + if (guest_info->gic_version == 2) { + gicc->base_address = memmap[VIRT_GIC_CPU].base; + } gicc->cpu_interface_number = i; - gicc->arm_mpidr = i; + gicc->arm_mpidr = armcpu->mp_affinity; gicc->uid = i; - if (test_bit(i, cpuinfo->found_cpus)) { - gicc->flags = cpu_to_le32(ACPI_GICC_ENABLED); - } + gicc->flags = cpu_to_le32(ACPI_GICC_ENABLED); } - gicd = acpi_data_push(table_data, sizeof *gicd); - gicd->type = ACPI_APIC_GENERIC_DISTRIBUTOR; - gicd->length = sizeof(*gicd); - gicd->base_address = memmap[VIRT_GIC_DIST].base; - - gic_msi = acpi_data_push(table_data, sizeof *gic_msi); - gic_msi->type = ACPI_APIC_GENERIC_MSI_FRAME; - gic_msi->length = sizeof(*gic_msi); - gic_msi->gic_msi_frame_id = 0; - gic_msi->base_address = cpu_to_le64(memmap[VIRT_GIC_V2M].base); - gic_msi->flags = cpu_to_le32(1); - gic_msi->spi_count = cpu_to_le16(NUM_GICV2M_SPIS); - gic_msi->spi_base = cpu_to_le16(irqmap[VIRT_GIC_V2M] + ARM_SPI_BASE); + if (guest_info->gic_version == 3) { + AcpiMadtGenericRedistributor *gicr = acpi_data_push(table_data, + sizeof *gicr); + + gicr->type = ACPI_APIC_GENERIC_REDISTRIBUTOR; + gicr->length = sizeof(*gicr); + gicr->base_address = cpu_to_le64(memmap[VIRT_GIC_REDIST].base); + gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST].size); + } else { + gic_msi = acpi_data_push(table_data, sizeof *gic_msi); + gic_msi->type = ACPI_APIC_GENERIC_MSI_FRAME; + gic_msi->length = sizeof(*gic_msi); + gic_msi->gic_msi_frame_id = 0; + gic_msi->base_address = cpu_to_le64(memmap[VIRT_GIC_V2M].base); + gic_msi->flags = cpu_to_le32(1); + gic_msi->spi_count = cpu_to_le16(NUM_GICV2M_SPIS); + gic_msi->spi_base = cpu_to_le16(irqmap[VIRT_GIC_V2M] + ARM_SPI_BASE); + } build_header(linker, table_data, (void *)(table_data->data + madt_start), "APIC", - table_data->len - madt_start, 3); + table_data->len - madt_start, 3, NULL, NULL); } /* FADT */ @@ -486,7 +539,7 @@ build_fadt(GArray *table_data, GArray *linker, unsigned dsdt) sizeof fadt->dsdt); build_header(linker, table_data, - (void *)fadt, "FACP", sizeof(*fadt), 5); + (void *)fadt, "FACP", sizeof(*fadt), 5, NULL, NULL); } /* DSDT */ @@ -501,16 +554,24 @@ build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) /* Reserve space for header */ acpi_data_push(dsdt->buf, sizeof(AcpiTableHeader)); + /* When booting the VM with UEFI, UEFI takes ownership of the RTC hardware. + * While UEFI can use libfdt to disable the RTC device node in the DTB that + * it passes to the OS, it cannot modify AML. Therefore, we won't generate + * the RTC ACPI device at all when using UEFI. + */ scope = aml_scope("\\_SB"); acpi_dsdt_add_cpus(scope, guest_info->smp_cpus); acpi_dsdt_add_uart(scope, &memmap[VIRT_UART], (irqmap[VIRT_UART] + ARM_SPI_BASE)); - acpi_dsdt_add_rtc(scope, &memmap[VIRT_RTC], - (irqmap[VIRT_RTC] + ARM_SPI_BASE)); acpi_dsdt_add_flash(scope, &memmap[VIRT_FLASH]); + acpi_dsdt_add_fw_cfg(scope, &memmap[VIRT_FW_CFG]); acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO], (irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS); - acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE)); + acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE), + guest_info->use_highmem); + acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO], + (irqmap[VIRT_GPIO] + ARM_SPI_BASE)); + acpi_dsdt_add_power_button(scope); aml_append(dsdt, scope); @@ -518,7 +579,7 @@ build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len); build_header(linker, table_data, (void *)(table_data->data + table_data->len - dsdt->buf->len), - "DSDT", dsdt->buf->len, 2); + "DSDT", dsdt->buf->len, 2, NULL, NULL); free_aml_allocator(); } @@ -538,11 +599,8 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) { GArray *table_offsets; unsigned dsdt, rsdt; - VirtAcpiCpuInfo cpuinfo; GArray *tables_blob = tables->table_data; - virt_acpi_get_cpu_info(&cpuinfo); - table_offsets = g_array_new(false, true /* clear */, sizeof(uint32_t)); @@ -569,7 +627,7 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) build_fadt(tables_blob, tables->linker, dsdt); acpi_add_table(table_offsets, tables_blob); - build_madt(tables_blob, tables->linker, guest_info, &cpuinfo); + build_madt(tables_blob, tables->linker, guest_info); acpi_add_table(table_offsets, tables_blob); build_gtdt(tables_blob, tables->linker); @@ -582,7 +640,7 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) /* RSDT is pointed to by RSDP */ rsdt = tables_blob->len; - build_rsdt(tables_blob, tables->linker, table_offsets); + build_rsdt(tables_blob, tables->linker, table_offsets, NULL, NULL); /* RSDP is in FSEG memory, so allocate it separately */ build_rsdp(tables->rsdp, tables->linker, rsdt); @@ -603,7 +661,7 @@ static void acpi_ram_update(MemoryRegion *mr, GArray *data) memory_region_set_dirty(mr, 0, size); } -static void virt_acpi_build_update(void *build_opaque, uint32_t offset) +static void virt_acpi_build_update(void *build_opaque) { AcpiBuildState *build_state = build_opaque; AcpiBuildTables tables; diff --git a/qemu/hw/arm/virt.c b/qemu/hw/arm/virt.c index 484689264..56d35c771 100644 --- a/qemu/hw/arm/virt.c +++ b/qemu/hw/arm/virt.c @@ -28,6 +28,8 @@ * This is essentially the same approach kvmtool uses. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/sysbus.h" #include "hw/arm/arm.h" #include "hw/arm/primecell.h" @@ -48,6 +50,11 @@ #include "hw/arm/sysbus-fdt.h" #include "hw/platform-bus.h" #include "hw/arm/fdt.h" +#include "hw/intc/arm_gic_common.h" +#include "kvm_arm.h" +#include "hw/smbios/smbios.h" +#include "qapi/visitor.h" +#include "standard-headers/linux/input.h" /* Number of external interrupt lines to configure the GIC with */ #define NUM_IRQS 256 @@ -67,6 +74,7 @@ typedef struct VirtBoardInfo { uint32_t clock_phandle; uint32_t gic_phandle; uint32_t v2m_phandle; + bool using_psci; } VirtBoardInfo; typedef struct { @@ -77,9 +85,11 @@ typedef struct { typedef struct { MachineState parent; bool secure; + bool highmem; + int32_t gic_version; } VirtMachineState; -#define TYPE_VIRT_MACHINE "virt" +#define TYPE_VIRT_MACHINE MACHINE_TYPE_NAME("virt") #define VIRT_MACHINE(obj) \ OBJECT_CHECK(VirtMachineState, (obj), TYPE_VIRT_MACHINE) #define VIRT_MACHINE_GET_CLASS(obj) \ @@ -87,6 +97,23 @@ typedef struct { #define VIRT_MACHINE_CLASS(klass) \ OBJECT_CLASS_CHECK(VirtMachineClass, klass, TYPE_VIRT_MACHINE) +/* RAM limit in GB. Since VIRT_MEM starts at the 1GB mark, this means + * RAM can go up to the 256GB mark, leaving 256GB of the physical + * address space unallocated and free for future use between 256G and 512G. + * If we need to provide more RAM to VMs in the future then we need to: + * * allocate a second bank of RAM starting at 2TB and working up + * * fix the DT and ACPI table generation code in QEMU to correctly + * report two split lumps of RAM to the guest + * * fix KVM in the host kernel to allow guests with >40 bit address spaces + * (We don't want to fill all the way up to 512GB with RAM because + * we might want it for non-RAM purposes later. Conversely it seems + * reasonable to assume that anybody configuring a VM with a quarter + * of a terabyte of RAM will be doing it on a host with more than a + * terabyte of physical address space.) + */ +#define RAMLIMIT_GB 255 +#define RAMLIMIT_BYTES (RAMLIMIT_GB * 1024ULL * 1024 * 1024) + /* Addresses and sizes of our components. * 0..128MB is space for a flash device so we can run bootrom code such as UEFI. * 128MB..256MB is used for miscellaneous device I/O. @@ -107,22 +134,33 @@ static const MemMapEntry a15memmap[] = { [VIRT_GIC_DIST] = { 0x08000000, 0x00010000 }, [VIRT_GIC_CPU] = { 0x08010000, 0x00010000 }, [VIRT_GIC_V2M] = { 0x08020000, 0x00001000 }, + /* The space in between here is reserved for GICv3 CPU/vCPU/HYP */ + [VIRT_GIC_ITS] = { 0x08080000, 0x00020000 }, + /* This redistributor space allows up to 2*64kB*123 CPUs */ + [VIRT_GIC_REDIST] = { 0x080A0000, 0x00F60000 }, [VIRT_UART] = { 0x09000000, 0x00001000 }, [VIRT_RTC] = { 0x09010000, 0x00001000 }, - [VIRT_FW_CFG] = { 0x09020000, 0x0000000a }, + [VIRT_FW_CFG] = { 0x09020000, 0x00000018 }, + [VIRT_GPIO] = { 0x09030000, 0x00001000 }, + [VIRT_SECURE_UART] = { 0x09040000, 0x00001000 }, [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 }, + [VIRT_SECURE_MEM] = { 0x0e000000, 0x01000000 }, [VIRT_PCIE_MMIO] = { 0x10000000, 0x2eff0000 }, [VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 }, [VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 }, - [VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 }, + [VIRT_MEM] = { 0x40000000, RAMLIMIT_BYTES }, + /* Second PCIe window, 512GB wide at the 512GB boundary */ + [VIRT_PCIE_MMIO_HIGH] = { 0x8000000000ULL, 0x8000000000ULL }, }; static const int a15irqmap[] = { [VIRT_UART] = 1, [VIRT_RTC] = 2, [VIRT_PCIE] = 3, /* ... to 6 */ + [VIRT_GPIO] = 7, + [VIRT_SECURE_UART] = 8, [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */ [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */ [VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */ @@ -212,6 +250,10 @@ static void fdt_add_psci_node(const VirtBoardInfo *vbi) void *fdt = vbi->fdt; ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0)); + if (!vbi->using_psci) { + return; + } + qemu_fdt_add_subnode(fdt, "/psci"); if (armcpu->psci_version == 2) { const char comp[] = "arm,psci-0.2\0arm,psci"; @@ -249,7 +291,7 @@ static void fdt_add_psci_node(const VirtBoardInfo *vbi) qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn); } -static void fdt_add_timer_nodes(const VirtBoardInfo *vbi) +static void fdt_add_timer_nodes(const VirtBoardInfo *vbi, int gictype) { /* Note that on A15 h/w these interrupts are level-triggered, * but for the GIC implementation provided by both QEMU and KVM @@ -258,8 +300,11 @@ static void fdt_add_timer_nodes(const VirtBoardInfo *vbi) ARMCPU *armcpu; uint32_t irqflags = GIC_FDT_IRQ_FLAGS_EDGE_LO_HI; - irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START, - GIC_FDT_IRQ_PPI_CPU_WIDTH, (1 << vbi->smp_cpus) - 1); + if (gictype == 2) { + irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START, + GIC_FDT_IRQ_PPI_CPU_WIDTH, + (1 << vbi->smp_cpus) - 1); + } qemu_fdt_add_subnode(vbi->fdt, "/timer"); @@ -272,6 +317,7 @@ static void fdt_add_timer_nodes(const VirtBoardInfo *vbi) qemu_fdt_setprop_string(vbi->fdt, "/timer", "compatible", "arm,armv7-timer"); } + qemu_fdt_setprop(vbi->fdt, "/timer", "always-on", NULL, 0); qemu_fdt_setprop_cells(vbi->fdt, "/timer", "interrupts", GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_S_EL1_IRQ, irqflags, GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_NS_EL1_IRQ, irqflags, @@ -282,9 +328,32 @@ static void fdt_add_timer_nodes(const VirtBoardInfo *vbi) static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi) { int cpu; + int addr_cells = 1; + + /* + * From Documentation/devicetree/bindings/arm/cpus.txt + * On ARM v8 64-bit systems value should be set to 2, + * that corresponds to the MPIDR_EL1 register size. + * If MPIDR_EL1[63:32] value is equal to 0 on all CPUs + * in the system, #address-cells can be set to 1, since + * MPIDR_EL1[63:32] bits are not used for CPUs + * identification. + * + * Here we actually don't know whether our system is 32- or 64-bit one. + * The simplest way to go is to examine affinity IDs of all our CPUs. If + * at least one of them has Aff3 populated, we set #address-cells to 2. + */ + for (cpu = 0; cpu < vbi->smp_cpus; cpu++) { + ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu)); + + if (armcpu->mp_affinity & ARM_AFF3_MASK) { + addr_cells = 2; + break; + } + } qemu_fdt_add_subnode(vbi->fdt, "/cpus"); - qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#address-cells", 0x1); + qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#address-cells", addr_cells); qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#size-cells", 0x0); for (cpu = vbi->smp_cpus - 1; cpu >= 0; cpu--) { @@ -296,12 +365,19 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi) qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", armcpu->dtb_compatible); - if (vbi->smp_cpus > 1) { + if (vbi->using_psci && vbi->smp_cpus > 1) { qemu_fdt_setprop_string(vbi->fdt, nodename, "enable-method", "psci"); } - qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", armcpu->mp_affinity); + if (addr_cells == 2) { + qemu_fdt_setprop_u64(vbi->fdt, nodename, "reg", + armcpu->mp_affinity); + } else { + qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", + armcpu->mp_affinity); + } + g_free(nodename); } } @@ -319,25 +395,36 @@ static void fdt_add_v2m_gic_node(VirtBoardInfo *vbi) qemu_fdt_setprop_cell(vbi->fdt, "/intc/v2m", "phandle", vbi->v2m_phandle); } -static void fdt_add_gic_node(VirtBoardInfo *vbi) +static void fdt_add_gic_node(VirtBoardInfo *vbi, int type) { vbi->gic_phandle = qemu_fdt_alloc_phandle(vbi->fdt); qemu_fdt_setprop_cell(vbi->fdt, "/", "interrupt-parent", vbi->gic_phandle); qemu_fdt_add_subnode(vbi->fdt, "/intc"); - /* 'cortex-a15-gic' means 'GIC v2' */ - qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible", - "arm,cortex-a15-gic"); qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#interrupt-cells", 3); qemu_fdt_setprop(vbi->fdt, "/intc", "interrupt-controller", NULL, 0); - qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg", - 2, vbi->memmap[VIRT_GIC_DIST].base, - 2, vbi->memmap[VIRT_GIC_DIST].size, - 2, vbi->memmap[VIRT_GIC_CPU].base, - 2, vbi->memmap[VIRT_GIC_CPU].size); qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#address-cells", 0x2); qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#size-cells", 0x2); qemu_fdt_setprop(vbi->fdt, "/intc", "ranges", NULL, 0); + if (type == 3) { + qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible", + "arm,gic-v3"); + qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg", + 2, vbi->memmap[VIRT_GIC_DIST].base, + 2, vbi->memmap[VIRT_GIC_DIST].size, + 2, vbi->memmap[VIRT_GIC_REDIST].base, + 2, vbi->memmap[VIRT_GIC_REDIST].size); + } else { + /* 'cortex-a15-gic' means 'GIC v2' */ + qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible", + "arm,cortex-a15-gic"); + qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg", + 2, vbi->memmap[VIRT_GIC_DIST].base, + 2, vbi->memmap[VIRT_GIC_DIST].size, + 2, vbi->memmap[VIRT_GIC_CPU].base, + 2, vbi->memmap[VIRT_GIC_CPU].size); + } + qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", vbi->gic_phandle); } @@ -360,29 +447,34 @@ static void create_v2m(VirtBoardInfo *vbi, qemu_irq *pic) fdt_add_v2m_gic_node(vbi); } -static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic) +static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, int type, bool secure) { - /* We create a standalone GIC v2 */ + /* We create a standalone GIC */ DeviceState *gicdev; SysBusDevice *gicbusdev; - const char *gictype = "arm_gic"; + const char *gictype; int i; - if (kvm_irqchip_in_kernel()) { - gictype = "kvm-arm-gic"; - } + gictype = (type == 3) ? gicv3_class_name() : gic_class_name(); gicdev = qdev_create(NULL, gictype); - qdev_prop_set_uint32(gicdev, "revision", 2); + qdev_prop_set_uint32(gicdev, "revision", type); qdev_prop_set_uint32(gicdev, "num-cpu", smp_cpus); /* Note that the num-irq property counts both internal and external * interrupts; there are always 32 of the former (mandated by GIC spec). */ qdev_prop_set_uint32(gicdev, "num-irq", NUM_IRQS + 32); + if (!kvm_irqchip_in_kernel()) { + qdev_prop_set_bit(gicdev, "has-security-extensions", secure); + } qdev_init_nofail(gicdev); gicbusdev = SYS_BUS_DEVICE(gicdev); sysbus_mmio_map(gicbusdev, 0, vbi->memmap[VIRT_GIC_DIST].base); - sysbus_mmio_map(gicbusdev, 1, vbi->memmap[VIRT_GIC_CPU].base); + if (type == 3) { + sysbus_mmio_map(gicbusdev, 1, vbi->memmap[VIRT_GIC_REDIST].base); + } else { + sysbus_mmio_map(gicbusdev, 1, vbi->memmap[VIRT_GIC_CPU].base); + } /* Wire the outputs from each CPU's generic timer to the * appropriate GIC PPI inputs, and the GIC's IRQ output to @@ -390,15 +482,23 @@ static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic) */ for (i = 0; i < smp_cpus; i++) { DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); - int ppibase = NUM_IRQS + i * 32; - /* physical timer; we wire it up to the non-secure timer's ID, - * since a real A15 always has TrustZone but QEMU doesn't. + int ppibase = NUM_IRQS + i * GIC_INTERNAL + GIC_NR_SGIS; + int irq; + /* Mapping from the output timer irq lines from the CPU to the + * GIC PPI inputs we use for the virt board. */ - qdev_connect_gpio_out(cpudev, 0, - qdev_get_gpio_in(gicdev, ppibase + 30)); - /* virtual timer */ - qdev_connect_gpio_out(cpudev, 1, - qdev_get_gpio_in(gicdev, ppibase + 27)); + const int timer_irq[] = { + [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ, + [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ, + [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, + [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ, + }; + + for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { + qdev_connect_gpio_out(cpudev, irq, + qdev_get_gpio_in(gicdev, + ppibase + timer_irq[irq])); + } sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); sysbus_connect_irq(gicbusdev, i + smp_cpus, @@ -409,21 +509,29 @@ static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic) pic[i] = qdev_get_gpio_in(gicdev, i); } - fdt_add_gic_node(vbi); + fdt_add_gic_node(vbi, type); - create_v2m(vbi, pic); + if (type == 2) { + create_v2m(vbi, pic); + } } -static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic) +static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic, int uart, + MemoryRegion *mem) { char *nodename; - hwaddr base = vbi->memmap[VIRT_UART].base; - hwaddr size = vbi->memmap[VIRT_UART].size; - int irq = vbi->irqmap[VIRT_UART]; + hwaddr base = vbi->memmap[uart].base; + hwaddr size = vbi->memmap[uart].size; + int irq = vbi->irqmap[uart]; const char compat[] = "arm,pl011\0arm,primecell"; const char clocknames[] = "uartclk\0apb_pclk"; + DeviceState *dev = qdev_create(NULL, "pl011"); + SysBusDevice *s = SYS_BUS_DEVICE(dev); - sysbus_create_simple("pl011", base, pic[irq]); + qdev_init_nofail(dev); + memory_region_add_subregion(mem, base, + sysbus_mmio_get_region(s, 0)); + sysbus_connect_irq(s, 0, pic[irq]); nodename = g_strdup_printf("/pl011@%" PRIx64, base); qemu_fdt_add_subnode(vbi->fdt, nodename); @@ -440,7 +548,14 @@ static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic) qemu_fdt_setprop(vbi->fdt, nodename, "clock-names", clocknames, sizeof(clocknames)); - qemu_fdt_setprop_string(vbi->fdt, "/chosen", "stdout-path", nodename); + if (uart == VIRT_UART) { + qemu_fdt_setprop_string(vbi->fdt, "/chosen", "stdout-path", nodename); + } else { + /* Mark as not usable by the normal world */ + qemu_fdt_setprop_string(vbi->fdt, nodename, "status", "disabled"); + qemu_fdt_setprop_string(vbi->fdt, nodename, "secure-status", "okay"); + } + g_free(nodename); } @@ -467,6 +582,64 @@ static void create_rtc(const VirtBoardInfo *vbi, qemu_irq *pic) g_free(nodename); } +static DeviceState *gpio_key_dev; +static void virt_powerdown_req(Notifier *n, void *opaque) +{ + /* use gpio Pin 3 for power button event */ + qemu_set_irq(qdev_get_gpio_in(gpio_key_dev, 0), 1); +} + +static Notifier virt_system_powerdown_notifier = { + .notify = virt_powerdown_req +}; + +static void create_gpio(const VirtBoardInfo *vbi, qemu_irq *pic) +{ + char *nodename; + DeviceState *pl061_dev; + hwaddr base = vbi->memmap[VIRT_GPIO].base; + hwaddr size = vbi->memmap[VIRT_GPIO].size; + int irq = vbi->irqmap[VIRT_GPIO]; + const char compat[] = "arm,pl061\0arm,primecell"; + + pl061_dev = sysbus_create_simple("pl061", base, pic[irq]); + + uint32_t phandle = qemu_fdt_alloc_phandle(vbi->fdt); + nodename = g_strdup_printf("/pl061@%" PRIx64, base); + qemu_fdt_add_subnode(vbi->fdt, nodename); + qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", + 2, base, 2, size); + qemu_fdt_setprop(vbi->fdt, nodename, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_cell(vbi->fdt, nodename, "#gpio-cells", 2); + qemu_fdt_setprop(vbi->fdt, nodename, "gpio-controller", NULL, 0); + qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, irq, + GIC_FDT_IRQ_FLAGS_LEVEL_HI); + qemu_fdt_setprop_cell(vbi->fdt, nodename, "clocks", vbi->clock_phandle); + qemu_fdt_setprop_string(vbi->fdt, nodename, "clock-names", "apb_pclk"); + qemu_fdt_setprop_cell(vbi->fdt, nodename, "phandle", phandle); + + gpio_key_dev = sysbus_create_simple("gpio-key", -1, + qdev_get_gpio_in(pl061_dev, 3)); + qemu_fdt_add_subnode(vbi->fdt, "/gpio-keys"); + qemu_fdt_setprop_string(vbi->fdt, "/gpio-keys", "compatible", "gpio-keys"); + qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys", "#size-cells", 0); + qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys", "#address-cells", 1); + + qemu_fdt_add_subnode(vbi->fdt, "/gpio-keys/poweroff"); + qemu_fdt_setprop_string(vbi->fdt, "/gpio-keys/poweroff", + "label", "GPIO Key Poweroff"); + qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys/poweroff", "linux,code", + KEY_POWER); + qemu_fdt_setprop_cells(vbi->fdt, "/gpio-keys/poweroff", + "gpios", phandle, 3, 0); + + /* connect powerdown request */ + qemu_register_powerdown_notifier(&virt_system_powerdown_notifier); + + g_free(nodename); +} + static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic) { int i; @@ -532,13 +705,15 @@ static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic) } static void create_one_flash(const char *name, hwaddr flashbase, - hwaddr flashsize) + hwaddr flashsize, const char *file, + MemoryRegion *sysmem) { /* Create and map a single flash device. We use the same * parameters as the flash devices on the Versatile Express board. */ DriveInfo *dinfo = drive_get_next(IF_PFLASH); DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); const uint64_t sectorlength = 256 * 1024; if (dinfo) { @@ -558,19 +733,10 @@ static void create_one_flash(const char *name, hwaddr flashbase, qdev_prop_set_string(dev, "name", name); qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, flashbase); -} - -static void create_flash(const VirtBoardInfo *vbi) -{ - /* Create two flash devices to fill the VIRT_FLASH space in the memmap. - * Any file passed via -bios goes in the first of these. - */ - hwaddr flashsize = vbi->memmap[VIRT_FLASH].size / 2; - hwaddr flashbase = vbi->memmap[VIRT_FLASH].base; - char *nodename; + memory_region_add_subregion(sysmem, flashbase, + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); - if (bios_name) { + if (file) { char *fn; int image_size; @@ -580,39 +746,82 @@ static void create_flash(const VirtBoardInfo *vbi) "but you cannot use both options at once"); exit(1); } - fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, file); if (!fn) { - error_report("Could not find ROM image '%s'", bios_name); + error_report("Could not find ROM image '%s'", file); exit(1); } - image_size = load_image_targphys(fn, flashbase, flashsize); + image_size = load_image_mr(fn, sysbus_mmio_get_region(sbd, 0)); g_free(fn); if (image_size < 0) { - error_report("Could not load ROM image '%s'", bios_name); + error_report("Could not load ROM image '%s'", file); exit(1); } } +} + +static void create_flash(const VirtBoardInfo *vbi, + MemoryRegion *sysmem, + MemoryRegion *secure_sysmem) +{ + /* Create two flash devices to fill the VIRT_FLASH space in the memmap. + * Any file passed via -bios goes in the first of these. + * sysmem is the system memory space. secure_sysmem is the secure view + * of the system, and the first flash device should be made visible only + * there. The second flash device is visible to both secure and nonsecure. + * If sysmem == secure_sysmem this means there is no separate Secure + * address space and both flash devices are generally visible. + */ + hwaddr flashsize = vbi->memmap[VIRT_FLASH].size / 2; + hwaddr flashbase = vbi->memmap[VIRT_FLASH].base; + char *nodename; - create_one_flash("virt.flash0", flashbase, flashsize); - create_one_flash("virt.flash1", flashbase + flashsize, flashsize); + create_one_flash("virt.flash0", flashbase, flashsize, + bios_name, secure_sysmem); + create_one_flash("virt.flash1", flashbase + flashsize, flashsize, + NULL, sysmem); - nodename = g_strdup_printf("/flash@%" PRIx64, flashbase); - qemu_fdt_add_subnode(vbi->fdt, nodename); - qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash"); - qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", - 2, flashbase, 2, flashsize, - 2, flashbase + flashsize, 2, flashsize); - qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4); - g_free(nodename); + if (sysmem == secure_sysmem) { + /* Report both flash devices as a single node in the DT */ + nodename = g_strdup_printf("/flash@%" PRIx64, flashbase); + qemu_fdt_add_subnode(vbi->fdt, nodename); + qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash"); + qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", + 2, flashbase, 2, flashsize, + 2, flashbase + flashsize, 2, flashsize); + qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4); + g_free(nodename); + } else { + /* Report the devices as separate nodes so we can mark one as + * only visible to the secure world. + */ + nodename = g_strdup_printf("/secflash@%" PRIx64, flashbase); + qemu_fdt_add_subnode(vbi->fdt, nodename); + qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash"); + qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", + 2, flashbase, 2, flashsize); + qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4); + qemu_fdt_setprop_string(vbi->fdt, nodename, "status", "disabled"); + qemu_fdt_setprop_string(vbi->fdt, nodename, "secure-status", "okay"); + g_free(nodename); + + nodename = g_strdup_printf("/flash@%" PRIx64, flashbase); + qemu_fdt_add_subnode(vbi->fdt, nodename); + qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash"); + qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", + 2, flashbase + flashsize, 2, flashsize); + qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4); + g_free(nodename); + } } -static void create_fw_cfg(const VirtBoardInfo *vbi) +static void create_fw_cfg(const VirtBoardInfo *vbi, AddressSpace *as) { hwaddr base = vbi->memmap[VIRT_FW_CFG].base; hwaddr size = vbi->memmap[VIRT_FW_CFG].size; char *nodename; - fw_cfg_init_mem_wide(base + 8, base, 8); + fw_cfg_init_mem_wide(base + 8, base, 8, base + 16, as); nodename = g_strdup_printf("/fw-cfg@%" PRIx64, base); qemu_fdt_add_subnode(vbi->fdt, nodename); @@ -658,10 +867,13 @@ static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle, 0x7 /* PCI irq */); } -static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic) +static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic, + bool use_highmem) { hwaddr base_mmio = vbi->memmap[VIRT_PCIE_MMIO].base; hwaddr size_mmio = vbi->memmap[VIRT_PCIE_MMIO].size; + hwaddr base_mmio_high = vbi->memmap[VIRT_PCIE_MMIO_HIGH].base; + hwaddr size_mmio_high = vbi->memmap[VIRT_PCIE_MMIO_HIGH].size; hwaddr base_pio = vbi->memmap[VIRT_PCIE_PIO].base; hwaddr size_pio = vbi->memmap[VIRT_PCIE_PIO].size; hwaddr base_ecam = vbi->memmap[VIRT_PCIE_ECAM].base; @@ -676,6 +888,7 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic) DeviceState *dev; char *nodename; int i; + PCIHostState *pci; dev = qdev_create(NULL, TYPE_GPEX_HOST); qdev_init_nofail(dev); @@ -698,6 +911,16 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic) mmio_reg, base_mmio, size_mmio); memory_region_add_subregion(get_system_memory(), base_mmio, mmio_alias); + if (use_highmem) { + /* Map high MMIO space */ + MemoryRegion *high_mmio_alias = g_new0(MemoryRegion, 1); + + memory_region_init_alias(high_mmio_alias, OBJECT(dev), "pcie-mmio-high", + mmio_reg, base_mmio_high, size_mmio_high); + memory_region_add_subregion(get_system_memory(), base_mmio_high, + high_mmio_alias); + } + /* Map IO port space */ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_pio); @@ -705,6 +928,19 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic) sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]); } + pci = PCI_HOST_BRIDGE(dev); + if (pci->bus) { + for (i = 0; i < nb_nics; i++) { + NICInfo *nd = &nd_table[i]; + + if (!nd->model) { + nd->model = g_strdup("virtio"); + } + + pci_nic_init_nofail(nd, pci->bus, nd->model, NULL); + } + } + nodename = g_strdup_printf("/pcie@%" PRIx64, base); qemu_fdt_add_subnode(vbi->fdt, nodename); qemu_fdt_setprop_string(vbi->fdt, nodename, @@ -715,15 +951,30 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic) qemu_fdt_setprop_cells(vbi->fdt, nodename, "bus-range", 0, nr_pcie_buses - 1); - qemu_fdt_setprop_cells(vbi->fdt, nodename, "msi-parent", vbi->v2m_phandle); + if (vbi->v2m_phandle) { + qemu_fdt_setprop_cells(vbi->fdt, nodename, "msi-parent", + vbi->v2m_phandle); + } qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", 2, base_ecam, 2, size_ecam); - qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "ranges", - 1, FDT_PCI_RANGE_IOPORT, 2, 0, - 2, base_pio, 2, size_pio, - 1, FDT_PCI_RANGE_MMIO, 2, base_mmio, - 2, base_mmio, 2, size_mmio); + + if (use_highmem) { + qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "ranges", + 1, FDT_PCI_RANGE_IOPORT, 2, 0, + 2, base_pio, 2, size_pio, + 1, FDT_PCI_RANGE_MMIO, 2, base_mmio, + 2, base_mmio, 2, size_mmio, + 1, FDT_PCI_RANGE_MMIO_64BIT, + 2, base_mmio_high, + 2, base_mmio_high, 2, size_mmio_high); + } else { + qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "ranges", + 1, FDT_PCI_RANGE_IOPORT, 2, 0, + 2, base_pio, 2, size_pio, + 1, FDT_PCI_RANGE_MMIO, 2, base_mmio, + 2, base_mmio, 2, size_mmio); + } qemu_fdt_setprop_cell(vbi->fdt, nodename, "#interrupt-cells", 1); create_pcie_irq_map(vbi, vbi->gic_phandle, irq, nodename); @@ -772,6 +1023,27 @@ static void create_platform_bus(VirtBoardInfo *vbi, qemu_irq *pic) sysbus_mmio_get_region(s, 0)); } +static void create_secure_ram(VirtBoardInfo *vbi, MemoryRegion *secure_sysmem) +{ + MemoryRegion *secram = g_new(MemoryRegion, 1); + char *nodename; + hwaddr base = vbi->memmap[VIRT_SECURE_MEM].base; + hwaddr size = vbi->memmap[VIRT_SECURE_MEM].size; + + memory_region_init_ram(secram, NULL, "virt.secure-ram", size, &error_fatal); + vmstate_register_ram_global(secram); + memory_region_add_subregion(secure_sysmem, base, secram); + + nodename = g_strdup_printf("/secram@%" PRIx64, base); + qemu_fdt_add_subnode(vbi->fdt, nodename); + qemu_fdt_setprop_string(vbi->fdt, nodename, "device_type", "memory"); + qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", 2, base, 2, size); + qemu_fdt_setprop_string(vbi->fdt, nodename, "status", "disabled"); + qemu_fdt_setprop_string(vbi->fdt, nodename, "secure-status", "okay"); + + g_free(nodename); +} + static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size) { const VirtBoardInfo *board = (const VirtBoardInfo *)binfo; @@ -780,12 +1052,42 @@ static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size) return board->fdt; } +static void virt_build_smbios(VirtGuestInfo *guest_info) +{ + FWCfgState *fw_cfg = guest_info->fw_cfg; + uint8_t *smbios_tables, *smbios_anchor; + size_t smbios_tables_len, smbios_anchor_len; + const char *product = "QEMU Virtual Machine"; + + if (!fw_cfg) { + return; + } + + if (kvm_enabled()) { + product = "KVM Virtual Machine"; + } + + smbios_set_defaults("QEMU", product, + "1.0", false, true, SMBIOS_ENTRY_POINT_30); + + smbios_get_tables(NULL, 0, &smbios_tables, &smbios_tables_len, + &smbios_anchor, &smbios_anchor_len); + + if (smbios_anchor) { + fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-tables", + smbios_tables, smbios_tables_len); + fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-anchor", + smbios_anchor, smbios_anchor_len); + } +} + static void virt_guest_info_machine_done(Notifier *notifier, void *data) { VirtGuestInfoState *guest_info_state = container_of(notifier, VirtGuestInfoState, machine_done); virt_acpi_setup(&guest_info_state->info); + virt_build_smbios(&guest_info_state->info); } static void machvirt_init(MachineState *machine) @@ -793,18 +1095,33 @@ static void machvirt_init(MachineState *machine) VirtMachineState *vms = VIRT_MACHINE(machine); qemu_irq pic[NUM_IRQS]; MemoryRegion *sysmem = get_system_memory(); - int n; + MemoryRegion *secure_sysmem = NULL; + int gic_version = vms->gic_version; + int n, virt_max_cpus; MemoryRegion *ram = g_new(MemoryRegion, 1); const char *cpu_model = machine->cpu_model; VirtBoardInfo *vbi; VirtGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state); VirtGuestInfo *guest_info = &guest_info_state->info; char **cpustr; + bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0); if (!cpu_model) { cpu_model = "cortex-a15"; } + /* We can probe only here because during property set + * KVM is not available yet + */ + if (!gic_version) { + gic_version = kvm_arm_vgic_probe(); + if (!gic_version) { + error_report("Unable to determine GIC version supported by host"); + error_printf("KVM acceleration is probably not supported\n"); + exit(1); + } + } + /* Separate the actual CPU model name from any appended features */ cpustr = g_strsplit(cpu_model, ",", 2); @@ -815,13 +1132,55 @@ static void machvirt_init(MachineState *machine) exit(1); } + /* If we have an EL3 boot ROM then the assumption is that it will + * implement PSCI itself, so disable QEMU's internal implementation + * so it doesn't get in the way. Instead of starting secondary + * CPUs in PSCI powerdown state we will start them all running and + * let the boot ROM sort them out. + * The usual case is that we do use QEMU's PSCI implementation. + */ + vbi->using_psci = !(vms->secure && firmware_loaded); + + /* The maximum number of CPUs depends on the GIC version, or on how + * many redistributors we can fit into the memory map. + */ + if (gic_version == 3) { + virt_max_cpus = vbi->memmap[VIRT_GIC_REDIST].size / 0x20000; + } else { + virt_max_cpus = GIC_NCPU; + } + + if (max_cpus > virt_max_cpus) { + error_report("Number of SMP CPUs requested (%d) exceeds max CPUs " + "supported by machine 'mach-virt' (%d)", + max_cpus, virt_max_cpus); + exit(1); + } + vbi->smp_cpus = smp_cpus; if (machine->ram_size > vbi->memmap[VIRT_MEM].size) { - error_report("mach-virt: cannot model more than 30GB RAM"); + error_report("mach-virt: cannot model more than %dGB RAM", RAMLIMIT_GB); exit(1); } + if (vms->secure) { + if (kvm_enabled()) { + error_report("mach-virt: KVM does not support Security extensions"); + exit(1); + } + + /* The Secure view of the world is the same as the NonSecure, + * but with a few extra devices. Create it as a container region + * containing the system memory at low priority; any secure-only + * devices go in at higher priority and take precedence. + */ + secure_sysmem = g_new(MemoryRegion, 1); + memory_region_init(secure_sysmem, OBJECT(machine), "secure-memory", + UINT64_MAX); + memory_region_add_subregion_overlap(secure_sysmem, 0, sysmem, -1); + } + create_fdt(vbi); for (n = 0; n < smp_cpus; n++) { @@ -832,7 +1191,7 @@ static void machvirt_init(MachineState *machine) char *cpuopts = g_strdup(cpustr[1]); if (!oc) { - fprintf(stderr, "Unable to find CPU definition\n"); + error_report("Unable to find CPU definition"); exit(1); } cpuobj = object_new(object_class_get_name(oc)); @@ -849,12 +1208,15 @@ static void machvirt_init(MachineState *machine) object_property_set_bool(cpuobj, false, "has_el3", NULL); } - object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC, "psci-conduit", - NULL); + if (vbi->using_psci) { + object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC, + "psci-conduit", NULL); - /* Secondary CPUs start in PSCI powered-down state */ - if (n > 0) { - object_property_set_bool(cpuobj, true, "start-powered-off", NULL); + /* Secondary CPUs start in PSCI powered-down state */ + if (n > 0) { + object_property_set_bool(cpuobj, true, + "start-powered-off", NULL); + } } if (object_property_find(cpuobj, "reset-cbar", NULL)) { @@ -862,10 +1224,17 @@ static void machvirt_init(MachineState *machine) "reset-cbar", &error_abort); } + object_property_set_link(cpuobj, OBJECT(sysmem), "memory", + &error_abort); + if (vms->secure) { + object_property_set_link(cpuobj, OBJECT(secure_sysmem), + "secure-memory", &error_abort); + } + object_property_set_bool(cpuobj, true, "realized", NULL); } g_strfreev(cpustr); - fdt_add_timer_nodes(vbi); + fdt_add_timer_nodes(vbi, gic_version); fdt_add_cpu_nodes(vbi); fdt_add_psci_node(vbi); @@ -873,15 +1242,22 @@ static void machvirt_init(MachineState *machine) machine->ram_size); memory_region_add_subregion(sysmem, vbi->memmap[VIRT_MEM].base, ram); - create_flash(vbi); + create_flash(vbi, sysmem, secure_sysmem ? secure_sysmem : sysmem); + + create_gic(vbi, pic, gic_version, vms->secure); - create_gic(vbi, pic); + create_uart(vbi, pic, VIRT_UART, sysmem); - create_uart(vbi, pic); + if (vms->secure) { + create_secure_ram(vbi, secure_sysmem); + create_uart(vbi, pic, VIRT_SECURE_UART, secure_sysmem); + } create_rtc(vbi, pic); - create_pcie(vbi, pic); + create_pcie(vbi, pic, vms->highmem); + + create_gpio(vbi, pic); /* Create mmio transports, so the user can create virtio backends * (which will be automatically plugged in to the transports). If @@ -889,13 +1265,15 @@ static void machvirt_init(MachineState *machine) */ create_virtio_devices(vbi, pic); - create_fw_cfg(vbi); + create_fw_cfg(vbi, &address_space_memory); rom_set_fw(fw_cfg_find()); guest_info->smp_cpus = smp_cpus; guest_info->fw_cfg = fw_cfg_find(); guest_info->memmap = vbi->memmap; guest_info->irqmap = vbi->irqmap; + guest_info->use_highmem = vms->highmem; + guest_info->gic_version = gic_version; guest_info_state->machine_done.notify = virt_guest_info_machine_done; qemu_add_machine_init_done_notifier(&guest_info_state->machine_done); @@ -907,7 +1285,7 @@ static void machvirt_init(MachineState *machine) vbi->bootinfo.board_id = -1; vbi->bootinfo.loader_start = vbi->memmap[VIRT_MEM].base; vbi->bootinfo.get_dtb = machvirt_dtb; - vbi->bootinfo.firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0); + vbi->bootinfo.firmware_loaded = firmware_loaded; arm_load_kernel(ARM_CPU(first_cpu), &vbi->bootinfo); /* @@ -933,45 +1311,125 @@ static void virt_set_secure(Object *obj, bool value, Error **errp) vms->secure = value; } -static void virt_instance_init(Object *obj) +static bool virt_get_highmem(Object *obj, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + return vms->highmem; +} + +static void virt_set_highmem(Object *obj, bool value, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + vms->highmem = value; +} + +static char *virt_get_gic_version(Object *obj, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + const char *val = vms->gic_version == 3 ? "3" : "2"; + + return g_strdup(val); +} + +static void virt_set_gic_version(Object *obj, const char *value, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + if (!strcmp(value, "3")) { + vms->gic_version = 3; + } else if (!strcmp(value, "2")) { + vms->gic_version = 2; + } else if (!strcmp(value, "host")) { + vms->gic_version = 0; /* Will probe later */ + } else { + error_setg(errp, "Invalid gic-version value"); + error_append_hint(errp, "Valid values are 3, 2, host.\n"); + } +} + +static void virt_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->init = machvirt_init; + /* Start max_cpus at the maximum QEMU supports. We'll further restrict + * it later in machvirt_init, where we have more information about the + * configuration of the particular instance. + */ + mc->max_cpus = MAX_CPUMASK_BITS; + mc->has_dynamic_sysbus = true; + mc->block_default_type = IF_VIRTIO; + mc->no_cdrom = 1; + mc->pci_allow_0_address = true; +} + +static const TypeInfo virt_machine_info = { + .name = TYPE_VIRT_MACHINE, + .parent = TYPE_MACHINE, + .abstract = true, + .instance_size = sizeof(VirtMachineState), + .class_size = sizeof(VirtMachineClass), + .class_init = virt_machine_class_init, +}; + +static void virt_2_6_instance_init(Object *obj) { VirtMachineState *vms = VIRT_MACHINE(obj); - /* EL3 is enabled by default on virt */ - vms->secure = true; + /* EL3 is disabled by default on virt: this makes us consistent + * between KVM and TCG for this board, and it also allows us to + * boot UEFI blobs which assume no TrustZone support. + */ + vms->secure = false; object_property_add_bool(obj, "secure", virt_get_secure, virt_set_secure, NULL); object_property_set_description(obj, "secure", "Set on/off to enable/disable the ARM " "Security Extensions (TrustZone)", NULL); + + /* High memory is enabled by default */ + vms->highmem = true; + object_property_add_bool(obj, "highmem", virt_get_highmem, + virt_set_highmem, NULL); + object_property_set_description(obj, "highmem", + "Set on/off to enable/disable using " + "physical address space above 32 bits", + NULL); + /* Default GIC type is v2 */ + vms->gic_version = 2; + object_property_add_str(obj, "gic-version", virt_get_gic_version, + virt_set_gic_version, NULL); + object_property_set_description(obj, "gic-version", + "Set GIC version. " + "Valid values are 2, 3 and host", NULL); } -static void virt_class_init(ObjectClass *oc, void *data) +static void virt_2_6_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + static GlobalProperty compat_props[] = { + { /* end of list */ } + }; - mc->name = TYPE_VIRT_MACHINE; - mc->desc = "ARM Virtual Machine", - mc->init = machvirt_init; - mc->max_cpus = 8; - mc->has_dynamic_sysbus = true; - mc->block_default_type = IF_VIRTIO; - mc->no_cdrom = 1; + mc->desc = "QEMU 2.6 ARM Virtual Machine"; + mc->alias = "virt"; + mc->compat_props = compat_props; } static const TypeInfo machvirt_info = { - .name = TYPE_VIRT_MACHINE, - .parent = TYPE_MACHINE, - .instance_size = sizeof(VirtMachineState), - .instance_init = virt_instance_init, - .class_size = sizeof(VirtMachineClass), - .class_init = virt_class_init, + .name = MACHINE_TYPE_NAME("virt-2.6"), + .parent = TYPE_VIRT_MACHINE, + .instance_init = virt_2_6_instance_init, + .class_init = virt_2_6_class_init, }; static void machvirt_machine_init(void) { + type_register_static(&virt_machine_info); type_register_static(&machvirt_info); } -machine_init(machvirt_machine_init); +type_init(machvirt_machine_init); diff --git a/qemu/hw/arm/xilinx_zynq.c b/qemu/hw/arm/xilinx_zynq.c index a4e7b5c63..98b17c9ae 100644 --- a/qemu/hw/arm/xilinx_zynq.c +++ b/qemu/hw/arm/xilinx_zynq.c @@ -15,6 +15,10 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "hw/arm/arm.h" #include "net/net.h" @@ -24,8 +28,10 @@ #include "hw/block/flash.h" #include "sysemu/block-backend.h" #include "hw/loader.h" -#include "hw/ssi.h" +#include "hw/misc/zynq-xadc.h" +#include "hw/ssi/ssi.h" #include "qemu/error-report.h" +#include "hw/sd/sd.h" #define NUM_SPI_FLASHES 4 #define NUM_QSPI_FLASHES 2 @@ -43,6 +49,45 @@ static const int dma_irqs[8] = { 46, 47, 48, 49, 72, 73, 74, 75 }; +#define BOARD_SETUP_ADDR 0x100 + +#define SLCR_LOCK_OFFSET 0x004 +#define SLCR_UNLOCK_OFFSET 0x008 +#define SLCR_ARM_PLL_OFFSET 0x100 + +#define SLCR_XILINX_UNLOCK_KEY 0xdf0d +#define SLCR_XILINX_LOCK_KEY 0x767b + +#define ARMV7_IMM16(x) (extract32((x), 0, 12) | \ + extract32((x), 12, 4) << 16) + +/* Write immediate val to address r0 + addr. r0 should contain base offset + * of the SLCR block. Clobbers r1. + */ + +#define SLCR_WRITE(addr, val) \ + 0xe3001000 + ARMV7_IMM16(extract32((val), 0, 16)), /* movw r1 ... */ \ + 0xe3401000 + ARMV7_IMM16(extract32((val), 16, 16)), /* movt r1 ... */ \ + 0xe5801000 + (addr) + +static void zynq_write_board_setup(ARMCPU *cpu, + const struct arm_boot_info *info) +{ + int n; + uint32_t board_setup_blob[] = { + 0xe3a004f8, /* mov r0, #0xf8000000 */ + SLCR_WRITE(SLCR_UNLOCK_OFFSET, SLCR_XILINX_UNLOCK_KEY), + SLCR_WRITE(SLCR_ARM_PLL_OFFSET, 0x00014008), + SLCR_WRITE(SLCR_LOCK_OFFSET, SLCR_XILINX_LOCK_KEY), + 0xe12fff1e, /* bx lr */ + }; + for (n = 0; n < ARRAY_SIZE(board_setup_blob); n++) { + board_setup_blob[n] = tswap32(board_setup_blob[n]); + } + rom_add_blob_fixed("board-setup", board_setup_blob, + sizeof(board_setup_blob), BOARD_SETUP_ADDR); +} + static struct arm_boot_info zynq_binfo = {}; static void gem_init(NICInfo *nd, uint32_t base, qemu_irq irq) @@ -113,10 +158,11 @@ static void zynq_init(MachineState *machine) MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ext_ram = g_new(MemoryRegion, 1); MemoryRegion *ocm_ram = g_new(MemoryRegion, 1); - DeviceState *dev; + DeviceState *dev, *carddev; SysBusDevice *busdev; + DriveInfo *di; + BlockBackend *blk; qemu_irq pic[64]; - Error *err = NULL; int n; if (!cpu_model) { @@ -131,29 +177,14 @@ static void zynq_init(MachineState *machine) * realization. */ if (object_property_find(OBJECT(cpu), "has_el3", NULL)) { - object_property_set_bool(OBJECT(cpu), false, "has_el3", &err); - if (err) { - error_report_err(err); - exit(1); - } - } - - object_property_set_int(OBJECT(cpu), ZYNQ_BOARD_MIDR, "midr", &err); - if (err) { - error_report_err(err); - exit(1); + object_property_set_bool(OBJECT(cpu), false, "has_el3", &error_fatal); } - object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar", &err); - if (err) { - error_report_err(err); - exit(1); - } - object_property_set_bool(OBJECT(cpu), true, "realized", &err); - if (err) { - error_report_err(err); - exit(1); - } + object_property_set_int(OBJECT(cpu), ZYNQ_BOARD_MIDR, "midr", + &error_fatal); + object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar", + &error_fatal); + object_property_set_bool(OBJECT(cpu), true, "realized", &error_fatal); /* max 2GB ram */ if (ram_size > 0x80000000) { @@ -167,7 +198,7 @@ static void zynq_init(MachineState *machine) /* 256K of on-chip memory */ memory_region_init_ram(ocm_ram, NULL, "zynq.ocm_ram", 256 << 10, - &error_abort); + &error_fatal); vmstate_register_ram_global(ocm_ram); memory_region_add_subregion(address_space_mem, 0xFFFC0000, ocm_ram); @@ -220,11 +251,28 @@ static void zynq_init(MachineState *machine) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xE0100000); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[56-IRQ_OFFSET]); + di = drive_get_next(IF_SD); + blk = di ? blk_by_legacy_dinfo(di) : NULL; + carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal); + dev = qdev_create(NULL, "generic-sdhci"); qdev_init_nofail(dev); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xE0101000); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[79-IRQ_OFFSET]); + di = drive_get_next(IF_SD); + blk = di ? blk_by_legacy_dinfo(di) : NULL; + carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal); + + dev = qdev_create(NULL, TYPE_ZYNQ_XADC); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xF8007100); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[39-IRQ_OFFSET]); + dev = qdev_create(NULL, "pl330"); qdev_prop_set_uint8(dev, "num_chnls", 8); qdev_prop_set_uint8(dev, "num_periph_req", 4); @@ -252,21 +300,19 @@ static void zynq_init(MachineState *machine) zynq_binfo.nb_cpus = 1; zynq_binfo.board_id = 0xd32; zynq_binfo.loader_start = 0; + zynq_binfo.board_setup_addr = BOARD_SETUP_ADDR; + zynq_binfo.write_board_setup = zynq_write_board_setup; + arm_load_kernel(ARM_CPU(first_cpu), &zynq_binfo); } -static QEMUMachine zynq_machine = { - .name = "xilinx-zynq-a9", - .desc = "Xilinx Zynq Platform Baseboard for Cortex-A9", - .init = zynq_init, - .block_default_type = IF_SCSI, - .max_cpus = 1, - .no_sdcard = 1, -}; - -static void zynq_machine_init(void) +static void zynq_machine_init(MachineClass *mc) { - qemu_register_machine(&zynq_machine); + mc->desc = "Xilinx Zynq Platform Baseboard for Cortex-A9"; + mc->init = zynq_init; + mc->block_default_type = IF_SCSI; + mc->max_cpus = 1; + mc->no_sdcard = 1; } -machine_init(zynq_machine_init); +DEFINE_MACHINE("xilinx-zynq-a9", zynq_machine_init) diff --git a/qemu/hw/arm/xlnx-ep108.c b/qemu/hw/arm/xlnx-ep108.c index f94da86cb..5f480182b 100644 --- a/qemu/hw/arm/xlnx-ep108.c +++ b/qemu/hw/arm/xlnx-ep108.c @@ -15,6 +15,10 @@ * for more details. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/arm/xlnx-zynqmp.h" #include "hw/boards.h" #include "qemu/error-report.h" @@ -25,42 +29,76 @@ typedef struct XlnxEP108 { MemoryRegion ddr_ram; } XlnxEP108; -/* Max 2GB RAM */ -#define EP108_MAX_RAM_SIZE 0x80000000ull - static struct arm_boot_info xlnx_ep108_binfo; static void xlnx_ep108_init(MachineState *machine) { XlnxEP108 *s = g_new0(XlnxEP108, 1); - Error *err = NULL; + int i; + uint64_t ram_size = machine->ram_size; + + /* Create the memory region to pass to the SoC */ + if (ram_size > XLNX_ZYNQMP_MAX_RAM_SIZE) { + error_report("ERROR: RAM size 0x%" PRIx64 " above max supported of " + "0x%llx", ram_size, + XLNX_ZYNQMP_MAX_RAM_SIZE); + exit(1); + } + + if (ram_size < 0x08000000) { + qemu_log("WARNING: RAM size 0x%" PRIx64 " is small for EP108", + ram_size); + } + + memory_region_allocate_system_memory(&s->ddr_ram, NULL, "ddr-ram", + ram_size); object_initialize(&s->soc, sizeof(s->soc), TYPE_XLNX_ZYNQMP); object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), &error_abort); - object_property_set_bool(OBJECT(&s->soc), true, "realized", &err); - if (err) { - error_report("%s", error_get_pretty(err)); - exit(1); - } + object_property_set_link(OBJECT(&s->soc), OBJECT(&s->ddr_ram), + "ddr-ram", &error_abort); - if (machine->ram_size > EP108_MAX_RAM_SIZE) { - error_report("WARNING: RAM size " RAM_ADDR_FMT " above max supported, " - "reduced to %llx", machine->ram_size, EP108_MAX_RAM_SIZE); - machine->ram_size = EP108_MAX_RAM_SIZE; - } + object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_fatal); + + /* Create and plug in the SD cards */ + for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) { + BusState *bus; + DriveInfo *di = drive_get_next(IF_SD); + BlockBackend *blk = di ? blk_by_legacy_dinfo(di) : NULL; + DeviceState *carddev; + char *bus_name; - if (machine->ram_size <= 0x08000000) { - qemu_log("WARNING: RAM size " RAM_ADDR_FMT " is small for EP108", - machine->ram_size); + bus_name = g_strdup_printf("sd-bus%d", i); + bus = qdev_get_child_bus(DEVICE(&s->soc), bus_name); + g_free(bus_name); + if (!bus) { + error_report("No SD bus found for SD card %d", i); + exit(1); + } + carddev = qdev_create(bus, TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + object_property_set_bool(OBJECT(carddev), true, "realized", + &error_fatal); } - memory_region_allocate_system_memory(&s->ddr_ram, NULL, "ddr-ram", - machine->ram_size); - memory_region_add_subregion(get_system_memory(), 0, &s->ddr_ram); + for (i = 0; i < XLNX_ZYNQMP_NUM_SPIS; i++) { + SSIBus *spi_bus; + DeviceState *flash_dev; + qemu_irq cs_line; + gchar *bus_name = g_strdup_printf("spi%d", i); + + spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(&s->soc), bus_name); + g_free(bus_name); - xlnx_ep108_binfo.ram_size = machine->ram_size; + flash_dev = ssi_create_slave(spi_bus, "sst25wf080"); + cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->soc.spi[i]), 1, cs_line); + } + + xlnx_ep108_binfo.ram_size = ram_size; xlnx_ep108_binfo.kernel_filename = machine->kernel_filename; xlnx_ep108_binfo.kernel_cmdline = machine->kernel_cmdline; xlnx_ep108_binfo.initrd_filename = machine->initrd_filename; @@ -68,15 +106,10 @@ static void xlnx_ep108_init(MachineState *machine) arm_load_kernel(s->soc.boot_cpu_ptr, &xlnx_ep108_binfo); } -static QEMUMachine xlnx_ep108_machine = { - .name = "xlnx-ep108", - .desc = "Xilinx ZynqMP EP108 board", - .init = xlnx_ep108_init, -}; - -static void xlnx_ep108_machine_init(void) +static void xlnx_ep108_machine_init(MachineClass *mc) { - qemu_register_machine(&xlnx_ep108_machine); + mc->desc = "Xilinx ZynqMP EP108 board"; + mc->init = xlnx_ep108_init; } -machine_init(xlnx_ep108_machine_init); +DEFINE_MACHINE("xlnx-ep108", xlnx_ep108_machine_init) diff --git a/qemu/hw/arm/xlnx-zynqmp.c b/qemu/hw/arm/xlnx-zynqmp.c index 62ef4ceb3..4d504da64 100644 --- a/qemu/hw/arm/xlnx-zynqmp.c +++ b/qemu/hw/arm/xlnx-zynqmp.c @@ -15,6 +15,10 @@ * for more details. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/arm/xlnx-zynqmp.h" #include "hw/intc/arm_gic_common.h" #include "exec/address-spaces.h" @@ -28,6 +32,10 @@ #define GIC_DIST_ADDR 0xf9010000 #define GIC_CPU_ADDR 0xf9020000 +#define SATA_INTR 133 +#define SATA_ADDR 0xFD0C0000 +#define SATA_NUM_PORTS 2 + static const uint64_t gem_addr[XLNX_ZYNQMP_NUM_GEMS] = { 0xFF0B0000, 0xFF0C0000, 0xFF0D0000, 0xFF0E0000, }; @@ -44,6 +52,22 @@ static const int uart_intr[XLNX_ZYNQMP_NUM_UARTS] = { 21, 22, }; +static const uint64_t sdhci_addr[XLNX_ZYNQMP_NUM_SDHCI] = { + 0xFF160000, 0xFF170000, +}; + +static const int sdhci_intr[XLNX_ZYNQMP_NUM_SDHCI] = { + 48, 49, +}; + +static const uint64_t spi_addr[XLNX_ZYNQMP_NUM_SPIS] = { + 0xFF040000, 0xFF050000, +}; + +static const int spi_intr[XLNX_ZYNQMP_NUM_SPIS] = { + 19, 20, +}; + typedef struct XlnxZynqMPGICRegion { int region_index; uint32_t address; @@ -78,6 +102,11 @@ static void xlnx_zynqmp_init(Object *obj) &error_abort); } + object_property_add_link(obj, "ddr-ram", TYPE_MEMORY_REGION, + (Object **)&s->ddr_ram, + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); + object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC); qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default()); @@ -90,6 +119,22 @@ static void xlnx_zynqmp_init(Object *obj) object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_CADENCE_UART); qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default()); } + + object_initialize(&s->sata, sizeof(s->sata), TYPE_SYSBUS_AHCI); + qdev_set_parent_bus(DEVICE(&s->sata), sysbus_get_default()); + + for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) { + object_initialize(&s->sdhci[i], sizeof(s->sdhci[i]), + TYPE_SYSBUS_SDHCI); + qdev_set_parent_bus(DEVICE(&s->sdhci[i]), + sysbus_get_default()); + } + + for (i = 0; i < XLNX_ZYNQMP_NUM_SPIS; i++) { + object_initialize(&s->spi[i], sizeof(s->spi[i]), + TYPE_XILINX_SPIPS); + qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default()); + } } static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) @@ -97,16 +142,63 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) XlnxZynqMPState *s = XLNX_ZYNQMP(dev); MemoryRegion *system_memory = get_system_memory(); uint8_t i; + uint64_t ram_size; const char *boot_cpu = s->boot_cpu ? s->boot_cpu : "apu-cpu[0]"; + ram_addr_t ddr_low_size, ddr_high_size; qemu_irq gic_spi[GIC_NUM_SPI_INTR]; Error *err = NULL; + ram_size = memory_region_size(s->ddr_ram); + + /* Create the DDR Memory Regions. User friendly checks should happen at + * the board level + */ + if (ram_size > XLNX_ZYNQMP_MAX_LOW_RAM_SIZE) { + /* The RAM size is above the maximum available for the low DDR. + * Create the high DDR memory region as well. + */ + assert(ram_size <= XLNX_ZYNQMP_MAX_RAM_SIZE); + ddr_low_size = XLNX_ZYNQMP_MAX_LOW_RAM_SIZE; + ddr_high_size = ram_size - XLNX_ZYNQMP_MAX_LOW_RAM_SIZE; + + memory_region_init_alias(&s->ddr_ram_high, NULL, + "ddr-ram-high", s->ddr_ram, + ddr_low_size, ddr_high_size); + memory_region_add_subregion(get_system_memory(), + XLNX_ZYNQMP_HIGH_RAM_START, + &s->ddr_ram_high); + } else { + /* RAM must be non-zero */ + assert(ram_size); + ddr_low_size = ram_size; + } + + memory_region_init_alias(&s->ddr_ram_low, NULL, + "ddr-ram-low", s->ddr_ram, + 0, ddr_low_size); + memory_region_add_subregion(get_system_memory(), 0, &s->ddr_ram_low); + + /* Create the four OCM banks */ + for (i = 0; i < XLNX_ZYNQMP_NUM_OCM_BANKS; i++) { + char *ocm_name = g_strdup_printf("zynqmp.ocm_ram_bank_%d", i); + + memory_region_init_ram(&s->ocm_ram[i], NULL, ocm_name, + XLNX_ZYNQMP_OCM_RAM_SIZE, &error_fatal); + vmstate_register_ram_global(&s->ocm_ram[i]); + memory_region_add_subregion(get_system_memory(), + XLNX_ZYNQMP_OCM_RAM_0_ADDRESS + + i * XLNX_ZYNQMP_OCM_RAM_SIZE, + &s->ocm_ram[i]); + + g_free(ocm_name); + } + qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", GIC_NUM_SPI_INTR + 32); qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2); qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", XLNX_ZYNQMP_NUM_APU_CPUS); object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); if (err) { - error_propagate((errp), (err)); + error_propagate(errp, err); return; } assert(ARRAY_SIZE(xlnx_zynqmp_gic_regions) == XLNX_ZYNQMP_GIC_REGIONS); @@ -147,16 +239,11 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) g_free(name); object_property_set_int(OBJECT(&s->apu_cpu[i]), GIC_BASE_ADDR, - "reset-cbar", &err); - if (err) { - error_propagate((errp), (err)); - return; - } - + "reset-cbar", &error_abort); object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized", &err); if (err) { - error_propagate((errp), (err)); + error_propagate(errp, err); return; } @@ -185,22 +272,17 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) g_free(name); object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "reset-hivecs", - &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - + &error_abort); object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "realized", &err); if (err) { - error_propagate((errp), (err)); + error_propagate(errp, err); return; } } if (!s->boot_cpu_ptr) { - error_setg(errp, "ZynqMP Boot cpu %s not found\n", boot_cpu); + error_setg(errp, "ZynqMP Boot cpu %s not found", boot_cpu); return; } @@ -217,7 +299,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) } object_property_set_bool(OBJECT(&s->gem[i]), true, "realized", &err); if (err) { - error_propagate((errp), (err)); + error_propagate(errp, err); return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem[i]), 0, gem_addr[i]); @@ -228,13 +310,62 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) for (i = 0; i < XLNX_ZYNQMP_NUM_UARTS; i++) { object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); if (err) { - error_propagate((errp), (err)); + error_propagate(errp, err); return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, uart_addr[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, gic_spi[uart_intr[i]]); } + + object_property_set_int(OBJECT(&s->sata), SATA_NUM_PORTS, "num-ports", + &error_abort); + object_property_set_bool(OBJECT(&s->sata), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sata), 0, SATA_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->sata), 0, gic_spi[SATA_INTR]); + + for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) { + char *bus_name; + + object_property_set_bool(OBJECT(&s->sdhci[i]), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdhci[i]), 0, + sdhci_addr[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci[i]), 0, + gic_spi[sdhci_intr[i]]); + /* Alias controller SD bus to the SoC itself */ + bus_name = g_strdup_printf("sd-bus%d", i); + object_property_add_alias(OBJECT(s), bus_name, + OBJECT(&s->sdhci[i]), "sd-bus", + &error_abort); + g_free(bus_name); + } + + for (i = 0; i < XLNX_ZYNQMP_NUM_SPIS; i++) { + gchar *bus_name; + + object_property_set_bool(OBJECT(&s->spi[i]), true, "realized", &err); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, spi_addr[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0, + gic_spi[spi_intr[i]]); + + /* Alias controller SPI bus to the SoC itself */ + bus_name = g_strdup_printf("spi%d", i); + object_property_add_alias(OBJECT(s), bus_name, + OBJECT(&s->spi[i]), "spi0", + &error_abort); + g_free(bus_name); + } } static Property xlnx_zynqmp_props[] = { @@ -248,6 +379,12 @@ static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data) dc->props = xlnx_zynqmp_props; dc->realize = xlnx_zynqmp_realize; + + /* + * Reason: creates an ARM CPU, thus use after free(), see + * arm_cpu_class_init() + */ + dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo xlnx_zynqmp_type_info = { diff --git a/qemu/hw/arm/z2.c b/qemu/hw/arm/z2.c index 17355479a..aea895a50 100644 --- a/qemu/hw/arm/z2.c +++ b/qemu/hw/arm/z2.c @@ -11,12 +11,13 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/arm/pxa.h" #include "hw/arm/arm.h" #include "hw/devices.h" #include "hw/i2c/i2c.h" -#include "hw/ssi.h" +#include "hw/ssi/ssi.h" #include "hw/boards.h" #include "sysemu/sysemu.h" #include "hw/block/flash.h" @@ -372,15 +373,10 @@ static void z2_init(MachineState *machine) arm_load_kernel(mpu->cpu, &z2_binfo); } -static QEMUMachine z2_machine = { - .name = "z2", - .desc = "Zipit Z2 (PXA27x)", - .init = z2_init, -}; - -static void z2_machine_init(void) +static void z2_machine_init(MachineClass *mc) { - qemu_register_machine(&z2_machine); + mc->desc = "Zipit Z2 (PXA27x)"; + mc->init = z2_init; } -machine_init(z2_machine_init); +DEFINE_MACHINE("z2", z2_machine_init) diff --git a/qemu/hw/audio/ac97.c b/qemu/hw/audio/ac97.c index b17383555..cbd959e0b 100644 --- a/qemu/hw/audio/ac97.c +++ b/qemu/hw/audio/ac97.c @@ -17,6 +17,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/audio/audio.h" #include "audio/audio.h" diff --git a/qemu/hw/audio/adlib.c b/qemu/hw/audio/adlib.c index 656eb3773..7836446fc 100644 --- a/qemu/hw/audio/adlib.c +++ b/qemu/hw/audio/adlib.c @@ -22,6 +22,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "hw/audio/audio.h" #include "audio/audio.h" @@ -57,11 +59,6 @@ void YMF262UpdateOneQEMU (int which, INT16 *dst, int length); #define SHIFT 1 #endif -#define IO_READ_PROTO(name) \ - uint32_t name (void *opaque, uint32_t nport) -#define IO_WRITE_PROTO(name) \ - void name (void *opaque, uint32_t nport, uint32_t val) - #define TYPE_ADLIB "adlib" #define ADLIB(obj) OBJECT_CHECK(AdlibState, (obj), TYPE_ADLIB) @@ -124,7 +121,7 @@ static void adlib_kill_timers (AdlibState *s) } } -static IO_WRITE_PROTO (adlib_write) +static void adlib_write(void *opaque, uint32_t nport, uint32_t val) { AdlibState *s = opaque; int a = nport & 3; @@ -141,7 +138,7 @@ static IO_WRITE_PROTO (adlib_write) #endif } -static IO_READ_PROTO (adlib_read) +static uint32_t adlib_read(void *opaque, uint32_t nport) { AdlibState *s = opaque; uint8_t data; @@ -173,7 +170,7 @@ static void timer_handler (int c, double interval_Sec) s->ticking[n] = 1; #ifdef DEBUG - interval = get_ticks_per_sec () * interval_Sec; + interval = NANOSECONDS_PER_SECOND * interval_Sec; exp = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + interval; s->exp[n] = exp; #endif diff --git a/qemu/hw/audio/cs4231.c b/qemu/hw/audio/cs4231.c index 6325a8cea..caf97c169 100644 --- a/qemu/hw/audio/cs4231.c +++ b/qemu/hw/audio/cs4231.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "trace.h" diff --git a/qemu/hw/audio/cs4231a.c b/qemu/hw/audio/cs4231a.c index f96f561c7..3ecd0582b 100644 --- a/qemu/hw/audio/cs4231a.c +++ b/qemu/hw/audio/cs4231a.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/audio/audio.h" #include "audio/audio.h" @@ -69,6 +70,7 @@ typedef struct CSState { uint32_t irq; uint32_t dma; uint32_t port; + IsaDma *isa_dma; int shift; int dma_running; int audio_free; @@ -264,6 +266,7 @@ static void cs_reset_voices (CSState *s, uint32_t val) { int xtal; struct audsettings as; + IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); #ifdef DEBUG_XLAW if (val == 0 || val == 32) @@ -327,7 +330,7 @@ static void cs_reset_voices (CSState *s, uint32_t val) if (s->dregs[Interface_Configuration] & PEN) { if (!s->dma_running) { - DMA_hold_DREQ (s->dma); + k->hold_DREQ(s->isa_dma, s->dma); AUD_set_active_out (s->voice, 1); s->transferred = 0; } @@ -335,7 +338,7 @@ static void cs_reset_voices (CSState *s, uint32_t val) } else { if (s->dma_running) { - DMA_release_DREQ (s->dma); + k->release_DREQ(s->isa_dma, s->dma); AUD_set_active_out (s->voice, 0); } s->dma_running = 0; @@ -344,7 +347,7 @@ static void cs_reset_voices (CSState *s, uint32_t val) error: if (s->dma_running) { - DMA_release_DREQ (s->dma); + k->release_DREQ(s->isa_dma, s->dma); AUD_set_active_out (s->voice, 0); } } @@ -452,7 +455,8 @@ static void cs_write (void *opaque, hwaddr addr, } else { if (s->dma_running) { - DMA_release_DREQ (s->dma); + IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); + k->release_DREQ(s->isa_dma, s->dma); AUD_set_active_out (s->voice, 0); s->dma_running = 0; } @@ -517,6 +521,7 @@ static int cs_write_audio (CSState *s, int nchan, int dma_pos, { int temp, net; uint8_t tmpbuf[4096]; + IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); temp = len; net = 0; @@ -531,7 +536,7 @@ static int cs_write_audio (CSState *s, int nchan, int dma_pos, to_copy = sizeof (tmpbuf); } - copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy); + copied = k->read_memory(s->isa_dma, nchan, tmpbuf, dma_pos, to_copy); if (s->tab) { int i; int16_t linbuf[4096]; @@ -599,7 +604,8 @@ static int cs4231a_pre_load (void *opaque) CSState *s = opaque; if (s->dma_running) { - DMA_release_DREQ (s->dma); + IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); + k->release_DREQ(s->isa_dma, s->dma); AUD_set_active_out (s->voice, 0); } s->dma_running = 0; @@ -655,13 +661,15 @@ static void cs4231a_realizefn (DeviceState *dev, Error **errp) { ISADevice *d = ISA_DEVICE (dev); CSState *s = CS4231A (dev); + IsaDmaClass *k; isa_init_irq (d, &s->pic, s->irq); + s->isa_dma = isa_get_dma(isa_bus_from_device(d), s->dma); + k = ISADMA_GET_CLASS(s->isa_dma); + k->register_channel(s->isa_dma, s->dma, cs_dma_read, s); isa_register_ioport (d, &s->ioports, s->port); - DMA_register_channel (s->dma, cs_dma_read, s); - AUD_register_card ("cs4231a", &s->card); } diff --git a/qemu/hw/audio/es1370.c b/qemu/hw/audio/es1370.c index 8e7bcf503..8449b5f43 100644 --- a/qemu/hw/audio/es1370.c +++ b/qemu/hw/audio/es1370.c @@ -26,6 +26,7 @@ /* #define VERBOSE_ES1370 */ #define SILENT_ES1370 +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/audio/audio.h" #include "audio/audio.h" @@ -157,11 +158,6 @@ static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 }; #define DAC2_CHANNEL 1 #define ADC_CHANNEL 2 -#define IO_READ_PROTO(n) \ -static uint32_t n (void *opaque, uint32_t addr) -#define IO_WRITE_PROTO(n) \ -static void n (void *opaque, uint32_t addr, uint32_t val) - static void es1370_dac1_callback (void *opaque, int free); static void es1370_dac2_callback (void *opaque, int free); static void es1370_adc_callback (void *opaque, int avail); @@ -293,6 +289,10 @@ struct chan_bits { uint32_t *old_freq, uint32_t *new_freq); }; +#define TYPE_ES1370 "ES1370" +#define ES1370(obj) \ + OBJECT_CHECK(ES1370State, (obj), TYPE_ES1370) + static void es1370_dac1_calc_freq (ES1370State *s, uint32_t ctl, uint32_t *old_freq, uint32_t *new_freq); static void es1370_dac2_and_adc_calc_freq (ES1370State *s, uint32_t ctl, @@ -474,7 +474,7 @@ static inline uint32_t es1370_fixup (ES1370State *s, uint32_t addr) return addr; } -IO_WRITE_PROTO (es1370_writeb) +static void es1370_writeb(void *opaque, uint32_t addr, uint32_t val) { ES1370State *s = opaque; uint32_t shift, mask; @@ -512,7 +512,7 @@ IO_WRITE_PROTO (es1370_writeb) } } -IO_WRITE_PROTO (es1370_writew) +static void es1370_writew(void *opaque, uint32_t addr, uint32_t val) { ES1370State *s = opaque; addr = es1370_fixup (s, addr); @@ -549,7 +549,7 @@ IO_WRITE_PROTO (es1370_writew) } } -IO_WRITE_PROTO (es1370_writel) +static void es1370_writel(void *opaque, uint32_t addr, uint32_t val) { ES1370State *s = opaque; struct chan *d = &s->chan[0]; @@ -615,7 +615,7 @@ IO_WRITE_PROTO (es1370_writel) } } -IO_READ_PROTO (es1370_readb) +static uint32_t es1370_readb(void *opaque, uint32_t addr) { ES1370State *s = opaque; uint32_t val; @@ -650,7 +650,7 @@ IO_READ_PROTO (es1370_readb) return val; } -IO_READ_PROTO (es1370_readw) +static uint32_t es1370_readw(void *opaque, uint32_t addr) { ES1370State *s = opaque; struct chan *d = &s->chan[0]; @@ -692,7 +692,7 @@ IO_READ_PROTO (es1370_readw) return val; } -IO_READ_PROTO (es1370_readl) +static uint32_t es1370_readl(void *opaque, uint32_t addr) { ES1370State *s = opaque; uint32_t val; @@ -1018,7 +1018,7 @@ static void es1370_on_reset (void *opaque) static void es1370_realize(PCIDevice *dev, Error **errp) { - ES1370State *s = DO_UPCAST (ES1370State, dev, dev); + ES1370State *s = ES1370(dev); uint8_t *c = s->dev.config; c[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_SLOW >> 8; @@ -1043,7 +1043,7 @@ static void es1370_realize(PCIDevice *dev, Error **errp) static int es1370_init (PCIBus *bus) { - pci_create_simple (bus, -1, "ES1370"); + pci_create_simple (bus, -1, TYPE_ES1370); return 0; } @@ -1064,7 +1064,7 @@ static void es1370_class_init (ObjectClass *klass, void *data) } static const TypeInfo es1370_info = { - .name = "ES1370", + .name = TYPE_ES1370, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof (ES1370State), .class_init = es1370_class_init, diff --git a/qemu/hw/audio/fmopl.c b/qemu/hw/audio/fmopl.c index adcef2d3b..731110fe8 100644 --- a/qemu/hw/audio/fmopl.c +++ b/qemu/hw/audio/fmopl.c @@ -32,10 +32,7 @@ #define HAS_YM3812 1 -#include -#include -#include -#include +#include "qemu/osdep.h" #include //#include "driver.h" /* use M.A.M.E. */ #include "fmopl.h" @@ -1177,7 +1174,7 @@ void OPLResetChip(FM_OPL *OPL) OPLWriteReg(OPL,0x03,0); /* Timer2 */ OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */ for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0); - /* reset OPerator paramater */ + /* reset operator parameter */ for( c = 0 ; c < OPL->max_ch ; c++ ) { OPL_CH *CH = &OPL->P_CH[c]; diff --git a/qemu/hw/audio/gus.c b/qemu/hw/audio/gus.c index 86223a954..9dd6947be 100644 --- a/qemu/hw/audio/gus.c +++ b/qemu/hw/audio/gus.c @@ -21,6 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "hw/audio/audio.h" #include "audio/audio.h" @@ -41,11 +43,6 @@ #define GUS_ENDIANNESS 0 #endif -#define IO_READ_PROTO(name) \ - static uint32_t name (void *opaque, uint32_t nport) -#define IO_WRITE_PROTO(name) \ - static void name (void *opaque, uint32_t nport, uint32_t val) - #define TYPE_GUS "gus" #define GUS(obj) OBJECT_CHECK (GUSState, (obj), TYPE_GUS) @@ -62,16 +59,17 @@ typedef struct GUSState { SWVoiceOut *voice; int64_t last_ticks; qemu_irq pic; + IsaDma *isa_dma; } GUSState; -IO_READ_PROTO (gus_readb) +static uint32_t gus_readb(void *opaque, uint32_t nport) { GUSState *s = opaque; return gus_read (&s->emu, nport, 1); } -IO_WRITE_PROTO (gus_writeb) +static void gus_writeb(void *opaque, uint32_t nport, uint32_t val) { GUSState *s = opaque; @@ -172,34 +170,36 @@ void GUS_irqclear (GUSEmuState *emu, int hwirq) #endif } -void GUS_dmarequest (GUSEmuState *der) +void GUS_dmarequest (GUSEmuState *emu) { - /* GUSState *s = (GUSState *) der; */ + GUSState *s = emu->opaque; + IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); ldebug ("dma request %d\n", der->gusdma); - DMA_hold_DREQ (der->gusdma); + k->hold_DREQ(s->isa_dma, s->emu.gusdma); } static int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) { GUSState *s = opaque; + IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); char tmpbuf[4096]; int pos = dma_pos, mode, left = dma_len - dma_pos; ldebug ("read DMA %#x %d\n", dma_pos, dma_len); - mode = DMA_get_channel_mode (s->emu.gusdma); + mode = k->has_autoinitialization(s->isa_dma, s->emu.gusdma); while (left) { int to_copy = audio_MIN ((size_t) left, sizeof (tmpbuf)); int copied; ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos); - copied = DMA_read_memory (nchan, tmpbuf, pos, to_copy); + copied = k->read_memory(s->isa_dma, nchan, tmpbuf, pos, to_copy); gus_dma_transferdata (&s->emu, tmpbuf, copied, left == copied); left -= copied; pos += copied; } if (((mode >> 4) & 1) == 0) { - DMA_release_DREQ (s->emu.gusdma); + k->release_DREQ(s->isa_dma, s->emu.gusdma); } return dma_len; } @@ -236,6 +236,7 @@ static void gus_realizefn (DeviceState *dev, Error **errp) { ISADevice *d = ISA_DEVICE(dev); GUSState *s = GUS (dev); + IsaDmaClass *k; struct audsettings as; AUD_register_card ("gus", &s->card); @@ -268,7 +269,9 @@ static void gus_realizefn (DeviceState *dev, Error **errp) isa_register_portio_list (d, (s->port + 0x100) & 0xf00, gus_portio_list2, s, "gus"); - DMA_register_channel (s->emu.gusdma, GUS_read_DMA, s); + s->isa_dma = isa_get_dma(isa_bus_from_device(d), s->emu.gusdma); + k = ISADMA_GET_CLASS(s->isa_dma); + k->register_channel(s->isa_dma, s->emu.gusdma, GUS_read_DMA, s); s->emu.himemaddr = s->himem; s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32; s->emu.opaque = s; diff --git a/qemu/hw/audio/gusemu.h b/qemu/hw/audio/gusemu.h index 331bb6fec..b7f075126 100644 --- a/qemu/hw/audio/gusemu.h +++ b/qemu/hw/audio/gusemu.h @@ -34,7 +34,6 @@ typedef signed char GUSchar; typedef signed short GUSsample; #else - #include typedef int8_t GUSchar; typedef uint8_t GUSbyte; typedef uint16_t GUSword; diff --git a/qemu/hw/audio/gusemu_hal.c b/qemu/hw/audio/gusemu_hal.c index 609669073..973d6b9f4 100644 --- a/qemu/hw/audio/gusemu_hal.c +++ b/qemu/hw/audio/gusemu_hal.c @@ -26,6 +26,7 @@ * TODO: check mixer: see 7.20 of sdk for panning pos (applies to all gus models?)? */ +#include "qemu/osdep.h" #include "gustate.h" #include "gusemu.h" diff --git a/qemu/hw/audio/gusemu_mixer.c b/qemu/hw/audio/gusemu_mixer.c index 6d8d9ced1..701e8fb0e 100644 --- a/qemu/hw/audio/gusemu_mixer.c +++ b/qemu/hw/audio/gusemu_mixer.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "gusemu.h" #include "gustate.h" diff --git a/qemu/hw/audio/hda-codec.c b/qemu/hw/audio/hda-codec.c index 3c03ff566..52d4640e6 100644 --- a/qemu/hw/audio/hda-codec.c +++ b/qemu/hw/audio/hda-codec.c @@ -17,6 +17,7 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "intel-hda.h" diff --git a/qemu/hw/audio/intel-hda.c b/qemu/hw/audio/intel-hda.c index 433463e9c..d372d4ab9 100644 --- a/qemu/hw/audio/intel-hda.c +++ b/qemu/hw/audio/intel-hda.c @@ -17,6 +17,7 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "hw/pci/msi.h" diff --git a/qemu/hw/audio/lm4549.c b/qemu/hw/audio/lm4549.c index 380ef603b..a46f2301a 100644 --- a/qemu/hw/audio/lm4549.c +++ b/qemu/hw/audio/lm4549.c @@ -13,6 +13,7 @@ * It supports only one playback voice and no record voice. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "audio/audio.h" #include "lm4549.h" @@ -33,7 +34,6 @@ do { printf("lm4549: " fmt , ## __VA_ARGS__); } while (0) #endif #if defined(LM4549_DUMP_DAC_INPUT) -#include static FILE *fp_dac_input; #endif diff --git a/qemu/hw/audio/marvell_88w8618.c b/qemu/hw/audio/marvell_88w8618.c index 86992677e..a6ca1806b 100644 --- a/qemu/hw/audio/marvell_88w8618.c +++ b/qemu/hw/audio/marvell_88w8618.c @@ -9,6 +9,7 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/hw.h" #include "hw/i2c/i2c.h" diff --git a/qemu/hw/audio/milkymist-ac97.c b/qemu/hw/audio/milkymist-ac97.c index 28f55e853..6a3b53674 100644 --- a/qemu/hw/audio/milkymist-ac97.c +++ b/qemu/hw/audio/milkymist-ac97.c @@ -21,6 +21,7 @@ * http://www.milkymist.org/socdoc/ac97.pdf */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "trace.h" diff --git a/qemu/hw/audio/pcspk.c b/qemu/hw/audio/pcspk.c index 5266fb545..f9afc8eda 100644 --- a/qemu/hw/audio/pcspk.c +++ b/qemu/hw/audio/pcspk.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/isa/isa.h" diff --git a/qemu/hw/audio/pl041.c b/qemu/hw/audio/pl041.c index 19982f247..4717bc9b9 100644 --- a/qemu/hw/audio/pl041.c +++ b/qemu/hw/audio/pl041.c @@ -20,6 +20,7 @@ * */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "pl041.h" diff --git a/qemu/hw/audio/sb16.c b/qemu/hw/audio/sb16.c index b052de5f7..3a4a57ac3 100644 --- a/qemu/hw/audio/sb16.c +++ b/qemu/hw/audio/sb16.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/audio/audio.h" #include "audio/audio.h" @@ -40,11 +41,6 @@ #define ldebug(...) #endif -#define IO_READ_PROTO(name) \ - uint32_t name (void *opaque, uint32_t nport) -#define IO_WRITE_PROTO(name) \ - void name (void *opaque, uint32_t nport, uint32_t val) - static const char e3[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992."; #define TYPE_SB16 "sb16" @@ -60,6 +56,8 @@ typedef struct SB16State { uint32_t hdma; uint32_t port; uint32_t ver; + IsaDma *isa_dma; + IsaDma *isa_hdma; int in_index; int out_data_len; @@ -170,16 +168,18 @@ static void speaker (SB16State *s, int on) static void control (SB16State *s, int hold) { int dma = s->use_hdma ? s->hdma : s->dma; + IsaDma *isa_dma = s->use_hdma ? s->isa_hdma : s->isa_dma; + IsaDmaClass *k = ISADMA_GET_CLASS(isa_dma); s->dma_running = hold; ldebug ("hold %d high %d dma %d\n", hold, s->use_hdma, dma); if (hold) { - DMA_hold_DREQ (dma); + k->hold_DREQ(isa_dma, dma); AUD_set_active_out (s->voice, 1); } else { - DMA_release_DREQ (dma); + k->release_DREQ(isa_dma, dma); AUD_set_active_out (s->voice, 0); } } @@ -762,8 +762,8 @@ static void complete (SB16State *s) freq = s->freq > 0 ? s->freq : 11025; samples = dsp_get_lohi (s) + 1; bytes = samples << s->fmt_stereo << (s->fmt_bits == 16); - ticks = muldiv64 (bytes, get_ticks_per_sec (), freq); - if (ticks < get_ticks_per_sec () / 1024) { + ticks = muldiv64(bytes, NANOSECONDS_PER_SECOND, freq); + if (ticks < NANOSECONDS_PER_SECOND / 1024) { qemu_irq_raise (s->pic); } else { @@ -881,7 +881,7 @@ static void reset (SB16State *s) legacy_reset (s); } -static IO_WRITE_PROTO (dsp_write) +static void dsp_write(void *opaque, uint32_t nport, uint32_t val) { SB16State *s = opaque; int iport; @@ -959,7 +959,7 @@ static IO_WRITE_PROTO (dsp_write) } } -static IO_READ_PROTO (dsp_read) +static uint32_t dsp_read(void *opaque, uint32_t nport) { SB16State *s = opaque; int iport, retval, ack = 0; @@ -1058,14 +1058,14 @@ static void reset_mixer (SB16State *s) } } -static IO_WRITE_PROTO (mixer_write_indexb) +static void mixer_write_indexb(void *opaque, uint32_t nport, uint32_t val) { SB16State *s = opaque; (void) nport; s->mixer_nreg = val; } -static IO_WRITE_PROTO (mixer_write_datab) +static void mixer_write_datab(void *opaque, uint32_t nport, uint32_t val) { SB16State *s = opaque; @@ -1121,7 +1121,7 @@ static IO_WRITE_PROTO (mixer_write_datab) s->mixer_regs[s->mixer_nreg] = val; } -static IO_READ_PROTO (mixer_read) +static uint32_t mixer_read(void *opaque, uint32_t nport) { SB16State *s = opaque; @@ -1141,6 +1141,8 @@ static IO_READ_PROTO (mixer_read) static int write_audio (SB16State *s, int nchan, int dma_pos, int dma_len, int len) { + IsaDma *isa_dma = nchan == s->dma ? s->isa_dma : s->isa_hdma; + IsaDmaClass *k = ISADMA_GET_CLASS(isa_dma); int temp, net; uint8_t tmpbuf[4096]; @@ -1157,7 +1159,7 @@ static int write_audio (SB16State *s, int nchan, int dma_pos, to_copy = sizeof (tmpbuf); } - copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy); + copied = k->read_memory(isa_dma, nchan, tmpbuf, dma_pos, to_copy); copied = AUD_write (s->voice, tmpbuf, copied); temp -= copied; @@ -1359,6 +1361,7 @@ static void sb16_realizefn (DeviceState *dev, Error **errp) { ISADevice *isadev = ISA_DEVICE (dev); SB16State *s = SB16 (dev); + IsaDmaClass *k; isa_init_irq (isadev, &s->pic, s->irq); @@ -1377,8 +1380,14 @@ static void sb16_realizefn (DeviceState *dev, Error **errp) isa_register_portio_list (isadev, s->port, sb16_ioport_list, s, "sb16"); - DMA_register_channel (s->hdma, SB_read_DMA, s); - DMA_register_channel (s->dma, SB_read_DMA, s); + s->isa_hdma = isa_get_dma(isa_bus_from_device(isadev), s->hdma); + k = ISADMA_GET_CLASS(s->isa_hdma); + k->register_channel(s->isa_hdma, s->hdma, SB_read_DMA, s); + + s->isa_dma = isa_get_dma(isa_bus_from_device(isadev), s->dma); + k = ISADMA_GET_CLASS(s->isa_dma); + k->register_channel(s->isa_dma, s->dma, SB_read_DMA, s); + s->can_write = 1; AUD_register_card ("sb16", &s->card); diff --git a/qemu/hw/audio/wm8750.c b/qemu/hw/audio/wm8750.c index b50b33140..0c6500e96 100644 --- a/qemu/hw/audio/wm8750.c +++ b/qemu/hw/audio/wm8750.c @@ -7,6 +7,7 @@ * This file is licensed under GNU GPL. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i2c/i2c.h" #include "audio/audio.h" diff --git a/qemu/hw/block/block.c b/qemu/hw/block/block.c index f7243e5b9..97a59d4fa 100644 --- a/qemu/hw/block/block.c +++ b/qemu/hw/block/block.c @@ -7,9 +7,11 @@ * later. See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "sysemu/blockdev.h" #include "sysemu/block-backend.h" #include "hw/block/block.h" +#include "qapi/error.h" #include "qemu/error-report.h" void blkconf_serial(BlockConf *conf, char **serial) diff --git a/qemu/hw/block/cdrom.c b/qemu/hw/block/cdrom.c index 4e1019c89..da937fe33 100644 --- a/qemu/hw/block/cdrom.c +++ b/qemu/hw/block/cdrom.c @@ -25,6 +25,7 @@ /* ??? Most of the ATAPI emulation is still in ide.c. It should be moved here. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/scsi/scsi.h" diff --git a/qemu/hw/block/dataplane/virtio-blk.c b/qemu/hw/block/dataplane/virtio-blk.c index 6106e4615..3cb97c9a2 100644 --- a/qemu/hw/block/dataplane/virtio-blk.c +++ b/qemu/hw/block/dataplane/virtio-blk.c @@ -12,13 +12,13 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "trace.h" #include "qemu/iov.h" #include "qemu/thread.h" #include "qemu/error-report.h" #include "hw/virtio/virtio-access.h" -#include "hw/virtio/dataplane/vring.h" -#include "hw/virtio/dataplane/vring-accessors.h" #include "sysemu/block-backend.h" #include "hw/virtio/virtio-blk.h" #include "virtio-blk.h" @@ -27,114 +27,93 @@ #include "qom/object_interfaces.h" struct VirtIOBlockDataPlane { - bool started; bool starting; bool stopping; - bool disabled; VirtIOBlkConf *conf; VirtIODevice *vdev; - Vring vring; /* virtqueue vring */ + VirtQueue *vq; /* virtqueue vring */ EventNotifier *guest_notifier; /* irq */ QEMUBH *bh; /* bh for guest notification */ + Notifier insert_notifier, remove_notifier; + /* Note that these EventNotifiers are assigned by value. This is * fine as long as you do not call event_notifier_cleanup on them * (because you don't own the file descriptor or handle; you just * use it). */ IOThread *iothread; - IOThread internal_iothread_obj; AioContext *ctx; - EventNotifier host_notifier; /* doorbell */ /* Operation blocker on BDS */ Error *blocker; - void (*saved_complete_request)(struct VirtIOBlockReq *req, - unsigned char status); }; /* Raise an interrupt to signal guest, if necessary */ -static void notify_guest(VirtIOBlockDataPlane *s) +void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s) { - if (!vring_should_notify(s->vdev, &s->vring)) { - return; - } - - event_notifier_set(s->guest_notifier); + qemu_bh_schedule(s->bh); } static void notify_guest_bh(void *opaque) { VirtIOBlockDataPlane *s = opaque; - notify_guest(s); + if (!virtio_should_notify(s->vdev, s->vq)) { + return; + } + + event_notifier_set(s->guest_notifier); } -static void complete_request_vring(VirtIOBlockReq *req, unsigned char status) +static void data_plane_set_up_op_blockers(VirtIOBlockDataPlane *s) { - VirtIOBlockDataPlane *s = req->dev->dataplane; - stb_p(&req->in->status, status); - - vring_push(s->vdev, &req->dev->dataplane->vring, &req->elem, req->in_len); + assert(!s->blocker); + error_setg(&s->blocker, "block device is in use by data plane"); + blk_op_block_all(s->conf->conf.blk, s->blocker); + blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_RESIZE, s->blocker); + blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker); + blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_BACKUP_SOURCE, s->blocker); + blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_CHANGE, s->blocker); + blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_COMMIT_SOURCE, s->blocker); + blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_COMMIT_TARGET, s->blocker); + blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_EJECT, s->blocker); + blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, + s->blocker); + blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, + s->blocker); + blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, + s->blocker); + blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_MIRROR_SOURCE, s->blocker); + blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_STREAM, s->blocker); + blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_REPLACE, s->blocker); +} - /* Suppress notification to guest by BH and its scheduled - * flag because requests are completed as a batch after io - * plug & unplug is introduced, and the BH can still be - * executed in dataplane aio context even after it is - * stopped, so needn't worry about notification loss with BH. - */ - qemu_bh_schedule(s->bh); +static void data_plane_remove_op_blockers(VirtIOBlockDataPlane *s) +{ + if (s->blocker) { + blk_op_unblock_all(s->conf->conf.blk, s->blocker); + error_free(s->blocker); + s->blocker = NULL; + } } -static void handle_notify(EventNotifier *e) +static void data_plane_blk_insert_notifier(Notifier *n, void *data) { - VirtIOBlockDataPlane *s = container_of(e, VirtIOBlockDataPlane, - host_notifier); - VirtIOBlock *vblk = VIRTIO_BLK(s->vdev); + VirtIOBlockDataPlane *s = container_of(n, VirtIOBlockDataPlane, + insert_notifier); + assert(s->conf->conf.blk == data); + data_plane_set_up_op_blockers(s); +} - event_notifier_test_and_clear(&s->host_notifier); - blk_io_plug(s->conf->conf.blk); - for (;;) { - MultiReqBuffer mrb = {}; - int ret; - - /* Disable guest->host notifies to avoid unnecessary vmexits */ - vring_disable_notification(s->vdev, &s->vring); - - for (;;) { - VirtIOBlockReq *req = virtio_blk_alloc_request(vblk); - - ret = vring_pop(s->vdev, &s->vring, &req->elem); - if (ret < 0) { - virtio_blk_free_request(req); - break; /* no more requests */ - } - - trace_virtio_blk_data_plane_process_request(s, req->elem.out_num, - req->elem.in_num, - req->elem.index); - - virtio_blk_handle_request(req, &mrb); - } - - if (mrb.num_reqs) { - virtio_blk_submit_multireq(s->conf->conf.blk, &mrb); - } - - if (likely(ret == -EAGAIN)) { /* vring emptied */ - /* Re-enable guest->host notifies and stop processing the vring. - * But if the guest has snuck in more descriptors, keep processing. - */ - if (vring_enable_notification(s->vdev, &s->vring)) { - break; - } - } else { /* fatal error */ - break; - } - } - blk_io_unplug(s->conf->conf.blk); +static void data_plane_blk_remove_notifier(Notifier *n, void *data) +{ + VirtIOBlockDataPlane *s = container_of(n, VirtIOBlockDataPlane, + remove_notifier); + assert(s->conf->conf.blk == data); + data_plane_remove_op_blockers(s); } /* Context: QEMU global mutex held */ @@ -143,20 +122,19 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, Error **errp) { VirtIOBlockDataPlane *s; - Error *local_err = NULL; BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); *dataplane = NULL; - if (!conf->data_plane && !conf->iothread) { + if (!conf->iothread) { return; } /* Don't try if transport does not support notifiers. */ if (!k->set_guest_notifiers || !k->set_host_notifier) { error_setg(errp, - "device is incompatible with x-data-plane " + "device is incompatible with dataplane " "(transport does not support notifiers)"); return; } @@ -164,11 +142,8 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, /* If dataplane is (re-)enabled while the guest is running there could be * block jobs that can conflict. */ - if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, - &local_err)) { - error_setg(errp, "cannot start dataplane thread: %s", - error_get_pretty(local_err)); - error_free(local_err); + if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { + error_prepend(errp, "cannot start dataplane thread: "); return; } @@ -179,36 +154,16 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, if (conf->iothread) { s->iothread = conf->iothread; object_ref(OBJECT(s->iothread)); - } else { - /* Create per-device IOThread if none specified. This is for - * x-data-plane option compatibility. If x-data-plane is removed we - * can drop this. - */ - object_initialize(&s->internal_iothread_obj, - sizeof(s->internal_iothread_obj), - TYPE_IOTHREAD); - user_creatable_complete(OBJECT(&s->internal_iothread_obj), &error_abort); - s->iothread = &s->internal_iothread_obj; } s->ctx = iothread_get_aio_context(s->iothread); s->bh = aio_bh_new(s->ctx, notify_guest_bh, s); - error_setg(&s->blocker, "block device is in use by data plane"); - blk_op_block_all(conf->conf.blk, s->blocker); - blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_RESIZE, s->blocker); - blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker); - blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_BACKUP_SOURCE, s->blocker); - blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_CHANGE, s->blocker); - blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT_SOURCE, s->blocker); - blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT_TARGET, s->blocker); - blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_EJECT, s->blocker); - blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, s->blocker); - blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, s->blocker); - blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, - s->blocker); - blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_MIRROR, s->blocker); - blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_STREAM, s->blocker); - blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_REPLACE, s->blocker); + s->insert_notifier.notify = data_plane_blk_insert_notifier; + s->remove_notifier.notify = data_plane_blk_remove_notifier; + blk_add_insert_bs_notifier(conf->conf.blk, &s->insert_notifier); + blk_add_remove_bs_notifier(conf->conf.blk, &s->remove_notifier); + + data_plane_set_up_op_blockers(s); *dataplane = s; } @@ -221,36 +176,39 @@ void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s) } virtio_blk_data_plane_stop(s); - blk_op_unblock_all(s->conf->conf.blk, s->blocker); - error_free(s->blocker); + data_plane_remove_op_blockers(s); + notifier_remove(&s->insert_notifier); + notifier_remove(&s->remove_notifier); qemu_bh_delete(s->bh); object_unref(OBJECT(s->iothread)); g_free(s); } +static void virtio_blk_data_plane_handle_output(VirtIODevice *vdev, + VirtQueue *vq) +{ + VirtIOBlock *s = (VirtIOBlock *)vdev; + + assert(s->dataplane); + assert(s->dataplane_started); + + virtio_blk_handle_vq(s, vq); +} + /* Context: QEMU global mutex held */ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); VirtIOBlock *vblk = VIRTIO_BLK(s->vdev); - VirtQueue *vq; int r; - if (s->started || s->disabled) { - return; - } - - if (s->starting) { + if (vblk->dataplane_started || s->starting) { return; } s->starting = true; - - vq = virtio_get_queue(s->vdev, 0); - if (!vring_setup(&s->vring, s->vdev, 0)) { - goto fail_vring; - } + s->vq = virtio_get_queue(s->vdev, 0); /* Set up guest notifier (irq) */ r = k->set_guest_notifiers(qbus->parent, 1, true); @@ -259,7 +217,7 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s) "ensure -enable-kvm is set\n", r); goto fail_guest_notifiers; } - s->guest_notifier = virtio_queue_get_guest_notifier(vq); + s->guest_notifier = virtio_queue_get_guest_notifier(s->vq); /* Set up virtqueue notify */ r = k->set_host_notifier(qbus->parent, 0, true); @@ -267,33 +225,29 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s) fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r); goto fail_host_notifier; } - s->host_notifier = *virtio_queue_get_host_notifier(vq); - - s->saved_complete_request = vblk->complete_request; - vblk->complete_request = complete_request_vring; s->starting = false; - s->started = true; + vblk->dataplane_started = true; trace_virtio_blk_data_plane_start(s); blk_set_aio_context(s->conf->conf.blk, s->ctx); /* Kick right away to begin processing requests already in vring */ - event_notifier_set(virtio_queue_get_host_notifier(vq)); + event_notifier_set(virtio_queue_get_host_notifier(s->vq)); /* Get this show started by hooking up our callbacks */ aio_context_acquire(s->ctx); - aio_set_event_notifier(s->ctx, &s->host_notifier, handle_notify); + virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx, + virtio_blk_data_plane_handle_output); aio_context_release(s->ctx); return; fail_host_notifier: k->set_guest_notifiers(qbus->parent, 1, false); fail_guest_notifiers: - vring_teardown(&s->vring, s->vdev, 0); - s->disabled = true; - fail_vring: + vblk->dataplane_disabled = true; s->starting = false; + vblk->dataplane_started = true; } /* Context: QEMU global mutex held */ @@ -303,39 +257,34 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s) VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); VirtIOBlock *vblk = VIRTIO_BLK(s->vdev); - - /* Better luck next time. */ - if (s->disabled) { - s->disabled = false; + if (!vblk->dataplane_started || s->stopping) { return; } - if (!s->started || s->stopping) { + + /* Better luck next time. */ + if (vblk->dataplane_disabled) { + vblk->dataplane_disabled = false; + vblk->dataplane_started = false; return; } s->stopping = true; - vblk->complete_request = s->saved_complete_request; trace_virtio_blk_data_plane_stop(s); aio_context_acquire(s->ctx); /* Stop notifications for new requests from guest */ - aio_set_event_notifier(s->ctx, &s->host_notifier, NULL); + virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx, NULL); /* Drain and switch bs back to the QEMU main loop */ blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context()); aio_context_release(s->ctx); - /* Sync vring state back to virtqueue so that non-dataplane request - * processing can continue when we disable the host notifier below. - */ - vring_teardown(&s->vring, s->vdev, 0); - k->set_host_notifier(qbus->parent, 0, false); /* Clean up guest notifier (irq) */ k->set_guest_notifiers(qbus->parent, 1, false); - s->started = false; + vblk->dataplane_started = false; s->stopping = false; } diff --git a/qemu/hw/block/dataplane/virtio-blk.h b/qemu/hw/block/dataplane/virtio-blk.h index c88d40e72..0714c11a2 100644 --- a/qemu/hw/block/dataplane/virtio-blk.h +++ b/qemu/hw/block/dataplane/virtio-blk.h @@ -26,5 +26,6 @@ void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s); void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s); void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s); void virtio_blk_data_plane_drain(VirtIOBlockDataPlane *s); +void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s); #endif /* HW_DATAPLANE_VIRTIO_BLK_H */ diff --git a/qemu/hw/block/ecc.c b/qemu/hw/block/ecc.c index 10bb23308..48311d260 100644 --- a/qemu/hw/block/ecc.c +++ b/qemu/hw/block/ecc.c @@ -11,6 +11,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/block/flash.h" diff --git a/qemu/hw/block/fdc.c b/qemu/hw/block/fdc.c index 5e1b67ee4..372227569 100644 --- a/qemu/hw/block/fdc.c +++ b/qemu/hw/block/fdc.c @@ -27,8 +27,10 @@ * way. There are changes in DOR register and DMA is not available. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/block/fdc.h" +#include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/timer.h" #include "hw/isa/isa.h" @@ -40,14 +42,15 @@ /********************************************************/ /* debug Floppy devices */ -//#define DEBUG_FLOPPY -#ifdef DEBUG_FLOPPY +#define DEBUG_FLOPPY 0 + #define FLOPPY_DPRINTF(fmt, ...) \ - do { printf("FLOPPY: " fmt , ## __VA_ARGS__); } while (0) -#else -#define FLOPPY_DPRINTF(fmt, ...) -#endif + do { \ + if (DEBUG_FLOPPY) { \ + fprintf(stderr, "FLOPPY: " fmt , ## __VA_ARGS__); \ + } \ + } while (0) /********************************************************/ /* Floppy drive emulation */ @@ -59,104 +62,82 @@ typedef enum FDriveRate { FDRIVE_RATE_1M = 0x03, /* 1 Mbps */ } FDriveRate; +typedef enum FDriveSize { + FDRIVE_SIZE_UNKNOWN, + FDRIVE_SIZE_350, + FDRIVE_SIZE_525, +} FDriveSize; + typedef struct FDFormat { - FDriveType drive; + FloppyDriveType drive; uint8_t last_sect; uint8_t max_track; uint8_t max_head; FDriveRate rate; } FDFormat; +/* In many cases, the total sector size of a format is enough to uniquely + * identify it. However, there are some total sector collisions between + * formats of different physical size, and these are noted below by + * highlighting the total sector size for entries with collisions. */ static const FDFormat fd_formats[] = { /* First entry is default format */ /* 1.44 MB 3"1/2 floppy disks */ - { FDRIVE_DRV_144, 18, 80, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_144, 20, 80, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_144, 21, 80, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_144, 21, 82, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_144, 21, 83, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_144, 22, 80, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_144, 23, 80, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_144, 24, 80, 1, FDRIVE_RATE_500K, }, + { FLOPPY_DRIVE_TYPE_144, 18, 80, 1, FDRIVE_RATE_500K, }, /* 3.5" 2880 */ + { FLOPPY_DRIVE_TYPE_144, 20, 80, 1, FDRIVE_RATE_500K, }, /* 3.5" 3200 */ + { FLOPPY_DRIVE_TYPE_144, 21, 80, 1, FDRIVE_RATE_500K, }, + { FLOPPY_DRIVE_TYPE_144, 21, 82, 1, FDRIVE_RATE_500K, }, + { FLOPPY_DRIVE_TYPE_144, 21, 83, 1, FDRIVE_RATE_500K, }, + { FLOPPY_DRIVE_TYPE_144, 22, 80, 1, FDRIVE_RATE_500K, }, + { FLOPPY_DRIVE_TYPE_144, 23, 80, 1, FDRIVE_RATE_500K, }, + { FLOPPY_DRIVE_TYPE_144, 24, 80, 1, FDRIVE_RATE_500K, }, /* 2.88 MB 3"1/2 floppy disks */ - { FDRIVE_DRV_288, 36, 80, 1, FDRIVE_RATE_1M, }, - { FDRIVE_DRV_288, 39, 80, 1, FDRIVE_RATE_1M, }, - { FDRIVE_DRV_288, 40, 80, 1, FDRIVE_RATE_1M, }, - { FDRIVE_DRV_288, 44, 80, 1, FDRIVE_RATE_1M, }, - { FDRIVE_DRV_288, 48, 80, 1, FDRIVE_RATE_1M, }, + { FLOPPY_DRIVE_TYPE_288, 36, 80, 1, FDRIVE_RATE_1M, }, + { FLOPPY_DRIVE_TYPE_288, 39, 80, 1, FDRIVE_RATE_1M, }, + { FLOPPY_DRIVE_TYPE_288, 40, 80, 1, FDRIVE_RATE_1M, }, + { FLOPPY_DRIVE_TYPE_288, 44, 80, 1, FDRIVE_RATE_1M, }, + { FLOPPY_DRIVE_TYPE_288, 48, 80, 1, FDRIVE_RATE_1M, }, /* 720 kB 3"1/2 floppy disks */ - { FDRIVE_DRV_144, 9, 80, 1, FDRIVE_RATE_250K, }, - { FDRIVE_DRV_144, 10, 80, 1, FDRIVE_RATE_250K, }, - { FDRIVE_DRV_144, 10, 82, 1, FDRIVE_RATE_250K, }, - { FDRIVE_DRV_144, 10, 83, 1, FDRIVE_RATE_250K, }, - { FDRIVE_DRV_144, 13, 80, 1, FDRIVE_RATE_250K, }, - { FDRIVE_DRV_144, 14, 80, 1, FDRIVE_RATE_250K, }, + { FLOPPY_DRIVE_TYPE_144, 9, 80, 1, FDRIVE_RATE_250K, }, /* 3.5" 1440 */ + { FLOPPY_DRIVE_TYPE_144, 10, 80, 1, FDRIVE_RATE_250K, }, + { FLOPPY_DRIVE_TYPE_144, 10, 82, 1, FDRIVE_RATE_250K, }, + { FLOPPY_DRIVE_TYPE_144, 10, 83, 1, FDRIVE_RATE_250K, }, + { FLOPPY_DRIVE_TYPE_144, 13, 80, 1, FDRIVE_RATE_250K, }, + { FLOPPY_DRIVE_TYPE_144, 14, 80, 1, FDRIVE_RATE_250K, }, /* 1.2 MB 5"1/4 floppy disks */ - { FDRIVE_DRV_120, 15, 80, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_120, 18, 80, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_120, 18, 82, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_120, 18, 83, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_120, 20, 80, 1, FDRIVE_RATE_500K, }, + { FLOPPY_DRIVE_TYPE_120, 15, 80, 1, FDRIVE_RATE_500K, }, + { FLOPPY_DRIVE_TYPE_120, 18, 80, 1, FDRIVE_RATE_500K, }, /* 5.25" 2880 */ + { FLOPPY_DRIVE_TYPE_120, 18, 82, 1, FDRIVE_RATE_500K, }, + { FLOPPY_DRIVE_TYPE_120, 18, 83, 1, FDRIVE_RATE_500K, }, + { FLOPPY_DRIVE_TYPE_120, 20, 80, 1, FDRIVE_RATE_500K, }, /* 5.25" 3200 */ /* 720 kB 5"1/4 floppy disks */ - { FDRIVE_DRV_120, 9, 80, 1, FDRIVE_RATE_250K, }, - { FDRIVE_DRV_120, 11, 80, 1, FDRIVE_RATE_250K, }, + { FLOPPY_DRIVE_TYPE_120, 9, 80, 1, FDRIVE_RATE_250K, }, /* 5.25" 1440 */ + { FLOPPY_DRIVE_TYPE_120, 11, 80, 1, FDRIVE_RATE_250K, }, /* 360 kB 5"1/4 floppy disks */ - { FDRIVE_DRV_120, 9, 40, 1, FDRIVE_RATE_300K, }, - { FDRIVE_DRV_120, 9, 40, 0, FDRIVE_RATE_300K, }, - { FDRIVE_DRV_120, 10, 41, 1, FDRIVE_RATE_300K, }, - { FDRIVE_DRV_120, 10, 42, 1, FDRIVE_RATE_300K, }, + { FLOPPY_DRIVE_TYPE_120, 9, 40, 1, FDRIVE_RATE_300K, }, /* 5.25" 720 */ + { FLOPPY_DRIVE_TYPE_120, 9, 40, 0, FDRIVE_RATE_300K, }, + { FLOPPY_DRIVE_TYPE_120, 10, 41, 1, FDRIVE_RATE_300K, }, + { FLOPPY_DRIVE_TYPE_120, 10, 42, 1, FDRIVE_RATE_300K, }, /* 320 kB 5"1/4 floppy disks */ - { FDRIVE_DRV_120, 8, 40, 1, FDRIVE_RATE_250K, }, - { FDRIVE_DRV_120, 8, 40, 0, FDRIVE_RATE_250K, }, + { FLOPPY_DRIVE_TYPE_120, 8, 40, 1, FDRIVE_RATE_250K, }, + { FLOPPY_DRIVE_TYPE_120, 8, 40, 0, FDRIVE_RATE_250K, }, /* 360 kB must match 5"1/4 better than 3"1/2... */ - { FDRIVE_DRV_144, 9, 80, 0, FDRIVE_RATE_250K, }, + { FLOPPY_DRIVE_TYPE_144, 9, 80, 0, FDRIVE_RATE_250K, }, /* 3.5" 720 */ /* end */ - { FDRIVE_DRV_NONE, -1, -1, 0, 0, }, + { FLOPPY_DRIVE_TYPE_NONE, -1, -1, 0, 0, }, }; -static void pick_geometry(BlockBackend *blk, int *nb_heads, - int *max_track, int *last_sect, - FDriveType drive_in, FDriveType *drive, - FDriveRate *rate) +static FDriveSize drive_size(FloppyDriveType drive) { - const FDFormat *parse; - uint64_t nb_sectors, size; - int i, first_match, match; - - blk_get_geometry(blk, &nb_sectors); - match = -1; - first_match = -1; - for (i = 0; ; i++) { - parse = &fd_formats[i]; - if (parse->drive == FDRIVE_DRV_NONE) { - break; - } - if (drive_in == parse->drive || - drive_in == FDRIVE_DRV_NONE) { - size = (parse->max_head + 1) * parse->max_track * - parse->last_sect; - if (nb_sectors == size) { - match = i; - break; - } - if (first_match == -1) { - first_match = i; - } - } - } - if (match == -1) { - if (first_match == -1) { - match = 1; - } else { - match = first_match; - } - parse = &fd_formats[match]; + switch (drive) { + case FLOPPY_DRIVE_TYPE_120: + return FDRIVE_SIZE_525; + case FLOPPY_DRIVE_TYPE_144: + case FLOPPY_DRIVE_TYPE_288: + return FDRIVE_SIZE_350; + default: + return FDRIVE_SIZE_UNKNOWN; } - *nb_heads = parse->max_head + 1; - *max_track = parse->max_track; - *last_sect = parse->last_sect; - *drive = parse->drive; - *rate = parse->rate; } #define GET_CUR_DRV(fdctrl) ((fdctrl)->cur_drv) @@ -178,13 +159,14 @@ typedef struct FDrive { FDCtrl *fdctrl; BlockBackend *blk; /* Drive status */ - FDriveType drive; + FloppyDriveType drive; /* CMOS drive type */ uint8_t perpendicular; /* 2.88 MB access mode */ /* Position */ uint8_t head; uint8_t track; uint8_t sect; /* Media */ + FloppyDriveType disk; /* Current disk type */ FDiskFlags flags; uint8_t last_sect; /* Nb sector per track */ uint8_t max_track; /* Nb of tracks */ @@ -192,16 +174,38 @@ typedef struct FDrive { uint8_t ro; /* Is read-only */ uint8_t media_changed; /* Is media changed */ uint8_t media_rate; /* Data rate of medium */ + + bool media_validated; /* Have we validated the media? */ } FDrive; + +static FloppyDriveType get_fallback_drive_type(FDrive *drv); + +/* Hack: FD_SEEK is expected to work on empty drives. However, QEMU + * currently goes through some pains to keep seeks within the bounds + * established by last_sect and max_track. Correcting this is difficult, + * as refactoring FDC code tends to expose nasty bugs in the Linux kernel. + * + * For now: allow empty drives to have large bounds so we can seek around, + * with the understanding that when a diskette is inserted, the bounds will + * properly tighten to match the geometry of that inserted medium. + */ +static void fd_empty_seek_hack(FDrive *drv) +{ + drv->last_sect = 0xFF; + drv->max_track = 0xFF; +} + static void fd_init(FDrive *drv) { /* Drive */ - drv->drive = FDRIVE_DRV_NONE; drv->perpendicular = 0; /* Disk */ + drv->disk = FLOPPY_DRIVE_TYPE_NONE; drv->last_sect = 0; drv->max_track = 0; + drv->ro = true; + drv->media_changed = 1; } #define NUM_SIDES(drv) ((drv)->flags & FDISK_DBL_SIDES ? 2 : 1) @@ -284,39 +288,149 @@ static void fd_recalibrate(FDrive *drv) fd_seek(drv, 0, 0, 1, 1); } +/** + * Determine geometry based on inserted diskette. + * Will not operate on an empty drive. + * + * @return: 0 on success, -1 if the drive is empty. + */ +static int pick_geometry(FDrive *drv) +{ + BlockBackend *blk = drv->blk; + const FDFormat *parse; + uint64_t nb_sectors, size; + int i; + int match, size_match, type_match; + bool magic = drv->drive == FLOPPY_DRIVE_TYPE_AUTO; + + /* We can only pick a geometry if we have a diskette. */ + if (!drv->blk || !blk_is_inserted(drv->blk) || + drv->drive == FLOPPY_DRIVE_TYPE_NONE) + { + return -1; + } + + /* We need to determine the likely geometry of the inserted medium. + * In order of preference, we look for: + * (1) The same drive type and number of sectors, + * (2) The same diskette size and number of sectors, + * (3) The same drive type. + * + * In all cases, matches that occur higher in the drive table will take + * precedence over matches that occur later in the table. + */ + blk_get_geometry(blk, &nb_sectors); + match = size_match = type_match = -1; + for (i = 0; ; i++) { + parse = &fd_formats[i]; + if (parse->drive == FLOPPY_DRIVE_TYPE_NONE) { + break; + } + size = (parse->max_head + 1) * parse->max_track * parse->last_sect; + if (nb_sectors == size) { + if (magic || parse->drive == drv->drive) { + /* (1) perfect match -- nb_sectors and drive type */ + goto out; + } else if (drive_size(parse->drive) == drive_size(drv->drive)) { + /* (2) size match -- nb_sectors and physical medium size */ + match = (match == -1) ? i : match; + } else { + /* This is suspicious -- Did the user misconfigure? */ + size_match = (size_match == -1) ? i : size_match; + } + } else if (type_match == -1) { + if ((parse->drive == drv->drive) || + (magic && (parse->drive == get_fallback_drive_type(drv)))) { + /* (3) type match -- nb_sectors mismatch, but matches the type + * specified explicitly by the user, or matches the fallback + * default type when using the drive autodetect mechanism */ + type_match = i; + } + } + } + + /* No exact match found */ + if (match == -1) { + if (size_match != -1) { + parse = &fd_formats[size_match]; + FLOPPY_DPRINTF("User requested floppy drive type '%s', " + "but inserted medium appears to be a " + "%"PRId64" sector '%s' type\n", + FloppyDriveType_lookup[drv->drive], + nb_sectors, + FloppyDriveType_lookup[parse->drive]); + } + match = type_match; + } + + /* No match of any kind found -- fd_format is misconfigured, abort. */ + if (match == -1) { + error_setg(&error_abort, "No candidate geometries present in table " + " for floppy drive type '%s'", + FloppyDriveType_lookup[drv->drive]); + } + + parse = &(fd_formats[match]); + + out: + if (parse->max_head == 0) { + drv->flags &= ~FDISK_DBL_SIDES; + } else { + drv->flags |= FDISK_DBL_SIDES; + } + drv->max_track = parse->max_track; + drv->last_sect = parse->last_sect; + drv->disk = parse->drive; + drv->media_rate = parse->rate; + return 0; +} + +static void pick_drive_type(FDrive *drv) +{ + if (drv->drive != FLOPPY_DRIVE_TYPE_AUTO) { + return; + } + + if (pick_geometry(drv) == 0) { + drv->drive = drv->disk; + } else { + drv->drive = get_fallback_drive_type(drv); + } + + g_assert(drv->drive != FLOPPY_DRIVE_TYPE_AUTO); +} + /* Revalidate a disk drive after a disk change */ static void fd_revalidate(FDrive *drv) { - int nb_heads, max_track, last_sect, ro; - FDriveType drive; - FDriveRate rate; + int rc; FLOPPY_DPRINTF("revalidate\n"); if (drv->blk != NULL) { - ro = blk_is_read_only(drv->blk); - pick_geometry(drv->blk, &nb_heads, &max_track, - &last_sect, drv->drive, &drive, &rate); + drv->ro = blk_is_read_only(drv->blk); if (!blk_is_inserted(drv->blk)) { FLOPPY_DPRINTF("No disk in drive\n"); - } else { - FLOPPY_DPRINTF("Floppy disk (%d h %d t %d s) %s\n", nb_heads, - max_track, last_sect, ro ? "ro" : "rw"); - } - if (nb_heads == 1) { - drv->flags &= ~FDISK_DBL_SIDES; - } else { - drv->flags |= FDISK_DBL_SIDES; + drv->disk = FLOPPY_DRIVE_TYPE_NONE; + fd_empty_seek_hack(drv); + } else if (!drv->media_validated) { + rc = pick_geometry(drv); + if (rc) { + FLOPPY_DPRINTF("Could not validate floppy drive media"); + } else { + drv->media_validated = true; + FLOPPY_DPRINTF("Floppy disk (%d h %d t %d s) %s\n", + (drv->flags & FDISK_DBL_SIDES) ? 2 : 1, + drv->max_track, drv->last_sect, + drv->ro ? "ro" : "rw"); + } } - drv->max_track = max_track; - drv->last_sect = last_sect; - drv->ro = ro; - drv->drive = drive; - drv->media_rate = rate; } else { FLOPPY_DPRINTF("No drive connected\n"); drv->last_sect = 0; drv->max_track = 0; drv->flags &= ~FDISK_DBL_SIDES; + drv->drive = FLOPPY_DRIVE_TYPE_NONE; + drv->disk = FLOPPY_DRIVE_TYPE_NONE; } } @@ -532,6 +646,7 @@ struct FDCtrl { QEMUTimer *result_timer; int dma_chann; uint8_t phase; + IsaDma *dma; /* Controller's identification */ uint8_t version; /* HW */ @@ -566,11 +681,17 @@ struct FDCtrl { FDrive drives[MAX_FD]; int reset_sensei; uint32_t check_media_rate; + FloppyDriveType fallback; /* type=auto failure fallback */ /* Timers state */ uint8_t timer0; uint8_t timer1; }; +static FloppyDriveType get_fallback_drive_type(FDrive *drv) +{ + return drv->fdctrl->fallback; +} + #define TYPE_SYSBUS_FDC "base-sysbus-fdc" #define SYSBUS_FDC(obj) OBJECT_CHECK(FDCtrlSysBus, (obj), TYPE_SYSBUS_FDC) @@ -1311,7 +1432,8 @@ static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0, fdctrl->fifo[6] = FD_SECTOR_SC; fdctrl->data_dir = FD_DIR_READ; if (!(fdctrl->msr & FD_MSR_NONDMA)) { - DMA_release_DREQ(fdctrl->dma_chann); + IsaDmaClass *k = ISADMA_GET_CLASS(fdctrl->dma); + k->release_DREQ(fdctrl->dma, fdctrl->dma_chann); } fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO; fdctrl->msr &= ~FD_MSR_NONDMA; @@ -1397,27 +1519,43 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction) } fdctrl->eot = fdctrl->fifo[6]; if (fdctrl->dor & FD_DOR_DMAEN) { - int dma_mode; + IsaDmaTransferMode dma_mode; + IsaDmaClass *k = ISADMA_GET_CLASS(fdctrl->dma); + bool dma_mode_ok; /* DMA transfer are enabled. Check if DMA channel is well programmed */ - dma_mode = DMA_get_channel_mode(fdctrl->dma_chann); - dma_mode = (dma_mode >> 2) & 3; + dma_mode = k->get_transfer_mode(fdctrl->dma, fdctrl->dma_chann); FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n", dma_mode, direction, (128 << fdctrl->fifo[5]) * (cur_drv->last_sect - ks + 1), fdctrl->data_len); - if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL || - direction == FD_DIR_SCANH) && dma_mode == 0) || - (direction == FD_DIR_WRITE && dma_mode == 2) || - (direction == FD_DIR_READ && dma_mode == 1) || - (direction == FD_DIR_VERIFY)) { + switch (direction) { + case FD_DIR_SCANE: + case FD_DIR_SCANL: + case FD_DIR_SCANH: + dma_mode_ok = (dma_mode == ISADMA_TRANSFER_VERIFY); + break; + case FD_DIR_WRITE: + dma_mode_ok = (dma_mode == ISADMA_TRANSFER_WRITE); + break; + case FD_DIR_READ: + dma_mode_ok = (dma_mode == ISADMA_TRANSFER_READ); + break; + case FD_DIR_VERIFY: + dma_mode_ok = true; + break; + default: + dma_mode_ok = false; + break; + } + if (dma_mode_ok) { /* No access is allowed until DMA transfer has completed */ fdctrl->msr &= ~FD_MSR_RQM; if (direction != FD_DIR_VERIFY) { /* Now, we just have to wait for the DMA controller to * recall us... */ - DMA_hold_DREQ(fdctrl->dma_chann); - DMA_schedule(fdctrl->dma_chann); + k->hold_DREQ(fdctrl->dma, fdctrl->dma_chann); + k->schedule(fdctrl->dma); } else { /* Start transfer */ fdctrl_transfer_handler(fdctrl, fdctrl->dma_chann, 0, @@ -1456,12 +1594,14 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, FDrive *cur_drv; int len, start_pos, rel_pos; uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00; + IsaDmaClass *k; fdctrl = opaque; if (fdctrl->msr & FD_MSR_RQM) { FLOPPY_DPRINTF("Not in DMA transfer mode !\n"); return 0; } + k = ISADMA_GET_CLASS(fdctrl->dma); cur_drv = get_cur_drv(fdctrl); if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL || fdctrl->data_dir == FD_DIR_SCANH) @@ -1500,8 +1640,8 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, switch (fdctrl->data_dir) { case FD_DIR_READ: /* READ commands */ - DMA_write_memory (nchan, fdctrl->fifo + rel_pos, - fdctrl->data_pos, len); + k->write_memory(fdctrl->dma, nchan, fdctrl->fifo + rel_pos, + fdctrl->data_pos, len); break; case FD_DIR_WRITE: /* WRITE commands */ @@ -1515,8 +1655,8 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, goto transfer_error; } - DMA_read_memory (nchan, fdctrl->fifo + rel_pos, - fdctrl->data_pos, len); + k->read_memory(fdctrl->dma, nchan, fdctrl->fifo + rel_pos, + fdctrl->data_pos, len); if (blk_write(cur_drv->blk, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { FLOPPY_DPRINTF("error writing sector %d\n", @@ -1533,7 +1673,8 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, { uint8_t tmpbuf[FD_SECTOR_LEN]; int ret; - DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len); + k->read_memory(fdctrl->dma, nchan, tmpbuf, fdctrl->data_pos, + len); ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len); if (ret == 0) { status2 = FD_SR2_SEH; @@ -1798,8 +1939,8 @@ static void fdctrl_handle_readid(FDCtrl *fdctrl, int direction) FDrive *cur_drv = get_cur_drv(fdctrl); cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; - timer_mod(fdctrl->result_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (get_ticks_per_sec() / 50)); + timer_mod(fdctrl->result_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (NANOSECONDS_PER_SECOND / 50)); } static void fdctrl_handle_format_track(FDCtrl *fdctrl, int direction) @@ -2185,6 +2326,7 @@ static void fdctrl_change_cb(void *opaque, bool load) FDrive *drive = opaque; drive->media_changed = 1; + drive->media_validated = false; fd_revalidate(drive); } @@ -2214,10 +2356,11 @@ static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp) } fd_init(drive); - fdctrl_change_cb(drive, 0); if (drive->blk) { blk_set_dev_ops(drive->blk, &fdctrl_block_ops, drive); + pick_drive_type(drive); } + fd_revalidate(drive); } } @@ -2233,10 +2376,12 @@ ISADevice *fdctrl_init_isa(ISABus *bus, DriveInfo **fds) dev = DEVICE(isadev); if (fds[0]) { - qdev_prop_set_drive_nofail(dev, "driveA", blk_by_legacy_dinfo(fds[0])); + qdev_prop_set_drive(dev, "driveA", blk_by_legacy_dinfo(fds[0]), + &error_fatal); } if (fds[1]) { - qdev_prop_set_drive_nofail(dev, "driveB", blk_by_legacy_dinfo(fds[1])); + qdev_prop_set_drive(dev, "driveB", blk_by_legacy_dinfo(fds[1]), + &error_fatal); } qdev_init_nofail(dev); @@ -2256,10 +2401,12 @@ void fdctrl_init_sysbus(qemu_irq irq, int dma_chann, fdctrl = &sys->state; fdctrl->dma_chann = dma_chann; /* FIXME */ if (fds[0]) { - qdev_prop_set_drive_nofail(dev, "driveA", blk_by_legacy_dinfo(fds[0])); + qdev_prop_set_drive(dev, "driveA", blk_by_legacy_dinfo(fds[0]), + &error_fatal); } if (fds[1]) { - qdev_prop_set_drive_nofail(dev, "driveB", blk_by_legacy_dinfo(fds[1])); + qdev_prop_set_drive(dev, "driveB", blk_by_legacy_dinfo(fds[1]), + &error_fatal); } qdev_init_nofail(dev); sbd = SYS_BUS_DEVICE(dev); @@ -2275,7 +2422,8 @@ void sun4m_fdctrl_init(qemu_irq irq, hwaddr io_base, dev = qdev_create(NULL, "SUNW,fdtwo"); if (fds[0]) { - qdev_prop_set_drive_nofail(dev, "drive", blk_by_legacy_dinfo(fds[0])); + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(fds[0]), + &error_fatal); } qdev_init_nofail(dev); sys = SYSBUS_FDC(dev); @@ -2289,6 +2437,10 @@ static void fdctrl_realize_common(FDCtrl *fdctrl, Error **errp) int i, j; static int command_tables_inited = 0; + if (fdctrl->fallback == FLOPPY_DRIVE_TYPE_AUTO) { + error_setg(errp, "Cannot choose a fallback FDrive type of 'auto'"); + } + /* Fill 'command_to_handler' lookup table */ if (!command_tables_inited) { command_tables_inited = 1; @@ -2312,7 +2464,11 @@ static void fdctrl_realize_common(FDCtrl *fdctrl, Error **errp) fdctrl->num_floppies = MAX_FD; if (fdctrl->dma_chann != -1) { - DMA_register_channel(fdctrl->dma_chann, &fdctrl_transfer_handler, fdctrl); + IsaDmaClass *k; + assert(fdctrl->dma); + k = ISADMA_GET_CLASS(fdctrl->dma); + k->register_channel(fdctrl->dma, fdctrl->dma_chann, + &fdctrl_transfer_handler, fdctrl); } fdctrl_connect_drives(fdctrl, errp); } @@ -2335,6 +2491,10 @@ static void isabus_fdc_realize(DeviceState *dev, Error **errp) isa_init_irq(isadev, &fdctrl->irq, isa->irq); fdctrl->dma_chann = isa->dma; + if (fdctrl->dma_chann != -1) { + fdctrl->dma = isa_get_dma(isa_bus_from_device(isadev), isa->dma); + assert(fdctrl->dma); + } qdev_set_legacy_instance_id(dev, isa->iobase, 2); fdctrl_realize_common(fdctrl, &err); @@ -2363,6 +2523,8 @@ static void sun4m_fdc_initfn(Object *obj) FDCtrlSysBus *sys = SYSBUS_FDC(obj); FDCtrl *fdctrl = &sys->state; + fdctrl->dma_chann = -1; + memory_region_init_io(&fdctrl->iomem, obj, &fdctrl_mem_strict_ops, fdctrl, "fdctrl", 0x08); sysbus_init_mmio(sbd, &fdctrl->iomem); @@ -2389,13 +2551,36 @@ static void sysbus_fdc_common_realize(DeviceState *dev, Error **errp) fdctrl_realize_common(fdctrl, errp); } -FDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i) +FloppyDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i) { FDCtrlISABus *isa = ISA_FDC(fdc); return isa->state.drives[i].drive; } +void isa_fdc_get_drive_max_chs(FloppyDriveType type, + uint8_t *maxc, uint8_t *maxh, uint8_t *maxs) +{ + const FDFormat *fdf; + + *maxc = *maxh = *maxs = 0; + for (fdf = fd_formats; fdf->drive != FLOPPY_DRIVE_TYPE_NONE; fdf++) { + if (fdf->drive != type) { + continue; + } + if (*maxc < fdf->max_track) { + *maxc = fdf->max_track; + } + if (*maxh < fdf->max_head) { + *maxh = fdf->max_head; + } + if (*maxs < fdf->last_sect) { + *maxs = fdf->last_sect; + } + } + (*maxc)--; +} + static const VMStateDescription vmstate_isa_fdc ={ .name = "fdc", .version_id = 2, @@ -2414,6 +2599,15 @@ static Property isa_fdc_properties[] = { DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.drives[1].blk), DEFINE_PROP_BIT("check_media_rate", FDCtrlISABus, state.check_media_rate, 0, true), + DEFINE_PROP_DEFAULT("fdtypeA", FDCtrlISABus, state.drives[0].drive, + FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, + FloppyDriveType), + DEFINE_PROP_DEFAULT("fdtypeB", FDCtrlISABus, state.drives[1].drive, + FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, + FloppyDriveType), + DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback, + FLOPPY_DRIVE_TYPE_288, qdev_prop_fdc_drive_type, + FloppyDriveType), DEFINE_PROP_END_OF_LIST(), }; @@ -2462,6 +2656,15 @@ static const VMStateDescription vmstate_sysbus_fdc ={ static Property sysbus_fdc_properties[] = { DEFINE_PROP_DRIVE("driveA", FDCtrlSysBus, state.drives[0].blk), DEFINE_PROP_DRIVE("driveB", FDCtrlSysBus, state.drives[1].blk), + DEFINE_PROP_DEFAULT("fdtypeA", FDCtrlSysBus, state.drives[0].drive, + FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, + FloppyDriveType), + DEFINE_PROP_DEFAULT("fdtypeB", FDCtrlSysBus, state.drives[1].drive, + FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, + FloppyDriveType), + DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback, + FLOPPY_DRIVE_TYPE_144, qdev_prop_fdc_drive_type, + FloppyDriveType), DEFINE_PROP_END_OF_LIST(), }; @@ -2482,6 +2685,12 @@ static const TypeInfo sysbus_fdc_info = { static Property sun4m_fdc_properties[] = { DEFINE_PROP_DRIVE("drive", FDCtrlSysBus, state.drives[0].blk), + DEFINE_PROP_DEFAULT("fdtype", FDCtrlSysBus, state.drives[0].drive, + FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, + FloppyDriveType), + DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback, + FLOPPY_DRIVE_TYPE_144, qdev_prop_fdc_drive_type, + FloppyDriveType), DEFINE_PROP_END_OF_LIST(), }; diff --git a/qemu/hw/block/hd-geometry.c b/qemu/hw/block/hd-geometry.c index b187878fa..6d02192db 100644 --- a/qemu/hw/block/hd-geometry.c +++ b/qemu/hw/block/hd-geometry.c @@ -30,6 +30,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "sysemu/block-backend.h" #include "hw/block/block.h" #include "trace.h" diff --git a/qemu/hw/block/m25p80.c b/qemu/hw/block/m25p80.c index efc43dde6..906b71257 100644 --- a/qemu/hw/block/m25p80.c +++ b/qemu/hw/block/m25p80.c @@ -21,10 +21,12 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" -#include "hw/ssi.h" +#include "hw/ssi/ssi.h" +#include "qemu/bitops.h" #ifndef M25P80_ERR_DEBUG #define M25P80_ERR_DEBUG 0 @@ -45,7 +47,10 @@ /* set to allow the page program command to write 0s back to 1. Useful for * modelling EEPROM with SPI flash command set */ -#define WR_1 0x100 +#define EEPROM 0x100 + +/* 16 MiB max in 3 byte address mode */ +#define MAX_3BYTES_SIZE 0x1000000 typedef struct FlashPartInfo { const char *part_name; @@ -60,7 +65,7 @@ typedef struct FlashPartInfo { uint32_t sector_size; uint32_t n_sectors; uint32_t page_size; - uint8_t flags; + uint16_t flags; } FlashPartInfo; /* adapted from linux */ @@ -78,6 +83,30 @@ typedef struct FlashPartInfo { #define JEDEC_WINBOND 0xEF #define JEDEC_SPANSION 0x01 +/* Numonyx (Micron) Configuration register macros */ +#define VCFG_DUMMY 0x1 +#define VCFG_WRAP_SEQUENTIAL 0x2 +#define NVCFG_XIP_MODE_DISABLED (7 << 9) +#define NVCFG_XIP_MODE_MASK (7 << 9) +#define VCFG_XIP_MODE_ENABLED (1 << 3) +#define CFG_DUMMY_CLK_LEN 4 +#define NVCFG_DUMMY_CLK_POS 12 +#define VCFG_DUMMY_CLK_POS 4 +#define EVCFG_OUT_DRIVER_STRENGHT_DEF 7 +#define EVCFG_VPP_ACCELERATOR (1 << 3) +#define EVCFG_RESET_HOLD_ENABLED (1 << 4) +#define NVCFG_DUAL_IO_MASK (1 << 2) +#define EVCFG_DUAL_IO_ENABLED (1 << 6) +#define NVCFG_QUAD_IO_MASK (1 << 3) +#define EVCFG_QUAD_IO_ENABLED (1 << 7) +#define NVCFG_4BYTE_ADDR_MASK (1 << 0) +#define NVCFG_LOWER_SEGMENT_MASK (1 << 1) +#define CFG_UPPER_128MB_SEG_ENABLED 0x3 + +/* Numonyx (Micron) Flag Status Register macros */ +#define FSR_4BYTE_ADDR_MODE_ENABLED 0x1 +#define FSR_FLASH_READY (1 << 7) + static const FlashPartInfo known_devices[] = { /* Atmel -- some are (confusingly) marketed as "DataFlash" */ { INFO("at25fs010", 0x1f6601, 0, 32 << 10, 4, ER_4K) }, @@ -94,6 +123,12 @@ static const FlashPartInfo known_devices[] = { { INFO("at45db081d", 0x1f2500, 0, 64 << 10, 16, ER_4K) }, + /* Atmel EEPROMS - it is assumed, that don't care bit in command + * is set to 0. Block protection is not supported. + */ + { INFO("at25128a-nonjedec", 0x0, 0, 1, 131072, EEPROM) }, + { INFO("at25256a-nonjedec", 0x0, 0, 1, 262144, EEPROM) }, + /* EON -- en25xxx */ { INFO("en25f32", 0x1c3116, 0, 64 << 10, 64, ER_4K) }, { INFO("en25p32", 0x1c2016, 0, 64 << 10, 64, 0) }, @@ -163,6 +198,7 @@ static const FlashPartInfo known_devices[] = { { INFO("sst25wf010", 0xbf2502, 0, 64 << 10, 2, ER_4K) }, { INFO("sst25wf020", 0xbf2503, 0, 64 << 10, 4, ER_4K) }, { INFO("sst25wf040", 0xbf2504, 0, 64 << 10, 8, ER_4K) }, + { INFO("sst25wf080", 0xbf2505, 0, 64 << 10, 16, ER_4K) }, /* ST Microelectronics -- newer production may have feature updates */ { INFO("m25p05", 0x202010, 0, 32 << 10, 2, 0) }, @@ -204,8 +240,9 @@ static const FlashPartInfo known_devices[] = { { INFO("w25q80bl", 0xef4014, 0, 64 << 10, 16, ER_4K) }, { INFO("w25q256", 0xef4019, 0, 64 << 10, 512, ER_4K) }, - /* Numonyx -- n25q128 */ { INFO("n25q128", 0x20ba18, 0, 64 << 10, 256, 0) }, + { INFO("n25q256a", 0x20ba19, 0, 64 << 10, 512, ER_4K) }, + { INFO("n25q512a", 0x20ba20, 0, 64 << 10, 1024, ER_4K) }, }; typedef enum { @@ -216,21 +253,49 @@ typedef enum { WREN = 0x6, JEDEC_READ = 0x9f, BULK_ERASE = 0xc7, + READ_FSR = 0x70, - READ = 0x3, - FAST_READ = 0xb, + READ = 0x03, + READ4 = 0x13, + FAST_READ = 0x0b, + FAST_READ4 = 0x0c, DOR = 0x3b, + DOR4 = 0x3c, QOR = 0x6b, + QOR4 = 0x6c, DIOR = 0xbb, + DIOR4 = 0xbc, QIOR = 0xeb, + QIOR4 = 0xec, - PP = 0x2, + PP = 0x02, + PP4 = 0x12, DPP = 0xa2, QPP = 0x32, ERASE_4K = 0x20, + ERASE4_4K = 0x21, ERASE_32K = 0x52, ERASE_SECTOR = 0xd8, + ERASE4_SECTOR = 0xdc, + + EN_4BYTE_ADDR = 0xB7, + EX_4BYTE_ADDR = 0xE9, + + EXTEND_ADDR_READ = 0xC8, + EXTEND_ADDR_WRITE = 0xC5, + + RESET_ENABLE = 0x66, + RESET_MEMORY = 0x99, + + RNVCR = 0xB5, + WNVCR = 0xB1, + + RVCR = 0x85, + WVCR = 0x81, + + REVCR = 0x65, + WEVCR = 0x61, } FlashCMD; typedef enum { @@ -244,8 +309,6 @@ typedef enum { typedef struct Flash { SSISlave parent_obj; - uint32_t r; - BlockBackend *blk; uint8_t *storage; @@ -259,7 +322,13 @@ typedef struct Flash { uint8_t needed_bytes; uint8_t cmd_in_progress; uint64_t cur_addr; + uint32_t nonvolatile_cfg; + uint32_t volatile_cfg; + uint32_t enh_volatile_cfg; bool write_enable; + bool four_bytes_address_mode; + bool reset_enable; + uint8_t ear; int64_t dirty_page; @@ -331,6 +400,7 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd) switch (cmd) { case ERASE_4K: + case ERASE4_4K: len = 4 << 10; capa_to_assert = ER_4K; break; @@ -339,6 +409,7 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd) capa_to_assert = ER_32K; break; case ERASE_SECTOR: + case ERASE4_SECTOR: len = s->pi->sector_size; break; case BULK_ERASE: @@ -385,7 +456,7 @@ void flash_write8(Flash *s, uint64_t addr, uint8_t data) " -> %" PRIx8 "\n", addr, prev, data); } - if (s->pi->flags & WR_1) { + if (s->pi->flags & EEPROM) { s->storage[s->cur_addr] = data; } else { s->storage[s->cur_addr] &= data; @@ -395,11 +466,43 @@ void flash_write8(Flash *s, uint64_t addr, uint8_t data) s->dirty_page = page; } +static inline int get_addr_length(Flash *s) +{ + /* check if eeprom is in use */ + if (s->pi->flags == EEPROM) { + return 2; + } + + switch (s->cmd_in_progress) { + case PP4: + case READ4: + case QIOR4: + case ERASE4_4K: + case ERASE4_SECTOR: + case FAST_READ4: + case DOR4: + case QOR4: + case DIOR4: + return 4; + default: + return s->four_bytes_address_mode ? 4 : 3; + } +} + static void complete_collecting_data(Flash *s) { - s->cur_addr = s->data[0] << 16; - s->cur_addr |= s->data[1] << 8; - s->cur_addr |= s->data[2]; + int i; + + s->cur_addr = 0; + + for (i = 0; i < get_addr_length(s); ++i) { + s->cur_addr <<= 8; + s->cur_addr |= s->data[i]; + } + + if (get_addr_length(s) == 3) { + s->cur_addr += (s->ear & 0x3) * MAX_3BYTES_SIZE; + } s->state = STATE_IDLE; @@ -407,19 +510,28 @@ static void complete_collecting_data(Flash *s) case DPP: case QPP: case PP: + case PP4: s->state = STATE_PAGE_PROGRAM; break; case READ: + case READ4: case FAST_READ: + case FAST_READ4: case DOR: + case DOR4: case QOR: + case QOR4: case DIOR: + case DIOR4: case QIOR: + case QIOR4: s->state = STATE_READ; break; case ERASE_4K: + case ERASE4_4K: case ERASE_32K: case ERASE_SECTOR: + case ERASE4_SECTOR: flash_erase(s, s->cur_addr, s->cmd_in_progress); break; case WRSR: @@ -427,49 +539,128 @@ static void complete_collecting_data(Flash *s) s->write_enable = false; } break; + case EXTEND_ADDR_WRITE: + s->ear = s->data[0]; + break; + case WNVCR: + s->nonvolatile_cfg = s->data[0] | (s->data[1] << 8); + break; + case WVCR: + s->volatile_cfg = s->data[0]; + break; + case WEVCR: + s->enh_volatile_cfg = s->data[0]; + break; default: break; } } +static void reset_memory(Flash *s) +{ + s->cmd_in_progress = NOP; + s->cur_addr = 0; + s->ear = 0; + s->four_bytes_address_mode = false; + s->len = 0; + s->needed_bytes = 0; + s->pos = 0; + s->state = STATE_IDLE; + s->write_enable = false; + s->reset_enable = false; + + if (((s->pi->jedec >> 16) & 0xFF) == JEDEC_NUMONYX) { + s->volatile_cfg = 0; + s->volatile_cfg |= VCFG_DUMMY; + s->volatile_cfg |= VCFG_WRAP_SEQUENTIAL; + if ((s->nonvolatile_cfg & NVCFG_XIP_MODE_MASK) + != NVCFG_XIP_MODE_DISABLED) { + s->volatile_cfg |= VCFG_XIP_MODE_ENABLED; + } + s->volatile_cfg |= deposit32(s->volatile_cfg, + VCFG_DUMMY_CLK_POS, + CFG_DUMMY_CLK_LEN, + extract32(s->nonvolatile_cfg, + NVCFG_DUMMY_CLK_POS, + CFG_DUMMY_CLK_LEN) + ); + + s->enh_volatile_cfg = 0; + s->enh_volatile_cfg |= EVCFG_OUT_DRIVER_STRENGHT_DEF; + s->enh_volatile_cfg |= EVCFG_VPP_ACCELERATOR; + s->enh_volatile_cfg |= EVCFG_RESET_HOLD_ENABLED; + if (s->nonvolatile_cfg & NVCFG_DUAL_IO_MASK) { + s->enh_volatile_cfg |= EVCFG_DUAL_IO_ENABLED; + } + if (s->nonvolatile_cfg & NVCFG_QUAD_IO_MASK) { + s->enh_volatile_cfg |= EVCFG_QUAD_IO_ENABLED; + } + if (!(s->nonvolatile_cfg & NVCFG_4BYTE_ADDR_MASK)) { + s->four_bytes_address_mode = true; + } + if (!(s->nonvolatile_cfg & NVCFG_LOWER_SEGMENT_MASK)) { + s->ear = CFG_UPPER_128MB_SEG_ENABLED; + } + } + + DB_PRINT_L(0, "Reset done.\n"); +} + static void decode_new_cmd(Flash *s, uint32_t value) { s->cmd_in_progress = value; DB_PRINT_L(0, "decoded new command:%x\n", value); + if (value != RESET_MEMORY) { + s->reset_enable = false; + } + switch (value) { case ERASE_4K: + case ERASE4_4K: case ERASE_32K: case ERASE_SECTOR: + case ERASE4_SECTOR: case READ: + case READ4: case DPP: case QPP: case PP: - s->needed_bytes = 3; + case PP4: + s->needed_bytes = get_addr_length(s); s->pos = 0; s->len = 0; s->state = STATE_COLLECTING_DATA; break; case FAST_READ: + case FAST_READ4: case DOR: + case DOR4: case QOR: - s->needed_bytes = 4; + case QOR4: + s->needed_bytes = get_addr_length(s); + if (((s->pi->jedec >> 16) & 0xFF) == JEDEC_NUMONYX) { + /* Dummy cycles modeled with bytes writes instead of bits */ + s->needed_bytes += extract32(s->volatile_cfg, 4, 4); + } s->pos = 0; s->len = 0; s->state = STATE_COLLECTING_DATA; break; case DIOR: + case DIOR4: switch ((s->pi->jedec >> 16) & 0xFF) { case JEDEC_WINBOND: case JEDEC_SPANSION: s->needed_bytes = 4; break; - case JEDEC_NUMONYX: default: - s->needed_bytes = 5; + s->needed_bytes = get_addr_length(s); + /* Dummy cycles modeled with bytes writes instead of bits */ + s->needed_bytes += extract32(s->volatile_cfg, 4, 4); } s->pos = 0; s->len = 0; @@ -477,14 +668,16 @@ static void decode_new_cmd(Flash *s, uint32_t value) break; case QIOR: + case QIOR4: switch ((s->pi->jedec >> 16) & 0xFF) { case JEDEC_WINBOND: case JEDEC_SPANSION: s->needed_bytes = 6; break; - case JEDEC_NUMONYX: default: - s->needed_bytes = 8; + s->needed_bytes = get_addr_length(s); + /* Dummy cycles modeled with bytes writes instead of bits */ + s->needed_bytes += extract32(s->volatile_cfg, 4, 4); } s->pos = 0; s->len = 0; @@ -514,6 +707,16 @@ static void decode_new_cmd(Flash *s, uint32_t value) s->state = STATE_READING_DATA; break; + case READ_FSR: + s->data[0] = FSR_FLASH_READY; + if (s->four_bytes_address_mode) { + s->data[0] |= FSR_4BYTE_ADDR_MODE_ENABLED; + } + s->pos = 0; + s->len = 1; + s->state = STATE_READING_DATA; + break; + case JEDEC_READ: DB_PRINT_L(0, "populated jedec code\n"); s->data[0] = (s->pi->jedec >> 16) & 0xff; @@ -541,6 +744,77 @@ static void decode_new_cmd(Flash *s, uint32_t value) break; case NOP: break; + case EN_4BYTE_ADDR: + s->four_bytes_address_mode = true; + break; + case EX_4BYTE_ADDR: + s->four_bytes_address_mode = false; + break; + case EXTEND_ADDR_READ: + s->data[0] = s->ear; + s->pos = 0; + s->len = 1; + s->state = STATE_READING_DATA; + break; + case EXTEND_ADDR_WRITE: + if (s->write_enable) { + s->needed_bytes = 1; + s->pos = 0; + s->len = 0; + s->state = STATE_COLLECTING_DATA; + } + break; + case RNVCR: + s->data[0] = s->nonvolatile_cfg & 0xFF; + s->data[1] = (s->nonvolatile_cfg >> 8) & 0xFF; + s->pos = 0; + s->len = 2; + s->state = STATE_READING_DATA; + break; + case WNVCR: + if (s->write_enable) { + s->needed_bytes = 2; + s->pos = 0; + s->len = 0; + s->state = STATE_COLLECTING_DATA; + } + break; + case RVCR: + s->data[0] = s->volatile_cfg & 0xFF; + s->pos = 0; + s->len = 1; + s->state = STATE_READING_DATA; + break; + case WVCR: + if (s->write_enable) { + s->needed_bytes = 1; + s->pos = 0; + s->len = 0; + s->state = STATE_COLLECTING_DATA; + } + break; + case REVCR: + s->data[0] = s->enh_volatile_cfg & 0xFF; + s->pos = 0; + s->len = 1; + s->state = STATE_READING_DATA; + break; + case WEVCR: + if (s->write_enable) { + s->needed_bytes = 1; + s->pos = 0; + s->len = 0; + s->state = STATE_COLLECTING_DATA; + } + break; + case RESET_ENABLE: + s->reset_enable = true; + break; + case RESET_MEMORY: + if (s->reset_enable) { + reset_memory(s); + } + break; default: qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value); break; @@ -647,14 +921,26 @@ static int m25p80_init(SSISlave *ss) return 0; } +static void m25p80_reset(DeviceState *d) +{ + Flash *s = M25P80(d); + + reset_memory(s); +} + static void m25p80_pre_save(void *opaque) { flash_sync_dirty((Flash *)opaque, -1); } +static Property m25p80_properties[] = { + DEFINE_PROP_UINT32("nonvolatile-cfg", Flash, nonvolatile_cfg, 0x8FFF), + DEFINE_PROP_END_OF_LIST(), +}; + static const VMStateDescription vmstate_m25p80 = { .name = "xilinx_spi", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, .pre_save = m25p80_pre_save, .fields = (VMStateField[]) { @@ -666,6 +952,12 @@ static const VMStateDescription vmstate_m25p80 = { VMSTATE_UINT8(cmd_in_progress, Flash), VMSTATE_UINT64(cur_addr, Flash), VMSTATE_BOOL(write_enable, Flash), + VMSTATE_BOOL_V(reset_enable, Flash, 2), + VMSTATE_UINT8_V(ear, Flash, 2), + VMSTATE_BOOL_V(four_bytes_address_mode, Flash, 2), + VMSTATE_UINT32_V(nonvolatile_cfg, Flash, 2), + VMSTATE_UINT32_V(volatile_cfg, Flash, 2), + VMSTATE_UINT32_V(enh_volatile_cfg, Flash, 2), VMSTATE_END_OF_LIST() } }; @@ -681,6 +973,8 @@ static void m25p80_class_init(ObjectClass *klass, void *data) k->set_cs = m25p80_cs; k->cs_polarity = SSI_CS_LOW; dc->vmsd = &vmstate_m25p80; + dc->props = m25p80_properties; + dc->reset = m25p80_reset; mc->pi = data; } diff --git a/qemu/hw/block/nand.c b/qemu/hw/block/nand.c index 61d2cec03..29c659681 100644 --- a/qemu/hw/block/nand.c +++ b/qemu/hw/block/nand.c @@ -18,10 +18,12 @@ #ifndef NAND_IO -# include "hw/hw.h" -# include "hw/block/flash.h" +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/block/flash.h" #include "sysemu/block-backend.h" #include "hw/qdev.h" +#include "qapi/error.h" #include "qemu/error-report.h" # define NAND_CMD_READ0 0x00 @@ -522,8 +524,8 @@ void nand_setio(DeviceState *dev, uint32_t value) if (s->ale) { unsigned int shift = s->addrlen * 8; - unsigned int mask = ~(0xff << shift); - unsigned int v = value << shift; + uint64_t mask = ~(0xffull << shift); + uint64_t v = (uint64_t)value << shift; s->addr = (s->addr & mask) | v; s->addrlen ++; @@ -635,7 +637,7 @@ DeviceState *nand_init(BlockBackend *blk, int manf_id, int chip_id) qdev_prop_set_uint8(dev, "manufacturer_id", manf_id); qdev_prop_set_uint8(dev, "chip_id", chip_id); if (blk) { - qdev_prop_set_drive_nofail(dev, "drive", blk); + qdev_prop_set_drive(dev, "drive", blk, &error_fatal); } qdev_init_nofail(dev); @@ -712,7 +714,7 @@ static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s) memset(s->storage + (PAGE(addr) << OOB_SHIFT), 0xff, OOB_SIZE << s->erase_shift); i = SECTOR(addr); - page = SECTOR(addr + (ADDR_SHIFT + s->erase_shift)); + page = SECTOR(addr + (1 << (ADDR_SHIFT + s->erase_shift))); for (; i < page; i ++) if (blk_write(s->blk, i, iobuf, 1) < 0) { printf("%s: write error in sector %" PRIu64 "\n", __func__, i); diff --git a/qemu/hw/block/nvme.c b/qemu/hw/block/nvme.c index 40d488032..173988ee8 100644 --- a/qemu/hw/block/nvme.c +++ b/qemu/hw/block/nvme.c @@ -20,11 +20,13 @@ * -device nvme,drive=,serial=,id= */ +#include "qemu/osdep.h" #include #include #include #include #include "sysemu/sysemu.h" +#include "qapi/error.h" #include "qapi/visitor.h" #include "sysemu/block-backend.h" @@ -201,10 +203,11 @@ static void nvme_rw_cb(void *opaque, int ret) NvmeCtrl *n = sq->ctrl; NvmeCQueue *cq = n->cq[sq->cqid]; - block_acct_done(blk_get_stats(n->conf.blk), &req->acct); if (!ret) { + block_acct_done(blk_get_stats(n->conf.blk), &req->acct); req->status = NVME_SUCCESS; } else { + block_acct_failed(blk_get_stats(n->conf.blk), &req->acct); req->status = NVME_INTERNAL_DEV_ERROR; } if (req->has_sg) { @@ -238,18 +241,22 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, uint64_t data_size = (uint64_t)nlb << data_shift; uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS); int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0; + enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ; if ((slba + nlb) > ns->id_ns.nsze) { + block_acct_invalid(blk_get_stats(n->conf.blk), acct); return NVME_LBA_RANGE | NVME_DNR; } + if (nvme_map_prp(&req->qsg, prp1, prp2, data_size, n)) { + block_acct_invalid(blk_get_stats(n->conf.blk), acct); return NVME_INVALID_FIELD | NVME_DNR; } + assert((nlb << data_shift) == req->qsg.size); req->has_sg = true; - dma_acct_start(n->conf.blk, &req->acct, &req->qsg, - is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ); + dma_acct_start(n->conf.blk, &req->acct, &req->qsg, acct); req->aiocb = is_write ? dma_blk_write(n->conf.blk, &req->qsg, aio_slba, nvme_rw_cb, req) : dma_blk_read(n->conf.blk, &req->qsg, aio_slba, nvme_rw_cb, req); @@ -805,7 +812,7 @@ static int nvme_init(PCIDevice *pci_dev) n->num_namespaces = 1; n->num_queues = 64; - n->reg_size = 1 << qemu_fls(0x1004 + 2 * (n->num_queues + 1) * 4); + n->reg_size = pow2ceil(0x1004 + 2 * (n->num_queues + 1) * 4); n->ns_size = bs_size / (uint64_t)n->num_namespaces; n->namespaces = g_new0(NvmeNamespace, n->num_namespaces); @@ -910,45 +917,13 @@ static void nvme_class_init(ObjectClass *oc, void *data) dc->vmsd = &nvme_vmstate; } -static void nvme_get_bootindex(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) -{ - NvmeCtrl *s = NVME(obj); - - visit_type_int32(v, &s->conf.bootindex, name, errp); -} - -static void nvme_set_bootindex(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void nvme_instance_init(Object *obj) { NvmeCtrl *s = NVME(obj); - int32_t boot_index; - Error *local_err = NULL; - visit_type_int32(v, &boot_index, name, &local_err); - if (local_err) { - goto out; - } - /* check whether bootindex is present in fw_boot_order list */ - check_boot_index(boot_index, &local_err); - if (local_err) { - goto out; - } - /* change bootindex to a new one */ - s->conf.bootindex = boot_index; - -out: - if (local_err) { - error_propagate(errp, local_err); - } -} - -static void nvme_instance_init(Object *obj) -{ - object_property_add(obj, "bootindex", "int32", - nvme_get_bootindex, - nvme_set_bootindex, NULL, NULL, NULL); - object_property_set_int(obj, -1, "bootindex", NULL); + device_add_bootindex_property(obj, &s->conf.bootindex, + "bootindex", "/namespace@1,0", + DEVICE(obj), &error_abort); } static const TypeInfo nvme_info = { diff --git a/qemu/hw/block/nvme.h b/qemu/hw/block/nvme.h index bf3a3ccac..8fb0c1075 100644 --- a/qemu/hw/block/nvme.h +++ b/qemu/hw/block/nvme.h @@ -1,5 +1,6 @@ #ifndef HW_NVME_H #define HW_NVME_H +#include "qemu/cutils.h" typedef struct NvmeBar { uint64_t cap; diff --git a/qemu/hw/block/onenand.c b/qemu/hw/block/onenand.c index 1b2c89375..883f4b1fa 100644 --- a/qemu/hw/block/onenand.c +++ b/qemu/hw/block/onenand.c @@ -18,6 +18,8 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "hw/hw.h" #include "hw/block/flash.h" @@ -786,7 +788,7 @@ static int onenand_initfn(SysBusDevice *sbd) s->otp = memset(g_malloc((64 + 2) << PAGE_SHIFT), 0xff, (64 + 2) << PAGE_SHIFT); memory_region_init_ram(&s->ram, OBJECT(s), "onenand.ram", - 0xc000 << s->shift, &error_abort); + 0xc000 << s->shift, &error_fatal); vmstate_register_ram_global(&s->ram); ram = memory_region_get_ram_ptr(&s->ram); s->boot[0] = ram + (0x0000 << s->shift); diff --git a/qemu/hw/block/pflash_cfi01.c b/qemu/hw/block/pflash_cfi01.c index 2ba6c7729..106a77523 100644 --- a/qemu/hw/block/pflash_cfi01.c +++ b/qemu/hw/block/pflash_cfi01.c @@ -36,14 +36,17 @@ * It does not implement much more ... */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/block/flash.h" #include "sysemu/block-backend.h" +#include "qapi/error.h" #include "qemu/timer.h" #include "qemu/bitops.h" #include "exec/address-spaces.h" #include "qemu/host-utils.h" #include "hw/sysbus.h" +#include "sysemu/sysemu.h" #define PFLASH_BUG(fmt, ...) \ do { \ @@ -95,6 +98,7 @@ struct pflash_t { MemoryRegion mem; char *name; void *storage; + VMChangeStateEntry *vmstate; }; static int pflash_post_load(void *opaque, int version_id); @@ -942,13 +946,25 @@ MemoryRegion *pflash_cfi01_get_memory(pflash_t *fl) return &fl->mem; } +static void postload_update_cb(void *opaque, int running, RunState state) +{ + pflash_t *pfl = opaque; + + /* This is called after bdrv_invalidate_cache_all. */ + qemu_del_vm_change_state_handler(pfl->vmstate); + pfl->vmstate = NULL; + + DPRINTF("%s: updating bdrv for %s\n", __func__, pfl->name); + pflash_update(pfl, 0, pfl->sector_len * pfl->nb_blocs); +} + static int pflash_post_load(void *opaque, int version_id) { pflash_t *pfl = opaque; if (!pfl->ro) { - DPRINTF("%s: updating bdrv for %s\n", __func__, pfl->name); - pflash_update(pfl, 0, pfl->sector_len * pfl->nb_blocs); + pfl->vmstate = qemu_add_vm_change_state_handler(postload_update_cb, + pfl); } return 0; } diff --git a/qemu/hw/block/pflash_cfi02.c b/qemu/hw/block/pflash_cfi02.c index 074a005f6..b13172c6e 100644 --- a/qemu/hw/block/pflash_cfi02.c +++ b/qemu/hw/block/pflash_cfi02.c @@ -35,8 +35,10 @@ * It does not implement multiple sectors erase */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/block/flash.h" +#include "qapi/error.h" #include "qemu/timer.h" #include "sysemu/block-backend.h" #include "exec/address-spaces.h" @@ -430,8 +432,8 @@ static void pflash_write (pflash_t *pfl, hwaddr offset, } pfl->status = 0x00; /* Let's wait 5 seconds before chip erase is done */ - timer_mod(pfl->timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (get_ticks_per_sec() * 5)); + timer_mod(pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (NANOSECONDS_PER_SECOND * 5)); break; case 0x30: /* Sector erase */ @@ -445,8 +447,8 @@ static void pflash_write (pflash_t *pfl, hwaddr offset, } pfl->status = 0x00; /* Let's wait 1/2 second before sector erase is done */ - timer_mod(pfl->timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (get_ticks_per_sec() / 2)); + timer_mod(pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (NANOSECONDS_PER_SECOND / 2)); break; default: DPRINTF("%s: invalid command %02x (wc 5)\n", __func__, cmd); diff --git a/qemu/hw/block/tc58128.c b/qemu/hw/block/tc58128.c index 728f1c3b6..7909d5041 100644 --- a/qemu/hw/block/tc58128.c +++ b/qemu/hw/block/tc58128.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sh4/sh.h" #include "hw/loader.h" diff --git a/qemu/hw/block/virtio-blk.c b/qemu/hw/block/virtio-blk.c index 1556c9cf5..3f88f8cf5 100644 --- a/qemu/hw/block/virtio-blk.c +++ b/qemu/hw/block/virtio-blk.c @@ -11,6 +11,8 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/iov.h" #include "qemu/error-report.h" @@ -20,7 +22,6 @@ #include "sysemu/blockdev.h" #include "hw/virtio/virtio-blk.h" #include "dataplane/virtio-blk.h" -#include "migration/migration.h" #include "block/scsi.h" #ifdef __linux__ # include @@ -28,26 +29,23 @@ #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" -VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s) +void virtio_blk_init_request(VirtIOBlock *s, VirtIOBlockReq *req) { - VirtIOBlockReq *req = g_slice_new(VirtIOBlockReq); req->dev = s; req->qiov.size = 0; req->in_len = 0; req->next = NULL; req->mr_next = NULL; - return req; } void virtio_blk_free_request(VirtIOBlockReq *req) { if (req) { - g_slice_free(VirtIOBlockReq, req); + g_free(req); } } -static void virtio_blk_complete_request(VirtIOBlockReq *req, - unsigned char status) +static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status) { VirtIOBlock *s = req->dev; VirtIODevice *vdev = VIRTIO_DEVICE(s); @@ -56,12 +54,11 @@ static void virtio_blk_complete_request(VirtIOBlockReq *req, stb_p(&req->in->status, status); virtqueue_push(s->vq, &req->elem, req->in_len); - virtio_notify(vdev, s->vq); -} - -static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status) -{ - req->dev->complete_request(req, status); + if (s->dataplane_started && !s->dataplane_disabled) { + virtio_blk_data_plane_notify(s->dataplane); + } else { + virtio_notify(vdev, s->vq); + } } static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, @@ -72,11 +69,14 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, VirtIOBlock *s = req->dev; if (action == BLOCK_ERROR_ACTION_STOP) { + /* Break the link as the next request is going to be parsed from the + * ring again. Otherwise we may end up doing a double completion! */ + req->mr_next = NULL; req->next = s->rq; s->rq = req; } else if (action == BLOCK_ERROR_ACTION_REPORT) { virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); - block_acct_done(blk_get_stats(s->blk), &req->acct); + block_acct_failed(blk_get_stats(s->blk), &req->acct); virtio_blk_free_request(req); } @@ -189,13 +189,11 @@ out: static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s) { - VirtIOBlockReq *req = virtio_blk_alloc_request(s); + VirtIOBlockReq *req = virtqueue_pop(s->vq, sizeof(VirtIOBlockReq)); - if (!virtqueue_pop(s->vq, &req->elem)) { - virtio_blk_free_request(req); - return NULL; + if (req) { + virtio_blk_init_request(s, req); } - return req; } @@ -404,24 +402,16 @@ void virtio_blk_submit_multireq(BlockBackend *blk, MultiReqBuffer *mrb) for (i = 0; i < mrb->num_reqs; i++) { VirtIOBlockReq *req = mrb->reqs[i]; if (num_reqs > 0) { - bool merge = true; - - /* merge would exceed maximum number of IOVs */ - if (niov + req->qiov.niov > IOV_MAX) { - merge = false; - } - - /* merge would exceed maximum transfer length of backend device */ - if (req->qiov.size / BDRV_SECTOR_SIZE + nb_sectors > max_xfer_len) { - merge = false; - } - - /* requests are not sequential */ - if (sector_num + nb_sectors != req->sector_num) { - merge = false; - } - - if (!merge) { + /* + * NOTE: We cannot merge the requests in below situations: + * 1. requests are not sequential + * 2. merge would exceed maximum number of IOVs + * 3. merge would exceed maximum transfer length of backend device + */ + if (sector_num + nb_sectors != req->sector_num || + niov > blk_get_max_iov(blk) - req->qiov.niov || + req->qiov.size / BDRV_SECTOR_SIZE > max_xfer_len || + nb_sectors > max_xfer_len - req->qiov.size / BDRV_SECTOR_SIZE) { submit_requests(blk, mrb, start, num_reqs, niov); num_reqs = 0; } @@ -536,6 +526,8 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) if (!virtio_blk_sect_range_ok(req->dev, req->sector_num, req->qiov.size)) { virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); + block_acct_invalid(blk_get_stats(req->dev->blk), + is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ); virtio_blk_free_request(req); return; } @@ -586,19 +578,12 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) } } -static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) +void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq) { - VirtIOBlock *s = VIRTIO_BLK(vdev); VirtIOBlockReq *req; MultiReqBuffer mrb = {}; - /* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start - * dataplane here instead of waiting for .set_status(). - */ - if (s->dataplane) { - virtio_blk_data_plane_start(s->dataplane); - return; - } + blk_io_plug(s->blk); while ((req = virtio_blk_get_request(s))) { virtio_blk_handle_request(req, &mrb); @@ -607,6 +592,24 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) if (mrb.num_reqs) { virtio_blk_submit_multireq(s->blk, &mrb); } + + blk_io_unplug(s->blk); +} + +static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOBlock *s = (VirtIOBlock *)vdev; + + if (s->dataplane) { + /* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start + * dataplane here instead of waiting for .set_status(). + */ + virtio_blk_data_plane_start(s->dataplane); + if (!s->dataplane_disabled) { + return; + } + } + virtio_blk_handle_vq(s, vq); } static void virtio_blk_dma_restart_bh(void *opaque) @@ -731,7 +734,7 @@ static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features, virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY); virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY); virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE); - if (__virtio_has_feature(features, VIRTIO_F_VERSION_1)) { + if (virtio_has_feature(features, VIRTIO_F_VERSION_1)) { if (s->conf.scsi) { error_setg(errp, "Please set scsi=off for virtio-blk devices in order to use virtio 1.0"); return 0; @@ -782,10 +785,11 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) * * s->blk would erroneously be placed in writethrough mode. */ - if (!virtio_has_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE)) { + if (!virtio_vdev_has_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE)) { aio_context_acquire(blk_get_aio_context(s->blk)); blk_set_enable_write_cache(s->blk, - virtio_has_feature(vdev, VIRTIO_BLK_F_WCE)); + virtio_vdev_has_feature(vdev, + VIRTIO_BLK_F_WCE)); aio_context_release(blk_get_aio_context(s->blk)); } } @@ -793,6 +797,11 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) static void virtio_blk_save(QEMUFile *f, void *opaque) { VirtIODevice *vdev = VIRTIO_DEVICE(opaque); + VirtIOBlock *s = VIRTIO_BLK(vdev); + + if (s->dataplane) { + virtio_blk_data_plane_stop(s->dataplane); + } virtio_save(vdev, f); } @@ -804,8 +813,7 @@ static void virtio_blk_save_device(VirtIODevice *vdev, QEMUFile *f) while (req) { qemu_put_sbyte(f, 1); - qemu_put_buffer(f, (unsigned char *)&req->elem, - sizeof(VirtQueueElement)); + qemu_put_virtqueue_element(f, &req->elem); req = req->next; } qemu_put_sbyte(f, 0); @@ -828,16 +836,11 @@ static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f, VirtIOBlock *s = VIRTIO_BLK(vdev); while (qemu_get_sbyte(f)) { - VirtIOBlockReq *req = virtio_blk_alloc_request(s); - qemu_get_buffer(f, (unsigned char *)&req->elem, - sizeof(VirtQueueElement)); + VirtIOBlockReq *req; + req = qemu_get_virtqueue_element(f, sizeof(VirtIOBlockReq)); + virtio_blk_init_request(s, req); req->next = s->rq; s->rq = req; - - virtqueue_map_sg(req->elem.in_sg, req->elem.in_addr, - req->elem.in_num, 1); - virtqueue_map_sg(req->elem.out_sg, req->elem.out_addr, - req->elem.out_num, 0); } return 0; @@ -854,36 +857,6 @@ static const BlockDevOps virtio_block_ops = { .resize_cb = virtio_blk_resize, }; -/* Disable dataplane thread during live migration since it does not - * update the dirty memory bitmap yet. - */ -static void virtio_blk_migration_state_changed(Notifier *notifier, void *data) -{ - VirtIOBlock *s = container_of(notifier, VirtIOBlock, - migration_state_notifier); - MigrationState *mig = data; - Error *err = NULL; - - if (migration_in_setup(mig)) { - if (!s->dataplane) { - return; - } - virtio_blk_data_plane_destroy(s->dataplane); - s->dataplane = NULL; - } else if (migration_has_finished(mig) || - migration_has_failed(mig)) { - if (s->dataplane) { - return; - } - blk_drain_all(); /* complete in-flight non-dataplane requests */ - virtio_blk_data_plane_create(VIRTIO_DEVICE(s), &s->conf, - &s->dataplane, &err); - if (err != NULL) { - error_report_err(err); - } - } -} - static void virtio_blk_device_realize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -918,15 +891,12 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) s->sector_mask = (s->conf.conf.logical_block_size / BDRV_SECTOR_SIZE) - 1; s->vq = virtio_add_queue(vdev, 128, virtio_blk_handle_output); - s->complete_request = virtio_blk_complete_request; virtio_blk_data_plane_create(vdev, conf, &s->dataplane, &err); if (err != NULL) { error_propagate(errp, err); virtio_cleanup(vdev); return; } - s->migration_state_notifier.notify = virtio_blk_migration_state_changed; - add_migration_state_change_notifier(&s->migration_state_notifier); s->change = qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s); register_savevm(dev, "virtio-blk", virtio_blk_id++, 2, @@ -942,7 +912,6 @@ static void virtio_blk_device_unrealize(DeviceState *dev, Error **errp) VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOBlock *s = VIRTIO_BLK(dev); - remove_migration_state_change_notifier(&s->migration_state_notifier); virtio_blk_data_plane_destroy(s->dataplane); s->dataplane = NULL; qemu_del_vm_change_state_handler(s->change); @@ -970,11 +939,10 @@ static Property virtio_blk_properties[] = { DEFINE_PROP_STRING("serial", VirtIOBlock, conf.serial), DEFINE_PROP_BIT("config-wce", VirtIOBlock, conf.config_wce, 0, true), #ifdef __linux__ - DEFINE_PROP_BIT("scsi", VirtIOBlock, conf.scsi, 0, true), + DEFINE_PROP_BIT("scsi", VirtIOBlock, conf.scsi, 0, false), #endif DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0, true), - DEFINE_PROP_BIT("x-data-plane", VirtIOBlock, conf.data_plane, 0, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/qemu/hw/block/xen_blkif.h b/qemu/hw/block/xen_blkif.h index 711b69274..c68487cb3 100644 --- a/qemu/hw/block/xen_blkif.h +++ b/qemu/hw/block/xen_blkif.h @@ -85,8 +85,10 @@ static inline void blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_reque d->nr_sectors = s->nr_sectors; return; } - if (n > src->nr_segments) - n = src->nr_segments; + /* prevent the compiler from optimizing the code and using src->nr_segments instead */ + barrier(); + if (n > dst->nr_segments) + n = dst->nr_segments; for (i = 0; i < n; i++) dst->seg[i] = src->seg[i]; } @@ -106,8 +108,10 @@ static inline void blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_reque d->nr_sectors = s->nr_sectors; return; } - if (n > src->nr_segments) - n = src->nr_segments; + /* prevent the compiler from optimizing the code and using src->nr_segments instead */ + barrier(); + if (n > dst->nr_segments) + n = dst->nr_segments; for (i = 0; i < n; i++) dst->seg[i] = src->seg[i]; } diff --git a/qemu/hw/block/xen_disk.c b/qemu/hw/block/xen_disk.c index 267d8a8c7..d4ce380fe 100644 --- a/qemu/hw/block/xen_disk.c +++ b/qemu/hw/block/xen_disk.c @@ -19,19 +19,8 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include -#include -#include #include #include @@ -40,6 +29,7 @@ #include "xen_blkif.h" #include "sysemu/blockdev.h" #include "sysemu/block-backend.h" +#include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" @@ -76,7 +66,6 @@ struct ioreq { off_t start; QEMUIOVector v; int presync; - int postsync; uint8_t mapped; /* grant mapping */ @@ -145,7 +134,6 @@ static void ioreq_reset(struct ioreq *ioreq) ioreq->status = 0; ioreq->start = 0; ioreq->presync = 0; - ioreq->postsync = 0; ioreq->mapped = 0; memset(ioreq->domids, 0, sizeof(ioreq->domids)); @@ -174,11 +162,11 @@ static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data) static void destroy_grant(gpointer pgnt) { PersistentGrant *grant = pgnt; - XenGnttab gnt = grant->blkdev->xendev.gnttabdev; + xengnttab_handle *gnt = grant->blkdev->xendev.gnttabdev; - if (xc_gnttab_munmap(gnt, grant->page, 1) != 0) { + if (xengnttab_unmap(gnt, grant->page, 1) != 0) { xen_be_printf(&grant->blkdev->xendev, 0, - "xc_gnttab_munmap failed: %s\n", + "xengnttab_unmap failed: %s\n", strerror(errno)); } grant->blkdev->persistent_gnt_count--; @@ -191,11 +179,11 @@ static void remove_persistent_region(gpointer data, gpointer dev) { PersistentRegion *region = data; struct XenBlkDev *blkdev = dev; - XenGnttab gnt = blkdev->xendev.gnttabdev; + xengnttab_handle *gnt = blkdev->xendev.gnttabdev; - if (xc_gnttab_munmap(gnt, region->addr, region->num) != 0) { + if (xengnttab_unmap(gnt, region->addr, region->num) != 0) { xen_be_printf(&blkdev->xendev, 0, - "xc_gnttab_munmap region %p failed: %s\n", + "xengnttab_unmap region %p failed: %s\n", region->addr, strerror(errno)); } xen_be_printf(&blkdev->xendev, 3, @@ -330,7 +318,7 @@ err: static void ioreq_unmap(struct ioreq *ioreq) { - XenGnttab gnt = ioreq->blkdev->xendev.gnttabdev; + xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev; int i; if (ioreq->num_unmap == 0 || ioreq->mapped == 0) { @@ -340,8 +328,9 @@ static void ioreq_unmap(struct ioreq *ioreq) if (!ioreq->pages) { return; } - if (xc_gnttab_munmap(gnt, ioreq->pages, ioreq->num_unmap) != 0) { - xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", + if (xengnttab_unmap(gnt, ioreq->pages, ioreq->num_unmap) != 0) { + xen_be_printf(&ioreq->blkdev->xendev, 0, + "xengnttab_unmap failed: %s\n", strerror(errno)); } ioreq->blkdev->cnt_map -= ioreq->num_unmap; @@ -351,8 +340,9 @@ static void ioreq_unmap(struct ioreq *ioreq) if (!ioreq->page[i]) { continue; } - if (xc_gnttab_munmap(gnt, ioreq->page[i], 1) != 0) { - xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", + if (xengnttab_unmap(gnt, ioreq->page[i], 1) != 0) { + xen_be_printf(&ioreq->blkdev->xendev, 0, + "xengnttab_unmap failed: %s\n", strerror(errno)); } ioreq->blkdev->cnt_map--; @@ -364,7 +354,7 @@ static void ioreq_unmap(struct ioreq *ioreq) static int ioreq_map(struct ioreq *ioreq) { - XenGnttab gnt = ioreq->blkdev->xendev.gnttabdev; + xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev; uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST]; uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; @@ -415,7 +405,7 @@ static int ioreq_map(struct ioreq *ioreq) } if (batch_maps && new_maps) { - ioreq->pages = xc_gnttab_map_grant_refs + ioreq->pages = xengnttab_map_grant_refs (gnt, new_maps, domids, refs, ioreq->prot); if (ioreq->pages == NULL) { xen_be_printf(&ioreq->blkdev->xendev, 0, @@ -431,7 +421,7 @@ static int ioreq_map(struct ioreq *ioreq) ioreq->blkdev->cnt_map += new_maps; } else if (new_maps) { for (i = 0; i < new_maps; i++) { - ioreq->page[i] = xc_gnttab_map_grant_ref + ioreq->page[i] = xengnttab_map_grant_ref (gnt, domids[i], refs[i], ioreq->prot); if (ioreq->page[i] == NULL) { xen_be_printf(&ioreq->blkdev->xendev, 0, @@ -521,12 +511,6 @@ static void qemu_aio_complete(void *opaque, int ret) if (ioreq->aio_inflight > 0) { return; } - if (ioreq->postsync) { - ioreq->postsync = 0; - ioreq->aio_inflight++; - blk_aio_flush(ioreq->blkdev->blk, qemu_aio_complete, ioreq); - return; - } ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY; ioreq_unmap(ioreq); @@ -538,7 +522,11 @@ static void qemu_aio_complete(void *opaque, int ret) break; } case BLKIF_OP_READ: - block_acct_done(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct); + if (ioreq->status == BLKIF_RSP_OKAY) { + block_acct_done(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct); + } else { + block_acct_failed(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct); + } break; case BLKIF_OP_DISCARD: default: @@ -577,7 +565,9 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) } block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct, - ioreq->v.size, BLOCK_ACCT_WRITE); + ioreq->v.size, + ioreq->req.operation == BLKIF_OP_WRITE ? + BLOCK_ACCT_WRITE : BLOCK_ACCT_FLUSH); ioreq->aio_inflight++; blk_aio_writev(blkdev->blk, ioreq->start / BLOCK_SIZE, &ioreq->v, ioreq->v.size / BLOCK_SIZE, @@ -721,6 +711,23 @@ static void blk_handle_requests(struct XenBlkDev *blkdev) /* parse them */ if (ioreq_parse(ioreq) != 0) { + + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + block_acct_invalid(blk_get_stats(blkdev->blk), + BLOCK_ACCT_READ); + break; + case BLKIF_OP_WRITE: + block_acct_invalid(blk_get_stats(blkdev->blk), + BLOCK_ACCT_WRITE); + break; + case BLKIF_OP_FLUSH_DISKCACHE: + block_acct_invalid(blk_get_stats(blkdev->blk), + BLOCK_ACCT_FLUSH); + default: + break; + }; + if (blk_send_response_one(ioreq)) { xen_be_send_notify(&blkdev->xendev); } @@ -764,9 +771,9 @@ static void blk_alloc(struct XenDevice *xendev) if (xen_mode != XEN_EMULATE) { batch_maps = 1; } - if (xc_gnttab_set_max_grants(xendev->gnttabdev, + if (xengnttab_set_max_grants(xendev->gnttabdev, MAX_GRANTS(max_requests, BLKIF_MAX_SEGMENTS_PER_REQUEST)) < 0) { - xen_be_printf(xendev, 0, "xc_gnttab_set_max_grants failed: %s\n", + xen_be_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", strerror(errno)); } } @@ -811,6 +818,9 @@ static int blk_init(struct XenDevice *xendev) if (!strcmp("aio", blkdev->fileproto)) { blkdev->fileproto = "raw"; } + if (!strcmp("vhd", blkdev->fileproto)) { + blkdev->fileproto = "vpc"; + } if (blkdev->mode == NULL) { blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode"); } @@ -879,12 +889,14 @@ static int blk_connect(struct XenDevice *xendev) struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); int pers, index, qflags; bool readonly = true; + bool writethrough = true; /* read-only ? */ if (blkdev->directiosafe) { qflags = BDRV_O_NOCACHE | BDRV_O_NATIVE_AIO; } else { - qflags = BDRV_O_CACHE_WB; + qflags = 0; + writethrough = false; } if (strcmp(blkdev->mode, "w") == 0) { qflags |= BDRV_O_RDWR; @@ -908,7 +920,7 @@ static int blk_connect(struct XenDevice *xendev) /* setup via xenbus -> create new block driver instance */ xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); - blkdev->blk = blk_new_open(blkdev->dev, blkdev->filename, NULL, options, + blkdev->blk = blk_new_open(blkdev->filename, NULL, options, qflags, &local_err); if (!blkdev->blk) { xen_be_printf(&blkdev->xendev, 0, "error: %s\n", @@ -916,6 +928,7 @@ static int blk_connect(struct XenDevice *xendev) error_free(local_err); return -1; } + blk_set_enable_write_cache(blkdev->blk, !writethrough); } else { /* setup via qemu cmdline -> already setup for us */ xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n"); @@ -932,9 +945,11 @@ static int blk_connect(struct XenDevice *xendev) blk_attach_dev_nofail(blkdev->blk, blkdev); blkdev->file_size = blk_getlength(blkdev->blk); if (blkdev->file_size < 0) { + BlockDriverState *bs = blk_bs(blkdev->blk); + const char *drv_name = bs ? bdrv_get_format_name(bs) : NULL; xen_be_printf(&blkdev->xendev, 1, "blk_getlength: %d (%s) | drv %s\n", (int)blkdev->file_size, strerror(-blkdev->file_size), - bdrv_get_format_name(blk_bs(blkdev->blk)) ?: "-"); + drv_name ?: "-"); blkdev->file_size = 0; } @@ -971,7 +986,7 @@ static int blk_connect(struct XenDevice *xendev) } } - blkdev->sring = xc_gnttab_map_grant_ref(blkdev->xendev.gnttabdev, + blkdev->sring = xengnttab_map_grant_ref(blkdev->xendev.gnttabdev, blkdev->xendev.dom, blkdev->ring_ref, PROT_READ | PROT_WRITE); @@ -1036,7 +1051,7 @@ static void blk_disconnect(struct XenDevice *xendev) xen_be_unbind_evtchn(&blkdev->xendev); if (blkdev->sring) { - xc_gnttab_munmap(blkdev->xendev.gnttabdev, blkdev->sring, 1); + xengnttab_unmap(blkdev->xendev.gnttabdev, blkdev->sring, 1); blkdev->cnt_map--; blkdev->sring = NULL; } diff --git a/qemu/hw/bt/core.c b/qemu/hw/bt/core.c index 0ffc94889..615f0af07 100644 --- a/qemu/hw/bt/core.c +++ b/qemu/hw/bt/core.c @@ -17,6 +17,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "sysemu/bt.h" #include "hw/bt.h" diff --git a/qemu/hw/bt/hci-csr.c b/qemu/hw/bt/hci-csr.c index 7b9b91608..2e970b656 100644 --- a/qemu/hw/bt/hci-csr.c +++ b/qemu/hw/bt/hci-csr.c @@ -18,6 +18,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "sysemu/char.h" #include "qemu/timer.h" @@ -362,7 +363,7 @@ static int csrhci_ioctl(struct CharDriverState *chr, int cmd, void *arg) switch (cmd) { case CHR_IOCTL_SERIAL_SET_PARAMS: ssp = (QEMUSerialSetParams *) arg; - s->baud_delay = get_ticks_per_sec() / ssp->speed; + s->baud_delay = NANOSECONDS_PER_SECOND / ssp->speed; /* Moments later... (but shorter than 100ms) */ s->modem_state |= CHR_TIOCM_CTS; break; @@ -388,7 +389,7 @@ static void csrhci_reset(struct csrhci_s *s) s->out_len = 0; s->out_size = FIFO_LEN; s->in_len = 0; - s->baud_delay = get_ticks_per_sec(); + s->baud_delay = NANOSECONDS_PER_SECOND; s->enable = 0; s->in_hdr = INT_MAX; s->in_data = INT_MAX; diff --git a/qemu/hw/bt/hci.c b/qemu/hw/bt/hci.c index 7ea3dc6b7..7d5220509 100644 --- a/qemu/hw/bt/hci.c +++ b/qemu/hw/bt/hci.c @@ -18,11 +18,16 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/timer.h" #include "hw/usb.h" #include "sysemu/bt.h" #include "hw/bt.h" +#include "qapi/qmp/qerror.h" +#include "sysemu/replay.h" +#include "qemu/cutils.h" struct bt_hci_s { uint8_t *(*evt_packet)(void *opaque); @@ -72,6 +77,8 @@ struct bt_hci_s { struct HCIInfo info; struct bt_device_s device; + + Error *replay_blocker; }; #define DEFAULT_RSSI_DBM 20 @@ -595,7 +602,7 @@ static void bt_hci_inquiry_result(struct bt_hci_s *hci, static void bt_hci_mod_timer_1280ms(QEMUTimer *timer, int period) { timer_mod(timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - muldiv64(period << 7, get_ticks_per_sec(), 100)); + (uint64_t)(period << 7) * 10000000); } static void bt_hci_inquiry_start(struct bt_hci_s *hci, int length) @@ -1099,7 +1106,7 @@ static int bt_hci_mode_change(struct bt_hci_s *hci, uint16_t handle, bt_hci_event_status(hci, HCI_SUCCESS); timer_mod(link->acl_mode_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - muldiv64(interval * 625, get_ticks_per_sec(), 1000000)); + ((uint64_t)interval * 625) * 1000); bt_hci_lmp_mode_change_master(hci, link->link, mode, interval); return 0; @@ -1151,8 +1158,7 @@ static void bt_hci_reset(struct bt_hci_s *hci) hci->event_mask[7] = 0x00; hci->device.inquiry_scan = 0; hci->device.page_scan = 0; - if (hci->device.lmp_name) - g_free((void *) hci->device.lmp_name); + g_free((void *) hci->device.lmp_name); hci->device.lmp_name = NULL; hci->device.class[0] = 0x00; hci->device.class[1] = 0x00; @@ -1829,8 +1835,7 @@ static void bt_submit_hci(struct HCIInfo *info, case cmd_opcode_pack(OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME): LENGTH_CHECK(change_local_name); - if (hci->device.lmp_name) - g_free((void *) hci->device.lmp_name); + g_free((void *) hci->device.lmp_name); hci->device.lmp_name = g_strndup(PARAM(change_local_name, name), sizeof(PARAM(change_local_name, name))); bt_hci_event_complete_status(hci, HCI_SUCCESS); @@ -2191,6 +2196,9 @@ struct HCIInfo *bt_new_hci(struct bt_scatternet_s *net) s->device.handle_destroy = bt_hci_destroy; + error_setg(&s->replay_blocker, QERR_REPLAY_NOT_SUPPORTED, "-bt hci"); + replay_add_blocker(s->replay_blocker); + return &s->info; } @@ -2231,8 +2239,7 @@ static void bt_hci_done(struct HCIInfo *info) bt_device_done(&hci->device); - if (hci->device.lmp_name) - g_free((void *) hci->device.lmp_name); + g_free((void *) hci->device.lmp_name); /* Be gentle and send DISCONNECT to all connected peers and those * currently waiting for us to accept or reject a connection request. diff --git a/qemu/hw/bt/hid.c b/qemu/hw/bt/hid.c index af494e1e0..f6affbbb4 100644 --- a/qemu/hw/bt/hid.c +++ b/qemu/hw/bt/hid.c @@ -18,6 +18,7 @@ * with this program; if not, if not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/timer.h" #include "ui/console.h" diff --git a/qemu/hw/bt/l2cap.c b/qemu/hw/bt/l2cap.c index 591e04778..806525194 100644 --- a/qemu/hw/bt/l2cap.c +++ b/qemu/hw/bt/l2cap.c @@ -17,6 +17,7 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/timer.h" #include "hw/bt.h" diff --git a/qemu/hw/bt/sdp.c b/qemu/hw/bt/sdp.c index c90374795..be26009b0 100644 --- a/qemu/hw/bt/sdp.c +++ b/qemu/hw/bt/sdp.c @@ -17,6 +17,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/bt.h" @@ -42,7 +43,7 @@ struct bt_l2cap_sdp_state_s { static ssize_t sdp_datalen(const uint8_t **element, ssize_t *left) { - size_t len = *(*element) ++ & SDP_DSIZE_MASK; + uint32_t len = *(*element) ++ & SDP_DSIZE_MASK; if (!*left) return -1; @@ -150,12 +151,14 @@ static ssize_t sdp_svc_search(struct bt_l2cap_sdp_state_s *sdp, if (seqlen < 3 || len < seqlen) return -SDP_INVALID_SYNTAX; len -= seqlen; - while (seqlen) if (sdp_svc_match(sdp, &req, &seqlen)) return -SDP_INVALID_SYNTAX; - } else if (sdp_svc_match(sdp, &req, &seqlen)) - return -SDP_INVALID_SYNTAX; + } else { + if (sdp_svc_match(sdp, &req, &len)) { + return -SDP_INVALID_SYNTAX; + } + } if (len < 3) return -SDP_INVALID_SYNTAX; @@ -278,8 +281,11 @@ static ssize_t sdp_attr_get(struct bt_l2cap_sdp_state_s *sdp, while (seqlen) if (sdp_attr_match(record, &req, &seqlen)) return -SDP_INVALID_SYNTAX; - } else if (sdp_attr_match(record, &req, &seqlen)) - return -SDP_INVALID_SYNTAX; + } else { + if (sdp_attr_match(record, &req, &len)) { + return -SDP_INVALID_SYNTAX; + } + } if (len < 1) return -SDP_INVALID_SYNTAX; @@ -393,8 +399,11 @@ static ssize_t sdp_svc_search_attr_get(struct bt_l2cap_sdp_state_s *sdp, while (seqlen) if (sdp_svc_match(sdp, &req, &seqlen)) return -SDP_INVALID_SYNTAX; - } else if (sdp_svc_match(sdp, &req, &seqlen)) - return -SDP_INVALID_SYNTAX; + } else { + if (sdp_svc_match(sdp, &req, &len)) { + return -SDP_INVALID_SYNTAX; + } + } if (len < 3) return -SDP_INVALID_SYNTAX; @@ -413,8 +422,11 @@ static ssize_t sdp_svc_search_attr_get(struct bt_l2cap_sdp_state_s *sdp, while (seqlen) if (sdp_svc_attr_match(sdp, &req, &seqlen)) return -SDP_INVALID_SYNTAX; - } else if (sdp_svc_attr_match(sdp, &req, &seqlen)) - return -SDP_INVALID_SYNTAX; + } else { + if (sdp_svc_attr_match(sdp, &req, &len)) { + return -SDP_INVALID_SYNTAX; + } + } if (len < 1) return -SDP_INVALID_SYNTAX; diff --git a/qemu/hw/char/Makefile.objs b/qemu/hw/char/Makefile.objs index 5931cc840..69a553cd8 100644 --- a/qemu/hw/char/Makefile.objs +++ b/qemu/hw/char/Makefile.objs @@ -16,6 +16,7 @@ obj-$(CONFIG_SH4) += sh_serial.o obj-$(CONFIG_PSERIES) += spapr_vty.o obj-$(CONFIG_DIGIC) += digic-uart.o obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o +obj-$(CONFIG_RASPI) += bcm2835_aux.o common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o diff --git a/qemu/hw/char/bcm2835_aux.c b/qemu/hw/char/bcm2835_aux.c new file mode 100644 index 000000000..0394d11a8 --- /dev/null +++ b/qemu/hw/char/bcm2835_aux.c @@ -0,0 +1,316 @@ +/* + * BCM2835 (Raspberry Pi / Pi 2) Aux block (mini UART and SPI). + * Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * Based on pl011.c, copyright terms below: + * + * Arm PrimeCell PL011 UART + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the GPL. + * + * At present only the core UART functions (data path for tx/rx) are + * implemented. The following features/registers are unimplemented: + * - Line/modem control + * - Scratch register + * - Extra control + * - Baudrate + * - SPI interfaces + */ + +#include "qemu/osdep.h" +#include "hw/char/bcm2835_aux.h" + +#define AUX_IRQ 0x0 +#define AUX_ENABLES 0x4 +#define AUX_MU_IO_REG 0x40 +#define AUX_MU_IER_REG 0x44 +#define AUX_MU_IIR_REG 0x48 +#define AUX_MU_LCR_REG 0x4c +#define AUX_MU_MCR_REG 0x50 +#define AUX_MU_LSR_REG 0x54 +#define AUX_MU_MSR_REG 0x58 +#define AUX_MU_SCRATCH 0x5c +#define AUX_MU_CNTL_REG 0x60 +#define AUX_MU_STAT_REG 0x64 +#define AUX_MU_BAUD_REG 0x68 + +/* bits in IER/IIR registers */ +#define TX_INT 0x1 +#define RX_INT 0x2 + +static void bcm2835_aux_update(BCM2835AuxState *s) +{ + /* signal an interrupt if either: + * 1. rx interrupt is enabled and we have a non-empty rx fifo, or + * 2. the tx interrupt is enabled (since we instantly drain the tx fifo) + */ + s->iir = 0; + if ((s->ier & RX_INT) && s->read_count != 0) { + s->iir |= RX_INT; + } + if (s->ier & TX_INT) { + s->iir |= TX_INT; + } + qemu_set_irq(s->irq, s->iir != 0); +} + +static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size) +{ + BCM2835AuxState *s = opaque; + uint32_t c, res; + + switch (offset) { + case AUX_IRQ: + return s->iir != 0; + + case AUX_ENABLES: + return 1; /* mini UART permanently enabled */ + + case AUX_MU_IO_REG: + /* "DLAB bit set means access baudrate register" is NYI */ + c = s->read_fifo[s->read_pos]; + if (s->read_count > 0) { + s->read_count--; + if (++s->read_pos == BCM2835_AUX_RX_FIFO_LEN) { + s->read_pos = 0; + } + } + if (s->chr) { + qemu_chr_accept_input(s->chr); + } + bcm2835_aux_update(s); + return c; + + case AUX_MU_IER_REG: + /* "DLAB bit set means access baudrate register" is NYI */ + return 0xc0 | s->ier; /* FIFO enables always read 1 */ + + case AUX_MU_IIR_REG: + res = 0xc0; /* FIFO enables */ + /* The spec is unclear on what happens when both tx and rx + * interrupts are active, besides that this cannot occur. At + * present, we choose to prioritise the rx interrupt, since + * the tx fifo is always empty. */ + if (s->read_count != 0) { + res |= 0x4; + } else { + res |= 0x2; + } + if (s->iir == 0) { + res |= 0x1; + } + return res; + + case AUX_MU_LCR_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__); + return 0; + + case AUX_MU_MCR_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__); + return 0; + + case AUX_MU_LSR_REG: + res = 0x60; /* tx idle, empty */ + if (s->read_count != 0) { + res |= 0x1; + } + return res; + + case AUX_MU_MSR_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MSR_REG unsupported\n", __func__); + return 0; + + case AUX_MU_SCRATCH: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__); + return 0; + + case AUX_MU_CNTL_REG: + return 0x3; /* tx, rx enabled */ + + case AUX_MU_STAT_REG: + res = 0x30e; /* space in the output buffer, empty tx fifo, idle tx/rx */ + if (s->read_count > 0) { + res |= 0x1; /* data in input buffer */ + assert(s->read_count < BCM2835_AUX_RX_FIFO_LEN); + res |= ((uint32_t)s->read_count) << 16; /* rx fifo fill level */ + } + return res; + + case AUX_MU_BAUD_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__); + return 0; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return 0; + } +} + +static void bcm2835_aux_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + BCM2835AuxState *s = opaque; + unsigned char ch; + + switch (offset) { + case AUX_ENABLES: + if (value != 1) { + qemu_log_mask(LOG_UNIMP, "%s: unsupported attempt to enable SPI " + "or disable UART\n", __func__); + } + break; + + case AUX_MU_IO_REG: + /* "DLAB bit set means access baudrate register" is NYI */ + ch = value; + if (s->chr) { + qemu_chr_fe_write(s->chr, &ch, 1); + } + break; + + case AUX_MU_IER_REG: + /* "DLAB bit set means access baudrate register" is NYI */ + s->ier = value & (TX_INT | RX_INT); + bcm2835_aux_update(s); + break; + + case AUX_MU_IIR_REG: + if (value & 0x2) { + s->read_count = 0; + } + break; + + case AUX_MU_LCR_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__); + break; + + case AUX_MU_MCR_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__); + break; + + case AUX_MU_SCRATCH: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__); + break; + + case AUX_MU_CNTL_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_CNTL_REG unsupported\n", __func__); + break; + + case AUX_MU_BAUD_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + } + + bcm2835_aux_update(s); +} + +static int bcm2835_aux_can_receive(void *opaque) +{ + BCM2835AuxState *s = opaque; + + return s->read_count < BCM2835_AUX_RX_FIFO_LEN; +} + +static void bcm2835_aux_put_fifo(void *opaque, uint8_t value) +{ + BCM2835AuxState *s = opaque; + int slot; + + slot = s->read_pos + s->read_count; + if (slot >= BCM2835_AUX_RX_FIFO_LEN) { + slot -= BCM2835_AUX_RX_FIFO_LEN; + } + s->read_fifo[slot] = value; + s->read_count++; + if (s->read_count == BCM2835_AUX_RX_FIFO_LEN) { + /* buffer full */ + } + bcm2835_aux_update(s); +} + +static void bcm2835_aux_receive(void *opaque, const uint8_t *buf, int size) +{ + bcm2835_aux_put_fifo(opaque, *buf); +} + +static const MemoryRegionOps bcm2835_aux_ops = { + .read = bcm2835_aux_read, + .write = bcm2835_aux_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static const VMStateDescription vmstate_bcm2835_aux = { + .name = TYPE_BCM2835_AUX, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(read_fifo, BCM2835AuxState, + BCM2835_AUX_RX_FIFO_LEN), + VMSTATE_UINT8(read_pos, BCM2835AuxState), + VMSTATE_UINT8(read_count, BCM2835AuxState), + VMSTATE_UINT8(ier, BCM2835AuxState), + VMSTATE_UINT8(iir, BCM2835AuxState), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2835_aux_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + BCM2835AuxState *s = BCM2835_AUX(obj); + + memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_aux_ops, s, + TYPE_BCM2835_AUX, 0x100); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); +} + +static void bcm2835_aux_realize(DeviceState *dev, Error **errp) +{ + BCM2835AuxState *s = BCM2835_AUX(dev); + + if (s->chr) { + qemu_chr_add_handlers(s->chr, bcm2835_aux_can_receive, + bcm2835_aux_receive, NULL, s); + } +} + +static Property bcm2835_aux_props[] = { + DEFINE_PROP_CHR("chardev", BCM2835AuxState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void bcm2835_aux_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = bcm2835_aux_realize; + dc->vmsd = &vmstate_bcm2835_aux; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); + dc->props = bcm2835_aux_props; +} + +static const TypeInfo bcm2835_aux_info = { + .name = TYPE_BCM2835_AUX, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835AuxState), + .instance_init = bcm2835_aux_init, + .class_init = bcm2835_aux_class_init, +}; + +static void bcm2835_aux_register_types(void) +{ + type_register_static(&bcm2835_aux_info); +} + +type_init(bcm2835_aux_register_types) diff --git a/qemu/hw/char/cadence_uart.c b/qemu/hw/char/cadence_uart.c index 9d379e5b1..797787823 100644 --- a/qemu/hw/char/cadence_uart.c +++ b/qemu/hw/char/cadence_uart.c @@ -16,6 +16,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/char/cadence_uart.h" #ifdef CADENCE_UART_ERR_DEBUG @@ -204,7 +205,7 @@ static void uart_parameters_setup(CadenceUARTState *s) } packet_size += ssp.data_bits + ssp.stop_bits; - s->char_tx_time = (get_ticks_per_sec() / ssp.speed) * packet_size; + s->char_tx_time = (NANOSECONDS_PER_SECOND / ssp.speed) * packet_size; if (s->chr) { qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); } @@ -374,6 +375,9 @@ static void uart_write(void *opaque, hwaddr offset, DB_PRINT(" offset:%x data:%08x\n", (unsigned)offset, (unsigned)value); offset >>= 2; + if (offset >= CADENCE_UART_R_MAX) { + return; + } switch (offset) { case R_IER: /* ier (wts imr) */ s->r[R_IMR] |= value; @@ -478,7 +482,7 @@ static void cadence_uart_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); - s->char_tx_time = (get_ticks_per_sec() / 9600) * 10; + s->char_tx_time = (NANOSECONDS_PER_SECOND / 9600) * 10; } static int cadence_uart_post_load(void *opaque, int version_id) diff --git a/qemu/hw/char/debugcon.c b/qemu/hw/char/debugcon.c index 36f1c4adb..e7f025ec6 100644 --- a/qemu/hw/char/debugcon.c +++ b/qemu/hw/char/debugcon.c @@ -24,6 +24,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "sysemu/char.h" #include "hw/isa/isa.h" diff --git a/qemu/hw/char/digic-uart.c b/qemu/hw/char/digic-uart.c index 6d44576ff..d3bc533d7 100644 --- a/qemu/hw/char/digic-uart.c +++ b/qemu/hw/char/digic-uart.c @@ -26,6 +26,7 @@ * */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "sysemu/char.h" diff --git a/qemu/hw/char/escc.c b/qemu/hw/char/escc.c index ba653efd6..7bf09a007 100644 --- a/qemu/hw/char/escc.c +++ b/qemu/hw/char/escc.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "hw/char/escc.h" @@ -714,7 +715,7 @@ MemoryRegion *escc_init(hwaddr base, qemu_irq irqA, qemu_irq irqB, return &d->mmio; } -static const uint8_t qcode_to_keycode[Q_KEY_CODE_MAX] = { +static const uint8_t qcode_to_keycode[Q_KEY_CODE__MAX] = { [Q_KEY_CODE_SHIFT] = 99, [Q_KEY_CODE_SHIFT_R] = 110, [Q_KEY_CODE_ALT] = 19, @@ -841,14 +842,16 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src, { ChannelState *s = (ChannelState *)dev; int qcode, keycode; + InputKeyEvent *key; - assert(evt->kind == INPUT_EVENT_KIND_KEY); - qcode = qemu_input_key_value_to_qcode(evt->key->key); + assert(evt->type == INPUT_EVENT_KIND_KEY); + key = evt->u.key.data; + qcode = qemu_input_key_value_to_qcode(key->key); trace_escc_sunkbd_event_in(qcode, QKeyCode_lookup[qcode], - evt->key->down); + key->down); if (qcode == Q_KEY_CODE_CAPS_LOCK) { - if (evt->key->down) { + if (key->down) { s->caps_lock_mode ^= 1; if (s->caps_lock_mode == 2) { return; /* Drop second press */ @@ -862,7 +865,7 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src, } if (qcode == Q_KEY_CODE_NUM_LOCK) { - if (evt->key->down) { + if (key->down) { s->num_lock_mode ^= 1; if (s->num_lock_mode == 2) { return; /* Drop second press */ @@ -876,7 +879,7 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src, } keycode = qcode_to_keycode[qcode]; - if (!evt->key->down) { + if (!key->down) { keycode |= 0x80; } trace_escc_sunkbd_event_out(keycode); @@ -1035,6 +1038,7 @@ static void escc_class_init(ObjectClass *klass, void *data) dc->reset = escc_reset; dc->vmsd = &vmstate_escc; dc->props = escc_properties; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } static const TypeInfo escc_info = { diff --git a/qemu/hw/char/etraxfs_ser.c b/qemu/hw/char/etraxfs_ser.c index 857c13621..146b387e7 100644 --- a/qemu/hw/char/etraxfs_ser.c +++ b/qemu/hw/char/etraxfs_ser.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "sysemu/char.h" #include "qemu/log.h" @@ -165,7 +166,7 @@ static void serial_receive(void *opaque, const uint8_t *buf, int size) /* Got a byte. */ if (s->rx_fifo_len >= 16) { - qemu_log("WARNING: UART dropped char.\n"); + D(qemu_log("WARNING: UART dropped char.\n")); return; } @@ -182,15 +183,13 @@ static void serial_receive(void *opaque, const uint8_t *buf, int size) static int serial_can_receive(void *opaque) { ETRAXSerial *s = opaque; - int r; /* Is the receiver enabled? */ if (!(s->regs[RW_REC_CTRL] & (1 << 3))) { return 0; } - r = sizeof(s->rx_fifo) - s->rx_fifo_len; - return r; + return sizeof(s->rx_fifo) - s->rx_fifo_len; } static void serial_event(void *opaque, int event) diff --git a/qemu/hw/char/exynos4210_uart.c b/qemu/hw/char/exynos4210_uart.c index 7614e5860..885ecc027 100644 --- a/qemu/hw/char/exynos4210_uart.c +++ b/qemu/hw/char/exynos4210_uart.c @@ -19,7 +19,9 @@ * */ +#include "qemu/osdep.h" #include "hw/sysbus.h" +#include "qemu/error-report.h" #include "sysemu/sysemu.h" #include "sysemu/char.h" @@ -234,10 +236,8 @@ static int fifo_empty_elements_number(Exynos4210UartFIFO *q) static void fifo_reset(Exynos4210UartFIFO *q) { - if (q->data != NULL) { - g_free(q->data); - q->data = NULL; - } + g_free(q->data); + q->data = NULL; q->data = (uint8_t *)g_malloc0(q->size); @@ -597,15 +597,17 @@ DeviceState *exynos4210_uart_create(hwaddr addr, if (!chr) { if (channel >= MAX_SERIAL_PORTS) { - hw_error("Only %d serial ports are supported by QEMU.\n", - MAX_SERIAL_PORTS); + error_report("Only %d serial ports are supported by QEMU", + MAX_SERIAL_PORTS); + exit(1); } chr = serial_hds[channel]; if (!chr) { snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, channel); chr = qemu_chr_new(label, "null", NULL); if (!(chr)) { - hw_error("Can't assign serial port to UART%d.\n", channel); + error_report("Can't assign serial port to UART%d", channel); + exit(1); } } } diff --git a/qemu/hw/char/grlib_apbuart.c b/qemu/hw/char/grlib_apbuart.c index 35ef66177..871524c82 100644 --- a/qemu/hw/char/grlib_apbuart.c +++ b/qemu/hw/char/grlib_apbuart.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "sysemu/char.h" diff --git a/qemu/hw/char/imx_serial.c b/qemu/hw/char/imx_serial.c index f3fbc776b..6df74ac7c 100644 --- a/qemu/hw/char/imx_serial.c +++ b/qemu/hw/char/imx_serial.c @@ -4,6 +4,7 @@ * Copyright (c) 2008 OKL * Originally Written by Hans Jiang * Copyright (c) 2011 NICTA Pty Ltd. + * Updated by Jean-Christophe Dubois * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -17,63 +18,25 @@ * is a real serial device. */ -#include "hw/hw.h" -#include "hw/sysbus.h" +#include "qemu/osdep.h" +#include "hw/char/imx_serial.h" #include "sysemu/sysemu.h" #include "sysemu/char.h" -#include "hw/arm/imx.h" -//#define DEBUG_SERIAL 1 -#ifdef DEBUG_SERIAL -#define DPRINTF(fmt, args...) \ -do { printf("imx_serial: " fmt , ##args); } while (0) -#else -#define DPRINTF(fmt, args...) do {} while (0) -#endif - -/* - * Define to 1 for messages about attempts to - * access unimplemented registers or similar. - */ -//#define DEBUG_IMPLEMENTATION 1 -#ifdef DEBUG_IMPLEMENTATION -# define IPRINTF(fmt, args...) \ - do { fprintf(stderr, "imx_serial: " fmt, ##args); } while (0) -#else -# define IPRINTF(fmt, args...) do {} while (0) +#ifndef DEBUG_IMX_UART +#define DEBUG_IMX_UART 0 #endif -#define TYPE_IMX_SERIAL "imx-serial" -#define IMX_SERIAL(obj) OBJECT_CHECK(IMXSerialState, (obj), TYPE_IMX_SERIAL) - -typedef struct IMXSerialState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - int32_t readbuff; - - uint32_t usr1; - uint32_t usr2; - uint32_t ucr1; - uint32_t ucr2; - uint32_t uts1; - - /* - * The registers below are implemented just so that the - * guest OS sees what it has written - */ - uint32_t onems; - uint32_t ufcr; - uint32_t ubmr; - uint32_t ubrc; - uint32_t ucr3; - - qemu_irq irq; - CharDriverState *chr; -} IMXSerialState; +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_UART) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_SERIAL, \ + __func__, ##args); \ + } \ + } while (0) static const VMStateDescription vmstate_imx_serial = { - .name = "imx-serial", + .name = TYPE_IMX_SERIAL, .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { @@ -91,61 +54,14 @@ static const VMStateDescription vmstate_imx_serial = { }, }; - -#define URXD_CHARRDY (1<<15) /* character read is valid */ -#define URXD_ERR (1<<14) /* Character has error */ -#define URXD_BRK (1<<11) /* Break received */ - -#define USR1_PARTYER (1<<15) /* Parity Error */ -#define USR1_RTSS (1<<14) /* RTS pin status */ -#define USR1_TRDY (1<<13) /* Tx ready */ -#define USR1_RTSD (1<<12) /* RTS delta: pin changed state */ -#define USR1_ESCF (1<<11) /* Escape sequence interrupt */ -#define USR1_FRAMERR (1<<10) /* Framing error */ -#define USR1_RRDY (1<<9) /* receiver ready */ -#define USR1_AGTIM (1<<8) /* Aging timer interrupt */ -#define USR1_DTRD (1<<7) /* DTR changed */ -#define USR1_RXDS (1<<6) /* Receiver is idle */ -#define USR1_AIRINT (1<<5) /* Aysnch IR interrupt */ -#define USR1_AWAKE (1<<4) /* Falling edge detected on RXd pin */ - -#define USR2_ADET (1<<15) /* Autobaud complete */ -#define USR2_TXFE (1<<14) /* Transmit FIFO empty */ -#define USR2_DTRF (1<<13) /* DTR/DSR transition */ -#define USR2_IDLE (1<<12) /* UART has been idle for too long */ -#define USR2_ACST (1<<11) /* Autobaud counter stopped */ -#define USR2_RIDELT (1<<10) /* Ring Indicator delta */ -#define USR2_RIIN (1<<9) /* Ring Indicator Input */ -#define USR2_IRINT (1<<8) /* Serial Infrared Interrupt */ -#define USR2_WAKE (1<<7) /* Start bit detected */ -#define USR2_DCDDELT (1<<6) /* Data Carrier Detect delta */ -#define USR2_DCDIN (1<<5) /* Data Carrier Detect Input */ -#define USR2_RTSF (1<<4) /* RTS transition */ -#define USR2_TXDC (1<<3) /* Transmission complete */ -#define USR2_BRCD (1<<2) /* Break condition detected */ -#define USR2_ORE (1<<1) /* Overrun error */ -#define USR2_RDR (1<<0) /* Receive data ready */ - -#define UCR1_TRDYEN (1<<13) /* Tx Ready Interrupt Enable */ -#define UCR1_RRDYEN (1<<9) /* Rx Ready Interrupt Enable */ -#define UCR1_TXMPTYEN (1<<6) /* Tx Empty Interrupt Enable */ -#define UCR1_UARTEN (1<<0) /* UART Enable */ - -#define UCR2_TXEN (1<<2) /* Transmitter enable */ -#define UCR2_RXEN (1<<1) /* Receiver enable */ -#define UCR2_SRST (1<<0) /* Reset complete */ - -#define UTS1_TXEMPTY (1<<6) -#define UTS1_RXEMPTY (1<<5) -#define UTS1_TXFULL (1<<4) -#define UTS1_RXFULL (1<<3) - static void imx_update(IMXSerialState *s) { uint32_t flags; flags = (s->usr1 & s->ucr1) & (USR1_TRDY|USR1_RRDY); - if (!(s->ucr1 & UCR1_TXMPTYEN)) { + if (s->ucr1 & UCR1_TXMPTYEN) { + flags |= (s->uts1 & UTS1_TXEMPTY); + } else { flags &= ~USR1_TRDY; } @@ -192,7 +108,8 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset, IMXSerialState *s = (IMXSerialState *)opaque; uint32_t c; - DPRINTF("read(offset=%x)\n", offset >> 2); + DPRINTF("read(offset=0x%" HWADDR_PRIx ")\n", offset); + switch (offset >> 2) { case 0x0: /* URXD */ c = s->readbuff; @@ -203,7 +120,9 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset, s->usr2 &= ~USR2_RDR; s->uts1 |= UTS1_RXEMPTY; imx_update(s); - qemu_chr_accept_input(s->chr); + if (s->chr) { + qemu_chr_accept_input(s->chr); + } } return c; @@ -242,20 +161,20 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset, return 0x0; /* TODO */ default: - IPRINTF("imx_serial_read: bad offset: 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset); return 0; } } static void imx_serial_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) + uint64_t value, unsigned size) { IMXSerialState *s = (IMXSerialState *)opaque; unsigned char ch; - DPRINTF("write(offset=%x, value = %x) to %s\n", - offset >> 2, - (unsigned int)value, s->chr ? s->chr->label : "NODEV"); + DPRINTF("write(offset=0x%" HWADDR_PRIx ", value = 0x%x) to %s\n", + offset, (unsigned int)value, s->chr ? s->chr->label : "NODEV"); switch (offset >> 2) { case 0x10: /* UTXD */ @@ -273,7 +192,9 @@ static void imx_serial_write(void *opaque, hwaddr offset, case 0x20: /* UCR1 */ s->ucr1 = value & 0xffff; + DPRINTF("write(ucr1=%x)\n", (unsigned int)value); + imx_update(s); break; @@ -290,7 +211,9 @@ static void imx_serial_write(void *opaque, hwaddr offset, } if (value & UCR2_RXEN) { if (!(s->ucr2 & UCR2_RXEN)) { - qemu_chr_accept_input(s->chr); + if (s->chr) { + qemu_chr_accept_input(s->chr); + } } } s->ucr2 = value & 0xffff; @@ -298,25 +221,25 @@ static void imx_serial_write(void *opaque, hwaddr offset, case 0x25: /* USR1 */ value &= USR1_AWAKE | USR1_AIRINT | USR1_DTRD | USR1_AGTIM | - USR1_FRAMERR | USR1_ESCF | USR1_RTSD | USR1_PARTYER; + USR1_FRAMERR | USR1_ESCF | USR1_RTSD | USR1_PARTYER; s->usr1 &= ~value; break; case 0x26: /* USR2 */ - /* - * Writing 1 to some bits clears them; all other - * values are ignored - */ + /* + * Writing 1 to some bits clears them; all other + * values are ignored + */ value &= USR2_ADET | USR2_DTRF | USR2_IDLE | USR2_ACST | - USR2_RIDELT | USR2_IRINT | USR2_WAKE | - USR2_DCDDELT | USR2_RTSF | USR2_BRCD | USR2_ORE; + USR2_RIDELT | USR2_IRINT | USR2_WAKE | + USR2_DCDDELT | USR2_RTSF | USR2_BRCD | USR2_ORE; s->usr2 &= ~value; break; - /* - * Linux expects to see what it writes to these registers - * We don't currently alter the baud rate - */ + /* + * Linux expects to see what it writes to these registers + * We don't currently alter the baud rate + */ case 0x29: /* UBIR */ s->ubrc = value & 0xffff; break; @@ -339,12 +262,14 @@ static void imx_serial_write(void *opaque, hwaddr offset, case 0x2d: /* UTS1 */ case 0x23: /* UCR4 */ - IPRINTF("Unimplemented Register %x written to\n", offset >> 2); + qemu_log_mask(LOG_UNIMP, "[%s]%s: Unimplemented reg 0x%" + HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset); /* TODO */ break; default: - IPRINTF("imx_serial_write: Bad offset 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset); } } @@ -357,7 +282,9 @@ static int imx_can_receive(void *opaque) static void imx_put_data(void *opaque, uint32_t value) { IMXSerialState *s = (IMXSerialState *)opaque; + DPRINTF("received char\n"); + s->usr1 |= USR1_RRDY; s->usr2 |= USR2_RDR; s->uts1 &= ~UTS1_RXEMPTY; @@ -384,62 +311,30 @@ static const struct MemoryRegionOps imx_serial_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int imx_serial_init(SysBusDevice *dev) +static void imx_serial_realize(DeviceState *dev, Error **errp) { IMXSerialState *s = IMX_SERIAL(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &imx_serial_ops, s, - "imx-serial", 0x1000); - sysbus_init_mmio(dev, &s->iomem); - sysbus_init_irq(dev, &s->irq); - if (s->chr) { qemu_chr_add_handlers(s->chr, imx_can_receive, imx_receive, imx_event, s); } else { - DPRINTF("No char dev for uart at 0x%lx\n", - (unsigned long)s->iomem.ram_addr); + DPRINTF("No char dev for uart\n"); } - - return 0; } -void imx_serial_create(int uart, const hwaddr addr, qemu_irq irq) +static void imx_serial_init(Object *obj) { - DeviceState *dev; - SysBusDevice *bus; - CharDriverState *chr; - const char chr_name[] = "serial"; - char label[ARRAY_SIZE(chr_name) + 1]; - - dev = qdev_create(NULL, TYPE_IMX_SERIAL); - - if (uart >= MAX_SERIAL_PORTS) { - hw_error("Cannot assign uart %d: QEMU supports only %d ports\n", - uart, MAX_SERIAL_PORTS); - } - chr = serial_hds[uart]; - if (!chr) { - snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, uart); - chr = qemu_chr_new(label, "null", NULL); - if (!(chr)) { - hw_error("Can't assign serial port to imx-uart%d.\n", uart); - } - } - - qdev_prop_set_chr(dev, "chardev", chr); - bus = SYS_BUS_DEVICE(dev); - qdev_init_nofail(dev); - if (addr != (hwaddr)-1) { - sysbus_mmio_map(bus, 0, addr); - } - sysbus_connect_irq(bus, 0, irq); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + IMXSerialState *s = IMX_SERIAL(obj); + memory_region_init_io(&s->iomem, obj, &imx_serial_ops, s, + TYPE_IMX_SERIAL, 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); } - -static Property imx32_serial_properties[] = { +static Property imx_serial_properties[] = { DEFINE_PROP_CHR("chardev", IMXSerialState, chr), DEFINE_PROP_END_OF_LIST(), }; @@ -447,21 +342,21 @@ static Property imx32_serial_properties[] = { static void imx_serial_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = imx_serial_init; + dc->realize = imx_serial_realize; dc->vmsd = &vmstate_imx_serial; dc->reset = imx_serial_reset_at_boot; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); dc->desc = "i.MX series UART"; - dc->props = imx32_serial_properties; + dc->props = imx_serial_properties; } static const TypeInfo imx_serial_info = { - .name = TYPE_IMX_SERIAL, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IMXSerialState), - .class_init = imx_serial_class_init, + .name = TYPE_IMX_SERIAL, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMXSerialState), + .instance_init = imx_serial_init, + .class_init = imx_serial_class_init, }; static void imx_serial_register_types(void) diff --git a/qemu/hw/char/ipoctal232.c b/qemu/hw/char/ipoctal232.c index c8d5cdb36..bc0ae4980 100644 --- a/qemu/hw/char/ipoctal232.c +++ b/qemu/hw/char/ipoctal232.c @@ -8,6 +8,7 @@ * later version. */ +#include "qemu/osdep.h" #include "hw/ipack/ipack.h" #include "qemu/bitops.h" #include "sysemu/char.h" diff --git a/qemu/hw/char/lm32_juart.c b/qemu/hw/char/lm32_juart.c index 62763f2f4..5bf8acfe8 100644 --- a/qemu/hw/char/lm32_juart.c +++ b/qemu/hw/char/lm32_juart.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "trace.h" diff --git a/qemu/hw/char/lm32_uart.c b/qemu/hw/char/lm32_uart.c index 837a46e8e..036813d0f 100644 --- a/qemu/hw/char/lm32_uart.c +++ b/qemu/hw/char/lm32_uart.c @@ -22,6 +22,7 @@ */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "trace.h" diff --git a/qemu/hw/char/mcf_uart.c b/qemu/hw/char/mcf_uart.c index 98fd44e66..3c0438fd7 100644 --- a/qemu/hw/char/mcf_uart.c +++ b/qemu/hw/char/mcf_uart.c @@ -5,6 +5,7 @@ * * This code is licensed under the GPL */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/m68k/mcf.h" #include "sysemu/char.h" @@ -126,7 +127,7 @@ static void mcf_uart_do_tx(mcf_uart_state *s) static void mcf_do_command(mcf_uart_state *s, uint8_t cmd) { /* Misc command. */ - switch ((cmd >> 4) & 3) { + switch ((cmd >> 4) & 7) { case 0: /* No-op. */ break; case 1: /* Reset mode register pointer. */ diff --git a/qemu/hw/char/milkymist-uart.c b/qemu/hw/char/milkymist-uart.c index 9b89b7e63..03b36b223 100644 --- a/qemu/hw/char/milkymist-uart.c +++ b/qemu/hw/char/milkymist-uart.c @@ -21,6 +21,7 @@ * http://www.milkymist.org/socdoc/uart.pdf */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "trace.h" diff --git a/qemu/hw/char/omap_uart.c b/qemu/hw/char/omap_uart.c index 88f20943e..415bec5fa 100644 --- a/qemu/hw/char/omap_uart.c +++ b/qemu/hw/char/omap_uart.c @@ -17,6 +17,7 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ +#include "qemu/osdep.h" #include "sysemu/char.h" #include "hw/hw.h" #include "hw/arm/omap.h" @@ -55,8 +56,7 @@ struct omap_uart_s *omap_uart_init(hwaddr base, qemu_irq txdma, qemu_irq rxdma, const char *label, CharDriverState *chr) { - struct omap_uart_s *s = (struct omap_uart_s *) - g_malloc0(sizeof(struct omap_uart_s)); + struct omap_uart_s *s = g_new0(struct omap_uart_s, 1); s->base = base; s->fclk = fclk; diff --git a/qemu/hw/char/parallel.c b/qemu/hw/char/parallel.c index c2b553f0d..11c78fed8 100644 --- a/qemu/hw/char/parallel.c +++ b/qemu/hw/char/parallel.c @@ -22,6 +22,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "sysemu/char.h" #include "hw/isa/isa.h" diff --git a/qemu/hw/char/pl011.c b/qemu/hw/char/pl011.c index eac6fac09..210c87b4c 100644 --- a/qemu/hw/char/pl011.c +++ b/qemu/hw/char/pl011.c @@ -7,6 +7,7 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "sysemu/char.h" diff --git a/qemu/hw/char/sclpconsole-lm.c b/qemu/hw/char/sclpconsole-lm.c index 02ac80b65..7d4ff8120 100644 --- a/qemu/hw/char/sclpconsole-lm.c +++ b/qemu/hw/char/sclpconsole-lm.c @@ -13,6 +13,7 @@ * */ +#include "qemu/osdep.h" #include "hw/qdev.h" #include "qemu/thread.h" #include "qemu/error-report.h" diff --git a/qemu/hw/char/sclpconsole.c b/qemu/hw/char/sclpconsole.c index b014c7f52..45997ff4a 100644 --- a/qemu/hw/char/sclpconsole.c +++ b/qemu/hw/char/sclpconsole.c @@ -12,6 +12,7 @@ * */ +#include "qemu/osdep.h" #include #include "qemu/thread.h" #include "qemu/error-report.h" diff --git a/qemu/hw/char/serial-isa.c b/qemu/hw/char/serial-isa.c index f3db024d0..1594ec4db 100644 --- a/qemu/hw/char/serial-isa.c +++ b/qemu/hw/char/serial-isa.c @@ -23,6 +23,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/char/serial.h" #include "hw/isa/isa.h" diff --git a/qemu/hw/char/serial-pci.c b/qemu/hw/char/serial-pci.c index 1c8b9be5c..303104dd1 100644 --- a/qemu/hw/char/serial-pci.c +++ b/qemu/hw/char/serial-pci.c @@ -25,6 +25,8 @@ /* see docs/specs/pci-serial.txt */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/char/serial.h" #include "hw/pci/pci.h" diff --git a/qemu/hw/char/serial.c b/qemu/hw/char/serial.c index 513d73c27..6d815b5c6 100644 --- a/qemu/hw/char/serial.c +++ b/qemu/hw/char/serial.c @@ -23,8 +23,10 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/char/serial.h" #include "sysemu/char.h" +#include "qapi/error.h" #include "qemu/timer.h" #include "exec/address-spaces.h" #include "qemu/error-report.h" @@ -177,7 +179,7 @@ static void serial_update_parameters(SerialState *s) ssp.parity = parity; ssp.data_bits = data_bits; ssp.stop_bits = stop_bits; - s->char_transmit_time = (get_ticks_per_sec() / speed) * frame_size; + s->char_transmit_time = (NANOSECONDS_PER_SECOND / speed) * frame_size; qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); DPRINTF("speed=%d parity=%c data=%d stop=%d\n", @@ -215,8 +217,10 @@ static void serial_update_msl(SerialState *s) /* The real 16550A apparently has a 250ns response latency to line status changes. We'll be lazy and poll only every 10ms, and only poll it at all if MSI interrupts are turned on */ - if (s->poll_msl) - timer_mod(s->modem_status_poll, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + get_ticks_per_sec() / 100); + if (s->poll_msl) { + timer_mod(s->modem_status_poll, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + NANOSECONDS_PER_SECOND / 100); + } } static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque) @@ -822,7 +826,7 @@ static void serial_reset(void *opaque) s->mcr = UART_MCR_OUT2; s->scr = 0; s->tsr_retry = 0; - s->char_transmit_time = (get_ticks_per_sec() / 9600) * 10; + s->char_transmit_time = (NANOSECONDS_PER_SECOND / 9600) * 10; s->poll_msl = 0; s->timeout_ipending = 0; @@ -888,18 +892,13 @@ SerialState *serial_init(int base, qemu_irq irq, int baudbase, CharDriverState *chr, MemoryRegion *system_io) { SerialState *s; - Error *err = NULL; s = g_malloc0(sizeof(SerialState)); s->irq = irq; s->baudbase = baudbase; s->chr = chr; - serial_realize_core(s, &err); - if (err != NULL) { - error_report_err(err); - exit(1); - } + serial_realize_core(s, &error_fatal); vmstate_register(NULL, base, &vmstate_serial, s); @@ -949,7 +948,6 @@ SerialState *serial_mm_init(MemoryRegion *address_space, CharDriverState *chr, enum device_endian end) { SerialState *s; - Error *err = NULL; s = g_malloc0(sizeof(SerialState)); @@ -958,11 +956,7 @@ SerialState *serial_mm_init(MemoryRegion *address_space, s->baudbase = baudbase; s->chr = chr; - serial_realize_core(s, &err); - if (err != NULL) { - error_report_err(err); - exit(1); - } + serial_realize_core(s, &error_fatal); vmstate_register(NULL, base, &vmstate_serial, s); memory_region_init_io(&s->io, NULL, &serial_mm_ops[end], s, diff --git a/qemu/hw/char/sh_serial.c b/qemu/hw/char/sh_serial.c index 9328dd1b5..4c55dcb7d 100644 --- a/qemu/hw/char/sh_serial.c +++ b/qemu/hw/char/sh_serial.c @@ -24,6 +24,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sh4/sh.h" #include "sysemu/char.h" diff --git a/qemu/hw/char/spapr_vty.c b/qemu/hw/char/spapr_vty.c index 36b328b9a..3498d7b05 100644 --- a/qemu/hw/char/spapr_vty.c +++ b/qemu/hw/char/spapr_vty.c @@ -1,3 +1,7 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/qdev.h" #include "sysemu/char.h" #include "hw/ppc/spapr.h" diff --git a/qemu/hw/char/stm32f2xx_usart.c b/qemu/hw/char/stm32f2xx_usart.c index c9d3a1be7..a94d61ceb 100644 --- a/qemu/hw/char/stm32f2xx_usart.c +++ b/qemu/hw/char/stm32f2xx_usart.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/char/stm32f2xx_usart.h" #ifndef STM_USART_ERR_DEBUG diff --git a/qemu/hw/char/virtio-console.c b/qemu/hw/char/virtio-console.c index 2a867cb4e..2e36481a7 100644 --- a/qemu/hw/char/virtio-console.c +++ b/qemu/hw/char/virtio-console.c @@ -10,6 +10,7 @@ * the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "sysemu/char.h" #include "qemu/error-report.h" #include "trace.h" diff --git a/qemu/hw/char/virtio-serial-bus.c b/qemu/hw/char/virtio-serial-bus.c index bc56f5d52..6e5de6dec 100644 --- a/qemu/hw/char/virtio-serial-bus.c +++ b/qemu/hw/char/virtio-serial-bus.c @@ -18,6 +18,8 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/iov.h" #include "monitor/monitor.h" #include "qemu/error-report.h" @@ -76,13 +78,13 @@ static VirtIOSerialPort *find_port_by_name(char *name) static bool use_multiport(VirtIOSerial *vser) { VirtIODevice *vdev = VIRTIO_DEVICE(vser); - return virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT); + return virtio_vdev_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT); } static size_t write_to_port(VirtIOSerialPort *port, const uint8_t *buf, size_t size) { - VirtQueueElement elem; + VirtQueueElement *elem; VirtQueue *vq; size_t offset; @@ -95,15 +97,17 @@ static size_t write_to_port(VirtIOSerialPort *port, while (offset < size) { size_t len; - if (!virtqueue_pop(vq, &elem)) { + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!elem) { break; } - len = iov_from_buf(elem.in_sg, elem.in_num, 0, + len = iov_from_buf(elem->in_sg, elem->in_num, 0, buf + offset, size - offset); offset += len; - virtqueue_push(vq, &elem, len); + virtqueue_push(vq, elem, len); + g_free(elem); } virtio_notify(VIRTIO_DEVICE(port->vser), vq); @@ -112,13 +116,18 @@ static size_t write_to_port(VirtIOSerialPort *port, static void discard_vq_data(VirtQueue *vq, VirtIODevice *vdev) { - VirtQueueElement elem; + VirtQueueElement *elem; if (!virtio_queue_ready(vq)) { return; } - while (virtqueue_pop(vq, &elem)) { - virtqueue_push(vq, &elem, 0); + for (;;) { + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!elem) { + break; + } + virtqueue_push(vq, elem, 0); + g_free(elem); } virtio_notify(vdev, vq); } @@ -137,21 +146,22 @@ static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, unsigned int i; /* Pop an elem only if we haven't left off a previous one mid-way */ - if (!port->elem.out_num) { - if (!virtqueue_pop(vq, &port->elem)) { + if (!port->elem) { + port->elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!port->elem) { break; } port->iov_idx = 0; port->iov_offset = 0; } - for (i = port->iov_idx; i < port->elem.out_num; i++) { + for (i = port->iov_idx; i < port->elem->out_num; i++) { size_t buf_size; ssize_t ret; - buf_size = port->elem.out_sg[i].iov_len - port->iov_offset; + buf_size = port->elem->out_sg[i].iov_len - port->iov_offset; ret = vsc->have_data(port, - port->elem.out_sg[i].iov_base + port->elem->out_sg[i].iov_base + port->iov_offset, buf_size); if (port->throttled) { @@ -166,8 +176,9 @@ static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, if (port->throttled) { break; } - virtqueue_push(vq, &port->elem, 0); - port->elem.out_num = 0; + virtqueue_push(vq, port->elem, 0); + g_free(port->elem); + port->elem = NULL; } virtio_notify(vdev, vq); } @@ -184,22 +195,26 @@ static void flush_queued_data(VirtIOSerialPort *port) static size_t send_control_msg(VirtIOSerial *vser, void *buf, size_t len) { - VirtQueueElement elem; + VirtQueueElement *elem; VirtQueue *vq; vq = vser->c_ivq; if (!virtio_queue_ready(vq)) { return 0; } - if (!virtqueue_pop(vq, &elem)) { + + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!elem) { return 0; } /* TODO: detect a buffer that's too short, set NEEDS_RESET */ - iov_from_buf(elem.in_sg, elem.in_num, 0, buf, len); + iov_from_buf(elem->in_sg, elem->in_num, 0, buf, len); - virtqueue_push(vq, &elem, len); + virtqueue_push(vq, elem, len); virtio_notify(VIRTIO_DEVICE(vser), vq); + g_free(elem); + return len; } @@ -413,7 +428,7 @@ static void control_in(VirtIODevice *vdev, VirtQueue *vq) static void control_out(VirtIODevice *vdev, VirtQueue *vq) { - VirtQueueElement elem; + VirtQueueElement *elem; VirtIOSerial *vser; uint8_t *buf; size_t len; @@ -422,10 +437,15 @@ static void control_out(VirtIODevice *vdev, VirtQueue *vq) len = 0; buf = NULL; - while (virtqueue_pop(vq, &elem)) { + for (;;) { size_t cur_len; - cur_len = iov_size(elem.out_sg, elem.out_num); + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!elem) { + break; + } + + cur_len = iov_size(elem->out_sg, elem->out_num); /* * Allocate a new buf only if we didn't have one previously or * if the size of the buf differs @@ -436,10 +456,11 @@ static void control_out(VirtIODevice *vdev, VirtQueue *vq) buf = g_malloc(cur_len); len = cur_len; } - iov_to_buf(elem.out_sg, elem.out_num, 0, buf, cur_len); + iov_to_buf(elem->out_sg, elem->out_num, 0, buf, cur_len); handle_control_message(vser, buf, cur_len); - virtqueue_push(vq, &elem, 0); + virtqueue_push(vq, elem, 0); + g_free(elem); } g_free(buf); virtio_notify(vdev, vq); @@ -619,16 +640,14 @@ static void virtio_serial_save_device(VirtIODevice *vdev, QEMUFile *f) qemu_put_byte(f, port->host_connected); elem_popped = 0; - if (port->elem.out_num) { + if (port->elem) { elem_popped = 1; } qemu_put_be32s(f, &elem_popped); if (elem_popped) { qemu_put_be32s(f, &port->iov_idx); qemu_put_be64s(f, &port->iov_offset); - - qemu_put_buffer(f, (unsigned char *)&port->elem, - sizeof(port->elem)); + qemu_put_virtqueue_element(f, port->elem); } } } @@ -703,12 +722,8 @@ static int fetch_active_ports_list(QEMUFile *f, int version_id, qemu_get_be32s(f, &port->iov_idx); qemu_get_be64s(f, &port->iov_offset); - qemu_get_buffer(f, (unsigned char *)&port->elem, - sizeof(port->elem)); - virtqueue_map_sg(port->elem.in_sg, port->elem.in_addr, - port->elem.in_num, 1); - virtqueue_map_sg(port->elem.out_sg, port->elem.out_addr, - port->elem.out_num, 1); + port->elem = + qemu_get_virtqueue_element(f, sizeof(VirtQueueElement)); /* * Port was throttled on source machine. Let's @@ -801,7 +816,7 @@ static const TypeInfo virtser_bus_info = { static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) { - VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev); + VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(qdev); monitor_printf(mon, "%*sport %d, guest %s, host %s, throttle %s\n", indent, "", port->id, @@ -930,7 +945,7 @@ static void virtser_port_device_realize(DeviceState *dev, Error **errp) return; } - port->elem.out_num = 0; + port->elem = NULL; } static void virtser_port_device_plug(HotplugHandler *hotplug_dev, diff --git a/qemu/hw/char/xen_console.c b/qemu/hw/char/xen_console.c index eb7f450ab..cbf1dccbb 100644 --- a/qemu/hw/char/xen_console.c +++ b/qemu/hw/char/xen_console.c @@ -19,14 +19,9 @@ * with this program; if not, see . */ -#include -#include -#include +#include "qemu/osdep.h" #include -#include -#include #include -#include #include #include "hw/hw.h" @@ -228,12 +223,12 @@ static int con_initialise(struct XenDevice *xendev) con->buffer.max_capacity = limit; if (!xendev->dev) { - con->sring = xc_map_foreign_range(xen_xc, con->xendev.dom, - XC_PAGE_SIZE, + xen_pfn_t mfn = con->ring_ref; + con->sring = xenforeignmemory_map(xen_fmem, con->xendev.dom, PROT_READ|PROT_WRITE, - con->ring_ref); + 1, &mfn, NULL); } else { - con->sring = xc_gnttab_map_grant_ref(xendev->gnttabdev, con->xendev.dom, + con->sring = xengnttab_map_grant_ref(xendev->gnttabdev, con->xendev.dom, con->ring_ref, PROT_READ|PROT_WRITE); } @@ -265,9 +260,6 @@ static void con_disconnect(struct XenDevice *xendev) { struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); - if (!xendev->dev) { - return; - } if (con->chr) { qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL); qemu_chr_fe_release(con->chr); @@ -275,12 +267,12 @@ static void con_disconnect(struct XenDevice *xendev) xen_be_unbind_evtchn(&con->xendev); if (con->sring) { - if (!xendev->gnttabdev) { - munmap(con->sring, XC_PAGE_SIZE); + if (!xendev->dev) { + xenforeignmemory_unmap(xen_fmem, con->sring, 1); } else { - xc_gnttab_munmap(xendev->gnttabdev, con->sring, 1); + xengnttab_unmap(xendev->gnttabdev, con->sring, 1); } - con->sring = NULL; + con->sring = NULL; } } diff --git a/qemu/hw/char/xilinx_uartlite.c b/qemu/hw/char/xilinx_uartlite.c index ef883a898..911af4a0d 100644 --- a/qemu/hw/char/xilinx_uartlite.c +++ b/qemu/hw/char/xilinx_uartlite.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "sysemu/char.h" diff --git a/qemu/hw/core/empty_slot.c b/qemu/hw/core/empty_slot.c index 612b1093a..c1b9c2b10 100644 --- a/qemu/hw/core/empty_slot.c +++ b/qemu/hw/core/empty_slot.c @@ -9,6 +9,7 @@ * version. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "hw/empty_slot.h" diff --git a/qemu/hw/core/fw-path-provider.c b/qemu/hw/core/fw-path-provider.c index 7442d322d..33b99830e 100644 --- a/qemu/hw/core/fw-path-provider.c +++ b/qemu/hw/core/fw-path-provider.c @@ -15,6 +15,7 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/fw-path-provider.h" char *fw_path_provider_get_dev_path(FWPathProvider *p, BusState *bus, diff --git a/qemu/hw/core/hotplug.c b/qemu/hw/core/hotplug.c index 4e0107455..645cfca1b 100644 --- a/qemu/hw/core/hotplug.c +++ b/qemu/hw/core/hotplug.c @@ -9,6 +9,7 @@ * 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 "hw/hotplug.h" #include "qemu/module.h" diff --git a/qemu/hw/core/irq.c b/qemu/hw/core/irq.c index 8a62a36d5..49ff2e64f 100644 --- a/qemu/hw/core/irq.c +++ b/qemu/hw/core/irq.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/irq.h" #include "qom/object.h" diff --git a/qemu/hw/core/loader.c b/qemu/hw/core/loader.c index 216eeeb91..c0499571c 100644 --- a/qemu/hw/core/loader.c +++ b/qemu/hw/core/loader.c @@ -42,6 +42,8 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "disas/disas.h" #include "monitor/monitor.h" @@ -51,12 +53,11 @@ #include "hw/nvram/fw_cfg.h" #include "exec/memory.h" #include "exec/address-spaces.h" +#include "hw/boards.h" +#include "qemu/cutils.h" #include -bool option_rom_has_mr = false; -bool rom_file_has_mr = true; - static int roms_loaded; /* return the size or -1 if error */ @@ -148,6 +149,28 @@ int load_image_targphys(const char *filename, return size; } +int load_image_mr(const char *filename, MemoryRegion *mr) +{ + int size; + + if (!memory_access_is_direct(mr, false)) { + /* Can only load an image into RAM or ROM */ + return -1; + } + + size = get_image_size(filename); + + if (size > memory_region_size(mr)) { + return -1; + } + if (size > 0) { + if (rom_add_file_mr(filename, mr, -1) < 0) { + return -1; + } + } + return size; +} + void pstrcpy_targphys(const char *name, hwaddr dest, int buf_size, const char *source) { @@ -333,10 +356,66 @@ const char *load_elf_strerror(int error) } } +void load_elf_hdr(const char *filename, void *hdr, bool *is64, Error **errp) +{ + int fd; + uint8_t e_ident_local[EI_NIDENT]; + uint8_t *e_ident; + size_t hdr_size, off; + bool is64l; + + if (!hdr) { + hdr = e_ident_local; + } + e_ident = hdr; + + fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) { + error_setg_errno(errp, errno, "Failed to open file: %s", filename); + return; + } + if (read(fd, hdr, EI_NIDENT) != EI_NIDENT) { + error_setg_errno(errp, errno, "Failed to read file: %s", filename); + goto fail; + } + if (e_ident[0] != ELFMAG0 || + e_ident[1] != ELFMAG1 || + e_ident[2] != ELFMAG2 || + e_ident[3] != ELFMAG3) { + error_setg(errp, "Bad ELF magic"); + goto fail; + } + + is64l = e_ident[EI_CLASS] == ELFCLASS64; + hdr_size = is64l ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Ehdr); + if (is64) { + *is64 = is64l; + } + + off = EI_NIDENT; + while (hdr != e_ident_local && off < hdr_size) { + size_t br = read(fd, hdr + off, hdr_size - off); + switch (br) { + case 0: + error_setg(errp, "File too short: %s", filename); + goto fail; + case -1: + error_setg_errno(errp, errno, "Failed to read file: %s", + filename); + goto fail; + } + off += br; + } + +fail: + close(fd); +} + /* return < 0 if error, otherwise the number of bytes loaded in memory */ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, int big_endian, int elf_machine, int clear_lsb) + uint64_t *highaddr, int big_endian, int elf_machine, + int clear_lsb, int data_swab) { int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED; uint8_t e_ident[EI_NIDENT]; @@ -375,10 +454,12 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), lseek(fd, 0, SEEK_SET); if (e_ident[EI_CLASS] == ELFCLASS64) { ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab, - pentry, lowaddr, highaddr, elf_machine, clear_lsb); + pentry, lowaddr, highaddr, elf_machine, clear_lsb, + data_swab); } else { ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab, - pentry, lowaddr, highaddr, elf_machine, clear_lsb); + pentry, lowaddr, highaddr, elf_machine, clear_lsb, + data_swab); } fail: @@ -594,8 +675,7 @@ static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr, ret = hdr->ih_size; out: - if (data) - g_free(data); + g_free(data); close(fd); return ret; } @@ -741,7 +821,7 @@ static void *rom_set_mr(Rom *rom, Object *owner, const char *name) memory_region_init_resizeable_ram(rom->mr, owner, name, rom->datasize, rom->romsize, fw_cfg_resized, - &error_abort); + &error_fatal); memory_region_set_readonly(rom->mr, true); vmstate_register_ram_global(rom->mr); @@ -753,8 +833,9 @@ static void *rom_set_mr(Rom *rom, Object *owner, const char *name) int rom_add_file(const char *file, const char *fw_dir, hwaddr addr, int32_t bootindex, - bool option_rom) + bool option_rom, MemoryRegion *mr) { + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); Rom *rom; int rc, fd = -1; char devpath[100]; @@ -811,7 +892,7 @@ int rom_add_file(const char *file, const char *fw_dir, basename); snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name); - if ((!option_rom || option_rom_has_mr) && rom_file_has_mr) { + if ((!option_rom || mc->option_rom_has_mr) && mc->rom_file_has_mr) { data = rom_set_mr(rom, OBJECT(fw_cfg), devpath); } else { data = rom->data; @@ -819,7 +900,12 @@ int rom_add_file(const char *file, const char *fw_dir, fw_cfg_add_file(fw_cfg, fw_file_name, data, rom->romsize); } else { - snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr); + if (mr) { + rom->mr = mr; + snprintf(devpath, sizeof(devpath), "/rom@%s", file); + } else { + snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr); + } } add_boot_device_path(bootindex, NULL, devpath); @@ -839,6 +925,7 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len, size_t max_len, hwaddr addr, const char *fw_file_name, FWCfgReadCallback fw_callback, void *callback_opaque) { + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); Rom *rom; MemoryRegion *mr = NULL; @@ -856,7 +943,7 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len, snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name); - if (rom_file_has_mr) { + if (mc->rom_file_has_mr) { data = rom_set_mr(rom, OBJECT(fw_cfg), devpath); mr = rom->mr; } else { @@ -892,12 +979,12 @@ int rom_add_elf_program(const char *name, void *data, size_t datasize, int rom_add_vga(const char *file) { - return rom_add_file(file, "vgaroms", 0, -1, true); + return rom_add_file(file, "vgaroms", 0, -1, true, NULL); } int rom_add_option(const char *file, int32_t bootindex) { - return rom_add_file(file, "genroms", 0, bootindex, true); + return rom_add_file(file, "genroms", 0, bootindex, true, NULL); } static void rom_reset(void *unused) @@ -966,6 +1053,20 @@ void rom_set_fw(FWCfgState *f) fw_cfg = f; } +void rom_set_order_override(int order) +{ + if (!fw_cfg) + return; + fw_cfg_set_order_override(fw_cfg, order); +} + +void rom_reset_order_override(void) +{ + if (!fw_cfg) + return; + fw_cfg_reset_order_override(fw_cfg); +} + static Rom *find_rom(hwaddr addr) { Rom *rom; diff --git a/qemu/hw/core/machine.c b/qemu/hw/core/machine.c index ac4654e9d..6dbbc85b9 100644 --- a/qemu/hw/core/machine.c +++ b/qemu/hw/core/machine.c @@ -10,11 +10,15 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "hw/boards.h" +#include "qapi/error.h" +#include "qapi-visit.h" #include "qapi/visitor.h" #include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "qemu/error-report.h" +#include "qemu/cutils.h" static char *machine_get_accel(Object *obj, Error **errp) { @@ -31,33 +35,60 @@ static void machine_set_accel(Object *obj, const char *value, Error **errp) ms->accel = g_strdup(value); } -static void machine_set_kernel_irqchip(Object *obj, bool value, Error **errp) +static void machine_set_kernel_irqchip(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { + Error *err = NULL; MachineState *ms = MACHINE(obj); + OnOffSplit mode; - ms->kernel_irqchip_allowed = value; - ms->kernel_irqchip_required = value; + visit_type_OnOffSplit(v, name, &mode, &err); + if (err) { + error_propagate(errp, err); + return; + } else { + switch (mode) { + case ON_OFF_SPLIT_ON: + ms->kernel_irqchip_allowed = true; + ms->kernel_irqchip_required = true; + ms->kernel_irqchip_split = false; + break; + case ON_OFF_SPLIT_OFF: + ms->kernel_irqchip_allowed = false; + ms->kernel_irqchip_required = false; + ms->kernel_irqchip_split = false; + break; + case ON_OFF_SPLIT_SPLIT: + ms->kernel_irqchip_allowed = true; + ms->kernel_irqchip_required = true; + ms->kernel_irqchip_split = true; + break; + default: + abort(); + } + } } static void machine_get_kvm_shadow_mem(Object *obj, Visitor *v, - void *opaque, const char *name, + const char *name, void *opaque, Error **errp) { MachineState *ms = MACHINE(obj); int64_t value = ms->kvm_shadow_mem; - visit_type_int(v, &value, name, errp); + visit_type_int(v, name, &value, errp); } static void machine_set_kvm_shadow_mem(Object *obj, Visitor *v, - void *opaque, const char *name, + const char *name, void *opaque, Error **errp) { MachineState *ms = MACHINE(obj); Error *error = NULL; int64_t value; - visit_type_int(v, &value, name, &error); + visit_type_int(v, name, &value, &error); if (error) { error_propagate(errp, error); return; @@ -142,24 +173,24 @@ static void machine_set_dumpdtb(Object *obj, const char *value, Error **errp) } static void machine_get_phandle_start(Object *obj, Visitor *v, - void *opaque, const char *name, - Error **errp) + const char *name, void *opaque, + Error **errp) { MachineState *ms = MACHINE(obj); int64_t value = ms->phandle_start; - visit_type_int(v, &value, name, errp); + visit_type_int(v, name, &value, errp); } static void machine_set_phandle_start(Object *obj, Visitor *v, - void *opaque, const char *name, - Error **errp) + const char *name, void *opaque, + Error **errp) { MachineState *ms = MACHINE(obj); Error *error = NULL; int64_t value; - visit_type_int(v, &value, name, &error); + visit_type_int(v, name, &value, &error); if (error) { error_propagate(errp, error); return; @@ -226,6 +257,20 @@ static void machine_set_usb(Object *obj, bool value, Error **errp) ms->usb_disabled = !value; } +static bool machine_get_igd_gfx_passthru(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return ms->igd_gfx_passthru; +} + +static void machine_set_igd_gfx_passthru(Object *obj, bool value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + ms->igd_gfx_passthru = value; +} + static char *machine_get_firmware(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); @@ -269,6 +314,21 @@ static bool machine_get_suppress_vmdesc(Object *obj, Error **errp) return ms->suppress_vmdesc; } +static void machine_set_enforce_config_section(Object *obj, bool value, + Error **errp) +{ + MachineState *ms = MACHINE(obj); + + ms->enforce_config_section = value; +} + +static bool machine_get_enforce_config_section(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return ms->enforce_config_section; +} + static int error_on_sysbus_device(SysBusDevice *sbdev, void *opaque) { error_report("Option '-device %s' cannot be handled by this machine", @@ -300,6 +360,18 @@ static void machine_class_init(ObjectClass *oc, void *data) /* Default 128 MB as guest ram size */ mc->default_ram_size = 128 * M_BYTE; + mc->rom_file_has_mr = true; +} + +static void machine_class_base_init(ObjectClass *oc, void *data) +{ + if (!object_class_is_abstract(oc)) { + MachineClass *mc = MACHINE_CLASS(oc); + const char *cname = object_class_get_name(oc); + assert(g_str_has_suffix(cname, TYPE_MACHINE_SUFFIX)); + mc->name = g_strndup(cname, + strlen(cname) - strlen(TYPE_MACHINE_SUFFIX)); + } } static void machine_initfn(Object *obj) @@ -316,12 +388,12 @@ static void machine_initfn(Object *obj) object_property_set_description(obj, "accel", "Accelerator list", NULL); - object_property_add_bool(obj, "kernel-irqchip", - NULL, - machine_set_kernel_irqchip, - NULL); + object_property_add(obj, "kernel-irqchip", "OnOffSplit", + NULL, + machine_set_kernel_irqchip, + NULL, NULL, NULL); object_property_set_description(obj, "kernel-irqchip", - "Use KVM in-kernel irqchip", + "Configure KVM in-kernel irqchip", NULL); object_property_add(obj, "kvm-shadow-mem", "int", machine_get_kvm_shadow_mem, @@ -388,6 +460,12 @@ static void machine_initfn(Object *obj) object_property_set_description(obj, "usb", "Set on/off to enable/disable usb", NULL); + object_property_add_bool(obj, "igd-passthru", + machine_get_igd_gfx_passthru, + machine_set_igd_gfx_passthru, NULL); + object_property_set_description(obj, "igd-passthru", + "Set on/off to enable/disable igd passthrou", + NULL); object_property_add_str(obj, "firmware", machine_get_firmware, machine_set_firmware, NULL); @@ -406,6 +484,12 @@ static void machine_initfn(Object *obj) object_property_set_description(obj, "suppress-vmdesc", "Set on to disable self-describing migration", NULL); + object_property_add_bool(obj, "enforce-config-section", + machine_get_enforce_config_section, + machine_set_enforce_config_section, NULL); + object_property_set_description(obj, "enforce-config-section", + "Set on to enforce configuration section migration", + NULL); /* Register notifier when init is done for sysbus sanity checks */ ms->sysbus_notifier.notify = machine_init_notify; @@ -431,11 +515,6 @@ bool machine_usb(MachineState *machine) return machine->usb; } -bool machine_iommu(MachineState *machine) -{ - return machine->iommu; -} - bool machine_kernel_irqchip_allowed(MachineState *machine) { return machine->kernel_irqchip_allowed; @@ -446,6 +525,11 @@ bool machine_kernel_irqchip_required(MachineState *machine) return machine->kernel_irqchip_required; } +bool machine_kernel_irqchip_split(MachineState *machine) +{ + return machine->kernel_irqchip_split; +} + int machine_kvm_shadow_mem(MachineState *machine) { return machine->kvm_shadow_mem; @@ -472,6 +556,7 @@ static const TypeInfo machine_info = { .abstract = true, .class_size = sizeof(MachineClass), .class_init = machine_class_init, + .class_base_init = machine_class_base_init, .instance_size = sizeof(MachineState), .instance_init = machine_initfn, .instance_finalize = machine_finalize, diff --git a/qemu/hw/core/nmi.c b/qemu/hw/core/nmi.c index de1d1f8cb..e8bcc4177 100644 --- a/qemu/hw/core/nmi.c +++ b/qemu/hw/core/nmi.c @@ -19,13 +19,15 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/nmi.h" +#include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "monitor/monitor.h" struct do_nmi_s { int cpu_index; - Error *errp; + Error *err; bool handled; }; @@ -40,8 +42,8 @@ static int do_nmi(Object *o, void *opaque) NMIClass *nc = NMI_GET_CLASS(n); ns->handled = true; - nc->nmi_monitor_handler(n, ns->cpu_index, &ns->errp); - if (ns->errp) { + nc->nmi_monitor_handler(n, ns->cpu_index, &ns->err); + if (ns->err) { return -1; } } @@ -59,13 +61,13 @@ void nmi_monitor_handle(int cpu_index, Error **errp) { struct do_nmi_s ns = { .cpu_index = cpu_index, - .errp = NULL, + .err = NULL, .handled = false }; nmi_children(object_get_root(), &ns); if (ns.handled) { - error_propagate(errp, ns.errp); + error_propagate(errp, ns.err); } else { error_setg(errp, QERR_UNSUPPORTED); } diff --git a/qemu/hw/core/null-machine.c b/qemu/hw/core/null-machine.c index 1ec7c3bbe..0351ba782 100644 --- a/qemu/hw/core/null-machine.c +++ b/qemu/hw/core/null-machine.c @@ -11,6 +11,7 @@ * */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/hw.h" #include "hw/boards.h" @@ -19,17 +20,11 @@ static void machine_none_init(MachineState *machine) { } -static QEMUMachine machine_none = { - .name = "none", - .desc = "empty machine", - .init = machine_none_init, - .max_cpus = 0, -}; - -static void register_machines(void) +static void machine_none_machine_init(MachineClass *mc) { - qemu_register_machine(&machine_none); + mc->desc = "empty machine"; + mc->init = machine_none_init; + mc->max_cpus = 0; } -machine_init(register_machines); - +DEFINE_MACHINE("none", machine_none_machine_init) diff --git a/qemu/hw/core/platform-bus.c b/qemu/hw/core/platform-bus.c index 70e051890..36f84ab72 100644 --- a/qemu/hw/core/platform-bus.c +++ b/qemu/hw/core/platform-bus.c @@ -19,8 +19,10 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "hw/platform-bus.h" #include "exec/address-spaces.h" +#include "qemu/error-report.h" #include "sysemu/sysemu.h" @@ -106,31 +108,29 @@ static void plaform_bus_refresh_irqs(PlatformBusDevice *pbus) pbus->done_gathering = true; } -static int platform_bus_map_irq(PlatformBusDevice *pbus, SysBusDevice *sbdev, - int n) +static void platform_bus_map_irq(PlatformBusDevice *pbus, SysBusDevice *sbdev, + int n) { int max_irqs = pbus->num_irqs; int irqn; if (sysbus_is_irq_connected(sbdev, n)) { /* IRQ is already mapped, nothing to do */ - return 0; + return; } irqn = find_first_zero_bit(pbus->used_irqs, max_irqs); if (irqn >= max_irqs) { - hw_error("Platform Bus: Can not fit IRQ line"); - return -1; + error_report("Platform Bus: Can not fit IRQ line"); + exit(1); } set_bit(irqn, pbus->used_irqs); sysbus_connect_irq(sbdev, n, pbus->irqs[irqn]); - - return 0; } -static int platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev, - int n) +static void platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev, + int n) { MemoryRegion *sbdev_mr = sysbus_mmio_get_region(sbdev, n); uint64_t size = memory_region_size(sbdev_mr); @@ -140,7 +140,7 @@ static int platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev, if (memory_region_is_mapped(sbdev_mr)) { /* Region is already mapped, nothing to do */ - return 0; + return; } /* @@ -155,13 +155,13 @@ static int platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev, } if (!found_region) { - hw_error("Platform Bus: Can not fit MMIO region of size %"PRIx64, size); + error_report("Platform Bus: Can not fit MMIO region of size %"PRIx64, + size); + exit(1); } /* Map the device's region into our Platform Bus MMIO space */ memory_region_add_subregion(&pbus->mmio, off, sbdev_mr); - - return 0; } /* diff --git a/qemu/hw/core/ptimer.c b/qemu/hw/core/ptimer.c index 8437bd6e8..153c83513 100644 --- a/qemu/hw/core/ptimer.c +++ b/qemu/hw/core/ptimer.c @@ -5,10 +5,12 @@ * * This code is licensed under the GNU LGPL. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "qemu/timer.h" #include "hw/ptimer.h" #include "qemu/host-utils.h" +#include "sysemu/replay.h" struct ptimer_state { @@ -27,7 +29,7 @@ struct ptimer_state static void ptimer_trigger(ptimer_state *s) { if (s->bh) { - qemu_bh_schedule(s->bh); + replay_bh_schedule_event(s->bh); } } diff --git a/qemu/hw/core/qdev-properties-system.c b/qemu/hw/core/qdev-properties-system.c index 921e799db..891219ae0 100644 --- a/qemu/hw/core/qdev-properties-system.c +++ b/qemu/hw/core/qdev-properties-system.c @@ -10,8 +10,10 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "net/net.h" #include "hw/qdev.h" +#include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" @@ -30,7 +32,7 @@ static void get_pointer(Object *obj, Visitor *v, Property *prop, char *p; p = *ptr ? print(*ptr) : g_strdup(""); - visit_type_str(v, &p, name, errp); + visit_type_str(v, name, &p, errp); g_free(p); } @@ -50,7 +52,7 @@ static void set_pointer(Object *obj, Visitor *v, Property *prop, return; } - visit_type_str(v, &str, name, &local_err); + visit_type_str(v, name, &str, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -111,14 +113,14 @@ static char *print_drive(void *ptr) return g_strdup(blk_name(ptr)); } -static void get_drive(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void get_drive(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { get_pointer(obj, v, opaque, print_drive, name, errp); } -static void set_drive(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void set_drive(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { set_pointer(obj, v, opaque, parse_drive, name, errp); } @@ -172,14 +174,14 @@ static char *print_chr(void *ptr) return g_strdup(val); } -static void get_chr(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void get_chr(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { get_pointer(obj, v, opaque, print_chr, name, errp); } -static void set_chr(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { set_pointer(obj, v, opaque, parse_chr, name, errp); } @@ -193,20 +195,20 @@ PropertyInfo qdev_prop_chr = { }; /* --- netdev device --- */ -static void get_netdev(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void get_netdev(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); char *p = g_strdup(peers_ptr->ncs[0] ? peers_ptr->ncs[0]->name : ""); - visit_type_str(v, &p, name, errp); + visit_type_str(v, name, &p, errp); g_free(p); } -static void set_netdev(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void set_netdev(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -222,7 +224,7 @@ static void set_netdev(Object *obj, Visitor *v, void *opaque, return; } - visit_type_str(v, &str, name, &local_err); + visit_type_str(v, name, &str, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -292,8 +294,8 @@ static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len) return snprintf(dest, len, ""); } -static void get_vlan(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void get_vlan(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -307,11 +309,11 @@ static void get_vlan(Object *obj, Visitor *v, void *opaque, } } - visit_type_int32(v, &id, name, errp); + visit_type_int32(v, name, &id, errp); } -static void set_vlan(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void set_vlan(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -326,7 +328,7 @@ static void set_vlan(Object *obj, Visitor *v, void *opaque, return; } - visit_type_int32(v, &id, name, &local_err); + visit_type_int32(v, name, &id, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -364,18 +366,6 @@ void qdev_prop_set_drive(DeviceState *dev, const char *name, name, errp); } -void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, - BlockBackend *value) -{ - Error *err = NULL; - - qdev_prop_set_drive(dev, name, value, &err); - if (err) { - error_report_err(err); - exit(1); - } -} - void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value) { diff --git a/qemu/hw/core/qdev-properties.c b/qemu/hw/core/qdev-properties.c index 04fd80a4d..737d29c63 100644 --- a/qemu/hw/core/qdev-properties.c +++ b/qemu/hw/core/qdev-properties.c @@ -1,5 +1,8 @@ +#include "qemu/osdep.h" #include "net/net.h" #include "hw/qdev.h" +#include "qapi/error.h" +#include "hw/pci/pci.h" #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "sysemu/block-backend.h" @@ -41,19 +44,18 @@ void *qdev_get_prop_ptr(DeviceState *dev, Property *prop) return ptr; } -static void get_enum(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void get_enum(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; int *ptr = qdev_get_prop_ptr(dev, prop); - visit_type_enum(v, ptr, prop->info->enum_table, - prop->info->name, prop->name, errp); + visit_type_enum(v, prop->name, ptr, prop->info->enum_table, errp); } -static void set_enum(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void set_enum(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -64,8 +66,7 @@ static void set_enum(Object *obj, Visitor *v, void *opaque, return; } - visit_type_enum(v, ptr, prop->info->enum_table, - prop->info->name, prop->name, errp); + visit_type_enum(v, prop->name, ptr, prop->info->enum_table, errp); } /* Bit */ @@ -87,19 +88,19 @@ static void bit_prop_set(DeviceState *dev, Property *props, bool val) } } -static void prop_get_bit(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void prop_get_bit(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; uint32_t *p = qdev_get_prop_ptr(dev, prop); bool value = (*p & qdev_get_prop_mask(prop)) != 0; - visit_type_bool(v, &value, name, errp); + visit_type_bool(v, name, &value, errp); } -static void prop_set_bit(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void prop_set_bit(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -111,7 +112,7 @@ static void prop_set_bit(Object *obj, Visitor *v, void *opaque, return; } - visit_type_bool(v, &value, name, &local_err); + visit_type_bool(v, name, &value, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -145,19 +146,19 @@ static void bit64_prop_set(DeviceState *dev, Property *props, bool val) } } -static void prop_get_bit64(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void prop_get_bit64(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; uint64_t *p = qdev_get_prop_ptr(dev, prop); bool value = (*p & qdev_get_prop_mask64(prop)) != 0; - visit_type_bool(v, &value, name, errp); + visit_type_bool(v, name, &value, errp); } -static void prop_set_bit64(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void prop_set_bit64(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -169,7 +170,7 @@ static void prop_set_bit64(Object *obj, Visitor *v, void *opaque, return; } - visit_type_bool(v, &value, name, &local_err); + visit_type_bool(v, name, &value, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -186,18 +187,18 @@ PropertyInfo qdev_prop_bit64 = { /* --- bool --- */ -static void get_bool(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void get_bool(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; bool *ptr = qdev_get_prop_ptr(dev, prop); - visit_type_bool(v, ptr, name, errp); + visit_type_bool(v, name, ptr, errp); } -static void set_bool(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void set_bool(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -208,7 +209,7 @@ static void set_bool(Object *obj, Visitor *v, void *opaque, return; } - visit_type_bool(v, ptr, name, errp); + visit_type_bool(v, name, ptr, errp); } PropertyInfo qdev_prop_bool = { @@ -219,18 +220,18 @@ PropertyInfo qdev_prop_bool = { /* --- 8bit integer --- */ -static void get_uint8(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void get_uint8(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; uint8_t *ptr = qdev_get_prop_ptr(dev, prop); - visit_type_uint8(v, ptr, name, errp); + visit_type_uint8(v, name, ptr, errp); } -static void set_uint8(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void set_uint8(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -241,7 +242,7 @@ static void set_uint8(Object *obj, Visitor *v, void *opaque, return; } - visit_type_uint8(v, ptr, name, errp); + visit_type_uint8(v, name, ptr, errp); } PropertyInfo qdev_prop_uint8 = { @@ -252,18 +253,18 @@ PropertyInfo qdev_prop_uint8 = { /* --- 16bit integer --- */ -static void get_uint16(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void get_uint16(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; uint16_t *ptr = qdev_get_prop_ptr(dev, prop); - visit_type_uint16(v, ptr, name, errp); + visit_type_uint16(v, name, ptr, errp); } -static void set_uint16(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void set_uint16(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -274,7 +275,7 @@ static void set_uint16(Object *obj, Visitor *v, void *opaque, return; } - visit_type_uint16(v, ptr, name, errp); + visit_type_uint16(v, name, ptr, errp); } PropertyInfo qdev_prop_uint16 = { @@ -285,18 +286,18 @@ PropertyInfo qdev_prop_uint16 = { /* --- 32bit integer --- */ -static void get_uint32(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void get_uint32(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; uint32_t *ptr = qdev_get_prop_ptr(dev, prop); - visit_type_uint32(v, ptr, name, errp); + visit_type_uint32(v, name, ptr, errp); } -static void set_uint32(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void set_uint32(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -307,21 +308,21 @@ static void set_uint32(Object *obj, Visitor *v, void *opaque, return; } - visit_type_uint32(v, ptr, name, errp); + visit_type_uint32(v, name, ptr, errp); } -static void get_int32(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void get_int32(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; int32_t *ptr = qdev_get_prop_ptr(dev, prop); - visit_type_int32(v, ptr, name, errp); + visit_type_int32(v, name, ptr, errp); } -static void set_int32(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void set_int32(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -332,7 +333,7 @@ static void set_int32(Object *obj, Visitor *v, void *opaque, return; } - visit_type_int32(v, ptr, name, errp); + visit_type_int32(v, name, ptr, errp); } PropertyInfo qdev_prop_uint32 = { @@ -349,18 +350,18 @@ PropertyInfo qdev_prop_int32 = { /* --- 64bit integer --- */ -static void get_uint64(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void get_uint64(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; uint64_t *ptr = qdev_get_prop_ptr(dev, prop); - visit_type_uint64(v, ptr, name, errp); + visit_type_uint64(v, name, ptr, errp); } -static void set_uint64(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void set_uint64(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -371,7 +372,7 @@ static void set_uint64(Object *obj, Visitor *v, void *opaque, return; } - visit_type_uint64(v, ptr, name, errp); + visit_type_uint64(v, name, ptr, errp); } PropertyInfo qdev_prop_uint64 = { @@ -388,8 +389,8 @@ static void release_string(Object *obj, const char *name, void *opaque) g_free(*(char **)qdev_get_prop_ptr(DEVICE(obj), prop)); } -static void get_string(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void get_string(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -397,14 +398,14 @@ static void get_string(Object *obj, Visitor *v, void *opaque, if (!*ptr) { char *str = (char *)""; - visit_type_str(v, &str, name, errp); + visit_type_str(v, name, &str, errp); } else { - visit_type_str(v, ptr, name, errp); + visit_type_str(v, name, ptr, errp); } } -static void set_string(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void set_string(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -417,14 +418,12 @@ static void set_string(Object *obj, Visitor *v, void *opaque, return; } - visit_type_str(v, &str, name, &local_err); + visit_type_str(v, name, &str, &local_err); if (local_err) { error_propagate(errp, local_err); return; } - if (*ptr) { - g_free(*ptr); - } + g_free(*ptr); *ptr = str; } @@ -449,8 +448,8 @@ PropertyInfo qdev_prop_ptr = { * 01:02:03:04:05:06 * 01-02-03-04-05-06 */ -static void get_mac(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void get_mac(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -462,11 +461,11 @@ static void get_mac(Object *obj, Visitor *v, void *opaque, mac->a[0], mac->a[1], mac->a[2], mac->a[3], mac->a[4], mac->a[5]); - visit_type_str(v, &p, name, errp); + visit_type_str(v, name, &p, errp); } -static void set_mac(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void set_mac(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -480,7 +479,7 @@ static void set_mac(Object *obj, Visitor *v, void *opaque, return; } - visit_type_str(v, &str, name, &local_err); + visit_type_str(v, name, &str, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -519,6 +518,16 @@ PropertyInfo qdev_prop_macaddr = { .set = set_mac, }; +/* --- on/off/auto --- */ + +PropertyInfo qdev_prop_on_off_auto = { + .name = "OnOffAuto", + .description = "on/off/auto", + .enum_table = OnOffAuto_lookup, + .get = get_enum, + .set = set_enum, +}; + /* --- lost tick policy --- */ QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int)); @@ -543,13 +552,24 @@ PropertyInfo qdev_prop_bios_chs_trans = { .set = set_enum, }; +/* --- FDC default drive types */ + +PropertyInfo qdev_prop_fdc_drive_type = { + .name = "FdcDriveType", + .description = "FDC drive type, " + "144/288/120/none/auto", + .enum_table = FloppyDriveType_lookup, + .get = get_enum, + .set = set_enum +}; + /* --- pci address --- */ /* * bus-local address, i.e. "$slot" or "$slot.$fn" */ -static void set_pci_devfn(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void set_pci_devfn(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -563,11 +583,11 @@ static void set_pci_devfn(Object *obj, Visitor *v, void *opaque, return; } - visit_type_str(v, &str, name, &local_err); + visit_type_str(v, name, &str, &local_err); if (local_err) { error_free(local_err); local_err = NULL; - visit_type_int32(v, &value, name, &local_err); + visit_type_int32(v, name, &value, &local_err); if (local_err) { error_propagate(errp, local_err); } else if (value < -1 || value > 255) { @@ -619,8 +639,8 @@ PropertyInfo qdev_prop_pci_devfn = { /* --- blocksize --- */ -static void set_blocksize(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void set_blocksize(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -634,7 +654,7 @@ static void set_blocksize(Object *obj, Visitor *v, void *opaque, return; } - visit_type_uint16(v, &value, name, &local_err); + visit_type_uint16(v, name, &value, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -666,8 +686,8 @@ PropertyInfo qdev_prop_blocksize = { /* --- pci host address --- */ -static void get_pci_host_devaddr(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void get_pci_host_devaddr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -680,15 +700,15 @@ static void get_pci_host_devaddr(Object *obj, Visitor *v, void *opaque, addr->domain, addr->bus, addr->slot, addr->function); assert(rc == sizeof(buffer) - 1); - visit_type_str(v, &p, name, errp); + visit_type_str(v, name, &p, errp); } /* * Parse [:]:. * if is not supplied, it's assumed to be 0. */ -static void set_pci_host_devaddr(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void set_pci_host_devaddr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -705,7 +725,7 @@ static void set_pci_host_devaddr(Object *obj, Visitor *v, void *opaque, return; } - visit_type_str(v, &str, name, &local_err); + visit_type_str(v, name, &str, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -801,8 +821,8 @@ static void array_element_release(Object *obj, const char *name, void *opaque) g_free(p); } -static void set_prop_arraylen(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void set_prop_arraylen(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { /* Setter for the property which defines the length of a * variable-sized property array. As well as actually setting the @@ -827,7 +847,7 @@ static void set_prop_arraylen(Object *obj, Visitor *v, void *opaque, name); return; } - visit_type_uint32(v, alenptr, name, &local_err); + visit_type_uint32(v, name, alenptr, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -1065,10 +1085,8 @@ static void qdev_prop_set_globals_for_type(DeviceState *dev, object_property_parse(OBJECT(dev), prop->value, prop->property, &err); if (err != NULL) { assert(prop->user_provided); - error_report("Warning: global %s.%s=%s ignored (%s)", - prop->driver, prop->property, prop->value, - error_get_pretty(err)); - error_free(err); + error_reportf_err(err, "Warning: global %s.%s=%s ignored: ", + prop->driver, prop->property, prop->value); return; } } @@ -1086,24 +1104,24 @@ void qdev_prop_set_globals(DeviceState *dev) /* --- 64bit unsigned int 'size' type --- */ -static void get_size(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void get_size(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; uint64_t *ptr = qdev_get_prop_ptr(dev, prop); - visit_type_size(v, ptr, name, errp); + visit_type_size(v, name, ptr, errp); } -static void set_size(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void set_size(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; uint64_t *ptr = qdev_get_prop_ptr(dev, prop); - visit_type_size(v, ptr, name, errp); + visit_type_size(v, name, ptr, errp); } PropertyInfo qdev_prop_size = { diff --git a/qemu/hw/core/qdev.c b/qemu/hw/core/qdev.c index b2f404a76..db41aa1f2 100644 --- a/qemu/hw/core/qdev.c +++ b/qemu/hw/core/qdev.c @@ -25,10 +25,10 @@ inherit from a particular bus (e.g. PCI or I2C) rather than this API directly. */ +#include "qemu/osdep.h" #include "hw/qdev.h" #include "hw/fw-path-provider.h" #include "sysemu/sysemu.h" -#include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qapi/visitor.h" #include "qapi/qmp/qjson.h" @@ -325,6 +325,11 @@ void qdev_reset_all(DeviceState *dev) qdev_walk_children(dev, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL); } +void qdev_reset_all_fn(void *opaque) +{ + qdev_reset_all(DEVICE(opaque)); +} + void qbus_reset_all(BusState *bus) { qbus_walk_children(bus, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL); @@ -365,9 +370,8 @@ void qdev_init_nofail(DeviceState *dev) object_property_set_bool(OBJECT(dev), true, "realized", &err); if (err) { - error_report("Initialization of device %s failed: %s", - object_get_typename(OBJECT(dev)), - error_get_pretty(err)); + error_reportf_err(err, "Initialization of device %s failed: ", + object_get_typename(OBJECT(dev))); exit(1); } } @@ -417,17 +421,21 @@ void qdev_init_gpio_in_named(DeviceState *dev, qemu_irq_handler handler, { int i; NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); - char *propname = g_strdup_printf("%s[*]", name ? name : "unnamed-gpio-in"); assert(gpio_list->num_out == 0 || !name); gpio_list->in = qemu_extend_irqs(gpio_list->in, gpio_list->num_in, handler, dev, n); + if (!name) { + name = "unnamed-gpio-in"; + } for (i = gpio_list->num_in; i < gpio_list->num_in + n; i++) { + gchar *propname = g_strdup_printf("%s[%u]", name, i); + object_property_add_child(OBJECT(dev), propname, OBJECT(gpio_list->in[i]), &error_abort); + g_free(propname); } - g_free(propname); gpio_list->num_in += n; } @@ -442,20 +450,25 @@ void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins, { int i; NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); - char *propname = g_strdup_printf("%s[*]", name ? name : "unnamed-gpio-out"); assert(gpio_list->num_in == 0 || !name); - gpio_list->num_out += n; + if (!name) { + name = "unnamed-gpio-out"; + } + memset(pins, 0, sizeof(*pins) * n); for (i = 0; i < n; ++i) { - memset(&pins[i], 0, sizeof(*pins)); + gchar *propname = g_strdup_printf("%s[%u]", name, + gpio_list->num_out + i); + object_property_add_link(OBJECT(dev), propname, TYPE_IRQ, (Object **)&pins[i], object_property_allow_set_link, OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); + g_free(propname); } - g_free(propname); + gpio_list->num_out += n; } void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n) @@ -506,7 +519,7 @@ qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n) return ret; } -/* disconnect a GPIO ouput, returning the disconnected input (if any) */ +/* disconnect a GPIO output, returning the disconnected input (if any) */ static qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev, const char *name, int n) @@ -567,6 +580,12 @@ void qdev_pass_gpios(DeviceState *dev, DeviceState *container, BusState *qdev_get_child_bus(DeviceState *dev, const char *name) { BusState *bus; + Object *child = object_resolve_path_component(OBJECT(dev), name); + + bus = (BusState *)object_dynamic_cast(child, TYPE_BUS); + if (bus) { + return bus; + } QLIST_FOREACH(bus, &dev->child_bus, sibling) { if (strcmp(name, bus->name) == 0) { @@ -874,8 +893,9 @@ char *qdev_get_dev_path(DeviceState *dev) * Legacy property handling */ -static void qdev_get_legacy_property(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void qdev_get_legacy_property(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { DeviceState *dev = DEVICE(obj); Property *prop = opaque; @@ -884,7 +904,7 @@ static void qdev_get_legacy_property(Object *obj, Visitor *v, void *opaque, char *ptr = buffer; prop->info->print(dev, prop, buffer, sizeof(buffer)); - visit_type_str(v, &ptr, name, errp); + visit_type_str(v, name, &ptr, errp); } /** @@ -1120,7 +1140,6 @@ post_realize_fail: fail: error_propagate(errp, local_err); - return; } static bool device_get_hotpluggable(Object *obj, Error **errp) @@ -1194,7 +1213,6 @@ static void device_finalize(Object *obj) NamedGPIOList *ngl, *next; DeviceState *dev = DEVICE(obj); - qemu_opts_del(dev->opts); QLIST_FOREACH_SAFE(ngl, &dev->gpios, node, next) { QLIST_REMOVE(ngl, node); @@ -1242,6 +1260,9 @@ static void device_unparent(Object *obj) qapi_event_send_device_deleted(!!dev->id, dev->id, path, &error_abort); g_free(path); } + + qemu_opts_del(dev->opts); + dev->opts = NULL; } static void device_class_init(ObjectClass *class, void *data) diff --git a/qemu/hw/core/stream.c b/qemu/hw/core/stream.c index e6a05a543..4439ecdf0 100644 --- a/qemu/hw/core/stream.c +++ b/qemu/hw/core/stream.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/stream.h" size_t diff --git a/qemu/hw/core/sysbus.c b/qemu/hw/core/sysbus.c index 3c5862989..a7dbe2b32 100644 --- a/qemu/hw/core/sysbus.c +++ b/qemu/hw/core/sysbus.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "monitor/monitor.h" #include "exec/address-spaces.h" diff --git a/qemu/hw/cpu/Makefile.objs b/qemu/hw/cpu/Makefile.objs index 6381238cc..0954a1872 100644 --- a/qemu/hw/cpu/Makefile.objs +++ b/qemu/hw/cpu/Makefile.objs @@ -2,5 +2,4 @@ obj-$(CONFIG_ARM11MPCORE) += arm11mpcore.o obj-$(CONFIG_REALVIEW) += realview_mpcore.o obj-$(CONFIG_A9MPCORE) += a9mpcore.o obj-$(CONFIG_A15MPCORE) += a15mpcore.o -obj-$(CONFIG_ICC_BUS) += icc_bus.o diff --git a/qemu/hw/cpu/a15mpcore.c b/qemu/hw/cpu/a15mpcore.c index acc419e11..bc05152fd 100644 --- a/qemu/hw/cpu/a15mpcore.c +++ b/qemu/hw/cpu/a15mpcore.c @@ -18,8 +18,11 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/cpu/a15mpcore.h" #include "sysemu/kvm.h" +#include "kvm_arm.h" static void a15mp_priv_set_irq(void *opaque, int irq, int level) { @@ -33,16 +36,11 @@ static void a15mp_priv_initfn(Object *obj) SysBusDevice *sbd = SYS_BUS_DEVICE(obj); A15MPPrivState *s = A15MPCORE_PRIV(obj); DeviceState *gicdev; - const char *gictype = "arm_gic"; - - if (kvm_irqchip_in_kernel()) { - gictype = "kvm-arm-gic"; - } memory_region_init(&s->container, obj, "a15mp-priv-container", 0x8000); sysbus_init_mmio(sbd, &s->container); - object_initialize(&s->gic, sizeof(s->gic), gictype); + object_initialize(&s->gic, sizeof(s->gic), gic_class_name()); gicdev = DEVICE(&s->gic); qdev_set_parent_bus(gicdev, sysbus_get_default()); qdev_prop_set_uint32(gicdev, "revision", 2); @@ -56,10 +54,23 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp) SysBusDevice *busdev; int i; Error *err = NULL; + bool has_el3; + Object *cpuobj; gicdev = DEVICE(&s->gic); qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu); qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq); + + if (!kvm_irqchip_in_kernel()) { + /* Make the GIC's TZ support match the CPUs. We assume that + * either all the CPUs have TZ, or none do. + */ + cpuobj = OBJECT(qemu_get_cpu(0)); + has_el3 = object_property_find(cpuobj, "has_el3", NULL) && + object_property_get_bool(cpuobj, "has_el3", &error_abort); + qdev_prop_set_bit(gicdev, "has-security-extensions", has_el3); + } + object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); @@ -79,20 +90,27 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp) for (i = 0; i < s->num_cpu; i++) { DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); int ppibase = s->num_irq - 32 + i * 32; - /* physical timer; we wire it up to the non-secure timer's ID, - * since a real A15 always has TrustZone but QEMU doesn't. + int irq; + /* Mapping from the output timer irq lines from the CPU to the + * GIC PPI inputs used on the A15: */ - qdev_connect_gpio_out(cpudev, 0, - qdev_get_gpio_in(gicdev, ppibase + 30)); - /* virtual timer */ - qdev_connect_gpio_out(cpudev, 1, - qdev_get_gpio_in(gicdev, ppibase + 27)); + const int timer_irq[] = { + [GTIMER_PHYS] = 30, + [GTIMER_VIRT] = 27, + [GTIMER_HYP] = 26, + [GTIMER_SEC] = 29, + }; + for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { + qdev_connect_gpio_out(cpudev, irq, + qdev_get_gpio_in(gicdev, + ppibase + timer_irq[irq])); + } } /* Memory map (addresses are offsets from PERIPHBASE): * 0x0000-0x0fff -- reserved * 0x1000-0x1fff -- GIC Distributor - * 0x2000-0x2fff -- GIC CPU interface + * 0x2000-0x3fff -- GIC CPU interface * 0x4000-0x4fff -- GIC virtual interface control (not modelled) * 0x5000-0x5fff -- GIC virtual interface control (not modelled) * 0x6000-0x7fff -- GIC virtual CPU interface (not modelled) diff --git a/qemu/hw/cpu/a9mpcore.c b/qemu/hw/cpu/a9mpcore.c index c09358c6e..5459ae8c1 100644 --- a/qemu/hw/cpu/a9mpcore.c +++ b/qemu/hw/cpu/a9mpcore.c @@ -8,6 +8,8 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/cpu/a9mpcore.h" static void a9mp_priv_set_irq(void *opaque, int irq, int level) @@ -49,6 +51,8 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) *wdtbusdev; Error *err = NULL; int i; + bool has_el3; + Object *cpuobj; scudev = DEVICE(&s->scu); qdev_prop_set_uint32(scudev, "num-cpu", s->num_cpu); @@ -62,6 +66,15 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) gicdev = DEVICE(&s->gic); qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu); qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq); + + /* Make the GIC's TZ support match the CPUs. We assume that + * either all the CPUs have TZ, or none do. + */ + cpuobj = OBJECT(qemu_get_cpu(0)); + has_el3 = object_property_find(cpuobj, "has_el3", NULL) && + object_property_get_bool(cpuobj, "has_el3", &error_abort); + qdev_prop_set_bit(gicdev, "has-security-extensions", has_el3); + object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); diff --git a/qemu/hw/cpu/arm11mpcore.c b/qemu/hw/cpu/arm11mpcore.c index 717d3e4f8..eb244658b 100644 --- a/qemu/hw/cpu/arm11mpcore.c +++ b/qemu/hw/cpu/arm11mpcore.c @@ -7,6 +7,8 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/cpu/arm11mpcore.h" #include "hw/intc/realview_gic.h" diff --git a/qemu/hw/cpu/icc_bus.c b/qemu/hw/cpu/icc_bus.c deleted file mode 100644 index 6646ea2b3..000000000 --- a/qemu/hw/cpu/icc_bus.c +++ /dev/null @@ -1,118 +0,0 @@ -/* icc_bus.c - * emulate x86 ICC (Interrupt Controller Communications) bus - * - * Copyright (c) 2013 Red Hat, Inc - * - * Authors: - * Igor Mammedov - * - * 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 - */ -#include "hw/cpu/icc_bus.h" -#include "hw/sysbus.h" - -/* icc-bridge implementation */ - -static const TypeInfo icc_bus_info = { - .name = TYPE_ICC_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(ICCBus), -}; - - -/* icc-device implementation */ - -static void icc_device_realize(DeviceState *dev, Error **errp) -{ - ICCDeviceClass *idc = ICC_DEVICE_GET_CLASS(dev); - - /* convert to QOM */ - if (idc->realize) { - idc->realize(dev, errp); - } - -} - -static void icc_device_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = icc_device_realize; - dc->bus_type = TYPE_ICC_BUS; -} - -static const TypeInfo icc_device_info = { - .name = TYPE_ICC_DEVICE, - .parent = TYPE_DEVICE, - .abstract = true, - .instance_size = sizeof(ICCDevice), - .class_size = sizeof(ICCDeviceClass), - .class_init = icc_device_class_init, -}; - - -/* icc-bridge implementation */ - -typedef struct ICCBridgeState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - ICCBus icc_bus; - MemoryRegion apic_container; -} ICCBridgeState; - -#define ICC_BRIDGE(obj) OBJECT_CHECK(ICCBridgeState, (obj), TYPE_ICC_BRIDGE) - -static void icc_bridge_init(Object *obj) -{ - ICCBridgeState *s = ICC_BRIDGE(obj); - SysBusDevice *sb = SYS_BUS_DEVICE(obj); - - qbus_create_inplace(&s->icc_bus, sizeof(s->icc_bus), TYPE_ICC_BUS, - DEVICE(s), "icc"); - - /* Do not change order of registering regions, - * APIC must be first registered region, board maps it by 0 index - */ - memory_region_init(&s->apic_container, obj, "icc-apic-container", - APIC_SPACE_SIZE); - sysbus_init_mmio(sb, &s->apic_container); - s->icc_bus.apic_address_space = &s->apic_container; -} - -static void icc_bridge_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); -} - -static const TypeInfo icc_bridge_info = { - .name = TYPE_ICC_BRIDGE, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_init = icc_bridge_init, - .instance_size = sizeof(ICCBridgeState), - .class_init = icc_bridge_class_init, -}; - - -static void icc_bus_register_types(void) -{ - type_register_static(&icc_bus_info); - type_register_static(&icc_device_info); - type_register_static(&icc_bridge_info); -} - -type_init(icc_bus_register_types) diff --git a/qemu/hw/cpu/realview_mpcore.c b/qemu/hw/cpu/realview_mpcore.c index c39a2da42..39d4ebeb1 100644 --- a/qemu/hw/cpu/realview_mpcore.c +++ b/qemu/hw/cpu/realview_mpcore.c @@ -8,6 +8,8 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/cpu/arm11mpcore.h" #include "hw/intc/realview_gic.h" diff --git a/qemu/hw/cris/axis_dev88.c b/qemu/hw/cris/axis_dev88.c index 3cae480fa..9f5865874 100644 --- a/qemu/hw/cris/axis_dev88.c +++ b/qemu/hw/cris/axis_dev88.c @@ -22,6 +22,10 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "net/net.h" #include "hw/block/flash.h" @@ -138,7 +142,7 @@ static void tempsensor_clkedge(struct tempsensor_t *s, s->count = 16; if ((s->regs[0] & 0xff) == 0) { - /* 25 degrees celcius. */ + /* 25 degrees celsius. */ s->shiftreg = 0x0b9f; } else if ((s->regs[0] & 0xff) == 0xff) { /* Sensor ID, 0x8100 LM70. */ @@ -277,7 +281,7 @@ void axisdev88_init(MachineState *machine) /* The ETRAX-FS has 128Kb on chip ram, the docs refer to it as the internal memory. */ memory_region_init_ram(phys_intmem, NULL, "axisdev88.chipram", INTMEM_SIZE, - &error_abort); + &error_fatal); vmstate_register_ram_global(phys_intmem); memory_region_add_subregion(address_space_mem, 0x38000000, phys_intmem); @@ -351,16 +355,11 @@ void axisdev88_init(MachineState *machine) } } -static QEMUMachine axisdev88_machine = { - .name = "axis-dev88", - .desc = "AXIS devboard 88", - .init = axisdev88_init, - .is_default = 1, -}; - -static void axisdev88_machine_init(void) +static void axisdev88_machine_init(MachineClass *mc) { - qemu_register_machine(&axisdev88_machine); + mc->desc = "AXIS devboard 88"; + mc->init = axisdev88_init; + mc->is_default = 1; } -machine_init(axisdev88_machine_init); +DEFINE_MACHINE("axis-dev88", axisdev88_machine_init) diff --git a/qemu/hw/cris/boot.c b/qemu/hw/cris/boot.c index 622f353c9..f896ed7f8 100644 --- a/qemu/hw/cris/boot.c +++ b/qemu/hw/cris/boot.c @@ -22,10 +22,14 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/loader.h" #include "elf.h" #include "boot.h" +#include "qemu/cutils.h" static void main_cpu_reset(void *opaque) { @@ -72,7 +76,7 @@ void cris_load_image(CRISCPU *cpu, struct cris_load_info *li) /* Boots a kernel elf binary, os/linux-2.6/vmlinux from the axis devboard SDK. */ image_size = load_elf(li->image_filename, translate_kernel_address, NULL, - &entry, NULL, &high, 0, ELF_MACHINE, 0); + &entry, NULL, &high, 0, EM_CRIS, 0, 0); li->entry = entry; if (image_size < 0) { /* Takes a kimage from the axis devboard SDK. */ diff --git a/qemu/hw/display/Makefile.objs b/qemu/hw/display/Makefile.objs index dd8ea76d1..d99780eeb 100644 --- a/qemu/hw/display/Makefile.objs +++ b/qemu/hw/display/Makefile.objs @@ -27,6 +27,7 @@ endif obj-$(CONFIG_OMAP) += omap_dss.o obj-$(CONFIG_OMAP) += omap_lcdc.o obj-$(CONFIG_PXA2XX) += pxa2xx_lcd.o +obj-$(CONFIG_RASPI) += bcm2835_fb.o obj-$(CONFIG_SM501) += sm501.o obj-$(CONFIG_TCX) += tcx.o obj-$(CONFIG_CG3) += cg3.o @@ -35,6 +36,10 @@ obj-$(CONFIG_VGA) += vga.o common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o -obj-$(CONFIG_VIRTIO) += virtio-gpu.o +obj-$(CONFIG_VIRTIO) += virtio-gpu.o virtio-gpu-3d.o obj-$(CONFIG_VIRTIO_PCI) += virtio-gpu-pci.o obj-$(CONFIG_VIRTIO_VGA) += virtio-vga.o +virtio-gpu.o-cflags := $(VIRGL_CFLAGS) +virtio-gpu.o-libs += $(VIRGL_LIBS) +virtio-gpu-3d.o-cflags := $(VIRGL_CFLAGS) +virtio-gpu-3d.o-libs += $(VIRGL_LIBS) diff --git a/qemu/hw/display/ads7846.c b/qemu/hw/display/ads7846.c index 3f35369bb..05aa2d1e6 100644 --- a/qemu/hw/display/ads7846.c +++ b/qemu/hw/display/ads7846.c @@ -10,7 +10,8 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw/ssi.h" +#include "qemu/osdep.h" +#include "hw/ssi/ssi.h" #include "ui/console.h" typedef struct { diff --git a/qemu/hw/display/bcm2835_fb.c b/qemu/hw/display/bcm2835_fb.c new file mode 100644 index 000000000..506f1d3d9 --- /dev/null +++ b/qemu/hw/display/bcm2835_fb.c @@ -0,0 +1,425 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann. + * This code is licensed under the GNU GPLv2 and later. + * + * Heavily based on milkymist-vgafb.c, copyright terms below: + * QEMU model of the Milkymist VGA framebuffer. + * + * Copyright (c) 2010-2012 Michael Walle + * + * 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/display/bcm2835_fb.h" +#include "hw/display/framebuffer.h" +#include "ui/pixel_ops.h" +#include "hw/misc/bcm2835_mbox_defs.h" + +#define DEFAULT_VCRAM_SIZE 0x4000000 +#define BCM2835_FB_OFFSET 0x00100000 + +static void fb_invalidate_display(void *opaque) +{ + BCM2835FBState *s = BCM2835_FB(opaque); + + s->invalidate = true; +} + +static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src, + int width, int deststep) +{ + BCM2835FBState *s = opaque; + uint16_t rgb565; + uint32_t rgb888; + uint8_t r, g, b; + DisplaySurface *surface = qemu_console_surface(s->con); + int bpp = surface_bits_per_pixel(surface); + + while (width--) { + switch (s->bpp) { + case 8: + /* lookup palette starting at video ram base + * TODO: cache translation, rather than doing this each time! + */ + rgb888 = ldl_le_phys(&s->dma_as, s->vcram_base + (*src << 2)); + r = (rgb888 >> 0) & 0xff; + g = (rgb888 >> 8) & 0xff; + b = (rgb888 >> 16) & 0xff; + src++; + break; + case 16: + rgb565 = lduw_le_p(src); + r = ((rgb565 >> 11) & 0x1f) << 3; + g = ((rgb565 >> 5) & 0x3f) << 2; + b = ((rgb565 >> 0) & 0x1f) << 3; + src += 2; + break; + case 24: + rgb888 = ldl_le_p(src); + r = (rgb888 >> 0) & 0xff; + g = (rgb888 >> 8) & 0xff; + b = (rgb888 >> 16) & 0xff; + src += 3; + break; + case 32: + rgb888 = ldl_le_p(src); + r = (rgb888 >> 0) & 0xff; + g = (rgb888 >> 8) & 0xff; + b = (rgb888 >> 16) & 0xff; + src += 4; + break; + default: + r = 0; + g = 0; + b = 0; + break; + } + + if (s->pixo == 0) { + /* swap to BGR pixel format */ + uint8_t tmp = r; + r = b; + b = tmp; + } + + switch (bpp) { + case 8: + *dst++ = rgb_to_pixel8(r, g, b); + break; + case 15: + *(uint16_t *)dst = rgb_to_pixel15(r, g, b); + dst += 2; + break; + case 16: + *(uint16_t *)dst = rgb_to_pixel16(r, g, b); + dst += 2; + break; + case 24: + rgb888 = rgb_to_pixel24(r, g, b); + *dst++ = rgb888 & 0xff; + *dst++ = (rgb888 >> 8) & 0xff; + *dst++ = (rgb888 >> 16) & 0xff; + break; + case 32: + *(uint32_t *)dst = rgb_to_pixel32(r, g, b); + dst += 4; + break; + default: + return; + } + } +} + +static void fb_update_display(void *opaque) +{ + BCM2835FBState *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->con); + int first = 0; + int last = 0; + int src_width = 0; + int dest_width = 0; + + if (s->lock || !s->xres) { + return; + } + + src_width = s->xres * (s->bpp >> 3); + dest_width = s->xres; + + switch (surface_bits_per_pixel(surface)) { + case 0: + return; + case 8: + break; + case 15: + dest_width *= 2; + break; + case 16: + dest_width *= 2; + break; + case 24: + dest_width *= 3; + break; + case 32: + dest_width *= 4; + break; + default: + hw_error("bcm2835_fb: bad color depth\n"); + break; + } + + if (s->invalidate) { + framebuffer_update_memory_section(&s->fbsection, s->dma_mr, s->base, + s->yres, src_width); + } + + framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres, + src_width, dest_width, 0, s->invalidate, + draw_line_src16, s, &first, &last); + + if (first >= 0) { + dpy_gfx_update(s->con, 0, first, s->xres, last - first + 1); + } + + s->invalidate = false; +} + +static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value) +{ + value &= ~0xf; + + s->lock = true; + + s->xres = ldl_le_phys(&s->dma_as, value); + s->yres = ldl_le_phys(&s->dma_as, value + 4); + s->xres_virtual = ldl_le_phys(&s->dma_as, value + 8); + s->yres_virtual = ldl_le_phys(&s->dma_as, value + 12); + s->bpp = ldl_le_phys(&s->dma_as, value + 20); + s->xoffset = ldl_le_phys(&s->dma_as, value + 24); + s->yoffset = ldl_le_phys(&s->dma_as, value + 28); + + s->base = s->vcram_base | (value & 0xc0000000); + s->base += BCM2835_FB_OFFSET; + + /* TODO - Manage properly virtual resolution */ + + s->pitch = s->xres * (s->bpp >> 3); + s->size = s->yres * s->pitch; + + stl_le_phys(&s->dma_as, value + 16, s->pitch); + stl_le_phys(&s->dma_as, value + 32, s->base); + stl_le_phys(&s->dma_as, value + 36, s->size); + + s->invalidate = true; + qemu_console_resize(s->con, s->xres, s->yres); + s->lock = false; +} + +void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres, + uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp, + uint32_t *pixo, uint32_t *alpha) +{ + s->lock = true; + + /* TODO: input validation! */ + if (xres) { + s->xres = *xres; + } + if (yres) { + s->yres = *yres; + } + if (xoffset) { + s->xoffset = *xoffset; + } + if (yoffset) { + s->yoffset = *yoffset; + } + if (bpp) { + s->bpp = *bpp; + } + if (pixo) { + s->pixo = *pixo; + } + if (alpha) { + s->alpha = *alpha; + } + + /* TODO - Manage properly virtual resolution */ + + s->pitch = s->xres * (s->bpp >> 3); + s->size = s->yres * s->pitch; + + s->invalidate = true; + qemu_console_resize(s->con, s->xres, s->yres); + s->lock = false; +} + +static uint64_t bcm2835_fb_read(void *opaque, hwaddr offset, unsigned size) +{ + BCM2835FBState *s = opaque; + uint32_t res = 0; + + switch (offset) { + case MBOX_AS_DATA: + res = MBOX_CHAN_FB; + s->pending = false; + qemu_set_irq(s->mbox_irq, 0); + break; + + case MBOX_AS_PENDING: + res = s->pending; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return 0; + } + + return res; +} + +static void bcm2835_fb_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + BCM2835FBState *s = opaque; + + switch (offset) { + case MBOX_AS_DATA: + /* bcm2835_mbox should check our pending status before pushing */ + assert(!s->pending); + s->pending = true; + bcm2835_fb_mbox_push(s, value); + qemu_set_irq(s->mbox_irq, 1); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return; + } +} + +static const MemoryRegionOps bcm2835_fb_ops = { + .read = bcm2835_fb_read, + .write = bcm2835_fb_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static const VMStateDescription vmstate_bcm2835_fb = { + .name = TYPE_BCM2835_FB, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL(lock, BCM2835FBState), + VMSTATE_BOOL(invalidate, BCM2835FBState), + VMSTATE_BOOL(pending, BCM2835FBState), + VMSTATE_UINT32(xres, BCM2835FBState), + VMSTATE_UINT32(yres, BCM2835FBState), + VMSTATE_UINT32(xres_virtual, BCM2835FBState), + VMSTATE_UINT32(yres_virtual, BCM2835FBState), + VMSTATE_UINT32(xoffset, BCM2835FBState), + VMSTATE_UINT32(yoffset, BCM2835FBState), + VMSTATE_UINT32(bpp, BCM2835FBState), + VMSTATE_UINT32(base, BCM2835FBState), + VMSTATE_UINT32(pitch, BCM2835FBState), + VMSTATE_UINT32(size, BCM2835FBState), + VMSTATE_UINT32(pixo, BCM2835FBState), + VMSTATE_UINT32(alpha, BCM2835FBState), + VMSTATE_END_OF_LIST() + } +}; + +static const GraphicHwOps vgafb_ops = { + .invalidate = fb_invalidate_display, + .gfx_update = fb_update_display, +}; + +static void bcm2835_fb_init(Object *obj) +{ + BCM2835FBState *s = BCM2835_FB(obj); + + memory_region_init_io(&s->iomem, obj, &bcm2835_fb_ops, s, TYPE_BCM2835_FB, + 0x10); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq); +} + +static void bcm2835_fb_reset(DeviceState *dev) +{ + BCM2835FBState *s = BCM2835_FB(dev); + + s->pending = false; + + s->xres_virtual = s->xres; + s->yres_virtual = s->yres; + s->xoffset = 0; + s->yoffset = 0; + s->base = s->vcram_base + BCM2835_FB_OFFSET; + s->pitch = s->xres * (s->bpp >> 3); + s->size = s->yres * s->pitch; + + s->invalidate = true; + s->lock = false; +} + +static void bcm2835_fb_realize(DeviceState *dev, Error **errp) +{ + BCM2835FBState *s = BCM2835_FB(dev); + Error *err = NULL; + Object *obj; + + if (s->vcram_base == 0) { + error_setg(errp, "%s: required vcram-base property not set", __func__); + return; + } + + obj = object_property_get_link(OBJECT(dev), "dma-mr", &err); + if (obj == NULL) { + error_setg(errp, "%s: required dma-mr link not found: %s", + __func__, error_get_pretty(err)); + return; + } + + s->dma_mr = MEMORY_REGION(obj); + address_space_init(&s->dma_as, s->dma_mr, NULL); + + bcm2835_fb_reset(dev); + + s->con = graphic_console_init(dev, 0, &vgafb_ops, s); + qemu_console_resize(s->con, s->xres, s->yres); +} + +static Property bcm2835_fb_props[] = { + DEFINE_PROP_UINT32("vcram-base", BCM2835FBState, vcram_base, 0),/*required*/ + DEFINE_PROP_UINT32("vcram-size", BCM2835FBState, vcram_size, + DEFAULT_VCRAM_SIZE), + DEFINE_PROP_UINT32("xres", BCM2835FBState, xres, 640), + DEFINE_PROP_UINT32("yres", BCM2835FBState, yres, 480), + DEFINE_PROP_UINT32("bpp", BCM2835FBState, bpp, 16), + DEFINE_PROP_UINT32("pixo", BCM2835FBState, pixo, 1), /* 1=RGB, 0=BGR */ + DEFINE_PROP_UINT32("alpha", BCM2835FBState, alpha, 2), /* alpha ignored */ + DEFINE_PROP_END_OF_LIST() +}; + +static void bcm2835_fb_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = bcm2835_fb_props; + dc->realize = bcm2835_fb_realize; + dc->reset = bcm2835_fb_reset; + dc->vmsd = &vmstate_bcm2835_fb; +} + +static TypeInfo bcm2835_fb_info = { + .name = TYPE_BCM2835_FB, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835FBState), + .class_init = bcm2835_fb_class_init, + .instance_init = bcm2835_fb_init, +}; + +static void bcm2835_fb_register_types(void) +{ + type_register_static(&bcm2835_fb_info); +} + +type_init(bcm2835_fb_register_types) diff --git a/qemu/hw/display/blizzard.c b/qemu/hw/display/blizzard.c index 5019bbbef..c231960d9 100644 --- a/qemu/hw/display/blizzard.c +++ b/qemu/hw/display/blizzard.c @@ -18,6 +18,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "ui/console.h" #include "hw/devices.h" diff --git a/qemu/hw/display/cg3.c b/qemu/hw/display/cg3.c index 34dcbc311..fc0d97fa4 100644 --- a/qemu/hw/display/cg3.c +++ b/qemu/hw/display/cg3.c @@ -23,6 +23,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/error-report.h" #include "ui/console.h" @@ -280,12 +282,12 @@ static void cg3_initfn(Object *obj) SysBusDevice *sbd = SYS_BUS_DEVICE(obj); CG3State *s = CG3(obj); - memory_region_init_ram(&s->rom, NULL, "cg3.prom", FCODE_MAX_ROM_SIZE, - &error_abort); + memory_region_init_ram(&s->rom, obj, "cg3.prom", FCODE_MAX_ROM_SIZE, + &error_fatal); memory_region_set_readonly(&s->rom, true); sysbus_init_mmio(sbd, &s->rom); - memory_region_init_io(&s->reg, NULL, &cg3_reg_ops, s, "cg3.reg", + memory_region_init_io(&s->reg, obj, &cg3_reg_ops, s, "cg3.reg", CG3_REG_SIZE); sysbus_init_mmio(sbd, &s->reg); } @@ -310,7 +312,7 @@ static void cg3_realizefn(DeviceState *dev, Error **errp) } memory_region_init_ram(&s->vram_mem, NULL, "cg3.vram", s->vram_size, - &error_abort); + &error_fatal); memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA); vmstate_register_ram_global(&s->vram_mem); sysbus_init_mmio(sbd, &s->vram_mem); diff --git a/qemu/hw/display/cirrus_vga.c b/qemu/hw/display/cirrus_vga.c index 5198037d8..3d712d592 100644 --- a/qemu/hw/display/cirrus_vga.c +++ b/qemu/hw/display/cirrus_vga.c @@ -26,6 +26,8 @@ * Reference: Finn Thogersons' VGADOC4b * available at http://home.worldonline.dk/~finth/ */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "ui/console.h" @@ -275,14 +277,14 @@ static bool blit_region_is_unsafe(struct CirrusVGAState *s, + ((int64_t)s->cirrus_blt_height-1) * pitch; int32_t max = addr + s->cirrus_blt_width; - if (min < 0 || max >= s->vga.vram_size) { + if (min < 0 || max > s->vga.vram_size) { return true; } } else { int64_t max = addr + ((int64_t)s->cirrus_blt_height-1) * pitch + s->cirrus_blt_width; - if (max >= s->vga.vram_size) { + if (max > s->vga.vram_size) { return true; } } diff --git a/qemu/hw/display/exynos4210_fimd.c b/qemu/hw/display/exynos4210_fimd.c index 603ef5056..728eb214a 100644 --- a/qemu/hw/display/exynos4210_fimd.c +++ b/qemu/hw/display/exynos4210_fimd.c @@ -22,6 +22,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/sysbus.h" #include "ui/console.h" @@ -1354,9 +1355,7 @@ static void exynos4210_fimd_reset(DeviceState *d) fimd_update_get_alpha(s, w); } - if (s->ifb != NULL) { - g_free(s->ifb); - } + g_free(s->ifb); s->ifb = NULL; exynos4210_fimd_invalidate(s); diff --git a/qemu/hw/display/framebuffer.c b/qemu/hw/display/framebuffer.c index 7f075ce77..df51358e7 100644 --- a/qemu/hw/display/framebuffer.c +++ b/qemu/hw/display/framebuffer.c @@ -17,6 +17,7 @@ - Remove all DisplayState knowledge from devices. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "ui/console.h" #include "framebuffer.h" diff --git a/qemu/hw/display/g364fb.c b/qemu/hw/display/g364fb.c index 7f83a007b..70ef2c745 100644 --- a/qemu/hw/display/g364fb.c +++ b/qemu/hw/display/g364fb.c @@ -17,6 +17,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "qemu/error-report.h" #include "ui/console.h" diff --git a/qemu/hw/display/jazz_led.c b/qemu/hw/display/jazz_led.c index 12b1707cb..09dcdb46a 100644 --- a/qemu/hw/display/jazz_led.c +++ b/qemu/hw/display/jazz_led.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "ui/console.h" #include "ui/pixel_ops.h" @@ -233,8 +234,10 @@ static void jazz_led_text_update(void *opaque, console_ch_t *chardata) /* TODO: draw the segments */ snprintf(buf, 2, "%02hhx\n", s->segments); - console_write_ch(chardata++, 0x00200100 | buf[0]); - console_write_ch(chardata++, 0x00200100 | buf[1]); + console_write_ch(chardata++, ATTR2CHTYPE(buf[0], QEMU_COLOR_BLUE, + QEMU_COLOR_BLACK, 1)); + console_write_ch(chardata++, ATTR2CHTYPE(buf[1], QEMU_COLOR_BLUE, + QEMU_COLOR_BLACK, 1)); dpy_text_update(s->con, 0, 0, 2, 1); } diff --git a/qemu/hw/display/milkymist-tmu2.c b/qemu/hw/display/milkymist-tmu2.c index 3e1d0b9c2..9bc88f93b 100644 --- a/qemu/hw/display/milkymist-tmu2.c +++ b/qemu/hw/display/milkymist-tmu2.c @@ -24,14 +24,15 @@ * */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "trace.h" #include "qemu/error-report.h" #include -#include -#include +#include +#include enum { R_CTL = 0, diff --git a/qemu/hw/display/milkymist-vgafb.c b/qemu/hw/display/milkymist-vgafb.c index ab3074fad..19ca25647 100644 --- a/qemu/hw/display/milkymist-vgafb.c +++ b/qemu/hw/display/milkymist-vgafb.c @@ -22,6 +22,7 @@ * http://www.milkymist.org/socdoc/vgafb.pdf */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "trace.h" diff --git a/qemu/hw/display/omap_dss.c b/qemu/hw/display/omap_dss.c index f1fef2767..783e9e131 100644 --- a/qemu/hw/display/omap_dss.c +++ b/qemu/hw/display/omap_dss.c @@ -17,6 +17,7 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "ui/console.h" #include "hw/arm/omap.h" @@ -1051,8 +1052,7 @@ struct omap_dss_s *omap_dss_init(struct omap_target_agent_s *ta, omap_clk fck1, omap_clk fck2, omap_clk ck54m, omap_clk ick1, omap_clk ick2) { - struct omap_dss_s *s = (struct omap_dss_s *) - g_malloc0(sizeof(struct omap_dss_s)); + struct omap_dss_s *s = g_new0(struct omap_dss_s, 1); s->irq = irq; s->drq = drq; diff --git a/qemu/hw/display/omap_lcd_template.h b/qemu/hw/display/omap_lcd_template.h index e5dd44716..f0ce71fd6 100644 --- a/qemu/hw/display/omap_lcd_template.h +++ b/qemu/hw/display/omap_lcd_template.h @@ -136,7 +136,7 @@ static void glue(draw_line12_, DEPTH)(void *opaque, uint8_t r, g, b; do { - v = lduw_p((void *) s); + v = lduw_le_p((void *) s); r = (v >> 4) & 0xf0; g = v & 0xf0; b = (v << 4) & 0xf0; @@ -159,7 +159,7 @@ static void glue(draw_line16_, DEPTH)(void *opaque, uint8_t r, g, b; do { - v = lduw_p((void *) s); + v = lduw_le_p((void *) s); r = (v >> 8) & 0xf8; g = (v >> 3) & 0xfc; b = (v << 3) & 0xf8; diff --git a/qemu/hw/display/omap_lcdc.c b/qemu/hw/display/omap_lcdc.c index a7c6cd79b..ce1058bf8 100644 --- a/qemu/hw/display/omap_lcdc.c +++ b/qemu/hw/display/omap_lcdc.c @@ -16,6 +16,7 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "ui/console.h" #include "hw/arm/omap.h" @@ -403,8 +404,7 @@ struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem, struct omap_dma_lcd_channel_s *dma, omap_clk clk) { - struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) - g_malloc0(sizeof(struct omap_lcd_panel_s)); + struct omap_lcd_panel_s *s = g_new0(struct omap_lcd_panel_s, 1); s->irq = irq; s->dma = dma; diff --git a/qemu/hw/display/pl110.c b/qemu/hw/display/pl110.c index ef1a7b1a5..d589959f1 100644 --- a/qemu/hw/display/pl110.c +++ b/qemu/hw/display/pl110.c @@ -7,6 +7,7 @@ * This code is licensed under the GNU LGPL */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "ui/console.h" #include "framebuffer.h" diff --git a/qemu/hw/display/pxa2xx_lcd.c b/qemu/hw/display/pxa2xx_lcd.c index 494700d07..845521c5b 100644 --- a/qemu/hw/display/pxa2xx_lcd.c +++ b/qemu/hw/display/pxa2xx_lcd.c @@ -10,6 +10,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "ui/console.h" #include "hw/arm/pxa.h" @@ -309,10 +310,10 @@ static void pxa2xx_descriptor_load(PXA2xxLCDState *s) } cpu_physical_memory_read(descptr, &desc, sizeof(desc)); - s->dma_ch[i].descriptor = tswap32(desc.fdaddr); - s->dma_ch[i].source = tswap32(desc.fsaddr); - s->dma_ch[i].id = tswap32(desc.fidr); - s->dma_ch[i].command = tswap32(desc.ldcmd); + s->dma_ch[i].descriptor = le32_to_cpu(desc.fdaddr); + s->dma_ch[i].source = le32_to_cpu(desc.fsaddr); + s->dma_ch[i].id = le32_to_cpu(desc.fidr); + s->dma_ch[i].command = le32_to_cpu(desc.ldcmd); } } diff --git a/qemu/hw/display/qxl-logger.c b/qemu/hw/display/qxl-logger.c index d944d3fdb..2ec6d8fa3 100644 --- a/qemu/hw/display/qxl-logger.c +++ b/qemu/hw/display/qxl-logger.c @@ -19,6 +19,7 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu/timer.h" #include "qxl.h" diff --git a/qemu/hw/display/qxl-render.c b/qemu/hw/display/qxl-render.c index a542087fc..9ad9d9e0f 100644 --- a/qemu/hw/display/qxl-render.c +++ b/qemu/hw/display/qxl-render.c @@ -19,6 +19,7 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include "qxl.h" #include "trace.h" @@ -159,7 +160,7 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) /* * use ssd.lock to protect render_update_cookie_num. * qxl_render_update is called by io thread or vcpu thread, and the completion - * callbacks are called by spice_server thread, defering to bh called from the + * callbacks are called by spice_server thread, deferring to bh called from the * io thread. */ void qxl_render_update(PCIQXLDevice *qxl) diff --git a/qemu/hw/display/qxl.c b/qemu/hw/display/qxl.c index 2288238d0..919dc5cd3 100644 --- a/qemu/hw/display/qxl.c +++ b/qemu/hw/display/qxl.c @@ -18,8 +18,8 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include -#include #include "qemu-common.h" #include "qemu/timer.h" @@ -1156,7 +1156,9 @@ static void qxl_soft_reset(PCIQXLDevice *d) trace_qxl_soft_reset(d->id); qxl_check_state(d); qxl_clear_guest_bug(d); + qemu_mutex_lock(&d->async_lock); d->current_async = QXL_UNDEFINED_IO; + qemu_mutex_unlock(&d->async_lock); if (d->id == 0) { qxl_enter_vga_mode(d); @@ -1970,14 +1972,14 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp) qxl->rom_size = qxl_rom_size(); memory_region_init_ram(&qxl->rom_bar, OBJECT(qxl), "qxl.vrom", - qxl->rom_size, &error_abort); + qxl->rom_size, &error_fatal); vmstate_register_ram(&qxl->rom_bar, &qxl->pci.qdev); init_qxl_rom(qxl); init_qxl_ram(qxl); qxl->guest_surfaces.cmds = g_new0(QXLPHYSICAL, qxl->ssd.num_surfaces); memory_region_init_ram(&qxl->vram_bar, OBJECT(qxl), "qxl.vram", - qxl->vram_size, &error_abort); + qxl->vram_size, &error_fatal); vmstate_register_ram(&qxl->vram_bar, &qxl->pci.qdev); memory_region_init_alias(&qxl->vram32_bar, OBJECT(qxl), "qxl.vram32", &qxl->vram_bar, 0, qxl->vram32_size); @@ -2079,7 +2081,7 @@ static void qxl_realize_secondary(PCIDevice *dev, Error **errp) qxl->id = device_id++; qxl_init_ramsize(qxl); memory_region_init_ram(&qxl->vga.vram, OBJECT(dev), "qxl.vgavram", - qxl->vga.vram_size, &error_abort); + qxl->vga.vram_size, &error_fatal); vmstate_register_ram(&qxl->vga.vram, &qxl->pci.qdev); qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram); qxl->vga.con = graphic_console_init(DEVICE(dev), 0, &qxl_ops, qxl); @@ -2156,7 +2158,7 @@ static int qxl_post_load(void *opaque, int version) qxl_create_guest_primary(d, 1, QXL_SYNC); /* replay surface-create and cursor-set commands */ - cmds = g_malloc0(sizeof(QXLCommandExt) * (d->ssd.num_surfaces + 1)); + cmds = g_new0(QXLCommandExt, d->ssd.num_surfaces + 1); for (in = 0, out = 0; in < d->ssd.num_surfaces; in++) { if (d->guest_surfaces.cmds[in] == 0) { continue; diff --git a/qemu/hw/display/sm501.c b/qemu/hw/display/sm501.c index 15a5ba800..5f7101210 100644 --- a/qemu/hw/display/sm501.c +++ b/qemu/hw/display/sm501.c @@ -22,7 +22,10 @@ * THE SOFTWARE. */ -#include +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/char/serial.h" #include "ui/console.h" @@ -1411,7 +1414,7 @@ void sm501_init(MemoryRegion *address_space_mem, uint32_t base, /* allocate local memory */ memory_region_init_ram(&s->local_mem_region, NULL, "sm501.local", - local_mem_bytes, &error_abort); + local_mem_bytes, &error_fatal); vmstate_register_ram_global(&s->local_mem_region); memory_region_set_log(&s->local_mem_region, true, DIRTY_MEMORY_VGA); s->local_mem = memory_region_get_ram_ptr(&s->local_mem_region); diff --git a/qemu/hw/display/ssd0303.c b/qemu/hw/display/ssd0303.c index f6804fb51..d3017563f 100644 --- a/qemu/hw/display/ssd0303.c +++ b/qemu/hw/display/ssd0303.c @@ -10,6 +10,7 @@ /* The controller can support a variety of different displays, but we only implement one. Most of the commends relating to brightness and geometry setup are ignored. */ +#include "qemu/osdep.h" #include "hw/i2c/i2c.h" #include "ui/console.h" diff --git a/qemu/hw/display/ssd0323.c b/qemu/hw/display/ssd0323.c index 97270077e..14c1bf339 100644 --- a/qemu/hw/display/ssd0323.c +++ b/qemu/hw/display/ssd0323.c @@ -10,7 +10,8 @@ /* The controller can support a variety of different displays, but we only implement one. Most of the commends relating to brightness and geometry setup are ignored. */ -#include "hw/ssi.h" +#include "qemu/osdep.h" +#include "hw/ssi/ssi.h" #include "ui/console.h" //#define DEBUG_SSD0323 1 diff --git a/qemu/hw/display/tc6393xb.c b/qemu/hw/display/tc6393xb.c index f5f3f3e69..da3ceceb0 100644 --- a/qemu/hw/display/tc6393xb.c +++ b/qemu/hw/display/tc6393xb.c @@ -10,6 +10,8 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "hw/devices.h" #include "hw/block/flash.h" @@ -584,7 +586,7 @@ TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq) memory_region_add_subregion(sysmem, base, &s->iomem); memory_region_init_ram(&s->vram, NULL, "tc6393xb.vram", 0x100000, - &error_abort); + &error_fatal); vmstate_register_ram_global(&s->vram); s->vram_ptr = memory_region_get_ram_ptr(&s->vram); memory_region_add_subregion(sysmem, base + 0x100000, &s->vram); diff --git a/qemu/hw/display/tcx.c b/qemu/hw/display/tcx.c index 6acdc2d28..8e26aae80 100644 --- a/qemu/hw/display/tcx.c +++ b/qemu/hw/display/tcx.c @@ -22,7 +22,10 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" +#include "cpu.h" /* FIXME shouldn't use TARGET_PAGE_SIZE */ #include "ui/console.h" #include "ui/pixel_ops.h" #include "hw/loader.h" @@ -944,57 +947,55 @@ static void tcx_initfn(Object *obj) SysBusDevice *sbd = SYS_BUS_DEVICE(obj); TCXState *s = TCX(obj); - memory_region_init_ram(&s->rom, NULL, "tcx.prom", FCODE_MAX_ROM_SIZE, - &error_abort); + memory_region_init_ram(&s->rom, obj, "tcx.prom", FCODE_MAX_ROM_SIZE, + &error_fatal); memory_region_set_readonly(&s->rom, true); sysbus_init_mmio(sbd, &s->rom); /* 2/STIP : Stippler */ - memory_region_init_io(&s->stip, OBJECT(s), &tcx_stip_ops, s, "tcx.stip", + memory_region_init_io(&s->stip, obj, &tcx_stip_ops, s, "tcx.stip", TCX_STIP_NREGS); sysbus_init_mmio(sbd, &s->stip); /* 3/BLIT : Blitter */ - memory_region_init_io(&s->blit, OBJECT(s), &tcx_blit_ops, s, "tcx.blit", + memory_region_init_io(&s->blit, obj, &tcx_blit_ops, s, "tcx.blit", TCX_BLIT_NREGS); sysbus_init_mmio(sbd, &s->blit); /* 5/RSTIP : Raw Stippler */ - memory_region_init_io(&s->rstip, OBJECT(s), &tcx_rstip_ops, s, "tcx.rstip", + memory_region_init_io(&s->rstip, obj, &tcx_rstip_ops, s, "tcx.rstip", TCX_RSTIP_NREGS); sysbus_init_mmio(sbd, &s->rstip); /* 6/RBLIT : Raw Blitter */ - memory_region_init_io(&s->rblit, OBJECT(s), &tcx_rblit_ops, s, "tcx.rblit", + memory_region_init_io(&s->rblit, obj, &tcx_rblit_ops, s, "tcx.rblit", TCX_RBLIT_NREGS); sysbus_init_mmio(sbd, &s->rblit); /* 7/TEC : ??? */ - memory_region_init_io(&s->tec, OBJECT(s), &tcx_dummy_ops, s, - "tcx.tec", TCX_TEC_NREGS); + memory_region_init_io(&s->tec, obj, &tcx_dummy_ops, s, "tcx.tec", + TCX_TEC_NREGS); sysbus_init_mmio(sbd, &s->tec); /* 8/CMAP : DAC */ - memory_region_init_io(&s->dac, OBJECT(s), &tcx_dac_ops, s, - "tcx.dac", TCX_DAC_NREGS); + memory_region_init_io(&s->dac, obj, &tcx_dac_ops, s, "tcx.dac", + TCX_DAC_NREGS); sysbus_init_mmio(sbd, &s->dac); /* 9/THC : Cursor */ - memory_region_init_io(&s->thc, OBJECT(s), &tcx_thc_ops, s, "tcx.thc", + memory_region_init_io(&s->thc, obj, &tcx_thc_ops, s, "tcx.thc", TCX_THC_NREGS); sysbus_init_mmio(sbd, &s->thc); /* 11/DHC : ??? */ - memory_region_init_io(&s->dhc, OBJECT(s), &tcx_dummy_ops, s, "tcx.dhc", + memory_region_init_io(&s->dhc, obj, &tcx_dummy_ops, s, "tcx.dhc", TCX_DHC_NREGS); sysbus_init_mmio(sbd, &s->dhc); /* 12/ALT : ??? */ - memory_region_init_io(&s->alt, OBJECT(s), &tcx_dummy_ops, s, "tcx.alt", + memory_region_init_io(&s->alt, obj, &tcx_dummy_ops, s, "tcx.alt", TCX_ALT_NREGS); sysbus_init_mmio(sbd, &s->alt); - - return; } static void tcx_realizefn(DeviceState *dev, Error **errp) @@ -1007,7 +1008,7 @@ static void tcx_realizefn(DeviceState *dev, Error **errp) char *fcode_filename; memory_region_init_ram(&s->vram_mem, OBJECT(s), "tcx.vram", - s->vram_size * (1 + 4 + 4), &error_abort); + s->vram_size * (1 + 4 + 4), &error_fatal); vmstate_register_ram_global(&s->vram_mem); memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA); vram_base = memory_region_get_ram_ptr(&s->vram_mem); diff --git a/qemu/hw/display/vga-isa-mm.c b/qemu/hw/display/vga-isa-mm.c index 4efc22278..51ccbccc4 100644 --- a/qemu/hw/display/vga-isa-mm.c +++ b/qemu/hw/display/vga-isa-mm.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "ui/console.h" #include "hw/i386/pc.h" diff --git a/qemu/hw/display/vga-isa.c b/qemu/hw/display/vga-isa.c index 7f3c98941..f5aff1cbe 100644 --- a/qemu/hw/display/vga-isa.c +++ b/qemu/hw/display/vga-isa.c @@ -23,6 +23,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "ui/console.h" #include "hw/i386/pc.h" diff --git a/qemu/hw/display/vga-pci.c b/qemu/hw/display/vga-pci.c index 1dfa331e6..ac9a76499 100644 --- a/qemu/hw/display/vga-pci.c +++ b/qemu/hw/display/vga-pci.c @@ -23,6 +23,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "ui/console.h" #include "hw/pci/pci.h" diff --git a/qemu/hw/display/vga.c b/qemu/hw/display/vga.c index b35d523e6..4a55ec6db 100644 --- a/qemu/hw/display/vga.c +++ b/qemu/hw/display/vga.c @@ -21,6 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "vga.h" #include "ui/console.h" @@ -140,6 +142,13 @@ static uint32_t expand4[256]; static uint16_t expand2[256]; static uint8_t expand4to8[16]; +static void vbe_update_vgaregs(VGACommonState *s); + +static inline bool vbe_enabled(VGACommonState *s) +{ + return s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED; +} + static void vga_update_memory_access(VGACommonState *s) { hwaddr base, offset, size; @@ -177,6 +186,7 @@ static void vga_update_memory_access(VGACommonState *s) size = 0x8000; break; } + assert(offset + size <= s->vram_size); memory_region_init_alias(&s->chain4_alias, memory_region_owner(&s->vram), "vga.chain4", &s->vram, offset, size); memory_region_add_subregion_overlap(s->legacy_address_space, base, @@ -234,9 +244,9 @@ static void vga_precise_update_retrace_info(VGACommonState *s) r->total_chars = vtotal_lines * htotal_chars; if (r->freq) { - r->ticks_per_char = get_ticks_per_sec() / (r->total_chars * r->freq); + r->ticks_per_char = NANOSECONDS_PER_SECOND / (r->total_chars * r->freq); } else { - r->ticks_per_char = get_ticks_per_sec() / chars_per_sec; + r->ticks_per_char = NANOSECONDS_PER_SECOND / chars_per_sec; } r->vstart = vretr_start_line; @@ -264,7 +274,7 @@ static void vga_precise_update_retrace_info(VGACommonState *s) "dots = %d\n" "ticks/char = %" PRId64 "\n" "\n", - (double) get_ticks_per_sec() / (r->ticks_per_char * r->total_chars), + (double) NANOSECONDS_PER_SECOND / (r->ticks_per_char * r->total_chars), htotal_chars, hretr_start_char, hretr_skew_chars, @@ -476,6 +486,7 @@ void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) printf("vga: write SR%x = 0x%02x\n", s->sr_index, val); #endif s->sr[s->sr_index] = val & sr_mask[s->sr_index]; + vbe_update_vgaregs(s); if (s->sr_index == VGA_SEQ_CLOCK_MODE) { s->update_retrace_info(s); } @@ -507,6 +518,7 @@ void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) printf("vga: write GR%x = 0x%02x\n", s->gr_index, val); #endif s->gr[s->gr_index] = val & gr_mask[s->gr_index]; + vbe_update_vgaregs(s); vga_update_memory_access(s); break; case VGA_CRT_IM: @@ -525,10 +537,12 @@ void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) if (s->cr_index == VGA_CRTC_OVERFLOW) { s->cr[VGA_CRTC_OVERFLOW] = (s->cr[VGA_CRTC_OVERFLOW] & ~0x10) | (val & 0x10); + vbe_update_vgaregs(s); } return; } s->cr[s->cr_index] = val; + vbe_update_vgaregs(s); switch(s->cr_index) { case VGA_CRTC_H_TOTAL: @@ -561,7 +575,7 @@ static void vbe_fixup_regs(VGACommonState *s) uint16_t *r = s->vbe_regs; uint32_t bits, linelength, maxy, offset; - if (!(r[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) { + if (!vbe_enabled(s)) { /* vbe is turned off -- nothing to do */ return; } @@ -636,6 +650,49 @@ static void vbe_fixup_regs(VGACommonState *s) s->vbe_start_addr = offset / 4; } +/* we initialize the VGA graphic mode */ +static void vbe_update_vgaregs(VGACommonState *s) +{ + int h, shift_control; + + if (!vbe_enabled(s)) { + /* vbe is turned off -- nothing to do */ + return; + } + + /* graphic mode + memory map 1 */ + s->gr[VGA_GFX_MISC] = (s->gr[VGA_GFX_MISC] & ~0x0c) | 0x04 | + VGA_GR06_GRAPHICS_MODE; + s->cr[VGA_CRTC_MODE] |= 3; /* no CGA modes */ + s->cr[VGA_CRTC_OFFSET] = s->vbe_line_offset >> 3; + /* width */ + s->cr[VGA_CRTC_H_DISP] = + (s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 3) - 1; + /* height (only meaningful if < 1024) */ + h = s->vbe_regs[VBE_DISPI_INDEX_YRES] - 1; + s->cr[VGA_CRTC_V_DISP_END] = h; + s->cr[VGA_CRTC_OVERFLOW] = (s->cr[VGA_CRTC_OVERFLOW] & ~0x42) | + ((h >> 7) & 0x02) | ((h >> 3) & 0x40); + /* line compare to 1023 */ + s->cr[VGA_CRTC_LINE_COMPARE] = 0xff; + s->cr[VGA_CRTC_OVERFLOW] |= 0x10; + s->cr[VGA_CRTC_MAX_SCAN] |= 0x40; + + if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) { + shift_control = 0; + s->sr[VGA_SEQ_CLOCK_MODE] &= ~8; /* no double line */ + } else { + shift_control = 2; + /* set chain 4 mode */ + s->sr[VGA_SEQ_MEMORY_MODE] |= VGA_SR04_CHN_4M; + /* activate all planes */ + s->sr[VGA_SEQ_PLANE_WRITE] |= VGA_SR02_ALL_PLANES; + } + s->gr[VGA_GFX_MODE] = (s->gr[VGA_GFX_MODE] & ~0x60) | + (shift_control << 5); + s->cr[VGA_CRTC_MAX_SCAN] &= ~0x9f; /* no double scan */ +} + static uint32_t vbe_ioport_read_index(void *opaque, uint32_t addr) { VGACommonState *s = opaque; @@ -712,13 +769,10 @@ void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val) case VBE_DISPI_INDEX_Y_OFFSET: s->vbe_regs[s->vbe_index] = val; vbe_fixup_regs(s); + vbe_update_vgaregs(s); break; case VBE_DISPI_INDEX_BANK: - if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) { - val &= (s->vbe_bank_mask >> 2); - } else { - val &= s->vbe_bank_mask; - } + val &= s->vbe_bank_mask; s->vbe_regs[s->vbe_index] = val; s->bank_offset = (val << 16); vga_update_memory_access(s); @@ -726,52 +780,19 @@ void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val) case VBE_DISPI_INDEX_ENABLE: if ((val & VBE_DISPI_ENABLED) && !(s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) { - int h, shift_control; s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = 0; s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0; s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0; s->vbe_regs[VBE_DISPI_INDEX_ENABLE] |= VBE_DISPI_ENABLED; vbe_fixup_regs(s); + vbe_update_vgaregs(s); /* clear the screen */ if (!(val & VBE_DISPI_NOCLEARMEM)) { memset(s->vram_ptr, 0, s->vbe_regs[VBE_DISPI_INDEX_YRES] * s->vbe_line_offset); } - - /* we initialize the VGA graphic mode */ - /* graphic mode + memory map 1 */ - s->gr[VGA_GFX_MISC] = (s->gr[VGA_GFX_MISC] & ~0x0c) | 0x04 | - VGA_GR06_GRAPHICS_MODE; - s->cr[VGA_CRTC_MODE] |= 3; /* no CGA modes */ - s->cr[VGA_CRTC_OFFSET] = s->vbe_line_offset >> 3; - /* width */ - s->cr[VGA_CRTC_H_DISP] = - (s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 3) - 1; - /* height (only meaningful if < 1024) */ - h = s->vbe_regs[VBE_DISPI_INDEX_YRES] - 1; - s->cr[VGA_CRTC_V_DISP_END] = h; - s->cr[VGA_CRTC_OVERFLOW] = (s->cr[VGA_CRTC_OVERFLOW] & ~0x42) | - ((h >> 7) & 0x02) | ((h >> 3) & 0x40); - /* line compare to 1023 */ - s->cr[VGA_CRTC_LINE_COMPARE] = 0xff; - s->cr[VGA_CRTC_OVERFLOW] |= 0x10; - s->cr[VGA_CRTC_MAX_SCAN] |= 0x40; - - if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) { - shift_control = 0; - s->sr[VGA_SEQ_CLOCK_MODE] &= ~8; /* no double line */ - } else { - shift_control = 2; - /* set chain 4 mode */ - s->sr[VGA_SEQ_MEMORY_MODE] |= VGA_SR04_CHN_4M; - /* activate all planes */ - s->sr[VGA_SEQ_PLANE_WRITE] |= VGA_SR02_ALL_PLANES; - } - s->gr[VGA_GFX_MODE] = (s->gr[VGA_GFX_MODE] & ~0x60) | - (shift_control << 5); - s->cr[VGA_CRTC_MAX_SCAN] &= ~0x9f; /* no double scan */ } else { s->bank_offset = 0; } @@ -817,13 +838,21 @@ uint32_t vga_mem_readb(VGACommonState *s, hwaddr addr) if (s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) { /* chain 4 mode : simplest access */ + assert(addr < s->vram_size); ret = s->vram_ptr[addr]; } else if (s->gr[VGA_GFX_MODE] & 0x10) { /* odd/even mode (aka text mode mapping) */ plane = (s->gr[VGA_GFX_PLANE_READ] & 2) | (addr & 1); - ret = s->vram_ptr[((addr & ~1) << 1) | plane]; + addr = ((addr & ~1) << 1) | plane; + if (addr >= s->vram_size) { + return 0xff; + } + ret = s->vram_ptr[addr]; } else { /* standard VGA latched access */ + if (addr * sizeof(uint32_t) >= s->vram_size) { + return 0xff; + } s->latch = ((uint32_t *)s->vram_ptr)[addr]; if (!(s->gr[VGA_GFX_MODE] & 0x08)) { @@ -880,6 +909,7 @@ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val) plane = addr & 3; mask = (1 << plane); if (s->sr[VGA_SEQ_PLANE_WRITE] & mask) { + assert(addr < s->vram_size); s->vram_ptr[addr] = val; #ifdef DEBUG_VGA_MEM printf("vga: chain4: [0x" TARGET_FMT_plx "]\n", addr); @@ -893,6 +923,9 @@ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val) mask = (1 << plane); if (s->sr[VGA_SEQ_PLANE_WRITE] & mask) { addr = ((addr & ~1) << 1) | plane; + if (addr >= s->vram_size) { + return; + } s->vram_ptr[addr] = val; #ifdef DEBUG_VGA_MEM printf("vga: odd/even: [0x" TARGET_FMT_plx "]\n", addr); @@ -966,6 +999,9 @@ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val) mask = s->sr[VGA_SEQ_PLANE_WRITE]; s->plane_updated |= mask; /* only used to detect font change */ write_mask = mask16[mask]; + if (addr * sizeof(uint32_t) >= s->vram_size) { + return; + } ((uint32_t *)s->vram_ptr)[addr] = (((uint32_t *)s->vram_ptr)[addr] & ~write_mask) | (val & write_mask); @@ -1044,7 +1080,7 @@ static void vga_get_offsets(VGACommonState *s, { uint32_t start_addr, line_offset, line_compare; - if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) { + if (vbe_enabled(s)) { line_offset = s->vbe_line_offset; start_addr = s->vbe_start_addr; line_compare = 65535; @@ -1369,7 +1405,7 @@ static int vga_get_bpp(VGACommonState *s) { int ret; - if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) { + if (vbe_enabled(s)) { ret = s->vbe_regs[VBE_DISPI_INDEX_BPP]; } else { ret = 0; @@ -1381,7 +1417,7 @@ static void vga_get_resolution(VGACommonState *s, int *pwidth, int *pheight) { int width, height; - if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) { + if (vbe_enabled(s)) { width = s->vbe_regs[VBE_DISPI_INDEX_XRES]; height = s->vbe_regs[VBE_DISPI_INDEX_YRES]; } else { @@ -1979,7 +2015,8 @@ static void vga_update_text(void *opaque, console_ch_t *chardata) width = (s->last_width - size) / 2; dst = chardata + s->last_width + width; for (i = 0; i < size; i ++) - console_write_ch(dst ++, 0x00200100 | msg_buffer[i]); + console_write_ch(dst ++, ATTR2CHTYPE(msg_buffer[i], QEMU_COLOR_BLUE, + QEMU_COLOR_BLACK, 1)); dpy_text_update(s->con, 0, 0, s->last_width, height); } @@ -2139,7 +2176,7 @@ void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate) s->is_vbe_vmstate = 1; memory_region_init_ram(&s->vram, obj, "vga.vram", s->vram_size, - &error_abort); + &error_fatal); vmstate_register_ram(&s->vram, global_vmstate ? NULL : DEVICE(obj)); xen_register_framebuffer(&s->vram); s->vram_ptr = memory_region_get_ram_ptr(&s->vram); diff --git a/qemu/hw/display/vga_int.h b/qemu/hw/display/vga_int.h index 40ba6a420..bdb43a5a3 100644 --- a/qemu/hw/display/vga_int.h +++ b/qemu/hw/display/vga_int.h @@ -25,7 +25,6 @@ #define HW_VGA_INT_H 1 #include -#include "qapi/error.h" #include "exec/memory.h" #define ST01_V_RETRACE 0x08 diff --git a/qemu/hw/display/virtio-gpu-3d.c b/qemu/hw/display/virtio-gpu-3d.c new file mode 100644 index 000000000..fa192946a --- /dev/null +++ b/qemu/hw/display/virtio-gpu-3d.c @@ -0,0 +1,606 @@ +/* + * Virtio GPU Device + * + * Copyright Red Hat, Inc. 2013-2014 + * + * Authors: + * Dave Airlie + * Gerd Hoffmann + * + * 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/iov.h" +#include "trace.h" +#include "hw/virtio/virtio.h" +#include "hw/virtio/virtio-gpu.h" + +#ifdef CONFIG_VIRGL + +#include "virglrenderer.h" + +static struct virgl_renderer_callbacks virtio_gpu_3d_cbs; + +static void virgl_cmd_create_resource_2d(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resource_create_2d c2d; + struct virgl_renderer_resource_create_args args; + + VIRTIO_GPU_FILL_CMD(c2d); + trace_virtio_gpu_cmd_res_create_2d(c2d.resource_id, c2d.format, + c2d.width, c2d.height); + + args.handle = c2d.resource_id; + args.target = 2; + args.format = c2d.format; + args.bind = (1 << 1); + args.width = c2d.width; + args.height = c2d.height; + args.depth = 1; + args.array_size = 1; + args.last_level = 0; + args.nr_samples = 0; + args.flags = VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP; + virgl_renderer_resource_create(&args, NULL, 0); +} + +static void virgl_cmd_create_resource_3d(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resource_create_3d c3d; + struct virgl_renderer_resource_create_args args; + + VIRTIO_GPU_FILL_CMD(c3d); + trace_virtio_gpu_cmd_res_create_3d(c3d.resource_id, c3d.format, + c3d.width, c3d.height, c3d.depth); + + args.handle = c3d.resource_id; + args.target = c3d.target; + args.format = c3d.format; + args.bind = c3d.bind; + args.width = c3d.width; + args.height = c3d.height; + args.depth = c3d.depth; + args.array_size = c3d.array_size; + args.last_level = c3d.last_level; + args.nr_samples = c3d.nr_samples; + args.flags = c3d.flags; + virgl_renderer_resource_create(&args, NULL, 0); +} + +static void virgl_cmd_resource_unref(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resource_unref unref; + + VIRTIO_GPU_FILL_CMD(unref); + trace_virtio_gpu_cmd_res_unref(unref.resource_id); + + virgl_renderer_resource_unref(unref.resource_id); +} + +static void virgl_cmd_context_create(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_ctx_create cc; + + VIRTIO_GPU_FILL_CMD(cc); + trace_virtio_gpu_cmd_ctx_create(cc.hdr.ctx_id, + cc.debug_name); + + virgl_renderer_context_create(cc.hdr.ctx_id, cc.nlen, + cc.debug_name); +} + +static void virgl_cmd_context_destroy(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_ctx_destroy cd; + + VIRTIO_GPU_FILL_CMD(cd); + trace_virtio_gpu_cmd_ctx_destroy(cd.hdr.ctx_id); + + virgl_renderer_context_destroy(cd.hdr.ctx_id); +} + +static void virtio_gpu_rect_update(VirtIOGPU *g, int idx, int x, int y, + int width, int height) +{ + if (!g->scanout[idx].con) { + return; + } + + dpy_gl_update(g->scanout[idx].con, x, y, width, height); +} + +static void virgl_cmd_resource_flush(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resource_flush rf; + int i; + + VIRTIO_GPU_FILL_CMD(rf); + trace_virtio_gpu_cmd_res_flush(rf.resource_id, + rf.r.width, rf.r.height, rf.r.x, rf.r.y); + + for (i = 0; i < VIRTIO_GPU_MAX_SCANOUT; i++) { + if (g->scanout[i].resource_id != rf.resource_id) { + continue; + } + virtio_gpu_rect_update(g, i, rf.r.x, rf.r.y, rf.r.width, rf.r.height); + } +} + +static void virgl_cmd_set_scanout(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_set_scanout ss; + struct virgl_renderer_resource_info info; + int ret; + + VIRTIO_GPU_FILL_CMD(ss); + trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id, + ss.r.width, ss.r.height, ss.r.x, ss.r.y); + + if (ss.scanout_id >= VIRTIO_GPU_MAX_SCANOUT) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d", + __func__, ss.scanout_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; + return; + } + g->enable = 1; + + memset(&info, 0, sizeof(info)); + + if (ss.resource_id && ss.r.width && ss.r.height) { + ret = virgl_renderer_resource_get_info(ss.resource_id, &info); + if (ret == -1) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: illegal resource specified %d\n", + __func__, ss.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + qemu_console_resize(g->scanout[ss.scanout_id].con, + ss.r.width, ss.r.height); + virgl_renderer_force_ctx_0(); + dpy_gl_scanout(g->scanout[ss.scanout_id].con, info.tex_id, + info.flags & 1 /* FIXME: Y_0_TOP */, + ss.r.x, ss.r.y, ss.r.width, ss.r.height); + } else { + if (ss.scanout_id != 0) { + dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL); + } + dpy_gl_scanout(g->scanout[ss.scanout_id].con, 0, false, + 0, 0, 0, 0); + } + g->scanout[ss.scanout_id].resource_id = ss.resource_id; +} + +static void virgl_cmd_submit_3d(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_cmd_submit cs; + void *buf; + size_t s; + + VIRTIO_GPU_FILL_CMD(cs); + trace_virtio_gpu_cmd_ctx_submit(cs.hdr.ctx_id, cs.size); + + buf = g_malloc(cs.size); + s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, + sizeof(cs), buf, cs.size); + if (s != cs.size) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: size mismatch (%zd/%d)", + __func__, s, cs.size); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + goto out; + } + + if (virtio_gpu_stats_enabled(g->conf)) { + g->stats.req_3d++; + g->stats.bytes_3d += cs.size; + } + + virgl_renderer_submit_cmd(buf, cs.hdr.ctx_id, cs.size / 4); + +out: + g_free(buf); +} + +static void virgl_cmd_transfer_to_host_2d(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_transfer_to_host_2d t2d; + struct virtio_gpu_box box; + + VIRTIO_GPU_FILL_CMD(t2d); + trace_virtio_gpu_cmd_res_xfer_toh_2d(t2d.resource_id); + + box.x = t2d.r.x; + box.y = t2d.r.y; + box.z = 0; + box.w = t2d.r.width; + box.h = t2d.r.height; + box.d = 1; + + virgl_renderer_transfer_write_iov(t2d.resource_id, + 0, + 0, + 0, + 0, + (struct virgl_box *)&box, + t2d.offset, NULL, 0); +} + +static void virgl_cmd_transfer_to_host_3d(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_transfer_host_3d t3d; + + VIRTIO_GPU_FILL_CMD(t3d); + trace_virtio_gpu_cmd_res_xfer_toh_3d(t3d.resource_id); + + virgl_renderer_transfer_write_iov(t3d.resource_id, + t3d.hdr.ctx_id, + t3d.level, + t3d.stride, + t3d.layer_stride, + (struct virgl_box *)&t3d.box, + t3d.offset, NULL, 0); +} + +static void +virgl_cmd_transfer_from_host_3d(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_transfer_host_3d tf3d; + + VIRTIO_GPU_FILL_CMD(tf3d); + trace_virtio_gpu_cmd_res_xfer_fromh_3d(tf3d.resource_id); + + virgl_renderer_transfer_read_iov(tf3d.resource_id, + tf3d.hdr.ctx_id, + tf3d.level, + tf3d.stride, + tf3d.layer_stride, + (struct virgl_box *)&tf3d.box, + tf3d.offset, NULL, 0); +} + + +static void virgl_resource_attach_backing(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resource_attach_backing att_rb; + struct iovec *res_iovs; + int ret; + + VIRTIO_GPU_FILL_CMD(att_rb); + trace_virtio_gpu_cmd_res_back_attach(att_rb.resource_id); + + ret = virtio_gpu_create_mapping_iov(&att_rb, cmd, &res_iovs); + if (ret != 0) { + cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; + return; + } + + virgl_renderer_resource_attach_iov(att_rb.resource_id, + res_iovs, att_rb.nr_entries); +} + +static void virgl_resource_detach_backing(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resource_detach_backing detach_rb; + struct iovec *res_iovs = NULL; + int num_iovs = 0; + + VIRTIO_GPU_FILL_CMD(detach_rb); + trace_virtio_gpu_cmd_res_back_detach(detach_rb.resource_id); + + virgl_renderer_resource_detach_iov(detach_rb.resource_id, + &res_iovs, + &num_iovs); + if (res_iovs == NULL || num_iovs == 0) { + return; + } + virtio_gpu_cleanup_mapping_iov(res_iovs, num_iovs); +} + + +static void virgl_cmd_ctx_attach_resource(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_ctx_resource att_res; + + VIRTIO_GPU_FILL_CMD(att_res); + trace_virtio_gpu_cmd_ctx_res_attach(att_res.hdr.ctx_id, + att_res.resource_id); + + virgl_renderer_ctx_attach_resource(att_res.hdr.ctx_id, att_res.resource_id); +} + +static void virgl_cmd_ctx_detach_resource(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_ctx_resource det_res; + + VIRTIO_GPU_FILL_CMD(det_res); + trace_virtio_gpu_cmd_ctx_res_detach(det_res.hdr.ctx_id, + det_res.resource_id); + + virgl_renderer_ctx_detach_resource(det_res.hdr.ctx_id, det_res.resource_id); +} + +static void virgl_cmd_get_capset_info(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_get_capset_info info; + struct virtio_gpu_resp_capset_info resp; + + VIRTIO_GPU_FILL_CMD(info); + + if (info.capset_index == 0) { + resp.capset_id = VIRTIO_GPU_CAPSET_VIRGL; + virgl_renderer_get_cap_set(resp.capset_id, + &resp.capset_max_version, + &resp.capset_max_size); + } else { + resp.capset_max_version = 0; + resp.capset_max_size = 0; + } + resp.hdr.type = VIRTIO_GPU_RESP_OK_CAPSET_INFO; + virtio_gpu_ctrl_response(g, cmd, &resp.hdr, sizeof(resp)); +} + +static void virgl_cmd_get_capset(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_get_capset gc; + struct virtio_gpu_resp_capset *resp; + uint32_t max_ver, max_size; + VIRTIO_GPU_FILL_CMD(gc); + + virgl_renderer_get_cap_set(gc.capset_id, &max_ver, + &max_size); + resp = g_malloc(sizeof(*resp) + max_size); + + resp->hdr.type = VIRTIO_GPU_RESP_OK_CAPSET; + virgl_renderer_fill_caps(gc.capset_id, + gc.capset_version, + (void *)resp->capset_data); + virtio_gpu_ctrl_response(g, cmd, &resp->hdr, sizeof(*resp) + max_size); + g_free(resp); +} + +void virtio_gpu_virgl_process_cmd(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + VIRTIO_GPU_FILL_CMD(cmd->cmd_hdr); + + cmd->waiting = g->renderer_blocked; + if (cmd->waiting) { + return; + } + + virgl_renderer_force_ctx_0(); + switch (cmd->cmd_hdr.type) { + case VIRTIO_GPU_CMD_CTX_CREATE: + virgl_cmd_context_create(g, cmd); + break; + case VIRTIO_GPU_CMD_CTX_DESTROY: + virgl_cmd_context_destroy(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: + virgl_cmd_create_resource_2d(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_CREATE_3D: + virgl_cmd_create_resource_3d(g, cmd); + break; + case VIRTIO_GPU_CMD_SUBMIT_3D: + virgl_cmd_submit_3d(g, cmd); + break; + case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: + virgl_cmd_transfer_to_host_2d(g, cmd); + break; + case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D: + virgl_cmd_transfer_to_host_3d(g, cmd); + break; + case VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D: + virgl_cmd_transfer_from_host_3d(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: + virgl_resource_attach_backing(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: + virgl_resource_detach_backing(g, cmd); + break; + case VIRTIO_GPU_CMD_SET_SCANOUT: + virgl_cmd_set_scanout(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_FLUSH: + virgl_cmd_resource_flush(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_UNREF: + virgl_cmd_resource_unref(g, cmd); + break; + case VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE: + /* TODO add security */ + virgl_cmd_ctx_attach_resource(g, cmd); + break; + case VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE: + /* TODO add security */ + virgl_cmd_ctx_detach_resource(g, cmd); + break; + case VIRTIO_GPU_CMD_GET_CAPSET_INFO: + virgl_cmd_get_capset_info(g, cmd); + break; + case VIRTIO_GPU_CMD_GET_CAPSET: + virgl_cmd_get_capset(g, cmd); + break; + + case VIRTIO_GPU_CMD_GET_DISPLAY_INFO: + virtio_gpu_get_display_info(g, cmd); + break; + default: + cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; + break; + } + + if (cmd->finished) { + return; + } + if (cmd->error) { + fprintf(stderr, "%s: ctrl 0x%x, error 0x%x\n", __func__, + cmd->cmd_hdr.type, cmd->error); + virtio_gpu_ctrl_response_nodata(g, cmd, cmd->error); + return; + } + if (!(cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE)) { + virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA); + return; + } + + trace_virtio_gpu_fence_ctrl(cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type); + virgl_renderer_create_fence(cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type); +} + +static void virgl_write_fence(void *opaque, uint32_t fence) +{ + VirtIOGPU *g = opaque; + struct virtio_gpu_ctrl_command *cmd, *tmp; + + QTAILQ_FOREACH_SAFE(cmd, &g->fenceq, next, tmp) { + /* + * the guest can end up emitting fences out of order + * so we should check all fenced cmds not just the first one. + */ + if (cmd->cmd_hdr.fence_id > fence) { + continue; + } + trace_virtio_gpu_fence_resp(cmd->cmd_hdr.fence_id); + virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA); + QTAILQ_REMOVE(&g->fenceq, cmd, next); + g_free(cmd); + g->inflight--; + if (virtio_gpu_stats_enabled(g->conf)) { + fprintf(stderr, "inflight: %3d (-)\r", g->inflight); + } + } +} + +static virgl_renderer_gl_context +virgl_create_context(void *opaque, int scanout_idx, + struct virgl_renderer_gl_ctx_param *params) +{ + VirtIOGPU *g = opaque; + QEMUGLContext ctx; + QEMUGLParams qparams; + + qparams.major_ver = params->major_ver; + qparams.minor_ver = params->minor_ver; + + ctx = dpy_gl_ctx_create(g->scanout[scanout_idx].con, &qparams); + return (virgl_renderer_gl_context)ctx; +} + +static void virgl_destroy_context(void *opaque, virgl_renderer_gl_context ctx) +{ + VirtIOGPU *g = opaque; + QEMUGLContext qctx = (QEMUGLContext)ctx; + + dpy_gl_ctx_destroy(g->scanout[0].con, qctx); +} + +static int virgl_make_context_current(void *opaque, int scanout_idx, + virgl_renderer_gl_context ctx) +{ + VirtIOGPU *g = opaque; + QEMUGLContext qctx = (QEMUGLContext)ctx; + + return dpy_gl_ctx_make_current(g->scanout[scanout_idx].con, qctx); +} + +static struct virgl_renderer_callbacks virtio_gpu_3d_cbs = { + .version = 1, + .write_fence = virgl_write_fence, + .create_gl_context = virgl_create_context, + .destroy_gl_context = virgl_destroy_context, + .make_current = virgl_make_context_current, +}; + +static void virtio_gpu_print_stats(void *opaque) +{ + VirtIOGPU *g = opaque; + + if (g->stats.requests) { + fprintf(stderr, "stats: vq req %4d, %3d -- 3D %4d (%5d)\n", + g->stats.requests, + g->stats.max_inflight, + g->stats.req_3d, + g->stats.bytes_3d); + g->stats.requests = 0; + g->stats.max_inflight = 0; + g->stats.req_3d = 0; + g->stats.bytes_3d = 0; + } else { + fprintf(stderr, "stats: idle\r"); + } + timer_mod(g->print_stats, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000); +} + +static void virtio_gpu_fence_poll(void *opaque) +{ + VirtIOGPU *g = opaque; + + virgl_renderer_poll(); + virtio_gpu_process_cmdq(g); + if (!QTAILQ_EMPTY(&g->cmdq) || !QTAILQ_EMPTY(&g->fenceq)) { + timer_mod(g->fence_poll, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 10); + } +} + +void virtio_gpu_virgl_fence_poll(VirtIOGPU *g) +{ + virtio_gpu_fence_poll(g); +} + +void virtio_gpu_virgl_reset(VirtIOGPU *g) +{ + int i; + + /* virgl_renderer_reset() ??? */ + for (i = 0; i < g->conf.max_outputs; i++) { + if (i != 0) { + dpy_gfx_replace_surface(g->scanout[i].con, NULL); + } + dpy_gl_scanout(g->scanout[i].con, 0, false, 0, 0, 0, 0); + } +} + +int virtio_gpu_virgl_init(VirtIOGPU *g) +{ + int ret; + + ret = virgl_renderer_init(g, 0, &virtio_gpu_3d_cbs); + if (ret != 0) { + return ret; + } + + g->fence_poll = timer_new_ms(QEMU_CLOCK_VIRTUAL, + virtio_gpu_fence_poll, g); + + if (virtio_gpu_stats_enabled(g->conf)) { + g->print_stats = timer_new_ms(QEMU_CLOCK_VIRTUAL, + virtio_gpu_print_stats, g); + timer_mod(g->print_stats, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000); + } + return 0; +} + +#endif /* CONFIG_VIRGL */ diff --git a/qemu/hw/display/virtio-gpu-pci.c b/qemu/hw/display/virtio-gpu-pci.c index 5bc62cf34..a71b230d3 100644 --- a/qemu/hw/display/virtio-gpu-pci.c +++ b/qemu/hw/display/virtio-gpu-pci.c @@ -6,10 +6,11 @@ * Authors: * Dave Airlie * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. * */ +#include "qemu/osdep.h" #include "hw/pci/pci.h" #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-bus.h" diff --git a/qemu/hw/display/virtio-gpu.c b/qemu/hw/display/virtio-gpu.c index a67d927f5..c181fb364 100644 --- a/qemu/hw/display/virtio-gpu.c +++ b/qemu/hw/display/virtio-gpu.c @@ -7,10 +7,11 @@ * Dave Airlie * Gerd Hoffmann * - * This work is licensed under the terms of the GNU GPL, version 2. + * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/iov.h" #include "ui/console.h" @@ -22,6 +23,23 @@ static struct virtio_gpu_simple_resource* virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id); +#ifdef CONFIG_VIRGL +#include "virglrenderer.h" +#define VIRGL(_g, _virgl, _simple, ...) \ + do { \ + if (_g->use_virgl_renderer) { \ + _virgl(__VA_ARGS__); \ + } else { \ + _simple(__VA_ARGS__); \ + } \ + } while (0) +#else +#define VIRGL(_g, _virgl, _simple, ...) \ + do { \ + _simple(__VA_ARGS__); \ + } while (0) +#endif + static void update_cursor_data_simple(VirtIOGPU *g, struct virtio_gpu_scanout *s, uint32_t resource_id) @@ -45,16 +63,49 @@ static void update_cursor_data_simple(VirtIOGPU *g, pixels * sizeof(uint32_t)); } +#ifdef CONFIG_VIRGL + +static void update_cursor_data_virgl(VirtIOGPU *g, + struct virtio_gpu_scanout *s, + uint32_t resource_id) +{ + uint32_t width, height; + uint32_t pixels, *data; + + data = virgl_renderer_get_cursor_data(resource_id, &width, &height); + if (!data) { + return; + } + + if (width != s->current_cursor->width || + height != s->current_cursor->height) { + return; + } + + pixels = s->current_cursor->width * s->current_cursor->height; + memcpy(s->current_cursor->data, data, pixels * sizeof(uint32_t)); + free(data); +} + +#endif + static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor) { struct virtio_gpu_scanout *s; + bool move = cursor->hdr.type != VIRTIO_GPU_CMD_MOVE_CURSOR; if (cursor->pos.scanout_id >= g->conf.max_outputs) { return; } s = &g->scanout[cursor->pos.scanout_id]; - if (cursor->hdr.type != VIRTIO_GPU_CMD_MOVE_CURSOR) { + trace_virtio_gpu_update_cursor(cursor->pos.scanout_id, + cursor->pos.x, + cursor->pos.y, + move ? "move" : "update", + cursor->resource_id); + + if (move) { if (!s->current_cursor) { s->current_cursor = cursor_alloc(64, 64); } @@ -63,7 +114,8 @@ static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor) s->current_cursor->hot_y = cursor->hot_y; if (cursor->resource_id > 0) { - update_cursor_data_simple(g, s, cursor->resource_id); + VIRGL(g, update_cursor_data_virgl, update_cursor_data_simple, + g, s, cursor->resource_id); } dpy_cursor_define(s->con, s->current_cursor); } @@ -92,9 +144,23 @@ static void virtio_gpu_set_config(VirtIODevice *vdev, const uint8_t *config) static uint64_t virtio_gpu_get_features(VirtIODevice *vdev, uint64_t features, Error **errp) { + VirtIOGPU *g = VIRTIO_GPU(vdev); + + if (virtio_gpu_virgl_enabled(g->conf)) { + features |= (1 << VIRTIO_GPU_F_VIRGL); + } return features; } +static void virtio_gpu_set_features(VirtIODevice *vdev, uint64_t features) +{ + static const uint32_t virgl = (1 << VIRTIO_GPU_F_VIRGL); + VirtIOGPU *g = VIRTIO_GPU(vdev); + + g->use_virgl_renderer = ((features & virgl) == virgl); + trace_virtio_gpu_features(g->use_virgl_renderer); +} + static void virtio_gpu_notify_event(VirtIOGPU *g, uint32_t event_type) { g->virtio_config.events_read |= event_type; @@ -506,10 +572,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, scanout->width != ss.r.width || scanout->height != ss.r.height) { /* realloc the surface ptr */ - scanout->ds = qemu_create_displaysurface_from - (ss.r.width, ss.r.height, format, - pixman_image_get_stride(res->image), - (uint8_t *)pixman_image_get_data(res->image) + offset); + scanout->ds = qemu_create_displaysurface_pixman(res->image); if (!scanout->ds) { cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; return; @@ -563,7 +626,6 @@ int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab, __func__, ab->resource_id, i); virtio_gpu_cleanup_mapping_iov(*iov, i); g_free(ents); - g_free(*iov); *iov = NULL; return -1; } @@ -580,12 +642,12 @@ void virtio_gpu_cleanup_mapping_iov(struct iovec *iov, uint32_t count) cpu_physical_memory_unmap(iov[i].iov_base, iov[i].iov_len, 1, iov[i].iov_len); } + g_free(iov); } static void virtio_gpu_cleanup_mapping(struct virtio_gpu_simple_resource *res) { virtio_gpu_cleanup_mapping_iov(res->iov, res->iov_cnt); - g_free(res->iov); res->iov = NULL; res->iov_cnt = 0; } @@ -690,6 +752,39 @@ static void virtio_gpu_handle_cursor_cb(VirtIODevice *vdev, VirtQueue *vq) qemu_bh_schedule(g->cursor_bh); } +void virtio_gpu_process_cmdq(VirtIOGPU *g) +{ + struct virtio_gpu_ctrl_command *cmd; + + while (!QTAILQ_EMPTY(&g->cmdq)) { + cmd = QTAILQ_FIRST(&g->cmdq); + + /* process command */ + VIRGL(g, virtio_gpu_virgl_process_cmd, virtio_gpu_simple_process_cmd, + g, cmd); + if (cmd->waiting) { + break; + } + QTAILQ_REMOVE(&g->cmdq, cmd, next); + if (virtio_gpu_stats_enabled(g->conf)) { + g->stats.requests++; + } + + if (!cmd->finished) { + QTAILQ_INSERT_TAIL(&g->fenceq, cmd, next); + g->inflight++; + if (virtio_gpu_stats_enabled(g->conf)) { + if (g->stats.max_inflight < g->inflight) { + g->stats.max_inflight = g->inflight; + } + fprintf(stderr, "inflight: %3d (+)\r", g->inflight); + } + } else { + g_free(cmd); + } + } +} + static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) { VirtIOGPU *g = VIRTIO_GPU(vdev); @@ -699,25 +794,30 @@ static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) return; } - cmd = g_new(struct virtio_gpu_ctrl_command, 1); - while (virtqueue_pop(vq, &cmd->elem)) { +#ifdef CONFIG_VIRGL + if (!g->renderer_inited && g->use_virgl_renderer) { + virtio_gpu_virgl_init(g); + g->renderer_inited = true; + } +#endif + + cmd = virtqueue_pop(vq, sizeof(struct virtio_gpu_ctrl_command)); + while (cmd) { cmd->vq = vq; cmd->error = 0; cmd->finished = false; - g->stats.requests++; + cmd->waiting = false; + QTAILQ_INSERT_TAIL(&g->cmdq, cmd, next); + cmd = virtqueue_pop(vq, sizeof(struct virtio_gpu_ctrl_command)); + } - virtio_gpu_simple_process_cmd(g, cmd); - if (!cmd->finished) { - QTAILQ_INSERT_TAIL(&g->fenceq, cmd, next); - g->stats.inflight++; - if (g->stats.max_inflight < g->stats.inflight) { - g->stats.max_inflight = g->stats.inflight; - } - fprintf(stderr, "inflight: %3d (+)\r", g->stats.inflight); - cmd = g_new(struct virtio_gpu_ctrl_command, 1); - } + virtio_gpu_process_cmdq(g); + +#ifdef CONFIG_VIRGL + if (g->use_virgl_renderer) { + virtio_gpu_virgl_fence_poll(g); } - g_free(cmd); +#endif } static void virtio_gpu_ctrl_bh(void *opaque) @@ -729,15 +829,20 @@ static void virtio_gpu_ctrl_bh(void *opaque) static void virtio_gpu_handle_cursor(VirtIODevice *vdev, VirtQueue *vq) { VirtIOGPU *g = VIRTIO_GPU(vdev); - VirtQueueElement elem; + VirtQueueElement *elem; size_t s; struct virtio_gpu_update_cursor cursor_info; if (!virtio_queue_ready(vq)) { return; } - while (virtqueue_pop(vq, &elem)) { - s = iov_to_buf(elem.out_sg, elem.out_num, 0, + for (;;) { + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!elem) { + break; + } + + s = iov_to_buf(elem->out_sg, elem->out_num, 0, &cursor_info, sizeof(cursor_info)); if (s != sizeof(cursor_info)) { qemu_log_mask(LOG_GUEST_ERROR, @@ -746,8 +851,9 @@ static void virtio_gpu_handle_cursor(VirtIODevice *vdev, VirtQueue *vq) } else { update_cursor(g, &cursor_info); } - virtqueue_push(vq, &elem, 0); + virtqueue_push(vq, elem, 0); virtio_notify(vdev, vq); + g_free(elem); } } @@ -793,17 +899,34 @@ static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) return 0; } +static void virtio_gpu_gl_block(void *opaque, bool block) +{ + VirtIOGPU *g = opaque; + + g->renderer_blocked = block; + if (!block) { + virtio_gpu_process_cmdq(g); + } +} + const GraphicHwOps virtio_gpu_ops = { .invalidate = virtio_gpu_invalidate_display, .gfx_update = virtio_gpu_update_display, .text_update = virtio_gpu_text_update, .ui_info = virtio_gpu_ui_info, + .gl_block = virtio_gpu_gl_block, +}; + +static const VMStateDescription vmstate_virtio_gpu_unmigratable = { + .name = "virtio-gpu", + .unmigratable = 1, }; static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(qdev); VirtIOGPU *g = VIRTIO_GPU(qdev); + bool have_virgl; int i; g->config_size = sizeof(struct virtio_gpu_config); @@ -814,12 +937,30 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) g->req_state[0].width = 1024; g->req_state[0].height = 768; - g->ctrl_vq = virtio_add_queue(vdev, 64, virtio_gpu_handle_ctrl_cb); - g->cursor_vq = virtio_add_queue(vdev, 16, virtio_gpu_handle_cursor_cb); + g->use_virgl_renderer = false; +#if !defined(CONFIG_VIRGL) || defined(HOST_WORDS_BIGENDIAN) + have_virgl = false; +#else + have_virgl = display_opengl; +#endif + if (!have_virgl) { + g->conf.flags &= ~(1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED); + } + + if (virtio_gpu_virgl_enabled(g->conf)) { + /* use larger control queue in 3d mode */ + g->ctrl_vq = virtio_add_queue(vdev, 256, virtio_gpu_handle_ctrl_cb); + g->cursor_vq = virtio_add_queue(vdev, 16, virtio_gpu_handle_cursor_cb); + g->virtio_config.num_capsets = 1; + } else { + g->ctrl_vq = virtio_add_queue(vdev, 64, virtio_gpu_handle_ctrl_cb); + g->cursor_vq = virtio_add_queue(vdev, 16, virtio_gpu_handle_cursor_cb); + } g->ctrl_bh = qemu_bh_new(virtio_gpu_ctrl_bh, g); g->cursor_bh = qemu_bh_new(virtio_gpu_cursor_bh, g); QTAILQ_INIT(&g->reslist); + QTAILQ_INIT(&g->cmdq); QTAILQ_INIT(&g->fenceq); g->enabled_output_bitmask = 1; @@ -832,6 +973,8 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) dpy_gfx_replace_surface(g->scanout[i].con, NULL); } } + + vmstate_register(qdev, -1, &vmstate_virtio_gpu_unmigratable, g); } static void virtio_gpu_instance_init(Object *obj) @@ -869,10 +1012,23 @@ static void virtio_gpu_reset(VirtIODevice *vdev) g->scanout[i].ds = NULL; } g->enabled_output_bitmask = 1; + +#ifdef CONFIG_VIRGL + if (g->use_virgl_renderer) { + virtio_gpu_virgl_reset(g); + g->use_virgl_renderer = 0; + } +#endif } static Property virtio_gpu_properties[] = { DEFINE_PROP_UINT32("max_outputs", VirtIOGPU, conf.max_outputs, 1), +#ifdef CONFIG_VIRGL + DEFINE_PROP_BIT("virgl", VirtIOGPU, conf.flags, + VIRTIO_GPU_FLAG_VIRGL_ENABLED, true), + DEFINE_PROP_BIT("stats", VirtIOGPU, conf.flags, + VIRTIO_GPU_FLAG_STATS_ENABLED, false), +#endif DEFINE_PROP_END_OF_LIST(), }; @@ -885,6 +1041,7 @@ static void virtio_gpu_class_init(ObjectClass *klass, void *data) vdc->get_config = virtio_gpu_get_config; vdc->set_config = virtio_gpu_set_config; vdc->get_features = virtio_gpu_get_features; + vdc->set_features = virtio_gpu_set_features; vdc->reset = virtio_gpu_reset; @@ -917,3 +1074,14 @@ QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_mem_entry) != 16); QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_attach_backing) != 32); QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_detach_backing) != 32); QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_display_info) != 408); + +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_host_3d) != 72); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_3d) != 72); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_create) != 96); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_destroy) != 24); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_resource) != 32); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_cmd_submit) != 32); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset_info) != 32); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset_info) != 40); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset) != 32); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset) != 24); diff --git a/qemu/hw/display/virtio-vga.c b/qemu/hw/display/virtio-vga.c index f7e539fe9..e58b165ae 100644 --- a/qemu/hw/display/virtio-vga.c +++ b/qemu/hw/display/virtio-vga.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "ui/console.h" @@ -65,11 +66,21 @@ static int virtio_vga_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) return -1; } +static void virtio_vga_gl_block(void *opaque, bool block) +{ + VirtIOVGA *vvga = opaque; + + if (virtio_gpu_ops.gl_block) { + virtio_gpu_ops.gl_block(&vvga->vdev, block); + } +} + static const GraphicHwOps virtio_vga_ops = { .invalidate = virtio_vga_invalidate_display, .gfx_update = virtio_vga_update_display, .text_update = virtio_vga_text_update, .ui_info = virtio_vga_ui_info, + .gl_block = virtio_vga_gl_block, }; /* VGA device wrapper around PCI device around virtio GPU */ diff --git a/qemu/hw/display/vmware_vga.c b/qemu/hw/display/vmware_vga.c index 7f397d3c2..0c63fa851 100644 --- a/qemu/hw/display/vmware_vga.c +++ b/qemu/hw/display/vmware_vga.c @@ -21,6 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "hw/loader.h" #include "trace.h" @@ -488,10 +490,10 @@ static inline int vmsvga_fill_rect(struct vmsvga_state_s *s, #endif struct vmsvga_cursor_definition_s { - int width; - int height; + uint32_t width; + uint32_t height; int id; - int bpp; + uint32_t bpp; int hot_x; int hot_y; uint32_t mask[1024]; @@ -658,7 +660,10 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s) cursor.bpp = vmsvga_fifo_read(s); args = SVGA_BITMAP_SIZE(x, y) + SVGA_PIXMAP_SIZE(x, y, cursor.bpp); - if (SVGA_BITMAP_SIZE(x, y) > sizeof cursor.mask || + if (cursor.width > 256 || + cursor.height > 256 || + cursor.bpp > 32 || + SVGA_BITMAP_SIZE(x, y) > sizeof cursor.mask || SVGA_PIXMAP_SIZE(x, y, cursor.bpp) > sizeof cursor.image) { goto badcmd; } @@ -1244,7 +1249,7 @@ static void vmsvga_init(DeviceState *dev, struct vmsvga_state_s *s, s->fifo_size = SVGA_FIFO_SIZE; memory_region_init_ram(&s->fifo_ram, NULL, "vmsvga.fifo", s->fifo_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(&s->fifo_ram); s->fifo_ptr = memory_region_get_ram_ptr(&s->fifo_ram); diff --git a/qemu/hw/display/xenfb.c b/qemu/hw/display/xenfb.c index 5e324ef62..9866dfda5 100644 --- a/qemu/hw/display/xenfb.c +++ b/qemu/hw/display/xenfb.c @@ -24,16 +24,8 @@ * with this program; if not, see . */ -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include -#include -#include -#include -#include #include "hw/hw.h" #include "ui/console.h" @@ -95,23 +87,24 @@ struct XenFB { static int common_bind(struct common *c) { - uint64_t mfn; + uint64_t val; + xen_pfn_t mfn; - if (xenstore_read_fe_uint64(&c->xendev, "page-ref", &mfn) == -1) + if (xenstore_read_fe_uint64(&c->xendev, "page-ref", &val) == -1) return -1; - assert(mfn == (xen_pfn_t)mfn); + mfn = (xen_pfn_t)val; + assert(val == mfn); if (xenstore_read_fe_int(&c->xendev, "event-channel", &c->xendev.remote_port) == -1) return -1; - c->page = xc_map_foreign_range(xen_xc, c->xendev.dom, - XC_PAGE_SIZE, - PROT_READ | PROT_WRITE, mfn); + c->page = xenforeignmemory_map(xen_fmem, c->xendev.dom, + PROT_READ | PROT_WRITE, 1, &mfn, NULL); if (c->page == NULL) return -1; xen_be_bind_evtchn(&c->xendev); - xen_be_printf(&c->xendev, 1, "ring mfn %"PRIx64", remote-port %d, local-port %d\n", + xen_be_printf(&c->xendev, 1, "ring mfn %"PRI_xen_pfn", remote-port %d, local-port %d\n", mfn, c->xendev.remote_port, c->xendev.local_port); return 0; @@ -121,7 +114,7 @@ static void common_unbind(struct common *c) { xen_be_unbind_evtchn(&c->xendev); if (c->page) { - munmap(c->page, XC_PAGE_SIZE); + xenforeignmemory_unmap(xen_fmem, c->page, 1); c->page = NULL; } } @@ -248,9 +241,7 @@ static int xenfb_send_motion(struct XenInput *xenfb, event.type = XENKBD_TYPE_MOTION; event.motion.rel_x = rel_x; event.motion.rel_y = rel_y; -#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030207 event.motion.rel_z = rel_z; -#endif return xenfb_kbd_event(xenfb, &event); } @@ -265,12 +256,7 @@ static int xenfb_send_position(struct XenInput *xenfb, event.type = XENKBD_TYPE_POS; event.pos.abs_x = abs_x; event.pos.abs_y = abs_y; -#if __XEN_LATEST_INTERFACE_VERSION__ == 0x00030207 - event.pos.abs_z = z; -#endif -#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030208 event.pos.rel_z = z; -#endif return xenfb_kbd_event(xenfb, &event); } @@ -494,15 +480,15 @@ static int xenfb_map_fb(struct XenFB *xenfb) fbmfns = g_malloc0(sizeof(xen_pfn_t) * xenfb->fbpages); xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd); - map = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom, - PROT_READ, pgmfns, n_fbdirs); + map = xenforeignmemory_map(xen_fmem, xenfb->c.xendev.dom, + PROT_READ, n_fbdirs, pgmfns, NULL); if (map == NULL) goto out; xenfb_copy_mfns(mode, xenfb->fbpages, fbmfns, map); - munmap(map, n_fbdirs * XC_PAGE_SIZE); + xenforeignmemory_unmap(xen_fmem, map, n_fbdirs); - xenfb->pixels = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom, - PROT_READ, fbmfns, xenfb->fbpages); + xenfb->pixels = xenforeignmemory_map(xen_fmem, xenfb->c.xendev.dom, + PROT_READ, xenfb->fbpages, fbmfns, NULL); if (xenfb->pixels == NULL) goto out; @@ -784,18 +770,21 @@ static void xenfb_invalidate(void *opaque) static void xenfb_handle_events(struct XenFB *xenfb) { - uint32_t prod, cons; + uint32_t prod, cons, out_cons; struct xenfb_page *page = xenfb->c.page; prod = page->out_prod; - if (prod == page->out_cons) - return; + out_cons = page->out_cons; + if (prod - out_cons > XENFB_OUT_RING_LEN) { + return; + } xen_rmb(); /* ensure we see ring contents up to prod */ - for (cons = page->out_cons; cons != prod; cons++) { + for (cons = out_cons; cons != prod; cons++) { union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons); + uint8_t type = event->type; int x, y, w, h; - switch (event->type) { + switch (type) { case XENFB_TYPE_UPDATE: if (xenfb->up_count == UP_QUEUE) xenfb->up_fullscreen = 1; @@ -909,6 +898,7 @@ static void fb_disconnect(struct XenDevice *xendev) * Replacing the framebuffer with anonymous shared memory * instead. This releases the guest pages and keeps qemu happy. */ + xenforeignmemory_unmap(xen_fmem, fb->pixels, fb->fbpages); fb->pixels = mmap(fb->pixels, fb->fbpages * XC_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); diff --git a/qemu/hw/dma/Makefile.objs b/qemu/hw/dma/Makefile.objs index 0e65ed0d7..a1abbcf74 100644 --- a/qemu/hw/dma/Makefile.objs +++ b/qemu/hw/dma/Makefile.objs @@ -11,3 +11,4 @@ common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o +obj-$(CONFIG_RASPI) += bcm2835_dma.o diff --git a/qemu/hw/dma/bcm2835_dma.c b/qemu/hw/dma/bcm2835_dma.c new file mode 100644 index 000000000..542117599 --- /dev/null +++ b/qemu/hw/dma/bcm2835_dma.c @@ -0,0 +1,409 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/dma/bcm2835_dma.h" + +/* DMA CS Control and Status bits */ +#define BCM2708_DMA_ACTIVE (1 << 0) +#define BCM2708_DMA_END (1 << 1) /* GE */ +#define BCM2708_DMA_INT (1 << 2) +#define BCM2708_DMA_ISPAUSED (1 << 4) /* Pause requested or not active */ +#define BCM2708_DMA_ISHELD (1 << 5) /* Is held by DREQ flow control */ +#define BCM2708_DMA_ERR (1 << 8) +#define BCM2708_DMA_ABORT (1 << 30) /* stop current CB, go to next, WO */ +#define BCM2708_DMA_RESET (1 << 31) /* WO, self clearing */ + +/* DMA control block "info" field bits */ +#define BCM2708_DMA_INT_EN (1 << 0) +#define BCM2708_DMA_TDMODE (1 << 1) +#define BCM2708_DMA_WAIT_RESP (1 << 3) +#define BCM2708_DMA_D_INC (1 << 4) +#define BCM2708_DMA_D_WIDTH (1 << 5) +#define BCM2708_DMA_D_DREQ (1 << 6) +#define BCM2708_DMA_D_IGNORE (1 << 7) +#define BCM2708_DMA_S_INC (1 << 8) +#define BCM2708_DMA_S_WIDTH (1 << 9) +#define BCM2708_DMA_S_DREQ (1 << 10) +#define BCM2708_DMA_S_IGNORE (1 << 11) + +/* Register offsets */ +#define BCM2708_DMA_CS 0x00 /* Control and Status */ +#define BCM2708_DMA_ADDR 0x04 /* Control block address */ +/* the current control block appears in the following registers - read only */ +#define BCM2708_DMA_INFO 0x08 +#define BCM2708_DMA_SOURCE_AD 0x0c +#define BCM2708_DMA_DEST_AD 0x10 +#define BCM2708_DMA_TXFR_LEN 0x14 +#define BCM2708_DMA_STRIDE 0x18 +#define BCM2708_DMA_NEXTCB 0x1C +#define BCM2708_DMA_DEBUG 0x20 + +#define BCM2708_DMA_INT_STATUS 0xfe0 /* Interrupt status of each channel */ +#define BCM2708_DMA_ENABLE 0xff0 /* Global enable bits for each channel */ + +#define BCM2708_DMA_CS_RW_MASK 0x30ff0001 /* All RW bits in DMA_CS */ + +static void bcm2835_dma_update(BCM2835DMAState *s, unsigned c) +{ + BCM2835DMAChan *ch = &s->chan[c]; + uint32_t data, xlen, ylen; + int16_t dst_stride, src_stride; + + if (!(s->enable & (1 << c))) { + return; + } + + while ((s->enable & (1 << c)) && (ch->conblk_ad != 0)) { + /* CB fetch */ + ch->ti = ldl_le_phys(&s->dma_as, ch->conblk_ad); + ch->source_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 4); + ch->dest_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 8); + ch->txfr_len = ldl_le_phys(&s->dma_as, ch->conblk_ad + 12); + ch->stride = ldl_le_phys(&s->dma_as, ch->conblk_ad + 16); + ch->nextconbk = ldl_le_phys(&s->dma_as, ch->conblk_ad + 20); + + if (ch->ti & BCM2708_DMA_TDMODE) { + /* 2D transfer mode */ + ylen = (ch->txfr_len >> 16) & 0x3fff; + xlen = ch->txfr_len & 0xffff; + dst_stride = ch->stride >> 16; + src_stride = ch->stride & 0xffff; + } else { + ylen = 1; + xlen = ch->txfr_len; + dst_stride = 0; + src_stride = 0; + } + + while (ylen != 0) { + /* Normal transfer mode */ + while (xlen != 0) { + if (ch->ti & BCM2708_DMA_S_IGNORE) { + /* Ignore reads */ + data = 0; + } else { + data = ldl_le_phys(&s->dma_as, ch->source_ad); + } + if (ch->ti & BCM2708_DMA_S_INC) { + ch->source_ad += 4; + } + + if (ch->ti & BCM2708_DMA_D_IGNORE) { + /* Ignore writes */ + } else { + stl_le_phys(&s->dma_as, ch->dest_ad, data); + } + if (ch->ti & BCM2708_DMA_D_INC) { + ch->dest_ad += 4; + } + + /* update remaining transfer length */ + xlen -= 4; + if (ch->ti & BCM2708_DMA_TDMODE) { + ch->txfr_len = (ylen << 16) | xlen; + } else { + ch->txfr_len = xlen; + } + } + + if (--ylen != 0) { + ch->source_ad += src_stride; + ch->dest_ad += dst_stride; + } + } + ch->cs |= BCM2708_DMA_END; + if (ch->ti & BCM2708_DMA_INT_EN) { + ch->cs |= BCM2708_DMA_INT; + s->int_status |= (1 << c); + qemu_set_irq(ch->irq, 1); + } + + /* Process next CB */ + ch->conblk_ad = ch->nextconbk; + } + + ch->cs &= ~BCM2708_DMA_ACTIVE; + ch->cs |= BCM2708_DMA_ISPAUSED; +} + +static void bcm2835_dma_chan_reset(BCM2835DMAChan *ch) +{ + ch->cs = 0; + ch->conblk_ad = 0; +} + +static uint64_t bcm2835_dma_read(BCM2835DMAState *s, hwaddr offset, + unsigned size, unsigned c) +{ + BCM2835DMAChan *ch; + uint32_t res = 0; + + assert(size == 4); + assert(c < BCM2835_DMA_NCHANS); + + ch = &s->chan[c]; + + switch (offset) { + case BCM2708_DMA_CS: + res = ch->cs; + break; + case BCM2708_DMA_ADDR: + res = ch->conblk_ad; + break; + case BCM2708_DMA_INFO: + res = ch->ti; + break; + case BCM2708_DMA_SOURCE_AD: + res = ch->source_ad; + break; + case BCM2708_DMA_DEST_AD: + res = ch->dest_ad; + break; + case BCM2708_DMA_TXFR_LEN: + res = ch->txfr_len; + break; + case BCM2708_DMA_STRIDE: + res = ch->stride; + break; + case BCM2708_DMA_NEXTCB: + res = ch->nextconbk; + break; + case BCM2708_DMA_DEBUG: + res = ch->debug; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + break; + } + return res; +} + +static void bcm2835_dma_write(BCM2835DMAState *s, hwaddr offset, + uint64_t value, unsigned size, unsigned c) +{ + BCM2835DMAChan *ch; + uint32_t oldcs; + + assert(size == 4); + assert(c < BCM2835_DMA_NCHANS); + + ch = &s->chan[c]; + + switch (offset) { + case BCM2708_DMA_CS: + oldcs = ch->cs; + if (value & BCM2708_DMA_RESET) { + bcm2835_dma_chan_reset(ch); + } + if (value & BCM2708_DMA_ABORT) { + /* abort is a no-op, since we always run to completion */ + } + if (value & BCM2708_DMA_END) { + ch->cs &= ~BCM2708_DMA_END; + } + if (value & BCM2708_DMA_INT) { + ch->cs &= ~BCM2708_DMA_INT; + s->int_status &= ~(1 << c); + qemu_set_irq(ch->irq, 0); + } + ch->cs &= ~BCM2708_DMA_CS_RW_MASK; + ch->cs |= (value & BCM2708_DMA_CS_RW_MASK); + if (!(oldcs & BCM2708_DMA_ACTIVE) && (ch->cs & BCM2708_DMA_ACTIVE)) { + bcm2835_dma_update(s, c); + } + break; + case BCM2708_DMA_ADDR: + ch->conblk_ad = value; + break; + case BCM2708_DMA_DEBUG: + ch->debug = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + break; + } +} + +static uint64_t bcm2835_dma0_read(void *opaque, hwaddr offset, unsigned size) +{ + BCM2835DMAState *s = opaque; + + if (offset < 0xf00) { + return bcm2835_dma_read(s, (offset & 0xff), size, (offset >> 8) & 0xf); + } else { + switch (offset) { + case BCM2708_DMA_INT_STATUS: + return s->int_status; + case BCM2708_DMA_ENABLE: + return s->enable; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return 0; + } + } +} + +static uint64_t bcm2835_dma15_read(void *opaque, hwaddr offset, unsigned size) +{ + return bcm2835_dma_read(opaque, (offset & 0xff), size, 15); +} + +static void bcm2835_dma0_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + BCM2835DMAState *s = opaque; + + if (offset < 0xf00) { + bcm2835_dma_write(s, (offset & 0xff), value, size, (offset >> 8) & 0xf); + } else { + switch (offset) { + case BCM2708_DMA_INT_STATUS: + break; + case BCM2708_DMA_ENABLE: + s->enable = (value & 0xffff); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + } + } + +} + +static void bcm2835_dma15_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + bcm2835_dma_write(opaque, (offset & 0xff), value, size, 15); +} + +static const MemoryRegionOps bcm2835_dma0_ops = { + .read = bcm2835_dma0_read, + .write = bcm2835_dma0_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static const MemoryRegionOps bcm2835_dma15_ops = { + .read = bcm2835_dma15_read, + .write = bcm2835_dma15_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static const VMStateDescription vmstate_bcm2835_dma_chan = { + .name = TYPE_BCM2835_DMA "-chan", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cs, BCM2835DMAChan), + VMSTATE_UINT32(conblk_ad, BCM2835DMAChan), + VMSTATE_UINT32(ti, BCM2835DMAChan), + VMSTATE_UINT32(source_ad, BCM2835DMAChan), + VMSTATE_UINT32(dest_ad, BCM2835DMAChan), + VMSTATE_UINT32(txfr_len, BCM2835DMAChan), + VMSTATE_UINT32(stride, BCM2835DMAChan), + VMSTATE_UINT32(nextconbk, BCM2835DMAChan), + VMSTATE_UINT32(debug, BCM2835DMAChan), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_bcm2835_dma = { + .name = TYPE_BCM2835_DMA, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(chan, BCM2835DMAState, BCM2835_DMA_NCHANS, 1, + vmstate_bcm2835_dma_chan, BCM2835DMAChan), + VMSTATE_UINT32(int_status, BCM2835DMAState), + VMSTATE_UINT32(enable, BCM2835DMAState), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2835_dma_init(Object *obj) +{ + BCM2835DMAState *s = BCM2835_DMA(obj); + int n; + + /* DMA channels 0-14 occupy a contiguous block of IO memory, along + * with the global enable and interrupt status bits. Channel 15 + * has the same register map, but is mapped at a discontiguous + * address in a separate IO block. + */ + memory_region_init_io(&s->iomem0, OBJECT(s), &bcm2835_dma0_ops, s, + TYPE_BCM2835_DMA, 0x1000); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem0); + + memory_region_init_io(&s->iomem15, OBJECT(s), &bcm2835_dma15_ops, s, + TYPE_BCM2835_DMA "-chan15", 0x100); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem15); + + for (n = 0; n < 16; n++) { + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->chan[n].irq); + } +} + +static void bcm2835_dma_reset(DeviceState *dev) +{ + BCM2835DMAState *s = BCM2835_DMA(dev); + int n; + + s->enable = 0xffff; + s->int_status = 0; + for (n = 0; n < BCM2835_DMA_NCHANS; n++) { + bcm2835_dma_chan_reset(&s->chan[n]); + } +} + +static void bcm2835_dma_realize(DeviceState *dev, Error **errp) +{ + BCM2835DMAState *s = BCM2835_DMA(dev); + Error *err = NULL; + Object *obj; + + obj = object_property_get_link(OBJECT(dev), "dma-mr", &err); + if (obj == NULL) { + error_setg(errp, "%s: required dma-mr link not found: %s", + __func__, error_get_pretty(err)); + return; + } + + s->dma_mr = MEMORY_REGION(obj); + address_space_init(&s->dma_as, s->dma_mr, NULL); + + bcm2835_dma_reset(dev); +} + +static void bcm2835_dma_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = bcm2835_dma_realize; + dc->reset = bcm2835_dma_reset; + dc->vmsd = &vmstate_bcm2835_dma; +} + +static TypeInfo bcm2835_dma_info = { + .name = TYPE_BCM2835_DMA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835DMAState), + .class_init = bcm2835_dma_class_init, + .instance_init = bcm2835_dma_init, +}; + +static void bcm2835_dma_register_types(void) +{ + type_register_static(&bcm2835_dma_info); +} + +type_init(bcm2835_dma_register_types) diff --git a/qemu/hw/dma/etraxfs_dma.c b/qemu/hw/dma/etraxfs_dma.c index 359951341..d5650eb88 100644 --- a/qemu/hw/dma/etraxfs_dma.c +++ b/qemu/hw/dma/etraxfs_dma.c @@ -21,8 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include -#include +#include "qemu/osdep.h" #include "hw/hw.h" #include "exec/address-spaces.h" #include "qemu-common.h" @@ -441,13 +440,16 @@ static int channel_out_run(struct fs_dma_ctrl *ctrl, int c) D(printf("channel %d pushes %x %u bytes eop=%u\n", c, saved_data_buf, len, out_eop)); - if (ctrl->channels[c].client->client.push) - ctrl->channels[c].client->client.push( - ctrl->channels[c].client->client.opaque, - buf, len, out_eop); - else + if (ctrl->channels[c].client->client.push) { + if (len > 0) { + ctrl->channels[c].client->client.push( + ctrl->channels[c].client->client.opaque, + buf, len, out_eop); + } + } else { printf("WARNING: DMA ch%d dataloss," " no attached client.\n", c); + } saved_data_buf += len; diff --git a/qemu/hw/dma/i82374.c b/qemu/hw/dma/i82374.c index b8ad2e64e..6c0f975df 100644 --- a/qemu/hw/dma/i82374.c +++ b/qemu/hw/dma/i82374.c @@ -22,8 +22,12 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/isa/isa.h" +#define TYPE_I82374 "i82374" +#define I82374(obj) OBJECT_CHECK(I82374State, (obj), TYPE_I82374) + //#define DEBUG_I82374 #ifdef DEBUG_I82374 @@ -37,8 +41,10 @@ do {} while (0) do { fprintf(stderr, "i82374 ERROR: " fmt , ## __VA_ARGS__); } while (0) typedef struct I82374State { + ISADevice parent_obj; + + uint32_t iobase; uint8_t commands[8]; - qemu_irq out; PortioList port_list; } I82374State; @@ -99,32 +105,6 @@ static uint32_t i82374_read_descriptor(void *opaque, uint32_t nport) return val; } -static void i82374_realize(I82374State *s, Error **errp) -{ - DMA_init(1, &s->out); - memset(s->commands, 0, sizeof(s->commands)); -} - -#define TYPE_I82374 "i82374" -#define I82374(obj) OBJECT_CHECK(ISAi82374State, (obj), TYPE_I82374) - -typedef struct ISAi82374State { - ISADevice parent_obj; - - uint32_t iobase; - I82374State state; -} ISAi82374State; - -static const VMStateDescription vmstate_isa_i82374 = { - .name = "isa-i82374", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(state, ISAi82374State, 0, vmstate_i82374, I82374State), - VMSTATE_END_OF_LIST() - }, -}; - static const MemoryRegionPortio i82374_portio_list[] = { { 0x0A, 1, 1, .read = i82374_read_isr, }, { 0x10, 8, 1, .write = i82374_write_command, }, @@ -134,23 +114,21 @@ static const MemoryRegionPortio i82374_portio_list[] = { PORTIO_END_OF_LIST(), }; -static void i82374_isa_realize(DeviceState *dev, Error **errp) +static void i82374_realize(DeviceState *dev, Error **errp) { - ISAi82374State *isa = I82374(dev); - I82374State *s = &isa->state; + I82374State *s = I82374(dev); - portio_list_init(&s->port_list, OBJECT(isa), i82374_portio_list, s, + portio_list_init(&s->port_list, OBJECT(s), i82374_portio_list, s, "i82374"); - portio_list_add(&s->port_list, isa_address_space_io(&isa->parent_obj), - isa->iobase); - - i82374_realize(s, errp); + portio_list_add(&s->port_list, isa_address_space_io(&s->parent_obj), + s->iobase); - qdev_init_gpio_out(dev, &s->out, 1); + DMA_init(isa_bus_from_device(ISA_DEVICE(dev)), 1); + memset(s->commands, 0, sizeof(s->commands)); } static Property i82374_properties[] = { - DEFINE_PROP_UINT32("iobase", ISAi82374State, iobase, 0x400), + DEFINE_PROP_UINT32("iobase", I82374State, iobase, 0x400), DEFINE_PROP_END_OF_LIST() }; @@ -158,21 +136,21 @@ static void i82374_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->realize = i82374_isa_realize; - dc->vmsd = &vmstate_isa_i82374; + dc->realize = i82374_realize; + dc->vmsd = &vmstate_i82374; dc->props = i82374_properties; } -static const TypeInfo i82374_isa_info = { +static const TypeInfo i82374_info = { .name = TYPE_I82374, .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(ISAi82374State), + .instance_size = sizeof(I82374State), .class_init = i82374_class_init, }; static void i82374_register_types(void) { - type_register_static(&i82374_isa_info); + type_register_static(&i82374_info); } type_init(i82374_register_types) diff --git a/qemu/hw/dma/i8257.c b/qemu/hw/dma/i8257.c index a414029be..f345c5476 100644 --- a/qemu/hw/dma/i8257.c +++ b/qemu/hw/dma/i8257.c @@ -21,11 +21,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/isa/isa.h" +#include "hw/isa/i8257.h" #include "qemu/main-loop.h" #include "trace.h" +#define I8257(obj) \ + OBJECT_CHECK(I8257State, (obj), TYPE_I8257) + /* #define DEBUG_DMA */ #define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__) @@ -37,33 +42,9 @@ #define ldebug(...) #endif -struct dma_regs { - int now[2]; - uint16_t base[2]; - uint8_t mode; - uint8_t page; - uint8_t pageh; - uint8_t dack; - uint8_t eop; - DMA_transfer_handler transfer_handler; - void *opaque; -}; - #define ADDR 0 #define COUNT 1 -static struct dma_cont { - uint8_t status; - uint8_t command; - uint8_t mask; - uint8_t flip_flop; - int dshift; - struct dma_regs regs[4]; - qemu_irq *cpu_request_exit; - MemoryRegion channel_io; - MemoryRegion cont_io; -} dma_controllers[2]; - enum { CMD_MEMORY_TO_MEMORY = 0x01, CMD_FIXED_ADDRESS = 0x02, @@ -79,13 +60,13 @@ enum { }; -static void DMA_run (void); +static void i8257_dma_run(void *opaque); -static int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0}; +static const int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0}; -static void write_page (void *opaque, uint32_t nport, uint32_t data) +static void i8257_write_page(void *opaque, uint32_t nport, uint32_t data) { - struct dma_cont *d = opaque; + I8257State *d = opaque; int ichan; ichan = channels[nport & 7]; @@ -96,9 +77,9 @@ static void write_page (void *opaque, uint32_t nport, uint32_t data) d->regs[ichan].page = data; } -static void write_pageh (void *opaque, uint32_t nport, uint32_t data) +static void i8257_write_pageh(void *opaque, uint32_t nport, uint32_t data) { - struct dma_cont *d = opaque; + I8257State *d = opaque; int ichan; ichan = channels[nport & 7]; @@ -109,9 +90,9 @@ static void write_pageh (void *opaque, uint32_t nport, uint32_t data) d->regs[ichan].pageh = data; } -static uint32_t read_page (void *opaque, uint32_t nport) +static uint32_t i8257_read_page(void *opaque, uint32_t nport) { - struct dma_cont *d = opaque; + I8257State *d = opaque; int ichan; ichan = channels[nport & 7]; @@ -122,9 +103,9 @@ static uint32_t read_page (void *opaque, uint32_t nport) return d->regs[ichan].page; } -static uint32_t read_pageh (void *opaque, uint32_t nport) +static uint32_t i8257_read_pageh(void *opaque, uint32_t nport) { - struct dma_cont *d = opaque; + I8257State *d = opaque; int ichan; ichan = channels[nport & 7]; @@ -135,16 +116,16 @@ static uint32_t read_pageh (void *opaque, uint32_t nport) return d->regs[ichan].pageh; } -static inline void init_chan (struct dma_cont *d, int ichan) +static inline void i8257_init_chan(I8257State *d, int ichan) { - struct dma_regs *r; + I8257Regs *r; r = d->regs + ichan; r->now[ADDR] = r->base[ADDR] << d->dshift; r->now[COUNT] = 0; } -static inline int getff (struct dma_cont *d) +static inline int i8257_getff(I8257State *d) { int ff; @@ -153,11 +134,11 @@ static inline int getff (struct dma_cont *d) return ff; } -static uint64_t read_chan(void *opaque, hwaddr nport, unsigned size) +static uint64_t i8257_read_chan(void *opaque, hwaddr nport, unsigned size) { - struct dma_cont *d = opaque; + I8257State *d = opaque; int ichan, nreg, iport, ff, val, dir; - struct dma_regs *r; + I8257Regs *r; iport = (nport >> d->dshift) & 0x0f; ichan = iport >> 1; @@ -165,7 +146,7 @@ static uint64_t read_chan(void *opaque, hwaddr nport, unsigned size) r = d->regs + ichan; dir = ((r->mode >> 5) & 1) ? -1 : 1; - ff = getff (d); + ff = i8257_getff(d); if (nreg) val = (r->base[COUNT] << d->dshift) - r->now[COUNT]; else @@ -175,29 +156,29 @@ static uint64_t read_chan(void *opaque, hwaddr nport, unsigned size) return (val >> (d->dshift + (ff << 3))) & 0xff; } -static void write_chan(void *opaque, hwaddr nport, uint64_t data, - unsigned size) +static void i8257_write_chan(void *opaque, hwaddr nport, uint64_t data, + unsigned int size) { - struct dma_cont *d = opaque; + I8257State *d = opaque; int iport, ichan, nreg; - struct dma_regs *r; + I8257Regs *r; iport = (nport >> d->dshift) & 0x0f; ichan = iport >> 1; nreg = iport & 1; r = d->regs + ichan; - if (getff (d)) { + if (i8257_getff(d)) { r->base[nreg] = (r->base[nreg] & 0xff) | ((data << 8) & 0xff00); - init_chan (d, ichan); + i8257_init_chan(d, ichan); } else { r->base[nreg] = (r->base[nreg] & 0xff00) | (data & 0xff); } } -static void write_cont(void *opaque, hwaddr nport, uint64_t data, - unsigned size) +static void i8257_write_cont(void *opaque, hwaddr nport, uint64_t data, + unsigned int size) { - struct dma_cont *d = opaque; + I8257State *d = opaque; int iport, ichan = 0; iport = (nport >> d->dshift) & 0x0f; @@ -219,7 +200,7 @@ static void write_cont(void *opaque, hwaddr nport, uint64_t data, d->status &= ~(1 << (ichan + 4)); } d->status &= ~(1 << ichan); - DMA_run(); + i8257_dma_run(d); break; case 0x02: /* single mask */ @@ -227,7 +208,7 @@ static void write_cont(void *opaque, hwaddr nport, uint64_t data, d->mask |= 1 << (data & 3); else d->mask &= ~(1 << (data & 3)); - DMA_run(); + i8257_dma_run(d); break; case 0x03: /* mode */ @@ -262,12 +243,12 @@ static void write_cont(void *opaque, hwaddr nport, uint64_t data, case 0x06: /* clear mask for all channels */ d->mask = 0; - DMA_run(); + i8257_dma_run(d); break; case 0x07: /* write mask for all channels */ d->mask = data; - DMA_run(); + i8257_dma_run(d); break; default: @@ -283,9 +264,9 @@ static void write_cont(void *opaque, hwaddr nport, uint64_t data, #endif } -static uint64_t read_cont(void *opaque, hwaddr nport, unsigned size) +static uint64_t i8257_read_cont(void *opaque, hwaddr nport, unsigned size) { - struct dma_cont *d = opaque; + I8257State *d = opaque; int iport, val; iport = (nport >> d->dshift) & 0x0f; @@ -306,37 +287,43 @@ static uint64_t read_cont(void *opaque, hwaddr nport, unsigned size) return val; } -int DMA_get_channel_mode (int nchan) +static IsaDmaTransferMode i8257_dma_get_transfer_mode(IsaDma *obj, int nchan) { - return dma_controllers[nchan > 3].regs[nchan & 3].mode; + I8257State *d = I8257(obj); + return (d->regs[nchan & 3].mode >> 2) & 3; } -void DMA_hold_DREQ (int nchan) +static bool i8257_dma_has_autoinitialization(IsaDma *obj, int nchan) { - int ncont, ichan; + I8257State *d = I8257(obj); + return (d->regs[nchan & 3].mode >> 4) & 1; +} + +static void i8257_dma_hold_DREQ(IsaDma *obj, int nchan) +{ + I8257State *d = I8257(obj); + int ichan; - ncont = nchan > 3; ichan = nchan & 3; - linfo ("held cont=%d chan=%d\n", ncont, ichan); - dma_controllers[ncont].status |= 1 << (ichan + 4); - DMA_run(); + d->status |= 1 << (ichan + 4); + i8257_dma_run(d); } -void DMA_release_DREQ (int nchan) +static void i8257_dma_release_DREQ(IsaDma *obj, int nchan) { - int ncont, ichan; + I8257State *d = I8257(obj); + int ichan; - ncont = nchan > 3; ichan = nchan & 3; - linfo ("released cont=%d chan=%d\n", ncont, ichan); - dma_controllers[ncont].status &= ~(1 << (ichan + 4)); - DMA_run(); + d->status &= ~(1 << (ichan + 4)); + i8257_dma_run(d); } -static void channel_run (int ncont, int ichan) +static void i8257_channel_run(I8257State *d, int ichan) { + int ncont = d->dshift; int n; - struct dma_regs *r = &dma_controllers[ncont].regs[ichan]; + I8257Regs *r = &d->regs[ichan]; #ifdef DEBUG_DMA int dir, opmode; @@ -355,68 +342,64 @@ static void channel_run (int ncont, int ichan) r->now[COUNT], (r->base[COUNT] + 1) << ncont); r->now[COUNT] = n; ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont); + if (n == (r->base[COUNT] + 1) << ncont) { + ldebug("transfer done\n"); + d->status |= (1 << ichan); + } } -static QEMUBH *dma_bh; - -static void DMA_run (void) +static void i8257_dma_run(void *opaque) { - struct dma_cont *d; - int icont, ichan; + I8257State *d = opaque; + int ichan; int rearm = 0; - static int running = 0; - if (running) { + if (d->running) { rearm = 1; goto out; } else { - running = 1; + d->running = 1; } - d = dma_controllers; + for (ichan = 0; ichan < 4; ichan++) { + int mask; - for (icont = 0; icont < 2; icont++, d++) { - for (ichan = 0; ichan < 4; ichan++) { - int mask; + mask = 1 << ichan; - mask = 1 << ichan; - - if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) { - channel_run (icont, ichan); - rearm = 1; - } + if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) { + i8257_channel_run(d, ichan); + rearm = 1; } } - running = 0; + d->running = 0; out: - if (rearm) - qemu_bh_schedule_idle(dma_bh); -} - -static void DMA_run_bh(void *unused) -{ - DMA_run(); + if (rearm) { + qemu_bh_schedule_idle(d->dma_bh); + d->dma_bh_scheduled = true; + } } -void DMA_register_channel (int nchan, - DMA_transfer_handler transfer_handler, - void *opaque) +static void i8257_dma_register_channel(IsaDma *obj, int nchan, + IsaDmaTransferHandler transfer_handler, + void *opaque) { - struct dma_regs *r; - int ichan, ncont; + I8257State *d = I8257(obj); + I8257Regs *r; + int ichan; - ncont = nchan > 3; ichan = nchan & 3; - r = dma_controllers[ncont].regs + ichan; + r = d->regs + ichan; r->transfer_handler = transfer_handler; r->opaque = opaque; } -int DMA_read_memory (int nchan, void *buf, int pos, int len) +static int i8257_dma_read_memory(IsaDma *obj, int nchan, void *buf, int pos, + int len) { - struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3]; + I8257State *d = I8257(obj); + I8257Regs *r = &d->regs[nchan & 3]; hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; if (r->mode & 0x20) { @@ -436,9 +419,11 @@ int DMA_read_memory (int nchan, void *buf, int pos, int len) return len; } -int DMA_write_memory (int nchan, void *buf, int pos, int len) +static int i8257_dma_write_memory(IsaDma *obj, int nchan, void *buf, int pos, + int len) { - struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3]; + I8257State *s = I8257(obj); + I8257Regs *r = &s->regs[nchan & 3]; hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; if (r->mode & 0x20) { @@ -458,21 +443,25 @@ int DMA_write_memory (int nchan, void *buf, int pos, int len) return len; } -/* request the emulator to transfer a new DMA memory block ASAP */ -void DMA_schedule(int nchan) +/* request the emulator to transfer a new DMA memory block ASAP (even + * if the idle bottom half would not have exited the iothread yet). + */ +static void i8257_dma_schedule(IsaDma *obj) { - struct dma_cont *d = &dma_controllers[nchan > 3]; - - qemu_irq_pulse(*d->cpu_request_exit); + I8257State *d = I8257(obj); + if (d->dma_bh_scheduled) { + qemu_notify_event(); + } } -static void dma_reset(void *opaque) +static void i8257_reset(DeviceState *dev) { - struct dma_cont *d = opaque; - write_cont(d, (0x05 << d->dshift), 0, 1); + I8257State *d = I8257(dev); + i8257_write_cont(d, (0x05 << d->dshift), 0, 1); } -static int dma_phony_handler (void *opaque, int nchan, int dma_pos, int dma_len) +static int i8257_phony_handler(void *opaque, int nchan, int dma_pos, + int dma_len) { trace_i8257_unregistered_dma(nchan, dma_pos, dma_len); return dma_pos; @@ -480,8 +469,8 @@ static int dma_phony_handler (void *opaque, int nchan, int dma_pos, int dma_len) static const MemoryRegionOps channel_io_ops = { - .read = read_chan, - .write = write_chan, + .read = i8257_read_chan, + .write = i8257_write_chan, .endianness = DEVICE_NATIVE_ENDIAN, .impl = { .min_access_size = 1, @@ -491,21 +480,21 @@ static const MemoryRegionOps channel_io_ops = { /* IOport from page_base */ static const MemoryRegionPortio page_portio_list[] = { - { 0x01, 3, 1, .write = write_page, .read = read_page, }, - { 0x07, 1, 1, .write = write_page, .read = read_page, }, + { 0x01, 3, 1, .write = i8257_write_page, .read = i8257_read_page, }, + { 0x07, 1, 1, .write = i8257_write_page, .read = i8257_read_page, }, PORTIO_END_OF_LIST(), }; /* IOport from pageh_base */ static const MemoryRegionPortio pageh_portio_list[] = { - { 0x01, 3, 1, .write = write_pageh, .read = read_pageh, }, - { 0x07, 3, 1, .write = write_pageh, .read = read_pageh, }, + { 0x01, 3, 1, .write = i8257_write_pageh, .read = i8257_read_pageh, }, + { 0x07, 3, 1, .write = i8257_write_pageh, .read = i8257_read_pageh, }, PORTIO_END_OF_LIST(), }; static const MemoryRegionOps cont_io_ops = { - .read = read_cont, - .write = write_cont, + .read = i8257_read_cont, + .write = i8257_write_cont, .endianness = DEVICE_NATIVE_ENDIAN, .impl = { .min_access_size = 1, @@ -513,86 +502,142 @@ static const MemoryRegionOps cont_io_ops = { }, }; -/* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */ -static void dma_init2(struct dma_cont *d, int base, int dshift, - int page_base, int pageh_base, - qemu_irq *cpu_request_exit) +static const VMStateDescription vmstate_i8257_regs = { + .name = "dma_regs", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32_ARRAY(now, I8257Regs, 2), + VMSTATE_UINT16_ARRAY(base, I8257Regs, 2), + VMSTATE_UINT8(mode, I8257Regs), + VMSTATE_UINT8(page, I8257Regs), + VMSTATE_UINT8(pageh, I8257Regs), + VMSTATE_UINT8(dack, I8257Regs), + VMSTATE_UINT8(eop, I8257Regs), + VMSTATE_END_OF_LIST() + } +}; + +static int i8257_post_load(void *opaque, int version_id) { - int i; + I8257State *d = opaque; + i8257_dma_run(d); - d->dshift = dshift; - d->cpu_request_exit = cpu_request_exit; + return 0; +} + +static const VMStateDescription vmstate_i8257 = { + .name = "dma", + .version_id = 1, + .minimum_version_id = 1, + .post_load = i8257_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8(command, I8257State), + VMSTATE_UINT8(mask, I8257State), + VMSTATE_UINT8(flip_flop, I8257State), + VMSTATE_INT32(dshift, I8257State), + VMSTATE_STRUCT_ARRAY(regs, I8257State, 4, 1, vmstate_i8257_regs, + I8257Regs), + VMSTATE_END_OF_LIST() + } +}; + +static void i8257_realize(DeviceState *dev, Error **errp) +{ + ISADevice *isa = ISA_DEVICE(dev); + I8257State *d = I8257(dev); + int i; memory_region_init_io(&d->channel_io, NULL, &channel_io_ops, d, "dma-chan", 8 << d->dshift); - memory_region_add_subregion(isa_address_space_io(NULL), - base, &d->channel_io); + memory_region_add_subregion(isa_address_space_io(isa), + d->base, &d->channel_io); - isa_register_portio_list(NULL, page_base, page_portio_list, d, + isa_register_portio_list(isa, d->page_base, page_portio_list, d, "dma-page"); - if (pageh_base >= 0) { - isa_register_portio_list(NULL, pageh_base, pageh_portio_list, d, + if (d->pageh_base >= 0) { + isa_register_portio_list(isa, d->pageh_base, pageh_portio_list, d, "dma-pageh"); } - memory_region_init_io(&d->cont_io, NULL, &cont_io_ops, d, "dma-cont", - 8 << d->dshift); - memory_region_add_subregion(isa_address_space_io(NULL), - base + (8 << d->dshift), &d->cont_io); + memory_region_init_io(&d->cont_io, OBJECT(isa), &cont_io_ops, d, + "dma-cont", 8 << d->dshift); + memory_region_add_subregion(isa_address_space_io(isa), + d->base + (8 << d->dshift), &d->cont_io); - qemu_register_reset(dma_reset, d); - dma_reset(d); - for (i = 0; i < ARRAY_SIZE (d->regs); ++i) { - d->regs[i].transfer_handler = dma_phony_handler; + for (i = 0; i < ARRAY_SIZE(d->regs); ++i) { + d->regs[i].transfer_handler = i8257_phony_handler; } + + d->dma_bh = qemu_bh_new(i8257_dma_run, d); } -static const VMStateDescription vmstate_dma_regs = { - .name = "dma_regs", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32_ARRAY(now, struct dma_regs, 2), - VMSTATE_UINT16_ARRAY(base, struct dma_regs, 2), - VMSTATE_UINT8(mode, struct dma_regs), - VMSTATE_UINT8(page, struct dma_regs), - VMSTATE_UINT8(pageh, struct dma_regs), - VMSTATE_UINT8(dack, struct dma_regs), - VMSTATE_UINT8(eop, struct dma_regs), - VMSTATE_END_OF_LIST() - } +static Property i8257_properties[] = { + DEFINE_PROP_INT32("base", I8257State, base, 0x00), + DEFINE_PROP_INT32("page-base", I8257State, page_base, 0x80), + DEFINE_PROP_INT32("pageh-base", I8257State, pageh_base, 0x480), + DEFINE_PROP_INT32("dshift", I8257State, dshift, 0), + DEFINE_PROP_END_OF_LIST() }; -static int dma_post_load(void *opaque, int version_id) +static void i8257_class_init(ObjectClass *klass, void *data) { - DMA_run(); - - return 0; + DeviceClass *dc = DEVICE_CLASS(klass); + IsaDmaClass *idc = ISADMA_CLASS(klass); + + dc->realize = i8257_realize; + dc->reset = i8257_reset; + dc->vmsd = &vmstate_i8257; + dc->props = i8257_properties; + + idc->get_transfer_mode = i8257_dma_get_transfer_mode; + idc->has_autoinitialization = i8257_dma_has_autoinitialization; + idc->read_memory = i8257_dma_read_memory; + idc->write_memory = i8257_dma_write_memory; + idc->hold_DREQ = i8257_dma_hold_DREQ; + idc->release_DREQ = i8257_dma_release_DREQ; + idc->schedule = i8257_dma_schedule; + idc->register_channel = i8257_dma_register_channel; } -static const VMStateDescription vmstate_dma = { - .name = "dma", - .version_id = 1, - .minimum_version_id = 1, - .post_load = dma_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(command, struct dma_cont), - VMSTATE_UINT8(mask, struct dma_cont), - VMSTATE_UINT8(flip_flop, struct dma_cont), - VMSTATE_INT32(dshift, struct dma_cont), - VMSTATE_STRUCT_ARRAY(regs, struct dma_cont, 4, 1, vmstate_dma_regs, struct dma_regs), - VMSTATE_END_OF_LIST() +static const TypeInfo i8257_info = { + .name = TYPE_I8257, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(I8257State), + .class_init = i8257_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_ISADMA }, + { } } }; -void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit) +static void i8257_register_types(void) +{ + type_register_static(&i8257_info); +} + +type_init(i8257_register_types) + +void DMA_init(ISABus *bus, int high_page_enable) { - dma_init2(&dma_controllers[0], 0x00, 0, 0x80, - high_page_enable ? 0x480 : -1, cpu_request_exit); - dma_init2(&dma_controllers[1], 0xc0, 1, 0x88, - high_page_enable ? 0x488 : -1, cpu_request_exit); - vmstate_register (NULL, 0, &vmstate_dma, &dma_controllers[0]); - vmstate_register (NULL, 1, &vmstate_dma, &dma_controllers[1]); - - dma_bh = qemu_bh_new(DMA_run_bh, NULL); + ISADevice *isa1, *isa2; + DeviceState *d; + + isa1 = isa_create(bus, TYPE_I8257); + d = DEVICE(isa1); + qdev_prop_set_int32(d, "base", 0x00); + qdev_prop_set_int32(d, "page-base", 0x80); + qdev_prop_set_int32(d, "pageh-base", high_page_enable ? 0x480 : -1); + qdev_prop_set_int32(d, "dshift", 0); + qdev_init_nofail(d); + + isa2 = isa_create(bus, TYPE_I8257); + d = DEVICE(isa2); + qdev_prop_set_int32(d, "base", 0xc0); + qdev_prop_set_int32(d, "page-base", 0x88); + qdev_prop_set_int32(d, "pageh-base", high_page_enable ? 0x488 : -1); + qdev_prop_set_int32(d, "dshift", 1); + qdev_init_nofail(d); + + isa_bus_dma(bus, ISADMA(isa1), ISADMA(isa2)); } diff --git a/qemu/hw/dma/omap_dma.c b/qemu/hw/dma/omap_dma.c index 97c57a03c..700cd6b43 100644 --- a/qemu/hw/dma/omap_dma.c +++ b/qemu/hw/dma/omap_dma.c @@ -17,6 +17,7 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/timer.h" #include "hw/arm/omap.h" @@ -1626,8 +1627,7 @@ struct soc_dma_s *omap_dma_init(hwaddr base, qemu_irq *irqs, enum omap_dma_model model) { int num_irqs, memsize, i; - struct omap_dma_s *s = (struct omap_dma_s *) - g_malloc0(sizeof(struct omap_dma_s)); + struct omap_dma_s *s = g_new0(struct omap_dma_s, 1); if (model <= omap_dma_3_1) { num_irqs = 6; @@ -2061,8 +2061,7 @@ struct soc_dma_s *omap_dma4_init(hwaddr base, qemu_irq *irqs, int chans, omap_clk iclk, omap_clk fclk) { int i; - struct omap_dma_s *s = (struct omap_dma_s *) - g_malloc0(sizeof(struct omap_dma_s)); + struct omap_dma_s *s = g_new0(struct omap_dma_s, 1); s->model = omap_dma_4; s->chans = chans; diff --git a/qemu/hw/dma/pl080.c b/qemu/hw/dma/pl080.c index b89b4744f..9318108b8 100644 --- a/qemu/hw/dma/pl080.c +++ b/qemu/hw/dma/pl080.c @@ -7,6 +7,7 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "exec/address-spaces.h" diff --git a/qemu/hw/dma/pl330.c b/qemu/hw/dma/pl330.c index 5be3df521..ea89ecb00 100644 --- a/qemu/hw/dma/pl330.c +++ b/qemu/hw/dma/pl330.c @@ -14,7 +14,9 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/sysbus.h" +#include "qapi/error.h" #include "qemu/timer.h" #include "sysemu/dma.h" diff --git a/qemu/hw/dma/puv3_dma.c b/qemu/hw/dma/puv3_dma.c index 101bd7f8a..b97a6c176 100644 --- a/qemu/hw/dma/puv3_dma.c +++ b/qemu/hw/dma/puv3_dma.c @@ -8,6 +8,7 @@ * published by the Free Software Foundation, or any later version. * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" diff --git a/qemu/hw/dma/pxa2xx_dma.c b/qemu/hw/dma/pxa2xx_dma.c index d4501fb4c..2306abc35 100644 --- a/qemu/hw/dma/pxa2xx_dma.c +++ b/qemu/hw/dma/pxa2xx_dma.c @@ -8,6 +8,7 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/arm/pxa.h" #include "hw/sysbus.h" @@ -459,9 +460,8 @@ static int pxa2xx_dma_init(SysBusDevice *sbd) return -1; } - s->chan = g_malloc0(sizeof(PXA2xxDMAChannel) * s->channels); + s->chan = g_new0(PXA2xxDMAChannel, s->channels); - memset(s->chan, 0, sizeof(PXA2xxDMAChannel) * s->channels); for (i = 0; i < s->channels; i ++) s->chan[i].state = DCSR_STOPINTR; diff --git a/qemu/hw/dma/rc4030.c b/qemu/hw/dma/rc4030.c index 3efa6de35..a06c2359a 100644 --- a/qemu/hw/dma/rc4030.c +++ b/qemu/hw/dma/rc4030.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/mips/mips.h" #include "hw/sysbus.h" @@ -111,7 +112,7 @@ static void set_next_tick(rc4030State *s) tm_hz = 1000 / (s->itr + 1); timer_mod(s->periodic_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - get_ticks_per_sec() / tm_hz); + NANOSECONDS_PER_SECOND / tm_hz); } /* called for accesses to rc4030 */ diff --git a/qemu/hw/dma/soc_dma.c b/qemu/hw/dma/soc_dma.c index c06aabb40..9bb499bf9 100644 --- a/qemu/hw/dma/soc_dma.c +++ b/qemu/hw/dma/soc_dma.c @@ -17,6 +17,7 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/timer.h" #include "hw/arm/soc_dma.h" @@ -269,11 +270,10 @@ void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base, if (entry->type == soc_dma_port_mem) { if (entry->addr <= virt_base && entry->addr + entry->u.mem.size > virt_base) { - fprintf(stderr, "%s: FIFO at " TARGET_FMT_lx - " collides with RAM region at " TARGET_FMT_lx - "-" TARGET_FMT_lx "\n", __FUNCTION__, - (target_ulong) virt_base, - (target_ulong) entry->addr, (target_ulong) + fprintf(stderr, "%s: FIFO at %"PRIx64 + " collides with RAM region at %"PRIx64 + "-%"PRIx64 "\n", __func__, + virt_base, entry->addr, (entry->addr + entry->u.mem.size)); exit(-1); } @@ -284,10 +284,9 @@ void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base, while (entry < dma->memmap + dma->memmap_size && entry->addr <= virt_base) { if (entry->addr == virt_base && entry->u.fifo.out == out) { - fprintf(stderr, "%s: FIFO at " TARGET_FMT_lx - " collides FIFO at " TARGET_FMT_lx "\n", - __FUNCTION__, (target_ulong) virt_base, - (target_ulong) entry->addr); + fprintf(stderr, "%s: FIFO at %"PRIx64 + " collides FIFO at %"PRIx64 "\n", + __func__, virt_base, entry->addr); exit(-1); } @@ -322,13 +321,11 @@ void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base, if ((entry->addr >= virt_base && entry->addr < virt_base + size) || (entry->addr <= virt_base && entry->addr + entry->u.mem.size > virt_base)) { - fprintf(stderr, "%s: RAM at " TARGET_FMT_lx "-" TARGET_FMT_lx - " collides with RAM region at " TARGET_FMT_lx - "-" TARGET_FMT_lx "\n", __FUNCTION__, - (target_ulong) virt_base, - (target_ulong) (virt_base + size), - (target_ulong) entry->addr, (target_ulong) - (entry->addr + entry->u.mem.size)); + fprintf(stderr, "%s: RAM at %"PRIx64 "-%"PRIx64 + " collides with RAM region at %"PRIx64 + "-%"PRIx64 "\n", __func__, + virt_base, virt_base + size, + entry->addr, entry->addr + entry->u.mem.size); exit(-1); } @@ -337,12 +334,11 @@ void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base, } else { if (entry->addr >= virt_base && entry->addr < virt_base + size) { - fprintf(stderr, "%s: RAM at " TARGET_FMT_lx "-" TARGET_FMT_lx - " collides with FIFO at " TARGET_FMT_lx - "\n", __FUNCTION__, - (target_ulong) virt_base, - (target_ulong) (virt_base + size), - (target_ulong) entry->addr); + fprintf(stderr, "%s: RAM at %"PRIx64 "-%"PRIx64 + " collides with FIFO at %"PRIx64 + "\n", __func__, + virt_base, virt_base + size, + entry->addr); exit(-1); } diff --git a/qemu/hw/dma/sparc32_dma.c b/qemu/hw/dma/sparc32_dma.c index e6a453ce5..9d545e412 100644 --- a/qemu/hw/dma/sparc32_dma.c +++ b/qemu/hw/dma/sparc32_dma.c @@ -25,6 +25,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sparc/sparc32_dma.h" #include "hw/sparc/sun4m.h" diff --git a/qemu/hw/dma/sun4m_iommu.c b/qemu/hw/dma/sun4m_iommu.c index 9a488bc9b..b3cbc54c2 100644 --- a/qemu/hw/dma/sun4m_iommu.c +++ b/qemu/hw/dma/sun4m_iommu.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sparc/sun4m.h" #include "hw/sysbus.h" #include "exec/address-spaces.h" diff --git a/qemu/hw/dma/xilinx_axidma.c b/qemu/hw/dma/xilinx_axidma.c index cf842a3cc..a4753e55a 100644 --- a/qemu/hw/dma/xilinx_axidma.c +++ b/qemu/hw/dma/xilinx_axidma.c @@ -22,7 +22,9 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" +#include "qapi/error.h" #include "qemu/timer.h" #include "hw/ptimer.h" #include "qemu/log.h" @@ -133,7 +135,7 @@ struct XilinxAXIDMA { }; /* - * Helper calls to extract info from desriptors and other trivial + * Helper calls to extract info from descriptors and other trivial * state from regs. */ static inline int stream_desc_sof(struct SDesc *d) @@ -177,16 +179,6 @@ static inline int streamid_from_addr(hwaddr addr) return sid; } -#ifdef DEBUG_ENET -static void stream_desc_show(struct SDesc *d) -{ - qemu_log("buffer_addr = " PRIx64 "\n", d->buffer_address); - qemu_log("nxtdesc = " PRIx64 "\n", d->nxtdesc); - qemu_log("control = %x\n", d->control); - qemu_log("status = %x\n", d->status); -} -#endif - static void stream_desc_load(struct Stream *s, hwaddr addr) { struct SDesc *d = &s->desc; diff --git a/qemu/hw/gpio/Makefile.objs b/qemu/hw/gpio/Makefile.objs index 1abcf1798..a43c7cf44 100644 --- a/qemu/hw/gpio/Makefile.objs +++ b/qemu/hw/gpio/Makefile.objs @@ -3,5 +3,7 @@ common-obj-$(CONFIG_PL061) += pl061.o common-obj-$(CONFIG_PUV3) += puv3_gpio.o common-obj-$(CONFIG_ZAURUS) += zaurus.o common-obj-$(CONFIG_E500) += mpc8xxx.o +common-obj-$(CONFIG_GPIO_KEY) += gpio_key.o obj-$(CONFIG_OMAP) += omap_gpio.o +obj-$(CONFIG_IMX) += imx_gpio.o diff --git a/qemu/hw/gpio/gpio_key.c b/qemu/hw/gpio/gpio_key.c new file mode 100644 index 000000000..ef287727b --- /dev/null +++ b/qemu/hw/gpio/gpio_key.c @@ -0,0 +1,104 @@ +/* + * GPIO key + * + * Copyright (c) 2016 Linaro Limited + * + * Author: Shannon Zhao + * + * Emulate a (human) keypress -- when the key is triggered by + * setting the incoming gpio line, the outbound irq line is + * raised for 100ms before being dropped again. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License; 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 . + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" + +#define TYPE_GPIOKEY "gpio-key" +#define GPIOKEY(obj) OBJECT_CHECK(GPIOKEYState, (obj), TYPE_GPIOKEY) +#define GPIO_KEY_LATENCY 100 /* 100ms */ + +typedef struct GPIOKEYState { + SysBusDevice parent_obj; + + QEMUTimer *timer; + qemu_irq irq; +} GPIOKEYState; + +static const VMStateDescription vmstate_gpio_key = { + .name = "gpio-key", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_TIMER_PTR(timer, GPIOKEYState), + VMSTATE_END_OF_LIST() + } +}; + +static void gpio_key_reset(DeviceState *dev) +{ + GPIOKEYState *s = GPIOKEY(dev); + + timer_del(s->timer); +} + +static void gpio_key_timer_expired(void *opaque) +{ + GPIOKEYState *s = (GPIOKEYState *)opaque; + + qemu_set_irq(s->irq, 0); + timer_del(s->timer); +} + +static void gpio_key_set_irq(void *opaque, int irq, int level) +{ + GPIOKEYState *s = (GPIOKEYState *)opaque; + + qemu_set_irq(s->irq, 1); + timer_mod(s->timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + GPIO_KEY_LATENCY); +} + +static void gpio_key_realize(DeviceState *dev, Error **errp) +{ + GPIOKEYState *s = GPIOKEY(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + sysbus_init_irq(sbd, &s->irq); + qdev_init_gpio_in(dev, gpio_key_set_irq, 1); + s->timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, gpio_key_timer_expired, s); +} + +static void gpio_key_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = gpio_key_realize; + dc->vmsd = &vmstate_gpio_key; + dc->reset = &gpio_key_reset; +} + +static const TypeInfo gpio_key_info = { + .name = TYPE_GPIOKEY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(GPIOKEYState), + .class_init = gpio_key_class_init, +}; + +static void gpio_key_register_types(void) +{ + type_register_static(&gpio_key_info); +} + +type_init(gpio_key_register_types) diff --git a/qemu/hw/gpio/imx_gpio.c b/qemu/hw/gpio/imx_gpio.c new file mode 100644 index 000000000..ed7e247f5 --- /dev/null +++ b/qemu/hw/gpio/imx_gpio.c @@ -0,0 +1,350 @@ +/* + * i.MX processors GPIO emulation. + * + * Copyright (C) 2015 Jean-Christophe Dubois + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "hw/gpio/imx_gpio.h" + +#ifndef DEBUG_IMX_GPIO +#define DEBUG_IMX_GPIO 0 +#endif + +typedef enum IMXGPIOLevel { + IMX_GPIO_LEVEL_LOW = 0, + IMX_GPIO_LEVEL_HIGH = 1, +} IMXGPIOLevel; + +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_GPIO) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_GPIO, \ + __func__, ##args); \ + } \ + } while (0) + +static const char *imx_gpio_reg_name(uint32_t reg) +{ + switch (reg) { + case DR_ADDR: + return "DR"; + case GDIR_ADDR: + return "GDIR"; + case PSR_ADDR: + return "PSR"; + case ICR1_ADDR: + return "ICR1"; + case ICR2_ADDR: + return "ICR2"; + case IMR_ADDR: + return "IMR"; + case ISR_ADDR: + return "ISR"; + case EDGE_SEL_ADDR: + return "EDGE_SEL"; + default: + return "[?]"; + } +} + +static void imx_gpio_update_int(IMXGPIOState *s) +{ + if (s->has_upper_pin_irq) { + qemu_set_irq(s->irq[0], (s->isr & s->imr & 0x0000FFFF) ? 1 : 0); + qemu_set_irq(s->irq[1], (s->isr & s->imr & 0xFFFF0000) ? 1 : 0); + } else { + qemu_set_irq(s->irq[0], (s->isr & s->imr) ? 1 : 0); + } +} + +static void imx_gpio_set_int_line(IMXGPIOState *s, int line, IMXGPIOLevel level) +{ + /* if this signal isn't configured as an input signal, nothing to do */ + if (!extract32(s->gdir, line, 1)) { + return; + } + + /* When set, EDGE_SEL overrides the ICR config */ + if (extract32(s->edge_sel, line, 1)) { + /* we detect interrupt on rising and falling edge */ + if (extract32(s->psr, line, 1) != level) { + /* level changed */ + s->isr = deposit32(s->isr, line, 1, 1); + } + } else if (extract64(s->icr, 2*line + 1, 1)) { + /* interrupt is edge sensitive */ + if (extract32(s->psr, line, 1) != level) { + /* level changed */ + if (extract64(s->icr, 2*line, 1) != level) { + s->isr = deposit32(s->isr, line, 1, 1); + } + } + } else { + /* interrupt is level sensitive */ + if (extract64(s->icr, 2*line, 1) == level) { + s->isr = deposit32(s->isr, line, 1, 1); + } + } +} + +static void imx_gpio_set(void *opaque, int line, int level) +{ + IMXGPIOState *s = IMX_GPIO(opaque); + IMXGPIOLevel imx_level = level ? IMX_GPIO_LEVEL_HIGH : IMX_GPIO_LEVEL_LOW; + + imx_gpio_set_int_line(s, line, imx_level); + + /* this is an input signal, so set PSR */ + s->psr = deposit32(s->psr, line, 1, imx_level); + + imx_gpio_update_int(s); +} + +static void imx_gpio_set_all_int_lines(IMXGPIOState *s) +{ + int i; + + for (i = 0; i < IMX_GPIO_PIN_COUNT; i++) { + IMXGPIOLevel imx_level = extract32(s->psr, i, 1); + imx_gpio_set_int_line(s, i, imx_level); + } + + imx_gpio_update_int(s); +} + +static inline void imx_gpio_set_all_output_lines(IMXGPIOState *s) +{ + int i; + + for (i = 0; i < IMX_GPIO_PIN_COUNT; i++) { + /* + * if the line is set as output, then forward the line + * level to its user. + */ + if (extract32(s->gdir, i, 1) && s->output[i]) { + qemu_set_irq(s->output[i], extract32(s->dr, i, 1)); + } + } +} + +static uint64_t imx_gpio_read(void *opaque, hwaddr offset, unsigned size) +{ + IMXGPIOState *s = IMX_GPIO(opaque); + uint32_t reg_value = 0; + + switch (offset) { + case DR_ADDR: + /* + * depending on the "line" configuration, the bit values + * are coming either from DR or PSR + */ + reg_value = (s->dr & s->gdir) | (s->psr & ~s->gdir); + break; + + case GDIR_ADDR: + reg_value = s->gdir; + break; + + case PSR_ADDR: + reg_value = s->psr & ~s->gdir; + break; + + case ICR1_ADDR: + reg_value = extract64(s->icr, 0, 32); + break; + + case ICR2_ADDR: + reg_value = extract64(s->icr, 32, 32); + break; + + case IMR_ADDR: + reg_value = s->imr; + break; + + case ISR_ADDR: + reg_value = s->isr; + break; + + case EDGE_SEL_ADDR: + if (s->has_edge_sel) { + reg_value = s->edge_sel; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: EDGE_SEL register not " + "present on this version of GPIO device\n", + TYPE_IMX_GPIO, __func__); + } + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_GPIO, __func__, offset); + break; + } + + DPRINTF("(%s) = 0x%" PRIx32 "\n", imx_gpio_reg_name(offset), reg_value); + + return reg_value; +} + +static void imx_gpio_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + IMXGPIOState *s = IMX_GPIO(opaque); + + DPRINTF("(%s, value = 0x%" PRIx32 ")\n", imx_gpio_reg_name(offset), + (uint32_t)value); + + switch (offset) { + case DR_ADDR: + s->dr = value; + imx_gpio_set_all_output_lines(s); + break; + + case GDIR_ADDR: + s->gdir = value; + imx_gpio_set_all_output_lines(s); + imx_gpio_set_all_int_lines(s); + break; + + case ICR1_ADDR: + s->icr = deposit64(s->icr, 0, 32, value); + imx_gpio_set_all_int_lines(s); + break; + + case ICR2_ADDR: + s->icr = deposit64(s->icr, 32, 32, value); + imx_gpio_set_all_int_lines(s); + break; + + case IMR_ADDR: + s->imr = value; + imx_gpio_update_int(s); + break; + + case ISR_ADDR: + s->isr |= ~value; + imx_gpio_set_all_int_lines(s); + break; + + case EDGE_SEL_ADDR: + if (s->has_edge_sel) { + s->edge_sel = value; + imx_gpio_set_all_int_lines(s); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: EDGE_SEL register not " + "present on this version of GPIO device\n", + TYPE_IMX_GPIO, __func__); + } + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_GPIO, __func__, offset); + break; + } + + return; +} + +static const MemoryRegionOps imx_gpio_ops = { + .read = imx_gpio_read, + .write = imx_gpio_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription vmstate_imx_gpio = { + .name = TYPE_IMX_GPIO, + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(dr, IMXGPIOState), + VMSTATE_UINT32(gdir, IMXGPIOState), + VMSTATE_UINT32(psr, IMXGPIOState), + VMSTATE_UINT64(icr, IMXGPIOState), + VMSTATE_UINT32(imr, IMXGPIOState), + VMSTATE_UINT32(isr, IMXGPIOState), + VMSTATE_BOOL(has_edge_sel, IMXGPIOState), + VMSTATE_UINT32(edge_sel, IMXGPIOState), + VMSTATE_END_OF_LIST() + } +}; + +static Property imx_gpio_properties[] = { + DEFINE_PROP_BOOL("has-edge-sel", IMXGPIOState, has_edge_sel, true), + DEFINE_PROP_BOOL("has-upper-pin-irq", IMXGPIOState, has_upper_pin_irq, + false), + DEFINE_PROP_END_OF_LIST(), +}; + +static void imx_gpio_reset(DeviceState *dev) +{ + IMXGPIOState *s = IMX_GPIO(dev); + + s->dr = 0; + s->gdir = 0; + s->psr = 0; + s->icr = 0; + s->imr = 0; + s->isr = 0; + s->edge_sel = 0; + + imx_gpio_set_all_output_lines(s); + imx_gpio_update_int(s); +} + +static void imx_gpio_realize(DeviceState *dev, Error **errp) +{ + IMXGPIOState *s = IMX_GPIO(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &imx_gpio_ops, s, + TYPE_IMX_GPIO, IMX_GPIO_MEM_SIZE); + + qdev_init_gpio_in(DEVICE(s), imx_gpio_set, IMX_GPIO_PIN_COUNT); + qdev_init_gpio_out(DEVICE(s), s->output, IMX_GPIO_PIN_COUNT); + + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[0]); + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[1]); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); +} + +static void imx_gpio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = imx_gpio_realize; + dc->reset = imx_gpio_reset; + dc->props = imx_gpio_properties; + dc->vmsd = &vmstate_imx_gpio; + dc->desc = "i.MX GPIO controller"; +} + +static const TypeInfo imx_gpio_info = { + .name = TYPE_IMX_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMXGPIOState), + .class_init = imx_gpio_class_init, +}; + +static void imx_gpio_register_types(void) +{ + type_register_static(&imx_gpio_info); +} + +type_init(imx_gpio_register_types) diff --git a/qemu/hw/gpio/max7310.c b/qemu/hw/gpio/max7310.c index 2f59b134e..1bd5eaf91 100644 --- a/qemu/hw/gpio/max7310.c +++ b/qemu/hw/gpio/max7310.c @@ -7,6 +7,7 @@ * This file is licensed under GNU GPL. */ +#include "qemu/osdep.h" #include "hw/i2c/i2c.h" #define TYPE_MAX7310 "max7310" diff --git a/qemu/hw/gpio/mpc8xxx.c b/qemu/hw/gpio/mpc8xxx.c index 1aeaaaaf0..d14971946 100644 --- a/qemu/hw/gpio/mpc8xxx.c +++ b/qemu/hw/gpio/mpc8xxx.c @@ -19,6 +19,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #define TYPE_MPC8XXX_GPIO "mpc8xxx_gpio" diff --git a/qemu/hw/gpio/omap_gpio.c b/qemu/hw/gpio/omap_gpio.c index d92f8cfba..9b1b004fc 100644 --- a/qemu/hw/gpio/omap_gpio.c +++ b/qemu/hw/gpio/omap_gpio.c @@ -18,9 +18,11 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/arm/omap.h" #include "hw/sysbus.h" +#include "qemu/error-report.h" struct omap_gpio_s { qemu_irq irq; @@ -682,7 +684,8 @@ static int omap_gpio_init(SysBusDevice *sbd) struct omap_gpif_s *s = OMAP1_GPIO(dev); if (!s->clk) { - hw_error("omap-gpio: clk not connected\n"); + error_report("omap-gpio: clk not connected"); + return -1; } qdev_init_gpio_in(dev, omap_gpio_set, 16); qdev_init_gpio_out(dev, s->omap1.handler, 16); @@ -700,25 +703,35 @@ static int omap2_gpio_init(SysBusDevice *sbd) int i; if (!s->iclk) { - hw_error("omap2-gpio: iclk not connected\n"); + error_report("omap2-gpio: iclk not connected"); + return -1; } + + s->modulecount = s->mpu_model < omap2430 ? 4 + : s->mpu_model < omap3430 ? 5 + : 6; + + for (i = 0; i < s->modulecount; i++) { + if (!s->fclk[i]) { + error_report("omap2-gpio: fclk%d not connected", i); + return -1; + } + } + if (s->mpu_model < omap3430) { - s->modulecount = (s->mpu_model < omap2430) ? 4 : 5; memory_region_init_io(&s->iomem, OBJECT(s), &omap2_gpif_top_ops, s, "omap2.gpio", 0x1000); sysbus_init_mmio(sbd, &s->iomem); - } else { - s->modulecount = 6; } - s->modules = g_malloc0(s->modulecount * sizeof(struct omap2_gpio_s)); - s->handler = g_malloc0(s->modulecount * 32 * sizeof(qemu_irq)); + + s->modules = g_new0(struct omap2_gpio_s, s->modulecount); + s->handler = g_new0(qemu_irq, s->modulecount * 32); qdev_init_gpio_in(dev, omap2_gpio_set, s->modulecount * 32); qdev_init_gpio_out(dev, s->handler, s->modulecount * 32); + for (i = 0; i < s->modulecount; i++) { struct omap2_gpio_s *m = &s->modules[i]; - if (!s->fclk[i]) { - hw_error("omap2-gpio: fclk%d not connected\n", i); - } + m->revision = (s->mpu_model < omap3430) ? 0x18 : 0x25; m->handler = &s->handler[i * 32]; sysbus_init_irq(sbd, &m->irq[0]); /* mpu irq */ @@ -728,6 +741,7 @@ static int omap2_gpio_init(SysBusDevice *sbd) "omap.gpio-module", 0x1000); sysbus_init_mmio(sbd, &m->iomem); } + return 0; } diff --git a/qemu/hw/gpio/pl061.c b/qemu/hw/gpio/pl061.c index 4ba730b47..29dc7fc38 100644 --- a/qemu/hw/gpio/pl061.c +++ b/qemu/hw/gpio/pl061.c @@ -8,6 +8,7 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" //#define DEBUG_PL061 1 @@ -55,17 +56,17 @@ typedef struct PL061State { uint32_t slr; uint32_t den; uint32_t cr; - uint32_t float_high; uint32_t amsel; qemu_irq irq; qemu_irq out[8]; const unsigned char *id; + uint32_t rsvd_start; /* reserved area: [rsvd_start, 0xfcc] */ } PL061State; static const VMStateDescription vmstate_pl061 = { .name = "pl061", - .version_id = 3, - .minimum_version_id = 3, + .version_id = 4, + .minimum_version_id = 4, .fields = (VMStateField[]) { VMSTATE_UINT32(locked, PL061State), VMSTATE_UINT32(data, PL061State), @@ -87,7 +88,6 @@ static const VMStateDescription vmstate_pl061 = { VMSTATE_UINT32(slr, PL061State), VMSTATE_UINT32(den, PL061State), VMSTATE_UINT32(cr, PL061State), - VMSTATE_UINT32(float_high, PL061State), VMSTATE_UINT32_V(amsel, PL061State, 2), VMSTATE_END_OF_LIST() } @@ -153,12 +153,15 @@ static uint64_t pl061_read(void *opaque, hwaddr offset, { PL061State *s = (PL061State *)opaque; - if (offset >= 0xfd0 && offset < 0x1000) { - return s->id[(offset - 0xfd0) >> 2]; - } if (offset < 0x400) { return s->data & (offset >> 2); } + if (offset >= s->rsvd_start && offset <= 0xfcc) { + goto err_out; + } + if (offset >= 0xfd0 && offset < 0x1000) { + return s->id[(offset - 0xfd0) >> 2]; + } switch (offset) { case 0x400: /* Direction */ return s->dir; @@ -199,10 +202,12 @@ static uint64_t pl061_read(void *opaque, hwaddr offset, case 0x528: /* Analog mode select */ return s->amsel; default: - qemu_log_mask(LOG_GUEST_ERROR, - "pl061_read: Bad offset %x\n", (int)offset); - return 0; + break; } +err_out: + qemu_log_mask(LOG_GUEST_ERROR, + "pl061_read: Bad offset %x\n", (int)offset); + return 0; } static void pl061_write(void *opaque, hwaddr offset, @@ -217,6 +222,9 @@ static void pl061_write(void *opaque, hwaddr offset, pl061_update(s); return; } + if (offset >= s->rsvd_start) { + goto err_out; + } switch (offset) { case 0x400: /* Direction */ s->dir = value & 0xff; @@ -275,16 +283,41 @@ static void pl061_write(void *opaque, hwaddr offset, s->amsel = value & 0xff; break; default: - qemu_log_mask(LOG_GUEST_ERROR, - "pl061_write: Bad offset %x\n", (int)offset); + goto err_out; } pl061_update(s); + return; +err_out: + qemu_log_mask(LOG_GUEST_ERROR, + "pl061_write: Bad offset %x\n", (int)offset); } -static void pl061_reset(PL061State *s) +static void pl061_reset(DeviceState *dev) { - s->locked = 1; - s->cr = 0xff; + PL061State *s = PL061(dev); + + /* reset values from PL061 TRM, Stellaris LM3S5P31 & LM3S8962 Data Sheet */ + s->data = 0; + s->old_out_data = 0; + s->old_in_data = 0; + s->dir = 0; + s->isense = 0; + s->ibe = 0; + s->iev = 0; + s->im = 0; + s->istate = 0; + s->afsel = 0; + s->dr2r = 0xff; + s->dr4r = 0; + s->dr8r = 0; + s->odr = 0; + s->pur = 0; + s->pdr = 0; + s->slr = 0; + s->den = 0; + s->locked = 1; + s->cr = 0xff; + s->amsel = 0; } static void pl061_set_irq(void * opaque, int irq, int level) @@ -317,7 +350,7 @@ static int pl061_initfn(SysBusDevice *sbd) sysbus_init_irq(sbd, &s->irq); qdev_init_gpio_in(dev, pl061_set_irq, 8); qdev_init_gpio_out(dev, s->out, 8); - pl061_reset(s); + return 0; } @@ -326,6 +359,7 @@ static void pl061_luminary_init(Object *obj) PL061State *s = PL061(obj); s->id = pl061_id_luminary; + s->rsvd_start = 0x52c; } static void pl061_init(Object *obj) @@ -333,6 +367,7 @@ static void pl061_init(Object *obj) PL061State *s = PL061(obj); s->id = pl061_id; + s->rsvd_start = 0x424; } static void pl061_class_init(ObjectClass *klass, void *data) @@ -342,6 +377,7 @@ static void pl061_class_init(ObjectClass *klass, void *data) k->init = pl061_initfn; dc->vmsd = &vmstate_pl061; + dc->reset = &pl061_reset; } static const TypeInfo pl061_info = { diff --git a/qemu/hw/gpio/puv3_gpio.c b/qemu/hw/gpio/puv3_gpio.c index 39840aa73..445afccf9 100644 --- a/qemu/hw/gpio/puv3_gpio.c +++ b/qemu/hw/gpio/puv3_gpio.c @@ -8,6 +8,7 @@ * published by the Free Software Foundation, or any later version. * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" diff --git a/qemu/hw/gpio/zaurus.c b/qemu/hw/gpio/zaurus.c index 24a77272d..555da281c 100644 --- a/qemu/hw/gpio/zaurus.c +++ b/qemu/hw/gpio/zaurus.c @@ -15,6 +15,7 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/arm/sharpsl.h" #include "hw/sysbus.h" @@ -235,10 +236,6 @@ static const VMStateDescription vmstate_scoop_regs = { }, }; -static Property scoop_sysbus_properties[] = { - DEFINE_PROP_END_OF_LIST(), -}; - static void scoop_sysbus_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -247,7 +244,6 @@ static void scoop_sysbus_class_init(ObjectClass *klass, void *data) k->init = scoop_init; dc->desc = "Scoop2 Sharp custom ASIC"; dc->vmsd = &vmstate_scoop_regs; - dc->props = scoop_sysbus_properties; } static const TypeInfo scoop_sysbus_info = { diff --git a/qemu/hw/i2c/Makefile.objs b/qemu/hw/i2c/Makefile.objs index 0f130608c..aeb8f38d7 100644 --- a/qemu/hw/i2c/Makefile.objs +++ b/qemu/hw/i2c/Makefile.objs @@ -4,4 +4,5 @@ common-obj-$(CONFIG_ACPI_X86) += smbus_ich9.o common-obj-$(CONFIG_APM) += pm_smbus.o common-obj-$(CONFIG_BITBANG_I2C) += bitbang_i2c.o common-obj-$(CONFIG_EXYNOS4) += exynos4210_i2c.o +common-obj-$(CONFIG_IMX_I2C) += imx_i2c.o obj-$(CONFIG_OMAP) += omap_i2c.o diff --git a/qemu/hw/i2c/bitbang_i2c.c b/qemu/hw/i2c/bitbang_i2c.c index 6d1bb03d6..6ed206020 100644 --- a/qemu/hw/i2c/bitbang_i2c.c +++ b/qemu/hw/i2c/bitbang_i2c.c @@ -9,6 +9,7 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "bitbang_i2c.h" #include "hw/sysbus.h" diff --git a/qemu/hw/i2c/core.c b/qemu/hw/i2c/core.c index 5a6402634..ba22104af 100644 --- a/qemu/hw/i2c/core.c +++ b/qemu/hw/i2c/core.c @@ -7,6 +7,7 @@ * This code is licensed under the LGPL. */ +#include "qemu/osdep.h" #include "hw/i2c/i2c.h" struct I2CBus diff --git a/qemu/hw/i2c/exynos4210_i2c.c b/qemu/hw/i2c/exynos4210_i2c.c index fb99dfda1..8c2a2c163 100644 --- a/qemu/hw/i2c/exynos4210_i2c.c +++ b/qemu/hw/i2c/exynos4210_i2c.c @@ -20,6 +20,7 @@ * */ +#include "qemu/osdep.h" #include "qemu/timer.h" #include "hw/sysbus.h" #include "hw/i2c/i2c.h" diff --git a/qemu/hw/i2c/imx_i2c.c b/qemu/hw/i2c/imx_i2c.c new file mode 100644 index 000000000..a01e43ebe --- /dev/null +++ b/qemu/hw/i2c/imx_i2c.c @@ -0,0 +1,337 @@ +/* + * i.MX I2C Bus Serial Interface Emulation + * + * Copyright (C) 2013 Jean-Christophe Dubois. + * + * 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 . + * + */ + +#include "qemu/osdep.h" +#include "hw/i2c/imx_i2c.h" +#include "hw/i2c/i2c.h" + +#ifndef DEBUG_IMX_I2C +#define DEBUG_IMX_I2C 0 +#endif + +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_I2C) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_I2C, \ + __func__, ##args); \ + } \ + } while (0) + +static const char *imx_i2c_get_regname(unsigned offset) +{ + switch (offset) { + case IADR_ADDR: + return "IADR"; + case IFDR_ADDR: + return "IFDR"; + case I2CR_ADDR: + return "I2CR"; + case I2SR_ADDR: + return "I2SR"; + case I2DR_ADDR: + return "I2DR"; + default: + return "[?]"; + } +} + +static inline bool imx_i2c_is_enabled(IMXI2CState *s) +{ + return s->i2cr & I2CR_IEN; +} + +static inline bool imx_i2c_interrupt_is_enabled(IMXI2CState *s) +{ + return s->i2cr & I2CR_IIEN; +} + +static inline bool imx_i2c_is_master(IMXI2CState *s) +{ + return s->i2cr & I2CR_MSTA; +} + +static void imx_i2c_reset(DeviceState *dev) +{ + IMXI2CState *s = IMX_I2C(dev); + + if (s->address != ADDR_RESET) { + i2c_end_transfer(s->bus); + } + + s->address = ADDR_RESET; + s->iadr = IADR_RESET; + s->ifdr = IFDR_RESET; + s->i2cr = I2CR_RESET; + s->i2sr = I2SR_RESET; + s->i2dr_read = I2DR_RESET; + s->i2dr_write = I2DR_RESET; +} + +static inline void imx_i2c_raise_interrupt(IMXI2CState *s) +{ + /* + * raise an interrupt if the device is enabled and it is configured + * to generate some interrupts. + */ + if (imx_i2c_is_enabled(s) && imx_i2c_interrupt_is_enabled(s)) { + s->i2sr |= I2SR_IIF; + qemu_irq_raise(s->irq); + } +} + +static uint64_t imx_i2c_read(void *opaque, hwaddr offset, + unsigned size) +{ + uint16_t value; + IMXI2CState *s = IMX_I2C(opaque); + + switch (offset) { + case IADR_ADDR: + value = s->iadr; + break; + case IFDR_ADDR: + value = s->ifdr; + break; + case I2CR_ADDR: + value = s->i2cr; + break; + case I2SR_ADDR: + value = s->i2sr; + break; + case I2DR_ADDR: + value = s->i2dr_read; + + if (imx_i2c_is_master(s)) { + int ret = 0xff; + + if (s->address == ADDR_RESET) { + /* something is wrong as the address is not set */ + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read " + "without specifying the slave address\n", + TYPE_IMX_I2C, __func__); + } else if (s->i2cr & I2CR_MTX) { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read " + "but MTX is set\n", TYPE_IMX_I2C, __func__); + } else { + /* get the next byte */ + ret = i2c_recv(s->bus); + + if (ret >= 0) { + imx_i2c_raise_interrupt(s); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: read failed " + "for device 0x%02x\n", TYPE_IMX_I2C, + __func__, s->address); + ret = 0xff; + } + } + + s->i2dr_read = ret; + } else { + qemu_log_mask(LOG_UNIMP, "[%s]%s: slave mode not implemented\n", + TYPE_IMX_I2C, __func__); + } + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_I2C, __func__, offset); + value = 0; + break; + } + + DPRINTF("read %s [0x%" HWADDR_PRIx "] -> 0x%02x\n", + imx_i2c_get_regname(offset), offset, value); + + return (uint64_t)value; +} + +static void imx_i2c_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + IMXI2CState *s = IMX_I2C(opaque); + + DPRINTF("write %s [0x%" HWADDR_PRIx "] <- 0x%02x\n", + imx_i2c_get_regname(offset), offset, (int)value); + + value &= 0xff; + + switch (offset) { + case IADR_ADDR: + s->iadr = value & IADR_MASK; + /* i2c_set_slave_address(s->bus, (uint8_t)s->iadr); */ + break; + case IFDR_ADDR: + s->ifdr = value & IFDR_MASK; + break; + case I2CR_ADDR: + if (imx_i2c_is_enabled(s) && ((value & I2CR_IEN) == 0)) { + /* This is a soft reset. IADR is preserved during soft resets */ + uint16_t iadr = s->iadr; + imx_i2c_reset(DEVICE(s)); + s->iadr = iadr; + } else { /* normal write */ + s->i2cr = value & I2CR_MASK; + + if (imx_i2c_is_master(s)) { + /* set the bus to busy */ + s->i2sr |= I2SR_IBB; + } else { /* slave mode */ + /* bus is not busy anymore */ + s->i2sr &= ~I2SR_IBB; + + /* + * if we unset the master mode then it ends the ongoing + * transfer if any + */ + if (s->address != ADDR_RESET) { + i2c_end_transfer(s->bus); + s->address = ADDR_RESET; + } + } + + if (s->i2cr & I2CR_RSTA) { /* Restart */ + /* if this is a restart then it ends the ongoing transfer */ + if (s->address != ADDR_RESET) { + i2c_end_transfer(s->bus); + s->address = ADDR_RESET; + s->i2cr &= ~I2CR_RSTA; + } + } + } + break; + case I2SR_ADDR: + /* + * if the user writes 0 to IIF then lower the interrupt and + * reset the bit + */ + if ((s->i2sr & I2SR_IIF) && !(value & I2SR_IIF)) { + s->i2sr &= ~I2SR_IIF; + qemu_irq_lower(s->irq); + } + + /* + * if the user writes 0 to IAL, reset the bit + */ + if ((s->i2sr & I2SR_IAL) && !(value & I2SR_IAL)) { + s->i2sr &= ~I2SR_IAL; + } + + break; + case I2DR_ADDR: + /* if the device is not enabled, nothing to do */ + if (!imx_i2c_is_enabled(s)) { + break; + } + + s->i2dr_write = value & I2DR_MASK; + + if (imx_i2c_is_master(s)) { + /* If this is the first write cycle then it is the slave addr */ + if (s->address == ADDR_RESET) { + if (i2c_start_transfer(s->bus, extract32(s->i2dr_write, 1, 7), + extract32(s->i2dr_write, 0, 1))) { + /* if non zero is returned, the adress is not valid */ + s->i2sr |= I2SR_RXAK; + } else { + s->address = s->i2dr_write; + s->i2sr &= ~I2SR_RXAK; + imx_i2c_raise_interrupt(s); + } + } else { /* This is a normal data write */ + if (i2c_send(s->bus, s->i2dr_write)) { + /* if the target return non zero then end the transfer */ + s->i2sr |= I2SR_RXAK; + s->address = ADDR_RESET; + i2c_end_transfer(s->bus); + } else { + s->i2sr &= ~I2SR_RXAK; + imx_i2c_raise_interrupt(s); + } + } + } else { + qemu_log_mask(LOG_UNIMP, "[%s]%s: slave mode not implemented\n", + TYPE_IMX_I2C, __func__); + } + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_I2C, __func__, offset); + break; + } +} + +static const MemoryRegionOps imx_i2c_ops = { + .read = imx_i2c_read, + .write = imx_i2c_write, + .valid.min_access_size = 1, + .valid.max_access_size = 2, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription imx_i2c_vmstate = { + .name = TYPE_IMX_I2C, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT16(address, IMXI2CState), + VMSTATE_UINT16(iadr, IMXI2CState), + VMSTATE_UINT16(ifdr, IMXI2CState), + VMSTATE_UINT16(i2cr, IMXI2CState), + VMSTATE_UINT16(i2sr, IMXI2CState), + VMSTATE_UINT16(i2dr_read, IMXI2CState), + VMSTATE_UINT16(i2dr_write, IMXI2CState), + VMSTATE_END_OF_LIST() + } +}; + +static void imx_i2c_realize(DeviceState *dev, Error **errp) +{ + IMXI2CState *s = IMX_I2C(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &imx_i2c_ops, s, TYPE_IMX_I2C, + IMX_I2C_MEM_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); + s->bus = i2c_init_bus(DEVICE(dev), "i2c"); +} + +static void imx_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &imx_i2c_vmstate; + dc->reset = imx_i2c_reset; + dc->realize = imx_i2c_realize; + dc->desc = "i.MX I2C Controller"; +} + +static const TypeInfo imx_i2c_type_info = { + .name = TYPE_IMX_I2C, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMXI2CState), + .class_init = imx_i2c_class_init, +}; + +static void imx_i2c_register_types(void) +{ + type_register_static(&imx_i2c_type_info); +} + +type_init(imx_i2c_register_types) diff --git a/qemu/hw/i2c/omap_i2c.c b/qemu/hw/i2c/omap_i2c.c index b6f544a22..67fbbff8e 100644 --- a/qemu/hw/i2c/omap_i2c.c +++ b/qemu/hw/i2c/omap_i2c.c @@ -16,10 +16,12 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i2c/i2c.h" #include "hw/arm/omap.h" #include "hw/sysbus.h" +#include "qemu/error-report.h" #define TYPE_OMAP_I2C "omap_i2c" #define OMAP_I2C(obj) OBJECT_CHECK(OMAPI2CState, (obj), TYPE_OMAP_I2C) @@ -449,12 +451,15 @@ static int omap_i2c_init(SysBusDevice *sbd) OMAPI2CState *s = OMAP_I2C(dev); if (!s->fclk) { - hw_error("omap_i2c: fclk not connected\n"); + error_report("omap_i2c: fclk not connected"); + return -1; } if (s->revision >= OMAP2_INTR_REV && !s->iclk) { /* Note that OMAP1 doesn't have a separate interface clock */ - hw_error("omap_i2c: iclk not connected\n"); + error_report("omap_i2c: iclk not connected"); + return -1; } + sysbus_init_irq(sbd, &s->irq); sysbus_init_irq(sbd, &s->drq[0]); sysbus_init_irq(sbd, &s->drq[1]); diff --git a/qemu/hw/i2c/pm_smbus.c b/qemu/hw/i2c/pm_smbus.c index ce1713d26..6fc3923f5 100644 --- a/qemu/hw/i2c/pm_smbus.c +++ b/qemu/hw/i2c/pm_smbus.c @@ -17,6 +17,7 @@ * License along with this library; if not, see * . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/i2c/pm_smbus.h" diff --git a/qemu/hw/i2c/smbus.c b/qemu/hw/i2c/smbus.c index 6e27ae8bd..3979b3dad 100644 --- a/qemu/hw/i2c/smbus.c +++ b/qemu/hw/i2c/smbus.c @@ -9,6 +9,7 @@ /* TODO: Implement PEC. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i2c/i2c.h" #include "hw/i2c/smbus.h" diff --git a/qemu/hw/i2c/smbus_eeprom.c b/qemu/hw/i2c/smbus_eeprom.c index 72c09cba6..5b7bd891b 100644 --- a/qemu/hw/i2c/smbus_eeprom.c +++ b/qemu/hw/i2c/smbus_eeprom.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i2c/i2c.h" #include "hw/i2c/smbus.h" diff --git a/qemu/hw/i2c/smbus_ich9.c b/qemu/hw/i2c/smbus_ich9.c index 91d4d322c..498f03e83 100644 --- a/qemu/hw/i2c/smbus_ich9.c +++ b/qemu/hw/i2c/smbus_ich9.c @@ -24,6 +24,7 @@ * GNU GPL, version 2 or (at your option) any later version. * */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/i2c/pm_smbus.h" diff --git a/qemu/hw/i2c/versatile_i2c.c b/qemu/hw/i2c/versatile_i2c.c index 3c0c2c106..fee3bc761 100644 --- a/qemu/hw/i2c/versatile_i2c.c +++ b/qemu/hw/i2c/versatile_i2c.c @@ -21,6 +21,7 @@ * */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "bitbang_i2c.h" diff --git a/qemu/hw/i386/Makefile.objs b/qemu/hw/i386/Makefile.objs index bd4f147f9..b52d5b875 100644 --- a/qemu/hw/i386/Makefile.objs +++ b/qemu/hw/i386/Makefile.objs @@ -1,5 +1,5 @@ obj-$(CONFIG_KVM) += kvm/ -obj-y += multiboot.o smbios.o +obj-y += multiboot.o obj-y += pc.o pc_piix.o pc_q35.o obj-y += pc_sysfw.o obj-y += intel_iommu.o @@ -7,27 +7,4 @@ obj-$(CONFIG_XEN) += ../xenpv/ xen/ obj-y += kvmvapic.o obj-y += acpi-build.o -hw/i386/acpi-build.o: hw/i386/acpi-build.c \ - hw/i386/acpi-dsdt.hex hw/i386/q35-acpi-dsdt.hex - -iasl-option=$(shell if test -z "`$(1) $(2) 2>&1 > /dev/null`" \ - ; then echo "$(2)"; else echo "$(3)"; fi ;) - -ifdef IASL -#IASL Present. Generate hex files from .dsl -hw/i386/%.hex: $(SRC_PATH)/hw/i386/%.dsl $(SRC_PATH)/scripts/acpi_extract_preprocess.py $(SRC_PATH)/scripts/acpi_extract.py - $(call quiet-command, $(CPP) -x c -P $(QEMU_DGFLAGS) $(QEMU_INCLUDES) $< -o $*.dsl.i.orig, " CPP $(TARGET_DIR)$*.dsl.i.orig") - $(call quiet-command, $(PYTHON) $(SRC_PATH)/scripts/acpi_extract_preprocess.py $*.dsl.i.orig > $*.dsl.i, " ACPI_PREPROCESS $(TARGET_DIR)$*.dsl.i") - $(call quiet-command, $(IASL) $(call iasl-option,$(IASL),-Pn,) -vs -l -tc -p $* $*.dsl.i $(if $(V), , > /dev/null) 2>&1 ," IASL $(TARGET_DIR)$*.dsl.i") - $(call quiet-command, $(PYTHON) $(SRC_PATH)/scripts/acpi_extract.py $*.lst > $*.off, " ACPI_EXTRACT $(TARGET_DIR)$*.off") - $(call quiet-command, cat $*.off > $@, " CAT $(TARGET_DIR)$@") -else -#IASL Not present. Restore pre-generated hex files. -hw/i386/%.hex: $(SRC_PATH)/hw/i386/%.hex.generated - $(call quiet-command, cp -f $< $@, " CP $(TARGET_DIR)$@") -endif - -.PHONY: cleanhex -cleanhex: - rm -f hw/i386/*hex -clean: cleanhex +obj-y += pci-assign-load-rom.o diff --git a/qemu/hw/i386/acpi-build.c b/qemu/hw/i386/acpi-build.c index 46eddb8e4..64770034f 100644 --- a/qemu/hw/i386/acpi-build.c +++ b/qemu/hw/i386/acpi-build.c @@ -20,12 +20,12 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "acpi-build.h" -#include #include #include "qemu-common.h" #include "qemu/bitmap.h" -#include "qemu/osdep.h" #include "qemu/error-report.h" #include "hw/pci/pci.h" #include "qom/cpu.h" @@ -38,10 +38,12 @@ #include "hw/acpi/bios-linker-loader.h" #include "hw/loader.h" #include "hw/isa/isa.h" +#include "hw/block/fdc.h" #include "hw/acpi/memory_hotplug.h" #include "sysemu/tpm.h" #include "hw/acpi/tpm.h" #include "sysemu/tpm_backend.h" +#include "hw/timer/mc146818rtc_regs.h" /* Supported chipsets: */ #include "hw/acpi/piix4.h" @@ -50,9 +52,7 @@ #include "hw/pci/pci_bus.h" #include "hw/pci-host/q35.h" #include "hw/i386/intel_iommu.h" - -#include "hw/i386/q35-acpi-dsdt.hex" -#include "hw/i386/acpi-dsdt.hex" +#include "hw/timer/hpet.h" #include "hw/acpi/aml-build.h" @@ -77,10 +77,6 @@ #define ACPI_BUILD_DPRINTF(fmt, ...) #endif -typedef struct AcpiCpuInfo { - DECLARE_BITMAP(found_cpus, ACPI_CPU_HOTPLUG_ID_LIMIT); -} AcpiCpuInfo; - typedef struct AcpiMcfgInfo { uint64_t mcfg_base; uint32_t mcfg_size; @@ -106,6 +102,7 @@ typedef struct AcpiPmInfo { } AcpiPmInfo; typedef struct AcpiMiscInfo { + bool is_piix4; bool has_hpet; TPMVersion tpm_version; const unsigned char *dsdt_code; @@ -121,47 +118,6 @@ typedef struct AcpiBuildPciBusHotplugState { bool pcihp_bridge_en; } AcpiBuildPciBusHotplugState; -static void acpi_get_dsdt(AcpiMiscInfo *info) -{ - Object *piix = piix4_pm_find(); - Object *lpc = ich9_lpc_find(); - assert(!!piix != !!lpc); - - if (piix) { - info->dsdt_code = AcpiDsdtAmlCode; - info->dsdt_size = sizeof AcpiDsdtAmlCode; - } - if (lpc) { - info->dsdt_code = Q35AcpiDsdtAmlCode; - info->dsdt_size = sizeof Q35AcpiDsdtAmlCode; - } -} - -static -int acpi_add_cpu_info(Object *o, void *opaque) -{ - AcpiCpuInfo *cpu = opaque; - uint64_t apic_id; - - if (object_dynamic_cast(o, TYPE_CPU)) { - apic_id = object_property_get_int(o, "apic-id", NULL); - assert(apic_id < ACPI_CPU_HOTPLUG_ID_LIMIT); - - set_bit(apic_id, cpu->found_cpus); - } - - object_child_foreach(o, acpi_add_cpu_info, opaque); - return 0; -} - -static void acpi_get_cpu_info(AcpiCpuInfo *cpu) -{ - Object *root = object_get_root(); - - memset(cpu->found_cpus, 0, sizeof cpu->found_cpus); - object_child_foreach(root, acpi_add_cpu_info, cpu); -} - static void acpi_get_pm_info(AcpiPmInfo *pm) { Object *piix = piix4_pm_find(); @@ -169,6 +125,7 @@ static void acpi_get_pm_info(AcpiPmInfo *pm) Object *obj = NULL; QObject *o; + pm->cpu_hp_io_base = 0; pm->pcihp_io_base = 0; pm->pcihp_io_len = 0; if (piix) { @@ -234,6 +191,17 @@ static void acpi_get_pm_info(AcpiPmInfo *pm) static void acpi_get_misc_info(AcpiMiscInfo *info) { + Object *piix = piix4_pm_find(); + Object *lpc = ich9_lpc_find(); + assert(!!piix != !!lpc); + + if (piix) { + info->is_piix4 = true; + } + if (lpc) { + info->is_piix4 = false; + } + info->has_hpet = hpet_find(); info->tpm_version = tpm_get_version(); info->pvpanic_port = pvpanic_port(); @@ -294,7 +262,7 @@ static void acpi_align_size(GArray *blob, unsigned align) /* FACS */ static void -build_facs(GArray *table_data, GArray *linker, PcGuestInfo *guest_info) +build_facs(GArray *table_data, GArray *linker) { AcpiFacsDescriptorRev1 *facs = acpi_data_push(table_data, sizeof *facs); memcpy(&facs->signature, "FACS", 4); @@ -333,13 +301,15 @@ static void fadt_setup(AcpiFadtDescriptorRev1 *fadt, AcpiPmInfo *pm) if (max_cpus > 8) { fadt->flags |= cpu_to_le32(1 << ACPI_FADT_F_FORCE_APIC_CLUSTER_MODEL); } + fadt->century = RTC_CENTURY; } /* FADT */ static void build_fadt(GArray *table_data, GArray *linker, AcpiPmInfo *pm, - unsigned facs, unsigned dsdt) + unsigned facs, unsigned dsdt, + const char *oem_id, const char *oem_table_id) { AcpiFadtDescriptorRev1 *fadt = acpi_data_push(table_data, sizeof(*fadt)); @@ -360,13 +330,14 @@ build_fadt(GArray *table_data, GArray *linker, AcpiPmInfo *pm, fadt_setup(fadt, pm); build_header(linker, table_data, - (void *)fadt, "FACP", sizeof(*fadt), 1); + (void *)fadt, "FACP", sizeof(*fadt), 1, oem_id, oem_table_id); } static void -build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu, - PcGuestInfo *guest_info) +build_madt(GArray *table_data, GArray *linker, PCMachineState *pcms) { + MachineClass *mc = MACHINE_GET_CLASS(pcms); + CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(MACHINE(pcms)); int madt_start = table_data->len; AcpiMultipleApicTable *madt; @@ -379,18 +350,28 @@ build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu, madt->local_apic_address = cpu_to_le32(APIC_DEFAULT_ADDRESS); madt->flags = cpu_to_le32(1); - for (i = 0; i < guest_info->apic_id_limit; i++) { + for (i = 0; i < apic_ids->len; i++) { AcpiMadtProcessorApic *apic = acpi_data_push(table_data, sizeof *apic); + int apic_id = apic_ids->cpus[i].arch_id; + apic->type = ACPI_APIC_PROCESSOR; apic->length = sizeof(*apic); - apic->processor_id = i; - apic->local_apic_id = i; - if (test_bit(i, cpu->found_cpus)) { + apic->processor_id = apic_id; + apic->local_apic_id = apic_id; + if (apic_ids->cpus[i].cpu != NULL) { apic->flags = cpu_to_le32(1); } else { + /* ACPI spec says that LAPIC entry for non present + * CPU may be omitted from MADT or it must be marked + * as disabled. However omitting non present CPU from + * MADT breaks hotplug on linux. So possible CPUs + * should be put in MADT but kept disabled. + */ apic->flags = cpu_to_le32(0); } } + g_free(apic_ids); + io_apic = acpi_data_push(table_data, sizeof *io_apic); io_apic->type = ACPI_APIC_IO; io_apic->length = sizeof(*io_apic); @@ -399,7 +380,7 @@ build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu, io_apic->address = cpu_to_le32(IO_APIC_DEFAULT_ADDRESS); io_apic->interrupt = cpu_to_le32(0); - if (guest_info->apic_xrupt_override) { + if (pcms->apic_xrupt_override) { intsrcovr = acpi_data_push(table_data, sizeof *intsrcovr); intsrcovr->type = ACPI_APIC_XRUPT_OVERRIDE; intsrcovr->length = sizeof(*intsrcovr); @@ -430,7 +411,7 @@ build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu, build_header(linker, table_data, (void *)(table_data->data + madt_start), "APIC", - table_data->len - madt_start, 1); + table_data->len - madt_start, 1, NULL, NULL); } /* Assign BSEL property to all buses. In the future, this can be changed @@ -468,7 +449,7 @@ static void build_append_pcihp_notify_entry(Aml *method, int slot) Aml *if_ctx; int32_t devfn = PCI_DEVFN(slot, 0); - if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot))); + if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot), NULL)); aml_append(if_ctx, aml_notify(aml_name("S%.02X", devfn), aml_arg(1))); aml_append(method, if_ctx); } @@ -486,7 +467,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, int64_t bsel_val = qint_get_int(qobject_to_qint(bsel)); aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val))); - notify_method = aml_method("DVNT", 2); + notify_method = aml_method("DVNT", 2, AML_NOTSERIALIZED); } for (i = 0; i < ARRAY_SIZE(bus->devices); i += PCI_FUNC_MAX) { @@ -502,7 +483,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, dev = aml_device("S%.02X", PCI_DEVFN(slot, 0)); aml_append(dev, aml_name_decl("_SUN", aml_int(slot))); aml_append(dev, aml_name_decl("_ADR", aml_int(slot << 16))); - method = aml_method("_EJ0", 1); + method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); aml_append(method, aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN")) ); @@ -545,22 +526,22 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, s3d = 0; } - method = aml_method("_S1D", 0); + method = aml_method("_S1D", 0, AML_NOTSERIALIZED); aml_append(method, aml_return(aml_int(0))); aml_append(dev, method); - method = aml_method("_S2D", 0); + method = aml_method("_S2D", 0, AML_NOTSERIALIZED); aml_append(method, aml_return(aml_int(0))); aml_append(dev, method); - method = aml_method("_S3D", 0); + method = aml_method("_S3D", 0, AML_NOTSERIALIZED); aml_append(method, aml_return(aml_int(s3d))); aml_append(dev, method); } else if (hotplug_enabled_dev) { /* add _SUN/_EJ0 to make slot hotpluggable */ aml_append(dev, aml_name_decl("_SUN", aml_int(slot))); - method = aml_method("_EJ0", 1); + method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); aml_append(method, aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN")) ); @@ -589,7 +570,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, /* Append PCNT method to notify about events on local and child buses. * Add unconditionally for root since DSDT expects it. */ - method = aml_method("PCNT", 0); + method = aml_method("PCNT", 0, AML_NOTSERIALIZED); /* If bus supports hotplug select it and notify about local events */ if (bsel) { @@ -615,6 +596,23 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, qobject_decref(bsel); } +/** + * build_prt_entry: + * @link_name: link name for PCI route entry + * + * build AML package containing a PCI route entry for @link_name + */ +static Aml *build_prt_entry(const char *link_name) +{ + Aml *a_zero = aml_int(0); + Aml *pkg = aml_package(4); + aml_append(pkg, a_zero); + aml_append(pkg, a_zero); + aml_append(pkg, aml_name("%s", link_name)); + aml_append(pkg, a_zero); + return pkg; +} + /* * initialize_route - Initialize the interrupt routing rule * through a specific LINK: @@ -625,12 +623,8 @@ static Aml *initialize_route(Aml *route, const char *link_name, Aml *lnk_idx, int idx) { Aml *if_ctx = aml_if(aml_equal(lnk_idx, aml_int(idx))); - Aml *pkg = aml_package(4); + Aml *pkg = build_prt_entry(link_name); - aml_append(pkg, aml_int(0)); - aml_append(pkg, aml_int(0)); - aml_append(pkg, aml_name("%s", link_name)); - aml_append(pkg, aml_int(0)); aml_append(if_ctx, aml_store(pkg, route)); return if_ctx; @@ -646,11 +640,11 @@ static Aml *initialize_route(Aml *route, const char *link_name, * The hash function is (slot + pin) & 3 -> "LNK[D|A|B|C]". * */ -static Aml *build_prt(void) +static Aml *build_prt(bool is_pci0_prt) { Aml *method, *while_ctx, *pin, *res; - method = aml_method("_PRT", 0); + method = aml_method("_PRT", 0, AML_NOTSERIALIZED); res = aml_local(0); pin = aml_local(1); aml_append(method, aml_store(aml_package(128), res)); @@ -665,24 +659,49 @@ static Aml *build_prt(void) /* slot = pin >> 2 */ aml_append(while_ctx, - aml_store(aml_shiftright(pin, aml_int(2)), slot)); + aml_store(aml_shiftright(pin, aml_int(2), NULL), slot)); /* lnk_idx = (slot + pin) & 3 */ aml_append(while_ctx, - aml_store(aml_and(aml_add(pin, slot), aml_int(3)), lnk_idx)); + aml_store(aml_and(aml_add(pin, slot, NULL), aml_int(3), NULL), + lnk_idx)); /* route[2] = "LNK[D|A|B|C]", selection based on pin % 3 */ aml_append(while_ctx, initialize_route(route, "LNKD", lnk_idx, 0)); - aml_append(while_ctx, initialize_route(route, "LNKA", lnk_idx, 1)); + if (is_pci0_prt) { + Aml *if_device_1, *if_pin_4, *else_pin_4; + + /* device 1 is the power-management device, needs SCI */ + if_device_1 = aml_if(aml_equal(lnk_idx, aml_int(1))); + { + if_pin_4 = aml_if(aml_equal(pin, aml_int(4))); + { + aml_append(if_pin_4, + aml_store(build_prt_entry("LNKS"), route)); + } + aml_append(if_device_1, if_pin_4); + else_pin_4 = aml_else(); + { + aml_append(else_pin_4, + aml_store(build_prt_entry("LNKA"), route)); + } + aml_append(if_device_1, else_pin_4); + } + aml_append(while_ctx, if_device_1); + } else { + aml_append(while_ctx, initialize_route(route, "LNKA", lnk_idx, 1)); + } aml_append(while_ctx, initialize_route(route, "LNKB", lnk_idx, 2)); aml_append(while_ctx, initialize_route(route, "LNKC", lnk_idx, 3)); /* route[0] = 0x[slot]FFFF */ aml_append(while_ctx, - aml_store(aml_or(aml_shiftleft(slot, aml_int(16)), aml_int(0xFFFF)), + aml_store(aml_or(aml_shiftleft(slot, aml_int(16)), aml_int(0xFFFF), + NULL), aml_index(route, aml_int(0)))); /* route[1] = pin & 3 */ aml_append(while_ctx, - aml_store(aml_and(pin, aml_int(3)), aml_index(route, aml_int(1)))); + aml_store(aml_and(pin, aml_int(3), NULL), + aml_index(route, aml_int(1)))); /* res[pin] = route */ aml_append(while_ctx, aml_store(route, aml_index(res, pin))); /* pin++ */ @@ -761,16 +780,59 @@ static void crs_replace_with_free_ranges(GPtrArray *ranges, g_ptr_array_free(free_ranges, false); } +/* + * crs_range_merge - merges adjacent ranges in the given array. + * Array elements are deleted and replaced with the merged ranges. + */ +static void crs_range_merge(GPtrArray *range) +{ + GPtrArray *tmp = g_ptr_array_new_with_free_func(crs_range_free); + CrsRangeEntry *entry; + uint64_t range_base, range_limit; + int i; + + if (!range->len) { + return; + } + + g_ptr_array_sort(range, crs_range_compare); + + entry = g_ptr_array_index(range, 0); + range_base = entry->base; + range_limit = entry->limit; + for (i = 1; i < range->len; i++) { + entry = g_ptr_array_index(range, i); + if (entry->base - 1 == range_limit) { + range_limit = entry->limit; + } else { + crs_range_insert(tmp, range_base, range_limit); + range_base = entry->base; + range_limit = entry->limit; + } + } + crs_range_insert(tmp, range_base, range_limit); + + g_ptr_array_set_size(range, 0); + for (i = 0; i < tmp->len; i++) { + entry = g_ptr_array_index(tmp, i); + crs_range_insert(range, entry->base, entry->limit); + } + g_ptr_array_free(tmp, true); +} + static Aml *build_crs(PCIHostState *host, GPtrArray *io_ranges, GPtrArray *mem_ranges) { Aml *crs = aml_resource_template(); + GPtrArray *host_io_ranges = g_ptr_array_new_with_free_func(crs_range_free); + GPtrArray *host_mem_ranges = g_ptr_array_new_with_free_func(crs_range_free); + CrsRangeEntry *entry; uint8_t max_bus = pci_bus_num(host->bus); uint8_t type; int devfn; + int i; for (devfn = 0; devfn < ARRAY_SIZE(host->bus->devices); devfn++) { - int i; uint64_t range_base, range_limit; PCIDevice *dev = host->bus->devices[devfn]; @@ -793,26 +855,9 @@ static Aml *build_crs(PCIHostState *host, } if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { - aml_append(crs, - aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED, - AML_POS_DECODE, AML_ENTIRE_RANGE, - 0, - range_base, - range_limit, - 0, - range_limit - range_base + 1)); - crs_range_insert(io_ranges, range_base, range_limit); + crs_range_insert(host_io_ranges, range_base, range_limit); } else { /* "memory" */ - aml_append(crs, - aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, - AML_MAX_FIXED, AML_NON_CACHEABLE, - AML_READ_WRITE, - 0, - range_base, - range_limit, - 0, - range_limit - range_base + 1)); - crs_range_insert(mem_ranges, range_base, range_limit); + crs_range_insert(host_mem_ranges, range_base, range_limit); } } @@ -831,15 +876,7 @@ static Aml *build_crs(PCIHostState *host, * that do not support multiple root buses */ if (range_base && range_base <= range_limit) { - aml_append(crs, - aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED, - AML_POS_DECODE, AML_ENTIRE_RANGE, - 0, - range_base, - range_limit, - 0, - range_limit - range_base + 1)); - crs_range_insert(io_ranges, range_base, range_limit); + crs_range_insert(host_io_ranges, range_base, range_limit); } range_base = @@ -852,16 +889,7 @@ static Aml *build_crs(PCIHostState *host, * that do not support multiple root buses */ if (range_base && range_base <= range_limit) { - aml_append(crs, - aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, - AML_MAX_FIXED, AML_NON_CACHEABLE, - AML_READ_WRITE, - 0, - range_base, - range_limit, - 0, - range_limit - range_base + 1)); - crs_range_insert(mem_ranges, range_base, range_limit); + crs_range_insert(host_mem_ranges, range_base, range_limit); } range_base = @@ -874,20 +902,36 @@ static Aml *build_crs(PCIHostState *host, * that do not support multiple root buses */ if (range_base && range_base <= range_limit) { - aml_append(crs, - aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, - AML_MAX_FIXED, AML_NON_CACHEABLE, - AML_READ_WRITE, - 0, - range_base, - range_limit, - 0, - range_limit - range_base + 1)); - crs_range_insert(mem_ranges, range_base, range_limit); + crs_range_insert(host_mem_ranges, range_base, range_limit); } } } + crs_range_merge(host_io_ranges); + for (i = 0; i < host_io_ranges->len; i++) { + entry = g_ptr_array_index(host_io_ranges, i); + aml_append(crs, + aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED, + AML_POS_DECODE, AML_ENTIRE_RANGE, + 0, entry->base, entry->limit, 0, + entry->limit - entry->base + 1)); + crs_range_insert(io_ranges, entry->base, entry->limit); + } + g_ptr_array_free(host_io_ranges, true); + + crs_range_merge(host_mem_ranges); + for (i = 0; i < host_mem_ranges->len; i++) { + entry = g_ptr_array_index(host_mem_ranges, i); + aml_append(crs, + aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, + AML_MAX_FIXED, AML_NON_CACHEABLE, + AML_READ_WRITE, + 0, entry->base, entry->limit, 0, + entry->limit - entry->base + 1)); + crs_range_insert(mem_ranges, entry->base, entry->limit); + } + g_ptr_array_free(host_mem_ranges, true); + aml_append(crs, aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, 0, @@ -899,33 +943,1151 @@ static Aml *build_crs(PCIHostState *host, return crs; } +static void build_processor_devices(Aml *sb_scope, MachineState *machine, + AcpiPmInfo *pm) +{ + int i, apic_idx; + Aml *dev; + Aml *crs; + Aml *pkg; + Aml *field; + Aml *ifctx; + Aml *method; + MachineClass *mc = MACHINE_GET_CLASS(machine); + CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(machine); + PCMachineState *pcms = PC_MACHINE(machine); + + /* The current AML generator can cover the APIC ID range [0..255], + * inclusive, for VCPU hotplug. */ + QEMU_BUILD_BUG_ON(ACPI_CPU_HOTPLUG_ID_LIMIT > 256); + g_assert(pcms->apic_id_limit <= ACPI_CPU_HOTPLUG_ID_LIMIT); + + /* create PCI0.PRES device and its _CRS to reserve CPU hotplug MMIO */ + dev = aml_device("PCI0." stringify(CPU_HOTPLUG_RESOURCE_DEVICE)); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A06"))); + aml_append(dev, + aml_name_decl("_UID", aml_string("CPU Hotplug resources")) + ); + /* device present, functioning, decoding, not shown in UI */ + aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); + crs = aml_resource_template(); + aml_append(crs, + aml_io(AML_DECODE16, pm->cpu_hp_io_base, pm->cpu_hp_io_base, 1, + pm->cpu_hp_io_len) + ); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(sb_scope, dev); + /* declare CPU hotplug MMIO region and PRS field to access it */ + aml_append(sb_scope, aml_operation_region( + "PRST", AML_SYSTEM_IO, aml_int(pm->cpu_hp_io_base), pm->cpu_hp_io_len)); + field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE); + aml_append(field, aml_named_field("PRS", 256)); + aml_append(sb_scope, field); + + /* build Processor object for each processor */ + for (i = 0; i < apic_ids->len; i++) { + int apic_id = apic_ids->cpus[i].arch_id; + + assert(apic_id < ACPI_CPU_HOTPLUG_ID_LIMIT); + + dev = aml_processor(apic_id, 0, 0, "CP%.02X", apic_id); + + method = aml_method("_MAT", 0, AML_NOTSERIALIZED); + aml_append(method, + aml_return(aml_call1(CPU_MAT_METHOD, aml_int(apic_id)))); + aml_append(dev, method); + + method = aml_method("_STA", 0, AML_NOTSERIALIZED); + aml_append(method, + aml_return(aml_call1(CPU_STATUS_METHOD, aml_int(apic_id)))); + aml_append(dev, method); + + method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); + aml_append(method, + aml_return(aml_call2(CPU_EJECT_METHOD, aml_int(apic_id), + aml_arg(0))) + ); + aml_append(dev, method); + + aml_append(sb_scope, dev); + } + + /* build this code: + * Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...} + */ + /* Arg0 = Processor ID = APIC ID */ + method = aml_method(AML_NOTIFY_METHOD, 2, AML_NOTSERIALIZED); + for (i = 0; i < apic_ids->len; i++) { + int apic_id = apic_ids->cpus[i].arch_id; + + ifctx = aml_if(aml_equal(aml_arg(0), aml_int(apic_id))); + aml_append(ifctx, + aml_notify(aml_name("CP%.02X", apic_id), aml_arg(1)) + ); + aml_append(method, ifctx); + } + aml_append(sb_scope, method); + + /* build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })" + * + * Note: The ability to create variable-sized packages was first + * introduced in ACPI 2.0. ACPI 1.0 only allowed fixed-size packages + * ith up to 255 elements. Windows guests up to win2k8 fail when + * VarPackageOp is used. + */ + pkg = pcms->apic_id_limit <= 255 ? aml_package(pcms->apic_id_limit) : + aml_varpackage(pcms->apic_id_limit); + + for (i = 0, apic_idx = 0; i < apic_ids->len; i++) { + int apic_id = apic_ids->cpus[i].arch_id; + + for (; apic_idx < apic_id; apic_idx++) { + aml_append(pkg, aml_int(0)); + } + aml_append(pkg, aml_int(apic_ids->cpus[i].cpu ? 1 : 0)); + apic_idx = apic_id + 1; + } + aml_append(sb_scope, aml_name_decl(CPU_ON_BITMAP, pkg)); + g_free(apic_ids); +} + +static void build_memory_devices(Aml *sb_scope, int nr_mem, + uint16_t io_base, uint16_t io_len) +{ + int i; + Aml *scope; + Aml *crs; + Aml *field; + Aml *dev; + Aml *method; + Aml *ifctx; + + /* build memory devices */ + assert(nr_mem <= ACPI_MAX_RAM_SLOTS); + scope = aml_scope("\\_SB.PCI0." MEMORY_HOTPLUG_DEVICE); + aml_append(scope, + aml_name_decl(MEMORY_SLOTS_NUMBER, aml_int(nr_mem)) + ); + + crs = aml_resource_template(); + aml_append(crs, + aml_io(AML_DECODE16, io_base, io_base, 0, io_len) + ); + aml_append(scope, aml_name_decl("_CRS", crs)); + + aml_append(scope, aml_operation_region( + MEMORY_HOTPLUG_IO_REGION, AML_SYSTEM_IO, + aml_int(io_base), io_len) + ); + + field = aml_field(MEMORY_HOTPLUG_IO_REGION, AML_DWORD_ACC, + AML_NOLOCK, AML_PRESERVE); + aml_append(field, /* read only */ + aml_named_field(MEMORY_SLOT_ADDR_LOW, 32)); + aml_append(field, /* read only */ + aml_named_field(MEMORY_SLOT_ADDR_HIGH, 32)); + aml_append(field, /* read only */ + aml_named_field(MEMORY_SLOT_SIZE_LOW, 32)); + aml_append(field, /* read only */ + aml_named_field(MEMORY_SLOT_SIZE_HIGH, 32)); + aml_append(field, /* read only */ + aml_named_field(MEMORY_SLOT_PROXIMITY, 32)); + aml_append(scope, field); + + field = aml_field(MEMORY_HOTPLUG_IO_REGION, AML_BYTE_ACC, + AML_NOLOCK, AML_WRITE_AS_ZEROS); + aml_append(field, aml_reserved_field(160 /* bits, Offset(20) */)); + aml_append(field, /* 1 if enabled, read only */ + aml_named_field(MEMORY_SLOT_ENABLED, 1)); + aml_append(field, + /*(read) 1 if has a insert event. (write) 1 to clear event */ + aml_named_field(MEMORY_SLOT_INSERT_EVENT, 1)); + aml_append(field, + /* (read) 1 if has a remove event. (write) 1 to clear event */ + aml_named_field(MEMORY_SLOT_REMOVE_EVENT, 1)); + aml_append(field, + /* initiates device eject, write only */ + aml_named_field(MEMORY_SLOT_EJECT, 1)); + aml_append(scope, field); + + field = aml_field(MEMORY_HOTPLUG_IO_REGION, AML_DWORD_ACC, + AML_NOLOCK, AML_PRESERVE); + aml_append(field, /* DIMM selector, write only */ + aml_named_field(MEMORY_SLOT_SLECTOR, 32)); + aml_append(field, /* _OST event code, write only */ + aml_named_field(MEMORY_SLOT_OST_EVENT, 32)); + aml_append(field, /* _OST status code, write only */ + aml_named_field(MEMORY_SLOT_OST_STATUS, 32)); + aml_append(scope, field); + aml_append(sb_scope, scope); + + for (i = 0; i < nr_mem; i++) { + #define BASEPATH "\\_SB.PCI0." MEMORY_HOTPLUG_DEVICE "." + const char *s; + + dev = aml_device("MP%02X", i); + aml_append(dev, aml_name_decl("_UID", aml_string("0x%02X", i))); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C80"))); + + method = aml_method("_CRS", 0, AML_NOTSERIALIZED); + s = BASEPATH MEMORY_SLOT_CRS_METHOD; + aml_append(method, aml_return(aml_call1(s, aml_name("_UID")))); + aml_append(dev, method); + + method = aml_method("_STA", 0, AML_NOTSERIALIZED); + s = BASEPATH MEMORY_SLOT_STATUS_METHOD; + aml_append(method, aml_return(aml_call1(s, aml_name("_UID")))); + aml_append(dev, method); + + method = aml_method("_PXM", 0, AML_NOTSERIALIZED); + s = BASEPATH MEMORY_SLOT_PROXIMITY_METHOD; + aml_append(method, aml_return(aml_call1(s, aml_name("_UID")))); + aml_append(dev, method); + + method = aml_method("_OST", 3, AML_NOTSERIALIZED); + s = BASEPATH MEMORY_SLOT_OST_METHOD; + + aml_append(method, aml_return(aml_call4( + s, aml_name("_UID"), aml_arg(0), aml_arg(1), aml_arg(2) + ))); + aml_append(dev, method); + + method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); + s = BASEPATH MEMORY_SLOT_EJECT_METHOD; + aml_append(method, aml_return(aml_call2( + s, aml_name("_UID"), aml_arg(0)))); + aml_append(dev, method); + + aml_append(sb_scope, dev); + } + + /* build Method(MEMORY_SLOT_NOTIFY_METHOD, 2) { + * If (LEqual(Arg0, 0x00)) {Notify(MP00, Arg1)} ... } + */ + method = aml_method(MEMORY_SLOT_NOTIFY_METHOD, 2, AML_NOTSERIALIZED); + for (i = 0; i < nr_mem; i++) { + ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i))); + aml_append(ifctx, + aml_notify(aml_name("MP%.02X", i), aml_arg(1)) + ); + aml_append(method, ifctx); + } + aml_append(sb_scope, method); +} + +static void build_hpet_aml(Aml *table) +{ + Aml *crs; + Aml *field; + Aml *method; + Aml *if_ctx; + Aml *scope = aml_scope("_SB"); + Aml *dev = aml_device("HPET"); + Aml *zero = aml_int(0); + Aml *id = aml_local(0); + Aml *period = aml_local(1); + + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0103"))); + aml_append(dev, aml_name_decl("_UID", zero)); + + aml_append(dev, + aml_operation_region("HPTM", AML_SYSTEM_MEMORY, aml_int(HPET_BASE), + HPET_LEN)); + field = aml_field("HPTM", AML_DWORD_ACC, AML_LOCK, AML_PRESERVE); + aml_append(field, aml_named_field("VEND", 32)); + aml_append(field, aml_named_field("PRD", 32)); + aml_append(dev, field); + + method = aml_method("_STA", 0, AML_NOTSERIALIZED); + aml_append(method, aml_store(aml_name("VEND"), id)); + aml_append(method, aml_store(aml_name("PRD"), period)); + aml_append(method, aml_shiftright(id, aml_int(16), id)); + if_ctx = aml_if(aml_lor(aml_equal(id, zero), + aml_equal(id, aml_int(0xffff)))); + { + aml_append(if_ctx, aml_return(zero)); + } + aml_append(method, if_ctx); + + if_ctx = aml_if(aml_lor(aml_equal(period, zero), + aml_lgreater(period, aml_int(100000000)))); + { + aml_append(if_ctx, aml_return(zero)); + } + aml_append(method, if_ctx); + + aml_append(method, aml_return(aml_int(0x0F))); + aml_append(dev, method); + + crs = aml_resource_template(); + aml_append(crs, aml_memory32_fixed(HPET_BASE, HPET_LEN, AML_READ_ONLY)); + aml_append(dev, aml_name_decl("_CRS", crs)); + + aml_append(scope, dev); + aml_append(table, scope); +} + +static Aml *build_fdinfo_aml(int idx, FloppyDriveType type) +{ + Aml *dev, *fdi; + uint8_t maxc, maxh, maxs; + + isa_fdc_get_drive_max_chs(type, &maxc, &maxh, &maxs); + + dev = aml_device("FLP%c", 'A' + idx); + + aml_append(dev, aml_name_decl("_ADR", aml_int(idx))); + + fdi = aml_package(16); + aml_append(fdi, aml_int(idx)); /* Drive Number */ + aml_append(fdi, + aml_int(cmos_get_fd_drive_type(type))); /* Device Type */ + /* + * the values below are the limits of the drive, and are thus independent + * of the inserted media + */ + aml_append(fdi, aml_int(maxc)); /* Maximum Cylinder Number */ + aml_append(fdi, aml_int(maxs)); /* Maximum Sector Number */ + aml_append(fdi, aml_int(maxh)); /* Maximum Head Number */ + /* + * SeaBIOS returns the below values for int 0x13 func 0x08 regardless of + * the drive type, so shall we + */ + aml_append(fdi, aml_int(0xAF)); /* disk_specify_1 */ + aml_append(fdi, aml_int(0x02)); /* disk_specify_2 */ + aml_append(fdi, aml_int(0x25)); /* disk_motor_wait */ + aml_append(fdi, aml_int(0x02)); /* disk_sector_siz */ + aml_append(fdi, aml_int(0x12)); /* disk_eot */ + aml_append(fdi, aml_int(0x1B)); /* disk_rw_gap */ + aml_append(fdi, aml_int(0xFF)); /* disk_dtl */ + aml_append(fdi, aml_int(0x6C)); /* disk_formt_gap */ + aml_append(fdi, aml_int(0xF6)); /* disk_fill */ + aml_append(fdi, aml_int(0x0F)); /* disk_head_sttl */ + aml_append(fdi, aml_int(0x08)); /* disk_motor_strt */ + + aml_append(dev, aml_name_decl("_FDI", fdi)); + return dev; +} + +static Aml *build_fdc_device_aml(ISADevice *fdc) +{ + int i; + Aml *dev; + Aml *crs; + +#define ACPI_FDE_MAX_FD 4 + uint32_t fde_buf[5] = { + 0, 0, 0, 0, /* presence of floppy drives #0 - #3 */ + cpu_to_le32(2) /* tape presence (2 == never present) */ + }; + + dev = aml_device("FDC0"); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0700"))); + + crs = aml_resource_template(); + aml_append(crs, aml_io(AML_DECODE16, 0x03F2, 0x03F2, 0x00, 0x04)); + aml_append(crs, aml_io(AML_DECODE16, 0x03F7, 0x03F7, 0x00, 0x01)); + aml_append(crs, aml_irq_no_flags(6)); + aml_append(crs, + aml_dma(AML_COMPATIBILITY, AML_NOTBUSMASTER, AML_TRANSFER8, 2)); + aml_append(dev, aml_name_decl("_CRS", crs)); + + for (i = 0; i < MIN(MAX_FD, ACPI_FDE_MAX_FD); i++) { + FloppyDriveType type = isa_fdc_get_drive_type(fdc, i); + + if (type < FLOPPY_DRIVE_TYPE_NONE) { + fde_buf[i] = cpu_to_le32(1); /* drive present */ + aml_append(dev, build_fdinfo_aml(i, type)); + } + } + aml_append(dev, aml_name_decl("_FDE", + aml_buffer(sizeof(fde_buf), (uint8_t *)fde_buf))); + + return dev; +} + +static Aml *build_rtc_device_aml(void) +{ + Aml *dev; + Aml *crs; + + dev = aml_device("RTC"); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0B00"))); + crs = aml_resource_template(); + aml_append(crs, aml_io(AML_DECODE16, 0x0070, 0x0070, 0x10, 0x02)); + aml_append(crs, aml_irq_no_flags(8)); + aml_append(crs, aml_io(AML_DECODE16, 0x0072, 0x0072, 0x02, 0x06)); + aml_append(dev, aml_name_decl("_CRS", crs)); + + return dev; +} + +static Aml *build_kbd_device_aml(void) +{ + Aml *dev; + Aml *crs; + Aml *method; + + dev = aml_device("KBD"); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0303"))); + + method = aml_method("_STA", 0, AML_NOTSERIALIZED); + aml_append(method, aml_return(aml_int(0x0f))); + aml_append(dev, method); + + crs = aml_resource_template(); + aml_append(crs, aml_io(AML_DECODE16, 0x0060, 0x0060, 0x01, 0x01)); + aml_append(crs, aml_io(AML_DECODE16, 0x0064, 0x0064, 0x01, 0x01)); + aml_append(crs, aml_irq_no_flags(1)); + aml_append(dev, aml_name_decl("_CRS", crs)); + + return dev; +} + +static Aml *build_mouse_device_aml(void) +{ + Aml *dev; + Aml *crs; + Aml *method; + + dev = aml_device("MOU"); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0F13"))); + + method = aml_method("_STA", 0, AML_NOTSERIALIZED); + aml_append(method, aml_return(aml_int(0x0f))); + aml_append(dev, method); + + crs = aml_resource_template(); + aml_append(crs, aml_irq_no_flags(12)); + aml_append(dev, aml_name_decl("_CRS", crs)); + + return dev; +} + +static Aml *build_lpt_device_aml(void) +{ + Aml *dev; + Aml *crs; + Aml *method; + Aml *if_ctx; + Aml *else_ctx; + Aml *zero = aml_int(0); + Aml *is_present = aml_local(0); + + dev = aml_device("LPT"); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0400"))); + + method = aml_method("_STA", 0, AML_NOTSERIALIZED); + aml_append(method, aml_store(aml_name("LPEN"), is_present)); + if_ctx = aml_if(aml_equal(is_present, zero)); + { + aml_append(if_ctx, aml_return(aml_int(0x00))); + } + aml_append(method, if_ctx); + else_ctx = aml_else(); + { + aml_append(else_ctx, aml_return(aml_int(0x0f))); + } + aml_append(method, else_ctx); + aml_append(dev, method); + + crs = aml_resource_template(); + aml_append(crs, aml_io(AML_DECODE16, 0x0378, 0x0378, 0x08, 0x08)); + aml_append(crs, aml_irq_no_flags(7)); + aml_append(dev, aml_name_decl("_CRS", crs)); + + return dev; +} + +static Aml *build_com_device_aml(uint8_t uid) +{ + Aml *dev; + Aml *crs; + Aml *method; + Aml *if_ctx; + Aml *else_ctx; + Aml *zero = aml_int(0); + Aml *is_present = aml_local(0); + const char *enabled_field = "CAEN"; + uint8_t irq = 4; + uint16_t io_port = 0x03F8; + + assert(uid == 1 || uid == 2); + if (uid == 2) { + enabled_field = "CBEN"; + irq = 3; + io_port = 0x02F8; + } + + dev = aml_device("COM%d", uid); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0501"))); + aml_append(dev, aml_name_decl("_UID", aml_int(uid))); + + method = aml_method("_STA", 0, AML_NOTSERIALIZED); + aml_append(method, aml_store(aml_name("%s", enabled_field), is_present)); + if_ctx = aml_if(aml_equal(is_present, zero)); + { + aml_append(if_ctx, aml_return(aml_int(0x00))); + } + aml_append(method, if_ctx); + else_ctx = aml_else(); + { + aml_append(else_ctx, aml_return(aml_int(0x0f))); + } + aml_append(method, else_ctx); + aml_append(dev, method); + + crs = aml_resource_template(); + aml_append(crs, aml_io(AML_DECODE16, io_port, io_port, 0x00, 0x08)); + aml_append(crs, aml_irq_no_flags(irq)); + aml_append(dev, aml_name_decl("_CRS", crs)); + + return dev; +} + +static void build_isa_devices_aml(Aml *table) +{ + ISADevice *fdc = pc_find_fdc0(); + + Aml *scope = aml_scope("_SB.PCI0.ISA"); + + aml_append(scope, build_rtc_device_aml()); + aml_append(scope, build_kbd_device_aml()); + aml_append(scope, build_mouse_device_aml()); + if (fdc) { + aml_append(scope, build_fdc_device_aml(fdc)); + } + aml_append(scope, build_lpt_device_aml()); + aml_append(scope, build_com_device_aml(1)); + aml_append(scope, build_com_device_aml(2)); + + aml_append(table, scope); +} + +static void build_dbg_aml(Aml *table) +{ + Aml *field; + Aml *method; + Aml *while_ctx; + Aml *scope = aml_scope("\\"); + Aml *buf = aml_local(0); + Aml *len = aml_local(1); + Aml *idx = aml_local(2); + + aml_append(scope, + aml_operation_region("DBG", AML_SYSTEM_IO, aml_int(0x0402), 0x01)); + field = aml_field("DBG", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE); + aml_append(field, aml_named_field("DBGB", 8)); + aml_append(scope, field); + + method = aml_method("DBUG", 1, AML_NOTSERIALIZED); + + aml_append(method, aml_to_hexstring(aml_arg(0), buf)); + aml_append(method, aml_to_buffer(buf, buf)); + aml_append(method, aml_subtract(aml_sizeof(buf), aml_int(1), len)); + aml_append(method, aml_store(aml_int(0), idx)); + + while_ctx = aml_while(aml_lless(idx, len)); + aml_append(while_ctx, + aml_store(aml_derefof(aml_index(buf, idx)), aml_name("DBGB"))); + aml_append(while_ctx, aml_increment(idx)); + aml_append(method, while_ctx); + + aml_append(method, aml_store(aml_int(0x0A), aml_name("DBGB"))); + aml_append(scope, method); + + aml_append(table, scope); +} + +static Aml *build_link_dev(const char *name, uint8_t uid, Aml *reg) +{ + Aml *dev; + Aml *crs; + Aml *method; + uint32_t irqs[] = {5, 10, 11}; + + dev = aml_device("%s", name); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C0F"))); + aml_append(dev, aml_name_decl("_UID", aml_int(uid))); + + crs = aml_resource_template(); + aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, + AML_SHARED, irqs, ARRAY_SIZE(irqs))); + aml_append(dev, aml_name_decl("_PRS", crs)); + + method = aml_method("_STA", 0, AML_NOTSERIALIZED); + aml_append(method, aml_return(aml_call1("IQST", reg))); + aml_append(dev, method); + + method = aml_method("_DIS", 0, AML_NOTSERIALIZED); + aml_append(method, aml_or(reg, aml_int(0x80), reg)); + aml_append(dev, method); + + method = aml_method("_CRS", 0, AML_NOTSERIALIZED); + aml_append(method, aml_return(aml_call1("IQCR", reg))); + aml_append(dev, method); + + method = aml_method("_SRS", 1, AML_NOTSERIALIZED); + aml_append(method, aml_create_dword_field(aml_arg(0), aml_int(5), "PRRI")); + aml_append(method, aml_store(aml_name("PRRI"), reg)); + aml_append(dev, method); + + return dev; + } + +static Aml *build_gsi_link_dev(const char *name, uint8_t uid, uint8_t gsi) +{ + Aml *dev; + Aml *crs; + Aml *method; + uint32_t irqs; + + dev = aml_device("%s", name); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C0F"))); + aml_append(dev, aml_name_decl("_UID", aml_int(uid))); + + crs = aml_resource_template(); + irqs = gsi; + aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, + AML_SHARED, &irqs, 1)); + aml_append(dev, aml_name_decl("_PRS", crs)); + + aml_append(dev, aml_name_decl("_CRS", crs)); + + /* + * _DIS can be no-op because the interrupt cannot be disabled. + */ + method = aml_method("_DIS", 0, AML_NOTSERIALIZED); + aml_append(dev, method); + + method = aml_method("_SRS", 1, AML_NOTSERIALIZED); + aml_append(dev, method); + + return dev; +} + +/* _CRS method - get current settings */ +static Aml *build_iqcr_method(bool is_piix4) +{ + Aml *if_ctx; + uint32_t irqs; + Aml *method = aml_method("IQCR", 1, AML_SERIALIZED); + Aml *crs = aml_resource_template(); + + irqs = 0; + aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, + AML_ACTIVE_HIGH, AML_SHARED, &irqs, 1)); + aml_append(method, aml_name_decl("PRR0", crs)); + + aml_append(method, + aml_create_dword_field(aml_name("PRR0"), aml_int(5), "PRRI")); + + if (is_piix4) { + if_ctx = aml_if(aml_lless(aml_arg(0), aml_int(0x80))); + aml_append(if_ctx, aml_store(aml_arg(0), aml_name("PRRI"))); + aml_append(method, if_ctx); + } else { + aml_append(method, + aml_store(aml_and(aml_arg(0), aml_int(0xF), NULL), + aml_name("PRRI"))); + } + + aml_append(method, aml_return(aml_name("PRR0"))); + return method; +} + +/* _STA method - get status */ +static Aml *build_irq_status_method(void) +{ + Aml *if_ctx; + Aml *method = aml_method("IQST", 1, AML_NOTSERIALIZED); + + if_ctx = aml_if(aml_and(aml_int(0x80), aml_arg(0), NULL)); + aml_append(if_ctx, aml_return(aml_int(0x09))); + aml_append(method, if_ctx); + aml_append(method, aml_return(aml_int(0x0B))); + return method; +} + +static void build_piix4_pci0_int(Aml *table) +{ + Aml *dev; + Aml *crs; + Aml *field; + Aml *method; + uint32_t irqs; + Aml *sb_scope = aml_scope("_SB"); + Aml *pci0_scope = aml_scope("PCI0"); + + aml_append(pci0_scope, build_prt(true)); + aml_append(sb_scope, pci0_scope); + + field = aml_field("PCI0.ISA.P40C", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE); + aml_append(field, aml_named_field("PRQ0", 8)); + aml_append(field, aml_named_field("PRQ1", 8)); + aml_append(field, aml_named_field("PRQ2", 8)); + aml_append(field, aml_named_field("PRQ3", 8)); + aml_append(sb_scope, field); + + aml_append(sb_scope, build_irq_status_method()); + aml_append(sb_scope, build_iqcr_method(true)); + + aml_append(sb_scope, build_link_dev("LNKA", 0, aml_name("PRQ0"))); + aml_append(sb_scope, build_link_dev("LNKB", 1, aml_name("PRQ1"))); + aml_append(sb_scope, build_link_dev("LNKC", 2, aml_name("PRQ2"))); + aml_append(sb_scope, build_link_dev("LNKD", 3, aml_name("PRQ3"))); + + dev = aml_device("LNKS"); + { + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C0F"))); + aml_append(dev, aml_name_decl("_UID", aml_int(4))); + + crs = aml_resource_template(); + irqs = 9; + aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, + AML_ACTIVE_HIGH, AML_SHARED, + &irqs, 1)); + aml_append(dev, aml_name_decl("_PRS", crs)); + + /* The SCI cannot be disabled and is always attached to GSI 9, + * so these are no-ops. We only need this link to override the + * polarity to active high and match the content of the MADT. + */ + method = aml_method("_STA", 0, AML_NOTSERIALIZED); + aml_append(method, aml_return(aml_int(0x0b))); + aml_append(dev, method); + + method = aml_method("_DIS", 0, AML_NOTSERIALIZED); + aml_append(dev, method); + + method = aml_method("_CRS", 0, AML_NOTSERIALIZED); + aml_append(method, aml_return(aml_name("_PRS"))); + aml_append(dev, method); + + method = aml_method("_SRS", 1, AML_NOTSERIALIZED); + aml_append(dev, method); + } + aml_append(sb_scope, dev); + + aml_append(table, sb_scope); +} + +static void append_q35_prt_entry(Aml *ctx, uint32_t nr, const char *name) +{ + int i; + int head; + Aml *pkg; + char base = name[3] < 'E' ? 'A' : 'E'; + char *s = g_strdup(name); + Aml *a_nr = aml_int((nr << 16) | 0xffff); + + assert(strlen(s) == 4); + + head = name[3] - base; + for (i = 0; i < 4; i++) { + if (head + i > 3) { + head = i * -1; + } + s[3] = base + head + i; + pkg = aml_package(4); + aml_append(pkg, a_nr); + aml_append(pkg, aml_int(i)); + aml_append(pkg, aml_name("%s", s)); + aml_append(pkg, aml_int(0)); + aml_append(ctx, pkg); + } + g_free(s); +} + +static Aml *build_q35_routing_table(const char *str) +{ + int i; + Aml *pkg; + char *name = g_strdup_printf("%s ", str); + + pkg = aml_package(128); + for (i = 0; i < 0x18; i++) { + name[3] = 'E' + (i & 0x3); + append_q35_prt_entry(pkg, i, name); + } + + name[3] = 'E'; + append_q35_prt_entry(pkg, 0x18, name); + + /* INTA -> PIRQA for slot 25 - 31, see the default value of DIR */ + for (i = 0x0019; i < 0x1e; i++) { + name[3] = 'A'; + append_q35_prt_entry(pkg, i, name); + } + + /* PCIe->PCI bridge. use PIRQ[E-H] */ + name[3] = 'E'; + append_q35_prt_entry(pkg, 0x1e, name); + name[3] = 'A'; + append_q35_prt_entry(pkg, 0x1f, name); + + g_free(name); + return pkg; +} + +static void build_q35_pci0_int(Aml *table) +{ + Aml *field; + Aml *method; + Aml *sb_scope = aml_scope("_SB"); + Aml *pci0_scope = aml_scope("PCI0"); + + /* Zero => PIC mode, One => APIC Mode */ + aml_append(table, aml_name_decl("PICF", aml_int(0))); + method = aml_method("_PIC", 1, AML_NOTSERIALIZED); + { + aml_append(method, aml_store(aml_arg(0), aml_name("PICF"))); + } + aml_append(table, method); + + aml_append(pci0_scope, + aml_name_decl("PRTP", build_q35_routing_table("LNK"))); + aml_append(pci0_scope, + aml_name_decl("PRTA", build_q35_routing_table("GSI"))); + + method = aml_method("_PRT", 0, AML_NOTSERIALIZED); + { + Aml *if_ctx; + Aml *else_ctx; + + /* PCI IRQ routing table, example from ACPI 2.0a specification, + section 6.2.8.1 */ + /* Note: we provide the same info as the PCI routing + table of the Bochs BIOS */ + if_ctx = aml_if(aml_equal(aml_name("PICF"), aml_int(0))); + aml_append(if_ctx, aml_return(aml_name("PRTP"))); + aml_append(method, if_ctx); + else_ctx = aml_else(); + aml_append(else_ctx, aml_return(aml_name("PRTA"))); + aml_append(method, else_ctx); + } + aml_append(pci0_scope, method); + aml_append(sb_scope, pci0_scope); + + field = aml_field("PCI0.ISA.PIRQ", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE); + aml_append(field, aml_named_field("PRQA", 8)); + aml_append(field, aml_named_field("PRQB", 8)); + aml_append(field, aml_named_field("PRQC", 8)); + aml_append(field, aml_named_field("PRQD", 8)); + aml_append(field, aml_reserved_field(0x20)); + aml_append(field, aml_named_field("PRQE", 8)); + aml_append(field, aml_named_field("PRQF", 8)); + aml_append(field, aml_named_field("PRQG", 8)); + aml_append(field, aml_named_field("PRQH", 8)); + aml_append(sb_scope, field); + + aml_append(sb_scope, build_irq_status_method()); + aml_append(sb_scope, build_iqcr_method(false)); + + aml_append(sb_scope, build_link_dev("LNKA", 0, aml_name("PRQA"))); + aml_append(sb_scope, build_link_dev("LNKB", 1, aml_name("PRQB"))); + aml_append(sb_scope, build_link_dev("LNKC", 2, aml_name("PRQC"))); + aml_append(sb_scope, build_link_dev("LNKD", 3, aml_name("PRQD"))); + aml_append(sb_scope, build_link_dev("LNKE", 4, aml_name("PRQE"))); + aml_append(sb_scope, build_link_dev("LNKF", 5, aml_name("PRQF"))); + aml_append(sb_scope, build_link_dev("LNKG", 6, aml_name("PRQG"))); + aml_append(sb_scope, build_link_dev("LNKH", 7, aml_name("PRQH"))); + + aml_append(sb_scope, build_gsi_link_dev("GSIA", 0x10, 0x10)); + aml_append(sb_scope, build_gsi_link_dev("GSIB", 0x11, 0x11)); + aml_append(sb_scope, build_gsi_link_dev("GSIC", 0x12, 0x12)); + aml_append(sb_scope, build_gsi_link_dev("GSID", 0x13, 0x13)); + aml_append(sb_scope, build_gsi_link_dev("GSIE", 0x14, 0x14)); + aml_append(sb_scope, build_gsi_link_dev("GSIF", 0x15, 0x15)); + aml_append(sb_scope, build_gsi_link_dev("GSIG", 0x16, 0x16)); + aml_append(sb_scope, build_gsi_link_dev("GSIH", 0x17, 0x17)); + + aml_append(table, sb_scope); +} + +static void build_q35_isa_bridge(Aml *table) +{ + Aml *dev; + Aml *scope; + Aml *field; + + scope = aml_scope("_SB.PCI0"); + dev = aml_device("ISA"); + aml_append(dev, aml_name_decl("_ADR", aml_int(0x001F0000))); + + /* ICH9 PCI to ISA irq remapping */ + aml_append(dev, aml_operation_region("PIRQ", AML_PCI_CONFIG, + aml_int(0x60), 0x0C)); + + aml_append(dev, aml_operation_region("LPCD", AML_PCI_CONFIG, + aml_int(0x80), 0x02)); + field = aml_field("LPCD", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE); + aml_append(field, aml_named_field("COMA", 3)); + aml_append(field, aml_reserved_field(1)); + aml_append(field, aml_named_field("COMB", 3)); + aml_append(field, aml_reserved_field(1)); + aml_append(field, aml_named_field("LPTD", 2)); + aml_append(dev, field); + + aml_append(dev, aml_operation_region("LPCE", AML_PCI_CONFIG, + aml_int(0x82), 0x02)); + /* enable bits */ + field = aml_field("LPCE", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE); + aml_append(field, aml_named_field("CAEN", 1)); + aml_append(field, aml_named_field("CBEN", 1)); + aml_append(field, aml_named_field("LPEN", 1)); + aml_append(dev, field); + + aml_append(scope, dev); + aml_append(table, scope); +} + +static void build_piix4_pm(Aml *table) +{ + Aml *dev; + Aml *scope; + + scope = aml_scope("_SB.PCI0"); + dev = aml_device("PX13"); + aml_append(dev, aml_name_decl("_ADR", aml_int(0x00010003))); + + aml_append(dev, aml_operation_region("P13C", AML_PCI_CONFIG, + aml_int(0x00), 0xff)); + aml_append(scope, dev); + aml_append(table, scope); +} + +static void build_piix4_isa_bridge(Aml *table) +{ + Aml *dev; + Aml *scope; + Aml *field; + + scope = aml_scope("_SB.PCI0"); + dev = aml_device("ISA"); + aml_append(dev, aml_name_decl("_ADR", aml_int(0x00010000))); + + /* PIIX PCI to ISA irq remapping */ + aml_append(dev, aml_operation_region("P40C", AML_PCI_CONFIG, + aml_int(0x60), 0x04)); + /* enable bits */ + field = aml_field("^PX13.P13C", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE); + /* Offset(0x5f),, 7, */ + aml_append(field, aml_reserved_field(0x2f8)); + aml_append(field, aml_reserved_field(7)); + aml_append(field, aml_named_field("LPEN", 1)); + /* Offset(0x67),, 3, */ + aml_append(field, aml_reserved_field(0x38)); + aml_append(field, aml_reserved_field(3)); + aml_append(field, aml_named_field("CAEN", 1)); + aml_append(field, aml_reserved_field(3)); + aml_append(field, aml_named_field("CBEN", 1)); + aml_append(dev, field); + + aml_append(scope, dev); + aml_append(table, scope); +} + +static void build_piix4_pci_hotplug(Aml *table) +{ + Aml *scope; + Aml *field; + Aml *method; + + scope = aml_scope("_SB.PCI0"); + + aml_append(scope, + aml_operation_region("PCST", AML_SYSTEM_IO, aml_int(0xae00), 0x08)); + field = aml_field("PCST", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); + aml_append(field, aml_named_field("PCIU", 32)); + aml_append(field, aml_named_field("PCID", 32)); + aml_append(scope, field); + + aml_append(scope, + aml_operation_region("SEJ", AML_SYSTEM_IO, aml_int(0xae08), 0x04)); + field = aml_field("SEJ", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); + aml_append(field, aml_named_field("B0EJ", 32)); + aml_append(scope, field); + + aml_append(scope, + aml_operation_region("BNMR", AML_SYSTEM_IO, aml_int(0xae10), 0x04)); + field = aml_field("BNMR", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); + aml_append(field, aml_named_field("BNUM", 32)); + aml_append(scope, field); + + aml_append(scope, aml_mutex("BLCK", 0)); + + method = aml_method("PCEJ", 2, AML_NOTSERIALIZED); + aml_append(method, aml_acquire(aml_name("BLCK"), 0xFFFF)); + aml_append(method, aml_store(aml_arg(0), aml_name("BNUM"))); + aml_append(method, + aml_store(aml_shiftleft(aml_int(1), aml_arg(1)), aml_name("B0EJ"))); + aml_append(method, aml_release(aml_name("BLCK"))); + aml_append(method, aml_return(aml_int(0))); + aml_append(scope, method); + + aml_append(table, scope); +} + +static Aml *build_q35_osc_method(void) +{ + Aml *if_ctx; + Aml *if_ctx2; + Aml *else_ctx; + Aml *method; + Aml *a_cwd1 = aml_name("CDW1"); + Aml *a_ctrl = aml_name("CTRL"); + + method = aml_method("_OSC", 4, AML_NOTSERIALIZED); + aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1")); + + if_ctx = aml_if(aml_equal( + aml_arg(0), aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766"))); + aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2")); + aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3")); + + aml_append(if_ctx, aml_store(aml_name("CDW2"), aml_name("SUPP"))); + aml_append(if_ctx, aml_store(aml_name("CDW3"), a_ctrl)); + + /* + * Always allow native PME, AER (no dependencies) + * Never allow SHPC (no SHPC controller in this system) + */ + aml_append(if_ctx, aml_and(a_ctrl, aml_int(0x1D), a_ctrl)); + + if_ctx2 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(1)))); + /* Unknown revision */ + aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x08), a_cwd1)); + aml_append(if_ctx, if_ctx2); + + if_ctx2 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl))); + /* Capabilities bits were masked */ + aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x10), a_cwd1)); + aml_append(if_ctx, if_ctx2); + + /* Update DWORD3 in the buffer */ + aml_append(if_ctx, aml_store(a_ctrl, aml_name("CDW3"))); + aml_append(method, if_ctx); + + else_ctx = aml_else(); + /* Unrecognized UUID */ + aml_append(else_ctx, aml_or(a_cwd1, aml_int(4), a_cwd1)); + aml_append(method, else_ctx); + + aml_append(method, aml_return(aml_arg(3))); + return method; +} + static void -build_ssdt(GArray *table_data, GArray *linker, - AcpiCpuInfo *cpu, AcpiPmInfo *pm, AcpiMiscInfo *misc, - PcPciInfo *pci, PcGuestInfo *guest_info) +build_dsdt(GArray *table_data, GArray *linker, + AcpiPmInfo *pm, AcpiMiscInfo *misc, + PcPciInfo *pci, MachineState *machine) { - MachineState *machine = MACHINE(qdev_get_machine()); - uint32_t nr_mem = machine->ram_slots; - unsigned acpi_cpus = guest_info->apic_id_limit; - Aml *ssdt, *sb_scope, *scope, *pkg, *dev, *method, *crs, *field, *ifctx; - PCIBus *bus = NULL; - GPtrArray *io_ranges = g_ptr_array_new_with_free_func(crs_range_free); - GPtrArray *mem_ranges = g_ptr_array_new_with_free_func(crs_range_free); CrsRangeEntry *entry; + Aml *dsdt, *sb_scope, *scope, *dev, *method, *field, *pkg, *crs; + GPtrArray *mem_ranges = g_ptr_array_new_with_free_func(crs_range_free); + GPtrArray *io_ranges = g_ptr_array_new_with_free_func(crs_range_free); + PCMachineState *pcms = PC_MACHINE(machine); + uint32_t nr_mem = machine->ram_slots; int root_bus_limit = 0xFF; + PCIBus *bus = NULL; int i; - ssdt = init_aml_allocator(); - /* The current AML generator can cover the APIC ID range [0..255], - * inclusive, for VCPU hotplug. */ - QEMU_BUILD_BUG_ON(ACPI_CPU_HOTPLUG_ID_LIMIT > 256); - g_assert(acpi_cpus <= ACPI_CPU_HOTPLUG_ID_LIMIT); + dsdt = init_aml_allocator(); /* Reserve space for header */ - acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader)); + acpi_data_push(dsdt->buf, sizeof(AcpiTableHeader)); + + build_dbg_aml(dsdt); + if (misc->is_piix4) { + sb_scope = aml_scope("_SB"); + dev = aml_device("PCI0"); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A03"))); + aml_append(dev, aml_name_decl("_ADR", aml_int(0))); + aml_append(dev, aml_name_decl("_UID", aml_int(1))); + aml_append(sb_scope, dev); + aml_append(dsdt, sb_scope); + + build_hpet_aml(dsdt); + build_piix4_pm(dsdt); + build_piix4_isa_bridge(dsdt); + build_isa_devices_aml(dsdt); + build_piix4_pci_hotplug(dsdt); + build_piix4_pci0_int(dsdt); + } else { + sb_scope = aml_scope("_SB"); + aml_append(sb_scope, + aml_operation_region("PCST", AML_SYSTEM_IO, aml_int(0xae00), 0x0c)); + aml_append(sb_scope, + aml_operation_region("PCSB", AML_SYSTEM_IO, aml_int(0xae0c), 0x01)); + field = aml_field("PCSB", AML_ANY_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); + aml_append(field, aml_named_field("PCIB", 8)); + aml_append(sb_scope, field); + aml_append(dsdt, sb_scope); + + sb_scope = aml_scope("_SB"); + dev = aml_device("PCI0"); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A08"))); + aml_append(dev, aml_name_decl("_CID", aml_eisaid("PNP0A03"))); + aml_append(dev, aml_name_decl("_ADR", aml_int(0))); + aml_append(dev, aml_name_decl("_UID", aml_int(1))); + aml_append(dev, aml_name_decl("SUPP", aml_int(0))); + aml_append(dev, aml_name_decl("CTRL", aml_int(0))); + aml_append(dev, build_q35_osc_method()); + aml_append(sb_scope, dev); + aml_append(dsdt, sb_scope); + + build_hpet_aml(dsdt); + build_q35_isa_bridge(dsdt); + build_isa_devices_aml(dsdt); + build_q35_pci0_int(dsdt); + } - /* Extra PCI root buses are implemented only for i440fx */ - bus = find_i440fx(); + build_cpu_hotplug_aml(dsdt); + build_memory_hotplug_aml(dsdt, nr_mem, pm->mem_hp_io_base, + pm->mem_hp_io_len); + + scope = aml_scope("_GPE"); + { + aml_append(scope, aml_name_decl("_HID", aml_string("ACPI0006"))); + + aml_append(scope, aml_method("_L00", 0, AML_NOTSERIALIZED)); + + if (misc->is_piix4) { + method = aml_method("_E01", 0, AML_NOTSERIALIZED); + aml_append(method, + aml_acquire(aml_name("\\_SB.PCI0.BLCK"), 0xFFFF)); + aml_append(method, aml_call0("\\_SB.PCI0.PCNT")); + aml_append(method, aml_release(aml_name("\\_SB.PCI0.BLCK"))); + aml_append(scope, method); + } else { + aml_append(scope, aml_method("_L01", 0, AML_NOTSERIALIZED)); + } + + method = aml_method("_E02", 0, AML_NOTSERIALIZED); + aml_append(method, aml_call0("\\_SB." CPU_SCAN_METHOD)); + aml_append(scope, method); + + method = aml_method("_E03", 0, AML_NOTSERIALIZED); + aml_append(method, aml_call0(MEMORY_HOTPLUG_HANDLER_PATH)); + aml_append(scope, method); + + aml_append(scope, aml_method("_L04", 0, AML_NOTSERIALIZED)); + aml_append(scope, aml_method("_L05", 0, AML_NOTSERIALIZED)); + aml_append(scope, aml_method("_L06", 0, AML_NOTSERIALIZED)); + aml_append(scope, aml_method("_L07", 0, AML_NOTSERIALIZED)); + aml_append(scope, aml_method("_L08", 0, AML_NOTSERIALIZED)); + aml_append(scope, aml_method("_L09", 0, AML_NOTSERIALIZED)); + aml_append(scope, aml_method("_L0A", 0, AML_NOTSERIALIZED)); + aml_append(scope, aml_method("_L0B", 0, AML_NOTSERIALIZED)); + aml_append(scope, aml_method("_L0C", 0, AML_NOTSERIALIZED)); + aml_append(scope, aml_method("_L0D", 0, AML_NOTSERIALIZED)); + aml_append(scope, aml_method("_L0E", 0, AML_NOTSERIALIZED)); + aml_append(scope, aml_method("_L0F", 0, AML_NOTSERIALIZED)); + } + aml_append(dsdt, scope); + + bus = PC_MACHINE(machine)->bus; if (bus) { QLIST_FOREACH(bus, &bus->child, sibling) { uint8_t bus_num = pci_bus_num(bus); @@ -950,12 +2112,12 @@ build_ssdt(GArray *table_data, GArray *linker, aml_append(dev, aml_name_decl("_PXM", aml_int(numa_node))); } - aml_append(dev, build_prt()); + aml_append(dev, build_prt(false)); crs = build_crs(PCI_HOST_BRIDGE(BUS(bus)->parent), io_ranges, mem_ranges); aml_append(dev, aml_name_decl("_CRS", crs)); aml_append(scope, dev); - aml_append(ssdt, scope); + aml_append(dsdt, scope); } } @@ -1005,6 +2167,11 @@ build_ssdt(GArray *table_data, GArray *linker, 0, pci->w64.begin, pci->w64.end - 1, 0, pci->w64.end - pci->w64.begin)); } + + if (misc->tpm_version != TPM_VERSION_UNSPEC) { + aml_append(crs, aml_memory32_fixed(TPM_TIS_ADDR_BASE, + TPM_TIS_ADDR_SIZE, AML_READ_WRITE)); + } aml_append(scope, aml_name_decl("_CRS", crs)); /* reserve GPE0 block resources */ @@ -1039,7 +2206,7 @@ build_ssdt(GArray *table_data, GArray *linker, aml_append(dev, aml_name_decl("_CRS", crs)); aml_append(scope, dev); } - aml_append(ssdt, scope); + aml_append(dsdt, scope); /* create S3_ / S4_ / S5_ packages if necessary */ scope = aml_scope("\\"); @@ -1068,7 +2235,36 @@ build_ssdt(GArray *table_data, GArray *linker, aml_append(pkg, aml_int(0)); /* reserved */ aml_append(pkg, aml_int(0)); /* reserved */ aml_append(scope, aml_name_decl("_S5", pkg)); - aml_append(ssdt, scope); + aml_append(dsdt, scope); + + /* create fw_cfg node, unconditionally */ + { + /* when using port i/o, the 8-bit data register *always* overlaps + * with half of the 16-bit control register. Hence, the total size + * of the i/o region used is FW_CFG_CTL_SIZE; when using DMA, the + * DMA control register is located at FW_CFG_DMA_IO_BASE + 4 */ + uint8_t io_size = object_property_get_bool(OBJECT(pcms->fw_cfg), + "dma_enabled", NULL) ? + ROUND_UP(FW_CFG_CTL_SIZE, 4) + sizeof(dma_addr_t) : + FW_CFG_CTL_SIZE; + + scope = aml_scope("\\_SB.PCI0"); + dev = aml_device("FWCF"); + + aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002"))); + + /* device present, functioning, decoding, not shown in UI */ + aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); + + crs = aml_resource_template(); + aml_append(crs, + aml_io(AML_DECODE16, FW_CFG_IO_BASE, FW_CFG_IO_BASE, 0x01, io_size) + ); + aml_append(dev, aml_name_decl("_CRS", crs)); + + aml_append(scope, dev); + aml_append(dsdt, scope); + } if (misc->applesmc_io_base) { scope = aml_scope("\\_SB.PCI0.ISA"); @@ -1087,7 +2283,7 @@ build_ssdt(GArray *table_data, GArray *linker, aml_append(dev, aml_name_decl("_CRS", crs)); aml_append(scope, dev); - aml_append(ssdt, scope); + aml_append(dsdt, scope); } if (misc->pvpanic_port) { @@ -1103,214 +2299,33 @@ build_ssdt(GArray *table_data, GArray *linker, aml_append(dev, aml_name_decl("_CRS", crs)); aml_append(dev, aml_operation_region("PEOR", AML_SYSTEM_IO, - misc->pvpanic_port, 1)); - field = aml_field("PEOR", AML_BYTE_ACC, AML_PRESERVE); + aml_int(misc->pvpanic_port), 1)); + field = aml_field("PEOR", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE); aml_append(field, aml_named_field("PEPT", 8)); aml_append(dev, field); /* device present, functioning, decoding, shown in UI */ aml_append(dev, aml_name_decl("_STA", aml_int(0xF))); - method = aml_method("RDPT", 0); + method = aml_method("RDPT", 0, AML_NOTSERIALIZED); aml_append(method, aml_store(aml_name("PEPT"), aml_local(0))); aml_append(method, aml_return(aml_local(0))); aml_append(dev, method); - method = aml_method("WRPT", 1); + method = aml_method("WRPT", 1, AML_NOTSERIALIZED); aml_append(method, aml_store(aml_arg(0), aml_name("PEPT"))); aml_append(dev, method); aml_append(scope, dev); - aml_append(ssdt, scope); + aml_append(dsdt, scope); } sb_scope = aml_scope("\\_SB"); { - /* create PCI0.PRES device and its _CRS to reserve CPU hotplug MMIO */ - dev = aml_device("PCI0." stringify(CPU_HOTPLUG_RESOURCE_DEVICE)); - aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A06"))); - aml_append(dev, - aml_name_decl("_UID", aml_string("CPU Hotplug resources")) - ); - /* device present, functioning, decoding, not shown in UI */ - aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); - crs = aml_resource_template(); - aml_append(crs, - aml_io(AML_DECODE16, pm->cpu_hp_io_base, pm->cpu_hp_io_base, 1, - pm->cpu_hp_io_len) - ); - aml_append(dev, aml_name_decl("_CRS", crs)); - aml_append(sb_scope, dev); - /* declare CPU hotplug MMIO region and PRS field to access it */ - aml_append(sb_scope, aml_operation_region( - "PRST", AML_SYSTEM_IO, pm->cpu_hp_io_base, pm->cpu_hp_io_len)); - field = aml_field("PRST", AML_BYTE_ACC, AML_PRESERVE); - aml_append(field, aml_named_field("PRS", 256)); - aml_append(sb_scope, field); - - /* build Processor object for each processor */ - for (i = 0; i < acpi_cpus; i++) { - dev = aml_processor(i, 0, 0, "CP%.02X", i); - - method = aml_method("_MAT", 0); - aml_append(method, aml_return(aml_call1("CPMA", aml_int(i)))); - aml_append(dev, method); - - method = aml_method("_STA", 0); - aml_append(method, aml_return(aml_call1("CPST", aml_int(i)))); - aml_append(dev, method); - - method = aml_method("_EJ0", 1); - aml_append(method, - aml_return(aml_call2("CPEJ", aml_int(i), aml_arg(0))) - ); - aml_append(dev, method); + build_processor_devices(sb_scope, machine, pm); - aml_append(sb_scope, dev); - } - - /* build this code: - * Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...} - */ - /* Arg0 = Processor ID = APIC ID */ - method = aml_method("NTFY", 2); - for (i = 0; i < acpi_cpus; i++) { - ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i))); - aml_append(ifctx, - aml_notify(aml_name("CP%.02X", i), aml_arg(1)) - ); - aml_append(method, ifctx); - } - aml_append(sb_scope, method); - - /* build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })" - * - * Note: The ability to create variable-sized packages was first - * introduced in ACPI 2.0. ACPI 1.0 only allowed fixed-size packages - * ith up to 255 elements. Windows guests up to win2k8 fail when - * VarPackageOp is used. - */ - pkg = acpi_cpus <= 255 ? aml_package(acpi_cpus) : - aml_varpackage(acpi_cpus); - - for (i = 0; i < acpi_cpus; i++) { - uint8_t b = test_bit(i, cpu->found_cpus) ? 0x01 : 0x00; - aml_append(pkg, aml_int(b)); - } - aml_append(sb_scope, aml_name_decl("CPON", pkg)); - - /* build memory devices */ - assert(nr_mem <= ACPI_MAX_RAM_SLOTS); - scope = aml_scope("\\_SB.PCI0." stringify(MEMORY_HOTPLUG_DEVICE)); - aml_append(scope, - aml_name_decl(stringify(MEMORY_SLOTS_NUMBER), aml_int(nr_mem)) - ); - - crs = aml_resource_template(); - aml_append(crs, - aml_io(AML_DECODE16, pm->mem_hp_io_base, pm->mem_hp_io_base, 0, - pm->mem_hp_io_len) - ); - aml_append(scope, aml_name_decl("_CRS", crs)); - - aml_append(scope, aml_operation_region( - stringify(MEMORY_HOTPLUG_IO_REGION), AML_SYSTEM_IO, - pm->mem_hp_io_base, pm->mem_hp_io_len) - ); - - field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_DWORD_ACC, - AML_PRESERVE); - aml_append(field, /* read only */ - aml_named_field(stringify(MEMORY_SLOT_ADDR_LOW), 32)); - aml_append(field, /* read only */ - aml_named_field(stringify(MEMORY_SLOT_ADDR_HIGH), 32)); - aml_append(field, /* read only */ - aml_named_field(stringify(MEMORY_SLOT_SIZE_LOW), 32)); - aml_append(field, /* read only */ - aml_named_field(stringify(MEMORY_SLOT_SIZE_HIGH), 32)); - aml_append(field, /* read only */ - aml_named_field(stringify(MEMORY_SLOT_PROXIMITY), 32)); - aml_append(scope, field); - - field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_BYTE_ACC, - AML_WRITE_AS_ZEROS); - aml_append(field, aml_reserved_field(160 /* bits, Offset(20) */)); - aml_append(field, /* 1 if enabled, read only */ - aml_named_field(stringify(MEMORY_SLOT_ENABLED), 1)); - aml_append(field, - /*(read) 1 if has a insert event. (write) 1 to clear event */ - aml_named_field(stringify(MEMORY_SLOT_INSERT_EVENT), 1)); - aml_append(field, - /* (read) 1 if has a remove event. (write) 1 to clear event */ - aml_named_field(stringify(MEMORY_SLOT_REMOVE_EVENT), 1)); - aml_append(field, - /* initiates device eject, write only */ - aml_named_field(stringify(MEMORY_SLOT_EJECT), 1)); - aml_append(scope, field); - - field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_DWORD_ACC, - AML_PRESERVE); - aml_append(field, /* DIMM selector, write only */ - aml_named_field(stringify(MEMORY_SLOT_SLECTOR), 32)); - aml_append(field, /* _OST event code, write only */ - aml_named_field(stringify(MEMORY_SLOT_OST_EVENT), 32)); - aml_append(field, /* _OST status code, write only */ - aml_named_field(stringify(MEMORY_SLOT_OST_STATUS), 32)); - aml_append(scope, field); - - aml_append(sb_scope, scope); - - for (i = 0; i < nr_mem; i++) { - #define BASEPATH "\\_SB.PCI0." stringify(MEMORY_HOTPLUG_DEVICE) "." - const char *s; - - dev = aml_device("MP%02X", i); - aml_append(dev, aml_name_decl("_UID", aml_string("0x%02X", i))); - aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C80"))); - - method = aml_method("_CRS", 0); - s = BASEPATH stringify(MEMORY_SLOT_CRS_METHOD); - aml_append(method, aml_return(aml_call1(s, aml_name("_UID")))); - aml_append(dev, method); - - method = aml_method("_STA", 0); - s = BASEPATH stringify(MEMORY_SLOT_STATUS_METHOD); - aml_append(method, aml_return(aml_call1(s, aml_name("_UID")))); - aml_append(dev, method); - - method = aml_method("_PXM", 0); - s = BASEPATH stringify(MEMORY_SLOT_PROXIMITY_METHOD); - aml_append(method, aml_return(aml_call1(s, aml_name("_UID")))); - aml_append(dev, method); - - method = aml_method("_OST", 3); - s = BASEPATH stringify(MEMORY_SLOT_OST_METHOD); - aml_append(method, aml_return(aml_call4( - s, aml_name("_UID"), aml_arg(0), aml_arg(1), aml_arg(2) - ))); - aml_append(dev, method); - - method = aml_method("_EJ0", 1); - s = BASEPATH stringify(MEMORY_SLOT_EJECT_METHOD); - aml_append(method, aml_return(aml_call2( - s, aml_name("_UID"), aml_arg(0)))); - aml_append(dev, method); - - aml_append(sb_scope, dev); - } - - /* build Method(MEMORY_SLOT_NOTIFY_METHOD, 2) { - * If (LEqual(Arg0, 0x00)) {Notify(MP00, Arg1)} ... } - */ - method = aml_method(stringify(MEMORY_SLOT_NOTIFY_METHOD), 2); - for (i = 0; i < nr_mem; i++) { - ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i))); - aml_append(ifctx, - aml_notify(aml_name("MP%.02X", i), aml_arg(1)) - ); - aml_append(method, ifctx); - } - aml_append(sb_scope, method); + build_memory_devices(sb_scope, nr_mem, pm->mem_hp_io_base, + pm->mem_hp_io_len); { Object *pci_host; @@ -1333,7 +2348,12 @@ build_ssdt(GArray *table_data, GArray *linker, crs = aml_resource_template(); aml_append(crs, aml_memory32_fixed(TPM_TIS_ADDR_BASE, TPM_TIS_ADDR_SIZE, AML_READ_WRITE)); - aml_append(crs, aml_irq_no_flags(TPM_TIS_IRQ)); + /* + FIXME: TPM_TIS_IRQ=5 conflicts with PNP0C0F irqs, + Rewrite to take IRQ from TPM device model and + fix default IRQ value there to use some unused IRQ + */ + /* aml_append(crs, aml_irq_no_flags(TPM_TIS_IRQ)); */ aml_append(dev, aml_name_decl("_CRS", crs)); aml_append(scope, dev); } @@ -1341,14 +2361,14 @@ build_ssdt(GArray *table_data, GArray *linker, aml_append(sb_scope, scope); } } - aml_append(ssdt, sb_scope); + aml_append(dsdt, sb_scope); } /* copy AML table into ACPI tables blob and patch header there */ - g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len); + g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len); build_header(linker, table_data, - (void *)(table_data->data + table_data->len - ssdt->buf->len), - "SSDT", ssdt->buf->len, 1); + (void *)(table_data->data + table_data->len - dsdt->buf->len), + "DSDT", dsdt->buf->len, 1, NULL, NULL); free_aml_allocator(); } @@ -1364,7 +2384,7 @@ build_hpet(GArray *table_data, GArray *linker) hpet->timer_block_id = cpu_to_le32(0x8086a201); hpet->addr.address = cpu_to_le64(HPET_BASE); build_header(linker, table_data, - (void *)hpet, "HPET", sizeof(*hpet), 1); + (void *)hpet, "HPET", sizeof(*hpet), 1, NULL, NULL); } static void @@ -1387,7 +2407,7 @@ build_tpm_tcpa(GArray *table_data, GArray *linker, GArray *tcpalog) sizeof(tcpa->log_area_start_address)); build_header(linker, table_data, - (void *)tcpa, "TCPA", sizeof(*tcpa), 2); + (void *)tcpa, "TCPA", sizeof(*tcpa), 2, NULL, NULL); acpi_data_push(tcpalog, TPM_LOG_AREA_MINIMUM_SIZE); } @@ -1404,7 +2424,7 @@ build_tpm2(GArray *table_data, GArray *linker) tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_MMIO); build_header(linker, table_data, - (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4); + (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL, NULL); } typedef enum { @@ -1428,7 +2448,7 @@ acpi_build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base, } static void -build_srat(GArray *table_data, GArray *linker, PcGuestInfo *guest_info) +build_srat(GArray *table_data, GArray *linker, MachineState *machine) { AcpiSystemResourceAffinityTable *srat; AcpiSratProcessorAffinity *core; @@ -1438,7 +2458,9 @@ build_srat(GArray *table_data, GArray *linker, PcGuestInfo *guest_info) uint64_t curnode; int srat_start, numa_start, slots; uint64_t mem_len, mem_base, next_base; - PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(machine); + CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(machine); + PCMachineState *pcms = PC_MACHINE(machine); ram_addr_t hotplugabble_address_space_size = object_property_get_int(OBJECT(pcms), PC_MACHINE_MEMHP_REGION_SIZE, NULL); @@ -1447,14 +2469,15 @@ build_srat(GArray *table_data, GArray *linker, PcGuestInfo *guest_info) srat = acpi_data_push(table_data, sizeof *srat); srat->reserved1 = cpu_to_le32(1); - core = (void *)(srat + 1); - for (i = 0; i < guest_info->apic_id_limit; ++i) { + for (i = 0; i < apic_ids->len; i++) { + int apic_id = apic_ids->cpus[i].arch_id; + core = acpi_data_push(table_data, sizeof *core); core->type = ACPI_SRAT_PROCESSOR; core->length = sizeof(*core); - core->local_apic_id = i; - curnode = guest_info->node_cpu[i]; + core->local_apic_id = apic_id; + curnode = pcms->node_cpu[apic_id]; core->proximity_lo = curnode; memset(core->proximity_hi, 0, 3); core->local_sapic_eid = 0; @@ -1471,33 +2494,33 @@ build_srat(GArray *table_data, GArray *linker, PcGuestInfo *guest_info) numamem = acpi_data_push(table_data, sizeof *numamem); acpi_build_srat_memory(numamem, 0, 640*1024, 0, MEM_AFFINITY_ENABLED); next_base = 1024 * 1024; - for (i = 1; i < guest_info->numa_nodes + 1; ++i) { + for (i = 1; i < pcms->numa_nodes + 1; ++i) { mem_base = next_base; - mem_len = guest_info->node_mem[i - 1]; + mem_len = pcms->node_mem[i - 1]; if (i == 1) { mem_len -= 1024 * 1024; } next_base = mem_base + mem_len; /* Cut out the ACPI_PCI hole */ - if (mem_base <= guest_info->ram_size_below_4g && - next_base > guest_info->ram_size_below_4g) { - mem_len -= next_base - guest_info->ram_size_below_4g; + if (mem_base <= pcms->below_4g_mem_size && + next_base > pcms->below_4g_mem_size) { + mem_len -= next_base - pcms->below_4g_mem_size; if (mem_len > 0) { numamem = acpi_data_push(table_data, sizeof *numamem); acpi_build_srat_memory(numamem, mem_base, mem_len, i - 1, MEM_AFFINITY_ENABLED); } mem_base = 1ULL << 32; - mem_len = next_base - guest_info->ram_size_below_4g; - next_base += (1ULL << 32) - guest_info->ram_size_below_4g; + mem_len = next_base - pcms->below_4g_mem_size; + next_base += (1ULL << 32) - pcms->below_4g_mem_size; } numamem = acpi_data_push(table_data, sizeof *numamem); acpi_build_srat_memory(numamem, mem_base, mem_len, i - 1, MEM_AFFINITY_ENABLED); } slots = (table_data->len - numa_start) / sizeof *numamem; - for (; slots < guest_info->numa_nodes + 2; slots++) { + for (; slots < pcms->numa_nodes + 2; slots++) { numamem = acpi_data_push(table_data, sizeof *numamem); acpi_build_srat_memory(numamem, 0, 0, 0, MEM_AFFINITY_NOFLAGS); } @@ -1518,7 +2541,8 @@ build_srat(GArray *table_data, GArray *linker, PcGuestInfo *guest_info) build_header(linker, table_data, (void *)(table_data->data + srat_start), "SRAT", - table_data->len - srat_start, 1); + table_data->len - srat_start, 1, NULL, NULL); + g_free(apic_ids); } static void @@ -1547,7 +2571,7 @@ build_mcfg_q35(GArray *table_data, GArray *linker, AcpiMcfgInfo *info) } else { sig = "MCFG"; } - build_header(linker, table_data, (void *)mcfg, sig, len, 1); + build_header(linker, table_data, (void *)mcfg, sig, len, 1, NULL, NULL); } static void @@ -1571,22 +2595,7 @@ build_dmar_q35(GArray *table_data, GArray *linker) drhd->address = cpu_to_le64(Q35_HOST_BRIDGE_IOMMU_ADDR); build_header(linker, table_data, (void *)(table_data->data + dmar_start), - "DMAR", table_data->len - dmar_start, 1); -} - -static void -build_dsdt(GArray *table_data, GArray *linker, AcpiMiscInfo *misc) -{ - AcpiTableHeader *dsdt; - - assert(misc->dsdt_code && misc->dsdt_size); - - dsdt = acpi_data_push(table_data, misc->dsdt_size); - memcpy(dsdt, misc->dsdt_code, misc->dsdt_size); - - memset(dsdt, 0, sizeof *dsdt); - build_header(linker, table_data, dsdt, "DSDT", - misc->dsdt_size, 1); + "DMAR", table_data->len - dmar_start, 1, NULL, NULL); } static GArray * @@ -1608,7 +2617,8 @@ build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt) rsdp->checksum = 0; /* Checksum to be filled by Guest linker */ bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE, - rsdp, rsdp, sizeof *rsdp, &rsdp->checksum); + rsdp_table, rsdp, sizeof *rsdp, + &rsdp->checksum); return rsdp_table; } @@ -1619,7 +2629,6 @@ struct AcpiBuildState { MemoryRegion *table_mr; /* Is table patched? */ uint8_t patched; - PcGuestInfo *guest_info; void *rsdp; MemoryRegion *rsdp_mr; MemoryRegion *linker_mr; @@ -1658,11 +2667,12 @@ static bool acpi_has_iommu(void) } static -void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) +void acpi_build(AcpiBuildTables *tables, MachineState *machine) { + PCMachineState *pcms = PC_MACHINE(machine); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); GArray *table_offsets; - unsigned facs, ssdt, dsdt, rsdt; - AcpiCpuInfo cpu; + unsigned facs, dsdt, rsdt, fadt; AcpiPmInfo pm; AcpiMiscInfo misc; AcpiMcfgInfo mcfg; @@ -1670,12 +2680,12 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) uint8_t *u; size_t aml_len = 0; GArray *tables_blob = tables->table_data; + AcpiSlicOem slic_oem = { .id = NULL, .table_id = NULL }; - acpi_get_cpu_info(&cpu); acpi_get_pm_info(&pm); - acpi_get_dsdt(&misc); acpi_get_misc_info(&misc); acpi_get_pci_info(&pci); + acpi_get_slic_oem(&slic_oem); table_offsets = g_array_new(false, true /* clear */, sizeof(uint32_t)); @@ -1691,11 +2701,11 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) * requirements. */ facs = tables_blob->len; - build_facs(tables_blob, tables->linker, guest_info); + build_facs(tables_blob, tables->linker); /* DSDT is pointed to by FADT */ dsdt = tables_blob->len; - build_dsdt(tables_blob, tables->linker, &misc); + build_dsdt(tables_blob, tables->linker, &pm, &misc, &pci, machine); /* Count the size of the DSDT and SSDT, we will need it for legacy * sizing of ACPI tables. @@ -1703,17 +2713,14 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) aml_len += tables_blob->len - dsdt; /* ACPI tables pointed to by RSDT */ + fadt = tables_blob->len; acpi_add_table(table_offsets, tables_blob); - build_fadt(tables_blob, tables->linker, &pm, facs, dsdt); - - ssdt = tables_blob->len; - acpi_add_table(table_offsets, tables_blob); - build_ssdt(tables_blob, tables->linker, &cpu, &pm, &misc, &pci, - guest_info); - aml_len += tables_blob->len - ssdt; + build_fadt(tables_blob, tables->linker, &pm, facs, dsdt, + slic_oem.id, slic_oem.table_id); + aml_len += tables_blob->len - fadt; acpi_add_table(table_offsets, tables_blob); - build_madt(tables_blob, tables->linker, &cpu, guest_info); + build_madt(tables_blob, tables->linker, pcms); if (misc.has_hpet) { acpi_add_table(table_offsets, tables_blob); @@ -1728,9 +2735,9 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) build_tpm2(tables_blob, tables->linker); } } - if (guest_info->numa_nodes) { + if (pcms->numa_nodes) { acpi_add_table(table_offsets, tables_blob); - build_srat(tables_blob, tables->linker, guest_info); + build_srat(tables_blob, tables->linker, machine); } if (acpi_get_mcfg(&mcfg)) { acpi_add_table(table_offsets, tables_blob); @@ -1740,6 +2747,9 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) acpi_add_table(table_offsets, tables_blob); build_dmar_q35(tables_blob, tables->linker); } + if (pcms->acpi_nvdimm_state.is_enabled) { + nvdimm_build_acpi(table_offsets, tables_blob, tables->linker); + } /* Add tables supplied by user (if any) */ for (u = acpi_table_first(); u; u = acpi_table_next(u)) { @@ -1751,7 +2761,8 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) /* RSDT is pointed to by RSDP */ rsdt = tables_blob->len; - build_rsdt(tables_blob, tables->linker, table_offsets); + build_rsdt(tables_blob, tables->linker, table_offsets, + slic_oem.id, slic_oem.table_id); /* RSDP is in FSEG memory, so allocate it separately */ build_rsdp(tables->rsdp, tables->linker, rsdt); @@ -1773,12 +2784,12 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) * * All this is for PIIX4, since QEMU 2.0 didn't support Q35 migration. */ - if (guest_info->legacy_acpi_table_size) { + if (pcmc->legacy_acpi_table_size) { /* Subtracting aml_len gives the size of fixed tables. Then add the * size of the PIIX4 DSDT/SSDT in QEMU 2.0. */ int legacy_aml_len = - guest_info->legacy_acpi_table_size + + pcmc->legacy_acpi_table_size + ACPI_BUILD_LEGACY_CPU_AML_SIZE * max_cpus; int legacy_table_size = ROUND_UP(tables_blob->len - aml_len + legacy_aml_len, @@ -1817,7 +2828,7 @@ static void acpi_ram_update(MemoryRegion *mr, GArray *data) memory_region_set_dirty(mr, 0, size); } -static void acpi_build_update(void *build_opaque, uint32_t offset) +static void acpi_build_update(void *build_opaque) { AcpiBuildState *build_state = build_opaque; AcpiBuildTables tables; @@ -1830,7 +2841,7 @@ static void acpi_build_update(void *build_opaque, uint32_t offset) acpi_build_tables_init(&tables); - acpi_build(build_state->guest_info, &tables); + acpi_build(&tables, MACHINE(qdev_get_machine())); acpi_ram_update(build_state->table_mr, tables.table_data); @@ -1868,17 +2879,19 @@ static const VMStateDescription vmstate_acpi_build = { }, }; -void acpi_setup(PcGuestInfo *guest_info) +void acpi_setup(void) { + PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); AcpiBuildTables tables; AcpiBuildState *build_state; - if (!guest_info->fw_cfg) { + if (!pcms->fw_cfg) { ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n"); return; } - if (!guest_info->has_acpi_build) { + if (!pcmc->has_acpi_build) { ACPI_BUILD_DPRINTF("ACPI build disabled. Bailing out.\n"); return; } @@ -1890,12 +2903,10 @@ void acpi_setup(PcGuestInfo *guest_info) build_state = g_malloc0(sizeof *build_state); - build_state->guest_info = guest_info; - acpi_set_pci_info(); acpi_build_tables_init(&tables); - acpi_build(build_state->guest_info, &tables); + acpi_build(&tables, MACHINE(pcms)); /* Now expose it all to Guest */ build_state->table_mr = acpi_add_rom_blob(build_state, tables.table_data, @@ -1906,10 +2917,10 @@ void acpi_setup(PcGuestInfo *guest_info) build_state->linker_mr = acpi_add_rom_blob(build_state, tables.linker, "etc/table-loader", 0); - fw_cfg_add_file(guest_info->fw_cfg, ACPI_BUILD_TPMLOG_FILE, + fw_cfg_add_file(pcms->fw_cfg, ACPI_BUILD_TPMLOG_FILE, tables.tcpalog->data, acpi_data_len(tables.tcpalog)); - if (!guest_info->rsdp_in_ram) { + if (!pcmc->rsdp_in_ram) { /* * Keep for compatibility with old machine types. * Though RSDP is small, its contents isn't immutable, so @@ -1918,7 +2929,7 @@ void acpi_setup(PcGuestInfo *guest_info) uint32_t rsdp_size = acpi_data_len(tables.rsdp); build_state->rsdp = g_memdup(tables.rsdp->data, rsdp_size); - fw_cfg_add_file_callback(guest_info->fw_cfg, ACPI_BUILD_RSDP_FILE, + fw_cfg_add_file_callback(pcms->fw_cfg, ACPI_BUILD_RSDP_FILE, acpi_build_update, build_state, build_state->rsdp, rsdp_size); build_state->rsdp_mr = NULL; diff --git a/qemu/hw/i386/acpi-build.h b/qemu/hw/i386/acpi-build.h index e57b1aafd..007332e51 100644 --- a/qemu/hw/i386/acpi-build.h +++ b/qemu/hw/i386/acpi-build.h @@ -2,8 +2,6 @@ #ifndef HW_I386_ACPI_BUILD_H #define HW_I386_ACPI_BUILD_H -#include "qemu/typedefs.h" - -void acpi_setup(PcGuestInfo *); +void acpi_setup(void); #endif diff --git a/qemu/hw/i386/acpi-dsdt-cpu-hotplug.dsl b/qemu/hw/i386/acpi-dsdt-cpu-hotplug.dsl deleted file mode 100644 index 1aff74627..000000000 --- a/qemu/hw/i386/acpi-dsdt-cpu-hotplug.dsl +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 . - */ - -/**************************************************************** - * CPU hotplug - ****************************************************************/ - -Scope(\_SB) { - /* Objects filled in by run-time generated SSDT */ - External(NTFY, MethodObj) - External(CPON, PkgObj) - External(PRS, FieldUnitObj) - - /* Methods called by run-time generated SSDT Processor objects */ - Method(CPMA, 1, NotSerialized) { - // _MAT method - create an madt apic buffer - // Arg0 = Processor ID = Local APIC ID - // Local0 = CPON flag for this cpu - Store(DerefOf(Index(CPON, Arg0)), Local0) - // Local1 = Buffer (in madt apic form) to return - Store(Buffer(8) {0x00, 0x08, 0x00, 0x00, 0x00, 0, 0, 0}, Local1) - // Update the processor id, lapic id, and enable/disable status - Store(Arg0, Index(Local1, 2)) - Store(Arg0, Index(Local1, 3)) - Store(Local0, Index(Local1, 4)) - Return (Local1) - } - Method(CPST, 1, NotSerialized) { - // _STA method - return ON status of cpu - // Arg0 = Processor ID = Local APIC ID - // Local0 = CPON flag for this cpu - Store(DerefOf(Index(CPON, Arg0)), Local0) - If (Local0) { - Return (0xF) - } Else { - Return (0x0) - } - } - Method(CPEJ, 2, NotSerialized) { - // _EJ0 method - eject callback - Sleep(200) - } - -#define CPU_STATUS_LEN ACPI_GPE_PROC_LEN - Method(PRSC, 0) { - // Local5 = active cpu bitmap - Store(PRS, Local5) - // Local2 = last read byte from bitmap - Store(Zero, Local2) - // Local0 = Processor ID / APIC ID iterator - Store(Zero, Local0) - While (LLess(Local0, SizeOf(CPON))) { - // Local1 = CPON flag for this cpu - Store(DerefOf(Index(CPON, Local0)), Local1) - If (And(Local0, 0x07)) { - // Shift down previously read bitmap byte - ShiftRight(Local2, 1, Local2) - } Else { - // Read next byte from cpu bitmap - Store(DerefOf(Index(Local5, ShiftRight(Local0, 3))), Local2) - } - // Local3 = active state for this cpu - Store(And(Local2, 1), Local3) - - If (LNotEqual(Local1, Local3)) { - // State change - update CPON with new state - Store(Local3, Index(CPON, Local0)) - // Do CPU notify - If (LEqual(Local3, 1)) { - NTFY(Local0, 1) - } Else { - NTFY(Local0, 3) - } - } - Increment(Local0) - } - } -} diff --git a/qemu/hw/i386/acpi-dsdt-dbug.dsl b/qemu/hw/i386/acpi-dsdt-dbug.dsl deleted file mode 100644 index 86230f75a..000000000 --- a/qemu/hw/i386/acpi-dsdt-dbug.dsl +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 . - */ - -/**************************************************************** - * Debugging - ****************************************************************/ - -Scope(\) { - /* Debug Output */ - OperationRegion(DBG, SystemIO, 0x0402, 0x01) - Field(DBG, ByteAcc, NoLock, Preserve) { - DBGB, 8, - } - - /* Debug method - use this method to send output to the QEMU - * BIOS debug port. This method handles strings, integers, - * and buffers. For example: DBUG("abc") DBUG(0x123) */ - Method(DBUG, 1) { - ToHexString(Arg0, Local0) - ToBuffer(Local0, Local0) - Subtract(SizeOf(Local0), 1, Local1) - Store(Zero, Local2) - While (LLess(Local2, Local1)) { - Store(DerefOf(Index(Local0, Local2)), DBGB) - Increment(Local2) - } - Store(0x0A, DBGB) - } -} diff --git a/qemu/hw/i386/acpi-dsdt-hpet.dsl b/qemu/hw/i386/acpi-dsdt-hpet.dsl deleted file mode 100644 index 44961b87a..000000000 --- a/qemu/hw/i386/acpi-dsdt-hpet.dsl +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 . - */ - -/**************************************************************** - * HPET - ****************************************************************/ - -Scope(\_SB) { - Device(HPET) { - Name(_HID, EISAID("PNP0103")) - Name(_UID, 0) - OperationRegion(HPTM, SystemMemory, 0xFED00000, 0x400) - Field(HPTM, DWordAcc, Lock, Preserve) { - VEND, 32, - PRD, 32, - } - Method(_STA, 0, NotSerialized) { - Store(VEND, Local0) - Store(PRD, Local1) - ShiftRight(Local0, 16, Local0) - If (LOr(LEqual(Local0, 0), LEqual(Local0, 0xffff))) { - Return (0x0) - } - If (LOr(LEqual(Local1, 0), LGreater(Local1, 100000000))) { - Return (0x0) - } - Return (0x0F) - } - Name(_CRS, ResourceTemplate() { - Memory32Fixed(ReadOnly, - 0xFED00000, // Address Base - 0x00000400, // Address Length - ) - }) - } -} diff --git a/qemu/hw/i386/acpi-dsdt-isa.dsl b/qemu/hw/i386/acpi-dsdt-isa.dsl deleted file mode 100644 index 89caa1649..000000000 --- a/qemu/hw/i386/acpi-dsdt-isa.dsl +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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 . - */ - -/* Common legacy ISA style devices. */ -Scope(\_SB.PCI0.ISA) { - - Device(RTC) { - Name(_HID, EisaId("PNP0B00")) - Name(_CRS, ResourceTemplate() { - IO(Decode16, 0x0070, 0x0070, 0x10, 0x02) - IRQNoFlags() { 8 } - IO(Decode16, 0x0072, 0x0072, 0x02, 0x06) - }) - } - - Device(KBD) { - Name(_HID, EisaId("PNP0303")) - Method(_STA, 0, NotSerialized) { - Return (0x0f) - } - Name(_CRS, ResourceTemplate() { - IO(Decode16, 0x0060, 0x0060, 0x01, 0x01) - IO(Decode16, 0x0064, 0x0064, 0x01, 0x01) - IRQNoFlags() { 1 } - }) - } - - Device(MOU) { - Name(_HID, EisaId("PNP0F13")) - Method(_STA, 0, NotSerialized) { - Return (0x0f) - } - Name(_CRS, ResourceTemplate() { - IRQNoFlags() { 12 } - }) - } - - Device(FDC0) { - Name(_HID, EisaId("PNP0700")) - Method(_STA, 0, NotSerialized) { - Store(FDEN, Local0) - If (LEqual(Local0, 0)) { - Return (0x00) - } Else { - Return (0x0F) - } - } - Name(_CRS, ResourceTemplate() { - IO(Decode16, 0x03F2, 0x03F2, 0x00, 0x04) - IO(Decode16, 0x03F7, 0x03F7, 0x00, 0x01) - IRQNoFlags() { 6 } - DMA(Compatibility, NotBusMaster, Transfer8) { 2 } - }) - } - - Device(LPT) { - Name(_HID, EisaId("PNP0400")) - Method(_STA, 0, NotSerialized) { - Store(LPEN, Local0) - If (LEqual(Local0, 0)) { - Return (0x00) - } Else { - Return (0x0F) - } - } - Name(_CRS, ResourceTemplate() { - IO(Decode16, 0x0378, 0x0378, 0x08, 0x08) - IRQNoFlags() { 7 } - }) - } - - Device(COM1) { - Name(_HID, EisaId("PNP0501")) - Name(_UID, 0x01) - Method(_STA, 0, NotSerialized) { - Store(CAEN, Local0) - If (LEqual(Local0, 0)) { - Return (0x00) - } Else { - Return (0x0F) - } - } - Name(_CRS, ResourceTemplate() { - IO(Decode16, 0x03F8, 0x03F8, 0x00, 0x08) - IRQNoFlags() { 4 } - }) - } - - Device(COM2) { - Name(_HID, EisaId("PNP0501")) - Name(_UID, 0x02) - Method(_STA, 0, NotSerialized) { - Store(CBEN, Local0) - If (LEqual(Local0, 0)) { - Return (0x00) - } Else { - Return (0x0F) - } - } - Name(_CRS, ResourceTemplate() { - IO(Decode16, 0x02F8, 0x02F8, 0x00, 0x08) - IRQNoFlags() { 3 } - }) - } -} diff --git a/qemu/hw/i386/acpi-dsdt-mem-hotplug.dsl b/qemu/hw/i386/acpi-dsdt-mem-hotplug.dsl deleted file mode 100644 index c2bb6a160..000000000 --- a/qemu/hw/i386/acpi-dsdt-mem-hotplug.dsl +++ /dev/null @@ -1,171 +0,0 @@ -/* - * 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 . - */ - - External(MEMORY_SLOT_NOTIFY_METHOD, MethodObj) - - Scope(\_SB.PCI0) { - Device(MEMORY_HOTPLUG_DEVICE) { - Name(_HID, "PNP0A06") - Name(_UID, "Memory hotplug resources") - External(MEMORY_SLOTS_NUMBER, IntObj) - - /* Memory hotplug IO registers */ - External(MEMORY_SLOT_ADDR_LOW, FieldUnitObj) // read only - External(MEMORY_SLOT_ADDR_HIGH, FieldUnitObj) // read only - External(MEMORY_SLOT_SIZE_LOW, FieldUnitObj) // read only - External(MEMORY_SLOT_SIZE_HIGH, FieldUnitObj) // read only - External(MEMORY_SLOT_PROXIMITY, FieldUnitObj) // read only - External(MEMORY_SLOT_ENABLED, FieldUnitObj) // 1 if enabled, read only - External(MEMORY_SLOT_INSERT_EVENT, FieldUnitObj) // (read) 1 if has a insert event. (write) 1 to clear event - External(MEMORY_SLOT_REMOVE_EVENT, FieldUnitObj) // (read) 1 if has a remove event. (write) 1 to clear event - External(MEMORY_SLOT_EJECT, FieldUnitObj) // initiates device eject, write only - External(MEMORY_SLOT_SLECTOR, FieldUnitObj) // DIMM selector, write only - External(MEMORY_SLOT_OST_EVENT, FieldUnitObj) // _OST event code, write only - External(MEMORY_SLOT_OST_STATUS, FieldUnitObj) // _OST status code, write only - - Method(_STA, 0) { - If (LEqual(MEMORY_SLOTS_NUMBER, Zero)) { - Return(0x0) - } - /* present, functioning, decoding, not shown in UI */ - Return(0xB) - } - - Mutex (MEMORY_SLOT_LOCK, 0) - - Method(MEMORY_SLOT_SCAN_METHOD, 0) { - If (LEqual(MEMORY_SLOTS_NUMBER, Zero)) { - Return(Zero) - } - - Store(Zero, Local0) // Mem devs iterrator - Acquire(MEMORY_SLOT_LOCK, 0xFFFF) - while (LLess(Local0, MEMORY_SLOTS_NUMBER)) { - Store(Local0, MEMORY_SLOT_SLECTOR) // select Local0 DIMM - If (LEqual(MEMORY_SLOT_INSERT_EVENT, One)) { // Memory device needs check - MEMORY_SLOT_NOTIFY_METHOD(Local0, 1) - Store(1, MEMORY_SLOT_INSERT_EVENT) - } Elseif (LEqual(MEMORY_SLOT_REMOVE_EVENT, One)) { // Ejection request - MEMORY_SLOT_NOTIFY_METHOD(Local0, 3) - Store(1, MEMORY_SLOT_REMOVE_EVENT) - } - Add(Local0, One, Local0) // goto next DIMM - } - Release(MEMORY_SLOT_LOCK) - Return(One) - } - - Method(MEMORY_SLOT_STATUS_METHOD, 1) { - Store(Zero, Local0) - - Acquire(MEMORY_SLOT_LOCK, 0xFFFF) - Store(ToInteger(Arg0), MEMORY_SLOT_SLECTOR) // select DIMM - - If (LEqual(MEMORY_SLOT_ENABLED, One)) { - Store(0xF, Local0) - } - - Release(MEMORY_SLOT_LOCK) - Return(Local0) - } - - Method(MEMORY_SLOT_CRS_METHOD, 1, Serialized) { - Acquire(MEMORY_SLOT_LOCK, 0xFFFF) - Store(ToInteger(Arg0), MEMORY_SLOT_SLECTOR) // select DIMM - - Name(MR64, ResourceTemplate() { - QWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, - Cacheable, ReadWrite, - 0x0000000000000000, // Address Space Granularity - 0x0000000000000000, // Address Range Minimum - 0xFFFFFFFFFFFFFFFE, // Address Range Maximum - 0x0000000000000000, // Address Translation Offset - 0xFFFFFFFFFFFFFFFF, // Address Length - ,, MW64, AddressRangeMemory, TypeStatic) - }) - - CreateDWordField(MR64, 14, MINL) - CreateDWordField(MR64, 18, MINH) - CreateDWordField(MR64, 38, LENL) - CreateDWordField(MR64, 42, LENH) - CreateDWordField(MR64, 22, MAXL) - CreateDWordField(MR64, 26, MAXH) - - Store(MEMORY_SLOT_ADDR_HIGH, MINH) - Store(MEMORY_SLOT_ADDR_LOW, MINL) - Store(MEMORY_SLOT_SIZE_HIGH, LENH) - Store(MEMORY_SLOT_SIZE_LOW, LENL) - - // 64-bit math: MAX = MIN + LEN - 1 - Add(MINL, LENL, MAXL) - Add(MINH, LENH, MAXH) - If (LLess(MAXL, MINL)) { - Add(MAXH, One, MAXH) - } - If (LLess(MAXL, One)) { - Subtract(MAXH, One, MAXH) - } - Subtract(MAXL, One, MAXL) - - If (LEqual(MAXH, Zero)){ - Name(MR32, ResourceTemplate() { - DWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, - Cacheable, ReadWrite, - 0x00000000, // Address Space Granularity - 0x00000000, // Address Range Minimum - 0xFFFFFFFE, // Address Range Maximum - 0x00000000, // Address Translation Offset - 0xFFFFFFFF, // Address Length - ,, MW32, AddressRangeMemory, TypeStatic) - }) - CreateDWordField(MR32, MW32._MIN, MIN) - CreateDWordField(MR32, MW32._MAX, MAX) - CreateDWordField(MR32, MW32._LEN, LEN) - Store(MINL, MIN) - Store(MAXL, MAX) - Store(LENL, LEN) - - Release(MEMORY_SLOT_LOCK) - Return(MR32) - } - - Release(MEMORY_SLOT_LOCK) - Return(MR64) - } - - Method(MEMORY_SLOT_PROXIMITY_METHOD, 1) { - Acquire(MEMORY_SLOT_LOCK, 0xFFFF) - Store(ToInteger(Arg0), MEMORY_SLOT_SLECTOR) // select DIMM - Store(MEMORY_SLOT_PROXIMITY, Local0) - Release(MEMORY_SLOT_LOCK) - Return(Local0) - } - - Method(MEMORY_SLOT_OST_METHOD, 4) { - Acquire(MEMORY_SLOT_LOCK, 0xFFFF) - Store(ToInteger(Arg0), MEMORY_SLOT_SLECTOR) // select DIMM - Store(Arg1, MEMORY_SLOT_OST_EVENT) - Store(Arg2, MEMORY_SLOT_OST_STATUS) - Release(MEMORY_SLOT_LOCK) - } - - Method(MEMORY_SLOT_EJECT_METHOD, 2) { - Acquire(MEMORY_SLOT_LOCK, 0xFFFF) - Store(ToInteger(Arg0), MEMORY_SLOT_SLECTOR) // select DIMM - Store(1, MEMORY_SLOT_EJECT) - Release(MEMORY_SLOT_LOCK) - } - } // Device() - } // Scope() diff --git a/qemu/hw/i386/acpi-dsdt.dsl b/qemu/hw/i386/acpi-dsdt.dsl deleted file mode 100644 index a2d84ecf8..000000000 --- a/qemu/hw/i386/acpi-dsdt.dsl +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Bochs/QEMU ACPI DSDT ASL definition - * - * Copyright (c) 2006 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 version 2 as published by the Free Software Foundation. - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -ACPI_EXTRACT_ALL_CODE AcpiDsdtAmlCode - -DefinitionBlock ( - "acpi-dsdt.aml", // Output Filename - "DSDT", // Signature - 0x01, // DSDT Compliance Revision - "BXPC", // OEMID - "BXDSDT", // TABLE ID - 0x1 // OEM Revision - ) -{ - -#include "acpi-dsdt-dbug.dsl" - - Scope(\_SB) { - Device(PCI0) { - Name(_HID, EisaId("PNP0A03")) - Name(_ADR, 0x00) - Name(_UID, 1) -//#define PX13 S0B_ -// External(PX13, DeviceObj) - } - } - -#include "acpi-dsdt-hpet.dsl" - - -/**************************************************************** - * PIIX4 PM - ****************************************************************/ - - Scope(\_SB.PCI0) { - Device(PX13) { - Name(_ADR, 0x00010003) - OperationRegion(P13C, PCI_Config, 0x00, 0xff) - } - } - - -/**************************************************************** - * PIIX3 ISA bridge - ****************************************************************/ - - Scope(\_SB.PCI0) { - - External(ISA, DeviceObj) - - Device(ISA) { - Name(_ADR, 0x00010000) - - /* PIIX PCI to ISA irq remapping */ - OperationRegion(P40C, PCI_Config, 0x60, 0x04) - - /* enable bits */ - Field(\_SB.PCI0.PX13.P13C, AnyAcc, NoLock, Preserve) { - Offset(0x5f), - , 7, - LPEN, 1, // LPT - Offset(0x67), - , 3, - CAEN, 1, // COM1 - , 3, - CBEN, 1, // COM2 - } - Name(FDEN, 1) - } - } - -#include "acpi-dsdt-isa.dsl" - - -/**************************************************************** - * PCI hotplug - ****************************************************************/ - - Scope(\_SB.PCI0) { - OperationRegion(PCST, SystemIO, 0xae00, 0x08) - Field(PCST, DWordAcc, NoLock, WriteAsZeros) { - PCIU, 32, - PCID, 32, - } - - OperationRegion(SEJ, SystemIO, 0xae08, 0x04) - Field(SEJ, DWordAcc, NoLock, WriteAsZeros) { - B0EJ, 32, - } - - OperationRegion(BNMR, SystemIO, 0xae10, 0x04) - Field(BNMR, DWordAcc, NoLock, WriteAsZeros) { - BNUM, 32, - } - - /* Lock to protect access to fields above. */ - Mutex(BLCK, 0) - - /* Methods called by bulk generated PCI devices below */ - - /* Methods called by hotplug devices */ - Method(PCEJ, 2, NotSerialized) { - // _EJ0 method - eject callback - Acquire(BLCK, 0xFFFF) - Store(Arg0, BNUM) - Store(ShiftLeft(1, Arg1), B0EJ) - Release(BLCK) - Return (0x0) - } - - /* Hotplug notification method supplied by SSDT */ - External(\_SB.PCI0.PCNT, MethodObj) - } - - -/**************************************************************** - * PCI IRQs - ****************************************************************/ - - Scope(\_SB) { - Scope(PCI0) { - Method (_PRT, 0) { - Store(Package(128) {}, Local0) - Store(Zero, Local1) - While(LLess(Local1, 128)) { - // slot = pin >> 2 - Store(ShiftRight(Local1, 2), Local2) - - // lnk = (slot + pin) & 3 - Store(And(Add(Local1, Local2), 3), Local3) - If (LEqual(Local3, 0)) { - Store(Package(4) { Zero, Zero, LNKD, Zero }, Local4) - } - If (LEqual(Local3, 1)) { - // device 1 is the power-management device, needs SCI - If (LEqual(Local1, 4)) { - Store(Package(4) { Zero, Zero, LNKS, Zero }, Local4) - } Else { - Store(Package(4) { Zero, Zero, LNKA, Zero }, Local4) - } - } - If (LEqual(Local3, 2)) { - Store(Package(4) { Zero, Zero, LNKB, Zero }, Local4) - } - If (LEqual(Local3, 3)) { - Store(Package(4) { Zero, Zero, LNKC, Zero }, Local4) - } - - // Complete the interrupt routing entry: - // Package(4) { 0x[slot]FFFF, [pin], [link], 0) } - - Store(Or(ShiftLeft(Local2, 16), 0xFFFF), Index(Local4, 0)) - Store(And(Local1, 3), Index(Local4, 1)) - Store(Local4, Index(Local0, Local1)) - - Increment(Local1) - } - - Return(Local0) - } - } - - Field(PCI0.ISA.P40C, ByteAcc, NoLock, Preserve) { - PRQ0, 8, - PRQ1, 8, - PRQ2, 8, - PRQ3, 8 - } - - Method(IQST, 1, NotSerialized) { - // _STA method - get status - If (And(0x80, Arg0)) { - Return (0x09) - } - Return (0x0B) - } - Method(IQCR, 1, Serialized) { - // _CRS method - get current settings - Name(PRR0, ResourceTemplate() { - Interrupt(, Level, ActiveHigh, Shared) { 0 } - }) - CreateDWordField(PRR0, 0x05, PRRI) - If (LLess(Arg0, 0x80)) { - Store(Arg0, PRRI) - } - Return (PRR0) - } - -#define define_link(link, uid, reg) \ - Device(link) { \ - Name(_HID, EISAID("PNP0C0F")) \ - Name(_UID, uid) \ - Name(_PRS, ResourceTemplate() { \ - Interrupt(, Level, ActiveHigh, Shared) { \ - 5, 10, 11 \ - } \ - }) \ - Method(_STA, 0, NotSerialized) { \ - Return (IQST(reg)) \ - } \ - Method(_DIS, 0, NotSerialized) { \ - Or(reg, 0x80, reg) \ - } \ - Method(_CRS, 0, NotSerialized) { \ - Return (IQCR(reg)) \ - } \ - Method(_SRS, 1, NotSerialized) { \ - CreateDWordField(Arg0, 0x05, PRRI) \ - Store(PRRI, reg) \ - } \ - } - - define_link(LNKA, 0, PRQ0) - define_link(LNKB, 1, PRQ1) - define_link(LNKC, 2, PRQ2) - define_link(LNKD, 3, PRQ3) - - Device(LNKS) { - Name(_HID, EISAID("PNP0C0F")) - Name(_UID, 4) - Name(_PRS, ResourceTemplate() { - Interrupt(, Level, ActiveHigh, Shared) { 9 } - }) - - // The SCI cannot be disabled and is always attached to GSI 9, - // so these are no-ops. We only need this link to override the - // polarity to active high and match the content of the MADT. - Method(_STA, 0, NotSerialized) { Return (0x0b) } - Method(_DIS, 0, NotSerialized) { } - Method(_CRS, 0, NotSerialized) { Return (_PRS) } - Method(_SRS, 1, NotSerialized) { } - } - } - -#include "hw/acpi/pc-hotplug.h" -#define CPU_STATUS_BASE PIIX4_CPU_HOTPLUG_IO_BASE -#include "acpi-dsdt-cpu-hotplug.dsl" -#include "acpi-dsdt-mem-hotplug.dsl" - - -/**************************************************************** - * General purpose events - ****************************************************************/ - Scope(\_GPE) { - Name(_HID, "ACPI0006") - - Method(_L00) { - } - Method(_E01) { - // PCI hotplug event - Acquire(\_SB.PCI0.BLCK, 0xFFFF) - \_SB.PCI0.PCNT() - Release(\_SB.PCI0.BLCK) - } - Method(_E02) { - // CPU hotplug event - \_SB.PRSC() - } - Method(_E03) { - // Memory hotplug event - \_SB.PCI0.MEMORY_HOTPLUG_DEVICE.MEMORY_SLOT_SCAN_METHOD() - } - Method(_L04) { - } - Method(_L05) { - } - Method(_L06) { - } - Method(_L07) { - } - Method(_L08) { - } - Method(_L09) { - } - Method(_L0A) { - } - Method(_L0B) { - } - Method(_L0C) { - } - Method(_L0D) { - } - Method(_L0E) { - } - Method(_L0F) { - } - } -} diff --git a/qemu/hw/i386/acpi-dsdt.hex.generated b/qemu/hw/i386/acpi-dsdt.hex.generated deleted file mode 100644 index ecaa4a548..000000000 --- a/qemu/hw/i386/acpi-dsdt.hex.generated +++ /dev/null @@ -1,2972 +0,0 @@ -static unsigned char AcpiDsdtAmlCode[] = { -0x44, -0x53, -0x44, -0x54, -0x9a, -0xb, -0x0, -0x0, -0x1, -0xf8, -0x42, -0x58, -0x50, -0x43, -0x0, -0x0, -0x42, -0x58, -0x44, -0x53, -0x44, -0x54, -0x0, -0x0, -0x1, -0x0, -0x0, -0x0, -0x49, -0x4e, -0x54, -0x4c, -0x7, -0x11, -0x14, -0x20, -0x10, -0x49, -0x4, -0x5c, -0x0, -0x5b, -0x80, -0x44, -0x42, -0x47, -0x5f, -0x1, -0xb, -0x2, -0x4, -0x1, -0x5b, -0x81, -0xb, -0x44, -0x42, -0x47, -0x5f, -0x1, -0x44, -0x42, -0x47, -0x42, -0x8, -0x14, -0x2c, -0x44, -0x42, -0x55, -0x47, -0x1, -0x98, -0x68, -0x60, -0x96, -0x60, -0x60, -0x74, -0x87, -0x60, -0x1, -0x61, -0x70, -0x0, -0x62, -0xa2, -0x10, -0x95, -0x62, -0x61, -0x70, -0x83, -0x88, -0x60, -0x62, -0x0, -0x44, -0x42, -0x47, -0x42, -0x75, -0x62, -0x70, -0xa, -0xa, -0x44, -0x42, -0x47, -0x42, -0x10, -0x22, -0x5f, -0x53, -0x42, -0x5f, -0x5b, -0x82, -0x1b, -0x50, -0x43, -0x49, -0x30, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xa, -0x3, -0x8, -0x5f, -0x41, -0x44, -0x52, -0x0, -0x8, -0x5f, -0x55, -0x49, -0x44, -0x1, -0x10, -0x4d, -0x8, -0x5f, -0x53, -0x42, -0x5f, -0x5b, -0x82, -0x45, -0x8, -0x48, -0x50, -0x45, -0x54, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0x1, -0x3, -0x8, -0x5f, -0x55, -0x49, -0x44, -0x0, -0x5b, -0x80, -0x48, -0x50, -0x54, -0x4d, -0x0, -0xc, -0x0, -0x0, -0xd0, -0xfe, -0xb, -0x0, -0x4, -0x5b, -0x81, -0x10, -0x48, -0x50, -0x54, -0x4d, -0x13, -0x56, -0x45, -0x4e, -0x44, -0x20, -0x50, -0x52, -0x44, -0x5f, -0x20, -0x14, -0x36, -0x5f, -0x53, -0x54, -0x41, -0x0, -0x70, -0x56, -0x45, -0x4e, -0x44, -0x60, -0x70, -0x50, -0x52, -0x44, -0x5f, -0x61, -0x7a, -0x60, -0xa, -0x10, -0x60, -0xa0, -0xc, -0x91, -0x93, -0x60, -0x0, -0x93, -0x60, -0xb, -0xff, -0xff, -0xa4, -0x0, -0xa0, -0xe, -0x91, -0x93, -0x61, -0x0, -0x94, -0x61, -0xc, -0x0, -0xe1, -0xf5, -0x5, -0xa4, -0x0, -0xa4, -0xa, -0xf, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x11, -0xa, -0xe, -0x86, -0x9, -0x0, -0x0, -0x0, -0x0, -0xd0, -0xfe, -0x0, -0x4, -0x0, -0x0, -0x79, -0x0, -0x10, -0x25, -0x2e, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x5b, -0x82, -0x19, -0x50, -0x58, -0x31, -0x33, -0x8, -0x5f, -0x41, -0x44, -0x52, -0xc, -0x3, -0x0, -0x1, -0x0, -0x5b, -0x80, -0x50, -0x31, -0x33, -0x43, -0x2, -0x0, -0xa, -0xff, -0x10, -0x46, -0x5, -0x2e, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x5b, -0x82, -0x49, -0x4, -0x49, -0x53, -0x41, -0x5f, -0x8, -0x5f, -0x41, -0x44, -0x52, -0xc, -0x0, -0x0, -0x1, -0x0, -0x5b, -0x80, -0x50, -0x34, -0x30, -0x43, -0x2, -0xa, -0x60, -0xa, -0x4, -0x5b, -0x81, -0x26, -0x5e, -0x2e, -0x50, -0x58, -0x31, -0x33, -0x50, -0x31, -0x33, -0x43, -0x0, -0x0, -0x48, -0x2f, -0x0, -0x7, -0x4c, -0x50, -0x45, -0x4e, -0x1, -0x0, -0x38, -0x0, -0x3, -0x43, -0x41, -0x45, -0x4e, -0x1, -0x0, -0x3, -0x43, -0x42, -0x45, -0x4e, -0x1, -0x8, -0x46, -0x44, -0x45, -0x4e, -0x1, -0x10, -0x4c, -0x1b, -0x2f, -0x3, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x49, -0x53, -0x41, -0x5f, -0x5b, -0x82, -0x2d, -0x52, -0x54, -0x43, -0x5f, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xb, -0x0, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x18, -0xa, -0x15, -0x47, -0x1, -0x70, -0x0, -0x70, -0x0, -0x10, -0x2, -0x22, -0x0, -0x1, -0x47, -0x1, -0x72, -0x0, -0x72, -0x0, -0x2, -0x6, -0x79, -0x0, -0x5b, -0x82, -0x37, -0x4b, -0x42, -0x44, -0x5f, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0x3, -0x3, -0x14, -0x9, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0xa, -0xf, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x18, -0xa, -0x15, -0x47, -0x1, -0x60, -0x0, -0x60, -0x0, -0x1, -0x1, -0x47, -0x1, -0x64, -0x0, -0x64, -0x0, -0x1, -0x1, -0x22, -0x2, -0x0, -0x79, -0x0, -0x5b, -0x82, -0x27, -0x4d, -0x4f, -0x55, -0x5f, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xf, -0x13, -0x14, -0x9, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0xa, -0xf, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x8, -0xa, -0x5, -0x22, -0x0, -0x10, -0x79, -0x0, -0x5b, -0x82, -0x4a, -0x4, -0x46, -0x44, -0x43, -0x30, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0x7, -0x0, -0x14, -0x18, -0x5f, -0x53, -0x54, -0x41, -0x0, -0x70, -0x46, -0x44, -0x45, -0x4e, -0x60, -0xa0, -0x6, -0x93, -0x60, -0x0, -0xa4, -0x0, -0xa1, -0x4, -0xa4, -0xa, -0xf, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x1b, -0xa, -0x18, -0x47, -0x1, -0xf2, -0x3, -0xf2, -0x3, -0x0, -0x4, -0x47, -0x1, -0xf7, -0x3, -0xf7, -0x3, -0x0, -0x1, -0x22, -0x40, -0x0, -0x2a, -0x4, -0x0, -0x79, -0x0, -0x5b, -0x82, -0x3e, -0x4c, -0x50, -0x54, -0x5f, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0x4, -0x0, -0x14, -0x18, -0x5f, -0x53, -0x54, -0x41, -0x0, -0x70, -0x4c, -0x50, -0x45, -0x4e, -0x60, -0xa0, -0x6, -0x93, -0x60, -0x0, -0xa4, -0x0, -0xa1, -0x4, -0xa4, -0xa, -0xf, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x10, -0xa, -0xd, -0x47, -0x1, -0x78, -0x3, -0x78, -0x3, -0x8, -0x8, -0x22, -0x80, -0x0, -0x79, -0x0, -0x5b, -0x82, -0x45, -0x4, -0x43, -0x4f, -0x4d, -0x31, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0x5, -0x1, -0x8, -0x5f, -0x55, -0x49, -0x44, -0x1, -0x14, -0x18, -0x5f, -0x53, -0x54, -0x41, -0x0, -0x70, -0x43, -0x41, -0x45, -0x4e, -0x60, -0xa0, -0x6, -0x93, -0x60, -0x0, -0xa4, -0x0, -0xa1, -0x4, -0xa4, -0xa, -0xf, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x10, -0xa, -0xd, -0x47, -0x1, -0xf8, -0x3, -0xf8, -0x3, -0x0, -0x8, -0x22, -0x10, -0x0, -0x79, -0x0, -0x5b, -0x82, -0x46, -0x4, -0x43, -0x4f, -0x4d, -0x32, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0x5, -0x1, -0x8, -0x5f, -0x55, -0x49, -0x44, -0xa, -0x2, -0x14, -0x18, -0x5f, -0x53, -0x54, -0x41, -0x0, -0x70, -0x43, -0x42, -0x45, -0x4e, -0x60, -0xa0, -0x6, -0x93, -0x60, -0x0, -0xa4, -0x0, -0xa1, -0x4, -0xa4, -0xa, -0xf, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x10, -0xa, -0xd, -0x47, -0x1, -0xf8, -0x2, -0xf8, -0x2, -0x0, -0x8, -0x22, -0x8, -0x0, -0x79, -0x0, -0x10, -0x48, -0x8, -0x2e, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x5b, -0x80, -0x50, -0x43, -0x53, -0x54, -0x1, -0xb, -0x0, -0xae, -0xa, -0x8, -0x5b, -0x81, -0x10, -0x50, -0x43, -0x53, -0x54, -0x43, -0x50, -0x43, -0x49, -0x55, -0x20, -0x50, -0x43, -0x49, -0x44, -0x20, -0x5b, -0x80, -0x53, -0x45, -0x4a, -0x5f, -0x1, -0xb, -0x8, -0xae, -0xa, -0x4, -0x5b, -0x81, -0xb, -0x53, -0x45, -0x4a, -0x5f, -0x43, -0x42, -0x30, -0x45, -0x4a, -0x20, -0x5b, -0x80, -0x42, -0x4e, -0x4d, -0x52, -0x1, -0xb, -0x10, -0xae, -0xa, -0x4, -0x5b, -0x81, -0xb, -0x42, -0x4e, -0x4d, -0x52, -0x43, -0x42, -0x4e, -0x55, -0x4d, -0x20, -0x5b, -0x1, -0x42, -0x4c, -0x43, -0x4b, -0x0, -0x14, -0x25, -0x50, -0x43, -0x45, -0x4a, -0x2, -0x5b, -0x23, -0x42, -0x4c, -0x43, -0x4b, -0xff, -0xff, -0x70, -0x68, -0x42, -0x4e, -0x55, -0x4d, -0x70, -0x79, -0x1, -0x69, -0x0, -0x42, -0x30, -0x45, -0x4a, -0x5b, -0x27, -0x42, -0x4c, -0x43, -0x4b, -0xa4, -0x0, -0x10, -0x4e, -0x36, -0x5f, -0x53, -0x42, -0x5f, -0x10, -0x4b, -0xa, -0x50, -0x43, -0x49, -0x30, -0x14, -0x44, -0xa, -0x5f, -0x50, -0x52, -0x54, -0x0, -0x70, -0x12, -0x2, -0x80, -0x60, -0x70, -0x0, -0x61, -0xa2, -0x42, -0x9, -0x95, -0x61, -0xa, -0x80, -0x70, -0x7a, -0x61, -0xa, -0x2, -0x0, -0x62, -0x70, -0x7b, -0x72, -0x61, -0x62, -0x0, -0xa, -0x3, -0x0, -0x63, -0xa0, -0x10, -0x93, -0x63, -0x0, -0x70, -0x12, -0x9, -0x4, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x44, -0x0, -0x64, -0xa0, -0x24, -0x93, -0x63, -0x1, -0xa0, -0x11, -0x93, -0x61, -0xa, -0x4, -0x70, -0x12, -0x9, -0x4, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x53, -0x0, -0x64, -0xa1, -0xd, -0x70, -0x12, -0x9, -0x4, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x41, -0x0, -0x64, -0xa0, -0x11, -0x93, -0x63, -0xa, -0x2, -0x70, -0x12, -0x9, -0x4, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x42, -0x0, -0x64, -0xa0, -0x11, -0x93, -0x63, -0xa, -0x3, -0x70, -0x12, -0x9, -0x4, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x43, -0x0, -0x64, -0x70, -0x7d, -0x79, -0x62, -0xa, -0x10, -0x0, -0xb, -0xff, -0xff, -0x0, -0x88, -0x64, -0x0, -0x0, -0x70, -0x7b, -0x61, -0xa, -0x3, -0x0, -0x88, -0x64, -0x1, -0x0, -0x70, -0x64, -0x88, -0x60, -0x61, -0x0, -0x75, -0x61, -0xa4, -0x60, -0x5b, -0x81, -0x24, -0x2f, -0x3, -0x50, -0x43, -0x49, -0x30, -0x49, -0x53, -0x41, -0x5f, -0x50, -0x34, -0x30, -0x43, -0x1, -0x50, -0x52, -0x51, -0x30, -0x8, -0x50, -0x52, -0x51, -0x31, -0x8, -0x50, -0x52, -0x51, -0x32, -0x8, -0x50, -0x52, -0x51, -0x33, -0x8, -0x14, -0x13, -0x49, -0x51, -0x53, -0x54, -0x1, -0xa0, -0x9, -0x7b, -0xa, -0x80, -0x68, -0x0, -0xa4, -0xa, -0x9, -0xa4, -0xa, -0xb, -0x14, -0x36, -0x49, -0x51, -0x43, -0x52, -0x9, -0x8, -0x50, -0x52, -0x52, -0x30, -0x11, -0xe, -0xa, -0xb, -0x89, -0x6, -0x0, -0x9, -0x1, -0x0, -0x0, -0x0, -0x0, -0x79, -0x0, -0x8a, -0x50, -0x52, -0x52, -0x30, -0xa, -0x5, -0x50, -0x52, -0x52, -0x49, -0xa0, -0xb, -0x95, -0x68, -0xa, -0x80, -0x70, -0x68, -0x50, -0x52, -0x52, -0x49, -0xa4, -0x50, -0x52, -0x52, -0x30, -0x5b, -0x82, -0x4c, -0x7, -0x4c, -0x4e, -0x4b, -0x41, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0x0, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0x16, -0xa, -0x13, -0x89, -0xe, -0x0, -0x9, -0x3, -0x5, -0x0, -0x0, -0x0, -0xa, -0x0, -0x0, -0x0, -0xb, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0xf, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0x49, -0x51, -0x53, -0x54, -0x50, -0x52, -0x51, -0x30, -0x14, -0x11, -0x5f, -0x44, -0x49, -0x53, -0x0, -0x7d, -0x50, -0x52, -0x51, -0x30, -0xa, -0x80, -0x50, -0x52, -0x51, -0x30, -0x14, -0xf, -0x5f, -0x43, -0x52, -0x53, -0x0, -0xa4, -0x49, -0x51, -0x43, -0x52, -0x50, -0x52, -0x51, -0x30, -0x14, -0x17, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x8a, -0x68, -0xa, -0x5, -0x50, -0x52, -0x52, -0x49, -0x70, -0x50, -0x52, -0x52, -0x49, -0x50, -0x52, -0x51, -0x30, -0x5b, -0x82, -0x4c, -0x7, -0x4c, -0x4e, -0x4b, -0x42, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0x1, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0x16, -0xa, -0x13, -0x89, -0xe, -0x0, -0x9, -0x3, -0x5, -0x0, -0x0, -0x0, -0xa, -0x0, -0x0, -0x0, -0xb, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0xf, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0x49, -0x51, -0x53, -0x54, -0x50, -0x52, -0x51, -0x31, -0x14, -0x11, -0x5f, -0x44, -0x49, -0x53, -0x0, -0x7d, -0x50, -0x52, -0x51, -0x31, -0xa, -0x80, -0x50, -0x52, -0x51, -0x31, -0x14, -0xf, -0x5f, -0x43, -0x52, -0x53, -0x0, -0xa4, -0x49, -0x51, -0x43, -0x52, -0x50, -0x52, -0x51, -0x31, -0x14, -0x17, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x8a, -0x68, -0xa, -0x5, -0x50, -0x52, -0x52, -0x49, -0x70, -0x50, -0x52, -0x52, -0x49, -0x50, -0x52, -0x51, -0x31, -0x5b, -0x82, -0x4d, -0x7, -0x4c, -0x4e, -0x4b, -0x43, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0xa, -0x2, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0x16, -0xa, -0x13, -0x89, -0xe, -0x0, -0x9, -0x3, -0x5, -0x0, -0x0, -0x0, -0xa, -0x0, -0x0, -0x0, -0xb, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0xf, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0x49, -0x51, -0x53, -0x54, -0x50, -0x52, -0x51, -0x32, -0x14, -0x11, -0x5f, -0x44, -0x49, -0x53, -0x0, -0x7d, -0x50, -0x52, -0x51, -0x32, -0xa, -0x80, -0x50, -0x52, -0x51, -0x32, -0x14, -0xf, -0x5f, -0x43, -0x52, -0x53, -0x0, -0xa4, -0x49, -0x51, -0x43, -0x52, -0x50, -0x52, -0x51, -0x32, -0x14, -0x17, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x8a, -0x68, -0xa, -0x5, -0x50, -0x52, -0x52, -0x49, -0x70, -0x50, -0x52, -0x52, -0x49, -0x50, -0x52, -0x51, -0x32, -0x5b, -0x82, -0x4d, -0x7, -0x4c, -0x4e, -0x4b, -0x44, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0xa, -0x3, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0x16, -0xa, -0x13, -0x89, -0xe, -0x0, -0x9, -0x3, -0x5, -0x0, -0x0, -0x0, -0xa, -0x0, -0x0, -0x0, -0xb, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0xf, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0x49, -0x51, -0x53, -0x54, -0x50, -0x52, -0x51, -0x33, -0x14, -0x11, -0x5f, -0x44, -0x49, -0x53, -0x0, -0x7d, -0x50, -0x52, -0x51, -0x33, -0xa, -0x80, -0x50, -0x52, -0x51, -0x33, -0x14, -0xf, -0x5f, -0x43, -0x52, -0x53, -0x0, -0xa4, -0x49, -0x51, -0x43, -0x52, -0x50, -0x52, -0x51, -0x33, -0x14, -0x17, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x8a, -0x68, -0xa, -0x5, -0x50, -0x52, -0x52, -0x49, -0x70, -0x50, -0x52, -0x52, -0x49, -0x50, -0x52, -0x51, -0x33, -0x5b, -0x82, -0x4f, -0x4, -0x4c, -0x4e, -0x4b, -0x53, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0xa, -0x4, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0xe, -0xa, -0xb, -0x89, -0x6, -0x0, -0x9, -0x1, -0x9, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0x9, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0xa, -0xb, -0x14, -0x6, -0x5f, -0x44, -0x49, -0x53, -0x0, -0x14, -0xb, -0x5f, -0x43, -0x52, -0x53, -0x0, -0xa4, -0x5f, -0x50, -0x52, -0x53, -0x14, -0x6, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x10, -0x4d, -0xc, -0x5f, -0x53, -0x42, -0x5f, -0x14, -0x35, -0x43, -0x50, -0x4d, -0x41, -0x1, -0x70, -0x83, -0x88, -0x43, -0x50, -0x4f, -0x4e, -0x68, -0x0, -0x60, -0x70, -0x11, -0xb, -0xa, -0x8, -0x0, -0x8, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x61, -0x70, -0x68, -0x88, -0x61, -0xa, -0x2, -0x0, -0x70, -0x68, -0x88, -0x61, -0xa, -0x3, -0x0, -0x70, -0x60, -0x88, -0x61, -0xa, -0x4, -0x0, -0xa4, -0x61, -0x14, -0x1a, -0x43, -0x50, -0x53, -0x54, -0x1, -0x70, -0x83, -0x88, -0x43, -0x50, -0x4f, -0x4e, -0x68, -0x0, -0x60, -0xa0, -0x5, -0x60, -0xa4, -0xa, -0xf, -0xa1, -0x3, -0xa4, -0x0, -0x14, -0xa, -0x43, -0x50, -0x45, -0x4a, -0x2, -0x5b, -0x22, -0xa, -0xc8, -0x14, -0x4a, -0x6, -0x50, -0x52, -0x53, -0x43, -0x0, -0x70, -0x50, -0x52, -0x53, -0x5f, -0x65, -0x70, -0x0, -0x62, -0x70, -0x0, -0x60, -0xa2, -0x46, -0x5, -0x95, -0x60, -0x87, -0x43, -0x50, -0x4f, -0x4e, -0x70, -0x83, -0x88, -0x43, -0x50, -0x4f, -0x4e, -0x60, -0x0, -0x61, -0xa0, -0xa, -0x7b, -0x60, -0xa, -0x7, -0x0, -0x7a, -0x62, -0x1, -0x62, -0xa1, -0xc, -0x70, -0x83, -0x88, -0x65, -0x7a, -0x60, -0xa, -0x3, -0x0, -0x0, -0x62, -0x70, -0x7b, -0x62, -0x1, -0x0, -0x63, -0xa0, -0x22, -0x92, -0x93, -0x61, -0x63, -0x70, -0x63, -0x88, -0x43, -0x50, -0x4f, -0x4e, -0x60, -0x0, -0xa0, -0xa, -0x93, -0x63, -0x1, -0x4e, -0x54, -0x46, -0x59, -0x60, -0x1, -0xa1, -0x8, -0x4e, -0x54, -0x46, -0x59, -0x60, -0xa, -0x3, -0x75, -0x60, -0x10, -0x44, -0x2a, -0x2e, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x5b, -0x82, -0x47, -0x29, -0x4d, -0x48, -0x50, -0x44, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xd, -0x50, -0x4e, -0x50, -0x30, -0x41, -0x30, -0x36, -0x0, -0x8, -0x5f, -0x55, -0x49, -0x44, -0xd, -0x4d, -0x65, -0x6d, -0x6f, -0x72, -0x79, -0x20, -0x68, -0x6f, -0x74, -0x70, -0x6c, -0x75, -0x67, -0x20, -0x72, -0x65, -0x73, -0x6f, -0x75, -0x72, -0x63, -0x65, -0x73, -0x0, -0x14, -0x13, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa0, -0x9, -0x93, -0x4d, -0x44, -0x4e, -0x52, -0x0, -0xa4, -0x0, -0xa4, -0xa, -0xb, -0x5b, -0x1, -0x4d, -0x4c, -0x43, -0x4b, -0x0, -0x14, -0x4a, -0x4, -0x4d, -0x53, -0x43, -0x4e, -0x0, -0xa0, -0x9, -0x93, -0x4d, -0x44, -0x4e, -0x52, -0x0, -0xa4, -0x0, -0x70, -0x0, -0x60, -0x5b, -0x23, -0x4d, -0x4c, -0x43, -0x4b, -0xff, -0xff, -0xa2, -0x25, -0x95, -0x60, -0x4d, -0x44, -0x4e, -0x52, -0x70, -0x60, -0x4d, -0x53, -0x45, -0x4c, -0xa0, -0x13, -0x93, -0x4d, -0x49, -0x4e, -0x53, -0x1, -0x4d, -0x54, -0x46, -0x59, -0x60, -0x1, -0x70, -0x1, -0x4d, -0x49, -0x4e, -0x53, -0x72, -0x60, -0x1, -0x60, -0x5b, -0x27, -0x4d, -0x4c, -0x43, -0x4b, -0xa4, -0x1, -0x14, -0x2d, -0x4d, -0x52, -0x53, -0x54, -0x1, -0x70, -0x0, -0x60, -0x5b, -0x23, -0x4d, -0x4c, -0x43, -0x4b, -0xff, -0xff, -0x70, -0x99, -0x68, -0x0, -0x4d, -0x53, -0x45, -0x4c, -0xa0, -0xb, -0x93, -0x4d, -0x45, -0x53, -0x5f, -0x1, -0x70, -0xa, -0xf, -0x60, -0x5b, -0x27, -0x4d, -0x4c, -0x43, -0x4b, -0xa4, -0x60, -0x14, -0x41, -0x18, -0x4d, -0x43, -0x52, -0x53, -0x9, -0x5b, -0x23, -0x4d, -0x4c, -0x43, -0x4b, -0xff, -0xff, -0x70, -0x99, -0x68, -0x0, -0x4d, -0x53, -0x45, -0x4c, -0x8, -0x4d, -0x52, -0x36, -0x34, -0x11, -0x33, -0xa, -0x30, -0x8a, -0x2b, -0x0, -0x0, -0xc, -0x3, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0xfe, -0xff, -0xff, -0xff, -0xff, -0xff, -0xff, -0xff, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0xff, -0xff, -0xff, -0xff, -0xff, -0xff, -0xff, -0xff, -0x79, -0x0, -0x8a, -0x4d, -0x52, -0x36, -0x34, -0xa, -0xe, -0x4d, -0x49, -0x4e, -0x4c, -0x8a, -0x4d, -0x52, -0x36, -0x34, -0xa, -0x12, -0x4d, -0x49, -0x4e, -0x48, -0x8a, -0x4d, -0x52, -0x36, -0x34, -0xa, -0x26, -0x4c, -0x45, -0x4e, -0x4c, -0x8a, -0x4d, -0x52, -0x36, -0x34, -0xa, -0x2a, -0x4c, -0x45, -0x4e, -0x48, -0x8a, -0x4d, -0x52, -0x36, -0x34, -0xa, -0x16, -0x4d, -0x41, -0x58, -0x4c, -0x8a, -0x4d, -0x52, -0x36, -0x34, -0xa, -0x1a, -0x4d, -0x41, -0x58, -0x48, -0x70, -0x4d, -0x52, -0x42, -0x48, -0x4d, -0x49, -0x4e, -0x48, -0x70, -0x4d, -0x52, -0x42, -0x4c, -0x4d, -0x49, -0x4e, -0x4c, -0x70, -0x4d, -0x52, -0x4c, -0x48, -0x4c, -0x45, -0x4e, -0x48, -0x70, -0x4d, -0x52, -0x4c, -0x4c, -0x4c, -0x45, -0x4e, -0x4c, -0x72, -0x4d, -0x49, -0x4e, -0x4c, -0x4c, -0x45, -0x4e, -0x4c, -0x4d, -0x41, -0x58, -0x4c, -0x72, -0x4d, -0x49, -0x4e, -0x48, -0x4c, -0x45, -0x4e, -0x48, -0x4d, -0x41, -0x58, -0x48, -0xa0, -0x14, -0x95, -0x4d, -0x41, -0x58, -0x4c, -0x4d, -0x49, -0x4e, -0x4c, -0x72, -0x4d, -0x41, -0x58, -0x48, -0x1, -0x4d, -0x41, -0x58, -0x48, -0xa0, -0x11, -0x95, -0x4d, -0x41, -0x58, -0x4c, -0x1, -0x74, -0x4d, -0x41, -0x58, -0x48, -0x1, -0x4d, -0x41, -0x58, -0x48, -0x74, -0x4d, -0x41, -0x58, -0x4c, -0x1, -0x4d, -0x41, -0x58, -0x4c, -0xa0, -0x44, -0x7, -0x93, -0x4d, -0x41, -0x58, -0x48, -0x0, -0x8, -0x4d, -0x52, -0x33, -0x32, -0x11, -0x1f, -0xa, -0x1c, -0x87, -0x17, -0x0, -0x0, -0xc, -0x3, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0xfe, -0xff, -0xff, -0xff, -0x0, -0x0, -0x0, -0x0, -0xff, -0xff, -0xff, -0xff, -0x79, -0x0, -0x8a, -0x4d, -0x52, -0x33, -0x32, -0xa, -0xa, -0x4d, -0x49, -0x4e, -0x5f, -0x8a, -0x4d, -0x52, -0x33, -0x32, -0xa, -0xe, -0x4d, -0x41, -0x58, -0x5f, -0x8a, -0x4d, -0x52, -0x33, -0x32, -0xa, -0x16, -0x4c, -0x45, -0x4e, -0x5f, -0x70, -0x4d, -0x49, -0x4e, -0x4c, -0x4d, -0x49, -0x4e, -0x5f, -0x70, -0x4d, -0x41, -0x58, -0x4c, -0x4d, -0x41, -0x58, -0x5f, -0x70, -0x4c, -0x45, -0x4e, -0x4c, -0x4c, -0x45, -0x4e, -0x5f, -0x5b, -0x27, -0x4d, -0x4c, -0x43, -0x4b, -0xa4, -0x4d, -0x52, -0x33, -0x32, -0x5b, -0x27, -0x4d, -0x4c, -0x43, -0x4b, -0xa4, -0x4d, -0x52, -0x36, -0x34, -0x14, -0x24, -0x4d, -0x50, -0x58, -0x4d, -0x1, -0x5b, -0x23, -0x4d, -0x4c, -0x43, -0x4b, -0xff, -0xff, -0x70, -0x99, -0x68, -0x0, -0x4d, -0x53, -0x45, -0x4c, -0x70, -0x4d, -0x50, -0x58, -0x5f, -0x60, -0x5b, -0x27, -0x4d, -0x4c, -0x43, -0x4b, -0xa4, -0x60, -0x14, -0x28, -0x4d, -0x4f, -0x53, -0x54, -0x4, -0x5b, -0x23, -0x4d, -0x4c, -0x43, -0x4b, -0xff, -0xff, -0x70, -0x99, -0x68, -0x0, -0x4d, -0x53, -0x45, -0x4c, -0x70, -0x69, -0x4d, -0x4f, -0x45, -0x56, -0x70, -0x6a, -0x4d, -0x4f, -0x53, -0x43, -0x5b, -0x27, -0x4d, -0x4c, -0x43, -0x4b, -0x10, -0x45, -0xd, -0x5f, -0x47, -0x50, -0x45, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xd, -0x41, -0x43, -0x50, -0x49, -0x30, -0x30, -0x30, -0x36, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x30, -0x0, -0x14, -0x39, -0x5f, -0x45, -0x30, -0x31, -0x0, -0x5b, -0x23, -0x5c, -0x2f, -0x3, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x42, -0x4c, -0x43, -0x4b, -0xff, -0xff, -0x5c, -0x2f, -0x3, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x50, -0x43, -0x4e, -0x54, -0x5b, -0x27, -0x5c, -0x2f, -0x3, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x42, -0x4c, -0x43, -0x4b, -0x14, -0x10, -0x5f, -0x45, -0x30, -0x32, -0x0, -0x5c, -0x2e, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x52, -0x53, -0x43, -0x14, -0x19, -0x5f, -0x45, -0x30, -0x33, -0x0, -0x5c, -0x2f, -0x4, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x4d, -0x48, -0x50, -0x44, -0x4d, -0x53, -0x43, -0x4e, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x34, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x35, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x36, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x37, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x38, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x39, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x41, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x42, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x43, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x44, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x45, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x46, -0x0 -}; diff --git a/qemu/hw/i386/intel_iommu.c b/qemu/hw/i386/intel_iommu.c index 08055a8d8..347718f93 100644 --- a/qemu/hw/i386/intel_iommu.c +++ b/qemu/hw/i386/intel_iommu.c @@ -19,9 +19,11 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "exec/address-spaces.h" #include "intel_iommu_internal.h" +#include "hw/pci/pci.h" /*#define DEBUG_INTEL_IOMMU*/ #ifdef DEBUG_INTEL_IOMMU @@ -151,14 +153,27 @@ static gboolean vtd_hash_remove_by_domain(gpointer key, gpointer value, return entry->domain_id == domain_id; } +/* The shift of an addr for a certain level of paging structure */ +static inline uint32_t vtd_slpt_level_shift(uint32_t level) +{ + return VTD_PAGE_SHIFT_4K + (level - 1) * VTD_SL_LEVEL_BITS; +} + +static inline uint64_t vtd_slpt_level_page_mask(uint32_t level) +{ + return ~((1ULL << vtd_slpt_level_shift(level)) - 1); +} + static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value, gpointer user_data) { VTDIOTLBEntry *entry = (VTDIOTLBEntry *)value; VTDIOTLBPageInvInfo *info = (VTDIOTLBPageInvInfo *)user_data; - uint64_t gfn = info->gfn & info->mask; + uint64_t gfn = (info->addr >> VTD_PAGE_SHIFT_4K) & info->mask; + uint64_t gfn_tlb = (info->addr & entry->mask) >> VTD_PAGE_SHIFT_4K; return (entry->domain_id == info->domain_id) && - ((entry->gfn & info->mask) == gfn); + (((entry->gfn & info->mask) == gfn) || + (entry->gfn == gfn_tlb)); } /* Reset all the gen of VTDAddressSpace to zero and set the gen of @@ -166,19 +181,17 @@ static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value, */ static void vtd_reset_context_cache(IntelIOMMUState *s) { - VTDAddressSpace **pvtd_as; VTDAddressSpace *vtd_as; - uint32_t bus_it; + VTDBus *vtd_bus; + GHashTableIter bus_it; uint32_t devfn_it; + g_hash_table_iter_init(&bus_it, s->vtd_as_by_busptr); + VTD_DPRINTF(CACHE, "global context_cache_gen=1"); - for (bus_it = 0; bus_it < VTD_PCI_BUS_MAX; ++bus_it) { - pvtd_as = s->address_spaces[bus_it]; - if (!pvtd_as) { - continue; - } + while (g_hash_table_iter_next (&bus_it, NULL, (void**)&vtd_bus)) { for (devfn_it = 0; devfn_it < VTD_PCI_DEVFN_MAX; ++devfn_it) { - vtd_as = pvtd_as[devfn_it]; + vtd_as = vtd_bus->dev_as[devfn_it]; if (!vtd_as) { continue; } @@ -194,24 +207,46 @@ static void vtd_reset_iotlb(IntelIOMMUState *s) g_hash_table_remove_all(s->iotlb); } +static uint64_t vtd_get_iotlb_key(uint64_t gfn, uint8_t source_id, + uint32_t level) +{ + return gfn | ((uint64_t)(source_id) << VTD_IOTLB_SID_SHIFT) | + ((uint64_t)(level) << VTD_IOTLB_LVL_SHIFT); +} + +static uint64_t vtd_get_iotlb_gfn(hwaddr addr, uint32_t level) +{ + return (addr & vtd_slpt_level_page_mask(level)) >> VTD_PAGE_SHIFT_4K; +} + static VTDIOTLBEntry *vtd_lookup_iotlb(IntelIOMMUState *s, uint16_t source_id, hwaddr addr) { + VTDIOTLBEntry *entry; uint64_t key; + int level; + + for (level = VTD_SL_PT_LEVEL; level < VTD_SL_PML4_LEVEL; level++) { + key = vtd_get_iotlb_key(vtd_get_iotlb_gfn(addr, level), + source_id, level); + entry = g_hash_table_lookup(s->iotlb, &key); + if (entry) { + goto out; + } + } - key = (addr >> VTD_PAGE_SHIFT_4K) | - ((uint64_t)(source_id) << VTD_IOTLB_SID_SHIFT); - return g_hash_table_lookup(s->iotlb, &key); - +out: + return entry; } static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, uint16_t domain_id, hwaddr addr, uint64_t slpte, - bool read_flags, bool write_flags) + bool read_flags, bool write_flags, + uint32_t level) { VTDIOTLBEntry *entry = g_malloc(sizeof(*entry)); uint64_t *key = g_malloc(sizeof(*key)); - uint64_t gfn = addr >> VTD_PAGE_SHIFT_4K; + uint64_t gfn = vtd_get_iotlb_gfn(addr, level); VTD_DPRINTF(CACHE, "update iotlb sid 0x%"PRIx16 " gpa 0x%"PRIx64 " slpte 0x%"PRIx64 " did 0x%"PRIx16, source_id, addr, slpte, @@ -226,7 +261,8 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, entry->slpte = slpte; entry->read_flags = read_flags; entry->write_flags = write_flags; - *key = gfn | ((uint64_t)(source_id) << VTD_IOTLB_SID_SHIFT); + entry->mask = vtd_slpt_level_page_mask(level); + *key = vtd_get_iotlb_key(gfn, source_id, level); g_hash_table_replace(s->iotlb, key, entry); } @@ -501,12 +537,6 @@ static inline dma_addr_t vtd_get_slpt_base_from_context(VTDContextEntry *ce) return ce->lo & VTD_CONTEXT_ENTRY_SLPTPTR; } -/* The shift of an addr for a certain level of paging structure */ -static inline uint32_t vtd_slpt_level_shift(uint32_t level) -{ - return VTD_PAGE_SHIFT_4K + (level - 1) * VTD_SL_LEVEL_BITS; -} - static inline uint64_t vtd_get_slpte_addr(uint64_t slpte) { return slpte & VTD_SL_PT_BASE_ADDR_MASK; @@ -754,14 +784,15 @@ static inline bool vtd_is_interrupt_addr(hwaddr addr) * @is_write: The access is a write operation * @entry: IOMMUTLBEntry that contain the addr to be translated and result */ -static void vtd_do_iommu_translate(VTDAddressSpace *vtd_as, uint8_t bus_num, +static void vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, uint8_t devfn, hwaddr addr, bool is_write, IOMMUTLBEntry *entry) { IntelIOMMUState *s = vtd_as->iommu_state; VTDContextEntry ce; + uint8_t bus_num = pci_bus_num(bus); VTDContextCacheEntry *cc_entry = &vtd_as->context_cache_entry; - uint64_t slpte; + uint64_t slpte, page_mask; uint32_t level; uint16_t source_id = vtd_make_source_id(bus_num, devfn); int ret_fr; @@ -801,6 +832,7 @@ static void vtd_do_iommu_translate(VTDAddressSpace *vtd_as, uint8_t bus_num, slpte = iotlb_entry->slpte; reads = iotlb_entry->read_flags; writes = iotlb_entry->write_flags; + page_mask = iotlb_entry->mask; goto out; } /* Try to fetch context-entry from cache first */ @@ -847,12 +879,13 @@ static void vtd_do_iommu_translate(VTDAddressSpace *vtd_as, uint8_t bus_num, return; } + page_mask = vtd_slpt_level_page_mask(level); vtd_update_iotlb(s, source_id, VTD_CONTEXT_ENTRY_DID(ce.hi), addr, slpte, - reads, writes); + reads, writes, level); out: - entry->iova = addr & VTD_PAGE_MASK_4K; - entry->translated_addr = vtd_get_slpte_addr(slpte) & VTD_PAGE_MASK_4K; - entry->addr_mask = ~VTD_PAGE_MASK_4K; + entry->iova = addr & page_mask; + entry->translated_addr = vtd_get_slpte_addr(slpte) & page_mask; + entry->addr_mask = ~page_mask; entry->perm = (writes ? 2 : 0) + (reads ? 1 : 0); } @@ -874,6 +907,29 @@ static void vtd_context_global_invalidate(IntelIOMMUState *s) } } + +/* Find the VTD address space currently associated with a given bus number, + */ +static VTDBus *vtd_find_as_from_bus_num(IntelIOMMUState *s, uint8_t bus_num) +{ + VTDBus *vtd_bus = s->vtd_as_by_bus_num[bus_num]; + if (!vtd_bus) { + /* Iterate over the registered buses to find the one + * which currently hold this bus number, and update the bus_num lookup table: + */ + GHashTableIter iter; + + g_hash_table_iter_init(&iter, s->vtd_as_by_busptr); + while (g_hash_table_iter_next (&iter, NULL, (void**)&vtd_bus)) { + if (pci_bus_num(vtd_bus->bus) == bus_num) { + s->vtd_as_by_bus_num[bus_num] = vtd_bus; + return vtd_bus; + } + } + } + return vtd_bus; +} + /* Do a context-cache device-selective invalidation. * @func_mask: FM field after shifting */ @@ -882,7 +938,7 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s, uint16_t func_mask) { uint16_t mask; - VTDAddressSpace **pvtd_as; + VTDBus *vtd_bus; VTDAddressSpace *vtd_as; uint16_t devfn; uint16_t devfn_it; @@ -903,11 +959,11 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s, } VTD_DPRINTF(INV, "device-selective invalidation source 0x%"PRIx16 " mask %"PRIu16, source_id, mask); - pvtd_as = s->address_spaces[VTD_SID_TO_BUS(source_id)]; - if (pvtd_as) { + vtd_bus = vtd_find_as_from_bus_num(s, VTD_SID_TO_BUS(source_id)); + if (vtd_bus) { devfn = VTD_SID_TO_DEVFN(source_id); for (devfn_it = 0; devfn_it < VTD_PCI_DEVFN_MAX; ++devfn_it) { - vtd_as = pvtd_as[devfn_it]; + vtd_as = vtd_bus->dev_as[devfn_it]; if (vtd_as && ((devfn_it & mask) == (devfn & mask))) { VTD_DPRINTF(INV, "invalidate context-cahce of devfn 0x%"PRIx16, devfn_it); @@ -967,7 +1023,7 @@ static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id, assert(am <= VTD_MAMV); info.domain_id = domain_id; - info.gfn = addr >> VTD_PAGE_SHIFT_4K; + info.addr = addr; info.mask = ~((1 << am) - 1); g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page, &info); } @@ -1805,11 +1861,11 @@ static IOMMUTLBEntry vtd_iommu_translate(MemoryRegion *iommu, hwaddr addr, return ret; } - vtd_do_iommu_translate(vtd_as, vtd_as->bus_num, vtd_as->devfn, addr, + vtd_do_iommu_translate(vtd_as, vtd_as->bus, vtd_as->devfn, addr, is_write, &ret); VTD_DPRINTF(MMU, "bus %"PRIu8 " slot %"PRIu8 " func %"PRIu8 " devfn %"PRIu8 - " gpa 0x%"PRIx64 " hpa 0x%"PRIx64, vtd_as->bus_num, + " gpa 0x%"PRIx64 " hpa 0x%"PRIx64, pci_bus_num(vtd_as->bus), VTD_PCI_SLOT(vtd_as->devfn), VTD_PCI_FUNC(vtd_as->devfn), vtd_as->devfn, addr, ret.translated_addr); return ret; @@ -1839,6 +1895,38 @@ static Property vtd_properties[] = { DEFINE_PROP_END_OF_LIST(), }; + +VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) +{ + uintptr_t key = (uintptr_t)bus; + VTDBus *vtd_bus = g_hash_table_lookup(s->vtd_as_by_busptr, &key); + VTDAddressSpace *vtd_dev_as; + + if (!vtd_bus) { + /* No corresponding free() */ + vtd_bus = g_malloc0(sizeof(VTDBus) + sizeof(VTDAddressSpace *) * VTD_PCI_DEVFN_MAX); + vtd_bus->bus = bus; + key = (uintptr_t)bus; + g_hash_table_insert(s->vtd_as_by_busptr, &key, vtd_bus); + } + + vtd_dev_as = vtd_bus->dev_as[devfn]; + + if (!vtd_dev_as) { + vtd_bus->dev_as[devfn] = vtd_dev_as = g_malloc0(sizeof(VTDAddressSpace)); + + vtd_dev_as->bus = bus; + vtd_dev_as->devfn = (uint8_t)devfn; + vtd_dev_as->iommu_state = s; + vtd_dev_as->context_cache_entry.context_cache_gen = 0; + memory_region_init_iommu(&vtd_dev_as->iommu, OBJECT(s), + &s->iommu_ops, "intel_iommu", UINT64_MAX); + address_space_init(&vtd_dev_as->as, + &vtd_dev_as->iommu, "intel_iommu"); + } + return vtd_dev_as; +} + /* Do the initialization. It will also be called when reset, so pay * attention when adding new initialization stuff. */ @@ -1861,7 +1949,7 @@ static void vtd_init(IntelIOMMUState *s) s->iq_last_desc_type = VTD_INV_DESC_NONE; s->next_frcd_reg = 0; s->cap = VTD_CAP_FRO | VTD_CAP_NFR | VTD_CAP_ND | VTD_CAP_MGAW | - VTD_CAP_SAGAW | VTD_CAP_MAMV | VTD_CAP_PSI; + VTD_CAP_SAGAW | VTD_CAP_MAMV | VTD_CAP_PSI | VTD_CAP_SLLPS; s->ecap = VTD_ECAP_QI | VTD_ECAP_IRO; vtd_reset_context_cache(s); @@ -1931,13 +2019,15 @@ static void vtd_realize(DeviceState *dev, Error **errp) IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev); VTD_DPRINTF(GENERAL, ""); - memset(s->address_spaces, 0, sizeof(s->address_spaces)); + memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num)); memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s, "intel_iommu", DMAR_REG_SIZE); sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->csrmem); /* No corresponding destroy */ s->iotlb = g_hash_table_new_full(vtd_uint64_hash, vtd_uint64_equal, g_free, g_free); + s->vtd_as_by_busptr = g_hash_table_new_full(vtd_uint64_hash, vtd_uint64_equal, + g_free, g_free); vtd_init(s); } diff --git a/qemu/hw/i386/intel_iommu_internal.h b/qemu/hw/i386/intel_iommu_internal.h index ba288ab1d..e5f514c6e 100644 --- a/qemu/hw/i386/intel_iommu_internal.h +++ b/qemu/hw/i386/intel_iommu_internal.h @@ -113,6 +113,7 @@ /* The shift of source_id in the key of IOTLB hash table */ #define VTD_IOTLB_SID_SHIFT 36 +#define VTD_IOTLB_LVL_SHIFT 44 #define VTD_IOTLB_MAX_SIZE 1024 /* Max size of the hash table */ /* IOTLB_REG */ @@ -185,9 +186,10 @@ #define VTD_CAP_ND (((VTD_DOMAIN_ID_SHIFT - 4) / 2) & 7ULL) #define VTD_MGAW 39 /* Maximum Guest Address Width */ #define VTD_CAP_MGAW (((VTD_MGAW - 1) & 0x3fULL) << 16) -#define VTD_MAMV 9ULL +#define VTD_MAMV 18ULL #define VTD_CAP_MAMV (VTD_MAMV << 48) #define VTD_CAP_PSI (1ULL << 39) +#define VTD_CAP_SLLPS ((1ULL << 34) | (1ULL << 35)) /* Supported Adjusted Guest Address Widths */ #define VTD_CAP_SAGAW_SHIFT 8 @@ -320,7 +322,7 @@ typedef struct VTDInvDesc VTDInvDesc; /* Information about page-selective IOTLB invalidate */ struct VTDIOTLBPageInvInfo { uint16_t domain_id; - uint64_t gfn; + uint64_t addr; uint8_t mask; }; typedef struct VTDIOTLBPageInvInfo VTDIOTLBPageInvInfo; diff --git a/qemu/hw/i386/kvm/apic.c b/qemu/hw/i386/kvm/apic.c index 5b470562a..3c7c8fa00 100644 --- a/qemu/hw/i386/kvm/apic.c +++ b/qemu/hw/i386/kvm/apic.c @@ -9,6 +9,7 @@ * 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 "hw/i386/apic_internal.h" #include "hw/pci/msi.h" #include "sysemu/kvm.h" @@ -185,7 +186,7 @@ static void kvm_apic_realize(DeviceState *dev, Error **errp) APIC_SPACE_SIZE); if (kvm_has_gsi_routing()) { - msi_supported = true; + msi_nonbroken = true; } } diff --git a/qemu/hw/i386/kvm/clock.c b/qemu/hw/i386/kvm/clock.c index 0593a3f1f..a3b300cad 100644 --- a/qemu/hw/i386/kvm/clock.c +++ b/qemu/hw/i386/kvm/clock.c @@ -13,6 +13,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/host-utils.h" #include "sysemu/sysemu.h" diff --git a/qemu/hw/i386/kvm/i8254.c b/qemu/hw/i386/kvm/i8254.c index 90eea10df..a4462e5ca 100644 --- a/qemu/hw/i386/kvm/i8254.c +++ b/qemu/hw/i386/kvm/i8254.c @@ -22,6 +22,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "hw/timer/i8254.h" diff --git a/qemu/hw/i386/kvm/i8259.c b/qemu/hw/i386/kvm/i8259.c index 53e3ca8c6..2b207de01 100644 --- a/qemu/hw/i386/kvm/i8259.c +++ b/qemu/hw/i386/kvm/i8259.c @@ -9,6 +9,7 @@ * 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 "hw/isa/i8259_internal.h" #include "hw/i386/apic_internal.h" #include "sysemu/kvm.h" diff --git a/qemu/hw/i386/kvm/ioapic.c b/qemu/hw/i386/kvm/ioapic.c index d2a6c4cf6..8eb2c7a70 100644 --- a/qemu/hw/i386/kvm/ioapic.c +++ b/qemu/hw/i386/kvm/ioapic.c @@ -10,6 +10,8 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" +#include "monitor/monitor.h" #include "hw/i386/pc.h" #include "hw/i386/ioapic_internal.h" #include "hw/i386/apic_internal.h" @@ -110,6 +112,15 @@ static void kvm_ioapic_put(IOAPICCommonState *s) } } +void kvm_ioapic_dump_state(Monitor *mon, const QDict *qdict) +{ + IOAPICCommonState s; + + kvm_ioapic_get(&s); + + ioapic_print_redtbl(mon, &s); +} + static void kvm_ioapic_reset(DeviceState *dev) { IOAPICCommonState *s = IOAPIC_COMMON(dev); diff --git a/qemu/hw/i386/kvm/pci-assign.c b/qemu/hw/i386/kvm/pci-assign.c index 74d22f4fd..bf425a2b9 100644 --- a/qemu/hw/i386/kvm/pci-assign.c +++ b/qemu/hw/i386/kvm/pci-assign.c @@ -20,12 +20,9 @@ * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com) * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com) */ -#include -#include -#include +#include "qemu/osdep.h" +#include "qapi/error.h" #include -#include -#include #include "hw/hw.h" #include "hw/i386/pc.h" #include "qemu/error-report.h" @@ -37,6 +34,7 @@ #include "hw/pci/pci.h" #include "hw/pci/msi.h" #include "kvm_i386.h" +#include "hw/pci/pci-assign.h" #define MSIX_PAGE_SIZE 0x1000 @@ -48,17 +46,6 @@ #define IORESOURCE_PREFETCH 0x00002000 /* No side effects */ #define IORESOURCE_MEM_64 0x00100000 -//#define DEVICE_ASSIGNMENT_DEBUG - -#ifdef DEVICE_ASSIGNMENT_DEBUG -#define DEBUG(fmt, ...) \ - do { \ - fprintf(stderr, "%s: " fmt, __func__ , __VA_ARGS__); \ - } while (0) -#else -#define DEBUG(fmt, ...) -#endif - typedef struct PCIRegion { int type; /* Memory or port I/O */ int valid; @@ -781,7 +768,7 @@ static char *assign_failed_examine(const AssignedDevice *dev) "*** $ echo \"%04x:%02x:%02x.%x\" > /sys/bus/pci/drivers/" "pci-stub/bind\n" "*** $ echo \"%04x %04x\" > /sys/bus/pci/drivers/pci-stub/remove_id\n" - "***", + "***\n", ns, dev->host.domain, dev->host.bus, dev->host.slot, dev->host.function, vendor_id, device_id, dev->host.domain, dev->host.bus, dev->host.slot, dev->host.function, @@ -789,7 +776,7 @@ static char *assign_failed_examine(const AssignedDevice *dev) dev->host.function, vendor_id, device_id); fail: - return g_strdup("Couldn't find out why."); + return g_strdup("Couldn't find out why.\n"); } static void assign_device(AssignedDevice *dev, Error **errp) @@ -823,8 +810,9 @@ static void assign_device(AssignedDevice *dev, Error **errp) char *cause; cause = assign_failed_examine(dev); - error_setg_errno(errp, -r, "Failed to assign device \"%s\"\n%s", - dev->dev.qdev.id, cause); + error_setg_errno(errp, -r, "Failed to assign device \"%s\"", + dev->dev.qdev.id); + error_append_hint(errp, "%s", cause); g_free(cause); break; } @@ -923,11 +911,10 @@ retry: dev->features |= ASSIGNED_DEVICE_PREFER_MSI_MASK; goto retry; } - error_setg_errno(errp, -r, - "Failed to assign irq for \"%s\"\n" - "Perhaps you are assigning a device " - "that shares an IRQ with another device?", + error_setg_errno(errp, -r, "Failed to assign irq for \"%s\"", dev->dev.qdev.id); + error_append_hint(errp, "Perhaps you are assigning a device " + "that shares an IRQ with another device?\n"); return r; } @@ -990,7 +977,7 @@ static void assigned_dev_update_msi(PCIDevice *pci_dev) MSIMessage msg = msi_get_message(pci_dev, 0); int virq; - virq = kvm_irqchip_add_msi_route(kvm_state, msg); + virq = kvm_irqchip_add_msi_route(kvm_state, msg, pci_dev); if (virq < 0) { perror("assigned_dev_update_msi: kvm_irqchip_add_msi_route"); return; @@ -1028,7 +1015,7 @@ static void assigned_dev_update_msi_msg(PCIDevice *pci_dev) } kvm_irqchip_update_msi_route(kvm_state, assigned_dev->msi_virq[0], - msi_get_message(pci_dev, 0)); + msi_get_message(pci_dev, 0), pci_dev); } static bool assigned_dev_msix_masked(MSIXTableEntry *entry) @@ -1094,7 +1081,7 @@ static int assigned_dev_update_msix_mmio(PCIDevice *pci_dev) msg.address = entry->addr_lo | ((uint64_t)entry->addr_hi << 32); msg.data = entry->data; - r = kvm_irqchip_add_msi_route(kvm_state, msg); + r = kvm_irqchip_add_msi_route(kvm_state, msg, pci_dev); if (r < 0) { return r; } @@ -1494,7 +1481,7 @@ static int assigned_device_pci_cap_init(PCIDevice *pci_dev, Error **errp) * error bits, leave the rest. */ status = pci_get_long(pci_dev->config + pos + PCI_X_STATUS); status &= ~(PCI_X_STATUS_BUS | PCI_X_STATUS_DEVFN); - status |= (pci_bus_num(pci_dev->bus) << 8) | pci_dev->devfn; + status |= pci_requester_id(pci_dev); status &= ~(PCI_X_STATUS_SPL_DISC | PCI_X_STATUS_UNX_SPL | PCI_X_STATUS_SPL_ERR); pci_set_long(pci_dev->config + pos + PCI_X_STATUS, status); @@ -1613,7 +1600,8 @@ static void assigned_dev_msix_mmio_write(void *opaque, hwaddr addr, msg.data = entry->data; ret = kvm_irqchip_update_msi_route(kvm_state, - adev->msi_virq[i], msg); + adev->msi_virq[i], msg, + pdev); if (ret) { error_report("Error updating irq routing entry (%d)", ret); } @@ -1896,73 +1884,15 @@ static void assign_register_types(void) type_init(assign_register_types) -/* - * Scan the assigned devices for the devices that have an option ROM, and then - * load the corresponding ROM data to RAM. If an error occurs while loading an - * option ROM, we just ignore that option ROM and continue with the next one. - */ static void assigned_dev_load_option_rom(AssignedDevice *dev) { - char name[32], rom_file[64]; - FILE *fp; - uint8_t val; - struct stat st; - void *ptr; - - /* If loading ROM from file, pci handles it */ - if (dev->dev.romfile || !dev->dev.rom_bar) { - return; - } + int size = 0; - snprintf(rom_file, sizeof(rom_file), - "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/rom", - dev->host.domain, dev->host.bus, dev->host.slot, - dev->host.function); + pci_assign_dev_load_option_rom(&dev->dev, OBJECT(dev), &size, + dev->host.domain, dev->host.bus, + dev->host.slot, dev->host.function); - if (stat(rom_file, &st)) { - return; - } - - if (access(rom_file, F_OK)) { - error_report("pci-assign: Insufficient privileges for %s", rom_file); - return; - } - - /* Write "1" to the ROM file to enable it */ - fp = fopen(rom_file, "r+"); - if (fp == NULL) { - return; + if (!size) { + error_report("pci-assign: Invalid ROM."); } - val = 1; - if (fwrite(&val, 1, 1, fp) != 1) { - goto close_rom; - } - fseek(fp, 0, SEEK_SET); - - snprintf(name, sizeof(name), "%s.rom", - object_get_typename(OBJECT(dev))); - memory_region_init_ram(&dev->dev.rom, OBJECT(dev), name, st.st_size, - &error_abort); - vmstate_register_ram(&dev->dev.rom, &dev->dev.qdev); - ptr = memory_region_get_ram_ptr(&dev->dev.rom); - memset(ptr, 0xff, st.st_size); - - if (!fread(ptr, 1, st.st_size, fp)) { - error_report("pci-assign: Cannot read from host %s", rom_file); - error_printf("Device option ROM contents are probably invalid " - "(check dmesg).\nSkip option ROM probe with rombar=0, " - "or load from file with romfile=\n"); - goto close_rom; - } - - pci_register_bar(&dev->dev, PCI_ROM_SLOT, 0, &dev->dev.rom); - dev->dev.has_rom = true; -close_rom: - /* Write "0" to disable ROM */ - fseek(fp, 0, SEEK_SET); - val = 0; - if (!fwrite(&val, 1, 1, fp)) { - DEBUG("%s\n", "Failed to disable pci-sysfs rom file"); - } - fclose(fp); } diff --git a/qemu/hw/i386/kvmvapic.c b/qemu/hw/i386/kvmvapic.c index c6d34b254..c69f37404 100644 --- a/qemu/hw/i386/kvmvapic.c +++ b/qemu/hw/i386/kvmvapic.c @@ -8,6 +8,7 @@ * (at your option) any later version. See the COPYING file in the * top-level directory. */ +#include "qemu/osdep.h" #include "sysemu/sysemu.h" #include "sysemu/cpus.h" #include "sysemu/kvm.h" @@ -634,13 +635,18 @@ static int vapic_prepare(VAPICROMState *s) static void vapic_write(void *opaque, hwaddr addr, uint64_t data, unsigned int size) { - CPUState *cs = current_cpu; - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - hwaddr rom_paddr; VAPICROMState *s = opaque; + X86CPU *cpu; + CPUX86State *env; + hwaddr rom_paddr; - cpu_synchronize_state(cs); + if (!current_cpu) { + return; + } + + cpu_synchronize_state(current_cpu); + cpu = X86_CPU(current_cpu); + env = &cpu->env; /* * The VAPIC supports two PIO-based hypercalls, both via port 0x7E. diff --git a/qemu/hw/i386/multiboot.c b/qemu/hw/i386/multiboot.c index 1adbe9e25..387caa67d 100644 --- a/qemu/hw/i386/multiboot.c +++ b/qemu/hw/i386/multiboot.c @@ -22,6 +22,9 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/nvram/fw_cfg.h" #include "multiboot.h" @@ -195,7 +198,8 @@ int load_multiboot(FWCfgState *fw_cfg, } kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, - &elf_low, &elf_high, 0, ELF_MACHINE, 0); + &elf_low, &elf_high, 0, I386_ELF_MACHINE, + 0, 0); if (kernel_size < 0) { fprintf(stderr, "Error while loading elf kernel\n"); exit(1); diff --git a/qemu/hw/i386/pc.c b/qemu/hw/i386/pc.c index 7661ea9cd..99437e0b7 100644 --- a/qemu/hw/i386/pc.c +++ b/qemu/hw/i386/pc.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/char/serial.h" @@ -33,7 +34,7 @@ #include "hw/pci/pci_bus.h" #include "hw/nvram/fw_cfg.h" #include "hw/timer/hpet.h" -#include "hw/i386/smbios.h" +#include "hw/smbios/smbios.h" #include "hw/loader.h" #include "elf.h" #include "multiboot.h" @@ -59,13 +60,13 @@ #include "qemu/error-report.h" #include "hw/acpi/acpi.h" #include "hw/acpi/cpu_hotplug.h" -#include "hw/cpu/icc_bus.h" #include "hw/boards.h" #include "hw/pci/pci_host.h" #include "acpi-build.h" #include "hw/mem/pc-dimm.h" #include "qapi/visitor.h" #include "qapi-visit.h" +#include "qom/cpu.h" /* debug PC/ISA interrupts */ //#define DEBUG_IRQ @@ -77,16 +78,6 @@ #define DPRINTF(fmt, ...) #endif -/* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables - * (128K) and other BIOS datastructures (less than 4K reported to be used at - * the moment, 32K should be enough for a while). */ -static unsigned acpi_data_size = 0x20000 + 0x8000; -void pc_set_legacy_acpi_data_size(void) -{ - acpi_data_size = 0x10000; -} - -#define BIOS_CFG_IOPORT 0x510 #define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0) #define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1) #define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2) @@ -208,24 +199,24 @@ static void pic_irq_request(void *opaque, int irq, int level) #define REG_EQUIPMENT_BYTE 0x14 -static int cmos_get_fd_drive_type(FDriveType fd0) +int cmos_get_fd_drive_type(FloppyDriveType fd0) { int val; switch (fd0) { - case FDRIVE_DRV_144: + case FLOPPY_DRIVE_TYPE_144: /* 1.44 Mb 3"5 drive */ val = 4; break; - case FDRIVE_DRV_288: + case FLOPPY_DRIVE_TYPE_288: /* 2.88 Mb 3"5 drive */ val = 5; break; - case FDRIVE_DRV_120: + case FLOPPY_DRIVE_TYPE_120: /* 1.2 Mb 5"5 drive */ val = 2; break; - case FDRIVE_DRV_NONE: + case FLOPPY_DRIVE_TYPE_NONE: default: val = 0; break; @@ -296,7 +287,8 @@ static void pc_boot_set(void *opaque, const char *boot_device, Error **errp) static void pc_cmos_init_floppy(ISADevice *rtc_state, ISADevice *floppy) { int val, nb, i; - FDriveType fd_type[2] = { FDRIVE_DRV_NONE, FDRIVE_DRV_NONE }; + FloppyDriveType fd_type[2] = { FLOPPY_DRIVE_TYPE_NONE, + FLOPPY_DRIVE_TYPE_NONE }; /* floppy type */ if (floppy) { @@ -310,10 +302,10 @@ static void pc_cmos_init_floppy(ISADevice *rtc_state, ISADevice *floppy) val = rtc_get_memory(rtc_state, REG_EQUIPMENT_BYTE); nb = 0; - if (fd_type[0] < FDRIVE_DRV_NONE) { + if (fd_type[0] != FLOPPY_DRIVE_TYPE_NONE) { nb++; } - if (fd_type[1] < FDRIVE_DRV_NONE) { + if (fd_type[1] != FLOPPY_DRIVE_TYPE_NONE) { nb++; } switch (nb) { @@ -369,6 +361,31 @@ static const char * const fdc_container_path[] = { "/unattached", "/peripheral", "/peripheral-anon" }; +/* + * Locate the FDC at IO address 0x3f0, in order to configure the CMOS registers + * and ACPI objects. + */ +ISADevice *pc_find_fdc0(void) +{ + int i; + Object *container; + CheckFdcState state = { 0 }; + + for (i = 0; i < ARRAY_SIZE(fdc_container_path); i++) { + container = container_get(qdev_get_machine(), fdc_container_path[i]); + object_child_foreach(container, check_fdc, &state); + } + + if (state.multiple) { + error_report("warning: multiple floppy disk controllers with " + "iobase=0x3f0 have been found"); + error_printf("the one being picked for CMOS setup might not reflect " + "your intent"); + } + + return state.floppy; +} + static void pc_cmos_init_late(void *opaque) { pc_cmos_init_late_arg *arg = opaque; @@ -377,8 +394,6 @@ static void pc_cmos_init_late(void *opaque) int8_t heads, sectors; int val; int i, trans; - Object *container; - CheckFdcState state = { 0 }; val = 0; if (ide_get_geometry(arg->idebus[0], 0, @@ -408,46 +423,28 @@ static void pc_cmos_init_late(void *opaque) } rtc_set_memory(s, 0x39, val); - /* - * Locate the FDC at IO address 0x3f0, and configure the CMOS registers - * accordingly. - */ - for (i = 0; i < ARRAY_SIZE(fdc_container_path); i++) { - container = container_get(qdev_get_machine(), fdc_container_path[i]); - object_child_foreach(container, check_fdc, &state); - } - - if (state.multiple) { - error_report("warning: multiple floppy disk controllers with " - "iobase=0x3f0 have been found;\n" - "the one being picked for CMOS setup might not reflect " - "your intent"); - } - pc_cmos_init_floppy(s, state.floppy); + pc_cmos_init_floppy(s, pc_find_fdc0()); qemu_unregister_reset(pc_cmos_init_late, opaque); } -void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, - const char *boot_device, MachineState *machine, +void pc_cmos_init(PCMachineState *pcms, BusState *idebus0, BusState *idebus1, ISADevice *s) { int val; static pc_cmos_init_late_arg arg; - PCMachineState *pc_machine = PC_MACHINE(machine); - Error *local_err = NULL; /* various important CMOS locations needed by PC/Bochs bios */ /* memory size */ /* base memory (first MiB) */ - val = MIN(ram_size / 1024, 640); + val = MIN(pcms->below_4g_mem_size / 1024, 640); rtc_set_memory(s, 0x15, val); rtc_set_memory(s, 0x16, val >> 8); /* extended memory (next 64MiB) */ - if (ram_size > 1024 * 1024) { - val = (ram_size - 1024 * 1024) / 1024; + if (pcms->below_4g_mem_size > 1024 * 1024) { + val = (pcms->below_4g_mem_size - 1024 * 1024) / 1024; } else { val = 0; } @@ -458,8 +455,8 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, rtc_set_memory(s, 0x30, val); rtc_set_memory(s, 0x31, val >> 8); /* memory between 16MiB and 4GiB */ - if (ram_size > 16 * 1024 * 1024) { - val = (ram_size - 16 * 1024 * 1024) / 65536; + if (pcms->below_4g_mem_size > 16 * 1024 * 1024) { + val = (pcms->below_4g_mem_size - 16 * 1024 * 1024) / 65536; } else { val = 0; } @@ -468,7 +465,7 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, rtc_set_memory(s, 0x34, val); rtc_set_memory(s, 0x35, val >> 8); /* memory above 4GiB */ - val = above_4g_mem_size / 65536; + val = pcms->above_4g_mem_size / 65536; rtc_set_memory(s, 0x5b, val); rtc_set_memory(s, 0x5c, val >> 8); rtc_set_memory(s, 0x5d, val >> 16); @@ -476,19 +473,15 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, /* set the number of CPU */ rtc_set_memory(s, 0x5f, smp_cpus - 1); - object_property_add_link(OBJECT(machine), "rtc_state", + object_property_add_link(OBJECT(pcms), "rtc_state", TYPE_ISA_DEVICE, - (Object **)&pc_machine->rtc, + (Object **)&pcms->rtc, object_property_allow_set_link, OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); - object_property_set_link(OBJECT(machine), OBJECT(s), + object_property_set_link(OBJECT(pcms), OBJECT(s), "rtc_state", &error_abort); - set_boot_dev(s, boot_device, &local_err); - if (local_err) { - error_report_err(local_err); - exit(1); - } + set_boot_dev(s, MACHINE(pcms)->boot_order, &error_fatal); val = 0; val |= 0x02; /* FPU is there */ @@ -706,28 +699,51 @@ static uint32_t x86_cpu_apic_id_from_index(unsigned int cpu_index) } } -/* Calculates the limit to CPU APIC ID values - * - * This function returns the limit for the APIC ID value, so that all - * CPU APIC IDs are < pc_apic_id_limit(). - * - * This is used for FW_CFG_MAX_CPUS. See comments on bochs_bios_init(). - */ -static unsigned int pc_apic_id_limit(unsigned int max_cpus) +static void pc_build_smbios(FWCfgState *fw_cfg) { - return x86_cpu_apic_id_from_index(max_cpus - 1) + 1; + uint8_t *smbios_tables, *smbios_anchor; + size_t smbios_tables_len, smbios_anchor_len; + struct smbios_phys_mem_area *mem_array; + unsigned i, array_count; + + smbios_tables = smbios_get_table_legacy(&smbios_tables_len); + if (smbios_tables) { + fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES, + smbios_tables, smbios_tables_len); + } + + /* build the array of physical mem area from e820 table */ + mem_array = g_malloc0(sizeof(*mem_array) * e820_get_num_entries()); + for (i = 0, array_count = 0; i < e820_get_num_entries(); i++) { + uint64_t addr, len; + + if (e820_get_entry(i, E820_RAM, &addr, &len)) { + mem_array[array_count].address = addr; + mem_array[array_count].length = len; + array_count++; + } + } + smbios_get_tables(mem_array, array_count, + &smbios_tables, &smbios_tables_len, + &smbios_anchor, &smbios_anchor_len); + g_free(mem_array); + + if (smbios_anchor) { + fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-tables", + smbios_tables, smbios_tables_len); + fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-anchor", + smbios_anchor, smbios_anchor_len); + } } -static FWCfgState *bochs_bios_init(void) +static FWCfgState *bochs_bios_init(AddressSpace *as, PCMachineState *pcms) { FWCfgState *fw_cfg; - uint8_t *smbios_tables, *smbios_anchor; - size_t smbios_tables_len, smbios_anchor_len; uint64_t *numa_fw_cfg; int i, j; - unsigned int apic_id_limit = pc_apic_id_limit(max_cpus); - fw_cfg = fw_cfg_init_io(BIOS_CFG_IOPORT); + fw_cfg = fw_cfg_init_io_dma(FW_CFG_IO_BASE, FW_CFG_IO_BASE + 4, as); + /* FW_CFG_MAX_CPUS is a bit confusing/problematic on x86: * * SeaBIOS needs FW_CFG_MAX_CPUS for CPU hotplug, but the CPU hotplug @@ -742,26 +758,13 @@ static FWCfgState *bochs_bios_init(void) * [1] The only kind of "CPU identifier" used between SeaBIOS and QEMU is * the APIC ID, not the "CPU index" */ - fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)apic_id_limit); + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)pcms->apic_id_limit); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES, acpi_tables, acpi_tables_len); fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, kvm_allows_irq0_override()); - smbios_tables = smbios_get_table_legacy(&smbios_tables_len); - if (smbios_tables) { - fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES, - smbios_tables, smbios_tables_len); - } - - smbios_get_tables(&smbios_tables, &smbios_tables_len, - &smbios_anchor, &smbios_anchor_len); - if (smbios_anchor) { - fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-tables", - smbios_tables, smbios_tables_len); - fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-anchor", - smbios_anchor, smbios_anchor_len); - } + pc_build_smbios(fw_cfg); fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE, &e820_reserve, sizeof(e820_reserve)); @@ -773,11 +776,11 @@ static FWCfgState *bochs_bios_init(void) * of nodes, one word for each VCPU->node and one word for each node to * hold the amount of memory. */ - numa_fw_cfg = g_new0(uint64_t, 1 + apic_id_limit + nb_numa_nodes); + numa_fw_cfg = g_new0(uint64_t, 1 + pcms->apic_id_limit + nb_numa_nodes); numa_fw_cfg[0] = cpu_to_le64(nb_numa_nodes); for (i = 0; i < max_cpus; i++) { unsigned int apic_id = x86_cpu_apic_id_from_index(i); - assert(apic_id < apic_id_limit); + assert(apic_id < pcms->apic_id_limit); for (j = 0; j < nb_numa_nodes; j++) { if (test_bit(i, numa_info[j].node_cpu)) { numa_fw_cfg[apic_id + 1] = cpu_to_le64(j); @@ -786,10 +789,11 @@ static FWCfgState *bochs_bios_init(void) } } for (i = 0; i < nb_numa_nodes; i++) { - numa_fw_cfg[apic_id_limit + 1 + i] = cpu_to_le64(numa_info[i].node_mem); + numa_fw_cfg[pcms->apic_id_limit + 1 + i] = + cpu_to_le64(numa_info[i].node_mem); } fw_cfg_add_bytes(fw_cfg, FW_CFG_NUMA, numa_fw_cfg, - (1 + apic_id_limit + nb_numa_nodes) * + (1 + pcms->apic_id_limit + nb_numa_nodes) * sizeof(*numa_fw_cfg)); return fw_cfg; @@ -809,11 +813,8 @@ static long get_file_size(FILE *f) return size; } -static void load_linux(FWCfgState *fw_cfg, - const char *kernel_filename, - const char *initrd_filename, - const char *kernel_cmdline, - hwaddr max_ram_size) +static void load_linux(PCMachineState *pcms, + FWCfgState *fw_cfg) { uint16_t protocol; int setup_size, kernel_size, initrd_size = 0, cmdline_size; @@ -822,6 +823,11 @@ static void load_linux(FWCfgState *fw_cfg, hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0; FILE *f; char *vmode; + MachineState *machine = MACHINE(pcms); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + const char *kernel_filename = machine->kernel_filename; + const char *initrd_filename = machine->initrd_filename; + const char *kernel_cmdline = machine->kernel_cmdline; /* Align to 16 bytes as a paranoia measure */ cmdline_size = (strlen(kernel_cmdline)+16) & ~15; @@ -886,8 +892,8 @@ static void load_linux(FWCfgState *fw_cfg, initrd_max = 0x37ffffff; } - if (initrd_max >= max_ram_size - acpi_data_size) { - initrd_max = max_ram_size - acpi_data_size - 1; + if (initrd_max >= pcms->below_4g_mem_size - pcmc->acpi_data_size) { + initrd_max = pcms->below_4g_mem_size - pcmc->acpi_data_size - 1; } fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr); @@ -965,6 +971,10 @@ static void load_linux(FWCfgState *fw_cfg, setup_size = 4; } setup_size = (setup_size+1)*512; + if (setup_size > kernel_size) { + fprintf(stderr, "qemu: invalid kernel header\n"); + exit(1); + } kernel_size -= setup_size; setup = g_malloc(setup_size); @@ -1031,23 +1041,16 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level) } static X86CPU *pc_new_cpu(const char *cpu_model, int64_t apic_id, - DeviceState *icc_bridge, Error **errp) + Error **errp) { X86CPU *cpu = NULL; Error *local_err = NULL; - if (icc_bridge == NULL) { - error_setg(&local_err, "Invalid icc-bridge value"); - goto out; - } - cpu = cpu_x86_create(cpu_model, &local_err); if (local_err != NULL) { goto out; } - qdev_set_parent_bus(DEVICE(cpu), qdev_get_child_bus(icc_bridge, "icc")); - object_property_set_int(OBJECT(cpu), apic_id, "apic-id", &local_err); object_property_set_bool(OBJECT(cpu), true, "realized", &local_err); @@ -1060,12 +1063,10 @@ out: return cpu; } -static const char *current_cpu_model; - void pc_hot_add_cpu(const int64_t id, Error **errp) { - DeviceState *icc_bridge; X86CPU *cpu; + MachineState *machine = MACHINE(qdev_get_machine()); int64_t apic_id = x86_cpu_apic_id_from_index(id); Error *local_err = NULL; @@ -1093,9 +1094,7 @@ void pc_hot_add_cpu(const int64_t id, Error **errp) return; } - icc_bridge = DEVICE(object_resolve_path_type("icc-bridge", - TYPE_ICC_BRIDGE, NULL)); - cpu = pc_new_cpu(current_cpu_model, apic_id, icc_bridge, &local_err); + cpu = pc_new_cpu(machine->cpu_model, apic_id, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -1103,45 +1102,46 @@ void pc_hot_add_cpu(const int64_t id, Error **errp) object_unref(OBJECT(cpu)); } -void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge) +void pc_cpus_init(PCMachineState *pcms) { int i; X86CPU *cpu = NULL; - Error *error = NULL; - unsigned long apic_id_limit; + MachineState *machine = MACHINE(pcms); /* init CPUs */ - if (cpu_model == NULL) { + if (machine->cpu_model == NULL) { #ifdef TARGET_X86_64 - cpu_model = "qemu64"; + machine->cpu_model = "qemu64"; #else - cpu_model = "qemu32"; + machine->cpu_model = "qemu32"; #endif } - current_cpu_model = cpu_model; - apic_id_limit = pc_apic_id_limit(max_cpus); - if (apic_id_limit > ACPI_CPU_HOTPLUG_ID_LIMIT) { - error_report("max_cpus is too large. APIC ID of last CPU is %lu", - apic_id_limit - 1); + /* Calculates the limit to CPU APIC ID values + * + * Limit for the APIC ID value, so that all + * CPU APIC IDs are < pcms->apic_id_limit. + * + * This is used for FW_CFG_MAX_CPUS. See comments on bochs_bios_init(). + */ + pcms->apic_id_limit = x86_cpu_apic_id_from_index(max_cpus - 1) + 1; + if (pcms->apic_id_limit > ACPI_CPU_HOTPLUG_ID_LIMIT) { + error_report("max_cpus is too large. APIC ID of last CPU is %u", + pcms->apic_id_limit - 1); exit(1); } - for (i = 0; i < smp_cpus; i++) { - cpu = pc_new_cpu(cpu_model, x86_cpu_apic_id_from_index(i), - icc_bridge, &error); - if (error) { - error_report_err(error); - exit(1); + pcms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) + + sizeof(CPUArchId) * max_cpus); + for (i = 0; i < max_cpus; i++) { + pcms->possible_cpus->cpus[i].arch_id = x86_cpu_apic_id_from_index(i); + pcms->possible_cpus->len++; + if (i < smp_cpus) { + cpu = pc_new_cpu(machine->cpu_model, x86_cpu_apic_id_from_index(i), + &error_fatal); + pcms->possible_cpus->cpus[i].cpu = CPU(cpu); + object_unref(OBJECT(cpu)); } - object_unref(OBJECT(cpu)); - } - - /* map APIC MMIO area if CPU has APIC */ - if (cpu && cpu->apic_state) { - /* XXX: what if the base changes? */ - sysbus_mmio_map_overlap(SYS_BUS_DEVICE(icc_bridge), 0, - APIC_DEFAULT_ADDRESS, 0x1000); } /* tell smbios about cpuid version and features */ @@ -1156,18 +1156,12 @@ typedef struct PcRomPciInfo { uint64_t w64_max; } PcRomPciInfo; -typedef struct PcGuestInfoState { - PcGuestInfo info; - Notifier machine_done; -} PcGuestInfoState; - static -void pc_guest_info_machine_done(Notifier *notifier, void *data) +void pc_machine_done(Notifier *notifier, void *data) { - PcGuestInfoState *guest_info_state = container_of(notifier, - PcGuestInfoState, - machine_done); - PCIBus *bus = find_i440fx(); + PCMachineState *pcms = container_of(notifier, + PCMachineState, machine_done); + PCIBus *bus = pcms->bus; if (bus) { int extra_hosts = 0; @@ -1178,52 +1172,45 @@ void pc_guest_info_machine_done(Notifier *notifier, void *data) extra_hosts++; } } - if (extra_hosts && guest_info_state->info.fw_cfg) { + if (extra_hosts && pcms->fw_cfg) { uint64_t *val = g_malloc(sizeof(*val)); *val = cpu_to_le64(extra_hosts); - fw_cfg_add_file(guest_info_state->info.fw_cfg, + fw_cfg_add_file(pcms->fw_cfg, "etc/extra-pci-roots", val, sizeof(*val)); } } - acpi_setup(&guest_info_state->info); + acpi_setup(); } -PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size, - ram_addr_t above_4g_mem_size) +void pc_guest_info_init(PCMachineState *pcms) { - PcGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state); - PcGuestInfo *guest_info = &guest_info_state->info; int i, j; - guest_info->ram_size_below_4g = below_4g_mem_size; - guest_info->ram_size = below_4g_mem_size + above_4g_mem_size; - guest_info->apic_id_limit = pc_apic_id_limit(max_cpus); - guest_info->apic_xrupt_override = kvm_allows_irq0_override(); - guest_info->numa_nodes = nb_numa_nodes; - guest_info->node_mem = g_malloc0(guest_info->numa_nodes * - sizeof *guest_info->node_mem); + pcms->apic_xrupt_override = kvm_allows_irq0_override(); + pcms->numa_nodes = nb_numa_nodes; + pcms->node_mem = g_malloc0(pcms->numa_nodes * + sizeof *pcms->node_mem); for (i = 0; i < nb_numa_nodes; i++) { - guest_info->node_mem[i] = numa_info[i].node_mem; + pcms->node_mem[i] = numa_info[i].node_mem; } - guest_info->node_cpu = g_malloc0(guest_info->apic_id_limit * - sizeof *guest_info->node_cpu); + pcms->node_cpu = g_malloc0(pcms->apic_id_limit * + sizeof *pcms->node_cpu); for (i = 0; i < max_cpus; i++) { unsigned int apic_id = x86_cpu_apic_id_from_index(i); - assert(apic_id < guest_info->apic_id_limit); + assert(apic_id < pcms->apic_id_limit); for (j = 0; j < nb_numa_nodes; j++) { if (test_bit(i, numa_info[j].node_cpu)) { - guest_info->node_cpu[apic_id] = j; + pcms->node_cpu[apic_id] = j; break; } } } - guest_info_state->machine_done.notify = pc_guest_info_machine_done; - qemu_add_machine_init_done_notifier(&guest_info_state->machine_done); - return guest_info; + pcms->machine_done.notify = pc_machine_done; + qemu_add_machine_init_done_notifier(&pcms->machine_done); } /* setup pci memory address space mapping into system address space */ @@ -1256,54 +1243,46 @@ void pc_acpi_init(const char *default_dsdt) acpi_table_add_builtin(opts, &err); if (err) { - error_report("WARNING: failed to load %s: %s", filename, - error_get_pretty(err)); - error_free(err); + error_reportf_err(err, "WARNING: failed to load %s: ", + filename); } g_free(filename); } } -FWCfgState *xen_load_linux(const char *kernel_filename, - const char *kernel_cmdline, - const char *initrd_filename, - ram_addr_t below_4g_mem_size, - PcGuestInfo *guest_info) +void xen_load_linux(PCMachineState *pcms) { int i; FWCfgState *fw_cfg; - assert(kernel_filename != NULL); + assert(MACHINE(pcms)->kernel_filename != NULL); - fw_cfg = fw_cfg_init_io(BIOS_CFG_IOPORT); + fw_cfg = fw_cfg_init_io(FW_CFG_IO_BASE); rom_set_fw(fw_cfg); - load_linux(fw_cfg, kernel_filename, initrd_filename, - kernel_cmdline, below_4g_mem_size); + load_linux(pcms, fw_cfg); for (i = 0; i < nb_option_roms; i++) { assert(!strcmp(option_rom[i].name, "linuxboot.bin") || !strcmp(option_rom[i].name, "multiboot.bin")); rom_add_option(option_rom[i].name, option_rom[i].bootindex); } - guest_info->fw_cfg = fw_cfg; - return fw_cfg; + pcms->fw_cfg = fw_cfg; } -FWCfgState *pc_memory_init(MachineState *machine, - MemoryRegion *system_memory, - ram_addr_t below_4g_mem_size, - ram_addr_t above_4g_mem_size, - MemoryRegion *rom_memory, - MemoryRegion **ram_memory, - PcGuestInfo *guest_info) +void pc_memory_init(PCMachineState *pcms, + MemoryRegion *system_memory, + MemoryRegion *rom_memory, + MemoryRegion **ram_memory) { int linux_boot, i; MemoryRegion *ram, *option_rom_mr; MemoryRegion *ram_below_4g, *ram_above_4g; FWCfgState *fw_cfg; - PCMachineState *pcms = PC_MACHINE(machine); + MachineState *machine = MACHINE(pcms); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); - assert(machine->ram_size == below_4g_mem_size + above_4g_mem_size); + assert(machine->ram_size == pcms->below_4g_mem_size + + pcms->above_4g_mem_size); linux_boot = (machine->kernel_filename != NULL); @@ -1317,19 +1296,20 @@ FWCfgState *pc_memory_init(MachineState *machine, *ram_memory = ram; ram_below_4g = g_malloc(sizeof(*ram_below_4g)); memory_region_init_alias(ram_below_4g, NULL, "ram-below-4g", ram, - 0, below_4g_mem_size); + 0, pcms->below_4g_mem_size); memory_region_add_subregion(system_memory, 0, ram_below_4g); - e820_add_entry(0, below_4g_mem_size, E820_RAM); - if (above_4g_mem_size > 0) { + e820_add_entry(0, pcms->below_4g_mem_size, E820_RAM); + if (pcms->above_4g_mem_size > 0) { ram_above_4g = g_malloc(sizeof(*ram_above_4g)); memory_region_init_alias(ram_above_4g, NULL, "ram-above-4g", ram, - below_4g_mem_size, above_4g_mem_size); + pcms->below_4g_mem_size, + pcms->above_4g_mem_size); memory_region_add_subregion(system_memory, 0x100000000ULL, ram_above_4g); - e820_add_entry(0x100000000ULL, above_4g_mem_size, E820_RAM); + e820_add_entry(0x100000000ULL, pcms->above_4g_mem_size, E820_RAM); } - if (!guest_info->has_reserved_memory && + if (!pcmc->has_reserved_memory && (machine->ram_slots || (machine->maxram_size > machine->ram_size))) { MachineClass *mc = MACHINE_GET_CLASS(machine); @@ -1340,7 +1320,7 @@ FWCfgState *pc_memory_init(MachineState *machine, } /* initialize hotplug memory address space */ - if (guest_info->has_reserved_memory && + if (pcmc->has_reserved_memory && (machine->ram_size < machine->maxram_size)) { ram_addr_t hotplug_mem_size = machine->maxram_size - machine->ram_size; @@ -1359,9 +1339,9 @@ FWCfgState *pc_memory_init(MachineState *machine, } pcms->hotplug_memory.base = - ROUND_UP(0x100000000ULL + above_4g_mem_size, 1ULL << 30); + ROUND_UP(0x100000000ULL + pcms->above_4g_mem_size, 1ULL << 30); - if (pcms->enforce_aligned_dimm) { + if (pcmc->enforce_aligned_dimm) { /* size hotplug region assuming 1G page max alignment per slot */ hotplug_mem_size += (1ULL << 30) * machine->ram_slots; } @@ -1380,36 +1360,41 @@ FWCfgState *pc_memory_init(MachineState *machine, } /* Initialize PC system firmware */ - pc_system_firmware_init(rom_memory, guest_info->isapc_ram_fw); + pc_system_firmware_init(rom_memory, !pcmc->pci_enabled); option_rom_mr = g_malloc(sizeof(*option_rom_mr)); memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE, - &error_abort); + &error_fatal); vmstate_register_ram_global(option_rom_mr); memory_region_add_subregion_overlap(rom_memory, PC_ROM_MIN_VGA, option_rom_mr, 1); - fw_cfg = bochs_bios_init(); + fw_cfg = bochs_bios_init(&address_space_memory, pcms); + rom_set_fw(fw_cfg); - if (guest_info->has_reserved_memory && pcms->hotplug_memory.base) { + if (pcmc->has_reserved_memory && pcms->hotplug_memory.base) { uint64_t *val = g_malloc(sizeof(*val)); - *val = cpu_to_le64(ROUND_UP(pcms->hotplug_memory.base, 0x1ULL << 30)); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + uint64_t res_mem_end = pcms->hotplug_memory.base; + + if (!pcmc->broken_reserved_end) { + res_mem_end += memory_region_size(&pcms->hotplug_memory.mr); + } + *val = cpu_to_le64(ROUND_UP(res_mem_end, 0x1ULL << 30)); fw_cfg_add_file(fw_cfg, "etc/reserved-memory-end", val, sizeof(*val)); } if (linux_boot) { - load_linux(fw_cfg, machine->kernel_filename, machine->initrd_filename, - machine->kernel_cmdline, below_4g_mem_size); + load_linux(pcms, fw_cfg); } for (i = 0; i < nb_option_roms; i++) { rom_add_option(option_rom[i].name, option_rom[i].bootindex); } - guest_info->fw_cfg = fw_cfg; - return fw_cfg; + pcms->fw_cfg = fw_cfg; } qemu_irq pc_allocate_cpu_irq(void) @@ -1421,6 +1406,7 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus) { DeviceState *dev = NULL; + rom_set_order_override(FW_CFG_ORDER_OVERRIDE_VGA); if (pci_bus) { PCIDevice *pcidev = pci_vga_init(pci_bus); dev = pcidev ? &pcidev->qdev : NULL; @@ -1428,18 +1414,10 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus) ISADevice *isadev = isa_vga_init(isa_bus); dev = isadev ? DEVICE(isadev) : NULL; } + rom_reset_order_override(); return dev; } -static void cpu_request_exit(void *opaque, int irq, int level) -{ - CPUState *cpu = current_cpu; - - if (cpu && level) { - cpu_exit(cpu); - } -} - static const MemoryRegionOps ioport80_io_ops = { .write = ioport80_write, .read = ioport80_read, @@ -1464,7 +1442,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, ISADevice **rtc_state, bool create_fdctrl, bool no_vmport, - uint32 hpet_irqs) + uint32_t hpet_irqs) { int i; DriveInfo *fd[MAX_FD]; @@ -1474,7 +1452,6 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, qemu_irq rtc_irq = NULL; qemu_irq *a20_line; ISADevice *i8042, *port92, *vmmouse, *pit = NULL; - qemu_irq *cpu_exit_irq; MemoryRegion *ioport80_io = g_new(MemoryRegion, 1); MemoryRegion *ioportF0_io = g_new(MemoryRegion, 1); @@ -1519,7 +1496,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, qemu_register_boot_set(pc_boot_set, *rtc_state); if (!xen_enabled()) { - if (kvm_irqchip_in_kernel()) { + if (kvm_pit_in_kernel()) { pit = kvm_pit_init(isa_bus, 0x40); } else { pit = pit_init(isa_bus, 0x40, pit_isa_irq, pit_alt_irq); @@ -1551,8 +1528,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, port92 = isa_create_simple(isa_bus, "port92"); port92_init(port92, &a20_line[1]); - cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1); - DMA_init(0, cpu_exit_irq); + DMA_init(isa_bus, 0); for(i = 0; i < MAX_FD; i++) { fd[i] = drive_get(IF_FLOPPY, 0, i); @@ -1567,6 +1543,7 @@ void pc_nic_init(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]; @@ -1576,6 +1553,7 @@ void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus) pci_nic_init_nofail(nd, pci_bus, "e1000", NULL); } } + rom_reset_order_override(); } void pc_pci_device_init(PCIBus *pci_bus) @@ -1595,7 +1573,7 @@ void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name) SysBusDevice *d; unsigned int i; - if (kvm_irqchip_in_kernel()) { + if (kvm_ioapic_in_kernel()) { dev = qdev_create(NULL, "kvm-ioapic"); } else { dev = qdev_create(NULL, "ioapic"); @@ -1619,12 +1597,13 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev, HotplugHandlerClass *hhc; Error *local_err = NULL; PCMachineState *pcms = PC_MACHINE(hotplug_dev); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *mr = ddc->get_memory_region(dimm); uint64_t align = TARGET_PAGE_SIZE; - if (memory_region_get_alignment(mr) && pcms->enforce_aligned_dimm) { + if (memory_region_get_alignment(mr) && pcmc->enforce_aligned_dimm) { align = memory_region_get_alignment(mr); } @@ -1689,9 +1668,19 @@ static void pc_dimm_unplug(HotplugHandler *hotplug_dev, error_propagate(errp, local_err); } +static int pc_apic_cmp(const void *a, const void *b) +{ + CPUArchId *apic_a = (CPUArchId *)a; + CPUArchId *apic_b = (CPUArchId *)b; + + return apic_a->arch_id - apic_b->arch_id; +} + static void pc_cpu_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + CPUClass *cc = CPU_GET_CLASS(dev); + CPUArchId apic_id, *found_cpu; HotplugHandlerClass *hhc; Error *local_err = NULL; PCMachineState *pcms = PC_MACHINE(hotplug_dev); @@ -1714,6 +1703,13 @@ static void pc_cpu_plug(HotplugHandler *hotplug_dev, /* increment the number of CPUs */ rtc_set_memory(pcms->rtc, 0x5f, rtc_get_memory(pcms->rtc, 0x5f) + 1); + + apic_id.arch_id = cc->get_arch_id(CPU(dev)); + found_cpu = bsearch(&apic_id, pcms->possible_cpus->cpus, + pcms->possible_cpus->len, sizeof(*pcms->possible_cpus->cpus), + pc_apic_cmp); + assert(found_cpu); + found_cpu->cpu = CPU(dev); out: error_propagate(errp, local_err); } @@ -1765,42 +1761,43 @@ static HotplugHandler *pc_get_hotpug_handler(MachineState *machine, } static void -pc_machine_get_hotplug_memory_region_size(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +pc_machine_get_hotplug_memory_region_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); int64_t value = memory_region_size(&pcms->hotplug_memory.mr); - visit_type_int(v, &value, name, errp); + visit_type_int(v, name, &value, errp); } static void pc_machine_get_max_ram_below_4g(Object *obj, Visitor *v, - void *opaque, const char *name, - Error **errp) + const char *name, void *opaque, + Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); uint64_t value = pcms->max_ram_below_4g; - visit_type_size(v, &value, name, errp); + visit_type_size(v, name, &value, errp); } static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v, - void *opaque, const char *name, - Error **errp) + const char *name, void *opaque, + Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); Error *error = NULL; uint64_t value; - visit_type_size(v, &value, name, &error); + visit_type_size(v, name, &value, &error); if (error) { error_propagate(errp, error); return; } if (value > (1ULL << 32)) { - error_set(&error, ERROR_CLASS_GENERIC_ERROR, - "Machine option 'max-ram-below-4g=%"PRIu64 - "' expects size less than or equal to 4G", value); + error_setg(&error, + "Machine option 'max-ram-below-4g=%"PRIu64 + "' expects size less than or equal to 4G", value); error_propagate(errp, error); return; } @@ -1814,21 +1811,21 @@ static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v, pcms->max_ram_below_4g = value; } -static void pc_machine_get_vmport(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void pc_machine_get_vmport(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); OnOffAuto vmport = pcms->vmport; - visit_type_OnOffAuto(v, &vmport, name, errp); + visit_type_OnOffAuto(v, name, &vmport, errp); } -static void pc_machine_set_vmport(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void pc_machine_set_vmport(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); - visit_type_OnOffAuto(v, &pcms->vmport, name, errp); + visit_type_OnOffAuto(v, name, &pcms->vmport, errp); } bool pc_machine_is_smm_enabled(PCMachineState *pcms) @@ -1856,28 +1853,35 @@ bool pc_machine_is_smm_enabled(PCMachineState *pcms) return false; } -static void pc_machine_get_smm(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void pc_machine_get_smm(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); OnOffAuto smm = pcms->smm; - visit_type_OnOffAuto(v, &smm, name, errp); + visit_type_OnOffAuto(v, name, &smm, errp); +} + +static void pc_machine_set_smm(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + + visit_type_OnOffAuto(v, name, &pcms->smm, errp); } -static void pc_machine_set_smm(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static bool pc_machine_get_nvdimm(Object *obj, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); - visit_type_OnOffAuto(v, &pcms->smm, name, errp); + return pcms->acpi_nvdimm_state.is_enabled; } -static bool pc_machine_get_aligned_dimm(Object *obj, Error **errp) +static void pc_machine_set_nvdimm(Object *obj, bool value, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); - return pcms->enforce_aligned_dimm; + pcms->acpi_nvdimm_state.is_enabled = value; } static void pc_machine_initfn(Object *obj) @@ -1886,47 +1890,77 @@ static void pc_machine_initfn(Object *obj) object_property_add(obj, PC_MACHINE_MEMHP_REGION_SIZE, "int", pc_machine_get_hotplug_memory_region_size, - NULL, NULL, NULL, NULL); + NULL, NULL, NULL, &error_abort); pcms->max_ram_below_4g = 1ULL << 32; /* 4G */ object_property_add(obj, PC_MACHINE_MAX_RAM_BELOW_4G, "size", pc_machine_get_max_ram_below_4g, pc_machine_set_max_ram_below_4g, - NULL, NULL, NULL); + NULL, NULL, &error_abort); object_property_set_description(obj, PC_MACHINE_MAX_RAM_BELOW_4G, "Maximum ram below the 4G boundary (32bit boundary)", - NULL); + &error_abort); pcms->smm = ON_OFF_AUTO_AUTO; object_property_add(obj, PC_MACHINE_SMM, "OnOffAuto", pc_machine_get_smm, pc_machine_set_smm, - NULL, NULL, NULL); + NULL, NULL, &error_abort); object_property_set_description(obj, PC_MACHINE_SMM, "Enable SMM (pc & q35)", - NULL); + &error_abort); pcms->vmport = ON_OFF_AUTO_AUTO; object_property_add(obj, PC_MACHINE_VMPORT, "OnOffAuto", pc_machine_get_vmport, pc_machine_set_vmport, - NULL, NULL, NULL); + NULL, NULL, &error_abort); object_property_set_description(obj, PC_MACHINE_VMPORT, "Enable vmport (pc & q35)", - NULL); + &error_abort); + + /* nvdimm is disabled on default. */ + pcms->acpi_nvdimm_state.is_enabled = false; + object_property_add_bool(obj, PC_MACHINE_NVDIMM, pc_machine_get_nvdimm, + pc_machine_set_nvdimm, &error_abort); +} + +static void pc_machine_reset(void) +{ + CPUState *cs; + X86CPU *cpu; + + qemu_devices_reset(); + + /* Reset APIC after devices have been reset to cancel + * any changes that qemu_devices_reset() might have done. + */ + CPU_FOREACH(cs) { + cpu = X86_CPU(cs); - pcms->enforce_aligned_dimm = true; - object_property_add_bool(obj, PC_MACHINE_ENFORCE_ALIGNED_DIMM, - pc_machine_get_aligned_dimm, - NULL, NULL); + if (cpu->apic_state) { + device_reset(cpu->apic_state); + } + } } static unsigned pc_cpu_index_to_socket_id(unsigned cpu_index) { - unsigned pkg_id, core_id, smt_id; + X86CPUTopoInfo topo; x86_topo_ids_from_idx(smp_cores, smp_threads, cpu_index, - &pkg_id, &core_id, &smt_id); - return pkg_id; + &topo); + return topo.pkg_id; +} + +static CPUArchIdList *pc_possible_cpu_arch_ids(MachineState *machine) +{ + PCMachineState *pcms = PC_MACHINE(machine); + int len = sizeof(CPUArchIdList) + + sizeof(CPUArchId) * (pcms->possible_cpus->len); + CPUArchIdList *list = g_malloc(len); + + memcpy(list, pcms->possible_cpus, len); + return list; } static void pc_machine_class_init(ObjectClass *oc, void *data) @@ -1936,8 +1970,26 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); pcmc->get_hotplug_handler = mc->get_hotplug_handler; + pcmc->pci_enabled = true; + pcmc->has_acpi_build = true; + pcmc->rsdp_in_ram = true; + pcmc->smbios_defaults = true; + pcmc->smbios_uuid_encoded = true; + pcmc->gigabyte_align = true; + pcmc->has_reserved_memory = true; + pcmc->kvmclock_enabled = true; + pcmc->enforce_aligned_dimm = true; + /* BIOS ACPI tables: 128K. Other BIOS datastructures: less than 4K reported + * to be used at the moment, 32K should be enough for a while. */ + pcmc->acpi_data_size = 0x20000 + 0x8000; + pcmc->save_tsc_khz = true; mc->get_hotplug_handler = pc_get_hotpug_handler; mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id; + mc->possible_cpu_arch_ids = pc_possible_cpu_arch_ids; + mc->default_boot_order = "cad"; + mc->hot_add_cpu = pc_hot_add_cpu; + mc->max_cpus = 255; + mc->reset = pc_machine_reset; hc->plug = pc_machine_device_plug_cb; hc->unplug_request = pc_machine_device_unplug_request_cb; hc->unplug = pc_machine_device_unplug_cb; diff --git a/qemu/hw/i386/pc_piix.c b/qemu/hw/i386/pc_piix.c index a896624f8..7f50116bc 100644 --- a/qemu/hw/i386/pc_piix.c +++ b/qemu/hw/i386/pc_piix.c @@ -22,13 +22,14 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include "hw/hw.h" #include "hw/loader.h" #include "hw/i386/pc.h" #include "hw/i386/apic.h" -#include "hw/i386/smbios.h" +#include "hw/smbios/smbios.h" #include "hw/pci/pci.h" #include "hw/pci/pci_ids.h" #include "hw/usb.h" @@ -39,7 +40,6 @@ #include "hw/kvm/clock.h" #include "sysemu/sysemu.h" #include "hw/sysbus.h" -#include "hw/cpu/icc_bus.h" #include "sysemu/arch_init.h" #include "sysemu/block-backend.h" #include "hw/i2c/smbus.h" @@ -50,9 +50,11 @@ #include "cpu.h" #include "qemu/error-report.h" #ifdef CONFIG_XEN -# include +#include +#include "hw/xen/xen_pt.h" #endif #include "migration/migration.h" +#include "kvm_i386.h" #define MAX_IDE_BUS 2 @@ -60,29 +62,15 @@ static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 }; static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 }; static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; -static bool pci_enabled = true; -static bool has_acpi_build = true; -static bool rsdp_in_ram = true; -static int legacy_acpi_table_size; -static bool smbios_defaults = true; -static bool smbios_legacy_mode; -static bool smbios_uuid_encoded = true; -/* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to - * host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte - * pages in the host. - */ -static bool gigabyte_align = true; -static bool has_reserved_memory = true; -static bool kvmclock_enabled = true; - /* PC hardware initialisation */ -static void pc_init1(MachineState *machine) +static void pc_init1(MachineState *machine, + const char *host_type, const char *pci_type) { - PCMachineState *pc_machine = PC_MACHINE(machine); + PCMachineState *pcms = PC_MACHINE(machine); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *system_io = get_system_io(); int i; - ram_addr_t below_4g_mem_size, above_4g_mem_size; PCIBus *pci_bus; ISABus *isa_bus; PCII440FXState *i440fx_state; @@ -97,8 +85,6 @@ static void pc_init1(MachineState *machine) MemoryRegion *ram_memory; MemoryRegion *pci_memory; MemoryRegion *rom_memory; - DeviceState *icc_bridge; - PcGuestInfo *guest_info; ram_addr_t lowmem; /* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory). @@ -109,7 +95,7 @@ static void pc_init1(MachineState *machine) * breaking migration. */ if (machine->ram_size >= 0xe0000000) { - lowmem = gigabyte_align ? 0xc0000000 : 0xe0000000; + lowmem = pcmc->gigabyte_align ? 0xc0000000 : 0xe0000000; } else { lowmem = 0xe0000000; } @@ -117,41 +103,35 @@ static void pc_init1(MachineState *machine) /* Handle the machine opt max-ram-below-4g. It is basically doing * min(qemu limit, user limit). */ - if (lowmem > pc_machine->max_ram_below_4g) { - lowmem = pc_machine->max_ram_below_4g; + if (lowmem > pcms->max_ram_below_4g) { + lowmem = pcms->max_ram_below_4g; if (machine->ram_size - lowmem > lowmem && lowmem & ((1ULL << 30) - 1)) { error_report("Warning: Large machine and max_ram_below_4g(%"PRIu64 ") not a multiple of 1G; possible bad performance.", - pc_machine->max_ram_below_4g); + pcms->max_ram_below_4g); } } if (machine->ram_size >= lowmem) { - above_4g_mem_size = machine->ram_size - lowmem; - below_4g_mem_size = lowmem; + pcms->above_4g_mem_size = machine->ram_size - lowmem; + pcms->below_4g_mem_size = lowmem; } else { - above_4g_mem_size = 0; - below_4g_mem_size = machine->ram_size; + pcms->above_4g_mem_size = 0; + pcms->below_4g_mem_size = machine->ram_size; } - if (xen_enabled() && xen_hvm_init(&below_4g_mem_size, &above_4g_mem_size, - &ram_memory) != 0) { - fprintf(stderr, "xen hardware virtual machine initialisation failed\n"); - exit(1); + if (xen_enabled()) { + xen_hvm_init(pcms, &ram_memory); } - icc_bridge = qdev_create(NULL, TYPE_ICC_BRIDGE); - object_property_add_child(qdev_get_machine(), "icc-bridge", - OBJECT(icc_bridge), NULL); - - pc_cpus_init(machine->cpu_model, icc_bridge); + pc_cpus_init(pcms); - if (kvm_enabled() && kvmclock_enabled) { + if (kvm_enabled() && pcmc->kvmclock_enabled) { kvmclock_create(); } - if (pci_enabled) { + if (pcmc->pci_enabled) { pci_memory = g_new(MemoryRegion, 1); memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); rom_memory = pci_memory; @@ -160,60 +140,54 @@ static void pc_init1(MachineState *machine) rom_memory = system_memory; } - guest_info = pc_guest_info_init(below_4g_mem_size, above_4g_mem_size); - - guest_info->has_acpi_build = has_acpi_build; - guest_info->legacy_acpi_table_size = legacy_acpi_table_size; + pc_guest_info_init(pcms); - guest_info->isapc_ram_fw = !pci_enabled; - guest_info->has_reserved_memory = has_reserved_memory; - guest_info->rsdp_in_ram = rsdp_in_ram; - - if (smbios_defaults) { + if (pcmc->smbios_defaults) { MachineClass *mc = MACHINE_GET_CLASS(machine); /* These values are guest ABI, do not change */ smbios_set_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)", - mc->name, smbios_legacy_mode, smbios_uuid_encoded); + mc->name, pcmc->smbios_legacy_mode, + pcmc->smbios_uuid_encoded, + SMBIOS_ENTRY_POINT_21); } /* allocate ram and load rom/bios */ if (!xen_enabled()) { - pc_memory_init(machine, system_memory, - below_4g_mem_size, above_4g_mem_size, - rom_memory, &ram_memory, guest_info); + pc_memory_init(pcms, system_memory, + rom_memory, &ram_memory); } else if (machine->kernel_filename != NULL) { /* For xen HVM direct kernel boot, load linux here */ - xen_load_linux(machine->kernel_filename, - machine->kernel_cmdline, - machine->initrd_filename, - below_4g_mem_size, - guest_info); + xen_load_linux(pcms); } gsi_state = g_malloc0(sizeof(*gsi_state)); - if (kvm_irqchip_in_kernel()) { - kvm_pc_setup_irq_routing(pci_enabled); + if (kvm_ioapic_in_kernel()) { + kvm_pc_setup_irq_routing(pcmc->pci_enabled); gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state, GSI_NUM_PINS); } else { gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS); } - if (pci_enabled) { - pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, &isa_bus, gsi, + if (pcmc->pci_enabled) { + pci_bus = i440fx_init(host_type, + pci_type, + &i440fx_state, &piix3_devfn, &isa_bus, gsi, system_memory, system_io, machine->ram_size, - below_4g_mem_size, - above_4g_mem_size, + pcms->below_4g_mem_size, + pcms->above_4g_mem_size, pci_memory, ram_memory); + pcms->bus = pci_bus; } else { pci_bus = NULL; i440fx_state = NULL; - isa_bus = isa_bus_new(NULL, get_system_memory(), system_io); + isa_bus = isa_bus_new(NULL, get_system_memory(), system_io, + &error_abort); no_hpet = 1; } isa_bus_irqs(isa_bus, gsi); - if (kvm_irqchip_in_kernel()) { + if (kvm_pic_in_kernel()) { i8259 = kvm_i8259_init(isa_bus); } else if (xen_enabled()) { i8259 = xen_interrupt_controller_init(); @@ -225,28 +199,27 @@ static void pc_init1(MachineState *machine) gsi_state->i8259_irq[i] = i8259[i]; } g_free(i8259); - if (pci_enabled) { + if (pcmc->pci_enabled) { ioapic_init_gsi(gsi_state, "i440fx"); } - qdev_init_nofail(icc_bridge); pc_register_ferr_irq(gsi[13]); - pc_vga_init(isa_bus, pci_enabled ? pci_bus : NULL); + pc_vga_init(isa_bus, pcmc->pci_enabled ? pci_bus : NULL); - assert(pc_machine->vmport != ON_OFF_AUTO_MAX); - if (pc_machine->vmport == ON_OFF_AUTO_AUTO) { - pc_machine->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON; + assert(pcms->vmport != ON_OFF_AUTO__MAX); + if (pcms->vmport == ON_OFF_AUTO_AUTO) { + pcms->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON; } /* init basic PC hardware */ pc_basic_device_init(isa_bus, gsi, &rtc_state, true, - (pc_machine->vmport != ON_OFF_AUTO_ON), 0x4); + (pcms->vmport != ON_OFF_AUTO_ON), 0x4); pc_nic_init(isa_bus, pci_bus); ide_drive_get(hd, ARRAY_SIZE(hd)); - if (pci_enabled) { + if (pcmc->pci_enabled) { PCIDevice *dev; if (xen_enabled()) { dev = pci_piix3_xen_ide_init(pci_bus, hd, piix3_devfn + 1); @@ -271,14 +244,13 @@ static void pc_init1(MachineState *machine) } } - pc_cmos_init(below_4g_mem_size, above_4g_mem_size, machine->boot_order, - machine, idebus[0], idebus[1], rtc_state); + pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state); - if (pci_enabled && usb_enabled()) { + if (pcmc->pci_enabled && usb_enabled()) { pci_create_simple(pci_bus, piix3_devfn + 2, "piix3-usb-uhci"); } - if (pci_enabled && acpi_enabled) { + if (pcmc->pci_enabled && acpi_enabled) { DeviceState *piix4_pm; I2CBus *smbus; @@ -286,24 +258,36 @@ static void pc_init1(MachineState *machine) /* TODO: Populate SPD eeprom data. */ smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100, gsi[9], smi_irq, - pc_machine_is_smm_enabled(pc_machine), + pc_machine_is_smm_enabled(pcms), &piix4_pm); smbus_eeprom_init(smbus, 8, NULL, 0); object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, TYPE_HOTPLUG_HANDLER, - (Object **)&pc_machine->acpi_dev, + (Object **)&pcms->acpi_dev, object_property_allow_set_link, OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); object_property_set_link(OBJECT(machine), OBJECT(piix4_pm), PC_MACHINE_ACPI_DEVICE_PROP, &error_abort); } - if (pci_enabled) { + if (pcmc->pci_enabled) { pc_pci_device_init(pci_bus); } + + if (pcms->acpi_nvdimm_state.is_enabled) { + nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io, + pcms->fw_cfg, OBJECT(pcms)); + } } +/* Looking for a pc_compat_2_4() function? It doesn't exist. + * pc_compat_*() functions that run on machine-init time and + * change global QEMU state are deprecated. Please don't create + * one, and implement any pc-*-2.4 (and newer) compat code in + * HW_COMPAT_*, PC_COMPAT_*, or * pc_*_machine_options(). + */ + static void pc_compat_2_3(MachineState *machine) { PCMachineState *pcms = PC_MACHINE(machine); @@ -318,80 +302,29 @@ static void pc_compat_2_3(MachineState *machine) static void pc_compat_2_2(MachineState *machine) { pc_compat_2_3(machine); - rsdp_in_ram = false; - x86_cpu_compat_set_features("kvm64", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("kvm32", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Conroe", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Penryn", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Nehalem", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Westmere", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("SandyBridge", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Haswell", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Broadwell", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Opteron_G1", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Opteron_G2", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Opteron_G3", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Opteron_G4", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Opteron_G5", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Haswell", FEAT_1_ECX, 0, CPUID_EXT_F16C); - x86_cpu_compat_set_features("Haswell", FEAT_1_ECX, 0, CPUID_EXT_RDRAND); - x86_cpu_compat_set_features("Broadwell", FEAT_1_ECX, 0, CPUID_EXT_F16C); - x86_cpu_compat_set_features("Broadwell", FEAT_1_ECX, 0, CPUID_EXT_RDRAND); machine->suppress_vmdesc = true; } static void pc_compat_2_1(MachineState *machine) { - PCMachineState *pcms = PC_MACHINE(machine); - pc_compat_2_2(machine); - smbios_uuid_encoded = false; - x86_cpu_compat_set_features("coreduo", FEAT_1_ECX, CPUID_EXT_VMX, 0); - x86_cpu_compat_set_features("core2duo", FEAT_1_ECX, CPUID_EXT_VMX, 0); - x86_cpu_compat_kvm_no_autodisable(FEAT_8000_0001_ECX, CPUID_EXT3_SVM); - pcms->enforce_aligned_dimm = false; + x86_cpu_change_kvm_default("svm", NULL); } static void pc_compat_2_0(MachineState *machine) { pc_compat_2_1(machine); - /* This value depends on the actual DSDT and SSDT compiled into - * the source QEMU; unfortunately it depends on the binary and - * not on the machine type, so we cannot make pc-i440fx-1.7 work on - * both QEMU 1.7 and QEMU 2.0. - * - * Large variations cause migration to fail for more than one - * consecutive value of the "-smp" maxcpus option. - * - * For small variations of the kind caused by different iasl versions, - * the 4k rounding usually leaves slack. However, there could be still - * one or two values that break. For QEMU 1.7 and QEMU 2.0 the - * slack is only ~10 bytes before one "-smp maxcpus" value breaks! - * - * 6652 is valid for QEMU 2.0, the right value for pc-i440fx-1.7 on - * QEMU 1.7 it is 6414. For RHEL/CentOS 7.0 it is 6418. - */ - legacy_acpi_table_size = 6652; - smbios_legacy_mode = true; - has_reserved_memory = false; - pc_set_legacy_acpi_data_size(); } static void pc_compat_1_7(MachineState *machine) { pc_compat_2_0(machine); - smbios_defaults = false; - gigabyte_align = false; - option_rom_has_mr = true; - legacy_acpi_table_size = 6414; - x86_cpu_compat_kvm_no_autoenable(FEAT_1_ECX, CPUID_EXT_X2APIC); + x86_cpu_change_kvm_default("x2apic", NULL); } static void pc_compat_1_6(MachineState *machine) { pc_compat_1_7(machine); - rom_file_has_mr = false; - has_acpi_build = false; } static void pc_compat_1_5(MachineState *machine) @@ -402,8 +335,6 @@ static void pc_compat_1_5(MachineState *machine) static void pc_compat_1_4(MachineState *machine) { pc_compat_1_5(machine); - x86_cpu_compat_set_features("n270", FEAT_1_ECX, 0, CPUID_EXT_MOVBE); - x86_cpu_compat_set_features("Westmere", FEAT_1_ECX, 0, CPUID_EXT_PCLMULQDQ); } static void pc_compat_1_3(MachineState *machine) @@ -416,40 +347,46 @@ static void pc_compat_1_3(MachineState *machine) static void pc_compat_1_2(MachineState *machine) { pc_compat_1_3(machine); - x86_cpu_compat_kvm_no_autoenable(FEAT_KVM, 1 << KVM_FEATURE_PV_EOI); + x86_cpu_change_kvm_default("kvm-pv-eoi", NULL); } /* PC compat function for pc-0.10 to pc-0.13 */ static void pc_compat_0_13(MachineState *machine) { pc_compat_1_2(machine); - kvmclock_enabled = false; } static void pc_init_isa(MachineState *machine) { - pci_enabled = false; - has_acpi_build = false; - smbios_defaults = false; - gigabyte_align = false; - smbios_legacy_mode = true; - has_reserved_memory = false; - option_rom_has_mr = true; - rom_file_has_mr = false; if (!machine->cpu_model) { machine->cpu_model = "486"; } - x86_cpu_compat_kvm_no_autoenable(FEAT_KVM, 1 << KVM_FEATURE_PV_EOI); + x86_cpu_change_kvm_default("kvm-pv-eoi", NULL); enable_compat_apic_id_mode(); - pc_init1(machine); + pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, TYPE_I440FX_PCI_DEVICE); } #ifdef CONFIG_XEN +static void pc_xen_hvm_init_pci(MachineState *machine) +{ + const char *pci_type = has_igd_gfx_passthru ? + TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE : TYPE_I440FX_PCI_DEVICE; + + pc_init1(machine, + TYPE_I440FX_PCI_HOST_BRIDGE, + pci_type); +} + static void pc_xen_hvm_init(MachineState *machine) { PCIBus *bus; - pc_init1(machine); + if (!xen_enabled()) { + error_report("xenfv machine requires the xen accelerator"); + exit(1); + } + + pc_xen_hvm_init_pci(machine); bus = pci_find_primary_bus(); if (bus != NULL) { @@ -465,27 +402,55 @@ static void pc_xen_hvm_init(MachineState *machine) if (compat) { \ compat(machine); \ } \ - pc_init1(machine); \ + pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \ + TYPE_I440FX_PCI_DEVICE); \ } \ DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn) static void pc_i440fx_machine_options(MachineClass *m) { - pc_default_machine_options(m); m->family = "pc_piix"; m->desc = "Standard PC (i440FX + PIIX, 1996)"; m->hot_add_cpu = pc_hot_add_cpu; + m->default_machine_opts = "firmware=bios-256k.bin"; + m->default_display = "std"; } -static void pc_i440fx_2_4_machine_options(MachineClass *m) +static void pc_i440fx_2_6_machine_options(MachineClass *m) { pc_i440fx_machine_options(m); - m->default_machine_opts = "firmware=bios-256k.bin"; - m->default_display = "std"; m->alias = "pc"; m->is_default = 1; } +DEFINE_I440FX_MACHINE(v2_6, "pc-i440fx-2.6", NULL, + pc_i440fx_2_6_machine_options); + + +static void pc_i440fx_2_5_machine_options(MachineClass *m) +{ + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pc_i440fx_2_6_machine_options(m); + m->alias = NULL; + m->is_default = 0; + pcmc->save_tsc_khz = false; + m->legacy_fw_cfg_order = 1; + SET_MACHINE_COMPAT(m, PC_COMPAT_2_5); +} + +DEFINE_I440FX_MACHINE(v2_5, "pc-i440fx-2.5", NULL, + pc_i440fx_2_5_machine_options); + + +static void pc_i440fx_2_4_machine_options(MachineClass *m) +{ + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pc_i440fx_2_5_machine_options(m); + m->hw_version = "2.4.0"; + pcmc->broken_reserved_end = true; + SET_MACHINE_COMPAT(m, PC_COMPAT_2_4); +} + DEFINE_I440FX_MACHINE(v2_4, "pc-i440fx-2.4", NULL, pc_i440fx_2_4_machine_options) @@ -493,8 +458,7 @@ DEFINE_I440FX_MACHINE(v2_4, "pc-i440fx-2.4", NULL, static void pc_i440fx_2_3_machine_options(MachineClass *m) { pc_i440fx_2_4_machine_options(m); - m->alias = NULL; - m->is_default = 0; + m->hw_version = "2.3.0"; SET_MACHINE_COMPAT(m, PC_COMPAT_2_3); } @@ -504,8 +468,11 @@ DEFINE_I440FX_MACHINE(v2_3, "pc-i440fx-2.3", pc_compat_2_3, static void pc_i440fx_2_2_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_2_3_machine_options(m); + m->hw_version = "2.2.0"; SET_MACHINE_COMPAT(m, PC_COMPAT_2_2); + pcmc->rsdp_in_ram = false; } DEFINE_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2, @@ -514,9 +481,13 @@ DEFINE_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2, static void pc_i440fx_2_1_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_2_2_machine_options(m); + m->hw_version = "2.1.0"; m->default_display = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_2_1); + pcmc->smbios_uuid_encoded = false; + pcmc->enforce_aligned_dimm = false; } DEFINE_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1, @@ -526,8 +497,30 @@ DEFINE_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1, static void pc_i440fx_2_0_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_2_1_machine_options(m); + m->hw_version = "2.0.0"; SET_MACHINE_COMPAT(m, PC_COMPAT_2_0); + pcmc->smbios_legacy_mode = true; + pcmc->has_reserved_memory = false; + /* This value depends on the actual DSDT and SSDT compiled into + * the source QEMU; unfortunately it depends on the binary and + * not on the machine type, so we cannot make pc-i440fx-1.7 work on + * both QEMU 1.7 and QEMU 2.0. + * + * Large variations cause migration to fail for more than one + * consecutive value of the "-smp" maxcpus option. + * + * For small variations of the kind caused by different iasl versions, + * the 4k rounding usually leaves slack. However, there could be still + * one or two values that break. For QEMU 1.7 and QEMU 2.0 the + * slack is only ~10 bytes before one "-smp maxcpus" value breaks! + * + * 6652 is valid for QEMU 2.0, the right value for pc-i440fx-1.7 on + * QEMU 1.7 it is 6414. For RHEL/CentOS 7.0 it is 6418. + */ + pcmc->legacy_acpi_table_size = 6652; + pcmc->acpi_data_size = 0x10000; } DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0, @@ -536,9 +529,15 @@ DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0, static void pc_i440fx_1_7_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_2_0_machine_options(m); + m->hw_version = "1.7.0"; m->default_machine_opts = NULL; + m->option_rom_has_mr = true; SET_MACHINE_COMPAT(m, PC_COMPAT_1_7); + pcmc->smbios_defaults = false; + pcmc->gigabyte_align = false; + pcmc->legacy_acpi_table_size = 6414; } DEFINE_I440FX_MACHINE(v1_7, "pc-i440fx-1.7", pc_compat_1_7, @@ -547,8 +546,12 @@ DEFINE_I440FX_MACHINE(v1_7, "pc-i440fx-1.7", pc_compat_1_7, static void pc_i440fx_1_6_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_1_7_machine_options(m); + m->hw_version = "1.6.0"; + m->rom_file_has_mr = false; SET_MACHINE_COMPAT(m, PC_COMPAT_1_6); + pcmc->has_acpi_build = false; } DEFINE_I440FX_MACHINE(v1_6, "pc-i440fx-1.6", pc_compat_1_6, @@ -558,6 +561,7 @@ DEFINE_I440FX_MACHINE(v1_6, "pc-i440fx-1.6", pc_compat_1_6, static void pc_i440fx_1_5_machine_options(MachineClass *m) { pc_i440fx_1_6_machine_options(m); + m->hw_version = "1.5.0"; SET_MACHINE_COMPAT(m, PC_COMPAT_1_5); } @@ -568,6 +572,7 @@ DEFINE_I440FX_MACHINE(v1_5, "pc-i440fx-1.5", pc_compat_1_5, static void pc_i440fx_1_4_machine_options(MachineClass *m) { pc_i440fx_1_5_machine_options(m); + m->hw_version = "1.4.0"; m->hot_add_cpu = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_1_4); } @@ -600,6 +605,7 @@ DEFINE_I440FX_MACHINE(v1_4, "pc-i440fx-1.4", pc_compat_1_4, static void pc_i440fx_1_3_machine_options(MachineClass *m) { pc_i440fx_1_4_machine_options(m); + m->hw_version = "1.3.0"; SET_MACHINE_COMPAT(m, PC_COMPAT_1_3); } @@ -638,6 +644,7 @@ DEFINE_I440FX_MACHINE(v1_3, "pc-1.3", pc_compat_1_3, static void pc_i440fx_1_2_machine_options(MachineClass *m) { pc_i440fx_1_3_machine_options(m); + m->hw_version = "1.2.0"; SET_MACHINE_COMPAT(m, PC_COMPAT_1_2); } @@ -680,6 +687,7 @@ DEFINE_I440FX_MACHINE(v1_2, "pc-1.2", pc_compat_1_2, static void pc_i440fx_1_1_machine_options(MachineClass *m) { pc_i440fx_1_2_machine_options(m); + m->hw_version = "1.1.0"; SET_MACHINE_COMPAT(m, PC_COMPAT_1_1); } @@ -797,9 +805,11 @@ DEFINE_I440FX_MACHINE(v0_14, "pc-0.14", pc_compat_1_2, static void pc_i440fx_0_13_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_0_14_machine_options(m); m->hw_version = "0.13"; SET_MACHINE_COMPAT(m, PC_COMPAT_0_13); + pcmc->kvmclock_enabled = false; } DEFINE_I440FX_MACHINE(v0_13, "pc-0.13", pc_compat_0_13, @@ -906,12 +916,132 @@ static void pc_i440fx_0_10_machine_options(MachineClass *m) DEFINE_I440FX_MACHINE(v0_10, "pc-0.10", pc_compat_0_13, pc_i440fx_0_10_machine_options); +typedef struct { + uint16_t gpu_device_id; + uint16_t pch_device_id; + uint8_t pch_revision_id; +} IGDDeviceIDInfo; + +/* In real world different GPU should have different PCH. But actually + * the different PCH DIDs likely map to different PCH SKUs. We do the + * same thing for the GPU. For PCH, the different SKUs are going to be + * all the same silicon design and implementation, just different + * features turn on and off with fuses. The SW interfaces should be + * consistent across all SKUs in a given family (eg LPT). But just same + * features may not be supported. + * + * Most of these different PCH features probably don't matter to the + * Gfx driver, but obviously any difference in display port connections + * will so it should be fine with any PCH in case of passthrough. + * + * So currently use one PCH version, 0x8c4e, to cover all HSW(Haswell) + * scenarios, 0x9cc3 for BDW(Broadwell). + */ +static const IGDDeviceIDInfo igd_combo_id_infos[] = { + /* HSW Classic */ + {0x0402, 0x8c4e, 0x04}, /* HSWGT1D, HSWD_w7 */ + {0x0406, 0x8c4e, 0x04}, /* HSWGT1M, HSWM_w7 */ + {0x0412, 0x8c4e, 0x04}, /* HSWGT2D, HSWD_w7 */ + {0x0416, 0x8c4e, 0x04}, /* HSWGT2M, HSWM_w7 */ + {0x041E, 0x8c4e, 0x04}, /* HSWGT15D, HSWD_w7 */ + /* HSW ULT */ + {0x0A06, 0x8c4e, 0x04}, /* HSWGT1UT, HSWM_w7 */ + {0x0A16, 0x8c4e, 0x04}, /* HSWGT2UT, HSWM_w7 */ + {0x0A26, 0x8c4e, 0x06}, /* HSWGT3UT, HSWM_w7 */ + {0x0A2E, 0x8c4e, 0x04}, /* HSWGT3UT28W, HSWM_w7 */ + {0x0A1E, 0x8c4e, 0x04}, /* HSWGT2UX, HSWM_w7 */ + {0x0A0E, 0x8c4e, 0x04}, /* HSWGT1ULX, HSWM_w7 */ + /* HSW CRW */ + {0x0D26, 0x8c4e, 0x04}, /* HSWGT3CW, HSWM_w7 */ + {0x0D22, 0x8c4e, 0x04}, /* HSWGT3CWDT, HSWD_w7 */ + /* HSW Server */ + {0x041A, 0x8c4e, 0x04}, /* HSWSVGT2, HSWD_w7 */ + /* HSW SRVR */ + {0x040A, 0x8c4e, 0x04}, /* HSWSVGT1, HSWD_w7 */ + /* BSW */ + {0x1606, 0x9cc3, 0x03}, /* BDWULTGT1, BDWM_w7 */ + {0x1616, 0x9cc3, 0x03}, /* BDWULTGT2, BDWM_w7 */ + {0x1626, 0x9cc3, 0x03}, /* BDWULTGT3, BDWM_w7 */ + {0x160E, 0x9cc3, 0x03}, /* BDWULXGT1, BDWM_w7 */ + {0x161E, 0x9cc3, 0x03}, /* BDWULXGT2, BDWM_w7 */ + {0x1602, 0x9cc3, 0x03}, /* BDWHALOGT1, BDWM_w7 */ + {0x1612, 0x9cc3, 0x03}, /* BDWHALOGT2, BDWM_w7 */ + {0x1622, 0x9cc3, 0x03}, /* BDWHALOGT3, BDWM_w7 */ + {0x162B, 0x9cc3, 0x03}, /* BDWHALO28W, BDWM_w7 */ + {0x162A, 0x9cc3, 0x03}, /* BDWGT3WRKS, BDWM_w7 */ + {0x162D, 0x9cc3, 0x03}, /* BDWGT3SRVR, BDWM_w7 */ +}; + +static void isa_bridge_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + dc->desc = "ISA bridge faked to support IGD PT"; + k->vendor_id = PCI_VENDOR_ID_INTEL; + k->class_id = PCI_CLASS_BRIDGE_ISA; +}; + +static TypeInfo isa_bridge_info = { + .name = "igd-passthrough-isa-bridge", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDevice), + .class_init = isa_bridge_class_init, +}; + +static void pt_graphics_register_types(void) +{ + type_register_static(&isa_bridge_info); +} +type_init(pt_graphics_register_types) + +void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id) +{ + struct PCIDevice *bridge_dev; + int i, num; + uint16_t pch_dev_id = 0xffff; + uint8_t pch_rev_id; + + num = ARRAY_SIZE(igd_combo_id_infos); + for (i = 0; i < num; i++) { + if (gpu_dev_id == igd_combo_id_infos[i].gpu_device_id) { + pch_dev_id = igd_combo_id_infos[i].pch_device_id; + pch_rev_id = igd_combo_id_infos[i].pch_revision_id; + } + } + + if (pch_dev_id == 0xffff) { + return; + } + + /* Currently IGD drivers always need to access PCH by 1f.0. */ + bridge_dev = pci_create_simple(bus, PCI_DEVFN(0x1f, 0), + "igd-passthrough-isa-bridge"); + + /* + * Note that vendor id is always PCI_VENDOR_ID_INTEL. + */ + if (!bridge_dev) { + fprintf(stderr, "set igd-passthrough-isa-bridge failed!\n"); + return; + } + pci_config_set_device_id(bridge_dev->config, pch_dev_id); + pci_config_set_revision(bridge_dev->config, pch_rev_id); +} static void isapc_machine_options(MachineClass *m) { - pc_common_machine_options(m); + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); m->desc = "ISA-only PC"; m->max_cpus = 1; + m->option_rom_has_mr = true; + m->rom_file_has_mr = false; + pcmc->pci_enabled = false; + pcmc->has_acpi_build = false; + pcmc->smbios_defaults = false; + pcmc->gigabyte_align = false; + pcmc->smbios_legacy_mode = true; + pcmc->has_reserved_memory = false; } DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa, @@ -921,7 +1051,6 @@ DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa, #ifdef CONFIG_XEN static void xenfv_machine_options(MachineClass *m) { - pc_common_machine_options(m); m->desc = "Xen Fully-virtualized PC"; m->max_cpus = HVM_MAX_VCPUS; m->default_machine_opts = "accel=xen"; diff --git a/qemu/hw/i386/pc_q35.c b/qemu/hw/i386/pc_q35.c index 974aead5a..04aae8958 100644 --- a/qemu/hw/i386/pc_q35.c +++ b/qemu/hw/i386/pc_q35.c @@ -27,6 +27,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/loader.h" #include "sysemu/arch_init.h" @@ -38,54 +39,40 @@ #include "hw/kvm/clock.h" #include "hw/pci-host/q35.h" #include "exec/address-spaces.h" +#include "hw/i386/pc.h" #include "hw/i386/ich9.h" -#include "hw/i386/smbios.h" +#include "hw/smbios/smbios.h" #include "hw/ide/pci.h" #include "hw/ide/ahci.h" #include "hw/usb.h" -#include "hw/cpu/icc_bus.h" #include "qemu/error-report.h" #include "migration/migration.h" /* ICH9 AHCI has 6 ports */ #define MAX_SATA_PORTS 6 -static bool has_acpi_build = true; -static bool rsdp_in_ram = true; -static bool smbios_defaults = true; -static bool smbios_legacy_mode; -static bool smbios_uuid_encoded = true; -/* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to - * host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte - * pages in the host. - */ -static bool gigabyte_align = true; -static bool has_reserved_memory = true; - /* PC hardware initialisation */ static void pc_q35_init(MachineState *machine) { - PCMachineState *pc_machine = PC_MACHINE(machine); - ram_addr_t below_4g_mem_size, above_4g_mem_size; + PCMachineState *pcms = PC_MACHINE(machine); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); Q35PCIHost *q35_host; PCIHostState *phb; PCIBus *host_bus; PCIDevice *lpc; BusState *idebus[MAX_SATA_PORTS]; ISADevice *rtc_state; + MemoryRegion *system_io = get_system_io(); MemoryRegion *pci_memory; MemoryRegion *rom_memory; MemoryRegion *ram_memory; GSIState *gsi_state; ISABus *isa_bus; - int pci_enabled = 1; qemu_irq *gsi; qemu_irq *i8259; int i; ICH9LPCState *ich9_lpc; PCIDevice *ahci; - DeviceState *icc_bridge; - PcGuestInfo *guest_info; ram_addr_t lowmem; DriveInfo *hd[MAX_SATA_PORTS]; MachineClass *mc = MACHINE_GET_CLASS(machine); @@ -96,11 +83,9 @@ static void pc_q35_init(MachineState *machine) * If it doesn't, we need to split it in chunks below and above 4G. * In any case, try to make sure that guest addresses aligned at * 1G boundaries get mapped to host addresses aligned at 1G boundaries. - * For old machine types, use whatever split we used historically to avoid - * breaking migration. */ if (machine->ram_size >= 0xb0000000) { - lowmem = gigabyte_align ? 0x80000000 : 0xb0000000; + lowmem = 0x80000000; } else { lowmem = 0xb0000000; } @@ -108,41 +93,34 @@ static void pc_q35_init(MachineState *machine) /* Handle the machine opt max-ram-below-4g. It is basically doing * min(qemu limit, user limit). */ - if (lowmem > pc_machine->max_ram_below_4g) { - lowmem = pc_machine->max_ram_below_4g; + if (lowmem > pcms->max_ram_below_4g) { + lowmem = pcms->max_ram_below_4g; if (machine->ram_size - lowmem > lowmem && lowmem & ((1ULL << 30) - 1)) { error_report("Warning: Large machine and max_ram_below_4g(%"PRIu64 ") not a multiple of 1G; possible bad performance.", - pc_machine->max_ram_below_4g); + pcms->max_ram_below_4g); } } if (machine->ram_size >= lowmem) { - above_4g_mem_size = machine->ram_size - lowmem; - below_4g_mem_size = lowmem; + pcms->above_4g_mem_size = machine->ram_size - lowmem; + pcms->below_4g_mem_size = lowmem; } else { - above_4g_mem_size = 0; - below_4g_mem_size = machine->ram_size; + pcms->above_4g_mem_size = 0; + pcms->below_4g_mem_size = machine->ram_size; } - if (xen_enabled() && xen_hvm_init(&below_4g_mem_size, &above_4g_mem_size, - &ram_memory) != 0) { - fprintf(stderr, "xen hardware virtual machine initialisation failed\n"); - exit(1); + if (xen_enabled()) { + xen_hvm_init(pcms, &ram_memory); } - icc_bridge = qdev_create(NULL, TYPE_ICC_BRIDGE); - object_property_add_child(qdev_get_machine(), "icc-bridge", - OBJECT(icc_bridge), NULL); - - pc_cpus_init(machine->cpu_model, icc_bridge); - pc_acpi_init("q35-acpi-dsdt.aml"); + pc_cpus_init(pcms); kvmclock_create(); /* pci enabled */ - if (pci_enabled) { + if (pcmc->pci_enabled) { pci_memory = g_new(MemoryRegion, 1); memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); rom_memory = pci_memory; @@ -151,34 +129,26 @@ static void pc_q35_init(MachineState *machine) rom_memory = get_system_memory(); } - guest_info = pc_guest_info_init(below_4g_mem_size, above_4g_mem_size); - guest_info->isapc_ram_fw = false; - guest_info->has_acpi_build = has_acpi_build; - guest_info->has_reserved_memory = has_reserved_memory; - guest_info->rsdp_in_ram = rsdp_in_ram; - - /* Migration was not supported in 2.0 for Q35, so do not bother - * with this hack (see hw/i386/acpi-build.c). - */ - guest_info->legacy_acpi_table_size = 0; + pc_guest_info_init(pcms); - if (smbios_defaults) { + if (pcmc->smbios_defaults) { /* These values are guest ABI, do not change */ smbios_set_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)", - mc->name, smbios_legacy_mode, smbios_uuid_encoded); + mc->name, pcmc->smbios_legacy_mode, + pcmc->smbios_uuid_encoded, + SMBIOS_ENTRY_POINT_21); } /* allocate ram and load rom/bios */ if (!xen_enabled()) { - pc_memory_init(machine, get_system_memory(), - below_4g_mem_size, above_4g_mem_size, - rom_memory, &ram_memory, guest_info); + pc_memory_init(pcms, get_system_memory(), + rom_memory, &ram_memory); } /* irq lines */ gsi_state = g_malloc0(sizeof(*gsi_state)); - if (kvm_irqchip_in_kernel()) { - kvm_pc_setup_irq_routing(pci_enabled); + if (kvm_ioapic_in_kernel()) { + kvm_pc_setup_irq_routing(pcmc->pci_enabled); gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state, GSI_NUM_PINS); } else { @@ -192,14 +162,14 @@ static void pc_q35_init(MachineState *machine) q35_host->mch.ram_memory = ram_memory; q35_host->mch.pci_address_space = pci_memory; q35_host->mch.system_memory = get_system_memory(); - q35_host->mch.address_space_io = get_system_io(); - q35_host->mch.below_4g_mem_size = below_4g_mem_size; - q35_host->mch.above_4g_mem_size = above_4g_mem_size; - q35_host->mch.guest_info = guest_info; + q35_host->mch.address_space_io = system_io; + q35_host->mch.below_4g_mem_size = pcms->below_4g_mem_size; + q35_host->mch.above_4g_mem_size = pcms->above_4g_mem_size; /* pci */ qdev_init_nofail(DEVICE(q35_host)); phb = PCI_HOST_BRIDGE(q35_host); host_bus = phb->bus; + pcms->bus = phb->bus; /* create ISA bus */ lpc = pci_create_simple_multifunction(host_bus, PCI_DEVFN(ICH9_LPC_DEV, ICH9_LPC_FUNC), true, @@ -207,7 +177,7 @@ static void pc_q35_init(MachineState *machine) object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, TYPE_HOTPLUG_HANDLER, - (Object **)&pc_machine->acpi_dev, + (Object **)&pcms->acpi_dev, object_property_allow_set_link, OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); object_property_set_link(OBJECT(machine), OBJECT(lpc), @@ -224,7 +194,7 @@ static void pc_q35_init(MachineState *machine) /*end early*/ isa_bus_irqs(isa_bus, gsi); - if (kvm_irqchip_in_kernel()) { + if (kvm_pic_in_kernel()) { i8259 = kvm_i8259_init(isa_bus); } else if (xen_enabled()) { i8259 = xen_interrupt_controller_init(); @@ -235,24 +205,23 @@ static void pc_q35_init(MachineState *machine) for (i = 0; i < ISA_NUM_IRQS; i++) { gsi_state->i8259_irq[i] = i8259[i]; } - if (pci_enabled) { + if (pcmc->pci_enabled) { ioapic_init_gsi(gsi_state, "q35"); } - qdev_init_nofail(icc_bridge); pc_register_ferr_irq(gsi[13]); - assert(pc_machine->vmport != ON_OFF_AUTO_MAX); - if (pc_machine->vmport == ON_OFF_AUTO_AUTO) { - pc_machine->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON; + assert(pcms->vmport != ON_OFF_AUTO__MAX); + if (pcms->vmport == ON_OFF_AUTO_AUTO) { + pcms->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON; } /* init basic PC hardware */ pc_basic_device_init(isa_bus, gsi, &rtc_state, !mc->no_floppy, - (pc_machine->vmport != ON_OFF_AUTO_ON), 0xff0104); + (pcms->vmport != ON_OFF_AUTO_ON), 0xff0104); /* connect pm stuff to lpc */ - ich9_lpc_pm_init(lpc, pc_machine_is_smm_enabled(pc_machine), !mc->no_tco); + ich9_lpc_pm_init(lpc, pc_machine_is_smm_enabled(pcms)); /* ahci and SATA device, for q35 1 ahci controller is built-in */ ahci = pci_create_simple_multifunction(host_bus, @@ -276,99 +245,19 @@ static void pc_q35_init(MachineState *machine) 0xb100), 8, NULL, 0); - pc_cmos_init(below_4g_mem_size, above_4g_mem_size, machine->boot_order, - machine, idebus[0], idebus[1], rtc_state); + pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state); /* the rest devices to which pci devfn is automatically assigned */ pc_vga_init(isa_bus, host_bus); pc_nic_init(isa_bus, host_bus); - if (pci_enabled) { + if (pcmc->pci_enabled) { pc_pci_device_init(host_bus); } -} -static void pc_compat_2_3(MachineState *machine) -{ - PCMachineState *pcms = PC_MACHINE(machine); - savevm_skip_section_footers(); - if (kvm_enabled()) { - pcms->smm = ON_OFF_AUTO_OFF; + if (pcms->acpi_nvdimm_state.is_enabled) { + nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io, + pcms->fw_cfg, OBJECT(pcms)); } - global_state_set_optional(); - savevm_skip_configuration(); -} - -static void pc_compat_2_2(MachineState *machine) -{ - pc_compat_2_3(machine); - rsdp_in_ram = false; - x86_cpu_compat_set_features("kvm64", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("kvm32", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Conroe", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Penryn", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Nehalem", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Westmere", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("SandyBridge", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Haswell", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Broadwell", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Opteron_G1", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Opteron_G2", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Opteron_G3", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Opteron_G4", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Opteron_G5", FEAT_1_EDX, 0, CPUID_VME); - x86_cpu_compat_set_features("Haswell", FEAT_1_ECX, 0, CPUID_EXT_F16C); - x86_cpu_compat_set_features("Haswell", FEAT_1_ECX, 0, CPUID_EXT_RDRAND); - x86_cpu_compat_set_features("Broadwell", FEAT_1_ECX, 0, CPUID_EXT_F16C); - x86_cpu_compat_set_features("Broadwell", FEAT_1_ECX, 0, CPUID_EXT_RDRAND); - machine->suppress_vmdesc = true; -} - -static void pc_compat_2_1(MachineState *machine) -{ - PCMachineState *pcms = PC_MACHINE(machine); - - pc_compat_2_2(machine); - pcms->enforce_aligned_dimm = false; - smbios_uuid_encoded = false; - x86_cpu_compat_set_features("coreduo", FEAT_1_ECX, CPUID_EXT_VMX, 0); - x86_cpu_compat_set_features("core2duo", FEAT_1_ECX, CPUID_EXT_VMX, 0); - x86_cpu_compat_kvm_no_autodisable(FEAT_8000_0001_ECX, CPUID_EXT3_SVM); -} - -static void pc_compat_2_0(MachineState *machine) -{ - pc_compat_2_1(machine); - smbios_legacy_mode = true; - has_reserved_memory = false; - pc_set_legacy_acpi_data_size(); -} - -static void pc_compat_1_7(MachineState *machine) -{ - pc_compat_2_0(machine); - smbios_defaults = false; - gigabyte_align = false; - option_rom_has_mr = true; - x86_cpu_compat_kvm_no_autoenable(FEAT_1_ECX, CPUID_EXT_X2APIC); -} - -static void pc_compat_1_6(MachineState *machine) -{ - pc_compat_1_7(machine); - rom_file_has_mr = false; - has_acpi_build = false; -} - -static void pc_compat_1_5(MachineState *machine) -{ - pc_compat_1_6(machine); -} - -static void pc_compat_1_4(MachineState *machine) -{ - pc_compat_1_5(machine); - x86_cpu_compat_set_features("n270", FEAT_1_ECX, 0, CPUID_EXT_MOVBE); - x86_cpu_compat_set_features("Westmere", FEAT_1_ECX, 0, CPUID_EXT_PCLMULQDQ); } #define DEFINE_Q35_MACHINE(suffix, name, compatfn, optionfn) \ @@ -385,108 +274,45 @@ static void pc_compat_1_4(MachineState *machine) static void pc_q35_machine_options(MachineClass *m) { - pc_default_machine_options(m); m->family = "pc_q35"; m->desc = "Standard PC (Q35 + ICH9, 2009)"; m->hot_add_cpu = pc_hot_add_cpu; m->units_per_default_bus = 1; -} - -static void pc_q35_2_4_machine_options(MachineClass *m) -{ - pc_q35_machine_options(m); m->default_machine_opts = "firmware=bios-256k.bin"; m->default_display = "std"; m->no_floppy = 1; - m->no_tco = 0; - m->alias = "q35"; } -DEFINE_Q35_MACHINE(v2_4, "pc-q35-2.4", NULL, - pc_q35_2_4_machine_options); - - -static void pc_q35_2_3_machine_options(MachineClass *m) -{ - pc_q35_2_4_machine_options(m); - m->no_floppy = 0; - m->no_tco = 1; - m->alias = NULL; - SET_MACHINE_COMPAT(m, PC_COMPAT_2_3); -} - -DEFINE_Q35_MACHINE(v2_3, "pc-q35-2.3", pc_compat_2_3, - pc_q35_2_3_machine_options); - - -static void pc_q35_2_2_machine_options(MachineClass *m) -{ - pc_q35_2_3_machine_options(m); - SET_MACHINE_COMPAT(m, PC_COMPAT_2_2); -} - -DEFINE_Q35_MACHINE(v2_2, "pc-q35-2.2", pc_compat_2_2, - pc_q35_2_2_machine_options); - - -static void pc_q35_2_1_machine_options(MachineClass *m) -{ - pc_q35_2_2_machine_options(m); - m->default_display = NULL; - SET_MACHINE_COMPAT(m, PC_COMPAT_2_1); -} - -DEFINE_Q35_MACHINE(v2_1, "pc-q35-2.1", pc_compat_2_1, - pc_q35_2_1_machine_options); - - -static void pc_q35_2_0_machine_options(MachineClass *m) -{ - pc_q35_2_1_machine_options(m); - SET_MACHINE_COMPAT(m, PC_COMPAT_2_0); -} - -DEFINE_Q35_MACHINE(v2_0, "pc-q35-2.0", pc_compat_2_0, - pc_q35_2_0_machine_options); - - -static void pc_q35_1_7_machine_options(MachineClass *m) -{ - pc_q35_2_0_machine_options(m); - m->default_machine_opts = NULL; - SET_MACHINE_COMPAT(m, PC_COMPAT_1_7); -} - -DEFINE_Q35_MACHINE(v1_7, "pc-q35-1.7", pc_compat_1_7, - pc_q35_1_7_machine_options); - - -static void pc_q35_1_6_machine_options(MachineClass *m) +static void pc_q35_2_6_machine_options(MachineClass *m) { pc_q35_machine_options(m); - SET_MACHINE_COMPAT(m, PC_COMPAT_1_6); + m->alias = "q35"; } -DEFINE_Q35_MACHINE(v1_6, "pc-q35-1.6", pc_compat_1_6, - pc_q35_1_6_machine_options); +DEFINE_Q35_MACHINE(v2_6, "pc-q35-2.6", NULL, + pc_q35_2_6_machine_options); - -static void pc_q35_1_5_machine_options(MachineClass *m) +static void pc_q35_2_5_machine_options(MachineClass *m) { - pc_q35_1_6_machine_options(m); - SET_MACHINE_COMPAT(m, PC_COMPAT_1_5); + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pc_q35_2_6_machine_options(m); + m->alias = NULL; + pcmc->save_tsc_khz = false; + m->legacy_fw_cfg_order = 1; + SET_MACHINE_COMPAT(m, PC_COMPAT_2_5); } -DEFINE_Q35_MACHINE(v1_5, "pc-q35-1.5", pc_compat_1_5, - pc_q35_1_5_machine_options); +DEFINE_Q35_MACHINE(v2_5, "pc-q35-2.5", NULL, + pc_q35_2_5_machine_options); - -static void pc_q35_1_4_machine_options(MachineClass *m) +static void pc_q35_2_4_machine_options(MachineClass *m) { - pc_q35_1_5_machine_options(m); - m->hot_add_cpu = NULL; - SET_MACHINE_COMPAT(m, PC_COMPAT_1_4); + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pc_q35_2_5_machine_options(m); + m->hw_version = "2.4.0"; + pcmc->broken_reserved_end = true; + SET_MACHINE_COMPAT(m, PC_COMPAT_2_4); } -DEFINE_Q35_MACHINE(v1_4, "pc-q35-1.4", pc_compat_1_4, - pc_q35_1_4_machine_options); +DEFINE_Q35_MACHINE(v2_4, "pc-q35-2.4", NULL, + pc_q35_2_4_machine_options); diff --git a/qemu/hw/i386/pc_sysfw.c b/qemu/hw/i386/pc_sysfw.c index 662d99768..f915ad0a3 100644 --- a/qemu/hw/i386/pc_sysfw.c +++ b/qemu/hw/i386/pc_sysfw.c @@ -23,6 +23,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "sysemu/block-backend.h" #include "qemu/error-report.h" #include "hw/sysbus.h" @@ -56,7 +58,7 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory, isa_bios_size = MIN(flash_size, 128 * 1024); isa_bios = g_malloc(sizeof(*isa_bios)); memory_region_init_ram(isa_bios, NULL, "isa-bios", isa_bios_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(isa_bios); memory_region_add_subregion_overlap(rom_memory, 0x100000 - isa_bios_size, @@ -193,7 +195,7 @@ static void old_pc_system_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw) goto bios_error; } bios = g_malloc(sizeof(*bios)); - memory_region_init_ram(bios, NULL, "pc.bios", bios_size, &error_abort); + memory_region_init_ram(bios, NULL, "pc.bios", bios_size, &error_fatal); vmstate_register_ram_global(bios); if (!isapc_ram_fw) { memory_region_set_readonly(bios, true); diff --git a/qemu/hw/i386/pci-assign-load-rom.c b/qemu/hw/i386/pci-assign-load-rom.c new file mode 100644 index 000000000..4bbb08c95 --- /dev/null +++ b/qemu/hw/i386/pci-assign-load-rom.c @@ -0,0 +1,85 @@ +/* + * This is splited from hw/i386/kvm/pci-assign.c + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/i386/pc.h" +#include "qemu/error-report.h" +#include "ui/console.h" +#include "hw/loader.h" +#include "monitor/monitor.h" +#include "qemu/range.h" +#include "sysemu/sysemu.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci-assign.h" + +/* + * Scan the assigned devices for the devices that have an option ROM, and then + * load the corresponding ROM data to RAM. If an error occurs while loading an + * option ROM, we just ignore that option ROM and continue with the next one. + */ +void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner, + int *size, unsigned int domain, + unsigned int bus, unsigned int slot, + unsigned int function) +{ + char name[32], rom_file[64]; + FILE *fp; + uint8_t val; + struct stat st; + void *ptr = NULL; + + /* If loading ROM from file, pci handles it */ + if (dev->romfile || !dev->rom_bar) { + return NULL; + } + + snprintf(rom_file, sizeof(rom_file), + "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/rom", + domain, bus, slot, function); + + if (stat(rom_file, &st)) { + return NULL; + } + + /* Write "1" to the ROM file to enable it */ + fp = fopen(rom_file, "r+"); + if (fp == NULL) { + error_report("pci-assign: Cannot open %s: %s", rom_file, strerror(errno)); + return NULL; + } + val = 1; + if (fwrite(&val, 1, 1, fp) != 1) { + goto close_rom; + } + fseek(fp, 0, SEEK_SET); + + snprintf(name, sizeof(name), "%s.rom", object_get_typename(owner)); + memory_region_init_ram(&dev->rom, owner, name, st.st_size, &error_abort); + vmstate_register_ram(&dev->rom, &dev->qdev); + ptr = memory_region_get_ram_ptr(&dev->rom); + memset(ptr, 0xff, st.st_size); + + if (!fread(ptr, 1, st.st_size, fp)) { + error_report("pci-assign: Cannot read from host %s", rom_file); + error_printf("Device option ROM contents are probably invalid " + "(check dmesg).\nSkip option ROM probe with rombar=0, " + "or load from file with romfile=\n"); + goto close_rom; + } + + pci_register_bar(dev, PCI_ROM_SLOT, 0, &dev->rom); + dev->has_rom = true; + *size = st.st_size; +close_rom: + /* Write "0" to disable ROM */ + fseek(fp, 0, SEEK_SET); + val = 0; + if (!fwrite(&val, 1, 1, fp)) { + DEBUG("%s\n", "Failed to disable pci-sysfs rom file"); + } + fclose(fp); + + return ptr; +} diff --git a/qemu/hw/i386/q35-acpi-dsdt.dsl b/qemu/hw/i386/q35-acpi-dsdt.dsl deleted file mode 100644 index 16eaca3fa..000000000 --- a/qemu/hw/i386/q35-acpi-dsdt.dsl +++ /dev/null @@ -1,435 +0,0 @@ -/* - * Bochs/QEMU ACPI DSDT ASL definition - * - * Copyright (c) 2006 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 version 2 as published by the Free Software Foundation. - * - * 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/* - * Copyright (c) 2010 Isaku Yamahata - * yamahata at valinux co jp - * Based on acpi-dsdt.dsl, but heavily modified for q35 chipset. - */ - -ACPI_EXTRACT_ALL_CODE Q35AcpiDsdtAmlCode - -DefinitionBlock ( - "q35-acpi-dsdt.aml",// Output Filename - "DSDT", // Signature - 0x01, // DSDT Compliance Revision - "BXPC", // OEMID - "BXDSDT", // TABLE ID - 0x2 // OEM Revision - ) -{ - -#include "acpi-dsdt-dbug.dsl" - - Scope(\_SB) { - OperationRegion(PCST, SystemIO, 0xae00, 0x0c) - OperationRegion(PCSB, SystemIO, 0xae0c, 0x01) - Field(PCSB, AnyAcc, NoLock, WriteAsZeros) { - PCIB, 8, - } - } - - -/**************************************************************** - * PCI Bus definition - ****************************************************************/ - Scope(\_SB) { - Device(PCI0) { - Name(_HID, EisaId("PNP0A08")) - Name(_CID, EisaId("PNP0A03")) - Name(_ADR, 0x00) - Name(_UID, 1) - - External(ISA, DeviceObj) - - // _OSC: based on sample of ACPI3.0b spec - Name(SUPP, 0) // PCI _OSC Support Field value - Name(CTRL, 0) // PCI _OSC Control Field value - Method(_OSC, 4) { - // Create DWORD-addressable fields from the Capabilities Buffer - CreateDWordField(Arg3, 0, CDW1) - - // Check for proper UUID - If (LEqual(Arg0, ToUUID("33DB4D5B-1FF7-401C-9657-7441C03DD766"))) { - // Create DWORD-addressable fields from the Capabilities Buffer - CreateDWordField(Arg3, 4, CDW2) - CreateDWordField(Arg3, 8, CDW3) - - // Save Capabilities DWORD2 & 3 - Store(CDW2, SUPP) - Store(CDW3, CTRL) - - // Always allow native PME, AER (no dependencies) - // Never allow SHPC (no SHPC controller in this system) - And(CTRL, 0x1D, CTRL) - -#if 0 // For now, nothing to do - If (Not(And(CDW1, 1))) { // Query flag clear? - // Disable GPEs for features granted native control. - If (And(CTRL, 0x01)) { // Hot plug control granted? - Store(0, HPCE) // clear the hot plug SCI enable bit - Store(1, HPCS) // clear the hot plug SCI status bit - } - If (And(CTRL, 0x04)) { // PME control granted? - Store(0, PMCE) // clear the PME SCI enable bit - Store(1, PMCS) // clear the PME SCI status bit - } - If (And(CTRL, 0x10)) { // OS restoring PCI Express cap structure? - // Set status to not restore PCI Express cap structure - // upon resume from S3 - Store(1, S3CR) - } - } -#endif - If (LNotEqual(Arg1, One)) { - // Unknown revision - Or(CDW1, 0x08, CDW1) - } - If (LNotEqual(CDW3, CTRL)) { - // Capabilities bits were masked - Or(CDW1, 0x10, CDW1) - } - // Update DWORD3 in the buffer - Store(CTRL, CDW3) - } Else { - Or(CDW1, 4, CDW1) // Unrecognized UUID - } - Return (Arg3) - } - } - } - -#include "acpi-dsdt-hpet.dsl" - - -/**************************************************************** - * LPC ISA bridge - ****************************************************************/ - - Scope(\_SB.PCI0) { - /* PCI D31:f0 LPC ISA bridge */ - Device(ISA) { - Name (_ADR, 0x001F0000) // _ADR: Address - - /* ICH9 PCI to ISA irq remapping */ - OperationRegion(PIRQ, PCI_Config, 0x60, 0x0C) - - OperationRegion(LPCD, PCI_Config, 0x80, 0x2) - Field(LPCD, AnyAcc, NoLock, Preserve) { - COMA, 3, - , 1, - COMB, 3, - - Offset(0x01), - LPTD, 2, - , 2, - FDCD, 2 - } - OperationRegion(LPCE, PCI_Config, 0x82, 0x2) - Field(LPCE, AnyAcc, NoLock, Preserve) { - CAEN, 1, - CBEN, 1, - LPEN, 1, - FDEN, 1 - } - } - } - -#include "acpi-dsdt-isa.dsl" - - -/**************************************************************** - * PCI IRQs - ****************************************************************/ - - /* Zero => PIC mode, One => APIC Mode */ - Name(\PICF, Zero) - Method(\_PIC, 1, NotSerialized) { - Store(Arg0, \PICF) - } - - Scope(\_SB) { - Scope(PCI0) { -#define prt_slot_lnk(nr, lnk0, lnk1, lnk2, lnk3) \ - Package() { nr##ffff, 0, lnk0, 0 }, \ - Package() { nr##ffff, 1, lnk1, 0 }, \ - Package() { nr##ffff, 2, lnk2, 0 }, \ - Package() { nr##ffff, 3, lnk3, 0 } - -#define prt_slot_lnkA(nr) prt_slot_lnk(nr, LNKA, LNKB, LNKC, LNKD) -#define prt_slot_lnkB(nr) prt_slot_lnk(nr, LNKB, LNKC, LNKD, LNKA) -#define prt_slot_lnkC(nr) prt_slot_lnk(nr, LNKC, LNKD, LNKA, LNKB) -#define prt_slot_lnkD(nr) prt_slot_lnk(nr, LNKD, LNKA, LNKB, LNKC) - -#define prt_slot_lnkE(nr) prt_slot_lnk(nr, LNKE, LNKF, LNKG, LNKH) -#define prt_slot_lnkF(nr) prt_slot_lnk(nr, LNKF, LNKG, LNKH, LNKE) -#define prt_slot_lnkG(nr) prt_slot_lnk(nr, LNKG, LNKH, LNKE, LNKF) -#define prt_slot_lnkH(nr) prt_slot_lnk(nr, LNKH, LNKE, LNKF, LNKG) - - Name(PRTP, package() { - prt_slot_lnkE(0x0000), - prt_slot_lnkF(0x0001), - prt_slot_lnkG(0x0002), - prt_slot_lnkH(0x0003), - prt_slot_lnkE(0x0004), - prt_slot_lnkF(0x0005), - prt_slot_lnkG(0x0006), - prt_slot_lnkH(0x0007), - prt_slot_lnkE(0x0008), - prt_slot_lnkF(0x0009), - prt_slot_lnkG(0x000a), - prt_slot_lnkH(0x000b), - prt_slot_lnkE(0x000c), - prt_slot_lnkF(0x000d), - prt_slot_lnkG(0x000e), - prt_slot_lnkH(0x000f), - prt_slot_lnkE(0x0010), - prt_slot_lnkF(0x0011), - prt_slot_lnkG(0x0012), - prt_slot_lnkH(0x0013), - prt_slot_lnkE(0x0014), - prt_slot_lnkF(0x0015), - prt_slot_lnkG(0x0016), - prt_slot_lnkH(0x0017), - prt_slot_lnkE(0x0018), - - /* INTA -> PIRQA for slot 25 - 31 - see the default value of DIR */ - prt_slot_lnkA(0x0019), - prt_slot_lnkA(0x001a), - prt_slot_lnkA(0x001b), - prt_slot_lnkA(0x001c), - prt_slot_lnkA(0x001d), - - /* PCIe->PCI bridge. use PIRQ[E-H] */ - prt_slot_lnkE(0x001e), - - prt_slot_lnkA(0x001f) - }) - -#define prt_slot_gsi(nr, gsi0, gsi1, gsi2, gsi3) \ - Package() { nr##ffff, 0, gsi0, 0 }, \ - Package() { nr##ffff, 1, gsi1, 0 }, \ - Package() { nr##ffff, 2, gsi2, 0 }, \ - Package() { nr##ffff, 3, gsi3, 0 } - -#define prt_slot_gsiA(nr) prt_slot_gsi(nr, GSIA, GSIB, GSIC, GSID) -#define prt_slot_gsiB(nr) prt_slot_gsi(nr, GSIB, GSIC, GSID, GSIA) -#define prt_slot_gsiC(nr) prt_slot_gsi(nr, GSIC, GSID, GSIA, GSIB) -#define prt_slot_gsiD(nr) prt_slot_gsi(nr, GSID, GSIA, GSIB, GSIC) - -#define prt_slot_gsiE(nr) prt_slot_gsi(nr, GSIE, GSIF, GSIG, GSIH) -#define prt_slot_gsiF(nr) prt_slot_gsi(nr, GSIF, GSIG, GSIH, GSIE) -#define prt_slot_gsiG(nr) prt_slot_gsi(nr, GSIG, GSIH, GSIE, GSIF) -#define prt_slot_gsiH(nr) prt_slot_gsi(nr, GSIH, GSIE, GSIF, GSIG) - - Name(PRTA, package() { - prt_slot_gsiE(0x0000), - prt_slot_gsiF(0x0001), - prt_slot_gsiG(0x0002), - prt_slot_gsiH(0x0003), - prt_slot_gsiE(0x0004), - prt_slot_gsiF(0x0005), - prt_slot_gsiG(0x0006), - prt_slot_gsiH(0x0007), - prt_slot_gsiE(0x0008), - prt_slot_gsiF(0x0009), - prt_slot_gsiG(0x000a), - prt_slot_gsiH(0x000b), - prt_slot_gsiE(0x000c), - prt_slot_gsiF(0x000d), - prt_slot_gsiG(0x000e), - prt_slot_gsiH(0x000f), - prt_slot_gsiE(0x0010), - prt_slot_gsiF(0x0011), - prt_slot_gsiG(0x0012), - prt_slot_gsiH(0x0013), - prt_slot_gsiE(0x0014), - prt_slot_gsiF(0x0015), - prt_slot_gsiG(0x0016), - prt_slot_gsiH(0x0017), - prt_slot_gsiE(0x0018), - - /* INTA -> PIRQA for slot 25 - 31, but 30 - see the default value of DIR */ - prt_slot_gsiA(0x0019), - prt_slot_gsiA(0x001a), - prt_slot_gsiA(0x001b), - prt_slot_gsiA(0x001c), - prt_slot_gsiA(0x001d), - - /* PCIe->PCI bridge. use PIRQ[E-H] */ - prt_slot_gsiE(0x001e), - - prt_slot_gsiA(0x001f) - }) - - Method(_PRT, 0, NotSerialized) { - /* PCI IRQ routing table, example from ACPI 2.0a specification, - section 6.2.8.1 */ - /* Note: we provide the same info as the PCI routing - table of the Bochs BIOS */ - If (LEqual(\PICF, Zero)) { - Return (PRTP) - } Else { - Return (PRTA) - } - } - } - - Field(PCI0.ISA.PIRQ, ByteAcc, NoLock, Preserve) { - PRQA, 8, - PRQB, 8, - PRQC, 8, - PRQD, 8, - - Offset(0x08), - PRQE, 8, - PRQF, 8, - PRQG, 8, - PRQH, 8 - } - - Method(IQST, 1, NotSerialized) { - // _STA method - get status - If (And(0x80, Arg0)) { - Return (0x09) - } - Return (0x0B) - } - Method(IQCR, 1, Serialized) { - // _CRS method - get current settings - Name(PRR0, ResourceTemplate() { - Interrupt(, Level, ActiveHigh, Shared) { 0 } - }) - CreateDWordField(PRR0, 0x05, PRRI) - Store(And(Arg0, 0x0F), PRRI) - Return (PRR0) - } - -#define define_link(link, uid, reg) \ - Device(link) { \ - Name(_HID, EISAID("PNP0C0F")) \ - Name(_UID, uid) \ - Name(_PRS, ResourceTemplate() { \ - Interrupt(, Level, ActiveHigh, Shared) { \ - 5, 10, 11 \ - } \ - }) \ - Method(_STA, 0, NotSerialized) { \ - Return (IQST(reg)) \ - } \ - Method(_DIS, 0, NotSerialized) { \ - Or(reg, 0x80, reg) \ - } \ - Method(_CRS, 0, NotSerialized) { \ - Return (IQCR(reg)) \ - } \ - Method(_SRS, 1, NotSerialized) { \ - CreateDWordField(Arg0, 0x05, PRRI) \ - Store(PRRI, reg) \ - } \ - } - - define_link(LNKA, 0, PRQA) - define_link(LNKB, 1, PRQB) - define_link(LNKC, 2, PRQC) - define_link(LNKD, 3, PRQD) - define_link(LNKE, 4, PRQE) - define_link(LNKF, 5, PRQF) - define_link(LNKG, 6, PRQG) - define_link(LNKH, 7, PRQH) - -#define define_gsi_link(link, uid, gsi) \ - Device(link) { \ - Name(_HID, EISAID("PNP0C0F")) \ - Name(_UID, uid) \ - Name(_PRS, ResourceTemplate() { \ - Interrupt(, Level, ActiveHigh, Shared) { \ - gsi \ - } \ - }) \ - Name(_CRS, ResourceTemplate() { \ - Interrupt(, Level, ActiveHigh, Shared) { \ - gsi \ - } \ - }) \ - Method(_SRS, 1, NotSerialized) { \ - } \ - } - - define_gsi_link(GSIA, 0, 0x10) - define_gsi_link(GSIB, 0, 0x11) - define_gsi_link(GSIC, 0, 0x12) - define_gsi_link(GSID, 0, 0x13) - define_gsi_link(GSIE, 0, 0x14) - define_gsi_link(GSIF, 0, 0x15) - define_gsi_link(GSIG, 0, 0x16) - define_gsi_link(GSIH, 0, 0x17) - } - -#include "hw/acpi/pc-hotplug.h" -#define CPU_STATUS_BASE ICH9_CPU_HOTPLUG_IO_BASE -#include "acpi-dsdt-cpu-hotplug.dsl" -#include "acpi-dsdt-mem-hotplug.dsl" - - -/**************************************************************** - * General purpose events - ****************************************************************/ - Scope(\_GPE) { - Name(_HID, "ACPI0006") - - Method(_L00) { - } - Method(_L01) { - } - Method(_E02) { - // CPU hotplug event - \_SB.PRSC() - } - Method(_E03) { - // Memory hotplug event - \_SB.PCI0.MEMORY_HOTPLUG_DEVICE.MEMORY_SLOT_SCAN_METHOD() - } - Method(_L04) { - } - Method(_L05) { - } - Method(_L06) { - } - Method(_L07) { - } - Method(_L08) { - } - Method(_L09) { - } - Method(_L0A) { - } - Method(_L0B) { - } - Method(_L0C) { - } - Method(_L0D) { - } - Method(_L0E) { - } - Method(_L0F) { - } - } -} diff --git a/qemu/hw/i386/q35-acpi-dsdt.hex.generated b/qemu/hw/i386/q35-acpi-dsdt.hex.generated deleted file mode 100644 index ed9a2cc8e..000000000 --- a/qemu/hw/i386/q35-acpi-dsdt.hex.generated +++ /dev/null @@ -1,7610 +0,0 @@ -static unsigned char Q35AcpiDsdtAmlCode[] = { -0x44, -0x53, -0x44, -0x54, -0xb8, -0x1d, -0x0, -0x0, -0x1, -0x35, -0x42, -0x58, -0x50, -0x43, -0x0, -0x0, -0x42, -0x58, -0x44, -0x53, -0x44, -0x54, -0x0, -0x0, -0x2, -0x0, -0x0, -0x0, -0x49, -0x4e, -0x54, -0x4c, -0x7, -0x11, -0x14, -0x20, -0x10, -0x49, -0x4, -0x5c, -0x0, -0x5b, -0x80, -0x44, -0x42, -0x47, -0x5f, -0x1, -0xb, -0x2, -0x4, -0x1, -0x5b, -0x81, -0xb, -0x44, -0x42, -0x47, -0x5f, -0x1, -0x44, -0x42, -0x47, -0x42, -0x8, -0x14, -0x2c, -0x44, -0x42, -0x55, -0x47, -0x1, -0x98, -0x68, -0x60, -0x96, -0x60, -0x60, -0x74, -0x87, -0x60, -0x1, -0x61, -0x70, -0x0, -0x62, -0xa2, -0x10, -0x95, -0x62, -0x61, -0x70, -0x83, -0x88, -0x60, -0x62, -0x0, -0x44, -0x42, -0x47, -0x42, -0x75, -0x62, -0x70, -0xa, -0xa, -0x44, -0x42, -0x47, -0x42, -0x10, -0x29, -0x5f, -0x53, -0x42, -0x5f, -0x5b, -0x80, -0x50, -0x43, -0x53, -0x54, -0x1, -0xb, -0x0, -0xae, -0xa, -0xc, -0x5b, -0x80, -0x50, -0x43, -0x53, -0x42, -0x1, -0xb, -0xc, -0xae, -0x1, -0x5b, -0x81, -0xb, -0x50, -0x43, -0x53, -0x42, -0x40, -0x50, -0x43, -0x49, -0x42, -0x8, -0x10, -0x4f, -0xc, -0x5f, -0x53, -0x42, -0x5f, -0x5b, -0x82, -0x47, -0xc, -0x50, -0x43, -0x49, -0x30, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xa, -0x8, -0x8, -0x5f, -0x43, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xa, -0x3, -0x8, -0x5f, -0x41, -0x44, -0x52, -0x0, -0x8, -0x5f, -0x55, -0x49, -0x44, -0x1, -0x8, -0x53, -0x55, -0x50, -0x50, -0x0, -0x8, -0x43, -0x54, -0x52, -0x4c, -0x0, -0x14, -0x44, -0x9, -0x5f, -0x4f, -0x53, -0x43, -0x4, -0x8a, -0x6b, -0x0, -0x43, -0x44, -0x57, -0x31, -0xa0, -0x46, -0x7, -0x93, -0x68, -0x11, -0x13, -0xa, -0x10, -0x5b, -0x4d, -0xdb, -0x33, -0xf7, -0x1f, -0x1c, -0x40, -0x96, -0x57, -0x74, -0x41, -0xc0, -0x3d, -0xd7, -0x66, -0x8a, -0x6b, -0xa, -0x4, -0x43, -0x44, -0x57, -0x32, -0x8a, -0x6b, -0xa, -0x8, -0x43, -0x44, -0x57, -0x33, -0x70, -0x43, -0x44, -0x57, -0x32, -0x53, -0x55, -0x50, -0x50, -0x70, -0x43, -0x44, -0x57, -0x33, -0x43, -0x54, -0x52, -0x4c, -0x7b, -0x43, -0x54, -0x52, -0x4c, -0xa, -0x1d, -0x43, -0x54, -0x52, -0x4c, -0xa0, -0x10, -0x92, -0x93, -0x69, -0x1, -0x7d, -0x43, -0x44, -0x57, -0x31, -0xa, -0x8, -0x43, -0x44, -0x57, -0x31, -0xa0, -0x16, -0x92, -0x93, -0x43, -0x44, -0x57, -0x33, -0x43, -0x54, -0x52, -0x4c, -0x7d, -0x43, -0x44, -0x57, -0x31, -0xa, -0x10, -0x43, -0x44, -0x57, -0x31, -0x70, -0x43, -0x54, -0x52, -0x4c, -0x43, -0x44, -0x57, -0x33, -0xa1, -0xc, -0x7d, -0x43, -0x44, -0x57, -0x31, -0xa, -0x4, -0x43, -0x44, -0x57, -0x31, -0xa4, -0x6b, -0x10, -0x4d, -0x8, -0x5f, -0x53, -0x42, -0x5f, -0x5b, -0x82, -0x45, -0x8, -0x48, -0x50, -0x45, -0x54, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0x1, -0x3, -0x8, -0x5f, -0x55, -0x49, -0x44, -0x0, -0x5b, -0x80, -0x48, -0x50, -0x54, -0x4d, -0x0, -0xc, -0x0, -0x0, -0xd0, -0xfe, -0xb, -0x0, -0x4, -0x5b, -0x81, -0x10, -0x48, -0x50, -0x54, -0x4d, -0x13, -0x56, -0x45, -0x4e, -0x44, -0x20, -0x50, -0x52, -0x44, -0x5f, -0x20, -0x14, -0x36, -0x5f, -0x53, -0x54, -0x41, -0x0, -0x70, -0x56, -0x45, -0x4e, -0x44, -0x60, -0x70, -0x50, -0x52, -0x44, -0x5f, -0x61, -0x7a, -0x60, -0xa, -0x10, -0x60, -0xa0, -0xc, -0x91, -0x93, -0x60, -0x0, -0x93, -0x60, -0xb, -0xff, -0xff, -0xa4, -0x0, -0xa0, -0xe, -0x91, -0x93, -0x61, -0x0, -0x94, -0x61, -0xc, -0x0, -0xe1, -0xf5, -0x5, -0xa4, -0x0, -0xa4, -0xa, -0xf, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x11, -0xa, -0xe, -0x86, -0x9, -0x0, -0x0, -0x0, -0x0, -0xd0, -0xfe, -0x0, -0x4, -0x0, -0x0, -0x79, -0x0, -0x10, -0x4c, -0x7, -0x2e, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x5b, -0x82, -0x4f, -0x6, -0x49, -0x53, -0x41, -0x5f, -0x8, -0x5f, -0x41, -0x44, -0x52, -0xc, -0x0, -0x0, -0x1f, -0x0, -0x5b, -0x80, -0x50, -0x49, -0x52, -0x51, -0x2, -0xa, -0x60, -0xa, -0xc, -0x5b, -0x80, -0x4c, -0x50, -0x43, -0x44, -0x2, -0xa, -0x80, -0xa, -0x2, -0x5b, -0x81, -0x20, -0x4c, -0x50, -0x43, -0x44, -0x0, -0x43, -0x4f, -0x4d, -0x41, -0x3, -0x0, -0x1, -0x43, -0x4f, -0x4d, -0x42, -0x3, -0x0, -0x1, -0x4c, -0x50, -0x54, -0x44, -0x2, -0x0, -0x2, -0x46, -0x44, -0x43, -0x44, -0x2, -0x5b, -0x80, -0x4c, -0x50, -0x43, -0x45, -0x2, -0xa, -0x82, -0xa, -0x2, -0x5b, -0x81, -0x1a, -0x4c, -0x50, -0x43, -0x45, -0x0, -0x43, -0x41, -0x45, -0x4e, -0x1, -0x43, -0x42, -0x45, -0x4e, -0x1, -0x4c, -0x50, -0x45, -0x4e, -0x1, -0x46, -0x44, -0x45, -0x4e, -0x1, -0x10, -0x4c, -0x1b, -0x2f, -0x3, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x49, -0x53, -0x41, -0x5f, -0x5b, -0x82, -0x2d, -0x52, -0x54, -0x43, -0x5f, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xb, -0x0, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x18, -0xa, -0x15, -0x47, -0x1, -0x70, -0x0, -0x70, -0x0, -0x10, -0x2, -0x22, -0x0, -0x1, -0x47, -0x1, -0x72, -0x0, -0x72, -0x0, -0x2, -0x6, -0x79, -0x0, -0x5b, -0x82, -0x37, -0x4b, -0x42, -0x44, -0x5f, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0x3, -0x3, -0x14, -0x9, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0xa, -0xf, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x18, -0xa, -0x15, -0x47, -0x1, -0x60, -0x0, -0x60, -0x0, -0x1, -0x1, -0x47, -0x1, -0x64, -0x0, -0x64, -0x0, -0x1, -0x1, -0x22, -0x2, -0x0, -0x79, -0x0, -0x5b, -0x82, -0x27, -0x4d, -0x4f, -0x55, -0x5f, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xf, -0x13, -0x14, -0x9, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0xa, -0xf, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x8, -0xa, -0x5, -0x22, -0x0, -0x10, -0x79, -0x0, -0x5b, -0x82, -0x4a, -0x4, -0x46, -0x44, -0x43, -0x30, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0x7, -0x0, -0x14, -0x18, -0x5f, -0x53, -0x54, -0x41, -0x0, -0x70, -0x46, -0x44, -0x45, -0x4e, -0x60, -0xa0, -0x6, -0x93, -0x60, -0x0, -0xa4, -0x0, -0xa1, -0x4, -0xa4, -0xa, -0xf, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x1b, -0xa, -0x18, -0x47, -0x1, -0xf2, -0x3, -0xf2, -0x3, -0x0, -0x4, -0x47, -0x1, -0xf7, -0x3, -0xf7, -0x3, -0x0, -0x1, -0x22, -0x40, -0x0, -0x2a, -0x4, -0x0, -0x79, -0x0, -0x5b, -0x82, -0x3e, -0x4c, -0x50, -0x54, -0x5f, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0x4, -0x0, -0x14, -0x18, -0x5f, -0x53, -0x54, -0x41, -0x0, -0x70, -0x4c, -0x50, -0x45, -0x4e, -0x60, -0xa0, -0x6, -0x93, -0x60, -0x0, -0xa4, -0x0, -0xa1, -0x4, -0xa4, -0xa, -0xf, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x10, -0xa, -0xd, -0x47, -0x1, -0x78, -0x3, -0x78, -0x3, -0x8, -0x8, -0x22, -0x80, -0x0, -0x79, -0x0, -0x5b, -0x82, -0x45, -0x4, -0x43, -0x4f, -0x4d, -0x31, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0x5, -0x1, -0x8, -0x5f, -0x55, -0x49, -0x44, -0x1, -0x14, -0x18, -0x5f, -0x53, -0x54, -0x41, -0x0, -0x70, -0x43, -0x41, -0x45, -0x4e, -0x60, -0xa0, -0x6, -0x93, -0x60, -0x0, -0xa4, -0x0, -0xa1, -0x4, -0xa4, -0xa, -0xf, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x10, -0xa, -0xd, -0x47, -0x1, -0xf8, -0x3, -0xf8, -0x3, -0x0, -0x8, -0x22, -0x10, -0x0, -0x79, -0x0, -0x5b, -0x82, -0x46, -0x4, -0x43, -0x4f, -0x4d, -0x32, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0x5, -0x1, -0x8, -0x5f, -0x55, -0x49, -0x44, -0xa, -0x2, -0x14, -0x18, -0x5f, -0x53, -0x54, -0x41, -0x0, -0x70, -0x43, -0x42, -0x45, -0x4e, -0x60, -0xa0, -0x6, -0x93, -0x60, -0x0, -0xa4, -0x0, -0xa1, -0x4, -0xa4, -0xa, -0xf, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x10, -0xa, -0xd, -0x47, -0x1, -0xf8, -0x2, -0xf8, -0x2, -0x0, -0x8, -0x22, -0x8, -0x0, -0x79, -0x0, -0x8, -0x50, -0x49, -0x43, -0x46, -0x0, -0x14, -0xc, -0x5f, -0x50, -0x49, -0x43, -0x1, -0x70, -0x68, -0x50, -0x49, -0x43, -0x46, -0x10, -0x8e, -0x55, -0x1, -0x5f, -0x53, -0x42, -0x5f, -0x10, -0x43, -0xea, -0x50, -0x43, -0x49, -0x30, -0x8, -0x50, -0x52, -0x54, -0x50, -0x12, -0x4b, -0x73, -0x80, -0x12, -0xb, -0x4, -0xb, -0xff, -0xff, -0x0, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xb, -0x4, -0xb, -0xff, -0xff, -0x1, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xc, -0x4, -0xb, -0xff, -0xff, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xc, -0x4, -0xb, -0xff, -0xff, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x2, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x2, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x2, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x2, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x3, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x3, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x3, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x3, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x4, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x4, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x4, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x4, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x5, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x5, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x5, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x5, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x6, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x6, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x6, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x6, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x7, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x7, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x7, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x7, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x8, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x8, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x8, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x8, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x9, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x9, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x9, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x9, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xa, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xa, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xa, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xa, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xb, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xb, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xb, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xb, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xc, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xc, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xc, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xc, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xd, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xd, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xd, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xd, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xe, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xe, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xe, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xe, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xf, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xf, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xf, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xf, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x10, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x10, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x10, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x10, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x11, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x11, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x11, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x11, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x12, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x12, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x12, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x12, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x13, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x13, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x13, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x13, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x14, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x14, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x14, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x14, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x15, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x15, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x15, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x15, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x16, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x16, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x16, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x16, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x17, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x17, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x17, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x17, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x18, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x18, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x18, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x18, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x19, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x41, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x19, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x42, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x19, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x43, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x19, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x44, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1a, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x41, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1a, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x42, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1a, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x43, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1a, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x44, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1b, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x41, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1b, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x42, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1b, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x43, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1b, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x44, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1c, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x41, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1c, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x42, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1c, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x43, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1c, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x44, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1d, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x41, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1d, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x42, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1d, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x43, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1d, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x44, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1e, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1e, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1e, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1e, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1f, -0x0, -0x0, -0x4c, -0x4e, -0x4b, -0x41, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1f, -0x0, -0x1, -0x4c, -0x4e, -0x4b, -0x42, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1f, -0x0, -0xa, -0x2, -0x4c, -0x4e, -0x4b, -0x43, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1f, -0x0, -0xa, -0x3, -0x4c, -0x4e, -0x4b, -0x44, -0x0, -0x8, -0x50, -0x52, -0x54, -0x41, -0x12, -0x4b, -0x73, -0x80, -0x12, -0xb, -0x4, -0xb, -0xff, -0xff, -0x0, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xb, -0x4, -0xb, -0xff, -0xff, -0x1, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xc, -0x4, -0xb, -0xff, -0xff, -0xa, -0x2, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xc, -0x4, -0xb, -0xff, -0xff, -0xa, -0x3, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1, -0x0, -0x0, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1, -0x0, -0x1, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x2, -0x0, -0x0, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x2, -0x0, -0x1, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x2, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x2, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x3, -0x0, -0x0, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x3, -0x0, -0x1, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x3, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x3, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x4, -0x0, -0x0, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x4, -0x0, -0x1, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x4, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x4, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x5, -0x0, -0x0, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x5, -0x0, -0x1, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x5, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x5, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x6, -0x0, -0x0, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x6, -0x0, -0x1, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x6, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x6, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x7, -0x0, -0x0, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x7, -0x0, -0x1, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x7, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x7, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x8, -0x0, -0x0, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x8, -0x0, -0x1, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x8, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x8, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x9, -0x0, -0x0, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x9, -0x0, -0x1, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x9, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x9, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xa, -0x0, -0x0, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xa, -0x0, -0x1, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xa, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xa, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xb, -0x0, -0x0, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xb, -0x0, -0x1, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xb, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xb, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xc, -0x0, -0x0, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xc, -0x0, -0x1, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xc, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xc, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xd, -0x0, -0x0, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xd, -0x0, -0x1, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xd, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xd, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xe, -0x0, -0x0, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xe, -0x0, -0x1, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xe, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xe, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xf, -0x0, -0x0, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0xf, -0x0, -0x1, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xf, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0xf, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x10, -0x0, -0x0, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x10, -0x0, -0x1, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x10, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x10, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x11, -0x0, -0x0, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x11, -0x0, -0x1, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x11, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x11, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x12, -0x0, -0x0, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x12, -0x0, -0x1, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x12, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x12, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x13, -0x0, -0x0, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x13, -0x0, -0x1, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x13, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x13, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x14, -0x0, -0x0, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x14, -0x0, -0x1, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x14, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x14, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x15, -0x0, -0x0, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x15, -0x0, -0x1, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x15, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x15, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x16, -0x0, -0x0, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x16, -0x0, -0x1, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x16, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x16, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x17, -0x0, -0x0, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x17, -0x0, -0x1, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x17, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x17, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x18, -0x0, -0x0, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x18, -0x0, -0x1, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x18, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x18, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x19, -0x0, -0x0, -0x47, -0x53, -0x49, -0x41, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x19, -0x0, -0x1, -0x47, -0x53, -0x49, -0x42, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x19, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x43, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x19, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x44, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1a, -0x0, -0x0, -0x47, -0x53, -0x49, -0x41, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1a, -0x0, -0x1, -0x47, -0x53, -0x49, -0x42, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1a, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x43, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1a, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x44, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1b, -0x0, -0x0, -0x47, -0x53, -0x49, -0x41, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1b, -0x0, -0x1, -0x47, -0x53, -0x49, -0x42, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1b, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x43, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1b, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x44, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1c, -0x0, -0x0, -0x47, -0x53, -0x49, -0x41, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1c, -0x0, -0x1, -0x47, -0x53, -0x49, -0x42, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1c, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x43, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1c, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x44, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1d, -0x0, -0x0, -0x47, -0x53, -0x49, -0x41, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1d, -0x0, -0x1, -0x47, -0x53, -0x49, -0x42, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1d, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x43, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1d, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x44, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1e, -0x0, -0x0, -0x47, -0x53, -0x49, -0x45, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1e, -0x0, -0x1, -0x47, -0x53, -0x49, -0x46, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1e, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x47, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1e, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x48, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1f, -0x0, -0x0, -0x47, -0x53, -0x49, -0x41, -0x0, -0x12, -0xd, -0x4, -0xc, -0xff, -0xff, -0x1f, -0x0, -0x1, -0x47, -0x53, -0x49, -0x42, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1f, -0x0, -0xa, -0x2, -0x47, -0x53, -0x49, -0x43, -0x0, -0x12, -0xe, -0x4, -0xc, -0xff, -0xff, -0x1f, -0x0, -0xa, -0x3, -0x47, -0x53, -0x49, -0x44, -0x0, -0x14, -0x1a, -0x5f, -0x50, -0x52, -0x54, -0x0, -0xa0, -0xc, -0x93, -0x50, -0x49, -0x43, -0x46, -0x0, -0xa4, -0x50, -0x52, -0x54, -0x50, -0xa1, -0x6, -0xa4, -0x50, -0x52, -0x54, -0x41, -0x5b, -0x81, -0x3a, -0x2f, -0x3, -0x50, -0x43, -0x49, -0x30, -0x49, -0x53, -0x41, -0x5f, -0x50, -0x49, -0x52, -0x51, -0x1, -0x50, -0x52, -0x51, -0x41, -0x8, -0x50, -0x52, -0x51, -0x42, -0x8, -0x50, -0x52, -0x51, -0x43, -0x8, -0x50, -0x52, -0x51, -0x44, -0x8, -0x0, -0x20, -0x50, -0x52, -0x51, -0x45, -0x8, -0x50, -0x52, -0x51, -0x46, -0x8, -0x50, -0x52, -0x51, -0x47, -0x8, -0x50, -0x52, -0x51, -0x48, -0x8, -0x14, -0x13, -0x49, -0x51, -0x53, -0x54, -0x1, -0xa0, -0x9, -0x7b, -0xa, -0x80, -0x68, -0x0, -0xa4, -0xa, -0x9, -0xa4, -0xa, -0xb, -0x14, -0x34, -0x49, -0x51, -0x43, -0x52, -0x9, -0x8, -0x50, -0x52, -0x52, -0x30, -0x11, -0xe, -0xa, -0xb, -0x89, -0x6, -0x0, -0x9, -0x1, -0x0, -0x0, -0x0, -0x0, -0x79, -0x0, -0x8a, -0x50, -0x52, -0x52, -0x30, -0xa, -0x5, -0x50, -0x52, -0x52, -0x49, -0x70, -0x7b, -0x68, -0xa, -0xf, -0x0, -0x50, -0x52, -0x52, -0x49, -0xa4, -0x50, -0x52, -0x52, -0x30, -0x5b, -0x82, -0x4c, -0x7, -0x4c, -0x4e, -0x4b, -0x41, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0x0, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0x16, -0xa, -0x13, -0x89, -0xe, -0x0, -0x9, -0x3, -0x5, -0x0, -0x0, -0x0, -0xa, -0x0, -0x0, -0x0, -0xb, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0xf, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0x49, -0x51, -0x53, -0x54, -0x50, -0x52, -0x51, -0x41, -0x14, -0x11, -0x5f, -0x44, -0x49, -0x53, -0x0, -0x7d, -0x50, -0x52, -0x51, -0x41, -0xa, -0x80, -0x50, -0x52, -0x51, -0x41, -0x14, -0xf, -0x5f, -0x43, -0x52, -0x53, -0x0, -0xa4, -0x49, -0x51, -0x43, -0x52, -0x50, -0x52, -0x51, -0x41, -0x14, -0x17, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x8a, -0x68, -0xa, -0x5, -0x50, -0x52, -0x52, -0x49, -0x70, -0x50, -0x52, -0x52, -0x49, -0x50, -0x52, -0x51, -0x41, -0x5b, -0x82, -0x4c, -0x7, -0x4c, -0x4e, -0x4b, -0x42, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0x1, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0x16, -0xa, -0x13, -0x89, -0xe, -0x0, -0x9, -0x3, -0x5, -0x0, -0x0, -0x0, -0xa, -0x0, -0x0, -0x0, -0xb, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0xf, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0x49, -0x51, -0x53, -0x54, -0x50, -0x52, -0x51, -0x42, -0x14, -0x11, -0x5f, -0x44, -0x49, -0x53, -0x0, -0x7d, -0x50, -0x52, -0x51, -0x42, -0xa, -0x80, -0x50, -0x52, -0x51, -0x42, -0x14, -0xf, -0x5f, -0x43, -0x52, -0x53, -0x0, -0xa4, -0x49, -0x51, -0x43, -0x52, -0x50, -0x52, -0x51, -0x42, -0x14, -0x17, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x8a, -0x68, -0xa, -0x5, -0x50, -0x52, -0x52, -0x49, -0x70, -0x50, -0x52, -0x52, -0x49, -0x50, -0x52, -0x51, -0x42, -0x5b, -0x82, -0x4d, -0x7, -0x4c, -0x4e, -0x4b, -0x43, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0xa, -0x2, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0x16, -0xa, -0x13, -0x89, -0xe, -0x0, -0x9, -0x3, -0x5, -0x0, -0x0, -0x0, -0xa, -0x0, -0x0, -0x0, -0xb, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0xf, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0x49, -0x51, -0x53, -0x54, -0x50, -0x52, -0x51, -0x43, -0x14, -0x11, -0x5f, -0x44, -0x49, -0x53, -0x0, -0x7d, -0x50, -0x52, -0x51, -0x43, -0xa, -0x80, -0x50, -0x52, -0x51, -0x43, -0x14, -0xf, -0x5f, -0x43, -0x52, -0x53, -0x0, -0xa4, -0x49, -0x51, -0x43, -0x52, -0x50, -0x52, -0x51, -0x43, -0x14, -0x17, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x8a, -0x68, -0xa, -0x5, -0x50, -0x52, -0x52, -0x49, -0x70, -0x50, -0x52, -0x52, -0x49, -0x50, -0x52, -0x51, -0x43, -0x5b, -0x82, -0x4d, -0x7, -0x4c, -0x4e, -0x4b, -0x44, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0xa, -0x3, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0x16, -0xa, -0x13, -0x89, -0xe, -0x0, -0x9, -0x3, -0x5, -0x0, -0x0, -0x0, -0xa, -0x0, -0x0, -0x0, -0xb, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0xf, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0x49, -0x51, -0x53, -0x54, -0x50, -0x52, -0x51, -0x44, -0x14, -0x11, -0x5f, -0x44, -0x49, -0x53, -0x0, -0x7d, -0x50, -0x52, -0x51, -0x44, -0xa, -0x80, -0x50, -0x52, -0x51, -0x44, -0x14, -0xf, -0x5f, -0x43, -0x52, -0x53, -0x0, -0xa4, -0x49, -0x51, -0x43, -0x52, -0x50, -0x52, -0x51, -0x44, -0x14, -0x17, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x8a, -0x68, -0xa, -0x5, -0x50, -0x52, -0x52, -0x49, -0x70, -0x50, -0x52, -0x52, -0x49, -0x50, -0x52, -0x51, -0x44, -0x5b, -0x82, -0x4d, -0x7, -0x4c, -0x4e, -0x4b, -0x45, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0xa, -0x4, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0x16, -0xa, -0x13, -0x89, -0xe, -0x0, -0x9, -0x3, -0x5, -0x0, -0x0, -0x0, -0xa, -0x0, -0x0, -0x0, -0xb, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0xf, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0x49, -0x51, -0x53, -0x54, -0x50, -0x52, -0x51, -0x45, -0x14, -0x11, -0x5f, -0x44, -0x49, -0x53, -0x0, -0x7d, -0x50, -0x52, -0x51, -0x45, -0xa, -0x80, -0x50, -0x52, -0x51, -0x45, -0x14, -0xf, -0x5f, -0x43, -0x52, -0x53, -0x0, -0xa4, -0x49, -0x51, -0x43, -0x52, -0x50, -0x52, -0x51, -0x45, -0x14, -0x17, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x8a, -0x68, -0xa, -0x5, -0x50, -0x52, -0x52, -0x49, -0x70, -0x50, -0x52, -0x52, -0x49, -0x50, -0x52, -0x51, -0x45, -0x5b, -0x82, -0x4d, -0x7, -0x4c, -0x4e, -0x4b, -0x46, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0xa, -0x5, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0x16, -0xa, -0x13, -0x89, -0xe, -0x0, -0x9, -0x3, -0x5, -0x0, -0x0, -0x0, -0xa, -0x0, -0x0, -0x0, -0xb, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0xf, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0x49, -0x51, -0x53, -0x54, -0x50, -0x52, -0x51, -0x46, -0x14, -0x11, -0x5f, -0x44, -0x49, -0x53, -0x0, -0x7d, -0x50, -0x52, -0x51, -0x46, -0xa, -0x80, -0x50, -0x52, -0x51, -0x46, -0x14, -0xf, -0x5f, -0x43, -0x52, -0x53, -0x0, -0xa4, -0x49, -0x51, -0x43, -0x52, -0x50, -0x52, -0x51, -0x46, -0x14, -0x17, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x8a, -0x68, -0xa, -0x5, -0x50, -0x52, -0x52, -0x49, -0x70, -0x50, -0x52, -0x52, -0x49, -0x50, -0x52, -0x51, -0x46, -0x5b, -0x82, -0x4d, -0x7, -0x4c, -0x4e, -0x4b, -0x47, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0xa, -0x6, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0x16, -0xa, -0x13, -0x89, -0xe, -0x0, -0x9, -0x3, -0x5, -0x0, -0x0, -0x0, -0xa, -0x0, -0x0, -0x0, -0xb, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0xf, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0x49, -0x51, -0x53, -0x54, -0x50, -0x52, -0x51, -0x47, -0x14, -0x11, -0x5f, -0x44, -0x49, -0x53, -0x0, -0x7d, -0x50, -0x52, -0x51, -0x47, -0xa, -0x80, -0x50, -0x52, -0x51, -0x47, -0x14, -0xf, -0x5f, -0x43, -0x52, -0x53, -0x0, -0xa4, -0x49, -0x51, -0x43, -0x52, -0x50, -0x52, -0x51, -0x47, -0x14, -0x17, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x8a, -0x68, -0xa, -0x5, -0x50, -0x52, -0x52, -0x49, -0x70, -0x50, -0x52, -0x52, -0x49, -0x50, -0x52, -0x51, -0x47, -0x5b, -0x82, -0x4d, -0x7, -0x4c, -0x4e, -0x4b, -0x48, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0xa, -0x7, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0x16, -0xa, -0x13, -0x89, -0xe, -0x0, -0x9, -0x3, -0x5, -0x0, -0x0, -0x0, -0xa, -0x0, -0x0, -0x0, -0xb, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0xf, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0x49, -0x51, -0x53, -0x54, -0x50, -0x52, -0x51, -0x48, -0x14, -0x11, -0x5f, -0x44, -0x49, -0x53, -0x0, -0x7d, -0x50, -0x52, -0x51, -0x48, -0xa, -0x80, -0x50, -0x52, -0x51, -0x48, -0x14, -0xf, -0x5f, -0x43, -0x52, -0x53, -0x0, -0xa4, -0x49, -0x51, -0x43, -0x52, -0x50, -0x52, -0x51, -0x48, -0x14, -0x17, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x8a, -0x68, -0xa, -0x5, -0x50, -0x52, -0x52, -0x49, -0x70, -0x50, -0x52, -0x52, -0x49, -0x50, -0x52, -0x51, -0x48, -0x5b, -0x82, -0x45, -0x4, -0x47, -0x53, -0x49, -0x41, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0x0, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0xe, -0xa, -0xb, -0x89, -0x6, -0x0, -0x9, -0x1, -0x10, -0x0, -0x0, -0x0, -0x79, -0x0, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0xe, -0xa, -0xb, -0x89, -0x6, -0x0, -0x9, -0x1, -0x10, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0x6, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x5b, -0x82, -0x45, -0x4, -0x47, -0x53, -0x49, -0x42, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0x0, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0xe, -0xa, -0xb, -0x89, -0x6, -0x0, -0x9, -0x1, -0x11, -0x0, -0x0, -0x0, -0x79, -0x0, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0xe, -0xa, -0xb, -0x89, -0x6, -0x0, -0x9, -0x1, -0x11, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0x6, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x5b, -0x82, -0x45, -0x4, -0x47, -0x53, -0x49, -0x43, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0x0, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0xe, -0xa, -0xb, -0x89, -0x6, -0x0, -0x9, -0x1, -0x12, -0x0, -0x0, -0x0, -0x79, -0x0, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0xe, -0xa, -0xb, -0x89, -0x6, -0x0, -0x9, -0x1, -0x12, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0x6, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x5b, -0x82, -0x45, -0x4, -0x47, -0x53, -0x49, -0x44, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0x0, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0xe, -0xa, -0xb, -0x89, -0x6, -0x0, -0x9, -0x1, -0x13, -0x0, -0x0, -0x0, -0x79, -0x0, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0xe, -0xa, -0xb, -0x89, -0x6, -0x0, -0x9, -0x1, -0x13, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0x6, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x5b, -0x82, -0x45, -0x4, -0x47, -0x53, -0x49, -0x45, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0x0, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0xe, -0xa, -0xb, -0x89, -0x6, -0x0, -0x9, -0x1, -0x14, -0x0, -0x0, -0x0, -0x79, -0x0, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0xe, -0xa, -0xb, -0x89, -0x6, -0x0, -0x9, -0x1, -0x14, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0x6, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x5b, -0x82, -0x45, -0x4, -0x47, -0x53, -0x49, -0x46, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0x0, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0xe, -0xa, -0xb, -0x89, -0x6, -0x0, -0x9, -0x1, -0x15, -0x0, -0x0, -0x0, -0x79, -0x0, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0xe, -0xa, -0xb, -0x89, -0x6, -0x0, -0x9, -0x1, -0x15, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0x6, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x5b, -0x82, -0x45, -0x4, -0x47, -0x53, -0x49, -0x47, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0x0, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0xe, -0xa, -0xb, -0x89, -0x6, -0x0, -0x9, -0x1, -0x16, -0x0, -0x0, -0x0, -0x79, -0x0, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0xe, -0xa, -0xb, -0x89, -0x6, -0x0, -0x9, -0x1, -0x16, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0x6, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x5b, -0x82, -0x45, -0x4, -0x47, -0x53, -0x49, -0x48, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0xf, -0x8, -0x5f, -0x55, -0x49, -0x44, -0x0, -0x8, -0x5f, -0x50, -0x52, -0x53, -0x11, -0xe, -0xa, -0xb, -0x89, -0x6, -0x0, -0x9, -0x1, -0x17, -0x0, -0x0, -0x0, -0x79, -0x0, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0xe, -0xa, -0xb, -0x89, -0x6, -0x0, -0x9, -0x1, -0x17, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0x6, -0x5f, -0x53, -0x52, -0x53, -0x1, -0x10, -0x4d, -0xc, -0x5f, -0x53, -0x42, -0x5f, -0x14, -0x35, -0x43, -0x50, -0x4d, -0x41, -0x1, -0x70, -0x83, -0x88, -0x43, -0x50, -0x4f, -0x4e, -0x68, -0x0, -0x60, -0x70, -0x11, -0xb, -0xa, -0x8, -0x0, -0x8, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x61, -0x70, -0x68, -0x88, -0x61, -0xa, -0x2, -0x0, -0x70, -0x68, -0x88, -0x61, -0xa, -0x3, -0x0, -0x70, -0x60, -0x88, -0x61, -0xa, -0x4, -0x0, -0xa4, -0x61, -0x14, -0x1a, -0x43, -0x50, -0x53, -0x54, -0x1, -0x70, -0x83, -0x88, -0x43, -0x50, -0x4f, -0x4e, -0x68, -0x0, -0x60, -0xa0, -0x5, -0x60, -0xa4, -0xa, -0xf, -0xa1, -0x3, -0xa4, -0x0, -0x14, -0xa, -0x43, -0x50, -0x45, -0x4a, -0x2, -0x5b, -0x22, -0xa, -0xc8, -0x14, -0x4a, -0x6, -0x50, -0x52, -0x53, -0x43, -0x0, -0x70, -0x50, -0x52, -0x53, -0x5f, -0x65, -0x70, -0x0, -0x62, -0x70, -0x0, -0x60, -0xa2, -0x46, -0x5, -0x95, -0x60, -0x87, -0x43, -0x50, -0x4f, -0x4e, -0x70, -0x83, -0x88, -0x43, -0x50, -0x4f, -0x4e, -0x60, -0x0, -0x61, -0xa0, -0xa, -0x7b, -0x60, -0xa, -0x7, -0x0, -0x7a, -0x62, -0x1, -0x62, -0xa1, -0xc, -0x70, -0x83, -0x88, -0x65, -0x7a, -0x60, -0xa, -0x3, -0x0, -0x0, -0x62, -0x70, -0x7b, -0x62, -0x1, -0x0, -0x63, -0xa0, -0x22, -0x92, -0x93, -0x61, -0x63, -0x70, -0x63, -0x88, -0x43, -0x50, -0x4f, -0x4e, -0x60, -0x0, -0xa0, -0xa, -0x93, -0x63, -0x1, -0x4e, -0x54, -0x46, -0x59, -0x60, -0x1, -0xa1, -0x8, -0x4e, -0x54, -0x46, -0x59, -0x60, -0xa, -0x3, -0x75, -0x60, -0x10, -0x44, -0x2a, -0x2e, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x5b, -0x82, -0x47, -0x29, -0x4d, -0x48, -0x50, -0x44, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xd, -0x50, -0x4e, -0x50, -0x30, -0x41, -0x30, -0x36, -0x0, -0x8, -0x5f, -0x55, -0x49, -0x44, -0xd, -0x4d, -0x65, -0x6d, -0x6f, -0x72, -0x79, -0x20, -0x68, -0x6f, -0x74, -0x70, -0x6c, -0x75, -0x67, -0x20, -0x72, -0x65, -0x73, -0x6f, -0x75, -0x72, -0x63, -0x65, -0x73, -0x0, -0x14, -0x13, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa0, -0x9, -0x93, -0x4d, -0x44, -0x4e, -0x52, -0x0, -0xa4, -0x0, -0xa4, -0xa, -0xb, -0x5b, -0x1, -0x4d, -0x4c, -0x43, -0x4b, -0x0, -0x14, -0x4a, -0x4, -0x4d, -0x53, -0x43, -0x4e, -0x0, -0xa0, -0x9, -0x93, -0x4d, -0x44, -0x4e, -0x52, -0x0, -0xa4, -0x0, -0x70, -0x0, -0x60, -0x5b, -0x23, -0x4d, -0x4c, -0x43, -0x4b, -0xff, -0xff, -0xa2, -0x25, -0x95, -0x60, -0x4d, -0x44, -0x4e, -0x52, -0x70, -0x60, -0x4d, -0x53, -0x45, -0x4c, -0xa0, -0x13, -0x93, -0x4d, -0x49, -0x4e, -0x53, -0x1, -0x4d, -0x54, -0x46, -0x59, -0x60, -0x1, -0x70, -0x1, -0x4d, -0x49, -0x4e, -0x53, -0x72, -0x60, -0x1, -0x60, -0x5b, -0x27, -0x4d, -0x4c, -0x43, -0x4b, -0xa4, -0x1, -0x14, -0x2d, -0x4d, -0x52, -0x53, -0x54, -0x1, -0x70, -0x0, -0x60, -0x5b, -0x23, -0x4d, -0x4c, -0x43, -0x4b, -0xff, -0xff, -0x70, -0x99, -0x68, -0x0, -0x4d, -0x53, -0x45, -0x4c, -0xa0, -0xb, -0x93, -0x4d, -0x45, -0x53, -0x5f, -0x1, -0x70, -0xa, -0xf, -0x60, -0x5b, -0x27, -0x4d, -0x4c, -0x43, -0x4b, -0xa4, -0x60, -0x14, -0x41, -0x18, -0x4d, -0x43, -0x52, -0x53, -0x9, -0x5b, -0x23, -0x4d, -0x4c, -0x43, -0x4b, -0xff, -0xff, -0x70, -0x99, -0x68, -0x0, -0x4d, -0x53, -0x45, -0x4c, -0x8, -0x4d, -0x52, -0x36, -0x34, -0x11, -0x33, -0xa, -0x30, -0x8a, -0x2b, -0x0, -0x0, -0xc, -0x3, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0xfe, -0xff, -0xff, -0xff, -0xff, -0xff, -0xff, -0xff, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0xff, -0xff, -0xff, -0xff, -0xff, -0xff, -0xff, -0xff, -0x79, -0x0, -0x8a, -0x4d, -0x52, -0x36, -0x34, -0xa, -0xe, -0x4d, -0x49, -0x4e, -0x4c, -0x8a, -0x4d, -0x52, -0x36, -0x34, -0xa, -0x12, -0x4d, -0x49, -0x4e, -0x48, -0x8a, -0x4d, -0x52, -0x36, -0x34, -0xa, -0x26, -0x4c, -0x45, -0x4e, -0x4c, -0x8a, -0x4d, -0x52, -0x36, -0x34, -0xa, -0x2a, -0x4c, -0x45, -0x4e, -0x48, -0x8a, -0x4d, -0x52, -0x36, -0x34, -0xa, -0x16, -0x4d, -0x41, -0x58, -0x4c, -0x8a, -0x4d, -0x52, -0x36, -0x34, -0xa, -0x1a, -0x4d, -0x41, -0x58, -0x48, -0x70, -0x4d, -0x52, -0x42, -0x48, -0x4d, -0x49, -0x4e, -0x48, -0x70, -0x4d, -0x52, -0x42, -0x4c, -0x4d, -0x49, -0x4e, -0x4c, -0x70, -0x4d, -0x52, -0x4c, -0x48, -0x4c, -0x45, -0x4e, -0x48, -0x70, -0x4d, -0x52, -0x4c, -0x4c, -0x4c, -0x45, -0x4e, -0x4c, -0x72, -0x4d, -0x49, -0x4e, -0x4c, -0x4c, -0x45, -0x4e, -0x4c, -0x4d, -0x41, -0x58, -0x4c, -0x72, -0x4d, -0x49, -0x4e, -0x48, -0x4c, -0x45, -0x4e, -0x48, -0x4d, -0x41, -0x58, -0x48, -0xa0, -0x14, -0x95, -0x4d, -0x41, -0x58, -0x4c, -0x4d, -0x49, -0x4e, -0x4c, -0x72, -0x4d, -0x41, -0x58, -0x48, -0x1, -0x4d, -0x41, -0x58, -0x48, -0xa0, -0x11, -0x95, -0x4d, -0x41, -0x58, -0x4c, -0x1, -0x74, -0x4d, -0x41, -0x58, -0x48, -0x1, -0x4d, -0x41, -0x58, -0x48, -0x74, -0x4d, -0x41, -0x58, -0x4c, -0x1, -0x4d, -0x41, -0x58, -0x4c, -0xa0, -0x44, -0x7, -0x93, -0x4d, -0x41, -0x58, -0x48, -0x0, -0x8, -0x4d, -0x52, -0x33, -0x32, -0x11, -0x1f, -0xa, -0x1c, -0x87, -0x17, -0x0, -0x0, -0xc, -0x3, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0xfe, -0xff, -0xff, -0xff, -0x0, -0x0, -0x0, -0x0, -0xff, -0xff, -0xff, -0xff, -0x79, -0x0, -0x8a, -0x4d, -0x52, -0x33, -0x32, -0xa, -0xa, -0x4d, -0x49, -0x4e, -0x5f, -0x8a, -0x4d, -0x52, -0x33, -0x32, -0xa, -0xe, -0x4d, -0x41, -0x58, -0x5f, -0x8a, -0x4d, -0x52, -0x33, -0x32, -0xa, -0x16, -0x4c, -0x45, -0x4e, -0x5f, -0x70, -0x4d, -0x49, -0x4e, -0x4c, -0x4d, -0x49, -0x4e, -0x5f, -0x70, -0x4d, -0x41, -0x58, -0x4c, -0x4d, -0x41, -0x58, -0x5f, -0x70, -0x4c, -0x45, -0x4e, -0x4c, -0x4c, -0x45, -0x4e, -0x5f, -0x5b, -0x27, -0x4d, -0x4c, -0x43, -0x4b, -0xa4, -0x4d, -0x52, -0x33, -0x32, -0x5b, -0x27, -0x4d, -0x4c, -0x43, -0x4b, -0xa4, -0x4d, -0x52, -0x36, -0x34, -0x14, -0x24, -0x4d, -0x50, -0x58, -0x4d, -0x1, -0x5b, -0x23, -0x4d, -0x4c, -0x43, -0x4b, -0xff, -0xff, -0x70, -0x99, -0x68, -0x0, -0x4d, -0x53, -0x45, -0x4c, -0x70, -0x4d, -0x50, -0x58, -0x5f, -0x60, -0x5b, -0x27, -0x4d, -0x4c, -0x43, -0x4b, -0xa4, -0x60, -0x14, -0x28, -0x4d, -0x4f, -0x53, -0x54, -0x4, -0x5b, -0x23, -0x4d, -0x4c, -0x43, -0x4b, -0xff, -0xff, -0x70, -0x99, -0x68, -0x0, -0x4d, -0x53, -0x45, -0x4c, -0x70, -0x69, -0x4d, -0x4f, -0x45, -0x56, -0x70, -0x6a, -0x4d, -0x4f, -0x53, -0x43, -0x5b, -0x27, -0x4d, -0x4c, -0x43, -0x4b, -0x10, -0x42, -0xa, -0x5f, -0x47, -0x50, -0x45, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xd, -0x41, -0x43, -0x50, -0x49, -0x30, -0x30, -0x30, -0x36, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x30, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x31, -0x0, -0x14, -0x10, -0x5f, -0x45, -0x30, -0x32, -0x0, -0x5c, -0x2e, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x52, -0x53, -0x43, -0x14, -0x19, -0x5f, -0x45, -0x30, -0x33, -0x0, -0x5c, -0x2f, -0x4, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x4d, -0x48, -0x50, -0x44, -0x4d, -0x53, -0x43, -0x4e, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x34, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x35, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x36, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x37, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x38, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x39, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x41, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x42, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x43, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x44, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x45, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, -0x46, -0x0 -}; diff --git a/qemu/hw/i386/smbios.c b/qemu/hw/i386/smbios.c deleted file mode 100644 index 1341e0234..000000000 --- a/qemu/hw/i386/smbios.c +++ /dev/null @@ -1,1102 +0,0 @@ -/* - * SMBIOS Support - * - * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. - * Copyright (C) 2013 Red Hat, Inc. - * - * Authors: - * Alex Williamson - * Markus Armbruster - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#include "qemu/config-file.h" -#include "qemu/error-report.h" -#include "sysemu/sysemu.h" -#include "sysemu/cpus.h" -#include "hw/i386/pc.h" -#include "hw/i386/smbios.h" -#include "hw/loader.h" - - -/* legacy structures and constants for <= 2.0 machines */ -struct smbios_header { - uint16_t length; - uint8_t type; -} QEMU_PACKED; - -struct smbios_field { - struct smbios_header header; - uint8_t type; - uint16_t offset; - uint8_t data[]; -} QEMU_PACKED; - -struct smbios_table { - struct smbios_header header; - uint8_t data[]; -} QEMU_PACKED; - -#define SMBIOS_FIELD_ENTRY 0 -#define SMBIOS_TABLE_ENTRY 1 - -static uint8_t *smbios_entries; -static size_t smbios_entries_len; -static bool smbios_legacy = true; -static bool smbios_uuid_encoded = true; -/* end: legacy structures & constants for <= 2.0 machines */ - - -static uint8_t *smbios_tables; -static size_t smbios_tables_len; -static unsigned smbios_table_max; -static unsigned smbios_table_cnt; -static struct smbios_entry_point ep; - -static int smbios_type4_count = 0; -static bool smbios_immutable; -static bool smbios_have_defaults; -static uint32_t smbios_cpuid_version, smbios_cpuid_features, smbios_smp_sockets; - -static DECLARE_BITMAP(have_binfile_bitmap, SMBIOS_MAX_TYPE+1); -static DECLARE_BITMAP(have_fields_bitmap, SMBIOS_MAX_TYPE+1); - -static struct { - const char *vendor, *version, *date; - bool have_major_minor, uefi; - uint8_t major, minor; -} type0; - -static struct { - const char *manufacturer, *product, *version, *serial, *sku, *family; - /* uuid is in qemu_uuid[] */ -} type1; - -static struct { - const char *manufacturer, *product, *version, *serial, *asset, *location; -} type2; - -static struct { - const char *manufacturer, *version, *serial, *asset, *sku; -} type3; - -static struct { - const char *sock_pfx, *manufacturer, *version, *serial, *asset, *part; -} type4; - -static struct { - const char *loc_pfx, *bank, *manufacturer, *serial, *asset, *part; - uint16_t speed; -} type17; - -static QemuOptsList qemu_smbios_opts = { - .name = "smbios", - .head = QTAILQ_HEAD_INITIALIZER(qemu_smbios_opts.head), - .desc = { - /* - * no elements => accept any params - * validation will happen later - */ - { /* end of list */ } - } -}; - -static const QemuOptDesc qemu_smbios_file_opts[] = { - { - .name = "file", - .type = QEMU_OPT_STRING, - .help = "binary file containing an SMBIOS element", - }, - { /* end of list */ } -}; - -static const QemuOptDesc qemu_smbios_type0_opts[] = { - { - .name = "type", - .type = QEMU_OPT_NUMBER, - .help = "SMBIOS element type", - },{ - .name = "vendor", - .type = QEMU_OPT_STRING, - .help = "vendor name", - },{ - .name = "version", - .type = QEMU_OPT_STRING, - .help = "version number", - },{ - .name = "date", - .type = QEMU_OPT_STRING, - .help = "release date", - },{ - .name = "release", - .type = QEMU_OPT_STRING, - .help = "revision number", - },{ - .name = "uefi", - .type = QEMU_OPT_BOOL, - .help = "uefi support", - }, - { /* end of list */ } -}; - -static const QemuOptDesc qemu_smbios_type1_opts[] = { - { - .name = "type", - .type = QEMU_OPT_NUMBER, - .help = "SMBIOS element type", - },{ - .name = "manufacturer", - .type = QEMU_OPT_STRING, - .help = "manufacturer name", - },{ - .name = "product", - .type = QEMU_OPT_STRING, - .help = "product name", - },{ - .name = "version", - .type = QEMU_OPT_STRING, - .help = "version number", - },{ - .name = "serial", - .type = QEMU_OPT_STRING, - .help = "serial number", - },{ - .name = "uuid", - .type = QEMU_OPT_STRING, - .help = "UUID", - },{ - .name = "sku", - .type = QEMU_OPT_STRING, - .help = "SKU number", - },{ - .name = "family", - .type = QEMU_OPT_STRING, - .help = "family name", - }, - { /* end of list */ } -}; - -static const QemuOptDesc qemu_smbios_type2_opts[] = { - { - .name = "type", - .type = QEMU_OPT_NUMBER, - .help = "SMBIOS element type", - },{ - .name = "manufacturer", - .type = QEMU_OPT_STRING, - .help = "manufacturer name", - },{ - .name = "product", - .type = QEMU_OPT_STRING, - .help = "product name", - },{ - .name = "version", - .type = QEMU_OPT_STRING, - .help = "version number", - },{ - .name = "serial", - .type = QEMU_OPT_STRING, - .help = "serial number", - },{ - .name = "asset", - .type = QEMU_OPT_STRING, - .help = "asset tag number", - },{ - .name = "location", - .type = QEMU_OPT_STRING, - .help = "location in chassis", - }, - { /* end of list */ } -}; - -static const QemuOptDesc qemu_smbios_type3_opts[] = { - { - .name = "type", - .type = QEMU_OPT_NUMBER, - .help = "SMBIOS element type", - },{ - .name = "manufacturer", - .type = QEMU_OPT_STRING, - .help = "manufacturer name", - },{ - .name = "version", - .type = QEMU_OPT_STRING, - .help = "version number", - },{ - .name = "serial", - .type = QEMU_OPT_STRING, - .help = "serial number", - },{ - .name = "asset", - .type = QEMU_OPT_STRING, - .help = "asset tag number", - },{ - .name = "sku", - .type = QEMU_OPT_STRING, - .help = "SKU number", - }, - { /* end of list */ } -}; - -static const QemuOptDesc qemu_smbios_type4_opts[] = { - { - .name = "type", - .type = QEMU_OPT_NUMBER, - .help = "SMBIOS element type", - },{ - .name = "sock_pfx", - .type = QEMU_OPT_STRING, - .help = "socket designation string prefix", - },{ - .name = "manufacturer", - .type = QEMU_OPT_STRING, - .help = "manufacturer name", - },{ - .name = "version", - .type = QEMU_OPT_STRING, - .help = "version number", - },{ - .name = "serial", - .type = QEMU_OPT_STRING, - .help = "serial number", - },{ - .name = "asset", - .type = QEMU_OPT_STRING, - .help = "asset tag number", - },{ - .name = "part", - .type = QEMU_OPT_STRING, - .help = "part number", - }, - { /* end of list */ } -}; - -static const QemuOptDesc qemu_smbios_type17_opts[] = { - { - .name = "type", - .type = QEMU_OPT_NUMBER, - .help = "SMBIOS element type", - },{ - .name = "loc_pfx", - .type = QEMU_OPT_STRING, - .help = "device locator string prefix", - },{ - .name = "bank", - .type = QEMU_OPT_STRING, - .help = "bank locator string", - },{ - .name = "manufacturer", - .type = QEMU_OPT_STRING, - .help = "manufacturer name", - },{ - .name = "serial", - .type = QEMU_OPT_STRING, - .help = "serial number", - },{ - .name = "asset", - .type = QEMU_OPT_STRING, - .help = "asset tag number", - },{ - .name = "part", - .type = QEMU_OPT_STRING, - .help = "part number", - },{ - .name = "speed", - .type = QEMU_OPT_NUMBER, - .help = "maximum capable speed", - }, - { /* end of list */ } -}; - -static void smbios_register_config(void) -{ - qemu_add_opts(&qemu_smbios_opts); -} - -machine_init(smbios_register_config); - -static void smbios_validate_table(void) -{ - uint32_t expect_t4_count = smbios_legacy ? smp_cpus : smbios_smp_sockets; - - if (smbios_type4_count && smbios_type4_count != expect_t4_count) { - error_report("Expected %d SMBIOS Type 4 tables, got %d instead", - expect_t4_count, smbios_type4_count); - exit(1); - } -} - - -/* legacy setup functions for <= 2.0 machines */ -static void smbios_add_field(int type, int offset, const void *data, size_t len) -{ - struct smbios_field *field; - - if (!smbios_entries) { - smbios_entries_len = sizeof(uint16_t); - smbios_entries = g_malloc0(smbios_entries_len); - } - smbios_entries = g_realloc(smbios_entries, smbios_entries_len + - sizeof(*field) + len); - field = (struct smbios_field *)(smbios_entries + smbios_entries_len); - field->header.type = SMBIOS_FIELD_ENTRY; - field->header.length = cpu_to_le16(sizeof(*field) + len); - - field->type = type; - field->offset = cpu_to_le16(offset); - memcpy(field->data, data, len); - - smbios_entries_len += sizeof(*field) + len; - (*(uint16_t *)smbios_entries) = - cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); -} - -static void smbios_maybe_add_str(int type, int offset, const char *data) -{ - if (data) { - smbios_add_field(type, offset, data, strlen(data) + 1); - } -} - -static void smbios_build_type_0_fields(void) -{ - smbios_maybe_add_str(0, offsetof(struct smbios_type_0, vendor_str), - type0.vendor); - smbios_maybe_add_str(0, offsetof(struct smbios_type_0, bios_version_str), - type0.version); - smbios_maybe_add_str(0, offsetof(struct smbios_type_0, - bios_release_date_str), - type0.date); - if (type0.have_major_minor) { - smbios_add_field(0, offsetof(struct smbios_type_0, - system_bios_major_release), - &type0.major, 1); - smbios_add_field(0, offsetof(struct smbios_type_0, - system_bios_minor_release), - &type0.minor, 1); - } -} - -static void smbios_build_type_1_fields(void) -{ - smbios_maybe_add_str(1, offsetof(struct smbios_type_1, manufacturer_str), - type1.manufacturer); - smbios_maybe_add_str(1, offsetof(struct smbios_type_1, product_name_str), - type1.product); - smbios_maybe_add_str(1, offsetof(struct smbios_type_1, version_str), - type1.version); - smbios_maybe_add_str(1, offsetof(struct smbios_type_1, serial_number_str), - type1.serial); - smbios_maybe_add_str(1, offsetof(struct smbios_type_1, sku_number_str), - type1.sku); - smbios_maybe_add_str(1, offsetof(struct smbios_type_1, family_str), - type1.family); - if (qemu_uuid_set) { - /* We don't encode the UUID in the "wire format" here because this - * function is for legacy mode and needs to keep the guest ABI, and - * because we don't know what's the SMBIOS version advertised by the - * BIOS. - */ - smbios_add_field(1, offsetof(struct smbios_type_1, uuid), - qemu_uuid, 16); - } -} - -uint8_t *smbios_get_table_legacy(size_t *length) -{ - if (!smbios_legacy) { - *length = 0; - return NULL; - } - - if (!smbios_immutable) { - smbios_build_type_0_fields(); - smbios_build_type_1_fields(); - smbios_validate_table(); - smbios_immutable = true; - } - *length = smbios_entries_len; - return smbios_entries; -} -/* end: legacy setup functions for <= 2.0 machines */ - - -static bool smbios_skip_table(uint8_t type, bool required_table) -{ - if (test_bit(type, have_binfile_bitmap)) { - return true; /* user provided their own binary blob(s) */ - } - if (test_bit(type, have_fields_bitmap)) { - return false; /* user provided fields via command line */ - } - if (smbios_have_defaults && required_table) { - return false; /* we're building tables, and this one's required */ - } - return true; -} - -#define SMBIOS_BUILD_TABLE_PRE(tbl_type, tbl_handle, tbl_required) \ - struct smbios_type_##tbl_type *t; \ - size_t t_off; /* table offset into smbios_tables */ \ - int str_index = 0; \ - do { \ - /* should we skip building this table ? */ \ - if (smbios_skip_table(tbl_type, tbl_required)) { \ - return; \ - } \ - \ - /* use offset of table t within smbios_tables */ \ - /* (pointer must be updated after each realloc) */ \ - t_off = smbios_tables_len; \ - smbios_tables_len += sizeof(*t); \ - smbios_tables = g_realloc(smbios_tables, smbios_tables_len); \ - t = (struct smbios_type_##tbl_type *)(smbios_tables + t_off); \ - \ - t->header.type = tbl_type; \ - t->header.length = sizeof(*t); \ - t->header.handle = cpu_to_le16(tbl_handle); \ - } while (0) - -#define SMBIOS_TABLE_SET_STR(tbl_type, field, value) \ - do { \ - int len = (value != NULL) ? strlen(value) + 1 : 0; \ - if (len > 1) { \ - smbios_tables = g_realloc(smbios_tables, \ - smbios_tables_len + len); \ - memcpy(smbios_tables + smbios_tables_len, value, len); \ - smbios_tables_len += len; \ - /* update pointer post-realloc */ \ - t = (struct smbios_type_##tbl_type *)(smbios_tables + t_off); \ - t->field = ++str_index; \ - } else { \ - t->field = 0; \ - } \ - } while (0) - -#define SMBIOS_BUILD_TABLE_POST \ - do { \ - size_t term_cnt, t_size; \ - \ - /* add '\0' terminator (add two if no strings defined) */ \ - term_cnt = (str_index == 0) ? 2 : 1; \ - smbios_tables = g_realloc(smbios_tables, \ - smbios_tables_len + term_cnt); \ - memset(smbios_tables + smbios_tables_len, 0, term_cnt); \ - smbios_tables_len += term_cnt; \ - \ - /* update smbios max. element size */ \ - t_size = smbios_tables_len - t_off; \ - if (t_size > smbios_table_max) { \ - smbios_table_max = t_size; \ - } \ - \ - /* update smbios element count */ \ - smbios_table_cnt++; \ - } while (0) - -static void smbios_build_type_0_table(void) -{ - SMBIOS_BUILD_TABLE_PRE(0, 0x000, false); /* optional, leave up to BIOS */ - - SMBIOS_TABLE_SET_STR(0, vendor_str, type0.vendor); - SMBIOS_TABLE_SET_STR(0, bios_version_str, type0.version); - - t->bios_starting_address_segment = cpu_to_le16(0xE800); /* from SeaBIOS */ - - SMBIOS_TABLE_SET_STR(0, bios_release_date_str, type0.date); - - t->bios_rom_size = 0; /* hardcoded in SeaBIOS with FIXME comment */ - - t->bios_characteristics = cpu_to_le64(0x08); /* Not supported */ - t->bios_characteristics_extension_bytes[0] = 0; - t->bios_characteristics_extension_bytes[1] = 0x14; /* TCD/SVVP | VM */ - if (type0.uefi) { - t->bios_characteristics_extension_bytes[1] |= 0x08; /* |= UEFI */ - } - - if (type0.have_major_minor) { - t->system_bios_major_release = type0.major; - t->system_bios_minor_release = type0.minor; - } else { - t->system_bios_major_release = 0; - t->system_bios_minor_release = 0; - } - - /* hardcoded in SeaBIOS */ - t->embedded_controller_major_release = 0xFF; - t->embedded_controller_minor_release = 0xFF; - - SMBIOS_BUILD_TABLE_POST; -} - -/* Encode UUID from the big endian encoding described on RFC4122 to the wire - * format specified by SMBIOS version 2.6. - */ -static void smbios_encode_uuid(struct smbios_uuid *uuid, const uint8_t *buf) -{ - memcpy(uuid, buf, 16); - if (smbios_uuid_encoded) { - uuid->time_low = bswap32(uuid->time_low); - uuid->time_mid = bswap16(uuid->time_mid); - uuid->time_hi_and_version = bswap16(uuid->time_hi_and_version); - } -} - -static void smbios_build_type_1_table(void) -{ - SMBIOS_BUILD_TABLE_PRE(1, 0x100, true); /* required */ - - SMBIOS_TABLE_SET_STR(1, manufacturer_str, type1.manufacturer); - SMBIOS_TABLE_SET_STR(1, product_name_str, type1.product); - SMBIOS_TABLE_SET_STR(1, version_str, type1.version); - SMBIOS_TABLE_SET_STR(1, serial_number_str, type1.serial); - if (qemu_uuid_set) { - smbios_encode_uuid(&t->uuid, qemu_uuid); - } else { - memset(&t->uuid, 0, 16); - } - t->wake_up_type = 0x06; /* power switch */ - SMBIOS_TABLE_SET_STR(1, sku_number_str, type1.sku); - SMBIOS_TABLE_SET_STR(1, family_str, type1.family); - - SMBIOS_BUILD_TABLE_POST; -} - -static void smbios_build_type_2_table(void) -{ - SMBIOS_BUILD_TABLE_PRE(2, 0x200, false); /* optional */ - - SMBIOS_TABLE_SET_STR(2, manufacturer_str, type2.manufacturer); - SMBIOS_TABLE_SET_STR(2, product_str, type2.product); - SMBIOS_TABLE_SET_STR(2, version_str, type2.version); - SMBIOS_TABLE_SET_STR(2, serial_number_str, type2.serial); - SMBIOS_TABLE_SET_STR(2, asset_tag_number_str, type2.asset); - t->feature_flags = 0x01; /* Motherboard */ - SMBIOS_TABLE_SET_STR(2, location_str, type2.location); - t->chassis_handle = cpu_to_le16(0x300); /* Type 3 (System enclosure) */ - t->board_type = 0x0A; /* Motherboard */ - t->contained_element_count = 0; - - SMBIOS_BUILD_TABLE_POST; -} - -static void smbios_build_type_3_table(void) -{ - SMBIOS_BUILD_TABLE_PRE(3, 0x300, true); /* required */ - - SMBIOS_TABLE_SET_STR(3, manufacturer_str, type3.manufacturer); - t->type = 0x01; /* Other */ - SMBIOS_TABLE_SET_STR(3, version_str, type3.version); - SMBIOS_TABLE_SET_STR(3, serial_number_str, type3.serial); - SMBIOS_TABLE_SET_STR(3, asset_tag_number_str, type3.asset); - t->boot_up_state = 0x03; /* Safe */ - t->power_supply_state = 0x03; /* Safe */ - t->thermal_state = 0x03; /* Safe */ - t->security_status = 0x02; /* Unknown */ - t->oem_defined = cpu_to_le32(0); - t->height = 0; - t->number_of_power_cords = 0; - t->contained_element_count = 0; - SMBIOS_TABLE_SET_STR(3, sku_number_str, type3.sku); - - SMBIOS_BUILD_TABLE_POST; -} - -static void smbios_build_type_4_table(unsigned instance) -{ - char sock_str[128]; - - SMBIOS_BUILD_TABLE_PRE(4, 0x400 + instance, true); /* required */ - - snprintf(sock_str, sizeof(sock_str), "%s%2x", type4.sock_pfx, instance); - SMBIOS_TABLE_SET_STR(4, socket_designation_str, sock_str); - t->processor_type = 0x03; /* CPU */ - t->processor_family = 0x01; /* Other */ - SMBIOS_TABLE_SET_STR(4, processor_manufacturer_str, type4.manufacturer); - t->processor_id[0] = cpu_to_le32(smbios_cpuid_version); - t->processor_id[1] = cpu_to_le32(smbios_cpuid_features); - SMBIOS_TABLE_SET_STR(4, processor_version_str, type4.version); - t->voltage = 0; - t->external_clock = cpu_to_le16(0); /* Unknown */ - /* SVVP requires max_speed and current_speed to not be unknown. */ - t->max_speed = cpu_to_le16(2000); /* 2000 MHz */ - t->current_speed = cpu_to_le16(2000); /* 2000 MHz */ - t->status = 0x41; /* Socket populated, CPU enabled */ - t->processor_upgrade = 0x01; /* Other */ - t->l1_cache_handle = cpu_to_le16(0xFFFF); /* N/A */ - t->l2_cache_handle = cpu_to_le16(0xFFFF); /* N/A */ - t->l3_cache_handle = cpu_to_le16(0xFFFF); /* N/A */ - SMBIOS_TABLE_SET_STR(4, serial_number_str, type4.serial); - SMBIOS_TABLE_SET_STR(4, asset_tag_number_str, type4.asset); - SMBIOS_TABLE_SET_STR(4, part_number_str, type4.part); - t->core_count = t->core_enabled = smp_cores; - t->thread_count = smp_threads; - t->processor_characteristics = cpu_to_le16(0x02); /* Unknown */ - t->processor_family2 = cpu_to_le16(0x01); /* Other */ - - SMBIOS_BUILD_TABLE_POST; - smbios_type4_count++; -} - -#define ONE_KB ((ram_addr_t)1 << 10) -#define ONE_MB ((ram_addr_t)1 << 20) -#define ONE_GB ((ram_addr_t)1 << 30) - -#define MAX_T16_STD_SZ 0x80000000 /* 2T in Kilobytes */ - -static void smbios_build_type_16_table(unsigned dimm_cnt) -{ - uint64_t size_kb; - - SMBIOS_BUILD_TABLE_PRE(16, 0x1000, true); /* required */ - - t->location = 0x01; /* Other */ - t->use = 0x03; /* System memory */ - t->error_correction = 0x06; /* Multi-bit ECC (for Microsoft, per SeaBIOS) */ - size_kb = QEMU_ALIGN_UP(ram_size, ONE_KB) / ONE_KB; - if (size_kb < MAX_T16_STD_SZ) { - t->maximum_capacity = cpu_to_le32(size_kb); - t->extended_maximum_capacity = cpu_to_le64(0); - } else { - t->maximum_capacity = cpu_to_le32(MAX_T16_STD_SZ); - t->extended_maximum_capacity = cpu_to_le64(ram_size); - } - t->memory_error_information_handle = cpu_to_le16(0xFFFE); /* Not provided */ - t->number_of_memory_devices = cpu_to_le16(dimm_cnt); - - SMBIOS_BUILD_TABLE_POST; -} - -#define MAX_T17_STD_SZ 0x7FFF /* (32G - 1M), in Megabytes */ -#define MAX_T17_EXT_SZ 0x80000000 /* 2P, in Megabytes */ - -static void smbios_build_type_17_table(unsigned instance, uint64_t size) -{ - char loc_str[128]; - uint64_t size_mb; - - SMBIOS_BUILD_TABLE_PRE(17, 0x1100 + instance, true); /* required */ - - t->physical_memory_array_handle = cpu_to_le16(0x1000); /* Type 16 above */ - t->memory_error_information_handle = cpu_to_le16(0xFFFE); /* Not provided */ - t->total_width = cpu_to_le16(0xFFFF); /* Unknown */ - t->data_width = cpu_to_le16(0xFFFF); /* Unknown */ - size_mb = QEMU_ALIGN_UP(size, ONE_MB) / ONE_MB; - if (size_mb < MAX_T17_STD_SZ) { - t->size = cpu_to_le16(size_mb); - t->extended_size = cpu_to_le32(0); - } else { - assert(size_mb < MAX_T17_EXT_SZ); - t->size = cpu_to_le16(MAX_T17_STD_SZ); - t->extended_size = cpu_to_le32(size_mb); - } - t->form_factor = 0x09; /* DIMM */ - t->device_set = 0; /* Not in a set */ - snprintf(loc_str, sizeof(loc_str), "%s %d", type17.loc_pfx, instance); - SMBIOS_TABLE_SET_STR(17, device_locator_str, loc_str); - SMBIOS_TABLE_SET_STR(17, bank_locator_str, type17.bank); - t->memory_type = 0x07; /* RAM */ - t->type_detail = cpu_to_le16(0x02); /* Other */ - t->speed = cpu_to_le16(type17.speed); - SMBIOS_TABLE_SET_STR(17, manufacturer_str, type17.manufacturer); - SMBIOS_TABLE_SET_STR(17, serial_number_str, type17.serial); - SMBIOS_TABLE_SET_STR(17, asset_tag_number_str, type17.asset); - SMBIOS_TABLE_SET_STR(17, part_number_str, type17.part); - t->attributes = 0; /* Unknown */ - t->configured_clock_speed = t->speed; /* reuse value for max speed */ - t->minimum_voltage = cpu_to_le16(0); /* Unknown */ - t->maximum_voltage = cpu_to_le16(0); /* Unknown */ - t->configured_voltage = cpu_to_le16(0); /* Unknown */ - - SMBIOS_BUILD_TABLE_POST; -} - -static void smbios_build_type_19_table(unsigned instance, - uint64_t start, uint64_t size) -{ - uint64_t end, start_kb, end_kb; - - SMBIOS_BUILD_TABLE_PRE(19, 0x1300 + instance, true); /* required */ - - end = start + size - 1; - assert(end > start); - start_kb = start / ONE_KB; - end_kb = end / ONE_KB; - if (start_kb < UINT32_MAX && end_kb < UINT32_MAX) { - t->starting_address = cpu_to_le32(start_kb); - t->ending_address = cpu_to_le32(end_kb); - t->extended_starting_address = - t->extended_ending_address = cpu_to_le64(0); - } else { - t->starting_address = t->ending_address = cpu_to_le32(UINT32_MAX); - t->extended_starting_address = cpu_to_le64(start); - t->extended_ending_address = cpu_to_le64(end); - } - t->memory_array_handle = cpu_to_le16(0x1000); /* Type 16 above */ - t->partition_width = 1; /* One device per row */ - - SMBIOS_BUILD_TABLE_POST; -} - -static void smbios_build_type_32_table(void) -{ - SMBIOS_BUILD_TABLE_PRE(32, 0x2000, true); /* required */ - - memset(t->reserved, 0, 6); - t->boot_status = 0; /* No errors detected */ - - SMBIOS_BUILD_TABLE_POST; -} - -static void smbios_build_type_127_table(void) -{ - SMBIOS_BUILD_TABLE_PRE(127, 0x7F00, true); /* required */ - SMBIOS_BUILD_TABLE_POST; -} - -void smbios_set_cpuid(uint32_t version, uint32_t features) -{ - smbios_cpuid_version = version; - smbios_cpuid_features = features; -} - -#define SMBIOS_SET_DEFAULT(field, value) \ - if (!field) { \ - field = value; \ - } - -void smbios_set_defaults(const char *manufacturer, const char *product, - const char *version, bool legacy_mode, - bool uuid_encoded) -{ - smbios_have_defaults = true; - smbios_legacy = legacy_mode; - smbios_uuid_encoded = uuid_encoded; - - /* drop unwanted version of command-line file blob(s) */ - if (smbios_legacy) { - g_free(smbios_tables); - /* in legacy mode, also complain if fields were given for types > 1 */ - if (find_next_bit(have_fields_bitmap, - SMBIOS_MAX_TYPE+1, 2) < SMBIOS_MAX_TYPE+1) { - error_report("can't process fields for smbios " - "types > 1 on machine versions < 2.1!"); - exit(1); - } - } else { - g_free(smbios_entries); - } - - SMBIOS_SET_DEFAULT(type1.manufacturer, manufacturer); - SMBIOS_SET_DEFAULT(type1.product, product); - SMBIOS_SET_DEFAULT(type1.version, version); - SMBIOS_SET_DEFAULT(type2.manufacturer, manufacturer); - SMBIOS_SET_DEFAULT(type2.product, product); - SMBIOS_SET_DEFAULT(type2.version, version); - SMBIOS_SET_DEFAULT(type3.manufacturer, manufacturer); - SMBIOS_SET_DEFAULT(type3.version, version); - SMBIOS_SET_DEFAULT(type4.sock_pfx, "CPU"); - SMBIOS_SET_DEFAULT(type4.manufacturer, manufacturer); - SMBIOS_SET_DEFAULT(type4.version, version); - SMBIOS_SET_DEFAULT(type17.loc_pfx, "DIMM"); - SMBIOS_SET_DEFAULT(type17.manufacturer, manufacturer); -} - -static void smbios_entry_point_setup(void) -{ - memcpy(ep.anchor_string, "_SM_", 4); - memcpy(ep.intermediate_anchor_string, "_DMI_", 5); - ep.length = sizeof(struct smbios_entry_point); - ep.entry_point_revision = 0; /* formatted_area reserved, per spec v2.1+ */ - memset(ep.formatted_area, 0, 5); - - /* compliant with smbios spec v2.8 */ - ep.smbios_major_version = 2; - ep.smbios_minor_version = 8; - ep.smbios_bcd_revision = 0x28; - - /* set during table construction, but BIOS may override: */ - ep.structure_table_length = cpu_to_le16(smbios_tables_len); - ep.max_structure_size = cpu_to_le16(smbios_table_max); - ep.number_of_structures = cpu_to_le16(smbios_table_cnt); - - /* BIOS must recalculate: */ - ep.checksum = 0; - ep.intermediate_checksum = 0; - ep.structure_table_address = cpu_to_le32(0); -} - -void smbios_get_tables(uint8_t **tables, size_t *tables_len, - uint8_t **anchor, size_t *anchor_len) -{ - unsigned i, dimm_cnt, instance; - - if (smbios_legacy) { - *tables = *anchor = NULL; - *tables_len = *anchor_len = 0; - return; - } - - if (!smbios_immutable) { - smbios_build_type_0_table(); - smbios_build_type_1_table(); - smbios_build_type_2_table(); - smbios_build_type_3_table(); - - smbios_smp_sockets = DIV_ROUND_UP(smp_cpus, smp_cores * smp_threads); - assert(smbios_smp_sockets >= 1); - - for (i = 0; i < smbios_smp_sockets; i++) { - smbios_build_type_4_table(i); - } - -#define MAX_DIMM_SZ (16ll * ONE_GB) -#define GET_DIMM_SZ ((i < dimm_cnt - 1) ? MAX_DIMM_SZ \ - : ((ram_size - 1) % MAX_DIMM_SZ) + 1) - - dimm_cnt = QEMU_ALIGN_UP(ram_size, MAX_DIMM_SZ) / MAX_DIMM_SZ; - - smbios_build_type_16_table(dimm_cnt); - - for (i = 0; i < dimm_cnt; i++) { - smbios_build_type_17_table(i, GET_DIMM_SZ); - } - - for (i = 0, instance = 0; i < e820_get_num_entries(); i++) { - uint64_t address, length; - if (e820_get_entry(i, E820_RAM, &address, &length)) { - smbios_build_type_19_table(instance++, address, length); - } - } - - smbios_build_type_32_table(); - smbios_build_type_127_table(); - - smbios_validate_table(); - smbios_entry_point_setup(); - smbios_immutable = true; - } - - /* return tables blob and entry point (anchor), and their sizes */ - *tables = smbios_tables; - *tables_len = smbios_tables_len; - *anchor = (uint8_t *)&ep; - *anchor_len = sizeof(struct smbios_entry_point); -} - -static void save_opt(const char **dest, QemuOpts *opts, const char *name) -{ - const char *val = qemu_opt_get(opts, name); - - if (val) { - *dest = val; - } -} - -void smbios_entry_add(QemuOpts *opts) -{ - Error *local_err = NULL; - const char *val; - - assert(!smbios_immutable); - - val = qemu_opt_get(opts, "file"); - if (val) { - struct smbios_structure_header *header; - int size; - struct smbios_table *table; /* legacy mode only */ - - qemu_opts_validate(opts, qemu_smbios_file_opts, &local_err); - if (local_err) { - error_report_err(local_err); - exit(1); - } - - size = get_image_size(val); - if (size == -1 || size < sizeof(struct smbios_structure_header)) { - error_report("Cannot read SMBIOS file %s", val); - exit(1); - } - - /* - * NOTE: standard double '\0' terminator expected, per smbios spec. - * (except in legacy mode, where the second '\0' is implicit and - * will be inserted by the BIOS). - */ - smbios_tables = g_realloc(smbios_tables, smbios_tables_len + size); - header = (struct smbios_structure_header *)(smbios_tables + - smbios_tables_len); - - if (load_image(val, (uint8_t *)header) != size) { - error_report("Failed to load SMBIOS file %s", val); - exit(1); - } - - if (test_bit(header->type, have_fields_bitmap)) { - error_report("can't load type %d struct, fields already specified!", - header->type); - exit(1); - } - set_bit(header->type, have_binfile_bitmap); - - if (header->type == 4) { - smbios_type4_count++; - } - - smbios_tables_len += size; - if (size > smbios_table_max) { - smbios_table_max = size; - } - smbios_table_cnt++; - - /* add a copy of the newly loaded blob to legacy smbios_entries */ - /* NOTE: This code runs before smbios_set_defaults(), so we don't - * yet know which mode (legacy vs. aggregate-table) will be - * required. We therefore add the binary blob to both legacy - * (smbios_entries) and aggregate (smbios_tables) tables, and - * delete the one we don't need from smbios_set_defaults(), - * once we know which machine version has been requested. - */ - if (!smbios_entries) { - smbios_entries_len = sizeof(uint16_t); - smbios_entries = g_malloc0(smbios_entries_len); - } - smbios_entries = g_realloc(smbios_entries, smbios_entries_len + - size + sizeof(*table)); - table = (struct smbios_table *)(smbios_entries + smbios_entries_len); - table->header.type = SMBIOS_TABLE_ENTRY; - table->header.length = cpu_to_le16(sizeof(*table) + size); - memcpy(table->data, header, size); - smbios_entries_len += sizeof(*table) + size; - (*(uint16_t *)smbios_entries) = - cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); - /* end: add a copy of the newly loaded blob to legacy smbios_entries */ - - return; - } - - val = qemu_opt_get(opts, "type"); - if (val) { - unsigned long type = strtoul(val, NULL, 0); - - if (type > SMBIOS_MAX_TYPE) { - error_report("out of range!"); - exit(1); - } - - if (test_bit(type, have_binfile_bitmap)) { - error_report("can't add fields, binary file already loaded!"); - exit(1); - } - set_bit(type, have_fields_bitmap); - - switch (type) { - case 0: - qemu_opts_validate(opts, qemu_smbios_type0_opts, &local_err); - if (local_err) { - error_report_err(local_err); - exit(1); - } - save_opt(&type0.vendor, opts, "vendor"); - save_opt(&type0.version, opts, "version"); - save_opt(&type0.date, opts, "date"); - type0.uefi = qemu_opt_get_bool(opts, "uefi", false); - - val = qemu_opt_get(opts, "release"); - if (val) { - if (sscanf(val, "%hhu.%hhu", &type0.major, &type0.minor) != 2) { - error_report("Invalid release"); - exit(1); - } - type0.have_major_minor = true; - } - return; - case 1: - qemu_opts_validate(opts, qemu_smbios_type1_opts, &local_err); - if (local_err) { - error_report_err(local_err); - exit(1); - } - save_opt(&type1.manufacturer, opts, "manufacturer"); - save_opt(&type1.product, opts, "product"); - save_opt(&type1.version, opts, "version"); - save_opt(&type1.serial, opts, "serial"); - save_opt(&type1.sku, opts, "sku"); - save_opt(&type1.family, opts, "family"); - - val = qemu_opt_get(opts, "uuid"); - if (val) { - if (qemu_uuid_parse(val, qemu_uuid) != 0) { - error_report("Invalid UUID"); - exit(1); - } - qemu_uuid_set = true; - } - return; - case 2: - qemu_opts_validate(opts, qemu_smbios_type2_opts, &local_err); - if (local_err) { - error_report_err(local_err); - exit(1); - } - save_opt(&type2.manufacturer, opts, "manufacturer"); - save_opt(&type2.product, opts, "product"); - save_opt(&type2.version, opts, "version"); - save_opt(&type2.serial, opts, "serial"); - save_opt(&type2.asset, opts, "asset"); - save_opt(&type2.location, opts, "location"); - return; - case 3: - qemu_opts_validate(opts, qemu_smbios_type3_opts, &local_err); - if (local_err) { - error_report_err(local_err); - exit(1); - } - save_opt(&type3.manufacturer, opts, "manufacturer"); - save_opt(&type3.version, opts, "version"); - save_opt(&type3.serial, opts, "serial"); - save_opt(&type3.asset, opts, "asset"); - save_opt(&type3.sku, opts, "sku"); - return; - case 4: - qemu_opts_validate(opts, qemu_smbios_type4_opts, &local_err); - if (local_err) { - error_report_err(local_err); - exit(1); - } - save_opt(&type4.sock_pfx, opts, "sock_pfx"); - save_opt(&type4.manufacturer, opts, "manufacturer"); - save_opt(&type4.version, opts, "version"); - save_opt(&type4.serial, opts, "serial"); - save_opt(&type4.asset, opts, "asset"); - save_opt(&type4.part, opts, "part"); - return; - case 17: - qemu_opts_validate(opts, qemu_smbios_type17_opts, &local_err); - if (local_err) { - error_report_err(local_err); - exit(1); - } - save_opt(&type17.loc_pfx, opts, "loc_pfx"); - save_opt(&type17.bank, opts, "bank"); - save_opt(&type17.manufacturer, opts, "manufacturer"); - save_opt(&type17.serial, opts, "serial"); - save_opt(&type17.asset, opts, "asset"); - save_opt(&type17.part, opts, "part"); - type17.speed = qemu_opt_get_number(opts, "speed", 0); - return; - default: - error_report("Don't know how to build fields for SMBIOS type %ld", - type); - exit(1); - } - } - - error_report("Must specify type= or file="); - exit(1); -} diff --git a/qemu/hw/i386/xen/xen_apic.c b/qemu/hw/i386/xen/xen_apic.c index f5acd6a09..21d68ee04 100644 --- a/qemu/hw/i386/xen/xen_apic.c +++ b/qemu/hw/i386/xen/xen_apic.c @@ -9,6 +9,7 @@ * 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 "hw/i386/apic_internal.h" #include "hw/pci/msi.h" #include "hw/xen/xen.h" @@ -43,11 +44,7 @@ static void xen_apic_realize(DeviceState *dev, Error **errp) s->vapic_control = 0; memory_region_init_io(&s->io_memory, OBJECT(s), &xen_apic_io_ops, s, "xen-apic-msi", APIC_SPACE_SIZE); - -#if defined(CONFIG_XEN_CTRL_INTERFACE_VERSION) \ - && CONFIG_XEN_CTRL_INTERFACE_VERSION >= 420 - msi_supported = true; -#endif + msi_nonbroken = true; } static void xen_apic_set_base(APICCommonState *s, uint64_t val) diff --git a/qemu/hw/i386/xen/xen_platform.c b/qemu/hw/i386/xen/xen_platform.c index 28b324a6f..aa7839324 100644 --- a/qemu/hw/i386/xen/xen_platform.c +++ b/qemu/hw/i386/xen/xen_platform.c @@ -23,8 +23,8 @@ * THE SOFTWARE. */ -#include - +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/ide.h" @@ -35,6 +35,7 @@ #include "trace.h" #include "exec/address-spaces.h" #include "sysemu/block-backend.h" +#include "qemu/error-report.h" #include @@ -384,11 +385,17 @@ static const VMStateDescription vmstate_xen_platform = { } }; -static int xen_platform_initfn(PCIDevice *dev) +static void xen_platform_realize(PCIDevice *dev, Error **errp) { PCIXenPlatformState *d = XEN_PLATFORM(dev); uint8_t *pci_conf; + /* Device will crash on reset if xen is not initialized */ + if (!xen_enabled()) { + error_setg(errp, "xen-platform device requires the Xen accelerator"); + return; + } + pci_conf = dev->config; pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY); @@ -406,8 +413,6 @@ static int xen_platform_initfn(PCIDevice *dev) &d->mmio_bar); platform_fixed_ioport_init(d); - - return 0; } static void platform_reset(DeviceState *dev) @@ -422,7 +427,7 @@ static void xen_platform_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = xen_platform_initfn; + k->realize = xen_platform_realize; k->vendor_id = PCI_VENDOR_ID_XEN; k->device_id = PCI_DEVICE_ID_XEN_PLATFORM; k->class_id = PCI_CLASS_OTHERS << 8 | 0x80; diff --git a/qemu/hw/i386/xen/xen_pvdevice.c b/qemu/hw/i386/xen/xen_pvdevice.c index c2189473b..c093b3445 100644 --- a/qemu/hw/i386/xen/xen_pvdevice.c +++ b/qemu/hw/i386/xen/xen_pvdevice.c @@ -29,6 +29,8 @@ * SUCH DAMAGE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "trace.h" @@ -69,14 +71,16 @@ static const MemoryRegionOps xen_pv_mmio_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static int xen_pv_init(PCIDevice *pci_dev) +static void xen_pv_realize(PCIDevice *pci_dev, Error **errp) { XenPVDevice *d = XEN_PV_DEVICE(pci_dev); uint8_t *pci_conf; /* device-id property must always be supplied */ - if (d->device_id == 0xffff) - return -1; + if (d->device_id == 0xffff) { + error_setg(errp, "Device ID invalid, it must always be supplied"); + return; + } pci_conf = pci_dev->config; @@ -97,8 +101,6 @@ static int xen_pv_init(PCIDevice *pci_dev) pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_MEM_PREFETCH, &d->mmio); - - return 0; } static Property xen_pv_props[] = { @@ -114,7 +116,7 @@ static void xen_pv_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = xen_pv_init; + k->realize = xen_pv_realize; k->class_id = PCI_CLASS_SYSTEM_OTHER; dc->desc = "Xen PV Device"; dc->props = xen_pv_props; diff --git a/qemu/hw/ide/ahci.c b/qemu/hw/ide/ahci.c index 48749c1dc..f244bc01c 100644 --- a/qemu/hw/ide/ahci.c +++ b/qemu/hw/ide/ahci.c @@ -21,11 +21,11 @@ * */ +#include "qemu/osdep.h" #include #include #include #include -#include #include "qemu/error-report.h" #include "sysemu/block-backend.h" @@ -47,10 +47,9 @@ do { \ static void check_cmd(AHCIState *s, int port); static int handle_cmd(AHCIState *s, int port, uint8_t slot); static void ahci_reset_port(AHCIState *s, int port); -static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis); +static bool ahci_write_fis_d2h(AHCIDevice *ad); static void ahci_init_d2h(AHCIDevice *ad); static int ahci_dma_prepare_buf(IDEDMA *dma, int32_t limit); -static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes); static bool ahci_map_clb_address(AHCIDevice *ad); static bool ahci_map_fis_address(AHCIDevice *ad); static void ahci_unmap_clb_address(AHCIDevice *ad); @@ -122,9 +121,9 @@ static uint32_t ahci_port_read(AHCIState *s, int port, int offset) static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev) { - AHCIPCIState *d = container_of(s, AHCIPCIState, ahci); - PCIDevice *pci_dev = - (PCIDevice *)object_dynamic_cast(OBJECT(d), TYPE_PCI_DEVICE); + DeviceState *dev_state = s->container; + PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state), + TYPE_PCI_DEVICE); DPRINTF(0, "raise irq\n"); @@ -137,9 +136,9 @@ static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev) static void ahci_irq_lower(AHCIState *s, AHCIDevice *dev) { - AHCIPCIState *d = container_of(s, AHCIPCIState, ahci); - PCIDevice *pci_dev = - (PCIDevice *)object_dynamic_cast(OBJECT(d), TYPE_PCI_DEVICE); + DeviceState *dev_state = s->container; + PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state), + TYPE_PCI_DEVICE); DPRINTF(0, "lower irq\n"); @@ -200,52 +199,38 @@ static void map_page(AddressSpace *as, uint8_t **ptr, uint64_t addr, * Check the cmd register to see if we should start or stop * the DMA or FIS RX engines. * - * @ad: Device to engage. - * @allow_stop: Allow device to transition from started to stopped? - * 'no' is useful for migration post_load, which does not expect a transition. + * @ad: Device to dis/engage. * * @return 0 on success, -1 on error. */ -static int ahci_cond_start_engines(AHCIDevice *ad, bool allow_stop) +static int ahci_cond_start_engines(AHCIDevice *ad) { AHCIPortRegs *pr = &ad->port_regs; - - if (pr->cmd & PORT_CMD_START) { - if (ahci_map_clb_address(ad)) { - pr->cmd |= PORT_CMD_LIST_ON; - } else { + bool cmd_start = pr->cmd & PORT_CMD_START; + bool cmd_on = pr->cmd & PORT_CMD_LIST_ON; + bool fis_start = pr->cmd & PORT_CMD_FIS_RX; + bool fis_on = pr->cmd & PORT_CMD_FIS_ON; + + if (cmd_start && !cmd_on) { + if (!ahci_map_clb_address(ad)) { + pr->cmd &= ~PORT_CMD_START; error_report("AHCI: Failed to start DMA engine: " "bad command list buffer address"); return -1; } - } else if (pr->cmd & PORT_CMD_LIST_ON) { - if (allow_stop) { - ahci_unmap_clb_address(ad); - pr->cmd = pr->cmd & ~(PORT_CMD_LIST_ON); - } else { - error_report("AHCI: DMA engine should be off, " - "but appears to still be running"); - return -1; - } + } else if (!cmd_start && cmd_on) { + ahci_unmap_clb_address(ad); } - if (pr->cmd & PORT_CMD_FIS_RX) { - if (ahci_map_fis_address(ad)) { - pr->cmd |= PORT_CMD_FIS_ON; - } else { + if (fis_start && !fis_on) { + if (!ahci_map_fis_address(ad)) { + pr->cmd &= ~PORT_CMD_FIS_RX; error_report("AHCI: Failed to start FIS receive engine: " "bad FIS receive buffer address"); return -1; } - } else if (pr->cmd & PORT_CMD_FIS_ON) { - if (allow_stop) { - ahci_unmap_fis_address(ad); - pr->cmd = pr->cmd & ~(PORT_CMD_FIS_ON); - } else { - error_report("AHCI: FIS receive engine should be off, " - "but appears to still be running"); - return -1; - } + } else if (!fis_start && fis_on) { + ahci_unmap_fis_address(ad); } return 0; @@ -287,8 +272,8 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) pr->cmd = (pr->cmd & PORT_CMD_RO_MASK) | (val & ~(PORT_CMD_RO_MASK|PORT_CMD_ICC_MASK)); - /* Check FIS RX and CLB engines, allow transition to false: */ - ahci_cond_start_engines(&s->dev[port], true); + /* Check FIS RX and CLB engines */ + ahci_cond_start_engines(&s->dev[port]); /* XXX usually the FIS would be pending on the bus here and issuing deferred until the OS enables FIS receival. @@ -297,7 +282,6 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) if ((pr->cmd & PORT_CMD_FIS_ON) && !s->dev[port].init_d2h_sent) { ahci_init_d2h(&s->dev[port]); - s->dev[port].init_d2h_sent = true; } check_cmd(s, port); @@ -381,17 +365,23 @@ static uint64_t ahci_mem_read(void *opaque, hwaddr addr, unsigned size) int ofst = addr - aligned; uint64_t lo = ahci_mem_read_32(opaque, aligned); uint64_t hi; + uint64_t val; /* if < 8 byte read does not cross 4 byte boundary */ if (ofst + size <= 4) { - return lo >> (ofst * 8); + val = lo >> (ofst * 8); + } else { + g_assert_cmpint(size, >, 1); + + /* If the 64bit read is unaligned, we will produce undefined + * results. AHCI does not support unaligned 64bit reads. */ + hi = ahci_mem_read_32(opaque, aligned + 4); + val = (hi << 32 | lo) >> (ofst * 8); } - g_assert_cmpint(size, >, 1); - /* If the 64bit read is unaligned, we will produce undefined - * results. AHCI does not support unaligned 64bit reads. */ - hi = ahci_mem_read_32(opaque, aligned + 4); - return (hi << 32 | lo) >> (ofst * 8); + DPRINTF(-1, "addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 ", size=%d\n", + addr, val, size); + return val; } @@ -400,6 +390,9 @@ static void ahci_mem_write(void *opaque, hwaddr addr, { AHCIState *s = opaque; + DPRINTF(-1, "addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 ", size=%d\n", + addr, val, size); + /* Only aligned reads are allowed on AHCI */ if (addr & 3) { fprintf(stderr, "ahci: Mis-aligned write to addr 0x" @@ -540,20 +533,33 @@ static void ahci_check_cmd_bh(void *opaque) static void ahci_init_d2h(AHCIDevice *ad) { - uint8_t init_fis[20]; IDEState *ide_state = &ad->port.ifs[0]; + AHCIPortRegs *pr = &ad->port_regs; - memset(init_fis, 0, sizeof(init_fis)); - - init_fis[4] = 1; - init_fis[12] = 1; + if (ad->init_d2h_sent) { + return; + } - if (ide_state->drive_kind == IDE_CD) { - init_fis[5] = ide_state->lcyl; - init_fis[6] = ide_state->hcyl; + if (ahci_write_fis_d2h(ad)) { + ad->init_d2h_sent = true; + /* We're emulating receiving the first Reg H2D Fis from the device; + * Update the SIG register, but otherwise proceed as normal. */ + pr->sig = ((uint32_t)ide_state->hcyl << 24) | + (ide_state->lcyl << 16) | + (ide_state->sector << 8) | + (ide_state->nsector & 0xFF); } +} - ahci_write_fis_d2h(ad, init_fis); +static void ahci_set_signature(AHCIDevice *ad, uint32_t sig) +{ + IDEState *s = &ad->port.ifs[0]; + s->hcyl = sig >> 24 & 0xFF; + s->lcyl = sig >> 16 & 0xFF; + s->sector = sig >> 8 & 0xFF; + s->nsector = sig & 0xFF; + + DPRINTF(ad->port_no, "set hcyl:lcyl:sect:nsect = 0x%08x\n", sig); } static void ahci_reset_port(AHCIState *s, int port) @@ -604,17 +610,11 @@ static void ahci_reset_port(AHCIState *s, int port) } s->dev[port].port_state = STATE_RUN; - if (!ide_state->blk) { - pr->sig = 0; - ide_state->status = SEEK_STAT | WRERR_STAT; - } else if (ide_state->drive_kind == IDE_CD) { - pr->sig = SATA_SIGNATURE_CDROM; - ide_state->lcyl = 0x14; - ide_state->hcyl = 0xeb; - DPRINTF(port, "set lcyl = %d\n", ide_state->lcyl); + if (ide_state->drive_kind == IDE_CD) { + ahci_set_signature(d, SATA_SIGNATURE_CDROM);\ ide_state->status = SEEK_STAT | WRERR_STAT | READY_STAT; } else { - pr->sig = SATA_SIGNATURE_DISK; + ahci_set_signature(d, SATA_SIGNATURE_DISK); ide_state->status = SEEK_STAT | WRERR_STAT; } @@ -643,11 +643,22 @@ static bool ahci_map_fis_address(AHCIDevice *ad) AHCIPortRegs *pr = &ad->port_regs; map_page(ad->hba->as, &ad->res_fis, ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); - return ad->res_fis != NULL; + if (ad->res_fis != NULL) { + pr->cmd |= PORT_CMD_FIS_ON; + return true; + } + + pr->cmd &= ~PORT_CMD_FIS_ON; + return false; } static void ahci_unmap_fis_address(AHCIDevice *ad) { + if (ad->res_fis == NULL) { + DPRINTF(ad->port_no, "Attempt to unmap NULL FIS address\n"); + return; + } + ad->port_regs.cmd &= ~PORT_CMD_FIS_ON; dma_memory_unmap(ad->hba->as, ad->res_fis, 256, DMA_DIRECTION_FROM_DEVICE, 256); ad->res_fis = NULL; @@ -659,11 +670,22 @@ static bool ahci_map_clb_address(AHCIDevice *ad) ad->cur_cmd = NULL; map_page(ad->hba->as, &ad->lst, ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); - return ad->lst != NULL; + if (ad->lst != NULL) { + pr->cmd |= PORT_CMD_LIST_ON; + return true; + } + + pr->cmd &= ~PORT_CMD_LIST_ON; + return false; } static void ahci_unmap_clb_address(AHCIDevice *ad) { + if (ad->lst == NULL) { + DPRINTF(ad->port_no, "Attempt to unmap NULL CLB address\n"); + return; + } + ad->port_regs.cmd &= ~PORT_CMD_LIST_ON; dma_memory_unmap(ad->hba->as, ad->lst, 1024, DMA_DIRECTION_FROM_DEVICE, 1024); ad->lst = NULL; @@ -750,7 +772,7 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) ahci_trigger_irq(ad->hba, ad, PORT_IRQ_PIOS_FIS); } -static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis) +static bool ahci_write_fis_d2h(AHCIDevice *ad) { AHCIPortRegs *pr = &ad->port_regs; uint8_t *d2h_fis; @@ -758,7 +780,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis) IDEState *s = &ad->port.ifs[0]; if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) { - return; + return false; } d2h_fis = &ad->res_fis[RES_FIS_RFIS]; @@ -791,6 +813,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis) } ahci_trigger_irq(ad->hba, ad, PORT_IRQ_D2H_REG_FIS); + return true; } static int prdt_tbl_entry_size(const AHCI_SG *tbl) @@ -799,8 +822,21 @@ static int prdt_tbl_entry_size(const AHCI_SG *tbl) return (le32_to_cpu(tbl->flags_size) & AHCI_PRDT_SIZE_MASK) + 1; } +/** + * Fetch entries in a guest-provided PRDT and convert it into a QEMU SGlist. + * @ad: The AHCIDevice for whom we are building the SGList. + * @sglist: The SGList target to add PRD entries to. + * @cmd: The AHCI Command Header that describes where the PRDT is. + * @limit: The remaining size of the S/ATA transaction, in bytes. + * @offset: The number of bytes already transferred, in bytes. + * + * The AHCI PRDT can describe up to 256GiB. S/ATA only support transactions of + * up to 32MiB as of ATA8-ACS3 rev 1b, assuming a 512 byte sector size. We stop + * building the sglist from the PRDT as soon as we hit @limit bytes, + * which is <= INT32_MAX/2GiB. + */ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, - AHCICmdHdr *cmd, int64_t limit, int32_t offset) + AHCICmdHdr *cmd, int64_t limit, uint64_t offset) { uint16_t opts = le16_to_cpu(cmd->opts); uint16_t prdtl = le16_to_cpu(cmd->prdtl); @@ -818,14 +854,6 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, IDEBus *bus = &ad->port; BusState *qbus = BUS(bus); - /* - * Note: AHCI PRDT can describe up to 256GiB. SATA/ATA only support - * transactions of up to 32MiB as of ATA8-ACS3 rev 1b, assuming a - * 512 byte sector size. We limit the PRDT in this implementation to - * a reasonably large 2GiB, which can accommodate the maximum transfer - * request for sector sizes up to 32K. - */ - if (!prdtl) { DPRINTF(ad->port_no, "no sg list given by guest: 0x%08x\n", opts); return -1; @@ -875,13 +903,6 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr), MIN(prdt_tbl_entry_size(&tbl[i]), limit - sglist->size)); - if (sglist->size > INT32_MAX) { - error_report("AHCI Physical Region Descriptor Table describes " - "more than 2 GiB.\n"); - qemu_sglist_destroy(sglist); - r = -1; - goto out; - } } } @@ -898,6 +919,7 @@ static void ncq_err(NCQTransferState *ncq_tfs) ide_state->error = ABRT_ERR; ide_state->status = READY_STAT | ERR_STAT; ncq_tfs->drive->port_regs.scr_err |= (1 << ncq_tfs->tag); + ncq_tfs->used = 0; } static void ncq_finish(NCQTransferState *ncq_tfs) @@ -1290,7 +1312,7 @@ out: s->data_ptr = s->data_end; /* Update number of transferred bytes, destroy sglist */ - ahci_commit_buf(dma, size); + dma_buf_commit(s, size); s->end_transfer_func(s); @@ -1332,9 +1354,8 @@ static void ahci_restart(IDEDMA *dma) } /** - * Called in DMA R/W chains to read the PRDT, utilizing ahci_populate_sglist. - * Not currently invoked by PIO R/W chains, - * which invoke ahci_populate_sglist via ahci_start_transfer. + * Called in DMA and PIO R/W chains to read the PRDT. + * Not shared with NCQ pathways. */ static int32_t ahci_dma_prepare_buf(IDEDMA *dma, int32_t limit) { @@ -1353,21 +1374,16 @@ static int32_t ahci_dma_prepare_buf(IDEDMA *dma, int32_t limit) } /** - * Destroys the scatter-gather list, - * and updates the command header with a bytes-read value. - * called explicitly via ahci_dma_rw_buf (ATAPI DMA), - * and ahci_start_transfer (PIO R/W), - * and called via callback from ide_dma_cb for DMA R/W paths. + * Updates the command header with a bytes-read value. + * Called via dma_buf_commit, for both DMA and PIO paths. + * sglist destruction is handled within dma_buf_commit. */ static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); - IDEState *s = &ad->port.ifs[0]; tx_bytes += le32_to_cpu(ad->cur_cmd->status); ad->cur_cmd->status = cpu_to_le32(tx_bytes); - - qemu_sglist_destroy(&s->sg); } static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) @@ -1388,10 +1404,9 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) } /* free sglist, update byte count */ - ahci_commit_buf(dma, l); + dma_buf_commit(s, l); s->io_buffer_index += l; - s->io_buffer_offset += l; DPRINTF(ad->port_no, "len=%#x\n", l); @@ -1405,7 +1420,7 @@ static void ahci_cmd_done(IDEDMA *dma) DPRINTF(ad->port_no, "cmd done\n"); /* update d2h status */ - ahci_write_fis_d2h(ad, NULL); + ahci_write_fis_d2h(ad); if (!ad->check_bh) { /* maybe we still have something to process, check later */ @@ -1429,7 +1444,17 @@ static const IDEDMAOps ahci_dma_ops = { .cmd_done = ahci_cmd_done, }; -void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports) +void ahci_init(AHCIState *s, DeviceState *qdev) +{ + s->container = qdev; + /* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */ + memory_region_init_io(&s->mem, OBJECT(qdev), &ahci_mem_ops, s, + "ahci", AHCI_MEM_BAR_SIZE); + memory_region_init_io(&s->idp, OBJECT(qdev), &ahci_idp_ops, s, + "ahci-idp", 32); +} + +void ahci_realize(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports) { qemu_irq *irqs; int i; @@ -1438,14 +1463,7 @@ void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports) s->ports = ports; s->dev = g_new0(AHCIDevice, ports); ahci_reg_init(s); - /* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */ - memory_region_init_io(&s->mem, OBJECT(qdev), &ahci_mem_ops, s, - "ahci", AHCI_MEM_BAR_SIZE); - memory_region_init_io(&s->idp, OBJECT(qdev), &ahci_idp_ops, s, - "ahci-idp", 32); - irqs = qemu_allocate_irqs(ahci_irq_set, s, s->ports); - for (i = 0; i < s->ports; i++) { AHCIDevice *ad = &s->dev[i]; @@ -1542,14 +1560,28 @@ static int ahci_state_post_load(void *opaque, int version_id) int i, j; struct AHCIDevice *ad; NCQTransferState *ncq_tfs; + AHCIPortRegs *pr; AHCIState *s = opaque; for (i = 0; i < s->ports; i++) { ad = &s->dev[i]; + pr = &ad->port_regs; + + if (!(pr->cmd & PORT_CMD_START) && (pr->cmd & PORT_CMD_LIST_ON)) { + error_report("AHCI: DMA engine should be off, but status bit " + "indicates it is still running."); + return -1; + } + if (!(pr->cmd & PORT_CMD_FIS_RX) && (pr->cmd & PORT_CMD_FIS_ON)) { + error_report("AHCI: FIS RX engine should be off, but status bit " + "indicates it is still running."); + return -1; + } - /* Only remap the CLB address if appropriate, disallowing a state - * transition from 'on' to 'off' it should be consistent here. */ - if (ahci_cond_start_engines(ad, false) != 0) { + /* After a migrate, the DMA/FIS engines are "off" and + * need to be conditionally restarted */ + pr->cmd &= ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON); + if (ahci_cond_start_engines(ad) != 0) { return -1; } @@ -1625,18 +1657,6 @@ const VMStateDescription vmstate_ahci = { }, }; -#define TYPE_SYSBUS_AHCI "sysbus-ahci" -#define SYSBUS_AHCI(obj) OBJECT_CHECK(SysbusAHCIState, (obj), TYPE_SYSBUS_AHCI) - -typedef struct SysbusAHCIState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - AHCIState ahci; - uint32_t num_ports; -} SysbusAHCIState; - static const VMStateDescription vmstate_sysbus_ahci = { .name = "sysbus-ahci", .fields = (VMStateField[]) { @@ -1652,17 +1672,24 @@ static void sysbus_ahci_reset(DeviceState *dev) ahci_reset(&s->ahci); } -static void sysbus_ahci_realize(DeviceState *dev, Error **errp) +static void sysbus_ahci_init(Object *obj) { - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - SysbusAHCIState *s = SYSBUS_AHCI(dev); + SysbusAHCIState *s = SYSBUS_AHCI(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - ahci_init(&s->ahci, dev, &address_space_memory, s->num_ports); + ahci_init(&s->ahci, DEVICE(obj)); sysbus_init_mmio(sbd, &s->ahci.mem); sysbus_init_irq(sbd, &s->ahci.irq); } +static void sysbus_ahci_realize(DeviceState *dev, Error **errp) +{ + SysbusAHCIState *s = SYSBUS_AHCI(dev); + + ahci_realize(&s->ahci, dev, &address_space_memory, s->num_ports); +} + static Property sysbus_ahci_properties[] = { DEFINE_PROP_UINT32("num-ports", SysbusAHCIState, num_ports, 1), DEFINE_PROP_END_OF_LIST(), @@ -1683,12 +1710,108 @@ static const TypeInfo sysbus_ahci_info = { .name = TYPE_SYSBUS_AHCI, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SysbusAHCIState), + .instance_init = sysbus_ahci_init, .class_init = sysbus_ahci_class_init, }; +#define ALLWINNER_AHCI_BISTAFR ((0xa0 - ALLWINNER_AHCI_MMIO_OFF) / 4) +#define ALLWINNER_AHCI_BISTCR ((0xa4 - ALLWINNER_AHCI_MMIO_OFF) / 4) +#define ALLWINNER_AHCI_BISTFCTR ((0xa8 - ALLWINNER_AHCI_MMIO_OFF) / 4) +#define ALLWINNER_AHCI_BISTSR ((0xac - ALLWINNER_AHCI_MMIO_OFF) / 4) +#define ALLWINNER_AHCI_BISTDECR ((0xb0 - ALLWINNER_AHCI_MMIO_OFF) / 4) +#define ALLWINNER_AHCI_DIAGNR0 ((0xb4 - ALLWINNER_AHCI_MMIO_OFF) / 4) +#define ALLWINNER_AHCI_DIAGNR1 ((0xb8 - ALLWINNER_AHCI_MMIO_OFF) / 4) +#define ALLWINNER_AHCI_OOBR ((0xbc - ALLWINNER_AHCI_MMIO_OFF) / 4) +#define ALLWINNER_AHCI_PHYCS0R ((0xc0 - ALLWINNER_AHCI_MMIO_OFF) / 4) +#define ALLWINNER_AHCI_PHYCS1R ((0xc4 - ALLWINNER_AHCI_MMIO_OFF) / 4) +#define ALLWINNER_AHCI_PHYCS2R ((0xc8 - ALLWINNER_AHCI_MMIO_OFF) / 4) +#define ALLWINNER_AHCI_TIMER1MS ((0xe0 - ALLWINNER_AHCI_MMIO_OFF) / 4) +#define ALLWINNER_AHCI_GPARAM1R ((0xe8 - ALLWINNER_AHCI_MMIO_OFF) / 4) +#define ALLWINNER_AHCI_GPARAM2R ((0xec - ALLWINNER_AHCI_MMIO_OFF) / 4) +#define ALLWINNER_AHCI_PPARAMR ((0xf0 - ALLWINNER_AHCI_MMIO_OFF) / 4) +#define ALLWINNER_AHCI_TESTR ((0xf4 - ALLWINNER_AHCI_MMIO_OFF) / 4) +#define ALLWINNER_AHCI_VERSIONR ((0xf8 - ALLWINNER_AHCI_MMIO_OFF) / 4) +#define ALLWINNER_AHCI_IDR ((0xfc - ALLWINNER_AHCI_MMIO_OFF) / 4) +#define ALLWINNER_AHCI_RWCR ((0xfc - ALLWINNER_AHCI_MMIO_OFF) / 4) + +static uint64_t allwinner_ahci_mem_read(void *opaque, hwaddr addr, + unsigned size) +{ + AllwinnerAHCIState *a = opaque; + uint64_t val = a->regs[addr/4]; + + switch (addr / 4) { + case ALLWINNER_AHCI_PHYCS0R: + val |= 0x2 << 28; + break; + case ALLWINNER_AHCI_PHYCS2R: + val &= ~(0x1 << 24); + break; + } + DPRINTF(-1, "addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 ", size=%d\n", + addr, val, size); + return val; +} + +static void allwinner_ahci_mem_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + AllwinnerAHCIState *a = opaque; + + DPRINTF(-1, "addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 ", size=%d\n", + addr, val, size); + a->regs[addr/4] = val; +} + +static const MemoryRegionOps allwinner_ahci_mem_ops = { + .read = allwinner_ahci_mem_read, + .write = allwinner_ahci_mem_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void allwinner_ahci_init(Object *obj) +{ + SysbusAHCIState *s = SYSBUS_AHCI(obj); + AllwinnerAHCIState *a = ALLWINNER_AHCI(obj); + + memory_region_init_io(&a->mmio, OBJECT(obj), &allwinner_ahci_mem_ops, a, + "allwinner-ahci", ALLWINNER_AHCI_MMIO_SIZE); + memory_region_add_subregion(&s->ahci.mem, ALLWINNER_AHCI_MMIO_OFF, + &a->mmio); +} + +static const VMStateDescription vmstate_allwinner_ahci = { + .name = "allwinner-ahci", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AllwinnerAHCIState, + ALLWINNER_AHCI_MMIO_SIZE/4), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_ahci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_allwinner_ahci; +} + +static const TypeInfo allwinner_ahci_info = { + .name = TYPE_ALLWINNER_AHCI, + .parent = TYPE_SYSBUS_AHCI, + .instance_size = sizeof(AllwinnerAHCIState), + .instance_init = allwinner_ahci_init, + .class_init = allwinner_ahci_class_init, +}; + static void sysbus_ahci_register_types(void) { type_register_static(&sysbus_ahci_info); + type_register_static(&allwinner_ahci_info); } type_init(sysbus_ahci_register_types) diff --git a/qemu/hw/ide/ahci.h b/qemu/hw/ide/ahci.h index 79a463d93..bc777ed5c 100644 --- a/qemu/hw/ide/ahci.h +++ b/qemu/hw/ide/ahci.h @@ -24,6 +24,8 @@ #ifndef HW_IDE_AHCI_H #define HW_IDE_AHCI_H +#include + #define AHCI_MEM_BAR_SIZE 0x1000 #define AHCI_MAX_PORTS 32 #define AHCI_MAX_SG 168 /* hardware max is 64K */ @@ -285,6 +287,8 @@ struct AHCIDevice { }; typedef struct AHCIState { + DeviceState *container; + AHCIDevice *dev; AHCIControlRegs control_regs; MemoryRegion mem; @@ -362,11 +366,40 @@ typedef struct SDBFIS { uint32_t payload; } QEMU_PACKED SDBFIS; -void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports); +void ahci_realize(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports); +void ahci_init(AHCIState *s, DeviceState *qdev); void ahci_uninit(AHCIState *s); void ahci_reset(AHCIState *s); void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **hd); +#define TYPE_SYSBUS_AHCI "sysbus-ahci" +#define SYSBUS_AHCI(obj) OBJECT_CHECK(SysbusAHCIState, (obj), TYPE_SYSBUS_AHCI) + +typedef struct SysbusAHCIState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + AHCIState ahci; + uint32_t num_ports; +} SysbusAHCIState; + +#define TYPE_ALLWINNER_AHCI "allwinner-ahci" +#define ALLWINNER_AHCI(obj) OBJECT_CHECK(AllwinnerAHCIState, (obj), \ + TYPE_ALLWINNER_AHCI) + +#define ALLWINNER_AHCI_MMIO_OFF 0x80 +#define ALLWINNER_AHCI_MMIO_SIZE 0x80 + +struct AllwinnerAHCIState { + /*< private >*/ + SysbusAHCIState parent_obj; + /*< public >*/ + + MemoryRegion mmio; + uint32_t regs[ALLWINNER_AHCI_MMIO_SIZE/4]; +}; + #endif /* HW_IDE_AHCI_H */ diff --git a/qemu/hw/ide/atapi.c b/qemu/hw/ide/atapi.c index 79dd16710..2bb606c1c 100644 --- a/qemu/hw/ide/atapi.c +++ b/qemu/hw/ide/atapi.c @@ -23,6 +23,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/ide/internal.h" #include "hw/scsi/scsi.h" #include "sysemu/block-backend.h" @@ -105,33 +106,99 @@ static void cd_data_to_raw(uint8_t *buf, int lba) memset(buf, 0, 288); } -static int cd_read_sector(IDEState *s, int lba, uint8_t *buf, int sector_size) +static int +cd_read_sector_sync(IDEState *s) { int ret; + block_acct_start(blk_get_stats(s->blk), &s->acct, + 4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ); - switch(sector_size) { +#ifdef DEBUG_IDE_ATAPI + printf("cd_read_sector_sync: lba=%d\n", s->lba); +#endif + + switch (s->cd_sector_size) { case 2048: - block_acct_start(blk_get_stats(s->blk), &s->acct, - 4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ); - ret = blk_read(s->blk, (int64_t)lba << 2, buf, 4); - block_acct_done(blk_get_stats(s->blk), &s->acct); + ret = blk_read(s->blk, (int64_t)s->lba << 2, + s->io_buffer, 4); break; case 2352: - block_acct_start(blk_get_stats(s->blk), &s->acct, - 4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ); - ret = blk_read(s->blk, (int64_t)lba << 2, buf + 16, 4); - block_acct_done(blk_get_stats(s->blk), &s->acct); - if (ret < 0) - return ret; - cd_data_to_raw(buf, lba); + ret = blk_read(s->blk, (int64_t)s->lba << 2, + s->io_buffer + 16, 4); + if (ret >= 0) { + cd_data_to_raw(s->io_buffer, s->lba); + } break; default: - ret = -EIO; - break; + block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ); + return -EIO; + } + + if (ret < 0) { + block_acct_failed(blk_get_stats(s->blk), &s->acct); + } else { + block_acct_done(blk_get_stats(s->blk), &s->acct); + s->lba++; + s->io_buffer_index = 0; } + return ret; } +static void cd_read_sector_cb(void *opaque, int ret) +{ + IDEState *s = opaque; + +#ifdef DEBUG_IDE_ATAPI + printf("cd_read_sector_cb: lba=%d ret=%d\n", s->lba, ret); +#endif + + if (ret < 0) { + block_acct_failed(blk_get_stats(s->blk), &s->acct); + ide_atapi_io_error(s, ret); + return; + } + + block_acct_done(blk_get_stats(s->blk), &s->acct); + + if (s->cd_sector_size == 2352) { + cd_data_to_raw(s->io_buffer, s->lba); + } + + s->lba++; + s->io_buffer_index = 0; + s->status &= ~BUSY_STAT; + + ide_atapi_cmd_reply_end(s); +} + +static int cd_read_sector(IDEState *s) +{ + if (s->cd_sector_size != 2048 && s->cd_sector_size != 2352) { + block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ); + return -EINVAL; + } + + s->iov.iov_base = (s->cd_sector_size == 2352) ? + s->io_buffer + 16 : s->io_buffer; + + s->iov.iov_len = 4 * BDRV_SECTOR_SIZE; + qemu_iovec_init_external(&s->qiov, &s->iov, 1); + +#ifdef DEBUG_IDE_ATAPI + printf("cd_read_sector: lba=%d\n", s->lba); +#endif + + block_acct_start(blk_get_stats(s->blk), &s->acct, + 4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ); + + ide_buffered_readv(s, (int64_t)s->lba << 2, &s->qiov, 4, + cd_read_sector_cb, s); + + s->status |= BUSY_STAT; + return 0; +} + void ide_atapi_cmd_ok(IDEState *s) { s->error = 0; @@ -167,6 +234,17 @@ void ide_atapi_io_error(IDEState *s, int ret) } } +static uint16_t atapi_byte_count_limit(IDEState *s) +{ + uint16_t bcl; + + bcl = s->lcyl | (s->hcyl << 8); + if (bcl == 0xffff) { + return 0xfffe; + } + return bcl; +} + /* The whole ATAPI transfer logic is handled in this function */ void ide_atapi_cmd_reply_end(IDEState *s) { @@ -182,18 +260,27 @@ void ide_atapi_cmd_reply_end(IDEState *s) ide_atapi_cmd_ok(s); ide_set_irq(s->bus); #ifdef DEBUG_IDE_ATAPI - printf("status=0x%x\n", s->status); + printf("end of transfer, status=0x%x\n", s->status); #endif } else { /* see if a new sector must be read */ if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) { - ret = cd_read_sector(s, s->lba, s->io_buffer, s->cd_sector_size); - if (ret < 0) { - ide_atapi_io_error(s, ret); + if (!s->elementary_transfer_size) { + ret = cd_read_sector(s); + if (ret < 0) { + ide_atapi_io_error(s, ret); + } return; + } else { + /* rebuffering within an elementary transfer is + * only possible with a sync request because we + * end up with a race condition otherwise */ + ret = cd_read_sector_sync(s); + if (ret < 0) { + ide_atapi_io_error(s, ret); + return; + } } - s->lba++; - s->io_buffer_index = 0; } if (s->elementary_transfer_size > 0) { /* there are some data left to transmit in this elementary @@ -209,12 +296,10 @@ void ide_atapi_cmd_reply_end(IDEState *s) } else { /* a new transfer is needed */ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO; - byte_count_limit = s->lcyl | (s->hcyl << 8); + byte_count_limit = atapi_byte_count_limit(s); #ifdef DEBUG_IDE_ATAPI printf("byte_count_limit=%d\n", byte_count_limit); #endif - if (byte_count_limit == 0xffff) - byte_count_limit--; size = s->packet_transfer_size; if (size > byte_count_limit) { /* byte count limit must be even if this case */ @@ -275,7 +360,6 @@ static void ide_atapi_cmd_read_pio(IDEState *s, int lba, int nb_sectors, s->io_buffer_index = sector_size; s->cd_sector_size = sector_size; - s->status = READY_STAT | SEEK_STAT; ide_atapi_cmd_reply_end(s); } @@ -291,15 +375,18 @@ static void ide_atapi_cmd_check_status(IDEState *s) } /* ATAPI DMA support */ -/* XXX: handle read errors */ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret) { IDEState *s = opaque; int data_offset, n; if (ret < 0) { - ide_atapi_io_error(s, ret); - goto eot; + if (ide_handle_rw_error(s, -ret, ide_dma_cmd_to_retry(s->dma_cmd))) { + if (s->bus->error_status) { + return; + } + goto eot; + } } if (s->io_buffer_size > 0) { @@ -351,13 +438,17 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret) s->bus->dma->iov.iov_len = n * 4 * 512; qemu_iovec_init_external(&s->bus->dma->qiov, &s->bus->dma->iov, 1); - s->bus->dma->aiocb = blk_aio_readv(s->blk, (int64_t)s->lba << 2, - &s->bus->dma->qiov, n * 4, - ide_atapi_cmd_read_dma_cb, s); + s->bus->dma->aiocb = ide_buffered_readv(s, (int64_t)s->lba << 2, + &s->bus->dma->qiov, n * 4, + ide_atapi_cmd_read_dma_cb, s); return; eot: - block_acct_done(blk_get_stats(s->blk), &s->acct); + if (ret < 0) { + block_acct_failed(blk_get_stats(s->blk), &s->acct); + } else { + block_acct_done(blk_get_stats(s->blk), &s->acct); + } ide_set_inactive(s, false); } @@ -393,21 +484,16 @@ static void ide_atapi_cmd_read(IDEState *s, int lba, int nb_sectors, } } - -/* Called by *_restart_bh when the transfer function points - * to ide_atapi_cmd - */ void ide_atapi_dma_restart(IDEState *s) { /* - * I'm not sure we have enough stored to restart the command - * safely, so give the guest an error it should recover from. - * I'm assuming most guests will try to recover from something - * listed as a medium error on a CD; it seems to work on Linux. - * This would be more of a problem if we did any other type of - * DMA operation. + * At this point we can just re-evaluate the packet command and start over. + * The presence of ->dma_cb callback in the pre_save ensures that the packet + * command has been completely sent and we can safely restart command. */ - ide_atapi_cmd_error(s, MEDIUM_ERROR, ASC_NO_SEEK_COMPLETE); + s->unit = s->bus->retry_unit; + s->bus->dma->ops->restart_dma(s->bus->dma); + ide_atapi_cmd(s); } static inline uint8_t ide_atapi_set_profile(uint8_t *buf, uint8_t *index, @@ -737,7 +823,6 @@ static void cmd_inquiry(IDEState *s, uint8_t *buf) out: buf[size_idx] = idx - preamble_len; ide_atapi_cmd_reply(s, idx, max_len); - return; } static void cmd_get_configuration(IDEState *s, uint8_t *buf) @@ -1169,20 +1254,28 @@ enum { * 4.1.8) */ CHECK_READY = 0x02, + + /* + * Commands flagged with NONDATA do not in any circumstances return + * any data via ide_atapi_cmd_reply. These commands are exempt from + * the normal byte_count_limit constraints. + * See ATA8-ACS3 "7.21.5 Byte Count Limit" + */ + NONDATA = 0x04, }; -static const struct { +static const struct AtapiCmd { void (*handler)(IDEState *s, uint8_t *buf); int flags; } atapi_cmd_table[0x100] = { - [ 0x00 ] = { cmd_test_unit_ready, CHECK_READY }, + [ 0x00 ] = { cmd_test_unit_ready, CHECK_READY | NONDATA }, [ 0x03 ] = { cmd_request_sense, ALLOW_UA }, [ 0x12 ] = { cmd_inquiry, ALLOW_UA }, - [ 0x1b ] = { cmd_start_stop_unit, 0 }, /* [1] */ - [ 0x1e ] = { cmd_prevent_allow_medium_removal, 0 }, + [ 0x1b ] = { cmd_start_stop_unit, NONDATA }, /* [1] */ + [ 0x1e ] = { cmd_prevent_allow_medium_removal, NONDATA }, [ 0x25 ] = { cmd_read_cdvd_capacity, CHECK_READY }, [ 0x28 ] = { cmd_read, /* (10) */ CHECK_READY }, - [ 0x2b ] = { cmd_seek, CHECK_READY }, + [ 0x2b ] = { cmd_seek, CHECK_READY | NONDATA }, [ 0x43 ] = { cmd_read_toc_pma_atip, CHECK_READY }, [ 0x46 ] = { cmd_get_configuration, ALLOW_UA }, [ 0x4a ] = { cmd_get_event_status_notification, ALLOW_UA }, @@ -1190,7 +1283,7 @@ static const struct { [ 0x5a ] = { cmd_mode_sense, /* (10) */ 0 }, [ 0xa8 ] = { cmd_read, /* (12) */ CHECK_READY }, [ 0xad ] = { cmd_read_dvd_structure, CHECK_READY }, - [ 0xbb ] = { cmd_set_speed, 0 }, + [ 0xbb ] = { cmd_set_speed, NONDATA }, [ 0xbd ] = { cmd_mechanism_status, 0 }, [ 0xbe ] = { cmd_read_cd, CHECK_READY }, /* [1] handler detects and reports not ready condition itself */ @@ -1198,9 +1291,9 @@ static const struct { void ide_atapi_cmd(IDEState *s) { - uint8_t *buf; + uint8_t *buf = s->io_buffer; + const struct AtapiCmd *cmd = &atapi_cmd_table[s->io_buffer[0]]; - buf = s->io_buffer; #ifdef DEBUG_IDE_ATAPI { int i; @@ -1211,14 +1304,14 @@ void ide_atapi_cmd(IDEState *s) printf("\n"); } #endif + /* * If there's a UNIT_ATTENTION condition pending, only command flagged with * ALLOW_UA are allowed to complete. with other commands getting a CHECK * condition response unless a higher priority status, defined by the drive * here, is pending. */ - if (s->sense_key == UNIT_ATTENTION && - !(atapi_cmd_table[s->io_buffer[0]].flags & ALLOW_UA)) { + if (s->sense_key == UNIT_ATTENTION && !(cmd->flags & ALLOW_UA)) { ide_atapi_cmd_check_status(s); return; } @@ -1229,7 +1322,7 @@ void ide_atapi_cmd(IDEState *s) * GET_EVENT_STATUS_NOTIFICATION to detect such tray open/close * states rely on this behavior. */ - if (!(atapi_cmd_table[s->io_buffer[0]].flags & ALLOW_UA) && + if (!(cmd->flags & ALLOW_UA) && !s->tray_open && blk_is_inserted(s->blk) && s->cdrom_changed) { if (s->cdrom_changed == 1) { @@ -1244,16 +1337,29 @@ void ide_atapi_cmd(IDEState *s) } /* Report a Not Ready condition if appropriate for the command */ - if ((atapi_cmd_table[s->io_buffer[0]].flags & CHECK_READY) && + if ((cmd->flags & CHECK_READY) && (!media_present(s) || !blk_is_inserted(s->blk))) { ide_atapi_cmd_error(s, NOT_READY, ASC_MEDIUM_NOT_PRESENT); return; } + /* Nondata commands permit the byte_count_limit to be 0. + * If this is a data-transferring PIO command and BCL is 0, + * we abort at the /ATA/ level, not the ATAPI level. + * See ATA8 ACS3 section 7.17.6.49 and 7.21.5 */ + if (cmd->handler && !(cmd->flags & NONDATA)) { + /* TODO: Check IDENTIFY data word 125 for default BCL (currently 0) */ + if (!(atapi_byte_count_limit(s) || s->atapi_dma)) { + /* TODO: Move abort back into core.c and make static inline again */ + ide_abort_command(s); + return; + } + } + /* Execute the command */ - if (atapi_cmd_table[s->io_buffer[0]].handler) { - atapi_cmd_table[s->io_buffer[0]].handler(s, buf); + if (cmd->handler) { + cmd->handler(s, buf); return; } diff --git a/qemu/hw/ide/cmd646.c b/qemu/hw/ide/cmd646.c index 66fb9d96d..49294a531 100644 --- a/qemu/hw/ide/cmd646.c +++ b/qemu/hw/ide/cmd646.c @@ -22,6 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include #include @@ -417,6 +418,7 @@ static void cmd646_ide_class_init(ObjectClass *klass, void *data) k->config_read = cmd646_pci_config_read; k->config_write = cmd646_pci_config_write; dc->props = cmd646_ide_properties; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } static const TypeInfo cmd646_ide_info = { diff --git a/qemu/hw/ide/core.c b/qemu/hw/ide/core.c index 50449cae0..41e6a2dc4 100644 --- a/qemu/hw/ide/core.c +++ b/qemu/hw/ide/core.c @@ -22,6 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include #include @@ -32,6 +33,7 @@ #include "sysemu/dma.h" #include "hw/block/block.h" #include "sysemu/block-backend.h" +#include "qemu/cutils.h" #include @@ -55,7 +57,6 @@ static const int smart_attributes[][12] = { { 190, 0x03, 0x00, 0x45, 0x45, 0x1f, 0x00, 0x1f, 0x1f, 0x00, 0x00, 0x32}, }; -static int ide_handle_rw_error(IDEState *s, int error, int op); static void ide_dummy_transfer_stop(IDEState *s); static void padstr(char *str, const char *src, int len) @@ -457,7 +458,7 @@ BlockAIOCB *ide_issue_trim(BlockBackend *blk, return &iocb->common; } -static inline void ide_abort_command(IDEState *s) +void ide_abort_command(IDEState *s) { ide_transfer_stop(s); s->status = READY_STAT | ERR_STAT; @@ -486,13 +487,27 @@ static void ide_cmd_done(IDEState *s) } } -void ide_transfer_stop(IDEState *s) +static void ide_transfer_halt(IDEState *s, + void(*end_transfer_func)(IDEState *), + bool notify) { - s->end_transfer_func = ide_transfer_stop; + s->end_transfer_func = end_transfer_func; s->data_ptr = s->io_buffer; s->data_end = s->io_buffer; s->status &= ~DRQ_STAT; - ide_cmd_done(s); + if (notify) { + ide_cmd_done(s); + } +} + +void ide_transfer_stop(IDEState *s) +{ + ide_transfer_halt(s, ide_transfer_stop, true); +} + +static void ide_transfer_cancel(IDEState *s) +{ + ide_transfer_halt(s, ide_transfer_cancel, false); } int64_t ide_get_sector(IDEState *s) @@ -561,6 +576,98 @@ static bool ide_sect_range_ok(IDEState *s, return true; } +static void ide_buffered_readv_cb(void *opaque, int ret) +{ + IDEBufferedRequest *req = opaque; + if (!req->orphaned) { + if (!ret) { + qemu_iovec_from_buf(req->original_qiov, 0, req->iov.iov_base, + req->original_qiov->size); + } + req->original_cb(req->original_opaque, ret); + } + QLIST_REMOVE(req, list); + qemu_vfree(req->iov.iov_base); + g_free(req); +} + +#define MAX_BUFFERED_REQS 16 + +BlockAIOCB *ide_buffered_readv(IDEState *s, int64_t sector_num, + QEMUIOVector *iov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + BlockAIOCB *aioreq; + IDEBufferedRequest *req; + int c = 0; + + QLIST_FOREACH(req, &s->buffered_requests, list) { + c++; + } + if (c > MAX_BUFFERED_REQS) { + return blk_abort_aio_request(s->blk, cb, opaque, -EIO); + } + + req = g_new0(IDEBufferedRequest, 1); + req->original_qiov = iov; + req->original_cb = cb; + req->original_opaque = opaque; + req->iov.iov_base = qemu_blockalign(blk_bs(s->blk), iov->size); + req->iov.iov_len = iov->size; + qemu_iovec_init_external(&req->qiov, &req->iov, 1); + + aioreq = blk_aio_readv(s->blk, sector_num, &req->qiov, nb_sectors, + ide_buffered_readv_cb, req); + + QLIST_INSERT_HEAD(&s->buffered_requests, req, list); + return aioreq; +} + +/** + * Cancel all pending DMA requests. + * Any buffered DMA requests are instantly canceled, + * but any pending unbuffered DMA requests must be waited on. + */ +void ide_cancel_dma_sync(IDEState *s) +{ + IDEBufferedRequest *req; + + /* First invoke the callbacks of all buffered requests + * and flag those requests as orphaned. Ideally there + * are no unbuffered (Scatter Gather DMA Requests or + * write requests) pending and we can avoid to drain. */ + QLIST_FOREACH(req, &s->buffered_requests, list) { + if (!req->orphaned) { +#ifdef DEBUG_IDE + printf("%s: invoking cb %p of buffered request %p with" + " -ECANCELED\n", __func__, req->original_cb, req); +#endif + req->original_cb(req->original_opaque, -ECANCELED); + } + req->orphaned = true; + } + + /* + * We can't cancel Scatter Gather DMA in the middle of the + * operation or a partial (not full) DMA transfer would reach + * the storage so we wait for completion instead (we beahve + * like if the DMA was completed by the time the guest trying + * to cancel dma with bmdma_cmd_writeb with BM_CMD_START not + * set). + * + * In the future we'll be able to safely cancel the I/O if the + * whole DMA operation will be submitted to disk with a single + * aio operation with preadv/pwritev. + */ + if (s->bus->dma->aiocb) { +#ifdef DEBUG_IDE + printf("%s: draining all remaining requests", __func__); +#endif + blk_drain(s->blk); + assert(s->bus->dma->aiocb == NULL); + } +} + static void ide_sector_read(IDEState *s); static void ide_sector_read_cb(void *opaque, int ret) @@ -574,7 +681,6 @@ static void ide_sector_read_cb(void *opaque, int ret) if (ret == -ECANCELED) { return; } - block_acct_done(blk_get_stats(s->blk), &s->acct); if (ret != 0) { if (ide_handle_rw_error(s, -ret, IDE_RETRY_PIO | IDE_RETRY_READ)) { @@ -582,6 +688,8 @@ static void ide_sector_read_cb(void *opaque, int ret) } } + block_acct_done(blk_get_stats(s->blk), &s->acct); + n = s->nsector; if (n > s->req_nb_sectors) { n = s->req_nb_sectors; @@ -591,7 +699,6 @@ static void ide_sector_read_cb(void *opaque, int ret) s->nsector -= n; /* Allow the guest to read the io_buffer */ ide_transfer_start(s, s->io_buffer, n * BDRV_SECTOR_SIZE, ide_sector_read); - s->io_buffer_offset += 512 * n; ide_set_irq(s->bus); } @@ -622,6 +729,7 @@ static void ide_sector_read(IDEState *s) if (!ide_sect_range_ok(s, sector_num, n)) { ide_rw_error(s); + block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ); return; } @@ -631,15 +739,16 @@ static void ide_sector_read(IDEState *s) block_acct_start(blk_get_stats(s->blk), &s->acct, n * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ); - s->pio_aiocb = blk_aio_readv(s->blk, sector_num, &s->qiov, n, - ide_sector_read_cb, s); + s->pio_aiocb = ide_buffered_readv(s, sector_num, &s->qiov, n, + ide_sector_read_cb, s); } -static void dma_buf_commit(IDEState *s, uint32_t tx_bytes) +void dma_buf_commit(IDEState *s, uint32_t tx_bytes) { if (s->bus->dma->ops->commit_buf) { s->bus->dma->ops->commit_buf(s->bus->dma, tx_bytes); } + s->io_buffer_offset += tx_bytes; qemu_sglist_destroy(&s->sg); } @@ -663,7 +772,7 @@ void ide_dma_error(IDEState *s) ide_set_irq(s->bus); } -static int ide_handle_rw_error(IDEState *s, int error, int op) +int ide_handle_rw_error(IDEState *s, int error, int op) { bool is_read = (op & IDE_RETRY_READ) != 0; BlockErrorAction action = blk_get_error_action(s->blk, is_read, error); @@ -672,8 +781,11 @@ static int ide_handle_rw_error(IDEState *s, int error, int op) assert(s->bus->retry_unit == s->unit); s->bus->error_status = op; } else if (action == BLOCK_ERROR_ACTION_REPORT) { - if (op & IDE_RETRY_DMA) { + block_acct_failed(blk_get_stats(s->blk), &s->acct); + if (IS_IDE_RETRY_DMA(op)) { ide_dma_error(s); + } else if (IS_IDE_RETRY_ATAPI(op)) { + ide_atapi_io_error(s, -error); } else { ide_rw_error(s); } @@ -693,14 +805,7 @@ static void ide_dma_cb(void *opaque, int ret) return; } if (ret < 0) { - int op = IDE_RETRY_DMA; - - if (s->dma_cmd == IDE_DMA_READ) - op |= IDE_RETRY_READ; - else if (s->dma_cmd == IDE_DMA_TRIM) - op |= IDE_RETRY_TRIM; - - if (ide_handle_rw_error(s, -ret, op)) { + if (ide_handle_rw_error(s, -ret, ide_dma_cmd_to_retry(s->dma_cmd))) { return; } } @@ -750,6 +855,7 @@ static void ide_dma_cb(void *opaque, int ret) if ((s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) && !ide_sect_range_ok(s, sector_num, n)) { ide_dma_error(s); + block_acct_invalid(blk_get_stats(s->blk), s->acct.type); return; } @@ -767,6 +873,8 @@ static void ide_dma_cb(void *opaque, int ret) ide_issue_trim, ide_dma_cb, s, DMA_DIRECTION_TO_DEVICE); break; + default: + abort(); } return; @@ -826,7 +934,6 @@ static void ide_sector_write_cb(void *opaque, int ret) if (ret == -ECANCELED) { return; } - block_acct_done(blk_get_stats(s->blk), &s->acct); s->pio_aiocb = NULL; s->status &= ~BUSY_STAT; @@ -837,12 +944,13 @@ static void ide_sector_write_cb(void *opaque, int ret) } } + block_acct_done(blk_get_stats(s->blk), &s->acct); + n = s->nsector; if (n > s->req_nb_sectors) { n = s->req_nb_sectors; } s->nsector -= n; - s->io_buffer_offset += 512 * n; ide_set_sector(s, ide_get_sector(s) + n); if (s->nsector == 0) { @@ -864,8 +972,8 @@ static void ide_sector_write_cb(void *opaque, int ret) that at the expense of slower write performances. Use this option _only_ to install Windows 2000. You must disable it for normal use. */ - timer_mod(s->sector_write_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (get_ticks_per_sec() / 1000)); + timer_mod(s->sector_write_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (NANOSECONDS_PER_SECOND / 1000)); } else { ide_set_irq(s->bus); } @@ -888,6 +996,7 @@ static void ide_sector_write(IDEState *s) if (!ide_sect_range_ok(s, sector_num, n)) { ide_rw_error(s); + block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_WRITE); return; } @@ -896,7 +1005,7 @@ static void ide_sector_write(IDEState *s) qemu_iovec_init_external(&s->qiov, &s->iov, 1); block_acct_start(blk_get_stats(s->blk), &s->acct, - n * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ); + n * BDRV_SECTOR_SIZE, BLOCK_ACCT_WRITE); s->pio_aiocb = blk_aio_writev(s->blk, sector_num, &s->qiov, n, ide_sector_write_cb, s); } @@ -1122,11 +1231,86 @@ void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val) } } +static void ide_reset(IDEState *s) +{ +#ifdef DEBUG_IDE + printf("ide: reset\n"); +#endif + + if (s->pio_aiocb) { + blk_aio_cancel(s->pio_aiocb); + s->pio_aiocb = NULL; + } + + if (s->drive_kind == IDE_CFATA) + s->mult_sectors = 0; + else + s->mult_sectors = MAX_MULT_SECTORS; + /* ide regs */ + s->feature = 0; + s->error = 0; + s->nsector = 0; + s->sector = 0; + s->lcyl = 0; + s->hcyl = 0; + + /* lba48 */ + s->hob_feature = 0; + s->hob_sector = 0; + s->hob_nsector = 0; + s->hob_lcyl = 0; + s->hob_hcyl = 0; + + s->select = 0xa0; + s->status = READY_STAT | SEEK_STAT; + + s->lba48 = 0; + + /* ATAPI specific */ + s->sense_key = 0; + s->asc = 0; + s->cdrom_changed = 0; + s->packet_transfer_size = 0; + s->elementary_transfer_size = 0; + s->io_buffer_index = 0; + s->cd_sector_size = 0; + s->atapi_dma = 0; + s->tray_locked = 0; + s->tray_open = 0; + /* ATA DMA state */ + s->io_buffer_size = 0; + s->req_nb_sectors = 0; + + ide_set_signature(s); + /* init the transfer handler so that 0xffff is returned on data + accesses */ + s->end_transfer_func = ide_dummy_transfer_stop; + ide_dummy_transfer_stop(s); + s->media_changed = 0; +} + static bool cmd_nop(IDEState *s, uint8_t cmd) { return true; } +static bool cmd_device_reset(IDEState *s, uint8_t cmd) +{ + /* Halt PIO (in the DRQ phase), then DMA */ + ide_transfer_cancel(s); + ide_cancel_dma_sync(s); + + /* Reset any PIO commands, reset signature, etc */ + ide_reset(s); + + /* RESET: ATA8-ACS3 7.10.4 "Normal Outputs"; + * ATA8-ACS3 Table 184 "Device Signatures for Normal Output" */ + s->status = 0x00; + + /* Do not overwrite status register */ + return false; +} + static bool cmd_data_set_management(IDEState *s, uint8_t cmd) { switch (s->feature) { @@ -1443,15 +1627,6 @@ static bool cmd_exec_dev_diagnostic(IDEState *s, uint8_t cmd) return false; } -static bool cmd_device_reset(IDEState *s, uint8_t cmd) -{ - ide_set_signature(s); - s->status = 0x00; /* NOTE: READY is _not_ set */ - s->error = 0x01; - - return false; -} - static bool cmd_packet(IDEState *s, uint8_t cmd) { /* overlapping commands not supported */ @@ -1462,6 +1637,9 @@ static bool cmd_packet(IDEState *s, uint8_t cmd) s->status = READY_STAT | SEEK_STAT; s->atapi_dma = s->feature & 1; + if (s->atapi_dma) { + s->dma_cmd = IDE_DMA_ATAPI; + } s->nsector = 1; ide_transfer_start(s, s->io_buffer, ATAPI_PACKET_SIZE, ide_atapi_cmd); @@ -1747,11 +1925,11 @@ static const struct { } ide_cmd_table[0x100] = { /* NOP not implemented, mandatory for CD */ [CFA_REQ_EXT_ERROR_CODE] = { cmd_cfa_req_ext_error_code, CFA_OK }, - [WIN_DSM] = { cmd_data_set_management, ALL_OK }, + [WIN_DSM] = { cmd_data_set_management, HD_CFA_OK }, [WIN_DEVICE_RESET] = { cmd_device_reset, CD_OK }, [WIN_RECAL] = { cmd_nop, HD_CFA_OK | SET_DSC}, [WIN_READ] = { cmd_read_pio, ALL_OK }, - [WIN_READ_ONCE] = { cmd_read_pio, ALL_OK }, + [WIN_READ_ONCE] = { cmd_read_pio, HD_CFA_OK }, [WIN_READ_EXT] = { cmd_read_pio, HD_CFA_OK }, [WIN_READDMA_EXT] = { cmd_read_dma, HD_CFA_OK }, [WIN_READ_NATIVE_MAX_EXT] = { cmd_read_native_max, HD_CFA_OK | SET_DSC }, @@ -1770,12 +1948,12 @@ static const struct { [CFA_TRANSLATE_SECTOR] = { cmd_cfa_translate_sector, CFA_OK }, [WIN_DIAGNOSE] = { cmd_exec_dev_diagnostic, ALL_OK }, [WIN_SPECIFY] = { cmd_nop, HD_CFA_OK | SET_DSC }, - [WIN_STANDBYNOW2] = { cmd_nop, ALL_OK }, - [WIN_IDLEIMMEDIATE2] = { cmd_nop, ALL_OK }, - [WIN_STANDBY2] = { cmd_nop, ALL_OK }, - [WIN_SETIDLE2] = { cmd_nop, ALL_OK }, - [WIN_CHECKPOWERMODE2] = { cmd_check_power_mode, ALL_OK | SET_DSC }, - [WIN_SLEEPNOW2] = { cmd_nop, ALL_OK }, + [WIN_STANDBYNOW2] = { cmd_nop, HD_CFA_OK }, + [WIN_IDLEIMMEDIATE2] = { cmd_nop, HD_CFA_OK }, + [WIN_STANDBY2] = { cmd_nop, HD_CFA_OK }, + [WIN_SETIDLE2] = { cmd_nop, HD_CFA_OK }, + [WIN_CHECKPOWERMODE2] = { cmd_check_power_mode, HD_CFA_OK | SET_DSC }, + [WIN_SLEEPNOW2] = { cmd_nop, HD_CFA_OK }, [WIN_PACKETCMD] = { cmd_packet, CD_OK }, [WIN_PIDENTIFY] = { cmd_identify_packet, CD_OK }, [WIN_SMART] = { cmd_smart, HD_CFA_OK | SET_DSC }, @@ -1789,19 +1967,19 @@ static const struct { [WIN_WRITEDMA] = { cmd_write_dma, HD_CFA_OK }, [WIN_WRITEDMA_ONCE] = { cmd_write_dma, HD_CFA_OK }, [CFA_WRITE_MULTI_WO_ERASE] = { cmd_write_multiple, CFA_OK }, - [WIN_STANDBYNOW1] = { cmd_nop, ALL_OK }, - [WIN_IDLEIMMEDIATE] = { cmd_nop, ALL_OK }, - [WIN_STANDBY] = { cmd_nop, ALL_OK }, - [WIN_SETIDLE1] = { cmd_nop, ALL_OK }, - [WIN_CHECKPOWERMODE1] = { cmd_check_power_mode, ALL_OK | SET_DSC }, - [WIN_SLEEPNOW1] = { cmd_nop, ALL_OK }, + [WIN_STANDBYNOW1] = { cmd_nop, HD_CFA_OK }, + [WIN_IDLEIMMEDIATE] = { cmd_nop, HD_CFA_OK }, + [WIN_STANDBY] = { cmd_nop, HD_CFA_OK }, + [WIN_SETIDLE1] = { cmd_nop, HD_CFA_OK }, + [WIN_CHECKPOWERMODE1] = { cmd_check_power_mode, HD_CFA_OK | SET_DSC }, + [WIN_SLEEPNOW1] = { cmd_nop, HD_CFA_OK }, [WIN_FLUSH_CACHE] = { cmd_flush_cache, ALL_OK }, [WIN_FLUSH_CACHE_EXT] = { cmd_flush_cache, HD_CFA_OK }, [WIN_IDENTIFY] = { cmd_identify, ALL_OK }, [WIN_SETFEATURES] = { cmd_set_features, ALL_OK | SET_DSC }, [IBM_SENSE_CONDITION] = { cmd_ibm_sense_condition, CFA_OK | SET_DSC }, [CFA_WEAR_LEVEL] = { cmd_cfa_erase_sectors, HD_CFA_OK | SET_DSC }, - [WIN_READ_NATIVE_MAX] = { cmd_read_native_max, ALL_OK | SET_DSC }, + [WIN_READ_NATIVE_MAX] = { cmd_read_native_max, HD_CFA_OK | SET_DSC }, }; static bool ide_cmd_permitted(IDEState *s, uint32_t cmd) @@ -1824,9 +2002,13 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) return; } - /* Only DEVICE RESET is allowed while BSY or/and DRQ are set */ - if ((s->status & (BUSY_STAT|DRQ_STAT)) && val != WIN_DEVICE_RESET) - return; + /* Only RESET is allowed while BSY and/or DRQ are set, + * and only to ATAPI devices. */ + if (s->status & (BUSY_STAT|DRQ_STAT)) { + if (val != WIN_DEVICE_RESET || s->drive_kind != IDE_CD) { + return; + } + } if (!ide_cmd_permitted(s, val)) { ide_abort_command(s); @@ -2126,64 +2308,6 @@ static void ide_dummy_transfer_stop(IDEState *s) s->io_buffer[3] = 0xff; } -static void ide_reset(IDEState *s) -{ -#ifdef DEBUG_IDE - printf("ide: reset\n"); -#endif - - if (s->pio_aiocb) { - blk_aio_cancel(s->pio_aiocb); - s->pio_aiocb = NULL; - } - - if (s->drive_kind == IDE_CFATA) - s->mult_sectors = 0; - else - s->mult_sectors = MAX_MULT_SECTORS; - /* ide regs */ - s->feature = 0; - s->error = 0; - s->nsector = 0; - s->sector = 0; - s->lcyl = 0; - s->hcyl = 0; - - /* lba48 */ - s->hob_feature = 0; - s->hob_sector = 0; - s->hob_nsector = 0; - s->hob_lcyl = 0; - s->hob_hcyl = 0; - - s->select = 0xa0; - s->status = READY_STAT | SEEK_STAT; - - s->lba48 = 0; - - /* ATAPI specific */ - s->sense_key = 0; - s->asc = 0; - s->cdrom_changed = 0; - s->packet_transfer_size = 0; - s->elementary_transfer_size = 0; - s->io_buffer_index = 0; - s->cd_sector_size = 0; - s->atapi_dma = 0; - s->tray_locked = 0; - s->tray_open = 0; - /* ATA DMA state */ - s->io_buffer_size = 0; - s->req_nb_sectors = 0; - - ide_set_signature(s); - /* init the transfer handler so that 0xffff is returned on data - accesses */ - s->end_transfer_func = ide_dummy_transfer_stop; - ide_dummy_transfer_stop(s); - s->media_changed = 0; -} - void ide_bus_reset(IDEBus *bus) { bus->unit = 0; @@ -2313,7 +2437,7 @@ int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind, if (version) { pstrcpy(s->version, sizeof(s->version), version); } else { - pstrcpy(s->version, sizeof(s->version), qemu_get_version()); + pstrcpy(s->version, sizeof(s->version), qemu_hw_version()); } ide_reset(s); @@ -2400,15 +2524,13 @@ static void ide_restart_bh(void *opaque) if (s->bus->dma->ops->restart) { s->bus->dma->ops->restart(s->bus->dma); } - } - - if (error_status & IDE_RETRY_DMA) { + } else if (IS_IDE_RETRY_DMA(error_status)) { if (error_status & IDE_RETRY_TRIM) { ide_restart_dma(s, IDE_DMA_TRIM); } else { ide_restart_dma(s, is_read ? IDE_DMA_READ : IDE_DMA_WRITE); } - } else if (error_status & IDE_RETRY_PIO) { + } else if (IS_IDE_RETRY_PIO(error_status)) { if (is_read) { ide_sector_read(s); } else { @@ -2416,15 +2538,11 @@ static void ide_restart_bh(void *opaque) } } else if (error_status & IDE_RETRY_FLUSH) { ide_flush_cache(s); + } else if (IS_IDE_RETRY_ATAPI(error_status)) { + assert(s->end_transfer_func == ide_atapi_cmd); + ide_atapi_dma_restart(s); } else { - /* - * We've not got any bits to tell us about ATAPI - but - * we do have the end_transfer_func that tells us what - * we're trying to do. - */ - if (s->end_transfer_func == ide_atapi_cmd) { - ide_atapi_dma_restart(s); - } + abort(); } } diff --git a/qemu/hw/ide/ich.c b/qemu/hw/ide/ich.c index 350c7f1c7..0a13334ba 100644 --- a/qemu/hw/ide/ich.c +++ b/qemu/hw/ide/ich.c @@ -60,6 +60,7 @@ * */ +#include "qemu/osdep.h" #include #include #include @@ -97,6 +98,13 @@ static void pci_ich9_reset(DeviceState *dev) ahci_reset(&d->ahci); } +static void pci_ich9_ahci_init(Object *obj) +{ + struct AHCIPCIState *d = ICH_AHCI(obj); + + ahci_init(&d->ahci, DEVICE(obj)); +} + static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp) { struct AHCIPCIState *d; @@ -104,7 +112,7 @@ static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp) uint8_t *sata_cap; d = ICH_AHCI(dev); - ahci_init(&d->ahci, DEVICE(dev), pci_get_address_space(dev), 6); + ahci_realize(&d->ahci, DEVICE(dev), pci_get_address_space(dev), 6); pci_config_set_prog_interface(dev->config, AHCI_PROGMODE_MAJOR_REV_1); @@ -171,6 +179,7 @@ static const TypeInfo ich_ahci_info = { .name = TYPE_ICH9_AHCI, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(AHCIPCIState), + .instance_init = pci_ich9_ahci_init, .class_init = ich_ahci_class_init, }; diff --git a/qemu/hw/ide/internal.h b/qemu/hw/ide/internal.h index 30fdcbc5f..d2c458f57 100644 --- a/qemu/hw/ide/internal.h +++ b/qemu/hw/ide/internal.h @@ -338,11 +338,22 @@ enum ide_dma_cmd { IDE_DMA_READ, IDE_DMA_WRITE, IDE_DMA_TRIM, + IDE_DMA_ATAPI, }; #define ide_cmd_is_read(s) \ ((s)->dma_cmd == IDE_DMA_READ) +typedef struct IDEBufferedRequest { + QLIST_ENTRY(IDEBufferedRequest) list; + struct iovec iov; + QEMUIOVector qiov; + QEMUIOVector *original_qiov; + BlockCompletionFunc *original_cb; + void *original_opaque; + bool orphaned; +} IDEBufferedRequest; + /* NOTE: IDEState represents in fact one drive */ struct IDEState { IDEBus *bus; @@ -396,8 +407,9 @@ struct IDEState { BlockAIOCB *pio_aiocb; struct iovec iov; QEMUIOVector qiov; + QLIST_HEAD(, IDEBufferedRequest) buffered_requests; /* ATA DMA state */ - int32_t io_buffer_offset; + uint64_t io_buffer_offset; int32_t io_buffer_size; QEMUSGList sg; /* PIO transfer handling */ @@ -495,13 +507,45 @@ struct IDEDevice { }; /* These are used for the error_status field of IDEBus */ +#define IDE_RETRY_MASK 0xf8 #define IDE_RETRY_DMA 0x08 #define IDE_RETRY_PIO 0x10 +#define IDE_RETRY_ATAPI 0x20 /* reused IDE_RETRY_READ bit */ #define IDE_RETRY_READ 0x20 #define IDE_RETRY_FLUSH 0x40 #define IDE_RETRY_TRIM 0x80 #define IDE_RETRY_HBA 0x100 +#define IS_IDE_RETRY_DMA(_status) \ + ((_status) & IDE_RETRY_DMA) + +#define IS_IDE_RETRY_PIO(_status) \ + ((_status) & IDE_RETRY_PIO) + +/* + * The method of the IDE_RETRY_ATAPI determination is to use a previously + * impossible bit combination as a new status value. + */ +#define IS_IDE_RETRY_ATAPI(_status) \ + (((_status) & IDE_RETRY_MASK) == IDE_RETRY_ATAPI) + +static inline uint8_t ide_dma_cmd_to_retry(uint8_t dma_cmd) +{ + switch (dma_cmd) { + case IDE_DMA_READ: + return IDE_RETRY_DMA | IDE_RETRY_READ; + case IDE_DMA_WRITE: + return IDE_RETRY_DMA; + case IDE_DMA_TRIM: + return IDE_RETRY_DMA | IDE_RETRY_TRIM; + case IDE_DMA_ATAPI: + return IDE_RETRY_ATAPI; + default: + break; + } + return 0; +} + static inline IDEState *idebus_active_if(IDEBus *bus) { return bus->ifs + bus->unit; @@ -536,7 +580,9 @@ int64_t ide_get_sector(IDEState *s); void ide_set_sector(IDEState *s, int64_t sector_num); void ide_start_dma(IDEState *s, BlockCompletionFunc *cb); +void dma_buf_commit(IDEState *s, uint32_t tx_bytes); void ide_dma_error(IDEState *s); +void ide_abort_command(IDEState *s); void ide_atapi_cmd_ok(IDEState *s); void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc); @@ -570,6 +616,10 @@ void ide_set_inactive(IDEState *s, bool more); BlockAIOCB *ide_issue_trim(BlockBackend *blk, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockCompletionFunc *cb, void *opaque); +BlockAIOCB *ide_buffered_readv(IDEState *s, int64_t sector_num, + QEMUIOVector *iov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque); +void ide_cancel_dma_sync(IDEState *s); /* hw/ide/atapi.c */ void ide_atapi_cmd(IDEState *s); @@ -580,4 +630,6 @@ void ide_bus_new(IDEBus *idebus, size_t idebus_size, DeviceState *dev, int bus_id, int max_units); IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive); +int ide_handle_rw_error(IDEState *s, int error, int op); + #endif /* HW_IDE_INTERNAL_H */ diff --git a/qemu/hw/ide/isa.c b/qemu/hw/ide/isa.c index 9f80503fa..eba567c87 100644 --- a/qemu/hw/ide/isa.c +++ b/qemu/hw/ide/isa.c @@ -22,6 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include #include diff --git a/qemu/hw/ide/macio.c b/qemu/hw/ide/macio.c index 66ac2baa9..76256eb8a 100644 --- a/qemu/hw/ide/macio.c +++ b/qemu/hw/ide/macio.c @@ -22,6 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/ppc/mac.h" #include "hw/ppc/mac_dbdma.h" @@ -119,8 +120,8 @@ static void pmac_dma_read(BlockBackend *blk, MACIO_DPRINTF("--- Block read transfer - sector_num: %" PRIx64 " " "nsector: %x\n", (offset >> 9), (bytes >> 9)); - m->aiocb = blk_aio_readv(blk, (offset >> 9), &io->iov, (bytes >> 9), - cb, io); + s->bus->dma->aiocb = blk_aio_readv(blk, (offset >> 9), &io->iov, + (bytes >> 9), cb, io); } static void pmac_dma_write(BlockBackend *blk, @@ -204,8 +205,8 @@ static void pmac_dma_write(BlockBackend *blk, MACIO_DPRINTF("--- Block write transfer - sector_num: %" PRIx64 " " "nsector: %x\n", (offset >> 9), (bytes >> 9)); - m->aiocb = blk_aio_writev(blk, (offset >> 9), &io->iov, (bytes >> 9), - cb, io); + s->bus->dma->aiocb = blk_aio_writev(blk, (offset >> 9), &io->iov, + (bytes >> 9), cb, io); } static void pmac_dma_trim(BlockBackend *blk, @@ -231,8 +232,8 @@ static void pmac_dma_trim(BlockBackend *blk, s->io_buffer_index += io->len; io->len = 0; - m->aiocb = ide_issue_trim(blk, (offset >> 9), &io->iov, (bytes >> 9), - cb, io); + s->bus->dma->aiocb = ide_issue_trim(blk, (offset >> 9), &io->iov, + (bytes >> 9), cb, io); } static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) @@ -280,16 +281,20 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) } /* Calculate current offset */ - offset = (int64_t)(s->lba << 11) + s->io_buffer_index; + offset = ((int64_t)s->lba << 11) + s->io_buffer_index; pmac_dma_read(s->blk, offset, io->len, pmac_ide_atapi_transfer_cb, io); return; done: - block_acct_done(blk_get_stats(s->blk), &s->acct); - io->dma_end(opaque); + if (ret < 0) { + block_acct_failed(blk_get_stats(s->blk), &s->acct); + } else { + block_acct_done(blk_get_stats(s->blk), &s->acct); + } - return; + ide_set_inactive(s, false); + io->dma_end(opaque); } static void pmac_ide_transfer_cb(void *opaque, int ret) @@ -303,7 +308,6 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) if (ret < 0) { MACIO_DPRINTF("DMA error: %d\n", ret); - m->aiocb = NULL; ide_dma_error(s); goto done; } @@ -342,14 +346,22 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) case IDE_DMA_TRIM: pmac_dma_trim(s->blk, offset, io->len, pmac_ide_transfer_cb, io); break; + default: + abort(); } return; done: if (s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) { - block_acct_done(blk_get_stats(s->blk), &s->acct); + if (ret < 0) { + block_acct_failed(blk_get_stats(s->blk), &s->acct); + } else { + block_acct_done(blk_get_stats(s->blk), &s->acct); + } } + + ide_set_inactive(s, false); io->dma_end(opaque); } @@ -387,8 +399,9 @@ static void pmac_ide_transfer(DBDMA_io *io) static void pmac_ide_flush(DBDMA_io *io) { MACIOIDEState *m = io->opaque; + IDEState *s = idebus_active_if(&m->bus); - if (m->aiocb) { + if (s->bus->dma->aiocb) { blk_drain_all(); } } @@ -506,11 +519,12 @@ static const MemoryRegionOps pmac_ide_ops = { static const VMStateDescription vmstate_pmac = { .name = "ide", - .version_id = 3, + .version_id = 4, .minimum_version_id = 0, .fields = (VMStateField[]) { VMSTATE_IDE_BUS(bus, MACIOIDEState), VMSTATE_IDE_DRIVES(bus.ifs, MACIOIDEState), + VMSTATE_BOOL(dma_active, MACIOIDEState), VMSTATE_END_OF_LIST() } }; @@ -590,6 +604,7 @@ static void macio_ide_class_init(ObjectClass *oc, void *data) dc->realize = macio_ide_realizefn; dc->reset = macio_ide_reset; dc->vmsd = &vmstate_pmac; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } static const TypeInfo macio_ide_type_info = { diff --git a/qemu/hw/ide/microdrive.c b/qemu/hw/ide/microdrive.c index 6639dd488..5c9db8047 100644 --- a/qemu/hw/ide/microdrive.c +++ b/qemu/hw/ide/microdrive.c @@ -22,6 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include #include diff --git a/qemu/hw/ide/mmio.c b/qemu/hw/ide/mmio.c index b6ce62ac5..493f65a1d 100644 --- a/qemu/hw/ide/mmio.c +++ b/qemu/hw/ide/mmio.c @@ -22,6 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "sysemu/block-backend.h" diff --git a/qemu/hw/ide/pci.c b/qemu/hw/ide/pci.c index d31ff885b..8d56a00b1 100644 --- a/qemu/hw/ide/pci.c +++ b/qemu/hw/ide/pci.c @@ -22,6 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include #include @@ -103,13 +104,6 @@ static int32_t bmdma_prepare_buf(IDEDMA *dma, int32_t limit) qemu_sglist_add(&s->sg, bm->cur_prd_addr, sg_len); } - /* Note: We limit the max transfer to be 2GiB. - * This should accommodate the largest ATA transaction - * for LBA48 (65,536 sectors) and 32K sector sizes. */ - if (s->sg.size > INT32_MAX) { - error_report("IDE: sglist describes more than 2GiB."); - break; - } bm->cur_prd_addr += l; bm->cur_prd_len -= l; s->io_buffer_size += l; @@ -240,22 +234,7 @@ void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val) /* Ignore writes to SSBM if it keeps the old value */ if ((val & BM_CMD_START) != (bm->cmd & BM_CMD_START)) { if (!(val & BM_CMD_START)) { - /* - * We can't cancel Scatter Gather DMA in the middle of the - * operation or a partial (not full) DMA transfer would reach - * the storage so we wait for completion instead (we beahve - * like if the DMA was completed by the time the guest trying - * to cancel dma with bmdma_cmd_writeb with BM_CMD_START not - * set). - * - * In the future we'll be able to safely cancel the I/O if the - * whole DMA operation will be submitted to disk with a single - * aio operation with preadv/pwritev. - */ - if (bm->bus->dma->aiocb) { - blk_drain_all(); - assert(bm->bus->dma->aiocb == NULL); - } + ide_cancel_dma_sync(idebus_active_if(bm->bus)); bm->status &= ~BM_STATUS_DMAING; } else { bm->cur_addr = bm->addr; @@ -329,6 +308,10 @@ static void ide_bmdma_pre_save(void *opaque) BMDMAState *bm = opaque; uint8_t abused_bits = BM_MIGRATION_COMPAT_STATUS_BITS; + if (!(bm->status & BM_STATUS_DMAING) && bm->dma_cb) { + bm->bus->error_status = + ide_dma_cmd_to_retry(bmdma_active_if(bm)->dma_cmd); + } bm->migration_retry_unit = bm->bus->retry_unit; bm->migration_retry_sector_num = bm->bus->retry_sector_num; bm->migration_retry_nsector = bm->bus->retry_nsector; diff --git a/qemu/hw/ide/piix.c b/qemu/hw/ide/piix.c index 5a26c86a8..6d76ce980 100644 --- a/qemu/hw/ide/piix.c +++ b/qemu/hw/ide/piix.c @@ -23,6 +23,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include #include @@ -188,6 +189,7 @@ int pci_piix3_xen_ide_unplug(DeviceState *dev) idedev = pci_ide->bus[di->bus].slave; } idedev->conf.blk = NULL; + monitor_remove_blk(blk); blk_unref(blk); } } @@ -257,22 +259,10 @@ static const TypeInfo piix3_ide_info = { .class_init = piix3_ide_class_init, }; -static void piix3_ide_xen_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = pci_piix_ide_realize; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82371SB_1; - k->class_id = PCI_CLASS_STORAGE_IDE; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - static const TypeInfo piix3_ide_xen_info = { .name = "piix3-ide-xen", .parent = TYPE_PCI_IDE, - .class_init = piix3_ide_xen_class_init, + .class_init = piix3_ide_class_init, }; static void piix4_ide_class_init(ObjectClass *klass, void *data) diff --git a/qemu/hw/ide/qdev.c b/qemu/hw/ide/qdev.c index 788b36133..4bc74a32d 100644 --- a/qemu/hw/ide/qdev.c +++ b/qemu/hw/ide/qdev.c @@ -16,8 +16,10 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include #include "sysemu/dma.h" +#include "qapi/error.h" #include "qemu/error-report.h" #include #include "sysemu/block-backend.h" @@ -118,7 +120,8 @@ IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive) dev = qdev_create(&bus->qbus, drive->media_cd ? "ide-cd" : "ide-hd"); qdev_prop_set_uint32(dev, "unit", unit); - qdev_prop_set_drive_nofail(dev, "drive", blk_by_legacy_dinfo(drive)); + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(drive), + &error_fatal); qdev_init_nofail(dev); return DO_UPCAST(IDEDevice, qdev, dev); } @@ -171,7 +174,7 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) blkconf_serial(&dev->conf, &dev->serial); if (kind != IDE_CD) { - blkconf_geometry(&dev->conf, &dev->chs_trans, 65536, 16, 255, &err); + blkconf_geometry(&dev->conf, &dev->chs_trans, 65535, 16, 255, &err); if (err) { error_report_err(err); return -1; @@ -198,22 +201,22 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) return 0; } -static void ide_dev_get_bootindex(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void ide_dev_get_bootindex(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { IDEDevice *d = IDE_DEVICE(obj); - visit_type_int32(v, &d->conf.bootindex, name, errp); + visit_type_int32(v, name, &d->conf.bootindex, errp); } -static void ide_dev_set_bootindex(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void ide_dev_set_bootindex(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { IDEDevice *d = IDE_DEVICE(obj); int32_t boot_index; Error *local_err = NULL; - visit_type_int32(v, &boot_index, name, &local_err); + visit_type_int32(v, name, &boot_index, &local_err); if (local_err) { goto out; } diff --git a/qemu/hw/ide/via.c b/qemu/hw/ide/via.c index e2da9ef71..d3f72267a 100644 --- a/qemu/hw/ide/via.c +++ b/qemu/hw/ide/via.c @@ -23,6 +23,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include #include diff --git a/qemu/hw/input/Makefile.objs b/qemu/hw/input/Makefile.objs index 624ba7ea4..7715d7230 100644 --- a/qemu/hw/input/Makefile.objs +++ b/qemu/hw/input/Makefile.objs @@ -8,9 +8,9 @@ common-obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o common-obj-$(CONFIG_TSC2005) += tsc2005.o common-obj-$(CONFIG_VMMOUSE) += vmmouse.o -ifeq ($(CONFIG_LINUX),y) common-obj-$(CONFIG_VIRTIO) += virtio-input.o common-obj-$(CONFIG_VIRTIO) += virtio-input-hid.o +ifeq ($(CONFIG_LINUX),y) common-obj-$(CONFIG_VIRTIO) += virtio-input-host.o endif diff --git a/qemu/hw/input/adb.c b/qemu/hw/input/adb.c index a18eea265..f0ad0d447 100644 --- a/qemu/hw/input/adb.c +++ b/qemu/hw/input/adb.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/input/adb.h" #include "ui/console.h" @@ -88,7 +89,7 @@ int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) } /* XXX: move that to cuda ? */ -int adb_poll(ADBBusState *s, uint8_t *obuf) +int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask) { ADBDevice *d; int olen, i; @@ -99,13 +100,15 @@ int adb_poll(ADBBusState *s, uint8_t *obuf) if (s->poll_index >= s->nb_devices) s->poll_index = 0; d = s->devices[s->poll_index]; - buf[0] = ADB_READREG | (d->devaddr << 4); - olen = adb_request(s, obuf + 1, buf, 1); - /* if there is data, we poll again the same device */ - if (olen > 0) { - obuf[0] = buf[0]; - olen++; - break; + if ((1 << d->devaddr) & poll_mask) { + buf[0] = ADB_READREG | (d->devaddr << 4); + olen = adb_request(s, obuf + 1, buf, 1); + /* if there is data, we poll again the same device */ + if (olen > 0) { + obuf[0] = buf[0]; + olen++; + break; + } } s->poll_index++; } @@ -362,6 +365,7 @@ static void adb_kbd_class_init(ObjectClass *oc, void *data) akc->parent_realize = dc->realize; dc->realize = adb_kbd_realizefn; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); adc->devreq = adb_kbd_request; dc->reset = adb_kbd_reset; @@ -566,6 +570,7 @@ static void adb_mouse_class_init(ObjectClass *oc, void *data) amc->parent_realize = dc->realize; dc->realize = adb_mouse_realizefn; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); adc->devreq = adb_mouse_request; dc->reset = adb_mouse_reset; diff --git a/qemu/hw/input/hid.c b/qemu/hw/input/hid.c index 21ebd9e71..d92c7463b 100644 --- a/qemu/hw/input/hid.c +++ b/qemu/hw/input/hid.c @@ -22,6 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "ui/console.h" #include "qemu/timer.h" @@ -44,7 +45,7 @@ static const uint8_t hid_usage_keys[0x100] = { 0xe2, 0x2c, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f, 0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59, - 0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44, + 0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x64, 0x44, 0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, @@ -95,7 +96,7 @@ void hid_set_next_idle(HIDState *hs) { if (hs->idle) { uint64_t expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - get_ticks_per_sec() * hs->idle * 4 / 1000; + NANOSECONDS_PER_SECOND * hs->idle * 4 / 1000; if (!hs->idle_timer) { hs->idle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hid_idle_timer, hs); } @@ -108,44 +109,49 @@ void hid_set_next_idle(HIDState *hs) static void hid_pointer_event(DeviceState *dev, QemuConsole *src, InputEvent *evt) { - static const int bmap[INPUT_BUTTON_MAX] = { + static const int bmap[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = 0x01, [INPUT_BUTTON_RIGHT] = 0x02, [INPUT_BUTTON_MIDDLE] = 0x04, }; HIDState *hs = (HIDState *)dev; HIDPointerEvent *e; + InputMoveEvent *move; + InputBtnEvent *btn; assert(hs->n < QUEUE_LENGTH); e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK]; - switch (evt->kind) { + switch (evt->type) { case INPUT_EVENT_KIND_REL: - if (evt->rel->axis == INPUT_AXIS_X) { - e->xdx += evt->rel->value; - } else if (evt->rel->axis == INPUT_AXIS_Y) { - e->ydy += evt->rel->value; + move = evt->u.rel.data; + if (move->axis == INPUT_AXIS_X) { + e->xdx += move->value; + } else if (move->axis == INPUT_AXIS_Y) { + e->ydy += move->value; } break; case INPUT_EVENT_KIND_ABS: - if (evt->rel->axis == INPUT_AXIS_X) { - e->xdx = evt->rel->value; - } else if (evt->rel->axis == INPUT_AXIS_Y) { - e->ydy = evt->rel->value; + move = evt->u.abs.data; + if (move->axis == INPUT_AXIS_X) { + e->xdx = move->value; + } else if (move->axis == INPUT_AXIS_Y) { + e->ydy = move->value; } break; case INPUT_EVENT_KIND_BTN: - if (evt->btn->down) { - e->buttons_state |= bmap[evt->btn->button]; - if (evt->btn->button == INPUT_BUTTON_WHEEL_UP) { + btn = evt->u.btn.data; + if (btn->down) { + e->buttons_state |= bmap[btn->button]; + if (btn->button == INPUT_BUTTON_WHEEL_UP) { e->dz--; - } else if (evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) { + } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) { e->dz++; } } else { - e->buttons_state &= ~bmap[evt->btn->button]; + e->buttons_state &= ~bmap[btn->button]; } break; @@ -222,9 +228,10 @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src, HIDState *hs = (HIDState *)dev; int scancodes[3], i, count; int slot; + InputKeyEvent *key = evt->u.key.data; - count = qemu_input_key_value_to_scancode(evt->key->key, - evt->key->down, + count = qemu_input_key_value_to_scancode(key->key, + key->down, scancodes); if (hs->n + count > QUEUE_LENGTH) { fprintf(stderr, "usb-kbd: warning: key event queue full\n"); diff --git a/qemu/hw/input/lm832x.c b/qemu/hw/input/lm832x.c index 530a6e01f..539682cac 100644 --- a/qemu/hw/input/lm832x.c +++ b/qemu/hw/input/lm832x.c @@ -18,6 +18,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i2c/i2c.h" #include "qemu/timer.h" diff --git a/qemu/hw/input/milkymist-softusb.c b/qemu/hw/input/milkymist-softusb.c index 7b0f4db88..40dfca157 100644 --- a/qemu/hw/input/milkymist-softusb.c +++ b/qemu/hw/input/milkymist-softusb.c @@ -21,6 +21,8 @@ * not available yet */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "trace.h" @@ -255,12 +257,12 @@ static int milkymist_softusb_init(SysBusDevice *dev) /* register pmem and dmem */ memory_region_init_ram(&s->pmem, OBJECT(s), "milkymist-softusb.pmem", - s->pmem_size, &error_abort); + s->pmem_size, &error_fatal); vmstate_register_ram_global(&s->pmem); s->pmem_ptr = memory_region_get_ram_ptr(&s->pmem); sysbus_init_mmio(dev, &s->pmem); memory_region_init_ram(&s->dmem, OBJECT(s), "milkymist-softusb.dmem", - s->dmem_size, &error_abort); + s->dmem_size, &error_fatal); vmstate_register_ram_global(&s->dmem); s->dmem_ptr = memory_region_get_ram_ptr(&s->dmem); sysbus_init_mmio(dev, &s->dmem); diff --git a/qemu/hw/input/pckbd.c b/qemu/hw/input/pckbd.c index ddac69df6..1d932ec19 100644 --- a/qemu/hw/input/pckbd.c +++ b/qemu/hw/input/pckbd.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/isa/isa.h" #include "hw/i386/pc.h" diff --git a/qemu/hw/input/pl050.c b/qemu/hw/input/pl050.c index c1b08d5a4..3092b0fe3 100644 --- a/qemu/hw/input/pl050.c +++ b/qemu/hw/input/pl050.c @@ -7,6 +7,7 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/input/ps2.h" diff --git a/qemu/hw/input/ps2.c b/qemu/hw/input/ps2.c index fdbe565e6..a8aa36f5c 100644 --- a/qemu/hw/input/ps2.c +++ b/qemu/hw/input/ps2.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/input/ps2.h" #include "ui/console.h" @@ -181,10 +182,11 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, { PS2KbdState *s = (PS2KbdState *)dev; int scancodes[3], i, count; + InputKeyEvent *key = evt->u.key.data; qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); - count = qemu_input_key_value_to_scancode(evt->key->key, - evt->key->down, + count = qemu_input_key_value_to_scancode(key->key, + key->down, scancodes); for (i = 0; i < count; i++) { ps2_put_keycode(s, scancodes[i]); @@ -382,36 +384,40 @@ static void ps2_mouse_send_packet(PS2MouseState *s) static void ps2_mouse_event(DeviceState *dev, QemuConsole *src, InputEvent *evt) { - static const int bmap[INPUT_BUTTON_MAX] = { + static const int bmap[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON, [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON, [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON, }; PS2MouseState *s = (PS2MouseState *)dev; + InputMoveEvent *move; + InputBtnEvent *btn; /* check if deltas are recorded when disabled */ if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) return; - switch (evt->kind) { + switch (evt->type) { case INPUT_EVENT_KIND_REL: - if (evt->rel->axis == INPUT_AXIS_X) { - s->mouse_dx += evt->rel->value; - } else if (evt->rel->axis == INPUT_AXIS_Y) { - s->mouse_dy -= evt->rel->value; + move = evt->u.rel.data; + if (move->axis == INPUT_AXIS_X) { + s->mouse_dx += move->value; + } else if (move->axis == INPUT_AXIS_Y) { + s->mouse_dy -= move->value; } break; case INPUT_EVENT_KIND_BTN: - if (evt->btn->down) { - s->mouse_buttons |= bmap[evt->btn->button]; - if (evt->btn->button == INPUT_BUTTON_WHEEL_UP) { + btn = evt->u.btn.data; + if (btn->down) { + s->mouse_buttons |= bmap[btn->button]; + if (btn->button == INPUT_BUTTON_WHEEL_UP) { s->mouse_dz--; - } else if (evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) { + } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) { s->mouse_dz++; } } else { - s->mouse_buttons &= ~bmap[evt->btn->button]; + s->mouse_buttons &= ~bmap[btn->button]; } break; @@ -622,7 +628,7 @@ static void ps2_kbd_reset(void *opaque) ps2_common_reset(&s->common); s->scan_enabled = 0; s->translate = 0; - s->scancode_set = 0; + s->scancode_set = 2; } static void ps2_mouse_reset(void *opaque) diff --git a/qemu/hw/input/pxa2xx_keypad.c b/qemu/hw/input/pxa2xx_keypad.c index 85011145e..2b70bbb95 100644 --- a/qemu/hw/input/pxa2xx_keypad.c +++ b/qemu/hw/input/pxa2xx_keypad.c @@ -11,6 +11,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/arm/pxa.h" #include "ui/console.h" diff --git a/qemu/hw/input/stellaris_input.c b/qemu/hw/input/stellaris_input.c index 0609e8086..99168bfee 100644 --- a/qemu/hw/input/stellaris_input.c +++ b/qemu/hw/input/stellaris_input.c @@ -6,6 +6,7 @@ * * This code is licensed under the GPL. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/devices.h" #include "ui/console.h" @@ -69,14 +70,14 @@ static const VMStateDescription vmstate_stellaris_gamepad = { } }; -/* Returns an array 5 ouput slots. */ +/* Returns an array of 5 output slots. */ void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode) { gamepad_state *s; int i; - s = (gamepad_state *)g_malloc0(sizeof (gamepad_state)); - s->buttons = (gamepad_button *)g_malloc0(n * sizeof (gamepad_button)); + s = g_new0(gamepad_state, 1); + s->buttons = g_new0(gamepad_button, n); for (i = 0; i < n; i++) { s->buttons[i].irq = irq[i]; s->buttons[i].keycode = keycode[i]; diff --git a/qemu/hw/input/tsc2005.c b/qemu/hw/input/tsc2005.c index 21d4f4dbb..9b359aaec 100644 --- a/qemu/hw/input/tsc2005.c +++ b/qemu/hw/input/tsc2005.c @@ -18,6 +18,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "qemu/timer.h" #include "ui/console.h" @@ -290,7 +291,8 @@ static void tsc2005_pin_update(TSC2005State *s) s->precision = s->nextprecision; s->function = s->nextfunction; s->pdst = !s->pnd0; /* Synchronised on internal clock */ - expires = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (get_ticks_per_sec() >> 7); + expires = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (NANOSECONDS_PER_SECOND >> 7); timer_mod(s->timer, expires); } diff --git a/qemu/hw/input/tsc210x.c b/qemu/hw/input/tsc210x.c index fae338563..93ca374fc 100644 --- a/qemu/hw/input/tsc210x.c +++ b/qemu/hw/input/tsc210x.c @@ -19,6 +19,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "audio/audio.h" #include "qemu/timer.h" @@ -834,7 +835,8 @@ static void tsc210x_pin_update(TSC210xState *s) s->busy = 1; s->precision = s->nextprecision; s->function = s->nextfunction; - expires = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (get_ticks_per_sec() >> 10); + expires = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (NANOSECONDS_PER_SECOND >> 10); timer_mod(s->timer, expires); } @@ -1086,9 +1088,7 @@ uWireSlave *tsc2102_init(qemu_irq pint) { TSC210xState *s; - s = (TSC210xState *) - g_malloc0(sizeof(TSC210xState)); - memset(s, 0, sizeof(TSC210xState)); + s = g_new0(TSC210xState, 1); s->x = 160; s->y = 160; s->pressure = 0; @@ -1135,9 +1135,7 @@ uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav) { TSC210xState *s; - s = (TSC210xState *) - g_malloc0(sizeof(TSC210xState)); - memset(s, 0, sizeof(TSC210xState)); + s = g_new0(TSC210xState, 1); s->x = 400; s->y = 240; s->pressure = 0; diff --git a/qemu/hw/input/virtio-input-hid.c b/qemu/hw/input/virtio-input-hid.c index 4d85dad4d..3ee0c1814 100644 --- a/qemu/hw/input/virtio-input-hid.c +++ b/qemu/hw/input/virtio-input-hid.c @@ -4,6 +4,7 @@ * top-level directory. */ +#include "qemu/osdep.h" #include "qemu/iov.h" #include "hw/qdev.h" @@ -21,7 +22,7 @@ /* ----------------------------------------------------------------- */ -static const unsigned int keymap_qcode[Q_KEY_CODE_MAX] = { +static const unsigned int keymap_qcode[Q_KEY_CODE__MAX] = { [Q_KEY_CODE_ESC] = KEY_ESC, [Q_KEY_CODE_1] = KEY_1, [Q_KEY_CODE_2] = KEY_2, @@ -120,6 +121,8 @@ static const unsigned int keymap_qcode[Q_KEY_CODE_MAX] = { [Q_KEY_CODE_CTRL_R] = KEY_RIGHTCTRL, [Q_KEY_CODE_SYSRQ] = KEY_SYSRQ, + [Q_KEY_CODE_PRINT] = KEY_SYSRQ, + [Q_KEY_CODE_PAUSE] = KEY_PAUSE, [Q_KEY_CODE_ALT_R] = KEY_RIGHTALT, [Q_KEY_CODE_HOME] = KEY_HOME, @@ -138,7 +141,7 @@ static const unsigned int keymap_qcode[Q_KEY_CODE_MAX] = { [Q_KEY_CODE_MENU] = KEY_MENU, }; -static const unsigned int keymap_button[INPUT_BUTTON_MAX] = { +static const unsigned int keymap_button[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = BTN_LEFT, [INPUT_BUTTON_RIGHT] = BTN_RIGHT, [INPUT_BUTTON_MIDDLE] = BTN_MIDDLE, @@ -146,12 +149,12 @@ static const unsigned int keymap_button[INPUT_BUTTON_MAX] = { [INPUT_BUTTON_WHEEL_DOWN] = BTN_GEAR_DOWN, }; -static const unsigned int axismap_rel[INPUT_AXIS_MAX] = { +static const unsigned int axismap_rel[INPUT_AXIS__MAX] = { [INPUT_AXIS_X] = REL_X, [INPUT_AXIS_Y] = REL_Y, }; -static const unsigned int axismap_abs[INPUT_AXIS_MAX] = { +static const unsigned int axismap_abs[INPUT_AXIS__MAX] = { [INPUT_AXIS_X] = ABS_X, [INPUT_AXIS_Y] = ABS_Y, }; @@ -190,45 +193,53 @@ static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src, VirtIOInput *vinput = VIRTIO_INPUT(dev); virtio_input_event event; int qcode; + InputKeyEvent *key; + InputMoveEvent *move; + InputBtnEvent *btn; - switch (evt->kind) { + switch (evt->type) { case INPUT_EVENT_KIND_KEY: - qcode = qemu_input_key_value_to_qcode(evt->key->key); + key = evt->u.key.data; + qcode = qemu_input_key_value_to_qcode(key->key); if (qcode && keymap_qcode[qcode]) { event.type = cpu_to_le16(EV_KEY); event.code = cpu_to_le16(keymap_qcode[qcode]); - event.value = cpu_to_le32(evt->key->down ? 1 : 0); + event.value = cpu_to_le32(key->down ? 1 : 0); virtio_input_send(vinput, &event); } else { - if (evt->key->down) { + if (key->down) { fprintf(stderr, "%s: unmapped key: %d [%s]\n", __func__, qcode, QKeyCode_lookup[qcode]); } } break; case INPUT_EVENT_KIND_BTN: - if (keymap_button[evt->btn->button]) { + btn = evt->u.btn.data; + if (keymap_button[btn->button]) { event.type = cpu_to_le16(EV_KEY); - event.code = cpu_to_le16(keymap_button[evt->btn->button]); - event.value = cpu_to_le32(evt->btn->down ? 1 : 0); + event.code = cpu_to_le16(keymap_button[btn->button]); + event.value = cpu_to_le32(btn->down ? 1 : 0); virtio_input_send(vinput, &event); } else { - if (evt->btn->down) { + if (btn->down) { fprintf(stderr, "%s: unmapped button: %d [%s]\n", __func__, - evt->btn->button, InputButton_lookup[evt->btn->button]); + btn->button, + InputButton_lookup[btn->button]); } } break; case INPUT_EVENT_KIND_REL: + move = evt->u.rel.data; event.type = cpu_to_le16(EV_REL); - event.code = cpu_to_le16(axismap_rel[evt->rel->axis]); - event.value = cpu_to_le32(evt->rel->value); + event.code = cpu_to_le16(axismap_rel[move->axis]); + event.value = cpu_to_le32(move->value); virtio_input_send(vinput, &event); break; case INPUT_EVENT_KIND_ABS: + move = evt->u.abs.data; event.type = cpu_to_le16(EV_ABS); - event.code = cpu_to_le16(axismap_abs[evt->abs->axis]); - event.value = cpu_to_le32(evt->abs->value); + event.code = cpu_to_le16(axismap_abs[move->axis]); + event.value = cpu_to_le32(move->value); virtio_input_send(vinput, &event); break; default: @@ -473,12 +484,12 @@ static struct virtio_input_config virtio_tablet_config[] = { .select = VIRTIO_INPUT_CFG_ABS_INFO, .subsel = ABS_X, .size = sizeof(virtio_input_absinfo), - .u.abs.max = const_le32(INPUT_EVENT_ABS_SIZE), + .u.abs.max = const_le32(INPUT_EVENT_ABS_SIZE - 1), },{ .select = VIRTIO_INPUT_CFG_ABS_INFO, .subsel = ABS_Y, .size = sizeof(virtio_input_absinfo), - .u.abs.max = const_le32(INPUT_EVENT_ABS_SIZE), + .u.abs.max = const_le32(INPUT_EVENT_ABS_SIZE - 1), }, { /* end of list */ }, }; diff --git a/qemu/hw/input/virtio-input-host.c b/qemu/hw/input/virtio-input-host.c index 8978f16ba..cb79e8002 100644 --- a/qemu/hw/input/virtio-input-host.c +++ b/qemu/hw/input/virtio-input-host.c @@ -4,6 +4,8 @@ * top-level directory. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/sockets.h" @@ -68,13 +70,39 @@ static void virtio_input_bits_config(VirtIOInputHost *vih, virtio_input_add_config(VIRTIO_INPUT(vih), &bits); } +static void virtio_input_abs_config(VirtIOInputHost *vih, int axis) +{ + virtio_input_config config; + struct input_absinfo absinfo; + int rc; + + rc = ioctl(vih->fd, EVIOCGABS(axis), &absinfo); + if (rc < 0) { + return; + } + + memset(&config, 0, sizeof(config)); + config.select = VIRTIO_INPUT_CFG_ABS_INFO; + config.subsel = axis; + config.size = sizeof(virtio_input_absinfo); + + config.u.abs.min = cpu_to_le32(absinfo.minimum); + config.u.abs.max = cpu_to_le32(absinfo.maximum); + config.u.abs.fuzz = cpu_to_le32(absinfo.fuzz); + config.u.abs.flat = cpu_to_le32(absinfo.flat); + config.u.abs.res = cpu_to_le32(absinfo.resolution); + + virtio_input_add_config(VIRTIO_INPUT(vih), &config); +} + static void virtio_input_host_realize(DeviceState *dev, Error **errp) { VirtIOInputHost *vih = VIRTIO_INPUT_HOST(dev); VirtIOInput *vinput = VIRTIO_INPUT(dev); - virtio_input_config id; + virtio_input_config id, *abs; struct input_id ids; - int rc, ver; + int rc, ver, i, axis; + uint8_t byte; if (!vih->evdev) { error_setg(errp, "evdev property is required"); @@ -123,6 +151,23 @@ static void virtio_input_host_realize(DeviceState *dev, Error **errp) virtio_input_bits_config(vih, EV_ABS, ABS_CNT); virtio_input_bits_config(vih, EV_MSC, MSC_CNT); virtio_input_bits_config(vih, EV_SW, SW_CNT); + virtio_input_bits_config(vih, EV_LED, LED_CNT); + + abs = virtio_input_find_config(VIRTIO_INPUT(vih), + VIRTIO_INPUT_CFG_EV_BITS, EV_ABS); + if (abs) { + for (i = 0; i < abs->size; i++) { + byte = abs->u.bitmap[i]; + axis = 8 * i; + while (byte) { + if (byte & 1) { + virtio_input_abs_config(vih, axis); + } + axis++; + byte >>= 1; + } + } + } qemu_set_fd_handler(vih->fd, virtio_input_host_event, NULL, vih); return; @@ -143,6 +188,28 @@ static void virtio_input_host_unrealize(DeviceState *dev, Error **errp) } } +static void virtio_input_host_handle_status(VirtIOInput *vinput, + virtio_input_event *event) +{ + VirtIOInputHost *vih = VIRTIO_INPUT_HOST(vinput); + struct input_event evdev; + int rc; + + if (gettimeofday(&evdev.time, NULL)) { + perror("virtio_input_host_handle_status: gettimeofday"); + return; + } + + evdev.type = le16_to_cpu(event->type); + evdev.code = le16_to_cpu(event->code); + evdev.value = le32_to_cpu(event->value); + + rc = write(vih->fd, &evdev, sizeof(evdev)); + if (rc == -1) { + perror("virtio_input_host_handle_status: write"); + } +} + static const VMStateDescription vmstate_virtio_input_host = { .name = "virtio-input-host", .unmigratable = 1, @@ -162,6 +229,7 @@ static void virtio_input_host_class_init(ObjectClass *klass, void *data) dc->props = virtio_input_host_properties; vic->realize = virtio_input_host_realize; vic->unrealize = virtio_input_host_unrealize; + vic->handle_status = virtio_input_host_handle_status; } static void virtio_input_host_init(Object *obj) diff --git a/qemu/hw/input/virtio-input.c b/qemu/hw/input/virtio-input.c index 7b25d2769..f59749a94 100644 --- a/qemu/hw/input/virtio-input.c +++ b/qemu/hw/input/virtio-input.c @@ -4,6 +4,8 @@ * top-level directory. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/iov.h" #include "hw/qdev.h" @@ -12,14 +14,20 @@ #include "standard-headers/linux/input.h" +#define VIRTIO_INPUT_VM_VERSION 1 + /* ----------------------------------------------------------------- */ void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event) { - VirtQueueElement elem; + VirtQueueElement *elem; unsigned have, need; int i, len; + if (!vinput->active) { + return; + } + /* queue up events ... */ if (vinput->qindex == vinput->qsize) { vinput->qsize++; @@ -45,14 +53,16 @@ void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event) /* ... and finally pass them to the guest */ for (i = 0; i < vinput->qindex; i++) { - if (!virtqueue_pop(vinput->evt, &elem)) { + elem = virtqueue_pop(vinput->evt, sizeof(VirtQueueElement)); + if (!elem) { /* should not happen, we've checked for space beforehand */ fprintf(stderr, "%s: Huh? No vq elem available ...\n", __func__); return; } - len = iov_from_buf(elem.in_sg, elem.in_num, + len = iov_from_buf(elem->in_sg, elem->in_num, 0, vinput->queue+i, sizeof(virtio_input_event)); - virtqueue_push(vinput->evt, &elem, len); + virtqueue_push(vinput->evt, elem, len); + g_free(elem); } virtio_notify(VIRTIO_DEVICE(vinput), vinput->evt); vinput->qindex = 0; @@ -68,24 +78,30 @@ static void virtio_input_handle_sts(VirtIODevice *vdev, VirtQueue *vq) VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev); VirtIOInput *vinput = VIRTIO_INPUT(vdev); virtio_input_event event; - VirtQueueElement elem; + VirtQueueElement *elem; int len; - while (virtqueue_pop(vinput->sts, &elem)) { + for (;;) { + elem = virtqueue_pop(vinput->sts, sizeof(VirtQueueElement)); + if (!elem) { + break; + } + memset(&event, 0, sizeof(event)); - len = iov_to_buf(elem.out_sg, elem.out_num, + len = iov_to_buf(elem->out_sg, elem->out_num, 0, &event, sizeof(event)); if (vic->handle_status) { vic->handle_status(vinput, &event); } - virtqueue_push(vinput->sts, &elem, len); + virtqueue_push(vinput->sts, elem, len); + g_free(elem); } virtio_notify(vdev, vinput->sts); } -static virtio_input_config *virtio_input_find_config(VirtIOInput *vinput, - uint8_t select, - uint8_t subsel) +virtio_input_config *virtio_input_find_config(VirtIOInput *vinput, + uint8_t select, + uint8_t subsel) { VirtIOInputConfig *cfg; @@ -200,6 +216,38 @@ static void virtio_input_reset(VirtIODevice *vdev) } } +static void virtio_input_save(QEMUFile *f, void *opaque) +{ + VirtIOInput *vinput = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(vinput); + + virtio_save(vdev, f); +} + +static int virtio_input_load(QEMUFile *f, void *opaque, int version_id) +{ + VirtIOInput *vinput = opaque; + VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vinput); + VirtIODevice *vdev = VIRTIO_DEVICE(vinput); + int ret; + + if (version_id != VIRTIO_INPUT_VM_VERSION) { + return -EINVAL; + } + + ret = virtio_load(vdev, f, version_id); + if (ret) { + return ret; + } + + /* post_load() */ + vinput->active = vdev->status & VIRTIO_CONFIG_S_DRIVER_OK; + if (vic->change_active) { + vic->change_active(vinput); + } + return 0; +} + static void virtio_input_device_realize(DeviceState *dev, Error **errp) { VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev); @@ -231,14 +279,20 @@ static void virtio_input_device_realize(DeviceState *dev, Error **errp) vinput->cfg_size); vinput->evt = virtio_add_queue(vdev, 64, virtio_input_handle_evt); vinput->sts = virtio_add_queue(vdev, 64, virtio_input_handle_sts); + + register_savevm(dev, "virtio-input", -1, VIRTIO_INPUT_VM_VERSION, + virtio_input_save, virtio_input_load, vinput); } static void virtio_input_device_unrealize(DeviceState *dev, Error **errp) { VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev); VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VirtIOInput *vinput = VIRTIO_INPUT(dev); Error *local_err = NULL; + unregister_savevm(dev, "virtio-input", vinput); + if (vic->unrealize) { vic->unrealize(dev, &local_err); if (local_err) { diff --git a/qemu/hw/input/vmmouse.c b/qemu/hw/input/vmmouse.c index d7b1c76f5..6d15a887c 100644 --- a/qemu/hw/input/vmmouse.c +++ b/qemu/hw/input/vmmouse.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "ui/console.h" #include "hw/input/ps2.h" diff --git a/qemu/hw/intc/Makefile.objs b/qemu/hw/intc/Makefile.objs index 092d8a80a..0e47f0f9e 100644 --- a/qemu/hw/intc/Makefile.objs +++ b/qemu/hw/intc/Makefile.objs @@ -12,19 +12,23 @@ common-obj-$(CONFIG_IOAPIC) += ioapic_common.o common-obj-$(CONFIG_ARM_GIC) += arm_gic_common.o common-obj-$(CONFIG_ARM_GIC) += arm_gic.o common-obj-$(CONFIG_ARM_GIC) += arm_gicv2m.o +common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_common.o common-obj-$(CONFIG_OPENPIC) += openpic.o obj-$(CONFIG_APIC) += apic.o apic_common.o obj-$(CONFIG_ARM_GIC_KVM) += arm_gic_kvm.o +obj-$(call land,$(CONFIG_ARM_GIC_KVM),$(TARGET_AARCH64)) += arm_gicv3_kvm.o obj-$(CONFIG_STELLARIS) += armv7m_nvic.o obj-$(CONFIG_EXYNOS4) += exynos4210_gic.o exynos4210_combiner.o obj-$(CONFIG_GRLIB) += grlib_irqmp.o obj-$(CONFIG_IOAPIC) += ioapic.o obj-$(CONFIG_OMAP) += omap_intc.o obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o +obj-$(CONFIG_RASPI) += bcm2835_ic.o bcm2836_control.o obj-$(CONFIG_SH4) += sh_intc.o obj-$(CONFIG_XICS) += xics.o obj-$(CONFIG_XICS_KVM) += xics_kvm.o obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o obj-$(CONFIG_S390_FLIC) += s390_flic.o obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o +obj-$(CONFIG_ASPEED_SOC) += aspeed_vic.o diff --git a/qemu/hw/intc/allwinner-a10-pic.c b/qemu/hw/intc/allwinner-a10-pic.c index eed7621f1..dc971a160 100644 --- a/qemu/hw/intc/allwinner-a10-pic.c +++ b/qemu/hw/intc/allwinner-a10-pic.c @@ -15,6 +15,7 @@ * for more details. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/devices.h" #include "sysemu/sysemu.h" diff --git a/qemu/hw/intc/apic.c b/qemu/hw/intc/apic.c index 77b639cce..28c2ea540 100644 --- a/qemu/hw/intc/apic.c +++ b/qemu/hw/intc/apic.c @@ -16,6 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see */ +#include "qemu/osdep.h" #include "qemu/thread.h" #include "hw/i386/apic_internal.h" #include "hw/i386/apic.h" @@ -51,14 +52,6 @@ static int apic_ffs_bit(uint32_t value) return ctz32(value); } -static inline void apic_set_bit(uint32_t *tab, int index) -{ - int i, mask; - i = index >> 5; - mask = 1 << (index & 0x1f); - tab[i] |= mask; -} - static inline void apic_reset_bit(uint32_t *tab, int index) { int i, mask; @@ -67,14 +60,6 @@ static inline void apic_reset_bit(uint32_t *tab, int index) tab[i] &= ~mask; } -static inline int apic_get_bit(uint32_t *tab, int index) -{ - int i, mask; - i = index >> 5; - mask = 1 << (index & 0x1f); - return !!(tab[i] & mask); -} - /* return -1 if no bit is set */ static int get_highest_priority_int(uint32_t *tab) { @@ -318,7 +303,7 @@ static uint8_t apic_get_tpr(APICCommonState *s) return s->tpr >> 4; } -static int apic_get_ppr(APICCommonState *s) +int apic_get_ppr(APICCommonState *s) { int tpr, isrv, ppr; @@ -739,7 +724,7 @@ static uint32_t apic_mem_readl(void *opaque, hwaddr addr) val = s->divide_conf; break; default: - s->esr |= ESR_ILLEGAL_ADDRESS; + s->esr |= APIC_ESR_ILLEGAL_ADDRESS; val = 0; break; } @@ -852,7 +837,7 @@ static void apic_mem_writel(void *opaque, hwaddr addr, uint32_t val) } break; default: - s->esr |= ESR_ILLEGAL_ADDRESS; + s->esr |= APIC_ESR_ILLEGAL_ADDRESS; break; } } @@ -889,7 +874,7 @@ static void apic_realize(DeviceState *dev, Error **errp) s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, apic_timer, s); local_apics[s->idx] = s; - msi_supported = true; + msi_nonbroken = true; } static void apic_class_init(ObjectClass *klass, void *data) diff --git a/qemu/hw/intc/apic_common.c b/qemu/hw/intc/apic_common.c index 0032b97c5..4abe145c6 100644 --- a/qemu/hw/intc/apic_common.c +++ b/qemu/hw/intc/apic_common.c @@ -17,6 +17,8 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/i386/apic.h" #include "hw/i386/apic_internal.h" #include "trace.h" @@ -296,7 +298,6 @@ static void apic_common_realize(DeviceState *dev, Error **errp) APICCommonClass *info; static DeviceState *vapic; static int apic_no; - static bool mmio_registered; if (apic_no >= MAX_APICS) { error_setg(errp, "%s initialization failed.", @@ -307,11 +308,6 @@ static void apic_common_realize(DeviceState *dev, Error **errp) info = APIC_COMMON_GET_CLASS(s); info->realize(dev, errp); - if (!mmio_registered) { - ICCBus *b = ICC_BUS(qdev_get_parent_bus(dev)); - memory_region_add_subregion(b->apic_address_space, 0, &s->io_memory); - mmio_registered = true; - } /* Note: We need at least 1M to map the VAPIC option ROM */ if (!vapic && s->vapic_control & VAPIC_ENABLE_MASK && @@ -425,13 +421,12 @@ static Property apic_properties_common[] = { static void apic_common_class_init(ObjectClass *klass, void *data) { - ICCDeviceClass *idc = ICC_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_apic_common; dc->reset = apic_reset_common; dc->props = apic_properties_common; - idc->realize = apic_common_realize; + dc->realize = apic_common_realize; /* * Reason: APIC and CPU need to be wired up by * x86_cpu_apic_create() @@ -441,7 +436,7 @@ static void apic_common_class_init(ObjectClass *klass, void *data) static const TypeInfo apic_common_type = { .name = TYPE_APIC_COMMON, - .parent = TYPE_ICC_DEVICE, + .parent = TYPE_DEVICE, .instance_size = sizeof(APICCommonState), .class_size = sizeof(APICCommonClass), .class_init = apic_common_class_init, diff --git a/qemu/hw/intc/arm_gic.c b/qemu/hw/intc/arm_gic.c index 454bfd7df..f55124174 100644 --- a/qemu/hw/intc/arm_gic.c +++ b/qemu/hw/intc/arm_gic.c @@ -18,8 +18,10 @@ * armv7m_nvic device. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "gic_internal.h" +#include "qapi/error.h" #include "qom/cpu.h" //#define DEBUG_GIC @@ -31,11 +33,17 @@ do { fprintf(stderr, "arm_gic: " fmt , ## __VA_ARGS__); } while (0) #define DPRINTF(fmt, ...) do {} while(0) #endif -static const uint8_t gic_id[] = { - 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 +static const uint8_t gic_id_11mpcore[] = { + 0x00, 0x00, 0x00, 0x00, 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; -#define NUM_CPU(s) ((s)->num_cpu) +static const uint8_t gic_id_gicv1[] = { + 0x04, 0x00, 0x00, 0x00, 0x90, 0xb3, 0x1b, 0x00, 0x0d, 0xf0, 0x05, 0xb1 +}; + +static const uint8_t gic_id_gicv2[] = { + 0x04, 0x00, 0x00, 0x00, 0x90, 0xb4, 0x2b, 0x00, 0x0d, 0xf0, 0x05, 0xb1 +}; static inline int gic_get_current_cpu(GICState *s) { @@ -64,7 +72,7 @@ void gic_update(GICState *s) int cpu; int cm; - for (cpu = 0; cpu < NUM_CPU(s); cpu++) { + for (cpu = 0; cpu < s->num_cpu; cpu++) { cm = 1 << cpu; s->current_pending[cpu] = 1023; if (!(s->ctlr & (GICD_CTLR_EN_GRP0 | GICD_CTLR_EN_GRP1)) @@ -219,15 +227,99 @@ static uint16_t gic_get_current_pending_irq(GICState *s, int cpu, return pending_irq; } -static void gic_set_running_irq(GICState *s, int cpu, int irq) +static int gic_get_group_priority(GICState *s, int cpu, int irq) { - s->running_irq[cpu] = irq; - if (irq == 1023) { - s->running_priority[cpu] = 0x100; + /* Return the group priority of the specified interrupt + * (which is the top bits of its priority, with the number + * of bits masked determined by the applicable binary point register). + */ + int bpr; + uint32_t mask; + + if (gic_has_groups(s) && + !(s->cpu_ctlr[cpu] & GICC_CTLR_CBPR) && + GIC_TEST_GROUP(irq, (1 << cpu))) { + bpr = s->abpr[cpu]; } else { - s->running_priority[cpu] = GIC_GET_PRIORITY(irq, cpu); + bpr = s->bpr[cpu]; } - gic_update(s); + + /* a BPR of 0 means the group priority bits are [7:1]; + * a BPR of 1 means they are [7:2], and so on down to + * a BPR of 7 meaning no group priority bits at all. + */ + mask = ~0U << ((bpr & 7) + 1); + + return GIC_GET_PRIORITY(irq, cpu) & mask; +} + +static void gic_activate_irq(GICState *s, int cpu, int irq) +{ + /* Set the appropriate Active Priority Register bit for this IRQ, + * and update the running priority. + */ + int prio = gic_get_group_priority(s, cpu, irq); + int preemption_level = prio >> (GIC_MIN_BPR + 1); + int regno = preemption_level / 32; + int bitno = preemption_level % 32; + + if (gic_has_groups(s) && GIC_TEST_GROUP(irq, (1 << cpu))) { + s->nsapr[regno][cpu] |= (1 << bitno); + } else { + s->apr[regno][cpu] |= (1 << bitno); + } + + s->running_priority[cpu] = prio; + GIC_SET_ACTIVE(irq, 1 << cpu); +} + +static int gic_get_prio_from_apr_bits(GICState *s, int cpu) +{ + /* Recalculate the current running priority for this CPU based + * on the set bits in the Active Priority Registers. + */ + int i; + for (i = 0; i < GIC_NR_APRS; i++) { + uint32_t apr = s->apr[i][cpu] | s->nsapr[i][cpu]; + if (!apr) { + continue; + } + return (i * 32 + ctz32(apr)) << (GIC_MIN_BPR + 1); + } + return 0x100; +} + +static void gic_drop_prio(GICState *s, int cpu, int group) +{ + /* Drop the priority of the currently active interrupt in the + * specified group. + * + * Note that we can guarantee (because of the requirement to nest + * GICC_IAR reads [which activate an interrupt and raise priority] + * with GICC_EOIR writes [which drop the priority for the interrupt]) + * that the interrupt we're being called for is the highest priority + * active interrupt, meaning that it has the lowest set bit in the + * APR registers. + * + * If the guest does not honour the ordering constraints then the + * behaviour of the GIC is UNPREDICTABLE, which for us means that + * the values of the APR registers might become incorrect and the + * running priority will be wrong, so interrupts that should preempt + * might not do so, and interrupts that should not preempt might do so. + */ + int i; + + for (i = 0; i < GIC_NR_APRS; i++) { + uint32_t *papr = group ? &s->nsapr[i][cpu] : &s->apr[i][cpu]; + if (!*papr) { + continue; + } + /* Clear lowest set bit */ + *papr &= *papr - 1; + break; + } + + s->running_priority[cpu] = gic_get_prio_from_apr_bits(s, cpu); } uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs) @@ -239,7 +331,7 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs) * for the case where this GIC supports grouping and the pending interrupt * is in the wrong group. */ - irq = gic_get_current_pending_irq(s, cpu, attrs);; + irq = gic_get_current_pending_irq(s, cpu, attrs); if (irq >= GIC_MAXIRQ) { DPRINTF("ACK, no pending interrupt or it is hidden: %d\n", irq); @@ -250,7 +342,6 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs) DPRINTF("ACK, pending interrupt (%d) has insufficient priority\n", irq); return 1023; } - s->last_active[irq][cpu] = s->running_irq[cpu]; if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { /* Clear pending flags for both level and edge triggered interrupts. @@ -281,7 +372,8 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs) } } - gic_set_running_irq(s, cpu, irq); + gic_activate_irq(s, cpu, irq); + gic_update(s); DPRINTF("ACK %d\n", irq); return ret; } @@ -409,10 +501,46 @@ static uint8_t gic_get_running_priority(GICState *s, int cpu, MemTxAttrs attrs) } } +/* Return true if we should split priority drop and interrupt deactivation, + * ie whether the relevant EOIMode bit is set. + */ +static bool gic_eoi_split(GICState *s, int cpu, MemTxAttrs attrs) +{ + if (s->revision != 2) { + /* Before GICv2 prio-drop and deactivate are not separable */ + return false; + } + if (s->security_extn && !attrs.secure) { + return s->cpu_ctlr[cpu] & GICC_CTLR_EOIMODE_NS; + } + return s->cpu_ctlr[cpu] & GICC_CTLR_EOIMODE; +} + +static void gic_deactivate_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) +{ + int cm = 1 << cpu; + int group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm); + + if (!gic_eoi_split(s, cpu, attrs)) { + /* This is UNPREDICTABLE; we choose to ignore it */ + qemu_log_mask(LOG_GUEST_ERROR, + "gic_deactivate_irq: GICC_DIR write when EOIMode clear"); + return; + } + + if (s->security_extn && !attrs.secure && !group) { + DPRINTF("Non-secure DI for Group0 interrupt %d ignored\n", irq); + return; + } + + GIC_CLEAR_ACTIVE(irq, cm); +} + void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) { - int update = 0; int cm = 1 << cpu; + int group; + DPRINTF("EOI %d\n", irq); if (irq >= s->num_irq) { /* This handles two cases: @@ -425,8 +553,9 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) */ return; } - if (s->running_irq[cpu] == 1023) + if (s->running_priority[cpu] == 0x100) { return; /* No active IRQ. */ + } if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { /* Mark level triggered interrupts as pending if they are still @@ -435,11 +564,12 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) { DPRINTF("Set %d pending mask %x\n", irq, cm); GIC_SET_PENDING(irq, cm); - update = 1; } } - if (s->security_extn && !attrs.secure && !GIC_TEST_GROUP(irq, cm)) { + group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm); + + if (s->security_extn && !attrs.secure && !group) { DPRINTF("Non-secure EOI for Group0 interrupt %d ignored\n", irq); return; } @@ -449,23 +579,13 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) * i.e. go ahead and complete the irq anyway. */ - if (irq != s->running_irq[cpu]) { - /* Complete an IRQ that is not currently running. */ - int tmp = s->running_irq[cpu]; - while (s->last_active[tmp][cpu] != 1023) { - if (s->last_active[tmp][cpu] == irq) { - s->last_active[tmp][cpu] = s->last_active[irq][cpu]; - break; - } - tmp = s->last_active[tmp][cpu]; - } - if (update) { - gic_update(s); - } - } else { - /* Complete the current running IRQ. */ - gic_set_running_irq(s, cpu, s->last_active[s->running_irq[cpu]][cpu]); + gic_drop_prio(s, cpu, group); + + /* In GICv2 the guest can choose to split priority-drop and deactivate */ + if (!gic_eoi_split(s, cpu, attrs)) { + GIC_CLEAR_ACTIVE(irq, cm); } + gic_update(s); } static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) @@ -494,7 +614,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) if (offset == 4) /* Interrupt Controller Type Register */ return ((s->num_irq / 32) - 1) - | ((NUM_CPU(s) - 1) << 5) + | ((s->num_cpu - 1) << 5) | (s->security_extn << 10); if (offset < 0x08) return 0; @@ -612,14 +732,31 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) } res = s->sgi_pending[irq][cpu]; - } else if (offset < 0xfe0) { + } else if (offset < 0xfd0) { goto bad_reg; - } else /* offset >= 0xfe0 */ { + } else if (offset < 0x1000) { if (offset & 3) { res = 0; } else { - res = gic_id[(offset - 0xfe0) >> 2]; + switch (s->revision) { + case REV_11MPCORE: + res = gic_id_11mpcore[(offset - 0xfd0) >> 2]; + break; + case 1: + res = gic_id_gicv1[(offset - 0xfd0) >> 2]; + break; + case 2: + res = gic_id_gicv2[(offset - 0xfd0) >> 2]; + break; + case REV_NVIC: + /* Shouldn't be able to get here */ + abort(); + default: + res = 0; + } } + } else { + g_assert_not_reached(); } return res; bad_reg: @@ -922,11 +1059,67 @@ static MemTxResult gic_dist_write(void *opaque, hwaddr offset, uint64_t data, } } -static const MemoryRegionOps gic_dist_ops = { - .read_with_attrs = gic_dist_read, - .write_with_attrs = gic_dist_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; +static inline uint32_t gic_apr_ns_view(GICState *s, int cpu, int regno) +{ + /* Return the Nonsecure view of GICC_APR. This is the + * second half of GICC_NSAPR. + */ + switch (GIC_MIN_BPR) { + case 0: + if (regno < 2) { + return s->nsapr[regno + 2][cpu]; + } + break; + case 1: + if (regno == 0) { + return s->nsapr[regno + 1][cpu]; + } + break; + case 2: + if (regno == 0) { + return extract32(s->nsapr[0][cpu], 16, 16); + } + break; + case 3: + if (regno == 0) { + return extract32(s->nsapr[0][cpu], 8, 8); + } + break; + default: + g_assert_not_reached(); + } + return 0; +} + +static inline void gic_apr_write_ns_view(GICState *s, int cpu, int regno, + uint32_t value) +{ + /* Write the Nonsecure view of GICC_APR. */ + switch (GIC_MIN_BPR) { + case 0: + if (regno < 2) { + s->nsapr[regno + 2][cpu] = value; + } + break; + case 1: + if (regno == 0) { + s->nsapr[regno + 1][cpu] = value; + } + break; + case 2: + if (regno == 0) { + s->nsapr[0][cpu] = deposit32(s->nsapr[0][cpu], 16, 16, value); + } + break; + case 3: + if (regno == 0) { + s->nsapr[0][cpu] = deposit32(s->nsapr[0][cpu], 8, 8, value); + } + break; + default: + g_assert_not_reached(); + } +} static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset, uint64_t *data, MemTxAttrs attrs) @@ -968,8 +1161,31 @@ static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset, } break; case 0xd0: case 0xd4: case 0xd8: case 0xdc: - *data = s->apr[(offset - 0xd0) / 4][cpu]; + { + int regno = (offset - 0xd0) / 4; + + if (regno >= GIC_NR_APRS || s->revision != 2) { + *data = 0; + } else if (s->security_extn && !attrs.secure) { + /* NS view of GICC_APR is the top half of GIC_NSAPR */ + *data = gic_apr_ns_view(s, regno, cpu); + } else { + *data = s->apr[regno][cpu]; + } + break; + } + case 0xe0: case 0xe4: case 0xe8: case 0xec: + { + int regno = (offset - 0xe0) / 4; + + if (regno >= GIC_NR_APRS || s->revision != 2 || !gic_has_groups(s) || + (s->security_extn && !attrs.secure)) { + *data = 0; + } else { + *data = s->nsapr[regno][cpu]; + } break; + } default: qemu_log_mask(LOG_GUEST_ERROR, "gic_cpu_read: Bad offset %x\n", (int)offset); @@ -1007,7 +1223,36 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset, } break; case 0xd0: case 0xd4: case 0xd8: case 0xdc: - qemu_log_mask(LOG_UNIMP, "Writing APR not implemented\n"); + { + int regno = (offset - 0xd0) / 4; + + if (regno >= GIC_NR_APRS || s->revision != 2) { + return MEMTX_OK; + } + if (s->security_extn && !attrs.secure) { + /* NS view of GICC_APR is the top half of GIC_NSAPR */ + gic_apr_write_ns_view(s, regno, cpu, value); + } else { + s->apr[regno][cpu] = value; + } + break; + } + case 0xe0: case 0xe4: case 0xe8: case 0xec: + { + int regno = (offset - 0xe0) / 4; + + if (regno >= GIC_NR_APRS || s->revision != 2) { + return MEMTX_OK; + } + if (!gic_has_groups(s) || (s->security_extn && !attrs.secure)) { + return MEMTX_OK; + } + s->nsapr[regno][cpu] = value; + break; + } + case 0x1000: + /* GICC_DIR */ + gic_deactivate_irq(s, cpu, value & 0x3ff, attrs); break; default: qemu_log_mask(LOG_GUEST_ERROR, @@ -1056,10 +1301,17 @@ static MemTxResult gic_do_cpu_write(void *opaque, hwaddr addr, return gic_cpu_write(s, id, addr, value, attrs); } -static const MemoryRegionOps gic_thiscpu_ops = { - .read_with_attrs = gic_thiscpu_read, - .write_with_attrs = gic_thiscpu_write, - .endianness = DEVICE_NATIVE_ENDIAN, +static const MemoryRegionOps gic_ops[2] = { + { + .read_with_attrs = gic_dist_read, + .write_with_attrs = gic_dist_write, + .endianness = DEVICE_NATIVE_ENDIAN, + }, + { + .read_with_attrs = gic_thiscpu_read, + .write_with_attrs = gic_thiscpu_write, + .endianness = DEVICE_NATIVE_ENDIAN, + } }; static const MemoryRegionOps gic_cpu_ops = { @@ -1068,31 +1320,10 @@ static const MemoryRegionOps gic_cpu_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; +/* This function is used by nvic model */ void gic_init_irqs_and_distributor(GICState *s) { - SysBusDevice *sbd = SYS_BUS_DEVICE(s); - int i; - - i = s->num_irq - GIC_INTERNAL; - /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU. - * GPIO array layout is thus: - * [0..N-1] SPIs - * [N..N+31] PPIs for CPU 0 - * [N+32..N+63] PPIs for CPU 1 - * ... - */ - if (s->revision != REV_NVIC) { - i += (GIC_INTERNAL * s->num_cpu); - } - qdev_init_gpio_in(DEVICE(s), gic_set_irq, i); - for (i = 0; i < NUM_CPU(s); i++) { - sysbus_init_irq(sbd, &s->parent_irq[i]); - } - for (i = 0; i < NUM_CPU(s); i++) { - sysbus_init_irq(sbd, &s->parent_fiq[i]); - } - memory_region_init_io(&s->iomem, OBJECT(s), &gic_dist_ops, s, - "gic_dist", 0x1000); + gic_init_irqs_and_mmio(s, gic_set_irq, gic_ops); } static void arm_gic_realize(DeviceState *dev, Error **errp) @@ -1110,28 +1341,22 @@ static void arm_gic_realize(DeviceState *dev, Error **errp) return; } - gic_init_irqs_and_distributor(s); + /* This creates distributor and main CPU interface (s->cpuiomem[0]) */ + gic_init_irqs_and_mmio(s, gic_set_irq, gic_ops); - /* Memory regions for the CPU interfaces (NVIC doesn't have these): - * a region for "CPU interface for this core", then a region for - * "CPU interface for core 0", "for core 1", ... + /* Extra core-specific regions for the CPU interfaces. This is + * necessary for "franken-GIC" implementations, for example on + * Exynos 4. * NB that the memory region size of 0x100 applies for the 11MPCore * and also cores following the GIC v1 spec (ie A9). * GIC v2 defines a larger memory region (0x1000) so this will need * to be extended when we implement A15. */ - memory_region_init_io(&s->cpuiomem[0], OBJECT(s), &gic_thiscpu_ops, s, - "gic_cpu", 0x100); - for (i = 0; i < NUM_CPU(s); i++) { + for (i = 0; i < s->num_cpu; i++) { s->backref[i] = s; memory_region_init_io(&s->cpuiomem[i+1], OBJECT(s), &gic_cpu_ops, &s->backref[i], "gic_cpu", 0x100); - } - /* Distributor */ - sysbus_init_mmio(sbd, &s->iomem); - /* cpu interfaces (one for "current cpu" plus one per cpu) */ - for (i = 0; i <= NUM_CPU(s); i++) { - sysbus_init_mmio(sbd, &s->cpuiomem[i]); + sysbus_init_mmio(sbd, &s->cpuiomem[i+1]); } } diff --git a/qemu/hw/intc/arm_gic_common.c b/qemu/hw/intc/arm_gic_common.c index a64d0714e..0a1f56af1 100644 --- a/qemu/hw/intc/arm_gic_common.c +++ b/qemu/hw/intc/arm_gic_common.c @@ -18,7 +18,10 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "gic_internal.h" +#include "hw/arm/linux-boot-if.h" static void gic_pre_save(void *opaque) { @@ -59,8 +62,8 @@ static const VMStateDescription vmstate_gic_irq_state = { static const VMStateDescription vmstate_gic = { .name = "arm_gic", - .version_id = 10, - .minimum_version_id = 10, + .version_id = 12, + .minimum_version_id = 12, .pre_save = gic_pre_save, .post_load = gic_post_load, .fields = (VMStateField[]) { @@ -71,19 +74,59 @@ static const VMStateDescription vmstate_gic = { VMSTATE_UINT8_ARRAY(irq_target, GICState, GIC_MAXIRQ), VMSTATE_UINT8_2DARRAY(priority1, GICState, GIC_INTERNAL, GIC_NCPU), VMSTATE_UINT8_ARRAY(priority2, GICState, GIC_MAXIRQ - GIC_INTERNAL), - VMSTATE_UINT16_2DARRAY(last_active, GICState, GIC_MAXIRQ, GIC_NCPU), VMSTATE_UINT8_2DARRAY(sgi_pending, GICState, GIC_NR_SGIS, GIC_NCPU), VMSTATE_UINT16_ARRAY(priority_mask, GICState, GIC_NCPU), - VMSTATE_UINT16_ARRAY(running_irq, GICState, GIC_NCPU), VMSTATE_UINT16_ARRAY(running_priority, GICState, GIC_NCPU), VMSTATE_UINT16_ARRAY(current_pending, GICState, GIC_NCPU), VMSTATE_UINT8_ARRAY(bpr, GICState, GIC_NCPU), VMSTATE_UINT8_ARRAY(abpr, GICState, GIC_NCPU), VMSTATE_UINT32_2DARRAY(apr, GICState, GIC_NR_APRS, GIC_NCPU), + VMSTATE_UINT32_2DARRAY(nsapr, GICState, GIC_NR_APRS, GIC_NCPU), VMSTATE_END_OF_LIST() } }; +void gic_init_irqs_and_mmio(GICState *s, qemu_irq_handler handler, + const MemoryRegionOps *ops) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(s); + int i = s->num_irq - GIC_INTERNAL; + + /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU. + * GPIO array layout is thus: + * [0..N-1] SPIs + * [N..N+31] PPIs for CPU 0 + * [N+32..N+63] PPIs for CPU 1 + * ... + */ + if (s->revision != REV_NVIC) { + i += (GIC_INTERNAL * s->num_cpu); + } + qdev_init_gpio_in(DEVICE(s), handler, i); + + for (i = 0; i < s->num_cpu; i++) { + sysbus_init_irq(sbd, &s->parent_irq[i]); + } + for (i = 0; i < s->num_cpu; i++) { + sysbus_init_irq(sbd, &s->parent_fiq[i]); + } + + /* Distributor */ + memory_region_init_io(&s->iomem, OBJECT(s), ops, s, "gic_dist", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + + if (s->revision != REV_NVIC) { + /* This is the main CPU interface "for this core". It is always + * present because it is required by both software emulation and KVM. + * NVIC is not handled here because its CPU interface is different, + * neither it can use KVM. + */ + memory_region_init_io(&s->cpuiomem[0], OBJECT(s), ops ? &ops[1] : NULL, + s, "gic_cpu", s->revision == 2 ? 0x2000 : 0x100); + sysbus_init_mmio(sbd, &s->cpuiomem[0]); + } +} + static void arm_gic_common_realize(DeviceState *dev, Error **errp) { GICState *s = ARM_GIC_COMMON(dev); @@ -124,21 +167,35 @@ static void arm_gic_common_reset(DeviceState *dev) { GICState *s = ARM_GIC_COMMON(dev); int i, j; + int resetprio; + + /* If we're resetting a TZ-aware GIC as if secure firmware + * had set it up ready to start a kernel in non-secure, + * we need to set interrupt priorities to a "zero for the + * NS view" value. This is particularly critical for the + * priority_mask[] values, because if they are zero then NS + * code cannot ever rewrite the priority to anything else. + */ + if (s->security_extn && s->irq_reset_nonsecure) { + resetprio = 0x80; + } else { + resetprio = 0; + } + memset(s->irq_state, 0, GIC_MAXIRQ * sizeof(gic_irq_state)); for (i = 0 ; i < s->num_cpu; i++) { if (s->revision == REV_11MPCORE) { s->priority_mask[i] = 0xf0; } else { - s->priority_mask[i] = 0; + s->priority_mask[i] = resetprio; } s->current_pending[i] = 1023; - s->running_irq[i] = 1023; s->running_priority[i] = 0x100; s->cpu_ctlr[i] = 0; s->bpr[i] = GIC_MIN_BPR; s->abpr[i] = GIC_MIN_ABPR; for (j = 0; j < GIC_INTERNAL; j++) { - s->priority1[j][i] = 0; + s->priority1[j][i] = resetprio; } for (j = 0; j < GIC_NR_SGIS; j++) { s->sgi_pending[j][i] = 0; @@ -150,7 +207,7 @@ static void arm_gic_common_reset(DeviceState *dev) } for (i = 0; i < ARRAY_SIZE(s->priority2); i++) { - s->priority2[i] = 0; + s->priority2[i] = resetprio; } for (i = 0; i < GIC_MAXIRQ; i++) { @@ -161,9 +218,32 @@ static void arm_gic_common_reset(DeviceState *dev) s->irq_target[i] = 0; } } + if (s->security_extn && s->irq_reset_nonsecure) { + for (i = 0; i < GIC_MAXIRQ; i++) { + GIC_SET_GROUP(i, ALL_CPU_MASK); + } + } + s->ctlr = 0; } +static void arm_gic_common_linux_init(ARMLinuxBootIf *obj, + bool secure_boot) +{ + GICState *s = ARM_GIC_COMMON(obj); + + if (s->security_extn && !secure_boot) { + /* We're directly booting a kernel into NonSecure. If this GIC + * implements the security extensions then we must configure it + * to have all the interrupts be NonSecure (this is a job that + * is done by the Secure boot firmware in real hardware, and in + * this mode QEMU is acting as a minimalist firmware-and-bootloader + * equivalent). + */ + s->irq_reset_nonsecure = true; + } +} + static Property arm_gic_common_properties[] = { DEFINE_PROP_UINT32("num-cpu", GICState, num_cpu, 1), DEFINE_PROP_UINT32("num-irq", GICState, num_irq, 32), @@ -180,11 +260,13 @@ static Property arm_gic_common_properties[] = { static void arm_gic_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ARMLinuxBootIfClass *albifc = ARM_LINUX_BOOT_IF_CLASS(klass); dc->reset = arm_gic_common_reset; dc->realize = arm_gic_common_realize; dc->props = arm_gic_common_properties; dc->vmsd = &vmstate_gic; + albifc->arm_linux_init = arm_gic_common_linux_init; } static const TypeInfo arm_gic_common_type = { @@ -194,6 +276,10 @@ static const TypeInfo arm_gic_common_type = { .class_size = sizeof(ARMGICCommonClass), .class_init = arm_gic_common_class_init, .abstract = true, + .interfaces = (InterfaceInfo []) { + { TYPE_ARM_LINUX_BOOT_IF }, + { }, + }, }; static void register_types(void) diff --git a/qemu/hw/intc/arm_gic_kvm.c b/qemu/hw/intc/arm_gic_kvm.c index f56bff1af..bc85ab769 100644 --- a/qemu/hw/intc/arm_gic_kvm.c +++ b/qemu/hw/intc/arm_gic_kvm.c @@ -19,10 +19,14 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/sysbus.h" +#include "migration/migration.h" #include "sysemu/kvm.h" #include "kvm_arm.h" #include "gic_internal.h" +#include "vgic_common.h" //#define DEBUG_GIC_KVM @@ -52,7 +56,7 @@ typedef struct KVMARMGICClass { void (*parent_reset)(DeviceState *dev); } KVMARMGICClass; -static void kvm_arm_gic_set_irq(void *opaque, int irq, int level) +void kvm_arm_gic_set_irq(uint32_t num_irq, int irq, int level) { /* Meaning of the 'irq' parameter: * [0..N-1] : external interrupts @@ -63,10 +67,9 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level) * has separate fields in the irq number for type, * CPU number and interrupt number. */ - GICState *s = (GICState *)opaque; int kvm_irq, irqtype, cpu; - if (irq < (s->num_irq - GIC_INTERNAL)) { + if (irq < (num_irq - GIC_INTERNAL)) { /* External interrupt. The kernel numbers these like the GIC * hardware, with external interrupt IDs starting after the * internal ones. @@ -77,7 +80,7 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level) } else { /* Internal interrupt: decode into (cpu, interrupt id) */ irqtype = KVM_ARM_IRQ_TYPE_PPI; - irq -= (s->num_irq - GIC_INTERNAL); + irq -= (num_irq - GIC_INTERNAL); cpu = irq / GIC_INTERNAL; irq %= GIC_INTERNAL; } @@ -87,69 +90,36 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level) kvm_set_irq(kvm_state, kvm_irq, !!level); } -static bool kvm_arm_gic_can_save_restore(GICState *s) -{ - return s->dev_fd >= 0; -} - -static bool kvm_gic_supports_attr(GICState *s, int group, int attrnum) +static void kvm_arm_gicv2_set_irq(void *opaque, int irq, int level) { - struct kvm_device_attr attr = { - .group = group, - .attr = attrnum, - .flags = 0, - }; - - if (s->dev_fd == -1) { - return false; - } + GICState *s = (GICState *)opaque; - return kvm_device_ioctl(s->dev_fd, KVM_HAS_DEVICE_ATTR, &attr) == 0; + kvm_arm_gic_set_irq(s->num_irq, irq, level); } -static void kvm_gic_access(GICState *s, int group, int offset, - int cpu, uint32_t *val, bool write) +static bool kvm_arm_gic_can_save_restore(GICState *s) { - struct kvm_device_attr attr; - int type; - int err; - - cpu = cpu & 0xff; - - attr.flags = 0; - attr.group = group; - attr.attr = (((uint64_t)cpu << KVM_DEV_ARM_VGIC_CPUID_SHIFT) & - KVM_DEV_ARM_VGIC_CPUID_MASK) | - (((uint64_t)offset << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) & - KVM_DEV_ARM_VGIC_OFFSET_MASK); - attr.addr = (uintptr_t)val; - - if (write) { - type = KVM_SET_DEVICE_ATTR; - } else { - type = KVM_GET_DEVICE_ATTR; - } - - err = kvm_device_ioctl(s->dev_fd, type, &attr); - if (err < 0) { - fprintf(stderr, "KVM_{SET/GET}_DEVICE_ATTR failed: %s\n", - strerror(-err)); - abort(); - } + return s->dev_fd >= 0; } +#define KVM_VGIC_ATTR(offset, cpu) \ + ((((uint64_t)(cpu) << KVM_DEV_ARM_VGIC_CPUID_SHIFT) & \ + KVM_DEV_ARM_VGIC_CPUID_MASK) | \ + (((uint64_t)(offset) << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) & \ + KVM_DEV_ARM_VGIC_OFFSET_MASK)) + static void kvm_gicd_access(GICState *s, int offset, int cpu, uint32_t *val, bool write) { - kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, - offset, cpu, val, write); + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, + KVM_VGIC_ATTR(offset, cpu), val, write); } static void kvm_gicc_access(GICState *s, int offset, int cpu, uint32_t *val, bool write) { - kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_CPU_REGS, - offset, cpu, val, write); + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_REGS, + KVM_VGIC_ATTR(offset, cpu), val, write); } #define for_each_irq_reg(_ctr, _max_irq, _field_width) \ @@ -340,11 +310,6 @@ static void kvm_arm_gic_put(GICState *s) int num_cpu; int num_irq; - if (!kvm_arm_gic_can_save_restore(s)) { - DPRINTF("Cannot put kernel gic state, no kernel interface"); - return; - } - /* Note: We do the restore in a slightly different order than the save * (where the order doesn't matter and is simply ordered according to the * register offset values */ @@ -444,11 +409,6 @@ static void kvm_arm_gic_get(GICState *s) int i; int cpu; - if (!kvm_arm_gic_can_save_restore(s)) { - DPRINTF("Cannot get kernel gic state, no kernel interface"); - return; - } - /***************************************************************** * Distributor State */ @@ -536,14 +496,16 @@ static void kvm_arm_gic_reset(DeviceState *dev) KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s); kgc->parent_reset(dev); - kvm_arm_gic_put(s); + + if (kvm_arm_gic_can_save_restore(s)) { + kvm_arm_gic_put(s); + } } static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) { int i; GICState *s = KVM_ARM_GIC(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s); Error *local_err = NULL; int ret; @@ -560,58 +522,37 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) return; } - i = s->num_irq - GIC_INTERNAL; - /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU. - * GPIO array layout is thus: - * [0..N-1] SPIs - * [N..N+31] PPIs for CPU 0 - * [N+32..N+63] PPIs for CPU 1 - * ... - */ - i += (GIC_INTERNAL * s->num_cpu); - qdev_init_gpio_in(dev, kvm_arm_gic_set_irq, i); + gic_init_irqs_and_mmio(s, kvm_arm_gicv2_set_irq, NULL); for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) { qemu_irq irq = qdev_get_gpio_in(dev, i); kvm_irqchip_set_qemuirq_gsi(kvm_state, irq, i); } - /* We never use our outbound IRQ/FIQ lines but provide them so that - * we maintain the same interface as the non-KVM GIC. - */ - for (i = 0; i < s->num_cpu; i++) { - sysbus_init_irq(sbd, &s->parent_irq[i]); - } - for (i = 0; i < s->num_cpu; i++) { - sysbus_init_irq(sbd, &s->parent_fiq[i]); - } - /* Try to create the device via the device control API */ s->dev_fd = -1; ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_V2, false); if (ret >= 0) { s->dev_fd = ret; + + /* Newstyle API is used, we may have attributes */ + if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)) { + uint32_t numirqs = s->num_irq; + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, + &numirqs, true); + } + /* Tell the kernel to complete VGIC initialization now */ + if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_CTRL_INIT)) { + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true); + } } else if (ret != -ENODEV && ret != -ENOTSUP) { error_setg_errno(errp, -ret, "error creating in-kernel VGIC"); return; } - if (kvm_gic_supports_attr(s, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)) { - uint32_t numirqs = s->num_irq; - kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, 0, &numirqs, 1); - } - - /* Tell the kernel to complete VGIC initialization now */ - if (kvm_gic_supports_attr(s, KVM_DEV_ARM_VGIC_GRP_CTRL, - KVM_DEV_ARM_VGIC_CTRL_INIT)) { - kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_CTRL, - KVM_DEV_ARM_VGIC_CTRL_INIT, 0, 0, 1); - } - /* Distributor */ - memory_region_init_reservation(&s->iomem, OBJECT(s), - "kvm-gic_dist", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); kvm_arm_register_device(&s->iomem, (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT) | KVM_VGIC_V2_ADDR_TYPE_DIST, @@ -622,15 +563,18 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) * provide the "interface for core #N" memory regions, because * cores with a VGIC don't have those. */ - memory_region_init_reservation(&s->cpuiomem[0], OBJECT(s), - "kvm-gic_cpu", 0x1000); - sysbus_init_mmio(sbd, &s->cpuiomem[0]); kvm_arm_register_device(&s->cpuiomem[0], (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT) | KVM_VGIC_V2_ADDR_TYPE_CPU, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V2_ADDR_TYPE_CPU, s->dev_fd); + + if (!kvm_arm_gic_can_save_restore(s)) { + error_setg(&s->migration_blocker, "This operating system kernel does " + "not support vGICv2 migration"); + migrate_add_blocker(s->migration_blocker); + } } static void kvm_arm_gic_class_init(ObjectClass *klass, void *data) diff --git a/qemu/hw/intc/arm_gicv2m.c b/qemu/hw/intc/arm_gicv2m.c index 43d1976c4..e8b5177dc 100644 --- a/qemu/hw/intc/arm_gicv2m.c +++ b/qemu/hw/intc/arm_gicv2m.c @@ -25,6 +25,8 @@ * identification registers and with a single non-secure MSI register frame. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/sysbus.h" #include "hw/pci/msi.h" @@ -147,7 +149,7 @@ static void gicv2m_realize(DeviceState *dev, Error **errp) sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->spi[i]); } - msi_supported = true; + msi_nonbroken = true; kvm_gsi_direct_mapping = true; kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled(); } diff --git a/qemu/hw/intc/arm_gicv3_common.c b/qemu/hw/intc/arm_gicv3_common.c new file mode 100644 index 000000000..b9d3824f2 --- /dev/null +++ b/qemu/hw/intc/arm_gicv3_common.c @@ -0,0 +1,142 @@ +/* + * ARM GICv3 support - common bits of emulated and KVM kernel model + * + * Copyright (c) 2012 Linaro Limited + * Copyright (c) 2015 Huawei. + * Written by Peter Maydell + * Extended to 64 cores by Shlomo Pongratz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/intc/arm_gicv3_common.h" + +static void gicv3_pre_save(void *opaque) +{ + GICv3State *s = (GICv3State *)opaque; + ARMGICv3CommonClass *c = ARM_GICV3_COMMON_GET_CLASS(s); + + if (c->pre_save) { + c->pre_save(s); + } +} + +static int gicv3_post_load(void *opaque, int version_id) +{ + GICv3State *s = (GICv3State *)opaque; + ARMGICv3CommonClass *c = ARM_GICV3_COMMON_GET_CLASS(s); + + if (c->post_load) { + c->post_load(s); + } + return 0; +} + +static const VMStateDescription vmstate_gicv3 = { + .name = "arm_gicv3", + .unmigratable = 1, + .pre_save = gicv3_pre_save, + .post_load = gicv3_post_load, +}; + +void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, + const MemoryRegionOps *ops) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(s); + int i; + + /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU. + * GPIO array layout is thus: + * [0..N-1] spi + * [N..N+31] PPIs for CPU 0 + * [N+32..N+63] PPIs for CPU 1 + * ... + */ + i = s->num_irq - GIC_INTERNAL + GIC_INTERNAL * s->num_cpu; + qdev_init_gpio_in(DEVICE(s), handler, i); + + s->parent_irq = g_malloc(s->num_cpu * sizeof(qemu_irq)); + s->parent_fiq = g_malloc(s->num_cpu * sizeof(qemu_irq)); + + for (i = 0; i < s->num_cpu; i++) { + sysbus_init_irq(sbd, &s->parent_irq[i]); + } + for (i = 0; i < s->num_cpu; i++) { + sysbus_init_irq(sbd, &s->parent_fiq[i]); + } + + memory_region_init_io(&s->iomem_dist, OBJECT(s), ops, s, + "gicv3_dist", 0x10000); + memory_region_init_io(&s->iomem_redist, OBJECT(s), ops ? &ops[1] : NULL, s, + "gicv3_redist", 0x20000 * s->num_cpu); + + sysbus_init_mmio(sbd, &s->iomem_dist); + sysbus_init_mmio(sbd, &s->iomem_redist); +} + +static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) +{ + GICv3State *s = ARM_GICV3_COMMON(dev); + + /* revision property is actually reserved and currently used only in order + * to keep the interface compatible with GICv2 code, avoiding extra + * conditions. However, in future it could be used, for example, if we + * implement GICv4. + */ + if (s->revision != 3) { + error_setg(errp, "unsupported GIC revision %d", s->revision); + return; + } +} + +static void arm_gicv3_common_reset(DeviceState *dev) +{ + /* TODO */ +} + +static Property arm_gicv3_common_properties[] = { + DEFINE_PROP_UINT32("num-cpu", GICv3State, num_cpu, 1), + DEFINE_PROP_UINT32("num-irq", GICv3State, num_irq, 32), + DEFINE_PROP_UINT32("revision", GICv3State, revision, 3), + DEFINE_PROP_BOOL("has-security-extensions", GICv3State, security_extn, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void arm_gicv3_common_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = arm_gicv3_common_reset; + dc->realize = arm_gicv3_common_realize; + dc->props = arm_gicv3_common_properties; + dc->vmsd = &vmstate_gicv3; +} + +static const TypeInfo arm_gicv3_common_type = { + .name = TYPE_ARM_GICV3_COMMON, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(GICv3State), + .class_size = sizeof(ARMGICv3CommonClass), + .class_init = arm_gicv3_common_class_init, + .abstract = true, +}; + +static void register_types(void) +{ + type_register_static(&arm_gicv3_common_type); +} + +type_init(register_types) diff --git a/qemu/hw/intc/arm_gicv3_kvm.c b/qemu/hw/intc/arm_gicv3_kvm.c new file mode 100644 index 000000000..acc173004 --- /dev/null +++ b/qemu/hw/intc/arm_gicv3_kvm.c @@ -0,0 +1,151 @@ +/* + * ARM Generic Interrupt Controller using KVM in-kernel support + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * Written by Pavel Fedin + * Based on vGICv2 code by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/intc/arm_gicv3_common.h" +#include "hw/sysbus.h" +#include "sysemu/kvm.h" +#include "kvm_arm.h" +#include "vgic_common.h" + +#ifdef DEBUG_GICV3_KVM +#define DPRINTF(fmt, ...) \ + do { fprintf(stderr, "kvm_gicv3: " fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +#define TYPE_KVM_ARM_GICV3 "kvm-arm-gicv3" +#define KVM_ARM_GICV3(obj) \ + OBJECT_CHECK(GICv3State, (obj), TYPE_KVM_ARM_GICV3) +#define KVM_ARM_GICV3_CLASS(klass) \ + OBJECT_CLASS_CHECK(KVMARMGICv3Class, (klass), TYPE_KVM_ARM_GICV3) +#define KVM_ARM_GICV3_GET_CLASS(obj) \ + OBJECT_GET_CLASS(KVMARMGICv3Class, (obj), TYPE_KVM_ARM_GICV3) + +typedef struct KVMARMGICv3Class { + ARMGICv3CommonClass parent_class; + DeviceRealize parent_realize; + void (*parent_reset)(DeviceState *dev); +} KVMARMGICv3Class; + +static void kvm_arm_gicv3_set_irq(void *opaque, int irq, int level) +{ + GICv3State *s = (GICv3State *)opaque; + + kvm_arm_gic_set_irq(s->num_irq, irq, level); +} + +static void kvm_arm_gicv3_put(GICv3State *s) +{ + /* TODO */ + DPRINTF("Cannot put kernel gic state, no kernel interface\n"); +} + +static void kvm_arm_gicv3_get(GICv3State *s) +{ + /* TODO */ + DPRINTF("Cannot get kernel gic state, no kernel interface\n"); +} + +static void kvm_arm_gicv3_reset(DeviceState *dev) +{ + GICv3State *s = ARM_GICV3_COMMON(dev); + KVMARMGICv3Class *kgc = KVM_ARM_GICV3_GET_CLASS(s); + + DPRINTF("Reset\n"); + + kgc->parent_reset(dev); + kvm_arm_gicv3_put(s); +} + +static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) +{ + GICv3State *s = KVM_ARM_GICV3(dev); + KVMARMGICv3Class *kgc = KVM_ARM_GICV3_GET_CLASS(s); + Error *local_err = NULL; + + DPRINTF("kvm_arm_gicv3_realize\n"); + + kgc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (s->security_extn) { + error_setg(errp, "the in-kernel VGICv3 does not implement the " + "security extensions"); + return; + } + + gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL); + + /* Try to create the device via the device control API */ + s->dev_fd = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_V3, false); + if (s->dev_fd < 0) { + error_setg_errno(errp, -s->dev_fd, "error creating in-kernel VGIC"); + return; + } + + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, + 0, &s->num_irq, true); + + /* Tell the kernel to complete VGIC initialization now */ + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true); + + kvm_arm_register_device(&s->iomem_dist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd); + kvm_arm_register_device(&s->iomem_redist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd); +} + +static void kvm_arm_gicv3_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass); + KVMARMGICv3Class *kgc = KVM_ARM_GICV3_CLASS(klass); + + agcc->pre_save = kvm_arm_gicv3_get; + agcc->post_load = kvm_arm_gicv3_put; + kgc->parent_realize = dc->realize; + kgc->parent_reset = dc->reset; + dc->realize = kvm_arm_gicv3_realize; + dc->reset = kvm_arm_gicv3_reset; +} + +static const TypeInfo kvm_arm_gicv3_info = { + .name = TYPE_KVM_ARM_GICV3, + .parent = TYPE_ARM_GICV3_COMMON, + .instance_size = sizeof(GICv3State), + .class_init = kvm_arm_gicv3_class_init, + .class_size = sizeof(KVMARMGICv3Class), +}; + +static void kvm_arm_gicv3_register_types(void) +{ + type_register_static(&kvm_arm_gicv3_info); +} + +type_init(kvm_arm_gicv3_register_types) diff --git a/qemu/hw/intc/armv7m_nvic.c b/qemu/hw/intc/armv7m_nvic.c index e13b729e1..669e82adf 100644 --- a/qemu/hw/intc/armv7m_nvic.c +++ b/qemu/hw/intc/armv7m_nvic.c @@ -10,6 +10,9 @@ * NVIC. Much of that is also implemented here. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" #include "hw/sysbus.h" #include "qemu/timer.h" #include "hw/arm/arm.h" @@ -28,6 +31,7 @@ typedef struct { MemoryRegion gic_iomem_alias; MemoryRegion container; uint32_t num_irq; + qemu_irq sysresetreq; } nvic_state; #define TYPE_NVIC "armv7m_nvic" @@ -185,26 +189,25 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset) return cpu->midr; case 0xd04: /* Interrupt Control State. */ /* VECTACTIVE */ - val = s->gic.running_irq[0]; + cpu = ARM_CPU(current_cpu); + val = cpu->env.v7m.exception; if (val == 1023) { val = 0; } else if (val >= 32) { val -= 16; } - /* RETTOBASE */ - if (s->gic.running_irq[0] == 1023 - || s->gic.last_active[s->gic.running_irq[0]][0] == 1023) { - val |= (1 << 11); - } /* VECTPENDING */ if (s->gic.current_pending[0] != 1023) val |= (s->gic.current_pending[0] << 12); - /* ISRPENDING */ + /* ISRPENDING and RETTOBASE */ for (irq = 32; irq < s->num_irq; irq++) { if (s->gic.irq_state[irq].pending) { val |= (1 << 22); break; } + if (irq != cpu->env.v7m.exception && s->gic.irq_state[irq].active) { + val |= (1 << 11); + } } /* PENDSTSET */ if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending) @@ -349,10 +352,13 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value) break; case 0xd0c: /* Application Interrupt/Reset Control. */ if ((value >> 16) == 0x05fa) { + if (value & 4) { + qemu_irq_pulse(s->sysresetreq); + } if (value & 2) { qemu_log_mask(LOG_UNIMP, "VECTCLRACTIVE unimplemented\n"); } - if (value & 5) { + if (value & 1) { qemu_log_mask(LOG_UNIMP, "AIRCR system reset unimplemented\n"); } if (value & 0x700) { @@ -536,11 +542,14 @@ static void armv7m_nvic_instance_init(Object *obj) * value in the GICState struct. */ GICState *s = ARM_GIC_COMMON(obj); + DeviceState *dev = DEVICE(obj); + nvic_state *nvic = NVIC(obj); /* The ARM v7m may have anything from 0 to 496 external interrupt * IRQ lines. We default to 64. Other boards may differ and should * set the num-irq property appropriately. */ s->num_irq = 64; + qdev_init_gpio_out_named(dev, &nvic->sysresetreq, "SYSRESETREQ", 1); } static void armv7m_nvic_class_init(ObjectClass *klass, void *data) diff --git a/qemu/hw/intc/aspeed_vic.c b/qemu/hw/intc/aspeed_vic.c new file mode 100644 index 000000000..19a0ff748 --- /dev/null +++ b/qemu/hw/intc/aspeed_vic.c @@ -0,0 +1,339 @@ +/* + * ASPEED Interrupt Controller (New) + * + * Andrew Jeffery + * + * Copyright 2015, 2016 IBM Corp. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +/* The hardware exposes two register sets, a legacy set and a 'new' set. The + * model implements the 'new' register set, and logs warnings on accesses to + * the legacy IO space. + * + * The hardware uses 32bit registers to manage 51 IRQs, with low and high + * registers for each conceptual register. The device model's implementation + * uses 64bit data types to store both low and high register values (in the one + * member), but must cope with access offset values in multiples of 4 passed to + * the callbacks. As such the read() and write() implementations process the + * provided offset to understand whether the access is requesting the lower or + * upper 32 bits of the 64bit member. + * + * Additionally, the "Interrupt Enable", "Edge Status" and "Software Interrupt" + * fields have separate "enable"/"status" and "clear" registers, where set bits + * are written to one or the other to change state (avoiding a + * read-modify-write sequence). + */ + +#include "qemu/osdep.h" +#include +#include "hw/intc/aspeed_vic.h" +#include "qemu/bitops.h" +#include "trace.h" + +#define AVIC_NEW_BASE_OFFSET 0x80 + +#define AVIC_L_MASK 0xFFFFFFFFU +#define AVIC_H_MASK 0x0007FFFFU +#define AVIC_EVENT_W_MASK (0x78000ULL << 32) + +static void aspeed_vic_update(AspeedVICState *s) +{ + uint64_t new = (s->raw & s->enable); + uint64_t flags; + + flags = new & s->select; + trace_aspeed_vic_update_fiq(!!flags); + qemu_set_irq(s->fiq, !!flags); + + flags = new & ~s->select; + trace_aspeed_vic_update_irq(!!flags); + qemu_set_irq(s->irq, !!flags); +} + +static void aspeed_vic_set_irq(void *opaque, int irq, int level) +{ + uint64_t irq_mask; + bool raise; + AspeedVICState *s = (AspeedVICState *)opaque; + + if (irq > ASPEED_VIC_NR_IRQS) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + __func__, irq); + return; + } + + trace_aspeed_vic_set_irq(irq, level); + + irq_mask = BIT(irq); + if (s->sense & irq_mask) { + /* level-triggered */ + if (s->event & irq_mask) { + /* high-sensitive */ + raise = level; + } else { + /* low-sensitive */ + raise = !level; + } + s->raw = deposit64(s->raw, irq, 1, raise); + } else { + uint64_t old_level = s->level & irq_mask; + + /* edge-triggered */ + if (s->dual_edge & irq_mask) { + raise = (!!old_level) != (!!level); + } else { + if (s->event & irq_mask) { + /* rising-sensitive */ + raise = !old_level && level; + } else { + /* falling-sensitive */ + raise = old_level && !level; + } + } + if (raise) { + s->raw = deposit64(s->raw, irq, 1, raise); + } + } + s->level = deposit64(s->level, irq, 1, level); + aspeed_vic_update(s); +} + +static uint64_t aspeed_vic_read(void *opaque, hwaddr offset, unsigned size) +{ + uint64_t val; + const bool high = !!(offset & 0x4); + hwaddr n_offset = (offset & ~0x4); + AspeedVICState *s = (AspeedVICState *)opaque; + + if (offset < AVIC_NEW_BASE_OFFSET) { + qemu_log_mask(LOG_UNIMP, "%s: Ignoring read from legacy registers " + "at 0x%" HWADDR_PRIx "[%u]\n", __func__, offset, size); + return 0; + } + + n_offset -= AVIC_NEW_BASE_OFFSET; + + switch (n_offset) { + case 0x0: /* IRQ Status */ + val = s->raw & ~s->select & s->enable; + break; + case 0x08: /* FIQ Status */ + val = s->raw & s->select & s->enable; + break; + case 0x10: /* Raw Interrupt Status */ + val = s->raw; + break; + case 0x18: /* Interrupt Selection */ + val = s->select; + break; + case 0x20: /* Interrupt Enable */ + val = s->enable; + break; + case 0x30: /* Software Interrupt */ + val = s->trigger; + break; + case 0x40: /* Interrupt Sensitivity */ + val = s->sense; + break; + case 0x48: /* Interrupt Both Edge Trigger Control */ + val = s->dual_edge; + break; + case 0x50: /* Interrupt Event */ + val = s->event; + break; + case 0x60: /* Edge Triggered Interrupt Status */ + val = s->raw & ~s->sense; + break; + /* Illegal */ + case 0x28: /* Interrupt Enable Clear */ + case 0x38: /* Software Interrupt Clear */ + case 0x58: /* Edge Triggered Interrupt Clear */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Read of write-only register with offset 0x%" + HWADDR_PRIx "\n", __func__, offset); + val = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad register at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + val = 0; + break; + } + if (high) { + val = extract64(val, 32, 19); + } + trace_aspeed_vic_read(offset, size, val); + return val; +} + +static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + const bool high = !!(offset & 0x4); + hwaddr n_offset = (offset & ~0x4); + AspeedVICState *s = (AspeedVICState *)opaque; + + if (offset < AVIC_NEW_BASE_OFFSET) { + qemu_log_mask(LOG_UNIMP, + "%s: Ignoring write to legacy registers at 0x%" + HWADDR_PRIx "[%u] <- 0x%" PRIx64 "\n", __func__, offset, + size, data); + return; + } + + n_offset -= AVIC_NEW_BASE_OFFSET; + trace_aspeed_vic_write(offset, size, data); + + /* Given we have members using separate enable/clear registers, deposit64() + * isn't quite the tool for the job. Instead, relocate the incoming bits to + * the required bit offset based on the provided access address + */ + if (high) { + data &= AVIC_H_MASK; + data <<= 32; + } else { + data &= AVIC_L_MASK; + } + + switch (n_offset) { + case 0x18: /* Interrupt Selection */ + /* Register has deposit64() semantics - overwrite requested 32 bits */ + if (high) { + s->select &= AVIC_L_MASK; + } else { + s->select &= ((uint64_t) AVIC_H_MASK) << 32; + } + s->select |= data; + break; + case 0x20: /* Interrupt Enable */ + s->enable |= data; + break; + case 0x28: /* Interrupt Enable Clear */ + s->enable &= ~data; + break; + case 0x30: /* Software Interrupt */ + qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. " + "IRQs requested: 0x%016" PRIx64 "\n", __func__, data); + break; + case 0x38: /* Software Interrupt Clear */ + qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. " + "IRQs to be cleared: 0x%016" PRIx64 "\n", __func__, data); + break; + case 0x50: /* Interrupt Event */ + /* Register has deposit64() semantics - overwrite the top four valid + * IRQ bits, as only the top four IRQs (GPIOs) can change their event + * type */ + if (high) { + s->event &= ~AVIC_EVENT_W_MASK; + s->event |= (data & AVIC_EVENT_W_MASK); + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "Ignoring invalid write to interrupt event register"); + } + break; + case 0x58: /* Edge Triggered Interrupt Clear */ + s->raw &= ~(data & ~s->sense); + break; + case 0x00: /* IRQ Status */ + case 0x08: /* FIQ Status */ + case 0x10: /* Raw Interrupt Status */ + case 0x40: /* Interrupt Sensitivity */ + case 0x48: /* Interrupt Both Edge Trigger Control */ + case 0x60: /* Edge Triggered Interrupt Status */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Write of read-only register with offset 0x%" + HWADDR_PRIx "\n", __func__, offset); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad register at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + break; + } + aspeed_vic_update(s); +} + +static const MemoryRegionOps aspeed_vic_ops = { + .read = aspeed_vic_read, + .write = aspeed_vic_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +static void aspeed_vic_reset(DeviceState *dev) +{ + AspeedVICState *s = ASPEED_VIC(dev); + + s->level = 0; + s->raw = 0; + s->select = 0; + s->enable = 0; + s->trigger = 0; + s->sense = 0x1F07FFF8FFFFULL; + s->dual_edge = 0xF800070000ULL; + s->event = 0x5F07FFF8FFFFULL; +} + +#define AVIC_IO_REGION_SIZE 0x20000 + +static void aspeed_vic_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AspeedVICState *s = ASPEED_VIC(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_vic_ops, s, + TYPE_ASPEED_VIC, AVIC_IO_REGION_SIZE); + + sysbus_init_mmio(sbd, &s->iomem); + + qdev_init_gpio_in(dev, aspeed_vic_set_irq, ASPEED_VIC_NR_IRQS); + sysbus_init_irq(sbd, &s->irq); + sysbus_init_irq(sbd, &s->fiq); +} + +static const VMStateDescription vmstate_aspeed_vic = { + .name = "aspeed.new-vic", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(level, AspeedVICState), + VMSTATE_UINT64(raw, AspeedVICState), + VMSTATE_UINT64(select, AspeedVICState), + VMSTATE_UINT64(enable, AspeedVICState), + VMSTATE_UINT64(trigger, AspeedVICState), + VMSTATE_UINT64(sense, AspeedVICState), + VMSTATE_UINT64(dual_edge, AspeedVICState), + VMSTATE_UINT64(event, AspeedVICState), + VMSTATE_END_OF_LIST() + } +}; + +static void aspeed_vic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = aspeed_vic_realize; + dc->reset = aspeed_vic_reset; + dc->desc = "ASPEED Interrupt Controller (New)"; + dc->vmsd = &vmstate_aspeed_vic; +} + +static const TypeInfo aspeed_vic_info = { + .name = TYPE_ASPEED_VIC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedVICState), + .class_init = aspeed_vic_class_init, +}; + +static void aspeed_vic_register_types(void) +{ + type_register_static(&aspeed_vic_info); +} + +type_init(aspeed_vic_register_types); diff --git a/qemu/hw/intc/bcm2835_ic.c b/qemu/hw/intc/bcm2835_ic.c new file mode 100644 index 000000000..80513b28f --- /dev/null +++ b/qemu/hw/intc/bcm2835_ic.c @@ -0,0 +1,237 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann. + * This code is licensed under the GNU GPLv2 and later. + * Heavily based on pl190.c, copyright terms below: + * + * Arm PrimeCell PL190 Vector Interrupt Controller + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the GPL. + */ + +#include "qemu/osdep.h" +#include "hw/intc/bcm2835_ic.h" + +#define GPU_IRQS 64 +#define ARM_IRQS 8 + +#define IRQ_PENDING_BASIC 0x00 /* IRQ basic pending */ +#define IRQ_PENDING_1 0x04 /* IRQ pending 1 */ +#define IRQ_PENDING_2 0x08 /* IRQ pending 2 */ +#define FIQ_CONTROL 0x0C /* FIQ register */ +#define IRQ_ENABLE_1 0x10 /* Interrupt enable register 1 */ +#define IRQ_ENABLE_2 0x14 /* Interrupt enable register 2 */ +#define IRQ_ENABLE_BASIC 0x18 /* Base interrupt enable register */ +#define IRQ_DISABLE_1 0x1C /* Interrupt disable register 1 */ +#define IRQ_DISABLE_2 0x20 /* Interrupt disable register 2 */ +#define IRQ_DISABLE_BASIC 0x24 /* Base interrupt disable register */ + +/* Update interrupts. */ +static void bcm2835_ic_update(BCM2835ICState *s) +{ + bool set = false; + + if (s->fiq_enable) { + if (s->fiq_select >= GPU_IRQS) { + /* ARM IRQ */ + set = extract32(s->arm_irq_level, s->fiq_select - GPU_IRQS, 1); + } else { + set = extract64(s->gpu_irq_level, s->fiq_select, 1); + } + } + qemu_set_irq(s->fiq, set); + + set = (s->gpu_irq_level & s->gpu_irq_enable) + || (s->arm_irq_level & s->arm_irq_enable); + qemu_set_irq(s->irq, set); + +} + +static void bcm2835_ic_set_gpu_irq(void *opaque, int irq, int level) +{ + BCM2835ICState *s = opaque; + + assert(irq >= 0 && irq < 64); + s->gpu_irq_level = deposit64(s->gpu_irq_level, irq, 1, level != 0); + bcm2835_ic_update(s); +} + +static void bcm2835_ic_set_arm_irq(void *opaque, int irq, int level) +{ + BCM2835ICState *s = opaque; + + assert(irq >= 0 && irq < 8); + s->arm_irq_level = deposit32(s->arm_irq_level, irq, 1, level != 0); + bcm2835_ic_update(s); +} + +static const int irq_dups[] = { 7, 9, 10, 18, 19, 53, 54, 55, 56, 57, 62 }; + +static uint64_t bcm2835_ic_read(void *opaque, hwaddr offset, unsigned size) +{ + BCM2835ICState *s = opaque; + uint32_t res = 0; + uint64_t gpu_pending = s->gpu_irq_level & s->gpu_irq_enable; + int i; + + switch (offset) { + case IRQ_PENDING_BASIC: + /* bits 0-7: ARM irqs */ + res = s->arm_irq_level & s->arm_irq_enable; + + /* bits 8 & 9: pending registers 1 & 2 */ + res |= (((uint32_t)gpu_pending) != 0) << 8; + res |= ((gpu_pending >> 32) != 0) << 9; + + /* bits 10-20: selected GPU IRQs */ + for (i = 0; i < ARRAY_SIZE(irq_dups); i++) { + res |= extract64(gpu_pending, irq_dups[i], 1) << (i + 10); + } + break; + case IRQ_PENDING_1: + res = gpu_pending; + break; + case IRQ_PENDING_2: + res = gpu_pending >> 32; + break; + case FIQ_CONTROL: + res = (s->fiq_enable << 7) | s->fiq_select; + break; + case IRQ_ENABLE_1: + res = s->gpu_irq_enable; + break; + case IRQ_ENABLE_2: + res = s->gpu_irq_enable >> 32; + break; + case IRQ_ENABLE_BASIC: + res = s->arm_irq_enable; + break; + case IRQ_DISABLE_1: + res = ~s->gpu_irq_enable; + break; + case IRQ_DISABLE_2: + res = ~s->gpu_irq_enable >> 32; + break; + case IRQ_DISABLE_BASIC: + res = ~s->arm_irq_enable; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return 0; + } + + return res; +} + +static void bcm2835_ic_write(void *opaque, hwaddr offset, uint64_t val, + unsigned size) +{ + BCM2835ICState *s = opaque; + + switch (offset) { + case FIQ_CONTROL: + s->fiq_select = extract32(val, 0, 7); + s->fiq_enable = extract32(val, 7, 1); + break; + case IRQ_ENABLE_1: + s->gpu_irq_enable |= val; + break; + case IRQ_ENABLE_2: + s->gpu_irq_enable |= val << 32; + break; + case IRQ_ENABLE_BASIC: + s->arm_irq_enable |= val & 0xff; + break; + case IRQ_DISABLE_1: + s->gpu_irq_enable &= ~val; + break; + case IRQ_DISABLE_2: + s->gpu_irq_enable &= ~(val << 32); + break; + case IRQ_DISABLE_BASIC: + s->arm_irq_enable &= ~val & 0xff; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return; + } + bcm2835_ic_update(s); +} + +static const MemoryRegionOps bcm2835_ic_ops = { + .read = bcm2835_ic_read, + .write = bcm2835_ic_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static void bcm2835_ic_reset(DeviceState *d) +{ + BCM2835ICState *s = BCM2835_IC(d); + + s->gpu_irq_enable = 0; + s->arm_irq_enable = 0; + s->fiq_enable = false; + s->fiq_select = 0; +} + +static void bcm2835_ic_init(Object *obj) +{ + BCM2835ICState *s = BCM2835_IC(obj); + + memory_region_init_io(&s->iomem, obj, &bcm2835_ic_ops, s, TYPE_BCM2835_IC, + 0x200); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); + + qdev_init_gpio_in_named(DEVICE(s), bcm2835_ic_set_gpu_irq, + BCM2835_IC_GPU_IRQ, GPU_IRQS); + qdev_init_gpio_in_named(DEVICE(s), bcm2835_ic_set_arm_irq, + BCM2835_IC_ARM_IRQ, ARM_IRQS); + + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->fiq); +} + +static const VMStateDescription vmstate_bcm2835_ic = { + .name = TYPE_BCM2835_IC, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(gpu_irq_level, BCM2835ICState), + VMSTATE_UINT64(gpu_irq_enable, BCM2835ICState), + VMSTATE_UINT8(arm_irq_level, BCM2835ICState), + VMSTATE_UINT8(arm_irq_enable, BCM2835ICState), + VMSTATE_BOOL(fiq_enable, BCM2835ICState), + VMSTATE_UINT8(fiq_select, BCM2835ICState), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2835_ic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = bcm2835_ic_reset; + dc->vmsd = &vmstate_bcm2835_ic; +} + +static TypeInfo bcm2835_ic_info = { + .name = TYPE_BCM2835_IC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835ICState), + .class_init = bcm2835_ic_class_init, + .instance_init = bcm2835_ic_init, +}; + +static void bcm2835_ic_register_types(void) +{ + type_register_static(&bcm2835_ic_info); +} + +type_init(bcm2835_ic_register_types) diff --git a/qemu/hw/intc/bcm2836_control.c b/qemu/hw/intc/bcm2836_control.c new file mode 100644 index 000000000..d0271810c --- /dev/null +++ b/qemu/hw/intc/bcm2836_control.c @@ -0,0 +1,304 @@ +/* + * Rasperry Pi 2 emulation ARM control logic module. + * Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * Based on bcm2835_ic.c (Raspberry Pi emulation) (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + * + * At present, only implements interrupt routing, and mailboxes (i.e., + * not local timer, PMU interrupt, or AXI counters). + * + * Ref: + * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf + */ + +#include "qemu/osdep.h" +#include "hw/intc/bcm2836_control.h" + +#define REG_GPU_ROUTE 0x0c +#define REG_TIMERCONTROL 0x40 +#define REG_MBOXCONTROL 0x50 +#define REG_IRQSRC 0x60 +#define REG_FIQSRC 0x70 +#define REG_MBOX0_WR 0x80 +#define REG_MBOX0_RDCLR 0xc0 +#define REG_LIMIT 0x100 + +#define IRQ_BIT(cntrl, num) (((cntrl) & (1 << (num))) != 0) +#define FIQ_BIT(cntrl, num) (((cntrl) & (1 << ((num) + 4))) != 0) + +#define IRQ_CNTPSIRQ 0 +#define IRQ_CNTPNSIRQ 1 +#define IRQ_CNTHPIRQ 2 +#define IRQ_CNTVIRQ 3 +#define IRQ_MAILBOX0 4 +#define IRQ_MAILBOX1 5 +#define IRQ_MAILBOX2 6 +#define IRQ_MAILBOX3 7 +#define IRQ_GPU 8 +#define IRQ_PMU 9 +#define IRQ_AXI 10 +#define IRQ_TIMER 11 +#define IRQ_MAX IRQ_TIMER + +static void deliver_local(BCM2836ControlState *s, uint8_t core, uint8_t irq, + uint32_t controlreg, uint8_t controlidx) +{ + if (FIQ_BIT(controlreg, controlidx)) { + /* deliver a FIQ */ + s->fiqsrc[core] |= (uint32_t)1 << irq; + } else if (IRQ_BIT(controlreg, controlidx)) { + /* deliver an IRQ */ + s->irqsrc[core] |= (uint32_t)1 << irq; + } else { + /* the interrupt is masked */ + } +} + +/* Update interrupts. */ +static void bcm2836_control_update(BCM2836ControlState *s) +{ + int i, j; + + /* reset pending IRQs/FIQs */ + for (i = 0; i < BCM2836_NCORES; i++) { + s->irqsrc[i] = s->fiqsrc[i] = 0; + } + + /* apply routing logic, update status regs */ + if (s->gpu_irq) { + assert(s->route_gpu_irq < BCM2836_NCORES); + s->irqsrc[s->route_gpu_irq] |= (uint32_t)1 << IRQ_GPU; + } + + if (s->gpu_fiq) { + assert(s->route_gpu_fiq < BCM2836_NCORES); + s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU; + } + + for (i = 0; i < BCM2836_NCORES; i++) { + /* handle local timer interrupts for this core */ + if (s->timerirqs[i]) { + assert(s->timerirqs[i] < (1 << (IRQ_CNTVIRQ + 1))); /* sane mask? */ + for (j = 0; j <= IRQ_CNTVIRQ; j++) { + if ((s->timerirqs[i] & (1 << j)) != 0) { + /* local interrupt j is set */ + deliver_local(s, i, j, s->timercontrol[i], j); + } + } + } + + /* handle mailboxes for this core */ + for (j = 0; j < BCM2836_MBPERCORE; j++) { + if (s->mailboxes[i * BCM2836_MBPERCORE + j] != 0) { + /* mailbox j is set */ + deliver_local(s, i, j + IRQ_MAILBOX0, s->mailboxcontrol[i], j); + } + } + } + + /* call set_irq appropriately for each output */ + for (i = 0; i < BCM2836_NCORES; i++) { + qemu_set_irq(s->irq[i], s->irqsrc[i] != 0); + qemu_set_irq(s->fiq[i], s->fiqsrc[i] != 0); + } +} + +static void bcm2836_control_set_local_irq(void *opaque, int core, int local_irq, + int level) +{ + BCM2836ControlState *s = opaque; + + assert(core >= 0 && core < BCM2836_NCORES); + assert(local_irq >= 0 && local_irq <= IRQ_CNTVIRQ); + + s->timerirqs[core] = deposit32(s->timerirqs[core], local_irq, 1, !!level); + + bcm2836_control_update(s); +} + +/* XXX: the following wrapper functions are a kludgy workaround, + * needed because I can't seem to pass useful information in the "irq" + * parameter when using named interrupts. Feel free to clean this up! + */ + +static void bcm2836_control_set_local_irq0(void *opaque, int core, int level) +{ + bcm2836_control_set_local_irq(opaque, core, 0, level); +} + +static void bcm2836_control_set_local_irq1(void *opaque, int core, int level) +{ + bcm2836_control_set_local_irq(opaque, core, 1, level); +} + +static void bcm2836_control_set_local_irq2(void *opaque, int core, int level) +{ + bcm2836_control_set_local_irq(opaque, core, 2, level); +} + +static void bcm2836_control_set_local_irq3(void *opaque, int core, int level) +{ + bcm2836_control_set_local_irq(opaque, core, 3, level); +} + +static void bcm2836_control_set_gpu_irq(void *opaque, int irq, int level) +{ + BCM2836ControlState *s = opaque; + + s->gpu_irq = level; + + bcm2836_control_update(s); +} + +static void bcm2836_control_set_gpu_fiq(void *opaque, int irq, int level) +{ + BCM2836ControlState *s = opaque; + + s->gpu_fiq = level; + + bcm2836_control_update(s); +} + +static uint64_t bcm2836_control_read(void *opaque, hwaddr offset, unsigned size) +{ + BCM2836ControlState *s = opaque; + + if (offset == REG_GPU_ROUTE) { + assert(s->route_gpu_fiq < BCM2836_NCORES + && s->route_gpu_irq < BCM2836_NCORES); + return ((uint32_t)s->route_gpu_fiq << 2) | s->route_gpu_irq; + } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) { + return s->timercontrol[(offset - REG_TIMERCONTROL) >> 2]; + } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) { + return s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2]; + } else if (offset >= REG_IRQSRC && offset < REG_FIQSRC) { + return s->irqsrc[(offset - REG_IRQSRC) >> 2]; + } else if (offset >= REG_FIQSRC && offset < REG_MBOX0_WR) { + return s->fiqsrc[(offset - REG_FIQSRC) >> 2]; + } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) { + return s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2]; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return 0; + } +} + +static void bcm2836_control_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + BCM2836ControlState *s = opaque; + + if (offset == REG_GPU_ROUTE) { + s->route_gpu_irq = val & 0x3; + s->route_gpu_fiq = (val >> 2) & 0x3; + } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) { + s->timercontrol[(offset - REG_TIMERCONTROL) >> 2] = val & 0xff; + } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) { + s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2] = val & 0xff; + } else if (offset >= REG_MBOX0_WR && offset < REG_MBOX0_RDCLR) { + s->mailboxes[(offset - REG_MBOX0_WR) >> 2] |= val; + } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) { + s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2] &= ~val; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return; + } + + bcm2836_control_update(s); +} + +static const MemoryRegionOps bcm2836_control_ops = { + .read = bcm2836_control_read, + .write = bcm2836_control_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static void bcm2836_control_reset(DeviceState *d) +{ + BCM2836ControlState *s = BCM2836_CONTROL(d); + int i; + + s->route_gpu_irq = s->route_gpu_fiq = 0; + + for (i = 0; i < BCM2836_NCORES; i++) { + s->timercontrol[i] = 0; + s->mailboxcontrol[i] = 0; + } + + for (i = 0; i < BCM2836_NCORES * BCM2836_MBPERCORE; i++) { + s->mailboxes[i] = 0; + } +} + +static void bcm2836_control_init(Object *obj) +{ + BCM2836ControlState *s = BCM2836_CONTROL(obj); + DeviceState *dev = DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &bcm2836_control_ops, s, + TYPE_BCM2836_CONTROL, REG_LIMIT); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); + + /* inputs from each CPU core */ + qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq0, "cntpsirq", + BCM2836_NCORES); + qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq1, "cntpnsirq", + BCM2836_NCORES); + qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq2, "cnthpirq", + BCM2836_NCORES); + qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq3, "cntvirq", + BCM2836_NCORES); + + /* IRQ and FIQ inputs from upstream bcm2835 controller */ + qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_irq, "gpu-irq", 1); + qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_fiq, "gpu-fiq", 1); + + /* outputs to CPU cores */ + qdev_init_gpio_out_named(dev, s->irq, "irq", BCM2836_NCORES); + qdev_init_gpio_out_named(dev, s->fiq, "fiq", BCM2836_NCORES); +} + +static const VMStateDescription vmstate_bcm2836_control = { + .name = TYPE_BCM2836_CONTROL, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(mailboxes, BCM2836ControlState, + BCM2836_NCORES * BCM2836_MBPERCORE), + VMSTATE_UINT8(route_gpu_irq, BCM2836ControlState), + VMSTATE_UINT8(route_gpu_fiq, BCM2836ControlState), + VMSTATE_UINT32_ARRAY(timercontrol, BCM2836ControlState, BCM2836_NCORES), + VMSTATE_UINT32_ARRAY(mailboxcontrol, BCM2836ControlState, + BCM2836_NCORES), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2836_control_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = bcm2836_control_reset; + dc->vmsd = &vmstate_bcm2836_control; +} + +static TypeInfo bcm2836_control_info = { + .name = TYPE_BCM2836_CONTROL, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2836ControlState), + .class_init = bcm2836_control_class_init, + .instance_init = bcm2836_control_init, +}; + +static void bcm2836_control_register_types(void) +{ + type_register_static(&bcm2836_control_info); +} + +type_init(bcm2836_control_register_types) diff --git a/qemu/hw/intc/etraxfs_pic.c b/qemu/hw/intc/etraxfs_pic.c index bd588681e..48f947706 100644 --- a/qemu/hw/intc/etraxfs_pic.c +++ b/qemu/hw/intc/etraxfs_pic.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/hw.h" //#include "pc.h" diff --git a/qemu/hw/intc/exynos4210_combiner.c b/qemu/hw/intc/exynos4210_combiner.c index a6b702890..dc0c90326 100644 --- a/qemu/hw/intc/exynos4210_combiner.c +++ b/qemu/hw/intc/exynos4210_combiner.c @@ -27,6 +27,7 @@ * IRQs are passed to GIC through Combiner. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/arm/exynos4210.h" diff --git a/qemu/hw/intc/exynos4210_gic.c b/qemu/hw/intc/exynos4210_gic.c index b2a4950bc..4f7e89f7b 100644 --- a/qemu/hw/intc/exynos4210_gic.c +++ b/qemu/hw/intc/exynos4210_gic.c @@ -20,6 +20,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "qemu-common.h" #include "hw/irq.h" diff --git a/qemu/hw/intc/grlib_irqmp.c b/qemu/hw/intc/grlib_irqmp.c index d1813f76b..f5ca8f752 100644 --- a/qemu/hw/intc/grlib_irqmp.c +++ b/qemu/hw/intc/grlib_irqmp.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "cpu.h" diff --git a/qemu/hw/intc/heathrow_pic.c b/qemu/hw/intc/heathrow_pic.c index 9ff3119ed..171f5ed81 100644 --- a/qemu/hw/intc/heathrow_pic.c +++ b/qemu/hw/intc/heathrow_pic.c @@ -22,6 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/ppc/mac.h" diff --git a/qemu/hw/intc/i8259.c b/qemu/hw/intc/i8259.c index 0f5c0259a..bb43669b9 100644 --- a/qemu/hw/intc/i8259.c +++ b/qemu/hw/intc/i8259.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/isa/isa.h" @@ -229,7 +230,7 @@ int pic_read_irq(DeviceState *d) printf("IRQ%d latency=%0.3fus\n", irq, (double)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - - irq_time[irq]) * 1000000.0 / get_ticks_per_sec()); + irq_time[irq]) * 1000000.0 / NANOSECONDS_PER_SECOND); #endif DPRINTF("pic_interrupt: irq=%d\n", irq); return intno; diff --git a/qemu/hw/intc/i8259_common.c b/qemu/hw/intc/i8259_common.c index fbf26e557..3a850b0c6 100644 --- a/qemu/hw/intc/i8259_common.c +++ b/qemu/hw/intc/i8259_common.c @@ -22,6 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/i386/pc.h" #include "hw/isa/i8259_internal.h" diff --git a/qemu/hw/intc/imx_avic.c b/qemu/hw/intc/imx_avic.c index e48f66c8f..702765577 100644 --- a/qemu/hw/intc/imx_avic.c +++ b/qemu/hw/intc/imx_avic.c @@ -7,6 +7,7 @@ * Copyright (c) 2008 OKL * Copyright (c) 2011 NICTA Pty Ltd * Originally written by Hans Jiang + * Updated by Jean-Christophe Dubois * * This code is licensed under the GPL version 2 or later. See * the COPYING file in the top-level directory. @@ -14,67 +15,23 @@ * TODO: implement vectors. */ -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "qemu/host-utils.h" +#include "qemu/osdep.h" +#include "hw/intc/imx_avic.h" -#define DEBUG_INT 1 -#undef DEBUG_INT /* comment out for debugging */ - -#ifdef DEBUG_INT -#define DPRINTF(fmt, args...) \ -do { printf("imx_avic: " fmt , ##args); } while (0) -#else -#define DPRINTF(fmt, args...) do {} while (0) +#ifndef DEBUG_IMX_AVIC +#define DEBUG_IMX_AVIC 0 #endif -/* - * Define to 1 for messages about attempts to - * access unimplemented registers or similar. - */ -#define DEBUG_IMPLEMENTATION 1 -#if DEBUG_IMPLEMENTATION -# define IPRINTF(fmt, args...) \ - do { fprintf(stderr, "imx_avic: " fmt, ##args); } while (0) -#else -# define IPRINTF(fmt, args...) do {} while (0) -#endif - -#define IMX_AVIC_NUM_IRQS 64 - -/* Interrupt Control Bits */ -#define ABFLAG (1<<25) -#define ABFEN (1<<24) -#define NIDIS (1<<22) /* Normal Interrupt disable */ -#define FIDIS (1<<21) /* Fast interrupt disable */ -#define NIAD (1<<20) /* Normal Interrupt Arbiter Rise ARM level */ -#define FIAD (1<<19) /* Fast Interrupt Arbiter Rise ARM level */ -#define NM (1<<18) /* Normal interrupt mode */ - - -#define PRIO_PER_WORD (sizeof(uint32_t) * 8 / 4) -#define PRIO_WORDS (IMX_AVIC_NUM_IRQS/PRIO_PER_WORD) - -#define TYPE_IMX_AVIC "imx_avic" -#define IMX_AVIC(obj) \ - OBJECT_CHECK(IMXAVICState, (obj), TYPE_IMX_AVIC) - -typedef struct IMXAVICState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint64_t pending; - uint64_t enabled; - uint64_t is_fiq; - uint32_t intcntl; - uint32_t intmask; - qemu_irq irq; - qemu_irq fiq; - uint32_t prio[PRIO_WORDS]; /* Priorities are 4-bits each */ -} IMXAVICState; +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_AVIC) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_AVIC, \ + __func__, ##args); \ + } \ + } while (0) static const VMStateDescription vmstate_imx_avic = { - .name = "imx-avic", + .name = TYPE_IMX_AVIC, .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { @@ -88,8 +45,6 @@ static const VMStateDescription vmstate_imx_avic = { }, }; - - static inline int imx_avic_prio(IMXAVICState *s, int irq) { uint32_t word = irq / PRIO_PER_WORD; @@ -151,8 +106,8 @@ static uint64_t imx_avic_read(void *opaque, { IMXAVICState *s = (IMXAVICState *)opaque; + DPRINTF("read(offset = 0x%" HWADDR_PRIx ")\n", offset); - DPRINTF("read(offset = 0x%x)\n", offset >> 2); switch (offset >> 2) { case 0: /* INTCNTL */ return s->intcntl; @@ -249,7 +204,8 @@ static uint64_t imx_avic_read(void *opaque, return 0x4; default: - IPRINTF("imx_avic_read: Bad offset 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_AVIC, __func__, offset); return 0; } } @@ -261,13 +217,13 @@ static void imx_avic_write(void *opaque, hwaddr offset, /* Vector Registers not yet supported */ if (offset >= 0x100 && offset <= 0x2fc) { - IPRINTF("imx_avic_write to vector register %d ignored\n", - (unsigned int)((offset - 0x100) >> 2)); + qemu_log_mask(LOG_UNIMP, "[%s]%s: vector %d ignored\n", + TYPE_IMX_AVIC, __func__, (int)((offset - 0x100) >> 2)); return; } - DPRINTF("imx_avic_write(0x%x) = %x\n", - (unsigned int)offset>>2, (unsigned int)val); + DPRINTF("(0x%" HWADDR_PRIx ") = 0x%x\n", offset, (unsigned int)val); + switch (offset >> 2) { case 0: /* Interrupt Control Register, INTCNTL */ s->intcntl = val & (ABFEN | NIDIS | FIDIS | NIAD | FIAD | NM); @@ -341,7 +297,8 @@ static void imx_avic_write(void *opaque, hwaddr offset, return; default: - IPRINTF("imx_avic_write: Bad offset %x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_AVIC, __func__, offset); } imx_avic_update(s); } @@ -370,7 +327,7 @@ static int imx_avic_init(SysBusDevice *sbd) IMXAVICState *s = IMX_AVIC(dev); memory_region_init_io(&s->iomem, OBJECT(s), &imx_avic_ops, s, - "imx_avic", 0x1000); + TYPE_IMX_AVIC, 0x1000); sysbus_init_mmio(sbd, &s->iomem); qdev_init_gpio_in(dev, imx_avic_set_irq, IMX_AVIC_NUM_IRQS); diff --git a/qemu/hw/intc/ioapic.c b/qemu/hw/intc/ioapic.c index b52793238..378e663f6 100644 --- a/qemu/hw/intc/ioapic.c +++ b/qemu/hw/intc/ioapic.c @@ -20,10 +20,14 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" +#include "monitor/monitor.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/i386/ioapic.h" #include "hw/i386/ioapic_internal.h" +#include "include/hw/pci/msi.h" +#include "sysemu/kvm.h" //#define DEBUG_IOAPIC @@ -34,6 +38,10 @@ #define DPRINTF(fmt, ...) #endif +#define APIC_DELIVERY_MODE_SHIFT 8 +#define APIC_POLARITY_SHIFT 14 +#define APIC_TRIG_MODE_SHIFT 15 + static IOAPICCommonState *ioapics[MAX_IOAPICS]; /* global variable from ioapic_common.c */ @@ -53,6 +61,8 @@ static void ioapic_service(IOAPICCommonState *s) for (i = 0; i < IOAPIC_NUM_PINS; i++) { mask = 1 << i; if (s->irr & mask) { + int coalesce = 0; + entry = s->ioredtbl[i]; if (!(entry & IOAPIC_LVT_MASKED)) { trig_mode = ((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1); @@ -63,6 +73,7 @@ static void ioapic_service(IOAPICCommonState *s) if (trig_mode == IOAPIC_TRIGGER_EDGE) { s->irr &= ~mask; } else { + coalesce = s->ioredtbl[i] & IOAPIC_LVT_REMOTE_IRR; s->ioredtbl[i] |= IOAPIC_LVT_REMOTE_IRR; } if (delivery_mode == IOAPIC_DM_EXTINT) { @@ -70,8 +81,23 @@ static void ioapic_service(IOAPICCommonState *s) } else { vector = entry & IOAPIC_VECTOR_MASK; } - apic_deliver_irq(dest, dest_mode, delivery_mode, - vector, trig_mode); +#ifdef CONFIG_KVM + if (kvm_irqchip_is_split()) { + if (trig_mode == IOAPIC_TRIGGER_EDGE) { + kvm_set_irq(kvm_state, i, 1); + kvm_set_irq(kvm_state, i, 0); + } else { + if (!coalesce) { + kvm_set_irq(kvm_state, i, 1); + } + } + continue; + } +#else + (void)coalesce; +#endif + apic_deliver_irq(dest, dest_mode, delivery_mode, vector, + trig_mode); } } } @@ -98,7 +124,9 @@ static void ioapic_set_irq(void *opaque, int vector, int level) /* level triggered */ if (level) { s->irr |= mask; - ioapic_service(s); + if (!(entry & IOAPIC_LVT_REMOTE_IRR)) { + ioapic_service(s); + } } else { s->irr &= ~mask; } @@ -113,6 +141,44 @@ static void ioapic_set_irq(void *opaque, int vector, int level) } } +static void ioapic_update_kvm_routes(IOAPICCommonState *s) +{ +#ifdef CONFIG_KVM + int i; + + if (kvm_irqchip_is_split()) { + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + uint64_t entry = s->ioredtbl[i]; + uint8_t trig_mode; + uint8_t delivery_mode; + uint8_t dest; + uint8_t dest_mode; + uint64_t pin_polarity; + MSIMessage msg; + + trig_mode = ((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1); + dest = entry >> IOAPIC_LVT_DEST_SHIFT; + dest_mode = (entry >> IOAPIC_LVT_DEST_MODE_SHIFT) & 1; + pin_polarity = (entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1; + delivery_mode = + (entry >> IOAPIC_LVT_DELIV_MODE_SHIFT) & IOAPIC_DM_MASK; + + msg.address = APIC_DEFAULT_ADDRESS; + msg.address |= dest_mode << 2; + msg.address |= dest << 12; + + msg.data = entry & IOAPIC_VECTOR_MASK; + msg.data |= delivery_mode << APIC_DELIVERY_MODE_SHIFT; + msg.data |= pin_polarity << APIC_POLARITY_SHIFT; + msg.data |= trig_mode << APIC_TRIG_MODE_SHIFT; + + kvm_irqchip_update_msi_route(kvm_state, i, msg, NULL); + } + kvm_irqchip_commit_routes(kvm_state); + } +#endif +} + void ioapic_eoi_broadcast(int vector) { IOAPICCommonState *s; @@ -137,6 +203,17 @@ void ioapic_eoi_broadcast(int vector) } } +void ioapic_dump_state(Monitor *mon, const QDict *qdict) +{ + int i; + + for (i = 0; i < MAX_IOAPICS; i++) { + if (ioapics[i] != 0) { + ioapic_print_redtbl(mon, ioapics[i]); + } + } +} + static uint64_t ioapic_mem_read(void *opaque, hwaddr addr, unsigned int size) { @@ -154,15 +231,13 @@ ioapic_mem_read(void *opaque, hwaddr addr, unsigned int size) } switch (s->ioregsel) { case IOAPIC_REG_ID: + case IOAPIC_REG_ARB: val = s->id << IOAPIC_ID_SHIFT; break; case IOAPIC_REG_VER: val = IOAPIC_VERSION | ((IOAPIC_NUM_PINS - 1) << IOAPIC_VER_ENTRIES_SHIFT); break; - case IOAPIC_REG_ARB: - val = 0; - break; default: index = (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1; if (index >= 0 && index < IOAPIC_NUM_PINS) { @@ -217,6 +292,8 @@ ioapic_mem_write(void *opaque, hwaddr addr, uint64_t val, } break; } + + ioapic_update_kvm_routes(s); } static const MemoryRegionOps ioapic_io_ops = { diff --git a/qemu/hw/intc/ioapic_common.c b/qemu/hw/intc/ioapic_common.c index 8b7d11806..1b7ec5ec2 100644 --- a/qemu/hw/intc/ioapic_common.c +++ b/qemu/hw/intc/ioapic_common.c @@ -19,6 +19,9 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "monitor/monitor.h" #include "hw/i386/ioapic.h" #include "hw/i386/ioapic_internal.h" #include "hw/sysbus.h" @@ -31,6 +34,60 @@ */ int ioapic_no; +static void ioapic_irr_dump(Monitor *mon, const char *name, uint32_t bitmap) +{ + int i; + + monitor_printf(mon, "%-10s ", name); + if (bitmap == 0) { + monitor_printf(mon, "(none)\n"); + return; + } + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + if (bitmap & (1 << i)) { + monitor_printf(mon, "%-2u ", i); + } + } + monitor_printf(mon, "\n"); +} + +void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s) +{ + static const char *delm_str[] = { + "fixed", "lowest", "SMI", "...", "NMI", "INIT", "...", "extINT"}; + uint32_t remote_irr = 0; + int i; + + monitor_printf(mon, "ioapic id=0x%02x sel=0x%02x", s->id, s->ioregsel); + if (s->ioregsel) { + monitor_printf(mon, " (redir[%u])\n", + (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1); + } else { + monitor_printf(mon, "\n"); + } + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + uint64_t entry = s->ioredtbl[i]; + uint32_t delm = (uint32_t)((entry & IOAPIC_LVT_DELIV_MODE) >> + IOAPIC_LVT_DELIV_MODE_SHIFT); + monitor_printf(mon, "pin %-2u 0x%016"PRIx64" dest=%"PRIx64 + " vec=%-3"PRIu64" %s %-5s %-6s %-6s %s\n", + i, entry, + (entry >> IOAPIC_LVT_DEST_SHIFT) & + (entry & IOAPIC_LVT_DEST_MODE ? 0xff : 0xf), + entry & IOAPIC_VECTOR_MASK, + entry & IOAPIC_LVT_POLARITY ? "active-lo" : "active-hi", + entry & IOAPIC_LVT_TRIGGER_MODE ? "level" : "edge", + entry & IOAPIC_LVT_MASKED ? "masked" : "", + delm_str[delm], + entry & IOAPIC_LVT_DEST_MODE ? "logical" : "physical"); + + remote_irr |= entry & IOAPIC_LVT_TRIGGER_MODE ? + (entry & IOAPIC_LVT_REMOTE_IRR ? (1 << i) : 0) : 0; + } + ioapic_irr_dump(mon, "IRR", s->irr); + ioapic_irr_dump(mon, "Remote IRR", remote_irr); +} + void ioapic_reset_common(DeviceState *dev) { IOAPICCommonState *s = IOAPIC_COMMON(dev); diff --git a/qemu/hw/intc/lm32_pic.c b/qemu/hw/intc/lm32_pic.c index 641ee4727..edc08f184 100644 --- a/qemu/hw/intc/lm32_pic.c +++ b/qemu/hw/intc/lm32_pic.c @@ -17,7 +17,7 @@ * License along with this library; if not, see . */ -#include +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i386/pc.h" diff --git a/qemu/hw/intc/omap_intc.c b/qemu/hw/intc/omap_intc.c index e9b38a3c6..336882510 100644 --- a/qemu/hw/intc/omap_intc.c +++ b/qemu/hw/intc/omap_intc.c @@ -17,9 +17,11 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/arm/omap.h" #include "hw/sysbus.h" +#include "qemu/error-report.h" /* Interrupt Handlers */ struct omap_intr_handler_bank_s { @@ -367,7 +369,8 @@ static int omap_intc_init(SysBusDevice *sbd) struct omap_intr_handler_s *s = OMAP_INTC(dev); if (!s->iclk) { - hw_error("omap-intc: clk not connected\n"); + error_report("omap-intc: clk not connected"); + return -1; } s->nbanks = 1; sysbus_init_irq(sbd, &s->parent_intr[0]); @@ -608,10 +611,12 @@ static int omap2_intc_init(SysBusDevice *sbd) struct omap_intr_handler_s *s = OMAP_INTC(dev); if (!s->iclk) { - hw_error("omap2-intc: iclk not connected\n"); + error_report("omap2-intc: iclk not connected"); + return -1; } if (!s->fclk) { - hw_error("omap2-intc: fclk not connected\n"); + error_report("omap2-intc: fclk not connected"); + return -1; } s->level_only = 1; s->nbanks = 3; diff --git a/qemu/hw/intc/openpic.c b/qemu/hw/intc/openpic.c index 14ab0e31b..2d3769310 100644 --- a/qemu/hw/intc/openpic.c +++ b/qemu/hw/intc/openpic.c @@ -33,6 +33,7 @@ * Serial interrupts, as implemented in Raven chipset are not supported yet. * */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/ppc/mac.h" #include "hw/pci/pci.h" @@ -40,6 +41,7 @@ #include "hw/ppc/ppc_e500.h" #include "hw/sysbus.h" #include "hw/pci/msi.h" +#include "qapi/error.h" #include "qemu/bitops.h" #include "qapi/qmp/qerror.h" @@ -1374,7 +1376,7 @@ static void fsl_common_init(OpenPICState *opp) opp->irq_msi = 224; - msi_supported = true; + msi_nonbroken = true; for (i = 0; i < opp->fsl->max_ext; i++) { opp->src[i].level = false; } @@ -1643,6 +1645,7 @@ static void openpic_class_init(ObjectClass *oc, void *data) dc->props = openpic_properties; dc->reset = openpic_reset; dc->vmsd = &vmstate_openpic; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); } static const TypeInfo openpic_info = { diff --git a/qemu/hw/intc/openpic_kvm.c b/qemu/hw/intc/openpic_kvm.c index f7cac585a..e47e94f2c 100644 --- a/qemu/hw/intc/openpic_kvm.c +++ b/qemu/hw/intc/openpic_kvm.c @@ -22,6 +22,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include #include "exec/address-spaces.h" #include "hw/hw.h" @@ -238,7 +240,7 @@ static void kvm_openpic_realize(DeviceState *dev, Error **errp) memory_listener_register(&opp->mem_listener, &address_space_memory); /* indicate pic capabilities */ - msi_supported = true; + msi_nonbroken = true; kvm_kernel_irqchip = true; kvm_async_interrupts_allowed = true; @@ -275,6 +277,7 @@ static void kvm_openpic_class_init(ObjectClass *oc, void *data) dc->realize = kvm_openpic_realize; dc->props = kvm_openpic_properties; dc->reset = kvm_openpic_reset; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); } static const TypeInfo kvm_openpic_info = { diff --git a/qemu/hw/intc/pl190.c b/qemu/hw/intc/pl190.c index 2bf359a76..5ecbc4a48 100644 --- a/qemu/hw/intc/pl190.c +++ b/qemu/hw/intc/pl190.c @@ -7,6 +7,7 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" /* The number of virtual priority levels. 16 user vectors plus the diff --git a/qemu/hw/intc/puv3_intc.c b/qemu/hw/intc/puv3_intc.c index c2803d07d..ef8488aac 100644 --- a/qemu/hw/intc/puv3_intc.c +++ b/qemu/hw/intc/puv3_intc.c @@ -8,6 +8,7 @@ * published by the Free Software Foundation, or any later version. * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #undef DEBUG_PUV3 diff --git a/qemu/hw/intc/realview_gic.c b/qemu/hw/intc/realview_gic.c index 6c812961c..50bbab66e 100644 --- a/qemu/hw/intc/realview_gic.c +++ b/qemu/hw/intc/realview_gic.c @@ -7,6 +7,8 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/intc/realview_gic.h" static void realview_gic_set_irq(void *opaque, int irq, int level) diff --git a/qemu/hw/intc/s390_flic.c b/qemu/hw/intc/s390_flic.c index 02e10b750..bc75fa7d9 100644 --- a/qemu/hw/intc/s390_flic.c +++ b/qemu/hw/intc/s390_flic.c @@ -10,6 +10,7 @@ * directory. */ +#include "qemu/osdep.h" #include "qemu/error-report.h" #include "hw/sysbus.h" #include "migration/qemu-file.h" diff --git a/qemu/hw/intc/s390_flic_kvm.c b/qemu/hw/intc/s390_flic_kvm.c index b471e7a41..02449b390 100644 --- a/qemu/hw/intc/s390_flic_kvm.c +++ b/qemu/hw/intc/s390_flic_kvm.c @@ -10,6 +10,7 @@ * directory. */ +#include "qemu/osdep.h" #include #include "qemu/error-report.h" #include "hw/sysbus.h" @@ -228,6 +229,8 @@ static int kvm_s390_add_adapter_routes(S390FLICState *fs, routes->gsi[i] = ret; routes->adapter.ind_offset++; } + kvm_irqchip_commit_routes(kvm_state); + /* Restore passed-in structure to original state. */ routes->adapter.ind_offset = ind_offset; return 0; diff --git a/qemu/hw/intc/sh_intc.c b/qemu/hw/intc/sh_intc.c index 55c76e4af..6ce2a8084 100644 --- a/qemu/hw/intc/sh_intc.c +++ b/qemu/hw/intc/sh_intc.c @@ -8,6 +8,9 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sh4/sh_intc.h" #include "hw/hw.h" #include "hw/sh4/sh.h" diff --git a/qemu/hw/intc/slavio_intctl.c b/qemu/hw/intc/slavio_intctl.c index f22aba031..c9486ed99 100644 --- a/qemu/hw/intc/slavio_intctl.c +++ b/qemu/hw/intc/slavio_intctl.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sparc/sun4m.h" #include "monitor/monitor.h" #include "hw/sysbus.h" diff --git a/qemu/hw/intc/vgic_common.h b/qemu/hw/intc/vgic_common.h new file mode 100644 index 000000000..80d919eb9 --- /dev/null +++ b/qemu/hw/intc/vgic_common.h @@ -0,0 +1,35 @@ +/* + * ARM KVM vGIC utility functions + * + * Copyright (c) 2015 Samsung Electronics + * Written by Pavel Fedin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef QEMU_ARM_VGIC_COMMON_H +#define QEMU_ARM_VGIC_COMMON_H + +/** + * kvm_arm_gic_set_irq - Send an IRQ to the in-kernel vGIC + * @num_irq: Total number of IRQs configured for the GIC instance + * @irq: qemu internal IRQ line number: + * [0..N-1] : external interrupts + * [N..N+31] : PPI (internal) interrupts for CPU 0 + * [N+32..N+63] : PPI (internal interrupts for CPU 1 + * @level: level of the IRQ line. + */ +void kvm_arm_gic_set_irq(uint32_t num_irq, int irq, int level); + +#endif diff --git a/qemu/hw/intc/xics.c b/qemu/hw/intc/xics.c index 924b1ae3c..8659be017 100644 --- a/qemu/hw/intc/xics.c +++ b/qemu/hw/intc/xics.c @@ -25,6 +25,10 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "trace.h" #include "qemu/timer.h" @@ -88,24 +92,24 @@ static void xics_common_reset(DeviceState *d) device_reset(DEVICE(icp->ics)); } -static void xics_prop_get_nr_irqs(Object *obj, Visitor *v, - void *opaque, const char *name, Error **errp) +static void xics_prop_get_nr_irqs(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { XICSState *icp = XICS_COMMON(obj); int64_t value = icp->nr_irqs; - visit_type_int(v, &value, name, errp); + visit_type_int(v, name, &value, errp); } -static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, - void *opaque, const char *name, Error **errp) +static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { XICSState *icp = XICS_COMMON(obj); XICSStateClass *info = XICS_COMMON_GET_CLASS(icp); Error *error = NULL; int64_t value; - visit_type_int(v, &value, name, &error); + visit_type_int(v, name, &value, &error); if (error) { error_propagate(errp, error); return; @@ -122,17 +126,17 @@ static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, } static void xics_prop_get_nr_servers(Object *obj, Visitor *v, - void *opaque, const char *name, + const char *name, void *opaque, Error **errp) { XICSState *icp = XICS_COMMON(obj); int64_t value = icp->nr_servers; - visit_type_int(v, &value, name, errp); + visit_type_int(v, name, &value, errp); } static void xics_prop_set_nr_servers(Object *obj, Visitor *v, - void *opaque, const char *name, + const char *name, void *opaque, Error **errp) { XICSState *icp = XICS_COMMON(obj); @@ -140,7 +144,7 @@ static void xics_prop_set_nr_servers(Object *obj, Visitor *v, Error *error = NULL; int64_t value; - visit_type_int(v, &value, name, &error); + visit_type_int(v, name, &value, &error); if (error) { error_propagate(errp, error); return; @@ -711,7 +715,7 @@ static int ics_find_free_block(ICSState *ics, int num, int alignnum) return -1; } -int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi) +int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi, Error **errp) { ICSState *ics = &icp->ics[src]; int irq; @@ -719,14 +723,14 @@ int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi) if (irq_hint) { assert(src == xics_find_source(icp, irq_hint)); if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) { - trace_xics_alloc_failed_hint(src, irq_hint); + error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint); return -1; } irq = irq_hint; } else { irq = ics_find_free_block(ics, 1, 1); if (irq < 0) { - trace_xics_alloc_failed_no_left(src); + error_setg(errp, "can't allocate IRQ: no IRQ left"); return -1; } irq += ics->offset; @@ -739,10 +743,11 @@ int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi) } /* - * Allocate block of consequtive IRQs, returns a number of the first. + * Allocate block of consecutive IRQs, and return the number of the first IRQ in the block. * If align==true, aligns the first IRQ number to num. */ -int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align) +int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align, + Error **errp) { int i, first = -1; ICSState *ics = &icp->ics[src]; @@ -762,6 +767,10 @@ int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align) } else { first = ics_find_free_block(ics, num, 1); } + if (first < 0) { + error_setg(errp, "can't find a free %d-IRQ block", num); + return -1; + } if (first >= 0) { for (i = first; i < first + num; ++i) { @@ -848,7 +857,7 @@ static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t xirr = icp_accept(ss); args[0] = xirr; - args[1] = cpu_get_real_ticks(); + args[1] = cpu_get_host_ticks(); return H_SUCCESS; } diff --git a/qemu/hw/intc/xics_kvm.c b/qemu/hw/intc/xics_kvm.c index d58729cfa..9029d9ee0 100644 --- a/qemu/hw/intc/xics_kvm.c +++ b/qemu/hw/intc/xics_kvm.c @@ -25,6 +25,10 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "trace.h" #include "hw/ppc/spapr.h" diff --git a/qemu/hw/intc/xilinx_intc.c b/qemu/hw/intc/xilinx_intc.c index 12804ab72..9d8139bc6 100644 --- a/qemu/hw/intc/xilinx_intc.c +++ b/qemu/hw/intc/xilinx_intc.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/hw.h" diff --git a/qemu/hw/ipack/ipack.c b/qemu/hw/ipack/ipack.c index 59bfe2869..5f99ed9a7 100644 --- a/qemu/hw/ipack/ipack.c +++ b/qemu/hw/ipack/ipack.c @@ -8,6 +8,8 @@ * later version. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/ipack/ipack.h" IPackDevice *ipack_device_find(IPackBus *bus, int32_t slot) diff --git a/qemu/hw/ipack/tpci200.c b/qemu/hw/ipack/tpci200.c index 1df02ee85..fdda6f414 100644 --- a/qemu/hw/ipack/tpci200.c +++ b/qemu/hw/ipack/tpci200.c @@ -8,10 +8,10 @@ * later version. */ +#include "qemu/osdep.h" #include "hw/ipack/ipack.h" #include "hw/pci/pci.h" #include "qemu/bitops.h" -#include /* #define DEBUG_TPCI */ diff --git a/qemu/hw/ipmi/Makefile.objs b/qemu/hw/ipmi/Makefile.objs new file mode 100644 index 000000000..a90318d5b --- /dev/null +++ b/qemu/hw/ipmi/Makefile.objs @@ -0,0 +1,5 @@ +common-obj-$(CONFIG_IPMI) += ipmi.o +common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_sim.o +common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_extern.o +common-obj-$(CONFIG_ISA_IPMI_KCS) += isa_ipmi_kcs.o +common-obj-$(CONFIG_ISA_IPMI_BT) += isa_ipmi_bt.o diff --git a/qemu/hw/ipmi/ipmi.c b/qemu/hw/ipmi/ipmi.c new file mode 100644 index 000000000..6adec1e99 --- /dev/null +++ b/qemu/hw/ipmi/ipmi.c @@ -0,0 +1,151 @@ +/* + * QEMU IPMI emulation + * + * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/ipmi/ipmi.h" +#include "sysemu/sysemu.h" +#include "qmp-commands.h" +#include "qom/object_interfaces.h" +#include "qapi/visitor.h" + +static int ipmi_do_hw_op(IPMIInterface *s, enum ipmi_op op, int checkonly) +{ + switch (op) { + case IPMI_RESET_CHASSIS: + if (checkonly) { + return 0; + } + qemu_system_reset_request(); + return 0; + + case IPMI_POWEROFF_CHASSIS: + if (checkonly) { + return 0; + } + qemu_system_powerdown_request(); + return 0; + + case IPMI_SEND_NMI: + if (checkonly) { + return 0; + } + qmp_inject_nmi(NULL); + return 0; + + case IPMI_POWERCYCLE_CHASSIS: + case IPMI_PULSE_DIAG_IRQ: + case IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP: + case IPMI_POWERON_CHASSIS: + default: + return IPMI_CC_COMMAND_NOT_SUPPORTED; + } +} + +static void ipmi_interface_class_init(ObjectClass *class, void *data) +{ + IPMIInterfaceClass *ik = IPMI_INTERFACE_CLASS(class); + + ik->do_hw_op = ipmi_do_hw_op; +} + +static TypeInfo ipmi_interface_type_info = { + .name = TYPE_IPMI_INTERFACE, + .parent = TYPE_INTERFACE, + .class_size = sizeof(IPMIInterfaceClass), + .class_init = ipmi_interface_class_init, +}; + +static void isa_ipmi_bmc_check(Object *obj, const char *name, + Object *val, Error **errp) +{ + IPMIBmc *bmc = IPMI_BMC(val); + + if (bmc->intf) + error_setg(errp, "BMC object is already in use"); +} + +void ipmi_bmc_find_and_link(Object *obj, Object **bmc) +{ + object_property_add_link(obj, "bmc", TYPE_IPMI_BMC, bmc, + isa_ipmi_bmc_check, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + &error_abort); +} + +static Property ipmi_bmc_properties[] = { + DEFINE_PROP_UINT8("slave_addr", IPMIBmc, slave_addr, 0x20), + DEFINE_PROP_END_OF_LIST(), +}; + +static void bmc_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->props = ipmi_bmc_properties; +} + +static TypeInfo ipmi_bmc_type_info = { + .name = TYPE_IPMI_BMC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(IPMIBmc), + .abstract = true, + .class_size = sizeof(IPMIBmcClass), + .class_init = bmc_class_init, +}; + +static void ipmi_register_types(void) +{ + type_register_static(&ipmi_interface_type_info); + type_register_static(&ipmi_bmc_type_info); +} + +type_init(ipmi_register_types) + +static IPMIFwInfo *ipmi_fw_info; +static unsigned int ipmi_fw_info_len; + +static uint32_t current_uuid = 1; + +void ipmi_add_fwinfo(IPMIFwInfo *info, Error **errp) +{ + info->uuid = current_uuid++; + ipmi_fw_info = g_realloc(ipmi_fw_info, + sizeof(*ipmi_fw_info) * (ipmi_fw_info_len + 1)); + ipmi_fw_info[ipmi_fw_info_len] = *info; +} + +IPMIFwInfo *ipmi_first_fwinfo(void) +{ + return ipmi_fw_info; +} + +IPMIFwInfo *ipmi_next_fwinfo(IPMIFwInfo *current) +{ + current++; + if (current >= &ipmi_fw_info[ipmi_fw_info_len]) { + return NULL; + } + return current; +} diff --git a/qemu/hw/ipmi/ipmi_bmc_extern.c b/qemu/hw/ipmi/ipmi_bmc_extern.c new file mode 100644 index 000000000..fe12112a2 --- /dev/null +++ b/qemu/hw/ipmi/ipmi_bmc_extern.c @@ -0,0 +1,519 @@ +/* + * IPMI BMC external connection + * + * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * This is designed to connect with OpenIPMI's lanserv serial interface + * using the "VM" connection type. See that for details. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/timer.h" +#include "sysemu/char.h" +#include "sysemu/sysemu.h" +#include "hw/ipmi/ipmi.h" + +#define VM_MSG_CHAR 0xA0 /* Marks end of message */ +#define VM_CMD_CHAR 0xA1 /* Marks end of a command */ +#define VM_ESCAPE_CHAR 0xAA /* Set bit 4 from the next byte to 0 */ + +#define VM_PROTOCOL_VERSION 1 +#define VM_CMD_VERSION 0xff /* A version number byte follows */ +#define VM_CMD_NOATTN 0x00 +#define VM_CMD_ATTN 0x01 +#define VM_CMD_ATTN_IRQ 0x02 +#define VM_CMD_POWEROFF 0x03 +#define VM_CMD_RESET 0x04 +#define VM_CMD_ENABLE_IRQ 0x05 /* Enable/disable the messaging irq */ +#define VM_CMD_DISABLE_IRQ 0x06 +#define VM_CMD_SEND_NMI 0x07 +#define VM_CMD_CAPABILITIES 0x08 +#define VM_CAPABILITIES_POWER 0x01 +#define VM_CAPABILITIES_RESET 0x02 +#define VM_CAPABILITIES_IRQ 0x04 +#define VM_CAPABILITIES_NMI 0x08 +#define VM_CAPABILITIES_ATTN 0x10 +#define VM_CMD_FORCEOFF 0x09 + +#define TYPE_IPMI_BMC_EXTERN "ipmi-bmc-extern" +#define IPMI_BMC_EXTERN(obj) OBJECT_CHECK(IPMIBmcExtern, (obj), \ + TYPE_IPMI_BMC_EXTERN) +typedef struct IPMIBmcExtern { + IPMIBmc parent; + + CharDriverState *chr; + + bool connected; + + unsigned char inbuf[MAX_IPMI_MSG_SIZE + 2]; + unsigned int inpos; + bool in_escape; + bool in_too_many; + bool waiting_rsp; + bool sending_cmd; + + unsigned char outbuf[(MAX_IPMI_MSG_SIZE + 2) * 2 + 1]; + unsigned int outpos; + unsigned int outlen; + + struct QEMUTimer *extern_timer; + + /* A reset event is pending to be sent upstream. */ + bool send_reset; +} IPMIBmcExtern; + +static int can_receive(void *opaque); +static void receive(void *opaque, const uint8_t *buf, int size); +static void chr_event(void *opaque, int event); + +static unsigned char +ipmb_checksum(const unsigned char *data, int size, unsigned char start) +{ + unsigned char csum = start; + + for (; size > 0; size--, data++) { + csum += *data; + } + return csum; +} + +static void continue_send(IPMIBmcExtern *ibe) +{ + if (ibe->outlen == 0) { + goto check_reset; + } + send: + ibe->outpos += qemu_chr_fe_write(ibe->chr, ibe->outbuf + ibe->outpos, + ibe->outlen - ibe->outpos); + if (ibe->outpos < ibe->outlen) { + /* Not fully transmitted, try again in a 10ms */ + timer_mod_ns(ibe->extern_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 10000000); + } else { + /* Sent */ + ibe->outlen = 0; + ibe->outpos = 0; + if (!ibe->sending_cmd) { + ibe->waiting_rsp = true; + } else { + ibe->sending_cmd = false; + } + check_reset: + if (ibe->connected && ibe->send_reset) { + /* Send the reset */ + ibe->outbuf[0] = VM_CMD_RESET; + ibe->outbuf[1] = VM_CMD_CHAR; + ibe->outlen = 2; + ibe->outpos = 0; + ibe->send_reset = false; + ibe->sending_cmd = true; + goto send; + } + + if (ibe->waiting_rsp) { + /* Make sure we get a response within 4 seconds. */ + timer_mod_ns(ibe->extern_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 4000000000ULL); + } + } + return; +} + +static void extern_timeout(void *opaque) +{ + IPMIBmcExtern *ibe = opaque; + IPMIInterface *s = ibe->parent.intf; + + if (ibe->connected) { + if (ibe->waiting_rsp && (ibe->outlen == 0)) { + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + /* The message response timed out, return an error. */ + ibe->waiting_rsp = false; + ibe->inbuf[1] = ibe->outbuf[1] | 0x04; + ibe->inbuf[2] = ibe->outbuf[2]; + ibe->inbuf[3] = IPMI_CC_TIMEOUT; + k->handle_rsp(s, ibe->outbuf[0], ibe->inbuf + 1, 3); + } else { + continue_send(ibe); + } + } +} + +static void addchar(IPMIBmcExtern *ibe, unsigned char ch) +{ + switch (ch) { + case VM_MSG_CHAR: + case VM_CMD_CHAR: + case VM_ESCAPE_CHAR: + ibe->outbuf[ibe->outlen] = VM_ESCAPE_CHAR; + ibe->outlen++; + ch |= 0x10; + /* No break */ + + default: + ibe->outbuf[ibe->outlen] = ch; + ibe->outlen++; + } +} + +static void ipmi_bmc_extern_handle_command(IPMIBmc *b, + uint8_t *cmd, unsigned int cmd_len, + unsigned int max_cmd_len, + uint8_t msg_id) +{ + IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b); + IPMIInterface *s = ibe->parent.intf; + uint8_t err = 0, csum; + unsigned int i; + + if (ibe->outlen) { + /* We already have a command queued. Shouldn't ever happen. */ + fprintf(stderr, "IPMI KCS: Got command when not finished with the" + " previous commmand\n"); + abort(); + } + + /* If it's too short or it was truncated, return an error. */ + if (cmd_len < 2) { + err = IPMI_CC_REQUEST_DATA_LENGTH_INVALID; + } else if ((cmd_len > max_cmd_len) || (cmd_len > MAX_IPMI_MSG_SIZE)) { + err = IPMI_CC_REQUEST_DATA_TRUNCATED; + } else if (!ibe->connected) { + err = IPMI_CC_BMC_INIT_IN_PROGRESS; + } + if (err) { + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + unsigned char rsp[3]; + rsp[0] = cmd[0] | 0x04; + rsp[1] = cmd[1]; + rsp[2] = err; + ibe->waiting_rsp = false; + k->handle_rsp(s, msg_id, rsp, 3); + goto out; + } + + addchar(ibe, msg_id); + for (i = 0; i < cmd_len; i++) { + addchar(ibe, cmd[i]); + } + csum = ipmb_checksum(&msg_id, 1, 0); + addchar(ibe, -ipmb_checksum(cmd, cmd_len, csum)); + + ibe->outbuf[ibe->outlen] = VM_MSG_CHAR; + ibe->outlen++; + + /* Start the transmit */ + continue_send(ibe); + + out: + return; +} + +static void handle_hw_op(IPMIBmcExtern *ibe, unsigned char hw_op) +{ + IPMIInterface *s = ibe->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + + switch (hw_op) { + case VM_CMD_VERSION: + /* We only support one version at this time. */ + break; + + case VM_CMD_NOATTN: + k->set_atn(s, 0, 0); + break; + + case VM_CMD_ATTN: + k->set_atn(s, 1, 0); + break; + + case VM_CMD_ATTN_IRQ: + k->set_atn(s, 1, 1); + break; + + case VM_CMD_POWEROFF: + k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0); + break; + + case VM_CMD_RESET: + k->do_hw_op(s, IPMI_RESET_CHASSIS, 0); + break; + + case VM_CMD_ENABLE_IRQ: + k->set_irq_enable(s, 1); + break; + + case VM_CMD_DISABLE_IRQ: + k->set_irq_enable(s, 0); + break; + + case VM_CMD_SEND_NMI: + k->do_hw_op(s, IPMI_SEND_NMI, 0); + break; + + case VM_CMD_FORCEOFF: + qemu_system_shutdown_request(); + break; + } +} + +static void handle_msg(IPMIBmcExtern *ibe) +{ + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(ibe->parent.intf); + + if (ibe->in_escape) { + ipmi_debug("msg escape not ended\n"); + return; + } + if (ibe->inpos < 5) { + ipmi_debug("msg too short\n"); + return; + } + if (ibe->in_too_many) { + ibe->inbuf[3] = IPMI_CC_REQUEST_DATA_TRUNCATED; + ibe->inpos = 4; + } else if (ipmb_checksum(ibe->inbuf, ibe->inpos, 0) != 0) { + ipmi_debug("msg checksum failure\n"); + return; + } else { + ibe->inpos--; /* Remove checkum */ + } + + timer_del(ibe->extern_timer); + ibe->waiting_rsp = false; + k->handle_rsp(ibe->parent.intf, ibe->inbuf[0], ibe->inbuf + 1, ibe->inpos - 1); +} + +static int can_receive(void *opaque) +{ + return 1; +} + +static void receive(void *opaque, const uint8_t *buf, int size) +{ + IPMIBmcExtern *ibe = opaque; + int i; + unsigned char hw_op; + + for (i = 0; i < size; i++) { + unsigned char ch = buf[i]; + + switch (ch) { + case VM_MSG_CHAR: + handle_msg(ibe); + ibe->in_too_many = false; + ibe->inpos = 0; + break; + + case VM_CMD_CHAR: + if (ibe->in_too_many) { + ipmi_debug("cmd in too many\n"); + ibe->in_too_many = false; + ibe->inpos = 0; + break; + } + if (ibe->in_escape) { + ipmi_debug("cmd in escape\n"); + ibe->in_too_many = false; + ibe->inpos = 0; + ibe->in_escape = false; + break; + } + ibe->in_too_many = false; + if (ibe->inpos < 1) { + break; + } + hw_op = ibe->inbuf[0]; + ibe->inpos = 0; + goto out_hw_op; + break; + + case VM_ESCAPE_CHAR: + ibe->in_escape = true; + break; + + default: + if (ibe->in_escape) { + ch &= ~0x10; + ibe->in_escape = false; + } + if (ibe->in_too_many) { + break; + } + if (ibe->inpos >= sizeof(ibe->inbuf)) { + ibe->in_too_many = true; + break; + } + ibe->inbuf[ibe->inpos] = ch; + ibe->inpos++; + break; + } + } + return; + + out_hw_op: + handle_hw_op(ibe, hw_op); +} + +static void chr_event(void *opaque, int event) +{ + IPMIBmcExtern *ibe = opaque; + IPMIInterface *s = ibe->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + unsigned char v; + + switch (event) { + case CHR_EVENT_OPENED: + ibe->connected = true; + ibe->outpos = 0; + ibe->outlen = 0; + addchar(ibe, VM_CMD_VERSION); + addchar(ibe, VM_PROTOCOL_VERSION); + ibe->outbuf[ibe->outlen] = VM_CMD_CHAR; + ibe->outlen++; + addchar(ibe, VM_CMD_CAPABILITIES); + v = VM_CAPABILITIES_IRQ | VM_CAPABILITIES_ATTN; + if (k->do_hw_op(ibe->parent.intf, IPMI_POWEROFF_CHASSIS, 1) == 0) { + v |= VM_CAPABILITIES_POWER; + } + if (k->do_hw_op(ibe->parent.intf, IPMI_RESET_CHASSIS, 1) == 0) { + v |= VM_CAPABILITIES_RESET; + } + if (k->do_hw_op(ibe->parent.intf, IPMI_SEND_NMI, 1) == 0) { + v |= VM_CAPABILITIES_NMI; + } + addchar(ibe, v); + ibe->outbuf[ibe->outlen] = VM_CMD_CHAR; + ibe->outlen++; + ibe->sending_cmd = false; + continue_send(ibe); + break; + + case CHR_EVENT_CLOSED: + if (!ibe->connected) { + return; + } + ibe->connected = false; + if (ibe->waiting_rsp) { + ibe->waiting_rsp = false; + ibe->inbuf[1] = ibe->outbuf[1] | 0x04; + ibe->inbuf[2] = ibe->outbuf[2]; + ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS; + k->handle_rsp(s, ibe->outbuf[0], ibe->inbuf + 1, 3); + } + break; + } +} + +static void ipmi_bmc_extern_handle_reset(IPMIBmc *b) +{ + IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b); + + ibe->send_reset = true; + continue_send(ibe); +} + +static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp) +{ + IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev); + + if (!ibe->chr) { + error_setg(errp, "IPMI external bmc requires chardev attribute"); + return; + } + + qemu_chr_add_handlers(ibe->chr, can_receive, receive, chr_event, ibe); +} + +static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id) +{ + IPMIBmcExtern *ibe = opaque; + + /* + * We don't directly restore waiting_rsp, Instead, we return an + * error on the interface if a response was being waited for. + */ + if (ibe->waiting_rsp) { + IPMIInterface *ii = ibe->parent.intf; + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + + ibe->waiting_rsp = false; + ibe->inbuf[1] = ibe->outbuf[1] | 0x04; + ibe->inbuf[2] = ibe->outbuf[2]; + ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS; + iic->handle_rsp(ii, ibe->outbuf[0], ibe->inbuf + 1, 3); + } + return 0; +} + +static const VMStateDescription vmstate_ipmi_bmc_extern = { + .name = TYPE_IPMI_BMC_EXTERN, + .version_id = 1, + .minimum_version_id = 1, + .post_load = ipmi_bmc_extern_post_migrate, + .fields = (VMStateField[]) { + VMSTATE_BOOL(send_reset, IPMIBmcExtern), + VMSTATE_BOOL(waiting_rsp, IPMIBmcExtern), + VMSTATE_END_OF_LIST() + } +}; + +static void ipmi_bmc_extern_init(Object *obj) +{ + IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj); + + ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe); + vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe); +} + +static Property ipmi_bmc_extern_properties[] = { + DEFINE_PROP_CHR("chardev", IPMIBmcExtern, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ipmi_bmc_extern_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + IPMIBmcClass *bk = IPMI_BMC_CLASS(oc); + + bk->handle_command = ipmi_bmc_extern_handle_command; + bk->handle_reset = ipmi_bmc_extern_handle_reset; + dc->realize = ipmi_bmc_extern_realize; + dc->props = ipmi_bmc_extern_properties; +} + +static const TypeInfo ipmi_bmc_extern_type = { + .name = TYPE_IPMI_BMC_EXTERN, + .parent = TYPE_IPMI_BMC, + .instance_size = sizeof(IPMIBmcExtern), + .instance_init = ipmi_bmc_extern_init, + .class_init = ipmi_bmc_extern_class_init, + }; + +static void ipmi_bmc_extern_register_types(void) +{ + type_register_static(&ipmi_bmc_extern_type); +} + +type_init(ipmi_bmc_extern_register_types) diff --git a/qemu/hw/ipmi/ipmi_bmc_sim.c b/qemu/hw/ipmi/ipmi_bmc_sim.c new file mode 100644 index 000000000..dc9c14cd2 --- /dev/null +++ b/qemu/hw/ipmi/ipmi_bmc_sim.c @@ -0,0 +1,1810 @@ +/* + * IPMI BMC emulation + * + * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "sysemu/sysemu.h" +#include "qemu/timer.h" +#include "hw/ipmi/ipmi.h" +#include "qemu/error-report.h" + +#define IPMI_NETFN_CHASSIS 0x00 + +#define IPMI_CMD_GET_CHASSIS_CAPABILITIES 0x00 +#define IPMI_CMD_GET_CHASSIS_STATUS 0x01 +#define IPMI_CMD_CHASSIS_CONTROL 0x02 +#define IPMI_CMD_GET_SYS_RESTART_CAUSE 0x09 + +#define IPMI_NETFN_SENSOR_EVENT 0x04 + +#define IPMI_CMD_SET_SENSOR_EVT_ENABLE 0x28 +#define IPMI_CMD_GET_SENSOR_EVT_ENABLE 0x29 +#define IPMI_CMD_REARM_SENSOR_EVTS 0x2a +#define IPMI_CMD_GET_SENSOR_EVT_STATUS 0x2b +#define IPMI_CMD_GET_SENSOR_READING 0x2d +#define IPMI_CMD_SET_SENSOR_TYPE 0x2e +#define IPMI_CMD_GET_SENSOR_TYPE 0x2f + +/* #define IPMI_NETFN_APP 0x06 In ipmi.h */ + +#define IPMI_CMD_GET_DEVICE_ID 0x01 +#define IPMI_CMD_COLD_RESET 0x02 +#define IPMI_CMD_WARM_RESET 0x03 +#define IPMI_CMD_SET_ACPI_POWER_STATE 0x06 +#define IPMI_CMD_GET_ACPI_POWER_STATE 0x07 +#define IPMI_CMD_GET_DEVICE_GUID 0x08 +#define IPMI_CMD_RESET_WATCHDOG_TIMER 0x22 +#define IPMI_CMD_SET_WATCHDOG_TIMER 0x24 +#define IPMI_CMD_GET_WATCHDOG_TIMER 0x25 +#define IPMI_CMD_SET_BMC_GLOBAL_ENABLES 0x2e +#define IPMI_CMD_GET_BMC_GLOBAL_ENABLES 0x2f +#define IPMI_CMD_CLR_MSG_FLAGS 0x30 +#define IPMI_CMD_GET_MSG_FLAGS 0x31 +#define IPMI_CMD_GET_MSG 0x33 +#define IPMI_CMD_SEND_MSG 0x34 +#define IPMI_CMD_READ_EVT_MSG_BUF 0x35 + +#define IPMI_NETFN_STORAGE 0x0a + +#define IPMI_CMD_GET_SDR_REP_INFO 0x20 +#define IPMI_CMD_GET_SDR_REP_ALLOC_INFO 0x21 +#define IPMI_CMD_RESERVE_SDR_REP 0x22 +#define IPMI_CMD_GET_SDR 0x23 +#define IPMI_CMD_ADD_SDR 0x24 +#define IPMI_CMD_PARTIAL_ADD_SDR 0x25 +#define IPMI_CMD_DELETE_SDR 0x26 +#define IPMI_CMD_CLEAR_SDR_REP 0x27 +#define IPMI_CMD_GET_SDR_REP_TIME 0x28 +#define IPMI_CMD_SET_SDR_REP_TIME 0x29 +#define IPMI_CMD_ENTER_SDR_REP_UPD_MODE 0x2A +#define IPMI_CMD_EXIT_SDR_REP_UPD_MODE 0x2B +#define IPMI_CMD_RUN_INIT_AGENT 0x2C +#define IPMI_CMD_GET_SEL_INFO 0x40 +#define IPMI_CMD_GET_SEL_ALLOC_INFO 0x41 +#define IPMI_CMD_RESERVE_SEL 0x42 +#define IPMI_CMD_GET_SEL_ENTRY 0x43 +#define IPMI_CMD_ADD_SEL_ENTRY 0x44 +#define IPMI_CMD_PARTIAL_ADD_SEL_ENTRY 0x45 +#define IPMI_CMD_DELETE_SEL_ENTRY 0x46 +#define IPMI_CMD_CLEAR_SEL 0x47 +#define IPMI_CMD_GET_SEL_TIME 0x48 +#define IPMI_CMD_SET_SEL_TIME 0x49 + + +/* Same as a timespec struct. */ +struct ipmi_time { + long tv_sec; + long tv_nsec; +}; + +#define MAX_SEL_SIZE 128 + +typedef struct IPMISel { + uint8_t sel[MAX_SEL_SIZE][16]; + unsigned int next_free; + long time_offset; + uint16_t reservation; + uint8_t last_addition[4]; + uint8_t last_clear[4]; + uint8_t overflow; +} IPMISel; + +#define MAX_SDR_SIZE 16384 + +typedef struct IPMISdr { + uint8_t sdr[MAX_SDR_SIZE]; + unsigned int next_free; + uint16_t next_rec_id; + uint16_t reservation; + uint8_t last_addition[4]; + uint8_t last_clear[4]; + uint8_t overflow; +} IPMISdr; + +typedef struct IPMISensor { + uint8_t status; + uint8_t reading; + uint16_t states_suppt; + uint16_t assert_suppt; + uint16_t deassert_suppt; + uint16_t states; + uint16_t assert_states; + uint16_t deassert_states; + uint16_t assert_enable; + uint16_t deassert_enable; + uint8_t sensor_type; + uint8_t evt_reading_type_code; +} IPMISensor; +#define IPMI_SENSOR_GET_PRESENT(s) ((s)->status & 0x01) +#define IPMI_SENSOR_SET_PRESENT(s, v) ((s)->status = (s->status & ~0x01) | \ + !!(v)) +#define IPMI_SENSOR_GET_SCAN_ON(s) ((s)->status & 0x40) +#define IPMI_SENSOR_SET_SCAN_ON(s, v) ((s)->status = (s->status & ~0x40) | \ + ((!!(v)) << 6)) +#define IPMI_SENSOR_GET_EVENTS_ON(s) ((s)->status & 0x80) +#define IPMI_SENSOR_SET_EVENTS_ON(s, v) ((s)->status = (s->status & ~0x80) | \ + ((!!(v)) << 7)) +#define IPMI_SENSOR_GET_RET_STATUS(s) ((s)->status & 0xc0) +#define IPMI_SENSOR_SET_RET_STATUS(s, v) ((s)->status = (s->status & ~0xc0) | \ + (v & 0xc0)) +#define IPMI_SENSOR_IS_DISCRETE(s) ((s)->evt_reading_type_code != 1) + +#define MAX_SENSORS 20 +#define IPMI_WATCHDOG_SENSOR 0 + +typedef struct IPMIBmcSim IPMIBmcSim; +typedef struct RspBuffer RspBuffer; + +#define MAX_NETFNS 64 + +typedef struct IPMICmdHandler { + void (*cmd_handler)(IPMIBmcSim *s, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp); + unsigned int cmd_len_min; +} IPMICmdHandler; + +typedef struct IPMINetfn { + unsigned int cmd_nums; + const IPMICmdHandler *cmd_handlers; +} IPMINetfn; + +typedef struct IPMIRcvBufEntry { + QTAILQ_ENTRY(IPMIRcvBufEntry) entry; + uint8_t len; + uint8_t buf[MAX_IPMI_MSG_SIZE]; +} IPMIRcvBufEntry; + +#define TYPE_IPMI_BMC_SIMULATOR "ipmi-bmc-sim" +#define IPMI_BMC_SIMULATOR(obj) OBJECT_CHECK(IPMIBmcSim, (obj), \ + TYPE_IPMI_BMC_SIMULATOR) +struct IPMIBmcSim { + IPMIBmc parent; + + QEMUTimer *timer; + + uint8_t bmc_global_enables; + uint8_t msg_flags; + + bool watchdog_initialized; + uint8_t watchdog_use; + uint8_t watchdog_action; + uint8_t watchdog_pretimeout; /* In seconds */ + bool watchdog_expired; + uint16_t watchdog_timeout; /* in 100's of milliseconds */ + + bool watchdog_running; + bool watchdog_preaction_ran; + int64_t watchdog_expiry; + + uint8_t device_id; + uint8_t ipmi_version; + uint8_t device_rev; + uint8_t fwrev1; + uint8_t fwrev2; + uint8_t mfg_id[3]; + uint8_t product_id[2]; + + uint8_t restart_cause; + + uint8_t acpi_power_state[2]; + uint8_t uuid[16]; + + IPMISel sel; + IPMISdr sdr; + IPMISensor sensors[MAX_SENSORS]; + + /* Odd netfns are for responses, so we only need the even ones. */ + const IPMINetfn *netfns[MAX_NETFNS / 2]; + + QemuMutex lock; + /* We allow one event in the buffer */ + uint8_t evtbuf[16]; + + QTAILQ_HEAD(, IPMIRcvBufEntry) rcvbufs; +}; + +#define IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK (1 << 3) +#define IPMI_BMC_MSG_FLAG_EVT_BUF_FULL (1 << 1) +#define IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE (1 << 0) +#define IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK_SET(s) \ + (IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK & (s)->msg_flags) +#define IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(s) \ + (IPMI_BMC_MSG_FLAG_EVT_BUF_FULL & (s)->msg_flags) +#define IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(s) \ + (IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE & (s)->msg_flags) + +#define IPMI_BMC_RCV_MSG_QUEUE_INT_BIT 0 +#define IPMI_BMC_EVBUF_FULL_INT_BIT 1 +#define IPMI_BMC_EVENT_MSG_BUF_BIT 2 +#define IPMI_BMC_EVENT_LOG_BIT 3 +#define IPMI_BMC_MSG_INTS_ON(s) ((s)->bmc_global_enables & \ + (1 << IPMI_BMC_RCV_MSG_QUEUE_INT_BIT)) +#define IPMI_BMC_EVBUF_FULL_INT_ENABLED(s) ((s)->bmc_global_enables & \ + (1 << IPMI_BMC_EVBUF_FULL_INT_BIT)) +#define IPMI_BMC_EVENT_LOG_ENABLED(s) ((s)->bmc_global_enables & \ + (1 << IPMI_BMC_EVENT_LOG_BIT)) +#define IPMI_BMC_EVENT_MSG_BUF_ENABLED(s) ((s)->bmc_global_enables & \ + (1 << IPMI_BMC_EVENT_MSG_BUF_BIT)) + +#define IPMI_BMC_WATCHDOG_USE_MASK 0xc7 +#define IPMI_BMC_WATCHDOG_ACTION_MASK 0x77 +#define IPMI_BMC_WATCHDOG_GET_USE(s) ((s)->watchdog_use & 0x7) +#define IPMI_BMC_WATCHDOG_GET_DONT_LOG(s) (((s)->watchdog_use >> 7) & 0x1) +#define IPMI_BMC_WATCHDOG_GET_DONT_STOP(s) (((s)->watchdog_use >> 6) & 0x1) +#define IPMI_BMC_WATCHDOG_GET_PRE_ACTION(s) (((s)->watchdog_action >> 4) & 0x7) +#define IPMI_BMC_WATCHDOG_PRE_NONE 0 +#define IPMI_BMC_WATCHDOG_PRE_SMI 1 +#define IPMI_BMC_WATCHDOG_PRE_NMI 2 +#define IPMI_BMC_WATCHDOG_PRE_MSG_INT 3 +#define IPMI_BMC_WATCHDOG_GET_ACTION(s) ((s)->watchdog_action & 0x7) +#define IPMI_BMC_WATCHDOG_ACTION_NONE 0 +#define IPMI_BMC_WATCHDOG_ACTION_RESET 1 +#define IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN 2 +#define IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE 3 + +struct RspBuffer { + uint8_t buffer[MAX_IPMI_MSG_SIZE]; + unsigned int len; +}; + +#define RSP_BUFFER_INITIALIZER { } + +static inline void rsp_buffer_set_error(RspBuffer *rsp, uint8_t byte) +{ + rsp->buffer[2] = byte; +} + +/* Add a byte to the response. */ +static inline void rsp_buffer_push(RspBuffer *rsp, uint8_t byte) +{ + if (rsp->len >= sizeof(rsp->buffer)) { + rsp_buffer_set_error(rsp, IPMI_CC_REQUEST_DATA_TRUNCATED); + return; + } + rsp->buffer[rsp->len++] = byte; +} + +static inline void rsp_buffer_pushmore(RspBuffer *rsp, uint8_t *bytes, + unsigned int n) +{ + if (rsp->len + n >= sizeof(rsp->buffer)) { + rsp_buffer_set_error(rsp, IPMI_CC_REQUEST_DATA_TRUNCATED); + return; + } + + memcpy(&rsp->buffer[rsp->len], bytes, n); + rsp->len += n; +} + +static void ipmi_sim_handle_timeout(IPMIBmcSim *ibs); + +static void ipmi_gettime(struct ipmi_time *time) +{ + int64_t stime; + + stime = qemu_clock_get_ns(QEMU_CLOCK_HOST); + time->tv_sec = stime / 1000000000LL; + time->tv_nsec = stime % 1000000000LL; +} + +static int64_t ipmi_getmonotime(void) +{ + return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +} + +static void ipmi_timeout(void *opaque) +{ + IPMIBmcSim *ibs = opaque; + + ipmi_sim_handle_timeout(ibs); +} + +static void set_timestamp(IPMIBmcSim *ibs, uint8_t *ts) +{ + unsigned int val; + struct ipmi_time now; + + ipmi_gettime(&now); + val = now.tv_sec + ibs->sel.time_offset; + ts[0] = val & 0xff; + ts[1] = (val >> 8) & 0xff; + ts[2] = (val >> 16) & 0xff; + ts[3] = (val >> 24) & 0xff; +} + +static void sdr_inc_reservation(IPMISdr *sdr) +{ + sdr->reservation++; + if (sdr->reservation == 0) { + sdr->reservation = 1; + } +} + +static int sdr_add_entry(IPMIBmcSim *ibs, + const struct ipmi_sdr_header *sdrh_entry, + unsigned int len, uint16_t *recid) +{ + struct ipmi_sdr_header *sdrh = + (struct ipmi_sdr_header *) &ibs->sdr.sdr[ibs->sdr.next_free]; + + if ((len < IPMI_SDR_HEADER_SIZE) || (len > 255)) { + return 1; + } + + if (ipmi_sdr_length(sdrh_entry) != len) { + return 1; + } + + if (ibs->sdr.next_free + len > MAX_SDR_SIZE) { + ibs->sdr.overflow = 1; + return 1; + } + + memcpy(sdrh, sdrh_entry, len); + sdrh->rec_id[0] = ibs->sdr.next_rec_id & 0xff; + sdrh->rec_id[1] = (ibs->sdr.next_rec_id >> 8) & 0xff; + sdrh->sdr_version = 0x51; /* Conform to IPMI 1.5 spec */ + + if (recid) { + *recid = ibs->sdr.next_rec_id; + } + ibs->sdr.next_rec_id++; + set_timestamp(ibs, ibs->sdr.last_addition); + ibs->sdr.next_free += len; + sdr_inc_reservation(&ibs->sdr); + return 0; +} + +static int sdr_find_entry(IPMISdr *sdr, uint16_t recid, + unsigned int *retpos, uint16_t *nextrec) +{ + unsigned int pos = *retpos; + + while (pos < sdr->next_free) { + struct ipmi_sdr_header *sdrh = + (struct ipmi_sdr_header *) &sdr->sdr[pos]; + uint16_t trec = ipmi_sdr_recid(sdrh); + unsigned int nextpos = pos + ipmi_sdr_length(sdrh); + + if (trec == recid) { + if (nextrec) { + if (nextpos >= sdr->next_free) { + *nextrec = 0xffff; + } else { + *nextrec = (sdr->sdr[nextpos] | + (sdr->sdr[nextpos + 1] << 8)); + } + } + *retpos = pos; + return 0; + } + pos = nextpos; + } + return 1; +} + +static void sel_inc_reservation(IPMISel *sel) +{ + sel->reservation++; + if (sel->reservation == 0) { + sel->reservation = 1; + } +} + +/* Returns 1 if the SEL is full and can't hold the event. */ +static int sel_add_event(IPMIBmcSim *ibs, uint8_t *event) +{ + event[0] = 0xff; + event[1] = 0xff; + set_timestamp(ibs, event + 3); + if (ibs->sel.next_free == MAX_SEL_SIZE) { + ibs->sel.overflow = 1; + return 1; + } + event[0] = ibs->sel.next_free & 0xff; + event[1] = (ibs->sel.next_free >> 8) & 0xff; + memcpy(ibs->sel.last_addition, event + 3, 4); + memcpy(ibs->sel.sel[ibs->sel.next_free], event, 16); + ibs->sel.next_free++; + sel_inc_reservation(&ibs->sel); + return 0; +} + +static int attn_set(IPMIBmcSim *ibs) +{ + return IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(ibs) + || IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(ibs) + || IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK_SET(ibs); +} + +static int attn_irq_enabled(IPMIBmcSim *ibs) +{ + return (IPMI_BMC_MSG_INTS_ON(ibs) && IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(ibs)) + || (IPMI_BMC_EVBUF_FULL_INT_ENABLED(ibs) && + IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(ibs)); +} + +static void gen_event(IPMIBmcSim *ibs, unsigned int sens_num, uint8_t deassert, + uint8_t evd1, uint8_t evd2, uint8_t evd3) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + uint8_t evt[16]; + IPMISensor *sens = ibs->sensors + sens_num; + + if (!IPMI_BMC_EVENT_MSG_BUF_ENABLED(ibs)) { + return; + } + if (!IPMI_SENSOR_GET_EVENTS_ON(sens)) { + return; + } + + evt[2] = 0x2; /* System event record */ + evt[7] = ibs->parent.slave_addr; + evt[8] = 0; + evt[9] = 0x04; /* Format version */ + evt[10] = sens->sensor_type; + evt[11] = sens_num; + evt[12] = sens->evt_reading_type_code | (!!deassert << 7); + evt[13] = evd1; + evt[14] = evd2; + evt[15] = evd3; + + if (IPMI_BMC_EVENT_LOG_ENABLED(ibs)) { + sel_add_event(ibs, evt); + } + + if (ibs->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL) { + return; + } + + memcpy(ibs->evtbuf, evt, 16); + ibs->msg_flags |= IPMI_BMC_MSG_FLAG_EVT_BUF_FULL; + k->set_atn(s, 1, attn_irq_enabled(ibs)); +} + +static void sensor_set_discrete_bit(IPMIBmcSim *ibs, unsigned int sensor, + unsigned int bit, unsigned int val, + uint8_t evd1, uint8_t evd2, uint8_t evd3) +{ + IPMISensor *sens; + uint16_t mask; + + if (sensor >= MAX_SENSORS) { + return; + } + if (bit >= 16) { + return; + } + + mask = (1 << bit); + sens = ibs->sensors + sensor; + if (val) { + sens->states |= mask & sens->states_suppt; + if (sens->assert_states & mask) { + return; /* Already asserted */ + } + sens->assert_states |= mask & sens->assert_suppt; + if (sens->assert_enable & mask & sens->assert_states) { + /* Send an event on assert */ + gen_event(ibs, sensor, 0, evd1, evd2, evd3); + } + } else { + sens->states &= ~(mask & sens->states_suppt); + if (sens->deassert_states & mask) { + return; /* Already deasserted */ + } + sens->deassert_states |= mask & sens->deassert_suppt; + if (sens->deassert_enable & mask & sens->deassert_states) { + /* Send an event on deassert */ + gen_event(ibs, sensor, 1, evd1, evd2, evd3); + } + } +} + +static void ipmi_init_sensors_from_sdrs(IPMIBmcSim *s) +{ + unsigned int i, pos; + IPMISensor *sens; + + for (i = 0; i < MAX_SENSORS; i++) { + memset(s->sensors + i, 0, sizeof(*sens)); + } + + pos = 0; + for (i = 0; !sdr_find_entry(&s->sdr, i, &pos, NULL); i++) { + struct ipmi_sdr_compact *sdr = + (struct ipmi_sdr_compact *) &s->sdr.sdr[pos]; + unsigned int len = sdr->header.rec_length; + + if (len < 20) { + continue; + } + if (sdr->header.rec_type != IPMI_SDR_COMPACT_TYPE) { + continue; /* Not a sensor SDR we set from */ + } + + if (sdr->sensor_owner_number >= MAX_SENSORS) { + continue; + } + sens = s->sensors + sdr->sensor_owner_number; + + IPMI_SENSOR_SET_PRESENT(sens, 1); + IPMI_SENSOR_SET_SCAN_ON(sens, (sdr->sensor_init >> 6) & 1); + IPMI_SENSOR_SET_EVENTS_ON(sens, (sdr->sensor_init >> 5) & 1); + sens->assert_suppt = sdr->assert_mask[0] | (sdr->assert_mask[1] << 8); + sens->deassert_suppt = + sdr->deassert_mask[0] | (sdr->deassert_mask[1] << 8); + sens->states_suppt = + sdr->discrete_mask[0] | (sdr->discrete_mask[1] << 8); + sens->sensor_type = sdr->sensor_type; + sens->evt_reading_type_code = sdr->reading_type & 0x7f; + + /* Enable all the events that are supported. */ + sens->assert_enable = sens->assert_suppt; + sens->deassert_enable = sens->deassert_suppt; + } +} + +static int ipmi_register_netfn(IPMIBmcSim *s, unsigned int netfn, + const IPMINetfn *netfnd) +{ + if ((netfn & 1) || (netfn >= MAX_NETFNS) || (s->netfns[netfn / 2])) { + return -1; + } + s->netfns[netfn / 2] = netfnd; + return 0; +} + +static const IPMICmdHandler *ipmi_get_handler(IPMIBmcSim *ibs, + unsigned int netfn, + unsigned int cmd) +{ + const IPMICmdHandler *hdl; + + if (netfn & 1 || netfn >= MAX_NETFNS || !ibs->netfns[netfn / 2]) { + return NULL; + } + + if (cmd >= ibs->netfns[netfn / 2]->cmd_nums) { + return NULL; + } + + hdl = &ibs->netfns[netfn / 2]->cmd_handlers[cmd]; + if (!hdl->cmd_handler) { + return NULL; + } + + return hdl; +} + +static void next_timeout(IPMIBmcSim *ibs) +{ + int64_t next; + if (ibs->watchdog_running) { + next = ibs->watchdog_expiry; + } else { + /* Wait a minute */ + next = ipmi_getmonotime() + 60 * 1000000000LL; + } + timer_mod_ns(ibs->timer, next); +} + +static void ipmi_sim_handle_command(IPMIBmc *b, + uint8_t *cmd, unsigned int cmd_len, + unsigned int max_cmd_len, + uint8_t msg_id) +{ + IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b); + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + const IPMICmdHandler *hdl; + RspBuffer rsp = RSP_BUFFER_INITIALIZER; + + /* Set up the response, set the low bit of NETFN. */ + /* Note that max_rsp_len must be at least 3 */ + if (sizeof(rsp.buffer) < 3) { + rsp_buffer_set_error(&rsp, IPMI_CC_REQUEST_DATA_TRUNCATED); + goto out; + } + + rsp_buffer_push(&rsp, cmd[0] | 0x04); + rsp_buffer_push(&rsp, cmd[1]); + rsp_buffer_push(&rsp, 0); /* Assume success */ + + /* If it's too short or it was truncated, return an error. */ + if (cmd_len < 2) { + rsp_buffer_set_error(&rsp, IPMI_CC_REQUEST_DATA_LENGTH_INVALID); + goto out; + } + if (cmd_len > max_cmd_len) { + rsp_buffer_set_error(&rsp, IPMI_CC_REQUEST_DATA_TRUNCATED); + goto out; + } + + if ((cmd[0] & 0x03) != 0) { + /* Only have stuff on LUN 0 */ + rsp_buffer_set_error(&rsp, IPMI_CC_COMMAND_INVALID_FOR_LUN); + goto out; + } + + hdl = ipmi_get_handler(ibs, cmd[0] >> 2, cmd[1]); + if (!hdl) { + rsp_buffer_set_error(&rsp, IPMI_CC_INVALID_CMD); + goto out; + } + + if (cmd_len < hdl->cmd_len_min) { + rsp_buffer_set_error(&rsp, IPMI_CC_REQUEST_DATA_LENGTH_INVALID); + goto out; + } + + hdl->cmd_handler(ibs, cmd, cmd_len, &rsp); + + out: + k->handle_rsp(s, msg_id, rsp.buffer, rsp.len); + + next_timeout(ibs); +} + +static void ipmi_sim_handle_timeout(IPMIBmcSim *ibs) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + + if (!ibs->watchdog_running) { + goto out; + } + + if (!ibs->watchdog_preaction_ran) { + switch (IPMI_BMC_WATCHDOG_GET_PRE_ACTION(ibs)) { + case IPMI_BMC_WATCHDOG_PRE_NMI: + ibs->msg_flags |= IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK; + k->do_hw_op(s, IPMI_SEND_NMI, 0); + sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 8, 1, + 0xc8, (2 << 4) | 0xf, 0xff); + break; + + case IPMI_BMC_WATCHDOG_PRE_MSG_INT: + ibs->msg_flags |= IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK; + k->set_atn(s, 1, attn_irq_enabled(ibs)); + sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 8, 1, + 0xc8, (3 << 4) | 0xf, 0xff); + break; + + default: + goto do_full_expiry; + } + + ibs->watchdog_preaction_ran = 1; + /* Issued the pretimeout, do the rest of the timeout now. */ + ibs->watchdog_expiry = ipmi_getmonotime(); + ibs->watchdog_expiry += ibs->watchdog_pretimeout * 1000000000LL; + goto out; + } + + do_full_expiry: + ibs->watchdog_running = 0; /* Stop the watchdog on a timeout */ + ibs->watchdog_expired |= (1 << IPMI_BMC_WATCHDOG_GET_USE(ibs)); + switch (IPMI_BMC_WATCHDOG_GET_ACTION(ibs)) { + case IPMI_BMC_WATCHDOG_ACTION_NONE: + sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 0, 1, + 0xc0, ibs->watchdog_use & 0xf, 0xff); + break; + + case IPMI_BMC_WATCHDOG_ACTION_RESET: + sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 1, 1, + 0xc1, ibs->watchdog_use & 0xf, 0xff); + k->do_hw_op(s, IPMI_RESET_CHASSIS, 0); + break; + + case IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN: + sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 2, 1, + 0xc2, ibs->watchdog_use & 0xf, 0xff); + k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0); + break; + + case IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE: + sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 2, 1, + 0xc3, ibs->watchdog_use & 0xf, 0xff); + k->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 0); + break; + } + + out: + next_timeout(ibs); +} + +static void chassis_capabilities(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + rsp_buffer_push(rsp, 0); + rsp_buffer_push(rsp, ibs->parent.slave_addr); + rsp_buffer_push(rsp, ibs->parent.slave_addr); + rsp_buffer_push(rsp, ibs->parent.slave_addr); + rsp_buffer_push(rsp, ibs->parent.slave_addr); +} + +static void chassis_status(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + rsp_buffer_push(rsp, 0x61); /* Unknown power restore, power is on */ + rsp_buffer_push(rsp, 0); + rsp_buffer_push(rsp, 0); + rsp_buffer_push(rsp, 0); +} + +static void chassis_control(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + + switch (cmd[2] & 0xf) { + case 0: /* power down */ + rsp_buffer_set_error(rsp, k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0)); + break; + case 1: /* power up */ + rsp_buffer_set_error(rsp, k->do_hw_op(s, IPMI_POWERON_CHASSIS, 0)); + break; + case 2: /* power cycle */ + rsp_buffer_set_error(rsp, k->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 0)); + break; + case 3: /* hard reset */ + rsp_buffer_set_error(rsp, k->do_hw_op(s, IPMI_RESET_CHASSIS, 0)); + break; + case 4: /* pulse diagnostic interrupt */ + rsp_buffer_set_error(rsp, k->do_hw_op(s, IPMI_PULSE_DIAG_IRQ, 0)); + break; + case 5: /* soft shutdown via ACPI by overtemp emulation */ + rsp_buffer_set_error(rsp, k->do_hw_op(s, + IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP, 0)); + break; + default: + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } +} + +static void chassis_get_sys_restart_cause(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) + +{ + rsp_buffer_push(rsp, ibs->restart_cause & 0xf); /* Restart Cause */ + rsp_buffer_push(rsp, 0); /* Channel 0 */ +} + +static void get_device_id(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + rsp_buffer_push(rsp, ibs->device_id); + rsp_buffer_push(rsp, ibs->device_rev & 0xf); + rsp_buffer_push(rsp, ibs->fwrev1 & 0x7f); + rsp_buffer_push(rsp, ibs->fwrev2); + rsp_buffer_push(rsp, ibs->ipmi_version); + rsp_buffer_push(rsp, 0x07); /* sensor, SDR, and SEL. */ + rsp_buffer_push(rsp, ibs->mfg_id[0]); + rsp_buffer_push(rsp, ibs->mfg_id[1]); + rsp_buffer_push(rsp, ibs->mfg_id[2]); + rsp_buffer_push(rsp, ibs->product_id[0]); + rsp_buffer_push(rsp, ibs->product_id[1]); +} + +static void set_global_enables(IPMIBmcSim *ibs, uint8_t val) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + bool irqs_on; + + ibs->bmc_global_enables = val; + + irqs_on = val & (IPMI_BMC_EVBUF_FULL_INT_BIT | + IPMI_BMC_RCV_MSG_QUEUE_INT_BIT); + + k->set_irq_enable(s, irqs_on); +} + +static void cold_reset(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + + /* Disable all interrupts */ + set_global_enables(ibs, 1 << IPMI_BMC_EVENT_LOG_BIT); + + if (k->reset) { + k->reset(s, true); + } +} + +static void warm_reset(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + + if (k->reset) { + k->reset(s, false); + } +} +static void set_acpi_power_state(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + ibs->acpi_power_state[0] = cmd[2]; + ibs->acpi_power_state[1] = cmd[3]; +} + +static void get_acpi_power_state(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + rsp_buffer_push(rsp, ibs->acpi_power_state[0]); + rsp_buffer_push(rsp, ibs->acpi_power_state[1]); +} + +static void get_device_guid(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + unsigned int i; + + for (i = 0; i < 16; i++) { + rsp_buffer_push(rsp, ibs->uuid[i]); + } +} + +static void set_bmc_global_enables(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + set_global_enables(ibs, cmd[2]); +} + +static void get_bmc_global_enables(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + rsp_buffer_push(rsp, ibs->bmc_global_enables); +} + +static void clr_msg_flags(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + + ibs->msg_flags &= ~cmd[2]; + k->set_atn(s, attn_set(ibs), attn_irq_enabled(ibs)); +} + +static void get_msg_flags(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + rsp_buffer_push(rsp, ibs->msg_flags); +} + +static void read_evt_msg_buf(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + unsigned int i; + + if (!(ibs->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL)) { + rsp_buffer_set_error(rsp, 0x80); + return; + } + for (i = 0; i < 16; i++) { + rsp_buffer_push(rsp, ibs->evtbuf[i]); + } + ibs->msg_flags &= ~IPMI_BMC_MSG_FLAG_EVT_BUF_FULL; + k->set_atn(s, attn_set(ibs), attn_irq_enabled(ibs)); +} + +static void get_msg(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + IPMIRcvBufEntry *msg; + + qemu_mutex_lock(&ibs->lock); + if (QTAILQ_EMPTY(&ibs->rcvbufs)) { + rsp_buffer_set_error(rsp, 0x80); /* Queue empty */ + goto out; + } + rsp_buffer_push(rsp, 0); /* Channel 0 */ + msg = QTAILQ_FIRST(&ibs->rcvbufs); + rsp_buffer_pushmore(rsp, msg->buf, msg->len); + QTAILQ_REMOVE(&ibs->rcvbufs, msg, entry); + g_free(msg); + + if (QTAILQ_EMPTY(&ibs->rcvbufs)) { + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + + ibs->msg_flags &= ~IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE; + k->set_atn(s, attn_set(ibs), attn_irq_enabled(ibs)); + } + +out: + qemu_mutex_unlock(&ibs->lock); + return; +} + +static unsigned char +ipmb_checksum(unsigned char *data, int size, unsigned char csum) +{ + for (; size > 0; size--, data++) { + csum += *data; + } + + return -csum; +} + +static void send_msg(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + IPMIRcvBufEntry *msg; + uint8_t *buf; + uint8_t netfn, rqLun, rsLun, rqSeq; + + if (cmd[2] != 0) { + /* We only handle channel 0 with no options */ + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + if (cmd_len < 10) { + rsp_buffer_set_error(rsp, IPMI_CC_REQUEST_DATA_LENGTH_INVALID); + return; + } + + if (cmd[3] != 0x40) { + /* We only emulate a MC at address 0x40. */ + rsp_buffer_set_error(rsp, 0x83); /* NAK on write */ + return; + } + + cmd += 3; /* Skip the header. */ + cmd_len -= 3; + + /* + * At this point we "send" the message successfully. Any error will + * be returned in the response. + */ + if (ipmb_checksum(cmd, cmd_len, 0) != 0 || + cmd[3] != 0x20) { /* Improper response address */ + return; /* No response */ + } + + netfn = cmd[1] >> 2; + rqLun = cmd[4] & 0x3; + rsLun = cmd[1] & 0x3; + rqSeq = cmd[4] >> 2; + + if (rqLun != 2) { + /* We only support LUN 2 coming back to us. */ + return; + } + + msg = g_malloc(sizeof(*msg)); + msg->buf[0] = ((netfn | 1) << 2) | rqLun; /* NetFN, and make a response */ + msg->buf[1] = ipmb_checksum(msg->buf, 1, 0); + msg->buf[2] = cmd[0]; /* rsSA */ + msg->buf[3] = (rqSeq << 2) | rsLun; + msg->buf[4] = cmd[5]; /* Cmd */ + msg->buf[5] = 0; /* Completion Code */ + msg->len = 6; + + if ((cmd[1] >> 2) != IPMI_NETFN_APP || cmd[5] != IPMI_CMD_GET_DEVICE_ID) { + /* Not a command we handle. */ + msg->buf[5] = IPMI_CC_INVALID_CMD; + goto end_msg; + } + + buf = msg->buf + msg->len; /* After the CC */ + buf[0] = 0; + buf[1] = 0; + buf[2] = 0; + buf[3] = 0; + buf[4] = 0x51; + buf[5] = 0; + buf[6] = 0; + buf[7] = 0; + buf[8] = 0; + buf[9] = 0; + buf[10] = 0; + msg->len += 11; + + end_msg: + msg->buf[msg->len] = ipmb_checksum(msg->buf, msg->len, 0); + msg->len++; + qemu_mutex_lock(&ibs->lock); + QTAILQ_INSERT_TAIL(&ibs->rcvbufs, msg, entry); + ibs->msg_flags |= IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE; + k->set_atn(s, 1, attn_irq_enabled(ibs)); + qemu_mutex_unlock(&ibs->lock); +} + +static void do_watchdog_reset(IPMIBmcSim *ibs) +{ + if (IPMI_BMC_WATCHDOG_GET_ACTION(ibs) == + IPMI_BMC_WATCHDOG_ACTION_NONE) { + ibs->watchdog_running = 0; + return; + } + ibs->watchdog_preaction_ran = 0; + + + /* Timeout is in tenths of a second, offset is in seconds */ + ibs->watchdog_expiry = ipmi_getmonotime(); + ibs->watchdog_expiry += ibs->watchdog_timeout * 100000000LL; + if (IPMI_BMC_WATCHDOG_GET_PRE_ACTION(ibs) != IPMI_BMC_WATCHDOG_PRE_NONE) { + ibs->watchdog_expiry -= ibs->watchdog_pretimeout * 1000000000LL; + } + ibs->watchdog_running = 1; +} + +static void reset_watchdog_timer(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + if (!ibs->watchdog_initialized) { + rsp_buffer_set_error(rsp, 0x80); + return; + } + do_watchdog_reset(ibs); +} + +static void set_watchdog_timer(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + unsigned int val; + + val = cmd[2] & 0x7; /* Validate use */ + if (val == 0 || val > 5) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + val = cmd[3] & 0x7; /* Validate action */ + switch (val) { + case IPMI_BMC_WATCHDOG_ACTION_NONE: + break; + + case IPMI_BMC_WATCHDOG_ACTION_RESET: + rsp_buffer_set_error(rsp, k->do_hw_op(s, IPMI_RESET_CHASSIS, 1)); + break; + + case IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN: + rsp_buffer_set_error(rsp, k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 1)); + break; + + case IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE: + rsp_buffer_set_error(rsp, k->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 1)); + break; + + default: + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + } + if (rsp->buffer[2]) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + val = (cmd[3] >> 4) & 0x7; /* Validate preaction */ + switch (val) { + case IPMI_BMC_WATCHDOG_PRE_MSG_INT: + case IPMI_BMC_WATCHDOG_PRE_NONE: + break; + + case IPMI_BMC_WATCHDOG_PRE_NMI: + if (!k->do_hw_op(s, IPMI_SEND_NMI, 1)) { + /* NMI not supported. */ + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + break; + + default: + /* We don't support PRE_SMI */ + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + ibs->watchdog_initialized = 1; + ibs->watchdog_use = cmd[2] & IPMI_BMC_WATCHDOG_USE_MASK; + ibs->watchdog_action = cmd[3] & IPMI_BMC_WATCHDOG_ACTION_MASK; + ibs->watchdog_pretimeout = cmd[4]; + ibs->watchdog_expired &= ~cmd[5]; + ibs->watchdog_timeout = cmd[6] | (((uint16_t) cmd[7]) << 8); + if (ibs->watchdog_running & IPMI_BMC_WATCHDOG_GET_DONT_STOP(ibs)) { + do_watchdog_reset(ibs); + } else { + ibs->watchdog_running = 0; + } +} + +static void get_watchdog_timer(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + rsp_buffer_push(rsp, ibs->watchdog_use); + rsp_buffer_push(rsp, ibs->watchdog_action); + rsp_buffer_push(rsp, ibs->watchdog_pretimeout); + rsp_buffer_push(rsp, ibs->watchdog_expired); + if (ibs->watchdog_running) { + long timeout; + timeout = ((ibs->watchdog_expiry - ipmi_getmonotime() + 50000000) + / 100000000); + rsp_buffer_push(rsp, timeout & 0xff); + rsp_buffer_push(rsp, (timeout >> 8) & 0xff); + } else { + rsp_buffer_push(rsp, 0); + rsp_buffer_push(rsp, 0); + } +} + +static void get_sdr_rep_info(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + unsigned int i; + + rsp_buffer_push(rsp, 0x51); /* Conform to IPMI 1.5 spec */ + rsp_buffer_push(rsp, ibs->sdr.next_rec_id & 0xff); + rsp_buffer_push(rsp, (ibs->sdr.next_rec_id >> 8) & 0xff); + rsp_buffer_push(rsp, (MAX_SDR_SIZE - ibs->sdr.next_free) & 0xff); + rsp_buffer_push(rsp, ((MAX_SDR_SIZE - ibs->sdr.next_free) >> 8) & 0xff); + for (i = 0; i < 4; i++) { + rsp_buffer_push(rsp, ibs->sdr.last_addition[i]); + } + for (i = 0; i < 4; i++) { + rsp_buffer_push(rsp, ibs->sdr.last_clear[i]); + } + /* Only modal support, reserve supported */ + rsp_buffer_push(rsp, (ibs->sdr.overflow << 7) | 0x22); +} + +static void reserve_sdr_rep(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + rsp_buffer_push(rsp, ibs->sdr.reservation & 0xff); + rsp_buffer_push(rsp, (ibs->sdr.reservation >> 8) & 0xff); +} + +static void get_sdr(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + unsigned int pos; + uint16_t nextrec; + struct ipmi_sdr_header *sdrh; + + if (cmd[6]) { + if ((cmd[2] | (cmd[3] << 8)) != ibs->sdr.reservation) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_RESERVATION); + return; + } + } + + pos = 0; + if (sdr_find_entry(&ibs->sdr, cmd[4] | (cmd[5] << 8), + &pos, &nextrec)) { + rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT); + return; + } + + sdrh = (struct ipmi_sdr_header *) &ibs->sdr.sdr[pos]; + + if (cmd[6] > ipmi_sdr_length(sdrh)) { + rsp_buffer_set_error(rsp, IPMI_CC_PARM_OUT_OF_RANGE); + return; + } + + rsp_buffer_push(rsp, nextrec & 0xff); + rsp_buffer_push(rsp, (nextrec >> 8) & 0xff); + + if (cmd[7] == 0xff) { + cmd[7] = ipmi_sdr_length(sdrh) - cmd[6]; + } + + if ((cmd[7] + rsp->len) > sizeof(rsp->buffer)) { + rsp_buffer_set_error(rsp, IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES); + return; + } + + rsp_buffer_pushmore(rsp, ibs->sdr.sdr + pos + cmd[6], cmd[7]); +} + +static void add_sdr(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + uint16_t recid; + struct ipmi_sdr_header *sdrh = (struct ipmi_sdr_header *) cmd + 2; + + if (sdr_add_entry(ibs, sdrh, cmd_len - 2, &recid)) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + rsp_buffer_push(rsp, recid & 0xff); + rsp_buffer_push(rsp, (recid >> 8) & 0xff); +} + +static void clear_sdr_rep(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + if ((cmd[2] | (cmd[3] << 8)) != ibs->sdr.reservation) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_RESERVATION); + return; + } + + if (cmd[4] != 'C' || cmd[5] != 'L' || cmd[6] != 'R') { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + if (cmd[7] == 0xaa) { + ibs->sdr.next_free = 0; + ibs->sdr.overflow = 0; + set_timestamp(ibs, ibs->sdr.last_clear); + rsp_buffer_push(rsp, 1); /* Erasure complete */ + sdr_inc_reservation(&ibs->sdr); + } else if (cmd[7] == 0) { + rsp_buffer_push(rsp, 1); /* Erasure complete */ + } else { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } +} + +static void get_sel_info(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + unsigned int i, val; + + rsp_buffer_push(rsp, 0x51); /* Conform to IPMI 1.5 */ + rsp_buffer_push(rsp, ibs->sel.next_free & 0xff); + rsp_buffer_push(rsp, (ibs->sel.next_free >> 8) & 0xff); + val = (MAX_SEL_SIZE - ibs->sel.next_free) * 16; + rsp_buffer_push(rsp, val & 0xff); + rsp_buffer_push(rsp, (val >> 8) & 0xff); + for (i = 0; i < 4; i++) { + rsp_buffer_push(rsp, ibs->sel.last_addition[i]); + } + for (i = 0; i < 4; i++) { + rsp_buffer_push(rsp, ibs->sel.last_clear[i]); + } + /* Only support Reserve SEL */ + rsp_buffer_push(rsp, (ibs->sel.overflow << 7) | 0x02); +} + +static void reserve_sel(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + rsp_buffer_push(rsp, ibs->sel.reservation & 0xff); + rsp_buffer_push(rsp, (ibs->sel.reservation >> 8) & 0xff); +} + +static void get_sel_entry(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + unsigned int val; + + if (cmd[6]) { + if ((cmd[2] | (cmd[3] << 8)) != ibs->sel.reservation) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_RESERVATION); + return; + } + } + if (ibs->sel.next_free == 0) { + rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT); + return; + } + if (cmd[6] > 15) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + if (cmd[7] == 0xff) { + cmd[7] = 16; + } else if ((cmd[7] + cmd[6]) > 16) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } else { + cmd[7] += cmd[6]; + } + + val = cmd[4] | (cmd[5] << 8); + if (val == 0xffff) { + val = ibs->sel.next_free - 1; + } else if (val >= ibs->sel.next_free) { + rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT); + return; + } + if ((val + 1) == ibs->sel.next_free) { + rsp_buffer_push(rsp, 0xff); + rsp_buffer_push(rsp, 0xff); + } else { + rsp_buffer_push(rsp, (val + 1) & 0xff); + rsp_buffer_push(rsp, ((val + 1) >> 8) & 0xff); + } + for (; cmd[6] < cmd[7]; cmd[6]++) { + rsp_buffer_push(rsp, ibs->sel.sel[val][cmd[6]]); + } +} + +static void add_sel_entry(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + if (sel_add_event(ibs, cmd + 2)) { + rsp_buffer_set_error(rsp, IPMI_CC_OUT_OF_SPACE); + return; + } + /* sel_add_event fills in the record number. */ + rsp_buffer_push(rsp, cmd[2]); + rsp_buffer_push(rsp, cmd[3]); +} + +static void clear_sel(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + if ((cmd[2] | (cmd[3] << 8)) != ibs->sel.reservation) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_RESERVATION); + return; + } + + if (cmd[4] != 'C' || cmd[5] != 'L' || cmd[6] != 'R') { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + if (cmd[7] == 0xaa) { + ibs->sel.next_free = 0; + ibs->sel.overflow = 0; + set_timestamp(ibs, ibs->sdr.last_clear); + rsp_buffer_push(rsp, 1); /* Erasure complete */ + sel_inc_reservation(&ibs->sel); + } else if (cmd[7] == 0) { + rsp_buffer_push(rsp, 1); /* Erasure complete */ + } else { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } +} + +static void get_sel_time(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + uint32_t val; + struct ipmi_time now; + + ipmi_gettime(&now); + val = now.tv_sec + ibs->sel.time_offset; + rsp_buffer_push(rsp, val & 0xff); + rsp_buffer_push(rsp, (val >> 8) & 0xff); + rsp_buffer_push(rsp, (val >> 16) & 0xff); + rsp_buffer_push(rsp, (val >> 24) & 0xff); +} + +static void set_sel_time(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + uint32_t val; + struct ipmi_time now; + + val = cmd[2] | (cmd[3] << 8) | (cmd[4] << 16) | (cmd[5] << 24); + ipmi_gettime(&now); + ibs->sel.time_offset = now.tv_sec - ((long) val); +} + +static void set_sensor_evt_enable(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + IPMISensor *sens; + + if ((cmd[2] >= MAX_SENSORS) || + !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { + rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT); + return; + } + sens = ibs->sensors + cmd[2]; + switch ((cmd[3] >> 4) & 0x3) { + case 0: /* Do not change */ + break; + case 1: /* Enable bits */ + if (cmd_len > 4) { + sens->assert_enable |= cmd[4]; + } + if (cmd_len > 5) { + sens->assert_enable |= cmd[5] << 8; + } + if (cmd_len > 6) { + sens->deassert_enable |= cmd[6]; + } + if (cmd_len > 7) { + sens->deassert_enable |= cmd[7] << 8; + } + break; + case 2: /* Disable bits */ + if (cmd_len > 4) { + sens->assert_enable &= ~cmd[4]; + } + if (cmd_len > 5) { + sens->assert_enable &= ~(cmd[5] << 8); + } + if (cmd_len > 6) { + sens->deassert_enable &= ~cmd[6]; + } + if (cmd_len > 7) { + sens->deassert_enable &= ~(cmd[7] << 8); + } + break; + case 3: + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + IPMI_SENSOR_SET_RET_STATUS(sens, cmd[3]); +} + +static void get_sensor_evt_enable(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + IPMISensor *sens; + + if ((cmd[2] >= MAX_SENSORS) || + !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { + rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT); + return; + } + sens = ibs->sensors + cmd[2]; + rsp_buffer_push(rsp, IPMI_SENSOR_GET_RET_STATUS(sens)); + rsp_buffer_push(rsp, sens->assert_enable & 0xff); + rsp_buffer_push(rsp, (sens->assert_enable >> 8) & 0xff); + rsp_buffer_push(rsp, sens->deassert_enable & 0xff); + rsp_buffer_push(rsp, (sens->deassert_enable >> 8) & 0xff); +} + +static void rearm_sensor_evts(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + IPMISensor *sens; + + if ((cmd[2] >= MAX_SENSORS) || + !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { + rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT); + return; + } + sens = ibs->sensors + cmd[2]; + + if ((cmd[3] & 0x80) == 0) { + /* Just clear everything */ + sens->states = 0; + return; + } +} + +static void get_sensor_evt_status(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + IPMISensor *sens; + + if ((cmd[2] >= MAX_SENSORS) || + !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { + rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT); + return; + } + sens = ibs->sensors + cmd[2]; + rsp_buffer_push(rsp, sens->reading); + rsp_buffer_push(rsp, IPMI_SENSOR_GET_RET_STATUS(sens)); + rsp_buffer_push(rsp, sens->assert_states & 0xff); + rsp_buffer_push(rsp, (sens->assert_states >> 8) & 0xff); + rsp_buffer_push(rsp, sens->deassert_states & 0xff); + rsp_buffer_push(rsp, (sens->deassert_states >> 8) & 0xff); +} + +static void get_sensor_reading(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + IPMISensor *sens; + + if ((cmd[2] >= MAX_SENSORS) || + !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { + rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT); + return; + } + sens = ibs->sensors + cmd[2]; + rsp_buffer_push(rsp, sens->reading); + rsp_buffer_push(rsp, IPMI_SENSOR_GET_RET_STATUS(sens)); + rsp_buffer_push(rsp, sens->states & 0xff); + if (IPMI_SENSOR_IS_DISCRETE(sens)) { + rsp_buffer_push(rsp, (sens->states >> 8) & 0xff); + } +} + +static void set_sensor_type(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + IPMISensor *sens; + + + if ((cmd[2] >= MAX_SENSORS) || + !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { + rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT); + return; + } + sens = ibs->sensors + cmd[2]; + sens->sensor_type = cmd[3]; + sens->evt_reading_type_code = cmd[4] & 0x7f; +} + +static void get_sensor_type(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + IPMISensor *sens; + + + if ((cmd[2] >= MAX_SENSORS) || + !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { + rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT); + return; + } + sens = ibs->sensors + cmd[2]; + rsp_buffer_push(rsp, sens->sensor_type); + rsp_buffer_push(rsp, sens->evt_reading_type_code); +} + + +static const IPMICmdHandler chassis_cmds[] = { + [IPMI_CMD_GET_CHASSIS_CAPABILITIES] = { chassis_capabilities }, + [IPMI_CMD_GET_CHASSIS_STATUS] = { chassis_status }, + [IPMI_CMD_CHASSIS_CONTROL] = { chassis_control, 3 }, + [IPMI_CMD_GET_SYS_RESTART_CAUSE] = { chassis_get_sys_restart_cause } +}; +static const IPMINetfn chassis_netfn = { + .cmd_nums = ARRAY_SIZE(chassis_cmds), + .cmd_handlers = chassis_cmds +}; + +static const IPMICmdHandler sensor_event_cmds[] = { + [IPMI_CMD_SET_SENSOR_EVT_ENABLE] = { set_sensor_evt_enable, 4 }, + [IPMI_CMD_GET_SENSOR_EVT_ENABLE] = { get_sensor_evt_enable, 3 }, + [IPMI_CMD_REARM_SENSOR_EVTS] = { rearm_sensor_evts, 4 }, + [IPMI_CMD_GET_SENSOR_EVT_STATUS] = { get_sensor_evt_status, 3 }, + [IPMI_CMD_GET_SENSOR_READING] = { get_sensor_reading, 3 }, + [IPMI_CMD_SET_SENSOR_TYPE] = { set_sensor_type, 5 }, + [IPMI_CMD_GET_SENSOR_TYPE] = { get_sensor_type, 3 }, +}; +static const IPMINetfn sensor_event_netfn = { + .cmd_nums = ARRAY_SIZE(sensor_event_cmds), + .cmd_handlers = sensor_event_cmds +}; + +static const IPMICmdHandler app_cmds[] = { + [IPMI_CMD_GET_DEVICE_ID] = { get_device_id }, + [IPMI_CMD_COLD_RESET] = { cold_reset }, + [IPMI_CMD_WARM_RESET] = { warm_reset }, + [IPMI_CMD_SET_ACPI_POWER_STATE] = { set_acpi_power_state, 4 }, + [IPMI_CMD_GET_ACPI_POWER_STATE] = { get_acpi_power_state }, + [IPMI_CMD_GET_DEVICE_GUID] = { get_device_guid }, + [IPMI_CMD_SET_BMC_GLOBAL_ENABLES] = { set_bmc_global_enables, 3 }, + [IPMI_CMD_GET_BMC_GLOBAL_ENABLES] = { get_bmc_global_enables }, + [IPMI_CMD_CLR_MSG_FLAGS] = { clr_msg_flags, 3 }, + [IPMI_CMD_GET_MSG_FLAGS] = { get_msg_flags }, + [IPMI_CMD_GET_MSG] = { get_msg }, + [IPMI_CMD_SEND_MSG] = { send_msg, 3 }, + [IPMI_CMD_READ_EVT_MSG_BUF] = { read_evt_msg_buf }, + [IPMI_CMD_RESET_WATCHDOG_TIMER] = { reset_watchdog_timer }, + [IPMI_CMD_SET_WATCHDOG_TIMER] = { set_watchdog_timer, 8 }, + [IPMI_CMD_GET_WATCHDOG_TIMER] = { get_watchdog_timer }, +}; +static const IPMINetfn app_netfn = { + .cmd_nums = ARRAY_SIZE(app_cmds), + .cmd_handlers = app_cmds +}; + +static const IPMICmdHandler storage_cmds[] = { + [IPMI_CMD_GET_SDR_REP_INFO] = { get_sdr_rep_info }, + [IPMI_CMD_RESERVE_SDR_REP] = { reserve_sdr_rep }, + [IPMI_CMD_GET_SDR] = { get_sdr, 8 }, + [IPMI_CMD_ADD_SDR] = { add_sdr }, + [IPMI_CMD_CLEAR_SDR_REP] = { clear_sdr_rep, 8 }, + [IPMI_CMD_GET_SEL_INFO] = { get_sel_info }, + [IPMI_CMD_RESERVE_SEL] = { reserve_sel }, + [IPMI_CMD_GET_SEL_ENTRY] = { get_sel_entry, 8 }, + [IPMI_CMD_ADD_SEL_ENTRY] = { add_sel_entry, 18 }, + [IPMI_CMD_CLEAR_SEL] = { clear_sel, 8 }, + [IPMI_CMD_GET_SEL_TIME] = { get_sel_time, 6 }, + [IPMI_CMD_SET_SEL_TIME] = { set_sel_time }, +}; + +static const IPMINetfn storage_netfn = { + .cmd_nums = ARRAY_SIZE(storage_cmds), + .cmd_handlers = storage_cmds +}; + +static void register_cmds(IPMIBmcSim *s) +{ + ipmi_register_netfn(s, IPMI_NETFN_CHASSIS, &chassis_netfn); + ipmi_register_netfn(s, IPMI_NETFN_SENSOR_EVENT, &sensor_event_netfn); + ipmi_register_netfn(s, IPMI_NETFN_APP, &app_netfn); + ipmi_register_netfn(s, IPMI_NETFN_STORAGE, &storage_netfn); +} + +static uint8_t init_sdrs[] = { + /* Watchdog device */ + 0x00, 0x00, 0x51, 0x02, 35, 0x20, 0x00, 0x00, + 0x23, 0x01, 0x63, 0x00, 0x23, 0x6f, 0x0f, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, + 'W', 'a', 't', 'c', 'h', 'd', 'o', 'g', +}; + +static void ipmi_sdr_init(IPMIBmcSim *ibs) +{ + unsigned int i; + int len; + size_t sdrs_size; + uint8_t *sdrs; + + sdrs_size = sizeof(init_sdrs); + sdrs = init_sdrs; + + for (i = 0; i < sdrs_size; i += len) { + struct ipmi_sdr_header *sdrh; + + if (i + IPMI_SDR_HEADER_SIZE > sdrs_size) { + error_report("Problem with recid 0x%4.4x", i); + return; + } + sdrh = (struct ipmi_sdr_header *) &sdrs[i]; + len = ipmi_sdr_length(sdrh); + if (i + len > sdrs_size) { + error_report("Problem with recid 0x%4.4x", i); + return; + } + sdr_add_entry(ibs, sdrh, len, NULL); + } +} + +static const VMStateDescription vmstate_ipmi_sim = { + .name = TYPE_IPMI_BMC_SIMULATOR, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(bmc_global_enables, IPMIBmcSim), + VMSTATE_UINT8(msg_flags, IPMIBmcSim), + VMSTATE_BOOL(watchdog_initialized, IPMIBmcSim), + VMSTATE_UINT8(watchdog_use, IPMIBmcSim), + VMSTATE_UINT8(watchdog_action, IPMIBmcSim), + VMSTATE_UINT8(watchdog_pretimeout, IPMIBmcSim), + VMSTATE_BOOL(watchdog_expired, IPMIBmcSim), + VMSTATE_UINT16(watchdog_timeout, IPMIBmcSim), + VMSTATE_BOOL(watchdog_running, IPMIBmcSim), + VMSTATE_BOOL(watchdog_preaction_ran, IPMIBmcSim), + VMSTATE_INT64(watchdog_expiry, IPMIBmcSim), + VMSTATE_UINT8_ARRAY(evtbuf, IPMIBmcSim, 16), + VMSTATE_UINT8(sensors[IPMI_WATCHDOG_SENSOR].status, IPMIBmcSim), + VMSTATE_UINT8(sensors[IPMI_WATCHDOG_SENSOR].reading, IPMIBmcSim), + VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].states, IPMIBmcSim), + VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].assert_states, IPMIBmcSim), + VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].deassert_states, + IPMIBmcSim), + VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].assert_enable, IPMIBmcSim), + VMSTATE_END_OF_LIST() + } +}; + +static void ipmi_sim_realize(DeviceState *dev, Error **errp) +{ + IPMIBmc *b = IPMI_BMC(dev); + unsigned int i; + IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b); + + qemu_mutex_init(&ibs->lock); + QTAILQ_INIT(&ibs->rcvbufs); + + ibs->bmc_global_enables = (1 << IPMI_BMC_EVENT_LOG_BIT); + ibs->device_id = 0x20; + ibs->ipmi_version = 0x02; /* IPMI 2.0 */ + ibs->restart_cause = 0; + for (i = 0; i < 4; i++) { + ibs->sel.last_addition[i] = 0xff; + ibs->sel.last_clear[i] = 0xff; + ibs->sdr.last_addition[i] = 0xff; + ibs->sdr.last_clear[i] = 0xff; + } + + ipmi_sdr_init(ibs); + + ibs->acpi_power_state[0] = 0; + ibs->acpi_power_state[1] = 0; + + if (qemu_uuid_set) { + memcpy(&ibs->uuid, qemu_uuid, 16); + } else { + memset(&ibs->uuid, 0, 16); + } + + ipmi_init_sensors_from_sdrs(ibs); + register_cmds(ibs); + + ibs->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ipmi_timeout, ibs); + + vmstate_register(NULL, 0, &vmstate_ipmi_sim, ibs); +} + +static void ipmi_sim_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + IPMIBmcClass *bk = IPMI_BMC_CLASS(oc); + + dc->realize = ipmi_sim_realize; + bk->handle_command = ipmi_sim_handle_command; +} + +static const TypeInfo ipmi_sim_type = { + .name = TYPE_IPMI_BMC_SIMULATOR, + .parent = TYPE_IPMI_BMC, + .instance_size = sizeof(IPMIBmcSim), + .class_init = ipmi_sim_class_init, +}; + +static void ipmi_sim_register_types(void) +{ + type_register_static(&ipmi_sim_type); +} + +type_init(ipmi_sim_register_types) diff --git a/qemu/hw/ipmi/isa_ipmi_bt.c b/qemu/hw/ipmi/isa_ipmi_bt.c new file mode 100644 index 000000000..aaea12ecd --- /dev/null +++ b/qemu/hw/ipmi/isa_ipmi_bt.c @@ -0,0 +1,530 @@ +/* + * QEMU ISA IPMI BT emulation + * + * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/ipmi/ipmi.h" +#include "hw/isa/isa.h" +#include "hw/i386/pc.h" + +/* Control register */ +#define IPMI_BT_CLR_WR_BIT 0 +#define IPMI_BT_CLR_RD_BIT 1 +#define IPMI_BT_H2B_ATN_BIT 2 +#define IPMI_BT_B2H_ATN_BIT 3 +#define IPMI_BT_SMS_ATN_BIT 4 +#define IPMI_BT_HBUSY_BIT 6 +#define IPMI_BT_BBUSY_BIT 7 + +#define IPMI_BT_CLR_WR_MASK (1 << IPMI_BT_CLR_WR_BIT) +#define IPMI_BT_GET_CLR_WR(d) (((d) >> IPMI_BT_CLR_WR_BIT) & 0x1) +#define IPMI_BT_SET_CLR_WR(d, v) (d) = (((d) & ~IPMI_BT_CLR_WR_MASK) | \ + (((v & 1) << IPMI_BT_CLR_WR_BIT))) + +#define IPMI_BT_CLR_RD_MASK (1 << IPMI_BT_CLR_RD_BIT) +#define IPMI_BT_GET_CLR_RD(d) (((d) >> IPMI_BT_CLR_RD_BIT) & 0x1) +#define IPMI_BT_SET_CLR_RD(d, v) (d) = (((d) & ~IPMI_BT_CLR_RD_MASK) | \ + (((v & 1) << IPMI_BT_CLR_RD_BIT))) + +#define IPMI_BT_H2B_ATN_MASK (1 << IPMI_BT_H2B_ATN_BIT) +#define IPMI_BT_GET_H2B_ATN(d) (((d) >> IPMI_BT_H2B_ATN_BIT) & 0x1) +#define IPMI_BT_SET_H2B_ATN(d, v) (d) = (((d) & ~IPMI_BT_H2B_ATN_MASK) | \ + (((v & 1) << IPMI_BT_H2B_ATN_BIT))) + +#define IPMI_BT_B2H_ATN_MASK (1 << IPMI_BT_B2H_ATN_BIT) +#define IPMI_BT_GET_B2H_ATN(d) (((d) >> IPMI_BT_B2H_ATN_BIT) & 0x1) +#define IPMI_BT_SET_B2H_ATN(d, v) (d) = (((d) & ~IPMI_BT_B2H_ATN_MASK) | \ + (((v & 1) << IPMI_BT_B2H_ATN_BIT))) + +#define IPMI_BT_SMS_ATN_MASK (1 << IPMI_BT_SMS_ATN_BIT) +#define IPMI_BT_GET_SMS_ATN(d) (((d) >> IPMI_BT_SMS_ATN_BIT) & 0x1) +#define IPMI_BT_SET_SMS_ATN(d, v) (d) = (((d) & ~IPMI_BT_SMS_ATN_MASK) | \ + (((v & 1) << IPMI_BT_SMS_ATN_BIT))) + +#define IPMI_BT_HBUSY_MASK (1 << IPMI_BT_HBUSY_BIT) +#define IPMI_BT_GET_HBUSY(d) (((d) >> IPMI_BT_HBUSY_BIT) & 0x1) +#define IPMI_BT_SET_HBUSY(d, v) (d) = (((d) & ~IPMI_BT_HBUSY_MASK) | \ + (((v & 1) << IPMI_BT_HBUSY_BIT))) + +#define IPMI_BT_BBUSY_MASK (1 << IPMI_BT_BBUSY_BIT) +#define IPMI_BT_GET_BBUSY(d) (((d) >> IPMI_BT_BBUSY_BIT) & 0x1) +#define IPMI_BT_SET_BBUSY(d, v) (d) = (((d) & ~IPMI_BT_BBUSY_MASK) | \ + (((v & 1) << IPMI_BT_BBUSY_BIT))) + + +/* Mask register */ +#define IPMI_BT_B2H_IRQ_EN_BIT 0 +#define IPMI_BT_B2H_IRQ_BIT 1 + +#define IPMI_BT_B2H_IRQ_EN_MASK (1 << IPMI_BT_B2H_IRQ_EN_BIT) +#define IPMI_BT_GET_B2H_IRQ_EN(d) (((d) >> IPMI_BT_B2H_IRQ_EN_BIT) & 0x1) +#define IPMI_BT_SET_B2H_IRQ_EN(d, v) (d) = (((d) & ~IPMI_BT_B2H_IRQ_EN_MASK) | \ + (((v & 1) << IPMI_BT_B2H_IRQ_EN_BIT))) + +#define IPMI_BT_B2H_IRQ_MASK (1 << IPMI_BT_B2H_IRQ_BIT) +#define IPMI_BT_GET_B2H_IRQ(d) (((d) >> IPMI_BT_B2H_IRQ_BIT) & 0x1) +#define IPMI_BT_SET_B2H_IRQ(d, v) (d) = (((d) & ~IPMI_BT_B2H_IRQ_MASK) | \ + (((v & 1) << IPMI_BT_B2H_IRQ_BIT))) + +typedef struct IPMIBT { + IPMIBmc *bmc; + + bool do_wake; + + qemu_irq irq; + + uint32_t io_base; + unsigned long io_length; + MemoryRegion io; + + bool obf_irq_set; + bool atn_irq_set; + bool use_irq; + bool irqs_enabled; + + uint8_t outmsg[MAX_IPMI_MSG_SIZE]; + uint32_t outpos; + uint32_t outlen; + + uint8_t inmsg[MAX_IPMI_MSG_SIZE]; + uint32_t inlen; + + uint8_t control_reg; + uint8_t mask_reg; + + /* + * This is a response number that we send with the command to make + * sure that the response matches the command. + */ + uint8_t waiting_rsp; + uint8_t waiting_seq; +} IPMIBT; + +#define IPMI_CMD_GET_BT_INTF_CAP 0x36 + +static void ipmi_bt_handle_event(IPMIInterface *ii) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + if (ib->inlen < 4) { + goto out; + } + /* Note that overruns are handled by handle_command */ + if (ib->inmsg[0] != (ib->inlen - 1)) { + /* Length mismatch, just ignore. */ + IPMI_BT_SET_BBUSY(ib->control_reg, 1); + ib->inlen = 0; + goto out; + } + if ((ib->inmsg[1] == (IPMI_NETFN_APP << 2)) && + (ib->inmsg[3] == IPMI_CMD_GET_BT_INTF_CAP)) { + /* We handle this one ourselves. */ + ib->outmsg[0] = 9; + ib->outmsg[1] = ib->inmsg[1] | 0x04; + ib->outmsg[2] = ib->inmsg[2]; + ib->outmsg[3] = ib->inmsg[3]; + ib->outmsg[4] = 0; + ib->outmsg[5] = 1; /* Only support 1 outstanding request. */ + if (sizeof(ib->inmsg) > 0xff) { /* Input buffer size */ + ib->outmsg[6] = 0xff; + } else { + ib->outmsg[6] = (unsigned char) sizeof(ib->inmsg); + } + if (sizeof(ib->outmsg) > 0xff) { /* Output buffer size */ + ib->outmsg[7] = 0xff; + } else { + ib->outmsg[7] = (unsigned char) sizeof(ib->outmsg); + } + ib->outmsg[8] = 10; /* Max request to response time */ + ib->outmsg[9] = 0; /* Don't recommend retries */ + ib->outlen = 10; + IPMI_BT_SET_BBUSY(ib->control_reg, 0); + IPMI_BT_SET_B2H_ATN(ib->control_reg, 1); + if (ib->use_irq && ib->irqs_enabled && + !IPMI_BT_GET_B2H_IRQ(ib->mask_reg) && + IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); + qemu_irq_raise(ib->irq); + } + goto out; + } + ib->waiting_seq = ib->inmsg[2]; + ib->inmsg[2] = ib->inmsg[1]; + { + IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ib->bmc); + bk->handle_command(ib->bmc, ib->inmsg + 2, ib->inlen - 2, + sizeof(ib->inmsg), ib->waiting_rsp); + } + out: + return; +} + +static void ipmi_bt_handle_rsp(IPMIInterface *ii, uint8_t msg_id, + unsigned char *rsp, unsigned int rsp_len) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + if (ib->waiting_rsp == msg_id) { + ib->waiting_rsp++; + if (rsp_len > (sizeof(ib->outmsg) - 2)) { + ib->outmsg[0] = 4; + ib->outmsg[1] = rsp[0]; + ib->outmsg[2] = ib->waiting_seq; + ib->outmsg[3] = rsp[1]; + ib->outmsg[4] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES; + ib->outlen = 5; + } else { + ib->outmsg[0] = rsp_len + 1; + ib->outmsg[1] = rsp[0]; + ib->outmsg[2] = ib->waiting_seq; + memcpy(ib->outmsg + 3, rsp + 1, rsp_len - 1); + ib->outlen = rsp_len + 2; + } + IPMI_BT_SET_BBUSY(ib->control_reg, 0); + IPMI_BT_SET_B2H_ATN(ib->control_reg, 1); + if (ib->use_irq && ib->irqs_enabled && + !IPMI_BT_GET_B2H_IRQ(ib->mask_reg) && + IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); + qemu_irq_raise(ib->irq); + } + } +} + + +static uint64_t ipmi_bt_ioport_read(void *opaque, hwaddr addr, unsigned size) +{ + IPMIInterface *ii = opaque; + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + uint32_t ret = 0xff; + + switch (addr & 3) { + case 0: + ret = ib->control_reg; + break; + case 1: + if (ib->outpos < ib->outlen) { + ret = ib->outmsg[ib->outpos]; + ib->outpos++; + if (ib->outpos == ib->outlen) { + ib->outpos = 0; + ib->outlen = 0; + } + } else { + ret = 0xff; + } + break; + case 2: + ret = ib->mask_reg; + break; + } + return ret; +} + +static void ipmi_bt_signal(IPMIBT *ib, IPMIInterface *ii) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + + ib->do_wake = 1; + while (ib->do_wake) { + ib->do_wake = 0; + iic->handle_if_event(ii); + } +} + +static void ipmi_bt_ioport_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + IPMIInterface *ii = opaque; + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + switch (addr & 3) { + case 0: + if (IPMI_BT_GET_CLR_WR(val)) { + ib->inlen = 0; + } + if (IPMI_BT_GET_CLR_RD(val)) { + ib->outpos = 0; + } + if (IPMI_BT_GET_B2H_ATN(val)) { + IPMI_BT_SET_B2H_ATN(ib->control_reg, 0); + } + if (IPMI_BT_GET_SMS_ATN(val)) { + IPMI_BT_SET_SMS_ATN(ib->control_reg, 0); + } + if (IPMI_BT_GET_HBUSY(val)) { + /* Toggle */ + IPMI_BT_SET_HBUSY(ib->control_reg, + !IPMI_BT_GET_HBUSY(ib->control_reg)); + } + if (IPMI_BT_GET_H2B_ATN(val)) { + IPMI_BT_SET_BBUSY(ib->control_reg, 1); + ipmi_bt_signal(ib, ii); + } + break; + + case 1: + if (ib->inlen < sizeof(ib->inmsg)) { + ib->inmsg[ib->inlen] = val; + } + ib->inlen++; + break; + + case 2: + if (IPMI_BT_GET_B2H_IRQ_EN(val) != + IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { + if (IPMI_BT_GET_B2H_IRQ_EN(val)) { + if (IPMI_BT_GET_B2H_ATN(ib->control_reg) || + IPMI_BT_GET_SMS_ATN(ib->control_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); + qemu_irq_raise(ib->irq); + } + IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 1); + } else { + if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); + qemu_irq_lower(ib->irq); + } + IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0); + } + } + if (IPMI_BT_GET_B2H_IRQ(val) && IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); + qemu_irq_lower(ib->irq); + } + break; + } +} + +static const MemoryRegionOps ipmi_bt_io_ops = { + .read = ipmi_bt_ioport_read, + .write = ipmi_bt_ioport_write, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void ipmi_bt_set_atn(IPMIInterface *ii, int val, int irq) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + if (!!val == IPMI_BT_GET_SMS_ATN(ib->control_reg)) { + return; + } + + IPMI_BT_SET_SMS_ATN(ib->control_reg, val); + if (val) { + if (irq && ib->use_irq && ib->irqs_enabled && + !IPMI_BT_GET_B2H_ATN(ib->control_reg) && + IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); + qemu_irq_raise(ib->irq); + } + } else { + if (!IPMI_BT_GET_B2H_ATN(ib->control_reg) && + IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); + qemu_irq_lower(ib->irq); + } + } +} + +static void ipmi_bt_handle_reset(IPMIInterface *ii, bool is_cold) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + if (is_cold) { + /* Disable the BT interrupt on reset */ + if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); + qemu_irq_lower(ib->irq); + } + IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0); + } +} + +static void ipmi_bt_set_irq_enable(IPMIInterface *ii, int val) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + ib->irqs_enabled = val; +} + +static void ipmi_bt_init(IPMIInterface *ii, Error **errp) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + ib->io_length = 3; + + memory_region_init_io(&ib->io, NULL, &ipmi_bt_io_ops, ii, "ipmi-bt", 3); +} + +static void ipmi_bt_class_init(IPMIInterfaceClass *iic) +{ + iic->init = ipmi_bt_init; + iic->set_atn = ipmi_bt_set_atn; + iic->handle_rsp = ipmi_bt_handle_rsp; + iic->handle_if_event = ipmi_bt_handle_event; + iic->set_irq_enable = ipmi_bt_set_irq_enable; + iic->reset = ipmi_bt_handle_reset; +} + + +#define TYPE_ISA_IPMI_BT "isa-ipmi-bt" +#define ISA_IPMI_BT(obj) OBJECT_CHECK(ISAIPMIBTDevice, (obj), \ + TYPE_ISA_IPMI_BT) + +typedef struct ISAIPMIBTDevice { + ISADevice dev; + int32_t isairq; + IPMIBT bt; + IPMIFwInfo fwinfo; +} ISAIPMIBTDevice; + +static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) +{ + ISADevice *isadev = ISA_DEVICE(dev); + ISAIPMIBTDevice *iib = ISA_IPMI_BT(dev); + IPMIInterface *ii = IPMI_INTERFACE(dev); + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + + if (!iib->bt.bmc) { + error_setg(errp, "IPMI device requires a bmc attribute to be set"); + return; + } + + iib->bt.bmc->intf = ii; + + iic->init(ii, errp); + if (*errp) + return; + + if (iib->isairq > 0) { + isa_init_irq(isadev, &iib->bt.irq, iib->isairq); + iib->bt.use_irq = 1; + } + + qdev_set_legacy_instance_id(dev, iib->bt.io_base, iib->bt.io_length); + + isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base); + + iib->fwinfo.interface_name = "bt"; + iib->fwinfo.interface_type = IPMI_SMBIOS_BT; + iib->fwinfo.ipmi_spec_major_revision = 2; + iib->fwinfo.ipmi_spec_minor_revision = 0; + iib->fwinfo.base_address = iib->bt.io_base; + iib->fwinfo.register_length = iib->bt.io_length; + iib->fwinfo.register_spacing = 1; + iib->fwinfo.memspace = IPMI_MEMSPACE_IO; + iib->fwinfo.irq_type = IPMI_LEVEL_IRQ; + iib->fwinfo.interrupt_number = iib->isairq; + iib->fwinfo.acpi_parent = "\\_SB.PCI0.ISA"; + iib->fwinfo.i2c_slave_address = iib->bt.bmc->slave_addr; + ipmi_add_fwinfo(&iib->fwinfo, errp); +} + +static const VMStateDescription vmstate_ISAIPMIBTDevice = { + .name = TYPE_IPMI_INTERFACE, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL(bt.obf_irq_set, ISAIPMIBTDevice), + VMSTATE_BOOL(bt.atn_irq_set, ISAIPMIBTDevice), + VMSTATE_BOOL(bt.use_irq, ISAIPMIBTDevice), + VMSTATE_BOOL(bt.irqs_enabled, ISAIPMIBTDevice), + VMSTATE_UINT32(bt.outpos, ISAIPMIBTDevice), + VMSTATE_VBUFFER_UINT32(bt.outmsg, ISAIPMIBTDevice, 1, NULL, 0, + bt.outlen), + VMSTATE_VBUFFER_UINT32(bt.inmsg, ISAIPMIBTDevice, 1, NULL, 0, + bt.inlen), + VMSTATE_UINT8(bt.control_reg, ISAIPMIBTDevice), + VMSTATE_UINT8(bt.mask_reg, ISAIPMIBTDevice), + VMSTATE_UINT8(bt.waiting_rsp, ISAIPMIBTDevice), + VMSTATE_UINT8(bt.waiting_seq, ISAIPMIBTDevice), + VMSTATE_END_OF_LIST() + } +}; + +static void isa_ipmi_bt_init(Object *obj) +{ + ISAIPMIBTDevice *iib = ISA_IPMI_BT(obj); + + ipmi_bmc_find_and_link(obj, (Object **) &iib->bt.bmc); + + vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, iib); +} + +static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii) +{ + ISAIPMIBTDevice *iib = ISA_IPMI_BT(ii); + + return &iib->bt; +} + +static Property ipmi_isa_properties[] = { + DEFINE_PROP_UINT32("ioport", ISAIPMIBTDevice, bt.io_base, 0xe4), + DEFINE_PROP_INT32("irq", ISAIPMIBTDevice, isairq, 5), + DEFINE_PROP_END_OF_LIST(), +}; + +static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); + + dc->realize = isa_ipmi_bt_realize; + dc->props = ipmi_isa_properties; + + iic->get_backend_data = isa_ipmi_bt_get_backend_data; + ipmi_bt_class_init(iic); +} + +static const TypeInfo isa_ipmi_bt_info = { + .name = TYPE_ISA_IPMI_BT, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(ISAIPMIBTDevice), + .instance_init = isa_ipmi_bt_init, + .class_init = isa_ipmi_bt_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_IPMI_INTERFACE }, + { } + } +}; + +static void ipmi_register_types(void) +{ + type_register_static(&isa_ipmi_bt_info); +} + +type_init(ipmi_register_types) diff --git a/qemu/hw/ipmi/isa_ipmi_kcs.c b/qemu/hw/ipmi/isa_ipmi_kcs.c new file mode 100644 index 000000000..2742ce06c --- /dev/null +++ b/qemu/hw/ipmi/isa_ipmi_kcs.c @@ -0,0 +1,495 @@ +/* + * QEMU ISA IPMI KCS emulation + * + * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/ipmi/ipmi.h" +#include "hw/isa/isa.h" +#include "hw/i386/pc.h" + +#define IPMI_KCS_OBF_BIT 0 +#define IPMI_KCS_IBF_BIT 1 +#define IPMI_KCS_SMS_ATN_BIT 2 +#define IPMI_KCS_CD_BIT 3 + +#define IPMI_KCS_OBF_MASK (1 << IPMI_KCS_OBF_BIT) +#define IPMI_KCS_GET_OBF(d) (((d) >> IPMI_KCS_OBF_BIT) & 0x1) +#define IPMI_KCS_SET_OBF(d, v) (d) = (((d) & ~IPMI_KCS_OBF_MASK) | \ + (((v) & 1) << IPMI_KCS_OBF_BIT)) +#define IPMI_KCS_IBF_MASK (1 << IPMI_KCS_IBF_BIT) +#define IPMI_KCS_GET_IBF(d) (((d) >> IPMI_KCS_IBF_BIT) & 0x1) +#define IPMI_KCS_SET_IBF(d, v) (d) = (((d) & ~IPMI_KCS_IBF_MASK) | \ + (((v) & 1) << IPMI_KCS_IBF_BIT)) +#define IPMI_KCS_SMS_ATN_MASK (1 << IPMI_KCS_SMS_ATN_BIT) +#define IPMI_KCS_GET_SMS_ATN(d) (((d) >> IPMI_KCS_SMS_ATN_BIT) & 0x1) +#define IPMI_KCS_SET_SMS_ATN(d, v) (d) = (((d) & ~IPMI_KCS_SMS_ATN_MASK) | \ + (((v) & 1) << IPMI_KCS_SMS_ATN_BIT)) +#define IPMI_KCS_CD_MASK (1 << IPMI_KCS_CD_BIT) +#define IPMI_KCS_GET_CD(d) (((d) >> IPMI_KCS_CD_BIT) & 0x1) +#define IPMI_KCS_SET_CD(d, v) (d) = (((d) & ~IPMI_KCS_CD_MASK) | \ + (((v) & 1) << IPMI_KCS_CD_BIT)) + +#define IPMI_KCS_IDLE_STATE 0 +#define IPMI_KCS_READ_STATE 1 +#define IPMI_KCS_WRITE_STATE 2 +#define IPMI_KCS_ERROR_STATE 3 + +#define IPMI_KCS_GET_STATE(d) (((d) >> 6) & 0x3) +#define IPMI_KCS_SET_STATE(d, v) ((d) = ((d) & ~0xc0) | (((v) & 0x3) << 6)) + +#define IPMI_KCS_ABORT_STATUS_CMD 0x60 +#define IPMI_KCS_WRITE_START_CMD 0x61 +#define IPMI_KCS_WRITE_END_CMD 0x62 +#define IPMI_KCS_READ_CMD 0x68 + +#define IPMI_KCS_STATUS_NO_ERR 0x00 +#define IPMI_KCS_STATUS_ABORTED_ERR 0x01 +#define IPMI_KCS_STATUS_BAD_CC_ERR 0x02 +#define IPMI_KCS_STATUS_LENGTH_ERR 0x06 + +typedef struct IPMIKCS { + IPMIBmc *bmc; + + bool do_wake; + + qemu_irq irq; + + uint32_t io_base; + unsigned long io_length; + MemoryRegion io; + + bool obf_irq_set; + bool atn_irq_set; + bool use_irq; + bool irqs_enabled; + + uint8_t outmsg[MAX_IPMI_MSG_SIZE]; + uint32_t outpos; + uint32_t outlen; + + uint8_t inmsg[MAX_IPMI_MSG_SIZE]; + uint32_t inlen; + bool write_end; + + uint8_t status_reg; + uint8_t data_out_reg; + + int16_t data_in_reg; /* -1 means not written */ + int16_t cmd_reg; + + /* + * This is a response number that we send with the command to make + * sure that the response matches the command. + */ + uint8_t waiting_rsp; +} IPMIKCS; + +#define SET_OBF() \ + do { \ + IPMI_KCS_SET_OBF(ik->status_reg, 1); \ + if (ik->use_irq && ik->irqs_enabled && !ik->obf_irq_set) { \ + ik->obf_irq_set = 1; \ + if (!ik->atn_irq_set) { \ + qemu_irq_raise(ik->irq); \ + } \ + } \ + } while (0) + +static void ipmi_kcs_signal(IPMIKCS *ik, IPMIInterface *ii) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + + ik->do_wake = 1; + while (ik->do_wake) { + ik->do_wake = 0; + iic->handle_if_event(ii); + } +} + +static void ipmi_kcs_handle_event(IPMIInterface *ii) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIKCS *ik = iic->get_backend_data(ii); + + if (ik->cmd_reg == IPMI_KCS_ABORT_STATUS_CMD) { + if (IPMI_KCS_GET_STATE(ik->status_reg) != IPMI_KCS_ERROR_STATE) { + ik->waiting_rsp++; /* Invalidate the message */ + ik->outmsg[0] = IPMI_KCS_STATUS_ABORTED_ERR; + ik->outlen = 1; + ik->outpos = 0; + IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE); + SET_OBF(); + } + goto out; + } + + switch (IPMI_KCS_GET_STATE(ik->status_reg)) { + case IPMI_KCS_IDLE_STATE: + if (ik->cmd_reg == IPMI_KCS_WRITE_START_CMD) { + IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_WRITE_STATE); + ik->cmd_reg = -1; + ik->write_end = 0; + ik->inlen = 0; + SET_OBF(); + } + break; + + case IPMI_KCS_READ_STATE: + handle_read: + if (ik->outpos >= ik->outlen) { + IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_IDLE_STATE); + SET_OBF(); + } else if (ik->data_in_reg == IPMI_KCS_READ_CMD) { + ik->data_out_reg = ik->outmsg[ik->outpos]; + ik->outpos++; + SET_OBF(); + } else { + ik->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR; + ik->outlen = 1; + ik->outpos = 0; + IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE); + SET_OBF(); + goto out; + } + break; + + case IPMI_KCS_WRITE_STATE: + if (ik->data_in_reg != -1) { + /* + * Don't worry about input overrun here, that will be + * handled in the BMC. + */ + if (ik->inlen < sizeof(ik->inmsg)) { + ik->inmsg[ik->inlen] = ik->data_in_reg; + } + ik->inlen++; + } + if (ik->write_end) { + IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ik->bmc); + ik->outlen = 0; + ik->write_end = 0; + ik->outpos = 0; + bk->handle_command(ik->bmc, ik->inmsg, ik->inlen, sizeof(ik->inmsg), + ik->waiting_rsp); + goto out_noibf; + } else if (ik->cmd_reg == IPMI_KCS_WRITE_END_CMD) { + ik->cmd_reg = -1; + ik->write_end = 1; + } + SET_OBF(); + break; + + case IPMI_KCS_ERROR_STATE: + if (ik->data_in_reg != -1) { + IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_READ_STATE); + ik->data_in_reg = IPMI_KCS_READ_CMD; + goto handle_read; + } + break; + } + + if (ik->cmd_reg != -1) { + /* Got an invalid command */ + ik->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR; + ik->outlen = 1; + ik->outpos = 0; + IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE); + } + + out: + ik->cmd_reg = -1; + ik->data_in_reg = -1; + IPMI_KCS_SET_IBF(ik->status_reg, 0); + out_noibf: + return; +} + +static void ipmi_kcs_handle_rsp(IPMIInterface *ii, uint8_t msg_id, + unsigned char *rsp, unsigned int rsp_len) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIKCS *ik = iic->get_backend_data(ii); + + if (ik->waiting_rsp == msg_id) { + ik->waiting_rsp++; + if (rsp_len > sizeof(ik->outmsg)) { + ik->outmsg[0] = rsp[0]; + ik->outmsg[1] = rsp[1]; + ik->outmsg[2] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES; + ik->outlen = 3; + } else { + memcpy(ik->outmsg, rsp, rsp_len); + ik->outlen = rsp_len; + } + IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_READ_STATE); + ik->data_in_reg = IPMI_KCS_READ_CMD; + ipmi_kcs_signal(ik, ii); + } +} + + +static uint64_t ipmi_kcs_ioport_read(void *opaque, hwaddr addr, unsigned size) +{ + IPMIInterface *ii = opaque; + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIKCS *ik = iic->get_backend_data(ii); + uint32_t ret; + + switch (addr & 1) { + case 0: + ret = ik->data_out_reg; + IPMI_KCS_SET_OBF(ik->status_reg, 0); + if (ik->obf_irq_set) { + ik->obf_irq_set = 0; + if (!ik->atn_irq_set) { + qemu_irq_lower(ik->irq); + } + } + break; + case 1: + ret = ik->status_reg; + if (ik->atn_irq_set) { + ik->atn_irq_set = 0; + if (!ik->obf_irq_set) { + qemu_irq_lower(ik->irq); + } + } + break; + } + return ret; +} + +static void ipmi_kcs_ioport_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + IPMIInterface *ii = opaque; + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIKCS *ik = iic->get_backend_data(ii); + + if (IPMI_KCS_GET_IBF(ik->status_reg)) { + return; + } + + switch (addr & 1) { + case 0: + ik->data_in_reg = val; + break; + + case 1: + ik->cmd_reg = val; + break; + } + IPMI_KCS_SET_IBF(ik->status_reg, 1); + ipmi_kcs_signal(ik, ii); +} + +const MemoryRegionOps ipmi_kcs_io_ops = { + .read = ipmi_kcs_ioport_read, + .write = ipmi_kcs_ioport_write, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void ipmi_kcs_set_atn(IPMIInterface *ii, int val, int irq) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIKCS *ik = iic->get_backend_data(ii); + + IPMI_KCS_SET_SMS_ATN(ik->status_reg, val); + if (val) { + if (irq && !ik->atn_irq_set && ik->use_irq && ik->irqs_enabled) { + ik->atn_irq_set = 1; + if (!ik->obf_irq_set) { + qemu_irq_raise(ik->irq); + } + } + } else { + if (ik->atn_irq_set) { + ik->atn_irq_set = 0; + if (!ik->obf_irq_set) { + qemu_irq_lower(ik->irq); + } + } + } +} + +static void ipmi_kcs_set_irq_enable(IPMIInterface *ii, int val) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIKCS *ik = iic->get_backend_data(ii); + + ik->irqs_enabled = val; +} + +static void ipmi_kcs_init(IPMIInterface *ii, Error **errp) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIKCS *ik = iic->get_backend_data(ii); + + ik->io_length = 2; + memory_region_init_io(&ik->io, NULL, &ipmi_kcs_io_ops, ii, "ipmi-kcs", 2); +} + +static void ipmi_kcs_class_init(IPMIInterfaceClass *iic) +{ + iic->init = ipmi_kcs_init; + iic->set_atn = ipmi_kcs_set_atn; + iic->handle_rsp = ipmi_kcs_handle_rsp; + iic->handle_if_event = ipmi_kcs_handle_event; + iic->set_irq_enable = ipmi_kcs_set_irq_enable; +} + + +#define TYPE_ISA_IPMI_KCS "isa-ipmi-kcs" +#define ISA_IPMI_KCS(obj) OBJECT_CHECK(ISAIPMIKCSDevice, (obj), \ + TYPE_ISA_IPMI_KCS) + +typedef struct ISAIPMIKCSDevice { + ISADevice dev; + int32_t isairq; + IPMIKCS kcs; + IPMIFwInfo fwinfo; +} ISAIPMIKCSDevice; + +static void ipmi_isa_realize(DeviceState *dev, Error **errp) +{ + ISADevice *isadev = ISA_DEVICE(dev); + ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(dev); + IPMIInterface *ii = IPMI_INTERFACE(dev); + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + + if (!iik->kcs.bmc) { + error_setg(errp, "IPMI device requires a bmc attribute to be set"); + return; + } + + iik->kcs.bmc->intf = ii; + + iic->init(ii, errp); + if (*errp) + return; + + if (iik->isairq > 0) { + isa_init_irq(isadev, &iik->kcs.irq, iik->isairq); + iik->kcs.use_irq = 1; + } + + qdev_set_legacy_instance_id(dev, iik->kcs.io_base, iik->kcs.io_length); + + isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base); + + iik->fwinfo.interface_name = "kcs"; + iik->fwinfo.interface_type = IPMI_SMBIOS_KCS; + iik->fwinfo.ipmi_spec_major_revision = 2; + iik->fwinfo.ipmi_spec_minor_revision = 0; + iik->fwinfo.base_address = iik->kcs.io_base; + iik->fwinfo.i2c_slave_address = iik->kcs.bmc->slave_addr; + iik->fwinfo.register_length = iik->kcs.io_length; + iik->fwinfo.register_spacing = 1; + iik->fwinfo.memspace = IPMI_MEMSPACE_IO; + iik->fwinfo.irq_type = IPMI_LEVEL_IRQ; + iik->fwinfo.interrupt_number = iik->isairq; + iik->fwinfo.acpi_parent = "\\_SB.PCI0.ISA"; + ipmi_add_fwinfo(&iik->fwinfo, errp); +} + +const VMStateDescription vmstate_ISAIPMIKCSDevice = { + .name = TYPE_IPMI_INTERFACE, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL(kcs.obf_irq_set, ISAIPMIKCSDevice), + VMSTATE_BOOL(kcs.atn_irq_set, ISAIPMIKCSDevice), + VMSTATE_BOOL(kcs.use_irq, ISAIPMIKCSDevice), + VMSTATE_BOOL(kcs.irqs_enabled, ISAIPMIKCSDevice), + VMSTATE_UINT32(kcs.outpos, ISAIPMIKCSDevice), + VMSTATE_VBUFFER_UINT32(kcs.outmsg, ISAIPMIKCSDevice, 1, NULL, 0, + kcs.outlen), + VMSTATE_VBUFFER_UINT32(kcs.inmsg, ISAIPMIKCSDevice, 1, NULL, 0, + kcs.inlen), + VMSTATE_BOOL(kcs.write_end, ISAIPMIKCSDevice), + VMSTATE_UINT8(kcs.status_reg, ISAIPMIKCSDevice), + VMSTATE_UINT8(kcs.data_out_reg, ISAIPMIKCSDevice), + VMSTATE_INT16(kcs.data_in_reg, ISAIPMIKCSDevice), + VMSTATE_INT16(kcs.cmd_reg, ISAIPMIKCSDevice), + VMSTATE_UINT8(kcs.waiting_rsp, ISAIPMIKCSDevice), + VMSTATE_END_OF_LIST() + } +}; + +static void isa_ipmi_kcs_init(Object *obj) +{ + ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(obj); + + ipmi_bmc_find_and_link(obj, (Object **) &iik->kcs.bmc); + + vmstate_register(NULL, 0, &vmstate_ISAIPMIKCSDevice, iik); +} + +static void *isa_ipmi_kcs_get_backend_data(IPMIInterface *ii) +{ + ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(ii); + + return &iik->kcs; +} + +static Property ipmi_isa_properties[] = { + DEFINE_PROP_UINT32("ioport", ISAIPMIKCSDevice, kcs.io_base, 0xca2), + DEFINE_PROP_INT32("irq", ISAIPMIKCSDevice, isairq, 5), + DEFINE_PROP_END_OF_LIST(), +}; + +static void isa_ipmi_kcs_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); + + dc->realize = ipmi_isa_realize; + dc->props = ipmi_isa_properties; + + iic->get_backend_data = isa_ipmi_kcs_get_backend_data; + ipmi_kcs_class_init(iic); +} + +static const TypeInfo isa_ipmi_kcs_info = { + .name = TYPE_ISA_IPMI_KCS, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(ISAIPMIKCSDevice), + .instance_init = isa_ipmi_kcs_init, + .class_init = isa_ipmi_kcs_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_IPMI_INTERFACE }, + { } + } +}; + +static void ipmi_register_types(void) +{ + type_register_static(&isa_ipmi_kcs_info); +} + +type_init(ipmi_register_types) diff --git a/qemu/hw/isa/apm.c b/qemu/hw/isa/apm.c index 26ab17021..e232b0da0 100644 --- a/qemu/hw/isa/apm.c +++ b/qemu/hw/isa/apm.c @@ -20,6 +20,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/isa/apm.h" #include "hw/hw.h" #include "hw/pci/pci.h" diff --git a/qemu/hw/isa/i82378.c b/qemu/hw/isa/i82378.c index fcf97d86a..4d29a9900 100644 --- a/qemu/hw/isa/i82378.c +++ b/qemu/hw/isa/i82378.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "hw/pci/pci.h" #include "hw/i386/pc.h" #include "hw/timer/i8254.h" @@ -75,7 +76,10 @@ static void i82378_realize(PCIDevice *pci, Error **errp) pci_config_set_interrupt_pin(pci_conf, 1); /* interrupt pin 0 */ isabus = isa_bus_new(dev, get_system_memory(), - pci_address_space_io(pci)); + pci_address_space_io(pci), errp); + if (!isabus) { + return; + } /* This device has: 2 82C59 (irq) @@ -100,7 +104,6 @@ static void i82378_realize(PCIDevice *pci, Error **errp) /* 2 82C37 (dma) */ isa = isa_create_simple(isabus, "i82374"); - qdev_connect_gpio_out(DEVICE(isa), 0, s->out[1]); /* timer */ isa_create_simple(isabus, "mc146818rtc"); @@ -111,7 +114,7 @@ static void i82378_init(Object *obj) DeviceState *dev = DEVICE(obj); I82378State *s = I82378(obj); - qdev_init_gpio_out(dev, s->out, 2); + qdev_init_gpio_out(dev, s->out, 1); qdev_init_gpio_in(dev, i82378_request_pic_irq, 16); } diff --git a/qemu/hw/isa/isa-bus.c b/qemu/hw/isa/isa-bus.c index 43e0cd8dd..7aa115caf 100644 --- a/qemu/hw/isa/isa-bus.c +++ b/qemu/hw/isa/isa-bus.c @@ -16,6 +16,8 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "monitor/monitor.h" #include "hw/sysbus.h" @@ -36,6 +38,12 @@ static void isa_bus_class_init(ObjectClass *klass, void *data) k->get_fw_dev_path = isabus_get_fw_dev_path; } +static const TypeInfo isa_dma_info = { + .name = TYPE_ISADMA, + .parent = TYPE_INTERFACE, + .class_size = sizeof(IsaDmaClass), +}; + static const TypeInfo isa_bus_info = { .name = TYPE_ISA_BUS, .parent = TYPE_BUS, @@ -44,10 +52,10 @@ static const TypeInfo isa_bus_info = { }; ISABus *isa_bus_new(DeviceState *dev, MemoryRegion* address_space, - MemoryRegion *address_space_io) + MemoryRegion *address_space_io, Error **errp) { if (isabus) { - fprintf(stderr, "Can't create a second ISA bus\n"); + error_setg(errp, "Can't create a second ISA bus"); return NULL; } if (!dev) { @@ -63,9 +71,6 @@ ISABus *isa_bus_new(DeviceState *dev, MemoryRegion* address_space, void isa_bus_irqs(ISABus *bus, qemu_irq *irqs) { - if (!bus) { - hw_error("Can't set isa irqs with no isa bus present."); - } bus->irqs = irqs; } @@ -92,6 +97,20 @@ void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq) dev->nirqs++; } +void isa_bus_dma(ISABus *bus, IsaDma *dma8, IsaDma *dma16) +{ + assert(bus && dma8 && dma16); + assert(!bus->dma[0] && !bus->dma[1]); + bus->dma[0] = dma8; + bus->dma[1] = dma16; +} + +IsaDma *isa_get_dma(ISABus *bus, int nchan) +{ + assert(bus); + return bus->dma[nchan > 3 ? 1 : 0]; +} + static inline void isa_init_ioport(ISADevice *dev, uint16_t ioport) { if (dev && (dev->ioport_id == 0 || ioport < dev->ioport_id)) { @@ -137,10 +156,6 @@ ISADevice *isa_create(ISABus *bus, const char *name) { DeviceState *dev; - if (!bus) { - hw_error("Tried to create isa device %s with no isa bus present.", - name); - } dev = qdev_create(BUS(bus), name); return ISA_DEVICE(dev); } @@ -149,10 +164,6 @@ ISADevice *isa_try_create(ISABus *bus, const char *name) { DeviceState *dev; - if (!bus) { - hw_error("Tried to create isa device %s with no isa bus present.", - name); - } dev = qdev_try_create(BUS(bus), name); return ISA_DEVICE(dev); } @@ -233,6 +244,7 @@ static const TypeInfo isa_device_type_info = { static void isabus_register_types(void) { + type_register_static(&isa_dma_info); type_register_static(&isa_bus_info); type_register_static(&isabus_bridge_info); type_register_static(&isa_device_type_info); diff --git a/qemu/hw/isa/lpc_ich9.c b/qemu/hw/isa/lpc_ich9.c index 360699f6f..99cd3ba9e 100644 --- a/qemu/hw/isa/lpc_ich9.c +++ b/qemu/hw/isa/lpc_ich9.c @@ -27,7 +27,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "qapi/visitor.h" #include "qemu/range.h" @@ -368,13 +370,13 @@ static void ich9_set_sci(void *opaque, int irq_num, int level) } } -void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool smm_enabled, bool enable_tco) +void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool smm_enabled) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci); qemu_irq sci_irq; sci_irq = qemu_allocate_irq(ich9_set_sci, lpc, 0); - ich9_pm_init(lpc_pci, &lpc->pm, smm_enabled, enable_tco, sci_irq); + ich9_pm_init(lpc_pci, &lpc->pm, smm_enabled, sci_irq); ich9_lpc_reset(&lpc->d.qdev); } @@ -394,7 +396,7 @@ static void ich9_apm_ctrl_changed(uint32_t val, void *arg) /* SMI_EN = PMBASE + 30. SMI control and enable register */ if (lpc->pm.smi_en & ICH9_PMIO_SMI_EN_APMC_EN) { - cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI); + cpu_interrupt(current_cpu, CPU_INTERRUPT_SMI); } } @@ -408,18 +410,18 @@ ich9_lpc_pmbase_update(ICH9LPCState *lpc) ich9_pm_iospace_update(&lpc->pm, pm_io_base); } -/* config:RBCA */ -static void ich9_lpc_rcba_update(ICH9LPCState *lpc, uint32_t rbca_old) +/* config:RCBA */ +static void ich9_lpc_rcba_update(ICH9LPCState *lpc, uint32_t rcba_old) { - uint32_t rbca = pci_get_long(lpc->d.config + ICH9_LPC_RCBA); + uint32_t rcba = pci_get_long(lpc->d.config + ICH9_LPC_RCBA); - if (rbca_old & ICH9_LPC_RCBA_EN) { - memory_region_del_subregion(get_system_memory(), &lpc->rbca_mem); + if (rcba_old & ICH9_LPC_RCBA_EN) { + memory_region_del_subregion(get_system_memory(), &lpc->rcrb_mem); } - if (rbca & ICH9_LPC_RCBA_EN) { - memory_region_add_subregion_overlap(get_system_memory(), - rbca & ICH9_LPC_RCBA_BA_MASK, - &lpc->rbca_mem, 1); + if (rcba & ICH9_LPC_RCBA_EN) { + memory_region_add_subregion_overlap(get_system_memory(), + rcba & ICH9_LPC_RCBA_BA_MASK, + &lpc->rcrb_mem, 1); } } @@ -443,7 +445,7 @@ static int ich9_lpc_post_load(void *opaque, int version_id) ICH9LPCState *lpc = opaque; ich9_lpc_pmbase_update(lpc); - ich9_lpc_rcba_update(lpc, 0 /* disabled ICH9_LPC_RBCA_EN */); + ich9_lpc_rcba_update(lpc, 0 /* disabled ICH9_LPC_RCBA_EN */); ich9_lpc_pmcon_update(lpc); return 0; } @@ -452,14 +454,14 @@ static void ich9_lpc_config_write(PCIDevice *d, uint32_t addr, uint32_t val, int len) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(d); - uint32_t rbca_old = pci_get_long(d->config + ICH9_LPC_RCBA); + uint32_t rcba_old = pci_get_long(d->config + ICH9_LPC_RCBA); pci_default_write_config(d, addr, val, len); if (ranges_overlap(addr, len, ICH9_LPC_PMBASE, 4)) { ich9_lpc_pmbase_update(lpc); } if (ranges_overlap(addr, len, ICH9_LPC_RCBA, 4)) { - ich9_lpc_rcba_update(lpc, rbca_old); + ich9_lpc_rcba_update(lpc, rcba_old); } if (ranges_overlap(addr, len, ICH9_LPC_PIRQA_ROUT, 4)) { pci_bus_fire_intx_routing_notifier(lpc->d.bus); @@ -476,7 +478,7 @@ static void ich9_lpc_reset(DeviceState *qdev) { PCIDevice *d = PCI_DEVICE(qdev); ICH9LPCState *lpc = ICH9_LPC_DEVICE(d); - uint32_t rbca_old = pci_get_long(d->config + ICH9_LPC_RCBA); + uint32_t rcba_old = pci_get_long(d->config + ICH9_LPC_RCBA); int i; for (i = 0; i < 4; i++) { @@ -495,13 +497,14 @@ static void ich9_lpc_reset(DeviceState *qdev) ich9_cc_reset(lpc); ich9_lpc_pmbase_update(lpc); - ich9_lpc_rcba_update(lpc, rbca_old); + ich9_lpc_rcba_update(lpc, rcba_old); lpc->sci_level = 0; lpc->rst_cnt = 0; } -static const MemoryRegionOps rbca_mmio_ops = { +/* root complex register block is mapped into memory space */ +static const MemoryRegionOps rcrb_mmio_ops = { .read = ich9_cc_read, .write = ich9_cc_write, .endianness = DEVICE_LITTLE_ENDIAN, @@ -569,14 +572,13 @@ Object *ich9_lpc_find(void) return o; } -static void ich9_lpc_get_sci_int(Object *obj, Visitor *v, - void *opaque, const char *name, - Error **errp) +static void ich9_lpc_get_sci_int(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(obj); uint32_t value = ich9_lpc_sci_irq(lpc); - visit_type_uint32(v, &value, name, errp); + visit_type_uint32(v, name, &value, errp); } static void ich9_lpc_add_properties(ICH9LPCState *lpc) @@ -602,18 +604,22 @@ static void ich9_lpc_initfn(Object *obj) ich9_lpc_add_properties(lpc); } -static int ich9_lpc_init(PCIDevice *d) +static void ich9_lpc_realize(PCIDevice *d, Error **errp) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(d); ISABus *isa_bus; - isa_bus = isa_bus_new(DEVICE(d), get_system_memory(), get_system_io()); + isa_bus = isa_bus_new(DEVICE(d), get_system_memory(), get_system_io(), + errp); + if (!isa_bus) { + return; + } pci_set_long(d->wmask + ICH9_LPC_PMBASE, ICH9_LPC_PMBASE_BASE_ADDRESS_MASK); - memory_region_init_io(&lpc->rbca_mem, OBJECT(d), &rbca_mmio_ops, lpc, - "lpc-rbca-mmio", ICH9_CC_SIZE); + memory_region_init_io(&lpc->rcrb_mem, OBJECT(d), &rcrb_mmio_ops, lpc, + "lpc-rcrb-mmio", ICH9_CC_SIZE); lpc->isa_bus = isa_bus; @@ -628,7 +634,6 @@ static int ich9_lpc_init(PCIDevice *d) memory_region_add_subregion_overlap(pci_address_space_io(d), ICH9_RST_CNT_IOPORT, &lpc->rst_cnt_mem, 1); - return 0; } static void ich9_device_plug_cb(HotplugHandler *hotplug_dev, @@ -706,7 +711,7 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->reset = ich9_lpc_reset; - k->init = ich9_lpc_init; + k->realize = ich9_lpc_realize; dc->vmsd = &vmstate_ich9_lpc; dc->props = ich9_lpc_properties; k->config_write = ich9_lpc_config_write; diff --git a/qemu/hw/isa/pc87312.c b/qemu/hw/isa/pc87312.c index 3b1fcec53..c3ebf3e7a 100644 --- a/qemu/hw/isa/pc87312.c +++ b/qemu/hw/isa/pc87312.c @@ -23,7 +23,9 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/isa/pc87312.h" +#include "qapi/error.h" #include "qemu/error-report.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" @@ -324,14 +326,14 @@ static void pc87312_realize(DeviceState *dev, Error **errp) /* FIXME use a qdev drive property instead of drive_get() */ drive = drive_get(IF_FLOPPY, 0, 0); if (drive != NULL) { - qdev_prop_set_drive_nofail(d, "driveA", - blk_by_legacy_dinfo(drive)); + 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_nofail(d, "driveB", - blk_by_legacy_dinfo(drive)); + qdev_prop_set_drive(d, "driveB", blk_by_legacy_dinfo(drive), + &error_fatal); } qdev_init_nofail(d); s->fdc.dev = isa; diff --git a/qemu/hw/isa/piix4.c b/qemu/hw/isa/piix4.c index 2c59e91ff..5500fcc4d 100644 --- a/qemu/hw/isa/piix4.c +++ b/qemu/hw/isa/piix4.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/pci/pci.h" @@ -90,8 +91,10 @@ static void piix4_realize(PCIDevice *dev, Error **errp) { PIIX4State *d = PIIX4_PCI_DEVICE(dev); - isa_bus_new(DEVICE(d), pci_address_space(dev), - pci_address_space_io(dev)); + if (!isa_bus_new(DEVICE(d), pci_address_space(dev), + pci_address_space_io(dev), errp)) { + return; + } piix4_dev = &d->dev; qemu_register_reset(piix4_reset, d); } diff --git a/qemu/hw/isa/vt82c686.c b/qemu/hw/isa/vt82c686.c index 252e1d714..41d5254f8 100644 --- a/qemu/hw/isa/vt82c686.c +++ b/qemu/hw/isa/vt82c686.c @@ -10,6 +10,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/isa/vt82c686.h" @@ -440,7 +441,10 @@ static void vt82c686b_realize(PCIDevice *d, Error **errp) int i; isa_bus = isa_bus_new(DEVICE(d), get_system_memory(), - pci_address_space_io(d)); + pci_address_space_io(d), errp); + if (!isa_bus) { + return; + } pci_conf = d->config; pci_config_set_prog_interface(pci_conf, 0x0); diff --git a/qemu/hw/lm32/lm32_boards.c b/qemu/hw/lm32/lm32_boards.c index 70f48d3b1..c0290560f 100644 --- a/qemu/hw/lm32/lm32_boards.c +++ b/qemu/hw/lm32/lm32_boards.c @@ -17,6 +17,9 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "hw/hw.h" #include "hw/block/flash.h" @@ -142,7 +145,7 @@ static void lm32_evr_init(MachineState *machine) int kernel_size; kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, - 1, ELF_MACHINE, 0); + 1, EM_LATTICEMICO32, 0, 0); reset_info->bootstrap_pc = entry; if (kernel_size < 0) { @@ -244,7 +247,7 @@ static void lm32_uclinux_init(MachineState *machine) int kernel_size; kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, - 1, ELF_MACHINE, 0); + 1, EM_LATTICEMICO32, 0, 0); reset_info->bootstrap_pc = entry; if (kernel_size < 0) { @@ -292,24 +295,40 @@ static void lm32_uclinux_init(MachineState *machine) qemu_register_reset(main_cpu_reset, reset_info); } -static QEMUMachine lm32_evr_machine = { - .name = "lm32-evr", - .desc = "LatticeMico32 EVR32 eval system", - .init = lm32_evr_init, - .is_default = 1, +static void lm32_evr_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "LatticeMico32 EVR32 eval system"; + mc->init = lm32_evr_init; + mc->is_default = 1; +} + +static const TypeInfo lm32_evr_type = { + .name = MACHINE_TYPE_NAME("lm32-evr"), + .parent = TYPE_MACHINE, + .class_init = lm32_evr_class_init, }; -static QEMUMachine lm32_uclinux_machine = { - .name = "lm32-uclinux", - .desc = "lm32 platform for uClinux and u-boot by Theobroma Systems", - .init = lm32_uclinux_init, - .is_default = 0, +static void lm32_uclinux_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "lm32 platform for uClinux and u-boot by Theobroma Systems"; + mc->init = lm32_uclinux_init; + mc->is_default = 0; +} + +static const TypeInfo lm32_uclinux_type = { + .name = MACHINE_TYPE_NAME("lm32-uclinux"), + .parent = TYPE_MACHINE, + .class_init = lm32_uclinux_class_init, }; static void lm32_machine_init(void) { - qemu_register_machine(&lm32_uclinux_machine); - qemu_register_machine(&lm32_evr_machine); + type_register_static(&lm32_evr_type); + type_register_static(&lm32_uclinux_type); } -machine_init(lm32_machine_init); +type_init(lm32_machine_init) diff --git a/qemu/hw/lm32/lm32_hwsetup.h b/qemu/hw/lm32/lm32_hwsetup.h index 838754d5d..b71e6eafb 100644 --- a/qemu/hw/lm32/lm32_hwsetup.h +++ b/qemu/hw/lm32/lm32_hwsetup.h @@ -26,6 +26,7 @@ #define QEMU_HW_LM32_HWSETUP_H #include "qemu-common.h" +#include "qemu/cutils.h" #include "hw/loader.h" typedef struct { diff --git a/qemu/hw/lm32/milkymist-hw.h b/qemu/hw/lm32/milkymist-hw.h index 8d20cac1d..c8dfb4d2d 100644 --- a/qemu/hw/lm32/milkymist-hw.h +++ b/qemu/hw/lm32/milkymist-hw.h @@ -88,7 +88,8 @@ static inline DeviceState *milkymist_pfpu_create(hwaddr base, #ifdef CONFIG_OPENGL #include -#include +#include +#include static const int glx_fbconfig_attr[] = { GLX_GREEN_SIZE, 5, GLX_GREEN_SIZE, 6, diff --git a/qemu/hw/lm32/milkymist.c b/qemu/hw/lm32/milkymist.c index e755f5b24..96e6f4dc2 100644 --- a/qemu/hw/lm32/milkymist.c +++ b/qemu/hw/lm32/milkymist.c @@ -17,6 +17,9 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "hw/hw.h" #include "hw/block/flash.h" @@ -30,6 +33,7 @@ #include "milkymist-hw.h" #include "lm32.h" #include "exec/address-spaces.h" +#include "qemu/cutils.h" #define BIOS_FILENAME "mmone-bios.bin" #define BIOS_OFFSET 0x00860000 @@ -176,7 +180,7 @@ milkymist_init(MachineState *machine) /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, - 1, ELF_MACHINE, 0); + 1, EM_LATTICEMICO32, 0, 0); reset_info->bootstrap_pc = entry; if (kernel_size < 0) { @@ -209,16 +213,11 @@ milkymist_init(MachineState *machine) qemu_register_reset(main_cpu_reset, reset_info); } -static QEMUMachine milkymist_machine = { - .name = "milkymist", - .desc = "Milkymist One", - .init = milkymist_init, - .is_default = 0, -}; - -static void milkymist_machine_init(void) +static void milkymist_machine_init(MachineClass *mc) { - qemu_register_machine(&milkymist_machine); + mc->desc = "Milkymist One"; + mc->init = milkymist_init; + mc->is_default = 0; } -machine_init(milkymist_machine_init); +DEFINE_MACHINE("milkymist", milkymist_machine_init) diff --git a/qemu/hw/m68k/an5206.c b/qemu/hw/m68k/an5206.c index f63ab2b94..142bab98c 100644 --- a/qemu/hw/m68k/an5206.c +++ b/qemu/hw/m68k/an5206.c @@ -6,12 +6,17 @@ * This code is licensed under the GPL */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/m68k/mcf.h" #include "hw/boards.h" #include "hw/loader.h" #include "elf.h" #include "exec/address-spaces.h" +#include "qemu/error-report.h" #include "sysemu/qtest.h" #define KERNEL_LOAD_ADDR 0x10000 @@ -39,7 +44,8 @@ static void an5206_init(MachineState *machine) } cpu = cpu_m68k_init(cpu_model); if (!cpu) { - hw_error("Unable to find m68k CPU definition\n"); + error_report("Unable to find m68k CPU definition"); + exit(1); } env = &cpu->env; @@ -54,7 +60,7 @@ static void an5206_init(MachineState *machine) memory_region_add_subregion(address_space_mem, 0, ram); /* Internal SRAM. */ - memory_region_init_ram(sram, NULL, "an5206.sram", 512, &error_abort); + memory_region_init_ram(sram, NULL, "an5206.sram", 512, &error_fatal); vmstate_register_ram_global(sram); memory_region_add_subregion(address_space_mem, AN5206_RAMBAR_ADDR, sram); @@ -70,7 +76,7 @@ static void an5206_init(MachineState *machine) } kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, - NULL, NULL, 1, ELF_MACHINE, 0); + NULL, NULL, 1, EM_68K, 0, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, @@ -89,15 +95,10 @@ static void an5206_init(MachineState *machine) env->pc = entry; } -static QEMUMachine an5206_machine = { - .name = "an5206", - .desc = "Arnewsh 5206", - .init = an5206_init, -}; - -static void an5206_machine_init(void) +static void an5206_machine_init(MachineClass *mc) { - qemu_register_machine(&an5206_machine); + mc->desc = "Arnewsh 5206"; + mc->init = an5206_init; } -machine_init(an5206_machine_init); +DEFINE_MACHINE("an5206", an5206_machine_init) diff --git a/qemu/hw/m68k/dummy_m68k.c b/qemu/hw/m68k/dummy_m68k.c index 5b77d930e..0b11d2074 100644 --- a/qemu/hw/m68k/dummy_m68k.c +++ b/qemu/hw/m68k/dummy_m68k.c @@ -6,6 +6,9 @@ * This code is licensed under the GPL */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/boards.h" #include "hw/loader.h" @@ -49,7 +52,7 @@ static void dummy_m68k_init(MachineState *machine) /* Load kernel. */ if (kernel_filename) { kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, - NULL, NULL, 1, ELF_MACHINE, 0); + NULL, NULL, 1, EM_68K, 0, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, @@ -72,15 +75,10 @@ static void dummy_m68k_init(MachineState *machine) env->pc = entry; } -static QEMUMachine dummy_m68k_machine = { - .name = "dummy", - .desc = "Dummy board", - .init = dummy_m68k_init, -}; - -static void dummy_m68k_machine_init(void) +static void dummy_m68k_machine_init(MachineClass *mc) { - qemu_register_machine(&dummy_m68k_machine); + mc->desc = "Dummy board"; + mc->init = dummy_m68k_init; } -machine_init(dummy_m68k_machine_init); +DEFINE_MACHINE("dummy", dummy_m68k_machine_init) diff --git a/qemu/hw/m68k/mcf5206.c b/qemu/hw/m68k/mcf5206.c index 1727a4685..e14896e52 100644 --- a/qemu/hw/m68k/mcf5206.c +++ b/qemu/hw/m68k/mcf5206.c @@ -5,6 +5,9 @@ * * This code is licensed under the GPL */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/m68k/mcf.h" #include "qemu/timer.h" diff --git a/qemu/hw/m68k/mcf5208.c b/qemu/hw/m68k/mcf5208.c index 326a42d27..24155574f 100644 --- a/qemu/hw/m68k/mcf5208.c +++ b/qemu/hw/m68k/mcf5208.c @@ -5,6 +5,10 @@ * * This code is licensed under the GPL */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/m68k/mcf.h" #include "qemu/timer.h" @@ -222,7 +226,7 @@ static void mcf5208evb_init(MachineState *machine) memory_region_add_subregion(address_space_mem, 0x40000000, ram); /* Internal SRAM. */ - memory_region_init_ram(sram, NULL, "mcf5208.sram", 16384, &error_abort); + memory_region_init_ram(sram, NULL, "mcf5208.sram", 16384, &error_fatal); vmstate_register_ram_global(sram); memory_region_add_subregion(address_space_mem, 0x80000000, sram); @@ -275,7 +279,7 @@ static void mcf5208evb_init(MachineState *machine) } kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, - NULL, NULL, 1, ELF_MACHINE, 0); + NULL, NULL, 1, EM_68K, 0, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, @@ -294,16 +298,11 @@ static void mcf5208evb_init(MachineState *machine) env->pc = entry; } -static QEMUMachine mcf5208evb_machine = { - .name = "mcf5208evb", - .desc = "MCF5206EVB", - .init = mcf5208evb_init, - .is_default = 1, -}; - -static void mcf5208evb_machine_init(void) +static void mcf5208evb_machine_init(MachineClass *mc) { - qemu_register_machine(&mcf5208evb_machine); + mc->desc = "MCF5206EVB"; + mc->init = mcf5208evb_init; + mc->is_default = 1; } -machine_init(mcf5208evb_machine_init); +DEFINE_MACHINE("mcf5208evb", mcf5208evb_machine_init) diff --git a/qemu/hw/m68k/mcf_intc.c b/qemu/hw/m68k/mcf_intc.c index f13c7f3ae..cf581324e 100644 --- a/qemu/hw/m68k/mcf_intc.c +++ b/qemu/hw/m68k/mcf_intc.c @@ -5,6 +5,9 @@ * * This code is licensed under the GPL */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/m68k/mcf.h" #include "exec/address-spaces.h" diff --git a/qemu/hw/mem/Makefile.objs b/qemu/hw/mem/Makefile.objs index b000fb42b..f12f8b97a 100644 --- a/qemu/hw/mem/Makefile.objs +++ b/qemu/hw/mem/Makefile.objs @@ -1 +1,2 @@ common-obj-$(CONFIG_MEM_HOTPLUG) += pc-dimm.o +common-obj-$(CONFIG_NVDIMM) += nvdimm.o diff --git a/qemu/hw/mem/nvdimm.c b/qemu/hw/mem/nvdimm.c new file mode 100644 index 000000000..0a602f28b --- /dev/null +++ b/qemu/hw/mem/nvdimm.c @@ -0,0 +1,47 @@ +/* + * Non-Volatile Dual In-line Memory Module Virtualization Implementation + * + * Copyright(C) 2015 Intel Corporation. + * + * Author: + * Xiao Guangrong + * + * Currently, it only supports PMEM Virtualization. + * + * 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 + */ + +#include "qemu/osdep.h" +#include "hw/mem/nvdimm.h" + +static void nvdimm_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + /* nvdimm hotplug has not been supported yet. */ + dc->hotpluggable = false; +} + +static TypeInfo nvdimm_info = { + .name = TYPE_NVDIMM, + .parent = TYPE_PC_DIMM, + .class_init = nvdimm_class_init, +}; + +static void nvdimm_register_types(void) +{ + type_register_static(&nvdimm_info); +} + +type_init(nvdimm_register_types) diff --git a/qemu/hw/mem/pc-dimm.c b/qemu/hw/mem/pc-dimm.c index bb04862de..9e7de5682 100644 --- a/qemu/hw/mem/pc-dimm.c +++ b/qemu/hw/mem/pc-dimm.c @@ -18,13 +18,16 @@ * License along with this library; if not, see */ +#include "qemu/osdep.h" #include "hw/mem/pc-dimm.h" +#include "qapi/error.h" #include "qemu/config-file.h" #include "qapi/visitor.h" #include "qemu/range.h" #include "sysemu/numa.h" #include "sysemu/kvm.h" #include "trace.h" +#include "hw/virtio/vhost.h" typedef struct pc_dimms_capacity { uint64_t size; @@ -95,6 +98,12 @@ void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms, goto out; } + if (!vhost_has_free_slot()) { + error_setg(&local_err, "a used vhost backend has no free" + " memory slots left"); + goto out; + } + memory_region_add_subregion(&hpms->mr, addr - hpms->base, mr); vmstate_register_ram(mr, dev); numa_set_mem_node_id(addr, memory_region_size(mr), dimm->node); @@ -172,7 +181,7 @@ int qmp_pc_dimm_device_list(Object *obj, void *opaque) NULL); di->memdev = object_get_canonical_path(OBJECT(dimm->hostmem)); - info->dimm = di; + info->u.dimm.data = di; elem->value = info; elem->next = NULL; **prev = elem; @@ -184,32 +193,6 @@ int qmp_pc_dimm_device_list(Object *obj, void *opaque) return 0; } -ram_addr_t get_current_ram_size(void) -{ - MemoryDeviceInfoList *info_list = NULL; - MemoryDeviceInfoList **prev = &info_list; - MemoryDeviceInfoList *info; - ram_addr_t size = ram_size; - - qmp_pc_dimm_device_list(qdev_get_machine(), &prev); - for (info = info_list; info; info = info->next) { - MemoryDeviceInfo *value = info->value; - - if (value) { - switch (value->kind) { - case MEMORY_DEVICE_INFO_KIND_DIMM: - size += value->dimm->size; - break; - default: - break; - } - } - } - qapi_free_MemoryDeviceInfoList(info_list); - - return size; -} - static int pc_dimm_slot2bitmap(Object *obj, void *opaque) { unsigned long *bitmap = opaque; @@ -365,8 +348,8 @@ static Property pc_dimm_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static void pc_dimm_get_size(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void pc_dimm_get_size(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { int64_t value; MemoryRegion *mr; @@ -375,22 +358,29 @@ static void pc_dimm_get_size(Object *obj, Visitor *v, void *opaque, mr = host_memory_backend_get_memory(dimm->hostmem, errp); value = memory_region_size(mr); - visit_type_int(v, &value, name, errp); + visit_type_int(v, name, &value, errp); } static void pc_dimm_check_memdev_is_busy(Object *obj, const char *name, Object *val, Error **errp) { MemoryRegion *mr; + Error *local_err = NULL; - mr = host_memory_backend_get_memory(MEMORY_BACKEND(val), errp); + mr = host_memory_backend_get_memory(MEMORY_BACKEND(val), &local_err); + if (local_err) { + goto out; + } if (memory_region_is_mapped(mr)) { char *path = object_get_canonical_path_component(val); - error_setg(errp, "can't use already busy memdev: %s", path); + error_setg(&local_err, "can't use already busy memdev: %s", path); g_free(path); } else { - qdev_prop_allow_set_link_before_realize(obj, name, val, errp); + qdev_prop_allow_set_link_before_realize(obj, name, val, &local_err); } + +out: + error_propagate(errp, local_err); } static void pc_dimm_init(Object *obj) @@ -414,10 +404,11 @@ static void pc_dimm_realize(DeviceState *dev, Error **errp) error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property is not set"); return; } - if ((nb_numa_nodes > 0) && (dimm->node >= nb_numa_nodes)) { + if (((nb_numa_nodes > 0) && (dimm->node >= nb_numa_nodes)) || + (!nb_numa_nodes && dimm->node)) { error_setg(errp, "'DIMM property " PC_DIMM_NODE_PROP " has value %" PRIu32 "' which exceeds the number of numa nodes: %d", - dimm->node, nb_numa_nodes); + dimm->node, nb_numa_nodes ? nb_numa_nodes : 1); return; } } diff --git a/qemu/hw/microblaze/boot.c b/qemu/hw/microblaze/boot.c index 3e8820f36..9eebb1a52 100644 --- a/qemu/hw/microblaze/boot.c +++ b/qemu/hw/microblaze/boot.c @@ -24,6 +24,9 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "qemu/option.h" #include "qemu/config-file.h" #include "qemu/error-report.h" @@ -32,6 +35,7 @@ #include "sysemu/sysemu.h" #include "hw/loader.h" #include "elf.h" +#include "qemu/cutils.h" #include "boot.h" @@ -141,12 +145,12 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, &low, &high, - big_endian, ELF_MACHINE, 0); + big_endian, EM_MICROBLAZE, 0, 0); base32 = entry; if (base32 == 0xc0000000) { kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, &entry, NULL, NULL, - big_endian, ELF_MACHINE, 0); + big_endian, EM_MICROBLAZE, 0, 0); } /* Always boot into physical ram. */ boot_info.bootstrap_pc = (uint32_t)entry; diff --git a/qemu/hw/microblaze/petalogix_ml605_mmu.c b/qemu/hw/microblaze/petalogix_ml605_mmu.c index ed84a37e6..07527b677 100644 --- a/qemu/hw/microblaze/petalogix_ml605_mmu.c +++ b/qemu/hw/microblaze/petalogix_ml605_mmu.c @@ -25,6 +25,10 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "hw/hw.h" #include "net/net.h" @@ -35,7 +39,7 @@ #include "sysemu/block-backend.h" #include "hw/char/serial.h" #include "exec/address-spaces.h" -#include "hw/ssi.h" +#include "hw/ssi/ssi.h" #include "boot.h" @@ -81,6 +85,7 @@ petalogix_ml605_init(MachineState *machine) /* init CPUs */ cpu = MICROBLAZE_CPU(object_new(TYPE_MICROBLAZE_CPU)); + object_property_set_str(OBJECT(cpu), "8.10.a", "version", &error_abort); /* Use FPU but don't use floating point conversion and square * root instructions */ @@ -92,12 +97,12 @@ petalogix_ml605_init(MachineState *machine) /* Attach emulated BRAM through the LMB. */ memory_region_init_ram(phys_lmb_bram, NULL, "petalogix_ml605.lmb_bram", - LMB_BRAM_SIZE, &error_abort); + LMB_BRAM_SIZE, &error_fatal); vmstate_register_ram_global(phys_lmb_bram); memory_region_add_subregion(address_space_mem, 0x00000000, phys_lmb_bram); memory_region_init_ram(phys_ram, NULL, "petalogix_ml605.ram", ram_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(phys_ram); memory_region_add_subregion(address_space_mem, MEMORY_BASEADDR, phys_ram); @@ -206,16 +211,11 @@ petalogix_ml605_init(MachineState *machine) } -static QEMUMachine petalogix_ml605_machine = { - .name = "petalogix-ml605", - .desc = "PetaLogix linux refdesign for xilinx ml605 little endian", - .init = petalogix_ml605_init, - .is_default = 0, -}; - -static void petalogix_ml605_machine_init(void) +static void petalogix_ml605_machine_init(MachineClass *mc) { - qemu_register_machine(&petalogix_ml605_machine); + mc->desc = "PetaLogix linux refdesign for xilinx ml605 little endian"; + mc->init = petalogix_ml605_init; + mc->is_default = 0; } -machine_init(petalogix_ml605_machine_init); +DEFINE_MACHINE("petalogix-ml605", petalogix_ml605_machine_init) diff --git a/qemu/hw/microblaze/petalogix_s3adsp1800_mmu.c b/qemu/hw/microblaze/petalogix_s3adsp1800_mmu.c index 0c2140c3f..f821e1cfe 100644 --- a/qemu/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/qemu/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -23,6 +23,10 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "hw/hw.h" #include "net/net.h" @@ -66,17 +70,18 @@ petalogix_s3adsp1800_init(MachineState *machine) MemoryRegion *sysmem = get_system_memory(); cpu = MICROBLAZE_CPU(object_new(TYPE_MICROBLAZE_CPU)); + object_property_set_str(OBJECT(cpu), "7.10.d", "version", &error_abort); object_property_set_bool(OBJECT(cpu), true, "realized", &error_abort); /* Attach emulated BRAM through the LMB. */ memory_region_init_ram(phys_lmb_bram, NULL, "petalogix_s3adsp1800.lmb_bram", LMB_BRAM_SIZE, - &error_abort); + &error_fatal); vmstate_register_ram_global(phys_lmb_bram); memory_region_add_subregion(sysmem, 0x00000000, phys_lmb_bram); memory_region_init_ram(phys_ram, NULL, "petalogix_s3adsp1800.ram", - ram_size, &error_abort); + ram_size, &error_fatal); vmstate_register_ram_global(phys_ram); memory_region_add_subregion(sysmem, ddr_base, phys_ram); @@ -124,16 +129,11 @@ petalogix_s3adsp1800_init(MachineState *machine) NULL); } -static QEMUMachine petalogix_s3adsp1800_machine = { - .name = "petalogix-s3adsp1800", - .desc = "PetaLogix linux refdesign for xilinx Spartan 3ADSP1800", - .init = petalogix_s3adsp1800_init, - .is_default = 1, -}; - -static void petalogix_s3adsp1800_machine_init(void) +static void petalogix_s3adsp1800_machine_init(MachineClass *mc) { - qemu_register_machine(&petalogix_s3adsp1800_machine); + mc->desc = "PetaLogix linux refdesign for xilinx Spartan 3ADSP1800"; + mc->init = petalogix_s3adsp1800_init; + mc->is_default = 1; } -machine_init(petalogix_s3adsp1800_machine_init); +DEFINE_MACHINE("petalogix-s3adsp1800", petalogix_s3adsp1800_machine_init) diff --git a/qemu/hw/mips/Makefile.objs b/qemu/hw/mips/Makefile.objs index 9633f3a57..9352a1c06 100644 --- a/qemu/hw/mips/Makefile.objs +++ b/qemu/hw/mips/Makefile.objs @@ -3,3 +3,4 @@ obj-y += addr.o cputimer.o mips_int.o obj-$(CONFIG_JAZZ) += mips_jazz.o obj-$(CONFIG_FULONG) += mips_fulong2e.o obj-y += gt64xxx_pci.o +obj-$(CONFIG_MIPS_CPS) += cps.o diff --git a/qemu/hw/mips/addr.c b/qemu/hw/mips/addr.c index ff3b95260..e4e86b4a7 100644 --- a/qemu/hw/mips/addr.c +++ b/qemu/hw/mips/addr.c @@ -20,6 +20,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/mips/cpudevs.h" diff --git a/qemu/hw/mips/cps.c b/qemu/hw/mips/cps.c new file mode 100644 index 000000000..1bafbbb27 --- /dev/null +++ b/qemu/hw/mips/cps.c @@ -0,0 +1,180 @@ +/* + * Coherent Processing System emulation. + * + * Copyright (c) 2016 Imagination Technologies + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/mips/cps.h" +#include "hw/mips/mips.h" +#include "hw/mips/cpudevs.h" +#include "sysemu/kvm.h" + +qemu_irq get_cps_irq(MIPSCPSState *s, int pin_number) +{ + MIPSCPU *cpu = MIPS_CPU(first_cpu); + CPUMIPSState *env = &cpu->env; + + assert(pin_number < s->num_irq); + + /* TODO: return GIC pins once implemented */ + return env->irq[pin_number]; +} + +static void mips_cps_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + MIPSCPSState *s = MIPS_CPS(obj); + + /* Cover entire address space as there do not seem to be any + * constraints for the base address of CPC and GIC. */ + memory_region_init(&s->container, obj, "mips-cps-container", UINT64_MAX); + sysbus_init_mmio(sbd, &s->container); +} + +static void main_cpu_reset(void *opaque) +{ + MIPSCPU *cpu = opaque; + CPUState *cs = CPU(cpu); + + cpu_reset(cs); + + /* All VPs are halted on reset. Leave powering up to CPC. */ + cs->halted = 1; +} + +static bool cpu_mips_itu_supported(CPUMIPSState *env) +{ + bool is_mt = (env->CP0_Config5 & (1 << CP0C5_VP)) || + (env->CP0_Config3 & (1 << CP0C3_MT)); + + return is_mt && !kvm_enabled(); +} + +static void mips_cps_realize(DeviceState *dev, Error **errp) +{ + MIPSCPSState *s = MIPS_CPS(dev); + CPUMIPSState *env; + MIPSCPU *cpu; + int i; + Error *err = NULL; + target_ulong gcr_base; + bool itu_present = false; + + for (i = 0; i < s->num_vp; i++) { + cpu = cpu_mips_init(s->cpu_model); + if (cpu == NULL) { + error_setg(errp, "%s: CPU initialization failed\n", __func__); + return; + } + env = &cpu->env; + + /* Init internal devices */ + cpu_mips_irq_init_cpu(env); + cpu_mips_clock_init(env); + if (cpu_mips_itu_supported(env)) { + itu_present = true; + /* Attach ITC Tag to the VP */ + env->itc_tag = mips_itu_get_tag_region(&s->itu); + } + qemu_register_reset(main_cpu_reset, cpu); + } + + cpu = MIPS_CPU(first_cpu); + env = &cpu->env; + + /* Inter-Thread Communication Unit */ + if (itu_present) { + object_initialize(&s->itu, sizeof(s->itu), TYPE_MIPS_ITU); + qdev_set_parent_bus(DEVICE(&s->itu), sysbus_get_default()); + + object_property_set_int(OBJECT(&s->itu), 16, "num-fifo", &err); + object_property_set_int(OBJECT(&s->itu), 16, "num-semaphores", &err); + object_property_set_bool(OBJECT(&s->itu), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->container, 0, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->itu), 0)); + } + + /* Cluster Power Controller */ + object_initialize(&s->cpc, sizeof(s->cpc), TYPE_MIPS_CPC); + qdev_set_parent_bus(DEVICE(&s->cpc), sysbus_get_default()); + + object_property_set_int(OBJECT(&s->cpc), s->num_vp, "num-vp", &err); + object_property_set_int(OBJECT(&s->cpc), 1, "vp-start-running", &err); + object_property_set_bool(OBJECT(&s->cpc), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->container, 0, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cpc), 0)); + + /* Global Configuration Registers */ + gcr_base = env->CP0_CMGCRBase << 4; + + object_initialize(&s->gcr, sizeof(s->gcr), TYPE_MIPS_GCR); + qdev_set_parent_bus(DEVICE(&s->gcr), sysbus_get_default()); + + object_property_set_int(OBJECT(&s->gcr), s->num_vp, "num-vp", &err); + object_property_set_int(OBJECT(&s->gcr), 0x800, "gcr-rev", &err); + object_property_set_int(OBJECT(&s->gcr), gcr_base, "gcr-base", &err); + object_property_set_link(OBJECT(&s->gcr), OBJECT(&s->cpc.mr), "cpc", &err); + object_property_set_bool(OBJECT(&s->gcr), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->container, gcr_base, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gcr), 0)); +} + +static Property mips_cps_properties[] = { + DEFINE_PROP_UINT32("num-vp", MIPSCPSState, num_vp, 1), + DEFINE_PROP_UINT32("num-irq", MIPSCPSState, num_irq, 8), + DEFINE_PROP_STRING("cpu-model", MIPSCPSState, cpu_model), + DEFINE_PROP_END_OF_LIST() +}; + +static void mips_cps_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = mips_cps_realize; + dc->props = mips_cps_properties; +} + +static const TypeInfo mips_cps_info = { + .name = TYPE_MIPS_CPS, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MIPSCPSState), + .instance_init = mips_cps_init, + .class_init = mips_cps_class_init, +}; + +static void mips_cps_register_types(void) +{ + type_register_static(&mips_cps_info); +} + +type_init(mips_cps_register_types) diff --git a/qemu/hw/mips/cputimer.c b/qemu/hw/mips/cputimer.c index 577c9aeab..efb227d06 100644 --- a/qemu/hw/mips/cputimer.c +++ b/qemu/hw/mips/cputimer.c @@ -20,23 +20,32 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/mips/cpudevs.h" #include "qemu/timer.h" #include "sysemu/kvm.h" -#define TIMER_FREQ 100 * 1000 * 1000 +#define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */ /* XXX: do not use a global */ uint32_t cpu_mips_get_random (CPUMIPSState *env) { - static uint32_t lfsr = 1; + static uint32_t seed = 1; static uint32_t prev_idx = 0; uint32_t idx; + uint32_t nb_rand_tlb = env->tlb->nb_tlb - env->CP0_Wired; + + if (nb_rand_tlb == 1) { + return env->tlb->nb_tlb - 1; + } + /* Don't return same value twice, so get another value */ do { - lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0xd0000001u); - idx = lfsr % (env->tlb->nb_tlb - env->CP0_Wired) + env->CP0_Wired; + /* Use a simple algorithm of Linear Congruential Generator + * from ISO/IEC 9899 standard. */ + seed = 1103515245 * seed + 12345; + idx = (seed >> 16) % nb_rand_tlb + env->CP0_Wired; } while (idx == prev_idx); prev_idx = idx; return idx; @@ -49,9 +58,8 @@ static void cpu_mips_timer_update(CPUMIPSState *env) uint32_t wait; now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - wait = env->CP0_Compare - env->CP0_Count - - (uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec()); - next = now + muldiv64(wait, get_ticks_per_sec(), TIMER_FREQ); + wait = env->CP0_Compare - env->CP0_Count - (uint32_t)(now / TIMER_PERIOD); + next = now + (uint64_t)wait * TIMER_PERIOD; timer_mod(env->timer, next); } @@ -79,8 +87,7 @@ uint32_t cpu_mips_get_count (CPUMIPSState *env) cpu_mips_timer_expire(env); } - return env->CP0_Count + - (uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec()); + return env->CP0_Count + (uint32_t)(now / TIMER_PERIOD); } } @@ -95,9 +102,8 @@ void cpu_mips_store_count (CPUMIPSState *env, uint32_t count) env->CP0_Count = count; else { /* Store new count register */ - env->CP0_Count = - count - (uint32_t)muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - TIMER_FREQ, get_ticks_per_sec()); + env->CP0_Count = count - + (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD); /* Update timer timer */ cpu_mips_timer_update(env); } @@ -121,8 +127,8 @@ void cpu_mips_start_count(CPUMIPSState *env) void cpu_mips_stop_count(CPUMIPSState *env) { /* Store the current value */ - env->CP0_Count += (uint32_t)muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - TIMER_FREQ, get_ticks_per_sec()); + env->CP0_Count += (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / + TIMER_PERIOD); } static void mips_timer_cb (void *opaque) diff --git a/qemu/hw/mips/gt64xxx_pci.c b/qemu/hw/mips/gt64xxx_pci.c index 10fcca33f..3f4523df2 100644 --- a/qemu/hw/mips/gt64xxx_pci.c +++ b/qemu/hw/mips/gt64xxx_pci.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/mips/mips.h" #include "hw/pci/pci.h" @@ -275,7 +276,8 @@ static void check_reserved_space (hwaddr *start, static void gt64120_isd_mapping(GT64120State *s) { - hwaddr start = s->regs[GT_ISD] << 21; + /* Bits 14:0 of ISD map to bits 35:21 of the start address. */ + hwaddr start = ((hwaddr)s->regs[GT_ISD] << 21) & 0xFFFE00000ull; hwaddr length = 0x1000; if (s->ISD_length) { @@ -1192,7 +1194,7 @@ static int gt64120_init(SysBusDevice *dev) return 0; } -static int gt64120_pci_init(PCIDevice *d) +static void gt64120_pci_realize(PCIDevice *d, Error **errp) { /* FIXME: Malta specific hw assumptions ahead */ pci_set_word(d->config + PCI_COMMAND, 0); @@ -1206,8 +1208,6 @@ static int gt64120_pci_init(PCIDevice *d) pci_set_long(d->config + PCI_BASE_ADDRESS_4, 0x14000000); pci_set_long(d->config + PCI_BASE_ADDRESS_5, 0x14000001); pci_set_byte(d->config + 0x3d, 0x01); - - return 0; } static void gt64120_pci_class_init(ObjectClass *klass, void *data) @@ -1215,7 +1215,7 @@ static void gt64120_pci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = gt64120_pci_init; + k->realize = gt64120_pci_realize; k->vendor_id = PCI_VENDOR_ID_MARVELL; k->device_id = PCI_DEVICE_ID_MARVELL_GT6412X; k->revision = 0x10; diff --git a/qemu/hw/mips/mips_fulong2e.c b/qemu/hw/mips/mips_fulong2e.c index dea941ad8..bdb716e72 100644 --- a/qemu/hw/mips/mips_fulong2e.c +++ b/qemu/hw/mips/mips_fulong2e.c @@ -18,6 +18,8 @@ * http://www.loongsondeveloper.com/doc/Loongson2EUserGuide.pdf */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/char/serial.h" @@ -116,7 +118,7 @@ static int64_t load_kernel (CPUMIPSState *env) if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL, (uint64_t *)&kernel_entry, (uint64_t *)&kernel_low, - (uint64_t *)&kernel_high, 0, ELF_MACHINE, 1) < 0) { + (uint64_t *)&kernel_high, 0, EM_MIPS, 1, 0) < 0) { fprintf(stderr, "qemu: could not load kernel '%s'\n", loaderparams.kernel_filename); exit(1); @@ -251,15 +253,6 @@ static void network_init (PCIBus *pci_bus) } } -static void cpu_request_exit(void *opaque, int irq, int level) -{ - CPUState *cpu = current_cpu; - - if (cpu && level) { - cpu_exit(cpu); - } -} - static void mips_fulong2e_init(MachineState *machine) { ram_addr_t ram_size = machine->ram_size; @@ -274,7 +267,6 @@ static void mips_fulong2e_init(MachineState *machine) long bios_size; int64_t kernel_entry; qemu_irq *i8259; - qemu_irq *cpu_exit_irq; PCIBus *pci_bus; ISABus *isa_bus; I2CBus *smbus; @@ -304,7 +296,7 @@ static void mips_fulong2e_init(MachineState *machine) /* allocate RAM */ memory_region_allocate_system_memory(ram, NULL, "fulong2e.ram", ram_size); memory_region_init_ram(bios, NULL, "fulong2e.bios", bios_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(bios); memory_region_set_readonly(bios, true); @@ -375,8 +367,7 @@ static void mips_fulong2e_init(MachineState *machine) /* init other devices */ pit = pit_init(isa_bus, 0x40, 0, NULL); - cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1); - DMA_init(0, cpu_exit_irq); + DMA_init(isa_bus, 0); /* Super I/O */ isa_create_simple(isa_bus, "i8042"); @@ -392,15 +383,10 @@ static void mips_fulong2e_init(MachineState *machine) network_init(pci_bus); } -static QEMUMachine mips_fulong2e_machine = { - .name = "fulong2e", - .desc = "Fulong 2e mini pc", - .init = mips_fulong2e_init, -}; - -static void mips_fulong2e_machine_init(void) +static void mips_fulong2e_machine_init(MachineClass *mc) { - qemu_register_machine(&mips_fulong2e_machine); + mc->desc = "Fulong 2e mini pc"; + mc->init = mips_fulong2e_init; } -machine_init(mips_fulong2e_machine_init); +DEFINE_MACHINE("fulong2e", mips_fulong2e_machine_init) diff --git a/qemu/hw/mips/mips_int.c b/qemu/hw/mips/mips_int.c index d740046ba..59081f9d1 100644 --- a/qemu/hw/mips/mips_int.c +++ b/qemu/hw/mips/mips_int.c @@ -20,6 +20,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/mips/cpudevs.h" #include "cpu.h" diff --git a/qemu/hw/mips/mips_jazz.c b/qemu/hw/mips/mips_jazz.c index 9d60633ef..ac7c64125 100644 --- a/qemu/hw/mips/mips_jazz.c +++ b/qemu/hw/mips/mips_jazz.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/mips/mips.h" #include "hw/mips/cpudevs.h" @@ -44,6 +45,7 @@ #include "exec/address-spaces.h" #include "sysemu/qtest.h" #include "qemu/error-report.h" +#include "qemu/help_option.h" enum jazz_model_e { @@ -104,15 +106,6 @@ static const MemoryRegionOps dma_dummy_ops = { #define MAGNUM_BIOS_SIZE_MAX 0x7e000 #define MAGNUM_BIOS_SIZE (BIOS_SIZE < MAGNUM_BIOS_SIZE_MAX ? BIOS_SIZE : MAGNUM_BIOS_SIZE_MAX) -static void cpu_request_exit(void *opaque, int irq, int level) -{ - CPUState *cpu = current_cpu; - - if (cpu && level) { - cpu_exit(cpu); - } -} - static CPUUnassignedAccess real_do_unassigned_access; static void mips_jazz_do_unassigned_access(CPUState *cpu, hwaddr addr, bool is_write, bool is_exec, @@ -150,7 +143,6 @@ static void mips_jazz_init(MachineState *machine, ISADevice *pit; DriveInfo *fds[MAX_FD]; qemu_irq esp_reset, dma_enable; - qemu_irq *cpu_exit_irq; MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios = g_new(MemoryRegion, 1); MemoryRegion *bios2 = g_new(MemoryRegion, 1); @@ -184,7 +176,7 @@ static void mips_jazz_init(MachineState *machine, memory_region_add_subregion(address_space, 0, ram); memory_region_init_ram(bios, NULL, "mips_jazz.bios", MAGNUM_BIOS_SIZE, - &error_abort); + &error_fatal); vmstate_register_ram_global(bios); memory_region_set_readonly(bios, true); memory_region_init_alias(bios2, NULL, "mips_jazz.bios", bios, @@ -229,13 +221,12 @@ static void mips_jazz_init(MachineState *machine, memory_region_init(isa_mem, NULL, "isa-mem", 0x01000000); memory_region_add_subregion(address_space, 0x90000000, isa_io); memory_region_add_subregion(address_space, 0x91000000, isa_mem); - isa_bus = isa_bus_new(NULL, isa_mem, isa_io); + isa_bus = isa_bus_new(NULL, isa_mem, isa_io, &error_abort); /* ISA devices */ i8259 = i8259_init(isa_bus, env->irq[4]); isa_bus_irqs(isa_bus, i8259); - cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1); - DMA_init(0, cpu_exit_irq); + DMA_init(isa_bus, 0); pit = pit_init(isa_bus, 0x40, 0, NULL); pcspk_init(isa_bus, pit); @@ -252,7 +243,7 @@ static void mips_jazz_init(MachineState *machine, /* Simple ROM, so user doesn't have to provide one */ MemoryRegion *rom_mr = g_new(MemoryRegion, 1); memory_region_init_ram(rom_mr, NULL, "g364fb.rom", 0x80000, - &error_abort); + &error_fatal); vmstate_register_ram_global(rom_mr); memory_region_set_readonly(rom_mr, true); uint8_t *rom = memory_region_get_ram_ptr(rom_mr); @@ -307,7 +298,8 @@ static void mips_jazz_init(MachineState *machine, for (n = 0; n < MAX_FD; n++) { fds[n] = drive_get(IF_FLOPPY, 0, n); } - fdctrl_init_sysbus(qdev_get_gpio_in(rc4030, 1), 0, 0x80003000, fds); + /* FIXME: we should enable DMA with a custom IsaDma device */ + fdctrl_init_sysbus(qdev_get_gpio_in(rc4030, 1), -1, 0x80003000, fds); /* Real time clock */ rtc_init(isa_bus, 1980, NULL); @@ -360,24 +352,40 @@ void mips_pica61_init(MachineState *machine) mips_jazz_init(machine, JAZZ_PICA61); } -static QEMUMachine mips_magnum_machine = { - .name = "magnum", - .desc = "MIPS Magnum", - .init = mips_magnum_init, - .block_default_type = IF_SCSI, +static void mips_magnum_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "MIPS Magnum"; + mc->init = mips_magnum_init; + mc->block_default_type = IF_SCSI; +} + +static const TypeInfo mips_magnum_type = { + .name = MACHINE_TYPE_NAME("magnum"), + .parent = TYPE_MACHINE, + .class_init = mips_magnum_class_init, }; -static QEMUMachine mips_pica61_machine = { - .name = "pica61", - .desc = "Acer Pica 61", - .init = mips_pica61_init, - .block_default_type = IF_SCSI, +static void mips_pica61_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Acer Pica 61"; + mc->init = mips_pica61_init; + mc->block_default_type = IF_SCSI; +} + +static const TypeInfo mips_pica61_type = { + .name = MACHINE_TYPE_NAME("pica61"), + .parent = TYPE_MACHINE, + .class_init = mips_pica61_class_init, }; static void mips_jazz_machine_init(void) { - qemu_register_machine(&mips_magnum_machine); - qemu_register_machine(&mips_pica61_machine); + type_register_static(&mips_magnum_type); + type_register_static(&mips_pica61_type); } -machine_init(mips_jazz_machine_init); +type_init(mips_jazz_machine_init) diff --git a/qemu/hw/mips/mips_malta.c b/qemu/hw/mips/mips_malta.c index 3082e7534..fa769e5c0 100644 --- a/qemu/hw/mips/mips_malta.c +++ b/qemu/hw/mips/mips_malta.c @@ -22,6 +22,9 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/char/serial.h" @@ -54,6 +57,7 @@ #include "hw/empty_slot.h" #include "sysemu/kvm.h" #include "exec/semihost.h" +#include "hw/mips/cps.h" //#define DEBUG_BOARD_INIT @@ -92,6 +96,7 @@ typedef struct { typedef struct { SysBusDevice parent_obj; + MIPSCPSState *cps; qemu_irq *i8259; } MaltaState; @@ -605,8 +610,8 @@ static void network_init(PCIBus *pci_bus) a3 - RAM size in bytes */ -static void write_bootloader (CPUMIPSState *env, uint8_t *base, - int64_t run_addr, int64_t kernel_entry) +static void write_bootloader(uint8_t *base, int64_t run_addr, + int64_t kernel_entry) { uint32_t *p; @@ -795,7 +800,7 @@ static int64_t load_kernel (void) if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL, (uint64_t *)&kernel_entry, NULL, (uint64_t *)&kernel_high, - big_endian, ELF_MACHINE, 1) < 0) { + big_endian, EM_MIPS, 1, 0) < 0) { fprintf(stderr, "qemu: could not load kernel '%s'\n", loaderparams.kernel_filename); exit(1); @@ -901,16 +906,77 @@ static void main_cpu_reset(void *opaque) if (kvm_enabled()) { /* Start running from the bootloader we wrote to end of RAM */ - env->active_tc.PC = 0x40000000 + loaderparams.ram_size; + env->active_tc.PC = 0x40000000 + loaderparams.ram_low_size; } } -static void cpu_request_exit(void *opaque, int irq, int level) +static void create_cpu_without_cps(const char *cpu_model, + qemu_irq *cbus_irq, qemu_irq *i8259_irq) { - CPUState *cpu = current_cpu; + CPUMIPSState *env; + MIPSCPU *cpu; + int i; - if (cpu && level) { - cpu_exit(cpu); + for (i = 0; i < smp_cpus; i++) { + cpu = cpu_mips_init(cpu_model); + if (cpu == NULL) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + env = &cpu->env; + + /* Init internal devices */ + cpu_mips_irq_init_cpu(env); + cpu_mips_clock_init(env); + qemu_register_reset(main_cpu_reset, cpu); + } + + cpu = MIPS_CPU(first_cpu); + env = &cpu->env; + *i8259_irq = env->irq[2]; + *cbus_irq = env->irq[4]; +} + +static void create_cps(MaltaState *s, const char *cpu_model, + qemu_irq *cbus_irq, qemu_irq *i8259_irq) +{ + Error *err = NULL; + s->cps = g_new0(MIPSCPSState, 1); + + object_initialize(s->cps, sizeof(MIPSCPSState), TYPE_MIPS_CPS); + qdev_set_parent_bus(DEVICE(s->cps), sysbus_get_default()); + + object_property_set_str(OBJECT(s->cps), cpu_model, "cpu-model", &err); + object_property_set_int(OBJECT(s->cps), smp_cpus, "num-vp", &err); + object_property_set_bool(OBJECT(s->cps), true, "realized", &err); + if (err != NULL) { + error_report("%s", error_get_pretty(err)); + exit(1); + } + + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s->cps), 0, 0, 1); + + /* FIXME: When GIC is present then we should use GIC's IRQ 3. + Until then CPS exposes CPU's IRQs thus use the default IRQ 2. */ + *i8259_irq = get_cps_irq(s->cps, 2); + *cbus_irq = NULL; +} + +static void create_cpu(MaltaState *s, const char *cpu_model, + qemu_irq *cbus_irq, qemu_irq *i8259_irq) +{ + if (cpu_model == NULL) { +#ifdef TARGET_MIPS64 + cpu_model = "20Kc"; +#else + cpu_model = "24Kf"; +#endif + } + + if ((smp_cpus > 1) && cpu_supports_cps_smp(cpu_model)) { + create_cps(s, cpu_model, cbus_irq, i8259_irq); + } else { + create_cpu_without_cps(cpu_model, cbus_irq, i8259_irq); } } @@ -919,7 +985,6 @@ void mips_malta_init(MachineState *machine) { ram_addr_t ram_size = machine->ram_size; ram_addr_t ram_low_size; - const char *cpu_model = machine->cpu_model; const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; @@ -936,10 +1001,8 @@ void mips_malta_init(MachineState *machine) int64_t kernel_entry, bootloader_run_addr; PCIBus *pci_bus; ISABus *isa_bus; - MIPSCPU *cpu; - CPUMIPSState *env; qemu_irq *isa_irq; - qemu_irq *cpu_exit_irq; + qemu_irq cbus_irq, i8259_irq; int piix4_devfn; I2CBus *smbus; int i; @@ -969,30 +1032,8 @@ void mips_malta_init(MachineState *machine) } } - /* init CPUs */ - if (cpu_model == NULL) { -#ifdef TARGET_MIPS64 - cpu_model = "20Kc"; -#else - cpu_model = "24Kf"; -#endif - } - - for (i = 0; i < smp_cpus; i++) { - cpu = cpu_mips_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - env = &cpu->env; - - /* Init internal devices */ - cpu_mips_irq_init_cpu(env); - cpu_mips_clock_init(env); - qemu_register_reset(main_cpu_reset, cpu); - } - cpu = MIPS_CPU(first_cpu); - env = &cpu->env; + /* create CPU */ + create_cpu(s, machine->cpu_model, &cbus_irq, &i8259_irq); /* allocate RAM */ if (ram_size > (2048u << 20)) { @@ -1033,7 +1074,7 @@ void mips_malta_init(MachineState *machine) #endif /* FPGA */ /* The CBUS UART is attached to the MIPS CPU INT2 pin, ie interrupt 4 */ - malta_fpga_init(system_memory, FPGA_ADDRESS, env->irq[4], serial_hds[2]); + malta_fpga_init(system_memory, FPGA_ADDRESS, cbus_irq, serial_hds[2]); /* Load firmware in flash / BIOS. */ dinfo = drive_get(IF_PFLASH, 0, fl_idx); @@ -1070,11 +1111,11 @@ void mips_malta_init(MachineState *machine) loaderparams.initrd_filename = initrd_filename; kernel_entry = load_kernel(); - write_bootloader(env, memory_region_get_ram_ptr(bios), + write_bootloader(memory_region_get_ram_ptr(bios), bootloader_run_addr, kernel_entry); if (kvm_enabled()) { /* Write the bootloader code @ the end of RAM, 1MB reserved */ - write_bootloader(env, memory_region_get_ram_ptr(ram_low_preio) + + write_bootloader(memory_region_get_ram_ptr(ram_low_preio) + ram_low_size, bootloader_run_addr, kernel_entry); } @@ -1130,7 +1171,7 @@ void mips_malta_init(MachineState *machine) * regions are not executable. */ memory_region_init_ram(bios_copy, NULL, "bios.1fc", BIOS_SIZE, - &error_abort); + &error_fatal); if (!rom_copy(memory_region_get_ram_ptr(bios_copy), FLASH_ADDRESS, BIOS_SIZE)) { memcpy(memory_region_get_ram_ptr(bios_copy), @@ -1142,10 +1183,6 @@ void mips_malta_init(MachineState *machine) /* Board ID = 0x420 (Malta Board with CoreLV) */ stl_p(memory_region_get_ram_ptr(bios_copy) + 0x10, 0x00000420); - /* Init internal devices */ - cpu_mips_irq_init_cpu(env); - cpu_mips_clock_init(env); - /* * We have a circular dependency problem: pci_bus depends on isa_irq, * isa_irq is provided by i8259, i8259 depends on ISA, ISA depends @@ -1165,7 +1202,7 @@ void mips_malta_init(MachineState *machine) /* Interrupt controller */ /* The 8259 is attached to the MIPS CPU INT0 pin, ie interrupt 2 */ - s->i8259 = i8259_init(isa_bus, env->irq[2]); + s->i8259 = i8259_init(isa_bus, i8259_irq); isa_bus_irqs(isa_bus, s->i8259); pci_piix4_ide_init(pci_bus, hd, piix4_devfn + 1); @@ -1175,8 +1212,7 @@ void mips_malta_init(MachineState *machine) smbus_eeprom_init(smbus, 8, smbus_eeprom_buf, smbus_eeprom_size); g_free(smbus_eeprom_buf); pit = pit_init(isa_bus, 0x40, 0, NULL); - cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1); - DMA_init(0, cpu_exit_irq); + DMA_init(isa_bus, 0); /* Super I/O */ isa_create_simple(isa_bus, "i8042"); @@ -1216,23 +1252,19 @@ static const TypeInfo mips_malta_device = { .class_init = mips_malta_class_init, }; -static QEMUMachine mips_malta_machine = { - .name = "malta", - .desc = "MIPS Malta Core LV", - .init = mips_malta_init, - .max_cpus = 16, - .is_default = 1, -}; - -static void mips_malta_register_types(void) +static void mips_malta_machine_init(MachineClass *mc) { - type_register_static(&mips_malta_device); + mc->desc = "MIPS Malta Core LV"; + mc->init = mips_malta_init; + mc->max_cpus = 16; + mc->is_default = 1; } -static void mips_malta_machine_init(void) +DEFINE_MACHINE("malta", mips_malta_machine_init) + +static void mips_malta_register_types(void) { - qemu_register_machine(&mips_malta_machine); + type_register_static(&mips_malta_device); } type_init(mips_malta_register_types) -machine_init(mips_malta_machine_init); diff --git a/qemu/hw/mips/mips_mipssim.c b/qemu/hw/mips/mips_mipssim.c index 61f74a631..a2c2a1646 100644 --- a/qemu/hw/mips/mips_mipssim.c +++ b/qemu/hw/mips/mips_mipssim.c @@ -24,6 +24,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/mips/mips.h" #include "hw/mips/cpudevs.h" @@ -69,7 +73,7 @@ static int64_t load_kernel(void) kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL, (uint64_t *)&entry, NULL, (uint64_t *)&kernel_high, big_endian, - ELF_MACHINE, 1); + EM_MIPS, 1, 0); if (kernel_size >= 0) { if ((entry & ~0x7fffffffULL) == 0x80000000) entry = (int32_t)entry; @@ -174,7 +178,7 @@ mips_mipssim_init(MachineState *machine) memory_region_allocate_system_memory(ram, NULL, "mips_mipssim.ram", ram_size); memory_region_init_ram(bios, NULL, "mips_mipssim.bios", BIOS_SIZE, - &error_abort); + &error_fatal); vmstate_register_ram_global(bios); memory_region_set_readonly(bios, true); @@ -231,15 +235,10 @@ mips_mipssim_init(MachineState *machine) mipsnet_init(0x4200, env->irq[2], &nd_table[0]); } -static QEMUMachine mips_mipssim_machine = { - .name = "mipssim", - .desc = "MIPS MIPSsim platform", - .init = mips_mipssim_init, -}; - -static void mips_mipssim_machine_init(void) +static void mips_mipssim_machine_init(MachineClass *mc) { - qemu_register_machine(&mips_mipssim_machine); + mc->desc = "MIPS MIPSsim platform"; + mc->init = mips_mipssim_init; } -machine_init(mips_mipssim_machine_init); +DEFINE_MACHINE("mipssim", mips_mipssim_machine_init) diff --git a/qemu/hw/mips/mips_r4k.c b/qemu/hw/mips/mips_r4k.c index f4dcacd86..21aca981c 100644 --- a/qemu/hw/mips/mips_r4k.c +++ b/qemu/hw/mips/mips_r4k.c @@ -7,6 +7,10 @@ * All peripherial devices are attached to this "bus" with * the standard PC ISA addresses. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/mips/mips.h" #include "hw/mips/cpudevs.h" @@ -87,7 +91,7 @@ static int64_t load_kernel(void) kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL, (uint64_t *)&entry, NULL, (uint64_t *)&kernel_high, big_endian, - ELF_MACHINE, 1); + EM_MIPS, 1, 0); if (kernel_size >= 0) { if ((entry & ~0x7fffffffULL) == 0x80000000) entry = (int32_t)entry; @@ -233,7 +237,7 @@ void mips_r4k_init(MachineState *machine) if ((bios_size > 0) && (bios_size <= BIOS_SIZE)) { bios = g_new(MemoryRegion, 1); memory_region_init_ram(bios, NULL, "mips_r4k.bios", BIOS_SIZE, - &error_abort); + &error_fatal); vmstate_register_ram_global(bios); memory_region_set_readonly(bios, true); memory_region_add_subregion(get_system_memory(), 0x1fc00000, bios); @@ -252,9 +256,7 @@ void mips_r4k_init(MachineState *machine) fprintf(stderr, "qemu: Warning, could not load MIPS bios '%s'\n", bios_name); } - if (filename) { - g_free(filename); - } + g_free(filename); if (kernel_filename) { loaderparams.ram_size = ram_size; @@ -274,7 +276,7 @@ void mips_r4k_init(MachineState *machine) memory_region_init(isa_mem, NULL, "isa-mem", 0x01000000); memory_region_add_subregion(get_system_memory(), 0x14000000, isa_io); memory_region_add_subregion(get_system_memory(), 0x10000000, isa_mem); - isa_bus = isa_bus_new(NULL, isa_mem, get_system_io()); + isa_bus = isa_bus_new(NULL, isa_mem, get_system_io(), &error_abort); /* The PIC is attached to the MIPS CPU INT0 pin */ i8259 = i8259_init(isa_bus, env->irq[2]); @@ -300,15 +302,10 @@ void mips_r4k_init(MachineState *machine) isa_create_simple(isa_bus, "i8042"); } -static QEMUMachine mips_machine = { - .name = "mips", - .desc = "mips r4k platform", - .init = mips_r4k_init, -}; - -static void mips_machine_init(void) +static void mips_machine_init(MachineClass *mc) { - qemu_register_machine(&mips_machine); + mc->desc = "mips r4k platform"; + mc->init = mips_r4k_init; } -machine_init(mips_machine_init); +DEFINE_MACHINE("mips", mips_machine_init) diff --git a/qemu/hw/misc/Makefile.objs b/qemu/hw/misc/Makefile.objs index 4aa76ffec..93f952880 100644 --- a/qemu/hw/misc/Makefile.objs +++ b/qemu/hw/misc/Makefile.objs @@ -26,6 +26,9 @@ obj-$(CONFIG_NSERIES) += cbus.o obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o obj-$(CONFIG_IMX) += imx_ccm.o +obj-$(CONFIG_IMX) += imx31_ccm.o +obj-$(CONFIG_IMX) += imx25_ccm.o +obj-$(CONFIG_IMX) += imx6_ccm.o obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o obj-$(CONFIG_MAINSTONE) += mst_fpga.o @@ -34,9 +37,16 @@ obj-$(CONFIG_OMAP) += omap_gpmc.o obj-$(CONFIG_OMAP) += omap_l4.o obj-$(CONFIG_OMAP) += omap_sdrc.o obj-$(CONFIG_OMAP) += omap_tap.o +obj-$(CONFIG_RASPI) += bcm2835_mbox.o +obj-$(CONFIG_RASPI) += bcm2835_property.o obj-$(CONFIG_SLAVIO) += slavio_misc.o obj-$(CONFIG_ZYNQ) += zynq_slcr.o +obj-$(CONFIG_ZYNQ) += zynq-xadc.o obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o +obj-$(CONFIG_MIPS_CPS) += mips_cmgcr.o +obj-$(CONFIG_MIPS_CPS) += mips_cpc.o +obj-$(CONFIG_MIPS_ITU) += mips_itu.o obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_EDU) += edu.o +obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o diff --git a/qemu/hw/misc/a9scu.c b/qemu/hw/misc/a9scu.c index 443494590..3e8ad8cd7 100644 --- a/qemu/hw/misc/a9scu.c +++ b/qemu/hw/misc/a9scu.c @@ -8,6 +8,7 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" #include "hw/misc/a9scu.h" static uint64_t a9_scu_read(void *opaque, hwaddr offset, diff --git a/qemu/hw/misc/applesmc.c b/qemu/hw/misc/applesmc.c index 6bd61e782..77fab5b9d 100644 --- a/qemu/hw/misc/applesmc.c +++ b/qemu/hw/misc/applesmc.c @@ -30,6 +30,7 @@ * */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/isa/isa.h" #include "ui/console.h" diff --git a/qemu/hw/misc/arm11scu.c b/qemu/hw/misc/arm11scu.c index a79167544..5e54b494b 100644 --- a/qemu/hw/misc/arm11scu.c +++ b/qemu/hw/misc/arm11scu.c @@ -8,6 +8,7 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" #include "hw/misc/arm11scu.h" static uint64_t mpcore_scu_read(void *opaque, hwaddr offset, diff --git a/qemu/hw/misc/arm_integrator_debug.c b/qemu/hw/misc/arm_integrator_debug.c index 99b720fbb..902605fef 100644 --- a/qemu/hw/misc/arm_integrator_debug.c +++ b/qemu/hw/misc/arm_integrator_debug.c @@ -14,6 +14,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "exec/address-spaces.h" @@ -79,7 +80,7 @@ static void intdbg_control_init(Object *obj) SysBusDevice *sd = SYS_BUS_DEVICE(obj); IntegratorDebugState *s = INTEGRATOR_DEBUG(obj); - memory_region_init_io(&s->iomem, NULL, &intdbg_control_ops, + memory_region_init_io(&s->iomem, obj, &intdbg_control_ops, NULL, "dbg-leds", 0x1000000); sysbus_init_mmio(sd, &s->iomem); } diff --git a/qemu/hw/misc/arm_l2x0.c b/qemu/hw/misc/arm_l2x0.c index 9e220c9a5..7e179f1a4 100644 --- a/qemu/hw/misc/arm_l2x0.c +++ b/qemu/hw/misc/arm_l2x0.c @@ -18,6 +18,7 @@ * */ +#include "qemu/osdep.h" #include "hw/sysbus.h" /* L2C-310 r3p2 */ diff --git a/qemu/hw/misc/arm_sysctl.c b/qemu/hw/misc/arm_sysctl.c index 3fad6f86d..34d90d523 100644 --- a/qemu/hw/misc/arm_sysctl.c +++ b/qemu/hw/misc/arm_sysctl.c @@ -7,6 +7,7 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "qemu/timer.h" #include "qemu/bitops.h" @@ -170,7 +171,8 @@ static uint64_t arm_sysctl_read(void *opaque, hwaddr offset, case 0x58: /* BOOTCS */ return 0; case 0x5c: /* 24MHz */ - return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24000000, get_ticks_per_sec()); + return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24000000, + NANOSECONDS_PER_SECOND); case 0x60: /* MISC */ return 0; case 0x84: /* PROCID0 */ diff --git a/qemu/hw/misc/bcm2835_mbox.c b/qemu/hw/misc/bcm2835_mbox.c new file mode 100644 index 000000000..263280fd4 --- /dev/null +++ b/qemu/hw/misc/bcm2835_mbox.c @@ -0,0 +1,335 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + * + * This file models the system mailboxes, which are used for + * communication with low-bandwidth GPU peripherals. Refs: + * https://github.com/raspberrypi/firmware/wiki/Mailboxes + * https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/misc/bcm2835_mbox.h" + +#define MAIL0_PEEK 0x90 +#define MAIL0_SENDER 0x94 +#define MAIL1_STATUS 0xb8 + +/* Mailbox status register */ +#define MAIL0_STATUS 0x98 +#define ARM_MS_FULL 0x80000000 +#define ARM_MS_EMPTY 0x40000000 +#define ARM_MS_LEVEL 0x400000FF /* Max. value depends on mailbox depth */ + +/* MAILBOX config/status register */ +#define MAIL0_CONFIG 0x9c +/* ANY write to this register clears the error bits! */ +#define ARM_MC_IHAVEDATAIRQEN 0x00000001 /* mbox irq enable: has data */ +#define ARM_MC_IHAVESPACEIRQEN 0x00000002 /* mbox irq enable: has space */ +#define ARM_MC_OPPISEMPTYIRQEN 0x00000004 /* mbox irq enable: Opp is empty */ +#define ARM_MC_MAIL_CLEAR 0x00000008 /* mbox clear write 1, then 0 */ +#define ARM_MC_IHAVEDATAIRQPEND 0x00000010 /* mbox irq pending: has space */ +#define ARM_MC_IHAVESPACEIRQPEND 0x00000020 /* mbox irq pending: Opp is empty */ +#define ARM_MC_OPPISEMPTYIRQPEND 0x00000040 /* mbox irq pending */ +/* Bit 7 is unused */ +#define ARM_MC_ERRNOOWN 0x00000100 /* error : none owner read from mailbox */ +#define ARM_MC_ERROVERFLW 0x00000200 /* error : write to fill mailbox */ +#define ARM_MC_ERRUNDRFLW 0x00000400 /* error : read from empty mailbox */ + +static void mbox_update_status(BCM2835Mbox *mb) +{ + mb->status &= ~(ARM_MS_EMPTY | ARM_MS_FULL); + if (mb->count == 0) { + mb->status |= ARM_MS_EMPTY; + } else if (mb->count == MBOX_SIZE) { + mb->status |= ARM_MS_FULL; + } +} + +static void mbox_reset(BCM2835Mbox *mb) +{ + int n; + + mb->count = 0; + mb->config = 0; + for (n = 0; n < MBOX_SIZE; n++) { + mb->reg[n] = MBOX_INVALID_DATA; + } + mbox_update_status(mb); +} + +static uint32_t mbox_pull(BCM2835Mbox *mb, int index) +{ + int n; + uint32_t val; + + assert(mb->count > 0); + assert(index < mb->count); + + val = mb->reg[index]; + for (n = index + 1; n < mb->count; n++) { + mb->reg[n - 1] = mb->reg[n]; + } + mb->count--; + mb->reg[mb->count] = MBOX_INVALID_DATA; + + mbox_update_status(mb); + + return val; +} + +static void mbox_push(BCM2835Mbox *mb, uint32_t val) +{ + assert(mb->count < MBOX_SIZE); + mb->reg[mb->count++] = val; + mbox_update_status(mb); +} + +static void bcm2835_mbox_update(BCM2835MboxState *s) +{ + uint32_t value; + bool set; + int n; + + s->mbox_irq_disabled = true; + + /* Get pending responses and put them in the vc->arm mbox, + * as long as it's not full + */ + for (n = 0; n < MBOX_CHAN_COUNT; n++) { + while (s->available[n] && !(s->mbox[0].status & ARM_MS_FULL)) { + value = ldl_le_phys(&s->mbox_as, n << MBOX_AS_CHAN_SHIFT); + assert(value != MBOX_INVALID_DATA); /* Pending interrupt but no data */ + mbox_push(&s->mbox[0], value); + } + } + + /* TODO (?): Try to push pending requests from the arm->vc mbox */ + + /* Re-enable calls from the IRQ routine */ + s->mbox_irq_disabled = false; + + /* Update ARM IRQ status */ + set = false; + s->mbox[0].config &= ~ARM_MC_IHAVEDATAIRQPEND; + if (!(s->mbox[0].status & ARM_MS_EMPTY)) { + s->mbox[0].config |= ARM_MC_IHAVEDATAIRQPEND; + if (s->mbox[0].config & ARM_MC_IHAVEDATAIRQEN) { + set = true; + } + } + qemu_set_irq(s->arm_irq, set); +} + +static void bcm2835_mbox_set_irq(void *opaque, int irq, int level) +{ + BCM2835MboxState *s = opaque; + + s->available[irq] = level; + + /* avoid recursively calling bcm2835_mbox_update when the interrupt + * status changes due to the ldl_phys call within that function + */ + if (!s->mbox_irq_disabled) { + bcm2835_mbox_update(s); + } +} + +static uint64_t bcm2835_mbox_read(void *opaque, hwaddr offset, unsigned size) +{ + BCM2835MboxState *s = opaque; + uint32_t res = 0; + + offset &= 0xff; + + switch (offset) { + case 0x80 ... 0x8c: /* MAIL0_READ */ + if (s->mbox[0].status & ARM_MS_EMPTY) { + res = MBOX_INVALID_DATA; + } else { + res = mbox_pull(&s->mbox[0], 0); + } + break; + + case MAIL0_PEEK: + res = s->mbox[0].reg[0]; + break; + + case MAIL0_SENDER: + break; + + case MAIL0_STATUS: + res = s->mbox[0].status; + break; + + case MAIL0_CONFIG: + res = s->mbox[0].config; + break; + + case MAIL1_STATUS: + res = s->mbox[1].status; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return 0; + } + + bcm2835_mbox_update(s); + + return res; +} + +static void bcm2835_mbox_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + BCM2835MboxState *s = opaque; + hwaddr childaddr; + uint8_t ch; + + offset &= 0xff; + + switch (offset) { + case MAIL0_SENDER: + break; + + case MAIL0_CONFIG: + s->mbox[0].config &= ~ARM_MC_IHAVEDATAIRQEN; + s->mbox[0].config |= value & ARM_MC_IHAVEDATAIRQEN; + break; + + case 0xa0 ... 0xac: /* MAIL1_WRITE */ + if (s->mbox[1].status & ARM_MS_FULL) { + /* Mailbox full */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: mailbox full\n", __func__); + } else { + ch = value & 0xf; + if (ch < MBOX_CHAN_COUNT) { + childaddr = ch << MBOX_AS_CHAN_SHIFT; + if (ldl_le_phys(&s->mbox_as, childaddr + MBOX_AS_PENDING)) { + /* Child busy, push delayed. Push it in the arm->vc mbox */ + mbox_push(&s->mbox[1], value); + } else { + /* Push it directly to the child device */ + stl_le_phys(&s->mbox_as, childaddr, value); + } + } else { + /* Invalid channel number */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid channel %u\n", + __func__, ch); + } + } + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return; + } + + bcm2835_mbox_update(s); +} + +static const MemoryRegionOps bcm2835_mbox_ops = { + .read = bcm2835_mbox_read, + .write = bcm2835_mbox_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +/* vmstate of a single mailbox */ +static const VMStateDescription vmstate_bcm2835_mbox_box = { + .name = TYPE_BCM2835_MBOX "_box", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(reg, BCM2835Mbox, MBOX_SIZE), + VMSTATE_UINT32(count, BCM2835Mbox), + VMSTATE_UINT32(status, BCM2835Mbox), + VMSTATE_UINT32(config, BCM2835Mbox), + VMSTATE_END_OF_LIST() + } +}; + +/* vmstate of the entire device */ +static const VMStateDescription vmstate_bcm2835_mbox = { + .name = TYPE_BCM2835_MBOX, + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL_ARRAY(available, BCM2835MboxState, MBOX_CHAN_COUNT), + VMSTATE_STRUCT_ARRAY(mbox, BCM2835MboxState, 2, 1, + vmstate_bcm2835_mbox_box, BCM2835Mbox), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2835_mbox_init(Object *obj) +{ + BCM2835MboxState *s = BCM2835_MBOX(obj); + + memory_region_init_io(&s->iomem, obj, &bcm2835_mbox_ops, s, + TYPE_BCM2835_MBOX, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->arm_irq); + qdev_init_gpio_in(DEVICE(s), bcm2835_mbox_set_irq, MBOX_CHAN_COUNT); +} + +static void bcm2835_mbox_reset(DeviceState *dev) +{ + BCM2835MboxState *s = BCM2835_MBOX(dev); + int n; + + mbox_reset(&s->mbox[0]); + mbox_reset(&s->mbox[1]); + s->mbox_irq_disabled = false; + for (n = 0; n < MBOX_CHAN_COUNT; n++) { + s->available[n] = false; + } +} + +static void bcm2835_mbox_realize(DeviceState *dev, Error **errp) +{ + BCM2835MboxState *s = BCM2835_MBOX(dev); + Object *obj; + Error *err = NULL; + + obj = object_property_get_link(OBJECT(dev), "mbox-mr", &err); + if (obj == NULL) { + error_setg(errp, "%s: required mbox-mr link not found: %s", + __func__, error_get_pretty(err)); + return; + } + + s->mbox_mr = MEMORY_REGION(obj); + address_space_init(&s->mbox_as, s->mbox_mr, NULL); + bcm2835_mbox_reset(dev); +} + +static void bcm2835_mbox_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = bcm2835_mbox_realize; + dc->reset = bcm2835_mbox_reset; + dc->vmsd = &vmstate_bcm2835_mbox; +} + +static TypeInfo bcm2835_mbox_info = { + .name = TYPE_BCM2835_MBOX, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835MboxState), + .class_init = bcm2835_mbox_class_init, + .instance_init = bcm2835_mbox_init, +}; + +static void bcm2835_mbox_register_types(void) +{ + type_register_static(&bcm2835_mbox_info); +} + +type_init(bcm2835_mbox_register_types) diff --git a/qemu/hw/misc/bcm2835_property.c b/qemu/hw/misc/bcm2835_property.c new file mode 100644 index 000000000..530411f84 --- /dev/null +++ b/qemu/hw/misc/bcm2835_property.c @@ -0,0 +1,424 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/misc/bcm2835_property.h" +#include "hw/misc/bcm2835_mbox_defs.h" +#include "sysemu/dma.h" + +/* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */ + +static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) +{ + uint32_t tag; + uint32_t bufsize; + uint32_t tot_len; + size_t resplen; + uint32_t tmp; + int n; + uint32_t offset, length, color; + uint32_t xres, yres, xoffset, yoffset, bpp, pixo, alpha; + uint32_t *newxres = NULL, *newyres = NULL, *newxoffset = NULL, + *newyoffset = NULL, *newbpp = NULL, *newpixo = NULL, *newalpha = NULL; + + value &= ~0xf; + + s->addr = value; + + tot_len = ldl_le_phys(&s->dma_as, value); + + /* @(addr + 4) : Buffer response code */ + value = s->addr + 8; + while (value + 8 <= s->addr + tot_len) { + tag = ldl_le_phys(&s->dma_as, value); + bufsize = ldl_le_phys(&s->dma_as, value + 4); + /* @(value + 8) : Request/response indicator */ + resplen = 0; + switch (tag) { + case 0x00000000: /* End tag */ + break; + case 0x00000001: /* Get firmware revision */ + stl_le_phys(&s->dma_as, value + 12, 346337); + resplen = 4; + break; + case 0x00010001: /* Get board model */ + qemu_log_mask(LOG_UNIMP, + "bcm2835_property: %x get board model NYI\n", tag); + resplen = 4; + break; + case 0x00010002: /* Get board revision */ + stl_le_phys(&s->dma_as, value + 12, s->board_rev); + resplen = 4; + break; + case 0x00010003: /* Get board MAC address */ + resplen = sizeof(s->macaddr.a); + dma_memory_write(&s->dma_as, value + 12, s->macaddr.a, resplen); + break; + case 0x00010004: /* Get board serial */ + qemu_log_mask(LOG_UNIMP, + "bcm2835_property: %x get board serial NYI\n", tag); + resplen = 8; + break; + case 0x00010005: /* Get ARM memory */ + /* base */ + stl_le_phys(&s->dma_as, value + 12, 0); + /* size */ + stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_base); + resplen = 8; + break; + case 0x00010006: /* Get VC memory */ + /* base */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->vcram_base); + /* size */ + stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_size); + resplen = 8; + break; + case 0x00028001: /* Set power state */ + /* Assume that whatever device they asked for exists, + * and we'll just claim we set it to the desired state + */ + tmp = ldl_le_phys(&s->dma_as, value + 16); + stl_le_phys(&s->dma_as, value + 16, (tmp & 1)); + resplen = 8; + break; + + /* Clocks */ + + case 0x00030001: /* Get clock state */ + stl_le_phys(&s->dma_as, value + 16, 0x1); + resplen = 8; + break; + + case 0x00038001: /* Set clock state */ + qemu_log_mask(LOG_UNIMP, + "bcm2835_property: %x set clock state NYI\n", tag); + resplen = 8; + break; + + case 0x00030002: /* Get clock rate */ + case 0x00030004: /* Get max clock rate */ + case 0x00030007: /* Get min clock rate */ + switch (ldl_le_phys(&s->dma_as, value + 12)) { + case 1: /* EMMC */ + stl_le_phys(&s->dma_as, value + 16, 50000000); + break; + case 2: /* UART */ + stl_le_phys(&s->dma_as, value + 16, 3000000); + break; + default: + stl_le_phys(&s->dma_as, value + 16, 700000000); + break; + } + resplen = 8; + break; + + case 0x00038002: /* Set clock rate */ + case 0x00038004: /* Set max clock rate */ + case 0x00038007: /* Set min clock rate */ + qemu_log_mask(LOG_UNIMP, + "bcm2835_property: %x set clock rates NYI\n", tag); + resplen = 8; + break; + + /* Temperature */ + + case 0x00030006: /* Get temperature */ + stl_le_phys(&s->dma_as, value + 16, 25000); + resplen = 8; + break; + + case 0x0003000A: /* Get max temperature */ + stl_le_phys(&s->dma_as, value + 16, 99000); + resplen = 8; + break; + + /* Frame buffer */ + + case 0x00040001: /* Allocate buffer */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->base); + stl_le_phys(&s->dma_as, value + 16, s->fbdev->size); + resplen = 8; + break; + case 0x00048001: /* Release buffer */ + resplen = 0; + break; + case 0x00040002: /* Blank screen */ + resplen = 4; + break; + case 0x00040003: /* Get display width/height */ + case 0x00040004: + stl_le_phys(&s->dma_as, value + 12, s->fbdev->xres); + stl_le_phys(&s->dma_as, value + 16, s->fbdev->yres); + resplen = 8; + break; + case 0x00044003: /* Test display width/height */ + case 0x00044004: + resplen = 8; + break; + case 0x00048003: /* Set display width/height */ + case 0x00048004: + xres = ldl_le_phys(&s->dma_as, value + 12); + newxres = &xres; + yres = ldl_le_phys(&s->dma_as, value + 16); + newyres = &yres; + resplen = 8; + break; + case 0x00040005: /* Get depth */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->bpp); + resplen = 4; + break; + case 0x00044005: /* Test depth */ + resplen = 4; + break; + case 0x00048005: /* Set depth */ + bpp = ldl_le_phys(&s->dma_as, value + 12); + newbpp = &bpp; + resplen = 4; + break; + case 0x00040006: /* Get pixel order */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->pixo); + resplen = 4; + break; + case 0x00044006: /* Test pixel order */ + resplen = 4; + break; + case 0x00048006: /* Set pixel order */ + pixo = ldl_le_phys(&s->dma_as, value + 12); + newpixo = &pixo; + resplen = 4; + break; + case 0x00040007: /* Get alpha */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->alpha); + resplen = 4; + break; + case 0x00044007: /* Test pixel alpha */ + resplen = 4; + break; + case 0x00048007: /* Set alpha */ + alpha = ldl_le_phys(&s->dma_as, value + 12); + newalpha = α + resplen = 4; + break; + case 0x00040008: /* Get pitch */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->pitch); + resplen = 4; + break; + case 0x00040009: /* Get virtual offset */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->xoffset); + stl_le_phys(&s->dma_as, value + 16, s->fbdev->yoffset); + resplen = 8; + break; + case 0x00044009: /* Test virtual offset */ + resplen = 8; + break; + case 0x00048009: /* Set virtual offset */ + xoffset = ldl_le_phys(&s->dma_as, value + 12); + newxoffset = &xoffset; + yoffset = ldl_le_phys(&s->dma_as, value + 16); + newyoffset = &yoffset; + resplen = 8; + break; + case 0x0004000a: /* Get/Test/Set overscan */ + case 0x0004400a: + case 0x0004800a: + stl_le_phys(&s->dma_as, value + 12, 0); + stl_le_phys(&s->dma_as, value + 16, 0); + stl_le_phys(&s->dma_as, value + 20, 0); + stl_le_phys(&s->dma_as, value + 24, 0); + resplen = 16; + break; + case 0x0004800b: /* Set palette */ + offset = ldl_le_phys(&s->dma_as, value + 12); + length = ldl_le_phys(&s->dma_as, value + 16); + n = 0; + while (n < length - offset) { + color = ldl_le_phys(&s->dma_as, value + 20 + (n << 2)); + stl_le_phys(&s->dma_as, + s->fbdev->vcram_base + ((offset + n) << 2), color); + n++; + } + stl_le_phys(&s->dma_as, value + 12, 0); + resplen = 4; + break; + + case 0x00060001: /* Get DMA channels */ + /* channels 2-5 */ + stl_le_phys(&s->dma_as, value + 12, 0x003C); + resplen = 4; + break; + + case 0x00050001: /* Get command line */ + resplen = 0; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "bcm2835_property: unhandled tag %08x\n", tag); + break; + } + + if (tag == 0) { + break; + } + + stl_le_phys(&s->dma_as, value + 8, (1 << 31) | resplen); + value += bufsize + 12; + } + + /* Reconfigure framebuffer if required */ + if (newxres || newyres || newxoffset || newyoffset || newbpp || newpixo + || newalpha) { + bcm2835_fb_reconfigure(s->fbdev, newxres, newyres, newxoffset, + newyoffset, newbpp, newpixo, newalpha); + } + + /* Buffer response code */ + stl_le_phys(&s->dma_as, s->addr + 4, (1 << 31)); +} + +static uint64_t bcm2835_property_read(void *opaque, hwaddr offset, + unsigned size) +{ + BCM2835PropertyState *s = opaque; + uint32_t res = 0; + + switch (offset) { + case MBOX_AS_DATA: + res = MBOX_CHAN_PROPERTY | s->addr; + s->pending = false; + qemu_set_irq(s->mbox_irq, 0); + break; + + case MBOX_AS_PENDING: + res = s->pending; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return 0; + } + + return res; +} + +static void bcm2835_property_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + BCM2835PropertyState *s = opaque; + + switch (offset) { + case MBOX_AS_DATA: + /* bcm2835_mbox should check our pending status before pushing */ + assert(!s->pending); + s->pending = true; + bcm2835_property_mbox_push(s, value); + qemu_set_irq(s->mbox_irq, 1); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return; + } +} + +static const MemoryRegionOps bcm2835_property_ops = { + .read = bcm2835_property_read, + .write = bcm2835_property_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static const VMStateDescription vmstate_bcm2835_property = { + .name = TYPE_BCM2835_PROPERTY, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_MACADDR(macaddr, BCM2835PropertyState), + VMSTATE_UINT32(addr, BCM2835PropertyState), + VMSTATE_BOOL(pending, BCM2835PropertyState), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2835_property_init(Object *obj) +{ + BCM2835PropertyState *s = BCM2835_PROPERTY(obj); + + memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_property_ops, s, + TYPE_BCM2835_PROPERTY, 0x10); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq); +} + +static void bcm2835_property_reset(DeviceState *dev) +{ + BCM2835PropertyState *s = BCM2835_PROPERTY(dev); + + s->pending = false; +} + +static void bcm2835_property_realize(DeviceState *dev, Error **errp) +{ + BCM2835PropertyState *s = BCM2835_PROPERTY(dev); + Object *obj; + Error *err = NULL; + + obj = object_property_get_link(OBJECT(dev), "fb", &err); + if (obj == NULL) { + error_setg(errp, "%s: required fb link not found: %s", + __func__, error_get_pretty(err)); + return; + } + + s->fbdev = BCM2835_FB(obj); + + obj = object_property_get_link(OBJECT(dev), "dma-mr", &err); + if (obj == NULL) { + error_setg(errp, "%s: required dma-mr link not found: %s", + __func__, error_get_pretty(err)); + return; + } + + s->dma_mr = MEMORY_REGION(obj); + address_space_init(&s->dma_as, s->dma_mr, NULL); + + /* TODO: connect to MAC address of USB NIC device, once we emulate it */ + qemu_macaddr_default_if_unset(&s->macaddr); + + bcm2835_property_reset(dev); +} + +static Property bcm2835_property_props[] = { + DEFINE_PROP_UINT32("board-rev", BCM2835PropertyState, board_rev, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void bcm2835_property_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = bcm2835_property_props; + dc->realize = bcm2835_property_realize; + dc->vmsd = &vmstate_bcm2835_property; +} + +static TypeInfo bcm2835_property_info = { + .name = TYPE_BCM2835_PROPERTY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835PropertyState), + .class_init = bcm2835_property_class_init, + .instance_init = bcm2835_property_init, +}; + +static void bcm2835_property_register_types(void) +{ + type_register_static(&bcm2835_property_info); +} + +type_init(bcm2835_property_register_types) diff --git a/qemu/hw/misc/cbus.c b/qemu/hw/misc/cbus.c index 495d5078f..0c207e310 100644 --- a/qemu/hw/misc/cbus.c +++ b/qemu/hw/misc/cbus.c @@ -20,7 +20,8 @@ * with this program; if not, see . */ -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "hw/hw.h" #include "hw/irq.h" #include "hw/devices.h" #include "sysemu/sysemu.h" diff --git a/qemu/hw/misc/debugexit.c b/qemu/hw/misc/debugexit.c index 69a1b004c..84fa1a5b9 100644 --- a/qemu/hw/misc/debugexit.c +++ b/qemu/hw/misc/debugexit.c @@ -7,6 +7,7 @@ * (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/isa/isa.h" diff --git a/qemu/hw/misc/eccmemctl.c b/qemu/hw/misc/eccmemctl.c index 8bad6f682..a0071f3ea 100644 --- a/qemu/hw/misc/eccmemctl.c +++ b/qemu/hw/misc/eccmemctl.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "trace.h" diff --git a/qemu/hw/misc/edu.c b/qemu/hw/misc/edu.c index fe50b42af..888ba49a0 100644 --- a/qemu/hw/misc/edu.c +++ b/qemu/hw/misc/edu.c @@ -22,6 +22,7 @@ * DEALINGS IN THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/pci/pci.h" #include "qemu/timer.h" #include "qemu/main-loop.h" /* iothread mutex */ @@ -327,7 +328,7 @@ static void *edu_fact_thread(void *opaque) return NULL; } -static int pci_edu_init(PCIDevice *pdev) +static void pci_edu_realize(PCIDevice *pdev, Error **errp) { EduState *edu = DO_UPCAST(EduState, pdev, pdev); uint8_t *pci_conf = pdev->config; @@ -344,8 +345,6 @@ static int pci_edu_init(PCIDevice *pdev) memory_region_init_io(&edu->mmio, OBJECT(edu), &edu_mmio_ops, edu, "edu-mmio", 1 << 20); pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &edu->mmio); - - return 0; } static void pci_edu_uninit(PCIDevice *pdev) @@ -364,12 +363,12 @@ static void pci_edu_uninit(PCIDevice *pdev) timer_del(&edu->dma_timer); } -static void edu_obj_uint64(Object *obj, struct Visitor *v, void *opaque, - const char *name, Error **errp) +static void edu_obj_uint64(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { uint64_t *val = opaque; - visit_type_uint64(v, val, name, errp); + visit_type_uint64(v, name, val, errp); } static void edu_instance_init(Object *obj) @@ -385,7 +384,7 @@ static void edu_class_init(ObjectClass *class, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(class); - k->init = pci_edu_init; + k->realize = pci_edu_realize; k->exit = pci_edu_uninit; k->vendor_id = PCI_VENDOR_ID_QEMU; k->device_id = 0x11e8; diff --git a/qemu/hw/misc/exynos4210_pmu.c b/qemu/hw/misc/exynos4210_pmu.c index 2b118c725..889abadfe 100644 --- a/qemu/hw/misc/exynos4210_pmu.c +++ b/qemu/hw/misc/exynos4210_pmu.c @@ -24,6 +24,7 @@ * uses PMU INFORM5 register as a holding pen. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #ifndef DEBUG_PMU diff --git a/qemu/hw/misc/hyperv_testdev.c b/qemu/hw/misc/hyperv_testdev.c new file mode 100644 index 000000000..1883fd7f2 --- /dev/null +++ b/qemu/hw/misc/hyperv_testdev.c @@ -0,0 +1,168 @@ +/* + * QEMU KVM Hyper-V test device to support Hyper-V kvm-unit-tests + * + * Copyright (C) 2015 Andrey Smetanin + * + * Authors: + * Andrey Smetanin + * + * 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 "hw/hw.h" +#include "hw/qdev.h" +#include "hw/isa/isa.h" +#include "sysemu/kvm.h" +#include "linux/kvm.h" +#include "target-i386/hyperv.h" +#include "kvm_i386.h" + +#define HV_TEST_DEV_MAX_SINT_ROUTES 64 + +struct HypervTestDev { + ISADevice parent_obj; + MemoryRegion sint_control; + HvSintRoute *sint_route[HV_TEST_DEV_MAX_SINT_ROUTES]; +}; +typedef struct HypervTestDev HypervTestDev; + +#define TYPE_HYPERV_TEST_DEV "hyperv-testdev" +#define HYPERV_TEST_DEV(obj) \ + OBJECT_CHECK(HypervTestDev, (obj), TYPE_HYPERV_TEST_DEV) + +enum { + HV_TEST_DEV_SINT_ROUTE_CREATE = 1, + HV_TEST_DEV_SINT_ROUTE_DESTROY, + HV_TEST_DEV_SINT_ROUTE_SET_SINT +}; + +static int alloc_sint_route_index(HypervTestDev *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dev->sint_route); i++) { + if (dev->sint_route[i] == NULL) { + return i; + } + } + return -1; +} + +static void free_sint_route_index(HypervTestDev *dev, int i) +{ + assert(i >= 0 && i < ARRAY_SIZE(dev->sint_route)); + dev->sint_route[i] = NULL; +} + +static int find_sint_route_index(HypervTestDev *dev, uint32_t vcpu_id, + uint32_t sint) +{ + HvSintRoute *sint_route; + int i; + + for (i = 0; i < ARRAY_SIZE(dev->sint_route); i++) { + sint_route = dev->sint_route[i]; + if (sint_route && sint_route->vcpu_id == vcpu_id && + sint_route->sint == sint) { + return i; + } + } + return -1; +} + +static void hv_synic_test_dev_control(HypervTestDev *dev, uint32_t ctl, + uint32_t vcpu_id, uint32_t sint) +{ + int i; + HvSintRoute *sint_route; + + switch (ctl) { + case HV_TEST_DEV_SINT_ROUTE_CREATE: + i = alloc_sint_route_index(dev); + assert(i >= 0); + sint_route = kvm_hv_sint_route_create(vcpu_id, sint, NULL); + assert(sint_route); + dev->sint_route[i] = sint_route; + break; + case HV_TEST_DEV_SINT_ROUTE_DESTROY: + i = find_sint_route_index(dev, vcpu_id, sint); + assert(i >= 0); + sint_route = dev->sint_route[i]; + kvm_hv_sint_route_destroy(sint_route); + free_sint_route_index(dev, i); + break; + case HV_TEST_DEV_SINT_ROUTE_SET_SINT: + i = find_sint_route_index(dev, vcpu_id, sint); + assert(i >= 0); + sint_route = dev->sint_route[i]; + kvm_hv_sint_route_set_sint(sint_route); + break; + default: + break; + } +} + +static void hv_test_dev_control(void *opaque, hwaddr addr, uint64_t data, + uint32_t len) +{ + HypervTestDev *dev = HYPERV_TEST_DEV(opaque); + uint8_t ctl; + + ctl = (data >> 16ULL) & 0xFF; + switch (ctl) { + case HV_TEST_DEV_SINT_ROUTE_CREATE: + case HV_TEST_DEV_SINT_ROUTE_DESTROY: + case HV_TEST_DEV_SINT_ROUTE_SET_SINT: { + uint8_t sint = data & 0xFF; + uint8_t vcpu_id = (data >> 8ULL) & 0xFF; + hv_synic_test_dev_control(dev, ctl, vcpu_id, sint); + break; + } + default: + break; + } +} + +static const MemoryRegionOps synic_test_sint_ops = { + .write = hv_test_dev_control, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void hv_test_dev_realizefn(DeviceState *d, Error **errp) +{ + ISADevice *isa = ISA_DEVICE(d); + HypervTestDev *dev = HYPERV_TEST_DEV(d); + MemoryRegion *io = isa_address_space_io(isa); + + memset(dev->sint_route, 0, sizeof(dev->sint_route)); + memory_region_init_io(&dev->sint_control, OBJECT(dev), + &synic_test_sint_ops, dev, + "hyperv-testdev-ctl", 4); + memory_region_add_subregion(io, 0x3000, &dev->sint_control); +} + +static void hv_test_dev_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->realize = hv_test_dev_realizefn; +} + +static const TypeInfo hv_test_dev_info = { + .name = TYPE_HYPERV_TEST_DEV, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(HypervTestDev), + .class_init = hv_test_dev_class_init, +}; + +static void hv_test_dev_register_types(void) +{ + type_register_static(&hv_test_dev_info); +} +type_init(hv_test_dev_register_types); diff --git a/qemu/hw/misc/imx25_ccm.c b/qemu/hw/misc/imx25_ccm.c new file mode 100644 index 000000000..225604d82 --- /dev/null +++ b/qemu/hw/misc/imx25_ccm.c @@ -0,0 +1,317 @@ +/* + * IMX25 Clock Control Module + * + * Copyright (C) 2012 NICTA + * Updated by Jean-Christophe Dubois + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * To get the timer frequencies right, we need to emulate at least part of + * the CCM. + */ + +#include "qemu/osdep.h" +#include "hw/misc/imx25_ccm.h" + +#ifndef DEBUG_IMX25_CCM +#define DEBUG_IMX25_CCM 0 +#endif + +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX25_CCM) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX25_CCM, \ + __func__, ##args); \ + } \ + } while (0) + +static char const *imx25_ccm_reg_name(uint32_t reg) +{ + static char unknown[20]; + + switch (reg) { + case IMX25_CCM_MPCTL_REG: + return "mpctl"; + case IMX25_CCM_UPCTL_REG: + return "upctl"; + case IMX25_CCM_CCTL_REG: + return "cctl"; + case IMX25_CCM_CGCR0_REG: + return "cgcr0"; + case IMX25_CCM_CGCR1_REG: + return "cgcr1"; + case IMX25_CCM_CGCR2_REG: + return "cgcr2"; + case IMX25_CCM_PCDR0_REG: + return "pcdr0"; + case IMX25_CCM_PCDR1_REG: + return "pcdr1"; + case IMX25_CCM_PCDR2_REG: + return "pcdr2"; + case IMX25_CCM_PCDR3_REG: + return "pcdr3"; + case IMX25_CCM_RCSR_REG: + return "rcsr"; + case IMX25_CCM_CRDR_REG: + return "crdr"; + case IMX25_CCM_DCVR0_REG: + return "dcvr0"; + case IMX25_CCM_DCVR1_REG: + return "dcvr1"; + case IMX25_CCM_DCVR2_REG: + return "dcvr2"; + case IMX25_CCM_DCVR3_REG: + return "dcvr3"; + case IMX25_CCM_LTR0_REG: + return "ltr0"; + case IMX25_CCM_LTR1_REG: + return "ltr1"; + case IMX25_CCM_LTR2_REG: + return "ltr2"; + case IMX25_CCM_LTR3_REG: + return "ltr3"; + case IMX25_CCM_LTBR0_REG: + return "ltbr0"; + case IMX25_CCM_LTBR1_REG: + return "ltbr1"; + case IMX25_CCM_PMCR0_REG: + return "pmcr0"; + case IMX25_CCM_PMCR1_REG: + return "pmcr1"; + case IMX25_CCM_PMCR2_REG: + return "pmcr2"; + case IMX25_CCM_MCR_REG: + return "mcr"; + case IMX25_CCM_LPIMR0_REG: + return "lpimr0"; + case IMX25_CCM_LPIMR1_REG: + return "lpimr1"; + default: + sprintf(unknown, "[%d ?]", reg); + return unknown; + } +} +#define CKIH_FREQ 24000000 /* 24MHz crystal input */ + +static const VMStateDescription vmstate_imx25_ccm = { + .name = TYPE_IMX25_CCM, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(reg, IMX25CCMState, IMX25_CCM_MAX_REG), + VMSTATE_END_OF_LIST() + }, +}; + +static uint32_t imx25_ccm_get_mpll_clk(IMXCCMState *dev) +{ + uint32_t freq; + IMX25CCMState *s = IMX25_CCM(dev); + + if (EXTRACT(s->reg[IMX25_CCM_CCTL_REG], MPLL_BYPASS)) { + freq = CKIH_FREQ; + } else { + freq = imx_ccm_calc_pll(s->reg[IMX25_CCM_MPCTL_REG], CKIH_FREQ); + } + + DPRINTF("freq = %d\n", freq); + + return freq; +} + +static uint32_t imx25_ccm_get_mcu_clk(IMXCCMState *dev) +{ + uint32_t freq; + IMX25CCMState *s = IMX25_CCM(dev); + + freq = imx25_ccm_get_mpll_clk(dev); + + if (EXTRACT(s->reg[IMX25_CCM_CCTL_REG], ARM_SRC)) { + freq = (freq * 3 / 4); + } + + freq = freq / (1 + EXTRACT(s->reg[IMX25_CCM_CCTL_REG], ARM_CLK_DIV)); + + DPRINTF("freq = %d\n", freq); + + return freq; +} + +static uint32_t imx25_ccm_get_ahb_clk(IMXCCMState *dev) +{ + uint32_t freq; + IMX25CCMState *s = IMX25_CCM(dev); + + freq = imx25_ccm_get_mcu_clk(dev) + / (1 + EXTRACT(s->reg[IMX25_CCM_CCTL_REG], AHB_CLK_DIV)); + + DPRINTF("freq = %d\n", freq); + + return freq; +} + +static uint32_t imx25_ccm_get_ipg_clk(IMXCCMState *dev) +{ + uint32_t freq; + + freq = imx25_ccm_get_ahb_clk(dev) / 2; + + DPRINTF("freq = %d\n", freq); + + return freq; +} + +static uint32_t imx25_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) +{ + uint32_t freq = 0; + DPRINTF("Clock = %d)\n", clock); + + switch (clock) { + case CLK_NONE: + break; + case CLK_IPG: + case CLK_IPG_HIGH: + freq = imx25_ccm_get_ipg_clk(dev); + break; + case CLK_32k: + freq = CKIL_FREQ; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n", + TYPE_IMX25_CCM, __func__, clock); + break; + } + + DPRINTF("Clock = %d) = %d\n", clock, freq); + + return freq; +} + +static void imx25_ccm_reset(DeviceState *dev) +{ + IMX25CCMState *s = IMX25_CCM(dev); + + DPRINTF("\n"); + + memset(s->reg, 0, IMX25_CCM_MAX_REG * sizeof(uint32_t)); + s->reg[IMX25_CCM_MPCTL_REG] = 0x800b2c01; + s->reg[IMX25_CCM_UPCTL_REG] = 0x84042800; + /* + * The value below gives: + * CPU = 133 MHz, AHB = 66,5 MHz, IPG = 33 MHz. + */ + s->reg[IMX25_CCM_CCTL_REG] = 0xd0030000; + s->reg[IMX25_CCM_CGCR0_REG] = 0x028A0100; + s->reg[IMX25_CCM_CGCR1_REG] = 0x04008100; + s->reg[IMX25_CCM_CGCR2_REG] = 0x00000438; + s->reg[IMX25_CCM_PCDR0_REG] = 0x01010101; + s->reg[IMX25_CCM_PCDR1_REG] = 0x01010101; + s->reg[IMX25_CCM_PCDR2_REG] = 0x01010101; + s->reg[IMX25_CCM_PCDR3_REG] = 0x01010101; + s->reg[IMX25_CCM_PMCR0_REG] = 0x00A00000; + s->reg[IMX25_CCM_PMCR1_REG] = 0x0000A030; + s->reg[IMX25_CCM_PMCR2_REG] = 0x0000A030; + s->reg[IMX25_CCM_MCR_REG] = 0x43000000; + + /* + * default boot will change the reset values to allow: + * CPU = 399 MHz, AHB = 133 MHz, IPG = 66,5 MHz. + * For some reason, this doesn't work. With the value below, linux + * detects a 88 MHz IPG CLK instead of 66,5 MHz. + s->reg[IMX25_CCM_CCTL_REG] = 0x20032000; + */ +} + +static uint64_t imx25_ccm_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32_t value = 0; + IMX25CCMState *s = (IMX25CCMState *)opaque; + + if (offset < 0x70) { + value = s->reg[offset >> 2]; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX25_CCM, __func__, offset); + } + + DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx25_ccm_reg_name(offset >> 2), + value); + + return value; +} + +static void imx25_ccm_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + IMX25CCMState *s = (IMX25CCMState *)opaque; + + DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx25_ccm_reg_name(offset >> 2), + (uint32_t)value); + + if (offset < 0x70) { + /* + * We will do a better implementation later. In particular some bits + * cannot be written to. + */ + s->reg[offset >> 2] = value; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX25_CCM, __func__, offset); + } +} + +static const struct MemoryRegionOps imx25_ccm_ops = { + .read = imx25_ccm_read, + .write = imx25_ccm_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void imx25_ccm_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + SysBusDevice *sd = SYS_BUS_DEVICE(obj); + IMX25CCMState *s = IMX25_CCM(obj); + + memory_region_init_io(&s->iomem, OBJECT(dev), &imx25_ccm_ops, s, + TYPE_IMX25_CCM, 0x1000); + sysbus_init_mmio(sd, &s->iomem); +} + +static void imx25_ccm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + IMXCCMClass *ccm = IMX_CCM_CLASS(klass); + + dc->reset = imx25_ccm_reset; + dc->vmsd = &vmstate_imx25_ccm; + dc->desc = "i.MX25 Clock Control Module"; + + ccm->get_clock_frequency = imx25_ccm_get_clock_frequency; +} + +static const TypeInfo imx25_ccm_info = { + .name = TYPE_IMX25_CCM, + .parent = TYPE_IMX_CCM, + .instance_size = sizeof(IMX25CCMState), + .instance_init = imx25_ccm_init, + .class_init = imx25_ccm_class_init, +}; + +static void imx25_ccm_register_types(void) +{ + type_register_static(&imx25_ccm_info); +} + +type_init(imx25_ccm_register_types) diff --git a/qemu/hw/misc/imx31_ccm.c b/qemu/hw/misc/imx31_ccm.c new file mode 100644 index 000000000..80c164716 --- /dev/null +++ b/qemu/hw/misc/imx31_ccm.c @@ -0,0 +1,344 @@ +/* + * IMX31 Clock Control Module + * + * Copyright (C) 2012 NICTA + * Updated by Jean-Christophe Dubois + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * To get the timer frequencies right, we need to emulate at least part of + * the i.MX31 CCM. + */ + +#include "qemu/osdep.h" +#include "hw/misc/imx31_ccm.h" + +#define CKIH_FREQ 26000000 /* 26MHz crystal input */ + +#ifndef DEBUG_IMX31_CCM +#define DEBUG_IMX31_CCM 0 +#endif + +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX31_CCM) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX31_CCM, \ + __func__, ##args); \ + } \ + } while (0) + +static char const *imx31_ccm_reg_name(uint32_t reg) +{ + static char unknown[20]; + + switch (reg) { + case IMX31_CCM_CCMR_REG: + return "CCMR"; + case IMX31_CCM_PDR0_REG: + return "PDR0"; + case IMX31_CCM_PDR1_REG: + return "PDR1"; + case IMX31_CCM_RCSR_REG: + return "RCSR"; + case IMX31_CCM_MPCTL_REG: + return "MPCTL"; + case IMX31_CCM_UPCTL_REG: + return "UPCTL"; + case IMX31_CCM_SPCTL_REG: + return "SPCTL"; + case IMX31_CCM_COSR_REG: + return "COSR"; + case IMX31_CCM_CGR0_REG: + return "CGR0"; + case IMX31_CCM_CGR1_REG: + return "CGR1"; + case IMX31_CCM_CGR2_REG: + return "CGR2"; + case IMX31_CCM_WIMR_REG: + return "WIMR"; + case IMX31_CCM_LDC_REG: + return "LDC"; + case IMX31_CCM_DCVR0_REG: + return "DCVR0"; + case IMX31_CCM_DCVR1_REG: + return "DCVR1"; + case IMX31_CCM_DCVR2_REG: + return "DCVR2"; + case IMX31_CCM_DCVR3_REG: + return "DCVR3"; + case IMX31_CCM_LTR0_REG: + return "LTR0"; + case IMX31_CCM_LTR1_REG: + return "LTR1"; + case IMX31_CCM_LTR2_REG: + return "LTR2"; + case IMX31_CCM_LTR3_REG: + return "LTR3"; + case IMX31_CCM_LTBR0_REG: + return "LTBR0"; + case IMX31_CCM_LTBR1_REG: + return "LTBR1"; + case IMX31_CCM_PMCR0_REG: + return "PMCR0"; + case IMX31_CCM_PMCR1_REG: + return "PMCR1"; + case IMX31_CCM_PDR2_REG: + return "PDR2"; + default: + sprintf(unknown, "[%d ?]", reg); + return unknown; + } +} + +static const VMStateDescription vmstate_imx31_ccm = { + .name = TYPE_IMX31_CCM, + .version_id = 2, + .minimum_version_id = 2, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(reg, IMX31CCMState, IMX31_CCM_MAX_REG), + VMSTATE_END_OF_LIST() + }, +}; + +static uint32_t imx31_ccm_get_pll_ref_clk(IMXCCMState *dev) +{ + uint32_t freq = 0; + IMX31CCMState *s = IMX31_CCM(dev); + + if ((s->reg[IMX31_CCM_CCMR_REG] & CCMR_PRCS) == 2) { + if (s->reg[IMX31_CCM_CCMR_REG] & CCMR_FPME) { + freq = CKIL_FREQ; + if (s->reg[IMX31_CCM_CCMR_REG] & CCMR_FPMF) { + freq *= 1024; + } + } + } else { + freq = CKIH_FREQ; + } + + DPRINTF("freq = %d\n", freq); + + return freq; +} + +static uint32_t imx31_ccm_get_mpll_clk(IMXCCMState *dev) +{ + uint32_t freq; + IMX31CCMState *s = IMX31_CCM(dev); + + freq = imx_ccm_calc_pll(s->reg[IMX31_CCM_MPCTL_REG], + imx31_ccm_get_pll_ref_clk(dev)); + + DPRINTF("freq = %d\n", freq); + + return freq; +} + +static uint32_t imx31_ccm_get_mcu_main_clk(IMXCCMState *dev) +{ + uint32_t freq; + IMX31CCMState *s = IMX31_CCM(dev); + + if ((s->reg[IMX31_CCM_CCMR_REG] & CCMR_MDS) || + !(s->reg[IMX31_CCM_CCMR_REG] & CCMR_MPE)) { + freq = imx31_ccm_get_pll_ref_clk(dev); + } else { + freq = imx31_ccm_get_mpll_clk(dev); + } + + DPRINTF("freq = %d\n", freq); + + return freq; +} + +static uint32_t imx31_ccm_get_hclk_clk(IMXCCMState *dev) +{ + uint32_t freq; + IMX31CCMState *s = IMX31_CCM(dev); + + freq = imx31_ccm_get_mcu_main_clk(dev) + / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], MAX)); + + DPRINTF("freq = %d\n", freq); + + return freq; +} + +static uint32_t imx31_ccm_get_ipg_clk(IMXCCMState *dev) +{ + uint32_t freq; + IMX31CCMState *s = IMX31_CCM(dev); + + freq = imx31_ccm_get_hclk_clk(dev) + / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], IPG)); + + DPRINTF("freq = %d\n", freq); + + return freq; +} + +static uint32_t imx31_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) +{ + uint32_t freq = 0; + + switch (clock) { + case CLK_NONE: + break; + case CLK_IPG: + case CLK_IPG_HIGH: + freq = imx31_ccm_get_ipg_clk(dev); + break; + case CLK_32k: + freq = CKIL_FREQ; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n", + TYPE_IMX31_CCM, __func__, clock); + break; + } + + DPRINTF("Clock = %d) = %d\n", clock, freq); + + return freq; +} + +static void imx31_ccm_reset(DeviceState *dev) +{ + IMX31CCMState *s = IMX31_CCM(dev); + + DPRINTF("()\n"); + + memset(s->reg, 0, sizeof(uint32_t) * IMX31_CCM_MAX_REG); + + s->reg[IMX31_CCM_CCMR_REG] = 0x074b0b7d; + s->reg[IMX31_CCM_PDR0_REG] = 0xff870b48; + s->reg[IMX31_CCM_PDR1_REG] = 0x49fcfe7f; + s->reg[IMX31_CCM_RCSR_REG] = 0x007f0000; + s->reg[IMX31_CCM_MPCTL_REG] = 0x04001800; + s->reg[IMX31_CCM_UPCTL_REG] = 0x04051c03; + s->reg[IMX31_CCM_SPCTL_REG] = 0x04043001; + s->reg[IMX31_CCM_COSR_REG] = 0x00000280; + s->reg[IMX31_CCM_CGR0_REG] = 0xffffffff; + s->reg[IMX31_CCM_CGR1_REG] = 0xffffffff; + s->reg[IMX31_CCM_CGR2_REG] = 0xffffffff; + s->reg[IMX31_CCM_WIMR_REG] = 0xffffffff; + s->reg[IMX31_CCM_LTR1_REG] = 0x00004040; + s->reg[IMX31_CCM_PMCR0_REG] = 0x80209828; + s->reg[IMX31_CCM_PMCR1_REG] = 0x00aa0000; + s->reg[IMX31_CCM_PDR2_REG] = 0x00000285; +} + +static uint64_t imx31_ccm_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32_t value = 0; + IMX31CCMState *s = (IMX31CCMState *)opaque; + + if ((offset >> 2) < IMX31_CCM_MAX_REG) { + value = s->reg[offset >> 2]; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX31_CCM, __func__, offset); + } + + DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx31_ccm_reg_name(offset >> 2), + value); + + return (uint64_t)value; +} + +static void imx31_ccm_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + IMX31CCMState *s = (IMX31CCMState *)opaque; + + DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx31_ccm_reg_name(offset >> 2), + (uint32_t)value); + + switch (offset >> 2) { + case IMX31_CCM_CCMR_REG: + s->reg[IMX31_CCM_CCMR_REG] = CCMR_FPMF | (value & 0x3b6fdfff); + break; + case IMX31_CCM_PDR0_REG: + s->reg[IMX31_CCM_PDR0_REG] = value & 0xff9f3fff; + break; + case IMX31_CCM_PDR1_REG: + s->reg[IMX31_CCM_PDR1_REG] = value; + break; + case IMX31_CCM_MPCTL_REG: + s->reg[IMX31_CCM_MPCTL_REG] = value & 0xbfff3fff; + break; + case IMX31_CCM_SPCTL_REG: + s->reg[IMX31_CCM_SPCTL_REG] = value & 0xbfff3fff; + break; + case IMX31_CCM_CGR0_REG: + s->reg[IMX31_CCM_CGR0_REG] = value; + break; + case IMX31_CCM_CGR1_REG: + s->reg[IMX31_CCM_CGR1_REG] = value; + break; + case IMX31_CCM_CGR2_REG: + s->reg[IMX31_CCM_CGR2_REG] = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX31_CCM, __func__, offset); + break; + } +} + +static const struct MemoryRegionOps imx31_ccm_ops = { + .read = imx31_ccm_read, + .write = imx31_ccm_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + +}; + +static void imx31_ccm_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + SysBusDevice *sd = SYS_BUS_DEVICE(obj); + IMX31CCMState *s = IMX31_CCM(obj); + + memory_region_init_io(&s->iomem, OBJECT(dev), &imx31_ccm_ops, s, + TYPE_IMX31_CCM, 0x1000); + sysbus_init_mmio(sd, &s->iomem); +} + +static void imx31_ccm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + IMXCCMClass *ccm = IMX_CCM_CLASS(klass); + + dc->reset = imx31_ccm_reset; + dc->vmsd = &vmstate_imx31_ccm; + dc->desc = "i.MX31 Clock Control Module"; + + ccm->get_clock_frequency = imx31_ccm_get_clock_frequency; +} + +static const TypeInfo imx31_ccm_info = { + .name = TYPE_IMX31_CCM, + .parent = TYPE_IMX_CCM, + .instance_size = sizeof(IMX31CCMState), + .instance_init = imx31_ccm_init, + .class_init = imx31_ccm_class_init, +}; + +static void imx31_ccm_register_types(void) +{ + type_register_static(&imx31_ccm_info); +} + +type_init(imx31_ccm_register_types) diff --git a/qemu/hw/misc/imx6_ccm.c b/qemu/hw/misc/imx6_ccm.c new file mode 100644 index 000000000..4e1d49da6 --- /dev/null +++ b/qemu/hw/misc/imx6_ccm.c @@ -0,0 +1,774 @@ +/* + * IMX6 Clock Control Module + * + * Copyright (c) 2015 Jean-Christophe Dubois + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * To get the timer frequencies right, we need to emulate at least part of + * the CCM. + */ + +#include "qemu/osdep.h" +#include "hw/misc/imx6_ccm.h" + +#ifndef DEBUG_IMX6_CCM +#define DEBUG_IMX6_CCM 0 +#endif + +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX6_CCM) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX6_CCM, \ + __func__, ##args); \ + } \ + } while (0) + +static char const *imx6_ccm_reg_name(uint32_t reg) +{ + static char unknown[20]; + + switch (reg) { + case CCM_CCR: + return "CCR"; + case CCM_CCDR: + return "CCDR"; + case CCM_CSR: + return "CSR"; + case CCM_CCSR: + return "CCSR"; + case CCM_CACRR: + return "CACRR"; + case CCM_CBCDR: + return "CBCDR"; + case CCM_CBCMR: + return "CBCMR"; + case CCM_CSCMR1: + return "CSCMR1"; + case CCM_CSCMR2: + return "CSCMR2"; + case CCM_CSCDR1: + return "CSCDR1"; + case CCM_CS1CDR: + return "CS1CDR"; + case CCM_CS2CDR: + return "CS2CDR"; + case CCM_CDCDR: + return "CDCDR"; + case CCM_CHSCCDR: + return "CHSCCDR"; + case CCM_CSCDR2: + return "CSCDR2"; + case CCM_CSCDR3: + return "CSCDR3"; + case CCM_CDHIPR: + return "CDHIPR"; + case CCM_CTOR: + return "CTOR"; + case CCM_CLPCR: + return "CLPCR"; + case CCM_CISR: + return "CISR"; + case CCM_CIMR: + return "CIMR"; + case CCM_CCOSR: + return "CCOSR"; + case CCM_CGPR: + return "CGPR"; + case CCM_CCGR0: + return "CCGR0"; + case CCM_CCGR1: + return "CCGR1"; + case CCM_CCGR2: + return "CCGR2"; + case CCM_CCGR3: + return "CCGR3"; + case CCM_CCGR4: + return "CCGR4"; + case CCM_CCGR5: + return "CCGR5"; + case CCM_CCGR6: + return "CCGR6"; + case CCM_CMEOR: + return "CMEOR"; + default: + sprintf(unknown, "%d ?", reg); + return unknown; + } +} + +static char const *imx6_analog_reg_name(uint32_t reg) +{ + static char unknown[20]; + + switch (reg) { + case CCM_ANALOG_PLL_ARM: + return "PLL_ARM"; + case CCM_ANALOG_PLL_ARM_SET: + return "PLL_ARM_SET"; + case CCM_ANALOG_PLL_ARM_CLR: + return "PLL_ARM_CLR"; + case CCM_ANALOG_PLL_ARM_TOG: + return "PLL_ARM_TOG"; + case CCM_ANALOG_PLL_USB1: + return "PLL_USB1"; + case CCM_ANALOG_PLL_USB1_SET: + return "PLL_USB1_SET"; + case CCM_ANALOG_PLL_USB1_CLR: + return "PLL_USB1_CLR"; + case CCM_ANALOG_PLL_USB1_TOG: + return "PLL_USB1_TOG"; + case CCM_ANALOG_PLL_USB2: + return "PLL_USB2"; + case CCM_ANALOG_PLL_USB2_SET: + return "PLL_USB2_SET"; + case CCM_ANALOG_PLL_USB2_CLR: + return "PLL_USB2_CLR"; + case CCM_ANALOG_PLL_USB2_TOG: + return "PLL_USB2_TOG"; + case CCM_ANALOG_PLL_SYS: + return "PLL_SYS"; + case CCM_ANALOG_PLL_SYS_SET: + return "PLL_SYS_SET"; + case CCM_ANALOG_PLL_SYS_CLR: + return "PLL_SYS_CLR"; + case CCM_ANALOG_PLL_SYS_TOG: + return "PLL_SYS_TOG"; + case CCM_ANALOG_PLL_SYS_SS: + return "PLL_SYS_SS"; + case CCM_ANALOG_PLL_SYS_NUM: + return "PLL_SYS_NUM"; + case CCM_ANALOG_PLL_SYS_DENOM: + return "PLL_SYS_DENOM"; + case CCM_ANALOG_PLL_AUDIO: + return "PLL_AUDIO"; + case CCM_ANALOG_PLL_AUDIO_SET: + return "PLL_AUDIO_SET"; + case CCM_ANALOG_PLL_AUDIO_CLR: + return "PLL_AUDIO_CLR"; + case CCM_ANALOG_PLL_AUDIO_TOG: + return "PLL_AUDIO_TOG"; + case CCM_ANALOG_PLL_AUDIO_NUM: + return "PLL_AUDIO_NUM"; + case CCM_ANALOG_PLL_AUDIO_DENOM: + return "PLL_AUDIO_DENOM"; + case CCM_ANALOG_PLL_VIDEO: + return "PLL_VIDEO"; + case CCM_ANALOG_PLL_VIDEO_SET: + return "PLL_VIDEO_SET"; + case CCM_ANALOG_PLL_VIDEO_CLR: + return "PLL_VIDEO_CLR"; + case CCM_ANALOG_PLL_VIDEO_TOG: + return "PLL_VIDEO_TOG"; + case CCM_ANALOG_PLL_VIDEO_NUM: + return "PLL_VIDEO_NUM"; + case CCM_ANALOG_PLL_VIDEO_DENOM: + return "PLL_VIDEO_DENOM"; + case CCM_ANALOG_PLL_MLB: + return "PLL_MLB"; + case CCM_ANALOG_PLL_MLB_SET: + return "PLL_MLB_SET"; + case CCM_ANALOG_PLL_MLB_CLR: + return "PLL_MLB_CLR"; + case CCM_ANALOG_PLL_MLB_TOG: + return "PLL_MLB_TOG"; + case CCM_ANALOG_PLL_ENET: + return "PLL_ENET"; + case CCM_ANALOG_PLL_ENET_SET: + return "PLL_ENET_SET"; + case CCM_ANALOG_PLL_ENET_CLR: + return "PLL_ENET_CLR"; + case CCM_ANALOG_PLL_ENET_TOG: + return "PLL_ENET_TOG"; + case CCM_ANALOG_PFD_480: + return "PFD_480"; + case CCM_ANALOG_PFD_480_SET: + return "PFD_480_SET"; + case CCM_ANALOG_PFD_480_CLR: + return "PFD_480_CLR"; + case CCM_ANALOG_PFD_480_TOG: + return "PFD_480_TOG"; + case CCM_ANALOG_PFD_528: + return "PFD_528"; + case CCM_ANALOG_PFD_528_SET: + return "PFD_528_SET"; + case CCM_ANALOG_PFD_528_CLR: + return "PFD_528_CLR"; + case CCM_ANALOG_PFD_528_TOG: + return "PFD_528_TOG"; + case CCM_ANALOG_MISC0: + return "MISC0"; + case CCM_ANALOG_MISC0_SET: + return "MISC0_SET"; + case CCM_ANALOG_MISC0_CLR: + return "MISC0_CLR"; + case CCM_ANALOG_MISC0_TOG: + return "MISC0_TOG"; + case CCM_ANALOG_MISC2: + return "MISC2"; + case CCM_ANALOG_MISC2_SET: + return "MISC2_SET"; + case CCM_ANALOG_MISC2_CLR: + return "MISC2_CLR"; + case CCM_ANALOG_MISC2_TOG: + return "MISC2_TOG"; + case PMU_REG_1P1: + return "PMU_REG_1P1"; + case PMU_REG_3P0: + return "PMU_REG_3P0"; + case PMU_REG_2P5: + return "PMU_REG_2P5"; + case PMU_REG_CORE: + return "PMU_REG_CORE"; + case PMU_MISC1: + return "PMU_MISC1"; + case PMU_MISC1_SET: + return "PMU_MISC1_SET"; + case PMU_MISC1_CLR: + return "PMU_MISC1_CLR"; + case PMU_MISC1_TOG: + return "PMU_MISC1_TOG"; + case USB_ANALOG_DIGPROG: + return "USB_ANALOG_DIGPROG"; + default: + sprintf(unknown, "%d ?", reg); + return unknown; + } +} + +#define CKIH_FREQ 24000000 /* 24MHz crystal input */ + +static const VMStateDescription vmstate_imx6_ccm = { + .name = TYPE_IMX6_CCM, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(ccm, IMX6CCMState, CCM_MAX), + VMSTATE_UINT32_ARRAY(analog, IMX6CCMState, CCM_ANALOG_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static uint64_t imx6_analog_get_pll2_clk(IMX6CCMState *dev) +{ + uint64_t freq = 24000000; + + if (EXTRACT(dev->analog[CCM_ANALOG_PLL_SYS], DIV_SELECT)) { + freq *= 22; + } else { + freq *= 20; + } + + DPRINTF("freq = %d\n", (uint32_t)freq); + + return freq; +} + +static uint64_t imx6_analog_get_pll2_pfd0_clk(IMX6CCMState *dev) +{ + uint64_t freq = 0; + + freq = imx6_analog_get_pll2_clk(dev) * 18 + / EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD0_FRAC); + + DPRINTF("freq = %d\n", (uint32_t)freq); + + return freq; +} + +static uint64_t imx6_analog_get_pll2_pfd2_clk(IMX6CCMState *dev) +{ + uint64_t freq = 0; + + freq = imx6_analog_get_pll2_clk(dev) * 18 + / EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD2_FRAC); + + DPRINTF("freq = %d\n", (uint32_t)freq); + + return freq; +} + +static uint64_t imx6_analog_get_periph_clk(IMX6CCMState *dev) +{ + uint64_t freq = 0; + + switch (EXTRACT(dev->ccm[CCM_CBCMR], PRE_PERIPH_CLK_SEL)) { + case 0: + freq = imx6_analog_get_pll2_clk(dev); + break; + case 1: + freq = imx6_analog_get_pll2_pfd2_clk(dev); + break; + case 2: + freq = imx6_analog_get_pll2_pfd0_clk(dev); + break; + case 3: + freq = imx6_analog_get_pll2_pfd2_clk(dev) / 2; + break; + default: + /* We should never get there */ + g_assert_not_reached(); + break; + } + + DPRINTF("freq = %d\n", (uint32_t)freq); + + return freq; +} + +static uint64_t imx6_ccm_get_ahb_clk(IMX6CCMState *dev) +{ + uint64_t freq = 0; + + freq = imx6_analog_get_periph_clk(dev) + / (1 + EXTRACT(dev->ccm[CCM_CBCDR], AHB_PODF)); + + DPRINTF("freq = %d\n", (uint32_t)freq); + + return freq; +} + +static uint64_t imx6_ccm_get_ipg_clk(IMX6CCMState *dev) +{ + uint64_t freq = 0; + + freq = imx6_ccm_get_ahb_clk(dev) + / (1 + EXTRACT(dev->ccm[CCM_CBCDR], IPG_PODF));; + + DPRINTF("freq = %d\n", (uint32_t)freq); + + return freq; +} + +static uint64_t imx6_ccm_get_per_clk(IMX6CCMState *dev) +{ + uint64_t freq = 0; + + freq = imx6_ccm_get_ipg_clk(dev) + / (1 + EXTRACT(dev->ccm[CCM_CSCMR1], PERCLK_PODF)); + + DPRINTF("freq = %d\n", (uint32_t)freq); + + return freq; +} + +static uint32_t imx6_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) +{ + uint32_t freq = 0; + IMX6CCMState *s = IMX6_CCM(dev); + + switch (clock) { + case CLK_NONE: + break; + case CLK_IPG: + freq = imx6_ccm_get_ipg_clk(s); + break; + case CLK_IPG_HIGH: + freq = imx6_ccm_get_per_clk(s); + break; + case CLK_32k: + freq = CKIL_FREQ; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n", + TYPE_IMX6_CCM, __func__, clock); + break; + } + + DPRINTF("Clock = %d) = %d\n", clock, freq); + + return freq; +} + +static void imx6_ccm_reset(DeviceState *dev) +{ + IMX6CCMState *s = IMX6_CCM(dev); + + DPRINTF("\n"); + + s->ccm[CCM_CCR] = 0x040116FF; + s->ccm[CCM_CCDR] = 0x00000000; + s->ccm[CCM_CSR] = 0x00000010; + s->ccm[CCM_CCSR] = 0x00000100; + s->ccm[CCM_CACRR] = 0x00000000; + s->ccm[CCM_CBCDR] = 0x00018D40; + s->ccm[CCM_CBCMR] = 0x00022324; + s->ccm[CCM_CSCMR1] = 0x00F00000; + s->ccm[CCM_CSCMR2] = 0x02B92F06; + s->ccm[CCM_CSCDR1] = 0x00490B00; + s->ccm[CCM_CS1CDR] = 0x0EC102C1; + s->ccm[CCM_CS2CDR] = 0x000736C1; + s->ccm[CCM_CDCDR] = 0x33F71F92; + s->ccm[CCM_CHSCCDR] = 0x0002A150; + s->ccm[CCM_CSCDR2] = 0x0002A150; + s->ccm[CCM_CSCDR3] = 0x00014841; + s->ccm[CCM_CDHIPR] = 0x00000000; + s->ccm[CCM_CTOR] = 0x00000000; + s->ccm[CCM_CLPCR] = 0x00000079; + s->ccm[CCM_CISR] = 0x00000000; + s->ccm[CCM_CIMR] = 0xFFFFFFFF; + s->ccm[CCM_CCOSR] = 0x000A0001; + s->ccm[CCM_CGPR] = 0x0000FE62; + s->ccm[CCM_CCGR0] = 0xFFFFFFFF; + s->ccm[CCM_CCGR1] = 0xFFFFFFFF; + s->ccm[CCM_CCGR2] = 0xFC3FFFFF; + s->ccm[CCM_CCGR3] = 0xFFFFFFFF; + s->ccm[CCM_CCGR4] = 0xFFFFFFFF; + s->ccm[CCM_CCGR5] = 0xFFFFFFFF; + s->ccm[CCM_CCGR6] = 0xFFFFFFFF; + s->ccm[CCM_CMEOR] = 0xFFFFFFFF; + + s->analog[CCM_ANALOG_PLL_ARM] = 0x00013042; + s->analog[CCM_ANALOG_PLL_USB1] = 0x00012000; + s->analog[CCM_ANALOG_PLL_USB2] = 0x00012000; + s->analog[CCM_ANALOG_PLL_SYS] = 0x00013001; + s->analog[CCM_ANALOG_PLL_SYS_SS] = 0x00000000; + s->analog[CCM_ANALOG_PLL_SYS_NUM] = 0x00000000; + s->analog[CCM_ANALOG_PLL_SYS_DENOM] = 0x00000012; + s->analog[CCM_ANALOG_PLL_AUDIO] = 0x00011006; + s->analog[CCM_ANALOG_PLL_AUDIO_NUM] = 0x05F5E100; + s->analog[CCM_ANALOG_PLL_AUDIO_DENOM] = 0x2964619C; + s->analog[CCM_ANALOG_PLL_VIDEO] = 0x0001100C; + s->analog[CCM_ANALOG_PLL_VIDEO_NUM] = 0x05F5E100; + s->analog[CCM_ANALOG_PLL_VIDEO_DENOM] = 0x10A24447; + s->analog[CCM_ANALOG_PLL_MLB] = 0x00010000; + s->analog[CCM_ANALOG_PLL_ENET] = 0x00011001; + s->analog[CCM_ANALOG_PFD_480] = 0x1311100C; + s->analog[CCM_ANALOG_PFD_528] = 0x1018101B; + + s->analog[PMU_REG_1P1] = 0x00001073; + s->analog[PMU_REG_3P0] = 0x00000F74; + s->analog[PMU_REG_2P5] = 0x00005071; + s->analog[PMU_REG_CORE] = 0x00402010; + s->analog[PMU_MISC0] = 0x04000000; + s->analog[PMU_MISC1] = 0x00000000; + s->analog[PMU_MISC2] = 0x00272727; + + s->analog[USB_ANALOG_USB1_VBUS_DETECT] = 0x00000004; + s->analog[USB_ANALOG_USB1_CHRG_DETECT] = 0x00000000; + s->analog[USB_ANALOG_USB1_VBUS_DETECT_STAT] = 0x00000000; + s->analog[USB_ANALOG_USB1_CHRG_DETECT_STAT] = 0x00000000; + s->analog[USB_ANALOG_USB1_MISC] = 0x00000002; + s->analog[USB_ANALOG_USB2_VBUS_DETECT] = 0x00000004; + s->analog[USB_ANALOG_USB2_CHRG_DETECT] = 0x00000000; + s->analog[USB_ANALOG_USB2_MISC] = 0x00000002; + s->analog[USB_ANALOG_DIGPROG] = 0x00000000; + + /* all PLLs need to be locked */ + s->analog[CCM_ANALOG_PLL_ARM] |= CCM_ANALOG_PLL_LOCK; + s->analog[CCM_ANALOG_PLL_USB1] |= CCM_ANALOG_PLL_LOCK; + s->analog[CCM_ANALOG_PLL_USB2] |= CCM_ANALOG_PLL_LOCK; + s->analog[CCM_ANALOG_PLL_SYS] |= CCM_ANALOG_PLL_LOCK; + s->analog[CCM_ANALOG_PLL_AUDIO] |= CCM_ANALOG_PLL_LOCK; + s->analog[CCM_ANALOG_PLL_VIDEO] |= CCM_ANALOG_PLL_LOCK; + s->analog[CCM_ANALOG_PLL_MLB] |= CCM_ANALOG_PLL_LOCK; + s->analog[CCM_ANALOG_PLL_ENET] |= CCM_ANALOG_PLL_LOCK; +} + +static uint64_t imx6_ccm_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32_t value = 0; + uint32_t index = offset >> 2; + IMX6CCMState *s = (IMX6CCMState *)opaque; + + value = s->ccm[index]; + + DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_ccm_reg_name(index), value); + + return (uint64_t)value; +} + +static void imx6_ccm_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + uint32_t index = offset >> 2; + IMX6CCMState *s = (IMX6CCMState *)opaque; + + DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_ccm_reg_name(index), + (uint32_t)value); + + /* + * We will do a better implementation later. In particular some bits + * cannot be written to. + */ + s->ccm[index] = (uint32_t)value; +} + +static uint64_t imx6_analog_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32_t value; + uint32_t index = offset >> 2; + IMX6CCMState *s = (IMX6CCMState *)opaque; + + switch (index) { + case CCM_ANALOG_PLL_ARM_SET: + case CCM_ANALOG_PLL_USB1_SET: + case CCM_ANALOG_PLL_USB2_SET: + case CCM_ANALOG_PLL_SYS_SET: + case CCM_ANALOG_PLL_AUDIO_SET: + case CCM_ANALOG_PLL_VIDEO_SET: + case CCM_ANALOG_PLL_MLB_SET: + case CCM_ANALOG_PLL_ENET_SET: + case CCM_ANALOG_PFD_480_SET: + case CCM_ANALOG_PFD_528_SET: + case CCM_ANALOG_MISC0_SET: + case PMU_MISC1_SET: + case CCM_ANALOG_MISC2_SET: + case USB_ANALOG_USB1_VBUS_DETECT_SET: + case USB_ANALOG_USB1_CHRG_DETECT_SET: + case USB_ANALOG_USB1_MISC_SET: + case USB_ANALOG_USB2_VBUS_DETECT_SET: + case USB_ANALOG_USB2_CHRG_DETECT_SET: + case USB_ANALOG_USB2_MISC_SET: + /* + * All REG_NAME_SET register access are in fact targeting the + * the REG_NAME register. + */ + value = s->analog[index - 1]; + break; + case CCM_ANALOG_PLL_ARM_CLR: + case CCM_ANALOG_PLL_USB1_CLR: + case CCM_ANALOG_PLL_USB2_CLR: + case CCM_ANALOG_PLL_SYS_CLR: + case CCM_ANALOG_PLL_AUDIO_CLR: + case CCM_ANALOG_PLL_VIDEO_CLR: + case CCM_ANALOG_PLL_MLB_CLR: + case CCM_ANALOG_PLL_ENET_CLR: + case CCM_ANALOG_PFD_480_CLR: + case CCM_ANALOG_PFD_528_CLR: + case CCM_ANALOG_MISC0_CLR: + case PMU_MISC1_CLR: + case CCM_ANALOG_MISC2_CLR: + case USB_ANALOG_USB1_VBUS_DETECT_CLR: + case USB_ANALOG_USB1_CHRG_DETECT_CLR: + case USB_ANALOG_USB1_MISC_CLR: + case USB_ANALOG_USB2_VBUS_DETECT_CLR: + case USB_ANALOG_USB2_CHRG_DETECT_CLR: + case USB_ANALOG_USB2_MISC_CLR: + /* + * All REG_NAME_CLR register access are in fact targeting the + * the REG_NAME register. + */ + value = s->analog[index - 2]; + break; + case CCM_ANALOG_PLL_ARM_TOG: + case CCM_ANALOG_PLL_USB1_TOG: + case CCM_ANALOG_PLL_USB2_TOG: + case CCM_ANALOG_PLL_SYS_TOG: + case CCM_ANALOG_PLL_AUDIO_TOG: + case CCM_ANALOG_PLL_VIDEO_TOG: + case CCM_ANALOG_PLL_MLB_TOG: + case CCM_ANALOG_PLL_ENET_TOG: + case CCM_ANALOG_PFD_480_TOG: + case CCM_ANALOG_PFD_528_TOG: + case CCM_ANALOG_MISC0_TOG: + case PMU_MISC1_TOG: + case CCM_ANALOG_MISC2_TOG: + case USB_ANALOG_USB1_VBUS_DETECT_TOG: + case USB_ANALOG_USB1_CHRG_DETECT_TOG: + case USB_ANALOG_USB1_MISC_TOG: + case USB_ANALOG_USB2_VBUS_DETECT_TOG: + case USB_ANALOG_USB2_CHRG_DETECT_TOG: + case USB_ANALOG_USB2_MISC_TOG: + /* + * All REG_NAME_TOG register access are in fact targeting the + * the REG_NAME register. + */ + value = s->analog[index - 3]; + break; + default: + value = s->analog[index]; + break; + } + + DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_analog_reg_name(index), value); + + return (uint64_t)value; +} + +static void imx6_analog_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + uint32_t index = offset >> 2; + IMX6CCMState *s = (IMX6CCMState *)opaque; + + DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_analog_reg_name(index), + (uint32_t)value); + + switch (index) { + case CCM_ANALOG_PLL_ARM_SET: + case CCM_ANALOG_PLL_USB1_SET: + case CCM_ANALOG_PLL_USB2_SET: + case CCM_ANALOG_PLL_SYS_SET: + case CCM_ANALOG_PLL_AUDIO_SET: + case CCM_ANALOG_PLL_VIDEO_SET: + case CCM_ANALOG_PLL_MLB_SET: + case CCM_ANALOG_PLL_ENET_SET: + case CCM_ANALOG_PFD_480_SET: + case CCM_ANALOG_PFD_528_SET: + case CCM_ANALOG_MISC0_SET: + case PMU_MISC1_SET: + case CCM_ANALOG_MISC2_SET: + case USB_ANALOG_USB1_VBUS_DETECT_SET: + case USB_ANALOG_USB1_CHRG_DETECT_SET: + case USB_ANALOG_USB1_MISC_SET: + case USB_ANALOG_USB2_VBUS_DETECT_SET: + case USB_ANALOG_USB2_CHRG_DETECT_SET: + case USB_ANALOG_USB2_MISC_SET: + /* + * All REG_NAME_SET register access are in fact targeting the + * the REG_NAME register. So we change the value of the + * REG_NAME register, setting bits passed in the value. + */ + s->analog[index - 1] |= value; + break; + case CCM_ANALOG_PLL_ARM_CLR: + case CCM_ANALOG_PLL_USB1_CLR: + case CCM_ANALOG_PLL_USB2_CLR: + case CCM_ANALOG_PLL_SYS_CLR: + case CCM_ANALOG_PLL_AUDIO_CLR: + case CCM_ANALOG_PLL_VIDEO_CLR: + case CCM_ANALOG_PLL_MLB_CLR: + case CCM_ANALOG_PLL_ENET_CLR: + case CCM_ANALOG_PFD_480_CLR: + case CCM_ANALOG_PFD_528_CLR: + case CCM_ANALOG_MISC0_CLR: + case PMU_MISC1_CLR: + case CCM_ANALOG_MISC2_CLR: + case USB_ANALOG_USB1_VBUS_DETECT_CLR: + case USB_ANALOG_USB1_CHRG_DETECT_CLR: + case USB_ANALOG_USB1_MISC_CLR: + case USB_ANALOG_USB2_VBUS_DETECT_CLR: + case USB_ANALOG_USB2_CHRG_DETECT_CLR: + case USB_ANALOG_USB2_MISC_CLR: + /* + * All REG_NAME_CLR register access are in fact targeting the + * the REG_NAME register. So we change the value of the + * REG_NAME register, unsetting bits passed in the value. + */ + s->analog[index - 2] &= ~value; + break; + case CCM_ANALOG_PLL_ARM_TOG: + case CCM_ANALOG_PLL_USB1_TOG: + case CCM_ANALOG_PLL_USB2_TOG: + case CCM_ANALOG_PLL_SYS_TOG: + case CCM_ANALOG_PLL_AUDIO_TOG: + case CCM_ANALOG_PLL_VIDEO_TOG: + case CCM_ANALOG_PLL_MLB_TOG: + case CCM_ANALOG_PLL_ENET_TOG: + case CCM_ANALOG_PFD_480_TOG: + case CCM_ANALOG_PFD_528_TOG: + case CCM_ANALOG_MISC0_TOG: + case PMU_MISC1_TOG: + case CCM_ANALOG_MISC2_TOG: + case USB_ANALOG_USB1_VBUS_DETECT_TOG: + case USB_ANALOG_USB1_CHRG_DETECT_TOG: + case USB_ANALOG_USB1_MISC_TOG: + case USB_ANALOG_USB2_VBUS_DETECT_TOG: + case USB_ANALOG_USB2_CHRG_DETECT_TOG: + case USB_ANALOG_USB2_MISC_TOG: + /* + * All REG_NAME_TOG register access are in fact targeting the + * the REG_NAME register. So we change the value of the + * REG_NAME register, toggling bits passed in the value. + */ + s->analog[index - 3] ^= value; + break; + default: + /* + * We will do a better implementation later. In particular some bits + * cannot be written to. + */ + s->analog[index] = value; + break; + } +} + +static const struct MemoryRegionOps imx6_ccm_ops = { + .read = imx6_ccm_read, + .write = imx6_ccm_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static const struct MemoryRegionOps imx6_analog_ops = { + .read = imx6_analog_read, + .write = imx6_analog_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void imx6_ccm_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + SysBusDevice *sd = SYS_BUS_DEVICE(obj); + IMX6CCMState *s = IMX6_CCM(obj); + + /* initialize a container for the all memory range */ + memory_region_init(&s->container, OBJECT(dev), TYPE_IMX6_CCM, 0x5000); + + /* We initialize an IO memory region for the CCM part */ + memory_region_init_io(&s->ioccm, OBJECT(dev), &imx6_ccm_ops, s, + TYPE_IMX6_CCM ".ccm", CCM_MAX * sizeof(uint32_t)); + + /* Add the CCM as a subregion at offset 0 */ + memory_region_add_subregion(&s->container, 0, &s->ioccm); + + /* We initialize an IO memory region for the ANALOG part */ + memory_region_init_io(&s->ioanalog, OBJECT(dev), &imx6_analog_ops, s, + TYPE_IMX6_CCM ".analog", + CCM_ANALOG_MAX * sizeof(uint32_t)); + + /* Add the ANALOG as a subregion at offset 0x4000 */ + memory_region_add_subregion(&s->container, 0x4000, &s->ioanalog); + + sysbus_init_mmio(sd, &s->container); +} + +static void imx6_ccm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + IMXCCMClass *ccm = IMX_CCM_CLASS(klass); + + dc->reset = imx6_ccm_reset; + dc->vmsd = &vmstate_imx6_ccm; + dc->desc = "i.MX6 Clock Control Module"; + + ccm->get_clock_frequency = imx6_ccm_get_clock_frequency; +} + +static const TypeInfo imx6_ccm_info = { + .name = TYPE_IMX6_CCM, + .parent = TYPE_IMX_CCM, + .instance_size = sizeof(IMX6CCMState), + .instance_init = imx6_ccm_init, + .class_init = imx6_ccm_class_init, +}; + +static void imx6_ccm_register_types(void) +{ + type_register_static(&imx6_ccm_info); +} + +type_init(imx6_ccm_register_types) diff --git a/qemu/hw/misc/imx_ccm.c b/qemu/hw/misc/imx_ccm.c index 092028863..986d890ca 100644 --- a/qemu/hw/misc/imx_ccm.c +++ b/qemu/hw/misc/imx_ccm.c @@ -2,138 +2,51 @@ * IMX31 Clock Control Module * * Copyright (C) 2012 NICTA + * Updated by Jean-Christophe Dubois * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. * - * To get the timer frequencies right, we need to emulate at least part of - * the CCM. + * This is an abstract base class used to get a common interface to + * retrieve the CCM frequencies from the various i.MX SOC. */ -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "sysemu/sysemu.h" -#include "hw/arm/imx.h" +#include "qemu/osdep.h" +#include "hw/misc/imx_ccm.h" -#define CKIH_FREQ 26000000 /* 26MHz crystal input */ -#define CKIL_FREQ 32768 /* nominal 32khz clock */ - - -//#define DEBUG_CCM 1 -#ifdef DEBUG_CCM -#define DPRINTF(fmt, args...) \ -do { printf("imx_ccm: " fmt , ##args); } while (0) -#else -#define DPRINTF(fmt, args...) do {} while (0) +#ifndef DEBUG_IMX_CCM +#define DEBUG_IMX_CCM 0 #endif -static int imx_ccm_post_load(void *opaque, int version_id); - -#define TYPE_IMX_CCM "imx_ccm" -#define IMX_CCM(obj) OBJECT_CHECK(IMXCCMState, (obj), TYPE_IMX_CCM) - -typedef struct IMXCCMState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - - uint32_t ccmr; - uint32_t pdr0; - uint32_t pdr1; - uint32_t mpctl; - uint32_t spctl; - uint32_t cgr[3]; - uint32_t pmcr0; - uint32_t pmcr1; - - /* Frequencies precalculated on register changes */ - uint32_t pll_refclk_freq; - uint32_t mcu_clk_freq; - uint32_t hsp_clk_freq; - uint32_t ipg_clk_freq; -} IMXCCMState; - -static const VMStateDescription vmstate_imx_ccm = { - .name = "imx-ccm", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(ccmr, IMXCCMState), - VMSTATE_UINT32(pdr0, IMXCCMState), - VMSTATE_UINT32(pdr1, IMXCCMState), - VMSTATE_UINT32(mpctl, IMXCCMState), - VMSTATE_UINT32(spctl, IMXCCMState), - VMSTATE_UINT32_ARRAY(cgr, IMXCCMState, 3), - VMSTATE_UINT32(pmcr0, IMXCCMState), - VMSTATE_UINT32(pmcr1, IMXCCMState), - VMSTATE_UINT32(pll_refclk_freq, IMXCCMState), - VMSTATE_END_OF_LIST() - }, - .post_load = imx_ccm_post_load, -}; - -/* CCMR */ -#define CCMR_FPME (1<<0) -#define CCMR_MPE (1<<3) -#define CCMR_MDS (1<<7) -#define CCMR_FPMF (1<<26) -#define CCMR_PRCS (3<<1) - -/* PDR0 */ -#define PDR0_MCU_PODF_SHIFT (0) -#define PDR0_MCU_PODF_MASK (0x7) -#define PDR0_MAX_PODF_SHIFT (3) -#define PDR0_MAX_PODF_MASK (0x7) -#define PDR0_IPG_PODF_SHIFT (6) -#define PDR0_IPG_PODF_MASK (0x3) -#define PDR0_NFC_PODF_SHIFT (8) -#define PDR0_NFC_PODF_MASK (0x7) -#define PDR0_HSP_PODF_SHIFT (11) -#define PDR0_HSP_PODF_MASK (0x7) -#define PDR0_PER_PODF_SHIFT (16) -#define PDR0_PER_PODF_MASK (0x1f) -#define PDR0_CSI_PODF_SHIFT (23) -#define PDR0_CSI_PODF_MASK (0x1ff) - -#define EXTRACT(value, name) (((value) >> PDR0_##name##_PODF_SHIFT) \ - & PDR0_##name##_PODF_MASK) -#define INSERT(value, name) (((value) & PDR0_##name##_PODF_MASK) << \ - PDR0_##name##_PODF_SHIFT) -/* PLL control registers */ -#define PD(v) (((v) >> 26) & 0xf) -#define MFD(v) (((v) >> 16) & 0x3ff) -#define MFI(v) (((v) >> 10) & 0xf); -#define MFN(v) ((v) & 0x3ff) +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_CCM) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_CCM, \ + __func__, ##args); \ + } \ + } while (0) -#define PLL_PD(x) (((x) & 0xf) << 26) -#define PLL_MFD(x) (((x) & 0x3ff) << 16) -#define PLL_MFI(x) (((x) & 0xf) << 10) -#define PLL_MFN(x) (((x) & 0x3ff) << 0) -uint32_t imx_clock_frequency(DeviceState *dev, IMXClk clock) +uint32_t imx_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) { - IMXCCMState *s = IMX_CCM(dev); + uint32_t freq = 0; + IMXCCMClass *klass = IMX_GET_CLASS(dev); - switch (clock) { - case NOCLK: - return 0; - case MCU: - return s->mcu_clk_freq; - case HSP: - return s->hsp_clk_freq; - case IPG: - return s->ipg_clk_freq; - case CLK_32k: - return CKIL_FREQ; + if (klass->get_clock_frequency) { + freq = klass->get_clock_frequency(dev, clock); } - return 0; + + DPRINTF("(clock = %d) = %d\n", clock, freq); + + return freq; } /* * Calculate PLL output frequency */ -static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq) +uint32_t imx_ccm_calc_pll(uint32_t pllreg, uint32_t base_freq) { + int32_t freq; int32_t mfn = MFN(pllreg); /* Numerator */ uint32_t mfi = MFI(pllreg); /* Integer part */ uint32_t mfd = 1 + MFD(pllreg); /* Denominator */ @@ -142,180 +55,26 @@ static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq) if (mfi < 5) { mfi = 5; } + /* mfn is 10-bit signed twos-complement */ mfn <<= 32 - 10; mfn >>= 32 - 10; - return ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) / + freq = ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) / (mfd * pd)) << 10; -} - -static void update_clocks(IMXCCMState *s) -{ - /* - * If we ever emulate more clocks, this should switch to a data-driven - * approach - */ - - if ((s->ccmr & CCMR_PRCS) == 2) { - s->pll_refclk_freq = CKIL_FREQ * 1024; - } else { - s->pll_refclk_freq = CKIH_FREQ; - } - - /* ipg_clk_arm aka MCU clock */ - if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) { - s->mcu_clk_freq = s->pll_refclk_freq; - } else { - s->mcu_clk_freq = calc_pll(s->mpctl, s->pll_refclk_freq); - } - - /* High-speed clock */ - s->hsp_clk_freq = s->mcu_clk_freq / (1 + EXTRACT(s->pdr0, HSP)); - s->ipg_clk_freq = s->hsp_clk_freq / (1 + EXTRACT(s->pdr0, IPG)); - DPRINTF("Clocks: mcu %uMHz, HSP %uMHz, IPG %uHz\n", - s->mcu_clk_freq / 1000000, - s->hsp_clk_freq / 1000000, - s->ipg_clk_freq); -} - -static void imx_ccm_reset(DeviceState *dev) -{ - IMXCCMState *s = IMX_CCM(dev); - - s->ccmr = 0x074b0b7b; - s->pdr0 = 0xff870b48; - s->pdr1 = 0x49fcfe7f; - s->mpctl = PLL_PD(1) | PLL_MFD(0) | PLL_MFI(6) | PLL_MFN(0); - s->cgr[0] = s->cgr[1] = s->cgr[2] = 0xffffffff; - s->spctl = PLL_PD(1) | PLL_MFD(4) | PLL_MFI(0xc) | PLL_MFN(1); - s->pmcr0 = 0x80209828; - - update_clocks(s); -} - -static uint64_t imx_ccm_read(void *opaque, hwaddr offset, - unsigned size) -{ - IMXCCMState *s = (IMXCCMState *)opaque; - - DPRINTF("read(offset=%x)", offset >> 2); - switch (offset >> 2) { - case 0: /* CCMR */ - DPRINTF(" ccmr = 0x%x\n", s->ccmr); - return s->ccmr; - case 1: - DPRINTF(" pdr0 = 0x%x\n", s->pdr0); - return s->pdr0; - case 2: - DPRINTF(" pdr1 = 0x%x\n", s->pdr1); - return s->pdr1; - case 4: - DPRINTF(" mpctl = 0x%x\n", s->mpctl); - return s->mpctl; - case 6: - DPRINTF(" spctl = 0x%x\n", s->spctl); - return s->spctl; - case 8: - DPRINTF(" cgr0 = 0x%x\n", s->cgr[0]); - return s->cgr[0]; - case 9: - DPRINTF(" cgr1 = 0x%x\n", s->cgr[1]); - return s->cgr[1]; - case 10: - DPRINTF(" cgr2 = 0x%x\n", s->cgr[2]); - return s->cgr[2]; - case 18: /* LTR1 */ - return 0x00004040; - case 23: - DPRINTF(" pcmr0 = 0x%x\n", s->pmcr0); - return s->pmcr0; - } - DPRINTF(" return 0\n"); - return 0; -} - -static void imx_ccm_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - IMXCCMState *s = (IMXCCMState *)opaque; - - DPRINTF("write(offset=%x, value = %x)\n", - offset >> 2, (unsigned int)value); - switch (offset >> 2) { - case 0: - s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff); - break; - case 1: - s->pdr0 = value & 0xff9f3fff; - break; - case 2: - s->pdr1 = value; - break; - case 4: - s->mpctl = value & 0xbfff3fff; - break; - case 6: - s->spctl = value & 0xbfff3fff; - break; - case 8: - s->cgr[0] = value; - return; - case 9: - s->cgr[1] = value; - return; - case 10: - s->cgr[2] = value; - return; - - default: - return; - } - update_clocks(s); -} - -static const struct MemoryRegionOps imx_ccm_ops = { - .read = imx_ccm_read, - .write = imx_ccm_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int imx_ccm_init(SysBusDevice *dev) -{ - IMXCCMState *s = IMX_CCM(dev); - - memory_region_init_io(&s->iomem, OBJECT(dev), &imx_ccm_ops, s, - "imx_ccm", 0x1000); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static int imx_ccm_post_load(void *opaque, int version_id) -{ - IMXCCMState *s = (IMXCCMState *)opaque; - - update_clocks(s); - return 0; -} - -static void imx_ccm_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); + DPRINTF("(pllreg = 0x%08x, base_freq = %d) = %d\n", pllreg, base_freq, + freq); - sbc->init = imx_ccm_init; - dc->reset = imx_ccm_reset; - dc->vmsd = &vmstate_imx_ccm; - dc->desc = "i.MX Clock Control Module"; + return freq; } static const TypeInfo imx_ccm_info = { - .name = TYPE_IMX_CCM, - .parent = TYPE_SYS_BUS_DEVICE, + .name = TYPE_IMX_CCM, + .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IMXCCMState), - .class_init = imx_ccm_class_init, + .class_size = sizeof(IMXCCMClass), + .abstract = true, }; static void imx_ccm_register_types(void) diff --git a/qemu/hw/misc/ivshmem.c b/qemu/hw/misc/ivshmem.c index cc76989a3..e40f23bfc 100644 --- a/qemu/hw/misc/ivshmem.c +++ b/qemu/hw/misc/ivshmem.c @@ -16,39 +16,57 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/cutils.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/pci/pci.h" +#include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "sysemu/kvm.h" #include "migration/migration.h" #include "qemu/error-report.h" #include "qemu/event_notifier.h" -#include "qemu/fifo8.h" +#include "qom/object_interfaces.h" #include "sysemu/char.h" +#include "sysemu/hostmem.h" +#include "sysemu/qtest.h" +#include "qapi/visitor.h" +#include "exec/ram_addr.h" + +#include "hw/misc/ivshmem.h" #include -#include -#include #define PCI_VENDOR_ID_IVSHMEM PCI_VENDOR_ID_REDHAT_QUMRANET #define PCI_DEVICE_ID_IVSHMEM 0x1110 +#define IVSHMEM_MAX_PEERS UINT16_MAX #define IVSHMEM_IOEVENTFD 0 #define IVSHMEM_MSI 1 -#define IVSHMEM_PEER 0 -#define IVSHMEM_MASTER 1 - #define IVSHMEM_REG_BAR_SIZE 0x100 -//#define DEBUG_IVSHMEM -#ifdef DEBUG_IVSHMEM -#define IVSHMEM_DPRINTF(fmt, ...) \ - do {printf("IVSHMEM: " fmt, ## __VA_ARGS__); } while (0) -#else -#define IVSHMEM_DPRINTF(fmt, ...) -#endif +#define IVSHMEM_DEBUG 0 +#define IVSHMEM_DPRINTF(fmt, ...) \ + do { \ + if (IVSHMEM_DEBUG) { \ + printf("IVSHMEM: " fmt, ## __VA_ARGS__); \ + } \ + } while (0) + +#define TYPE_IVSHMEM_COMMON "ivshmem-common" +#define IVSHMEM_COMMON(obj) \ + OBJECT_CHECK(IVShmemState, (obj), TYPE_IVSHMEM_COMMON) + +#define TYPE_IVSHMEM_PLAIN "ivshmem-plain" +#define IVSHMEM_PLAIN(obj) \ + OBJECT_CHECK(IVShmemState, (obj), TYPE_IVSHMEM_PLAIN) + +#define TYPE_IVSHMEM_DOORBELL "ivshmem-doorbell" +#define IVSHMEM_DOORBELL(obj) \ + OBJECT_CHECK(IVShmemState, (obj), TYPE_IVSHMEM_DOORBELL) #define TYPE_IVSHMEM "ivshmem" #define IVSHMEM(obj) \ @@ -59,51 +77,50 @@ typedef struct Peer { EventNotifier *eventfds; } Peer; -typedef struct EventfdEntry { +typedef struct MSIVector { PCIDevice *pdev; - int vector; -} EventfdEntry; + int virq; +} MSIVector; typedef struct IVShmemState { /*< private >*/ PCIDevice parent_obj; /*< public >*/ + uint32_t features; + + /* exactly one of these two may be set */ + HostMemoryBackend *hostmem; /* with interrupts */ + CharDriverState *server_chr; /* without interrupts */ + + /* registers */ uint32_t intrmask; uint32_t intrstatus; - uint32_t doorbell; - - CharDriverState **eventfd_chr; - CharDriverState *server_chr; - Fifo8 incoming_fifo; - MemoryRegion ivshmem_mmio; + int vm_id; - /* We might need to register the BAR before we actually have the memory. - * So prepare a container MemoryRegion for the BAR immediately and - * add a subregion when we have the memory. - */ - MemoryRegion bar; - MemoryRegion ivshmem; - uint64_t ivshmem_size; /* size of shared memory region */ - uint32_t ivshmem_attr; - uint32_t ivshmem_64bit; - int shm_fd; /* shared memory file descriptor */ + /* BARs */ + MemoryRegion ivshmem_mmio; /* BAR 0 (registers) */ + MemoryRegion *ivshmem_bar2; /* BAR 2 (shared memory) */ + MemoryRegion server_bar2; /* used with server_chr */ + /* interrupt support */ Peer *peers; - int nb_peers; /* how many guests we have space for */ - int max_peer; /* maximum numbered peer */ - - int vm_id; + int nb_peers; /* space in @peers[] */ uint32_t vectors; - uint32_t features; - EventfdEntry *eventfd_table; + MSIVector *msi_vectors; + uint64_t msg_buf; /* buffer for receiving server messages */ + int msg_buffered_bytes; /* #bytes in @msg_buf */ + /* migration stuff */ + OnOffAuto master; Error *migration_blocker; - char * shmobj; - char * sizearg; - char * role; - int role_val; /* scalar to avoid multiple string comparisons */ + /* legacy cruft */ + char *role; + char *shmobj; + char *sizearg; + size_t legacy_size; + uint32_t not_legacy_32bit; } IVShmemState; /* registers for the Inter-VM shared memory device */ @@ -119,16 +136,34 @@ static inline uint32_t ivshmem_has_feature(IVShmemState *ivs, return (ivs->features & (1 << feature)); } -static inline bool is_power_of_two(uint64_t x) { - return (x & (x - 1)) == 0; +static inline bool ivshmem_is_master(IVShmemState *s) +{ + assert(s->master != ON_OFF_AUTO_AUTO); + return s->master == ON_OFF_AUTO_ON; } -/* accessing registers - based on rtl8139 */ -static void ivshmem_update_irq(IVShmemState *s, int val) +static void ivshmem_update_irq(IVShmemState *s) { PCIDevice *d = PCI_DEVICE(s); - int isr; - isr = (s->intrstatus & s->intrmask) & 0xffffffff; + uint32_t isr = s->intrstatus & s->intrmask; + + /* + * Do nothing unless the device actually uses INTx. Here's how + * the device variants signal interrupts, what they put in PCI + * config space: + * Device variant Interrupt Interrupt Pin MSI-X cap. + * ivshmem-plain none 0 no + * ivshmem-doorbell MSI-X 1 yes(1) + * ivshmem,msi=off INTx 1 no + * ivshmem,msi=on MSI-X 1(2) yes(1) + * (1) if guest enabled MSI-X + * (2) the device lies + * Leads to the condition for doing nothing: + */ + if (ivshmem_has_feature(s, IVSHMEM_MSI) + || !d->config[PCI_INTERRUPT_PIN]) { + return; + } /* don't print ISR resets */ if (isr) { @@ -136,7 +171,7 @@ static void ivshmem_update_irq(IVShmemState *s, int val) isr ? 1 : 0, s->intrstatus, s->intrmask); } - pci_set_irq(d, (isr != 0)); + pci_set_irq(d, isr != 0); } static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val) @@ -144,8 +179,7 @@ static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val) IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val); s->intrmask = val; - - ivshmem_update_irq(s, val); + ivshmem_update_irq(s); } static uint32_t ivshmem_IntrMask_read(IVShmemState *s) @@ -153,7 +187,6 @@ static uint32_t ivshmem_IntrMask_read(IVShmemState *s) uint32_t ret = s->intrmask; IVSHMEM_DPRINTF("intrmask read(w) val = 0x%04x\n", ret); - return ret; } @@ -162,8 +195,7 @@ static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val) IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val); s->intrstatus = val; - - ivshmem_update_irq(s, val); + ivshmem_update_irq(s); } static uint32_t ivshmem_IntrStatus_read(IVShmemState *s) @@ -172,9 +204,7 @@ static uint32_t ivshmem_IntrStatus_read(IVShmemState *s) /* reading ISR clears all interrupts */ s->intrstatus = 0; - - ivshmem_update_irq(s, 0); - + ivshmem_update_irq(s); return ret; } @@ -201,7 +231,7 @@ static void ivshmem_io_write(void *opaque, hwaddr addr, case DOORBELL: /* check that dest VM ID is reasonable */ - if (dest > s->max_peer) { + if (dest >= s->nb_peers) { IVSHMEM_DPRINTF("Invalid destination VM ID (%d)\n", dest); break; } @@ -210,10 +240,13 @@ static void ivshmem_io_write(void *opaque, hwaddr addr, if (vector < s->peers[dest].nb_eventfds) { IVSHMEM_DPRINTF("Notifying VM %d on vector %d\n", dest, vector); event_notifier_set(&s->peers[dest].eventfds[vector]); + } else { + IVSHMEM_DPRINTF("Invalid destination vector %d on VM %d\n", + vector, dest); } break; default: - IVSHMEM_DPRINTF("Invalid VM Doorbell VM %d\n", dest); + IVSHMEM_DPRINTF("Unhandled write " TARGET_FMT_plx "\n", addr); } } @@ -235,12 +268,7 @@ static uint64_t ivshmem_io_read(void *opaque, hwaddr addr, break; case IVPOSITION: - /* return my VM ID if the memory is mapped */ - if (s->shm_fd > 0) { - ret = s->vm_id; - } else { - ret = -1; - } + ret = s->vm_id; break; default: @@ -261,105 +289,95 @@ static const MemoryRegionOps ivshmem_mmio_ops = { }, }; -static void ivshmem_receive(void *opaque, const uint8_t *buf, int size) +static void ivshmem_vector_notify(void *opaque) { - IVShmemState *s = opaque; - - ivshmem_IntrStatus_write(s, *buf); + MSIVector *entry = opaque; + PCIDevice *pdev = entry->pdev; + IVShmemState *s = IVSHMEM_COMMON(pdev); + int vector = entry - s->msi_vectors; + EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; - IVSHMEM_DPRINTF("ivshmem_receive 0x%02x\n", *buf); -} + if (!event_notifier_test_and_clear(n)) { + return; + } -static int ivshmem_can_receive(void * opaque) -{ - return 8; + IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, vector); + if (ivshmem_has_feature(s, IVSHMEM_MSI)) { + if (msix_enabled(pdev)) { + msix_notify(pdev, vector); + } + } else { + ivshmem_IntrStatus_write(s, 1); + } } -static void ivshmem_event(void *opaque, int event) +static int ivshmem_vector_unmask(PCIDevice *dev, unsigned vector, + MSIMessage msg) { - IVSHMEM_DPRINTF("ivshmem_event %d\n", event); -} + IVShmemState *s = IVSHMEM_COMMON(dev); + EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; + MSIVector *v = &s->msi_vectors[vector]; + int ret; -static void fake_irqfd(void *opaque, const uint8_t *buf, int size) { + IVSHMEM_DPRINTF("vector unmask %p %d\n", dev, vector); - EventfdEntry *entry = opaque; - PCIDevice *pdev = entry->pdev; + ret = kvm_irqchip_update_msi_route(kvm_state, v->virq, msg, dev); + if (ret < 0) { + return ret; + } - IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, entry->vector); - msix_notify(pdev, entry->vector); + return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, v->virq); } -static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier *n, - int vector) +static void ivshmem_vector_mask(PCIDevice *dev, unsigned vector) { - /* create a event character device based on the passed eventfd */ - IVShmemState *s = opaque; - CharDriverState * chr; - int eventfd = event_notifier_get_fd(n); - - chr = qemu_chr_open_eventfd(eventfd); - - if (chr == NULL) { - error_report("creating eventfd for eventfd %d failed", eventfd); - exit(1); - } - qemu_chr_fe_claim_no_fail(chr); + IVShmemState *s = IVSHMEM_COMMON(dev); + EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; + int ret; - /* if MSI is supported we need multiple interrupts */ - if (ivshmem_has_feature(s, IVSHMEM_MSI)) { - s->eventfd_table[vector].pdev = PCI_DEVICE(s); - s->eventfd_table[vector].vector = vector; + IVSHMEM_DPRINTF("vector mask %p %d\n", dev, vector); - qemu_chr_add_handlers(chr, ivshmem_can_receive, fake_irqfd, - ivshmem_event, &s->eventfd_table[vector]); - } else { - qemu_chr_add_handlers(chr, ivshmem_can_receive, ivshmem_receive, - ivshmem_event, s); + ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, + s->msi_vectors[vector].virq); + if (ret != 0) { + error_report("remove_irqfd_notifier_gsi failed"); } +} - return chr; +static void ivshmem_vector_poll(PCIDevice *dev, + unsigned int vector_start, + unsigned int vector_end) +{ + IVShmemState *s = IVSHMEM_COMMON(dev); + unsigned int vector; -} + IVSHMEM_DPRINTF("vector poll %p %d-%d\n", dev, vector_start, vector_end); -static int check_shm_size(IVShmemState *s, int fd) { - /* check that the guest isn't going to try and map more memory than the - * the object has allocated return -1 to indicate error */ + vector_end = MIN(vector_end, s->vectors); - struct stat buf; + for (vector = vector_start; vector < vector_end; vector++) { + EventNotifier *notifier = &s->peers[s->vm_id].eventfds[vector]; - if (fstat(fd, &buf) < 0) { - error_report("exiting: fstat on fd %d failed: %s", - fd, strerror(errno)); - return -1; - } + if (!msix_is_masked(dev, vector)) { + continue; + } - if (s->ivshmem_size > buf.st_size) { - error_report("Requested memory size greater" - " than shared object size (%" PRIu64 " > %" PRIu64")", - s->ivshmem_size, (uint64_t)buf.st_size); - return -1; - } else { - return 0; + if (event_notifier_test_and_clear(notifier)) { + msix_set_pending(dev, vector); + } } } -/* create the shared memory BAR when we are not using the server, so we can - * create the BAR and map the memory immediately */ -static void create_shared_memory_BAR(IVShmemState *s, int fd) { - - void * ptr; - - s->shm_fd = fd; - - ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); +static void watch_vector_notifier(IVShmemState *s, EventNotifier *n, + int vector) +{ + int eventfd = event_notifier_get_fd(n); - memory_region_init_ram_ptr(&s->ivshmem, OBJECT(s), "ivshmem.bar2", - s->ivshmem_size, ptr); - vmstate_register_ram(&s->ivshmem, DEVICE(s)); - memory_region_add_subregion(&s->bar, 0, &s->ivshmem); + assert(!s->msi_vectors[vector].pdev); + s->msi_vectors[vector].pdev = PCI_DEVICE(s); - /* region for shared memory */ - pci_register_bar(PCI_DEVICE(s), 2, s->ivshmem_attr, &s->bar); + qemu_set_fd_handler(eventfd, ivshmem_vector_notify, + NULL, &s->msi_vectors[vector]); } static void ivshmem_add_eventfd(IVShmemState *s, int posn, int i) @@ -382,25 +400,22 @@ static void ivshmem_del_eventfd(IVShmemState *s, int posn, int i) &s->peers[posn].eventfds[i]); } -static void close_guest_eventfds(IVShmemState *s, int posn) +static void close_peer_eventfds(IVShmemState *s, int posn) { - int i, guest_curr_max; + int i, n; - if (!ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { - return; - } - if (posn < 0 || posn >= s->nb_peers) { - return; - } + assert(posn >= 0 && posn < s->nb_peers); + n = s->peers[posn].nb_eventfds; - guest_curr_max = s->peers[posn].nb_eventfds; - - memory_region_transaction_begin(); - for (i = 0; i < guest_curr_max; i++) { - ivshmem_del_eventfd(s, posn, i); + if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { + memory_region_transaction_begin(); + for (i = 0; i < n; i++) { + ivshmem_del_eventfd(s, posn, i); + } + memory_region_transaction_commit(); } - memory_region_transaction_commit(); - for (i = 0; i < guest_curr_max; i++) { + + for (i = 0; i < n; i++) { event_notifier_cleanup(&s->peers[posn].eventfds[i]); } @@ -408,187 +423,320 @@ static void close_guest_eventfds(IVShmemState *s, int posn) s->peers[posn].nb_eventfds = 0; } -/* this function increase the dynamic storage need to store data about other - * guests */ -static int increase_dynamic_storage(IVShmemState *s, int new_min_size) +static void resize_peers(IVShmemState *s, int nb_peers) { + int old_nb_peers = s->nb_peers; + int i; - int j, old_nb_alloc; - - /* check for integer overflow */ - if (new_min_size >= INT_MAX / sizeof(Peer) - 1 || new_min_size <= 0) { - return -1; - } + assert(nb_peers > old_nb_peers); + IVSHMEM_DPRINTF("bumping storage to %d peers\n", nb_peers); - old_nb_alloc = s->nb_peers; + s->peers = g_realloc(s->peers, nb_peers * sizeof(Peer)); + s->nb_peers = nb_peers; - if (new_min_size >= s->nb_peers) { - /* +1 because #new_min_size is used as last array index */ - s->nb_peers = new_min_size + 1; - } else { - return 0; + for (i = old_nb_peers; i < nb_peers; i++) { + s->peers[i].eventfds = g_new0(EventNotifier, s->vectors); + s->peers[i].nb_eventfds = 0; } +} - IVSHMEM_DPRINTF("bumping storage to %d guests\n", s->nb_peers); - s->peers = g_realloc(s->peers, s->nb_peers * sizeof(Peer)); +static void ivshmem_add_kvm_msi_virq(IVShmemState *s, int vector, + Error **errp) +{ + PCIDevice *pdev = PCI_DEVICE(s); + MSIMessage msg = msix_get_message(pdev, vector); + int ret; - /* zero out new pointers */ - for (j = old_nb_alloc; j < s->nb_peers; j++) { - s->peers[j].eventfds = NULL; - s->peers[j].nb_eventfds = 0; + IVSHMEM_DPRINTF("ivshmem_add_kvm_msi_virq vector:%d\n", vector); + assert(!s->msi_vectors[vector].pdev); + + ret = kvm_irqchip_add_msi_route(kvm_state, msg, pdev); + if (ret < 0) { + error_setg(errp, "kvm_irqchip_add_msi_route failed"); + return; } - return 0; + s->msi_vectors[vector].virq = ret; + s->msi_vectors[vector].pdev = pdev; } -static void ivshmem_read(void *opaque, const uint8_t *buf, int size) +static void setup_interrupt(IVShmemState *s, int vector, Error **errp) { - IVShmemState *s = opaque; - int incoming_fd, tmp_fd; - int guest_max_eventfd; - long incoming_posn; - - if (fifo8_is_empty(&s->incoming_fifo) && size == sizeof(incoming_posn)) { - memcpy(&incoming_posn, buf, size); - } else { - const uint8_t *p; - uint32_t num; - - IVSHMEM_DPRINTF("short read of %d bytes\n", size); - num = MAX(size, sizeof(long) - fifo8_num_used(&s->incoming_fifo)); - fifo8_push_all(&s->incoming_fifo, buf, num); - if (fifo8_num_used(&s->incoming_fifo) < sizeof(incoming_posn)) { + EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; + bool with_irqfd = kvm_msi_via_irqfd_enabled() && + ivshmem_has_feature(s, IVSHMEM_MSI); + PCIDevice *pdev = PCI_DEVICE(s); + Error *err = NULL; + + IVSHMEM_DPRINTF("setting up interrupt for vector: %d\n", vector); + + if (!with_irqfd) { + IVSHMEM_DPRINTF("with eventfd\n"); + watch_vector_notifier(s, n, vector); + } else if (msix_enabled(pdev)) { + IVSHMEM_DPRINTF("with irqfd\n"); + ivshmem_add_kvm_msi_virq(s, vector, &err); + if (err) { + error_propagate(errp, err); return; } - size -= num; - buf += num; - p = fifo8_pop_buf(&s->incoming_fifo, sizeof(incoming_posn), &num); - g_assert(num == sizeof(incoming_posn)); - memcpy(&incoming_posn, p, sizeof(incoming_posn)); - if (size > 0) { - fifo8_push_all(&s->incoming_fifo, buf, size); + + if (!msix_is_masked(pdev, vector)) { + kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, + s->msi_vectors[vector].virq); + /* TODO handle error */ } + } else { + /* it will be delayed until msix is enabled, in write_config */ + IVSHMEM_DPRINTF("with irqfd, delayed until msix enabled\n"); } +} - if (incoming_posn < -1) { - IVSHMEM_DPRINTF("invalid incoming_posn %ld\n", incoming_posn); +static void process_msg_shmem(IVShmemState *s, int fd, Error **errp) +{ + struct stat buf; + size_t size; + void *ptr; + + if (s->ivshmem_bar2) { + error_setg(errp, "server sent unexpected shared memory message"); + close(fd); + return; + } + + if (fstat(fd, &buf) < 0) { + error_setg_errno(errp, errno, + "can't determine size of shared memory sent by server"); + close(fd); return; } - /* pick off s->server_chr->msgfd and store it, posn should accompany msg */ - tmp_fd = qemu_chr_fe_get_msgfd(s->server_chr); - IVSHMEM_DPRINTF("posn is %ld, fd is %d\n", incoming_posn, tmp_fd); + size = buf.st_size; - /* make sure we have enough space for this guest */ - if (incoming_posn >= s->nb_peers) { - if (increase_dynamic_storage(s, incoming_posn) < 0) { - error_report("increase_dynamic_storage() failed"); - if (tmp_fd != -1) { - close(tmp_fd); - } + /* Legacy cruft */ + if (s->legacy_size != SIZE_MAX) { + if (size < s->legacy_size) { + error_setg(errp, "server sent only %zd bytes of shared memory", + (size_t)buf.st_size); + close(fd); return; } + size = s->legacy_size; } - if (tmp_fd == -1) { - /* if posn is positive and unseen before then this is our posn*/ - if ((incoming_posn >= 0) && - (s->peers[incoming_posn].eventfds == NULL)) { - /* receive our posn */ - s->vm_id = incoming_posn; - return; - } else { - /* otherwise an fd == -1 means an existing guest has gone away */ - IVSHMEM_DPRINTF("posn %ld has gone away\n", incoming_posn); - close_guest_eventfds(s, incoming_posn); - return; - } + /* mmap the region and map into the BAR2 */ + ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (ptr == MAP_FAILED) { + error_setg_errno(errp, errno, "Failed to mmap shared memory"); + close(fd); + return; } + memory_region_init_ram_ptr(&s->server_bar2, OBJECT(s), + "ivshmem.bar2", size, ptr); + qemu_set_ram_fd(memory_region_get_ram_addr(&s->server_bar2), fd); + s->ivshmem_bar2 = &s->server_bar2; +} - /* because of the implementation of get_msgfd, we need a dup */ - incoming_fd = dup(tmp_fd); +static void process_msg_disconnect(IVShmemState *s, uint16_t posn, + Error **errp) +{ + IVSHMEM_DPRINTF("posn %d has gone away\n", posn); + if (posn >= s->nb_peers || posn == s->vm_id) { + error_setg(errp, "invalid peer %d", posn); + return; + } + close_peer_eventfds(s, posn); +} - if (incoming_fd == -1) { - error_report("could not allocate file descriptor %s", strerror(errno)); - close(tmp_fd); +static void process_msg_connect(IVShmemState *s, uint16_t posn, int fd, + Error **errp) +{ + Peer *peer = &s->peers[posn]; + int vector; + + /* + * The N-th connect message for this peer comes with the file + * descriptor for vector N-1. Count messages to find the vector. + */ + if (peer->nb_eventfds >= s->vectors) { + error_setg(errp, "Too many eventfd received, device has %d vectors", + s->vectors); + close(fd); return; } + vector = peer->nb_eventfds++; + + IVSHMEM_DPRINTF("eventfds[%d][%d] = %d\n", posn, vector, fd); + event_notifier_init_fd(&peer->eventfds[vector], fd); + fcntl_setfl(fd, O_NONBLOCK); /* msix/irqfd poll non block */ + + if (posn == s->vm_id) { + setup_interrupt(s, vector, errp); + /* TODO do we need to handle the error? */ + } - /* if the position is -1, then it's shared memory region fd */ - if (incoming_posn == -1) { + if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { + ivshmem_add_eventfd(s, posn, vector); + } +} - void * map_ptr; +static void process_msg(IVShmemState *s, int64_t msg, int fd, Error **errp) +{ + IVSHMEM_DPRINTF("posn is %" PRId64 ", fd is %d\n", msg, fd); - s->max_peer = 0; + if (msg < -1 || msg > IVSHMEM_MAX_PEERS) { + error_setg(errp, "server sent invalid message %" PRId64, msg); + close(fd); + return; + } - if (check_shm_size(s, incoming_fd) == -1) { - exit(1); - } + if (msg == -1) { + process_msg_shmem(s, fd, errp); + return; + } - /* mmap the region and map into the BAR2 */ - map_ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, - incoming_fd, 0); - memory_region_init_ram_ptr(&s->ivshmem, OBJECT(s), - "ivshmem.bar2", s->ivshmem_size, map_ptr); - vmstate_register_ram(&s->ivshmem, DEVICE(s)); + if (msg >= s->nb_peers) { + resize_peers(s, msg + 1); + } - IVSHMEM_DPRINTF("guest h/w addr = %p, size = %" PRIu64 "\n", - map_ptr, s->ivshmem_size); + if (fd >= 0) { + process_msg_connect(s, msg, fd, errp); + } else { + process_msg_disconnect(s, msg, errp); + } +} - memory_region_add_subregion(&s->bar, 0, &s->ivshmem); +static int ivshmem_can_receive(void *opaque) +{ + IVShmemState *s = opaque; - /* only store the fd if it is successfully mapped */ - s->shm_fd = incoming_fd; + assert(s->msg_buffered_bytes < sizeof(s->msg_buf)); + return sizeof(s->msg_buf) - s->msg_buffered_bytes; +} +static void ivshmem_read(void *opaque, const uint8_t *buf, int size) +{ + IVShmemState *s = opaque; + Error *err = NULL; + int fd; + int64_t msg; + + assert(size >= 0 && s->msg_buffered_bytes + size <= sizeof(s->msg_buf)); + memcpy((unsigned char *)&s->msg_buf + s->msg_buffered_bytes, buf, size); + s->msg_buffered_bytes += size; + if (s->msg_buffered_bytes < sizeof(s->msg_buf)) { return; } + msg = le64_to_cpu(s->msg_buf); + s->msg_buffered_bytes = 0; - /* each guest has an array of eventfds, and we keep track of how many - * guests for each VM */ - guest_max_eventfd = s->peers[incoming_posn].nb_eventfds; + fd = qemu_chr_fe_get_msgfd(s->server_chr); + IVSHMEM_DPRINTF("posn is %" PRId64 ", fd is %d\n", msg, fd); - if (guest_max_eventfd == 0) { - /* one eventfd per MSI vector */ - s->peers[incoming_posn].eventfds = g_new(EventNotifier, s->vectors); + process_msg(s, msg, fd, &err); + if (err) { + error_report_err(err); } +} - /* this is an eventfd for a particular guest VM */ - IVSHMEM_DPRINTF("eventfds[%ld][%d] = %d\n", incoming_posn, - guest_max_eventfd, incoming_fd); - event_notifier_init_fd(&s->peers[incoming_posn].eventfds[guest_max_eventfd], - incoming_fd); +static int64_t ivshmem_recv_msg(IVShmemState *s, int *pfd, Error **errp) +{ + int64_t msg; + int n, ret; + + n = 0; + do { + ret = qemu_chr_fe_read_all(s->server_chr, (uint8_t *)&msg + n, + sizeof(msg) - n); + if (ret < 0 && ret != -EINTR) { + error_setg_errno(errp, -ret, "read from server failed"); + return INT64_MIN; + } + n += ret; + } while (n < sizeof(msg)); - /* increment count for particular guest */ - s->peers[incoming_posn].nb_eventfds++; + *pfd = qemu_chr_fe_get_msgfd(s->server_chr); + return msg; +} - /* keep track of the maximum VM ID */ - if (incoming_posn > s->max_peer) { - s->max_peer = incoming_posn; - } +static void ivshmem_recv_setup(IVShmemState *s, Error **errp) +{ + Error *err = NULL; + int64_t msg; + int fd; - if (incoming_posn == s->vm_id) { - s->eventfd_chr[guest_max_eventfd] = create_eventfd_chr_device(s, - &s->peers[s->vm_id].eventfds[guest_max_eventfd], - guest_max_eventfd); + msg = ivshmem_recv_msg(s, &fd, &err); + if (err) { + error_propagate(errp, err); + return; + } + if (msg != IVSHMEM_PROTOCOL_VERSION) { + error_setg(errp, "server sent version %" PRId64 ", expecting %d", + msg, IVSHMEM_PROTOCOL_VERSION); + return; + } + if (fd != -1) { + error_setg(errp, "server sent invalid version message"); + return; } - if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { - ivshmem_add_eventfd(s, incoming_posn, guest_max_eventfd); + /* + * ivshmem-server sends the remaining initial messages in a fixed + * order, but the device has always accepted them in any order. + * Stay as compatible as practical, just in case people use + * servers that behave differently. + */ + + /* + * ivshmem_device_spec.txt has always required the ID message + * right here, and ivshmem-server has always complied. However, + * older versions of the device accepted it out of order, but + * broke when an interrupt setup message arrived before it. + */ + msg = ivshmem_recv_msg(s, &fd, &err); + if (err) { + error_propagate(errp, err); + return; + } + if (fd != -1 || msg < 0 || msg > IVSHMEM_MAX_PEERS) { + error_setg(errp, "server sent invalid ID message"); + return; } + s->vm_id = msg; + + /* + * Receive more messages until we got shared memory. + */ + do { + msg = ivshmem_recv_msg(s, &fd, &err); + if (err) { + error_propagate(errp, err); + return; + } + process_msg(s, msg, fd, &err); + if (err) { + error_propagate(errp, err); + return; + } + } while (msg != -1); + + /* + * This function must either map the shared memory or fail. The + * loop above ensures that: it terminates normally only after it + * successfully processed the server's shared memory message. + * Assert that actually mapped the shared memory: + */ + assert(s->ivshmem_bar2); } /* Select the MSI-X vectors used by device. * ivshmem maps events to vectors statically, so * we just enable all vectors on init and after reset. */ -static void ivshmem_use_msix(IVShmemState * s) +static void ivshmem_msix_vector_use(IVShmemState *s) { PCIDevice *d = PCI_DEVICE(s); int i; - if (!msix_present(d)) { - return; - } - for (i = 0; i < s->vectors; i++) { msix_vector_use(d, i); } @@ -596,295 +744,579 @@ static void ivshmem_use_msix(IVShmemState * s) static void ivshmem_reset(DeviceState *d) { - IVShmemState *s = IVSHMEM(d); + IVShmemState *s = IVSHMEM_COMMON(d); s->intrstatus = 0; - ivshmem_use_msix(s); + s->intrmask = 0; + if (ivshmem_has_feature(s, IVSHMEM_MSI)) { + ivshmem_msix_vector_use(s); + } } -static uint64_t ivshmem_get_size(IVShmemState * s) { +static int ivshmem_setup_interrupts(IVShmemState *s) +{ + /* allocate QEMU callback data for receiving interrupts */ + s->msi_vectors = g_malloc0(s->vectors * sizeof(MSIVector)); - uint64_t value; - char *ptr; + if (ivshmem_has_feature(s, IVSHMEM_MSI)) { + if (msix_init_exclusive_bar(PCI_DEVICE(s), s->vectors, 1)) { + return -1; + } - value = strtoull(s->sizearg, &ptr, 10); - switch (*ptr) { - case 0: case 'M': case 'm': - value <<= 20; - break; - case 'G': case 'g': - value <<= 30; - break; - default: - error_report("invalid ram size: %s", s->sizearg); - exit(1); + IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors); + ivshmem_msix_vector_use(s); } - /* BARs must be a power of 2 */ - if (!is_power_of_two(value)) { - error_report("size must be power of 2"); - exit(1); + return 0; +} + +static void ivshmem_enable_irqfd(IVShmemState *s) +{ + PCIDevice *pdev = PCI_DEVICE(s); + int i; + + for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) { + Error *err = NULL; + + ivshmem_add_kvm_msi_virq(s, i, &err); + if (err) { + error_report_err(err); + /* TODO do we need to handle the error? */ + } } - return value; + if (msix_set_vector_notifiers(pdev, + ivshmem_vector_unmask, + ivshmem_vector_mask, + ivshmem_vector_poll)) { + error_report("ivshmem: msix_set_vector_notifiers failed"); + } } -static void ivshmem_setup_msi(IVShmemState * s) +static void ivshmem_remove_kvm_msi_virq(IVShmemState *s, int vector) { - if (msix_init_exclusive_bar(PCI_DEVICE(s), s->vectors, 1)) { - IVSHMEM_DPRINTF("msix initialization failed\n"); - exit(1); - } + IVSHMEM_DPRINTF("ivshmem_remove_kvm_msi_virq vector:%d\n", vector); - IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors); + if (s->msi_vectors[vector].pdev == NULL) { + return; + } - /* allocate QEMU char devices for receiving interrupts */ - s->eventfd_table = g_malloc0(s->vectors * sizeof(EventfdEntry)); + /* it was cleaned when masked in the frontend. */ + kvm_irqchip_release_virq(kvm_state, s->msi_vectors[vector].virq); - ivshmem_use_msix(s); + s->msi_vectors[vector].pdev = NULL; } -static void ivshmem_save(QEMUFile* f, void *opaque) +static void ivshmem_disable_irqfd(IVShmemState *s) { - IVShmemState *proxy = opaque; - PCIDevice *pci_dev = PCI_DEVICE(proxy); - - IVSHMEM_DPRINTF("ivshmem_save\n"); - pci_device_save(pci_dev, f); + PCIDevice *pdev = PCI_DEVICE(s); + int i; - if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) { - msix_save(pci_dev, f); - } else { - qemu_put_be32(f, proxy->intrstatus); - qemu_put_be32(f, proxy->intrmask); + for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) { + ivshmem_remove_kvm_msi_virq(s, i); } + msix_unset_vector_notifiers(pdev); } -static int ivshmem_load(QEMUFile* f, void *opaque, int version_id) +static void ivshmem_write_config(PCIDevice *pdev, uint32_t address, + uint32_t val, int len) { - IVSHMEM_DPRINTF("ivshmem_load\n"); + IVShmemState *s = IVSHMEM_COMMON(pdev); + int is_enabled, was_enabled = msix_enabled(pdev); - IVShmemState *proxy = opaque; - PCIDevice *pci_dev = PCI_DEVICE(proxy); - int ret; + pci_default_write_config(pdev, address, val, len); + is_enabled = msix_enabled(pdev); - if (version_id > 0) { - return -EINVAL; + if (kvm_msi_via_irqfd_enabled()) { + if (!was_enabled && is_enabled) { + ivshmem_enable_irqfd(s); + } else if (was_enabled && !is_enabled) { + ivshmem_disable_irqfd(s); + } } +} - if (proxy->role_val == IVSHMEM_PEER) { - error_report("'peer' devices are not migratable"); - return -EINVAL; +static void ivshmem_common_realize(PCIDevice *dev, Error **errp) +{ + IVShmemState *s = IVSHMEM_COMMON(dev); + Error *err = NULL; + uint8_t *pci_conf; + uint8_t attr = PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_PREFETCH; + + /* IRQFD requires MSI */ + if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) && + !ivshmem_has_feature(s, IVSHMEM_MSI)) { + error_setg(errp, "ioeventfd/irqfd requires MSI"); + return; } - ret = pci_device_load(pci_dev, f); - if (ret) { - return ret; + pci_conf = dev->config; + pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY; + + memory_region_init_io(&s->ivshmem_mmio, OBJECT(s), &ivshmem_mmio_ops, s, + "ivshmem-mmio", IVSHMEM_REG_BAR_SIZE); + + /* region for registers*/ + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, + &s->ivshmem_mmio); + + if (!s->not_legacy_32bit) { + attr |= PCI_BASE_ADDRESS_MEM_TYPE_64; } - if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) { - msix_load(pci_dev, f); - ivshmem_use_msix(proxy); + if (s->hostmem != NULL) { + IVSHMEM_DPRINTF("using hostmem\n"); + + s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem, + &error_abort); } else { - proxy->intrstatus = qemu_get_be32(f); - proxy->intrmask = qemu_get_be32(f); + assert(s->server_chr); + + IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n", + s->server_chr->filename); + + /* we allocate enough space for 16 peers and grow as needed */ + resize_peers(s, 16); + + /* + * Receive setup messages from server synchronously. + * Older versions did it asynchronously, but that creates a + * number of entertaining race conditions. + */ + ivshmem_recv_setup(s, &err); + if (err) { + error_propagate(errp, err); + return; + } + + if (s->master == ON_OFF_AUTO_ON && s->vm_id != 0) { + error_setg(errp, + "master must connect to the server before any peers"); + return; + } + + qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, + ivshmem_read, NULL, s); + + if (ivshmem_setup_interrupts(s) < 0) { + error_setg(errp, "failed to initialize interrupts"); + return; + } } - return 0; -} + vmstate_register_ram(s->ivshmem_bar2, DEVICE(s)); + pci_register_bar(PCI_DEVICE(s), 2, attr, s->ivshmem_bar2); -static void ivshmem_write_config(PCIDevice *pci_dev, uint32_t address, - uint32_t val, int len) -{ - pci_default_write_config(pci_dev, address, val, len); + if (s->master == ON_OFF_AUTO_AUTO) { + s->master = s->vm_id == 0 ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; + } + + if (!ivshmem_is_master(s)) { + error_setg(&s->migration_blocker, + "Migration is disabled when using feature 'peer mode' in device 'ivshmem'"); + migrate_add_blocker(s->migration_blocker); + } } -static int pci_ivshmem_init(PCIDevice *dev) +static void ivshmem_exit(PCIDevice *dev) { - IVShmemState *s = IVSHMEM(dev); - uint8_t *pci_conf; + IVShmemState *s = IVSHMEM_COMMON(dev); + int i; - if (s->sizearg == NULL) - s->ivshmem_size = 4 << 20; /* 4 MB default */ - else { - s->ivshmem_size = ivshmem_get_size(s); + if (s->migration_blocker) { + migrate_del_blocker(s->migration_blocker); + error_free(s->migration_blocker); } - fifo8_create(&s->incoming_fifo, sizeof(long)); + if (memory_region_is_mapped(s->ivshmem_bar2)) { + if (!s->hostmem) { + void *addr = memory_region_get_ram_ptr(s->ivshmem_bar2); + int fd; - register_savevm(DEVICE(dev), "ivshmem", 0, 0, ivshmem_save, ivshmem_load, - dev); + if (munmap(addr, memory_region_size(s->ivshmem_bar2) == -1)) { + error_report("Failed to munmap shared memory %s", + strerror(errno)); + } - /* IRQFD requires MSI */ - if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) && - !ivshmem_has_feature(s, IVSHMEM_MSI)) { - error_report("ioeventfd/irqfd requires MSI"); - exit(1); + fd = qemu_get_ram_fd(memory_region_get_ram_addr(s->ivshmem_bar2)); + close(fd); + } + + vmstate_unregister_ram(s->ivshmem_bar2, DEVICE(dev)); } - /* check that role is reasonable */ - if (s->role) { - if (strncmp(s->role, "peer", 5) == 0) { - s->role_val = IVSHMEM_PEER; - } else if (strncmp(s->role, "master", 7) == 0) { - s->role_val = IVSHMEM_MASTER; - } else { - error_report("'role' must be 'peer' or 'master'"); - exit(1); + if (s->peers) { + for (i = 0; i < s->nb_peers; i++) { + close_peer_eventfds(s, i); } - } else { - s->role_val = IVSHMEM_MASTER; /* default */ + g_free(s->peers); } - if (s->role_val == IVSHMEM_PEER) { - error_setg(&s->migration_blocker, - "Migration is disabled when using feature 'peer mode' in device 'ivshmem'"); - migrate_add_blocker(s->migration_blocker); + if (ivshmem_has_feature(s, IVSHMEM_MSI)) { + msix_uninit_exclusive_bar(dev); } - pci_conf = dev->config; - pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY; + g_free(s->msi_vectors); +} - pci_config_set_interrupt_pin(pci_conf, 1); +static int ivshmem_pre_load(void *opaque) +{ + IVShmemState *s = opaque; - s->shm_fd = 0; + if (!ivshmem_is_master(s)) { + error_report("'peer' devices are not migratable"); + return -EINVAL; + } - memory_region_init_io(&s->ivshmem_mmio, OBJECT(s), &ivshmem_mmio_ops, s, - "ivshmem-mmio", IVSHMEM_REG_BAR_SIZE); + return 0; +} - /* region for registers*/ - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, - &s->ivshmem_mmio); +static int ivshmem_post_load(void *opaque, int version_id) +{ + IVShmemState *s = opaque; - memory_region_init(&s->bar, OBJECT(s), "ivshmem-bar2-container", s->ivshmem_size); - s->ivshmem_attr = PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_PREFETCH; - if (s->ivshmem_64bit) { - s->ivshmem_attr |= PCI_BASE_ADDRESS_MEM_TYPE_64; + if (ivshmem_has_feature(s, IVSHMEM_MSI)) { + ivshmem_msix_vector_use(s); } + return 0; +} - if ((s->server_chr != NULL) && - (strncmp(s->server_chr->filename, "unix:", 5) == 0)) { - /* if we get a UNIX socket as the parameter we will talk - * to the ivshmem server to receive the memory region */ +static void ivshmem_common_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - if (s->shmobj != NULL) { - error_report("WARNING: do not specify both 'chardev' " - "and 'shm' with ivshmem"); - } + k->realize = ivshmem_common_realize; + k->exit = ivshmem_exit; + k->config_write = ivshmem_write_config; + k->vendor_id = PCI_VENDOR_ID_IVSHMEM; + k->device_id = PCI_DEVICE_ID_IVSHMEM; + k->class_id = PCI_CLASS_MEMORY_RAM; + k->revision = 1; + dc->reset = ivshmem_reset; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->desc = "Inter-VM shared memory"; +} - IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n", - s->server_chr->filename); +static const TypeInfo ivshmem_common_info = { + .name = TYPE_IVSHMEM_COMMON, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(IVShmemState), + .abstract = true, + .class_init = ivshmem_common_class_init, +}; - if (ivshmem_has_feature(s, IVSHMEM_MSI)) { - ivshmem_setup_msi(s); - } +static void ivshmem_check_memdev_is_busy(Object *obj, const char *name, + Object *val, Error **errp) +{ + MemoryRegion *mr; - /* we allocate enough space for 16 guests and grow as needed */ - s->nb_peers = 16; - s->vm_id = -1; + mr = host_memory_backend_get_memory(MEMORY_BACKEND(val), &error_abort); + if (memory_region_is_mapped(mr)) { + char *path = object_get_canonical_path_component(val); + error_setg(errp, "can't use already busy memdev: %s", path); + g_free(path); + } else { + qdev_prop_allow_set_link_before_realize(obj, name, val, errp); + } +} + +static const VMStateDescription ivshmem_plain_vmsd = { + .name = TYPE_IVSHMEM_PLAIN, + .version_id = 0, + .minimum_version_id = 0, + .pre_load = ivshmem_pre_load, + .post_load = ivshmem_post_load, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, IVShmemState), + VMSTATE_UINT32(intrstatus, IVShmemState), + VMSTATE_UINT32(intrmask, IVShmemState), + VMSTATE_END_OF_LIST() + }, +}; - /* allocate/initialize space for interrupt handling */ - s->peers = g_malloc0(s->nb_peers * sizeof(Peer)); +static Property ivshmem_plain_properties[] = { + DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF), + DEFINE_PROP_END_OF_LIST(), +}; - pci_register_bar(dev, 2, s->ivshmem_attr, &s->bar); +static void ivshmem_plain_init(Object *obj) +{ + IVShmemState *s = IVSHMEM_PLAIN(obj); - s->eventfd_chr = g_malloc0(s->vectors * sizeof(CharDriverState *)); + object_property_add_link(obj, "memdev", TYPE_MEMORY_BACKEND, + (Object **)&s->hostmem, + ivshmem_check_memdev_is_busy, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + &error_abort); +} - qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read, - ivshmem_event, s); - } else { - /* just map the file immediately, we're not using a server */ - int fd; +static void ivshmem_plain_realize(PCIDevice *dev, Error **errp) +{ + IVShmemState *s = IVSHMEM_COMMON(dev); - if (s->shmobj == NULL) { - error_report("Must specify 'chardev' or 'shm' to ivshmem"); - exit(1); - } + if (!s->hostmem) { + error_setg(errp, "You must specify a 'memdev'"); + return; + } - IVSHMEM_DPRINTF("using shm_open (shm object = %s)\n", s->shmobj); + ivshmem_common_realize(dev, errp); +} - /* try opening with O_EXCL and if it succeeds zero the memory - * by truncating to 0 */ - if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR|O_EXCL, - S_IRWXU|S_IRWXG|S_IRWXO)) > 0) { - /* truncate file to length PCI device's memory */ - if (ftruncate(fd, s->ivshmem_size) != 0) { - error_report("could not truncate shared file"); - } +static void ivshmem_plain_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - } else if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR, - S_IRWXU|S_IRWXG|S_IRWXO)) < 0) { - error_report("could not open shared file"); - exit(1); + k->realize = ivshmem_plain_realize; + dc->props = ivshmem_plain_properties; + dc->vmsd = &ivshmem_plain_vmsd; +} - } +static const TypeInfo ivshmem_plain_info = { + .name = TYPE_IVSHMEM_PLAIN, + .parent = TYPE_IVSHMEM_COMMON, + .instance_size = sizeof(IVShmemState), + .instance_init = ivshmem_plain_init, + .class_init = ivshmem_plain_class_init, +}; - if (check_shm_size(s, fd) == -1) { - exit(1); - } +static const VMStateDescription ivshmem_doorbell_vmsd = { + .name = TYPE_IVSHMEM_DOORBELL, + .version_id = 0, + .minimum_version_id = 0, + .pre_load = ivshmem_pre_load, + .post_load = ivshmem_post_load, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, IVShmemState), + VMSTATE_MSIX(parent_obj, IVShmemState), + VMSTATE_UINT32(intrstatus, IVShmemState), + VMSTATE_UINT32(intrmask, IVShmemState), + VMSTATE_END_OF_LIST() + }, +}; - create_shared_memory_BAR(s, fd); +static Property ivshmem_doorbell_properties[] = { + DEFINE_PROP_CHR("chardev", IVShmemState, server_chr), + DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1), + DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, + true), + DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF), + DEFINE_PROP_END_OF_LIST(), +}; +static void ivshmem_doorbell_init(Object *obj) +{ + IVShmemState *s = IVSHMEM_DOORBELL(obj); + + s->features |= (1 << IVSHMEM_MSI); + s->legacy_size = SIZE_MAX; /* whatever the server sends */ +} + +static void ivshmem_doorbell_realize(PCIDevice *dev, Error **errp) +{ + IVShmemState *s = IVSHMEM_COMMON(dev); + + if (!s->server_chr) { + error_setg(errp, "You must specify a 'chardev'"); + return; } - dev->config_write = ivshmem_write_config; + ivshmem_common_realize(dev, errp); +} - return 0; +static void ivshmem_doorbell_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->realize = ivshmem_doorbell_realize; + dc->props = ivshmem_doorbell_properties; + dc->vmsd = &ivshmem_doorbell_vmsd; } -static void pci_ivshmem_uninit(PCIDevice *dev) +static const TypeInfo ivshmem_doorbell_info = { + .name = TYPE_IVSHMEM_DOORBELL, + .parent = TYPE_IVSHMEM_COMMON, + .instance_size = sizeof(IVShmemState), + .instance_init = ivshmem_doorbell_init, + .class_init = ivshmem_doorbell_class_init, +}; + +static int ivshmem_load_old(QEMUFile *f, void *opaque, int version_id) { - IVShmemState *s = IVSHMEM(dev); + IVShmemState *s = opaque; + PCIDevice *pdev = PCI_DEVICE(s); + int ret; - if (s->migration_blocker) { - migrate_del_blocker(s->migration_blocker); - error_free(s->migration_blocker); + IVSHMEM_DPRINTF("ivshmem_load_old\n"); + + if (version_id != 0) { + return -EINVAL; + } + + ret = ivshmem_pre_load(s); + if (ret) { + return ret; + } + + ret = pci_device_load(pdev, f); + if (ret) { + return ret; + } + + if (ivshmem_has_feature(s, IVSHMEM_MSI)) { + msix_load(pdev, f); + ivshmem_msix_vector_use(s); + } else { + s->intrstatus = qemu_get_be32(f); + s->intrmask = qemu_get_be32(f); } - memory_region_del_subregion(&s->bar, &s->ivshmem); - vmstate_unregister_ram(&s->ivshmem, DEVICE(dev)); - unregister_savevm(DEVICE(dev), "ivshmem", s); - fifo8_destroy(&s->incoming_fifo); + return 0; } +static bool test_msix(void *opaque, int version_id) +{ + IVShmemState *s = opaque; + + return ivshmem_has_feature(s, IVSHMEM_MSI); +} + +static bool test_no_msix(void *opaque, int version_id) +{ + return !test_msix(opaque, version_id); +} + +static const VMStateDescription ivshmem_vmsd = { + .name = "ivshmem", + .version_id = 1, + .minimum_version_id = 1, + .pre_load = ivshmem_pre_load, + .post_load = ivshmem_post_load, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, IVShmemState), + + VMSTATE_MSIX_TEST(parent_obj, IVShmemState, test_msix), + VMSTATE_UINT32_TEST(intrstatus, IVShmemState, test_no_msix), + VMSTATE_UINT32_TEST(intrmask, IVShmemState, test_no_msix), + + VMSTATE_END_OF_LIST() + }, + .load_state_old = ivshmem_load_old, + .minimum_version_id_old = 0 +}; + static Property ivshmem_properties[] = { DEFINE_PROP_CHR("chardev", IVShmemState, server_chr), DEFINE_PROP_STRING("size", IVShmemState, sizearg), DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1), - DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, false), + DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, + false), DEFINE_PROP_BIT("msi", IVShmemState, features, IVSHMEM_MSI, true), DEFINE_PROP_STRING("shm", IVShmemState, shmobj), DEFINE_PROP_STRING("role", IVShmemState, role), - DEFINE_PROP_UINT32("use64", IVShmemState, ivshmem_64bit, 1), + DEFINE_PROP_UINT32("use64", IVShmemState, not_legacy_32bit, 1), DEFINE_PROP_END_OF_LIST(), }; +static void desugar_shm(IVShmemState *s) +{ + Object *obj; + char *path; + + obj = object_new("memory-backend-file"); + path = g_strdup_printf("/dev/shm/%s", s->shmobj); + object_property_set_str(obj, path, "mem-path", &error_abort); + g_free(path); + object_property_set_int(obj, s->legacy_size, "size", &error_abort); + object_property_set_bool(obj, true, "share", &error_abort); + object_property_add_child(OBJECT(s), "internal-shm-backend", obj, + &error_abort); + user_creatable_complete(obj, &error_abort); + s->hostmem = MEMORY_BACKEND(obj); +} + +static void ivshmem_realize(PCIDevice *dev, Error **errp) +{ + IVShmemState *s = IVSHMEM_COMMON(dev); + + if (!qtest_enabled()) { + error_report("ivshmem is deprecated, please use ivshmem-plain" + " or ivshmem-doorbell instead"); + } + + if (!!s->server_chr + !!s->shmobj != 1) { + error_setg(errp, "You must specify either 'shm' or 'chardev'"); + return; + } + + if (s->sizearg == NULL) { + s->legacy_size = 4 << 20; /* 4 MB default */ + } else { + char *end; + int64_t size = qemu_strtosz(s->sizearg, &end); + if (size < 0 || (size_t)size != size || *end != '\0' + || !is_power_of_2(size)) { + error_setg(errp, "Invalid size %s", s->sizearg); + return; + } + s->legacy_size = size; + } + + /* check that role is reasonable */ + if (s->role) { + if (strncmp(s->role, "peer", 5) == 0) { + s->master = ON_OFF_AUTO_OFF; + } else if (strncmp(s->role, "master", 7) == 0) { + s->master = ON_OFF_AUTO_ON; + } else { + error_setg(errp, "'role' must be 'peer' or 'master'"); + return; + } + } else { + s->master = ON_OFF_AUTO_AUTO; + } + + if (s->shmobj) { + desugar_shm(s); + } + + /* + * Note: we don't use INTx with IVSHMEM_MSI at all, so this is a + * bald-faced lie then. But it's a backwards compatible lie. + */ + pci_config_set_interrupt_pin(dev->config, 1); + + ivshmem_common_realize(dev, errp); +} + static void ivshmem_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = pci_ivshmem_init; - k->exit = pci_ivshmem_uninit; - k->vendor_id = PCI_VENDOR_ID_IVSHMEM; - k->device_id = PCI_DEVICE_ID_IVSHMEM; - k->class_id = PCI_CLASS_MEMORY_RAM; - dc->reset = ivshmem_reset; + k->realize = ivshmem_realize; + k->revision = 0; + dc->desc = "Inter-VM shared memory (legacy)"; dc->props = ivshmem_properties; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->vmsd = &ivshmem_vmsd; } static const TypeInfo ivshmem_info = { .name = TYPE_IVSHMEM, - .parent = TYPE_PCI_DEVICE, + .parent = TYPE_IVSHMEM_COMMON, .instance_size = sizeof(IVShmemState), .class_init = ivshmem_class_init, }; static void ivshmem_register_types(void) { + type_register_static(&ivshmem_common_info); + type_register_static(&ivshmem_plain_info); + type_register_static(&ivshmem_doorbell_info); type_register_static(&ivshmem_info); } diff --git a/qemu/hw/misc/macio/cuda.c b/qemu/hw/misc/macio/cuda.c index f3984e3a2..f15f30110 100644 --- a/qemu/hw/misc/macio/cuda.c +++ b/qemu/hw/misc/macio/cuda.c @@ -22,11 +22,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/ppc/mac.h" #include "hw/input/adb.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" +#include "qemu/cutils.h" /* XXX: implement all timer modes */ @@ -57,6 +59,8 @@ #define IER_SET 0x80 /* set bits in IER */ #define IER_CLR 0 /* clear bits in IER */ #define SR_INT 0x04 /* Shift register full/empty */ +#define SR_DATA_INT 0x08 +#define SR_CLOCK_INT 0x10 #define T1_INT 0x40 /* Timer 1 interrupt */ #define T2_INT 0x20 /* Timer 2 interrupt */ @@ -103,11 +107,28 @@ #define CUDA_COMBINED_FORMAT_IIC 0x25 #define CUDA_TIMER_FREQ (4700000 / 6) -#define CUDA_ADB_POLL_FREQ 50 /* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */ #define RTC_OFFSET 2082844800 +/* CUDA registers */ +#define CUDA_REG_B 0x00 +#define CUDA_REG_A 0x01 +#define CUDA_REG_DIRB 0x02 +#define CUDA_REG_DIRA 0x03 +#define CUDA_REG_T1CL 0x04 +#define CUDA_REG_T1CH 0x05 +#define CUDA_REG_T1LL 0x06 +#define CUDA_REG_T1LH 0x07 +#define CUDA_REG_T2CL 0x08 +#define CUDA_REG_T2CH 0x09 +#define CUDA_REG_SR 0x0a +#define CUDA_REG_ACR 0x0b +#define CUDA_REG_PCR 0x0c +#define CUDA_REG_IFR 0x0d +#define CUDA_REG_IER 0x0e +#define CUDA_REG_ANH 0x0f + static void cuda_update(CUDAState *s); static void cuda_receive_packet_from_host(CUDAState *s, const uint8_t *data, int len); @@ -116,47 +137,48 @@ static void cuda_timer_update(CUDAState *s, CUDATimer *ti, static void cuda_update_irq(CUDAState *s) { - if (s->ifr & s->ier & (SR_INT | T1_INT)) { + if (s->ifr & s->ier & (SR_INT | T1_INT | T2_INT)) { qemu_irq_raise(s->irq); } else { qemu_irq_lower(s->irq); } } -static uint64_t get_tb(uint64_t freq) +static uint64_t get_tb(uint64_t time, uint64_t freq) { - return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - freq, get_ticks_per_sec()); + return muldiv64(time, freq, NANOSECONDS_PER_SECOND); } -static unsigned int get_counter(CUDATimer *s) +static unsigned int get_counter(CUDATimer *ti) { int64_t d; unsigned int counter; uint64_t tb_diff; + uint64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); /* Reverse of the tb calculation algorithm that Mac OS X uses on bootup. */ - tb_diff = get_tb(s->frequency) - s->load_time; - d = (tb_diff * 0xBF401675E5DULL) / (s->frequency << 24); + tb_diff = get_tb(current_time, ti->frequency) - ti->load_time; + d = (tb_diff * 0xBF401675E5DULL) / (ti->frequency << 24); - if (s->index == 0) { + if (ti->index == 0) { /* the timer goes down from latch to -1 (period of latch + 2) */ - if (d <= (s->counter_value + 1)) { - counter = (s->counter_value - d) & 0xffff; + if (d <= (ti->counter_value + 1)) { + counter = (ti->counter_value - d) & 0xffff; } else { - counter = (d - (s->counter_value + 1)) % (s->latch + 2); - counter = (s->latch - counter) & 0xffff; + counter = (d - (ti->counter_value + 1)) % (ti->latch + 2); + counter = (ti->latch - counter) & 0xffff; } } else { - counter = (s->counter_value - d) & 0xffff; + counter = (ti->counter_value - d) & 0xffff; } return counter; } static void set_counter(CUDAState *s, CUDATimer *ti, unsigned int val) { - CUDA_DPRINTF("T%d.counter=%d\n", 1 + (ti->timer == NULL), val); - ti->load_time = get_tb(s->frequency); + CUDA_DPRINTF("T%d.counter=%d\n", 1 + ti->index, val); + ti->load_time = get_tb(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + s->frequency); ti->counter_value = val; cuda_timer_update(s, ti, ti->load_time); } @@ -168,7 +190,7 @@ static int64_t get_next_irq_time(CUDATimer *s, int64_t current_time) /* current counter value */ d = muldiv64(current_time - s->load_time, - CUDA_TIMER_FREQ, get_ticks_per_sec()); + CUDA_TIMER_FREQ, NANOSECONDS_PER_SECOND); /* the timer goes down from latch to -1 (period of latch + 2) */ if (d <= (s->counter_value + 1)) { counter = (s->counter_value - d) & 0xffff; @@ -187,7 +209,7 @@ static int64_t get_next_irq_time(CUDATimer *s, int64_t current_time) } CUDA_DPRINTF("latch=%d counter=%" PRId64 " delta_next=%" PRId64 "\n", s->latch, d, next_time - d); - next_time = muldiv64(next_time, get_ticks_per_sec(), CUDA_TIMER_FREQ) + + next_time = muldiv64(next_time, NANOSECONDS_PER_SECOND, CUDA_TIMER_FREQ) + s->load_time; if (next_time <= current_time) next_time = current_time + 1; @@ -199,7 +221,7 @@ static void cuda_timer_update(CUDAState *s, CUDATimer *ti, { if (!ti->timer) return; - if ((s->acr & T1MODE) != T1MODE_CONT) { + if (ti->index == 0 && (s->acr & T1MODE) != T1MODE_CONT) { timer_del(ti->timer); } else { ti->next_irq_time = get_next_irq_time(ti, current_time); @@ -217,6 +239,41 @@ static void cuda_timer1(void *opaque) cuda_update_irq(s); } +static void cuda_timer2(void *opaque) +{ + CUDAState *s = opaque; + CUDATimer *ti = &s->timers[1]; + + cuda_timer_update(s, ti, ti->next_irq_time); + s->ifr |= T2_INT; + cuda_update_irq(s); +} + +static void cuda_set_sr_int(void *opaque) +{ + CUDAState *s = opaque; + + CUDA_DPRINTF("CUDA: %s:%d\n", __func__, __LINE__); + s->ifr |= SR_INT; + cuda_update_irq(s); +} + +static void cuda_delay_set_sr_int(CUDAState *s) +{ + int64_t expire; + + if (s->dirb == 0xff) { + /* Not in Mac OS, fire the IRQ directly */ + cuda_set_sr_int(s); + return; + } + + CUDA_DPRINTF("CUDA: %s:%d\n", __func__, __LINE__); + + expire = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 300 * SCALE_US; + timer_mod(s->sr_delay_timer, expire); +} + static uint32_t cuda_readb(void *opaque, hwaddr addr) { CUDAState *s = opaque; @@ -224,66 +281,68 @@ static uint32_t cuda_readb(void *opaque, hwaddr addr) addr = (addr >> 9) & 0xf; switch(addr) { - case 0: + case CUDA_REG_B: val = s->b; break; - case 1: + case CUDA_REG_A: val = s->a; break; - case 2: + case CUDA_REG_DIRB: val = s->dirb; break; - case 3: + case CUDA_REG_DIRA: val = s->dira; break; - case 4: + case CUDA_REG_T1CL: val = get_counter(&s->timers[0]) & 0xff; s->ifr &= ~T1_INT; cuda_update_irq(s); break; - case 5: + case CUDA_REG_T1CH: val = get_counter(&s->timers[0]) >> 8; cuda_update_irq(s); break; - case 6: + case CUDA_REG_T1LL: val = s->timers[0].latch & 0xff; break; - case 7: + case CUDA_REG_T1LH: /* XXX: check this */ val = (s->timers[0].latch >> 8) & 0xff; break; - case 8: + case CUDA_REG_T2CL: val = get_counter(&s->timers[1]) & 0xff; s->ifr &= ~T2_INT; + cuda_update_irq(s); break; - case 9: + case CUDA_REG_T2CH: val = get_counter(&s->timers[1]) >> 8; break; - case 10: + case CUDA_REG_SR: val = s->sr; - s->ifr &= ~SR_INT; + s->ifr &= ~(SR_INT | SR_CLOCK_INT | SR_DATA_INT); cuda_update_irq(s); break; - case 11: + case CUDA_REG_ACR: val = s->acr; break; - case 12: + case CUDA_REG_PCR: val = s->pcr; break; - case 13: + case CUDA_REG_IFR: val = s->ifr; - if (s->ifr & s->ier) + if (s->ifr & s->ier) { val |= 0x80; + } break; - case 14: + case CUDA_REG_IER: val = s->ier | 0x80; break; default: - case 15: + case CUDA_REG_ANH: val = s->anh; break; } - if (addr != 13 || val != 0) { + if (addr != CUDA_REG_IFR || val != 0) { CUDA_DPRINTF("read: reg=0x%x val=%02x\n", (int)addr, val); } @@ -298,61 +357,65 @@ static void cuda_writeb(void *opaque, hwaddr addr, uint32_t val) CUDA_DPRINTF("write: reg=0x%x val=%02x\n", (int)addr, val); switch(addr) { - case 0: + case CUDA_REG_B: s->b = val; cuda_update(s); break; - case 1: + case CUDA_REG_A: s->a = val; break; - case 2: + case CUDA_REG_DIRB: s->dirb = val; break; - case 3: + case CUDA_REG_DIRA: s->dira = val; break; - case 4: + case CUDA_REG_T1CL: s->timers[0].latch = (s->timers[0].latch & 0xff00) | val; cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); break; - case 5: + case CUDA_REG_T1CH: s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8); s->ifr &= ~T1_INT; set_counter(s, &s->timers[0], s->timers[0].latch); break; - case 6: + case CUDA_REG_T1LL: s->timers[0].latch = (s->timers[0].latch & 0xff00) | val; cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); break; - case 7: + case CUDA_REG_T1LH: s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8); s->ifr &= ~T1_INT; cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); break; - case 8: - s->timers[1].latch = val; - set_counter(s, &s->timers[1], val); + case CUDA_REG_T2CL: + s->timers[1].latch = (s->timers[1].latch & 0xff00) | val; break; - case 9: - set_counter(s, &s->timers[1], (val << 8) | s->timers[1].latch); + case CUDA_REG_T2CH: + /* To ensure T2 generates an interrupt on zero crossing with the + common timer code, write the value directly from the latch to + the counter */ + s->timers[1].latch = (s->timers[1].latch & 0xff) | (val << 8); + s->ifr &= ~T2_INT; + set_counter(s, &s->timers[1], s->timers[1].latch); break; - case 10: + case CUDA_REG_SR: s->sr = val; break; - case 11: + case CUDA_REG_ACR: s->acr = val; cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); cuda_update(s); break; - case 12: + case CUDA_REG_PCR: s->pcr = val; break; - case 13: + case CUDA_REG_IFR: /* reset bits */ s->ifr &= ~val; cuda_update_irq(s); break; - case 14: + case CUDA_REG_IER: if (val & IER_SET) { /* set bits */ s->ier |= val & 0x7f; @@ -363,7 +426,7 @@ static void cuda_writeb(void *opaque, hwaddr addr, uint32_t val) cuda_update_irq(s); break; default: - case 15: + case CUDA_REG_ANH: s->anh = val; break; } @@ -384,8 +447,7 @@ static void cuda_update(CUDAState *s) if (s->data_out_index < sizeof(s->data_out)) { CUDA_DPRINTF("send: %02x\n", s->sr); s->data_out[s->data_out_index++] = s->sr; - s->ifr |= SR_INT; - cuda_update_irq(s); + cuda_delay_set_sr_int(s); } } } else { @@ -398,8 +460,7 @@ static void cuda_update(CUDAState *s) if (s->data_in_index >= s->data_in_size) { s->b = (s->b | TREQ); } - s->ifr |= SR_INT; - cuda_update_irq(s); + cuda_delay_set_sr_int(s); } } } @@ -411,15 +472,13 @@ static void cuda_update(CUDAState *s) s->b = (s->b | TREQ); else s->b = (s->b & ~TREQ); - s->ifr |= SR_INT; - cuda_update_irq(s); + cuda_delay_set_sr_int(s); } else { if (!(s->last_b & TIP)) { /* handle end of host to cuda transfer */ packet_received = (s->data_out_index > 0); /* always an IRQ at the end of transfer */ - s->ifr |= SR_INT; - cuda_update_irq(s); + cuda_delay_set_sr_int(s); } /* signal if there is data to read */ if (s->data_in_index < s->data_in_size) { @@ -456,8 +515,7 @@ static void cuda_send_packet_to_host(CUDAState *s, s->data_in_size = len; s->data_in_index = 0; cuda_update(s); - s->ifr |= SR_INT; - cuda_update_irq(s); + cuda_delay_set_sr_int(s); } static void cuda_adb_poll(void *opaque) @@ -466,7 +524,7 @@ static void cuda_adb_poll(void *opaque) uint8_t obuf[ADB_MAX_OUT_LEN + 2]; int olen; - olen = adb_poll(&s->adb_bus, obuf + 2); + olen = adb_poll(&s->adb_bus, obuf + 2, s->adb_poll_mask); if (olen > 0) { obuf[0] = ADB_PACKET; obuf[1] = 0x40; /* polled data */ @@ -474,75 +532,213 @@ static void cuda_adb_poll(void *opaque) } timer_mod(s->adb_poll_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (get_ticks_per_sec() / CUDA_ADB_POLL_FREQ)); + (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms))); } -static void cuda_receive_packet(CUDAState *s, - const uint8_t *data, int len) +/* description of commands */ +typedef struct CudaCommand { + uint8_t command; + const char *name; + bool (*handler)(CUDAState *s, + const uint8_t *in_args, int in_len, + uint8_t *out_args, int *out_len); +} CudaCommand; + +static bool cuda_cmd_autopoll(CUDAState *s, + const uint8_t *in_data, int in_len, + uint8_t *out_data, int *out_len) { - uint8_t obuf[16]; int autopoll; + + if (in_len != 1) { + return false; + } + + autopoll = (in_data[0] != 0); + if (autopoll != s->autopoll) { + s->autopoll = autopoll; + if (autopoll) { + timer_mod(s->adb_poll_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms))); + } else { + timer_del(s->adb_poll_timer); + } + } + return true; +} + +static bool cuda_cmd_set_autorate(CUDAState *s, + const uint8_t *in_data, int in_len, + uint8_t *out_data, int *out_len) +{ + if (in_len != 1) { + return false; + } + + /* we don't want a period of 0 ms */ + /* FIXME: check what real hardware does */ + if (in_data[0] == 0) { + return false; + } + + s->autopoll_rate_ms = in_data[0]; + if (s->autopoll) { + timer_mod(s->adb_poll_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms))); + } + return true; +} + +static bool cuda_cmd_set_device_list(CUDAState *s, + const uint8_t *in_data, int in_len, + uint8_t *out_data, int *out_len) +{ + if (in_len != 2) { + return false; + } + + s->adb_poll_mask = (((uint16_t)in_data[0]) << 8) | in_data[1]; + return true; +} + +static bool cuda_cmd_powerdown(CUDAState *s, + const uint8_t *in_data, int in_len, + uint8_t *out_data, int *out_len) +{ + if (in_len != 0) { + return false; + } + + qemu_system_shutdown_request(); + return true; +} + +static bool cuda_cmd_reset_system(CUDAState *s, + const uint8_t *in_data, int in_len, + uint8_t *out_data, int *out_len) +{ + if (in_len != 0) { + return false; + } + + qemu_system_reset_request(); + return true; +} + +static bool cuda_cmd_set_file_server_flag(CUDAState *s, + const uint8_t *in_data, int in_len, + uint8_t *out_data, int *out_len) +{ + if (in_len != 1) { + return false; + } + + qemu_log_mask(LOG_UNIMP, + "CUDA: unimplemented command FILE_SERVER_FLAG %d\n", + in_data[0]); + return true; +} + +static bool cuda_cmd_set_power_message(CUDAState *s, + const uint8_t *in_data, int in_len, + uint8_t *out_data, int *out_len) +{ + if (in_len != 1) { + return false; + } + + qemu_log_mask(LOG_UNIMP, + "CUDA: unimplemented command SET_POWER_MESSAGE %d\n", + in_data[0]); + return true; +} + +static bool cuda_cmd_get_time(CUDAState *s, + const uint8_t *in_data, int in_len, + uint8_t *out_data, int *out_len) +{ uint32_t ti; - switch(data[0]) { - case CUDA_AUTOPOLL: - autopoll = (data[1] != 0); - if (autopoll != s->autopoll) { - s->autopoll = autopoll; - if (autopoll) { - timer_mod(s->adb_poll_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (get_ticks_per_sec() / CUDA_ADB_POLL_FREQ)); + if (in_len != 0) { + return false; + } + + ti = s->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + / NANOSECONDS_PER_SECOND); + out_data[0] = ti >> 24; + out_data[1] = ti >> 16; + out_data[2] = ti >> 8; + out_data[3] = ti; + *out_len = 4; + return true; +} + +static bool cuda_cmd_set_time(CUDAState *s, + const uint8_t *in_data, int in_len, + uint8_t *out_data, int *out_len) +{ + uint32_t ti; + + if (in_len != 4) { + return false; + } + + ti = (((uint32_t)in_data[0]) << 24) + (((uint32_t)in_data[1]) << 16) + + (((uint32_t)in_data[2]) << 8) + in_data[3]; + s->tick_offset = ti - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + / NANOSECONDS_PER_SECOND); + return true; +} + +static const CudaCommand handlers[] = { + { CUDA_AUTOPOLL, "AUTOPOLL", cuda_cmd_autopoll }, + { CUDA_SET_AUTO_RATE, "SET_AUTO_RATE", cuda_cmd_set_autorate }, + { CUDA_SET_DEVICE_LIST, "SET_DEVICE_LIST", cuda_cmd_set_device_list }, + { CUDA_POWERDOWN, "POWERDOWN", cuda_cmd_powerdown }, + { CUDA_RESET_SYSTEM, "RESET_SYSTEM", cuda_cmd_reset_system }, + { CUDA_FILE_SERVER_FLAG, "FILE_SERVER_FLAG", + cuda_cmd_set_file_server_flag }, + { CUDA_SET_POWER_MESSAGES, "SET_POWER_MESSAGES", + cuda_cmd_set_power_message }, + { CUDA_GET_TIME, "GET_TIME", cuda_cmd_get_time }, + { CUDA_SET_TIME, "SET_TIME", cuda_cmd_set_time }, +}; + +static void cuda_receive_packet(CUDAState *s, + const uint8_t *data, int len) +{ + uint8_t obuf[16] = { CUDA_PACKET, 0, data[0] }; + int i, out_len = 0; + + for (i = 0; i < ARRAY_SIZE(handlers); i++) { + const CudaCommand *desc = &handlers[i]; + if (desc->command == data[0]) { + CUDA_DPRINTF("handling command %s\n", desc->name); + out_len = 0; + if (desc->handler(s, data + 1, len - 1, obuf + 3, &out_len)) { + cuda_send_packet_to_host(s, obuf, 3 + out_len); } else { - timer_del(s->adb_poll_timer); + qemu_log_mask(LOG_GUEST_ERROR, + "CUDA: %s: wrong parameters %d\n", + desc->name, len); + obuf[0] = ERROR_PACKET; + obuf[1] = 0x5; /* bad parameters */ + obuf[2] = CUDA_PACKET; + obuf[3] = data[0]; + cuda_send_packet_to_host(s, obuf, 4); } + return; } - obuf[0] = CUDA_PACKET; - obuf[1] = data[1]; - cuda_send_packet_to_host(s, obuf, 2); - break; - case CUDA_SET_TIME: - ti = (((uint32_t)data[1]) << 24) + (((uint32_t)data[2]) << 16) + (((uint32_t)data[3]) << 8) + data[4]; - s->tick_offset = ti - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / get_ticks_per_sec()); - obuf[0] = CUDA_PACKET; - obuf[1] = 0; - obuf[2] = 0; - cuda_send_packet_to_host(s, obuf, 3); - break; - case CUDA_GET_TIME: - ti = s->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / get_ticks_per_sec()); - obuf[0] = CUDA_PACKET; - obuf[1] = 0; - obuf[2] = 0; - obuf[3] = ti >> 24; - obuf[4] = ti >> 16; - obuf[5] = ti >> 8; - obuf[6] = ti; - cuda_send_packet_to_host(s, obuf, 7); - break; - case CUDA_FILE_SERVER_FLAG: - case CUDA_SET_DEVICE_LIST: - case CUDA_SET_AUTO_RATE: - case CUDA_SET_POWER_MESSAGES: - obuf[0] = CUDA_PACKET; - obuf[1] = 0; - cuda_send_packet_to_host(s, obuf, 2); - break; - case CUDA_POWERDOWN: - obuf[0] = CUDA_PACKET; - obuf[1] = 0; - cuda_send_packet_to_host(s, obuf, 2); - qemu_system_shutdown_request(); - break; - case CUDA_RESET_SYSTEM: - obuf[0] = CUDA_PACKET; - obuf[1] = 0; - cuda_send_packet_to_host(s, obuf, 2); - qemu_system_reset_request(); - break; - default: - break; } + + qemu_log_mask(LOG_GUEST_ERROR, "CUDA: unknown command 0x%02x\n", data[0]); + obuf[0] = ERROR_PACKET; + obuf[1] = 0x2; /* unknown command */ + obuf[2] = CUDA_PACKET; + obuf[3] = data[0]; + cuda_send_packet_to_host(s, obuf, 4); } static void cuda_receive_packet_from_host(CUDAState *s, @@ -560,19 +756,21 @@ static void cuda_receive_packet_from_host(CUDAState *s, switch(data[0]) { case ADB_PACKET: { - uint8_t obuf[ADB_MAX_OUT_LEN + 2]; + uint8_t obuf[ADB_MAX_OUT_LEN + 3]; int olen; olen = adb_request(&s->adb_bus, obuf + 2, data + 1, len - 1); if (olen > 0) { obuf[0] = ADB_PACKET; obuf[1] = 0x00; + cuda_send_packet_to_host(s, obuf, olen + 2); } else { /* error */ obuf[0] = ADB_PACKET; obuf[1] = -olen; + obuf[2] = data[1]; olen = 0; + cuda_send_packet_to_host(s, obuf, olen + 3); } - cuda_send_packet_to_host(s, obuf, olen + 2); } break; case CUDA_PACKET: @@ -638,15 +836,17 @@ static const VMStateDescription vmstate_cuda_timer = { static const VMStateDescription vmstate_cuda = { .name = "cuda", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 4, + .minimum_version_id = 4, .fields = (VMStateField[]) { VMSTATE_UINT8(a, CUDAState), VMSTATE_UINT8(b, CUDAState), + VMSTATE_UINT8(last_b, CUDAState), VMSTATE_UINT8(dira, CUDAState), VMSTATE_UINT8(dirb, CUDAState), VMSTATE_UINT8(sr, CUDAState), VMSTATE_UINT8(acr, CUDAState), + VMSTATE_UINT8(last_acr, CUDAState), VMSTATE_UINT8(pcr, CUDAState), VMSTATE_UINT8(ifr, CUDAState), VMSTATE_UINT8(ier, CUDAState), @@ -655,12 +855,15 @@ static const VMStateDescription vmstate_cuda = { VMSTATE_INT32(data_in_index, CUDAState), VMSTATE_INT32(data_out_index, CUDAState), VMSTATE_UINT8(autopoll, CUDAState), + VMSTATE_UINT8(autopoll_rate_ms, CUDAState), + VMSTATE_UINT16(adb_poll_mask, CUDAState), VMSTATE_BUFFER(data_in, CUDAState), VMSTATE_BUFFER(data_out, CUDAState), VMSTATE_UINT32(tick_offset, CUDAState), VMSTATE_STRUCT_ARRAY(timers, CUDAState, 2, 1, vmstate_cuda_timer, CUDATimer), VMSTATE_TIMER_PTR(adb_poll_timer, CUDAState), + VMSTATE_TIMER_PTR(sr_delay_timer, CUDAState), VMSTATE_END_OF_LIST() } }; @@ -671,7 +874,7 @@ static void cuda_reset(DeviceState *dev) s->b = 0; s->a = 0; - s->dirb = 0; + s->dirb = 0xff; s->dira = 0; s->sr = 0; s->acr = 0; @@ -688,8 +891,9 @@ static void cuda_reset(DeviceState *dev) s->timers[0].latch = 0xffff; set_counter(s, &s->timers[0], 0xffff); - s->timers[1].latch = 0; - set_counter(s, &s->timers[1], 0xffff); + s->timers[1].latch = 0xffff; + + s->sr_delay_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_set_sr_int, s); } static void cuda_realizefn(DeviceState *dev, Error **errp) @@ -699,12 +903,15 @@ static void cuda_realizefn(DeviceState *dev, Error **errp) s->timers[0].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_timer1, s); s->timers[0].frequency = s->frequency; - s->timers[1].frequency = s->frequency; + s->timers[1].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_timer2, s); + s->timers[1].frequency = (SCALE_US * 6000) / 4700; qemu_get_timedate(&tm, 0); s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; s->adb_poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_adb_poll, s); + s->autopoll_rate_ms = 20; + s->adb_poll_mask = 0xffff; } static void cuda_initfn(Object *obj) @@ -713,7 +920,7 @@ static void cuda_initfn(Object *obj) CUDAState *s = CUDA(obj); int i; - memory_region_init_io(&s->mem, NULL, &cuda_ops, s, "cuda", 0x2000); + memory_region_init_io(&s->mem, obj, &cuda_ops, s, "cuda", 0x2000); sysbus_init_mmio(d, &s->mem); sysbus_init_irq(d, &s->irq); @@ -738,6 +945,7 @@ static void cuda_class_init(ObjectClass *oc, void *data) dc->reset = cuda_reset; dc->vmsd = &vmstate_cuda; dc->props = cuda_properties; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo cuda_type_info = { diff --git a/qemu/hw/misc/macio/mac_dbdma.c b/qemu/hw/misc/macio/mac_dbdma.c index b25e8511b..6051f17db 100644 --- a/qemu/hw/misc/macio/mac_dbdma.c +++ b/qemu/hw/misc/macio/mac_dbdma.c @@ -36,6 +36,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/isa/isa.h" #include "hw/ppc/mac_dbdma.h" @@ -556,12 +557,13 @@ void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq, DBDMA_DPRINTF("DBDMA_register_channel 0x%x\n", nchan); + assert(rw); + assert(flush); + ch->irq = irq; - ch->channel = nchan; ch->rw = rw; ch->flush = flush; ch->io.opaque = opaque; - ch->io.channel = ch; } static void @@ -590,10 +592,11 @@ dbdma_control_write(DBDMA_channel *ch) if ((ch->regs[DBDMA_STATUS] & RUN) && !(status & RUN)) { /* RUN is cleared */ status &= ~(ACTIVE|DEAD); - if ((status & FLUSH) && ch->flush) { - ch->flush(&ch->io); - status &= ~FLUSH; - } + } + + if ((status & FLUSH) && ch->flush) { + ch->flush(&ch->io); + status &= ~FLUSH; } DBDMA_DPRINTF(" status 0x%08x\n", status); @@ -603,9 +606,6 @@ dbdma_control_write(DBDMA_channel *ch) if (status & ACTIVE) { DBDMA_kick(dbdma_from_ch(ch)); } - if ((status & FLUSH) && ch->flush) { - ch->flush(&ch->io); - } } static void dbdma_write(void *opaque, hwaddr addr, @@ -715,20 +715,52 @@ static const MemoryRegionOps dbdma_ops = { }, }; -static const VMStateDescription vmstate_dbdma_channel = { - .name = "dbdma_channel", +static const VMStateDescription vmstate_dbdma_io = { + .name = "dbdma_io", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT64(addr, struct DBDMA_io), + VMSTATE_INT32(len, struct DBDMA_io), + VMSTATE_INT32(is_last, struct DBDMA_io), + VMSTATE_INT32(is_dma_out, struct DBDMA_io), + VMSTATE_BOOL(processing, struct DBDMA_io), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_dbdma_cmd = { + .name = "dbdma_cmd", .version_id = 0, .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT16(req_count, dbdma_cmd), + VMSTATE_UINT16(command, dbdma_cmd), + VMSTATE_UINT32(phy_addr, dbdma_cmd), + VMSTATE_UINT32(cmd_dep, dbdma_cmd), + VMSTATE_UINT16(res_count, dbdma_cmd), + VMSTATE_UINT16(xfer_status, dbdma_cmd), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_dbdma_channel = { + .name = "dbdma_channel", + .version_id = 1, + .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, struct DBDMA_channel, DBDMA_REGS), + VMSTATE_STRUCT(io, struct DBDMA_channel, 0, vmstate_dbdma_io, DBDMA_io), + VMSTATE_STRUCT(current, struct DBDMA_channel, 0, vmstate_dbdma_cmd, + dbdma_cmd), VMSTATE_END_OF_LIST() } }; static const VMStateDescription vmstate_dbdma = { .name = "dbdma", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .fields = (VMStateField[]) { VMSTATE_STRUCT_ARRAY(channels, DBDMAState, DBDMA_CHANNELS, 1, vmstate_dbdma_channel, DBDMA_channel), @@ -745,6 +777,20 @@ static void dbdma_reset(void *opaque) memset(s->channels[i].regs, 0, DBDMA_SIZE); } +static void dbdma_unassigned_rw(DBDMA_io *io) +{ + DBDMA_channel *ch = io->channel; + qemu_log_mask(LOG_GUEST_ERROR, "%s: use of unassigned channel %d\n", + __func__, ch->channel); +} + +static void dbdma_unassigned_flush(DBDMA_io *io) +{ + DBDMA_channel *ch = io->channel; + qemu_log_mask(LOG_GUEST_ERROR, "%s: use of unassigned channel %d\n", + __func__, ch->channel); +} + void* DBDMA_init (MemoryRegion **dbdma_mem) { DBDMAState *s; @@ -754,7 +800,13 @@ void* DBDMA_init (MemoryRegion **dbdma_mem) for (i = 0; i < DBDMA_CHANNELS; i++) { DBDMA_io *io = &s->channels[i].io; + DBDMA_channel *ch = &s->channels[i]; qemu_iovec_init(&io->iov, 1); + + ch->rw = dbdma_unassigned_rw; + ch->flush = dbdma_unassigned_flush; + ch->channel = i; + ch->io.channel = ch; } memory_region_init_io(&s->mem, NULL, &dbdma_ops, s, "dbdma", 0x1000); diff --git a/qemu/hw/misc/macio/macio.c b/qemu/hw/misc/macio/macio.c index e3c0242d4..be03926b9 100644 --- a/qemu/hw/misc/macio/macio.c +++ b/qemu/hw/misc/macio/macio.c @@ -22,6 +22,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "hw/ppc/mac.h" #include "hw/pci/pci.h" @@ -105,10 +107,10 @@ static void macio_escc_legacy_setup(MacIOState *macio_state) 0xF0, 0xE0, }; - memory_region_init(escc_legacy, NULL, "escc-legacy", 256); + memory_region_init(escc_legacy, OBJECT(macio_state), "escc-legacy", 256); for (i = 0; i < ARRAY_SIZE(maps); i += 2) { MemoryRegion *port = g_new(MemoryRegion, 1); - memory_region_init_alias(port, NULL, "escc-legacy-port", + memory_region_init_alias(port, OBJECT(macio_state), "escc-legacy-port", macio_state->escc_mem, maps[i+1], 0x2); memory_region_add_subregion(escc_legacy, maps[i], port); } @@ -131,6 +133,10 @@ static void macio_common_realize(PCIDevice *d, Error **errp) MacIOState *s = MACIO(d); SysBusDevice *sysbus_dev; Error *err = NULL; + MemoryRegion *dbdma_mem; + + s->dbdma = DBDMA_init(&dbdma_mem); + memory_region_add_subregion(&s->bar, 0x08000, dbdma_mem); object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err); if (err) { @@ -248,7 +254,7 @@ static uint64_t timer_read(void *opaque, hwaddr addr, unsigned size) uint64_t systime = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); uint64_t kltime; - kltime = muldiv64(systime, 4194300, get_ticks_per_sec() * 4); + kltime = muldiv64(systime, 4194300, NANOSECONDS_PER_SECOND * 4); kltime = muldiv64(kltime, 18432000, 1048575); switch (addr) { @@ -328,16 +334,12 @@ static void macio_newworld_init(Object *obj) static void macio_instance_init(Object *obj) { MacIOState *s = MACIO(obj); - MemoryRegion *dbdma_mem; - memory_region_init(&s->bar, NULL, "macio", 0x80000); + memory_region_init(&s->bar, obj, "macio", 0x80000); object_initialize(&s->cuda, sizeof(s->cuda), TYPE_CUDA); qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default()); object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL); - - s->dbdma = DBDMA_init(&dbdma_mem); - memory_region_add_subregion(&s->bar, 0x08000, dbdma_mem); } static const VMStateDescription vmstate_macio_oldworld = { @@ -393,6 +395,7 @@ static void macio_class_init(ObjectClass *klass, void *data) k->vendor_id = PCI_VENDOR_ID_APPLE; k->class_id = PCI_CLASS_OTHERS << 8; dc->props = macio_properties; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo macio_oldworld_type_info = { diff --git a/qemu/hw/misc/max111x.c b/qemu/hw/misc/max111x.c index bef3651d6..9014f0f70 100644 --- a/qemu/hw/misc/max111x.c +++ b/qemu/hw/misc/max111x.c @@ -10,7 +10,8 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw/ssi.h" +#include "qemu/osdep.h" +#include "hw/ssi/ssi.h" typedef struct { SSISlave parent_obj; diff --git a/qemu/hw/misc/milkymist-hpdmc.c b/qemu/hw/misc/milkymist-hpdmc.c index f5f4c1b34..b97000fc4 100644 --- a/qemu/hw/misc/milkymist-hpdmc.c +++ b/qemu/hw/misc/milkymist-hpdmc.c @@ -21,6 +21,7 @@ * http://www.milkymist.org/socdoc/hpdmc.pdf */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "trace.h" diff --git a/qemu/hw/misc/milkymist-pfpu.c b/qemu/hw/misc/milkymist-pfpu.c index 08b604f13..57acd7b36 100644 --- a/qemu/hw/misc/milkymist-pfpu.c +++ b/qemu/hw/misc/milkymist-pfpu.c @@ -22,6 +22,7 @@ * */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "trace.h" diff --git a/qemu/hw/misc/mips_cmgcr.c b/qemu/hw/misc/mips_cmgcr.c new file mode 100644 index 000000000..37be23995 --- /dev/null +++ b/qemu/hw/misc/mips_cmgcr.c @@ -0,0 +1,160 @@ +/* + * 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) 2012 MIPS Technologies, Inc. All rights reserved. + * Authors: Sanjay Lal + * + * Copyright (C) 2015 Imagination Technologies + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "hw/misc/mips_cmgcr.h" +#include "hw/misc/mips_cpc.h" + +static inline bool is_cpc_connected(MIPSGCRState *s) +{ + return s->cpc_mr != NULL; +} + +static inline void update_cpc_base(MIPSGCRState *gcr, uint64_t val) +{ + if (is_cpc_connected(gcr)) { + gcr->cpc_base = val & GCR_CPC_BASE_MSK; + memory_region_transaction_begin(); + memory_region_set_address(gcr->cpc_mr, + gcr->cpc_base & GCR_CPC_BASE_CPCBASE_MSK); + memory_region_set_enabled(gcr->cpc_mr, + gcr->cpc_base & GCR_CPC_BASE_CPCEN_MSK); + memory_region_transaction_commit(); + } +} + +/* Read GCR registers */ +static uint64_t gcr_read(void *opaque, hwaddr addr, unsigned size) +{ + MIPSGCRState *gcr = (MIPSGCRState *) opaque; + + switch (addr) { + /* Global Control Block Register */ + case GCR_CONFIG_OFS: + /* Set PCORES to 0 */ + return 0; + case GCR_BASE_OFS: + return gcr->gcr_base; + case GCR_REV_OFS: + return gcr->gcr_rev; + case GCR_CPC_BASE_OFS: + return gcr->cpc_base; + case GCR_CPC_STATUS_OFS: + return is_cpc_connected(gcr); + case GCR_L2_CONFIG_OFS: + /* L2 BYPASS */ + return GCR_L2_CONFIG_BYPASS_MSK; + /* Core-Local and Core-Other Control Blocks */ + case MIPS_CLCB_OFS + GCR_CL_CONFIG_OFS: + case MIPS_COCB_OFS + GCR_CL_CONFIG_OFS: + /* Set PVP to # of VPs - 1 */ + return gcr->num_vps - 1; + case MIPS_CLCB_OFS + GCR_CL_OTHER_OFS: + return 0; + default: + qemu_log_mask(LOG_UNIMP, "Read %d bytes at GCR offset 0x%" HWADDR_PRIx + "\n", size, addr); + return 0; + } + return 0; +} + +/* Write GCR registers */ +static void gcr_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) +{ + MIPSGCRState *gcr = (MIPSGCRState *)opaque; + + switch (addr) { + case GCR_CPC_BASE_OFS: + update_cpc_base(gcr, data); + break; + default: + qemu_log_mask(LOG_UNIMP, "Write %d bytes at GCR offset 0x%" HWADDR_PRIx + " 0x%" PRIx64 "\n", size, addr, data); + break; + } +} + +static const MemoryRegionOps gcr_ops = { + .read = gcr_read, + .write = gcr_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .max_access_size = 8, + }, +}; + +static void mips_gcr_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + MIPSGCRState *s = MIPS_GCR(obj); + + object_property_add_link(obj, "cpc", TYPE_MEMORY_REGION, + (Object **)&s->cpc_mr, + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + &error_abort); + + memory_region_init_io(&s->iomem, OBJECT(s), &gcr_ops, s, + "mips-gcr", GCR_ADDRSPACE_SZ); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void mips_gcr_reset(DeviceState *dev) +{ + MIPSGCRState *s = MIPS_GCR(dev); + + update_cpc_base(s, 0); +} + +static const VMStateDescription vmstate_mips_gcr = { + .name = "mips-gcr", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT64(cpc_base, MIPSGCRState), + VMSTATE_END_OF_LIST() + }, +}; + +static Property mips_gcr_properties[] = { + DEFINE_PROP_INT32("num-vp", MIPSGCRState, num_vps, 1), + DEFINE_PROP_INT32("gcr-rev", MIPSGCRState, gcr_rev, 0x800), + DEFINE_PROP_UINT64("gcr-base", MIPSGCRState, gcr_base, GCR_BASE_ADDR), + DEFINE_PROP_END_OF_LIST(), +}; + +static void mips_gcr_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + dc->props = mips_gcr_properties; + dc->vmsd = &vmstate_mips_gcr; + dc->reset = mips_gcr_reset; +} + +static const TypeInfo mips_gcr_info = { + .name = TYPE_MIPS_GCR, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MIPSGCRState), + .instance_init = mips_gcr_init, + .class_init = mips_gcr_class_init, +}; + +static void mips_gcr_register_types(void) +{ + type_register_static(&mips_gcr_info); +} + +type_init(mips_gcr_register_types) diff --git a/qemu/hw/misc/mips_cpc.c b/qemu/hw/misc/mips_cpc.c new file mode 100644 index 000000000..d2b8e42da --- /dev/null +++ b/qemu/hw/misc/mips_cpc.c @@ -0,0 +1,177 @@ +/* + * Cluster Power Controller emulation + * + * Copyright (c) 2016 Imagination Technologies + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/sysbus.h" + +#include "hw/misc/mips_cpc.h" + +static inline uint64_t cpc_vp_run_mask(MIPSCPCState *cpc) +{ + return (1ULL << cpc->num_vp) - 1; +} + +static void cpc_run_vp(MIPSCPCState *cpc, uint64_t vp_run) +{ + CPUState *cs = first_cpu; + + CPU_FOREACH(cs) { + uint64_t i = 1ULL << cs->cpu_index; + if (i & vp_run & ~cpc->vp_running) { + cpu_interrupt(cs, CPU_INTERRUPT_WAKE); + cpc->vp_running |= i; + } + } +} + +static void cpc_stop_vp(MIPSCPCState *cpc, uint64_t vp_stop) +{ + CPUState *cs = first_cpu; + + CPU_FOREACH(cs) { + uint64_t i = 1ULL << cs->cpu_index; + if (i & vp_stop & cpc->vp_running) { + cs->halted = 1; + cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE); + cpc->vp_running &= ~i; + } + } +} + +static void cpc_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + MIPSCPCState *s = opaque; + + switch (offset) { + case CPC_CL_BASE_OFS + CPC_VP_RUN_OFS: + case CPC_CO_BASE_OFS + CPC_VP_RUN_OFS: + cpc_run_vp(s, data & cpc_vp_run_mask(s)); + break; + case CPC_CL_BASE_OFS + CPC_VP_STOP_OFS: + case CPC_CO_BASE_OFS + CPC_VP_STOP_OFS: + cpc_stop_vp(s, data & cpc_vp_run_mask(s)); + break; + default: + qemu_log_mask(LOG_UNIMP, + "%s: Bad offset 0x%x\n", __func__, (int)offset); + break; + } + + return; +} + +static uint64_t cpc_read(void *opaque, hwaddr offset, unsigned size) +{ + MIPSCPCState *s = opaque; + + switch (offset) { + case CPC_CL_BASE_OFS + CPC_VP_RUNNING_OFS: + case CPC_CO_BASE_OFS + CPC_VP_RUNNING_OFS: + return s->vp_running; + default: + qemu_log_mask(LOG_UNIMP, + "%s: Bad offset 0x%x\n", __func__, (int)offset); + return 0; + } +} + +static const MemoryRegionOps cpc_ops = { + .read = cpc_read, + .write = cpc_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .max_access_size = 8, + }, +}; + +static void mips_cpc_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + MIPSCPCState *s = MIPS_CPC(obj); + + memory_region_init_io(&s->mr, OBJECT(s), &cpc_ops, s, "mips-cpc", + CPC_ADDRSPACE_SZ); + sysbus_init_mmio(sbd, &s->mr); +} + +static void mips_cpc_realize(DeviceState *dev, Error **errp) +{ + MIPSCPCState *s = MIPS_CPC(dev); + + if (s->vp_start_running > cpc_vp_run_mask(s)) { + error_setg(errp, + "incorrect vp_start_running 0x%" PRIx64 " for num_vp = %d", + s->vp_running, s->num_vp); + return; + } +} + +static void mips_cpc_reset(DeviceState *dev) +{ + MIPSCPCState *s = MIPS_CPC(dev); + + /* Reflect the fact that all VPs are halted on reset */ + s->vp_running = 0; + + /* Put selected VPs into run state */ + cpc_run_vp(s, s->vp_start_running); +} + +static const VMStateDescription vmstate_mips_cpc = { + .name = "mips-cpc", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT64(vp_running, MIPSCPCState), + VMSTATE_END_OF_LIST() + }, +}; + +static Property mips_cpc_properties[] = { + DEFINE_PROP_UINT32("num-vp", MIPSCPCState, num_vp, 0x1), + DEFINE_PROP_UINT64("vp-start-running", MIPSCPCState, vp_start_running, 0x1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void mips_cpc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = mips_cpc_realize; + dc->reset = mips_cpc_reset; + dc->vmsd = &vmstate_mips_cpc; + dc->props = mips_cpc_properties; +} + +static const TypeInfo mips_cpc_info = { + .name = TYPE_MIPS_CPC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MIPSCPCState), + .instance_init = mips_cpc_init, + .class_init = mips_cpc_class_init, +}; + +static void mips_cpc_register_types(void) +{ + type_register_static(&mips_cpc_info); +} + +type_init(mips_cpc_register_types) diff --git a/qemu/hw/misc/mips_itu.c b/qemu/hw/misc/mips_itu.c new file mode 100644 index 000000000..da5455062 --- /dev/null +++ b/qemu/hw/misc/mips_itu.c @@ -0,0 +1,521 @@ +/* + * Inter-Thread Communication Unit emulation. + * + * Copyright (c) 2016 Imagination Technologies + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "hw/misc/mips_itu.h" + +#define ITC_TAG_ADDRSPACE_SZ (ITC_ADDRESSMAP_NUM * 8) +/* Initialize as 4kB area to fit all 32 cells with default 128B grain. + Storage may be resized by the software. */ +#define ITC_STORAGE_ADDRSPACE_SZ 0x1000 + +#define ITC_FIFO_NUM_MAX 16 +#define ITC_SEMAPH_NUM_MAX 16 +#define ITC_AM1_NUMENTRIES_OFS 20 + +#define ITC_CELL_PV_MAX_VAL 0xFFFF + +#define ITC_CELL_TAG_FIFO_DEPTH 28 +#define ITC_CELL_TAG_FIFO_PTR 18 +#define ITC_CELL_TAG_FIFO 17 +#define ITC_CELL_TAG_T 16 +#define ITC_CELL_TAG_F 1 +#define ITC_CELL_TAG_E 0 + +#define ITC_AM0_BASE_ADDRESS_MASK 0xFFFFFC00ULL +#define ITC_AM0_EN_MASK 0x1 + +#define ITC_AM1_ADDR_MASK_MASK 0x1FC00 +#define ITC_AM1_ENTRY_GRAIN_MASK 0x7 + +typedef enum ITCView { + ITCVIEW_BYPASS = 0, + ITCVIEW_CONTROL = 1, + ITCVIEW_EF_SYNC = 2, + ITCVIEW_EF_TRY = 3, + ITCVIEW_PV_SYNC = 4, + ITCVIEW_PV_TRY = 5 +} ITCView; + +MemoryRegion *mips_itu_get_tag_region(MIPSITUState *itu) +{ + return &itu->tag_io; +} + +static uint64_t itc_tag_read(void *opaque, hwaddr addr, unsigned size) +{ + MIPSITUState *tag = (MIPSITUState *)opaque; + uint64_t index = addr >> 3; + + if (index >= ITC_ADDRESSMAP_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "Read 0x%" PRIx64 "\n", addr); + return 0; + } + + return tag->ITCAddressMap[index]; +} + +static void itc_reconfigure(MIPSITUState *tag) +{ + uint64_t *am = &tag->ITCAddressMap[0]; + MemoryRegion *mr = &tag->storage_io; + hwaddr address = am[0] & ITC_AM0_BASE_ADDRESS_MASK; + uint64_t size = (1 << 10) + (am[1] & ITC_AM1_ADDR_MASK_MASK); + bool is_enabled = (am[0] & ITC_AM0_EN_MASK) != 0; + + memory_region_transaction_begin(); + if (!(size & (size - 1))) { + memory_region_set_size(mr, size); + } + memory_region_set_address(mr, address); + memory_region_set_enabled(mr, is_enabled); + memory_region_transaction_commit(); +} + +static void itc_tag_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + MIPSITUState *tag = (MIPSITUState *)opaque; + uint64_t *am = &tag->ITCAddressMap[0]; + uint64_t am_old, mask; + uint64_t index = addr >> 3; + + switch (index) { + case 0: + mask = ITC_AM0_BASE_ADDRESS_MASK | ITC_AM0_EN_MASK; + break; + case 1: + mask = ITC_AM1_ADDR_MASK_MASK | ITC_AM1_ENTRY_GRAIN_MASK; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "Bad write 0x%" PRIx64 "\n", addr); + return; + } + + am_old = am[index]; + am[index] = (data & mask) | (am_old & ~mask); + if (am_old != am[index]) { + itc_reconfigure(tag); + } +} + +static const MemoryRegionOps itc_tag_ops = { + .read = itc_tag_read, + .write = itc_tag_write, + .impl = { + .max_access_size = 8, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static inline uint32_t get_num_cells(MIPSITUState *s) +{ + return s->num_fifo + s->num_semaphores; +} + +static inline ITCView get_itc_view(hwaddr addr) +{ + return (addr >> 3) & 0xf; +} + +static inline int get_cell_stride_shift(const MIPSITUState *s) +{ + /* Minimum interval (for EntryGain = 0) is 128 B */ + return 7 + (s->ITCAddressMap[1] & ITC_AM1_ENTRY_GRAIN_MASK); +} + +static inline ITCStorageCell *get_cell(MIPSITUState *s, + hwaddr addr) +{ + uint32_t cell_idx = addr >> get_cell_stride_shift(s); + uint32_t num_cells = get_num_cells(s); + + if (cell_idx >= num_cells) { + cell_idx = num_cells - 1; + } + + return &s->cell[cell_idx]; +} + +static void wake_blocked_threads(ITCStorageCell *c) +{ + CPUState *cs; + CPU_FOREACH(cs) { + if (cs->halted && (c->blocked_threads & (1ULL << cs->cpu_index))) { + cpu_interrupt(cs, CPU_INTERRUPT_WAKE); + } + } + c->blocked_threads = 0; +} + +static void QEMU_NORETURN block_thread_and_exit(ITCStorageCell *c) +{ + c->blocked_threads |= 1ULL << current_cpu->cpu_index; + cpu_restore_state(current_cpu, current_cpu->mem_io_pc); + current_cpu->halted = 1; + current_cpu->exception_index = EXCP_HLT; + cpu_loop_exit(current_cpu); +} + +/* ITC Bypass View */ + +static inline uint64_t view_bypass_read(ITCStorageCell *c) +{ + if (c->tag.FIFO) { + return c->data[c->fifo_out]; + } else { + return c->data[0]; + } +} + +static inline void view_bypass_write(ITCStorageCell *c, uint64_t val) +{ + if (c->tag.FIFO && (c->tag.FIFOPtr > 0)) { + int idx = (c->fifo_out + c->tag.FIFOPtr - 1) % ITC_CELL_DEPTH; + c->data[idx] = val; + } + + /* ignore a write to the semaphore cell */ +} + +/* ITC Control View */ + +static inline uint64_t view_control_read(ITCStorageCell *c) +{ + return ((uint64_t)c->tag.FIFODepth << ITC_CELL_TAG_FIFO_DEPTH) | + (c->tag.FIFOPtr << ITC_CELL_TAG_FIFO_PTR) | + (c->tag.FIFO << ITC_CELL_TAG_FIFO) | + (c->tag.T << ITC_CELL_TAG_T) | + (c->tag.E << ITC_CELL_TAG_E) | + (c->tag.F << ITC_CELL_TAG_F); +} + +static inline void view_control_write(ITCStorageCell *c, uint64_t val) +{ + c->tag.T = (val >> ITC_CELL_TAG_T) & 1; + c->tag.E = (val >> ITC_CELL_TAG_E) & 1; + c->tag.F = (val >> ITC_CELL_TAG_F) & 1; + + if (c->tag.E) { + c->tag.FIFOPtr = 0; + } +} + +/* ITC Empty/Full View */ + +static uint64_t view_ef_common_read(ITCStorageCell *c, bool blocking) +{ + uint64_t ret = 0; + + if (!c->tag.FIFO) { + return 0; + } + + c->tag.F = 0; + + if (blocking && c->tag.E) { + block_thread_and_exit(c); + } + + if (c->blocked_threads) { + wake_blocked_threads(c); + } + + if (c->tag.FIFOPtr > 0) { + ret = c->data[c->fifo_out]; + c->fifo_out = (c->fifo_out + 1) % ITC_CELL_DEPTH; + c->tag.FIFOPtr--; + } + + if (c->tag.FIFOPtr == 0) { + c->tag.E = 1; + } + + return ret; +} + +static uint64_t view_ef_sync_read(ITCStorageCell *c) +{ + return view_ef_common_read(c, true); +} + +static uint64_t view_ef_try_read(ITCStorageCell *c) +{ + return view_ef_common_read(c, false); +} + +static inline void view_ef_common_write(ITCStorageCell *c, uint64_t val, + bool blocking) +{ + if (!c->tag.FIFO) { + return; + } + + c->tag.E = 0; + + if (blocking && c->tag.F) { + block_thread_and_exit(c); + } + + if (c->blocked_threads) { + wake_blocked_threads(c); + } + + if (c->tag.FIFOPtr < ITC_CELL_DEPTH) { + int idx = (c->fifo_out + c->tag.FIFOPtr) % ITC_CELL_DEPTH; + c->data[idx] = val; + c->tag.FIFOPtr++; + } + + if (c->tag.FIFOPtr == ITC_CELL_DEPTH) { + c->tag.F = 1; + } +} + +static void view_ef_sync_write(ITCStorageCell *c, uint64_t val) +{ + view_ef_common_write(c, val, true); +} + +static void view_ef_try_write(ITCStorageCell *c, uint64_t val) +{ + view_ef_common_write(c, val, false); +} + +/* ITC P/V View */ + +static uint64_t view_pv_common_read(ITCStorageCell *c, bool blocking) +{ + uint64_t ret = c->data[0]; + + if (c->tag.FIFO) { + return 0; + } + + if (c->data[0] > 0) { + c->data[0]--; + } else if (blocking) { + block_thread_and_exit(c); + } + + return ret; +} + +static uint64_t view_pv_sync_read(ITCStorageCell *c) +{ + return view_pv_common_read(c, true); +} + +static uint64_t view_pv_try_read(ITCStorageCell *c) +{ + return view_pv_common_read(c, false); +} + +static inline void view_pv_common_write(ITCStorageCell *c) +{ + if (c->tag.FIFO) { + return; + } + + if (c->data[0] < ITC_CELL_PV_MAX_VAL) { + c->data[0]++; + } + + if (c->blocked_threads) { + wake_blocked_threads(c); + } +} + +static void view_pv_sync_write(ITCStorageCell *c) +{ + view_pv_common_write(c); +} + +static void view_pv_try_write(ITCStorageCell *c) +{ + view_pv_common_write(c); +} + +static uint64_t itc_storage_read(void *opaque, hwaddr addr, unsigned size) +{ + MIPSITUState *s = (MIPSITUState *)opaque; + ITCStorageCell *cell = get_cell(s, addr); + ITCView view = get_itc_view(addr); + uint64_t ret = -1; + + switch (view) { + case ITCVIEW_BYPASS: + ret = view_bypass_read(cell); + break; + case ITCVIEW_CONTROL: + ret = view_control_read(cell); + break; + case ITCVIEW_EF_SYNC: + ret = view_ef_sync_read(cell); + break; + case ITCVIEW_EF_TRY: + ret = view_ef_try_read(cell); + break; + case ITCVIEW_PV_SYNC: + ret = view_pv_sync_read(cell); + break; + case ITCVIEW_PV_TRY: + ret = view_pv_try_read(cell); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "itc_storage_read: Bad ITC View %d\n", (int)view); + break; + } + + return ret; +} + +static void itc_storage_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + MIPSITUState *s = (MIPSITUState *)opaque; + ITCStorageCell *cell = get_cell(s, addr); + ITCView view = get_itc_view(addr); + + switch (view) { + case ITCVIEW_BYPASS: + view_bypass_write(cell, data); + break; + case ITCVIEW_CONTROL: + view_control_write(cell, data); + break; + case ITCVIEW_EF_SYNC: + view_ef_sync_write(cell, data); + break; + case ITCVIEW_EF_TRY: + view_ef_try_write(cell, data); + break; + case ITCVIEW_PV_SYNC: + view_pv_sync_write(cell); + break; + case ITCVIEW_PV_TRY: + view_pv_try_write(cell); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "itc_storage_write: Bad ITC View %d\n", (int)view); + break; + } + +} + +static const MemoryRegionOps itc_storage_ops = { + .read = itc_storage_read, + .write = itc_storage_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void itc_reset_cells(MIPSITUState *s) +{ + int i; + + memset(s->cell, 0, get_num_cells(s) * sizeof(s->cell[0])); + + for (i = 0; i < s->num_fifo; i++) { + s->cell[i].tag.E = 1; + s->cell[i].tag.FIFO = 1; + s->cell[i].tag.FIFODepth = ITC_CELL_DEPTH_SHIFT; + } +} + +static void mips_itu_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + MIPSITUState *s = MIPS_ITU(obj); + + memory_region_init_io(&s->storage_io, OBJECT(s), &itc_storage_ops, s, + "mips-itc-storage", ITC_STORAGE_ADDRSPACE_SZ); + sysbus_init_mmio(sbd, &s->storage_io); + + memory_region_init_io(&s->tag_io, OBJECT(s), &itc_tag_ops, s, + "mips-itc-tag", ITC_TAG_ADDRSPACE_SZ); +} + +static void mips_itu_realize(DeviceState *dev, Error **errp) +{ + MIPSITUState *s = MIPS_ITU(dev); + + if (s->num_fifo > ITC_FIFO_NUM_MAX) { + error_setg(errp, "Exceed maximum number of FIFO cells: %d", + s->num_fifo); + return; + } + if (s->num_semaphores > ITC_SEMAPH_NUM_MAX) { + error_setg(errp, "Exceed maximum number of Semaphore cells: %d", + s->num_semaphores); + return; + } + + s->cell = g_new(ITCStorageCell, get_num_cells(s)); +} + +static void mips_itu_reset(DeviceState *dev) +{ + MIPSITUState *s = MIPS_ITU(dev); + + s->ITCAddressMap[0] = 0; + s->ITCAddressMap[1] = + ((ITC_STORAGE_ADDRSPACE_SZ - 1) & ITC_AM1_ADDR_MASK_MASK) | + (get_num_cells(s) << ITC_AM1_NUMENTRIES_OFS); + itc_reconfigure(s); + + itc_reset_cells(s); +} + +static Property mips_itu_properties[] = { + DEFINE_PROP_INT32("num-fifo", MIPSITUState, num_fifo, + ITC_FIFO_NUM_MAX), + DEFINE_PROP_INT32("num-semaphores", MIPSITUState, num_semaphores, + ITC_SEMAPH_NUM_MAX), + DEFINE_PROP_END_OF_LIST(), +}; + +static void mips_itu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = mips_itu_properties; + dc->realize = mips_itu_realize; + dc->reset = mips_itu_reset; +} + +static const TypeInfo mips_itu_info = { + .name = TYPE_MIPS_ITU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MIPSITUState), + .instance_init = mips_itu_init, + .class_init = mips_itu_class_init, +}; + +static void mips_itu_register_types(void) +{ + type_register_static(&mips_itu_info); +} + +type_init(mips_itu_register_types) diff --git a/qemu/hw/misc/mst_fpga.c b/qemu/hw/misc/mst_fpga.c index d5090799f..48d7dfb2d 100644 --- a/qemu/hw/misc/mst_fpga.c +++ b/qemu/hw/misc/mst_fpga.c @@ -10,6 +10,7 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" diff --git a/qemu/hw/misc/omap_clk.c b/qemu/hw/misc/omap_clk.c index 80a3c50e1..19151d07d 100644 --- a/qemu/hw/misc/omap_clk.c +++ b/qemu/hw/misc/omap_clk.c @@ -18,6 +18,7 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/arm/omap.h" @@ -1239,7 +1240,7 @@ void omap_clk_init(struct omap_mpu_state_s *mpu) for (i = onchip_clks, count = 0; *i; i ++) if ((*i)->flags & flag) count ++; - mpu->clks = (struct clk *) g_malloc0(sizeof(struct clk) * (count + 1)); + mpu->clks = g_new0(struct clk, count + 1); for (i = onchip_clks, j = mpu->clks; *i; i ++) if ((*i)->flags & flag) { memcpy(j, *i, sizeof(struct clk)); diff --git a/qemu/hw/misc/omap_gpmc.c b/qemu/hw/misc/omap_gpmc.c index 74fc91c8e..67d8e2f02 100644 --- a/qemu/hw/misc/omap_gpmc.c +++ b/qemu/hw/misc/omap_gpmc.c @@ -18,6 +18,7 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/block/flash.h" #include "hw/arm/omap.h" @@ -826,8 +827,7 @@ struct omap_gpmc_s *omap_gpmc_init(struct omap_mpu_state_s *mpu, qemu_irq irq, qemu_irq drq) { int cs; - struct omap_gpmc_s *s = (struct omap_gpmc_s *) - g_malloc0(sizeof(struct omap_gpmc_s)); + struct omap_gpmc_s *s = g_new0(struct omap_gpmc_s, 1); memory_region_init_io(&s->iomem, NULL, &omap_gpmc_ops, s, "omap-gpmc", 0x1000); memory_region_add_subregion(get_system_memory(), base, &s->iomem); diff --git a/qemu/hw/misc/omap_l4.c b/qemu/hw/misc/omap_l4.c index 245ceac84..88c533a0f 100644 --- a/qemu/hw/misc/omap_l4.c +++ b/qemu/hw/misc/omap_l4.c @@ -17,6 +17,7 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/arm/omap.h" diff --git a/qemu/hw/misc/omap_sdrc.c b/qemu/hw/misc/omap_sdrc.c index 3de0c0e9d..dff37ecaf 100644 --- a/qemu/hw/misc/omap_sdrc.c +++ b/qemu/hw/misc/omap_sdrc.c @@ -17,6 +17,7 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/arm/omap.h" @@ -157,8 +158,7 @@ static const MemoryRegionOps omap_sdrc_ops = { struct omap_sdrc_s *omap_sdrc_init(MemoryRegion *sysmem, hwaddr base) { - struct omap_sdrc_s *s = (struct omap_sdrc_s *) - g_malloc0(sizeof(struct omap_sdrc_s)); + struct omap_sdrc_s *s = g_new0(struct omap_sdrc_s, 1); omap_sdrc_reset(s); diff --git a/qemu/hw/misc/omap_tap.c b/qemu/hw/misc/omap_tap.c index 6f02bb9e4..e6ea8ee23 100644 --- a/qemu/hw/misc/omap_tap.c +++ b/qemu/hw/misc/omap_tap.c @@ -18,6 +18,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/arm/omap.h" diff --git a/qemu/hw/misc/pc-testdev.c b/qemu/hw/misc/pc-testdev.c index 18e94e07b..086893dcc 100644 --- a/qemu/hw/misc/pc-testdev.c +++ b/qemu/hw/misc/pc-testdev.c @@ -35,7 +35,7 @@ * git://git.kernel.org/pub/scm/virt/kvm/kvm-unit-tests.git */ -#include "config-host.h" +#include "qemu/osdep.h" #if defined(CONFIG_POSIX) #include #endif diff --git a/qemu/hw/misc/pci-testdev.c b/qemu/hw/misc/pci-testdev.c index 26b9b8617..2f2e98977 100644 --- a/qemu/hw/misc/pci-testdev.c +++ b/qemu/hw/misc/pci-testdev.c @@ -17,10 +17,10 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "qemu/event_notifier.h" -#include "qemu/osdep.h" typedef struct PCITestDevHdr { uint8_t test; @@ -239,6 +239,7 @@ static void pci_testdev_realize(PCIDevice *pci_dev, Error **errp) uint8_t *pci_conf; char *name; int r, i; + bool fastmmio = kvm_ioeventfd_any_length_enabled(); pci_conf = pci_dev->config; @@ -261,8 +262,12 @@ static void pci_testdev_realize(PCIDevice *pci_dev, Error **errp) memcpy(test->hdr->name, name, strlen(name) + 1); g_free(name); test->hdr->offset = cpu_to_le32(IOTEST_SIZE(i) + i * IOTEST_ACCESS_WIDTH); - test->size = IOTEST_ACCESS_WIDTH; test->match_data = strcmp(IOTEST_TEST(i), "wildcard-eventfd"); + if (fastmmio && IOTEST_IS_MEM(i) && !test->match_data) { + test->size = 0; + } else { + test->size = IOTEST_ACCESS_WIDTH; + } test->hdr->test = i; test->hdr->data = test->match_data ? IOTEST_DATAMATCH : IOTEST_NOMATCH; test->hdr->width = IOTEST_ACCESS_WIDTH; diff --git a/qemu/hw/misc/puv3_pm.c b/qemu/hw/misc/puv3_pm.c index 37f23695d..577cebaac 100644 --- a/qemu/hw/misc/puv3_pm.c +++ b/qemu/hw/misc/puv3_pm.c @@ -8,6 +8,7 @@ * published by the Free Software Foundation, or any later version. * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" diff --git a/qemu/hw/misc/pvpanic.c b/qemu/hw/misc/pvpanic.c index 994f8af8e..0ac1e6ac9 100644 --- a/qemu/hw/misc/pvpanic.c +++ b/qemu/hw/misc/pvpanic.c @@ -12,6 +12,7 @@ * */ +#include "qemu/osdep.h" #include "qapi/qmp/qobject.h" #include "qapi/qmp/qjson.h" #include "sysemu/sysemu.h" @@ -41,8 +42,7 @@ static void handle_event(int event) } if (event & PVPANIC_PANICKED) { - qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, &error_abort); - vm_stop(RUN_STATE_GUEST_PANICKED); + qemu_system_guest_panicked(); return; } } diff --git a/qemu/hw/misc/sga.c b/qemu/hw/misc/sga.c index 83d2fd9d3..03b006d6f 100644 --- a/qemu/hw/misc/sga.c +++ b/qemu/hw/misc/sga.c @@ -24,6 +24,7 @@ * sgabios code originally available at code.google.com/p/sgabios * */ +#include "qemu/osdep.h" #include "hw/pci/pci.h" #include "hw/i386/pc.h" #include "hw/loader.h" diff --git a/qemu/hw/misc/slavio_misc.c b/qemu/hw/misc/slavio_misc.c index ec50f1075..edd5de070 100644 --- a/qemu/hw/misc/slavio_misc.c +++ b/qemu/hw/misc/slavio_misc.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "sysemu/sysemu.h" #include "hw/sysbus.h" #include "trace.h" diff --git a/qemu/hw/misc/stm32f2xx_syscfg.c b/qemu/hw/misc/stm32f2xx_syscfg.c index 4ae4042bf..d0d7076ef 100644 --- a/qemu/hw/misc/stm32f2xx_syscfg.c +++ b/qemu/hw/misc/stm32f2xx_syscfg.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/misc/stm32f2xx_syscfg.h" #ifndef STM_SYSCFG_ERR_DEBUG diff --git a/qemu/hw/misc/tmp105.c b/qemu/hw/misc/tmp105.c index f3fe8b81f..f5c2472b5 100644 --- a/qemu/hw/misc/tmp105.c +++ b/qemu/hw/misc/tmp105.c @@ -18,9 +18,11 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i2c/i2c.h" #include "tmp105.h" +#include "qapi/error.h" #include "qapi/visitor.h" static void tmp105_interrupt_update(TMP105State *s) @@ -52,26 +54,26 @@ static void tmp105_alarm_update(TMP105State *s) tmp105_interrupt_update(s); } -static void tmp105_get_temperature(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void tmp105_get_temperature(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { TMP105State *s = TMP105(obj); int64_t value = s->temperature * 1000 / 256; - visit_type_int(v, &value, name, errp); + visit_type_int(v, name, &value, errp); } /* Units are 0.001 centigrades relative to 0 C. s->temperature is 8.8 * fixed point, so units are 1/256 centigrades. A simple ratio will do. */ -static void tmp105_set_temperature(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void tmp105_set_temperature(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { TMP105State *s = TMP105(obj); Error *local_err = NULL; int64_t temp; - visit_type_int(v, &temp, name, &local_err); + visit_type_int(v, name, &temp, &local_err); if (local_err) { error_propagate(errp, local_err); return; diff --git a/qemu/hw/misc/vmport.c b/qemu/hw/misc/vmport.c index cd5716a46..689678980 100644 --- a/qemu/hw/misc/vmport.c +++ b/qemu/hw/misc/vmport.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/isa/isa.h" #include "hw/i386/pc.h" diff --git a/qemu/hw/misc/zynq-xadc.c b/qemu/hw/misc/zynq-xadc.c new file mode 100644 index 000000000..71fbccd79 --- /dev/null +++ b/qemu/hw/misc/zynq-xadc.c @@ -0,0 +1,303 @@ +/* + * ADC registers for Xilinx Zynq Platform + * + * Copyright (c) 2015 Guenter Roeck + * Based on hw/misc/zynq_slcr.c, written by Michal Simek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/misc/zynq-xadc.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" + +enum { + CFG = 0x000 / 4, + INT_STS, + INT_MASK, + MSTS, + CMDFIFO, + RDFIFO, + MCTL, +}; + +#define CFG_ENABLE BIT(31) +#define CFG_CFIFOTH_SHIFT 20 +#define CFG_CFIFOTH_LENGTH 4 +#define CFG_DFIFOTH_SHIFT 16 +#define CFG_DFIFOTH_LENGTH 4 +#define CFG_WEDGE BIT(13) +#define CFG_REDGE BIT(12) +#define CFG_TCKRATE_SHIFT 8 +#define CFG_TCKRATE_LENGTH 2 + +#define CFG_TCKRATE_DIV(x) (0x1 << (x - 1)) + +#define CFG_IGAP_SHIFT 0 +#define CFG_IGAP_LENGTH 5 + +#define INT_CFIFO_LTH BIT(9) +#define INT_DFIFO_GTH BIT(8) +#define INT_OT BIT(7) +#define INT_ALM_SHIFT 0 +#define INT_ALM_LENGTH 7 +#define INT_ALM_MASK (((1 << INT_ALM_LENGTH) - 1) << INT_ALM_SHIFT) + +#define INT_ALL (INT_CFIFO_LTH | INT_DFIFO_GTH | INT_OT | INT_ALM_MASK) + +#define MSTS_CFIFO_LVL_SHIFT 16 +#define MSTS_CFIFO_LVL_LENGTH 4 +#define MSTS_DFIFO_LVL_SHIFT 12 +#define MSTS_DFIFO_LVL_LENGTH 4 +#define MSTS_CFIFOF BIT(11) +#define MSTS_CFIFOE BIT(10) +#define MSTS_DFIFOF BIT(9) +#define MSTS_DFIFOE BIT(8) +#define MSTS_OT BIT(7) +#define MSTS_ALM_SHIFT 0 +#define MSTS_ALM_LENGTH 7 + +#define MCTL_RESET BIT(4) + +#define CMD_NOP 0x00 +#define CMD_READ 0x01 +#define CMD_WRITE 0x02 + +static void zynq_xadc_update_ints(ZynqXADCState *s) +{ + + /* We are fast, commands are actioned instantly so the CFIFO is always + * empty (and below threshold). + */ + s->regs[INT_STS] |= INT_CFIFO_LTH; + + if (s->xadc_dfifo_entries > + extract32(s->regs[CFG], CFG_DFIFOTH_SHIFT, CFG_DFIFOTH_LENGTH)) { + s->regs[INT_STS] |= INT_DFIFO_GTH; + } + + qemu_set_irq(s->qemu_irq, !!(s->regs[INT_STS] & ~s->regs[INT_MASK])); +} + +static void zynq_xadc_reset(DeviceState *d) +{ + ZynqXADCState *s = ZYNQ_XADC(d); + + s->regs[CFG] = 0x14 << CFG_IGAP_SHIFT | + CFG_TCKRATE_DIV(4) << CFG_TCKRATE_SHIFT | CFG_REDGE; + s->regs[INT_STS] = INT_CFIFO_LTH; + s->regs[INT_MASK] = 0xffffffff; + s->regs[CMDFIFO] = 0; + s->regs[RDFIFO] = 0; + s->regs[MCTL] = MCTL_RESET; + + memset(s->xadc_regs, 0, sizeof(s->xadc_regs)); + memset(s->xadc_dfifo, 0, sizeof(s->xadc_dfifo)); + s->xadc_dfifo_entries = 0; + + zynq_xadc_update_ints(s); +} + +static uint16_t xadc_pop_dfifo(ZynqXADCState *s) +{ + uint16_t rv = s->xadc_dfifo[0]; + int i; + + if (s->xadc_dfifo_entries > 0) { + s->xadc_dfifo_entries--; + } + for (i = 0; i < s->xadc_dfifo_entries; i++) { + s->xadc_dfifo[i] = s->xadc_dfifo[i + 1]; + } + s->xadc_dfifo[s->xadc_dfifo_entries] = 0; + zynq_xadc_update_ints(s); + return rv; +} + +static void xadc_push_dfifo(ZynqXADCState *s, uint16_t regval) +{ + if (s->xadc_dfifo_entries < ZYNQ_XADC_FIFO_DEPTH) { + s->xadc_dfifo[s->xadc_dfifo_entries++] = s->xadc_read_reg_previous; + } + s->xadc_read_reg_previous = regval; + zynq_xadc_update_ints(s); +} + +static bool zynq_xadc_check_offset(hwaddr offset, bool rnw) +{ + switch (offset) { + case CFG: + case INT_MASK: + case INT_STS: + case MCTL: + return true; + case RDFIFO: + case MSTS: + return rnw; /* read only */ + case CMDFIFO: + return !rnw; /* write only */ + default: + return false; + } +} + +static uint64_t zynq_xadc_read(void *opaque, hwaddr offset, unsigned size) +{ + ZynqXADCState *s = opaque; + int reg = offset / 4; + uint32_t rv = 0; + + if (!zynq_xadc_check_offset(reg, true)) { + qemu_log_mask(LOG_GUEST_ERROR, "zynq_xadc: Invalid read access to " + "addr %" HWADDR_PRIx "\n", offset); + return 0; + } + + switch (reg) { + case CFG: + case INT_MASK: + case INT_STS: + case MCTL: + rv = s->regs[reg]; + break; + case MSTS: + rv = MSTS_CFIFOE; + rv |= s->xadc_dfifo_entries << MSTS_DFIFO_LVL_SHIFT; + if (!s->xadc_dfifo_entries) { + rv |= MSTS_DFIFOE; + } else if (s->xadc_dfifo_entries == ZYNQ_XADC_FIFO_DEPTH) { + rv |= MSTS_DFIFOF; + } + break; + case RDFIFO: + rv = xadc_pop_dfifo(s); + break; + } + return rv; +} + +static void zynq_xadc_write(void *opaque, hwaddr offset, uint64_t val, + unsigned size) +{ + ZynqXADCState *s = (ZynqXADCState *)opaque; + int reg = offset / 4; + int xadc_reg; + int xadc_cmd; + int xadc_data; + + if (!zynq_xadc_check_offset(reg, false)) { + qemu_log_mask(LOG_GUEST_ERROR, "zynq_xadc: Invalid write access " + "to addr %" HWADDR_PRIx "\n", offset); + return; + } + + switch (reg) { + case CFG: + s->regs[CFG] = val; + break; + case INT_STS: + s->regs[INT_STS] &= ~val; + break; + case INT_MASK: + s->regs[INT_MASK] = val & INT_ALL; + break; + case CMDFIFO: + xadc_cmd = extract32(val, 26, 4); + xadc_reg = extract32(val, 16, 10); + xadc_data = extract32(val, 0, 16); + + if (s->regs[MCTL] & MCTL_RESET) { + qemu_log_mask(LOG_GUEST_ERROR, "zynq_xadc: Sending command " + "while comm channel held in reset: %" PRIx32 "\n", + (uint32_t) val); + break; + } + + if (xadc_reg >= ZYNQ_XADC_NUM_ADC_REGS && xadc_cmd != CMD_NOP) { + qemu_log_mask(LOG_GUEST_ERROR, "read/write op to invalid xadc " + "reg 0x%x\n", xadc_reg); + break; + } + + switch (xadc_cmd) { + case CMD_READ: + xadc_push_dfifo(s, s->xadc_regs[xadc_reg]); + break; + case CMD_WRITE: + s->xadc_regs[xadc_reg] = xadc_data; + /* fallthrough */ + case CMD_NOP: + xadc_push_dfifo(s, 0); + break; + } + break; + case MCTL: + s->regs[MCTL] = val & 0x00fffeff; + break; + } + zynq_xadc_update_ints(s); +} + +static const MemoryRegionOps xadc_ops = { + .read = zynq_xadc_read, + .write = zynq_xadc_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void zynq_xadc_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + ZynqXADCState *s = ZYNQ_XADC(obj); + + memory_region_init_io(&s->iomem, obj, &xadc_ops, s, "zynq-xadc", + ZYNQ_XADC_MMIO_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->qemu_irq); +} + +static const VMStateDescription vmstate_zynq_xadc = { + .name = "zynq-xadc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, ZynqXADCState, ZYNQ_XADC_NUM_IO_REGS), + VMSTATE_UINT16_ARRAY(xadc_regs, ZynqXADCState, + ZYNQ_XADC_NUM_ADC_REGS), + VMSTATE_UINT16_ARRAY(xadc_dfifo, ZynqXADCState, + ZYNQ_XADC_FIFO_DEPTH), + VMSTATE_UINT16(xadc_read_reg_previous, ZynqXADCState), + VMSTATE_UINT16(xadc_dfifo_entries, ZynqXADCState), + VMSTATE_END_OF_LIST() + } +}; + +static void zynq_xadc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_zynq_xadc; + dc->reset = zynq_xadc_reset; +} + +static const TypeInfo zynq_xadc_info = { + .class_init = zynq_xadc_class_init, + .name = TYPE_ZYNQ_XADC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ZynqXADCState), + .instance_init = zynq_xadc_init, +}; + +static void zynq_xadc_register_types(void) +{ + type_register_static(&zynq_xadc_info); +} + +type_init(zynq_xadc_register_types) diff --git a/qemu/hw/misc/zynq_slcr.c b/qemu/hw/misc/zynq_slcr.c index 964f2532f..b1b7591ef 100644 --- a/qemu/hw/misc/zynq_slcr.c +++ b/qemu/hw/misc/zynq_slcr.c @@ -14,6 +14,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "qemu/timer.h" #include "hw/sysbus.h" @@ -393,12 +394,12 @@ static void zynq_slcr_write(void *opaque, hwaddr offset, return; } - if (!s->regs[LOCKSTA]) { - s->regs[offset / 4] = val; - } else { - DB_PRINT("SCLR registers are locked. Unlock them first\n"); + if (s->regs[LOCKSTA]) { + qemu_log_mask(LOG_GUEST_ERROR, + "SCLR registers are locked. Unlock them first\n"); return; } + s->regs[offset] = val; switch (offset) { case PSS_RST_CTRL: diff --git a/qemu/hw/moxie/moxiesim.c b/qemu/hw/moxie/moxiesim.c index 80bcc5b4f..3069834cf 100644 --- a/qemu/hw/moxie/moxiesim.c +++ b/qemu/hw/moxie/moxiesim.c @@ -24,6 +24,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "hw/hw.h" #include "hw/i386/pc.h" @@ -34,6 +38,7 @@ #include "hw/loader.h" #include "hw/char/serial.h" #include "exec/address-spaces.h" +#include "elf.h" #define PHYS_MEM_BASE 0x80000000 @@ -52,8 +57,8 @@ static void load_kernel(MoxieCPU *cpu, LoaderParams *loader_params) ram_addr_t initrd_offset; kernel_size = load_elf(loader_params->kernel_filename, NULL, NULL, - &entry, &kernel_low, &kernel_high, 1, - ELF_MACHINE, 0); + &entry, &kernel_low, &kernel_high, 1, EM_MOXIE, + 0, 0); if (kernel_size <= 0) { fprintf(stderr, "qemu: could not load kernel '%s'\n", @@ -123,11 +128,11 @@ static void moxiesim_init(MachineState *machine) qemu_register_reset(main_cpu_reset, cpu); /* Allocate RAM. */ - memory_region_init_ram(ram, NULL, "moxiesim.ram", ram_size, &error_abort); + memory_region_init_ram(ram, NULL, "moxiesim.ram", ram_size, &error_fatal); vmstate_register_ram_global(ram); memory_region_add_subregion(address_space_mem, ram_base, ram); - memory_region_init_ram(rom, NULL, "moxie.rom", 128*0x1000, &error_abort); + memory_region_init_ram(rom, NULL, "moxie.rom", 128*0x1000, &error_fatal); vmstate_register_ram_global(rom); memory_region_add_subregion(get_system_memory(), 0x1000, rom); @@ -146,16 +151,11 @@ static void moxiesim_init(MachineState *machine) } } -static QEMUMachine moxiesim_machine = { - .name = "moxiesim", - .desc = "Moxie simulator platform", - .init = moxiesim_init, - .is_default = 1, -}; - -static void moxie_machine_init(void) +static void moxiesim_machine_init(MachineClass *mc) { - qemu_register_machine(&moxiesim_machine); + mc->desc = "Moxie simulator platform"; + mc->init = moxiesim_init; + mc->is_default = 1; } -machine_init(moxie_machine_init) +DEFINE_MACHINE("moxiesim", moxiesim_machine_init) diff --git a/qemu/hw/net/Makefile.objs b/qemu/hw/net/Makefile.objs index 98801739e..64d044923 100644 --- a/qemu/hw/net/Makefile.objs +++ b/qemu/hw/net/Makefile.objs @@ -19,6 +19,7 @@ common-obj-$(CONFIG_XGMAC) += xgmac.o common-obj-$(CONFIG_MIPSNET) += mipsnet.o common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o common-obj-$(CONFIG_ALLWINNER_EMAC) += allwinner_emac.o +common-obj-$(CONFIG_IMX_FEC) += imx_fec.o common-obj-$(CONFIG_CADENCE) += cadence_gem.o common-obj-$(CONFIG_STELLARIS_ENET) += stellaris_enet.o diff --git a/qemu/hw/net/allwinner_emac.c b/qemu/hw/net/allwinner_emac.c index 0407dee6d..16d4b63ba 100644 --- a/qemu/hw/net/allwinner_emac.c +++ b/qemu/hw/net/allwinner_emac.c @@ -16,6 +16,7 @@ * GNU General Public License for more details. * */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "net/net.h" #include "qemu/fifo8.h" diff --git a/qemu/hw/net/cadence_gem.c b/qemu/hw/net/cadence_gem.c index 494a346cf..0346f3e33 100644 --- a/qemu/hw/net/cadence_gem.c +++ b/qemu/hw/net/cadence_gem.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include /* For crc32 */ #include "hw/net/cadence_gem.h" @@ -677,6 +678,10 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) } else { unsigned crc_val; + if (size > sizeof(rxbuf) - sizeof(crc_val)) { + size = sizeof(rxbuf) - sizeof(crc_val); + } + bytes_to_copy = size; /* The application wants the FCS field, which QEMU does not provide. * We must try and calculate one. */ @@ -862,6 +867,14 @@ static void gem_transmit(CadenceGEMState *s) break; } + if (tx_desc_get_length(desc) > sizeof(tx_packet) - (p - tx_packet)) { + DB_PRINT("TX descriptor @ 0x%x too large: size 0x%x space 0x%x\n", + (unsigned)packet_desc_addr, + (unsigned)tx_desc_get_length(desc), + sizeof(tx_packet) - (p - tx_packet)); + break; + } + /* Gather this fragment of the packet from "dma memory" to our contig. * buffer. */ @@ -951,7 +964,7 @@ static void gem_phy_reset(CadenceGEMState *s) s->phy_regs[PHY_REG_1000BTSTAT] = 0x7C00; s->phy_regs[PHY_REG_EXTSTAT] = 0x3000; s->phy_regs[PHY_REG_PHYSPCFC_CTL] = 0x0078; - s->phy_regs[PHY_REG_PHYSPCFC_ST] = 0xBC00; + s->phy_regs[PHY_REG_PHYSPCFC_ST] = 0x7C00; s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL] = 0x0C60; s->phy_regs[PHY_REG_LED] = 0x4100; s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL2] = 0x000A; @@ -964,6 +977,7 @@ static void gem_reset(DeviceState *d) { int i; CadenceGEMState *s = CADENCE_GEM(d); + const uint8_t *a; DB_PRINT("\n"); @@ -982,6 +996,11 @@ static void gem_reset(DeviceState *d) s->regs[GEM_DESCONF5] = 0x002f2145; s->regs[GEM_DESCONF6] = 0x00000200; + /* Set MAC address */ + a = &s->conf.macaddr.a[0]; + s->regs[GEM_SPADDR1LO] = a[0] | (a[1] << 8) | (a[2] << 16) | (a[3] << 24); + s->regs[GEM_SPADDR1HI] = a[4] | (a[5] << 8); + for (i = 0; i < 4; i++) { s->sar_active[i] = false; } diff --git a/qemu/hw/net/dp8393x.c b/qemu/hw/net/dp8393x.c index ab607e484..0fa652c39 100644 --- a/qemu/hw/net/dp8393x.c +++ b/qemu/hw/net/dp8393x.c @@ -17,9 +17,11 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/devices.h" #include "net/net.h" +#include "qapi/error.h" #include "qemu/timer.h" #include @@ -292,7 +294,7 @@ static void dp8393x_set_next_tick(dp8393xState *s) ticks = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0]; s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - delay = get_ticks_per_sec() * ticks / 5000000; + delay = NANOSECONDS_PER_SECOND * ticks / 5000000; timer_mod(s->watchdog, s->wt_last_update + delay); } diff --git a/qemu/hw/net/e1000.c b/qemu/hw/net/e1000.c index 09c9e9d53..8e79b550e 100644 --- a/qemu/hw/net/e1000.c +++ b/qemu/hw/net/e1000.c @@ -25,6 +25,7 @@ */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "net/net.h" @@ -37,24 +38,26 @@ #include "e1000_regs.h" +static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + #define E1000_DEBUG #ifdef E1000_DEBUG enum { - DEBUG_GENERAL, DEBUG_IO, DEBUG_MMIO, DEBUG_INTERRUPT, - DEBUG_RX, DEBUG_TX, DEBUG_MDIC, DEBUG_EEPROM, - DEBUG_UNKNOWN, DEBUG_TXSUM, DEBUG_TXERR, DEBUG_RXERR, + DEBUG_GENERAL, DEBUG_IO, DEBUG_MMIO, DEBUG_INTERRUPT, + DEBUG_RX, DEBUG_TX, DEBUG_MDIC, DEBUG_EEPROM, + DEBUG_UNKNOWN, DEBUG_TXSUM, DEBUG_TXERR, DEBUG_RXERR, DEBUG_RXFILTER, DEBUG_PHY, DEBUG_NOTYET, }; -#define DBGBIT(x) (1<compat_flags & E1000_FLAG_##x) + typedef struct E1000BaseClass { PCIDeviceClass parent_class; uint16_t phy_id2; @@ -155,20 +162,36 @@ typedef struct E1000BaseClass { #define E1000_DEVICE_GET_CLASS(obj) \ OBJECT_GET_CLASS(E1000BaseClass, (obj), TYPE_E1000_BASE) -#define defreg(x) x = (E1000_##x>>2) +#define defreg(x) x = (E1000_##x>>2) enum { - defreg(CTRL), defreg(EECD), defreg(EERD), defreg(GPRC), - defreg(GPTC), defreg(ICR), defreg(ICS), defreg(IMC), - defreg(IMS), defreg(LEDCTL), defreg(MANC), defreg(MDIC), - defreg(MPC), defreg(PBA), defreg(RCTL), defreg(RDBAH), - defreg(RDBAL), defreg(RDH), defreg(RDLEN), defreg(RDT), - defreg(STATUS), defreg(SWSM), defreg(TCTL), defreg(TDBAH), - defreg(TDBAL), defreg(TDH), defreg(TDLEN), defreg(TDT), - defreg(TORH), defreg(TORL), defreg(TOTH), defreg(TOTL), - defreg(TPR), defreg(TPT), defreg(TXDCTL), defreg(WUFC), - defreg(RA), defreg(MTA), defreg(CRCERRS),defreg(VFTA), - defreg(VET), defreg(RDTR), defreg(RADV), defreg(TADV), - defreg(ITR), + defreg(CTRL), defreg(EECD), defreg(EERD), defreg(GPRC), + defreg(GPTC), defreg(ICR), defreg(ICS), defreg(IMC), + defreg(IMS), defreg(LEDCTL), defreg(MANC), defreg(MDIC), + defreg(MPC), defreg(PBA), defreg(RCTL), defreg(RDBAH), + defreg(RDBAL), defreg(RDH), defreg(RDLEN), defreg(RDT), + defreg(STATUS), defreg(SWSM), defreg(TCTL), defreg(TDBAH), + defreg(TDBAL), defreg(TDH), defreg(TDLEN), defreg(TDT), + defreg(TORH), defreg(TORL), defreg(TOTH), defreg(TOTL), + defreg(TPR), defreg(TPT), defreg(TXDCTL), defreg(WUFC), + defreg(RA), defreg(MTA), defreg(CRCERRS), defreg(VFTA), + defreg(VET), defreg(RDTR), defreg(RADV), defreg(TADV), + defreg(ITR), defreg(FCRUC), defreg(TDFH), defreg(TDFT), + defreg(TDFHS), defreg(TDFTS), defreg(TDFPC), defreg(RDFH), + defreg(RDFT), defreg(RDFHS), defreg(RDFTS), defreg(RDFPC), + defreg(IPAV), defreg(WUC), defreg(WUS), defreg(AIT), + defreg(IP6AT), defreg(IP4AT), defreg(FFLT), defreg(FFMT), + defreg(FFVT), defreg(WUPM), defreg(PBM), defreg(SCC), + defreg(ECOL), defreg(MCC), defreg(LATECOL), defreg(COLC), + defreg(DC), defreg(TNCRS), defreg(SEC), defreg(CEXTERR), + defreg(RLEC), defreg(XONRXC), defreg(XONTXC), defreg(XOFFRXC), + defreg(XOFFTXC), defreg(RFC), defreg(RJC), defreg(RNBC), + defreg(TSCTFC), defreg(MGTPRC), defreg(MGTPDC), defreg(MGTPTC), + defreg(RUC), defreg(ROC), defreg(GORCL), defreg(GORCH), + defreg(GOTCL), defreg(GOTCH), defreg(BPRC), defreg(MPRC), + defreg(TSCTC), defreg(PRC64), defreg(PRC127), defreg(PRC255), + defreg(PRC511), defreg(PRC1023), defreg(PRC1522), defreg(PTC64), + defreg(PTC127), defreg(PTC255), defreg(PTC511), defreg(PTC1023), + defreg(PTC1522), defreg(MPTC), defreg(BPTC) }; static void @@ -193,8 +216,7 @@ e1000_link_up(E1000State *s) static bool have_autoneg(E1000State *s) { - return (s->compat_flags & E1000_FLAG_AUTONEG) && - (s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN); + return chkflag(AUTONEG) && (s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN); } static void @@ -226,18 +248,18 @@ enum { NPHYWRITEOPS = ARRAY_SIZE(phyreg_writeops) }; enum { PHY_R = 1, PHY_W = 2, PHY_RW = PHY_R | PHY_W }; static const char phy_regcap[0x20] = { - [PHY_STATUS] = PHY_R, [M88E1000_EXT_PHY_SPEC_CTRL] = PHY_RW, - [PHY_ID1] = PHY_R, [M88E1000_PHY_SPEC_CTRL] = PHY_RW, - [PHY_CTRL] = PHY_RW, [PHY_1000T_CTRL] = PHY_RW, - [PHY_LP_ABILITY] = PHY_R, [PHY_1000T_STATUS] = PHY_R, - [PHY_AUTONEG_ADV] = PHY_RW, [M88E1000_RX_ERR_CNTR] = PHY_R, - [PHY_ID2] = PHY_R, [M88E1000_PHY_SPEC_STATUS] = PHY_R, + [PHY_STATUS] = PHY_R, [M88E1000_EXT_PHY_SPEC_CTRL] = PHY_RW, + [PHY_ID1] = PHY_R, [M88E1000_PHY_SPEC_CTRL] = PHY_RW, + [PHY_CTRL] = PHY_RW, [PHY_1000T_CTRL] = PHY_RW, + [PHY_LP_ABILITY] = PHY_R, [PHY_1000T_STATUS] = PHY_R, + [PHY_AUTONEG_ADV] = PHY_RW, [M88E1000_RX_ERR_CNTR] = PHY_R, + [PHY_ID2] = PHY_R, [M88E1000_PHY_SPEC_STATUS] = PHY_R, [PHY_AUTONEG_EXP] = PHY_R, }; /* PHY_ID2 documented in 8254x_GBe_SDM.pdf, pp. 250 */ static const uint16_t phy_reg_init[] = { - [PHY_CTRL] = MII_CR_SPEED_SELECT_MSB | + [PHY_CTRL] = MII_CR_SPEED_SELECT_MSB | MII_CR_FULL_DUPLEX | MII_CR_AUTO_NEG_EN, @@ -264,15 +286,15 @@ static const uint16_t phy_reg_init[] = { }; static const uint32_t mac_reg_init[] = { - [PBA] = 0x00100030, - [LEDCTL] = 0x602, - [CTRL] = E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN0 | + [PBA] = 0x00100030, + [LEDCTL] = 0x602, + [CTRL] = E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN0 | E1000_CTRL_SPD_1000 | E1000_CTRL_SLU, - [STATUS] = 0x80000000 | E1000_STATUS_GIO_MASTER_ENABLE | + [STATUS] = 0x80000000 | E1000_STATUS_GIO_MASTER_ENABLE | E1000_STATUS_ASDV | E1000_STATUS_MTXCKOK | E1000_STATUS_SPEED_1000 | E1000_STATUS_FD | E1000_STATUS_LU, - [MANC] = E1000_MANC_EN_MNG2HOST | E1000_MANC_RCV_TCO_EN | + [MANC] = E1000_MANC_EN_MNG2HOST | E1000_MANC_RCV_TCO_EN | E1000_MANC_ARP_EN | E1000_MANC_0298_EN | E1000_MANC_RMCP_EN, }; @@ -319,7 +341,7 @@ set_interrupt_cause(E1000State *s, int index, uint32_t val) if (s->mit_timer_on) { return; } - if (s->compat_flags & E1000_FLAG_MIT) { + if (chkflag(MIT)) { /* Compute the next mitigation delay according to pending * interrupts and the current values of RADV (provided * RDTR!=0), TADV and ITR. @@ -335,6 +357,14 @@ set_interrupt_cause(E1000State *s, int index, uint32_t val) } mit_update_delay(&mit_delay, s->mac_reg[ITR]); + /* + * According to e1000 SPEC, the Ethernet controller guarantees + * a maximum observable interrupt rate of 7813 interrupts/sec. + * Thus if mit_delay < 500 then the delay should be set to the + * minimum delay possible which is 500. + */ + mit_delay = (mit_delay < 500) ? 500 : mit_delay; + if (mit_delay) { s->mit_timer_on = 1; timer_mod(s->mit_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + @@ -510,17 +540,19 @@ set_eecd(E1000State *s, int index, uint32_t val) s->eecd_state.old_eecd = val & (E1000_EECD_SK | E1000_EECD_CS | E1000_EECD_DI|E1000_EECD_FWE_MASK|E1000_EECD_REQ); - if (!(E1000_EECD_CS & val)) // CS inactive; nothing to do - return; - if (E1000_EECD_CS & (val ^ oldval)) { // CS rise edge; reset state - s->eecd_state.val_in = 0; - s->eecd_state.bitnum_in = 0; - s->eecd_state.bitnum_out = 0; - s->eecd_state.reading = 0; + if (!(E1000_EECD_CS & val)) { /* CS inactive; nothing to do */ + return; } - if (!(E1000_EECD_SK & (val ^ oldval))) // no clock edge + if (E1000_EECD_CS & (val ^ oldval)) { /* CS rise edge; reset state */ + s->eecd_state.val_in = 0; + s->eecd_state.bitnum_in = 0; + s->eecd_state.bitnum_out = 0; + s->eecd_state.reading = 0; + } + if (!(E1000_EECD_SK & (val ^ oldval))) { /* no clock edge */ return; - if (!(E1000_EECD_SK & val)) { // falling edge + } + if (!(E1000_EECD_SK & val)) { /* falling edge */ s->eecd_state.bitnum_out++; return; } @@ -565,6 +597,56 @@ putsum(uint8_t *data, uint32_t n, uint32_t sloc, uint32_t css, uint32_t cse) } } +static inline void +inc_reg_if_not_full(E1000State *s, int index) +{ + if (s->mac_reg[index] != 0xffffffff) { + s->mac_reg[index]++; + } +} + +static inline void +inc_tx_bcast_or_mcast_count(E1000State *s, const unsigned char *arr) +{ + if (!memcmp(arr, bcast, sizeof bcast)) { + inc_reg_if_not_full(s, BPTC); + } else if (arr[0] & 1) { + inc_reg_if_not_full(s, MPTC); + } +} + +static void +grow_8reg_if_not_full(E1000State *s, int index, int size) +{ + uint64_t sum = s->mac_reg[index] | (uint64_t)s->mac_reg[index+1] << 32; + + if (sum + size < sum) { + sum = ~0ULL; + } else { + sum += size; + } + s->mac_reg[index] = sum; + s->mac_reg[index+1] = sum >> 32; +} + +static void +increase_size_stats(E1000State *s, const int *size_regs, int size) +{ + if (size > 1023) { + inc_reg_if_not_full(s, size_regs[5]); + } else if (size > 511) { + inc_reg_if_not_full(s, size_regs[4]); + } else if (size > 255) { + inc_reg_if_not_full(s, size_regs[3]); + } else if (size > 127) { + inc_reg_if_not_full(s, size_regs[2]); + } else if (size > 64) { + inc_reg_if_not_full(s, size_regs[1]); + } else if (size == 64) { + inc_reg_if_not_full(s, size_regs[0]); + } +} + static inline int vlan_enabled(E1000State *s) { @@ -602,40 +684,49 @@ fcs_len(E1000State *s) static void e1000_send_packet(E1000State *s, const uint8_t *buf, int size) { + static const int PTCregs[6] = { PTC64, PTC127, PTC255, PTC511, + PTC1023, PTC1522 }; + NetClientState *nc = qemu_get_queue(s->nic); if (s->phy_reg[PHY_CTRL] & MII_CR_LOOPBACK) { nc->info->receive(nc, buf, size); } else { qemu_send_packet(nc, buf, size); } + inc_tx_bcast_or_mcast_count(s, buf); + increase_size_stats(s, PTCregs, size); } static void xmit_seg(E1000State *s) { uint16_t len, *sp; - unsigned int frames = s->tx.tso_frames, css, sofar, n; + unsigned int frames = s->tx.tso_frames, css, sofar; struct e1000_tx *tp = &s->tx; if (tp->tse && tp->cptse) { css = tp->ipcss; DBGOUT(TXSUM, "frames %d size %d ipcss %d\n", frames, tp->size, css); - if (tp->ip) { // IPv4 + if (tp->ip) { /* IPv4 */ stw_be_p(tp->data+css+2, tp->size - css); stw_be_p(tp->data+css+4, - be16_to_cpup((uint16_t *)(tp->data+css+4))+frames); - } else // IPv6 + be16_to_cpup((uint16_t *)(tp->data+css+4))+frames); + } else { /* IPv6 */ stw_be_p(tp->data+css+4, tp->size - css); + } css = tp->tucss; len = tp->size - css; DBGOUT(TXSUM, "tcp %d tucss %d len %d\n", tp->tcp, css, len); if (tp->tcp) { sofar = frames * tp->mss; stl_be_p(tp->data+css+4, ldl_be_p(tp->data+css+4)+sofar); /* seq */ - if (tp->paylen - sofar > tp->mss) - tp->data[css + 13] &= ~9; // PSH, FIN - } else // UDP + if (tp->paylen - sofar > tp->mss) { + tp->data[css + 13] &= ~9; /* PSH, FIN */ + } else if (frames) { + inc_reg_if_not_full(s, TSCTC); + } + } else /* UDP */ stw_be_p(tp->data+css+4, len); if (tp->sum_needed & E1000_TXD_POPTS_TXSM) { unsigned int phsum; @@ -657,13 +748,15 @@ xmit_seg(E1000State *s) memmove(tp->data, tp->data + 4, 8); memcpy(tp->data + 8, tp->vlan_header, 4); e1000_send_packet(s, tp->vlan, tp->size + 4); - } else + } else { e1000_send_packet(s, tp->data, tp->size); - s->mac_reg[TPT]++; - s->mac_reg[GPTC]++; - n = s->mac_reg[TOTL]; - if ((s->mac_reg[TOTL] += s->tx.size) < n) - s->mac_reg[TOTH]++; + } + + inc_reg_if_not_full(s, TPT); + grow_8reg_if_not_full(s, TOTL, s->tx.size); + s->mac_reg[GPTC] = s->mac_reg[TPT]; + s->mac_reg[GOTCL] = s->mac_reg[TOTL]; + s->mac_reg[GOTCH] = s->mac_reg[TOTH]; } static void @@ -679,7 +772,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) struct e1000_tx *tp = &s->tx; s->mit_ide |= (txd_lower & E1000_TXD_CMD_IDE); - if (dtype == E1000_TXD_CMD_DEXT) { // context descriptor + if (dtype == E1000_TXD_CMD_DEXT) { /* context descriptor */ op = le32_to_cpu(xp->cmd_and_length); tp->ipcss = xp->lower_setup.ip_fields.ipcss; tp->ipcso = xp->lower_setup.ip_fields.ipcso; @@ -694,7 +787,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) tp->tcp = (op & E1000_TXD_CMD_TCP) ? 1 : 0; tp->tse = (op & E1000_TXD_CMD_TSE) ? 1 : 0; tp->tso_frames = 0; - if (tp->tucso == 0) { // this is probably wrong + if (tp->tucso == 0) { /* this is probably wrong */ DBGOUT(TXSUM, "TCP/UDP: cso 0!\n"); tp->tucso = tp->tucss + (tp->tcp ? 16 : 6); } @@ -718,7 +811,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) stw_be_p(tp->vlan_header + 2, le16_to_cpu(dp->upper.fields.special)); } - + addr = le64_to_cpu(dp->buffer_addr); if (tp->tse && tp->cptse) { msh = tp->hdr_len + tp->mss; @@ -819,7 +912,8 @@ start_xmit(E1000State *s) * bogus values to TDT/TDLEN. * there's nothing too intelligent we could do about this. */ - if (s->mac_reg[TDH] == tdh_start) { + if (s->mac_reg[TDH] == tdh_start || + tdh_start >= s->mac_reg[TDLEN] / sizeof(desc)) { DBGOUT(TXERR, "TDH wraparound @%x, TDT %x, TDLEN %x\n", tdh_start, s->mac_reg[TDT], s->mac_reg[TDLEN]); break; @@ -831,9 +925,9 @@ start_xmit(E1000State *s) static int receive_filter(E1000State *s, const uint8_t *buf, int size) { - static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; static const int mta_shift[] = {4, 3, 2, 0}; uint32_t f, rctl = s->mac_reg[RCTL], ra[2], *rp; + int isbcast = !memcmp(buf, bcast, sizeof bcast), ismcast = (buf[0] & 1); if (is_vlan_packet(s, buf) && vlan_rx_filter_enabled(s)) { uint16_t vid = be16_to_cpup((uint16_t *)(buf + 14)); @@ -843,14 +937,19 @@ receive_filter(E1000State *s, const uint8_t *buf, int size) return 0; } - if (rctl & E1000_RCTL_UPE) // promiscuous + if (!isbcast && !ismcast && (rctl & E1000_RCTL_UPE)) { /* promiscuous ucast */ return 1; + } - if ((buf[0] & 1) && (rctl & E1000_RCTL_MPE)) // promiscuous mcast + if (ismcast && (rctl & E1000_RCTL_MPE)) { /* promiscuous mcast */ + inc_reg_if_not_full(s, MPRC); return 1; + } - if ((rctl & E1000_RCTL_BAM) && !memcmp(buf, bcast, sizeof bcast)) + if (isbcast && (rctl & E1000_RCTL_BAM)) { /* broadcast enabled */ + inc_reg_if_not_full(s, BPRC); return 1; + } for (rp = s->mac_reg + RA; rp < s->mac_reg + RA + 32; rp += 2) { if (!(rp[1] & E1000_RAH_AV)) @@ -870,8 +969,10 @@ receive_filter(E1000State *s, const uint8_t *buf, int size) f = mta_shift[(rctl >> E1000_RCTL_MO_SHIFT) & 3]; f = (((buf[5] << 8) | buf[4]) >> f) & 0xfff; - if (s->mac_reg[MTA + (f >> 5)] & (1 << (f & 0x1f))) + if (s->mac_reg[MTA + (f >> 5)] & (1 << (f & 0x1f))) { + inc_reg_if_not_full(s, MPRC); return 1; + } DBGOUT(RXFILTER, "dropping, inexact filter mismatch: %02x:%02x:%02x:%02x:%02x:%02x MO %d MTA[%d] %x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], @@ -960,6 +1061,8 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) size_t desc_offset; size_t desc_size; size_t total_size; + static const int PRCregs[6] = { PRC64, PRC127, PRC255, PRC511, + PRC1023, PRC1522 }; if (!(s->mac_reg[STATUS] & E1000_STATUS_LU)) { return -1; @@ -973,6 +1076,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) if (size < sizeof(min_buf)) { iov_to_buf(iov, iovcnt, 0, min_buf, size); memset(&min_buf[size], 0, sizeof(min_buf) - size); + inc_reg_if_not_full(s, RUC); min_iov.iov_base = filter_buf = min_buf; min_iov.iov_len = size = sizeof(min_buf); iovcnt = 1; @@ -988,6 +1092,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) (size > MAXIMUM_ETHERNET_VLAN_SIZE && !(s->mac_reg[RCTL] & E1000_RCTL_LPE))) && !(s->mac_reg[RCTL] & E1000_RCTL_SBP)) { + inc_reg_if_not_full(s, ROC); return size; } @@ -1065,7 +1170,8 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) if (++s->mac_reg[RDH] * sizeof(desc) >= s->mac_reg[RDLEN]) s->mac_reg[RDH] = 0; /* see comment in start_xmit; same here */ - if (s->mac_reg[RDH] == rdh_start) { + if (s->mac_reg[RDH] == rdh_start || + rdh_start >= s->mac_reg[RDLEN] / sizeof(desc)) { DBGOUT(RXERR, "RDH wraparound @%x, RDT %x, RDLEN %x\n", rdh_start, s->mac_reg[RDT], s->mac_reg[RDLEN]); set_ics(s, 0, E1000_ICS_RXO); @@ -1073,16 +1179,17 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) } } while (desc_offset < total_size); - s->mac_reg[GPRC]++; - s->mac_reg[TPR]++; + increase_size_stats(s, PRCregs, total_size); + inc_reg_if_not_full(s, TPR); + s->mac_reg[GPRC] = s->mac_reg[TPR]; /* TOR - Total Octets Received: * This register includes bytes received in a packet from the field through the field, inclusively. + * Always include FCS length (4) in size. */ - n = s->mac_reg[TORL] + size + /* Always include FCS length. */ 4; - if (n < s->mac_reg[TORL]) - s->mac_reg[TORH]++; - s->mac_reg[TORL] = n; + grow_8reg_if_not_full(s, TORL, size+4); + s->mac_reg[GORCL] = s->mac_reg[TORL]; + s->mac_reg[GORCH] = s->mac_reg[TORH]; n = E1000_ICS_RXT0; if ((rdt = s->mac_reg[RDT]) < s->mac_reg[RDH]) @@ -1113,6 +1220,30 @@ mac_readreg(E1000State *s, int index) return s->mac_reg[index]; } +static uint32_t +mac_low4_read(E1000State *s, int index) +{ + return s->mac_reg[index] & 0xf; +} + +static uint32_t +mac_low11_read(E1000State *s, int index) +{ + return s->mac_reg[index] & 0x7ff; +} + +static uint32_t +mac_low13_read(E1000State *s, int index) +{ + return s->mac_reg[index] & 0x1fff; +} + +static uint32_t +mac_low16_read(E1000State *s, int index) +{ + return s->mac_reg[index] & 0xffff; +} + static uint32_t mac_icr_read(E1000State *s, int index) { @@ -1206,46 +1337,144 @@ set_ims(E1000State *s, int index, uint32_t val) set_ics(s, 0, 0); } -#define getreg(x) [x] = mac_readreg +#define getreg(x) [x] = mac_readreg static uint32_t (*macreg_readops[])(E1000State *, int) = { - getreg(PBA), getreg(RCTL), getreg(TDH), getreg(TXDCTL), - getreg(WUFC), getreg(TDT), getreg(CTRL), getreg(LEDCTL), - getreg(MANC), getreg(MDIC), getreg(SWSM), getreg(STATUS), - getreg(TORL), getreg(TOTL), getreg(IMS), getreg(TCTL), - getreg(RDH), getreg(RDT), getreg(VET), getreg(ICS), - getreg(TDBAL), getreg(TDBAH), getreg(RDBAH), getreg(RDBAL), - getreg(TDLEN), getreg(RDLEN), getreg(RDTR), getreg(RADV), - getreg(TADV), getreg(ITR), - - [TOTH] = mac_read_clr8, [TORH] = mac_read_clr8, [GPRC] = mac_read_clr4, - [GPTC] = mac_read_clr4, [TPR] = mac_read_clr4, [TPT] = mac_read_clr4, - [ICR] = mac_icr_read, [EECD] = get_eecd, [EERD] = flash_eerd_read, - [CRCERRS ... MPC] = &mac_readreg, - [RA ... RA+31] = &mac_readreg, - [MTA ... MTA+127] = &mac_readreg, + getreg(PBA), getreg(RCTL), getreg(TDH), getreg(TXDCTL), + getreg(WUFC), getreg(TDT), getreg(CTRL), getreg(LEDCTL), + getreg(MANC), getreg(MDIC), getreg(SWSM), getreg(STATUS), + getreg(TORL), getreg(TOTL), getreg(IMS), getreg(TCTL), + getreg(RDH), getreg(RDT), getreg(VET), getreg(ICS), + getreg(TDBAL), getreg(TDBAH), getreg(RDBAH), getreg(RDBAL), + getreg(TDLEN), getreg(RDLEN), getreg(RDTR), getreg(RADV), + getreg(TADV), getreg(ITR), getreg(FCRUC), getreg(IPAV), + getreg(WUC), getreg(WUS), getreg(SCC), getreg(ECOL), + getreg(MCC), getreg(LATECOL), getreg(COLC), getreg(DC), + getreg(TNCRS), getreg(SEC), getreg(CEXTERR), getreg(RLEC), + getreg(XONRXC), getreg(XONTXC), getreg(XOFFRXC), getreg(XOFFTXC), + getreg(RFC), getreg(RJC), getreg(RNBC), getreg(TSCTFC), + getreg(MGTPRC), getreg(MGTPDC), getreg(MGTPTC), getreg(GORCL), + getreg(GOTCL), + + [TOTH] = mac_read_clr8, [TORH] = mac_read_clr8, + [GOTCH] = mac_read_clr8, [GORCH] = mac_read_clr8, + [PRC64] = mac_read_clr4, [PRC127] = mac_read_clr4, + [PRC255] = mac_read_clr4, [PRC511] = mac_read_clr4, + [PRC1023] = mac_read_clr4, [PRC1522] = mac_read_clr4, + [PTC64] = mac_read_clr4, [PTC127] = mac_read_clr4, + [PTC255] = mac_read_clr4, [PTC511] = mac_read_clr4, + [PTC1023] = mac_read_clr4, [PTC1522] = mac_read_clr4, + [GPRC] = mac_read_clr4, [GPTC] = mac_read_clr4, + [TPT] = mac_read_clr4, [TPR] = mac_read_clr4, + [RUC] = mac_read_clr4, [ROC] = mac_read_clr4, + [BPRC] = mac_read_clr4, [MPRC] = mac_read_clr4, + [TSCTC] = mac_read_clr4, [BPTC] = mac_read_clr4, + [MPTC] = mac_read_clr4, + [ICR] = mac_icr_read, [EECD] = get_eecd, + [EERD] = flash_eerd_read, + [RDFH] = mac_low13_read, [RDFT] = mac_low13_read, + [RDFHS] = mac_low13_read, [RDFTS] = mac_low13_read, + [RDFPC] = mac_low13_read, + [TDFH] = mac_low11_read, [TDFT] = mac_low11_read, + [TDFHS] = mac_low13_read, [TDFTS] = mac_low13_read, + [TDFPC] = mac_low13_read, + [AIT] = mac_low16_read, + + [CRCERRS ... MPC] = &mac_readreg, + [IP6AT ... IP6AT+3] = &mac_readreg, [IP4AT ... IP4AT+6] = &mac_readreg, + [FFLT ... FFLT+6] = &mac_low11_read, + [RA ... RA+31] = &mac_readreg, + [WUPM ... WUPM+31] = &mac_readreg, + [MTA ... MTA+127] = &mac_readreg, [VFTA ... VFTA+127] = &mac_readreg, + [FFMT ... FFMT+254] = &mac_low4_read, + [FFVT ... FFVT+254] = &mac_readreg, + [PBM ... PBM+16383] = &mac_readreg, }; enum { NREADOPS = ARRAY_SIZE(macreg_readops) }; -#define putreg(x) [x] = mac_writereg +#define putreg(x) [x] = mac_writereg static void (*macreg_writeops[])(E1000State *, int, uint32_t) = { - putreg(PBA), putreg(EERD), putreg(SWSM), putreg(WUFC), - putreg(TDBAL), putreg(TDBAH), putreg(TXDCTL), putreg(RDBAH), - putreg(RDBAL), putreg(LEDCTL), putreg(VET), - [TDLEN] = set_dlen, [RDLEN] = set_dlen, [TCTL] = set_tctl, - [TDT] = set_tctl, [MDIC] = set_mdic, [ICS] = set_ics, - [TDH] = set_16bit, [RDH] = set_16bit, [RDT] = set_rdt, - [IMC] = set_imc, [IMS] = set_ims, [ICR] = set_icr, - [EECD] = set_eecd, [RCTL] = set_rx_control, [CTRL] = set_ctrl, - [RDTR] = set_16bit, [RADV] = set_16bit, [TADV] = set_16bit, - [ITR] = set_16bit, - [RA ... RA+31] = &mac_writereg, - [MTA ... MTA+127] = &mac_writereg, + putreg(PBA), putreg(EERD), putreg(SWSM), putreg(WUFC), + putreg(TDBAL), putreg(TDBAH), putreg(TXDCTL), putreg(RDBAH), + putreg(RDBAL), putreg(LEDCTL), putreg(VET), putreg(FCRUC), + putreg(TDFH), putreg(TDFT), putreg(TDFHS), putreg(TDFTS), + putreg(TDFPC), putreg(RDFH), putreg(RDFT), putreg(RDFHS), + putreg(RDFTS), putreg(RDFPC), putreg(IPAV), putreg(WUC), + putreg(WUS), putreg(AIT), + + [TDLEN] = set_dlen, [RDLEN] = set_dlen, [TCTL] = set_tctl, + [TDT] = set_tctl, [MDIC] = set_mdic, [ICS] = set_ics, + [TDH] = set_16bit, [RDH] = set_16bit, [RDT] = set_rdt, + [IMC] = set_imc, [IMS] = set_ims, [ICR] = set_icr, + [EECD] = set_eecd, [RCTL] = set_rx_control, [CTRL] = set_ctrl, + [RDTR] = set_16bit, [RADV] = set_16bit, [TADV] = set_16bit, + [ITR] = set_16bit, + + [IP6AT ... IP6AT+3] = &mac_writereg, [IP4AT ... IP4AT+6] = &mac_writereg, + [FFLT ... FFLT+6] = &mac_writereg, + [RA ... RA+31] = &mac_writereg, + [WUPM ... WUPM+31] = &mac_writereg, + [MTA ... MTA+127] = &mac_writereg, [VFTA ... VFTA+127] = &mac_writereg, + [FFMT ... FFMT+254] = &mac_writereg, [FFVT ... FFVT+254] = &mac_writereg, + [PBM ... PBM+16383] = &mac_writereg, }; enum { NWRITEOPS = ARRAY_SIZE(macreg_writeops) }; +enum { MAC_ACCESS_PARTIAL = 1, MAC_ACCESS_FLAG_NEEDED = 2 }; + +#define markflag(x) ((E1000_FLAG_##x << 2) | MAC_ACCESS_FLAG_NEEDED) +/* In the array below the meaning of the bits is: [f|f|f|f|f|f|n|p] + * f - flag bits (up to 6 possible flags) + * n - flag needed + * p - partially implenented */ +static const uint8_t mac_reg_access[0x8000] = { + [RDTR] = markflag(MIT), [TADV] = markflag(MIT), + [RADV] = markflag(MIT), [ITR] = markflag(MIT), + + [IPAV] = markflag(MAC), [WUC] = markflag(MAC), + [IP6AT] = markflag(MAC), [IP4AT] = markflag(MAC), + [FFVT] = markflag(MAC), [WUPM] = markflag(MAC), + [ECOL] = markflag(MAC), [MCC] = markflag(MAC), + [DC] = markflag(MAC), [TNCRS] = markflag(MAC), + [RLEC] = markflag(MAC), [XONRXC] = markflag(MAC), + [XOFFTXC] = markflag(MAC), [RFC] = markflag(MAC), + [TSCTFC] = markflag(MAC), [MGTPRC] = markflag(MAC), + [WUS] = markflag(MAC), [AIT] = markflag(MAC), + [FFLT] = markflag(MAC), [FFMT] = markflag(MAC), + [SCC] = markflag(MAC), [FCRUC] = markflag(MAC), + [LATECOL] = markflag(MAC), [COLC] = markflag(MAC), + [SEC] = markflag(MAC), [CEXTERR] = markflag(MAC), + [XONTXC] = markflag(MAC), [XOFFRXC] = markflag(MAC), + [RJC] = markflag(MAC), [RNBC] = markflag(MAC), + [MGTPDC] = markflag(MAC), [MGTPTC] = markflag(MAC), + [RUC] = markflag(MAC), [ROC] = markflag(MAC), + [GORCL] = markflag(MAC), [GORCH] = markflag(MAC), + [GOTCL] = markflag(MAC), [GOTCH] = markflag(MAC), + [BPRC] = markflag(MAC), [MPRC] = markflag(MAC), + [TSCTC] = markflag(MAC), [PRC64] = markflag(MAC), + [PRC127] = markflag(MAC), [PRC255] = markflag(MAC), + [PRC511] = markflag(MAC), [PRC1023] = markflag(MAC), + [PRC1522] = markflag(MAC), [PTC64] = markflag(MAC), + [PTC127] = markflag(MAC), [PTC255] = markflag(MAC), + [PTC511] = markflag(MAC), [PTC1023] = markflag(MAC), + [PTC1522] = markflag(MAC), [MPTC] = markflag(MAC), + [BPTC] = markflag(MAC), + + [TDFH] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [TDFT] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [TDFHS] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [TDFTS] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [TDFPC] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [RDFH] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [RDFT] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [RDFHS] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [RDFTS] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [RDFPC] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [PBM] = markflag(MAC) | MAC_ACCESS_PARTIAL, +}; + static void e1000_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) @@ -1254,9 +1483,20 @@ e1000_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned int index = (addr & 0x1ffff) >> 2; if (index < NWRITEOPS && macreg_writeops[index]) { - macreg_writeops[index](s, index, val); + if (!(mac_reg_access[index] & MAC_ACCESS_FLAG_NEEDED) + || (s->compat_flags & (mac_reg_access[index] >> 2))) { + if (mac_reg_access[index] & MAC_ACCESS_PARTIAL) { + DBGOUT(GENERAL, "Writing to register at offset: 0x%08x. " + "It is not fully implemented.\n", index<<2); + } + macreg_writeops[index](s, index, val); + } else { /* "flag needed" bit is set, but the flag is not active */ + DBGOUT(MMIO, "MMIO write attempt to disabled reg. addr=0x%08x\n", + index<<2); + } } else if (index < NREADOPS && macreg_readops[index]) { - DBGOUT(MMIO, "e1000_mmio_writel RO %x: 0x%04"PRIx64"\n", index<<2, val); + DBGOUT(MMIO, "e1000_mmio_writel RO %x: 0x%04"PRIx64"\n", + index<<2, val); } else { DBGOUT(UNKNOWN, "MMIO unknown write addr=0x%08x,val=0x%08"PRIx64"\n", index<<2, val); @@ -1269,11 +1509,21 @@ e1000_mmio_read(void *opaque, hwaddr addr, unsigned size) E1000State *s = opaque; unsigned int index = (addr & 0x1ffff) >> 2; - if (index < NREADOPS && macreg_readops[index]) - { - return macreg_readops[index](s, index); + if (index < NREADOPS && macreg_readops[index]) { + if (!(mac_reg_access[index] & MAC_ACCESS_FLAG_NEEDED) + || (s->compat_flags & (mac_reg_access[index] >> 2))) { + if (mac_reg_access[index] & MAC_ACCESS_PARTIAL) { + DBGOUT(GENERAL, "Reading register at offset: 0x%08x. " + "It is not fully implemented.\n", index<<2); + } + return macreg_readops[index](s, index); + } else { /* "flag needed" bit is set, but the flag is not active */ + DBGOUT(MMIO, "MMIO read attempt of disabled reg. addr=0x%08x\n", + index<<2); + } + } else { + DBGOUT(UNKNOWN, "MMIO unknown read addr=0x%08x\n", index<<2); } - DBGOUT(UNKNOWN, "MMIO unknown read addr=0x%08x\n", index<<2); return 0; } @@ -1340,7 +1590,7 @@ static int e1000_post_load(void *opaque, int version_id) E1000State *s = opaque; NetClientState *nc = qemu_get_queue(s->nic); - if (!(s->compat_flags & E1000_FLAG_MIT)) { + if (!chkflag(MIT)) { s->mac_reg[ITR] = s->mac_reg[RDTR] = s->mac_reg[RADV] = s->mac_reg[TADV] = 0; s->mit_irq_level = false; @@ -1367,7 +1617,14 @@ static bool e1000_mit_state_needed(void *opaque) { E1000State *s = opaque; - return s->compat_flags & E1000_FLAG_MIT; + return chkflag(MIT); +} + +static bool e1000_full_mac_needed(void *opaque) +{ + E1000State *s = opaque; + + return chkflag(MAC); } static const VMStateDescription vmstate_e1000_mit_state = { @@ -1385,6 +1642,17 @@ static const VMStateDescription vmstate_e1000_mit_state = { } }; +static const VMStateDescription vmstate_e1000_full_mac_state = { + .name = "e1000/full_mac_state", + .version_id = 1, + .minimum_version_id = 1, + .needed = e1000_full_mac_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(mac_reg, E1000State, 0x8000), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_e1000 = { .name = "e1000", .version_id = 2, @@ -1464,6 +1732,7 @@ static const VMStateDescription vmstate_e1000 = { }, .subsections = (const VMStateDescription*[]) { &vmstate_e1000_mit_state, + &vmstate_e1000_full_mac_state, NULL } }; @@ -1596,6 +1865,8 @@ static Property e1000_properties[] = { compat_flags, E1000_FLAG_AUTONEG_BIT, true), DEFINE_PROP_BIT("mitigation", E1000State, compat_flags, E1000_FLAG_MIT_BIT, true), + DEFINE_PROP_BIT("extra_mac_registers", E1000State, + compat_flags, E1000_FLAG_MAC_BIT, true), DEFINE_PROP_END_OF_LIST(), }; @@ -1647,7 +1918,7 @@ static const TypeInfo e1000_base_info = { static const E1000Info e1000_devices[] = { { - .name = "e1000-82540em", + .name = "e1000", .device_id = E1000_DEV_ID_82540EM, .revision = 0x03, .phy_id2 = E1000_PHY_ID2_8254xx_DEFAULT, @@ -1666,11 +1937,6 @@ static const E1000Info e1000_devices[] = { }, }; -static const TypeInfo e1000_default_info = { - .name = "e1000", - .parent = "e1000-82540em", -}; - static void e1000_register_types(void) { int i; @@ -1688,7 +1954,6 @@ static void e1000_register_types(void) type_register(&type_info); } - type_register_static(&e1000_default_info); } type_init(e1000_register_types) diff --git a/qemu/hw/net/e1000_regs.h b/qemu/hw/net/e1000_regs.h index 60b96aaf1..1c40244ab 100644 --- a/qemu/hw/net/e1000_regs.h +++ b/qemu/hw/net/e1000_regs.h @@ -158,7 +158,8 @@ #define E1000_PHY_CTRL 0x00F10 /* PHY Control Register in CSR */ #define FEXTNVM_SW_CONFIG 0x0001 #define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */ -#define E1000_PBS 0x01008 /* Packet Buffer Size */ +#define E1000_PBM 0x10000 /* Packet Buffer Memory - RW */ +#define E1000_PBS 0x01008 /* Packet Buffer Size - RW */ #define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */ #define E1000_FLASH_UPDATES 1000 #define E1000_EEARBC 0x01024 /* EEPROM Auto Read Bus Control */ @@ -191,6 +192,11 @@ #define E1000_RAID 0x02C08 /* Receive Ack Interrupt Delay - RW */ #define E1000_TXDMAC 0x03000 /* TX DMA Control - RW */ #define E1000_KABGTXD 0x03004 /* AFE Band Gap Transmit Ref Data */ +#define E1000_RDFH 0x02410 /* Receive Data FIFO Head Register - RW */ +#define E1000_RDFT 0x02418 /* Receive Data FIFO Tail Register - RW */ +#define E1000_RDFHS 0x02420 /* Receive Data FIFO Head Saved Register - RW */ +#define E1000_RDFTS 0x02428 /* Receive Data FIFO Tail Saved Register - RW */ +#define E1000_RDFPC 0x02430 /* Receive Data FIFO Packet Count - RW */ #define E1000_TDFH 0x03410 /* TX Data FIFO Head - RW */ #define E1000_TDFT 0x03418 /* TX Data FIFO Tail - RW */ #define E1000_TDFHS 0x03420 /* TX Data FIFO Head Saved - RW */ diff --git a/qemu/hw/net/eepro100.c b/qemu/hw/net/eepro100.c index 60333b7fc..9b4b9b59d 100644 --- a/qemu/hw/net/eepro100.c +++ b/qemu/hw/net/eepro100.c @@ -40,7 +40,7 @@ * * Wake-on-LAN is not implemented. */ -#include /* offsetof */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "net/net.h" @@ -774,6 +774,11 @@ static void tx_command(EEPRO100State *s) #if 0 uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6); #endif + if (tx_buffer_size == 0) { + /* Prevent an endless loop. */ + logout("loop in %s:%u\n", __FILE__, __LINE__); + break; + } tbd_address += 8; TRACE(RXTX, logout ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n", @@ -855,6 +860,10 @@ static void set_multicast_list(EEPRO100State *s) static void action_command(EEPRO100State *s) { + /* The loop below won't stop if it gets special handcrafted data. + Therefore we limit the number of iterations. */ + unsigned max_loop_count = 16; + for (;;) { bool bit_el; bool bit_s; @@ -870,6 +879,13 @@ static void action_command(EEPRO100State *s) #if 0 bool bit_sf = ((s->tx.command & COMMAND_SF) != 0); #endif + + if (max_loop_count-- == 0) { + /* Prevent an endless loop. */ + logout("loop in %s:%u\n", __FILE__, __LINE__); + break; + } + s->cu_offset = s->tx.link; TRACE(OTHER, logout("val=(cu start), status=0x%04x, command=0x%04x, link=0x%08x\n", diff --git a/qemu/hw/net/etraxfs_eth.c b/qemu/hw/net/etraxfs_eth.c index d6002750f..05495ec40 100644 --- a/qemu/hw/net/etraxfs_eth.c +++ b/qemu/hw/net/etraxfs_eth.c @@ -22,10 +22,11 @@ * THE SOFTWARE. */ -#include +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "net/net.h" #include "hw/cris/etraxfs.h" +#include "qemu/error-report.h" #define D(x) @@ -589,7 +590,8 @@ static int fs_eth_init(SysBusDevice *sbd) ETRAXFSEthState *s = ETRAX_FS_ETH(dev); if (!s->dma_out || !s->dma_in) { - hw_error("Unconnected ETRAX-FS Ethernet MAC.\n"); + error_report("Unconnected ETRAX-FS Ethernet MAC"); + return -1; } s->dma_out->client.push = eth_tx_push; diff --git a/qemu/hw/net/fsl_etsec/etsec.c b/qemu/hw/net/fsl_etsec/etsec.c index 0f5cf4477..1e35f7f8c 100644 --- a/qemu/hw/net/fsl_etsec/etsec.c +++ b/qemu/hw/net/fsl_etsec/etsec.c @@ -26,6 +26,7 @@ * This implementation doesn't include ring priority, TCP/IP Off-Load, QoS. */ +#include "qemu/osdep.h" #include "sysemu/sysemu.h" #include "hw/sysbus.h" #include "trace.h" @@ -353,7 +354,7 @@ static ssize_t etsec_receive(NetClientState *nc, etsec->need_flush = false; ret = etsec_rx_ring_write(etsec, buf, size); if (ret == 0) { - /* The packet will be queued, let's flush it when buffer is avilable + /* The packet will be queued, let's flush it when buffer is available * again. */ etsec->need_flush = true; } diff --git a/qemu/hw/net/fsl_etsec/miim.c b/qemu/hw/net/fsl_etsec/miim.c index 1931b74e6..6bba01c82 100644 --- a/qemu/hw/net/fsl_etsec/miim.c +++ b/qemu/hw/net/fsl_etsec/miim.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "etsec.h" #include "registers.h" diff --git a/qemu/hw/net/fsl_etsec/registers.c b/qemu/hw/net/fsl_etsec/registers.c index a7bbfa113..46ce7a84b 100644 --- a/qemu/hw/net/fsl_etsec/registers.c +++ b/qemu/hw/net/fsl_etsec/registers.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "registers.h" const eTSEC_Register_Definition eTSEC_registers_def[] = { diff --git a/qemu/hw/net/fsl_etsec/registers.h b/qemu/hw/net/fsl_etsec/registers.h index 7ad768647..6fb96842b 100644 --- a/qemu/hw/net/fsl_etsec/registers.h +++ b/qemu/hw/net/fsl_etsec/registers.h @@ -24,7 +24,6 @@ #ifndef _ETSEC_REGISTERS_H_ #define _ETSEC_REGISTERS_H_ -#include enum eTSEC_Register_Access_Type { ACC_RW = 1, /* Read/Write */ diff --git a/qemu/hw/net/fsl_etsec/rings.c b/qemu/hw/net/fsl_etsec/rings.c index 68e7b6d16..ed1de7da9 100644 --- a/qemu/hw/net/fsl_etsec/rings.c +++ b/qemu/hw/net/fsl_etsec/rings.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "net/checksum.h" #include "etsec.h" @@ -464,9 +465,7 @@ static void rx_init_frame(eTSEC *etsec, const uint8_t *buf, size_t size) etsec->rx_fcb_size = 0; } - if (etsec->rx_buffer != NULL) { - g_free(etsec->rx_buffer); - } + g_free(etsec->rx_buffer); /* Do not copy the frame for now */ etsec->rx_buffer = (uint8_t *)buf; diff --git a/qemu/hw/net/imx_fec.c b/qemu/hw/net/imx_fec.c new file mode 100644 index 000000000..e60e3380e --- /dev/null +++ b/qemu/hw/net/imx_fec.c @@ -0,0 +1,711 @@ +/* + * i.MX Fast Ethernet Controller emulation. + * + * Copyright (c) 2013 Jean-Christophe Dubois. + * + * Based on Coldfire Fast Ethernet Controller emulation. + * + * Copyright (c) 2007 CodeSourcery. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "hw/net/imx_fec.h" +#include "sysemu/dma.h" + +/* For crc32 */ +#include + +#ifndef DEBUG_IMX_FEC +#define DEBUG_IMX_FEC 0 +#endif + +#define FEC_PRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_FEC) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_FEC, \ + __func__, ##args); \ + } \ + } while (0) + +#ifndef DEBUG_IMX_PHY +#define DEBUG_IMX_PHY 0 +#endif + +#define PHY_PRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_PHY) { \ + fprintf(stderr, "[%s.phy]%s: " fmt , TYPE_IMX_FEC, \ + __func__, ##args); \ + } \ + } while (0) + +static const VMStateDescription vmstate_imx_fec = { + .name = TYPE_IMX_FEC, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(irq_state, IMXFECState), + VMSTATE_UINT32(eir, IMXFECState), + VMSTATE_UINT32(eimr, IMXFECState), + VMSTATE_UINT32(rx_enabled, IMXFECState), + VMSTATE_UINT32(rx_descriptor, IMXFECState), + VMSTATE_UINT32(tx_descriptor, IMXFECState), + VMSTATE_UINT32(ecr, IMXFECState), + VMSTATE_UINT32(mmfr, IMXFECState), + VMSTATE_UINT32(mscr, IMXFECState), + VMSTATE_UINT32(mibc, IMXFECState), + VMSTATE_UINT32(rcr, IMXFECState), + VMSTATE_UINT32(tcr, IMXFECState), + VMSTATE_UINT32(tfwr, IMXFECState), + VMSTATE_UINT32(frsr, IMXFECState), + VMSTATE_UINT32(erdsr, IMXFECState), + VMSTATE_UINT32(etdsr, IMXFECState), + VMSTATE_UINT32(emrbr, IMXFECState), + VMSTATE_UINT32(miigsk_cfgr, IMXFECState), + VMSTATE_UINT32(miigsk_enr, IMXFECState), + + VMSTATE_UINT32(phy_status, IMXFECState), + VMSTATE_UINT32(phy_control, IMXFECState), + VMSTATE_UINT32(phy_advertise, IMXFECState), + VMSTATE_UINT32(phy_int, IMXFECState), + VMSTATE_UINT32(phy_int_mask, IMXFECState), + VMSTATE_END_OF_LIST() + } +}; + +#define PHY_INT_ENERGYON (1 << 7) +#define PHY_INT_AUTONEG_COMPLETE (1 << 6) +#define PHY_INT_FAULT (1 << 5) +#define PHY_INT_DOWN (1 << 4) +#define PHY_INT_AUTONEG_LP (1 << 3) +#define PHY_INT_PARFAULT (1 << 2) +#define PHY_INT_AUTONEG_PAGE (1 << 1) + +static void imx_fec_update(IMXFECState *s); + +/* + * The MII phy could raise a GPIO to the processor which in turn + * could be handled as an interrpt by the OS. + * For now we don't handle any GPIO/interrupt line, so the OS will + * have to poll for the PHY status. + */ +static void phy_update_irq(IMXFECState *s) +{ + imx_fec_update(s); +} + +static void phy_update_link(IMXFECState *s) +{ + /* Autonegotiation status mirrors link status. */ + if (qemu_get_queue(s->nic)->link_down) { + PHY_PRINTF("link is down\n"); + s->phy_status &= ~0x0024; + s->phy_int |= PHY_INT_DOWN; + } else { + PHY_PRINTF("link is up\n"); + s->phy_status |= 0x0024; + s->phy_int |= PHY_INT_ENERGYON; + s->phy_int |= PHY_INT_AUTONEG_COMPLETE; + } + phy_update_irq(s); +} + +static void imx_fec_set_link(NetClientState *nc) +{ + phy_update_link(IMX_FEC(qemu_get_nic_opaque(nc))); +} + +static void phy_reset(IMXFECState *s) +{ + s->phy_status = 0x7809; + s->phy_control = 0x3000; + s->phy_advertise = 0x01e1; + s->phy_int_mask = 0; + s->phy_int = 0; + phy_update_link(s); +} + +static uint32_t do_phy_read(IMXFECState *s, int reg) +{ + uint32_t val; + + if (reg > 31) { + /* we only advertise one phy */ + return 0; + } + + switch (reg) { + case 0: /* Basic Control */ + val = s->phy_control; + break; + case 1: /* Basic Status */ + val = s->phy_status; + break; + case 2: /* ID1 */ + val = 0x0007; + break; + case 3: /* ID2 */ + val = 0xc0d1; + break; + case 4: /* Auto-neg advertisement */ + val = s->phy_advertise; + break; + case 5: /* Auto-neg Link Partner Ability */ + val = 0x0f71; + break; + case 6: /* Auto-neg Expansion */ + val = 1; + break; + case 29: /* Interrupt source. */ + val = s->phy_int; + s->phy_int = 0; + phy_update_irq(s); + break; + case 30: /* Interrupt mask */ + val = s->phy_int_mask; + break; + case 17: + case 18: + case 27: + case 31: + qemu_log_mask(LOG_UNIMP, "[%s.phy]%s: reg %d not implemented\n", + TYPE_IMX_FEC, __func__, reg); + val = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n", + TYPE_IMX_FEC, __func__, reg); + val = 0; + break; + } + + PHY_PRINTF("read 0x%04x @ %d\n", val, reg); + + return val; +} + +static void do_phy_write(IMXFECState *s, int reg, uint32_t val) +{ + PHY_PRINTF("write 0x%04x @ %d\n", val, reg); + + if (reg > 31) { + /* we only advertise one phy */ + return; + } + + switch (reg) { + case 0: /* Basic Control */ + if (val & 0x8000) { + phy_reset(s); + } else { + s->phy_control = val & 0x7980; + /* Complete autonegotiation immediately. */ + if (val & 0x1000) { + s->phy_status |= 0x0020; + } + } + break; + case 4: /* Auto-neg advertisement */ + s->phy_advertise = (val & 0x2d7f) | 0x80; + break; + case 30: /* Interrupt mask */ + s->phy_int_mask = val & 0xff; + phy_update_irq(s); + break; + case 17: + case 18: + case 27: + case 31: + qemu_log_mask(LOG_UNIMP, "[%s.phy)%s: reg %d not implemented\n", + TYPE_IMX_FEC, __func__, reg); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n", + TYPE_IMX_FEC, __func__, reg); + break; + } +} + +static void imx_fec_read_bd(IMXFECBufDesc *bd, dma_addr_t addr) +{ + dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd)); +} + +static void imx_fec_write_bd(IMXFECBufDesc *bd, dma_addr_t addr) +{ + dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd)); +} + +static void imx_fec_update(IMXFECState *s) +{ + uint32_t active; + uint32_t changed; + + active = s->eir & s->eimr; + changed = active ^ s->irq_state; + if (changed) { + qemu_set_irq(s->irq, active); + } + s->irq_state = active; +} + +static void imx_fec_do_tx(IMXFECState *s) +{ + int frame_size = 0; + uint8_t frame[FEC_MAX_FRAME_SIZE]; + uint8_t *ptr = frame; + uint32_t addr = s->tx_descriptor; + + while (1) { + IMXFECBufDesc bd; + int len; + + imx_fec_read_bd(&bd, addr); + FEC_PRINTF("tx_bd %x flags %04x len %d data %08x\n", + addr, bd.flags, bd.length, bd.data); + if ((bd.flags & FEC_BD_R) == 0) { + /* Run out of descriptors to transmit. */ + break; + } + len = bd.length; + if (frame_size + len > FEC_MAX_FRAME_SIZE) { + len = FEC_MAX_FRAME_SIZE - frame_size; + s->eir |= FEC_INT_BABT; + } + dma_memory_read(&address_space_memory, bd.data, ptr, len); + ptr += len; + frame_size += len; + if (bd.flags & FEC_BD_L) { + /* Last buffer in frame. */ + qemu_send_packet(qemu_get_queue(s->nic), frame, len); + ptr = frame; + frame_size = 0; + s->eir |= FEC_INT_TXF; + } + s->eir |= FEC_INT_TXB; + bd.flags &= ~FEC_BD_R; + /* Write back the modified descriptor. */ + imx_fec_write_bd(&bd, addr); + /* Advance to the next descriptor. */ + if ((bd.flags & FEC_BD_W) != 0) { + addr = s->etdsr; + } else { + addr += 8; + } + } + + s->tx_descriptor = addr; + + imx_fec_update(s); +} + +static void imx_fec_enable_rx(IMXFECState *s) +{ + IMXFECBufDesc bd; + uint32_t tmp; + + imx_fec_read_bd(&bd, s->rx_descriptor); + + tmp = ((bd.flags & FEC_BD_E) != 0); + + if (!tmp) { + FEC_PRINTF("RX buffer full\n"); + } else if (!s->rx_enabled) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } + + s->rx_enabled = tmp; +} + +static void imx_fec_reset(DeviceState *d) +{ + IMXFECState *s = IMX_FEC(d); + + /* Reset the FEC */ + s->eir = 0; + s->eimr = 0; + s->rx_enabled = 0; + s->ecr = 0; + s->mscr = 0; + s->mibc = 0xc0000000; + s->rcr = 0x05ee0001; + s->tcr = 0; + s->tfwr = 0; + s->frsr = 0x500; + s->miigsk_cfgr = 0; + s->miigsk_enr = 0x6; + + /* We also reset the PHY */ + phy_reset(s); +} + +static uint64_t imx_fec_read(void *opaque, hwaddr addr, unsigned size) +{ + IMXFECState *s = IMX_FEC(opaque); + + FEC_PRINTF("reading from @ 0x%" HWADDR_PRIx "\n", addr); + + switch (addr & 0x3ff) { + case 0x004: + return s->eir; + case 0x008: + return s->eimr; + case 0x010: + return s->rx_enabled ? (1 << 24) : 0; /* RDAR */ + case 0x014: + return 0; /* TDAR */ + case 0x024: + return s->ecr; + case 0x040: + return s->mmfr; + case 0x044: + return s->mscr; + case 0x064: + return s->mibc; /* MIBC */ + case 0x084: + return s->rcr; + case 0x0c4: + return s->tcr; + case 0x0e4: /* PALR */ + return (s->conf.macaddr.a[0] << 24) + | (s->conf.macaddr.a[1] << 16) + | (s->conf.macaddr.a[2] << 8) + | s->conf.macaddr.a[3]; + break; + case 0x0e8: /* PAUR */ + return (s->conf.macaddr.a[4] << 24) + | (s->conf.macaddr.a[5] << 16) + | 0x8808; + case 0x0ec: + return 0x10000; /* OPD */ + case 0x118: + return 0; + case 0x11c: + return 0; + case 0x120: + return 0; + case 0x124: + return 0; + case 0x144: + return s->tfwr; + case 0x14c: + return 0x600; + case 0x150: + return s->frsr; + case 0x180: + return s->erdsr; + case 0x184: + return s->etdsr; + case 0x188: + return s->emrbr; + case 0x300: + return s->miigsk_cfgr; + case 0x308: + return s->miigsk_enr; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_FEC, __func__, addr); + return 0; + } +} + +static void imx_fec_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + IMXFECState *s = IMX_FEC(opaque); + + FEC_PRINTF("writing 0x%08x @ 0x%" HWADDR_PRIx "\n", (int)value, addr); + + switch (addr & 0x3ff) { + case 0x004: /* EIR */ + s->eir &= ~value; + break; + case 0x008: /* EIMR */ + s->eimr = value; + break; + case 0x010: /* RDAR */ + if ((s->ecr & FEC_EN) && !s->rx_enabled) { + imx_fec_enable_rx(s); + } + break; + case 0x014: /* TDAR */ + if (s->ecr & FEC_EN) { + imx_fec_do_tx(s); + } + break; + case 0x024: /* ECR */ + s->ecr = value; + if (value & FEC_RESET) { + imx_fec_reset(DEVICE(s)); + } + if ((s->ecr & FEC_EN) == 0) { + s->rx_enabled = 0; + } + break; + case 0x040: /* MMFR */ + /* store the value */ + s->mmfr = value; + if (extract32(value, 28, 1)) { + do_phy_write(s, extract32(value, 18, 9), extract32(value, 0, 16)); + } else { + s->mmfr = do_phy_read(s, extract32(value, 18, 9)); + } + /* raise the interrupt as the PHY operation is done */ + s->eir |= FEC_INT_MII; + break; + case 0x044: /* MSCR */ + s->mscr = value & 0xfe; + break; + case 0x064: /* MIBC */ + /* TODO: Implement MIB. */ + s->mibc = (value & 0x80000000) ? 0xc0000000 : 0; + break; + case 0x084: /* RCR */ + s->rcr = value & 0x07ff003f; + /* TODO: Implement LOOP mode. */ + break; + case 0x0c4: /* TCR */ + /* We transmit immediately, so raise GRA immediately. */ + s->tcr = value; + if (value & 1) { + s->eir |= FEC_INT_GRA; + } + break; + case 0x0e4: /* PALR */ + s->conf.macaddr.a[0] = value >> 24; + s->conf.macaddr.a[1] = value >> 16; + s->conf.macaddr.a[2] = value >> 8; + s->conf.macaddr.a[3] = value; + break; + case 0x0e8: /* PAUR */ + s->conf.macaddr.a[4] = value >> 24; + s->conf.macaddr.a[5] = value >> 16; + break; + case 0x0ec: /* OPDR */ + break; + case 0x118: /* IAUR */ + case 0x11c: /* IALR */ + case 0x120: /* GAUR */ + case 0x124: /* GALR */ + /* TODO: implement MAC hash filtering. */ + break; + case 0x144: /* TFWR */ + s->tfwr = value & 3; + break; + case 0x14c: /* FRBR */ + /* FRBR writes ignored. */ + break; + case 0x150: /* FRSR */ + s->frsr = (value & 0x3fc) | 0x400; + break; + case 0x180: /* ERDSR */ + s->erdsr = value & ~3; + s->rx_descriptor = s->erdsr; + break; + case 0x184: /* ETDSR */ + s->etdsr = value & ~3; + s->tx_descriptor = s->etdsr; + break; + case 0x188: /* EMRBR */ + s->emrbr = value & 0x7f0; + break; + case 0x300: /* MIIGSK_CFGR */ + s->miigsk_cfgr = value & 0x53; + break; + case 0x308: /* MIIGSK_ENR */ + s->miigsk_enr = (value & 0x2) ? 0x6 : 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_FEC, __func__, addr); + break; + } + + imx_fec_update(s); +} + +static int imx_fec_can_receive(NetClientState *nc) +{ + IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); + + return s->rx_enabled; +} + +static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf, + size_t len) +{ + IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); + IMXFECBufDesc bd; + uint32_t flags = 0; + uint32_t addr; + uint32_t crc; + uint32_t buf_addr; + uint8_t *crc_ptr; + unsigned int buf_len; + size_t size = len; + + FEC_PRINTF("len %d\n", (int)size); + + if (!s->rx_enabled) { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Unexpected packet\n", + TYPE_IMX_FEC, __func__); + return 0; + } + + /* 4 bytes for the CRC. */ + size += 4; + crc = cpu_to_be32(crc32(~0, buf, size)); + crc_ptr = (uint8_t *) &crc; + + /* Huge frames are truncted. */ + if (size > FEC_MAX_FRAME_SIZE) { + size = FEC_MAX_FRAME_SIZE; + flags |= FEC_BD_TR | FEC_BD_LG; + } + + /* Frames larger than the user limit just set error flags. */ + if (size > (s->rcr >> 16)) { + flags |= FEC_BD_LG; + } + + addr = s->rx_descriptor; + while (size > 0) { + imx_fec_read_bd(&bd, addr); + if ((bd.flags & FEC_BD_E) == 0) { + /* No descriptors available. Bail out. */ + /* + * FIXME: This is wrong. We should probably either + * save the remainder for when more RX buffers are + * available, or flag an error. + */ + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Lost end of frame\n", + TYPE_IMX_FEC, __func__); + break; + } + buf_len = (size <= s->emrbr) ? size : s->emrbr; + bd.length = buf_len; + size -= buf_len; + + FEC_PRINTF("rx_bd 0x%x length %d\n", addr, bd.length); + + /* The last 4 bytes are the CRC. */ + if (size < 4) { + buf_len += size - 4; + } + buf_addr = bd.data; + dma_memory_write(&address_space_memory, buf_addr, buf, buf_len); + buf += buf_len; + if (size < 4) { + dma_memory_write(&address_space_memory, buf_addr + buf_len, + crc_ptr, 4 - size); + crc_ptr += 4 - size; + } + bd.flags &= ~FEC_BD_E; + if (size == 0) { + /* Last buffer in frame. */ + bd.flags |= flags | FEC_BD_L; + FEC_PRINTF("rx frame flags %04x\n", bd.flags); + s->eir |= FEC_INT_RXF; + } else { + s->eir |= FEC_INT_RXB; + } + imx_fec_write_bd(&bd, addr); + /* Advance to the next descriptor. */ + if ((bd.flags & FEC_BD_W) != 0) { + addr = s->erdsr; + } else { + addr += 8; + } + } + s->rx_descriptor = addr; + imx_fec_enable_rx(s); + imx_fec_update(s); + return len; +} + +static const MemoryRegionOps imx_fec_ops = { + .read = imx_fec_read, + .write = imx_fec_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void imx_fec_cleanup(NetClientState *nc) +{ + IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); + + s->nic = NULL; +} + +static NetClientInfo net_imx_fec_info = { + .type = NET_CLIENT_OPTIONS_KIND_NIC, + .size = sizeof(NICState), + .can_receive = imx_fec_can_receive, + .receive = imx_fec_receive, + .cleanup = imx_fec_cleanup, + .link_status_changed = imx_fec_set_link, +}; + + +static void imx_fec_realize(DeviceState *dev, Error **errp) +{ + IMXFECState *s = IMX_FEC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &imx_fec_ops, s, + TYPE_IMX_FEC, 0x400); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); + qemu_macaddr_default_if_unset(&s->conf.macaddr); + + s->conf.peers.ncs[0] = nd_table[0].netdev; + + s->nic = qemu_new_nic(&net_imx_fec_info, &s->conf, + object_get_typename(OBJECT(dev)), DEVICE(dev)->id, + s); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); +} + +static Property imx_fec_properties[] = { + DEFINE_NIC_PROPERTIES(IMXFECState, conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static void imx_fec_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_imx_fec; + dc->reset = imx_fec_reset; + dc->props = imx_fec_properties; + dc->realize = imx_fec_realize; + dc->desc = "i.MX FEC Ethernet Controller"; +} + +static const TypeInfo imx_fec_info = { + .name = TYPE_IMX_FEC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMXFECState), + .class_init = imx_fec_class_init, +}; + +static void imx_fec_register_types(void) +{ + type_register_static(&imx_fec_info); +} + +type_init(imx_fec_register_types) diff --git a/qemu/hw/net/lan9118.c b/qemu/hw/net/lan9118.c index 4f0e840f0..08dc474d6 100644 --- a/qemu/hw/net/lan9118.c +++ b/qemu/hw/net/lan9118.c @@ -10,6 +10,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "net/net.h" #include "hw/devices.h" @@ -56,6 +57,8 @@ do { fprintf(stderr, "lan9118: error: " fmt , ## __VA_ARGS__);} while (0) #define CSR_E2P_CMD 0xb0 #define CSR_E2P_DATA 0xb4 +#define E2P_CMD_MAC_ADDR_LOADED 0x100 + /* IRQ_CFG */ #define IRQ_INT 0x00001000 #define IRQ_EN 0x00000100 @@ -352,14 +355,14 @@ static void lan9118_reload_eeprom(lan9118_state *s) { int i; if (s->eeprom[0] != 0xa5) { - s->e2p_cmd &= ~0x10; + s->e2p_cmd &= ~E2P_CMD_MAC_ADDR_LOADED; DPRINTF("MACADDR load failed\n"); return; } for (i = 0; i < 6; i++) { s->conf.macaddr.a[i] = s->eeprom[i + 1]; } - s->e2p_cmd |= 0x10; + s->e2p_cmd |= E2P_CMD_MAC_ADDR_LOADED; DPRINTF("MACADDR loaded from eeprom\n"); lan9118_mac_changed(s); } @@ -902,7 +905,8 @@ static void do_mac_write(lan9118_state *s, int reg, uint32_t val) */ break; default: - hw_error("lan9118: Unimplemented MAC register write: %d = 0x%x\n", + qemu_log_mask(LOG_GUEST_ERROR, + "lan9118: Unimplemented MAC register write: %d = 0x%x\n", s->mac_cmd & 0xf, val); } } @@ -930,14 +934,16 @@ static uint32_t do_mac_read(lan9118_state *s, int reg) case MAC_FLOW: return s->mac_flow; default: - hw_error("lan9118: Unimplemented MAC register read: %d\n", + qemu_log_mask(LOG_GUEST_ERROR, + "lan9118: Unimplemented MAC register read: %d\n", s->mac_cmd & 0xf); + return 0; } } static void lan9118_eeprom_cmd(lan9118_state *s, int cmd, int addr) { - s->e2p_cmd = (s->e2p_cmd & 0x10) | (cmd << 28) | addr; + s->e2p_cmd = (s->e2p_cmd & E2P_CMD_MAC_ADDR_LOADED) | (cmd << 28) | addr; switch (cmd) { case 0: s->e2p_data = s->eeprom[addr]; @@ -1128,7 +1134,8 @@ static void lan9118_writel(void *opaque, hwaddr offset, break; default: - hw_error("lan9118_write: Bad reg 0x%x = %x\n", (int)offset, (int)val); + qemu_log_mask(LOG_GUEST_ERROR, "lan9118_write: Bad reg 0x%x = %x\n", + (int)offset, (int)val); break; } lan9118_update(s); @@ -1246,7 +1253,7 @@ static uint64_t lan9118_readl(void *opaque, hwaddr offset, case CSR_E2P_DATA: return s->e2p_data; } - hw_error("lan9118_read: Bad reg 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "lan9118_read: Bad reg 0x%x\n", (int)offset); return 0; } diff --git a/qemu/hw/net/lance.c b/qemu/hw/net/lance.c index 780b39d65..6253d2103 100644 --- a/qemu/hw/net/lance.c +++ b/qemu/hw/net/lance.c @@ -35,6 +35,7 @@ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "net/net.h" #include "qemu/timer.h" diff --git a/qemu/hw/net/mcf_fec.c b/qemu/hw/net/mcf_fec.c index 21928f9f3..7c0398ed9 100644 --- a/qemu/hw/net/mcf_fec.c +++ b/qemu/hw/net/mcf_fec.c @@ -5,6 +5,7 @@ * * This code is licensed under the GPL */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "net/net.h" #include "hw/m68k/mcf.h" diff --git a/qemu/hw/net/milkymist-minimac2.c b/qemu/hw/net/milkymist-minimac2.c index 5d1cf0851..1e147c33c 100644 --- a/qemu/hw/net/milkymist-minimac2.c +++ b/qemu/hw/net/milkymist-minimac2.c @@ -22,6 +22,10 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" /* FIXME: why does this use TARGET_PAGE_ALIGN? */ #include "hw/hw.h" #include "hw/sysbus.h" #include "trace.h" @@ -463,7 +467,7 @@ static int milkymist_minimac2_init(SysBusDevice *sbd) /* register buffers memory */ memory_region_init_ram(&s->buffers, OBJECT(dev), "milkymist-minimac2.buffers", - buffers_size, &error_abort); + buffers_size, &error_fatal); vmstate_register_ram_global(&s->buffers); s->rx0_buf = memory_region_get_ram_ptr(&s->buffers); s->rx1_buf = s->rx0_buf + MINIMAC2_BUFFER_SIZE; diff --git a/qemu/hw/net/mipsnet.c b/qemu/hw/net/mipsnet.c index f261011a2..740cd98ff 100644 --- a/qemu/hw/net/mipsnet.c +++ b/qemu/hw/net/mipsnet.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/hw.h" #include "net/net.h" #include "trace.h" diff --git a/qemu/hw/net/ne2000-isa.c b/qemu/hw/net/ne2000-isa.c index 17e7199f7..a7f5a9464 100644 --- a/qemu/hw/net/ne2000-isa.c +++ b/qemu/hw/net/ne2000-isa.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/isa/isa.h" @@ -28,6 +29,7 @@ #include "net/net.h" #include "ne2000.h" #include "exec/address-spaces.h" +#include "qapi/error.h" #include "qapi/visitor.h" #define TYPE_ISA_NE2000 "ne2k_isa" @@ -44,7 +46,6 @@ typedef struct ISANE2000State { static NetClientInfo net_ne2000_isa_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), - .can_receive = ne2000_can_receive, .receive = ne2000_receive, }; @@ -94,24 +95,26 @@ static void isa_ne2000_class_initfn(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } -static void isa_ne2000_get_bootindex(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void isa_ne2000_get_bootindex(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { ISANE2000State *isa = ISA_NE2000(obj); NE2000State *s = &isa->ne2000; - visit_type_int32(v, &s->c.bootindex, name, errp); + visit_type_int32(v, name, &s->c.bootindex, errp); } -static void isa_ne2000_set_bootindex(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void isa_ne2000_set_bootindex(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { ISANE2000State *isa = ISA_NE2000(obj); NE2000State *s = &isa->ne2000; int32_t boot_index; Error *local_err = NULL; - visit_type_int32(v, &boot_index, name, &local_err); + visit_type_int32(v, name, &boot_index, &local_err); if (local_err) { goto out; } diff --git a/qemu/hw/net/ne2000.c b/qemu/hw/net/ne2000.c index 2bdb4c927..f0feaf96b 100644 --- a/qemu/hw/net/ne2000.c +++ b/qemu/hw/net/ne2000.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "net/net.h" @@ -154,6 +155,10 @@ static int ne2000_buffer_full(NE2000State *s) { int avail, index, boundary; + if (s->stop <= s->start) { + return 1; + } + index = s->curpag << 8; boundary = s->boundary << 8; if (index < boundary) @@ -165,15 +170,6 @@ static int ne2000_buffer_full(NE2000State *s) return 0; } -int ne2000_can_receive(NetClientState *nc) -{ - NE2000State *s = qemu_get_nic_opaque(nc); - - if (s->cmd & E8390_STOP) - return 1; - return !ne2000_buffer_full(s); -} - #define MIN_BUF_SIZE 60 ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_) @@ -476,8 +472,9 @@ static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr, uint32_t val) { addr &= ~1; /* XXX: check exact behaviour if not even */ - if (addr < 32 || - (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + if (addr < 32 + || (addr >= NE2000_PMEM_START + && addr + sizeof(uint32_t) <= NE2000_MEM_SIZE)) { stl_le_p(s->mem + addr, val); } } @@ -506,8 +503,9 @@ static inline uint32_t ne2000_mem_readw(NE2000State *s, uint32_t addr) static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr) { addr &= ~1; /* XXX: check exact behaviour if not even */ - if (addr < 32 || - (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + if (addr < 32 + || (addr >= NE2000_PMEM_START + && addr + sizeof(uint32_t) <= NE2000_MEM_SIZE)) { return ldl_le_p(s->mem + addr); } else { return 0xffffffff; @@ -716,7 +714,6 @@ void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size) static NetClientInfo net_ne2000_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), - .can_receive = ne2000_can_receive, .receive = ne2000_receive, }; diff --git a/qemu/hw/net/ne2000.h b/qemu/hw/net/ne2000.h index e500306aa..d022b28fc 100644 --- a/qemu/hw/net/ne2000.h +++ b/qemu/hw/net/ne2000.h @@ -34,7 +34,6 @@ typedef struct NE2000State { void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size); extern const VMStateDescription vmstate_ne2000; void ne2000_reset(NE2000State *s); -int ne2000_can_receive(NetClientState *nc); ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_); #endif diff --git a/qemu/hw/net/opencores_eth.c b/qemu/hw/net/opencores_eth.c index 3642046ef..c6094fbb5 100644 --- a/qemu/hw/net/opencores_eth.c +++ b/qemu/hw/net/opencores_eth.c @@ -31,6 +31,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "net/net.h" @@ -84,7 +85,7 @@ static void mii_reset(Mii *s) { memset(s->regs, 0, sizeof(s->regs)); s->regs[MII_BMCR] = 0x1000; - s->regs[MII_BMSR] = 0x7848; /* no ext regs */ + s->regs[MII_BMSR] = 0x7868; /* no ext regs */ s->regs[MII_PHYIDR1] = 0x2000; s->regs[MII_PHYIDR2] = 0x5c90; s->regs[MII_ANAR] = 0x01e1; diff --git a/qemu/hw/net/pcnet-pci.c b/qemu/hw/net/pcnet-pci.c index b4d60b812..595439a65 100644 --- a/qemu/hw/net/pcnet-pci.c +++ b/qemu/hw/net/pcnet-pci.c @@ -27,6 +27,7 @@ * AMD Publication# 19436 Rev:E Amendment/0 Issue Date: June 2000 */ +#include "qemu/osdep.h" #include "hw/pci/pci.h" #include "net/net.h" #include "hw/loader.h" diff --git a/qemu/hw/net/pcnet.c b/qemu/hw/net/pcnet.c index 34373767d..198a01f92 100644 --- a/qemu/hw/net/pcnet.c +++ b/qemu/hw/net/pcnet.c @@ -35,6 +35,7 @@ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt */ +#include "qemu/osdep.h" #include "hw/qdev.h" #include "net/net.h" #include "qemu/timer.h" @@ -670,8 +671,7 @@ static inline hwaddr pcnet_rdra_addr(PCNetState *s, int idx) static inline int64_t pcnet_get_next_poll_time(PCNetState *s, int64_t current_time) { int64_t next_time = current_time + - muldiv64(65536 - (CSR_SPND(s) ? 0 : CSR_POLL(s)), - get_ticks_per_sec(), 33000000L); + (65536 - (CSR_SPND(s) ? 0 : CSR_POLL(s))) * 30; if (next_time <= current_time) next_time = current_time + 1; return next_time; @@ -1065,6 +1065,12 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) int pktcount = 0; if (!s->looptest) { + if (size > 4092) { +#ifdef PCNET_DEBUG_RMD + fprintf(stderr, "pcnet: truncates rx packet.\n"); +#endif + size = 4092; + } memcpy(src, buf, size); /* no need to compute the CRC */ src[size] = 0; @@ -1085,7 +1091,7 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) uint32_t fcs = ~0; uint8_t *p = src; - while (p != &src[size-4]) + while (p != &src[size]) CRC(fcs, *p++); crc_err = (*(uint32_t *)p != htonl(fcs)); } @@ -1234,8 +1240,10 @@ static void pcnet_transmit(PCNetState *s) bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); /* if multi-tmd packet outsizes s->buffer then skip it silently. - Note: this is not what real hw does */ - if (s->xmit_pos + bcnt > sizeof(s->buffer)) { + * Note: this is not what real hw does. + * Last four bytes of s->buffer are used to store CRC FCS code. + */ + if (s->xmit_pos + bcnt > sizeof(s->buffer) - 4) { s->xmit_pos = -1; goto txdone; } diff --git a/qemu/hw/net/rocker/qmp-norocker.c b/qemu/hw/net/rocker/qmp-norocker.c index 49b498b64..6acbcdb02 100644 --- a/qemu/hw/net/rocker/qmp-norocker.c +++ b/qemu/hw/net/rocker/qmp-norocker.c @@ -15,6 +15,7 @@ * GNU General Public License for more details. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qmp-commands.h" #include "qapi/qmp/qerror.h" diff --git a/qemu/hw/net/rocker/rocker.c b/qemu/hw/net/rocker/rocker.c index 47d080fd3..30f2ce417 100644 --- a/qemu/hw/net/rocker/rocker.c +++ b/qemu/hw/net/rocker/rocker.c @@ -15,6 +15,7 @@ * GNU General Public License for more details. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "hw/pci/msix.h" @@ -42,6 +43,7 @@ struct rocker { /* switch configuration */ char *name; /* switch name */ + char *world_name; /* world name */ uint32_t fp_ports; /* front-panel port count */ NICPeers *fp_ports_peers; MACAddr fp_start_macaddr; /* front-panel port 0 mac addr */ @@ -101,8 +103,7 @@ RockerSwitch *qmp_query_rocker(const char *name, Error **errp) r = rocker_find(name); if (!r) { - error_set(errp, ERROR_CLASS_GENERIC_ERROR, - "rocker %s not found", name); + error_setg(errp, "rocker %s not found", name); return NULL; } @@ -122,8 +123,7 @@ RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp) r = rocker_find(name); if (!r) { - error_set(errp, ERROR_CLASS_GENERIC_ERROR, - "rocker %s not found", name); + error_setg(errp, "rocker %s not found", name); return NULL; } @@ -234,6 +234,9 @@ static int tx_consume(Rocker *r, DescInfo *info) frag_addr = rocker_tlv_get_le64(tlvs[ROCKER_TLV_TX_FRAG_ATTR_ADDR]); frag_len = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_FRAG_ATTR_LEN]); + if (iovcnt >= ROCKER_TX_FRAGS_MAX) { + goto err_too_many_frags; + } iov[iovcnt].iov_len = frag_len; iov[iovcnt].iov_base = g_malloc(frag_len); if (!iov[iovcnt].iov_base) { @@ -246,10 +249,7 @@ static int tx_consume(Rocker *r, DescInfo *info) err = -ROCKER_ENXIO; goto err_bad_io; } - - if (++iovcnt > ROCKER_TX_FRAGS_MAX) { - goto err_too_many_frags; - } + iovcnt++; } if (iovcnt) { @@ -265,9 +265,7 @@ err_bad_io: err_no_mem: err_bad_attr: for (i = 0; i < ROCKER_TX_FRAGS_MAX; i++) { - if (iov[i].iov_base) { - g_free(iov[i].iov_base); - } + g_free(iov[i].iov_base); } return err; @@ -403,7 +401,13 @@ static int cmd_set_port_settings(Rocker *r, if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]) { mode = rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]); - fp_port_set_world(fp_port, r->worlds[mode]); + if (mode >= ROCKER_WORLD_TYPE_MAX) { + return -ROCKER_EINVAL; + } + /* We don't support world change. */ + if (!fp_port_check_world(fp_port, r->worlds[mode])) { + return -ROCKER_EINVAL; + } } if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING]) { @@ -1283,6 +1287,18 @@ static void rocker_msix_uninit(Rocker *r) rocker_msix_vectors_unuse(r, ROCKER_MSIX_VEC_COUNT(r->fp_ports)); } +static World *rocker_world_type_by_name(Rocker *r, const char *name) +{ + int i; + + for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) { + if (strcmp(name, world_name(r->worlds[i])) == 0) { + return r->worlds[i]; + } + } + return NULL; +} + static int pci_rocker_init(PCIDevice *dev) { Rocker *r = to_rocker(dev); @@ -1294,14 +1310,27 @@ static int pci_rocker_init(PCIDevice *dev) /* allocate worlds */ r->worlds[ROCKER_WORLD_TYPE_OF_DPA] = of_dpa_world_alloc(r); - r->world_dflt = r->worlds[ROCKER_WORLD_TYPE_OF_DPA]; for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) { if (!r->worlds[i]) { + err = -ENOMEM; goto err_world_alloc; } } + if (!r->world_name) { + r->world_name = g_strdup(world_name(r->worlds[ROCKER_WORLD_TYPE_OF_DPA])); + } + + r->world_dflt = rocker_world_type_by_name(r, r->world_name); + if (!r->world_dflt) { + fprintf(stderr, + "rocker: requested world \"%s\" does not exist\n", + r->world_name); + err = -EINVAL; + goto err_world_type_by_name; + } + /* set up memory-mapped region at BAR0 */ memory_region_init_io(&r->mmio, OBJECT(r), &rocker_mmio_ops, r, @@ -1364,7 +1393,7 @@ static int pci_rocker_init(PCIDevice *dev) r->fp_ports = ROCKER_FP_PORTS_MAX; } - r->rings = g_malloc(sizeof(DescRing *) * rocker_pci_ring_count(r)); + r->rings = g_new(DescRing *, rocker_pci_ring_count(r)); if (!r->rings) { goto err_rings_alloc; } @@ -1435,6 +1464,7 @@ err_duplicate: err_msix_init: object_unparent(OBJECT(&r->msix_bar)); object_unparent(OBJECT(&r->mmio)); +err_world_type_by_name: err_world_alloc: for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) { if (r->worlds[i]) { @@ -1506,6 +1536,7 @@ static void rocker_reset(DeviceState *dev) static Property rocker_properties[] = { DEFINE_PROP_STRING("name", Rocker, name), + DEFINE_PROP_STRING("world", Rocker, world_name), DEFINE_PROP_MACADDR("fp_start_macaddr", Rocker, fp_start_macaddr), DEFINE_PROP_UINT64("switch_id", Rocker, diff --git a/qemu/hw/net/rocker/rocker_desc.c b/qemu/hw/net/rocker/rocker_desc.c index 9d896fe47..ac02797b7 100644 --- a/qemu/hw/net/rocker/rocker_desc.c +++ b/qemu/hw/net/rocker/rocker_desc.c @@ -14,6 +14,7 @@ * GNU General Public License for more details. */ +#include "qemu/osdep.h" #include "net/net.h" #include "hw/hw.h" #include "hw/pci/pci.h" @@ -136,15 +137,13 @@ bool desc_ring_set_size(DescRing *ring, uint32_t size) } for (i = 0; i < ring->size; i++) { - if (ring->info[i].buf) { - g_free(ring->info[i].buf); - } + g_free(ring->info[i].buf); } ring->size = size; ring->head = ring->tail = 0; - ring->info = g_realloc(ring->info, size * sizeof(DescInfo)); + ring->info = g_renew(DescInfo, ring->info, size); if (!ring->info) { return false; } @@ -347,7 +346,7 @@ DescRing *desc_ring_alloc(Rocker *r, int index) { DescRing *ring; - ring = g_malloc0(sizeof(DescRing)); + ring = g_new0(DescRing, 1); if (!ring) { return NULL; } @@ -360,9 +359,7 @@ DescRing *desc_ring_alloc(Rocker *r, int index) void desc_ring_free(DescRing *ring) { - if (ring->info) { - g_free(ring->info); - } + g_free(ring->info); g_free(ring); } diff --git a/qemu/hw/net/rocker/rocker_fp.c b/qemu/hw/net/rocker/rocker_fp.c index c693ae508..0149899c6 100644 --- a/qemu/hw/net/rocker/rocker_fp.c +++ b/qemu/hw/net/rocker/rocker_fp.c @@ -14,6 +14,7 @@ * GNU General Public License for more details. */ +#include "qemu/osdep.h" #include "net/clients.h" #include "rocker.h" @@ -185,6 +186,11 @@ void fp_port_set_world(FpPort *port, World *world) port->world = world; } +bool fp_port_check_world(FpPort *port, World *world) +{ + return port->world == world; +} + bool fp_port_enabled(FpPort *port) { return port->enabled; @@ -218,7 +224,7 @@ FpPort *fp_port_alloc(Rocker *r, char *sw_name, MACAddr *start_mac, unsigned int index, NICPeers *peers) { - FpPort *port = g_malloc0(sizeof(FpPort)); + FpPort *port = g_new0(FpPort, 1); if (!port) { return NULL; diff --git a/qemu/hw/net/rocker/rocker_fp.h b/qemu/hw/net/rocker/rocker_fp.h index ab80fd833..04592bbfd 100644 --- a/qemu/hw/net/rocker/rocker_fp.h +++ b/qemu/hw/net/rocker/rocker_fp.h @@ -40,6 +40,7 @@ int fp_port_set_settings(FpPort *port, uint32_t speed, bool fp_port_from_pport(uint32_t pport, uint32_t *port); World *fp_port_get_world(FpPort *port); void fp_port_set_world(FpPort *port, World *world); +bool fp_port_check_world(FpPort *port, World *world); bool fp_port_enabled(FpPort *port); void fp_port_enable(FpPort *port); void fp_port_disable(FpPort *port); diff --git a/qemu/hw/net/rocker/rocker_of_dpa.c b/qemu/hw/net/rocker/rocker_of_dpa.c index 874fb01d6..0a134ebca 100644 --- a/qemu/hw/net/rocker/rocker_of_dpa.c +++ b/qemu/hw/net/rocker/rocker_of_dpa.c @@ -14,6 +14,7 @@ * GNU General Public License for more details. */ +#include "qemu/osdep.h" #include "net/eth.h" #include "qemu/iov.h" #include "qemu/timer.h" @@ -367,7 +368,7 @@ static OfDpaFlow *of_dpa_flow_alloc(uint64_t cookie) OfDpaFlow *flow; int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) / 1000; - flow = g_malloc0(sizeof(OfDpaFlow)); + flow = g_new0(OfDpaFlow, 1); if (!flow) { return NULL; } @@ -811,7 +812,7 @@ static int of_dpa_group_get_stats(OfDpa *of_dpa, uint32_t id) static OfDpaGroup *of_dpa_group_alloc(uint32_t id) { - OfDpaGroup *group = g_malloc0(sizeof(OfDpaGroup)); + OfDpaGroup *group = g_new0(OfDpaGroup, 1); if (!group) { return NULL; @@ -2039,15 +2040,14 @@ static int of_dpa_cmd_add_l2_flood(OfDpa *of_dpa, OfDpaGroup *group, group->l2_flood.group_count = rocker_tlv_get_le16(group_tlvs[ROCKER_TLV_OF_DPA_GROUP_COUNT]); - tlvs = g_malloc0((group->l2_flood.group_count + 1) * - sizeof(RockerTlv *)); + tlvs = g_new0(RockerTlv *, group->l2_flood.group_count + 1); if (!tlvs) { return -ROCKER_ENOMEM; } g_free(group->l2_flood.group_ids); group->l2_flood.group_ids = - g_malloc0(group->l2_flood.group_count * sizeof(uint32_t)); + g_new0(uint32_t, group->l2_flood.group_count); if (!group->l2_flood.group_ids) { err = -ROCKER_ENOMEM; goto err_out; @@ -2463,15 +2463,13 @@ RockerOfDpaFlowList *qmp_query_rocker_of_dpa_flows(const char *name, r = rocker_find(name); if (!r) { - error_set(errp, ERROR_CLASS_GENERIC_ERROR, - "rocker %s not found", name); + error_setg(errp, "rocker %s not found", name); return NULL; } w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA); if (!w) { - error_set(errp, ERROR_CLASS_GENERIC_ERROR, - "rocker %s doesn't have OF-DPA world", name); + error_setg(errp, "rocker %s doesn't have OF-DPA world", name); return NULL; } @@ -2598,15 +2596,13 @@ RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name, r = rocker_find(name); if (!r) { - error_set(errp, ERROR_CLASS_GENERIC_ERROR, - "rocker %s not found", name); + error_setg(errp, "rocker %s not found", name); return NULL; } w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA); if (!w) { - error_set(errp, ERROR_CLASS_GENERIC_ERROR, - "rocker %s doesn't have OF-DPA world", name); + error_setg(errp, "rocker %s doesn't have OF-DPA world", name); return NULL; } @@ -2618,6 +2614,7 @@ RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name, } static WorldOps of_dpa_ops = { + .name = "ofdpa", .init = of_dpa_init, .uninit = of_dpa_uninit, .ig = of_dpa_ig, diff --git a/qemu/hw/net/rocker/rocker_world.c b/qemu/hw/net/rocker/rocker_world.c index a6b18f175..89777e968 100644 --- a/qemu/hw/net/rocker/rocker_world.c +++ b/qemu/hw/net/rocker/rocker_world.c @@ -14,6 +14,7 @@ * GNU General Public License for more details. */ +#include "qemu/osdep.h" #include "qemu/iov.h" #include "rocker.h" @@ -97,10 +98,5 @@ enum rocker_world_type world_type(World *world) const char *world_name(World *world) { - switch (world->type) { - case ROCKER_WORLD_TYPE_OF_DPA: - return "OF_DPA"; - default: - return "unknown"; - } + return world->ops->name; } diff --git a/qemu/hw/net/rocker/rocker_world.h b/qemu/hw/net/rocker/rocker_world.h index 18d277b92..58ade4733 100644 --- a/qemu/hw/net/rocker/rocker_world.h +++ b/qemu/hw/net/rocker/rocker_world.h @@ -33,6 +33,7 @@ typedef int (world_cmd)(World *world, DescInfo *info, RockerTlv *cmd_info_tlv); typedef struct world_ops { + const char *name; world_init *init; world_uninit *uninit; world_ig *ig; diff --git a/qemu/hw/net/rtl8139.c b/qemu/hw/net/rtl8139.c index edbb61ccf..1e5ec149f 100644 --- a/qemu/hw/net/rtl8139.c +++ b/qemu/hw/net/rtl8139.c @@ -43,12 +43,13 @@ * Added rx/tx buffer reset when enabling rx/tx operation * * 2010-Feb-04 Frediano Ziglio: Rewrote timer support using QEMU timer only - * when strictly needed (required for for + * when strictly needed (required for * Darwin) * 2011-Mar-22 Benjamin Poirier: Implemented VLAN offloading */ /* For crc32 */ +#include "qemu/osdep.h" #include #include "hw/hw.h" @@ -56,6 +57,7 @@ #include "sysemu/dma.h" #include "qemu/timer.h" #include "net/net.h" +#include "net/eth.h" #include "hw/loader.h" #include "sysemu/sysemu.h" #include "qemu/iov.h" @@ -63,7 +65,7 @@ /* debug RTL8139 card */ //#define DEBUG_RTL8139 1 -#define PCI_FREQUENCY 33000000L +#define PCI_PERIOD 30 /* 30 ns period = 33.333333 Mhz frequency */ #define SET_MASKED(input, mask, curr) \ ( ( (input) & ~(mask) ) | ( (curr) & (mask) ) ) @@ -72,11 +74,7 @@ #define MOD2(input, size) \ ( ( input ) & ( size - 1 ) ) -#define ETHER_ADDR_LEN 6 #define ETHER_TYPE_LEN 2 -#define ETH_HLEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN) -#define ETH_P_IP 0x0800 /* Internet Protocol packet */ -#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ #define ETH_MTU 1500 #define VLAN_TCI_LEN 2 @@ -1016,8 +1014,8 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t /* write VLAN info to descriptor variables. */ if (s->CpCmd & CPlusRxVLAN && be16_to_cpup((uint16_t *) - &buf[ETHER_ADDR_LEN * 2]) == ETH_P_8021Q) { - dot1q_buf = &buf[ETHER_ADDR_LEN * 2]; + &buf[ETH_ALEN * 2]) == ETH_P_VLAN) { + dot1q_buf = &buf[ETH_ALEN * 2]; size -= VLAN_HLEN; /* if too small buffer, use the tailroom added duing expansion */ if (size < MIN_BUF_SIZE) { @@ -1058,10 +1056,10 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t /* receive/copy to target memory */ if (dot1q_buf) { - pci_dma_write(d, rx_addr, buf, 2 * ETHER_ADDR_LEN); - pci_dma_write(d, rx_addr + 2 * ETHER_ADDR_LEN, - buf + 2 * ETHER_ADDR_LEN + VLAN_HLEN, - size - 2 * ETHER_ADDR_LEN); + pci_dma_write(d, rx_addr, buf, 2 * ETH_ALEN); + pci_dma_write(d, rx_addr + 2 * ETH_ALEN, + buf + 2 * ETH_ALEN + VLAN_HLEN, + size - 2 * ETH_ALEN); } else { pci_dma_write(d, rx_addr, buf, size); } @@ -1148,7 +1146,9 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t /* if receiver buffer is empty then avail == 0 */ - if (avail != 0 && size + 8 >= avail) +#define RX_ALIGN(x) (((x) + 3) & ~0x3) + + if (avail != 0 && RX_ALIGN(size + 8) >= avail) { DPRINTF("rx overflow: rx buffer length %d head 0x%04x " "read 0x%04x === available 0x%04x need 0x%04x\n", @@ -1157,7 +1157,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t s->IntrStatus |= RxOverflow; ++s->RxMissed; rtl8139_update_irq(s); - return size_; + return 0; } packet_header |= RxStatusOK; @@ -1176,7 +1176,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t rtl8139_write_buffer(s, (uint8_t *)&val, 4); /* correct buffer write pointer */ - s->RxBufAddr = MOD2((s->RxBufAddr + 3) & ~0x3, s->RxBufferSize); + s->RxBufAddr = MOD2(RX_ALIGN(s->RxBufAddr), s->RxBufferSize); /* now we can signal we have received something */ @@ -1783,12 +1783,12 @@ static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size, return; } - if (dot1q_buf && size >= ETHER_ADDR_LEN * 2) { + if (dot1q_buf && size >= ETH_ALEN * 2) { iov = (struct iovec[3]) { - { .iov_base = buf, .iov_len = ETHER_ADDR_LEN * 2 }, + { .iov_base = buf, .iov_len = ETH_ALEN * 2 }, { .iov_base = (void *) dot1q_buf, .iov_len = VLAN_HLEN }, - { .iov_base = buf + ETHER_ADDR_LEN * 2, - .iov_len = size - ETHER_ADDR_LEN * 2 }, + { .iov_base = buf + ETH_ALEN * 2, + .iov_len = size - ETH_ALEN * 2 }, }; memcpy(vlan_iov, iov, sizeof(vlan_iov)); @@ -1868,64 +1868,12 @@ static int rtl8139_transmit_one(RTL8139State *s, int descriptor) } /* structures and macros for task offloading */ -typedef struct ip_header -{ - uint8_t ip_ver_len; /* version and header length */ - uint8_t ip_tos; /* type of service */ - uint16_t ip_len; /* total length */ - uint16_t ip_id; /* identification */ - uint16_t ip_off; /* fragment offset field */ - uint8_t ip_ttl; /* time to live */ - uint8_t ip_p; /* protocol */ - uint16_t ip_sum; /* checksum */ - uint32_t ip_src,ip_dst; /* source and dest address */ -} ip_header; - -#define IP_HEADER_VERSION_4 4 -#define IP_HEADER_VERSION(ip) ((ip->ip_ver_len >> 4)&0xf) -#define IP_HEADER_LENGTH(ip) (((ip->ip_ver_len)&0xf) << 2) - -typedef struct tcp_header -{ - uint16_t th_sport; /* source port */ - uint16_t th_dport; /* destination port */ - uint32_t th_seq; /* sequence number */ - uint32_t th_ack; /* acknowledgement number */ - uint16_t th_offset_flags; /* data offset, reserved 6 bits, TCP protocol flags */ - uint16_t th_win; /* window */ - uint16_t th_sum; /* checksum */ - uint16_t th_urp; /* urgent pointer */ -} tcp_header; - -typedef struct udp_header -{ - uint16_t uh_sport; /* source port */ - uint16_t uh_dport; /* destination port */ - uint16_t uh_ulen; /* udp length */ - uint16_t uh_sum; /* udp checksum */ -} udp_header; - -typedef struct ip_pseudo_header -{ - uint32_t ip_src; - uint32_t ip_dst; - uint8_t zeros; - uint8_t ip_proto; - uint16_t ip_payload; -} ip_pseudo_header; - -#define IP_PROTO_TCP 6 -#define IP_PROTO_UDP 17 - #define TCP_HEADER_DATA_OFFSET(tcp) (((be16_to_cpu(tcp->th_offset_flags) >> 12)&0xf) << 2) #define TCP_FLAGS_ONLY(flags) ((flags)&0x3f) #define TCP_HEADER_FLAGS(tcp) TCP_FLAGS_ONLY(be16_to_cpu(tcp->th_offset_flags)) #define TCP_HEADER_CLEAR_FLAGS(tcp, off) ((tcp)->th_offset_flags &= cpu_to_be16(~TCP_FLAGS_ONLY(off))) -#define TCP_FLAG_FIN 0x01 -#define TCP_FLAG_PUSH 0x08 - /* produces ones' complement sum of data */ static uint16_t ones_complement_sum(uint8_t *data, size_t len) { @@ -2098,7 +2046,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) } /* transfer ownership to target */ - txdw0 &= ~CP_RX_OWN; + txdw0 &= ~CP_TX_OWN; /* reset error indicator bits */ txdw0 &= ~CP_TX_STATUS_UNF; @@ -2134,7 +2082,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) bswap16(txdw1 & CP_TX_VLAN_TAG_MASK)); dot1q_buffer = (uint16_t *) dot1q_buffer_space; - dot1q_buffer[0] = cpu_to_be16(ETH_P_8021Q); + dot1q_buffer[0] = cpu_to_be16(ETH_P_VLAN); /* BE + le_to_cpu() + ~cpu_to_le()~ = BE */ dot1q_buffer[1] = cpu_to_le16(txdw1 & CP_TX_VLAN_TAG_MASK); } else { @@ -2151,12 +2099,12 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) DPRINTF("+++ C+ mode offloaded task checksum\n"); /* Large enough for Ethernet and IP headers? */ - if (saved_size < ETH_HLEN + sizeof(ip_header)) { + if (saved_size < ETH_HLEN + sizeof(struct ip_header)) { goto skip_offload; } /* ip packet header */ - ip_header *ip = NULL; + struct ip_header *ip = NULL; int hlen = 0; uint8_t ip_protocol = 0; uint16_t ip_data_len = 0; @@ -2172,11 +2120,15 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) DPRINTF("+++ C+ mode has IP packet\n"); - /* not aligned */ + /* Note on memory alignment: eth_payload_data is 16-bit aligned + * since saved_buffer is allocated with g_malloc() and ETH_HLEN is + * even. 32-bit accesses must use ldl/stl wrappers to avoid + * unaligned accesses. + */ eth_payload_data = saved_buffer + ETH_HLEN; eth_payload_len = saved_size - ETH_HLEN; - ip = (ip_header*)eth_payload_data; + ip = (struct ip_header*)eth_payload_data; if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) { DPRINTF("+++ C+ mode packet has bad IP version %d " @@ -2185,8 +2137,8 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) goto skip_offload; } - hlen = IP_HEADER_LENGTH(ip); - if (hlen < sizeof(ip_header) || hlen > eth_payload_len) { + hlen = IP_HDR_GET_LEN(ip); + if (hlen < sizeof(struct ip_header) || hlen > eth_payload_len) { goto skip_offload; } @@ -2269,7 +2221,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) } DPRINTF("+++ C+ mode TSO TCP seqno %08x\n", - be32_to_cpu(p_tcp_hdr->th_seq)); + ldl_be_p(&p_tcp_hdr->th_seq)); /* add 4 TCP pseudoheader fields */ /* copy IP source and destination fields */ @@ -2287,7 +2239,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) /* keep PUSH and FIN flags only for the last frame */ if (!is_last_frame) { - TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TCP_FLAG_PUSH|TCP_FLAG_FIN); + TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TH_PUSH | TH_FIN); } /* recalculate TCP checksum */ @@ -2325,7 +2277,8 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) 0, (uint8_t *) dot1q_buffer); /* add transferred count to TCP sequence number */ - p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq)); + stl_be_p(&p_tcp_hdr->th_seq, + chunk_size + ldl_be_p(&p_tcp_hdr->th_seq)); ++send_count; } @@ -2881,8 +2834,7 @@ static void rtl8139_io_writew(void *opaque, uint8_t addr, uint32_t val) static void rtl8139_set_next_tctr_time(RTL8139State *s) { - const uint64_t ns_per_period = - muldiv64(0x100000000LL, get_ticks_per_sec(), PCI_FREQUENCY); + const uint64_t ns_per_period = (uint64_t)PCI_PERIOD << 32; DPRINTF("entered rtl8139_set_next_tctr_time\n"); @@ -2900,7 +2852,7 @@ static void rtl8139_set_next_tctr_time(RTL8139State *s) if (!s->TimerInt) { timer_del(s->timer); } else { - uint64_t delta = muldiv64(s->TimerInt, get_ticks_per_sec(), PCI_FREQUENCY); + uint64_t delta = (uint64_t)s->TimerInt * PCI_PERIOD; if (s->TCTR_base + delta <= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) { delta += ns_per_period; } @@ -3174,8 +3126,8 @@ static uint32_t rtl8139_io_readl(void *opaque, uint8_t addr) break; case Timer: - ret = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->TCTR_base, - PCI_FREQUENCY, get_ticks_per_sec()); + ret = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->TCTR_base) / + PCI_PERIOD; DPRINTF("TCTR Timer read val=0x%08x\n", ret); break; @@ -3269,8 +3221,7 @@ static void rtl8139_pre_save(void *opaque) int64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); /* for migration to older versions */ - s->TCTR = muldiv64(current_time - s->TCTR_base, PCI_FREQUENCY, - get_ticks_per_sec()); + s->TCTR = (current_time - s->TCTR_base) / PCI_PERIOD; s->rtl8139_mmio_io_addr_dummy = 0; } @@ -3438,10 +3389,8 @@ static void pci_rtl8139_uninit(PCIDevice *dev) { RTL8139State *s = RTL8139(dev); - if (s->cplus_txbuffer) { - g_free(s->cplus_txbuffer); - s->cplus_txbuffer = NULL; - } + g_free(s->cplus_txbuffer); + s->cplus_txbuffer = NULL; timer_del(s->timer); timer_free(s->timer); qemu_del_nic(s->nic); diff --git a/qemu/hw/net/smc91c111.c b/qemu/hw/net/smc91c111.c index 74e06e6c7..21c1b8f54 100644 --- a/qemu/hw/net/smc91c111.c +++ b/qemu/hw/net/smc91c111.c @@ -7,6 +7,7 @@ * This code is licensed under the GPL */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "net/net.h" #include "hw/devices.h" @@ -124,6 +125,25 @@ static void smc91c111_update(smc91c111_state *s) qemu_set_irq(s->irq, level); } +static int smc91c111_can_receive(smc91c111_state *s) +{ + if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST)) { + return 1; + } + if (s->allocated == (1 << NUM_PACKETS) - 1 || + s->rx_fifo_len == NUM_PACKETS) { + return 0; + } + return 1; +} + +static inline void smc91c111_flush_queued_packets(smc91c111_state *s) +{ + if (smc91c111_can_receive(s)) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } +} + /* Try to allocate a packet. Returns 0x80 on failure. */ static int smc91c111_allocate_packet(smc91c111_state *s) { @@ -164,6 +184,7 @@ static void smc91c111_pop_rx_fifo(smc91c111_state *s) } else { s->int_level &= ~INT_RCV; } + smc91c111_flush_queued_packets(s); smc91c111_update(s); } @@ -185,7 +206,7 @@ static void smc91c111_release_packet(smc91c111_state *s, int packet) s->allocated &= ~(1 << packet); if (s->tx_alloc == 0x80) smc91c111_tx_alloc(s); - qemu_flush_queued_packets(qemu_get_queue(s->nic)); + smc91c111_flush_queued_packets(s); } /* Flush the TX FIFO. */ @@ -311,6 +332,7 @@ static void smc91c111_writeb(void *opaque, hwaddr offset, if (s->rcr & RCR_SOFT_RST) { smc91c111_reset(DEVICE(s)); } + smc91c111_flush_queued_packets(s); return; case 10: case 11: /* RPCR */ /* Ignored */ @@ -636,15 +658,11 @@ static uint32_t smc91c111_readl(void *opaque, hwaddr offset) return val; } -static int smc91c111_can_receive(NetClientState *nc) +static int smc91c111_can_receive_nc(NetClientState *nc) { smc91c111_state *s = qemu_get_nic_opaque(nc); - if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST)) - return 1; - if (s->allocated == (1 << NUM_PACKETS) - 1) - return 0; - return 1; + return smc91c111_can_receive(s); } static ssize_t smc91c111_receive(NetClientState *nc, const uint8_t *buf, size_t size) @@ -739,7 +757,7 @@ static const MemoryRegionOps smc91c111_mem_ops = { static NetClientInfo net_smc91c111_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), - .can_receive = smc91c111_can_receive, + .can_receive = smc91c111_can_receive_nc, .receive = smc91c111_receive, }; diff --git a/qemu/hw/net/spapr_llan.c b/qemu/hw/net/spapr_llan.c index 1ca5e9ce6..a647f25d9 100644 --- a/qemu/hw/net/spapr_llan.c +++ b/qemu/hw/net/spapr_llan.c @@ -24,6 +24,9 @@ * THE SOFTWARE. * */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "net/net.h" #include "hw/qdev.h" @@ -44,6 +47,10 @@ #define DPRINTF(fmt...) #endif +/* Compatibility flags for migration */ +#define SPAPRVLAN_FLAG_RX_BUF_POOLS_BIT 0 +#define SPAPRVLAN_FLAG_RX_BUF_POOLS (1 << SPAPRVLAN_FLAG_RX_BUF_POOLS_BIT) + /* * Virtual LAN device */ @@ -85,6 +92,15 @@ typedef uint64_t vlan_bd_t; #define VIO_SPAPR_VLAN_DEVICE(obj) \ OBJECT_CHECK(VIOsPAPRVLANDevice, (obj), TYPE_VIO_SPAPR_VLAN_DEVICE) +#define RX_POOL_MAX_BDS 4096 +#define RX_MAX_POOLS 5 + +typedef struct { + int32_t bufsize; + int32_t count; + vlan_bd_t bds[RX_POOL_MAX_BDS]; +} RxBufPool; + typedef struct VIOsPAPRVLANDevice { VIOsPAPRDevice sdev; NICConf nicconf; @@ -93,6 +109,8 @@ typedef struct VIOsPAPRVLANDevice { target_ulong buf_list; uint32_t add_buf_ptr, use_buf_ptr, rx_bufs; target_ulong rxq_ptr; + uint32_t compat_flags; /* Compatability flags for migration */ + RxBufPool *rx_pool[RX_MAX_POOLS]; /* Receive buffer descriptor pools */ } VIOsPAPRVLANDevice; static int spapr_vlan_can_receive(NetClientState *nc) @@ -102,6 +120,73 @@ static int spapr_vlan_can_receive(NetClientState *nc) return (dev->isopen && dev->rx_bufs > 0); } +/** + * Get buffer descriptor from one of our receive buffer pools + */ +static vlan_bd_t spapr_vlan_get_rx_bd_from_pool(VIOsPAPRVLANDevice *dev, + size_t size) +{ + vlan_bd_t bd; + int pool; + + for (pool = 0; pool < RX_MAX_POOLS; pool++) { + if (dev->rx_pool[pool]->count > 0 && + dev->rx_pool[pool]->bufsize >= size + 8) { + break; + } + } + if (pool == RX_MAX_POOLS) { + /* Failed to find a suitable buffer */ + return 0; + } + + DPRINTF("Found buffer: pool=%d count=%d rxbufs=%d\n", pool, + dev->rx_pool[pool]->count, dev->rx_bufs); + + /* Remove the buffer from the pool */ + dev->rx_pool[pool]->count--; + bd = dev->rx_pool[pool]->bds[dev->rx_pool[pool]->count]; + dev->rx_pool[pool]->bds[dev->rx_pool[pool]->count] = 0; + + return bd; +} + +/** + * Get buffer descriptor from the receive buffer list page that has been + * supplied by the guest with the H_REGISTER_LOGICAL_LAN call + */ +static vlan_bd_t spapr_vlan_get_rx_bd_from_page(VIOsPAPRVLANDevice *dev, + size_t size) +{ + int buf_ptr = dev->use_buf_ptr; + vlan_bd_t bd; + + do { + buf_ptr += 8; + if (buf_ptr >= VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF) { + buf_ptr = VLAN_RX_BDS_OFF; + } + + bd = vio_ldq(&dev->sdev, dev->buf_list + buf_ptr); + DPRINTF("use_buf_ptr=%d bd=0x%016llx\n", + buf_ptr, (unsigned long long)bd); + } while ((!(bd & VLAN_BD_VALID) || VLAN_BD_LEN(bd) < size + 8) + && buf_ptr != dev->use_buf_ptr); + + if (!(bd & VLAN_BD_VALID) || VLAN_BD_LEN(bd) < size + 8) { + /* Failed to find a suitable buffer */ + return 0; + } + + /* Remove the buffer from the pool */ + dev->use_buf_ptr = buf_ptr; + vio_stq(&dev->sdev, dev->buf_list + dev->use_buf_ptr, 0); + + DPRINTF("Found buffer: ptr=%d rxbufs=%d\n", dev->use_buf_ptr, dev->rx_bufs); + + return bd; +} + static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, size_t size) { @@ -109,7 +194,6 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, VIOsPAPRDevice *sdev = VIO_SPAPR_DEVICE(dev); vlan_bd_t rxq_bd = vio_ldq(sdev, dev->buf_list + VLAN_RXQ_BD_OFF); vlan_bd_t bd; - int buf_ptr = dev->use_buf_ptr; uint64_t handle; uint8_t control; @@ -124,29 +208,16 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, return -1; } - do { - buf_ptr += 8; - if (buf_ptr >= (VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF)) { - buf_ptr = VLAN_RX_BDS_OFF; - } - - bd = vio_ldq(sdev, dev->buf_list + buf_ptr); - DPRINTF("use_buf_ptr=%d bd=0x%016llx\n", - buf_ptr, (unsigned long long)bd); - } while ((!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8))) - && (buf_ptr != dev->use_buf_ptr)); - - if (!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8))) { - /* Failed to find a suitable buffer */ + if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { + bd = spapr_vlan_get_rx_bd_from_pool(dev, size); + } else { + bd = spapr_vlan_get_rx_bd_from_page(dev, size); + } + if (!bd) { return -1; } - /* Remove the buffer from the pool */ dev->rx_bufs--; - dev->use_buf_ptr = buf_ptr; - vio_stq(sdev, dev->buf_list + dev->use_buf_ptr, 0); - - DPRINTF("Found buffer: ptr=%d num=%d\n", dev->use_buf_ptr, dev->rx_bufs); /* Transfer the packet data */ if (spapr_vio_dma_write(sdev, VLAN_BD_ADDR(bd) + 8, buf, size) < 0) { @@ -194,13 +265,31 @@ static NetClientInfo net_spapr_vlan_info = { .receive = spapr_vlan_receive, }; +static void spapr_vlan_reset_rx_pool(RxBufPool *rxp) +{ + /* + * Use INT_MAX as bufsize so that unused buffers are moved to the end + * of the list during the qsort in spapr_vlan_add_rxbuf_to_pool() later. + */ + rxp->bufsize = INT_MAX; + rxp->count = 0; + memset(rxp->bds, 0, sizeof(rxp->bds)); +} + static void spapr_vlan_reset(VIOsPAPRDevice *sdev) { VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); + int i; dev->buf_list = 0; dev->rx_bufs = 0; dev->isopen = 0; + + if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { + for (i = 0; i < RX_MAX_POOLS; i++) { + spapr_vlan_reset_rx_pool(dev->rx_pool[i]); + } + } } static void spapr_vlan_realize(VIOsPAPRDevice *sdev, Error **errp) @@ -217,10 +306,31 @@ static void spapr_vlan_realize(VIOsPAPRDevice *sdev, Error **errp) static void spapr_vlan_instance_init(Object *obj) { VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(obj); + int i; device_add_bootindex_property(obj, &dev->nicconf.bootindex, "bootindex", "", DEVICE(dev), NULL); + + if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { + for (i = 0; i < RX_MAX_POOLS; i++) { + dev->rx_pool[i] = g_new(RxBufPool, 1); + spapr_vlan_reset_rx_pool(dev->rx_pool[i]); + } + } +} + +static void spapr_vlan_instance_finalize(Object *obj) +{ + VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(obj); + int i; + + if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { + for (i = 0; i < RX_MAX_POOLS; i++) { + g_free(dev->rx_pool[i]); + dev->rx_pool[i] = NULL; + } + } } void spapr_vlan_create(VIOsPAPRBus *bus, NICInfo *nd) @@ -371,6 +481,113 @@ static target_ulong h_free_logical_lan(PowerPCCPU *cpu, return H_SUCCESS; } +/** + * Used for qsort, this function compares two RxBufPools by size. + */ +static int rx_pool_size_compare(const void *p1, const void *p2) +{ + const RxBufPool *pool1 = *(RxBufPool **)p1; + const RxBufPool *pool2 = *(RxBufPool **)p2; + + if (pool1->bufsize < pool2->bufsize) { + return -1; + } + return pool1->bufsize > pool2->bufsize; +} + +/** + * Search for a matching buffer pool with exact matching size, + * or return -1 if no matching pool has been found. + */ +static int spapr_vlan_get_rx_pool_id(VIOsPAPRVLANDevice *dev, int size) +{ + int pool; + + for (pool = 0; pool < RX_MAX_POOLS; pool++) { + if (dev->rx_pool[pool]->bufsize == size) { + return pool; + } + } + + return -1; +} + +/** + * Enqueuing receive buffer by adding it to one of our receive buffer pools + */ +static target_long spapr_vlan_add_rxbuf_to_pool(VIOsPAPRVLANDevice *dev, + target_ulong buf) +{ + int size = VLAN_BD_LEN(buf); + int pool; + + pool = spapr_vlan_get_rx_pool_id(dev, size); + if (pool < 0) { + /* + * No matching pool found? Try to use a new one. If the guest used all + * pools before, but changed the size of one pool inbetween, we might + * need to recycle that pool here (if it's empty already). Thus scan + * all buffer pools now, starting with the last (likely empty) one. + */ + for (pool = RX_MAX_POOLS - 1; pool >= 0 ; pool--) { + if (dev->rx_pool[pool]->count == 0) { + dev->rx_pool[pool]->bufsize = size; + /* + * Sort pools by size so that spapr_vlan_receive() + * can later find the smallest buffer pool easily. + */ + qsort(dev->rx_pool, RX_MAX_POOLS, sizeof(dev->rx_pool[0]), + rx_pool_size_compare); + pool = spapr_vlan_get_rx_pool_id(dev, size); + DPRINTF("created RX pool %d for size %lld\n", pool, + VLAN_BD_LEN(buf)); + break; + } + } + } + /* Still no usable pool? Give up */ + if (pool < 0 || dev->rx_pool[pool]->count >= RX_POOL_MAX_BDS) { + return H_RESOURCE; + } + + DPRINTF("h_add_llan_buf(): Add buf using pool %i (size %lli, count=%i)\n", + pool, VLAN_BD_LEN(buf), dev->rx_pool[pool]->count); + + dev->rx_pool[pool]->bds[dev->rx_pool[pool]->count++] = buf; + + return 0; +} + +/** + * This is the old way of enqueuing receive buffers: Add it to the rx queue + * page that has been supplied by the guest (which is quite limited in size). + */ +static target_long spapr_vlan_add_rxbuf_to_page(VIOsPAPRVLANDevice *dev, + target_ulong buf) +{ + vlan_bd_t bd; + + if (dev->rx_bufs >= VLAN_MAX_BUFS) { + return H_RESOURCE; + } + + do { + dev->add_buf_ptr += 8; + if (dev->add_buf_ptr >= VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF) { + dev->add_buf_ptr = VLAN_RX_BDS_OFF; + } + + bd = vio_ldq(&dev->sdev, dev->buf_list + dev->add_buf_ptr); + } while (bd & VLAN_BD_VALID); + + vio_stq(&dev->sdev, dev->buf_list + dev->add_buf_ptr, buf); + + DPRINTF("h_add_llan_buf(): Added buf ptr=%d rx_bufs=%d bd=0x%016llx\n", + dev->add_buf_ptr, dev->rx_bufs, (unsigned long long)buf); + + return 0; +} + static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, @@ -380,7 +597,7 @@ static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu, target_ulong buf = args[1]; VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); - vlan_bd_t bd; + target_long ret; DPRINTF("H_ADD_LOGICAL_LAN_BUFFER(0x" TARGET_FMT_lx ", 0x" TARGET_FMT_lx ")\n", reg, buf); @@ -396,29 +613,23 @@ static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu, return H_PARAMETER; } - if (!dev->isopen || dev->rx_bufs >= VLAN_MAX_BUFS) { + if (!dev->isopen) { return H_RESOURCE; } - do { - dev->add_buf_ptr += 8; - if (dev->add_buf_ptr >= (VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF)) { - dev->add_buf_ptr = VLAN_RX_BDS_OFF; - } - - bd = vio_ldq(sdev, dev->buf_list + dev->add_buf_ptr); - } while (bd & VLAN_BD_VALID); - - vio_stq(sdev, dev->buf_list + dev->add_buf_ptr, buf); + if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { + ret = spapr_vlan_add_rxbuf_to_pool(dev, buf); + } else { + ret = spapr_vlan_add_rxbuf_to_page(dev, buf); + } + if (ret) { + return ret; + } dev->rx_bufs++; qemu_flush_queued_packets(qemu_get_queue(dev->nic)); - DPRINTF("h_add_logical_lan_buffer(): Added buf ptr=%d rx_bufs=%d" - " bd=0x%016llx\n", dev->add_buf_ptr, dev->rx_bufs, - (unsigned long long)buf); - return H_SUCCESS; } @@ -508,9 +719,44 @@ static target_ulong h_multicast_ctrl(PowerPCCPU *cpu, sPAPRMachineState *spapr, static Property spapr_vlan_properties[] = { DEFINE_SPAPR_PROPERTIES(VIOsPAPRVLANDevice, sdev), DEFINE_NIC_PROPERTIES(VIOsPAPRVLANDevice, nicconf), + DEFINE_PROP_BIT("use-rx-buffer-pools", VIOsPAPRVLANDevice, + compat_flags, SPAPRVLAN_FLAG_RX_BUF_POOLS_BIT, true), DEFINE_PROP_END_OF_LIST(), }; +static bool spapr_vlan_rx_buffer_pools_needed(void *opaque) +{ + VIOsPAPRVLANDevice *dev = opaque; + + return (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) != 0; +} + +static const VMStateDescription vmstate_rx_buffer_pool = { + .name = "spapr_llan/rx_buffer_pool", + .version_id = 1, + .minimum_version_id = 1, + .needed = spapr_vlan_rx_buffer_pools_needed, + .fields = (VMStateField[]) { + VMSTATE_INT32(bufsize, RxBufPool), + VMSTATE_INT32(count, RxBufPool), + VMSTATE_UINT64_ARRAY(bds, RxBufPool, RX_POOL_MAX_BDS), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_rx_pools = { + .name = "spapr_llan/rx_pools", + .version_id = 1, + .minimum_version_id = 1, + .needed = spapr_vlan_rx_buffer_pools_needed, + .fields = (VMStateField[]) { + VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(rx_pool, VIOsPAPRVLANDevice, + RX_MAX_POOLS, 1, + vmstate_rx_buffer_pool, RxBufPool), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_spapr_llan = { .name = "spapr_llan", .version_id = 1, @@ -527,6 +773,10 @@ static const VMStateDescription vmstate_spapr_llan = { VMSTATE_END_OF_LIST() }, + .subsections = (const VMStateDescription * []) { + &vmstate_rx_pools, + NULL + } }; static void spapr_vlan_class_init(ObjectClass *klass, void *data) @@ -553,6 +803,7 @@ static const TypeInfo spapr_vlan_info = { .instance_size = sizeof(VIOsPAPRVLANDevice), .class_init = spapr_vlan_class_init, .instance_init = spapr_vlan_instance_init, + .instance_finalize = spapr_vlan_instance_finalize, }; static void spapr_vlan_register_types(void) diff --git a/qemu/hw/net/stellaris_enet.c b/qemu/hw/net/stellaris_enet.c index 21a47735d..688089494 100644 --- a/qemu/hw/net/stellaris_enet.c +++ b/qemu/hw/net/stellaris_enet.c @@ -6,6 +6,7 @@ * * This code is licensed under the GPL. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "net/net.h" #include @@ -235,8 +236,18 @@ static ssize_t stellaris_enet_receive(NetClientState *nc, const uint8_t *buf, si n = s->next_packet + s->np; if (n >= 31) n -= 31; - s->np++; + if (size >= sizeof(s->rx[n].data) - 6) { + /* If the packet won't fit into the + * emulated 2K RAM, this is reported + * as a FIFO overrun error. + */ + s->ris |= SE_INT_FOV; + stellaris_enet_update(s); + return -1; + } + + s->np++; s->rx[n].len = size + 6; p = s->rx[n].data; *(p++) = (size + 6); diff --git a/qemu/hw/net/vhost_net.c b/qemu/hw/net/vhost_net.c index 5c1d11f51..6e1032fc1 100644 --- a/qemu/hw/net/vhost_net.c +++ b/qemu/hw/net/vhost_net.c @@ -13,6 +13,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "net/net.h" #include "net/tap.h" #include "net/vhost-user.h" @@ -21,24 +22,20 @@ #include "net/vhost_net.h" #include "qemu/error-report.h" -#include "config.h" #ifdef CONFIG_VHOST_NET #include #include #include -#include #include #include #include #include -#include #include "standard-headers/linux/virtio_ring.h" #include "hw/virtio/vhost.h" #include "hw/virtio/virtio-bus.h" -#include "hw/virtio/virtio-access.h" struct vhost_net { struct vhost_dev dev; @@ -77,13 +74,9 @@ static const int user_feature_bits[] = { VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MRG_RXBUF, - VIRTIO_NET_F_STATUS, - VIRTIO_NET_F_CTRL_VQ, - VIRTIO_NET_F_CTRL_RX, - VIRTIO_NET_F_CTRL_VLAN, - VIRTIO_NET_F_CTRL_RX_EXTRA, - VIRTIO_NET_F_CTRL_MAC_ADDR, - VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, + + /* This bit implies RARP isn't sent by QEMU out of band */ + VIRTIO_NET_F_GUEST_ANNOUNCE, VIRTIO_NET_F_MQ, @@ -122,6 +115,11 @@ void vhost_net_ack_features(struct vhost_net *net, uint64_t features) vhost_ack_features(&net->dev, vhost_net_get_feature_bits(net), features); } +uint64_t vhost_net_get_max_queues(VHostNetState *net) +{ + return net->dev.max_queues; +} + static int vhost_net_get_fd(NetClientState *backend) { switch (backend->info->type) { @@ -143,6 +141,11 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) fprintf(stderr, "vhost-net requires net backend to be setup\n"); goto fail; } + net->nc = options->net_backend; + + net->dev.max_queues = 1; + net->dev.nvqs = 2; + net->dev.vqs = net->vqs; if (backend_kernel) { r = vhost_net_get_fd(options->net_backend); @@ -152,14 +155,15 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) net->dev.backend_features = qemu_has_vnet_hdr(options->net_backend) ? 0 : (1ULL << VHOST_NET_F_VIRTIO_NET_HDR); net->backend = r; + net->dev.protocol_features = 0; } else { net->dev.backend_features = 0; + net->dev.protocol_features = 0; net->backend = -1; - } - net->nc = options->net_backend; - net->dev.nvqs = 2; - net->dev.vqs = net->vqs; + /* vhost-user needs vq_index to initiate a specific queue pair */ + net->dev.vq_index = net->nc->queue_index * net->dev.nvqs; + } r = vhost_dev_init(&net->dev, options->opaque, options->backend_type); @@ -192,27 +196,6 @@ static void vhost_net_set_vq_index(struct vhost_net *net, int vq_index) net->dev.vq_index = vq_index; } -static int vhost_net_set_vnet_endian(VirtIODevice *dev, NetClientState *peer, - bool set) -{ - int r = 0; - - if (virtio_has_feature(dev, VIRTIO_F_VERSION_1) || - (virtio_legacy_is_cross_endian(dev) && !virtio_is_big_endian(dev))) { - r = qemu_set_vnet_le(peer, set); - if (r) { - error_report("backend does not support LE vnet headers"); - } - } else if (virtio_legacy_is_cross_endian(dev)) { - r = qemu_set_vnet_be(peer, set); - if (r) { - error_report("backend does not support BE vnet headers"); - } - } - - return r; -} - static int vhost_net_start_one(struct vhost_net *net, VirtIODevice *dev) { @@ -241,8 +224,7 @@ static int vhost_net_start_one(struct vhost_net *net, file.fd = net->backend; for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { const VhostOps *vhost_ops = net->dev.vhost_ops; - r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND, - &file); + r = vhost_ops->vhost_net_set_backend(&net->dev, &file); if (r < 0) { r = -errno; goto fail; @@ -255,8 +237,7 @@ fail: if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) { while (file.index-- > 0) { const VhostOps *vhost_ops = net->dev.vhost_ops; - int r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND, - &file); + int r = vhost_ops->vhost_net_set_backend(&net->dev, &file); assert(r >= 0); } } @@ -278,15 +259,7 @@ static void vhost_net_stop_one(struct vhost_net *net, if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) { for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { const VhostOps *vhost_ops = net->dev.vhost_ops; - int r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND, - &file); - assert(r >= 0); - } - } else if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) { - for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { - const VhostOps *vhost_ops = net->dev.vhost_ops; - int r = vhost_ops->vhost_call(&net->dev, VHOST_RESET_OWNER, - NULL); + int r = vhost_ops->vhost_net_set_backend(&net->dev, &file); assert(r >= 0); } } @@ -307,23 +280,28 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, if (!k->set_guest_notifiers) { error_report("binding does not support guest notifiers"); - r = -ENOSYS; - goto err; - } - - r = vhost_net_set_vnet_endian(dev, ncs[0].peer, true); - if (r < 0) { - goto err; + return -ENOSYS; } for (i = 0; i < total_queues; i++) { - vhost_net_set_vq_index(get_vhost_net(ncs[i].peer), i * 2); - } + struct vhost_net *net; + + net = get_vhost_net(ncs[i].peer); + vhost_net_set_vq_index(net, i * 2); + + /* Suppress the masking guest notifiers on vhost user + * because vhost user doesn't interrupt masking/unmasking + * properly. + */ + if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) { + dev->use_guest_notifier_mask = false; + } + } r = k->set_guest_notifiers(qbus->parent, total_queues * 2, true); if (r < 0) { error_report("Error binding guest notifier: %d", -r); - goto err_endian; + goto err; } for (i = 0; i < total_queues; i++) { @@ -345,8 +323,6 @@ err_start: fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", e); fflush(stderr); } -err_endian: - vhost_net_set_vnet_endian(dev, ncs[0].peer, false); err: return r; } @@ -369,8 +345,6 @@ void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, fflush(stderr); } assert(r >= 0); - - assert(vhost_net_set_vnet_endian(dev, ncs[0].peer, false) >= 0); } void vhost_net_cleanup(struct vhost_net *net) @@ -379,6 +353,18 @@ void vhost_net_cleanup(struct vhost_net *net) g_free(net); } +int vhost_net_notify_migration_done(struct vhost_net *net, char* mac_addr) +{ + const VhostOps *vhost_ops = net->dev.vhost_ops; + int r = -1; + + if (vhost_ops->vhost_migration_done) { + r = vhost_ops->vhost_migration_done(&net->dev, mac_addr); + } + + return r; +} + bool vhost_net_virtqueue_pending(VHostNetState *net, int idx) { return vhost_virtqueue_pending(&net->dev, idx); @@ -411,7 +397,25 @@ VHostNetState *get_vhost_net(NetClientState *nc) return vhost_net; } + +int vhost_set_vring_enable(NetClientState *nc, int enable) +{ + VHostNetState *net = get_vhost_net(nc); + const VhostOps *vhost_ops = net->dev.vhost_ops; + + if (vhost_ops->vhost_set_vring_enable) { + return vhost_ops->vhost_set_vring_enable(&net->dev, enable); + } + + return 0; +} + #else +uint64_t vhost_net_get_max_queues(VHostNetState *net) +{ + return 1; +} + struct vhost_net *vhost_net_init(VhostNetOptions *options) { error_report("vhost-net support is not compiled in"); @@ -452,8 +456,18 @@ void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, { } +int vhost_net_notify_migration_done(struct vhost_net *net, char* mac_addr) +{ + return -1; +} + VHostNetState *get_vhost_net(NetClientState *nc) { return 0; } + +int vhost_set_vring_enable(NetClientState *nc, int enable) +{ + return 0; +} #endif diff --git a/qemu/hw/net/virtio-net.c b/qemu/hw/net/virtio-net.c index 151083954..5798f87d8 100644 --- a/qemu/hw/net/virtio-net.c +++ b/qemu/hw/net/virtio-net.c @@ -11,6 +11,7 @@ * */ +#include "qemu/osdep.h" #include "qemu/iov.h" #include "hw/virtio/virtio.h" #include "net/net.h" @@ -86,8 +87,8 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config) memcpy(&netcfg, config, n->config_size); - if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) && - !virtio_has_feature(vdev, VIRTIO_F_VERSION_1) && + if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) && + !virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1) && memcmp(netcfg.mac, n->mac, ETH_ALEN)) { memcpy(n->mac, netcfg.mac, ETH_ALEN); qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); @@ -128,6 +129,13 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) if (!n->vhost_started) { int r, i; + if (n->needs_vnet_hdr_swap) { + error_report("backend does not support %s vnet headers; " + "falling back on userspace virtio", + virtio_is_big_endian(vdev) ? "BE" : "LE"); + return; + } + /* Any packets outstanding? Purge them to avoid touching rings * when vhost is running. */ @@ -152,6 +160,59 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) } } +static int virtio_net_set_vnet_endian_one(VirtIODevice *vdev, + NetClientState *peer, + bool enable) +{ + if (virtio_is_big_endian(vdev)) { + return qemu_set_vnet_be(peer, enable); + } else { + return qemu_set_vnet_le(peer, enable); + } +} + +static bool virtio_net_set_vnet_endian(VirtIODevice *vdev, NetClientState *ncs, + int queues, bool enable) +{ + int i; + + for (i = 0; i < queues; i++) { + if (virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, enable) < 0 && + enable) { + while (--i >= 0) { + virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, false); + } + + return true; + } + } + + return false; +} + +static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(n); + int queues = n->multiqueue ? n->max_queues : 1; + + if (virtio_net_started(n, status)) { + /* Before using the device, we tell the network backend about the + * endianness to use when parsing vnet headers. If the backend + * can't do it, we fallback onto fixing the headers in the core + * virtio-net code. + */ + n->needs_vnet_hdr_swap = virtio_net_set_vnet_endian(vdev, n->nic->ncs, + queues, true); + } else if (virtio_net_started(n, vdev->status)) { + /* After using the device, we need to reset the network backend to + * the default (guest native endianness), otherwise the guest may + * lose network connectivity if it is rebooted into a different + * endianness. + */ + virtio_net_set_vnet_endian(vdev, n->nic->ncs, queues, false); + } +} + static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) { VirtIONet *n = VIRTIO_NET(vdev); @@ -159,6 +220,7 @@ static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) int i; uint8_t queue_status; + virtio_net_vnet_endian_status(n, status); virtio_net_vhost_status(n, status); for (i = 0; i < n->max_queues; i++) { @@ -304,7 +366,7 @@ static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc) info->multicast_table = str_list; info->vlan_table = get_vlan_table(n); - if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VLAN)) { + if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VLAN)) { info->vlan = RX_STATE_ALL; } else if (!info->vlan_table) { info->vlan = RX_STATE_NONE; @@ -406,6 +468,10 @@ static int peer_attach(VirtIONet *n, int index) return 0; } + if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) { + vhost_set_vring_enable(nc->peer, 1); + } + if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { return 0; } @@ -421,6 +487,10 @@ static int peer_detach(VirtIONet *n, int index) return 0; } + if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) { + vhost_set_vring_enable(nc->peer, 0); + } + if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { return 0; } @@ -529,13 +599,13 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) int i; virtio_net_set_multiqueue(n, - __virtio_has_feature(features, VIRTIO_NET_F_MQ)); + virtio_has_feature(features, VIRTIO_NET_F_MQ)); virtio_net_set_mrg_rx_bufs(n, - __virtio_has_feature(features, - VIRTIO_NET_F_MRG_RXBUF), - __virtio_has_feature(features, - VIRTIO_F_VERSION_1)); + virtio_has_feature(features, + VIRTIO_NET_F_MRG_RXBUF), + virtio_has_feature(features, + VIRTIO_F_VERSION_1)); if (n->has_vnet_hdr) { n->curr_guest_offloads = @@ -552,7 +622,7 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) vhost_net_ack_features(get_vhost_net(nc->peer), features); } - if (__virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) { + if (virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) { memset(n->vlans, 0, MAX_VLAN >> 3); } else { memset(n->vlans, 0xff, MAX_VLAN >> 3); @@ -599,7 +669,7 @@ static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd, uint64_t offloads; size_t s; - if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { + if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { return VIRTIO_NET_ERR; } @@ -810,20 +880,24 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) VirtIONet *n = VIRTIO_NET(vdev); struct virtio_net_ctrl_hdr ctrl; virtio_net_ctrl_ack status = VIRTIO_NET_ERR; - VirtQueueElement elem; + VirtQueueElement *elem; size_t s; struct iovec *iov, *iov2; unsigned int iov_cnt; - while (virtqueue_pop(vq, &elem)) { - if (iov_size(elem.in_sg, elem.in_num) < sizeof(status) || - iov_size(elem.out_sg, elem.out_num) < sizeof(ctrl)) { + for (;;) { + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!elem) { + break; + } + if (iov_size(elem->in_sg, elem->in_num) < sizeof(status) || + iov_size(elem->out_sg, elem->out_num) < sizeof(ctrl)) { error_report("virtio-net ctrl missing headers"); exit(1); } - iov_cnt = elem.out_num; - iov2 = iov = g_memdup(elem.out_sg, sizeof(struct iovec) * elem.out_num); + iov_cnt = elem->out_num; + iov2 = iov = g_memdup(elem->out_sg, sizeof(struct iovec) * elem->out_num); s = iov_to_buf(iov, iov_cnt, 0, &ctrl, sizeof(ctrl)); iov_discard_front(&iov, &iov_cnt, sizeof(ctrl)); if (s != sizeof(ctrl)) { @@ -842,12 +916,13 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) status = virtio_net_handle_offloads(n, ctrl.cmd, iov, iov_cnt); } - s = iov_from_buf(elem.in_sg, elem.in_num, 0, &status, sizeof(status)); + s = iov_from_buf(elem->in_sg, elem->in_num, 0, &status, sizeof(status)); assert(s == sizeof(status)); - virtqueue_push(vq, &elem, sizeof(status)); + virtqueue_push(vq, elem, sizeof(status)); virtio_notify(vdev, vq); g_free(iov2); + g_free(elem); } } @@ -949,7 +1024,10 @@ static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt, void *wbuf = (void *)buf; work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len, size - n->host_hdr_len); - virtio_net_hdr_swap(VIRTIO_DEVICE(n), wbuf); + + if (n->needs_vnet_hdr_swap) { + virtio_net_hdr_swap(VIRTIO_DEVICE(n), wbuf); + } iov_from_buf(iov, iov_cnt, 0, buf, sizeof(struct virtio_net_hdr)); } else { struct virtio_net_hdr hdr = { @@ -1036,13 +1114,14 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t offset = i = 0; while (offset < size) { - VirtQueueElement elem; + VirtQueueElement *elem; int len, total; - const struct iovec *sg = elem.in_sg; + const struct iovec *sg; total = 0; - if (virtqueue_pop(q->rx_vq, &elem) == 0) { + elem = virtqueue_pop(q->rx_vq, sizeof(VirtQueueElement)); + if (!elem) { if (i == 0) return -1; error_report("virtio-net unexpected empty queue: " @@ -1055,21 +1134,22 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t exit(1); } - if (elem.in_num < 1) { + if (elem->in_num < 1) { error_report("virtio-net receive queue contains no in buffers"); exit(1); } + sg = elem->in_sg; if (i == 0) { assert(offset == 0); if (n->mergeable_rx_bufs) { mhdr_cnt = iov_copy(mhdr_sg, ARRAY_SIZE(mhdr_sg), - sg, elem.in_num, + sg, elem->in_num, offsetof(typeof(mhdr), num_buffers), sizeof(mhdr.num_buffers)); } - receive_header(n, sg, elem.in_num, buf, size); + receive_header(n, sg, elem->in_num, buf, size); offset = n->host_hdr_len; total += n->guest_hdr_len; guest_offset = n->guest_hdr_len; @@ -1078,7 +1158,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t } /* copy in packet. ugh */ - len = iov_from_buf(sg, elem.in_num, guest_offset, + len = iov_from_buf(sg, elem->in_num, guest_offset, buf + offset, size - offset); total += len; offset += len; @@ -1086,18 +1166,14 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t * must have consumed the complete packet. * Otherwise, drop it. */ if (!n->mergeable_rx_bufs && offset < size) { -#if 0 - error_report("virtio-net truncated non-mergeable packet: " - "i %zd mergeable %d offset %zd, size %zd, " - "guest hdr len %zd, host hdr len %zd", - i, n->mergeable_rx_bufs, - offset, size, n->guest_hdr_len, n->host_hdr_len); -#endif + virtqueue_discard(q->rx_vq, elem, total); + g_free(elem); return size; } /* signal other side */ - virtqueue_fill(q->rx_vq, &elem, total, i++); + virtqueue_fill(q->rx_vq, elem, total, i++); + g_free(elem); } if (mhdr_cnt) { @@ -1121,10 +1197,11 @@ static void virtio_net_tx_complete(NetClientState *nc, ssize_t len) VirtIONetQueue *q = virtio_net_get_subqueue(nc); VirtIODevice *vdev = VIRTIO_DEVICE(n); - virtqueue_push(q->tx_vq, &q->async_tx.elem, 0); + virtqueue_push(q->tx_vq, q->async_tx.elem, 0); virtio_notify(vdev, q->tx_vq); - q->async_tx.elem.out_num = q->async_tx.len = 0; + g_free(q->async_tx.elem); + q->async_tx.elem = NULL; virtio_queue_set_notification(q->tx_vq, 1); virtio_net_flush_tx(q); @@ -1135,25 +1212,31 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) { VirtIONet *n = q->n; VirtIODevice *vdev = VIRTIO_DEVICE(n); - VirtQueueElement elem; + VirtQueueElement *elem; int32_t num_packets = 0; int queue_index = vq2q(virtio_get_queue_index(q->tx_vq)); if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { return num_packets; } - if (q->async_tx.elem.out_num) { + if (q->async_tx.elem) { virtio_queue_set_notification(q->tx_vq, 0); return num_packets; } - while (virtqueue_pop(q->tx_vq, &elem)) { - ssize_t ret, len; - unsigned int out_num = elem.out_num; - struct iovec *out_sg = &elem.out_sg[0]; - struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1]; + for (;;) { + ssize_t ret; + unsigned int out_num; + struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1], *out_sg; struct virtio_net_hdr_mrg_rxbuf mhdr; + elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement)); + if (!elem) { + break; + } + + out_num = elem->out_num; + out_sg = elem->out_sg; if (out_num < 1) { error_report("virtio-net header not in first element"); exit(1); @@ -1165,7 +1248,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) error_report("virtio-net header incorrect"); exit(1); } - if (virtio_needs_swap(vdev)) { + if (n->needs_vnet_hdr_swap) { virtio_net_hdr_swap(vdev, (void *) &mhdr); sg2[0].iov_base = &mhdr; sg2[0].iov_len = n->guest_hdr_len; @@ -1196,21 +1279,18 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) out_sg = sg; } - len = n->guest_hdr_len; - ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index), out_sg, out_num, virtio_net_tx_complete); if (ret == 0) { virtio_queue_set_notification(q->tx_vq, 0); q->async_tx.elem = elem; - q->async_tx.len = len; return -EBUSY; } - len += ret; drop: - virtqueue_push(q->tx_vq, &elem, 0); + virtqueue_push(q->tx_vq, elem, 0); virtio_notify(vdev, q->tx_vq); + g_free(elem); if (++num_packets >= n->tx_burst) { break; @@ -1453,7 +1533,7 @@ static void virtio_net_save_device(VirtIODevice *vdev, QEMUFile *f) } } - if (virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { qemu_put_be64(f, n->curr_guest_offloads); } } @@ -1462,11 +1542,33 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) { VirtIONet *n = opaque; VirtIODevice *vdev = VIRTIO_DEVICE(n); + int ret; if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION) return -EINVAL; - return virtio_load(vdev, f, version_id); + ret = virtio_load(vdev, f, version_id); + if (ret) { + return ret; + } + + if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { + n->curr_guest_offloads = qemu_get_be64(f); + } else { + n->curr_guest_offloads = virtio_net_supported_guest_offloads(n); + } + + if (peer_has_vnet_hdr(n)) { + virtio_net_apply_guest_offloads(n); + } + + if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) && + virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) { + n->announce_counter = SELF_ANNOUNCE_ROUNDS; + timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)); + } + + return 0; } static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, @@ -1479,7 +1581,8 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, n->vqs[0].tx_waiting = qemu_get_be32(f); virtio_net_set_mrg_rx_bufs(n, qemu_get_be32(f), - virtio_has_feature(vdev, VIRTIO_F_VERSION_1)); + virtio_vdev_has_feature(vdev, + VIRTIO_F_VERSION_1)); if (version_id >= 3) n->status = qemu_get_be16(f); @@ -1562,16 +1665,6 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, } } - if (virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { - n->curr_guest_offloads = qemu_get_be64(f); - } else { - n->curr_guest_offloads = virtio_net_supported_guest_offloads(n); - } - - if (peer_has_vnet_hdr(n)) { - virtio_net_apply_guest_offloads(n); - } - virtio_net_set_queues(n); /* Find the first multicast entry in the saved MAC filter */ @@ -1589,12 +1682,6 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, qemu_get_subqueue(n->nic, i)->link_down = link_down; } - if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) && - virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) { - n->announce_counter = SELF_ANNOUNCE_ROUNDS; - timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)); - } - return 0; } diff --git a/qemu/hw/net/vmware_utils.h b/qemu/hw/net/vmware_utils.h index 1099df669..c0dbb2ff4 100644 --- a/qemu/hw/net/vmware_utils.h +++ b/qemu/hw/net/vmware_utils.h @@ -18,10 +18,7 @@ #define VMWARE_UTILS_H #include "qemu/range.h" - -#ifndef VMW_SHPRN -#define VMW_SHPRN(fmt, ...) do {} while (0) -#endif +#include "vmxnet_debug.h" /* * Shared memory access functions with byte swap support @@ -52,7 +49,7 @@ vmw_shmem_rw(hwaddr addr, void *buf, int len, int is_write) } static inline void -vmw_shmem_set(hwaddr addr, uint8 val, int len) +vmw_shmem_set(hwaddr addr, uint8_t val, int len) { int i; VMW_SHPRN("SHMEM set: %" PRIx64 ", len: %d (value 0x%X)", addr, len, val); diff --git a/qemu/hw/net/vmxnet3.c b/qemu/hw/net/vmxnet3.c index 59b06b841..093a71e12 100644 --- a/qemu/hw/net/vmxnet3.c +++ b/qemu/hw/net/vmxnet3.c @@ -15,6 +15,7 @@ * */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "net/net.h" @@ -36,12 +37,28 @@ #define VMXNET3_MSIX_BAR_SIZE 0x2000 #define MIN_BUF_SIZE 60 +/* Compatability flags for migration */ +#define VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT 0 +#define VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS \ + (1 << VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT) +#define VMXNET3_COMPAT_FLAG_DISABLE_PCIE_BIT 1 +#define VMXNET3_COMPAT_FLAG_DISABLE_PCIE \ + (1 << VMXNET3_COMPAT_FLAG_DISABLE_PCIE_BIT) + +#define VMXNET3_EXP_EP_OFFSET (0x48) +#define VMXNET3_MSI_OFFSET(s) \ + ((s)->compat_flags & VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS ? 0x50 : 0x84) +#define VMXNET3_MSIX_OFFSET(s) \ + ((s)->compat_flags & VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS ? 0 : 0x9c) +#define VMXNET3_DSN_OFFSET (0x100) + #define VMXNET3_BAR0_IDX (0) #define VMXNET3_BAR1_IDX (1) #define VMXNET3_MSIX_BAR_IDX (2) #define VMXNET3_OFF_MSIX_TABLE (0x000) -#define VMXNET3_OFF_MSIX_PBA (0x800) +#define VMXNET3_OFF_MSIX_PBA(s) \ + ((s)->compat_flags & VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS ? 0x800 : 0x1000) /* Link speed in Mbps should be shifted by 16 */ #define VMXNET3_LINK_SPEED (1000 << 16) @@ -50,7 +67,7 @@ #define VMXNET3_LINK_STATUS_UP 0x1 /* Least significant bit should be set for revision and version */ -#define VMXNET3_DEVICE_VERSION 0x1 +#define VMXNET3_UPT_REVISION 0x1 #define VMXNET3_DEVICE_REVISION 0x1 /* Number of interrupt vectors for non-MSIx modes */ @@ -108,9 +125,19 @@ #define VMXNET_FLAG_IS_SET(field, flag) (((field) & (flag)) == (flag)) +typedef struct VMXNET3Class { + PCIDeviceClass parent_class; + DeviceRealize parent_dc_realize; +} VMXNET3Class; + #define TYPE_VMXNET3 "vmxnet3" #define VMXNET3(obj) OBJECT_CHECK(VMXNET3State, (obj), TYPE_VMXNET3) +#define VMXNET3_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(VMXNET3Class, (klass), TYPE_VMXNET3) +#define VMXNET3_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VMXNET3Class, (obj), TYPE_VMXNET3) + /* Cyclic ring abstraction */ typedef struct { hwaddr pa; @@ -138,7 +165,7 @@ static inline void vmxnet3_ring_init(Vmxnet3Ring *ring, } #define VMXNET3_RING_DUMP(macro, ring_name, ridx, r) \ - macro("%s#%d: base %" PRIx64 " size %lu cell_size %lu gen %d next %lu", \ + macro("%s#%d: base %" PRIx64 " size %zu cell_size %zu gen %d next %zu", \ (ring_name), (ridx), \ (r)->pa, (r)->size, (r)->cell_size, (r)->gen, (r)->next) @@ -313,6 +340,9 @@ typedef struct { MACAddr *mcast_list; uint32_t mcast_list_len; uint32_t mcast_list_buff_size; /* needed for live migration. */ + + /* Compatability flags for migration */ + uint32_t compat_flags; } VMXNET3State; /* Interrupt management */ @@ -493,7 +523,7 @@ vmxnet3_dec_rx_completion_counter(VMXNET3State *s, int qidx) vmxnet3_ring_dec(&s->rxq_descr[qidx].comp_ring); } -static void vmxnet3_complete_packet(VMXNET3State *s, int qidx, uint32 tx_ridx) +static void vmxnet3_complete_packet(VMXNET3State *s, int qidx, uint32_t tx_ridx) { struct Vmxnet3_TxCompDesc txcq_descr; @@ -729,9 +759,7 @@ static void vmxnet3_process_tx_queue(VMXNET3State *s, int qidx) } if (txd.eop) { - if (!s->skip_current_tx_pkt) { - vmxnet_tx_pkt_parse(s->tx_pkt); - + if (!s->skip_current_tx_pkt && vmxnet_tx_pkt_parse(s->tx_pkt)) { if (s->needs_vlan) { vmxnet_tx_pkt_setup_vlan_header(s->tx_pkt, s->tci); } @@ -927,9 +955,9 @@ static void vmxnet3_rx_need_csum_calculate(struct VmxnetRxPkt *pkt, /* Validate packet len: csum_start + scum_offset + length of csum field */ if (pkt_len < (vhdr->csum_start + vhdr->csum_offset + 2)) { - VMW_PKPRN("packet len:%d < csum_start(%d) + csum_offset(%d) + 2, " + VMW_PKPRN("packet len:%zu < csum_start(%d) + csum_offset(%d) + 2, " "cannot calculate checksum", - len, vhdr->csum_start, vhdr->csum_offset); + pkt_len, vhdr->csum_start, vhdr->csum_offset); return; } @@ -1165,9 +1193,13 @@ vmxnet3_io_bar0_write(void *opaque, hwaddr addr, static uint64_t vmxnet3_io_bar0_read(void *opaque, hwaddr addr, unsigned size) { + VMXNET3State *s = opaque; + if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_IMR, VMXNET3_MAX_INTRS, VMXNET3_REG_ALIGN)) { - g_assert_not_reached(); + int l = VMW_MULTIREG_IDX_BY_ADDR(addr, VMXNET3_REG_IMR, + VMXNET3_REG_ALIGN); + return s->interrupt_states[l].is_masked; } VMW_CBPRN("BAR0 unknown read [%" PRIx64 "], size %d", addr, size); @@ -1192,8 +1224,13 @@ static void vmxnet3_reset_mac(VMXNET3State *s) static void vmxnet3_deactivate_device(VMXNET3State *s) { - VMW_CBPRN("Deactivating vmxnet3..."); - s->device_active = false; + if (s->device_active) { + VMW_CBPRN("Deactivating vmxnet3..."); + vmxnet_tx_pkt_reset(s->tx_pkt); + vmxnet_tx_pkt_uninit(s->tx_pkt); + vmxnet_rx_pkt_uninit(s->rx_pkt); + s->device_active = false; + } } static void vmxnet3_reset(VMXNET3State *s) @@ -1202,7 +1239,6 @@ static void vmxnet3_reset(VMXNET3State *s) vmxnet3_deactivate_device(s); vmxnet3_reset_interrupt_states(s); - vmxnet_tx_pkt_reset(s->tx_pkt); s->drv_shmem = 0; s->tx_sop = true; s->skip_current_tx_pkt = false; @@ -1287,6 +1323,10 @@ static uint32_t vmxnet3_get_interrupt_config(VMXNET3State *s) static void vmxnet3_fill_stats(VMXNET3State *s) { int i; + + if (!s->device_active) + return; + for (i = 0; i < s->txq_num; i++) { cpu_physical_memory_write(s->txq_descr[i].tx_stats_pa, &s->txq_descr[i].txq_stats, @@ -1425,6 +1465,12 @@ static void vmxnet3_activate_device(VMXNET3State *s) return; } + /* Verify if device is active */ + if (s->device_active) { + VMW_CFPRN("Vmxnet3 device is active"); + return; + } + vmxnet3_adjust_by_guest_type(s); vmxnet3_update_features(s); vmxnet3_update_pm_state(s); @@ -1621,7 +1667,7 @@ static void vmxnet3_handle_command(VMXNET3State *s, uint64_t cmd) break; case VMXNET3_CMD_QUIESCE_DEV: - VMW_CBPRN("Set: VMXNET3_CMD_QUIESCE_DEV - pause the device"); + VMW_CBPRN("Set: VMXNET3_CMD_QUIESCE_DEV - deactivate the device"); vmxnet3_deactivate_device(s); break; @@ -1629,6 +1675,23 @@ static void vmxnet3_handle_command(VMXNET3State *s, uint64_t cmd) VMW_CBPRN("Set: VMXNET3_CMD_GET_CONF_INTR - interrupt configuration"); break; + case VMXNET3_CMD_GET_ADAPTIVE_RING_INFO: + VMW_CBPRN("Set: VMXNET3_CMD_GET_ADAPTIVE_RING_INFO - " + "adaptive ring info flags"); + break; + + case VMXNET3_CMD_GET_DID_LO: + VMW_CBPRN("Set: Get lower part of device ID"); + break; + + case VMXNET3_CMD_GET_DID_HI: + VMW_CBPRN("Set: Get upper part of device ID"); + break; + + case VMXNET3_CMD_GET_DEV_EXTRA_INFO: + VMW_CBPRN("Set: Get device extra info"); + break; + default: VMW_CBPRN("Received unknown command: %" PRIx64, cmd); break; @@ -1641,13 +1704,14 @@ static uint64_t vmxnet3_get_command_status(VMXNET3State *s) switch (s->last_command) { case VMXNET3_CMD_ACTIVATE_DEV: - ret = (s->device_active) ? 0 : -1; + ret = (s->device_active) ? 0 : 1; VMW_CFPRN("Device active: %" PRIx64, ret); break; case VMXNET3_CMD_RESET_DEV: case VMXNET3_CMD_QUIESCE_DEV: case VMXNET3_CMD_GET_QUEUE_STATUS: + case VMXNET3_CMD_GET_DEV_EXTRA_INFO: ret = 0; break; @@ -1668,9 +1732,21 @@ static uint64_t vmxnet3_get_command_status(VMXNET3State *s) ret = vmxnet3_get_interrupt_config(s); break; + case VMXNET3_CMD_GET_ADAPTIVE_RING_INFO: + ret = VMXNET3_DISABLE_ADAPTIVE_RING; + break; + + case VMXNET3_CMD_GET_DID_LO: + ret = PCI_DEVICE_ID_VMWARE_VMXNET3; + break; + + case VMXNET3_CMD_GET_DID_HI: + ret = VMXNET3_DEVICE_REVISION; + break; + default: VMW_WRPRN("Received request for unknown command: %x", s->last_command); - ret = -1; + ret = 0; break; } @@ -1726,7 +1802,7 @@ vmxnet3_io_bar1_write(void *opaque, * shared address only after we get the high part */ if (val == 0) { - s->device_active = false; + vmxnet3_deactivate_device(s); } s->temp_shared_guest_driver_memory = val; s->drv_shmem = 0; @@ -1801,7 +1877,7 @@ vmxnet3_io_bar1_read(void *opaque, hwaddr addr, unsigned size) /* UPT Version Report Selection */ case VMXNET3_REG_UVRS: VMW_CBPRN("Read BAR1 [VMXNET3_REG_UVRS], size %d", size); - ret = VMXNET3_DEVICE_VERSION; + ret = VMXNET3_UPT_REVISION; break; /* Command */ @@ -1959,7 +2035,7 @@ vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size) vmxnet_rx_pkt_attach_data(s->rx_pkt, buf, size, s->rx_vlan_stripping); bytes_indicated = vmxnet3_indicate_packet(s) ? size : -1; if (bytes_indicated < size) { - VMW_PKPRN("RX: %lu of %lu bytes indicated", bytes_indicated, size); + VMW_PKPRN("RX: %zu of %zu bytes indicated", bytes_indicated, size); } } else { VMW_PKPRN("Packet dropped by RX filter"); @@ -1988,7 +2064,6 @@ static void vmxnet3_set_link_status(NetClientState *nc) static NetClientInfo net_vmxnet3_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), - .can_receive = vmxnet3_can_receive, .receive = vmxnet3_receive, .link_status_changed = vmxnet3_set_link_status, }; @@ -2001,16 +2076,13 @@ static bool vmxnet3_peer_has_vnet_hdr(VMXNET3State *s) return true; } - VMW_WRPRN("Peer has no virtio extension. Task offloads will be emulated."); return false; } static void vmxnet3_net_uninit(VMXNET3State *s) { g_free(s->mcast_list); - vmxnet_tx_pkt_reset(s->tx_pkt); - vmxnet_tx_pkt_uninit(s->tx_pkt); - vmxnet_rx_pkt_uninit(s->rx_pkt); + vmxnet3_deactivate_device(s); qemu_del_nic(s->nic); } @@ -2030,7 +2102,7 @@ static void vmxnet3_net_init(VMXNET3State *s) s->link_status_and_speed = VMXNET3_LINK_SPEED | VMXNET3_LINK_STATUS_UP; - VMW_CFPRN("Permanent MAC: " MAC_FMT, MAC_ARG(s->perm_mac.a)); + VMW_CFPRN("Permanent MAC: " VMXNET_MF, VMXNET_MA(s->perm_mac.a)); s->nic = qemu_new_nic(&net_vmxnet3_info, &s->conf, object_get_typename(OBJECT(s)), @@ -2088,8 +2160,8 @@ vmxnet3_init_msix(VMXNET3State *s) &s->msix_bar, VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_TABLE, &s->msix_bar, - VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_PBA, - 0); + VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_PBA(s), + VMXNET3_MSIX_OFFSET(s)); if (0 > res) { VMW_WRPRN("Failed to initialize MSI-X, error %d", res); @@ -2117,7 +2189,6 @@ vmxnet3_cleanup_msix(VMXNET3State *s) } } -#define VMXNET3_MSI_OFFSET (0x50) #define VMXNET3_USE_64BIT (true) #define VMXNET3_PER_VECTOR_MASK (false) @@ -2127,7 +2198,7 @@ vmxnet3_init_msi(VMXNET3State *s) PCIDevice *d = PCI_DEVICE(s); int res; - res = msi_init(d, VMXNET3_MSI_OFFSET, VMXNET3_MAX_NMSIX_INTRS, + res = msi_init(d, VMXNET3_MSI_OFFSET(s), VMXNET3_MAX_NMSIX_INTRS, VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK); if (0 > res) { VMW_WRPRN("Failed to initialize MSI, error %d", res); @@ -2184,6 +2255,22 @@ static const MemoryRegionOps b1_ops = { }, }; +static uint8_t *vmxnet3_device_serial_num(VMXNET3State *s) +{ + static uint64_t dsn_payload; + uint8_t *dsnp = (uint8_t *)&dsn_payload; + + dsnp[0] = 0xfe; + dsnp[1] = s->conf.macaddr.a[3]; + dsnp[2] = s->conf.macaddr.a[4]; + dsnp[3] = s->conf.macaddr.a[5]; + dsnp[4] = s->conf.macaddr.a[0]; + dsnp[5] = s->conf.macaddr.a[1]; + dsnp[6] = s->conf.macaddr.a[2]; + dsnp[7] = 0xff; + return dsnp; +} + static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp) { DeviceState *dev = DEVICE(pci_dev); @@ -2221,6 +2308,17 @@ static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp) vmxnet3_net_init(s); + if (pci_is_express(pci_dev)) { + if (pci_bus_is_express(pci_dev->bus)) { + pcie_endpoint_cap_init(pci_dev, VMXNET3_EXP_EP_OFFSET); + } + + pcie_add_capability(pci_dev, PCI_EXT_CAP_ID_DSN, 0x1, + VMXNET3_DSN_OFFSET, PCI_EXT_CAP_DSN_SIZEOF); + memcpy(pci_dev->config + VMXNET3_DSN_OFFSET + 4, + vmxnet3_device_serial_num(s), sizeof(uint64_t)); + } + register_savevm(dev, "vmxnet3-msix", -1, 1, vmxnet3_msix_save, vmxnet3_msix_load, s); } @@ -2490,6 +2588,29 @@ static const VMStateInfo int_state_info = { .put = vmxnet3_put_int_state }; +static bool vmxnet3_vmstate_need_pcie_device(void *opaque) +{ + VMXNET3State *s = VMXNET3(opaque); + + return !(s->compat_flags & VMXNET3_COMPAT_FLAG_DISABLE_PCIE); +} + +static bool vmxnet3_vmstate_test_pci_device(void *opaque, int version_id) +{ + return !vmxnet3_vmstate_need_pcie_device(opaque); +} + +static const VMStateDescription vmstate_vmxnet3_pcie_device = { + .name = "vmxnet3/pcie", + .version_id = 1, + .minimum_version_id = 1, + .needed = vmxnet3_vmstate_need_pcie_device, + .fields = (VMStateField[]) { + VMSTATE_PCIE_DEVICE(parent_obj, VMXNET3State), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_vmxnet3 = { .name = "vmxnet3", .version_id = 1, @@ -2497,7 +2618,9 @@ static const VMStateDescription vmstate_vmxnet3 = { .pre_save = vmxnet3_pre_save, .post_load = vmxnet3_post_load, .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, VMXNET3State), + VMSTATE_STRUCT_TEST(parent_obj, VMXNET3State, + vmxnet3_vmstate_test_pci_device, 0, + vmstate_pci_device, PCIDevice), VMSTATE_BOOL(rx_packets_compound, VMXNET3State), VMSTATE_BOOL(rx_vlan_stripping, VMXNET3State), VMSTATE_BOOL(lro_supported, VMXNET3State), @@ -2532,19 +2655,38 @@ static const VMStateDescription vmstate_vmxnet3 = { }, .subsections = (const VMStateDescription*[]) { &vmxstate_vmxnet3_mcast_list, + &vmstate_vmxnet3_pcie_device, NULL } }; static Property vmxnet3_properties[] = { DEFINE_NIC_PROPERTIES(VMXNET3State, conf), + DEFINE_PROP_BIT("x-old-msi-offsets", VMXNET3State, compat_flags, + VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT, false), + DEFINE_PROP_BIT("x-disable-pcie", VMXNET3State, compat_flags, + VMXNET3_COMPAT_FLAG_DISABLE_PCIE_BIT, false), DEFINE_PROP_END_OF_LIST(), }; +static void vmxnet3_realize(DeviceState *qdev, Error **errp) +{ + VMXNET3Class *vc = VMXNET3_DEVICE_GET_CLASS(qdev); + PCIDevice *pci_dev = PCI_DEVICE(qdev); + VMXNET3State *s = VMXNET3(qdev); + + if (!(s->compat_flags & VMXNET3_COMPAT_FLAG_DISABLE_PCIE)) { + pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; + } + + vc->parent_dc_realize(qdev, errp); +} + static void vmxnet3_class_init(ObjectClass *class, void *data) { DeviceClass *dc = DEVICE_CLASS(class); PCIDeviceClass *c = PCI_DEVICE_CLASS(class); + VMXNET3Class *vc = VMXNET3_DEVICE_CLASS(class); c->realize = vmxnet3_pci_realize; c->exit = vmxnet3_pci_uninit; @@ -2554,6 +2696,8 @@ static void vmxnet3_class_init(ObjectClass *class, void *data) c->class_id = PCI_CLASS_NETWORK_ETHERNET; c->subsystem_vendor_id = PCI_VENDOR_ID_VMWARE; c->subsystem_id = PCI_DEVICE_ID_VMWARE_VMXNET3; + vc->parent_dc_realize = dc->realize; + dc->realize = vmxnet3_realize; dc->desc = "VMWare Paravirtualized Ethernet v3"; dc->reset = vmxnet3_qdev_reset; dc->vmsd = &vmstate_vmxnet3; @@ -2564,6 +2708,7 @@ static void vmxnet3_class_init(ObjectClass *class, void *data) static const TypeInfo vmxnet3_info = { .name = TYPE_VMXNET3, .parent = TYPE_PCI_DEVICE, + .class_size = sizeof(VMXNET3Class), .instance_size = sizeof(VMXNET3State), .class_init = vmxnet3_class_init, .instance_init = vmxnet3_instance_init, diff --git a/qemu/hw/net/vmxnet3.h b/qemu/hw/net/vmxnet3.h index f987d7126..f7006afe9 100644 --- a/qemu/hw/net/vmxnet3.h +++ b/qemu/hw/net/vmxnet3.h @@ -198,9 +198,13 @@ enum { VMXNET3_CMD_GET_DID_LO, /* 0xF00D0005 */ VMXNET3_CMD_GET_DID_HI, /* 0xF00D0006 */ VMXNET3_CMD_GET_DEV_EXTRA_INFO, /* 0xF00D0007 */ - VMXNET3_CMD_GET_CONF_INTR /* 0xF00D0008 */ + VMXNET3_CMD_GET_CONF_INTR, /* 0xF00D0008 */ + VMXNET3_CMD_GET_ADAPTIVE_RING_INFO /* 0xF00D0009 */ }; +/* Adaptive Ring Info Flags */ +#define VMXNET3_DISABLE_ADAPTIVE_RING 1 + /* * Little Endian layout of bitfields - * Byte 0 : 7.....len.....0 diff --git a/qemu/hw/net/vmxnet_debug.h b/qemu/hw/net/vmxnet_debug.h index 96dae0f91..96495dbb1 100644 --- a/qemu/hw/net/vmxnet_debug.h +++ b/qemu/hw/net/vmxnet_debug.h @@ -20,94 +20,127 @@ #define VMXNET_DEVICE_NAME "vmxnet3" -/* #define VMXNET_DEBUG_CB */ #define VMXNET_DEBUG_WARNINGS #define VMXNET_DEBUG_ERRORS -/* #define VMXNET_DEBUG_INTERRUPTS */ -/* #define VMXNET_DEBUG_CONFIG */ -/* #define VMXNET_DEBUG_RINGS */ -/* #define VMXNET_DEBUG_PACKETS */ -/* #define VMXNET_DEBUG_SHMEM_ACCESS */ + +#undef VMXNET_DEBUG_CB +#undef VMXNET_DEBUG_INTERRUPTS +#undef VMXNET_DEBUG_CONFIG +#undef VMXNET_DEBUG_RINGS +#undef VMXNET_DEBUG_PACKETS +#undef VMXNET_DEBUG_SHMEM_ACCESS + +#ifdef VMXNET_DEBUG_CB +# define VMXNET_DEBUG_CB_ENABLED 1 +#else +# define VMXNET_DEBUG_CB_ENABLED 0 +#endif + +#ifdef VMXNET_DEBUG_WARNINGS +# define VMXNET_DEBUG_WARNINGS_ENABLED 1 +#else +# define VMXNET_DEBUG_WARNINGS_ENABLED 0 +#endif + +#ifdef VMXNET_DEBUG_ERRORS +# define VMXNET_DEBUG_ERRORS_ENABLED 1 +#else +# define VMXNET_DEBUG_ERRORS_ENABLED 0 +#endif + +#ifdef VMXNET_DEBUG_CONFIG +# define VMXNET_DEBUG_CONFIG_ENABLED 1 +#else +# define VMXNET_DEBUG_CONFIG_ENABLED 0 +#endif + +#ifdef VMXNET_DEBUG_RINGS +# define VMXNET_DEBUG_RINGS_ENABLED 1 +#else +# define VMXNET_DEBUG_RINGS_ENABLED 0 +#endif + +#ifdef VMXNET_DEBUG_PACKETS +# define VMXNET_DEBUG_PACKETS_ENABLED 1 +#else +# define VMXNET_DEBUG_PACKETS_ENABLED 0 +#endif + +#ifdef VMXNET_DEBUG_INTERRUPTS +# define VMXNET_DEBUG_INTERRUPTS_ENABLED 1 +#else +# define VMXNET_DEBUG_INTERRUPTS_ENABLED 0 +#endif #ifdef VMXNET_DEBUG_SHMEM_ACCESS +# define VMXNET_DEBUG_SHMEM_ACCESS_ENABLED 1 +#else +# define VMXNET_DEBUG_SHMEM_ACCESS_ENABLED 0 +#endif + #define VMW_SHPRN(fmt, ...) \ do { \ - printf("[%s][SH][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ + if (VMXNET_DEBUG_SHMEM_ACCESS_ENABLED) { \ + printf("[%s][SH][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } \ } while (0) -#else -#define VMW_SHPRN(fmt, ...) do {} while (0) -#endif -#ifdef VMXNET_DEBUG_CB #define VMW_CBPRN(fmt, ...) \ do { \ - printf("[%s][CB][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ + if (VMXNET_DEBUG_CB_ENABLED) { \ + printf("[%s][CB][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } \ } while (0) -#else -#define VMW_CBPRN(fmt, ...) do {} while (0) -#endif -#ifdef VMXNET_DEBUG_PACKETS #define VMW_PKPRN(fmt, ...) \ do { \ - printf("[%s][PK][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ + if (VMXNET_DEBUG_PACKETS_ENABLED) { \ + printf("[%s][PK][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } \ } while (0) -#else -#define VMW_PKPRN(fmt, ...) do {} while (0) -#endif -#ifdef VMXNET_DEBUG_WARNINGS #define VMW_WRPRN(fmt, ...) \ do { \ - printf("[%s][WR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ + if (VMXNET_DEBUG_WARNINGS_ENABLED) { \ + printf("[%s][WR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } \ } while (0) -#else -#define VMW_WRPRN(fmt, ...) do {} while (0) -#endif -#ifdef VMXNET_DEBUG_ERRORS #define VMW_ERPRN(fmt, ...) \ do { \ - printf("[%s][ER][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ + if (VMXNET_DEBUG_ERRORS_ENABLED) { \ + printf("[%s][ER][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } \ } while (0) -#else -#define VMW_ERPRN(fmt, ...) do {} while (0) -#endif -#ifdef VMXNET_DEBUG_INTERRUPTS #define VMW_IRPRN(fmt, ...) \ do { \ - printf("[%s][IR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ + if (VMXNET_DEBUG_INTERRUPTS_ENABLED) { \ + printf("[%s][IR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } \ } while (0) -#else -#define VMW_IRPRN(fmt, ...) do {} while (0) -#endif -#ifdef VMXNET_DEBUG_CONFIG #define VMW_CFPRN(fmt, ...) \ do { \ - printf("[%s][CF][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ + if (VMXNET_DEBUG_CONFIG_ENABLED) { \ + printf("[%s][CF][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } \ } while (0) -#else -#define VMW_CFPRN(fmt, ...) do {} while (0) -#endif -#ifdef VMXNET_DEBUG_RINGS #define VMW_RIPRN(fmt, ...) \ do { \ - printf("[%s][RI][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ + if (VMXNET_DEBUG_RINGS_ENABLED) { \ + printf("[%s][RI][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } \ } while (0) -#else -#define VMW_RIPRN(fmt, ...) do {} while (0) -#endif #define VMXNET_MF "%02X:%02X:%02X:%02X:%02X:%02X" #define VMXNET_MA(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] diff --git a/qemu/hw/net/vmxnet_rx_pkt.c b/qemu/hw/net/vmxnet_rx_pkt.c index aa5462931..21bb46e68 100644 --- a/qemu/hw/net/vmxnet_rx_pkt.c +++ b/qemu/hw/net/vmxnet_rx_pkt.c @@ -15,6 +15,7 @@ * */ +#include "qemu/osdep.h" #include "vmxnet_rx_pkt.h" #include "net/eth.h" #include "qemu-common.h" diff --git a/qemu/hw/net/vmxnet_rx_pkt.h b/qemu/hw/net/vmxnet_rx_pkt.h index a425846b5..0a45c1ba0 100644 --- a/qemu/hw/net/vmxnet_rx_pkt.h +++ b/qemu/hw/net/vmxnet_rx_pkt.h @@ -18,8 +18,6 @@ #ifndef VMXNET_RX_PKT_H #define VMXNET_RX_PKT_H -#include "stdint.h" -#include "stdbool.h" #include "net/eth.h" /* defines to enable packet dump functions */ diff --git a/qemu/hw/net/vmxnet_tx_pkt.c b/qemu/hw/net/vmxnet_tx_pkt.c index f7344c4cb..91e1e08fd 100644 --- a/qemu/hw/net/vmxnet_tx_pkt.c +++ b/qemu/hw/net/vmxnet_tx_pkt.c @@ -15,6 +15,7 @@ * */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "vmxnet_tx_pkt.h" #include "net/eth.h" @@ -142,11 +143,24 @@ static bool vmxnet_tx_pkt_parse_headers(struct VmxnetTxPkt *pkt) bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 0, l2_hdr->iov_base, ETH_MAX_L2_HDR_LEN); - if (bytes_read < ETH_MAX_L2_HDR_LEN) { + if (bytes_read < sizeof(struct eth_header)) { + l2_hdr->iov_len = 0; + return false; + } + + l2_hdr->iov_len = sizeof(struct eth_header); + switch (be16_to_cpu(PKT_GET_ETH_HDR(l2_hdr->iov_base)->h_proto)) { + case ETH_P_VLAN: + l2_hdr->iov_len += sizeof(struct vlan_header); + break; + case ETH_P_DVLAN: + l2_hdr->iov_len += 2 * sizeof(struct vlan_header); + break; + } + + if (bytes_read < l2_hdr->iov_len) { l2_hdr->iov_len = 0; return false; - } else { - l2_hdr->iov_len = eth_get_l2_hdr_length(l2_hdr->iov_base); } l3_proto = eth_get_l3_proto(l2_hdr->iov_base, l2_hdr->iov_len); diff --git a/qemu/hw/net/vmxnet_tx_pkt.h b/qemu/hw/net/vmxnet_tx_pkt.h index 57121a6fe..f51e98ad9 100644 --- a/qemu/hw/net/vmxnet_tx_pkt.h +++ b/qemu/hw/net/vmxnet_tx_pkt.h @@ -18,8 +18,6 @@ #ifndef VMXNET_TX_PKT_H #define VMXNET_TX_PKT_H -#include "stdint.h" -#include "stdbool.h" #include "net/eth.h" #include "exec/hwaddr.h" diff --git a/qemu/hw/net/xen_nic.c b/qemu/hw/net/xen_nic.c index d7cbfc103..7281730d9 100644 --- a/qemu/hw/net/xen_nic.c +++ b/qemu/hw/net/xen_nic.c @@ -19,19 +19,9 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include #include -#include -#include #include #include @@ -169,7 +159,7 @@ static void net_tx_packets(struct XenNetDev *netdev) (txreq.flags & NETTXF_more_data) ? " more_data" : "", (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); - page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + page = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, netdev->xendev.dom, txreq.gref, PROT_READ); if (page == NULL) { @@ -191,7 +181,7 @@ static void net_tx_packets(struct XenNetDev *netdev) qemu_send_packet(qemu_get_queue(netdev->nic), page + txreq.offset, txreq.size); } - xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); + xengnttab_unmap(netdev->xendev.gnttabdev, page, 1); net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); } if (!netdev->tx_work) { @@ -261,7 +251,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq)); netdev->rx_ring.req_cons = ++rc; - page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + page = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, netdev->xendev.dom, rxreq.gref, PROT_WRITE); if (page == NULL) { @@ -271,7 +261,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size return -1; } memcpy(page + NET_IP_ALIGN, buf, size); - xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); + xengnttab_unmap(netdev->xendev.gnttabdev, page, 1); net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0); return size; @@ -343,19 +333,19 @@ static int net_connect(struct XenDevice *xendev) return -1; } - netdev->txs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->txs = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, netdev->xendev.dom, netdev->tx_ring_ref, PROT_READ | PROT_WRITE); if (!netdev->txs) { return -1; } - netdev->rxs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->rxs = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, netdev->xendev.dom, netdev->rx_ring_ref, PROT_READ | PROT_WRITE); if (!netdev->rxs) { - xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1); + xengnttab_unmap(netdev->xendev.gnttabdev, netdev->txs, 1); netdev->txs = NULL; return -1; } @@ -380,11 +370,11 @@ static void net_disconnect(struct XenDevice *xendev) xen_be_unbind_evtchn(&netdev->xendev); if (netdev->txs) { - xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1); + xengnttab_unmap(netdev->xendev.gnttabdev, netdev->txs, 1); netdev->txs = NULL; } if (netdev->rxs) { - xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->rxs, 1); + xengnttab_unmap(netdev->xendev.gnttabdev, netdev->rxs, 1); netdev->rxs = NULL; } } diff --git a/qemu/hw/net/xgmac.c b/qemu/hw/net/xgmac.c index 15fb68194..0c5f793bd 100644 --- a/qemu/hw/net/xgmac.c +++ b/qemu/hw/net/xgmac.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "sysemu/char.h" #include "qemu/log.h" diff --git a/qemu/hw/net/xilinx_axienet.c b/qemu/hw/net/xilinx_axienet.c index d63c42324..de23ab5dc 100644 --- a/qemu/hw/net/xilinx_axienet.c +++ b/qemu/hw/net/xilinx_axienet.c @@ -22,7 +22,9 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" +#include "qapi/error.h" #include "qemu/log.h" #include "net/net.h" #include "net/checksum.h" diff --git a/qemu/hw/net/xilinx_ethlite.c b/qemu/hw/net/xilinx_ethlite.c index ad6b55306..bc846e709 100644 --- a/qemu/hw/net/xilinx_ethlite.c +++ b/qemu/hw/net/xilinx_ethlite.c @@ -22,6 +22,9 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" /* FIXME should not use tswap* */ #include "hw/sysbus.h" #include "hw/hw.h" #include "net/net.h" diff --git a/qemu/hw/nvram/ds1225y.c b/qemu/hw/nvram/ds1225y.c index 332598b25..57d5ab215 100644 --- a/qemu/hw/nvram/ds1225y.c +++ b/qemu/hw/nvram/ds1225y.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "trace.h" diff --git a/qemu/hw/nvram/eeprom93xx.c b/qemu/hw/nvram/eeprom93xx.c index 0af4d6707..2c16fc23d 100644 --- a/qemu/hw/nvram/eeprom93xx.c +++ b/qemu/hw/nvram/eeprom93xx.c @@ -35,6 +35,7 @@ * - No emulation of EEPROM timings. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/nvram/eeprom93xx.h" diff --git a/qemu/hw/nvram/fw_cfg.c b/qemu/hw/nvram/fw_cfg.c index 88481b78c..999f48028 100644 --- a/qemu/hw/nvram/fw_cfg.c +++ b/qemu/hw/nvram/fw_cfg.c @@ -21,16 +21,19 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "sysemu/sysemu.h" +#include "sysemu/dma.h" #include "hw/isa/isa.h" #include "hw/nvram/fw_cfg.h" #include "hw/sysbus.h" +#include "hw/boards.h" #include "trace.h" #include "qemu/error-report.h" #include "qemu/config-file.h" +#include "qemu/cutils.h" -#define FW_CFG_SIZE 2 #define FW_CFG_NAME "fw_cfg" #define FW_CFG_PATH "/machine/" FW_CFG_NAME @@ -42,6 +45,18 @@ #define FW_CFG_IO(obj) OBJECT_CHECK(FWCfgIoState, (obj), TYPE_FW_CFG_IO) #define FW_CFG_MEM(obj) OBJECT_CHECK(FWCfgMemState, (obj), TYPE_FW_CFG_MEM) +/* FW_CFG_VERSION bits */ +#define FW_CFG_VERSION 0x01 +#define FW_CFG_VERSION_DMA 0x02 + +/* FW_CFG_DMA_CONTROL bits */ +#define FW_CFG_DMA_CTL_ERROR 0x01 +#define FW_CFG_DMA_CTL_READ 0x02 +#define FW_CFG_DMA_CTL_SKIP 0x04 +#define FW_CFG_DMA_CTL_SELECT 0x08 + +#define FW_CFG_DMA_SIGNATURE 0x51454d5520434647ULL /* "QEMU CFG" */ + typedef struct FWCfgEntry { uint32_t len; uint8_t *data; @@ -55,10 +70,18 @@ struct FWCfgState { /*< public >*/ FWCfgEntry entries[2][FW_CFG_MAX_ENTRY]; + int entry_order[FW_CFG_MAX_ENTRY]; FWCfgFiles *files; uint16_t cur_entry; uint32_t cur_offset; Notifier machine_ready; + + int fw_cfg_order_override; + + bool dma_enabled; + dma_addr_t dma_addr; + AddressSpace *dma_as; + MemoryRegion dma_iomem; }; struct FWCfgIoState { @@ -67,7 +90,7 @@ struct FWCfgIoState { /*< public >*/ MemoryRegion comb_iomem; - uint32_t iobase; + uint32_t iobase, dma_iobase; }; struct FWCfgMemState { @@ -187,9 +210,7 @@ static void fw_cfg_bootsplash(FWCfgState *s) g_free(filename); return; } - if (boot_splash_filedata != NULL) { - g_free(boot_splash_filedata); - } + g_free(boot_splash_filedata); boot_splash_filedata = (uint8_t *)file_data; boot_splash_filedata_size = file_size; @@ -236,7 +257,8 @@ static void fw_cfg_write(FWCfgState *s, uint8_t value) static int fw_cfg_select(FWCfgState *s, uint16_t key) { - int ret; + int arch, ret; + FWCfgEntry *e; s->cur_offset = 0; if ((key & FW_CFG_ENTRY_MASK) >= FW_CFG_MAX_ENTRY) { @@ -245,41 +267,45 @@ static int fw_cfg_select(FWCfgState *s, uint16_t key) } else { s->cur_entry = key; ret = 1; - } - - trace_fw_cfg_select(s, key, ret); - return ret; -} - -static uint8_t fw_cfg_read(FWCfgState *s) -{ - int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); - FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; - uint8_t ret; - - if (s->cur_entry == FW_CFG_INVALID || !e->data || s->cur_offset >= e->len) - ret = 0; - else { + /* entry successfully selected, now run callback if present */ + arch = !!(key & FW_CFG_ARCH_LOCAL); + e = &s->entries[arch][key & FW_CFG_ENTRY_MASK]; if (e->read_callback) { - e->read_callback(e->callback_opaque, s->cur_offset); + e->read_callback(e->callback_opaque); } - ret = e->data[s->cur_offset++]; } - trace_fw_cfg_read(s, ret); + trace_fw_cfg_select(s, key, ret); return ret; } -static uint64_t fw_cfg_data_mem_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t fw_cfg_data_read(void *opaque, hwaddr addr, unsigned size) { FWCfgState *s = opaque; + int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); + FWCfgEntry *e = (s->cur_entry == FW_CFG_INVALID) ? NULL : + &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; uint64_t value = 0; - unsigned i; - for (i = 0; i < size; ++i) { - value = (value << 8) | fw_cfg_read(s); + assert(size > 0 && size <= sizeof(value)); + if (s->cur_entry != FW_CFG_INVALID && e->data && s->cur_offset < e->len) { + /* The least significant 'size' bytes of the return value are + * expected to contain a string preserving portion of the item + * data, padded with zeros on the right in case we run out early. + * In technical terms, we're composing the host-endian representation + * of the big endian interpretation of the fw_cfg string. + */ + do { + value = (value << 8) | e->data[s->cur_offset++]; + } while (--size && s->cur_offset < e->len); + /* If size is still not zero, we *did* run out early, so continue + * left-shifting, to add the appropriate number of padding zeros + * on the right. + */ + value <<= 8 * size; } + + trace_fw_cfg_read(s, value); return value; } @@ -294,6 +320,126 @@ static void fw_cfg_data_mem_write(void *opaque, hwaddr addr, } while (i); } +static void fw_cfg_dma_transfer(FWCfgState *s) +{ + dma_addr_t len; + FWCfgDmaAccess dma; + int arch; + FWCfgEntry *e; + int read; + dma_addr_t dma_addr; + + /* Reset the address before the next access */ + dma_addr = s->dma_addr; + s->dma_addr = 0; + + if (dma_memory_read(s->dma_as, dma_addr, &dma, sizeof(dma))) { + stl_be_dma(s->dma_as, dma_addr + offsetof(FWCfgDmaAccess, control), + FW_CFG_DMA_CTL_ERROR); + return; + } + + dma.address = be64_to_cpu(dma.address); + dma.length = be32_to_cpu(dma.length); + dma.control = be32_to_cpu(dma.control); + + if (dma.control & FW_CFG_DMA_CTL_SELECT) { + fw_cfg_select(s, dma.control >> 16); + } + + arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); + e = (s->cur_entry == FW_CFG_INVALID) ? NULL : + &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; + + if (dma.control & FW_CFG_DMA_CTL_READ) { + read = 1; + } else if (dma.control & FW_CFG_DMA_CTL_SKIP) { + read = 0; + } else { + dma.length = 0; + } + + dma.control = 0; + + while (dma.length > 0 && !(dma.control & FW_CFG_DMA_CTL_ERROR)) { + if (s->cur_entry == FW_CFG_INVALID || !e->data || + s->cur_offset >= e->len) { + len = dma.length; + + /* If the access is not a read access, it will be a skip access, + * tested before. + */ + if (read) { + if (dma_memory_set(s->dma_as, dma.address, 0, len)) { + dma.control |= FW_CFG_DMA_CTL_ERROR; + } + } + + } else { + if (dma.length <= (e->len - s->cur_offset)) { + len = dma.length; + } else { + len = (e->len - s->cur_offset); + } + + /* If the access is not a read access, it will be a skip access, + * tested before. + */ + if (read) { + if (dma_memory_write(s->dma_as, dma.address, + &e->data[s->cur_offset], len)) { + dma.control |= FW_CFG_DMA_CTL_ERROR; + } + } + + s->cur_offset += len; + } + + dma.address += len; + dma.length -= len; + + } + + stl_be_dma(s->dma_as, dma_addr + offsetof(FWCfgDmaAccess, control), + dma.control); + + trace_fw_cfg_read(s, 0); +} + +static uint64_t fw_cfg_dma_mem_read(void *opaque, hwaddr addr, + unsigned size) +{ + /* Return a signature value (and handle various read sizes) */ + return extract64(FW_CFG_DMA_SIGNATURE, (8 - addr - size) * 8, size * 8); +} + +static void fw_cfg_dma_mem_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + FWCfgState *s = opaque; + + if (size == 4) { + if (addr == 0) { + /* FWCfgDmaAccess high address */ + s->dma_addr = value << 32; + } else if (addr == 4) { + /* FWCfgDmaAccess low address */ + s->dma_addr |= value; + fw_cfg_dma_transfer(s); + } + } else if (size == 8 && addr == 0) { + s->dma_addr = value; + fw_cfg_dma_transfer(s); + } +} + +static bool fw_cfg_dma_mem_valid(void *opaque, hwaddr addr, + unsigned size, bool is_write) +{ + return !is_write || ((size == 4 && (addr == 0 || addr == 4)) || + (size == 8 && addr == 0)); +} + static bool fw_cfg_data_mem_valid(void *opaque, hwaddr addr, unsigned size, bool is_write) { @@ -312,12 +458,6 @@ static bool fw_cfg_ctl_mem_valid(void *opaque, hwaddr addr, return is_write && size == 2; } -static uint64_t fw_cfg_comb_read(void *opaque, hwaddr addr, - unsigned size) -{ - return fw_cfg_read(opaque); -} - static void fw_cfg_comb_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { @@ -344,7 +484,7 @@ static const MemoryRegionOps fw_cfg_ctl_mem_ops = { }; static const MemoryRegionOps fw_cfg_data_mem_ops = { - .read = fw_cfg_data_mem_read, + .read = fw_cfg_data_read, .write = fw_cfg_data_mem_write, .endianness = DEVICE_BIG_ENDIAN, .valid = { @@ -355,17 +495,27 @@ static const MemoryRegionOps fw_cfg_data_mem_ops = { }; static const MemoryRegionOps fw_cfg_comb_mem_ops = { - .read = fw_cfg_comb_read, + .read = fw_cfg_data_read, .write = fw_cfg_comb_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid.accepts = fw_cfg_comb_valid, }; +static const MemoryRegionOps fw_cfg_dma_mem_ops = { + .read = fw_cfg_dma_mem_read, + .write = fw_cfg_dma_mem_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid.accepts = fw_cfg_dma_mem_valid, + .valid.max_access_size = 8, + .impl.max_access_size = 8, +}; + static void fw_cfg_reset(DeviceState *d) { FWCfgState *s = FW_CFG(d); - fw_cfg_select(s, 0); + /* we never register a read callback for FW_CFG_SIGNATURE */ + fw_cfg_select(s, FW_CFG_SIGNATURE); } /* Save restore 32 bit int as uint16_t @@ -401,6 +551,22 @@ static bool is_version_1(void *opaque, int version_id) return version_id == 1; } +static bool fw_cfg_dma_enabled(void *opaque) +{ + FWCfgState *s = opaque; + + return s->dma_enabled; +} + +static const VMStateDescription vmstate_fw_cfg_dma = { + .name = "fw_cfg/dma", + .needed = fw_cfg_dma_enabled, + .fields = (VMStateField[]) { + VMSTATE_UINT64(dma_addr, FWCfgState), + VMSTATE_END_OF_LIST() + }, +}; + static const VMStateDescription vmstate_fw_cfg = { .name = "fw_cfg", .version_id = 2, @@ -410,6 +576,10 @@ static const VMStateDescription vmstate_fw_cfg = { VMSTATE_UINT16_HACK(cur_offset, FWCfgState, is_version_1), VMSTATE_UINT32_V(cur_offset, FWCfgState, 2), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription*[]) { + &vmstate_fw_cfg_dma, + NULL, } }; @@ -499,12 +669,87 @@ void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value) fw_cfg_add_bytes(s, key, copy, sizeof(value)); } +void fw_cfg_set_order_override(FWCfgState *s, int order) +{ + assert(s->fw_cfg_order_override == 0); + s->fw_cfg_order_override = order; +} + +void fw_cfg_reset_order_override(FWCfgState *s) +{ + assert(s->fw_cfg_order_override != 0); + s->fw_cfg_order_override = 0; +} + +/* + * This is the legacy order list. For legacy systems, files are in + * the fw_cfg in the order defined below, by the "order" value. Note + * that some entries (VGA ROMs, NIC option ROMS, etc.) go into a + * specific area, but there may be more than one and they occur in the + * order that the user specifies them on the command line. Those are + * handled in a special manner, using the order override above. + * + * For non-legacy, the files are sorted by filename to avoid this kind + * of complexity in the future. + * + * This is only for x86, other arches don't implement versioning so + * they won't set legacy mode. + */ +static struct { + const char *name; + int order; +} fw_cfg_order[] = { + { "etc/boot-menu-wait", 10 }, + { "bootsplash.jpg", 11 }, + { "bootsplash.bmp", 12 }, + { "etc/boot-fail-wait", 15 }, + { "etc/smbios/smbios-tables", 20 }, + { "etc/smbios/smbios-anchor", 30 }, + { "etc/e820", 40 }, + { "etc/reserved-memory-end", 50 }, + { "genroms/kvmvapic.bin", 55 }, + { "genroms/linuxboot.bin", 60 }, + { }, /* VGA ROMs from pc_vga_init come here, 70. */ + { }, /* NIC option ROMs from pc_nic_init come here, 80. */ + { "etc/system-states", 90 }, + { }, /* User ROMs come here, 100. */ + { }, /* Device FW comes here, 110. */ + { "etc/extra-pci-roots", 120 }, + { "etc/acpi/tables", 130 }, + { "etc/table-loader", 140 }, + { "etc/tpm/log", 150 }, + { "etc/acpi/rsdp", 160 }, + { "bootorder", 170 }, + +#define FW_CFG_ORDER_OVERRIDE_LAST 200 +}; + +static int get_fw_cfg_order(FWCfgState *s, const char *name) +{ + int i; + + if (s->fw_cfg_order_override > 0) + return s->fw_cfg_order_override; + + for (i = 0; i < ARRAY_SIZE(fw_cfg_order); i++) { + if (fw_cfg_order[i].name == NULL) + continue; + if (strcmp(name, fw_cfg_order[i].name) == 0) + return fw_cfg_order[i].order; + } + /* Stick unknown stuff at the end. */ + error_report("warning: Unknown firmware file in legacy mode: %s\n", name); + return FW_CFG_ORDER_OVERRIDE_LAST; +} + void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, FWCfgReadCallback callback, void *callback_opaque, void *data, size_t len) { - int i, index; + int i, index, count; size_t dsize; + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); + int order = 0; if (!s->files) { dsize = sizeof(uint32_t) + sizeof(FWCfgFile) * FW_CFG_FILE_SLOTS; @@ -512,13 +757,48 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, fw_cfg_add_bytes(s, FW_CFG_FILE_DIR, s->files, dsize); } - index = be32_to_cpu(s->files->count); - assert(index < FW_CFG_FILE_SLOTS); + count = be32_to_cpu(s->files->count); + assert(count < FW_CFG_FILE_SLOTS); + + /* Find the insertion point. */ + if (mc->legacy_fw_cfg_order) { + /* + * Sort by order. For files with the same order, we keep them + * in the sequence in which they were added. + */ + order = get_fw_cfg_order(s, filename); + for (index = count; + index > 0 && order < s->entry_order[index - 1]; + index--); + } else { + /* Sort by file name. */ + for (index = count; + index > 0 && strcmp(filename, s->files->f[index - 1].name) < 0; + index--); + } - pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name), - filename); - for (i = 0; i < index; i++) { - if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) { + /* + * Move all the entries from the index point and after down one + * to create a slot for the new entry. Because calculations are + * being done with the index, make it so that "i" is the current + * index and "i - 1" is the one being copied from, thus the + * unusual start and end in the for statement. + */ + for (i = count + 1; i > index; i--) { + s->files->f[i] = s->files->f[i - 1]; + s->files->f[i].select = cpu_to_be16(FW_CFG_FILE_FIRST + i); + s->entries[0][FW_CFG_FILE_FIRST + i] = + s->entries[0][FW_CFG_FILE_FIRST + i - 1]; + s->entry_order[i] = s->entry_order[i - 1]; + } + + memset(&s->files->f[index], 0, sizeof(FWCfgFile)); + memset(&s->entries[0][FW_CFG_FILE_FIRST + index], 0, sizeof(FWCfgEntry)); + + pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name), filename); + for (i = 0; i <= count; i++) { + if (i != index && + strcmp(s->files->f[index].name, s->files->f[i].name) == 0) { error_report("duplicate fw_cfg file name: %s", s->files->f[index].name); exit(1); @@ -530,9 +810,10 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, s->files->f[index].size = cpu_to_be32(len); s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index); + s->entry_order[index] = order; trace_fw_cfg_add_file(s, index, s->files->f[index].name, len); - s->files->count = cpu_to_be32(index+1); + s->files->count = cpu_to_be32(count+1); } void fw_cfg_add_file(FWCfgState *s, const char *filename, @@ -595,7 +876,6 @@ static void fw_cfg_init1(DeviceState *dev) qdev_init_nofail(dev); fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4); - fw_cfg_add_i32(s, FW_CFG_ID, 1); fw_cfg_add_bytes(s, FW_CFG_UUID, qemu_uuid, 16); fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)(display_type == DT_NOGRAPHIC)); fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); @@ -607,25 +887,57 @@ static void fw_cfg_init1(DeviceState *dev) qemu_add_machine_init_done_notifier(&s->machine_ready); } -FWCfgState *fw_cfg_init_io(uint32_t iobase) +FWCfgState *fw_cfg_init_io_dma(uint32_t iobase, uint32_t dma_iobase, + AddressSpace *dma_as) { DeviceState *dev; + FWCfgState *s; + uint32_t version = FW_CFG_VERSION; + bool dma_requested = dma_iobase && dma_as; dev = qdev_create(NULL, TYPE_FW_CFG_IO); qdev_prop_set_uint32(dev, "iobase", iobase); + qdev_prop_set_uint32(dev, "dma_iobase", dma_iobase); + if (!dma_requested) { + qdev_prop_set_bit(dev, "dma_enabled", false); + } + fw_cfg_init1(dev); + s = FW_CFG(dev); + + if (s->dma_enabled) { + /* 64 bits for the address field */ + s->dma_as = dma_as; + s->dma_addr = 0; + + version |= FW_CFG_VERSION_DMA; + } + + fw_cfg_add_i32(s, FW_CFG_ID, version); + + return s; +} - return FW_CFG(dev); +FWCfgState *fw_cfg_init_io(uint32_t iobase) +{ + return fw_cfg_init_io_dma(iobase, 0, NULL); } -FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, hwaddr data_addr, - uint32_t data_width) +FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, + hwaddr data_addr, uint32_t data_width, + hwaddr dma_addr, AddressSpace *dma_as) { DeviceState *dev; SysBusDevice *sbd; + FWCfgState *s; + uint32_t version = FW_CFG_VERSION; + bool dma_requested = dma_addr && dma_as; dev = qdev_create(NULL, TYPE_FW_CFG_MEM); qdev_prop_set_uint32(dev, "data_width", data_width); + if (!dma_requested) { + qdev_prop_set_bit(dev, "dma_enabled", false); + } fw_cfg_init1(dev); @@ -633,13 +945,25 @@ FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, hwaddr data_addr, sysbus_mmio_map(sbd, 0, ctl_addr); sysbus_mmio_map(sbd, 1, data_addr); - return FW_CFG(dev); + s = FW_CFG(dev); + + if (s->dma_enabled) { + s->dma_as = dma_as; + s->dma_addr = 0; + sysbus_mmio_map(sbd, 2, dma_addr); + version |= FW_CFG_VERSION_DMA; + } + + fw_cfg_add_i32(s, FW_CFG_ID, version); + + return s; } FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr) { return fw_cfg_init_mem_wide(ctl_addr, data_addr, - fw_cfg_data_mem_ops.valid.max_access_size); + fw_cfg_data_mem_ops.valid.max_access_size, + 0, NULL); } @@ -666,6 +990,9 @@ static const TypeInfo fw_cfg_info = { static Property fw_cfg_io_properties[] = { DEFINE_PROP_UINT32("iobase", FWCfgIoState, iobase, -1), + DEFINE_PROP_UINT32("dma_iobase", FWCfgIoState, dma_iobase, -1), + DEFINE_PROP_BOOL("dma_enabled", FWCfgIoState, parent_obj.dma_enabled, + true), DEFINE_PROP_END_OF_LIST(), }; @@ -674,9 +1001,19 @@ static void fw_cfg_io_realize(DeviceState *dev, Error **errp) FWCfgIoState *s = FW_CFG_IO(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + /* when using port i/o, the 8-bit data register ALWAYS overlaps + * with half of the 16-bit control register. Hence, the total size + * of the i/o region used is FW_CFG_CTL_SIZE */ memory_region_init_io(&s->comb_iomem, OBJECT(s), &fw_cfg_comb_mem_ops, - FW_CFG(s), "fwcfg", FW_CFG_SIZE); + FW_CFG(s), "fwcfg", FW_CFG_CTL_SIZE); sysbus_add_io(sbd, s->iobase, &s->comb_iomem); + + if (FW_CFG(s)->dma_enabled) { + memory_region_init_io(&FW_CFG(s)->dma_iomem, OBJECT(s), + &fw_cfg_dma_mem_ops, FW_CFG(s), "fwcfg.dma", + sizeof(dma_addr_t)); + sysbus_add_io(sbd, s->dma_iobase, &FW_CFG(s)->dma_iomem); + } } static void fw_cfg_io_class_init(ObjectClass *klass, void *data) @@ -697,6 +1034,8 @@ static const TypeInfo fw_cfg_io_info = { static Property fw_cfg_mem_properties[] = { DEFINE_PROP_UINT32("data_width", FWCfgMemState, data_width, -1), + DEFINE_PROP_BOOL("dma_enabled", FWCfgMemState, parent_obj.dma_enabled, + true), DEFINE_PROP_END_OF_LIST(), }; @@ -707,7 +1046,7 @@ static void fw_cfg_mem_realize(DeviceState *dev, Error **errp) const MemoryRegionOps *data_ops = &fw_cfg_data_mem_ops; memory_region_init_io(&s->ctl_iomem, OBJECT(s), &fw_cfg_ctl_mem_ops, - FW_CFG(s), "fwcfg.ctl", FW_CFG_SIZE); + FW_CFG(s), "fwcfg.ctl", FW_CFG_CTL_SIZE); sysbus_init_mmio(sbd, &s->ctl_iomem); if (s->data_width > data_ops->valid.max_access_size) { @@ -725,6 +1064,13 @@ static void fw_cfg_mem_realize(DeviceState *dev, Error **errp) memory_region_init_io(&s->data_iomem, OBJECT(s), data_ops, FW_CFG(s), "fwcfg.data", data_ops->valid.max_access_size); sysbus_init_mmio(sbd, &s->data_iomem); + + if (FW_CFG(s)->dma_enabled) { + memory_region_init_io(&FW_CFG(s)->dma_iomem, OBJECT(s), + &fw_cfg_dma_mem_ops, FW_CFG(s), "fwcfg.dma", + sizeof(dma_addr_t)); + sysbus_init_mmio(sbd, &FW_CFG(s)->dma_iomem); + } } static void fw_cfg_mem_class_init(ObjectClass *klass, void *data) diff --git a/qemu/hw/nvram/mac_nvram.c b/qemu/hw/nvram/mac_nvram.c index d35f8a312..24f61212b 100644 --- a/qemu/hw/nvram/mac_nvram.c +++ b/qemu/hw/nvram/mac_nvram.c @@ -22,10 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/nvram/openbios_firmware_abi.h" #include "sysemu/sysemu.h" #include "hw/ppc/mac.h" +#include "qemu/cutils.h" #include /* debug NVR */ @@ -48,7 +50,8 @@ static void macio_nvram_writeb(void *opaque, hwaddr addr, addr = (addr >> s->it_shift) & (s->size - 1); s->data[addr] = value; - NVR_DPRINTF("writeb addr %04" PHYS_PRIx " val %" PRIx64 "\n", addr, value); + NVR_DPRINTF("writeb addr %04" HWADDR_PRIx " val %" PRIx64 "\n", + addr, value); } static uint64_t macio_nvram_readb(void *opaque, hwaddr addr, @@ -59,7 +62,8 @@ static uint64_t macio_nvram_readb(void *opaque, hwaddr addr, addr = (addr >> s->it_shift) & (s->size - 1); value = s->data[addr]; - NVR_DPRINTF("readb addr %04x val %x\n", (int)addr, value); + NVR_DPRINTF("readb addr %04" HWADDR_PRIx " val %" PRIx32 "\n", + addr, value); return value; } @@ -123,6 +127,7 @@ static void macio_nvram_class_init(ObjectClass *oc, void *data) dc->reset = macio_nvram_reset; dc->vmsd = &vmstate_macio_nvram; dc->props = macio_nvram_properties; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); } static const TypeInfo macio_nvram_type_info = { diff --git a/qemu/hw/nvram/spapr_nvram.c b/qemu/hw/nvram/spapr_nvram.c index fcaa77dd9..802636ef3 100644 --- a/qemu/hw/nvram/spapr_nvram.c +++ b/qemu/hw/nvram/spapr_nvram.c @@ -22,6 +22,10 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include #include "sysemu/block-backend.h" diff --git a/qemu/hw/openrisc/cputimer.c b/qemu/hw/openrisc/cputimer.c index 9c5494510..a98c799de 100644 --- a/qemu/hw/openrisc/cputimer.c +++ b/qemu/hw/openrisc/cputimer.c @@ -18,11 +18,12 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "hw/hw.h" #include "qemu/timer.h" -#define TIMER_FREQ (20 * 1000 * 1000) /* 20MHz */ +#define TIMER_PERIOD 50 /* 50 ns period for 20 MHz timer */ /* The time when TTCR changes */ static uint64_t last_clk; @@ -36,8 +37,7 @@ void cpu_openrisc_count_update(OpenRISCCPU *cpu) return; } now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - cpu->env.ttcr += (uint32_t)muldiv64(now - last_clk, TIMER_FREQ, - get_ticks_per_sec()); + cpu->env.ttcr += (uint32_t)((now - last_clk) / TIMER_PERIOD); last_clk = now; } @@ -59,7 +59,7 @@ void cpu_openrisc_timer_update(OpenRISCCPU *cpu) } else { wait = (cpu->env.ttmr & TTMR_TP) - (cpu->env.ttcr & TTMR_TP); } - next = now + muldiv64(wait, get_ticks_per_sec(), TIMER_FREQ); + next = now + (uint64_t)wait * TIMER_PERIOD; timer_mod(cpu->env.timer, next); } diff --git a/qemu/hw/openrisc/openrisc_sim.c b/qemu/hw/openrisc/openrisc_sim.c index 1da0657dd..6d06d5be0 100644 --- a/qemu/hw/openrisc/openrisc_sim.c +++ b/qemu/hw/openrisc/openrisc_sim.c @@ -18,6 +18,10 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/boards.h" #include "elf.h" @@ -68,7 +72,8 @@ static void cpu_openrisc_load_kernel(ram_addr_t ram_size, if (kernel_filename && !qtest_enabled()) { kernel_size = load_elf(kernel_filename, NULL, NULL, - &elf_entry, NULL, NULL, 1, ELF_MACHINE, 1); + &elf_entry, NULL, NULL, 1, EM_OPENRISC, + 1, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, @@ -114,7 +119,7 @@ static void openrisc_sim_init(MachineState *machine) } ram = g_malloc(sizeof(*ram)); - memory_region_init_ram(ram, NULL, "openrisc.ram", ram_size, &error_abort); + memory_region_init_ram(ram, NULL, "openrisc.ram", ram_size, &error_fatal); vmstate_register_ram_global(ram); memory_region_add_subregion(get_system_memory(), 0, ram); @@ -132,17 +137,12 @@ static void openrisc_sim_init(MachineState *machine) cpu_openrisc_load_kernel(ram_size, kernel_filename, cpu); } -static QEMUMachine openrisc_sim_machine = { - .name = "or32-sim", - .desc = "or32 simulation", - .init = openrisc_sim_init, - .max_cpus = 1, - .is_default = 1, -}; - -static void openrisc_sim_machine_init(void) +static void openrisc_sim_machine_init(MachineClass *mc) { - qemu_register_machine(&openrisc_sim_machine); + mc->desc = "or32 simulation"; + mc->init = openrisc_sim_init; + mc->max_cpus = 1; + mc->is_default = 1; } -machine_init(openrisc_sim_machine_init); +DEFINE_MACHINE("or32-sim", openrisc_sim_machine_init) diff --git a/qemu/hw/openrisc/pic_cpu.c b/qemu/hw/openrisc/pic_cpu.c index 2af1d6013..569b443f5 100644 --- a/qemu/hw/openrisc/pic_cpu.c +++ b/qemu/hw/openrisc/pic_cpu.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "cpu.h" diff --git a/qemu/hw/pci-bridge/dec.c b/qemu/hw/pci-bridge/dec.c index 28d0ff9c8..840c96198 100644 --- a/qemu/hw/pci-bridge/dec.c +++ b/qemu/hw/pci-bridge/dec.c @@ -23,6 +23,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "dec.h" #include "hw/sysbus.h" #include "hw/pci/pci.h" @@ -51,9 +52,9 @@ static int dec_map_irq(PCIDevice *pci_dev, int irq_num) return irq_num; } -static int dec_pci_bridge_initfn(PCIDevice *pci_dev) +static void dec_pci_bridge_realize(PCIDevice *pci_dev, Error **errp) { - return pci_bridge_initfn(pci_dev, TYPE_PCI_BUS); + pci_bridge_initfn(pci_dev, TYPE_PCI_BUS); } static void dec_21154_pci_bridge_class_init(ObjectClass *klass, void *data) @@ -61,7 +62,7 @@ static void dec_21154_pci_bridge_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = dec_pci_bridge_initfn; + k->realize = dec_pci_bridge_realize; k->exit = pci_bridge_exitfn; k->vendor_id = PCI_VENDOR_ID_DEC; k->device_id = PCI_DEVICE_ID_DEC_21154; diff --git a/qemu/hw/pci-bridge/i82801b11.c b/qemu/hw/pci-bridge/i82801b11.c index 7e79bc01e..2404e7eba 100644 --- a/qemu/hw/pci-bridge/i82801b11.c +++ b/qemu/hw/pci-bridge/i82801b11.c @@ -41,6 +41,7 @@ * License along with this library; if not, see */ +#include "qemu/osdep.h" #include "hw/pci/pci.h" #include "hw/i386/ich9.h" @@ -61,10 +62,7 @@ static int i82801b11_bridge_initfn(PCIDevice *d) { int rc; - rc = pci_bridge_initfn(d, TYPE_PCI_BUS); - if (rc < 0) { - return rc; - } + pci_bridge_initfn(d, TYPE_PCI_BUS); rc = pci_bridge_ssvid_init(d, I82801ba_SSVID_OFFSET, I82801ba_SSVID_SVID, I82801ba_SSVID_SSID); @@ -80,6 +78,14 @@ err_bridge: return rc; } +static const VMStateDescription i82801b11_bridge_dev_vmstate = { + .name = "i82801b11_bridge", + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, PCIBridge), + VMSTATE_END_OF_LIST() + } +}; + static void i82801b11_bridge_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -91,6 +97,7 @@ static void i82801b11_bridge_class_init(ObjectClass *klass, void *data) k->revision = ICH9_D2P_A2_REVISION; k->init = i82801b11_bridge_initfn; k->config_write = pci_bridge_write_config; + dc->vmsd = &i82801b11_bridge_dev_vmstate; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } diff --git a/qemu/hw/pci-bridge/ioh3420.c b/qemu/hw/pci-bridge/ioh3420.c index cce2fdd8e..0937fa34b 100644 --- a/qemu/hw/pci-bridge/ioh3420.c +++ b/qemu/hw/pci-bridge/ioh3420.c @@ -20,6 +20,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/pci/pci_ids.h" #include "hw/pci/msi.h" #include "hw/pci/pcie.h" @@ -97,11 +98,7 @@ static int ioh3420_initfn(PCIDevice *d) PCIESlot *s = PCIE_SLOT(d); int rc; - rc = pci_bridge_initfn(d, TYPE_PCIE_BUS); - if (rc < 0) { - return rc; - } - + pci_bridge_initfn(d, TYPE_PCIE_BUS); pcie_port_init_reg(d); rc = pci_bridge_ssvid_init(d, IOH_EP_SSVID_OFFSET, @@ -129,7 +126,7 @@ static int ioh3420_initfn(PCIDevice *d) goto err_pcie_cap; } pcie_cap_root_init(d); - rc = pcie_aer_init(d, IOH_EP_AER_OFFSET); + rc = pcie_aer_init(d, IOH_EP_AER_OFFSET, PCI_ERR_SIZEOF); if (rc < 0) { goto err; } diff --git a/qemu/hw/pci-bridge/pci_bridge_dev.c b/qemu/hw/pci-bridge/pci_bridge_dev.c index 26aded9f0..7b582e96a 100644 --- a/qemu/hw/pci-bridge/pci_bridge_dev.c +++ b/qemu/hw/pci-bridge/pci_bridge_dev.c @@ -19,6 +19,8 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/pci/pci_bridge.h" #include "hw/pci/pci_ids.h" #include "hw/pci/msi.h" @@ -52,10 +54,8 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev); int err; - err = pci_bridge_initfn(dev, TYPE_PCI_BUS); - if (err) { - goto bridge_error; - } + pci_bridge_initfn(dev, TYPE_PCI_BUS); + if (bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_SHPC_REQ)) { dev->config[PCI_INTERRUPT_PIN] = 0x1; memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar", @@ -73,7 +73,7 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) goto slotid_error; } if ((bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_MSI_REQ)) && - msi_supported) { + msi_nonbroken) { err = msi_init(dev, 0, 1, true, true); if (err < 0) { goto msi_error; @@ -94,7 +94,7 @@ slotid_error: } shpc_error: pci_bridge_exitfn(dev); -bridge_error: + return err; } diff --git a/qemu/hw/pci-bridge/pci_expander_bridge.c b/qemu/hw/pci-bridge/pci_expander_bridge.c index 57f8a3762..ba320bd85 100644 --- a/qemu/hw/pci-bridge/pci_expander_bridge.c +++ b/qemu/hw/pci-bridge/pci_expander_bridge.c @@ -10,6 +10,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bus.h" #include "hw/pci/pci_host.h" @@ -23,6 +24,9 @@ #define TYPE_PXB_BUS "pxb-bus" #define PXB_BUS(obj) OBJECT_CHECK(PXBBus, (obj), TYPE_PXB_BUS) +#define TYPE_PXB_PCIE_BUS "pxb-pcie-bus" +#define PXB_PCIE_BUS(obj) OBJECT_CHECK(PXBBus, (obj), TYPE_PXB_PCIE_BUS) + typedef struct PXBBus { /*< private >*/ PCIBus parent_obj; @@ -34,6 +38,9 @@ typedef struct PXBBus { #define TYPE_PXB_DEVICE "pxb" #define PXB_DEV(obj) OBJECT_CHECK(PXBDev, (obj), TYPE_PXB_DEVICE) +#define TYPE_PXB_PCIE_DEVICE "pxb-pcie" +#define PXB_PCIE_DEV(obj) OBJECT_CHECK(PXBDev, (obj), TYPE_PXB_PCIE_DEVICE) + typedef struct PXBDev { /*< private >*/ PCIDevice parent_obj; @@ -43,13 +50,18 @@ typedef struct PXBDev { uint16_t numa_node; } PXBDev; +static PXBDev *convert_to_pxb(PCIDevice *dev) +{ + return pci_bus_is_express(dev->bus) ? PXB_PCIE_DEV(dev) : PXB_DEV(dev); +} + static GList *pxb_dev_list; #define TYPE_PXB_HOST "pxb-host" static int pxb_bus_num(PCIBus *bus) { - PXBDev *pxb = PXB_DEV(bus->parent_dev); + PXBDev *pxb = convert_to_pxb(bus->parent_dev); return pxb->bus_nr; } @@ -61,7 +73,7 @@ static bool pxb_is_root(PCIBus *bus) static uint16_t pxb_bus_numa_node(PCIBus *bus) { - PXBDev *pxb = PXB_DEV(bus->parent_dev); + PXBDev *pxb = convert_to_pxb(bus->parent_dev); return pxb->numa_node; } @@ -82,10 +94,18 @@ static const TypeInfo pxb_bus_info = { .class_init = pxb_bus_class_init, }; +static const TypeInfo pxb_pcie_bus_info = { + .name = TYPE_PXB_PCIE_BUS, + .parent = TYPE_PCIE_BUS, + .instance_size = sizeof(PXBBus), + .class_init = pxb_bus_class_init, +}; + static const char *pxb_host_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) { - PXBBus *bus = PXB_BUS(rootbus); + PXBBus *bus = pci_bus_is_express(rootbus) ? + PXB_PCIE_BUS(rootbus) : PXB_BUS(rootbus); snprintf(bus->bus_path, 8, "0000:%02x", pxb_bus_num(rootbus)); return bus->bus_path; @@ -103,7 +123,7 @@ static char *pxb_host_ofw_unit_address(const SysBusDevice *dev) pxb_host = PCI_HOST_BRIDGE(dev); pxb_bus = pxb_host->bus; - pxb_dev = PXB_DEV(pxb_bus->parent_dev); + pxb_dev = convert_to_pxb(pxb_bus->parent_dev); position = g_list_index(pxb_dev_list, pxb_dev); assert(position >= 0); @@ -193,10 +213,10 @@ static gint pxb_compare(gconstpointer a, gconstpointer b) 0; } -static int pxb_dev_initfn(PCIDevice *dev) +static int pxb_dev_init_common(PCIDevice *dev, bool pcie) { - PXBDev *pxb = PXB_DEV(dev); - DeviceState *ds, *bds; + PXBDev *pxb = convert_to_pxb(dev); + DeviceState *ds, *bds = NULL; PCIBus *bus; const char *dev_name = NULL; @@ -211,26 +231,31 @@ static int pxb_dev_initfn(PCIDevice *dev) } ds = qdev_create(NULL, TYPE_PXB_HOST); - bus = pci_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS); + if (pcie) { + bus = pci_bus_new(ds, dev_name, NULL, NULL, 0, TYPE_PXB_PCIE_BUS); + } else { + bus = pci_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS); + bds = qdev_create(BUS(bus), "pci-bridge"); + bds->id = dev_name; + qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr); + qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false); + } bus->parent_dev = dev; bus->address_space_mem = dev->bus->address_space_mem; bus->address_space_io = dev->bus->address_space_io; bus->map_irq = pxb_map_irq_fn; - bds = qdev_create(BUS(bus), "pci-bridge"); - bds->id = dev_name; - qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr); - qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false); - PCI_HOST_BRIDGE(ds)->bus = bus; if (pxb_register_bus(dev, bus)) { - return -EINVAL; + goto err_register_bus; } qdev_init_nofail(ds); - qdev_init_nofail(bds); + if (bds) { + qdev_init_nofail(bds); + } pci_word_test_and_set_mask(dev->config + PCI_STATUS, PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); @@ -238,17 +263,33 @@ static int pxb_dev_initfn(PCIDevice *dev) pxb_dev_list = g_list_insert_sorted(pxb_dev_list, pxb, pxb_compare); return 0; + +err_register_bus: + object_unref(OBJECT(bds)); + object_unparent(OBJECT(bus)); + object_unref(OBJECT(ds)); + return -EINVAL; +} + +static int pxb_dev_initfn(PCIDevice *dev) +{ + if (pci_bus_is_express(dev->bus)) { + error_report("pxb devices cannot reside on a PCIe bus!"); + return -EINVAL; + } + + return pxb_dev_init_common(dev, false); } static void pxb_dev_exitfn(PCIDevice *pci_dev) { - PXBDev *pxb = PXB_DEV(pci_dev); + PXBDev *pxb = convert_to_pxb(pci_dev); pxb_dev_list = g_list_remove(pxb_dev_list, pxb); } static Property pxb_dev_properties[] = { - /* Note: 0 is not a legal a PXB bus number. */ + /* Note: 0 is not a legal PXB bus number. */ DEFINE_PROP_UINT8("bus_nr", PXBDev, bus_nr, 0), DEFINE_PROP_UINT16("numa_node", PXBDev, numa_node, NUMA_NODE_UNASSIGNED), DEFINE_PROP_END_OF_LIST(), @@ -267,6 +308,7 @@ static void pxb_dev_class_init(ObjectClass *klass, void *data) dc->desc = "PCI Expander Bridge"; dc->props = pxb_dev_properties; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo pxb_dev_info = { @@ -276,11 +318,46 @@ static const TypeInfo pxb_dev_info = { .class_init = pxb_dev_class_init, }; +static int pxb_pcie_dev_initfn(PCIDevice *dev) +{ + if (!pci_bus_is_express(dev->bus)) { + error_report("pxb-pcie devices cannot reside on a PCI bus!"); + return -EINVAL; + } + + return pxb_dev_init_common(dev, true); +} + +static void pxb_pcie_dev_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = pxb_pcie_dev_initfn; + k->exit = pxb_dev_exitfn; + k->vendor_id = PCI_VENDOR_ID_REDHAT; + k->device_id = PCI_DEVICE_ID_REDHAT_PXB_PCIE; + k->class_id = PCI_CLASS_BRIDGE_HOST; + + dc->desc = "PCI Express Expander Bridge"; + dc->props = pxb_dev_properties; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); +} + +static const TypeInfo pxb_pcie_dev_info = { + .name = TYPE_PXB_PCIE_DEVICE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PXBDev), + .class_init = pxb_pcie_dev_class_init, +}; + static void pxb_register_types(void) { type_register_static(&pxb_bus_info); + type_register_static(&pxb_pcie_bus_info); type_register_static(&pxb_host_info); type_register_static(&pxb_dev_info); + type_register_static(&pxb_pcie_dev_info); } type_init(pxb_register_types) diff --git a/qemu/hw/pci-bridge/xio3130_downstream.c b/qemu/hw/pci-bridge/xio3130_downstream.c index b3a647926..cf1ee63ab 100644 --- a/qemu/hw/pci-bridge/xio3130_downstream.c +++ b/qemu/hw/pci-bridge/xio3130_downstream.c @@ -19,6 +19,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/pci/pci_ids.h" #include "hw/pci/msi.h" #include "hw/pci/pcie.h" @@ -60,11 +61,7 @@ static int xio3130_downstream_initfn(PCIDevice *d) PCIESlot *s = PCIE_SLOT(d); int rc; - rc = pci_bridge_initfn(d, TYPE_PCIE_BUS); - if (rc < 0) { - return rc; - } - + pci_bridge_initfn(d, TYPE_PCIE_BUS); pcie_port_init_reg(d); rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR, @@ -92,7 +89,7 @@ static int xio3130_downstream_initfn(PCIDevice *d) goto err_pcie_cap; } pcie_cap_arifwd_init(d); - rc = pcie_aer_init(d, XIO3130_AER_OFFSET); + rc = pcie_aer_init(d, XIO3130_AER_OFFSET, PCI_ERR_SIZEOF); if (rc < 0) { goto err; } diff --git a/qemu/hw/pci-bridge/xio3130_upstream.c b/qemu/hw/pci-bridge/xio3130_upstream.c index eada5828a..164ef58c4 100644 --- a/qemu/hw/pci-bridge/xio3130_upstream.c +++ b/qemu/hw/pci-bridge/xio3130_upstream.c @@ -19,6 +19,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/pci/pci_ids.h" #include "hw/pci/msi.h" #include "hw/pci/pcie.h" @@ -56,11 +57,7 @@ static int xio3130_upstream_initfn(PCIDevice *d) PCIEPort *p = PCIE_PORT(d); int rc; - rc = pci_bridge_initfn(d, TYPE_PCIE_BUS); - if (rc < 0) { - return rc; - } - + pci_bridge_initfn(d, TYPE_PCIE_BUS); pcie_port_init_reg(d); rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR, @@ -81,7 +78,7 @@ static int xio3130_upstream_initfn(PCIDevice *d) } pcie_cap_flr_init(d); pcie_cap_deverr_init(d); - rc = pcie_aer_init(d, XIO3130_AER_OFFSET); + rc = pcie_aer_init(d, XIO3130_AER_OFFSET, PCI_ERR_SIZEOF); if (rc < 0) { goto err; } diff --git a/qemu/hw/pci-host/apb.c b/qemu/hw/pci-host/apb.c index 599768e2d..aaef7bb3a 100644 --- a/qemu/hw/pci-host/apb.c +++ b/qemu/hw/pci-host/apb.c @@ -27,6 +27,7 @@ Ultrasparc PCI host is called the PCI Bus Module (PBM). The APB is the secondary PCI bridge. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" @@ -634,12 +635,7 @@ static void pci_apb_set_irq(void *opaque, int irq_num, int level) static int apb_pci_bridge_initfn(PCIDevice *dev) { - int rc; - - rc = pci_bridge_initfn(dev, TYPE_PCI_BUS); - if (rc < 0) { - return rc; - } + pci_bridge_initfn(dev, TYPE_PCI_BUS); /* * command register: diff --git a/qemu/hw/pci-host/bonito.c b/qemu/hw/pci-host/bonito.c index 3a731fe18..1999ece59 100644 --- a/qemu/hw/pci-host/bonito.c +++ b/qemu/hw/pci-host/bonito.c @@ -37,7 +37,7 @@ * north bridge address to pci address. */ -#include +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" @@ -180,8 +180,6 @@ #define PCI_ADDR(busno,devno,funno,regno) \ ((((busno)<<16)&0xff0000) + (((devno)<<11)&0xf800) + (((funno)<<8)&0x700) + (regno)) -#define TYPE_BONITO_PCI_HOST_BRIDGE "Bonito-pcihost" - typedef struct BonitoState BonitoState; typedef struct PCIBonitoState @@ -215,17 +213,20 @@ typedef struct PCIBonitoState } PCIBonitoState; -#define BONITO_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(BonitoState, (obj), TYPE_BONITO_PCI_HOST_BRIDGE) - struct BonitoState { PCIHostState parent_obj; - qemu_irq *pic; - PCIBonitoState *pci_dev; }; +#define TYPE_BONITO_PCI_HOST_BRIDGE "Bonito-pcihost" +#define BONITO_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(BonitoState, (obj), TYPE_BONITO_PCI_HOST_BRIDGE) + +#define TYPE_PCI_BONITO "Bonito" +#define PCI_BONITO(obj) \ + OBJECT_CHECK(PCIBonitoState, (obj), TYPE_PCI_BONITO) + static void bonito_writel(void *opaque, hwaddr addr, uint64_t val, unsigned size) { @@ -355,6 +356,10 @@ static uint64_t bonito_ldma_readl(void *opaque, hwaddr addr, uint32_t val; PCIBonitoState *s = opaque; + if (addr >= sizeof(s->bonldma)) { + return 0; + } + val = ((uint32_t *)(&s->bonldma))[addr/sizeof(uint32_t)]; return val; @@ -365,6 +370,10 @@ static void bonito_ldma_writel(void *opaque, hwaddr addr, { PCIBonitoState *s = opaque; + if (addr >= sizeof(s->bonldma)) { + return; + } + ((uint32_t *)(&s->bonldma))[addr/sizeof(uint32_t)] = val & 0xffffffff; } @@ -384,6 +393,10 @@ static uint64_t bonito_cop_readl(void *opaque, hwaddr addr, uint32_t val; PCIBonitoState *s = opaque; + if (addr >= sizeof(s->boncop)) { + return 0; + } + val = ((uint32_t *)(&s->boncop))[addr/sizeof(uint32_t)]; return val; @@ -394,6 +407,10 @@ static void bonito_cop_writel(void *opaque, hwaddr addr, { PCIBonitoState *s = opaque; + if (addr >= sizeof(s->boncop)) { + return; + } + ((uint32_t *)(&s->boncop))[addr/sizeof(uint32_t)] = val & 0xffffffff; } @@ -707,7 +724,7 @@ static int bonito_pcihost_initfn(SysBusDevice *dev) static void bonito_realize(PCIDevice *dev, Error **errp) { - PCIBonitoState *s = DO_UPCAST(PCIBonitoState, dev, dev); + PCIBonitoState *s = PCI_BONITO(dev); SysBusDevice *sysbus = SYS_BUS_DEVICE(s->pcihost); PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); @@ -783,8 +800,8 @@ PCIBus *bonito_init(qemu_irq *pic) qdev_init_nofail(dev); /* set the pcihost pointer before bonito_initfn is called */ - d = pci_create(phb->bus, PCI_DEVFN(0, 0), "Bonito"); - s = DO_UPCAST(PCIBonitoState, dev, d); + d = pci_create(phb->bus, PCI_DEVFN(0, 0), TYPE_PCI_BONITO); + s = PCI_BONITO(d); s->pcihost = pcihost; pcihost->pci_dev = s; qdev_init_nofail(DEVICE(d)); @@ -812,7 +829,7 @@ static void bonito_class_init(ObjectClass *klass, void *data) } static const TypeInfo bonito_info = { - .name = "Bonito", + .name = TYPE_PCI_BONITO, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIBonitoState), .class_init = bonito_class_init, diff --git a/qemu/hw/pci-host/gpex.c b/qemu/hw/pci-host/gpex.c index 9d8fb5a49..66055ee5c 100644 --- a/qemu/hw/pci-host/gpex.c +++ b/qemu/hw/pci-host/gpex.c @@ -28,6 +28,7 @@ * http://www.kernel.org/doc/Documentation/devicetree/bindings/pci/host-generic-pci.txt * http://www.firmware.org/1275/practice/imap/imap0_9d.pdf */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci-host/gpex.h" diff --git a/qemu/hw/pci-host/grackle.c b/qemu/hw/pci-host/grackle.c index bfe707a1a..8f9121615 100644 --- a/qemu/hw/pci-host/grackle.c +++ b/qemu/hw/pci-host/grackle.c @@ -23,6 +23,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/pci/pci_host.h" #include "hw/ppc/mac.h" #include "hw/pci/pci.h" @@ -146,8 +147,10 @@ static const TypeInfo grackle_pci_info = { static void pci_grackle_class_init(ObjectClass *klass, void *data) { SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); k->init = pci_grackle_init_device; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo grackle_pci_host_info = { diff --git a/qemu/hw/pci-host/pam.c b/qemu/hw/pci-host/pam.c index 17d826cba..e361ecb7e 100644 --- a/qemu/hw/pci-host/pam.c +++ b/qemu/hw/pci-host/pam.c @@ -27,6 +27,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qom/object.h" #include "sysemu/sysemu.h" #include "hw/pci-host/pam.h" diff --git a/qemu/hw/pci-host/piix.c b/qemu/hw/pci-host/piix.c index ad55f9966..df2b0e26f 100644 --- a/qemu/hw/pci-host/piix.c +++ b/qemu/hw/pci-host/piix.c @@ -22,25 +22,27 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" #include "hw/isa/isa.h" #include "hw/sysbus.h" +#include "qapi/error.h" #include "qemu/range.h" #include "hw/xen/xen.h" #include "hw/pci-host/pam.h" #include "sysemu/sysemu.h" #include "hw/i386/ioapic.h" #include "qapi/visitor.h" +#include "qemu/error-report.h" /* * I440FX chipset data sheet. * http://download.intel.com/design/chipsets/datashts/29054901.pdf */ -#define TYPE_I440FX_PCI_HOST_BRIDGE "i440FX-pcihost" #define I440FX_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(I440FXState, (obj), TYPE_I440FX_PCI_HOST_BRIDGE) @@ -95,7 +97,6 @@ typedef struct PIIX3State { #define PIIX3_PCI_DEVICE(obj) \ OBJECT_CHECK(PIIX3State, (obj), TYPE_PIIX3_PCI_DEVICE) -#define TYPE_I440FX_PCI_DEVICE "i440FX" #define I440FX_PCI_DEVICE(obj) \ OBJECT_CHECK(PCII440FXState, (obj), TYPE_I440FX_PCI_DEVICE) @@ -117,6 +118,11 @@ struct PCII440FXState { #define I440FX_PAM_SIZE 7 #define I440FX_SMRAM 0x72 +/* Older coreboot versions (4.0 and older) read a config register that doesn't + * exist in real hardware, to get the RAM size from QEMU. + */ +#define I440FX_COREBOOT_RAM_SIZE 0x57 + static void piix3_set_irq(void *opaque, int pirq, int level); static PCIINTxRoute piix3_route_intx_pin_to_irq(void *opaque, int pci_intx); static void piix3_write_config_xen(PCIDevice *dev, @@ -211,39 +217,39 @@ static const VMStateDescription vmstate_i440fx = { }; static void i440fx_pcihost_get_pci_hole_start(Object *obj, Visitor *v, - void *opaque, const char *name, + const char *name, void *opaque, Error **errp) { I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); uint32_t value = s->pci_info.w32.begin; - visit_type_uint32(v, &value, name, errp); + visit_type_uint32(v, name, &value, errp); } static void i440fx_pcihost_get_pci_hole_end(Object *obj, Visitor *v, - void *opaque, const char *name, + const char *name, void *opaque, Error **errp) { I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); uint32_t value = s->pci_info.w32.end; - visit_type_uint32(v, &value, name, errp); + visit_type_uint32(v, name, &value, errp); } static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v, - void *opaque, const char *name, - Error **errp) + const char *name, + void *opaque, Error **errp) { PCIHostState *h = PCI_HOST_BRIDGE(obj); Range w64; pci_bus_get_w64_range(h->bus, &w64); - visit_type_uint64(v, &w64.begin, name, errp); + visit_type_uint64(v, name, &w64.begin, errp); } static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v, - void *opaque, const char *name, + const char *name, void *opaque, Error **errp) { PCIHostState *h = PCI_HOST_BRIDGE(obj); @@ -251,7 +257,7 @@ static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v, pci_bus_get_w64_range(h->bus, &w64); - visit_type_uint64(v, &w64.end, name, errp); + visit_type_uint64(v, name, &w64.end, errp); } static void i440fx_pcihost_initfn(Object *obj) @@ -298,9 +304,14 @@ static void i440fx_pcihost_realize(DeviceState *dev, Error **errp) static void i440fx_realize(PCIDevice *dev, Error **errp) { dev->config[I440FX_SMRAM] = 0x02; + + if (object_property_get_bool(qdev_get_machine(), "iommu", NULL)) { + error_report("warning: i440fx doesn't support emulated iommu"); + } } -PCIBus *i440fx_init(PCII440FXState **pi440fx_state, +PCIBus *i440fx_init(const char *host_type, const char *pci_type, + PCII440FXState **pi440fx_state, int *piix3_devfn, ISABus **isa_bus, qemu_irq *pic, MemoryRegion *address_space_mem, @@ -320,7 +331,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, unsigned i; I440FXState *i440fx; - dev = qdev_create(NULL, TYPE_I440FX_PCI_HOST_BRIDGE); + dev = qdev_create(NULL, host_type); s = PCI_HOST_BRIDGE(dev); b = pci_bus_new(dev, NULL, pci_address_space, address_space_io, 0, TYPE_PCI_BUS); @@ -328,7 +339,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, object_property_add_child(qdev_get_machine(), "i440fx", OBJECT(dev), NULL); qdev_init_nofail(dev); - d = pci_create_simple(b, 0, TYPE_I440FX_PCI_DEVICE); + d = pci_create_simple(b, 0, pci_type); *pi440fx_state = I440FX_PCI_DEVICE(d); f = *pi440fx_state; f->system_memory = address_space_mem; @@ -394,7 +405,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, if (ram_size > 255) { ram_size = 255; } - d->config[0x57] = ram_size; + d->config[I440FX_COREBOOT_RAM_SIZE] = ram_size; i440fx_update_memory_mappings(f); @@ -642,8 +653,10 @@ static void piix3_realize(PCIDevice *dev, Error **errp) { PIIX3State *d = PIIX3_PCI_DEVICE(dev); - isa_bus_new(DEVICE(d), get_system_memory(), - pci_address_space_io(dev)); + if (!isa_bus_new(DEVICE(d), get_system_memory(), + pci_address_space_io(dev), errp)) { + return; + } memory_region_init_io(&d->rcr_mem, OBJECT(dev), &rcr_ops, d, "piix3-reset-control", 1); @@ -735,6 +748,95 @@ static const TypeInfo i440fx_info = { .class_init = i440fx_class_init, }; +/* IGD Passthrough Host Bridge. */ +typedef struct { + uint8_t offset; + uint8_t len; +} IGDHostInfo; + +/* Here we just expose minimal host bridge offset subset. */ +static const IGDHostInfo igd_host_bridge_infos[] = { + {0x08, 2}, /* revision id */ + {0x2c, 2}, /* sybsystem vendor id */ + {0x2e, 2}, /* sybsystem id */ + {0x50, 2}, /* SNB: processor graphics control register */ + {0x52, 2}, /* processor graphics control register */ + {0xa4, 4}, /* SNB: graphics base of stolen memory */ + {0xa8, 4}, /* SNB: base of GTT stolen memory */ +}; + +static int host_pci_config_read(int pos, int len, uint32_t *val) +{ + char path[PATH_MAX]; + int config_fd; + ssize_t size = sizeof(path); + /* Access real host bridge. */ + int rc = snprintf(path, size, "/sys/bus/pci/devices/%04x:%02x:%02x.%d/%s", + 0, 0, 0, 0, "config"); + int ret = 0; + + if (rc >= size || rc < 0) { + return -ENODEV; + } + + config_fd = open(path, O_RDWR); + if (config_fd < 0) { + return -ENODEV; + } + + if (lseek(config_fd, pos, SEEK_SET) != pos) { + ret = -errno; + goto out; + } + + do { + rc = read(config_fd, (uint8_t *)val, len); + } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); + if (rc != len) { + ret = -errno; + } + +out: + close(config_fd); + return ret; +} + +static int igd_pt_i440fx_initfn(struct PCIDevice *pci_dev) +{ + uint32_t val = 0; + int rc, i, num; + int pos, len; + + num = ARRAY_SIZE(igd_host_bridge_infos); + for (i = 0; i < num; i++) { + pos = igd_host_bridge_infos[i].offset; + len = igd_host_bridge_infos[i].len; + rc = host_pci_config_read(pos, len, &val); + if (rc) { + return -ENODEV; + } + pci_default_write_config(pci_dev, pos, val, len); + } + + return 0; +} + +static void igd_passthrough_i440fx_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = igd_pt_i440fx_initfn; + dc->desc = "IGD Passthrough Host bridge"; +} + +static const TypeInfo igd_passthrough_i440fx_info = { + .name = TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE, + .parent = TYPE_I440FX_PCI_DEVICE, + .instance_size = sizeof(PCII440FXState), + .class_init = igd_passthrough_i440fx_class_init, +}; + static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) { @@ -776,6 +878,7 @@ static const TypeInfo i440fx_pcihost_info = { static void i440fx_register_types(void) { type_register_static(&i440fx_info); + type_register_static(&igd_passthrough_i440fx_info); type_register_static(&piix3_pci_type_info); type_register_static(&piix3_info); type_register_static(&piix3_xen_info); diff --git a/qemu/hw/pci-host/ppce500.c b/qemu/hw/pci-host/ppce500.c index 613ba73c6..e502bc050 100644 --- a/qemu/hw/pci-host/ppce500.c +++ b/qemu/hw/pci-host/ppce500.c @@ -14,6 +14,7 @@ * (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/ppc/e500-ccsr.h" #include "hw/pci/pci.h" @@ -140,7 +141,7 @@ static uint64_t pci_reg_read4(void *opaque, hwaddr addr, case PPCE500_PCI_OW3: case PPCE500_PCI_OW4: idx = (addr >> 5) & 0x7; - switch (addr & 0xC) { + switch (addr & 0x1F) { case PCI_POTAR: value = pci->pob[idx].potar; break; @@ -162,7 +163,7 @@ static uint64_t pci_reg_read4(void *opaque, hwaddr addr, case PPCE500_PCI_IW2: case PPCE500_PCI_IW1: idx = ((addr >> 5) & 0x3) - 1; - switch (addr & 0xC) { + switch (addr & 0x1F) { case PCI_PITAR: value = pci->pib[idx].pitar; break; diff --git a/qemu/hw/pci-host/prep.c b/qemu/hw/pci-host/prep.c index c63f45d21..487e32ecb 100644 --- a/qemu/hw/pci-host/prep.c +++ b/qemu/hw/pci-host/prep.c @@ -23,6 +23,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bus.h" @@ -302,7 +304,7 @@ static void raven_realize(PCIDevice *d, Error **errp) d->config[0x34] = 0x00; // capabilities_pointer memory_region_init_ram(&s->bios, OBJECT(s), "bios", BIOS_SIZE, - &error_abort); + &error_fatal); memory_region_set_readonly(&s->bios, true); memory_region_add_subregion(get_system_memory(), (uint32_t)(-BIOS_SIZE), &s->bios); @@ -312,7 +314,7 @@ static void raven_realize(PCIDevice *d, Error **errp) if (filename) { if (s->elf_machine != EM_NONE) { bios_size = load_elf(filename, NULL, NULL, NULL, - NULL, NULL, 1, s->elf_machine, 0); + NULL, NULL, 1, s->elf_machine, 0, 0); } if (bios_size < 0) { bios_size = get_image_size(filename); @@ -326,11 +328,10 @@ static void raven_realize(PCIDevice *d, Error **errp) } } if (bios_size < 0 || bios_size > BIOS_SIZE) { + /* FIXME should error_setg() */ hw_error("qemu: could not load bios image '%s'\n", s->bios_name); } - if (filename) { - g_free(filename); - } + g_free(filename); } } @@ -357,8 +358,9 @@ static void raven_class_init(ObjectClass *klass, void *data) dc->desc = "PReP Host Bridge - Motorola Raven"; dc->vmsd = &vmstate_raven; /* - * PCI-facing part of the host bridge, not usable without the - * host-facing part, which can't be device_add'ed, yet. + * Reason: PCI-facing part of the host bridge, not usable without + * the host-facing part, which can't be device_add'ed, yet. + * Reason: realize() method uses hw_error(). */ dc->cannot_instantiate_with_device_add_yet = true; } diff --git a/qemu/hw/pci-host/q35.c b/qemu/hw/pci-host/q35.c index bd7409456..70f897e3a 100644 --- a/qemu/hw/pci-host/q35.c +++ b/qemu/hw/pci-host/q35.c @@ -27,8 +27,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci-host/q35.h" +#include "qapi/error.h" #include "qapi/visitor.h" /**************************************************************************** @@ -67,27 +69,27 @@ static const char *q35_host_root_bus_path(PCIHostState *host_bridge, } static void q35_host_get_pci_hole_start(Object *obj, Visitor *v, - void *opaque, const char *name, + const char *name, void *opaque, Error **errp) { Q35PCIHost *s = Q35_HOST_DEVICE(obj); uint32_t value = s->mch.pci_info.w32.begin; - visit_type_uint32(v, &value, name, errp); + visit_type_uint32(v, name, &value, errp); } static void q35_host_get_pci_hole_end(Object *obj, Visitor *v, - void *opaque, const char *name, + const char *name, void *opaque, Error **errp) { Q35PCIHost *s = Q35_HOST_DEVICE(obj); uint32_t value = s->mch.pci_info.w32.end; - visit_type_uint32(v, &value, name, errp); + visit_type_uint32(v, name, &value, errp); } static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v, - void *opaque, const char *name, + const char *name, void *opaque, Error **errp) { PCIHostState *h = PCI_HOST_BRIDGE(obj); @@ -95,11 +97,11 @@ static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v, pci_bus_get_w64_range(h->bus, &w64); - visit_type_uint64(v, &w64.begin, name, errp); + visit_type_uint64(v, name, &w64.begin, errp); } static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v, - void *opaque, const char *name, + const char *name, void *opaque, Error **errp) { PCIHostState *h = PCI_HOST_BRIDGE(obj); @@ -107,17 +109,16 @@ static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v, pci_bus_get_w64_range(h->bus, &w64); - visit_type_uint64(v, &w64.end, name, errp); + visit_type_uint64(v, name, &w64.end, errp); } -static void q35_host_get_mmcfg_size(Object *obj, Visitor *v, - void *opaque, const char *name, - Error **errp) +static void q35_host_get_mmcfg_size(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { PCIExpressHost *e = PCIE_HOST_BRIDGE(obj); uint32_t value = e->size; - visit_type_uint32(v, &value, name, errp); + visit_type_uint32(v, name, &value, errp); } static Property mch_props[] = { @@ -426,31 +427,12 @@ static void mch_reset(DeviceState *qdev) static AddressSpace *q35_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) { IntelIOMMUState *s = opaque; - VTDAddressSpace **pvtd_as; - int bus_num = pci_bus_num(bus); + VTDAddressSpace *vtd_as; - assert(0 <= bus_num && bus_num <= VTD_PCI_BUS_MAX); assert(0 <= devfn && devfn <= VTD_PCI_DEVFN_MAX); - pvtd_as = s->address_spaces[bus_num]; - if (!pvtd_as) { - /* No corresponding free() */ - pvtd_as = g_malloc0(sizeof(VTDAddressSpace *) * VTD_PCI_DEVFN_MAX); - s->address_spaces[bus_num] = pvtd_as; - } - if (!pvtd_as[devfn]) { - pvtd_as[devfn] = g_malloc0(sizeof(VTDAddressSpace)); - - pvtd_as[devfn]->bus_num = (uint8_t)bus_num; - pvtd_as[devfn]->devfn = (uint8_t)devfn; - pvtd_as[devfn]->iommu_state = s; - pvtd_as[devfn]->context_cache_entry.context_cache_gen = 0; - memory_region_init_iommu(&pvtd_as[devfn]->iommu, OBJECT(s), - &s->iommu_ops, "intel_iommu", UINT64_MAX); - address_space_init(&pvtd_as[devfn]->as, - &pvtd_as[devfn]->iommu, "intel_iommu"); - } - return &pvtd_as[devfn]->as; + vtd_as = vtd_find_add_as(s, bus, devfn); + return &vtd_as->as; } static void mch_init_dmar(MCHPCIState *mch) @@ -525,7 +507,7 @@ static void mch_realize(PCIDevice *d, Error **errp) PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE); } /* Intel IOMMU (VT-d) */ - if (machine_iommu(current_machine)) { + if (object_property_get_bool(qdev_get_machine(), "iommu", NULL)) { mch_init_dmar(mch); } } diff --git a/qemu/hw/pci-host/uninorth.c b/qemu/hw/pci-host/uninorth.c index f0144eb7b..15b105423 100644 --- a/qemu/hw/pci-host/uninorth.c +++ b/qemu/hw/pci-host/uninorth.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/ppc/mac.h" #include "hw/pci/pci.h" @@ -119,7 +120,7 @@ static void unin_data_write(void *opaque, hwaddr addr, { UNINState *s = opaque; PCIHostState *phb = PCI_HOST_BRIDGE(s); - UNIN_DPRINTF("write addr %" TARGET_FMT_plx " len %d val %"PRIx64"\n", + UNIN_DPRINTF("write addr " TARGET_FMT_plx " len %d val %"PRIx64"\n", addr, len, val); pci_data_write(phb->bus, unin_get_config_reg(phb->config_reg, addr), @@ -136,7 +137,7 @@ static uint64_t unin_data_read(void *opaque, hwaddr addr, val = pci_data_read(phb->bus, unin_get_config_reg(phb->config_reg, addr), len); - UNIN_DPRINTF("read addr %" TARGET_FMT_plx " len %d val %x\n", + UNIN_DPRINTF("read addr " TARGET_FMT_plx " len %d val %x\n", addr, len, val); return val; } @@ -330,6 +331,15 @@ static void unin_agp_pci_host_realize(PCIDevice *d, Error **errp) d->config[0x0C] = 0x08; // cache_line_size d->config[0x0D] = 0x10; // latency_timer // d->config[0x34] = 0x80; // capabilities_pointer + /* + * Set kMacRISCPCIAddressSelect (0x48) register to indicate PCI + * memory space with base 0x80000000, size 0x10000000 for Apple's + * AppleMacRiscPCI driver + */ + d->config[0x48] = 0x0; + d->config[0x49] = 0x0; + d->config[0x4a] = 0x0; + d->config[0x4b] = 0x1; } static void u3_agp_pci_host_realize(PCIDevice *d, Error **errp) @@ -446,8 +456,10 @@ static const TypeInfo unin_internal_pci_host_info = { static void pci_unin_main_class_init(ObjectClass *klass, void *data) { SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); sbc->init = pci_unin_main_init_device; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo pci_unin_main_info = { @@ -460,8 +472,10 @@ static const TypeInfo pci_unin_main_info = { static void pci_u3_agp_class_init(ObjectClass *klass, void *data) { SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); sbc->init = pci_u3_agp_init_device; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo pci_u3_agp_info = { @@ -474,8 +488,10 @@ static const TypeInfo pci_u3_agp_info = { static void pci_unin_agp_class_init(ObjectClass *klass, void *data) { SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); sbc->init = pci_unin_agp_init_device; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo pci_unin_agp_info = { @@ -488,8 +504,10 @@ static const TypeInfo pci_unin_agp_info = { static void pci_unin_internal_class_init(ObjectClass *klass, void *data) { SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); sbc->init = pci_unin_internal_init_device; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo pci_unin_internal_info = { diff --git a/qemu/hw/pci-host/versatile.c b/qemu/hw/pci-host/versatile.c index 6d2355309..339ec2c50 100644 --- a/qemu/hw/pci-host/versatile.c +++ b/qemu/hw/pci-host/versatile.c @@ -7,6 +7,7 @@ * This code is licensed under the LGPL. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bus.h" @@ -500,6 +501,8 @@ static void pci_vpb_class_init(ObjectClass *klass, void *data) dc->reset = pci_vpb_reset; dc->vmsd = &pci_vpb_vmstate; dc->props = pci_vpb_properties; + /* Reason: object_unref() hangs */ + dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo pci_vpb_info = { @@ -521,10 +524,19 @@ static void pci_realview_init(Object *obj) s->mem_win_size[2] = 0x08000000; } +static void pci_realview_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(class); + + /* Reason: object_unref() hangs */ + dc->cannot_destroy_with_object_finalize_yet = true; +} + static const TypeInfo pci_realview_info = { .name = "realview_pci", .parent = TYPE_VERSATILE_PCI, .instance_init = pci_realview_init, + .class_init = pci_realview_class_init, }; static void versatile_pci_register_types(void) diff --git a/qemu/hw/pci/msi.c b/qemu/hw/pci/msi.c index f9c048442..e0e64c2d9 100644 --- a/qemu/hw/pci/msi.c +++ b/qemu/hw/pci/msi.c @@ -18,7 +18,9 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/pci/msi.h" +#include "hw/xen/xen.h" #include "qemu/range.h" /* PCI_MSI_ADDRESS_LO */ @@ -32,8 +34,21 @@ #define PCI_MSI_VECTORS_MAX 32 -/* Flag for interrupt controller to declare MSI/MSI-X support */ -bool msi_supported; +/* + * Flag for interrupt controllers to declare broken MSI/MSI-X support. + * values: false - broken; true - non-broken. + * + * Setting this flag to false will remove MSI/MSI-X capability from all devices. + * + * It is preferrable for controllers to set this to true (non-broken) even if + * they do not actually support MSI/MSI-X: guests normally probe the controller + * type and do not attempt to enable MSI/MSI-X with interrupt controllers not + * supporting such, so removing the capability is not required, and + * it seems cleaner to have a given device look the same for all boards. + * + * TODO: some existing controllers violate the above rule. Identify and fix them. + */ +bool msi_nonbroken; /* If we get rid of cap allocator, we won't need this. */ static inline uint8_t msi_cap_sizeof(uint16_t flags) @@ -158,7 +173,7 @@ int msi_init(struct PCIDevice *dev, uint8_t offset, uint8_t cap_size; int config_offset; - if (!msi_supported) { + if (!msi_nonbroken) { return -ENOTSUP; } @@ -253,13 +268,19 @@ void msi_reset(PCIDevice *dev) static bool msi_is_masked(const PCIDevice *dev, unsigned int vector) { uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); - uint32_t mask; + uint32_t mask, data; + bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; assert(vector < PCI_MSI_VECTORS_MAX); if (!(flags & PCI_MSI_FLAGS_MASKBIT)) { return false; } + data = pci_get_word(dev->config + msi_data_off(dev, msi64bit)); + if (xen_is_pirq_msi(data)) { + return false; + } + mask = pci_get_long(dev->config + msi_mask_off(dev, flags & PCI_MSI_FLAGS_64BIT)); return mask & (1U << vector); @@ -294,7 +315,7 @@ void msi_send_message(PCIDevice *dev, MSIMessage msg) { MemTxAttrs attrs = {}; - attrs.stream_id = (pci_bus_num(dev->bus) << 8) | dev->devfn; + attrs.requester_id = pci_requester_id(dev); address_space_stl_le(&dev->bus_master_as, msg.address, msg.data, attrs, NULL); } diff --git a/qemu/hw/pci/msix.c b/qemu/hw/pci/msix.c index 7716bf364..b75f0e9c4 100644 --- a/qemu/hw/pci/msix.c +++ b/qemu/hw/pci/msix.c @@ -14,10 +14,12 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "hw/pci/pci.h" +#include "hw/xen/xen.h" #include "qemu/range.h" #define MSIX_CAP_LENGTH 12 @@ -77,8 +79,15 @@ static void msix_clr_pending(PCIDevice *dev, int vector) static bool msix_vector_masked(PCIDevice *dev, unsigned int vector, bool fmask) { - unsigned offset = vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL; - return fmask || dev->msix_table[offset] & PCI_MSIX_ENTRY_CTRL_MASKBIT; + unsigned offset = vector * PCI_MSIX_ENTRY_SIZE; + uint8_t *data = &dev->msix_table[offset + PCI_MSIX_ENTRY_DATA]; + /* MSIs on Xen can be remapped into pirqs. In those cases, masking + * and unmasking go through the PV evtchn path. */ + if (xen_enabled() && xen_is_pirq_msi(pci_get_long(data))) { + return false; + } + return fmask || dev->msix_table[offset + PCI_MSIX_ENTRY_VECTOR_CTRL] & + PCI_MSIX_ENTRY_CTRL_MASKBIT; } bool msix_is_masked(PCIDevice *dev, unsigned int vector) @@ -200,8 +209,14 @@ static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr, return pci_get_long(dev->msix_pba + addr); } +static void msix_pba_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ +} + static const MemoryRegionOps msix_pba_mmio_ops = { .read = msix_pba_mmio_read, + .write = msix_pba_mmio_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, @@ -234,7 +249,7 @@ int msix_init(struct PCIDevice *dev, unsigned short nentries, uint8_t *config; /* Nothing to do if MSI is not supported by interrupt controller */ - if (!msi_supported) { + if (!msi_nonbroken) { return -ENOTSUP; } @@ -314,9 +329,7 @@ int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries, bar_size = bar_pba_offset + bar_pba_size; } - if (bar_size & (bar_size - 1)) { - bar_size = 1 << qemu_fls(bar_size); - } + bar_size = pow2ceil(bar_size); name = g_strdup_printf("%s-msix", dev->name); memory_region_init(&dev->msix_exclusive_bar, OBJECT(dev), name, bar_size); diff --git a/qemu/hw/pci/pci-stub.c b/qemu/hw/pci/pci-stub.c index 063a7c242..36d2c430c 100644 --- a/qemu/hw/pci/pci-stub.c +++ b/qemu/hw/pci/pci-stub.c @@ -18,6 +18,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "sysemu/sysemu.h" #include "monitor/monitor.h" #include "qapi/qmp/qerror.h" diff --git a/qemu/hw/pci/pci.c b/qemu/hw/pci/pci.c index a017614d4..bb605efae 100644 --- a/qemu/hw/pci/pci.c +++ b/qemu/hw/pci/pci.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bridge.h" @@ -38,6 +39,8 @@ #include "hw/pci/msix.h" #include "exec/address-spaces.h" #include "hw/hotplug.h" +#include "hw/boards.h" +#include "qemu/cutils.h" //#define DEBUG_PCI #ifdef DEBUG_PCI @@ -276,9 +279,9 @@ static void pcibus_reset(BusState *qbus) } } -static void pci_host_bus_register(PCIBus *bus, DeviceState *parent) +static void pci_host_bus_register(DeviceState *host) { - PCIHostState *host_bridge = PCI_HOST_BRIDGE(parent); + PCIHostState *host_bridge = PCI_HOST_BRIDGE(host); QLIST_INSERT_HEAD(&pci_host_bridges, host_bridge, next); } @@ -329,7 +332,6 @@ const char *pci_root_bus_path(PCIDevice *dev) } static void pci_bus_init(PCIBus *bus, DeviceState *parent, - const char *name, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, uint8_t devfn_min) @@ -342,7 +344,7 @@ static void pci_bus_init(PCIBus *bus, DeviceState *parent, /* host bridge */ QLIST_INIT(&bus->child); - pci_host_bus_register(bus, parent); + pci_host_bus_register(parent); } bool pci_bus_is_express(PCIBus *bus) @@ -362,8 +364,7 @@ void pci_bus_new_inplace(PCIBus *bus, size_t bus_size, DeviceState *parent, uint8_t devfn_min, const char *typename) { qbus_create_inplace(bus, bus_size, typename, parent, name); - pci_bus_init(bus, parent, name, address_space_mem, - address_space_io, devfn_min); + pci_bus_init(bus, parent, address_space_mem, address_space_io, devfn_min); } PCIBus *pci_bus_new(DeviceState *parent, const char *name, @@ -374,8 +375,7 @@ PCIBus *pci_bus_new(DeviceState *parent, const char *name, PCIBus *bus; bus = PCI_BUS(qbus_create(typename, parent, name)); - pci_bus_init(bus, parent, name, address_space_mem, - address_space_io, devfn_min); + pci_bus_init(bus, parent, address_space_mem, address_space_io, devfn_min); return bus; } @@ -846,6 +846,16 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, PCIConfigWriteFunc *config_write = pc->config_write; Error *local_err = NULL; AddressSpace *dma_as; + DeviceState *dev = DEVICE(pci_dev); + + pci_dev->bus = bus; + /* Only pci bridges can be attached to extra PCI root buses */ + if (pci_bus_is_root(bus) && bus->parent_dev && !pc->is_bridge) { + error_setg(errp, + "PCI: Only PCI/PCIe bridges can be plugged into %s", + bus->parent_dev->name); + return NULL; + } if (devfn < 0) { for(devfn = bus->devfn_min ; devfn < ARRAY_SIZE(bus->devices); @@ -863,9 +873,17 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, PCI_SLOT(devfn), PCI_FUNC(devfn), name, bus->devices[devfn]->name); return NULL; + } else if (dev->hotplugged && + pci_get_function_0(pci_dev)) { + error_setg(errp, "PCI: slot %d function 0 already ocuppied by %s," + " new func %s cannot be exposed to guest.", + PCI_SLOT(devfn), + bus->devices[PCI_DEVFN(PCI_SLOT(devfn), 0)]->name, + name); + + return NULL; } - pci_dev->bus = bus; pci_dev->devfn = devfn; dma_as = pci_device_iommu_address_space(pci_dev); @@ -1065,6 +1083,10 @@ static pcibus_t pci_bar_address(PCIDevice *d, pcibus_t new_addr, last_addr; int bar = pci_bar(d, reg); uint16_t cmd = pci_get_word(d->config + PCI_COMMAND); + Object *machine = qdev_get_machine(); + ObjectClass *oc = object_get_class(machine); + MachineClass *mc = MACHINE_CLASS(oc); + bool allow_0_address = mc->pci_allow_0_address; if (type & PCI_BASE_ADDRESS_SPACE_IO) { if (!(cmd & PCI_COMMAND_IO)) { @@ -1075,7 +1097,8 @@ static pcibus_t pci_bar_address(PCIDevice *d, /* Check if 32 bit BAR wraps around explicitly. * TODO: make priorities correct and remove this work around. */ - if (last_addr <= new_addr || new_addr == 0 || last_addr >= UINT32_MAX) { + if (last_addr <= new_addr || last_addr >= UINT32_MAX || + (!allow_0_address && new_addr == 0)) { return PCI_BAR_UNMAPPED; } return new_addr; @@ -1099,8 +1122,8 @@ static pcibus_t pci_bar_address(PCIDevice *d, /* XXX: as we cannot support really dynamic mappings, we handle specific values as invalid mappings. */ - if (last_addr <= new_addr || new_addr == 0 || - last_addr == PCI_BAR_UNMAPPED) { + if (last_addr <= new_addr || last_addr == PCI_BAR_UNMAPPED || + (!allow_0_address && new_addr == 0)) { return PCI_BAR_UNMAPPED; } @@ -1148,16 +1171,16 @@ static void pci_update_mappings(PCIDevice *d) /* now do the real mapping */ if (r->addr != PCI_BAR_UNMAPPED) { trace_pci_update_mappings_del(d, pci_bus_num(d->bus), - PCI_FUNC(d->devfn), PCI_SLOT(d->devfn), + PCI_FUNC(d->devfn), i, r->addr, r->size); memory_region_del_subregion(r->address_space, r->memory); } r->addr = new_addr; if (r->addr != PCI_BAR_UNMAPPED) { trace_pci_update_mappings_add(d, pci_bus_num(d->bus), - PCI_FUNC(d->devfn), PCI_SLOT(d->devfn), + PCI_FUNC(d->devfn), i, r->addr, r->size); memory_region_add_subregion_overlap(r->address_space, r->addr, r->memory, 1); @@ -2065,9 +2088,7 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, g_free(path); return; } - if (size & (size - 1)) { - size = 1 << qemu_fls(size); - } + size = pow2ceil(size); vmsd = qdev_get_vmsd(DEVICE(pdev)); @@ -2077,7 +2098,7 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, snprintf(name, sizeof(name), "%s.rom", object_get_typename(OBJECT(pdev))); } pdev->has_rom = true; - memory_region_init_ram(&pdev->rom, OBJECT(pdev), name, size, &error_abort); + memory_region_init_ram(&pdev->rom, OBJECT(pdev), name, size, &error_fatal); vmstate_register_ram(&pdev->rom, &pdev->qdev); ptr = memory_region_get_ram_ptr(&pdev->rom); load_image(path, ptr); @@ -2379,17 +2400,14 @@ static void pci_device_class_init(ObjectClass *klass, void *data) AddressSpace *pci_device_iommu_address_space(PCIDevice *dev) { PCIBus *bus = PCI_BUS(dev->bus); + PCIBus *iommu_bus = bus; - if (bus->iommu_fn) { - return bus->iommu_fn(bus, bus->iommu_opaque, dev->devfn); + while(iommu_bus && !iommu_bus->iommu_fn && iommu_bus->parent_dev) { + iommu_bus = PCI_BUS(iommu_bus->parent_dev->bus); } - - if (bus->parent_dev) { - /** We are ignoring the bus master DMA bit of the bridge - * as it would complicate things such as VFIO for no good reason */ - return pci_device_iommu_address_space(bus->parent_dev); + if (iommu_bus && iommu_bus->iommu_fn) { + return iommu_bus->iommu_fn(bus, iommu_bus->iommu_opaque, dev->devfn); } - return &address_space_memory; } @@ -2453,6 +2471,33 @@ void pci_bus_get_w64_range(PCIBus *bus, Range *range) pci_for_each_device_under_bus(bus, pci_dev_get_w64, range); } +static bool pcie_has_upstream_port(PCIDevice *dev) +{ + PCIDevice *parent_dev = pci_bridge_get_device(dev->bus); + + /* Device associated with an upstream port. + * As there are several types of these, it's easier to check the + * parent device: upstream ports are always connected to + * root or downstream ports. + */ + return parent_dev && + pci_is_express(parent_dev) && + parent_dev->exp.exp_cap && + (pcie_cap_get_type(parent_dev) == PCI_EXP_TYPE_ROOT_PORT || + pcie_cap_get_type(parent_dev) == PCI_EXP_TYPE_DOWNSTREAM); +} + +PCIDevice *pci_get_function_0(PCIDevice *pci_dev) +{ + if(pcie_has_upstream_port(pci_dev)) { + /* With an upstream PCIe port, we only support 1 device at slot 0 */ + return pci_dev->bus->devices[0]; + } else { + /* Other bus types might support multiple devices at slots 0-31 */ + return pci_dev->bus->devices[PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 0)]; + } +} + static const TypeInfo pci_device_type_info = { .name = TYPE_PCI_DEVICE, .parent = TYPE_DEVICE, diff --git a/qemu/hw/pci/pci_bridge.c b/qemu/hw/pci/pci_bridge.c index 40c97b155..3cf30bd33 100644 --- a/qemu/hw/pci/pci_bridge.c +++ b/qemu/hw/pci/pci_bridge.c @@ -29,6 +29,7 @@ * VA Linux Systems Japan K.K. */ +#include "qemu/osdep.h" #include "hw/pci/pci_bridge.h" #include "hw/pci/pci_bus.h" #include "qemu/range.h" @@ -332,7 +333,7 @@ void pci_bridge_reset(DeviceState *qdev) } /* default qdev initialization function for PCI-to-PCI bridge */ -int pci_bridge_initfn(PCIDevice *dev, const char *typename) +void pci_bridge_initfn(PCIDevice *dev, const char *typename) { PCIBus *parent = dev->bus; PCIBridge *br = PCI_BRIDGE(dev); @@ -378,7 +379,6 @@ int pci_bridge_initfn(PCIDevice *dev, const char *typename) br->windows = pci_bridge_region_init(br); QLIST_INIT(&sec_bus->child); QLIST_INSERT_HEAD(&parent->child, sec_bus, sibling); - return 0; } /* default qdev clean up function for PCI-to-PCI bridge */ diff --git a/qemu/hw/pci/pci_host.c b/qemu/hw/pci/pci_host.c index 3e26f9256..5eaa935cb 100644 --- a/qemu/hw/pci/pci_host.c +++ b/qemu/hw/pci/pci_host.c @@ -18,8 +18,10 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" +#include "hw/pci/pci_bus.h" #include "trace.h" /* debug PCI */ @@ -52,6 +54,13 @@ void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr, uint32_t limit, uint32_t val, uint32_t len) { assert(len <= 4); + /* non-zero functions are only exposed when function 0 is present, + * allowing direct removal of unexposed functions. + */ + if (pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) { + return; + } + trace_pci_cfg_write(pci_dev->name, PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn), addr, val); pci_dev->config_write(pci_dev, addr, val, MIN(len, limit - addr)); @@ -63,6 +72,13 @@ uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, uint32_t ret; assert(len <= 4); + /* non-zero functions are only exposed when function 0 is present, + * allowing direct removal of unexposed functions. + */ + if (pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) { + return ~0x0; + } + ret = pci_dev->config_read(pci_dev, addr, MIN(len, limit - addr)); trace_pci_cfg_read(pci_dev->name, PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn), addr, ret); diff --git a/qemu/hw/pci/pcie.c b/qemu/hw/pci/pcie.c index 6e28985bd..728386ada 100644 --- a/qemu/hw/pci/pcie.c +++ b/qemu/hw/pci/pcie.c @@ -18,6 +18,8 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "hw/pci/pci_bridge.h" #include "hw/pci/pcie.h" @@ -249,25 +251,43 @@ void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, return; } - /* TODO: multifunction hot-plug. - * Right now, only a device of function = 0 is allowed to be - * hot plugged/unplugged. + /* To enable multifunction hot-plug, we just ensure the function + * 0 added last. When function 0 is added, we set the sltsta and + * inform OS via event notification. */ - assert(PCI_FUNC(pci_dev->devfn) == 0); + if (pci_get_function_0(pci_dev)) { + pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDS); + pcie_cap_slot_event(PCI_DEVICE(hotplug_dev), + PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP); + } +} - pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_PDS); - pcie_cap_slot_event(PCI_DEVICE(hotplug_dev), - PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP); +static void pcie_unplug_device(PCIBus *bus, PCIDevice *dev, void *opaque) +{ + object_unparent(OBJECT(dev)); } void pcie_cap_slot_hot_unplug_request_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { uint8_t *exp_cap; + PCIDevice *pci_dev = PCI_DEVICE(dev); + PCIBus *bus = pci_dev->bus; pcie_cap_slot_hotplug_common(PCI_DEVICE(hotplug_dev), dev, &exp_cap, errp); + /* In case user cancel the operation of multi-function hot-add, + * remove the function that is unexposed to guest individually, + * without interaction with guest. + */ + if (pci_dev->devfn && + !bus->devices[0]) { + pcie_unplug_device(bus, pci_dev, NULL); + + return; + } + pcie_cap_slot_push_attention_button(PCI_DEVICE(hotplug_dev)); } @@ -378,11 +398,6 @@ void pcie_cap_slot_reset(PCIDevice *dev) hotplug_event_update_event_status(dev); } -static void pcie_unplug_device(PCIBus *bus, PCIDevice *dev, void *opaque) -{ - object_unparent(OBJECT(dev)); -} - void pcie_cap_slot_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len) { @@ -413,13 +428,13 @@ void pcie_cap_slot_write_config(PCIDevice *dev, */ if ((sltsta & PCI_EXP_SLTSTA_PDS) && (val & PCI_EXP_SLTCTL_PCC) && ((val & PCI_EXP_SLTCTL_PIC_OFF) == PCI_EXP_SLTCTL_PIC_OFF)) { - PCIBus *sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(dev)); - pci_for_each_device(sec_bus, pci_bus_num(sec_bus), - pcie_unplug_device, NULL); + PCIBus *sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(dev)); + pci_for_each_device(sec_bus, pci_bus_num(sec_bus), + pcie_unplug_device, NULL); - pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_PDS); - pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDS); + pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC); } @@ -594,7 +609,7 @@ void pcie_add_capability(PCIDevice *dev, assert(offset >= PCI_CONFIG_SPACE_SIZE); assert(offset < offset + size); - assert(offset + size < PCIE_CONFIG_SPACE_SIZE); + assert(offset + size <= PCIE_CONFIG_SPACE_SIZE); assert(size >= 8); assert(pci_is_express(dev)); diff --git a/qemu/hw/pci/pcie_aer.c b/qemu/hw/pci/pcie_aer.c index f1847ac21..e2d4e68ba 100644 --- a/qemu/hw/pci/pcie_aer.c +++ b/qemu/hw/pci/pcie_aer.c @@ -18,6 +18,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "sysemu/sysemu.h" #include "qapi/qmp/types.h" #include "monitor/monitor.h" @@ -94,12 +95,12 @@ static void aer_log_clear_all_err(PCIEAERLog *aer_log) aer_log->log_num = 0; } -int pcie_aer_init(PCIDevice *dev, uint16_t offset) +int pcie_aer_init(PCIDevice *dev, uint16_t offset, uint16_t size) { PCIExpressDevice *exp; pcie_add_capability(dev, PCI_EXT_CAP_ID_ERR, PCI_ERR_VER, - offset, PCI_ERR_SIZEOF); + offset, size); exp = &dev->exp; exp->aer_cap = offset; @@ -370,7 +371,7 @@ static void pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg) * * Walk up the bus tree from the device, propagate the error message. */ -static void pcie_aer_msg(PCIDevice *dev, const PCIEAERMsg *msg) +void pcie_aer_msg(PCIDevice *dev, const PCIEAERMsg *msg) { uint8_t type; @@ -827,10 +828,6 @@ typedef struct PCIEAERErrorName { */ static const struct PCIEAERErrorName pcie_aer_error_list[] = { { - .name = "TRAIN", - .val = PCI_ERR_UNC_TRAIN, - .correctable = false, - }, { .name = "DLP", .val = PCI_ERR_UNC_DLP, .correctable = false, @@ -983,7 +980,7 @@ static int do_pcie_aer_inject_error(Monitor *mon, } } err.status = error_status; - err.source_id = (pci_bus_num(dev->bus) << 8) | dev->devfn; + err.source_id = pci_requester_id(dev); err.flags = 0; if (correctable) { diff --git a/qemu/hw/pci/pcie_host.c b/qemu/hw/pci/pcie_host.c index d8afba863..dcebf57ed 100644 --- a/qemu/hw/pci/pcie_host.c +++ b/qemu/hw/pci/pcie_host.c @@ -19,6 +19,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "hw/pci/pcie_host.h" diff --git a/qemu/hw/pci/pcie_port.c b/qemu/hw/pci/pcie_port.c index 40ca8d5d1..6432b9ac1 100644 --- a/qemu/hw/pci/pcie_port.c +++ b/qemu/hw/pci/pcie_port.c @@ -18,6 +18,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/pci/pcie_port.h" #include "hw/hotplug.h" diff --git a/qemu/hw/pci/shpc.c b/qemu/hw/pci/shpc.c index bfb4d31b6..3dcd472eb 100644 --- a/qemu/hw/pci/shpc.c +++ b/qemu/hw/pci/shpc.c @@ -1,6 +1,6 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" -#include -#include #include "qemu/range.h" #include "qemu/error-report.h" #include "hw/pci/shpc.h" diff --git a/qemu/hw/pci/slotid_cap.c b/qemu/hw/pci/slotid_cap.c index 1c01d346c..aec1e9166 100644 --- a/qemu/hw/pci/slotid_cap.c +++ b/qemu/hw/pci/slotid_cap.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/pci/slotid_cap.h" #include "hw/pci/pci.h" #include "qemu/error-report.h" diff --git a/qemu/hw/pcmcia/pcmcia.c b/qemu/hw/pcmcia/pcmcia.c index 78efe5a67..195672186 100644 --- a/qemu/hw/pcmcia/pcmcia.c +++ b/qemu/hw/pcmcia/pcmcia.c @@ -4,6 +4,7 @@ * Copyright 2013 SUSE LINUX Products GmbH */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/hw.h" #include "hw/pcmcia.h" diff --git a/qemu/hw/pcmcia/pxa2xx.c b/qemu/hw/pcmcia/pxa2xx.c index a7e187743..20c9c753d 100644 --- a/qemu/hw/pcmcia/pxa2xx.c +++ b/qemu/hw/pcmcia/pxa2xx.c @@ -10,6 +10,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "hw/pcmcia.h" @@ -163,7 +164,7 @@ static void pxa2xx_pcmcia_initfn(Object *obj) sysbus_init_mmio(sbd, &s->container_mem); /* Socket I/O Memory Space */ - memory_region_init_io(&s->iomem, NULL, &pxa2xx_pcmcia_io_ops, s, + memory_region_init_io(&s->iomem, obj, &pxa2xx_pcmcia_io_ops, s, "pxa2xx-pcmcia-io", 0x04000000); memory_region_add_subregion(&s->container_mem, 0x00000000, &s->iomem); @@ -171,13 +172,13 @@ static void pxa2xx_pcmcia_initfn(Object *obj) /* Then next 64 MB is reserved */ /* Socket Attribute Memory Space */ - memory_region_init_io(&s->attr_iomem, NULL, &pxa2xx_pcmcia_attr_ops, s, + memory_region_init_io(&s->attr_iomem, obj, &pxa2xx_pcmcia_attr_ops, s, "pxa2xx-pcmcia-attribute", 0x04000000); memory_region_add_subregion(&s->container_mem, 0x08000000, &s->attr_iomem); /* Socket Common Memory Space */ - memory_region_init_io(&s->common_iomem, NULL, &pxa2xx_pcmcia_common_ops, s, + memory_region_init_io(&s->common_iomem, obj, &pxa2xx_pcmcia_common_ops, s, "pxa2xx-pcmcia-common", 0x04000000); memory_region_add_subregion(&s->container_mem, 0x0c000000, &s->common_iomem); diff --git a/qemu/hw/ppc/Makefile.objs b/qemu/hw/ppc/Makefile.objs index c8ab06e7f..c1ffc7771 100644 --- a/qemu/hw/ppc/Makefile.objs +++ b/qemu/hw/ppc/Makefile.objs @@ -3,7 +3,7 @@ obj-y += ppc.o ppc_booke.o # IBM pSeries (sPAPR) obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o -obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o +obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) obj-y += spapr_pci_vfio.o endif diff --git a/qemu/hw/ppc/e500.c b/qemu/hw/ppc/e500.c index d300846c3..ee1c60b82 100644 --- a/qemu/hw/ppc/e500.c +++ b/qemu/hw/ppc/e500.c @@ -14,7 +14,8 @@ * (at your option) any later version. */ -#include "config.h" +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "e500.h" #include "e500-ccsr.h" @@ -751,8 +752,8 @@ static qemu_irq *ppce500_init_mpic(MachineState *machine, PPCE500Params *params, dev = ppce500_init_mpic_kvm(params, irqs, &err); } if (machine_kernel_irqchip_required(machine) && !dev) { - error_report("kernel_irqchip requested but unavailable: %s", - error_get_pretty(err)); + error_reportf_err(err, + "kernel_irqchip requested but unavailable: "); exit(1); } } @@ -1017,7 +1018,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); bios_size = load_elf(filename, NULL, NULL, &bios_entry, &loadaddr, NULL, - 1, ELF_MACHINE, 0); + 1, PPC_ELF_MACHINE, 0, 0); if (bios_size < 0) { /* * Hrm. No ELF image? Try a uImage, maybe someone is giving us an @@ -1048,10 +1049,6 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) boot_info->entry = bios_entry; boot_info->dt_base = dt_base; boot_info->dt_size = dt_size; - - if (kvm_enabled()) { - kvmppc_init(); - } } static int e500_ccsr_initfn(SysBusDevice *dev) diff --git a/qemu/hw/ppc/e500plat.c b/qemu/hw/ppc/e500plat.c index 14b14eaa7..b00565c3d 100644 --- a/qemu/hw/ppc/e500plat.c +++ b/qemu/hw/ppc/e500plat.c @@ -9,7 +9,7 @@ * (at your option) any later version. */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "e500.h" #include "hw/boards.h" @@ -57,17 +57,12 @@ static void e500plat_init(MachineState *machine) ppce500_init(machine, ¶ms); } -static QEMUMachine e500plat_machine = { - .name = "ppce500", - .desc = "generic paravirt e500 platform", - .init = e500plat_init, - .max_cpus = 32, - .has_dynamic_sysbus = true, -}; - -static void e500plat_machine_init(void) +static void e500plat_machine_init(MachineClass *mc) { - qemu_register_machine(&e500plat_machine); + mc->desc = "generic paravirt e500 platform"; + mc->init = e500plat_init; + mc->max_cpus = 32; + mc->has_dynamic_sysbus = true; } -machine_init(e500plat_machine_init); +DEFINE_MACHINE("ppce500", e500plat_machine_init) diff --git a/qemu/hw/ppc/mac.h b/qemu/hw/ppc/mac.h index 8bdba30c1..5764b86c2 100644 --- a/qemu/hw/ppc/mac.h +++ b/qemu/hw/ppc/mac.h @@ -103,11 +103,16 @@ typedef struct CUDAState { uint8_t last_b; uint8_t last_acr; + /* MacOS 9 is racy and requires a delay upon setting the SR_INT bit */ + QEMUTimer *sr_delay_timer; + int data_in_size; int data_in_index; int data_out_index; qemu_irq irq; + uint16_t adb_poll_mask; + uint8_t autopoll_rate_ms; uint8_t autopoll; uint8_t data_in[128]; uint8_t data_out[16]; @@ -131,7 +136,6 @@ typedef struct MACIOIDEState { MemoryRegion mem; IDEBus bus; - BlockAIOCB *aiocb; IDEDMA dma; void *dbdma; bool dma_active; diff --git a/qemu/hw/ppc/mac_newworld.c b/qemu/hw/ppc/mac_newworld.c index 77d5c819e..32e88b378 100644 --- a/qemu/hw/ppc/mac_newworld.c +++ b/qemu/hw/ppc/mac_newworld.c @@ -46,6 +46,8 @@ * 0001:05:0c.0 IDE interface [0101]: Broadcom K2 SATA [1166:0240] * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "hw/ppc/mac.h" @@ -62,12 +64,14 @@ #include "hw/ide.h" #include "hw/loader.h" #include "elf.h" +#include "qemu/error-report.h" #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" #define MAX_IDE_BUS 2 #define CFG_ADDR 0xf0000510 @@ -207,7 +211,7 @@ static void ppc_core99_init(MachineState *machine) /* allocate and load BIOS */ memory_region_init_ram(bios, NULL, "ppc_core99.bios", BIOS_SIZE, - &error_abort); + &error_fatal); vmstate_register_ram_global(bios); if (bios_name == NULL) @@ -219,14 +223,14 @@ static void ppc_core99_init(MachineState *machine) /* Load OpenBIOS (ELF) */ if (filename) { bios_size = load_elf(filename, NULL, NULL, NULL, - NULL, NULL, 1, ELF_MACHINE, 0); + NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); g_free(filename); } else { bios_size = -1; } if (bios_size < 0 || bios_size > BIOS_SIZE) { - hw_error("qemu: could not load PowerPC bios '%s'\n", bios_name); + error_report("could not load PowerPC bios '%s'", bios_name); exit(1); } @@ -242,7 +246,8 @@ static void ppc_core99_init(MachineState *machine) kernel_base = KERNEL_LOAD_ADDR; kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0); + NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, + 0, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, kernel_base, ram_size - kernel_base, bswap_needed, @@ -252,7 +257,7 @@ static void ppc_core99_init(MachineState *machine) kernel_base, ram_size - kernel_base); if (kernel_size < 0) { - hw_error("qemu: could not load kernel '%s'\n", kernel_filename); + error_report("could not load kernel '%s'", kernel_filename); exit(1); } /* load initrd */ @@ -261,8 +266,8 @@ static void ppc_core99_init(MachineState *machine) initrd_size = load_image_targphys(initrd_filename, initrd_base, ram_size - initrd_base); if (initrd_size < 0) { - hw_error("qemu: could not load initial ram disk '%s'\n", - initrd_filename); + error_report("could not load initial ram disk '%s'", + initrd_filename); exit(1); } cmdline_base = round_page(initrd_base + initrd_size); @@ -344,7 +349,7 @@ static void ppc_core99_init(MachineState *machine) break; #endif /* defined(TARGET_PPC64) */ default: - hw_error("Bus model not supported on mac99 machine\n"); + error_report("Bus model not supported on mac99 machine"); exit(1); } } @@ -371,12 +376,13 @@ static void ppc_core99_init(MachineState *machine) /* 970 gets a U3 bus */ pci_bus = pci_pmac_u3_init(pic, get_system_memory(), get_system_io()); machine_arch = ARCH_MAC99_U3; - machine->usb |= defaults_enabled() && !machine->usb_disabled; } else { pci_bus = pci_pmac_init(pic, get_system_memory(), get_system_io()); machine_arch = ARCH_MAC99; } + machine->usb |= defaults_enabled() && !machine->usb_disabled; + /* Timebase Frequency */ if (kvm_enabled()) { tbfreq = kvmppc_get_tbfreq(); @@ -508,7 +514,6 @@ static void core99_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - mc->name = "mac99"; mc->desc = "Mac99 based PowerMAC"; mc->init = ppc_core99_init; mc->max_cpus = MAX_CPUS; @@ -517,7 +522,7 @@ static void core99_machine_class_init(ObjectClass *oc, void *data) } static const TypeInfo core99_machine_info = { - .name = "mac99-machine", + .name = MACHINE_TYPE_NAME("mac99"), .parent = TYPE_MACHINE, .class_init = core99_machine_class_init, }; diff --git a/qemu/hw/ppc/mac_oldworld.c b/qemu/hw/ppc/mac_oldworld.c index 06fdbaf58..a9bb1c27d 100644 --- a/qemu/hw/ppc/mac_oldworld.c +++ b/qemu/hw/ppc/mac_oldworld.c @@ -23,6 +23,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "mac.h" @@ -38,10 +40,12 @@ #include "hw/ide.h" #include "hw/loader.h" #include "elf.h" +#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" #define MAX_IDE_BUS 2 #define CFG_ADDR 0xf0000510 @@ -135,7 +139,7 @@ static void ppc_heathrow_init(MachineState *machine) /* allocate and load BIOS */ memory_region_init_ram(bios, NULL, "ppc_heathrow.bios", BIOS_SIZE, - &error_abort); + &error_fatal); vmstate_register_ram_global(bios); if (bios_name == NULL) @@ -147,13 +151,13 @@ static void ppc_heathrow_init(MachineState *machine) /* Load OpenBIOS (ELF) */ if (filename) { bios_size = load_elf(filename, 0, NULL, NULL, NULL, NULL, - 1, ELF_MACHINE, 0); + 1, PPC_ELF_MACHINE, 0, 0); g_free(filename); } else { bios_size = -1; } if (bios_size < 0 || bios_size > BIOS_SIZE) { - hw_error("qemu: could not load PowerPC bios '%s'\n", bios_name); + error_report("could not load PowerPC bios '%s'", bios_name); exit(1); } @@ -168,7 +172,8 @@ static void ppc_heathrow_init(MachineState *machine) #endif kernel_base = KERNEL_LOAD_ADDR; kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0); + NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, + 0, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, kernel_base, ram_size - kernel_base, bswap_needed, @@ -178,8 +183,7 @@ static void ppc_heathrow_init(MachineState *machine) kernel_base, ram_size - kernel_base); if (kernel_size < 0) { - hw_error("qemu: could not load kernel '%s'\n", - kernel_filename); + error_report("could not load kernel '%s'", kernel_filename); exit(1); } /* load initrd */ @@ -188,8 +192,8 @@ static void ppc_heathrow_init(MachineState *machine) initrd_size = load_image_targphys(initrd_filename, initrd_base, ram_size - initrd_base); if (initrd_size < 0) { - hw_error("qemu: could not load initial ram disk '%s'\n", - initrd_filename); + error_report("could not load initial ram disk '%s'", + initrd_filename); exit(1); } cmdline_base = round_page(initrd_base + initrd_size); @@ -246,7 +250,8 @@ static void ppc_heathrow_init(MachineState *machine) ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]; break; default: - hw_error("Bus model not supported on OldWorld Mac machine\n"); + error_report("Bus model not supported on OldWorld Mac machine"); + exit(1); } } @@ -259,7 +264,8 @@ static void ppc_heathrow_init(MachineState *machine) /* init basic PC hardware */ if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) { - hw_error("Only 6xx bus is supported on heathrow machine\n"); + error_report("Only 6xx bus is supported on heathrow machine"); + exit(1); } pic = heathrow_pic_init(&pic_mem, 1, heathrow_irqs); pci_bus = pci_grackle_init(0xfec00000, pic, @@ -357,21 +363,17 @@ static int heathrow_kvm_type(const char *arg) return 2; } -static QEMUMachine heathrow_machine = { - .name = "g3beige", - .desc = "Heathrow based PowerMAC", - .init = ppc_heathrow_init, - .max_cpus = MAX_CPUS, +static void heathrow_machine_init(MachineClass *mc) +{ + mc->desc = "Heathrow based PowerMAC"; + mc->init = ppc_heathrow_init; + mc->max_cpus = MAX_CPUS; #ifndef TARGET_PPC64 - .is_default = 1, + mc->is_default = 1; #endif - .default_boot_order = "cd", /* TOFIX "cad" when Mac floppy is implemented */ - .kvm_type = heathrow_kvm_type, -}; - -static void heathrow_machine_init(void) -{ - qemu_register_machine(&heathrow_machine); + /* TOFIX "cad" when Mac floppy is implemented */ + mc->default_boot_order = "cd"; + mc->kvm_type = heathrow_kvm_type; } -machine_init(heathrow_machine_init); +DEFINE_MACHINE("g3beige", heathrow_machine_init) diff --git a/qemu/hw/ppc/mpc8544_guts.c b/qemu/hw/ppc/mpc8544_guts.c index a10abe978..ba69178d6 100644 --- a/qemu/hw/ppc/mpc8544_guts.c +++ b/qemu/hw/ppc/mpc8544_guts.c @@ -17,6 +17,9 @@ * */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "sysemu/sysemu.h" #include "hw/sysbus.h" diff --git a/qemu/hw/ppc/mpc8544ds.c b/qemu/hw/ppc/mpc8544ds.c index 3a3b141e4..27b828901 100644 --- a/qemu/hw/ppc/mpc8544ds.c +++ b/qemu/hw/ppc/mpc8544ds.c @@ -9,7 +9,7 @@ * (at your option) any later version. */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "e500.h" #include "hw/boards.h" @@ -50,16 +50,11 @@ static void mpc8544ds_init(MachineState *machine) } -static QEMUMachine ppce500_machine = { - .name = "mpc8544ds", - .desc = "mpc8544ds", - .init = mpc8544ds_init, - .max_cpus = 15, -}; - -static void ppce500_machine_init(void) +static void ppce500_machine_init(MachineClass *mc) { - qemu_register_machine(&ppce500_machine); + mc->desc = "mpc8544ds"; + mc->init = mpc8544ds_init; + mc->max_cpus = 15; } -machine_init(ppce500_machine_init); +DEFINE_MACHINE("mpc8544ds", ppce500_machine_init) diff --git a/qemu/hw/ppc/ppc.c b/qemu/hw/ppc/ppc.c index b77e30357..38ff2e159 100644 --- a/qemu/hw/ppc/ppc.c +++ b/qemu/hw/ppc/ppc.c @@ -21,6 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "hw/ppc/ppc_e500.h" @@ -462,7 +465,7 @@ void ppce500_set_mpic_proxy(bool enabled) uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset) { /* TB time in tb periods */ - return muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec()) + tb_offset; + return muldiv64(vmclk, tb_env->tb_freq, NANOSECONDS_PER_SECOND) + tb_offset; } uint64_t cpu_ppc_load_tbl (CPUPPCState *env) @@ -503,7 +506,9 @@ uint32_t cpu_ppc_load_tbu (CPUPPCState *env) static inline void cpu_ppc_store_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t *tb_offsetp, uint64_t value) { - *tb_offsetp = value - muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec()); + *tb_offsetp = value - + muldiv64(vmclk, tb_env->tb_freq, NANOSECONDS_PER_SECOND); + LOG_TB("%s: tb %016" PRIx64 " offset %08" PRIx64 "\n", __func__, value, *tb_offsetp); } @@ -637,11 +642,11 @@ static inline uint32_t _cpu_ppc_load_decr(CPUPPCState *env, uint64_t next) diff = next - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); if (diff >= 0) { - decr = muldiv64(diff, tb_env->decr_freq, get_ticks_per_sec()); + decr = muldiv64(diff, tb_env->decr_freq, NANOSECONDS_PER_SECOND); } else if (tb_env->flags & PPC_TIMER_BOOKE) { decr = 0; } else { - decr = -muldiv64(-diff, tb_env->decr_freq, get_ticks_per_sec()); + decr = -muldiv64(-diff, tb_env->decr_freq, NANOSECONDS_PER_SECOND); } LOG_TB("%s: %08" PRIx32 "\n", __func__, decr); @@ -673,7 +678,8 @@ uint64_t cpu_ppc_load_purr (CPUPPCState *env) diff = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - tb_env->purr_start; - return tb_env->purr_load + muldiv64(diff, tb_env->tb_freq, get_ticks_per_sec()); + return tb_env->purr_load + + muldiv64(diff, tb_env->tb_freq, NANOSECONDS_PER_SECOND); } /* When decrementer expires, @@ -749,7 +755,7 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, /* Calculate the next timer event */ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - next = now + muldiv64(value, get_ticks_per_sec(), tb_env->decr_freq); + next = now + muldiv64(value, NANOSECONDS_PER_SECOND, tb_env->decr_freq); *nextp = next; /* Adjust timer */ @@ -834,7 +840,7 @@ static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) static void timebase_pre_save(void *opaque) { PPCTimebase *tb = opaque; - uint64_t ticks = cpu_get_real_ticks(); + uint64_t ticks = cpu_get_host_ticks(); PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu); if (!first_ppc_cpu->env.tb_env) { @@ -878,7 +884,7 @@ static int timebase_post_load(void *opaque, int version_id) NANOSECONDS_PER_SECOND); guest_tb = tb_remote->guest_timebase + MIN(0, migration_duration_tb); - tb_off_adj = guest_tb - cpu_get_real_ticks(); + tb_off_adj = guest_tb - cpu_get_host_ticks(); tb_off = first_ppc_cpu->env.tb_env->tb_offset; trace_ppc_tb_adjust(tb_off, tb_off_adj, tb_off_adj - tb_off, @@ -1010,7 +1016,7 @@ static void cpu_4xx_fit_cb (void *opaque) /* Cannot occur, but makes gcc happy */ return; } - next = now + muldiv64(next, get_ticks_per_sec(), tb_env->tb_freq); + next = now + muldiv64(next, NANOSECONDS_PER_SECOND, tb_env->tb_freq); if (next == now) next++; timer_mod(ppc40x_timer->fit_timer, next); @@ -1041,7 +1047,7 @@ static void start_stop_pit (CPUPPCState *env, ppc_tb_t *tb_env, int is_excp) __func__, ppc40x_timer->pit_reload); now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); next = now + muldiv64(ppc40x_timer->pit_reload, - get_ticks_per_sec(), tb_env->decr_freq); + NANOSECONDS_PER_SECOND, tb_env->decr_freq); if (is_excp) next += tb_env->decr_next - now; if (next == now) @@ -1106,7 +1112,7 @@ static void cpu_4xx_wdt_cb (void *opaque) /* Cannot occur, but makes gcc happy */ return; } - next = now + muldiv64(next, get_ticks_per_sec(), tb_env->decr_freq); + next = now + muldiv64(next, NANOSECONDS_PER_SECOND, tb_env->decr_freq); if (next == now) next++; LOG_TB("%s: TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx "\n", __func__, diff --git a/qemu/hw/ppc/ppc405_boards.c b/qemu/hw/ppc/ppc405_boards.c index ec6c4cbaf..4b2f07aec 100644 --- a/qemu/hw/ppc/ppc405_boards.c +++ b/qemu/hw/ppc/ppc405_boards.c @@ -21,6 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "ppc405.h" @@ -215,7 +219,8 @@ static void ref405ep_init(MachineState *machine) 33333333, &pic, kernel_filename == NULL ? 0 : 1); /* allocate SRAM */ sram_size = 512 * 1024; - memory_region_init_ram(sram, NULL, "ef405ep.sram", sram_size, &error_abort); + memory_region_init_ram(sram, NULL, "ef405ep.sram", sram_size, + &error_fatal); vmstate_register_ram_global(sram); memory_region_add_subregion(sysmem, 0xFFF00000, sram); /* allocate and load BIOS */ @@ -250,7 +255,7 @@ static void ref405ep_init(MachineState *machine) #endif bios = g_new(MemoryRegion, 1); memory_region_init_ram(bios, NULL, "ef405ep.bios", BIOS_SIZE, - &error_abort); + &error_fatal); vmstate_register_ram_global(bios); if (bios_name == NULL) @@ -368,10 +373,18 @@ static void ref405ep_init(MachineState *machine) #endif } -static QEMUMachine ref405ep_machine = { - .name = "ref405ep", - .desc = "ref405ep", - .init = ref405ep_init, +static void ref405ep_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "ref405ep"; + mc->init = ref405ep_init; +} + +static const TypeInfo ref405ep_type = { + .name = MACHINE_TYPE_NAME("ref405ep"), + .parent = TYPE_MACHINE, + .class_init = ref405ep_class_init, }; /*****************************************************************************/ @@ -399,7 +412,7 @@ struct taihu_cpld_t { uint8_t reg1; }; -static uint32_t taihu_cpld_readb (void *opaque, hwaddr addr) +static uint64_t taihu_cpld_read(void *opaque, hwaddr addr, unsigned size) { taihu_cpld_t *cpld; uint32_t ret; @@ -420,8 +433,8 @@ static uint32_t taihu_cpld_readb (void *opaque, hwaddr addr) return ret; } -static void taihu_cpld_writeb (void *opaque, - hwaddr addr, uint32_t value) +static void taihu_cpld_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) { taihu_cpld_t *cpld; @@ -438,48 +451,12 @@ static void taihu_cpld_writeb (void *opaque, } } -static uint32_t taihu_cpld_readw (void *opaque, hwaddr addr) -{ - uint32_t ret; - - ret = taihu_cpld_readb(opaque, addr) << 8; - ret |= taihu_cpld_readb(opaque, addr + 1); - - return ret; -} - -static void taihu_cpld_writew (void *opaque, - hwaddr addr, uint32_t value) -{ - taihu_cpld_writeb(opaque, addr, (value >> 8) & 0xFF); - taihu_cpld_writeb(opaque, addr + 1, value & 0xFF); -} - -static uint32_t taihu_cpld_readl (void *opaque, hwaddr addr) -{ - uint32_t ret; - - ret = taihu_cpld_readb(opaque, addr) << 24; - ret |= taihu_cpld_readb(opaque, addr + 1) << 16; - ret |= taihu_cpld_readb(opaque, addr + 2) << 8; - ret |= taihu_cpld_readb(opaque, addr + 3); - - return ret; -} - -static void taihu_cpld_writel (void *opaque, - hwaddr addr, uint32_t value) -{ - taihu_cpld_writel(opaque, addr, (value >> 24) & 0xFF); - taihu_cpld_writel(opaque, addr + 1, (value >> 16) & 0xFF); - taihu_cpld_writel(opaque, addr + 2, (value >> 8) & 0xFF); - taihu_cpld_writeb(opaque, addr + 3, value & 0xFF); -} - static const MemoryRegionOps taihu_cpld_ops = { - .old_mmio = { - .read = { taihu_cpld_readb, taihu_cpld_readw, taihu_cpld_readl, }, - .write = { taihu_cpld_writeb, taihu_cpld_writew, taihu_cpld_writel, }, + .read = taihu_cpld_read, + .write = taihu_cpld_write, + .impl = { + .min_access_size = 1, + .max_access_size = 1, }, .endianness = DEVICE_NATIVE_ENDIAN, }; @@ -579,7 +556,7 @@ static void taihu_405ep_init(MachineState *machine) bios_name = BIOS_FILENAME; bios = g_new(MemoryRegion, 1); memory_region_init_ram(bios, NULL, "taihu_405ep.bios", BIOS_SIZE, - &error_abort); + &error_fatal); vmstate_register_ram_global(bios); filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { @@ -664,16 +641,24 @@ static void taihu_405ep_init(MachineState *machine) #endif } -static QEMUMachine taihu_machine = { - .name = "taihu", - .desc = "taihu", - .init = taihu_405ep_init, +static void taihu_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "taihu"; + mc->init = taihu_405ep_init; +} + +static const TypeInfo taihu_type = { + .name = MACHINE_TYPE_NAME("taihu"), + .parent = TYPE_MACHINE, + .class_init = taihu_class_init, }; static void ppc405_machine_init(void) { - qemu_register_machine(&ref405ep_machine); - qemu_register_machine(&taihu_machine); + type_register_static(&ref405ep_type); + type_register_static(&taihu_type); } -machine_init(ppc405_machine_init); +type_init(ppc405_machine_init) diff --git a/qemu/hw/ppc/ppc405_uc.c b/qemu/hw/ppc/ppc405_uc.c index c77434ae0..d6d3fc2c4 100644 --- a/qemu/hw/ppc/ppc405_uc.c +++ b/qemu/hw/ppc/ppc405_uc.c @@ -21,6 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "hw/boards.h" @@ -975,7 +979,7 @@ static void ppc405_ocm_init(CPUPPCState *env) ocm = g_malloc0(sizeof(ppc405_ocm_t)); /* XXX: Size is 4096 or 0x04000000 */ memory_region_init_ram(&ocm->isarc_ram, NULL, "ppc405.ocm", 4096, - &error_abort); + &error_fatal); vmstate_register_ram_global(&ocm->isarc_ram); memory_region_init_alias(&ocm->dsarc_ram, NULL, "ppc405.dsarc", &ocm->isarc_ram, 0, 4096); @@ -1352,7 +1356,7 @@ static uint32_t ppc4xx_gpt_readl (void *opaque, hwaddr addr) case 0x00: /* Time base counter */ ret = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + gpt->tb_offset, - gpt->tb_freq, get_ticks_per_sec()); + gpt->tb_freq, NANOSECONDS_PER_SECOND); break; case 0x10: /* Output enable */ @@ -1407,7 +1411,7 @@ static void ppc4xx_gpt_writel (void *opaque, switch (addr) { case 0x00: /* Time base counter */ - gpt->tb_offset = muldiv64(value, get_ticks_per_sec(), gpt->tb_freq) + gpt->tb_offset = muldiv64(value, NANOSECONDS_PER_SECOND, gpt->tb_freq) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ppc4xx_gpt_compute_timer(gpt); break; diff --git a/qemu/hw/ppc/ppc440_bamboo.c b/qemu/hw/ppc/ppc440_bamboo.c index 032fa803d..5c535b18a 100644 --- a/qemu/hw/ppc/ppc440_bamboo.c +++ b/qemu/hw/ppc/ppc440_bamboo.c @@ -11,7 +11,7 @@ * */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "net/net.h" #include "hw/hw.h" @@ -256,7 +256,8 @@ static void bamboo_init(MachineState *machine) NULL, NULL); if (success < 0) { success = load_elf(kernel_filename, NULL, NULL, &elf_entry, - &elf_lowaddr, NULL, 1, ELF_MACHINE, 0); + &elf_lowaddr, NULL, 1, PPC_ELF_MACHINE, + 0, 0); entry = elf_entry; loadaddr = elf_lowaddr; } @@ -288,20 +289,12 @@ static void bamboo_init(MachineState *machine) exit(1); } } - - if (kvm_enabled()) - kvmppc_init(); } -static QEMUMachine bamboo_machine = { - .name = "bamboo", - .desc = "bamboo", - .init = bamboo_init, -}; - -static void bamboo_machine_init(void) +static void bamboo_machine_init(MachineClass *mc) { - qemu_register_machine(&bamboo_machine); + mc->desc = "bamboo"; + mc->init = bamboo_init; } -machine_init(bamboo_machine_init); +DEFINE_MACHINE("bamboo", bamboo_machine_init) diff --git a/qemu/hw/ppc/ppc4xx_devs.c b/qemu/hw/ppc/ppc4xx_devs.c index 2f38ff7d2..7d59018fc 100644 --- a/qemu/hw/ppc/ppc4xx_devs.c +++ b/qemu/hw/ppc/ppc4xx_devs.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "hw/ppc/ppc4xx.h" diff --git a/qemu/hw/ppc/ppc4xx_pci.c b/qemu/hw/ppc/ppc4xx_pci.c index 0bb3cdb46..683218e5c 100644 --- a/qemu/hw/ppc/ppc4xx_pci.c +++ b/qemu/hw/ppc/ppc4xx_pci.c @@ -19,6 +19,7 @@ /* This file implements emulation of the 32-bit PCI controller found in some * 4xx SoCs, such as the 440EP. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "hw/ppc/ppc4xx.h" diff --git a/qemu/hw/ppc/ppc_booke.c b/qemu/hw/ppc/ppc_booke.c index 8b94da6b0..ab8d026c3 100644 --- a/qemu/hw/ppc/ppc_booke.c +++ b/qemu/hw/ppc/ppc_booke.c @@ -21,6 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "qemu/timer.h" @@ -162,7 +165,7 @@ static void booke_update_fixed_timer(CPUPPCState *env, ticks += delta_tick; } - *next = now + muldiv64(ticks, get_ticks_per_sec(), tb_env->tb_freq); + *next = now + muldiv64(ticks, NANOSECONDS_PER_SECOND, tb_env->tb_freq); if ((*next < now) || (*next > INT64_MAX)) { /* Overflow, so assume the biggest number the qemu timer supports. */ *next = INT64_MAX; diff --git a/qemu/hw/ppc/ppce500_spin.c b/qemu/hw/ppc/ppce500_spin.c index a99f7b039..76bd78bfd 100644 --- a/qemu/hw/ppc/ppce500_spin.c +++ b/qemu/hw/ppc/ppce500_spin.c @@ -27,6 +27,7 @@ * */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "sysemu/sysemu.h" #include "hw/sysbus.h" diff --git a/qemu/hw/ppc/prep.c b/qemu/hw/ppc/prep.c index 45b5f62d6..3ffb85e60 100644 --- a/qemu/hw/ppc/prep.c +++ b/qemu/hw/ppc/prep.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/timer/m48t59.h" #include "hw/i386/pc.h" @@ -33,6 +34,7 @@ #include "hw/pci/pci_host.h" #include "hw/ppc/ppc.h" #include "hw/boards.h" +#include "qemu/error-report.h" #include "qemu/log.h" #include "hw/ide.h" #include "hw/loader.h" @@ -42,10 +44,9 @@ #include "sysemu/arch_init.h" #include "sysemu/qtest.h" #include "exec/address-spaces.h" +#include "trace.h" #include "elf.h" - -//#define HARD_DEBUG_PPC_IO -//#define DEBUG_PPC_IO +#include "qemu/cutils.h" /* SMP is not enabled, for now */ #define MAX_CPUS 1 @@ -57,26 +58,6 @@ #define KERNEL_LOAD_ADDR 0x01000000 #define INITRD_LOAD_ADDR 0x01800000 -#if defined (HARD_DEBUG_PPC_IO) && !defined (DEBUG_PPC_IO) -#define DEBUG_PPC_IO -#endif - -#if defined (HARD_DEBUG_PPC_IO) -#define PPC_IO_DPRINTF(fmt, ...) \ -do { \ - if (qemu_loglevel_mask(CPU_LOG_IOPORT)) { \ - qemu_log("%s: " fmt, __func__ , ## __VA_ARGS__); \ - } else { \ - printf("%s : " fmt, __func__ , ## __VA_ARGS__); \ - } \ -} while (0) -#elif defined (DEBUG_PPC_IO) -#define PPC_IO_DPRINTF(fmt, ...) \ -qemu_log_mask(CPU_LOG_IOPORT, fmt, ## __VA_ARGS__) -#else -#define PPC_IO_DPRINTF(fmt, ...) do { } while (0) -#endif - /* Constants for devices init */ static const int ide_iobase[2] = { 0x1f0, 0x170 }; static const int ide_iobase2[2] = { 0x3f6, 0x376 }; @@ -199,8 +180,7 @@ static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val) { sysctrl_t *sysctrl = opaque; - PPC_IO_DPRINTF("0x%08" PRIx32 " => 0x%02" PRIx32 "\n", - addr - PPC_IO_BASE, val); + trace_prep_io_800_writeb(addr - PPC_IO_BASE, val); switch (addr) { case 0x0092: /* Special port 92 */ @@ -327,8 +307,7 @@ static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr) printf("ERROR: unaffected IO port: %04" PRIx32 " read\n", addr); break; } - PPC_IO_DPRINTF("0x%08" PRIx32 " <= 0x%02" PRIx32 "\n", - addr - PPC_IO_BASE, retval); + trace_prep_io_800_readb(addr - PPC_IO_BASE, retval); return retval; } @@ -336,15 +315,6 @@ static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr) #define NVRAM_SIZE 0x2000 -static void cpu_request_exit(void *opaque, int irq, int level) -{ - CPUState *cpu = current_cpu; - - if (cpu && level) { - cpu_exit(cpu); - } -} - static void ppc_prep_reset(void *opaque) { PowerPCCPU *cpu = opaque; @@ -565,7 +535,7 @@ static void ppc_prep_init(MachineState *machine) kernel_size = load_image_targphys(kernel_filename, kernel_base, ram_size - kernel_base); if (kernel_size < 0) { - hw_error("qemu: could not load kernel '%s'\n", kernel_filename); + error_report("could not load kernel '%s'", kernel_filename); exit(1); } /* load initrd */ @@ -574,8 +544,9 @@ static void ppc_prep_init(MachineState *machine) initrd_size = load_image_targphys(initrd_filename, initrd_base, ram_size - initrd_base); if (initrd_size < 0) { - hw_error("qemu: could not load initial ram disk '%s'\n", - initrd_filename); + error_report("could not load initial ram disk '%s'", + initrd_filename); + exit(1); } } else { initrd_base = 0; @@ -602,7 +573,8 @@ static void ppc_prep_init(MachineState *machine) } if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) { - hw_error("Only 6xx bus is supported on PREP machine\n"); + error_report("Only 6xx bus is supported on PREP machine"); + exit(1); } dev = qdev_create(NULL, "raven-pcihost"); @@ -610,7 +582,7 @@ static void ppc_prep_init(MachineState *machine) bios_name = BIOS_FILENAME; } qdev_prop_set_string(dev, "bios-name", bios_name); - qdev_prop_set_uint32(dev, "elf-machine", ELF_MACHINE); + qdev_prop_set_uint32(dev, "elf-machine", PPC_ELF_MACHINE); pcihost = PCI_HOST_BRIDGE(dev); object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev), NULL); qdev_init_nofail(dev); @@ -626,8 +598,6 @@ static void ppc_prep_init(MachineState *machine) cpu = POWERPC_CPU(first_cpu); qdev_connect_gpio_out(&pci->qdev, 0, cpu->env.irq_inputs[PPC6xx_INPUT_INT]); - qdev_connect_gpio_out(&pci->qdev, 1, - qemu_allocate_irq(cpu_request_exit, NULL, 0)); sysbus_connect_irq(&pcihost->busdev, 0, qdev_get_gpio_in(&pci->qdev, 9)); sysbus_connect_irq(&pcihost->busdev, 1, qdev_get_gpio_in(&pci->qdev, 11)); sysbus_connect_irq(&pcihost->busdev, 2, qdev_get_gpio_in(&pci->qdev, 9)); @@ -698,17 +668,12 @@ static void ppc_prep_init(MachineState *machine) graphic_width, graphic_height, graphic_depth); } -static QEMUMachine prep_machine = { - .name = "prep", - .desc = "PowerPC PREP platform", - .init = ppc_prep_init, - .max_cpus = MAX_CPUS, - .default_boot_order = "cad", -}; - -static void prep_machine_init(void) +static void prep_machine_init(MachineClass *mc) { - qemu_register_machine(&prep_machine); + mc->desc = "PowerPC PREP platform"; + mc->init = ppc_prep_init; + mc->max_cpus = MAX_CPUS; + mc->default_boot_order = "cad"; } -machine_init(prep_machine_init); +DEFINE_MACHINE("prep", prep_machine_init) diff --git a/qemu/hw/ppc/spapr.c b/qemu/hw/ppc/spapr.c index a6f19473c..b69995e0d 100644 --- a/qemu/hw/ppc/spapr.c +++ b/qemu/hw/ppc/spapr.c @@ -24,15 +24,19 @@ * THE SOFTWARE. * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "sysemu/sysemu.h" #include "sysemu/numa.h" #include "hw/hw.h" #include "hw/fw-path-provider.h" #include "elf.h" #include "net/net.h" +#include "sysemu/device_tree.h" #include "sysemu/block-backend.h" #include "sysemu/cpus.h" #include "sysemu/kvm.h" +#include "sysemu/device_tree.h" #include "kvm_ppc.h" #include "migration/migration.h" #include "mmu-hash64.h" @@ -60,6 +64,7 @@ #include "hw/nmi.h" #include "hw/compat.h" +#include "qemu/cutils.h" #include @@ -73,7 +78,7 @@ * * We load our kernel at 4M, leaving space for SLOF initial image */ -#define FDT_MAX_SIZE 0x40000 +#define FDT_MAX_SIZE 0x100000 #define RTAS_MAX_SIZE 0x10000 #define RTAS_MAX_ADDR 0x80000000 /* RTAS must stay below that */ #define FW_MAX_SIZE 0x400000 @@ -85,8 +90,6 @@ #define TIMEBASE_FREQ 512000000ULL -#define MAX_CPUS 255 - #define PHANDLE_XICP 0x00001111 #define HTAB_SIZE(spapr) (1ULL << ((spapr)->htab_shift)) @@ -110,7 +113,7 @@ static XICSState *try_create_xics(const char *type, int nr_servers, } static XICSState *xics_system_init(MachineState *machine, - int nr_servers, int nr_irqs) + int nr_servers, int nr_irqs, Error **errp) { XICSState *icp = NULL; @@ -121,13 +124,15 @@ static XICSState *xics_system_init(MachineState *machine, icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs, &err); } if (machine_kernel_irqchip_required(machine) && !icp) { - error_report("kernel_irqchip requested but unavailable: %s", - error_get_pretty(err)); + error_reportf_err(err, + "kernel_irqchip requested but unavailable: "); + } else { + error_free(err); } } if (!icp) { - icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs, &error_abort); + icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs, errp); } return icp; @@ -373,8 +378,16 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base, qemu_uuid[14], qemu_uuid[15]); _FDT((fdt_property_string(fdt, "vm,uuid", buf))); + if (qemu_uuid_set) { + _FDT((fdt_property_string(fdt, "system-id", buf))); + } g_free(buf); + if (qemu_get_vm_name()) { + _FDT((fdt_property_string(fdt, "ibm,partition-name", + qemu_get_vm_name()))); + } + _FDT((fdt_property_cell(fdt, "#address-cells", 0x2))); _FDT((fdt_property_cell(fdt, "#size-cells", 0x2))); @@ -427,6 +440,10 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base, _FDT((fdt_property_cell(fdt, "rtas-event-scan-rate", RTAS_EVENT_SCAN_RATE))); + if (msi_nonbroken) { + _FDT((fdt_property(fdt, "ibm,change-msix-capable", NULL, 0))); + } + /* * According to PAPR, rtas ibm,os-term does not guarantee a return * back to the guest cpu. @@ -481,10 +498,11 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base, * Older KVM versions with older guest kernels were broken with the * magic page, don't allow the guest to map it. */ - kvmppc_get_hypercall(first_cpu->env_ptr, hypercall, - sizeof(hypercall)); - _FDT((fdt_property(fdt, "hcall-instructions", hypercall, - sizeof(hypercall)))); + if (!kvmppc_get_hypercall(first_cpu->env_ptr, hypercall, + sizeof(hypercall))) { + _FDT((fdt_property(fdt, "hcall-instructions", hypercall, + sizeof(hypercall)))); + } } _FDT((fdt_end_node(fdt))); } @@ -495,44 +513,7 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base, return fdt; } -int spapr_h_cas_compose_response(sPAPRMachineState *spapr, - target_ulong addr, target_ulong size) -{ - void *fdt, *fdt_skel; - sPAPRDeviceTreeUpdateHeader hdr = { .version_id = 1 }; - - size -= sizeof(hdr); - - /* Create sceleton */ - fdt_skel = g_malloc0(size); - _FDT((fdt_create(fdt_skel, size))); - _FDT((fdt_begin_node(fdt_skel, ""))); - _FDT((fdt_end_node(fdt_skel))); - _FDT((fdt_finish(fdt_skel))); - fdt = g_malloc0(size); - _FDT((fdt_open_into(fdt_skel, fdt, size))); - g_free(fdt_skel); - - /* Fix skeleton up */ - _FDT((spapr_fixup_cpu_dt(fdt, spapr))); - - /* Pack resulting tree */ - _FDT((fdt_pack(fdt))); - - if (fdt_totalsize(fdt) + sizeof(hdr) > size) { - trace_spapr_cas_failed(size); - return -1; - } - - cpu_physical_memory_write(addr, &hdr, sizeof(hdr)); - cpu_physical_memory_write(addr + sizeof(hdr), fdt, fdt_totalsize(fdt)); - trace_spapr_cas_continue(fdt_totalsize(fdt) + sizeof(hdr)); - g_free(fdt); - - return 0; -} - -static void spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start, +static int spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start, hwaddr size) { uint32_t associativity[] = { @@ -555,6 +536,7 @@ static void spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start, sizeof(mem_reg_property)))); _FDT((fdt_setprop(fdt, off, "ibm,associativity", associativity, sizeof(associativity)))); + return off; } static int spapr_populate_memory(sPAPRMachineState *spapr, void *fdt) @@ -620,11 +602,27 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000; uint32_t page_sizes_prop[64]; size_t page_sizes_prop_size; - QemuOpts *opts = qemu_opts_find(qemu_find_opts("smp-opts"), NULL); - unsigned sockets = opts ? qemu_opt_get_number(opts, "sockets", 0) : 0; - uint32_t cpus_per_socket = sockets ? (smp_cpus / sockets) : 1; + uint32_t vcpus_per_socket = smp_threads * smp_cores; uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)}; + /* Note: we keep CI large pages off for now because a 64K capable guest + * provisioned with large pages might otherwise try to map a qemu + * framebuffer (or other kind of memory mapped PCI BAR) using 64K pages + * even if that qemu runs on a 4k host. + * + * We can later add this bit back when we are confident this is not + * an issue (!HV KVM or 64K host) + */ + uint8_t pa_features_206[] = { 6, 0, + 0xf6, 0x1f, 0xc7, 0x00, 0x80, 0xc0 }; + uint8_t pa_features_207[] = { 24, 0, + 0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 }; + uint8_t *pa_features; + size_t pa_size; + _FDT((fdt_setprop_cell(fdt, offset, "reg", index))); _FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu"))); @@ -653,6 +651,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq))); _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq))); + _FDT((fdt_setprop_cell(fdt, offset, "slb-size", env->slb_nr))); _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", env->slb_nr))); _FDT((fdt_setprop_string(fdt, offset, "status", "okay"))); _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0))); @@ -690,8 +689,21 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, page_sizes_prop, page_sizes_prop_size))); } + /* Do the ibm,pa-features property, adjust it for ci-large-pages */ + if (env->mmu_model == POWERPC_MMU_2_06) { + pa_features = pa_features_206; + pa_size = sizeof(pa_features_206); + } else /* env->mmu_model == POWERPC_MMU_2_07 */ { + pa_features = pa_features_207; + pa_size = sizeof(pa_features_207); + } + if (env->ci_large_pages) { + pa_features[3] |= 0x20; + } + _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features, pa_size))); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", - cs->cpu_index / cpus_per_socket))); + cs->cpu_index / vcpus_per_socket))); _FDT((fdt_setprop(fdt, offset, "ibm,pft-size", pft_size_prop, sizeof(pft_size_prop)))); @@ -738,12 +750,162 @@ static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr) } +/* + * Adds ibm,dynamic-reconfiguration-memory node. + * Refer to docs/specs/ppc-spapr-hotplug.txt for the documentation + * of this device tree node. + */ +static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt) +{ + MachineState *machine = MACHINE(spapr); + int ret, i, offset; + uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; + uint32_t prop_lmb_size[] = {0, cpu_to_be32(lmb_size)}; + uint32_t nr_lmbs = (machine->maxram_size - machine->ram_size)/lmb_size; + uint32_t *int_buf, *cur_index, buf_len; + int nr_nodes = nb_numa_nodes ? nb_numa_nodes : 1; + + /* + * Don't create the node if there are no DR LMBs. + */ + if (!nr_lmbs) { + return 0; + } + + /* + * Allocate enough buffer size to fit in ibm,dynamic-memory + * or ibm,associativity-lookup-arrays + */ + buf_len = MAX(nr_lmbs * SPAPR_DR_LMB_LIST_ENTRY_SIZE + 1, nr_nodes * 4 + 2) + * sizeof(uint32_t); + cur_index = int_buf = g_malloc0(buf_len); + + offset = fdt_add_subnode(fdt, 0, "ibm,dynamic-reconfiguration-memory"); + + ret = fdt_setprop(fdt, offset, "ibm,lmb-size", prop_lmb_size, + sizeof(prop_lmb_size)); + if (ret < 0) { + goto out; + } + + ret = fdt_setprop_cell(fdt, offset, "ibm,memory-flags-mask", 0xff); + if (ret < 0) { + goto out; + } + + ret = fdt_setprop_cell(fdt, offset, "ibm,memory-preservation-time", 0x0); + if (ret < 0) { + goto out; + } + + /* ibm,dynamic-memory */ + int_buf[0] = cpu_to_be32(nr_lmbs); + cur_index++; + for (i = 0; i < nr_lmbs; i++) { + sPAPRDRConnector *drc; + sPAPRDRConnectorClass *drck; + uint64_t addr = i * lmb_size + spapr->hotplug_memory.base;; + uint32_t *dynamic_memory = cur_index; + + drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, + addr/lmb_size); + g_assert(drc); + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + + dynamic_memory[0] = cpu_to_be32(addr >> 32); + dynamic_memory[1] = cpu_to_be32(addr & 0xffffffff); + dynamic_memory[2] = cpu_to_be32(drck->get_index(drc)); + dynamic_memory[3] = cpu_to_be32(0); /* reserved */ + dynamic_memory[4] = cpu_to_be32(numa_get_node(addr, NULL)); + if (addr < machine->ram_size || + memory_region_present(get_system_memory(), addr)) { + dynamic_memory[5] = cpu_to_be32(SPAPR_LMB_FLAGS_ASSIGNED); + } else { + dynamic_memory[5] = cpu_to_be32(0); + } + + cur_index += SPAPR_DR_LMB_LIST_ENTRY_SIZE; + } + ret = fdt_setprop(fdt, offset, "ibm,dynamic-memory", int_buf, buf_len); + if (ret < 0) { + goto out; + } + + /* ibm,associativity-lookup-arrays */ + cur_index = int_buf; + int_buf[0] = cpu_to_be32(nr_nodes); + int_buf[1] = cpu_to_be32(4); /* Number of entries per associativity list */ + cur_index += 2; + for (i = 0; i < nr_nodes; i++) { + uint32_t associativity[] = { + cpu_to_be32(0x0), + cpu_to_be32(0x0), + cpu_to_be32(0x0), + cpu_to_be32(i) + }; + memcpy(cur_index, associativity, sizeof(associativity)); + cur_index += 4; + } + ret = fdt_setprop(fdt, offset, "ibm,associativity-lookup-arrays", int_buf, + (cur_index - int_buf) * sizeof(uint32_t)); +out: + g_free(int_buf); + return ret; +} + +int spapr_h_cas_compose_response(sPAPRMachineState *spapr, + target_ulong addr, target_ulong size, + bool cpu_update, bool memory_update) +{ + void *fdt, *fdt_skel; + sPAPRDeviceTreeUpdateHeader hdr = { .version_id = 1 }; + sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(qdev_get_machine()); + + size -= sizeof(hdr); + + /* Create sceleton */ + fdt_skel = g_malloc0(size); + _FDT((fdt_create(fdt_skel, size))); + _FDT((fdt_begin_node(fdt_skel, ""))); + _FDT((fdt_end_node(fdt_skel))); + _FDT((fdt_finish(fdt_skel))); + fdt = g_malloc0(size); + _FDT((fdt_open_into(fdt_skel, fdt, size))); + g_free(fdt_skel); + + /* Fixup cpu nodes */ + if (cpu_update) { + _FDT((spapr_fixup_cpu_dt(fdt, spapr))); + } + + /* Generate ibm,dynamic-reconfiguration-memory node if required */ + if (memory_update && smc->dr_lmb_enabled) { + _FDT((spapr_populate_drconf_memory(spapr, fdt))); + } + + /* Pack resulting tree */ + _FDT((fdt_pack(fdt))); + + if (fdt_totalsize(fdt) + sizeof(hdr) > size) { + trace_spapr_cas_failed(size); + return -1; + } + + cpu_physical_memory_write(addr, &hdr, sizeof(hdr)); + cpu_physical_memory_write(addr + sizeof(hdr), fdt, fdt_totalsize(fdt)); + trace_spapr_cas_continue(fdt_totalsize(fdt) + sizeof(hdr)); + g_free(fdt); + + return 0; +} + static void spapr_finalize_fdt(sPAPRMachineState *spapr, hwaddr fdt_addr, hwaddr rtas_addr, hwaddr rtas_size) { MachineState *machine = MACHINE(qdev_get_machine()); + sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); const char *boot_device = machine->boot_order; int ret, i; size_t cb = 0; @@ -768,13 +930,20 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr, exit(1); } - QLIST_FOREACH(phb, &spapr->phbs, list) { - ret = spapr_populate_pci_dt(phb, PHANDLE_XICP, fdt); + if (object_resolve_path_type("", TYPE_SPAPR_RNG, NULL)) { + ret = spapr_rng_populate_dt(fdt); + if (ret < 0) { + fprintf(stderr, "could not set up rng device in the fdt\n"); + exit(1); + } } - if (ret < 0) { - fprintf(stderr, "couldn't setup PCI devices in fdt\n"); - exit(1); + QLIST_FOREACH(phb, &spapr->phbs, list) { + ret = spapr_populate_pci_dt(phb, PHANDLE_XICP, fdt); + if (ret < 0) { + error_report("couldn't setup PCI devices in fdt"); + exit(1); + } } /* RTAS */ @@ -814,6 +983,10 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr, spapr_populate_chosen_stdout(fdt, spapr->vio_bus); } + if (smc->dr_lmb_enabled) { + _FDT(spapr_drc_populate_dt(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_LMB)); + } + _FDT((fdt_pack(fdt))); if (fdt_totalsize(fdt) > FDT_MAX_SIZE) { @@ -822,6 +995,7 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr, exit(1); } + qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt)); g_free(bootlist); @@ -851,45 +1025,93 @@ static void emulate_spapr_hypercall(PowerPCCPU *cpu) #define CLEAN_HPTE(_hpte) ((*(uint64_t *)(_hpte)) &= tswap64(~HPTE64_V_HPTE_DIRTY)) #define DIRTY_HPTE(_hpte) ((*(uint64_t *)(_hpte)) |= tswap64(HPTE64_V_HPTE_DIRTY)) -static void spapr_reset_htab(sPAPRMachineState *spapr) +/* + * Get the fd to access the kernel htab, re-opening it if necessary + */ +static int get_htab_fd(sPAPRMachineState *spapr) { - long shift; - int index; + if (spapr->htab_fd >= 0) { + return spapr->htab_fd; + } + + spapr->htab_fd = kvmppc_get_htab_fd(false); + if (spapr->htab_fd < 0) { + error_report("Unable to open fd for reading hash table from KVM: %s", + strerror(errno)); + } - /* allocate hash page table. For now we always make this 16mb, - * later we should probably make it scale to the size of guest - * RAM */ + return spapr->htab_fd; +} - shift = kvmppc_reset_htab(spapr->htab_shift); +static void close_htab_fd(sPAPRMachineState *spapr) +{ + if (spapr->htab_fd >= 0) { + close(spapr->htab_fd); + } + spapr->htab_fd = -1; +} - if (shift > 0) { - /* Kernel handles htab, we don't need to allocate one */ - spapr->htab_shift = shift; - kvmppc_kern_htab = true; +static int spapr_hpt_shift_for_ramsize(uint64_t ramsize) +{ + int shift; + + /* We aim for a hash table of size 1/128 the size of RAM (rounded + * up). The PAPR recommendation is actually 1/64 of RAM size, but + * that's much more than is needed for Linux guests */ + shift = ctz64(pow2ceil(ramsize)) - 7; + shift = MAX(shift, 18); /* Minimum architected size */ + shift = MIN(shift, 46); /* Maximum architected size */ + return shift; +} - /* Tell readers to update their file descriptor */ - if (spapr->htab_fd >= 0) { - spapr->htab_fd_stale = true; +static void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift, + Error **errp) +{ + long rc; + + /* Clean up any HPT info from a previous boot */ + g_free(spapr->htab); + spapr->htab = NULL; + spapr->htab_shift = 0; + close_htab_fd(spapr); + + rc = kvmppc_reset_htab(shift); + if (rc < 0) { + /* kernel-side HPT needed, but couldn't allocate one */ + error_setg_errno(errp, errno, + "Failed to allocate KVM HPT of order %d (try smaller maxmem?)", + shift); + /* This is almost certainly fatal, but if the caller really + * wants to carry on with shift == 0, it's welcome to try */ + } else if (rc > 0) { + /* kernel-side HPT allocated */ + if (rc != shift) { + error_setg(errp, + "Requested order %d HPT, but kernel allocated order %ld (try smaller maxmem?)", + shift, rc); } + + spapr->htab_shift = shift; + spapr->htab = NULL; } else { + /* kernel-side HPT not needed, allocate in userspace instead */ + size_t size = 1ULL << shift; + int i; + + spapr->htab = qemu_memalign(size, size); if (!spapr->htab) { - /* Allocate an htab if we don't yet have one */ - spapr->htab = qemu_memalign(HTAB_SIZE(spapr), HTAB_SIZE(spapr)); + error_setg_errno(errp, errno, + "Could not allocate HPT of order %d", shift); + return; } - /* And clear it */ - memset(spapr->htab, 0, HTAB_SIZE(spapr)); + memset(spapr->htab, 0, size); + spapr->htab_shift = shift; - for (index = 0; index < HTAB_SIZE(spapr) / HASH_PTE_SIZE_64; index++) { - DIRTY_HPTE(HPTE(spapr->htab, index)); + for (i = 0; i < size / HASH_PTE_SIZE_64; i++) { + DIRTY_HPTE(HPTE(spapr->htab, i)); } } - - /* Update the RMA size if necessary */ - if (spapr->vrma_adjust) { - spapr->rma_size = kvmppc_rma_size(spapr_node0_size(), - spapr->htab_shift); - } } static int find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque) @@ -909,39 +1131,26 @@ static int find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque) return 0; } -/* - * A guest reset will cause spapr->htab_fd to become stale if being used. - * Reopen the file descriptor to make sure the whole HTAB is properly read. - */ -static int spapr_check_htab_fd(sPAPRMachineState *spapr) -{ - int rc = 0; - - if (spapr->htab_fd_stale) { - close(spapr->htab_fd); - spapr->htab_fd = kvmppc_get_htab_fd(false); - if (spapr->htab_fd < 0) { - error_report("Unable to open fd for reading hash table from KVM: " - "%s", strerror(errno)); - rc = -1; - } - spapr->htab_fd_stale = false; - } - - return rc; -} - static void ppc_spapr_reset(void) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + MachineState *machine = MACHINE(qdev_get_machine()); + sPAPRMachineState *spapr = SPAPR_MACHINE(machine); PowerPCCPU *first_ppc_cpu; uint32_t rtas_limit; /* Check for unknown sysbus devices */ foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL); - /* Reset the hash table & recalc the RMA */ - spapr_reset_htab(spapr); + /* Allocate and/or reset the hash page table */ + spapr_reallocate_hpt(spapr, + spapr_hpt_shift_for_ramsize(machine->maxram_size), + &error_fatal); + + /* Update the RMA size if necessary */ + if (spapr->vrma_adjust) { + spapr->rma_size = kvmppc_rma_size(spapr_node0_size(), + spapr->htab_shift); + } qemu_devices_reset(); @@ -987,24 +1196,8 @@ static void spapr_cpu_reset(void *opaque) env->spr[SPR_HIOR] = 0; - env->external_htab = (uint8_t *)spapr->htab; - if (kvm_enabled() && !env->external_htab) { - /* - * HV KVM, set external_htab to 1 so our ppc_hash64_load_hpte* - * functions do the right thing. - */ - env->external_htab = (void *)1; - } - env->htab_base = -1; - /* - * htab_mask is the mask used to normalize hash value to PTEG index. - * htab_shift is log2 of hash table size. - * We have 8 hpte per group, and each hpte is 16 bytes. - * ie have 128 bytes per hpte entry. - */ - env->htab_mask = (1ULL << (spapr->htab_shift - 7)) - 1; - env->spr[SPR_SDR1] = (target_ulong)(uintptr_t)spapr->htab | - (spapr->htab_shift - 18); + ppc_hash64_set_external_hpt(cpu, spapr->htab, spapr->htab_shift, + &error_fatal); } static void spapr_create_nvram(sPAPRMachineState *spapr) @@ -1013,7 +1206,8 @@ static void spapr_create_nvram(sPAPRMachineState *spapr) DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0); if (dinfo) { - qdev_prop_set_drive_nofail(dev, "drive", blk_by_legacy_dinfo(dinfo)); + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo), + &error_fatal); } qdev_init_nofail(dev); @@ -1033,7 +1227,7 @@ static void spapr_rtc_create(sPAPRMachineState *spapr) } /* Returns whether we want to use VGA or not */ -static int spapr_vga_init(PCIBus *pci_bus) +static bool spapr_vga_init(PCIBus *pci_bus, Error **errp) { switch (vga_interface_type) { case VGA_NONE: @@ -1041,11 +1235,12 @@ static int spapr_vga_init(PCIBus *pci_bus) case VGA_DEVICE: return true; case VGA_STD: + case VGA_VIRTIO: return pci_vga_init(pci_bus) != NULL; default: - fprintf(stderr, "This vga model is not supported," - "currently it only supports -vga std\n"); - exit(0); + error_setg(errp, + "Unsupported VGA mode, only -vga std or -vga virtio is supported"); + return false; } } @@ -1099,14 +1294,6 @@ static int htab_save_setup(QEMUFile *f, void *opaque) spapr->htab_first_pass = true; } else { assert(kvm_enabled()); - - spapr->htab_fd = kvmppc_get_htab_fd(false); - spapr->htab_fd_stale = false; - if (spapr->htab_fd < 0) { - fprintf(stderr, "Unable to open fd for reading hash table from KVM: %s\n", - strerror(errno)); - return -1; - } } @@ -1116,6 +1303,7 @@ static int htab_save_setup(QEMUFile *f, void *opaque) static void htab_save_first_pass(QEMUFile *f, sPAPRMachineState *spapr, int64_t max_ns) { + bool has_timeout = max_ns != -1; int htabslots = HTAB_SIZE(spapr) / HASH_PTE_SIZE_64; int index = spapr->htab_save_index; int64_t starttime = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); @@ -1149,7 +1337,8 @@ static void htab_save_first_pass(QEMUFile *f, sPAPRMachineState *spapr, qemu_put_buffer(f, HPTE(spapr->htab, chunkstart), HASH_PTE_SIZE_64 * n_valid); - if ((qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - starttime) > max_ns) { + if (has_timeout && + (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - starttime) > max_ns) { break; } } @@ -1246,6 +1435,7 @@ static int htab_save_later_pass(QEMUFile *f, sPAPRMachineState *spapr, static int htab_save_iterate(QEMUFile *f, void *opaque) { sPAPRMachineState *spapr = opaque; + int fd; int rc = 0; /* Iteration header */ @@ -1254,13 +1444,12 @@ static int htab_save_iterate(QEMUFile *f, void *opaque) if (!spapr->htab) { assert(kvm_enabled()); - rc = spapr_check_htab_fd(spapr); - if (rc < 0) { - return rc; + fd = get_htab_fd(spapr); + if (fd < 0) { + return fd; } - rc = kvmppc_save_htab(f, spapr->htab_fd, - MAX_KVM_BUF_SIZE, MAX_ITERATION_NS); + rc = kvmppc_save_htab(f, fd, MAX_KVM_BUF_SIZE, MAX_ITERATION_NS); if (rc < 0) { return rc; } @@ -1281,6 +1470,7 @@ static int htab_save_iterate(QEMUFile *f, void *opaque) static int htab_save_complete(QEMUFile *f, void *opaque) { sPAPRMachineState *spapr = opaque; + int fd; /* Iteration header */ qemu_put_be32(f, 0); @@ -1290,18 +1480,20 @@ static int htab_save_complete(QEMUFile *f, void *opaque) assert(kvm_enabled()); - rc = spapr_check_htab_fd(spapr); - if (rc < 0) { - return rc; + fd = get_htab_fd(spapr); + if (fd < 0) { + return fd; } - rc = kvmppc_save_htab(f, spapr->htab_fd, MAX_KVM_BUF_SIZE, -1); + rc = kvmppc_save_htab(f, fd, MAX_KVM_BUF_SIZE, -1); if (rc < 0) { return rc; } - close(spapr->htab_fd); - spapr->htab_fd = -1; + close_htab_fd(spapr); } else { + if (spapr->htab_first_pass) { + htab_save_first_pass(f, spapr, -1); + } htab_save_later_pass(f, spapr, -1); } @@ -1320,15 +1512,19 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id) int fd = -1; if (version_id < 1 || version_id > 1) { - fprintf(stderr, "htab_load() bad version\n"); + error_report("htab_load() bad version"); return -EINVAL; } section_hdr = qemu_get_be32(f); if (section_hdr) { - /* First section, just the hash shift */ - if (spapr->htab_shift != section_hdr) { + Error *local_err = NULL; + + /* First section gives the htab size */ + spapr_reallocate_hpt(spapr, section_hdr, &local_err); + if (local_err) { + error_report_err(local_err); return -EINVAL; } return 0; @@ -1339,8 +1535,8 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id) fd = kvmppc_get_htab_fd(true); if (fd < 0) { - fprintf(stderr, "Unable to open fd to restore KVM hash table: %s\n", - strerror(errno)); + error_report("Unable to open fd to restore KVM hash table: %s", + strerror(errno)); } } @@ -1360,9 +1556,9 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id) if ((index + n_valid + n_invalid) > (HTAB_SIZE(spapr) / HASH_PTE_SIZE_64)) { /* Bad index in stream */ - fprintf(stderr, "htab_load() bad index %d (%hd+%hd entries) " - "in htab stream (htab_shift=%d)\n", index, n_valid, n_invalid, - spapr->htab_shift); + error_report( + "htab_load() bad index %d (%hd+%hd entries) in htab stream (htab_shift=%d)", + index, n_valid, n_invalid, spapr->htab_shift); return -EINVAL; } @@ -1398,7 +1594,7 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id) static SaveVMHandlers savevm_htab_handlers = { .save_live_setup = htab_save_setup, .save_live_iterate = htab_save_iterate, - .save_live_complete = htab_save_complete, + .save_live_complete_precopy = htab_save_complete, .load_state = htab_load, }; @@ -1409,26 +1605,24 @@ static void spapr_boot_set(void *opaque, const char *boot_device, machine->boot_order = g_strdup(boot_device); } -static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu) +static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, + Error **errp) { CPUPPCState *env = &cpu->env; /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, TIMEBASE_FREQ); - /* PAPR always has exception vectors in RAM not ROM. To ensure this, - * MSR[IP] should never be set. - */ - env->msr_mask &= ~(1 << 6); - - /* Tell KVM that we're in PAPR mode */ - if (kvm_enabled()) { - kvmppc_set_papr(cpu); - } + /* Enable PAPR mode in TCG or KVM */ + cpu_ppc_set_papr(cpu); if (cpu->max_compat) { - if (ppc_set_compat(cpu, cpu->max_compat) < 0) { - exit(1); + Error *local_err = NULL; + + ppc_set_compat(cpu, cpu->max_compat, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; } } @@ -1437,10 +1631,84 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu) qemu_register_reset(spapr_cpu_reset, cpu); } +/* + * Reset routine for LMB DR devices. + * + * Unlike PCI DR devices, LMB DR devices explicitly register this reset + * routine. Reset for PCI DR devices will be handled by PHB reset routine + * when it walks all its children devices. LMB devices reset occurs + * as part of spapr_ppc_reset(). + */ +static void spapr_drc_reset(void *opaque) +{ + sPAPRDRConnector *drc = opaque; + DeviceState *d = DEVICE(drc); + + if (d) { + device_reset(d); + } +} + +static void spapr_create_lmb_dr_connectors(sPAPRMachineState *spapr) +{ + MachineState *machine = MACHINE(spapr); + uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; + uint32_t nr_lmbs = (machine->maxram_size - machine->ram_size)/lmb_size; + int i; + + for (i = 0; i < nr_lmbs; i++) { + sPAPRDRConnector *drc; + uint64_t addr; + + addr = i * lmb_size + spapr->hotplug_memory.base; + drc = spapr_dr_connector_new(OBJECT(spapr), SPAPR_DR_CONNECTOR_TYPE_LMB, + addr/lmb_size); + qemu_register_reset(spapr_drc_reset, drc); + } +} + +/* + * If RAM size, maxmem size and individual node mem sizes aren't aligned + * to SPAPR_MEMORY_BLOCK_SIZE(256MB), then refuse to start the guest + * since we can't support such unaligned sizes with DRCONF_MEMORY. + */ +static void spapr_validate_node_memory(MachineState *machine, Error **errp) +{ + int i; + + if (machine->ram_size % SPAPR_MEMORY_BLOCK_SIZE) { + error_setg(errp, "Memory size 0x" RAM_ADDR_FMT + " is not aligned to %llu MiB", + machine->ram_size, + SPAPR_MEMORY_BLOCK_SIZE / M_BYTE); + return; + } + + if (machine->maxram_size % SPAPR_MEMORY_BLOCK_SIZE) { + error_setg(errp, "Maximum memory size 0x" RAM_ADDR_FMT + " is not aligned to %llu MiB", + machine->ram_size, + SPAPR_MEMORY_BLOCK_SIZE / M_BYTE); + return; + } + + for (i = 0; i < nb_numa_nodes; i++) { + if (numa_info[i].node_mem % SPAPR_MEMORY_BLOCK_SIZE) { + error_setg(errp, + "Node %d memory size 0x%" PRIx64 + " is not aligned to %llu MiB", + i, numa_info[i].node_mem, + SPAPR_MEMORY_BLOCK_SIZE / M_BYTE); + return; + } + } +} + /* pSeries LPAR / sPAPR hardware init */ static void ppc_spapr_init(MachineState *machine) { sPAPRMachineState *spapr = SPAPR_MACHINE(machine); + sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; @@ -1459,7 +1727,7 @@ static void ppc_spapr_init(MachineState *machine) bool kernel_le = false; char *filename; - msi_supported = true; + msi_nonbroken = true; QLIST_INIT(&spapr->phbs); @@ -1494,30 +1762,23 @@ static void ppc_spapr_init(MachineState *machine) } if (spapr->rma_size > node0_size) { - fprintf(stderr, "Error: Numa node 0 has to span the RMA (%#08"HWADDR_PRIx")\n", - spapr->rma_size); + error_report("Numa node 0 has to span the RMA (%#08"HWADDR_PRIx")", + spapr->rma_size); exit(1); } /* Setup a load limit for the ramdisk leaving room for SLOF and FDT */ load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD; - /* We aim for a hash table of size 1/128 the size of RAM. The - * normal rule of thumb is 1/64 the size of RAM, but that's much - * more than needed for the Linux guests we support. */ - spapr->htab_shift = 18; /* Minimum architected size */ - while (spapr->htab_shift <= 46) { - if ((1ULL << (spapr->htab_shift + 7)) >= machine->ram_size) { - break; - } - spapr->htab_shift++; - } - /* Set up Interrupt Controller before we create the VCPUs */ spapr->icp = xics_system_init(machine, DIV_ROUND_UP(max_cpus * kvmppc_smt_threads(), smp_threads), - XICS_IRQS); + XICS_IRQS, &error_fatal); + + if (smc->dr_lmb_enabled) { + spapr_validate_node_memory(machine, &error_fatal); + } /* init CPUs */ if (machine->cpu_model == NULL) { @@ -1526,15 +1787,16 @@ static void ppc_spapr_init(MachineState *machine) for (i = 0; i < smp_cpus; i++) { cpu = cpu_ppc_init(machine->cpu_model); if (cpu == NULL) { - fprintf(stderr, "Unable to find PowerPC CPU definition\n"); + error_report("Unable to find PowerPC CPU definition"); exit(1); } - spapr_cpu_init(spapr, cpu); + spapr_cpu_init(spapr, cpu, &error_fatal); } if (kvm_enabled()) { /* Enable H_LOGICAL_CI_* so SLOF can talk to in-kernel devices */ kvmppc_enable_logical_ci_hcalls(); + kvmppc_enable_set_mode_hcall(); } /* allocate RAM */ @@ -1550,6 +1812,29 @@ static void ppc_spapr_init(MachineState *machine) memory_region_add_subregion(sysmem, 0, rma_region); } + /* initialize hotplug memory address space */ + if (machine->ram_size < machine->maxram_size) { + ram_addr_t hotplug_mem_size = machine->maxram_size - machine->ram_size; + + if (machine->ram_slots > SPAPR_MAX_RAM_SLOTS) { + error_report("Specified number of memory slots %" + PRIu64" exceeds max supported %d", + machine->ram_slots, SPAPR_MAX_RAM_SLOTS); + exit(1); + } + + spapr->hotplug_memory.base = ROUND_UP(machine->ram_size, + SPAPR_HOTPLUG_MEM_ALIGN); + memory_region_init(&spapr->hotplug_memory.mr, OBJECT(spapr), + "hotplug-memory", hotplug_mem_size); + memory_region_add_subregion(sysmem, spapr->hotplug_memory.base, + &spapr->hotplug_memory.mr); + } + + if (smc->dr_lmb_enabled) { + spapr_create_lmb_dr_connectors(spapr); + } + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin"); if (!filename) { error_report("Could not find LPAR rtas '%s'", "spapr-rtas.bin"); @@ -1610,13 +1895,17 @@ static void ppc_spapr_init(MachineState *machine) } /* Graphics */ - if (spapr_vga_init(phb->bus)) { + if (spapr_vga_init(phb->bus, &error_fatal)) { spapr->has_graphics = true; machine->usb |= defaults_enabled() && !machine->usb_disabled; } if (machine->usb) { - pci_create_simple(phb->bus, -1, "pci-ohci"); + if (smc->use_ohci_by_default) { + pci_create_simple(phb->bus, -1, "pci-ohci"); + } else { + pci_create_simple(phb->bus, -1, "nec-usb-xhci"); + } if (spapr->has_graphics) { USBBus *usb_bus = usb_bus_find(-1); @@ -1627,8 +1916,9 @@ static void ppc_spapr_init(MachineState *machine) } if (spapr->rma_size < (MIN_RMA_SLOF << 20)) { - fprintf(stderr, "qemu: pSeries SLOF firmware requires >= " - "%ldM guest RMA (Real Mode Area memory)\n", MIN_RMA_SLOF); + error_report( + "pSeries SLOF firmware requires >= %ldM guest RMA (Real Mode Area memory)", + MIN_RMA_SLOF); exit(1); } @@ -1636,16 +1926,18 @@ static void ppc_spapr_init(MachineState *machine) uint64_t lowaddr = 0; kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0); + NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, + 0, 0); if (kernel_size == ELF_LOAD_WRONG_ENDIAN) { kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - NULL, &lowaddr, NULL, 0, ELF_MACHINE, 0); + NULL, &lowaddr, NULL, 0, PPC_ELF_MACHINE, + 0, 0); kernel_le = kernel_size > 0; } if (kernel_size < 0) { - fprintf(stderr, "qemu: error loading %s: %s\n", - kernel_filename, load_elf_strerror(kernel_size)); + error_report("error loading %s: %s", + kernel_filename, load_elf_strerror(kernel_size)); exit(1); } @@ -1658,8 +1950,8 @@ static void ppc_spapr_init(MachineState *machine) initrd_size = load_image_targphys(initrd_filename, initrd_base, load_limit - initrd_base); if (initrd_size < 0) { - fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", - initrd_filename); + error_report("could not load initial ram disk '%s'", + initrd_filename); exit(1); } } else { @@ -1796,6 +2088,9 @@ static void spapr_set_kvm_type(Object *obj, const char *value, Error **errp) static void spapr_machine_initfn(Object *obj) { + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + + spapr->htab_fd = -1; object_property_add_str(obj, "kvm-type", spapr_get_kvm_type, spapr_set_kvm_type, NULL); object_property_set_description(obj, "kvm-type", @@ -1803,6 +2098,13 @@ static void spapr_machine_initfn(Object *obj) NULL); } +static void spapr_machine_finalizefn(Object *obj) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + + g_free(spapr->kvm_type); +} + static void ppc_cpu_do_nmi_on_cpu(void *arg) { CPUState *cs = arg; @@ -1820,22 +2122,177 @@ static void spapr_nmi(NMIState *n, int cpu_index, Error **errp) } } +static void spapr_add_lmbs(DeviceState *dev, uint64_t addr, uint64_t size, + uint32_t node, Error **errp) +{ + sPAPRDRConnector *drc; + sPAPRDRConnectorClass *drck; + uint32_t nr_lmbs = size/SPAPR_MEMORY_BLOCK_SIZE; + int i, fdt_offset, fdt_size; + void *fdt; + + /* + * Check for DRC connectors and send hotplug notification to the + * guest only in case of hotplugged memory. This allows cold plugged + * memory to be specified at boot time. + */ + if (!dev->hotplugged) { + return; + } + + for (i = 0; i < nr_lmbs; i++) { + drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, + addr/SPAPR_MEMORY_BLOCK_SIZE); + g_assert(drc); + + fdt = create_device_tree(&fdt_size); + fdt_offset = spapr_populate_memory_node(fdt, node, addr, + SPAPR_MEMORY_BLOCK_SIZE); + + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + drck->attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, errp); + addr += SPAPR_MEMORY_BLOCK_SIZE; + } + spapr_hotplug_req_add_by_count(SPAPR_DR_CONNECTOR_TYPE_LMB, nr_lmbs); +} + +static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + uint32_t node, Error **errp) +{ + Error *local_err = NULL; + sPAPRMachineState *ms = SPAPR_MACHINE(hotplug_dev); + PCDIMMDevice *dimm = PC_DIMM(dev); + PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); + MemoryRegion *mr = ddc->get_memory_region(dimm); + uint64_t align = memory_region_get_alignment(mr); + uint64_t size = memory_region_size(mr); + uint64_t addr; + + if (size % SPAPR_MEMORY_BLOCK_SIZE) { + error_setg(&local_err, "Hotplugged memory size must be a multiple of " + "%lld MB", SPAPR_MEMORY_BLOCK_SIZE/M_BYTE); + goto out; + } + + pc_dimm_memory_plug(dev, &ms->hotplug_memory, mr, align, &local_err); + if (local_err) { + goto out; + } + + addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err); + if (local_err) { + pc_dimm_memory_unplug(dev, &ms->hotplug_memory, mr); + goto out; + } + + spapr_add_lmbs(dev, addr, size, node, &error_abort); + +out: + error_propagate(errp, local_err); +} + +static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(qdev_get_machine()); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + int node; + + if (!smc->dr_lmb_enabled) { + error_setg(errp, "Memory hotplug not supported for this machine"); + return; + } + node = object_property_get_int(OBJECT(dev), PC_DIMM_NODE_PROP, errp); + if (*errp) { + return; + } + if (node < 0 || node >= MAX_NODES) { + error_setg(errp, "Invaild node %d", node); + return; + } + + /* + * Currently PowerPC kernel doesn't allow hot-adding memory to + * memory-less node, but instead will silently add the memory + * to the first node that has some memory. This causes two + * unexpected behaviours for the user. + * + * - Memory gets hotplugged to a different node than what the user + * specified. + * - Since pc-dimm subsystem in QEMU still thinks that memory belongs + * to memory-less node, a reboot will set things accordingly + * and the previously hotplugged memory now ends in the right node. + * This appears as if some memory moved from one node to another. + * + * So until kernel starts supporting memory hotplug to memory-less + * nodes, just prevent such attempts upfront in QEMU. + */ + if (nb_numa_nodes && !numa_info[node].node_mem) { + error_setg(errp, "Can't hotplug memory to memory-less node %d", + node); + return; + } + + spapr_memory_plug(hotplug_dev, dev, node, errp); + } +} + +static void spapr_machine_device_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + error_setg(errp, "Memory hot unplug not supported by sPAPR"); + } +} + +static HotplugHandler *spapr_get_hotpug_handler(MachineState *machine, + DeviceState *dev) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + return HOTPLUG_HANDLER(machine); + } + return NULL; +} + +static unsigned spapr_cpu_index_to_socket_id(unsigned cpu_index) +{ + /* Allocate to NUMA nodes on a "socket" basis (not that concept of + * socket means much for the paravirtualized PAPR platform) */ + return cpu_index / smp_threads / smp_cores; +} + static void spapr_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(oc); FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); + + mc->desc = "pSeries Logical Partition (PAPR compliant)"; + /* + * We set up the default / latest behaviour here. The class_init + * functions for the specific versioned machine types can override + * these details for backwards compatibility + */ mc->init = ppc_spapr_init; mc->reset = ppc_spapr_reset; mc->block_default_type = IF_SCSI; - mc->max_cpus = MAX_CPUS; + mc->max_cpus = MAX_CPUMASK_BITS; mc->no_parallel = 1; mc->default_boot_order = ""; mc->default_ram_size = 512 * M_BYTE; mc->kvm_type = spapr_kvm_type; mc->has_dynamic_sysbus = true; + mc->pci_allow_0_address = true; + mc->get_hotplug_handler = spapr_get_hotpug_handler; + hc->plug = spapr_machine_device_plug; + hc->unplug = spapr_machine_device_unplug; + mc->cpu_index_to_socket_id = spapr_cpu_index_to_socket_id; + smc->dr_lmb_enabled = true; fwc->get_dev_path = spapr_get_fw_dev_path; nc->nmi_monitor_handler = spapr_nmi; } @@ -1846,153 +2303,183 @@ static const TypeInfo spapr_machine_info = { .abstract = true, .instance_size = sizeof(sPAPRMachineState), .instance_init = spapr_machine_initfn, + .instance_finalize = spapr_machine_finalizefn, .class_size = sizeof(sPAPRMachineClass), .class_init = spapr_machine_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_FW_PATH_PROVIDER }, { TYPE_NMI }, + { TYPE_HOTPLUG_HANDLER }, { } }, }; -#define SPAPR_COMPAT_2_3 \ - HW_COMPAT_2_3 \ - {\ - .driver = "spapr-pci-host-bridge",\ - .property = "dynamic-reconfiguration",\ - .value = "off",\ - }, - -#define SPAPR_COMPAT_2_2 \ - SPAPR_COMPAT_2_3 \ - HW_COMPAT_2_2 \ - {\ - .driver = TYPE_SPAPR_PCI_HOST_BRIDGE,\ - .property = "mem_win_size",\ - .value = "0x20000000",\ - }, - -#define SPAPR_COMPAT_2_1 \ - SPAPR_COMPAT_2_2 \ - HW_COMPAT_2_1 +#define DEFINE_SPAPR_MACHINE(suffix, verstr, latest) \ + static void spapr_machine_##suffix##_class_init(ObjectClass *oc, \ + void *data) \ + { \ + MachineClass *mc = MACHINE_CLASS(oc); \ + spapr_machine_##suffix##_class_options(mc); \ + if (latest) { \ + mc->alias = "pseries"; \ + mc->is_default = 1; \ + } \ + } \ + static void spapr_machine_##suffix##_instance_init(Object *obj) \ + { \ + MachineState *machine = MACHINE(obj); \ + spapr_machine_##suffix##_instance_options(machine); \ + } \ + static const TypeInfo spapr_machine_##suffix##_info = { \ + .name = MACHINE_TYPE_NAME("pseries-" verstr), \ + .parent = TYPE_SPAPR_MACHINE, \ + .class_init = spapr_machine_##suffix##_class_init, \ + .instance_init = spapr_machine_##suffix##_instance_init, \ + }; \ + static void spapr_machine_register_##suffix(void) \ + { \ + type_register(&spapr_machine_##suffix##_info); \ + } \ + type_init(spapr_machine_register_##suffix) -static void spapr_compat_2_3(Object *obj) +/* + * pseries-2.6 + */ +static void spapr_machine_2_6_instance_options(MachineState *machine) { - savevm_skip_section_footers(); - global_state_set_optional(); } -static void spapr_compat_2_2(Object *obj) +static void spapr_machine_2_6_class_options(MachineClass *mc) { - spapr_compat_2_3(obj); + /* Defaults for the latest behaviour inherited from the base class */ } -static void spapr_compat_2_1(Object *obj) -{ - spapr_compat_2_2(obj); -} +DEFINE_SPAPR_MACHINE(2_6, "2.6", true); + +/* + * pseries-2.5 + */ +#define SPAPR_COMPAT_2_5 \ + HW_COMPAT_2_5 \ + { \ + .driver = "spapr-vlan", \ + .property = "use-rx-buffer-pools", \ + .value = "off", \ + }, -static void spapr_machine_2_3_instance_init(Object *obj) +static void spapr_machine_2_5_instance_options(MachineState *machine) { - spapr_compat_2_3(obj); - spapr_machine_initfn(obj); } -static void spapr_machine_2_2_instance_init(Object *obj) +static void spapr_machine_2_5_class_options(MachineClass *mc) { - spapr_compat_2_2(obj); - spapr_machine_initfn(obj); + sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + + spapr_machine_2_6_class_options(mc); + smc->use_ohci_by_default = true; + SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_5); } -static void spapr_machine_2_1_instance_init(Object *obj) +DEFINE_SPAPR_MACHINE(2_5, "2.5", false); + +/* + * pseries-2.4 + */ +#define SPAPR_COMPAT_2_4 \ + SPAPR_COMPAT_2_5 \ + HW_COMPAT_2_4 + +static void spapr_machine_2_4_instance_options(MachineState *machine) { - spapr_compat_2_1(obj); - spapr_machine_initfn(obj); + spapr_machine_2_5_instance_options(machine); } -static void spapr_machine_2_1_class_init(ObjectClass *oc, void *data) +static void spapr_machine_2_4_class_options(MachineClass *mc) { - MachineClass *mc = MACHINE_CLASS(oc); - static GlobalProperty compat_props[] = { - SPAPR_COMPAT_2_1 - { /* end of list */ } - }; + sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); - mc->name = "pseries-2.1"; - mc->desc = "pSeries Logical Partition (PAPR compliant) v2.1"; - mc->compat_props = compat_props; + spapr_machine_2_5_class_options(mc); + smc->dr_lmb_enabled = false; + SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_4); } -static const TypeInfo spapr_machine_2_1_info = { - .name = TYPE_SPAPR_MACHINE "2.1", - .parent = TYPE_SPAPR_MACHINE, - .class_init = spapr_machine_2_1_class_init, - .instance_init = spapr_machine_2_1_instance_init, -}; +DEFINE_SPAPR_MACHINE(2_4, "2.4", false); + +/* + * pseries-2.3 + */ +#define SPAPR_COMPAT_2_3 \ + SPAPR_COMPAT_2_4 \ + HW_COMPAT_2_3 \ + {\ + .driver = "spapr-pci-host-bridge",\ + .property = "dynamic-reconfiguration",\ + .value = "off",\ + }, -static void spapr_machine_2_2_class_init(ObjectClass *oc, void *data) +static void spapr_machine_2_3_instance_options(MachineState *machine) { - static GlobalProperty compat_props[] = { - SPAPR_COMPAT_2_2 - { /* end of list */ } - }; - MachineClass *mc = MACHINE_CLASS(oc); + spapr_machine_2_4_instance_options(machine); + savevm_skip_section_footers(); + global_state_set_optional(); + savevm_skip_configuration(); +} - mc->name = "pseries-2.2"; - mc->desc = "pSeries Logical Partition (PAPR compliant) v2.2"; - mc->compat_props = compat_props; +static void spapr_machine_2_3_class_options(MachineClass *mc) +{ + spapr_machine_2_4_class_options(mc); + SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_3); } +DEFINE_SPAPR_MACHINE(2_3, "2.3", false); -static const TypeInfo spapr_machine_2_2_info = { - .name = TYPE_SPAPR_MACHINE "2.2", - .parent = TYPE_SPAPR_MACHINE, - .class_init = spapr_machine_2_2_class_init, - .instance_init = spapr_machine_2_2_instance_init, -}; +/* + * pseries-2.2 + */ -static void spapr_machine_2_3_class_init(ObjectClass *oc, void *data) +#define SPAPR_COMPAT_2_2 \ + SPAPR_COMPAT_2_3 \ + HW_COMPAT_2_2 \ + {\ + .driver = TYPE_SPAPR_PCI_HOST_BRIDGE,\ + .property = "mem_win_size",\ + .value = "0x20000000",\ + }, + +static void spapr_machine_2_2_instance_options(MachineState *machine) { - static GlobalProperty compat_props[] = { - SPAPR_COMPAT_2_3 - { /* end of list */ } - }; - MachineClass *mc = MACHINE_CLASS(oc); + spapr_machine_2_3_instance_options(machine); + machine->suppress_vmdesc = true; +} - mc->name = "pseries-2.3"; - mc->desc = "pSeries Logical Partition (PAPR compliant) v2.3"; - mc->compat_props = compat_props; +static void spapr_machine_2_2_class_options(MachineClass *mc) +{ + spapr_machine_2_3_class_options(mc); + SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_2); } +DEFINE_SPAPR_MACHINE(2_2, "2.2", false); -static const TypeInfo spapr_machine_2_3_info = { - .name = TYPE_SPAPR_MACHINE "2.3", - .parent = TYPE_SPAPR_MACHINE, - .class_init = spapr_machine_2_3_class_init, - .instance_init = spapr_machine_2_3_instance_init, -}; +/* + * pseries-2.1 + */ +#define SPAPR_COMPAT_2_1 \ + SPAPR_COMPAT_2_2 \ + HW_COMPAT_2_1 -static void spapr_machine_2_4_class_init(ObjectClass *oc, void *data) +static void spapr_machine_2_1_instance_options(MachineState *machine) { - MachineClass *mc = MACHINE_CLASS(oc); - - mc->name = "pseries-2.4"; - mc->desc = "pSeries Logical Partition (PAPR compliant) v2.4"; - mc->alias = "pseries"; - mc->is_default = 1; + spapr_machine_2_2_instance_options(machine); } -static const TypeInfo spapr_machine_2_4_info = { - .name = TYPE_SPAPR_MACHINE "2.4", - .parent = TYPE_SPAPR_MACHINE, - .class_init = spapr_machine_2_4_class_init, -}; +static void spapr_machine_2_1_class_options(MachineClass *mc) +{ + spapr_machine_2_2_class_options(mc); + SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_1); +} +DEFINE_SPAPR_MACHINE(2_1, "2.1", false); static void spapr_machine_register_types(void) { type_register_static(&spapr_machine_info); - type_register_static(&spapr_machine_2_1_info); - type_register_static(&spapr_machine_2_2_info); - type_register_static(&spapr_machine_2_3_info); - type_register_static(&spapr_machine_2_4_info); } type_init(spapr_machine_register_types) diff --git a/qemu/hw/ppc/spapr_drc.c b/qemu/hw/ppc/spapr_drc.c index ee874326e..1f5f1d790 100644 --- a/qemu/hw/ppc/spapr_drc.c +++ b/qemu/hw/ppc/spapr_drc.c @@ -10,11 +10,16 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "cpu.h" +#include "qemu/cutils.h" #include "hw/ppc/spapr_drc.h" #include "qom/object.h" #include "hw/qdev.h" #include "qapi/visitor.h" #include "qemu/error-report.h" +#include "hw/ppc/spapr.h" /* for RTAS return codes */ /* #define DEBUG_SPAPR_DRC */ @@ -32,7 +37,7 @@ #define DRC_CONTAINER_PATH "/dr-connector" #define DRC_INDEX_TYPE_SHIFT 28 -#define DRC_INDEX_ID_MASK (~(~0 << DRC_INDEX_TYPE_SHIFT)) +#define DRC_INDEX_ID_MASK ((1ULL << DRC_INDEX_TYPE_SHIFT) - 1) static sPAPRDRConnectorTypeShift get_type_shift(sPAPRDRConnectorType type) { @@ -59,13 +64,23 @@ static uint32_t get_index(sPAPRDRConnector *drc) (drc->id & DRC_INDEX_ID_MASK); } -static int set_isolation_state(sPAPRDRConnector *drc, - sPAPRDRIsolationState state) +static uint32_t set_isolation_state(sPAPRDRConnector *drc, + sPAPRDRIsolationState state) { sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); DPRINTFN("drc: %x, set_isolation_state: %x", get_index(drc), state); + if (state == SPAPR_DR_ISOLATION_STATE_UNISOLATED) { + /* cannot unisolate a non-existant resource, and, or resources + * which are in an 'UNUSABLE' allocation state. (PAPR 2.7, 13.5.3.5) + */ + if (!drc->dev || + drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) { + return RTAS_OUT_NO_SUCH_INDICATOR; + } + } + drc->isolation_state = state; if (drc->isolation_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) { @@ -89,24 +104,35 @@ static int set_isolation_state(sPAPRDRConnector *drc, drc->configured = false; } - return 0; + return RTAS_OUT_SUCCESS; } -static int set_indicator_state(sPAPRDRConnector *drc, - sPAPRDRIndicatorState state) +static uint32_t set_indicator_state(sPAPRDRConnector *drc, + sPAPRDRIndicatorState state) { DPRINTFN("drc: %x, set_indicator_state: %x", get_index(drc), state); drc->indicator_state = state; - return 0; + return RTAS_OUT_SUCCESS; } -static int set_allocation_state(sPAPRDRConnector *drc, - sPAPRDRAllocationState state) +static uint32_t set_allocation_state(sPAPRDRConnector *drc, + sPAPRDRAllocationState state) { sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); DPRINTFN("drc: %x, set_allocation_state: %x", get_index(drc), state); + if (state == SPAPR_DR_ALLOCATION_STATE_USABLE) { + /* if there's no resource/device associated with the DRC, there's + * no way for us to put it in an allocation state consistent with + * being 'USABLE'. PAPR 2.7, 13.5.3.4 documents that this should + * result in an RTAS return code of -3 / "no such indicator" + */ + if (!drc->dev) { + return RTAS_OUT_NO_SUCH_INDICATOR; + } + } + if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI) { drc->allocation_state = state; if (drc->awaiting_release && @@ -116,7 +142,7 @@ static int set_allocation_state(sPAPRDRConnector *drc, drc->detach_cb_opaque, NULL); } } - return 0; + return RTAS_OUT_SUCCESS; } static uint32_t get_type(sPAPRDRConnector *drc) @@ -150,6 +176,12 @@ static void set_configured(sPAPRDRConnector *drc) drc->configured = true; } +/* has the guest been notified of device attachment? */ +static void set_signalled(sPAPRDRConnector *drc) +{ + drc->signalled = true; +} + /* * dr-entity-sense sensor value * returned via get-sensor-state RTAS calls @@ -157,10 +189,8 @@ static void set_configured(sPAPRDRConnector *drc) * based on the current allocation/indicator/power states * for the DR connector. */ -static sPAPRDREntitySense entity_sense(sPAPRDRConnector *drc) +static uint32_t entity_sense(sPAPRDRConnector *drc, sPAPRDREntitySense *state) { - sPAPRDREntitySense state; - if (drc->dev) { if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI && drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) { @@ -169,7 +199,7 @@ static sPAPRDREntitySense entity_sense(sPAPRDRConnector *drc) * Otherwise, report the state as USABLE/PRESENT, * as we would for PCI. */ - state = SPAPR_DR_ENTITY_SENSE_UNUSABLE; + *state = SPAPR_DR_ENTITY_SENSE_UNUSABLE; } else { /* this assumes all PCI devices are assigned to * a 'live insertion' power domain, where QEMU @@ -177,39 +207,39 @@ static sPAPRDREntitySense entity_sense(sPAPRDRConnector *drc) * to the guest. present, non-PCI resources are * unaffected by power state. */ - state = SPAPR_DR_ENTITY_SENSE_PRESENT; + *state = SPAPR_DR_ENTITY_SENSE_PRESENT; } } else { if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) { /* PCI devices, and only PCI devices, use EMPTY * in cases where we'd otherwise use UNUSABLE */ - state = SPAPR_DR_ENTITY_SENSE_EMPTY; + *state = SPAPR_DR_ENTITY_SENSE_EMPTY; } else { - state = SPAPR_DR_ENTITY_SENSE_UNUSABLE; + *state = SPAPR_DR_ENTITY_SENSE_UNUSABLE; } } DPRINTFN("drc: %x, entity_sense: %x", get_index(drc), state); - return state; + return RTAS_OUT_SUCCESS; } -static void prop_get_index(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void prop_get_index(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); uint32_t value = (uint32_t)drck->get_index(drc); - visit_type_uint32(v, &value, name, errp); + visit_type_uint32(v, name, &value, errp); } -static void prop_get_type(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void prop_get_type(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); uint32_t value = (uint32_t)drck->get_type(drc); - visit_type_uint32(v, &value, name, errp); + visit_type_uint32(v, name, &value, errp); } static char *prop_get_name(Object *obj, Error **errp) @@ -219,23 +249,31 @@ static char *prop_get_name(Object *obj, Error **errp) return g_strdup(drck->get_name(drc)); } -static void prop_get_entity_sense(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void prop_get_entity_sense(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - uint32_t value = (uint32_t)drck->entity_sense(drc); - visit_type_uint32(v, &value, name, errp); + uint32_t value; + + drck->entity_sense(drc, &value); + visit_type_uint32(v, name, &value, errp); } -static void prop_get_fdt(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void prop_get_fdt(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); + Error *err = NULL; int fdt_offset_next, fdt_offset, fdt_depth; void *fdt; if (!drc->fdt) { + visit_start_struct(v, name, NULL, 0, &err); + if (!err) { + visit_end_struct(v, &err); + } + error_propagate(errp, err); return; } @@ -254,24 +292,39 @@ static void prop_get_fdt(Object *obj, Visitor *v, void *opaque, case FDT_BEGIN_NODE: fdt_depth++; name = fdt_get_name(fdt, fdt_offset, &name_len); - visit_start_struct(v, NULL, NULL, name, 0, NULL); + visit_start_struct(v, name, NULL, 0, &err); + if (err) { + error_propagate(errp, err); + return; + } break; case FDT_END_NODE: /* shouldn't ever see an FDT_END_NODE before FDT_BEGIN_NODE */ g_assert(fdt_depth > 0); - visit_end_struct(v, NULL); + visit_end_struct(v, &err); + if (err) { + error_propagate(errp, err); + return; + } fdt_depth--; break; case FDT_PROP: { int i; prop = fdt_get_property_by_offset(fdt, fdt_offset, &prop_len); name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); - visit_start_list(v, name, NULL); + visit_start_list(v, name, &err); + if (err) { + error_propagate(errp, err); + return; + } for (i = 0; i < prop_len; i++) { - visit_type_uint8(v, (uint8_t *)&prop->data[i], NULL, NULL); - + visit_type_uint8(v, NULL, (uint8_t *)&prop->data[i], &err); + if (err) { + error_propagate(errp, err); + return; + } } - visit_end_list(v, NULL); + visit_end_list(v); break; } default: @@ -310,7 +363,18 @@ static void attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt, drc->dev = d; drc->fdt = fdt; drc->fdt_start_offset = fdt_start_offset; - drc->configured = false; + drc->configured = coldplug; + /* 'logical' DR resources such as memory/cpus are in some cases treated + * as a pool of resources from which the guest is free to choose from + * based on only a count. for resources that can be assigned in this + * fashion, we must assume the resource is signalled immediately + * since a single hotplug request might make an arbitrary number of + * such attached resources available to the guest, as opposed to + * 'physical' DR resources such as PCI where each device/resource is + * signalled individually. + */ + drc->signalled = (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI) + ? true : coldplug; object_property_add_link(OBJECT(drc), "device", object_get_typename(OBJECT(drc->dev)), @@ -327,6 +391,26 @@ static void detach(sPAPRDRConnector *drc, DeviceState *d, drc->detach_cb = detach_cb; drc->detach_cb_opaque = detach_cb_opaque; + /* if we've signalled device presence to the guest, or if the guest + * has gone ahead and configured the device (via manually-executed + * device add via drmgr in guest, namely), we need to wait + * for the guest to quiesce the device before completing detach. + * Otherwise, we can assume the guest hasn't seen it and complete the + * detach immediately. Note that there is a small race window + * just before, or during, configuration, which is this context + * refers mainly to fetching the device tree via RTAS. + * During this window the device access will be arbitrated by + * associated DRC, which will simply fail the RTAS calls as invalid. + * This is recoverable within guest and current implementations of + * drmgr should be able to cope. + */ + if (!drc->signalled && !drc->configured) { + /* if the guest hasn't seen the device we can't rely on it to + * set it back to an isolated state via RTAS, so do it here manually + */ + drc->isolation_state = SPAPR_DR_ISOLATION_STATE_ISOLATED; + } + if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_ISOLATED) { DPRINTFN("awaiting transition to isolated state before removal"); drc->awaiting_release = true; @@ -365,6 +449,7 @@ static void reset(DeviceState *d) { sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d); sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + sPAPRDREntitySense state; DPRINTFN("drc reset: %x", drck->get_index(drc)); /* immediately upon reset we can safely assume DRCs whose devices @@ -392,6 +477,11 @@ static void reset(DeviceState *d) drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_UNUSABLE); } } + + drck->entity_sense(drc, &state); + if (state == SPAPR_DR_ENTITY_SENSE_PRESENT) { + drck->set_signalled(drc); + } } static void realize(DeviceState *d, Error **errp) @@ -418,8 +508,7 @@ static void realize(DeviceState *d, Error **errp) object_property_add_alias(root_container, link_name, drc->owner, child_name, &err); if (err) { - error_report("%s", error_get_pretty(err)); - error_free(err); + error_report_err(err); object_unref(OBJECT(drc)); } g_free(child_name); @@ -439,8 +528,7 @@ static void unrealize(DeviceState *d, Error **errp) snprintf(name, sizeof(name), "%x", drck->get_index(drc)); object_property_del(root_container, name, &err); if (err) { - error_report("%s", error_get_pretty(err)); - error_free(err); + error_report_err(err); object_unref(OBJECT(drc)); } } @@ -451,14 +539,17 @@ sPAPRDRConnector *spapr_dr_connector_new(Object *owner, { sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(object_new(TYPE_SPAPR_DR_CONNECTOR)); + char *prop_name; g_assert(type); drc->type = type; drc->id = id; drc->owner = owner; - object_property_add_child(owner, "dr-connector[*]", OBJECT(drc), NULL); + prop_name = g_strdup_printf("dr-connector[%"PRIu32"]", get_index(drc)); + object_property_add_child(owner, prop_name, OBJECT(drc), NULL); object_property_set_bool(OBJECT(drc), true, "realized", NULL); + g_free(prop_name); /* human-readable name for a DRC to encode into the DT * description. this is mainly only used within a guest in place @@ -549,6 +640,11 @@ static void spapr_dr_connector_class_init(ObjectClass *k, void *data) drck->attach = attach; drck->detach = detach; drck->release_pending = release_pending; + drck->set_signalled = set_signalled; + /* + * Reason: it crashes FIXME find and document the real reason + */ + dk->cannot_instantiate_with_device_add_yet = true; } static const TypeInfo spapr_dr_connector_info = { @@ -632,6 +728,7 @@ int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner, { Object *root_container; ObjectProperty *prop; + ObjectPropertyIterator iter; uint32_t drc_count = 0; GArray *drc_indexes, *drc_power_domains; GString *drc_names, *drc_types; @@ -655,7 +752,8 @@ int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner, */ root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); - QTAILQ_FOREACH(prop, &root_container->properties, node) { + object_property_iter_init(&iter, root_container); + while ((prop = object_property_iter_next(&iter))) { Object *obj; sPAPRDRConnector *drc; sPAPRDRConnectorClass *drck; diff --git a/qemu/hw/ppc/spapr_events.c b/qemu/hw/ppc/spapr_events.c index f626eb7b3..049fb1b32 100644 --- a/qemu/hw/ppc/spapr_events.c +++ b/qemu/hw/ppc/spapr_events.c @@ -24,6 +24,8 @@ * THE SOFTWARE. * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "sysemu/sysemu.h" #include "sysemu/char.h" @@ -35,7 +37,8 @@ #include "hw/pci/pci.h" #include "hw/pci-host/spapr.h" #include "hw/ppc/spapr_drc.h" - +#include "qemu/help_option.h" +#include "qemu/bcd.h" #include struct rtas_error_log { @@ -386,7 +389,16 @@ static void spapr_powerdown_req(Notifier *n, void *opaque) qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq)); } -static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action) +static void spapr_hotplug_set_signalled(uint32_t drc_index) +{ + sPAPRDRConnector *drc = spapr_dr_connector_by_index(drc_index); + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + drck->set_signalled(drc); +} + +static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, + sPAPRDRConnectorType drc_type, + uint32_t drc) { sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); struct hp_log_full *new_hp; @@ -395,8 +407,6 @@ static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action) struct rtas_event_log_v6_maina *maina; struct rtas_event_log_v6_mainb *mainb; struct rtas_event_log_v6_hp *hp; - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - sPAPRDRConnectorType drc_type = drck->get_type(drc); new_hp = g_malloc0(sizeof(struct hp_log_full)); hdr = &new_hp->hdr; @@ -427,13 +437,17 @@ static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action) hp->hdr.section_length = cpu_to_be16(sizeof(*hp)); hp->hdr.section_version = 1; /* includes extended modifier */ hp->hotplug_action = hp_action; - + hp->hotplug_identifier = hp_id; switch (drc_type) { case SPAPR_DR_CONNECTOR_TYPE_PCI: - hp->drc.index = cpu_to_be32(drck->get_index(drc)); - hp->hotplug_identifier = RTAS_LOG_V6_HP_ID_DRC_INDEX; hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PCI; + if (hp->hotplug_action == RTAS_LOG_V6_HP_ACTION_ADD) { + spapr_hotplug_set_signalled(drc); + } + break; + case SPAPR_DR_CONNECTOR_TYPE_LMB: + hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_MEMORY; break; default: /* we shouldn't be signaling hotplug events for resources @@ -443,19 +457,49 @@ static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action) return; } + if (hp_id == RTAS_LOG_V6_HP_ID_DRC_COUNT) { + hp->drc.count = cpu_to_be32(drc); + } else if (hp_id == RTAS_LOG_V6_HP_ID_DRC_INDEX) { + hp->drc.index = cpu_to_be32(drc); + } + rtas_event_log_queue(RTAS_LOG_TYPE_HOTPLUG, new_hp, true); qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq)); } -void spapr_hotplug_req_add_event(sPAPRDRConnector *drc) +void spapr_hotplug_req_add_by_index(sPAPRDRConnector *drc) +{ + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + sPAPRDRConnectorType drc_type = drck->get_type(drc); + uint32_t index = drck->get_index(drc); + + spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_INDEX, + RTAS_LOG_V6_HP_ACTION_ADD, drc_type, index); +} + +void spapr_hotplug_req_remove_by_index(sPAPRDRConnector *drc) +{ + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + sPAPRDRConnectorType drc_type = drck->get_type(drc); + uint32_t index = drck->get_index(drc); + + spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_INDEX, + RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, index); +} + +void spapr_hotplug_req_add_by_count(sPAPRDRConnectorType drc_type, + uint32_t count) { - spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_ADD); + spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT, + RTAS_LOG_V6_HP_ACTION_ADD, drc_type, count); } -void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc) +void spapr_hotplug_req_remove_by_count(sPAPRDRConnectorType drc_type, + uint32_t count) { - spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_REMOVE); + spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT, + RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, count); } static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr, @@ -556,7 +600,8 @@ out_no_events: void spapr_events_init(sPAPRMachineState *spapr) { QTAILQ_INIT(&spapr->pending_events); - spapr->check_exception_irq = xics_alloc(spapr->icp, 0, 0, false); + spapr->check_exception_irq = xics_alloc(spapr->icp, 0, 0, false, + &error_fatal); spapr->epow_notifier.notify = spapr_powerdown_req; qemu_register_powerdown_notifier(&spapr->epow_notifier); spapr_rtas_register(RTAS_CHECK_EXCEPTION, "check-exception", diff --git a/qemu/hw/ppc/spapr_hcall.c b/qemu/hw/ppc/spapr_hcall.c index 652ddf6e3..8f40602a5 100644 --- a/qemu/hw/ppc/spapr_hcall.c +++ b/qemu/hw/ppc/spapr_hcall.c @@ -1,3 +1,5 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "sysemu/sysemu.h" #include "cpu.h" #include "helper_regs.h" @@ -37,40 +39,10 @@ static void set_spr(CPUState *cs, int spr, target_ulong value, run_on_cpu(cs, do_spr_sync, &s); } -static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r, - target_ulong pte_index) +static bool has_spr(PowerPCCPU *cpu, int spr) { - target_ulong rb, va_low; - - rb = (v & ~0x7fULL) << 16; /* AVA field */ - va_low = pte_index >> 3; - if (v & HPTE64_V_SECONDARY) { - va_low = ~va_low; - } - /* xor vsid from AVA */ - if (!(v & HPTE64_V_1TB_SEG)) { - va_low ^= v >> 12; - } else { - va_low ^= v >> 24; - } - va_low &= 0x7ff; - if (v & HPTE64_V_LARGE) { - rb |= 1; /* L field */ -#if 0 /* Disable that P7 specific bit for now */ - if (r & 0xff000) { - /* non-16MB large page, must be 64k */ - /* (masks depend on page size) */ - rb |= 0x1000; /* page encoding in LP field */ - rb |= (va_low & 0x7f) << 16; /* 7b of VA in AVA/LP field */ - rb |= (va_low & 0xfe); /* AVAL field */ - } -#endif - } else { - /* 4kB page */ - rb |= (va_low & 0x7ff) << 12; /* remaining 11b of AVA */ - } - rb |= (v >> 54) & 0x300; /* B field */ - return rb; + /* We can test whether the SPR is defined by checking for a valid name */ + return cpu->env.spr_cb[spr].name != NULL; } static inline bool valid_pte_index(CPUPPCState *env, target_ulong pte_index) @@ -84,42 +56,44 @@ static inline bool valid_pte_index(CPUPPCState *env, target_ulong pte_index) return true; } +static bool is_ram_address(sPAPRMachineState *spapr, hwaddr addr) +{ + MachineState *machine = MACHINE(spapr); + MemoryHotplugState *hpms = &spapr->hotplug_memory; + + if (addr < machine->ram_size) { + return true; + } + if ((addr >= hpms->base) + && ((addr - hpms->base) < memory_region_size(&hpms->mr))) { + return true; + } + + return false; +} + static target_ulong h_enter(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - MachineState *machine = MACHINE(spapr); CPUPPCState *env = &cpu->env; target_ulong flags = args[0]; target_ulong pte_index = args[1]; target_ulong pteh = args[2]; target_ulong ptel = args[3]; - target_ulong page_shift = 12; + unsigned apshift, spshift; target_ulong raddr; target_ulong index; uint64_t token; - /* only handle 4k and 16M pages for now */ - if (pteh & HPTE64_V_LARGE) { -#if 0 /* We don't support 64k pages yet */ - if ((ptel & 0xf000) == 0x1000) { - /* 64k page */ - } else -#endif - if ((ptel & 0xff000) == 0) { - /* 16M page */ - page_shift = 24; - /* lowest AVA bit must be 0 for 16M pages */ - if (pteh & 0x80) { - return H_PARAMETER; - } - } else { - return H_PARAMETER; - } + apshift = ppc_hash64_hpte_page_shift_noslb(cpu, pteh, ptel, &spshift); + if (!apshift) { + /* Bad page size encoding */ + return H_PARAMETER; } - raddr = (ptel & HPTE64_R_RPN) & ~((1ULL << page_shift) - 1); + raddr = (ptel & HPTE64_R_RPN) & ~((1ULL << apshift) - 1); - if (raddr < machine->ram_size) { + if (is_ram_address(spapr, raddr)) { /* Regular RAM - should have WIMG=0010 */ if ((ptel & HPTE64_R_WIMG) != HPTE64_R_M) { return H_PARAMETER; @@ -145,24 +119,24 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPRMachineState *spapr, pte_index &= ~7ULL; token = ppc_hash64_start_access(cpu, pte_index); for (; index < 8; index++) { - if ((ppc_hash64_load_hpte0(env, token, index) & HPTE64_V_VALID) == 0) { + if (!(ppc_hash64_load_hpte0(cpu, token, index) & HPTE64_V_VALID)) { break; } } - ppc_hash64_stop_access(token); + ppc_hash64_stop_access(cpu, token); if (index == 8) { return H_PTEG_FULL; } } else { token = ppc_hash64_start_access(cpu, pte_index); - if (ppc_hash64_load_hpte0(env, token, 0) & HPTE64_V_VALID) { - ppc_hash64_stop_access(token); + if (ppc_hash64_load_hpte0(cpu, token, 0) & HPTE64_V_VALID) { + ppc_hash64_stop_access(cpu, token); return H_PTEG_FULL; } - ppc_hash64_stop_access(token); + ppc_hash64_stop_access(cpu, token); } - ppc_hash64_store_hpte(env, pte_index + index, + ppc_hash64_store_hpte(cpu, pte_index + index, pteh | HPTE64_V_HPTE_DIRTY, ptel); args[0] = pte_index + index; @@ -176,22 +150,23 @@ typedef enum { REMOVE_HW = 3, } RemoveResult; -static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex, +static RemoveResult remove_hpte(PowerPCCPU *cpu, target_ulong ptex, target_ulong avpn, target_ulong flags, target_ulong *vp, target_ulong *rp) { + CPUPPCState *env = &cpu->env; uint64_t token; - target_ulong v, r, rb; + target_ulong v, r; if (!valid_pte_index(env, ptex)) { return REMOVE_PARM; } - token = ppc_hash64_start_access(ppc_env_get_cpu(env), ptex); - v = ppc_hash64_load_hpte0(env, token, 0); - r = ppc_hash64_load_hpte1(env, token, 0); - ppc_hash64_stop_access(token); + token = ppc_hash64_start_access(cpu, ptex); + v = ppc_hash64_load_hpte0(cpu, token, 0); + r = ppc_hash64_load_hpte1(cpu, token, 0); + ppc_hash64_stop_access(cpu, token); if ((v & HPTE64_V_VALID) == 0 || ((flags & H_AVPN) && (v & ~0x7fULL) != avpn) || @@ -200,22 +175,20 @@ static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex, } *vp = v; *rp = r; - ppc_hash64_store_hpte(env, ptex, HPTE64_V_HPTE_DIRTY, 0); - rb = compute_tlbie_rb(v, r, ptex); - ppc_tlb_invalidate_one(env, rb); + ppc_hash64_store_hpte(cpu, ptex, HPTE64_V_HPTE_DIRTY, 0); + ppc_hash64_tlb_flush_hpte(cpu, ptex, v, r); return REMOVE_SUCCESS; } static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - CPUPPCState *env = &cpu->env; target_ulong flags = args[0]; target_ulong pte_index = args[1]; target_ulong avpn = args[2]; RemoveResult ret; - ret = remove_hpte(env, pte_index, avpn, flags, + ret = remove_hpte(cpu, pte_index, avpn, flags, &args[0], &args[1]); switch (ret) { @@ -256,7 +229,6 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - CPUPPCState *env = &cpu->env; int i; for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) { @@ -278,7 +250,7 @@ static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_PARAMETER; } - ret = remove_hpte(env, *tsh & H_BULK_REMOVE_PTEX, tsl, + ret = remove_hpte(cpu, *tsh & H_BULK_REMOVE_PTEX, tsl, (*tsh & H_BULK_REMOVE_FLAGS) >> 26, &v, &r); @@ -308,16 +280,16 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong pte_index = args[1]; target_ulong avpn = args[2]; uint64_t token; - target_ulong v, r, rb; + target_ulong v, r; if (!valid_pte_index(env, pte_index)) { return H_PARAMETER; } token = ppc_hash64_start_access(cpu, pte_index); - v = ppc_hash64_load_hpte0(env, token, 0); - r = ppc_hash64_load_hpte1(env, token, 0); - ppc_hash64_stop_access(token); + v = ppc_hash64_load_hpte0(cpu, token, 0); + r = ppc_hash64_load_hpte1(cpu, token, 0); + ppc_hash64_stop_access(cpu, token); if ((v & HPTE64_V_VALID) == 0 || ((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) { @@ -329,12 +301,11 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPRMachineState *spapr, r |= (flags << 55) & HPTE64_R_PP0; r |= (flags << 48) & HPTE64_R_KEY_HI; r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO); - rb = compute_tlbie_rb(v, r, pte_index); - ppc_hash64_store_hpte(env, pte_index, + ppc_hash64_store_hpte(cpu, pte_index, (v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY, 0); - ppc_tlb_invalidate_one(env, rb); + ppc_hash64_tlb_flush_hpte(cpu, pte_index, v, r); /* Don't need a memory barrier, due to qemu's global lock */ - ppc_hash64_store_hpte(env, pte_index, v | HPTE64_V_HPTE_DIRTY, r); + ppc_hash64_store_hpte(cpu, pte_index, v | HPTE64_V_HPTE_DIRTY, r); return H_SUCCESS; } @@ -368,11 +339,111 @@ static target_ulong h_read(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_SUCCESS; } +static target_ulong h_set_sprg0(PowerPCCPU *cpu, sPAPRMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + cpu_synchronize_state(CPU(cpu)); + cpu->env.spr[SPR_SPRG0] = args[0]; + + return H_SUCCESS; +} + static target_ulong h_set_dabr(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - /* FIXME: actually implement this */ - return H_HARDWARE; + if (!has_spr(cpu, SPR_DABR)) { + return H_HARDWARE; /* DABR register not available */ + } + cpu_synchronize_state(CPU(cpu)); + + if (has_spr(cpu, SPR_DABRX)) { + cpu->env.spr[SPR_DABRX] = 0x3; /* Use Problem and Privileged state */ + } else if (!(args[0] & 0x4)) { /* Breakpoint Translation set? */ + return H_RESERVED_DABR; + } + + cpu->env.spr[SPR_DABR] = args[0]; + return H_SUCCESS; +} + +static target_ulong h_set_xdabr(PowerPCCPU *cpu, sPAPRMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong dabrx = args[1]; + + if (!has_spr(cpu, SPR_DABR) || !has_spr(cpu, SPR_DABRX)) { + return H_HARDWARE; + } + + if ((dabrx & ~0xfULL) != 0 || (dabrx & H_DABRX_HYPERVISOR) != 0 + || (dabrx & (H_DABRX_KERNEL | H_DABRX_USER)) == 0) { + return H_PARAMETER; + } + + cpu_synchronize_state(CPU(cpu)); + cpu->env.spr[SPR_DABRX] = dabrx; + cpu->env.spr[SPR_DABR] = args[0]; + + return H_SUCCESS; +} + +static target_ulong h_page_init(PowerPCCPU *cpu, sPAPRMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong flags = args[0]; + hwaddr dst = args[1]; + hwaddr src = args[2]; + hwaddr len = TARGET_PAGE_SIZE; + uint8_t *pdst, *psrc; + target_long ret = H_SUCCESS; + + if (flags & ~(H_ICACHE_SYNCHRONIZE | H_ICACHE_INVALIDATE + | H_COPY_PAGE | H_ZERO_PAGE)) { + qemu_log_mask(LOG_UNIMP, "h_page_init: Bad flags (" TARGET_FMT_lx "\n", + flags); + return H_PARAMETER; + } + + /* Map-in destination */ + if (!is_ram_address(spapr, dst) || (dst & ~TARGET_PAGE_MASK) != 0) { + return H_PARAMETER; + } + pdst = cpu_physical_memory_map(dst, &len, 1); + if (!pdst || len != TARGET_PAGE_SIZE) { + return H_PARAMETER; + } + + if (flags & H_COPY_PAGE) { + /* Map-in source, copy to destination, and unmap source again */ + if (!is_ram_address(spapr, src) || (src & ~TARGET_PAGE_MASK) != 0) { + ret = H_PARAMETER; + goto unmap_out; + } + psrc = cpu_physical_memory_map(src, &len, 0); + if (!psrc || len != TARGET_PAGE_SIZE) { + ret = H_PARAMETER; + goto unmap_out; + } + memcpy(pdst, psrc, len); + cpu_physical_memory_unmap(psrc, len, 0, len); + } else if (flags & H_ZERO_PAGE) { + memset(pdst, 0, len); /* Just clear the destination page */ + } + + if (kvm_enabled() && (flags & H_ICACHE_SYNCHRONIZE) != 0) { + kvmppc_dcbst_range(cpu, pdst, len); + } + if (flags & (H_ICACHE_SYNCHRONIZE | H_ICACHE_INVALIDATE)) { + if (kvm_enabled()) { + kvmppc_icbi_range(cpu, pdst, len); + } else { + tb_flush(CPU(cpu)); + } + } + +unmap_out: + cpu_physical_memory_unmap(pdst, TARGET_PAGE_SIZE, 1, len); + return ret; } #define FLAGS_REGISTER_VPA 0x0000200000000000ULL @@ -753,7 +824,6 @@ static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu, { CPUState *cs; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - target_ulong prefix; if (!(pcc->insns_flags2 & PPC2_ISA207S)) { return H_P2; @@ -765,25 +835,12 @@ static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu, return H_P4; } - switch (mflags) { - case H_SET_MODE_ADDR_TRANS_NONE: - prefix = 0; - break; - case H_SET_MODE_ADDR_TRANS_0001_8000: - prefix = 0x18000; - break; - case H_SET_MODE_ADDR_TRANS_C000_0000_0000_4000: - prefix = 0xC000000000004000ULL; - break; - default: + if (mflags == AIL_RESERVED) { return H_UNSUPPORTED_FLAG; } CPU_FOREACH(cs) { - CPUPPCState *env = &POWERPC_CPU(cpu)->env; - set_spr(cs, SPR_LPCR, mflags << LPCR_AIL_SHIFT, LPCR_AIL); - env->excp_prefix = prefix; } return H_SUCCESS; @@ -808,10 +865,36 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPRMachineState *spapr, return ret; } +/* + * Return the offset to the requested option vector @vector in the + * option vector table @table. + */ +static target_ulong cas_get_option_vector(int vector, target_ulong table) +{ + int i; + char nr_vectors, nr_entries; + + if (!table) { + return 0; + } + + nr_vectors = (ldl_phys(&address_space_memory, table) >> 24) + 1; + if (!vector || vector > nr_vectors) { + return 0; + } + table++; /* skip nr option vectors */ + + for (i = 0; i < vector - 1; i++) { + nr_entries = ldl_phys(&address_space_memory, table) >> 24; + table += nr_entries + 2; + } + return table; +} + typedef struct { PowerPCCPU *cpu; uint32_t cpu_version; - int ret; + Error *err; } SetCompatState; static void do_set_compat(void *arg) @@ -819,7 +902,7 @@ static void do_set_compat(void *arg) SetCompatState *s = arg; cpu_synchronize_state(CPU(s->cpu)); - s->ret = ppc_set_compat(s->cpu, s->cpu_version); + ppc_set_compat(s->cpu, s->cpu_version, &s->err); } #define get_compat_level(cpuver) ( \ @@ -828,27 +911,31 @@ static void do_set_compat(void *arg) ((cpuver) == CPU_POWERPC_LOGICAL_2_06_PLUS) ? 2061 : \ ((cpuver) == CPU_POWERPC_LOGICAL_2_07) ? 2070 : 0) +#define OV5_DRCONF_MEMORY 0x20 + static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - target_ulong list = args[0]; + target_ulong list = ppc64_phys_to_real(args[0]); + target_ulong ov_table, ov5; PowerPCCPUClass *pcc_ = POWERPC_CPU_GET_CLASS(cpu_); CPUState *cs; - bool cpu_match = false; + bool cpu_match = false, cpu_update = true, memory_update = false; unsigned old_cpu_version = cpu_->cpu_version; unsigned compat_lvl = 0, cpu_version = 0; unsigned max_lvl = get_compat_level(cpu_->max_compat); int counter; + char ov5_byte2; /* Parse PVR list */ for (counter = 0; counter < 512; ++counter) { uint32_t pvr, pvr_mask; - pvr_mask = rtas_ld(list, 0); + pvr_mask = ldl_be_phys(&address_space_memory, list); list += 4; - pvr = rtas_ld(list, 0); + pvr = ldl_be_phys(&address_space_memory, list); list += 4; trace_spapr_cas_pvr_try(pvr); @@ -890,8 +977,6 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, } } - /* For the future use: here @list points to the first capability */ - /* Parsing finished */ trace_spapr_cas_pvr(cpu_->cpu_version, cpu_match, cpu_version, pcc_->pcr_mask); @@ -902,27 +987,38 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, SetCompatState s = { .cpu = POWERPC_CPU(cs), .cpu_version = cpu_version, - .ret = 0 + .err = NULL, }; run_on_cpu(cs, do_set_compat, &s); - if (s.ret < 0) { - fprintf(stderr, "Unable to set compatibility mode\n"); + if (s.err) { + error_report_err(s.err); return H_HARDWARE; } } } if (!cpu_version) { - return H_SUCCESS; + cpu_update = false; } - if (!list) { + /* For the future use: here @ov_table points to the first option vector */ + ov_table = list; + + ov5 = cas_get_option_vector(5, ov_table); + if (!ov5) { return H_SUCCESS; } - if (spapr_h_cas_compose_response(spapr, args[1], args[2])) { + /* @list now points to OV 5 */ + ov5_byte2 = ldub_phys(&address_space_memory, ov5 + 2); + if (ov5_byte2 & OV5_DRCONF_MEMORY) { + memory_update = true; + } + + if (spapr_h_cas_compose_response(spapr, args[1], args[2], + cpu_update, memory_update)) { qemu_system_reset_request(); } @@ -971,7 +1067,8 @@ target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode, } } - hcall_dprintf("Unimplemented hcall 0x" TARGET_FMT_lx "\n", opcode); + qemu_log_mask(LOG_UNIMP, "Unimplemented SPAPR hcall 0x" TARGET_FMT_lx "\n", + opcode); return H_FUNCTION; } @@ -986,13 +1083,17 @@ static void hypercall_register_types(void) /* hcall-bulk */ spapr_register_hypercall(H_BULK_REMOVE, h_bulk_remove); - /* hcall-dabr */ - spapr_register_hypercall(H_SET_DABR, h_set_dabr); - /* hcall-splpar */ spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa); spapr_register_hypercall(H_CEDE, h_cede); + /* processor register resource access h-calls */ + spapr_register_hypercall(H_SET_SPRG0, h_set_sprg0); + spapr_register_hypercall(H_SET_DABR, h_set_dabr); + spapr_register_hypercall(H_SET_XDABR, h_set_xdabr); + spapr_register_hypercall(H_PAGE_INIT, h_page_init); + spapr_register_hypercall(H_SET_MODE, h_set_mode); + /* "debugger" hcalls (also used by SLOF). Note: We do -not- differenciate * here between the "CI" and the "CACHE" variants, they will use whatever * mapping attributes qemu is using. When using KVM, the kernel will @@ -1009,8 +1110,6 @@ static void hypercall_register_types(void) /* qemu/KVM-PPC specific hcalls */ spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas); - spapr_register_hypercall(H_SET_MODE, h_set_mode); - /* ibm,client-architecture-support support */ spapr_register_hypercall(KVMPPC_H_CAS, h_client_architecture_support); } diff --git a/qemu/hw/ppc/spapr_iommu.c b/qemu/hw/ppc/spapr_iommu.c index f61504e0c..7dd458846 100644 --- a/qemu/hw/ppc/spapr_iommu.c +++ b/qemu/hw/ppc/spapr_iommu.c @@ -16,6 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "sysemu/kvm.h" #include "hw/qdev.h" @@ -146,7 +147,7 @@ static int spapr_tce_table_realize(DeviceState *dev) tcet->table = kvmppc_create_spapr_tce(tcet->liobn, window_size, &tcet->fd, - tcet->vfio_accel); + tcet->need_vfio); } if (!tcet->table) { @@ -168,11 +169,43 @@ static int spapr_tce_table_realize(DeviceState *dev) return 0; } +void spapr_tce_set_need_vfio(sPAPRTCETable *tcet, bool need_vfio) +{ + size_t table_size = tcet->nb_table * sizeof(uint64_t); + void *newtable; + + if (need_vfio == tcet->need_vfio) { + /* Nothing to do */ + return; + } + + if (!need_vfio) { + /* FIXME: We don't support transition back to KVM accelerated + * TCEs yet */ + return; + } + + tcet->need_vfio = true; + + if (tcet->fd < 0) { + /* Table is already in userspace, nothing to be do */ + return; + } + + newtable = g_malloc(table_size); + memcpy(newtable, tcet->table, table_size); + + kvmppc_remove_spapr_tce(tcet->table, tcet->fd, tcet->nb_table); + + tcet->fd = -1; + tcet->table = newtable; +} + sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, uint64_t bus_offset, uint32_t page_shift, uint32_t nb_table, - bool vfio_accel) + bool need_vfio) { sPAPRTCETable *tcet; char tmp[64]; @@ -192,7 +225,7 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, tcet->bus_offset = bus_offset; tcet->page_shift = page_shift; tcet->nb_table = nb_table; - tcet->vfio_accel = vfio_accel; + tcet->need_vfio = need_vfio; snprintf(tmp, sizeof(tmp), "tce-table-%x", liobn); object_property_add_child(OBJECT(owner), tmp, OBJECT(tcet), NULL); diff --git a/qemu/hw/ppc/spapr_pci.c b/qemu/hw/ppc/spapr_pci.c index a8f79d800..573e635bf 100644 --- a/qemu/hw/ppc/spapr_pci.c +++ b/qemu/hw/ppc/spapr_pci.c @@ -22,6 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "hw/pci/pci.h" @@ -41,6 +45,8 @@ #include "hw/ppc/spapr_drc.h" #include "sysemu/device_tree.h" +#include "hw/vfio/vfio.h" + /* Copied from the kernel arch/powerpc/platforms/pseries/msi.c */ #define RTAS_QUERY_FN 0 #define RTAS_CHANGE_FN 1 @@ -140,7 +146,7 @@ static void rtas_ibm_read_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr, return; } - buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + buid = rtas_ldq(args, 1); size = rtas_ld(args, 3); addr = rtas_ld(args, 0); @@ -206,7 +212,7 @@ static void rtas_ibm_write_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr, return; } - buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + buid = rtas_ldq(args, 1); val = rtas_ld(args, 4); size = rtas_ld(args, 3); addr = rtas_ld(args, 0); @@ -269,16 +275,17 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong rets) { uint32_t config_addr = rtas_ld(args, 0); - uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + uint64_t buid = rtas_ldq(args, 1); unsigned int func = rtas_ld(args, 3); unsigned int req_num = rtas_ld(args, 4); /* 0 == remove all */ unsigned int seq_num = rtas_ld(args, 5); unsigned int ret_intr_type; - unsigned int irq, max_irqs = 0, num = 0; + unsigned int irq, max_irqs = 0; sPAPRPHBState *phb = NULL; PCIDevice *pdev = NULL; spapr_pci_msi *msi; int *config_addr_key; + Error *err = NULL; switch (func) { case RTAS_CHANGE_MSI_FN: @@ -304,9 +311,10 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, return; } + msi = (spapr_pci_msi *) g_hash_table_lookup(phb->msi, &config_addr); + /* Releasing MSIs */ if (!req_num) { - msi = (spapr_pci_msi *) g_hash_table_lookup(phb->msi, &config_addr); if (!msi) { trace_spapr_pci_msi("Releasing wrong config", config_addr); rtas_st(rets, 0, RTAS_OUT_HW_ERROR); @@ -315,10 +323,10 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, xics_free(spapr->icp, msi->first_irq, msi->num); if (msi_present(pdev)) { - spapr_msi_setmsg(pdev, 0, false, 0, num); + spapr_msi_setmsg(pdev, 0, false, 0, 0); } if (msix_present(pdev)) { - spapr_msi_setmsg(pdev, 0, true, 0, num); + spapr_msi_setmsg(pdev, 0, true, 0, 0); } g_hash_table_remove(phb->msi, &config_addr); @@ -352,13 +360,20 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, /* Allocate MSIs */ irq = xics_alloc_block(spapr->icp, 0, req_num, false, - ret_intr_type == RTAS_TYPE_MSI); - if (!irq) { - error_report("Cannot allocate MSIs for device %x", config_addr); + ret_intr_type == RTAS_TYPE_MSI, &err); + if (err) { + error_reportf_err(err, "Can't allocate MSIs for device %x: ", + config_addr); rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } + /* Release previous MSIs */ + if (msi) { + xics_free(spapr->icp, msi->first_irq, msi->num); + g_hash_table_remove(phb->msi, &config_addr); + } + /* Setup MSI/MSIX vectors in the device (via cfgspace or MSIX BAR) */ spapr_msi_setmsg(pdev, SPAPR_PCI_MSI_WINDOW, ret_intr_type == RTAS_TYPE_MSIX, irq, req_num); @@ -375,7 +390,9 @@ out: rtas_st(rets, 0, RTAS_OUT_SUCCESS); rtas_st(rets, 1, req_num); rtas_st(rets, 2, ++seq_num); - rtas_st(rets, 3, ret_intr_type); + if (nret > 3) { + rtas_st(rets, 3, ret_intr_type); + } trace_spapr_pci_rtas_ibm_change_msi(config_addr, func, req_num, irq); } @@ -389,7 +406,7 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu, target_ulong rets) { uint32_t config_addr = rtas_ld(args, 0); - uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + uint64_t buid = rtas_ldq(args, 1); unsigned int intr_src_num = -1, ioa_intr_num = rtas_ld(args, 3); sPAPRPHBState *phb = NULL; PCIDevice *pdev = NULL; @@ -428,8 +445,6 @@ static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu, target_ulong rets) { sPAPRPHBState *sphb; - sPAPRPHBClass *spc; - PCIDevice *pdev; uint32_t addr, option; uint64_t buid; int ret; @@ -438,7 +453,7 @@ static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu, goto param_error_exit; } - buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + buid = rtas_ldq(args, 1); addr = rtas_ld(args, 0); option = rtas_ld(args, 3); @@ -447,18 +462,11 @@ static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu, goto param_error_exit; } - pdev = pci_find_device(PCI_HOST_BRIDGE(sphb)->bus, - (addr >> 16) & 0xFF, (addr >> 8) & 0xFF); - if (!pdev || !object_dynamic_cast(OBJECT(pdev), "vfio-pci")) { + if (!spapr_phb_eeh_available(sphb)) { goto param_error_exit; } - spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); - if (!spc->eeh_set_option) { - goto param_error_exit; - } - - ret = spc->eeh_set_option(sphb, addr, option); + ret = spapr_phb_vfio_eeh_set_option(sphb, addr, option); rtas_st(rets, 0, ret); return; @@ -473,7 +481,6 @@ static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu, target_ulong rets) { sPAPRPHBState *sphb; - sPAPRPHBClass *spc; PCIDevice *pdev; uint32_t addr, option; uint64_t buid; @@ -482,14 +489,13 @@ static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu, goto param_error_exit; } - buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + buid = rtas_ldq(args, 1); sphb = spapr_pci_find_phb(spapr, buid); if (!sphb) { goto param_error_exit; } - spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); - if (!spc->eeh_set_option) { + if (!spapr_phb_eeh_available(sphb)) { goto param_error_exit; } @@ -529,7 +535,6 @@ static void rtas_ibm_read_slot_reset_state2(PowerPCCPU *cpu, target_ulong rets) { sPAPRPHBState *sphb; - sPAPRPHBClass *spc; uint64_t buid; int state, ret; @@ -537,18 +542,17 @@ static void rtas_ibm_read_slot_reset_state2(PowerPCCPU *cpu, goto param_error_exit; } - buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + buid = rtas_ldq(args, 1); sphb = spapr_pci_find_phb(spapr, buid); if (!sphb) { goto param_error_exit; } - spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); - if (!spc->eeh_get_state) { + if (!spapr_phb_eeh_available(sphb)) { goto param_error_exit; } - ret = spc->eeh_get_state(sphb, &state); + ret = spapr_phb_vfio_eeh_get_state(sphb, &state); rtas_st(rets, 0, ret); if (ret != RTAS_OUT_SUCCESS) { return; @@ -573,7 +577,6 @@ static void rtas_ibm_set_slot_reset(PowerPCCPU *cpu, target_ulong rets) { sPAPRPHBState *sphb; - sPAPRPHBClass *spc; uint32_t option; uint64_t buid; int ret; @@ -582,19 +585,18 @@ static void rtas_ibm_set_slot_reset(PowerPCCPU *cpu, goto param_error_exit; } - buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + buid = rtas_ldq(args, 1); option = rtas_ld(args, 3); sphb = spapr_pci_find_phb(spapr, buid); if (!sphb) { goto param_error_exit; } - spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); - if (!spc->eeh_reset) { + if (!spapr_phb_eeh_available(sphb)) { goto param_error_exit; } - ret = spc->eeh_reset(sphb, option); + ret = spapr_phb_vfio_eeh_reset(sphb, option); rtas_st(rets, 0, ret); return; @@ -609,7 +611,6 @@ static void rtas_ibm_configure_pe(PowerPCCPU *cpu, target_ulong rets) { sPAPRPHBState *sphb; - sPAPRPHBClass *spc; uint64_t buid; int ret; @@ -617,18 +618,17 @@ static void rtas_ibm_configure_pe(PowerPCCPU *cpu, goto param_error_exit; } - buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + buid = rtas_ldq(args, 1); sphb = spapr_pci_find_phb(spapr, buid); if (!sphb) { goto param_error_exit; } - spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); - if (!spc->eeh_configure) { + if (!spapr_phb_eeh_available(sphb)) { goto param_error_exit; } - ret = spc->eeh_configure(sphb); + ret = spapr_phb_vfio_eeh_configure(sphb); rtas_st(rets, 0, ret); return; @@ -644,7 +644,6 @@ static void rtas_ibm_slot_error_detail(PowerPCCPU *cpu, target_ulong rets) { sPAPRPHBState *sphb; - sPAPRPHBClass *spc; int option; uint64_t buid; @@ -652,14 +651,13 @@ static void rtas_ibm_slot_error_detail(PowerPCCPU *cpu, goto param_error_exit; } - buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + buid = rtas_ldq(args, 1); sphb = spapr_pci_find_phb(spapr, buid); if (!sphb) { goto param_error_exit; } - spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); - if (!spc->eeh_set_option) { + if (!spapr_phb_eeh_available(sphb)) { goto param_error_exit; } @@ -955,6 +953,7 @@ static int spapr_populate_pci_child_dt(PCIDevice *dev, void *fdt, int offset, int pci_status, err; char *buf = NULL; uint32_t drc_index = spapr_phb_get_pci_drc_index(sphb, dev); + uint32_t max_msi, max_msix; if (pci_default_read_config(dev, PCI_HEADER_TYPE, 1) == PCI_HEADER_TYPE_BRIDGE) { @@ -1035,8 +1034,15 @@ static int spapr_populate_pci_child_dt(PCIDevice *dev, void *fdt, int offset, RESOURCE_CELLS_ADDRESS)); _FDT(fdt_setprop_cell(fdt, offset, "#size-cells", RESOURCE_CELLS_SIZE)); - _FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi-x", - RESOURCE_CELLS_SIZE)); + + max_msi = msi_nr_vectors_allocated(dev); + if (max_msi) { + _FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi", max_msi)); + } + max_msix = dev->msix_entries_nr; + if (max_msix) { + _FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi-x", max_msix)); + } populate_resource_props(dev, &rp); _FDT(fdt_setprop(fdt, offset, "reg", (uint8_t *)rp.reg, rp.reg_len)); @@ -1080,6 +1086,12 @@ static void spapr_phb_add_pci_device(sPAPRDRConnector *drc, void *fdt = NULL; int fdt_start_offset = 0, fdt_size; + if (object_dynamic_cast(OBJECT(pdev), "vfio-pci")) { + sPAPRTCETable *tcet = spapr_tce_find_by_liobn(phb->dma_liobn); + + spapr_tce_set_need_vfio(tcet, true); + } + if (dev->hotplugged) { fdt = create_device_tree(&fdt_size); fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0); @@ -1123,14 +1135,21 @@ static void spapr_phb_remove_pci_device(sPAPRDRConnector *drc, drck->detach(drc, DEVICE(pdev), spapr_phb_remove_pci_device_cb, phb, errp); } -static sPAPRDRConnector *spapr_phb_get_pci_drc(sPAPRPHBState *phb, - PCIDevice *pdev) +static sPAPRDRConnector *spapr_phb_get_pci_func_drc(sPAPRPHBState *phb, + uint32_t busnr, + int32_t devfn) { - uint32_t busnr = pci_bus_num(PCI_BUS(qdev_get_parent_bus(DEVICE(pdev)))); return spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_PCI, (phb->index << 16) | (busnr << 8) | - pdev->devfn); + devfn); +} + +static sPAPRDRConnector *spapr_phb_get_pci_drc(sPAPRPHBState *phb, + PCIDevice *pdev) +{ + uint32_t busnr = pci_bus_num(PCI_BUS(qdev_get_parent_bus(DEVICE(pdev)))); + return spapr_phb_get_pci_func_drc(phb, busnr, pdev->devfn); } static uint32_t spapr_phb_get_pci_drc_index(sPAPRPHBState *phb, @@ -1154,6 +1173,8 @@ static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler, PCIDevice *pdev = PCI_DEVICE(plugged_dev); sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev); Error *local_err = NULL; + PCIBus *bus = PCI_BUS(qdev_get_parent_bus(DEVICE(pdev))); + uint32_t slotnr = PCI_SLOT(pdev->devfn); /* if DR is disabled we don't need to do anything in the case of * hotplug or coldplug callbacks @@ -1171,13 +1192,44 @@ static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler, g_assert(drc); + /* Following the QEMU convention used for PCIe multifunction + * hotplug, we do not allow functions to be hotplugged to a + * slot that already has function 0 present + */ + if (plugged_dev->hotplugged && bus->devices[PCI_DEVFN(slotnr, 0)] && + PCI_FUNC(pdev->devfn) != 0) { + error_setg(errp, "PCI: slot %d function 0 already ocuppied by %s," + " additional functions can no longer be exposed to guest.", + slotnr, bus->devices[PCI_DEVFN(slotnr, 0)]->name); + return; + } + spapr_phb_add_pci_device(drc, phb, pdev, &local_err); if (local_err) { error_propagate(errp, local_err); return; } - if (plugged_dev->hotplugged) { - spapr_hotplug_req_add_event(drc); + + /* If this is function 0, signal hotplug for all the device functions. + * Otherwise defer sending the hotplug event. + */ + if (plugged_dev->hotplugged && PCI_FUNC(pdev->devfn) == 0) { + int i; + + for (i = 0; i < 8; i++) { + sPAPRDRConnector *func_drc; + sPAPRDRConnectorClass *func_drck; + sPAPRDREntitySense state; + + func_drc = spapr_phb_get_pci_func_drc(phb, pci_bus_num(bus), + PCI_DEVFN(slotnr, i)); + func_drck = SPAPR_DR_CONNECTOR_GET_CLASS(func_drc); + func_drck->entity_sense(func_drc, &state); + + if (state == SPAPR_DR_ENTITY_SENSE_PRESENT) { + spapr_hotplug_req_add_by_index(func_drc); + } + } } } @@ -1200,12 +1252,51 @@ static void spapr_phb_hot_unplug_child(HotplugHandler *plug_handler, drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); if (!drck->release_pending(drc)) { + PCIBus *bus = PCI_BUS(qdev_get_parent_bus(DEVICE(pdev))); + uint32_t slotnr = PCI_SLOT(pdev->devfn); + sPAPRDRConnector *func_drc; + sPAPRDRConnectorClass *func_drck; + sPAPRDREntitySense state; + int i; + + /* ensure any other present functions are pending unplug */ + if (PCI_FUNC(pdev->devfn) == 0) { + for (i = 1; i < 8; i++) { + func_drc = spapr_phb_get_pci_func_drc(phb, pci_bus_num(bus), + PCI_DEVFN(slotnr, i)); + func_drck = SPAPR_DR_CONNECTOR_GET_CLASS(func_drc); + func_drck->entity_sense(func_drc, &state); + if (state == SPAPR_DR_ENTITY_SENSE_PRESENT + && !func_drck->release_pending(func_drc)) { + error_setg(errp, + "PCI: slot %d, function %d still present. " + "Must unplug all non-0 functions first.", + slotnr, i); + return; + } + } + } + spapr_phb_remove_pci_device(drc, phb, pdev, &local_err); if (local_err) { error_propagate(errp, local_err); return; } - spapr_hotplug_req_remove_event(drc); + + /* if this isn't func 0, defer unplug event. otherwise signal removal + * for all present functions + */ + if (PCI_FUNC(pdev->devfn) == 0) { + for (i = 7; i >= 0; i--) { + func_drc = spapr_phb_get_pci_func_drc(phb, pci_bus_num(bus), + PCI_DEVFN(slotnr, i)); + func_drck = SPAPR_DR_CONNECTOR_GET_CLASS(func_drc); + func_drck->entity_sense(func_drc, &state); + if (state == SPAPR_DR_ENTITY_SENSE_PRESENT) { + spapr_hotplug_req_remove_by_index(func_drc); + } + } + } } } @@ -1215,11 +1306,12 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) SysBusDevice *s = SYS_BUS_DEVICE(dev); sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s); PCIHostState *phb = PCI_HOST_BRIDGE(s); - sPAPRPHBClass *info = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(s); char *namebuf; int i; PCIBus *bus; uint64_t msi_window_size = 4096; + sPAPRTCETable *tcet; + uint32_t nb_table; if (sphb->index != (uint32_t)-1) { hwaddr windows_base; @@ -1350,10 +1442,12 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) /* Initialize the LSI table */ for (i = 0; i < PCI_NUM_PINS; i++) { uint32_t irq; + Error *local_err = NULL; - irq = xics_alloc_block(spapr->icp, 0, 1, true, false); - if (!irq) { - error_setg(errp, "spapr_allocate_lsi failed"); + irq = xics_alloc_block(spapr->icp, 0, 1, true, false, &local_err); + if (local_err) { + error_propagate(errp, local_err); + error_prepend(errp, "can't allocate LSIs: "); return; } @@ -1369,33 +1463,20 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) } } - if (!info->finish_realize) { - error_setg(errp, "finish_realize not defined"); - return; - } - - info->finish_realize(sphb, errp); - - sphb->msi = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free); -} - -static void spapr_phb_finish_realize(sPAPRPHBState *sphb, Error **errp) -{ - sPAPRTCETable *tcet; - uint32_t nb_table; - - nb_table = SPAPR_PCI_DMA32_SIZE >> SPAPR_TCE_PAGE_SHIFT; + nb_table = sphb->dma_win_size >> SPAPR_TCE_PAGE_SHIFT; tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn, 0, SPAPR_TCE_PAGE_SHIFT, nb_table, false); if (!tcet) { error_setg(errp, "Unable to create TCE table for %s", sphb->dtbusname); - return ; + return; } /* Register default 32bit DMA window */ - memory_region_add_subregion(&sphb->iommu_root, 0, + memory_region_add_subregion(&sphb->iommu_root, sphb->dma_win_addr, spapr_tce_get_iommu(tcet)); + + sphb->msi = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free); } static int spapr_phb_children_reset(Object *child, void *opaque) @@ -1413,6 +1494,10 @@ static void spapr_phb_reset(DeviceState *qdev) { /* Reset the IOMMU state */ object_child_foreach(OBJECT(qdev), spapr_phb_children_reset, NULL); + + if (spapr_phb_eeh_available(SPAPR_PCI_HOST_BRIDGE(qdev))) { + spapr_phb_vfio_reset(qdev); + } } static Property spapr_phb_properties[] = { @@ -1427,6 +1512,9 @@ static Property spapr_phb_properties[] = { SPAPR_PCI_IO_WIN_SIZE), DEFINE_PROP_BOOL("dynamic-reconfiguration", sPAPRPHBState, dr_enabled, true), + /* Default DMA window is 0..1GB */ + DEFINE_PROP_UINT64("dma_win_addr", sPAPRPHBState, dma_win_addr, 0), + DEFINE_PROP_UINT64("dma_win_size", sPAPRPHBState, dma_win_size, 0x40000000), DEFINE_PROP_END_OF_LIST(), }; @@ -1460,10 +1548,8 @@ static void spapr_pci_pre_save(void *opaque) gpointer key, value; int i; - if (sphb->msi_devs) { - g_free(sphb->msi_devs); - sphb->msi_devs = NULL; - } + g_free(sphb->msi_devs); + sphb->msi_devs = NULL; sphb->msi_devs_num = g_hash_table_size(sphb->msi); if (!sphb->msi_devs_num) { return; @@ -1490,10 +1576,8 @@ static int spapr_pci_post_load(void *opaque, int version_id) sizeof(sphb->msi_devs[i].value)); g_hash_table_insert(sphb->msi, key, value); } - if (sphb->msi_devs) { - g_free(sphb->msi_devs); - sphb->msi_devs = NULL; - } + g_free(sphb->msi_devs); + sphb->msi_devs = NULL; sphb->msi_devs_num = 0; return 0; @@ -1533,7 +1617,6 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data) { PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - sPAPRPHBClass *spc = SPAPR_PCI_HOST_BRIDGE_CLASS(klass); HotplugHandlerClass *hp = HOTPLUG_HANDLER_CLASS(klass); hc->root_bus_path = spapr_phb_root_bus_path; @@ -1543,7 +1626,6 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_spapr_pci; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->cannot_instantiate_with_device_add_yet = false; - spc->finish_realize = spapr_phb_finish_realize; hp->plug = spapr_phb_hot_plug_child; hp->unplug = spapr_phb_hot_unplug_child; } @@ -1553,7 +1635,6 @@ static const TypeInfo spapr_phb_info = { .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(sPAPRPHBState), .class_init = spapr_phb_class_init, - .class_size = sizeof(sPAPRPHBClass), .interfaces = (InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } @@ -1735,6 +1816,9 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, sizeof(interrupt_map))); tcet = spapr_tce_find_by_liobn(SPAPR_PCI_LIOBN(phb->index, 0)); + if (!tcet) { + return -1; + } spapr_dma_dt(fdt, bus_off, "ibm,dma-window", tcet->liobn, tcet->bus_offset, tcet->nb_table << tcet->page_shift); @@ -1770,7 +1854,7 @@ void spapr_pci_rtas_init(void) rtas_ibm_read_pci_config); spapr_rtas_register(RTAS_IBM_WRITE_PCI_CONFIG, "ibm,write-pci-config", rtas_ibm_write_pci_config); - if (msi_supported) { + if (msi_nonbroken) { spapr_rtas_register(RTAS_IBM_QUERY_INTERRUPT_SOURCE_NUMBER, "ibm,query-interrupt-source-number", rtas_ibm_query_interrupt_source_number); diff --git a/qemu/hw/ppc/spapr_pci_vfio.c b/qemu/hw/ppc/spapr_pci_vfio.c index cca45ed31..cbd3d23c9 100644 --- a/qemu/hw/ppc/spapr_pci_vfio.c +++ b/qemu/hw/ppc/spapr_pci_vfio.c @@ -17,73 +17,51 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/ppc/spapr.h" #include "hw/pci-host/spapr.h" #include "hw/pci/msix.h" #include "linux/vfio.h" #include "hw/vfio/vfio.h" +#include "qemu/error-report.h" -static Property spapr_phb_vfio_properties[] = { - DEFINE_PROP_INT32("iommu", sPAPRPHBVFIOState, iommugroupid, -1), - DEFINE_PROP_END_OF_LIST(), -}; +#define TYPE_SPAPR_PCI_VFIO_HOST_BRIDGE "spapr-pci-vfio-host-bridge" -static void spapr_phb_vfio_finish_realize(sPAPRPHBState *sphb, Error **errp) -{ - sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb); - struct vfio_iommu_spapr_tce_info info = { .argsz = sizeof(info) }; - int ret; - sPAPRTCETable *tcet; - uint32_t liobn = svphb->phb.dma_liobn; +#define SPAPR_PCI_VFIO_HOST_BRIDGE(obj) \ + OBJECT_CHECK(sPAPRPHBVFIOState, (obj), TYPE_SPAPR_PCI_VFIO_HOST_BRIDGE) - if (svphb->iommugroupid == -1) { - error_setg(errp, "Wrong IOMMU group ID %d", svphb->iommugroupid); - return; - } +typedef struct sPAPRPHBVFIOState sPAPRPHBVFIOState; - ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, - VFIO_CHECK_EXTENSION, - (void *) VFIO_SPAPR_TCE_IOMMU); - if (ret != 1) { - error_setg_errno(errp, -ret, - "spapr-vfio: SPAPR extension is not supported"); - return; - } +struct sPAPRPHBVFIOState { + sPAPRPHBState phb; - ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, - VFIO_IOMMU_SPAPR_TCE_GET_INFO, &info); - if (ret) { - error_setg_errno(errp, -ret, - "spapr-vfio: get info from container failed"); - return; - } + int32_t iommugroupid; +}; - tcet = spapr_tce_new_table(DEVICE(sphb), liobn, info.dma32_window_start, - SPAPR_TCE_PAGE_SHIFT, - info.dma32_window_size >> SPAPR_TCE_PAGE_SHIFT, - true); - if (!tcet) { - error_setg(errp, "spapr-vfio: failed to create VFIO TCE table"); - return; - } +static Property spapr_phb_vfio_properties[] = { + DEFINE_PROP_INT32("iommu", sPAPRPHBVFIOState, iommugroupid, -1), + DEFINE_PROP_END_OF_LIST(), +}; - /* Register default 32bit DMA window */ - memory_region_add_subregion(&sphb->iommu_root, tcet->bus_offset, - spapr_tce_get_iommu(tcet)); +static void spapr_phb_vfio_instance_init(Object *obj) +{ + error_report("spapr-pci-vfio-host-bridge is deprecated"); } -static void spapr_phb_vfio_eeh_reenable(sPAPRPHBVFIOState *svphb) +bool spapr_phb_eeh_available(sPAPRPHBState *sphb) { - struct vfio_eeh_pe_op op = { - .argsz = sizeof(op), - .op = VFIO_EEH_PE_ENABLE - }; + return vfio_eeh_as_ok(&sphb->iommu_as); +} - vfio_container_ioctl(&svphb->phb.iommu_as, - svphb->iommugroupid, VFIO_EEH_PE_OP, &op); +static void spapr_phb_vfio_eeh_reenable(sPAPRPHBState *sphb) +{ + vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_ENABLE); } -static void spapr_phb_vfio_reset(DeviceState *qdev) +void spapr_phb_vfio_reset(DeviceState *qdev) { /* * The PE might be in frozen state. To reenable the EEH @@ -91,19 +69,18 @@ static void spapr_phb_vfio_reset(DeviceState *qdev) * ensures that the contained PCI devices will work properly * after reboot. */ - spapr_phb_vfio_eeh_reenable(SPAPR_PCI_VFIO_HOST_BRIDGE(qdev)); + spapr_phb_vfio_eeh_reenable(SPAPR_PCI_HOST_BRIDGE(qdev)); } -static int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb, - unsigned int addr, int option) +int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb, + unsigned int addr, int option) { - sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb); - struct vfio_eeh_pe_op op = { .argsz = sizeof(op) }; + uint32_t op; int ret; switch (option) { case RTAS_EEH_DISABLE: - op.op = VFIO_EEH_PE_DISABLE; + op = VFIO_EEH_PE_DISABLE; break; case RTAS_EEH_ENABLE: { PCIHostState *phb; @@ -117,25 +94,24 @@ static int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb, phb = PCI_HOST_BRIDGE(sphb); pdev = pci_find_device(phb->bus, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF); - if (!pdev) { + if (!pdev || !object_dynamic_cast(OBJECT(pdev), "vfio-pci")) { return RTAS_OUT_PARAM_ERROR; } - op.op = VFIO_EEH_PE_ENABLE; + op = VFIO_EEH_PE_ENABLE; break; } case RTAS_EEH_THAW_IO: - op.op = VFIO_EEH_PE_UNFREEZE_IO; + op = VFIO_EEH_PE_UNFREEZE_IO; break; case RTAS_EEH_THAW_DMA: - op.op = VFIO_EEH_PE_UNFREEZE_DMA; + op = VFIO_EEH_PE_UNFREEZE_DMA; break; default: return RTAS_OUT_PARAM_ERROR; } - ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, - VFIO_EEH_PE_OP, &op); + ret = vfio_eeh_as_op(&sphb->iommu_as, op); if (ret < 0) { return RTAS_OUT_HW_ERROR; } @@ -143,15 +119,11 @@ static int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb, return RTAS_OUT_SUCCESS; } -static int spapr_phb_vfio_eeh_get_state(sPAPRPHBState *sphb, int *state) +int spapr_phb_vfio_eeh_get_state(sPAPRPHBState *sphb, int *state) { - sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb); - struct vfio_eeh_pe_op op = { .argsz = sizeof(op) }; int ret; - op.op = VFIO_EEH_PE_GET_STATE; - ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, - VFIO_EEH_PE_OP, &op); + ret = vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_GET_STATE); if (ret < 0) { return RTAS_OUT_PARAM_ERROR; } @@ -203,30 +175,28 @@ static void spapr_phb_vfio_eeh_pre_reset(sPAPRPHBState *sphb) pci_for_each_bus(phb->bus, spapr_phb_vfio_eeh_clear_bus_msix, NULL); } -static int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option) +int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option) { - sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb); - struct vfio_eeh_pe_op op = { .argsz = sizeof(op) }; + uint32_t op; int ret; switch (option) { case RTAS_SLOT_RESET_DEACTIVATE: - op.op = VFIO_EEH_PE_RESET_DEACTIVATE; + op = VFIO_EEH_PE_RESET_DEACTIVATE; break; case RTAS_SLOT_RESET_HOT: spapr_phb_vfio_eeh_pre_reset(sphb); - op.op = VFIO_EEH_PE_RESET_HOT; + op = VFIO_EEH_PE_RESET_HOT; break; case RTAS_SLOT_RESET_FUNDAMENTAL: spapr_phb_vfio_eeh_pre_reset(sphb); - op.op = VFIO_EEH_PE_RESET_FUNDAMENTAL; + op = VFIO_EEH_PE_RESET_FUNDAMENTAL; break; default: return RTAS_OUT_PARAM_ERROR; } - ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, - VFIO_EEH_PE_OP, &op); + ret = vfio_eeh_as_op(&sphb->iommu_as, op); if (ret < 0) { return RTAS_OUT_HW_ERROR; } @@ -234,15 +204,11 @@ static int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option) return RTAS_OUT_SUCCESS; } -static int spapr_phb_vfio_eeh_configure(sPAPRPHBState *sphb) +int spapr_phb_vfio_eeh_configure(sPAPRPHBState *sphb) { - sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb); - struct vfio_eeh_pe_op op = { .argsz = sizeof(op) }; int ret; - op.op = VFIO_EEH_PE_CONFIGURE; - ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, - VFIO_EEH_PE_OP, &op); + ret = vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_CONFIGURE); if (ret < 0) { return RTAS_OUT_PARAM_ERROR; } @@ -253,23 +219,16 @@ static int spapr_phb_vfio_eeh_configure(sPAPRPHBState *sphb) static void spapr_phb_vfio_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - sPAPRPHBClass *spc = SPAPR_PCI_HOST_BRIDGE_CLASS(klass); dc->props = spapr_phb_vfio_properties; - dc->reset = spapr_phb_vfio_reset; - spc->finish_realize = spapr_phb_vfio_finish_realize; - spc->eeh_set_option = spapr_phb_vfio_eeh_set_option; - spc->eeh_get_state = spapr_phb_vfio_eeh_get_state; - spc->eeh_reset = spapr_phb_vfio_eeh_reset; - spc->eeh_configure = spapr_phb_vfio_eeh_configure; } static const TypeInfo spapr_phb_vfio_info = { .name = TYPE_SPAPR_PCI_VFIO_HOST_BRIDGE, .parent = TYPE_SPAPR_PCI_HOST_BRIDGE, .instance_size = sizeof(sPAPRPHBVFIOState), + .instance_init = spapr_phb_vfio_instance_init, .class_init = spapr_phb_vfio_class_init, - .class_size = sizeof(sPAPRPHBClass), }; static void spapr_pci_vfio_register_types(void) diff --git a/qemu/hw/ppc/spapr_rng.c b/qemu/hw/ppc/spapr_rng.c new file mode 100644 index 000000000..80515eb54 --- /dev/null +++ b/qemu/hw/ppc/spapr_rng.c @@ -0,0 +1,191 @@ +/* + * QEMU sPAPR random number generator "device" for H_RANDOM hypercall + * + * Copyright 2015 Thomas Huth, Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" +#include "qemu/error-report.h" +#include "sysemu/sysemu.h" +#include "sysemu/device_tree.h" +#include "sysemu/rng.h" +#include "hw/ppc/spapr.h" +#include "kvm_ppc.h" + +#define SPAPR_RNG(obj) \ + OBJECT_CHECK(sPAPRRngState, (obj), TYPE_SPAPR_RNG) + +struct sPAPRRngState { + /*< private >*/ + DeviceState ds; + RngBackend *backend; + bool use_kvm; +}; +typedef struct sPAPRRngState sPAPRRngState; + +struct HRandomData { + QemuSemaphore sem; + union { + uint64_t v64; + uint8_t v8[8]; + } val; + int received; +}; +typedef struct HRandomData HRandomData; + +/* Callback function for the RngBackend */ +static void random_recv(void *dest, const void *src, size_t size) +{ + HRandomData *hrdp = dest; + + if (src && size > 0) { + assert(size + hrdp->received <= sizeof(hrdp->val.v8)); + memcpy(&hrdp->val.v8[hrdp->received], src, size); + hrdp->received += size; + } + + qemu_sem_post(&hrdp->sem); +} + +/* Handler for the H_RANDOM hypercall */ +static target_ulong h_random(PowerPCCPU *cpu, sPAPRMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + sPAPRRngState *rngstate; + HRandomData hrdata; + + rngstate = SPAPR_RNG(object_resolve_path_type("", TYPE_SPAPR_RNG, NULL)); + + if (!rngstate || !rngstate->backend) { + return H_HARDWARE; + } + + qemu_sem_init(&hrdata.sem, 0); + hrdata.val.v64 = 0; + hrdata.received = 0; + + while (hrdata.received < 8) { + rng_backend_request_entropy(rngstate->backend, 8 - hrdata.received, + random_recv, &hrdata); + qemu_mutex_unlock_iothread(); + qemu_sem_wait(&hrdata.sem); + qemu_mutex_lock_iothread(); + } + + qemu_sem_destroy(&hrdata.sem); + args[0] = hrdata.val.v64; + + return H_SUCCESS; +} + +static void spapr_rng_instance_init(Object *obj) +{ + sPAPRRngState *rngstate = SPAPR_RNG(obj); + + if (object_resolve_path_type("", TYPE_SPAPR_RNG, NULL) != NULL) { + error_report("spapr-rng can not be instantiated twice!"); + return; + } + + object_property_add_link(obj, "rng", TYPE_RNG_BACKEND, + (Object **)&rngstate->backend, + object_property_allow_set_link, + OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); + object_property_set_description(obj, "rng", + "ID of the random number generator backend", + NULL); +} + +static void spapr_rng_realize(DeviceState *dev, Error **errp) +{ + + sPAPRRngState *rngstate = SPAPR_RNG(dev); + + if (rngstate->use_kvm) { + if (kvmppc_enable_hwrng() == 0) { + return; + } + /* + * If user specified both, use-kvm and a backend, we fall back to + * the backend now. If not, provide an appropriate error message. + */ + if (!rngstate->backend) { + error_setg(errp, "Could not initialize in-kernel H_RANDOM call!"); + return; + } + } + + if (rngstate->backend) { + spapr_register_hypercall(H_RANDOM, h_random); + } else { + error_setg(errp, "spapr-rng needs an RNG backend!"); + } +} + +int spapr_rng_populate_dt(void *fdt) +{ + int node; + int ret; + + node = qemu_fdt_add_subnode(fdt, "/ibm,platform-facilities"); + if (node <= 0) { + return -1; + } + ret = fdt_setprop_string(fdt, node, "device_type", + "ibm,platform-facilities"); + ret |= fdt_setprop_cell(fdt, node, "#address-cells", 0x1); + ret |= fdt_setprop_cell(fdt, node, "#size-cells", 0x0); + + node = fdt_add_subnode(fdt, node, "ibm,random-v1"); + if (node <= 0) { + return -1; + } + ret |= fdt_setprop_string(fdt, node, "compatible", "ibm,random"); + + return ret ? -1 : 0; +} + +static Property spapr_rng_properties[] = { + DEFINE_PROP_BOOL("use-kvm", sPAPRRngState, use_kvm, false), + DEFINE_PROP_END_OF_LIST(), +}; + +static void spapr_rng_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = spapr_rng_realize; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->props = spapr_rng_properties; + dc->hotpluggable = false; +} + +static const TypeInfo spapr_rng_info = { + .name = TYPE_SPAPR_RNG, + .parent = TYPE_DEVICE, + .instance_size = sizeof(sPAPRRngState), + .instance_init = spapr_rng_instance_init, + .class_init = spapr_rng_class_init, +}; + +static void spapr_rng_register_type(void) +{ + type_register_static(&spapr_rng_info); +} +type_init(spapr_rng_register_type) diff --git a/qemu/hw/ppc/spapr_rtas.c b/qemu/hw/ppc/spapr_rtas.c index 2986f94f0..f07325831 100644 --- a/qemu/hw/ppc/spapr_rtas.c +++ b/qemu/hw/ppc/spapr_rtas.c @@ -24,6 +24,7 @@ * THE SOFTWARE. * */ +#include "qemu/osdep.h" #include "cpu.h" #include "sysemu/sysemu.h" #include "sysemu/char.h" @@ -34,9 +35,11 @@ #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" #include "qapi-event.h" +#include "hw/boards.h" #include #include "hw/ppc/spapr_drc.h" +#include "qemu/cutils.h" /* #define DEBUG_SPAPR */ @@ -111,6 +114,7 @@ static void rtas_power_off(PowerPCCPU *cpu, sPAPRMachineState *spapr, return; } qemu_system_shutdown_request(); + cpu_stop_current(); rtas_st(rets, 0, RTAS_OUT_SUCCESS); } @@ -214,7 +218,7 @@ static void rtas_stop_self(PowerPCCPU *cpu, sPAPRMachineState *spapr, CPUPPCState *env = &cpu->env; cs->halted = 1; - cpu_exit(cs); + qemu_cpu_kick(cs); /* * While stopping a CPU, the guest calls H_CPPR which * effectively disables interrupts on XICS level. @@ -227,6 +231,19 @@ static void rtas_stop_self(PowerPCCPU *cpu, sPAPRMachineState *spapr, env->msr = 0; } +static inline int sysparm_st(target_ulong addr, target_ulong len, + const void *val, uint16_t vallen) +{ + hwaddr phys = ppc64_phys_to_real(addr); + + if (len < 2) { + return RTAS_OUT_SYSPARM_PARAM_ERROR; + } + stw_be_phys(&address_space_memory, phys, vallen); + cpu_physical_memory_write(phys + 2, val, MIN(len - 2, vallen)); + return RTAS_OUT_SUCCESS; +} + static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t token, uint32_t nargs, @@ -236,24 +253,30 @@ static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu, target_ulong parameter = rtas_ld(args, 0); target_ulong buffer = rtas_ld(args, 1); target_ulong length = rtas_ld(args, 2); - target_ulong ret = RTAS_OUT_SUCCESS; + target_ulong ret; switch (parameter) { case RTAS_SYSPARM_SPLPAR_CHARACTERISTICS: { - char *param_val = g_strdup_printf("MaxEntCap=%d,MaxPlatProcs=%d", - max_cpus, smp_cpus); - rtas_st_buffer(buffer, length, (uint8_t *)param_val, strlen(param_val)); + char *param_val = g_strdup_printf("MaxEntCap=%d," + "DesMem=%llu," + "DesProcs=%d," + "MaxPlatProcs=%d", + max_cpus, + current_machine->ram_size / M_BYTE, + smp_cpus, + max_cpus); + ret = sysparm_st(buffer, length, param_val, strlen(param_val) + 1); g_free(param_val); break; } case RTAS_SYSPARM_DIAGNOSTICS_RUN_MODE: { uint8_t param_val = DIAGNOSTICS_RUN_MODE_DISABLED; - rtas_st_buffer(buffer, length, ¶m_val, sizeof(param_val)); + ret = sysparm_st(buffer, length, ¶m_val, sizeof(param_val)); break; } case RTAS_SYSPARM_UUID: - rtas_st_buffer(buffer, length, qemu_uuid, (qemu_uuid_set ? 16 : 0)); + ret = sysparm_st(buffer, length, qemu_uuid, (qemu_uuid_set ? 16 : 0)); break; default: ret = RTAS_OUT_NOT_SUPPORTED; @@ -365,12 +388,13 @@ static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t sensor_type; uint32_t sensor_index; uint32_t sensor_state; + uint32_t ret = RTAS_OUT_SUCCESS; sPAPRDRConnector *drc; sPAPRDRConnectorClass *drck; if (nargs != 3 || nret != 1) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; + ret = RTAS_OUT_PARAM_ERROR; + goto out; } sensor_type = rtas_ld(args, 0); @@ -386,8 +410,8 @@ static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr, if (!drc) { DPRINTF("rtas_set_indicator: invalid sensor/DRC index: %xh\n", sensor_index); - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; + ret = RTAS_OUT_PARAM_ERROR; + goto out; } drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); @@ -406,19 +430,20 @@ static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr, spapr_ccs_remove(spapr, ccs); } } - drck->set_isolation_state(drc, sensor_state); + ret = drck->set_isolation_state(drc, sensor_state); break; case RTAS_SENSOR_TYPE_DR: - drck->set_indicator_state(drc, sensor_state); + ret = drck->set_indicator_state(drc, sensor_state); break; case RTAS_SENSOR_TYPE_ALLOCATION_STATE: - drck->set_allocation_state(drc, sensor_state); + ret = drck->set_allocation_state(drc, sensor_state); break; default: goto out_unimplemented; } - rtas_st(rets, 0, RTAS_OUT_SUCCESS); +out: + rtas_st(rets, 0, ret); return; out_unimplemented: @@ -435,13 +460,14 @@ static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr, { uint32_t sensor_type; uint32_t sensor_index; + uint32_t sensor_state = 0; sPAPRDRConnector *drc; sPAPRDRConnectorClass *drck; - uint32_t entity_sense; + uint32_t ret = RTAS_OUT_SUCCESS; if (nargs != 2 || nret != 2) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; + ret = RTAS_OUT_PARAM_ERROR; + goto out; } sensor_type = rtas_ld(args, 0); @@ -451,22 +477,23 @@ static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr, /* currently only DR-related sensors are implemented */ DPRINTF("rtas_get_sensor_state: sensor/indicator not implemented: %d\n", sensor_type); - rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); - return; + ret = RTAS_OUT_NOT_SUPPORTED; + goto out; } drc = spapr_dr_connector_by_index(sensor_index); if (!drc) { DPRINTF("rtas_get_sensor_state: invalid sensor/DRC index: %xh\n", sensor_index); - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; + ret = RTAS_OUT_PARAM_ERROR; + goto out; } drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - entity_sense = drck->entity_sense(drc); + ret = drck->entity_sense(drc, &sensor_state); - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - rtas_st(rets, 1, entity_sense); +out: + rtas_st(rets, 0, ret); + rtas_st(rets, 1, sensor_state); } /* configure-connector work area offsets, int32_t units for field @@ -481,6 +508,13 @@ static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr, #define CC_VAL_DATA_OFFSET ((CC_IDX_PROP_DATA_OFFSET + 1) * 4) #define CC_WA_LEN 4096 +static void configure_connector_st(target_ulong addr, target_ulong offset, + const void *buf, size_t len) +{ + cpu_physical_memory_write(ppc64_phys_to_real(addr + offset), + buf, MIN(len, CC_WA_LEN - offset)); +} + static void rtas_ibm_configure_connector(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t token, uint32_t nargs, @@ -515,6 +549,12 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu, drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); fdt = drck->get_fdt(drc, NULL); + if (!fdt) { + DPRINTF("rtas_ibm_configure_connector: Missing FDT for DRC index: %xh\n", + drc_index); + rc = SPAPR_DR_CC_RESPONSE_NOT_CONFIGURABLE; + goto out; + } ccs = spapr_ccs_find(spapr, drc_index); if (!ccs) { @@ -540,8 +580,7 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu, /* provide the name of the next OF node */ wa_offset = CC_VAL_DATA_OFFSET; rtas_st(wa_addr, CC_IDX_NODE_NAME_OFFSET, wa_offset); - rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset, - (uint8_t *)name, strlen(name) + 1); + configure_connector_st(wa_addr, wa_offset, name, strlen(name) + 1); resp = SPAPR_DR_CC_RESPONSE_NEXT_CHILD; break; case FDT_END_NODE: @@ -566,8 +605,7 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu, /* provide the name of the next OF property */ wa_offset = CC_VAL_DATA_OFFSET; rtas_st(wa_addr, CC_IDX_PROP_NAME_OFFSET, wa_offset); - rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset, - (uint8_t *)name, strlen(name) + 1); + configure_connector_st(wa_addr, wa_offset, name, strlen(name) + 1); /* provide the length and value of the OF property. data gets * placed immediately after NULL terminator of the OF property's @@ -576,9 +614,7 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu, wa_offset += strlen(name) + 1, rtas_st(wa_addr, CC_IDX_PROP_LEN, prop_len); rtas_st(wa_addr, CC_IDX_PROP_DATA_OFFSET, wa_offset); - rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset, - (uint8_t *)((struct fdt_property *)prop)->data, - prop_len); + configure_connector_st(wa_addr, wa_offset, prop->data, prop_len); resp = SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY; break; case FDT_END: @@ -631,17 +667,11 @@ target_ulong spapr_rtas_call(PowerPCCPU *cpu, sPAPRMachineState *spapr, void spapr_rtas_register(int token, const char *name, spapr_rtas_fn fn) { - if (!((token >= RTAS_TOKEN_BASE) && (token < RTAS_TOKEN_MAX))) { - fprintf(stderr, "RTAS invalid token 0x%x\n", token); - exit(1); - } + assert((token >= RTAS_TOKEN_BASE) && (token < RTAS_TOKEN_MAX)); token -= RTAS_TOKEN_BASE; - if (rtas_table[token].name) { - fprintf(stderr, "RTAS call \"%s\" is registered already as 0x%x\n", - rtas_table[token].name, token); - exit(1); - } + + assert(!rtas_table[token].name); rtas_table[token].name = name; rtas_table[token].fn = fn; @@ -654,6 +684,9 @@ int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr, int i; uint32_t lrdr_capacity[5]; MachineState *machine = MACHINE(qdev_get_machine()); + sPAPRMachineState *spapr = SPAPR_MACHINE(machine); + uint64_t max_hotplug_addr = spapr->hotplug_memory.base + + memory_region_size(&spapr->hotplug_memory.mr); ret = fdt_add_mem_rsv(fdt, rtas_addr, rtas_size); if (ret < 0) { @@ -703,8 +736,8 @@ int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr, } - lrdr_capacity[0] = cpu_to_be32(((uint64_t)machine->maxram_size) >> 32); - lrdr_capacity[1] = cpu_to_be32(machine->maxram_size & 0xffffffff); + lrdr_capacity[0] = cpu_to_be32(max_hotplug_addr >> 32); + lrdr_capacity[1] = cpu_to_be32(max_hotplug_addr & 0xffffffff); lrdr_capacity[2] = 0; lrdr_capacity[3] = cpu_to_be32(SPAPR_MEMORY_BLOCK_SIZE); lrdr_capacity[4] = cpu_to_be32(max_cpus/smp_threads); diff --git a/qemu/hw/ppc/spapr_rtc.c b/qemu/hw/ppc/spapr_rtc.c index 34b27db70..3a17ac42e 100644 --- a/qemu/hw/ppc/spapr_rtc.c +++ b/qemu/hw/ppc/spapr_rtc.c @@ -25,11 +25,13 @@ * THE SOFTWARE. * */ +#include "qemu/osdep.h" #include "cpu.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "hw/ppc/spapr.h" #include "qapi-event.h" +#include "qemu/cutils.h" #define SPAPR_RTC(obj) \ OBJECT_CHECK(sPAPRRTCState, (obj), TYPE_SPAPR_RTC) @@ -200,7 +202,6 @@ static const TypeInfo spapr_rtc_info = { .name = TYPE_SPAPR_RTC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(sPAPRRTCState), - .class_size = sizeof(XICSStateClass), .class_init = spapr_rtc_class_init, }; diff --git a/qemu/hw/ppc/spapr_vio.c b/qemu/hw/ppc/spapr_vio.c index c51eb8e24..8aa021fde 100644 --- a/qemu/hw/ppc/spapr_vio.c +++ b/qemu/hw/ppc/spapr_vio.c @@ -19,6 +19,8 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "sysemu/sysemu.h" #include "hw/boards.h" @@ -388,7 +390,7 @@ static void rtas_quiesce(PowerPCCPU *cpu, sPAPRMachineState *spapr, static VIOsPAPRDevice *reg_conflict(VIOsPAPRDevice *dev) { - VIOsPAPRBus *bus = DO_UPCAST(VIOsPAPRBus, bus, dev->qdev.parent_bus); + VIOsPAPRBus *bus = SPAPR_VIO_BUS(dev->qdev.parent_bus); BusChild *kid; VIOsPAPRDevice *other; @@ -430,6 +432,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp) VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev; VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); char *id; + Error *local_err = NULL; if (dev->reg != -1) { /* @@ -449,7 +452,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp) } } else { /* Need to assign an address */ - VIOsPAPRBus *bus = DO_UPCAST(VIOsPAPRBus, bus, dev->qdev.parent_bus); + VIOsPAPRBus *bus = SPAPR_VIO_BUS(dev->qdev.parent_bus); do { dev->reg = bus->next_reg++; @@ -462,9 +465,9 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp) dev->qdev.id = id; } - dev->irq = xics_alloc(spapr->icp, 0, dev->irq, false); - if (!dev->irq) { - error_setg(errp, "can't allocate IRQ"); + dev->irq = xics_alloc(spapr->icp, 0, dev->irq, false, &local_err); + if (local_err) { + error_propagate(errp, local_err); return; } @@ -523,13 +526,12 @@ VIOsPAPRBus *spapr_vio_bus_init(void) DeviceState *dev; /* Create bridge device */ - dev = qdev_create(NULL, "spapr-vio-bridge"); + dev = qdev_create(NULL, TYPE_SPAPR_VIO_BRIDGE); qdev_init_nofail(dev); /* Create bus on bridge device */ - qbus = qbus_create(TYPE_SPAPR_VIO_BUS, dev, "spapr-vio"); - bus = DO_UPCAST(VIOsPAPRBus, bus, qbus); + bus = SPAPR_VIO_BUS(qbus); bus->next_reg = 0x71000000; /* hcall-vio */ @@ -567,9 +569,8 @@ static void spapr_vio_bridge_class_init(ObjectClass *klass, void *data) } static const TypeInfo spapr_vio_bridge_info = { - .name = "spapr-vio-bridge", + .name = TYPE_SPAPR_VIO_BRIDGE, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SysBusDevice), .class_init = spapr_vio_bridge_class_init, }; diff --git a/qemu/hw/ppc/virtex_ml507.c b/qemu/hw/ppc/virtex_ml507.c index de86f7c64..b807a08c2 100644 --- a/qemu/hw/ppc/virtex_ml507.c +++ b/qemu/hw/ppc/virtex_ml507.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/hw.h" #include "hw/char/serial.h" @@ -257,7 +258,8 @@ static void virtex_init(MachineState *machine) /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, NULL, NULL, - &entry, &low, &high, 1, ELF_MACHINE, 0); + &entry, &low, &high, 1, PPC_ELF_MACHINE, + 0, 0); boot_info.bootstrap_pc = entry & 0x00ffffff; if (kernel_size < 0) { @@ -297,15 +299,10 @@ static void virtex_init(MachineState *machine) env->load_info = &boot_info; } -static QEMUMachine virtex_machine = { - .name = "virtex-ml507", - .desc = "Xilinx Virtex ML507 reference design", - .init = virtex_init, -}; - -static void virtex_machine_init(void) +static void virtex_machine_init(MachineClass *mc) { - qemu_register_machine(&virtex_machine); + mc->desc = "Xilinx Virtex ML507 reference design"; + mc->init = virtex_init; } -machine_init(virtex_machine_init); +DEFINE_MACHINE("virtex-ml507", virtex_machine_init) diff --git a/qemu/hw/s390x/Makefile.objs b/qemu/hw/s390x/Makefile.objs index 27cd75a93..220361782 100644 --- a/qemu/hw/s390x/Makefile.objs +++ b/qemu/hw/s390x/Makefile.objs @@ -1,4 +1,4 @@ -obj-y = s390-virtio-bus.o s390-virtio.o +obj-y += s390-virtio.o obj-y += s390-virtio-hcall.o obj-y += sclp.o obj-y += event-facility.o @@ -9,3 +9,5 @@ obj-y += css.o obj-y += s390-virtio-ccw.o obj-y += virtio-ccw.o obj-y += s390-pci-bus.o s390-pci-inst.o +obj-y += s390-skeys.o +obj-$(CONFIG_KVM) += s390-skeys-kvm.o diff --git a/qemu/hw/s390x/css.c b/qemu/hw/s390x/css.c index 5df450e00..3a1d91958 100644 --- a/qemu/hw/s390x/css.c +++ b/qemu/hw/s390x/css.c @@ -9,6 +9,7 @@ * directory. */ +#include "qemu/osdep.h" #include #include "qemu/bitops.h" #include "exec/address-spaces.h" @@ -49,6 +50,7 @@ typedef struct IoAdapter { typedef struct ChannelSubSys { QTAILQ_HEAD(, CrwContainer) pending_crws; + bool sei_pending; bool do_crw_mchk; bool crws_lost; uint8_t max_cssid; @@ -58,9 +60,81 @@ typedef struct ChannelSubSys { CssImage *css[MAX_CSSID + 1]; uint8_t default_cssid; QTAILQ_HEAD(, IoAdapter) io_adapters; + QTAILQ_HEAD(, IndAddr) indicator_addresses; } ChannelSubSys; -static ChannelSubSys *channel_subsys; +static ChannelSubSys channel_subsys = { + .pending_crws = QTAILQ_HEAD_INITIALIZER(channel_subsys.pending_crws), + .do_crw_mchk = true, + .sei_pending = false, + .do_crw_mchk = true, + .crws_lost = false, + .chnmon_active = false, + .io_adapters = QTAILQ_HEAD_INITIALIZER(channel_subsys.io_adapters), + .indicator_addresses = + QTAILQ_HEAD_INITIALIZER(channel_subsys.indicator_addresses), +}; + +IndAddr *get_indicator(hwaddr ind_addr, int len) +{ + IndAddr *indicator; + + QTAILQ_FOREACH(indicator, &channel_subsys.indicator_addresses, sibling) { + if (indicator->addr == ind_addr) { + indicator->refcnt++; + return indicator; + } + } + indicator = g_new0(IndAddr, 1); + indicator->addr = ind_addr; + indicator->len = len; + indicator->refcnt = 1; + QTAILQ_INSERT_TAIL(&channel_subsys.indicator_addresses, + indicator, sibling); + return indicator; +} + +static int s390_io_adapter_map(AdapterInfo *adapter, uint64_t map_addr, + bool do_map) +{ + S390FLICState *fs = s390_get_flic(); + S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); + + return fsc->io_adapter_map(fs, adapter->adapter_id, map_addr, do_map); +} + +void release_indicator(AdapterInfo *adapter, IndAddr *indicator) +{ + assert(indicator->refcnt > 0); + indicator->refcnt--; + if (indicator->refcnt > 0) { + return; + } + QTAILQ_REMOVE(&channel_subsys.indicator_addresses, indicator, sibling); + if (indicator->map) { + s390_io_adapter_map(adapter, indicator->map, false); + } + g_free(indicator); +} + +int map_indicator(AdapterInfo *adapter, IndAddr *indicator) +{ + int ret; + + if (indicator->map) { + return 0; /* already mapped is not an error */ + } + indicator->map = indicator->addr; + ret = s390_io_adapter_map(adapter, indicator->map, true); + if ((ret != 0) && (ret != -ENOSYS)) { + goto out_err; + } + return 0; + +out_err: + indicator->map = 0; + return ret; +} int css_create_css_image(uint8_t cssid, bool default_image) { @@ -68,12 +142,12 @@ int css_create_css_image(uint8_t cssid, bool default_image) if (cssid > MAX_CSSID) { return -EINVAL; } - if (channel_subsys->css[cssid]) { + if (channel_subsys.css[cssid]) { return -EBUSY; } - channel_subsys->css[cssid] = g_malloc0(sizeof(CssImage)); + channel_subsys.css[cssid] = g_malloc0(sizeof(CssImage)); if (default_image) { - channel_subsys->default_cssid = cssid; + channel_subsys.default_cssid = cssid; } return 0; } @@ -88,7 +162,7 @@ int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap, S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); *id = 0; - QTAILQ_FOREACH(adapter, &channel_subsys->io_adapters, sibling) { + QTAILQ_FOREACH(adapter, &channel_subsys.io_adapters, sibling) { if ((adapter->type == type) && (adapter->isc == isc)) { *id = adapter->id; found = true; @@ -108,7 +182,7 @@ int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap, adapter->id = *id; adapter->isc = isc; adapter->type = type; - QTAILQ_INSERT_TAIL(&channel_subsys->io_adapters, adapter, sibling); + QTAILQ_INSERT_TAIL(&channel_subsys.io_adapters, adapter, sibling); } else { g_free(adapter); fprintf(stderr, "Unexpected error %d when registering adapter %d\n", @@ -120,7 +194,7 @@ out: uint16_t css_build_subchannel_id(SubchDev *sch) { - if (channel_subsys->max_cssid > 0) { + if (channel_subsys.max_cssid > 0) { return (sch->cssid << 8) | (1 << 3) | (sch->ssid << 1) | 1; } return (sch->ssid << 1) | 1; @@ -261,11 +335,15 @@ static CCW1 copy_ccw_from_guest(hwaddr addr, bool fmt1) ret.flags = tmp0.flags; ret.count = be16_to_cpu(tmp0.count); ret.cda = be16_to_cpu(tmp0.cda1) | (tmp0.cda0 << 16); + if ((ret.cmd_code & 0x0f) == CCW_CMD_TIC) { + ret.cmd_code &= 0x0f; + } } return ret; } -static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr) +static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr, + bool suspend_allowed) { int ret; bool check_len; @@ -287,9 +365,13 @@ static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr) ((ccw.cmd_code & 0xf0) != 0)) { return -EINVAL; } + if (!sch->ccw_fmt_1 && (ccw.count == 0) && + (ccw.cmd_code != CCW_CMD_TIC)) { + return -EINVAL; + } if (ccw.flags & CCW_FLAG_SUSPEND) { - return -EINPROGRESS; + return suspend_allowed ? -EINPROGRESS : -EINVAL; } check_len = !((ccw.flags & CCW_FLAG_SLI) && !(ccw.flags & CCW_FLAG_DC)); @@ -387,11 +469,14 @@ static void sch_handle_start_func(SubchDev *sch, ORB *orb) SCSW *s = &sch->curr_status.scsw; int path; int ret; + bool suspend_allowed; /* Path management: In our simple css, we always choose the only path. */ path = 0x80; if (!(s->ctrl & SCSW_ACTL_SUSP)) { + s->cstat = 0; + s->dstat = 0; /* Look at the orb and try to execute the channel program. */ assert(orb != NULL); /* resume does not pass an orb */ p->intparm = orb->intparm; @@ -404,12 +489,15 @@ static void sch_handle_start_func(SubchDev *sch, ORB *orb) } sch->ccw_fmt_1 = !!(orb->ctrl0 & ORB_CTRL0_MASK_FMT); sch->ccw_no_data_cnt = 0; + suspend_allowed = !!(orb->ctrl0 & ORB_CTRL0_MASK_SPND); } else { s->ctrl &= ~(SCSW_ACTL_SUSP | SCSW_ACTL_RESUME_PEND); + /* The channel program had been suspended before. */ + suspend_allowed = true; } sch->last_cmd_valid = false; do { - ret = css_interpret_ccw(sch, sch->channel_prog); + ret = css_interpret_ccw(sch, sch->channel_prog, suspend_allowed); switch (ret) { case -EAGAIN: /* ccw chain, continue processing */ @@ -692,7 +780,7 @@ int css_do_csch(SubchDev *sch) /* Trigger the clear function. */ s->ctrl &= ~(SCSW_CTRL_MASK_FCTL | SCSW_CTRL_MASK_ACTL); - s->ctrl |= SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_CLEAR_FUNC; + s->ctrl |= SCSW_FCTL_CLEAR_FUNC | SCSW_ACTL_CLEAR_PEND; do_subchannel_work(sch, NULL); ret = 0; @@ -767,12 +855,12 @@ static void css_update_chnmon(SubchDev *sch) offset = sch->curr_status.pmcw.mbi << 5; count = address_space_lduw(&address_space_memory, - channel_subsys->chnmon_area + offset, + channel_subsys.chnmon_area + offset, MEMTXATTRS_UNSPECIFIED, NULL); count++; address_space_stw(&address_space_memory, - channel_subsys->chnmon_area + offset, count, + channel_subsys.chnmon_area + offset, count, MEMTXATTRS_UNSPECIFIED, NULL); } } @@ -801,7 +889,7 @@ int css_do_ssch(SubchDev *sch, ORB *orb) } /* If monitoring is active, update counter. */ - if (channel_subsys->chnmon_active) { + if (channel_subsys.chnmon_active) { css_update_chnmon(sch); } sch->channel_prog = orb->cpa; @@ -883,8 +971,14 @@ int css_do_tsch_get_irb(SubchDev *sch, IRB *target_irb, int *irb_len) /* If a unit check is pending, copy sense data. */ if ((s->dstat & SCSW_DSTAT_UNIT_CHECK) && (p->chars & PMCW_CHARS_MASK_CSENSE)) { + int i; + irb.scsw.flags |= SCSW_FLAGS_MASK_ESWF | SCSW_FLAGS_MASK_ECTL; + /* Attention: sense_data is already BE! */ memcpy(irb.ecw, sch->sense_data, sizeof(sch->sense_data)); + for (i = 0; i < ARRAY_SIZE(irb.ecw); i++) { + irb.ecw[i] = be32_to_cpu(irb.ecw[i]); + } irb.esw[1] = 0x01000000 | (sizeof(sch->sense_data) << 8); } } @@ -954,16 +1048,16 @@ int css_do_stcrw(CRW *crw) CrwContainer *crw_cont; int ret; - crw_cont = QTAILQ_FIRST(&channel_subsys->pending_crws); + crw_cont = QTAILQ_FIRST(&channel_subsys.pending_crws); if (crw_cont) { - QTAILQ_REMOVE(&channel_subsys->pending_crws, crw_cont, sibling); + QTAILQ_REMOVE(&channel_subsys.pending_crws, crw_cont, sibling); copy_crw_to_guest(crw, &crw_cont->crw); g_free(crw_cont); ret = 0; } else { /* List was empty, turn crw machine checks on again. */ memset(crw, 0, sizeof(*crw)); - channel_subsys->do_crw_mchk = true; + channel_subsys.do_crw_mchk = true; ret = 1; } @@ -982,12 +1076,12 @@ void css_undo_stcrw(CRW *crw) crw_cont = g_try_malloc0(sizeof(CrwContainer)); if (!crw_cont) { - channel_subsys->crws_lost = true; + channel_subsys.crws_lost = true; return; } copy_crw_from_guest(&crw_cont->crw, crw); - QTAILQ_INSERT_HEAD(&channel_subsys->pending_crws, crw_cont, sibling); + QTAILQ_INSERT_HEAD(&channel_subsys.pending_crws, crw_cont, sibling); } int css_do_tpi(IOIntCode *int_code, int lowcore) @@ -1005,9 +1099,9 @@ int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid, uint8_t l_chpid, CssImage *css; if (!m && !cssid) { - css = channel_subsys->css[channel_subsys->default_cssid]; + css = channel_subsys.css[channel_subsys.default_cssid]; } else { - css = channel_subsys->css[cssid]; + css = channel_subsys.css[cssid]; } if (!css) { return 0; @@ -1042,15 +1136,15 @@ void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo) { /* dct is currently ignored (not really meaningful for our devices) */ /* TODO: Don't ignore mbk. */ - if (update && !channel_subsys->chnmon_active) { + if (update && !channel_subsys.chnmon_active) { /* Enable measuring. */ - channel_subsys->chnmon_area = mbo; - channel_subsys->chnmon_active = true; + channel_subsys.chnmon_area = mbo; + channel_subsys.chnmon_active = true; } - if (!update && channel_subsys->chnmon_active) { + if (!update && channel_subsys.chnmon_active) { /* Disable measuring. */ - channel_subsys->chnmon_area = 0; - channel_subsys->chnmon_active = false; + channel_subsys.chnmon_area = 0; + channel_subsys.chnmon_active = false; } } @@ -1078,7 +1172,7 @@ int css_do_rsch(SubchDev *sch) } /* If monitoring is active, update counter. */ - if (channel_subsys->chnmon_active) { + if (channel_subsys.chnmon_active) { css_update_chnmon(sch); } @@ -1094,23 +1188,23 @@ int css_do_rchp(uint8_t cssid, uint8_t chpid) { uint8_t real_cssid; - if (cssid > channel_subsys->max_cssid) { + if (cssid > channel_subsys.max_cssid) { return -EINVAL; } - if (channel_subsys->max_cssid == 0) { - real_cssid = channel_subsys->default_cssid; + if (channel_subsys.max_cssid == 0) { + real_cssid = channel_subsys.default_cssid; } else { real_cssid = cssid; } - if (!channel_subsys->css[real_cssid]) { + if (!channel_subsys.css[real_cssid]) { return -EINVAL; } - if (!channel_subsys->css[real_cssid]->chpids[chpid].in_use) { + if (!channel_subsys.css[real_cssid]->chpids[chpid].in_use) { return -ENODEV; } - if (!channel_subsys->css[real_cssid]->chpids[chpid].is_virtual) { + if (!channel_subsys.css[real_cssid]->chpids[chpid].is_virtual) { fprintf(stderr, "rchp unsupported for non-virtual chpid %x.%02x!\n", real_cssid, chpid); @@ -1119,8 +1213,8 @@ int css_do_rchp(uint8_t cssid, uint8_t chpid) /* We don't really use a channel path, so we're done here. */ css_queue_crw(CRW_RSC_CHP, CRW_ERC_INIT, - channel_subsys->max_cssid > 0 ? 1 : 0, chpid); - if (channel_subsys->max_cssid > 0) { + channel_subsys.max_cssid > 0 ? 1 : 0, chpid); + if (channel_subsys.max_cssid > 0) { css_queue_crw(CRW_RSC_CHP, CRW_ERC_INIT, 0, real_cssid << 8); } return 0; @@ -1131,13 +1225,13 @@ bool css_schid_final(int m, uint8_t cssid, uint8_t ssid, uint16_t schid) SubchSet *set; uint8_t real_cssid; - real_cssid = (!m && (cssid == 0)) ? channel_subsys->default_cssid : cssid; + real_cssid = (!m && (cssid == 0)) ? channel_subsys.default_cssid : cssid; if (real_cssid > MAX_CSSID || ssid > MAX_SSID || - !channel_subsys->css[real_cssid] || - !channel_subsys->css[real_cssid]->sch_set[ssid]) { + !channel_subsys.css[real_cssid] || + !channel_subsys.css[real_cssid]->sch_set[ssid]) { return true; } - set = channel_subsys->css[real_cssid]->sch_set[ssid]; + set = channel_subsys.css[real_cssid]->sch_set[ssid]; return schid > find_last_bit(set->schids_used, (MAX_SCHID + 1) / sizeof(unsigned long)); } @@ -1150,7 +1244,7 @@ static int css_add_virtual_chpid(uint8_t cssid, uint8_t chpid, uint8_t type) if (cssid > MAX_CSSID) { return -EINVAL; } - css = channel_subsys->css[cssid]; + css = channel_subsys.css[cssid]; if (!css) { return -EINVAL; } @@ -1171,7 +1265,7 @@ void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type) PMCW *p = &sch->curr_status.pmcw; SCSW *s = &sch->curr_status.scsw; int i; - CssImage *css = channel_subsys->css[sch->cssid]; + CssImage *css = channel_subsys.css[sch->cssid]; assert(css != NULL); memset(p, 0, sizeof(PMCW)); @@ -1197,27 +1291,27 @@ SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid) { uint8_t real_cssid; - real_cssid = (!m && (cssid == 0)) ? channel_subsys->default_cssid : cssid; + real_cssid = (!m && (cssid == 0)) ? channel_subsys.default_cssid : cssid; - if (!channel_subsys->css[real_cssid]) { + if (!channel_subsys.css[real_cssid]) { return NULL; } - if (!channel_subsys->css[real_cssid]->sch_set[ssid]) { + if (!channel_subsys.css[real_cssid]->sch_set[ssid]) { return NULL; } - return channel_subsys->css[real_cssid]->sch_set[ssid]->sch[schid]; + return channel_subsys.css[real_cssid]->sch_set[ssid]->sch[schid]; } bool css_subch_visible(SubchDev *sch) { - if (sch->ssid > channel_subsys->max_ssid) { + if (sch->ssid > channel_subsys.max_ssid) { return false; } - if (sch->cssid != channel_subsys->default_cssid) { - return (channel_subsys->max_cssid > 0); + if (sch->cssid != channel_subsys.default_cssid) { + return (channel_subsys.max_cssid > 0); } return true; @@ -1225,20 +1319,20 @@ bool css_subch_visible(SubchDev *sch) bool css_present(uint8_t cssid) { - return (channel_subsys->css[cssid] != NULL); + return (channel_subsys.css[cssid] != NULL); } bool css_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno) { - if (!channel_subsys->css[cssid]) { + if (!channel_subsys.css[cssid]) { return false; } - if (!channel_subsys->css[cssid]->sch_set[ssid]) { + if (!channel_subsys.css[cssid]->sch_set[ssid]) { return false; } return !!test_bit(devno, - channel_subsys->css[cssid]->sch_set[ssid]->devnos_used); + channel_subsys.css[cssid]->sch_set[ssid]->devnos_used); } void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, @@ -1249,13 +1343,13 @@ void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, trace_css_assign_subch(sch ? "assign" : "deassign", cssid, ssid, schid, devno); - if (!channel_subsys->css[cssid]) { + if (!channel_subsys.css[cssid]) { fprintf(stderr, "Suspicious call to %s (%x.%x.%04x) for non-existing css!\n", __func__, cssid, ssid, schid); return; } - css = channel_subsys->css[cssid]; + css = channel_subsys.css[cssid]; if (!css->sch_set[ssid]) { css->sch_set[ssid] = g_malloc0(sizeof(SubchSet)); @@ -1280,7 +1374,7 @@ void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid) /* TODO: Maybe use a static crw pool? */ crw_cont = g_try_malloc0(sizeof(CrwContainer)); if (!crw_cont) { - channel_subsys->crws_lost = true; + channel_subsys.crws_lost = true; return; } crw_cont->crw.flags = (rsc << 8) | erc; @@ -1288,15 +1382,15 @@ void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid) crw_cont->crw.flags |= CRW_FLAGS_MASK_C; } crw_cont->crw.rsid = rsid; - if (channel_subsys->crws_lost) { + if (channel_subsys.crws_lost) { crw_cont->crw.flags |= CRW_FLAGS_MASK_R; - channel_subsys->crws_lost = false; + channel_subsys.crws_lost = false; } - QTAILQ_INSERT_TAIL(&channel_subsys->pending_crws, crw_cont, sibling); + QTAILQ_INSERT_TAIL(&channel_subsys.pending_crws, crw_cont, sibling); - if (channel_subsys->do_crw_mchk) { - channel_subsys->do_crw_mchk = false; + if (channel_subsys.do_crw_mchk) { + channel_subsys.do_crw_mchk = false; /* Inject crw pending machine check. */ s390_crw_mchk(); } @@ -1311,9 +1405,9 @@ void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, if (add && !hotplugged) { return; } - if (channel_subsys->max_cssid == 0) { + if (channel_subsys.max_cssid == 0) { /* Default cssid shows up as 0. */ - guest_cssid = (cssid == channel_subsys->default_cssid) ? 0 : cssid; + guest_cssid = (cssid == channel_subsys.default_cssid) ? 0 : cssid; } else { /* Show real cssid to the guest. */ guest_cssid = cssid; @@ -1322,14 +1416,14 @@ void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, * Only notify for higher subchannel sets/channel subsystems if the * guest has enabled it. */ - if ((ssid > channel_subsys->max_ssid) || - (guest_cssid > channel_subsys->max_cssid) || - ((channel_subsys->max_cssid == 0) && - (cssid != channel_subsys->default_cssid))) { + if ((ssid > channel_subsys.max_ssid) || + (guest_cssid > channel_subsys.max_cssid) || + ((channel_subsys.max_cssid == 0) && + (cssid != channel_subsys.default_cssid))) { return; } - chain_crw = (channel_subsys->max_ssid > 0) || - (channel_subsys->max_cssid > 0); + chain_crw = (channel_subsys.max_ssid > 0) || + (channel_subsys.max_cssid > 0); css_queue_crw(CRW_RSC_SUBCH, CRW_ERC_IPI, chain_crw ? 1 : 0, schid); if (chain_crw) { css_queue_crw(CRW_RSC_SUBCH, CRW_ERC_IPI, 0, @@ -1344,20 +1438,28 @@ void css_generate_chp_crws(uint8_t cssid, uint8_t chpid) void css_generate_css_crws(uint8_t cssid) { - css_queue_crw(CRW_RSC_CSS, 0, 0, cssid); + if (!channel_subsys.sei_pending) { + css_queue_crw(CRW_RSC_CSS, 0, 0, cssid); + } + channel_subsys.sei_pending = true; +} + +void css_clear_sei_pending(void) +{ + channel_subsys.sei_pending = false; } int css_enable_mcsse(void) { trace_css_enable_facility("mcsse"); - channel_subsys->max_cssid = MAX_CSSID; + channel_subsys.max_cssid = MAX_CSSID; return 0; } int css_enable_mss(void) { trace_css_enable_facility("mss"); - channel_subsys->max_ssid = MAX_SSID; + channel_subsys.max_ssid = MAX_SSID; return 0; } @@ -1415,7 +1517,6 @@ void subch_device_save(SubchDev *s, QEMUFile *f) } qemu_put_byte(f, s->ccw_fmt_1); qemu_put_byte(f, s->ccw_no_data_cnt); - return; } int subch_device_load(SubchDev *s, QEMUFile *f) @@ -1481,27 +1582,15 @@ int subch_device_load(SubchDev *s, QEMUFile *f) */ if (s->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA) { if (s->ssid) { - channel_subsys->max_ssid = MAX_SSID; + channel_subsys.max_ssid = MAX_SSID; } - if (s->cssid != channel_subsys->default_cssid) { - channel_subsys->max_cssid = MAX_CSSID; + if (s->cssid != channel_subsys.default_cssid) { + channel_subsys.max_cssid = MAX_CSSID; } } return 0; } - -static void css_init(void) -{ - channel_subsys = g_malloc0(sizeof(*channel_subsys)); - QTAILQ_INIT(&channel_subsys->pending_crws); - channel_subsys->do_crw_mchk = true; - channel_subsys->crws_lost = false; - channel_subsys->chnmon_active = false; - QTAILQ_INIT(&channel_subsys->io_adapters); -} -machine_init(css_init); - void css_reset_sch(SubchDev *sch) { PMCW *p = &sch->curr_status.pmcw; @@ -1539,18 +1628,19 @@ void css_reset(void) CrwContainer *crw_cont; /* Clean up monitoring. */ - channel_subsys->chnmon_active = false; - channel_subsys->chnmon_area = 0; + channel_subsys.chnmon_active = false; + channel_subsys.chnmon_area = 0; /* Clear pending CRWs. */ - while ((crw_cont = QTAILQ_FIRST(&channel_subsys->pending_crws))) { - QTAILQ_REMOVE(&channel_subsys->pending_crws, crw_cont, sibling); + while ((crw_cont = QTAILQ_FIRST(&channel_subsys.pending_crws))) { + QTAILQ_REMOVE(&channel_subsys.pending_crws, crw_cont, sibling); g_free(crw_cont); } - channel_subsys->do_crw_mchk = true; - channel_subsys->crws_lost = false; + channel_subsys.sei_pending = false; + channel_subsys.do_crw_mchk = true; + channel_subsys.crws_lost = false; /* Reset maximum ids. */ - channel_subsys->max_cssid = 0; - channel_subsys->max_ssid = 0; + channel_subsys.max_cssid = 0; + channel_subsys.max_ssid = 0; } diff --git a/qemu/hw/s390x/css.h b/qemu/hw/s390x/css.h index a09bb1f87..a320eea59 100644 --- a/qemu/hw/s390x/css.h +++ b/qemu/hw/s390x/css.h @@ -12,6 +12,8 @@ #ifndef CSS_H #define CSS_H +#include "hw/s390x/adapter.h" +#include "hw/s390x/s390_flic.h" #include "ioinst.h" /* Channel subsystem constants. */ @@ -86,6 +88,18 @@ struct SubchDev { void *driver_data; }; +typedef struct IndAddr { + hwaddr addr; + uint64_t map; + unsigned long refcnt; + int len; + QTAILQ_ENTRY(IndAddr) sibling; +} IndAddr; + +IndAddr *get_indicator(hwaddr ind_addr, int len); +void release_indicator(AdapterInfo *adapter, IndAddr *indicator); +int map_indicator(AdapterInfo *adapter, IndAddr *indicator); + typedef SubchDev *(*css_subch_cb_func)(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid); void subch_device_save(SubchDev *s, QEMUFile *f); @@ -103,6 +117,7 @@ void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, int hotplugged, int add); void css_generate_chp_crws(uint8_t cssid, uint8_t chpid); void css_generate_css_crws(uint8_t cssid); +void css_clear_sei_pending(void); void css_adapter_interrupt(uint8_t isc); #define CSS_IO_ADAPTER_VIRTIO 1 diff --git a/qemu/hw/s390x/event-facility.c b/qemu/hw/s390x/event-facility.c index 0c700effb..34b2faf01 100644 --- a/qemu/hw/s390x/event-facility.c +++ b/qemu/hw/s390x/event-facility.c @@ -15,6 +15,8 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "sysemu/sysemu.h" #include "hw/s390x/sclp.h" @@ -31,8 +33,6 @@ struct SCLPEventFacility { unsigned int receive_mask; }; -static SCLPEvent cpu_hotplug; - /* return true if any child has event pending set */ static bool event_pending(SCLPEventFacility *ef) { @@ -240,12 +240,13 @@ static void read_event_data(SCLPEventFacility *ef, SCCB *sccb) sclp_active_selection_mask = sclp_cp_receive_mask; break; case SCLP_SELECTIVE_READ: - if (!(sclp_cp_receive_mask & be32_to_cpu(red->mask))) { + sclp_active_selection_mask = be32_to_cpu(red->mask); + if (!sclp_cp_receive_mask || + (sclp_active_selection_mask & ~sclp_cp_receive_mask)) { sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SELECTION_MASK); goto out; } - sclp_active_selection_mask = be32_to_cpu(red->mask); break; default: sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_FUNCTION); @@ -286,8 +287,26 @@ out: #define TYPE_SCLP_EVENTS_BUS "s390-sclp-events-bus" +static void sclp_events_bus_realize(BusState *bus, Error **errp) +{ + BusChild *kid; + + /* TODO: recursive realization has to be done in common code */ + QTAILQ_FOREACH(kid, &bus->children, sibling) { + DeviceState *dev = kid->child; + + object_property_set_bool(OBJECT(dev), true, "realized", errp); + if (*errp) { + return; + } + } +} + static void sclp_events_bus_class_init(ObjectClass *klass, void *data) { + BusClass *bc = BUS_CLASS(klass); + + bc->realize = sclp_events_bus_realize; } static const TypeInfo sclp_events_bus_info = { @@ -324,26 +343,26 @@ static const VMStateDescription vmstate_event_facility = { } }; -static int init_event_facility(SCLPEventFacility *event_facility) +static void init_event_facility(Object *obj) { - DeviceState *sdev = DEVICE(event_facility); - DeviceState *quiesce; + SCLPEventFacility *event_facility = EVENT_FACILITY(obj); + DeviceState *sdev = DEVICE(obj); + Object *new; /* Spawn a new bus for SCLP events */ qbus_create_inplace(&event_facility->sbus, sizeof(event_facility->sbus), TYPE_SCLP_EVENTS_BUS, sdev, NULL); - quiesce = qdev_create(&event_facility->sbus.qbus, "sclpquiesce"); - if (!quiesce) { - return -1; - } - qdev_init_nofail(quiesce); - - object_initialize(&cpu_hotplug, sizeof(cpu_hotplug), TYPE_SCLP_CPU_HOTPLUG); - qdev_set_parent_bus(DEVICE(&cpu_hotplug), BUS(&event_facility->sbus)); - object_property_set_bool(OBJECT(&cpu_hotplug), true, "realized", NULL); + new = object_new(TYPE_SCLP_QUIESCE); + object_property_add_child(obj, TYPE_SCLP_QUIESCE, new, NULL); + object_unref(new); + qdev_set_parent_bus(DEVICE(new), &event_facility->sbus.qbus); - return 0; + new = object_new(TYPE_SCLP_CPU_HOTPLUG); + object_property_add_child(obj, TYPE_SCLP_CPU_HOTPLUG, new, NULL); + object_unref(new); + qdev_set_parent_bus(DEVICE(new), &event_facility->sbus.qbus); + /* the facility will automatically realize the devices via the bus */ } static void reset_event_facility(DeviceState *dev) @@ -362,7 +381,6 @@ static void init_event_facility_class(ObjectClass *klass, void *data) dc->reset = reset_event_facility; dc->vmsd = &vmstate_event_facility; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - k->init = init_event_facility; k->command_handler = command_handler; k->event_pending = event_pending; } @@ -370,6 +388,7 @@ static void init_event_facility_class(ObjectClass *klass, void *data) static const TypeInfo sclp_event_facility_info = { .name = TYPE_SCLP_EVENT_FACILITY, .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = init_event_facility, .instance_size = sizeof(SCLPEventFacility), .class_init = init_event_facility_class, .class_size = sizeof(SCLPEventFacilityClass), diff --git a/qemu/hw/s390x/ipl.c b/qemu/hw/s390x/ipl.c index 2e0a8b6e0..f10420027 100644 --- a/qemu/hw/s390x/ipl.c +++ b/qemu/hw/s390x/ipl.c @@ -11,11 +11,12 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "sysemu/sysemu.h" #include "cpu.h" #include "elf.h" #include "hw/loader.h" -#include "hw/sysbus.h" #include "hw/s390x/virtio-ccw.h" #include "hw/s390x/css.h" #include "ipl.h" @@ -29,44 +30,6 @@ #define ZIPL_IMAGE_START 0x009000UL #define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) -#define TYPE_S390_IPL "s390-ipl" -#define S390_IPL(obj) \ - OBJECT_CHECK(S390IPLState, (obj), TYPE_S390_IPL) -#if 0 -#define S390_IPL_CLASS(klass) \ - OBJECT_CLASS_CHECK(S390IPLState, (klass), TYPE_S390_IPL) -#define S390_IPL_GET_CLASS(obj) \ - OBJECT_GET_CLASS(S390IPLState, (obj), TYPE_S390_IPL) -#endif - -typedef struct S390IPLClass { - /*< private >*/ - SysBusDeviceClass parent_class; - /*< public >*/ - - void (*parent_reset) (SysBusDevice *dev); -} S390IPLClass; - -typedef struct S390IPLState { - /*< private >*/ - SysBusDevice parent_obj; - uint64_t start_addr; - uint64_t bios_start_addr; - bool enforce_bios; - IplParameterBlock iplb; - bool iplb_valid; - bool reipl_requested; - - /*< public >*/ - char *kernel; - char *initrd; - char *cmdline; - char *firmware; - uint8_t cssid; - uint8_t ssid; - uint16_t devno; -} S390IPLState; - static const VMStateDescription vmstate_iplb = { .name = "ipl/iplb", .version_id = 0, @@ -95,6 +58,11 @@ static const VMStateDescription vmstate_ipl = { } }; +static S390IPLState *get_ipl_device(void) +{ + return S390_IPL(object_resolve_path_type("", TYPE_S390_IPL, NULL)); +} + static uint64_t bios_translate_addr(void *opaque, uint64_t srcaddr) { uint64_t dstaddr = *(uint64_t *) opaque; @@ -105,11 +73,12 @@ static uint64_t bios_translate_addr(void *opaque, uint64_t srcaddr) return srcaddr + dstaddr; } -static int s390_ipl_init(SysBusDevice *dev) +static void s390_ipl_realize(DeviceState *dev, Error **errp) { S390IPLState *ipl = S390_IPL(dev); uint64_t pentry = KERN_IMAGE_START; int kernel_size; + Error *err = NULL; int bios_size; char *bios_filename; @@ -127,17 +96,18 @@ static int s390_ipl_init(SysBusDevice *dev) bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (bios_filename == NULL) { - hw_error("could not find stage1 bootloader\n"); + error_setg(&err, "could not find stage1 bootloader"); + goto error; } bios_size = load_elf(bios_filename, bios_translate_addr, &fwbase, &ipl->bios_start_addr, NULL, NULL, 1, - ELF_MACHINE, 0); + EM_S390, 0, 0); if (bios_size > 0) { /* Adjust ELF start address to final location */ ipl->bios_start_addr += fwbase; } else { - /* Try to load non-ELF file (e.g. s390-zipl.rom) */ + /* Try to load non-ELF file (e.g. s390-ccw.img) */ bios_size = load_image_targphys(bios_filename, ZIPL_IMAGE_START, 4096); ipl->bios_start_addr = ZIPL_IMAGE_START; @@ -145,7 +115,8 @@ static int s390_ipl_init(SysBusDevice *dev) g_free(bios_filename); if (bios_size == -1) { - hw_error("could not load bootloader '%s'\n", bios_name); + error_setg(&err, "could not load bootloader '%s'", bios_name); + goto error; } /* default boot target is the bios */ @@ -154,13 +125,13 @@ static int s390_ipl_init(SysBusDevice *dev) if (ipl->kernel) { kernel_size = load_elf(ipl->kernel, NULL, NULL, &pentry, NULL, - NULL, 1, ELF_MACHINE, 0); + NULL, 1, EM_S390, 0, 0); if (kernel_size < 0) { kernel_size = load_image_targphys(ipl->kernel, 0, ram_size); } if (kernel_size < 0) { - fprintf(stderr, "could not load kernel '%s'\n", ipl->kernel); - return -1; + error_setg(&err, "could not load kernel '%s'", ipl->kernel); + goto error; } /* * Is it a Linux kernel (starting at 0x10000)? If yes, we fill in the @@ -187,9 +158,8 @@ static int s390_ipl_init(SysBusDevice *dev) initrd_size = load_image_targphys(ipl->initrd, initrd_offset, ram_size - initrd_offset); if (initrd_size == -1) { - fprintf(stderr, "qemu: could not load initrd '%s'\n", - ipl->initrd); - exit(1); + error_setg(&err, "could not load initrd '%s'", ipl->initrd); + goto error; } /* @@ -200,7 +170,9 @@ static int s390_ipl_init(SysBusDevice *dev) stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size); } } - return 0; + qemu_register_reset(qdev_reset_all_fn, dev); +error: + error_propagate(errp, err); } static Property s390_ipl_properties[] = { @@ -218,7 +190,7 @@ static Property s390_ipl_properties[] = { * - -1 if no valid boot device was found * - ccw id of the boot device otherwise */ -static uint64_t s390_update_iplstate(CPUS390XState *env, S390IPLState *ipl) +static uint64_t s390_update_iplstate(S390IPLState *ipl) { DeviceState *dev_st; @@ -251,25 +223,19 @@ out: return (uint32_t) (ipl->cssid << 24 | ipl->ssid << 16 | ipl->devno); } -int s390_ipl_update_diag308(IplParameterBlock *iplb) +void s390_ipl_update_diag308(IplParameterBlock *iplb) { - S390IPLState *ipl; + S390IPLState *ipl = get_ipl_device(); - ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL)); - if (ipl) { - ipl->iplb = *iplb; - ipl->iplb_valid = true; - return 0; - } - return -1; + ipl->iplb = *iplb; + ipl->iplb_valid = true; } IplParameterBlock *s390_ipl_get_iplb(void) { - S390IPLState *ipl; + S390IPLState *ipl = get_ipl_device(); - ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL)); - if (!ipl || !ipl->iplb_valid) { + if (!ipl->iplb_valid) { return NULL; } return &ipl->iplb; @@ -277,41 +243,40 @@ IplParameterBlock *s390_ipl_get_iplb(void) void s390_reipl_request(void) { - S390IPLState *ipl; + S390IPLState *ipl = get_ipl_device(); - ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL)); ipl->reipl_requested = true; qemu_system_reset_request(); } +void s390_ipl_prepare_cpu(S390CPU *cpu) +{ + S390IPLState *ipl = get_ipl_device(); + + cpu->env.psw.addr = ipl->start_addr; + cpu->env.psw.mask = IPL_PSW_MASK; + + if (!ipl->kernel || ipl->iplb_valid) { + cpu->env.psw.addr = ipl->bios_start_addr; + cpu->env.regs[7] = s390_update_iplstate(ipl); + } +} + static void s390_ipl_reset(DeviceState *dev) { S390IPLState *ipl = S390_IPL(dev); - S390CPU *cpu = S390_CPU(qemu_get_cpu(0)); - CPUS390XState *env = &cpu->env; - - env->psw.addr = ipl->start_addr; - env->psw.mask = IPL_PSW_MASK; if (!ipl->reipl_requested) { ipl->iplb_valid = false; } ipl->reipl_requested = false; - - if (!ipl->kernel || ipl->iplb_valid) { - env->psw.addr = ipl->bios_start_addr; - env->regs[7] = s390_update_iplstate(env, ipl); - } - - s390_cpu_set_state(CPU_STATE_OPERATING, cpu); } static void s390_ipl_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = s390_ipl_init; + dc->realize = s390_ipl_realize; dc->props = s390_ipl_properties; dc->reset = s390_ipl_reset; dc->vmsd = &vmstate_ipl; @@ -320,8 +285,8 @@ static void s390_ipl_class_init(ObjectClass *klass, void *data) static const TypeInfo s390_ipl_info = { .class_init = s390_ipl_class_init, - .parent = TYPE_SYS_BUS_DEVICE, - .name = "s390-ipl", + .parent = TYPE_DEVICE, + .name = TYPE_S390_IPL, .instance_size = sizeof(S390IPLState), }; diff --git a/qemu/hw/s390x/ipl.h b/qemu/hw/s390x/ipl.h index 70497bc65..6b48ed7b9 100644 --- a/qemu/hw/s390x/ipl.h +++ b/qemu/hw/s390x/ipl.h @@ -12,14 +12,42 @@ #ifndef HW_S390_IPL_H #define HW_S390_IPL_H +#include "hw/qdev.h" +#include "cpu.h" + typedef struct IplParameterBlock { uint8_t reserved1[110]; uint16_t devno; uint8_t reserved2[88]; } IplParameterBlock; -int s390_ipl_update_diag308(IplParameterBlock *iplb); +void s390_ipl_update_diag308(IplParameterBlock *iplb); +void s390_ipl_prepare_cpu(S390CPU *cpu); IplParameterBlock *s390_ipl_get_iplb(void); void s390_reipl_request(void); +#define TYPE_S390_IPL "s390-ipl" +#define S390_IPL(obj) OBJECT_CHECK(S390IPLState, (obj), TYPE_S390_IPL) + +struct S390IPLState { + /*< private >*/ + DeviceState parent_obj; + uint64_t start_addr; + uint64_t bios_start_addr; + bool enforce_bios; + IplParameterBlock iplb; + bool iplb_valid; + bool reipl_requested; + + /*< public >*/ + char *kernel; + char *initrd; + char *cmdline; + char *firmware; + uint8_t cssid; + uint8_t ssid; + uint16_t devno; +}; +typedef struct S390IPLState S390IPLState; + #endif diff --git a/qemu/hw/s390x/s390-pci-bus.c b/qemu/hw/s390x/s390-pci-bus.c index 560b66a50..918b58543 100644 --- a/qemu/hw/s390x/s390-pci-bus.c +++ b/qemu/hw/s390x/s390-pci-bus.c @@ -11,6 +11,9 @@ * directory. */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "s390-pci-bus.h" #include #include @@ -123,7 +126,6 @@ void s390_pci_sclp_configure(int configure, SCCB *sccb) } psccb->header.response_code = cpu_to_be16(rc); - return; } static uint32_t s390_pci_get_pfid(PCIDevice *pdev) @@ -308,9 +310,8 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr, { uint64_t pte; uint32_t flags; - S390PCIBusDevice *pbdev = container_of(iommu, S390PCIBusDevice, mr); - S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pbdev->pdev) - ->qbus.parent); + S390PCIBusDevice *pbdev = container_of(iommu, S390PCIBusDevice, iommu_mr); + S390pciState *s; IOMMUTLBEntry ret = { .target_as = &address_space_memory, .iova = 0, @@ -319,8 +320,13 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr, .perm = IOMMU_NONE, }; + if (!pbdev->configured || !pbdev->pdev || !(pbdev->fh & FH_ENABLED)) { + return ret; + } + DPRINTF("iommu trans addr 0x%" PRIx64 "\n", addr); + s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pbdev->pdev)->qbus.parent); /* s390 does not have an APIC mapped to main storage so we use * a separate AddressSpace only for msix notifications */ @@ -425,6 +431,10 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data, return; } + if (!(pbdev->fh & FH_ENABLED)) { + return; + } + ind_bit = pbdev->routes.adapter.ind_offset; sum_bit = pbdev->routes.adapter.summary_offset; @@ -435,8 +445,6 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data, io_int_word = (pbdev->isc << 27) | IO_INT_WORD_AI; s390_io_interrupt(0, 0, 0, io_int_word); } - - return; } static uint64_t s390_msi_ctrl_read(void *opaque, hwaddr addr, unsigned size) @@ -450,14 +458,32 @@ static const MemoryRegionOps s390_msi_ctrl_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +void s390_pcihost_iommu_configure(S390PCIBusDevice *pbdev, bool enable) +{ + pbdev->configured = false; + + if (enable) { + uint64_t size = pbdev->pal - pbdev->pba + 1; + memory_region_init_iommu(&pbdev->iommu_mr, OBJECT(&pbdev->mr), + &s390_iommu_ops, "iommu-s390", size); + memory_region_add_subregion(&pbdev->mr, pbdev->pba, &pbdev->iommu_mr); + } else { + memory_region_del_subregion(&pbdev->mr, &pbdev->iommu_mr); + } + + pbdev->configured = true; +} + static void s390_pcihost_init_as(S390pciState *s) { int i; + S390PCIBusDevice *pbdev; for (i = 0; i < PCI_SLOT_MAX; i++) { - memory_region_init_iommu(&s->pbdev[i].mr, OBJECT(s), - &s390_iommu_ops, "iommu-s390", UINT64_MAX); - address_space_init(&s->pbdev[i].as, &s->pbdev[i].mr, "iommu-pci"); + pbdev = &s->pbdev[i]; + memory_region_init(&pbdev->mr, OBJECT(s), + "iommu-root-s390", UINT64_MAX); + address_space_init(&pbdev->as, &pbdev->mr, "iommu-pci"); } memory_region_init_io(&s->msix_notify_mr, OBJECT(s), @@ -500,7 +526,7 @@ static int s390_pcihost_setup_msix(S390PCIBusDevice *pbdev) return 0; } - ctrl = pci_host_config_read_common(pbdev->pdev, pos + PCI_CAP_FLAGS, + ctrl = pci_host_config_read_common(pbdev->pdev, pos + PCI_MSIX_FLAGS, pci_config_size(pbdev->pdev), sizeof(ctrl)); table = pci_host_config_read_common(pbdev->pdev, pos + PCI_MSIX_TABLE, pci_config_size(pbdev->pdev), sizeof(table)); @@ -539,7 +565,6 @@ static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev, s390_pci_generate_plug_event(HP_EVENT_TO_CONFIGURED, pbdev->fh, pbdev->fid); } - return; } static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev, @@ -574,7 +599,7 @@ static void s390_pcihost_class_init(ObjectClass *klass, void *data) k->init = s390_pcihost_init; hc->plug = s390_pcihost_hot_plug; hc->unplug = s390_pcihost_hot_unplug; - msi_supported = true; + msi_nonbroken = true; } static const TypeInfo s390_pcihost_info = { diff --git a/qemu/hw/s390x/s390-pci-bus.h b/qemu/hw/s390x/s390-pci-bus.h index 464a92eed..59fd5c958 100644 --- a/qemu/hw/s390x/s390-pci-bus.h +++ b/qemu/hw/s390x/s390-pci-bus.h @@ -23,6 +23,7 @@ #define TYPE_S390_PCI_HOST_BRIDGE "s390-pcihost" #define FH_VIRT 0x00ff0000 #define ENABLE_BIT_OFFSET 31 +#define FH_ENABLED (1 << ENABLE_BIT_OFFSET) #define S390_PCIPT_ADAPTER 2 #define S390_PCI_HOST_BRIDGE(obj) \ @@ -231,6 +232,9 @@ typedef struct S390PCIBusDevice { AdapterRoutes routes; AddressSpace as; MemoryRegion mr; + MemoryRegion iommu_mr; + IndAddr *summary_ind; + IndAddr *indicator; } S390PCIBusDevice; typedef struct S390pciState { @@ -244,6 +248,7 @@ typedef struct S390pciState { int chsc_sei_nt2_get_event(void *res); int chsc_sei_nt2_have_event(void); void s390_pci_sclp_configure(int configure, SCCB *sccb); +void s390_pcihost_iommu_configure(S390PCIBusDevice *pbdev, bool enable); S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx); S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh); S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid); diff --git a/qemu/hw/s390x/s390-pci-inst.c b/qemu/hw/s390x/s390-pci-inst.c index f9151a9af..b28e7d14f 100644 --- a/qemu/hw/s390x/s390-pci-inst.c +++ b/qemu/hw/s390x/s390-pci-inst.c @@ -11,6 +11,9 @@ * directory. */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "s390-pci-inst.h" #include "s390-pci-bus.h" #include @@ -105,7 +108,8 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) pci_get_word(pbdev->pdev->config + PCI_DEVICE_ID)); stw_p(&rrb->response.fh_list[idx - resume_token].vendor_id, pci_get_word(pbdev->pdev->config + PCI_VENDOR_ID)); - stl_p(&rrb->response.fh_list[idx - resume_token].config, 0x80000000); + stl_p(&rrb->response.fh_list[idx - resume_token].config, + pbdev->configured << 31); stl_p(&rrb->response.fh_list[idx - resume_token].fid, pbdev->fid); stl_p(&rrb->response.fh_list[idx - resume_token].fh, pbdev->fh); @@ -208,12 +212,12 @@ int clp_service_call(S390CPU *cpu, uint8_t r2) switch (reqsetpci->oc) { case CLP_SET_ENABLE_PCI_FN: - pbdev->fh = pbdev->fh | 1 << ENABLE_BIT_OFFSET; + pbdev->fh = pbdev->fh | FH_ENABLED; stl_p(&ressetpci->fh, pbdev->fh); stw_p(&ressetpci->hdr.rsp, CLP_RC_OK); break; case CLP_SET_DISABLE_PCI_FN: - pbdev->fh = pbdev->fh & ~(1 << ENABLE_BIT_OFFSET); + pbdev->fh = pbdev->fh & ~FH_ENABLED; pbdev->error_state = false; pbdev->lgstg_blocked = false; stl_p(&ressetpci->fh, pbdev->fh); @@ -313,7 +317,7 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) offset = env->regs[r2 + 1]; pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev) { + if (!pbdev || !(pbdev->fh & FH_ENABLED)) { DPRINTF("pcilg no pci dev\n"); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; @@ -430,7 +434,7 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) offset = env->regs[r2 + 1]; pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev) { + if (!pbdev || !(pbdev->fh & FH_ENABLED)) { DPRINTF("pcistg no pci dev\n"); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; @@ -521,14 +525,13 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) end = start + env->regs[r2 + 1]; pbdev = s390_pci_find_dev_by_fh(fh); - - if (!pbdev) { + if (!pbdev || !(pbdev->fh & FH_ENABLED)) { DPRINTF("rpcit no pci dev\n"); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); goto out; } - mr = pci_device_iommu_address_space(pbdev->pdev)->root; + mr = &pbdev->iommu_mr; while (start < end) { entry = mr->iommu_ops->translate(mr, start, 0); @@ -586,7 +589,7 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, } pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev) { + if (!pbdev || !(pbdev->fh & FH_ENABLED)) { DPRINTF("pcistb no pci dev fh 0x%x\n", fh); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; @@ -620,19 +623,19 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) { - int ret; - S390FLICState *fs = s390_get_flic(); - S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); + int ret, len; ret = css_register_io_adapter(S390_PCIPT_ADAPTER, FIB_DATA_ISC(ldl_p(&fib.data)), true, false, &pbdev->routes.adapter.adapter_id); assert(ret == 0); - fsc->io_adapter_map(fs, pbdev->routes.adapter.adapter_id, - ldq_p(&fib.aisb), true); - fsc->io_adapter_map(fs, pbdev->routes.adapter.adapter_id, - ldq_p(&fib.aibv), true); + pbdev->summary_ind = get_indicator(ldq_p(&fib.aisb), sizeof(uint64_t)); + len = BITS_TO_LONGS(FIB_DATA_NOI(ldl_p(&fib.data))) * sizeof(unsigned long); + pbdev->indicator = get_indicator(ldq_p(&fib.aibv), len); + + map_indicator(&pbdev->routes.adapter, pbdev->summary_ind); + map_indicator(&pbdev->routes.adapter, pbdev->indicator); pbdev->routes.adapter.summary_addr = ldq_p(&fib.aisb); pbdev->routes.adapter.summary_offset = FIB_DATA_AISBO(ldl_p(&fib.data)); @@ -648,12 +651,11 @@ static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) static int dereg_irqs(S390PCIBusDevice *pbdev) { - S390FLICState *fs = s390_get_flic(); - S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); - - fsc->io_adapter_map(fs, pbdev->routes.adapter.adapter_id, - pbdev->routes.adapter.ind_addr, false); + release_indicator(&pbdev->routes.adapter, pbdev->summary_ind); + release_indicator(&pbdev->routes.adapter, pbdev->indicator); + pbdev->summary_ind = NULL; + pbdev->indicator = NULL; pbdev->routes.adapter.summary_addr = 0; pbdev->routes.adapter.summary_offset = 0; pbdev->routes.adapter.ind_addr = 0; @@ -689,6 +691,9 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) pbdev->pba = pba; pbdev->pal = pal; pbdev->g_iota = g_iota; + + s390_pcihost_iommu_configure(pbdev, true); + return 0; } @@ -697,6 +702,8 @@ static void dereg_ioat(S390PCIBusDevice *pbdev) pbdev->pba = 0; pbdev->pal = 0; pbdev->g_iota = 0; + + s390_pcihost_iommu_configure(pbdev, false); } int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) @@ -722,7 +729,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) } pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev) { + if (!pbdev || !(pbdev->fh & FH_ENABLED)) { DPRINTF("mpcifc no pci dev fh 0x%x\n", fh); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; @@ -814,7 +821,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) ((uint32_t)pbdev->sum << 7) | pbdev->routes.adapter.summary_offset; stl_p(&fib.data, data); - if (pbdev->fh >> ENABLE_BIT_OFFSET) { + if (pbdev->fh & FH_ENABLED) { fib.fc |= 0x80; } diff --git a/qemu/hw/s390x/s390-skeys-kvm.c b/qemu/hw/s390x/s390-skeys-kvm.c new file mode 100644 index 000000000..131da56bb --- /dev/null +++ b/qemu/hw/s390x/s390-skeys-kvm.c @@ -0,0 +1,76 @@ +/* + * s390 storage key device + * + * Copyright 2015 IBM Corp. + * Author(s): Jason J. Herne + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include "qemu/osdep.h" +#include "hw/s390x/storage-keys.h" +#include "sysemu/kvm.h" +#include "qemu/error-report.h" + +static int kvm_s390_skeys_enabled(S390SKeysState *ss) +{ + S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss); + uint8_t single_key; + int r; + + r = skeyclass->get_skeys(ss, 0, 1, &single_key); + if (r != 0 && r != KVM_S390_GET_SKEYS_NONE) { + error_report("S390_GET_KEYS error %d", r); + } + return (r == 0); +} + +static int kvm_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn, + uint64_t count, uint8_t *keys) +{ + struct kvm_s390_skeys args = { + .start_gfn = start_gfn, + .count = count, + .skeydata_addr = (__u64)keys + }; + + return kvm_vm_ioctl(kvm_state, KVM_S390_GET_SKEYS, &args); +} + +static int kvm_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn, + uint64_t count, uint8_t *keys) +{ + struct kvm_s390_skeys args = { + .start_gfn = start_gfn, + .count = count, + .skeydata_addr = (__u64)keys + }; + + return kvm_vm_ioctl(kvm_state, KVM_S390_SET_SKEYS, &args); +} + +static void kvm_s390_skeys_class_init(ObjectClass *oc, void *data) +{ + S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc); + + skeyclass->skeys_enabled = kvm_s390_skeys_enabled; + skeyclass->get_skeys = kvm_s390_skeys_get; + skeyclass->set_skeys = kvm_s390_skeys_set; +} + +static const TypeInfo kvm_s390_skeys_info = { + .name = TYPE_KVM_S390_SKEYS, + .parent = TYPE_S390_SKEYS, + .instance_size = sizeof(S390SKeysState), + .class_init = kvm_s390_skeys_class_init, + .class_size = sizeof(S390SKeysClass), +}; + +static void kvm_s390_skeys_register_types(void) +{ + type_register_static(&kvm_s390_skeys_info); +} + +type_init(kvm_s390_skeys_register_types) diff --git a/qemu/hw/s390x/s390-skeys.c b/qemu/hw/s390x/s390-skeys.c new file mode 100644 index 000000000..6528ffed1 --- /dev/null +++ b/qemu/hw/s390x/s390-skeys.c @@ -0,0 +1,415 @@ +/* + * s390 storage key device + * + * Copyright 2015 IBM Corp. + * Author(s): Jason J. Herne + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include "qemu/osdep.h" +#include "hw/boards.h" +#include "qmp-commands.h" +#include "migration/qemu-file.h" +#include "hw/s390x/storage-keys.h" +#include "qemu/error-report.h" + +#define S390_SKEYS_BUFFER_SIZE 131072 /* Room for 128k storage keys */ +#define S390_SKEYS_SAVE_FLAG_EOS 0x01 +#define S390_SKEYS_SAVE_FLAG_SKEYS 0x02 +#define S390_SKEYS_SAVE_FLAG_ERROR 0x04 + +S390SKeysState *s390_get_skeys_device(void) +{ + S390SKeysState *ss; + + ss = S390_SKEYS(object_resolve_path_type("", TYPE_S390_SKEYS, NULL)); + assert(ss); + return ss; +} + +void s390_skeys_init(void) +{ + Object *obj; + + if (kvm_enabled()) { + obj = object_new(TYPE_KVM_S390_SKEYS); + } else { + obj = object_new(TYPE_QEMU_S390_SKEYS); + } + object_property_add_child(qdev_get_machine(), TYPE_S390_SKEYS, + obj, NULL); + object_unref(obj); + + qdev_init_nofail(DEVICE(obj)); +} + +static void write_keys(QEMUFile *f, uint8_t *keys, uint64_t startgfn, + uint64_t count, Error **errp) +{ + uint64_t curpage = startgfn; + uint64_t maxpage = curpage + count - 1; + const char *fmt = "page=%03" PRIx64 ": key(%d) => ACC=%X, FP=%d, REF=%d," + " ch=%d, reserved=%d\n"; + char buf[128]; + int len; + + for (; curpage <= maxpage; curpage++) { + uint8_t acc = (*keys & 0xF0) >> 4; + int fp = (*keys & 0x08); + int ref = (*keys & 0x04); + int ch = (*keys & 0x02); + int res = (*keys & 0x01); + + len = snprintf(buf, sizeof(buf), fmt, curpage, + *keys, acc, fp, ref, ch, res); + assert(len < sizeof(buf)); + qemu_put_buffer(f, (uint8_t *)buf, len); + keys++; + } +} + +void hmp_info_skeys(Monitor *mon, const QDict *qdict) +{ + S390SKeysState *ss = s390_get_skeys_device(); + S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss); + uint64_t addr = qdict_get_int(qdict, "addr"); + uint8_t key; + int r; + + /* Quick check to see if guest is using storage keys*/ + if (!skeyclass->skeys_enabled(ss)) { + monitor_printf(mon, "Error: This guest is not using storage keys\n"); + return; + } + + r = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); + if (r < 0) { + monitor_printf(mon, "Error: %s\n", strerror(-r)); + return; + } + + monitor_printf(mon, " key: 0x%X\n", key); +} + +void hmp_dump_skeys(Monitor *mon, const QDict *qdict) +{ + const char *filename = qdict_get_str(qdict, "filename"); + Error *err = NULL; + + qmp_dump_skeys(filename, &err); + if (err) { + error_report_err(err); + } +} + +void qmp_dump_skeys(const char *filename, Error **errp) +{ + S390SKeysState *ss = s390_get_skeys_device(); + S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss); + const uint64_t total_count = ram_size / TARGET_PAGE_SIZE; + uint64_t handled_count = 0, cur_count; + Error *lerr = NULL; + vaddr cur_gfn = 0; + uint8_t *buf; + int ret; + QEMUFile *f; + + /* Quick check to see if guest is using storage keys*/ + if (!skeyclass->skeys_enabled(ss)) { + error_setg(errp, "This guest is not using storage keys - " + "nothing to dump"); + return; + } + + f = qemu_fopen(filename, "wb"); + if (!f) { + error_setg_file_open(errp, errno, filename); + return; + } + + buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE); + if (!buf) { + error_setg(errp, "Could not allocate memory"); + goto out; + } + + /* we'll only dump initial memory for now */ + while (handled_count < total_count) { + /* Calculate how many keys to ask for & handle overflow case */ + cur_count = MIN(total_count - handled_count, S390_SKEYS_BUFFER_SIZE); + + ret = skeyclass->get_skeys(ss, cur_gfn, cur_count, buf); + if (ret < 0) { + error_setg(errp, "get_keys error %d", ret); + goto out_free; + } + + /* write keys to stream */ + write_keys(f, buf, cur_gfn, cur_count, &lerr); + if (lerr) { + goto out_free; + } + + cur_gfn += cur_count; + handled_count += cur_count; + } + +out_free: + error_propagate(errp, lerr); + g_free(buf); +out: + qemu_fclose(f); +} + +static void qemu_s390_skeys_init(Object *obj) +{ + QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(obj); + MachineState *machine = MACHINE(qdev_get_machine()); + + skeys->key_count = machine->maxram_size / TARGET_PAGE_SIZE; + skeys->keydata = g_malloc0(skeys->key_count); +} + +static int qemu_s390_skeys_enabled(S390SKeysState *ss) +{ + return 1; +} + +/* + * TODO: for memory hotplug support qemu_s390_skeys_set and qemu_s390_skeys_get + * will have to make sure that the given gfn belongs to a memory region and not + * a memory hole. + */ +static int qemu_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn, + uint64_t count, uint8_t *keys) +{ + QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss); + int i; + + /* Check for uint64 overflow and access beyond end of key data */ + if (start_gfn + count > skeydev->key_count || start_gfn + count < count) { + error_report("Error: Setting storage keys for page beyond the end " + "of memory: gfn=%" PRIx64 " count=%" PRId64, + start_gfn, count); + return -EINVAL; + } + + for (i = 0; i < count; i++) { + skeydev->keydata[start_gfn + i] = keys[i]; + } + return 0; +} + +static int qemu_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn, + uint64_t count, uint8_t *keys) +{ + QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss); + int i; + + /* Check for uint64 overflow and access beyond end of key data */ + if (start_gfn + count > skeydev->key_count || start_gfn + count < count) { + error_report("Error: Getting storage keys for page beyond the end " + "of memory: gfn=%" PRIx64 " count=%" PRId64, + start_gfn, count); + return -EINVAL; + } + + for (i = 0; i < count; i++) { + keys[i] = skeydev->keydata[start_gfn + i]; + } + return 0; +} + +static void qemu_s390_skeys_class_init(ObjectClass *oc, void *data) +{ + S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc); + + skeyclass->skeys_enabled = qemu_s390_skeys_enabled; + skeyclass->get_skeys = qemu_s390_skeys_get; + skeyclass->set_skeys = qemu_s390_skeys_set; +} + +static const TypeInfo qemu_s390_skeys_info = { + .name = TYPE_QEMU_S390_SKEYS, + .parent = TYPE_S390_SKEYS, + .instance_init = qemu_s390_skeys_init, + .instance_size = sizeof(QEMUS390SKeysState), + .class_init = qemu_s390_skeys_class_init, + .class_size = sizeof(S390SKeysClass), +}; + +static void s390_storage_keys_save(QEMUFile *f, void *opaque) +{ + S390SKeysState *ss = S390_SKEYS(opaque); + S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss); + uint64_t pages_left = ram_size / TARGET_PAGE_SIZE; + uint64_t read_count, eos = S390_SKEYS_SAVE_FLAG_EOS; + vaddr cur_gfn = 0; + int error = 0; + uint8_t *buf; + + if (!skeyclass->skeys_enabled(ss)) { + goto end_stream; + } + + buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE); + if (!buf) { + error_report("storage key save could not allocate memory"); + goto end_stream; + } + + /* We only support initial memory. Standby memory is not handled yet. */ + qemu_put_be64(f, (cur_gfn * TARGET_PAGE_SIZE) | S390_SKEYS_SAVE_FLAG_SKEYS); + qemu_put_be64(f, pages_left); + + while (pages_left) { + read_count = MIN(pages_left, S390_SKEYS_BUFFER_SIZE); + + if (!error) { + error = skeyclass->get_skeys(ss, cur_gfn, read_count, buf); + if (error) { + /* + * If error: we want to fill the stream with valid data instead + * of stopping early so we pad the stream with 0x00 values and + * use S390_SKEYS_SAVE_FLAG_ERROR to indicate failure to the + * reading side. + */ + error_report("S390_GET_KEYS error %d", error); + memset(buf, 0, S390_SKEYS_BUFFER_SIZE); + eos = S390_SKEYS_SAVE_FLAG_ERROR; + } + } + + qemu_put_buffer(f, buf, read_count); + cur_gfn += read_count; + pages_left -= read_count; + } + + g_free(buf); +end_stream: + qemu_put_be64(f, eos); +} + +static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id) +{ + S390SKeysState *ss = S390_SKEYS(opaque); + S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss); + int ret = 0; + + while (!ret) { + ram_addr_t addr; + int flags; + + addr = qemu_get_be64(f); + flags = addr & ~TARGET_PAGE_MASK; + addr &= TARGET_PAGE_MASK; + + switch (flags) { + case S390_SKEYS_SAVE_FLAG_SKEYS: { + const uint64_t total_count = qemu_get_be64(f); + uint64_t handled_count = 0, cur_count; + uint64_t cur_gfn = addr / TARGET_PAGE_SIZE; + uint8_t *buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE); + + if (!buf) { + error_report("storage key load could not allocate memory"); + ret = -ENOMEM; + break; + } + + while (handled_count < total_count) { + cur_count = MIN(total_count - handled_count, + S390_SKEYS_BUFFER_SIZE); + qemu_get_buffer(f, buf, cur_count); + + ret = skeyclass->set_skeys(ss, cur_gfn, cur_count, buf); + if (ret < 0) { + error_report("S390_SET_KEYS error %d", ret); + break; + } + handled_count += cur_count; + cur_gfn += cur_count; + } + g_free(buf); + break; + } + case S390_SKEYS_SAVE_FLAG_ERROR: { + error_report("Storage key data is incomplete"); + ret = -EINVAL; + break; + } + case S390_SKEYS_SAVE_FLAG_EOS: + /* normal exit */ + return 0; + default: + error_report("Unexpected storage key flag data: %#x", flags); + ret = -EINVAL; + } + } + + return ret; +} + +static inline bool s390_skeys_get_migration_enabled(Object *obj, Error **errp) +{ + S390SKeysState *ss = S390_SKEYS(obj); + + return ss->migration_enabled; +} + +static inline void s390_skeys_set_migration_enabled(Object *obj, bool value, + Error **errp) +{ + S390SKeysState *ss = S390_SKEYS(obj); + + /* Prevent double registration of savevm handler */ + if (ss->migration_enabled == value) { + return; + } + + ss->migration_enabled = value; + + if (ss->migration_enabled) { + register_savevm(NULL, TYPE_S390_SKEYS, 0, 1, s390_storage_keys_save, + s390_storage_keys_load, ss); + } else { + unregister_savevm(DEVICE(ss), TYPE_S390_SKEYS, ss); + } +} + +static void s390_skeys_instance_init(Object *obj) +{ + object_property_add_bool(obj, "migration-enabled", + s390_skeys_get_migration_enabled, + s390_skeys_set_migration_enabled, NULL); + object_property_set_bool(obj, true, "migration-enabled", NULL); +} + +static void s390_skeys_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->hotpluggable = false; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +static const TypeInfo s390_skeys_info = { + .name = TYPE_S390_SKEYS, + .parent = TYPE_DEVICE, + .instance_init = s390_skeys_instance_init, + .instance_size = sizeof(S390SKeysState), + .class_init = s390_skeys_class_init, + .class_size = sizeof(S390SKeysClass), + .abstract = true, +}; + +static void qemu_s390_skeys_register_types(void) +{ + type_register_static(&s390_skeys_info); + type_register_static(&qemu_s390_skeys_info); +} + +type_init(qemu_s390_skeys_register_types) diff --git a/qemu/hw/s390x/s390-virtio-bus.c b/qemu/hw/s390x/s390-virtio-bus.c deleted file mode 100644 index 77aec8a5b..000000000 --- a/qemu/hw/s390x/s390-virtio-bus.c +++ /dev/null @@ -1,763 +0,0 @@ -/* - * QEMU S390 virtio target - * - * Copyright (c) 2009 Alexander Graf - * - * 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 . - */ - -#include "hw/hw.h" -#include "sysemu/block-backend.h" -#include "sysemu/sysemu.h" -#include "hw/boards.h" -#include "hw/loader.h" -#include "elf.h" -#include "hw/virtio/virtio.h" -#include "hw/virtio/virtio-rng.h" -#include "hw/virtio/virtio-serial.h" -#include "hw/virtio/virtio-net.h" -#include "hw/virtio/vhost-scsi.h" -#include "hw/sysbus.h" -#include "sysemu/kvm.h" - -#include "hw/s390x/s390-virtio-bus.h" -#include "hw/virtio/virtio-bus.h" - -/* #define DEBUG_S390 */ - -#ifdef DEBUG_S390 -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - -#define VIRTIO_S390_QUEUE_MAX 64 - -static void virtio_s390_bus_new(VirtioBusState *bus, size_t bus_size, - VirtIOS390Device *dev); - -static const TypeInfo s390_virtio_bus_info = { - .name = TYPE_S390_VIRTIO_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(VirtIOS390Bus), -}; - -static ram_addr_t s390_virtio_device_num_vq(VirtIOS390Device *dev); - -/* length of VirtIO device pages */ -const hwaddr virtio_size = S390_DEVICE_PAGES * TARGET_PAGE_SIZE; - -static void s390_virtio_bus_reset(void *opaque) -{ - VirtIOS390Bus *bus = opaque; - bus->next_ring = bus->dev_page + TARGET_PAGE_SIZE; -} - -void s390_virtio_reset_idx(VirtIOS390Device *dev) -{ - int i; - hwaddr idx_addr; - uint8_t num_vq; - - num_vq = s390_virtio_device_num_vq(dev); - for (i = 0; i < num_vq; i++) { - idx_addr = virtio_queue_get_avail_addr(dev->vdev, i) + - VIRTIO_VRING_AVAIL_IDX_OFFS; - address_space_stw(&address_space_memory, idx_addr, 0, - MEMTXATTRS_UNSPECIFIED, NULL); - idx_addr = virtio_queue_get_avail_addr(dev->vdev, i) + - virtio_queue_get_avail_size(dev->vdev, i); - address_space_stw(&address_space_memory, idx_addr, 0, - MEMTXATTRS_UNSPECIFIED, NULL); - idx_addr = virtio_queue_get_used_addr(dev->vdev, i) + - VIRTIO_VRING_USED_IDX_OFFS; - address_space_stw(&address_space_memory, idx_addr, 0, - MEMTXATTRS_UNSPECIFIED, NULL); - idx_addr = virtio_queue_get_used_addr(dev->vdev, i) + - virtio_queue_get_used_size(dev->vdev, i); - address_space_stw(&address_space_memory, idx_addr, 0, - MEMTXATTRS_UNSPECIFIED, NULL); - } -} - -VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size) -{ - VirtIOS390Bus *bus; - BusState *_bus; - DeviceState *dev; - - /* Create bridge device */ - dev = qdev_create(NULL, "s390-virtio-bridge"); - qdev_init_nofail(dev); - - /* Create bus on bridge device */ - - _bus = qbus_create(TYPE_S390_VIRTIO_BUS, dev, "s390-virtio"); - bus = DO_UPCAST(VirtIOS390Bus, bus, _bus); - - bus->dev_page = *ram_size; - bus->dev_offs = bus->dev_page; - bus->next_ring = bus->dev_page + TARGET_PAGE_SIZE; - - /* Enable hotplugging */ - qbus_set_hotplug_handler(_bus, dev, &error_abort); - - /* Allocate RAM for VirtIO device pages (descriptors, queues, rings) */ - *ram_size += S390_DEVICE_PAGES * TARGET_PAGE_SIZE; - - qemu_register_reset(s390_virtio_bus_reset, bus); - return bus; -} - -static void s390_virtio_device_init(VirtIOS390Device *dev, - VirtIODevice *vdev) -{ - VirtIOS390Bus *bus; - int dev_len; - - bus = DO_UPCAST(VirtIOS390Bus, bus, dev->qdev.parent_bus); - dev->vdev = vdev; - dev->dev_offs = bus->dev_offs; - dev->feat_len = sizeof(uint32_t); /* always keep 32 bits features */ - - dev_len = VIRTIO_DEV_OFFS_CONFIG; - dev_len += s390_virtio_device_num_vq(dev) * VIRTIO_VQCONFIG_LEN; - dev_len += dev->feat_len * 2; - dev_len += virtio_bus_get_vdev_config_len(&dev->bus); - - bus->dev_offs += dev_len; - - s390_virtio_device_sync(dev); - s390_virtio_reset_idx(dev); - if (dev->qdev.hotplugged) { - s390_virtio_irq(VIRTIO_PARAM_DEV_ADD, dev->dev_offs); - } -} - -static void s390_virtio_net_realize(VirtIOS390Device *s390_dev, Error **errp) -{ - DeviceState *qdev = DEVICE(s390_dev); - VirtIONetS390 *dev = VIRTIO_NET_S390(s390_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - Error *err = NULL; - - virtio_net_set_netclient_name(&dev->vdev, qdev->id, - object_get_typename(OBJECT(qdev))); - qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - - s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); -} - -static void s390_virtio_net_instance_init(Object *obj) -{ - VirtIONetS390 *dev = VIRTIO_NET_S390(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_NET); - object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), - "bootindex", &error_abort); -} - -static void s390_virtio_blk_realize(VirtIOS390Device *s390_dev, Error **errp) -{ - VirtIOBlkS390 *dev = VIRTIO_BLK_S390(s390_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - Error *err = NULL; - - qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); -} - -static void s390_virtio_blk_instance_init(Object *obj) -{ - VirtIOBlkS390 *dev = VIRTIO_BLK_S390(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_BLK); - object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev),"iothread", - &error_abort); - object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), - "bootindex", &error_abort); -} - -static void s390_virtio_serial_realize(VirtIOS390Device *s390_dev, Error **errp) -{ - VirtIOSerialS390 *dev = VIRTIO_SERIAL_S390(s390_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - DeviceState *qdev = DEVICE(s390_dev); - Error *err = NULL; - VirtIOS390Bus *bus; - char *bus_name; - - bus = DO_UPCAST(VirtIOS390Bus, bus, qdev->parent_bus); - - /* - * For command line compatibility, this sets the virtio-serial-device bus - * name as before. - */ - if (qdev->id) { - bus_name = g_strdup_printf("%s.0", qdev->id); - virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name); - g_free(bus_name); - } - - qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - - s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); - bus->console = s390_dev; -} - -static void s390_virtio_serial_instance_init(Object *obj) -{ - VirtIOSerialS390 *dev = VIRTIO_SERIAL_S390(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_SERIAL); -} - -static void s390_virtio_scsi_realize(VirtIOS390Device *s390_dev, Error **errp) -{ - VirtIOSCSIS390 *dev = VIRTIO_SCSI_S390(s390_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - DeviceState *qdev = DEVICE(s390_dev); - Error *err = NULL; - char *bus_name; - - /* - * For command line compatibility, this sets the virtio-scsi-device bus - * name as before. - */ - if (qdev->id) { - bus_name = g_strdup_printf("%s.0", qdev->id); - virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name); - g_free(bus_name); - } - - qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - - s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); -} - -static void s390_virtio_scsi_instance_init(Object *obj) -{ - VirtIOSCSIS390 *dev = VIRTIO_SCSI_S390(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_SCSI); -} - -#ifdef CONFIG_VHOST_SCSI -static void s390_vhost_scsi_realize(VirtIOS390Device *s390_dev, Error **errp) -{ - VHostSCSIS390 *dev = VHOST_SCSI_S390(s390_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - Error *err = NULL; - - qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - - s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); -} - -static void s390_vhost_scsi_instance_init(Object *obj) -{ - VHostSCSIS390 *dev = VHOST_SCSI_S390(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VHOST_SCSI); -} -#endif - - -static void s390_virtio_rng_realize(VirtIOS390Device *s390_dev, Error **errp) -{ - VirtIORNGS390 *dev = VIRTIO_RNG_S390(s390_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - Error *err = NULL; - - qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - - object_property_set_link(OBJECT(dev), - OBJECT(dev->vdev.conf.rng), "rng", - NULL); - - s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); -} - -static void s390_virtio_rng_instance_init(Object *obj) -{ - VirtIORNGS390 *dev = VIRTIO_RNG_S390(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_RNG); - object_property_add_alias(obj, "rng", OBJECT(&dev->vdev), - "rng", &error_abort); -} - -static uint64_t s390_virtio_device_vq_token(VirtIOS390Device *dev, int vq) -{ - ram_addr_t token_off; - - token_off = (dev->dev_offs + VIRTIO_DEV_OFFS_CONFIG) + - (vq * VIRTIO_VQCONFIG_LEN) + - VIRTIO_VQCONFIG_OFFS_TOKEN; - - return address_space_ldq_be(&address_space_memory, token_off, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -static ram_addr_t s390_virtio_device_num_vq(VirtIOS390Device *dev) -{ - VirtIODevice *vdev = dev->vdev; - int num_vq; - - for (num_vq = 0; num_vq < VIRTIO_S390_QUEUE_MAX; num_vq++) { - if (!virtio_queue_get_num(vdev, num_vq)) { - break; - } - } - - return num_vq; -} - -static ram_addr_t s390_virtio_next_ring(VirtIOS390Bus *bus) -{ - ram_addr_t r = bus->next_ring; - - bus->next_ring += VIRTIO_RING_LEN; - return r; -} - -void s390_virtio_device_sync(VirtIOS390Device *dev) -{ - VirtIOS390Bus *bus = DO_UPCAST(VirtIOS390Bus, bus, dev->qdev.parent_bus); - ram_addr_t cur_offs; - uint8_t num_vq; - int i; - - virtio_reset(dev->vdev); - - /* Sync dev space */ - address_space_stb(&address_space_memory, - dev->dev_offs + VIRTIO_DEV_OFFS_TYPE, - dev->vdev->device_id, - MEMTXATTRS_UNSPECIFIED, - NULL); - - address_space_stb(&address_space_memory, - dev->dev_offs + VIRTIO_DEV_OFFS_NUM_VQ, - s390_virtio_device_num_vq(dev), - MEMTXATTRS_UNSPECIFIED, - NULL); - address_space_stb(&address_space_memory, - dev->dev_offs + VIRTIO_DEV_OFFS_FEATURE_LEN, - dev->feat_len, - MEMTXATTRS_UNSPECIFIED, - NULL); - - address_space_stb(&address_space_memory, - dev->dev_offs + VIRTIO_DEV_OFFS_CONFIG_LEN, - dev->vdev->config_len, - MEMTXATTRS_UNSPECIFIED, - NULL); - - num_vq = s390_virtio_device_num_vq(dev); - address_space_stb(&address_space_memory, - dev->dev_offs + VIRTIO_DEV_OFFS_NUM_VQ, num_vq, - MEMTXATTRS_UNSPECIFIED, NULL); - - /* Sync virtqueues */ - for (i = 0; i < num_vq; i++) { - ram_addr_t vq = (dev->dev_offs + VIRTIO_DEV_OFFS_CONFIG) + - (i * VIRTIO_VQCONFIG_LEN); - ram_addr_t vring; - - vring = s390_virtio_next_ring(bus); - virtio_queue_set_addr(dev->vdev, i, vring); - virtio_queue_set_vector(dev->vdev, i, i); - address_space_stq_be(&address_space_memory, - vq + VIRTIO_VQCONFIG_OFFS_ADDRESS, vring, - MEMTXATTRS_UNSPECIFIED, NULL); - address_space_stw_be(&address_space_memory, - vq + VIRTIO_VQCONFIG_OFFS_NUM, - virtio_queue_get_num(dev->vdev, i), - MEMTXATTRS_UNSPECIFIED, - NULL); - } - - cur_offs = dev->dev_offs; - cur_offs += VIRTIO_DEV_OFFS_CONFIG; - cur_offs += num_vq * VIRTIO_VQCONFIG_LEN; - - /* Sync feature bitmap */ - address_space_stl_le(&address_space_memory, cur_offs, - dev->vdev->host_features, - MEMTXATTRS_UNSPECIFIED, NULL); - - dev->feat_offs = cur_offs + dev->feat_len; - cur_offs += dev->feat_len * 2; - - /* Sync config space */ - virtio_bus_get_vdev_config(&dev->bus, dev->vdev->config); - - cpu_physical_memory_write(cur_offs, - dev->vdev->config, dev->vdev->config_len); - cur_offs += dev->vdev->config_len; -} - -void s390_virtio_device_update_status(VirtIOS390Device *dev) -{ - VirtIODevice *vdev = dev->vdev; - uint32_t features; - - virtio_set_status(vdev, - address_space_ldub(&address_space_memory, - dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, - MEMTXATTRS_UNSPECIFIED, NULL)); - - /* Update guest supported feature bitmap */ - - features = bswap32(address_space_ldl_be(&address_space_memory, - dev->feat_offs, - MEMTXATTRS_UNSPECIFIED, NULL)); - virtio_set_features(vdev, features); -} - -/* Find a device by vring address */ -VirtIOS390Device *s390_virtio_bus_find_vring(VirtIOS390Bus *bus, - ram_addr_t mem, - int *vq_num) -{ - BusChild *kid; - int i; - - QTAILQ_FOREACH(kid, &bus->bus.children, sibling) { - VirtIOS390Device *dev = (VirtIOS390Device *)kid->child; - - for (i = 0; i < VIRTIO_S390_QUEUE_MAX; i++) { - if (!virtio_queue_get_addr(dev->vdev, i)) - break; - if (virtio_queue_get_addr(dev->vdev, i) == mem) { - if (vq_num) { - *vq_num = i; - } - return dev; - } - } - } - - return NULL; -} - -/* Find a device by device descriptor location */ -VirtIOS390Device *s390_virtio_bus_find_mem(VirtIOS390Bus *bus, ram_addr_t mem) -{ - BusChild *kid; - - QTAILQ_FOREACH(kid, &bus->bus.children, sibling) { - VirtIOS390Device *dev = (VirtIOS390Device *)kid->child; - if (dev->dev_offs == mem) { - return dev; - } - } - - return NULL; -} - -/* DeviceState to VirtIOS390Device. Note: used on datapath, - * be careful and test performance if you change this. - */ -static inline VirtIOS390Device *to_virtio_s390_device_fast(DeviceState *d) -{ - return container_of(d, VirtIOS390Device, qdev); -} - -/* DeviceState to VirtIOS390Device. TODO: use QOM. */ -static inline VirtIOS390Device *to_virtio_s390_device(DeviceState *d) -{ - return container_of(d, VirtIOS390Device, qdev); -} - -static void virtio_s390_notify(DeviceState *d, uint16_t vector) -{ - VirtIOS390Device *dev = to_virtio_s390_device_fast(d); - uint64_t token = s390_virtio_device_vq_token(dev, vector); - - s390_virtio_irq(0, token); -} - -static void virtio_s390_device_plugged(DeviceState *d, Error **errp) -{ - VirtIOS390Device *dev = to_virtio_s390_device(d); - VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - int n = virtio_get_num_queues(vdev); - - if (n > VIRTIO_S390_QUEUE_MAX) { - error_setg(errp, "The nubmer of virtqueues %d " - "exceeds s390 limit %d", n, - VIRTIO_S390_QUEUE_MAX); - } -} - -/**************** S390 Virtio Bus Device Descriptions *******************/ - -static void s390_virtio_net_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); - - k->realize = s390_virtio_net_realize; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); -} - -static const TypeInfo s390_virtio_net = { - .name = TYPE_VIRTIO_NET_S390, - .parent = TYPE_VIRTIO_S390_DEVICE, - .instance_size = sizeof(VirtIONetS390), - .instance_init = s390_virtio_net_instance_init, - .class_init = s390_virtio_net_class_init, -}; - -static void s390_virtio_blk_class_init(ObjectClass *klass, void *data) -{ - VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = s390_virtio_blk_realize; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo s390_virtio_blk = { - .name = "virtio-blk-s390", - .parent = TYPE_VIRTIO_S390_DEVICE, - .instance_size = sizeof(VirtIOBlkS390), - .instance_init = s390_virtio_blk_instance_init, - .class_init = s390_virtio_blk_class_init, -}; - -static Property s390_virtio_serial_properties[] = { - DEFINE_PROP_END_OF_LIST(), -}; - -static void s390_virtio_serial_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); - - k->realize = s390_virtio_serial_realize; - dc->props = s390_virtio_serial_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static const TypeInfo s390_virtio_serial = { - .name = TYPE_VIRTIO_SERIAL_S390, - .parent = TYPE_VIRTIO_S390_DEVICE, - .instance_size = sizeof(VirtIOSerialS390), - .instance_init = s390_virtio_serial_instance_init, - .class_init = s390_virtio_serial_class_init, -}; - -static void s390_virtio_rng_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); - - k->realize = s390_virtio_rng_realize; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static const TypeInfo s390_virtio_rng = { - .name = TYPE_VIRTIO_RNG_S390, - .parent = TYPE_VIRTIO_S390_DEVICE, - .instance_size = sizeof(VirtIORNGS390), - .instance_init = s390_virtio_rng_instance_init, - .class_init = s390_virtio_rng_class_init, -}; - -static void s390_virtio_busdev_realize(DeviceState *dev, Error **errp) -{ - VirtIOS390Device *_dev = (VirtIOS390Device *)dev; - VirtIOS390DeviceClass *_info = VIRTIO_S390_DEVICE_GET_CLASS(dev); - - virtio_s390_bus_new(&_dev->bus, sizeof(_dev->bus), _dev); - - _info->realize(_dev, errp); -} - -static void s390_virtio_busdev_reset(DeviceState *dev) -{ - VirtIOS390Device *_dev = (VirtIOS390Device *)dev; - - virtio_reset(_dev->vdev); -} - -static void virtio_s390_device_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = s390_virtio_busdev_realize; - dc->bus_type = TYPE_S390_VIRTIO_BUS; - dc->reset = s390_virtio_busdev_reset; -} - -static const TypeInfo virtio_s390_device_info = { - .name = TYPE_VIRTIO_S390_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(VirtIOS390Device), - .class_init = virtio_s390_device_class_init, - .class_size = sizeof(VirtIOS390DeviceClass), - .abstract = true, -}; - -static void s390_virtio_scsi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); - - k->realize = s390_virtio_scsi_realize; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo s390_virtio_scsi = { - .name = TYPE_VIRTIO_SCSI_S390, - .parent = TYPE_VIRTIO_S390_DEVICE, - .instance_size = sizeof(VirtIOSCSIS390), - .instance_init = s390_virtio_scsi_instance_init, - .class_init = s390_virtio_scsi_class_init, -}; - -#ifdef CONFIG_VHOST_SCSI -static void s390_vhost_scsi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); - - k->realize = s390_vhost_scsi_realize; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo s390_vhost_scsi = { - .name = TYPE_VHOST_SCSI_S390, - .parent = TYPE_VIRTIO_S390_DEVICE, - .instance_size = sizeof(VHostSCSIS390), - .instance_init = s390_vhost_scsi_instance_init, - .class_init = s390_vhost_scsi_class_init, -}; -#endif - -/***************** S390 Virtio Bus Bridge Device *******************/ -/* Only required to have the virtio bus as child in the system bus */ - -static int s390_virtio_bridge_init(SysBusDevice *dev) -{ - /* nothing */ - return 0; -} - -static void s390_virtio_bridge_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->init = s390_virtio_bridge_init; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); -} - -static const TypeInfo s390_virtio_bridge_info = { - .name = "s390-virtio-bridge", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SysBusDevice), - .class_init = s390_virtio_bridge_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; - -/* virtio-s390-bus */ - -static void virtio_s390_bus_new(VirtioBusState *bus, size_t bus_size, - VirtIOS390Device *dev) -{ - DeviceState *qdev = DEVICE(dev); - char virtio_bus_name[] = "virtio-bus"; - - qbus_create_inplace(bus, bus_size, TYPE_VIRTIO_S390_BUS, - qdev, virtio_bus_name); -} - -static void virtio_s390_bus_class_init(ObjectClass *klass, void *data) -{ - VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); - BusClass *bus_class = BUS_CLASS(klass); - bus_class->max_dev = 1; - k->notify = virtio_s390_notify; - k->device_plugged = virtio_s390_device_plugged; -} - -static const TypeInfo virtio_s390_bus_info = { - .name = TYPE_VIRTIO_S390_BUS, - .parent = TYPE_VIRTIO_BUS, - .instance_size = sizeof(VirtioS390BusState), - .class_init = virtio_s390_bus_class_init, -}; - -static void s390_virtio_register_types(void) -{ - type_register_static(&virtio_s390_bus_info); - type_register_static(&s390_virtio_bus_info); - type_register_static(&virtio_s390_device_info); - type_register_static(&s390_virtio_serial); - type_register_static(&s390_virtio_blk); - type_register_static(&s390_virtio_net); - type_register_static(&s390_virtio_scsi); -#ifdef CONFIG_VHOST_SCSI - type_register_static(&s390_vhost_scsi); -#endif - type_register_static(&s390_virtio_rng); - type_register_static(&s390_virtio_bridge_info); -} - -type_init(s390_virtio_register_types) diff --git a/qemu/hw/s390x/s390-virtio-bus.h b/qemu/hw/s390x/s390-virtio-bus.h deleted file mode 100644 index 7ad295e68..000000000 --- a/qemu/hw/s390x/s390-virtio-bus.h +++ /dev/null @@ -1,186 +0,0 @@ -/* - * QEMU S390x VirtIO BUS definitions - * - * Copyright (c) 2009 Alexander Graf - * - * 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 . - */ -#ifndef HW_S390_VIRTIO_BUS_H -#define HW_S390_VIRTIO_BUS_H 1 - -#include - -#include "standard-headers/asm-s390/kvm_virtio.h" -#include "standard-headers/linux/virtio_ring.h" -#include "hw/virtio/virtio-blk.h" -#include "hw/virtio/virtio-net.h" -#include "hw/virtio/virtio-rng.h" -#include "hw/virtio/virtio-serial.h" -#include "hw/virtio/virtio-scsi.h" -#include "hw/virtio/virtio-bus.h" -#ifdef CONFIG_VHOST_SCSI -#include "hw/virtio/vhost-scsi.h" -#endif - -typedef struct kvm_device_desc KvmDeviceDesc; - -#define VIRTIO_DEV_OFFS_TYPE offsetof(KvmDeviceDesc, type) -#define VIRTIO_DEV_OFFS_NUM_VQ offsetof(KvmDeviceDesc, num_vq) -#define VIRTIO_DEV_OFFS_FEATURE_LEN offsetof(KvmDeviceDesc, feature_len) -#define VIRTIO_DEV_OFFS_CONFIG_LEN offsetof(KvmDeviceDesc, config_len) -#define VIRTIO_DEV_OFFS_STATUS offsetof(KvmDeviceDesc, status) -#define VIRTIO_DEV_OFFS_CONFIG offsetof(KvmDeviceDesc, config) - -typedef struct kvm_vqconfig KvmVqConfig; -#define VIRTIO_VQCONFIG_OFFS_TOKEN offsetof(KvmVqConfig,token) /* 64 bit */ -#define VIRTIO_VQCONFIG_OFFS_ADDRESS offsetof(KvmVqConfig, address) /* 64 bit */ -#define VIRTIO_VQCONFIG_OFFS_NUM offsetof(KvmVqConfig, num) /* 16 bit */ -#define VIRTIO_VQCONFIG_LEN sizeof(KvmVqConfig) - -#define VIRTIO_RING_LEN (TARGET_PAGE_SIZE * 3) -#define VIRTIO_VRING_AVAIL_IDX_OFFS offsetof(struct vring_avail, idx) -#define VIRTIO_VRING_USED_IDX_OFFS offsetof(struct vring_used, idx) -#define S390_DEVICE_PAGES 512 - -#define TYPE_VIRTIO_S390_DEVICE "virtio-s390-device" -#define VIRTIO_S390_DEVICE(obj) \ - OBJECT_CHECK(VirtIOS390Device, (obj), TYPE_VIRTIO_S390_DEVICE) -#define VIRTIO_S390_DEVICE_CLASS(klass) \ - OBJECT_CLASS_CHECK(VirtIOS390DeviceClass, (klass), TYPE_VIRTIO_S390_DEVICE) -#define VIRTIO_S390_DEVICE_GET_CLASS(obj) \ - OBJECT_GET_CLASS(VirtIOS390DeviceClass, (obj), TYPE_VIRTIO_S390_DEVICE) - -#define TYPE_S390_VIRTIO_BUS "s390-virtio-bus" -#define S390_VIRTIO_BUS(obj) \ - OBJECT_CHECK(VirtIOS390Bus, (obj), TYPE_S390_VIRTIO_BUS) - -/* virtio-s390-bus */ - -typedef struct VirtioBusState VirtioS390BusState; -typedef struct VirtioBusClass VirtioS390BusClass; - -#define TYPE_VIRTIO_S390_BUS "virtio-s390-bus" -#define VIRTIO_S390_BUS(obj) \ - OBJECT_CHECK(VirtioS390BusState, (obj), TYPE_VIRTIO_S390_BUS) -#define VIRTIO_S390_BUS_GET_CLASS(obj) \ - OBJECT_GET_CLASS(VirtioS390BusClass, obj, TYPE_VIRTIO_S390_BUS) -#define VIRTIO_S390_BUS_CLASS(klass) \ - OBJECT_CLASS_CHECK(VirtioS390BusClass, klass, TYPE_VIRTIO_S390_BUS) - - -typedef struct VirtIOS390Device VirtIOS390Device; - -typedef struct VirtIOS390DeviceClass { - DeviceClass qdev; - void (*realize)(VirtIOS390Device *dev, Error **errp); -} VirtIOS390DeviceClass; - -struct VirtIOS390Device { - DeviceState qdev; - ram_addr_t dev_offs; - ram_addr_t feat_offs; - uint8_t feat_len; - VirtIODevice *vdev; - VirtioBusState bus; -}; - -typedef struct VirtIOS390Bus { - BusState bus; - - VirtIOS390Device *console; - ram_addr_t dev_page; - ram_addr_t dev_offs; - ram_addr_t next_ring; -} VirtIOS390Bus; - - -void s390_virtio_device_update_status(VirtIOS390Device *dev); - -VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size); - -VirtIOS390Device *s390_virtio_bus_find_vring(VirtIOS390Bus *bus, - ram_addr_t mem, int *vq_num); -VirtIOS390Device *s390_virtio_bus_find_mem(VirtIOS390Bus *bus, ram_addr_t mem); -void s390_virtio_device_sync(VirtIOS390Device *dev); -void s390_virtio_reset_idx(VirtIOS390Device *dev); - -/* virtio-blk-s390 */ - -#define TYPE_VIRTIO_BLK_S390 "virtio-blk-s390" -#define VIRTIO_BLK_S390(obj) \ - OBJECT_CHECK(VirtIOBlkS390, (obj), TYPE_VIRTIO_BLK_S390) - -typedef struct VirtIOBlkS390 { - VirtIOS390Device parent_obj; - VirtIOBlock vdev; -} VirtIOBlkS390; - -/* virtio-scsi-s390 */ - -#define TYPE_VIRTIO_SCSI_S390 "virtio-scsi-s390" -#define VIRTIO_SCSI_S390(obj) \ - OBJECT_CHECK(VirtIOSCSIS390, (obj), TYPE_VIRTIO_SCSI_S390) - -typedef struct VirtIOSCSIS390 { - VirtIOS390Device parent_obj; - VirtIOSCSI vdev; -} VirtIOSCSIS390; - -/* virtio-serial-s390 */ - -#define TYPE_VIRTIO_SERIAL_S390 "virtio-serial-s390" -#define VIRTIO_SERIAL_S390(obj) \ - OBJECT_CHECK(VirtIOSerialS390, (obj), TYPE_VIRTIO_SERIAL_S390) - -typedef struct VirtIOSerialS390 { - VirtIOS390Device parent_obj; - VirtIOSerial vdev; -} VirtIOSerialS390; - -/* virtio-net-s390 */ - -#define TYPE_VIRTIO_NET_S390 "virtio-net-s390" -#define VIRTIO_NET_S390(obj) \ - OBJECT_CHECK(VirtIONetS390, (obj), TYPE_VIRTIO_NET_S390) - -typedef struct VirtIONetS390 { - VirtIOS390Device parent_obj; - VirtIONet vdev; -} VirtIONetS390; - -/* vhost-scsi-s390 */ - -#ifdef CONFIG_VHOST_SCSI -#define TYPE_VHOST_SCSI_S390 "vhost-scsi-s390" -#define VHOST_SCSI_S390(obj) \ - OBJECT_CHECK(VHostSCSIS390, (obj), TYPE_VHOST_SCSI_S390) - -typedef struct VHostSCSIS390 { - VirtIOS390Device parent_obj; - VHostSCSI vdev; -} VHostSCSIS390; -#endif - -/* virtio-rng-s390 */ - -#define TYPE_VIRTIO_RNG_S390 "virtio-rng-s390" -#define VIRTIO_RNG_S390(obj) \ - OBJECT_CHECK(VirtIORNGS390, (obj), TYPE_VIRTIO_RNG_S390) - -typedef struct VirtIORNGS390 { - VirtIOS390Device parent_obj; - VirtIORNG vdev; -} VirtIORNGS390; - -#endif diff --git a/qemu/hw/s390x/s390-virtio-ccw.c b/qemu/hw/s390x/s390-virtio-ccw.c index 4c51d1a5b..e3df9c78b 100644 --- a/qemu/hw/s390x/s390-virtio-ccw.c +++ b/qemu/hw/s390x/s390-virtio-ccw.c @@ -9,6 +9,10 @@ * directory. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/boards.h" #include "exec/address-spaces.h" #include "s390-virtio.h" @@ -19,41 +23,27 @@ #include "virtio-ccw.h" #include "qemu/config-file.h" #include "s390-pci-bus.h" +#include "hw/s390x/storage-keys.h" +#include "hw/compat.h" +#include "hw/s390x/s390-virtio-ccw.h" + +static const char *const reset_dev_types[] = { + "virtual-css-bridge", + "s390-sclp-event-facility", + "s390-flic", + "diag288", +}; -#define TYPE_S390_CCW_MACHINE "s390-ccw-machine" - -#define S390_CCW_MACHINE(obj) \ - OBJECT_CHECK(S390CcwMachineState, (obj), TYPE_S390_CCW_MACHINE) - -typedef struct S390CcwMachineState { - /*< private >*/ - MachineState parent_obj; - - /*< public >*/ - bool aes_key_wrap; - bool dea_key_wrap; -} S390CcwMachineState; - -void io_subsystem_reset(void) +void subsystem_reset(void) { - DeviceState *css, *sclp, *flic, *diag288; + DeviceState *dev; + int i; - css = DEVICE(object_resolve_path_type("", "virtual-css-bridge", NULL)); - if (css) { - qdev_reset_all(css); - } - sclp = DEVICE(object_resolve_path_type("", - "s390-sclp-event-facility", NULL)); - if (sclp) { - qdev_reset_all(sclp); - } - flic = DEVICE(object_resolve_path_type("", "s390-flic", NULL)); - if (flic) { - qdev_reset_all(flic); - } - diag288 = DEVICE(object_resolve_path_type("", "diag288", NULL)); - if (diag288) { - qdev_reset_all(diag288); + for (i = 0; i < ARRAY_SIZE(reset_dev_types); i++) { + dev = DEVICE(object_resolve_path_type("", reset_dev_types[i], NULL)); + if (dev) { + qdev_reset_all(dev); + } } } @@ -99,58 +89,30 @@ static void virtio_ccw_register_hcalls(void) virtio_ccw_hcall_early_printk); } -static void ccw_init(MachineState *machine) +void s390_memory_init(ram_addr_t mem_size) { - ram_addr_t my_ram_size = machine->ram_size; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); - sclpMemoryHotplugDev *mhd = init_sclp_memory_hotplug_dev(); - uint8_t *storage_keys; + + /* allocate RAM for core */ + memory_region_allocate_system_memory(ram, NULL, "s390.ram", mem_size); + memory_region_add_subregion(sysmem, 0, ram); + + /* Initialize storage key device */ + s390_skeys_init(); +} + +static void ccw_init(MachineState *machine) +{ int ret; VirtualCssBus *css_bus; DeviceState *dev; - QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory"), NULL); - ram_addr_t pad_size = 0; - ram_addr_t maxmem = qemu_opt_get_size(opts, "maxmem", my_ram_size); - ram_addr_t standby_mem_size = maxmem - my_ram_size; - uint64_t kvm_limit; - - /* The storage increment size is a multiple of 1M and is a power of 2. - * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer. - * The variable 'mhd->increment_size' is an exponent of 2 that can be - * used to calculate the size (in bytes) of an increment. */ - mhd->increment_size = 20; - while ((my_ram_size >> mhd->increment_size) > MAX_STORAGE_INCREMENTS) { - mhd->increment_size++; - } - while ((standby_mem_size >> mhd->increment_size) > MAX_STORAGE_INCREMENTS) { - mhd->increment_size++; - } - /* The core and standby memory areas need to be aligned with - * the increment size. In effect, this can cause the - * user-specified memory size to be rounded down to align - * with the nearest increment boundary. */ - standby_mem_size = standby_mem_size >> mhd->increment_size - << mhd->increment_size; - my_ram_size = my_ram_size >> mhd->increment_size - << mhd->increment_size; - - /* let's propagate the changed ram size into the global variable. */ - ram_size = my_ram_size; - machine->maxram_size = my_ram_size + standby_mem_size; - - ret = s390_set_memory_limit(machine->maxram_size, &kvm_limit); - if (ret == -E2BIG) { - hw_error("qemu: host supports a maximum of %" PRIu64 " GB", - kvm_limit >> 30); - } else if (ret) { - hw_error("qemu: setting the guest size failed"); - } + s390_sclp_init(); + s390_memory_init(machine->ram_size); /* get a BUS */ css_bus = virtual_css_bus_init(); - s390_sclp_init(); s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline, machine->initrd_filename, "s390-ccw.img", true); s390_flic_init(); @@ -163,27 +125,8 @@ static void ccw_init(MachineState *machine) /* register hypercalls */ virtio_ccw_register_hcalls(); - /* allocate RAM for core */ - memory_region_init_ram(ram, NULL, "s390.ram", my_ram_size, &error_abort); - vmstate_register_ram_global(ram); - memory_region_add_subregion(sysmem, 0, ram); - - /* If the size of ram is not on a MEM_SECTION_SIZE boundary, - calculate the pad size necessary to force this boundary. */ - if (standby_mem_size) { - if (my_ram_size % MEM_SECTION_SIZE) { - pad_size = MEM_SECTION_SIZE - my_ram_size % MEM_SECTION_SIZE; - } - my_ram_size += standby_mem_size + pad_size; - mhd->pad_size = pad_size; - mhd->standby_mem_size = standby_mem_size; - } - - /* allocate storage keys */ - storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE); - /* init CPUs */ - s390_init_cpus(machine->cpu_model, storage_keys); + s390_init_cpus(machine); if (kvm_enabled()) { kvm_s390_enable_css_support(s390_cpu_addr2state(0)); @@ -203,12 +146,54 @@ static void ccw_init(MachineState *machine) gtod_save, gtod_load, kvm_state); } +static void s390_cpu_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + gchar *name; + S390CPU *cpu = S390_CPU(dev); + CPUState *cs = CPU(dev); + + name = g_strdup_printf("cpu[%i]", cpu->env.cpu_num); + object_property_set_link(OBJECT(hotplug_dev), OBJECT(cs), name, + errp); + g_free(name); +} + +static void s390_machine_device_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + s390_cpu_plug(hotplug_dev, dev, errp); + } +} + +static HotplugHandler *s390_get_hotplug_handler(MachineState *machine, + DeviceState *dev) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + return HOTPLUG_HANDLER(machine); + } + return NULL; +} + +static void s390_hot_add_cpu(const int64_t id, Error **errp) +{ + MachineState *machine = MACHINE(qdev_get_machine()); + Error *err = NULL; + + s390x_new_cpu(machine->cpu_model, id, &err); + error_propagate(errp, err); +} + static void ccw_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); mc->init = ccw_init; + mc->reset = s390_machine_reset; + mc->hot_add_cpu = s390_hot_add_cpu; mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; mc->no_floppy = 1; @@ -217,6 +202,8 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) mc->no_sdcard = 1; mc->use_sclp = 1; mc->max_cpus = 255; + mc->get_hotplug_handler = s390_get_hotplug_handler; + hc->plug = s390_machine_device_plug; nc->nmi_monitor_handler = s390_nmi; } @@ -278,30 +265,117 @@ static const TypeInfo ccw_machine_info = { .class_init = ccw_machine_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_NMI }, + { TYPE_HOTPLUG_HANDLER}, { } }, }; -static void ccw_machine_2_4_class_init(ObjectClass *oc, void *data) +#define DEFINE_CCW_MACHINE(suffix, verstr, latest) \ + static void ccw_machine_##suffix##_class_init(ObjectClass *oc, \ + void *data) \ + { \ + MachineClass *mc = MACHINE_CLASS(oc); \ + ccw_machine_##suffix##_class_options(mc); \ + mc->desc = "VirtIO-ccw based S390 machine v" verstr; \ + if (latest) { \ + mc->alias = "s390-ccw-virtio"; \ + mc->is_default = 1; \ + } \ + } \ + static void ccw_machine_##suffix##_instance_init(Object *obj) \ + { \ + MachineState *machine = MACHINE(obj); \ + ccw_machine_##suffix##_instance_options(machine); \ + } \ + static const TypeInfo ccw_machine_##suffix##_info = { \ + .name = MACHINE_TYPE_NAME("s390-ccw-virtio-" verstr), \ + .parent = TYPE_S390_CCW_MACHINE, \ + .class_init = ccw_machine_##suffix##_class_init, \ + .instance_init = ccw_machine_##suffix##_instance_init, \ + }; \ + static void ccw_machine_register_##suffix(void) \ + { \ + type_register_static(&ccw_machine_##suffix##_info); \ + } \ + type_init(ccw_machine_register_##suffix) + +#define CCW_COMPAT_2_5 \ + HW_COMPAT_2_5 + +#define CCW_COMPAT_2_4 \ + CCW_COMPAT_2_5 \ + HW_COMPAT_2_4 \ + {\ + .driver = TYPE_S390_SKEYS,\ + .property = "migration-enabled",\ + .value = "off",\ + },{\ + .driver = "virtio-blk-ccw",\ + .property = "max_revision",\ + .value = "0",\ + },{\ + .driver = "virtio-balloon-ccw",\ + .property = "max_revision",\ + .value = "0",\ + },{\ + .driver = "virtio-serial-ccw",\ + .property = "max_revision",\ + .value = "0",\ + },{\ + .driver = "virtio-9p-ccw",\ + .property = "max_revision",\ + .value = "0",\ + },{\ + .driver = "virtio-rng-ccw",\ + .property = "max_revision",\ + .value = "0",\ + },{\ + .driver = "virtio-net-ccw",\ + .property = "max_revision",\ + .value = "0",\ + },{\ + .driver = "virtio-scsi-ccw",\ + .property = "max_revision",\ + .value = "0",\ + },{\ + .driver = "vhost-scsi-ccw",\ + .property = "max_revision",\ + .value = "0",\ + }, + +static void ccw_machine_2_6_instance_options(MachineState *machine) { - MachineClass *mc = MACHINE_CLASS(oc); +} - mc->name = "s390-ccw-virtio-2.4"; - mc->alias = "s390-ccw-virtio"; - mc->desc = "VirtIO-ccw based S390 machine v2.4"; - mc->is_default = 1; +static void ccw_machine_2_6_class_options(MachineClass *mc) +{ } +DEFINE_CCW_MACHINE(2_6, "2.6", true); -static const TypeInfo ccw_machine_2_4_info = { - .name = TYPE_S390_CCW_MACHINE "2.4", - .parent = TYPE_S390_CCW_MACHINE, - .class_init = ccw_machine_2_4_class_init, -}; +static void ccw_machine_2_5_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_2_5_class_options(MachineClass *mc) +{ + SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_5); +} +DEFINE_CCW_MACHINE(2_5, "2.5", false); + +static void ccw_machine_2_4_instance_options(MachineState *machine) +{ + ccw_machine_2_5_instance_options(machine); +} + +static void ccw_machine_2_4_class_options(MachineClass *mc) +{ + SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_4); +} +DEFINE_CCW_MACHINE(2_4, "2.4", false); static void ccw_machine_register_types(void) { type_register_static(&ccw_machine_info); - type_register_static(&ccw_machine_2_4_info); } type_init(ccw_machine_register_types) diff --git a/qemu/hw/s390x/s390-virtio-hcall.c b/qemu/hw/s390x/s390-virtio-hcall.c index c7bdc2005..23d67d617 100644 --- a/qemu/hw/s390x/s390-virtio-hcall.c +++ b/qemu/hw/s390x/s390-virtio-hcall.c @@ -9,6 +9,7 @@ * directory. */ +#include "qemu/osdep.h" #include "cpu.h" #include "hw/s390x/s390-virtio.h" diff --git a/qemu/hw/s390x/s390-virtio.c b/qemu/hw/s390x/s390-virtio.c index 1284e77b2..544c61643 100644 --- a/qemu/hw/s390x/s390-virtio.c +++ b/qemu/hw/s390x/s390-virtio.c @@ -21,8 +21,11 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "qapi/qmp/qerror.h" +#include "qemu/error-report.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "sysemu/sysemu.h" @@ -30,14 +33,15 @@ #include "hw/boards.h" #include "hw/loader.h" #include "hw/virtio/virtio.h" -#include "hw/sysbus.h" #include "sysemu/kvm.h" #include "exec/address-spaces.h" +#include "sysemu/qtest.h" -#include "hw/s390x/s390-virtio-bus.h" #include "hw/s390x/sclp.h" #include "hw/s390x/s390_flic.h" #include "hw/s390x/s390-virtio.h" +#include "hw/s390x/storage-keys.h" +#include "hw/s390x/ipl.h" #include "cpu.h" //#define DEBUG_S390 @@ -51,94 +55,20 @@ #endif #define MAX_BLK_DEVS 10 -#define ZIPL_FILENAME "s390-zipl.rom" -#define TYPE_S390_MACHINE "s390-machine" #define S390_TOD_CLOCK_VALUE_MISSING 0x00 #define S390_TOD_CLOCK_VALUE_PRESENT 0x01 -static VirtIOS390Bus *s390_bus; -static S390CPU **ipi_states; +static S390CPU **cpu_states; S390CPU *s390_cpu_addr2state(uint16_t cpu_addr) { - if (cpu_addr >= smp_cpus) { + if (cpu_addr >= max_cpus) { return NULL; } - return ipi_states[cpu_addr]; -} - -static int s390_virtio_hcall_notify(const uint64_t *args) -{ - uint64_t mem = args[0]; - int r = 0, i; - - if (mem > ram_size) { - VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus, mem, &i); - if (dev) { - /* - * Older kernels will use the virtqueue before setting DRIVER_OK. - * In this case the feature bits are not yet up to date, meaning - * that several funny things can happen, e.g. the guest thinks - * EVENT_IDX is on and QEMU thinks it is off. Let's force a feature - * and status sync. - */ - if (!(dev->vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { - s390_virtio_device_update_status(dev); - } - virtio_queue_notify(dev->vdev, i); - } else { - r = -EINVAL; - } - } else { - /* Early printk */ - } - return r; -} - -static int s390_virtio_hcall_reset(const uint64_t *args) -{ - uint64_t mem = args[0]; - VirtIOS390Device *dev; - - dev = s390_virtio_bus_find_mem(s390_bus, mem); - if (dev == NULL) { - return -EINVAL; - } - virtio_reset(dev->vdev); - address_space_stb(&address_space_memory, - dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0, - MEMTXATTRS_UNSPECIFIED, NULL); - s390_virtio_device_sync(dev); - s390_virtio_reset_idx(dev); - - return 0; -} - -static int s390_virtio_hcall_set_status(const uint64_t *args) -{ - uint64_t mem = args[0]; - int r = 0; - VirtIOS390Device *dev; - - dev = s390_virtio_bus_find_mem(s390_bus, mem); - if (dev) { - s390_virtio_device_update_status(dev); - } else { - r = -EINVAL; - } - return r; -} - -static void s390_virtio_register_hcalls(void) -{ - s390_register_virtio_hypercall(KVM_S390_VIRTIO_NOTIFY, - s390_virtio_hcall_notify); - s390_register_virtio_hypercall(KVM_S390_VIRTIO_RESET, - s390_virtio_hcall_reset); - s390_register_virtio_hypercall(KVM_S390_VIRTIO_SET_STATUS, - s390_virtio_hcall_set_status); + /* Fast lookup via CPU ID */ + return cpu_states[cpu_addr]; } void s390_init_ipl_dev(const char *kernel_filename, @@ -147,9 +77,9 @@ void s390_init_ipl_dev(const char *kernel_filename, const char *firmware, bool enforce_bios) { - DeviceState *dev; + Object *new = object_new(TYPE_S390_IPL); + DeviceState *dev = DEVICE(new); - dev = qdev_create(NULL, "s390-ipl"); if (kernel_filename) { qdev_prop_set_string(dev, "kernel", kernel_filename); } @@ -159,32 +89,35 @@ void s390_init_ipl_dev(const char *kernel_filename, qdev_prop_set_string(dev, "cmdline", kernel_cmdline); qdev_prop_set_string(dev, "firmware", firmware); qdev_prop_set_bit(dev, "enforce_bios", enforce_bios); - object_property_add_child(qdev_get_machine(), "s390-ipl", - OBJECT(dev), NULL); + object_property_add_child(qdev_get_machine(), TYPE_S390_IPL, + new, NULL); + object_unref(new); qdev_init_nofail(dev); } -void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys) +void s390_init_cpus(MachineState *machine) { int i; + gchar *name; - if (cpu_model == NULL) { - cpu_model = "host"; + if (machine->cpu_model == NULL) { + machine->cpu_model = "host"; } - ipi_states = g_malloc(sizeof(S390CPU *) * smp_cpus); - - for (i = 0; i < smp_cpus; i++) { - S390CPU *cpu; - CPUState *cs; + cpu_states = g_new0(S390CPU *, max_cpus); - cpu = cpu_s390x_init(cpu_model); - cs = CPU(cpu); + for (i = 0; i < max_cpus; i++) { + name = g_strdup_printf("cpu[%i]", i); + object_property_add_link(OBJECT(machine), name, TYPE_S390_CPU, + (Object **) &cpu_states[i], + object_property_allow_set_link, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + &error_abort); + g_free(name); + } - ipi_states[i] = cpu; - cs->halted = 1; - cs->exception_index = EXCP_HLT; - cpu->env.storage_keys = storage_keys; + for (i = 0; i < smp_cpus; i++) { + s390x_new_cpu(machine->cpu_model, i, &error_fatal); } } @@ -201,10 +134,7 @@ void s390_create_virtio_net(BusState *bus, const char *name) nd->model = g_strdup("virtio"); } - if (strcmp(nd->model, "virtio")) { - fprintf(stderr, "S390 only supports VirtIO nics\n"); - exit(1); - } + qemu_check_nic_model(nd, "virtio"); dev = qdev_create(bus, name); qdev_set_nic_properties(dev, nd); @@ -257,68 +187,6 @@ int gtod_load(QEMUFile *f, void *opaque, int version_id) return 0; } -/* PC hardware initialisation */ -static void s390_init(MachineState *machine) -{ - ram_addr_t my_ram_size = machine->ram_size; - MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - int increment_size = 20; - uint8_t *storage_keys; - void *virtio_region; - hwaddr virtio_region_len; - hwaddr virtio_region_start; - - /* - * The storage increment size is a multiple of 1M and is a power of 2. - * The number of storage increments must be MAX_STORAGE_INCREMENTS or - * fewer. - */ - while ((my_ram_size >> increment_size) > MAX_STORAGE_INCREMENTS) { - increment_size++; - } - my_ram_size = my_ram_size >> increment_size << increment_size; - - /* let's propagate the changed ram size into the global variable. */ - ram_size = my_ram_size; - - /* get a BUS */ - s390_bus = s390_virtio_bus_init(&my_ram_size); - s390_sclp_init(); - s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline, - machine->initrd_filename, ZIPL_FILENAME, false); - s390_flic_init(); - - /* register hypercalls */ - s390_virtio_register_hcalls(); - - /* allocate RAM */ - memory_region_init_ram(ram, NULL, "s390.ram", my_ram_size, &error_abort); - vmstate_register_ram_global(ram); - memory_region_add_subregion(sysmem, 0, ram); - - /* clear virtio region */ - virtio_region_len = my_ram_size - ram_size; - virtio_region_start = ram_size; - virtio_region = cpu_physical_memory_map(virtio_region_start, - &virtio_region_len, true); - memset(virtio_region, 0, virtio_region_len); - cpu_physical_memory_unmap(virtio_region, virtio_region_len, 1, - virtio_region_len); - - /* allocate storage keys */ - storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE); - - /* init CPUs */ - s390_init_cpus(machine->cpu_model, storage_keys); - - /* Create VirtIO network adapters */ - s390_create_virtio_net((BusState *)s390_bus, "virtio-net-s390"); - - /* Register savevm handler for guest TOD clock */ - register_savevm(NULL, "todclock", 0, 1, gtod_save, gtod_load, NULL); -} - void s390_nmi(NMIState *n, int cpu_index, Error **errp) { CPUState *cs = qemu_get_cpu(cpu_index); @@ -328,39 +196,15 @@ void s390_nmi(NMIState *n, int cpu_index, Error **errp) } } -static void s390_machine_class_init(ObjectClass *oc, void *data) +void s390_machine_reset(void) { - MachineClass *mc = MACHINE_CLASS(oc); - NMIClass *nc = NMI_CLASS(oc); - - mc->name = "s390-virtio"; - mc->alias = "s390"; - mc->desc = "VirtIO based S390 machine"; - mc->init = s390_init; - mc->block_default_type = IF_VIRTIO; - mc->max_cpus = 255; - mc->no_serial = 1; - mc->no_parallel = 1; - mc->use_virtcon = 1; - mc->no_floppy = 1; - mc->no_cdrom = 1; - mc->no_sdcard = 1; - nc->nmi_monitor_handler = s390_nmi; -} + S390CPU *ipl_cpu = S390_CPU(qemu_get_cpu(0)); -static const TypeInfo s390_machine_info = { - .name = TYPE_S390_MACHINE, - .parent = TYPE_MACHINE, - .class_init = s390_machine_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_NMI }, - { } - }, -}; - -static void s390_machine_register_types(void) -{ - type_register_static(&s390_machine_info); -} + qemu_devices_reset(); + s390_cmma_reset(); + s390_crypto_reset(); -type_init(s390_machine_register_types) + /* all cpus are stopped - configure and start the ipl cpu only */ + s390_ipl_prepare_cpu(ipl_cpu); + s390_cpu_set_state(CPU_STATE_OPERATING, ipl_cpu); +} diff --git a/qemu/hw/s390x/s390-virtio.h b/qemu/hw/s390x/s390-virtio.h index c84785395..ffd014cb5 100644 --- a/qemu/hw/s390x/s390-virtio.h +++ b/qemu/hw/s390x/s390-virtio.h @@ -19,7 +19,7 @@ typedef int (*s390_virtio_fn)(const uint64_t *args); void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn); -void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys); +void s390_init_cpus(MachineState *machine); void s390_init_ipl_dev(const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, @@ -27,4 +27,6 @@ void s390_init_ipl_dev(const char *kernel_filename, bool enforce_bios); void s390_create_virtio_net(BusState *bus, const char *name); void s390_nmi(NMIState *n, int cpu_index, Error **errp); +void s390_machine_reset(void); +void s390_memory_init(ram_addr_t mem_size); #endif diff --git a/qemu/hw/s390x/sclp.c b/qemu/hw/s390x/sclp.c index b3a6c5e5a..85dbe1b60 100644 --- a/qemu/hw/s390x/sclp.c +++ b/qemu/hw/s390x/sclp.c @@ -12,42 +12,34 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "sysemu/kvm.h" #include "exec/memory.h" #include "sysemu/sysemu.h" #include "exec/address-spaces.h" -#include "qemu/config-file.h" +#include "hw/boards.h" #include "hw/s390x/sclp.h" #include "hw/s390x/event-facility.h" #include "hw/s390x/s390-pci-bus.h" -static inline SCLPEventFacility *get_event_facility(void) +static inline SCLPDevice *get_sclp_device(void) { - ObjectProperty *op = object_property_find(qdev_get_machine(), - TYPE_SCLP_EVENT_FACILITY, - NULL); - assert(op); - return op->opaque; + return SCLP(object_resolve_path_type("", TYPE_SCLP, NULL)); } /* Provide information about the configuration, CPUs and storage */ -static void read_SCP_info(SCCB *sccb) +static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) { ReadInfo *read_info = (ReadInfo *) sccb; + MachineState *machine = MACHINE(qdev_get_machine()); sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); CPUState *cpu; int cpu_count = 0; int i = 0; - int increment_size = 20; int rnsize, rnmax; - QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory"), NULL); - int slots = qemu_opt_get_number(opts, "slots", 0); - int max_avail_slots = s390_get_memslot_count(kvm_state); - - if (slots > max_avail_slots) { - slots = max_avail_slots; - } + int slots = MIN(machine->ram_slots, s390_get_memslot_count(kvm_state)); CPU_FOREACH(cpu) { cpu_count++; @@ -66,23 +58,8 @@ static void read_SCP_info(SCCB *sccb) read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO | SCLP_HAS_PCI_RECONFIG); - /* - * The storage increment size is a multiple of 1M and is a power of 2. - * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer. - */ - while ((ram_size >> increment_size) > MAX_STORAGE_INCREMENTS) { - increment_size++; - } - rnmax = ram_size >> increment_size; - /* Memory Hotplug is only supported for the ccw machine type */ if (mhd) { - while ((mhd->standby_mem_size >> increment_size) > - MAX_STORAGE_INCREMENTS) { - increment_size++; - } - assert(increment_size == mhd->increment_size); - mhd->standby_subregion_size = MEM_SECTION_SIZE; /* Deduct the memory slot already used for core */ if (slots > 0) { @@ -108,13 +85,11 @@ static void read_SCP_info(SCCB *sccb) } mhd->padded_ram_size = ram_size + mhd->pad_size; mhd->rzm = 1 << mhd->increment_size; - rnmax = ((ram_size + mhd->standby_mem_size + mhd->pad_size) - >> mhd->increment_size); read_info->facilities |= cpu_to_be64(SCLP_FC_ASSIGN_ATTACH_READ_STOR); } - rnsize = 1 << (increment_size - 20); + rnsize = 1 << (sclp->increment_size - 20); if (rnsize <= 128) { read_info->rnsize = rnsize; } else { @@ -122,6 +97,7 @@ static void read_SCP_info(SCCB *sccb) read_info->rnsize2 = cpu_to_be32(rnsize); } + rnmax = machine->maxram_size >> sclp->increment_size; if (rnmax < 0x10000) { read_info->rnmax = cpu_to_be16(rnmax); } else { @@ -132,14 +108,17 @@ static void read_SCP_info(SCCB *sccb) sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); } -static void read_storage_element0_info(SCCB *sccb) +static void read_storage_element0_info(SCLPDevice *sclp, SCCB *sccb) { int i, assigned; int subincrement_id = SCLP_STARTING_SUBINCREMENT_ID; ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb; sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - assert(mhd); + if (!mhd) { + sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); + return; + } if ((ram_size >> mhd->increment_size) >= 0x10000) { sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION); @@ -158,12 +137,15 @@ static void read_storage_element0_info(SCCB *sccb) sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); } -static void read_storage_element1_info(SCCB *sccb) +static void read_storage_element1_info(SCLPDevice *sclp, SCCB *sccb) { ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb; sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - assert(mhd); + if (!mhd) { + sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); + return; + } if ((mhd->standby_mem_size >> mhd->increment_size) >= 0x10000) { sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION); @@ -179,13 +161,17 @@ static void read_storage_element1_info(SCCB *sccb) sccb->h.response_code = cpu_to_be16(SCLP_RC_STANDBY_READ_COMPLETION); } -static void attach_storage_element(SCCB *sccb, uint16_t element) +static void attach_storage_element(SCLPDevice *sclp, SCCB *sccb, + uint16_t element) { int i, assigned, subincrement_id; AttachStorageElement *attach_info = (AttachStorageElement *) sccb; sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - assert(mhd); + if (!mhd) { + sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); + return; + } if (element != 1) { sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); @@ -203,20 +189,26 @@ static void attach_storage_element(SCCB *sccb, uint16_t element) sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); } -static void assign_storage(SCCB *sccb) +static void assign_storage(SCLPDevice *sclp, SCCB *sccb) { MemoryRegion *mr = NULL; uint64_t this_subregion_size; AssignStorage *assign_info = (AssignStorage *) sccb; sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - assert(mhd); - ram_addr_t assign_addr = (assign_info->rn - 1) * mhd->rzm; + ram_addr_t assign_addr; MemoryRegion *sysmem = get_system_memory(); + if (!mhd) { + sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); + return; + } + assign_addr = (assign_info->rn - 1) * mhd->rzm; + if ((assign_addr % MEM_SECTION_SIZE == 0) && (assign_addr >= mhd->padded_ram_size)) { /* Re-use existing memory region if found */ mr = memory_region_find(sysmem, assign_addr, 1).mr; + memory_region_unref(mr); if (!mr) { MemoryRegion *standby_ram = g_new(MemoryRegion, 1); @@ -241,7 +233,13 @@ static void assign_storage(SCCB *sccb) this_subregion_size = mhd->standby_subregion_size; } - memory_region_init_ram(standby_ram, NULL, id, this_subregion_size, &error_abort); + memory_region_init_ram(standby_ram, NULL, id, this_subregion_size, + &error_fatal); + /* This is a hack to make memory hotunplug work again. Once we have + * subdevices, we have to unparent them when unassigning memory, + * instead of doing it via the ref count of the MemoryRegion. */ + object_ref(OBJECT(standby_ram)); + object_unparent(OBJECT(standby_ram)); vmstate_register_ram_global(standby_ram); memory_region_add_subregion(sysmem, offset, standby_ram); } @@ -252,15 +250,20 @@ static void assign_storage(SCCB *sccb) sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); } -static void unassign_storage(SCCB *sccb) +static void unassign_storage(SCLPDevice *sclp, SCCB *sccb) { MemoryRegion *mr = NULL; AssignStorage *assign_info = (AssignStorage *) sccb; sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - assert(mhd); - ram_addr_t unassign_addr = (assign_info->rn - 1) * mhd->rzm; + ram_addr_t unassign_addr; MemoryRegion *sysmem = get_system_memory(); + if (!mhd) { + sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); + return; + } + unassign_addr = (assign_info->rn - 1) * mhd->rzm; + /* if the addr is a multiple of 256 MB */ if ((unassign_addr % MEM_SECTION_SIZE == 0) && (unassign_addr >= mhd->padded_ram_size)) { @@ -269,6 +272,7 @@ static void unassign_storage(SCCB *sccb) /* find the specified memory region and destroy it */ mr = memory_region_find(sysmem, unassign_addr, 1).mr; + memory_region_unref(mr); if (mr) { int i; int is_removable = 1; @@ -287,8 +291,7 @@ static void unassign_storage(SCCB *sccb) } if (is_removable) { memory_region_del_subregion(sysmem, mr); - object_unparent(OBJECT(mr)); - g_free(mr); + object_unref(OBJECT(mr)); } } } @@ -296,7 +299,7 @@ static void unassign_storage(SCCB *sccb) } /* Provide information about the CPU */ -static void sclp_read_cpu_info(SCCB *sccb) +static void sclp_read_cpu_info(SCLPDevice *sclp, SCCB *sccb) { ReadCpuInfo *cpu_info = (ReadCpuInfo *) sccb; CPUState *cpu; @@ -323,34 +326,35 @@ static void sclp_read_cpu_info(SCCB *sccb) sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); } -static void sclp_execute(SCCB *sccb, uint32_t code) +static void sclp_execute(SCLPDevice *sclp, SCCB *sccb, uint32_t code) { - SCLPEventFacility *ef = get_event_facility(); + SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp); + SCLPEventFacility *ef = sclp->event_facility; SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef); switch (code & SCLP_CMD_CODE_MASK) { case SCLP_CMDW_READ_SCP_INFO: case SCLP_CMDW_READ_SCP_INFO_FORCED: - read_SCP_info(sccb); + sclp_c->read_SCP_info(sclp, sccb); break; case SCLP_CMDW_READ_CPU_INFO: - sclp_read_cpu_info(sccb); + sclp_c->read_cpu_info(sclp, sccb); break; case SCLP_READ_STORAGE_ELEMENT_INFO: if (code & 0xff00) { - read_storage_element1_info(sccb); + sclp_c->read_storage_element1_info(sclp, sccb); } else { - read_storage_element0_info(sccb); + sclp_c->read_storage_element0_info(sclp, sccb); } break; case SCLP_ATTACH_STORAGE_ELEMENT: - attach_storage_element(sccb, (code & 0xff00) >> 8); + sclp_c->attach_storage_element(sclp, sccb, (code & 0xff00) >> 8); break; case SCLP_ASSIGN_STORAGE: - assign_storage(sccb); + sclp_c->assign_storage(sclp, sccb); break; case SCLP_UNASSIGN_STORAGE: - unassign_storage(sccb); + sclp_c->unassign_storage(sclp, sccb); break; case SCLP_CMDW_CONFIGURE_PCI: s390_pci_sclp_configure(1, sccb); @@ -366,6 +370,8 @@ static void sclp_execute(SCCB *sccb, uint32_t code) int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code) { + SCLPDevice *sclp = get_sclp_device(); + SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp); int r = 0; SCCB work_sccb; @@ -400,20 +406,20 @@ int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code) goto out; } - sclp_execute((SCCB *)&work_sccb, code); + sclp_c->execute(sclp, (SCCB *)&work_sccb, code); cpu_physical_memory_write(sccb, &work_sccb, be16_to_cpu(work_sccb.h.length)); - sclp_service_interrupt(sccb); + sclp_c->service_interrupt(sclp, sccb); out: return r; } -void sclp_service_interrupt(uint32_t sccb) +static void service_interrupt(SCLPDevice *sclp, uint32_t sccb) { - SCLPEventFacility *ef = get_event_facility(); + SCLPEventFacility *ef = sclp->event_facility; SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef); uint32_t param = sccb & ~3; @@ -428,17 +434,149 @@ void sclp_service_interrupt(uint32_t sccb) s390_sclp_extint(param); } +void sclp_service_interrupt(uint32_t sccb) +{ + SCLPDevice *sclp = get_sclp_device(); + SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp); + + sclp_c->service_interrupt(sclp, sccb); +} + /* qemu object creation and initialization functions */ void s390_sclp_init(void) { - DeviceState *dev = qdev_create(NULL, TYPE_SCLP_EVENT_FACILITY); + Object *new = object_new(TYPE_SCLP); - object_property_add_child(qdev_get_machine(), TYPE_SCLP_EVENT_FACILITY, - OBJECT(dev), NULL); - qdev_init_nofail(dev); + object_property_add_child(qdev_get_machine(), TYPE_SCLP, new, + NULL); + object_unref(OBJECT(new)); + qdev_init_nofail(DEVICE(new)); } +static void sclp_realize(DeviceState *dev, Error **errp) +{ + MachineState *machine = MACHINE(qdev_get_machine()); + SCLPDevice *sclp = SCLP(dev); + Error *err = NULL; + uint64_t hw_limit; + int ret; + + object_property_set_bool(OBJECT(sclp->event_facility), true, "realized", + &err); + if (err) { + goto out; + } + /* + * qdev_device_add searches the sysbus for TYPE_SCLP_EVENTS_BUS. As long + * as we can't find a fitting bus via the qom tree, we have to add the + * event facility to the sysbus, so e.g. a sclp console can be created. + */ + qdev_set_parent_bus(DEVICE(sclp->event_facility), sysbus_get_default()); + + ret = s390_set_memory_limit(machine->maxram_size, &hw_limit); + if (ret == -E2BIG) { + error_setg(&err, "qemu: host supports a maximum of %" PRIu64 " GB", + hw_limit >> 30); + } else if (ret) { + error_setg(&err, "qemu: setting the guest size failed"); + } + +out: + error_propagate(errp, err); +} + +static void sclp_memory_init(SCLPDevice *sclp) +{ + MachineState *machine = MACHINE(qdev_get_machine()); + ram_addr_t initial_mem = machine->ram_size; + ram_addr_t max_mem = machine->maxram_size; + ram_addr_t standby_mem = max_mem - initial_mem; + ram_addr_t pad_mem = 0; + int increment_size = 20; + + /* The storage increment size is a multiple of 1M and is a power of 2. + * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer. + * The variable 'increment_size' is an exponent of 2 that can be + * used to calculate the size (in bytes) of an increment. */ + while ((initial_mem >> increment_size) > MAX_STORAGE_INCREMENTS) { + increment_size++; + } + if (machine->ram_slots) { + while ((standby_mem >> increment_size) > MAX_STORAGE_INCREMENTS) { + increment_size++; + } + } + sclp->increment_size = increment_size; + + /* The core and standby memory areas need to be aligned with + * the increment size. In effect, this can cause the + * user-specified memory size to be rounded down to align + * with the nearest increment boundary. */ + initial_mem = initial_mem >> increment_size << increment_size; + standby_mem = standby_mem >> increment_size << increment_size; + + /* If the size of ram is not on a MEM_SECTION_SIZE boundary, + calculate the pad size necessary to force this boundary. */ + if (machine->ram_slots && standby_mem) { + sclpMemoryHotplugDev *mhd = init_sclp_memory_hotplug_dev(); + + if (initial_mem % MEM_SECTION_SIZE) { + pad_mem = MEM_SECTION_SIZE - initial_mem % MEM_SECTION_SIZE; + } + mhd->increment_size = increment_size; + mhd->pad_size = pad_mem; + mhd->standby_mem_size = standby_mem; + } + machine->ram_size = initial_mem; + machine->maxram_size = initial_mem + pad_mem + standby_mem; + /* let's propagate the changed ram size into the global variable. */ + ram_size = initial_mem; +} + +static void sclp_init(Object *obj) +{ + SCLPDevice *sclp = SCLP(obj); + Object *new; + + new = object_new(TYPE_SCLP_EVENT_FACILITY); + object_property_add_child(obj, TYPE_SCLP_EVENT_FACILITY, new, NULL); + object_unref(new); + sclp->event_facility = EVENT_FACILITY(new); + + sclp_memory_init(sclp); +} + +static void sclp_class_init(ObjectClass *oc, void *data) +{ + SCLPDeviceClass *sc = SCLP_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->desc = "SCLP (Service-Call Logical Processor)"; + dc->realize = sclp_realize; + dc->hotpluggable = false; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + sc->read_SCP_info = read_SCP_info; + sc->read_storage_element0_info = read_storage_element0_info; + sc->read_storage_element1_info = read_storage_element1_info; + sc->attach_storage_element = attach_storage_element; + sc->assign_storage = assign_storage; + sc->unassign_storage = unassign_storage; + sc->read_cpu_info = sclp_read_cpu_info; + sc->execute = sclp_execute; + sc->service_interrupt = service_interrupt; +} + +static TypeInfo sclp_info = { + .name = TYPE_SCLP, + .parent = TYPE_DEVICE, + .instance_init = sclp_init, + .instance_size = sizeof(SCLPDevice), + .class_init = sclp_class_init, + .class_size = sizeof(SCLPDeviceClass), +}; + sclpMemoryHotplugDev *init_sclp_memory_hotplug_dev(void) { DeviceState *dev; @@ -475,5 +613,6 @@ static TypeInfo sclp_memory_hotplug_dev_info = { static void register_types(void) { type_register_static(&sclp_memory_hotplug_dev_info); + type_register_static(&sclp_info); } type_init(register_types); diff --git a/qemu/hw/s390x/sclpcpu.c b/qemu/hw/s390x/sclpcpu.c index 2fe8b5aa4..b1f3ef8c7 100644 --- a/qemu/hw/s390x/sclpcpu.c +++ b/qemu/hw/s390x/sclpcpu.c @@ -12,6 +12,7 @@ * option) any later version. See the COPYING file in the top-level directory. * */ +#include "qemu/osdep.h" #include "sysemu/sysemu.h" #include "hw/s390x/sclp.h" #include "hw/s390x/event-facility.h" @@ -25,13 +26,16 @@ typedef struct ConfigMgtData { uint8_t event_qualifier; } QEMU_PACKED ConfigMgtData; -static qemu_irq *irq_cpu_hotplug; /* Only used in this file */ - #define EVENT_QUAL_CPU_CHANGE 1 void raise_irq_cpu_hotplug(void) { - qemu_irq_raise(*irq_cpu_hotplug); + Object *obj = object_resolve_path_type("", TYPE_SCLP_CPU_HOTPLUG, NULL); + + SCLP_EVENT(obj)->event_pending = true; + + /* Trigger SCLP read operation */ + sclp_service_interrupt(0); } static unsigned int send_mask(void) @@ -70,36 +74,19 @@ static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, return 1; } -static void trigger_signal(void *opaque, int n, int level) -{ - SCLPEvent *event = opaque; - event->event_pending = true; - - /* Trigger SCLP read operation */ - sclp_service_interrupt(0); -} - -static int irq_cpu_hotplug_init(SCLPEvent *event) -{ - irq_cpu_hotplug = qemu_allocate_irqs(trigger_signal, event, 1); - return 0; -} - static void cpu_class_init(ObjectClass *oc, void *data) { SCLPEventClass *k = SCLP_EVENT_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); - k->init = irq_cpu_hotplug_init; k->get_send_mask = send_mask; k->get_receive_mask = receive_mask; k->read_event_data = read_event_data; - k->write_event_data = NULL; set_bit(DEVICE_CATEGORY_MISC, dc->categories); } static const TypeInfo sclp_cpu_info = { - .name = "sclp-cpu-hotplug", + .name = TYPE_SCLP_CPU_HOTPLUG, .parent = TYPE_SCLP_EVENT, .instance_size = sizeof(SCLPEvent), .class_init = cpu_class_init, diff --git a/qemu/hw/s390x/sclpquiesce.c b/qemu/hw/s390x/sclpquiesce.c index ffa555313..c0ecab9c3 100644 --- a/qemu/hw/s390x/sclpquiesce.c +++ b/qemu/hw/s390x/sclpquiesce.c @@ -11,6 +11,7 @@ * option) any later version. See the COPYING file in the top-level directory. * */ +#include "qemu/osdep.h" #include #include "sysemu/sysemu.h" #include "hw/s390x/sclp.h" @@ -66,7 +67,7 @@ static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, } static const VMStateDescription vmstate_sclpquiesce = { - .name = "sclpquiesce", + .name = TYPE_SCLP_QUIESCE, .version_id = 0, .minimum_version_id = 0, .fields = (VMStateField[]) { @@ -127,7 +128,7 @@ static void quiesce_class_init(ObjectClass *klass, void *data) } static const TypeInfo sclp_quiesce_info = { - .name = "sclpquiesce", + .name = TYPE_SCLP_QUIESCE, .parent = TYPE_SCLP_EVENT, .instance_size = sizeof(SCLPEvent), .class_init = quiesce_class_init, diff --git a/qemu/hw/s390x/virtio-ccw.c b/qemu/hw/s390x/virtio-ccw.c index d36373e88..d51642db0 100644 --- a/qemu/hw/s390x/virtio-ccw.c +++ b/qemu/hw/s390x/virtio-ccw.c @@ -10,6 +10,8 @@ * directory. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" @@ -31,69 +33,6 @@ #include "virtio-ccw.h" #include "trace.h" -static QTAILQ_HEAD(, IndAddr) indicator_addresses = - QTAILQ_HEAD_INITIALIZER(indicator_addresses); - -static IndAddr *get_indicator(hwaddr ind_addr, int len) -{ - IndAddr *indicator; - - QTAILQ_FOREACH(indicator, &indicator_addresses, sibling) { - if (indicator->addr == ind_addr) { - indicator->refcnt++; - return indicator; - } - } - indicator = g_new0(IndAddr, 1); - indicator->addr = ind_addr; - indicator->len = len; - indicator->refcnt = 1; - QTAILQ_INSERT_TAIL(&indicator_addresses, indicator, sibling); - return indicator; -} - -static int s390_io_adapter_map(AdapterInfo *adapter, uint64_t map_addr, - bool do_map) -{ - S390FLICState *fs = s390_get_flic(); - S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); - - return fsc->io_adapter_map(fs, adapter->adapter_id, map_addr, do_map); -} - -static void release_indicator(AdapterInfo *adapter, IndAddr *indicator) -{ - assert(indicator->refcnt > 0); - indicator->refcnt--; - if (indicator->refcnt > 0) { - return; - } - QTAILQ_REMOVE(&indicator_addresses, indicator, sibling); - if (indicator->map) { - s390_io_adapter_map(adapter, indicator->map, false); - } - g_free(indicator); -} - -static int map_indicator(AdapterInfo *adapter, IndAddr *indicator) -{ - int ret; - - if (indicator->map) { - return 0; /* already mapped is not an error */ - } - indicator->map = indicator->addr; - ret = s390_io_adapter_map(adapter, indicator->map, true); - if ((ret != 0) && (ret != -ENOSYS)) { - goto out_err; - } - return 0; - -out_err: - indicator->map = 0; - return ret; -} - static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, VirtioCcwDevice *dev); @@ -307,11 +246,18 @@ static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info, if (!desc) { virtio_queue_set_vector(vdev, index, VIRTIO_NO_VECTOR); } else { - /* Fail if we don't have a big enough queue. */ - /* TODO: Add interface to handle vring.num changing */ - if (virtio_queue_get_num(vdev, index) > num) { + if (info) { + /* virtio-1 allows changing the ring size. */ + if (virtio_queue_get_num(vdev, index) < num) { + /* Fail if we exceed the maximum number. */ + return -EINVAL; + } + virtio_queue_set_num(vdev, index, num); + } else if (virtio_queue_get_num(vdev, index) > num) { + /* Fail if we don't have a big enough queue. */ return -EINVAL; } + /* We ignore possible increased num for legacy for compatibility. */ virtio_queue_set_vector(vdev, index, index); } /* tell notify handler in case of config change */ @@ -460,16 +406,19 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) MEMTXATTRS_UNSPECIFIED, NULL); if (features.index == 0) { - features.features = (uint32_t)vdev->host_features; - } else if (features.index == 1) { - features.features = (uint32_t)(vdev->host_features >> 32); + if (dev->revision >= 1) { + /* Don't offer legacy features for modern devices. */ + features.features = (uint32_t) + (vdev->host_features & ~VIRTIO_LEGACY_FEATURES); + } else { + features.features = (uint32_t)vdev->host_features; + } + } else if ((features.index == 1) && (dev->revision >= 1)) { /* - * Don't offer version 1 to the guest if it did not - * negotiate at least revision 1. + * Only offer feature bits beyond 31 if the guest has + * negotiated at least revision 1. */ - if (dev->revision <= 0) { - features.features &= ~(1 << (VIRTIO_F_VERSION_1 - 32)); - } + features.features = (uint32_t)(vdev->host_features >> 32); } else { /* Return zeroes if the guest supports more feature bits. */ features.features = 0; @@ -508,14 +457,12 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) virtio_set_features(vdev, (vdev->guest_features & 0xffffffff00000000ULL) | features.features); - } else if (features.index == 1) { + } else if ((features.index == 1) && (dev->revision >= 1)) { /* - * The guest should not set version 1 if it didn't - * negotiate a revision >= 1. + * If the guest did not negotiate at least revision 1, + * we did not offer it any feature bits beyond 31. Such a + * guest passing us any bit here is therefore buggy. */ - if (dev->revision <= 0) { - features.features &= ~(1 << (VIRTIO_F_VERSION_1 - 32)); - } virtio_set_features(vdev, (vdev->guest_features & 0x00000000ffffffffULL) | ((uint64_t)features.features << 32)); @@ -766,7 +713,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) * need to fetch it here. Nothing to do for now, though. */ if (dev->revision >= 0 || - revinfo.revision > virtio_ccw_rev_max(vdev)) { + revinfo.revision > virtio_ccw_rev_max(dev)) { ret = -ENOSYS; break; } @@ -1169,7 +1116,8 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) SubchDev *sch = dev->sch; uint64_t indicators; - if (vector >= 128) { + /* queue indicators + secondary indicators */ + if (vector >= VIRTIO_CCW_QUEUE_MAX + 64) { return; } @@ -1539,10 +1487,25 @@ static void virtio_ccw_device_plugged(DeviceState *d, Error **errp) sch->id.cu_model = virtio_bus_get_vdev_id(&dev->bus); + if (dev->max_rev >= 1) { + virtio_add_feature(&vdev->host_features, VIRTIO_F_VERSION_1); + } + css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, d->hotplugged, 1); } +static void virtio_ccw_post_plugged(DeviceState *d, Error **errp) +{ + VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); + VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); + + if (!virtio_host_has_feature(vdev, VIRTIO_F_VERSION_1)) { + /* A backend didn't support modern virtio. */ + dev->max_rev = 0; + } +} + static void virtio_ccw_device_unplugged(DeviceState *d) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); @@ -1555,6 +1518,8 @@ static Property virtio_ccw_net_properties[] = { DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, + VIRTIO_CCW_MAX_REV), DEFINE_PROP_END_OF_LIST(), }; @@ -1582,6 +1547,8 @@ static Property virtio_ccw_blk_properties[] = { DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, + VIRTIO_CCW_MAX_REV), DEFINE_PROP_END_OF_LIST(), }; @@ -1609,6 +1576,8 @@ static Property virtio_ccw_serial_properties[] = { DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, + VIRTIO_CCW_MAX_REV), DEFINE_PROP_END_OF_LIST(), }; @@ -1636,6 +1605,8 @@ static Property virtio_ccw_balloon_properties[] = { DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, + VIRTIO_CCW_MAX_REV), DEFINE_PROP_END_OF_LIST(), }; @@ -1663,6 +1634,8 @@ static Property virtio_ccw_scsi_properties[] = { DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, + VIRTIO_CCW_MAX_REV), DEFINE_PROP_END_OF_LIST(), }; @@ -1689,6 +1662,8 @@ static const TypeInfo virtio_ccw_scsi = { #ifdef CONFIG_VHOST_SCSI static Property vhost_ccw_scsi_properties[] = { DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, + VIRTIO_CCW_MAX_REV), DEFINE_PROP_END_OF_LIST(), }; @@ -1727,6 +1702,8 @@ static Property virtio_ccw_rng_properties[] = { DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, + VIRTIO_CCW_MAX_REV), DEFINE_PROP_END_OF_LIST(), }; @@ -1865,6 +1842,7 @@ static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data) k->save_config = virtio_ccw_save_config; k->load_config = virtio_ccw_load_config; k->device_plugged = virtio_ccw_device_plugged; + k->post_plugged = virtio_ccw_post_plugged; k->device_unplugged = virtio_ccw_device_unplugged; } @@ -1880,6 +1858,8 @@ static Property virtio_ccw_9p_properties[] = { DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, + VIRTIO_CCW_MAX_REV), DEFINE_PROP_END_OF_LIST(), }; diff --git a/qemu/hw/s390x/virtio-ccw.h b/qemu/hw/s390x/virtio-ccw.h index 692ddd731..66c831ba8 100644 --- a/qemu/hw/s390x/virtio-ccw.h +++ b/qemu/hw/s390x/virtio-ccw.h @@ -23,7 +23,8 @@ #include #include #include -#include + +#include "css.h" #define VIRTUAL_CSSID 0xfe @@ -75,19 +76,12 @@ typedef struct VirtIOCCWDeviceClass { #define VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT 1 #define VIRTIO_CCW_FLAG_USE_IOEVENTFD (1 << VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT) -typedef struct IndAddr { - hwaddr addr; - uint64_t map; - unsigned long refcnt; - int len; - QTAILQ_ENTRY(IndAddr) sibling; -} IndAddr; - struct VirtioCcwDevice { DeviceState parent_obj; SubchDev *sch; char *bus_id; int revision; + uint32_t max_rev; VirtioBusState bus; bool ioeventfd_started; bool ioeventfd_disabled; @@ -102,9 +96,10 @@ struct VirtioCcwDevice { }; /* The maximum virtio revision we support. */ -static inline int virtio_ccw_rev_max(VirtIODevice *vdev) +#define VIRTIO_CCW_MAX_REV 1 +static inline int virtio_ccw_rev_max(VirtioCcwDevice *dev) { - return 0; + return dev->max_rev; } /* virtual css bus type */ @@ -208,7 +203,7 @@ VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch); typedef struct V9fsCCWState { VirtioCcwDevice parent_obj; - V9fsState vdev; + V9fsVirtioState vdev; } V9fsCCWState; #endif /* CONFIG_VIRTFS */ diff --git a/qemu/hw/scsi/Makefile.objs b/qemu/hw/scsi/Makefile.objs index 40c79d34c..5a2248be3 100644 --- a/qemu/hw/scsi/Makefile.objs +++ b/qemu/hw/scsi/Makefile.objs @@ -1,6 +1,7 @@ common-obj-y += scsi-disk.o common-obj-y += scsi-generic.o scsi-bus.o common-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o +common-obj-$(CONFIG_MPTSAS_SCSI_PCI) += mptsas.o mptconfig.o mptendian.o common-obj-$(CONFIG_MEGASAS_SCSI_PCI) += megasas.o common-obj-$(CONFIG_VMW_PVSCSI_SCSI_PCI) += vmw_pvscsi.o common-obj-$(CONFIG_ESP) += esp.o diff --git a/qemu/hw/scsi/esp-pci.c b/qemu/hw/scsi/esp-pci.c index 8d2242d0a..595f88b35 100644 --- a/qemu/hw/scsi/esp-pci.c +++ b/qemu/hw/scsi/esp-pci.c @@ -23,10 +23,12 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/pci/pci.h" #include "hw/nvram/eeprom93xx.h" #include "hw/scsi/esp.h" #include "trace.h" +#include "qapi/error.h" #include "qemu/log.h" #define TYPE_AM53C974_DEVICE "am53c974" diff --git a/qemu/hw/scsi/esp.c b/qemu/hw/scsi/esp.c index 272d13d63..8961be2f3 100644 --- a/qemu/hw/scsi/esp.c +++ b/qemu/hw/scsi/esp.c @@ -23,9 +23,11 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/scsi/esp.h" #include "trace.h" +#include "qapi/error.h" #include "qemu/log.h" /* diff --git a/qemu/hw/scsi/lsi53c895a.c b/qemu/hw/scsi/lsi53c895a.c index c5b0cc5ca..df205cdaf 100644 --- a/qemu/hw/scsi/lsi53c895a.c +++ b/qemu/hw/scsi/lsi53c895a.c @@ -13,7 +13,7 @@ * as well-behaved operating systems will not try to use them. */ -#include +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" diff --git a/qemu/hw/scsi/megasas.c b/qemu/hw/scsi/megasas.c index a04369c5a..a63a58155 100644 --- a/qemu/hw/scsi/megasas.c +++ b/qemu/hw/scsi/megasas.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "sysemu/dma.h" @@ -431,7 +432,7 @@ static uint64_t megasas_fw_time(void) static uint64_t megasas_get_sata_addr(uint16_t id) { uint64_t addr = (0x1221ULL << 48); - return addr & (id << 24); + return addr | ((uint64_t)id << 24); } /* @@ -718,7 +719,7 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd) BusChild *kid; int num_pd_disks = 0; - memset(&info, 0x0, cmd->iov_size); + memset(&info, 0x0, dcmd_size); if (cmd->iov_size < dcmd_size) { trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, dcmd_size); @@ -744,7 +745,7 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd) info.device.type = MFI_INFO_DEV_SAS3G; info.device.port_count = 8; QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child); + SCSIDevice *sdev = SCSI_DEVICE(kid->child); uint16_t pd_id; if (num_pd_disks < 8) { @@ -757,7 +758,7 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd) memcpy(info.product_name, base_class->product_name, 24); snprintf(info.serial_number, 32, "%s", s->hba_serial); - snprintf(info.package_version, 0x60, "%s-QEMU", QEMU_VERSION); + snprintf(info.package_version, 0x60, "%s-QEMU", qemu_hw_version()); memcpy(info.image_component[0].name, "APP", 3); snprintf(info.image_component[0].version, 10, "%s-QEMU", base_class->product_version); @@ -960,7 +961,7 @@ static int megasas_dcmd_pd_get_list(MegasasState *s, MegasasCmd *cmd) max_pd_disks = MFI_MAX_SYS_PDS; } QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child); + SCSIDevice *sdev = SCSI_DEVICE(kid->child); uint16_t pd_id; if (num_pd_disks >= max_pd_disks) @@ -1136,7 +1137,7 @@ static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd) max_ld_disks = MFI_MAX_LD; } QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child); + SCSIDevice *sdev = SCSI_DEVICE(kid->child); if (num_ld_disks >= max_ld_disks) { break; @@ -1187,7 +1188,7 @@ static int megasas_dcmd_ld_list_query(MegasasState *s, MegasasCmd *cmd) max_ld_disks = MFI_MAX_LD; } QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child); + SCSIDevice *sdev = SCSI_DEVICE(kid->child); if (num_ld_disks >= max_ld_disks) { break; @@ -1327,7 +1328,7 @@ static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd) ld_offset = array_offset + sizeof(struct mfi_array) * num_pd_disks; QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child); + SCSIDevice *sdev = SCSI_DEVICE(kid->child); uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (sdev->lun & 0xFF); struct mfi_array *array; struct mfi_ld_config *ld; @@ -2237,7 +2238,7 @@ static void megasas_soft_reset(MegasasState *s) * after the initial reset. */ QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child); + SCSIDevice *sdev = SCSI_DEVICE(kid->child); sdev->unit_attention = SENSE_CODE(NO_SENSE); scsi_device_unit_attention_reported(sdev); diff --git a/qemu/hw/scsi/mpi.h b/qemu/hw/scsi/mpi.h new file mode 100644 index 000000000..0568e1950 --- /dev/null +++ b/qemu/hw/scsi/mpi.h @@ -0,0 +1,1153 @@ +/*- + * Based on FreeBSD sys/dev/mpt/mpilib headers. + * + * Copyright (c) 2000-2010, LSI Logic Corporation and its contributors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon including + * a substantially similar Disclaimer requirement for further binary + * redistribution. + * 3. Neither the name of the LSI Logic Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF THE COPYRIGHT + * OWNER OR CONTRIBUTOR IS ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef MPI_H +#define MPI_H + +enum { + MPI_FUNCTION_SCSI_IO_REQUEST = 0x00, + MPI_FUNCTION_SCSI_TASK_MGMT = 0x01, + MPI_FUNCTION_IOC_INIT = 0x02, + MPI_FUNCTION_IOC_FACTS = 0x03, + MPI_FUNCTION_CONFIG = 0x04, + MPI_FUNCTION_PORT_FACTS = 0x05, + MPI_FUNCTION_PORT_ENABLE = 0x06, + MPI_FUNCTION_EVENT_NOTIFICATION = 0x07, + MPI_FUNCTION_EVENT_ACK = 0x08, + MPI_FUNCTION_FW_DOWNLOAD = 0x09, + MPI_FUNCTION_TARGET_CMD_BUFFER_POST = 0x0A, + MPI_FUNCTION_TARGET_ASSIST = 0x0B, + MPI_FUNCTION_TARGET_STATUS_SEND = 0x0C, + MPI_FUNCTION_TARGET_MODE_ABORT = 0x0D, + MPI_FUNCTION_FC_LINK_SRVC_BUF_POST = 0x0E, + MPI_FUNCTION_FC_LINK_SRVC_RSP = 0x0F, + MPI_FUNCTION_FC_EX_LINK_SRVC_SEND = 0x10, + MPI_FUNCTION_FC_ABORT = 0x11, + MPI_FUNCTION_FW_UPLOAD = 0x12, + MPI_FUNCTION_FC_COMMON_TRANSPORT_SEND = 0x13, + MPI_FUNCTION_FC_PRIMITIVE_SEND = 0x14, + + MPI_FUNCTION_RAID_ACTION = 0x15, + MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH = 0x16, + + MPI_FUNCTION_TOOLBOX = 0x17, + + MPI_FUNCTION_SCSI_ENCLOSURE_PROCESSOR = 0x18, + + MPI_FUNCTION_MAILBOX = 0x19, + + MPI_FUNCTION_SMP_PASSTHROUGH = 0x1A, + MPI_FUNCTION_SAS_IO_UNIT_CONTROL = 0x1B, + MPI_FUNCTION_SATA_PASSTHROUGH = 0x1C, + + MPI_FUNCTION_DIAG_BUFFER_POST = 0x1D, + MPI_FUNCTION_DIAG_RELEASE = 0x1E, + + MPI_FUNCTION_SCSI_IO_32 = 0x1F, + + MPI_FUNCTION_LAN_SEND = 0x20, + MPI_FUNCTION_LAN_RECEIVE = 0x21, + MPI_FUNCTION_LAN_RESET = 0x22, + + MPI_FUNCTION_TARGET_ASSIST_EXTENDED = 0x23, + MPI_FUNCTION_TARGET_CMD_BUF_BASE_POST = 0x24, + MPI_FUNCTION_TARGET_CMD_BUF_LIST_POST = 0x25, + + MPI_FUNCTION_INBAND_BUFFER_POST = 0x28, + MPI_FUNCTION_INBAND_SEND = 0x29, + MPI_FUNCTION_INBAND_RSP = 0x2A, + MPI_FUNCTION_INBAND_ABORT = 0x2B, + + MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET = 0x40, + MPI_FUNCTION_IO_UNIT_RESET = 0x41, + MPI_FUNCTION_HANDSHAKE = 0x42, + MPI_FUNCTION_REPLY_FRAME_REMOVAL = 0x43, + MPI_FUNCTION_HOST_PAGEBUF_ACCESS_CONTROL = 0x44, +}; + +/****************************************************************************/ +/* Registers */ +/****************************************************************************/ + +enum { + MPI_IOC_STATE_RESET = 0x00000000, + MPI_IOC_STATE_READY = 0x10000000, + MPI_IOC_STATE_OPERATIONAL = 0x20000000, + MPI_IOC_STATE_FAULT = 0x40000000, + + MPI_DOORBELL_OFFSET = 0x00000000, + MPI_DOORBELL_ACTIVE = 0x08000000, /* DoorbellUsed */ + MPI_DOORBELL_WHO_INIT_MASK = 0x07000000, + MPI_DOORBELL_WHO_INIT_SHIFT = 24, + MPI_DOORBELL_FUNCTION_MASK = 0xFF000000, + MPI_DOORBELL_FUNCTION_SHIFT = 24, + MPI_DOORBELL_ADD_DWORDS_MASK = 0x00FF0000, + MPI_DOORBELL_ADD_DWORDS_SHIFT = 16, + MPI_DOORBELL_DATA_MASK = 0x0000FFFF, + MPI_DOORBELL_FUNCTION_SPECIFIC_MASK = 0x0000FFFF, + + MPI_DB_HPBAC_VALUE_MASK = 0x0000F000, + MPI_DB_HPBAC_ENABLE_ACCESS = 0x01, + MPI_DB_HPBAC_DISABLE_ACCESS = 0x02, + MPI_DB_HPBAC_FREE_BUFFER = 0x03, + + MPI_WRITE_SEQUENCE_OFFSET = 0x00000004, + MPI_WRSEQ_KEY_VALUE_MASK = 0x0000000F, + MPI_WRSEQ_1ST_KEY_VALUE = 0x04, + MPI_WRSEQ_2ND_KEY_VALUE = 0x0B, + MPI_WRSEQ_3RD_KEY_VALUE = 0x02, + MPI_WRSEQ_4TH_KEY_VALUE = 0x07, + MPI_WRSEQ_5TH_KEY_VALUE = 0x0D, + + MPI_DIAGNOSTIC_OFFSET = 0x00000008, + MPI_DIAG_CLEAR_FLASH_BAD_SIG = 0x00000400, + MPI_DIAG_PREVENT_IOC_BOOT = 0x00000200, + MPI_DIAG_DRWE = 0x00000080, + MPI_DIAG_FLASH_BAD_SIG = 0x00000040, + MPI_DIAG_RESET_HISTORY = 0x00000020, + MPI_DIAG_RW_ENABLE = 0x00000010, + MPI_DIAG_RESET_ADAPTER = 0x00000004, + MPI_DIAG_DISABLE_ARM = 0x00000002, + MPI_DIAG_MEM_ENABLE = 0x00000001, + + MPI_TEST_BASE_ADDRESS_OFFSET = 0x0000000C, + + MPI_DIAG_RW_DATA_OFFSET = 0x00000010, + + MPI_DIAG_RW_ADDRESS_OFFSET = 0x00000014, + + MPI_HOST_INTERRUPT_STATUS_OFFSET = 0x00000030, + MPI_HIS_IOP_DOORBELL_STATUS = 0x80000000, + MPI_HIS_REPLY_MESSAGE_INTERRUPT = 0x00000008, + MPI_HIS_DOORBELL_INTERRUPT = 0x00000001, + + MPI_HOST_INTERRUPT_MASK_OFFSET = 0x00000034, + MPI_HIM_RIM = 0x00000008, + MPI_HIM_DIM = 0x00000001, + + MPI_REQUEST_QUEUE_OFFSET = 0x00000040, + MPI_REQUEST_POST_FIFO_OFFSET = 0x00000040, + + MPI_REPLY_QUEUE_OFFSET = 0x00000044, + MPI_REPLY_POST_FIFO_OFFSET = 0x00000044, + MPI_REPLY_FREE_FIFO_OFFSET = 0x00000044, + + MPI_HI_PRI_REQUEST_QUEUE_OFFSET = 0x00000048, +}; + +#define MPI_ADDRESS_REPLY_A_BIT 0x80000000 + +/****************************************************************************/ +/* Scatter/gather elements */ +/****************************************************************************/ + +typedef struct MPISGEntry { + uint32_t FlagsLength; + union + { + uint32_t Address32; + uint64_t Address64; + } u; +} QEMU_PACKED MPISGEntry; + +/* Flags field bit definitions */ + +enum { + MPI_SGE_FLAGS_LAST_ELEMENT = 0x80000000, + MPI_SGE_FLAGS_END_OF_BUFFER = 0x40000000, + MPI_SGE_FLAGS_ELEMENT_TYPE_MASK = 0x30000000, + MPI_SGE_FLAGS_LOCAL_ADDRESS = 0x08000000, + MPI_SGE_FLAGS_DIRECTION = 0x04000000, + MPI_SGE_FLAGS_64_BIT_ADDRESSING = 0x02000000, + MPI_SGE_FLAGS_END_OF_LIST = 0x01000000, + + MPI_SGE_LENGTH_MASK = 0x00FFFFFF, + MPI_SGE_CHAIN_LENGTH_MASK = 0x0000FFFF, + + MPI_SGE_FLAGS_TRANSACTION_ELEMENT = 0x00000000, + MPI_SGE_FLAGS_SIMPLE_ELEMENT = 0x10000000, + MPI_SGE_FLAGS_CHAIN_ELEMENT = 0x30000000, + + /* Direction */ + + MPI_SGE_FLAGS_IOC_TO_HOST = 0x00000000, + MPI_SGE_FLAGS_HOST_TO_IOC = 0x04000000, + + MPI_SGE_CHAIN_OFFSET_MASK = 0x00FF0000, +}; + +#define MPI_SGE_CHAIN_OFFSET_SHIFT 16 + +/****************************************************************************/ +/* Standard message request header for all request messages */ +/****************************************************************************/ + +typedef struct MPIRequestHeader { + uint8_t Reserved[2]; /* function specific */ + uint8_t ChainOffset; + uint8_t Function; + uint8_t Reserved1[3]; /* function specific */ + uint8_t MsgFlags; + uint32_t MsgContext; +} QEMU_PACKED MPIRequestHeader; + + +typedef struct MPIDefaultReply { + uint8_t Reserved[2]; /* function specific */ + uint8_t MsgLength; + uint8_t Function; + uint8_t Reserved1[3]; /* function specific */ + uint8_t MsgFlags; + uint32_t MsgContext; + uint8_t Reserved2[2]; /* function specific */ + uint16_t IOCStatus; + uint32_t IOCLogInfo; +} QEMU_PACKED MPIDefaultReply; + +/* MsgFlags definition for all replies */ + +#define MPI_MSGFLAGS_CONTINUATION_REPLY (0x80) + +enum { + + /************************************************************************/ + /* Common IOCStatus values for all replies */ + /************************************************************************/ + + MPI_IOCSTATUS_SUCCESS = 0x0000, + MPI_IOCSTATUS_INVALID_FUNCTION = 0x0001, + MPI_IOCSTATUS_BUSY = 0x0002, + MPI_IOCSTATUS_INVALID_SGL = 0x0003, + MPI_IOCSTATUS_INTERNAL_ERROR = 0x0004, + MPI_IOCSTATUS_RESERVED = 0x0005, + MPI_IOCSTATUS_INSUFFICIENT_RESOURCES = 0x0006, + MPI_IOCSTATUS_INVALID_FIELD = 0x0007, + MPI_IOCSTATUS_INVALID_STATE = 0x0008, + MPI_IOCSTATUS_OP_STATE_NOT_SUPPORTED = 0x0009, + + /************************************************************************/ + /* Config IOCStatus values */ + /************************************************************************/ + + MPI_IOCSTATUS_CONFIG_INVALID_ACTION = 0x0020, + MPI_IOCSTATUS_CONFIG_INVALID_TYPE = 0x0021, + MPI_IOCSTATUS_CONFIG_INVALID_PAGE = 0x0022, + MPI_IOCSTATUS_CONFIG_INVALID_DATA = 0x0023, + MPI_IOCSTATUS_CONFIG_NO_DEFAULTS = 0x0024, + MPI_IOCSTATUS_CONFIG_CANT_COMMIT = 0x0025, + + /************************************************************************/ + /* SCSIIO Reply = SPI & FCP, initiator values */ + /************************************************************************/ + + MPI_IOCSTATUS_SCSI_RECOVERED_ERROR = 0x0040, + MPI_IOCSTATUS_SCSI_INVALID_BUS = 0x0041, + MPI_IOCSTATUS_SCSI_INVALID_TARGETID = 0x0042, + MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE = 0x0043, + MPI_IOCSTATUS_SCSI_DATA_OVERRUN = 0x0044, + MPI_IOCSTATUS_SCSI_DATA_UNDERRUN = 0x0045, + MPI_IOCSTATUS_SCSI_IO_DATA_ERROR = 0x0046, + MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR = 0x0047, + MPI_IOCSTATUS_SCSI_TASK_TERMINATED = 0x0048, + MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH = 0x0049, + MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED = 0x004A, + MPI_IOCSTATUS_SCSI_IOC_TERMINATED = 0x004B, + MPI_IOCSTATUS_SCSI_EXT_TERMINATED = 0x004C, + + /************************************************************************/ + /* For use by SCSI Initiator and SCSI Target end-to-end data protection*/ + /************************************************************************/ + + MPI_IOCSTATUS_EEDP_GUARD_ERROR = 0x004D, + MPI_IOCSTATUS_EEDP_REF_TAG_ERROR = 0x004E, + MPI_IOCSTATUS_EEDP_APP_TAG_ERROR = 0x004F, + + /************************************************************************/ + /* SCSI Target values */ + /************************************************************************/ + + MPI_IOCSTATUS_TARGET_PRIORITY_IO = 0x0060, + MPI_IOCSTATUS_TARGET_INVALID_PORT = 0x0061, + MPI_IOCSTATUS_TARGET_INVALID_IO_INDEX = 0x0062, + MPI_IOCSTATUS_TARGET_ABORTED = 0x0063, + MPI_IOCSTATUS_TARGET_NO_CONN_RETRYABLE = 0x0064, + MPI_IOCSTATUS_TARGET_NO_CONNECTION = 0x0065, + MPI_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH = 0x006A, + MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT = 0x006B, + MPI_IOCSTATUS_TARGET_DATA_OFFSET_ERROR = 0x006D, + MPI_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA = 0x006E, + MPI_IOCSTATUS_TARGET_IU_TOO_SHORT = 0x006F, + MPI_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT = 0x0070, + MPI_IOCSTATUS_TARGET_NAK_RECEIVED = 0x0071, + + /************************************************************************/ + /* Fibre Channel Direct Access values */ + /************************************************************************/ + + MPI_IOCSTATUS_FC_ABORTED = 0x0066, + MPI_IOCSTATUS_FC_RX_ID_INVALID = 0x0067, + MPI_IOCSTATUS_FC_DID_INVALID = 0x0068, + MPI_IOCSTATUS_FC_NODE_LOGGED_OUT = 0x0069, + MPI_IOCSTATUS_FC_EXCHANGE_CANCELED = 0x006C, + + /************************************************************************/ + /* LAN values */ + /************************************************************************/ + + MPI_IOCSTATUS_LAN_DEVICE_NOT_FOUND = 0x0080, + MPI_IOCSTATUS_LAN_DEVICE_FAILURE = 0x0081, + MPI_IOCSTATUS_LAN_TRANSMIT_ERROR = 0x0082, + MPI_IOCSTATUS_LAN_TRANSMIT_ABORTED = 0x0083, + MPI_IOCSTATUS_LAN_RECEIVE_ERROR = 0x0084, + MPI_IOCSTATUS_LAN_RECEIVE_ABORTED = 0x0085, + MPI_IOCSTATUS_LAN_PARTIAL_PACKET = 0x0086, + MPI_IOCSTATUS_LAN_CANCELED = 0x0087, + + /************************************************************************/ + /* Serial Attached SCSI values */ + /************************************************************************/ + + MPI_IOCSTATUS_SAS_SMP_REQUEST_FAILED = 0x0090, + MPI_IOCSTATUS_SAS_SMP_DATA_OVERRUN = 0x0091, + + /************************************************************************/ + /* Inband values */ + /************************************************************************/ + + MPI_IOCSTATUS_INBAND_ABORTED = 0x0098, + MPI_IOCSTATUS_INBAND_NO_CONNECTION = 0x0099, + + /************************************************************************/ + /* Diagnostic Tools values */ + /************************************************************************/ + + MPI_IOCSTATUS_DIAGNOSTIC_RELEASED = 0x00A0, + + /************************************************************************/ + /* IOCStatus flag to indicate that log info is available */ + /************************************************************************/ + + MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE = 0x8000, + MPI_IOCSTATUS_MASK = 0x7FFF, + + /************************************************************************/ + /* LogInfo Types */ + /************************************************************************/ + + MPI_IOCLOGINFO_TYPE_MASK = 0xF0000000, + MPI_IOCLOGINFO_TYPE_SHIFT = 28, + MPI_IOCLOGINFO_TYPE_NONE = 0x0, + MPI_IOCLOGINFO_TYPE_SCSI = 0x1, + MPI_IOCLOGINFO_TYPE_FC = 0x2, + MPI_IOCLOGINFO_TYPE_SAS = 0x3, + MPI_IOCLOGINFO_TYPE_ISCSI = 0x4, + MPI_IOCLOGINFO_LOG_DATA_MASK = 0x0FFFFFFF, +}; + +/****************************************************************************/ +/* SCSI IO messages and associated structures */ +/****************************************************************************/ + +typedef struct MPIMsgSCSIIORequest { + uint8_t TargetID; /* 00h */ + uint8_t Bus; /* 01h */ + uint8_t ChainOffset; /* 02h */ + uint8_t Function; /* 03h */ + uint8_t CDBLength; /* 04h */ + uint8_t SenseBufferLength; /* 05h */ + uint8_t Reserved; /* 06h */ + uint8_t MsgFlags; /* 07h */ + uint32_t MsgContext; /* 08h */ + uint8_t LUN[8]; /* 0Ch */ + uint32_t Control; /* 14h */ + uint8_t CDB[16]; /* 18h */ + uint32_t DataLength; /* 28h */ + uint32_t SenseBufferLowAddr; /* 2Ch */ +} QEMU_PACKED MPIMsgSCSIIORequest; + +/* SCSI IO MsgFlags bits */ + +#define MPI_SCSIIO_MSGFLGS_SENSE_WIDTH (0x01) +#define MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_32 (0x00) +#define MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_64 (0x01) + +#define MPI_SCSIIO_MSGFLGS_SENSE_LOCATION (0x02) +#define MPI_SCSIIO_MSGFLGS_SENSE_LOC_HOST (0x00) +#define MPI_SCSIIO_MSGFLGS_SENSE_LOC_IOC (0x02) + +#define MPI_SCSIIO_MSGFLGS_CMD_DETERMINES_DATA_DIR (0x04) + +/* SCSI IO LUN fields */ + +#define MPI_SCSIIO_LUN_FIRST_LEVEL_ADDRESSING (0x0000FFFF) +#define MPI_SCSIIO_LUN_SECOND_LEVEL_ADDRESSING (0xFFFF0000) +#define MPI_SCSIIO_LUN_THIRD_LEVEL_ADDRESSING (0x0000FFFF) +#define MPI_SCSIIO_LUN_FOURTH_LEVEL_ADDRESSING (0xFFFF0000) +#define MPI_SCSIIO_LUN_LEVEL_1_WORD (0xFF00) +#define MPI_SCSIIO_LUN_LEVEL_1_DWORD (0x0000FF00) + +/* SCSI IO Control bits */ + +#define MPI_SCSIIO_CONTROL_DATADIRECTION_MASK (0x03000000) +#define MPI_SCSIIO_CONTROL_NODATATRANSFER (0x00000000) +#define MPI_SCSIIO_CONTROL_WRITE (0x01000000) +#define MPI_SCSIIO_CONTROL_READ (0x02000000) + +#define MPI_SCSIIO_CONTROL_ADDCDBLEN_MASK (0x3C000000) +#define MPI_SCSIIO_CONTROL_ADDCDBLEN_SHIFT (26) + +#define MPI_SCSIIO_CONTROL_TASKATTRIBUTE_MASK (0x00000700) +#define MPI_SCSIIO_CONTROL_SIMPLEQ (0x00000000) +#define MPI_SCSIIO_CONTROL_HEADOFQ (0x00000100) +#define MPI_SCSIIO_CONTROL_ORDEREDQ (0x00000200) +#define MPI_SCSIIO_CONTROL_ACAQ (0x00000400) +#define MPI_SCSIIO_CONTROL_UNTAGGED (0x00000500) +#define MPI_SCSIIO_CONTROL_NO_DISCONNECT (0x00000700) + +#define MPI_SCSIIO_CONTROL_TASKMANAGE_MASK (0x00FF0000) +#define MPI_SCSIIO_CONTROL_OBSOLETE (0x00800000) +#define MPI_SCSIIO_CONTROL_CLEAR_ACA_RSV (0x00400000) +#define MPI_SCSIIO_CONTROL_TARGET_RESET (0x00200000) +#define MPI_SCSIIO_CONTROL_LUN_RESET_RSV (0x00100000) +#define MPI_SCSIIO_CONTROL_RESERVED (0x00080000) +#define MPI_SCSIIO_CONTROL_CLR_TASK_SET_RSV (0x00040000) +#define MPI_SCSIIO_CONTROL_ABORT_TASK_SET (0x00020000) +#define MPI_SCSIIO_CONTROL_RESERVED2 (0x00010000) + +/* SCSI IO reply structure */ +typedef struct MPIMsgSCSIIOReply +{ + uint8_t TargetID; /* 00h */ + uint8_t Bus; /* 01h */ + uint8_t MsgLength; /* 02h */ + uint8_t Function; /* 03h */ + uint8_t CDBLength; /* 04h */ + uint8_t SenseBufferLength; /* 05h */ + uint8_t Reserved; /* 06h */ + uint8_t MsgFlags; /* 07h */ + uint32_t MsgContext; /* 08h */ + uint8_t SCSIStatus; /* 0Ch */ + uint8_t SCSIState; /* 0Dh */ + uint16_t IOCStatus; /* 0Eh */ + uint32_t IOCLogInfo; /* 10h */ + uint32_t TransferCount; /* 14h */ + uint32_t SenseCount; /* 18h */ + uint32_t ResponseInfo; /* 1Ch */ + uint16_t TaskTag; /* 20h */ + uint16_t Reserved1; /* 22h */ +} QEMU_PACKED MPIMsgSCSIIOReply; + +/* SCSI IO Reply SCSIStatus values (SAM-2 status codes) */ + +#define MPI_SCSI_STATUS_SUCCESS (0x00) +#define MPI_SCSI_STATUS_CHECK_CONDITION (0x02) +#define MPI_SCSI_STATUS_CONDITION_MET (0x04) +#define MPI_SCSI_STATUS_BUSY (0x08) +#define MPI_SCSI_STATUS_INTERMEDIATE (0x10) +#define MPI_SCSI_STATUS_INTERMEDIATE_CONDMET (0x14) +#define MPI_SCSI_STATUS_RESERVATION_CONFLICT (0x18) +#define MPI_SCSI_STATUS_COMMAND_TERMINATED (0x22) +#define MPI_SCSI_STATUS_TASK_SET_FULL (0x28) +#define MPI_SCSI_STATUS_ACA_ACTIVE (0x30) + +#define MPI_SCSI_STATUS_FCPEXT_DEVICE_LOGGED_OUT (0x80) +#define MPI_SCSI_STATUS_FCPEXT_NO_LINK (0x81) +#define MPI_SCSI_STATUS_FCPEXT_UNASSIGNED (0x82) + + +/* SCSI IO Reply SCSIState values */ + +#define MPI_SCSI_STATE_AUTOSENSE_VALID (0x01) +#define MPI_SCSI_STATE_AUTOSENSE_FAILED (0x02) +#define MPI_SCSI_STATE_NO_SCSI_STATUS (0x04) +#define MPI_SCSI_STATE_TERMINATED (0x08) +#define MPI_SCSI_STATE_RESPONSE_INFO_VALID (0x10) +#define MPI_SCSI_STATE_QUEUE_TAG_REJECTED (0x20) + +/* SCSI IO Reply ResponseInfo values */ +/* (FCP-1 RSP_CODE values and SPI-3 Packetized Failure codes) */ + +#define MPI_SCSI_RSP_INFO_FUNCTION_COMPLETE (0x00000000) +#define MPI_SCSI_RSP_INFO_FCP_BURST_LEN_ERROR (0x01000000) +#define MPI_SCSI_RSP_INFO_CMND_FIELDS_INVALID (0x02000000) +#define MPI_SCSI_RSP_INFO_FCP_DATA_RO_ERROR (0x03000000) +#define MPI_SCSI_RSP_INFO_TASK_MGMT_UNSUPPORTED (0x04000000) +#define MPI_SCSI_RSP_INFO_TASK_MGMT_FAILED (0x05000000) +#define MPI_SCSI_RSP_INFO_SPI_LQ_INVALID_TYPE (0x06000000) + +#define MPI_SCSI_TASKTAG_UNKNOWN (0xFFFF) + + +/****************************************************************************/ +/* SCSI Task Management messages */ +/****************************************************************************/ + +typedef struct MPIMsgSCSITaskMgmt { + uint8_t TargetID; /* 00h */ + uint8_t Bus; /* 01h */ + uint8_t ChainOffset; /* 02h */ + uint8_t Function; /* 03h */ + uint8_t Reserved; /* 04h */ + uint8_t TaskType; /* 05h */ + uint8_t Reserved1; /* 06h */ + uint8_t MsgFlags; /* 07h */ + uint32_t MsgContext; /* 08h */ + uint8_t LUN[8]; /* 0Ch */ + uint32_t Reserved2[7]; /* 14h */ + uint32_t TaskMsgContext; /* 30h */ +} QEMU_PACKED MPIMsgSCSITaskMgmt; + +enum { + /* TaskType values */ + + MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK = 0x01, + MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET = 0x02, + MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET = 0x03, + MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS = 0x04, + MPI_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET = 0x05, + MPI_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET = 0x06, + MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK = 0x07, + MPI_SCSITASKMGMT_TASKTYPE_CLR_ACA = 0x08, + + /* MsgFlags bits */ + + MPI_SCSITASKMGMT_MSGFLAGS_DO_NOT_SEND_TASK_IU = 0x01, + + MPI_SCSITASKMGMT_MSGFLAGS_TARGET_RESET_OPTION = 0x00, + MPI_SCSITASKMGMT_MSGFLAGS_LIP_RESET_OPTION = 0x02, + MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION = 0x04, + + MPI_SCSITASKMGMT_MSGFLAGS_SOFT_RESET_OPTION = 0x08, +}; + +/* SCSI Task Management Reply */ +typedef struct MPIMsgSCSITaskMgmtReply { + uint8_t TargetID; /* 00h */ + uint8_t Bus; /* 01h */ + uint8_t MsgLength; /* 02h */ + uint8_t Function; /* 03h */ + uint8_t ResponseCode; /* 04h */ + uint8_t TaskType; /* 05h */ + uint8_t Reserved1; /* 06h */ + uint8_t MsgFlags; /* 07h */ + uint32_t MsgContext; /* 08h */ + uint8_t Reserved2[2]; /* 0Ch */ + uint16_t IOCStatus; /* 0Eh */ + uint32_t IOCLogInfo; /* 10h */ + uint32_t TerminationCount; /* 14h */ +} QEMU_PACKED MPIMsgSCSITaskMgmtReply; + +/* ResponseCode values */ +enum { + MPI_SCSITASKMGMT_RSP_TM_COMPLETE = 0x00, + MPI_SCSITASKMGMT_RSP_INVALID_FRAME = 0x02, + MPI_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED = 0x04, + MPI_SCSITASKMGMT_RSP_TM_FAILED = 0x05, + MPI_SCSITASKMGMT_RSP_TM_SUCCEEDED = 0x08, + MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN = 0x09, + MPI_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC = 0x80, +}; + +/****************************************************************************/ +/* IOCInit message */ +/****************************************************************************/ + +typedef struct MPIMsgIOCInit { + uint8_t WhoInit; /* 00h */ + uint8_t Reserved; /* 01h */ + uint8_t ChainOffset; /* 02h */ + uint8_t Function; /* 03h */ + uint8_t Flags; /* 04h */ + uint8_t MaxDevices; /* 05h */ + uint8_t MaxBuses; /* 06h */ + uint8_t MsgFlags; /* 07h */ + uint32_t MsgContext; /* 08h */ + uint16_t ReplyFrameSize; /* 0Ch */ + uint8_t Reserved1[2]; /* 0Eh */ + uint32_t HostMfaHighAddr; /* 10h */ + uint32_t SenseBufferHighAddr; /* 14h */ + uint32_t ReplyFifoHostSignalingAddr; /* 18h */ + MPISGEntry HostPageBufferSGE; /* 1Ch */ + uint16_t MsgVersion; /* 28h */ + uint16_t HeaderVersion; /* 2Ah */ +} QEMU_PACKED MPIMsgIOCInit; + +enum { + /* WhoInit values */ + + MPI_WHOINIT_NO_ONE = 0x00, + MPI_WHOINIT_SYSTEM_BIOS = 0x01, + MPI_WHOINIT_ROM_BIOS = 0x02, + MPI_WHOINIT_PCI_PEER = 0x03, + MPI_WHOINIT_HOST_DRIVER = 0x04, + MPI_WHOINIT_MANUFACTURER = 0x05, + + /* Flags values */ + + MPI_IOCINIT_FLAGS_HOST_PAGE_BUFFER_PERSISTENT = 0x04, + MPI_IOCINIT_FLAGS_REPLY_FIFO_HOST_SIGNAL = 0x02, + MPI_IOCINIT_FLAGS_DISCARD_FW_IMAGE = 0x01, + + /* MsgVersion */ + + MPI_IOCINIT_MSGVERSION_MAJOR_MASK = 0xFF00, + MPI_IOCINIT_MSGVERSION_MAJOR_SHIFT = 8, + MPI_IOCINIT_MSGVERSION_MINOR_MASK = 0x00FF, + MPI_IOCINIT_MSGVERSION_MINOR_SHIFT = 0, + + /* HeaderVersion */ + + MPI_IOCINIT_HEADERVERSION_UNIT_MASK = 0xFF00, + MPI_IOCINIT_HEADERVERSION_UNIT_SHIFT = 8, + MPI_IOCINIT_HEADERVERSION_DEV_MASK = 0x00FF, + MPI_IOCINIT_HEADERVERSION_DEV_SHIFT = 0, +}; + +typedef struct MPIMsgIOCInitReply { + uint8_t WhoInit; /* 00h */ + uint8_t Reserved; /* 01h */ + uint8_t MsgLength; /* 02h */ + uint8_t Function; /* 03h */ + uint8_t Flags; /* 04h */ + uint8_t MaxDevices; /* 05h */ + uint8_t MaxBuses; /* 06h */ + uint8_t MsgFlags; /* 07h */ + uint32_t MsgContext; /* 08h */ + uint16_t Reserved2; /* 0Ch */ + uint16_t IOCStatus; /* 0Eh */ + uint32_t IOCLogInfo; /* 10h */ +} QEMU_PACKED MPIMsgIOCInitReply; + + + +/****************************************************************************/ +/* IOC Facts message */ +/****************************************************************************/ + +typedef struct MPIMsgIOCFacts { + uint8_t Reserved[2]; /* 00h */ + uint8_t ChainOffset; /* 01h */ + uint8_t Function; /* 02h */ + uint8_t Reserved1[3]; /* 03h */ + uint8_t MsgFlags; /* 04h */ + uint32_t MsgContext; /* 08h */ +} QEMU_PACKED MPIMsgIOCFacts; + +/* IOC Facts Reply */ +typedef struct MPIMsgIOCFactsReply { + uint16_t MsgVersion; /* 00h */ + uint8_t MsgLength; /* 02h */ + uint8_t Function; /* 03h */ + uint16_t HeaderVersion; /* 04h */ + uint8_t IOCNumber; /* 06h */ + uint8_t MsgFlags; /* 07h */ + uint32_t MsgContext; /* 08h */ + uint16_t IOCExceptions; /* 0Ch */ + uint16_t IOCStatus; /* 0Eh */ + uint32_t IOCLogInfo; /* 10h */ + uint8_t MaxChainDepth; /* 14h */ + uint8_t WhoInit; /* 15h */ + uint8_t BlockSize; /* 16h */ + uint8_t Flags; /* 17h */ + uint16_t ReplyQueueDepth; /* 18h */ + uint16_t RequestFrameSize; /* 1Ah */ + uint16_t Reserved_0101_FWVersion; /* 1Ch */ /* obsolete 16-bit FWVersion */ + uint16_t ProductID; /* 1Eh */ + uint32_t CurrentHostMfaHighAddr; /* 20h */ + uint16_t GlobalCredits; /* 24h */ + uint8_t NumberOfPorts; /* 26h */ + uint8_t EventState; /* 27h */ + uint32_t CurrentSenseBufferHighAddr; /* 28h */ + uint16_t CurReplyFrameSize; /* 2Ch */ + uint8_t MaxDevices; /* 2Eh */ + uint8_t MaxBuses; /* 2Fh */ + uint32_t FWImageSize; /* 30h */ + uint32_t IOCCapabilities; /* 34h */ + uint8_t FWVersionDev; /* 38h */ + uint8_t FWVersionUnit; /* 39h */ + uint8_t FWVersionMinor; /* 3ah */ + uint8_t FWVersionMajor; /* 3bh */ + uint16_t HighPriorityQueueDepth; /* 3Ch */ + uint16_t Reserved2; /* 3Eh */ + MPISGEntry HostPageBufferSGE; /* 40h */ + uint32_t ReplyFifoHostSignalingAddr; /* 4Ch */ +} QEMU_PACKED MPIMsgIOCFactsReply; + +enum { + MPI_IOCFACTS_MSGVERSION_MAJOR_MASK = 0xFF00, + MPI_IOCFACTS_MSGVERSION_MAJOR_SHIFT = 8, + MPI_IOCFACTS_MSGVERSION_MINOR_MASK = 0x00FF, + MPI_IOCFACTS_MSGVERSION_MINOR_SHIFT = 0, + + MPI_IOCFACTS_HDRVERSION_UNIT_MASK = 0xFF00, + MPI_IOCFACTS_HDRVERSION_UNIT_SHIFT = 8, + MPI_IOCFACTS_HDRVERSION_DEV_MASK = 0x00FF, + MPI_IOCFACTS_HDRVERSION_DEV_SHIFT = 0, + + MPI_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL = 0x0001, + MPI_IOCFACTS_EXCEPT_RAID_CONFIG_INVALID = 0x0002, + MPI_IOCFACTS_EXCEPT_FW_CHECKSUM_FAIL = 0x0004, + MPI_IOCFACTS_EXCEPT_PERSISTENT_TABLE_FULL = 0x0008, + MPI_IOCFACTS_EXCEPT_METADATA_UNSUPPORTED = 0x0010, + + MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT = 0x01, + MPI_IOCFACTS_FLAGS_REPLY_FIFO_HOST_SIGNAL = 0x02, + MPI_IOCFACTS_FLAGS_HOST_PAGE_BUFFER_PERSISTENT = 0x04, + + MPI_IOCFACTS_EVENTSTATE_DISABLED = 0x00, + MPI_IOCFACTS_EVENTSTATE_ENABLED = 0x01, + + MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q = 0x00000001, + MPI_IOCFACTS_CAPABILITY_REPLY_HOST_SIGNAL = 0x00000002, + MPI_IOCFACTS_CAPABILITY_QUEUE_FULL_HANDLING = 0x00000004, + MPI_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER = 0x00000008, + MPI_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER = 0x00000010, + MPI_IOCFACTS_CAPABILITY_EXTENDED_BUFFER = 0x00000020, + MPI_IOCFACTS_CAPABILITY_EEDP = 0x00000040, + MPI_IOCFACTS_CAPABILITY_BIDIRECTIONAL = 0x00000080, + MPI_IOCFACTS_CAPABILITY_MULTICAST = 0x00000100, + MPI_IOCFACTS_CAPABILITY_SCSIIO32 = 0x00000200, + MPI_IOCFACTS_CAPABILITY_NO_SCSIIO16 = 0x00000400, + MPI_IOCFACTS_CAPABILITY_TLR = 0x00000800, +}; + +/****************************************************************************/ +/* Port Facts message and Reply */ +/****************************************************************************/ + +typedef struct MPIMsgPortFacts { + uint8_t Reserved[2]; /* 00h */ + uint8_t ChainOffset; /* 02h */ + uint8_t Function; /* 03h */ + uint8_t Reserved1[2]; /* 04h */ + uint8_t PortNumber; /* 06h */ + uint8_t MsgFlags; /* 07h */ + uint32_t MsgContext; /* 08h */ +} QEMU_PACKED MPIMsgPortFacts; + +typedef struct MPIMsgPortFactsReply { + uint16_t Reserved; /* 00h */ + uint8_t MsgLength; /* 02h */ + uint8_t Function; /* 03h */ + uint16_t Reserved1; /* 04h */ + uint8_t PortNumber; /* 06h */ + uint8_t MsgFlags; /* 07h */ + uint32_t MsgContext; /* 08h */ + uint16_t Reserved2; /* 0Ch */ + uint16_t IOCStatus; /* 0Eh */ + uint32_t IOCLogInfo; /* 10h */ + uint8_t Reserved3; /* 14h */ + uint8_t PortType; /* 15h */ + uint16_t MaxDevices; /* 16h */ + uint16_t PortSCSIID; /* 18h */ + uint16_t ProtocolFlags; /* 1Ah */ + uint16_t MaxPostedCmdBuffers; /* 1Ch */ + uint16_t MaxPersistentIDs; /* 1Eh */ + uint16_t MaxLanBuckets; /* 20h */ + uint8_t MaxInitiators; /* 22h */ + uint8_t Reserved4; /* 23h */ + uint32_t Reserved5; /* 24h */ +} QEMU_PACKED MPIMsgPortFactsReply; + + +enum { + /* PortTypes values */ + MPI_PORTFACTS_PORTTYPE_INACTIVE = 0x00, + MPI_PORTFACTS_PORTTYPE_SCSI = 0x01, + MPI_PORTFACTS_PORTTYPE_FC = 0x10, + MPI_PORTFACTS_PORTTYPE_ISCSI = 0x20, + MPI_PORTFACTS_PORTTYPE_SAS = 0x30, + + /* ProtocolFlags values */ + MPI_PORTFACTS_PROTOCOL_LOGBUSADDR = 0x01, + MPI_PORTFACTS_PROTOCOL_LAN = 0x02, + MPI_PORTFACTS_PROTOCOL_TARGET = 0x04, + MPI_PORTFACTS_PROTOCOL_INITIATOR = 0x08, +}; + + +/****************************************************************************/ +/* Port Enable Message */ +/****************************************************************************/ + +typedef struct MPIMsgPortEnable { + uint8_t Reserved[2]; /* 00h */ + uint8_t ChainOffset; /* 02h */ + uint8_t Function; /* 03h */ + uint8_t Reserved1[2]; /* 04h */ + uint8_t PortNumber; /* 06h */ + uint8_t MsgFlags; /* 07h */ + uint32_t MsgContext; /* 08h */ +} QEMU_PACKED MPIMsgPortEnable; + +typedef struct MPIMsgPortEnableReply { + uint8_t Reserved[2]; /* 00h */ + uint8_t MsgLength; /* 02h */ + uint8_t Function; /* 03h */ + uint8_t Reserved1[2]; /* 04h */ + uint8_t PortNumber; /* 05h */ + uint8_t MsgFlags; /* 07h */ + uint32_t MsgContext; /* 08h */ + uint16_t Reserved2; /* 0Ch */ + uint16_t IOCStatus; /* 0Eh */ + uint32_t IOCLogInfo; /* 10h */ +} QEMU_PACKED MPIMsgPortEnableReply; + +/****************************************************************************/ +/* Event Notification messages */ +/****************************************************************************/ + +typedef struct MPIMsgEventNotify { + uint8_t Switch; /* 00h */ + uint8_t Reserved; /* 01h */ + uint8_t ChainOffset; /* 02h */ + uint8_t Function; /* 03h */ + uint8_t Reserved1[3]; /* 04h */ + uint8_t MsgFlags; /* 07h */ + uint32_t MsgContext; /* 08h */ +} QEMU_PACKED MPIMsgEventNotify; + +/* Event Notification Reply */ + +typedef struct MPIMsgEventNotifyReply { + uint16_t EventDataLength; /* 00h */ + uint8_t MsgLength; /* 02h */ + uint8_t Function; /* 03h */ + uint8_t Reserved1[2]; /* 04h */ + uint8_t AckRequired; /* 06h */ + uint8_t MsgFlags; /* 07h */ + uint32_t MsgContext; /* 08h */ + uint8_t Reserved2[2]; /* 0Ch */ + uint16_t IOCStatus; /* 0Eh */ + uint32_t IOCLogInfo; /* 10h */ + uint32_t Event; /* 14h */ + uint32_t EventContext; /* 18h */ + uint32_t Data[1]; /* 1Ch */ +} QEMU_PACKED MPIMsgEventNotifyReply; + +/* Event Acknowledge */ + +typedef struct MPIMsgEventAck { + uint8_t Reserved[2]; /* 00h */ + uint8_t ChainOffset; /* 02h */ + uint8_t Function; /* 03h */ + uint8_t Reserved1[3]; /* 04h */ + uint8_t MsgFlags; /* 07h */ + uint32_t MsgContext; /* 08h */ + uint32_t Event; /* 0Ch */ + uint32_t EventContext; /* 10h */ +} QEMU_PACKED MPIMsgEventAck; + +typedef struct MPIMsgEventAckReply { + uint8_t Reserved[2]; /* 00h */ + uint8_t MsgLength; /* 02h */ + uint8_t Function; /* 03h */ + uint8_t Reserved1[3]; /* 04h */ + uint8_t MsgFlags; /* 07h */ + uint32_t MsgContext; /* 08h */ + uint16_t Reserved2; /* 0Ch */ + uint16_t IOCStatus; /* 0Eh */ + uint32_t IOCLogInfo; /* 10h */ +} QEMU_PACKED MPIMsgEventAckReply; + +enum { + /* Switch */ + + MPI_EVENT_NOTIFICATION_SWITCH_OFF = 0x00, + MPI_EVENT_NOTIFICATION_SWITCH_ON = 0x01, + + /* Event */ + + MPI_EVENT_NONE = 0x00000000, + MPI_EVENT_LOG_DATA = 0x00000001, + MPI_EVENT_STATE_CHANGE = 0x00000002, + MPI_EVENT_UNIT_ATTENTION = 0x00000003, + MPI_EVENT_IOC_BUS_RESET = 0x00000004, + MPI_EVENT_EXT_BUS_RESET = 0x00000005, + MPI_EVENT_RESCAN = 0x00000006, + MPI_EVENT_LINK_STATUS_CHANGE = 0x00000007, + MPI_EVENT_LOOP_STATE_CHANGE = 0x00000008, + MPI_EVENT_LOGOUT = 0x00000009, + MPI_EVENT_EVENT_CHANGE = 0x0000000A, + MPI_EVENT_INTEGRATED_RAID = 0x0000000B, + MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE = 0x0000000C, + MPI_EVENT_ON_BUS_TIMER_EXPIRED = 0x0000000D, + MPI_EVENT_QUEUE_FULL = 0x0000000E, + MPI_EVENT_SAS_DEVICE_STATUS_CHANGE = 0x0000000F, + MPI_EVENT_SAS_SES = 0x00000010, + MPI_EVENT_PERSISTENT_TABLE_FULL = 0x00000011, + MPI_EVENT_SAS_PHY_LINK_STATUS = 0x00000012, + MPI_EVENT_SAS_DISCOVERY_ERROR = 0x00000013, + MPI_EVENT_IR_RESYNC_UPDATE = 0x00000014, + MPI_EVENT_IR2 = 0x00000015, + MPI_EVENT_SAS_DISCOVERY = 0x00000016, + MPI_EVENT_SAS_BROADCAST_PRIMITIVE = 0x00000017, + MPI_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE = 0x00000018, + MPI_EVENT_SAS_INIT_TABLE_OVERFLOW = 0x00000019, + MPI_EVENT_SAS_SMP_ERROR = 0x0000001A, + MPI_EVENT_SAS_EXPANDER_STATUS_CHANGE = 0x0000001B, + MPI_EVENT_LOG_ENTRY_ADDED = 0x00000021, + + /* AckRequired field values */ + + MPI_EVENT_NOTIFICATION_ACK_NOT_REQUIRED = 0x00, + MPI_EVENT_NOTIFICATION_ACK_REQUIRED = 0x01, +}; + +/**************************************************************************** +* Config Request Message +****************************************************************************/ + +typedef struct MPIMsgConfig { + uint8_t Action; /* 00h */ + uint8_t Reserved; /* 01h */ + uint8_t ChainOffset; /* 02h */ + uint8_t Function; /* 03h */ + uint16_t ExtPageLength; /* 04h */ + uint8_t ExtPageType; /* 06h */ + uint8_t MsgFlags; /* 07h */ + uint32_t MsgContext; /* 08h */ + uint8_t Reserved2[8]; /* 0Ch */ + uint8_t PageVersion; /* 14h */ + uint8_t PageLength; /* 15h */ + uint8_t PageNumber; /* 16h */ + uint8_t PageType; /* 17h */ + uint32_t PageAddress; /* 18h */ + MPISGEntry PageBufferSGE; /* 1Ch */ +} QEMU_PACKED MPIMsgConfig; + +/* Action field values */ + +enum { + MPI_CONFIG_ACTION_PAGE_HEADER = 0x00, + MPI_CONFIG_ACTION_PAGE_READ_CURRENT = 0x01, + MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT = 0x02, + MPI_CONFIG_ACTION_PAGE_DEFAULT = 0x03, + MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM = 0x04, + MPI_CONFIG_ACTION_PAGE_READ_DEFAULT = 0x05, + MPI_CONFIG_ACTION_PAGE_READ_NVRAM = 0x06, +}; + + +/* Config Reply Message */ +typedef struct MPIMsgConfigReply { + uint8_t Action; /* 00h */ + uint8_t Reserved; /* 01h */ + uint8_t MsgLength; /* 02h */ + uint8_t Function; /* 03h */ + uint16_t ExtPageLength; /* 04h */ + uint8_t ExtPageType; /* 06h */ + uint8_t MsgFlags; /* 07h */ + uint32_t MsgContext; /* 08h */ + uint8_t Reserved2[2]; /* 0Ch */ + uint16_t IOCStatus; /* 0Eh */ + uint32_t IOCLogInfo; /* 10h */ + uint8_t PageVersion; /* 14h */ + uint8_t PageLength; /* 15h */ + uint8_t PageNumber; /* 16h */ + uint8_t PageType; /* 17h */ +} QEMU_PACKED MPIMsgConfigReply; + +enum { + /* PageAddress field values */ + MPI_CONFIG_PAGEATTR_READ_ONLY = 0x00, + MPI_CONFIG_PAGEATTR_CHANGEABLE = 0x10, + MPI_CONFIG_PAGEATTR_PERSISTENT = 0x20, + MPI_CONFIG_PAGEATTR_RO_PERSISTENT = 0x30, + MPI_CONFIG_PAGEATTR_MASK = 0xF0, + + MPI_CONFIG_PAGETYPE_IO_UNIT = 0x00, + MPI_CONFIG_PAGETYPE_IOC = 0x01, + MPI_CONFIG_PAGETYPE_BIOS = 0x02, + MPI_CONFIG_PAGETYPE_SCSI_PORT = 0x03, + MPI_CONFIG_PAGETYPE_SCSI_DEVICE = 0x04, + MPI_CONFIG_PAGETYPE_FC_PORT = 0x05, + MPI_CONFIG_PAGETYPE_FC_DEVICE = 0x06, + MPI_CONFIG_PAGETYPE_LAN = 0x07, + MPI_CONFIG_PAGETYPE_RAID_VOLUME = 0x08, + MPI_CONFIG_PAGETYPE_MANUFACTURING = 0x09, + MPI_CONFIG_PAGETYPE_RAID_PHYSDISK = 0x0A, + MPI_CONFIG_PAGETYPE_INBAND = 0x0B, + MPI_CONFIG_PAGETYPE_EXTENDED = 0x0F, + MPI_CONFIG_PAGETYPE_MASK = 0x0F, + + MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT = 0x10, + MPI_CONFIG_EXTPAGETYPE_SAS_EXPANDER = 0x11, + MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE = 0x12, + MPI_CONFIG_EXTPAGETYPE_SAS_PHY = 0x13, + MPI_CONFIG_EXTPAGETYPE_LOG = 0x14, + MPI_CONFIG_EXTPAGETYPE_ENCLOSURE = 0x15, + + MPI_SCSI_PORT_PGAD_PORT_MASK = 0x000000FF, + + MPI_SCSI_DEVICE_FORM_MASK = 0xF0000000, + MPI_SCSI_DEVICE_FORM_BUS_TID = 0x00000000, + MPI_SCSI_DEVICE_TARGET_ID_MASK = 0x000000FF, + MPI_SCSI_DEVICE_TARGET_ID_SHIFT = 0, + MPI_SCSI_DEVICE_BUS_MASK = 0x0000FF00, + MPI_SCSI_DEVICE_BUS_SHIFT = 8, + MPI_SCSI_DEVICE_FORM_TARGET_MODE = 0x10000000, + MPI_SCSI_DEVICE_TM_RESPOND_ID_MASK = 0x000000FF, + MPI_SCSI_DEVICE_TM_RESPOND_ID_SHIFT = 0, + MPI_SCSI_DEVICE_TM_BUS_MASK = 0x0000FF00, + MPI_SCSI_DEVICE_TM_BUS_SHIFT = 8, + MPI_SCSI_DEVICE_TM_INIT_ID_MASK = 0x00FF0000, + MPI_SCSI_DEVICE_TM_INIT_ID_SHIFT = 16, + + MPI_FC_PORT_PGAD_PORT_MASK = 0xF0000000, + MPI_FC_PORT_PGAD_PORT_SHIFT = 28, + MPI_FC_PORT_PGAD_FORM_MASK = 0x0F000000, + MPI_FC_PORT_PGAD_FORM_INDEX = 0x01000000, + MPI_FC_PORT_PGAD_INDEX_MASK = 0x0000FFFF, + MPI_FC_PORT_PGAD_INDEX_SHIFT = 0, + + MPI_FC_DEVICE_PGAD_PORT_MASK = 0xF0000000, + MPI_FC_DEVICE_PGAD_PORT_SHIFT = 28, + MPI_FC_DEVICE_PGAD_FORM_MASK = 0x0F000000, + MPI_FC_DEVICE_PGAD_FORM_NEXT_DID = 0x00000000, + MPI_FC_DEVICE_PGAD_ND_PORT_MASK = 0xF0000000, + MPI_FC_DEVICE_PGAD_ND_PORT_SHIFT = 28, + MPI_FC_DEVICE_PGAD_ND_DID_MASK = 0x00FFFFFF, + MPI_FC_DEVICE_PGAD_ND_DID_SHIFT = 0, + MPI_FC_DEVICE_PGAD_FORM_BUS_TID = 0x01000000, + MPI_FC_DEVICE_PGAD_BT_BUS_MASK = 0x0000FF00, + MPI_FC_DEVICE_PGAD_BT_BUS_SHIFT = 8, + MPI_FC_DEVICE_PGAD_BT_TID_MASK = 0x000000FF, + MPI_FC_DEVICE_PGAD_BT_TID_SHIFT = 0, + + MPI_PHYSDISK_PGAD_PHYSDISKNUM_MASK = 0x000000FF, + MPI_PHYSDISK_PGAD_PHYSDISKNUM_SHIFT = 0, + + MPI_SAS_EXPAND_PGAD_FORM_MASK = 0xF0000000, + MPI_SAS_EXPAND_PGAD_FORM_SHIFT = 28, + MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE = 0x00000000, + MPI_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM = 0x00000001, + MPI_SAS_EXPAND_PGAD_FORM_HANDLE = 0x00000002, + MPI_SAS_EXPAND_PGAD_GNH_MASK_HANDLE = 0x0000FFFF, + MPI_SAS_EXPAND_PGAD_GNH_SHIFT_HANDLE = 0, + MPI_SAS_EXPAND_PGAD_HPN_MASK_PHY = 0x00FF0000, + MPI_SAS_EXPAND_PGAD_HPN_SHIFT_PHY = 16, + MPI_SAS_EXPAND_PGAD_HPN_MASK_HANDLE = 0x0000FFFF, + MPI_SAS_EXPAND_PGAD_HPN_SHIFT_HANDLE = 0, + MPI_SAS_EXPAND_PGAD_H_MASK_HANDLE = 0x0000FFFF, + MPI_SAS_EXPAND_PGAD_H_SHIFT_HANDLE = 0, + + MPI_SAS_DEVICE_PGAD_FORM_MASK = 0xF0000000, + MPI_SAS_DEVICE_PGAD_FORM_SHIFT = 28, + MPI_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE = 0x00000000, + MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID = 0x00000001, + MPI_SAS_DEVICE_PGAD_FORM_HANDLE = 0x00000002, + MPI_SAS_DEVICE_PGAD_GNH_HANDLE_MASK = 0x0000FFFF, + MPI_SAS_DEVICE_PGAD_GNH_HANDLE_SHIFT = 0, + MPI_SAS_DEVICE_PGAD_BT_BUS_MASK = 0x0000FF00, + MPI_SAS_DEVICE_PGAD_BT_BUS_SHIFT = 8, + MPI_SAS_DEVICE_PGAD_BT_TID_MASK = 0x000000FF, + MPI_SAS_DEVICE_PGAD_BT_TID_SHIFT = 0, + MPI_SAS_DEVICE_PGAD_H_HANDLE_MASK = 0x0000FFFF, + MPI_SAS_DEVICE_PGAD_H_HANDLE_SHIFT = 0, + + MPI_SAS_PHY_PGAD_FORM_MASK = 0xF0000000, + MPI_SAS_PHY_PGAD_FORM_SHIFT = 28, + MPI_SAS_PHY_PGAD_FORM_PHY_NUMBER = 0x0, + MPI_SAS_PHY_PGAD_FORM_PHY_TBL_INDEX = 0x1, + MPI_SAS_PHY_PGAD_PHY_NUMBER_MASK = 0x000000FF, + MPI_SAS_PHY_PGAD_PHY_NUMBER_SHIFT = 0, + MPI_SAS_PHY_PGAD_PHY_TBL_INDEX_MASK = 0x0000FFFF, + MPI_SAS_PHY_PGAD_PHY_TBL_INDEX_SHIFT = 0, + + MPI_SAS_ENCLOS_PGAD_FORM_MASK = 0xF0000000, + MPI_SAS_ENCLOS_PGAD_FORM_SHIFT = 28, + MPI_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE = 0x00000000, + MPI_SAS_ENCLOS_PGAD_FORM_HANDLE = 0x00000001, + MPI_SAS_ENCLOS_PGAD_GNH_HANDLE_MASK = 0x0000FFFF, + MPI_SAS_ENCLOS_PGAD_GNH_HANDLE_SHIFT = 0, + MPI_SAS_ENCLOS_PGAD_H_HANDLE_MASK = 0x0000FFFF, + MPI_SAS_ENCLOS_PGAD_H_HANDLE_SHIFT = 0, +}; + +/* Too many structs and definitions... see mptconfig.c for the few + * that are used. + */ + +/****************************************************************************/ +/* Firmware Upload message and associated structures */ +/****************************************************************************/ + +enum { + /* defines for using the ProductId field */ + MPI_FW_HEADER_PID_TYPE_MASK = 0xF000, + MPI_FW_HEADER_PID_TYPE_SCSI = 0x0000, + MPI_FW_HEADER_PID_TYPE_FC = 0x1000, + MPI_FW_HEADER_PID_TYPE_SAS = 0x2000, + + MPI_FW_HEADER_PID_PROD_MASK = 0x0F00, + MPI_FW_HEADER_PID_PROD_INITIATOR_SCSI = 0x0100, + MPI_FW_HEADER_PID_PROD_TARGET_INITIATOR_SCSI = 0x0200, + MPI_FW_HEADER_PID_PROD_TARGET_SCSI = 0x0300, + MPI_FW_HEADER_PID_PROD_IM_SCSI = 0x0400, + MPI_FW_HEADER_PID_PROD_IS_SCSI = 0x0500, + MPI_FW_HEADER_PID_PROD_CTX_SCSI = 0x0600, + MPI_FW_HEADER_PID_PROD_IR_SCSI = 0x0700, + + MPI_FW_HEADER_PID_FAMILY_MASK = 0x00FF, + + /* SCSI */ + MPI_FW_HEADER_PID_FAMILY_1030A0_SCSI = 0x0001, + MPI_FW_HEADER_PID_FAMILY_1030B0_SCSI = 0x0002, + MPI_FW_HEADER_PID_FAMILY_1030B1_SCSI = 0x0003, + MPI_FW_HEADER_PID_FAMILY_1030C0_SCSI = 0x0004, + MPI_FW_HEADER_PID_FAMILY_1020A0_SCSI = 0x0005, + MPI_FW_HEADER_PID_FAMILY_1020B0_SCSI = 0x0006, + MPI_FW_HEADER_PID_FAMILY_1020B1_SCSI = 0x0007, + MPI_FW_HEADER_PID_FAMILY_1020C0_SCSI = 0x0008, + MPI_FW_HEADER_PID_FAMILY_1035A0_SCSI = 0x0009, + MPI_FW_HEADER_PID_FAMILY_1035B0_SCSI = 0x000A, + MPI_FW_HEADER_PID_FAMILY_1030TA0_SCSI = 0x000B, + MPI_FW_HEADER_PID_FAMILY_1020TA0_SCSI = 0x000C, + + /* Fibre Channel */ + MPI_FW_HEADER_PID_FAMILY_909_FC = 0x0000, + MPI_FW_HEADER_PID_FAMILY_919_FC = 0x0001, /* 919 and 929 */ + MPI_FW_HEADER_PID_FAMILY_919X_FC = 0x0002, /* 919X and 929X */ + MPI_FW_HEADER_PID_FAMILY_919XL_FC = 0x0003, /* 919XL and 929XL */ + MPI_FW_HEADER_PID_FAMILY_939X_FC = 0x0004, /* 939X and 949X */ + MPI_FW_HEADER_PID_FAMILY_959_FC = 0x0005, + MPI_FW_HEADER_PID_FAMILY_949E_FC = 0x0006, + + /* SAS */ + MPI_FW_HEADER_PID_FAMILY_1064_SAS = 0x0001, + MPI_FW_HEADER_PID_FAMILY_1068_SAS = 0x0002, + MPI_FW_HEADER_PID_FAMILY_1078_SAS = 0x0003, + MPI_FW_HEADER_PID_FAMILY_106xE_SAS = 0x0004, /* 1068E, 1066E, and 1064E */ +}; + +#endif diff --git a/qemu/hw/scsi/mptconfig.c b/qemu/hw/scsi/mptconfig.c new file mode 100644 index 000000000..707185469 --- /dev/null +++ b/qemu/hw/scsi/mptconfig.c @@ -0,0 +1,905 @@ +/* + * QEMU LSI SAS1068 Host Bus Adapter emulation - configuration pages + * + * Copyright (c) 2016 Red Hat, Inc. + * + * Author: Paolo Bonzini + * + * 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. + */ +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "hw/scsi/scsi.h" + +#include "mptsas.h" +#include "mpi.h" +#include "trace.h" + +/* Generic functions for marshaling and unmarshaling. */ + +#define repl1(x) x +#define repl2(x) x x +#define repl3(x) x x x +#define repl4(x) x x x x +#define repl5(x) x x x x x +#define repl6(x) x x x x x x +#define repl7(x) x x x x x x x +#define repl8(x) x x x x x x x x + +#define repl(n, x) glue(repl, n)(x) + +typedef union PackValue { + uint64_t ll; + char *str; +} PackValue; + +static size_t vfill(uint8_t *data, size_t size, const char *fmt, va_list ap) +{ + size_t ofs; + PackValue val; + const char *p; + + ofs = 0; + p = fmt; + while (*p) { + memset(&val, 0, sizeof(val)); + switch (*p) { + case '*': + p++; + break; + case 'b': + case 'w': + case 'l': + val.ll = va_arg(ap, int); + break; + case 'q': + val.ll = va_arg(ap, int64_t); + break; + case 's': + val.str = va_arg(ap, void *); + break; + } + switch (*p++) { + case 'b': + if (data) { + stb_p(data + ofs, val.ll); + } + ofs++; + break; + case 'w': + if (data) { + stw_le_p(data + ofs, val.ll); + } + ofs += 2; + break; + case 'l': + if (data) { + stl_le_p(data + ofs, val.ll); + } + ofs += 4; + break; + case 'q': + if (data) { + stq_le_p(data + ofs, val.ll); + } + ofs += 8; + break; + case 's': + { + int cnt = atoi(p); + if (data) { + if (val.str) { + strncpy((void *)data + ofs, val.str, cnt); + } else { + memset((void *)data + ofs, 0, cnt); + } + } + ofs += cnt; + break; + } + } + } + + return ofs; +} + +static size_t vpack(uint8_t **p_data, const char *fmt, va_list ap1) +{ + size_t size = 0; + uint8_t *data = NULL; + + if (p_data) { + va_list ap2; + + va_copy(ap2, ap1); + size = vfill(NULL, 0, fmt, ap2); + *p_data = data = g_malloc(size); + va_end(ap2); + } + return vfill(data, size, fmt, ap1); +} + +static size_t fill(uint8_t *data, size_t size, const char *fmt, ...) +{ + va_list ap; + size_t ret; + + va_start(ap, fmt); + ret = vfill(data, size, fmt, ap); + va_end(ap); + + return ret; +} + +/* Functions to build the page header and fill in the length, always used + * through the macros. + */ + +#define MPTSAS_CONFIG_PACK(number, type, version, fmt, ...) \ + mptsas_config_pack(data, "b*bbb" fmt, version, number, type, \ + ## __VA_ARGS__) + +static size_t mptsas_config_pack(uint8_t **data, const char *fmt, ...) +{ + va_list ap; + size_t ret; + + va_start(ap, fmt); + ret = vpack(data, fmt, ap); + va_end(ap); + + if (data) { + assert(ret < 256 && (ret % 4) == 0); + stb_p(*data + 1, ret / 4); + } + return ret; +} + +#define MPTSAS_CONFIG_PACK_EXT(number, type, version, fmt, ...) \ + mptsas_config_pack_ext(data, "b*bbb*wb*b" fmt, version, number, \ + MPI_CONFIG_PAGETYPE_EXTENDED, type, ## __VA_ARGS__) + +static size_t mptsas_config_pack_ext(uint8_t **data, const char *fmt, ...) +{ + va_list ap; + size_t ret; + + va_start(ap, fmt); + ret = vpack(data, fmt, ap); + va_end(ap); + + if (data) { + assert(ret < 65536 && (ret % 4) == 0); + stw_le_p(*data + 4, ret / 4); + } + return ret; +} + +/* Manufacturing pages */ + +static +size_t mptsas_config_manufacturing_0(MPTSASState *s, uint8_t **data, int address) +{ + return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, + "s16s8s16s16s16", + "QEMU MPT Fusion", + "2.5", + "QEMU MPT Fusion", + "QEMU", + "0000111122223333"); +} + +static +size_t mptsas_config_manufacturing_1(MPTSASState *s, uint8_t **data, int address) +{ + /* VPD - all zeros */ + return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, + "s256"); +} + +static +size_t mptsas_config_manufacturing_2(MPTSASState *s, uint8_t **data, int address) +{ + PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s); + return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, + "wb*b*l", + pcic->device_id, pcic->revision); +} + +static +size_t mptsas_config_manufacturing_3(MPTSASState *s, uint8_t **data, int address) +{ + PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s); + return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, + "wb*b*l", + pcic->device_id, pcic->revision); +} + +static +size_t mptsas_config_manufacturing_4(MPTSASState *s, uint8_t **data, int address) +{ + /* All zeros */ + return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x05, + "*l*b*b*b*b*b*b*w*s56*l*l*l*l*l*l" + "*b*b*w*b*b*w*l*l"); +} + +static +size_t mptsas_config_manufacturing_5(MPTSASState *s, uint8_t **data, int address) +{ + return MPTSAS_CONFIG_PACK(5, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x02, + "q*b*b*w*l*l", s->sas_addr); +} + +static +size_t mptsas_config_manufacturing_6(MPTSASState *s, uint8_t **data, int address) +{ + return MPTSAS_CONFIG_PACK(6, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, + "*l"); +} + +static +size_t mptsas_config_manufacturing_7(MPTSASState *s, uint8_t **data, int address) +{ + return MPTSAS_CONFIG_PACK(7, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, + "*l*l*l*s16*b*b*w", MPTSAS_NUM_PORTS); +} + +static +size_t mptsas_config_manufacturing_8(MPTSASState *s, uint8_t **data, int address) +{ + return MPTSAS_CONFIG_PACK(8, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, + "*l"); +} + +static +size_t mptsas_config_manufacturing_9(MPTSASState *s, uint8_t **data, int address) +{ + return MPTSAS_CONFIG_PACK(9, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, + "*l"); +} + +static +size_t mptsas_config_manufacturing_10(MPTSASState *s, uint8_t **data, int address) +{ + return MPTSAS_CONFIG_PACK(10, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, + "*l"); +} + +/* I/O unit pages */ + +static +size_t mptsas_config_io_unit_0(MPTSASState *s, uint8_t **data, int address) +{ + PCIDevice *pci = PCI_DEVICE(s); + uint64_t unique_value = 0x53504D554D4551LL; /* "QEMUMPTx" */ + + unique_value |= (uint64_t)pci->devfn << 56; + return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x00, + "q", unique_value); +} + +static +size_t mptsas_config_io_unit_1(MPTSASState *s, uint8_t **data, int address) +{ + return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x02, "l", + 0x41 /* single function, RAID disabled */ ); +} + +static +size_t mptsas_config_io_unit_2(MPTSASState *s, uint8_t **data, int address) +{ + PCIDevice *pci = PCI_DEVICE(s); + uint8_t devfn = pci->devfn; + return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x02, + "llbbw*b*b*w*b*b*w*b*b*w*l", + 0, 0x100, 0 /* pci bus? */, devfn, 0); +} + +static +size_t mptsas_config_io_unit_3(MPTSASState *s, uint8_t **data, int address) +{ + return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x01, + "*b*b*w*l"); +} + +static +size_t mptsas_config_io_unit_4(MPTSASState *s, uint8_t **data, int address) +{ + return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x00, "*l*l*q"); +} + +/* I/O controller pages */ + +static +size_t mptsas_config_ioc_0(MPTSASState *s, uint8_t **data, int address) +{ + PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s); + + return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_IOC, 0x01, + "*l*lwwb*b*b*blww", + pcic->vendor_id, pcic->device_id, pcic->revision, + pcic->subsystem_vendor_id, + pcic->subsystem_id); +} + +static +size_t mptsas_config_ioc_1(MPTSASState *s, uint8_t **data, int address) +{ + return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_IOC, 0x03, + "*l*l*b*b*b*b"); +} + +static +size_t mptsas_config_ioc_2(MPTSASState *s, uint8_t **data, int address) +{ + return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_IOC, 0x04, + "*l*b*b*b*b"); +} + +static +size_t mptsas_config_ioc_3(MPTSASState *s, uint8_t **data, int address) +{ + return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_IOC, 0x00, + "*b*b*w"); +} + +static +size_t mptsas_config_ioc_4(MPTSASState *s, uint8_t **data, int address) +{ + return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_IOC, 0x00, + "*b*b*w"); +} + +static +size_t mptsas_config_ioc_5(MPTSASState *s, uint8_t **data, int address) +{ + return MPTSAS_CONFIG_PACK(5, MPI_CONFIG_PAGETYPE_IOC, 0x00, + "*l*b*b*w"); +} + +static +size_t mptsas_config_ioc_6(MPTSASState *s, uint8_t **data, int address) +{ + return MPTSAS_CONFIG_PACK(6, MPI_CONFIG_PAGETYPE_IOC, 0x01, + "*l*b*b*b*b*b*b*b*b*b*b*w*l*l*l*l*b*b*w" + "*w*w*w*w*l*l*l"); +} + +/* SAS I/O unit pages (extended) */ + +#define MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE 16 + +#define MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION 0x02 +#define MPI_SAS_IOUNIT0_RATE_1_5 0x08 +#define MPI_SAS_IOUNIT0_RATE_3_0 0x09 + +#define MPI_SAS_DEVICE_INFO_NO_DEVICE 0x00000000 +#define MPI_SAS_DEVICE_INFO_END_DEVICE 0x00000001 +#define MPI_SAS_DEVICE_INFO_SSP_TARGET 0x00000400 + +#define MPI_SAS_DEVICE0_ASTATUS_NO_ERRORS 0x00 + +#define MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT 0x0001 +#define MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED 0x0002 +#define MPI_SAS_DEVICE0_FLAGS_MAPPING_PERSISTENT 0x0004 + + + +static SCSIDevice *mptsas_phy_get_device(MPTSASState *s, int i, + int *phy_handle, int *dev_handle) +{ + SCSIDevice *d = scsi_device_find(&s->bus, 0, i, 0); + + if (phy_handle) { + *phy_handle = i + 1; + } + if (dev_handle) { + *dev_handle = d ? i + 1 + MPTSAS_NUM_PORTS : 0; + } + return d; +} + +static +size_t mptsas_config_sas_io_unit_0(MPTSASState *s, uint8_t **data, int address) +{ + size_t size = MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x04, + "*w*wb*b*w" + repl(MPTSAS_NUM_PORTS, "*s16"), + MPTSAS_NUM_PORTS); + + if (data) { + size_t ofs = size - MPTSAS_NUM_PORTS * MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE; + int i; + + for (i = 0; i < MPTSAS_NUM_PORTS; i++) { + int phy_handle, dev_handle; + SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); + + fill(*data + ofs, MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE, + "bbbblwwl", i, 0, 0, + (dev + ? MPI_SAS_IOUNIT0_RATE_3_0 + : MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION), + (dev + ? MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET + : MPI_SAS_DEVICE_INFO_NO_DEVICE), + dev_handle, + dev_handle, + 0); + ofs += MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE; + } + assert(ofs == size); + } + return size; +} + +#define MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE 12 + +static +size_t mptsas_config_sas_io_unit_1(MPTSASState *s, uint8_t **data, int address) +{ + size_t size = MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x07, + "*w*w*w*wb*b*b*b" + repl(MPTSAS_NUM_PORTS, "*s12"), + MPTSAS_NUM_PORTS); + + if (data) { + size_t ofs = size - MPTSAS_NUM_PORTS * MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE; + int i; + + for (i = 0; i < MPTSAS_NUM_PORTS; i++) { + SCSIDevice *dev = mptsas_phy_get_device(s, i, NULL, NULL); + fill(*data + ofs, MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE, + "bbbblww", i, 0, 0, + (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5, + (dev + ? MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET + : MPI_SAS_DEVICE_INFO_NO_DEVICE), + 0, 0); + ofs += MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE; + } + assert(ofs == size); + } + return size; +} + +static +size_t mptsas_config_sas_io_unit_2(MPTSASState *s, uint8_t **data, int address) +{ + return MPTSAS_CONFIG_PACK_EXT(2, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x06, + "*b*b*w*w*w*b*b*w"); +} + +static +size_t mptsas_config_sas_io_unit_3(MPTSASState *s, uint8_t **data, int address) +{ + return MPTSAS_CONFIG_PACK_EXT(3, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x06, + "*l*l*l*l*l*l*l*l*l"); +} + +/* SAS PHY pages (extended) */ + +static int mptsas_phy_addr_get(MPTSASState *s, int address) +{ + int i; + if ((address >> MPI_SAS_PHY_PGAD_FORM_SHIFT) == 0) { + i = address & 255; + } else if ((address >> MPI_SAS_PHY_PGAD_FORM_SHIFT) == 1) { + i = address & 65535; + } else { + return -EINVAL; + } + + if (i >= MPTSAS_NUM_PORTS) { + return -EINVAL; + } + + return i; +} + +static +size_t mptsas_config_phy_0(MPTSASState *s, uint8_t **data, int address) +{ + int phy_handle = -1; + int dev_handle = -1; + int i = mptsas_phy_addr_get(s, address); + SCSIDevice *dev; + + if (i < 0) { + trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 0); + return i; + } + + dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); + trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 0); + + return MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, 0x01, + "w*wqwb*blbb*b*b*l", + dev_handle, s->sas_addr, dev_handle, i, + (dev + ? MPI_SAS_DEVICE_INFO_END_DEVICE /* | MPI_SAS_DEVICE_INFO_SSP_TARGET?? */ + : MPI_SAS_DEVICE_INFO_NO_DEVICE), + (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5, + (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5); +} + +static +size_t mptsas_config_phy_1(MPTSASState *s, uint8_t **data, int address) +{ + int phy_handle = -1; + int dev_handle = -1; + int i = mptsas_phy_addr_get(s, address); + + if (i < 0) { + trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 1); + return i; + } + + (void) mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); + trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 1); + + return MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, 0x01, + "*l*l*l*l*l"); +} + +/* SAS device pages (extended) */ + +static int mptsas_device_addr_get(MPTSASState *s, int address) +{ + uint32_t handle, i; + uint32_t form = address >> MPI_SAS_PHY_PGAD_FORM_SHIFT; + if (form == MPI_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE) { + handle = address & MPI_SAS_DEVICE_PGAD_GNH_HANDLE_MASK; + do { + if (handle == 65535) { + handle = MPTSAS_NUM_PORTS + 1; + } else { + ++handle; + } + i = handle - 1 - MPTSAS_NUM_PORTS; + } while (i < MPTSAS_NUM_PORTS && !scsi_device_find(&s->bus, 0, i, 0)); + + } else if (form == MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID) { + if (address & MPI_SAS_DEVICE_PGAD_BT_BUS_MASK) { + return -EINVAL; + } + i = address & MPI_SAS_DEVICE_PGAD_BT_TID_MASK; + + } else if (form == MPI_SAS_DEVICE_PGAD_FORM_HANDLE) { + handle = address & MPI_SAS_DEVICE_PGAD_H_HANDLE_MASK; + i = handle - 1 - MPTSAS_NUM_PORTS; + + } else { + return -EINVAL; + } + + if (i >= MPTSAS_NUM_PORTS) { + return -EINVAL; + } + + return i; +} + +static +size_t mptsas_config_sas_device_0(MPTSASState *s, uint8_t **data, int address) +{ + int phy_handle = -1; + int dev_handle = -1; + int i = mptsas_device_addr_get(s, address); + SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); + + trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 0); + if (!dev) { + return -ENOENT; + } + + return MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x05, + "*w*wqwbbwbblwb*b", + dev->wwn, phy_handle, i, + MPI_SAS_DEVICE0_ASTATUS_NO_ERRORS, + dev_handle, i, 0, + MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET, + (MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT | + MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED | + MPI_SAS_DEVICE0_FLAGS_MAPPING_PERSISTENT), i); +} + +static +size_t mptsas_config_sas_device_1(MPTSASState *s, uint8_t **data, int address) +{ + int phy_handle = -1; + int dev_handle = -1; + int i = mptsas_device_addr_get(s, address); + SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); + + trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 1); + if (!dev) { + return -ENOENT; + } + + return MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x00, + "*lq*lwbb*s20", + dev->wwn, dev_handle, i, 0); +} + +static +size_t mptsas_config_sas_device_2(MPTSASState *s, uint8_t **data, int address) +{ + int phy_handle = -1; + int dev_handle = -1; + int i = mptsas_device_addr_get(s, address); + SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); + + trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 2); + if (!dev) { + return -ENOENT; + } + + return MPTSAS_CONFIG_PACK_EXT(2, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x01, + "ql", dev->wwn, 0); +} + +typedef struct MPTSASConfigPage { + uint8_t number; + uint8_t type; + size_t (*mpt_config_build)(MPTSASState *s, uint8_t **data, int address); +} MPTSASConfigPage; + +static const MPTSASConfigPage mptsas_config_pages[] = { + { + 0, MPI_CONFIG_PAGETYPE_MANUFACTURING, + mptsas_config_manufacturing_0, + }, { + 1, MPI_CONFIG_PAGETYPE_MANUFACTURING, + mptsas_config_manufacturing_1, + }, { + 2, MPI_CONFIG_PAGETYPE_MANUFACTURING, + mptsas_config_manufacturing_2, + }, { + 3, MPI_CONFIG_PAGETYPE_MANUFACTURING, + mptsas_config_manufacturing_3, + }, { + 4, MPI_CONFIG_PAGETYPE_MANUFACTURING, + mptsas_config_manufacturing_4, + }, { + 5, MPI_CONFIG_PAGETYPE_MANUFACTURING, + mptsas_config_manufacturing_5, + }, { + 6, MPI_CONFIG_PAGETYPE_MANUFACTURING, + mptsas_config_manufacturing_6, + }, { + 7, MPI_CONFIG_PAGETYPE_MANUFACTURING, + mptsas_config_manufacturing_7, + }, { + 8, MPI_CONFIG_PAGETYPE_MANUFACTURING, + mptsas_config_manufacturing_8, + }, { + 9, MPI_CONFIG_PAGETYPE_MANUFACTURING, + mptsas_config_manufacturing_9, + }, { + 10, MPI_CONFIG_PAGETYPE_MANUFACTURING, + mptsas_config_manufacturing_10, + }, { + 0, MPI_CONFIG_PAGETYPE_IO_UNIT, + mptsas_config_io_unit_0, + }, { + 1, MPI_CONFIG_PAGETYPE_IO_UNIT, + mptsas_config_io_unit_1, + }, { + 2, MPI_CONFIG_PAGETYPE_IO_UNIT, + mptsas_config_io_unit_2, + }, { + 3, MPI_CONFIG_PAGETYPE_IO_UNIT, + mptsas_config_io_unit_3, + }, { + 4, MPI_CONFIG_PAGETYPE_IO_UNIT, + mptsas_config_io_unit_4, + }, { + 0, MPI_CONFIG_PAGETYPE_IOC, + mptsas_config_ioc_0, + }, { + 1, MPI_CONFIG_PAGETYPE_IOC, + mptsas_config_ioc_1, + }, { + 2, MPI_CONFIG_PAGETYPE_IOC, + mptsas_config_ioc_2, + }, { + 3, MPI_CONFIG_PAGETYPE_IOC, + mptsas_config_ioc_3, + }, { + 4, MPI_CONFIG_PAGETYPE_IOC, + mptsas_config_ioc_4, + }, { + 5, MPI_CONFIG_PAGETYPE_IOC, + mptsas_config_ioc_5, + }, { + 6, MPI_CONFIG_PAGETYPE_IOC, + mptsas_config_ioc_6, + }, { + 0, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, + mptsas_config_sas_io_unit_0, + }, { + 1, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, + mptsas_config_sas_io_unit_1, + }, { + 2, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, + mptsas_config_sas_io_unit_2, + }, { + 3, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, + mptsas_config_sas_io_unit_3, + }, { + 0, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, + mptsas_config_phy_0, + }, { + 1, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, + mptsas_config_phy_1, + }, { + 0, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, + mptsas_config_sas_device_0, + }, { + 1, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, + mptsas_config_sas_device_1, + }, { + 2, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, + mptsas_config_sas_device_2, + } +}; + +static const MPTSASConfigPage *mptsas_find_config_page(int type, int number) +{ + const MPTSASConfigPage *page; + int i; + + for (i = 0; i < ARRAY_SIZE(mptsas_config_pages); i++) { + page = &mptsas_config_pages[i]; + if (page->type == type && page->number == number) { + return page; + } + } + + return NULL; +} + +void mptsas_process_config(MPTSASState *s, MPIMsgConfig *req) +{ + PCIDevice *pci = PCI_DEVICE(s); + + MPIMsgConfigReply reply; + const MPTSASConfigPage *page; + size_t length; + uint8_t type; + uint8_t *data = NULL; + uint32_t flags_and_length; + uint32_t dmalen; + uint64_t pa; + + mptsas_fix_config_endianness(req); + + QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req)); + QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply)); + + /* Copy common bits from the request into the reply. */ + memset(&reply, 0, sizeof(reply)); + reply.Action = req->Action; + reply.Function = req->Function; + reply.MsgContext = req->MsgContext; + reply.MsgLength = sizeof(reply) / 4; + reply.PageType = req->PageType; + reply.PageNumber = req->PageNumber; + reply.PageLength = req->PageLength; + reply.PageVersion = req->PageVersion; + + type = req->PageType & MPI_CONFIG_PAGETYPE_MASK; + if (type == MPI_CONFIG_PAGETYPE_EXTENDED) { + type = req->ExtPageType; + if (type <= MPI_CONFIG_PAGETYPE_MASK) { + reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_TYPE; + goto out; + } + + reply.ExtPageType = req->ExtPageType; + } + + page = mptsas_find_config_page(type, req->PageNumber); + + switch(req->Action) { + case MPI_CONFIG_ACTION_PAGE_DEFAULT: + case MPI_CONFIG_ACTION_PAGE_HEADER: + case MPI_CONFIG_ACTION_PAGE_READ_NVRAM: + case MPI_CONFIG_ACTION_PAGE_READ_CURRENT: + case MPI_CONFIG_ACTION_PAGE_READ_DEFAULT: + case MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT: + case MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM: + break; + + default: + reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_ACTION; + goto out; + } + + if (!page) { + page = mptsas_find_config_page(type, 1); + if (page) { + reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; + } else { + reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_TYPE; + } + goto out; + } + + if (req->Action == MPI_CONFIG_ACTION_PAGE_DEFAULT || + req->Action == MPI_CONFIG_ACTION_PAGE_HEADER) { + length = page->mpt_config_build(s, NULL, req->PageAddress); + if ((ssize_t)length < 0) { + reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; + goto out; + } else { + goto done; + } + } + + if (req->Action == MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT || + req->Action == MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM) { + length = page->mpt_config_build(s, NULL, req->PageAddress); + if ((ssize_t)length < 0) { + reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; + } else { + reply.IOCStatus = MPI_IOCSTATUS_CONFIG_CANT_COMMIT; + } + goto out; + } + + flags_and_length = req->PageBufferSGE.FlagsLength; + dmalen = flags_and_length & MPI_SGE_LENGTH_MASK; + if (dmalen == 0) { + length = page->mpt_config_build(s, NULL, req->PageAddress); + if ((ssize_t)length < 0) { + reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; + goto out; + } else { + goto done; + } + } + + if (flags_and_length & MPI_SGE_FLAGS_64_BIT_ADDRESSING) { + pa = req->PageBufferSGE.u.Address64; + } else { + pa = req->PageBufferSGE.u.Address32; + } + + /* Only read actions left. */ + length = page->mpt_config_build(s, &data, req->PageAddress); + if ((ssize_t)length < 0) { + reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; + goto out; + } else { + assert(data[2] == page->number); + pci_dma_write(pci, pa, data, MIN(length, dmalen)); + goto done; + } + + abort(); + +done: + if (type > MPI_CONFIG_PAGETYPE_MASK) { + reply.ExtPageLength = length / 4; + reply.ExtPageType = req->ExtPageType; + } else { + reply.PageLength = length / 4; + } + +out: + mptsas_fix_config_reply_endianness(&reply); + mptsas_reply(s, (MPIDefaultReply *)&reply); + g_free(data); +} diff --git a/qemu/hw/scsi/mptendian.c b/qemu/hw/scsi/mptendian.c new file mode 100644 index 000000000..b7fe2a2a3 --- /dev/null +++ b/qemu/hw/scsi/mptendian.c @@ -0,0 +1,204 @@ +/* + * QEMU LSI SAS1068 Host Bus Adapter emulation + * Endianness conversion for MPI data structures + * + * Copyright (c) 2016 Red Hat, Inc. + * + * Authors: Paolo Bonzini + * + * 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 . + */ + +#include "qemu/osdep.h" +#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" +#include "block/scsi.h" +#include "trace.h" + +#include "mptsas.h" +#include "mpi.h" + +static void mptsas_fix_sgentry_endianness(MPISGEntry *sge) +{ + le32_to_cpus(&sge->FlagsLength); + if (sge->FlagsLength & MPI_SGE_FLAGS_64_BIT_ADDRESSING) { + le64_to_cpus(&sge->u.Address64); + } else { + le32_to_cpus(&sge->u.Address32); + } +} + +static void mptsas_fix_sgentry_endianness_reply(MPISGEntry *sge) +{ + if (sge->FlagsLength & MPI_SGE_FLAGS_64_BIT_ADDRESSING) { + cpu_to_le64s(&sge->u.Address64); + } else { + cpu_to_le32s(&sge->u.Address32); + } + cpu_to_le32s(&sge->FlagsLength); +} + +void mptsas_fix_scsi_io_endianness(MPIMsgSCSIIORequest *req) +{ + le32_to_cpus(&req->MsgContext); + le32_to_cpus(&req->Control); + le32_to_cpus(&req->DataLength); + le32_to_cpus(&req->SenseBufferLowAddr); +} + +void mptsas_fix_scsi_io_reply_endianness(MPIMsgSCSIIOReply *reply) +{ + cpu_to_le32s(&reply->MsgContext); + cpu_to_le16s(&reply->IOCStatus); + cpu_to_le32s(&reply->IOCLogInfo); + cpu_to_le32s(&reply->TransferCount); + cpu_to_le32s(&reply->SenseCount); + cpu_to_le32s(&reply->ResponseInfo); + cpu_to_le16s(&reply->TaskTag); +} + +void mptsas_fix_scsi_task_mgmt_endianness(MPIMsgSCSITaskMgmt *req) +{ + le32_to_cpus(&req->MsgContext); + le32_to_cpus(&req->TaskMsgContext); +} + +void mptsas_fix_scsi_task_mgmt_reply_endianness(MPIMsgSCSITaskMgmtReply *reply) +{ + cpu_to_le32s(&reply->MsgContext); + cpu_to_le16s(&reply->IOCStatus); + cpu_to_le32s(&reply->IOCLogInfo); + cpu_to_le32s(&reply->TerminationCount); +} + +void mptsas_fix_ioc_init_endianness(MPIMsgIOCInit *req) +{ + le32_to_cpus(&req->MsgContext); + le16_to_cpus(&req->ReplyFrameSize); + le32_to_cpus(&req->HostMfaHighAddr); + le32_to_cpus(&req->SenseBufferHighAddr); + le32_to_cpus(&req->ReplyFifoHostSignalingAddr); + mptsas_fix_sgentry_endianness(&req->HostPageBufferSGE); + le16_to_cpus(&req->MsgVersion); + le16_to_cpus(&req->HeaderVersion); +} + +void mptsas_fix_ioc_init_reply_endianness(MPIMsgIOCInitReply *reply) +{ + cpu_to_le32s(&reply->MsgContext); + cpu_to_le16s(&reply->IOCStatus); + cpu_to_le32s(&reply->IOCLogInfo); +} + +void mptsas_fix_ioc_facts_endianness(MPIMsgIOCFacts *req) +{ + le32_to_cpus(&req->MsgContext); +} + +void mptsas_fix_ioc_facts_reply_endianness(MPIMsgIOCFactsReply *reply) +{ + cpu_to_le16s(&reply->MsgVersion); + cpu_to_le16s(&reply->HeaderVersion); + cpu_to_le32s(&reply->MsgContext); + cpu_to_le16s(&reply->IOCExceptions); + cpu_to_le16s(&reply->IOCStatus); + cpu_to_le32s(&reply->IOCLogInfo); + cpu_to_le16s(&reply->ReplyQueueDepth); + cpu_to_le16s(&reply->RequestFrameSize); + cpu_to_le16s(&reply->ProductID); + cpu_to_le32s(&reply->CurrentHostMfaHighAddr); + cpu_to_le16s(&reply->GlobalCredits); + cpu_to_le32s(&reply->CurrentSenseBufferHighAddr); + cpu_to_le16s(&reply->CurReplyFrameSize); + cpu_to_le32s(&reply->FWImageSize); + cpu_to_le32s(&reply->IOCCapabilities); + cpu_to_le16s(&reply->HighPriorityQueueDepth); + mptsas_fix_sgentry_endianness_reply(&reply->HostPageBufferSGE); + cpu_to_le32s(&reply->ReplyFifoHostSignalingAddr); +} + +void mptsas_fix_config_endianness(MPIMsgConfig *req) +{ + le16_to_cpus(&req->ExtPageLength); + le32_to_cpus(&req->MsgContext); + le32_to_cpus(&req->PageAddress); + mptsas_fix_sgentry_endianness(&req->PageBufferSGE); +} + +void mptsas_fix_config_reply_endianness(MPIMsgConfigReply *reply) +{ + cpu_to_le16s(&reply->ExtPageLength); + cpu_to_le32s(&reply->MsgContext); + cpu_to_le16s(&reply->IOCStatus); + cpu_to_le32s(&reply->IOCLogInfo); +} + +void mptsas_fix_port_facts_endianness(MPIMsgPortFacts *req) +{ + le32_to_cpus(&req->MsgContext); +} + +void mptsas_fix_port_facts_reply_endianness(MPIMsgPortFactsReply *reply) +{ + cpu_to_le32s(&reply->MsgContext); + cpu_to_le16s(&reply->IOCStatus); + cpu_to_le32s(&reply->IOCLogInfo); + cpu_to_le16s(&reply->MaxDevices); + cpu_to_le16s(&reply->PortSCSIID); + cpu_to_le16s(&reply->ProtocolFlags); + cpu_to_le16s(&reply->MaxPostedCmdBuffers); + cpu_to_le16s(&reply->MaxPersistentIDs); + cpu_to_le16s(&reply->MaxLanBuckets); +} + +void mptsas_fix_port_enable_endianness(MPIMsgPortEnable *req) +{ + le32_to_cpus(&req->MsgContext); +} + +void mptsas_fix_port_enable_reply_endianness(MPIMsgPortEnableReply *reply) +{ + cpu_to_le32s(&reply->MsgContext); + cpu_to_le16s(&reply->IOCStatus); + cpu_to_le32s(&reply->IOCLogInfo); +} + +void mptsas_fix_event_notification_endianness(MPIMsgEventNotify *req) +{ + le32_to_cpus(&req->MsgContext); +} + +void mptsas_fix_event_notification_reply_endianness(MPIMsgEventNotifyReply *reply) +{ + int length = reply->EventDataLength; + int i; + + cpu_to_le16s(&reply->EventDataLength); + cpu_to_le32s(&reply->MsgContext); + cpu_to_le16s(&reply->IOCStatus); + cpu_to_le32s(&reply->IOCLogInfo); + cpu_to_le32s(&reply->Event); + cpu_to_le32s(&reply->EventContext); + + /* Really depends on the event kind. This will do for now. */ + for (i = 0; i < length; i++) { + cpu_to_le32s(&reply->Data[i]); + } +} + diff --git a/qemu/hw/scsi/mptsas.c b/qemu/hw/scsi/mptsas.c new file mode 100644 index 000000000..499c1465a --- /dev/null +++ b/qemu/hw/scsi/mptsas.c @@ -0,0 +1,1442 @@ +/* + * QEMU LSI SAS1068 Host Bus Adapter emulation + * Based on the QEMU Megaraid emulator + * + * Copyright (c) 2009-2012 Hannes Reinecke, SUSE Labs + * Copyright (c) 2012 Verizon, Inc. + * Copyright (c) 2016 Red Hat, Inc. + * + * Authors: Don Slutz, Paolo Bonzini + * + * 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 . + */ + +#include "qemu/osdep.h" +#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" +#include "block/scsi.h" +#include "trace.h" + +#include "mptsas.h" +#include "mpi.h" + +#define NAA_LOCALLY_ASSIGNED_ID 0x3ULL +#define IEEE_COMPANY_LOCALLY_ASSIGNED 0x525400 + +#define TYPE_MPTSAS1068 "mptsas1068" + +#define MPT_SAS(obj) \ + OBJECT_CHECK(MPTSASState, (obj), TYPE_MPTSAS1068) + +#define MPTSAS1068_PRODUCT_ID \ + (MPI_FW_HEADER_PID_FAMILY_1068_SAS | \ + MPI_FW_HEADER_PID_PROD_INITIATOR_SCSI | \ + MPI_FW_HEADER_PID_TYPE_SAS) + +struct MPTSASRequest { + MPIMsgSCSIIORequest scsi_io; + SCSIRequest *sreq; + QEMUSGList qsg; + MPTSASState *dev; + + QTAILQ_ENTRY(MPTSASRequest) next; +}; + +static void mptsas_update_interrupt(MPTSASState *s) +{ + PCIDevice *pci = (PCIDevice *) s; + uint32_t state = s->intr_status & ~(s->intr_mask | MPI_HIS_IOP_DOORBELL_STATUS); + + if (s->msi_in_use && msi_enabled(pci)) { + if (state) { + trace_mptsas_irq_msi(s); + msi_notify(pci, 0); + } + } + + trace_mptsas_irq_intx(s, !!state); + pci_set_irq(pci, !!state); +} + +static void mptsas_set_fault(MPTSASState *s, uint32_t code) +{ + if ((s->state & MPI_IOC_STATE_FAULT) == 0) { + s->state = MPI_IOC_STATE_FAULT | code; + } +} + +#define MPTSAS_FIFO_INVALID(s, name) \ + ((s)->name##_head > ARRAY_SIZE((s)->name) || \ + (s)->name##_tail > ARRAY_SIZE((s)->name)) + +#define MPTSAS_FIFO_EMPTY(s, name) \ + ((s)->name##_head == (s)->name##_tail) + +#define MPTSAS_FIFO_FULL(s, name) \ + ((s)->name##_head == ((s)->name##_tail + 1) % ARRAY_SIZE((s)->name)) + +#define MPTSAS_FIFO_GET(s, name) ({ \ + uint32_t _val = (s)->name[(s)->name##_head++]; \ + (s)->name##_head %= ARRAY_SIZE((s)->name); \ + _val; \ +}) + +#define MPTSAS_FIFO_PUT(s, name, val) do { \ + (s)->name[(s)->name##_tail++] = (val); \ + (s)->name##_tail %= ARRAY_SIZE((s)->name); \ +} while(0) + +static void mptsas_post_reply(MPTSASState *s, MPIDefaultReply *reply) +{ + PCIDevice *pci = (PCIDevice *) s; + uint32_t addr_lo; + + if (MPTSAS_FIFO_EMPTY(s, reply_free) || MPTSAS_FIFO_FULL(s, reply_post)) { + mptsas_set_fault(s, MPI_IOCSTATUS_INSUFFICIENT_RESOURCES); + return; + } + + addr_lo = MPTSAS_FIFO_GET(s, reply_free); + + pci_dma_write(pci, addr_lo | s->host_mfa_high_addr, reply, + MIN(s->reply_frame_size, 4 * reply->MsgLength)); + + MPTSAS_FIFO_PUT(s, reply_post, MPI_ADDRESS_REPLY_A_BIT | (addr_lo >> 1)); + + s->intr_status |= MPI_HIS_REPLY_MESSAGE_INTERRUPT; + if (s->doorbell_state == DOORBELL_WRITE) { + s->doorbell_state = DOORBELL_NONE; + s->intr_status |= MPI_HIS_DOORBELL_INTERRUPT; + } + mptsas_update_interrupt(s); +} + +void mptsas_reply(MPTSASState *s, MPIDefaultReply *reply) +{ + if (s->doorbell_state == DOORBELL_WRITE) { + /* The reply is sent out in 16 bit chunks, while the size + * in the reply is in 32 bit units. + */ + s->doorbell_state = DOORBELL_READ; + s->doorbell_reply_idx = 0; + s->doorbell_reply_size = reply->MsgLength * 2; + memcpy(s->doorbell_reply, reply, s->doorbell_reply_size * 2); + s->intr_status |= MPI_HIS_DOORBELL_INTERRUPT; + mptsas_update_interrupt(s); + } else { + mptsas_post_reply(s, reply); + } +} + +static void mptsas_turbo_reply(MPTSASState *s, uint32_t msgctx) +{ + if (MPTSAS_FIFO_FULL(s, reply_post)) { + mptsas_set_fault(s, MPI_IOCSTATUS_INSUFFICIENT_RESOURCES); + return; + } + + /* The reply is just the message context ID (bit 31 = clear). */ + MPTSAS_FIFO_PUT(s, reply_post, msgctx); + + s->intr_status |= MPI_HIS_REPLY_MESSAGE_INTERRUPT; + mptsas_update_interrupt(s); +} + +#define MPTSAS_MAX_REQUEST_SIZE 52 + +static const int mpi_request_sizes[] = { + [MPI_FUNCTION_SCSI_IO_REQUEST] = sizeof(MPIMsgSCSIIORequest), + [MPI_FUNCTION_SCSI_TASK_MGMT] = sizeof(MPIMsgSCSITaskMgmt), + [MPI_FUNCTION_IOC_INIT] = sizeof(MPIMsgIOCInit), + [MPI_FUNCTION_IOC_FACTS] = sizeof(MPIMsgIOCFacts), + [MPI_FUNCTION_CONFIG] = sizeof(MPIMsgConfig), + [MPI_FUNCTION_PORT_FACTS] = sizeof(MPIMsgPortFacts), + [MPI_FUNCTION_PORT_ENABLE] = sizeof(MPIMsgPortEnable), + [MPI_FUNCTION_EVENT_NOTIFICATION] = sizeof(MPIMsgEventNotify), +}; + +static dma_addr_t mptsas_ld_sg_base(MPTSASState *s, uint32_t flags_and_length, + dma_addr_t *sgaddr) +{ + PCIDevice *pci = (PCIDevice *) s; + dma_addr_t addr; + + if (flags_and_length & MPI_SGE_FLAGS_64_BIT_ADDRESSING) { + addr = ldq_le_pci_dma(pci, *sgaddr + 4); + *sgaddr += 12; + } else { + addr = ldl_le_pci_dma(pci, *sgaddr + 4); + *sgaddr += 8; + } + return addr; +} + +static int mptsas_build_sgl(MPTSASState *s, MPTSASRequest *req, hwaddr addr) +{ + PCIDevice *pci = (PCIDevice *) s; + hwaddr next_chain_addr; + uint32_t left; + hwaddr sgaddr; + uint32_t chain_offset; + + chain_offset = req->scsi_io.ChainOffset; + next_chain_addr = addr + chain_offset * sizeof(uint32_t); + sgaddr = addr + sizeof(MPIMsgSCSIIORequest); + pci_dma_sglist_init(&req->qsg, pci, 4); + left = req->scsi_io.DataLength; + + for(;;) { + dma_addr_t addr, len; + uint32_t flags_and_length; + + flags_and_length = ldl_le_pci_dma(pci, sgaddr); + len = flags_and_length & MPI_SGE_LENGTH_MASK; + if ((flags_and_length & MPI_SGE_FLAGS_ELEMENT_TYPE_MASK) + != MPI_SGE_FLAGS_SIMPLE_ELEMENT || + (!len && + !(flags_and_length & MPI_SGE_FLAGS_END_OF_LIST) && + !(flags_and_length & MPI_SGE_FLAGS_END_OF_BUFFER))) { + return MPI_IOCSTATUS_INVALID_SGL; + } + + len = MIN(len, left); + if (!len) { + /* We reached the desired transfer length, ignore extra + * elements of the s/g list. + */ + break; + } + + addr = mptsas_ld_sg_base(s, flags_and_length, &sgaddr); + qemu_sglist_add(&req->qsg, addr, len); + left -= len; + + if (flags_and_length & MPI_SGE_FLAGS_END_OF_LIST) { + break; + } + + if (flags_and_length & MPI_SGE_FLAGS_LAST_ELEMENT) { + if (!chain_offset) { + break; + } + + flags_and_length = ldl_le_pci_dma(pci, next_chain_addr); + if ((flags_and_length & MPI_SGE_FLAGS_ELEMENT_TYPE_MASK) + != MPI_SGE_FLAGS_CHAIN_ELEMENT) { + return MPI_IOCSTATUS_INVALID_SGL; + } + + sgaddr = mptsas_ld_sg_base(s, flags_and_length, &next_chain_addr); + chain_offset = + (flags_and_length & MPI_SGE_CHAIN_OFFSET_MASK) >> MPI_SGE_CHAIN_OFFSET_SHIFT; + next_chain_addr = sgaddr + chain_offset * sizeof(uint32_t); + } + } + return 0; +} + +static void mptsas_free_request(MPTSASRequest *req) +{ + MPTSASState *s = req->dev; + + if (req->sreq != NULL) { + req->sreq->hba_private = NULL; + scsi_req_unref(req->sreq); + req->sreq = NULL; + QTAILQ_REMOVE(&s->pending, req, next); + } + qemu_sglist_destroy(&req->qsg); + g_free(req); +} + +static int mptsas_scsi_device_find(MPTSASState *s, int bus, int target, + uint8_t *lun, SCSIDevice **sdev) +{ + if (bus != 0) { + return MPI_IOCSTATUS_SCSI_INVALID_BUS; + } + + if (target >= s->max_devices) { + return MPI_IOCSTATUS_SCSI_INVALID_TARGETID; + } + + *sdev = scsi_device_find(&s->bus, bus, target, lun[1]); + if (!*sdev) { + return MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE; + } + + return 0; +} + +static int mptsas_process_scsi_io_request(MPTSASState *s, + MPIMsgSCSIIORequest *scsi_io, + hwaddr addr) +{ + MPTSASRequest *req; + MPIMsgSCSIIOReply reply; + SCSIDevice *sdev; + int status; + + mptsas_fix_scsi_io_endianness(scsi_io); + + trace_mptsas_process_scsi_io_request(s, scsi_io->Bus, scsi_io->TargetID, + scsi_io->LUN[1], scsi_io->DataLength); + + status = mptsas_scsi_device_find(s, scsi_io->Bus, scsi_io->TargetID, + scsi_io->LUN, &sdev); + if (status) { + goto bad; + } + + req = g_new(MPTSASRequest, 1); + QTAILQ_INSERT_TAIL(&s->pending, req, next); + req->scsi_io = *scsi_io; + req->dev = s; + + status = mptsas_build_sgl(s, req, addr); + if (status) { + goto free_bad; + } + + if (req->qsg.size < scsi_io->DataLength) { + trace_mptsas_sgl_overflow(s, scsi_io->MsgContext, scsi_io->DataLength, + req->qsg.size); + status = MPI_IOCSTATUS_INVALID_SGL; + goto free_bad; + } + + req->sreq = scsi_req_new(sdev, scsi_io->MsgContext, + scsi_io->LUN[1], scsi_io->CDB, req); + + if (req->sreq->cmd.xfer > scsi_io->DataLength) { + goto overrun; + } + switch (scsi_io->Control & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK) { + case MPI_SCSIIO_CONTROL_NODATATRANSFER: + if (req->sreq->cmd.mode != SCSI_XFER_NONE) { + goto overrun; + } + break; + + case MPI_SCSIIO_CONTROL_WRITE: + if (req->sreq->cmd.mode != SCSI_XFER_TO_DEV) { + goto overrun; + } + break; + + case MPI_SCSIIO_CONTROL_READ: + if (req->sreq->cmd.mode != SCSI_XFER_FROM_DEV) { + goto overrun; + } + break; + } + + if (scsi_req_enqueue(req->sreq)) { + scsi_req_continue(req->sreq); + } + return 0; + +overrun: + trace_mptsas_scsi_overflow(s, scsi_io->MsgContext, req->sreq->cmd.xfer, + scsi_io->DataLength); + status = MPI_IOCSTATUS_SCSI_DATA_OVERRUN; +free_bad: + mptsas_free_request(req); +bad: + memset(&reply, 0, sizeof(reply)); + reply.TargetID = scsi_io->TargetID; + reply.Bus = scsi_io->Bus; + reply.MsgLength = sizeof(reply) / 4; + reply.Function = scsi_io->Function; + reply.CDBLength = scsi_io->CDBLength; + reply.SenseBufferLength = scsi_io->SenseBufferLength; + reply.MsgContext = scsi_io->MsgContext; + reply.SCSIState = MPI_SCSI_STATE_NO_SCSI_STATUS; + reply.IOCStatus = status; + + mptsas_fix_scsi_io_reply_endianness(&reply); + mptsas_reply(s, (MPIDefaultReply *)&reply); + + return 0; +} + +typedef struct { + Notifier notifier; + MPTSASState *s; + MPIMsgSCSITaskMgmtReply *reply; +} MPTSASCancelNotifier; + +static void mptsas_cancel_notify(Notifier *notifier, void *data) +{ + MPTSASCancelNotifier *n = container_of(notifier, + MPTSASCancelNotifier, + notifier); + + /* Abusing IOCLogInfo to store the expected number of requests... */ + if (++n->reply->TerminationCount == n->reply->IOCLogInfo) { + n->reply->IOCLogInfo = 0; + mptsas_fix_scsi_task_mgmt_reply_endianness(n->reply); + mptsas_post_reply(n->s, (MPIDefaultReply *)n->reply); + g_free(n->reply); + } + g_free(n); +} + +static void mptsas_process_scsi_task_mgmt(MPTSASState *s, MPIMsgSCSITaskMgmt *req) +{ + MPIMsgSCSITaskMgmtReply reply; + MPIMsgSCSITaskMgmtReply *reply_async; + int status, count; + SCSIDevice *sdev; + SCSIRequest *r, *next; + BusChild *kid; + + mptsas_fix_scsi_task_mgmt_endianness(req); + + QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req)); + QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req)); + QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply)); + + memset(&reply, 0, sizeof(reply)); + reply.TargetID = req->TargetID; + reply.Bus = req->Bus; + reply.MsgLength = sizeof(reply) / 4; + reply.Function = req->Function; + reply.TaskType = req->TaskType; + reply.MsgContext = req->MsgContext; + + switch (req->TaskType) { + case MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK: + case MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK: + status = mptsas_scsi_device_find(s, req->Bus, req->TargetID, + req->LUN, &sdev); + if (status) { + reply.IOCStatus = status; + goto out; + } + if (sdev->lun != req->LUN[1]) { + reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN; + goto out; + } + + QTAILQ_FOREACH_SAFE(r, &sdev->requests, next, next) { + MPTSASRequest *cmd_req = r->hba_private; + if (cmd_req && cmd_req->scsi_io.MsgContext == req->TaskMsgContext) { + break; + } + } + if (r) { + /* + * Assert that the request has not been completed yet, we + * check for it in the loop above. + */ + assert(r->hba_private); + if (req->TaskType == MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK) { + /* "If the specified command is present in the task set, then + * return a service response set to FUNCTION SUCCEEDED". + */ + reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_SUCCEEDED; + } else { + MPTSASCancelNotifier *notifier; + + reply_async = g_memdup(&reply, sizeof(MPIMsgSCSITaskMgmtReply)); + reply_async->IOCLogInfo = INT_MAX; + + count = 1; + notifier = g_new(MPTSASCancelNotifier, 1); + notifier->s = s; + notifier->reply = reply_async; + notifier->notifier.notify = mptsas_cancel_notify; + scsi_req_cancel_async(r, ¬ifier->notifier); + goto reply_maybe_async; + } + } + break; + + case MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET: + case MPI_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET: + status = mptsas_scsi_device_find(s, req->Bus, req->TargetID, + req->LUN, &sdev); + if (status) { + reply.IOCStatus = status; + goto out; + } + if (sdev->lun != req->LUN[1]) { + reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN; + goto out; + } + + reply_async = g_memdup(&reply, sizeof(MPIMsgSCSITaskMgmtReply)); + reply_async->IOCLogInfo = INT_MAX; + + count = 0; + QTAILQ_FOREACH_SAFE(r, &sdev->requests, next, next) { + if (r->hba_private) { + MPTSASCancelNotifier *notifier; + + count++; + notifier = g_new(MPTSASCancelNotifier, 1); + notifier->s = s; + notifier->reply = reply_async; + notifier->notifier.notify = mptsas_cancel_notify; + scsi_req_cancel_async(r, ¬ifier->notifier); + } + } + +reply_maybe_async: + if (reply_async->TerminationCount < count) { + reply_async->IOCLogInfo = count; + return; + } + g_free(reply_async); + reply.TerminationCount = count; + break; + + case MPI_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET: + status = mptsas_scsi_device_find(s, req->Bus, req->TargetID, + req->LUN, &sdev); + if (status) { + reply.IOCStatus = status; + goto out; + } + if (sdev->lun != req->LUN[1]) { + reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN; + goto out; + } + qdev_reset_all(&sdev->qdev); + break; + + case MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET: + if (req->Bus != 0) { + reply.IOCStatus = MPI_IOCSTATUS_SCSI_INVALID_BUS; + goto out; + } + if (req->TargetID > s->max_devices) { + reply.IOCStatus = MPI_IOCSTATUS_SCSI_INVALID_TARGETID; + goto out; + } + + QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { + sdev = SCSI_DEVICE(kid->child); + if (sdev->channel == 0 && sdev->id == req->TargetID) { + qdev_reset_all(kid->child); + } + } + break; + + case MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS: + qbus_reset_all(&s->bus.qbus); + break; + + default: + reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED; + break; + } + +out: + mptsas_fix_scsi_task_mgmt_reply_endianness(&reply); + mptsas_post_reply(s, (MPIDefaultReply *)&reply); +} + +static void mptsas_process_ioc_init(MPTSASState *s, MPIMsgIOCInit *req) +{ + MPIMsgIOCInitReply reply; + + mptsas_fix_ioc_init_endianness(req); + + QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req)); + QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req)); + QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply)); + + s->who_init = req->WhoInit; + s->reply_frame_size = req->ReplyFrameSize; + s->max_buses = req->MaxBuses; + s->max_devices = req->MaxDevices ? req->MaxDevices : 256; + s->host_mfa_high_addr = (hwaddr)req->HostMfaHighAddr << 32; + s->sense_buffer_high_addr = (hwaddr)req->SenseBufferHighAddr << 32; + + if (s->state == MPI_IOC_STATE_READY) { + s->state = MPI_IOC_STATE_OPERATIONAL; + } + + memset(&reply, 0, sizeof(reply)); + reply.WhoInit = s->who_init; + reply.MsgLength = sizeof(reply) / 4; + reply.Function = req->Function; + reply.MaxDevices = s->max_devices; + reply.MaxBuses = s->max_buses; + reply.MsgContext = req->MsgContext; + + mptsas_fix_ioc_init_reply_endianness(&reply); + mptsas_reply(s, (MPIDefaultReply *)&reply); +} + +static void mptsas_process_ioc_facts(MPTSASState *s, + MPIMsgIOCFacts *req) +{ + MPIMsgIOCFactsReply reply; + + mptsas_fix_ioc_facts_endianness(req); + + QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req)); + QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req)); + QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply)); + + memset(&reply, 0, sizeof(reply)); + reply.MsgVersion = 0x0105; + reply.MsgLength = sizeof(reply) / 4; + reply.Function = req->Function; + reply.MsgContext = req->MsgContext; + reply.MaxChainDepth = MPTSAS_MAXIMUM_CHAIN_DEPTH; + reply.WhoInit = s->who_init; + reply.BlockSize = MPTSAS_MAX_REQUEST_SIZE / sizeof(uint32_t); + reply.ReplyQueueDepth = ARRAY_SIZE(s->reply_post) - 1; + QEMU_BUILD_BUG_ON(ARRAY_SIZE(s->reply_post) != ARRAY_SIZE(s->reply_free)); + + reply.RequestFrameSize = 128; + reply.ProductID = MPTSAS1068_PRODUCT_ID; + reply.CurrentHostMfaHighAddr = s->host_mfa_high_addr >> 32; + reply.GlobalCredits = ARRAY_SIZE(s->request_post) - 1; + reply.NumberOfPorts = MPTSAS_NUM_PORTS; + reply.CurrentSenseBufferHighAddr = s->sense_buffer_high_addr >> 32; + reply.CurReplyFrameSize = s->reply_frame_size; + reply.MaxDevices = s->max_devices; + reply.MaxBuses = s->max_buses; + reply.FWVersionDev = 0; + reply.FWVersionUnit = 0x92; + reply.FWVersionMinor = 0x32; + reply.FWVersionMajor = 0x1; + + mptsas_fix_ioc_facts_reply_endianness(&reply); + mptsas_reply(s, (MPIDefaultReply *)&reply); +} + +static void mptsas_process_port_facts(MPTSASState *s, + MPIMsgPortFacts *req) +{ + MPIMsgPortFactsReply reply; + + mptsas_fix_port_facts_endianness(req); + + QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req)); + QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req)); + QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply)); + + memset(&reply, 0, sizeof(reply)); + reply.MsgLength = sizeof(reply) / 4; + reply.Function = req->Function; + reply.PortNumber = req->PortNumber; + reply.MsgContext = req->MsgContext; + + if (req->PortNumber < MPTSAS_NUM_PORTS) { + reply.PortType = MPI_PORTFACTS_PORTTYPE_SAS; + reply.MaxDevices = MPTSAS_NUM_PORTS; + reply.PortSCSIID = MPTSAS_NUM_PORTS; + reply.ProtocolFlags = MPI_PORTFACTS_PROTOCOL_LOGBUSADDR | MPI_PORTFACTS_PROTOCOL_INITIATOR; + } + + mptsas_fix_port_facts_reply_endianness(&reply); + mptsas_reply(s, (MPIDefaultReply *)&reply); +} + +static void mptsas_process_port_enable(MPTSASState *s, + MPIMsgPortEnable *req) +{ + MPIMsgPortEnableReply reply; + + mptsas_fix_port_enable_endianness(req); + + QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req)); + QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req)); + QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply)); + + memset(&reply, 0, sizeof(reply)); + reply.MsgLength = sizeof(reply) / 4; + reply.PortNumber = req->PortNumber; + reply.Function = req->Function; + reply.MsgContext = req->MsgContext; + + mptsas_fix_port_enable_reply_endianness(&reply); + mptsas_reply(s, (MPIDefaultReply *)&reply); +} + +static void mptsas_process_event_notification(MPTSASState *s, + MPIMsgEventNotify *req) +{ + MPIMsgEventNotifyReply reply; + + mptsas_fix_event_notification_endianness(req); + + QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req)); + QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req)); + QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply)); + + /* Don't even bother storing whether event notification is enabled, + * since it is not accessible. + */ + + memset(&reply, 0, sizeof(reply)); + reply.EventDataLength = sizeof(reply.Data) / 4; + reply.MsgLength = sizeof(reply) / 4; + reply.Function = req->Function; + + /* This is set because events are sent through the reply FIFOs. */ + reply.MsgFlags = MPI_MSGFLAGS_CONTINUATION_REPLY; + + reply.MsgContext = req->MsgContext; + reply.Event = MPI_EVENT_EVENT_CHANGE; + reply.Data[0] = !!req->Switch; + + mptsas_fix_event_notification_reply_endianness(&reply); + mptsas_reply(s, (MPIDefaultReply *)&reply); +} + +static void mptsas_process_message(MPTSASState *s, MPIRequestHeader *req) +{ + trace_mptsas_process_message(s, req->Function, req->MsgContext); + switch (req->Function) { + case MPI_FUNCTION_SCSI_TASK_MGMT: + mptsas_process_scsi_task_mgmt(s, (MPIMsgSCSITaskMgmt *)req); + break; + + case MPI_FUNCTION_IOC_INIT: + mptsas_process_ioc_init(s, (MPIMsgIOCInit *)req); + break; + + case MPI_FUNCTION_IOC_FACTS: + mptsas_process_ioc_facts(s, (MPIMsgIOCFacts *)req); + break; + + case MPI_FUNCTION_PORT_FACTS: + mptsas_process_port_facts(s, (MPIMsgPortFacts *)req); + break; + + case MPI_FUNCTION_PORT_ENABLE: + mptsas_process_port_enable(s, (MPIMsgPortEnable *)req); + break; + + case MPI_FUNCTION_EVENT_NOTIFICATION: + mptsas_process_event_notification(s, (MPIMsgEventNotify *)req); + break; + + case MPI_FUNCTION_CONFIG: + mptsas_process_config(s, (MPIMsgConfig *)req); + break; + + default: + trace_mptsas_unhandled_cmd(s, req->Function, 0); + mptsas_set_fault(s, MPI_IOCSTATUS_INVALID_FUNCTION); + break; + } +} + +static void mptsas_fetch_request(MPTSASState *s) +{ + PCIDevice *pci = (PCIDevice *) s; + char req[MPTSAS_MAX_REQUEST_SIZE]; + MPIRequestHeader *hdr = (MPIRequestHeader *)req; + hwaddr addr; + int size; + + if (s->state != MPI_IOC_STATE_OPERATIONAL) { + mptsas_set_fault(s, MPI_IOCSTATUS_INVALID_STATE); + return; + } + + /* Read the message header from the guest first. */ + addr = s->host_mfa_high_addr | MPTSAS_FIFO_GET(s, request_post); + pci_dma_read(pci, addr, req, sizeof(hdr)); + + if (hdr->Function < ARRAY_SIZE(mpi_request_sizes) && + mpi_request_sizes[hdr->Function]) { + /* Read the rest of the request based on the type. Do not + * reread everything, as that could cause a TOC/TOU mismatch + * and leak data from the QEMU stack. + */ + size = mpi_request_sizes[hdr->Function]; + assert(size <= MPTSAS_MAX_REQUEST_SIZE); + pci_dma_read(pci, addr + sizeof(hdr), &req[sizeof(hdr)], + size - sizeof(hdr)); + } + + if (hdr->Function == MPI_FUNCTION_SCSI_IO_REQUEST) { + /* SCSI I/O requests are separate from mptsas_process_message + * because they cannot be sent through the doorbell yet. + */ + mptsas_process_scsi_io_request(s, (MPIMsgSCSIIORequest *)req, addr); + } else { + mptsas_process_message(s, (MPIRequestHeader *)req); + } +} + +static void mptsas_fetch_requests(void *opaque) +{ + MPTSASState *s = opaque; + + while (!MPTSAS_FIFO_EMPTY(s, request_post)) { + mptsas_fetch_request(s); + } +} + +static void mptsas_soft_reset(MPTSASState *s) +{ + uint32_t save_mask; + + trace_mptsas_reset(s); + + /* Temporarily disable interrupts */ + save_mask = s->intr_mask; + s->intr_mask = MPI_HIM_DIM | MPI_HIM_RIM; + mptsas_update_interrupt(s); + + qbus_reset_all(&s->bus.qbus); + s->intr_status = 0; + s->intr_mask = save_mask; + + s->reply_free_tail = 0; + s->reply_free_head = 0; + s->reply_post_tail = 0; + s->reply_post_head = 0; + s->request_post_tail = 0; + s->request_post_head = 0; + qemu_bh_cancel(s->request_bh); + + s->state = MPI_IOC_STATE_READY; +} + +static uint32_t mptsas_doorbell_read(MPTSASState *s) +{ + uint32_t ret; + + ret = (s->who_init << MPI_DOORBELL_WHO_INIT_SHIFT) & MPI_DOORBELL_WHO_INIT_MASK; + ret |= s->state; + switch (s->doorbell_state) { + case DOORBELL_NONE: + break; + + case DOORBELL_WRITE: + ret |= MPI_DOORBELL_ACTIVE; + break; + + case DOORBELL_READ: + /* Get rid of the IOC fault code. */ + ret &= ~MPI_DOORBELL_DATA_MASK; + + assert(s->intr_status & MPI_HIS_DOORBELL_INTERRUPT); + assert(s->doorbell_reply_idx <= s->doorbell_reply_size); + + ret |= MPI_DOORBELL_ACTIVE; + if (s->doorbell_reply_idx < s->doorbell_reply_size) { + /* For more information about this endian switch, see the + * commit message for commit 36b62ae ("fw_cfg: fix endianness in + * fw_cfg_data_mem_read() / _write()", 2015-01-16). + */ + ret |= le16_to_cpu(s->doorbell_reply[s->doorbell_reply_idx++]); + } + break; + + default: + abort(); + } + + return ret; +} + +static void mptsas_doorbell_write(MPTSASState *s, uint32_t val) +{ + if (s->doorbell_state == DOORBELL_WRITE) { + if (s->doorbell_idx < s->doorbell_cnt) { + /* For more information about this endian switch, see the + * commit message for commit 36b62ae ("fw_cfg: fix endianness in + * fw_cfg_data_mem_read() / _write()", 2015-01-16). + */ + s->doorbell_msg[s->doorbell_idx++] = cpu_to_le32(val); + if (s->doorbell_idx == s->doorbell_cnt) { + mptsas_process_message(s, (MPIRequestHeader *)s->doorbell_msg); + } + } + return; + } + + switch ((val & MPI_DOORBELL_FUNCTION_MASK) >> MPI_DOORBELL_FUNCTION_SHIFT) { + case MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET: + mptsas_soft_reset(s); + break; + case MPI_FUNCTION_IO_UNIT_RESET: + break; + case MPI_FUNCTION_HANDSHAKE: + s->doorbell_state = DOORBELL_WRITE; + s->doorbell_idx = 0; + s->doorbell_cnt = (val & MPI_DOORBELL_ADD_DWORDS_MASK) + >> MPI_DOORBELL_ADD_DWORDS_SHIFT; + s->intr_status |= MPI_HIS_DOORBELL_INTERRUPT; + mptsas_update_interrupt(s); + break; + default: + trace_mptsas_unhandled_doorbell_cmd(s, val); + break; + } +} + +static void mptsas_write_sequence_write(MPTSASState *s, uint32_t val) +{ + /* If the diagnostic register is enabled, any write to this register + * will disable it. Otherwise, the guest has to do a magic five-write + * sequence. + */ + if (s->diagnostic & MPI_DIAG_DRWE) { + goto disable; + } + + switch (s->diagnostic_idx) { + case 0: + if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_1ST_KEY_VALUE) { + goto disable; + } + break; + case 1: + if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_2ND_KEY_VALUE) { + goto disable; + } + break; + case 2: + if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_3RD_KEY_VALUE) { + goto disable; + } + break; + case 3: + if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_4TH_KEY_VALUE) { + goto disable; + } + break; + case 4: + if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_5TH_KEY_VALUE) { + goto disable; + } + /* Prepare Spaceball One for departure, and change the + * combination on my luggage! + */ + s->diagnostic |= MPI_DIAG_DRWE; + break; + } + s->diagnostic_idx++; + return; + +disable: + s->diagnostic &= ~MPI_DIAG_DRWE; + s->diagnostic_idx = 0; +} + +static int mptsas_hard_reset(MPTSASState *s) +{ + mptsas_soft_reset(s); + + s->intr_mask = MPI_HIM_DIM | MPI_HIM_RIM; + + s->host_mfa_high_addr = 0; + s->sense_buffer_high_addr = 0; + s->reply_frame_size = 0; + s->max_devices = MPTSAS_NUM_PORTS; + s->max_buses = 1; + + return 0; +} + +static void mptsas_interrupt_status_write(MPTSASState *s) +{ + switch (s->doorbell_state) { + case DOORBELL_NONE: + case DOORBELL_WRITE: + s->intr_status &= ~MPI_HIS_DOORBELL_INTERRUPT; + break; + + case DOORBELL_READ: + /* The reply can be read continuously, so leave the interrupt up. */ + assert(s->intr_status & MPI_HIS_DOORBELL_INTERRUPT); + if (s->doorbell_reply_idx == s->doorbell_reply_size) { + s->doorbell_state = DOORBELL_NONE; + } + break; + + default: + abort(); + } + mptsas_update_interrupt(s); +} + +static uint32_t mptsas_reply_post_read(MPTSASState *s) +{ + uint32_t ret; + + if (!MPTSAS_FIFO_EMPTY(s, reply_post)) { + ret = MPTSAS_FIFO_GET(s, reply_post); + } else { + ret = -1; + s->intr_status &= ~MPI_HIS_REPLY_MESSAGE_INTERRUPT; + mptsas_update_interrupt(s); + } + + return ret; +} + +static uint64_t mptsas_mmio_read(void *opaque, hwaddr addr, + unsigned size) +{ + MPTSASState *s = opaque; + uint32_t ret = 0; + + switch (addr & ~3) { + case MPI_DOORBELL_OFFSET: + ret = mptsas_doorbell_read(s); + break; + + case MPI_DIAGNOSTIC_OFFSET: + ret = s->diagnostic; + break; + + case MPI_HOST_INTERRUPT_STATUS_OFFSET: + ret = s->intr_status; + break; + + case MPI_HOST_INTERRUPT_MASK_OFFSET: + ret = s->intr_mask; + break; + + case MPI_REPLY_POST_FIFO_OFFSET: + ret = mptsas_reply_post_read(s); + break; + + default: + trace_mptsas_mmio_unhandled_read(s, addr); + break; + } + trace_mptsas_mmio_read(s, addr, ret); + return ret; +} + +static void mptsas_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + MPTSASState *s = opaque; + + trace_mptsas_mmio_write(s, addr, val); + switch (addr) { + case MPI_DOORBELL_OFFSET: + mptsas_doorbell_write(s, val); + break; + + case MPI_WRITE_SEQUENCE_OFFSET: + mptsas_write_sequence_write(s, val); + break; + + case MPI_DIAGNOSTIC_OFFSET: + if (val & MPI_DIAG_RESET_ADAPTER) { + mptsas_hard_reset(s); + } + break; + + case MPI_HOST_INTERRUPT_STATUS_OFFSET: + mptsas_interrupt_status_write(s); + break; + + case MPI_HOST_INTERRUPT_MASK_OFFSET: + s->intr_mask = val & (MPI_HIM_RIM | MPI_HIM_DIM); + mptsas_update_interrupt(s); + break; + + case MPI_REQUEST_POST_FIFO_OFFSET: + if (MPTSAS_FIFO_FULL(s, request_post)) { + mptsas_set_fault(s, MPI_IOCSTATUS_INSUFFICIENT_RESOURCES); + } else { + MPTSAS_FIFO_PUT(s, request_post, val & ~0x03); + qemu_bh_schedule(s->request_bh); + } + break; + + case MPI_REPLY_FREE_FIFO_OFFSET: + if (MPTSAS_FIFO_FULL(s, reply_free)) { + mptsas_set_fault(s, MPI_IOCSTATUS_INSUFFICIENT_RESOURCES); + } else { + MPTSAS_FIFO_PUT(s, reply_free, val); + } + break; + + default: + trace_mptsas_mmio_unhandled_write(s, addr, val); + break; + } +} + +static const MemoryRegionOps mptsas_mmio_ops = { + .read = mptsas_mmio_read, + .write = mptsas_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static const MemoryRegionOps mptsas_port_ops = { + .read = mptsas_mmio_read, + .write = mptsas_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static uint64_t mptsas_diag_read(void *opaque, hwaddr addr, + unsigned size) +{ + MPTSASState *s = opaque; + trace_mptsas_diag_read(s, addr, 0); + return 0; +} + +static void mptsas_diag_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + MPTSASState *s = opaque; + trace_mptsas_diag_write(s, addr, val); +} + +static const MemoryRegionOps mptsas_diag_ops = { + .read = mptsas_diag_read, + .write = mptsas_diag_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static QEMUSGList *mptsas_get_sg_list(SCSIRequest *sreq) +{ + MPTSASRequest *req = sreq->hba_private; + + return &req->qsg; +} + +static void mptsas_command_complete(SCSIRequest *sreq, + uint32_t status, size_t resid) +{ + MPTSASRequest *req = sreq->hba_private; + MPTSASState *s = req->dev; + uint8_t sense_buf[SCSI_SENSE_BUF_SIZE]; + uint8_t sense_len; + + hwaddr sense_buffer_addr = req->dev->sense_buffer_high_addr | + req->scsi_io.SenseBufferLowAddr; + + trace_mptsas_command_complete(s, req->scsi_io.MsgContext, status, resid); + + sense_len = scsi_req_get_sense(sreq, sense_buf, SCSI_SENSE_BUF_SIZE); + if (sense_len > 0) { + pci_dma_write(PCI_DEVICE(s), sense_buffer_addr, sense_buf, + MIN(req->scsi_io.SenseBufferLength, sense_len)); + } + + if (sreq->status != GOOD || resid || + req->dev->doorbell_state == DOORBELL_WRITE) { + MPIMsgSCSIIOReply reply; + + memset(&reply, 0, sizeof(reply)); + reply.TargetID = req->scsi_io.TargetID; + reply.Bus = req->scsi_io.Bus; + reply.MsgLength = sizeof(reply) / 4; + reply.Function = req->scsi_io.Function; + reply.CDBLength = req->scsi_io.CDBLength; + reply.SenseBufferLength = req->scsi_io.SenseBufferLength; + reply.MsgFlags = req->scsi_io.MsgFlags; + reply.MsgContext = req->scsi_io.MsgContext; + reply.SCSIStatus = sreq->status; + if (sreq->status == GOOD) { + reply.TransferCount = req->scsi_io.DataLength - resid; + if (resid) { + reply.IOCStatus = MPI_IOCSTATUS_SCSI_DATA_UNDERRUN; + } + } else { + reply.SCSIState = MPI_SCSI_STATE_AUTOSENSE_VALID; + reply.SenseCount = sense_len; + reply.IOCStatus = MPI_IOCSTATUS_SCSI_DATA_UNDERRUN; + } + + mptsas_fix_scsi_io_reply_endianness(&reply); + mptsas_post_reply(req->dev, (MPIDefaultReply *)&reply); + } else { + mptsas_turbo_reply(req->dev, req->scsi_io.MsgContext); + } + + mptsas_free_request(req); +} + +static void mptsas_request_cancelled(SCSIRequest *sreq) +{ + MPTSASRequest *req = sreq->hba_private; + MPIMsgSCSIIOReply reply; + + memset(&reply, 0, sizeof(reply)); + reply.TargetID = req->scsi_io.TargetID; + reply.Bus = req->scsi_io.Bus; + reply.MsgLength = sizeof(reply) / 4; + reply.Function = req->scsi_io.Function; + reply.CDBLength = req->scsi_io.CDBLength; + reply.SenseBufferLength = req->scsi_io.SenseBufferLength; + reply.MsgFlags = req->scsi_io.MsgFlags; + reply.MsgContext = req->scsi_io.MsgContext; + reply.SCSIState = MPI_SCSI_STATE_NO_SCSI_STATUS; + reply.IOCStatus = MPI_IOCSTATUS_SCSI_TASK_TERMINATED; + + mptsas_fix_scsi_io_reply_endianness(&reply); + mptsas_post_reply(req->dev, (MPIDefaultReply *)&reply); + mptsas_free_request(req); +} + +static void mptsas_save_request(QEMUFile *f, SCSIRequest *sreq) +{ + MPTSASRequest *req = sreq->hba_private; + int i; + + qemu_put_buffer(f, (unsigned char *)&req->scsi_io, sizeof(req->scsi_io)); + qemu_put_be32(f, req->qsg.nsg); + for (i = 0; i < req->qsg.nsg; i++) { + qemu_put_be64(f, req->qsg.sg[i].base); + qemu_put_be64(f, req->qsg.sg[i].len); + } +} + +static void *mptsas_load_request(QEMUFile *f, SCSIRequest *sreq) +{ + SCSIBus *bus = sreq->bus; + MPTSASState *s = container_of(bus, MPTSASState, bus); + PCIDevice *pci = PCI_DEVICE(s); + MPTSASRequest *req; + int i, n; + + req = g_new(MPTSASRequest, 1); + qemu_get_buffer(f, (unsigned char *)&req->scsi_io, sizeof(req->scsi_io)); + + n = qemu_get_be32(f); + /* TODO: add a way for SCSIBusInfo's load_request to fail, + * and fail migration instead of asserting here. + * When we do, we might be able to re-enable NDEBUG below. + */ +#ifdef NDEBUG +#error building with NDEBUG is not supported +#endif + assert(n >= 0); + + pci_dma_sglist_init(&req->qsg, pci, n); + for (i = 0; i < n; i++) { + uint64_t base = qemu_get_be64(f); + uint64_t len = qemu_get_be64(f); + qemu_sglist_add(&req->qsg, base, len); + } + + scsi_req_ref(sreq); + req->sreq = sreq; + req->dev = s; + + return req; +} + +static const struct SCSIBusInfo mptsas_scsi_info = { + .tcq = true, + .max_target = MPTSAS_NUM_PORTS, + .max_lun = 1, + + .get_sg_list = mptsas_get_sg_list, + .complete = mptsas_command_complete, + .cancel = mptsas_request_cancelled, + .save_request = mptsas_save_request, + .load_request = mptsas_load_request, +}; + +static void mptsas_scsi_init(PCIDevice *dev, Error **errp) +{ + DeviceState *d = DEVICE(dev); + MPTSASState *s = MPT_SAS(dev); + + dev->config[PCI_LATENCY_TIMER] = 0; + dev->config[PCI_INTERRUPT_PIN] = 0x01; + + memory_region_init_io(&s->mmio_io, OBJECT(s), &mptsas_mmio_ops, s, + "mptsas-mmio", 0x4000); + memory_region_init_io(&s->port_io, OBJECT(s), &mptsas_port_ops, s, + "mptsas-io", 256); + memory_region_init_io(&s->diag_io, OBJECT(s), &mptsas_diag_ops, s, + "mptsas-diag", 0x10000); + + if (s->msi_available && + msi_init(dev, 0, 1, true, false) >= 0) { + s->msi_in_use = true; + } + + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->port_io); + pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_32, &s->mmio_io); + pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_32, &s->diag_io); + + if (!s->sas_addr) { + s->sas_addr = ((NAA_LOCALLY_ASSIGNED_ID << 24) | + IEEE_COMPANY_LOCALLY_ASSIGNED) << 36; + s->sas_addr |= (pci_bus_num(dev->bus) << 16); + s->sas_addr |= (PCI_SLOT(dev->devfn) << 8); + s->sas_addr |= PCI_FUNC(dev->devfn); + } + s->max_devices = MPTSAS_NUM_PORTS; + + s->request_bh = qemu_bh_new(mptsas_fetch_requests, s); + + QTAILQ_INIT(&s->pending); + + scsi_bus_new(&s->bus, sizeof(s->bus), &dev->qdev, &mptsas_scsi_info, NULL); + if (!d->hotplugged) { + scsi_bus_legacy_handle_cmdline(&s->bus, errp); + } +} + +static void mptsas_scsi_uninit(PCIDevice *dev) +{ + MPTSASState *s = MPT_SAS(dev); + + qemu_bh_delete(s->request_bh); + if (s->msi_in_use) { + msi_uninit(dev); + } +} + +static void mptsas_reset(DeviceState *dev) +{ + MPTSASState *s = MPT_SAS(dev); + + mptsas_hard_reset(s); +} + +static int mptsas_post_load(void *opaque, int version_id) +{ + MPTSASState *s = opaque; + + if (s->doorbell_idx > s->doorbell_cnt || + s->doorbell_cnt > ARRAY_SIZE(s->doorbell_msg) || + s->doorbell_reply_idx > s->doorbell_reply_size || + s->doorbell_reply_size > ARRAY_SIZE(s->doorbell_reply) || + MPTSAS_FIFO_INVALID(s, request_post) || + MPTSAS_FIFO_INVALID(s, reply_post) || + MPTSAS_FIFO_INVALID(s, reply_free) || + s->diagnostic_idx > 4) { + return -EINVAL; + } + + return 0; +} + +static const VMStateDescription vmstate_mptsas = { + .name = "mptsas", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .post_load = mptsas_post_load, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(dev, MPTSASState), + VMSTATE_BOOL(msi_in_use, MPTSASState), + + VMSTATE_UINT32(state, MPTSASState), + VMSTATE_UINT8(who_init, MPTSASState), + VMSTATE_UINT8(doorbell_state, MPTSASState), + VMSTATE_UINT32_ARRAY(doorbell_msg, MPTSASState, 256), + VMSTATE_INT32(doorbell_idx, MPTSASState), + VMSTATE_INT32(doorbell_cnt, MPTSASState), + + VMSTATE_UINT16_ARRAY(doorbell_reply, MPTSASState, 256), + VMSTATE_INT32(doorbell_reply_idx, MPTSASState), + VMSTATE_INT32(doorbell_reply_size, MPTSASState), + + VMSTATE_UINT32(diagnostic, MPTSASState), + VMSTATE_UINT8(diagnostic_idx, MPTSASState), + + VMSTATE_UINT32(intr_status, MPTSASState), + VMSTATE_UINT32(intr_mask, MPTSASState), + + VMSTATE_UINT32_ARRAY(request_post, MPTSASState, + MPTSAS_REQUEST_QUEUE_DEPTH + 1), + VMSTATE_UINT16(request_post_head, MPTSASState), + VMSTATE_UINT16(request_post_tail, MPTSASState), + + VMSTATE_UINT32_ARRAY(reply_post, MPTSASState, + MPTSAS_REPLY_QUEUE_DEPTH + 1), + VMSTATE_UINT16(reply_post_head, MPTSASState), + VMSTATE_UINT16(reply_post_tail, MPTSASState), + + VMSTATE_UINT32_ARRAY(reply_free, MPTSASState, + MPTSAS_REPLY_QUEUE_DEPTH + 1), + VMSTATE_UINT16(reply_free_head, MPTSASState), + VMSTATE_UINT16(reply_free_tail, MPTSASState), + + VMSTATE_UINT16(max_buses, MPTSASState), + VMSTATE_UINT16(max_devices, MPTSASState), + VMSTATE_UINT16(reply_frame_size, MPTSASState), + VMSTATE_UINT64(host_mfa_high_addr, MPTSASState), + VMSTATE_UINT64(sense_buffer_high_addr, MPTSASState), + VMSTATE_END_OF_LIST() + } +}; + +static Property mptsas_properties[] = { + DEFINE_PROP_UINT64("sas_address", MPTSASState, sas_addr, 0), + /* TODO: test MSI support under Windows */ + DEFINE_PROP_BIT("msi", MPTSASState, msi_available, 0, true), + DEFINE_PROP_END_OF_LIST(), +}; + +static void mptsas1068_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); + + pc->realize = mptsas_scsi_init; + pc->exit = mptsas_scsi_uninit; + pc->romfile = 0; + pc->vendor_id = PCI_VENDOR_ID_LSI_LOGIC; + pc->device_id = PCI_DEVICE_ID_LSI_SAS1068; + pc->subsystem_vendor_id = PCI_VENDOR_ID_LSI_LOGIC; + pc->subsystem_id = 0x8000; + pc->class_id = PCI_CLASS_STORAGE_SCSI; + dc->props = mptsas_properties; + dc->reset = mptsas_reset; + dc->vmsd = &vmstate_mptsas; + dc->desc = "LSI SAS 1068"; +} + +static const TypeInfo mptsas_info = { + .name = TYPE_MPTSAS1068, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(MPTSASState), + .class_init = mptsas1068_class_init, +}; + +static void mptsas_register_types(void) +{ + type_register(&mptsas_info); +} + +type_init(mptsas_register_types) diff --git a/qemu/hw/scsi/mptsas.h b/qemu/hw/scsi/mptsas.h new file mode 100644 index 000000000..595f81fb5 --- /dev/null +++ b/qemu/hw/scsi/mptsas.h @@ -0,0 +1,100 @@ +#ifndef MPTSAS_H +#define MPTSAS_H + +#include "mpi.h" + +#define MPTSAS_NUM_PORTS 8 +#define MPTSAS_MAX_FRAMES 2048 /* Firmware limit at 65535 */ + +#define MPTSAS_REQUEST_QUEUE_DEPTH 128 +#define MPTSAS_REPLY_QUEUE_DEPTH 128 + +#define MPTSAS_MAXIMUM_CHAIN_DEPTH 0x22 + +typedef struct MPTSASState MPTSASState; +typedef struct MPTSASRequest MPTSASRequest; + +enum { + DOORBELL_NONE, + DOORBELL_WRITE, + DOORBELL_READ +}; + +struct MPTSASState { + PCIDevice dev; + MemoryRegion mmio_io; + MemoryRegion port_io; + MemoryRegion diag_io; + QEMUBH *request_bh; + + uint32_t msi_available; + uint64_t sas_addr; + + bool msi_in_use; + + /* Doorbell register */ + uint32_t state; + uint8_t who_init; + uint8_t doorbell_state; + + /* Buffer for requests that are sent through the doorbell register. */ + uint32_t doorbell_msg[256]; + int doorbell_idx; + int doorbell_cnt; + + uint16_t doorbell_reply[256]; + int doorbell_reply_idx; + int doorbell_reply_size; + + /* Other registers */ + uint8_t diagnostic_idx; + uint32_t diagnostic; + uint32_t intr_mask; + uint32_t intr_status; + + /* Request queues */ + uint32_t request_post[MPTSAS_REQUEST_QUEUE_DEPTH + 1]; + uint16_t request_post_head; + uint16_t request_post_tail; + + uint32_t reply_post[MPTSAS_REPLY_QUEUE_DEPTH + 1]; + uint16_t reply_post_head; + uint16_t reply_post_tail; + + uint32_t reply_free[MPTSAS_REPLY_QUEUE_DEPTH + 1]; + uint16_t reply_free_head; + uint16_t reply_free_tail; + + /* IOC Facts */ + hwaddr host_mfa_high_addr; + hwaddr sense_buffer_high_addr; + uint16_t max_devices; + uint16_t max_buses; + uint16_t reply_frame_size; + + SCSIBus bus; + QTAILQ_HEAD(, MPTSASRequest) pending; +}; + +void mptsas_fix_scsi_io_endianness(MPIMsgSCSIIORequest *req); +void mptsas_fix_scsi_io_reply_endianness(MPIMsgSCSIIOReply *reply); +void mptsas_fix_scsi_task_mgmt_endianness(MPIMsgSCSITaskMgmt *req); +void mptsas_fix_scsi_task_mgmt_reply_endianness(MPIMsgSCSITaskMgmtReply *reply); +void mptsas_fix_ioc_init_endianness(MPIMsgIOCInit *req); +void mptsas_fix_ioc_init_reply_endianness(MPIMsgIOCInitReply *reply); +void mptsas_fix_ioc_facts_endianness(MPIMsgIOCFacts *req); +void mptsas_fix_ioc_facts_reply_endianness(MPIMsgIOCFactsReply *reply); +void mptsas_fix_config_endianness(MPIMsgConfig *req); +void mptsas_fix_config_reply_endianness(MPIMsgConfigReply *reply); +void mptsas_fix_port_facts_endianness(MPIMsgPortFacts *req); +void mptsas_fix_port_facts_reply_endianness(MPIMsgPortFactsReply *reply); +void mptsas_fix_port_enable_endianness(MPIMsgPortEnable *req); +void mptsas_fix_port_enable_reply_endianness(MPIMsgPortEnableReply *reply); +void mptsas_fix_event_notification_endianness(MPIMsgEventNotify *req); +void mptsas_fix_event_notification_reply_endianness(MPIMsgEventNotifyReply *reply); + +void mptsas_reply(MPTSASState *s, MPIDefaultReply *reply); + +void mptsas_process_config(MPTSASState *s, MPIMsgConfig *req); + +#endif /* MPTSAS_H */ diff --git a/qemu/hw/scsi/scsi-bus.c b/qemu/hw/scsi/scsi-bus.c index f0ae4625f..ad6f398c3 100644 --- a/qemu/hw/scsi/scsi-bus.c +++ b/qemu/hw/scsi/scsi-bus.c @@ -1,4 +1,6 @@ +#include "qemu/osdep.h" #include "hw/hw.h" +#include "qapi/error.h" #include "qemu/error-report.h" #include "hw/scsi/scsi.h" #include "block/scsi.h" @@ -7,6 +9,7 @@ #include "sysemu/blockdev.h" #include "trace.h" #include "sysemu/dma.h" +#include "qemu/cutils.h" static char *scsibus_get_dev_path(DeviceState *dev); static char *scsibus_get_fw_dev_path(DeviceState *dev); @@ -136,7 +139,8 @@ static void scsi_dma_restart_cb(void *opaque, int running, RunState state) return; } if (!s->bh) { - s->bh = qemu_bh_new(scsi_dma_restart_bh, s); + AioContext *ctx = blk_get_aio_context(s->conf.blk); + s->bh = aio_bh_new(ctx, scsi_dma_restart_bh, s); qemu_bh_schedule(s->bh); } } @@ -452,7 +456,7 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r) r->buf[7] = 0x10 | (r->req.bus->info->tcq ? 0x02 : 0); /* Sync, TCQ. */ memcpy(&r->buf[8], "QEMU ", 8); memcpy(&r->buf[16], "QEMU TARGET ", 16); - pstrcpy((char *) &r->buf[32], 4, qemu_get_version()); + pstrcpy((char *) &r->buf[32], 4, qemu_hw_version()); } return true; } @@ -557,7 +561,7 @@ SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d, const int memset_off = offsetof(SCSIRequest, sense) + sizeof(req->sense); - req = g_slice_alloc(reqops->size); + req = g_malloc(reqops->size); memset((uint8_t *)req + memset_off, 0, reqops->size - memset_off); req->refcount = 1; req->bus = bus; @@ -987,7 +991,6 @@ static int scsi_req_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) } /* fall through */ case READ_10: - case RECOVER_BUFFERED_DATA: case READ_12: case READ_16: cmd->xfer *= dev->blocksize; @@ -1621,7 +1624,7 @@ void scsi_req_unref(SCSIRequest *req) } object_unref(OBJECT(req->dev)); object_unref(OBJECT(qbus->parent)); - g_slice_free1(req->ops->size, req); + g_free(req); } } @@ -1759,8 +1762,14 @@ void scsi_req_cancel_async(SCSIRequest *req, Notifier *notifier) notifier_list_add(&req->cancel_notifiers, notifier); } if (req->io_canceled) { + /* A blk_aio_cancel_async is pending; when it finishes, + * scsi_req_cancel_complete will be called and will + * call the notifier we just added. Just wait for that. + */ + assert(req->aiocb); return; } + /* Dropped in scsi_req_cancel_complete. */ scsi_req_ref(req); scsi_req_dequeue(req); req->io_canceled = true; @@ -1777,6 +1786,8 @@ void scsi_req_cancel(SCSIRequest *req) if (!req->enqueued) { return; } + assert(!req->io_canceled); + /* Dropped in scsi_req_cancel_complete. */ scsi_req_ref(req); scsi_req_dequeue(req); req->io_canceled = true; @@ -1840,17 +1851,19 @@ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense) { SCSIRequest *req; + aio_context_acquire(blk_get_aio_context(sdev->conf.blk)); while (!QTAILQ_EMPTY(&sdev->requests)) { req = QTAILQ_FIRST(&sdev->requests); - scsi_req_cancel(req); + scsi_req_cancel_async(req, NULL); } - + blk_drain(sdev->conf.blk); + aio_context_release(blk_get_aio_context(sdev->conf.blk)); scsi_device_set_ua(sdev, sense); } static char *scsibus_get_dev_path(DeviceState *dev) { - SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev); + SCSIDevice *d = SCSI_DEVICE(dev); DeviceState *hba = dev->parent_bus->parent; char *id; char *path; @@ -2023,7 +2036,7 @@ static void scsi_device_class_init(ObjectClass *klass, void *data) static void scsi_dev_instance_init(Object *obj) { DeviceState *dev = DEVICE(obj); - SCSIDevice *s = DO_UPCAST(SCSIDevice, qdev, dev); + SCSIDevice *s = SCSI_DEVICE(dev); device_add_bootindex_property(obj, &s->conf.bootindex, "bootindex", NULL, diff --git a/qemu/hw/scsi/scsi-disk.c b/qemu/hw/scsi/scsi-disk.c index 64f069473..c3ce54a20 100644 --- a/qemu/hw/scsi/scsi-disk.c +++ b/qemu/hw/scsi/scsi-disk.c @@ -28,7 +28,8 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #define DPRINTF(fmt, ...) do {} while(0) #endif -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/error-report.h" #include "hw/scsi/scsi.h" #include "block/scsi.h" @@ -37,6 +38,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #include "sysemu/blockdev.h" #include "hw/block/block.h" #include "sysemu/dma.h" +#include "qemu/cutils.h" #ifdef __linux #include @@ -76,8 +78,6 @@ struct SCSIDiskState bool media_changed; bool media_event; bool eject_request; - uint64_t wwn; - uint64_t port_wwn; uint16_t port_index; uint64_t max_unmap_size; uint64_t max_io_size; @@ -90,7 +90,7 @@ struct SCSIDiskState bool tray_locked; }; -static int scsi_handle_rw_error(SCSIDiskReq *r, int error); +static int scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed); static void scsi_free_request(SCSIRequest *req) { @@ -169,18 +169,18 @@ static void scsi_aio_complete(void *opaque, int ret) assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); if (r->req.io_canceled) { scsi_req_cancel_complete(&r->req); goto done; } if (ret < 0) { - if (scsi_handle_rw_error(r, -ret)) { + if (scsi_handle_rw_error(r, -ret, true)) { goto done; } } + block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); scsi_req_complete(&r->req, GOOD); done: @@ -217,6 +217,8 @@ static void scsi_write_do_fua(SCSIDiskReq *r) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + assert(r->req.aiocb == NULL); + if (r->req.io_canceled) { scsi_req_cancel_complete(&r->req); goto done; @@ -235,22 +237,17 @@ done: scsi_req_unref(&r->req); } -static void scsi_dma_complete_noio(void *opaque, int ret) +static void scsi_dma_complete_noio(SCSIDiskReq *r, int ret) { - SCSIDiskReq *r = (SCSIDiskReq *)opaque; - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + assert(r->req.aiocb == NULL); - if (r->req.aiocb != NULL) { - r->req.aiocb = NULL; - block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); - } if (r->req.io_canceled) { scsi_req_cancel_complete(&r->req); goto done; } if (ret < 0) { - if (scsi_handle_rw_error(r, -ret)) { + if (scsi_handle_rw_error(r, -ret, false)) { goto done; } } @@ -271,9 +268,17 @@ done: static void scsi_dma_complete(void *opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); assert(r->req.aiocb != NULL); - scsi_dma_complete_noio(opaque, ret); + r->req.aiocb = NULL; + + if (ret < 0) { + block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); + } else { + block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); + } + scsi_dma_complete_noio(r, ret); } static void scsi_read_complete(void * opaque, int ret) @@ -284,18 +289,18 @@ static void scsi_read_complete(void * opaque, int ret) assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); if (r->req.io_canceled) { scsi_req_cancel_complete(&r->req); goto done; } if (ret < 0) { - if (scsi_handle_rw_error(r, -ret)) { + if (scsi_handle_rw_error(r, -ret, true)) { goto done; } } + block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); DPRINTF("Data ready tag=0x%x len=%zd\n", r->req.tag, r->qiov.size); n = r->qiov.size / 512; @@ -308,23 +313,20 @@ done: } /* Actually issue a read to the block device. */ -static void scsi_do_read(void *opaque, int ret) +static void scsi_do_read(SCSIDiskReq *r, int ret) { - SCSIDiskReq *r = opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint32_t n; - if (r->req.aiocb != NULL) { - r->req.aiocb = NULL; - block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); - } + assert (r->req.aiocb == NULL); + if (r->req.io_canceled) { scsi_req_cancel_complete(&r->req); goto done; } if (ret < 0) { - if (scsi_handle_rw_error(r, -ret)) { + if (scsi_handle_rw_error(r, -ret, false)) { goto done; } } @@ -349,6 +351,22 @@ done: scsi_req_unref(&r->req); } +static void scsi_do_read_cb(void *opaque, int ret) +{ + SCSIDiskReq *r = (SCSIDiskReq *)opaque; + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + + assert (r->req.aiocb != NULL); + r->req.aiocb = NULL; + + if (ret < 0) { + block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); + } else { + block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); + } + scsi_do_read(opaque, ret); +} + /* Read more data from scsi device into buffer. */ static void scsi_read_data(SCSIRequest *req) { @@ -384,7 +402,7 @@ static void scsi_read_data(SCSIRequest *req) if (first && scsi_is_cmd_fua(&r->req.cmd)) { block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0, BLOCK_ACCT_FLUSH); - r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_do_read, r); + r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_do_read_cb, r); } else { scsi_do_read(r, 0); } @@ -397,14 +415,17 @@ static void scsi_read_data(SCSIRequest *req) * scsi_handle_rw_error always manages its reference counts, independent * of the return value. */ -static int scsi_handle_rw_error(SCSIDiskReq *r, int error) +static int scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed) { - bool is_read = (r->req.cmd.xfer == SCSI_XFER_FROM_DEV); + bool is_read = (r->req.cmd.mode == SCSI_XFER_FROM_DEV); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); BlockErrorAction action = blk_get_error_action(s->qdev.conf.blk, is_read, error); if (action == BLOCK_ERROR_ACTION_REPORT) { + if (acct_failed) { + block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); + } switch (error) { case ENOMEDIUM: scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); @@ -430,23 +451,19 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error) return action != BLOCK_ERROR_ACTION_IGNORE; } -static void scsi_write_complete(void * opaque, int ret) +static void scsi_write_complete_noio(SCSIDiskReq *r, int ret) { - SCSIDiskReq *r = (SCSIDiskReq *)opaque; - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint32_t n; - if (r->req.aiocb != NULL) { - r->req.aiocb = NULL; - block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); - } + assert (r->req.aiocb == NULL); + if (r->req.io_canceled) { scsi_req_cancel_complete(&r->req); goto done; } if (ret < 0) { - if (scsi_handle_rw_error(r, -ret)) { + if (scsi_handle_rw_error(r, -ret, false)) { goto done; } } @@ -467,6 +484,22 @@ done: scsi_req_unref(&r->req); } +static void scsi_write_complete(void * opaque, int ret) +{ + SCSIDiskReq *r = (SCSIDiskReq *)opaque; + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + + assert (r->req.aiocb != NULL); + r->req.aiocb = NULL; + + if (ret < 0) { + block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); + } else { + block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); + } + scsi_write_complete_noio(r, ret); +} + static void scsi_write_data(SCSIRequest *req) { SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); @@ -480,18 +513,18 @@ static void scsi_write_data(SCSIRequest *req) scsi_req_ref(&r->req); if (r->req.cmd.mode != SCSI_XFER_TO_DEV) { DPRINTF("Data transfer direction invalid\n"); - scsi_write_complete(r, -EINVAL); + scsi_write_complete_noio(r, -EINVAL); return; } if (!r->req.sg && !r->qiov.size) { /* Called for the first time. Ask the driver to send us more data. */ r->started = true; - scsi_write_complete(r, 0); + scsi_write_complete_noio(r, 0); return; } if (s->tray_open) { - scsi_write_complete(r, -ENOMEDIUM); + scsi_write_complete_noio(r, -ENOMEDIUM); return; } @@ -500,7 +533,7 @@ static void scsi_write_data(SCSIRequest *req) if (r->req.sg) { scsi_dma_complete_noio(r, 0); } else { - scsi_write_complete(r, 0); + scsi_write_complete_noio(r, 0); } return; } @@ -599,21 +632,21 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) memcpy(outbuf+buflen, str, id_len); buflen += id_len; - if (s->wwn) { + if (s->qdev.wwn) { outbuf[buflen++] = 0x1; // Binary outbuf[buflen++] = 0x3; // NAA outbuf[buflen++] = 0; // reserved outbuf[buflen++] = 8; - stq_be_p(&outbuf[buflen], s->wwn); + stq_be_p(&outbuf[buflen], s->qdev.wwn); buflen += 8; } - if (s->port_wwn) { + if (s->qdev.port_wwn) { outbuf[buflen++] = 0x61; // SAS / Binary outbuf[buflen++] = 0x93; // PIV / Target port / NAA outbuf[buflen++] = 0; // reserved outbuf[buflen++] = 8; - stq_be_p(&outbuf[buflen], s->port_wwn); + stq_be_p(&outbuf[buflen], s->qdev.port_wwn); buflen += 8; } @@ -1557,22 +1590,24 @@ typedef struct UnmapCBData { int count; } UnmapCBData; -static void scsi_unmap_complete(void *opaque, int ret) +static void scsi_unmap_complete(void *opaque, int ret); + +static void scsi_unmap_complete_noio(UnmapCBData *data, int ret) { - UnmapCBData *data = opaque; SCSIDiskReq *r = data->r; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint64_t sector_num; uint32_t nb_sectors; - r->req.aiocb = NULL; + assert(r->req.aiocb == NULL); + if (r->req.io_canceled) { scsi_req_cancel_complete(&r->req); goto done; } if (ret < 0) { - if (scsi_handle_rw_error(r, -ret)) { + if (scsi_handle_rw_error(r, -ret, false)) { goto done; } } @@ -1601,6 +1636,17 @@ done: g_free(data); } +static void scsi_unmap_complete(void *opaque, int ret) +{ + UnmapCBData *data = opaque; + SCSIDiskReq *r = data->r; + + assert(r->req.aiocb != NULL); + r->req.aiocb = NULL; + + scsi_unmap_complete_noio(data, ret); +} + static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); @@ -1638,7 +1684,7 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf) /* The matching unref is in scsi_unmap_complete, before data is freed. */ scsi_req_ref(&r->req); - scsi_unmap_complete(data, 0); + scsi_unmap_complete_noio(data, 0); return; invalid_param_len: @@ -1665,24 +1711,29 @@ static void scsi_write_same_complete(void *opaque, int ret) assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); if (r->req.io_canceled) { scsi_req_cancel_complete(&r->req); goto done; } if (ret < 0) { - if (scsi_handle_rw_error(r, -ret)) { + if (scsi_handle_rw_error(r, -ret, true)) { goto done; } } + block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); + data->nb_sectors -= data->iov.iov_len / 512; data->sector += data->iov.iov_len / 512; data->iov.iov_len = MIN(data->nb_sectors * 512, data->iov.iov_len); if (data->iov.iov_len) { block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, data->iov.iov_len, BLOCK_ACCT_WRITE); + /* blk_aio_write doesn't like the qiov size being different from + * nb_sectors, make sure they match. + */ + qemu_iovec_init_external(&data->qiov, &data->iov, 1); r->req.aiocb = blk_aio_writev(s->qdev.conf.blk, data->sector, &data->qiov, data->iov.iov_len / 512, scsi_write_same_complete, data); @@ -2280,7 +2331,7 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) } if (!s->version) { - s->version = g_strdup(qemu_get_version()); + s->version = g_strdup(qemu_hw_version()); } if (!s->vendor) { s->vendor = g_strdup("QEMU"); @@ -2523,6 +2574,7 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp) s->features |= (1 << SCSI_DISK_F_NO_REMOVABLE_DEVOPS); scsi_realize(&s->qdev, errp); + scsi_generic_read_device_identification(&s->qdev); } static bool scsi_block_is_passthrough(SCSIDiskState *s, uint8_t *buf) @@ -2616,8 +2668,8 @@ static Property scsi_hd_properties[] = { SCSI_DISK_F_REMOVABLE, false), DEFINE_PROP_BIT("dpofua", SCSIDiskState, features, SCSI_DISK_F_DPOFUA, false), - DEFINE_PROP_UINT64("wwn", SCSIDiskState, wwn, 0), - DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, port_wwn, 0), + DEFINE_PROP_UINT64("wwn", SCSIDiskState, qdev.wwn, 0), + DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, qdev.port_wwn, 0), DEFINE_PROP_UINT16("port_index", SCSIDiskState, port_index, 0), DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size, DEFAULT_MAX_UNMAP_SIZE), @@ -2666,8 +2718,8 @@ static const TypeInfo scsi_hd_info = { static Property scsi_cd_properties[] = { DEFINE_SCSI_DISK_PROPERTIES(), - DEFINE_PROP_UINT64("wwn", SCSIDiskState, wwn, 0), - DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, port_wwn, 0), + DEFINE_PROP_UINT64("wwn", SCSIDiskState, qdev.wwn, 0), + DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, qdev.port_wwn, 0), DEFINE_PROP_UINT16("port_index", SCSIDiskState, port_index, 0), DEFINE_PROP_UINT64("max_io_size", SCSIDiskState, max_io_size, DEFAULT_MAX_IO_SIZE), @@ -2731,8 +2783,8 @@ static Property scsi_disk_properties[] = { SCSI_DISK_F_REMOVABLE, false), DEFINE_PROP_BIT("dpofua", SCSIDiskState, features, SCSI_DISK_F_DPOFUA, false), - DEFINE_PROP_UINT64("wwn", SCSIDiskState, wwn, 0), - DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, port_wwn, 0), + DEFINE_PROP_UINT64("wwn", SCSIDiskState, qdev.wwn, 0), + DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, qdev.port_wwn, 0), DEFINE_PROP_UINT16("port_index", SCSIDiskState, port_index, 0), DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size, DEFAULT_MAX_UNMAP_SIZE), diff --git a/qemu/hw/scsi/scsi-generic.c b/qemu/hw/scsi/scsi-generic.c index e53470f85..7459465f6 100644 --- a/qemu/hw/scsi/scsi-generic.c +++ b/qemu/hw/scsi/scsi-generic.c @@ -11,6 +11,8 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/error-report.h" #include "hw/scsi/scsi.h" @@ -31,10 +33,6 @@ do { printf("scsi-generic: " fmt , ## __VA_ARGS__); } while (0) #define BADF(fmt, ...) \ do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0) -#include -#include -#include -#include #include #include "block/scsi.h" @@ -88,12 +86,12 @@ static void scsi_free_request(SCSIRequest *req) } /* Helper function for command completion. */ -static void scsi_command_complete(void *opaque, int ret) +static void scsi_command_complete_noio(SCSIGenericReq *r, int ret) { int status; - SCSIGenericReq *r = (SCSIGenericReq *)opaque; - r->req.aiocb = NULL; + assert(r->req.aiocb == NULL); + if (r->req.io_canceled) { scsi_req_cancel_complete(&r->req); goto done; @@ -142,6 +140,15 @@ done: scsi_req_unref(&r->req); } +static void scsi_command_complete(void *opaque, int ret) +{ + SCSIGenericReq *r = (SCSIGenericReq *)opaque; + + assert(r->req.aiocb != NULL); + r->req.aiocb = NULL; + scsi_command_complete_noio(r, ret); +} + static int execute_command(BlockBackend *blk, SCSIGenericReq *r, int direction, BlockCompletionFunc *complete) @@ -172,33 +179,51 @@ static void scsi_read_complete(void * opaque, int ret) SCSIDevice *s = r->req.dev; int len; + assert(r->req.aiocb != NULL); r->req.aiocb = NULL; + if (ret || r->req.io_canceled) { - scsi_command_complete(r, ret); + scsi_command_complete_noio(r, ret); return; } + len = r->io_header.dxfer_len - r->io_header.resid; DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len); r->len = -1; if (len == 0) { - scsi_command_complete(r, 0); - } else { - /* Snoop READ CAPACITY output to set the blocksize. */ - if (r->req.cmd.buf[0] == READ_CAPACITY_10 && - (ldl_be_p(&r->buf[0]) != 0xffffffffU || s->max_lba == 0)) { - s->blocksize = ldl_be_p(&r->buf[4]); - s->max_lba = ldl_be_p(&r->buf[0]) & 0xffffffffULL; - } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 && - (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) { - s->blocksize = ldl_be_p(&r->buf[8]); - s->max_lba = ldq_be_p(&r->buf[0]); - } - blk_set_guest_block_size(s->conf.blk, s->blocksize); + scsi_command_complete_noio(r, 0); + return; + } - scsi_req_data(&r->req, len); - scsi_req_unref(&r->req); + /* Snoop READ CAPACITY output to set the blocksize. */ + if (r->req.cmd.buf[0] == READ_CAPACITY_10 && + (ldl_be_p(&r->buf[0]) != 0xffffffffU || s->max_lba == 0)) { + s->blocksize = ldl_be_p(&r->buf[4]); + s->max_lba = ldl_be_p(&r->buf[0]) & 0xffffffffULL; + } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 && + (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) { + s->blocksize = ldl_be_p(&r->buf[8]); + s->max_lba = ldq_be_p(&r->buf[0]); + } + blk_set_guest_block_size(s->conf.blk, s->blocksize); + + /* Patch MODE SENSE device specific parameters if the BDS is opened + * readonly. + */ + if ((s->type == TYPE_DISK || s->type == TYPE_TAPE) && + blk_is_read_only(s->conf.blk) && + (r->req.cmd.buf[0] == MODE_SENSE || + r->req.cmd.buf[0] == MODE_SENSE_10) && + (r->req.cmd.buf[1] & 0x8) == 0) { + if (r->req.cmd.buf[0] == MODE_SENSE) { + r->buf[2] |= 0x80; + } else { + r->buf[3] |= 0x80; + } } + scsi_req_data(&r->req, len); + scsi_req_unref(&r->req); } /* Read more data from scsi device into buffer. */ @@ -213,14 +238,14 @@ static void scsi_read_data(SCSIRequest *req) /* The request is used as the AIO opaque value, so add a ref. */ scsi_req_ref(&r->req); if (r->len == -1) { - scsi_command_complete(r, 0); + scsi_command_complete_noio(r, 0); return; } ret = execute_command(s->conf.blk, r, SG_DXFER_FROM_DEV, scsi_read_complete); if (ret < 0) { - scsi_command_complete(r, ret); + scsi_command_complete_noio(r, ret); } } @@ -230,9 +255,12 @@ static void scsi_write_complete(void * opaque, int ret) SCSIDevice *s = r->req.dev; DPRINTF("scsi_write_complete() ret = %d\n", ret); + + assert(r->req.aiocb != NULL); r->req.aiocb = NULL; + if (ret || r->req.io_canceled) { - scsi_command_complete(r, ret); + scsi_command_complete_noio(r, ret); return; } @@ -242,7 +270,7 @@ static void scsi_write_complete(void * opaque, int ret) DPRINTF("block size %d\n", s->blocksize); } - scsi_command_complete(r, ret); + scsi_command_complete_noio(r, ret); } /* Write data to a scsi device. Returns nonzero on failure. @@ -264,7 +292,7 @@ static void scsi_write_data(SCSIRequest *req) scsi_req_ref(&r->req); ret = execute_command(s->conf.blk, r, SG_DXFER_TO_DEV, scsi_write_complete); if (ret < 0) { - scsi_command_complete(r, ret); + scsi_command_complete_noio(r, ret); } } @@ -306,7 +334,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) ret = execute_command(s->conf.blk, r, SG_DXFER_NONE, scsi_command_complete); if (ret < 0) { - scsi_command_complete(r, ret); + scsi_command_complete_noio(r, ret); return 0; } return 0; @@ -328,6 +356,96 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) } } +static int read_naa_id(const uint8_t *p, uint64_t *p_wwn) +{ + int i; + + if ((p[1] & 0xF) == 3) { + /* NAA designator type */ + if (p[3] != 8) { + return -EINVAL; + } + *p_wwn = ldq_be_p(p + 4); + return 0; + } + + if ((p[1] & 0xF) == 8) { + /* SCSI name string designator type */ + if (p[3] < 20 || memcmp(&p[4], "naa.", 4)) { + return -EINVAL; + } + if (p[3] > 20 && p[24] != ',') { + return -EINVAL; + } + *p_wwn = 0; + for (i = 8; i < 24; i++) { + char c = toupper(p[i]); + c -= (c >= '0' && c <= '9' ? '0' : 'A' - 10); + *p_wwn = (*p_wwn << 4) | c; + } + return 0; + } + + return -EINVAL; +} + +void scsi_generic_read_device_identification(SCSIDevice *s) +{ + uint8_t cmd[6]; + uint8_t buf[250]; + uint8_t sensebuf[8]; + sg_io_hdr_t io_header; + int ret; + int i, len; + + memset(cmd, 0, sizeof(cmd)); + memset(buf, 0, sizeof(buf)); + cmd[0] = INQUIRY; + cmd[1] = 1; + cmd[2] = 0x83; + cmd[4] = sizeof(buf); + + memset(&io_header, 0, sizeof(io_header)); + io_header.interface_id = 'S'; + io_header.dxfer_direction = SG_DXFER_FROM_DEV; + io_header.dxfer_len = sizeof(buf); + io_header.dxferp = buf; + io_header.cmdp = cmd; + io_header.cmd_len = sizeof(cmd); + io_header.mx_sb_len = sizeof(sensebuf); + io_header.sbp = sensebuf; + io_header.timeout = 6000; /* XXX */ + + ret = blk_ioctl(s->conf.blk, SG_IO, &io_header); + if (ret < 0 || io_header.driver_status || io_header.host_status) { + return; + } + + len = MIN((buf[2] << 8) | buf[3], sizeof(buf) - 4); + for (i = 0; i + 3 <= len; ) { + const uint8_t *p = &buf[i + 4]; + uint64_t wwn; + + if (i + (p[3] + 4) > len) { + break; + } + + if ((p[1] & 0x10) == 0) { + /* Associated with the logical unit */ + if (read_naa_id(p, &wwn) == 0) { + s->wwn = wwn; + } + } else if ((p[1] & 0x10) == 0x10) { + /* Associated with the target port */ + if (read_naa_id(p, &wwn) == 0) { + s->port_wwn = wwn; + } + } + + i += p[3] + 4; + } +} + static int get_stream_blocksize(BlockBackend *blk) { uint8_t cmd[6]; @@ -431,6 +549,8 @@ static void scsi_generic_realize(SCSIDevice *s, Error **errp) } DPRINTF("block size %d\n", s->blocksize); + + scsi_generic_read_device_identification(s); } const SCSIReqOps scsi_generic_req_ops = { diff --git a/qemu/hw/scsi/spapr_vscsi.c b/qemu/hw/scsi/spapr_vscsi.c index 891424fae..b00edf7fd 100644 --- a/qemu/hw/scsi/spapr_vscsi.c +++ b/qemu/hw/scsi/spapr_vscsi.c @@ -31,6 +31,9 @@ * - Add indirect descriptors support * - Maybe do autosense (PAPR seems to mandate it, linux doesn't care) */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/scsi/scsi.h" #include "block/scsi.h" @@ -750,7 +753,6 @@ static void vscsi_report_luns(VSCSIState *s, vscsi_req *req) len = n+8; resp_data = g_malloc0(len); - memset(resp_data, 0, len); stl_be_p(resp_data, n); i = found_lun0 ? 8 : 16; QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { diff --git a/qemu/hw/scsi/vhost-scsi.c b/qemu/hw/scsi/vhost-scsi.c index a69918bef..9261d51da 100644 --- a/qemu/hw/scsi/vhost-scsi.c +++ b/qemu/hw/scsi/vhost-scsi.c @@ -14,8 +14,9 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include -#include "config.h" #include "qemu/error-report.h" #include "qemu/queue.h" #include "monitor/monitor.h" @@ -26,6 +27,8 @@ #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" #include "hw/fw-path-provider.h" +#include "linux/vhost.h" +#include "qemu/cutils.h" /* Features supported by host kernel. */ static const int kernel_feature_bits[] = { @@ -45,7 +48,7 @@ static int vhost_scsi_set_endpoint(VHostSCSI *s) memset(&backend, 0, sizeof(backend)); pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn); - ret = vhost_ops->vhost_call(&s->dev, VHOST_SCSI_SET_ENDPOINT, &backend); + ret = vhost_ops->vhost_scsi_set_endpoint(&s->dev, &backend); if (ret < 0) { return -errno; } @@ -60,7 +63,7 @@ static void vhost_scsi_clear_endpoint(VHostSCSI *s) memset(&backend, 0, sizeof(backend)); pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn); - vhost_ops->vhost_call(&s->dev, VHOST_SCSI_CLEAR_ENDPOINT, &backend); + vhost_ops->vhost_scsi_clear_endpoint(&s->dev, &backend); } static int vhost_scsi_start(VHostSCSI *s) @@ -76,8 +79,7 @@ static int vhost_scsi_start(VHostSCSI *s) return -ENOSYS; } - ret = vhost_ops->vhost_call(&s->dev, - VHOST_SCSI_GET_ABI_VERSION, &abi_version); + ret = vhost_ops->vhost_scsi_get_abi_version(&s->dev, &abi_version); if (ret < 0) { return -errno; } @@ -118,7 +120,7 @@ static int vhost_scsi_start(VHostSCSI *s) * enabling/disabling irqfd. */ for (i = 0; i < s->dev.nvqs; i++) { - vhost_virtqueue_mask(&s->dev, vdev, i, false); + vhost_virtqueue_mask(&s->dev, vdev, s->dev.vq_index + i, false); } return ret; @@ -217,11 +219,9 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) } if (vs->conf.vhostfd) { - vhostfd = monitor_fd_param(cur_mon, vs->conf.vhostfd, &err); + vhostfd = monitor_fd_param(cur_mon, vs->conf.vhostfd, errp); if (vhostfd == -1) { - error_setg(errp, "vhost-scsi: unable to parse vhostfd: %s", - error_get_pretty(err)); - error_free(err); + error_prepend(errp, "vhost-scsi: unable to parse vhostfd: "); return; } } else { @@ -277,6 +277,7 @@ static void vhost_scsi_unrealize(DeviceState *dev, Error **errp) /* This will stop vhost backend. */ vhost_scsi_set_status(vdev, 0); + vhost_dev_cleanup(&s->dev); g_free(s->dev.vqs); virtio_scsi_common_unrealize(dev, errp); @@ -291,7 +292,7 @@ static char *vhost_scsi_get_fw_dev_path(FWPathProvider *p, BusState *bus, { VHostSCSI *s = VHOST_SCSI(dev); /* format: channel@channel/vhost-scsi@target,lun */ - return g_strdup_printf("channel@%x/%s@%x,%x", s->channel, + return g_strdup_printf("/channel@%x/%s@%x,%x", s->channel, qdev_fw_name(dev), s->target, s->lun); } diff --git a/qemu/hw/scsi/virtio-scsi-dataplane.c b/qemu/hw/scsi/virtio-scsi-dataplane.c index 5575648a9..1a49f1e4b 100644 --- a/qemu/hw/scsi/virtio-scsi-dataplane.c +++ b/qemu/hw/scsi/virtio-scsi-dataplane.c @@ -11,6 +11,7 @@ * */ +#include "qemu/osdep.h" #include "hw/virtio/virtio-scsi.h" #include "qemu/error-report.h" #include "sysemu/block-backend.h" @@ -18,7 +19,6 @@ #include #include #include "hw/virtio/virtio-access.h" -#include "stdio.h" /* Context: QEMU global mutex held */ void virtio_scsi_set_iothread(VirtIOSCSI *s, IOThread *iothread) @@ -38,120 +38,57 @@ void virtio_scsi_set_iothread(VirtIOSCSI *s, IOThread *iothread) } } -static VirtIOSCSIVring *virtio_scsi_vring_init(VirtIOSCSI *s, - VirtQueue *vq, - EventNotifierHandler *handler, - int n) +static void virtio_scsi_data_plane_handle_cmd(VirtIODevice *vdev, + VirtQueue *vq) { - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - VirtIOSCSIVring *r; - int rc; + VirtIOSCSI *s = (VirtIOSCSI *)vdev; - /* Set up virtqueue notify */ - rc = k->set_host_notifier(qbus->parent, n, true); - if (rc != 0) { - fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n", - rc); - s->dataplane_fenced = true; - return NULL; - } - - r = g_slice_new(VirtIOSCSIVring); - r->host_notifier = *virtio_queue_get_host_notifier(vq); - r->guest_notifier = *virtio_queue_get_guest_notifier(vq); - aio_set_event_notifier(s->ctx, &r->host_notifier, handler); - - r->parent = s; - - if (!vring_setup(&r->vring, VIRTIO_DEVICE(s), n)) { - fprintf(stderr, "virtio-scsi: VRing setup failed\n"); - goto fail_vring; - } - return r; - -fail_vring: - aio_set_event_notifier(s->ctx, &r->host_notifier, NULL); - k->set_host_notifier(qbus->parent, n, false); - g_slice_free(VirtIOSCSIVring, r); - return NULL; + assert(s->ctx && s->dataplane_started); + virtio_scsi_handle_cmd_vq(s, vq); } -VirtIOSCSIReq *virtio_scsi_pop_req_vring(VirtIOSCSI *s, - VirtIOSCSIVring *vring) +static void virtio_scsi_data_plane_handle_ctrl(VirtIODevice *vdev, + VirtQueue *vq) { - VirtIOSCSIReq *req = virtio_scsi_init_req(s, NULL); - int r; - - req->vring = vring; - r = vring_pop((VirtIODevice *)s, &vring->vring, &req->elem); - if (r < 0) { - virtio_scsi_free_req(req); - req = NULL; - } - return req; -} + VirtIOSCSI *s = VIRTIO_SCSI(vdev); -void virtio_scsi_vring_push_notify(VirtIOSCSIReq *req) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(req->vring->parent); - - vring_push(vdev, &req->vring->vring, &req->elem, - req->qsgl.size + req->resp_iov.size); - - if (vring_should_notify(vdev, &req->vring->vring)) { - event_notifier_set(&req->vring->guest_notifier); - } + assert(s->ctx && s->dataplane_started); + virtio_scsi_handle_ctrl_vq(s, vq); } -static void virtio_scsi_iothread_handle_ctrl(EventNotifier *notifier) +static void virtio_scsi_data_plane_handle_event(VirtIODevice *vdev, + VirtQueue *vq) { - VirtIOSCSIVring *vring = container_of(notifier, - VirtIOSCSIVring, host_notifier); - VirtIOSCSI *s = VIRTIO_SCSI(vring->parent); - VirtIOSCSIReq *req; - - event_notifier_test_and_clear(notifier); - while ((req = virtio_scsi_pop_req_vring(s, vring))) { - virtio_scsi_handle_ctrl_req(s, req); - } + VirtIOSCSI *s = VIRTIO_SCSI(vdev); + + assert(s->ctx && s->dataplane_started); + virtio_scsi_handle_event_vq(s, vq); } -static void virtio_scsi_iothread_handle_event(EventNotifier *notifier) +static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n, + void (*fn)(VirtIODevice *vdev, VirtQueue *vq)) { - VirtIOSCSIVring *vring = container_of(notifier, - VirtIOSCSIVring, host_notifier); - VirtIOSCSI *s = vring->parent; - VirtIODevice *vdev = VIRTIO_DEVICE(s); - - event_notifier_test_and_clear(notifier); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + int rc; - if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { - return; + /* Set up virtqueue notify */ + rc = k->set_host_notifier(qbus->parent, n, true); + if (rc != 0) { + fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n", + rc); + s->dataplane_fenced = true; + return rc; } - if (s->events_dropped) { - virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0); - } + virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, fn); + return 0; } -static void virtio_scsi_iothread_handle_cmd(EventNotifier *notifier) +void virtio_scsi_dataplane_notify(VirtIODevice *vdev, VirtIOSCSIReq *req) { - VirtIOSCSIVring *vring = container_of(notifier, - VirtIOSCSIVring, host_notifier); - VirtIOSCSI *s = (VirtIOSCSI *)vring->parent; - VirtIOSCSIReq *req, *next; - QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs); - - event_notifier_test_and_clear(notifier); - while ((req = virtio_scsi_pop_req_vring(s, vring))) { - if (virtio_scsi_handle_cmd_req_prepare(s, req)) { - QTAILQ_INSERT_TAIL(&reqs, req, next); - } - } - - QTAILQ_FOREACH_SAFE(req, &reqs, next, next) { - virtio_scsi_handle_cmd_req_submit(s, req); + if (virtio_should_notify(vdev, req->vq)) { + event_notifier_set(virtio_queue_get_guest_notifier(req->vq)); } } @@ -161,43 +98,10 @@ static void virtio_scsi_clear_aio(VirtIOSCSI *s) VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); int i; - if (s->ctrl_vring) { - aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, NULL); - } - if (s->event_vring) { - aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, NULL); - } - if (s->cmd_vrings) { - for (i = 0; i < vs->conf.num_queues && s->cmd_vrings[i]; i++) { - aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, NULL); - } - } -} - -static void virtio_scsi_vring_teardown(VirtIOSCSI *s) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(s); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - int i; - - if (s->ctrl_vring) { - vring_teardown(&s->ctrl_vring->vring, vdev, 0); - g_slice_free(VirtIOSCSIVring, s->ctrl_vring); - s->ctrl_vring = NULL; - } - if (s->event_vring) { - vring_teardown(&s->event_vring->vring, vdev, 1); - g_slice_free(VirtIOSCSIVring, s->event_vring); - s->event_vring = NULL; - } - if (s->cmd_vrings) { - for (i = 0; i < vs->conf.num_queues && s->cmd_vrings[i]; i++) { - vring_teardown(&s->cmd_vrings[i]->vring, vdev, 2 + i); - g_slice_free(VirtIOSCSIVring, s->cmd_vrings[i]); - s->cmd_vrings[i] = NULL; - } - free(s->cmd_vrings); - s->cmd_vrings = NULL; + virtio_queue_aio_set_host_notifier_handler(vs->ctrl_vq, s->ctx, NULL); + virtio_queue_aio_set_host_notifier_handler(vs->event_vq, s->ctx, NULL); + for (i = 0; i < vs->conf.num_queues; i++) { + virtio_queue_aio_set_host_notifier_handler(vs->cmd_vqs[i], s->ctx, NULL); } } @@ -224,30 +128,24 @@ void virtio_scsi_dataplane_start(VirtIOSCSI *s) if (rc != 0) { fprintf(stderr, "virtio-scsi: Failed to set guest notifiers (%d), " "ensure -enable-kvm is set\n", rc); - s->dataplane_fenced = true; goto fail_guest_notifiers; } aio_context_acquire(s->ctx); - s->ctrl_vring = virtio_scsi_vring_init(s, vs->ctrl_vq, - virtio_scsi_iothread_handle_ctrl, - 0); - if (!s->ctrl_vring) { + rc = virtio_scsi_vring_init(s, vs->ctrl_vq, 0, + virtio_scsi_data_plane_handle_ctrl); + if (rc) { goto fail_vrings; } - s->event_vring = virtio_scsi_vring_init(s, vs->event_vq, - virtio_scsi_iothread_handle_event, - 1); - if (!s->event_vring) { + rc = virtio_scsi_vring_init(s, vs->event_vq, 1, + virtio_scsi_data_plane_handle_event); + if (rc) { goto fail_vrings; } - s->cmd_vrings = g_new(VirtIOSCSIVring *, vs->conf.num_queues); for (i = 0; i < vs->conf.num_queues; i++) { - s->cmd_vrings[i] = - virtio_scsi_vring_init(s, vs->cmd_vqs[i], - virtio_scsi_iothread_handle_cmd, - i + 2); - if (!s->cmd_vrings[i]) { + rc = virtio_scsi_vring_init(s, vs->cmd_vqs[i], i + 2, + virtio_scsi_data_plane_handle_cmd); + if (rc) { goto fail_vrings; } } @@ -260,13 +158,14 @@ void virtio_scsi_dataplane_start(VirtIOSCSI *s) fail_vrings: virtio_scsi_clear_aio(s); aio_context_release(s->ctx); - virtio_scsi_vring_teardown(s); for (i = 0; i < vs->conf.num_queues + 2; i++) { k->set_host_notifier(qbus->parent, i, false); } k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); fail_guest_notifiers: + s->dataplane_fenced = true; s->dataplane_starting = false; + s->dataplane_started = true; } /* Context: QEMU global mutex held */ @@ -277,12 +176,14 @@ void virtio_scsi_dataplane_stop(VirtIOSCSI *s) VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); int i; + if (!s->dataplane_started || s->dataplane_stopping) { + return; + } + /* Better luck next time. */ if (s->dataplane_fenced) { s->dataplane_fenced = false; - return; - } - if (!s->dataplane_started || s->dataplane_stopping) { + s->dataplane_started = false; return; } s->dataplane_stopping = true; @@ -290,21 +191,12 @@ void virtio_scsi_dataplane_stop(VirtIOSCSI *s) aio_context_acquire(s->ctx); - aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, NULL); - aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, NULL); - for (i = 0; i < vs->conf.num_queues; i++) { - aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, NULL); - } + virtio_scsi_clear_aio(s); blk_drain_all(); /* ensure there are no in-flight requests */ aio_context_release(s->ctx); - /* Sync vring state back to virtqueue so that non-dataplane request - * processing can continue when we disable the host notifier below. - */ - virtio_scsi_vring_teardown(s); - for (i = 0; i < vs->conf.num_queues + 2; i++) { k->set_host_notifier(qbus->parent, i, false); } diff --git a/qemu/hw/scsi/virtio-scsi.c b/qemu/hw/scsi/virtio-scsi.c index 811c3da8b..30415c6a9 100644 --- a/qemu/hw/scsi/virtio-scsi.c +++ b/qemu/hw/scsi/virtio-scsi.c @@ -13,6 +13,8 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "standard-headers/linux/virtio_ids.h" #include "hw/virtio/virtio-scsi.h" #include "qemu/error-report.h" @@ -22,7 +24,6 @@ #include #include #include "hw/virtio/virtio-access.h" -#include "migration/migration.h" static inline int virtio_scsi_get_lun(uint8_t *lun) { @@ -40,29 +41,23 @@ static inline SCSIDevice *virtio_scsi_device_find(VirtIOSCSI *s, uint8_t *lun) return scsi_device_find(&s->bus, 0, lun[1], virtio_scsi_get_lun(lun)); } -VirtIOSCSIReq *virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq) +void virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq, VirtIOSCSIReq *req) { - VirtIOSCSIReq *req; - VirtIOSCSICommon *vs = (VirtIOSCSICommon *)s; - const size_t zero_skip = offsetof(VirtIOSCSIReq, elem) - + sizeof(VirtQueueElement); + const size_t zero_skip = + offsetof(VirtIOSCSIReq, resp_iov) + sizeof(req->resp_iov); - req = g_slice_alloc(sizeof(*req) + vs->cdb_size); req->vq = vq; req->dev = s; qemu_sglist_init(&req->qsgl, DEVICE(s), 8, &address_space_memory); qemu_iovec_init(&req->resp_iov, 1); memset((uint8_t *)req + zero_skip, 0, sizeof(*req) - zero_skip); - return req; } void virtio_scsi_free_req(VirtIOSCSIReq *req) { - VirtIOSCSICommon *vs = (VirtIOSCSICommon *)req->dev; - qemu_iovec_destroy(&req->resp_iov); qemu_sglist_destroy(&req->qsgl); - g_slice_free1(sizeof(*req) + vs->cdb_size, req); + g_free(req); } static void virtio_scsi_complete_req(VirtIOSCSIReq *req) @@ -72,11 +67,10 @@ static void virtio_scsi_complete_req(VirtIOSCSIReq *req) VirtIODevice *vdev = VIRTIO_DEVICE(s); qemu_iovec_from_buf(&req->resp_iov, 0, &req->resp, req->resp_size); - if (req->vring) { - assert(req->vq == NULL); - virtio_scsi_vring_push_notify(req); + virtqueue_push(vq, &req->elem, req->qsgl.size + req->resp_iov.size); + if (s->dataplane_started && !s->dataplane_fenced) { + virtio_scsi_dataplane_notify(vdev, req); } else { - virtqueue_push(vq, &req->elem, req->qsgl.size + req->resp_iov.size); virtio_notify(vdev, vq); } @@ -145,7 +139,7 @@ static int virtio_scsi_parse_req(VirtIOSCSIReq *req, * * TODO: always disable this workaround for virtio 1.0 devices. */ - if (!virtio_has_feature(vdev, VIRTIO_F_ANY_LAYOUT)) { + if (!virtio_vdev_has_feature(vdev, VIRTIO_F_ANY_LAYOUT)) { if (req->elem.out_num) { req_size = req->elem.out_sg[0].iov_len; } @@ -176,11 +170,14 @@ static int virtio_scsi_parse_req(VirtIOSCSIReq *req, static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq) { - VirtIOSCSIReq *req = virtio_scsi_init_req(s, vq); - if (!virtqueue_pop(vq, &req->elem)) { - virtio_scsi_free_req(req); + VirtIOSCSICommon *vs = (VirtIOSCSICommon *)s; + VirtIOSCSIReq *req; + + req = virtqueue_pop(vq, sizeof(VirtIOSCSIReq) + vs->cdb_size); + if (!req) { return NULL; } + virtio_scsi_init_req(s, vq, req); return req; } @@ -192,7 +189,7 @@ static void virtio_scsi_save_request(QEMUFile *f, SCSIRequest *sreq) assert(n < vs->conf.num_queues); qemu_put_be32s(f, &n); - qemu_put_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem)); + qemu_put_virtqueue_element(f, &req->elem); } static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq) @@ -205,17 +202,8 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq) qemu_get_be32s(f, &n); assert(n < vs->conf.num_queues); - req = virtio_scsi_init_req(s, vs->cmd_vqs[n]); - qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem)); - /* TODO: add a way for SCSIBusInfo's load_request to fail, - * and fail migration instead of asserting here. - * When we do, we might be able to re-enable NDEBUG below. - */ -#ifdef NDEBUG -#error building with NDEBUG is not supported -#endif - assert(req->elem.in_num <= ARRAY_SIZE(req->elem.in_sg)); - assert(req->elem.out_num <= ARRAY_SIZE(req->elem.out_sg)); + req = qemu_get_virtqueue_element(f, sizeof(VirtIOSCSIReq) + vs->cdb_size); + virtio_scsi_init_req(s, vs->cmd_vqs[n], req); if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size, sizeof(VirtIOSCSICmdResp) + vs->sense_size) < 0) { @@ -245,7 +233,7 @@ static void virtio_scsi_cancel_notify(Notifier *notifier, void *data) if (--n->tmf_req->remaining == 0) { virtio_scsi_complete_req(n->tmf_req); } - g_slice_free(VirtIOSCSICancelNotifier, n); + g_free(n); } /* Return 0 if the request is ready to be completed and return to guest; @@ -259,7 +247,7 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) int target; int ret = 0; - if (s->dataplane_started) { + if (s->dataplane_started && d) { assert(blk_get_aio_context(d->conf.blk) == s->ctx); } /* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */ @@ -296,7 +284,7 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) VirtIOSCSICancelNotifier *notifier; req->remaining = 1; - notifier = g_slice_new(VirtIOSCSICancelNotifier); + notifier = g_new(VirtIOSCSICancelNotifier, 1); notifier->tmf_req = req; notifier->notifier.notify = virtio_scsi_cancel_notify; scsi_req_cancel_async(r, ¬ifier->notifier); @@ -345,7 +333,7 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) VirtIOSCSICancelNotifier *notifier; req->remaining++; - notifier = g_slice_new(VirtIOSCSICancelNotifier); + notifier = g_new(VirtIOSCSICancelNotifier, 1); notifier->notifier.notify = virtio_scsi_cancel_notify; notifier->tmf_req = req; scsi_req_cancel_async(r, ¬ifier->notifier); @@ -361,7 +349,7 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) target = req->req.tmf.lun[1]; s->resetting++; QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - d = DO_UPCAST(SCSIDevice, qdev, kid->child); + d = SCSI_DEVICE(kid->child); if (d->channel == 0 && d->id == target) { qdev_reset_all(&d->qdev); } @@ -386,7 +374,7 @@ fail: return ret; } -void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req) +static void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req) { VirtIODevice *vdev = (VirtIODevice *)s; uint32_t type; @@ -424,20 +412,28 @@ void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req) } } -static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) +void virtio_scsi_handle_ctrl_vq(VirtIOSCSI *s, VirtQueue *vq) { - VirtIOSCSI *s = (VirtIOSCSI *)vdev; VirtIOSCSIReq *req; - if (s->ctx && !s->dataplane_disabled) { - virtio_scsi_dataplane_start(s); - return; - } while ((req = virtio_scsi_pop_req(s, vq))) { virtio_scsi_handle_ctrl_req(s, req); } } +static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOSCSI *s = (VirtIOSCSI *)vdev; + + if (s->ctx) { + virtio_scsi_dataplane_start(s); + if (!s->dataplane_fenced) { + return; + } + } + virtio_scsi_handle_ctrl_vq(s, vq); +} + static void virtio_scsi_complete_cmd_req(VirtIOSCSIReq *req) { /* Sense data is not in req->resp and is copied separately @@ -520,7 +516,7 @@ static void virtio_scsi_fail_cmd_req(VirtIOSCSIReq *req) virtio_scsi_complete_cmd_req(req); } -bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) +static bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) { VirtIOSCSICommon *vs = &s->parent_obj; SCSIDevice *d; @@ -562,7 +558,7 @@ bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) return true; } -void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req) +static void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req) { SCSIRequest *sreq = req->sreq; if (scsi_req_enqueue(sreq)) { @@ -572,17 +568,11 @@ void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req) scsi_req_unref(sreq); } -static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq) +void virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq) { - /* use non-QOM casts in the data path */ - VirtIOSCSI *s = (VirtIOSCSI *)vdev; VirtIOSCSIReq *req, *next; QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs); - if (s->ctx && !s->dataplane_disabled) { - virtio_scsi_dataplane_start(s); - return; - } while ((req = virtio_scsi_pop_req(s, vq))) { if (virtio_scsi_handle_cmd_req_prepare(s, req)) { QTAILQ_INSERT_TAIL(&reqs, req, next); @@ -594,6 +584,20 @@ static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq) } } +static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq) +{ + /* use non-QOM casts in the data path */ + VirtIOSCSI *s = (VirtIOSCSI *)vdev; + + if (s->ctx) { + virtio_scsi_dataplane_start(s); + if (!s->dataplane_fenced) { + return; + } + } + virtio_scsi_handle_cmd_vq(s, vq); +} + static void virtio_scsi_get_config(VirtIODevice *vdev, uint8_t *config) { @@ -662,6 +666,11 @@ static void virtio_scsi_reset(VirtIODevice *vdev) static void virtio_scsi_save(QEMUFile *f, void *opaque) { VirtIODevice *vdev = VIRTIO_DEVICE(opaque); + VirtIOSCSI *s = VIRTIO_SCSI(vdev); + + if (s->dataplane_started) { + virtio_scsi_dataplane_stop(s); + } virtio_save(vdev, f); } @@ -694,11 +703,7 @@ void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, aio_context_acquire(s->ctx); } - if (s->dataplane_started) { - req = virtio_scsi_pop_req_vring(s, s->event_vring); - } else { - req = virtio_scsi_pop_req(s, vs->event_vq); - } + req = virtio_scsi_pop_req(s, vs->event_vq); if (!req) { s->events_dropped = true; goto out; @@ -736,17 +741,24 @@ out: } } +void virtio_scsi_handle_event_vq(VirtIOSCSI *s, VirtQueue *vq) +{ + if (s->events_dropped) { + virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0); + } +} + static void virtio_scsi_handle_event(VirtIODevice *vdev, VirtQueue *vq) { VirtIOSCSI *s = VIRTIO_SCSI(vdev); - if (s->ctx && !s->dataplane_disabled) { + if (s->ctx) { virtio_scsi_dataplane_start(s); - return; - } - if (s->events_dropped) { - virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0); + if (!s->dataplane_fenced) { + return; + } } + virtio_scsi_handle_event_vq(s, vq); } static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) @@ -754,13 +766,29 @@ static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); VirtIODevice *vdev = VIRTIO_DEVICE(s); - if (virtio_has_feature(vdev, VIRTIO_SCSI_F_CHANGE) && + if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_CHANGE) && dev->type != TYPE_ROM) { virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_PARAM_CHANGE, sense.asc | (sense.ascq << 8)); } } +static void virtio_scsi_blk_insert_notifier(Notifier *n, void *data) +{ + VirtIOSCSIBlkChangeNotifier *cn = DO_UPCAST(VirtIOSCSIBlkChangeNotifier, + n, n); + assert(cn->sd->conf.blk == data); + blk_op_block_all(cn->sd->conf.blk, cn->s->blocker); +} + +static void virtio_scsi_blk_remove_notifier(Notifier *n, void *data) +{ + VirtIOSCSIBlkChangeNotifier *cn = DO_UPCAST(VirtIOSCSIBlkChangeNotifier, + n, n); + assert(cn->sd->conf.blk == data); + blk_op_unblock_all(cn->sd->conf.blk, cn->s->blocker); +} + static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -768,7 +796,9 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, VirtIOSCSI *s = VIRTIO_SCSI(vdev); SCSIDevice *sd = SCSI_DEVICE(dev); - if (s->ctx && !s->dataplane_disabled) { + if (s->ctx && !s->dataplane_fenced) { + VirtIOSCSIBlkChangeNotifier *insert_notifier, *remove_notifier; + if (blk_op_is_blocked(sd->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { return; } @@ -776,9 +806,23 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, aio_context_acquire(s->ctx); blk_set_aio_context(sd->conf.blk, s->ctx); aio_context_release(s->ctx); + + insert_notifier = g_new0(VirtIOSCSIBlkChangeNotifier, 1); + insert_notifier->n.notify = virtio_scsi_blk_insert_notifier; + insert_notifier->s = s; + insert_notifier->sd = sd; + blk_add_insert_bs_notifier(sd->conf.blk, &insert_notifier->n); + QTAILQ_INSERT_TAIL(&s->insert_notifiers, insert_notifier, next); + + remove_notifier = g_new0(VirtIOSCSIBlkChangeNotifier, 1); + remove_notifier->n.notify = virtio_scsi_blk_remove_notifier; + remove_notifier->s = s; + remove_notifier->sd = sd; + blk_add_remove_bs_notifier(sd->conf.blk, &remove_notifier->n); + QTAILQ_INSERT_TAIL(&s->remove_notifiers, remove_notifier, next); } - if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { virtio_scsi_push_event(s, sd, VIRTIO_SCSI_T_TRANSPORT_RESET, VIRTIO_SCSI_EVT_RESET_RESCAN); @@ -791,8 +835,9 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev, VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev); VirtIOSCSI *s = VIRTIO_SCSI(vdev); SCSIDevice *sd = SCSI_DEVICE(dev); + VirtIOSCSIBlkChangeNotifier *insert_notifier, *remove_notifier; - if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { virtio_scsi_push_event(s, sd, VIRTIO_SCSI_T_TRANSPORT_RESET, VIRTIO_SCSI_EVT_RESET_REMOVED); @@ -801,6 +846,25 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev, if (s->ctx) { blk_op_unblock_all(sd->conf.blk, s->blocker); } + + QTAILQ_FOREACH(insert_notifier, &s->insert_notifiers, next) { + if (insert_notifier->sd == sd) { + notifier_remove(&insert_notifier->n); + QTAILQ_REMOVE(&s->insert_notifiers, insert_notifier, next); + g_free(insert_notifier); + break; + } + } + + QTAILQ_FOREACH(remove_notifier, &s->remove_notifiers, next) { + if (remove_notifier->sd == sd) { + notifier_remove(&remove_notifier->n); + QTAILQ_REMOVE(&s->remove_notifiers, remove_notifier, next); + g_free(remove_notifier); + break; + } + } + qdev_simple_device_unplug_cb(hotplug_dev, dev, errp); } @@ -856,31 +920,6 @@ void virtio_scsi_common_realize(DeviceState *dev, Error **errp, } } -/* Disable dataplane thread during live migration since it does not - * update the dirty memory bitmap yet. - */ -static void virtio_scsi_migration_state_changed(Notifier *notifier, void *data) -{ - VirtIOSCSI *s = container_of(notifier, VirtIOSCSI, - migration_state_notifier); - MigrationState *mig = data; - - if (migration_in_setup(mig)) { - if (!s->dataplane_started) { - return; - } - virtio_scsi_dataplane_stop(s); - s->dataplane_disabled = true; - } else if (migration_has_finished(mig) || - migration_has_failed(mig)) { - if (s->dataplane_started) { - return; - } - blk_drain_all(); /* complete in-flight non-dataplane requests */ - s->dataplane_disabled = false; - } -} - static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -911,10 +950,11 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) register_savevm(dev, "virtio-scsi", virtio_scsi_id++, 1, virtio_scsi_save, virtio_scsi_load, s); - s->migration_state_notifier.notify = virtio_scsi_migration_state_changed; - add_migration_state_change_notifier(&s->migration_state_notifier); error_setg(&s->blocker, "block device is in use by data plane"); + + QTAILQ_INIT(&s->insert_notifiers); + QTAILQ_INIT(&s->remove_notifiers); } static void virtio_scsi_instance_init(Object *obj) @@ -943,8 +983,6 @@ static void virtio_scsi_device_unrealize(DeviceState *dev, Error **errp) error_free(s->blocker); unregister_savevm(dev, "virtio-scsi", s); - remove_migration_state_change_notifier(&s->migration_state_notifier); - virtio_scsi_common_unrealize(dev, errp); } diff --git a/qemu/hw/scsi/vmw_pvscsi.c b/qemu/hw/scsi/vmw_pvscsi.c index 9c71f31fe..e690b4ec0 100644 --- a/qemu/hw/scsi/vmw_pvscsi.c +++ b/qemu/hw/scsi/vmw_pvscsi.c @@ -25,6 +25,8 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/scsi/scsi.h" #include #include "hw/pci/msi.h" @@ -32,7 +34,6 @@ #include "trace.h" -#define PVSCSI_MSI_OFFSET (0x50) #define PVSCSI_USE_64BIT (true) #define PVSCSI_PER_VECTOR_MASK (false) @@ -49,9 +50,33 @@ (stl_le_pci_dma(&container_of(m, PVSCSIState, rings)->parent_obj, \ (m)->rs_pa + offsetof(struct PVSCSIRingsState, field), val)) +typedef struct PVSCSIClass { + PCIDeviceClass parent_class; + DeviceRealize parent_dc_realize; +} PVSCSIClass; + #define TYPE_PVSCSI "pvscsi" #define PVSCSI(obj) OBJECT_CHECK(PVSCSIState, (obj), TYPE_PVSCSI) +#define PVSCSI_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(PVSCSIClass, (klass), TYPE_PVSCSI) +#define PVSCSI_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PVSCSIClass, (obj), TYPE_PVSCSI) + +/* Compatability flags for migration */ +#define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT 0 +#define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION \ + (1 << PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT) +#define PVSCSI_COMPAT_DISABLE_PCIE_BIT 1 +#define PVSCSI_COMPAT_DISABLE_PCIE \ + (1 << PVSCSI_COMPAT_DISABLE_PCIE_BIT) + +#define PVSCSI_USE_OLD_PCI_CONFIGURATION(s) \ + ((s)->compat_flags & PVSCSI_COMPAT_OLD_PCI_CONFIGURATION) +#define PVSCSI_MSI_OFFSET(s) \ + (PVSCSI_USE_OLD_PCI_CONFIGURATION(s) ? 0x50 : 0x7c) +#define PVSCSI_EXP_EP_OFFSET (0x40) + typedef struct PVSCSIRingInfo { uint64_t rs_pa; uint32_t txr_len_mask; @@ -100,6 +125,8 @@ typedef struct { PVSCSIRingInfo rings; /* Data transfer rings manager */ uint32_t resetting; /* Reset in progress */ + + uint32_t compat_flags; } PVSCSIState; typedef struct PVSCSIRequest { @@ -1019,7 +1046,7 @@ pvscsi_init_msi(PVSCSIState *s) int res; PCIDevice *d = PCI_DEVICE(s); - res = msi_init(d, PVSCSI_MSI_OFFSET, PVSCSI_MSIX_NUM_VECTORS, + res = msi_init(d, PVSCSI_MSI_OFFSET(s), PVSCSI_MSIX_NUM_VECTORS, PVSCSI_USE_64BIT, PVSCSI_PER_VECTOR_MASK); if (res < 0) { trace_pvscsi_init_msi_fail(res); @@ -1069,9 +1096,16 @@ pvscsi_init(PCIDevice *pci_dev) trace_pvscsi_state("init"); - /* PCI subsystem ID */ - pci_dev->config[PCI_SUBSYSTEM_ID] = 0x00; - pci_dev->config[PCI_SUBSYSTEM_ID + 1] = 0x10; + /* PCI subsystem ID, subsystem vendor ID, revision */ + if (PVSCSI_USE_OLD_PCI_CONFIGURATION(s)) { + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, 0x1000); + } else { + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID, + PCI_VENDOR_ID_VMWARE); + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, + PCI_DEVICE_ID_VMWARE_PVSCSI); + pci_config_set_revision(pci_dev->config, 0x2); + } /* PCI latency timer = 255 */ pci_dev->config[PCI_LATENCY_TIMER] = 0xff; @@ -1085,6 +1119,10 @@ pvscsi_init(PCIDevice *pci_dev) pvscsi_init_msi(s); + if (pci_is_express(pci_dev) && pci_bus_is_express(pci_dev->bus)) { + pcie_endpoint_cap_init(pci_dev, PVSCSI_EXP_EP_OFFSET); + } + s->completion_worker = qemu_bh_new(pvscsi_process_completion_queue, s); if (!s->completion_worker) { pvscsi_cleanup_msi(s); @@ -1139,6 +1177,27 @@ pvscsi_post_load(void *opaque, int version_id) return 0; } +static bool pvscsi_vmstate_need_pcie_device(void *opaque) +{ + PVSCSIState *s = PVSCSI(opaque); + + return !(s->compat_flags & PVSCSI_COMPAT_DISABLE_PCIE); +} + +static bool pvscsi_vmstate_test_pci_device(void *opaque, int version_id) +{ + return !pvscsi_vmstate_need_pcie_device(opaque); +} + +static const VMStateDescription vmstate_pvscsi_pcie_device = { + .name = "pvscsi/pcie", + .needed = pvscsi_vmstate_need_pcie_device, + .fields = (VMStateField[]) { + VMSTATE_PCIE_DEVICE(parent_obj, PVSCSIState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_pvscsi = { .name = "pvscsi", .version_id = 0, @@ -1146,7 +1205,9 @@ static const VMStateDescription vmstate_pvscsi = { .pre_save = pvscsi_pre_save, .post_load = pvscsi_post_load, .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, PVSCSIState), + VMSTATE_STRUCT_TEST(parent_obj, PVSCSIState, + pvscsi_vmstate_test_pci_device, 0, + vmstate_pci_device, PCIDevice), VMSTATE_UINT8(msi_used, PVSCSIState), VMSTATE_UINT32(resetting, PVSCSIState), VMSTATE_UINT64(reg_interrupt_status, PVSCSIState), @@ -1171,18 +1232,40 @@ static const VMStateDescription vmstate_pvscsi = { VMSTATE_UINT64(rings.filled_cmp_ptr, PVSCSIState), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription*[]) { + &vmstate_pvscsi_pcie_device, + NULL } }; static Property pvscsi_properties[] = { DEFINE_PROP_UINT8("use_msg", PVSCSIState, use_msg, 1), + DEFINE_PROP_BIT("x-old-pci-configuration", PVSCSIState, compat_flags, + PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT, false), + DEFINE_PROP_BIT("x-disable-pcie", PVSCSIState, compat_flags, + PVSCSI_COMPAT_DISABLE_PCIE_BIT, false), DEFINE_PROP_END_OF_LIST(), }; +static void pvscsi_realize(DeviceState *qdev, Error **errp) +{ + PVSCSIClass *pvs_c = PVSCSI_DEVICE_GET_CLASS(qdev); + PCIDevice *pci_dev = PCI_DEVICE(qdev); + PVSCSIState *s = PVSCSI(qdev); + + if (!(s->compat_flags & PVSCSI_COMPAT_DISABLE_PCIE)) { + pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; + } + + pvs_c->parent_dc_realize(qdev, errp); +} + static void pvscsi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + PVSCSIClass *pvs_k = PVSCSI_DEVICE_CLASS(klass); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); k->init = pvscsi_init; @@ -1191,6 +1274,8 @@ static void pvscsi_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_VMWARE_PVSCSI; k->class_id = PCI_CLASS_STORAGE_SCSI; k->subsystem_id = 0x1000; + pvs_k->parent_dc_realize = dc->realize; + dc->realize = pvscsi_realize; dc->reset = pvscsi_reset; dc->vmsd = &vmstate_pvscsi; dc->props = pvscsi_properties; @@ -1202,6 +1287,7 @@ static void pvscsi_class_init(ObjectClass *klass, void *data) static const TypeInfo pvscsi_info = { .name = TYPE_PVSCSI, .parent = TYPE_PCI_DEVICE, + .class_size = sizeof(PVSCSIClass), .instance_size = sizeof(PVSCSIState), .class_init = pvscsi_class_init, .interfaces = (InterfaceInfo[]) { diff --git a/qemu/hw/sd/Makefile.objs b/qemu/hw/sd/Makefile.objs index f1aed83d9..31c83308f 100644 --- a/qemu/hw/sd/Makefile.objs +++ b/qemu/hw/sd/Makefile.objs @@ -1,6 +1,6 @@ common-obj-$(CONFIG_PL181) += pl181.o common-obj-$(CONFIG_SSI_SD) += ssi-sd.o -common-obj-$(CONFIG_SD) += sd.o +common-obj-$(CONFIG_SD) += sd.o core.o common-obj-$(CONFIG_SDHCI) += sdhci.o obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o diff --git a/qemu/hw/sd/core.c b/qemu/hw/sd/core.c new file mode 100644 index 000000000..14c2bdf27 --- /dev/null +++ b/qemu/hw/sd/core.c @@ -0,0 +1,146 @@ +/* + * SD card bus interface code. + * + * Copyright (c) 2015 Linaro Limited + * + * Author: + * Peter Maydell + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "hw/qdev-core.h" +#include "sysemu/block-backend.h" +#include "hw/sd/sd.h" + +static SDState *get_card(SDBus *sdbus) +{ + /* We only ever have one child on the bus so just return it */ + BusChild *kid = QTAILQ_FIRST(&sdbus->qbus.children); + + if (!kid) { + return NULL; + } + return SD_CARD(kid->child); +} + +int sdbus_do_command(SDBus *sdbus, SDRequest *req, uint8_t *response) +{ + SDState *card = get_card(sdbus); + + if (card) { + SDCardClass *sc = SD_CARD_GET_CLASS(card); + + return sc->do_command(card, req, response); + } + + return 0; +} + +void sdbus_write_data(SDBus *sdbus, uint8_t value) +{ + SDState *card = get_card(sdbus); + + if (card) { + SDCardClass *sc = SD_CARD_GET_CLASS(card); + + sc->write_data(card, value); + } +} + +uint8_t sdbus_read_data(SDBus *sdbus) +{ + SDState *card = get_card(sdbus); + + if (card) { + SDCardClass *sc = SD_CARD_GET_CLASS(card); + + return sc->read_data(card); + } + + return 0; +} + +bool sdbus_data_ready(SDBus *sdbus) +{ + SDState *card = get_card(sdbus); + + if (card) { + SDCardClass *sc = SD_CARD_GET_CLASS(card); + + return sc->data_ready(card); + } + + return false; +} + +bool sdbus_get_inserted(SDBus *sdbus) +{ + SDState *card = get_card(sdbus); + + if (card) { + SDCardClass *sc = SD_CARD_GET_CLASS(card); + + return sc->get_inserted(card); + } + + return false; +} + +bool sdbus_get_readonly(SDBus *sdbus) +{ + SDState *card = get_card(sdbus); + + if (card) { + SDCardClass *sc = SD_CARD_GET_CLASS(card); + + return sc->get_readonly(card); + } + + return false; +} + +void sdbus_set_inserted(SDBus *sdbus, bool inserted) +{ + SDBusClass *sbc = SD_BUS_GET_CLASS(sdbus); + BusState *qbus = BUS(sdbus); + + if (sbc->set_inserted) { + sbc->set_inserted(qbus->parent, inserted); + } +} + +void sdbus_set_readonly(SDBus *sdbus, bool readonly) +{ + SDBusClass *sbc = SD_BUS_GET_CLASS(sdbus); + BusState *qbus = BUS(sdbus); + + if (sbc->set_readonly) { + sbc->set_readonly(qbus->parent, readonly); + } +} + +static const TypeInfo sd_bus_info = { + .name = TYPE_SD_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(SDBus), + .class_size = sizeof(SDBusClass), +}; + +static void sd_bus_register_types(void) +{ + type_register_static(&sd_bus_info); +} + +type_init(sd_bus_register_types) diff --git a/qemu/hw/sd/milkymist-memcard.c b/qemu/hw/sd/milkymist-memcard.c index 2209ef1d5..c04ff02fa 100644 --- a/qemu/hw/sd/milkymist-memcard.c +++ b/qemu/hw/sd/milkymist-memcard.c @@ -21,6 +21,7 @@ * http://www.milkymist.org/socdoc/memcard.pdf */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "sysemu/sysemu.h" @@ -28,7 +29,7 @@ #include "qemu/error-report.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" -#include "hw/sd.h" +#include "hw/sd/sd.h" enum { ENABLE_CMD_TX = (1<<0), diff --git a/qemu/hw/sd/omap_mmc.c b/qemu/hw/sd/omap_mmc.c index d072deca1..e934cd365 100644 --- a/qemu/hw/sd/omap_mmc.c +++ b/qemu/hw/sd/omap_mmc.c @@ -16,9 +16,10 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/arm/omap.h" -#include "hw/sd.h" +#include "hw/sd/sd.h" struct omap_mmc_s { qemu_irq irq; @@ -578,8 +579,7 @@ struct omap_mmc_s *omap_mmc_init(hwaddr base, BlockBackend *blk, qemu_irq irq, qemu_irq dma[], omap_clk clk) { - struct omap_mmc_s *s = (struct omap_mmc_s *) - g_malloc0(sizeof(struct omap_mmc_s)); + struct omap_mmc_s *s = g_new0(struct omap_mmc_s, 1); s->irq = irq; s->dma = dma; @@ -605,8 +605,7 @@ struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta, BlockBackend *blk, qemu_irq irq, qemu_irq dma[], omap_clk fclk, omap_clk iclk) { - struct omap_mmc_s *s = (struct omap_mmc_s *) - g_malloc0(sizeof(struct omap_mmc_s)); + struct omap_mmc_s *s = g_new0(struct omap_mmc_s, 1); s->irq = irq; s->dma = dma; diff --git a/qemu/hw/sd/pl181.c b/qemu/hw/sd/pl181.c index 11fcd479d..e87abb205 100644 --- a/qemu/hw/sd/pl181.c +++ b/qemu/hw/sd/pl181.c @@ -7,10 +7,11 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/sysbus.h" -#include "hw/sd.h" +#include "hw/sd/sd.h" //#define DEBUG_PL181 1 @@ -46,7 +47,7 @@ typedef struct PL181State { int32_t fifo_pos; int32_t fifo_len; /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives - while it is reading the FIFO. We hack around this be defering + while it is reading the FIFO. We hack around this by deferring subsequent transfers until after the driver polls the status word. http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1 */ diff --git a/qemu/hw/sd/pxa2xx_mmci.c b/qemu/hw/sd/pxa2xx_mmci.c index d1fe6d58e..3deccf02c 100644 --- a/qemu/hw/sd/pxa2xx_mmci.c +++ b/qemu/hw/sd/pxa2xx_mmci.c @@ -10,18 +10,34 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" +#include "hw/sysbus.h" #include "hw/arm/pxa.h" -#include "hw/sd.h" +#include "hw/sd/sd.h" #include "hw/qdev.h" +#include "hw/qdev-properties.h" +#include "qemu/error-report.h" + +#define TYPE_PXA2XX_MMCI "pxa2xx-mmci" +#define PXA2XX_MMCI(obj) OBJECT_CHECK(PXA2xxMMCIState, (obj), TYPE_PXA2XX_MMCI) + +#define TYPE_PXA2XX_MMCI_BUS "pxa2xx-mmci-bus" +#define PXA2XX_MMCI_BUS(obj) OBJECT_CHECK(SDBus, (obj), TYPE_PXA2XX_MMCI_BUS) struct PXA2xxMMCIState { + SysBusDevice parent_obj; + MemoryRegion iomem; qemu_irq irq; qemu_irq rx_dma; qemu_irq tx_dma; + qemu_irq inserted; + qemu_irq readonly; - SDState *card; + BlockBackend *blk; + SDBus sdbus; uint32_t status; uint32_t clkrt; @@ -29,25 +45,70 @@ struct PXA2xxMMCIState { uint32_t cmdat; uint32_t resp_tout; uint32_t read_tout; - int blklen; - int numblk; + int32_t blklen; + int32_t numblk; uint32_t intmask; uint32_t intreq; - int cmd; + int32_t cmd; uint32_t arg; - int active; - int bytesleft; + int32_t active; + int32_t bytesleft; uint8_t tx_fifo[64]; - int tx_start; - int tx_len; + uint32_t tx_start; + uint32_t tx_len; uint8_t rx_fifo[32]; - int rx_start; - int rx_len; + uint32_t rx_start; + uint32_t rx_len; uint16_t resp_fifo[9]; - int resp_len; + uint32_t resp_len; - int cmdreq; + int32_t cmdreq; +}; + +static bool pxa2xx_mmci_vmstate_validate(void *opaque, int version_id) +{ + PXA2xxMMCIState *s = opaque; + + return s->tx_start < ARRAY_SIZE(s->tx_fifo) + && s->rx_start < ARRAY_SIZE(s->rx_fifo) + && s->tx_len <= ARRAY_SIZE(s->tx_fifo) + && s->rx_len <= ARRAY_SIZE(s->rx_fifo) + && s->resp_len <= ARRAY_SIZE(s->resp_fifo); +} + + +static const VMStateDescription vmstate_pxa2xx_mmci = { + .name = "pxa2xx-mmci", + .version_id = 2, + .minimum_version_id = 2, + .fields = (VMStateField[]) { + VMSTATE_UINT32(status, PXA2xxMMCIState), + VMSTATE_UINT32(clkrt, PXA2xxMMCIState), + VMSTATE_UINT32(spi, PXA2xxMMCIState), + VMSTATE_UINT32(cmdat, PXA2xxMMCIState), + VMSTATE_UINT32(resp_tout, PXA2xxMMCIState), + VMSTATE_UINT32(read_tout, PXA2xxMMCIState), + VMSTATE_INT32(blklen, PXA2xxMMCIState), + VMSTATE_INT32(numblk, PXA2xxMMCIState), + VMSTATE_UINT32(intmask, PXA2xxMMCIState), + VMSTATE_UINT32(intreq, PXA2xxMMCIState), + VMSTATE_INT32(cmd, PXA2xxMMCIState), + VMSTATE_UINT32(arg, PXA2xxMMCIState), + VMSTATE_INT32(cmdreq, PXA2xxMMCIState), + VMSTATE_INT32(active, PXA2xxMMCIState), + VMSTATE_INT32(bytesleft, PXA2xxMMCIState), + VMSTATE_UINT32(tx_start, PXA2xxMMCIState), + VMSTATE_UINT32(tx_len, PXA2xxMMCIState), + VMSTATE_UINT32(rx_start, PXA2xxMMCIState), + VMSTATE_UINT32(rx_len, PXA2xxMMCIState), + VMSTATE_UINT32(resp_len, PXA2xxMMCIState), + VMSTATE_VALIDATE("fifo size incorrect", pxa2xx_mmci_vmstate_validate), + VMSTATE_UINT8_ARRAY(tx_fifo, PXA2xxMMCIState, 64), + VMSTATE_UINT8_ARRAY(rx_fifo, PXA2xxMMCIState, 32), + VMSTATE_UINT16_ARRAY(resp_fifo, PXA2xxMMCIState, 9), + VMSTATE_END_OF_LIST() + } }; #define MMC_STRPCL 0x00 /* MMC Clock Start/Stop register */ @@ -121,7 +182,7 @@ static void pxa2xx_mmci_fifo_update(PXA2xxMMCIState *s) if (s->cmdat & CMDAT_WR_RD) { while (s->bytesleft && s->tx_len) { - sd_write_data(s->card, s->tx_fifo[s->tx_start ++]); + sdbus_write_data(&s->sdbus, s->tx_fifo[s->tx_start++]); s->tx_start &= 0x1f; s->tx_len --; s->bytesleft --; @@ -131,7 +192,7 @@ static void pxa2xx_mmci_fifo_update(PXA2xxMMCIState *s) } else while (s->bytesleft && s->rx_len < 32) { s->rx_fifo[(s->rx_start + (s->rx_len ++)) & 0x1f] = - sd_read_data(s->card); + sdbus_read_data(&s->sdbus); s->bytesleft --; s->intreq |= INT_RXFIFO_REQ; } @@ -165,7 +226,7 @@ static void pxa2xx_mmci_wakequeues(PXA2xxMMCIState *s) request.arg = s->arg; request.crc = 0; /* FIXME */ - rsplen = sd_do_command(s->card, &request, response); + rsplen = sdbus_do_command(&s->sdbus, &request, response); s->intreq |= INT_END_CMD; memset(s->resp_fifo, 0, sizeof(s->resp_fifo)); @@ -391,114 +452,147 @@ static const MemoryRegionOps pxa2xx_mmci_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void pxa2xx_mmci_save(QEMUFile *f, void *opaque) +PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem, + hwaddr base, + BlockBackend *blk, qemu_irq irq, + qemu_irq rx_dma, qemu_irq tx_dma) { - PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque; - int i; - - qemu_put_be32s(f, &s->status); - qemu_put_be32s(f, &s->clkrt); - qemu_put_be32s(f, &s->spi); - qemu_put_be32s(f, &s->cmdat); - qemu_put_be32s(f, &s->resp_tout); - qemu_put_be32s(f, &s->read_tout); - qemu_put_be32(f, s->blklen); - qemu_put_be32(f, s->numblk); - qemu_put_be32s(f, &s->intmask); - qemu_put_be32s(f, &s->intreq); - qemu_put_be32(f, s->cmd); - qemu_put_be32s(f, &s->arg); - qemu_put_be32(f, s->cmdreq); - qemu_put_be32(f, s->active); - qemu_put_be32(f, s->bytesleft); - - qemu_put_byte(f, s->tx_len); - for (i = 0; i < s->tx_len; i ++) - qemu_put_byte(f, s->tx_fifo[(s->tx_start + i) & 63]); - - qemu_put_byte(f, s->rx_len); - for (i = 0; i < s->rx_len; i ++) - qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 31]); - - qemu_put_byte(f, s->resp_len); - for (i = s->resp_len; i < 9; i ++) - qemu_put_be16s(f, &s->resp_fifo[i]); + DeviceState *dev, *carddev; + SysBusDevice *sbd; + PXA2xxMMCIState *s; + Error *err = NULL; + + dev = qdev_create(NULL, TYPE_PXA2XX_MMCI); + s = PXA2XX_MMCI(dev); + sbd = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(sbd, 0, base); + sysbus_connect_irq(sbd, 0, irq); + qdev_connect_gpio_out_named(dev, "rx-dma", 0, rx_dma); + qdev_connect_gpio_out_named(dev, "tx-dma", 0, tx_dma); + + /* Create and plug in the sd card */ + carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &err); + if (err) { + error_report("failed to init SD card: %s", error_get_pretty(err)); + return NULL; + } + object_property_set_bool(OBJECT(carddev), true, "realized", &err); + if (err) { + error_report("failed to init SD card: %s", error_get_pretty(err)); + return NULL; + } + + return s; } -static int pxa2xx_mmci_load(QEMUFile *f, void *opaque, int version_id) +static void pxa2xx_mmci_set_inserted(DeviceState *dev, bool inserted) { - PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque; - int i; - - qemu_get_be32s(f, &s->status); - qemu_get_be32s(f, &s->clkrt); - qemu_get_be32s(f, &s->spi); - qemu_get_be32s(f, &s->cmdat); - qemu_get_be32s(f, &s->resp_tout); - qemu_get_be32s(f, &s->read_tout); - s->blklen = qemu_get_be32(f); - s->numblk = qemu_get_be32(f); - qemu_get_be32s(f, &s->intmask); - qemu_get_be32s(f, &s->intreq); - s->cmd = qemu_get_be32(f); - qemu_get_be32s(f, &s->arg); - s->cmdreq = qemu_get_be32(f); - s->active = qemu_get_be32(f); - s->bytesleft = qemu_get_be32(f); - - s->tx_len = qemu_get_byte(f); - s->tx_start = 0; - if (s->tx_len >= sizeof(s->tx_fifo) || s->tx_len < 0) - return -EINVAL; - for (i = 0; i < s->tx_len; i ++) - s->tx_fifo[i] = qemu_get_byte(f); + PXA2xxMMCIState *s = PXA2XX_MMCI(dev); - s->rx_len = qemu_get_byte(f); - s->rx_start = 0; - if (s->rx_len >= sizeof(s->rx_fifo) || s->rx_len < 0) - return -EINVAL; - for (i = 0; i < s->rx_len; i ++) - s->rx_fifo[i] = qemu_get_byte(f); + qemu_set_irq(s->inserted, inserted); +} - s->resp_len = qemu_get_byte(f); - if (s->resp_len > 9 || s->resp_len < 0) - return -EINVAL; - for (i = s->resp_len; i < 9; i ++) - qemu_get_be16s(f, &s->resp_fifo[i]); +static void pxa2xx_mmci_set_readonly(DeviceState *dev, bool readonly) +{ + PXA2xxMMCIState *s = PXA2XX_MMCI(dev); - return 0; + qemu_set_irq(s->readonly, readonly); } -PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem, - hwaddr base, - BlockBackend *blk, qemu_irq irq, - qemu_irq rx_dma, qemu_irq tx_dma) +void pxa2xx_mmci_handlers(PXA2xxMMCIState *s, qemu_irq readonly, + qemu_irq coverswitch) { - PXA2xxMMCIState *s; + DeviceState *dev = DEVICE(s); + + s->readonly = readonly; + s->inserted = coverswitch; + + pxa2xx_mmci_set_inserted(dev, sdbus_get_inserted(&s->sdbus)); + pxa2xx_mmci_set_readonly(dev, sdbus_get_readonly(&s->sdbus)); +} + +static void pxa2xx_mmci_reset(DeviceState *d) +{ + PXA2xxMMCIState *s = PXA2XX_MMCI(d); + + s->status = 0; + s->clkrt = 0; + s->spi = 0; + s->cmdat = 0; + s->resp_tout = 0; + s->read_tout = 0; + s->blklen = 0; + s->numblk = 0; + s->intmask = 0; + s->intreq = 0; + s->cmd = 0; + s->arg = 0; + s->active = 0; + s->bytesleft = 0; + s->tx_start = 0; + s->tx_len = 0; + s->rx_start = 0; + s->rx_len = 0; + s->resp_len = 0; + s->cmdreq = 0; + memset(s->tx_fifo, 0, sizeof(s->tx_fifo)); + memset(s->rx_fifo, 0, sizeof(s->rx_fifo)); + memset(s->resp_fifo, 0, sizeof(s->resp_fifo)); +} - s = (PXA2xxMMCIState *) g_malloc0(sizeof(PXA2xxMMCIState)); - s->irq = irq; - s->rx_dma = rx_dma; - s->tx_dma = tx_dma; +static void pxa2xx_mmci_instance_init(Object *obj) +{ + PXA2xxMMCIState *s = PXA2XX_MMCI(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + DeviceState *dev = DEVICE(obj); - memory_region_init_io(&s->iomem, NULL, &pxa2xx_mmci_ops, s, + memory_region_init_io(&s->iomem, obj, &pxa2xx_mmci_ops, s, "pxa2xx-mmci", 0x00100000); - memory_region_add_subregion(sysmem, base, &s->iomem); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); + qdev_init_gpio_out_named(dev, &s->rx_dma, "rx-dma", 1); + qdev_init_gpio_out_named(dev, &s->tx_dma, "tx-dma", 1); - /* Instantiate the actual storage */ - s->card = sd_init(blk, false); - if (s->card == NULL) { - exit(1); - } + qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), + TYPE_PXA2XX_MMCI_BUS, DEVICE(obj), "sd-bus"); +} - register_savevm(NULL, "pxa2xx_mmci", 0, 0, - pxa2xx_mmci_save, pxa2xx_mmci_load, s); +static void pxa2xx_mmci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); - return s; + dc->vmsd = &vmstate_pxa2xx_mmci; + dc->reset = pxa2xx_mmci_reset; } -void pxa2xx_mmci_handlers(PXA2xxMMCIState *s, qemu_irq readonly, - qemu_irq coverswitch) +static void pxa2xx_mmci_bus_class_init(ObjectClass *klass, void *data) { - sd_set_cb(s->card, readonly, coverswitch); + SDBusClass *sbc = SD_BUS_CLASS(klass); + + sbc->set_inserted = pxa2xx_mmci_set_inserted; + sbc->set_readonly = pxa2xx_mmci_set_readonly; } + +static const TypeInfo pxa2xx_mmci_info = { + .name = TYPE_PXA2XX_MMCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PXA2xxMMCIState), + .instance_init = pxa2xx_mmci_instance_init, + .class_init = pxa2xx_mmci_class_init, +}; + +static const TypeInfo pxa2xx_mmci_bus_info = { + .name = TYPE_PXA2XX_MMCI_BUS, + .parent = TYPE_SD_BUS, + .instance_size = sizeof(SDBus), + .class_init = pxa2xx_mmci_bus_class_init, +}; + +static void pxa2xx_mmci_register_types(void) +{ + type_register_static(&pxa2xx_mmci_info); + type_register_static(&pxa2xx_mmci_bus_info); +} + +type_init(pxa2xx_mmci_register_types) diff --git a/qemu/hw/sd/sd.c b/qemu/hw/sd/sd.c index a1ff465a6..b66e5d2db 100644 --- a/qemu/hw/sd/sd.c +++ b/qemu/hw/sd/sd.c @@ -29,10 +29,16 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "qemu/osdep.h" +#include "hw/qdev.h" #include "hw/hw.h" #include "sysemu/block-backend.h" -#include "hw/sd.h" +#include "hw/sd/sd.h" +#include "qapi/error.h" #include "qemu/bitmap.h" +#include "hw/qdev-properties.h" +#include "qemu/error-report.h" +#include "qemu/timer.h" //#define DEBUG_SD 1 @@ -43,7 +49,9 @@ do { fprintf(stderr, "SD: " fmt , ## __VA_ARGS__); } while (0) #define DPRINTF(fmt, ...) do {} while(0) #endif -#define ACMD41_ENQUIRY_MASK 0x00ffffff +#define ACMD41_ENQUIRY_MASK 0x00ffffff +#define OCR_POWER_UP 0x80000000 +#define OCR_POWER_DELAY_NS 500000 /* 0.5ms */ typedef enum { sd_r0 = 0, /* no response */ @@ -77,9 +85,12 @@ enum SDCardStates { }; struct SDState { + DeviceState parent_obj; + uint32_t mode; /* current card mode, one of SDCardModes */ int32_t state; /* current card state, one of SDCardStates */ uint32_t ocr; + QEMUTimer *ocr_power_timer; uint8_t scr[8]; uint8_t cid[16]; uint8_t csd[16]; @@ -92,6 +103,7 @@ struct SDState { int32_t wpgrps_size; uint64_t size; uint32_t blk_len; + uint32_t multi_blk_cnt; uint32_t erase_start; uint32_t erase_end; uint8_t pwd[16]; @@ -193,8 +205,17 @@ static uint16_t sd_crc16(void *message, size_t width) static void sd_set_ocr(SDState *sd) { - /* All voltages OK, card power-up OK, Standard Capacity SD Memory Card */ - sd->ocr = 0x80ffff00; + /* All voltages OK, Standard Capacity SD Memory Card, not yet powered up */ + sd->ocr = 0x00ffff00; +} + +static void sd_ocr_powerup(void *opaque) +{ + SDState *sd = opaque; + + /* Set powered up bit in OCR */ + assert(!(sd->ocr & OCR_POWER_UP)); + sd->ocr |= OCR_POWER_UP; } static void sd_set_scr(SDState *sd) @@ -389,8 +410,9 @@ static inline uint64_t sd_addr_to_wpnum(uint64_t addr) return addr >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT); } -static void sd_reset(SDState *sd) +static void sd_reset(DeviceState *dev) { + SDState *sd = SD_CARD(dev); uint64_t size; uint64_t sect; @@ -412,8 +434,7 @@ static void sd_reset(SDState *sd) sd_set_cardstatus(sd); sd_set_sdstatus(sd); - if (sd->wp_groups) - g_free(sd->wp_groups); + g_free(sd->wp_groups); sd->wp_switch = sd->blk ? blk_is_read_only(sd->blk) : false; sd->wpgrps_size = sect; sd->wp_groups = bitmap_new(sd->wpgrps_size); @@ -424,16 +445,44 @@ static void sd_reset(SDState *sd) sd->blk_len = 0x200; sd->pwd_len = 0; sd->expecting_acmd = false; + sd->multi_blk_cnt = 0; +} + +static bool sd_get_inserted(SDState *sd) +{ + return sd->blk && blk_is_inserted(sd->blk); +} + +static bool sd_get_readonly(SDState *sd) +{ + return sd->wp_switch; } static void sd_cardchange(void *opaque, bool load) { SDState *sd = opaque; + DeviceState *dev = DEVICE(sd); + SDBus *sdbus = SD_BUS(qdev_get_parent_bus(dev)); + bool inserted = sd_get_inserted(sd); + bool readonly = sd_get_readonly(sd); - qemu_set_irq(sd->inserted_cb, blk_is_inserted(sd->blk)); - if (blk_is_inserted(sd->blk)) { - sd_reset(sd); - qemu_set_irq(sd->readonly_cb, sd->wp_switch); + if (inserted) { + sd_reset(dev); + } + + /* The IRQ notification is for legacy non-QOM SD controller devices; + * QOMified controllers use the SDBus APIs. + */ + if (sdbus) { + sdbus_set_inserted(sdbus, inserted); + if (inserted) { + sdbus_set_readonly(sdbus, readonly); + } + } else { + qemu_set_irq(sd->inserted_cb, inserted); + if (inserted) { + qemu_set_irq(sd->readonly_cb, readonly); + } } } @@ -441,10 +490,44 @@ static const BlockDevOps sd_block_ops = { .change_media_cb = sd_cardchange, }; +static bool sd_ocr_vmstate_needed(void *opaque) +{ + SDState *sd = opaque; + + /* Include the OCR state (and timer) if it is not yet powered up */ + return !(sd->ocr & OCR_POWER_UP); +} + +static const VMStateDescription sd_ocr_vmstate = { + .name = "sd-card/ocr-state", + .version_id = 1, + .minimum_version_id = 1, + .needed = sd_ocr_vmstate_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT32(ocr, SDState), + VMSTATE_TIMER_PTR(ocr_power_timer, SDState), + VMSTATE_END_OF_LIST() + }, +}; + +static int sd_vmstate_pre_load(void *opaque) +{ + SDState *sd = opaque; + + /* If the OCR state is not included (prior versions, or not + * needed), then the OCR must be set as powered up. If the OCR state + * is included, this will be replaced by the state restore. + */ + sd_ocr_powerup(sd); + + return 0; +} + static const VMStateDescription sd_vmstate = { .name = "sd-card", .version_id = 1, .minimum_version_id = 1, + .pre_load = sd_vmstate_pre_load, .fields = (VMStateField[]) { VMSTATE_UINT32(mode, SDState), VMSTATE_INT32(state, SDState), @@ -456,6 +539,7 @@ static const VMStateDescription sd_vmstate = { VMSTATE_UINT32(vhs, SDState), VMSTATE_BITMAP(wp_groups, SDState, 0, wpgrps_size), VMSTATE_UINT32(blk_len, SDState), + VMSTATE_UINT32(multi_blk_cnt, SDState), VMSTATE_UINT32(erase_start, SDState), VMSTATE_UINT32(erase_end, SDState), VMSTATE_UINT8_ARRAY(pwd, SDState, 16), @@ -470,34 +554,35 @@ static const VMStateDescription sd_vmstate = { VMSTATE_BUFFER_POINTER_UNSAFE(buf, SDState, 1, 512), VMSTATE_BOOL(enable, SDState), VMSTATE_END_OF_LIST() - } + }, + .subsections = (const VMStateDescription*[]) { + &sd_ocr_vmstate, + NULL + }, }; -/* We do not model the chip select pin, so allow the board to select - whether card should be in SSI or MMC/SD mode. It is also up to the - board to ensure that ssi transfers only occur when the chip select - is asserted. */ +/* Legacy initialization function for use by non-qdevified callers */ SDState *sd_init(BlockBackend *blk, bool is_spi) { - SDState *sd; - - if (blk && blk_is_read_only(blk)) { - fprintf(stderr, "sd_init: Cannot use read-only drive\n"); + Object *obj; + DeviceState *dev; + Error *err = NULL; + + obj = object_new(TYPE_SD_CARD); + dev = DEVICE(obj); + qdev_prop_set_drive(dev, "drive", blk, &err); + if (err) { + error_report("sd_init failed: %s", error_get_pretty(err)); return NULL; } - - sd = (SDState *) g_malloc0(sizeof(SDState)); - sd->buf = blk_blockalign(blk, 512); - sd->spi = is_spi; - sd->enable = true; - sd->blk = blk; - sd_reset(sd); - if (sd->blk) { - blk_attach_dev_nofail(sd->blk, sd); - blk_set_dev_ops(sd->blk, &sd_block_ops, sd); + qdev_prop_set_bit(dev, "spi", is_spi); + object_property_set_bool(obj, true, "realized", &err); + if (err) { + error_report("sd_init failed: %s", error_get_pretty(err)); + return NULL; } - vmstate_register(NULL, -1, &sd_vmstate, sd); - return sd; + + return SD_CARD(dev); } void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert) @@ -666,8 +751,16 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, /* Not interpreting this as an app command */ sd->card_status &= ~APP_CMD; - if (sd_cmd_type[req.cmd] == sd_ac || sd_cmd_type[req.cmd] == sd_adtc) + if (sd_cmd_type[req.cmd & 0x3F] == sd_ac + || sd_cmd_type[req.cmd & 0x3F] == sd_adtc) { rca = req.arg >> 16; + } + + /* CMD23 (set block count) must be immediately followed by CMD18 or CMD25 + * if not, its effects are cancelled */ + if (sd->multi_blk_cnt != 0 && !(req.cmd == 18 || req.cmd == 25)) { + sd->multi_blk_cnt = 0; + } DPRINTF("CMD%d 0x%08x state %d\n", req.cmd, req.arg, sd->state); switch (req.cmd) { @@ -679,7 +772,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, default: sd->state = sd_idle_state; - sd_reset(sd); + sd_reset(DEVICE(sd)); return sd->spi ? sd_r1 : sd_r0; } break; @@ -964,6 +1057,17 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, } break; + case 23: /* CMD23: SET_BLOCK_COUNT */ + switch (sd->state) { + case sd_transfer_state: + sd->multi_blk_cnt = req.arg; + return sd_r1; + + default: + break; + } + break; + /* Block write commands (Class 4) */ case 24: /* CMD24: WRITE_SINGLE_BLOCK */ if (sd->spi) @@ -1196,16 +1300,17 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, default: bad_cmd: - fprintf(stderr, "SD: Unknown CMD%i\n", req.cmd); + qemu_log_mask(LOG_GUEST_ERROR, "SD: Unknown CMD%i\n", req.cmd); return sd_illegal; unimplemented_cmd: /* Commands that are recognised but not yet implemented in SPI mode. */ - fprintf(stderr, "SD: CMD%i not implemented in SPI mode\n", req.cmd); + qemu_log_mask(LOG_UNIMP, "SD: CMD%i not implemented in SPI mode\n", + req.cmd); return sd_illegal; } - fprintf(stderr, "SD: CMD%i in a wrong state\n", req.cmd); + qemu_log_mask(LOG_GUEST_ERROR, "SD: CMD%i in a wrong state\n", req.cmd); return sd_illegal; } @@ -1273,9 +1378,28 @@ static sd_rsp_type_t sd_app_command(SDState *sd, } switch (sd->state) { case sd_idle_state: + /* If it's the first ACMD41 since reset, we need to decide + * whether to power up. If this is not an enquiry ACMD41, + * we immediately report power on and proceed below to the + * ready state, but if it is, we set a timer to model a + * delay for power up. This works around a bug in EDK2 + * UEFI, which sends an initial enquiry ACMD41, but + * assumes that the card is in ready state as soon as it + * sees the power up bit set. */ + if (!(sd->ocr & OCR_POWER_UP)) { + if ((req.arg & ACMD41_ENQUIRY_MASK) != 0) { + timer_del(sd->ocr_power_timer); + sd_ocr_powerup(sd); + } else if (!timer_pending(sd->ocr_power_timer)) { + timer_mod_ns(sd->ocr_power_timer, + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + OCR_POWER_DELAY_NS)); + } + } + /* We accept any voltage. 10000 V is nothing. * - * We don't model init delay so just advance straight to ready state + * Once we're powered up, we advance straight to ready state * unless it's an enquiry ACMD41 (bits 23:0 == 0). */ if (req.arg & ACMD41_ENQUIRY_MASK) { @@ -1318,7 +1442,7 @@ static sd_rsp_type_t sd_app_command(SDState *sd, return sd_normal_command(sd, req); } - fprintf(stderr, "SD: ACMD%i in a wrong state\n", req.cmd); + qemu_log_mask(LOG_GUEST_ERROR, "SD: ACMD%i in a wrong state\n", req.cmd); return sd_illegal; } @@ -1338,7 +1462,8 @@ static int cmd_valid_while_locked(SDState *sd, SDRequest *req) if (req->cmd == 16 || req->cmd == 55) { return 1; } - return sd_cmd_class[req->cmd] == 0 || sd_cmd_class[req->cmd] == 7; + return sd_cmd_class[req->cmd & 0x3F] == 0 + || sd_cmd_class[req->cmd & 0x3F] == 7; } int sd_do_command(SDState *sd, SDRequest *req, @@ -1361,7 +1486,7 @@ int sd_do_command(SDState *sd, SDRequest *req, if (!cmd_valid_while_locked(sd, req)) { sd->card_status |= ILLEGAL_COMMAND; sd->expecting_acmd = false; - fprintf(stderr, "SD: Card is locked\n"); + qemu_log_mask(LOG_GUEST_ERROR, "SD: Card is locked\n"); rtype = sd_illegal; goto send_response; } @@ -1519,7 +1644,8 @@ void sd_write_data(SDState *sd, uint8_t value) return; if (sd->state != sd_receivingdata_state) { - fprintf(stderr, "sd_write_data: not in Receiving-Data state\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "sd_write_data: not in Receiving-Data state\n"); return; } @@ -1563,6 +1689,14 @@ void sd_write_data(SDState *sd, uint8_t value) sd->csd[14] |= 0x40; /* Bzzzzzzztt .... Operation complete. */ + if (sd->multi_blk_cnt != 0) { + if (--sd->multi_blk_cnt == 0) { + /* Stop! */ + sd->state = sd_transfer_state; + break; + } + } + sd->state = sd_receivingdata_state; } break; @@ -1630,7 +1764,7 @@ void sd_write_data(SDState *sd, uint8_t value) break; default: - fprintf(stderr, "sd_write_data: unknown command\n"); + qemu_log_mask(LOG_GUEST_ERROR, "sd_write_data: unknown command\n"); break; } } @@ -1645,7 +1779,8 @@ uint8_t sd_read_data(SDState *sd) return 0x00; if (sd->state != sd_sendingdata_state) { - fprintf(stderr, "sd_read_data: not in Sending-Data state\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "sd_read_data: not in Sending-Data state\n"); return 0x00; } @@ -1709,6 +1844,15 @@ uint8_t sd_read_data(SDState *sd) if (sd->data_offset >= io_len) { sd->data_start += io_len; sd->data_offset = 0; + + if (sd->multi_blk_cnt != 0) { + if (--sd->multi_blk_cnt == 0) { + /* Stop! */ + sd->state = sd_transfer_state; + break; + } + } + if (sd->data_start + io_len > sd->size) { sd->card_status |= ADDRESS_ERROR; break; @@ -1747,7 +1891,7 @@ uint8_t sd_read_data(SDState *sd) break; default: - fprintf(stderr, "sd_read_data: unknown command\n"); + qemu_log_mask(LOG_GUEST_ERROR, "sd_read_data: unknown command\n"); return 0x00; } @@ -1763,3 +1907,73 @@ void sd_enable(SDState *sd, bool enable) { sd->enable = enable; } + +static void sd_instance_init(Object *obj) +{ + SDState *sd = SD_CARD(obj); + + sd->enable = true; + sd->ocr_power_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sd_ocr_powerup, sd); +} + +static void sd_realize(DeviceState *dev, Error **errp) +{ + SDState *sd = SD_CARD(dev); + + if (sd->blk && blk_is_read_only(sd->blk)) { + error_setg(errp, "Cannot use read-only drive as SD card"); + return; + } + + sd->buf = blk_blockalign(sd->blk, 512); + + if (sd->blk) { + blk_set_dev_ops(sd->blk, &sd_block_ops, sd); + } +} + +static Property sd_properties[] = { + DEFINE_PROP_DRIVE("drive", SDState, blk), + /* We do not model the chip select pin, so allow the board to select + * whether card should be in SSI or MMC/SD mode. It is also up to the + * board to ensure that ssi transfers only occur when the chip select + * is asserted. */ + DEFINE_PROP_BOOL("spi", SDState, spi, false), + DEFINE_PROP_END_OF_LIST() +}; + +static void sd_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SDCardClass *sc = SD_CARD_CLASS(klass); + + dc->realize = sd_realize; + dc->props = sd_properties; + dc->vmsd = &sd_vmstate; + dc->reset = sd_reset; + dc->bus_type = TYPE_SD_BUS; + + sc->do_command = sd_do_command; + sc->write_data = sd_write_data; + sc->read_data = sd_read_data; + sc->data_ready = sd_data_ready; + sc->enable = sd_enable; + sc->get_inserted = sd_get_inserted; + sc->get_readonly = sd_get_readonly; +} + +static const TypeInfo sd_info = { + .name = TYPE_SD_CARD, + .parent = TYPE_DEVICE, + .instance_size = sizeof(SDState), + .class_size = sizeof(SDCardClass), + .class_init = sd_class_init, + .instance_init = sd_instance_init, +}; + +static void sd_register_types(void) +{ + type_register_static(&sd_info); +} + +type_init(sd_register_types) diff --git a/qemu/hw/sd/sdhci-internal.h b/qemu/hw/sd/sdhci-internal.h new file mode 100644 index 000000000..161177cf3 --- /dev/null +++ b/qemu/hw/sd/sdhci-internal.h @@ -0,0 +1,232 @@ +/* + * SD Association Host Standard Specification v2.0 controller emulation + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Mitsyanko Igor + * Peter A.G. Crosthwaite + * + * Based on MMC controller for Samsung S5PC1xx-based board emulation + * by Alexey Merkulov and Vladimir Monakhov. + * + * 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 . + */ +#ifndef SDHCI_INTERNAL_H +#define SDHCI_INTERNAL_H + +#include "hw/sd/sdhci.h" + +/* R/W SDMA System Address register 0x0 */ +#define SDHC_SYSAD 0x00 + +/* R/W Host DMA Buffer Boundary and Transfer Block Size Register 0x0 */ +#define SDHC_BLKSIZE 0x04 + +/* R/W Blocks count for current transfer 0x0 */ +#define SDHC_BLKCNT 0x06 + +/* R/W Command Argument Register 0x0 */ +#define SDHC_ARGUMENT 0x08 + +/* R/W Transfer Mode Setting Register 0x0 */ +#define SDHC_TRNMOD 0x0C +#define SDHC_TRNS_DMA 0x0001 +#define SDHC_TRNS_BLK_CNT_EN 0x0002 +#define SDHC_TRNS_ACMD12 0x0004 +#define SDHC_TRNS_READ 0x0010 +#define SDHC_TRNS_MULTI 0x0020 + +/* R/W Command Register 0x0 */ +#define SDHC_CMDREG 0x0E +#define SDHC_CMD_RSP_WITH_BUSY (3 << 0) +#define SDHC_CMD_DATA_PRESENT (1 << 5) +#define SDHC_CMD_SUSPEND (1 << 6) +#define SDHC_CMD_RESUME (1 << 7) +#define SDHC_CMD_ABORT ((1 << 6)|(1 << 7)) +#define SDHC_CMD_TYPE_MASK ((1 << 6)|(1 << 7)) +#define SDHC_COMMAND_TYPE(x) ((x) & SDHC_CMD_TYPE_MASK) + +/* ROC Response Register 0 0x0 */ +#define SDHC_RSPREG0 0x10 +/* ROC Response Register 1 0x0 */ +#define SDHC_RSPREG1 0x14 +/* ROC Response Register 2 0x0 */ +#define SDHC_RSPREG2 0x18 +/* ROC Response Register 3 0x0 */ +#define SDHC_RSPREG3 0x1C + +/* R/W Buffer Data Register 0x0 */ +#define SDHC_BDATA 0x20 + +/* R/ROC Present State Register 0x000A0000 */ +#define SDHC_PRNSTS 0x24 +#define SDHC_CMD_INHIBIT 0x00000001 +#define SDHC_DATA_INHIBIT 0x00000002 +#define SDHC_DAT_LINE_ACTIVE 0x00000004 +#define SDHC_DOING_WRITE 0x00000100 +#define SDHC_DOING_READ 0x00000200 +#define SDHC_SPACE_AVAILABLE 0x00000400 +#define SDHC_DATA_AVAILABLE 0x00000800 +#define SDHC_CARD_PRESENT 0x00010000 +#define SDHC_CARD_DETECT 0x00040000 +#define SDHC_WRITE_PROTECT 0x00080000 +#define TRANSFERRING_DATA(x) \ + ((x) & (SDHC_DOING_READ | SDHC_DOING_WRITE)) + +/* R/W Host control Register 0x0 */ +#define SDHC_HOSTCTL 0x28 +#define SDHC_CTRL_DMA_CHECK_MASK 0x18 +#define SDHC_CTRL_SDMA 0x00 +#define SDHC_CTRL_ADMA1_32 0x08 +#define SDHC_CTRL_ADMA2_32 0x10 +#define SDHC_CTRL_ADMA2_64 0x18 +#define SDHC_DMA_TYPE(x) ((x) & SDHC_CTRL_DMA_CHECK_MASK) + +/* R/W Power Control Register 0x0 */ +#define SDHC_PWRCON 0x29 +#define SDHC_POWER_ON (1 << 0) + +/* R/W Block Gap Control Register 0x0 */ +#define SDHC_BLKGAP 0x2A +#define SDHC_STOP_AT_GAP_REQ 0x01 +#define SDHC_CONTINUE_REQ 0x02 + +/* R/W WakeUp Control Register 0x0 */ +#define SDHC_WAKCON 0x2B +#define SDHC_WKUP_ON_INS (1 << 1) +#define SDHC_WKUP_ON_RMV (1 << 2) + +/* CLKCON */ +#define SDHC_CLKCON 0x2C +#define SDHC_CLOCK_INT_STABLE 0x0002 +#define SDHC_CLOCK_INT_EN 0x0001 +#define SDHC_CLOCK_SDCLK_EN (1 << 2) +#define SDHC_CLOCK_CHK_MASK 0x0007 +#define SDHC_CLOCK_IS_ON(x) \ + (((x) & SDHC_CLOCK_CHK_MASK) == SDHC_CLOCK_CHK_MASK) + +/* R/W Timeout Control Register 0x0 */ +#define SDHC_TIMEOUTCON 0x2E + +/* R/W Software Reset Register 0x0 */ +#define SDHC_SWRST 0x2F +#define SDHC_RESET_ALL 0x01 +#define SDHC_RESET_CMD 0x02 +#define SDHC_RESET_DATA 0x04 + +/* ROC/RW1C Normal Interrupt Status Register 0x0 */ +#define SDHC_NORINTSTS 0x30 +#define SDHC_NIS_ERR 0x8000 +#define SDHC_NIS_CMDCMP 0x0001 +#define SDHC_NIS_TRSCMP 0x0002 +#define SDHC_NIS_BLKGAP 0x0004 +#define SDHC_NIS_DMA 0x0008 +#define SDHC_NIS_WBUFRDY 0x0010 +#define SDHC_NIS_RBUFRDY 0x0020 +#define SDHC_NIS_INSERT 0x0040 +#define SDHC_NIS_REMOVE 0x0080 +#define SDHC_NIS_CARDINT 0x0100 + +/* ROC/RW1C Error Interrupt Status Register 0x0 */ +#define SDHC_ERRINTSTS 0x32 +#define SDHC_EIS_CMDTIMEOUT 0x0001 +#define SDHC_EIS_BLKGAP 0x0004 +#define SDHC_EIS_CMDIDX 0x0008 +#define SDHC_EIS_CMD12ERR 0x0100 +#define SDHC_EIS_ADMAERR 0x0200 + +/* R/W Normal Interrupt Status Enable Register 0x0 */ +#define SDHC_NORINTSTSEN 0x34 +#define SDHC_NISEN_CMDCMP 0x0001 +#define SDHC_NISEN_TRSCMP 0x0002 +#define SDHC_NISEN_DMA 0x0008 +#define SDHC_NISEN_WBUFRDY 0x0010 +#define SDHC_NISEN_RBUFRDY 0x0020 +#define SDHC_NISEN_INSERT 0x0040 +#define SDHC_NISEN_REMOVE 0x0080 +#define SDHC_NISEN_CARDINT 0x0100 + +/* R/W Error Interrupt Status Enable Register 0x0 */ +#define SDHC_ERRINTSTSEN 0x36 +#define SDHC_EISEN_CMDTIMEOUT 0x0001 +#define SDHC_EISEN_BLKGAP 0x0004 +#define SDHC_EISEN_CMDIDX 0x0008 +#define SDHC_EISEN_ADMAERR 0x0200 + +/* R/W Normal Interrupt Signal Enable Register 0x0 */ +#define SDHC_NORINTSIGEN 0x38 +#define SDHC_NORINTSIG_INSERT (1 << 6) +#define SDHC_NORINTSIG_REMOVE (1 << 7) + +/* R/W Error Interrupt Signal Enable Register 0x0 */ +#define SDHC_ERRINTSIGEN 0x3A + +/* ROC Auto CMD12 error status register 0x0 */ +#define SDHC_ACMD12ERRSTS 0x3C + +/* HWInit Capabilities Register 0x05E80080 */ +#define SDHC_CAPAREG 0x40 +#define SDHC_CAN_DO_DMA 0x00400000 +#define SDHC_CAN_DO_ADMA2 0x00080000 +#define SDHC_CAN_DO_ADMA1 0x00100000 +#define SDHC_64_BIT_BUS_SUPPORT (1 << 28) +#define SDHC_CAPAB_BLOCKSIZE(x) (((x) >> 16) & 0x3) + +/* HWInit Maximum Current Capabilities Register 0x0 */ +#define SDHC_MAXCURR 0x48 + +/* W Force Event Auto CMD12 Error Interrupt Register 0x0000 */ +#define SDHC_FEAER 0x50 +/* W Force Event Error Interrupt Register Error Interrupt 0x0000 */ +#define SDHC_FEERR 0x52 + +/* R/W ADMA Error Status Register 0x00 */ +#define SDHC_ADMAERR 0x54 +#define SDHC_ADMAERR_LENGTH_MISMATCH (1 << 2) +#define SDHC_ADMAERR_STATE_ST_STOP (0 << 0) +#define SDHC_ADMAERR_STATE_ST_FDS (1 << 0) +#define SDHC_ADMAERR_STATE_ST_TFR (3 << 0) +#define SDHC_ADMAERR_STATE_MASK (3 << 0) + +/* R/W ADMA System Address Register 0x00 */ +#define SDHC_ADMASYSADDR 0x58 +#define SDHC_ADMA_ATTR_SET_LEN (1 << 4) +#define SDHC_ADMA_ATTR_ACT_TRAN (1 << 5) +#define SDHC_ADMA_ATTR_ACT_LINK (3 << 4) +#define SDHC_ADMA_ATTR_INT (1 << 2) +#define SDHC_ADMA_ATTR_END (1 << 1) +#define SDHC_ADMA_ATTR_VALID (1 << 0) +#define SDHC_ADMA_ATTR_ACT_MASK ((1 << 4)|(1 << 5)) + +/* Slot interrupt status */ +#define SDHC_SLOT_INT_STATUS 0xFC + +/* HWInit Host Controller Version Register 0x0401 */ +#define SDHC_HCVER 0xFE +#define SD_HOST_SPECv2_VERS 0x2401 + +#define SDHC_REGISTERS_MAP_SIZE 0x100 +#define SDHC_INSERTION_DELAY (NANOSECONDS_PER_SECOND) +#define SDHC_TRANSFER_DELAY 100 +#define SDHC_ADMA_DESCS_PER_DELAY 5 +#define SDHC_CMD_RESPONSE (3 << 0) + +enum { + sdhc_not_stopped = 0, /* normal SDHC state */ + sdhc_gap_read = 1, /* SDHC stopped at block gap during read operation */ + sdhc_gap_write = 2 /* SDHC stopped at block gap during write operation */ +}; + +extern const VMStateDescription sdhci_vmstate; + +#endif diff --git a/qemu/hw/sd/sdhci.c b/qemu/hw/sd/sdhci.c index e63367ba5..d28b5871f 100644 --- a/qemu/hw/sd/sdhci.c +++ b/qemu/hw/sd/sdhci.c @@ -22,38 +22,41 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "sysemu/dma.h" #include "qemu/timer.h" #include "qemu/bitops.h" - -#include "sdhci.h" +#include "sdhci-internal.h" /* host controller debug messages */ #ifndef SDHC_DEBUG #define SDHC_DEBUG 0 #endif -#if SDHC_DEBUG == 0 - #define DPRINT_L1(fmt, args...) do { } while (0) - #define DPRINT_L2(fmt, args...) do { } while (0) - #define ERRPRINT(fmt, args...) do { } while (0) -#elif SDHC_DEBUG == 1 - #define DPRINT_L1(fmt, args...) \ - do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0) - #define DPRINT_L2(fmt, args...) do { } while (0) - #define ERRPRINT(fmt, args...) \ - do {fprintf(stderr, "QEMU SDHC ERROR: "fmt, ## args); } while (0) -#else - #define DPRINT_L1(fmt, args...) \ - do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0) - #define DPRINT_L2(fmt, args...) \ - do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0) - #define ERRPRINT(fmt, args...) \ - do {fprintf(stderr, "QEMU SDHC ERROR: "fmt, ## args); } while (0) -#endif +#define DPRINT_L1(fmt, args...) \ + do { \ + if (SDHC_DEBUG) { \ + fprintf(stderr, "QEMU SDHC: " fmt, ## args); \ + } \ + } while (0) +#define DPRINT_L2(fmt, args...) \ + do { \ + if (SDHC_DEBUG > 1) { \ + fprintf(stderr, "QEMU SDHC: " fmt, ## args); \ + } \ + } while (0) +#define ERRPRINT(fmt, args...) \ + do { \ + if (SDHC_DEBUG) { \ + fprintf(stderr, "QEMU SDHC ERROR: " fmt, ## args); \ + } \ + } while (0) + +#define TYPE_SDHCI_BUS "sdhci-bus" +#define SDHCI_BUS(obj) OBJECT_CHECK(SDBus, (obj), TYPE_SDHCI_BUS) /* Default SD/MMC host controller features information, which will be * presented in CAPABILITIES register of generic SD host controller at reset. @@ -145,9 +148,9 @@ static void sdhci_raise_insertion_irq(void *opaque) } } -static void sdhci_insert_eject_cb(void *opaque, int irq, int level) +static void sdhci_set_inserted(DeviceState *dev, bool level) { - SDHCIState *s = (SDHCIState *)opaque; + SDHCIState *s = (SDHCIState *)dev; DPRINT_L1("Card state changed: %s!\n", level ? "insert" : "eject"); if ((s->norintsts & SDHC_NIS_REMOVE) && level) { @@ -172,9 +175,9 @@ static void sdhci_insert_eject_cb(void *opaque, int irq, int level) } } -static void sdhci_card_readonly_cb(void *opaque, int irq, int level) +static void sdhci_set_readonly(DeviceState *dev, bool level) { - SDHCIState *s = (SDHCIState *)opaque; + SDHCIState *s = (SDHCIState *)dev; if (level) { s->prnsts &= ~SDHC_WRITE_PROTECT; @@ -186,6 +189,8 @@ static void sdhci_card_readonly_cb(void *opaque, int irq, int level) static void sdhci_reset(SDHCIState *s) { + DeviceState *dev = DEVICE(s); + timer_del(s->insert_timer); timer_del(s->transfer_timer); /* Set all registers to 0. Capabilities registers are not cleared @@ -193,9 +198,28 @@ static void sdhci_reset(SDHCIState *s) * initialization */ memset(&s->sdmasysad, 0, (uintptr_t)&s->capareg - (uintptr_t)&s->sdmasysad); - sd_set_cb(s->card, s->ro_cb, s->eject_cb); + /* Reset other state based on current card insertion/readonly status */ + sdhci_set_inserted(dev, sdbus_get_inserted(&s->sdbus)); + sdhci_set_readonly(dev, sdbus_get_readonly(&s->sdbus)); + s->data_count = 0; s->stopped_state = sdhc_not_stopped; + s->pending_insert_state = false; +} + +static void sdhci_poweron_reset(DeviceState *dev) +{ + /* QOM (ie power-on) reset. This is identical to reset + * commanded via device register apart from handling of the + * 'pending insert on powerup' quirk. + */ + SDHCIState *s = (SDHCIState *)dev; + + sdhci_reset(s); + + if (s->pending_insert_quirk) { + s->pending_insert_state = true; + } } static void sdhci_data_transfer(void *opaque); @@ -211,7 +235,7 @@ static void sdhci_send_command(SDHCIState *s) request.cmd = s->cmdreg >> 8; request.arg = s->argument; DPRINT_L1("sending CMD%u ARG[0x%08x]\n", request.cmd, request.arg); - rlen = sd_do_command(s->card, &request, response); + rlen = sdbus_do_command(&s->sdbus, &request, response); if (s->cmdreg & SDHC_CMD_RESPONSE) { if (rlen == 4) { @@ -243,9 +267,6 @@ static void sdhci_send_command(SDHCIState *s) (s->cmdreg & SDHC_CMD_RESPONSE) == SDHC_CMD_RSP_WITH_BUSY) { s->norintsts |= SDHC_NIS_TRSCMP; } - } else if (rlen != 0 && (s->errintstsen & SDHC_EISEN_CMDIDX)) { - s->errintsts |= SDHC_EIS_CMDIDX; - s->norintsts |= SDHC_NIS_ERR; } if (s->norintstsen & SDHC_NISEN_CMDCMP) { @@ -270,7 +291,7 @@ static void sdhci_end_transfer(SDHCIState *s) request.cmd = 0x0C; request.arg = 0; DPRINT_L1("Automatically issue CMD%d %08x\n", request.cmd, request.arg); - sd_do_command(s->card, &request, response); + sdbus_do_command(&s->sdbus, &request, response); /* Auto CMD12 response goes to the upper Response register */ s->rspreg[3] = (response[0] << 24) | (response[1] << 16) | (response[2] << 8) | response[3]; @@ -302,7 +323,7 @@ static void sdhci_read_block_from_card(SDHCIState *s) } for (index = 0; index < (s->blksize & 0x0fff); index++) { - s->fifo_buffer[index] = sd_read_data(s->card); + s->fifo_buffer[index] = sdbus_read_data(&s->sdbus); } /* New data now available for READ through Buffer Port Register */ @@ -395,7 +416,7 @@ static void sdhci_write_block_to_card(SDHCIState *s) } for (index = 0; index < (s->blksize & 0x0fff); index++) { - sd_write_data(s->card, s->fifo_buffer[index]); + sdbus_write_data(&s->sdbus, s->fifo_buffer[index]); } /* Next data can be written through BUFFER DATORT register */ @@ -477,7 +498,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s) while (s->blkcnt) { if (s->data_count == 0) { for (n = 0; n < block_size; n++) { - s->fifo_buffer[n] = sd_read_data(s->card); + s->fifo_buffer[n] = sdbus_read_data(&s->sdbus); } } begin = s->data_count; @@ -518,7 +539,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s) s->sdmasysad += s->data_count - begin; if (s->data_count == block_size) { for (n = 0; n < block_size; n++) { - sd_write_data(s->card, s->fifo_buffer[n]); + sdbus_write_data(&s->sdbus, s->fifo_buffer[n]); } s->data_count = 0; if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { @@ -550,7 +571,7 @@ static void sdhci_sdma_transfer_single_block(SDHCIState *s) if (s->trnmod & SDHC_TRNS_READ) { for (n = 0; n < datacnt; n++) { - s->fifo_buffer[n] = sd_read_data(s->card); + s->fifo_buffer[n] = sdbus_read_data(&s->sdbus); } dma_memory_write(&address_space_memory, s->sdmasysad, s->fifo_buffer, datacnt); @@ -558,7 +579,7 @@ static void sdhci_sdma_transfer_single_block(SDHCIState *s) dma_memory_read(&address_space_memory, s->sdmasysad, s->fifo_buffer, datacnt); for (n = 0; n < datacnt; n++) { - sd_write_data(s->card, s->fifo_buffer[n]); + sdbus_write_data(&s->sdbus, s->fifo_buffer[n]); } } @@ -662,7 +683,7 @@ static void sdhci_do_adma(SDHCIState *s) while (length) { if (s->data_count == 0) { for (n = 0; n < block_size; n++) { - s->fifo_buffer[n] = sd_read_data(s->card); + s->fifo_buffer[n] = sdbus_read_data(&s->sdbus); } } begin = s->data_count; @@ -703,7 +724,7 @@ static void sdhci_do_adma(SDHCIState *s) dscr.addr += s->data_count - begin; if (s->data_count == block_size) { for (n = 0; n < block_size; n++) { - sd_write_data(s->card, s->fifo_buffer[n]); + sdbus_write_data(&s->sdbus, s->fifo_buffer[n]); } s->data_count = 0; if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { @@ -719,7 +740,8 @@ static void sdhci_do_adma(SDHCIState *s) break; case SDHC_ADMA_ATTR_ACT_LINK: /* link to next descriptor table */ s->admasysaddr = dscr.addr; - DPRINT_L1("ADMA link: admasysaddr=0x%lx\n", s->admasysaddr); + DPRINT_L1("ADMA link: admasysaddr=0x%" PRIx64 "\n", + s->admasysaddr); break; default: s->admasysaddr += dscr.incr; @@ -727,7 +749,8 @@ static void sdhci_do_adma(SDHCIState *s) } if (dscr.attr & SDHC_ADMA_ATTR_INT) { - DPRINT_L1("ADMA interrupt: admasysaddr=0x%lx\n", s->admasysaddr); + DPRINT_L1("ADMA interrupt: admasysaddr=0x%" PRIx64 "\n", + s->admasysaddr); if (s->norintstsen & SDHC_NISEN_DMA) { s->norintsts |= SDHC_NIS_DMA; } @@ -815,7 +838,7 @@ static void sdhci_data_transfer(void *opaque) break; } } else { - if ((s->trnmod & SDHC_TRNS_READ) && sd_data_ready(s->card)) { + if ((s->trnmod & SDHC_TRNS_READ) && sdbus_data_ready(&s->sdbus)) { s->prnsts |= SDHC_DOING_READ | SDHC_DATA_INHIBIT | SDHC_DAT_LINE_ACTIVE; sdhci_read_block_from_card(s); @@ -829,7 +852,7 @@ static void sdhci_data_transfer(void *opaque) static bool sdhci_can_issue_command(SDHCIState *s) { - if (!SDHC_CLOCK_IS_ON(s->clkcon) || !(s->pwrcon & SDHC_POWER_ON) || + if (!SDHC_CLOCK_IS_ON(s->clkcon) || (((s->prnsts & SDHC_DATA_INHIBIT) || s->stopped_state) && ((s->cmdreg & SDHC_CMD_DATA_PRESENT) || ((s->cmdreg & SDHC_CMD_RESPONSE) == SDHC_CMD_RSP_WITH_BUSY && @@ -1006,6 +1029,16 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) MASKED_WRITE(s->blksize, mask, value); MASKED_WRITE(s->blkcnt, mask >> 16, value >> 16); } + + /* Limit block size to the maximum buffer size */ + if (extract32(s->blksize, 0, 12) > s->buf_maxsz) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Size 0x%x is larger than " \ + "the maximum buffer 0x%x", __func__, s->blksize, + s->buf_maxsz); + + s->blksize = deposit32(s->blksize, 0, 12, s->buf_maxsz); + } + break; case SDHC_ARGUMENT: MASKED_WRITE(s->argument, mask, value); @@ -1078,6 +1111,13 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) } else { s->norintsts &= ~SDHC_NIS_ERR; } + /* Quirk for Raspberry Pi: pending card insert interrupt + * appears when first enabled after power on */ + if ((s->norintstsen & SDHC_NISEN_INSERT) && s->pending_insert_state) { + assert(s->pending_insert_quirk); + s->norintsts |= SDHC_NIS_INSERT; + s->pending_insert_state = false; + } sdhci_update_irq(s); break; case SDHC_NORINTSIGEN: @@ -1144,17 +1184,8 @@ static inline unsigned int sdhci_get_fifolen(SDHCIState *s) static void sdhci_initfn(SDHCIState *s) { - DriveInfo *di; - - /* FIXME use a qdev drive property instead of drive_get_next() */ - di = drive_get_next(IF_SD); - s->card = sd_init(di ? blk_by_legacy_dinfo(di) : NULL, false); - if (s->card == NULL) { - exit(1); - } - s->eject_cb = qemu_allocate_irq(sdhci_insert_eject_cb, s, 0); - s->ro_cb = qemu_allocate_irq(sdhci_card_readonly_cb, s, 0); - sd_set_cb(s->card, s->ro_cb, s->eject_cb); + qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), + TYPE_SDHCI_BUS, DEVICE(s), "sd-bus"); s->insert_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_raise_insertion_irq, s); s->transfer_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_data_transfer, s); @@ -1169,12 +1200,28 @@ static void sdhci_uninitfn(SDHCIState *s) qemu_free_irq(s->eject_cb); qemu_free_irq(s->ro_cb); - if (s->fifo_buffer) { - g_free(s->fifo_buffer); - s->fifo_buffer = NULL; - } + g_free(s->fifo_buffer); + s->fifo_buffer = NULL; } +static bool sdhci_pending_insert_vmstate_needed(void *opaque) +{ + SDHCIState *s = opaque; + + return s->pending_insert_state; +} + +static const VMStateDescription sdhci_pending_insert_vmstate = { + .name = "sdhci/pending-insert", + .version_id = 1, + .minimum_version_id = 1, + .needed = sdhci_pending_insert_vmstate_needed, + .fields = (VMStateField[]) { + VMSTATE_BOOL(pending_insert_state, SDHCIState), + VMSTATE_END_OF_LIST() + }, +}; + const VMStateDescription sdhci_vmstate = { .name = "sdhci", .version_id = 1, @@ -1209,12 +1256,16 @@ const VMStateDescription sdhci_vmstate = { VMSTATE_TIMER_PTR(insert_timer, SDHCIState), VMSTATE_TIMER_PTR(transfer_timer, SDHCIState), VMSTATE_END_OF_LIST() - } + }, + .subsections = (const VMStateDescription*[]) { + &sdhci_pending_insert_vmstate, + NULL + }, }; /* Capabilities registers provide information on supported features of this * specific host controller implementation */ -static Property sdhci_properties[] = { +static Property sdhci_pci_properties[] = { DEFINE_PROP_UINT32("capareg", SDHCIState, capareg, SDHC_CAPAB_REG_DEFAULT), DEFINE_PROP_UINT32("maxcurr", SDHCIState, maxcurr, 0), @@ -1253,9 +1304,8 @@ static void sdhci_pci_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_SYSTEM_SDHCI; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); dc->vmsd = &sdhci_vmstate; - dc->props = sdhci_properties; - /* Reason: realize() method uses drive_get_next() */ - dc->cannot_instantiate_with_device_add_yet = true; + dc->props = sdhci_pci_properties; + dc->reset = sdhci_poweron_reset; } static const TypeInfo sdhci_pci_info = { @@ -1265,9 +1315,19 @@ static const TypeInfo sdhci_pci_info = { .class_init = sdhci_pci_class_init, }; +static Property sdhci_sysbus_properties[] = { + DEFINE_PROP_UINT32("capareg", SDHCIState, capareg, + SDHC_CAPAB_REG_DEFAULT), + DEFINE_PROP_UINT32("maxcurr", SDHCIState, maxcurr, 0), + DEFINE_PROP_BOOL("pending-insert-quirk", SDHCIState, pending_insert_quirk, + false), + DEFINE_PROP_END_OF_LIST(), +}; + static void sdhci_sysbus_init(Object *obj) { SDHCIState *s = SYSBUS_SDHCI(obj); + sdhci_initfn(s); } @@ -1295,10 +1355,9 @@ static void sdhci_sysbus_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &sdhci_vmstate; - dc->props = sdhci_properties; + dc->props = sdhci_sysbus_properties; dc->realize = sdhci_sysbus_realize; - /* Reason: instance_init() method uses drive_get_next() */ - dc->cannot_instantiate_with_device_add_yet = true; + dc->reset = sdhci_poweron_reset; } static const TypeInfo sdhci_sysbus_info = { @@ -1310,10 +1369,26 @@ static const TypeInfo sdhci_sysbus_info = { .class_init = sdhci_sysbus_class_init, }; +static void sdhci_bus_class_init(ObjectClass *klass, void *data) +{ + SDBusClass *sbc = SD_BUS_CLASS(klass); + + sbc->set_inserted = sdhci_set_inserted; + sbc->set_readonly = sdhci_set_readonly; +} + +static const TypeInfo sdhci_bus_info = { + .name = TYPE_SDHCI_BUS, + .parent = TYPE_SD_BUS, + .instance_size = sizeof(SDBus), + .class_init = sdhci_bus_class_init, +}; + static void sdhci_register_types(void) { type_register_static(&sdhci_pci_info); type_register_static(&sdhci_sysbus_info); + type_register_static(&sdhci_bus_info); } type_init(sdhci_register_types) diff --git a/qemu/hw/sd/sdhci.h b/qemu/hw/sd/sdhci.h deleted file mode 100644 index 3352d23d6..000000000 --- a/qemu/hw/sd/sdhci.h +++ /dev/null @@ -1,295 +0,0 @@ -/* - * SD Association Host Standard Specification v2.0 controller emulation - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Mitsyanko Igor - * Peter A.G. Crosthwaite - * - * Based on MMC controller for Samsung S5PC1xx-based board emulation - * by Alexey Merkulov and Vladimir Monakhov. - * - * 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 . - */ - -#ifndef SDHCI_H -#define SDHCI_H - -#include "qemu-common.h" -#include "hw/pci/pci.h" -#include "hw/sysbus.h" -#include "hw/sd.h" - -/* R/W SDMA System Address register 0x0 */ -#define SDHC_SYSAD 0x00 - -/* R/W Host DMA Buffer Boundary and Transfer Block Size Register 0x0 */ -#define SDHC_BLKSIZE 0x04 - -/* R/W Blocks count for current transfer 0x0 */ -#define SDHC_BLKCNT 0x06 - -/* R/W Command Argument Register 0x0 */ -#define SDHC_ARGUMENT 0x08 - -/* R/W Transfer Mode Setting Register 0x0 */ -#define SDHC_TRNMOD 0x0C -#define SDHC_TRNS_DMA 0x0001 -#define SDHC_TRNS_BLK_CNT_EN 0x0002 -#define SDHC_TRNS_ACMD12 0x0004 -#define SDHC_TRNS_READ 0x0010 -#define SDHC_TRNS_MULTI 0x0020 - -/* R/W Command Register 0x0 */ -#define SDHC_CMDREG 0x0E -#define SDHC_CMD_RSP_WITH_BUSY (3 << 0) -#define SDHC_CMD_DATA_PRESENT (1 << 5) -#define SDHC_CMD_SUSPEND (1 << 6) -#define SDHC_CMD_RESUME (1 << 7) -#define SDHC_CMD_ABORT ((1 << 6)|(1 << 7)) -#define SDHC_CMD_TYPE_MASK ((1 << 6)|(1 << 7)) -#define SDHC_COMMAND_TYPE(x) ((x) & SDHC_CMD_TYPE_MASK) - -/* ROC Response Register 0 0x0 */ -#define SDHC_RSPREG0 0x10 -/* ROC Response Register 1 0x0 */ -#define SDHC_RSPREG1 0x14 -/* ROC Response Register 2 0x0 */ -#define SDHC_RSPREG2 0x18 -/* ROC Response Register 3 0x0 */ -#define SDHC_RSPREG3 0x1C - -/* R/W Buffer Data Register 0x0 */ -#define SDHC_BDATA 0x20 - -/* R/ROC Present State Register 0x000A0000 */ -#define SDHC_PRNSTS 0x24 -#define SDHC_CMD_INHIBIT 0x00000001 -#define SDHC_DATA_INHIBIT 0x00000002 -#define SDHC_DAT_LINE_ACTIVE 0x00000004 -#define SDHC_DOING_WRITE 0x00000100 -#define SDHC_DOING_READ 0x00000200 -#define SDHC_SPACE_AVAILABLE 0x00000400 -#define SDHC_DATA_AVAILABLE 0x00000800 -#define SDHC_CARD_PRESENT 0x00010000 -#define SDHC_CARD_DETECT 0x00040000 -#define SDHC_WRITE_PROTECT 0x00080000 -#define TRANSFERRING_DATA(x) \ - ((x) & (SDHC_DOING_READ | SDHC_DOING_WRITE)) - -/* R/W Host control Register 0x0 */ -#define SDHC_HOSTCTL 0x28 -#define SDHC_CTRL_DMA_CHECK_MASK 0x18 -#define SDHC_CTRL_SDMA 0x00 -#define SDHC_CTRL_ADMA1_32 0x08 -#define SDHC_CTRL_ADMA2_32 0x10 -#define SDHC_CTRL_ADMA2_64 0x18 -#define SDHC_DMA_TYPE(x) ((x) & SDHC_CTRL_DMA_CHECK_MASK) - -/* R/W Power Control Register 0x0 */ -#define SDHC_PWRCON 0x29 -#define SDHC_POWER_ON (1 << 0) - -/* R/W Block Gap Control Register 0x0 */ -#define SDHC_BLKGAP 0x2A -#define SDHC_STOP_AT_GAP_REQ 0x01 -#define SDHC_CONTINUE_REQ 0x02 - -/* R/W WakeUp Control Register 0x0 */ -#define SDHC_WAKCON 0x2B -#define SDHC_WKUP_ON_INS (1 << 1) -#define SDHC_WKUP_ON_RMV (1 << 2) - -/* CLKCON */ -#define SDHC_CLKCON 0x2C -#define SDHC_CLOCK_INT_STABLE 0x0002 -#define SDHC_CLOCK_INT_EN 0x0001 -#define SDHC_CLOCK_SDCLK_EN (1 << 2) -#define SDHC_CLOCK_CHK_MASK 0x0007 -#define SDHC_CLOCK_IS_ON(x) \ - (((x) & SDHC_CLOCK_CHK_MASK) == SDHC_CLOCK_CHK_MASK) - -/* R/W Timeout Control Register 0x0 */ -#define SDHC_TIMEOUTCON 0x2E - -/* R/W Software Reset Register 0x0 */ -#define SDHC_SWRST 0x2F -#define SDHC_RESET_ALL 0x01 -#define SDHC_RESET_CMD 0x02 -#define SDHC_RESET_DATA 0x04 - -/* ROC/RW1C Normal Interrupt Status Register 0x0 */ -#define SDHC_NORINTSTS 0x30 -#define SDHC_NIS_ERR 0x8000 -#define SDHC_NIS_CMDCMP 0x0001 -#define SDHC_NIS_TRSCMP 0x0002 -#define SDHC_NIS_BLKGAP 0x0004 -#define SDHC_NIS_DMA 0x0008 -#define SDHC_NIS_WBUFRDY 0x0010 -#define SDHC_NIS_RBUFRDY 0x0020 -#define SDHC_NIS_INSERT 0x0040 -#define SDHC_NIS_REMOVE 0x0080 -#define SDHC_NIS_CARDINT 0x0100 - -/* ROC/RW1C Error Interrupt Status Register 0x0 */ -#define SDHC_ERRINTSTS 0x32 -#define SDHC_EIS_CMDTIMEOUT 0x0001 -#define SDHC_EIS_BLKGAP 0x0004 -#define SDHC_EIS_CMDIDX 0x0008 -#define SDHC_EIS_CMD12ERR 0x0100 -#define SDHC_EIS_ADMAERR 0x0200 - -/* R/W Normal Interrupt Status Enable Register 0x0 */ -#define SDHC_NORINTSTSEN 0x34 -#define SDHC_NISEN_CMDCMP 0x0001 -#define SDHC_NISEN_TRSCMP 0x0002 -#define SDHC_NISEN_DMA 0x0008 -#define SDHC_NISEN_WBUFRDY 0x0010 -#define SDHC_NISEN_RBUFRDY 0x0020 -#define SDHC_NISEN_INSERT 0x0040 -#define SDHC_NISEN_REMOVE 0x0080 -#define SDHC_NISEN_CARDINT 0x0100 - -/* R/W Error Interrupt Status Enable Register 0x0 */ -#define SDHC_ERRINTSTSEN 0x36 -#define SDHC_EISEN_CMDTIMEOUT 0x0001 -#define SDHC_EISEN_BLKGAP 0x0004 -#define SDHC_EISEN_CMDIDX 0x0008 -#define SDHC_EISEN_ADMAERR 0x0200 - -/* R/W Normal Interrupt Signal Enable Register 0x0 */ -#define SDHC_NORINTSIGEN 0x38 -#define SDHC_NORINTSIG_INSERT (1 << 6) -#define SDHC_NORINTSIG_REMOVE (1 << 7) - -/* R/W Error Interrupt Signal Enable Register 0x0 */ -#define SDHC_ERRINTSIGEN 0x3A - -/* ROC Auto CMD12 error status register 0x0 */ -#define SDHC_ACMD12ERRSTS 0x3C - -/* HWInit Capabilities Register 0x05E80080 */ -#define SDHC_CAPAREG 0x40 -#define SDHC_CAN_DO_DMA 0x00400000 -#define SDHC_CAN_DO_ADMA2 0x00080000 -#define SDHC_CAN_DO_ADMA1 0x00100000 -#define SDHC_64_BIT_BUS_SUPPORT (1 << 28) -#define SDHC_CAPAB_BLOCKSIZE(x) (((x) >> 16) & 0x3) - -/* HWInit Maximum Current Capabilities Register 0x0 */ -#define SDHC_MAXCURR 0x48 - -/* W Force Event Auto CMD12 Error Interrupt Register 0x0000 */ -#define SDHC_FEAER 0x50 -/* W Force Event Error Interrupt Register Error Interrupt 0x0000 */ -#define SDHC_FEERR 0x52 - -/* R/W ADMA Error Status Register 0x00 */ -#define SDHC_ADMAERR 0x54 -#define SDHC_ADMAERR_LENGTH_MISMATCH (1 << 2) -#define SDHC_ADMAERR_STATE_ST_STOP (0 << 0) -#define SDHC_ADMAERR_STATE_ST_FDS (1 << 0) -#define SDHC_ADMAERR_STATE_ST_TFR (3 << 0) -#define SDHC_ADMAERR_STATE_MASK (3 << 0) - -/* R/W ADMA System Address Register 0x00 */ -#define SDHC_ADMASYSADDR 0x58 -#define SDHC_ADMA_ATTR_SET_LEN (1 << 4) -#define SDHC_ADMA_ATTR_ACT_TRAN (1 << 5) -#define SDHC_ADMA_ATTR_ACT_LINK (3 << 4) -#define SDHC_ADMA_ATTR_INT (1 << 2) -#define SDHC_ADMA_ATTR_END (1 << 1) -#define SDHC_ADMA_ATTR_VALID (1 << 0) -#define SDHC_ADMA_ATTR_ACT_MASK ((1 << 4)|(1 << 5)) - -/* Slot interrupt status */ -#define SDHC_SLOT_INT_STATUS 0xFC - -/* HWInit Host Controller Version Register 0x0401 */ -#define SDHC_HCVER 0xFE -#define SD_HOST_SPECv2_VERS 0x2401 - -#define SDHC_REGISTERS_MAP_SIZE 0x100 -#define SDHC_INSERTION_DELAY (get_ticks_per_sec()) -#define SDHC_TRANSFER_DELAY 100 -#define SDHC_ADMA_DESCS_PER_DELAY 5 -#define SDHC_CMD_RESPONSE (3 << 0) - -enum { - sdhc_not_stopped = 0, /* normal SDHC state */ - sdhc_gap_read = 1, /* SDHC stopped at block gap during read operation */ - sdhc_gap_write = 2 /* SDHC stopped at block gap during write operation */ -}; - -/* SD/MMC host controller state */ -typedef struct SDHCIState { - union { - PCIDevice pcidev; - SysBusDevice busdev; - }; - SDState *card; - MemoryRegion iomem; - - QEMUTimer *insert_timer; /* timer for 'changing' sd card. */ - QEMUTimer *transfer_timer; - qemu_irq eject_cb; - qemu_irq ro_cb; - qemu_irq irq; - - uint32_t sdmasysad; /* SDMA System Address register */ - uint16_t blksize; /* Host DMA Buff Boundary and Transfer BlkSize Reg */ - uint16_t blkcnt; /* Blocks count for current transfer */ - uint32_t argument; /* Command Argument Register */ - uint16_t trnmod; /* Transfer Mode Setting Register */ - uint16_t cmdreg; /* Command Register */ - uint32_t rspreg[4]; /* Response Registers 0-3 */ - uint32_t prnsts; /* Present State Register */ - uint8_t hostctl; /* Host Control Register */ - uint8_t pwrcon; /* Power control Register */ - uint8_t blkgap; /* Block Gap Control Register */ - uint8_t wakcon; /* WakeUp Control Register */ - uint16_t clkcon; /* Clock control Register */ - uint8_t timeoutcon; /* Timeout Control Register */ - uint8_t admaerr; /* ADMA Error Status Register */ - uint16_t norintsts; /* Normal Interrupt Status Register */ - uint16_t errintsts; /* Error Interrupt Status Register */ - uint16_t norintstsen; /* Normal Interrupt Status Enable Register */ - uint16_t errintstsen; /* Error Interrupt Status Enable Register */ - uint16_t norintsigen; /* Normal Interrupt Signal Enable Register */ - uint16_t errintsigen; /* Error Interrupt Signal Enable Register */ - uint16_t acmd12errsts; /* Auto CMD12 error status register */ - uint64_t admasysaddr; /* ADMA System Address Register */ - - uint32_t capareg; /* Capabilities Register */ - uint32_t maxcurr; /* Maximum Current Capabilities Register */ - uint8_t *fifo_buffer; /* SD host i/o FIFO buffer */ - uint32_t buf_maxsz; - uint16_t data_count; /* current element in FIFO buffer */ - uint8_t stopped_state;/* Current SDHC state */ - /* Buffer Data Port Register - virtual access point to R and W buffers */ - /* Software Reset Register - always reads as 0 */ - /* Force Event Auto CMD12 Error Interrupt Reg - write only */ - /* Force Event Error Interrupt Register- write only */ - /* RO Host Controller Version Register always reads as 0x2401 */ -} SDHCIState; - -extern const VMStateDescription sdhci_vmstate; - -#define TYPE_PCI_SDHCI "sdhci-pci" -#define PCI_SDHCI(obj) OBJECT_CHECK(SDHCIState, (obj), TYPE_PCI_SDHCI) - -#define TYPE_SYSBUS_SDHCI "generic-sdhci" -#define SYSBUS_SDHCI(obj) \ - OBJECT_CHECK(SDHCIState, (obj), TYPE_SYSBUS_SDHCI) - -#endif /* SDHCI_H */ diff --git a/qemu/hw/sd/ssi-sd.c b/qemu/hw/sd/ssi-sd.c index e4b2d4f83..075e4ed5d 100644 --- a/qemu/hw/sd/ssi-sd.c +++ b/qemu/hw/sd/ssi-sd.c @@ -10,10 +10,11 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" -#include "hw/ssi.h" -#include "hw/sd.h" +#include "hw/ssi/ssi.h" +#include "hw/sd/sd.h" //#define DEBUG_SSI_SD 1 diff --git a/qemu/hw/sh4/r2d.c b/qemu/hw/sh4/r2d.c index 5e22ed79b..db373c70c 100644 --- a/qemu/hw/sh4/r2d.c +++ b/qemu/hw/sh4/r2d.c @@ -23,6 +23,10 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "hw/hw.h" #include "hw/sh4/sh.h" @@ -255,7 +259,7 @@ static void r2d_init(MachineState *machine) qemu_register_reset(main_cpu_reset, reset_info); /* Allocate memory space */ - memory_region_init_ram(sdram, NULL, "r2d.sdram", SDRAM_SIZE, &error_abort); + memory_region_init_ram(sdram, NULL, "r2d.sdram", SDRAM_SIZE, &error_fatal); vmstate_register_ram_global(sdram); memory_region_add_subregion(address_space_mem, SDRAM_BASE, sdram); /* Register peripherals */ @@ -338,9 +342,9 @@ static void r2d_init(MachineState *machine) } /* initialization which should be done by firmware */ - boot_params.loader_type = 1; - boot_params.initrd_start = INITRD_LOAD_OFFSET; - boot_params.initrd_size = initrd_size; + boot_params.loader_type = tswap32(1); + boot_params.initrd_start = tswap32(INITRD_LOAD_OFFSET); + boot_params.initrd_size = tswap32(initrd_size); } if (kernel_cmdline) { @@ -354,15 +358,10 @@ static void r2d_init(MachineState *machine) SDRAM_BASE + BOOT_PARAMS_OFFSET); } -static QEMUMachine r2d_machine = { - .name = "r2d", - .desc = "r2d-plus board", - .init = r2d_init, -}; - -static void r2d_machine_init(void) +static void r2d_machine_init(MachineClass *mc) { - qemu_register_machine(&r2d_machine); + mc->desc = "r2d-plus board"; + mc->init = r2d_init; } -machine_init(r2d_machine_init); +DEFINE_MACHINE("r2d", r2d_machine_init) diff --git a/qemu/hw/sh4/sh7750.c b/qemu/hw/sh4/sh7750.c index 5dda5de34..a1ea760f6 100644 --- a/qemu/hw/sh4/sh7750.c +++ b/qemu/hw/sh4/sh7750.c @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sh4/sh.h" #include "sysemu/sysemu.h" diff --git a/qemu/hw/sh4/sh7750_regnames.c b/qemu/hw/sh4/sh7750_regnames.c index 52ac1cc78..34b4f99b8 100644 --- a/qemu/hw/sh4/sh7750_regnames.c +++ b/qemu/hw/sh4/sh7750_regnames.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sh4/sh.h" #include "sh7750_regs.h" diff --git a/qemu/hw/sh4/sh_pci.c b/qemu/hw/sh4/sh_pci.c index a2f6d9e0b..e820a3230 100644 --- a/qemu/hw/sh4/sh_pci.c +++ b/qemu/hw/sh4/sh_pci.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/sh4/sh.h" #include "hw/pci/pci.h" @@ -151,12 +152,11 @@ static int sh_pci_device_init(SysBusDevice *dev) return 0; } -static int sh_pci_host_init(PCIDevice *d) +static void sh_pci_host_realize(PCIDevice *d, Error **errp) { pci_set_word(d->config + PCI_COMMAND, PCI_COMMAND_WAIT); pci_set_word(d->config + PCI_STATUS, PCI_STATUS_CAP_LIST | PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); - return 0; } static void sh_pci_host_class_init(ObjectClass *klass, void *data) @@ -164,7 +164,7 @@ static void sh_pci_host_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = sh_pci_host_init; + k->realize = sh_pci_host_realize; k->vendor_id = PCI_VENDOR_ID_HITACHI; k->device_id = PCI_DEVICE_ID_HITACHI_SH7751R; /* diff --git a/qemu/hw/sh4/shix.c b/qemu/hw/sh4/shix.c index f93f98e56..ccc9e7589 100644 --- a/qemu/hw/sh4/shix.c +++ b/qemu/hw/sh4/shix.c @@ -27,6 +27,10 @@ More information in target-sh4/README.sh4 */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/sh4/sh.h" #include "sysemu/sysemu.h" @@ -59,16 +63,16 @@ static void shix_init(MachineState *machine) } /* Allocate memory space */ - memory_region_init_ram(rom, NULL, "shix.rom", 0x4000, &error_abort); + memory_region_init_ram(rom, NULL, "shix.rom", 0x4000, &error_fatal); vmstate_register_ram_global(rom); memory_region_set_readonly(rom, true); memory_region_add_subregion(sysmem, 0x00000000, rom); memory_region_init_ram(&sdram[0], NULL, "shix.sdram1", 0x01000000, - &error_abort); + &error_fatal); vmstate_register_ram_global(&sdram[0]); memory_region_add_subregion(sysmem, 0x08000000, &sdram[0]); memory_region_init_ram(&sdram[1], NULL, "shix.sdram2", 0x01000000, - &error_abort); + &error_fatal); vmstate_register_ram_global(&sdram[1]); memory_region_add_subregion(sysmem, 0x0c000000, &sdram[1]); @@ -87,16 +91,11 @@ static void shix_init(MachineState *machine) tc58128_init(s, "shix_linux_nand.bin", NULL); } -static QEMUMachine shix_machine = { - .name = "shix", - .desc = "shix card", - .init = shix_init, - .is_default = 1, -}; - -static void shix_machine_init(void) +static void shix_machine_init(MachineClass *mc) { - qemu_register_machine(&shix_machine); + mc->desc = "shix card"; + mc->init = shix_init; + mc->is_default = 1; } -machine_init(shix_machine_init); +DEFINE_MACHINE("shix", shix_machine_init) diff --git a/qemu/hw/smbios/Makefile.objs b/qemu/hw/smbios/Makefile.objs new file mode 100644 index 000000000..f69a92f96 --- /dev/null +++ b/qemu/hw/smbios/Makefile.objs @@ -0,0 +1 @@ +common-obj-$(CONFIG_SMBIOS) += smbios.o diff --git a/qemu/hw/smbios/smbios.c b/qemu/hw/smbios/smbios.c new file mode 100644 index 000000000..cb8a11110 --- /dev/null +++ b/qemu/hw/smbios/smbios.c @@ -0,0 +1,1112 @@ +/* + * SMBIOS Support + * + * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. + * Copyright (C) 2013 Red Hat, Inc. + * + * Authors: + * Alex Williamson + * Markus Armbruster + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/config-file.h" +#include "qemu/error-report.h" +#include "sysemu/sysemu.h" +#include "sysemu/cpus.h" +#include "hw/smbios/smbios.h" +#include "hw/loader.h" +#include "exec/cpu-common.h" + +/* legacy structures and constants for <= 2.0 machines */ +struct smbios_header { + uint16_t length; + uint8_t type; +} QEMU_PACKED; + +struct smbios_field { + struct smbios_header header; + uint8_t type; + uint16_t offset; + uint8_t data[]; +} QEMU_PACKED; + +struct smbios_table { + struct smbios_header header; + uint8_t data[]; +} QEMU_PACKED; + +#define SMBIOS_FIELD_ENTRY 0 +#define SMBIOS_TABLE_ENTRY 1 + +static uint8_t *smbios_entries; +static size_t smbios_entries_len; +static bool smbios_legacy = true; +static bool smbios_uuid_encoded = true; +/* end: legacy structures & constants for <= 2.0 machines */ + + +static uint8_t *smbios_tables; +static size_t smbios_tables_len; +static unsigned smbios_table_max; +static unsigned smbios_table_cnt; +static SmbiosEntryPointType smbios_ep_type = SMBIOS_ENTRY_POINT_21; + +static SmbiosEntryPoint ep; + +static int smbios_type4_count = 0; +static bool smbios_immutable; +static bool smbios_have_defaults; +static uint32_t smbios_cpuid_version, smbios_cpuid_features, smbios_smp_sockets; + +static DECLARE_BITMAP(have_binfile_bitmap, SMBIOS_MAX_TYPE+1); +static DECLARE_BITMAP(have_fields_bitmap, SMBIOS_MAX_TYPE+1); + +static struct { + const char *vendor, *version, *date; + bool have_major_minor, uefi; + uint8_t major, minor; +} type0; + +static struct { + const char *manufacturer, *product, *version, *serial, *sku, *family; + /* uuid is in qemu_uuid[] */ +} type1; + +static struct { + const char *manufacturer, *product, *version, *serial, *asset, *location; +} type2; + +static struct { + const char *manufacturer, *version, *serial, *asset, *sku; +} type3; + +static struct { + const char *sock_pfx, *manufacturer, *version, *serial, *asset, *part; +} type4; + +static struct { + const char *loc_pfx, *bank, *manufacturer, *serial, *asset, *part; + uint16_t speed; +} type17; + +static QemuOptsList qemu_smbios_opts = { + .name = "smbios", + .head = QTAILQ_HEAD_INITIALIZER(qemu_smbios_opts.head), + .desc = { + /* + * no elements => accept any params + * validation will happen later + */ + { /* end of list */ } + } +}; + +static const QemuOptDesc qemu_smbios_file_opts[] = { + { + .name = "file", + .type = QEMU_OPT_STRING, + .help = "binary file containing an SMBIOS element", + }, + { /* end of list */ } +}; + +static const QemuOptDesc qemu_smbios_type0_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + },{ + .name = "vendor", + .type = QEMU_OPT_STRING, + .help = "vendor name", + },{ + .name = "version", + .type = QEMU_OPT_STRING, + .help = "version number", + },{ + .name = "date", + .type = QEMU_OPT_STRING, + .help = "release date", + },{ + .name = "release", + .type = QEMU_OPT_STRING, + .help = "revision number", + },{ + .name = "uefi", + .type = QEMU_OPT_BOOL, + .help = "uefi support", + }, + { /* end of list */ } +}; + +static const QemuOptDesc qemu_smbios_type1_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + },{ + .name = "manufacturer", + .type = QEMU_OPT_STRING, + .help = "manufacturer name", + },{ + .name = "product", + .type = QEMU_OPT_STRING, + .help = "product name", + },{ + .name = "version", + .type = QEMU_OPT_STRING, + .help = "version number", + },{ + .name = "serial", + .type = QEMU_OPT_STRING, + .help = "serial number", + },{ + .name = "uuid", + .type = QEMU_OPT_STRING, + .help = "UUID", + },{ + .name = "sku", + .type = QEMU_OPT_STRING, + .help = "SKU number", + },{ + .name = "family", + .type = QEMU_OPT_STRING, + .help = "family name", + }, + { /* end of list */ } +}; + +static const QemuOptDesc qemu_smbios_type2_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + },{ + .name = "manufacturer", + .type = QEMU_OPT_STRING, + .help = "manufacturer name", + },{ + .name = "product", + .type = QEMU_OPT_STRING, + .help = "product name", + },{ + .name = "version", + .type = QEMU_OPT_STRING, + .help = "version number", + },{ + .name = "serial", + .type = QEMU_OPT_STRING, + .help = "serial number", + },{ + .name = "asset", + .type = QEMU_OPT_STRING, + .help = "asset tag number", + },{ + .name = "location", + .type = QEMU_OPT_STRING, + .help = "location in chassis", + }, + { /* end of list */ } +}; + +static const QemuOptDesc qemu_smbios_type3_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + },{ + .name = "manufacturer", + .type = QEMU_OPT_STRING, + .help = "manufacturer name", + },{ + .name = "version", + .type = QEMU_OPT_STRING, + .help = "version number", + },{ + .name = "serial", + .type = QEMU_OPT_STRING, + .help = "serial number", + },{ + .name = "asset", + .type = QEMU_OPT_STRING, + .help = "asset tag number", + },{ + .name = "sku", + .type = QEMU_OPT_STRING, + .help = "SKU number", + }, + { /* end of list */ } +}; + +static const QemuOptDesc qemu_smbios_type4_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + },{ + .name = "sock_pfx", + .type = QEMU_OPT_STRING, + .help = "socket designation string prefix", + },{ + .name = "manufacturer", + .type = QEMU_OPT_STRING, + .help = "manufacturer name", + },{ + .name = "version", + .type = QEMU_OPT_STRING, + .help = "version number", + },{ + .name = "serial", + .type = QEMU_OPT_STRING, + .help = "serial number", + },{ + .name = "asset", + .type = QEMU_OPT_STRING, + .help = "asset tag number", + },{ + .name = "part", + .type = QEMU_OPT_STRING, + .help = "part number", + }, + { /* end of list */ } +}; + +static const QemuOptDesc qemu_smbios_type17_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + },{ + .name = "loc_pfx", + .type = QEMU_OPT_STRING, + .help = "device locator string prefix", + },{ + .name = "bank", + .type = QEMU_OPT_STRING, + .help = "bank locator string", + },{ + .name = "manufacturer", + .type = QEMU_OPT_STRING, + .help = "manufacturer name", + },{ + .name = "serial", + .type = QEMU_OPT_STRING, + .help = "serial number", + },{ + .name = "asset", + .type = QEMU_OPT_STRING, + .help = "asset tag number", + },{ + .name = "part", + .type = QEMU_OPT_STRING, + .help = "part number", + },{ + .name = "speed", + .type = QEMU_OPT_NUMBER, + .help = "maximum capable speed", + }, + { /* end of list */ } +}; + +static void smbios_register_config(void) +{ + qemu_add_opts(&qemu_smbios_opts); +} + +opts_init(smbios_register_config); + +static void smbios_validate_table(void) +{ + uint32_t expect_t4_count = smbios_legacy ? smp_cpus : smbios_smp_sockets; + + if (smbios_type4_count && smbios_type4_count != expect_t4_count) { + error_report("Expected %d SMBIOS Type 4 tables, got %d instead", + expect_t4_count, smbios_type4_count); + exit(1); + } +} + + +/* legacy setup functions for <= 2.0 machines */ +static void smbios_add_field(int type, int offset, const void *data, size_t len) +{ + struct smbios_field *field; + + if (!smbios_entries) { + smbios_entries_len = sizeof(uint16_t); + smbios_entries = g_malloc0(smbios_entries_len); + } + smbios_entries = g_realloc(smbios_entries, smbios_entries_len + + sizeof(*field) + len); + field = (struct smbios_field *)(smbios_entries + smbios_entries_len); + field->header.type = SMBIOS_FIELD_ENTRY; + field->header.length = cpu_to_le16(sizeof(*field) + len); + + field->type = type; + field->offset = cpu_to_le16(offset); + memcpy(field->data, data, len); + + smbios_entries_len += sizeof(*field) + len; + (*(uint16_t *)smbios_entries) = + cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); +} + +static void smbios_maybe_add_str(int type, int offset, const char *data) +{ + if (data) { + smbios_add_field(type, offset, data, strlen(data) + 1); + } +} + +static void smbios_build_type_0_fields(void) +{ + smbios_maybe_add_str(0, offsetof(struct smbios_type_0, vendor_str), + type0.vendor); + smbios_maybe_add_str(0, offsetof(struct smbios_type_0, bios_version_str), + type0.version); + smbios_maybe_add_str(0, offsetof(struct smbios_type_0, + bios_release_date_str), + type0.date); + if (type0.have_major_minor) { + smbios_add_field(0, offsetof(struct smbios_type_0, + system_bios_major_release), + &type0.major, 1); + smbios_add_field(0, offsetof(struct smbios_type_0, + system_bios_minor_release), + &type0.minor, 1); + } +} + +static void smbios_build_type_1_fields(void) +{ + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, manufacturer_str), + type1.manufacturer); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, product_name_str), + type1.product); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, version_str), + type1.version); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, serial_number_str), + type1.serial); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, sku_number_str), + type1.sku); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, family_str), + type1.family); + if (qemu_uuid_set) { + /* We don't encode the UUID in the "wire format" here because this + * function is for legacy mode and needs to keep the guest ABI, and + * because we don't know what's the SMBIOS version advertised by the + * BIOS. + */ + smbios_add_field(1, offsetof(struct smbios_type_1, uuid), + qemu_uuid, 16); + } +} + +uint8_t *smbios_get_table_legacy(size_t *length) +{ + if (!smbios_legacy) { + *length = 0; + return NULL; + } + + if (!smbios_immutable) { + smbios_build_type_0_fields(); + smbios_build_type_1_fields(); + smbios_validate_table(); + smbios_immutable = true; + } + *length = smbios_entries_len; + return smbios_entries; +} +/* end: legacy setup functions for <= 2.0 machines */ + + +static bool smbios_skip_table(uint8_t type, bool required_table) +{ + if (test_bit(type, have_binfile_bitmap)) { + return true; /* user provided their own binary blob(s) */ + } + if (test_bit(type, have_fields_bitmap)) { + return false; /* user provided fields via command line */ + } + if (smbios_have_defaults && required_table) { + return false; /* we're building tables, and this one's required */ + } + return true; +} + +#define SMBIOS_BUILD_TABLE_PRE(tbl_type, tbl_handle, tbl_required) \ + struct smbios_type_##tbl_type *t; \ + size_t t_off; /* table offset into smbios_tables */ \ + int str_index = 0; \ + do { \ + /* should we skip building this table ? */ \ + if (smbios_skip_table(tbl_type, tbl_required)) { \ + return; \ + } \ + \ + /* use offset of table t within smbios_tables */ \ + /* (pointer must be updated after each realloc) */ \ + t_off = smbios_tables_len; \ + smbios_tables_len += sizeof(*t); \ + smbios_tables = g_realloc(smbios_tables, smbios_tables_len); \ + t = (struct smbios_type_##tbl_type *)(smbios_tables + t_off); \ + \ + t->header.type = tbl_type; \ + t->header.length = sizeof(*t); \ + t->header.handle = cpu_to_le16(tbl_handle); \ + } while (0) + +#define SMBIOS_TABLE_SET_STR(tbl_type, field, value) \ + do { \ + int len = (value != NULL) ? strlen(value) + 1 : 0; \ + if (len > 1) { \ + smbios_tables = g_realloc(smbios_tables, \ + smbios_tables_len + len); \ + memcpy(smbios_tables + smbios_tables_len, value, len); \ + smbios_tables_len += len; \ + /* update pointer post-realloc */ \ + t = (struct smbios_type_##tbl_type *)(smbios_tables + t_off); \ + t->field = ++str_index; \ + } else { \ + t->field = 0; \ + } \ + } while (0) + +#define SMBIOS_BUILD_TABLE_POST \ + do { \ + size_t term_cnt, t_size; \ + \ + /* add '\0' terminator (add two if no strings defined) */ \ + term_cnt = (str_index == 0) ? 2 : 1; \ + smbios_tables = g_realloc(smbios_tables, \ + smbios_tables_len + term_cnt); \ + memset(smbios_tables + smbios_tables_len, 0, term_cnt); \ + smbios_tables_len += term_cnt; \ + \ + /* update smbios max. element size */ \ + t_size = smbios_tables_len - t_off; \ + if (t_size > smbios_table_max) { \ + smbios_table_max = t_size; \ + } \ + \ + /* update smbios element count */ \ + smbios_table_cnt++; \ + } while (0) + +static void smbios_build_type_0_table(void) +{ + SMBIOS_BUILD_TABLE_PRE(0, 0x000, false); /* optional, leave up to BIOS */ + + SMBIOS_TABLE_SET_STR(0, vendor_str, type0.vendor); + SMBIOS_TABLE_SET_STR(0, bios_version_str, type0.version); + + t->bios_starting_address_segment = cpu_to_le16(0xE800); /* from SeaBIOS */ + + SMBIOS_TABLE_SET_STR(0, bios_release_date_str, type0.date); + + t->bios_rom_size = 0; /* hardcoded in SeaBIOS with FIXME comment */ + + t->bios_characteristics = cpu_to_le64(0x08); /* Not supported */ + t->bios_characteristics_extension_bytes[0] = 0; + t->bios_characteristics_extension_bytes[1] = 0x14; /* TCD/SVVP | VM */ + if (type0.uefi) { + t->bios_characteristics_extension_bytes[1] |= 0x08; /* |= UEFI */ + } + + if (type0.have_major_minor) { + t->system_bios_major_release = type0.major; + t->system_bios_minor_release = type0.minor; + } else { + t->system_bios_major_release = 0; + t->system_bios_minor_release = 0; + } + + /* hardcoded in SeaBIOS */ + t->embedded_controller_major_release = 0xFF; + t->embedded_controller_minor_release = 0xFF; + + SMBIOS_BUILD_TABLE_POST; +} + +/* Encode UUID from the big endian encoding described on RFC4122 to the wire + * format specified by SMBIOS version 2.6. + */ +static void smbios_encode_uuid(struct smbios_uuid *uuid, const uint8_t *buf) +{ + memcpy(uuid, buf, 16); + if (smbios_uuid_encoded) { + uuid->time_low = bswap32(uuid->time_low); + uuid->time_mid = bswap16(uuid->time_mid); + uuid->time_hi_and_version = bswap16(uuid->time_hi_and_version); + } +} + +static void smbios_build_type_1_table(void) +{ + SMBIOS_BUILD_TABLE_PRE(1, 0x100, true); /* required */ + + SMBIOS_TABLE_SET_STR(1, manufacturer_str, type1.manufacturer); + SMBIOS_TABLE_SET_STR(1, product_name_str, type1.product); + SMBIOS_TABLE_SET_STR(1, version_str, type1.version); + SMBIOS_TABLE_SET_STR(1, serial_number_str, type1.serial); + if (qemu_uuid_set) { + smbios_encode_uuid(&t->uuid, qemu_uuid); + } else { + memset(&t->uuid, 0, 16); + } + t->wake_up_type = 0x06; /* power switch */ + SMBIOS_TABLE_SET_STR(1, sku_number_str, type1.sku); + SMBIOS_TABLE_SET_STR(1, family_str, type1.family); + + SMBIOS_BUILD_TABLE_POST; +} + +static void smbios_build_type_2_table(void) +{ + SMBIOS_BUILD_TABLE_PRE(2, 0x200, false); /* optional */ + + SMBIOS_TABLE_SET_STR(2, manufacturer_str, type2.manufacturer); + SMBIOS_TABLE_SET_STR(2, product_str, type2.product); + SMBIOS_TABLE_SET_STR(2, version_str, type2.version); + SMBIOS_TABLE_SET_STR(2, serial_number_str, type2.serial); + SMBIOS_TABLE_SET_STR(2, asset_tag_number_str, type2.asset); + t->feature_flags = 0x01; /* Motherboard */ + SMBIOS_TABLE_SET_STR(2, location_str, type2.location); + t->chassis_handle = cpu_to_le16(0x300); /* Type 3 (System enclosure) */ + t->board_type = 0x0A; /* Motherboard */ + t->contained_element_count = 0; + + SMBIOS_BUILD_TABLE_POST; +} + +static void smbios_build_type_3_table(void) +{ + SMBIOS_BUILD_TABLE_PRE(3, 0x300, true); /* required */ + + SMBIOS_TABLE_SET_STR(3, manufacturer_str, type3.manufacturer); + t->type = 0x01; /* Other */ + SMBIOS_TABLE_SET_STR(3, version_str, type3.version); + SMBIOS_TABLE_SET_STR(3, serial_number_str, type3.serial); + SMBIOS_TABLE_SET_STR(3, asset_tag_number_str, type3.asset); + t->boot_up_state = 0x03; /* Safe */ + t->power_supply_state = 0x03; /* Safe */ + t->thermal_state = 0x03; /* Safe */ + t->security_status = 0x02; /* Unknown */ + t->oem_defined = cpu_to_le32(0); + t->height = 0; + t->number_of_power_cords = 0; + t->contained_element_count = 0; + SMBIOS_TABLE_SET_STR(3, sku_number_str, type3.sku); + + SMBIOS_BUILD_TABLE_POST; +} + +static void smbios_build_type_4_table(unsigned instance) +{ + char sock_str[128]; + + SMBIOS_BUILD_TABLE_PRE(4, 0x400 + instance, true); /* required */ + + snprintf(sock_str, sizeof(sock_str), "%s%2x", type4.sock_pfx, instance); + SMBIOS_TABLE_SET_STR(4, socket_designation_str, sock_str); + t->processor_type = 0x03; /* CPU */ + t->processor_family = 0x01; /* Other */ + SMBIOS_TABLE_SET_STR(4, processor_manufacturer_str, type4.manufacturer); + t->processor_id[0] = cpu_to_le32(smbios_cpuid_version); + t->processor_id[1] = cpu_to_le32(smbios_cpuid_features); + SMBIOS_TABLE_SET_STR(4, processor_version_str, type4.version); + t->voltage = 0; + t->external_clock = cpu_to_le16(0); /* Unknown */ + /* SVVP requires max_speed and current_speed to not be unknown. */ + t->max_speed = cpu_to_le16(2000); /* 2000 MHz */ + t->current_speed = cpu_to_le16(2000); /* 2000 MHz */ + t->status = 0x41; /* Socket populated, CPU enabled */ + t->processor_upgrade = 0x01; /* Other */ + t->l1_cache_handle = cpu_to_le16(0xFFFF); /* N/A */ + t->l2_cache_handle = cpu_to_le16(0xFFFF); /* N/A */ + t->l3_cache_handle = cpu_to_le16(0xFFFF); /* N/A */ + SMBIOS_TABLE_SET_STR(4, serial_number_str, type4.serial); + SMBIOS_TABLE_SET_STR(4, asset_tag_number_str, type4.asset); + SMBIOS_TABLE_SET_STR(4, part_number_str, type4.part); + t->core_count = t->core_enabled = smp_cores; + t->thread_count = smp_threads; + t->processor_characteristics = cpu_to_le16(0x02); /* Unknown */ + t->processor_family2 = cpu_to_le16(0x01); /* Other */ + + SMBIOS_BUILD_TABLE_POST; + smbios_type4_count++; +} + +#define ONE_KB ((ram_addr_t)1 << 10) +#define ONE_MB ((ram_addr_t)1 << 20) +#define ONE_GB ((ram_addr_t)1 << 30) + +#define MAX_T16_STD_SZ 0x80000000 /* 2T in Kilobytes */ + +static void smbios_build_type_16_table(unsigned dimm_cnt) +{ + uint64_t size_kb; + + SMBIOS_BUILD_TABLE_PRE(16, 0x1000, true); /* required */ + + t->location = 0x01; /* Other */ + t->use = 0x03; /* System memory */ + t->error_correction = 0x06; /* Multi-bit ECC (for Microsoft, per SeaBIOS) */ + size_kb = QEMU_ALIGN_UP(ram_size, ONE_KB) / ONE_KB; + if (size_kb < MAX_T16_STD_SZ) { + t->maximum_capacity = cpu_to_le32(size_kb); + t->extended_maximum_capacity = cpu_to_le64(0); + } else { + t->maximum_capacity = cpu_to_le32(MAX_T16_STD_SZ); + t->extended_maximum_capacity = cpu_to_le64(ram_size); + } + t->memory_error_information_handle = cpu_to_le16(0xFFFE); /* Not provided */ + t->number_of_memory_devices = cpu_to_le16(dimm_cnt); + + SMBIOS_BUILD_TABLE_POST; +} + +#define MAX_T17_STD_SZ 0x7FFF /* (32G - 1M), in Megabytes */ +#define MAX_T17_EXT_SZ 0x80000000 /* 2P, in Megabytes */ + +static void smbios_build_type_17_table(unsigned instance, uint64_t size) +{ + char loc_str[128]; + uint64_t size_mb; + + SMBIOS_BUILD_TABLE_PRE(17, 0x1100 + instance, true); /* required */ + + t->physical_memory_array_handle = cpu_to_le16(0x1000); /* Type 16 above */ + t->memory_error_information_handle = cpu_to_le16(0xFFFE); /* Not provided */ + t->total_width = cpu_to_le16(0xFFFF); /* Unknown */ + t->data_width = cpu_to_le16(0xFFFF); /* Unknown */ + size_mb = QEMU_ALIGN_UP(size, ONE_MB) / ONE_MB; + if (size_mb < MAX_T17_STD_SZ) { + t->size = cpu_to_le16(size_mb); + t->extended_size = cpu_to_le32(0); + } else { + assert(size_mb < MAX_T17_EXT_SZ); + t->size = cpu_to_le16(MAX_T17_STD_SZ); + t->extended_size = cpu_to_le32(size_mb); + } + t->form_factor = 0x09; /* DIMM */ + t->device_set = 0; /* Not in a set */ + snprintf(loc_str, sizeof(loc_str), "%s %d", type17.loc_pfx, instance); + SMBIOS_TABLE_SET_STR(17, device_locator_str, loc_str); + SMBIOS_TABLE_SET_STR(17, bank_locator_str, type17.bank); + t->memory_type = 0x07; /* RAM */ + t->type_detail = cpu_to_le16(0x02); /* Other */ + t->speed = cpu_to_le16(type17.speed); + SMBIOS_TABLE_SET_STR(17, manufacturer_str, type17.manufacturer); + SMBIOS_TABLE_SET_STR(17, serial_number_str, type17.serial); + SMBIOS_TABLE_SET_STR(17, asset_tag_number_str, type17.asset); + SMBIOS_TABLE_SET_STR(17, part_number_str, type17.part); + t->attributes = 0; /* Unknown */ + t->configured_clock_speed = t->speed; /* reuse value for max speed */ + t->minimum_voltage = cpu_to_le16(0); /* Unknown */ + t->maximum_voltage = cpu_to_le16(0); /* Unknown */ + t->configured_voltage = cpu_to_le16(0); /* Unknown */ + + SMBIOS_BUILD_TABLE_POST; +} + +static void smbios_build_type_19_table(unsigned instance, + uint64_t start, uint64_t size) +{ + uint64_t end, start_kb, end_kb; + + SMBIOS_BUILD_TABLE_PRE(19, 0x1300 + instance, true); /* required */ + + end = start + size - 1; + assert(end > start); + start_kb = start / ONE_KB; + end_kb = end / ONE_KB; + if (start_kb < UINT32_MAX && end_kb < UINT32_MAX) { + t->starting_address = cpu_to_le32(start_kb); + t->ending_address = cpu_to_le32(end_kb); + t->extended_starting_address = + t->extended_ending_address = cpu_to_le64(0); + } else { + t->starting_address = t->ending_address = cpu_to_le32(UINT32_MAX); + t->extended_starting_address = cpu_to_le64(start); + t->extended_ending_address = cpu_to_le64(end); + } + t->memory_array_handle = cpu_to_le16(0x1000); /* Type 16 above */ + t->partition_width = 1; /* One device per row */ + + SMBIOS_BUILD_TABLE_POST; +} + +static void smbios_build_type_32_table(void) +{ + SMBIOS_BUILD_TABLE_PRE(32, 0x2000, true); /* required */ + + memset(t->reserved, 0, 6); + t->boot_status = 0; /* No errors detected */ + + SMBIOS_BUILD_TABLE_POST; +} + +static void smbios_build_type_127_table(void) +{ + SMBIOS_BUILD_TABLE_PRE(127, 0x7F00, true); /* required */ + SMBIOS_BUILD_TABLE_POST; +} + +void smbios_set_cpuid(uint32_t version, uint32_t features) +{ + smbios_cpuid_version = version; + smbios_cpuid_features = features; +} + +#define SMBIOS_SET_DEFAULT(field, value) \ + if (!field) { \ + field = value; \ + } + +void smbios_set_defaults(const char *manufacturer, const char *product, + const char *version, bool legacy_mode, + bool uuid_encoded, SmbiosEntryPointType ep_type) +{ + smbios_have_defaults = true; + smbios_legacy = legacy_mode; + smbios_uuid_encoded = uuid_encoded; + smbios_ep_type = ep_type; + + /* drop unwanted version of command-line file blob(s) */ + if (smbios_legacy) { + g_free(smbios_tables); + /* in legacy mode, also complain if fields were given for types > 1 */ + if (find_next_bit(have_fields_bitmap, + SMBIOS_MAX_TYPE+1, 2) < SMBIOS_MAX_TYPE+1) { + error_report("can't process fields for smbios " + "types > 1 on machine versions < 2.1!"); + exit(1); + } + } else { + g_free(smbios_entries); + } + + SMBIOS_SET_DEFAULT(type1.manufacturer, manufacturer); + SMBIOS_SET_DEFAULT(type1.product, product); + SMBIOS_SET_DEFAULT(type1.version, version); + SMBIOS_SET_DEFAULT(type2.manufacturer, manufacturer); + SMBIOS_SET_DEFAULT(type2.product, product); + SMBIOS_SET_DEFAULT(type2.version, version); + SMBIOS_SET_DEFAULT(type3.manufacturer, manufacturer); + SMBIOS_SET_DEFAULT(type3.version, version); + SMBIOS_SET_DEFAULT(type4.sock_pfx, "CPU"); + SMBIOS_SET_DEFAULT(type4.manufacturer, manufacturer); + SMBIOS_SET_DEFAULT(type4.version, version); + SMBIOS_SET_DEFAULT(type17.loc_pfx, "DIMM"); + SMBIOS_SET_DEFAULT(type17.manufacturer, manufacturer); +} + +static void smbios_entry_point_setup(void) +{ + switch (smbios_ep_type) { + case SMBIOS_ENTRY_POINT_21: + memcpy(ep.ep21.anchor_string, "_SM_", 4); + memcpy(ep.ep21.intermediate_anchor_string, "_DMI_", 5); + ep.ep21.length = sizeof(struct smbios_21_entry_point); + ep.ep21.entry_point_revision = 0; /* formatted_area reserved */ + memset(ep.ep21.formatted_area, 0, 5); + + /* compliant with smbios spec v2.8 */ + ep.ep21.smbios_major_version = 2; + ep.ep21.smbios_minor_version = 8; + ep.ep21.smbios_bcd_revision = 0x28; + + /* set during table construction, but BIOS may override: */ + ep.ep21.structure_table_length = cpu_to_le16(smbios_tables_len); + ep.ep21.max_structure_size = cpu_to_le16(smbios_table_max); + ep.ep21.number_of_structures = cpu_to_le16(smbios_table_cnt); + + /* BIOS must recalculate */ + ep.ep21.checksum = 0; + ep.ep21.intermediate_checksum = 0; + ep.ep21.structure_table_address = cpu_to_le32(0); + + break; + case SMBIOS_ENTRY_POINT_30: + memcpy(ep.ep30.anchor_string, "_SM3_", 5); + ep.ep30.length = sizeof(struct smbios_30_entry_point); + ep.ep30.entry_point_revision = 1; + ep.ep30.reserved = 0; + + /* compliant with smbios spec 3.0 */ + ep.ep30.smbios_major_version = 3; + ep.ep30.smbios_minor_version = 0; + ep.ep30.smbios_doc_rev = 0; + + /* set during table construct, but BIOS might override */ + ep.ep30.structure_table_max_size = cpu_to_le32(smbios_tables_len); + + /* BIOS must recalculate */ + ep.ep30.checksum = 0; + ep.ep30.structure_table_address = cpu_to_le64(0); + + break; + default: + abort(); + break; + } +} + +void smbios_get_tables(const struct smbios_phys_mem_area *mem_array, + const unsigned int mem_array_size, + uint8_t **tables, size_t *tables_len, + uint8_t **anchor, size_t *anchor_len) +{ + unsigned i, dimm_cnt; + + if (smbios_legacy) { + *tables = *anchor = NULL; + *tables_len = *anchor_len = 0; + return; + } + + if (!smbios_immutable) { + smbios_build_type_0_table(); + smbios_build_type_1_table(); + smbios_build_type_2_table(); + smbios_build_type_3_table(); + + smbios_smp_sockets = DIV_ROUND_UP(smp_cpus, smp_cores * smp_threads); + assert(smbios_smp_sockets >= 1); + + for (i = 0; i < smbios_smp_sockets; i++) { + smbios_build_type_4_table(i); + } + +#define MAX_DIMM_SZ (16ll * ONE_GB) +#define GET_DIMM_SZ ((i < dimm_cnt - 1) ? MAX_DIMM_SZ \ + : ((ram_size - 1) % MAX_DIMM_SZ) + 1) + + dimm_cnt = QEMU_ALIGN_UP(ram_size, MAX_DIMM_SZ) / MAX_DIMM_SZ; + + smbios_build_type_16_table(dimm_cnt); + + for (i = 0; i < dimm_cnt; i++) { + smbios_build_type_17_table(i, GET_DIMM_SZ); + } + + for (i = 0; i < mem_array_size; i++) { + smbios_build_type_19_table(i, mem_array[i].address, + mem_array[i].length); + } + + smbios_build_type_32_table(); + smbios_build_type_127_table(); + + smbios_validate_table(); + smbios_entry_point_setup(); + smbios_immutable = true; + } + + /* return tables blob and entry point (anchor), and their sizes */ + *tables = smbios_tables; + *tables_len = smbios_tables_len; + *anchor = (uint8_t *)&ep; + + /* calculate length based on anchor string */ + if (!strncmp((char *)&ep, "_SM_", 4)) { + *anchor_len = sizeof(struct smbios_21_entry_point); + } else if (!strncmp((char *)&ep, "_SM3_", 5)) { + *anchor_len = sizeof(struct smbios_30_entry_point); + } else { + abort(); + } +} + +static void save_opt(const char **dest, QemuOpts *opts, const char *name) +{ + const char *val = qemu_opt_get(opts, name); + + if (val) { + *dest = val; + } +} + +void smbios_entry_add(QemuOpts *opts) +{ + const char *val; + + assert(!smbios_immutable); + + val = qemu_opt_get(opts, "file"); + if (val) { + struct smbios_structure_header *header; + int size; + struct smbios_table *table; /* legacy mode only */ + + qemu_opts_validate(opts, qemu_smbios_file_opts, &error_fatal); + + size = get_image_size(val); + if (size == -1 || size < sizeof(struct smbios_structure_header)) { + error_report("Cannot read SMBIOS file %s", val); + exit(1); + } + + /* + * NOTE: standard double '\0' terminator expected, per smbios spec. + * (except in legacy mode, where the second '\0' is implicit and + * will be inserted by the BIOS). + */ + smbios_tables = g_realloc(smbios_tables, smbios_tables_len + size); + header = (struct smbios_structure_header *)(smbios_tables + + smbios_tables_len); + + if (load_image(val, (uint8_t *)header) != size) { + error_report("Failed to load SMBIOS file %s", val); + exit(1); + } + + if (test_bit(header->type, have_fields_bitmap)) { + error_report("can't load type %d struct, fields already specified!", + header->type); + exit(1); + } + set_bit(header->type, have_binfile_bitmap); + + if (header->type == 4) { + smbios_type4_count++; + } + + smbios_tables_len += size; + if (size > smbios_table_max) { + smbios_table_max = size; + } + smbios_table_cnt++; + + /* add a copy of the newly loaded blob to legacy smbios_entries */ + /* NOTE: This code runs before smbios_set_defaults(), so we don't + * yet know which mode (legacy vs. aggregate-table) will be + * required. We therefore add the binary blob to both legacy + * (smbios_entries) and aggregate (smbios_tables) tables, and + * delete the one we don't need from smbios_set_defaults(), + * once we know which machine version has been requested. + */ + if (!smbios_entries) { + smbios_entries_len = sizeof(uint16_t); + smbios_entries = g_malloc0(smbios_entries_len); + } + smbios_entries = g_realloc(smbios_entries, smbios_entries_len + + size + sizeof(*table)); + table = (struct smbios_table *)(smbios_entries + smbios_entries_len); + table->header.type = SMBIOS_TABLE_ENTRY; + table->header.length = cpu_to_le16(sizeof(*table) + size); + memcpy(table->data, header, size); + smbios_entries_len += sizeof(*table) + size; + (*(uint16_t *)smbios_entries) = + cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); + /* end: add a copy of the newly loaded blob to legacy smbios_entries */ + + return; + } + + val = qemu_opt_get(opts, "type"); + if (val) { + unsigned long type = strtoul(val, NULL, 0); + + if (type > SMBIOS_MAX_TYPE) { + error_report("out of range!"); + exit(1); + } + + if (test_bit(type, have_binfile_bitmap)) { + error_report("can't add fields, binary file already loaded!"); + exit(1); + } + set_bit(type, have_fields_bitmap); + + switch (type) { + case 0: + qemu_opts_validate(opts, qemu_smbios_type0_opts, &error_fatal); + save_opt(&type0.vendor, opts, "vendor"); + save_opt(&type0.version, opts, "version"); + save_opt(&type0.date, opts, "date"); + type0.uefi = qemu_opt_get_bool(opts, "uefi", false); + + val = qemu_opt_get(opts, "release"); + if (val) { + if (sscanf(val, "%hhu.%hhu", &type0.major, &type0.minor) != 2) { + error_report("Invalid release"); + exit(1); + } + type0.have_major_minor = true; + } + return; + case 1: + qemu_opts_validate(opts, qemu_smbios_type1_opts, &error_fatal); + save_opt(&type1.manufacturer, opts, "manufacturer"); + save_opt(&type1.product, opts, "product"); + save_opt(&type1.version, opts, "version"); + save_opt(&type1.serial, opts, "serial"); + save_opt(&type1.sku, opts, "sku"); + save_opt(&type1.family, opts, "family"); + + val = qemu_opt_get(opts, "uuid"); + if (val) { + if (qemu_uuid_parse(val, qemu_uuid) != 0) { + error_report("Invalid UUID"); + exit(1); + } + qemu_uuid_set = true; + } + return; + case 2: + qemu_opts_validate(opts, qemu_smbios_type2_opts, &error_fatal); + save_opt(&type2.manufacturer, opts, "manufacturer"); + save_opt(&type2.product, opts, "product"); + save_opt(&type2.version, opts, "version"); + save_opt(&type2.serial, opts, "serial"); + save_opt(&type2.asset, opts, "asset"); + save_opt(&type2.location, opts, "location"); + return; + case 3: + qemu_opts_validate(opts, qemu_smbios_type3_opts, &error_fatal); + save_opt(&type3.manufacturer, opts, "manufacturer"); + save_opt(&type3.version, opts, "version"); + save_opt(&type3.serial, opts, "serial"); + save_opt(&type3.asset, opts, "asset"); + save_opt(&type3.sku, opts, "sku"); + return; + case 4: + qemu_opts_validate(opts, qemu_smbios_type4_opts, &error_fatal); + save_opt(&type4.sock_pfx, opts, "sock_pfx"); + save_opt(&type4.manufacturer, opts, "manufacturer"); + save_opt(&type4.version, opts, "version"); + save_opt(&type4.serial, opts, "serial"); + save_opt(&type4.asset, opts, "asset"); + save_opt(&type4.part, opts, "part"); + return; + case 17: + qemu_opts_validate(opts, qemu_smbios_type17_opts, &error_fatal); + save_opt(&type17.loc_pfx, opts, "loc_pfx"); + save_opt(&type17.bank, opts, "bank"); + save_opt(&type17.manufacturer, opts, "manufacturer"); + save_opt(&type17.serial, opts, "serial"); + save_opt(&type17.asset, opts, "asset"); + save_opt(&type17.part, opts, "part"); + type17.speed = qemu_opt_get_number(opts, "speed", 0); + return; + default: + error_report("Don't know how to build fields for SMBIOS type %ld", + type); + exit(1); + } + } + + error_report("Must specify type= or file="); + exit(1); +} diff --git a/qemu/hw/sparc/leon3.c b/qemu/hw/sparc/leon3.c index 7f5dcd6d8..dbae41f3a 100644 --- a/qemu/hw/sparc/leon3.c +++ b/qemu/hw/sparc/leon3.c @@ -21,6 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "qemu/timer.h" #include "hw/ptimer.h" @@ -156,7 +160,7 @@ static void leon3_generic_hw_init(MachineState *machine) /* Allocate BIOS */ prom_size = 8 * 1024 * 1024; /* 8Mb */ - memory_region_init_ram(prom, NULL, "Leon3.bios", prom_size, &error_abort); + memory_region_init_ram(prom, NULL, "Leon3.bios", prom_size, &error_fatal); vmstate_register_ram_global(prom); memory_region_set_readonly(prom, true); memory_region_add_subregion(address_space_mem, 0x00000000, prom); @@ -193,7 +197,7 @@ static void leon3_generic_hw_init(MachineState *machine) uint64_t entry; kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, - 1 /* big endian */, ELF_MACHINE, 0); + 1 /* big endian */, EM_SPARC, 0, 0); if (kernel_size < 0) { fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename); @@ -216,15 +220,10 @@ static void leon3_generic_hw_init(MachineState *machine) } } -static QEMUMachine leon3_generic_machine = { - .name = "leon3_generic", - .desc = "Leon-3 generic", - .init = leon3_generic_hw_init, -}; - -static void leon3_machine_init(void) +static void leon3_generic_machine_init(MachineClass *mc) { - qemu_register_machine(&leon3_generic_machine); + mc->desc = "Leon-3 generic"; + mc->init = leon3_generic_hw_init; } -machine_init(leon3_machine_init); +DEFINE_MACHINE("leon3_generic", leon3_generic_machine_init) diff --git a/qemu/hw/sparc/sun4m.c b/qemu/hw/sparc/sun4m.c index 68ac4d8bb..7bfc00abc 100644 --- a/qemu/hw/sparc/sun4m.c +++ b/qemu/hw/sparc/sun4m.c @@ -21,6 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "qemu/error-report.h" #include "qemu/timer.h" @@ -42,6 +46,7 @@ #include "elf.h" #include "sysemu/block-backend.h" #include "trace.h" +#include "qemu/cutils.h" /* * Sun4m architecture was used in the following machines: @@ -95,29 +100,7 @@ struct sun4m_hwdef { uint8_t nvram_machine_id; }; -int DMA_get_channel_mode (int nchan) -{ - return 0; -} -int DMA_read_memory (int nchan, void *buf, int pos, int size) -{ - return 0; -} -int DMA_write_memory (int nchan, void *buf, int pos, int size) -{ - return 0; -} -void DMA_hold_DREQ (int nchan) {} -void DMA_release_DREQ (int nchan) {} -void DMA_schedule(int nchan) {} - -void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit) -{ -} - -void DMA_register_channel (int nchan, - DMA_transfer_handler transfer_handler, - void *opaque) +void DMA_init(ISABus *bus, int high_page_enable) { } @@ -300,7 +283,7 @@ static unsigned long sun4m_load_kernel(const char *kernel_filename, bswap_needed = 0; #endif kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - NULL, NULL, NULL, 1, ELF_MACHINE, 0); + NULL, NULL, NULL, 1, EM_SPARC, 0, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR, RAM_size - KERNEL_LOAD_ADDR, bswap_needed, @@ -641,7 +624,7 @@ static int idreg_init1(SysBusDevice *dev) IDRegState *s = MACIO_ID_REGISTER(dev); memory_region_init_ram(&s->mem, OBJECT(s), - "sun4m.idreg", sizeof(idreg_data), &error_abort); + "sun4m.idreg", sizeof(idreg_data), &error_fatal); vmstate_register_ram_global(&s->mem); memory_region_set_readonly(&s->mem, true); sysbus_init_mmio(dev, &s->mem); @@ -688,7 +671,7 @@ static int afx_init1(SysBusDevice *dev) { AFXState *s = TCX_AFX(dev); - memory_region_init_ram(&s->mem, OBJECT(s), "sun4m.afx", 4, &error_abort); + memory_region_init_ram(&s->mem, OBJECT(s), "sun4m.afx", 4, &error_fatal); vmstate_register_ram_global(&s->mem); sysbus_init_mmio(dev, &s->mem); return 0; @@ -744,7 +727,7 @@ static void prom_init(hwaddr addr, const char *bios_name) filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { ret = load_elf(filename, translate_prom_address, &addr, NULL, - NULL, NULL, 1, ELF_MACHINE, 0); + NULL, NULL, 1, EM_SPARC, 0, 0); if (ret < 0 || ret > PROM_SIZE_MAX) { ret = load_image_targphys(filename, addr, PROM_SIZE_MAX); } @@ -763,7 +746,7 @@ static int prom_init1(SysBusDevice *dev) PROMState *s = OPENPROM(dev); memory_region_init_ram(&s->prom, OBJECT(s), "sun4m.prom", PROM_SIZE_MAX, - &error_abort); + &error_fatal); vmstate_register_ram_global(&s->prom); memory_region_set_readonly(&s->prom, true); sysbus_init_mmio(dev, &s->prom); @@ -1420,80 +1403,152 @@ static void sbook_init(MachineState *machine) sun4m_hw_init(&sun4m_hwdefs[8], machine); } -static QEMUMachine ss5_machine = { - .name = "SS-5", - .desc = "Sun4m platform, SPARCstation 5", - .init = ss5_init, - .block_default_type = IF_SCSI, - .is_default = 1, - .default_boot_order = "c", +static void ss5_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Sun4m platform, SPARCstation 5"; + mc->init = ss5_init; + mc->block_default_type = IF_SCSI; + mc->is_default = 1; + mc->default_boot_order = "c"; +} + +static const TypeInfo ss5_type = { + .name = MACHINE_TYPE_NAME("SS-5"), + .parent = TYPE_MACHINE, + .class_init = ss5_class_init, }; -static QEMUMachine ss10_machine = { - .name = "SS-10", - .desc = "Sun4m platform, SPARCstation 10", - .init = ss10_init, - .block_default_type = IF_SCSI, - .max_cpus = 4, - .default_boot_order = "c", +static void ss10_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Sun4m platform, SPARCstation 10"; + mc->init = ss10_init; + mc->block_default_type = IF_SCSI; + mc->max_cpus = 4; + mc->default_boot_order = "c"; +} + +static const TypeInfo ss10_type = { + .name = MACHINE_TYPE_NAME("SS-10"), + .parent = TYPE_MACHINE, + .class_init = ss10_class_init, }; -static QEMUMachine ss600mp_machine = { - .name = "SS-600MP", - .desc = "Sun4m platform, SPARCserver 600MP", - .init = ss600mp_init, - .block_default_type = IF_SCSI, - .max_cpus = 4, - .default_boot_order = "c", +static void ss600mp_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Sun4m platform, SPARCserver 600MP"; + mc->init = ss600mp_init; + mc->block_default_type = IF_SCSI; + mc->max_cpus = 4; + mc->default_boot_order = "c"; +} + +static const TypeInfo ss600mp_type = { + .name = MACHINE_TYPE_NAME("SS-600MP"), + .parent = TYPE_MACHINE, + .class_init = ss600mp_class_init, }; -static QEMUMachine ss20_machine = { - .name = "SS-20", - .desc = "Sun4m platform, SPARCstation 20", - .init = ss20_init, - .block_default_type = IF_SCSI, - .max_cpus = 4, - .default_boot_order = "c", +static void ss20_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Sun4m platform, SPARCstation 20"; + mc->init = ss20_init; + mc->block_default_type = IF_SCSI; + mc->max_cpus = 4; + mc->default_boot_order = "c"; +} + +static const TypeInfo ss20_type = { + .name = MACHINE_TYPE_NAME("SS-20"), + .parent = TYPE_MACHINE, + .class_init = ss20_class_init, }; -static QEMUMachine voyager_machine = { - .name = "Voyager", - .desc = "Sun4m platform, SPARCstation Voyager", - .init = vger_init, - .block_default_type = IF_SCSI, - .default_boot_order = "c", +static void voyager_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Sun4m platform, SPARCstation Voyager"; + mc->init = vger_init; + mc->block_default_type = IF_SCSI; + mc->default_boot_order = "c"; +} + +static const TypeInfo voyager_type = { + .name = MACHINE_TYPE_NAME("Voyager"), + .parent = TYPE_MACHINE, + .class_init = voyager_class_init, }; -static QEMUMachine ss_lx_machine = { - .name = "LX", - .desc = "Sun4m platform, SPARCstation LX", - .init = ss_lx_init, - .block_default_type = IF_SCSI, - .default_boot_order = "c", +static void ss_lx_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Sun4m platform, SPARCstation LX"; + mc->init = ss_lx_init; + mc->block_default_type = IF_SCSI; + mc->default_boot_order = "c"; +} + +static const TypeInfo ss_lx_type = { + .name = MACHINE_TYPE_NAME("LX"), + .parent = TYPE_MACHINE, + .class_init = ss_lx_class_init, }; -static QEMUMachine ss4_machine = { - .name = "SS-4", - .desc = "Sun4m platform, SPARCstation 4", - .init = ss4_init, - .block_default_type = IF_SCSI, - .default_boot_order = "c", +static void ss4_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Sun4m platform, SPARCstation 4"; + mc->init = ss4_init; + mc->block_default_type = IF_SCSI; + mc->default_boot_order = "c"; +} + +static const TypeInfo ss4_type = { + .name = MACHINE_TYPE_NAME("SS-4"), + .parent = TYPE_MACHINE, + .class_init = ss4_class_init, }; -static QEMUMachine scls_machine = { - .name = "SPARCClassic", - .desc = "Sun4m platform, SPARCClassic", - .init = scls_init, - .block_default_type = IF_SCSI, - .default_boot_order = "c", +static void scls_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Sun4m platform, SPARCClassic"; + mc->init = scls_init; + mc->block_default_type = IF_SCSI; + mc->default_boot_order = "c"; +} + +static const TypeInfo scls_type = { + .name = MACHINE_TYPE_NAME("SPARCClassic"), + .parent = TYPE_MACHINE, + .class_init = scls_class_init, }; -static QEMUMachine sbook_machine = { - .name = "SPARCbook", - .desc = "Sun4m platform, SPARCbook", - .init = sbook_init, - .block_default_type = IF_SCSI, - .default_boot_order = "c", +static void sbook_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Sun4m platform, SPARCbook"; + mc->init = sbook_init; + mc->block_default_type = IF_SCSI; + mc->default_boot_order = "c"; +} + +static const TypeInfo sbook_type = { + .name = MACHINE_TYPE_NAME("SPARCbook"), + .parent = TYPE_MACHINE, + .class_init = sbook_class_init, }; static void sun4m_register_types(void) @@ -1502,20 +1557,16 @@ static void sun4m_register_types(void) type_register_static(&afx_info); type_register_static(&prom_info); type_register_static(&ram_info); -} -static void sun4m_machine_init(void) -{ - qemu_register_machine(&ss5_machine); - qemu_register_machine(&ss10_machine); - qemu_register_machine(&ss600mp_machine); - qemu_register_machine(&ss20_machine); - qemu_register_machine(&voyager_machine); - qemu_register_machine(&ss_lx_machine); - qemu_register_machine(&ss4_machine); - qemu_register_machine(&scls_machine); - qemu_register_machine(&sbook_machine); + type_register_static(&ss5_type); + type_register_static(&ss10_type); + type_register_static(&ss600mp_type); + type_register_static(&ss20_type); + type_register_static(&voyager_type); + type_register_static(&ss_lx_type); + type_register_static(&ss4_type); + type_register_static(&scls_type); + type_register_static(&sbook_type); } type_init(sun4m_register_types) -machine_init(sun4m_machine_init); diff --git a/qemu/hw/sparc64/sun4u.c b/qemu/hw/sparc64/sun4u.c index 30cfa0e0a..3165e18eb 100644 --- a/qemu/hw/sparc64/sun4u.c +++ b/qemu/hw/sparc64/sun4u.c @@ -21,6 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "hw/pci-host/apb.h" @@ -40,6 +44,7 @@ #include "elf.h" #include "sysemu/block-backend.h" #include "exec/address-spaces.h" +#include "qemu/cutils.h" //#define DEBUG_IRQ //#define DEBUG_EBUS @@ -98,29 +103,7 @@ typedef struct EbusState { MemoryRegion bar1; } EbusState; -int DMA_get_channel_mode (int nchan) -{ - return 0; -} -int DMA_read_memory (int nchan, void *buf, int pos, int size) -{ - return 0; -} -int DMA_write_memory (int nchan, void *buf, int pos, int size) -{ - return 0; -} -void DMA_hold_DREQ (int nchan) {} -void DMA_release_DREQ (int nchan) {} -void DMA_schedule(int nchan) {} - -void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit) -{ -} - -void DMA_register_channel (int nchan, - DMA_transfer_handler transfer_handler, - void *opaque) +void DMA_init(ISABus *bus, int high_page_enable) { } @@ -208,7 +191,7 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename, bswap_needed = 0; #endif kernel_size = load_elf(kernel_filename, NULL, NULL, kernel_entry, - kernel_addr, &kernel_top, 1, ELF_MACHINE, 0); + kernel_addr, &kernel_top, 1, EM_SPARCV9, 0, 0); if (kernel_size < 0) { *kernel_addr = KERNEL_LOAD_ADDR; *kernel_entry = KERNEL_LOAD_ADDR; @@ -358,37 +341,19 @@ typedef struct ResetData { uint64_t prom_addr; } ResetData; -void cpu_put_timer(QEMUFile *f, CPUTimer *s) -{ - qemu_put_be32s(f, &s->frequency); - qemu_put_be32s(f, &s->disabled); - qemu_put_be64s(f, &s->disabled_mask); - qemu_put_sbe64s(f, &s->clock_offset); - - timer_put(f, s->qtimer); -} - -void cpu_get_timer(QEMUFile *f, CPUTimer *s) -{ - qemu_get_be32s(f, &s->frequency); - qemu_get_be32s(f, &s->disabled); - qemu_get_be64s(f, &s->disabled_mask); - qemu_get_sbe64s(f, &s->clock_offset); - - timer_get(f, s->qtimer); -} - static CPUTimer *cpu_timer_create(const char *name, SPARCCPU *cpu, QEMUBHFunc *cb, uint32_t frequency, - uint64_t disabled_mask) + uint64_t disabled_mask, uint64_t npt_mask) { CPUTimer *timer = g_malloc0(sizeof (CPUTimer)); timer->name = name; timer->frequency = frequency; timer->disabled_mask = disabled_mask; + timer->npt_mask = npt_mask; timer->disabled = 1; + timer->npt = 1; timer->clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); timer->qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cb, cpu); @@ -484,27 +449,27 @@ static void hstick_irq(void *opaque) static int64_t cpu_to_timer_ticks(int64_t cpu_ticks, uint32_t frequency) { - return muldiv64(cpu_ticks, get_ticks_per_sec(), frequency); + return muldiv64(cpu_ticks, NANOSECONDS_PER_SECOND, frequency); } static uint64_t timer_to_cpu_ticks(int64_t timer_ticks, uint32_t frequency) { - return muldiv64(timer_ticks, frequency, get_ticks_per_sec()); + return muldiv64(timer_ticks, frequency, NANOSECONDS_PER_SECOND); } void cpu_tick_set_count(CPUTimer *timer, uint64_t count) { - uint64_t real_count = count & ~timer->disabled_mask; - uint64_t disabled_bit = count & timer->disabled_mask; + uint64_t real_count = count & ~timer->npt_mask; + uint64_t npt_bit = count & timer->npt_mask; int64_t vm_clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - cpu_to_timer_ticks(real_count, timer->frequency); - TIMER_DPRINTF("%s set_count count=0x%016lx (%s) p=%p\n", + TIMER_DPRINTF("%s set_count count=0x%016lx (npt %s) p=%p\n", timer->name, real_count, - timer->disabled?"disabled":"enabled", timer); + timer->npt ? "disabled" : "enabled", timer); - timer->disabled = disabled_bit ? 1 : 0; + timer->npt = npt_bit ? 1 : 0; timer->clock_offset = vm_clock_offset; } @@ -514,12 +479,13 @@ uint64_t cpu_tick_get_count(CPUTimer *timer) qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - timer->clock_offset, timer->frequency); - TIMER_DPRINTF("%s get_count count=0x%016lx (%s) p=%p\n", + TIMER_DPRINTF("%s get_count count=0x%016lx (npt %s) p=%p\n", timer->name, real_count, - timer->disabled?"disabled":"enabled", timer); + timer->npt ? "disabled" : "enabled", timer); - if (timer->disabled) - real_count |= timer->disabled_mask; + if (timer->npt) { + real_count |= timer->npt_mask; + } return real_count; } @@ -593,13 +559,14 @@ pci_ebus_init(PCIBus *bus, int devfn, qemu_irq *irqs) return isa_bus; } -static int -pci_ebus_init1(PCIDevice *pci_dev) +static void pci_ebus_realize(PCIDevice *pci_dev, Error **errp) { EbusState *s = DO_UPCAST(EbusState, pci_dev, pci_dev); - isa_bus_new(DEVICE(pci_dev), get_system_memory(), - pci_address_space_io(pci_dev)); + if (!isa_bus_new(DEVICE(pci_dev), get_system_memory(), + pci_address_space_io(pci_dev), errp)) { + return; + } pci_dev->config[0x04] = 0x06; // command = bus master, pci mem pci_dev->config[0x05] = 0x00; @@ -614,14 +581,13 @@ pci_ebus_init1(PCIDevice *pci_dev) memory_region_init_alias(&s->bar1, OBJECT(s), "bar1", get_system_io(), 0, 0x4000); pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->bar1); - return 0; } static void ebus_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = pci_ebus_init1; + k->realize = pci_ebus_realize; k->vendor_id = PCI_VENDOR_ID_SUN; k->device_id = PCI_DEVICE_ID_SUN_EBUS; k->revision = 0x01; @@ -671,7 +637,7 @@ static void prom_init(hwaddr addr, const char *bios_name) filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { ret = load_elf(filename, translate_prom_address, &addr, - NULL, NULL, NULL, 1, ELF_MACHINE, 0); + NULL, NULL, NULL, 1, EM_SPARCV9, 0, 0); if (ret < 0 || ret > PROM_SIZE_MAX) { ret = load_image_targphys(filename, addr, PROM_SIZE_MAX); } @@ -690,7 +656,7 @@ static int prom_init1(SysBusDevice *dev) PROMState *s = OPENPROM(dev); memory_region_init_ram(&s->prom, OBJECT(s), "sun4u.prom", PROM_SIZE_MAX, - &error_abort); + &error_fatal); vmstate_register_ram_global(&s->prom); memory_region_set_readonly(&s->prom, true); sysbus_init_mmio(dev, &s->prom); @@ -734,7 +700,7 @@ static int ram_init1(SysBusDevice *dev) RamDevice *d = SUN4U_RAM(dev); memory_region_init_ram(&d->ram, OBJECT(d), "sun4u.ram", d->size, - &error_abort); + &error_fatal); vmstate_register_ram_global(&d->ram); sysbus_init_mmio(dev, &d->ram); return 0; @@ -799,13 +765,16 @@ static SPARCCPU *cpu_devinit(const char *cpu_model, const struct hwdef *hwdef) env = &cpu->env; env->tick = cpu_timer_create("tick", cpu, tick_irq, - tick_frequency, TICK_NPT_MASK); + tick_frequency, TICK_INT_DIS, + TICK_NPT_MASK); env->stick = cpu_timer_create("stick", cpu, stick_irq, - stick_frequency, TICK_INT_DIS); + stick_frequency, TICK_INT_DIS, + TICK_NPT_MASK); env->hstick = cpu_timer_create("hstick", cpu, hstick_irq, - hstick_frequency, TICK_INT_DIS); + hstick_frequency, TICK_INT_DIS, + TICK_NPT_MASK); reset_info = g_malloc0(sizeof(ResetData)); reset_info->cpu = cpu; @@ -829,6 +798,7 @@ static void sun4uv_init(MemoryRegion *address_space_mem, qemu_irq *ivec_irqs, *pbm_irqs; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; DriveInfo *fd[MAX_FD]; + DeviceState *dev; FWCfgState *fw_cfg; /* init CPUs */ @@ -865,10 +835,22 @@ static void sun4uv_init(MemoryRegion *address_space_mem, pci_cmd646_ide_init(pci_bus, hd, 1); isa_create_simple(isa_bus, "i8042"); + + /* Floppy */ for(i = 0; i < MAX_FD; i++) { fd[i] = drive_get(IF_FLOPPY, 0, i); } - fdctrl_init_isa(isa_bus, fd); + dev = DEVICE(isa_create(isa_bus, TYPE_ISA_FDC)); + if (fd[0]) { + qdev_prop_set_drive(dev, "driveA", blk_by_legacy_dinfo(fd[0]), + &error_abort); + } + if (fd[1]) { + qdev_prop_set_drive(dev, "driveB", blk_by_legacy_dinfo(fd[1]), + &error_abort); + } + qdev_prop_set_uint32(dev, "dma", -1); + qdev_init_nofail(dev); /* Map NVRAM into I/O (ebus) space */ nvram = m48t59_init(NULL, 0, 0, NVRAM_SIZE, 1968, 59); @@ -965,29 +947,53 @@ static void niagara_init(MachineState *machine) sun4uv_init(get_system_memory(), machine, &hwdefs[2]); } -static QEMUMachine sun4u_machine = { - .name = "sun4u", - .desc = "Sun4u platform", - .init = sun4u_init, - .max_cpus = 1, // XXX for now - .is_default = 1, - .default_boot_order = "c", +static void sun4u_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Sun4u platform"; + mc->init = sun4u_init; + mc->max_cpus = 1; /* XXX for now */ + mc->is_default = 1; + mc->default_boot_order = "c"; +} + +static const TypeInfo sun4u_type = { + .name = MACHINE_TYPE_NAME("sun4u"), + .parent = TYPE_MACHINE, + .class_init = sun4u_class_init, }; -static QEMUMachine sun4v_machine = { - .name = "sun4v", - .desc = "Sun4v platform", - .init = sun4v_init, - .max_cpus = 1, // XXX for now - .default_boot_order = "c", +static void sun4v_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Sun4v platform"; + mc->init = sun4v_init; + mc->max_cpus = 1; /* XXX for now */ + mc->default_boot_order = "c"; +} + +static const TypeInfo sun4v_type = { + .name = MACHINE_TYPE_NAME("sun4v"), + .parent = TYPE_MACHINE, + .class_init = sun4v_class_init, }; -static QEMUMachine niagara_machine = { - .name = "Niagara", - .desc = "Sun4v platform, Niagara", - .init = niagara_init, - .max_cpus = 1, // XXX for now - .default_boot_order = "c", +static void niagara_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Sun4v platform, Niagara"; + mc->init = niagara_init; + mc->max_cpus = 1; /* XXX for now */ + mc->default_boot_order = "c"; +} + +static const TypeInfo niagara_type = { + .name = MACHINE_TYPE_NAME("Niagara"), + .parent = TYPE_MACHINE, + .class_init = niagara_class_init, }; static void sun4u_register_types(void) @@ -995,14 +1001,10 @@ static void sun4u_register_types(void) type_register_static(&ebus_info); type_register_static(&prom_info); type_register_static(&ram_info); -} -static void sun4u_machine_init(void) -{ - qemu_register_machine(&sun4u_machine); - qemu_register_machine(&sun4v_machine); - qemu_register_machine(&niagara_machine); + type_register_static(&sun4u_type); + type_register_static(&sun4v_type); + type_register_static(&niagara_type); } type_init(sun4u_register_types) -machine_init(sun4u_machine_init); diff --git a/qemu/hw/ssi/omap_spi.c b/qemu/hw/ssi/omap_spi.c index 119e325a6..22034656b 100644 --- a/qemu/hw/ssi/omap_spi.c +++ b/qemu/hw/ssi/omap_spi.c @@ -19,6 +19,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/arm/omap.h" @@ -342,8 +343,7 @@ static const MemoryRegionOps omap_mcspi_ops = { struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum, qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk) { - struct omap_mcspi_s *s = (struct omap_mcspi_s *) - g_malloc0(sizeof(struct omap_mcspi_s)); + struct omap_mcspi_s *s = g_new0(struct omap_mcspi_s, 1); struct omap_mcspi_ch_s *ch = s->ch; s->irq = irq; diff --git a/qemu/hw/ssi/pl022.c b/qemu/hw/ssi/pl022.c index 61d568f36..564a0d36e 100644 --- a/qemu/hw/ssi/pl022.c +++ b/qemu/hw/ssi/pl022.c @@ -7,8 +7,9 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" -#include "hw/ssi.h" +#include "hw/ssi/ssi.h" //#define DEBUG_PL022 1 diff --git a/qemu/hw/ssi/ssi.c b/qemu/hw/ssi/ssi.c index 2aab79ba7..9791c0d94 100644 --- a/qemu/hw/ssi/ssi.c +++ b/qemu/hw/ssi/ssi.c @@ -12,7 +12,8 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw/ssi.h" +#include "qemu/osdep.h" +#include "hw/ssi/ssi.h" struct SSIBus { BusState parent_obj; diff --git a/qemu/hw/ssi/xilinx_spi.c b/qemu/hw/ssi/xilinx_spi.c index 620573cac..33482f04d 100644 --- a/qemu/hw/ssi/xilinx_spi.c +++ b/qemu/hw/ssi/xilinx_spi.c @@ -24,12 +24,13 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "qemu/log.h" #include "qemu/fifo8.h" -#include "hw/ssi.h" +#include "hw/ssi/ssi.h" #ifdef XILINX_SPI_ERR_DEBUG #define DB_PRINT(...) do { \ diff --git a/qemu/hw/ssi/xilinx_spips.c b/qemu/hw/ssi/xilinx_spips.c index 0910f5479..e2b77dc3d 100644 --- a/qemu/hw/ssi/xilinx_spips.c +++ b/qemu/hw/ssi/xilinx_spips.c @@ -22,13 +22,15 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "hw/ptimer.h" #include "qemu/log.h" #include "qemu/fifo8.h" -#include "hw/ssi.h" +#include "hw/ssi/ssi.h" #include "qemu/bitops.h" +#include "hw/ssi/xilinx_spips.h" #ifndef XILINX_SPIPS_ERR_DEBUG #define XILINX_SPIPS_ERR_DEBUG 0 @@ -103,8 +105,6 @@ #define R_MOD_ID (0xFC / 4) -#define R_MAX (R_MOD_ID+1) - /* size of TXRX FIFOs */ #define RXFF_A 32 #define TXFF_A 32 @@ -134,30 +134,6 @@ typedef enum { QPP = 0x32, } FlashCMD; -typedef struct { - SysBusDevice parent_obj; - - MemoryRegion iomem; - MemoryRegion mmlqspi; - - qemu_irq irq; - int irqline; - - uint8_t num_cs; - uint8_t num_busses; - - uint8_t snoop_state; - qemu_irq *cs_lines; - SSIBus **spi; - - Fifo8 rx_fifo; - Fifo8 tx_fifo; - - uint8_t num_txrx_bytes; - - uint32_t regs[R_MAX]; -} XilinxSPIPS; - typedef struct { XilinxSPIPS parent_obj; @@ -174,19 +150,6 @@ typedef struct XilinxSPIPSClass { uint32_t tx_fifo_size; } XilinxSPIPSClass; -#define TYPE_XILINX_SPIPS "xlnx.ps7-spi" -#define TYPE_XILINX_QSPIPS "xlnx.ps7-qspi" - -#define XILINX_SPIPS(obj) \ - OBJECT_CHECK(XilinxSPIPS, (obj), TYPE_XILINX_SPIPS) -#define XILINX_SPIPS_CLASS(klass) \ - OBJECT_CLASS_CHECK(XilinxSPIPSClass, (klass), TYPE_XILINX_SPIPS) -#define XILINX_SPIPS_GET_CLASS(obj) \ - OBJECT_GET_CLASS(XilinxSPIPSClass, (obj), TYPE_XILINX_SPIPS) - -#define XILINX_QSPIPS(obj) \ - OBJECT_CHECK(XilinxQSPIPS, (obj), TYPE_XILINX_QSPIPS) - static inline int num_effective_busses(XilinxSPIPS *s) { return (s->regs[R_LQSPI_CFG] & LQSPI_CFG_SEP_BUS && @@ -257,7 +220,7 @@ static void xilinx_spips_reset(DeviceState *d) XilinxSPIPS *s = XILINX_SPIPS(d); int i; - for (i = 0; i < R_MAX; i++) { + for (i = 0; i < XLNX_SPIPS_R_MAX; i++) { s->regs[i] = 0; } @@ -664,7 +627,7 @@ static void xilinx_spips_realize(DeviceState *dev, Error **errp) } memory_region_init_io(&s->iomem, OBJECT(s), xsc->reg_ops, s, - "spi", R_MAX*4); + "spi", XLNX_SPIPS_R_MAX * 4); sysbus_init_mmio(sbd, &s->iomem); s->irqline = -1; @@ -708,7 +671,7 @@ static const VMStateDescription vmstate_xilinx_spips = { .fields = (VMStateField[]) { VMSTATE_FIFO8(tx_fifo, XilinxSPIPS), VMSTATE_FIFO8(rx_fifo, XilinxSPIPS), - VMSTATE_UINT32_ARRAY(regs, XilinxSPIPS, R_MAX), + VMSTATE_UINT32_ARRAY(regs, XilinxSPIPS, XLNX_SPIPS_R_MAX), VMSTATE_UINT8(snoop_state, XilinxSPIPS), VMSTATE_END_OF_LIST() } diff --git a/qemu/hw/timer/Makefile.objs b/qemu/hw/timer/Makefile.objs index 133bd0d45..003c14fa2 100644 --- a/qemu/hw/timer/Makefile.objs +++ b/qemu/hw/timer/Makefile.objs @@ -25,7 +25,6 @@ obj-$(CONFIG_OMAP) += omap_gptimer.o obj-$(CONFIG_OMAP) += omap_synctimer.o obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o obj-$(CONFIG_SH4) += sh_timer.o -obj-$(CONFIG_TUSB6010) += tusb6010.o obj-$(CONFIG_DIGIC) += digic-timer.o obj-$(CONFIG_MC146818RTC) += mc146818rtc.o @@ -33,3 +32,4 @@ obj-$(CONFIG_MC146818RTC) += mc146818rtc.o obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o common-obj-$(CONFIG_STM32F2XX_TIMER) += stm32f2xx_timer.o +common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o diff --git a/qemu/hw/timer/a9gtimer.c b/qemu/hw/timer/a9gtimer.c index dd4aae8b3..afe577c76 100644 --- a/qemu/hw/timer/a9gtimer.c +++ b/qemu/hw/timer/a9gtimer.c @@ -20,10 +20,13 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/timer/a9gtimer.h" +#include "qapi/error.h" #include "qemu/timer.h" #include "qemu/bitops.h" #include "qemu/log.h" +#include "qom/cpu.h" #ifndef A9_GTIMER_ERR_DEBUG #define A9_GTIMER_ERR_DEBUG 0 diff --git a/qemu/hw/timer/allwinner-a10-pit.c b/qemu/hw/timer/allwinner-a10-pit.c index 34124fe3d..51cdc98f3 100644 --- a/qemu/hw/timer/allwinner-a10-pit.c +++ b/qemu/hw/timer/allwinner-a10-pit.c @@ -15,6 +15,7 @@ * for more details. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "hw/timer/allwinner-a10-pit.h" diff --git a/qemu/hw/timer/arm_mptimer.c b/qemu/hw/timer/arm_mptimer.c index 3e59c2a28..d66bbf01b 100644 --- a/qemu/hw/timer/arm_mptimer.c +++ b/qemu/hw/timer/arm_mptimer.c @@ -19,7 +19,9 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/timer/arm_mptimer.h" +#include "qapi/error.h" #include "qemu/timer.h" #include "qom/cpu.h" @@ -220,8 +222,9 @@ static void arm_mptimer_realize(DeviceState *dev, Error **errp) int i; if (s->num_cpu < 1 || s->num_cpu > ARM_MPTIMER_MAX_CPUS) { - hw_error("%s: num-cpu must be between 1 and %d\n", - __func__, ARM_MPTIMER_MAX_CPUS); + error_setg(errp, "num-cpu must be between 1 and %d", + ARM_MPTIMER_MAX_CPUS); + return; } /* We implement one timer block per CPU, and expose multiple MMIO regions: * * region 0 is "timer for this core" diff --git a/qemu/hw/timer/arm_timer.c b/qemu/hw/timer/arm_timer.c index d53f39ad6..f1ede5f53 100644 --- a/qemu/hw/timer/arm_timer.c +++ b/qemu/hw/timer/arm_timer.c @@ -7,6 +7,7 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "qemu/timer.h" #include "qemu-common.h" @@ -276,21 +277,25 @@ static const VMStateDescription vmstate_sp804 = { } }; -static int sp804_init(SysBusDevice *sbd) +static void sp804_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - SP804State *s = SP804(dev); + SP804State *s = SP804(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); sysbus_init_irq(sbd, &s->irq); + memory_region_init_io(&s->iomem, obj, &sp804_ops, s, + "sp804", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void sp804_realize(DeviceState *dev, Error **errp) +{ + SP804State *s = SP804(dev); + s->timer[0] = arm_timer_init(s->freq0); s->timer[1] = arm_timer_init(s->freq1); s->timer[0]->irq = qemu_allocate_irq(sp804_set_irq, s, 0); s->timer[1]->irq = qemu_allocate_irq(sp804_set_irq, s, 1); - memory_region_init_io(&s->iomem, OBJECT(s), &sp804_ops, s, - "sp804", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - vmstate_register(dev, -1, &vmstate_sp804, s); - return 0; } /* Integrator/CP timer module. */ @@ -343,9 +348,10 @@ static const MemoryRegionOps icp_pit_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int icp_pit_init(SysBusDevice *dev) +static void icp_pit_init(Object *obj) { - icp_pit_state *s = INTEGRATOR_PIT(dev); + icp_pit_state *s = INTEGRATOR_PIT(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); /* Timer 0 runs at the system clock speed (40MHz). */ s->timer[0] = arm_timer_init(40000000); @@ -357,26 +363,18 @@ static int icp_pit_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->timer[1]->irq); sysbus_init_irq(dev, &s->timer[2]->irq); - memory_region_init_io(&s->iomem, OBJECT(s), &icp_pit_ops, s, + memory_region_init_io(&s->iomem, obj, &icp_pit_ops, s, "icp_pit", 0x1000); sysbus_init_mmio(dev, &s->iomem); /* This device has no state to save/restore. The component timers will save themselves. */ - return 0; -} - -static void icp_pit_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = icp_pit_init; } static const TypeInfo icp_pit_info = { .name = TYPE_INTEGRATOR_PIT, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(icp_pit_state), - .class_init = icp_pit_class_init, + .instance_init = icp_pit_init, }; static Property sp804_properties[] = { @@ -387,17 +385,18 @@ static Property sp804_properties[] = { static void sp804_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *k = DEVICE_CLASS(klass); - sdc->init = sp804_init; + k->realize = sp804_realize; k->props = sp804_properties; + k->vmsd = &vmstate_sp804; } static const TypeInfo sp804_info = { .name = TYPE_SP804, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SP804State), + .instance_init = sp804_init, .class_init = sp804_class_init, }; diff --git a/qemu/hw/timer/aspeed_timer.c b/qemu/hw/timer/aspeed_timer.c new file mode 100644 index 000000000..51e8303cd --- /dev/null +++ b/qemu/hw/timer/aspeed_timer.c @@ -0,0 +1,449 @@ +/* + * ASPEED AST2400 Timer + * + * Andrew Jeffery + * + * Copyright (C) 2016 IBM Corp. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/ptimer.h" +#include "hw/sysbus.h" +#include "hw/timer/aspeed_timer.h" +#include "qemu-common.h" +#include "qemu/bitops.h" +#include "qemu/main-loop.h" +#include "qemu/timer.h" +#include "trace.h" + +#define TIMER_NR_REGS 4 + +#define TIMER_CTRL_BITS 4 +#define TIMER_CTRL_MASK ((1 << TIMER_CTRL_BITS) - 1) + +#define TIMER_CLOCK_USE_EXT true +#define TIMER_CLOCK_EXT_HZ 1000000 +#define TIMER_CLOCK_USE_APB false +#define TIMER_CLOCK_APB_HZ 24000000 + +#define TIMER_REG_STATUS 0 +#define TIMER_REG_RELOAD 1 +#define TIMER_REG_MATCH_FIRST 2 +#define TIMER_REG_MATCH_SECOND 3 + +#define TIMER_FIRST_CAP_PULSE 4 + +enum timer_ctrl_op { + op_enable = 0, + op_external_clock, + op_overflow_interrupt, + op_pulse_enable +}; + +/** + * Avoid mutual references between AspeedTimerCtrlState and AspeedTimer + * structs, as it's a waste of memory. The ptimer BH callback needs to know + * whether a specific AspeedTimer is enabled, but this information is held in + * AspeedTimerCtrlState. So, provide a helper to hoist ourselves from an + * arbitrary AspeedTimer to AspeedTimerCtrlState. + */ +static inline AspeedTimerCtrlState *timer_to_ctrl(AspeedTimer *t) +{ + const AspeedTimer (*timers)[] = (void *)t - (t->id * sizeof(*t)); + return container_of(timers, AspeedTimerCtrlState, timers); +} + +static inline bool timer_ctrl_status(AspeedTimer *t, enum timer_ctrl_op op) +{ + return !!(timer_to_ctrl(t)->ctrl & BIT(t->id * TIMER_CTRL_BITS + op)); +} + +static inline bool timer_enabled(AspeedTimer *t) +{ + return timer_ctrl_status(t, op_enable); +} + +static inline bool timer_overflow_interrupt(AspeedTimer *t) +{ + return timer_ctrl_status(t, op_overflow_interrupt); +} + +static inline bool timer_can_pulse(AspeedTimer *t) +{ + return t->id >= TIMER_FIRST_CAP_PULSE; +} + +static void aspeed_timer_expire(void *opaque) +{ + AspeedTimer *t = opaque; + + /* Only support interrupts on match values of zero for the moment - this is + * sufficient to boot an aspeed_defconfig Linux kernel. + * + * TODO: matching on arbitrary values (see e.g. hw/timer/a9gtimer.c) + */ + bool match = !(t->match[0] && t->match[1]); + bool interrupt = timer_overflow_interrupt(t) || match; + if (timer_enabled(t) && interrupt) { + t->level = !t->level; + qemu_set_irq(t->irq, t->level); + } +} + +static uint64_t aspeed_timer_get_value(AspeedTimer *t, int reg) +{ + uint64_t value; + + switch (reg) { + case TIMER_REG_STATUS: + value = ptimer_get_count(t->timer); + break; + case TIMER_REG_RELOAD: + value = t->reload; + break; + case TIMER_REG_MATCH_FIRST: + case TIMER_REG_MATCH_SECOND: + value = t->match[reg - 2]; + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: Programming error: unexpected reg: %d\n", + __func__, reg); + value = 0; + break; + } + return value; +} + +static uint64_t aspeed_timer_read(void *opaque, hwaddr offset, unsigned size) +{ + AspeedTimerCtrlState *s = opaque; + const int reg = (offset & 0xf) / 4; + uint64_t value; + + switch (offset) { + case 0x30: /* Control Register */ + value = s->ctrl; + break; + case 0x34: /* Control Register 2 */ + value = s->ctrl2; + break; + case 0x00 ... 0x2c: /* Timers 1 - 4 */ + value = aspeed_timer_get_value(&s->timers[(offset >> 4)], reg); + break; + case 0x40 ... 0x8c: /* Timers 5 - 8 */ + value = aspeed_timer_get_value(&s->timers[(offset >> 4) - 1], reg); + break; + /* Illegal */ + case 0x38: + case 0x3C: + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + value = 0; + break; + } + trace_aspeed_timer_read(offset, size, value); + return value; +} + +static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg, + uint32_t value) +{ + AspeedTimer *t; + + trace_aspeed_timer_set_value(timer, reg, value); + t = &s->timers[timer]; + switch (reg) { + case TIMER_REG_STATUS: + if (timer_enabled(t)) { + ptimer_set_count(t->timer, value); + } + break; + case TIMER_REG_RELOAD: + t->reload = value; + ptimer_set_limit(t->timer, value, 1); + break; + case TIMER_REG_MATCH_FIRST: + case TIMER_REG_MATCH_SECOND: + if (value) { + /* Non-zero match values are unsupported. As such an interrupt will + * always be triggered when the timer reaches zero even if the + * overflow interrupt control bit is clear. + */ + qemu_log_mask(LOG_UNIMP, "%s: Match value unsupported by device: " + "0x%" PRIx32 "\n", __func__, value); + } else { + t->match[reg - 2] = value; + } + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: Programming error: unexpected reg: %d\n", + __func__, reg); + break; + } +} + +/* Control register operations are broken out into helpers that can be + * explictly called on aspeed_timer_reset(), but also from + * aspeed_timer_ctrl_op(). + */ + +static void aspeed_timer_ctrl_enable(AspeedTimer *t, bool enable) +{ + trace_aspeed_timer_ctrl_enable(t->id, enable); + if (enable) { + ptimer_run(t->timer, 0); + } else { + ptimer_stop(t->timer); + ptimer_set_limit(t->timer, t->reload, 1); + } +} + +static void aspeed_timer_ctrl_external_clock(AspeedTimer *t, bool enable) +{ + trace_aspeed_timer_ctrl_external_clock(t->id, enable); + if (enable) { + ptimer_set_freq(t->timer, TIMER_CLOCK_EXT_HZ); + } else { + ptimer_set_freq(t->timer, TIMER_CLOCK_APB_HZ); + } +} + +static void aspeed_timer_ctrl_overflow_interrupt(AspeedTimer *t, bool enable) +{ + trace_aspeed_timer_ctrl_overflow_interrupt(t->id, enable); +} + +static void aspeed_timer_ctrl_pulse_enable(AspeedTimer *t, bool enable) +{ + if (timer_can_pulse(t)) { + trace_aspeed_timer_ctrl_pulse_enable(t->id, enable); + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Timer does not support pulse mode\n", __func__); + } +} + +/** + * Given the actions are fixed in number and completely described in helper + * functions, dispatch with a lookup table rather than manage control flow with + * a switch statement. + */ +static void (*const ctrl_ops[])(AspeedTimer *, bool) = { + [op_enable] = aspeed_timer_ctrl_enable, + [op_external_clock] = aspeed_timer_ctrl_external_clock, + [op_overflow_interrupt] = aspeed_timer_ctrl_overflow_interrupt, + [op_pulse_enable] = aspeed_timer_ctrl_pulse_enable, +}; + +/** + * Conditionally affect changes chosen by a timer's control bit. + * + * The aspeed_timer_ctrl_op() interface is convenient for the + * aspeed_timer_set_ctrl() function as the "no change" early exit can be + * calculated for all operations, which cleans up the caller code. However the + * interface isn't convenient for the reset function where we want to enter a + * specific state without artificially constructing old and new values that + * will fall through the change guard (and motivates extracting the actions + * out to helper functions). + * + * @t: The timer to manipulate + * @op: The type of operation to be performed + * @old: The old state of the timer's control bits + * @new: The incoming state for the timer's control bits + */ +static void aspeed_timer_ctrl_op(AspeedTimer *t, enum timer_ctrl_op op, + uint8_t old, uint8_t new) +{ + const uint8_t mask = BIT(op); + const bool enable = !!(new & mask); + const bool changed = ((old ^ new) & mask); + if (!changed) { + return; + } + ctrl_ops[op](t, enable); +} + +static void aspeed_timer_set_ctrl(AspeedTimerCtrlState *s, uint32_t reg) +{ + int i; + int shift; + uint8_t t_old, t_new; + AspeedTimer *t; + const uint8_t enable_mask = BIT(op_enable); + + /* Handle a dependency between the 'enable' and remaining three + * configuration bits - i.e. if more than one bit in the control set has + * changed, including the 'enable' bit, then we want either disable the + * timer and perform configuration, or perform configuration and then + * enable the timer + */ + for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { + t = &s->timers[i]; + shift = (i * TIMER_CTRL_BITS); + t_old = (s->ctrl >> shift) & TIMER_CTRL_MASK; + t_new = (reg >> shift) & TIMER_CTRL_MASK; + + /* If we are disabling, do so first */ + if ((t_old & enable_mask) && !(t_new & enable_mask)) { + aspeed_timer_ctrl_enable(t, false); + } + aspeed_timer_ctrl_op(t, op_external_clock, t_old, t_new); + aspeed_timer_ctrl_op(t, op_overflow_interrupt, t_old, t_new); + aspeed_timer_ctrl_op(t, op_pulse_enable, t_old, t_new); + /* If we are enabling, do so last */ + if (!(t_old & enable_mask) && (t_new & enable_mask)) { + aspeed_timer_ctrl_enable(t, true); + } + } + s->ctrl = reg; +} + +static void aspeed_timer_set_ctrl2(AspeedTimerCtrlState *s, uint32_t value) +{ + trace_aspeed_timer_set_ctrl2(value); +} + +static void aspeed_timer_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + const uint32_t tv = (uint32_t)(value & 0xFFFFFFFF); + const int reg = (offset & 0xf) / 4; + AspeedTimerCtrlState *s = opaque; + + switch (offset) { + /* Control Registers */ + case 0x30: + aspeed_timer_set_ctrl(s, tv); + break; + case 0x34: + aspeed_timer_set_ctrl2(s, tv); + break; + /* Timer Registers */ + case 0x00 ... 0x2c: + aspeed_timer_set_value(s, (offset >> TIMER_NR_REGS), reg, tv); + break; + case 0x40 ... 0x8c: + aspeed_timer_set_value(s, (offset >> TIMER_NR_REGS) - 1, reg, tv); + break; + /* Illegal */ + case 0x38: + case 0x3C: + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + break; + } +} + +static const MemoryRegionOps aspeed_timer_ops = { + .read = aspeed_timer_read, + .write = aspeed_timer_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +static void aspeed_init_one_timer(AspeedTimerCtrlState *s, uint8_t id) +{ + QEMUBH *bh; + AspeedTimer *t = &s->timers[id]; + + t->id = id; + bh = qemu_bh_new(aspeed_timer_expire, t); + t->timer = ptimer_init(bh); +} + +static void aspeed_timer_realize(DeviceState *dev, Error **errp) +{ + int i; + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AspeedTimerCtrlState *s = ASPEED_TIMER(dev); + + for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { + aspeed_init_one_timer(s, i); + sysbus_init_irq(sbd, &s->timers[i].irq); + } + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_timer_ops, s, + TYPE_ASPEED_TIMER, 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void aspeed_timer_reset(DeviceState *dev) +{ + int i; + AspeedTimerCtrlState *s = ASPEED_TIMER(dev); + + for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { + AspeedTimer *t = &s->timers[i]; + /* Explictly call helpers to avoid any conditional behaviour through + * aspeed_timer_set_ctrl(). + */ + aspeed_timer_ctrl_enable(t, false); + aspeed_timer_ctrl_external_clock(t, TIMER_CLOCK_USE_APB); + aspeed_timer_ctrl_overflow_interrupt(t, false); + aspeed_timer_ctrl_pulse_enable(t, false); + t->level = 0; + t->reload = 0; + t->match[0] = 0; + t->match[1] = 0; + } + s->ctrl = 0; + s->ctrl2 = 0; +} + +static const VMStateDescription vmstate_aspeed_timer = { + .name = "aspeed.timer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(id, AspeedTimer), + VMSTATE_INT32(level, AspeedTimer), + VMSTATE_PTIMER(timer, AspeedTimer), + VMSTATE_UINT32(reload, AspeedTimer), + VMSTATE_UINT32_ARRAY(match, AspeedTimer, 2), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_aspeed_timer_state = { + .name = "aspeed.timerctrl", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(ctrl, AspeedTimerCtrlState), + VMSTATE_UINT32(ctrl2, AspeedTimerCtrlState), + VMSTATE_STRUCT_ARRAY(timers, AspeedTimerCtrlState, + ASPEED_TIMER_NR_TIMERS, 1, vmstate_aspeed_timer, + AspeedTimer), + VMSTATE_END_OF_LIST() + } +}; + +static void timer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = aspeed_timer_realize; + dc->reset = aspeed_timer_reset; + dc->desc = "ASPEED Timer"; + dc->vmsd = &vmstate_aspeed_timer_state; +} + +static const TypeInfo aspeed_timer_info = { + .name = TYPE_ASPEED_TIMER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedTimerCtrlState), + .class_init = timer_class_init, +}; + +static void aspeed_timer_register_types(void) +{ + type_register_static(&aspeed_timer_info); +} + +type_init(aspeed_timer_register_types) diff --git a/qemu/hw/timer/cadence_ttc.c b/qemu/hw/timer/cadence_ttc.c index 35bc88033..03f5b9c20 100644 --- a/qemu/hw/timer/cadence_ttc.c +++ b/qemu/hw/timer/cadence_ttc.c @@ -16,6 +16,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "qemu/timer.h" diff --git a/qemu/hw/timer/digic-timer.c b/qemu/hw/timer/digic-timer.c index 7e28e7e5f..5b97e1e1a 100644 --- a/qemu/hw/timer/digic-timer.c +++ b/qemu/hw/timer/digic-timer.c @@ -26,6 +26,7 @@ * */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/ptimer.h" #include "qemu/main-loop.h" diff --git a/qemu/hw/timer/ds1338.c b/qemu/hw/timer/ds1338.c index ec6dbeeab..0112949e2 100644 --- a/qemu/hw/timer/ds1338.c +++ b/qemu/hw/timer/ds1338.c @@ -10,7 +10,10 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" +#include "qemu-common.h" #include "hw/i2c/i2c.h" +#include "qemu/bcd.h" /* Size of NVRAM including both the user-accessible area and the * secondary register area. diff --git a/qemu/hw/timer/etraxfs_timer.c b/qemu/hw/timer/etraxfs_timer.c index aee4990eb..36d8f462c 100644 --- a/qemu/hw/timer/etraxfs_timer.c +++ b/qemu/hw/timer/etraxfs_timer.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "qemu/timer.h" diff --git a/qemu/hw/timer/exynos4210_mct.c b/qemu/hw/timer/exynos4210_mct.c index 015bbaf1b..ae69345f0 100644 --- a/qemu/hw/timer/exynos4210_mct.c +++ b/qemu/hw/timer/exynos4210_mct.c @@ -52,6 +52,7 @@ * there is no way to avoid frequently events). */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "qemu/timer.h" #include "qemu/main-loop.h" @@ -1421,10 +1422,11 @@ static const MemoryRegionOps exynos4210_mct_ops = { }; /* MCT init */ -static int exynos4210_mct_init(SysBusDevice *dev) +static void exynos4210_mct_init(Object *obj) { int i; - Exynos4210MCTState *s = EXYNOS4210_MCT(dev); + Exynos4210MCTState *s = EXYNOS4210_MCT(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); QEMUBH *bh[2]; /* Global timer */ @@ -1449,19 +1451,15 @@ static int exynos4210_mct_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->l_timer[i].irq); } - memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_mct_ops, s, + memory_region_init_io(&s->iomem, obj, &exynos4210_mct_ops, s, "exynos4210-mct", MCT_SFR_SIZE); sysbus_init_mmio(dev, &s->iomem); - - return 0; } static void exynos4210_mct_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = exynos4210_mct_init; dc->reset = exynos4210_mct_reset; dc->vmsd = &vmstate_exynos4210_mct_state; } @@ -1470,6 +1468,7 @@ static const TypeInfo exynos4210_mct_info = { .name = TYPE_EXYNOS4210_MCT, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210MCTState), + .instance_init = exynos4210_mct_init, .class_init = exynos4210_mct_class_init, }; diff --git a/qemu/hw/timer/exynos4210_pwm.c b/qemu/hw/timer/exynos4210_pwm.c index 1c1a2b8ff..0e9e2e9bf 100644 --- a/qemu/hw/timer/exynos4210_pwm.c +++ b/qemu/hw/timer/exynos4210_pwm.c @@ -20,6 +20,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "qemu/timer.h" #include "qemu-common.h" @@ -379,9 +380,10 @@ static const MemoryRegionOps exynos4210_pwm_ops = { /* * PWM timer initialization */ -static int exynos4210_pwm_init(SysBusDevice *dev) +static void exynos4210_pwm_init(Object *obj) { - Exynos4210PWMState *s = EXYNOS4210_PWM(dev); + Exynos4210PWMState *s = EXYNOS4210_PWM(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); int i; QEMUBH *bh; @@ -393,19 +395,15 @@ static int exynos4210_pwm_init(SysBusDevice *dev) s->timer[i].parent = s; } - memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_pwm_ops, s, + memory_region_init_io(&s->iomem, obj, &exynos4210_pwm_ops, s, "exynos4210-pwm", EXYNOS4210_PWM_REG_MEM_SIZE); sysbus_init_mmio(dev, &s->iomem); - - return 0; } static void exynos4210_pwm_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = exynos4210_pwm_init; dc->reset = exynos4210_pwm_reset; dc->vmsd = &vmstate_exynos4210_pwm_state; } @@ -414,6 +412,7 @@ static const TypeInfo exynos4210_pwm_info = { .name = TYPE_EXYNOS4210_PWM, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210PWMState), + .instance_init = exynos4210_pwm_init, .class_init = exynos4210_pwm_class_init, }; diff --git a/qemu/hw/timer/exynos4210_rtc.c b/qemu/hw/timer/exynos4210_rtc.c index bf2ee9f80..da4dd451b 100644 --- a/qemu/hw/timer/exynos4210_rtc.c +++ b/qemu/hw/timer/exynos4210_rtc.c @@ -25,9 +25,11 @@ * CLKOUTEN Bit[9] not used */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "qemu/timer.h" #include "qemu-common.h" +#include "qemu/bcd.h" #include "hw/ptimer.h" #include "hw/hw.h" @@ -546,9 +548,10 @@ static const MemoryRegionOps exynos4210_rtc_ops = { /* * RTC timer initialization */ -static int exynos4210_rtc_init(SysBusDevice *dev) +static void exynos4210_rtc_init(Object *obj) { - Exynos4210RTCState *s = EXYNOS4210_RTC(dev); + Exynos4210RTCState *s = EXYNOS4210_RTC(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); QEMUBH *bh; bh = qemu_bh_new(exynos4210_rtc_tick, s); @@ -563,19 +566,15 @@ static int exynos4210_rtc_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->alm_irq); sysbus_init_irq(dev, &s->tick_irq); - memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_rtc_ops, s, + memory_region_init_io(&s->iomem, obj, &exynos4210_rtc_ops, s, "exynos4210-rtc", EXYNOS4210_RTC_REG_MEM_SIZE); sysbus_init_mmio(dev, &s->iomem); - - return 0; } static void exynos4210_rtc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = exynos4210_rtc_init; dc->reset = exynos4210_rtc_reset; dc->vmsd = &vmstate_exynos4210_rtc_state; } @@ -584,6 +583,7 @@ static const TypeInfo exynos4210_rtc_info = { .name = TYPE_EXYNOS4210_RTC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210RTCState), + .instance_init = exynos4210_rtc_init, .class_init = exynos4210_rtc_class_init, }; diff --git a/qemu/hw/timer/grlib_gptimer.c b/qemu/hw/timer/grlib_gptimer.c index d655bb2a2..dd000f5af 100644 --- a/qemu/hw/timer/grlib_gptimer.c +++ b/qemu/hw/timer/grlib_gptimer.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "qemu/timer.h" #include "hw/ptimer.h" diff --git a/qemu/hw/timer/hpet.c b/qemu/hw/timer/hpet.c index 2bb62211c..a2c18b30c 100644 --- a/qemu/hw/timer/hpet.c +++ b/qemu/hw/timer/hpet.c @@ -24,9 +24,11 @@ * This driver attempts to emulate an HPET device in software. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "ui/console.h" +#include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/timer.h" #include "hw/timer/hpet.h" @@ -116,22 +118,22 @@ static uint32_t timer_enabled(HPETTimer *t) static uint32_t hpet_time_after(uint64_t a, uint64_t b) { - return ((int32_t)(b) - (int32_t)(a) < 0); + return ((int32_t)(b - a) < 0); } static uint32_t hpet_time_after64(uint64_t a, uint64_t b) { - return ((int64_t)(b) - (int64_t)(a) < 0); + return ((int64_t)(b - a) < 0); } static uint64_t ticks_to_ns(uint64_t value) { - return (muldiv64(value, HPET_CLK_PERIOD, FS_PER_NS)); + return value * HPET_CLK_PERIOD; } static uint64_t ns_to_ticks(uint64_t value) { - return (muldiv64(value, FS_PER_NS, HPET_CLK_PERIOD)); + return value / HPET_CLK_PERIOD; } static uint64_t hpet_fixup_reg(uint64_t new, uint64_t old, uint64_t mask) @@ -199,12 +201,7 @@ static void update_irq(struct HPETTimer *timer, int set) if (!set || !timer_enabled(timer) || !hpet_enabled(timer->state)) { s->isr &= ~mask; if (!timer_fsb_route(timer)) { - /* fold the ICH PIRQ# pin's internal inversion logic into hpet */ - if (route >= ISA_NUM_IRQS) { - qemu_irq_raise(s->irqs[route]); - } else { - qemu_irq_lower(s->irqs[route]); - } + qemu_irq_lower(s->irqs[route]); } } else if (timer_fsb_route(timer)) { address_space_stl_le(&address_space_memory, timer->fsb >> 32, @@ -212,12 +209,7 @@ static void update_irq(struct HPETTimer *timer, int set) NULL); } else if (timer->config & HPET_TN_TYPE_LEVEL) { s->isr |= mask; - /* fold the ICH PIRQ# pin's internal inversion logic into hpet */ - if (route >= ISA_NUM_IRQS) { - qemu_irq_lower(s->irqs[route]); - } else { - qemu_irq_raise(s->irqs[route]); - } + qemu_irq_raise(s->irqs[route]); } else { s->isr &= ~mask; qemu_irq_pulse(s->irqs[route]); @@ -713,7 +705,7 @@ static void hpet_init(Object *obj) HPETState *s = HPET(obj); /* HPET Area */ - memory_region_init_io(&s->iomem, obj, &hpet_ram_ops, s, "hpet", 0x400); + memory_region_init_io(&s->iomem, obj, &hpet_ram_ops, s, "hpet", HPET_LEN); sysbus_init_mmio(sbd, &s->iomem); } @@ -758,7 +750,7 @@ static void hpet_realize(DeviceState *dev, Error **errp) /* 64-bit main counter; LegacyReplacementRoute. */ s->capability = 0x8086a001ULL; s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT; - s->capability |= ((HPET_CLK_PERIOD) << 32); + s->capability |= ((uint64_t)(HPET_CLK_PERIOD * FS_PER_NS) << 32); qdev_init_gpio_in(dev, hpet_handle_legacy_irq, 2); qdev_init_gpio_out(dev, &s->pit_enabled, 1); diff --git a/qemu/hw/timer/i8254.c b/qemu/hw/timer/i8254.c index 9b65a3369..5e61ad50a 100644 --- a/qemu/hw/timer/i8254.c +++ b/qemu/hw/timer/i8254.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/isa/isa.h" @@ -52,7 +53,7 @@ static int pit_get_count(PITChannelState *s) int counter; d = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->count_load_time, PIT_FREQ, - get_ticks_per_sec()); + NANOSECONDS_PER_SECOND); switch(s->mode) { case 0: case 1: @@ -262,7 +263,7 @@ static void pit_irq_timer_update(PITChannelState *s, int64_t current_time) #ifdef DEBUG_PIT printf("irq_level=%d next_delay=%f\n", irq_level, - (double)(expire_time - current_time) / get_ticks_per_sec()); + (double)(expire_time - current_time) / NANOSECONDS_PER_SECOND); #endif s->next_transition_time = expire_time; if (expire_time != -1) diff --git a/qemu/hw/timer/i8254_common.c b/qemu/hw/timer/i8254_common.c index 07345f668..e18299a48 100644 --- a/qemu/hw/timer/i8254_common.c +++ b/qemu/hw/timer/i8254_common.c @@ -22,6 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/isa/isa.h" @@ -46,7 +47,7 @@ int pit_get_out(PITChannelState *s, int64_t current_time) int out; d = muldiv64(current_time - s->count_load_time, PIT_FREQ, - get_ticks_per_sec()); + NANOSECONDS_PER_SECOND); switch (s->mode) { default: case 0: @@ -80,7 +81,7 @@ int64_t pit_get_next_transition_time(PITChannelState *s, int64_t current_time) int period2; d = muldiv64(current_time - s->count_load_time, PIT_FREQ, - get_ticks_per_sec()); + NANOSECONDS_PER_SECOND); switch (s->mode) { default: case 0: @@ -120,7 +121,7 @@ int64_t pit_get_next_transition_time(PITChannelState *s, int64_t current_time) break; } /* convert to timer units */ - next_time = s->count_load_time + muldiv64(next_time, get_ticks_per_sec(), + next_time = s->count_load_time + muldiv64(next_time, NANOSECONDS_PER_SECOND, PIT_FREQ); /* fix potential rounding problems */ /* XXX: better solution: use a clock at PIT_FREQ Hz */ diff --git a/qemu/hw/timer/imx_epit.c b/qemu/hw/timer/imx_epit.c index ffefc22f4..f5836e21f 100644 --- a/qemu/hw/timer/imx_epit.c +++ b/qemu/hw/timer/imx_epit.c @@ -5,25 +5,29 @@ * Copyright (c) 2011 NICTA Pty Ltd * Originally written by Hans Jiang * Updated by Peter Chubb - * Updated by Jean-Christophe Dubois + * Updated by Jean-Christophe Dubois * * This code is licensed under GPL version 2 or later. See * the COPYING file in the top-level directory. * */ -#include "hw/hw.h" -#include "qemu/bitops.h" -#include "qemu/timer.h" -#include "hw/ptimer.h" -#include "hw/sysbus.h" -#include "hw/arm/imx.h" +#include "qemu/osdep.h" +#include "hw/timer/imx_epit.h" +#include "hw/misc/imx_ccm.h" #include "qemu/main-loop.h" -#define TYPE_IMX_EPIT "imx.epit" +#ifndef DEBUG_IMX_EPIT +#define DEBUG_IMX_EPIT 0 +#endif -#define DEBUG_TIMER 0 -#if DEBUG_TIMER +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_EPIT) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_EPIT, \ + __func__, ##args); \ + } \ + } while (0) static char const *imx_epit_reg_name(uint32_t reg) { @@ -43,76 +47,17 @@ static char const *imx_epit_reg_name(uint32_t reg) } } -# define DPRINTF(fmt, args...) \ - do { fprintf(stderr, "%s: " fmt , __func__, ##args); } while (0) -#else -# define DPRINTF(fmt, args...) do {} while (0) -#endif - -/* - * Define to 1 for messages about attempts to - * access unimplemented registers or similar. - */ -#define DEBUG_IMPLEMENTATION 1 -#if DEBUG_IMPLEMENTATION -# define IPRINTF(fmt, args...) \ - do { fprintf(stderr, "%s: " fmt, __func__, ##args); } while (0) -#else -# define IPRINTF(fmt, args...) do {} while (0) -#endif - -#define IMX_EPIT(obj) \ - OBJECT_CHECK(IMXEPITState, (obj), TYPE_IMX_EPIT) - -/* - * EPIT: Enhanced periodic interrupt timer - */ - -#define CR_EN (1 << 0) -#define CR_ENMOD (1 << 1) -#define CR_OCIEN (1 << 2) -#define CR_RLD (1 << 3) -#define CR_PRESCALE_SHIFT (4) -#define CR_PRESCALE_MASK (0xfff) -#define CR_SWR (1 << 16) -#define CR_IOVW (1 << 17) -#define CR_DBGEN (1 << 18) -#define CR_WAITEN (1 << 19) -#define CR_DOZEN (1 << 20) -#define CR_STOPEN (1 << 21) -#define CR_CLKSRC_SHIFT (24) -#define CR_CLKSRC_MASK (0x3 << CR_CLKSRC_SHIFT) - -#define EPIT_TIMER_MAX 0XFFFFFFFFUL - /* * Exact clock frequencies vary from board to board. * These are typical. */ static const IMXClk imx_epit_clocks[] = { - 0, /* 00 disabled */ - IPG, /* 01 ipg_clk, ~532MHz */ - IPG, /* 10 ipg_clk_highfreq */ - CLK_32k, /* 11 ipg_clk_32k -- ~32kHz */ + CLK_NONE, /* 00 disabled */ + CLK_IPG, /* 01 ipg_clk, ~532MHz */ + CLK_IPG_HIGH, /* 10 ipg_clk_highfreq */ + CLK_32k, /* 11 ipg_clk_32k -- ~32kHz */ }; -typedef struct { - SysBusDevice busdev; - ptimer_state *timer_reload; - ptimer_state *timer_cmp; - MemoryRegion iomem; - DeviceState *ccm; - - uint32_t cr; - uint32_t sr; - uint32_t lr; - uint32_t cmp; - uint32_t cnt; - - uint32_t freq; - qemu_irq irq; -} IMXEPITState; - /* * Update interrupt status */ @@ -129,20 +74,18 @@ static void imx_epit_set_freq(IMXEPITState *s) { uint32_t clksrc; uint32_t prescaler; - uint32_t freq; clksrc = extract32(s->cr, CR_CLKSRC_SHIFT, 2); prescaler = 1 + extract32(s->cr, CR_PRESCALE_SHIFT, 12); - freq = imx_clock_frequency(s->ccm, imx_epit_clocks[clksrc]) / prescaler; + s->freq = imx_ccm_get_clock_frequency(s->ccm, + imx_epit_clocks[clksrc]) / prescaler; - s->freq = freq; + DPRINTF("Setting ptimer frequency to %u\n", s->freq); - DPRINTF("Setting ptimer frequency to %u\n", freq); - - if (freq) { - ptimer_set_freq(s->timer_reload, freq); - ptimer_set_freq(s->timer_cmp, freq); + if (s->freq) { + ptimer_set_freq(s->timer_reload, s->freq); + ptimer_set_freq(s->timer_cmp, s->freq); } } @@ -174,18 +117,17 @@ static void imx_epit_reset(DeviceState *dev) static uint32_t imx_epit_update_count(IMXEPITState *s) { - s->cnt = ptimer_get_count(s->timer_reload); + s->cnt = ptimer_get_count(s->timer_reload); - return s->cnt; + return s->cnt; } static uint64_t imx_epit_read(void *opaque, hwaddr offset, unsigned size) { IMXEPITState *s = IMX_EPIT(opaque); uint32_t reg_value = 0; - uint32_t reg = offset >> 2; - switch (reg) { + switch (offset >> 2) { case 0: /* Control Register */ reg_value = s->cr; break; @@ -208,11 +150,12 @@ static uint64_t imx_epit_read(void *opaque, hwaddr offset, unsigned size) break; default: - IPRINTF("Bad offset %x\n", reg); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_EPIT, __func__, offset); break; } - DPRINTF("(%s) = 0x%08x\n", imx_epit_reg_name(reg), reg_value); + DPRINTF("(%s) = 0x%08x\n", imx_epit_reg_name(offset >> 2), reg_value); return reg_value; } @@ -237,12 +180,12 @@ static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { IMXEPITState *s = IMX_EPIT(opaque); - uint32_t reg = offset >> 2; uint64_t oldcr; - DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(reg), (uint32_t)value); + DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(offset >> 2), + (uint32_t)value); - switch (reg) { + switch (offset >> 2) { case 0: /* CR */ oldcr = s->cr; @@ -318,7 +261,8 @@ static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value, break; default: - IPRINTF("Bad offset %x\n", reg); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_EPIT, __func__, offset); break; } @@ -333,24 +277,14 @@ static void imx_epit_cmp(void *opaque) imx_epit_update_int(s); } -void imx_timerp_create(const hwaddr addr, qemu_irq irq, DeviceState *ccm) -{ - IMXEPITState *pp; - DeviceState *dev; - - dev = sysbus_create_simple(TYPE_IMX_EPIT, addr, irq); - pp = IMX_EPIT(dev); - pp->ccm = ccm; -} - static const MemoryRegionOps imx_epit_ops = { - .read = imx_epit_read, - .write = imx_epit_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .read = imx_epit_read, + .write = imx_epit_write, + .endianness = DEVICE_NATIVE_ENDIAN, }; static const VMStateDescription vmstate_imx_timer_epit = { - .name = "imx.epit", + .name = TYPE_IMX_EPIT, .version_id = 2, .minimum_version_id = 2, .fields = (VMStateField[]) { diff --git a/qemu/hw/timer/imx_gpt.c b/qemu/hw/timer/imx_gpt.c index 3b3101084..ab2e213a1 100644 --- a/qemu/hw/timer/imx_gpt.c +++ b/qemu/hw/timer/imx_gpt.c @@ -5,28 +5,29 @@ * Copyright (c) 2011 NICTA Pty Ltd * Originally written by Hans Jiang * Updated by Peter Chubb - * Updated by Jean-Christophe Dubois + * Updated by Jean-Christophe Dubois * * This code is licensed under GPL version 2 or later. See * the COPYING file in the top-level directory. * */ -#include "hw/hw.h" -#include "qemu/bitops.h" -#include "qemu/timer.h" -#include "hw/ptimer.h" -#include "hw/sysbus.h" -#include "hw/arm/imx.h" +#include "qemu/osdep.h" +#include "hw/timer/imx_gpt.h" +#include "hw/misc/imx_ccm.h" #include "qemu/main-loop.h" -#define TYPE_IMX_GPT "imx.gpt" +#ifndef DEBUG_IMX_GPT +#define DEBUG_IMX_GPT 0 +#endif -/* - * Define to 1 for debug messages - */ -#define DEBUG_TIMER 0 -#if DEBUG_TIMER +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_GPT) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_GPT, \ + __func__, ##args); \ + } \ + } while (0) static char const *imx_gpt_reg_name(uint32_t reg) { @@ -56,94 +57,8 @@ static char const *imx_gpt_reg_name(uint32_t reg) } } -# define DPRINTF(fmt, args...) \ - do { printf("%s: " fmt , __func__, ##args); } while (0) -#else -# define DPRINTF(fmt, args...) do {} while (0) -#endif - -/* - * Define to 1 for messages about attempts to - * access unimplemented registers or similar. - */ -#define DEBUG_IMPLEMENTATION 1 -#if DEBUG_IMPLEMENTATION -# define IPRINTF(fmt, args...) \ - do { fprintf(stderr, "%s: " fmt, __func__, ##args); } while (0) -#else -# define IPRINTF(fmt, args...) do {} while (0) -#endif - -#define IMX_GPT(obj) \ - OBJECT_CHECK(IMXGPTState, (obj), TYPE_IMX_GPT) -/* - * GPT : General purpose timer - * - * This timer counts up continuously while it is enabled, resetting itself - * to 0 when it reaches GPT_TIMER_MAX (in freerun mode) or when it - * reaches the value of one of the ocrX (in periodic mode). - */ - -#define GPT_TIMER_MAX 0XFFFFFFFFUL - -/* Control register. Not all of these bits have any effect (yet) */ -#define GPT_CR_EN (1 << 0) /* GPT Enable */ -#define GPT_CR_ENMOD (1 << 1) /* GPT Enable Mode */ -#define GPT_CR_DBGEN (1 << 2) /* GPT Debug mode enable */ -#define GPT_CR_WAITEN (1 << 3) /* GPT Wait Mode Enable */ -#define GPT_CR_DOZEN (1 << 4) /* GPT Doze mode enable */ -#define GPT_CR_STOPEN (1 << 5) /* GPT Stop Mode Enable */ -#define GPT_CR_CLKSRC_SHIFT (6) -#define GPT_CR_CLKSRC_MASK (0x7) - -#define GPT_CR_FRR (1 << 9) /* Freerun or Restart */ -#define GPT_CR_SWR (1 << 15) /* Software Reset */ -#define GPT_CR_IM1 (3 << 16) /* Input capture channel 1 mode (2 bits) */ -#define GPT_CR_IM2 (3 << 18) /* Input capture channel 2 mode (2 bits) */ -#define GPT_CR_OM1 (7 << 20) /* Output Compare Channel 1 Mode (3 bits) */ -#define GPT_CR_OM2 (7 << 23) /* Output Compare Channel 2 Mode (3 bits) */ -#define GPT_CR_OM3 (7 << 26) /* Output Compare Channel 3 Mode (3 bits) */ -#define GPT_CR_FO1 (1 << 29) /* Force Output Compare Channel 1 */ -#define GPT_CR_FO2 (1 << 30) /* Force Output Compare Channel 2 */ -#define GPT_CR_FO3 (1 << 31) /* Force Output Compare Channel 3 */ - -#define GPT_SR_OF1 (1 << 0) -#define GPT_SR_OF2 (1 << 1) -#define GPT_SR_OF3 (1 << 2) -#define GPT_SR_ROV (1 << 5) - -#define GPT_IR_OF1IE (1 << 0) -#define GPT_IR_OF2IE (1 << 1) -#define GPT_IR_OF3IE (1 << 2) -#define GPT_IR_ROVIE (1 << 5) - -typedef struct { - SysBusDevice busdev; - ptimer_state *timer; - MemoryRegion iomem; - DeviceState *ccm; - - uint32_t cr; - uint32_t pr; - uint32_t sr; - uint32_t ir; - uint32_t ocr1; - uint32_t ocr2; - uint32_t ocr3; - uint32_t icr1; - uint32_t icr2; - uint32_t cnt; - - uint32_t next_timeout; - uint32_t next_int; - - uint32_t freq; - - qemu_irq irq; -} IMXGPTState; - static const VMStateDescription vmstate_imx_timer_gpt = { - .name = "imx.gpt", + .name = TYPE_IMX_GPT, .version_id = 3, .minimum_version_id = 3, .fields = (VMStateField[]) { @@ -166,27 +81,27 @@ static const VMStateDescription vmstate_imx_timer_gpt = { }; static const IMXClk imx_gpt_clocks[] = { - NOCLK, /* 000 No clock source */ - IPG, /* 001 ipg_clk, 532MHz*/ - IPG, /* 010 ipg_clk_highfreq */ - NOCLK, /* 011 not defined */ - CLK_32k, /* 100 ipg_clk_32k */ - NOCLK, /* 101 not defined */ - NOCLK, /* 110 not defined */ - NOCLK, /* 111 not defined */ + CLK_NONE, /* 000 No clock source */ + CLK_IPG, /* 001 ipg_clk, 532MHz*/ + CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */ + CLK_NONE, /* 011 not defined */ + CLK_32k, /* 100 ipg_clk_32k */ + CLK_NONE, /* 101 not defined */ + CLK_NONE, /* 110 not defined */ + CLK_NONE, /* 111 not defined */ }; static void imx_gpt_set_freq(IMXGPTState *s) { uint32_t clksrc = extract32(s->cr, GPT_CR_CLKSRC_SHIFT, 3); - uint32_t freq = imx_clock_frequency(s->ccm, imx_gpt_clocks[clksrc]) - / (1 + s->pr); - s->freq = freq; - DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, freq); + s->freq = imx_ccm_get_clock_frequency(s->ccm, + imx_gpt_clocks[clksrc]) / (1 + s->pr); + + DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, s->freq); - if (freq) { - ptimer_set_freq(s->timer, freq); + if (s->freq) { + ptimer_set_freq(s->timer, s->freq); } } @@ -207,7 +122,7 @@ static uint32_t imx_gpt_update_count(IMXGPTState *s) } static inline uint32_t imx_gpt_find_limit(uint32_t count, uint32_t reg, - uint32_t timeout) + uint32_t timeout) { if ((count < reg) && (timeout > reg)) { timeout = reg; @@ -219,7 +134,7 @@ static inline uint32_t imx_gpt_find_limit(uint32_t count, uint32_t reg, static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event) { uint32_t timeout = GPT_TIMER_MAX; - uint32_t count = 0; + uint32_t count; long long limit; if (!(s->cr & GPT_CR_EN)) { @@ -227,20 +142,23 @@ static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event) return; } - if (event) { - /* This is a timer event */ + /* update the count */ + count = imx_gpt_update_count(s); - if ((s->cr & GPT_CR_FRR) && (s->next_timeout != GPT_TIMER_MAX)) { - /* - * if we are in free running mode and we have not reached - * the GPT_TIMER_MAX limit, then update the count + if (event) { + /* + * This is an event (the ptimer reached 0 and stopped), and the + * timer counter is now equal to s->next_timeout. + */ + if (!(s->cr & GPT_CR_FRR) && (count == s->ocr1)) { + /* We are in restart mode and we crossed the compare channel 1 + * value. We need to reset the counter to 0. */ - count = imx_gpt_update_count(s); + count = s->cnt = s->next_timeout = 0; + } else if (count == GPT_TIMER_MAX) { + /* We reached GPT_TIMER_MAX so we need to rollover */ + count = s->cnt = s->next_timeout = 0; } - } else { - /* not a timer event, then just update the count */ - - count = imx_gpt_update_count(s); } /* now, find the next timeout related to count */ @@ -298,9 +216,8 @@ static uint64_t imx_gpt_read(void *opaque, hwaddr offset, unsigned size) { IMXGPTState *s = IMX_GPT(opaque); uint32_t reg_value = 0; - uint32_t reg = offset >> 2; - switch (reg) { + switch (offset >> 2) { case 0: /* Control Register */ reg_value = s->cr; break; @@ -330,12 +247,14 @@ static uint64_t imx_gpt_read(void *opaque, hwaddr offset, unsigned size) break; case 7: /* input Capture Register 1 */ - qemu_log_mask(LOG_UNIMP, "icr1 feature is not implemented\n"); + qemu_log_mask(LOG_UNIMP, "[%s]%s: icr1 feature is not implemented\n", + TYPE_IMX_GPT, __func__); reg_value = s->icr1; break; case 8: /* input Capture Register 2 */ - qemu_log_mask(LOG_UNIMP, "icr2 feature is not implemented\n"); + qemu_log_mask(LOG_UNIMP, "[%s]%s: icr2 feature is not implemented\n", + TYPE_IMX_GPT, __func__); reg_value = s->icr2; break; @@ -345,11 +264,12 @@ static uint64_t imx_gpt_read(void *opaque, hwaddr offset, unsigned size) break; default: - IPRINTF("Bad offset %x\n", reg); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_GPT, __func__, offset); break; } - DPRINTF("(%s) = 0x%08x\n", imx_gpt_reg_name(reg), reg_value); + DPRINTF("(%s) = 0x%08x\n", imx_gpt_reg_name(offset >> 2), reg_value); return reg_value; } @@ -396,12 +316,11 @@ static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value, { IMXGPTState *s = IMX_GPT(opaque); uint32_t oldreg; - uint32_t reg = offset >> 2; - DPRINTF("(%s, value = 0x%08x)\n", imx_gpt_reg_name(reg), + DPRINTF("(%s, value = 0x%08x)\n", imx_gpt_reg_name(offset >> 2), (uint32_t)value); - switch (reg) { + switch (offset >> 2) { case 0: oldreg = s->cr; s->cr = value & ~0x7c14; @@ -477,7 +396,8 @@ static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value, break; default: - IPRINTF("Bad offset %x\n", reg); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_GPT, __func__, offset); break; } } @@ -522,16 +442,6 @@ static void imx_gpt_realize(DeviceState *dev, Error **errp) s->timer = ptimer_init(bh); } -void imx_timerg_create(const hwaddr addr, qemu_irq irq, DeviceState *ccm) -{ - IMXGPTState *pp; - DeviceState *dev; - - dev = sysbus_create_simple(TYPE_IMX_GPT, addr, irq); - pp = IMX_GPT(dev); - pp->ccm = ccm; -} - static void imx_gpt_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/qemu/hw/timer/lm32_timer.c b/qemu/hw/timer/lm32_timer.c index d2ab1e74b..3198355aa 100644 --- a/qemu/hw/timer/lm32_timer.c +++ b/qemu/hw/timer/lm32_timer.c @@ -21,6 +21,7 @@ * http://www.latticesemi.com/documents/mico32timer.pdf */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "trace.h" diff --git a/qemu/hw/timer/m48t59.c b/qemu/hw/timer/m48t59.c index 8ab683dda..e46ca8839 100644 --- a/qemu/hw/timer/m48t59.c +++ b/qemu/hw/timer/m48t59.c @@ -22,13 +22,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/timer/m48t59.h" +#include "qapi/error.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "hw/sysbus.h" #include "hw/isa/isa.h" #include "exec/address-spaces.h" +#include "qemu/bcd.h" //#define DEBUG_NVRAM @@ -590,10 +593,8 @@ static void nvram_writel (void *opaque, hwaddr addr, uint32_t value) static uint32_t nvram_readb (void *opaque, hwaddr addr) { M48t59State *NVRAM = opaque; - uint32_t retval; - retval = m48t59_read(NVRAM, addr); - return retval; + return m48t59_read(NVRAM, addr); } static uint32_t nvram_readw (void *opaque, hwaddr addr) diff --git a/qemu/hw/timer/mc146818rtc.c b/qemu/hw/timer/mc146818rtc.c index a9f0efd5e..2ac0fd3e4 100644 --- a/qemu/hw/timer/mc146818rtc.c +++ b/qemu/hw/timer/mc146818rtc.c @@ -21,6 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "config-target.h" +#include "qemu/cutils.h" +#include "qemu/bcd.h" #include "hw/hw.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" @@ -105,8 +109,8 @@ static uint64_t get_guest_rtc_ns(RTCState *s) uint64_t guest_rtc; uint64_t guest_clock = qemu_clock_get_ns(rtc_clock); - guest_rtc = s->base_rtc * NANOSECONDS_PER_SECOND - + guest_clock - s->last_update + s->offset; + guest_rtc = s->base_rtc * NANOSECONDS_PER_SECOND + + guest_clock - s->last_update + s->offset; return guest_rtc; } @@ -119,7 +123,7 @@ static void rtc_coalesced_timer_update(RTCState *s) /* divide each RTC interval to 2 - 8 smaller intervals */ int c = MIN(s->irq_coalesced, 7) + 1; int64_t next_clock = qemu_clock_get_ns(rtc_clock) + - muldiv64(s->period / c, get_ticks_per_sec(), RTC_CLOCK_RATE); + muldiv64(s->period / c, NANOSECONDS_PER_SECOND, RTC_CLOCK_RATE); timer_mod(s->coalesced_timer, next_clock); } } @@ -165,10 +169,12 @@ static void periodic_timer_update(RTCState *s, int64_t current_time) s->period = period; #endif /* compute 32 khz clock */ - cur_clock = muldiv64(current_time, RTC_CLOCK_RATE, get_ticks_per_sec()); + cur_clock = + muldiv64(current_time, RTC_CLOCK_RATE, NANOSECONDS_PER_SECOND); + next_irq_clock = (cur_clock & ~(period - 1)) + period; - s->next_periodic_time = - muldiv64(next_irq_clock, get_ticks_per_sec(), RTC_CLOCK_RATE) + 1; + s->next_periodic_time = muldiv64(next_irq_clock, NANOSECONDS_PER_SECOND, + RTC_CLOCK_RATE) + 1; timer_mod(s->periodic_timer, s->next_periodic_time); } else { #ifdef TARGET_I386 diff --git a/qemu/hw/timer/milkymist-sysctl.c b/qemu/hw/timer/milkymist-sysctl.c index 30535a4e3..5f2948037 100644 --- a/qemu/hw/timer/milkymist-sysctl.c +++ b/qemu/hw/timer/milkymist-sysctl.c @@ -21,6 +21,7 @@ * http://www.milkymist.org/socdoc/sysctl.pdf */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "sysemu/sysemu.h" diff --git a/qemu/hw/timer/omap_gptimer.c b/qemu/hw/timer/omap_gptimer.c index b8c8c0137..3a4386304 100644 --- a/qemu/hw/timer/omap_gptimer.c +++ b/qemu/hw/timer/omap_gptimer.c @@ -17,6 +17,7 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "qemu/timer.h" #include "hw/arm/omap.h" @@ -401,7 +402,7 @@ static void omap_gp_timer_write(void *opaque, hwaddr addr, if (s->trigger == gpt_trigger_none) omap_gp_timer_out(s, s->scpwm); /* TODO: make sure this doesn't overflow 32-bits */ - s->ticks_per_sec = get_ticks_per_sec() << (s->pre ? s->ptv + 1 : 0); + s->ticks_per_sec = NANOSECONDS_PER_SECOND << (s->pre ? s->ptv + 1 : 0); omap_gp_timer_update(s); break; @@ -468,8 +469,7 @@ static const MemoryRegionOps omap_gp_timer_ops = { struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta, qemu_irq irq, omap_clk fclk, omap_clk iclk) { - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) - g_malloc0(sizeof(struct omap_gp_timer_s)); + struct omap_gp_timer_s *s = g_new0(struct omap_gp_timer_s, 1); s->ta = ta; s->irq = irq; diff --git a/qemu/hw/timer/omap_synctimer.c b/qemu/hw/timer/omap_synctimer.c index 8e50488d1..9ee651979 100644 --- a/qemu/hw/timer/omap_synctimer.c +++ b/qemu/hw/timer/omap_synctimer.c @@ -17,6 +17,7 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "qemu/timer.h" #include "hw/arm/omap.h" @@ -28,7 +29,8 @@ struct omap_synctimer_s { /* 32-kHz Sync Timer of the OMAP2 */ static uint32_t omap_synctimer_read(struct omap_synctimer_s *s) { - return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 0x8000, get_ticks_per_sec()); + return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 0x8000, + NANOSECONDS_PER_SECOND); } void omap_synctimer_reset(struct omap_synctimer_s *s) diff --git a/qemu/hw/timer/pl031.c b/qemu/hw/timer/pl031.c index 34d9b44e7..38e0cb5ad 100644 --- a/qemu/hw/timer/pl031.c +++ b/qemu/hw/timer/pl031.c @@ -11,9 +11,11 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" +#include "qemu/cutils.h" //#define DEBUG_PL031 @@ -79,7 +81,7 @@ static void pl031_interrupt(void * opaque) static uint32_t pl031_get_count(PL031State *s) { int64_t now = qemu_clock_get_ns(rtc_clock); - return s->tick_offset + now / get_ticks_per_sec(); + return s->tick_offset + now / NANOSECONDS_PER_SECOND; } static void pl031_set_alarm(PL031State *s) @@ -95,7 +97,7 @@ static void pl031_set_alarm(PL031State *s) pl031_interrupt(s); } else { int64_t now = qemu_clock_get_ns(rtc_clock); - timer_mod(s->timer, now + (int64_t)ticks * get_ticks_per_sec()); + timer_mod(s->timer, now + (int64_t)ticks * NANOSECONDS_PER_SECOND); } } @@ -191,21 +193,21 @@ static const MemoryRegionOps pl031_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int pl031_init(SysBusDevice *dev) +static void pl031_init(Object *obj) { - PL031State *s = PL031(dev); + PL031State *s = PL031(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); struct tm tm; - memory_region_init_io(&s->iomem, OBJECT(s), &pl031_ops, s, "pl031", 0x1000); + memory_region_init_io(&s->iomem, obj, &pl031_ops, s, "pl031", 0x1000); sysbus_init_mmio(dev, &s->iomem); sysbus_init_irq(dev, &s->irq); qemu_get_timedate(&tm, 0); s->tick_offset = mktimegm(&tm) - - qemu_clock_get_ns(rtc_clock) / get_ticks_per_sec(); + qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND; s->timer = timer_new_ns(rtc_clock, pl031_interrupt, s); - return 0; } static void pl031_pre_save(void *opaque) @@ -215,7 +217,7 @@ static void pl031_pre_save(void *opaque) /* tick_offset is base_time - rtc_clock base time. Instead, we want to * store the base time relative to the QEMU_CLOCK_VIRTUAL for backwards-compatibility. */ int64_t delta = qemu_clock_get_ns(rtc_clock) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - s->tick_offset_vmstate = s->tick_offset + delta / get_ticks_per_sec(); + s->tick_offset_vmstate = s->tick_offset + delta / NANOSECONDS_PER_SECOND; } static int pl031_post_load(void *opaque, int version_id) @@ -223,7 +225,7 @@ static int pl031_post_load(void *opaque, int version_id) PL031State *s = opaque; int64_t delta = qemu_clock_get_ns(rtc_clock) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - s->tick_offset = s->tick_offset_vmstate - delta / get_ticks_per_sec(); + s->tick_offset = s->tick_offset_vmstate - delta / NANOSECONDS_PER_SECOND; pl031_set_alarm(s); return 0; } @@ -248,9 +250,7 @@ static const VMStateDescription vmstate_pl031 = { static void pl031_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = pl031_init; dc->vmsd = &vmstate_pl031; } @@ -258,6 +258,7 @@ static const TypeInfo pl031_info = { .name = TYPE_PL031, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PL031State), + .instance_init = pl031_init, .class_init = pl031_class_init, }; diff --git a/qemu/hw/timer/puv3_ost.c b/qemu/hw/timer/puv3_ost.c index fa9eefd92..93650b799 100644 --- a/qemu/hw/timer/puv3_ost.c +++ b/qemu/hw/timer/puv3_ost.c @@ -8,6 +8,7 @@ * published by the Free Software Foundation, or any later version. * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/ptimer.h" #include "qemu/main-loop.h" diff --git a/qemu/hw/timer/pxa2xx_timer.c b/qemu/hw/timer/pxa2xx_timer.c index 130e9dc34..59002b407 100644 --- a/qemu/hw/timer/pxa2xx_timer.c +++ b/qemu/hw/timer/pxa2xx_timer.c @@ -7,6 +7,7 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" @@ -118,11 +119,11 @@ static void pxa2xx_timer_update(void *opaque, uint64_t now_qemu) uint64_t new_qemu; now_vm = s->clock + - muldiv64(now_qemu - s->lastload, s->freq, get_ticks_per_sec()); + muldiv64(now_qemu - s->lastload, s->freq, NANOSECONDS_PER_SECOND); for (i = 0; i < 4; i ++) { new_qemu = now_qemu + muldiv64((uint32_t) (s->timer[i].value - now_vm), - get_ticks_per_sec(), s->freq); + NANOSECONDS_PER_SECOND, s->freq); timer_mod(s->timer[i].qtimer, new_qemu); } } @@ -147,10 +148,10 @@ static void pxa2xx_timer_update4(void *opaque, uint64_t now_qemu, int n) now_vm = s->tm4[counter].clock + muldiv64(now_qemu - s->tm4[counter].lastload, - s->tm4[counter].freq, get_ticks_per_sec()); + s->tm4[counter].freq, NANOSECONDS_PER_SECOND); new_qemu = now_qemu + muldiv64((uint32_t) (s->tm4[n].tm.value - now_vm), - get_ticks_per_sec(), s->tm4[counter].freq); + NANOSECONDS_PER_SECOND, s->tm4[counter].freq); timer_mod(s->tm4[n].tm.qtimer, new_qemu); } @@ -189,7 +190,7 @@ static uint64_t pxa2xx_timer_read(void *opaque, hwaddr offset, return s->tm4[tm].tm.value; case OSCR: return s->clock + muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - - s->lastload, s->freq, get_ticks_per_sec()); + s->lastload, s->freq, NANOSECONDS_PER_SECOND); case OSCR11: tm ++; /* fall through */ case OSCR10: tm ++; @@ -213,15 +214,17 @@ static uint64_t pxa2xx_timer_read(void *opaque, hwaddr offset, s->snapshot = s->tm4[tm - 1].clock + muldiv64( qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->tm4[tm - 1].lastload, - s->tm4[tm - 1].freq, get_ticks_per_sec()); + s->tm4[tm - 1].freq, NANOSECONDS_PER_SECOND); else s->snapshot = s->tm4[tm - 1].clock; } if (!s->tm4[tm].freq) return s->tm4[tm].clock; - return s->tm4[tm].clock + muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - - s->tm4[tm].lastload, s->tm4[tm].freq, get_ticks_per_sec()); + return s->tm4[tm].clock + + muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - + s->tm4[tm].lastload, s->tm4[tm].freq, + NANOSECONDS_PER_SECOND); case OIER: return s->irq_enabled; case OSSR: /* Status register */ @@ -432,10 +435,10 @@ static int pxa25x_timer_post_load(void *opaque, int version_id) return 0; } -static int pxa2xx_timer_init(SysBusDevice *dev) +static void pxa2xx_timer_init(Object *obj) { - PXA2xxTimerInfo *s = PXA2XX_TIMER(dev); - int i; + PXA2xxTimerInfo *s = PXA2XX_TIMER(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); s->irq_enabled = 0; s->oldclock = 0; @@ -443,16 +446,28 @@ static int pxa2xx_timer_init(SysBusDevice *dev) s->lastload = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); s->reset3 = 0; + memory_region_init_io(&s->iomem, obj, &pxa2xx_timer_ops, s, + "pxa2xx-timer", 0x00001000); + sysbus_init_mmio(dev, &s->iomem); +} + +static void pxa2xx_timer_realize(DeviceState *dev, Error **errp) +{ + PXA2xxTimerInfo *s = PXA2XX_TIMER(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + int i; + for (i = 0; i < 4; i ++) { s->timer[i].value = 0; - sysbus_init_irq(dev, &s->timer[i].irq); + sysbus_init_irq(sbd, &s->timer[i].irq); s->timer[i].info = s; s->timer[i].num = i; s->timer[i].qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - pxa2xx_timer_tick, &s->timer[i]); + pxa2xx_timer_tick, &s->timer[i]); } + if (s->flags & (1 << PXA2XX_TIMER_HAVE_TM4)) { - sysbus_init_irq(dev, &s->irq4); + sysbus_init_irq(sbd, &s->irq4); for (i = 0; i < 8; i ++) { s->tm4[i].tm.value = 0; @@ -461,15 +476,9 @@ static int pxa2xx_timer_init(SysBusDevice *dev) s->tm4[i].freq = 0; s->tm4[i].control = 0x0; s->tm4[i].tm.qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - pxa2xx_timer_tick4, &s->tm4[i]); + pxa2xx_timer_tick4, &s->tm4[i]); } } - - memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_timer_ops, s, - "pxa2xx-timer", 0x00001000); - sysbus_init_mmio(dev, &s->iomem); - - return 0; } static const VMStateDescription vmstate_pxa2xx_timer0_regs = { @@ -572,9 +581,8 @@ static const TypeInfo pxa27x_timer_dev_info = { static void pxa2xx_timer_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(oc); - sdc->init = pxa2xx_timer_init; + dc->realize = pxa2xx_timer_realize; dc->vmsd = &vmstate_pxa2xx_timer_regs; } @@ -582,6 +590,7 @@ static const TypeInfo pxa2xx_timer_type_info = { .name = TYPE_PXA2XX_TIMER, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PXA2xxTimerInfo), + .instance_init = pxa2xx_timer_init, .abstract = true, .class_init = pxa2xx_timer_class_init, }; diff --git a/qemu/hw/timer/sh_timer.c b/qemu/hw/timer/sh_timer.c index 07f0670b5..255b2fc91 100644 --- a/qemu/hw/timer/sh_timer.c +++ b/qemu/hw/timer/sh_timer.c @@ -8,6 +8,7 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sh4/sh.h" #include "qemu/timer.h" diff --git a/qemu/hw/timer/slavio_timer.c b/qemu/hw/timer/slavio_timer.c index 45d97e667..fb3e08bed 100644 --- a/qemu/hw/timer/slavio_timer.c +++ b/qemu/hw/timer/slavio_timer.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sparc/sun4m.h" #include "qemu/timer.h" #include "hw/ptimer.h" diff --git a/qemu/hw/timer/stm32f2xx_timer.c b/qemu/hw/timer/stm32f2xx_timer.c index ecadf9df8..55dacbbe3 100644 --- a/qemu/hw/timer/stm32f2xx_timer.c +++ b/qemu/hw/timer/stm32f2xx_timer.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/timer/stm32f2xx_timer.h" #ifndef STM_TIMER_ERR_DEBUG diff --git a/qemu/hw/timer/tusb6010.c b/qemu/hw/timer/tusb6010.c deleted file mode 100644 index 459c748e1..000000000 --- a/qemu/hw/timer/tusb6010.c +++ /dev/null @@ -1,816 +0,0 @@ -/* - * Texas Instruments TUSB6010 emulation. - * Based on reverse-engineering of a linux driver. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * 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 . - */ -#include "qemu-common.h" -#include "qemu/timer.h" -#include "hw/usb.h" -#include "hw/arm/omap.h" -#include "hw/irq.h" -#include "hw/devices.h" -#include "hw/sysbus.h" - -#define TYPE_TUSB6010 "tusb6010" -#define TUSB(obj) OBJECT_CHECK(TUSBState, (obj), TYPE_TUSB6010) - -typedef struct TUSBState { - SysBusDevice parent_obj; - - MemoryRegion iomem[2]; - qemu_irq irq; - MUSBState *musb; - QEMUTimer *otg_timer; - QEMUTimer *pwr_timer; - - int power; - uint32_t scratch; - uint16_t test_reset; - uint32_t prcm_config; - uint32_t prcm_mngmt; - uint16_t otg_status; - uint32_t dev_config; - int host_mode; - uint32_t intr; - uint32_t intr_ok; - uint32_t mask; - uint32_t usbip_intr; - uint32_t usbip_mask; - uint32_t gpio_intr; - uint32_t gpio_mask; - uint32_t gpio_config; - uint32_t dma_intr; - uint32_t dma_mask; - uint32_t dma_map; - uint32_t dma_config; - uint32_t ep0_config; - uint32_t rx_config[15]; - uint32_t tx_config[15]; - uint32_t wkup_mask; - uint32_t pullup[2]; - uint32_t control_config; - uint32_t otg_timer_val; -} TUSBState; - -#define TUSB_DEVCLOCK 60000000 /* 60 MHz */ - -#define TUSB_VLYNQ_CTRL 0x004 - -/* Mentor Graphics OTG core registers. */ -#define TUSB_BASE_OFFSET 0x400 - -/* FIFO registers, 32-bit. */ -#define TUSB_FIFO_BASE 0x600 - -/* Device System & Control registers, 32-bit. */ -#define TUSB_SYS_REG_BASE 0x800 - -#define TUSB_DEV_CONF (TUSB_SYS_REG_BASE + 0x000) -#define TUSB_DEV_CONF_USB_HOST_MODE (1 << 16) -#define TUSB_DEV_CONF_PROD_TEST_MODE (1 << 15) -#define TUSB_DEV_CONF_SOFT_ID (1 << 1) -#define TUSB_DEV_CONF_ID_SEL (1 << 0) - -#define TUSB_PHY_OTG_CTRL_ENABLE (TUSB_SYS_REG_BASE + 0x004) -#define TUSB_PHY_OTG_CTRL (TUSB_SYS_REG_BASE + 0x008) -#define TUSB_PHY_OTG_CTRL_WRPROTECT (0xa5 << 24) -#define TUSB_PHY_OTG_CTRL_O_ID_PULLUP (1 << 23) -#define TUSB_PHY_OTG_CTRL_O_VBUS_DET_EN (1 << 19) -#define TUSB_PHY_OTG_CTRL_O_SESS_END_EN (1 << 18) -#define TUSB_PHY_OTG_CTRL_TESTM2 (1 << 17) -#define TUSB_PHY_OTG_CTRL_TESTM1 (1 << 16) -#define TUSB_PHY_OTG_CTRL_TESTM0 (1 << 15) -#define TUSB_PHY_OTG_CTRL_TX_DATA2 (1 << 14) -#define TUSB_PHY_OTG_CTRL_TX_GZ2 (1 << 13) -#define TUSB_PHY_OTG_CTRL_TX_ENABLE2 (1 << 12) -#define TUSB_PHY_OTG_CTRL_DM_PULLDOWN (1 << 11) -#define TUSB_PHY_OTG_CTRL_DP_PULLDOWN (1 << 10) -#define TUSB_PHY_OTG_CTRL_OSC_EN (1 << 9) -#define TUSB_PHY_OTG_CTRL_PHYREF_CLK(v) (((v) & 3) << 7) -#define TUSB_PHY_OTG_CTRL_PD (1 << 6) -#define TUSB_PHY_OTG_CTRL_PLL_ON (1 << 5) -#define TUSB_PHY_OTG_CTRL_EXT_RPU (1 << 4) -#define TUSB_PHY_OTG_CTRL_PWR_GOOD (1 << 3) -#define TUSB_PHY_OTG_CTRL_RESET (1 << 2) -#define TUSB_PHY_OTG_CTRL_SUSPENDM (1 << 1) -#define TUSB_PHY_OTG_CTRL_CLK_MODE (1 << 0) - -/* OTG status register */ -#define TUSB_DEV_OTG_STAT (TUSB_SYS_REG_BASE + 0x00c) -#define TUSB_DEV_OTG_STAT_PWR_CLK_GOOD (1 << 8) -#define TUSB_DEV_OTG_STAT_SESS_END (1 << 7) -#define TUSB_DEV_OTG_STAT_SESS_VALID (1 << 6) -#define TUSB_DEV_OTG_STAT_VBUS_VALID (1 << 5) -#define TUSB_DEV_OTG_STAT_VBUS_SENSE (1 << 4) -#define TUSB_DEV_OTG_STAT_ID_STATUS (1 << 3) -#define TUSB_DEV_OTG_STAT_HOST_DISCON (1 << 2) -#define TUSB_DEV_OTG_STAT_LINE_STATE (3 << 0) -#define TUSB_DEV_OTG_STAT_DP_ENABLE (1 << 1) -#define TUSB_DEV_OTG_STAT_DM_ENABLE (1 << 0) - -#define TUSB_DEV_OTG_TIMER (TUSB_SYS_REG_BASE + 0x010) -#define TUSB_DEV_OTG_TIMER_ENABLE (1 << 31) -#define TUSB_DEV_OTG_TIMER_VAL(v) ((v) & 0x07ffffff) -#define TUSB_PRCM_REV (TUSB_SYS_REG_BASE + 0x014) - -/* PRCM configuration register */ -#define TUSB_PRCM_CONF (TUSB_SYS_REG_BASE + 0x018) -#define TUSB_PRCM_CONF_SFW_CPEN (1 << 24) -#define TUSB_PRCM_CONF_SYS_CLKSEL(v) (((v) & 3) << 16) - -/* PRCM management register */ -#define TUSB_PRCM_MNGMT (TUSB_SYS_REG_BASE + 0x01c) -#define TUSB_PRCM_MNGMT_SRP_FIX_TMR(v) (((v) & 0xf) << 25) -#define TUSB_PRCM_MNGMT_SRP_FIX_EN (1 << 24) -#define TUSB_PRCM_MNGMT_VBUS_VAL_TMR(v) (((v) & 0xf) << 20) -#define TUSB_PRCM_MNGMT_VBUS_VAL_FLT_EN (1 << 19) -#define TUSB_PRCM_MNGMT_DFT_CLK_DIS (1 << 18) -#define TUSB_PRCM_MNGMT_VLYNQ_CLK_DIS (1 << 17) -#define TUSB_PRCM_MNGMT_OTG_SESS_END_EN (1 << 10) -#define TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN (1 << 9) -#define TUSB_PRCM_MNGMT_OTG_ID_PULLUP (1 << 8) -#define TUSB_PRCM_MNGMT_15_SW_EN (1 << 4) -#define TUSB_PRCM_MNGMT_33_SW_EN (1 << 3) -#define TUSB_PRCM_MNGMT_5V_CPEN (1 << 2) -#define TUSB_PRCM_MNGMT_PM_IDLE (1 << 1) -#define TUSB_PRCM_MNGMT_DEV_IDLE (1 << 0) - -/* Wake-up source clear and mask registers */ -#define TUSB_PRCM_WAKEUP_SOURCE (TUSB_SYS_REG_BASE + 0x020) -#define TUSB_PRCM_WAKEUP_CLEAR (TUSB_SYS_REG_BASE + 0x028) -#define TUSB_PRCM_WAKEUP_MASK (TUSB_SYS_REG_BASE + 0x02c) -#define TUSB_PRCM_WAKEUP_RESERVED_BITS (0xffffe << 13) -#define TUSB_PRCM_WGPIO_7 (1 << 12) -#define TUSB_PRCM_WGPIO_6 (1 << 11) -#define TUSB_PRCM_WGPIO_5 (1 << 10) -#define TUSB_PRCM_WGPIO_4 (1 << 9) -#define TUSB_PRCM_WGPIO_3 (1 << 8) -#define TUSB_PRCM_WGPIO_2 (1 << 7) -#define TUSB_PRCM_WGPIO_1 (1 << 6) -#define TUSB_PRCM_WGPIO_0 (1 << 5) -#define TUSB_PRCM_WHOSTDISCON (1 << 4) /* Host disconnect */ -#define TUSB_PRCM_WBUS (1 << 3) /* USB bus resume */ -#define TUSB_PRCM_WNORCS (1 << 2) /* NOR chip select */ -#define TUSB_PRCM_WVBUS (1 << 1) /* OTG PHY VBUS */ -#define TUSB_PRCM_WID (1 << 0) /* OTG PHY ID detect */ - -#define TUSB_PULLUP_1_CTRL (TUSB_SYS_REG_BASE + 0x030) -#define TUSB_PULLUP_2_CTRL (TUSB_SYS_REG_BASE + 0x034) -#define TUSB_INT_CTRL_REV (TUSB_SYS_REG_BASE + 0x038) -#define TUSB_INT_CTRL_CONF (TUSB_SYS_REG_BASE + 0x03c) -#define TUSB_USBIP_INT_SRC (TUSB_SYS_REG_BASE + 0x040) -#define TUSB_USBIP_INT_SET (TUSB_SYS_REG_BASE + 0x044) -#define TUSB_USBIP_INT_CLEAR (TUSB_SYS_REG_BASE + 0x048) -#define TUSB_USBIP_INT_MASK (TUSB_SYS_REG_BASE + 0x04c) -#define TUSB_DMA_INT_SRC (TUSB_SYS_REG_BASE + 0x050) -#define TUSB_DMA_INT_SET (TUSB_SYS_REG_BASE + 0x054) -#define TUSB_DMA_INT_CLEAR (TUSB_SYS_REG_BASE + 0x058) -#define TUSB_DMA_INT_MASK (TUSB_SYS_REG_BASE + 0x05c) -#define TUSB_GPIO_INT_SRC (TUSB_SYS_REG_BASE + 0x060) -#define TUSB_GPIO_INT_SET (TUSB_SYS_REG_BASE + 0x064) -#define TUSB_GPIO_INT_CLEAR (TUSB_SYS_REG_BASE + 0x068) -#define TUSB_GPIO_INT_MASK (TUSB_SYS_REG_BASE + 0x06c) - -/* NOR flash interrupt source registers */ -#define TUSB_INT_SRC (TUSB_SYS_REG_BASE + 0x070) -#define TUSB_INT_SRC_SET (TUSB_SYS_REG_BASE + 0x074) -#define TUSB_INT_SRC_CLEAR (TUSB_SYS_REG_BASE + 0x078) -#define TUSB_INT_MASK (TUSB_SYS_REG_BASE + 0x07c) -#define TUSB_INT_SRC_TXRX_DMA_DONE (1 << 24) -#define TUSB_INT_SRC_USB_IP_CORE (1 << 17) -#define TUSB_INT_SRC_OTG_TIMEOUT (1 << 16) -#define TUSB_INT_SRC_VBUS_SENSE_CHNG (1 << 15) -#define TUSB_INT_SRC_ID_STATUS_CHNG (1 << 14) -#define TUSB_INT_SRC_DEV_WAKEUP (1 << 13) -#define TUSB_INT_SRC_DEV_READY (1 << 12) -#define TUSB_INT_SRC_USB_IP_TX (1 << 9) -#define TUSB_INT_SRC_USB_IP_RX (1 << 8) -#define TUSB_INT_SRC_USB_IP_VBUS_ERR (1 << 7) -#define TUSB_INT_SRC_USB_IP_VBUS_REQ (1 << 6) -#define TUSB_INT_SRC_USB_IP_DISCON (1 << 5) -#define TUSB_INT_SRC_USB_IP_CONN (1 << 4) -#define TUSB_INT_SRC_USB_IP_SOF (1 << 3) -#define TUSB_INT_SRC_USB_IP_RST_BABBLE (1 << 2) -#define TUSB_INT_SRC_USB_IP_RESUME (1 << 1) -#define TUSB_INT_SRC_USB_IP_SUSPEND (1 << 0) - -#define TUSB_GPIO_REV (TUSB_SYS_REG_BASE + 0x080) -#define TUSB_GPIO_CONF (TUSB_SYS_REG_BASE + 0x084) -#define TUSB_DMA_CTRL_REV (TUSB_SYS_REG_BASE + 0x100) -#define TUSB_DMA_REQ_CONF (TUSB_SYS_REG_BASE + 0x104) -#define TUSB_EP0_CONF (TUSB_SYS_REG_BASE + 0x108) -#define TUSB_EP_IN_SIZE (TUSB_SYS_REG_BASE + 0x10c) -#define TUSB_DMA_EP_MAP (TUSB_SYS_REG_BASE + 0x148) -#define TUSB_EP_OUT_SIZE (TUSB_SYS_REG_BASE + 0x14c) -#define TUSB_EP_MAX_PACKET_SIZE_OFFSET (TUSB_SYS_REG_BASE + 0x188) -#define TUSB_SCRATCH_PAD (TUSB_SYS_REG_BASE + 0x1c4) -#define TUSB_WAIT_COUNT (TUSB_SYS_REG_BASE + 0x1c8) -#define TUSB_PROD_TEST_RESET (TUSB_SYS_REG_BASE + 0x1d8) - -#define TUSB_DIDR1_LO (TUSB_SYS_REG_BASE + 0x1f8) -#define TUSB_DIDR1_HI (TUSB_SYS_REG_BASE + 0x1fc) - -/* Device System & Control register bitfields */ -#define TUSB_INT_CTRL_CONF_INT_RLCYC(v) (((v) & 0x7) << 18) -#define TUSB_INT_CTRL_CONF_INT_POLARITY (1 << 17) -#define TUSB_INT_CTRL_CONF_INT_MODE (1 << 16) -#define TUSB_GPIO_CONF_DMAREQ(v) (((v) & 0x3f) << 24) -#define TUSB_DMA_REQ_CONF_BURST_SIZE(v) (((v) & 3) << 26) -#define TUSB_DMA_REQ_CONF_DMA_RQ_EN(v) (((v) & 0x3f) << 20) -#define TUSB_DMA_REQ_CONF_DMA_RQ_ASR(v) (((v) & 0xf) << 16) -#define TUSB_EP0_CONFIG_SW_EN (1 << 8) -#define TUSB_EP0_CONFIG_DIR_TX (1 << 7) -#define TUSB_EP0_CONFIG_XFR_SIZE(v) ((v) & 0x7f) -#define TUSB_EP_CONFIG_SW_EN (1 << 31) -#define TUSB_EP_CONFIG_XFR_SIZE(v) ((v) & 0x7fffffff) -#define TUSB_PROD_TEST_RESET_VAL 0xa596 - -static void tusb_intr_update(TUSBState *s) -{ - if (s->control_config & TUSB_INT_CTRL_CONF_INT_POLARITY) - qemu_set_irq(s->irq, s->intr & ~s->mask & s->intr_ok); - else - qemu_set_irq(s->irq, (!(s->intr & ~s->mask)) & s->intr_ok); -} - -static void tusb_usbip_intr_update(TUSBState *s) -{ - /* TX interrupt in the MUSB */ - if (s->usbip_intr & 0x0000ffff & ~s->usbip_mask) - s->intr |= TUSB_INT_SRC_USB_IP_TX; - else - s->intr &= ~TUSB_INT_SRC_USB_IP_TX; - - /* RX interrupt in the MUSB */ - if (s->usbip_intr & 0xffff0000 & ~s->usbip_mask) - s->intr |= TUSB_INT_SRC_USB_IP_RX; - else - s->intr &= ~TUSB_INT_SRC_USB_IP_RX; - - /* XXX: What about TUSB_INT_SRC_USB_IP_CORE? */ - - tusb_intr_update(s); -} - -static void tusb_dma_intr_update(TUSBState *s) -{ - if (s->dma_intr & ~s->dma_mask) - s->intr |= TUSB_INT_SRC_TXRX_DMA_DONE; - else - s->intr &= ~TUSB_INT_SRC_TXRX_DMA_DONE; - - tusb_intr_update(s); -} - -static void tusb_gpio_intr_update(TUSBState *s) -{ - /* TODO: How is this signalled? */ -} - -static uint32_t tusb_async_readb(void *opaque, hwaddr addr) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - return musb_read[0](s->musb, addr & 0x1ff); - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - return musb_read[0](s->musb, 0x20 + ((addr >> 3) & 0x3c)); - } - - printf("%s: unknown register at %03x\n", - __FUNCTION__, (int) (addr & 0xfff)); - return 0; -} - -static uint32_t tusb_async_readh(void *opaque, hwaddr addr) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - return musb_read[1](s->musb, addr & 0x1ff); - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - return musb_read[1](s->musb, 0x20 + ((addr >> 3) & 0x3c)); - } - - printf("%s: unknown register at %03x\n", - __FUNCTION__, (int) (addr & 0xfff)); - return 0; -} - -static uint32_t tusb_async_readw(void *opaque, hwaddr addr) -{ - TUSBState *s = (TUSBState *) opaque; - int offset = addr & 0xfff; - int epnum; - uint32_t ret; - - switch (offset) { - case TUSB_DEV_CONF: - return s->dev_config; - - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - return musb_read[2](s->musb, offset & 0x1ff); - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - return musb_read[2](s->musb, 0x20 + ((addr >> 3) & 0x3c)); - - case TUSB_PHY_OTG_CTRL_ENABLE: - case TUSB_PHY_OTG_CTRL: - return 0x00; /* TODO */ - - case TUSB_DEV_OTG_STAT: - ret = s->otg_status; -#if 0 - if (!(s->prcm_mngmt & TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN)) - ret &= ~TUSB_DEV_OTG_STAT_VBUS_VALID; -#endif - return ret; - case TUSB_DEV_OTG_TIMER: - return s->otg_timer_val; - - case TUSB_PRCM_REV: - return 0x20; - case TUSB_PRCM_CONF: - return s->prcm_config; - case TUSB_PRCM_MNGMT: - return s->prcm_mngmt; - case TUSB_PRCM_WAKEUP_SOURCE: - case TUSB_PRCM_WAKEUP_CLEAR: /* TODO: What does this one return? */ - return 0x00000000; - case TUSB_PRCM_WAKEUP_MASK: - return s->wkup_mask; - - case TUSB_PULLUP_1_CTRL: - return s->pullup[0]; - case TUSB_PULLUP_2_CTRL: - return s->pullup[1]; - - case TUSB_INT_CTRL_REV: - return 0x20; - case TUSB_INT_CTRL_CONF: - return s->control_config; - - case TUSB_USBIP_INT_SRC: - case TUSB_USBIP_INT_SET: /* TODO: What do these two return? */ - case TUSB_USBIP_INT_CLEAR: - return s->usbip_intr; - case TUSB_USBIP_INT_MASK: - return s->usbip_mask; - - case TUSB_DMA_INT_SRC: - case TUSB_DMA_INT_SET: /* TODO: What do these two return? */ - case TUSB_DMA_INT_CLEAR: - return s->dma_intr; - case TUSB_DMA_INT_MASK: - return s->dma_mask; - - case TUSB_GPIO_INT_SRC: /* TODO: What do these two return? */ - case TUSB_GPIO_INT_SET: - case TUSB_GPIO_INT_CLEAR: - return s->gpio_intr; - case TUSB_GPIO_INT_MASK: - return s->gpio_mask; - - case TUSB_INT_SRC: - case TUSB_INT_SRC_SET: /* TODO: What do these two return? */ - case TUSB_INT_SRC_CLEAR: - return s->intr; - case TUSB_INT_MASK: - return s->mask; - - case TUSB_GPIO_REV: - return 0x30; - case TUSB_GPIO_CONF: - return s->gpio_config; - - case TUSB_DMA_CTRL_REV: - return 0x30; - case TUSB_DMA_REQ_CONF: - return s->dma_config; - case TUSB_EP0_CONF: - return s->ep0_config; - case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b): - epnum = (offset - TUSB_EP_IN_SIZE) >> 2; - return s->tx_config[epnum]; - case TUSB_DMA_EP_MAP: - return s->dma_map; - case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b): - epnum = (offset - TUSB_EP_OUT_SIZE) >> 2; - return s->rx_config[epnum]; - case TUSB_EP_MAX_PACKET_SIZE_OFFSET ... - (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b): - return 0x00000000; /* TODO */ - case TUSB_WAIT_COUNT: - return 0x00; /* TODO */ - - case TUSB_SCRATCH_PAD: - return s->scratch; - - case TUSB_PROD_TEST_RESET: - return s->test_reset; - - /* DIE IDs */ - case TUSB_DIDR1_LO: - return 0xa9453c59; - case TUSB_DIDR1_HI: - return 0x54059adf; - } - - printf("%s: unknown register at %03x\n", __FUNCTION__, offset); - return 0; -} - -static void tusb_async_writeb(void *opaque, hwaddr addr, - uint32_t value) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - musb_write[0](s->musb, addr & 0x1ff, value); - break; - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - musb_write[0](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); - break; - - default: - printf("%s: unknown register at %03x\n", - __FUNCTION__, (int) (addr & 0xfff)); - return; - } -} - -static void tusb_async_writeh(void *opaque, hwaddr addr, - uint32_t value) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - musb_write[1](s->musb, addr & 0x1ff, value); - break; - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - musb_write[1](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); - break; - - default: - printf("%s: unknown register at %03x\n", - __FUNCTION__, (int) (addr & 0xfff)); - return; - } -} - -static void tusb_async_writew(void *opaque, hwaddr addr, - uint32_t value) -{ - TUSBState *s = (TUSBState *) opaque; - int offset = addr & 0xfff; - int epnum; - - switch (offset) { - case TUSB_VLYNQ_CTRL: - break; - - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - musb_write[2](s->musb, offset & 0x1ff, value); - break; - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - musb_write[2](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); - break; - - case TUSB_DEV_CONF: - s->dev_config = value; - s->host_mode = (value & TUSB_DEV_CONF_USB_HOST_MODE); - if (value & TUSB_DEV_CONF_PROD_TEST_MODE) - hw_error("%s: Product Test mode not allowed\n", __FUNCTION__); - break; - - case TUSB_PHY_OTG_CTRL_ENABLE: - case TUSB_PHY_OTG_CTRL: - return; /* TODO */ - case TUSB_DEV_OTG_TIMER: - s->otg_timer_val = value; - if (value & TUSB_DEV_OTG_TIMER_ENABLE) - timer_mod(s->otg_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - muldiv64(TUSB_DEV_OTG_TIMER_VAL(value), - get_ticks_per_sec(), TUSB_DEVCLOCK)); - else - timer_del(s->otg_timer); - break; - - case TUSB_PRCM_CONF: - s->prcm_config = value; - break; - case TUSB_PRCM_MNGMT: - s->prcm_mngmt = value; - break; - case TUSB_PRCM_WAKEUP_CLEAR: - break; - case TUSB_PRCM_WAKEUP_MASK: - s->wkup_mask = value; - break; - - case TUSB_PULLUP_1_CTRL: - s->pullup[0] = value; - break; - case TUSB_PULLUP_2_CTRL: - s->pullup[1] = value; - break; - case TUSB_INT_CTRL_CONF: - s->control_config = value; - tusb_intr_update(s); - break; - - case TUSB_USBIP_INT_SET: - s->usbip_intr |= value; - tusb_usbip_intr_update(s); - break; - case TUSB_USBIP_INT_CLEAR: - s->usbip_intr &= ~value; - tusb_usbip_intr_update(s); - musb_core_intr_clear(s->musb, ~value); - break; - case TUSB_USBIP_INT_MASK: - s->usbip_mask = value; - tusb_usbip_intr_update(s); - break; - - case TUSB_DMA_INT_SET: - s->dma_intr |= value; - tusb_dma_intr_update(s); - break; - case TUSB_DMA_INT_CLEAR: - s->dma_intr &= ~value; - tusb_dma_intr_update(s); - break; - case TUSB_DMA_INT_MASK: - s->dma_mask = value; - tusb_dma_intr_update(s); - break; - - case TUSB_GPIO_INT_SET: - s->gpio_intr |= value; - tusb_gpio_intr_update(s); - break; - case TUSB_GPIO_INT_CLEAR: - s->gpio_intr &= ~value; - tusb_gpio_intr_update(s); - break; - case TUSB_GPIO_INT_MASK: - s->gpio_mask = value; - tusb_gpio_intr_update(s); - break; - - case TUSB_INT_SRC_SET: - s->intr |= value; - tusb_intr_update(s); - break; - case TUSB_INT_SRC_CLEAR: - s->intr &= ~value; - tusb_intr_update(s); - break; - case TUSB_INT_MASK: - s->mask = value; - tusb_intr_update(s); - break; - - case TUSB_GPIO_CONF: - s->gpio_config = value; - break; - case TUSB_DMA_REQ_CONF: - s->dma_config = value; - break; - case TUSB_EP0_CONF: - s->ep0_config = value & 0x1ff; - musb_set_size(s->musb, 0, TUSB_EP0_CONFIG_XFR_SIZE(value), - value & TUSB_EP0_CONFIG_DIR_TX); - break; - case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b): - epnum = (offset - TUSB_EP_IN_SIZE) >> 2; - s->tx_config[epnum] = value; - musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 1); - break; - case TUSB_DMA_EP_MAP: - s->dma_map = value; - break; - case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b): - epnum = (offset - TUSB_EP_OUT_SIZE) >> 2; - s->rx_config[epnum] = value; - musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 0); - break; - case TUSB_EP_MAX_PACKET_SIZE_OFFSET ... - (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b): - return; /* TODO */ - case TUSB_WAIT_COUNT: - return; /* TODO */ - - case TUSB_SCRATCH_PAD: - s->scratch = value; - break; - - case TUSB_PROD_TEST_RESET: - s->test_reset = value; - break; - - default: - printf("%s: unknown register at %03x\n", __FUNCTION__, offset); - return; - } -} - -static const MemoryRegionOps tusb_async_ops = { - .old_mmio = { - .read = { tusb_async_readb, tusb_async_readh, tusb_async_readw, }, - .write = { tusb_async_writeb, tusb_async_writeh, tusb_async_writew, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void tusb_otg_tick(void *opaque) -{ - TUSBState *s = (TUSBState *) opaque; - - s->otg_timer_val = 0; - s->intr |= TUSB_INT_SRC_OTG_TIMEOUT; - tusb_intr_update(s); -} - -static void tusb_power_tick(void *opaque) -{ - TUSBState *s = (TUSBState *) opaque; - - if (s->power) { - s->intr_ok = ~0; - tusb_intr_update(s); - } -} - -static void tusb_musb_core_intr(void *opaque, int source, int level) -{ - TUSBState *s = (TUSBState *) opaque; - uint16_t otg_status = s->otg_status; - - switch (source) { - case musb_set_vbus: - if (level) - otg_status |= TUSB_DEV_OTG_STAT_VBUS_VALID; - else - otg_status &= ~TUSB_DEV_OTG_STAT_VBUS_VALID; - - /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_VBUS_DET_EN set? */ - /* XXX: only if TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN set? */ - if (s->otg_status != otg_status) { - s->otg_status = otg_status; - s->intr |= TUSB_INT_SRC_VBUS_SENSE_CHNG; - tusb_intr_update(s); - } - break; - - case musb_set_session: - /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_SESS_END_EN set? */ - /* XXX: only if TUSB_PRCM_MNGMT_OTG_SESS_END_EN set? */ - if (level) { - s->otg_status |= TUSB_DEV_OTG_STAT_SESS_VALID; - s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_END; - } else { - s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_VALID; - s->otg_status |= TUSB_DEV_OTG_STAT_SESS_END; - } - - /* XXX: some IRQ or anything? */ - break; - - case musb_irq_tx: - case musb_irq_rx: - s->usbip_intr = musb_core_intr_get(s->musb); - /* Fall through. */ - default: - if (level) - s->intr |= 1 << source; - else - s->intr &= ~(1 << source); - tusb_intr_update(s); - break; - } -} - -static void tusb6010_power(TUSBState *s, int on) -{ - if (!on) { - s->power = 0; - } else if (!s->power && on) { - s->power = 1; - /* Pull the interrupt down after TUSB6010 comes up. */ - s->intr_ok = 0; - tusb_intr_update(s); - timer_mod(s->pwr_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + get_ticks_per_sec() / 2); - } -} - -static void tusb6010_irq(void *opaque, int source, int level) -{ - if (source) { - tusb_musb_core_intr(opaque, source - 1, level); - } else { - tusb6010_power(opaque, level); - } -} - -static void tusb6010_reset(DeviceState *dev) -{ - TUSBState *s = TUSB(dev); - int i; - - s->test_reset = TUSB_PROD_TEST_RESET_VAL; - s->host_mode = 0; - s->dev_config = 0; - s->otg_status = 0; /* !TUSB_DEV_OTG_STAT_ID_STATUS means host mode */ - s->power = 0; - s->mask = 0xffffffff; - s->intr = 0x00000000; - s->otg_timer_val = 0; - s->scratch = 0; - s->prcm_config = 0; - s->prcm_mngmt = 0; - s->intr_ok = 0; - s->usbip_intr = 0; - s->usbip_mask = 0; - s->gpio_intr = 0; - s->gpio_mask = 0; - s->gpio_config = 0; - s->dma_intr = 0; - s->dma_mask = 0; - s->dma_map = 0; - s->dma_config = 0; - s->ep0_config = 0; - s->wkup_mask = 0; - s->pullup[0] = s->pullup[1] = 0; - s->control_config = 0; - for (i = 0; i < 15; i++) { - s->rx_config[i] = s->tx_config[i] = 0; - } - musb_reset(s->musb); -} - -static int tusb6010_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - TUSBState *s = TUSB(dev); - - s->otg_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tusb_otg_tick, s); - s->pwr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tusb_power_tick, s); - memory_region_init_io(&s->iomem[1], OBJECT(s), &tusb_async_ops, s, - "tusb-async", UINT32_MAX); - sysbus_init_mmio(sbd, &s->iomem[0]); - sysbus_init_mmio(sbd, &s->iomem[1]); - sysbus_init_irq(sbd, &s->irq); - qdev_init_gpio_in(dev, tusb6010_irq, musb_irq_max + 1); - s->musb = musb_init(dev, 1); - return 0; -} - -static void tusb6010_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = tusb6010_init; - dc->reset = tusb6010_reset; -} - -static const TypeInfo tusb6010_info = { - .name = TYPE_TUSB6010, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(TUSBState), - .class_init = tusb6010_class_init, -}; - -static void tusb6010_register_types(void) -{ - type_register_static(&tusb6010_info); -} - -type_init(tusb6010_register_types) diff --git a/qemu/hw/timer/twl92230.c b/qemu/hw/timer/twl92230.c index 7ded4ba2a..7ba4e9a7c 100644 --- a/qemu/hw/timer/twl92230.c +++ b/qemu/hw/timer/twl92230.c @@ -19,11 +19,13 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "qemu/timer.h" #include "hw/i2c/i2c.h" #include "sysemu/sysemu.h" #include "ui/console.h" +#include "qemu/bcd.h" #define VERBOSE 1 diff --git a/qemu/hw/timer/xilinx_timer.c b/qemu/hw/timer/xilinx_timer.c index cdb335517..2ea970dc9 100644 --- a/qemu/hw/timer/xilinx_timer.c +++ b/qemu/hw/timer/xilinx_timer.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/ptimer.h" #include "qemu/log.h" diff --git a/qemu/hw/tpm/tpm_passthrough.c b/qemu/hw/tpm/tpm_passthrough.c index 79a8f98af..e88c0d20b 100644 --- a/qemu/hw/tpm/tpm_passthrough.c +++ b/qemu/hw/tpm/tpm_passthrough.c @@ -22,10 +22,8 @@ * License along with this library; if not, see */ -#include - +#include "qemu/osdep.h" #include "qemu-common.h" -#include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/sockets.h" #include "sysemu/tpm_backend.h" @@ -85,12 +83,37 @@ static void tpm_passthrough_cancel_cmd(TPMBackend *tb); static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len) { - return send_all(fd, buf, len); + int ret, remain; + + remain = len; + while (remain > 0) { + ret = write(fd, buf, remain); + if (ret < 0) { + if (errno != EINTR && errno != EAGAIN) { + return -1; + } + } else if (ret == 0) { + break; + } else { + buf += ret; + remain -= ret; + } + } + return len - remain; } static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len) { - return recv_all(fd, buf, len, true); + int ret; + reread: + ret = read(fd, buf, len); + if (ret < 0) { + if (errno != EINTR && errno != EAGAIN) { + return -1; + } + goto reread; + } + return ret; } static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf) diff --git a/qemu/hw/tpm/tpm_tis.c b/qemu/hw/tpm/tpm_tis.c index 0806b5f82..381e7266e 100644 --- a/qemu/hw/tpm/tpm_tis.c +++ b/qemu/hw/tpm/tpm_tis.c @@ -22,6 +22,7 @@ * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43 */ +#include "qemu/osdep.h" #include "sysemu/tpm_backend.h" #include "tpm_int.h" #include "sysemu/block-backend.h" @@ -30,6 +31,7 @@ #include "hw/i386/pc.h" #include "hw/pci/pci_ids.h" #include "tpm_tis.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/main-loop.h" #include "sysemu/tpm_backend.h" @@ -141,7 +143,7 @@ #define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3 \ (TPM_TIS_IFACE_ID_INTERFACE_TIS1_3 | \ - (~0 << 4)/* all of it is don't care */) + (~0u << 4)/* all of it is don't care */) /* if backend was a TPM 2.0: */ #define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0 \ @@ -1051,7 +1053,7 @@ static void tpm_tis_realizefn(DeviceState *dev, Error **errp) if (tis->irq_num > 15) { error_setg(errp, "tpm_tis: IRQ %d for TPM TIS is outside valid range " - "of 0 to 15.\n", tis->irq_num); + "of 0 to 15", tis->irq_num); return; } diff --git a/qemu/hw/tpm/tpm_util.c b/qemu/hw/tpm/tpm_util.c index 4ace5852e..7b3542972 100644 --- a/qemu/hw/tpm/tpm_util.c +++ b/qemu/hw/tpm/tpm_util.c @@ -19,6 +19,7 @@ * License along with this library; if not, see */ +#include "qemu/osdep.h" #include "tpm_util.h" #include "tpm_int.h" diff --git a/qemu/hw/tricore/tricore_testboard.c b/qemu/hw/tricore/tricore_testboard.c index a059a20a3..8d3520f5b 100644 --- a/qemu/hw/tricore/tricore_testboard.c +++ b/qemu/hw/tricore/tricore_testboard.c @@ -18,6 +18,10 @@ */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/devices.h" #include "net/net.h" @@ -44,7 +48,7 @@ static void tricore_load_kernel(CPUTriCoreState *env) kernel_size = load_elf(tricoretb_binfo.kernel_filename, NULL, NULL, (uint64_t *)&entry, NULL, NULL, 0, - ELF_MACHINE, 1); + EM_TRICORE, 1, 0); if (kernel_size <= 0) { error_report("qemu: no kernel file '%s'", tricoretb_binfo.kernel_filename); @@ -76,17 +80,23 @@ static void tricore_testboard_init(MachineState *machine, int board_id) exit(1); } env = &cpu->env; - memory_region_init_ram(ext_cram, NULL, "powerlink_ext_c.ram", 2*1024*1024, &error_abort); + memory_region_init_ram(ext_cram, NULL, "powerlink_ext_c.ram", 2*1024*1024, + &error_fatal); vmstate_register_ram_global(ext_cram); - memory_region_init_ram(ext_dram, NULL, "powerlink_ext_d.ram", 4*1024*1024, &error_abort); + memory_region_init_ram(ext_dram, NULL, "powerlink_ext_d.ram", 4*1024*1024, + &error_fatal); vmstate_register_ram_global(ext_dram); - memory_region_init_ram(int_cram, NULL, "powerlink_int_c.ram", 48*1024, &error_abort); + memory_region_init_ram(int_cram, NULL, "powerlink_int_c.ram", 48*1024, + &error_fatal); vmstate_register_ram_global(int_cram); - memory_region_init_ram(int_dram, NULL, "powerlink_int_d.ram", 48*1024, &error_abort); + memory_region_init_ram(int_dram, NULL, "powerlink_int_d.ram", 48*1024, + &error_fatal); vmstate_register_ram_global(int_dram); - memory_region_init_ram(pcp_data, NULL, "powerlink_pcp_data.ram", 16*1024, &error_abort); + memory_region_init_ram(pcp_data, NULL, "powerlink_pcp_data.ram", 16*1024, + &error_fatal); vmstate_register_ram_global(pcp_data); - memory_region_init_ram(pcp_text, NULL, "powerlink_pcp_text.ram", 32*1024, &error_abort); + memory_region_init_ram(pcp_text, NULL, "powerlink_pcp_text.ram", 32*1024, + &error_fatal); vmstate_register_ram_global(pcp_text); memory_region_add_subregion(sysmem, 0x80000000, ext_cram); @@ -109,16 +119,11 @@ static void tricoreboard_init(MachineState *machine) tricore_testboard_init(machine, 0x183); } -static QEMUMachine ttb_machine = { - .name = "tricore_testboard", - .desc = "a minimal TriCore board", - .init = tricoreboard_init, - .is_default = 0, -}; - -static void tricore_testboard_machine_init(void) +static void ttb_machine_init(MachineClass *mc) { - qemu_register_machine(&ttb_machine); + mc->desc = "a minimal TriCore board"; + mc->init = tricoreboard_init; + mc->is_default = 0; } -machine_init(tricore_testboard_machine_init); +DEFINE_MACHINE("tricore_testboard", ttb_machine_init) diff --git a/qemu/hw/unicore32/puv3.c b/qemu/hw/unicore32/puv3.c index 703e29d6d..31cd17101 100644 --- a/qemu/hw/unicore32/puv3.c +++ b/qemu/hw/unicore32/puv3.c @@ -9,6 +9,10 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "qemu-common.h" #include "ui/console.h" #include "elf.h" @@ -17,6 +21,7 @@ #include "hw/boards.h" #include "hw/loader.h" #include "hw/i386/pc.h" +#include "qemu/error-report.h" #include "sysemu/qtest.h" #undef DEBUG_PUV3 @@ -75,7 +80,7 @@ static void puv3_board_init(CPUUniCore32State *env, ram_addr_t ram_size) /* SDRAM at address zero. */ memory_region_init_ram(ram_memory, NULL, "puv3.ram", ram_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(ram_memory); memory_region_add_subregion(get_system_memory(), 0, ram_memory); } @@ -95,7 +100,8 @@ static void puv3_load_kernel(const char *kernel_filename) size = load_image_targphys(kernel_filename, KERNEL_LOAD_ADDR, KERNEL_MAX_SIZE); if (size < 0) { - hw_error("Load kernel error: '%s'\n", kernel_filename); + error_report("Load kernel error: '%s'", kernel_filename); + exit(1); } /* cheat curses that we have a graphic console, only under ocd console */ @@ -112,7 +118,8 @@ static void puv3_init(MachineState *machine) UniCore32CPU *cpu; if (initrd_filename) { - hw_error("Please use kernel built-in initramdisk.\n"); + error_report("Please use kernel built-in initramdisk"); + exit(1); } if (!cpu_model) { @@ -121,7 +128,8 @@ static void puv3_init(MachineState *machine) cpu = uc32_cpu_init(cpu_model); if (!cpu) { - hw_error("Unable to find CPU definition\n"); + error_report("Unable to find CPU definition"); + exit(1); } env = &cpu->env; @@ -130,16 +138,11 @@ static void puv3_init(MachineState *machine) puv3_load_kernel(kernel_filename); } -static QEMUMachine puv3_machine = { - .name = "puv3", - .desc = "PKUnity Version-3 based on UniCore32", - .init = puv3_init, - .is_default = 1, -}; - -static void puv3_machine_init(void) +static void puv3_machine_init(MachineClass *mc) { - qemu_register_machine(&puv3_machine); + mc->desc = "PKUnity Version-3 based on UniCore32"; + mc->init = puv3_init; + mc->is_default = 1; } -machine_init(puv3_machine_init) +DEFINE_MACHINE("puv3", puv3_machine_init) diff --git a/qemu/hw/usb/Makefile.objs b/qemu/hw/usb/Makefile.objs index 7443e386b..2717027d3 100644 --- a/qemu/hw/usb/Makefile.objs +++ b/qemu/hw/usb/Makefile.objs @@ -10,6 +10,8 @@ common-obj-$(CONFIG_USB_EHCI_SYSBUS) += hcd-ehci-sysbus.o common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o common-obj-$(CONFIG_USB_MUSB) += hcd-musb.o +obj-$(CONFIG_TUSB6010) += tusb6010.o + # emulated usb devices common-obj-$(CONFIG_USB) += dev-hub.o common-obj-$(CONFIG_USB) += dev-hid.o @@ -23,9 +25,8 @@ common-obj-$(CONFIG_USB_BLUETOOTH) += dev-bluetooth.o ifeq ($(CONFIG_USB_SMARTCARD),y) common-obj-y += dev-smartcard-reader.o -common-obj-y += ccid-card-passthru.o -common-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o -ccid-card-emulated.o-cflags := -I$(SRC_PATH)/libcacard +common-obj-$(CONFIG_SMARTCARD) += ccid-card-passthru.o +common-obj-$(CONFIG_SMARTCARD) += ccid-card-emulated.o endif ifeq ($(CONFIG_POSIX),y) diff --git a/qemu/hw/usb/bus.c b/qemu/hw/usb/bus.c index 5f39e1e3a..16c3461d9 100644 --- a/qemu/hw/usb/bus.c +++ b/qemu/hw/usb/bus.c @@ -1,10 +1,13 @@ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/usb.h" #include "hw/qdev.h" +#include "qapi/error.h" #include "qemu/error-report.h" #include "sysemu/sysemu.h" #include "monitor/monitor.h" #include "trace.h" +#include "qemu/cutils.h" static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); @@ -329,9 +332,9 @@ static USBDevice *usb_try_create_simple(USBBus *bus, const char *name, } object_property_set_bool(OBJECT(dev), true, "realized", &err); if (err) { - error_setg(errp, "Failed to initialize USB device '%s': %s", - name, error_get_pretty(err)); - error_free(err); + error_propagate(errp, err); + error_prepend(errp, "Failed to initialize USB device '%s': ", + name); object_unparent(OBJECT(dev)); return NULL; } @@ -655,9 +658,12 @@ void hmp_info_usb(Monitor *mon, const QDict *qdict) dev = port->dev; if (!dev) continue; - monitor_printf(mon, " Device %d.%d, Port %s, Speed %s Mb/s, Product %s\n", - bus->busnr, dev->addr, port->path, usb_speed(dev->speed), - dev->product_desc); + monitor_printf(mon, " Device %d.%d, Port %s, Speed %s Mb/s, " + "Product %s%s%s\n", + bus->busnr, dev->addr, port->path, + usb_speed(dev->speed), dev->product_desc, + dev->qdev.id ? ", ID: " : "", + dev->qdev.id ?: ""); } } } @@ -722,9 +728,8 @@ USBDevice *usbdevice_create(const char *cmdline) } object_property_set_bool(OBJECT(dev), true, "realized", &err); if (err) { - error_report("Failed to initialize USB device '%s': %s", - f->name, error_get_pretty(err)); - error_free(err); + error_reportf_err(err, "Failed to initialize USB device '%s': ", + f->name); object_unparent(OBJECT(dev)); return NULL; } diff --git a/qemu/hw/usb/ccid-card-emulated.c b/qemu/hw/usb/ccid-card-emulated.c index 72329ed7d..3213f9f8a 100644 --- a/qemu/hw/usb/ccid-card-emulated.c +++ b/qemu/hw/usb/ccid-card-emulated.c @@ -26,6 +26,7 @@ * the db parameter. */ +#include "qemu/osdep.h" #include #include #include @@ -42,7 +43,10 @@ do {\ } \ } while (0) -#define EMULATED_DEV_NAME "ccid-card-emulated" + +#define TYPE_EMULATED_CCID "ccid-card-emulated" +#define EMULATED_CCID_CARD(obj) \ + OBJECT_CHECK(EmulatedState, (obj), TYPE_EMULATED_CCID) #define BACKEND_NSS_EMULATED_NAME "nss-emulated" #define BACKEND_CERTIFICATES_NAME "certificates" @@ -133,7 +137,7 @@ struct EmulatedState { static void emulated_apdu_from_guest(CCIDCardState *base, const uint8_t *apdu, uint32_t len) { - EmulatedState *card = DO_UPCAST(EmulatedState, base, base); + EmulatedState *card = EMULATED_CCID_CARD(base); EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len); assert(event); @@ -150,7 +154,7 @@ static void emulated_apdu_from_guest(CCIDCardState *base, static const uint8_t *emulated_get_atr(CCIDCardState *base, uint32_t *len) { - EmulatedState *card = DO_UPCAST(EmulatedState, base, base); + EmulatedState *card = EMULATED_CCID_CARD(base); *len = card->atr_length; return card->atr; @@ -166,7 +170,7 @@ static void emulated_push_event(EmulatedState *card, EmulEvent *event) static void emulated_push_type(EmulatedState *card, uint32_t type) { - EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent)); + EmulEvent *event = g_new(EmulEvent, 1); assert(event); event->p.gen.type = type; @@ -175,7 +179,7 @@ static void emulated_push_type(EmulatedState *card, uint32_t type) static void emulated_push_error(EmulatedState *card, uint64_t code) { - EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent)); + EmulEvent *event = g_new(EmulEvent, 1); assert(event); event->p.error.type = EMUL_ERROR; @@ -403,7 +407,7 @@ static int init_event_notifier(EmulatedState *card) DPRINTF(card, 2, "event notifier creation failed\n"); return -1; } - event_notifier_set_handler(&card->notifier, card_event_handler); + event_notifier_set_handler(&card->notifier, false, card_event_handler); return 0; } @@ -478,7 +482,7 @@ static uint32_t parse_enumeration(char *str, static int emulated_initfn(CCIDCardState *base) { - EmulatedState *card = DO_UPCAST(EmulatedState, base, base); + EmulatedState *card = EMULATED_CCID_CARD(base); VCardEmulError ret; const EnumTable *ptable; @@ -514,26 +518,26 @@ static int emulated_initfn(CCIDCardState *base) ret = emulated_initialize_vcard_from_certificates(card); } else { printf("%s: you must provide all three certs for" - " certificates backend\n", EMULATED_DEV_NAME); + " certificates backend\n", TYPE_EMULATED_CCID); return -1; } } else { if (card->backend != BACKEND_NSS_EMULATED) { printf("%s: bad backend specified. The options are:\n%s (default)," - " %s.\n", EMULATED_DEV_NAME, BACKEND_NSS_EMULATED_NAME, + " %s.\n", TYPE_EMULATED_CCID, BACKEND_NSS_EMULATED_NAME, BACKEND_CERTIFICATES_NAME); return -1; } if (card->cert1 != NULL || card->cert2 != NULL || card->cert3 != NULL) { printf("%s: unexpected cert parameters to nss emulated backend\n", - EMULATED_DEV_NAME); + TYPE_EMULATED_CCID); return -1; } /* default to mirroring the local hardware readers */ ret = wrap_vcard_emul_init(NULL); } if (ret != VCARD_EMUL_OK) { - printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME); + printf("%s: failed to initialize vcard\n", TYPE_EMULATED_CCID); return -1; } qemu_thread_create(&card->event_thread_id, "ccid/event", event_thread, @@ -545,7 +549,7 @@ static int emulated_initfn(CCIDCardState *base) static int emulated_exitfn(CCIDCardState *base) { - EmulatedState *card = DO_UPCAST(EmulatedState, base, base); + EmulatedState *card = EMULATED_CCID_CARD(base); VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL); vevent_queue_vevent(vevent); /* stop vevent thread */ @@ -588,7 +592,7 @@ static void emulated_class_initfn(ObjectClass *klass, void *data) } static const TypeInfo emulated_card_info = { - .name = EMULATED_DEV_NAME, + .name = TYPE_EMULATED_CCID, .parent = TYPE_CCID_CARD, .instance_size = sizeof(EmulatedState), .class_init = emulated_class_initfn, diff --git a/qemu/hw/usb/ccid-card-passthru.c b/qemu/hw/usb/ccid-card-passthru.c index 85a4fc3e5..c0e90e501 100644 --- a/qemu/hw/usb/ccid-card-passthru.c +++ b/qemu/hw/usb/ccid-card-passthru.c @@ -8,11 +8,12 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "sysemu/char.h" #include "qemu/error-report.h" #include "qemu/sockets.h" #include "ccid.h" -#include "libcacard/vscard_common.h" +#include "cacard/vscard_common.h" #define DPRINTF(card, lvl, fmt, ...) \ do { \ @@ -38,8 +39,6 @@ static const uint8_t DEFAULT_ATR[] = { 0x13, 0x08 }; - -#define PASSTHRU_DEV_NAME "ccid-card-passthru" #define VSCARD_IN_SIZE 65536 /* maximum size of ATR - from 7816-3 */ @@ -58,6 +57,10 @@ struct PassthruState { uint8_t debug; }; +#define TYPE_CCID_PASSTHRU "ccid-card-passthru" +#define PASSTHRU_CCID_CARD(obj) \ + OBJECT_CHECK(PassthruState, (obj), TYPE_CCID_PASSTHRU) + /* * VSCard protocol over chardev * This code should not depend on the card type. @@ -316,7 +319,7 @@ static void ccid_card_vscard_event(void *opaque, int event) static void passthru_apdu_from_guest( CCIDCardState *base, const uint8_t *apdu, uint32_t len) { - PassthruState *card = DO_UPCAST(PassthruState, base, base); + PassthruState *card = PASSTHRU_CCID_CARD(base); if (!card->cs) { printf("ccid-passthru: no chardev, discarding apdu length %d\n", len); @@ -327,7 +330,7 @@ static void passthru_apdu_from_guest( static const uint8_t *passthru_get_atr(CCIDCardState *base, uint32_t *len) { - PassthruState *card = DO_UPCAST(PassthruState, base, base); + PassthruState *card = PASSTHRU_CCID_CARD(base); *len = card->atr_length; return card->atr; @@ -335,7 +338,7 @@ static const uint8_t *passthru_get_atr(CCIDCardState *base, uint32_t *len) static int passthru_initfn(CCIDCardState *base) { - PassthruState *card = DO_UPCAST(PassthruState, base, base); + PassthruState *card = PASSTHRU_CCID_CARD(base); card->vscard_in_pos = 0; card->vscard_in_hdr = 0; @@ -399,7 +402,7 @@ static void passthru_class_initfn(ObjectClass *klass, void *data) } static const TypeInfo passthru_card_info = { - .name = PASSTHRU_DEV_NAME, + .name = TYPE_CCID_PASSTHRU, .parent = TYPE_CCID_CARD, .instance_size = sizeof(PassthruState), .class_init = passthru_class_initfn, diff --git a/qemu/hw/usb/combined-packet.c b/qemu/hw/usb/combined-packet.c index ad77705f8..48cac87f6 100644 --- a/qemu/hw/usb/combined-packet.c +++ b/qemu/hw/usb/combined-packet.c @@ -19,6 +19,7 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/usb.h" #include "qemu/iov.h" diff --git a/qemu/hw/usb/core.c b/qemu/hw/usb/core.c index d0025db60..45fa00c51 100644 --- a/qemu/hw/usb/core.c +++ b/qemu/hw/usb/core.c @@ -23,6 +23,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/usb.h" #include "qemu/iov.h" @@ -128,9 +129,16 @@ static void do_token_setup(USBDevice *s, USBPacket *p) } usb_packet_copy(p, s->setup_buf, p->iov.size); + s->setup_index = 0; p->actual_length = 0; s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; - s->setup_index = 0; + if (s->setup_len > sizeof(s->data_buf)) { + fprintf(stderr, + "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", + s->setup_len, sizeof(s->data_buf)); + p->status = USB_RET_STALL; + return; + } request = (s->setup_buf[0] << 8) | s->setup_buf[1]; value = (s->setup_buf[3] << 8) | s->setup_buf[2]; @@ -151,13 +159,6 @@ static void do_token_setup(USBDevice *s, USBPacket *p) } s->setup_state = SETUP_STATE_DATA; } else { - if (s->setup_len > sizeof(s->data_buf)) { - fprintf(stderr, - "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", - s->setup_len, sizeof(s->data_buf)); - p->status = USB_RET_STALL; - return; - } if (s->setup_len == 0) s->setup_state = SETUP_STATE_ACK; else @@ -176,7 +177,7 @@ static void do_token_in(USBDevice *s, USBPacket *p) request = (s->setup_buf[0] << 8) | s->setup_buf[1]; value = (s->setup_buf[3] << 8) | s->setup_buf[2]; index = (s->setup_buf[5] << 8) | s->setup_buf[4]; - + switch(s->setup_state) { case SETUP_STATE_ACK: if (!(s->setup_buf[0] & USB_DIR_IN)) { diff --git a/qemu/hw/usb/desc-msos.c b/qemu/hw/usb/desc-msos.c index 32c3600df..365291981 100644 --- a/qemu/hw/usb/desc-msos.c +++ b/qemu/hw/usb/desc-msos.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/usb.h" #include "hw/usb/desc.h" diff --git a/qemu/hw/usb/desc.c b/qemu/hw/usb/desc.c index b82c397ef..adb026e43 100644 --- a/qemu/hw/usb/desc.c +++ b/qemu/hw/usb/desc.c @@ -1,4 +1,4 @@ -#include +#include "qemu/osdep.h" #include "hw/usb.h" #include "hw/usb/desc.h" diff --git a/qemu/hw/usb/desc.h b/qemu/hw/usb/desc.h index 8e8db03a0..4d81c68e0 100644 --- a/qemu/hw/usb/desc.h +++ b/qemu/hw/usb/desc.h @@ -1,7 +1,6 @@ #ifndef QEMU_HW_USB_DESC_H #define QEMU_HW_USB_DESC_H -#include #include /* binary representation */ diff --git a/qemu/hw/usb/dev-audio.c b/qemu/hw/usb/dev-audio.c index f092bb849..87cab0a3d 100644 --- a/qemu/hw/usb/dev-audio.c +++ b/qemu/hw/usb/dev-audio.c @@ -29,6 +29,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/usb.h" #include "hw/usb/desc.h" @@ -664,7 +665,7 @@ static const VMStateDescription vmstate_usb_audio = { static Property usb_audio_properties[] = { DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0), DEFINE_PROP_UINT32("buffer", USBAudioState, buffer, - 8 * USBAUDIO_PACKET_SIZE), + 32 * USBAUDIO_PACKET_SIZE), DEFINE_PROP_END_OF_LIST(), }; diff --git a/qemu/hw/usb/dev-bluetooth.c b/qemu/hw/usb/dev-bluetooth.c index b19ec76b0..91a4a0b8b 100644 --- a/qemu/hw/usb/dev-bluetooth.c +++ b/qemu/hw/usb/dev-bluetooth.c @@ -18,6 +18,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/error-report.h" #include "hw/usb.h" diff --git a/qemu/hw/usb/dev-hid.c b/qemu/hw/usb/dev-hid.c index 2e7dcd96c..24d05f76f 100644 --- a/qemu/hw/usb/dev-hid.c +++ b/qemu/hw/usb/dev-hid.c @@ -22,10 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "ui/console.h" #include "hw/usb.h" #include "hw/usb/desc.h" +#include "qapi/error.h" #include "qemu/timer.h" #include "hw/input/hid.h" diff --git a/qemu/hw/usb/dev-hub.c b/qemu/hw/usb/dev-hub.c index c8c685550..a33f21cb3 100644 --- a/qemu/hw/usb/dev-hub.c +++ b/qemu/hw/usb/dev-hub.c @@ -21,6 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "trace.h" #include "hw/usb.h" diff --git a/qemu/hw/usb/dev-mtp.c b/qemu/hw/usb/dev-mtp.c index 809b1cb11..bda84a64b 100644 --- a/qemu/hw/usb/dev-mtp.c +++ b/qemu/hw/usb/dev-mtp.c @@ -9,12 +9,17 @@ * This code is licensed under the GPL v2 or later. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include #include -#include -#include #include +#ifdef CONFIG_INOTIFY1 +#include +#include "qapi/error.h" +#include "qemu/main-loop.h" +#endif #include "qemu-common.h" #include "qemu/iov.h" @@ -62,6 +67,11 @@ enum mtp_code { /* format codes */ FMT_UNDEFINED_OBJECT = 0x3000, FMT_ASSOCIATION = 0x3001, + + /* event codes */ + EVT_OBJ_ADDED = 0x4002, + EVT_OBJ_REMOVED = 0x4003, + EVT_OBJ_INFO_CHANGED = 0x4007, }; typedef struct { @@ -84,6 +94,17 @@ enum { EP_EVENT, }; +#ifdef CONFIG_INOTIFY1 +typedef struct MTPMonEntry MTPMonEntry; + +struct MTPMonEntry { + uint32_t event; + uint32_t handle; + + QTAILQ_ENTRY(MTPMonEntry) next; +}; +#endif + struct MTPControl { uint16_t code; uint32_t trans; @@ -108,9 +129,14 @@ struct MTPObject { char *name; char *path; struct stat stat; +#ifdef CONFIG_INOTIFY1 + /* inotify watch cookie */ + int watchfd; +#endif MTPObject *parent; - MTPObject **children; uint32_t nchildren; + QLIST_HEAD(, MTPObject) children; + QLIST_ENTRY(MTPObject) list; bool have_children; QTAILQ_ENTRY(MTPObject) next; }; @@ -128,6 +154,11 @@ struct MTPState { uint32_t next_handle; QTAILQ_HEAD(, MTPObject) objects; +#ifdef CONFIG_INOTIFY1 + /* inotify descriptor */ + int inotifyfd; + QTAILQ_HEAD(events, MTPMonEntry) events; +#endif }; #define TYPE_USB_MTP "usb-mtp" @@ -183,7 +214,7 @@ static const USBDescIface desc_iface_full = { },{ .bEndpointAddress = USB_DIR_IN | EP_EVENT, .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 8, + .wMaxPacketSize = 64, .bInterval = 0x0a, }, } @@ -225,7 +256,7 @@ static const USBDescIface desc_iface_high = { },{ .bEndpointAddress = USB_DIR_IN | EP_EVENT, .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 8, + .wMaxPacketSize = 64, .bInterval = 0x0a, }, } @@ -317,15 +348,24 @@ ignore: static void usb_mtp_object_free(MTPState *s, MTPObject *o) { - int i; + MTPObject *iter; + + if (!o) { + return; + } trace_usb_mtp_object_free(s->dev.addr, o->handle, o->path); QTAILQ_REMOVE(&s->objects, o, next); - for (i = 0; i < o->nchildren; i++) { - usb_mtp_object_free(s, o->children[i]); + if (o->parent) { + QLIST_REMOVE(o, list); + o->parent->nchildren--; + } + + while (!QLIST_EMPTY(&o->children)) { + iter = QLIST_FIRST(&o->children); + usb_mtp_object_free(s, iter); } - g_free(o->children); g_free(o->name); g_free(o->path); g_free(o); @@ -343,6 +383,203 @@ static MTPObject *usb_mtp_object_lookup(MTPState *s, uint32_t handle) return NULL; } +static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o, + char *name) +{ + MTPObject *child = + usb_mtp_object_alloc(s, s->next_handle++, o, name); + + if (child) { + trace_usb_mtp_add_child(s->dev.addr, child->handle, child->path); + QLIST_INSERT_HEAD(&o->children, child, list); + o->nchildren++; + + if (child->format == FMT_ASSOCIATION) { + QLIST_INIT(&child->children); + } + } + + return child; +} + +#ifdef CONFIG_INOTIFY1 +static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent, + char *name, int len) +{ + MTPObject *iter; + + QLIST_FOREACH(iter, &parent->children, list) { + if (strncmp(iter->name, name, len) == 0) { + return iter; + } + } + + return NULL; +} + +static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd) +{ + MTPObject *iter; + + QTAILQ_FOREACH(iter, &s->objects, next) { + if (iter->watchfd == wd) { + return iter; + } + } + + return NULL; +} + +static void inotify_watchfn(void *arg) +{ + MTPState *s = arg; + ssize_t bytes; + /* From the man page: atleast one event can be read */ + int pos; + char buf[sizeof(struct inotify_event) + NAME_MAX + 1]; + + for (;;) { + bytes = read(s->inotifyfd, buf, sizeof(buf)); + pos = 0; + + if (bytes <= 0) { + /* Better luck next time */ + return; + } + + /* + * TODO: Ignore initiator initiated events. + * For now we are good because the store is RO + */ + while (bytes > 0) { + char *p = buf + pos; + struct inotify_event *event = (struct inotify_event *)p; + int watchfd = 0; + uint32_t mask = event->mask & (IN_CREATE | IN_DELETE | + IN_MODIFY | IN_IGNORED); + MTPObject *parent = usb_mtp_object_lookup_wd(s, event->wd); + MTPMonEntry *entry = NULL; + MTPObject *o; + + pos = pos + sizeof(struct inotify_event) + event->len; + bytes = bytes - pos; + + if (!parent) { + continue; + } + + switch (mask) { + case IN_CREATE: + if (usb_mtp_object_lookup_name + (parent, event->name, event->len)) { + /* Duplicate create event */ + continue; + } + entry = g_new0(MTPMonEntry, 1); + entry->handle = s->next_handle; + entry->event = EVT_OBJ_ADDED; + o = usb_mtp_add_child(s, parent, event->name); + if (!o) { + g_free(entry); + continue; + } + o->watchfd = watchfd; + trace_usb_mtp_inotify_event(s->dev.addr, event->name, + event->mask, "Obj Added"); + break; + + case IN_DELETE: + /* + * The kernel issues a IN_IGNORED event + * when a dir containing a watchpoint is + * deleted, so we don't have to delete the + * watchpoint + */ + o = usb_mtp_object_lookup_name(parent, event->name, event->len); + if (!o) { + continue; + } + entry = g_new0(MTPMonEntry, 1); + entry->handle = o->handle; + entry->event = EVT_OBJ_REMOVED; + trace_usb_mtp_inotify_event(s->dev.addr, o->path, + event->mask, "Obj Deleted"); + usb_mtp_object_free(s, o); + break; + + case IN_MODIFY: + o = usb_mtp_object_lookup_name(parent, event->name, event->len); + if (!o) { + continue; + } + entry = g_new0(MTPMonEntry, 1); + entry->handle = o->handle; + entry->event = EVT_OBJ_INFO_CHANGED; + trace_usb_mtp_inotify_event(s->dev.addr, o->path, + event->mask, "Obj Modified"); + break; + + case IN_IGNORED: + o = usb_mtp_object_lookup_name(parent, event->name, event->len); + trace_usb_mtp_inotify_event(s->dev.addr, o->path, + event->mask, "Obj ignored"); + break; + + default: + fprintf(stderr, "usb-mtp: failed to parse inotify event\n"); + continue; + } + + if (entry) { + QTAILQ_INSERT_HEAD(&s->events, entry, next); + } + } + } +} + +static int usb_mtp_inotify_init(MTPState *s) +{ + int fd; + + fd = inotify_init1(IN_NONBLOCK); + if (fd == -1) { + return 1; + } + + QTAILQ_INIT(&s->events); + s->inotifyfd = fd; + + qemu_set_fd_handler(fd, inotify_watchfn, NULL, s); + + return 0; +} + +static void usb_mtp_inotify_cleanup(MTPState *s) +{ + MTPMonEntry *e, *p; + + if (!s->inotifyfd) { + return; + } + + qemu_set_fd_handler(s->inotifyfd, NULL, NULL, s); + close(s->inotifyfd); + + QTAILQ_FOREACH_SAFE(e, &s->events, next, p) { + QTAILQ_REMOVE(&s->events, e, next); + g_free(e); + } +} + +static int usb_mtp_add_watch(int inotifyfd, char *path) +{ + uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY | + IN_ISDIR; + + return inotify_add_watch(inotifyfd, path, mask); +} +#endif + static void usb_mtp_object_readdir(MTPState *s, MTPObject *o) { struct dirent *entry; @@ -357,16 +594,18 @@ static void usb_mtp_object_readdir(MTPState *s, MTPObject *o) if (!dir) { return; } +#ifdef CONFIG_INOTIFY1 + int watchfd = usb_mtp_add_watch(s->inotifyfd, o->path); + if (watchfd == -1) { + fprintf(stderr, "usb-mtp: failed to add watch for %s\n", o->path); + } else { + trace_usb_mtp_inotify_event(s->dev.addr, o->path, + 0, "Watch Added"); + o->watchfd = watchfd; + } +#endif while ((entry = readdir(dir)) != NULL) { - if ((o->nchildren % 32) == 0) { - o->children = g_realloc(o->children, - (o->nchildren + 32) * sizeof(MTPObject *)); - } - o->children[o->nchildren] = - usb_mtp_object_alloc(s, s->next_handle++, o, entry->d_name); - if (o->children[o->nchildren] != NULL) { - o->nchildren++; - } + usb_mtp_add_child(s, o, entry->d_name); } closedir(dir); } @@ -480,7 +719,7 @@ static void usb_mtp_add_wstr(MTPData *data, const wchar_t *str) static void usb_mtp_add_str(MTPData *data, const char *str) { uint32_t len = strlen(str)+1; - wchar_t wstr[len]; + wchar_t *wstr = g_new(wchar_t, len); size_t ret; ret = mbstowcs(wstr, str, len); @@ -489,6 +728,8 @@ static void usb_mtp_add_str(MTPData *data, const char *str) } else { usb_mtp_add_wstr(data, wstr); } + + g_free(wstr); } static void usb_mtp_add_time(MTPData *data, time_t time) @@ -618,13 +859,15 @@ static MTPData *usb_mtp_get_object_handles(MTPState *s, MTPControl *c, MTPObject *o) { MTPData *d = usb_mtp_data_alloc(c); - uint32_t i, handles[o->nchildren]; + uint32_t i = 0, handles[o->nchildren]; + MTPObject *iter; trace_usb_mtp_op_get_object_handles(s->dev.addr, o->handle, o->path); - for (i = 0; i < o->nchildren; i++) { - handles[i] = o->children[i]->handle; + QLIST_FOREACH(iter, &o->children, list) { + handles[i++] = iter->handle; } + assert(i == o->nchildren); usb_mtp_add_u32_array(d, o->nchildren, handles); return d; @@ -755,11 +998,19 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) trace_usb_mtp_op_open_session(s->dev.addr); s->session = c->argv[0]; usb_mtp_object_alloc(s, s->next_handle++, NULL, s->root); +#ifdef CONFIG_INOTIFY1 + if (usb_mtp_inotify_init(s)) { + fprintf(stderr, "usb-mtp: file monitoring init failed\n"); + } +#endif break; case CMD_CLOSE_SESSION: trace_usb_mtp_op_close_session(s->dev.addr); s->session = 0; s->next_handle = 0; +#ifdef CONFIG_INOTIFY1 + usb_mtp_inotify_cleanup(s); +#endif usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); assert(QTAILQ_EMPTY(&s->objects)); break; @@ -885,6 +1136,10 @@ static void usb_mtp_handle_reset(USBDevice *dev) trace_usb_mtp_reset(s->dev.addr); +#ifdef CONFIG_INOTIFY1 + usb_mtp_inotify_cleanup(s); +#endif + usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); s->session = 0; usb_mtp_data_free(s->data_in); s->data_in = NULL; @@ -1044,6 +1299,31 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p) } break; case EP_EVENT: +#ifdef CONFIG_INOTIFY1 + if (!QTAILQ_EMPTY(&s->events)) { + struct MTPMonEntry *e = QTAILQ_LAST(&s->events, events); + uint32_t handle; + int len = sizeof(container) + sizeof(uint32_t); + + if (p->iov.size < len) { + trace_usb_mtp_stall(s->dev.addr, + "packet too small to send event"); + p->status = USB_RET_STALL; + return; + } + + QTAILQ_REMOVE(&s->events, e, next); + container.length = cpu_to_le32(len); + container.type = cpu_to_le32(TYPE_EVENT); + container.code = cpu_to_le16(e->event); + container.trans = 0; /* no trans specific events */ + handle = cpu_to_le32(e->handle); + usb_packet_copy(p, &container, sizeof(container)); + usb_packet_copy(p, &handle, sizeof(uint32_t)); + g_free(e); + return; + } +#endif p->status = USB_RET_NAK; return; default: diff --git a/qemu/hw/usb/dev-network.c b/qemu/hw/usb/dev-network.c index 7800ceea5..74306b58e 100644 --- a/qemu/hw/usb/dev-network.c +++ b/qemu/hw/usb/dev-network.c @@ -23,6 +23,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "hw/usb.h" #include "hw/usb/desc.h" @@ -32,6 +34,7 @@ #include "qemu/config-file.h" #include "sysemu/sysemu.h" #include "qemu/iov.h" +#include "qemu/cutils.h" /*#define TRAFFIC_DEBUG*/ /* Thanks to NetChip Technologies for donating this product ID. @@ -653,7 +656,8 @@ typedef struct USBNetState { static int is_rndis(USBNetState *s) { - return s->dev.config->bConfigurationValue == DEV_RNDIS_CONFIG_VALUE; + return s->dev.config ? + s->dev.config->bConfigurationValue == DEV_RNDIS_CONFIG_VALUE : 0; } static int ndis_query(USBNetState *s, uint32_t oid, @@ -914,8 +918,9 @@ static int rndis_query_response(USBNetState *s, bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8; buflen = le32_to_cpu(buf->InformationBufferLength); - if (bufoffs + buflen > length) + if (buflen > length || bufoffs >= length || bufoffs + buflen > length) { return USB_RET_STALL; + } infobuflen = ndis_query(s, le32_to_cpu(buf->OID), bufoffs + (uint8_t *) buf, buflen, infobuf, @@ -960,8 +965,9 @@ static int rndis_set_response(USBNetState *s, bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8; buflen = le32_to_cpu(buf->InformationBufferLength); - if (bufoffs + buflen > length) + if (buflen > length || bufoffs >= length || bufoffs + buflen > length) { return USB_RET_STALL; + } ret = ndis_set(s, le32_to_cpu(buf->OID), bufoffs + (uint8_t *) buf, buflen); @@ -1211,8 +1217,9 @@ static void usb_net_handle_dataout(USBNetState *s, USBPacket *p) if (le32_to_cpu(msg->MessageType) == RNDIS_PACKET_MSG) { uint32_t offs = 8 + le32_to_cpu(msg->DataOffset); uint32_t size = le32_to_cpu(msg->DataLength); - if (offs + size <= len) + if (offs < len && size < len && offs + size <= len) { qemu_send_packet(qemu_get_queue(s->nic), s->out_buf + offs, size); + } } s->out_ptr -= len; memmove(s->out_buf, &s->out_buf[len], s->out_ptr); diff --git a/qemu/hw/usb/dev-serial.c b/qemu/hw/usb/dev-serial.c index a6a66008e..ba8538e60 100644 --- a/qemu/hw/usb/dev-serial.c +++ b/qemu/hw/usb/dev-serial.c @@ -8,7 +8,10 @@ * This code is licensed under the LGPL. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" +#include "qemu/cutils.h" #include "qemu/error-report.h" #include "hw/usb.h" #include "hw/usb/desc.h" diff --git a/qemu/hw/usb/dev-smartcard-reader.c b/qemu/hw/usb/dev-smartcard-reader.c index 8952efffa..af4b85135 100644 --- a/qemu/hw/usb/dev-smartcard-reader.c +++ b/qemu/hw/usb/dev-smartcard-reader.c @@ -34,6 +34,8 @@ * Not sure which messages trigger this. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/error-report.h" #include "hw/usb.h" diff --git a/qemu/hw/usb/dev-storage.c b/qemu/hw/usb/dev-storage.c index 9a4e7dc0c..248a58045 100644 --- a/qemu/hw/usb/dev-storage.c +++ b/qemu/hw/usb/dev-storage.c @@ -7,6 +7,8 @@ * This code is licensed under the LGPL. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/error-report.h" #include "qemu/option.h" @@ -20,6 +22,7 @@ #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "qapi/visitor.h" +#include "qemu/cutils.h" //#define DEBUG_MSD @@ -613,20 +616,22 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp) return; } - bdrv_add_key(blk_bs(blk), NULL, &err); - if (err) { - if (monitor_cur_is_qmp()) { - error_propagate(errp, err); - return; - } - error_free(err); - err = NULL; - if (cur_mon) { - monitor_read_bdrv_key_start(cur_mon, blk_bs(blk), - usb_msd_password_cb, s); - s->dev.auto_attach = 0; - } else { - autostart = 0; + if (blk_bs(blk)) { + bdrv_add_key(blk_bs(blk), NULL, &err); + if (err) { + if (monitor_cur_is_qmp()) { + error_propagate(errp, err); + return; + } + error_free(err); + err = NULL; + if (cur_mon) { + monitor_read_bdrv_key_start(cur_mon, blk_bs(blk), + usb_msd_password_cb, s); + s->dev.auto_attach = 0; + } else { + autostart = 0; + } } } @@ -778,24 +783,24 @@ static void usb_msd_class_initfn_storage(ObjectClass *klass, void *data) dc->props = msd_properties; } -static void usb_msd_get_bootindex(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void usb_msd_get_bootindex(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { USBDevice *dev = USB_DEVICE(obj); MSDState *s = USB_STORAGE_DEV(dev); - visit_type_int32(v, &s->conf.bootindex, name, errp); + visit_type_int32(v, name, &s->conf.bootindex, errp); } -static void usb_msd_set_bootindex(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void usb_msd_set_bootindex(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { USBDevice *dev = USB_DEVICE(obj); MSDState *s = USB_STORAGE_DEV(dev); int32_t boot_index; Error *local_err = NULL; - visit_type_int32(v, &boot_index, name, &local_err); + visit_type_int32(v, name, &boot_index, &local_err); if (local_err) { goto out; } diff --git a/qemu/hw/usb/dev-uas.c b/qemu/hw/usb/dev-uas.c index 38b26c586..0678b1b05 100644 --- a/qemu/hw/usb/dev-uas.c +++ b/qemu/hw/usb/dev-uas.c @@ -9,6 +9,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/option.h" #include "qemu/config-file.h" diff --git a/qemu/hw/usb/dev-wacom.c b/qemu/hw/usb/dev-wacom.c index c2450e729..c4702dbba 100644 --- a/qemu/hw/usb/dev-wacom.c +++ b/qemu/hw/usb/dev-wacom.c @@ -25,6 +25,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "ui/console.h" #include "hw/usb.h" diff --git a/qemu/hw/usb/hcd-ehci-pci.c b/qemu/hw/usb/hcd-ehci-pci.c index 7afa5f9d6..56577051e 100644 --- a/qemu/hw/usb/hcd-ehci-pci.c +++ b/qemu/hw/usb/hcd-ehci-pci.c @@ -15,6 +15,7 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/usb/hcd-ehci.h" #include "qemu/range.h" @@ -95,10 +96,8 @@ static void usb_ehci_pci_exit(PCIDevice *dev) usb_ehci_unrealize(s, DEVICE(dev), NULL); - if (s->irq) { - g_free(s->irq); - s->irq = NULL; - } + g_free(s->irq); + s->irq = NULL; } static void usb_ehci_pci_reset(DeviceState *dev) diff --git a/qemu/hw/usb/hcd-ehci-sysbus.c b/qemu/hw/usb/hcd-ehci-sysbus.c index cd1cc142a..6c20604d0 100644 --- a/qemu/hw/usb/hcd-ehci-sysbus.c +++ b/qemu/hw/usb/hcd-ehci-sysbus.c @@ -15,6 +15,7 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/usb/hcd-ehci.h" static const VMStateDescription vmstate_ehci_sysbus = { diff --git a/qemu/hw/usb/hcd-ehci.c b/qemu/hw/usb/hcd-ehci.c index 64a54c6e8..43a8f7abc 100644 --- a/qemu/hw/usb/hcd-ehci.c +++ b/qemu/hw/usb/hcd-ehci.c @@ -27,6 +27,8 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/usb/ehci-regs.h" #include "hw/usb/hcd-ehci.h" #include "trace.h" @@ -726,7 +728,7 @@ static void ehci_detach(USBPort *port) ehci_queues_rip_device(s, port->dev, 0); ehci_queues_rip_device(s, port->dev, 1); - *portsc &= ~(PORTSC_CONNECT|PORTSC_PED); + *portsc &= ~(PORTSC_CONNECT|PORTSC_PED|PORTSC_SUSPEND); *portsc |= PORTSC_CSC; ehci_raise_irq(s, USBSTS_PCD); @@ -865,6 +867,7 @@ void ehci_reset(void *opaque) s->usbsts = USBSTS_HALT; s->usbsts_pending = 0; s->usbsts_frindex = 0; + ehci_update_irq(s); s->astate = EST_INACTIVE; s->pstate = EST_INACTIVE; @@ -893,6 +896,11 @@ static uint64_t ehci_caps_read(void *ptr, hwaddr addr, return s->caps[addr]; } +static void ehci_caps_write(void *ptr, hwaddr addr, + uint64_t val, unsigned size) +{ +} + static uint64_t ehci_opreg_read(void *ptr, hwaddr addr, unsigned size) { @@ -1404,21 +1412,23 @@ static int ehci_process_itd(EHCIState *ehci, if (itd->transact[i] & ITD_XACT_ACTIVE) { pg = get_field(itd->transact[i], ITD_XACT_PGSEL); off = itd->transact[i] & ITD_XACT_OFFSET_MASK; - ptr1 = (itd->bufptr[pg] & ITD_BUFPTR_MASK); - ptr2 = (itd->bufptr[pg+1] & ITD_BUFPTR_MASK); len = get_field(itd->transact[i], ITD_XACT_LENGTH); if (len > max * mult) { len = max * mult; } - - if (len > BUFF_SIZE) { + if (len > BUFF_SIZE || pg > 6) { return -1; } + ptr1 = (itd->bufptr[pg] & ITD_BUFPTR_MASK); qemu_sglist_init(&ehci->isgl, ehci->device, 2, ehci->as); if (off + len > 4096) { /* transfer crosses page border */ + if (pg == 6) { + return -1; /* avoid page pg + 1 */ + } + ptr2 = (itd->bufptr[pg + 1] & ITD_BUFPTR_MASK); uint32_t len2 = off + len - 4096; uint32_t len1 = len - len2; qemu_sglist_add(&ehci->isgl, ptr1 + off, len1); @@ -2000,6 +2010,7 @@ static int ehci_state_writeback(EHCIQueue *q) static void ehci_advance_state(EHCIState *ehci, int async) { EHCIQueue *q = NULL; + int itd_count = 0; int again; do { @@ -2024,10 +2035,12 @@ static void ehci_advance_state(EHCIState *ehci, int async) case EST_FETCHITD: again = ehci_state_fetchitd(ehci, async); + itd_count++; break; case EST_FETCHSITD: again = ehci_state_fetchsitd(ehci, async); + itd_count++; break; case EST_ADVANCEQUEUE: @@ -2076,7 +2089,8 @@ static void ehci_advance_state(EHCIState *ehci, int async) break; } - if (again < 0) { + if (again < 0 || itd_count > 16) { + /* TODO: notify guest (raise HSE irq?) */ fprintf(stderr, "processing error - resetting ehci HC\n"); ehci_reset(ehci); again = 0; @@ -2298,10 +2312,11 @@ static void ehci_frame_timer(void *opaque) /* If we've raised int, we speed up the timer, so that we quickly * notice any new packets queued up in response */ if (ehci->int_req_by_async && (ehci->usbsts & USBSTS_INT)) { - expire_time = t_now + get_ticks_per_sec() / (FRAME_TIMER_FREQ * 4); + expire_time = t_now + + NANOSECONDS_PER_SECOND / (FRAME_TIMER_FREQ * 4); ehci->int_req_by_async = false; } else { - expire_time = t_now + (get_ticks_per_sec() + expire_time = t_now + (NANOSECONDS_PER_SECOND * (ehci->async_stepdown+1) / FRAME_TIMER_FREQ); } timer_mod(ehci->frame_timer, expire_time); @@ -2310,6 +2325,7 @@ static void ehci_frame_timer(void *opaque) static const MemoryRegionOps ehci_mmio_caps_ops = { .read = ehci_caps_read, + .write = ehci_caps_write, .valid.min_access_size = 1, .valid.max_access_size = 4, .impl.min_access_size = 1, diff --git a/qemu/hw/usb/hcd-musb.c b/qemu/hw/usb/hcd-musb.c index 61cc87894..27d9d0bd8 100644 --- a/qemu/hw/usb/hcd-musb.c +++ b/qemu/hw/usb/hcd-musb.c @@ -20,6 +20,7 @@ * * Only host-mode and non-DMA accesses are currently supported. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/timer.h" #include "hw/usb.h" @@ -563,7 +564,7 @@ static void musb_schedule_cb(USBPort *port, USBPacket *packey) ep->intv_timer[dir] = timer_new_ns(QEMU_CLOCK_VIRTUAL, musb_cb_tick, ep); timer_mod(ep->intv_timer[dir], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - muldiv64(timeout, get_ticks_per_sec(), 8000)); + muldiv64(timeout, NANOSECONDS_PER_SECOND, 8000)); } static int musb_timeout(int ttype, int speed, int val) diff --git a/qemu/hw/usb/hcd-ohci.c b/qemu/hw/usb/hcd-ohci.c index 7d6581806..ffab561cf 100644 --- a/qemu/hw/usb/hcd-ohci.c +++ b/qemu/hw/usb/hcd-ohci.c @@ -25,7 +25,9 @@ * o BIOS work to boot from USB storage */ +#include "qemu/osdep.h" #include "hw/hw.h" +#include "qapi/error.h" #include "qemu/timer.h" #include "hw/usb.h" #include "hw/pci/pci.h" @@ -439,15 +441,37 @@ static void ohci_stop_endpoints(OHCIState *ohci) } } -/* Reset the controller */ -static void ohci_reset(void *opaque) +static void ohci_roothub_reset(OHCIState *ohci) { - OHCIState *ohci = opaque; OHCIPort *port; int i; ohci_bus_stop(ohci); - ohci->ctl = 0; + ohci->rhdesc_a = OHCI_RHA_NPS | ohci->num_ports; + ohci->rhdesc_b = 0x0; /* Impl. specific */ + ohci->rhstatus = 0; + + for (i = 0; i < ohci->num_ports; i++) { + port = &ohci->rhport[i]; + port->ctrl = 0; + if (port->port.dev && port->port.dev->attached) { + usb_port_reset(&port->port); + } + } + if (ohci->async_td) { + usb_cancel_packet(&ohci->usb_packet); + ohci->async_td = 0; + } + ohci_stop_endpoints(ohci); +} + +/* Reset the controller */ +static void ohci_soft_reset(OHCIState *ohci) +{ + trace_usb_ohci_reset(ohci->name); + + ohci_bus_stop(ohci); + ohci->ctl = (ohci->ctl & OHCI_CTL_IR) | OHCI_USB_SUSPEND; ohci->old_ctl = 0; ohci->status = 0; ohci->intr_status = 0; @@ -470,25 +494,13 @@ static void ohci_reset(void *opaque) ohci->frame_number = 0; ohci->pstart = 0; ohci->lst = OHCI_LS_THRESH; +} - ohci->rhdesc_a = OHCI_RHA_NPS | ohci->num_ports; - ohci->rhdesc_b = 0x0; /* Impl. specific */ - ohci->rhstatus = 0; - - for (i = 0; i < ohci->num_ports; i++) - { - port = &ohci->rhport[i]; - port->ctrl = 0; - if (port->port.dev && port->port.dev->attached) { - usb_port_reset(&port->port); - } - } - if (ohci->async_td) { - usb_cancel_packet(&ohci->usb_packet); - ohci->async_td = 0; - } - ohci_stop_endpoints(ohci); - trace_usb_ohci_reset(ohci->name); +static void ohci_hard_reset(OHCIState *ohci) +{ + ohci_soft_reset(ohci); + ohci->ctl = 0; + ohci_roothub_reset(ohci); } /* Get an array of dwords from main memory */ @@ -1231,11 +1243,16 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) return active; } -/* Generate a SOF event, and set a timer for EOF */ -static void ohci_sof(OHCIState *ohci) +/* set a timer for EOF */ +static void ohci_eof_timer(OHCIState *ohci) { ohci->sof_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); timer_mod(ohci->eof_timer, ohci->sof_time + usb_frame_time); +} +/* Set a timer for EOF and generate a SOF event */ +static void ohci_sof(OHCIState *ohci) +{ + ohci_eof_timer(ohci); ohci_set_interrupt(ohci, OHCI_INTR_SF); } @@ -1331,19 +1348,14 @@ static void ohci_frame_boundary(void *opaque) */ static int ohci_bus_start(OHCIState *ohci) { - ohci->eof_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - ohci_frame_boundary, - ohci); - - if (ohci->eof_timer == NULL) { - trace_usb_ohci_bus_eof_timer_failed(ohci->name); - ohci_die(ohci); - return 0; - } - trace_usb_ohci_start(ohci->name); - ohci_sof(ohci); + /* Delay the first SOF event by one frame time as + * linux driver is not ready to receive it and + * can meet some race conditions + */ + + ohci_eof_timer(ohci); return 1; } @@ -1352,11 +1364,7 @@ static int ohci_bus_start(OHCIState *ohci) static void ohci_bus_stop(OHCIState *ohci) { trace_usb_ohci_stop(ohci->name); - if (ohci->eof_timer) { - timer_del(ohci->eof_timer); - timer_free(ohci->eof_timer); - } - ohci->eof_timer = NULL; + timer_del(ohci->eof_timer); } /* Sets a flag in a port status register but only set it if the port is @@ -1436,12 +1444,15 @@ static void ohci_set_ctl(OHCIState *ohci, uint32_t val) break; case OHCI_USB_SUSPEND: ohci_bus_stop(ohci); + /* clear pending SF otherwise linux driver loops in ohci_irq() */ + ohci->intr_status &= ~OHCI_INTR_SF; + ohci_intr_update(ohci); break; case OHCI_USB_RESUME: trace_usb_ohci_resume(ohci->name); break; case OHCI_USB_RESET: - ohci_reset(ohci); + ohci_roothub_reset(ohci); break; } } @@ -1704,7 +1715,7 @@ static void ohci_mem_write(void *opaque, ohci->status |= val; if (ohci->status & OHCI_STATUS_HCR) - ohci_reset(ohci); + ohci_soft_reset(ohci); break; case 3: /* HcInterruptStatus */ @@ -1783,7 +1794,7 @@ static void ohci_mem_write(void *opaque, case 25: /* HcHReset */ ohci->hreset = val & ~OHCI_HRESET_FSBIR; if (val & OHCI_HRESET_FSBIR) - ohci_reset(ohci); + ohci_hard_reset(ohci); break; case 26: /* HcHInterruptEnable */ @@ -1839,12 +1850,12 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, if (usb_frame_time == 0) { #ifdef OHCI_TIME_WARP - usb_frame_time = get_ticks_per_sec(); - usb_bit_time = muldiv64(1, get_ticks_per_sec(), USB_HZ/1000); + usb_frame_time = NANOSECONDS_PER_SECOND; + usb_bit_time = NANOSECONDS_PER_SECOND / (USB_HZ / 1000); #else - usb_frame_time = muldiv64(1, get_ticks_per_sec(), 1000); - if (get_ticks_per_sec() >= USB_HZ) { - usb_bit_time = muldiv64(1, get_ticks_per_sec(), USB_HZ); + usb_frame_time = NANOSECONDS_PER_SECOND / 1000; + if (NANOSECONDS_PER_SECOND >= USB_HZ) { + usb_bit_time = NANOSECONDS_PER_SECOND / USB_HZ; } else { usb_bit_time = 1; } @@ -1883,6 +1894,9 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, usb_packet_init(&ohci->usb_packet); ohci->async_td = 0; + + ohci->eof_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + ohci_frame_boundary, ohci); } #define TYPE_PCI_OHCI "pci-ohci" @@ -1952,6 +1966,9 @@ static void usb_ohci_exit(PCIDevice *dev) if (!ohci->masterbus) { usb_bus_release(&s->bus); } + + timer_del(s->eof_timer); + timer_free(s->eof_timer); } static void usb_ohci_reset_pci(DeviceState *d) @@ -1960,7 +1977,7 @@ static void usb_ohci_reset_pci(DeviceState *d) OHCIPCIState *ohci = PCI_OHCI(dev); OHCIState *s = &ohci->state; - ohci_reset(s); + ohci_hard_reset(s); } #define TYPE_SYSBUS_OHCI "sysbus-ohci" @@ -1993,7 +2010,7 @@ static void usb_ohci_reset_sysbus(DeviceState *dev) OHCISysBusState *s = SYSBUS_OHCI(dev); OHCIState *ohci = &s->ohci; - ohci_reset(ohci); + ohci_hard_reset(ohci); } static Property ohci_pci_properties[] = { @@ -2017,23 +2034,13 @@ static bool ohci_eof_timer_needed(void *opaque) { OHCIState *ohci = opaque; - return ohci->eof_timer != NULL; -} - -static int ohci_eof_timer_pre_load(void *opaque) -{ - OHCIState *ohci = opaque; - - ohci_bus_start(ohci); - - return 0; + return timer_pending(ohci->eof_timer); } static const VMStateDescription vmstate_ohci_eof_timer = { .name = "ohci-core/eof-timer", .version_id = 1, .minimum_version_id = 1, - .pre_load = ohci_eof_timer_pre_load, .needed = ohci_eof_timer_needed, .fields = (VMStateField[]) { VMSTATE_TIMER_PTR(eof_timer, OHCIState), diff --git a/qemu/hw/usb/hcd-uhci.c b/qemu/hw/usb/hcd-uhci.c index 3f0ed6268..ca72a80f2 100644 --- a/qemu/hw/usb/hcd-uhci.c +++ b/qemu/hw/usb/hcd-uhci.c @@ -25,10 +25,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/usb.h" #include "hw/usb/uhci-regs.h" #include "hw/pci/pci.h" +#include "qapi/error.h" #include "qemu/timer.h" #include "qemu/iov.h" #include "sysemu/dma.h" @@ -401,7 +403,7 @@ static int uhci_post_load(void *opaque, int version_id) if (version_id < 2) { s->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (get_ticks_per_sec() / FRAME_TIMER_FREQ); + (NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ); } return 0; } @@ -443,7 +445,7 @@ static void uhci_port_write(void *opaque, hwaddr addr, /* start frame processing */ trace_usb_uhci_schedule_start(); s->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (get_ticks_per_sec() / FRAME_TIMER_FREQ); + (NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ); timer_mod(s->frame_timer, s->expire_time); s->status &= ~UHCI_STS_HCHALTED; } else if (!(val & UHCI_CMD_RS)) { @@ -772,8 +774,9 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, bool spd; bool queuing = (q != NULL); uint8_t pid = td->token & 0xff; - UHCIAsync *async = uhci_async_find_td(s, td_addr); + UHCIAsync *async; + async = uhci_async_find_td(s, td_addr); if (async) { if (uhci_queue_verify(async->queue, qh_addr, td, td_addr, queuing)) { assert(q == NULL || q == async->queue); @@ -812,6 +815,19 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, return TD_RESULT_NEXT_QH; } + switch (pid) { + case USB_TOKEN_OUT: + case USB_TOKEN_SETUP: + case USB_TOKEN_IN: + break; + default: + /* invalid pid : frame interrupted */ + s->status |= UHCI_STS_HCPERR; + s->cmd &= ~UHCI_CMD_RS; + uhci_update_irq(s); + return TD_RESULT_STOP_FRAME; + } + if (async) { if (queuing) { /* we are busy filling the queue, we are not prepared @@ -879,11 +895,7 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, break; default: - /* invalid pid : frame interrupted */ - uhci_async_free(async); - s->status |= UHCI_STS_HCPERR; - uhci_update_irq(s); - return TD_RESULT_STOP_FRAME; + abort(); /* Never to execute */ } if (async->packet.status == USB_RET_ASYNC) { @@ -1119,7 +1131,7 @@ static void uhci_frame_timer(void *opaque) UHCIState *s = opaque; uint64_t t_now, t_last_run; int i, frames; - const uint64_t frame_t = get_ticks_per_sec() / FRAME_TIMER_FREQ; + const uint64_t frame_t = NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ; s->completions_only = false; qemu_bh_cancel(s->bh); diff --git a/qemu/hw/usb/hcd-xhci.c b/qemu/hw/usb/hcd-xhci.c index c673bed4c..bcde8a2f4 100644 --- a/qemu/hw/usb/hcd-xhci.c +++ b/qemu/hw/usb/hcd-xhci.c @@ -18,6 +18,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "qemu/timer.h" #include "hw/usb.h" @@ -697,11 +698,13 @@ static inline void xhci_dma_write_u32s(XHCIState *xhci, dma_addr_t addr, uint32_t *buf, size_t len) { int i; - uint32_t tmp[len / sizeof(uint32_t)]; + uint32_t tmp[5]; + uint32_t n = len / sizeof(uint32_t); assert((len % sizeof(uint32_t)) == 0); + assert(n <= ARRAY_SIZE(tmp)); - for (i = 0; i < (len / sizeof(uint32_t)); i++) { + for (i = 0; i < n; i++) { tmp[i] = cpu_to_le32(buf[i]); } pci_dma_write(PCI_DEVICE(xhci), addr, tmp, len); @@ -1453,9 +1456,7 @@ static int xhci_ep_nuke_one_xfer(XHCITransfer *t, TRBCCode report) t->running_retry = 0; killed = 1; } - if (t->trbs) { - g_free(t->trbs); - } + g_free(t->trbs); t->trbs = NULL; t->trb_count = t->trb_alloced = 0; @@ -2190,7 +2191,7 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, xfer->trbs = NULL; } if (!xfer->trbs) { - xfer->trbs = g_malloc(sizeof(XHCITRB) * length); + xfer->trbs = g_new(XHCITRB, length); xfer->trb_alloced = length; } xfer->trb_count = length; diff --git a/qemu/hw/usb/host-legacy.c b/qemu/hw/usb/host-legacy.c index 422ed9a65..3b57e21b5 100644 --- a/qemu/hw/usb/host-legacy.c +++ b/qemu/hw/usb/host-legacy.c @@ -30,6 +30,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/usb.h" #include "hw/usb/host.h" diff --git a/qemu/hw/usb/host-libusb.c b/qemu/hw/usb/host-libusb.c index 11429f5e7..6458a9448 100644 --- a/qemu/hw/usb/host-libusb.c +++ b/qemu/hw/usb/host-libusb.c @@ -33,9 +33,11 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include +#include "qapi/error.h" #include "qemu-common.h" #include "monitor/monitor.h" #include "qemu/error-report.h" @@ -451,6 +453,7 @@ static void usb_host_req_complete_iso(struct libusb_transfer *transfer) } if (xfer->ring->ep->pid == USB_TOKEN_IN) { QTAILQ_INSERT_TAIL(&xfer->ring->copy, xfer, next); + usb_wakeup(xfer->ring->ep, 0); } else { QTAILQ_INSERT_TAIL(&xfer->ring->unused, xfer, next); } @@ -1239,7 +1242,7 @@ static void usb_host_handle_control(USBDevice *udev, USBPacket *p, /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices * to work redirected to a not superspeed capable hcd */ - if (udev->speed == USB_SPEED_SUPER && + if ((udev->speedmask & USB_SPEED_MASK_SUPER) && !(udev->port->speedmask & USB_SPEED_MASK_SUPER) && request == 0x8006 && value == 0x100 && index == 0) { r->usb3ep0quirk = true; @@ -1429,7 +1432,7 @@ static void usb_host_free_streams(USBDevice *udev, USBEndpoint **eps, * still present in the first place. Attemping to contine where we * left off is impossible. * - * What we are going to to to here is emulate a surprise removal of + * What we are going to do here is emulate a surprise removal of * the usb device passed through, then kick host scan so the device * will get re-attached (and re-initialized by the guest) in case it * is still present. diff --git a/qemu/hw/usb/host-stub.c b/qemu/hw/usb/host-stub.c index 2eaaa8341..6ba65a1f6 100644 --- a/qemu/hw/usb/host-stub.c +++ b/qemu/hw/usb/host-stub.c @@ -30,6 +30,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "ui/console.h" #include "hw/usb.h" diff --git a/qemu/hw/usb/libhw.c b/qemu/hw/usb/libhw.c index 8df11c461..73cdf0c97 100644 --- a/qemu/hw/usb/libhw.c +++ b/qemu/hw/usb/libhw.c @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/hw.h" #include "hw/usb.h" diff --git a/qemu/hw/usb/quirks.c b/qemu/hw/usb/quirks.c index a761a9603..38a9c5634 100644 --- a/qemu/hw/usb/quirks.c +++ b/qemu/hw/usb/quirks.c @@ -12,6 +12,7 @@ * (at your option) any later version. */ +#include "qemu/osdep.h" #include "quirks.h" #include "hw/usb.h" diff --git a/qemu/hw/usb/redirect.c b/qemu/hw/usb/redirect.c index 25df25fd0..8d8054037 100644 --- a/qemu/hw/usb/redirect.c +++ b/qemu/hw/usb/redirect.c @@ -25,6 +25,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" @@ -33,14 +35,14 @@ #include "qemu/iov.h" #include "sysemu/char.h" -#include -#include -#include #include #include #include "hw/usb.h" +/* ERROR is defined below. Remove any previous definition. */ +#undef ERROR + #define MAX_ENDPOINTS 32 #define NO_INTERFACE_INFO 255 /* Valid interface_count always <= 32 */ #define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f)) @@ -324,7 +326,7 @@ static void packet_id_queue_add(struct PacketIdQueue *q, uint64_t id) DPRINTF("adding packet id %"PRIu64" to %s queue\n", id, q->name); - e = g_malloc0(sizeof(struct PacketIdQueueEntry)); + e = g_new0(struct PacketIdQueueEntry, 1); e->id = id; QTAILQ_INSERT_TAIL(&q->head, e, next); q->size++; @@ -448,7 +450,7 @@ static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev, return p; } -static void bufp_alloc(USBRedirDevice *dev, uint8_t *data, uint16_t len, +static int bufp_alloc(USBRedirDevice *dev, uint8_t *data, uint16_t len, uint8_t status, uint8_t ep, void *free_on_destroy) { struct buf_packet *bufp; @@ -465,12 +467,12 @@ static void bufp_alloc(USBRedirDevice *dev, uint8_t *data, uint16_t len, if (dev->endpoint[EP2I(ep)].bufpq_size > dev->endpoint[EP2I(ep)].bufpq_target_size) { free(data); - return; + return -1; } dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0; } - bufp = g_malloc(sizeof(struct buf_packet)); + bufp = g_new(struct buf_packet, 1); bufp->data = data; bufp->len = len; bufp->offset = 0; @@ -478,6 +480,7 @@ static void bufp_alloc(USBRedirDevice *dev, uint8_t *data, uint16_t len, bufp->free_on_destroy = free_on_destroy; QTAILQ_INSERT_TAIL(&dev->endpoint[EP2I(ep)].bufpq, bufp, next); dev->endpoint[EP2I(ep)].bufpq_size++; + return 0; } static void bufp_free(USBRedirDevice *dev, struct buf_packet *bufp, @@ -2083,13 +2086,17 @@ static void usbredir_buffered_bulk_packet(void *priv, uint64_t id, status = usb_redir_success; free_on_destroy = NULL; for (i = 0; i < data_len; i += len) { + int r; if (len >= (data_len - i)) { len = data_len - i; status = buffered_bulk_packet->status; free_on_destroy = data; } /* bufp_alloc also adds the packet to the ep queue */ - bufp_alloc(dev, data + i, len, status, ep, free_on_destroy); + r = bufp_alloc(dev, data + i, len, status, ep, free_on_destroy); + if (r) { + break; + } } if (dev->endpoint[EP2I(ep)].pending_async_packet) { @@ -2236,7 +2243,7 @@ static int usbredir_get_bufpq(QEMUFile *f, void *priv, size_t unused) endp->bufpq_size = qemu_get_be32(f); for (i = 0; i < endp->bufpq_size; i++) { - bufp = g_malloc(sizeof(struct buf_packet)); + bufp = g_new(struct buf_packet, 1); bufp->len = qemu_get_be32(f); bufp->status = qemu_get_be32(f); bufp->offset = 0; diff --git a/qemu/hw/usb/tusb6010.c b/qemu/hw/usb/tusb6010.c new file mode 100644 index 000000000..8f593a6fd --- /dev/null +++ b/qemu/hw/usb/tusb6010.c @@ -0,0 +1,817 @@ +/* + * Texas Instruments TUSB6010 emulation. + * Based on reverse-engineering of a linux driver. + * + * Copyright (C) 2008 Nokia Corporation + * Written by Andrzej Zaborowski + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/timer.h" +#include "hw/usb.h" +#include "hw/arm/omap.h" +#include "hw/irq.h" +#include "hw/devices.h" +#include "hw/sysbus.h" + +#define TYPE_TUSB6010 "tusb6010" +#define TUSB(obj) OBJECT_CHECK(TUSBState, (obj), TYPE_TUSB6010) + +typedef struct TUSBState { + SysBusDevice parent_obj; + + MemoryRegion iomem[2]; + qemu_irq irq; + MUSBState *musb; + QEMUTimer *otg_timer; + QEMUTimer *pwr_timer; + + int power; + uint32_t scratch; + uint16_t test_reset; + uint32_t prcm_config; + uint32_t prcm_mngmt; + uint16_t otg_status; + uint32_t dev_config; + int host_mode; + uint32_t intr; + uint32_t intr_ok; + uint32_t mask; + uint32_t usbip_intr; + uint32_t usbip_mask; + uint32_t gpio_intr; + uint32_t gpio_mask; + uint32_t gpio_config; + uint32_t dma_intr; + uint32_t dma_mask; + uint32_t dma_map; + uint32_t dma_config; + uint32_t ep0_config; + uint32_t rx_config[15]; + uint32_t tx_config[15]; + uint32_t wkup_mask; + uint32_t pullup[2]; + uint32_t control_config; + uint32_t otg_timer_val; +} TUSBState; + +#define TUSB_DEVCLOCK 60000000 /* 60 MHz */ + +#define TUSB_VLYNQ_CTRL 0x004 + +/* Mentor Graphics OTG core registers. */ +#define TUSB_BASE_OFFSET 0x400 + +/* FIFO registers, 32-bit. */ +#define TUSB_FIFO_BASE 0x600 + +/* Device System & Control registers, 32-bit. */ +#define TUSB_SYS_REG_BASE 0x800 + +#define TUSB_DEV_CONF (TUSB_SYS_REG_BASE + 0x000) +#define TUSB_DEV_CONF_USB_HOST_MODE (1 << 16) +#define TUSB_DEV_CONF_PROD_TEST_MODE (1 << 15) +#define TUSB_DEV_CONF_SOFT_ID (1 << 1) +#define TUSB_DEV_CONF_ID_SEL (1 << 0) + +#define TUSB_PHY_OTG_CTRL_ENABLE (TUSB_SYS_REG_BASE + 0x004) +#define TUSB_PHY_OTG_CTRL (TUSB_SYS_REG_BASE + 0x008) +#define TUSB_PHY_OTG_CTRL_WRPROTECT (0xa5 << 24) +#define TUSB_PHY_OTG_CTRL_O_ID_PULLUP (1 << 23) +#define TUSB_PHY_OTG_CTRL_O_VBUS_DET_EN (1 << 19) +#define TUSB_PHY_OTG_CTRL_O_SESS_END_EN (1 << 18) +#define TUSB_PHY_OTG_CTRL_TESTM2 (1 << 17) +#define TUSB_PHY_OTG_CTRL_TESTM1 (1 << 16) +#define TUSB_PHY_OTG_CTRL_TESTM0 (1 << 15) +#define TUSB_PHY_OTG_CTRL_TX_DATA2 (1 << 14) +#define TUSB_PHY_OTG_CTRL_TX_GZ2 (1 << 13) +#define TUSB_PHY_OTG_CTRL_TX_ENABLE2 (1 << 12) +#define TUSB_PHY_OTG_CTRL_DM_PULLDOWN (1 << 11) +#define TUSB_PHY_OTG_CTRL_DP_PULLDOWN (1 << 10) +#define TUSB_PHY_OTG_CTRL_OSC_EN (1 << 9) +#define TUSB_PHY_OTG_CTRL_PHYREF_CLK(v) (((v) & 3) << 7) +#define TUSB_PHY_OTG_CTRL_PD (1 << 6) +#define TUSB_PHY_OTG_CTRL_PLL_ON (1 << 5) +#define TUSB_PHY_OTG_CTRL_EXT_RPU (1 << 4) +#define TUSB_PHY_OTG_CTRL_PWR_GOOD (1 << 3) +#define TUSB_PHY_OTG_CTRL_RESET (1 << 2) +#define TUSB_PHY_OTG_CTRL_SUSPENDM (1 << 1) +#define TUSB_PHY_OTG_CTRL_CLK_MODE (1 << 0) + +/* OTG status register */ +#define TUSB_DEV_OTG_STAT (TUSB_SYS_REG_BASE + 0x00c) +#define TUSB_DEV_OTG_STAT_PWR_CLK_GOOD (1 << 8) +#define TUSB_DEV_OTG_STAT_SESS_END (1 << 7) +#define TUSB_DEV_OTG_STAT_SESS_VALID (1 << 6) +#define TUSB_DEV_OTG_STAT_VBUS_VALID (1 << 5) +#define TUSB_DEV_OTG_STAT_VBUS_SENSE (1 << 4) +#define TUSB_DEV_OTG_STAT_ID_STATUS (1 << 3) +#define TUSB_DEV_OTG_STAT_HOST_DISCON (1 << 2) +#define TUSB_DEV_OTG_STAT_LINE_STATE (3 << 0) +#define TUSB_DEV_OTG_STAT_DP_ENABLE (1 << 1) +#define TUSB_DEV_OTG_STAT_DM_ENABLE (1 << 0) + +#define TUSB_DEV_OTG_TIMER (TUSB_SYS_REG_BASE + 0x010) +#define TUSB_DEV_OTG_TIMER_ENABLE (1 << 31) +#define TUSB_DEV_OTG_TIMER_VAL(v) ((v) & 0x07ffffff) +#define TUSB_PRCM_REV (TUSB_SYS_REG_BASE + 0x014) + +/* PRCM configuration register */ +#define TUSB_PRCM_CONF (TUSB_SYS_REG_BASE + 0x018) +#define TUSB_PRCM_CONF_SFW_CPEN (1 << 24) +#define TUSB_PRCM_CONF_SYS_CLKSEL(v) (((v) & 3) << 16) + +/* PRCM management register */ +#define TUSB_PRCM_MNGMT (TUSB_SYS_REG_BASE + 0x01c) +#define TUSB_PRCM_MNGMT_SRP_FIX_TMR(v) (((v) & 0xf) << 25) +#define TUSB_PRCM_MNGMT_SRP_FIX_EN (1 << 24) +#define TUSB_PRCM_MNGMT_VBUS_VAL_TMR(v) (((v) & 0xf) << 20) +#define TUSB_PRCM_MNGMT_VBUS_VAL_FLT_EN (1 << 19) +#define TUSB_PRCM_MNGMT_DFT_CLK_DIS (1 << 18) +#define TUSB_PRCM_MNGMT_VLYNQ_CLK_DIS (1 << 17) +#define TUSB_PRCM_MNGMT_OTG_SESS_END_EN (1 << 10) +#define TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN (1 << 9) +#define TUSB_PRCM_MNGMT_OTG_ID_PULLUP (1 << 8) +#define TUSB_PRCM_MNGMT_15_SW_EN (1 << 4) +#define TUSB_PRCM_MNGMT_33_SW_EN (1 << 3) +#define TUSB_PRCM_MNGMT_5V_CPEN (1 << 2) +#define TUSB_PRCM_MNGMT_PM_IDLE (1 << 1) +#define TUSB_PRCM_MNGMT_DEV_IDLE (1 << 0) + +/* Wake-up source clear and mask registers */ +#define TUSB_PRCM_WAKEUP_SOURCE (TUSB_SYS_REG_BASE + 0x020) +#define TUSB_PRCM_WAKEUP_CLEAR (TUSB_SYS_REG_BASE + 0x028) +#define TUSB_PRCM_WAKEUP_MASK (TUSB_SYS_REG_BASE + 0x02c) +#define TUSB_PRCM_WAKEUP_RESERVED_BITS (0xffffe << 13) +#define TUSB_PRCM_WGPIO_7 (1 << 12) +#define TUSB_PRCM_WGPIO_6 (1 << 11) +#define TUSB_PRCM_WGPIO_5 (1 << 10) +#define TUSB_PRCM_WGPIO_4 (1 << 9) +#define TUSB_PRCM_WGPIO_3 (1 << 8) +#define TUSB_PRCM_WGPIO_2 (1 << 7) +#define TUSB_PRCM_WGPIO_1 (1 << 6) +#define TUSB_PRCM_WGPIO_0 (1 << 5) +#define TUSB_PRCM_WHOSTDISCON (1 << 4) /* Host disconnect */ +#define TUSB_PRCM_WBUS (1 << 3) /* USB bus resume */ +#define TUSB_PRCM_WNORCS (1 << 2) /* NOR chip select */ +#define TUSB_PRCM_WVBUS (1 << 1) /* OTG PHY VBUS */ +#define TUSB_PRCM_WID (1 << 0) /* OTG PHY ID detect */ + +#define TUSB_PULLUP_1_CTRL (TUSB_SYS_REG_BASE + 0x030) +#define TUSB_PULLUP_2_CTRL (TUSB_SYS_REG_BASE + 0x034) +#define TUSB_INT_CTRL_REV (TUSB_SYS_REG_BASE + 0x038) +#define TUSB_INT_CTRL_CONF (TUSB_SYS_REG_BASE + 0x03c) +#define TUSB_USBIP_INT_SRC (TUSB_SYS_REG_BASE + 0x040) +#define TUSB_USBIP_INT_SET (TUSB_SYS_REG_BASE + 0x044) +#define TUSB_USBIP_INT_CLEAR (TUSB_SYS_REG_BASE + 0x048) +#define TUSB_USBIP_INT_MASK (TUSB_SYS_REG_BASE + 0x04c) +#define TUSB_DMA_INT_SRC (TUSB_SYS_REG_BASE + 0x050) +#define TUSB_DMA_INT_SET (TUSB_SYS_REG_BASE + 0x054) +#define TUSB_DMA_INT_CLEAR (TUSB_SYS_REG_BASE + 0x058) +#define TUSB_DMA_INT_MASK (TUSB_SYS_REG_BASE + 0x05c) +#define TUSB_GPIO_INT_SRC (TUSB_SYS_REG_BASE + 0x060) +#define TUSB_GPIO_INT_SET (TUSB_SYS_REG_BASE + 0x064) +#define TUSB_GPIO_INT_CLEAR (TUSB_SYS_REG_BASE + 0x068) +#define TUSB_GPIO_INT_MASK (TUSB_SYS_REG_BASE + 0x06c) + +/* NOR flash interrupt source registers */ +#define TUSB_INT_SRC (TUSB_SYS_REG_BASE + 0x070) +#define TUSB_INT_SRC_SET (TUSB_SYS_REG_BASE + 0x074) +#define TUSB_INT_SRC_CLEAR (TUSB_SYS_REG_BASE + 0x078) +#define TUSB_INT_MASK (TUSB_SYS_REG_BASE + 0x07c) +#define TUSB_INT_SRC_TXRX_DMA_DONE (1 << 24) +#define TUSB_INT_SRC_USB_IP_CORE (1 << 17) +#define TUSB_INT_SRC_OTG_TIMEOUT (1 << 16) +#define TUSB_INT_SRC_VBUS_SENSE_CHNG (1 << 15) +#define TUSB_INT_SRC_ID_STATUS_CHNG (1 << 14) +#define TUSB_INT_SRC_DEV_WAKEUP (1 << 13) +#define TUSB_INT_SRC_DEV_READY (1 << 12) +#define TUSB_INT_SRC_USB_IP_TX (1 << 9) +#define TUSB_INT_SRC_USB_IP_RX (1 << 8) +#define TUSB_INT_SRC_USB_IP_VBUS_ERR (1 << 7) +#define TUSB_INT_SRC_USB_IP_VBUS_REQ (1 << 6) +#define TUSB_INT_SRC_USB_IP_DISCON (1 << 5) +#define TUSB_INT_SRC_USB_IP_CONN (1 << 4) +#define TUSB_INT_SRC_USB_IP_SOF (1 << 3) +#define TUSB_INT_SRC_USB_IP_RST_BABBLE (1 << 2) +#define TUSB_INT_SRC_USB_IP_RESUME (1 << 1) +#define TUSB_INT_SRC_USB_IP_SUSPEND (1 << 0) + +#define TUSB_GPIO_REV (TUSB_SYS_REG_BASE + 0x080) +#define TUSB_GPIO_CONF (TUSB_SYS_REG_BASE + 0x084) +#define TUSB_DMA_CTRL_REV (TUSB_SYS_REG_BASE + 0x100) +#define TUSB_DMA_REQ_CONF (TUSB_SYS_REG_BASE + 0x104) +#define TUSB_EP0_CONF (TUSB_SYS_REG_BASE + 0x108) +#define TUSB_EP_IN_SIZE (TUSB_SYS_REG_BASE + 0x10c) +#define TUSB_DMA_EP_MAP (TUSB_SYS_REG_BASE + 0x148) +#define TUSB_EP_OUT_SIZE (TUSB_SYS_REG_BASE + 0x14c) +#define TUSB_EP_MAX_PACKET_SIZE_OFFSET (TUSB_SYS_REG_BASE + 0x188) +#define TUSB_SCRATCH_PAD (TUSB_SYS_REG_BASE + 0x1c4) +#define TUSB_WAIT_COUNT (TUSB_SYS_REG_BASE + 0x1c8) +#define TUSB_PROD_TEST_RESET (TUSB_SYS_REG_BASE + 0x1d8) + +#define TUSB_DIDR1_LO (TUSB_SYS_REG_BASE + 0x1f8) +#define TUSB_DIDR1_HI (TUSB_SYS_REG_BASE + 0x1fc) + +/* Device System & Control register bitfields */ +#define TUSB_INT_CTRL_CONF_INT_RLCYC(v) (((v) & 0x7) << 18) +#define TUSB_INT_CTRL_CONF_INT_POLARITY (1 << 17) +#define TUSB_INT_CTRL_CONF_INT_MODE (1 << 16) +#define TUSB_GPIO_CONF_DMAREQ(v) (((v) & 0x3f) << 24) +#define TUSB_DMA_REQ_CONF_BURST_SIZE(v) (((v) & 3) << 26) +#define TUSB_DMA_REQ_CONF_DMA_RQ_EN(v) (((v) & 0x3f) << 20) +#define TUSB_DMA_REQ_CONF_DMA_RQ_ASR(v) (((v) & 0xf) << 16) +#define TUSB_EP0_CONFIG_SW_EN (1 << 8) +#define TUSB_EP0_CONFIG_DIR_TX (1 << 7) +#define TUSB_EP0_CONFIG_XFR_SIZE(v) ((v) & 0x7f) +#define TUSB_EP_CONFIG_SW_EN (1 << 31) +#define TUSB_EP_CONFIG_XFR_SIZE(v) ((v) & 0x7fffffff) +#define TUSB_PROD_TEST_RESET_VAL 0xa596 + +static void tusb_intr_update(TUSBState *s) +{ + if (s->control_config & TUSB_INT_CTRL_CONF_INT_POLARITY) + qemu_set_irq(s->irq, s->intr & ~s->mask & s->intr_ok); + else + qemu_set_irq(s->irq, (!(s->intr & ~s->mask)) & s->intr_ok); +} + +static void tusb_usbip_intr_update(TUSBState *s) +{ + /* TX interrupt in the MUSB */ + if (s->usbip_intr & 0x0000ffff & ~s->usbip_mask) + s->intr |= TUSB_INT_SRC_USB_IP_TX; + else + s->intr &= ~TUSB_INT_SRC_USB_IP_TX; + + /* RX interrupt in the MUSB */ + if (s->usbip_intr & 0xffff0000 & ~s->usbip_mask) + s->intr |= TUSB_INT_SRC_USB_IP_RX; + else + s->intr &= ~TUSB_INT_SRC_USB_IP_RX; + + /* XXX: What about TUSB_INT_SRC_USB_IP_CORE? */ + + tusb_intr_update(s); +} + +static void tusb_dma_intr_update(TUSBState *s) +{ + if (s->dma_intr & ~s->dma_mask) + s->intr |= TUSB_INT_SRC_TXRX_DMA_DONE; + else + s->intr &= ~TUSB_INT_SRC_TXRX_DMA_DONE; + + tusb_intr_update(s); +} + +static void tusb_gpio_intr_update(TUSBState *s) +{ + /* TODO: How is this signalled? */ +} + +static uint32_t tusb_async_readb(void *opaque, hwaddr addr) +{ + TUSBState *s = (TUSBState *) opaque; + + switch (addr & 0xfff) { + case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): + return musb_read[0](s->musb, addr & 0x1ff); + + case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): + return musb_read[0](s->musb, 0x20 + ((addr >> 3) & 0x3c)); + } + + printf("%s: unknown register at %03x\n", + __FUNCTION__, (int) (addr & 0xfff)); + return 0; +} + +static uint32_t tusb_async_readh(void *opaque, hwaddr addr) +{ + TUSBState *s = (TUSBState *) opaque; + + switch (addr & 0xfff) { + case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): + return musb_read[1](s->musb, addr & 0x1ff); + + case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): + return musb_read[1](s->musb, 0x20 + ((addr >> 3) & 0x3c)); + } + + printf("%s: unknown register at %03x\n", + __FUNCTION__, (int) (addr & 0xfff)); + return 0; +} + +static uint32_t tusb_async_readw(void *opaque, hwaddr addr) +{ + TUSBState *s = (TUSBState *) opaque; + int offset = addr & 0xfff; + int epnum; + uint32_t ret; + + switch (offset) { + case TUSB_DEV_CONF: + return s->dev_config; + + case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): + return musb_read[2](s->musb, offset & 0x1ff); + + case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): + return musb_read[2](s->musb, 0x20 + ((addr >> 3) & 0x3c)); + + case TUSB_PHY_OTG_CTRL_ENABLE: + case TUSB_PHY_OTG_CTRL: + return 0x00; /* TODO */ + + case TUSB_DEV_OTG_STAT: + ret = s->otg_status; +#if 0 + if (!(s->prcm_mngmt & TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN)) + ret &= ~TUSB_DEV_OTG_STAT_VBUS_VALID; +#endif + return ret; + case TUSB_DEV_OTG_TIMER: + return s->otg_timer_val; + + case TUSB_PRCM_REV: + return 0x20; + case TUSB_PRCM_CONF: + return s->prcm_config; + case TUSB_PRCM_MNGMT: + return s->prcm_mngmt; + case TUSB_PRCM_WAKEUP_SOURCE: + case TUSB_PRCM_WAKEUP_CLEAR: /* TODO: What does this one return? */ + return 0x00000000; + case TUSB_PRCM_WAKEUP_MASK: + return s->wkup_mask; + + case TUSB_PULLUP_1_CTRL: + return s->pullup[0]; + case TUSB_PULLUP_2_CTRL: + return s->pullup[1]; + + case TUSB_INT_CTRL_REV: + return 0x20; + case TUSB_INT_CTRL_CONF: + return s->control_config; + + case TUSB_USBIP_INT_SRC: + case TUSB_USBIP_INT_SET: /* TODO: What do these two return? */ + case TUSB_USBIP_INT_CLEAR: + return s->usbip_intr; + case TUSB_USBIP_INT_MASK: + return s->usbip_mask; + + case TUSB_DMA_INT_SRC: + case TUSB_DMA_INT_SET: /* TODO: What do these two return? */ + case TUSB_DMA_INT_CLEAR: + return s->dma_intr; + case TUSB_DMA_INT_MASK: + return s->dma_mask; + + case TUSB_GPIO_INT_SRC: /* TODO: What do these two return? */ + case TUSB_GPIO_INT_SET: + case TUSB_GPIO_INT_CLEAR: + return s->gpio_intr; + case TUSB_GPIO_INT_MASK: + return s->gpio_mask; + + case TUSB_INT_SRC: + case TUSB_INT_SRC_SET: /* TODO: What do these two return? */ + case TUSB_INT_SRC_CLEAR: + return s->intr; + case TUSB_INT_MASK: + return s->mask; + + case TUSB_GPIO_REV: + return 0x30; + case TUSB_GPIO_CONF: + return s->gpio_config; + + case TUSB_DMA_CTRL_REV: + return 0x30; + case TUSB_DMA_REQ_CONF: + return s->dma_config; + case TUSB_EP0_CONF: + return s->ep0_config; + case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b): + epnum = (offset - TUSB_EP_IN_SIZE) >> 2; + return s->tx_config[epnum]; + case TUSB_DMA_EP_MAP: + return s->dma_map; + case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b): + epnum = (offset - TUSB_EP_OUT_SIZE) >> 2; + return s->rx_config[epnum]; + case TUSB_EP_MAX_PACKET_SIZE_OFFSET ... + (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b): + return 0x00000000; /* TODO */ + case TUSB_WAIT_COUNT: + return 0x00; /* TODO */ + + case TUSB_SCRATCH_PAD: + return s->scratch; + + case TUSB_PROD_TEST_RESET: + return s->test_reset; + + /* DIE IDs */ + case TUSB_DIDR1_LO: + return 0xa9453c59; + case TUSB_DIDR1_HI: + return 0x54059adf; + } + + printf("%s: unknown register at %03x\n", __FUNCTION__, offset); + return 0; +} + +static void tusb_async_writeb(void *opaque, hwaddr addr, + uint32_t value) +{ + TUSBState *s = (TUSBState *) opaque; + + switch (addr & 0xfff) { + case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): + musb_write[0](s->musb, addr & 0x1ff, value); + break; + + case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): + musb_write[0](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); + break; + + default: + printf("%s: unknown register at %03x\n", + __FUNCTION__, (int) (addr & 0xfff)); + return; + } +} + +static void tusb_async_writeh(void *opaque, hwaddr addr, + uint32_t value) +{ + TUSBState *s = (TUSBState *) opaque; + + switch (addr & 0xfff) { + case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): + musb_write[1](s->musb, addr & 0x1ff, value); + break; + + case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): + musb_write[1](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); + break; + + default: + printf("%s: unknown register at %03x\n", + __FUNCTION__, (int) (addr & 0xfff)); + return; + } +} + +static void tusb_async_writew(void *opaque, hwaddr addr, + uint32_t value) +{ + TUSBState *s = (TUSBState *) opaque; + int offset = addr & 0xfff; + int epnum; + + switch (offset) { + case TUSB_VLYNQ_CTRL: + break; + + case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): + musb_write[2](s->musb, offset & 0x1ff, value); + break; + + case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): + musb_write[2](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); + break; + + case TUSB_DEV_CONF: + s->dev_config = value; + s->host_mode = (value & TUSB_DEV_CONF_USB_HOST_MODE); + if (value & TUSB_DEV_CONF_PROD_TEST_MODE) + hw_error("%s: Product Test mode not allowed\n", __FUNCTION__); + break; + + case TUSB_PHY_OTG_CTRL_ENABLE: + case TUSB_PHY_OTG_CTRL: + return; /* TODO */ + case TUSB_DEV_OTG_TIMER: + s->otg_timer_val = value; + if (value & TUSB_DEV_OTG_TIMER_ENABLE) + timer_mod(s->otg_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + muldiv64(TUSB_DEV_OTG_TIMER_VAL(value), + NANOSECONDS_PER_SECOND, TUSB_DEVCLOCK)); + else + timer_del(s->otg_timer); + break; + + case TUSB_PRCM_CONF: + s->prcm_config = value; + break; + case TUSB_PRCM_MNGMT: + s->prcm_mngmt = value; + break; + case TUSB_PRCM_WAKEUP_CLEAR: + break; + case TUSB_PRCM_WAKEUP_MASK: + s->wkup_mask = value; + break; + + case TUSB_PULLUP_1_CTRL: + s->pullup[0] = value; + break; + case TUSB_PULLUP_2_CTRL: + s->pullup[1] = value; + break; + case TUSB_INT_CTRL_CONF: + s->control_config = value; + tusb_intr_update(s); + break; + + case TUSB_USBIP_INT_SET: + s->usbip_intr |= value; + tusb_usbip_intr_update(s); + break; + case TUSB_USBIP_INT_CLEAR: + s->usbip_intr &= ~value; + tusb_usbip_intr_update(s); + musb_core_intr_clear(s->musb, ~value); + break; + case TUSB_USBIP_INT_MASK: + s->usbip_mask = value; + tusb_usbip_intr_update(s); + break; + + case TUSB_DMA_INT_SET: + s->dma_intr |= value; + tusb_dma_intr_update(s); + break; + case TUSB_DMA_INT_CLEAR: + s->dma_intr &= ~value; + tusb_dma_intr_update(s); + break; + case TUSB_DMA_INT_MASK: + s->dma_mask = value; + tusb_dma_intr_update(s); + break; + + case TUSB_GPIO_INT_SET: + s->gpio_intr |= value; + tusb_gpio_intr_update(s); + break; + case TUSB_GPIO_INT_CLEAR: + s->gpio_intr &= ~value; + tusb_gpio_intr_update(s); + break; + case TUSB_GPIO_INT_MASK: + s->gpio_mask = value; + tusb_gpio_intr_update(s); + break; + + case TUSB_INT_SRC_SET: + s->intr |= value; + tusb_intr_update(s); + break; + case TUSB_INT_SRC_CLEAR: + s->intr &= ~value; + tusb_intr_update(s); + break; + case TUSB_INT_MASK: + s->mask = value; + tusb_intr_update(s); + break; + + case TUSB_GPIO_CONF: + s->gpio_config = value; + break; + case TUSB_DMA_REQ_CONF: + s->dma_config = value; + break; + case TUSB_EP0_CONF: + s->ep0_config = value & 0x1ff; + musb_set_size(s->musb, 0, TUSB_EP0_CONFIG_XFR_SIZE(value), + value & TUSB_EP0_CONFIG_DIR_TX); + break; + case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b): + epnum = (offset - TUSB_EP_IN_SIZE) >> 2; + s->tx_config[epnum] = value; + musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 1); + break; + case TUSB_DMA_EP_MAP: + s->dma_map = value; + break; + case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b): + epnum = (offset - TUSB_EP_OUT_SIZE) >> 2; + s->rx_config[epnum] = value; + musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 0); + break; + case TUSB_EP_MAX_PACKET_SIZE_OFFSET ... + (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b): + return; /* TODO */ + case TUSB_WAIT_COUNT: + return; /* TODO */ + + case TUSB_SCRATCH_PAD: + s->scratch = value; + break; + + case TUSB_PROD_TEST_RESET: + s->test_reset = value; + break; + + default: + printf("%s: unknown register at %03x\n", __FUNCTION__, offset); + return; + } +} + +static const MemoryRegionOps tusb_async_ops = { + .old_mmio = { + .read = { tusb_async_readb, tusb_async_readh, tusb_async_readw, }, + .write = { tusb_async_writeb, tusb_async_writeh, tusb_async_writew, }, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void tusb_otg_tick(void *opaque) +{ + TUSBState *s = (TUSBState *) opaque; + + s->otg_timer_val = 0; + s->intr |= TUSB_INT_SRC_OTG_TIMEOUT; + tusb_intr_update(s); +} + +static void tusb_power_tick(void *opaque) +{ + TUSBState *s = (TUSBState *) opaque; + + if (s->power) { + s->intr_ok = ~0; + tusb_intr_update(s); + } +} + +static void tusb_musb_core_intr(void *opaque, int source, int level) +{ + TUSBState *s = (TUSBState *) opaque; + uint16_t otg_status = s->otg_status; + + switch (source) { + case musb_set_vbus: + if (level) + otg_status |= TUSB_DEV_OTG_STAT_VBUS_VALID; + else + otg_status &= ~TUSB_DEV_OTG_STAT_VBUS_VALID; + + /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_VBUS_DET_EN set? */ + /* XXX: only if TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN set? */ + if (s->otg_status != otg_status) { + s->otg_status = otg_status; + s->intr |= TUSB_INT_SRC_VBUS_SENSE_CHNG; + tusb_intr_update(s); + } + break; + + case musb_set_session: + /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_SESS_END_EN set? */ + /* XXX: only if TUSB_PRCM_MNGMT_OTG_SESS_END_EN set? */ + if (level) { + s->otg_status |= TUSB_DEV_OTG_STAT_SESS_VALID; + s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_END; + } else { + s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_VALID; + s->otg_status |= TUSB_DEV_OTG_STAT_SESS_END; + } + + /* XXX: some IRQ or anything? */ + break; + + case musb_irq_tx: + case musb_irq_rx: + s->usbip_intr = musb_core_intr_get(s->musb); + /* Fall through. */ + default: + if (level) + s->intr |= 1 << source; + else + s->intr &= ~(1 << source); + tusb_intr_update(s); + break; + } +} + +static void tusb6010_power(TUSBState *s, int on) +{ + if (!on) { + s->power = 0; + } else if (!s->power && on) { + s->power = 1; + /* Pull the interrupt down after TUSB6010 comes up. */ + s->intr_ok = 0; + tusb_intr_update(s); + timer_mod(s->pwr_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + NANOSECONDS_PER_SECOND / 2); + } +} + +static void tusb6010_irq(void *opaque, int source, int level) +{ + if (source) { + tusb_musb_core_intr(opaque, source - 1, level); + } else { + tusb6010_power(opaque, level); + } +} + +static void tusb6010_reset(DeviceState *dev) +{ + TUSBState *s = TUSB(dev); + int i; + + s->test_reset = TUSB_PROD_TEST_RESET_VAL; + s->host_mode = 0; + s->dev_config = 0; + s->otg_status = 0; /* !TUSB_DEV_OTG_STAT_ID_STATUS means host mode */ + s->power = 0; + s->mask = 0xffffffff; + s->intr = 0x00000000; + s->otg_timer_val = 0; + s->scratch = 0; + s->prcm_config = 0; + s->prcm_mngmt = 0; + s->intr_ok = 0; + s->usbip_intr = 0; + s->usbip_mask = 0; + s->gpio_intr = 0; + s->gpio_mask = 0; + s->gpio_config = 0; + s->dma_intr = 0; + s->dma_mask = 0; + s->dma_map = 0; + s->dma_config = 0; + s->ep0_config = 0; + s->wkup_mask = 0; + s->pullup[0] = s->pullup[1] = 0; + s->control_config = 0; + for (i = 0; i < 15; i++) { + s->rx_config[i] = s->tx_config[i] = 0; + } + musb_reset(s->musb); +} + +static int tusb6010_init(SysBusDevice *sbd) +{ + DeviceState *dev = DEVICE(sbd); + TUSBState *s = TUSB(dev); + + s->otg_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tusb_otg_tick, s); + s->pwr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tusb_power_tick, s); + memory_region_init_io(&s->iomem[1], OBJECT(s), &tusb_async_ops, s, + "tusb-async", UINT32_MAX); + sysbus_init_mmio(sbd, &s->iomem[0]); + sysbus_init_mmio(sbd, &s->iomem[1]); + sysbus_init_irq(sbd, &s->irq); + qdev_init_gpio_in(dev, tusb6010_irq, musb_irq_max + 1); + s->musb = musb_init(dev, 1); + return 0; +} + +static void tusb6010_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = tusb6010_init; + dc->reset = tusb6010_reset; +} + +static const TypeInfo tusb6010_info = { + .name = TYPE_TUSB6010, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(TUSBState), + .class_init = tusb6010_class_init, +}; + +static void tusb6010_register_types(void) +{ + type_register_static(&tusb6010_info); +} + +type_init(tusb6010_register_types) diff --git a/qemu/hw/vfio/Makefile.objs b/qemu/hw/vfio/Makefile.objs index d540c9d14..ceddbb8f9 100644 --- a/qemu/hw/vfio/Makefile.objs +++ b/qemu/hw/vfio/Makefile.objs @@ -1,6 +1,7 @@ ifeq ($(CONFIG_LINUX), y) obj-$(CONFIG_SOFTMMU) += common.o -obj-$(CONFIG_PCI) += pci.o +obj-$(CONFIG_PCI) += pci.o pci-quirks.o obj-$(CONFIG_SOFTMMU) += platform.o obj-$(CONFIG_SOFTMMU) += calxeda-xgmac.o +obj-$(CONFIG_SOFTMMU) += amd-xgbe.o endif diff --git a/qemu/hw/vfio/amd-xgbe.c b/qemu/hw/vfio/amd-xgbe.c new file mode 100644 index 000000000..2c60310cf --- /dev/null +++ b/qemu/hw/vfio/amd-xgbe.c @@ -0,0 +1,56 @@ +/* + * AMD XGBE VFIO device + * + * Copyright Linaro Limited, 2015 + * + * Authors: + * Eric Auger + * + * 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 "hw/vfio/vfio-amd-xgbe.h" + +static void amd_xgbe_realize(DeviceState *dev, Error **errp) +{ + VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev); + VFIOAmdXgbeDeviceClass *k = VFIO_AMD_XGBE_DEVICE_GET_CLASS(dev); + + vdev->compat = g_strdup("amd,xgbe-seattle-v1a"); + + k->parent_realize(dev, errp); +} + +static const VMStateDescription vfio_platform_amd_xgbe_vmstate = { + .name = TYPE_VFIO_AMD_XGBE, + .unmigratable = 1, +}; + +static void vfio_amd_xgbe_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VFIOAmdXgbeDeviceClass *vcxc = + VFIO_AMD_XGBE_DEVICE_CLASS(klass); + vcxc->parent_realize = dc->realize; + dc->realize = amd_xgbe_realize; + dc->desc = "VFIO AMD XGBE"; + dc->vmsd = &vfio_platform_amd_xgbe_vmstate; +} + +static const TypeInfo vfio_amd_xgbe_dev_info = { + .name = TYPE_VFIO_AMD_XGBE, + .parent = TYPE_VFIO_PLATFORM, + .instance_size = sizeof(VFIOAmdXgbeDevice), + .class_init = vfio_amd_xgbe_class_init, + .class_size = sizeof(VFIOAmdXgbeDeviceClass), +}; + +static void register_amd_xgbe_dev_type(void) +{ + type_register_static(&vfio_amd_xgbe_dev_info); +} + +type_init(register_amd_xgbe_dev_type) diff --git a/qemu/hw/vfio/calxeda-xgmac.c b/qemu/hw/vfio/calxeda-xgmac.c index eb914f0d0..bb15d588e 100644 --- a/qemu/hw/vfio/calxeda-xgmac.c +++ b/qemu/hw/vfio/calxeda-xgmac.c @@ -11,6 +11,7 @@ * */ +#include "qemu/osdep.h" #include "hw/vfio/vfio-calxeda-xgmac.h" static void calxeda_xgmac_realize(DeviceState *dev, Error **errp) diff --git a/qemu/hw/vfio/common.c b/qemu/hw/vfio/common.c index 85ee9b005..f27db36fb 100644 --- a/qemu/hw/vfio/common.c +++ b/qemu/hw/vfio/common.c @@ -18,6 +18,7 @@ * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com) */ +#include "qemu/osdep.h" #include #include #include @@ -226,7 +227,7 @@ static int vfio_dma_map(VFIOContainer *container, hwaddr iova, /* * Try the mapping, if it fails with EBUSY, unmap the region and try * again. This shouldn't be necessary, but we sometimes see it in - * the the VGA ROM space. + * the VGA ROM space. */ if (ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map) == 0 || (errno == EBUSY && vfio_dma_unmap(container, iova, size) == 0 && @@ -312,13 +313,17 @@ out: rcu_read_unlock(); } +static hwaddr vfio_container_granularity(VFIOContainer *container) +{ + return (hwaddr)1 << ctz64(container->iova_pgsizes); +} + static void vfio_listener_region_add(MemoryListener *listener, MemoryRegionSection *section) { - VFIOContainer *container = container_of(listener, VFIOContainer, - iommu_data.type1.listener); + VFIOContainer *container = container_of(listener, VFIOContainer, listener); hwaddr iova, end; - Int128 llend; + Int128 llend, llsize; void *vaddr; int ret; @@ -344,14 +349,22 @@ static void vfio_listener_region_add(MemoryListener *listener, if (int128_ge(int128_make64(iova), llend)) { return; } + end = int128_get64(int128_sub(llend, int128_one())); + + if ((iova < container->min_iova) || (end > container->max_iova)) { + error_report("vfio: IOMMU container %p can't map guest IOVA region" + " 0x%"HWADDR_PRIx"..0x%"HWADDR_PRIx, + container, iova, end); + ret = -EFAULT; + goto fail; + } memory_region_ref(section->mr); if (memory_region_is_iommu(section->mr)) { VFIOGuestIOMMU *giommu; - trace_vfio_listener_region_add_iommu(iova, - int128_get64(int128_sub(llend, int128_one()))); + trace_vfio_listener_region_add_iommu(iova, end); /* * FIXME: We should do some checking to see if the * capabilities of the host VFIO IOMMU are adequate to model @@ -362,65 +375,60 @@ static void vfio_listener_region_add(MemoryListener *listener, * would be the right place to wire that up (tell the KVM * device emulation the VFIO iommu handles to use). */ - /* - * This assumes that the guest IOMMU is empty of - * mappings at this point. - * - * One way of doing this is: - * 1. Avoid sharing IOMMUs between emulated devices or different - * IOMMU groups. - * 2. Implement VFIO_IOMMU_ENABLE in the host kernel to fail if - * there are some mappings in IOMMU. - * - * VFIO on SPAPR does that. Other IOMMU models may do that different, - * they must make sure there are no existing mappings or - * loop through existing mappings to map them into VFIO. - */ giommu = g_malloc0(sizeof(*giommu)); giommu->iommu = section->mr; giommu->container = container; giommu->n.notify = vfio_iommu_map_notify; QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next); + memory_region_register_iommu_notifier(giommu->iommu, &giommu->n); + memory_region_iommu_replay(giommu->iommu, &giommu->n, + vfio_container_granularity(container), + false); return; } /* Here we assume that memory_region_is_ram(section->mr)==true */ - end = int128_get64(llend); vaddr = memory_region_get_ram_ptr(section->mr) + section->offset_within_region + (iova - section->offset_within_address_space); - trace_vfio_listener_region_add_ram(iova, end - 1, vaddr); + trace_vfio_listener_region_add_ram(iova, end, vaddr); + + llsize = int128_sub(llend, int128_make64(iova)); - ret = vfio_dma_map(container, iova, end - iova, vaddr, section->readonly); + 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, end - iova, vaddr, ret); + container, iova, int128_get64(llsize), vaddr, ret); + goto fail; + } - /* - * On the initfn path, store the first error in the container so we - * can gracefully fail. Runtime, there's not much we can do other - * than throw a hardware error. - */ - if (!container->iommu_data.type1.initialized) { - if (!container->iommu_data.type1.error) { - container->iommu_data.type1.error = ret; - } - } else { - hw_error("vfio: DMA mapping failed, unable to continue"); + return; + +fail: + /* + * On the initfn path, store the first error in the container so we + * can gracefully fail. Runtime, there's not much we can do other + * than throw a hardware error. + */ + if (!container->initialized) { + if (!container->error) { + container->error = ret; } + } else { + hw_error("vfio: DMA mapping failed, unable to continue"); } } static void vfio_listener_region_del(MemoryListener *listener, MemoryRegionSection *section) { - VFIOContainer *container = container_of(listener, VFIOContainer, - iommu_data.type1.listener); + VFIOContainer *container = container_of(listener, VFIOContainer, listener); hwaddr iova, end; int ret; @@ -485,49 +493,165 @@ static const MemoryListener vfio_memory_listener = { static void vfio_listener_release(VFIOContainer *container) { - memory_listener_unregister(&container->iommu_data.type1.listener); + memory_listener_unregister(&container->listener); } -int vfio_mmap_region(Object *obj, VFIORegion *region, - MemoryRegion *mem, MemoryRegion *submem, - void **map, size_t size, off_t offset, - const char *name) +int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region, + int index, const char *name) { - int ret = 0; - VFIODevice *vbasedev = region->vbasedev; + struct vfio_region_info *info; + int ret; + + ret = vfio_get_region_info(vbasedev, index, &info); + if (ret) { + return ret; + } - if (vbasedev->allow_mmap && size && region->flags & - VFIO_REGION_INFO_FLAG_MMAP) { - int prot = 0; + region->vbasedev = vbasedev; + region->flags = info->flags; + region->size = info->size; + region->fd_offset = info->offset; + region->nr = index; - if (region->flags & VFIO_REGION_INFO_FLAG_READ) { - prot |= PROT_READ; + if (region->size) { + region->mem = g_new0(MemoryRegion, 1); + memory_region_init_io(region->mem, obj, &vfio_region_ops, + region, name, region->size); + + if (!vbasedev->no_mmap && + region->flags & VFIO_REGION_INFO_FLAG_MMAP && + !(region->size & ~qemu_real_host_page_mask)) { + + region->nr_mmaps = 1; + region->mmaps = g_new0(VFIOMmap, region->nr_mmaps); + + region->mmaps[0].offset = 0; + region->mmaps[0].size = region->size; } + } + + g_free(info); + + trace_vfio_region_setup(vbasedev->name, index, name, + region->flags, region->fd_offset, region->size); + return 0; +} + +int vfio_region_mmap(VFIORegion *region) +{ + int i, prot = 0; + char *name; + + if (!region->mem) { + return 0; + } - if (region->flags & VFIO_REGION_INFO_FLAG_WRITE) { - prot |= PROT_WRITE; + prot |= region->flags & VFIO_REGION_INFO_FLAG_READ ? PROT_READ : 0; + prot |= region->flags & VFIO_REGION_INFO_FLAG_WRITE ? PROT_WRITE : 0; + + for (i = 0; i < region->nr_mmaps; i++) { + region->mmaps[i].mmap = mmap(NULL, region->mmaps[i].size, prot, + MAP_SHARED, region->vbasedev->fd, + region->fd_offset + + region->mmaps[i].offset); + if (region->mmaps[i].mmap == MAP_FAILED) { + int ret = -errno; + + trace_vfio_region_mmap_fault(memory_region_name(region->mem), i, + region->fd_offset + + region->mmaps[i].offset, + region->fd_offset + + region->mmaps[i].offset + + region->mmaps[i].size - 1, ret); + + region->mmaps[i].mmap = NULL; + + for (i--; i >= 0; i--) { + memory_region_del_subregion(region->mem, ®ion->mmaps[i].mem); + munmap(region->mmaps[i].mmap, region->mmaps[i].size); + object_unparent(OBJECT(®ion->mmaps[i].mem)); + region->mmaps[i].mmap = NULL; + } + + return ret; } - *map = mmap(NULL, size, prot, MAP_SHARED, - vbasedev->fd, - region->fd_offset + offset); - if (*map == MAP_FAILED) { - *map = NULL; - ret = -errno; - goto empty_region; + name = g_strdup_printf("%s mmaps[%d]", + memory_region_name(region->mem), i); + memory_region_init_ram_ptr(®ion->mmaps[i].mem, + memory_region_owner(region->mem), + name, region->mmaps[i].size, + region->mmaps[i].mmap); + g_free(name); + memory_region_set_skip_dump(®ion->mmaps[i].mem); + memory_region_add_subregion(region->mem, region->mmaps[i].offset, + ®ion->mmaps[i].mem); + + trace_vfio_region_mmap(memory_region_name(®ion->mmaps[i].mem), + region->mmaps[i].offset, + region->mmaps[i].offset + + region->mmaps[i].size - 1); + } + + return 0; +} + +void vfio_region_exit(VFIORegion *region) +{ + int i; + + if (!region->mem) { + return; + } + + for (i = 0; i < region->nr_mmaps; i++) { + if (region->mmaps[i].mmap) { + memory_region_del_subregion(region->mem, ®ion->mmaps[i].mem); } + } - memory_region_init_ram_ptr(submem, obj, name, size, *map); - memory_region_set_skip_dump(submem); - } else { -empty_region: - /* Create a zero sized sub-region to make cleanup easy. */ - memory_region_init(submem, obj, name, 0); + trace_vfio_region_exit(region->vbasedev->name, region->nr); +} + +void vfio_region_finalize(VFIORegion *region) +{ + int i; + + if (!region->mem) { + return; } - memory_region_add_subregion(mem, offset, submem); + for (i = 0; i < region->nr_mmaps; i++) { + if (region->mmaps[i].mmap) { + munmap(region->mmaps[i].mmap, region->mmaps[i].size); + object_unparent(OBJECT(®ion->mmaps[i].mem)); + } + } - return ret; + object_unparent(OBJECT(region->mem)); + + g_free(region->mem); + g_free(region->mmaps); + + trace_vfio_region_finalize(region->vbasedev->name, region->nr); +} + +void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled) +{ + int i; + + if (!region->mem) { + return; + } + + for (i = 0; i < region->nr_mmaps; i++) { + if (region->mmaps[i].mmap) { + memory_region_set_enabled(®ion->mmaps[i].mem, enabled); + } + } + + trace_vfio_region_mmaps_set_enabled(memory_region_name(region->mem), + enabled); } void vfio_reset_handler(void *opaque) @@ -668,6 +792,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as) if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU) || ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1v2_IOMMU)) { bool v2 = !!ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1v2_IOMMU); + struct vfio_iommu_type1_info info; ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd); if (ret) { @@ -684,21 +809,27 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as) goto free_container_exit; } - container->iommu_data.type1.listener = vfio_memory_listener; - container->iommu_data.release = vfio_listener_release; - - memory_listener_register(&container->iommu_data.type1.listener, - container->space->as); - - if (container->iommu_data.type1.error) { - ret = container->iommu_data.type1.error; - error_report("vfio: memory listener initialization failed for container"); - goto listener_release_exit; + /* + * FIXME: This assumes that a Type1 IOMMU can map any 64-bit + * IOVA whatsoever. That's not actually true, but the current + * kernel interface doesn't tell us what it can map, and the + * existing Type1 IOMMUs generally support any IOVA we're + * going to actually try in practice. + */ + container->min_iova = 0; + container->max_iova = (hwaddr)-1; + + /* Assume just 4K IOVA page size */ + container->iova_pgsizes = 0x1000; + info.argsz = sizeof(info); + ret = ioctl(fd, VFIO_IOMMU_GET_INFO, &info); + /* Ignore errors */ + if ((ret == 0) && (info.flags & VFIO_IOMMU_INFO_PGSIZES)) { + container->iova_pgsizes = info.iova_pgsizes; } - - container->iommu_data.type1.initialized = true; - } else if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_IOMMU)) { + struct vfio_iommu_spapr_tce_info info; + ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd); if (ret) { error_report("vfio: failed to set group container: %m"); @@ -724,18 +855,41 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as) goto free_container_exit; } - container->iommu_data.type1.listener = vfio_memory_listener; - container->iommu_data.release = vfio_listener_release; - - memory_listener_register(&container->iommu_data.type1.listener, - container->space->as); + /* + * This only considers the host IOMMU's 32-bit window. At + * some point we need to add support for the optional 64-bit + * window and dynamic windows + */ + info.argsz = sizeof(info); + ret = ioctl(fd, VFIO_IOMMU_SPAPR_TCE_GET_INFO, &info); + if (ret) { + error_report("vfio: VFIO_IOMMU_SPAPR_TCE_GET_INFO failed: %m"); + ret = -errno; + goto free_container_exit; + } + container->min_iova = info.dma32_window_start; + container->max_iova = container->min_iova + info.dma32_window_size - 1; + /* Assume just 4K IOVA pages for now */ + container->iova_pgsizes = 0x1000; } else { error_report("vfio: No available IOMMU models"); ret = -EINVAL; goto free_container_exit; } + container->listener = vfio_memory_listener; + + memory_listener_register(&container->listener, container->space->as); + + if (container->error) { + ret = container->error; + error_report("vfio: memory listener initialization failed for container"); + goto listener_release_exit; + } + + container->initialized = true; + QLIST_INIT(&container->group_list); QLIST_INSERT_HEAD(&space->containers, container, next); @@ -774,9 +928,7 @@ static void vfio_disconnect_container(VFIOGroup *group) VFIOAddressSpace *space = container->space; VFIOGuestIOMMU *giommu, *tmp; - if (container->iommu_data.release) { - container->iommu_data.release(container); - } + vfio_listener_release(container); QLIST_REMOVE(container, next); QLIST_FOREACH_SAFE(giommu, &container->giommu_list, giommu_next, tmp) { @@ -926,47 +1078,115 @@ void vfio_put_base_device(VFIODevice *vbasedev) close(vbasedev->fd); } -static int vfio_container_do_ioctl(AddressSpace *as, int32_t groupid, - int req, void *param) +int vfio_get_region_info(VFIODevice *vbasedev, int index, + struct vfio_region_info **info) { - VFIOGroup *group; - VFIOContainer *container; - int ret = -1; + size_t argsz = sizeof(struct vfio_region_info); - group = vfio_get_group(groupid, as); - if (!group) { - error_report("vfio: group %d not registered", groupid); - return ret; + *info = g_malloc0(argsz); + + (*info)->index = index; + (*info)->argsz = argsz; + + if (ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, *info)) { + g_free(*info); + return -errno; } - container = group->container; - if (group->container) { - ret = ioctl(container->fd, req, param); - if (ret < 0) { - error_report("vfio: failed to ioctl %d to container: ret=%d, %s", - _IOC_NR(req) - VFIO_BASE, ret, strerror(errno)); - } + return 0; +} + +/* + * Interfaces for IBM EEH (Enhanced Error Handling) + */ +static bool vfio_eeh_container_ok(VFIOContainer *container) +{ + /* + * As of 2016-03-04 (linux-4.5) the host kernel EEH/VFIO + * implementation is broken if there are multiple groups in a + * container. The hardware works in units of Partitionable + * Endpoints (== IOMMU groups) and the EEH operations naively + * iterate across all groups in the container, without any logic + * to make sure the groups have their state synchronized. For + * certain operations (ENABLE) that might be ok, until an error + * occurs, but for others (GET_STATE) it's clearly broken. + */ + + /* + * XXX Once fixed kernels exist, test for them here + */ + + if (QLIST_EMPTY(&container->group_list)) { + return false; } - vfio_put_group(group); + if (QLIST_NEXT(QLIST_FIRST(&container->group_list), container_next)) { + return false; + } - return ret; + return true; } -int vfio_container_ioctl(AddressSpace *as, int32_t groupid, - int req, void *param) +static int vfio_eeh_container_op(VFIOContainer *container, uint32_t op) { - /* We allow only certain ioctls to the container */ - switch (req) { - case VFIO_CHECK_EXTENSION: - case VFIO_IOMMU_SPAPR_TCE_GET_INFO: - case VFIO_EEH_PE_OP: - break; - default: - /* Return an error on unknown requests */ - error_report("vfio: unsupported ioctl %X", req); - return -1; + struct vfio_eeh_pe_op pe_op = { + .argsz = sizeof(pe_op), + .op = op, + }; + int ret; + + if (!vfio_eeh_container_ok(container)) { + error_report("vfio/eeh: EEH_PE_OP 0x%x: " + "kernel requires a container with exactly one group", op); + return -EPERM; + } + + ret = ioctl(container->fd, VFIO_EEH_PE_OP, &pe_op); + if (ret < 0) { + error_report("vfio/eeh: EEH_PE_OP 0x%x failed: %m", op); + return -errno; + } + + return 0; +} + +static VFIOContainer *vfio_eeh_as_container(AddressSpace *as) +{ + VFIOAddressSpace *space = vfio_get_address_space(as); + VFIOContainer *container = NULL; + + if (QLIST_EMPTY(&space->containers)) { + /* No containers to act on */ + goto out; } - return vfio_container_do_ioctl(as, groupid, req, param); + container = QLIST_FIRST(&space->containers); + + if (QLIST_NEXT(container, next)) { + /* We don't yet have logic to synchronize EEH state across + * multiple containers */ + container = NULL; + goto out; + } + +out: + vfio_put_address_space(space); + return container; +} + +bool vfio_eeh_as_ok(AddressSpace *as) +{ + VFIOContainer *container = vfio_eeh_as_container(as); + + return (container != NULL) && vfio_eeh_container_ok(container); +} + +int vfio_eeh_as_op(AddressSpace *as, uint32_t op) +{ + VFIOContainer *container = vfio_eeh_as_container(as); + + if (!container) { + return -ENODEV; + } + return vfio_eeh_container_op(container, op); } diff --git a/qemu/hw/vfio/pci-quirks.c b/qemu/hw/vfio/pci-quirks.c new file mode 100644 index 000000000..49ecf1172 --- /dev/null +++ b/qemu/hw/vfio/pci-quirks.c @@ -0,0 +1,1205 @@ +/* + * device quirks for PCI devices + * + * Copyright Red Hat, Inc. 2012-2015 + * + * Authors: + * Alex Williamson + * + * 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 "pci.h" +#include "trace.h" +#include "qemu/range.h" + +/* Use uin32_t for vendor & device so PCI_ANY_ID expands and cannot match hw */ +static bool vfio_pci_is(VFIOPCIDevice *vdev, uint32_t vendor, uint32_t device) +{ + return (vendor == PCI_ANY_ID || vendor == vdev->vendor_id) && + (device == PCI_ANY_ID || device == vdev->device_id); +} + +static bool vfio_is_vga(VFIOPCIDevice *vdev) +{ + PCIDevice *pdev = &vdev->pdev; + uint16_t class = pci_get_word(pdev->config + PCI_CLASS_DEVICE); + + return class == PCI_CLASS_DISPLAY_VGA; +} + +/* + * List of device ids/vendor ids for which to disable + * option rom loading. This avoids the guest hangs during rom + * execution as noticed with the BCM 57810 card for lack of a + * more better way to handle such issues. + * The user can still override by specifying a romfile or + * rombar=1. + * Please see https://bugs.launchpad.net/qemu/+bug/1284874 + * for an analysis of the 57810 card hang. When adding + * a new vendor id/device id combination below, please also add + * your card/environment details and information that could + * help in debugging to the bug tracking this issue + */ +static const struct { + uint32_t vendor; + uint32_t device; +} romblacklist[] = { + { 0x14e4, 0x168e }, /* Broadcom BCM 57810 */ +}; + +bool vfio_blacklist_opt_rom(VFIOPCIDevice *vdev) +{ + int i; + + for (i = 0 ; i < ARRAY_SIZE(romblacklist); i++) { + if (vfio_pci_is(vdev, romblacklist[i].vendor, romblacklist[i].device)) { + trace_vfio_quirk_rom_blacklisted(vdev->vbasedev.name, + romblacklist[i].vendor, + romblacklist[i].device); + return true; + } + } + return false; +} + +/* + * Device specific region quirks (mostly backdoors to PCI config space) + */ + +/* + * The generic window quirks operate on an address and data register, + * vfio_generic_window_address_quirk handles the address register and + * vfio_generic_window_data_quirk handles the data register. These ops + * pass reads and writes through to hardware until a value matching the + * stored address match/mask is written. When this occurs, the data + * register access emulated PCI config space for the device rather than + * passing through accesses. This enables devices where PCI config space + * is accessible behind a window register to maintain the virtualization + * provided through vfio. + */ +typedef struct VFIOConfigWindowMatch { + uint32_t match; + uint32_t mask; +} VFIOConfigWindowMatch; + +typedef struct VFIOConfigWindowQuirk { + struct VFIOPCIDevice *vdev; + + uint32_t address_val; + + uint32_t address_offset; + uint32_t data_offset; + + bool window_enabled; + uint8_t bar; + + MemoryRegion *addr_mem; + MemoryRegion *data_mem; + + uint32_t nr_matches; + VFIOConfigWindowMatch matches[]; +} VFIOConfigWindowQuirk; + +static uint64_t vfio_generic_window_quirk_address_read(void *opaque, + hwaddr addr, + unsigned size) +{ + VFIOConfigWindowQuirk *window = opaque; + VFIOPCIDevice *vdev = window->vdev; + + return vfio_region_read(&vdev->bars[window->bar].region, + addr + window->address_offset, size); +} + +static void vfio_generic_window_quirk_address_write(void *opaque, hwaddr addr, + uint64_t data, + unsigned size) +{ + VFIOConfigWindowQuirk *window = opaque; + VFIOPCIDevice *vdev = window->vdev; + int i; + + window->window_enabled = false; + + vfio_region_write(&vdev->bars[window->bar].region, + addr + window->address_offset, data, size); + + for (i = 0; i < window->nr_matches; i++) { + if ((data & ~window->matches[i].mask) == window->matches[i].match) { + window->window_enabled = true; + window->address_val = data & window->matches[i].mask; + trace_vfio_quirk_generic_window_address_write(vdev->vbasedev.name, + memory_region_name(window->addr_mem), data); + break; + } + } +} + +static const MemoryRegionOps vfio_generic_window_address_quirk = { + .read = vfio_generic_window_quirk_address_read, + .write = vfio_generic_window_quirk_address_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t vfio_generic_window_quirk_data_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIOConfigWindowQuirk *window = opaque; + VFIOPCIDevice *vdev = window->vdev; + uint64_t data; + + /* Always read data reg, discard if window enabled */ + data = vfio_region_read(&vdev->bars[window->bar].region, + addr + window->data_offset, size); + + if (window->window_enabled) { + data = vfio_pci_read_config(&vdev->pdev, window->address_val, size); + trace_vfio_quirk_generic_window_data_read(vdev->vbasedev.name, + memory_region_name(window->data_mem), data); + } + + return data; +} + +static void vfio_generic_window_quirk_data_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOConfigWindowQuirk *window = opaque; + VFIOPCIDevice *vdev = window->vdev; + + if (window->window_enabled) { + vfio_pci_write_config(&vdev->pdev, window->address_val, data, size); + trace_vfio_quirk_generic_window_data_write(vdev->vbasedev.name, + memory_region_name(window->data_mem), data); + return; + } + + vfio_region_write(&vdev->bars[window->bar].region, + addr + window->data_offset, data, size); +} + +static const MemoryRegionOps vfio_generic_window_data_quirk = { + .read = vfio_generic_window_quirk_data_read, + .write = vfio_generic_window_quirk_data_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +/* + * The generic mirror quirk handles devices which expose PCI config space + * through a region within a BAR. When enabled, reads and writes are + * redirected through to emulated PCI config space. XXX if PCI config space + * used memory regions, this could just be an alias. + */ +typedef struct VFIOConfigMirrorQuirk { + struct VFIOPCIDevice *vdev; + uint32_t offset; + uint8_t bar; + MemoryRegion *mem; +} VFIOConfigMirrorQuirk; + +static uint64_t vfio_generic_quirk_mirror_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIOConfigMirrorQuirk *mirror = opaque; + VFIOPCIDevice *vdev = mirror->vdev; + uint64_t data; + + /* Read and discard in case the hardware cares */ + (void)vfio_region_read(&vdev->bars[mirror->bar].region, + addr + mirror->offset, size); + + data = vfio_pci_read_config(&vdev->pdev, addr, size); + trace_vfio_quirk_generic_mirror_read(vdev->vbasedev.name, + memory_region_name(mirror->mem), + addr, data); + return data; +} + +static void vfio_generic_quirk_mirror_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOConfigMirrorQuirk *mirror = opaque; + VFIOPCIDevice *vdev = mirror->vdev; + + vfio_pci_write_config(&vdev->pdev, addr, data, size); + trace_vfio_quirk_generic_mirror_write(vdev->vbasedev.name, + memory_region_name(mirror->mem), + addr, data); +} + +static const MemoryRegionOps vfio_generic_mirror_quirk = { + .read = vfio_generic_quirk_mirror_read, + .write = vfio_generic_quirk_mirror_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +/* Is range1 fully contained within range2? */ +static bool vfio_range_contained(uint64_t first1, uint64_t len1, + uint64_t first2, uint64_t len2) { + return (first1 >= first2 && first1 + len1 <= first2 + len2); +} + +#define PCI_VENDOR_ID_ATI 0x1002 + +/* + * Radeon HD cards (HD5450 & HD7850) report the upper byte of the I/O port BAR + * through VGA register 0x3c3. On newer cards, the I/O port BAR is always + * BAR4 (older cards like the X550 used BAR1, but we don't care to support + * those). Note that on bare metal, a read of 0x3c3 doesn't always return the + * I/O port BAR address. Originally this was coded to return the virtual BAR + * address only if the physical register read returns the actual BAR address, + * but users have reported greater success if we return the virtual address + * unconditionally. + */ +static uint64_t vfio_ati_3c3_quirk_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIOPCIDevice *vdev = opaque; + uint64_t data = vfio_pci_read_config(&vdev->pdev, + PCI_BASE_ADDRESS_4 + 1, size); + + trace_vfio_quirk_ati_3c3_read(vdev->vbasedev.name, data); + + return data; +} + +static const MemoryRegionOps vfio_ati_3c3_quirk = { + .read = vfio_ati_3c3_quirk_read, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void vfio_vga_probe_ati_3c3_quirk(VFIOPCIDevice *vdev) +{ + VFIOQuirk *quirk; + + /* + * As long as the BAR is >= 256 bytes it will be aligned such that the + * lower byte is always zero. Filter out anything else, if it exists. + */ + if (!vfio_pci_is(vdev, PCI_VENDOR_ID_ATI, PCI_ANY_ID) || + !vdev->bars[4].ioport || vdev->bars[4].region.size < 256) { + return; + } + + quirk = g_malloc0(sizeof(*quirk)); + quirk->mem = g_new0(MemoryRegion, 1); + quirk->nr_mem = 1; + + memory_region_init_io(quirk->mem, OBJECT(vdev), &vfio_ati_3c3_quirk, vdev, + "vfio-ati-3c3-quirk", 1); + memory_region_add_subregion(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem, + 3 /* offset 3 bytes from 0x3c0 */, quirk->mem); + + QLIST_INSERT_HEAD(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].quirks, + quirk, next); + + trace_vfio_quirk_ati_3c3_probe(vdev->vbasedev.name); +} + +/* + * Newer ATI/AMD devices, including HD5450 and HD7850, have a mirror to PCI + * config space through MMIO BAR2 at offset 0x4000. Nothing seems to access + * the MMIO space directly, but a window to this space is provided through + * I/O port BAR4. Offset 0x0 is the address register and offset 0x4 is the + * data register. When the address is programmed to a range of 0x4000-0x4fff + * PCI configuration space is available. Experimentation seems to indicate + * that read-only may be provided by hardware. + */ +static void vfio_probe_ati_bar4_quirk(VFIOPCIDevice *vdev, int nr) +{ + VFIOQuirk *quirk; + VFIOConfigWindowQuirk *window; + + /* This windows doesn't seem to be used except by legacy VGA code */ + if (!vfio_pci_is(vdev, PCI_VENDOR_ID_ATI, PCI_ANY_ID) || + !vdev->has_vga || nr != 4) { + return; + } + + quirk = g_malloc0(sizeof(*quirk)); + quirk->mem = g_new0(MemoryRegion, 2); + quirk->nr_mem = 2; + window = quirk->data = g_malloc0(sizeof(*window) + + sizeof(VFIOConfigWindowMatch)); + window->vdev = vdev; + window->address_offset = 0; + window->data_offset = 4; + window->nr_matches = 1; + window->matches[0].match = 0x4000; + window->matches[0].mask = vdev->config_size - 1; + window->bar = nr; + window->addr_mem = &quirk->mem[0]; + window->data_mem = &quirk->mem[1]; + + memory_region_init_io(window->addr_mem, OBJECT(vdev), + &vfio_generic_window_address_quirk, window, + "vfio-ati-bar4-window-address-quirk", 4); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + window->address_offset, + window->addr_mem, 1); + + memory_region_init_io(window->data_mem, OBJECT(vdev), + &vfio_generic_window_data_quirk, window, + "vfio-ati-bar4-window-data-quirk", 4); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + window->data_offset, + window->data_mem, 1); + + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); + + trace_vfio_quirk_ati_bar4_probe(vdev->vbasedev.name); +} + +/* + * Trap the BAR2 MMIO mirror to config space as well. + */ +static void vfio_probe_ati_bar2_quirk(VFIOPCIDevice *vdev, int nr) +{ + VFIOQuirk *quirk; + VFIOConfigMirrorQuirk *mirror; + + /* Only enable on newer devices where BAR2 is 64bit */ + if (!vfio_pci_is(vdev, PCI_VENDOR_ID_ATI, PCI_ANY_ID) || + !vdev->has_vga || nr != 2 || !vdev->bars[2].mem64) { + return; + } + + quirk = g_malloc0(sizeof(*quirk)); + mirror = quirk->data = g_malloc0(sizeof(*mirror)); + mirror->mem = quirk->mem = g_new0(MemoryRegion, 1); + quirk->nr_mem = 1; + mirror->vdev = vdev; + mirror->offset = 0x4000; + mirror->bar = nr; + + memory_region_init_io(mirror->mem, OBJECT(vdev), + &vfio_generic_mirror_quirk, mirror, + "vfio-ati-bar2-4000-quirk", PCI_CONFIG_SPACE_SIZE); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + mirror->offset, mirror->mem, 1); + + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); + + trace_vfio_quirk_ati_bar2_probe(vdev->vbasedev.name); +} + +/* + * Older ATI/AMD cards like the X550 have a similar window to that above. + * I/O port BAR1 provides a window to a mirror of PCI config space located + * in BAR2 at offset 0xf00. We don't care to support such older cards, but + * note it for future reference. + */ + +#define PCI_VENDOR_ID_NVIDIA 0x10de + +/* + * Nvidia has several different methods to get to config space, the + * nouveu project has several of these documented here: + * https://github.com/pathscale/envytools/tree/master/hwdocs + * + * The first quirk is actually not documented in envytools and is found + * on 10de:01d1 (NVIDIA Corporation G72 [GeForce 7300 LE]). This is an + * NV46 chipset. The backdoor uses the legacy VGA I/O ports to access + * the mirror of PCI config space found at BAR0 offset 0x1800. The access + * sequence first writes 0x338 to I/O port 0x3d4. The target offset is + * then written to 0x3d0. Finally 0x538 is written for a read and 0x738 + * is written for a write to 0x3d4. The BAR0 offset is then accessible + * through 0x3d0. This quirk doesn't seem to be necessary on newer cards + * that use the I/O port BAR5 window but it doesn't hurt to leave it. + */ +typedef enum {NONE = 0, SELECT, WINDOW, READ, WRITE} VFIONvidia3d0State; +static const char *nv3d0_states[] = { "NONE", "SELECT", + "WINDOW", "READ", "WRITE" }; + +typedef struct VFIONvidia3d0Quirk { + VFIOPCIDevice *vdev; + VFIONvidia3d0State state; + uint32_t offset; +} VFIONvidia3d0Quirk; + +static uint64_t vfio_nvidia_3d4_quirk_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIONvidia3d0Quirk *quirk = opaque; + VFIOPCIDevice *vdev = quirk->vdev; + + quirk->state = NONE; + + return vfio_vga_read(&vdev->vga->region[QEMU_PCI_VGA_IO_HI], + addr + 0x14, size); +} + +static void vfio_nvidia_3d4_quirk_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIONvidia3d0Quirk *quirk = opaque; + VFIOPCIDevice *vdev = quirk->vdev; + VFIONvidia3d0State old_state = quirk->state; + + quirk->state = NONE; + + switch (data) { + case 0x338: + if (old_state == NONE) { + quirk->state = SELECT; + trace_vfio_quirk_nvidia_3d0_state(vdev->vbasedev.name, + nv3d0_states[quirk->state]); + } + break; + case 0x538: + if (old_state == WINDOW) { + quirk->state = READ; + trace_vfio_quirk_nvidia_3d0_state(vdev->vbasedev.name, + nv3d0_states[quirk->state]); + } + break; + case 0x738: + if (old_state == WINDOW) { + quirk->state = WRITE; + trace_vfio_quirk_nvidia_3d0_state(vdev->vbasedev.name, + nv3d0_states[quirk->state]); + } + break; + } + + vfio_vga_write(&vdev->vga->region[QEMU_PCI_VGA_IO_HI], + addr + 0x14, data, size); +} + +static const MemoryRegionOps vfio_nvidia_3d4_quirk = { + .read = vfio_nvidia_3d4_quirk_read, + .write = vfio_nvidia_3d4_quirk_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t vfio_nvidia_3d0_quirk_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIONvidia3d0Quirk *quirk = opaque; + VFIOPCIDevice *vdev = quirk->vdev; + VFIONvidia3d0State old_state = quirk->state; + uint64_t data = vfio_vga_read(&vdev->vga->region[QEMU_PCI_VGA_IO_HI], + addr + 0x10, size); + + quirk->state = NONE; + + if (old_state == READ && + (quirk->offset & ~(PCI_CONFIG_SPACE_SIZE - 1)) == 0x1800) { + uint8_t offset = quirk->offset & (PCI_CONFIG_SPACE_SIZE - 1); + + data = vfio_pci_read_config(&vdev->pdev, offset, size); + trace_vfio_quirk_nvidia_3d0_read(vdev->vbasedev.name, + offset, size, data); + } + + return data; +} + +static void vfio_nvidia_3d0_quirk_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIONvidia3d0Quirk *quirk = opaque; + VFIOPCIDevice *vdev = quirk->vdev; + VFIONvidia3d0State old_state = quirk->state; + + quirk->state = NONE; + + if (old_state == SELECT) { + quirk->offset = (uint32_t)data; + quirk->state = WINDOW; + trace_vfio_quirk_nvidia_3d0_state(vdev->vbasedev.name, + nv3d0_states[quirk->state]); + } else if (old_state == WRITE) { + if ((quirk->offset & ~(PCI_CONFIG_SPACE_SIZE - 1)) == 0x1800) { + uint8_t offset = quirk->offset & (PCI_CONFIG_SPACE_SIZE - 1); + + vfio_pci_write_config(&vdev->pdev, offset, data, size); + trace_vfio_quirk_nvidia_3d0_write(vdev->vbasedev.name, + offset, data, size); + return; + } + } + + vfio_vga_write(&vdev->vga->region[QEMU_PCI_VGA_IO_HI], + addr + 0x10, data, size); +} + +static const MemoryRegionOps vfio_nvidia_3d0_quirk = { + .read = vfio_nvidia_3d0_quirk_read, + .write = vfio_nvidia_3d0_quirk_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void vfio_vga_probe_nvidia_3d0_quirk(VFIOPCIDevice *vdev) +{ + VFIOQuirk *quirk; + VFIONvidia3d0Quirk *data; + + if (!vfio_pci_is(vdev, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID) || + !vdev->bars[1].region.size) { + return; + } + + quirk = g_malloc0(sizeof(*quirk)); + quirk->data = data = g_malloc0(sizeof(*data)); + quirk->mem = g_new0(MemoryRegion, 2); + quirk->nr_mem = 2; + data->vdev = vdev; + + memory_region_init_io(&quirk->mem[0], OBJECT(vdev), &vfio_nvidia_3d4_quirk, + data, "vfio-nvidia-3d4-quirk", 2); + memory_region_add_subregion(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem, + 0x14 /* 0x3c0 + 0x14 */, &quirk->mem[0]); + + memory_region_init_io(&quirk->mem[1], OBJECT(vdev), &vfio_nvidia_3d0_quirk, + data, "vfio-nvidia-3d0-quirk", 2); + memory_region_add_subregion(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem, + 0x10 /* 0x3c0 + 0x10 */, &quirk->mem[1]); + + QLIST_INSERT_HEAD(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].quirks, + quirk, next); + + trace_vfio_quirk_nvidia_3d0_probe(vdev->vbasedev.name); +} + +/* + * The second quirk is documented in envytools. The I/O port BAR5 is just + * a set of address/data ports to the MMIO BARs. The BAR we care about is + * again BAR0. This backdoor is apparently a bit newer than the one above + * so we need to not only trap 256 bytes @0x1800, but all of PCI config + * space, including extended space is available at the 4k @0x88000. + */ +typedef struct VFIONvidiaBAR5Quirk { + uint32_t master; + uint32_t enable; + MemoryRegion *addr_mem; + MemoryRegion *data_mem; + bool enabled; + VFIOConfigWindowQuirk window; /* last for match data */ +} VFIONvidiaBAR5Quirk; + +static void vfio_nvidia_bar5_enable(VFIONvidiaBAR5Quirk *bar5) +{ + VFIOPCIDevice *vdev = bar5->window.vdev; + + if (((bar5->master & bar5->enable) & 0x1) == bar5->enabled) { + return; + } + + bar5->enabled = !bar5->enabled; + trace_vfio_quirk_nvidia_bar5_state(vdev->vbasedev.name, + bar5->enabled ? "Enable" : "Disable"); + memory_region_set_enabled(bar5->addr_mem, bar5->enabled); + memory_region_set_enabled(bar5->data_mem, bar5->enabled); +} + +static uint64_t vfio_nvidia_bar5_quirk_master_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIONvidiaBAR5Quirk *bar5 = opaque; + VFIOPCIDevice *vdev = bar5->window.vdev; + + return vfio_region_read(&vdev->bars[5].region, addr, size); +} + +static void vfio_nvidia_bar5_quirk_master_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIONvidiaBAR5Quirk *bar5 = opaque; + VFIOPCIDevice *vdev = bar5->window.vdev; + + vfio_region_write(&vdev->bars[5].region, addr, data, size); + + bar5->master = data; + vfio_nvidia_bar5_enable(bar5); +} + +static const MemoryRegionOps vfio_nvidia_bar5_quirk_master = { + .read = vfio_nvidia_bar5_quirk_master_read, + .write = vfio_nvidia_bar5_quirk_master_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t vfio_nvidia_bar5_quirk_enable_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIONvidiaBAR5Quirk *bar5 = opaque; + VFIOPCIDevice *vdev = bar5->window.vdev; + + return vfio_region_read(&vdev->bars[5].region, addr + 4, size); +} + +static void vfio_nvidia_bar5_quirk_enable_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIONvidiaBAR5Quirk *bar5 = opaque; + VFIOPCIDevice *vdev = bar5->window.vdev; + + vfio_region_write(&vdev->bars[5].region, addr + 4, data, size); + + bar5->enable = data; + vfio_nvidia_bar5_enable(bar5); +} + +static const MemoryRegionOps vfio_nvidia_bar5_quirk_enable = { + .read = vfio_nvidia_bar5_quirk_enable_read, + .write = vfio_nvidia_bar5_quirk_enable_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void vfio_probe_nvidia_bar5_quirk(VFIOPCIDevice *vdev, int nr) +{ + VFIOQuirk *quirk; + VFIONvidiaBAR5Quirk *bar5; + VFIOConfigWindowQuirk *window; + + if (!vfio_pci_is(vdev, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID) || + !vdev->has_vga || nr != 5) { + return; + } + + quirk = g_malloc0(sizeof(*quirk)); + quirk->mem = g_new0(MemoryRegion, 4); + quirk->nr_mem = 4; + bar5 = quirk->data = g_malloc0(sizeof(*bar5) + + (sizeof(VFIOConfigWindowMatch) * 2)); + window = &bar5->window; + + window->vdev = vdev; + window->address_offset = 0x8; + window->data_offset = 0xc; + window->nr_matches = 2; + window->matches[0].match = 0x1800; + window->matches[0].mask = PCI_CONFIG_SPACE_SIZE - 1; + window->matches[1].match = 0x88000; + window->matches[1].mask = vdev->config_size - 1; + window->bar = nr; + window->addr_mem = bar5->addr_mem = &quirk->mem[0]; + window->data_mem = bar5->data_mem = &quirk->mem[1]; + + memory_region_init_io(window->addr_mem, OBJECT(vdev), + &vfio_generic_window_address_quirk, window, + "vfio-nvidia-bar5-window-address-quirk", 4); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + window->address_offset, + window->addr_mem, 1); + memory_region_set_enabled(window->addr_mem, false); + + memory_region_init_io(window->data_mem, OBJECT(vdev), + &vfio_generic_window_data_quirk, window, + "vfio-nvidia-bar5-window-data-quirk", 4); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + window->data_offset, + window->data_mem, 1); + memory_region_set_enabled(window->data_mem, false); + + memory_region_init_io(&quirk->mem[2], OBJECT(vdev), + &vfio_nvidia_bar5_quirk_master, bar5, + "vfio-nvidia-bar5-master-quirk", 4); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + 0, &quirk->mem[2], 1); + + memory_region_init_io(&quirk->mem[3], OBJECT(vdev), + &vfio_nvidia_bar5_quirk_enable, bar5, + "vfio-nvidia-bar5-enable-quirk", 4); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + 4, &quirk->mem[3], 1); + + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); + + trace_vfio_quirk_nvidia_bar5_probe(vdev->vbasedev.name); +} + +/* + * Finally, BAR0 itself. We want to redirect any accesses to either + * 0x1800 or 0x88000 through the PCI config space access functions. + */ +static void vfio_nvidia_quirk_mirror_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOConfigMirrorQuirk *mirror = opaque; + VFIOPCIDevice *vdev = mirror->vdev; + PCIDevice *pdev = &vdev->pdev; + + vfio_generic_quirk_mirror_write(opaque, addr, data, size); + + /* + * Nvidia seems to acknowledge MSI interrupts by writing 0xff to the + * MSI capability ID register. Both the ID and next register are + * read-only, so we allow writes covering either of those to real hw. + */ + if ((pdev->cap_present & QEMU_PCI_CAP_MSI) && + vfio_range_contained(addr, size, pdev->msi_cap, PCI_MSI_FLAGS)) { + vfio_region_write(&vdev->bars[mirror->bar].region, + addr + mirror->offset, data, size); + trace_vfio_quirk_nvidia_bar0_msi_ack(vdev->vbasedev.name); + } +} + +static const MemoryRegionOps vfio_nvidia_mirror_quirk = { + .read = vfio_generic_quirk_mirror_read, + .write = vfio_nvidia_quirk_mirror_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void vfio_probe_nvidia_bar0_quirk(VFIOPCIDevice *vdev, int nr) +{ + VFIOQuirk *quirk; + VFIOConfigMirrorQuirk *mirror; + + if (!vfio_pci_is(vdev, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID) || + !vfio_is_vga(vdev) || nr != 0) { + return; + } + + quirk = g_malloc0(sizeof(*quirk)); + mirror = quirk->data = g_malloc0(sizeof(*mirror)); + mirror->mem = quirk->mem = g_new0(MemoryRegion, 1); + quirk->nr_mem = 1; + mirror->vdev = vdev; + mirror->offset = 0x88000; + mirror->bar = nr; + + memory_region_init_io(mirror->mem, OBJECT(vdev), + &vfio_nvidia_mirror_quirk, mirror, + "vfio-nvidia-bar0-88000-mirror-quirk", + vdev->config_size); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + mirror->offset, mirror->mem, 1); + + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); + + /* The 0x1800 offset mirror only seems to get used by legacy VGA */ + if (vdev->has_vga) { + quirk = g_malloc0(sizeof(*quirk)); + mirror = quirk->data = g_malloc0(sizeof(*mirror)); + mirror->mem = quirk->mem = g_new0(MemoryRegion, 1); + quirk->nr_mem = 1; + mirror->vdev = vdev; + mirror->offset = 0x1800; + mirror->bar = nr; + + memory_region_init_io(mirror->mem, OBJECT(vdev), + &vfio_nvidia_mirror_quirk, mirror, + "vfio-nvidia-bar0-1800-mirror-quirk", + PCI_CONFIG_SPACE_SIZE); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + mirror->offset, mirror->mem, 1); + + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); + } + + trace_vfio_quirk_nvidia_bar0_probe(vdev->vbasedev.name); +} + +/* + * TODO - Some Nvidia devices provide config access to their companion HDA + * device and even to their parent bridge via these config space mirrors. + * Add quirks for those regions. + */ + +#define PCI_VENDOR_ID_REALTEK 0x10ec + +/* + * RTL8168 devices have a backdoor that can access the MSI-X table. At BAR2 + * offset 0x70 there is a dword data register, offset 0x74 is a dword address + * register. According to the Linux r8169 driver, the MSI-X table is addressed + * when the "type" portion of the address register is set to 0x1. This appears + * to be bits 16:30. Bit 31 is both a write indicator and some sort of + * "address latched" indicator. Bits 12:15 are a mask field, which we can + * ignore because the MSI-X table should always be accessed as a dword (full + * mask). Bits 0:11 is offset within the type. + * + * Example trace: + * + * Read from MSI-X table offset 0 + * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x74, 0x1f000, 4) // store read addr + * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x74, 4) = 0x8001f000 // latch + * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x70, 4) = 0xfee00398 // read data + * + * Write 0xfee00000 to MSI-X table offset 0 + * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x70, 0xfee00000, 4) // write data + * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x74, 0x8001f000, 4) // do write + * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x74, 4) = 0x1f000 // complete + */ +typedef struct VFIOrtl8168Quirk { + VFIOPCIDevice *vdev; + uint32_t addr; + uint32_t data; + bool enabled; +} VFIOrtl8168Quirk; + +static uint64_t vfio_rtl8168_quirk_address_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIOrtl8168Quirk *rtl = opaque; + VFIOPCIDevice *vdev = rtl->vdev; + uint64_t data = vfio_region_read(&vdev->bars[2].region, addr + 0x74, size); + + if (rtl->enabled) { + data = rtl->addr ^ 0x80000000U; /* latch/complete */ + trace_vfio_quirk_rtl8168_fake_latch(vdev->vbasedev.name, data); + } + + return data; +} + +static void vfio_rtl8168_quirk_address_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOrtl8168Quirk *rtl = opaque; + VFIOPCIDevice *vdev = rtl->vdev; + + rtl->enabled = false; + + if ((data & 0x7fff0000) == 0x10000) { /* MSI-X table */ + rtl->enabled = true; + rtl->addr = (uint32_t)data; + + if (data & 0x80000000U) { /* Do write */ + if (vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX) { + hwaddr offset = data & 0xfff; + uint64_t val = rtl->data; + + trace_vfio_quirk_rtl8168_msix_write(vdev->vbasedev.name, + (uint16_t)offset, val); + + /* Write to the proper guest MSI-X table instead */ + memory_region_dispatch_write(&vdev->pdev.msix_table_mmio, + offset, val, size, + MEMTXATTRS_UNSPECIFIED); + } + return; /* Do not write guest MSI-X data to hardware */ + } + } + + vfio_region_write(&vdev->bars[2].region, addr + 0x74, data, size); +} + +static const MemoryRegionOps vfio_rtl_address_quirk = { + .read = vfio_rtl8168_quirk_address_read, + .write = vfio_rtl8168_quirk_address_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t vfio_rtl8168_quirk_data_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIOrtl8168Quirk *rtl = opaque; + VFIOPCIDevice *vdev = rtl->vdev; + uint64_t data = vfio_region_read(&vdev->bars[2].region, addr + 0x74, size); + + if (rtl->enabled && (vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX)) { + hwaddr offset = rtl->addr & 0xfff; + memory_region_dispatch_read(&vdev->pdev.msix_table_mmio, offset, + &data, size, MEMTXATTRS_UNSPECIFIED); + trace_vfio_quirk_rtl8168_msix_read(vdev->vbasedev.name, offset, data); + } + + return data; +} + +static void vfio_rtl8168_quirk_data_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOrtl8168Quirk *rtl = opaque; + VFIOPCIDevice *vdev = rtl->vdev; + + rtl->data = (uint32_t)data; + + vfio_region_write(&vdev->bars[2].region, addr + 0x70, data, size); +} + +static const MemoryRegionOps vfio_rtl_data_quirk = { + .read = vfio_rtl8168_quirk_data_read, + .write = vfio_rtl8168_quirk_data_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr) +{ + VFIOQuirk *quirk; + VFIOrtl8168Quirk *rtl; + + if (!vfio_pci_is(vdev, PCI_VENDOR_ID_REALTEK, 0x8168) || nr != 2) { + return; + } + + quirk = g_malloc0(sizeof(*quirk)); + quirk->mem = g_new0(MemoryRegion, 2); + quirk->nr_mem = 2; + quirk->data = rtl = g_malloc0(sizeof(*rtl)); + rtl->vdev = vdev; + + memory_region_init_io(&quirk->mem[0], OBJECT(vdev), + &vfio_rtl_address_quirk, rtl, + "vfio-rtl8168-window-address-quirk", 4); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + 0x74, &quirk->mem[0], 1); + + memory_region_init_io(&quirk->mem[1], OBJECT(vdev), + &vfio_rtl_data_quirk, rtl, + "vfio-rtl8168-window-data-quirk", 4); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + 0x70, &quirk->mem[1], 1); + + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); + + trace_vfio_quirk_rtl8168_probe(vdev->vbasedev.name); +} + +/* + * Common quirk probe entry points. + */ +void vfio_vga_quirk_setup(VFIOPCIDevice *vdev) +{ + vfio_vga_probe_ati_3c3_quirk(vdev); + vfio_vga_probe_nvidia_3d0_quirk(vdev); +} + +void vfio_vga_quirk_exit(VFIOPCIDevice *vdev) +{ + VFIOQuirk *quirk; + int i, j; + + for (i = 0; i < ARRAY_SIZE(vdev->vga->region); i++) { + QLIST_FOREACH(quirk, &vdev->vga->region[i].quirks, next) { + for (j = 0; j < quirk->nr_mem; j++) { + memory_region_del_subregion(&vdev->vga->region[i].mem, + &quirk->mem[j]); + } + } + } +} + +void vfio_vga_quirk_finalize(VFIOPCIDevice *vdev) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(vdev->vga->region); i++) { + while (!QLIST_EMPTY(&vdev->vga->region[i].quirks)) { + VFIOQuirk *quirk = QLIST_FIRST(&vdev->vga->region[i].quirks); + QLIST_REMOVE(quirk, next); + for (j = 0; j < quirk->nr_mem; j++) { + object_unparent(OBJECT(&quirk->mem[j])); + } + g_free(quirk->mem); + g_free(quirk->data); + g_free(quirk); + } + } +} + +void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr) +{ + vfio_probe_ati_bar4_quirk(vdev, nr); + vfio_probe_ati_bar2_quirk(vdev, nr); + vfio_probe_nvidia_bar5_quirk(vdev, nr); + vfio_probe_nvidia_bar0_quirk(vdev, nr); + vfio_probe_rtl8168_bar2_quirk(vdev, nr); +} + +void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr) +{ + VFIOBAR *bar = &vdev->bars[nr]; + VFIOQuirk *quirk; + int i; + + QLIST_FOREACH(quirk, &bar->quirks, next) { + for (i = 0; i < quirk->nr_mem; i++) { + memory_region_del_subregion(bar->region.mem, &quirk->mem[i]); + } + } +} + +void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr) +{ + VFIOBAR *bar = &vdev->bars[nr]; + int i; + + while (!QLIST_EMPTY(&bar->quirks)) { + VFIOQuirk *quirk = QLIST_FIRST(&bar->quirks); + QLIST_REMOVE(quirk, next); + for (i = 0; i < quirk->nr_mem; i++) { + object_unparent(OBJECT(&quirk->mem[i])); + } + g_free(quirk->mem); + g_free(quirk->data); + g_free(quirk); + } +} + +/* + * Reset quirks + */ + +/* + * AMD Radeon PCI config reset, based on Linux: + * drivers/gpu/drm/radeon/ci_smc.c:ci_is_smc_running() + * drivers/gpu/drm/radeon/radeon_device.c:radeon_pci_config_reset + * drivers/gpu/drm/radeon/ci_smc.c:ci_reset_smc() + * drivers/gpu/drm/radeon/ci_smc.c:ci_stop_smc_clock() + * IDs: include/drm/drm_pciids.h + * Registers: http://cgit.freedesktop.org/~agd5f/linux/commit/?id=4e2aa447f6f0 + * + * Bonaire and Hawaii GPUs do not respond to a bus reset. This is a bug in the + * hardware that should be fixed on future ASICs. The symptom of this is that + * once the accerlated driver loads, Windows guests will bsod on subsequent + * attmpts to load the driver, such as after VM reset or shutdown/restart. To + * work around this, we do an AMD specific PCI config reset, followed by an SMC + * reset. The PCI config reset only works if SMC firmware is running, so we + * have a dependency on the state of the device as to whether this reset will + * be effective. There are still cases where we won't be able to kick the + * device into working, but this greatly improves the usability overall. The + * config reset magic is relatively common on AMD GPUs, but the setup and SMC + * poking is largely ASIC specific. + */ +static bool vfio_radeon_smc_is_running(VFIOPCIDevice *vdev) +{ + uint32_t clk, pc_c; + + /* + * Registers 200h and 204h are index and data registers for accessing + * indirect configuration registers within the device. + */ + vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000004, 4); + clk = vfio_region_read(&vdev->bars[5].region, 0x204, 4); + vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000370, 4); + pc_c = vfio_region_read(&vdev->bars[5].region, 0x204, 4); + + return (!(clk & 1) && (0x20100 <= pc_c)); +} + +/* + * The scope of a config reset is controlled by a mode bit in the misc register + * and a fuse, exposed as a bit in another register. The fuse is the default + * (0 = GFX, 1 = whole GPU), the misc bit is a toggle, with the forumula + * scope = !(misc ^ fuse), where the resulting scope is defined the same as + * the fuse. A truth table therefore tells us that if misc == fuse, we need + * to flip the value of the bit in the misc register. + */ +static void vfio_radeon_set_gfx_only_reset(VFIOPCIDevice *vdev) +{ + uint32_t misc, fuse; + bool a, b; + + vfio_region_write(&vdev->bars[5].region, 0x200, 0xc00c0000, 4); + fuse = vfio_region_read(&vdev->bars[5].region, 0x204, 4); + b = fuse & 64; + + vfio_region_write(&vdev->bars[5].region, 0x200, 0xc0000010, 4); + misc = vfio_region_read(&vdev->bars[5].region, 0x204, 4); + a = misc & 2; + + if (a == b) { + vfio_region_write(&vdev->bars[5].region, 0x204, misc ^ 2, 4); + vfio_region_read(&vdev->bars[5].region, 0x204, 4); /* flush */ + } +} + +static int vfio_radeon_reset(VFIOPCIDevice *vdev) +{ + PCIDevice *pdev = &vdev->pdev; + int i, ret = 0; + uint32_t data; + + /* Defer to a kernel implemented reset */ + if (vdev->vbasedev.reset_works) { + trace_vfio_quirk_ati_bonaire_reset_skipped(vdev->vbasedev.name); + return -ENODEV; + } + + /* Enable only memory BAR access */ + vfio_pci_write_config(pdev, PCI_COMMAND, PCI_COMMAND_MEMORY, 2); + + /* Reset only works if SMC firmware is loaded and running */ + if (!vfio_radeon_smc_is_running(vdev)) { + ret = -EINVAL; + trace_vfio_quirk_ati_bonaire_reset_no_smc(vdev->vbasedev.name); + goto out; + } + + /* Make sure only the GFX function is reset */ + vfio_radeon_set_gfx_only_reset(vdev); + + /* AMD PCI config reset */ + vfio_pci_write_config(pdev, 0x7c, 0x39d5e86b, 4); + usleep(100); + + /* Read back the memory size to make sure we're out of reset */ + for (i = 0; i < 100000; i++) { + if (vfio_region_read(&vdev->bars[5].region, 0x5428, 4) != 0xffffffff) { + goto reset_smc; + } + usleep(1); + } + + trace_vfio_quirk_ati_bonaire_reset_timeout(vdev->vbasedev.name); + +reset_smc: + /* Reset SMC */ + vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000000, 4); + data = vfio_region_read(&vdev->bars[5].region, 0x204, 4); + data |= 1; + vfio_region_write(&vdev->bars[5].region, 0x204, data, 4); + + /* Disable SMC clock */ + vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000004, 4); + data = vfio_region_read(&vdev->bars[5].region, 0x204, 4); + data |= 1; + vfio_region_write(&vdev->bars[5].region, 0x204, data, 4); + + trace_vfio_quirk_ati_bonaire_reset_done(vdev->vbasedev.name); + +out: + /* Restore PCI command register */ + vfio_pci_write_config(pdev, PCI_COMMAND, 0, 2); + + return ret; +} + +void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev) +{ + switch (vdev->vendor_id) { + case 0x1002: + switch (vdev->device_id) { + /* Bonaire */ + case 0x6649: /* Bonaire [FirePro W5100] */ + case 0x6650: + case 0x6651: + case 0x6658: /* Bonaire XTX [Radeon R7 260X] */ + case 0x665c: /* Bonaire XT [Radeon HD 7790/8770 / R9 260 OEM] */ + case 0x665d: /* Bonaire [Radeon R7 200 Series] */ + /* Hawaii */ + case 0x67A0: /* Hawaii XT GL [FirePro W9100] */ + case 0x67A1: /* Hawaii PRO GL [FirePro W8100] */ + case 0x67A2: + case 0x67A8: + case 0x67A9: + case 0x67AA: + case 0x67B0: /* Hawaii XT [Radeon R9 290X] */ + case 0x67B1: /* Hawaii PRO [Radeon R9 290] */ + case 0x67B8: + case 0x67B9: + case 0x67BA: + case 0x67BE: + vdev->resetfn = vfio_radeon_reset; + trace_vfio_quirk_ati_bonaire_reset(vdev->vbasedev.name); + break; + } + break; + } +} diff --git a/qemu/hw/vfio/pci.c b/qemu/hw/vfio/pci.c index 4023d8e82..d091d8cf0 100644 --- a/qemu/hw/vfio/pci.c +++ b/qemu/hw/vfio/pci.c @@ -18,187 +18,24 @@ * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com) */ -#include +#include "qemu/osdep.h" #include #include #include -#include -#include -#include -#include "config.h" -#include "exec/address-spaces.h" -#include "exec/memory.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" -#include "hw/pci/pci.h" -#include "qemu-common.h" +#include "hw/pci/pci_bridge.h" #include "qemu/error-report.h" -#include "qemu/event_notifier.h" -#include "qemu/queue.h" #include "qemu/range.h" #include "sysemu/kvm.h" #include "sysemu/sysemu.h" +#include "pci.h" #include "trace.h" -#include "hw/vfio/vfio.h" -#include "hw/vfio/vfio-common.h" - -struct VFIOPCIDevice; - -typedef struct VFIOQuirk { - MemoryRegion mem; - struct VFIOPCIDevice *vdev; - QLIST_ENTRY(VFIOQuirk) next; - struct { - uint32_t base_offset:TARGET_PAGE_BITS; - uint32_t address_offset:TARGET_PAGE_BITS; - uint32_t address_size:3; - uint32_t bar:3; - - uint32_t address_match; - uint32_t address_mask; - - uint32_t address_val:TARGET_PAGE_BITS; - uint32_t data_offset:TARGET_PAGE_BITS; - uint32_t data_size:3; - - uint8_t flags; - uint8_t read_flags; - uint8_t write_flags; - } data; -} VFIOQuirk; - -typedef struct VFIOBAR { - VFIORegion region; - bool ioport; - bool mem64; - QLIST_HEAD(, VFIOQuirk) quirks; -} VFIOBAR; - -typedef struct VFIOVGARegion { - MemoryRegion mem; - off_t offset; - int nr; - QLIST_HEAD(, VFIOQuirk) quirks; -} VFIOVGARegion; - -typedef struct VFIOVGA { - off_t fd_offset; - int fd; - VFIOVGARegion region[QEMU_PCI_VGA_NUM_REGIONS]; -} VFIOVGA; - -typedef struct VFIOINTx { - bool pending; /* interrupt pending */ - bool kvm_accel; /* set when QEMU bypass through KVM enabled */ - uint8_t pin; /* which pin to pull for qemu_set_irq */ - EventNotifier interrupt; /* eventfd triggered on interrupt */ - EventNotifier unmask; /* eventfd for unmask on QEMU bypass */ - PCIINTxRoute route; /* routing info for QEMU bypass */ - uint32_t mmap_timeout; /* delay to re-enable mmaps after interrupt */ - QEMUTimer *mmap_timer; /* enable mmaps after periods w/o interrupts */ -} VFIOINTx; - -typedef struct VFIOMSIVector { - /* - * Two interrupt paths are configured per vector. The first, is only used - * for interrupts injected via QEMU. This is typically the non-accel path, - * but may also be used when we want QEMU to handle masking and pending - * bits. The KVM path bypasses QEMU and is therefore higher performance, - * but requires masking at the device. virq is used to track the MSI route - * through KVM, thus kvm_interrupt is only available when virq is set to a - * valid (>= 0) value. - */ - EventNotifier interrupt; - EventNotifier kvm_interrupt; - struct VFIOPCIDevice *vdev; /* back pointer to device */ - int virq; - bool use; -} VFIOMSIVector; - -enum { - VFIO_INT_NONE = 0, - VFIO_INT_INTx = 1, - VFIO_INT_MSI = 2, - VFIO_INT_MSIX = 3, -}; - -/* Cache of MSI-X setup plus extra mmap and memory region for split BAR map */ -typedef struct VFIOMSIXInfo { - uint8_t table_bar; - uint8_t pba_bar; - uint16_t entries; - uint32_t table_offset; - uint32_t pba_offset; - MemoryRegion mmap_mem; - void *mmap; -} VFIOMSIXInfo; - -typedef struct VFIOPCIDevice { - PCIDevice pdev; - VFIODevice vbasedev; - VFIOINTx intx; - unsigned int config_size; - uint8_t *emulated_config_bits; /* QEMU emulated bits, little-endian */ - off_t config_offset; /* Offset of config space region within device fd */ - unsigned int rom_size; - off_t rom_offset; /* Offset of ROM region within device fd */ - void *rom; - int msi_cap_size; - VFIOMSIVector *msi_vectors; - VFIOMSIXInfo *msix; - int nr_vectors; /* Number of MSI/MSIX vectors currently in use */ - int interrupt; /* Current interrupt type */ - VFIOBAR bars[PCI_NUM_REGIONS - 1]; /* No ROM */ - VFIOVGA vga; /* 0xa0000, 0x3b0, 0x3c0 */ - PCIHostDeviceAddress host; - EventNotifier err_notifier; - EventNotifier req_notifier; - int (*resetfn)(struct VFIOPCIDevice *); - uint32_t features; -#define VFIO_FEATURE_ENABLE_VGA_BIT 0 -#define VFIO_FEATURE_ENABLE_VGA (1 << VFIO_FEATURE_ENABLE_VGA_BIT) -#define VFIO_FEATURE_ENABLE_REQ_BIT 1 -#define VFIO_FEATURE_ENABLE_REQ (1 << VFIO_FEATURE_ENABLE_REQ_BIT) - int32_t bootindex; - uint8_t pm_cap; - bool has_vga; - bool pci_aer; - bool req_enabled; - bool has_flr; - bool has_pm_reset; - bool rom_read_failed; -} VFIOPCIDevice; - -typedef struct VFIORomBlacklistEntry { - uint16_t vendor_id; - uint16_t device_id; -} VFIORomBlacklistEntry; - -/* - * List of device ids/vendor ids for which to disable - * option rom loading. This avoids the guest hangs during rom - * execution as noticed with the BCM 57810 card for lack of a - * more better way to handle such issues. - * The user can still override by specifying a romfile or - * rombar=1. - * Please see https://bugs.launchpad.net/qemu/+bug/1284874 - * for an analysis of the 57810 card hang. When adding - * a new vendor id/device id combination below, please also add - * your card/environment details and information that could - * help in debugging to the bug tracking this issue - */ -static const VFIORomBlacklistEntry romblacklist[] = { - /* Broadcom BCM 57810 */ - { 0x14e4, 0x168e } -}; #define MSIX_CAP_LENGTH 12 static void vfio_disable_interrupts(VFIOPCIDevice *vdev); -static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len); -static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, - uint32_t val, int len); static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled); /* @@ -248,7 +85,7 @@ static void vfio_intx_interrupt(void *opaque) } } -static void vfio_eoi(VFIODevice *vbasedev) +static void vfio_intx_eoi(VFIODevice *vbasedev) { VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); @@ -256,14 +93,14 @@ static void vfio_eoi(VFIODevice *vbasedev) return; } - trace_vfio_eoi(vbasedev->name); + trace_vfio_intx_eoi(vbasedev->name); vdev->intx.pending = false; pci_irq_deassert(&vdev->pdev); vfio_unmask_single_irqindex(vbasedev, VFIO_PCI_INTX_IRQ_INDEX); } -static void vfio_enable_intx_kvm(VFIOPCIDevice *vdev) +static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev) { #ifdef CONFIG_KVM struct kvm_irqfd irqfd = { @@ -275,7 +112,7 @@ static void vfio_enable_intx_kvm(VFIOPCIDevice *vdev) int ret, argsz; int32_t *pfd; - if (!VFIO_ALLOW_KVM_INTX || !kvm_irqfds_enabled() || + if (vdev->no_kvm_intx || !kvm_irqfds_enabled() || vdev->intx.route.mode != PCI_INTX_ENABLED || !kvm_resamplefds_enabled()) { return; @@ -325,7 +162,7 @@ static void vfio_enable_intx_kvm(VFIOPCIDevice *vdev) vdev->intx.kvm_accel = true; - trace_vfio_enable_intx_kvm(vdev->vbasedev.name); + trace_vfio_intx_enable_kvm(vdev->vbasedev.name); return; @@ -340,7 +177,7 @@ fail: #endif } -static void vfio_disable_intx_kvm(VFIOPCIDevice *vdev) +static void vfio_intx_disable_kvm(VFIOPCIDevice *vdev) { #ifdef CONFIG_KVM struct kvm_irqfd irqfd = { @@ -377,11 +214,11 @@ static void vfio_disable_intx_kvm(VFIOPCIDevice *vdev) /* If we've missed an event, let it re-fire through QEMU */ vfio_unmask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); - trace_vfio_disable_intx_kvm(vdev->vbasedev.name); + trace_vfio_intx_disable_kvm(vdev->vbasedev.name); #endif } -static void vfio_update_irq(PCIDevice *pdev) +static void vfio_intx_update(PCIDevice *pdev) { VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); PCIINTxRoute route; @@ -396,10 +233,10 @@ static void vfio_update_irq(PCIDevice *pdev) return; /* Nothing changed */ } - trace_vfio_update_irq(vdev->vbasedev.name, - vdev->intx.route.irq, route.irq); + trace_vfio_intx_update(vdev->vbasedev.name, + vdev->intx.route.irq, route.irq); - vfio_disable_intx_kvm(vdev); + vfio_intx_disable_kvm(vdev); vdev->intx.route = route; @@ -407,13 +244,13 @@ static void vfio_update_irq(PCIDevice *pdev) return; } - vfio_enable_intx_kvm(vdev); + vfio_intx_enable_kvm(vdev); /* Re-enable the interrupt in cased we missed an EOI */ - vfio_eoi(&vdev->vbasedev); + vfio_intx_eoi(&vdev->vbasedev); } -static int vfio_enable_intx(VFIOPCIDevice *vdev) +static int vfio_intx_enable(VFIOPCIDevice *vdev) { uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1); int ret, argsz; @@ -468,21 +305,21 @@ static int vfio_enable_intx(VFIOPCIDevice *vdev) return -errno; } - vfio_enable_intx_kvm(vdev); + vfio_intx_enable_kvm(vdev); vdev->interrupt = VFIO_INT_INTx; - trace_vfio_enable_intx(vdev->vbasedev.name); + trace_vfio_intx_enable(vdev->vbasedev.name); return 0; } -static void vfio_disable_intx(VFIOPCIDevice *vdev) +static void vfio_intx_disable(VFIOPCIDevice *vdev) { int fd; timer_del(vdev->intx.mmap_timer); - vfio_disable_intx_kvm(vdev); + vfio_intx_disable_kvm(vdev); vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); vdev->intx.pending = false; pci_irq_deassert(&vdev->pdev); @@ -494,7 +331,7 @@ static void vfio_disable_intx(VFIOPCIDevice *vdev) vdev->interrupt = VFIO_INT_NONE; - trace_vfio_disable_intx(vdev->vbasedev.name); + trace_vfio_intx_disable(vdev->vbasedev.name); } /* @@ -504,33 +341,35 @@ static void vfio_msi_interrupt(void *opaque) { VFIOMSIVector *vector = opaque; VFIOPCIDevice *vdev = vector->vdev; + MSIMessage (*get_msg)(PCIDevice *dev, unsigned vector); + void (*notify)(PCIDevice *dev, unsigned vector); + MSIMessage msg; int nr = vector - vdev->msi_vectors; if (!event_notifier_test_and_clear(&vector->interrupt)) { return; } -#ifdef DEBUG_VFIO - MSIMessage msg; - if (vdev->interrupt == VFIO_INT_MSIX) { - msg = msix_get_message(&vdev->pdev, nr); + get_msg = msix_get_message; + notify = msix_notify; + + /* A masked vector firing needs to use the PBA, enable it */ + if (msix_is_masked(&vdev->pdev, nr)) { + set_bit(nr, vdev->msix->pending); + memory_region_set_enabled(&vdev->pdev.msix_pba_mmio, true); + trace_vfio_msix_pba_enable(vdev->vbasedev.name); + } } else if (vdev->interrupt == VFIO_INT_MSI) { - msg = msi_get_message(&vdev->pdev, nr); + get_msg = msi_get_message; + notify = msi_notify; } else { abort(); } + msg = get_msg(&vdev->pdev, nr); trace_vfio_msi_interrupt(vdev->vbasedev.name, nr, msg.address, msg.data); -#endif - - if (vdev->interrupt == VFIO_INT_MSIX) { - msix_notify(&vdev->pdev, nr); - } else if (vdev->interrupt == VFIO_INT_MSI) { - msi_notify(&vdev->pdev, nr); - } else { - error_report("vfio: MSI interrupt receieved, but not enabled?"); - } + notify(&vdev->pdev, nr); } static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix) @@ -577,13 +416,12 @@ static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix) return ret; } -static void vfio_add_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage *msg, - bool msix) +static void vfio_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, + MSIMessage *msg, bool msix) { int virq; - if ((msix && !VFIO_ALLOW_KVM_MSIX) || - (!msix && !VFIO_ALLOW_KVM_MSI) || !msg) { + if ((msix && vdev->no_kvm_msix) || (!msix && vdev->no_kvm_msi) || !msg) { return; } @@ -591,7 +429,7 @@ static void vfio_add_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage *msg, return; } - virq = kvm_irqchip_add_msi_route(kvm_state, *msg); + virq = kvm_irqchip_add_msi_route(kvm_state, *msg, &vdev->pdev); if (virq < 0) { event_notifier_cleanup(&vector->kvm_interrupt); return; @@ -616,9 +454,10 @@ static void vfio_remove_kvm_msi_virq(VFIOMSIVector *vector) event_notifier_cleanup(&vector->kvm_interrupt); } -static void vfio_update_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage msg) +static void vfio_update_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage msg, + PCIDevice *pdev) { - kvm_irqchip_update_msi_route(kvm_state, vector->virq, msg); + kvm_irqchip_update_msi_route(kvm_state, vector->virq, msg, pdev); } static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, @@ -653,10 +492,10 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, if (!msg) { vfio_remove_kvm_msi_virq(vector); } else { - vfio_update_kvm_msi_virq(vector, *msg); + vfio_update_kvm_msi_virq(vector, *msg, pdev); } } else { - vfio_add_kvm_msi_virq(vector, msg, true); + vfio_add_kvm_msi_virq(vdev, vector, msg, true); } /* @@ -700,6 +539,14 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, } } + /* Disable PBA emulation when nothing more is pending. */ + clear_bit(nr, vdev->msix->pending); + if (find_first_bit(vdev->msix->pending, + vdev->nr_vectors) == vdev->nr_vectors) { + memory_region_set_enabled(&vdev->pdev.msix_pba_mmio, false); + trace_vfio_msix_pba_disable(vdev->vbasedev.name); + } + return 0; } @@ -748,11 +595,11 @@ static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr) } } -static void vfio_enable_msix(VFIOPCIDevice *vdev) +static void vfio_msix_enable(VFIOPCIDevice *vdev) { vfio_disable_interrupts(vdev); - vdev->msi_vectors = g_malloc0(vdev->msix->entries * sizeof(VFIOMSIVector)); + vdev->msi_vectors = g_new0(VFIOMSIVector, vdev->msix->entries); vdev->interrupt = VFIO_INT_MSIX; @@ -777,10 +624,10 @@ static void vfio_enable_msix(VFIOPCIDevice *vdev) error_report("vfio: msix_set_vector_notifiers failed"); } - trace_vfio_enable_msix(vdev->vbasedev.name); + trace_vfio_msix_enable(vdev->vbasedev.name); } -static void vfio_enable_msi(VFIOPCIDevice *vdev) +static void vfio_msi_enable(VFIOPCIDevice *vdev) { int ret, i; @@ -788,7 +635,7 @@ static void vfio_enable_msi(VFIOPCIDevice *vdev) vdev->nr_vectors = msi_nr_vectors_allocated(&vdev->pdev); retry: - vdev->msi_vectors = g_malloc0(vdev->nr_vectors * sizeof(VFIOMSIVector)); + vdev->msi_vectors = g_new0(VFIOMSIVector, vdev->nr_vectors); for (i = 0; i < vdev->nr_vectors; i++) { VFIOMSIVector *vector = &vdev->msi_vectors[i]; @@ -809,7 +656,7 @@ retry: * Attempt to enable route through KVM irqchip, * default to userspace handling if unavailable. */ - vfio_add_kvm_msi_virq(vector, &msg, false); + vfio_add_kvm_msi_virq(vdev, vector, &msg, false); } /* Set interrupt type prior to possible interrupts */ @@ -853,10 +700,10 @@ retry: return; } - trace_vfio_enable_msi(vdev->vbasedev.name, vdev->nr_vectors); + trace_vfio_msi_enable(vdev->vbasedev.name, vdev->nr_vectors); } -static void vfio_disable_msi_common(VFIOPCIDevice *vdev) +static void vfio_msi_disable_common(VFIOPCIDevice *vdev) { int i; @@ -877,10 +724,10 @@ static void vfio_disable_msi_common(VFIOPCIDevice *vdev) vdev->nr_vectors = 0; vdev->interrupt = VFIO_INT_NONE; - vfio_enable_intx(vdev); + vfio_intx_enable(vdev); } -static void vfio_disable_msix(VFIOPCIDevice *vdev) +static void vfio_msix_disable(VFIOPCIDevice *vdev) { int i; @@ -901,17 +748,20 @@ static void vfio_disable_msix(VFIOPCIDevice *vdev) vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX); } - vfio_disable_msi_common(vdev); + vfio_msi_disable_common(vdev); + + memset(vdev->msix->pending, 0, + BITS_TO_LONGS(vdev->msix->entries) * sizeof(unsigned long)); - trace_vfio_disable_msix(vdev->vbasedev.name); + trace_vfio_msix_disable(vdev->vbasedev.name); } -static void vfio_disable_msi(VFIOPCIDevice *vdev) +static void vfio_msi_disable(VFIOPCIDevice *vdev) { vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSI_IRQ_INDEX); - vfio_disable_msi_common(vdev); + vfio_msi_disable_common(vdev); - trace_vfio_disable_msi(vdev->vbasedev.name); + trace_vfio_msi_disable(vdev->vbasedev.name); } static void vfio_update_msi(VFIOPCIDevice *vdev) @@ -927,31 +777,31 @@ static void vfio_update_msi(VFIOPCIDevice *vdev) } msg = msi_get_message(&vdev->pdev, i); - vfio_update_kvm_msi_virq(vector, msg); + vfio_update_kvm_msi_virq(vector, msg, &vdev->pdev); } } static void vfio_pci_load_rom(VFIOPCIDevice *vdev) { - struct vfio_region_info reg_info = { - .argsz = sizeof(reg_info), - .index = VFIO_PCI_ROM_REGION_INDEX - }; + struct vfio_region_info *reg_info; uint64_t size; off_t off = 0; ssize_t bytes; - if (ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_REGION_INFO, ®_info)) { + if (vfio_get_region_info(&vdev->vbasedev, + VFIO_PCI_ROM_REGION_INDEX, ®_info)) { error_report("vfio: Error getting ROM info: %m"); return; } - trace_vfio_pci_load_rom(vdev->vbasedev.name, (unsigned long)reg_info.size, - (unsigned long)reg_info.offset, - (unsigned long)reg_info.flags); + trace_vfio_pci_load_rom(vdev->vbasedev.name, (unsigned long)reg_info->size, + (unsigned long)reg_info->offset, + (unsigned long)reg_info->flags); + + vdev->rom_size = size = reg_info->size; + vdev->rom_offset = reg_info->offset; - vdev->rom_size = size = reg_info.size; - vdev->rom_offset = reg_info.offset; + g_free(reg_info); if (!vdev->rom_size) { vdev->rom_read_failed = true; @@ -982,6 +832,36 @@ static void vfio_pci_load_rom(VFIOPCIDevice *vdev) break; } } + + /* + * Test the ROM signature against our device, if the vendor is correct + * but the device ID doesn't match, store the correct device ID and + * recompute the checksum. Intel IGD devices need this and are known + * to have bogus checksums so we can't simply adjust the checksum. + */ + if (pci_get_word(vdev->rom) == 0xaa55 && + pci_get_word(vdev->rom + 0x18) + 8 < vdev->rom_size && + !memcmp(vdev->rom + pci_get_word(vdev->rom + 0x18), "PCIR", 4)) { + uint16_t vid, did; + + vid = pci_get_word(vdev->rom + pci_get_word(vdev->rom + 0x18) + 4); + did = pci_get_word(vdev->rom + pci_get_word(vdev->rom + 0x18) + 6); + + if (vid == vdev->vendor_id && did != vdev->device_id) { + int i; + uint8_t csum, *data = vdev->rom; + + pci_set_word(vdev->rom + pci_get_word(vdev->rom + 0x18) + 6, + vdev->device_id); + data[6] = 0; + + for (csum = 0, i = 0; i < vdev->rom_size; i++) { + csum += data[i]; + } + + data[6] = -csum; + } + } } static uint64_t vfio_rom_read(void *opaque, hwaddr addr, unsigned size) @@ -1034,43 +914,19 @@ static const MemoryRegionOps vfio_rom_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static bool vfio_blacklist_opt_rom(VFIOPCIDevice *vdev) -{ - PCIDevice *pdev = &vdev->pdev; - uint16_t vendor_id, device_id; - int count = 0; - - vendor_id = pci_get_word(pdev->config + PCI_VENDOR_ID); - device_id = pci_get_word(pdev->config + PCI_DEVICE_ID); - - while (count < ARRAY_SIZE(romblacklist)) { - if (romblacklist[count].vendor_id == vendor_id && - romblacklist[count].device_id == device_id) { - return true; - } - count++; - } - - return false; -} - static void vfio_pci_size_rom(VFIOPCIDevice *vdev) { uint32_t orig, size = cpu_to_le32((uint32_t)PCI_ROM_ADDRESS_MASK); off_t offset = vdev->config_offset + PCI_ROM_ADDRESS; DeviceState *dev = DEVICE(vdev); - char name[32]; + char *name; int fd = vdev->vbasedev.fd; if (vdev->pdev.romfile || !vdev->pdev.rom_bar) { /* Since pci handles romfile, just print a message and return */ if (vfio_blacklist_opt_rom(vdev) && vdev->pdev.romfile) { - error_printf("Warning : Device at %04x:%02x:%02x.%x " - "is known to cause system instability issues during " - "option rom execution. " - "Proceeding anyway since user specified romfile\n", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); + error_printf("Warning : Device at %s is known to cause system instability issues during option rom execution. Proceeding anyway since user specified romfile\n", + vdev->vbasedev.name); } return; } @@ -1083,9 +939,7 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) pwrite(fd, &size, 4, offset) != 4 || pread(fd, &size, 4, offset) != 4 || pwrite(fd, &orig, 4, offset) != 4) { - error_report("%s(%04x:%02x:%02x.%x) failed: %m", - __func__, vdev->host.domain, vdev->host.bus, - vdev->host.slot, vdev->host.function); + error_report("%s(%s) failed: %m", __func__, vdev->vbasedev.name); return; } @@ -1097,32 +951,22 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) if (vfio_blacklist_opt_rom(vdev)) { if (dev->opts && qemu_opt_get(dev->opts, "rombar")) { - error_printf("Warning : Device at %04x:%02x:%02x.%x " - "is known to cause system instability issues during " - "option rom execution. " - "Proceeding anyway since user specified non zero value for " - "rombar\n", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); + error_printf("Warning : Device at %s is known to cause system instability issues during option rom execution. Proceeding anyway since user specified non zero value for rombar\n", + vdev->vbasedev.name); } else { - error_printf("Warning : Rom loading for device at " - "%04x:%02x:%02x.%x has been disabled due to " - "system instability issues. " - "Specify rombar=1 or romfile to force\n", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); + error_printf("Warning : Rom loading for device at %s has been disabled due to system instability issues. Specify rombar=1 or romfile to force\n", + vdev->vbasedev.name); return; } } trace_vfio_pci_size_rom(vdev->vbasedev.name, size); - snprintf(name, sizeof(name), "vfio[%04x:%02x:%02x.%x].rom", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); + name = g_strdup_printf("vfio[%s].rom", vdev->vbasedev.name); memory_region_init_io(&vdev->pdev.rom, OBJECT(vdev), &vfio_rom_ops, vdev, name, size); + g_free(name); pci_register_bar(&vdev->pdev, PCI_ROM_SLOT, PCI_BASE_ADDRESS_SPACE_MEMORY, &vdev->pdev.rom); @@ -1131,7 +975,7 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) vdev->rom_read_failed = false; } -static void vfio_vga_write(void *opaque, hwaddr addr, +void vfio_vga_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { VFIOVGARegion *region = opaque; @@ -1155,916 +999,67 @@ static void vfio_vga_write(void *opaque, hwaddr addr, buf.dword = cpu_to_le32(data); break; default: - hw_error("vfio: unsupported write size, %d bytes", size); - break; - } - - if (pwrite(vga->fd, &buf, size, offset) != size) { - error_report("%s(,0x%"HWADDR_PRIx", 0x%"PRIx64", %d) failed: %m", - __func__, region->offset + addr, data, size); - } - - trace_vfio_vga_write(region->offset + addr, data, size); -} - -static uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size) -{ - VFIOVGARegion *region = opaque; - VFIOVGA *vga = container_of(region, VFIOVGA, region[region->nr]); - union { - uint8_t byte; - uint16_t word; - uint32_t dword; - uint64_t qword; - } buf; - uint64_t data = 0; - off_t offset = vga->fd_offset + region->offset + addr; - - if (pread(vga->fd, &buf, size, offset) != size) { - error_report("%s(,0x%"HWADDR_PRIx", %d) failed: %m", - __func__, region->offset + addr, size); - return (uint64_t)-1; - } - - switch (size) { - case 1: - data = buf.byte; - break; - case 2: - data = le16_to_cpu(buf.word); - break; - case 4: - data = le32_to_cpu(buf.dword); - break; - default: - hw_error("vfio: unsupported read size, %d bytes", size); - break; - } - - trace_vfio_vga_read(region->offset + addr, size, data); - - return data; -} - -static const MemoryRegionOps vfio_vga_ops = { - .read = vfio_vga_read, - .write = vfio_vga_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -/* - * Device specific quirks - */ - -/* Is range1 fully contained within range2? */ -static bool vfio_range_contained(uint64_t first1, uint64_t len1, - uint64_t first2, uint64_t len2) { - return (first1 >= first2 && first1 + len1 <= first2 + len2); -} - -static bool vfio_flags_enabled(uint8_t flags, uint8_t mask) -{ - return (mask && (flags & mask) == mask); -} - -static uint64_t vfio_generic_window_quirk_read(void *opaque, - hwaddr addr, unsigned size) -{ - VFIOQuirk *quirk = opaque; - VFIOPCIDevice *vdev = quirk->vdev; - uint64_t data; - - if (vfio_flags_enabled(quirk->data.flags, quirk->data.read_flags) && - ranges_overlap(addr, size, - quirk->data.data_offset, quirk->data.data_size)) { - hwaddr offset = addr - quirk->data.data_offset; - - if (!vfio_range_contained(addr, size, quirk->data.data_offset, - quirk->data.data_size)) { - hw_error("%s: window data read not fully contained: %s", - __func__, memory_region_name(&quirk->mem)); - } - - data = vfio_pci_read_config(&vdev->pdev, - quirk->data.address_val + offset, size); - - trace_vfio_generic_window_quirk_read(memory_region_name(&quirk->mem), - vdev->vbasedev.name, - quirk->data.bar, - addr, size, data); - } else { - data = vfio_region_read(&vdev->bars[quirk->data.bar].region, - addr + quirk->data.base_offset, size); - } - - return data; -} - -static void vfio_generic_window_quirk_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - VFIOQuirk *quirk = opaque; - VFIOPCIDevice *vdev = quirk->vdev; - - if (ranges_overlap(addr, size, - quirk->data.address_offset, quirk->data.address_size)) { - - if (addr != quirk->data.address_offset) { - hw_error("%s: offset write into address window: %s", - __func__, memory_region_name(&quirk->mem)); - } - - if ((data & ~quirk->data.address_mask) == quirk->data.address_match) { - quirk->data.flags |= quirk->data.write_flags | - quirk->data.read_flags; - quirk->data.address_val = data & quirk->data.address_mask; - } else { - quirk->data.flags &= ~(quirk->data.write_flags | - quirk->data.read_flags); - } - } - - if (vfio_flags_enabled(quirk->data.flags, quirk->data.write_flags) && - ranges_overlap(addr, size, - quirk->data.data_offset, quirk->data.data_size)) { - hwaddr offset = addr - quirk->data.data_offset; - - if (!vfio_range_contained(addr, size, quirk->data.data_offset, - quirk->data.data_size)) { - hw_error("%s: window data write not fully contained: %s", - __func__, memory_region_name(&quirk->mem)); - } - - vfio_pci_write_config(&vdev->pdev, - quirk->data.address_val + offset, data, size); - trace_vfio_generic_window_quirk_write(memory_region_name(&quirk->mem), - vdev->vbasedev.name, - quirk->data.bar, - addr, data, size); - return; - } - - vfio_region_write(&vdev->bars[quirk->data.bar].region, - addr + quirk->data.base_offset, data, size); -} - -static const MemoryRegionOps vfio_generic_window_quirk = { - .read = vfio_generic_window_quirk_read, - .write = vfio_generic_window_quirk_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static uint64_t vfio_generic_quirk_read(void *opaque, - hwaddr addr, unsigned size) -{ - VFIOQuirk *quirk = opaque; - VFIOPCIDevice *vdev = quirk->vdev; - hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK; - hwaddr offset = quirk->data.address_match & ~TARGET_PAGE_MASK; - uint64_t data; - - if (vfio_flags_enabled(quirk->data.flags, quirk->data.read_flags) && - ranges_overlap(addr, size, offset, quirk->data.address_mask + 1)) { - if (!vfio_range_contained(addr, size, offset, - quirk->data.address_mask + 1)) { - hw_error("%s: read not fully contained: %s", - __func__, memory_region_name(&quirk->mem)); - } - - data = vfio_pci_read_config(&vdev->pdev, addr - offset, size); - - trace_vfio_generic_quirk_read(memory_region_name(&quirk->mem), - vdev->vbasedev.name, quirk->data.bar, - addr + base, size, data); - } else { - data = vfio_region_read(&vdev->bars[quirk->data.bar].region, - addr + base, size); - } - - return data; -} - -static void vfio_generic_quirk_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - VFIOQuirk *quirk = opaque; - VFIOPCIDevice *vdev = quirk->vdev; - hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK; - hwaddr offset = quirk->data.address_match & ~TARGET_PAGE_MASK; - - if (vfio_flags_enabled(quirk->data.flags, quirk->data.write_flags) && - ranges_overlap(addr, size, offset, quirk->data.address_mask + 1)) { - if (!vfio_range_contained(addr, size, offset, - quirk->data.address_mask + 1)) { - hw_error("%s: write not fully contained: %s", - __func__, memory_region_name(&quirk->mem)); - } - - vfio_pci_write_config(&vdev->pdev, addr - offset, data, size); - - trace_vfio_generic_quirk_write(memory_region_name(&quirk->mem), - vdev->vbasedev.name, quirk->data.bar, - addr + base, data, size); - } else { - vfio_region_write(&vdev->bars[quirk->data.bar].region, - addr + base, data, size); - } -} - -static const MemoryRegionOps vfio_generic_quirk = { - .read = vfio_generic_quirk_read, - .write = vfio_generic_quirk_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -#define PCI_VENDOR_ID_ATI 0x1002 - -/* - * Radeon HD cards (HD5450 & HD7850) report the upper byte of the I/O port BAR - * through VGA register 0x3c3. On newer cards, the I/O port BAR is always - * BAR4 (older cards like the X550 used BAR1, but we don't care to support - * those). Note that on bare metal, a read of 0x3c3 doesn't always return the - * I/O port BAR address. Originally this was coded to return the virtual BAR - * address only if the physical register read returns the actual BAR address, - * but users have reported greater success if we return the virtual address - * unconditionally. - */ -static uint64_t vfio_ati_3c3_quirk_read(void *opaque, - hwaddr addr, unsigned size) -{ - VFIOQuirk *quirk = opaque; - VFIOPCIDevice *vdev = quirk->vdev; - uint64_t data = vfio_pci_read_config(&vdev->pdev, - PCI_BASE_ADDRESS_0 + (4 * 4) + 1, - size); - trace_vfio_ati_3c3_quirk_read(data); - - return data; -} - -static const MemoryRegionOps vfio_ati_3c3_quirk = { - .read = vfio_ati_3c3_quirk_read, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void vfio_vga_probe_ati_3c3_quirk(VFIOPCIDevice *vdev) -{ - PCIDevice *pdev = &vdev->pdev; - VFIOQuirk *quirk; - - if (pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) { - return; - } - - /* - * As long as the BAR is >= 256 bytes it will be aligned such that the - * lower byte is always zero. Filter out anything else, if it exists. - */ - if (!vdev->bars[4].ioport || vdev->bars[4].region.size < 256) { - return; - } - - quirk = g_malloc0(sizeof(*quirk)); - quirk->vdev = vdev; - - memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_ati_3c3_quirk, quirk, - "vfio-ati-3c3-quirk", 1); - memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem, - 3 /* offset 3 bytes from 0x3c0 */, &quirk->mem); - - QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks, - quirk, next); - - trace_vfio_vga_probe_ati_3c3_quirk(vdev->vbasedev.name); -} - -/* - * Newer ATI/AMD devices, including HD5450 and HD7850, have a window to PCI - * config space through MMIO BAR2 at offset 0x4000. Nothing seems to access - * the MMIO space directly, but a window to this space is provided through - * I/O port BAR4. Offset 0x0 is the address register and offset 0x4 is the - * data register. When the address is programmed to a range of 0x4000-0x4fff - * PCI configuration space is available. Experimentation seems to indicate - * that only read-only access is provided, but we drop writes when the window - * is enabled to config space nonetheless. - */ -static void vfio_probe_ati_bar4_window_quirk(VFIOPCIDevice *vdev, int nr) -{ - PCIDevice *pdev = &vdev->pdev; - VFIOQuirk *quirk; - - if (!vdev->has_vga || nr != 4 || - pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) { - return; - } - - quirk = g_malloc0(sizeof(*quirk)); - quirk->vdev = vdev; - quirk->data.address_size = 4; - quirk->data.data_offset = 4; - quirk->data.data_size = 4; - quirk->data.address_match = 0x4000; - quirk->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1; - quirk->data.bar = nr; - quirk->data.read_flags = quirk->data.write_flags = 1; - - memory_region_init_io(&quirk->mem, OBJECT(vdev), - &vfio_generic_window_quirk, quirk, - "vfio-ati-bar4-window-quirk", 8); - memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem, - quirk->data.base_offset, &quirk->mem, 1); - - QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); - - trace_vfio_probe_ati_bar4_window_quirk(vdev->vbasedev.name); -} - -#define PCI_VENDOR_ID_REALTEK 0x10ec - -/* - * RTL8168 devices have a backdoor that can access the MSI-X table. At BAR2 - * offset 0x70 there is a dword data register, offset 0x74 is a dword address - * register. According to the Linux r8169 driver, the MSI-X table is addressed - * when the "type" portion of the address register is set to 0x1. This appears - * to be bits 16:30. Bit 31 is both a write indicator and some sort of - * "address latched" indicator. Bits 12:15 are a mask field, which we can - * ignore because the MSI-X table should always be accessed as a dword (full - * mask). Bits 0:11 is offset within the type. - * - * Example trace: - * - * Read from MSI-X table offset 0 - * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x74, 0x1f000, 4) // store read addr - * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x74, 4) = 0x8001f000 // latch - * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x70, 4) = 0xfee00398 // read data - * - * Write 0xfee00000 to MSI-X table offset 0 - * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x70, 0xfee00000, 4) // write data - * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x74, 0x8001f000, 4) // do write - * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x74, 4) = 0x1f000 // complete - */ - -static uint64_t vfio_rtl8168_window_quirk_read(void *opaque, - hwaddr addr, unsigned size) -{ - VFIOQuirk *quirk = opaque; - VFIOPCIDevice *vdev = quirk->vdev; - - switch (addr) { - case 4: /* address */ - if (quirk->data.flags) { - trace_vfio_rtl8168_window_quirk_read_fake( - memory_region_name(&quirk->mem), - vdev->vbasedev.name); - - return quirk->data.address_match ^ 0x80000000U; - } - break; - case 0: /* data */ - if (quirk->data.flags) { - uint64_t val; - - trace_vfio_rtl8168_window_quirk_read_table( - memory_region_name(&quirk->mem), - vdev->vbasedev.name); - - if (!(vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX)) { - return 0; - } - - memory_region_dispatch_read(&vdev->pdev.msix_table_mmio, - (hwaddr)(quirk->data.address_match - & 0xfff), - &val, - size, - MEMTXATTRS_UNSPECIFIED); - return val; - } - } - - trace_vfio_rtl8168_window_quirk_read_direct(memory_region_name(&quirk->mem), - vdev->vbasedev.name); - - return vfio_region_read(&vdev->bars[quirk->data.bar].region, - addr + 0x70, size); -} - -static void vfio_rtl8168_window_quirk_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - VFIOQuirk *quirk = opaque; - VFIOPCIDevice *vdev = quirk->vdev; - - switch (addr) { - case 4: /* address */ - if ((data & 0x7fff0000) == 0x10000) { - if (data & 0x80000000U && - vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX) { - - trace_vfio_rtl8168_window_quirk_write_table( - memory_region_name(&quirk->mem), - vdev->vbasedev.name); - - memory_region_dispatch_write(&vdev->pdev.msix_table_mmio, - (hwaddr)(data & 0xfff), - (uint64_t)quirk->data.address_mask, - size, MEMTXATTRS_UNSPECIFIED); - } - - quirk->data.flags = 1; - quirk->data.address_match = data; - - return; - } - quirk->data.flags = 0; - break; - case 0: /* data */ - quirk->data.address_mask = data; - break; - } - - trace_vfio_rtl8168_window_quirk_write_direct( - memory_region_name(&quirk->mem), - vdev->vbasedev.name); - - vfio_region_write(&vdev->bars[quirk->data.bar].region, - addr + 0x70, data, size); -} - -static const MemoryRegionOps vfio_rtl8168_window_quirk = { - .read = vfio_rtl8168_window_quirk_read, - .write = vfio_rtl8168_window_quirk_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void vfio_probe_rtl8168_bar2_window_quirk(VFIOPCIDevice *vdev, int nr) -{ - PCIDevice *pdev = &vdev->pdev; - VFIOQuirk *quirk; - - if (pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_REALTEK || - pci_get_word(pdev->config + PCI_DEVICE_ID) != 0x8168 || nr != 2) { - return; - } - - quirk = g_malloc0(sizeof(*quirk)); - quirk->vdev = vdev; - quirk->data.bar = nr; - - memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_rtl8168_window_quirk, - quirk, "vfio-rtl8168-window-quirk", 8); - memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem, - 0x70, &quirk->mem, 1); - - QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); - - trace_vfio_probe_rtl8168_bar2_window_quirk(vdev->vbasedev.name); -} -/* - * Trap the BAR2 MMIO window to config space as well. - */ -static void vfio_probe_ati_bar2_4000_quirk(VFIOPCIDevice *vdev, int nr) -{ - PCIDevice *pdev = &vdev->pdev; - VFIOQuirk *quirk; - - /* Only enable on newer devices where BAR2 is 64bit */ - if (!vdev->has_vga || nr != 2 || !vdev->bars[2].mem64 || - pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) { - return; - } - - quirk = g_malloc0(sizeof(*quirk)); - quirk->vdev = vdev; - quirk->data.flags = quirk->data.read_flags = quirk->data.write_flags = 1; - quirk->data.address_match = 0x4000; - quirk->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1; - quirk->data.bar = nr; - - memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_generic_quirk, quirk, - "vfio-ati-bar2-4000-quirk", - TARGET_PAGE_ALIGN(quirk->data.address_mask + 1)); - memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem, - quirk->data.address_match & TARGET_PAGE_MASK, - &quirk->mem, 1); - - QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); - - trace_vfio_probe_ati_bar2_4000_quirk(vdev->vbasedev.name); -} - -/* - * Older ATI/AMD cards like the X550 have a similar window to that above. - * I/O port BAR1 provides a window to a mirror of PCI config space located - * in BAR2 at offset 0xf00. We don't care to support such older cards, but - * note it for future reference. - */ - -#define PCI_VENDOR_ID_NVIDIA 0x10de - -/* - * Nvidia has several different methods to get to config space, the - * nouveu project has several of these documented here: - * https://github.com/pathscale/envytools/tree/master/hwdocs - * - * The first quirk is actually not documented in envytools and is found - * on 10de:01d1 (NVIDIA Corporation G72 [GeForce 7300 LE]). This is an - * NV46 chipset. The backdoor uses the legacy VGA I/O ports to access - * the mirror of PCI config space found at BAR0 offset 0x1800. The access - * sequence first writes 0x338 to I/O port 0x3d4. The target offset is - * then written to 0x3d0. Finally 0x538 is written for a read and 0x738 - * is written for a write to 0x3d4. The BAR0 offset is then accessible - * through 0x3d0. This quirk doesn't seem to be necessary on newer cards - * that use the I/O port BAR5 window but it doesn't hurt to leave it. - */ -enum { - NV_3D0_NONE = 0, - NV_3D0_SELECT, - NV_3D0_WINDOW, - NV_3D0_READ, - NV_3D0_WRITE, -}; - -static uint64_t vfio_nvidia_3d0_quirk_read(void *opaque, - hwaddr addr, unsigned size) -{ - VFIOQuirk *quirk = opaque; - VFIOPCIDevice *vdev = quirk->vdev; - PCIDevice *pdev = &vdev->pdev; - uint64_t data = vfio_vga_read(&vdev->vga.region[QEMU_PCI_VGA_IO_HI], - addr + quirk->data.base_offset, size); - - if (quirk->data.flags == NV_3D0_READ && addr == quirk->data.data_offset) { - data = vfio_pci_read_config(pdev, quirk->data.address_val, size); - trace_vfio_nvidia_3d0_quirk_read(size, data); - } - - quirk->data.flags = NV_3D0_NONE; - - return data; -} - -static void vfio_nvidia_3d0_quirk_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - VFIOQuirk *quirk = opaque; - VFIOPCIDevice *vdev = quirk->vdev; - PCIDevice *pdev = &vdev->pdev; - - switch (quirk->data.flags) { - case NV_3D0_NONE: - if (addr == quirk->data.address_offset && data == 0x338) { - quirk->data.flags = NV_3D0_SELECT; - } - break; - case NV_3D0_SELECT: - quirk->data.flags = NV_3D0_NONE; - if (addr == quirk->data.data_offset && - (data & ~quirk->data.address_mask) == quirk->data.address_match) { - quirk->data.flags = NV_3D0_WINDOW; - quirk->data.address_val = data & quirk->data.address_mask; - } - break; - case NV_3D0_WINDOW: - quirk->data.flags = NV_3D0_NONE; - if (addr == quirk->data.address_offset) { - if (data == 0x538) { - quirk->data.flags = NV_3D0_READ; - } else if (data == 0x738) { - quirk->data.flags = NV_3D0_WRITE; - } - } - break; - case NV_3D0_WRITE: - quirk->data.flags = NV_3D0_NONE; - if (addr == quirk->data.data_offset) { - vfio_pci_write_config(pdev, quirk->data.address_val, data, size); - trace_vfio_nvidia_3d0_quirk_write(data, size); - return; - } - break; - } - - vfio_vga_write(&vdev->vga.region[QEMU_PCI_VGA_IO_HI], - addr + quirk->data.base_offset, data, size); -} - -static const MemoryRegionOps vfio_nvidia_3d0_quirk = { - .read = vfio_nvidia_3d0_quirk_read, - .write = vfio_nvidia_3d0_quirk_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void vfio_vga_probe_nvidia_3d0_quirk(VFIOPCIDevice *vdev) -{ - PCIDevice *pdev = &vdev->pdev; - VFIOQuirk *quirk; - - if (pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_NVIDIA || - !vdev->bars[1].region.size) { - return; - } - - quirk = g_malloc0(sizeof(*quirk)); - quirk->vdev = vdev; - quirk->data.base_offset = 0x10; - quirk->data.address_offset = 4; - quirk->data.address_size = 2; - quirk->data.address_match = 0x1800; - quirk->data.address_mask = PCI_CONFIG_SPACE_SIZE - 1; - quirk->data.data_offset = 0; - quirk->data.data_size = 4; - - memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_nvidia_3d0_quirk, - quirk, "vfio-nvidia-3d0-quirk", 6); - memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem, - quirk->data.base_offset, &quirk->mem); - - QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks, - quirk, next); - - trace_vfio_vga_probe_nvidia_3d0_quirk(vdev->vbasedev.name); -} - -/* - * The second quirk is documented in envytools. The I/O port BAR5 is just - * a set of address/data ports to the MMIO BARs. The BAR we care about is - * again BAR0. This backdoor is apparently a bit newer than the one above - * so we need to not only trap 256 bytes @0x1800, but all of PCI config - * space, including extended space is available at the 4k @0x88000. - */ -enum { - NV_BAR5_ADDRESS = 0x1, - NV_BAR5_ENABLE = 0x2, - NV_BAR5_MASTER = 0x4, - NV_BAR5_VALID = 0x7, -}; - -static void vfio_nvidia_bar5_window_quirk_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - VFIOQuirk *quirk = opaque; - - switch (addr) { - case 0x0: - if (data & 0x1) { - quirk->data.flags |= NV_BAR5_MASTER; - } else { - quirk->data.flags &= ~NV_BAR5_MASTER; - } - break; - case 0x4: - if (data & 0x1) { - quirk->data.flags |= NV_BAR5_ENABLE; - } else { - quirk->data.flags &= ~NV_BAR5_ENABLE; - } - break; - case 0x8: - if (quirk->data.flags & NV_BAR5_MASTER) { - if ((data & ~0xfff) == 0x88000) { - quirk->data.flags |= NV_BAR5_ADDRESS; - quirk->data.address_val = data & 0xfff; - } else if ((data & ~0xff) == 0x1800) { - quirk->data.flags |= NV_BAR5_ADDRESS; - quirk->data.address_val = data & 0xff; - } else { - quirk->data.flags &= ~NV_BAR5_ADDRESS; - } - } - break; - } - - vfio_generic_window_quirk_write(opaque, addr, data, size); -} - -static const MemoryRegionOps vfio_nvidia_bar5_window_quirk = { - .read = vfio_generic_window_quirk_read, - .write = vfio_nvidia_bar5_window_quirk_write, - .valid.min_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void vfio_probe_nvidia_bar5_window_quirk(VFIOPCIDevice *vdev, int nr) -{ - PCIDevice *pdev = &vdev->pdev; - VFIOQuirk *quirk; - - if (!vdev->has_vga || nr != 5 || - pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_NVIDIA) { - return; - } - - quirk = g_malloc0(sizeof(*quirk)); - quirk->vdev = vdev; - quirk->data.read_flags = quirk->data.write_flags = NV_BAR5_VALID; - quirk->data.address_offset = 0x8; - quirk->data.address_size = 0; /* actually 4, but avoids generic code */ - quirk->data.data_offset = 0xc; - quirk->data.data_size = 4; - quirk->data.bar = nr; - - memory_region_init_io(&quirk->mem, OBJECT(vdev), - &vfio_nvidia_bar5_window_quirk, quirk, - "vfio-nvidia-bar5-window-quirk", 16); - memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem, - 0, &quirk->mem, 1); - - QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); - - trace_vfio_probe_nvidia_bar5_window_quirk(vdev->vbasedev.name); -} - -static void vfio_nvidia_88000_quirk_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - VFIOQuirk *quirk = opaque; - VFIOPCIDevice *vdev = quirk->vdev; - PCIDevice *pdev = &vdev->pdev; - hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK; - - vfio_generic_quirk_write(opaque, addr, data, size); - - /* - * Nvidia seems to acknowledge MSI interrupts by writing 0xff to the - * MSI capability ID register. Both the ID and next register are - * read-only, so we allow writes covering either of those to real hw. - * NB - only fixed for the 0x88000 MMIO window. - */ - if ((pdev->cap_present & QEMU_PCI_CAP_MSI) && - vfio_range_contained(addr, size, pdev->msi_cap, PCI_MSI_FLAGS)) { - vfio_region_write(&vdev->bars[quirk->data.bar].region, - addr + base, data, size); - } -} - -static const MemoryRegionOps vfio_nvidia_88000_quirk = { - .read = vfio_generic_quirk_read, - .write = vfio_nvidia_88000_quirk_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -/* - * Finally, BAR0 itself. We want to redirect any accesses to either - * 0x1800 or 0x88000 through the PCI config space access functions. - * - * NB - quirk at a page granularity or else they don't seem to work when - * BARs are mmap'd - * - * Here's offset 0x88000... - */ -static void vfio_probe_nvidia_bar0_88000_quirk(VFIOPCIDevice *vdev, int nr) -{ - PCIDevice *pdev = &vdev->pdev; - VFIOQuirk *quirk; - uint16_t vendor, class; - - vendor = pci_get_word(pdev->config + PCI_VENDOR_ID); - class = pci_get_word(pdev->config + PCI_CLASS_DEVICE); - - if (nr != 0 || vendor != PCI_VENDOR_ID_NVIDIA || - class != PCI_CLASS_DISPLAY_VGA) { - return; - } - - quirk = g_malloc0(sizeof(*quirk)); - quirk->vdev = vdev; - quirk->data.flags = quirk->data.read_flags = quirk->data.write_flags = 1; - quirk->data.address_match = 0x88000; - quirk->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1; - quirk->data.bar = nr; - - memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_nvidia_88000_quirk, - quirk, "vfio-nvidia-bar0-88000-quirk", - TARGET_PAGE_ALIGN(quirk->data.address_mask + 1)); - memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem, - quirk->data.address_match & TARGET_PAGE_MASK, - &quirk->mem, 1); - - QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); - - trace_vfio_probe_nvidia_bar0_88000_quirk(vdev->vbasedev.name); -} - -/* - * And here's the same for BAR0 offset 0x1800... - */ -static void vfio_probe_nvidia_bar0_1800_quirk(VFIOPCIDevice *vdev, int nr) -{ - PCIDevice *pdev = &vdev->pdev; - VFIOQuirk *quirk; - - if (!vdev->has_vga || nr != 0 || - pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_NVIDIA) { - return; - } - - /* Log the chipset ID */ - trace_vfio_probe_nvidia_bar0_1800_quirk_id( - (unsigned int)(vfio_region_read(&vdev->bars[0].region, 0, 4) >> 20) - & 0xff); - - quirk = g_malloc0(sizeof(*quirk)); - quirk->vdev = vdev; - quirk->data.flags = quirk->data.read_flags = quirk->data.write_flags = 1; - quirk->data.address_match = 0x1800; - quirk->data.address_mask = PCI_CONFIG_SPACE_SIZE - 1; - quirk->data.bar = nr; - - memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_generic_quirk, quirk, - "vfio-nvidia-bar0-1800-quirk", - TARGET_PAGE_ALIGN(quirk->data.address_mask + 1)); - memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem, - quirk->data.address_match & TARGET_PAGE_MASK, - &quirk->mem, 1); - - QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); - - trace_vfio_probe_nvidia_bar0_1800_quirk(vdev->vbasedev.name); -} + hw_error("vfio: unsupported write size, %d bytes", size); + break; + } -/* - * TODO - Some Nvidia devices provide config access to their companion HDA - * device and even to their parent bridge via these config space mirrors. - * Add quirks for those regions. - */ + if (pwrite(vga->fd, &buf, size, offset) != size) { + error_report("%s(,0x%"HWADDR_PRIx", 0x%"PRIx64", %d) failed: %m", + __func__, region->offset + addr, data, size); + } -/* - * Common quirk probe entry points. - */ -static void vfio_vga_quirk_setup(VFIOPCIDevice *vdev) -{ - vfio_vga_probe_ati_3c3_quirk(vdev); - vfio_vga_probe_nvidia_3d0_quirk(vdev); + trace_vfio_vga_write(region->offset + addr, data, size); } -static void vfio_vga_quirk_teardown(VFIOPCIDevice *vdev) +uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size) { - VFIOQuirk *quirk; - int i; + VFIOVGARegion *region = opaque; + VFIOVGA *vga = container_of(region, VFIOVGA, region[region->nr]); + union { + uint8_t byte; + uint16_t word; + uint32_t dword; + uint64_t qword; + } buf; + uint64_t data = 0; + off_t offset = vga->fd_offset + region->offset + addr; - for (i = 0; i < ARRAY_SIZE(vdev->vga.region); i++) { - QLIST_FOREACH(quirk, &vdev->vga.region[i].quirks, next) { - memory_region_del_subregion(&vdev->vga.region[i].mem, &quirk->mem); - } + if (pread(vga->fd, &buf, size, offset) != size) { + error_report("%s(,0x%"HWADDR_PRIx", %d) failed: %m", + __func__, region->offset + addr, size); + return (uint64_t)-1; } -} - -static void vfio_vga_quirk_free(VFIOPCIDevice *vdev) -{ - int i; - for (i = 0; i < ARRAY_SIZE(vdev->vga.region); i++) { - while (!QLIST_EMPTY(&vdev->vga.region[i].quirks)) { - VFIOQuirk *quirk = QLIST_FIRST(&vdev->vga.region[i].quirks); - object_unparent(OBJECT(&quirk->mem)); - QLIST_REMOVE(quirk, next); - g_free(quirk); - } + switch (size) { + case 1: + data = buf.byte; + break; + case 2: + data = le16_to_cpu(buf.word); + break; + case 4: + data = le32_to_cpu(buf.dword); + break; + default: + hw_error("vfio: unsupported read size, %d bytes", size); + break; } -} -static void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr) -{ - vfio_probe_ati_bar4_window_quirk(vdev, nr); - vfio_probe_ati_bar2_4000_quirk(vdev, nr); - vfio_probe_nvidia_bar5_window_quirk(vdev, nr); - vfio_probe_nvidia_bar0_88000_quirk(vdev, nr); - vfio_probe_nvidia_bar0_1800_quirk(vdev, nr); - vfio_probe_rtl8168_bar2_window_quirk(vdev, nr); -} - -static void vfio_bar_quirk_teardown(VFIOPCIDevice *vdev, int nr) -{ - VFIOBAR *bar = &vdev->bars[nr]; - VFIOQuirk *quirk; + trace_vfio_vga_read(region->offset + addr, size, data); - QLIST_FOREACH(quirk, &bar->quirks, next) { - memory_region_del_subregion(&bar->region.mem, &quirk->mem); - } + return data; } -static void vfio_bar_quirk_free(VFIOPCIDevice *vdev, int nr) -{ - VFIOBAR *bar = &vdev->bars[nr]; - - while (!QLIST_EMPTY(&bar->quirks)) { - VFIOQuirk *quirk = QLIST_FIRST(&bar->quirks); - object_unparent(OBJECT(&quirk->mem)); - QLIST_REMOVE(quirk, next); - g_free(quirk); - } -} +static const MemoryRegionOps vfio_vga_ops = { + .read = vfio_vga_read, + .write = vfio_vga_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; /* * PCI config space */ -static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len) +uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len) { VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); uint32_t emu_bits = 0, emu_val = 0, phys_val = 0, val; @@ -2082,9 +1077,8 @@ static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len) ret = pread(vdev->vbasedev.fd, &phys_val, len, vdev->config_offset + addr); if (ret != len) { - error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x) failed: %m", - __func__, vdev->host.domain, vdev->host.bus, - vdev->host.slot, vdev->host.function, addr, len); + error_report("%s(%s, 0x%x, 0x%x) failed: %m", + __func__, vdev->vbasedev.name, addr, len); return -errno; } phys_val = le32_to_cpu(phys_val); @@ -2097,8 +1091,8 @@ static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len) return val; } -static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, - uint32_t val, int len) +void vfio_pci_write_config(PCIDevice *pdev, + uint32_t addr, uint32_t val, int len) { VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); uint32_t val_le = cpu_to_le32(val); @@ -2108,9 +1102,8 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, /* Write everything to VFIO, let it filter out what we can't write */ if (pwrite(vdev->vbasedev.fd, &val_le, len, vdev->config_offset + addr) != len) { - error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x, 0x%x) failed: %m", - __func__, vdev->host.domain, vdev->host.bus, - vdev->host.slot, vdev->host.function, addr, val, len); + error_report("%s(%s, 0x%x, 0x%x, 0x%x) failed: %m", + __func__, vdev->vbasedev.name, addr, val, len); } /* MSI/MSI-X Enabling/Disabling */ @@ -2124,11 +1117,11 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, if (!was_enabled) { if (is_enabled) { - vfio_enable_msi(vdev); + vfio_msi_enable(vdev); } } else { if (!is_enabled) { - vfio_disable_msi(vdev); + vfio_msi_disable(vdev); } else { vfio_update_msi(vdev); } @@ -2142,9 +1135,9 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, is_enabled = msix_enabled(pdev); if (!was_enabled && is_enabled) { - vfio_enable_msix(vdev); + vfio_msix_enable(vdev); } else if (was_enabled && !is_enabled) { - vfio_disable_msix(vdev); + vfio_msix_disable(vdev); } } else { /* Write everything to QEMU to keep emulated bits correct */ @@ -2163,17 +1156,17 @@ static void vfio_disable_interrupts(VFIOPCIDevice *vdev) * disable MSI/X and then cleanup by disabling INTx. */ if (vdev->interrupt == VFIO_INT_MSIX) { - vfio_disable_msix(vdev); + vfio_msix_disable(vdev); } else if (vdev->interrupt == VFIO_INT_MSI) { - vfio_disable_msi(vdev); + vfio_msi_disable(vdev); } if (vdev->interrupt == VFIO_INT_INTx) { - vfio_disable_intx(vdev); + vfio_intx_disable(vdev); } } -static int vfio_setup_msi(VFIOPCIDevice *vdev, int pos) +static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos) { uint16_t ctrl; bool msi_64bit, msi_maskbit; @@ -2189,7 +1182,7 @@ static int vfio_setup_msi(VFIOPCIDevice *vdev, int pos) msi_maskbit = !!(ctrl & PCI_MSI_FLAGS_MASKBIT); entries = 1 << ((ctrl & PCI_MSI_FLAGS_QMASK) >> 1); - trace_vfio_setup_msi(vdev->vbasedev.name, pos); + trace_vfio_msi_setup(vdev->vbasedev.name, pos); ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit); if (ret < 0) { @@ -2204,6 +1197,74 @@ static int vfio_setup_msi(VFIOPCIDevice *vdev, int pos) return 0; } +static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev) +{ + off_t start, end; + VFIORegion *region = &vdev->bars[vdev->msix->table_bar].region; + + /* + * We expect to find a single mmap covering the whole BAR, anything else + * means it's either unsupported or already setup. + */ + if (region->nr_mmaps != 1 || region->mmaps[0].offset || + region->size != region->mmaps[0].size) { + return; + } + + /* MSI-X table start and end aligned to host page size */ + start = vdev->msix->table_offset & qemu_real_host_page_mask; + end = REAL_HOST_PAGE_ALIGN((uint64_t)vdev->msix->table_offset + + (vdev->msix->entries * PCI_MSIX_ENTRY_SIZE)); + + /* + * Does the MSI-X table cover the beginning of the BAR? The whole BAR? + * NB - Host page size is necessarily a power of two and so is the PCI + * BAR (not counting EA yet), therefore if we have host page aligned + * @start and @end, then any remainder of the BAR before or after those + * must be at least host page sized and therefore mmap'able. + */ + if (!start) { + if (end >= region->size) { + region->nr_mmaps = 0; + g_free(region->mmaps); + region->mmaps = NULL; + trace_vfio_msix_fixup(vdev->vbasedev.name, + vdev->msix->table_bar, 0, 0); + } else { + region->mmaps[0].offset = end; + region->mmaps[0].size = region->size - end; + trace_vfio_msix_fixup(vdev->vbasedev.name, + vdev->msix->table_bar, region->mmaps[0].offset, + region->mmaps[0].offset + region->mmaps[0].size); + } + + /* Maybe it's aligned at the end of the BAR */ + } else if (end >= region->size) { + region->mmaps[0].size = start; + trace_vfio_msix_fixup(vdev->vbasedev.name, + vdev->msix->table_bar, region->mmaps[0].offset, + region->mmaps[0].offset + region->mmaps[0].size); + + /* Otherwise it must split the BAR */ + } else { + region->nr_mmaps = 2; + region->mmaps = g_renew(VFIOMmap, region->mmaps, 2); + + memcpy(®ion->mmaps[1], ®ion->mmaps[0], sizeof(VFIOMmap)); + + region->mmaps[0].size = start; + trace_vfio_msix_fixup(vdev->vbasedev.name, + vdev->msix->table_bar, region->mmaps[0].offset, + region->mmaps[0].offset + region->mmaps[0].size); + + region->mmaps[1].offset = end; + region->mmaps[1].size = region->size - end; + trace_vfio_msix_fixup(vdev->vbasedev.name, + vdev->msix->table_bar, region->mmaps[1].offset, + region->mmaps[1].offset + region->mmaps[1].size); + } +} + /* * We don't have any control over how pci_add_capability() inserts * capabilities into the chain. In order to setup MSI-X we need a @@ -2212,12 +1273,13 @@ static int vfio_setup_msi(VFIOPCIDevice *vdev, int pos) * need to first look for where the MSI-X table lives. So we * unfortunately split MSI-X setup across two functions. */ -static int vfio_early_setup_msix(VFIOPCIDevice *vdev) +static int vfio_msix_early_setup(VFIOPCIDevice *vdev) { uint8_t pos; uint16_t ctrl; uint32_t table, pba; int fd = vdev->vbasedev.fd; + VFIOMSIXInfo *msix; pos = pci_find_capability(&vdev->pdev, PCI_CAP_ID_MSIX); if (!pos) { @@ -2225,7 +1287,7 @@ static int vfio_early_setup_msix(VFIOPCIDevice *vdev) } if (pread(fd, &ctrl, sizeof(ctrl), - vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) { + vdev->config_offset + pos + PCI_MSIX_FLAGS) != sizeof(ctrl)) { return -errno; } @@ -2243,56 +1305,55 @@ static int vfio_early_setup_msix(VFIOPCIDevice *vdev) table = le32_to_cpu(table); pba = le32_to_cpu(pba); - vdev->msix = g_malloc0(sizeof(*(vdev->msix))); - vdev->msix->table_bar = table & PCI_MSIX_FLAGS_BIRMASK; - vdev->msix->table_offset = table & ~PCI_MSIX_FLAGS_BIRMASK; - vdev->msix->pba_bar = pba & PCI_MSIX_FLAGS_BIRMASK; - vdev->msix->pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK; - vdev->msix->entries = (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1; + msix = g_malloc0(sizeof(*msix)); + msix->table_bar = table & PCI_MSIX_FLAGS_BIRMASK; + msix->table_offset = table & ~PCI_MSIX_FLAGS_BIRMASK; + msix->pba_bar = pba & PCI_MSIX_FLAGS_BIRMASK; + msix->pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK; + msix->entries = (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1; /* * Test the size of the pba_offset variable and catch if it extends outside * of the specified BAR. If it is the case, we need to apply a hardware * specific quirk if the device is known or we have a broken configuration. */ - if (vdev->msix->pba_offset >= - vdev->bars[vdev->msix->pba_bar].region.size) { - - PCIDevice *pdev = &vdev->pdev; - uint16_t vendor = pci_get_word(pdev->config + PCI_VENDOR_ID); - uint16_t device = pci_get_word(pdev->config + PCI_DEVICE_ID); - + if (msix->pba_offset >= vdev->bars[msix->pba_bar].region.size) { /* * Chelsio T5 Virtual Function devices are encoded as 0x58xx for T5 * adapters. The T5 hardware returns an incorrect value of 0x8000 for * the VF PBA offset while the BAR itself is only 8k. The correct value * is 0x1000, so we hard code that here. */ - if (vendor == PCI_VENDOR_ID_CHELSIO && (device & 0xff00) == 0x5800) { - vdev->msix->pba_offset = 0x1000; + if (vdev->vendor_id == PCI_VENDOR_ID_CHELSIO && + (vdev->device_id & 0xff00) == 0x5800) { + msix->pba_offset = 0x1000; } else { error_report("vfio: Hardware reports invalid configuration, " "MSIX PBA outside of specified BAR"); + g_free(msix); return -EINVAL; } } - trace_vfio_early_setup_msix(vdev->vbasedev.name, pos, - vdev->msix->table_bar, - vdev->msix->table_offset, - vdev->msix->entries); + trace_vfio_msix_early_setup(vdev->vbasedev.name, pos, msix->table_bar, + msix->table_offset, msix->entries); + vdev->msix = msix; + + vfio_pci_fixup_msix_region(vdev); return 0; } -static int vfio_setup_msix(VFIOPCIDevice *vdev, int pos) +static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos) { int ret; + vdev->msix->pending = g_malloc0(BITS_TO_LONGS(vdev->msix->entries) * + sizeof(unsigned long)); ret = msix_init(&vdev->pdev, vdev->msix->entries, - &vdev->bars[vdev->msix->table_bar].region.mem, + vdev->bars[vdev->msix->table_bar].region.mem, vdev->msix->table_bar, vdev->msix->table_offset, - &vdev->bars[vdev->msix->pba_bar].region.mem, + vdev->bars[vdev->msix->pba_bar].region.mem, vdev->msix->pba_bar, vdev->msix->pba_offset, pos); if (ret < 0) { if (ret == -ENOTSUP) { @@ -2302,6 +1363,24 @@ static int vfio_setup_msix(VFIOPCIDevice *vdev, int pos) return ret; } + /* + * The PCI spec suggests that devices provide additional alignment for + * MSI-X structures and avoid overlapping non-MSI-X related registers. + * For an assigned device, this hopefully means that emulation of MSI-X + * structures does not affect the performance of the device. If devices + * fail to provide that alignment, a significant performance penalty may + * result, for instance Mellanox MT27500 VFs: + * http://www.spinics.net/lists/kvm/msg125881.html + * + * The PBA is simply not that important for such a serious regression and + * most drivers do not appear to look at it. The solution for this is to + * disable the PBA MemoryRegion unless it's being used. We disable it + * here and only enable it if a masked vector fires through QEMU. As the + * vector-use notifier is called, which occurs on unmask, we test whether + * PBA emulation is needed and again disable if not. + */ + memory_region_set_enabled(&vdev->pdev.msix_pba_mmio, false); + return 0; } @@ -2311,8 +1390,9 @@ static void vfio_teardown_msi(VFIOPCIDevice *vdev) if (vdev->msix) { msix_uninit(&vdev->pdev, - &vdev->bars[vdev->msix->table_bar].region.mem, - &vdev->bars[vdev->msix->pba_bar].region.mem); + vdev->bars[vdev->msix->table_bar].region.mem, + vdev->bars[vdev->msix->pba_bar].region.mem); + g_free(vdev->msix->pending); } } @@ -2324,71 +1404,23 @@ static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled) int i; for (i = 0; i < PCI_ROM_SLOT; i++) { - VFIOBAR *bar = &vdev->bars[i]; - - if (!bar->region.size) { - continue; - } - - memory_region_set_enabled(&bar->region.mmap_mem, enabled); - if (vdev->msix && vdev->msix->table_bar == i) { - memory_region_set_enabled(&vdev->msix->mmap_mem, enabled); - } - } -} - -static void vfio_unregister_bar(VFIOPCIDevice *vdev, int nr) -{ - VFIOBAR *bar = &vdev->bars[nr]; - - if (!bar->region.size) { - return; - } - - vfio_bar_quirk_teardown(vdev, nr); - - memory_region_del_subregion(&bar->region.mem, &bar->region.mmap_mem); - - if (vdev->msix && vdev->msix->table_bar == nr) { - memory_region_del_subregion(&bar->region.mem, &vdev->msix->mmap_mem); + vfio_region_mmaps_set_enabled(&vdev->bars[i].region, enabled); } } -static void vfio_unmap_bar(VFIOPCIDevice *vdev, int nr) +static void vfio_bar_setup(VFIOPCIDevice *vdev, int nr) { VFIOBAR *bar = &vdev->bars[nr]; - if (!bar->region.size) { - return; - } - - vfio_bar_quirk_free(vdev, nr); - - munmap(bar->region.mmap, memory_region_size(&bar->region.mmap_mem)); - - if (vdev->msix && vdev->msix->table_bar == nr) { - munmap(vdev->msix->mmap, memory_region_size(&vdev->msix->mmap_mem)); - } -} - -static void vfio_map_bar(VFIOPCIDevice *vdev, int nr) -{ - VFIOBAR *bar = &vdev->bars[nr]; - uint64_t size = bar->region.size; - char name[64]; uint32_t pci_bar; uint8_t type; int ret; /* Skip both unimplemented BARs and the upper half of 64bit BARS. */ - if (!size) { + if (!bar->region.size) { return; } - snprintf(name, sizeof(name), "VFIO %04x:%02x:%02x.%x BAR %d", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function, nr); - /* Determine what type of BAR this is for registration */ ret = pread(vdev->vbasedev.fd, &pci_bar, sizeof(pci_bar), vdev->config_offset + PCI_BASE_ADDRESS_0 + (4 * nr)); @@ -2403,102 +1435,78 @@ static void vfio_map_bar(VFIOPCIDevice *vdev, int nr) type = pci_bar & (bar->ioport ? ~PCI_BASE_ADDRESS_IO_MASK : ~PCI_BASE_ADDRESS_MEM_MASK); - /* A "slow" read/write mapping underlies all BARs */ - memory_region_init_io(&bar->region.mem, OBJECT(vdev), &vfio_region_ops, - bar, name, size); - pci_register_bar(&vdev->pdev, nr, type, &bar->region.mem); - - /* - * We can't mmap areas overlapping the MSIX vector table, so we - * potentially insert a direct-mapped subregion before and after it. - */ - if (vdev->msix && vdev->msix->table_bar == nr) { - size = vdev->msix->table_offset & qemu_real_host_page_mask; - } - - strncat(name, " mmap", sizeof(name) - strlen(name) - 1); - if (vfio_mmap_region(OBJECT(vdev), &bar->region, &bar->region.mem, - &bar->region.mmap_mem, &bar->region.mmap, - size, 0, name)) { - error_report("%s unsupported. Performance may be slow", name); - } - - if (vdev->msix && vdev->msix->table_bar == nr) { - uint64_t start; - - start = REAL_HOST_PAGE_ALIGN((uint64_t)vdev->msix->table_offset + - (vdev->msix->entries * - PCI_MSIX_ENTRY_SIZE)); - - size = start < bar->region.size ? bar->region.size - start : 0; - strncat(name, " msix-hi", sizeof(name) - strlen(name) - 1); - /* VFIOMSIXInfo contains another MemoryRegion for this mapping */ - if (vfio_mmap_region(OBJECT(vdev), &bar->region, &bar->region.mem, - &vdev->msix->mmap_mem, - &vdev->msix->mmap, size, start, name)) { - error_report("%s unsupported. Performance may be slow", name); - } + if (vfio_region_mmap(&bar->region)) { + error_report("Failed to mmap %s BAR %d. Performance may be slow", + vdev->vbasedev.name, nr); } vfio_bar_quirk_setup(vdev, nr); + + pci_register_bar(&vdev->pdev, nr, type, bar->region.mem); } -static void vfio_map_bars(VFIOPCIDevice *vdev) +static void vfio_bars_setup(VFIOPCIDevice *vdev) { int i; for (i = 0; i < PCI_ROM_SLOT; i++) { - vfio_map_bar(vdev, i); + vfio_bar_setup(vdev, i); } - if (vdev->has_vga) { - memory_region_init_io(&vdev->vga.region[QEMU_PCI_VGA_MEM].mem, + if (vdev->vga) { + memory_region_init_io(&vdev->vga->region[QEMU_PCI_VGA_MEM].mem, OBJECT(vdev), &vfio_vga_ops, - &vdev->vga.region[QEMU_PCI_VGA_MEM], + &vdev->vga->region[QEMU_PCI_VGA_MEM], "vfio-vga-mmio@0xa0000", QEMU_PCI_VGA_MEM_SIZE); - memory_region_init_io(&vdev->vga.region[QEMU_PCI_VGA_IO_LO].mem, + memory_region_init_io(&vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem, OBJECT(vdev), &vfio_vga_ops, - &vdev->vga.region[QEMU_PCI_VGA_IO_LO], + &vdev->vga->region[QEMU_PCI_VGA_IO_LO], "vfio-vga-io@0x3b0", QEMU_PCI_VGA_IO_LO_SIZE); - memory_region_init_io(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem, + memory_region_init_io(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem, OBJECT(vdev), &vfio_vga_ops, - &vdev->vga.region[QEMU_PCI_VGA_IO_HI], + &vdev->vga->region[QEMU_PCI_VGA_IO_HI], "vfio-vga-io@0x3c0", QEMU_PCI_VGA_IO_HI_SIZE); - pci_register_vga(&vdev->pdev, &vdev->vga.region[QEMU_PCI_VGA_MEM].mem, - &vdev->vga.region[QEMU_PCI_VGA_IO_LO].mem, - &vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem); + pci_register_vga(&vdev->pdev, &vdev->vga->region[QEMU_PCI_VGA_MEM].mem, + &vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem, + &vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem); vfio_vga_quirk_setup(vdev); } } -static void vfio_unregister_bars(VFIOPCIDevice *vdev) +static void vfio_bars_exit(VFIOPCIDevice *vdev) { int i; for (i = 0; i < PCI_ROM_SLOT; i++) { - vfio_unregister_bar(vdev, i); + vfio_bar_quirk_exit(vdev, i); + vfio_region_exit(&vdev->bars[i].region); } - if (vdev->has_vga) { - vfio_vga_quirk_teardown(vdev); + if (vdev->vga) { pci_unregister_vga(&vdev->pdev); + vfio_vga_quirk_exit(vdev); } } -static void vfio_unmap_bars(VFIOPCIDevice *vdev) +static void vfio_bars_finalize(VFIOPCIDevice *vdev) { int i; for (i = 0; i < PCI_ROM_SLOT; i++) { - vfio_unmap_bar(vdev, i); + vfio_bar_quirk_finalize(vdev, i); + vfio_region_finalize(&vdev->bars[i].region); } - if (vdev->has_vga) { - vfio_vga_quirk_free(vdev); + if (vdev->vga) { + vfio_vga_quirk_finalize(vdev); + for (i = 0; i < ARRAY_SIZE(vdev->vga->region); i++) { + object_unparent(OBJECT(&vdev->vga->region[i].mem)); + } + g_free(vdev->vga); } } @@ -2507,10 +1515,11 @@ static void vfio_unmap_bars(VFIOPCIDevice *vdev) */ static uint8_t vfio_std_cap_max_size(PCIDevice *pdev, uint8_t pos) { - uint8_t tmp, next = 0xff; + uint8_t tmp; + uint16_t next = PCI_CONFIG_SPACE_SIZE; for (tmp = pdev->config[PCI_CAPABILITY_LIST]; tmp; - tmp = pdev->config[tmp + 1]) { + tmp = pdev->config[tmp + PCI_CAP_LIST_NEXT]) { if (tmp > pos && tmp < next) { next = tmp; } @@ -2563,13 +1572,38 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size) } if (!pci_bus_is_express(vdev->pdev.bus)) { + PCIBus *bus = vdev->pdev.bus; + PCIDevice *bridge; + /* - * Use express capability as-is on PCI bus. It doesn't make much - * sense to even expose, but some drivers (ex. tg3) depend on it - * and guests don't seem to be particular about it. We'll need - * to revist this or force express devices to express buses if we - * ever expose an IOMMU to the guest. + * Traditionally PCI device assignment exposes the PCIe capability + * as-is on non-express buses. The reason being that some drivers + * simply assume that it's there, for example tg3. However when + * we're running on a native PCIe machine type, like Q35, we need + * to hide the PCIe capability. The reason for this is twofold; + * first Windows guests get a Code 10 error when the PCIe capability + * is exposed in this configuration. Therefore express devices won't + * work at all unless they're attached to express buses in the VM. + * Second, a native PCIe machine introduces the possibility of fine + * granularity IOMMUs supporting both translation and isolation. + * Guest code to discover the IOMMU visibility of a device, such as + * IOMMU grouping code on Linux, is very aware of device types and + * valid transitions between bus types. An express device on a non- + * express bus is not a valid combination on bare metal systems. + * + * Drivers that require a PCIe capability to make the device + * functional are simply going to need to have their devices placed + * on a PCIe bus in the VM. */ + while (!pci_bus_is_root(bus)) { + bridge = pci_bridge_get_device(bus); + bus = bridge->bus; + } + + if (pci_bus_is_express(bus)) { + return 0; + } + } else if (pci_bus_is_root(vdev->pdev.bus)) { /* * On a Root Complex bus Endpoints become Root Complex Integrated @@ -2674,7 +1708,7 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos) int ret; cap_id = pdev->config[pos]; - next = pdev->config[pos + 1]; + next = pdev->config[pos + PCI_CAP_LIST_NEXT]; /* * If it becomes important to configure capabilities to their actual @@ -2688,7 +1722,7 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos) * pci_add_capability always inserts the new capability at the head * of the chain. Therefore to end up with a chain that matches the * physical device, we insert from the end by making this recursive. - * This is also why we pre-caclulate size above as cached config space + * This is also why we pre-calculate size above as cached config space * will be changed as we unwind the stack. */ if (next) { @@ -2704,18 +1738,18 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos) } /* Use emulated next pointer to allow dropping caps */ - pci_set_byte(vdev->emulated_config_bits + pos + 1, 0xff); + pci_set_byte(vdev->emulated_config_bits + pos + PCI_CAP_LIST_NEXT, 0xff); switch (cap_id) { case PCI_CAP_ID_MSI: - ret = vfio_setup_msi(vdev, pos); + ret = vfio_msi_setup(vdev, pos); break; case PCI_CAP_ID_EXP: vfio_check_pcie_flr(vdev, pos); ret = vfio_setup_pcie_cap(vdev, pos, size); break; case PCI_CAP_ID_MSIX: - ret = vfio_setup_msix(vdev, pos); + ret = vfio_msix_setup(vdev, pos); break; case PCI_CAP_ID_PM: vfio_check_pm_reset(vdev, pos); @@ -2732,9 +1766,8 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos) } if (ret < 0) { - error_report("vfio: %04x:%02x:%02x.%x Error adding PCI capability " - "0x%x[0x%x]@0x%x: %d", vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function, + error_report("vfio: %s Error adding PCI capability " + "0x%x[0x%x]@0x%x: %d", vdev->vbasedev.name, cap_id, size, pos, ret); return ret; } @@ -2793,14 +1826,17 @@ static void vfio_pci_pre_reset(VFIOPCIDevice *vdev) static void vfio_pci_post_reset(VFIOPCIDevice *vdev) { - vfio_enable_intx(vdev); + vfio_intx_enable(vdev); } -static bool vfio_pci_host_match(PCIHostDeviceAddress *host1, - PCIHostDeviceAddress *host2) +static bool vfio_pci_host_match(PCIHostDeviceAddress *addr, const char *name) { - return (host1->domain == host2->domain && host1->bus == host2->bus && - host1->slot == host2->slot && host1->function == host2->function); + char tmp[13]; + + sprintf(tmp, "%04x:%02x:%02x.%1x", addr->domain, + addr->bus, addr->slot, addr->function); + + return (strcmp(tmp, name) == 0); } static int vfio_pci_hot_reset(VFIOPCIDevice *vdev, bool single) @@ -2825,9 +1861,8 @@ static int vfio_pci_hot_reset(VFIOPCIDevice *vdev, bool single) if (ret && errno != ENOSPC) { ret = -errno; if (!vdev->has_pm_reset) { - error_report("vfio: Cannot reset device %04x:%02x:%02x.%x, " - "no available reset mechanism.", vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function); + error_report("vfio: Cannot reset device %s, " + "no available reset mechanism.", vdev->vbasedev.name); } goto out_single; } @@ -2860,7 +1895,7 @@ static int vfio_pci_hot_reset(VFIOPCIDevice *vdev, bool single) trace_vfio_pci_hot_reset_dep_devices(host.domain, host.bus, host.slot, host.function, devices[i].group_id); - if (vfio_pci_host_match(&host, &vdev->host)) { + if (vfio_pci_host_match(&host, vdev->vbasedev.name)) { continue; } @@ -2886,7 +1921,7 @@ static int vfio_pci_hot_reset(VFIOPCIDevice *vdev, bool single) continue; } tmp = container_of(vbasedev_iter, VFIOPCIDevice, vbasedev); - if (vfio_pci_host_match(&host, &tmp->host)) { + if (vfio_pci_host_match(&host, tmp->vbasedev.name)) { if (single) { ret = -EINVAL; goto out_single; @@ -2948,7 +1983,7 @@ out: host.slot = PCI_SLOT(devices[i].devfn); host.function = PCI_FUNC(devices[i].devfn); - if (vfio_pci_host_match(&host, &vdev->host)) { + if (vfio_pci_host_match(&host, vdev->vbasedev.name)) { continue; } @@ -2967,7 +2002,7 @@ out: continue; } tmp = container_of(vbasedev_iter, VFIOPCIDevice, vbasedev); - if (vfio_pci_host_match(&host, &tmp->host)) { + if (vfio_pci_host_match(&host, tmp->vbasedev.name)) { vfio_pci_post_reset(tmp); break; } @@ -3017,13 +2052,59 @@ static void vfio_pci_compute_needs_reset(VFIODevice *vbasedev) static VFIODeviceOps vfio_pci_ops = { .vfio_compute_needs_reset = vfio_pci_compute_needs_reset, .vfio_hot_reset_multi = vfio_pci_hot_reset_multi, - .vfio_eoi = vfio_eoi, + .vfio_eoi = vfio_intx_eoi, }; +int vfio_populate_vga(VFIOPCIDevice *vdev) +{ + VFIODevice *vbasedev = &vdev->vbasedev; + struct vfio_region_info *reg_info; + int ret; + + if (vbasedev->num_regions > VFIO_PCI_VGA_REGION_INDEX) { + ret = vfio_get_region_info(vbasedev, + VFIO_PCI_VGA_REGION_INDEX, ®_info); + if (ret) { + return ret; + } + + if (!(reg_info->flags & VFIO_REGION_INFO_FLAG_READ) || + !(reg_info->flags & VFIO_REGION_INFO_FLAG_WRITE) || + reg_info->size < 0xbffff + 1) { + error_report("vfio: Unexpected VGA info, flags 0x%lx, size 0x%lx", + (unsigned long)reg_info->flags, + (unsigned long)reg_info->size); + g_free(reg_info); + return -EINVAL; + } + + vdev->vga = g_new0(VFIOVGA, 1); + + vdev->vga->fd_offset = reg_info->offset; + vdev->vga->fd = vdev->vbasedev.fd; + + g_free(reg_info); + + vdev->vga->region[QEMU_PCI_VGA_MEM].offset = QEMU_PCI_VGA_MEM_BASE; + vdev->vga->region[QEMU_PCI_VGA_MEM].nr = QEMU_PCI_VGA_MEM; + QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_MEM].quirks); + + vdev->vga->region[QEMU_PCI_VGA_IO_LO].offset = QEMU_PCI_VGA_IO_LO_BASE; + vdev->vga->region[QEMU_PCI_VGA_IO_LO].nr = QEMU_PCI_VGA_IO_LO; + QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_IO_LO].quirks); + + vdev->vga->region[QEMU_PCI_VGA_IO_HI].offset = QEMU_PCI_VGA_IO_HI_BASE; + vdev->vga->region[QEMU_PCI_VGA_IO_HI].nr = QEMU_PCI_VGA_IO_HI; + QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].quirks); + } + + return 0; +} + static int vfio_populate_device(VFIOPCIDevice *vdev) { VFIODevice *vbasedev = &vdev->vbasedev; - struct vfio_region_info reg_info = { .argsz = sizeof(reg_info) }; + struct vfio_region_info *reg_info; struct vfio_irq_info irq_info = { .argsz = sizeof(irq_info) }; int i, ret = -1; @@ -3045,85 +2126,47 @@ static int vfio_populate_device(VFIOPCIDevice *vdev) } for (i = VFIO_PCI_BAR0_REGION_INDEX; i < VFIO_PCI_ROM_REGION_INDEX; i++) { - reg_info.index = i; + char *name = g_strdup_printf("%s BAR %d", vbasedev->name, i); + + ret = vfio_region_setup(OBJECT(vdev), vbasedev, + &vdev->bars[i].region, i, name); + g_free(name); - ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, ®_info); if (ret) { error_report("vfio: Error getting region %d info: %m", i); goto error; } - trace_vfio_populate_device_region(vbasedev->name, i, - (unsigned long)reg_info.size, - (unsigned long)reg_info.offset, - (unsigned long)reg_info.flags); - - vdev->bars[i].region.vbasedev = vbasedev; - vdev->bars[i].region.flags = reg_info.flags; - vdev->bars[i].region.size = reg_info.size; - vdev->bars[i].region.fd_offset = reg_info.offset; - vdev->bars[i].region.nr = i; QLIST_INIT(&vdev->bars[i].quirks); } - reg_info.index = VFIO_PCI_CONFIG_REGION_INDEX; - - ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_REGION_INFO, ®_info); + ret = vfio_get_region_info(vbasedev, + VFIO_PCI_CONFIG_REGION_INDEX, ®_info); if (ret) { error_report("vfio: Error getting config info: %m"); goto error; } trace_vfio_populate_device_config(vdev->vbasedev.name, - (unsigned long)reg_info.size, - (unsigned long)reg_info.offset, - (unsigned long)reg_info.flags); + (unsigned long)reg_info->size, + (unsigned long)reg_info->offset, + (unsigned long)reg_info->flags); - vdev->config_size = reg_info.size; + vdev->config_size = reg_info->size; if (vdev->config_size == PCI_CONFIG_SPACE_SIZE) { vdev->pdev.cap_present &= ~QEMU_PCI_CAP_EXPRESS; } - vdev->config_offset = reg_info.offset; + vdev->config_offset = reg_info->offset; - if ((vdev->features & VFIO_FEATURE_ENABLE_VGA) && - vbasedev->num_regions > VFIO_PCI_VGA_REGION_INDEX) { - struct vfio_region_info vga_info = { - .argsz = sizeof(vga_info), - .index = VFIO_PCI_VGA_REGION_INDEX, - }; + g_free(reg_info); - ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_REGION_INFO, &vga_info); + if (vdev->features & VFIO_FEATURE_ENABLE_VGA) { + ret = vfio_populate_vga(vdev); if (ret) { error_report( "vfio: Device does not support requested feature x-vga"); goto error; } - - if (!(vga_info.flags & VFIO_REGION_INFO_FLAG_READ) || - !(vga_info.flags & VFIO_REGION_INFO_FLAG_WRITE) || - vga_info.size < 0xbffff + 1) { - error_report("vfio: Unexpected VGA info, flags 0x%lx, size 0x%lx", - (unsigned long)vga_info.flags, - (unsigned long)vga_info.size); - goto error; - } - - vdev->vga.fd_offset = vga_info.offset; - vdev->vga.fd = vdev->vbasedev.fd; - - vdev->vga.region[QEMU_PCI_VGA_MEM].offset = QEMU_PCI_VGA_MEM_BASE; - vdev->vga.region[QEMU_PCI_VGA_MEM].nr = QEMU_PCI_VGA_MEM; - QLIST_INIT(&vdev->vga.region[QEMU_PCI_VGA_MEM].quirks); - - vdev->vga.region[QEMU_PCI_VGA_IO_LO].offset = QEMU_PCI_VGA_IO_LO_BASE; - vdev->vga.region[QEMU_PCI_VGA_IO_LO].nr = QEMU_PCI_VGA_IO_LO; - QLIST_INIT(&vdev->vga.region[QEMU_PCI_VGA_IO_LO].quirks); - - vdev->vga.region[QEMU_PCI_VGA_IO_HI].offset = QEMU_PCI_VGA_IO_HI_BASE; - vdev->vga.region[QEMU_PCI_VGA_IO_HI].nr = QEMU_PCI_VGA_IO_HI; - QLIST_INIT(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks); - - vdev->has_vga = true; } irq_info.index = VFIO_PCI_ERR_IRQ_INDEX; @@ -3148,11 +2191,8 @@ error: static void vfio_put_device(VFIOPCIDevice *vdev) { g_free(vdev->vbasedev.name); - if (vdev->msix) { - object_unparent(OBJECT(&vdev->msix->mmap_mem)); - g_free(vdev->msix); - vdev->msix = NULL; - } + g_free(vdev->msix); + vfio_put_base_device(&vdev->vbasedev); } @@ -3173,10 +2213,7 @@ static void vfio_err_notifier_handler(void *opaque) * guest to contain the error. */ - error_report("%s(%04x:%02x:%02x.%x) Unrecoverable error detected. " - "Please collect any data possible and then kill the guest", - __func__, vdev->host.domain, vdev->host.bus, - vdev->host.slot, vdev->host.function); + error_report("%s(%s) Unrecoverable error detected. Please collect any data possible and then kill the guest", __func__, vdev->vbasedev.name); vm_stop(RUN_STATE_INTERNAL_ERROR); } @@ -3352,203 +2389,48 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev) vdev->req_enabled = false; } -/* - * AMD Radeon PCI config reset, based on Linux: - * drivers/gpu/drm/radeon/ci_smc.c:ci_is_smc_running() - * drivers/gpu/drm/radeon/radeon_device.c:radeon_pci_config_reset - * drivers/gpu/drm/radeon/ci_smc.c:ci_reset_smc() - * drivers/gpu/drm/radeon/ci_smc.c:ci_stop_smc_clock() - * IDs: include/drm/drm_pciids.h - * Registers: http://cgit.freedesktop.org/~agd5f/linux/commit/?id=4e2aa447f6f0 - * - * Bonaire and Hawaii GPUs do not respond to a bus reset. This is a bug in the - * hardware that should be fixed on future ASICs. The symptom of this is that - * once the accerlated driver loads, Windows guests will bsod on subsequent - * attmpts to load the driver, such as after VM reset or shutdown/restart. To - * work around this, we do an AMD specific PCI config reset, followed by an SMC - * reset. The PCI config reset only works if SMC firmware is running, so we - * have a dependency on the state of the device as to whether this reset will - * be effective. There are still cases where we won't be able to kick the - * device into working, but this greatly improves the usability overall. The - * config reset magic is relatively common on AMD GPUs, but the setup and SMC - * poking is largely ASIC specific. - */ -static bool vfio_radeon_smc_is_running(VFIOPCIDevice *vdev) -{ - uint32_t clk, pc_c; - - /* - * Registers 200h and 204h are index and data registers for acessing - * indirect configuration registers within the device. - */ - vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000004, 4); - clk = vfio_region_read(&vdev->bars[5].region, 0x204, 4); - vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000370, 4); - pc_c = vfio_region_read(&vdev->bars[5].region, 0x204, 4); - - return (!(clk & 1) && (0x20100 <= pc_c)); -} - -/* - * The scope of a config reset is controlled by a mode bit in the misc register - * and a fuse, exposed as a bit in another register. The fuse is the default - * (0 = GFX, 1 = whole GPU), the misc bit is a toggle, with the forumula - * scope = !(misc ^ fuse), where the resulting scope is defined the same as - * the fuse. A truth table therefore tells us that if misc == fuse, we need - * to flip the value of the bit in the misc register. - */ -static void vfio_radeon_set_gfx_only_reset(VFIOPCIDevice *vdev) -{ - uint32_t misc, fuse; - bool a, b; - - vfio_region_write(&vdev->bars[5].region, 0x200, 0xc00c0000, 4); - fuse = vfio_region_read(&vdev->bars[5].region, 0x204, 4); - b = fuse & 64; - - vfio_region_write(&vdev->bars[5].region, 0x200, 0xc0000010, 4); - misc = vfio_region_read(&vdev->bars[5].region, 0x204, 4); - a = misc & 2; - - if (a == b) { - vfio_region_write(&vdev->bars[5].region, 0x204, misc ^ 2, 4); - vfio_region_read(&vdev->bars[5].region, 0x204, 4); /* flush */ - } -} - -static int vfio_radeon_reset(VFIOPCIDevice *vdev) -{ - PCIDevice *pdev = &vdev->pdev; - int i, ret = 0; - uint32_t data; - - /* Defer to a kernel implemented reset */ - if (vdev->vbasedev.reset_works) { - return -ENODEV; - } - - /* Enable only memory BAR access */ - vfio_pci_write_config(pdev, PCI_COMMAND, PCI_COMMAND_MEMORY, 2); - - /* Reset only works if SMC firmware is loaded and running */ - if (!vfio_radeon_smc_is_running(vdev)) { - ret = -EINVAL; - goto out; - } - - /* Make sure only the GFX function is reset */ - vfio_radeon_set_gfx_only_reset(vdev); - - /* AMD PCI config reset */ - vfio_pci_write_config(pdev, 0x7c, 0x39d5e86b, 4); - usleep(100); - - /* Read back the memory size to make sure we're out of reset */ - for (i = 0; i < 100000; i++) { - if (vfio_region_read(&vdev->bars[5].region, 0x5428, 4) != 0xffffffff) { - break; - } - usleep(1); - } - - /* Reset SMC */ - vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000000, 4); - data = vfio_region_read(&vdev->bars[5].region, 0x204, 4); - data |= 1; - vfio_region_write(&vdev->bars[5].region, 0x204, data, 4); - - /* Disable SMC clock */ - vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000004, 4); - data = vfio_region_read(&vdev->bars[5].region, 0x204, 4); - data |= 1; - vfio_region_write(&vdev->bars[5].region, 0x204, data, 4); - -out: - /* Restore PCI command register */ - vfio_pci_write_config(pdev, PCI_COMMAND, 0, 2); - - return ret; -} - -static void vfio_setup_resetfn(VFIOPCIDevice *vdev) -{ - PCIDevice *pdev = &vdev->pdev; - uint16_t vendor, device; - - vendor = pci_get_word(pdev->config + PCI_VENDOR_ID); - device = pci_get_word(pdev->config + PCI_DEVICE_ID); - - switch (vendor) { - case 0x1002: - switch (device) { - /* Bonaire */ - case 0x6649: /* Bonaire [FirePro W5100] */ - case 0x6650: - case 0x6651: - case 0x6658: /* Bonaire XTX [Radeon R7 260X] */ - case 0x665c: /* Bonaire XT [Radeon HD 7790/8770 / R9 260 OEM] */ - case 0x665d: /* Bonaire [Radeon R7 200 Series] */ - /* Hawaii */ - case 0x67A0: /* Hawaii XT GL [FirePro W9100] */ - case 0x67A1: /* Hawaii PRO GL [FirePro W8100] */ - case 0x67A2: - case 0x67A8: - case 0x67A9: - case 0x67AA: - case 0x67B0: /* Hawaii XT [Radeon R9 290X] */ - case 0x67B1: /* Hawaii PRO [Radeon R9 290] */ - case 0x67B8: - case 0x67B9: - case 0x67BA: - case 0x67BE: - vdev->resetfn = vfio_radeon_reset; - break; - } - break; - } -} - static int vfio_initfn(PCIDevice *pdev) { VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); VFIODevice *vbasedev_iter; VFIOGroup *group; - char path[PATH_MAX], iommu_group_path[PATH_MAX], *group_name; + char *tmp, group_path[PATH_MAX], *group_name; ssize_t len; struct stat st; int groupid; int ret; - /* Check that the host device exists */ - snprintf(path, sizeof(path), - "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); - if (stat(path, &st) < 0) { - error_report("vfio: error: no such host device: %s", path); + if (!vdev->vbasedev.sysfsdev) { + vdev->vbasedev.sysfsdev = + g_strdup_printf("/sys/bus/pci/devices/%04x:%02x:%02x.%01x", + vdev->host.domain, vdev->host.bus, + vdev->host.slot, vdev->host.function); + } + + if (stat(vdev->vbasedev.sysfsdev, &st) < 0) { + error_report("vfio: error: no such host device: %s", + vdev->vbasedev.sysfsdev); return -errno; } + vdev->vbasedev.name = g_strdup(basename(vdev->vbasedev.sysfsdev)); vdev->vbasedev.ops = &vfio_pci_ops; - vdev->vbasedev.type = VFIO_DEVICE_TYPE_PCI; - vdev->vbasedev.name = g_strdup_printf("%04x:%02x:%02x.%01x", - vdev->host.domain, vdev->host.bus, - vdev->host.slot, vdev->host.function); - strncat(path, "iommu_group", sizeof(path) - strlen(path) - 1); + tmp = g_strdup_printf("%s/iommu_group", vdev->vbasedev.sysfsdev); + len = readlink(tmp, group_path, sizeof(group_path)); + g_free(tmp); - len = readlink(path, iommu_group_path, sizeof(path)); - if (len <= 0 || len >= sizeof(path)) { + if (len <= 0 || len >= sizeof(group_path)) { error_report("vfio: error no iommu_group for device"); return len < 0 ? -errno : -ENAMETOOLONG; } - iommu_group_path[len] = 0; - group_name = basename(iommu_group_path); + group_path[len] = 0; + group_name = basename(group_path); if (sscanf(group_name, "%d", &groupid) != 1) { - error_report("vfio: error reading %s: %m", path); + error_report("vfio: error reading %s: %m", group_path); return -errno; } @@ -3560,21 +2442,18 @@ static int vfio_initfn(PCIDevice *pdev) return -ENOENT; } - snprintf(path, sizeof(path), "%04x:%02x:%02x.%01x", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); - QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { if (strcmp(vbasedev_iter->name, vdev->vbasedev.name) == 0) { - error_report("vfio: error: device %s is already attached", path); + error_report("vfio: error: device %s is already attached", + vdev->vbasedev.name); vfio_put_group(group); return -EBUSY; } } - ret = vfio_get_device(group, path, &vdev->vbasedev); + ret = vfio_get_device(group, vdev->vbasedev.name, &vdev->vbasedev); if (ret) { - error_report("vfio: failed to get device %s", path); + error_report("vfio: failed to get device %s", vdev->vbasedev.name); vfio_put_group(group); return ret; } @@ -3600,6 +2479,54 @@ static int vfio_initfn(PCIDevice *pdev) /* QEMU can choose to expose the ROM or not */ memset(vdev->emulated_config_bits + PCI_ROM_ADDRESS, 0xff, 4); + /* + * The PCI spec reserves vendor ID 0xffff as an invalid value. The + * device ID is managed by the vendor and need only be a 16-bit value. + * Allow any 16-bit value for subsystem so they can be hidden or changed. + */ + if (vdev->vendor_id != PCI_ANY_ID) { + if (vdev->vendor_id >= 0xffff) { + error_report("vfio: Invalid PCI vendor ID provided"); + return -EINVAL; + } + vfio_add_emulated_word(vdev, PCI_VENDOR_ID, vdev->vendor_id, ~0); + trace_vfio_pci_emulated_vendor_id(vdev->vbasedev.name, vdev->vendor_id); + } else { + vdev->vendor_id = pci_get_word(pdev->config + PCI_VENDOR_ID); + } + + if (vdev->device_id != PCI_ANY_ID) { + if (vdev->device_id > 0xffff) { + error_report("vfio: Invalid PCI device ID provided"); + return -EINVAL; + } + vfio_add_emulated_word(vdev, PCI_DEVICE_ID, vdev->device_id, ~0); + trace_vfio_pci_emulated_device_id(vdev->vbasedev.name, vdev->device_id); + } else { + vdev->device_id = pci_get_word(pdev->config + PCI_DEVICE_ID); + } + + if (vdev->sub_vendor_id != PCI_ANY_ID) { + if (vdev->sub_vendor_id > 0xffff) { + error_report("vfio: Invalid PCI subsystem vendor ID provided"); + return -EINVAL; + } + vfio_add_emulated_word(vdev, PCI_SUBSYSTEM_VENDOR_ID, + vdev->sub_vendor_id, ~0); + trace_vfio_pci_emulated_sub_vendor_id(vdev->vbasedev.name, + vdev->sub_vendor_id); + } + + if (vdev->sub_device_id != PCI_ANY_ID) { + if (vdev->sub_device_id > 0xffff) { + error_report("vfio: Invalid PCI subsystem device ID provided"); + return -EINVAL; + } + vfio_add_emulated_word(vdev, PCI_SUBSYSTEM_ID, vdev->sub_device_id, ~0); + trace_vfio_pci_emulated_sub_device_id(vdev->vbasedev.name, + vdev->sub_device_id); + } + /* QEMU can change multi-function devices to single function, or reverse */ vdev->emulated_config_bits[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_MULTI_FUNCTION; @@ -3621,12 +2548,12 @@ static int vfio_initfn(PCIDevice *pdev) vfio_pci_size_rom(vdev); - ret = vfio_early_setup_msix(vdev); + ret = vfio_msix_early_setup(vdev); if (ret) { return ret; } - vfio_map_bars(vdev); + vfio_bars_setup(vdev); ret = vfio_add_capabilities(vdev); if (ret) { @@ -3647,8 +2574,8 @@ static int vfio_initfn(PCIDevice *pdev) if (vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1)) { vdev->intx.mmap_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, vfio_intx_mmap_enable, vdev); - pci_device_set_intx_routing_notifier(&vdev->pdev, vfio_update_irq); - ret = vfio_enable_intx(vdev); + pci_device_set_intx_routing_notifier(&vdev->pdev, vfio_intx_update); + ret = vfio_intx_enable(vdev); if (ret) { goto out_teardown; } @@ -3656,14 +2583,14 @@ static int vfio_initfn(PCIDevice *pdev) vfio_register_err_notifier(vdev); vfio_register_req_notifier(vdev); - vfio_setup_resetfn(vdev); + vfio_setup_resetfn_quirk(vdev); return 0; out_teardown: pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); vfio_teardown_msi(vdev); - vfio_unregister_bars(vdev); + vfio_bars_exit(vdev); return ret; } @@ -3673,7 +2600,7 @@ static void vfio_instance_finalize(Object *obj) VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pci_dev); VFIOGroup *group = vdev->vbasedev.group; - vfio_unmap_bars(vdev); + vfio_bars_finalize(vdev); g_free(vdev->emulated_config_bits); g_free(vdev->rom); vfio_put_device(vdev); @@ -3692,7 +2619,7 @@ static void vfio_exitfn(PCIDevice *pdev) timer_free(vdev->intx.mmap_timer); } vfio_teardown_msi(vdev); - vfio_unregister_bars(vdev); + vfio_bars_exit(vdev); } static void vfio_pci_reset(DeviceState *dev) @@ -3743,13 +2670,23 @@ 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_UINT32("x-intx-mmap-timeout-ms", VFIOPCIDevice, intx.mmap_timeout, 1100), DEFINE_PROP_BIT("x-vga", VFIOPCIDevice, features, VFIO_FEATURE_ENABLE_VGA_BIT, false), DEFINE_PROP_BIT("x-req", VFIOPCIDevice, features, VFIO_FEATURE_ENABLE_REQ_BIT, true), - DEFINE_PROP_BOOL("x-mmap", VFIOPCIDevice, vbasedev.allow_mmap, true), + DEFINE_PROP_BOOL("x-no-mmap", VFIOPCIDevice, vbasedev.no_mmap, false), + DEFINE_PROP_BOOL("x-no-kvm-intx", VFIOPCIDevice, no_kvm_intx, false), + DEFINE_PROP_BOOL("x-no-kvm-msi", VFIOPCIDevice, no_kvm_msi, false), + DEFINE_PROP_BOOL("x-no-kvm-msix", VFIOPCIDevice, no_kvm_msix, false), + DEFINE_PROP_UINT32("x-pci-vendor-id", VFIOPCIDevice, vendor_id, PCI_ANY_ID), + DEFINE_PROP_UINT32("x-pci-device-id", VFIOPCIDevice, device_id, PCI_ANY_ID), + DEFINE_PROP_UINT32("x-pci-sub-vendor-id", VFIOPCIDevice, + sub_vendor_id, PCI_ANY_ID), + DEFINE_PROP_UINT32("x-pci-sub-device-id", VFIOPCIDevice, + sub_device_id, PCI_ANY_ID), /* * TODO - support passed fds... is this necessary? * DEFINE_PROP_STRING("vfiofd", VFIOPCIDevice, vfiofd_name), diff --git a/qemu/hw/vfio/pci.h b/qemu/hw/vfio/pci.h new file mode 100644 index 000000000..3976f6854 --- /dev/null +++ b/qemu/hw/vfio/pci.h @@ -0,0 +1,162 @@ +/* + * vfio based device assignment support - PCI devices + * + * Copyright Red Hat, Inc. 2012-2015 + * + * Authors: + * Alex Williamson + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ +#ifndef HW_VFIO_VFIO_PCI_H +#define HW_VFIO_VFIO_PCI_H + +#include "qemu-common.h" +#include "exec/memory.h" +#include "hw/pci/pci.h" +#include "hw/vfio/vfio-common.h" +#include "qemu/event_notifier.h" +#include "qemu/queue.h" +#include "qemu/timer.h" + +#define PCI_ANY_ID (~0) + +struct VFIOPCIDevice; + +typedef struct VFIOQuirk { + QLIST_ENTRY(VFIOQuirk) next; + void *data; + int nr_mem; + MemoryRegion *mem; +} VFIOQuirk; + +typedef struct VFIOBAR { + VFIORegion region; + bool ioport; + bool mem64; + QLIST_HEAD(, VFIOQuirk) quirks; +} VFIOBAR; + +typedef struct VFIOVGARegion { + MemoryRegion mem; + off_t offset; + int nr; + QLIST_HEAD(, VFIOQuirk) quirks; +} VFIOVGARegion; + +typedef struct VFIOVGA { + off_t fd_offset; + int fd; + VFIOVGARegion region[QEMU_PCI_VGA_NUM_REGIONS]; +} VFIOVGA; + +typedef struct VFIOINTx { + bool pending; /* interrupt pending */ + bool kvm_accel; /* set when QEMU bypass through KVM enabled */ + uint8_t pin; /* which pin to pull for qemu_set_irq */ + EventNotifier interrupt; /* eventfd triggered on interrupt */ + EventNotifier unmask; /* eventfd for unmask on QEMU bypass */ + PCIINTxRoute route; /* routing info for QEMU bypass */ + uint32_t mmap_timeout; /* delay to re-enable mmaps after interrupt */ + QEMUTimer *mmap_timer; /* enable mmaps after periods w/o interrupts */ +} VFIOINTx; + +typedef struct VFIOMSIVector { + /* + * Two interrupt paths are configured per vector. The first, is only used + * for interrupts injected via QEMU. This is typically the non-accel path, + * but may also be used when we want QEMU to handle masking and pending + * bits. The KVM path bypasses QEMU and is therefore higher performance, + * but requires masking at the device. virq is used to track the MSI route + * through KVM, thus kvm_interrupt is only available when virq is set to a + * valid (>= 0) value. + */ + EventNotifier interrupt; + EventNotifier kvm_interrupt; + struct VFIOPCIDevice *vdev; /* back pointer to device */ + int virq; + bool use; +} VFIOMSIVector; + +enum { + VFIO_INT_NONE = 0, + VFIO_INT_INTx = 1, + VFIO_INT_MSI = 2, + VFIO_INT_MSIX = 3, +}; + +/* Cache of MSI-X setup plus extra mmap and memory region for split BAR map */ +typedef struct VFIOMSIXInfo { + uint8_t table_bar; + uint8_t pba_bar; + uint16_t entries; + uint32_t table_offset; + uint32_t pba_offset; + MemoryRegion mmap_mem; + void *mmap; + unsigned long *pending; +} VFIOMSIXInfo; + +typedef struct VFIOPCIDevice { + PCIDevice pdev; + VFIODevice vbasedev; + VFIOINTx intx; + unsigned int config_size; + uint8_t *emulated_config_bits; /* QEMU emulated bits, little-endian */ + off_t config_offset; /* Offset of config space region within device fd */ + unsigned int rom_size; + off_t rom_offset; /* Offset of ROM region within device fd */ + void *rom; + int msi_cap_size; + VFIOMSIVector *msi_vectors; + VFIOMSIXInfo *msix; + int nr_vectors; /* Number of MSI/MSIX vectors currently in use */ + int interrupt; /* Current interrupt type */ + VFIOBAR bars[PCI_NUM_REGIONS - 1]; /* No ROM */ + VFIOVGA *vga; /* 0xa0000, 0x3b0, 0x3c0 */ + PCIHostDeviceAddress host; + EventNotifier err_notifier; + EventNotifier req_notifier; + int (*resetfn)(struct VFIOPCIDevice *); + uint32_t vendor_id; + uint32_t device_id; + uint32_t sub_vendor_id; + uint32_t sub_device_id; + uint32_t features; +#define VFIO_FEATURE_ENABLE_VGA_BIT 0 +#define VFIO_FEATURE_ENABLE_VGA (1 << VFIO_FEATURE_ENABLE_VGA_BIT) +#define VFIO_FEATURE_ENABLE_REQ_BIT 1 +#define VFIO_FEATURE_ENABLE_REQ (1 << VFIO_FEATURE_ENABLE_REQ_BIT) + int32_t bootindex; + uint8_t pm_cap; + bool has_vga; + bool pci_aer; + bool req_enabled; + bool has_flr; + bool has_pm_reset; + bool rom_read_failed; + bool no_kvm_intx; + bool no_kvm_msi; + bool no_kvm_msix; +} VFIOPCIDevice; + +uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len); +void vfio_pci_write_config(PCIDevice *pdev, + uint32_t addr, uint32_t val, int len); + +uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size); +void vfio_vga_write(void *opaque, hwaddr addr, uint64_t data, unsigned size); + +bool vfio_blacklist_opt_rom(VFIOPCIDevice *vdev); +void vfio_vga_quirk_setup(VFIOPCIDevice *vdev); +void vfio_vga_quirk_exit(VFIOPCIDevice *vdev); +void vfio_vga_quirk_finalize(VFIOPCIDevice *vdev); +void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr); +void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr); +void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr); +void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev); + +int vfio_populate_vga(VFIOPCIDevice *vdev); + +#endif /* HW_VFIO_VFIO_PCI_H */ diff --git a/qemu/hw/vfio/platform.c b/qemu/hw/vfio/platform.c index 60365d127..1798a00a3 100644 --- a/qemu/hw/vfio/platform.c +++ b/qemu/hw/vfio/platform.c @@ -14,6 +14,8 @@ * Copyright Red Hat, Inc. 2012 */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include #include @@ -32,6 +34,11 @@ * Functions used whatever the injection method */ +static inline bool vfio_irq_is_automasked(VFIOINTp *intp) +{ + return intp->flags & VFIO_IRQ_INFO_AUTOMASKED; +} + /** * vfio_init_intp - allocate, initialize the IRQ struct pointer * and add it into the list of IRQs @@ -57,18 +64,25 @@ static VFIOINTp *vfio_init_intp(VFIODevice *vbasedev, sysbus_init_irq(sbdev, &intp->qemuirq); /* Get an eventfd for trigger */ - ret = event_notifier_init(&intp->interrupt, 0); + intp->interrupt = g_malloc0(sizeof(EventNotifier)); + ret = event_notifier_init(intp->interrupt, 0); if (ret) { + g_free(intp->interrupt); g_free(intp); error_report("vfio: Error: trigger event_notifier_init failed "); return NULL; } - /* Get an eventfd for resample/unmask */ - ret = event_notifier_init(&intp->unmask, 0); - if (ret) { - g_free(intp); - error_report("vfio: Error: resamplefd event_notifier_init failed"); - return NULL; + if (vfio_irq_is_automasked(intp)) { + /* Get an eventfd for resample/unmask */ + intp->unmask = g_malloc0(sizeof(EventNotifier)); + ret = event_notifier_init(intp->unmask, 0); + if (ret) { + g_free(intp->interrupt); + g_free(intp->unmask); + g_free(intp); + error_report("vfio: Error: resamplefd event_notifier_init failed"); + return NULL; + } } QLIST_INSERT_HEAD(&vdev->intp_list, intp, next); @@ -100,7 +114,7 @@ static int vfio_set_trigger_eventfd(VFIOINTp *intp, irq_set->start = 0; irq_set->count = 1; pfd = (int32_t *)&irq_set->data; - *pfd = event_notifier_get_fd(&intp->interrupt); + *pfd = event_notifier_get_fd(intp->interrupt); qemu_set_fd_handler(*pfd, (IOHandler *)handler, NULL, intp); ret = ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set); g_free(irq_set); @@ -130,12 +144,8 @@ static void vfio_mmap_set_enabled(VFIOPlatformDevice *vdev, bool enabled) { int i; - trace_vfio_platform_mmap_set_enabled(enabled); - for (i = 0; i < vdev->vbasedev.num_regions; i++) { - VFIORegion *region = vdev->regions[i]; - - memory_region_set_enabled(®ion->mmap_mem, enabled); + vfio_region_mmaps_set_enabled(vdev->regions[i], enabled); } } @@ -182,7 +192,7 @@ static void vfio_intp_mmap_enable(void *opaque) static void vfio_intp_inject_pending_lockheld(VFIOINTp *intp) { trace_vfio_platform_intp_inject_pending_lockheld(intp->pin, - event_notifier_get_fd(&intp->interrupt)); + event_notifier_get_fd(intp->interrupt)); intp->state = VFIO_IRQ_ACTIVE; @@ -224,18 +234,18 @@ static void vfio_intp_interrupt(VFIOINTp *intp) trace_vfio_intp_interrupt_set_pending(intp->pin); QSIMPLEQ_INSERT_TAIL(&vdev->pending_intp_queue, intp, pqnext); - ret = event_notifier_test_and_clear(&intp->interrupt); + ret = event_notifier_test_and_clear(intp->interrupt); qemu_mutex_unlock(&vdev->intp_mutex); return; } trace_vfio_platform_intp_interrupt(intp->pin, - event_notifier_get_fd(&intp->interrupt)); + event_notifier_get_fd(intp->interrupt)); - ret = event_notifier_test_and_clear(&intp->interrupt); + ret = event_notifier_test_and_clear(intp->interrupt); if (!ret) { - error_report("Error when clearing fd=%d (ret = %d)\n", - event_notifier_get_fd(&intp->interrupt), ret); + error_report("Error when clearing fd=%d (ret = %d)", + event_notifier_get_fd(intp->interrupt), ret); } intp->state = VFIO_IRQ_ACTIVE; @@ -283,13 +293,13 @@ static void vfio_platform_eoi(VFIODevice *vbasedev) QLIST_FOREACH(intp, &vdev->intp_list, next) { if (intp->state == VFIO_IRQ_ACTIVE) { trace_vfio_platform_eoi(intp->pin, - event_notifier_get_fd(&intp->interrupt)); + event_notifier_get_fd(intp->interrupt)); intp->state = VFIO_IRQ_INACTIVE; /* deassert the virtual IRQ */ qemu_set_irq(intp->qemuirq, 0); - if (intp->flags & VFIO_IRQ_INFO_AUTOMASKED) { + if (vfio_irq_is_automasked(intp)) { /* unmasks the physical level-sensitive IRQ */ vfio_unmask_single_irqindex(vbasedev, intp->pin); } @@ -310,18 +320,29 @@ static void vfio_platform_eoi(VFIODevice *vbasedev) /** * vfio_start_eventfd_injection - starts the virtual IRQ injection using * user-side handled eventfds - * @intp: the IRQ struct pointer + * @sbdev: the sysbus device handle + * @irq: the qemu irq handle */ -static int vfio_start_eventfd_injection(VFIOINTp *intp) +static void vfio_start_eventfd_injection(SysBusDevice *sbdev, qemu_irq irq) { int ret; + VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); + VFIOINTp *intp; + + QLIST_FOREACH(intp, &vdev->intp_list, next) { + if (intp->qemuirq == irq) { + break; + } + } + assert(intp); ret = vfio_set_trigger_eventfd(intp, vfio_intp_interrupt); if (ret) { - error_report("vfio: Error: Failed to pass IRQ fd to the driver: %m"); + error_report("vfio: failed to start eventfd signaling for IRQ %d: %m", + intp->pin); + abort(); } - return ret; } /* @@ -349,7 +370,7 @@ static int vfio_set_resample_eventfd(VFIOINTp *intp) irq_set->start = 0; irq_set->count = 1; pfd = (int32_t *)&irq_set->data; - *pfd = event_notifier_get_fd(&intp->unmask); + *pfd = event_notifier_get_fd(intp->unmask); qemu_set_fd_handler(*pfd, NULL, NULL, NULL); ret = ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set); g_free(irq_set); @@ -359,6 +380,15 @@ static int vfio_set_resample_eventfd(VFIOINTp *intp) return ret; } +/** + * vfio_start_irqfd_injection - starts the virtual IRQ injection using + * irqfd + * + * @sbdev: the sysbus device handle + * @irq: the qemu irq handle + * + * In case the irqfd setup fails, we fallback to userspace handled eventfd + */ static void vfio_start_irqfd_injection(SysBusDevice *sbdev, qemu_irq irq) { VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); @@ -366,7 +396,7 @@ static void vfio_start_irqfd_injection(SysBusDevice *sbdev, qemu_irq irq) if (!kvm_irqfds_enabled() || !kvm_resamplefds_enabled() || !vdev->irqfd_allowed) { - return; + goto fail_irqfd; } QLIST_FOREACH(intp, &vdev->intp_list, next) { @@ -376,39 +406,36 @@ static void vfio_start_irqfd_injection(SysBusDevice *sbdev, qemu_irq irq) } assert(intp); - /* Get to a known interrupt state */ - qemu_set_fd_handler(event_notifier_get_fd(&intp->interrupt), - NULL, NULL, vdev); - - vfio_mask_single_irqindex(&vdev->vbasedev, intp->pin); - qemu_set_irq(intp->qemuirq, 0); - - if (kvm_irqchip_add_irqfd_notifier(kvm_state, &intp->interrupt, - &intp->unmask, irq) < 0) { + if (kvm_irqchip_add_irqfd_notifier(kvm_state, intp->interrupt, + intp->unmask, irq) < 0) { goto fail_irqfd; } if (vfio_set_trigger_eventfd(intp, NULL) < 0) { goto fail_vfio; } - if (vfio_set_resample_eventfd(intp) < 0) { - goto fail_vfio; + if (vfio_irq_is_automasked(intp)) { + if (vfio_set_resample_eventfd(intp) < 0) { + goto fail_vfio; + } + trace_vfio_platform_start_level_irqfd_injection(intp->pin, + event_notifier_get_fd(intp->interrupt), + event_notifier_get_fd(intp->unmask)); + } else { + trace_vfio_platform_start_edge_irqfd_injection(intp->pin, + event_notifier_get_fd(intp->interrupt)); } - /* Let's resume injection with irqfd setup */ - vfio_unmask_single_irqindex(&vdev->vbasedev, intp->pin); - intp->kvm_accel = true; - trace_vfio_platform_start_irqfd_injection(intp->pin, - event_notifier_get_fd(&intp->interrupt), - event_notifier_get_fd(&intp->unmask)); return; fail_vfio: - kvm_irqchip_remove_irqfd_notifier(kvm_state, &intp->interrupt, irq); + kvm_irqchip_remove_irqfd_notifier(kvm_state, intp->interrupt, irq); + error_report("vfio: failed to start eventfd signaling for IRQ %d: %m", + intp->pin); + abort(); fail_irqfd: - vfio_start_eventfd_injection(intp); - vfio_unmask_single_irqindex(&vdev->vbasedev, intp->pin); + vfio_start_eventfd_injection(sbdev, irq); return; } @@ -446,28 +473,16 @@ static int vfio_populate_device(VFIODevice *vbasedev) vdev->regions = g_new0(VFIORegion *, vbasedev->num_regions); for (i = 0; i < vbasedev->num_regions; i++) { - struct vfio_region_info reg_info = { .argsz = sizeof(reg_info) }; - VFIORegion *ptr; + char *name = g_strdup_printf("VFIO %s region %d\n", vbasedev->name, i); - vdev->regions[i] = g_malloc0(sizeof(VFIORegion)); - ptr = vdev->regions[i]; - reg_info.index = i; - ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, ®_info); + vdev->regions[i] = g_new0(VFIORegion, 1); + ret = vfio_region_setup(OBJECT(vdev), vbasedev, + vdev->regions[i], i, name); + g_free(name); if (ret) { error_report("vfio: Error getting region %d info: %m", i); goto reg_error; } - ptr->flags = reg_info.flags; - ptr->size = reg_info.size; - ptr->fd_offset = reg_info.offset; - ptr->nr = i; - ptr->vbasedev = vbasedev; - - trace_vfio_platform_populate_regions(ptr->nr, - (unsigned long)ptr->flags, - (unsigned long)ptr->size, - ptr->vbasedev->fd, - (unsigned long)ptr->fd_offset); } vdev->mmap_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, @@ -504,6 +519,9 @@ irq_err: } reg_error: for (i = 0; i < vbasedev->num_regions; i++) { + if (vdev->regions[i]) { + vfio_region_finalize(vdev->regions[i]); + } g_free(vdev->regions[i]); } g_free(vdev->regions); @@ -530,38 +548,45 @@ static int vfio_base_device_init(VFIODevice *vbasedev) { VFIOGroup *group; VFIODevice *vbasedev_iter; - char path[PATH_MAX], iommu_group_path[PATH_MAX], *group_name; + char *tmp, group_path[PATH_MAX], *group_name; ssize_t len; struct stat st; int groupid; int ret; - /* name must be set prior to the call */ - if (!vbasedev->name || strchr(vbasedev->name, '/')) { - return -EINVAL; - } + /* @sysfsdev takes precedence over @host */ + if (vbasedev->sysfsdev) { + g_free(vbasedev->name); + vbasedev->name = g_strdup(basename(vbasedev->sysfsdev)); + } else { + if (!vbasedev->name || strchr(vbasedev->name, '/')) { + return -EINVAL; + } - /* Check that the host device exists */ - g_snprintf(path, sizeof(path), "/sys/bus/platform/devices/%s/", - vbasedev->name); + vbasedev->sysfsdev = g_strdup_printf("/sys/bus/platform/devices/%s", + vbasedev->name); + } - if (stat(path, &st) < 0) { - error_report("vfio: error: no such host device: %s", path); + if (stat(vbasedev->sysfsdev, &st) < 0) { + error_report("vfio: error: no such host device: %s", + vbasedev->sysfsdev); return -errno; } - g_strlcat(path, "iommu_group", sizeof(path)); - len = readlink(path, iommu_group_path, sizeof(iommu_group_path)); - if (len < 0 || len >= sizeof(iommu_group_path)) { + tmp = g_strdup_printf("%s/iommu_group", vbasedev->sysfsdev); + len = readlink(tmp, group_path, sizeof(group_path)); + g_free(tmp); + + if (len < 0 || len >= sizeof(group_path)) { error_report("vfio: error no iommu_group for device"); return len < 0 ? -errno : -ENAMETOOLONG; } - iommu_group_path[len] = 0; - group_name = basename(iommu_group_path); + group_path[len] = 0; + group_name = basename(group_path); if (sscanf(group_name, "%d", &groupid) != 1) { - error_report("vfio: error reading %s: %m", path); + error_report("vfio: error reading %s: %m", group_path); return -errno; } @@ -573,66 +598,30 @@ static int vfio_base_device_init(VFIODevice *vbasedev) return -ENOENT; } - g_snprintf(path, sizeof(path), "%s", vbasedev->name); - QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { if (strcmp(vbasedev_iter->name, vbasedev->name) == 0) { - error_report("vfio: error: device %s is already attached", path); + error_report("vfio: error: device %s is already attached", + vbasedev->name); vfio_put_group(group); return -EBUSY; } } - ret = vfio_get_device(group, path, vbasedev); + ret = vfio_get_device(group, vbasedev->name, vbasedev); if (ret) { - error_report("vfio: failed to get device %s", path); + error_report("vfio: failed to get device %s", vbasedev->name); vfio_put_group(group); return ret; } ret = vfio_populate_device(vbasedev); if (ret) { - error_report("vfio: failed to populate device %s", path); + error_report("vfio: failed to populate device %s", vbasedev->name); vfio_put_group(group); } return ret; } -/** - * vfio_map_region - initialize the 2 memory regions for a given - * MMIO region index - * @vdev: the VFIO platform device handle - * @nr: the index of the region - * - * Init the top memory region and the mmapped memory region beneath - * VFIOPlatformDevice is used since VFIODevice is not a QOM Object - * and could not be passed to memory region functions -*/ -static void vfio_map_region(VFIOPlatformDevice *vdev, int nr) -{ - VFIORegion *region = vdev->regions[nr]; - uint64_t size = region->size; - char name[64]; - - if (!size) { - return; - } - - g_snprintf(name, sizeof(name), "VFIO %s region %d", - vdev->vbasedev.name, nr); - - /* A "slow" read/write mapping underlies all regions */ - memory_region_init_io(®ion->mem, OBJECT(vdev), &vfio_region_ops, - region, name, size); - - g_strlcat(name, " mmap", sizeof(name)); - - if (vfio_mmap_region(OBJECT(vdev), region, ®ion->mem, - ®ion->mmap_mem, ®ion->mmap, size, 0, name)) { - error_report("%s unsupported. Performance may be slow", name); - } -} - /** * vfio_platform_realize - the device realize function * @dev: device state pointer @@ -646,13 +635,14 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp) VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev); SysBusDevice *sbdev = SYS_BUS_DEVICE(dev); VFIODevice *vbasedev = &vdev->vbasedev; - VFIOINTp *intp; int i, ret; vbasedev->type = VFIO_DEVICE_TYPE_PLATFORM; vbasedev->ops = &vfio_platform_ops; - trace_vfio_platform_realize(vbasedev->name, vdev->compat); + trace_vfio_platform_realize(vbasedev->sysfsdev ? + vbasedev->sysfsdev : vbasedev->name, + vdev->compat); ret = vfio_base_device_init(vbasedev); if (ret) { @@ -662,12 +652,11 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp) } for (i = 0; i < vbasedev->num_regions; i++) { - vfio_map_region(vdev, i); - sysbus_init_mmio(sbdev, &vdev->regions[i]->mem); - } - - QLIST_FOREACH(intp, &vdev->intp_list, next) { - vfio_start_eventfd_injection(intp); + if (vfio_region_mmap(vdev->regions[i])) { + error_report("%s mmap unsupported. Performance may be slow", + memory_region_name(vdev->regions[i]->mem)); + } + sysbus_init_mmio(sbdev, vdev->regions[i]->mem); } } @@ -678,7 +667,8 @@ static const VMStateDescription vfio_platform_vmstate = { static Property vfio_platform_dev_properties[] = { DEFINE_PROP_STRING("host", VFIOPlatformDevice, vbasedev.name), - DEFINE_PROP_BOOL("x-mmap", VFIOPlatformDevice, vbasedev.allow_mmap, true), + DEFINE_PROP_STRING("sysfsdev", VFIOPlatformDevice, vbasedev.sysfsdev), + DEFINE_PROP_BOOL("x-no-mmap", VFIOPlatformDevice, vbasedev.no_mmap, false), DEFINE_PROP_UINT32("mmap-timeout-ms", VFIOPlatformDevice, mmap_timeout, 1100), DEFINE_PROP_BOOL("x-irqfd", VFIOPlatformDevice, irqfd_allowed, true), diff --git a/qemu/hw/virtio/Makefile.objs b/qemu/hw/virtio/Makefile.objs index 19b224a44..3e2b175da 100644 --- a/qemu/hw/virtio/Makefile.objs +++ b/qemu/hw/virtio/Makefile.objs @@ -2,7 +2,6 @@ common-obj-y += virtio-rng.o common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o common-obj-y += virtio-bus.o common-obj-y += virtio-mmio.o -obj-$(CONFIG_VIRTIO) += dataplane/ obj-y += virtio.o virtio-balloon.o obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o diff --git a/qemu/hw/virtio/dataplane/Makefile.objs b/qemu/hw/virtio/dataplane/Makefile.objs deleted file mode 100644 index 753a9cab4..000000000 --- a/qemu/hw/virtio/dataplane/Makefile.objs +++ /dev/null @@ -1 +0,0 @@ -obj-y += vring.o diff --git a/qemu/hw/virtio/dataplane/vring.c b/qemu/hw/virtio/dataplane/vring.c deleted file mode 100644 index 07fd69c69..000000000 --- a/qemu/hw/virtio/dataplane/vring.c +++ /dev/null @@ -1,453 +0,0 @@ -/* Copyright 2012 Red Hat, Inc. - * Copyright IBM, Corp. 2012 - * - * Based on Linux 2.6.39 vhost code: - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2006 Rusty Russell IBM Corporation - * - * Author: Michael S. Tsirkin - * Stefan Hajnoczi - * - * Inspiration, some code, and most witty comments come from - * Documentation/virtual/lguest/lguest.c, by Rusty Russell - * - * This work is licensed under the terms of the GNU GPL, version 2. - */ - -#include "trace.h" -#include "hw/hw.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" -#include "hw/virtio/virtio-access.h" -#include "hw/virtio/dataplane/vring.h" -#include "hw/virtio/dataplane/vring-accessors.h" -#include "qemu/error-report.h" - -/* vring_map can be coupled with vring_unmap or (if you still have the - * value returned in *mr) memory_region_unref. - */ -static void *vring_map(MemoryRegion **mr, hwaddr phys, hwaddr len, - bool is_write) -{ - MemoryRegionSection section = memory_region_find(get_system_memory(), phys, len); - - if (!section.mr || int128_get64(section.size) < len) { - goto out; - } - if (is_write && section.readonly) { - goto out; - } - if (!memory_region_is_ram(section.mr)) { - goto out; - } - - /* Ignore regions with dirty logging, we cannot mark them dirty */ - if (memory_region_get_dirty_log_mask(section.mr)) { - goto out; - } - - *mr = section.mr; - return memory_region_get_ram_ptr(section.mr) + section.offset_within_region; - -out: - memory_region_unref(section.mr); - *mr = NULL; - return NULL; -} - -static void vring_unmap(void *buffer, bool is_write) -{ - ram_addr_t addr; - MemoryRegion *mr; - - mr = qemu_ram_addr_from_host(buffer, &addr); - memory_region_unref(mr); -} - -/* Map the guest's vring to host memory */ -bool vring_setup(Vring *vring, VirtIODevice *vdev, int n) -{ - hwaddr vring_addr = virtio_queue_get_ring_addr(vdev, n); - hwaddr vring_size = virtio_queue_get_ring_size(vdev, n); - void *vring_ptr; - - vring->broken = false; - - vring_ptr = vring_map(&vring->mr, vring_addr, vring_size, true); - if (!vring_ptr) { - error_report("Failed to map vring " - "addr %#" HWADDR_PRIx " size %" HWADDR_PRIu, - vring_addr, vring_size); - vring->broken = true; - return false; - } - - vring_init(&vring->vr, virtio_queue_get_num(vdev, n), vring_ptr, 4096); - - vring->last_avail_idx = virtio_queue_get_last_avail_idx(vdev, n); - vring->last_used_idx = vring_get_used_idx(vdev, vring); - vring->signalled_used = 0; - vring->signalled_used_valid = false; - - trace_vring_setup(virtio_queue_get_ring_addr(vdev, n), - vring->vr.desc, vring->vr.avail, vring->vr.used); - return true; -} - -void vring_teardown(Vring *vring, VirtIODevice *vdev, int n) -{ - virtio_queue_set_last_avail_idx(vdev, n, vring->last_avail_idx); - virtio_queue_invalidate_signalled_used(vdev, n); - - memory_region_unref(vring->mr); -} - -/* Disable guest->host notifies */ -void vring_disable_notification(VirtIODevice *vdev, Vring *vring) -{ - if (!virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { - vring_set_used_flags(vdev, vring, VRING_USED_F_NO_NOTIFY); - } -} - -/* Enable guest->host notifies - * - * Return true if the vring is empty, false if there are more requests. - */ -bool vring_enable_notification(VirtIODevice *vdev, Vring *vring) -{ - if (virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { - vring_avail_event(&vring->vr) = vring->vr.avail->idx; - } else { - vring_clear_used_flags(vdev, vring, VRING_USED_F_NO_NOTIFY); - } - smp_mb(); /* ensure update is seen before reading avail_idx */ - return !vring_more_avail(vdev, vring); -} - -/* This is stolen from linux/drivers/vhost/vhost.c:vhost_notify() */ -bool vring_should_notify(VirtIODevice *vdev, Vring *vring) -{ - uint16_t old, new; - bool v; - /* Flush out used index updates. This is paired - * with the barrier that the Guest executes when enabling - * interrupts. */ - smp_mb(); - - if (virtio_has_feature(vdev, VIRTIO_F_NOTIFY_ON_EMPTY) && - unlikely(!vring_more_avail(vdev, vring))) { - return true; - } - - if (!virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { - return !(vring_get_avail_flags(vdev, vring) & - VRING_AVAIL_F_NO_INTERRUPT); - } - old = vring->signalled_used; - v = vring->signalled_used_valid; - new = vring->signalled_used = vring->last_used_idx; - vring->signalled_used_valid = true; - - if (unlikely(!v)) { - return true; - } - - return vring_need_event(virtio_tswap16(vdev, vring_used_event(&vring->vr)), - new, old); -} - - -static int get_desc(Vring *vring, VirtQueueElement *elem, - struct vring_desc *desc) -{ - unsigned *num; - struct iovec *iov; - hwaddr *addr; - MemoryRegion *mr; - - if (desc->flags & VRING_DESC_F_WRITE) { - num = &elem->in_num; - iov = &elem->in_sg[*num]; - addr = &elem->in_addr[*num]; - } else { - num = &elem->out_num; - iov = &elem->out_sg[*num]; - addr = &elem->out_addr[*num]; - - /* If it's an output descriptor, they're all supposed - * to come before any input descriptors. */ - if (unlikely(elem->in_num)) { - error_report("Descriptor has out after in"); - return -EFAULT; - } - } - - /* Stop for now if there are not enough iovecs available. */ - if (*num >= VIRTQUEUE_MAX_SIZE) { - error_report("Invalid SG num: %u", *num); - return -EFAULT; - } - - /* TODO handle non-contiguous memory across region boundaries */ - iov->iov_base = vring_map(&mr, desc->addr, desc->len, - desc->flags & VRING_DESC_F_WRITE); - if (!iov->iov_base) { - error_report("Failed to map descriptor addr %#" PRIx64 " len %u", - (uint64_t)desc->addr, desc->len); - return -EFAULT; - } - - /* The MemoryRegion is looked up again and unref'ed later, leave the - * ref in place. */ - iov->iov_len = desc->len; - *addr = desc->addr; - *num += 1; - return 0; -} - -static void copy_in_vring_desc(VirtIODevice *vdev, - const struct vring_desc *guest, - struct vring_desc *host) -{ - host->addr = virtio_ldq_p(vdev, &guest->addr); - host->len = virtio_ldl_p(vdev, &guest->len); - host->flags = virtio_lduw_p(vdev, &guest->flags); - host->next = virtio_lduw_p(vdev, &guest->next); -} - -/* This is stolen from linux/drivers/vhost/vhost.c. */ -static int get_indirect(VirtIODevice *vdev, Vring *vring, - VirtQueueElement *elem, struct vring_desc *indirect) -{ - struct vring_desc desc; - unsigned int i = 0, count, found = 0; - int ret; - - /* Sanity check */ - if (unlikely(indirect->len % sizeof(desc))) { - error_report("Invalid length in indirect descriptor: " - "len %#x not multiple of %#zx", - indirect->len, sizeof(desc)); - vring->broken = true; - return -EFAULT; - } - - count = indirect->len / sizeof(desc); - /* Buffers are chained via a 16 bit next field, so - * we can have at most 2^16 of these. */ - if (unlikely(count > USHRT_MAX + 1)) { - error_report("Indirect buffer length too big: %d", indirect->len); - vring->broken = true; - return -EFAULT; - } - - do { - struct vring_desc *desc_ptr; - MemoryRegion *mr; - - /* Translate indirect descriptor */ - desc_ptr = vring_map(&mr, - indirect->addr + found * sizeof(desc), - sizeof(desc), false); - if (!desc_ptr) { - error_report("Failed to map indirect descriptor " - "addr %#" PRIx64 " len %zu", - (uint64_t)indirect->addr + found * sizeof(desc), - sizeof(desc)); - vring->broken = true; - return -EFAULT; - } - copy_in_vring_desc(vdev, desc_ptr, &desc); - memory_region_unref(mr); - - /* Ensure descriptor has been loaded before accessing fields */ - barrier(); /* read_barrier_depends(); */ - - if (unlikely(++found > count)) { - error_report("Loop detected: last one at %u " - "indirect size %u", i, count); - vring->broken = true; - return -EFAULT; - } - - if (unlikely(desc.flags & VRING_DESC_F_INDIRECT)) { - error_report("Nested indirect descriptor"); - vring->broken = true; - return -EFAULT; - } - - ret = get_desc(vring, elem, &desc); - if (ret < 0) { - vring->broken |= (ret == -EFAULT); - return ret; - } - i = desc.next; - } while (desc.flags & VRING_DESC_F_NEXT); - return 0; -} - -static void vring_unmap_element(VirtQueueElement *elem) -{ - int i; - - /* This assumes that the iovecs, if changed, are never moved past - * the end of the valid area. This is true if iovec manipulations - * are done with iov_discard_front and iov_discard_back. - */ - for (i = 0; i < elem->out_num; i++) { - vring_unmap(elem->out_sg[i].iov_base, false); - } - - for (i = 0; i < elem->in_num; i++) { - vring_unmap(elem->in_sg[i].iov_base, true); - } -} - -/* This looks in the virtqueue and for the first available buffer, and converts - * it to an iovec for convenient access. Since descriptors consist of some - * number of output then some number of input descriptors, it's actually two - * iovecs, but we pack them into one and note how many of each there were. - * - * This function returns the descriptor number found, or vq->num (which is - * never a valid descriptor number) if none was found. A negative code is - * returned on error. - * - * Stolen from linux/drivers/vhost/vhost.c. - */ -int vring_pop(VirtIODevice *vdev, Vring *vring, - VirtQueueElement *elem) -{ - struct vring_desc desc; - unsigned int i, head, found = 0, num = vring->vr.num; - uint16_t avail_idx, last_avail_idx; - int ret; - - /* Initialize elem so it can be safely unmapped */ - elem->in_num = elem->out_num = 0; - - /* If there was a fatal error then refuse operation */ - if (vring->broken) { - ret = -EFAULT; - goto out; - } - - /* Check it isn't doing very strange things with descriptor numbers. */ - last_avail_idx = vring->last_avail_idx; - avail_idx = vring_get_avail_idx(vdev, vring); - barrier(); /* load indices now and not again later */ - - if (unlikely((uint16_t)(avail_idx - last_avail_idx) > num)) { - error_report("Guest moved used index from %u to %u", - last_avail_idx, avail_idx); - ret = -EFAULT; - goto out; - } - - /* If there's nothing new since last we looked. */ - if (avail_idx == last_avail_idx) { - ret = -EAGAIN; - goto out; - } - - /* Only get avail ring entries after they have been exposed by guest. */ - smp_rmb(); - - /* Grab the next descriptor number they're advertising, and increment - * the index we've seen. */ - head = vring_get_avail_ring(vdev, vring, last_avail_idx % num); - - elem->index = head; - - /* If their number is silly, that's an error. */ - if (unlikely(head >= num)) { - error_report("Guest says index %u > %u is available", head, num); - ret = -EFAULT; - goto out; - } - - i = head; - do { - if (unlikely(i >= num)) { - error_report("Desc index is %u > %u, head = %u", i, num, head); - ret = -EFAULT; - goto out; - } - if (unlikely(++found > num)) { - error_report("Loop detected: last one at %u vq size %u head %u", - i, num, head); - ret = -EFAULT; - goto out; - } - copy_in_vring_desc(vdev, &vring->vr.desc[i], &desc); - - /* Ensure descriptor is loaded before accessing fields */ - barrier(); - - if (desc.flags & VRING_DESC_F_INDIRECT) { - ret = get_indirect(vdev, vring, elem, &desc); - if (ret < 0) { - goto out; - } - continue; - } - - ret = get_desc(vring, elem, &desc); - if (ret < 0) { - goto out; - } - - i = desc.next; - } while (desc.flags & VRING_DESC_F_NEXT); - - /* On success, increment avail index. */ - vring->last_avail_idx++; - if (virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { - vring_avail_event(&vring->vr) = - virtio_tswap16(vdev, vring->last_avail_idx); - } - - return head; - -out: - assert(ret < 0); - if (ret == -EFAULT) { - vring->broken = true; - } - vring_unmap_element(elem); - return ret; -} - -/* After we've used one of their buffers, we tell them about it. - * - * Stolen from linux/drivers/vhost/vhost.c. - */ -void vring_push(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem, - int len) -{ - unsigned int head = elem->index; - uint16_t new; - - vring_unmap_element(elem); - - /* Don't touch vring if a fatal error occurred */ - if (vring->broken) { - return; - } - - /* The virtqueue contains a ring of used buffers. Get a pointer to the - * next entry in that used ring. */ - vring_set_used_ring_id(vdev, vring, vring->last_used_idx % vring->vr.num, - head); - vring_set_used_ring_len(vdev, vring, vring->last_used_idx % vring->vr.num, - len); - - /* Make sure buffer is written before we update index. */ - smp_wmb(); - - new = ++vring->last_used_idx; - vring_set_used_idx(vdev, vring, new); - if (unlikely((int16_t)(new - vring->signalled_used) < (uint16_t)1)) { - vring->signalled_used_valid = false; - } -} diff --git a/qemu/hw/virtio/vhost-backend.c b/qemu/hw/virtio/vhost-backend.c index 4d68a2765..b35890289 100644 --- a/qemu/hw/virtio/vhost-backend.c +++ b/qemu/hw/virtio/vhost-backend.c @@ -8,9 +8,11 @@ * */ +#include "qemu/osdep.h" #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-backend.h" #include "qemu/error-report.h" +#include "linux/vhost.h" #include @@ -42,11 +44,152 @@ static int vhost_kernel_cleanup(struct vhost_dev *dev) return close(fd); } +static int vhost_kernel_memslots_limit(struct vhost_dev *dev) +{ + int limit = 64; + char *s; + + if (g_file_get_contents("/sys/module/vhost/parameters/max_mem_regions", + &s, NULL, NULL)) { + uint64_t val = g_ascii_strtoull(s, NULL, 10); + if (!((val == G_MAXUINT64 || !val) && errno)) { + return val; + } + error_report("ignoring invalid max_mem_regions value in vhost module:" + " %s", s); + } + return limit; +} + +static int vhost_kernel_net_set_backend(struct vhost_dev *dev, + struct vhost_vring_file *file) +{ + return vhost_kernel_call(dev, VHOST_NET_SET_BACKEND, file); +} + +static int vhost_kernel_scsi_set_endpoint(struct vhost_dev *dev, + struct vhost_scsi_target *target) +{ + return vhost_kernel_call(dev, VHOST_SCSI_SET_ENDPOINT, target); +} + +static int vhost_kernel_scsi_clear_endpoint(struct vhost_dev *dev, + struct vhost_scsi_target *target) +{ + return vhost_kernel_call(dev, VHOST_SCSI_CLEAR_ENDPOINT, target); +} + +static int vhost_kernel_scsi_get_abi_version(struct vhost_dev *dev, int *version) +{ + return vhost_kernel_call(dev, VHOST_SCSI_GET_ABI_VERSION, version); +} + +static int vhost_kernel_set_log_base(struct vhost_dev *dev, uint64_t base, + struct vhost_log *log) +{ + return vhost_kernel_call(dev, VHOST_SET_LOG_BASE, &base); +} + +static int vhost_kernel_set_mem_table(struct vhost_dev *dev, + struct vhost_memory *mem) +{ + return vhost_kernel_call(dev, VHOST_SET_MEM_TABLE, mem); +} + +static int vhost_kernel_set_vring_addr(struct vhost_dev *dev, + struct vhost_vring_addr *addr) +{ + return vhost_kernel_call(dev, VHOST_SET_VRING_ADDR, addr); +} + +static int vhost_kernel_set_vring_endian(struct vhost_dev *dev, + struct vhost_vring_state *ring) +{ + return vhost_kernel_call(dev, VHOST_SET_VRING_ENDIAN, ring); +} + +static int vhost_kernel_set_vring_num(struct vhost_dev *dev, + struct vhost_vring_state *ring) +{ + return vhost_kernel_call(dev, VHOST_SET_VRING_NUM, ring); +} + +static int vhost_kernel_set_vring_base(struct vhost_dev *dev, + struct vhost_vring_state *ring) +{ + return vhost_kernel_call(dev, VHOST_SET_VRING_BASE, ring); +} + +static int vhost_kernel_get_vring_base(struct vhost_dev *dev, + struct vhost_vring_state *ring) +{ + return vhost_kernel_call(dev, VHOST_GET_VRING_BASE, ring); +} + +static int vhost_kernel_set_vring_kick(struct vhost_dev *dev, + struct vhost_vring_file *file) +{ + return vhost_kernel_call(dev, VHOST_SET_VRING_KICK, file); +} + +static int vhost_kernel_set_vring_call(struct vhost_dev *dev, + struct vhost_vring_file *file) +{ + return vhost_kernel_call(dev, VHOST_SET_VRING_CALL, file); +} + +static int vhost_kernel_set_features(struct vhost_dev *dev, + uint64_t features) +{ + return vhost_kernel_call(dev, VHOST_SET_FEATURES, &features); +} + +static int vhost_kernel_get_features(struct vhost_dev *dev, + uint64_t *features) +{ + return vhost_kernel_call(dev, VHOST_GET_FEATURES, features); +} + +static int vhost_kernel_set_owner(struct vhost_dev *dev) +{ + return vhost_kernel_call(dev, VHOST_SET_OWNER, NULL); +} + +static int vhost_kernel_reset_device(struct vhost_dev *dev) +{ + return vhost_kernel_call(dev, VHOST_RESET_OWNER, NULL); +} + +static int vhost_kernel_get_vq_index(struct vhost_dev *dev, int idx) +{ + assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); + + return idx - dev->vq_index; +} + static const VhostOps kernel_ops = { .backend_type = VHOST_BACKEND_TYPE_KERNEL, - .vhost_call = vhost_kernel_call, .vhost_backend_init = vhost_kernel_init, - .vhost_backend_cleanup = vhost_kernel_cleanup + .vhost_backend_cleanup = vhost_kernel_cleanup, + .vhost_backend_memslots_limit = vhost_kernel_memslots_limit, + .vhost_net_set_backend = vhost_kernel_net_set_backend, + .vhost_scsi_set_endpoint = vhost_kernel_scsi_set_endpoint, + .vhost_scsi_clear_endpoint = vhost_kernel_scsi_clear_endpoint, + .vhost_scsi_get_abi_version = vhost_kernel_scsi_get_abi_version, + .vhost_set_log_base = vhost_kernel_set_log_base, + .vhost_set_mem_table = vhost_kernel_set_mem_table, + .vhost_set_vring_addr = vhost_kernel_set_vring_addr, + .vhost_set_vring_endian = vhost_kernel_set_vring_endian, + .vhost_set_vring_num = vhost_kernel_set_vring_num, + .vhost_set_vring_base = vhost_kernel_set_vring_base, + .vhost_get_vring_base = vhost_kernel_get_vring_base, + .vhost_set_vring_kick = vhost_kernel_set_vring_kick, + .vhost_set_vring_call = vhost_kernel_set_vring_call, + .vhost_set_features = vhost_kernel_set_features, + .vhost_get_features = vhost_kernel_get_features, + .vhost_set_owner = vhost_kernel_set_owner, + .vhost_reset_device = vhost_kernel_reset_device, + .vhost_get_vq_index = vhost_kernel_get_vq_index, }; int vhost_set_backend_type(struct vhost_dev *dev, VhostBackendType backend_type) diff --git a/qemu/hw/virtio/vhost-user.c b/qemu/hw/virtio/vhost-user.c index e7ab8293d..5914e8510 100644 --- a/qemu/hw/virtio/vhost-user.c +++ b/qemu/hw/virtio/vhost-user.c @@ -8,22 +8,35 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-backend.h" +#include "hw/virtio/virtio-net.h" #include "sysemu/char.h" #include "sysemu/kvm.h" #include "qemu/error-report.h" #include "qemu/sockets.h" #include "exec/ram_addr.h" +#include "migration/migration.h" -#include -#include #include #include #include #include #define VHOST_MEMORY_MAX_NREGIONS 8 +#define VHOST_USER_F_PROTOCOL_FEATURES 30 + +enum VhostUserProtocolFeature { + VHOST_USER_PROTOCOL_F_MQ = 0, + VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1, + VHOST_USER_PROTOCOL_F_RARP = 2, + + VHOST_USER_PROTOCOL_F_MAX +}; + +#define VHOST_USER_PROTOCOL_FEATURE_MASK ((1 << VHOST_USER_PROTOCOL_F_MAX) - 1) typedef enum VhostUserRequest { VHOST_USER_NONE = 0, @@ -41,6 +54,11 @@ typedef enum VhostUserRequest { VHOST_USER_SET_VRING_KICK = 12, VHOST_USER_SET_VRING_CALL = 13, VHOST_USER_SET_VRING_ERR = 14, + VHOST_USER_GET_PROTOCOL_FEATURES = 15, + VHOST_USER_SET_PROTOCOL_FEATURES = 16, + VHOST_USER_GET_QUEUE_NUM = 17, + VHOST_USER_SET_VRING_ENABLE = 18, + VHOST_USER_SEND_RARP = 19, VHOST_USER_MAX } VhostUserRequest; @@ -57,6 +75,11 @@ typedef struct VhostUserMemory { VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS]; } VhostUserMemory; +typedef struct VhostUserLog { + uint64_t mmap_size; + uint64_t mmap_offset; +} VhostUserLog; + typedef struct VhostUserMsg { VhostUserRequest request; @@ -71,7 +94,8 @@ typedef struct VhostUserMsg { struct vhost_vring_state state; struct vhost_vring_addr addr; VhostUserMemory memory; - }; + VhostUserLog log; + } payload; } QEMU_PACKED VhostUserMsg; static VhostUserMsg m __attribute__ ((unused)); @@ -89,37 +113,6 @@ static bool ioeventfd_enabled(void) return kvm_enabled() && kvm_eventfds_enabled(); } -static unsigned long int ioctl_to_vhost_user_request[VHOST_USER_MAX] = { - -1, /* VHOST_USER_NONE */ - VHOST_GET_FEATURES, /* VHOST_USER_GET_FEATURES */ - VHOST_SET_FEATURES, /* VHOST_USER_SET_FEATURES */ - VHOST_SET_OWNER, /* VHOST_USER_SET_OWNER */ - VHOST_RESET_OWNER, /* VHOST_USER_RESET_OWNER */ - VHOST_SET_MEM_TABLE, /* VHOST_USER_SET_MEM_TABLE */ - VHOST_SET_LOG_BASE, /* VHOST_USER_SET_LOG_BASE */ - VHOST_SET_LOG_FD, /* VHOST_USER_SET_LOG_FD */ - VHOST_SET_VRING_NUM, /* VHOST_USER_SET_VRING_NUM */ - VHOST_SET_VRING_ADDR, /* VHOST_USER_SET_VRING_ADDR */ - VHOST_SET_VRING_BASE, /* VHOST_USER_SET_VRING_BASE */ - VHOST_GET_VRING_BASE, /* VHOST_USER_GET_VRING_BASE */ - VHOST_SET_VRING_KICK, /* VHOST_USER_SET_VRING_KICK */ - VHOST_SET_VRING_CALL, /* VHOST_USER_SET_VRING_CALL */ - VHOST_SET_VRING_ERR /* VHOST_USER_SET_VRING_ERR */ -}; - -static VhostUserRequest vhost_user_request_translate(unsigned long int request) -{ - VhostUserRequest idx; - - for (idx = 0; idx < VHOST_USER_MAX; idx++) { - if (ioctl_to_vhost_user_request[idx] == request) { - break; - } - } - - return (idx == VHOST_USER_MAX) ? VHOST_USER_NONE : idx; -} - static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) { CharDriverState *chr = dev->opaque; @@ -128,8 +121,8 @@ static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) r = qemu_chr_fe_read_all(chr, p, size); if (r != size) { - error_report("Failed to read msg header. Read %d instead of %d.", r, - size); + error_report("Failed to read msg header. Read %d instead of %d." + " Original request %d.", r, size, msg->request); goto fail; } @@ -166,12 +159,35 @@ fail: return -1; } +static bool vhost_user_one_time_request(VhostUserRequest request) +{ + switch (request) { + case VHOST_USER_SET_OWNER: + case VHOST_USER_RESET_OWNER: + case VHOST_USER_SET_MEM_TABLE: + case VHOST_USER_GET_QUEUE_NUM: + return true; + default: + return false; + } +} + +/* most non-init callers ignore the error */ static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg, int *fds, int fd_num) { CharDriverState *chr = dev->opaque; int size = VHOST_USER_HDR_SIZE + msg->size; + /* + * For non-vring specific requests, like VHOST_USER_SET_MEM_TABLE, + * we just need send it once in the first time. For later such + * request, we just ignore it. + */ + if (vhost_user_one_time_request(msg->request) && dev->vq_index != 0) { + return 0; + } + if (fd_num) { qemu_chr_fe_set_msgfds(chr, fds, fd_num); } @@ -180,157 +196,364 @@ static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg, 0 : -1; } -static int vhost_user_call(struct vhost_dev *dev, unsigned long int request, - void *arg) +static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base, + struct vhost_log *log) { - VhostUserMsg msg; - VhostUserRequest msg_request; - struct vhost_vring_file *file = 0; - int need_reply = 0; int fds[VHOST_MEMORY_MAX_NREGIONS]; - int i, fd; size_t fd_num = 0; + bool shmfd = virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_LOG_SHMFD); + VhostUserMsg msg = { + .request = VHOST_USER_SET_LOG_BASE, + .flags = VHOST_USER_VERSION, + .payload.log.mmap_size = log->size * sizeof(*(log->log)), + .payload.log.mmap_offset = 0, + .size = sizeof(msg.payload.log), + }; - assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); + if (shmfd && log->fd != -1) { + fds[fd_num++] = log->fd; + } - msg_request = vhost_user_request_translate(request); - msg.request = msg_request; - msg.flags = VHOST_USER_VERSION; - msg.size = 0; + vhost_user_write(dev, &msg, fds, fd_num); - switch (request) { - case VHOST_GET_FEATURES: - need_reply = 1; - break; - - case VHOST_SET_FEATURES: - case VHOST_SET_LOG_BASE: - msg.u64 = *((__u64 *) arg); - msg.size = sizeof(m.u64); - break; - - case VHOST_SET_OWNER: - case VHOST_RESET_OWNER: - break; - - case VHOST_SET_MEM_TABLE: - for (i = 0; i < dev->mem->nregions; ++i) { - struct vhost_memory_region *reg = dev->mem->regions + i; - ram_addr_t ram_addr; - - assert((uintptr_t)reg->userspace_addr == reg->userspace_addr); - qemu_ram_addr_from_host((void *)(uintptr_t)reg->userspace_addr, &ram_addr); - fd = qemu_get_ram_fd(ram_addr); - if (fd > 0) { - msg.memory.regions[fd_num].userspace_addr = reg->userspace_addr; - msg.memory.regions[fd_num].memory_size = reg->memory_size; - msg.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr; - msg.memory.regions[fd_num].mmap_offset = reg->userspace_addr - - (uintptr_t) qemu_get_ram_block_host_ptr(ram_addr); - assert(fd_num < VHOST_MEMORY_MAX_NREGIONS); - fds[fd_num++] = fd; - } + if (shmfd) { + msg.size = 0; + if (vhost_user_read(dev, &msg) < 0) { + return 0; } - msg.memory.nregions = fd_num; - - if (!fd_num) { - error_report("Failed initializing vhost-user memory map, " - "consider using -object memory-backend-file share=on"); + if (msg.request != VHOST_USER_SET_LOG_BASE) { + error_report("Received unexpected msg type. " + "Expected %d received %d", + VHOST_USER_SET_LOG_BASE, msg.request); return -1; } + } - msg.size = sizeof(m.memory.nregions); - msg.size += sizeof(m.memory.padding); - msg.size += fd_num * sizeof(VhostUserMemoryRegion); - - break; - - case VHOST_SET_LOG_FD: - fds[fd_num++] = *((int *) arg); - break; - - case VHOST_SET_VRING_NUM: - case VHOST_SET_VRING_BASE: - memcpy(&msg.state, arg, sizeof(struct vhost_vring_state)); - msg.size = sizeof(m.state); - break; - - case VHOST_GET_VRING_BASE: - memcpy(&msg.state, arg, sizeof(struct vhost_vring_state)); - msg.size = sizeof(m.state); - need_reply = 1; - break; - - case VHOST_SET_VRING_ADDR: - memcpy(&msg.addr, arg, sizeof(struct vhost_vring_addr)); - msg.size = sizeof(m.addr); - break; - - case VHOST_SET_VRING_KICK: - case VHOST_SET_VRING_CALL: - case VHOST_SET_VRING_ERR: - file = arg; - msg.u64 = file->index & VHOST_USER_VRING_IDX_MASK; - msg.size = sizeof(m.u64); - if (ioeventfd_enabled() && file->fd > 0) { - fds[fd_num++] = file->fd; - } else { - msg.u64 |= VHOST_USER_VRING_NOFD_MASK; + return 0; +} + +static int vhost_user_set_mem_table(struct vhost_dev *dev, + struct vhost_memory *mem) +{ + int fds[VHOST_MEMORY_MAX_NREGIONS]; + int i, fd; + size_t fd_num = 0; + VhostUserMsg msg = { + .request = VHOST_USER_SET_MEM_TABLE, + .flags = VHOST_USER_VERSION, + }; + + for (i = 0; i < dev->mem->nregions; ++i) { + struct vhost_memory_region *reg = dev->mem->regions + i; + ram_addr_t ram_addr; + + assert((uintptr_t)reg->userspace_addr == reg->userspace_addr); + qemu_ram_addr_from_host((void *)(uintptr_t)reg->userspace_addr, + &ram_addr); + fd = qemu_get_ram_fd(ram_addr); + if (fd > 0) { + msg.payload.memory.regions[fd_num].userspace_addr = reg->userspace_addr; + msg.payload.memory.regions[fd_num].memory_size = reg->memory_size; + msg.payload.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr; + msg.payload.memory.regions[fd_num].mmap_offset = reg->userspace_addr - + (uintptr_t) qemu_get_ram_block_host_ptr(ram_addr); + assert(fd_num < VHOST_MEMORY_MAX_NREGIONS); + fds[fd_num++] = fd; } - break; - default: - error_report("vhost-user trying to send unhandled ioctl"); + } + + msg.payload.memory.nregions = fd_num; + + if (!fd_num) { + error_report("Failed initializing vhost-user memory map, " + "consider using -object memory-backend-file share=on"); return -1; - break; } - if (vhost_user_write(dev, &msg, fds, fd_num) < 0) { + msg.size = sizeof(msg.payload.memory.nregions); + msg.size += sizeof(msg.payload.memory.padding); + msg.size += fd_num * sizeof(VhostUserMemoryRegion); + + vhost_user_write(dev, &msg, fds, fd_num); + + return 0; +} + +static int vhost_user_set_vring_addr(struct vhost_dev *dev, + struct vhost_vring_addr *addr) +{ + VhostUserMsg msg = { + .request = VHOST_USER_SET_VRING_ADDR, + .flags = VHOST_USER_VERSION, + .payload.addr = *addr, + .size = sizeof(msg.payload.addr), + }; + + vhost_user_write(dev, &msg, NULL, 0); + + return 0; +} + +static int vhost_user_set_vring_endian(struct vhost_dev *dev, + struct vhost_vring_state *ring) +{ + error_report("vhost-user trying to send unhandled ioctl"); + return -1; +} + +static int vhost_set_vring(struct vhost_dev *dev, + unsigned long int request, + struct vhost_vring_state *ring) +{ + VhostUserMsg msg = { + .request = request, + .flags = VHOST_USER_VERSION, + .payload.state = *ring, + .size = sizeof(msg.payload.state), + }; + + vhost_user_write(dev, &msg, NULL, 0); + + return 0; +} + +static int vhost_user_set_vring_num(struct vhost_dev *dev, + struct vhost_vring_state *ring) +{ + return vhost_set_vring(dev, VHOST_USER_SET_VRING_NUM, ring); +} + +static int vhost_user_set_vring_base(struct vhost_dev *dev, + struct vhost_vring_state *ring) +{ + return vhost_set_vring(dev, VHOST_USER_SET_VRING_BASE, ring); +} + +static int vhost_user_set_vring_enable(struct vhost_dev *dev, int enable) +{ + int i; + + if (!virtio_has_feature(dev->features, VHOST_USER_F_PROTOCOL_FEATURES)) { + return -1; + } + + for (i = 0; i < dev->nvqs; ++i) { + struct vhost_vring_state state = { + .index = dev->vq_index + i, + .num = enable, + }; + + vhost_set_vring(dev, VHOST_USER_SET_VRING_ENABLE, &state); + } + + return 0; +} + +static int vhost_user_get_vring_base(struct vhost_dev *dev, + struct vhost_vring_state *ring) +{ + VhostUserMsg msg = { + .request = VHOST_USER_GET_VRING_BASE, + .flags = VHOST_USER_VERSION, + .payload.state = *ring, + .size = sizeof(msg.payload.state), + }; + + vhost_user_write(dev, &msg, NULL, 0); + + if (vhost_user_read(dev, &msg) < 0) { return 0; } - if (need_reply) { - if (vhost_user_read(dev, &msg) < 0) { - return 0; - } + if (msg.request != VHOST_USER_GET_VRING_BASE) { + error_report("Received unexpected msg type. Expected %d received %d", + VHOST_USER_GET_VRING_BASE, msg.request); + return -1; + } - if (msg_request != msg.request) { - error_report("Received unexpected msg type." - " Expected %d received %d", msg_request, msg.request); - return -1; - } + if (msg.size != sizeof(msg.payload.state)) { + error_report("Received bad msg size."); + return -1; + } - switch (msg_request) { - case VHOST_USER_GET_FEATURES: - if (msg.size != sizeof(m.u64)) { - error_report("Received bad msg size."); - return -1; - } - *((__u64 *) arg) = msg.u64; - break; - case VHOST_USER_GET_VRING_BASE: - if (msg.size != sizeof(m.state)) { - error_report("Received bad msg size."); - return -1; - } - memcpy(arg, &msg.state, sizeof(struct vhost_vring_state)); - break; - default: - error_report("Received unexpected msg type."); - return -1; - break; - } + *ring = msg.payload.state; + + return 0; +} + +static int vhost_set_vring_file(struct vhost_dev *dev, + VhostUserRequest request, + struct vhost_vring_file *file) +{ + int fds[VHOST_MEMORY_MAX_NREGIONS]; + size_t fd_num = 0; + VhostUserMsg msg = { + .request = request, + .flags = VHOST_USER_VERSION, + .payload.u64 = file->index & VHOST_USER_VRING_IDX_MASK, + .size = sizeof(msg.payload.u64), + }; + + if (ioeventfd_enabled() && file->fd > 0) { + fds[fd_num++] = file->fd; + } else { + msg.payload.u64 |= VHOST_USER_VRING_NOFD_MASK; } + vhost_user_write(dev, &msg, fds, fd_num); + + return 0; +} + +static int vhost_user_set_vring_kick(struct vhost_dev *dev, + struct vhost_vring_file *file) +{ + return vhost_set_vring_file(dev, VHOST_USER_SET_VRING_KICK, file); +} + +static int vhost_user_set_vring_call(struct vhost_dev *dev, + struct vhost_vring_file *file) +{ + return vhost_set_vring_file(dev, VHOST_USER_SET_VRING_CALL, file); +} + +static int vhost_user_set_u64(struct vhost_dev *dev, int request, uint64_t u64) +{ + VhostUserMsg msg = { + .request = request, + .flags = VHOST_USER_VERSION, + .payload.u64 = u64, + .size = sizeof(msg.payload.u64), + }; + + vhost_user_write(dev, &msg, NULL, 0); + + return 0; +} + +static int vhost_user_set_features(struct vhost_dev *dev, + uint64_t features) +{ + return vhost_user_set_u64(dev, VHOST_USER_SET_FEATURES, features); +} + +static int vhost_user_set_protocol_features(struct vhost_dev *dev, + uint64_t features) +{ + return vhost_user_set_u64(dev, VHOST_USER_SET_PROTOCOL_FEATURES, features); +} + +static int vhost_user_get_u64(struct vhost_dev *dev, int request, uint64_t *u64) +{ + VhostUserMsg msg = { + .request = request, + .flags = VHOST_USER_VERSION, + }; + + if (vhost_user_one_time_request(request) && dev->vq_index != 0) { + return 0; + } + + vhost_user_write(dev, &msg, NULL, 0); + + if (vhost_user_read(dev, &msg) < 0) { + return 0; + } + + if (msg.request != request) { + error_report("Received unexpected msg type. Expected %d received %d", + request, msg.request); + return -1; + } + + if (msg.size != sizeof(msg.payload.u64)) { + error_report("Received bad msg size."); + return -1; + } + + *u64 = msg.payload.u64; + + return 0; +} + +static int vhost_user_get_features(struct vhost_dev *dev, uint64_t *features) +{ + return vhost_user_get_u64(dev, VHOST_USER_GET_FEATURES, features); +} + +static int vhost_user_set_owner(struct vhost_dev *dev) +{ + VhostUserMsg msg = { + .request = VHOST_USER_SET_OWNER, + .flags = VHOST_USER_VERSION, + }; + + vhost_user_write(dev, &msg, NULL, 0); + + return 0; +} + +static int vhost_user_reset_device(struct vhost_dev *dev) +{ + VhostUserMsg msg = { + .request = VHOST_USER_RESET_OWNER, + .flags = VHOST_USER_VERSION, + }; + + vhost_user_write(dev, &msg, NULL, 0); + return 0; } static int vhost_user_init(struct vhost_dev *dev, void *opaque) { + uint64_t features; + int err; + assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); dev->opaque = opaque; + err = vhost_user_get_features(dev, &features); + if (err < 0) { + return err; + } + + if (virtio_has_feature(features, VHOST_USER_F_PROTOCOL_FEATURES)) { + dev->backend_features |= 1ULL << VHOST_USER_F_PROTOCOL_FEATURES; + + err = vhost_user_get_u64(dev, VHOST_USER_GET_PROTOCOL_FEATURES, + &features); + if (err < 0) { + return err; + } + + dev->protocol_features = features & VHOST_USER_PROTOCOL_FEATURE_MASK; + err = vhost_user_set_protocol_features(dev, dev->protocol_features); + if (err < 0) { + return err; + } + + /* query the max queues we support if backend supports Multiple Queue */ + if (dev->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_MQ)) { + err = vhost_user_get_u64(dev, VHOST_USER_GET_QUEUE_NUM, + &dev->max_queues); + if (err < 0) { + return err; + } + } + } + + if (dev->migration_blocker == NULL && + !virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_LOG_SHMFD)) { + error_setg(&dev->migration_blocker, + "Migration disabled: vhost-user backend lacks " + "VHOST_USER_PROTOCOL_F_LOG_SHMFD feature."); + } + return 0; } @@ -343,9 +566,92 @@ static int vhost_user_cleanup(struct vhost_dev *dev) return 0; } +static int vhost_user_get_vq_index(struct vhost_dev *dev, int idx) +{ + assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); + + return idx; +} + +static int vhost_user_memslots_limit(struct vhost_dev *dev) +{ + return VHOST_MEMORY_MAX_NREGIONS; +} + +static bool vhost_user_requires_shm_log(struct vhost_dev *dev) +{ + assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); + + return virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_LOG_SHMFD); +} + +static int vhost_user_migration_done(struct vhost_dev *dev, char* mac_addr) +{ + VhostUserMsg msg = { 0 }; + int err; + + assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); + + /* If guest supports GUEST_ANNOUNCE do nothing */ + if (virtio_has_feature(dev->acked_features, VIRTIO_NET_F_GUEST_ANNOUNCE)) { + return 0; + } + + /* if backend supports VHOST_USER_PROTOCOL_F_RARP ask it to send the RARP */ + if (virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_RARP)) { + msg.request = VHOST_USER_SEND_RARP; + msg.flags = VHOST_USER_VERSION; + memcpy((char *)&msg.payload.u64, mac_addr, 6); + msg.size = sizeof(msg.payload.u64); + + err = vhost_user_write(dev, &msg, NULL, 0); + return err; + } + return -1; +} + +static bool vhost_user_can_merge(struct vhost_dev *dev, + uint64_t start1, uint64_t size1, + uint64_t start2, uint64_t size2) +{ + ram_addr_t ram_addr; + int mfd, rfd; + MemoryRegion *mr; + + mr = qemu_ram_addr_from_host((void *)(uintptr_t)start1, &ram_addr); + assert(mr); + mfd = qemu_get_ram_fd(ram_addr); + + mr = qemu_ram_addr_from_host((void *)(uintptr_t)start2, &ram_addr); + assert(mr); + rfd = qemu_get_ram_fd(ram_addr); + + return mfd == rfd; +} + const VhostOps user_ops = { .backend_type = VHOST_BACKEND_TYPE_USER, - .vhost_call = vhost_user_call, .vhost_backend_init = vhost_user_init, - .vhost_backend_cleanup = vhost_user_cleanup - }; + .vhost_backend_cleanup = vhost_user_cleanup, + .vhost_backend_memslots_limit = vhost_user_memslots_limit, + .vhost_set_log_base = vhost_user_set_log_base, + .vhost_set_mem_table = vhost_user_set_mem_table, + .vhost_set_vring_addr = vhost_user_set_vring_addr, + .vhost_set_vring_endian = vhost_user_set_vring_endian, + .vhost_set_vring_num = vhost_user_set_vring_num, + .vhost_set_vring_base = vhost_user_set_vring_base, + .vhost_get_vring_base = vhost_user_get_vring_base, + .vhost_set_vring_kick = vhost_user_set_vring_kick, + .vhost_set_vring_call = vhost_user_set_vring_call, + .vhost_set_features = vhost_user_set_features, + .vhost_get_features = vhost_user_get_features, + .vhost_set_owner = vhost_user_set_owner, + .vhost_reset_device = vhost_user_reset_device, + .vhost_get_vq_index = vhost_user_get_vq_index, + .vhost_set_vring_enable = vhost_user_set_vring_enable, + .vhost_requires_shm_log = vhost_user_requires_shm_log, + .vhost_migration_done = vhost_user_migration_done, + .vhost_backend_can_merge = vhost_user_can_merge, +}; diff --git a/qemu/hw/virtio/vhost.c b/qemu/hw/virtio/vhost.c index 2712c6fc0..440071815 100644 --- a/qemu/hw/virtio/vhost.c +++ b/qemu/hw/virtio/vhost.c @@ -13,11 +13,14 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/virtio/vhost.h" #include "hw/hw.h" #include "qemu/atomic.h" #include "qemu/range.h" #include "qemu/error-report.h" +#include "qemu/memfd.h" #include #include "exec/address-spaces.h" #include "hw/virtio/virtio-bus.h" @@ -25,6 +28,23 @@ #include "migration/migration.h" static struct vhost_log *vhost_log; +static struct vhost_log *vhost_log_shm; + +static unsigned int used_memslots; +static QLIST_HEAD(, vhost_dev) vhost_devices = + QLIST_HEAD_INITIALIZER(vhost_devices); + +bool vhost_has_free_slot(void) +{ + unsigned int slots_limit = ~0U; + struct vhost_dev *hdev; + + QLIST_FOREACH(hdev, &vhost_devices, entry) { + unsigned int r = hdev->vhost_ops->vhost_backend_memslots_limit(hdev); + slots_limit = MIN(slots_limit, r); + } + return slots_limit > used_memslots; +} static void vhost_dev_sync_region(struct vhost_dev *dev, MemoryRegionSection *section, @@ -241,6 +261,13 @@ static void vhost_dev_assign_memory(struct vhost_dev *dev, continue; } + if (dev->vhost_ops->vhost_backend_can_merge && + !dev->vhost_ops->vhost_backend_can_merge(dev, uaddr, size, + reg->userspace_addr, + reg->memory_size)) { + continue; + } + if (merged) { --to; assert(to >= 0); @@ -286,25 +313,46 @@ static uint64_t vhost_get_log_size(struct vhost_dev *dev) } return log_size; } -static struct vhost_log *vhost_log_alloc(uint64_t size) + +static struct vhost_log *vhost_log_alloc(uint64_t size, bool share) { - struct vhost_log *log = g_malloc0(sizeof *log + size * sizeof(*(log->log))); + struct vhost_log *log; + uint64_t logsize = size * sizeof(*(log->log)); + int fd = -1; + + log = g_new0(struct vhost_log, 1); + if (share) { + log->log = qemu_memfd_alloc("vhost-log", logsize, + F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL, + &fd); + memset(log->log, 0, logsize); + } else { + log->log = g_malloc0(logsize); + } log->size = size; log->refcnt = 1; + log->fd = fd; return log; } -static struct vhost_log *vhost_log_get(uint64_t size) +static struct vhost_log *vhost_log_get(uint64_t size, bool share) { - if (!vhost_log || vhost_log->size != size) { - vhost_log = vhost_log_alloc(size); + struct vhost_log *log = share ? vhost_log_shm : vhost_log; + + if (!log || log->size != size) { + log = vhost_log_alloc(size, share); + if (share) { + vhost_log_shm = log; + } else { + vhost_log = log; + } } else { - ++vhost_log->refcnt; + ++log->refcnt; } - return vhost_log; + return log; } static void vhost_log_put(struct vhost_dev *dev, bool sync) @@ -321,20 +369,35 @@ static void vhost_log_put(struct vhost_dev *dev, bool sync) if (dev->log_size && sync) { vhost_log_sync_range(dev, 0, dev->log_size * VHOST_LOG_CHUNK - 1); } + if (vhost_log == log) { + g_free(log->log); vhost_log = NULL; + } else if (vhost_log_shm == log) { + qemu_memfd_free(log->log, log->size * sizeof(*(log->log)), + log->fd); + vhost_log_shm = NULL; } + g_free(log); } } -static inline void vhost_dev_log_resize(struct vhost_dev* dev, uint64_t size) +static bool vhost_dev_log_is_shared(struct vhost_dev *dev) { - struct vhost_log *log = vhost_log_get(size); + return dev->vhost_ops->vhost_requires_shm_log && + dev->vhost_ops->vhost_requires_shm_log(dev); +} + +static inline void vhost_dev_log_resize(struct vhost_dev *dev, uint64_t size) +{ + struct vhost_log *log = vhost_log_get(size, vhost_dev_log_is_shared(dev)); uint64_t log_base = (uintptr_t)log->log; int r; - r = dev->vhost_ops->vhost_call(dev, VHOST_SET_LOG_BASE, &log_base); + /* inform backend of log switching, this must be done before + releasing the current log, to ensure no logging is lost */ + r = dev->vhost_ops->vhost_set_log_base(dev, log_base, log); assert(r >= 0); vhost_log_put(dev, true); dev->log = log; @@ -457,6 +520,7 @@ static void vhost_set_memory(MemoryListener *listener, dev->mem_changed_start_addr = MIN(dev->mem_changed_start_addr, start_addr); dev->mem_changed_end_addr = MAX(dev->mem_changed_end_addr, start_addr + size - 1); dev->memory_changed = true; + used_memslots = dev->mem->nregions; } static bool vhost_section(MemoryRegionSection *section) @@ -500,7 +564,7 @@ static void vhost_commit(MemoryListener *listener) } if (!dev->log_enabled) { - r = dev->vhost_ops->vhost_call(dev, VHOST_SET_MEM_TABLE, dev->mem); + r = dev->vhost_ops->vhost_set_mem_table(dev, dev->mem); assert(r >= 0); dev->memory_changed = false; return; @@ -513,7 +577,7 @@ static void vhost_commit(MemoryListener *listener) if (dev->log_size < log_size) { vhost_dev_log_resize(dev, log_size + VHOST_LOG_BUFFER); } - r = dev->vhost_ops->vhost_call(dev, VHOST_SET_MEM_TABLE, dev->mem); + r = dev->vhost_ops->vhost_set_mem_table(dev, dev->mem); assert(r >= 0); /* To log less, can only decrease log size after table update. */ if (dev->log_size > log_size + VHOST_LOG_BUFFER) { @@ -581,7 +645,7 @@ static int vhost_virtqueue_set_addr(struct vhost_dev *dev, .log_guest_addr = vq->used_phys, .flags = enable_log ? (1 << VHOST_VRING_F_LOG) : 0, }; - int r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_ADDR, &addr); + int r = dev->vhost_ops->vhost_set_vring_addr(dev, &addr); if (r < 0) { return -errno; } @@ -595,19 +659,20 @@ static int vhost_dev_set_features(struct vhost_dev *dev, bool enable_log) if (enable_log) { features |= 0x1ULL << VHOST_F_LOG_ALL; } - r = dev->vhost_ops->vhost_call(dev, VHOST_SET_FEATURES, &features); + r = dev->vhost_ops->vhost_set_features(dev, features); return r < 0 ? -errno : 0; } static int vhost_dev_set_log(struct vhost_dev *dev, bool enable_log) { - int r, t, i; + int r, t, i, idx; r = vhost_dev_set_features(dev, enable_log); if (r < 0) { goto err_features; } for (i = 0; i < dev->nvqs; ++i) { - r = vhost_virtqueue_set_addr(dev, dev->vqs + i, i, + idx = dev->vhost_ops->vhost_get_vq_index(dev, dev->vq_index + i); + r = vhost_virtqueue_set_addr(dev, dev->vqs + i, idx, enable_log); if (r < 0) { goto err_vq; @@ -616,7 +681,8 @@ static int vhost_dev_set_log(struct vhost_dev *dev, bool enable_log) return 0; err_vq: for (; i >= 0; --i) { - t = vhost_virtqueue_set_addr(dev, dev->vqs + i, i, + idx = dev->vhost_ops->vhost_get_vq_index(dev, dev->vq_index + i); + t = vhost_virtqueue_set_addr(dev, dev->vqs + i, idx, dev->log_enabled); assert(t >= 0); } @@ -691,6 +757,27 @@ static void vhost_log_stop(MemoryListener *listener, /* FIXME: implement */ } +/* The vhost driver natively knows how to handle the vrings of non + * cross-endian legacy devices and modern devices. Only legacy devices + * exposed to a bi-endian guest may require the vhost driver to use a + * specific endianness. + */ +static inline bool vhost_needs_vring_endian(VirtIODevice *vdev) +{ + if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) { + return false; + } +#ifdef TARGET_IS_BIENDIAN +#ifdef HOST_WORDS_BIGENDIAN + return vdev->device_endian == VIRTIO_DEVICE_ENDIAN_LITTLE; +#else + return vdev->device_endian == VIRTIO_DEVICE_ENDIAN_BIG; +#endif +#else + return false; +#endif +} + static int vhost_virtqueue_set_vring_endian_legacy(struct vhost_dev *dev, bool is_big_endian, int vhost_vq_index) @@ -700,7 +787,7 @@ static int vhost_virtqueue_set_vring_endian_legacy(struct vhost_dev *dev, .num = is_big_endian }; - if (!dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_ENDIAN, &s)) { + if (!dev->vhost_ops->vhost_set_vring_endian(dev, &s)) { return 0; } @@ -719,7 +806,7 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, { hwaddr s, l, a; int r; - int vhost_vq_index = idx - dev->vq_index; + int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, idx); struct vhost_vring_file file = { .index = vhost_vq_index }; @@ -728,22 +815,20 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, }; struct VirtQueue *vvq = virtio_get_queue(vdev, idx); - assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); vq->num = state.num = virtio_queue_get_num(vdev, idx); - r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_NUM, &state); + r = dev->vhost_ops->vhost_set_vring_num(dev, &state); if (r) { return -errno; } state.num = virtio_queue_get_last_avail_idx(vdev, idx); - r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_BASE, &state); + r = dev->vhost_ops->vhost_set_vring_base(dev, &state); if (r) { return -errno; } - if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1) && - virtio_legacy_is_cross_endian(vdev)) { + if (vhost_needs_vring_endian(vdev)) { r = vhost_virtqueue_set_vring_endian_legacy(dev, virtio_is_big_endian(vdev), vhost_vq_index); @@ -789,7 +874,7 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, } file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq)); - r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_KICK, &file); + r = dev->vhost_ops->vhost_set_vring_kick(dev, &file); if (r) { r = -errno; goto fail_kick; @@ -798,6 +883,14 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, /* Clear and discard previous events if any. */ event_notifier_test_and_clear(&vq->masked_notifier); + /* Init vring in unmasked state, unless guest_notifier_mask + * will do it later. + */ + if (!vdev->use_guest_notifier_mask) { + /* TODO: check and handle errors. */ + vhost_virtqueue_mask(dev, vdev, idx, false); + } + return 0; fail_kick: @@ -822,13 +915,13 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev, struct vhost_virtqueue *vq, unsigned idx) { - int vhost_vq_index = idx - dev->vq_index; + int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, idx); struct vhost_vring_state state = { .index = vhost_vq_index, }; int r; - assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); - r = dev->vhost_ops->vhost_call(dev, VHOST_GET_VRING_BASE, &state); + + r = dev->vhost_ops->vhost_get_vring_base(dev, &state); if (r < 0) { fprintf(stderr, "vhost VQ %d ring restore failed: %d\n", idx, r); fflush(stderr); @@ -839,8 +932,7 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev, /* In the cross-endian case, we need to reset the vring endianness to * native as legacy devices expect so by default. */ - if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1) && - virtio_legacy_is_cross_endian(vdev)) { + if (vhost_needs_vring_endian(vdev)) { r = vhost_virtqueue_set_vring_endian_legacy(dev, !virtio_is_big_endian(vdev), vhost_vq_index); @@ -875,8 +967,9 @@ static void vhost_eventfd_del(MemoryListener *listener, static int vhost_virtqueue_init(struct vhost_dev *dev, struct vhost_virtqueue *vq, int n) { + int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, n); struct vhost_vring_file file = { - .index = n, + .index = vhost_vq_index, }; int r = event_notifier_init(&vq->masked_notifier, 0); if (r < 0) { @@ -884,7 +977,7 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, } file.fd = event_notifier_get_fd(&vq->masked_notifier); - r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_CALL, &file); + r = dev->vhost_ops->vhost_set_vring_call(dev, &file); if (r) { r = -errno; goto fail_call; @@ -906,6 +999,8 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, uint64_t features; int i, r; + hdev->migration_blocker = NULL; + if (vhost_set_backend_type(hdev, backend_type) < 0) { close((uintptr_t)opaque); return -1; @@ -916,18 +1011,26 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, return -errno; } - r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_OWNER, NULL); + if (used_memslots > hdev->vhost_ops->vhost_backend_memslots_limit(hdev)) { + fprintf(stderr, "vhost backend memory slots limit is less" + " than current number of present memory slots\n"); + close((uintptr_t)opaque); + return -1; + } + QLIST_INSERT_HEAD(&vhost_devices, hdev, entry); + + r = hdev->vhost_ops->vhost_set_owner(hdev); if (r < 0) { goto fail; } - r = hdev->vhost_ops->vhost_call(hdev, VHOST_GET_FEATURES, &features); + r = hdev->vhost_ops->vhost_get_features(hdev, &features); if (r < 0) { goto fail; } for (i = 0; i < hdev->nvqs; ++i) { - r = vhost_virtqueue_init(hdev, hdev->vqs + i, i); + r = vhost_virtqueue_init(hdev, hdev->vqs + i, hdev->vq_index + i); if (r < 0) { goto fail_vq; } @@ -949,12 +1052,21 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, .eventfd_del = vhost_eventfd_del, .priority = 10 }; - hdev->migration_blocker = NULL; - if (!(hdev->features & (0x1ULL << VHOST_F_LOG_ALL))) { - error_setg(&hdev->migration_blocker, - "Migration disabled: vhost lacks VHOST_F_LOG_ALL feature."); + + if (hdev->migration_blocker == NULL) { + if (!(hdev->features & (0x1ULL << VHOST_F_LOG_ALL))) { + error_setg(&hdev->migration_blocker, + "Migration disabled: vhost lacks VHOST_F_LOG_ALL feature."); + } else if (!qemu_memfd_check()) { + error_setg(&hdev->migration_blocker, + "Migration disabled: failed to allocate shared memory"); + } + } + + if (hdev->migration_blocker != NULL) { migrate_add_blocker(hdev->migration_blocker); } + hdev->mem = g_malloc0(offsetof(struct vhost_memory, regions)); hdev->n_mem_sections = 0; hdev->mem_sections = NULL; @@ -972,6 +1084,7 @@ fail_vq: fail: r = -errno; hdev->vhost_ops->vhost_backend_cleanup(hdev); + QLIST_REMOVE(hdev, entry); return r; } @@ -989,6 +1102,7 @@ void vhost_dev_cleanup(struct vhost_dev *hdev) g_free(hdev->mem); g_free(hdev->mem_sections); hdev->vhost_ops->vhost_backend_cleanup(hdev); + QLIST_REMOVE(hdev, entry); } /* Stop processing guest IO notifications in qemu. @@ -1066,18 +1180,17 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, { struct VirtQueue *vvq = virtio_get_queue(vdev, n); int r, index = n - hdev->vq_index; + struct vhost_vring_file file; - assert(n >= hdev->vq_index && n < hdev->vq_index + hdev->nvqs); - - struct vhost_vring_file file = { - .index = index - }; if (mask) { + assert(vdev->use_guest_notifier_mask); file.fd = event_notifier_get_fd(&hdev->vqs[index].masked_notifier); } else { file.fd = event_notifier_get_fd(virtio_queue_get_guest_notifier(vvq)); } - r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_VRING_CALL, &file); + + file.index = hdev->vhost_ops->vhost_get_vq_index(hdev, n); + r = hdev->vhost_ops->vhost_set_vring_call(hdev, &file); assert(r >= 0); } @@ -1119,7 +1232,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) if (r < 0) { goto fail_features; } - r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_MEM_TABLE, hdev->mem); + r = hdev->vhost_ops->vhost_set_mem_table(hdev, hdev->mem); if (r < 0) { r = -errno; goto fail_mem; @@ -1138,10 +1251,12 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) uint64_t log_base; hdev->log_size = vhost_get_log_size(hdev); - hdev->log = vhost_log_get(hdev->log_size); + hdev->log = vhost_log_get(hdev->log_size, + vhost_dev_log_is_shared(hdev)); log_base = (uintptr_t)hdev->log->log; - r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_LOG_BASE, - hdev->log_size ? &log_base : NULL); + r = hdev->vhost_ops->vhost_set_log_base(hdev, + hdev->log_size ? log_base : 0, + hdev->log); if (r < 0) { r = -errno; goto fail_log; diff --git a/qemu/hw/virtio/virtio-balloon.c b/qemu/hw/virtio/virtio-balloon.c index 3577b7af9..9dbe68179 100644 --- a/qemu/hw/virtio/virtio-balloon.c +++ b/qemu/hw/virtio/virtio-balloon.c @@ -13,12 +13,12 @@ * */ +#include "qemu/osdep.h" #include "qemu/iov.h" #include "qemu/timer.h" #include "qemu-common.h" #include "hw/virtio/virtio.h" #include "hw/i386/pc.h" -#include "cpu.h" #include "sysemu/balloon.h" #include "hw/virtio/virtio-balloon.h" #include "sysemu/kvm.h" @@ -34,12 +34,16 @@ #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" +#define BALLOON_PAGE_SIZE (1 << VIRTIO_BALLOON_PFN_SHIFT) + static void balloon_page(void *addr, int deflate) { #if defined(__linux__) - if (!kvm_enabled() || kvm_has_sync_mmu()) - qemu_madvise(addr, TARGET_PAGE_SIZE, + if (!qemu_balloon_is_inhibited() && (!kvm_enabled() || + kvm_has_sync_mmu())) { + qemu_madvise(addr, BALLOON_PAGE_SIZE, deflate ? QEMU_MADV_WILLNEED : QEMU_MADV_DONTNEED); + } #endif } @@ -50,6 +54,7 @@ static const char *balloon_stat_names[] = { [VIRTIO_BALLOON_S_MINFLT] = "stat-minor-faults", [VIRTIO_BALLOON_S_MEMFREE] = "stat-free-memory", [VIRTIO_BALLOON_S_MEMTOT] = "stat-total-memory", + [VIRTIO_BALLOON_S_AVAIL] = "stat-available-memory", [VIRTIO_BALLOON_S_NR] = NULL }; @@ -70,7 +75,7 @@ static inline void reset_stats(VirtIOBalloon *dev) static bool balloon_stats_supported(const VirtIOBalloon *s) { VirtIODevice *vdev = VIRTIO_DEVICE(s); - return virtio_has_feature(vdev, VIRTIO_BALLOON_F_STATS_VQ); + return virtio_vdev_has_feature(vdev, VIRTIO_BALLOON_F_STATS_VQ); } static bool balloon_stats_enabled(const VirtIOBalloon *s) @@ -98,39 +103,43 @@ static void balloon_stats_poll_cb(void *opaque) VirtIOBalloon *s = opaque; VirtIODevice *vdev = VIRTIO_DEVICE(s); - if (!balloon_stats_supported(s)) { + if (s->stats_vq_elem == NULL || !balloon_stats_supported(s)) { /* re-schedule */ balloon_stats_change_timer(s, s->stats_poll_interval); return; } - virtqueue_push(s->svq, &s->stats_vq_elem, s->stats_vq_offset); + virtqueue_push(s->svq, s->stats_vq_elem, s->stats_vq_offset); virtio_notify(vdev, s->svq); + g_free(s->stats_vq_elem); + s->stats_vq_elem = NULL; } -static void balloon_stats_get_all(Object *obj, struct Visitor *v, - void *opaque, const char *name, Error **errp) +static void balloon_stats_get_all(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { Error *err = NULL; VirtIOBalloon *s = opaque; int i; - visit_start_struct(v, NULL, "guest-stats", name, 0, &err); + visit_start_struct(v, name, NULL, 0, &err); if (err) { goto out; } - visit_type_int(v, &s->stats_last_update, "last-update", &err); + visit_type_int(v, "last-update", &s->stats_last_update, &err); if (err) { goto out_end; } - visit_start_struct(v, NULL, NULL, "stats", 0, &err); + visit_start_struct(v, "stats", NULL, 0, &err); if (err) { goto out_end; } - for (i = 0; !err && i < VIRTIO_BALLOON_S_NR; i++) { - visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i], - &err); + for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) { + visit_type_uint64(v, balloon_stat_names[i], &s->stats[i], &err); + if (err) { + break; + } } error_propagate(errp, err); err = NULL; @@ -144,23 +153,23 @@ out: error_propagate(errp, err); } -static void balloon_stats_get_poll_interval(Object *obj, struct Visitor *v, - void *opaque, const char *name, +static void balloon_stats_get_poll_interval(Object *obj, Visitor *v, + const char *name, void *opaque, Error **errp) { VirtIOBalloon *s = opaque; - visit_type_int(v, &s->stats_poll_interval, name, errp); + visit_type_int(v, name, &s->stats_poll_interval, errp); } -static void balloon_stats_set_poll_interval(Object *obj, struct Visitor *v, - void *opaque, const char *name, +static void balloon_stats_set_poll_interval(Object *obj, Visitor *v, + const char *name, void *opaque, Error **errp) { VirtIOBalloon *s = opaque; Error *local_err = NULL; int64_t value; - visit_type_int(v, &value, name, &local_err); + visit_type_int(v, name, &value, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -203,14 +212,18 @@ static void balloon_stats_set_poll_interval(Object *obj, struct Visitor *v, static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) { VirtIOBalloon *s = VIRTIO_BALLOON(vdev); - VirtQueueElement elem; + VirtQueueElement *elem; MemoryRegionSection section; - while (virtqueue_pop(vq, &elem)) { + for (;;) { size_t offset = 0; uint32_t pfn; + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!elem) { + return; + } - while (iov_to_buf(elem.out_sg, elem.out_num, offset, &pfn, 4) == 4) { + while (iov_to_buf(elem->out_sg, elem->out_num, offset, &pfn, 4) == 4) { ram_addr_t pa; ram_addr_t addr; int p = virtio_ldl_p(vdev, &pfn); @@ -233,23 +246,34 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) memory_region_unref(section.mr); } - virtqueue_push(vq, &elem, offset); + virtqueue_push(vq, elem, offset); virtio_notify(vdev, vq); + g_free(elem); } } static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) { VirtIOBalloon *s = VIRTIO_BALLOON(vdev); - VirtQueueElement *elem = &s->stats_vq_elem; + VirtQueueElement *elem; VirtIOBalloonStat stat; size_t offset = 0; qemu_timeval tv; - if (!virtqueue_pop(vq, elem)) { + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!elem) { goto out; } + if (s->stats_vq_elem != NULL) { + /* This should never happen if the driver follows the spec. */ + virtqueue_push(vq, s->stats_vq_elem, 0); + virtio_notify(vdev, vq); + g_free(s->stats_vq_elem); + } + + s->stats_vq_elem = elem; + /* Initialize the stats to get rid of any stale values. This is only * needed to handle the case where a guest supports fewer stats than it * used to (ie. it has booted into an old kernel). @@ -292,6 +316,39 @@ static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data) memcpy(config_data, &config, sizeof(struct virtio_balloon_config)); } +static int build_dimm_list(Object *obj, void *opaque) +{ + GSList **list = opaque; + + if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { + DeviceState *dev = DEVICE(obj); + if (dev->realized) { /* only realized DIMMs matter */ + *list = g_slist_prepend(*list, dev); + } + } + + object_child_foreach(obj, build_dimm_list, opaque); + return 0; +} + +static ram_addr_t get_current_ram_size(void) +{ + GSList *list = NULL, *item; + ram_addr_t size = ram_size; + + build_dimm_list(qdev_get_machine(), &list); + for (item = list; item; item = g_slist_next(item)) { + Object *obj = OBJECT(item->data); + if (!strcmp(object_get_typename(obj), TYPE_PC_DIMM)) { + size += object_property_get_int(obj, PC_DIMM_SIZE_PROP, + &error_abort); + } + } + g_slist_free(list); + + return size; +} + static void virtio_balloon_set_config(VirtIODevice *vdev, const uint8_t *config_data) { @@ -370,6 +427,10 @@ static int virtio_balloon_load_device(VirtIODevice *vdev, QEMUFile *f, s->num_pages = qemu_get_be32(f); s->actual = qemu_get_be32(f); + + if (balloon_stats_enabled(s)) { + balloon_stats_change_timer(s, s->stats_poll_interval); + } return 0; } @@ -412,6 +473,16 @@ static void virtio_balloon_device_unrealize(DeviceState *dev, Error **errp) virtio_cleanup(vdev); } +static void virtio_balloon_device_reset(VirtIODevice *vdev) +{ + VirtIOBalloon *s = VIRTIO_BALLOON(vdev); + + if (s->stats_vq_elem != NULL) { + g_free(s->stats_vq_elem); + s->stats_vq_elem = NULL; + } +} + static void virtio_balloon_instance_init(Object *obj) { VirtIOBalloon *s = VIRTIO_BALLOON(obj); @@ -440,6 +511,7 @@ static void virtio_balloon_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_MISC, dc->categories); vdc->realize = virtio_balloon_device_realize; vdc->unrealize = virtio_balloon_device_unrealize; + vdc->reset = virtio_balloon_device_reset; vdc->get_config = virtio_balloon_get_config; vdc->set_config = virtio_balloon_set_config; vdc->get_features = virtio_balloon_get_features; diff --git a/qemu/hw/virtio/virtio-bus.c b/qemu/hw/virtio/virtio-bus.c index febda76b9..574f0e23f 100644 --- a/qemu/hw/virtio/virtio-bus.c +++ b/qemu/hw/virtio/virtio-bus.c @@ -22,6 +22,7 @@ * */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "qemu/error-report.h" #include "hw/qdev.h" @@ -56,6 +57,9 @@ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp) assert(vdc->get_features != NULL); vdev->host_features = vdc->get_features(vdev, vdev->host_features, errp); + if (klass->post_plugged != NULL) { + klass->post_plugged(qbus->parent, errp); + } } /* Reset the virtio_bus */ diff --git a/qemu/hw/virtio/virtio-mmio.c b/qemu/hw/virtio/virtio-mmio.c index 18660b07b..d4cd91f8c 100644 --- a/qemu/hw/virtio/virtio-mmio.c +++ b/qemu/hw/virtio/virtio-mmio.c @@ -19,6 +19,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/virtio/virtio.h" #include "qemu/host-utils.h" diff --git a/qemu/hw/virtio/virtio-pci.c b/qemu/hw/virtio/virtio-pci.c index c024161f5..bfedbbf17 100644 --- a/qemu/hw/virtio/virtio-pci.c +++ b/qemu/hw/virtio/virtio-pci.c @@ -15,7 +15,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include +#include "qemu/osdep.h" #include "standard-headers/linux/virtio_pci.h" #include "hw/virtio/virtio.h" @@ -26,6 +26,7 @@ #include "hw/virtio/virtio-balloon.h" #include "hw/virtio/virtio-input.h" #include "hw/pci/pci.h" +#include "qapi/error.h" #include "qemu/error-report.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" @@ -47,6 +48,7 @@ static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size, VirtIOPCIProxy *dev); +static void virtio_pci_reset(DeviceState *qdev); /* virtio device */ /* DeviceState to VirtIOPCIProxy. For use off data-path. TODO: use QOM. */ @@ -86,6 +88,129 @@ static void virtio_pci_save_config(DeviceState *d, QEMUFile *f) qemu_put_be16(f, vdev->config_vector); } +static void virtio_pci_load_modern_queue_state(VirtIOPCIQueue *vq, + QEMUFile *f) +{ + vq->num = qemu_get_be16(f); + vq->enabled = qemu_get_be16(f); + vq->desc[0] = qemu_get_be32(f); + vq->desc[1] = qemu_get_be32(f); + vq->avail[0] = qemu_get_be32(f); + vq->avail[1] = qemu_get_be32(f); + vq->used[0] = qemu_get_be32(f); + vq->used[1] = qemu_get_be32(f); +} + +static bool virtio_pci_has_extra_state(DeviceState *d) +{ + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); + + return proxy->flags & VIRTIO_PCI_FLAG_MIGRATE_EXTRA; +} + +static int get_virtio_pci_modern_state(QEMUFile *f, void *pv, size_t size) +{ + VirtIOPCIProxy *proxy = pv; + int i; + + proxy->dfselect = qemu_get_be32(f); + proxy->gfselect = qemu_get_be32(f); + proxy->guest_features[0] = qemu_get_be32(f); + proxy->guest_features[1] = qemu_get_be32(f); + for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { + virtio_pci_load_modern_queue_state(&proxy->vqs[i], f); + } + + return 0; +} + +static void virtio_pci_save_modern_queue_state(VirtIOPCIQueue *vq, + QEMUFile *f) +{ + qemu_put_be16(f, vq->num); + qemu_put_be16(f, vq->enabled); + qemu_put_be32(f, vq->desc[0]); + qemu_put_be32(f, vq->desc[1]); + qemu_put_be32(f, vq->avail[0]); + qemu_put_be32(f, vq->avail[1]); + qemu_put_be32(f, vq->used[0]); + qemu_put_be32(f, vq->used[1]); +} + +static void put_virtio_pci_modern_state(QEMUFile *f, void *pv, size_t size) +{ + VirtIOPCIProxy *proxy = pv; + int i; + + qemu_put_be32(f, proxy->dfselect); + qemu_put_be32(f, proxy->gfselect); + qemu_put_be32(f, proxy->guest_features[0]); + qemu_put_be32(f, proxy->guest_features[1]); + for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { + virtio_pci_save_modern_queue_state(&proxy->vqs[i], f); + } +} + +static const VMStateInfo vmstate_info_virtio_pci_modern_state = { + .name = "virtqueue_state", + .get = get_virtio_pci_modern_state, + .put = put_virtio_pci_modern_state, +}; + +static bool virtio_pci_modern_state_needed(void *opaque) +{ + VirtIOPCIProxy *proxy = opaque; + + return !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN); +} + +static const VMStateDescription vmstate_virtio_pci_modern_state = { + .name = "virtio_pci/modern_state", + .version_id = 1, + .minimum_version_id = 1, + .needed = &virtio_pci_modern_state_needed, + .fields = (VMStateField[]) { + { + .name = "modern_state", + .version_id = 0, + .field_exists = NULL, + .size = 0, + .info = &vmstate_info_virtio_pci_modern_state, + .flags = VMS_SINGLE, + .offset = 0, + }, + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_virtio_pci = { + .name = "virtio_pci", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription*[]) { + &vmstate_virtio_pci_modern_state, + NULL + } +}; + +static void virtio_pci_save_extra_state(DeviceState *d, QEMUFile *f) +{ + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); + + vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL); +} + +static int virtio_pci_load_extra_state(DeviceState *d, QEMUFile *f) +{ + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); + + return vmstate_load_state(f, &vmstate_virtio_pci, proxy, 1); +} + static void virtio_pci_save_queue(DeviceState *d, int n, QEMUFile *f) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); @@ -133,6 +258,7 @@ static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f) if (vector != VIRTIO_NO_VECTOR) { return msix_vector_use(&proxy->pci_dev, vector); } + return 0; } @@ -146,7 +272,10 @@ static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy, EventNotifier *notifier = virtio_queue_get_host_notifier(vq); bool legacy = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_LEGACY); bool modern = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN); + bool fast_mmio = kvm_ioeventfd_any_length_enabled(); + bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY; MemoryRegion *modern_mr = &proxy->notify.mr; + MemoryRegion *modern_notify_mr = &proxy->notify_pio.mr; MemoryRegion *legacy_mr = &proxy->bar; hwaddr modern_addr = QEMU_VIRTIO_PCI_QUEUE_MEM_MULT * virtio_get_queue_index(vq); @@ -162,8 +291,17 @@ static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy, } virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler); if (modern) { - memory_region_add_eventfd(modern_mr, modern_addr, 2, - true, n, notifier); + if (fast_mmio) { + memory_region_add_eventfd(modern_mr, modern_addr, 0, + false, n, notifier); + } else { + memory_region_add_eventfd(modern_mr, modern_addr, 2, + false, n, notifier); + } + if (modern_pio) { + memory_region_add_eventfd(modern_notify_mr, 0, 2, + true, n, notifier); + } } if (legacy) { memory_region_add_eventfd(legacy_mr, legacy_addr, 2, @@ -171,8 +309,17 @@ static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy, } } else { if (modern) { - memory_region_del_eventfd(modern_mr, modern_addr, 2, - true, n, notifier); + if (fast_mmio) { + memory_region_del_eventfd(modern_mr, modern_addr, 0, + false, n, notifier); + } else { + memory_region_del_eventfd(modern_mr, modern_addr, 2, + false, n, notifier); + } + if (modern_pio) { + memory_region_del_eventfd(modern_notify_mr, 0, 2, + true, n, notifier); + } } if (legacy) { memory_region_del_eventfd(legacy_mr, legacy_addr, 2, @@ -259,9 +406,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) case VIRTIO_PCI_QUEUE_PFN: pa = (hwaddr)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT; if (pa == 0) { - virtio_pci_stop_ioeventfd(proxy); - virtio_reset(vdev); - msix_unuse_all_vectors(&proxy->pci_dev); + virtio_pci_reset(DEVICE(proxy)); } else virtio_queue_set_addr(vdev, vdev->queue_sel, pa); @@ -287,8 +432,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) } if (vdev->status == 0) { - virtio_reset(vdev); - msix_unuse_all_vectors(&proxy->pci_dev); + virtio_pci_reset(DEVICE(proxy)); } /* Linux before 2.6.34 drives the device without enabling @@ -590,7 +734,7 @@ static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy, int ret; if (irqfd->users == 0) { - ret = kvm_irqchip_add_msi_route(kvm_state, msg); + ret = kvm_irqchip_add_msi_route(kvm_state, msg, &proxy->pci_dev); if (ret < 0) { return ret; } @@ -661,7 +805,7 @@ static int kvm_virtio_pci_vector_use(VirtIOPCIProxy *proxy, int nvqs) /* If guest supports masking, set up irqfd now. * Otherwise, delay until unmasked in the frontend. */ - if (k->guest_notifier_mask) { + if (vdev->use_guest_notifier_mask && k->guest_notifier_mask) { ret = kvm_virtio_pci_irqfd_use(proxy, queue_no, vector); if (ret < 0) { kvm_virtio_pci_vq_vector_release(proxy, vector); @@ -677,7 +821,7 @@ undo: if (vector >= msix_nr_vectors_allocated(dev)) { continue; } - if (k->guest_notifier_mask) { + if (vdev->use_guest_notifier_mask && k->guest_notifier_mask) { kvm_virtio_pci_irqfd_release(proxy, queue_no, vector); } kvm_virtio_pci_vq_vector_release(proxy, vector); @@ -704,7 +848,7 @@ static void kvm_virtio_pci_vector_release(VirtIOPCIProxy *proxy, int nvqs) /* If guest supports masking, clean up irqfd now. * Otherwise, it was cleaned when masked in the frontend. */ - if (k->guest_notifier_mask) { + if (vdev->use_guest_notifier_mask && k->guest_notifier_mask) { kvm_virtio_pci_irqfd_release(proxy, queue_no, vector); } kvm_virtio_pci_vq_vector_release(proxy, vector); @@ -726,7 +870,8 @@ static int virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy, if (proxy->vector_irqfd) { irqfd = &proxy->vector_irqfd[vector]; if (irqfd->msg.data != msg.data || irqfd->msg.address != msg.address) { - ret = kvm_irqchip_update_msi_route(kvm_state, irqfd->virq, msg); + ret = kvm_irqchip_update_msi_route(kvm_state, irqfd->virq, msg, + &proxy->pci_dev); if (ret < 0) { return ret; } @@ -736,7 +881,7 @@ static int virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy, /* If guest supports masking, irqfd is already setup, unmask it. * Otherwise, set it up now. */ - if (k->guest_notifier_mask) { + if (vdev->use_guest_notifier_mask && k->guest_notifier_mask) { k->guest_notifier_mask(vdev, queue_no, false); /* Test after unmasking to avoid losing events. */ if (k->guest_notifier_pending && @@ -759,7 +904,7 @@ static void virtio_pci_vq_vector_mask(VirtIOPCIProxy *proxy, /* If guest supports masking, keep irqfd but mask it. * Otherwise, clean it up now. */ - if (k->guest_notifier_mask) { + if (vdev->use_guest_notifier_mask && k->guest_notifier_mask) { k->guest_notifier_mask(vdev, queue_no, true); } else { kvm_virtio_pci_irqfd_release(proxy, queue_no, vector); @@ -876,7 +1021,9 @@ static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign, event_notifier_cleanup(notifier); } - if (!msix_enabled(&proxy->pci_dev) && vdc->guest_notifier_mask) { + if (!msix_enabled(&proxy->pci_dev) && + vdev->use_guest_notifier_mask && + vdc->guest_notifier_mask) { vdc->guest_notifier_mask(vdev, n, !assign); } @@ -1205,8 +1352,7 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr, } if (vdev->status == 0) { - virtio_reset(vdev); - msix_unuse_all_vectors(&proxy->pci_dev); + virtio_pci_reset(DEVICE(proxy)); } break; @@ -1238,6 +1384,7 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr, proxy->vqs[vdev->queue_sel].avail[0], ((uint64_t)proxy->vqs[vdev->queue_sel].used[1]) << 32 | proxy->vqs[vdev->queue_sel].used[0]); + proxy->vqs[vdev->queue_sel].enabled = 1; break; case VIRTIO_PCI_COMMON_Q_DESCLO: proxy->vqs[vdev->queue_sel].desc[0] = val; @@ -1280,6 +1427,17 @@ static void virtio_pci_notify_write(void *opaque, hwaddr addr, } } +static void virtio_pci_notify_write_pio(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + VirtIODevice *vdev = opaque; + unsigned queue = val; + + if (queue < VIRTIO_QUEUE_MAX) { + virtio_queue_notify(vdev, queue); + } +} + static uint64_t virtio_pci_isr_read(void *opaque, hwaddr addr, unsigned size) { @@ -1373,6 +1531,16 @@ static void virtio_pci_modern_regions_init(VirtIOPCIProxy *proxy) }, .endianness = DEVICE_LITTLE_ENDIAN, }; + static const MemoryRegionOps notify_pio_ops = { + .read = virtio_pci_notify_read, + .write = virtio_pci_notify_write_pio, + .impl = { + .min_access_size = 1, + .max_access_size = 4, + }, + .endianness = DEVICE_LITTLE_ENDIAN, + }; + memory_region_init_io(&proxy->common.mr, OBJECT(proxy), &common_ops, @@ -1397,30 +1565,60 @@ static void virtio_pci_modern_regions_init(VirtIOPCIProxy *proxy) virtio_bus_get_device(&proxy->bus), "virtio-pci-notify", proxy->notify.size); + + memory_region_init_io(&proxy->notify_pio.mr, OBJECT(proxy), + ¬ify_pio_ops, + virtio_bus_get_device(&proxy->bus), + "virtio-pci-notify-pio", + proxy->notify.size); } static void virtio_pci_modern_region_map(VirtIOPCIProxy *proxy, VirtIOPCIRegion *region, - struct virtio_pci_cap *cap) + struct virtio_pci_cap *cap, + MemoryRegion *mr, + uint8_t bar) { - memory_region_add_subregion(&proxy->modern_bar, - region->offset, - ®ion->mr); + memory_region_add_subregion(mr, region->offset, ®ion->mr); cap->cfg_type = region->type; - cap->bar = proxy->modern_mem_bar; + cap->bar = bar; cap->offset = cpu_to_le32(region->offset); cap->length = cpu_to_le32(region->size); virtio_pci_add_mem_cap(proxy, cap); + +} + +static void virtio_pci_modern_mem_region_map(VirtIOPCIProxy *proxy, + VirtIOPCIRegion *region, + struct virtio_pci_cap *cap) +{ + virtio_pci_modern_region_map(proxy, region, cap, + &proxy->modern_bar, proxy->modern_mem_bar); } -static void virtio_pci_modern_region_unmap(VirtIOPCIProxy *proxy, - VirtIOPCIRegion *region) +static void virtio_pci_modern_io_region_map(VirtIOPCIProxy *proxy, + VirtIOPCIRegion *region, + struct virtio_pci_cap *cap) +{ + virtio_pci_modern_region_map(proxy, region, cap, + &proxy->io_bar, proxy->modern_io_bar); +} + +static void virtio_pci_modern_mem_region_unmap(VirtIOPCIProxy *proxy, + VirtIOPCIRegion *region) { memory_region_del_subregion(&proxy->modern_bar, ®ion->mr); } +static void virtio_pci_modern_io_region_unmap(VirtIOPCIProxy *proxy, + VirtIOPCIRegion *region) +{ + memory_region_del_subregion(&proxy->io_bar, + ®ion->mr); +} + /* This is called by virtio-bus just after the device is plugged. */ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) { @@ -1428,6 +1626,7 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) VirtioBusState *bus = &proxy->bus; bool legacy = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_LEGACY); bool modern = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN); + bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY; uint8_t *config; uint32_t size; VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); @@ -1466,16 +1665,31 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) .cap.cap_len = sizeof cfg, .cap.cfg_type = VIRTIO_PCI_CAP_PCI_CFG, }; - struct virtio_pci_cfg_cap *cfg_mask; + struct virtio_pci_notify_cap notify_pio = { + .cap.cap_len = sizeof notify, + .notify_off_multiplier = cpu_to_le32(0x0), + }; - /* TODO: add io access for speed */ + struct virtio_pci_cfg_cap *cfg_mask; virtio_add_feature(&vdev->host_features, VIRTIO_F_VERSION_1); virtio_pci_modern_regions_init(proxy); - virtio_pci_modern_region_map(proxy, &proxy->common, &cap); - virtio_pci_modern_region_map(proxy, &proxy->isr, &cap); - virtio_pci_modern_region_map(proxy, &proxy->device, &cap); - virtio_pci_modern_region_map(proxy, &proxy->notify, ¬ify.cap); + + virtio_pci_modern_mem_region_map(proxy, &proxy->common, &cap); + virtio_pci_modern_mem_region_map(proxy, &proxy->isr, &cap); + virtio_pci_modern_mem_region_map(proxy, &proxy->device, &cap); + virtio_pci_modern_mem_region_map(proxy, &proxy->notify, ¬ify.cap); + + if (modern_pio) { + memory_region_init(&proxy->io_bar, OBJECT(proxy), + "virtio-pci-io", 0x4); + + pci_register_bar(&proxy->pci_dev, proxy->modern_io_bar, + PCI_BASE_ADDRESS_SPACE_IO, &proxy->io_bar); + + virtio_pci_modern_io_region_map(proxy, &proxy->notify_pio, + ¬ify_pio.cap); + } pci_register_bar(&proxy->pci_dev, proxy->modern_mem_bar, PCI_BASE_ADDRESS_SPACE_MEMORY | @@ -1491,12 +1705,17 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) pci_set_long(cfg_mask->pci_cfg_data, ~0x0); } - if (proxy->nvectors && - msix_init_exclusive_bar(&proxy->pci_dev, proxy->nvectors, - proxy->msix_bar)) { - error_report("unable to init msix vectors to %" PRIu32, - proxy->nvectors); - proxy->nvectors = 0; + if (proxy->nvectors) { + int err = msix_init_exclusive_bar(&proxy->pci_dev, proxy->nvectors, + proxy->msix_bar); + if (err) { + /* Notice when a system that supports MSIx can't initialize it. */ + if (err != -ENOTSUP) { + error_report("unable to init msix vectors to %" PRIu32, + proxy->nvectors); + } + proxy->nvectors = 0; + } } proxy->pci_dev.config_write = virtio_write_config; @@ -1505,9 +1724,7 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) if (legacy) { size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev) + virtio_bus_get_vdev_config_len(bus); - if (size & (size - 1)) { - size = 1 << qemu_fls(size); - } + size = pow2ceil(size); memory_region_init_io(&proxy->bar, OBJECT(proxy), &virtio_pci_config_ops, @@ -1528,14 +1745,18 @@ static void virtio_pci_device_unplugged(DeviceState *d) { VirtIOPCIProxy *proxy = VIRTIO_PCI(d); bool modern = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN); + bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY; virtio_pci_stop_ioeventfd(proxy); if (modern) { - virtio_pci_modern_region_unmap(proxy, &proxy->common); - virtio_pci_modern_region_unmap(proxy, &proxy->isr); - virtio_pci_modern_region_unmap(proxy, &proxy->device); - virtio_pci_modern_region_unmap(proxy, &proxy->notify); + virtio_pci_modern_mem_region_unmap(proxy, &proxy->common); + virtio_pci_modern_mem_region_unmap(proxy, &proxy->isr); + virtio_pci_modern_mem_region_unmap(proxy, &proxy->device); + virtio_pci_modern_mem_region_unmap(proxy, &proxy->notify); + if (modern_pio) { + virtio_pci_modern_io_region_unmap(proxy, &proxy->notify_pio); + } } } @@ -1555,6 +1776,7 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) */ proxy->legacy_io_bar = 0; proxy->msix_bar = 1; + proxy->modern_io_bar = 2; proxy->modern_mem_bar = 4; proxy->common.offset = 0x0; @@ -1574,6 +1796,10 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) QEMU_VIRTIO_PCI_QUEUE_MEM_MULT * VIRTIO_QUEUE_MAX; proxy->notify.type = VIRTIO_PCI_CAP_NOTIFY_CFG; + proxy->notify_pio.offset = 0x0; + proxy->notify_pio.size = 0x4; + proxy->notify_pio.type = VIRTIO_PCI_CAP_NOTIFY_CFG; + /* subclasses can enforce modern, so do this unconditionally */ memory_region_init(&proxy->modern_bar, OBJECT(proxy), "virtio-pci", 2 * QEMU_VIRTIO_PCI_QUEUE_MEM_MULT * @@ -1588,6 +1814,29 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) address_space_init(&proxy->modern_as, &proxy->modern_cfg, "virtio-pci-cfg-as"); + if (pci_is_express(pci_dev) && pci_bus_is_express(pci_dev->bus) && + !pci_bus_is_root(pci_dev->bus)) { + int pos; + + pos = pcie_endpoint_cap_init(pci_dev, 0); + assert(pos > 0); + + pos = pci_add_capability(pci_dev, PCI_CAP_ID_PM, 0, PCI_PM_SIZEOF); + assert(pos > 0); + + /* + * Indicates that this function complies with revision 1.2 of the + * PCI Power Management Interface Specification. + */ + pci_set_word(pci_dev->config + pos + PCI_PM_PMC, 0x3); + } else { + /* + * make future invocations of pci_is_express() return false + * and pci_config_size() return PCI_CONFIG_SPACE_SIZE. + */ + pci_dev->cap_present &= ~QEMU_PCI_CAP_EXPRESS; + } + virtio_pci_bus_new(&proxy->bus, sizeof(proxy->bus), proxy); if (k->realize) { k->realize(proxy, errp); @@ -1606,9 +1855,15 @@ static void virtio_pci_reset(DeviceState *qdev) { VirtIOPCIProxy *proxy = VIRTIO_PCI(qdev); VirtioBusState *bus = VIRTIO_BUS(&proxy->bus); + int i; + virtio_pci_stop_ioeventfd(proxy); virtio_bus_reset(bus); msix_unuse_all_vectors(&proxy->pci_dev); + + for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { + proxy->vqs[i].enabled = 0; + } } static Property virtio_pci_properties[] = { @@ -1618,13 +1873,34 @@ static Property virtio_pci_properties[] = { VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT, false), DEFINE_PROP_BIT("disable-modern", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT, true), + DEFINE_PROP_BIT("migrate-extra", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT, true), + DEFINE_PROP_BIT("modern-pio-notify", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT, false), + DEFINE_PROP_BIT("x-disable-pcie", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT, false), DEFINE_PROP_END_OF_LIST(), }; +static void virtio_pci_dc_realize(DeviceState *qdev, Error **errp) +{ + VirtioPCIClass *vpciklass = VIRTIO_PCI_GET_CLASS(qdev); + VirtIOPCIProxy *proxy = VIRTIO_PCI(qdev); + PCIDevice *pci_dev = &proxy->pci_dev; + + if (!(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_PCIE) && + !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN)) { + pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; + } + + vpciklass->parent_dc_realize(qdev, errp); +} + static void virtio_pci_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass); dc->props = virtio_pci_properties; k->realize = virtio_pci_realize; @@ -1632,6 +1908,8 @@ static void virtio_pci_class_init(ObjectClass *klass, void *data) k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; k->revision = VIRTIO_PCI_ABI_VERSION; k->class_id = PCI_CLASS_OTHERS; + vpciklass->parent_dc_realize = dc->realize; + dc->realize = virtio_pci_dc_realize; dc->reset = virtio_pci_reset; } @@ -2009,10 +2287,6 @@ static const TypeInfo virtio_net_pci_info = { /* virtio-rng-pci */ -static Property virtio_rng_pci_properties[] = { - DEFINE_PROP_END_OF_LIST(), -}; - static void virtio_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { VirtIORngPCI *vrng = VIRTIO_RNG_PCI(vpci_dev); @@ -2039,7 +2313,6 @@ static void virtio_rng_pci_class_init(ObjectClass *klass, void *data) k->realize = virtio_rng_pci_realize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->props = virtio_rng_pci_properties; pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_RNG; @@ -2136,14 +2409,6 @@ static void virtio_tablet_initfn(Object *obj) TYPE_VIRTIO_TABLET); } -static void virtio_host_initfn(Object *obj) -{ - VirtIOInputHostPCI *dev = VIRTIO_INPUT_HOST_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_INPUT_HOST); -} - static const TypeInfo virtio_input_pci_info = { .name = TYPE_VIRTIO_INPUT_PCI, .parent = TYPE_VIRTIO_PCI, @@ -2182,12 +2447,22 @@ static const TypeInfo virtio_tablet_pci_info = { .instance_init = virtio_tablet_initfn, }; +#ifdef CONFIG_LINUX +static void virtio_host_initfn(Object *obj) +{ + VirtIOInputHostPCI *dev = VIRTIO_INPUT_HOST_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_INPUT_HOST); +} + static const TypeInfo virtio_host_pci_info = { .name = TYPE_VIRTIO_INPUT_HOST_PCI, .parent = TYPE_VIRTIO_INPUT_PCI, .instance_size = sizeof(VirtIOInputHostPCI), .instance_init = virtio_host_initfn, }; +#endif /* virtio-pci-bus */ @@ -2211,6 +2486,9 @@ static void virtio_pci_bus_class_init(ObjectClass *klass, void *data) k->load_config = virtio_pci_load_config; k->save_queue = virtio_pci_save_queue; k->load_queue = virtio_pci_load_queue; + k->save_extra_state = virtio_pci_save_extra_state; + k->load_extra_state = virtio_pci_load_extra_state; + k->has_extra_state = virtio_pci_has_extra_state; k->query_guest_notifiers = virtio_pci_query_guest_notifiers; k->set_host_notifier = virtio_pci_set_host_notifier; k->set_guest_notifiers = virtio_pci_set_guest_notifiers; @@ -2235,7 +2513,9 @@ static void virtio_pci_register_types(void) type_register_static(&virtio_keyboard_pci_info); type_register_static(&virtio_mouse_pci_info); type_register_static(&virtio_tablet_pci_info); +#ifdef CONFIG_LINUX type_register_static(&virtio_host_pci_info); +#endif type_register_static(&virtio_pci_bus_info); type_register_static(&virtio_pci_info); #ifdef CONFIG_VIRTFS diff --git a/qemu/hw/virtio/virtio-pci.h b/qemu/hw/virtio/virtio-pci.h index b6c442f52..e4548c2f9 100644 --- a/qemu/hw/virtio/virtio-pci.h +++ b/qemu/hw/virtio/virtio-pci.h @@ -23,7 +23,6 @@ #include "hw/virtio/virtio-scsi.h" #include "hw/virtio/virtio-balloon.h" #include "hw/virtio/virtio-bus.h" -#include "hw/virtio/virtio-9p.h" #include "hw/virtio/virtio-input.h" #include "hw/virtio/virtio-gpu.h" #ifdef CONFIG_VIRTFS @@ -59,21 +58,35 @@ typedef struct VirtioBusClass VirtioPCIBusClass; #define VIRTIO_PCI_BUS_CLASS(klass) \ OBJECT_CLASS_CHECK(VirtioPCIBusClass, klass, TYPE_VIRTIO_PCI_BUS) +enum { + VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, + VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT, + VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT, + VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT, + VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT, + VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT, +}; + /* Need to activate work-arounds for buggy guests at vmstate load. */ -#define VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT 0 #define VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION \ (1 << VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT) /* Performance improves when virtqueue kick processing is decoupled from the * vcpu thread using ioeventfd for some devices. */ -#define VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT 1 #define VIRTIO_PCI_FLAG_USE_IOEVENTFD (1 << VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT) /* virtio version flags */ -#define VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT 2 -#define VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT 3 #define VIRTIO_PCI_FLAG_DISABLE_LEGACY (1 << VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT) #define VIRTIO_PCI_FLAG_DISABLE_MODERN (1 << VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT) +#define VIRTIO_PCI_FLAG_DISABLE_PCIE (1 << VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT) + +/* migrate extra state */ +#define VIRTIO_PCI_FLAG_MIGRATE_EXTRA (1 << VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT) + +/* have pio notification for modern device ? */ +#define VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY \ + (1 << VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT) typedef struct { MSIMessage msg; @@ -94,6 +107,7 @@ typedef struct { typedef struct VirtioPCIClass { PCIDeviceClass parent_class; + DeviceRealize parent_dc_realize; void (*realize)(VirtIOPCIProxy *vpci_dev, Error **errp); } VirtioPCIClass; @@ -104,6 +118,14 @@ typedef struct VirtIOPCIRegion { uint32_t type; } VirtIOPCIRegion; +typedef struct VirtIOPCIQueue { + uint16_t num; + bool enabled; + uint32_t desc[2]; + uint32_t avail[2]; + uint32_t used[2]; +} VirtIOPCIQueue; + struct VirtIOPCIProxy { PCIDevice pci_dev; MemoryRegion bar; @@ -111,11 +133,14 @@ struct VirtIOPCIProxy { VirtIOPCIRegion isr; VirtIOPCIRegion device; VirtIOPCIRegion notify; + VirtIOPCIRegion notify_pio; MemoryRegion modern_bar; + MemoryRegion io_bar; MemoryRegion modern_cfg; AddressSpace modern_as; uint32_t legacy_io_bar; uint32_t msix_bar; + uint32_t modern_io_bar; uint32_t modern_mem_bar; int config_cap; uint32_t flags; @@ -124,13 +149,7 @@ struct VirtIOPCIProxy { uint32_t dfselect; uint32_t gfselect; uint32_t guest_features[2]; - struct { - uint16_t num; - bool enabled; - uint32_t desc[2]; - uint32_t avail[2]; - uint32_t used[2]; - } vqs[VIRTIO_QUEUE_MAX]; + VirtIOPCIQueue vqs[VIRTIO_QUEUE_MAX]; bool ioeventfd_disabled; bool ioeventfd_started; @@ -226,7 +245,7 @@ struct VirtIONetPCI { typedef struct V9fsPCIState { VirtIOPCIProxy parent_obj; - V9fsState vdev; + V9fsVirtioState vdev; } V9fsPCIState; #endif @@ -267,6 +286,8 @@ struct VirtIOInputHIDPCI { VirtIOInputHID vdev; }; +#ifdef CONFIG_LINUX + #define TYPE_VIRTIO_INPUT_HOST_PCI "virtio-input-host-pci" #define VIRTIO_INPUT_HOST_PCI(obj) \ OBJECT_CHECK(VirtIOInputHostPCI, (obj), TYPE_VIRTIO_INPUT_HOST_PCI) @@ -276,6 +297,8 @@ struct VirtIOInputHostPCI { VirtIOInputHost vdev; }; +#endif + /* * virtio-gpu-pci: This extends VirtioPCIProxy. */ diff --git a/qemu/hw/virtio/virtio-rng.c b/qemu/hw/virtio/virtio-rng.c index 97d154191..6b991a764 100644 --- a/qemu/hw/virtio/virtio-rng.c +++ b/qemu/hw/virtio/virtio-rng.c @@ -9,6 +9,8 @@ * top-level directory. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/iov.h" #include "hw/qdev.h" #include "hw/virtio/virtio.h" @@ -43,7 +45,7 @@ static void chr_read(void *opaque, const void *buf, size_t size) { VirtIORNG *vrng = opaque; VirtIODevice *vdev = VIRTIO_DEVICE(vrng); - VirtQueueElement elem; + VirtQueueElement *elem; size_t len; int offset; @@ -55,17 +57,26 @@ static void chr_read(void *opaque, const void *buf, size_t size) offset = 0; while (offset < size) { - if (!virtqueue_pop(vrng->vq, &elem)) { + elem = virtqueue_pop(vrng->vq, sizeof(VirtQueueElement)); + if (!elem) { break; } - len = iov_from_buf(elem.in_sg, elem.in_num, + len = iov_from_buf(elem->in_sg, elem->in_num, 0, buf + offset, size - offset); offset += len; - virtqueue_push(vrng->vq, &elem, len); + virtqueue_push(vrng->vq, elem, len); trace_virtio_rng_pushed(vrng, len); + g_free(elem); } virtio_notify(vdev, vrng->vq); + + if (!virtio_queue_empty(vrng->vq)) { + /* If we didn't drain the queue, call virtio_rng_process + * to take care of asking for more data as appropriate. + */ + virtio_rng_process(vrng); + } } static void virtio_rng_process(VirtIORNG *vrng) diff --git a/qemu/hw/virtio/virtio.c b/qemu/hw/virtio/virtio.c index 788b556a7..30ede3d1c 100644 --- a/qemu/hw/virtio/virtio.c +++ b/qemu/hw/virtio/virtio.c @@ -11,8 +11,10 @@ * */ -#include - +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "trace.h" #include "exec/address-spaces.h" #include "qemu/error-report.h" @@ -60,6 +62,7 @@ typedef struct VRingUsed typedef struct VRing { unsigned int num; + unsigned int num_default; unsigned int align; hwaddr desc; hwaddr avail; @@ -69,7 +72,15 @@ typedef struct VRing struct VirtQueue { VRing vring; + + /* Next head to pop */ uint16_t last_avail_idx; + + /* Last avail_idx read from VQ. */ + uint16_t shadow_avail_idx; + + uint16_t used_idx; + /* Last used index value we have signalled on */ uint16_t signalled_used; @@ -85,6 +96,7 @@ struct VirtQueue uint16_t vector; void (*handle_output)(VirtIODevice *vdev, VirtQueue *vq); + void (*handle_aio_output)(VirtIODevice *vdev, VirtQueue *vq); VirtIODevice *vdev; EventNotifier guest_notifier; EventNotifier host_notifier; @@ -106,35 +118,15 @@ void virtio_queue_update_rings(VirtIODevice *vdev, int n) vring->align); } -static inline uint64_t vring_desc_addr(VirtIODevice *vdev, hwaddr desc_pa, - int i) -{ - hwaddr pa; - pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, addr); - return virtio_ldq_phys(vdev, pa); -} - -static inline uint32_t vring_desc_len(VirtIODevice *vdev, hwaddr desc_pa, int i) +static void vring_desc_read(VirtIODevice *vdev, VRingDesc *desc, + hwaddr desc_pa, int i) { - hwaddr pa; - pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, len); - return virtio_ldl_phys(vdev, pa); -} - -static inline uint16_t vring_desc_flags(VirtIODevice *vdev, hwaddr desc_pa, - int i) -{ - hwaddr pa; - pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, flags); - return virtio_lduw_phys(vdev, pa); -} - -static inline uint16_t vring_desc_next(VirtIODevice *vdev, hwaddr desc_pa, - int i) -{ - hwaddr pa; - pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, next); - return virtio_lduw_phys(vdev, pa); + address_space_read(&address_space_memory, desc_pa + i * sizeof(VRingDesc), + MEMTXATTRS_UNSPECIFIED, (void *)desc, sizeof(VRingDesc)); + virtio_tswap64s(vdev, &desc->addr); + virtio_tswap32s(vdev, &desc->len); + virtio_tswap16s(vdev, &desc->flags); + virtio_tswap16s(vdev, &desc->next); } static inline uint16_t vring_avail_flags(VirtQueue *vq) @@ -148,7 +140,8 @@ static inline uint16_t vring_avail_idx(VirtQueue *vq) { hwaddr pa; pa = vq->vring.avail + offsetof(VRingAvail, idx); - return virtio_lduw_phys(vq->vdev, pa); + vq->shadow_avail_idx = virtio_lduw_phys(vq->vdev, pa); + return vq->shadow_avail_idx; } static inline uint16_t vring_avail_ring(VirtQueue *vq, int i) @@ -163,18 +156,15 @@ static inline uint16_t vring_get_used_event(VirtQueue *vq) return vring_avail_ring(vq, vq->vring.num); } -static inline void vring_used_ring_id(VirtQueue *vq, int i, uint32_t val) -{ - hwaddr pa; - pa = vq->vring.used + offsetof(VRingUsed, ring[i].id); - virtio_stl_phys(vq->vdev, pa, val); -} - -static inline void vring_used_ring_len(VirtQueue *vq, int i, uint32_t val) +static inline void vring_used_write(VirtQueue *vq, VRingUsedElem *uelem, + int i) { hwaddr pa; - pa = vq->vring.used + offsetof(VRingUsed, ring[i].len); - virtio_stl_phys(vq->vdev, pa, val); + virtio_tswap32s(vq->vdev, &uelem->id); + virtio_tswap32s(vq->vdev, &uelem->len); + pa = vq->vring.used + offsetof(VRingUsed, ring[i]); + address_space_write(&address_space_memory, pa, MEMTXATTRS_UNSPECIFIED, + (void *)uelem, sizeof(VRingUsedElem)); } static uint16_t vring_used_idx(VirtQueue *vq) @@ -189,6 +179,7 @@ static inline void vring_used_idx_set(VirtQueue *vq, uint16_t val) hwaddr pa; pa = vq->vring.used + offsetof(VRingUsed, idx); virtio_stw_phys(vq->vdev, pa, val); + vq->used_idx = val; } static inline void vring_used_flags_set_bit(VirtQueue *vq, int mask) @@ -220,7 +211,7 @@ static inline void vring_set_avail_event(VirtQueue *vq, uint16_t val) void virtio_queue_set_notification(VirtQueue *vq, int enable) { vq->notification = enable; - if (virtio_has_feature(vq->vdev, VIRTIO_RING_F_EVENT_IDX)) { + if (virtio_vdev_has_feature(vq->vdev, VIRTIO_RING_F_EVENT_IDX)) { vring_set_avail_event(vq, vring_avail_idx(vq)); } else if (enable) { vring_used_flags_unset_bit(vq, VRING_USED_F_NO_NOTIFY); @@ -238,19 +229,23 @@ int virtio_queue_ready(VirtQueue *vq) return vq->vring.avail != 0; } +/* Fetch avail_idx from VQ memory only when we really need to know if + * guest has added some buffers. */ int virtio_queue_empty(VirtQueue *vq) { + if (vq->shadow_avail_idx != vq->last_avail_idx) { + return 0; + } + return vring_avail_idx(vq) == vq->last_avail_idx; } -void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, - unsigned int len, unsigned int idx) +static void virtqueue_unmap_sg(VirtQueue *vq, const VirtQueueElement *elem, + unsigned int len) { unsigned int offset; int i; - trace_virtqueue_fill(vq, elem, len, idx); - offset = 0; for (i = 0; i < elem->in_num; i++) { size_t size = MIN(len - offset, elem->in_sg[i].iov_len); @@ -266,12 +261,29 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, cpu_physical_memory_unmap(elem->out_sg[i].iov_base, elem->out_sg[i].iov_len, 0, elem->out_sg[i].iov_len); +} - idx = (idx + vring_used_idx(vq)) % vq->vring.num; +void virtqueue_discard(VirtQueue *vq, const VirtQueueElement *elem, + unsigned int len) +{ + vq->last_avail_idx--; + virtqueue_unmap_sg(vq, elem, len); +} - /* Get a pointer to the next entry in the used ring. */ - vring_used_ring_id(vq, idx, elem->index); - vring_used_ring_len(vq, idx, len); +void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, + unsigned int len, unsigned int idx) +{ + VRingUsedElem uelem; + + trace_virtqueue_fill(vq, elem, len, idx); + + virtqueue_unmap_sg(vq, elem, len); + + idx = (idx + vq->used_idx) % vq->vring.num; + + uelem.id = elem->index; + uelem.len = len; + vring_used_write(vq, &uelem, idx); } void virtqueue_flush(VirtQueue *vq, unsigned int count) @@ -280,7 +292,7 @@ void virtqueue_flush(VirtQueue *vq, unsigned int count) /* Make sure buffer is written before we update index. */ smp_wmb(); trace_virtqueue_flush(vq, count); - old = vring_used_idx(vq); + old = vq->used_idx; new = old + count; vring_used_idx_set(vq, new); vq->inuse -= count; @@ -302,7 +314,7 @@ static int virtqueue_num_heads(VirtQueue *vq, unsigned int idx) /* Check it isn't doing very strange things with descriptor numbers. */ if (num_heads > vq->vring.num) { error_report("Guest moved used index from %u to %u", - idx, vring_avail_idx(vq)); + idx, vq->shadow_avail_idx); exit(1); } /* On success, callers read a descriptor at vq->last_avail_idx. @@ -331,18 +343,18 @@ static unsigned int virtqueue_get_head(VirtQueue *vq, unsigned int idx) return head; } -static unsigned virtqueue_next_desc(VirtIODevice *vdev, hwaddr desc_pa, - unsigned int i, unsigned int max) +static unsigned virtqueue_read_next_desc(VirtIODevice *vdev, VRingDesc *desc, + hwaddr desc_pa, unsigned int max) { unsigned int next; /* If this descriptor says it doesn't chain, we're done. */ - if (!(vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_NEXT)) { + if (!(desc->flags & VRING_DESC_F_NEXT)) { return max; } /* Check they're not leading us off end of descriptors. */ - next = vring_desc_next(vdev, desc_pa, i); + next = desc->next; /* Make sure compiler knows to grab that: we don't want it changing! */ smp_wmb(); @@ -351,6 +363,7 @@ static unsigned virtqueue_next_desc(VirtIODevice *vdev, hwaddr desc_pa, exit(1); } + vring_desc_read(vdev, desc, desc_pa, next); return next; } @@ -367,6 +380,7 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, while (virtqueue_num_heads(vq, idx)) { VirtIODevice *vdev = vq->vdev; unsigned int max, num_bufs, indirect = 0; + VRingDesc desc; hwaddr desc_pa; int i; @@ -374,9 +388,10 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, num_bufs = total_bufs; i = virtqueue_get_head(vq, idx++); desc_pa = vq->vring.desc; + vring_desc_read(vdev, &desc, desc_pa, i); - if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_INDIRECT) { - if (vring_desc_len(vdev, desc_pa, i) % sizeof(VRingDesc)) { + if (desc.flags & VRING_DESC_F_INDIRECT) { + if (desc.len % sizeof(VRingDesc)) { error_report("Invalid size for indirect buffer table"); exit(1); } @@ -389,9 +404,10 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, /* loop over the indirect descriptor table */ indirect = 1; - max = vring_desc_len(vdev, desc_pa, i) / sizeof(VRingDesc); - desc_pa = vring_desc_addr(vdev, desc_pa, i); + max = desc.len / sizeof(VRingDesc); + desc_pa = desc.addr; num_bufs = i = 0; + vring_desc_read(vdev, &desc, desc_pa, i); } do { @@ -401,15 +417,15 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, exit(1); } - if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_WRITE) { - in_total += vring_desc_len(vdev, desc_pa, i); + if (desc.flags & VRING_DESC_F_WRITE) { + in_total += desc.len; } else { - out_total += vring_desc_len(vdev, desc_pa, i); + out_total += desc.len; } if (in_total >= max_in_bytes && out_total >= max_out_bytes) { goto done; } - } while ((i = virtqueue_next_desc(vdev, desc_pa, i, max)) != max); + } while ((i = virtqueue_read_next_desc(vdev, &desc, desc_pa, max)) != max); if (!indirect) total_bufs = num_bufs; @@ -434,98 +450,256 @@ int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes, return in_bytes <= in_total && out_bytes <= out_total; } -void virtqueue_map_sg(struct iovec *sg, hwaddr *addr, - size_t num_sg, int is_write) +static void virtqueue_map_desc(unsigned int *p_num_sg, hwaddr *addr, struct iovec *iov, + unsigned int max_num_sg, bool is_write, + hwaddr pa, size_t sz) +{ + unsigned num_sg = *p_num_sg; + assert(num_sg <= max_num_sg); + + while (sz) { + hwaddr len = sz; + + if (num_sg == max_num_sg) { + error_report("virtio: too many write descriptors in indirect table"); + exit(1); + } + + iov[num_sg].iov_base = cpu_physical_memory_map(pa, &len, is_write); + iov[num_sg].iov_len = len; + addr[num_sg] = pa; + + sz -= len; + pa += len; + num_sg++; + } + *p_num_sg = num_sg; +} + +static void virtqueue_map_iovec(struct iovec *sg, hwaddr *addr, + unsigned int *num_sg, unsigned int max_size, + int is_write) { unsigned int i; hwaddr len; - if (num_sg > VIRTQUEUE_MAX_SIZE) { - error_report("virtio: map attempt out of bounds: %zd > %d", - num_sg, VIRTQUEUE_MAX_SIZE); - exit(1); - } + /* Note: this function MUST validate input, some callers + * are passing in num_sg values received over the network. + */ + /* TODO: teach all callers that this can fail, and return failure instead + * of asserting here. + * When we do, we might be able to re-enable NDEBUG below. + */ +#ifdef NDEBUG +#error building with NDEBUG is not supported +#endif + assert(*num_sg <= max_size); - for (i = 0; i < num_sg; i++) { + for (i = 0; i < *num_sg; i++) { len = sg[i].iov_len; sg[i].iov_base = cpu_physical_memory_map(addr[i], &len, is_write); - if (sg[i].iov_base == NULL || len != sg[i].iov_len) { + if (!sg[i].iov_base) { error_report("virtio: error trying to map MMIO memory"); exit(1); } + if (len != sg[i].iov_len) { + error_report("virtio: unexpected memory split"); + exit(1); + } } } -int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) +void virtqueue_map(VirtQueueElement *elem) +{ + virtqueue_map_iovec(elem->in_sg, elem->in_addr, &elem->in_num, + VIRTQUEUE_MAX_SIZE, 1); + virtqueue_map_iovec(elem->out_sg, elem->out_addr, &elem->out_num, + VIRTQUEUE_MAX_SIZE, 0); +} + +void *virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_num) +{ + VirtQueueElement *elem; + size_t in_addr_ofs = QEMU_ALIGN_UP(sz, __alignof__(elem->in_addr[0])); + size_t out_addr_ofs = in_addr_ofs + in_num * sizeof(elem->in_addr[0]); + size_t out_addr_end = out_addr_ofs + out_num * sizeof(elem->out_addr[0]); + size_t in_sg_ofs = QEMU_ALIGN_UP(out_addr_end, __alignof__(elem->in_sg[0])); + size_t out_sg_ofs = in_sg_ofs + in_num * sizeof(elem->in_sg[0]); + size_t out_sg_end = out_sg_ofs + out_num * sizeof(elem->out_sg[0]); + + assert(sz >= sizeof(VirtQueueElement)); + elem = g_malloc(out_sg_end); + elem->out_num = out_num; + elem->in_num = in_num; + elem->in_addr = (void *)elem + in_addr_ofs; + elem->out_addr = (void *)elem + out_addr_ofs; + elem->in_sg = (void *)elem + in_sg_ofs; + elem->out_sg = (void *)elem + out_sg_ofs; + return elem; +} + +void *virtqueue_pop(VirtQueue *vq, size_t sz) { unsigned int i, head, max; hwaddr desc_pa = vq->vring.desc; VirtIODevice *vdev = vq->vdev; + VirtQueueElement *elem; + unsigned out_num, in_num; + hwaddr addr[VIRTQUEUE_MAX_SIZE]; + struct iovec iov[VIRTQUEUE_MAX_SIZE]; + VRingDesc desc; - if (!virtqueue_num_heads(vq, vq->last_avail_idx)) - return 0; + if (virtio_queue_empty(vq)) { + return NULL; + } + /* Needed after virtio_queue_empty(), see comment in + * virtqueue_num_heads(). */ + smp_rmb(); /* When we start there are none of either input nor output. */ - elem->out_num = elem->in_num = 0; + out_num = in_num = 0; max = vq->vring.num; i = head = virtqueue_get_head(vq, vq->last_avail_idx++); - if (virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { vring_set_avail_event(vq, vq->last_avail_idx); } - if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_INDIRECT) { - if (vring_desc_len(vdev, desc_pa, i) % sizeof(VRingDesc)) { + vring_desc_read(vdev, &desc, desc_pa, i); + if (desc.flags & VRING_DESC_F_INDIRECT) { + if (desc.len % sizeof(VRingDesc)) { error_report("Invalid size for indirect buffer table"); exit(1); } /* loop over the indirect descriptor table */ - max = vring_desc_len(vdev, desc_pa, i) / sizeof(VRingDesc); - desc_pa = vring_desc_addr(vdev, desc_pa, i); + max = desc.len / sizeof(VRingDesc); + desc_pa = desc.addr; i = 0; + vring_desc_read(vdev, &desc, desc_pa, i); } /* Collect all the descriptors */ do { - struct iovec *sg; - - if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_WRITE) { - if (elem->in_num >= ARRAY_SIZE(elem->in_sg)) { - error_report("Too many write descriptors in indirect table"); - exit(1); - } - elem->in_addr[elem->in_num] = vring_desc_addr(vdev, desc_pa, i); - sg = &elem->in_sg[elem->in_num++]; + if (desc.flags & VRING_DESC_F_WRITE) { + virtqueue_map_desc(&in_num, addr + out_num, iov + out_num, + VIRTQUEUE_MAX_SIZE - out_num, true, desc.addr, desc.len); } else { - if (elem->out_num >= ARRAY_SIZE(elem->out_sg)) { - error_report("Too many read descriptors in indirect table"); + if (in_num) { + error_report("Incorrect order for descriptors"); exit(1); } - elem->out_addr[elem->out_num] = vring_desc_addr(vdev, desc_pa, i); - sg = &elem->out_sg[elem->out_num++]; + virtqueue_map_desc(&out_num, addr, iov, + VIRTQUEUE_MAX_SIZE, false, desc.addr, desc.len); } - sg->iov_len = vring_desc_len(vdev, desc_pa, i); - /* If we've got too many, that implies a descriptor loop. */ - if ((elem->in_num + elem->out_num) > max) { + if ((in_num + out_num) > max) { error_report("Looped descriptor"); exit(1); } - } while ((i = virtqueue_next_desc(vdev, desc_pa, i, max)) != max); - - /* Now map what we have collected */ - virtqueue_map_sg(elem->in_sg, elem->in_addr, elem->in_num, 1); - virtqueue_map_sg(elem->out_sg, elem->out_addr, elem->out_num, 0); + } while ((i = virtqueue_read_next_desc(vdev, &desc, desc_pa, max)) != max); + /* Now copy what we have collected and mapped */ + elem = virtqueue_alloc_element(sz, out_num, in_num); elem->index = head; + for (i = 0; i < out_num; i++) { + elem->out_addr[i] = addr[i]; + elem->out_sg[i] = iov[i]; + } + for (i = 0; i < in_num; i++) { + elem->in_addr[i] = addr[out_num + i]; + elem->in_sg[i] = iov[out_num + i]; + } vq->inuse++; trace_virtqueue_pop(vq, elem, elem->in_num, elem->out_num); - return elem->in_num + elem->out_num; + return elem; +} + +/* Reading and writing a structure directly to QEMUFile is *awful*, but + * it is what QEMU has always done by mistake. We can change it sooner + * or later by bumping the version number of the affected vm states. + * In the meanwhile, since the in-memory layout of VirtQueueElement + * has changed, we need to marshal to and from the layout that was + * used before the change. + */ +typedef struct VirtQueueElementOld { + unsigned int index; + unsigned int out_num; + unsigned int in_num; + hwaddr in_addr[VIRTQUEUE_MAX_SIZE]; + hwaddr out_addr[VIRTQUEUE_MAX_SIZE]; + struct iovec in_sg[VIRTQUEUE_MAX_SIZE]; + struct iovec out_sg[VIRTQUEUE_MAX_SIZE]; +} VirtQueueElementOld; + +void *qemu_get_virtqueue_element(QEMUFile *f, size_t sz) +{ + VirtQueueElement *elem; + VirtQueueElementOld data; + int i; + + qemu_get_buffer(f, (uint8_t *)&data, sizeof(VirtQueueElementOld)); + + elem = virtqueue_alloc_element(sz, data.out_num, data.in_num); + elem->index = data.index; + + for (i = 0; i < elem->in_num; i++) { + elem->in_addr[i] = data.in_addr[i]; + } + + for (i = 0; i < elem->out_num; i++) { + elem->out_addr[i] = data.out_addr[i]; + } + + for (i = 0; i < elem->in_num; i++) { + /* Base is overwritten by virtqueue_map. */ + elem->in_sg[i].iov_base = 0; + elem->in_sg[i].iov_len = data.in_sg[i].iov_len; + } + + for (i = 0; i < elem->out_num; i++) { + /* Base is overwritten by virtqueue_map. */ + elem->out_sg[i].iov_base = 0; + elem->out_sg[i].iov_len = data.out_sg[i].iov_len; + } + + virtqueue_map(elem); + return elem; +} + +void qemu_put_virtqueue_element(QEMUFile *f, VirtQueueElement *elem) +{ + VirtQueueElementOld data; + int i; + + memset(&data, 0, sizeof(data)); + data.index = elem->index; + data.in_num = elem->in_num; + data.out_num = elem->out_num; + + for (i = 0; i < elem->in_num; i++) { + data.in_addr[i] = elem->in_addr[i]; + } + + for (i = 0; i < elem->out_num; i++) { + data.out_addr[i] = elem->out_addr[i]; + } + + for (i = 0; i < elem->in_num; i++) { + /* Base is overwritten by virtqueue_map when loading. Do not + * save it, as it would leak the QEMU address space layout. */ + data.in_sg[i].iov_len = elem->in_sg[i].iov_len; + } + + for (i = 0; i < elem->out_num; i++) { + /* Do not save iov_base as above. */ + data.out_sg[i].iov_len = elem->out_sg[i].iov_len; + } + qemu_put_buffer(f, (uint8_t *)&data, sizeof(VirtQueueElementOld)); } /* virtio device */ @@ -560,7 +734,7 @@ int virtio_set_status(VirtIODevice *vdev, uint8_t val) VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); trace_virtio_set_status(vdev, val); - if (virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) { if (!(vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) && val & VIRTIO_CONFIG_S_FEATURES_OK) { int ret = virtio_validate_features(vdev); @@ -629,10 +803,13 @@ void virtio_reset(void *opaque) vdev->vq[i].vring.avail = 0; vdev->vq[i].vring.used = 0; vdev->vq[i].last_avail_idx = 0; + vdev->vq[i].shadow_avail_idx = 0; + vdev->vq[i].used_idx = 0; virtio_queue_set_vector(vdev, i, VIRTIO_NO_VECTOR); vdev->vq[i].signalled_used = 0; vdev->vq[i].signalled_used_valid = false; vdev->vq[i].notification = true; + vdev->vq[i].vring.num = vdev->vq[i].vring.num_default; } } @@ -898,7 +1075,7 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align) VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); /* virtio-1 compliant devices cannot change the alignment */ - if (virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) { error_report("tried to modify queue alignment for virtio-1 device"); return; } @@ -912,7 +1089,17 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align) virtio_queue_update_rings(vdev, n); } -void virtio_queue_notify_vq(VirtQueue *vq) +static void virtio_queue_notify_aio_vq(VirtQueue *vq) +{ + if (vq->vring.desc && vq->handle_aio_output) { + VirtIODevice *vdev = vq->vdev; + + trace_virtio_queue_notify(vdev, vq - vdev->vq, vq); + vq->handle_aio_output(vdev, vq); + } +} + +static void virtio_queue_notify_vq(VirtQueue *vq) { if (vq->vring.desc && vq->handle_output) { VirtIODevice *vdev = vq->vdev; @@ -964,8 +1151,10 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, abort(); vdev->vq[i].vring.num = queue_size; + vdev->vq[i].vring.num_default = queue_size; vdev->vq[i].vring.align = VIRTIO_PCI_VRING_ALIGN; vdev->vq[i].handle_output = handle_output; + vdev->vq[i].handle_aio_output = NULL; return &vdev->vq[i]; } @@ -977,6 +1166,7 @@ void virtio_del_queue(VirtIODevice *vdev, int n) } vdev->vq[n].vring.num = 0; + vdev->vq[n].vring.num_default = 0; } void virtio_irq(VirtQueue *vq) @@ -986,32 +1176,32 @@ void virtio_irq(VirtQueue *vq) virtio_notify_vector(vq->vdev, vq->vector); } -static bool vring_notify(VirtIODevice *vdev, VirtQueue *vq) +bool virtio_should_notify(VirtIODevice *vdev, VirtQueue *vq) { uint16_t old, new; bool v; /* We need to expose used array entries before checking used event. */ smp_mb(); /* Always notify when queue is empty (when feature acknowledge) */ - if (virtio_has_feature(vdev, VIRTIO_F_NOTIFY_ON_EMPTY) && - !vq->inuse && vring_avail_idx(vq) == vq->last_avail_idx) { + if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFY_ON_EMPTY) && + !vq->inuse && virtio_queue_empty(vq)) { return true; } - if (!virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { + if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { return !(vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT); } v = vq->signalled_used_valid; vq->signalled_used_valid = true; old = vq->signalled_used; - new = vq->signalled_used = vring_used_idx(vq); + new = vq->signalled_used = vq->used_idx; return !v || vring_need_event(vring_get_used_event(vq), new, old); } void virtio_notify(VirtIODevice *vdev, VirtQueue *vq) { - if (!vring_notify(vdev, vq)) { + if (!virtio_should_notify(vdev, vq)) { return; } @@ -1035,7 +1225,7 @@ static bool virtio_device_endian_needed(void *opaque) VirtIODevice *vdev = opaque; assert(vdev->device_endian != VIRTIO_DEVICE_ENDIAN_UNKNOWN); - if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) { + if (!virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) { return vdev->device_endian != virtio_default_endian(); } /* Devices conforming to VIRTIO 1.0 or later are always LE. */ @@ -1056,33 +1246,38 @@ static bool virtio_virtqueue_needed(void *opaque) return virtio_host_has_feature(vdev, VIRTIO_F_VERSION_1); } -static void put_virtqueue_state(QEMUFile *f, void *pv, size_t size) +static bool virtio_ringsize_needed(void *opaque) { - VirtIODevice *vdev = pv; + VirtIODevice *vdev = opaque; int i; for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - qemu_put_be64(f, vdev->vq[i].vring.avail); - qemu_put_be64(f, vdev->vq[i].vring.used); + if (vdev->vq[i].vring.num != vdev->vq[i].vring.num_default) { + return true; + } } + return false; } -static int get_virtqueue_state(QEMUFile *f, void *pv, size_t size) +static bool virtio_extra_state_needed(void *opaque) { - VirtIODevice *vdev = pv; - int i; + VirtIODevice *vdev = opaque; + BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - vdev->vq[i].vring.avail = qemu_get_be64(f); - vdev->vq[i].vring.used = qemu_get_be64(f); - } - return 0; + return k->has_extra_state && + k->has_extra_state(qbus->parent); } -static VMStateInfo vmstate_info_virtqueue = { +static const VMStateDescription vmstate_virtqueue = { .name = "virtqueue_state", - .get = get_virtqueue_state, - .put = put_virtqueue_state, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(vring.avail, struct VirtQueue), + VMSTATE_UINT64(vring.used, struct VirtQueue), + VMSTATE_END_OF_LIST() + } }; static const VMStateDescription vmstate_virtio_virtqueues = { @@ -1090,13 +1285,75 @@ static const VMStateDescription vmstate_virtio_virtqueues = { .version_id = 1, .minimum_version_id = 1, .needed = &virtio_virtqueue_needed, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_VARRAY_POINTER_KNOWN(vq, struct VirtIODevice, + VIRTIO_QUEUE_MAX, 0, vmstate_virtqueue, VirtQueue), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_ringsize = { + .name = "ringsize_state", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(vring.num_default, struct VirtQueue), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_virtio_ringsize = { + .name = "virtio/ringsize", + .version_id = 1, + .minimum_version_id = 1, + .needed = &virtio_ringsize_needed, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_VARRAY_POINTER_KNOWN(vq, struct VirtIODevice, + VIRTIO_QUEUE_MAX, 0, vmstate_ringsize, VirtQueue), + VMSTATE_END_OF_LIST() + } +}; + +static int get_extra_state(QEMUFile *f, void *pv, size_t size) +{ + VirtIODevice *vdev = pv; + BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + + if (!k->load_extra_state) { + return -1; + } else { + return k->load_extra_state(qbus->parent, f); + } +} + +static void put_extra_state(QEMUFile *f, void *pv, size_t size) +{ + VirtIODevice *vdev = pv; + BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + + k->save_extra_state(qbus->parent, f); +} + +static const VMStateInfo vmstate_info_extra_state = { + .name = "virtqueue_extra_state", + .get = get_extra_state, + .put = put_extra_state, +}; + +static const VMStateDescription vmstate_virtio_extra_state = { + .name = "virtio/extra_state", + .version_id = 1, + .minimum_version_id = 1, + .needed = &virtio_extra_state_needed, .fields = (VMStateField[]) { { - .name = "virtqueues", + .name = "extra_state", .version_id = 0, .field_exists = NULL, .size = 0, - .info = &vmstate_info_virtqueue, + .info = &vmstate_info_extra_state, .flags = VMS_SINGLE, .offset = 0, }, @@ -1138,6 +1395,8 @@ static const VMStateDescription vmstate_virtio = { &vmstate_virtio_device_endian, &vmstate_virtio_64bit_features, &vmstate_virtio_virtqueues, + &vmstate_virtio_ringsize, + &vmstate_virtio_extra_state, NULL } }; @@ -1264,7 +1523,7 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) num = qemu_get_be32(f); if (num > VIRTIO_QUEUE_MAX) { - error_report("Invalid number of PCI queues: 0x%x", num); + error_report("Invalid number of virtqueues: 0x%x", num); return -1; } @@ -1348,6 +1607,8 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) vdev->vq[i].last_avail_idx, nheads); return -1; } + vdev->vq[i].used_idx = vring_used_idx(&vdev->vq[i]); + vdev->vq[i].shadow_avail_idx = vring_avail_idx(&vdev->vq[i]); } } @@ -1430,6 +1691,7 @@ void virtio_init(VirtIODevice *vdev, const char *name, vdev->vmstate = qemu_add_vm_change_state_handler(virtio_vmstate_change, vdev); vdev->device_endian = virtio_default_endian(); + vdev->use_guest_notifier_mask = true; } hwaddr virtio_queue_get_desc_addr(VirtIODevice *vdev, int n) @@ -1460,7 +1722,7 @@ hwaddr virtio_queue_get_desc_size(VirtIODevice *vdev, int n) hwaddr virtio_queue_get_avail_size(VirtIODevice *vdev, int n) { return offsetof(VRingAvail, ring) + - sizeof(uint64_t) * vdev->vq[n].vring.num; + sizeof(uint16_t) * vdev->vq[n].vring.num; } hwaddr virtio_queue_get_used_size(VirtIODevice *vdev, int n) @@ -1483,6 +1745,7 @@ uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n) void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx) { vdev->vq[n].last_avail_idx = idx; + vdev->vq[n].shadow_avail_idx = idx; } void virtio_queue_invalidate_signalled_used(VirtIODevice *vdev, int n) @@ -1512,10 +1775,10 @@ void virtio_queue_set_guest_notifier_fd_handler(VirtQueue *vq, bool assign, bool with_irqfd) { if (assign && !with_irqfd) { - event_notifier_set_handler(&vq->guest_notifier, + event_notifier_set_handler(&vq->guest_notifier, false, virtio_queue_guest_notifier_read); } else { - event_notifier_set_handler(&vq->guest_notifier, NULL); + event_notifier_set_handler(&vq->guest_notifier, false, NULL); } if (!assign) { /* Test and clear notifier before closing it, @@ -1529,6 +1792,31 @@ EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq) return &vq->guest_notifier; } +static void virtio_queue_host_notifier_aio_read(EventNotifier *n) +{ + VirtQueue *vq = container_of(n, VirtQueue, host_notifier); + if (event_notifier_test_and_clear(n)) { + virtio_queue_notify_aio_vq(vq); + } +} + +void virtio_queue_aio_set_host_notifier_handler(VirtQueue *vq, AioContext *ctx, + void (*handle_output)(VirtIODevice *, + VirtQueue *)) +{ + if (handle_output) { + vq->handle_aio_output = handle_output; + aio_set_event_notifier(ctx, &vq->host_notifier, true, + virtio_queue_host_notifier_aio_read); + } else { + aio_set_event_notifier(ctx, &vq->host_notifier, true, NULL); + /* Test and clear notifier before after disabling event, + * in case poll callback didn't have time to run. */ + virtio_queue_host_notifier_aio_read(&vq->host_notifier); + vq->handle_aio_output = NULL; + } +} + static void virtio_queue_host_notifier_read(EventNotifier *n) { VirtQueue *vq = container_of(n, VirtQueue, host_notifier); @@ -1541,10 +1829,10 @@ void virtio_queue_set_host_notifier_fd_handler(VirtQueue *vq, bool assign, bool set_handler) { if (assign && set_handler) { - event_notifier_set_handler(&vq->host_notifier, + event_notifier_set_handler(&vq->host_notifier, true, virtio_queue_host_notifier_read); } else { - event_notifier_set_handler(&vq->host_notifier, NULL); + event_notifier_set_handler(&vq->host_notifier, true, NULL); } if (!assign) { /* Test and clear notifier before after disabling event, diff --git a/qemu/hw/watchdog/watchdog.c b/qemu/hw/watchdog/watchdog.c index 8d4b0eeeb..bbf3646ba 100644 --- a/qemu/hw/watchdog/watchdog.c +++ b/qemu/hw/watchdog/watchdog.c @@ -19,7 +19,7 @@ * By Richard W.M. Jones (rjones@redhat.com). */ -#include "qemu-common.h" +#include "qemu/osdep.h" #include "qemu/option.h" #include "qemu/config-file.h" #include "qemu/queue.h" @@ -28,15 +28,7 @@ #include "sysemu/watchdog.h" #include "qapi-event.h" #include "hw/nmi.h" - -/* Possible values for action parameter. */ -#define WDT_RESET 1 /* Hard reset. */ -#define WDT_SHUTDOWN 2 /* Shutdown. */ -#define WDT_POWEROFF 3 /* Quit. */ -#define WDT_PAUSE 4 /* Pause. */ -#define WDT_DEBUG 5 /* Prints a message and continues running. */ -#define WDT_NONE 6 /* Do nothing. */ -#define WDT_NMI 7 /* Inject nmi into the guest */ +#include "qemu/help_option.h" static int watchdog_action = WDT_RESET; static QLIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list; @@ -105,6 +97,11 @@ int select_watchdog_action(const char *p) return 0; } +int get_watchdog_action(void) +{ + return watchdog_action; +} + /* This actually performs the "action" once a watchdog has expired, * ie. reboot, shutdown, exit, etc. */ diff --git a/qemu/hw/watchdog/wdt_diag288.c b/qemu/hw/watchdog/wdt_diag288.c index 2a885a447..f54a35a0e 100644 --- a/qemu/hw/watchdog/wdt_diag288.c +++ b/qemu/hw/watchdog/wdt_diag288.c @@ -11,6 +11,7 @@ * */ +#include "qemu/osdep.h" #include "sysemu/watchdog.h" #include "hw/sysbus.h" #include "qemu/timer.h" @@ -50,8 +51,19 @@ static void diag288_reset(void *opaque) static void diag288_timer_expired(void *dev) { qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n"); + /* Reset the watchdog only if the guest gets notified about + * expiry. watchdog_perform_action() may temporarily relinquish + * the BQL; reset before triggering the action to avoid races with + * diag288 instructions. */ + switch (get_watchdog_action()) { + case WDT_DEBUG: + case WDT_NONE: + case WDT_PAUSE: + break; + default: + wdt_diag288_reset(dev); + } watchdog_perform_action(); - wdt_diag288_reset(dev); } static int wdt_diag288_handle_timer(DIAG288State *diag288, @@ -67,7 +79,7 @@ static int wdt_diag288_handle_timer(DIAG288State *diag288, } timer_mod(diag288->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - timeout * get_ticks_per_sec()); + timeout * NANOSECONDS_PER_SECOND); break; case WDT_DIAG288_CANCEL: if (!diag288->enabled) { diff --git a/qemu/hw/watchdog/wdt_i6300esb.c b/qemu/hw/watchdog/wdt_i6300esb.c index cfa2b1be1..a83d95121 100644 --- a/qemu/hw/watchdog/wdt_i6300esb.c +++ b/qemu/hw/watchdog/wdt_i6300esb.c @@ -19,7 +19,7 @@ * By Richard W.M. Jones (rjones@redhat.com). */ -#include +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/timer.h" @@ -129,14 +129,9 @@ static void i6300esb_restart_timer(I6300State *d, int stage) else timeout <<= 5; - /* Get the timeout in units of ticks_per_sec. - * - * ticks_per_sec is typically 10^9 == 0x3B9ACA00 (30 bits), with - * 20 bits of user supplied preload, and 15 bits of scale, the - * multiply here can exceed 64-bits, before we divide by 33MHz, so - * we use a higher-precision intermediate result. - */ - timeout = muldiv64(get_ticks_per_sec(), timeout, 33000000); + /* Get the timeout in nanoseconds. */ + + timeout = timeout * 30; /* on a PCI bus, 1 tick is 30 ns*/ i6300esb_debug("stage %d, timeout %" PRIi64 "\n", d->stage, timeout); diff --git a/qemu/hw/watchdog/wdt_ib700.c b/qemu/hw/watchdog/wdt_ib700.c index 0917a713d..532afe89e 100644 --- a/qemu/hw/watchdog/wdt_ib700.c +++ b/qemu/hw/watchdog/wdt_ib700.c @@ -19,6 +19,7 @@ * By Richard W.M. Jones (rjones@redhat.com). */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/timer.h" #include "sysemu/watchdog.h" @@ -63,7 +64,7 @@ static void ib700_write_enable_reg(void *vp, uint32_t addr, uint32_t data) ib700_debug("addr = %x, data = %x\n", addr, data); - timeout = (int64_t) time_map[data & 0xF] * get_ticks_per_sec(); + timeout = (int64_t) time_map[data & 0xF] * NANOSECONDS_PER_SECOND; timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + timeout); } diff --git a/qemu/hw/xen/Makefile.objs b/qemu/hw/xen/Makefile.objs index a0ca0aa3d..d3670940b 100644 --- a/qemu/hw/xen/Makefile.objs +++ b/qemu/hw/xen/Makefile.objs @@ -2,4 +2,4 @@ common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o -obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o +obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_graphics.o xen_pt_msi.o diff --git a/qemu/hw/xen/xen-host-pci-device.c b/qemu/hw/xen/xen-host-pci-device.c index 743b37b99..eed8cc88e 100644 --- a/qemu/hw/xen/xen-host-pci-device.c +++ b/qemu/hw/xen/xen-host-pci-device.c @@ -6,7 +6,10 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" +#include "qemu/cutils.h" #include "xen-host-pci-device.h" #define XEN_HOST_PCI_MAX_EXT_CAP \ @@ -31,25 +34,20 @@ #define IORESOURCE_PREFETCH 0x00001000 /* No side effects */ #define IORESOURCE_MEM_64 0x00100000 -static int xen_host_pci_sysfs_path(const XenHostPCIDevice *d, - const char *name, char *buf, ssize_t size) +static void xen_host_pci_sysfs_path(const XenHostPCIDevice *d, + const char *name, char *buf, ssize_t size) { int rc; rc = snprintf(buf, size, "/sys/bus/pci/devices/%04x:%02x:%02x.%d/%s", d->domain, d->bus, d->dev, d->func, name); - - if (rc >= size || rc < 0) { - /* The ouput is truncated or an other error is encountered */ - return -ENODEV; - } - return 0; + assert(rc >= 0 && rc < size); } /* This size should be enough to read the first 7 lines of a resource file */ #define XEN_HOST_PCI_RESOURCE_BUFFER_SIZE 400 -static int xen_host_pci_get_resource(XenHostPCIDevice *d) +static void xen_host_pci_get_resource(XenHostPCIDevice *d, Error **errp) { int i, rc, fd; char path[PATH_MAX]; @@ -58,25 +56,22 @@ static int xen_host_pci_get_resource(XenHostPCIDevice *d) char *endptr, *s; uint8_t type; - rc = xen_host_pci_sysfs_path(d, "resource", path, sizeof (path)); - if (rc) { - return rc; - } + xen_host_pci_sysfs_path(d, "resource", path, sizeof(path)); + fd = open(path, O_RDONLY); if (fd == -1) { - XEN_HOST_PCI_LOG("Error: Can't open %s: %s\n", path, strerror(errno)); - return -errno; + error_setg_file_open(errp, errno, path); + return; } do { - rc = read(fd, &buf, sizeof (buf) - 1); + rc = read(fd, &buf, sizeof(buf) - 1); if (rc < 0 && errno != EINTR) { - rc = -errno; + error_setg_errno(errp, errno, "read err"); goto out; } } while (rc < 0); buf[rc] = 0; - rc = 0; s = buf; for (i = 0; i < PCI_NUM_REGIONS; i++) { @@ -129,70 +124,69 @@ static int xen_host_pci_get_resource(XenHostPCIDevice *d) d->rom.bus_flags = flags & IORESOURCE_BITS; } } + if (i != PCI_NUM_REGIONS) { - /* Invalid format or input to short */ - rc = -ENODEV; + error_setg(errp, "Invalid format or input too short: %s", buf); } out: close(fd); - return rc; } /* This size should be enough to read a long from a file */ #define XEN_HOST_PCI_GET_VALUE_BUFFER_SIZE 22 -static int xen_host_pci_get_value(XenHostPCIDevice *d, const char *name, - unsigned int *pvalue, int base) +static void xen_host_pci_get_value(XenHostPCIDevice *d, const char *name, + unsigned int *pvalue, int base, Error **errp) { char path[PATH_MAX]; char buf[XEN_HOST_PCI_GET_VALUE_BUFFER_SIZE]; int fd, rc; unsigned long value; - char *endptr; + const char *endptr; + + xen_host_pci_sysfs_path(d, name, path, sizeof(path)); - rc = xen_host_pci_sysfs_path(d, name, path, sizeof (path)); - if (rc) { - return rc; - } fd = open(path, O_RDONLY); if (fd == -1) { - XEN_HOST_PCI_LOG("Error: Can't open %s: %s\n", path, strerror(errno)); - return -errno; + error_setg_file_open(errp, errno, path); + return; } + do { - rc = read(fd, &buf, sizeof (buf) - 1); + rc = read(fd, &buf, sizeof(buf) - 1); if (rc < 0 && errno != EINTR) { - rc = -errno; + error_setg_errno(errp, errno, "read err"); goto out; } } while (rc < 0); + buf[rc] = 0; - value = strtol(buf, &endptr, base); - if (endptr == buf || *endptr != '\n') { - rc = -1; - } else if ((value == LONG_MIN || value == LONG_MAX) && errno == ERANGE) { - rc = -errno; - } else { - rc = 0; + rc = qemu_strtoul(buf, &endptr, base, &value); + if (!rc) { + assert(value <= UINT_MAX); *pvalue = value; + } else { + error_setg_errno(errp, -rc, "failed to parse value '%s'", buf); } + out: close(fd); - return rc; } -static inline int xen_host_pci_get_hex_value(XenHostPCIDevice *d, - const char *name, - unsigned int *pvalue) +static inline void xen_host_pci_get_hex_value(XenHostPCIDevice *d, + const char *name, + unsigned int *pvalue, + Error **errp) { - return xen_host_pci_get_value(d, name, pvalue, 16); + xen_host_pci_get_value(d, name, pvalue, 16, errp); } -static inline int xen_host_pci_get_dec_value(XenHostPCIDevice *d, - const char *name, - unsigned int *pvalue) +static inline void xen_host_pci_get_dec_value(XenHostPCIDevice *d, + const char *name, + unsigned int *pvalue, + Error **errp) { - return xen_host_pci_get_value(d, name, pvalue, 10); + xen_host_pci_get_value(d, name, pvalue, 10, errp); } static bool xen_host_pci_dev_is_virtfn(XenHostPCIDevice *d) @@ -200,26 +194,21 @@ static bool xen_host_pci_dev_is_virtfn(XenHostPCIDevice *d) char path[PATH_MAX]; struct stat buf; - if (xen_host_pci_sysfs_path(d, "physfn", path, sizeof (path))) { - return false; - } + xen_host_pci_sysfs_path(d, "physfn", path, sizeof(path)); + return !stat(path, &buf); } -static int xen_host_pci_config_open(XenHostPCIDevice *d) +static void xen_host_pci_config_open(XenHostPCIDevice *d, Error **errp) { char path[PATH_MAX]; - int rc; - rc = xen_host_pci_sysfs_path(d, "config", path, sizeof (path)); - if (rc) { - return rc; - } + xen_host_pci_sysfs_path(d, "config", path, sizeof(path)); + d->config_fd = open(path, O_RDWR); - if (d->config_fd < 0) { - return -errno; + if (d->config_fd == -1) { + error_setg_file_open(errp, errno, path); } - return 0; } static int xen_host_pci_config_read(XenHostPCIDevice *d, @@ -341,11 +330,12 @@ int xen_host_pci_find_ext_cap_offset(XenHostPCIDevice *d, uint32_t cap) return -1; } -int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain, - uint8_t bus, uint8_t dev, uint8_t func) +void xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain, + uint8_t bus, uint8_t dev, uint8_t func, + Error **errp) { unsigned int v; - int rc = 0; + Error *err = NULL; d->config_fd = -1; d->domain = domain; @@ -353,38 +343,56 @@ int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain, d->dev = dev; d->func = func; - rc = xen_host_pci_config_open(d); - if (rc) { + xen_host_pci_config_open(d, &err); + if (err) { goto error; } - rc = xen_host_pci_get_resource(d); - if (rc) { + + xen_host_pci_get_resource(d, &err); + if (err) { goto error; } - rc = xen_host_pci_get_hex_value(d, "vendor", &v); - if (rc) { + + xen_host_pci_get_hex_value(d, "vendor", &v, &err); + if (err) { goto error; } d->vendor_id = v; - rc = xen_host_pci_get_hex_value(d, "device", &v); - if (rc) { + + xen_host_pci_get_hex_value(d, "device", &v, &err); + if (err) { goto error; } d->device_id = v; - rc = xen_host_pci_get_dec_value(d, "irq", &v); - if (rc) { + + xen_host_pci_get_dec_value(d, "irq", &v, &err); + if (err) { goto error; } d->irq = v; + + xen_host_pci_get_hex_value(d, "class", &v, &err); + if (err) { + goto error; + } + d->class_code = v; + d->is_virtfn = xen_host_pci_dev_is_virtfn(d); - return 0; + return; + error: + error_propagate(errp, err); + if (d->config_fd >= 0) { close(d->config_fd); d->config_fd = -1; } - return rc; +} + +bool xen_host_pci_device_closed(XenHostPCIDevice *d) +{ + return d->config_fd == -1; } void xen_host_pci_device_put(XenHostPCIDevice *d) diff --git a/qemu/hw/xen/xen-host-pci-device.h b/qemu/hw/xen/xen-host-pci-device.h index c2486f0c1..6acf36e13 100644 --- a/qemu/hw/xen/xen-host-pci-device.h +++ b/qemu/hw/xen/xen-host-pci-device.h @@ -25,6 +25,7 @@ typedef struct XenHostPCIDevice { uint16_t vendor_id; uint16_t device_id; + uint32_t class_code; int irq; XenHostPCIIORegion io_regions[PCI_NUM_REGIONS - 1]; @@ -35,9 +36,11 @@ typedef struct XenHostPCIDevice { int config_fd; } XenHostPCIDevice; -int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain, - uint8_t bus, uint8_t dev, uint8_t func); +void xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain, + uint8_t bus, uint8_t dev, uint8_t func, + Error **errp); void xen_host_pci_device_put(XenHostPCIDevice *pci_dev); +bool xen_host_pci_device_closed(XenHostPCIDevice *d); int xen_host_pci_get_byte(XenHostPCIDevice *d, int pos, uint8_t *p); int xen_host_pci_get_word(XenHostPCIDevice *d, int pos, uint16_t *p); diff --git a/qemu/hw/xen/xen_backend.c b/qemu/hw/xen/xen_backend.c index 2510e2e4f..60575ad38 100644 --- a/qemu/hw/xen/xen_backend.c +++ b/qemu/hw/xen/xen_backend.c @@ -22,15 +22,7 @@ * TODO: add some xenbus / xenstore concepts overview here. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include #include @@ -44,7 +36,8 @@ /* ------------------------------------------------------------- */ /* public */ -XenXC xen_xc = XC_HANDLER_INITIAL_VALUE; +xc_interface *xen_xc = NULL; +xenforeignmemory_handle *xen_fmem = NULL; struct xs_handle *xenstore = NULL; const char *xen_protocol; @@ -243,24 +236,24 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, xendev->debug = debug; xendev->local_port = -1; - xendev->evtchndev = xen_xc_evtchn_open(NULL, 0); - if (xendev->evtchndev == XC_HANDLER_INITIAL_VALUE) { + xendev->evtchndev = xenevtchn_open(NULL, 0); + if (xendev->evtchndev == NULL) { xen_be_printf(NULL, 0, "can't open evtchn device\n"); g_free(xendev); return NULL; } - fcntl(xc_evtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC); + fcntl(xenevtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC); if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { - xendev->gnttabdev = xen_xc_gnttab_open(NULL, 0); - if (xendev->gnttabdev == XC_HANDLER_INITIAL_VALUE) { + xendev->gnttabdev = xengnttab_open(NULL, 0); + if (xendev->gnttabdev == NULL) { xen_be_printf(NULL, 0, "can't open gnttab device\n"); - xc_evtchn_close(xendev->evtchndev); + xenevtchn_close(xendev->evtchndev); g_free(xendev); return NULL; } } else { - xendev->gnttabdev = XC_HANDLER_INITIAL_VALUE; + xendev->gnttabdev = NULL; } QTAILQ_INSERT_TAIL(&xendevs, xendev, next); @@ -306,11 +299,11 @@ static struct XenDevice *xen_be_del_xendev(int dom, int dev) g_free(xendev->fe); } - if (xendev->evtchndev != XC_HANDLER_INITIAL_VALUE) { - xc_evtchn_close(xendev->evtchndev); + if (xendev->evtchndev != NULL) { + xenevtchn_close(xendev->evtchndev); } - if (xendev->gnttabdev != XC_HANDLER_INITIAL_VALUE) { - xc_gnttab_close(xendev->gnttabdev); + if (xendev->gnttabdev != NULL) { + xengnttab_close(xendev->gnttabdev); } QTAILQ_REMOVE(&xendevs, xendev, next); @@ -691,13 +684,14 @@ static void xen_be_evtchn_event(void *opaque) struct XenDevice *xendev = opaque; evtchn_port_t port; - port = xc_evtchn_pending(xendev->evtchndev); + port = xenevtchn_pending(xendev->evtchndev); if (port != xendev->local_port) { - xen_be_printf(xendev, 0, "xc_evtchn_pending returned %d (expected %d)\n", + xen_be_printf(xendev, 0, + "xenevtchn_pending returned %d (expected %d)\n", port, xendev->local_port); return; } - xc_evtchn_unmask(xendev->evtchndev, port); + xenevtchn_unmask(xendev->evtchndev, port); if (xendev->ops->event) { xendev->ops->event(xendev); @@ -716,7 +710,7 @@ int xen_be_init(void) qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL); - if (xen_xc == XC_HANDLER_INITIAL_VALUE) { + if (xen_xc == NULL || xen_fmem == NULL) { /* Check if xen_init() have been called */ goto err; } @@ -740,14 +734,14 @@ int xen_be_bind_evtchn(struct XenDevice *xendev) if (xendev->local_port != -1) { return 0; } - xendev->local_port = xc_evtchn_bind_interdomain + xendev->local_port = xenevtchn_bind_interdomain (xendev->evtchndev, xendev->dom, xendev->remote_port); if (xendev->local_port == -1) { - xen_be_printf(xendev, 0, "xc_evtchn_bind_interdomain failed\n"); + xen_be_printf(xendev, 0, "xenevtchn_bind_interdomain failed\n"); return -1; } xen_be_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port); - qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), + qemu_set_fd_handler(xenevtchn_fd(xendev->evtchndev), xen_be_evtchn_event, NULL, xendev); return 0; } @@ -757,15 +751,15 @@ void xen_be_unbind_evtchn(struct XenDevice *xendev) if (xendev->local_port == -1) { return; } - qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), NULL, NULL, NULL); - xc_evtchn_unbind(xendev->evtchndev, xendev->local_port); + qemu_set_fd_handler(xenevtchn_fd(xendev->evtchndev), NULL, NULL, NULL); + xenevtchn_unbind(xendev->evtchndev, xendev->local_port); xen_be_printf(xendev, 2, "unbind evtchn port %d\n", xendev->local_port); xendev->local_port = -1; } int xen_be_send_notify(struct XenDevice *xendev) { - return xc_evtchn_notify(xendev->evtchndev, xendev->local_port); + return xenevtchn_notify(xendev->evtchndev, xendev->local_port); } /* diff --git a/qemu/hw/xen/xen_devconfig.c b/qemu/hw/xen/xen_devconfig.c index e138dbbec..1f30fe4f5 100644 --- a/qemu/hw/xen/xen_devconfig.c +++ b/qemu/hw/xen/xen_devconfig.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/xen/xen_backend.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" diff --git a/qemu/hw/xen/xen_pt.c b/qemu/hw/xen/xen_pt.c index ed5fcaec0..f593b046e 100644 --- a/qemu/hw/xen/xen_pt.c +++ b/qemu/hw/xen/xen_pt.c @@ -52,10 +52,13 @@ * - Set entry->pirq to '-1'. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include #include "hw/pci/pci.h" #include "hw/xen/xen.h" +#include "hw/i386/pc.h" #include "hw/xen/xen_backend.h" #include "xen_pt.h" #include "qemu/range.h" @@ -378,7 +381,7 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr, } } - /* need to shift back before passing them to xen_host_pci_device */ + /* need to shift back before passing them to xen_host_pci_set_block. */ val >>= (addr & 3) << 3; memory_region_transaction_commit(); @@ -406,7 +409,7 @@ out: (uint8_t *)&val + index, len); if (rc < 0) { - XEN_PT_ERR(d, "pci_write_block failed. return value: %d.\n", rc); + XEN_PT_ERR(d, "xen_host_pci_set_block failed. return value: %d.\n", rc); } } } @@ -502,6 +505,7 @@ static int xen_pt_register_regions(XenPCIPassthroughState *s, uint16_t *cmd) d->rom.size, d->rom.base_addr); } + xen_pt_register_vga_regions(d); return 0; } @@ -683,15 +687,89 @@ static const MemoryListener xen_pt_io_listener = { .priority = 10, }; +static void +xen_igd_passthrough_isa_bridge_create(XenPCIPassthroughState *s, + XenHostPCIDevice *dev) +{ + uint16_t gpu_dev_id; + PCIDevice *d = &s->dev; + + gpu_dev_id = dev->device_id; + igd_passthrough_isa_bridge_create(d->bus, gpu_dev_id); +} + +/* destroy. */ +static void xen_pt_destroy(PCIDevice *d) { + + XenPCIPassthroughState *s = XEN_PT_DEVICE(d); + XenHostPCIDevice *host_dev = &s->real_device; + uint8_t machine_irq = s->machine_irq; + uint8_t intx; + int rc; + + if (machine_irq && !xen_host_pci_device_closed(&s->real_device)) { + intx = xen_pt_pci_intx(s); + rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq, + PT_IRQ_TYPE_PCI, + pci_bus_num(d->bus), + PCI_SLOT(s->dev.devfn), + intx, + 0 /* isa_irq */); + if (rc < 0) { + XEN_PT_ERR(d, "unbinding of interrupt INT%c failed." + " (machine irq: %i, err: %d)" + " But bravely continuing on..\n", + 'a' + intx, machine_irq, errno); + } + } + + /* N.B. xen_pt_config_delete takes care of freeing them. */ + if (s->msi) { + xen_pt_msi_disable(s); + } + if (s->msix) { + xen_pt_msix_disable(s); + } + + if (machine_irq) { + xen_pt_mapped_machine_irq[machine_irq]--; + + if (xen_pt_mapped_machine_irq[machine_irq] == 0) { + rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq); + + if (rc < 0) { + XEN_PT_ERR(d, "unmapping of interrupt %i failed. (err: %d)" + " But bravely continuing on..\n", + machine_irq, errno); + } + } + s->machine_irq = 0; + } + + /* delete all emulated config registers */ + xen_pt_config_delete(s); + + xen_pt_unregister_vga_regions(host_dev); + + if (s->listener_set) { + memory_listener_unregister(&s->memory_listener); + memory_listener_unregister(&s->io_listener); + s->listener_set = false; + } + if (!xen_host_pci_device_closed(&s->real_device)) { + xen_host_pci_device_put(&s->real_device); + } +} /* init */ -static int xen_pt_initfn(PCIDevice *d) +static void xen_pt_realize(PCIDevice *d, Error **errp) { XenPCIPassthroughState *s = XEN_PT_DEVICE(d); - int rc = 0; - uint8_t machine_irq = 0; + int i, rc = 0; + uint8_t machine_irq = 0, scratch; uint16_t cmd = 0; int pirq = XEN_PT_UNASSIGNED_PIRQ; + Error *err = NULL; /* register real device */ XEN_PT_LOG(d, "Assigning real physical device %02x:%02x.%d" @@ -699,12 +777,14 @@ static int xen_pt_initfn(PCIDevice *d) s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function, s->dev.devfn); - rc = xen_host_pci_device_get(&s->real_device, - s->hostaddr.domain, s->hostaddr.bus, - s->hostaddr.slot, s->hostaddr.function); - if (rc) { - XEN_PT_ERR(d, "Failed to \"open\" the real pci device. rc: %i\n", rc); - return -1; + xen_host_pci_device_get(&s->real_device, + s->hostaddr.domain, s->hostaddr.bus, + s->hostaddr.slot, s->hostaddr.function, + &err); + if (err) { + error_append_hint(&err, "Failed to \"open\" the real pci device"); + error_propagate(errp, err); + return; } s->is_virtfn = s->real_device.is_virtfn; @@ -715,37 +795,62 @@ static int xen_pt_initfn(PCIDevice *d) } /* Initialize virtualized PCI configuration (Extended 256 Bytes) */ - if (xen_host_pci_get_block(&s->real_device, 0, d->config, - PCI_CONFIG_SPACE_SIZE) == -1) { - xen_host_pci_device_put(&s->real_device); - return -1; - } + memset(d->config, 0, PCI_CONFIG_SPACE_SIZE); s->memory_listener = xen_pt_memory_listener; s->io_listener = xen_pt_io_listener; + /* Setup VGA bios for passthrough GFX */ + if ((s->real_device.domain == 0) && (s->real_device.bus == 0) && + (s->real_device.dev == 2) && (s->real_device.func == 0)) { + if (!is_igd_vga_passthrough(&s->real_device)) { + error_setg(errp, "Need to enable igd-passthru if you're trying" + " to passthrough IGD GFX"); + xen_host_pci_device_put(&s->real_device); + return; + } + + xen_pt_setup_vga(s, &s->real_device, &err); + if (err) { + error_append_hint(&err, "Setup VGA BIOS of passthrough" + " GFX failed"); + error_propagate(errp, err); + xen_host_pci_device_put(&s->real_device); + return; + } + + /* Register ISA bridge for passthrough GFX. */ + xen_igd_passthrough_isa_bridge_create(s, &s->real_device); + } + /* Handle real device's MMIO/PIO BARs */ xen_pt_register_regions(s, &cmd); /* reinitialize each config register to be emulated */ - if (xen_pt_config_init(s)) { - XEN_PT_ERR(d, "PCI Config space initialisation failed.\n"); - xen_host_pci_device_put(&s->real_device); - return -1; + xen_pt_config_init(s, &err); + if (err) { + error_append_hint(&err, "PCI Config space initialisation failed"); + error_report_err(err); + rc = -1; + goto err_out; } /* Bind interrupt */ - if (!s->dev.config[PCI_INTERRUPT_PIN]) { - XEN_PT_LOG(d, "no pin interrupt\n"); + rc = xen_host_pci_get_byte(&s->real_device, PCI_INTERRUPT_PIN, &scratch); + if (rc) { + error_setg_errno(errp, errno, "Failed to read PCI_INTERRUPT_PIN"); + goto err_out; + } + if (!scratch) { + error_setg(errp, "no pin interrupt"); goto out; } machine_irq = s->real_device.irq; rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq); - if (rc < 0) { - XEN_PT_ERR(d, "Mapping machine irq %u to pirq %i failed, (err: %d)\n", - machine_irq, pirq, errno); + error_setg_errno(errp, errno, "Mapping machine irq %u to" + " pirq %i failed", machine_irq, pirq); /* Disable PCI intx assertion (turn on bit10 of devctl) */ cmd |= PCI_COMMAND_INTX_DISABLE; @@ -766,8 +871,8 @@ static int xen_pt_initfn(PCIDevice *d) PCI_SLOT(d->devfn), e_intx); if (rc < 0) { - XEN_PT_ERR(d, "Binding of interrupt %i failed! (err: %d)\n", - e_intx, errno); + error_setg_errno(errp, errno, "Binding of interrupt %u failed", + e_intx); /* Disable PCI intx assertion (turn on bit10 of devctl) */ cmd |= PCI_COMMAND_INTX_DISABLE; @@ -775,8 +880,8 @@ static int xen_pt_initfn(PCIDevice *d) if (xen_pt_mapped_machine_irq[machine_irq] == 0) { if (xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq)) { - XEN_PT_ERR(d, "Unmapping of machine interrupt %i failed!" - " (err: %d)\n", machine_irq, errno); + error_setg_errno(errp, errno, "Unmapping of machine" + " interrupt %u failed", machine_irq); } } s->machine_irq = 0; @@ -785,69 +890,45 @@ static int xen_pt_initfn(PCIDevice *d) out: if (cmd) { - xen_host_pci_set_word(&s->real_device, PCI_COMMAND, - pci_get_word(d->config + PCI_COMMAND) | cmd); + uint16_t val; + + rc = xen_host_pci_get_word(&s->real_device, PCI_COMMAND, &val); + if (rc) { + error_setg_errno(errp, errno, "Failed to read PCI_COMMAND"); + goto err_out; + } else { + val |= cmd; + rc = xen_host_pci_set_word(&s->real_device, PCI_COMMAND, val); + if (rc) { + error_setg_errno(errp, errno, "Failed to write PCI_COMMAND" + " val = 0x%x", val); + goto err_out; + } + } } memory_listener_register(&s->memory_listener, &s->dev.bus_master_as); memory_listener_register(&s->io_listener, &address_space_io); + s->listener_set = true; XEN_PT_LOG(d, - "Real physical device %02x:%02x.%d registered successfully!\n", + "Real physical device %02x:%02x.%d registered successfully\n", s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function); - return 0; -} - -static void xen_pt_unregister_device(PCIDevice *d) -{ - XenPCIPassthroughState *s = XEN_PT_DEVICE(d); - uint8_t machine_irq = s->machine_irq; - uint8_t intx = xen_pt_pci_intx(s); - int rc; - - if (machine_irq) { - rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq, - PT_IRQ_TYPE_PCI, - pci_bus_num(d->bus), - PCI_SLOT(s->dev.devfn), - intx, - 0 /* isa_irq */); - if (rc < 0) { - XEN_PT_ERR(d, "unbinding of interrupt INT%c failed." - " (machine irq: %i, err: %d)" - " But bravely continuing on..\n", - 'a' + intx, machine_irq, errno); - } - } - - if (s->msi) { - xen_pt_msi_disable(s); - } - if (s->msix) { - xen_pt_msix_disable(s); - } - - if (machine_irq) { - xen_pt_mapped_machine_irq[machine_irq]--; + return; - if (xen_pt_mapped_machine_irq[machine_irq] == 0) { - rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq); - - if (rc < 0) { - XEN_PT_ERR(d, "unmapping of interrupt %i failed. (err: %d)" - " But bravely continuing on..\n", - machine_irq, errno); - } - } +err_out: + for (i = 0; i < PCI_ROM_SLOT; i++) { + object_unparent(OBJECT(&s->bar[i])); } + object_unparent(OBJECT(&s->rom)); - /* delete all emulated config registers */ - xen_pt_config_delete(s); - - memory_listener_unregister(&s->memory_listener); - memory_listener_unregister(&s->io_listener); + xen_pt_destroy(d); + assert(rc); +} - xen_host_pci_device_put(&s->real_device); +static void xen_pt_unregister_device(PCIDevice *d) +{ + xen_pt_destroy(d); } static Property xen_pci_passthrough_properties[] = { @@ -861,7 +942,7 @@ static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = xen_pt_initfn; + k->realize = xen_pt_realize; k->exit = xen_pt_unregister_device; k->config_read = xen_pt_pci_read_config; k->config_write = xen_pt_pci_write_config; @@ -870,10 +951,18 @@ static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data) dc->props = xen_pci_passthrough_properties; }; +static void xen_pci_passthrough_finalize(Object *obj) +{ + XenPCIPassthroughState *s = XEN_PT_DEVICE(obj); + + xen_pt_msix_delete(s); +} + static const TypeInfo xen_pci_passthrough_info = { .name = TYPE_XEN_PT_DEVICE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(XenPCIPassthroughState), + .instance_finalize = xen_pci_passthrough_finalize, .class_init = xen_pci_passthrough_class_init, }; diff --git a/qemu/hw/xen/xen_pt.h b/qemu/hw/xen/xen_pt.h index 393f36ccb..c2f8e1fc2 100644 --- a/qemu/hw/xen/xen_pt.h +++ b/qemu/hw/xen/xen_pt.h @@ -40,6 +40,9 @@ typedef struct XenPCIPassthroughState XenPCIPassthroughState; #define XEN_PT_DEVICE(obj) \ OBJECT_CHECK(XenPCIPassthroughState, (obj), TYPE_XEN_PT_DEVICE) +uint32_t igd_read_opregion(XenPCIPassthroughState *s); +void igd_write_opregion(XenPCIPassthroughState *s, uint32_t val); + /* function type for config reg */ typedef int (*xen_pt_conf_reg_init) (XenPCIPassthroughState *, XenPTRegInfo *, uint32_t real_offset, @@ -66,8 +69,9 @@ typedef int (*xen_pt_conf_byte_read) #define XEN_PT_BAR_ALLF 0xFFFFFFFF #define XEN_PT_BAR_UNMAPPED (-1) -#define PCI_CAP_MAX 48 +#define XEN_PCI_CAP_MAX 48 +#define XEN_PCI_INTEL_OPREGION 0xfc typedef enum { XEN_PT_GRP_TYPE_HARDWIRED = 0, /* 0 Hardwired reg group */ @@ -109,6 +113,8 @@ struct XenPTRegInfo { uint32_t res_mask; /* reg read only field mask (ON:RO/ROS, OFF:other) */ uint32_t ro_mask; + /* reg read/write-1-clear field mask (ON:RW1C/RW1CS, OFF:other) */ + uint32_t rw1c_mask; /* reg emulate field mask (ON:emu, OFF:passthrough) */ uint32_t emu_mask; xen_pt_conf_reg_init init; @@ -134,7 +140,11 @@ struct XenPTRegInfo { struct XenPTReg { QLIST_ENTRY(XenPTReg) entries; XenPTRegInfo *reg; - uint32_t data; /* emulated value */ + union { + uint8_t *byte; + uint16_t *half_word; + uint32_t *word; + } ptr; /* pointer to dev.config. */ }; typedef const struct XenPTRegGroupInfo XenPTRegGroupInfo; @@ -179,13 +189,13 @@ typedef struct XenPTMSIXEntry { int pirq; uint64_t addr; uint32_t data; - uint32_t vector_ctrl; + uint32_t latch[4]; bool updated; /* indicate whether MSI ADDR or DATA is updated */ - bool warned; /* avoid issuing (bogus) warning more than once */ } XenPTMSIXEntry; typedef struct XenPTMSIX { uint32_t ctrl_offset; bool enabled; + bool maskall; int total_entries; int bar_index; uint64_t table_base; @@ -217,9 +227,10 @@ struct XenPCIPassthroughState { MemoryListener memory_listener; MemoryListener io_listener; + bool listener_set; }; -int xen_pt_config_init(XenPCIPassthroughState *s); +void xen_pt_config_init(XenPCIPassthroughState *s, Error **errp); void xen_pt_config_delete(XenPCIPassthroughState *s); XenPTRegGroup *xen_pt_find_reg_grp(XenPCIPassthroughState *s, uint32_t address); XenPTReg *xen_pt_find_reg(XenPTRegGroup *reg_grp, uint32_t address); @@ -282,6 +293,7 @@ static inline uint8_t xen_pt_pci_intx(XenPCIPassthroughState *s) " value=%i, acceptable range is 1 - 4\n", r_val); r_val = 0; } else { + /* Note that if s.real_device.config_fd is closed we make 0xff. */ r_val -= 1; } @@ -289,13 +301,13 @@ static inline uint8_t xen_pt_pci_intx(XenPCIPassthroughState *s) } /* MSI/MSI-X */ -int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool en); int xen_pt_msi_setup(XenPCIPassthroughState *s); int xen_pt_msi_update(XenPCIPassthroughState *d); void xen_pt_msi_disable(XenPCIPassthroughState *s); int xen_pt_msix_init(XenPCIPassthroughState *s, uint32_t base); void xen_pt_msix_delete(XenPCIPassthroughState *s); +void xen_pt_msix_unmap(XenPCIPassthroughState *s); int xen_pt_msix_update(XenPCIPassthroughState *s); int xen_pt_msix_update_remap(XenPCIPassthroughState *s, int bar_index); void xen_pt_msix_disable(XenPCIPassthroughState *s); @@ -305,5 +317,19 @@ static inline bool xen_pt_has_msix_mapping(XenPCIPassthroughState *s, int bar) return s->msix && s->msix->bar_index == bar; } - +extern void *pci_assign_dev_load_option_rom(PCIDevice *dev, + struct Object *owner, int *size, + unsigned int domain, + unsigned int bus, unsigned int slot, + unsigned int function); +extern bool has_igd_gfx_passthru; +static inline bool is_igd_vga_passthrough(XenHostPCIDevice *dev) +{ + return (has_igd_gfx_passthru + && ((dev->class_code >> 0x8) == PCI_CLASS_DISPLAY_VGA)); +} +int xen_pt_register_vga_regions(XenHostPCIDevice *dev); +int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev); +void xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev, + Error **errp); #endif /* !XEN_PT_H */ diff --git a/qemu/hw/xen/xen_pt_config_init.c b/qemu/hw/xen/xen_pt_config_init.c index dd37be38a..9869ffda0 100644 --- a/qemu/hw/xen/xen_pt_config_init.c +++ b/qemu/hw/xen/xen_pt_config_init.c @@ -12,6 +12,8 @@ * This file implements direct PCI assignment to a HVM guest */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/timer.h" #include "hw/xen/xen_backend.h" #include "xen_pt.h" @@ -128,10 +130,11 @@ static int xen_pt_byte_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, { XenPTRegInfo *reg = cfg_entry->reg; uint8_t valid_emu_mask = 0; + uint8_t *data = cfg_entry->ptr.byte; /* emulate byte register */ valid_emu_mask = reg->emu_mask & valid_mask; - *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + *value = XEN_PT_MERGE_VALUE(*value, *data, ~valid_emu_mask); return 0; } @@ -140,10 +143,11 @@ static int xen_pt_word_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, { XenPTRegInfo *reg = cfg_entry->reg; uint16_t valid_emu_mask = 0; + uint16_t *data = cfg_entry->ptr.half_word; /* emulate word register */ valid_emu_mask = reg->emu_mask & valid_mask; - *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + *value = XEN_PT_MERGE_VALUE(*value, *data, ~valid_emu_mask); return 0; } @@ -152,10 +156,11 @@ static int xen_pt_long_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, { XenPTRegInfo *reg = cfg_entry->reg; uint32_t valid_emu_mask = 0; + uint32_t *data = cfg_entry->ptr.word; /* emulate long register */ valid_emu_mask = reg->emu_mask & valid_mask; - *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + *value = XEN_PT_MERGE_VALUE(*value, *data, ~valid_emu_mask); return 0; } @@ -169,13 +174,15 @@ static int xen_pt_byte_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, XenPTRegInfo *reg = cfg_entry->reg; uint8_t writable_mask = 0; uint8_t throughable_mask = get_throughable_mask(s, reg, valid_mask); + uint8_t *data = cfg_entry->ptr.byte; /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); /* create value for writing to I/O device register */ - *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + *val = XEN_PT_MERGE_VALUE(*val, dev_value & ~reg->rw1c_mask, + throughable_mask); return 0; } @@ -186,13 +193,15 @@ static int xen_pt_word_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, XenPTRegInfo *reg = cfg_entry->reg; uint16_t writable_mask = 0; uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); + uint16_t *data = cfg_entry->ptr.half_word; /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); /* create value for writing to I/O device register */ - *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + *val = XEN_PT_MERGE_VALUE(*val, dev_value & ~reg->rw1c_mask, + throughable_mask); return 0; } @@ -203,13 +212,15 @@ static int xen_pt_long_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, XenPTRegInfo *reg = cfg_entry->reg; uint32_t writable_mask = 0; uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask); + uint32_t *data = cfg_entry->ptr.word; /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); /* create value for writing to I/O device register */ - *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + *val = XEN_PT_MERGE_VALUE(*val, dev_value & ~reg->rw1c_mask, + throughable_mask); return 0; } @@ -255,7 +266,7 @@ static int xen_pt_status_reg_init(XenPCIPassthroughState *s, reg_entry = xen_pt_find_reg(reg_grp_entry, PCI_CAPABILITY_LIST); if (reg_entry) { /* check Capabilities Pointer register */ - if (reg_entry->data) { + if (*reg_entry->ptr.half_word) { reg_field |= PCI_STATUS_CAP_LIST; } else { reg_field &= ~PCI_STATUS_CAP_LIST; @@ -301,10 +312,11 @@ static int xen_pt_cmd_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, XenPTRegInfo *reg = cfg_entry->reg; uint16_t writable_mask = 0; uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); + uint16_t *data = cfg_entry->ptr.half_word; /* modify emulate register */ writable_mask = ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); /* create value for writing to I/O device register */ if (*val & PCI_COMMAND_INTX_DISABLE) { @@ -447,7 +459,7 @@ static int xen_pt_bar_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, /* emulate BAR */ valid_emu_mask = bar_emu_mask & valid_mask; - *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + *value = XEN_PT_MERGE_VALUE(*value, *cfg_entry->ptr.word, ~valid_emu_mask); return 0; } @@ -464,6 +476,7 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint32_t bar_ro_mask = 0; uint32_t r_size = 0; int index = 0; + uint32_t *data = cfg_entry->ptr.word; index = xen_pt_bar_offset_to_index(reg->offset); if (index < 0 || index >= PCI_NUM_REGIONS) { @@ -500,7 +513,7 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, /* modify emulate register */ writable_mask = bar_emu_mask & ~bar_ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); /* check whether we need to update the virtual region address or not */ switch (s->bases[index].bar_flag) { @@ -533,6 +546,7 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s, uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask); pcibus_t r_size = 0; uint32_t bar_ro_mask = 0; + uint32_t *data = cfg_entry->ptr.word; r_size = d->io_regions[PCI_ROM_SLOT].size; base = &s->bases[PCI_ROM_SLOT]; @@ -544,7 +558,7 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s, /* modify emulate register */ writable_mask = ~bar_ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); /* create value for writing to I/O device register */ *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); @@ -552,6 +566,22 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s, return 0; } +static int xen_pt_intel_opregion_read(XenPCIPassthroughState *s, + XenPTReg *cfg_entry, + uint32_t *value, uint32_t valid_mask) +{ + *value = igd_read_opregion(s); + return 0; +} + +static int xen_pt_intel_opregion_write(XenPCIPassthroughState *s, + XenPTReg *cfg_entry, uint32_t *value, + uint32_t dev_value, uint32_t valid_mask) +{ + igd_write_opregion(s, *value); + return 0; +} + /* Header Type0 reg static information table */ static XenPTRegInfo xen_pt_emu_reg_header0[] = { /* Vendor ID reg */ @@ -608,6 +638,7 @@ static XenPTRegInfo xen_pt_emu_reg_header0[] = { .init_val = 0x0000, .res_mask = 0x0007, .ro_mask = 0x06F8, + .rw1c_mask = 0xF900, .emu_mask = 0x0010, .init = xen_pt_status_reg_init, .u.w.read = xen_pt_word_reg_read, @@ -800,15 +831,21 @@ static XenPTRegInfo xen_pt_emu_reg_vendor[] = { static inline uint8_t get_capability_version(XenPCIPassthroughState *s, uint32_t offset) { - uint8_t flags = pci_get_byte(s->dev.config + offset + PCI_EXP_FLAGS); - return flags & PCI_EXP_FLAGS_VERS; + uint8_t flag; + if (xen_host_pci_get_byte(&s->real_device, offset + PCI_EXP_FLAGS, &flag)) { + return 0; + } + return flag & PCI_EXP_FLAGS_VERS; } static inline uint8_t get_device_type(XenPCIPassthroughState *s, uint32_t offset) { - uint8_t flags = pci_get_byte(s->dev.config + offset + PCI_EXP_FLAGS); - return (flags & PCI_EXP_FLAGS_TYPE) >> 4; + uint8_t flag; + if (xen_host_pci_get_byte(&s->real_device, offset + PCI_EXP_FLAGS, &flag)) { + return 0; + } + return (flag & PCI_EXP_FLAGS_TYPE) >> 4; } /* initialize Link Control register */ @@ -857,8 +894,14 @@ static int xen_pt_linkctrl2_reg_init(XenPCIPassthroughState *s, reg_field = XEN_PT_INVALID_REG; } else { /* set Supported Link Speed */ - uint8_t lnkcap = pci_get_byte(s->dev.config + real_offset - reg->offset - + PCI_EXP_LNKCAP); + uint8_t lnkcap; + int rc; + rc = xen_host_pci_get_byte(&s->real_device, + real_offset - reg->offset + PCI_EXP_LNKCAP, + &lnkcap); + if (rc) { + return rc; + } reg_field |= PCI_EXP_LNKCAP_SLS & lnkcap; } @@ -907,6 +950,7 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[] = { .size = 2, .res_mask = 0xFFC0, .ro_mask = 0x0030, + .rw1c_mask = 0x000F, .init = xen_pt_common_reg_init, .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_word_reg_write, @@ -927,6 +971,7 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[] = { .offset = PCI_EXP_LNKSTA, .size = 2, .ro_mask = 0x3FFF, + .rw1c_mask = 0xC000, .init = xen_pt_common_reg_init, .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_word_reg_write, @@ -963,26 +1008,6 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[] = { * Power Management Capability */ -/* write Power Management Control/Status register */ -static int xen_pt_pmcsr_reg_write(XenPCIPassthroughState *s, - XenPTReg *cfg_entry, uint16_t *val, - uint16_t dev_value, uint16_t valid_mask) -{ - XenPTRegInfo *reg = cfg_entry->reg; - uint16_t writable_mask = 0; - uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); - - /* modify emulate register */ - writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); - - /* create value for writing to I/O device register */ - *val = XEN_PT_MERGE_VALUE(*val, dev_value & ~PCI_PM_CTRL_PME_STATUS, - throughable_mask); - - return 0; -} - /* Power Management Capability reg static information table */ static XenPTRegInfo xen_pt_emu_reg_pm[] = { /* Next Pointer reg */ @@ -1013,11 +1038,12 @@ static XenPTRegInfo xen_pt_emu_reg_pm[] = { .size = 2, .init_val = 0x0008, .res_mask = 0x00F0, - .ro_mask = 0xE10C, + .ro_mask = 0x610C, + .rw1c_mask = 0x8000, .emu_mask = 0x810B, .init = xen_pt_common_reg_init, .u.w.read = xen_pt_word_reg_read, - .u.w.write = xen_pt_pmcsr_reg_write, + .u.w.write = xen_pt_word_reg_write, }, { .size = 0, @@ -1039,13 +1065,15 @@ static int xen_pt_msgctrl_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { - PCIDevice *d = &s->dev; XenPTMSI *msi = s->msi; - uint16_t reg_field = 0; + uint16_t reg_field; + int rc; /* use I/O device register's value as initial value */ - reg_field = pci_get_word(d->config + real_offset); - + rc = xen_host_pci_get_word(&s->real_device, real_offset, ®_field); + if (rc) { + return rc; + } if (reg_field & PCI_MSI_FLAGS_ENABLE) { XEN_PT_LOG(&s->dev, "MSI already enabled, disabling it first\n"); xen_host_pci_set_word(&s->real_device, real_offset, @@ -1067,6 +1095,7 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s, XenPTMSI *msi = s->msi; uint16_t writable_mask = 0; uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); + uint16_t *data = cfg_entry->ptr.half_word; /* Currently no support for multi-vector */ if (*val & PCI_MSI_FLAGS_QSIZE) { @@ -1075,8 +1104,8 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s, /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); - msi->flags |= cfg_entry->data & ~PCI_MSI_FLAGS_ENABLE; + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); + msi->flags |= *data & ~PCI_MSI_FLAGS_ENABLE; /* create value for writing to I/O device register */ *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); @@ -1086,7 +1115,7 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s, /* setup MSI pirq for the first time */ if (!msi->initialized) { /* Init physical one */ - XEN_PT_LOG(&s->dev, "setup MSI\n"); + XEN_PT_LOG(&s->dev, "setup MSI (register: %x).\n", *val); if (xen_pt_msi_setup(s)) { /* We do not broadcast the error to the framework code, so * that MSI errors are contained in MSI emulation code and @@ -1094,12 +1123,12 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s, * Guest MSI would be actually not working. */ *val &= ~PCI_MSI_FLAGS_ENABLE; - XEN_PT_WARN(&s->dev, "Can not map MSI.\n"); + XEN_PT_WARN(&s->dev, "Can not map MSI (register: %x)!\n", *val); return 0; } if (xen_pt_msi_update(s)) { *val &= ~PCI_MSI_FLAGS_ENABLE; - XEN_PT_WARN(&s->dev, "Can not bind MSI\n"); + XEN_PT_WARN(&s->dev, "Can not bind MSI (register: %x)!\n", *val); return 0; } msi->initialized = true; @@ -1190,18 +1219,19 @@ static int xen_pt_msgaddr32_reg_write(XenPCIPassthroughState *s, { XenPTRegInfo *reg = cfg_entry->reg; uint32_t writable_mask = 0; - uint32_t old_addr = cfg_entry->data; + uint32_t old_addr = *cfg_entry->ptr.word; + uint32_t *data = cfg_entry->ptr.word; /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); - s->msi->addr_lo = cfg_entry->data; + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); + s->msi->addr_lo = *data; /* create value for writing to I/O device register */ *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0); /* update MSI */ - if (cfg_entry->data != old_addr) { + if (*data != old_addr) { if (s->msi->mapped) { xen_pt_msi_update(s); } @@ -1216,7 +1246,8 @@ static int xen_pt_msgaddr64_reg_write(XenPCIPassthroughState *s, { XenPTRegInfo *reg = cfg_entry->reg; uint32_t writable_mask = 0; - uint32_t old_addr = cfg_entry->data; + uint32_t old_addr = *cfg_entry->ptr.word; + uint32_t *data = cfg_entry->ptr.word; /* check whether the type is 64 bit or not */ if (!(s->msi->flags & PCI_MSI_FLAGS_64BIT)) { @@ -1227,15 +1258,15 @@ static int xen_pt_msgaddr64_reg_write(XenPCIPassthroughState *s, /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); /* update the msi_info too */ - s->msi->addr_hi = cfg_entry->data; + s->msi->addr_hi = *data; /* create value for writing to I/O device register */ *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0); /* update MSI */ - if (cfg_entry->data != old_addr) { + if (*data != old_addr) { if (s->msi->mapped) { xen_pt_msi_update(s); } @@ -1254,8 +1285,9 @@ static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s, XenPTRegInfo *reg = cfg_entry->reg; XenPTMSI *msi = s->msi; uint16_t writable_mask = 0; - uint16_t old_data = cfg_entry->data; + uint16_t old_data = *cfg_entry->ptr.half_word; uint32_t offset = reg->offset; + uint16_t *data = cfg_entry->ptr.half_word; /* check the offset whether matches the type or not */ if (!xen_pt_msi_check_type(offset, msi->flags, DATA)) { @@ -1266,15 +1298,15 @@ static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s, /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); /* update the msi_info too */ - msi->data = cfg_entry->data; + msi->data = *data; /* create value for writing to I/O device register */ *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0); /* update MSI */ - if (cfg_entry->data != old_data) { + if (*data != old_data) { if (msi->mapped) { xen_pt_msi_update(s); } @@ -1411,14 +1443,16 @@ static int xen_pt_msixctrl_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { - PCIDevice *d = &s->dev; - uint16_t reg_field = 0; + uint16_t reg_field; + int rc; /* use I/O device register's value as initial value */ - reg_field = pci_get_word(d->config + real_offset); - + rc = xen_host_pci_get_word(&s->real_device, real_offset, ®_field); + if (rc) { + return rc; + } if (reg_field & PCI_MSIX_FLAGS_ENABLE) { - XEN_PT_LOG(d, "MSIX already enabled, disabling it first\n"); + XEN_PT_LOG(&s->dev, "MSIX already enabled, disabling it first\n"); xen_host_pci_set_word(&s->real_device, real_offset, reg_field & ~PCI_MSIX_FLAGS_ENABLE); } @@ -1436,10 +1470,11 @@ static int xen_pt_msixctrl_reg_write(XenPCIPassthroughState *s, uint16_t writable_mask = 0; uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); int debug_msix_enabled_old; + uint16_t *data = cfg_entry->ptr.half_word; /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); /* create value for writing to I/O device register */ *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); @@ -1452,6 +1487,8 @@ static int xen_pt_msixctrl_reg_write(XenPCIPassthroughState *s, xen_pt_msix_disable(s); } + s->msix->maskall = *val & PCI_MSIX_FLAGS_MASKALL; + debug_msix_enabled_old = s->msix->enabled; s->msix->enabled = !!(*val & PCI_MSIX_FLAGS_ENABLE); if (s->msix->enabled != debug_msix_enabled_old) { @@ -1492,6 +1529,19 @@ static XenPTRegInfo xen_pt_emu_reg_msix[] = { }, }; +static XenPTRegInfo xen_pt_emu_reg_igd_opregion[] = { + /* Intel IGFX OpRegion reg */ + { + .offset = 0x0, + .size = 4, + .init_val = 0, + .u.dw.read = xen_pt_intel_opregion_read, + .u.dw.write = xen_pt_intel_opregion_write, + }, + { + .size = 0, + }, +}; /**************************** * Capabilities @@ -1511,8 +1561,7 @@ static int xen_pt_vendor_size_init(XenPCIPassthroughState *s, const XenPTRegGroupInfo *grp_reg, uint32_t base_offset, uint8_t *size) { - *size = pci_get_byte(s->dev.config + base_offset + 0x02); - return 0; + return xen_host_pci_get_byte(&s->real_device, base_offset + 0x02, size); } /* get PCI Express Capability Structure register group size */ static int xen_pt_pcie_size_init(XenPCIPassthroughState *s, @@ -1591,12 +1640,15 @@ static int xen_pt_msi_size_init(XenPCIPassthroughState *s, const XenPTRegGroupInfo *grp_reg, uint32_t base_offset, uint8_t *size) { - PCIDevice *d = &s->dev; uint16_t msg_ctrl = 0; uint8_t msi_size = 0xa; + int rc; - msg_ctrl = pci_get_word(d->config + (base_offset + PCI_MSI_FLAGS)); - + rc = xen_host_pci_get_word(&s->real_device, base_offset + PCI_MSI_FLAGS, + &msg_ctrl); + if (rc) { + return rc; + } /* check if 64-bit address is capable of per-vector masking */ if (msg_ctrl & PCI_MSI_FLAGS_64BIT) { msi_size += 4; @@ -1729,6 +1781,14 @@ static const XenPTRegGroupInfo xen_pt_emu_reg_grps[] = { .size_init = xen_pt_msix_size_init, .emu_regs = xen_pt_emu_reg_msix, }, + /* Intel IGD Opregion group */ + { + .grp_id = XEN_PCI_INTEL_OPREGION, + .grp_type = XEN_PT_GRP_TYPE_EMU, + .grp_size = 0x4, + .size_init = xen_pt_reg_grp_size_init, + .emu_regs = xen_pt_emu_reg_igd_opregion, + }, { .grp_size = 0, }, @@ -1739,11 +1799,14 @@ static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { - int i; - uint8_t *config = s->dev.config; - uint32_t reg_field = pci_get_byte(config + real_offset); + int i, rc; + uint8_t reg_field; uint8_t cap_id = 0; + rc = xen_host_pci_get_byte(&s->real_device, real_offset, ®_field); + if (rc) { + return rc; + } /* find capability offset */ while (reg_field) { for (i = 0; xen_pt_emu_reg_grps[i].grp_size != 0; i++) { @@ -1752,7 +1815,13 @@ static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s, continue; } - cap_id = pci_get_byte(config + reg_field + PCI_CAP_LIST_ID); + rc = xen_host_pci_get_byte(&s->real_device, + reg_field + PCI_CAP_LIST_ID, &cap_id); + if (rc) { + XEN_PT_ERR(&s->dev, "Failed to read capability @0x%x (rc:%d)\n", + reg_field + PCI_CAP_LIST_ID, rc); + return rc; + } if (xen_pt_emu_reg_grps[i].grp_id == cap_id) { if (xen_pt_emu_reg_grps[i].grp_type == XEN_PT_GRP_TYPE_EMU) { goto out; @@ -1763,7 +1832,11 @@ static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s, } /* next capability */ - reg_field = pci_get_byte(config + reg_field + PCI_CAP_LIST_NEXT); + rc = xen_host_pci_get_byte(&s->real_device, + reg_field + PCI_CAP_LIST_NEXT, ®_field); + if (rc) { + return rc; + } } out: @@ -1779,7 +1852,7 @@ out: static uint8_t find_cap_offset(XenPCIPassthroughState *s, uint8_t cap) { uint8_t id; - unsigned max_cap = PCI_CAP_MAX; + unsigned max_cap = XEN_PCI_CAP_MAX; uint8_t pos = PCI_CAPABILITY_LIST; uint8_t status = 0; @@ -1816,8 +1889,9 @@ static uint8_t find_cap_offset(XenPCIPassthroughState *s, uint8_t cap) return 0; } -static int xen_pt_config_reg_init(XenPCIPassthroughState *s, - XenPTRegGroup *reg_grp, XenPTRegInfo *reg) +static void xen_pt_config_reg_init(XenPCIPassthroughState *s, + XenPTRegGroup *reg_grp, XenPTRegInfo *reg, + Error **errp) { XenPTReg *reg_entry; uint32_t data = 0; @@ -1827,30 +1901,94 @@ static int xen_pt_config_reg_init(XenPCIPassthroughState *s, reg_entry->reg = reg; if (reg->init) { + uint32_t host_mask, size_mask; + unsigned int offset; + uint32_t val; + /* initialize emulate register */ rc = reg->init(s, reg_entry->reg, reg_grp->base_offset + reg->offset, &data); if (rc < 0) { g_free(reg_entry); - return rc; + error_setg(errp, "Init emulate register fail"); + return; } if (data == XEN_PT_INVALID_REG) { /* free unused BAR register entry */ g_free(reg_entry); - return 0; + return; + } + /* Sync up the data to dev.config */ + offset = reg_grp->base_offset + reg->offset; + size_mask = 0xFFFFFFFF >> ((4 - reg->size) << 3); + + switch (reg->size) { + case 1: rc = xen_host_pci_get_byte(&s->real_device, offset, (uint8_t *)&val); + break; + case 2: rc = xen_host_pci_get_word(&s->real_device, offset, (uint16_t *)&val); + break; + case 4: rc = xen_host_pci_get_long(&s->real_device, offset, &val); + break; + default: abort(); + } + if (rc) { + /* Serious issues when we cannot read the host values! */ + g_free(reg_entry); + error_setg(errp, "Cannot read host values"); + return; + } + /* Set bits in emu_mask are the ones we emulate. The dev.config shall + * contain the emulated view of the guest - therefore we flip the mask + * to mask out the host values (which dev.config initially has) . */ + host_mask = size_mask & ~reg->emu_mask; + + if ((data & host_mask) != (val & host_mask)) { + uint32_t new_val; + + /* Mask out host (including past size). */ + new_val = val & host_mask; + /* Merge emulated ones (excluding the non-emulated ones). */ + new_val |= data & host_mask; + /* Leave intact host and emulated values past the size - even though + * we do not care as we write per reg->size granularity, but for the + * logging below lets have the proper value. */ + new_val |= ((val | data)) & ~size_mask; + XEN_PT_LOG(&s->dev,"Offset 0x%04x mismatch! Emulated=0x%04x, host=0x%04x, syncing to 0x%04x.\n", + offset, data, val, new_val); + val = new_val; + } else + val = data; + + if (val & ~size_mask) { + error_setg(errp, "Offset 0x%04x:0x%04x expands past" + " register size (%d)", offset, val, reg->size); + g_free(reg_entry); + return; + } + /* This could be just pci_set_long as we don't modify the bits + * past reg->size, but in case this routine is run in parallel or the + * init value is larger, we do not want to over-write registers. */ + switch (reg->size) { + case 1: pci_set_byte(s->dev.config + offset, (uint8_t)val); + break; + case 2: pci_set_word(s->dev.config + offset, (uint16_t)val); + break; + case 4: pci_set_long(s->dev.config + offset, val); + break; + default: abort(); } - /* set register value */ - reg_entry->data = data; + /* set register value pointer to the data. */ + reg_entry->ptr.byte = s->dev.config + offset; + } /* list add register entry */ QLIST_INSERT_HEAD(®_grp->reg_tbl_list, reg_entry, entries); - - return 0; } -int xen_pt_config_init(XenPCIPassthroughState *s) +void xen_pt_config_init(XenPCIPassthroughState *s, Error **errp) { int i, rc; + Error *err = NULL; QLIST_INIT(&s->reg_grps); @@ -1858,7 +1996,8 @@ int xen_pt_config_init(XenPCIPassthroughState *s) uint32_t reg_grp_offset = 0; XenPTRegGroup *reg_grp_entry = NULL; - if (xen_pt_emu_reg_grps[i].grp_id != 0xFF) { + if (xen_pt_emu_reg_grps[i].grp_id != 0xFF + && xen_pt_emu_reg_grps[i].grp_id != XEN_PCI_INTEL_OPREGION) { if (xen_pt_hide_dev_cap(&s->real_device, xen_pt_emu_reg_grps[i].grp_id)) { continue; @@ -1871,6 +2010,15 @@ int xen_pt_config_init(XenPCIPassthroughState *s) } } + /* + * By default we will trap up to 0x40 in the cfg space. + * If an intel device is pass through we need to trap 0xfc, + * therefore the size should be 0xff. + */ + if (xen_pt_emu_reg_grps[i].grp_id == XEN_PCI_INTEL_OPREGION) { + reg_grp_offset = XEN_PCI_INTEL_OPREGION; + } + reg_grp_entry = g_new0(XenPTRegGroup, 1); QLIST_INIT(®_grp_entry->reg_tbl_list); QLIST_INSERT_HEAD(&s->reg_grps, reg_grp_entry, entries); @@ -1883,8 +2031,12 @@ int xen_pt_config_init(XenPCIPassthroughState *s) reg_grp_offset, ®_grp_entry->size); if (rc < 0) { + error_setg(&err, "Failed to initialize %d/%zu, type = 0x%x," + " rc: %d", i, ARRAY_SIZE(xen_pt_emu_reg_grps), + xen_pt_emu_reg_grps[i].grp_type, rc); + error_propagate(errp, err); xen_pt_config_delete(s); - return rc; + return; } } @@ -1892,20 +2044,24 @@ int xen_pt_config_init(XenPCIPassthroughState *s) if (xen_pt_emu_reg_grps[i].emu_regs) { int j = 0; XenPTRegInfo *regs = xen_pt_emu_reg_grps[i].emu_regs; + /* initialize capability register */ for (j = 0; regs->size != 0; j++, regs++) { - /* initialize capability register */ - rc = xen_pt_config_reg_init(s, reg_grp_entry, regs); - if (rc < 0) { + xen_pt_config_reg_init(s, reg_grp_entry, regs, &err); + if (err) { + error_append_hint(&err, "Failed to initialize %d/%zu" + " reg 0x%x in grp_type = 0x%x (%d/%zu)", + j, ARRAY_SIZE(xen_pt_emu_reg_grps[i].emu_regs), + regs->offset, xen_pt_emu_reg_grps[i].grp_type, + i, ARRAY_SIZE(xen_pt_emu_reg_grps)); + error_propagate(errp, err); xen_pt_config_delete(s); - return rc; + return; } } } } } - - return 0; } /* delete all emulate register */ @@ -1916,11 +2072,9 @@ void xen_pt_config_delete(XenPCIPassthroughState *s) /* free MSI/MSI-X info table */ if (s->msix) { - xen_pt_msix_delete(s); - } - if (s->msi) { - g_free(s->msi); + xen_pt_msix_unmap(s); } + g_free(s->msi); /* free all register group entry */ QLIST_FOREACH_SAFE(reg_group, &s->reg_grps, entries, next_grp) { diff --git a/qemu/hw/xen/xen_pt_graphics.c b/qemu/hw/xen/xen_pt_graphics.c new file mode 100644 index 000000000..0f4c8d77e --- /dev/null +++ b/qemu/hw/xen/xen_pt_graphics.c @@ -0,0 +1,275 @@ +/* + * graphics passthrough + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "xen_pt.h" +#include "xen-host-pci-device.h" +#include "hw/xen/xen_backend.h" + +static unsigned long igd_guest_opregion; +static unsigned long igd_host_opregion; + +#define XEN_PCI_INTEL_OPREGION_MASK 0xfff + +typedef struct VGARegion { + int type; /* Memory or port I/O */ + uint64_t guest_base_addr; + uint64_t machine_base_addr; + uint64_t size; /* size of the region */ + int rc; +} VGARegion; + +#define IORESOURCE_IO 0x00000100 +#define IORESOURCE_MEM 0x00000200 + +static struct VGARegion vga_args[] = { + { + .type = IORESOURCE_IO, + .guest_base_addr = 0x3B0, + .machine_base_addr = 0x3B0, + .size = 0xC, + .rc = -1, + }, + { + .type = IORESOURCE_IO, + .guest_base_addr = 0x3C0, + .machine_base_addr = 0x3C0, + .size = 0x20, + .rc = -1, + }, + { + .type = IORESOURCE_MEM, + .guest_base_addr = 0xa0000 >> XC_PAGE_SHIFT, + .machine_base_addr = 0xa0000 >> XC_PAGE_SHIFT, + .size = 0x20, + .rc = -1, + }, +}; + +/* + * register VGA resources for the domain with assigned gfx + */ +int xen_pt_register_vga_regions(XenHostPCIDevice *dev) +{ + int i = 0; + + if (!is_igd_vga_passthrough(dev)) { + return 0; + } + + for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) { + if (vga_args[i].type == IORESOURCE_IO) { + vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid, + vga_args[i].guest_base_addr, + vga_args[i].machine_base_addr, + vga_args[i].size, DPCI_ADD_MAPPING); + } else { + vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid, + vga_args[i].guest_base_addr, + vga_args[i].machine_base_addr, + vga_args[i].size, DPCI_ADD_MAPPING); + } + + if (vga_args[i].rc) { + XEN_PT_ERR(NULL, "VGA %s mapping failed! (rc: %i)\n", + vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory", + vga_args[i].rc); + return vga_args[i].rc; + } + } + + return 0; +} + +/* + * unregister VGA resources for the domain with assigned gfx + */ +int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev) +{ + int i = 0; + int ret = 0; + + if (!is_igd_vga_passthrough(dev)) { + return 0; + } + + for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) { + if (vga_args[i].type == IORESOURCE_IO) { + vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid, + vga_args[i].guest_base_addr, + vga_args[i].machine_base_addr, + vga_args[i].size, DPCI_REMOVE_MAPPING); + } else { + vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid, + vga_args[i].guest_base_addr, + vga_args[i].machine_base_addr, + vga_args[i].size, DPCI_REMOVE_MAPPING); + } + + if (vga_args[i].rc) { + XEN_PT_ERR(NULL, "VGA %s unmapping failed! (rc: %i)\n", + vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory", + vga_args[i].rc); + return vga_args[i].rc; + } + } + + if (igd_guest_opregion) { + ret = xc_domain_memory_mapping(xen_xc, xen_domid, + (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT), + (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT), + 3, + DPCI_REMOVE_MAPPING); + if (ret) { + return ret; + } + } + + return 0; +} + +static void *get_vgabios(XenPCIPassthroughState *s, int *size, + XenHostPCIDevice *dev) +{ + return pci_assign_dev_load_option_rom(&s->dev, OBJECT(&s->dev), size, + dev->domain, dev->bus, + dev->dev, dev->func); +} + +/* Refer to Seabios. */ +struct rom_header { + uint16_t signature; + uint8_t size; + uint8_t initVector[4]; + uint8_t reserved[17]; + uint16_t pcioffset; + uint16_t pnpoffset; +} __attribute__((packed)); + +struct pci_data { + uint32_t signature; + uint16_t vendor; + uint16_t device; + uint16_t vitaldata; + uint16_t dlen; + uint8_t drevision; + uint8_t class_lo; + uint16_t class_hi; + uint16_t ilen; + uint16_t irevision; + uint8_t type; + uint8_t indicator; + uint16_t reserved; +} __attribute__((packed)); + +void xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev, + Error **errp) +{ + unsigned char *bios = NULL; + struct rom_header *rom; + int bios_size; + char *c = NULL; + char checksum = 0; + uint32_t len = 0; + struct pci_data *pd = NULL; + + if (!is_igd_vga_passthrough(dev)) { + error_setg(errp, "Need to enable igd-passthrough"); + return; + } + + bios = get_vgabios(s, &bios_size, dev); + if (!bios) { + error_setg(errp, "VGA: Can't get VBIOS"); + return; + } + + /* Currently we fixed this address as a primary. */ + rom = (struct rom_header *)bios; + pd = (void *)(bios + (unsigned char)rom->pcioffset); + + /* We may need to fixup Device Identification. */ + if (pd->device != s->real_device.device_id) { + pd->device = s->real_device.device_id; + + len = rom->size * 512; + /* Then adjust the bios checksum */ + for (c = (char *)bios; c < ((char *)bios + len); c++) { + checksum += *c; + } + if (checksum) { + bios[len - 1] -= checksum; + XEN_PT_LOG(&s->dev, "vga bios checksum is adjusted %x!\n", + checksum); + } + } + + /* Currently we fixed this address as a primary for legacy BIOS. */ + cpu_physical_memory_rw(0xc0000, bios, bios_size, 1); +} + +uint32_t igd_read_opregion(XenPCIPassthroughState *s) +{ + uint32_t val = 0; + + if (!igd_guest_opregion) { + return val; + } + + val = igd_guest_opregion; + + XEN_PT_LOG(&s->dev, "Read opregion val=%x\n", val); + return val; +} + +#define XEN_PCI_INTEL_OPREGION_PAGES 0x3 +#define XEN_PCI_INTEL_OPREGION_ENABLE_ACCESSED 0x1 +void igd_write_opregion(XenPCIPassthroughState *s, uint32_t val) +{ + int ret; + + if (igd_guest_opregion) { + XEN_PT_LOG(&s->dev, "opregion register already been set, ignoring %x\n", + val); + return; + } + + /* We just work with LE. */ + xen_host_pci_get_block(&s->real_device, XEN_PCI_INTEL_OPREGION, + (uint8_t *)&igd_host_opregion, 4); + igd_guest_opregion = (unsigned long)(val & ~XEN_PCI_INTEL_OPREGION_MASK) + | (igd_host_opregion & XEN_PCI_INTEL_OPREGION_MASK); + + ret = xc_domain_iomem_permission(xen_xc, xen_domid, + (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT), + XEN_PCI_INTEL_OPREGION_PAGES, + XEN_PCI_INTEL_OPREGION_ENABLE_ACCESSED); + + if (ret) { + XEN_PT_ERR(&s->dev, "[%d]:Can't enable to access IGD host opregion:" + " 0x%lx.\n", ret, + (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT)), + igd_guest_opregion = 0; + return; + } + + ret = xc_domain_memory_mapping(xen_xc, xen_domid, + (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT), + (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT), + XEN_PCI_INTEL_OPREGION_PAGES, + DPCI_ADD_MAPPING); + + if (ret) { + XEN_PT_ERR(&s->dev, "[%d]:Can't map IGD host opregion:0x%lx to" + " guest opregion:0x%lx.\n", ret, + (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT), + (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT)); + igd_guest_opregion = 0; + return; + } + + XEN_PT_LOG(&s->dev, "Map OpRegion: 0x%lx -> 0x%lx\n", + (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT), + (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT)); +} diff --git a/qemu/hw/xen/xen_pt_msi.c b/qemu/hw/xen/xen_pt_msi.c index 263e0514a..9a16f2bff 100644 --- a/qemu/hw/xen/xen_pt_msi.c +++ b/qemu/hw/xen/xen_pt_msi.c @@ -9,6 +9,7 @@ * This file implements direct PCI assignment to a HVM guest */ +#include "qemu/osdep.h" #include #include "hw/xen/xen_backend.h" @@ -25,6 +26,7 @@ #define XEN_PT_GFLAGSSHIFT_DELIV_MODE 12 #define XEN_PT_GFLAGSSHIFT_TRG_MODE 15 +#define latch(fld) latch[PCI_MSIX_ENTRY_##fld / sizeof(uint32_t)] /* * Helpers @@ -75,19 +77,29 @@ static int msi_msix_enable(XenPCIPassthroughState *s, bool enable) { uint16_t val = 0; + int rc; if (!address) { return -1; } - xen_host_pci_get_word(&s->real_device, address, &val); + rc = xen_host_pci_get_word(&s->real_device, address, &val); + if (rc) { + XEN_PT_ERR(&s->dev, "Failed to read MSI/MSI-X register (0x%x), rc:%d\n", + address, rc); + return rc; + } if (enable) { val |= flag; } else { val &= ~flag; } - xen_host_pci_set_word(&s->real_device, address, val); - return 0; + rc = xen_host_pci_set_word(&s->real_device, address, val); + if (rc) { + XEN_PT_ERR(&s->dev, "Failed to write MSI/MSI-X register (0x%x), rc:%d\n", + address, rc); + } + return rc; } static int msi_msix_setup(XenPCIPassthroughState *s, @@ -103,9 +115,7 @@ static int msi_msix_setup(XenPCIPassthroughState *s, assert((!is_msix && msix_entry == 0) || is_msix); - if (gvec == 0) { - /* if gvec is 0, the guest is asking for a particular pirq that - * is passed as dest_id */ + if (xen_is_pirq_msi(data)) { *ppirq = msi_ext_dest_id(addr >> 32) | msi_dest_id(addr); if (!*ppirq) { /* this probably identifies an misconfiguration of the guest, @@ -220,7 +230,7 @@ static int msi_msix_disable(XenPCIPassthroughState *s, * MSI virtualization functions */ -int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool enable) +static int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool enable) { XEN_PT_LOG(&s->dev, "%s MSI.\n", enable ? "enabling" : "disabling"); @@ -276,7 +286,7 @@ void xen_pt_msi_disable(XenPCIPassthroughState *s) return; } - xen_pt_msi_set_enable(s, false); + (void)xen_pt_msi_set_enable(s, false); msi_msix_disable(s, msi_addr64(msi), msi->data, msi->pirq, false, msi->initialized); @@ -304,7 +314,8 @@ static int msix_set_enable(XenPCIPassthroughState *s, bool enabled) enabled); } -static int xen_pt_msix_update_one(XenPCIPassthroughState *s, int entry_nr) +static int xen_pt_msix_update_one(XenPCIPassthroughState *s, int entry_nr, + uint32_t vec_ctrl) { XenPTMSIXEntry *entry = NULL; int pirq; @@ -322,6 +333,19 @@ static int xen_pt_msix_update_one(XenPCIPassthroughState *s, int entry_nr) pirq = entry->pirq; + /* + * Update the entry addr and data to the latest values only when the + * entry is masked or they are all masked, as required by the spec. + * Addr and data changes while the MSI-X entry is unmasked get deferred + * until the next masked -> unmasked transition. + */ + if (pirq == XEN_PT_UNASSIGNED_PIRQ || s->msix->maskall || + (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)) { + entry->addr = entry->latch(LOWER_ADDR) | + ((uint64_t)entry->latch(UPPER_ADDR) << 32); + entry->data = entry->latch(DATA); + } + rc = msi_msix_setup(s, entry->addr, entry->data, &pirq, true, entry_nr, entry->pirq == XEN_PT_UNASSIGNED_PIRQ); if (rc) { @@ -347,7 +371,7 @@ int xen_pt_msix_update(XenPCIPassthroughState *s) int i; for (i = 0; i < msix->total_entries; i++) { - xen_pt_msix_update_one(s, i); + xen_pt_msix_update_one(s, i, msix->msix_entry[i].latch(VECTOR_CTRL)); } return 0; @@ -396,36 +420,14 @@ int xen_pt_msix_update_remap(XenPCIPassthroughState *s, int bar_index) static uint32_t get_entry_value(XenPTMSIXEntry *e, int offset) { - switch (offset) { - case PCI_MSIX_ENTRY_LOWER_ADDR: - return e->addr & UINT32_MAX; - case PCI_MSIX_ENTRY_UPPER_ADDR: - return e->addr >> 32; - case PCI_MSIX_ENTRY_DATA: - return e->data; - case PCI_MSIX_ENTRY_VECTOR_CTRL: - return e->vector_ctrl; - default: - return 0; - } + assert(!(offset % sizeof(*e->latch))); + return e->latch[offset / sizeof(*e->latch)]; } static void set_entry_value(XenPTMSIXEntry *e, int offset, uint32_t val) { - switch (offset) { - case PCI_MSIX_ENTRY_LOWER_ADDR: - e->addr = (e->addr & ((uint64_t)UINT32_MAX << 32)) | val; - break; - case PCI_MSIX_ENTRY_UPPER_ADDR: - e->addr = (uint64_t)val << 32 | (e->addr & UINT32_MAX); - break; - case PCI_MSIX_ENTRY_DATA: - e->data = val; - break; - case PCI_MSIX_ENTRY_VECTOR_CTRL: - e->vector_ctrl = val; - break; - } + assert(!(offset % sizeof(*e->latch))); + e->latch[offset / sizeof(*e->latch)] = val; } static void pci_msix_write(void *opaque, hwaddr addr, @@ -444,39 +446,26 @@ static void pci_msix_write(void *opaque, hwaddr addr, offset = addr % PCI_MSIX_ENTRY_SIZE; if (offset != PCI_MSIX_ENTRY_VECTOR_CTRL) { - const volatile uint32_t *vec_ctrl; - if (get_entry_value(entry, offset) == val && entry->pirq != XEN_PT_UNASSIGNED_PIRQ) { return; } + entry->updated = true; + } else if (msix->enabled && entry->updated && + !(val & PCI_MSIX_ENTRY_CTRL_MASKBIT)) { + const volatile uint32_t *vec_ctrl; + /* * If Xen intercepts the mask bit access, entry->vec_ctrl may not be * up-to-date. Read from hardware directly. */ vec_ctrl = s->msix->phys_iomem_base + entry_nr * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL; - - if (msix->enabled && !(*vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)) { - if (!entry->warned) { - entry->warned = true; - XEN_PT_ERR(&s->dev, "Can't update msix entry %d since MSI-X is" - " already enabled.\n", entry_nr); - } - return; - } - - entry->updated = true; + xen_pt_msix_update_one(s, entry_nr, *vec_ctrl); } set_entry_value(entry, offset, val); - - if (offset == PCI_MSIX_ENTRY_VECTOR_CTRL) { - if (msix->enabled && !(val & PCI_MSIX_ENTRY_CTRL_MASKBIT)) { - xen_pt_msix_update_one(s, entry_nr); - } - } } static uint64_t pci_msix_read(void *opaque, hwaddr addr, @@ -502,6 +491,12 @@ static uint64_t pci_msix_read(void *opaque, hwaddr addr, } } +static bool pci_msix_accepts(void *opaque, hwaddr addr, + unsigned size, bool is_write) +{ + return !(addr & (size - 1)); +} + static const MemoryRegionOps pci_msix_ops = { .read = pci_msix_read, .write = pci_msix_write, @@ -510,7 +505,13 @@ static const MemoryRegionOps pci_msix_ops = { .min_access_size = 4, .max_access_size = 4, .unaligned = false, + .accepts = pci_msix_accepts }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false + } }; int xen_pt_msix_init(XenPCIPassthroughState *s, uint32_t base) @@ -600,7 +601,7 @@ error_out: return rc; } -void xen_pt_msix_delete(XenPCIPassthroughState *s) +void xen_pt_msix_unmap(XenPCIPassthroughState *s) { XenPTMSIX *msix = s->msix; @@ -617,6 +618,17 @@ void xen_pt_msix_delete(XenPCIPassthroughState *s) } memory_region_del_subregion(&s->bar[msix->bar_index], &msix->mmio); +} + +void xen_pt_msix_delete(XenPCIPassthroughState *s) +{ + XenPTMSIX *msix = s->msix; + + if (!msix) { + return; + } + + object_unparent(OBJECT(&msix->mmio)); g_free(s->msix); s->msix = NULL; diff --git a/qemu/hw/xenpv/Makefile.objs b/qemu/hw/xenpv/Makefile.objs index 49f6e9e3c..bbf5873fd 100644 --- a/qemu/hw/xenpv/Makefile.objs +++ b/qemu/hw/xenpv/Makefile.objs @@ -1,2 +1,4 @@ # Xen PV machine support -obj-$(CONFIG_XEN) += xen_domainbuild.o xen_machine_pv.o +obj-$(CONFIG_XEN) += xen_machine_pv.o +# Xen PV machine builder support +obj-$(CONFIG_XEN_PV_DOMAIN_BUILD) += xen_domainbuild.o diff --git a/qemu/hw/xenpv/xen_domainbuild.c b/qemu/hw/xenpv/xen_domainbuild.c index c0ab7537d..5a9f5ac80 100644 --- a/qemu/hw/xenpv/xen_domainbuild.c +++ b/qemu/hw/xenpv/xen_domainbuild.c @@ -1,4 +1,4 @@ -#include +#include "qemu/osdep.h" #include "hw/xen/xen_backend.h" #include "xen_domainbuild.h" #include "qemu/timer.h" @@ -174,12 +174,15 @@ static int xen_domain_watcher(void) for (i = 3; i < n; i++) { if (i == fd[0]) continue; - if (i == xc_fd(xen_xc)) { - continue; - } close(i); } + /* + * Reopen xc interface, since the original is unsafe after fork + * and was closed above. + */ + xen_xc = xc_interface_open(0, 0, 0); + /* ignore term signals */ signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); @@ -234,7 +237,7 @@ int xen_domain_build_pv(const char *kernel, const char *ramdisk, int rc; memcpy(uuid, qemu_uuid, sizeof(uuid)); - rc = xc_domain_create(xen_xc, ssidref, uuid, flags, &xen_domid); + rc = xen_domain_create(xen_xc, ssidref, uuid, flags, &xen_domid); if (rc < 0) { fprintf(stderr, "xen: xc_domain_create() failed\n"); goto err; diff --git a/qemu/hw/xenpv/xen_machine_pv.c b/qemu/hw/xenpv/xen_machine_pv.c index 2e545d241..fc1353599 100644 --- a/qemu/hw/xenpv/xen_machine_pv.c +++ b/qemu/hw/xenpv/xen_machine_pv.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/boards.h" #include "hw/xen/xen_backend.h" @@ -30,9 +31,6 @@ static void xen_init_pv(MachineState *machine) { - const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; - const char *initrd_filename = machine->initrd_filename; DriveInfo *dinfo; int i; @@ -46,17 +44,27 @@ static void xen_init_pv(MachineState *machine) case XEN_ATTACH: /* nothing to do, xend handles everything */ break; - case XEN_CREATE: +#ifdef CONFIG_XEN_PV_DOMAIN_BUILD + case XEN_CREATE: { + const char *kernel_filename = machine->kernel_filename; + const char *kernel_cmdline = machine->kernel_cmdline; + const char *initrd_filename = machine->initrd_filename; if (xen_domain_build_pv(kernel_filename, initrd_filename, kernel_cmdline) < 0) { fprintf(stderr, "xen pv domain creation failed\n"); exit(1); } break; + } +#endif case XEN_EMULATE: fprintf(stderr, "xen emulation not implemented (yet)\n"); exit(1); break; + default: + fprintf(stderr, "unhandled xen_mode %d\n", xen_mode); + exit(1); + break; } xen_be_register("console", &xen_console_ops); @@ -93,17 +101,12 @@ static void xen_init_pv(MachineState *machine) xen_init_display(xen_domid); } -static QEMUMachine xenpv_machine = { - .name = "xenpv", - .desc = "Xen Para-virtualized PC", - .init = xen_init_pv, - .max_cpus = 1, - .default_machine_opts = "accel=xen", -}; - -static void xenpv_machine_init(void) +static void xenpv_machine_init(MachineClass *mc) { - qemu_register_machine(&xenpv_machine); + mc->desc = "Xen Para-virtualized PC"; + mc->init = xen_init_pv; + mc->max_cpus = 1; + mc->default_machine_opts = "accel=xen"; } -machine_init(xenpv_machine_init); +DEFINE_MACHINE("xenpv", xenpv_machine_init) diff --git a/qemu/hw/xtensa/pic_cpu.c b/qemu/hw/xtensa/pic_cpu.c index 18825d19f..c835bd009 100644 --- a/qemu/hw/xtensa/pic_cpu.c +++ b/qemu/hw/xtensa/pic_cpu.c @@ -25,6 +25,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "qemu/log.h" #include "qemu/timer.h" diff --git a/qemu/hw/xtensa/sim.c b/qemu/hw/xtensa/sim.c index 328d20975..5e9400426 100644 --- a/qemu/hw/xtensa/sim.c +++ b/qemu/hw/xtensa/sim.c @@ -25,6 +25,10 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "sysemu/sysemu.h" #include "hw/boards.h" #include "hw/loader.h" @@ -79,12 +83,12 @@ static void xtensa_sim_init(MachineState *machine) } ram = g_malloc(sizeof(*ram)); - memory_region_init_ram(ram, NULL, "xtensa.sram", ram_size, &error_abort); + memory_region_init_ram(ram, NULL, "xtensa.sram", ram_size, &error_fatal); vmstate_register_ram_global(ram); memory_region_add_subregion(get_system_memory(), 0, ram); rom = g_malloc(sizeof(*rom)); - memory_region_init_ram(rom, NULL, "xtensa.rom", 0x1000, &error_abort); + memory_region_init_ram(rom, NULL, "xtensa.rom", 0x1000, &error_fatal); vmstate_register_ram_global(rom); memory_region_add_subregion(get_system_memory(), 0xfe000000, rom); @@ -93,10 +97,10 @@ static void xtensa_sim_init(MachineState *machine) uint64_t elf_lowaddr; #ifdef TARGET_WORDS_BIGENDIAN int success = load_elf(kernel_filename, translate_phys_addr, cpu, - &elf_entry, &elf_lowaddr, NULL, 1, ELF_MACHINE, 0); + &elf_entry, &elf_lowaddr, NULL, 1, EM_XTENSA, 0, 0); #else int success = load_elf(kernel_filename, translate_phys_addr, cpu, - &elf_entry, &elf_lowaddr, NULL, 0, ELF_MACHINE, 0); + &elf_entry, &elf_lowaddr, NULL, 0, EM_XTENSA, 0, 0); #endif if (success > 0) { env->pc = elf_entry; @@ -104,17 +108,12 @@ static void xtensa_sim_init(MachineState *machine) } } -static QEMUMachine xtensa_sim_machine = { - .name = "sim", - .desc = "sim machine (" XTENSA_DEFAULT_CPU_MODEL ")", - .is_default = true, - .init = xtensa_sim_init, - .max_cpus = 4, -}; - -static void xtensa_sim_machine_init(void) +static void xtensa_sim_machine_init(MachineClass *mc) { - qemu_register_machine(&xtensa_sim_machine); + mc->desc = "sim machine (" XTENSA_DEFAULT_CPU_MODEL ")"; + mc->is_default = true; + mc->init = xtensa_sim_init; + mc->max_cpus = 4; } -machine_init(xtensa_sim_machine_init); +DEFINE_MACHINE("sim", xtensa_sim_machine_init) diff --git a/qemu/hw/xtensa/xtfpga.c b/qemu/hw/xtensa/xtfpga.c index ab4d0e412..2d117369a 100644 --- a/qemu/hw/xtensa/xtfpga.c +++ b/qemu/hw/xtensa/xtfpga.c @@ -25,6 +25,10 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "sysemu/sysemu.h" #include "hw/boards.h" #include "hw/loader.h" @@ -143,11 +147,34 @@ static void lx60_net_init(MemoryRegion *address_space, sysbus_mmio_get_region(s, 1)); ram = g_malloc(sizeof(*ram)); - memory_region_init_ram(ram, OBJECT(s), "open_eth.ram", 16384, &error_abort); + memory_region_init_ram(ram, OBJECT(s), "open_eth.ram", 16384, + &error_fatal); vmstate_register_ram_global(ram); memory_region_add_subregion(address_space, buffers, ram); } +static pflash_t *xtfpga_flash_init(MemoryRegion *address_space, + const LxBoardDesc *board, + DriveInfo *dinfo, int be) +{ + SysBusDevice *s; + DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); + + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo), + &error_abort); + qdev_prop_set_uint32(dev, "num-blocks", + board->flash_size / board->flash_sector_size); + qdev_prop_set_uint64(dev, "sector-length", board->flash_sector_size); + qdev_prop_set_uint8(dev, "width", 4); + qdev_prop_set_bit(dev, "big-endian", be); + qdev_prop_set_string(dev, "name", "lx60.io.flash"); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + memory_region_add_subregion(address_space, board->flash_base, + sysbus_mmio_get_region(s, 0)); + return OBJECT_CHECK(pflash_t, (dev), "cfi.pflash01"); +} + static uint64_t translate_phys_addr(void *opaque, uint64_t addr) { XtensaCPU *cpu = opaque; @@ -223,7 +250,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) ram = g_malloc(sizeof(*ram)); memory_region_init_ram(ram, NULL, "lx60.dram", machine->ram_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(ram); memory_region_add_subregion(system_memory, 0, ram); @@ -246,16 +273,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) dinfo = drive_get(IF_PFLASH, 0, 0); if (dinfo) { - flash = pflash_cfi01_register(board->flash_base, - NULL, "lx60.io.flash", board->flash_size, - blk_by_legacy_dinfo(dinfo), - board->flash_sector_size, - board->flash_size / board->flash_sector_size, - 4, 0x0000, 0x0000, 0x0000, 0x0000, be); - if (flash == NULL) { - error_report("unable to mount pflash"); - exit(EXIT_FAILURE); - } + flash = xtfpga_flash_init(system_io, board, dinfo, be); } /* Use presence of kernel file name as 'boot from SRAM' switch. */ @@ -275,7 +293,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) rom = g_malloc(sizeof(*rom)); memory_region_init_ram(rom, NULL, "lx60.sram", board->sram_size, - &error_abort); + &error_fatal); vmstate_register_ram_global(rom); memory_region_add_subregion(system_memory, 0xfe000000, rom); @@ -340,7 +358,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) uint64_t elf_entry; uint64_t elf_lowaddr; int success = load_elf(kernel_filename, translate_phys_addr, cpu, - &elf_entry, &elf_lowaddr, NULL, be, ELF_MACHINE, 0); + &elf_entry, &elf_lowaddr, NULL, be, EM_XTENSA, 0, 0); if (success > 0) { entry_point = elf_entry; } else { @@ -385,7 +403,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) static void xtensa_lx60_init(MachineState *machine) { static const LxBoardDesc lx60_board = { - .flash_base = 0xf8000000, + .flash_base = 0x08000000, .flash_size = 0x00400000, .flash_sector_size = 0x10000, .sram_size = 0x20000, @@ -396,7 +414,7 @@ static void xtensa_lx60_init(MachineState *machine) static void xtensa_lx200_init(MachineState *machine) { static const LxBoardDesc lx200_board = { - .flash_base = 0xf8000000, + .flash_base = 0x08000000, .flash_size = 0x01000000, .flash_sector_size = 0x20000, .sram_size = 0x2000000, @@ -407,7 +425,7 @@ static void xtensa_lx200_init(MachineState *machine) static void xtensa_ml605_init(MachineState *machine) { static const LxBoardDesc ml605_board = { - .flash_base = 0xf8000000, + .flash_base = 0x08000000, .flash_size = 0x01000000, .flash_sector_size = 0x20000, .sram_size = 0x2000000, @@ -418,7 +436,7 @@ static void xtensa_ml605_init(MachineState *machine) static void xtensa_kc705_init(MachineState *machine) { static const LxBoardDesc kc705_board = { - .flash_base = 0xf0000000, + .flash_base = 0x00000000, .flash_size = 0x08000000, .flash_boot_base = 0x06000000, .flash_sector_size = 0x20000, @@ -427,40 +445,72 @@ static void xtensa_kc705_init(MachineState *machine) lx_init(&kc705_board, machine); } -static QEMUMachine xtensa_lx60_machine = { - .name = "lx60", - .desc = "lx60 EVB (" XTENSA_DEFAULT_CPU_MODEL ")", - .init = xtensa_lx60_init, - .max_cpus = 4, +static void xtensa_lx60_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "lx60 EVB (" XTENSA_DEFAULT_CPU_MODEL ")"; + mc->init = xtensa_lx60_init; + mc->max_cpus = 4; +} + +static const TypeInfo xtensa_lx60_type = { + .name = MACHINE_TYPE_NAME("lx60"), + .parent = TYPE_MACHINE, + .class_init = xtensa_lx60_class_init, }; -static QEMUMachine xtensa_lx200_machine = { - .name = "lx200", - .desc = "lx200 EVB (" XTENSA_DEFAULT_CPU_MODEL ")", - .init = xtensa_lx200_init, - .max_cpus = 4, +static void xtensa_lx200_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "lx200 EVB (" XTENSA_DEFAULT_CPU_MODEL ")"; + mc->init = xtensa_lx200_init; + mc->max_cpus = 4; +} + +static const TypeInfo xtensa_lx200_type = { + .name = MACHINE_TYPE_NAME("lx200"), + .parent = TYPE_MACHINE, + .class_init = xtensa_lx200_class_init, }; -static QEMUMachine xtensa_ml605_machine = { - .name = "ml605", - .desc = "ml605 EVB (" XTENSA_DEFAULT_CPU_MODEL ")", - .init = xtensa_ml605_init, - .max_cpus = 4, +static void xtensa_ml605_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "ml605 EVB (" XTENSA_DEFAULT_CPU_MODEL ")"; + mc->init = xtensa_ml605_init; + mc->max_cpus = 4; +} + +static const TypeInfo xtensa_ml605_type = { + .name = MACHINE_TYPE_NAME("ml605"), + .parent = TYPE_MACHINE, + .class_init = xtensa_ml605_class_init, }; -static QEMUMachine xtensa_kc705_machine = { - .name = "kc705", - .desc = "kc705 EVB (" XTENSA_DEFAULT_CPU_MODEL ")", - .init = xtensa_kc705_init, - .max_cpus = 4, +static void xtensa_kc705_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "kc705 EVB (" XTENSA_DEFAULT_CPU_MODEL ")"; + mc->init = xtensa_kc705_init; + mc->max_cpus = 4; +} + +static const TypeInfo xtensa_kc705_type = { + .name = MACHINE_TYPE_NAME("kc705"), + .parent = TYPE_MACHINE, + .class_init = xtensa_kc705_class_init, }; static void xtensa_lx_machines_init(void) { - qemu_register_machine(&xtensa_lx60_machine); - qemu_register_machine(&xtensa_lx200_machine); - qemu_register_machine(&xtensa_ml605_machine); - qemu_register_machine(&xtensa_kc705_machine); + type_register_static(&xtensa_lx60_type); + type_register_static(&xtensa_lx200_type); + type_register_static(&xtensa_ml605_type); + type_register_static(&xtensa_kc705_type); } -machine_init(xtensa_lx_machines_init); +type_init(xtensa_lx_machines_init) diff --git a/qemu/include/block/accounting.h b/qemu/include/block/accounting.h index 4c406cff7..20891639d 100644 --- a/qemu/include/block/accounting.h +++ b/qemu/include/block/accounting.h @@ -2,6 +2,7 @@ * QEMU System Emulator block accounting * * Copyright (c) 2011 Christoph Hellwig + * Copyright (c) 2015 Igalia, S.L. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,9 +25,9 @@ #ifndef BLOCK_ACCOUNTING_H #define BLOCK_ACCOUNTING_H -#include +#include "qemu/timed-average.h" -#include "qemu/typedefs.h" +typedef struct BlockAcctTimedStats BlockAcctTimedStats; enum BlockAcctType { BLOCK_ACCT_READ, @@ -35,12 +36,23 @@ enum BlockAcctType { BLOCK_MAX_IOTYPE, }; +struct BlockAcctTimedStats { + TimedAverage latency[BLOCK_MAX_IOTYPE]; + unsigned interval_length; /* in seconds */ + QSLIST_ENTRY(BlockAcctTimedStats) entries; +}; + typedef struct BlockAcctStats { uint64_t nr_bytes[BLOCK_MAX_IOTYPE]; uint64_t nr_ops[BLOCK_MAX_IOTYPE]; + uint64_t invalid_ops[BLOCK_MAX_IOTYPE]; + uint64_t failed_ops[BLOCK_MAX_IOTYPE]; uint64_t total_time_ns[BLOCK_MAX_IOTYPE]; uint64_t merged[BLOCK_MAX_IOTYPE]; - uint64_t wr_highest_sector; + int64_t last_access_time_ns; + QSLIST_HEAD(, BlockAcctTimedStats) intervals; + bool account_invalid; + bool account_failed; } BlockAcctStats; typedef struct BlockAcctCookie { @@ -49,12 +61,21 @@ typedef struct BlockAcctCookie { enum BlockAcctType type; } BlockAcctCookie; +void block_acct_init(BlockAcctStats *stats, bool account_invalid, + bool account_failed); +void block_acct_cleanup(BlockAcctStats *stats); +void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length); +BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats, + BlockAcctTimedStats *s); void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie, int64_t bytes, enum BlockAcctType type); void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie); -void block_acct_highest_sector(BlockAcctStats *stats, int64_t sector_num, - unsigned int nb_sectors); +void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie); +void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type); void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type, int num_requests); +int64_t block_acct_idle_time_ns(BlockAcctStats *stats); +double block_acct_queue_depth(BlockAcctTimedStats *stats, + enum BlockAcctType type); #endif diff --git a/qemu/include/block/aio.h b/qemu/include/block/aio.h index 400b1b002..88a64eeb3 100644 --- a/qemu/include/block/aio.h +++ b/qemu/include/block/aio.h @@ -14,7 +14,6 @@ #ifndef QEMU_AIO_H #define QEMU_AIO_H -#include "qemu/typedefs.h" #include "qemu-common.h" #include "qemu/queue.h" #include "qemu/event_notifier.h" @@ -122,6 +121,13 @@ struct AioContext { /* TimerLists for calling timers - one per clock type */ QEMUTimerListGroup tlg; + + int external_disable_cnt; + + /* epoll(7) state used when built with CONFIG_EPOLL */ + int epollfd; + bool epoll_enabled; + bool epoll_available; }; /** @@ -206,6 +212,11 @@ void aio_notify(AioContext *ctx); */ void aio_notify_accept(AioContext *ctx); +/** + * aio_bh_call: Executes callback function of the specified BH. + */ +void aio_bh_call(QEMUBH *bh); + /** * aio_bh_poll: Poll bottom halves for an AioContext. * @@ -299,6 +310,7 @@ bool aio_poll(AioContext *ctx, bool blocking); */ void aio_set_fd_handler(AioContext *ctx, int fd, + bool is_external, IOHandler *io_read, IOHandler *io_write, void *opaque); @@ -312,6 +324,7 @@ void aio_set_fd_handler(AioContext *ctx, */ void aio_set_event_notifier(AioContext *ctx, EventNotifier *notifier, + bool is_external, EventNotifierHandler *io_read); /* Return a GSource that lets the main loop poll the file descriptors attached @@ -373,4 +386,59 @@ static inline void aio_timer_init(AioContext *ctx, */ int64_t aio_compute_timeout(AioContext *ctx); +/** + * aio_disable_external: + * @ctx: the aio context + * + * Disable the further processing of external clients. + */ +static inline void aio_disable_external(AioContext *ctx) +{ + atomic_inc(&ctx->external_disable_cnt); +} + +/** + * aio_enable_external: + * @ctx: the aio context + * + * Enable the processing of external clients. + */ +static inline void aio_enable_external(AioContext *ctx) +{ + assert(ctx->external_disable_cnt > 0); + atomic_dec(&ctx->external_disable_cnt); +} + +/** + * aio_external_disabled: + * @ctx: the aio context + * + * Return true if the external clients are disabled. + */ +static inline bool aio_external_disabled(AioContext *ctx) +{ + return atomic_read(&ctx->external_disable_cnt); +} + +/** + * aio_node_check: + * @ctx: the aio context + * @is_external: Whether or not the checked node is an external event source. + * + * Check if the node's is_external flag is okay to be polled by the ctx at this + * moment. True means green light. + */ +static inline bool aio_node_check(AioContext *ctx, bool is_external) +{ + return !is_external || !atomic_read(&ctx->external_disable_cnt); +} + +/** + * aio_context_setup: + * @ctx: the aio context + * + * Initialize the aio context. + */ +void aio_context_setup(AioContext *ctx, Error **errp); + #endif diff --git a/qemu/include/block/block.h b/qemu/include/block/block.h index 37916f720..3a731377d 100644 --- a/qemu/include/block/block.h +++ b/qemu/include/block/block.h @@ -2,18 +2,21 @@ #define BLOCK_H #include "block/aio.h" -#include "qemu-common.h" +#include "qemu/iov.h" #include "qemu/option.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "block/accounting.h" +#include "block/dirty-bitmap.h" #include "qapi/qmp/qobject.h" #include "qapi-types.h" +#include "qemu/hbitmap.h" /* block.c */ typedef struct BlockDriver BlockDriver; typedef struct BlockJob BlockJob; typedef struct BdrvChild BdrvChild; typedef struct BdrvChildRole BdrvChildRole; +typedef struct BlockJobTxn BlockJobTxn; typedef struct BlockDriverInfo { /* in bytes, 0 if irrelevant */ @@ -23,7 +26,7 @@ typedef struct BlockDriverInfo { bool is_dirty; /* * True if unallocated blocks read back as zeroes. This is equivalent - * to the the LBPRZ flag in the SCSI logical block provisioning page. + * to the LBPRZ flag in the SCSI logical block provisioning page. */ bool unallocated_blocks_are_zero; /* @@ -51,15 +54,17 @@ typedef struct BlockFragInfo { } BlockFragInfo; typedef enum { - BDRV_REQ_COPY_ON_READ = 0x1, - BDRV_REQ_ZERO_WRITE = 0x2, + BDRV_REQ_COPY_ON_READ = 0x1, + BDRV_REQ_ZERO_WRITE = 0x2, /* The BDRV_REQ_MAY_UNMAP flag is used to indicate that the block driver * is allowed to optimize a write zeroes request by unmapping (discarding) * blocks if it is guaranteed that the result will read back as * zeroes. The flag is only passed to the driver if the block device is * opened with BDRV_O_UNMAP. */ - BDRV_REQ_MAY_UNMAP = 0x4, + BDRV_REQ_MAY_UNMAP = 0x4, + BDRV_REQ_NO_SERIALISING = 0x8, + BDRV_REQ_FUA = 0x10, } BdrvRequestFlags; typedef struct BlockSizes { @@ -77,20 +82,20 @@ typedef struct HDGeometry { #define BDRV_O_SNAPSHOT 0x0008 /* open the file read only and save writes in a snapshot */ #define BDRV_O_TEMPORARY 0x0010 /* delete the file after use */ #define BDRV_O_NOCACHE 0x0020 /* do not use the host page cache */ -#define BDRV_O_CACHE_WB 0x0040 /* use write-back caching */ #define BDRV_O_NATIVE_AIO 0x0080 /* use native AIO instead of the thread pool */ #define BDRV_O_NO_BACKING 0x0100 /* don't open the backing file */ #define BDRV_O_NO_FLUSH 0x0200 /* disable flushing on this disk */ #define BDRV_O_COPY_ON_READ 0x0400 /* copy read backing sectors into image */ -#define BDRV_O_INCOMING 0x0800 /* consistency hint for incoming migration */ +#define BDRV_O_INACTIVE 0x0800 /* consistency hint for migration handoff */ #define BDRV_O_CHECK 0x1000 /* open solely for consistency check */ #define BDRV_O_ALLOW_RDWR 0x2000 /* allow reopen to change from r/o to r/w */ #define BDRV_O_UNMAP 0x4000 /* execute guest UNMAP/TRIM operations */ #define BDRV_O_PROTOCOL 0x8000 /* if no block driver is explicitly given: select an appropriate protocol driver, ignoring the format layer */ +#define BDRV_O_NO_IO 0x10000 /* don't initialize for I/O */ -#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH) +#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_NO_FLUSH) /* Option names of options parsed by the block layer */ @@ -109,9 +114,10 @@ typedef struct HDGeometry { /* * Allocation status flags - * BDRV_BLOCK_DATA: data is read from bs->file or another file + * BDRV_BLOCK_DATA: data is read from a file returned by bdrv_get_block_status. * BDRV_BLOCK_ZERO: sectors read as zero - * BDRV_BLOCK_OFFSET_VALID: sector stored in bs->file as raw data + * BDRV_BLOCK_OFFSET_VALID: sector stored as raw data in a file returned by + * bdrv_get_block_status. * BDRV_BLOCK_ALLOCATED: the content of the block is determined by this * layer (as opposed to the backing file) * BDRV_BLOCK_RAW: used internally to indicate that the request @@ -147,6 +153,8 @@ typedef QSIMPLEQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue; typedef struct BDRVReopenState { BlockDriverState *bs; int flags; + QDict *options; + QDict *explicit_options; void *opaque; } BDRVReopenState; @@ -165,18 +173,14 @@ typedef enum BlockOpType { BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, - BLOCK_OP_TYPE_MIRROR, + BLOCK_OP_TYPE_MIRROR_SOURCE, + BLOCK_OP_TYPE_MIRROR_TARGET, BLOCK_OP_TYPE_RESIZE, BLOCK_OP_TYPE_STREAM, BLOCK_OP_TYPE_REPLACE, BLOCK_OP_TYPE_MAX, } BlockOpType; -void bdrv_iostatus_enable(BlockDriverState *bs); -void bdrv_iostatus_reset(BlockDriverState *bs); -void bdrv_iostatus_disable(BlockDriverState *bs); -bool bdrv_iostatus_is_enabled(const BlockDriverState *bs); -void bdrv_iostatus_set_err(BlockDriverState *bs, int error); void bdrv_info_print(Monitor *mon, const QObject *data); void bdrv_info(Monitor *mon, QObject **ret_data); void bdrv_stats_print(Monitor *mon, const QObject *data); @@ -189,51 +193,43 @@ void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group); void bdrv_init(void); void bdrv_init_with_whitelist(void); +bool bdrv_uses_whitelist(void); BlockDriver *bdrv_find_protocol(const char *filename, bool allow_protocol_prefix, Error **errp); BlockDriver *bdrv_find_format(const char *format_name); -BlockDriver *bdrv_find_whitelisted_format(const char *format_name, - bool readonly); int bdrv_create(BlockDriver *drv, const char* filename, QemuOpts *opts, Error **errp); int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp); BlockDriverState *bdrv_new_root(void); BlockDriverState *bdrv_new(void); -void bdrv_make_anon(BlockDriverState *bs); -void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old); void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top); -int bdrv_parse_cache_flags(const char *mode, int *flags); +void bdrv_replace_in_backing_chain(BlockDriverState *old, + BlockDriverState *new); + +int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough); int bdrv_parse_discard_flags(const char *mode, int *flags); -int bdrv_open_image(BlockDriverState **pbs, const char *filename, - QDict *options, const char *bdref_key, - BlockDriverState* parent, const BdrvChildRole *child_role, - bool allow_none, Error **errp); BdrvChild *bdrv_open_child(const char *filename, QDict *options, const char *bdref_key, BlockDriverState* parent, const BdrvChildRole *child_role, bool allow_none, Error **errp); void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd); -int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp); -int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp); +int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, + const char *bdref_key, Error **errp); int bdrv_open(BlockDriverState **pbs, const char *filename, - const char *reference, QDict *options, int flags, - BlockDriver *drv, Error **errp); + const char *reference, QDict *options, int flags, Error **errp); BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, - BlockDriverState *bs, int flags); + BlockDriverState *bs, + QDict *options, int flags); int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp); int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp); int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, Error **errp); void bdrv_reopen_commit(BDRVReopenState *reopen_state); void bdrv_reopen_abort(BDRVReopenState *reopen_state); -void bdrv_close(BlockDriverState *bs); -void bdrv_add_close_notifier(BlockDriverState *bs, Notifier *notify); int bdrv_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors); -int bdrv_read_unthrottled(BlockDriverState *bs, int64_t sector_num, - uint8_t *buf, int nb_sectors); int bdrv_write(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors); int bdrv_write_zeroes(BlockDriverState *bs, int64_t sector_num, @@ -253,6 +249,8 @@ int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); int coroutine_fn bdrv_co_copy_on_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); +int coroutine_fn bdrv_co_readv_no_serialising(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); /* @@ -274,7 +272,6 @@ int64_t bdrv_get_allocated_file_size(BlockDriverState *bs); void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr); void bdrv_refresh_limits(BlockDriverState *bs, Error **errp); int bdrv_commit(BlockDriverState *bs); -int bdrv_commit_all(void); int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, const char *backing_fmt); void bdrv_register(BlockDriver *bdrv); @@ -307,9 +304,9 @@ int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix); * block driver; total_work_size may change during the course of the amendment * operation */ typedef void BlockDriverAmendStatusCB(BlockDriverState *bs, int64_t offset, - int64_t total_work_size); + int64_t total_work_size, void *opaque); int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts, - BlockDriverAmendStatusCB *status_cb); + BlockDriverAmendStatusCB *status_cb, void *cb_opaque); /* external snapshots */ bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs, @@ -317,11 +314,10 @@ bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs, bool bdrv_is_first_non_filter(BlockDriverState *candidate); /* check if a named node can be replaced when doing drive-mirror */ -BlockDriverState *check_to_replace_node(const char *node_name, Error **errp); +BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs, + const char *node_name, Error **errp); /* async block I/O */ -typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector, - int sector_num); BlockAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *iov, int nb_sectors, BlockCompletionFunc *cb, void *opaque); @@ -338,10 +334,18 @@ void bdrv_aio_cancel_async(BlockAIOCB *acb); typedef struct BlockRequest { /* Fields to be filled by multiwrite caller */ - int64_t sector; - int nb_sectors; - int flags; - QEMUIOVector *qiov; + union { + struct { + int64_t sector; + int nb_sectors; + int flags; + QEMUIOVector *qiov; + }; + struct { + int req; + void *buf; + }; + }; BlockCompletionFunc *cb; void *opaque; @@ -361,13 +365,14 @@ BlockAIOCB *bdrv_aio_ioctl(BlockDriverState *bs, /* Invalidate any cached metadata used by image formats */ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp); void bdrv_invalidate_cache_all(Error **errp); +int bdrv_inactivate_all(void); /* Ensure contents are flushed to disk. */ int bdrv_flush(BlockDriverState *bs); int coroutine_fn bdrv_co_flush(BlockDriverState *bs); -int bdrv_flush_all(void); void bdrv_close_all(void); void bdrv_drain(BlockDriverState *bs); +void coroutine_fn bdrv_co_drain(BlockDriverState *bs); void bdrv_drain_all(void); int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors); @@ -377,27 +382,21 @@ int bdrv_has_zero_init(BlockDriverState *bs); bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs); bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs); int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int *pnum); + int nb_sectors, int *pnum, + BlockDriverState **file); int64_t bdrv_get_block_status_above(BlockDriverState *bs, BlockDriverState *base, int64_t sector_num, - int nb_sectors, int *pnum); + int nb_sectors, int *pnum, + BlockDriverState **file); int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum); int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, int64_t sector_num, int nb_sectors, int *pnum); -void bdrv_set_on_error(BlockDriverState *bs, BlockdevOnError on_read_error, - BlockdevOnError on_write_error); -BlockdevOnError bdrv_get_on_error(BlockDriverState *bs, bool is_read); -BlockErrorAction bdrv_get_error_action(BlockDriverState *bs, bool is_read, int error); -void bdrv_error_action(BlockDriverState *bs, BlockErrorAction action, - bool is_read, int error); int bdrv_is_read_only(BlockDriverState *bs); int bdrv_is_sg(BlockDriverState *bs); -int bdrv_enable_write_cache(BlockDriverState *bs); -void bdrv_set_enable_write_cache(BlockDriverState *bs, bool wce); -int bdrv_is_inserted(BlockDriverState *bs); +bool bdrv_is_inserted(BlockDriverState *bs); int bdrv_media_changed(BlockDriverState *bs); void bdrv_lock_medium(BlockDriverState *bs, bool locked); void bdrv_eject(BlockDriverState *bs, bool eject_flag); @@ -410,6 +409,7 @@ BlockDriverState *bdrv_lookup_bs(const char *device, bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base); BlockDriverState *bdrv_next_node(BlockDriverState *bs); BlockDriverState *bdrv_next(BlockDriverState *bs); +BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs); int bdrv_is_encrypted(BlockDriverState *bs); int bdrv_key_required(BlockDriverState *bs); int bdrv_set_key(BlockDriverState *bs, const char *key); @@ -464,50 +464,12 @@ void bdrv_img_create(const char *filename, const char *fmt, size_t bdrv_min_mem_align(BlockDriverState *bs); /* Returns optimal alignment in bytes for bounce buffer */ size_t bdrv_opt_mem_align(BlockDriverState *bs); -void bdrv_set_guest_block_size(BlockDriverState *bs, int align); void *qemu_blockalign(BlockDriverState *bs, size_t size); void *qemu_blockalign0(BlockDriverState *bs, size_t size); void *qemu_try_blockalign(BlockDriverState *bs, size_t size); void *qemu_try_blockalign0(BlockDriverState *bs, size_t size); bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov); -struct HBitmapIter; -typedef struct BdrvDirtyBitmap BdrvDirtyBitmap; -BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, - uint32_t granularity, - const char *name, - Error **errp); -int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, - BdrvDirtyBitmap *bitmap, - Error **errp); -BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, - BdrvDirtyBitmap *bitmap, - Error **errp); -BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, - BdrvDirtyBitmap *bitmap, - Error **errp); -BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, - const char *name); -void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap); -void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap); -void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap); -void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap); -BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs); -uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs); -uint32_t bdrv_dirty_bitmap_granularity(BdrvDirtyBitmap *bitmap); -bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap); -bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap); -DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap); -int bdrv_get_dirty(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, int64_t sector); -void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, - int64_t cur_sector, int nr_sectors); -void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, - int64_t cur_sector, int nr_sectors); -void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap); -void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, struct HBitmapIter *hbi); -void bdrv_set_dirty_iter(struct HBitmapIter *hbi, int64_t offset); -int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap); - void bdrv_enable_copy_on_read(BlockDriverState *bs); void bdrv_disable_copy_on_read(BlockDriverState *bs); @@ -522,68 +484,14 @@ void bdrv_op_block_all(BlockDriverState *bs, Error *reason); void bdrv_op_unblock_all(BlockDriverState *bs, Error *reason); bool bdrv_op_blocker_is_empty(BlockDriverState *bs); -typedef enum { - BLKDBG_L1_UPDATE, - - BLKDBG_L1_GROW_ALLOC_TABLE, - BLKDBG_L1_GROW_WRITE_TABLE, - BLKDBG_L1_GROW_ACTIVATE_TABLE, - - BLKDBG_L2_LOAD, - BLKDBG_L2_UPDATE, - BLKDBG_L2_UPDATE_COMPRESSED, - BLKDBG_L2_ALLOC_COW_READ, - BLKDBG_L2_ALLOC_WRITE, - - BLKDBG_READ_AIO, - BLKDBG_READ_BACKING_AIO, - BLKDBG_READ_COMPRESSED, - - BLKDBG_WRITE_AIO, - BLKDBG_WRITE_COMPRESSED, - - BLKDBG_VMSTATE_LOAD, - BLKDBG_VMSTATE_SAVE, - - BLKDBG_COW_READ, - BLKDBG_COW_WRITE, - - BLKDBG_REFTABLE_LOAD, - BLKDBG_REFTABLE_GROW, - BLKDBG_REFTABLE_UPDATE, - - BLKDBG_REFBLOCK_LOAD, - BLKDBG_REFBLOCK_UPDATE, - BLKDBG_REFBLOCK_UPDATE_PART, - BLKDBG_REFBLOCK_ALLOC, - BLKDBG_REFBLOCK_ALLOC_HOOKUP, - BLKDBG_REFBLOCK_ALLOC_WRITE, - BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS, - BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE, - BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE, +#define BLKDBG_EVENT(child, evt) \ + do { \ + if (child) { \ + bdrv_debug_event(child->bs, evt); \ + } \ + } while (0) - BLKDBG_CLUSTER_ALLOC, - BLKDBG_CLUSTER_ALLOC_BYTES, - BLKDBG_CLUSTER_FREE, - - BLKDBG_FLUSH_TO_OS, - BLKDBG_FLUSH_TO_DISK, - - BLKDBG_PWRITEV_RMW_HEAD, - BLKDBG_PWRITEV_RMW_AFTER_HEAD, - BLKDBG_PWRITEV_RMW_TAIL, - BLKDBG_PWRITEV_RMW_AFTER_TAIL, - BLKDBG_PWRITEV, - BLKDBG_PWRITEV_ZERO, - BLKDBG_PWRITEV_DONE, - - BLKDBG_EMPTY_IMAGE_PREPARE, - - BLKDBG_EVENT_MAX, -} BlkDebugEvent; - -#define BLKDBG_EVENT(bs, evt) bdrv_debug_event(bs, evt) -void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event); +void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event); int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, const char *tag); @@ -614,6 +522,23 @@ void bdrv_io_plug(BlockDriverState *bs); void bdrv_io_unplug(BlockDriverState *bs); void bdrv_flush_io_queue(BlockDriverState *bs); -BlockAcctStats *bdrv_get_stats(BlockDriverState *bs); +/** + * bdrv_drained_begin: + * + * Begin a quiesced section for exclusive access to the BDS, by disabling + * external request sources including NBD server and device model. Note that + * this doesn't block timers or coroutines from submitting more requests, which + * means block_job_pause is still necessary. + * + * This function can be recursive. + */ +void bdrv_drained_begin(BlockDriverState *bs); + +/** + * bdrv_drained_end: + * + * End a quiescent section started by bdrv_drained_begin(). + */ +void bdrv_drained_end(BlockDriverState *bs); #endif diff --git a/qemu/include/block/block_int.h b/qemu/include/block/block_int.h index 14ad4c334..10d87595b 100644 --- a/qemu/include/block/block_int.h +++ b/qemu/include/block/block_int.h @@ -26,9 +26,10 @@ #include "block/accounting.h" #include "block/block.h" +#include "block/throttle-groups.h" #include "qemu/option.h" #include "qemu/queue.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "qemu/timer.h" #include "qapi-types.h" #include "qemu/hbitmap.h" @@ -59,11 +60,19 @@ #define BLOCK_PROBE_BUF_SIZE 512 +enum BdrvTrackedRequestType { + BDRV_TRACKED_READ, + BDRV_TRACKED_WRITE, + BDRV_TRACKED_FLUSH, + BDRV_TRACKED_IOCTL, + BDRV_TRACKED_DISCARD, +}; + typedef struct BdrvTrackedRequest { BlockDriverState *bs; int64_t offset; unsigned int bytes; - bool is_write; + enum BdrvTrackedRequestType type; bool serialising; int64_t overlap_offset; @@ -112,6 +121,7 @@ struct BlockDriver { BlockReopenQueue *queue, Error **errp); void (*bdrv_reopen_commit)(BDRVReopenState *reopen_state); void (*bdrv_reopen_abort)(BDRVReopenState *reopen_state); + void (*bdrv_join_options)(QDict *options, QDict *old_options); int (*bdrv_open)(BlockDriverState *bs, QDict *options, int flags, Error **errp); @@ -122,12 +132,11 @@ struct BlockDriver { int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors); void (*bdrv_close)(BlockDriverState *bs); - void (*bdrv_rebind)(BlockDriverState *bs); int (*bdrv_create)(const char *filename, QemuOpts *opts, Error **errp); int (*bdrv_set_key)(BlockDriverState *bs, const char *key); int (*bdrv_make_empty)(BlockDriverState *bs); - void (*bdrv_refresh_filename)(BlockDriverState *bs); + void (*bdrv_refresh_filename)(BlockDriverState *bs, QDict *options); /* aio */ BlockAIOCB *(*bdrv_aio_readv)(BlockDriverState *bs, @@ -146,6 +155,11 @@ struct BlockDriver { int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); int coroutine_fn (*bdrv_co_writev)(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); + int coroutine_fn (*bdrv_co_writev_flags)(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int flags); + + int supported_write_flags; + /* * Efficiently zero a region of the disk image. Typically an image format * would use a compact metadata representation to implement this. This @@ -157,12 +171,21 @@ struct BlockDriver { int coroutine_fn (*bdrv_co_discard)(BlockDriverState *bs, int64_t sector_num, int nb_sectors); int64_t coroutine_fn (*bdrv_co_get_block_status)(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, int *pnum); + int64_t sector_num, int nb_sectors, int *pnum, + BlockDriverState **file); /* * Invalidate any cached meta-data. */ void (*bdrv_invalidate_cache)(BlockDriverState *bs, Error **errp); + int (*bdrv_inactivate)(BlockDriverState *bs); + + /* + * Flushes all data for all layers by calling bdrv_co_flush for underlying + * layers, if needed. This function is needed for deterministic + * synchronization of the flush finishing callback. + */ + int coroutine_fn (*bdrv_co_flush)(BlockDriverState *bs); /* * Flushes all data that was already written to the OS all the way down to @@ -213,13 +236,12 @@ struct BlockDriver { const char *backing_file, const char *backing_fmt); /* removable device specific */ - int (*bdrv_is_inserted)(BlockDriverState *bs); + bool (*bdrv_is_inserted)(BlockDriverState *bs); int (*bdrv_media_changed)(BlockDriverState *bs); void (*bdrv_eject)(BlockDriverState *bs, bool eject_flag); void (*bdrv_lock_medium)(BlockDriverState *bs, bool locked); /* to control generic scsi devices */ - int (*bdrv_ioctl)(BlockDriverState *bs, unsigned long int req, void *buf); BlockAIOCB *(*bdrv_aio_ioctl)(BlockDriverState *bs, unsigned long int req, void *buf, BlockCompletionFunc *cb, void *opaque); @@ -235,9 +257,10 @@ struct BlockDriver { BdrvCheckMode fix); int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts, - BlockDriverAmendStatusCB *status_cb); + BlockDriverAmendStatusCB *status_cb, + void *cb_opaque); - void (*bdrv_debug_event)(BlockDriverState *bs, BlkDebugEvent event); + void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event); /* TODO Better pass a option string/QDict/QemuOpts to add any rule? */ int (*bdrv_debug_breakpoint)(BlockDriverState *bs, const char *event, @@ -288,6 +311,12 @@ struct BlockDriver { */ int (*bdrv_probe_geometry)(BlockDriverState *bs, HDGeometry *geo); + /** + * Drain and stop any internal sources of requests in the driver, and + * remain so until next I/O callback (e.g. bdrv_co_writev) is called. + */ + void (*bdrv_drain)(BlockDriverState *bs); + QLIST_ENTRY(BlockDriver) list; }; @@ -315,6 +344,9 @@ typedef struct BlockLimits { /* memory alignment for bounce buffer */ size_t opt_mem_alignment; + + /* maximum number of iovec elements */ + int max_iov; } BlockLimits; typedef struct BdrvOpBlocker BdrvOpBlocker; @@ -329,7 +361,8 @@ typedef struct BdrvAioNotifier { } BdrvAioNotifier; struct BdrvChildRole { - int (*inherit_flags)(int parent_flags); + void (*inherit_options)(int *child_flags, QDict *child_options, + int parent_flags, QDict *parent_options); }; extern const BdrvChildRole child_file; @@ -337,8 +370,10 @@ extern const BdrvChildRole child_format; struct BdrvChild { BlockDriverState *bs; + char *name; const BdrvChildRole *role; QLIST_ENTRY(BdrvChild) next; + QLIST_ENTRY(BdrvChild) next_parent; }; /* @@ -378,11 +413,8 @@ struct BlockDriverState { QDict *full_open_options; char exact_filename[PATH_MAX]; - BlockDriverState *backing_hd; - BdrvChild *backing_child; - BlockDriverState *file; - - NotifierList close_notifiers; + BdrvChild *backing; + BdrvChild *file; /* Callback before write request is processed */ NotifierWithReturnList before_write_notifiers; @@ -390,7 +422,10 @@ struct BlockDriverState { /* number of in-flight serialising requests */ unsigned int serialising_in_flight; - /* I/O throttling */ + /* I/O throttling. + * throttle_state tells us if this BDS has I/O limits configured. + * io_limits_enabled tells us if they are currently being + * enforced, but it can be temporarily set to false */ CoQueue throttled_reqs[2]; bool io_limits_enabled; /* The following fields are protected by the ThrottleGroup lock. @@ -400,8 +435,8 @@ struct BlockDriverState { unsigned pending_reqs[2]; QLIST_ENTRY(BlockDriverState) round_robin; - /* I/O stats (display with "info blockstats"). */ - BlockAcctStats stats; + /* Offset after the highest byte written to */ + uint64_t wr_highest_offset; /* I/O Limits */ BlockLimits bl; @@ -412,24 +447,14 @@ struct BlockDriverState { /* Alignment requirement for offset/length of I/O requests */ unsigned int request_alignment; - /* the block size for which the guest device expects atomicity */ - int guest_block_size; - - /* do we need to tell the quest if we have a volatile write cache? */ - int enable_write_cache; - - /* NOTE: the following infos are only hints for real hardware - drivers. They are not used by the block driver */ - BlockdevOnError on_read_error, on_write_error; - bool iostatus_enabled; - BlockDeviceIoStatus iostatus; - /* the following member gives a name to every node on the bs graph. */ char node_name[32]; /* element of the list of named nodes building the graph */ QTAILQ_ENTRY(BlockDriverState) node_list; - /* element of the list of "drives" the guest sees */ - QTAILQ_ENTRY(BlockDriverState) device_list; + /* element of the list of all BlockDriverStates (all_bdrv_states) */ + QTAILQ_ENTRY(BlockDriverState) bs_list; + /* element of the list of monitor-owned BDS */ + QTAILQ_ENTRY(BlockDriverState) monitor_list; QLIST_HEAD(, BdrvDirtyBitmap) dirty_bitmaps; int refcnt; @@ -446,8 +471,10 @@ struct BlockDriverState { * parent node of this node. */ BlockDriverState *inherits_from; QLIST_HEAD(, BdrvChild) children; + QLIST_HEAD(, BdrvChild) parents; QDict *options; + QDict *explicit_options; BlockdevDetectZeroesOptions detect_zeroes; /* The error object in use for blocking operations on backing_hd */ @@ -456,8 +483,24 @@ struct BlockDriverState { /* threshold limit for writes, in bytes. "High water mark". */ uint64_t write_threshold_offset; NotifierWithReturn write_threshold_notifier; + + int quiesce_counter; +}; + +struct BlockBackendRootState { + int open_flags; + bool read_only; + BlockdevDetectZeroesOptions detect_zeroes; + + char *throttle_group; + ThrottleState *throttle_state; }; +static inline BlockDriverState *backing_bs(BlockDriverState *bs) +{ + return bs->backing ? bs->backing->bs : NULL; +} + /* Essential block drivers which must always be statically linked into qemu, and * which therefore can be accessed without using bdrv_find_format() */ @@ -474,6 +517,13 @@ extern BlockDriver bdrv_qcow2; */ void bdrv_setup_io_funcs(BlockDriver *bdrv); +int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs, + int64_t offset, unsigned int bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags); +int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, + int64_t offset, unsigned int bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags); + int get_tmp_filename(char *filename, int size); BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, const char *filename); @@ -496,7 +546,7 @@ void bdrv_add_before_write_notifier(BlockDriverState *bs, * * May be called from .bdrv_detach_aio_context() to detach children from the * current #AioContext. This is only needed by block drivers that manage their - * own children. Both ->file and ->backing_hd are automatically handled and + * own children. Both ->file and ->backing are automatically handled and * block drivers should not call this function on them explicitly. */ void bdrv_detach_aio_context(BlockDriverState *bs); @@ -506,7 +556,7 @@ void bdrv_detach_aio_context(BlockDriverState *bs); * * May be called from .bdrv_attach_aio_context() to attach children to the new * #AioContext. This is only needed by block drivers that manage their own - * children. Both ->file and ->backing_hd are automatically handled and block + * children. Both ->file and ->backing are automatically handled and block * drivers should not call this function on them explicitly. */ void bdrv_attach_aio_context(BlockDriverState *bs, @@ -643,6 +693,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target, * @on_target_error: The action to take upon error writing to the target. * @cb: Completion function for the job. * @opaque: Opaque pointer value passed to @cb. + * @txn: Transaction that this job is part of (may be NULL). * * Start a backup operation on @bs. Clusters in @bs are written to @target * until the job is cancelled or manually completed. @@ -653,15 +704,29 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, BlockdevOnError on_source_error, BlockdevOnError on_target_error, BlockCompletionFunc *cb, void *opaque, - Error **errp); + BlockJobTxn *txn, Error **errp); + +void hmp_drive_add_node(Monitor *mon, const char *optstr); + +BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, + const char *child_name, + const BdrvChildRole *child_role); +void bdrv_root_unref_child(BdrvChild *child); void blk_dev_change_media_cb(BlockBackend *blk, bool load); bool blk_dev_has_removable_media(BlockBackend *blk); +bool blk_dev_has_tray(BlockBackend *blk); void blk_dev_eject_request(BlockBackend *blk, bool force); bool blk_dev_is_tray_open(BlockBackend *blk); bool blk_dev_is_medium_locked(BlockBackend *blk); void blk_dev_resize_cb(BlockBackend *blk); void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors); +bool bdrv_requests_pending(BlockDriverState *bs); + +void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out); +void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in); + +void blockdev_close_all_bdrv_states(void); #endif /* BLOCK_INT_H */ diff --git a/qemu/include/block/blockjob.h b/qemu/include/block/blockjob.h index dd9d5e6aa..8bedc4936 100644 --- a/qemu/include/block/blockjob.h +++ b/qemu/include/block/blockjob.h @@ -50,6 +50,26 @@ typedef struct BlockJobDriver { * manually. */ void (*complete)(BlockJob *job, Error **errp); + + /** + * 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. + * + * All jobs will complete with a call to either .commit() or .abort() but + * never both. + */ + void (*commit)(BlockJob *job); + + /** + * If the callback is not NULL, it will be invoked when any job in the + * same transaction fails; or upon this job's failure (due to error or + * cancellation) if it is not in a transaction. Skipped if NULL. + * + * All jobs will complete with a call to either .commit() or .abort() but + * never both. + */ + void (*abort)(BlockJob *job); } BlockJobDriver; /** @@ -64,6 +84,14 @@ struct BlockJob { /** The block device on which the job is operating. */ BlockDriverState *bs; + /** + * The ID of the block job. Currently the BlockBackend name of the BDS + * owning the job at the time when the job is started. + * + * TODO Decouple block job IDs from BlockBackend names + */ + char *id; + /** * The coroutine that executes the job. If not NULL, it is * reentered when busy is false and the job is cancelled. @@ -102,6 +130,11 @@ struct BlockJob { */ bool ready; + /** + * Set to true when the job has deferred work to the main loop. + */ + bool deferred_to_main_loop; + /** Status that is published by the query-block-jobs QMP API */ BlockDeviceIoStatus iostatus; @@ -122,6 +155,21 @@ struct BlockJob { /** The opaque value that is passed to the completion function. */ void *opaque; + + /** Reference count of the block job */ + int refcnt; + + /* True if this job has reported completion by calling block_job_completed. + */ + bool completed; + + /* ret code passed to block_job_completed. + */ + int ret; + + /** Non-NULL if this job is part of a transaction */ + BlockJobTxn *txn; + QLIST_ENTRY(BlockJob) txn_list; }; /** @@ -166,12 +214,21 @@ void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns); void block_job_yield(BlockJob *job); /** - * block_job_release: + * block_job_ref: * @bs: The block device. * - * Release job resources when an error occurred or job completed. + * Grab a reference to the block job. Should be paired with block_job_unref. */ -void block_job_release(BlockDriverState *bs); +void block_job_ref(BlockJob *job); + +/** + * block_job_unref: + * @bs: The block device. + * + * Release reference to the block job and release resources if it is the last + * reference. + */ +void block_job_unref(BlockJob *job); /** * block_job_completed: @@ -356,4 +413,39 @@ void block_job_defer_to_main_loop(BlockJob *job, BlockJobDeferToMainLoopFn *fn, void *opaque); +/** + * block_job_txn_new: + * + * Allocate and return a new block job transaction. Jobs can be added to the + * transaction using block_job_txn_add_job(). + * + * The transaction is automatically freed when the last job completes or is + * cancelled. + * + * All jobs in the transaction either complete successfully or fail/cancel as a + * group. Jobs wait for each other before completing. Cancelling one job + * cancels all jobs in the transaction. + */ +BlockJobTxn *block_job_txn_new(void); + +/** + * block_job_txn_unref: + * + * Release a reference that was previously acquired with block_job_txn_add_job + * or block_job_txn_new. If it's the last reference to the object, it will be + * freed. + */ +void block_job_txn_unref(BlockJobTxn *txn); + +/** + * block_job_txn_add_job: + * @txn: The transaction (may be NULL) + * @job: Job to add to the transaction + * + * Add @job to the transaction. The @job must not already be in a transaction. + * The caller must call either block_job_txn_unref() or block_job_completed() + * to release the reference that is automatically grabbed here. + */ +void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job); + #endif diff --git a/qemu/include/block/coroutine.h b/qemu/include/block/coroutine.h deleted file mode 100644 index 20c027a7f..000000000 --- a/qemu/include/block/coroutine.h +++ /dev/null @@ -1,219 +0,0 @@ -/* - * QEMU coroutine implementation - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Stefan Hajnoczi - * Kevin Wolf - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#ifndef QEMU_COROUTINE_H -#define QEMU_COROUTINE_H - -#include -#include "qemu/typedefs.h" -#include "qemu/queue.h" -#include "qemu/timer.h" - -/** - * Coroutines are a mechanism for stack switching and can be used for - * cooperative userspace threading. These functions provide a simple but - * useful flavor of coroutines that is suitable for writing sequential code, - * rather than callbacks, for operations that need to give up control while - * waiting for events to complete. - * - * These functions are re-entrant and may be used outside the global mutex. - */ - -/** - * Mark a function that executes in coroutine context - * - * Functions that execute in coroutine context cannot be called directly from - * normal functions. In the future it would be nice to enable compiler or - * static checker support for catching such errors. This annotation might make - * it possible and in the meantime it serves as documentation. - * - * For example: - * - * static void coroutine_fn foo(void) { - * .... - * } - */ -#define coroutine_fn - -typedef struct Coroutine Coroutine; - -/** - * Coroutine entry point - * - * When the coroutine is entered for the first time, opaque is passed in as an - * argument. - * - * When this function returns, the coroutine is destroyed automatically and - * execution continues in the caller who last entered the coroutine. - */ -typedef void coroutine_fn CoroutineEntry(void *opaque); - -/** - * Create a new coroutine - * - * Use qemu_coroutine_enter() to actually transfer control to the coroutine. - */ -Coroutine *qemu_coroutine_create(CoroutineEntry *entry); - -/** - * Transfer control to a coroutine - * - * The opaque argument is passed as the argument to the entry point when - * entering the coroutine for the first time. It is subsequently ignored. - */ -void qemu_coroutine_enter(Coroutine *coroutine, void *opaque); - -/** - * Transfer control back to a coroutine's caller - * - * This function does not return until the coroutine is re-entered using - * qemu_coroutine_enter(). - */ -void coroutine_fn qemu_coroutine_yield(void); - -/** - * Get the currently executing coroutine - */ -Coroutine *coroutine_fn qemu_coroutine_self(void); - -/** - * Return whether or not currently inside a coroutine - * - * This can be used to write functions that work both when in coroutine context - * and when not in coroutine context. Note that such functions cannot use the - * coroutine_fn annotation since they work outside coroutine context. - */ -bool qemu_in_coroutine(void); - - - -/** - * CoQueues are a mechanism to queue coroutines in order to continue executing - * them later. They provide the fundamental primitives on which coroutine locks - * are built. - */ -typedef struct CoQueue { - QTAILQ_HEAD(, Coroutine) entries; -} CoQueue; - -/** - * Initialise a CoQueue. This must be called before any other operation is used - * on the CoQueue. - */ -void qemu_co_queue_init(CoQueue *queue); - -/** - * Adds the current coroutine to the CoQueue and transfers control to the - * caller of the coroutine. - */ -void coroutine_fn qemu_co_queue_wait(CoQueue *queue); - -/** - * Restarts the next coroutine in the CoQueue and removes it from the queue. - * - * Returns true if a coroutine was restarted, false if the queue is empty. - */ -bool coroutine_fn qemu_co_queue_next(CoQueue *queue); - -/** - * Restarts all coroutines in the CoQueue and leaves the queue empty. - */ -void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue); - -/** - * Enter the next coroutine in the queue - */ -bool qemu_co_enter_next(CoQueue *queue); - -/** - * Checks if the CoQueue is empty. - */ -bool qemu_co_queue_empty(CoQueue *queue); - - -/** - * Provides a mutex that can be used to synchronise coroutines - */ -typedef struct CoMutex { - bool locked; - CoQueue queue; -} CoMutex; - -/** - * Initialises a CoMutex. This must be called before any other operation is used - * on the CoMutex. - */ -void qemu_co_mutex_init(CoMutex *mutex); - -/** - * Locks the mutex. If the lock cannot be taken immediately, control is - * transferred to the caller of the current coroutine. - */ -void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex); - -/** - * Unlocks the mutex and schedules the next coroutine that was waiting for this - * lock to be run. - */ -void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex); - -typedef struct CoRwlock { - bool writer; - int reader; - CoQueue queue; -} CoRwlock; - -/** - * Initialises a CoRwlock. This must be called before any other operation - * is used on the CoRwlock - */ -void qemu_co_rwlock_init(CoRwlock *lock); - -/** - * Read locks the CoRwlock. If the lock cannot be taken immediately because - * of a parallel writer, control is transferred to the caller of the current - * coroutine. - */ -void qemu_co_rwlock_rdlock(CoRwlock *lock); - -/** - * Write Locks the mutex. If the lock cannot be taken immediately because - * of a parallel reader, control is transferred to the caller of the current - * coroutine. - */ -void qemu_co_rwlock_wrlock(CoRwlock *lock); - -/** - * Unlocks the read/write lock and schedules the next coroutine that was - * waiting for this lock to be run. - */ -void qemu_co_rwlock_unlock(CoRwlock *lock); - -/** - * Yield the coroutine for a given duration - * - * Behaves similarly to co_sleep_ns(), but the sleeping coroutine will be - * resumed when using aio_poll(). - */ -void coroutine_fn co_aio_sleep_ns(AioContext *ctx, QEMUClockType type, - int64_t ns); - -/** - * Yield until a file descriptor becomes readable - * - * Note that this function clobbers the handlers for the file descriptor. - */ -void coroutine_fn yield_until_fd_readable(int fd); - -#endif /* QEMU_COROUTINE_H */ diff --git a/qemu/include/block/coroutine_int.h b/qemu/include/block/coroutine_int.h deleted file mode 100644 index 9aa1aae5d..000000000 --- a/qemu/include/block/coroutine_int.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Coroutine internals - * - * Copyright (c) 2011 Kevin Wolf - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QEMU_COROUTINE_INT_H -#define QEMU_COROUTINE_INT_H - -#include "qemu/queue.h" -#include "block/coroutine.h" - -typedef enum { - COROUTINE_YIELD = 1, - COROUTINE_TERMINATE = 2, - COROUTINE_ENTER = 3, -} CoroutineAction; - -struct Coroutine { - CoroutineEntry *entry; - void *entry_arg; - Coroutine *caller; - QSLIST_ENTRY(Coroutine) pool_next; - - /* Coroutines that should be woken up when we yield or terminate */ - QTAILQ_HEAD(, Coroutine) co_queue_wakeup; - QTAILQ_ENTRY(Coroutine) co_queue_next; -}; - -Coroutine *qemu_coroutine_new(void); -void qemu_coroutine_delete(Coroutine *co); -CoroutineAction qemu_coroutine_switch(Coroutine *from, Coroutine *to, - CoroutineAction action); -void coroutine_fn qemu_co_queue_run_restart(Coroutine *co); - -#endif diff --git a/qemu/include/block/dirty-bitmap.h b/qemu/include/block/dirty-bitmap.h new file mode 100644 index 000000000..80afe603f --- /dev/null +++ b/qemu/include/block/dirty-bitmap.h @@ -0,0 +1,44 @@ +#ifndef BLOCK_DIRTY_BITMAP_H +#define BLOCK_DIRTY_BITMAP_H + +#include "qemu-common.h" +#include "qemu/hbitmap.h" + +BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, + uint32_t granularity, + const char *name, + Error **errp); +int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, + Error **errp); +BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, + Error **errp); +BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, + Error **errp); +BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, + const char *name); +void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap); +void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap); +void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs); +void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap); +void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap); +BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs); +uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs); +uint32_t bdrv_dirty_bitmap_granularity(BdrvDirtyBitmap *bitmap); +bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap); +bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap); +DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap); +int bdrv_get_dirty(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, + int64_t sector); +void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, + int64_t cur_sector, int nr_sectors); +void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, + int64_t cur_sector, int nr_sectors); +void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, struct HBitmapIter *hbi); +void bdrv_set_dirty_iter(struct HBitmapIter *hbi, int64_t offset); +int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap); +void bdrv_dirty_bitmap_truncate(BlockDriverState *bs); + +#endif diff --git a/qemu/include/block/nbd.h b/qemu/include/block/nbd.h index 65f409d80..b86a97698 100644 --- a/qemu/include/block/nbd.h +++ b/qemu/include/block/nbd.h @@ -19,10 +19,11 @@ #ifndef NBD_H #define NBD_H -#include #include "qemu-common.h" #include "qemu/option.h" +#include "io/channel-socket.h" +#include "crypto/tlscreds.h" struct nbd_request { uint32_t magic; @@ -55,7 +56,10 @@ struct nbd_reply { #define NBD_REP_ACK (1) /* Data sending finished. */ #define NBD_REP_SERVER (2) /* Export description. */ #define NBD_REP_ERR_UNSUP ((UINT32_C(1) << 31) | 1) /* Unknown option. */ +#define NBD_REP_ERR_POLICY ((UINT32_C(1) << 31) | 2) /* Server denied */ #define NBD_REP_ERR_INVALID ((UINT32_C(1) << 31) | 3) /* Invalid length. */ +#define NBD_REP_ERR_TLS_REQD ((UINT32_C(1) << 31) | 5) /* TLS required */ + #define NBD_CMD_MASK_COMMAND 0x0000ffff #define NBD_CMD_FLAG_FUA (1 << 16) @@ -73,12 +77,19 @@ enum { /* Maximum size of a single READ/WRITE data buffer */ #define NBD_MAX_BUFFER_SIZE (32 * 1024 * 1024) -ssize_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read); -int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, +ssize_t nbd_wr_syncv(QIOChannel *ioc, + struct iovec *iov, + size_t niov, + size_t offset, + size_t length, + bool do_read); +int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags, + QCryptoTLSCreds *tlscreds, const char *hostname, + QIOChannel **outioc, off_t *size, Error **errp); -int nbd_init(int fd, int csock, uint32_t flags, off_t size); -ssize_t nbd_send_request(int csock, struct nbd_request *request); -ssize_t nbd_receive_reply(int csock, struct nbd_reply *reply); +int nbd_init(int fd, QIOChannelSocket *sioc, uint32_t flags, off_t size); +ssize_t nbd_send_request(QIOChannel *ioc, struct nbd_request *request); +ssize_t nbd_receive_reply(QIOChannel *ioc, struct nbd_reply *reply); int nbd_client(int fd); int nbd_disconnect(int fd); @@ -98,8 +109,11 @@ NBDExport *nbd_export_find(const char *name); void nbd_export_set_name(NBDExport *exp, const char *name); void nbd_export_close_all(void); -NBDClient *nbd_client_new(NBDExport *exp, int csock, - void (*close)(NBDClient *)); +void nbd_client_new(NBDExport *exp, + QIOChannelSocket *sioc, + QCryptoTLSCreds *tlscreds, + const char *tlsaclname, + void (*close)(NBDClient *)); void nbd_client_get(NBDClient *client); void nbd_client_put(NBDClient *client); diff --git a/qemu/include/block/qapi.h b/qemu/include/block/qapi.h index 327549d91..82ba4b63a 100644 --- a/qemu/include/block/qapi.h +++ b/qemu/include/block/qapi.h @@ -29,7 +29,8 @@ #include "block/block.h" #include "block/snapshot.h" -BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp); +BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, + BlockDriverState *bs, Error **errp); int bdrv_query_snapshot_info_list(BlockDriverState *bs, SnapshotInfoList **p_list, Error **errp); diff --git a/qemu/include/block/scsi.h b/qemu/include/block/scsi.h index edde960d1..a311341e6 100644 --- a/qemu/include/block/scsi.h +++ b/qemu/include/block/scsi.h @@ -229,7 +229,7 @@ const char *scsi_command_name(uint8_t cmd); #define MODE_PAGE_TO_PROTECT 0x1d #define MODE_PAGE_CAPABILITIES 0x2a #define MODE_PAGE_ALLS 0x3f -/* Not in Mt. Fuji, but in ATAPI 2.6 -- depricated now in favor +/* Not in Mt. Fuji, but in ATAPI 2.6 -- deprecated now in favor * of MODE_PAGE_SENSE_POWER */ #define MODE_PAGE_CDROM 0x0d diff --git a/qemu/include/block/snapshot.h b/qemu/include/block/snapshot.h index 770d9bbc8..e5c055311 100644 --- a/qemu/include/block/snapshot.h +++ b/qemu/include/block/snapshot.h @@ -26,7 +26,6 @@ #define SNAPSHOT_H #include "qemu-common.h" -#include "qapi/error.h" #include "qemu/option.h" @@ -63,9 +62,9 @@ int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id, const char *name, Error **errp); -void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs, - const char *id_or_name, - Error **errp); +int bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs, + const char *id_or_name, + Error **errp); int bdrv_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_info); int bdrv_snapshot_load_tmp(BlockDriverState *bs, @@ -75,4 +74,22 @@ int bdrv_snapshot_load_tmp(BlockDriverState *bs, int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs, const char *id_or_name, Error **errp); + + +/* Group operations. All block drivers are involved. + * These functions will properly handle dataplane (take aio_context_acquire + * when appropriate for appropriate block drivers */ + +bool bdrv_all_can_snapshot(BlockDriverState **first_bad_bs); +int bdrv_all_delete_snapshot(const char *name, BlockDriverState **first_bsd_bs, + Error **err); +int bdrv_all_goto_snapshot(const char *name, BlockDriverState **first_bsd_bs); +int bdrv_all_find_snapshot(const char *name, BlockDriverState **first_bad_bs); +int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn, + BlockDriverState *vm_state_bs, + uint64_t vm_state_size, + BlockDriverState **first_bad_bs); + +BlockDriverState *bdrv_all_find_vmstate_bs(void); + #endif diff --git a/qemu/include/block/throttle-groups.h b/qemu/include/block/throttle-groups.h index fab113f6d..aba28f30b 100644 --- a/qemu/include/block/throttle-groups.h +++ b/qemu/include/block/throttle-groups.h @@ -30,6 +30,9 @@ const char *throttle_group_get_name(BlockDriverState *bs); +ThrottleState *throttle_group_incref(const char *name); +void throttle_group_unref(ThrottleState *ts); + void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg); void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg); @@ -40,7 +43,4 @@ void coroutine_fn throttle_group_co_io_limits_intercept(BlockDriverState *bs, unsigned int bytes, bool is_write); -void throttle_group_lock(BlockDriverState *bs); -void throttle_group_unlock(BlockDriverState *bs); - #endif diff --git a/qemu/include/block/write-threshold.h b/qemu/include/block/write-threshold.h index f1b899cd5..234d2193e 100644 --- a/qemu/include/block/write-threshold.h +++ b/qemu/include/block/write-threshold.h @@ -12,9 +12,6 @@ #ifndef BLOCK_WRITE_THRESHOLD_H #define BLOCK_WRITE_THRESHOLD_H -#include - -#include "qemu/typedefs.h" #include "qemu-common.h" /* diff --git a/qemu/include/config.h b/qemu/include/config.h deleted file mode 100644 index e20f78696..000000000 --- a/qemu/include/config.h +++ /dev/null @@ -1,2 +0,0 @@ -#include "config-host.h" -#include "config-target.h" diff --git a/qemu/include/crypto/afsplit.h b/qemu/include/crypto/afsplit.h new file mode 100644 index 000000000..4cc4ca4b3 --- /dev/null +++ b/qemu/include/crypto/afsplit.h @@ -0,0 +1,135 @@ +/* + * QEMU Crypto anti forensic information splitter + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library 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 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 . + * + */ + +#ifndef QCRYPTO_AFSPLIT_H__ +#define QCRYPTO_AFSPLIT_H__ + +#include "crypto/hash.h" + +/** + * This module implements the anti-forensic splitter that is specified + * as part of the LUKS format: + * + * http://clemens.endorphin.org/cryptography + * http://clemens.endorphin.org/TKS1-draft.pdf + * + * The core idea is to take a short piece of data (key material) + * and process it to expand it to a much larger piece of data. + * The expansion process is reversible, to obtain the original + * short data. The key property of the expansion is that if any + * byte in the larger data set is changed / missing, it should be + * impossible to recreate the original short data. + * + * + * Creating a large split key for storage + * + * size_t nkey = 32; + * uint32_t stripes = 32768; // To produce a 1 MB split key + * uint8_t *masterkey = ....a 32-byte AES key... + * uint8_t *splitkey; + * + * splitkey = g_new0(uint8_t, nkey * stripes); + * + * if (qcrypto_afsplit_encode(QCRYPTO_HASH_ALG_SHA256, + * nkey, stripes, + * masterkey, splitkey, errp) < 0) { + * g_free(splitkey); + * g_free(masterkey); + * return -1; + * } + * + * ...store splitkey somewhere... + * + * g_free(splitkey); + * g_free(masterkey); + * + * + * + * + * Retrieving a master key from storage + * + * size_t nkey = 32; + * uint32_t stripes = 32768; // To produce a 1 MB split key + * uint8_t *masterkey; + * uint8_t *splitkey = .... read in 1 MB of data... + * + * masterkey = g_new0(uint8_t, nkey); + * + * if (qcrypto_afsplit_decode(QCRYPTO_HASH_ALG_SHA256, + * nkey, stripes, + * splitkey, masterkey, errp) < 0) { + * g_free(splitkey); + * g_free(masterkey); + * return -1; + * } + * + * ..decrypt data with masterkey... + * + * g_free(splitkey); + * g_free(masterkey); + * + * + */ + +/** + * qcrypto_afsplit_encode: + * @hash: the hash algorithm to use for data expansion + * @blocklen: the size of @in in bytes + * @stripes: the number of times to expand @in in size + * @in: the master key to be expanded in size + * @out: preallocated buffer to hold the split key + * @errp: pointer to a NULL-initialized error object + * + * Split the data in @in, which is @blocklen bytes in + * size, to form a larger piece of data @out, which is + * @blocklen * @stripes bytes in size. + * + * Returns: 0 on success, -1 on error; + */ +int qcrypto_afsplit_encode(QCryptoHashAlgorithm hash, + size_t blocklen, + uint32_t stripes, + const uint8_t *in, + uint8_t *out, + Error **errp); + +/** + * qcrypto_afsplit_decode: + * @hash: the hash algorithm to use for data compression + * @blocklen: the size of @out in bytes + * @stripes: the number of times to decrease @in in size + * @in: the split key to be recombined + * @out: preallocated buffer to hold the master key + * @errp: pointer to a NULL-initialized error object + * + * Join the data in @in, which is @blocklen * @stripes + * bytes in size, to form the original small piece of + * data @out, which is @blocklen bytes in size. + * + * Returns: 0 on success, -1 on error; + */ +int qcrypto_afsplit_decode(QCryptoHashAlgorithm hash, + size_t blocklen, + uint32_t stripes, + const uint8_t *in, + uint8_t *out, + Error **errp); + +#endif /* QCRYPTO_AFSPLIT_H__ */ diff --git a/qemu/include/crypto/block.h b/qemu/include/crypto/block.h new file mode 100644 index 000000000..a21e11ff8 --- /dev/null +++ b/qemu/include/crypto/block.h @@ -0,0 +1,232 @@ +/* + * QEMU Crypto block device encryption + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QCRYPTO_BLOCK_H__ +#define QCRYPTO_BLOCK_H__ + +#include "crypto/cipher.h" +#include "crypto/ivgen.h" + +typedef struct QCryptoBlock QCryptoBlock; + +/* See also QCryptoBlockFormat, QCryptoBlockCreateOptions + * and QCryptoBlockOpenOptions in qapi/crypto.json */ + +typedef ssize_t (*QCryptoBlockReadFunc)(QCryptoBlock *block, + size_t offset, + uint8_t *buf, + size_t buflen, + Error **errp, + void *opaque); + +typedef ssize_t (*QCryptoBlockInitFunc)(QCryptoBlock *block, + size_t headerlen, + Error **errp, + void *opaque); + +typedef ssize_t (*QCryptoBlockWriteFunc)(QCryptoBlock *block, + size_t offset, + const uint8_t *buf, + size_t buflen, + Error **errp, + void *opaque); + +/** + * qcrypto_block_has_format: + * @format: the encryption format + * @buf: the data from head of the volume + * @len: the length of @buf in bytes + * + * Given @len bytes of data from the head of a storage volume + * in @buf, probe to determine if the volume has the encryption + * format specified in @format. + * + * Returns: true if the data in @buf matches @format + */ +bool qcrypto_block_has_format(QCryptoBlockFormat format, + const uint8_t *buf, + size_t buflen); + +typedef enum { + QCRYPTO_BLOCK_OPEN_NO_IO = (1 << 0), +} QCryptoBlockOpenFlags; + +/** + * qcrypto_block_open: + * @options: the encryption options + * @readfunc: callback for reading data from the volume + * @opaque: data to pass to @readfunc + * @flags: bitmask of QCryptoBlockOpenFlags values + * @errp: pointer to a NULL-initialized error object + * + * Create a new block encryption object for an existing + * storage volume encrypted with format identified by + * the parameters in @options. + * + * This will use @readfunc to initialize the encryption + * context based on the volume header(s), extracting the + * master key(s) as required. + * + * If @flags contains QCRYPTO_BLOCK_OPEN_NO_IO then + * the open process will be optimized to skip any parts + * that are only required to perform I/O. In particular + * this would usually avoid the need to decrypt any + * master keys. The only thing that can be done with + * the resulting QCryptoBlock object would be to query + * metadata such as the payload offset. There will be + * no cipher or ivgen objects available. + * + * If any part of initializing the encryption context + * fails an error will be returned. This could be due + * to the volume being in the wrong format, a cipher + * or IV generator algorithm that is not supported, + * or incorrect passphrases. + * + * Returns: a block encryption format, or NULL on error + */ +QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options, + QCryptoBlockReadFunc readfunc, + void *opaque, + unsigned int flags, + Error **errp); + +/** + * qcrypto_block_create: + * @format: the encryption format + * @initfunc: callback for initializing volume header + * @writefunc: callback for writing data to the volume header + * @opaque: data to pass to @initfunc and @writefunc + * @errp: pointer to a NULL-initialized error object + * + * Create a new block encryption object for initializing + * a storage volume to be encrypted with format identified + * by the parameters in @options. + * + * This method will allocate space for a new volume header + * using @initfunc and then write header data using @writefunc, + * generating new master keys, etc as required. Any existing + * data present on the volume will be irrevocably destroyed. + * + * If any part of initializing the encryption context + * fails an error will be returned. This could be due + * to the volume being in the wrong format, a cipher + * or IV generator algorithm that is not supported, + * or incorrect passphrases. + * + * Returns: a block encryption format, or NULL on error + */ +QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, + QCryptoBlockInitFunc initfunc, + QCryptoBlockWriteFunc writefunc, + void *opaque, + Error **errp); + +/** + * @qcrypto_block_decrypt: + * @block: the block encryption object + * @startsector: the sector from which @buf was read + * @buf: the buffer to decrypt + * @len: the length of @buf in bytes + * @errp: pointer to a NULL-initialized error object + * + * Decrypt @len bytes of cipher text in @buf, writing + * plain text back into @buf + * + * Returns 0 on success, -1 on failure + */ +int qcrypto_block_decrypt(QCryptoBlock *block, + uint64_t startsector, + uint8_t *buf, + size_t len, + Error **errp); + +/** + * @qcrypto_block_encrypt: + * @block: the block encryption object + * @startsector: the sector to which @buf will be written + * @buf: the buffer to decrypt + * @len: the length of @buf in bytes + * @errp: pointer to a NULL-initialized error object + * + * Encrypt @len bytes of plain text in @buf, writing + * cipher text back into @buf + * + * Returns 0 on success, -1 on failure + */ +int qcrypto_block_encrypt(QCryptoBlock *block, + uint64_t startsector, + uint8_t *buf, + size_t len, + Error **errp); + +/** + * qcrypto_block_get_cipher: + * @block: the block encryption object + * + * Get the cipher to use for payload encryption + * + * Returns: the cipher object + */ +QCryptoCipher *qcrypto_block_get_cipher(QCryptoBlock *block); + +/** + * qcrypto_block_get_ivgen: + * @block: the block encryption object + * + * Get the initialization vector generator to use for + * payload encryption + * + * Returns: the IV generator object + */ +QCryptoIVGen *qcrypto_block_get_ivgen(QCryptoBlock *block); + + +/** + * qcrypto_block_get_kdf_hash: + * @block: the block encryption object + * + * Get the hash algorithm used with the key derivation + * function + * + * Returns: the hash algorithm + */ +QCryptoHashAlgorithm qcrypto_block_get_kdf_hash(QCryptoBlock *block); + +/** + * qcrypto_block_get_payload_offset: + * @block: the block encryption object + * + * Get the offset to the payload indicated by the + * encryption header, in bytes. + * + * Returns: the payload offset in bytes + */ +uint64_t qcrypto_block_get_payload_offset(QCryptoBlock *block); + +/** + * qcrypto_block_free: + * @block: the block encryption object + * + * Release all resources associated with the encryption + * object + */ +void qcrypto_block_free(QCryptoBlock *block); + +#endif /* QCRYPTO_BLOCK_H__ */ diff --git a/qemu/include/crypto/cipher.h b/qemu/include/crypto/cipher.h index b4d714f26..d770c4835 100644 --- a/qemu/include/crypto/cipher.h +++ b/qemu/include/crypto/cipher.h @@ -21,26 +21,12 @@ #ifndef QCRYPTO_CIPHER_H__ #define QCRYPTO_CIPHER_H__ -#include "qemu-common.h" -#include "qapi/error.h" +#include "qapi-types.h" typedef struct QCryptoCipher QCryptoCipher; -typedef enum { - QCRYPTO_CIPHER_ALG_AES_128, - QCRYPTO_CIPHER_ALG_AES_192, - QCRYPTO_CIPHER_ALG_AES_256, - QCRYPTO_CIPHER_ALG_DES_RFB, /* A stupid variant on DES for VNC */ - - QCRYPTO_CIPHER_ALG_LAST -} QCryptoCipherAlgorithm; - -typedef enum { - QCRYPTO_CIPHER_MODE_ECB, - QCRYPTO_CIPHER_MODE_CBC, - - QCRYPTO_CIPHER_MODE_LAST -} QCryptoCipherMode; +/* See also "QCryptoCipherAlgorithm" and "QCryptoCipherMode" + * enums defined in qapi/crypto.json */ /** * QCryptoCipher: @@ -107,6 +93,43 @@ struct QCryptoCipher { */ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg); +/** + * qcrypto_cipher_get_block_len: + * @alg: the cipher algorithm + * + * Get the required data block size in bytes. When + * encrypting data, it must be a multiple of the + * block size. + * + * Returns: the block size in bytes + */ +size_t qcrypto_cipher_get_block_len(QCryptoCipherAlgorithm alg); + + +/** + * qcrypto_cipher_get_key_len: + * @alg: the cipher algorithm + * + * Get the required key size in bytes. + * + * Returns: the key size in bytes + */ +size_t qcrypto_cipher_get_key_len(QCryptoCipherAlgorithm alg); + + +/** + * qcrypto_cipher_get_iv_len: + * @alg: the cipher algorithm + * @mode: the cipher mode + * + * Get the required initialization vector size + * in bytes, if one is required. + * + * Returns: the IV size in bytes, or 0 if no IV is permitted + */ +size_t qcrypto_cipher_get_iv_len(QCryptoCipherAlgorithm alg, + QCryptoCipherMode mode); + /** * qcrypto_cipher_new: @@ -114,7 +137,7 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg); * @mode: the cipher usage mode * @key: the private key bytes * @nkey: the length of @key - * @errp: pointer to an uninitialized error object + * @errp: pointer to a NULL-initialized error object * * Creates a new cipher object for encrypting/decrypting * data with the algorithm @alg in the usage mode @mode. @@ -150,7 +173,7 @@ void qcrypto_cipher_free(QCryptoCipher *cipher); * @in: buffer holding the plain text input data * @out: buffer to fill with the cipher text output data * @len: the length of @in and @out buffers - * @errp: pointer to an uninitialized error object + * @errp: pointer to a NULL-initialized error object * * Encrypts the plain text stored in @in, filling * @out with the resulting ciphered text. Both the @@ -172,7 +195,7 @@ int qcrypto_cipher_encrypt(QCryptoCipher *cipher, * @in: buffer holding the cipher text input data * @out: buffer to fill with the plain text output data * @len: the length of @in and @out buffers - * @errp: pointer to an uninitialized error object + * @errp: pointer to a NULL-initialized error object * * Decrypts the cipher text stored in @in, filling * @out with the resulting plain text. Both the @@ -192,7 +215,7 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher, * @cipher: the cipher object * @iv: the initialization vector bytes * @niv: the length of @iv - * @errpr: pointer to an uninitialized error object + * @errpr: pointer to a NULL-initialized error object * * If the @cipher object is setup to use a mode that requires * initialization vectors, this sets the initialization vector diff --git a/qemu/include/crypto/hash.h b/qemu/include/crypto/hash.h index b5acbf638..f38caed66 100644 --- a/qemu/include/crypto/hash.h +++ b/qemu/include/crypto/hash.h @@ -21,17 +21,9 @@ #ifndef QCRYPTO_HASH_H__ #define QCRYPTO_HASH_H__ -#include "qemu-common.h" -#include "qapi/error.h" - -typedef enum { - QCRYPTO_HASH_ALG_MD5, - QCRYPTO_HASH_ALG_SHA1, - QCRYPTO_HASH_ALG_SHA256, - - QCRYPTO_HASH_ALG_LAST -} QCryptoHashAlgorithm; +#include "qapi-types.h" +/* See also "QCryptoHashAlgorithm" defined in qapi/crypto.json */ /** * qcrypto_hash_supports: @@ -44,6 +36,17 @@ typedef enum { */ gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg); + +/** + * qcrypto_hash_digest_len: + * @alg: the hash algorithm + * + * Determine the size of the hash digest in bytes + * + * Returns: the digest length in bytes + */ +size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg); + /** * qcrypto_hash_bytesv: * @alg: the hash algorithm @@ -51,7 +54,7 @@ gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg); * @niov: the length of @iov * @result: pointer to hold output hash * @resultlen: pointer to hold length of @result - * @errp: pointer to uninitialized error object + * @errp: pointer to a NULL-initialized error object * * Computes the hash across all the memory regions * present in @iov. The @result pointer will be @@ -76,7 +79,7 @@ int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, * @len: the length of @buf * @result: pointer to hold output hash * @resultlen: pointer to hold length of @result - * @errp: pointer to uninitialized error object + * @errp: pointer to a NULL-initialized error object * * Computes the hash across all the memory region * @buf of length @len. The @result pointer will be @@ -100,7 +103,7 @@ int qcrypto_hash_bytes(QCryptoHashAlgorithm alg, * @iov: the array of memory regions to hash * @niov: the length of @iov * @digest: pointer to hold output hash - * @errp: pointer to uninitialized error object + * @errp: pointer to a NULL-initialized error object * * Computes the hash across all the memory regions * present in @iov. The @digest pointer will be @@ -123,7 +126,7 @@ int qcrypto_hash_digestv(QCryptoHashAlgorithm alg, * @buf: the memory region to hash * @len: the length of @buf * @digest: pointer to hold output hash - * @errp: pointer to uninitialized error object + * @errp: pointer to a NULL-initialized error object * * Computes the hash across all the memory region * @buf of length @len. The @digest pointer will be @@ -146,7 +149,7 @@ int qcrypto_hash_digest(QCryptoHashAlgorithm alg, * @iov: the array of memory regions to hash * @niov: the length of @iov * @base64: pointer to hold output hash - * @errp: pointer to uninitialized error object + * @errp: pointer to a NULL-initialized error object * * Computes the hash across all the memory regions * present in @iov. The @base64 pointer will be @@ -169,7 +172,7 @@ int qcrypto_hash_base64v(QCryptoHashAlgorithm alg, * @buf: the memory region to hash * @len: the length of @buf * @base64: pointer to hold output hash - * @errp: pointer to uninitialized error object + * @errp: pointer to a NULL-initialized error object * * Computes the hash across all the memory region * @buf of length @len. The @base64 pointer will be diff --git a/qemu/include/crypto/init.h b/qemu/include/crypto/init.h index 5fc510c4f..2513ed098 100644 --- a/qemu/include/crypto/init.h +++ b/qemu/include/crypto/init.h @@ -21,9 +21,6 @@ #ifndef QCRYPTO_INIT_H__ #define QCRYPTO_INIT_H__ -#include "qemu-common.h" -#include "qapi/error.h" - int qcrypto_init(Error **errp); #endif /* QCRYPTO_INIT_H__ */ diff --git a/qemu/include/crypto/ivgen.h b/qemu/include/crypto/ivgen.h new file mode 100644 index 000000000..09cdb6fcd --- /dev/null +++ b/qemu/include/crypto/ivgen.h @@ -0,0 +1,206 @@ +/* + * QEMU Crypto block IV generator + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QCRYPTO_IVGEN_H__ +#define QCRYPTO_IVGEN_H__ + +#include "crypto/cipher.h" +#include "crypto/hash.h" + +/** + * This module provides a framework for generating initialization + * vectors for block encryption schemes using chained cipher modes + * CBC. The principle is that each disk sector is assigned a unique + * initialization vector for use for encryption of data in that + * sector. + * + * + * Encrypting block data with initialiation vectors + * + * uint8_t *data = ....data to encrypt... + * size_t ndata = XXX; + * uint8_t *key = ....some encryption key... + * size_t nkey = XXX; + * uint8_t *iv; + * size_t niv; + * size_t sector = 0; + * + * g_assert((ndata % 512) == 0); + * + * QCryptoIVGen *ivgen = qcrypto_ivgen_new(QCRYPTO_IVGEN_ALG_ESSIV, + * QCRYPTO_CIPHER_ALG_AES_128, + * QCRYPTO_HASH_ALG_SHA256, + * key, nkey, errp); + * if (!ivgen) { + * return -1; + * } + * + * QCryptoCipher *cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_128, + * QCRYPTO_CIPHER_MODE_CBC, + * key, nkey, errp); + * if (!cipher) { + * goto error; + * } + * + * niv = qcrypto_cipher_get_iv_len(QCRYPTO_CIPHER_ALG_AES_128, + * QCRYPTO_CIPHER_MODE_CBC); + * iv = g_new0(uint8_t, niv); + * + * + * while (ndata) { + * if (qcrypto_ivgen_calculate(ivgen, sector, iv, niv, errp) < 0) { + * goto error; + * } + * if (qcrypto_cipher_setiv(cipher, iv, niv, errp) < 0) { + * goto error; + * } + * if (qcrypto_cipher_encrypt(cipher, + * data + (sector * 512), + * data + (sector * 512), + * 512, errp) < 0) { + * goto error; + * } + * sector++; + * ndata -= 512; + * } + * + * g_free(iv); + * qcrypto_ivgen_free(ivgen); + * qcrypto_cipher_free(cipher); + * return 0; + * + *error: + * g_free(iv); + * qcrypto_ivgen_free(ivgen); + * qcrypto_cipher_free(cipher); + * return -1; + * + * + */ + +typedef struct QCryptoIVGen QCryptoIVGen; + +/* See also QCryptoIVGenAlgorithm enum in qapi/crypto.json */ + + +/** + * qcrypto_ivgen_new: + * @alg: the initialization vector generation algorithm + * @cipheralg: the cipher algorithm or 0 + * @hash: the hash algorithm or 0 + * @key: the encryption key or NULL + * @nkey: the size of @key in bytes + * + * Create a new initialization vector generator that uses + * the algorithm @alg. Whether the remaining parameters + * are required or not depends on the choice of @alg + * requested. + * + * - QCRYPTO_IVGEN_ALG_PLAIN + * + * The IVs are generated by the 32-bit truncated sector + * number. This should never be used for block devices + * that are larger than 2^32 sectors in size. + * All the other parameters are unused. + * + * - QCRYPTO_IVGEN_ALG_PLAIN64 + * + * The IVs are generated by the 64-bit sector number. + * All the other parameters are unused. + * + * - QCRYPTO_IVGEN_ALG_ESSIV: + * + * The IVs are generated by encrypting the 64-bit sector + * number with a hash of an encryption key. The @cipheralg, + * @hash, @key and @nkey parameters are all required. + * + * Returns: a new IV generator, or NULL on error + */ +QCryptoIVGen *qcrypto_ivgen_new(QCryptoIVGenAlgorithm alg, + QCryptoCipherAlgorithm cipheralg, + QCryptoHashAlgorithm hash, + const uint8_t *key, size_t nkey, + Error **errp); + +/** + * qcrypto_ivgen_calculate: + * @ivgen: the IV generator object + * @sector: the 64-bit sector number + * @iv: a pre-allocated buffer to hold the generated IV + * @niv: the number of bytes in @iv + * @errp: pointer to a NULL-initialized error object + * + * Calculate a new initialiation vector for the data + * to be stored in sector @sector. The IV will be + * written into the buffer @iv of size @niv. + * + * Returns: 0 on success, -1 on error + */ +int qcrypto_ivgen_calculate(QCryptoIVGen *ivgen, + uint64_t sector, + uint8_t *iv, size_t niv, + Error **errp); + + +/** + * qcrypto_ivgen_get_algorithm: + * @ivgen: the IV generator object + * + * Get the algorithm used by this IV generator + * + * Returns: the IV generator algorithm + */ +QCryptoIVGenAlgorithm qcrypto_ivgen_get_algorithm(QCryptoIVGen *ivgen); + + +/** + * qcrypto_ivgen_get_cipher: + * @ivgen: the IV generator object + * + * Get the cipher algorithm used by this IV generator (if + * applicable) + * + * Returns: the cipher algorithm + */ +QCryptoCipherAlgorithm qcrypto_ivgen_get_cipher(QCryptoIVGen *ivgen); + + +/** + * qcrypto_ivgen_get_hash: + * @ivgen: the IV generator object + * + * Get the hash algorithm used by this IV generator (if + * applicable) + * + * Returns: the hash algorithm + */ +QCryptoHashAlgorithm qcrypto_ivgen_get_hash(QCryptoIVGen *ivgen); + + +/** + * qcrypto_ivgen_free: + * @ivgen: the IV generator object + * + * Release all resources associated with @ivgen, or a no-op + * if @ivgen is NULL + */ +void qcrypto_ivgen_free(QCryptoIVGen *ivgen); + +#endif /* QCRYPTO_IVGEN_H__ */ diff --git a/qemu/include/crypto/pbkdf.h b/qemu/include/crypto/pbkdf.h new file mode 100644 index 000000000..58a1fe62a --- /dev/null +++ b/qemu/include/crypto/pbkdf.h @@ -0,0 +1,152 @@ +/* + * QEMU Crypto PBKDF support (Password-Based Key Derivation Function) + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QCRYPTO_PBKDF_H__ +#define QCRYPTO_PBKDF_H__ + +#include "crypto/hash.h" + +/** + * This module provides an interface to the PBKDF2 algorithm + * + * https://en.wikipedia.org/wiki/PBKDF2 + * + * + * Generating an AES encryption key from a user password + * + * #include "crypto/cipher.h" + * #include "crypto/random.h" + * #include "crypto/pbkdf.h" + * + * .... + * + * char *password = "a-typical-awful-user-password"; + * size_t nkey = qcrypto_cipher_get_key_len(QCRYPTO_CIPHER_ALG_AES_128); + * uint8_t *salt = g_new0(uint8_t, nkey); + * uint8_t *key = g_new0(uint8_t, nkey); + * int iterations; + * QCryptoCipher *cipher; + * + * if (qcrypto_random_bytes(salt, nkey, errp) < 0) { + * g_free(key); + * g_free(salt); + * return -1; + * } + * + * iterations = qcrypto_pbkdf2_count_iters(QCRYPTO_HASH_ALG_SHA256, + * (const uint8_t *)password, + * strlen(password), + * salt, nkey, errp); + * if (iterations < 0) { + * g_free(key); + * g_free(salt); + * return -1; + * } + * + * if (qcrypto_pbkdf2(QCRYPTO_HASH_ALG_SHA256, + * (const uint8_t *)password, strlen(password), + * salt, nkey, iterations, key, nkey, errp) < 0) { + * g_free(key); + * g_free(salt); + * return -1; + * } + * + * g_free(salt); + * + * cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_128, + * QCRYPTO_CIPHER_MODE_ECB, + * key, nkey, errp); + * g_free(key); + * + * ....encrypt some data... + * + * qcrypto_cipher_free(cipher); + * + * + * + */ + +/** + * qcrypto_pbkdf2_supports: + * @hash: the hash algorithm + * + * Determine if the current build supports the PBKDF2 algorithm + * in combination with the hash @hash. + * + * Returns true if supported, false otherwise + */ +bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash); + + +/** + * qcrypto_pbkdf2: + * @hash: the hash algorithm to use + * @key: the user password / key + * @nkey: the length of @key in bytes + * @salt: a random salt + * @nsalt: length of @salt in bytes + * @iterations: the number of iterations to compute + * @out: pointer to pre-allocated buffer to hold output + * @nout: length of @out in bytes + * @errp: pointer to a NULL-initialized error object + * + * Apply the PBKDF2 algorithm to derive an encryption + * key from a user password provided in @key. The + * @salt parameter is used to perturb the algorithm. + * The @iterations count determines how many times + * the hashing process is run, which influences how + * hard it is to crack the key. The number of @iterations + * should be large enough such that the algorithm takes + * 1 second or longer to derive a key. The derived key + * will be stored in the preallocated buffer @out. + * + * Returns: 0 on success, -1 on error + */ +int qcrypto_pbkdf2(QCryptoHashAlgorithm hash, + const uint8_t *key, size_t nkey, + const uint8_t *salt, size_t nsalt, + unsigned int iterations, + uint8_t *out, size_t nout, + Error **errp); + +/** + * qcrypto_pbkdf2_count_iters: + * @hash: the hash algorithm to use + * @key: the user password / key + * @nkey: the length of @key in bytes + * @salt: a random salt + * @nsalt: length of @salt in bytes + * @errp: pointer to a NULL-initialized error object + * + * Time the PBKDF2 algorithm to determine how many + * iterations are required to derive an encryption + * key from a user password provided in @key in 1 + * second of compute time. The result of this can + * be used as a the @iterations parameter of a later + * call to qcrypto_pbkdf2(). + * + * Returns: number of iterations in 1 second, -1 on error + */ +int qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash, + const uint8_t *key, size_t nkey, + const uint8_t *salt, size_t nsalt, + Error **errp); + +#endif /* QCRYPTO_PBKDF_H__ */ diff --git a/qemu/include/crypto/random.h b/qemu/include/crypto/random.h new file mode 100644 index 000000000..b3021c4ce --- /dev/null +++ b/qemu/include/crypto/random.h @@ -0,0 +1,44 @@ +/* + * QEMU Crypto random number provider + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QCRYPTO_RANDOM_H__ +#define QCRYPTO_RANDOM_H__ + +#include "qemu-common.h" +#include "qapi/error.h" + + +/** + * qcrypto_random_bytes: + * @buf: the buffer to fill + * @buflen: length of @buf in bytes + * @errp: pointer to a NULL-initialized error object + * + * Fill @buf with @buflen bytes of cryptographically strong + * random data + * + * Returns 0 on sucess, -1 on error + */ +int qcrypto_random_bytes(uint8_t *buf, + size_t buflen, + Error **errp); + + +#endif /* QCRYPTO_RANDOM_H__ */ diff --git a/qemu/include/crypto/secret.h b/qemu/include/crypto/secret.h new file mode 100644 index 000000000..b7392c6ba --- /dev/null +++ b/qemu/include/crypto/secret.h @@ -0,0 +1,146 @@ +/* + * QEMU crypto secret support + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QCRYPTO_SECRET_H__ +#define QCRYPTO_SECRET_H__ + +#include "qom/object.h" + +#define TYPE_QCRYPTO_SECRET "secret" +#define QCRYPTO_SECRET(obj) \ + OBJECT_CHECK(QCryptoSecret, (obj), TYPE_QCRYPTO_SECRET) + +typedef struct QCryptoSecret QCryptoSecret; +typedef struct QCryptoSecretClass QCryptoSecretClass; + +/** + * QCryptoSecret: + * + * The QCryptoSecret object provides storage of secrets, + * which may be user passwords, encryption keys or any + * other kind of sensitive data that is represented as + * a sequence of bytes. + * + * The sensitive data associated with the secret can + * be provided directly via the 'data' property, or + * indirectly via the 'file' property. In the latter + * case there is support for file descriptor passing + * via the usual /dev/fdset/NN syntax that QEMU uses. + * + * The data for a secret can be provided in two formats, + * either as a UTF-8 string (the default), or as base64 + * encoded 8-bit binary data. The latter is appropriate + * for raw encryption keys, while the former is appropriate + * for user entered passwords. + * + * The data may be optionally encrypted with AES-256-CBC, + * and the decryption key provided by another + * QCryptoSecret instance identified by the 'keyid' + * property. When passing sensitive data directly + * via the 'data' property it is strongly recommended + * to use the AES encryption facility to prevent the + * sensitive data being exposed in the process listing + * or system log files. + * + * Providing data directly, insecurely (suitable for + * ad hoc developer testing only) + * + * $QEMU -object secret,id=sec0,data=letmein + * + * Providing data indirectly: + * + * # printf "letmein" > password.txt + * # $QEMU \ + * -object secret,id=sec0,file=password.txt + * + * Using a master encryption key with data. + * + * The master key needs to be created as 32 secure + * random bytes (optionally base64 encoded) + * + * # openssl rand -base64 32 > key.b64 + * # KEY=$(base64 -d key.b64 | hexdump -v -e '/1 "%02X"') + * + * Each secret to be encrypted needs to have a random + * initialization vector generated. These do not need + * to be kept secret + * + * # openssl rand -base64 16 > iv.b64 + * # IV=$(base64 -d iv.b64 | hexdump -v -e '/1 "%02X"') + * + * A secret to be defined can now be encrypted + * + * # SECRET=$(printf "letmein" | + * openssl enc -aes-256-cbc -a -K $KEY -iv $IV) + * + * When launching QEMU, create a master secret pointing + * to key.b64 and specify that to be used to decrypt + * the user password + * + * # $QEMU \ + * -object secret,id=secmaster0,format=base64,file=key.b64 \ + * -object secret,id=sec0,keyid=secmaster0,format=base64,\ + * data=$SECRET,iv=$(. + * + */ + +#ifndef QCRYPTO_TLSCRED_H__ +#define QCRYPTO_TLSCRED_H__ + +#include "qom/object.h" + +#ifdef CONFIG_GNUTLS +#include +#endif + +#define TYPE_QCRYPTO_TLS_CREDS "tls-creds" +#define QCRYPTO_TLS_CREDS(obj) \ + OBJECT_CHECK(QCryptoTLSCreds, (obj), TYPE_QCRYPTO_TLS_CREDS) + +typedef struct QCryptoTLSCreds QCryptoTLSCreds; +typedef struct QCryptoTLSCredsClass QCryptoTLSCredsClass; + +#define QCRYPTO_TLS_CREDS_DH_PARAMS "dh-params.pem" + + +/** + * QCryptoTLSCreds: + * + * The QCryptoTLSCreds object is an abstract base for different + * types of TLS handshake credentials. Most commonly the + * QCryptoTLSCredsX509 subclass will be used to provide x509 + * certificate credentials. + */ + +struct QCryptoTLSCreds { + Object parent_obj; + char *dir; + QCryptoTLSCredsEndpoint endpoint; +#ifdef CONFIG_GNUTLS + gnutls_dh_params_t dh_params; +#endif + bool verifyPeer; +}; + + +struct QCryptoTLSCredsClass { + ObjectClass parent_class; +}; + + +#endif /* QCRYPTO_TLSCRED_H__ */ + diff --git a/qemu/include/crypto/tlscredsanon.h b/qemu/include/crypto/tlscredsanon.h new file mode 100644 index 000000000..d3976b84b --- /dev/null +++ b/qemu/include/crypto/tlscredsanon.h @@ -0,0 +1,112 @@ +/* + * QEMU crypto TLS anonymous credential support + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QCRYPTO_TLSCRED_ANON_H__ +#define QCRYPTO_TLSCRED_ANON_H__ + +#include "crypto/tlscreds.h" + +#define TYPE_QCRYPTO_TLS_CREDS_ANON "tls-creds-anon" +#define QCRYPTO_TLS_CREDS_ANON(obj) \ + OBJECT_CHECK(QCryptoTLSCredsAnon, (obj), TYPE_QCRYPTO_TLS_CREDS_ANON) + + +typedef struct QCryptoTLSCredsAnon QCryptoTLSCredsAnon; +typedef struct QCryptoTLSCredsAnonClass QCryptoTLSCredsAnonClass; + +/** + * QCryptoTLSCredsAnon: + * + * The QCryptoTLSCredsAnon object provides a representation + * of anonymous credentials used perform a TLS handshake. + * This is primarily provided for backwards compatibility and + * its use is discouraged as it has poor security characteristics + * due to lacking MITM attack protection amongst other problems. + * + * This is a user creatable object, which can be instantiated + * via object_new_propv(): + * + * + * Creating anonymous TLS credential objects in code + * + * Object *obj; + * Error *err = NULL; + * obj = object_new_propv(TYPE_QCRYPTO_TLS_CREDS_ANON, + * "tlscreds0", + * &err, + * "endpoint", "server", + * "dir", "/path/x509/cert/dir", + * "verify-peer", "yes", + * NULL); + * + * + * + * Or via QMP: + * + * + * Creating anonymous TLS credential objects via QMP + * + * { + * "execute": "object-add", "arguments": { + * "id": "tlscreds0", + * "qom-type": "tls-creds-anon", + * "props": { + * "endpoint": "server", + * "dir": "/path/to/x509/cert/dir", + * "verify-peer": false + * } + * } + * } + * + * + * + * + * Or via the CLI: + * + * + * Creating anonymous TLS credential objects via CLI + * + * qemu-system-x86_64 -object tls-creds-anon,id=tlscreds0,\ + * endpoint=server,verify-peer=off,\ + * dir=/path/to/x509/certdir/ + * + * + * + */ + + +struct QCryptoTLSCredsAnon { + QCryptoTLSCreds parent_obj; +#ifdef CONFIG_GNUTLS + union { + gnutls_anon_server_credentials_t server; + gnutls_anon_client_credentials_t client; + } data; +#endif +}; + + +struct QCryptoTLSCredsAnonClass { + QCryptoTLSCredsClass parent_class; +}; + + +#endif /* QCRYPTO_TLSCRED_H__ */ + diff --git a/qemu/include/crypto/tlscredsx509.h b/qemu/include/crypto/tlscredsx509.h new file mode 100644 index 000000000..25796d7de --- /dev/null +++ b/qemu/include/crypto/tlscredsx509.h @@ -0,0 +1,114 @@ +/* + * QEMU crypto TLS x509 credential support + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QCRYPTO_TLSCRED_X509_H__ +#define QCRYPTO_TLSCRED_X509_H__ + +#include "crypto/tlscreds.h" + +#define TYPE_QCRYPTO_TLS_CREDS_X509 "tls-creds-x509" +#define QCRYPTO_TLS_CREDS_X509(obj) \ + OBJECT_CHECK(QCryptoTLSCredsX509, (obj), TYPE_QCRYPTO_TLS_CREDS_X509) + +typedef struct QCryptoTLSCredsX509 QCryptoTLSCredsX509; +typedef struct QCryptoTLSCredsX509Class QCryptoTLSCredsX509Class; + +#define QCRYPTO_TLS_CREDS_X509_CA_CERT "ca-cert.pem" +#define QCRYPTO_TLS_CREDS_X509_CA_CRL "ca-crl.pem" +#define QCRYPTO_TLS_CREDS_X509_SERVER_KEY "server-key.pem" +#define QCRYPTO_TLS_CREDS_X509_SERVER_CERT "server-cert.pem" +#define QCRYPTO_TLS_CREDS_X509_CLIENT_KEY "client-key.pem" +#define QCRYPTO_TLS_CREDS_X509_CLIENT_CERT "client-cert.pem" + + +/** + * QCryptoTLSCredsX509: + * + * The QCryptoTLSCredsX509 object provides a representation + * of x509 credentials used to perform a TLS handshake. + * + * This is a user creatable object, which can be instantiated + * via object_new_propv(): + * + * + * Creating x509 TLS credential objects in code + * + * Object *obj; + * Error *err = NULL; + * obj = object_new_propv(TYPE_QCRYPTO_TLS_CREDS_X509, + * "tlscreds0", + * &err, + * "endpoint", "server", + * "dir", "/path/x509/cert/dir", + * "verify-peer", "yes", + * NULL); + * + * + * + * Or via QMP: + * + * + * Creating x509 TLS credential objects via QMP + * + * { + * "execute": "object-add", "arguments": { + * "id": "tlscreds0", + * "qom-type": "tls-creds-x509", + * "props": { + * "endpoint": "server", + * "dir": "/path/to/x509/cert/dir", + * "verify-peer": false + * } + * } + * } + * + * + * + * + * Or via the CLI: + * + * + * Creating x509 TLS credential objects via CLI + * + * qemu-system-x86_64 -object tls-creds-x509,id=tlscreds0,\ + * endpoint=server,verify-peer=off,\ + * dir=/path/to/x509/certdir/ + * + * + * + */ + +struct QCryptoTLSCredsX509 { + QCryptoTLSCreds parent_obj; +#ifdef CONFIG_GNUTLS + gnutls_certificate_credentials_t data; +#endif + bool sanityCheck; + char *passwordid; +}; + + +struct QCryptoTLSCredsX509Class { + QCryptoTLSCredsClass parent_class; +}; + + +#endif /* QCRYPTO_TLSCRED_X509_H__ */ + diff --git a/qemu/include/crypto/tlssession.h b/qemu/include/crypto/tlssession.h new file mode 100644 index 000000000..c1bad9e4f --- /dev/null +++ b/qemu/include/crypto/tlssession.h @@ -0,0 +1,322 @@ +/* + * QEMU crypto TLS session support + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QCRYPTO_TLS_SESSION_H__ +#define QCRYPTO_TLS_SESSION_H__ + +#include "crypto/tlscreds.h" + +/** + * QCryptoTLSSession: + * + * The QCryptoTLSSession object encapsulates the + * logic to integrate with a TLS providing library such + * as GNUTLS, to setup and run TLS sessions. + * + * The API is designed such that it has no assumption about + * the type of transport it is running over. It may be a + * traditional TCP socket, or something else entirely. The + * only requirement is a full-duplex stream of some kind. + * + * + * Using TLS session objects + * + * static ssize_t mysock_send(const char *buf, size_t len, + * void *opaque) + * { + * int fd = GPOINTER_TO_INT(opaque); + * + * return write(*fd, buf, len); + * } + * + * static ssize_t mysock_recv(const char *buf, size_t len, + * void *opaque) + * { + * int fd = GPOINTER_TO_INT(opaque); + * + * return read(*fd, buf, len); + * } + * + * static int mysock_run_tls(int sockfd, + * QCryptoTLSCreds *creds, + * Error *errp) + * { + * QCryptoTLSSession *sess; + * + * sess = qcrypto_tls_session_new(creds, + * "vnc.example.com", + * NULL, + * QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, + * errp); + * if (sess == NULL) { + * return -1; + * } + * + * qcrypto_tls_session_set_callbacks(sess, + * mysock_send, + * mysock_recv + * GINT_TO_POINTER(fd)); + * + * while (1) { + * if (qcrypto_tls_session_handshake(sess, errp) < 0) { + * qcrypto_tls_session_free(sess); + * return -1; + * } + * + * switch(qcrypto_tls_session_get_handshake_status(sess)) { + * case QCRYPTO_TLS_HANDSHAKE_COMPLETE: + * if (qcrypto_tls_session_check_credentials(sess, errp) < )) { + * qcrypto_tls_session_free(sess); + * return -1; + * } + * goto done; + * case QCRYPTO_TLS_HANDSHAKE_RECVING: + * ...wait for GIO_IN event on fd... + * break; + * case QCRYPTO_TLS_HANDSHAKE_SENDING: + * ...wait for GIO_OUT event on fd... + * break; + * } + * } + * done: + * + * ....send/recv payload data on sess... + * + * qcrypto_tls_session_free(sess): + * } + * + * + */ + +typedef struct QCryptoTLSSession QCryptoTLSSession; + + +/** + * qcrypto_tls_session_new: + * @creds: pointer to a TLS credentials object + * @hostname: optional hostname to validate + * @aclname: optional ACL to validate peer credentials against + * @endpoint: role of the TLS session, client or server + * @errp: pointer to a NULL-initialized error object + * + * Create a new TLS session object that will be used to + * negotiate a TLS session over an arbitrary data channel. + * The session object can operate as either the server or + * client, according to the value of the @endpoint argument. + * + * For clients, the @hostname parameter should hold the full + * unmodified hostname as requested by the user. This will + * be used to verify the against the hostname reported in + * the server's credentials (aka x509 certificate). + * + * The @aclname parameter (optionally) specifies the name + * of an access control list that will be used to validate + * the peer's credentials. For x509 credentials, the ACL + * will be matched against the CommonName shown in the peer's + * certificate. If the session is acting as a server, setting + * an ACL will require that the client provide a validate + * x509 client certificate. + * + * After creating the session object, the I/O callbacks + * must be set using the qcrypto_tls_session_set_callbacks() + * method. A TLS handshake sequence must then be completed + * using qcrypto_tls_session_handshake(), before payload + * data is permitted to be sent/received. + * + * The session object must be released by calling + * qcrypto_tls_session_free() when no longer required + * + * Returns: a TLS session object, or NULL on error. + */ +QCryptoTLSSession *qcrypto_tls_session_new(QCryptoTLSCreds *creds, + const char *hostname, + const char *aclname, + QCryptoTLSCredsEndpoint endpoint, + Error **errp); + +/** + * qcrypto_tls_session_free: + * @sess: the TLS session object + * + * Release all memory associated with the TLS session + * object previously allocated by qcrypto_tls_session_new() + */ +void qcrypto_tls_session_free(QCryptoTLSSession *sess); + +/** + * qcrypto_tls_session_check_credentials: + * @sess: the TLS session object + * @errp: pointer to a NULL-initialized error object + * + * Validate the peer's credentials after a successful + * TLS handshake. It is an error to call this before + * qcrypto_tls_session_get_handshake_status() returns + * QCRYPTO_TLS_HANDSHAKE_COMPLETE + * + * Returns 0 if the credentials validated, -1 on error + */ +int qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess, + Error **errp); + +typedef ssize_t (*QCryptoTLSSessionWriteFunc)(const char *buf, + size_t len, + void *opaque); +typedef ssize_t (*QCryptoTLSSessionReadFunc)(char *buf, + size_t len, + void *opaque); + +/** + * qcrypto_tls_session_set_callbacks: + * @sess: the TLS session object + * @writeFunc: callback for sending data + * @readFunc: callback to receiving data + * @opaque: data to pass to callbacks + * + * Sets the callback functions that are to be used for sending + * and receiving data on the underlying data channel. Typically + * the callbacks to write/read to/from a TCP socket, but there + * is no assumption made about the type of channel used. + * + * The @writeFunc callback will be passed the encrypted + * data to send to the remote peer. + * + * The @readFunc callback will be passed a pointer to fill + * with encrypted data received from the remote peer + */ +void qcrypto_tls_session_set_callbacks(QCryptoTLSSession *sess, + QCryptoTLSSessionWriteFunc writeFunc, + QCryptoTLSSessionReadFunc readFunc, + void *opaque); + +/** + * qcrypto_tls_session_write: + * @sess: the TLS session object + * @buf: the plain text to send + * @len: the length of @buf + * + * Encrypt @len bytes of the data in @buf and send + * it to the remote peer using the callback previously + * registered with qcrypto_tls_session_set_callbacks() + * + * It is an error to call this before + * qcrypto_tls_session_get_handshake_status() returns + * QCRYPTO_TLS_HANDSHAKE_COMPLETE + * + * Returns: the number of bytes sent, or -1 on error + */ +ssize_t qcrypto_tls_session_write(QCryptoTLSSession *sess, + const char *buf, + size_t len); + +/** + * qcrypto_tls_session_read: + * @sess: the TLS session object + * @buf: to fill with plain text received + * @len: the length of @buf + * + * Receive up to @len bytes of data from the remote peer + * using the callback previously registered with + * qcrypto_tls_session_set_callbacks(), decrypt it and + * store it in @buf. + * + * It is an error to call this before + * qcrypto_tls_session_get_handshake_status() returns + * QCRYPTO_TLS_HANDSHAKE_COMPLETE + * + * Returns: the number of bytes received, or -1 on error + */ +ssize_t qcrypto_tls_session_read(QCryptoTLSSession *sess, + char *buf, + size_t len); + +/** + * qcrypto_tls_session_handshake: + * @sess: the TLS session object + * @errp: pointer to a NULL-initialized error object + * + * Start, or continue, a TLS handshake sequence. If + * the underlying data channel is non-blocking, then + * this method may return control before the handshake + * is complete. On non-blocking channels the + * qcrypto_tls_session_get_handshake_status() method + * should be used to determine whether the handshake + * has completed, or is waiting to send or receive + * data. In the latter cases, the caller should setup + * an event loop watch and call this method again + * once the underlying data channel is ready to read + * or write again + */ +int qcrypto_tls_session_handshake(QCryptoTLSSession *sess, + Error **errp); + +typedef enum { + QCRYPTO_TLS_HANDSHAKE_COMPLETE, + QCRYPTO_TLS_HANDSHAKE_SENDING, + QCRYPTO_TLS_HANDSHAKE_RECVING, +} QCryptoTLSSessionHandshakeStatus; + +/** + * qcrypto_tls_session_get_handshake_status: + * @sess: the TLS session object + * + * Check the status of the TLS handshake. This + * is used with non-blocking data channels to + * determine whether the handshake is waiting + * to send or receive further data to/from the + * remote peer. + * + * Once this returns QCRYPTO_TLS_HANDSHAKE_COMPLETE + * it is permitted to send/receive payload data on + * the channel + */ +QCryptoTLSSessionHandshakeStatus +qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess); + +/** + * qcrypto_tls_session_get_key_size: + * @sess: the TLS session object + * @errp: pointer to a NULL-initialized error object + * + * Check the size of the data channel encryption key + * + * Returns: the length in bytes of the encryption key + * or -1 on error + */ +int qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, + Error **errp); + +/** + * qcrypto_tls_session_get_peer_name: + * @sess: the TLS session object + * + * Get the identified name of the remote peer. If the + * TLS session was negotiated using x509 certificate + * credentials, this will return the CommonName from + * the peer's certificate. If no identified name is + * available it will return NULL. + * + * The returned data must be released with g_free() + * when no longer required. + * + * Returns: the peer's name or NULL. + */ +char *qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess); + +#endif /* QCRYPTO_TLS_SESSION_H__ */ diff --git a/qemu/include/crypto/xts.h b/qemu/include/crypto/xts.h new file mode 100644 index 000000000..c2924d8ba --- /dev/null +++ b/qemu/include/crypto/xts.h @@ -0,0 +1,86 @@ +/* + * QEMU Crypto XTS cipher mode + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + * This code is originally derived from public domain / WTFPL code in + * LibTomCrypt crytographic library http://libtom.org. The XTS code + * was donated by Elliptic Semiconductor Inc (www.ellipticsemi.com) + * to the LibTom Projects + * + */ + + +#ifndef QCRYPTO_XTS_H_ +#define QCRYPTO_XTS_H_ + +#include "qemu-common.h" +#include "qapi/error.h" + + +#define XTS_BLOCK_SIZE 16 + +typedef void xts_cipher_func(const void *ctx, + size_t length, + uint8_t *dst, + const uint8_t *src); + +/** + * xts_decrypt: + * @datactx: the cipher context for data decryption + * @tweakctx: the cipher context for tweak decryption + * @encfunc: the cipher function for encryption + * @decfunc: the cipher function for decryption + * @iv: the initialization vector tweak of XTS_BLOCK_SIZE bytes + * @length: the length of @dst and @src + * @dst: buffer to hold the decrypted plaintext + * @src: buffer providing the ciphertext + * + * Decrypts @src into @dst + */ +void xts_decrypt(const void *datactx, + const void *tweakctx, + xts_cipher_func *encfunc, + xts_cipher_func *decfunc, + uint8_t *iv, + size_t length, + uint8_t *dst, + const uint8_t *src); + +/** + * xts_decrypt: + * @datactx: the cipher context for data encryption + * @tweakctx: the cipher context for tweak encryption + * @encfunc: the cipher function for encryption + * @decfunc: the cipher function for decryption + * @iv: the initialization vector tweak of XTS_BLOCK_SIZE bytes + * @length: the length of @dst and @src + * @dst: buffer to hold the encrypted ciphertext + * @src: buffer providing the plaintext + * + * Decrypts @src into @dst + */ +void xts_encrypt(const void *datactx, + const void *tweakctx, + xts_cipher_func *encfunc, + xts_cipher_func *decfunc, + uint8_t *iv, + size_t length, + uint8_t *dst, + const uint8_t *src); + + +#endif /* QCRYPTO_XTS_H_ */ diff --git a/qemu/include/elf.h b/qemu/include/elf.h index 4afd474d5..28d448bbc 100644 --- a/qemu/include/elf.h +++ b/qemu/include/elf.h @@ -1,7 +1,6 @@ #ifndef _QEMU_ELF_H #define _QEMU_ELF_H -#include /* 32-bit ELF base types. */ typedef uint32_t Elf32_Addr; @@ -133,6 +132,11 @@ typedef int64_t Elf64_Sxword; #define EM_AARCH64 183 +#define EM_TILEGX 191 /* TILE-Gx */ + +#define EM_MOXIE 223 /* Moxie processor family */ +#define EM_MOXIE_OLD 0xFEED + /* This is the info that is needed to parse the dynamic section of the file */ #define DT_NULL 0 #define DT_NEEDED 1 @@ -1466,6 +1470,11 @@ typedef struct elf64_shdr { #define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ #define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ #define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ +#define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */ +#define NT_ARM_TLS 0x401 /* ARM TLS register */ +#define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */ +#define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */ +#define NT_ARM_SYSTEM_CALL 0x404 /* ARM system call number */ /* Note header in a PT_NOTE section */ diff --git a/qemu/include/exec/cpu-all.h b/qemu/include/exec/cpu-all.h index ea6a9a667..08e5093d0 100644 --- a/qemu/include/exec/cpu-all.h +++ b/qemu/include/exec/cpu-all.h @@ -154,24 +154,16 @@ static inline void tswap64s(uint64_t *s) /* MMU memory access macros */ #if defined(CONFIG_USER_ONLY) -#include #include "exec/user/abitypes.h" /* On some host systems the guest address space is reserved on the host. * This allows the guest address space to be offset to a convenient location. */ -#if defined(CONFIG_USE_GUEST_BASE) extern unsigned long guest_base; extern int have_guest_base; extern unsigned long reserved_va; -#define GUEST_BASE guest_base -#define RESERVED_VA reserved_va -#else -#define GUEST_BASE 0ul -#define RESERVED_VA 0ul -#endif -#define GUEST_ADDR_MAX (RESERVED_VA ? RESERVED_VA : \ +#define GUEST_ADDR_MAX (reserved_va ? reserved_va : \ (1ul << TARGET_VIRT_ADDR_SPACE_BITS) - 1) #endif @@ -181,11 +173,13 @@ extern unsigned long reserved_va; #define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1) #define TARGET_PAGE_ALIGN(addr) (((addr) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK) -/* ??? These should be the larger of uintptr_t and target_ulong. */ +/* Using intptr_t ensures that qemu_*_page_mask is sign-extended even + * when intptr_t is 32-bit and we are aligning a long long. + */ extern uintptr_t qemu_real_host_page_size; -extern uintptr_t qemu_real_host_page_mask; +extern intptr_t qemu_real_host_page_mask; extern uintptr_t qemu_host_page_size; -extern uintptr_t qemu_host_page_mask; +extern intptr_t qemu_host_page_mask; #define HOST_PAGE_ALIGN(addr) (((addr) + qemu_host_page_size - 1) & qemu_host_page_mask) #define REAL_HOST_PAGE_ALIGN(addr) (((addr) + qemu_real_host_page_size - 1) & \ @@ -273,44 +267,6 @@ CPUArchState *cpu_copy(CPUArchState *env); #if !defined(CONFIG_USER_ONLY) -/* memory API */ - -typedef struct RAMBlock RAMBlock; - -struct RAMBlock { - struct rcu_head rcu; - struct MemoryRegion *mr; - uint8_t *host; - ram_addr_t offset; - ram_addr_t used_length; - ram_addr_t max_length; - void (*resized)(const char*, uint64_t length, void *host); - uint32_t flags; - /* Protected by iothread lock. */ - char idstr[256]; - /* RCU-enabled, writes protected by the ramlist lock */ - QLIST_ENTRY(RAMBlock) next; - int fd; -}; - -static inline void *ramblock_ptr(RAMBlock *block, ram_addr_t offset) -{ - assert(offset < block->used_length); - assert(block->host); - return (char *)block->host + offset; -} - -typedef struct RAMList { - QemuMutex mutex; - /* Protected by the iothread lock. */ - unsigned long *dirty_memory[DIRTY_MEMORY_NUM]; - RAMBlock *mru_block; - /* RCU-enabled, writes protected by the ramlist lock. */ - QLIST_HEAD(, RAMBlock) blocks; - uint32_t version; -} RAMList; -extern RAMList ram_list; - /* Flags stored in the low bits of the TLB virtual address. These are defined so that fast path ram access is all zeros. */ /* Zero if TLB entry is valid. */ @@ -323,9 +279,6 @@ extern RAMList ram_list; void dump_exec_info(FILE *f, fprintf_function cpu_fprintf); void dump_opcount_info(FILE *f, fprintf_function cpu_fprintf); -ram_addr_t last_ram_offset(void); -void qemu_mutex_lock_ramlist(void); -void qemu_mutex_unlock_ramlist(void); #endif /* !CONFIG_USER_ONLY */ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, diff --git a/qemu/include/exec/cpu-common.h b/qemu/include/exec/cpu-common.h index 9fb1d541d..9e839e50c 100644 --- a/qemu/include/exec/cpu-common.h +++ b/qemu/include/exec/cpu-common.h @@ -14,7 +14,6 @@ #include "qemu/bswap.h" #include "qemu/queue.h" #include "qemu/fprintf-fn.h" -#include "qemu/typedefs.h" /** * CPUListState: @@ -54,7 +53,6 @@ typedef uintptr_t ram_addr_t; #endif extern ram_addr_t ram_size; -ram_addr_t get_current_ram_size(void); /* memory API */ @@ -64,8 +62,12 @@ typedef uint32_t CPUReadMemoryFunc(void *opaque, hwaddr addr); void qemu_ram_remap(ram_addr_t addr, ram_addr_t length); /* This should not be used by devices. */ MemoryRegion *qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr); +RAMBlock *qemu_ram_block_by_name(const char *name); +RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset, + ram_addr_t *ram_addr, ram_addr_t *offset); void qemu_ram_set_idstr(ram_addr_t addr, const char *name, DeviceState *dev); void qemu_ram_unset_idstr(ram_addr_t addr); +const char *qemu_ram_get_idstr(RAMBlock *rb); void cpu_physical_memory_rw(hwaddr addr, uint8_t *buf, int len, int is_write); diff --git a/qemu/include/exec/cpu-defs.h b/qemu/include/exec/cpu-defs.h index 98b9cff31..854e7e356 100644 --- a/qemu/include/exec/cpu-defs.h +++ b/qemu/include/exec/cpu-defs.h @@ -23,9 +23,6 @@ #error cpu.h included from common code #endif -#include "config.h" -#include -#include "qemu/osdep.h" #include "qemu/queue.h" #include "tcg-target.h" #ifndef CONFIG_USER_ONLY @@ -105,17 +102,18 @@ typedef struct CPUTLBEntry { bit 3 : indicates that the entry is invalid bit 2..0 : zero */ - target_ulong addr_read; - target_ulong addr_write; - target_ulong addr_code; - /* Addend to virtual address to get host address. IO accesses - use the corresponding iotlb value. */ - uintptr_t addend; - /* padding to get a power of two size */ - uint8_t dummy[(1 << CPU_TLB_ENTRY_BITS) - - (sizeof(target_ulong) * 3 + - ((-sizeof(target_ulong) * 3) & (sizeof(uintptr_t) - 1)) + - sizeof(uintptr_t))]; + union { + struct { + target_ulong addr_read; + target_ulong addr_write; + target_ulong addr_code; + /* Addend to virtual address to get host address. IO accesses + use the corresponding iotlb value. */ + uintptr_t addend; + }; + /* padding to get a power of two size */ + uint8_t dummy[1 << CPU_TLB_ENTRY_BITS]; + }; } CPUTLBEntry; QEMU_BUILD_BUG_ON(sizeof(CPUTLBEntry) != (1 << CPU_TLB_ENTRY_BITS)); diff --git a/qemu/include/exec/cpu_ldst.h b/qemu/include/exec/cpu_ldst.h index 1239c60f2..b573df53b 100644 --- a/qemu/include/exec/cpu_ldst.h +++ b/qemu/include/exec/cpu_ldst.h @@ -49,20 +49,20 @@ #if defined(CONFIG_USER_ONLY) /* 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)) +#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; \ + unsigned long __guest = (unsigned long)(x) - guest_base; \ (__guest < (1ul << TARGET_VIRT_ADDR_SPACE_BITS)) && \ - (!RESERVED_VA || (__guest < RESERVED_VA)); \ + (!reserved_va || (__guest < reserved_va)); \ }) #endif #define h2g_nocheck(x) ({ \ - unsigned long __ret = (unsigned long)(x) - GUEST_BASE; \ + unsigned long __ret = (unsigned long)(x) - guest_base; \ (abi_ulong)__ret; \ }) @@ -113,25 +113,6 @@ /* The memory helpers for tcg-generated code need tcg_target_long etc. */ #include "tcg.h" -uint8_t helper_ldb_mmu(CPUArchState *env, target_ulong addr, int mmu_idx); -uint16_t helper_ldw_mmu(CPUArchState *env, target_ulong addr, int mmu_idx); -uint32_t helper_ldl_mmu(CPUArchState *env, target_ulong addr, int mmu_idx); -uint64_t helper_ldq_mmu(CPUArchState *env, target_ulong addr, int mmu_idx); - -void helper_stb_mmu(CPUArchState *env, target_ulong addr, - uint8_t val, int mmu_idx); -void helper_stw_mmu(CPUArchState *env, target_ulong addr, - uint16_t val, int mmu_idx); -void helper_stl_mmu(CPUArchState *env, target_ulong addr, - uint32_t val, int mmu_idx); -void helper_stq_mmu(CPUArchState *env, target_ulong addr, - uint64_t val, int mmu_idx); - -uint8_t helper_ldb_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx); -uint16_t helper_ldw_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx); -uint32_t helper_ldl_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx); -uint64_t helper_ldq_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx); - #ifdef MMU_MODE0_SUFFIX #define CPU_MMU_INDEX 0 #define MEMSUFFIX MMU_MODE0_SUFFIX @@ -363,7 +344,7 @@ uint64_t helper_ldq_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx); #endif /* (NB_MMU_MODES > 12) */ /* these access are slower, they must be as rare as possible */ -#define CPU_MMU_INDEX (cpu_mmu_index(env)) +#define CPU_MMU_INDEX (cpu_mmu_index(env, false)) #define MEMSUFFIX _data #define DATA_SIZE 1 #include "exec/cpu_ldst_template.h" @@ -379,7 +360,7 @@ uint64_t helper_ldq_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx); #undef CPU_MMU_INDEX #undef MEMSUFFIX -#define CPU_MMU_INDEX (cpu_mmu_index(env)) +#define CPU_MMU_INDEX (cpu_mmu_index(env, true)) #define MEMSUFFIX _code #define SOFTMMU_CODE_ACCESS diff --git a/qemu/include/exec/cpu_ldst_template.h b/qemu/include/exec/cpu_ldst_template.h index 95ab7504e..3091c0003 100644 --- a/qemu/include/exec/cpu_ldst_template.h +++ b/qemu/include/exec/cpu_ldst_template.h @@ -27,20 +27,24 @@ #define SUFFIX q #define USUFFIX q #define DATA_TYPE uint64_t +#define SHIFT 3 #elif DATA_SIZE == 4 #define SUFFIX l #define USUFFIX l #define DATA_TYPE uint32_t +#define SHIFT 2 #elif DATA_SIZE == 2 #define SUFFIX w #define USUFFIX uw #define DATA_TYPE uint16_t #define DATA_STYPE int16_t +#define SHIFT 1 #elif DATA_SIZE == 1 #define SUFFIX b #define USUFFIX ub #define DATA_TYPE uint8_t #define DATA_STYPE int8_t +#define SHIFT 0 #else #error unsupported data size #endif @@ -54,27 +58,36 @@ #ifdef SOFTMMU_CODE_ACCESS #define ADDR_READ addr_code #define MMUSUFFIX _cmmu +#define URETSUFFIX SUFFIX +#define SRETSUFFIX SUFFIX #else #define ADDR_READ addr_read #define MMUSUFFIX _mmu +#define URETSUFFIX USUFFIX +#define SRETSUFFIX glue(s, SUFFIX) #endif /* generic load/store macros */ static inline RES_TYPE -glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) +glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, + target_ulong ptr, + uintptr_t retaddr) { int page_index; RES_TYPE res; target_ulong addr; int mmu_idx; + TCGMemOpIdx oi; addr = ptr; page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); mmu_idx = CPU_MMU_INDEX; if (unlikely(env->tlb_table[mmu_idx][page_index].ADDR_READ != (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) { - res = glue(glue(helper_ld, SUFFIX), MMUSUFFIX)(env, addr, mmu_idx); + oi = make_memop_idx(SHIFT, mmu_idx); + res = glue(glue(helper_ret_ld, URETSUFFIX), MMUSUFFIX)(env, addr, + oi, retaddr); } else { uintptr_t hostaddr = addr + env->tlb_table[mmu_idx][page_index].addend; res = glue(glue(ld, USUFFIX), _p)((uint8_t *)hostaddr); @@ -82,27 +95,43 @@ glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) return res; } +static inline RES_TYPE +glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) +{ + return glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(env, ptr, 0); +} + #if DATA_SIZE <= 2 static inline int -glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) +glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, + target_ulong ptr, + uintptr_t retaddr) { int res, page_index; target_ulong addr; int mmu_idx; + TCGMemOpIdx oi; addr = ptr; page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); mmu_idx = CPU_MMU_INDEX; if (unlikely(env->tlb_table[mmu_idx][page_index].ADDR_READ != (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) { - res = (DATA_STYPE)glue(glue(helper_ld, SUFFIX), - MMUSUFFIX)(env, addr, mmu_idx); + oi = make_memop_idx(SHIFT, mmu_idx); + res = (DATA_STYPE)glue(glue(helper_ret_ld, SRETSUFFIX), + MMUSUFFIX)(env, addr, oi, retaddr); } else { uintptr_t hostaddr = addr + env->tlb_table[mmu_idx][page_index].addend; res = glue(glue(lds, SUFFIX), _p)((uint8_t *)hostaddr); } return res; } + +static inline int +glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) +{ + return glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(env, ptr, 0); +} #endif #ifndef SOFTMMU_CODE_ACCESS @@ -110,25 +139,36 @@ glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) /* generic store macro */ static inline void -glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr, - RES_TYPE v) +glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, + target_ulong ptr, + RES_TYPE v, uintptr_t retaddr) { int page_index; target_ulong addr; int mmu_idx; + TCGMemOpIdx oi; addr = ptr; page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); mmu_idx = CPU_MMU_INDEX; if (unlikely(env->tlb_table[mmu_idx][page_index].addr_write != (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) { - glue(glue(helper_st, SUFFIX), MMUSUFFIX)(env, addr, v, mmu_idx); + oi = make_memop_idx(SHIFT, mmu_idx); + glue(glue(helper_ret_st, SUFFIX), MMUSUFFIX)(env, addr, v, oi, + retaddr); } else { uintptr_t hostaddr = addr + env->tlb_table[mmu_idx][page_index].addend; glue(glue(st, SUFFIX), _p)((uint8_t *)hostaddr, v); } } +static inline void +glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr, + RES_TYPE v) +{ + glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(env, ptr, v, 0); +} + #endif /* !SOFTMMU_CODE_ACCESS */ #undef RES_TYPE @@ -139,3 +179,6 @@ glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr, #undef DATA_SIZE #undef MMUSUFFIX #undef ADDR_READ +#undef URETSUFFIX +#undef SRETSUFFIX +#undef SHIFT diff --git a/qemu/include/exec/cpu_ldst_useronly_template.h b/qemu/include/exec/cpu_ldst_useronly_template.h index b3b865fae..040b14743 100644 --- a/qemu/include/exec/cpu_ldst_useronly_template.h +++ b/qemu/include/exec/cpu_ldst_useronly_template.h @@ -56,12 +56,28 @@ glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) return glue(glue(ld, USUFFIX), _p)(g2h(ptr)); } +static inline RES_TYPE +glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, + target_ulong ptr, + uintptr_t retaddr) +{ + return glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(env, ptr); +} + #if DATA_SIZE <= 2 static inline int glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) { return glue(glue(lds, SUFFIX), _p)(g2h(ptr)); } + +static inline int +glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, + target_ulong ptr, + uintptr_t retaddr) +{ + return glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(env, ptr); +} #endif #ifndef CODE_ACCESS @@ -71,6 +87,15 @@ glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr, { glue(glue(st, SUFFIX), _p)(g2h(ptr), v); } + +static inline void +glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, + target_ulong ptr, + RES_TYPE v, + uintptr_t retaddr) +{ + glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(env, ptr, v); +} #endif #undef RES_TYPE diff --git a/qemu/include/exec/cputlb.h b/qemu/include/exec/cputlb.h index 360815e1b..d454c005b 100644 --- a/qemu/include/exec/cputlb.h +++ b/qemu/include/exec/cputlb.h @@ -25,23 +25,7 @@ void tlb_protect_code(ram_addr_t ram_addr); void tlb_unprotect_code(ram_addr_t ram_addr); void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, uintptr_t start, uintptr_t length); -void cpu_tlb_reset_dirty_all(ram_addr_t start1, ram_addr_t length); -void tlb_set_dirty(CPUArchState *env, target_ulong vaddr); extern int tlb_flush_count; -/* exec.c */ -void tb_flush_jmp_cache(CPUState *cpu, target_ulong addr); - -MemoryRegionSection * -address_space_translate_for_iotlb(CPUState *cpu, hwaddr addr, hwaddr *xlat, - hwaddr *plen); -hwaddr memory_region_section_get_iotlb(CPUState *cpu, - MemoryRegionSection *section, - target_ulong vaddr, - hwaddr paddr, hwaddr xlat, - int prot, - target_ulong *address); -bool memory_region_is_unassigned(MemoryRegion *mr); - #endif #endif diff --git a/qemu/include/exec/exec-all.h b/qemu/include/exec/exec-all.h index a6fce04f6..736209505 100644 --- a/qemu/include/exec/exec-all.h +++ b/qemu/include/exec/exec-all.h @@ -62,26 +62,16 @@ typedef struct TranslationBlock TranslationBlock; #define OPC_BUF_SIZE 640 #define OPC_MAX_SIZE (OPC_BUF_SIZE - MAX_OP_PER_INSTR) -/* Maximum size a TCG op can expand to. This is complicated because a - single op may require several host instructions and register reloads. - For now take a wild guess at 192 bytes, which should allow at least - a couple of fixup instructions per argument. */ -#define TCG_MAX_OP_SIZE 192 - #define OPPARAM_BUF_SIZE (OPC_BUF_SIZE * MAX_OPC_PARAM) #include "qemu/log.h" void gen_intermediate_code(CPUArchState *env, struct TranslationBlock *tb); -void gen_intermediate_code_pc(CPUArchState *env, struct TranslationBlock *tb); void restore_state_to_opc(CPUArchState *env, struct TranslationBlock *tb, - int pc_pos); + target_ulong *data); void cpu_gen_init(void); -int cpu_gen_code(CPUArchState *env, struct TranslationBlock *tb, - int *gen_code_size_ptr); bool cpu_restore_state(CPUState *cpu, uintptr_t searched_pc); -void page_size_init(void); void QEMU_NORETURN cpu_resume_from_signal(CPUState *cpu, void *puc); void QEMU_NORETURN cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); @@ -90,20 +80,113 @@ TranslationBlock *tb_gen_code(CPUState *cpu, int cflags); void cpu_exec_init(CPUState *cpu, Error **errp); void QEMU_NORETURN cpu_loop_exit(CPUState *cpu); +void QEMU_NORETURN cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc); #if !defined(CONFIG_USER_ONLY) -bool qemu_in_vcpu_thread(void); -void cpu_reload_memory_map(CPUState *cpu); -void tcg_cpu_address_space_init(CPUState *cpu, AddressSpace *as); +void cpu_reloading_memory_map(void); +/** + * cpu_address_space_init: + * @cpu: CPU to add this address space to + * @as: address space to add + * @asidx: integer index of this address space + * + * Add the specified address space to the CPU's cpu_ases list. + * The address space added with @asidx 0 is the one used for the + * convenience pointer cpu->as. + * The target-specific code which registers ASes is responsible + * for defining what semantics address space 0, 1, 2, etc have. + * + * Before the first call to this function, the caller must set + * cpu->num_ases to the total number of address spaces it needs + * to support. + * + * Note that with KVM only one address space is supported. + */ +void cpu_address_space_init(CPUState *cpu, AddressSpace *as, int asidx); +/** + * cpu_get_address_space: + * @cpu: CPU to get address space from + * @asidx: index identifying which address space to get + * + * Return the requested address space of this CPU. @asidx + * specifies which address space to read. + */ +AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx); /* cputlb.c */ +/** + * tlb_flush_page: + * @cpu: CPU whose TLB should be flushed + * @addr: virtual address of page to be flushed + * + * Flush one page from the TLB of the specified CPU, for all + * MMU indexes. + */ void tlb_flush_page(CPUState *cpu, target_ulong addr); +/** + * tlb_flush: + * @cpu: CPU whose TLB should be flushed + * @flush_global: ignored + * + * Flush the entire TLB for the specified CPU. + * The flush_global flag is in theory an indicator of whether the whole + * TLB should be flushed, or only those entries not marked global. + * In practice QEMU does not implement any global/not global flag for + * TLB entries, and the argument is ignored. + */ void tlb_flush(CPUState *cpu, int flush_global); -void tlb_set_page(CPUState *cpu, target_ulong vaddr, - hwaddr paddr, int prot, - int mmu_idx, target_ulong size); +/** + * tlb_flush_page_by_mmuidx: + * @cpu: CPU whose TLB should be flushed + * @addr: virtual address of page to be flushed + * @...: list of MMU indexes to flush, terminated by a negative value + * + * Flush one page from the TLB of the specified CPU, for the specified + * MMU indexes. + */ +void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, ...); +/** + * tlb_flush_by_mmuidx: + * @cpu: CPU whose TLB should be flushed + * @...: list of MMU indexes to flush, terminated by a negative value + * + * Flush all entries from the TLB of the specified CPU, for the specified + * MMU indexes. + */ +void tlb_flush_by_mmuidx(CPUState *cpu, ...); +/** + * tlb_set_page_with_attrs: + * @cpu: CPU to add this TLB entry for + * @vaddr: virtual address of page to add entry for + * @paddr: physical address of the page + * @attrs: memory transaction attributes + * @prot: access permissions (PAGE_READ/PAGE_WRITE/PAGE_EXEC bits) + * @mmu_idx: MMU index to insert TLB entry for + * @size: size of the page in bytes + * + * Add an entry to this CPU's TLB (a mapping from virtual address + * @vaddr to physical address @paddr) with the specified memory + * transaction attributes. This is generally called by the target CPU + * specific code after it has been called through the tlb_fill() + * entry point and performed a successful page table walk to find + * the physical address and attributes for the virtual address + * which provoked the TLB miss. + * + * At most one entry for a given virtual address is permitted. Only a + * single TARGET_PAGE_SIZE region is mapped; the supplied @size is only + * used by tlb_flush_page. + */ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, hwaddr paddr, MemTxAttrs attrs, int prot, int mmu_idx, target_ulong size); +/* tlb_set_page: + * + * This function is equivalent to calling tlb_set_page_with_attrs() + * with an @attrs argument of MEMTXATTRS_UNSPECIFIED. It's provided + * as a convenience for CPUs which don't use memory transaction attributes. + */ +void tlb_set_page(CPUState *cpu, target_ulong vaddr, + hwaddr paddr, int prot, + int mmu_idx, target_ulong size); void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr); void probe_write(CPUArchState *env, target_ulong addr, int mmu_idx, uintptr_t retaddr); @@ -115,6 +198,15 @@ static inline void tlb_flush_page(CPUState *cpu, target_ulong addr) static inline void tlb_flush(CPUState *cpu, int flush_global) { } + +static inline void tlb_flush_page_by_mmuidx(CPUState *cpu, + target_ulong addr, ...) +{ +} + +static inline void tlb_flush_by_mmuidx(CPUState *cpu, ...) +{ +} #endif #define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ @@ -122,13 +214,14 @@ static inline void tlb_flush(CPUState *cpu, int flush_global) #define CODE_GEN_PHYS_HASH_BITS 15 #define CODE_GEN_PHYS_HASH_SIZE (1 << CODE_GEN_PHYS_HASH_BITS) -/* estimated block size for TB allocation */ -/* XXX: use a per code average code fragment size and modulate it - according to the host CPU */ +/* Estimated block size for TB allocation. */ +/* ??? The following is based on a 2015 survey of x86_64 host output. + Better would seem to be some sort of dynamically sized TB array, + adapting to the block sizes actually being produced. */ #if defined(CONFIG_SOFTMMU) -#define CODE_GEN_AVG_BLOCK_SIZE 128 +#define CODE_GEN_AVG_BLOCK_SIZE 400 #else -#define CODE_GEN_AVG_BLOCK_SIZE 64 +#define CODE_GEN_AVG_BLOCK_SIZE 150 #endif #if defined(__arm__) || defined(_ARCH_PPC) \ @@ -151,10 +244,14 @@ struct TranslationBlock { #define CF_LAST_IO 0x8000 /* Last insn may be an IO access. */ #define CF_NOCACHE 0x10000 /* To be freed after execution */ #define CF_USE_ICOUNT 0x20000 +#define CF_IGNORE_ICOUNT 0x40000 /* Do not generate icount code */ void *tc_ptr; /* pointer to the translated code */ + uint8_t *tc_search; /* pointer to search data */ /* next matching tb for physical address. */ struct TranslationBlock *phys_hash_next; + /* original tb when cflags has CF_NOCACHE */ + struct TranslationBlock *orig_tb; /* first and second physical page containing code. The lower bit of the pointer tells the index in page_next[] */ struct TranslationBlock *page_next[2]; @@ -176,7 +273,7 @@ struct TranslationBlock { struct TranslationBlock *jmp_first; }; -#include "exec/spinlock.h" +#include "qemu/thread.h" typedef struct TBContext TBContext; @@ -186,7 +283,7 @@ struct TBContext { TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE]; int nb_tbs; /* any access to the tbs or the page table must use this lock */ - spinlock_t tb_lock; + QemuMutex tb_lock; /* statistics */ int tb_flush_count; @@ -282,6 +379,11 @@ static inline void tb_add_jump(TranslationBlock *tb, int n, { /* NOTE: this test is only needed for thread safety */ if (!tb->jmp_next[n]) { + qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc, + "Linking TBs %p [" TARGET_FMT_lx + "] index %d -> %p [" TARGET_FMT_lx "]\n", + tb->tc_ptr, tb->pc, n, + tb_next->tc_ptr, tb_next->pc); /* patch the native jump address */ tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc_ptr); @@ -308,20 +410,14 @@ extern uintptr_t tci_tb_ptr; to indicate the compressed mode; subtracting two works around that. It is also the case that there are no host isas that contain a call insn smaller than 4 bytes, so we don't worry about special-casing this. */ -#if defined(CONFIG_TCG_INTERPRETER) -# define GETPC_ADJ 0 -#else -# define GETPC_ADJ 2 -#endif +#define GETPC_ADJ 2 #define GETPC() (GETRA() - GETPC_ADJ) #if !defined(CONFIG_USER_ONLY) -void phys_mem_set_alloc(void *(*alloc)(size_t, uint64_t *align)); - struct MemoryRegion *iotlb_to_region(CPUState *cpu, - hwaddr index); + hwaddr index, MemTxAttrs attrs); void tlb_fill(CPUState *cpu, target_ulong addr, int is_write, int mmu_idx, uintptr_t retaddr); @@ -329,43 +425,44 @@ void tlb_fill(CPUState *cpu, target_ulong addr, int is_write, int mmu_idx, #endif #if defined(CONFIG_USER_ONLY) +void mmap_lock(void); +void mmap_unlock(void); + static inline tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr) { return addr; } #else +static inline void mmap_lock(void) {} +static inline void mmap_unlock(void) {} + /* cputlb.c */ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr); + +void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length); +void tlb_set_dirty(CPUState *cpu, target_ulong vaddr); + +/* exec.c */ +void tb_flush_jmp_cache(CPUState *cpu, target_ulong addr); + +MemoryRegionSection * +address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr, + hwaddr *xlat, hwaddr *plen); +hwaddr memory_region_section_get_iotlb(CPUState *cpu, + MemoryRegionSection *section, + target_ulong vaddr, + hwaddr paddr, hwaddr xlat, + int prot, + target_ulong *address); +bool memory_region_is_unassigned(MemoryRegion *mr); + #endif /* vl.c */ extern int singlestep; -/* cpu-exec.c */ -extern volatile sig_atomic_t exit_request; +/* cpu-exec.c, accessed with atomic_mb_read/atomic_mb_set */ +extern CPUState *tcg_current_cpu; +extern bool exit_request; -/** - * cpu_can_do_io: - * @cpu: The CPU for which to check IO. - * - * Deterministic execution requires that IO only be performed on the last - * instruction of a TB so that interrupts take effect immediately. - * - * Returns: %true if memory-mapped IO is safe, %false otherwise. - */ -static inline bool cpu_can_do_io(CPUState *cpu) -{ - if (!use_icount) { - return true; - } - /* If not executing code then assume we are ok. */ - if (cpu->current_tb == NULL) { - return true; - } - return cpu->can_do_io != 0; -} - -#if !defined(CONFIG_USER_ONLY) -void migration_bitmap_extend(ram_addr_t old, ram_addr_t new); -#endif #endif diff --git a/qemu/include/exec/gdbstub.h b/qemu/include/exec/gdbstub.h index 05f57c243..d9e8cf771 100644 --- a/qemu/include/exec/gdbstub.h +++ b/qemu/include/exec/gdbstub.h @@ -14,7 +14,34 @@ typedef void (*gdb_syscall_complete_cb)(CPUState *cpu, target_ulong ret, target_ulong err); +/** + * gdb_do_syscall: + * @cb: function to call when the system call has completed + * @fmt: gdb syscall format string + * ...: list of arguments to interpolate into @fmt + * + * Send a GDB syscall request. This function will return immediately; + * the callback function will be called later when the remote system + * call has completed. + * + * @fmt should be in the 'call-id,parameter,parameter...' format documented + * for the F request packet in the GDB remote protocol. A limited set of + * printf-style format specifiers is supported: + * %x - target_ulong argument printed in hex + * %lx - 64-bit argument printed in hex + * %s - string pointer (target_ulong) and length (int) pair + */ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...); +/** + * gdb_do_syscallv: + * @cb: function to call when the system call has completed + * @fmt: gdb syscall format string + * @va: arguments to interpolate into @fmt + * + * As gdb_do_syscall, but taking a va_list rather than a variable + * argument list. + */ +void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va); int use_gdb_syscalls(void); void gdb_set_stop_cpu(CPUState *cpu); void gdb_exit(CPUArchState *, int); diff --git a/qemu/include/exec/helper-head.h b/qemu/include/exec/helper-head.h index b009ccb11..ec790432d 100644 --- a/qemu/include/exec/helper-head.h +++ b/qemu/include/exec/helper-head.h @@ -18,7 +18,6 @@ #ifndef DEF_HELPER_H #define DEF_HELPER_H 1 -#include "qemu/osdep.h" #define HELPER(name) glue(helper_, name) diff --git a/qemu/include/exec/log.h b/qemu/include/exec/log.h new file mode 100644 index 000000000..ba1c9b568 --- /dev/null +++ b/qemu/include/exec/log.h @@ -0,0 +1,60 @@ +#ifndef QEMU_EXEC_LOG_H +#define QEMU_EXEC_LOG_H + +#include "qemu/log.h" +#include "qom/cpu.h" +#include "disas/disas.h" + +/* cpu_dump_state() logging functions: */ +/** + * log_cpu_state: + * @cpu: The CPU whose state is to be logged. + * @flags: Flags what to log. + * + * Logs the output of cpu_dump_state(). + */ +static inline void log_cpu_state(CPUState *cpu, int flags) +{ + if (qemu_log_enabled()) { + cpu_dump_state(cpu, qemu_logfile, fprintf, flags); + } +} + +/** + * log_cpu_state_mask: + * @mask: Mask when to log. + * @cpu: The CPU whose state is to be logged. + * @flags: Flags what to log. + * + * Logs the output of cpu_dump_state() if loglevel includes @mask. + */ +static inline void log_cpu_state_mask(int mask, CPUState *cpu, int flags) +{ + if (qemu_loglevel & mask) { + log_cpu_state(cpu, flags); + } +} + +#ifdef NEED_CPU_H +/* disas() and target_disas() to qemu_logfile: */ +static inline void log_target_disas(CPUState *cpu, target_ulong start, + target_ulong len, int flags) +{ + target_disas(qemu_logfile, cpu, start, len, flags); +} + +static inline void log_disas(void *code, unsigned long size) +{ + disas(qemu_logfile, code, size); +} + +#if defined(CONFIG_USER_ONLY) +/* page_dump() output to the log file: */ +static inline void log_page_dump(void) +{ + page_dump(qemu_logfile); +} +#endif +#endif + +#endif diff --git a/qemu/include/exec/memattrs.h b/qemu/include/exec/memattrs.h index f8537a8d9..e60106184 100644 --- a/qemu/include/exec/memattrs.h +++ b/qemu/include/exec/memattrs.h @@ -35,8 +35,8 @@ typedef struct MemTxAttrs { unsigned int secure:1; /* Memory access is usermode (unprivileged) */ unsigned int user:1; - /* Stream ID (for MSI for example) */ - unsigned int stream_id:16; + /* Requester ID (for MSI for example) */ + unsigned int requester_id:16; } MemTxAttrs; /* Bus masters which don't specify any attributes will get this, diff --git a/qemu/include/exec/memory.h b/qemu/include/exec/memory.h index 94d20eae0..e2a3e9953 100644 --- a/qemu/include/exec/memory.h +++ b/qemu/include/exec/memory.h @@ -21,8 +21,6 @@ #define DIRTY_MEMORY_MIGRATION 2 #define DIRTY_MEMORY_NUM 3 /* num of dirty bits */ -#include -#include #include "exec/cpu-common.h" #ifndef CONFIG_USER_ONLY #include "exec/hwaddr.h" @@ -31,7 +29,6 @@ #include "qemu/queue.h" #include "qemu/int128.h" #include "qemu/notify.h" -#include "qapi/error.h" #include "qom/object.h" #include "qemu/rcu.h" @@ -159,27 +156,33 @@ typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd; struct MemoryRegion { Object parent_obj; + /* All fields are private - violators will be prosecuted */ - const MemoryRegionOps *ops; + + /* The following fields should fit in a cache line */ + bool romd_mode; + bool ram; + bool subpage; + bool readonly; /* For RAM regions */ + bool rom_device; + bool flush_coalesced_mmio; + bool global_locking; + uint8_t dirty_log_mask; + RAMBlock *ram_block; + Object *owner; const MemoryRegionIOMMUOps *iommu_ops; + + const MemoryRegionOps *ops; void *opaque; MemoryRegion *container; Int128 size; hwaddr addr; void (*destructor)(MemoryRegion *mr); - ram_addr_t ram_addr; uint64_t align; - bool subpage; bool terminates; - bool romd_mode; - bool ram; bool skip_dump; - bool readonly; /* For RAM regions */ bool enabled; - bool rom_device; bool warning_printed; /* For reservations */ - bool flush_coalesced_mmio; - bool global_locking; uint8_t vga_logging_count; MemoryRegion *alias; hwaddr alias_offset; @@ -189,7 +192,6 @@ struct MemoryRegion { QTAILQ_ENTRY(MemoryRegion) subregions_link; QTAILQ_HEAD(coalesced_ranges, CoalescedMemoryRange) coalesced; const char *name; - uint8_t dirty_log_mask; unsigned ioeventfd_nb; MemoryRegionIoeventfd *ioeventfds; NotifierList iommu_notify; @@ -236,6 +238,8 @@ struct AddressSpace { struct rcu_head rcu; char *name; MemoryRegion *root; + int ref_count; + bool malloced; /* Accessed via RCU. */ struct FlatView *current_map; @@ -324,7 +328,7 @@ void memory_region_unref(MemoryRegion *mr); * @owner: the object that tracks the region's reference count * @ops: a structure containing read and write callbacks to be used when * I/O is performed on the region. - * @opaque: passed to to the read and write callbacks of the @ops structure. + * @opaque: passed to the read and write callbacks of the @ops structure. * @name: used for debugging; not visible to the user or ABI * @size: size of the region. */ @@ -437,6 +441,9 @@ void memory_region_init_alias(MemoryRegion *mr, * memory_region_init_rom_device: Initialize a ROM memory region. Writes are * handled via callbacks. * + * If NULL callbacks pointer is given, then I/O space is not supposed to be + * handled by QEMU itself. Any access via the memory API will cause an abort(). + * * @mr: the #MemoryRegion to be initialized. * @owner: the object that tracks the region's reference count * @ops: callbacks for write access handling. @@ -459,16 +466,21 @@ void memory_region_init_rom_device(MemoryRegion *mr, * A reservation region primariy serves debugging purposes. It claims I/O * space that is not supposed to be handled by QEMU itself. Any access via * the memory API will cause an abort(). + * This function is deprecated. Use memory_region_init_io() with NULL + * callbacks instead. * * @mr: the #MemoryRegion to be initialized * @owner: the object that tracks the region's reference count * @name: used for debugging; not visible to the user or ABI * @size: size of the region. */ -void memory_region_init_reservation(MemoryRegion *mr, - struct Object *owner, +static inline void memory_region_init_reservation(MemoryRegion *mr, + Object *owner, const char *name, - uint64_t size); + uint64_t size) +{ + memory_region_init_io(mr, owner, NULL, mr, name, size); +} /** * memory_region_init_iommu: Initialize a memory region that translates @@ -510,7 +522,10 @@ uint64_t memory_region_size(MemoryRegion *mr); * * @mr: the memory region being queried */ -bool memory_region_is_ram(MemoryRegion *mr); +static inline bool memory_region_is_ram(MemoryRegion *mr) +{ + return mr->ram; +} /** * memory_region_is_skip_dump: check whether a memory region should not be @@ -550,7 +565,11 @@ static inline bool memory_region_is_romd(MemoryRegion *mr) * * @mr: the memory region being queried */ -bool memory_region_is_iommu(MemoryRegion *mr); +static inline bool memory_region_is_iommu(MemoryRegion *mr) +{ + return mr->iommu_ops; +} + /** * memory_region_notify_iommu: notify a change in an IOMMU translation entry. @@ -574,6 +593,19 @@ void memory_region_notify_iommu(MemoryRegion *mr, */ void memory_region_register_iommu_notifier(MemoryRegion *mr, Notifier *n); +/** + * memory_region_iommu_replay: replay existing IOMMU translations to + * a notifier + * + * @mr: the memory region to observe + * @n: the notifier to which to replay iommu mappings + * @granularity: Minimum page granularity to replay notifications for + * @is_write: Whether to treat the replay as a translate "write" + * through the iommu + */ +void memory_region_iommu_replay(MemoryRegion *mr, Notifier *n, + hwaddr granularity, bool is_write); + /** * memory_region_unregister_iommu_notifier: unregister a notifier for * changes to IOMMU translation entries. @@ -619,7 +651,11 @@ uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr); * * @mr: the memory region being queried */ -bool memory_region_is_rom(MemoryRegion *mr); +static inline bool memory_region_is_rom(MemoryRegion *mr) +{ + return mr->ram && mr->readonly; +} + /** * memory_region_get_fd: Get a file descriptor backing a RAM memory region. @@ -635,8 +671,13 @@ int memory_region_get_fd(MemoryRegion *mr); * memory_region_get_ram_ptr: Get a pointer into a RAM memory region. * * Returns a host pointer to a RAM memory region (created with - * memory_region_init_ram() or memory_region_init_ram_ptr()). Use with - * care. + * memory_region_init_ram() or memory_region_init_ram_ptr()). + * + * Use with care; by the time this function returns, the returned pointer is + * not protected by RCU anymore. If the caller is not within an RCU critical + * section and does not hold the iothread lock, it must have other means of + * protecting the pointer, such as a reference to the region that includes + * the incoming ram_addr_t. * * @mr: the memory region being queried. */ @@ -935,9 +976,6 @@ void memory_region_add_subregion_overlap(MemoryRegion *mr, /** * memory_region_get_ram_addr: Get the ram address associated with a memory * region - * - * DO NOT USE THIS FUNCTION. This is a temporary workaround while the Xen - * code is being reworked. */ ram_addr_t memory_region_get_ram_addr(MemoryRegion *mr); @@ -1138,12 +1176,28 @@ MemTxResult memory_region_dispatch_write(MemoryRegion *mr, * address_space_init: initializes an address space * * @as: an uninitialized #AddressSpace - * @root: a #MemoryRegion that routes addesses for the address space + * @root: a #MemoryRegion that routes addresses for the address space * @name: an address space name. The name is only used for debugging * output. */ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name); +/** + * address_space_init_shareable: return an address space for a memory region, + * creating it if it does not already exist + * + * @root: a #MemoryRegion that routes addresses for the address space + * @name: an address space name. The name is only used for debugging + * output. + * + * This function will return a pointer to an existing AddressSpace + * which was initialized with the specified MemoryRegion, or it will + * create and initialize one if it does not already exist. The ASes + * are reference-counted, so the memory will be freed automatically + * when the AddressSpace is destroyed via address_space_destroy. + */ +AddressSpace *address_space_init_shareable(MemoryRegion *root, + const char *name); /** * address_space_destroy: destroy an address space @@ -1189,23 +1243,7 @@ MemTxResult address_space_write(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, const uint8_t *buf, int len); -/** - * address_space_read: read from an address space. - * - * Return a MemTxResult indicating whether the operation succeeded - * or failed (eg unassigned memory, device rejected the transaction, - * IOMMU fault). - * - * @as: #AddressSpace to be accessed - * @addr: address within that address space - * @attrs: memory transaction attributes - * @buf: buffer with the data transferred - */ -MemTxResult address_space_read(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, - uint8_t *buf, int len); - -/** - * address_space_ld*: load from an address space +/* address_space_ld*: load from an address space * address_space_st*: store to an address space * * These functions perform a load or store of the byte, word, @@ -1335,6 +1373,66 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, int is_write, hwaddr access_len); +/* Internal functions, part of the implementation of address_space_read. */ +MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr, + MemTxAttrs attrs, uint8_t *buf, + int len, hwaddr addr1, hwaddr l, + MemoryRegion *mr); +MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr, + MemTxAttrs attrs, uint8_t *buf, int len); +void *qemu_get_ram_ptr(RAMBlock *ram_block, ram_addr_t addr); + +static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) +{ + if (is_write) { + return memory_region_is_ram(mr) && !mr->readonly; + } else { + return memory_region_is_ram(mr) || memory_region_is_romd(mr); + } +} + +/** + * address_space_read: read from an address space. + * + * Return a MemTxResult indicating whether the operation succeeded + * or failed (eg unassigned memory, device rejected the transaction, + * IOMMU fault). + * + * @as: #AddressSpace to be accessed + * @addr: address within that address space + * @attrs: memory transaction attributes + * @buf: buffer with the data transferred + */ +static inline __attribute__((__always_inline__)) +MemTxResult address_space_read(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, + uint8_t *buf, int len) +{ + MemTxResult result = MEMTX_OK; + hwaddr l, addr1; + void *ptr; + MemoryRegion *mr; + + if (__builtin_constant_p(len)) { + if (len) { + rcu_read_lock(); + l = len; + mr = address_space_translate(as, addr, &addr1, &l, false); + if (len == l && memory_access_is_direct(mr, false)) { + addr1 += memory_region_get_ram_addr(mr); + ptr = qemu_get_ram_ptr(mr->ram_block, addr1); + memcpy(buf, ptr, len); + } else { + result = address_space_read_continue(as, addr, attrs, buf, len, + addr1, l, mr); + } + rcu_read_unlock(); + } + } else { + result = address_space_read_full(as, addr, attrs, buf, len); + } + return result; +} + #endif #endif diff --git a/qemu/include/exec/ram_addr.h b/qemu/include/exec/ram_addr.h index c113f2114..5adf7a4fc 100644 --- a/qemu/include/exec/ram_addr.h +++ b/qemu/include/exec/ram_addr.h @@ -22,22 +22,93 @@ #ifndef CONFIG_USER_ONLY #include "hw/xen/xen.h" -ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, - bool share, const char *mem_path, - Error **errp); -ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, - MemoryRegion *mr, Error **errp); -ram_addr_t qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr, Error **errp); -ram_addr_t qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t max_size, - void (*resized)(const char*, - uint64_t length, - void *host), - MemoryRegion *mr, Error **errp); +struct RAMBlock { + struct rcu_head rcu; + struct MemoryRegion *mr; + uint8_t *host; + ram_addr_t offset; + ram_addr_t used_length; + ram_addr_t max_length; + void (*resized)(const char*, uint64_t length, void *host); + uint32_t flags; + /* Protected by iothread lock. */ + char idstr[256]; + /* RCU-enabled, writes protected by the ramlist lock */ + QLIST_ENTRY(RAMBlock) next; + int fd; +}; + +static inline bool offset_in_ramblock(RAMBlock *b, ram_addr_t offset) +{ + return (b && b->host && offset < b->used_length) ? true : false; +} + +static inline void *ramblock_ptr(RAMBlock *block, ram_addr_t offset) +{ + assert(offset_in_ramblock(block, offset)); + return (char *)block->host + offset; +} + +/* The dirty memory bitmap is split into fixed-size blocks to allow growth + * under RCU. The bitmap for a block can be accessed as follows: + * + * rcu_read_lock(); + * + * DirtyMemoryBlocks *blocks = + * atomic_rcu_read(&ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION]); + * + * ram_addr_t idx = (addr >> TARGET_PAGE_BITS) / DIRTY_MEMORY_BLOCK_SIZE; + * unsigned long *block = blocks.blocks[idx]; + * ...access block bitmap... + * + * rcu_read_unlock(); + * + * Remember to check for the end of the block when accessing a range of + * addresses. Move on to the next block if you reach the end. + * + * Organization into blocks allows dirty memory to grow (but not shrink) under + * RCU. When adding new RAMBlocks requires the dirty memory to grow, a new + * DirtyMemoryBlocks array is allocated with pointers to existing blocks kept + * the same. Other threads can safely access existing blocks while dirty + * memory is being grown. When no threads are using the old DirtyMemoryBlocks + * anymore it is freed by RCU (but the underlying blocks stay because they are + * pointed to from the new DirtyMemoryBlocks). + */ +#define DIRTY_MEMORY_BLOCK_SIZE ((ram_addr_t)256 * 1024 * 8) +typedef struct { + struct rcu_head rcu; + unsigned long *blocks[]; +} DirtyMemoryBlocks; + +typedef struct RAMList { + QemuMutex mutex; + RAMBlock *mru_block; + /* RCU-enabled, writes protected by the ramlist lock. */ + QLIST_HEAD(, RAMBlock) blocks; + DirtyMemoryBlocks *dirty_memory[DIRTY_MEMORY_NUM]; + uint32_t version; +} RAMList; +extern RAMList ram_list; + +ram_addr_t last_ram_offset(void); +void qemu_mutex_lock_ramlist(void); +void qemu_mutex_unlock_ramlist(void); + +RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, + bool share, const char *mem_path, + Error **errp); +RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, + MemoryRegion *mr, Error **errp); +RAMBlock *qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr, Error **errp); +RAMBlock *qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t max_size, + void (*resized)(const char*, + uint64_t length, + void *host), + MemoryRegion *mr, Error **errp); int qemu_get_ram_fd(ram_addr_t addr); +void qemu_set_ram_fd(ram_addr_t addr, int fd); void *qemu_get_ram_block_host_ptr(ram_addr_t addr); -void *qemu_get_ram_ptr(ram_addr_t addr); -void qemu_ram_free(ram_addr_t addr); -void qemu_ram_free_from_ptr(ram_addr_t addr); +void qemu_ram_free(RAMBlock *block); int qemu_ram_resize(ram_addr_t base, ram_addr_t newsize, Error **errp); @@ -48,30 +119,82 @@ static inline bool cpu_physical_memory_get_dirty(ram_addr_t start, ram_addr_t length, unsigned client) { - unsigned long end, page, next; + DirtyMemoryBlocks *blocks; + unsigned long end, page; + unsigned long idx, offset, base; + bool dirty = false; assert(client < DIRTY_MEMORY_NUM); end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; page = start >> TARGET_PAGE_BITS; - next = find_next_bit(ram_list.dirty_memory[client], end, page); - return next < end; + rcu_read_lock(); + + blocks = atomic_rcu_read(&ram_list.dirty_memory[client]); + + idx = page / DIRTY_MEMORY_BLOCK_SIZE; + offset = page % DIRTY_MEMORY_BLOCK_SIZE; + base = page - offset; + while (page < end) { + unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE); + unsigned long num = next - base; + unsigned long found = find_next_bit(blocks->blocks[idx], num, offset); + if (found < num) { + dirty = true; + break; + } + + page = next; + idx++; + offset = 0; + base += DIRTY_MEMORY_BLOCK_SIZE; + } + + rcu_read_unlock(); + + return dirty; } static inline bool cpu_physical_memory_all_dirty(ram_addr_t start, ram_addr_t length, unsigned client) { - unsigned long end, page, next; + DirtyMemoryBlocks *blocks; + unsigned long end, page; + unsigned long idx, offset, base; + bool dirty = true; assert(client < DIRTY_MEMORY_NUM); end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; page = start >> TARGET_PAGE_BITS; - next = find_next_zero_bit(ram_list.dirty_memory[client], end, page); - return next >= end; + rcu_read_lock(); + + blocks = atomic_rcu_read(&ram_list.dirty_memory[client]); + + idx = page / DIRTY_MEMORY_BLOCK_SIZE; + offset = page % DIRTY_MEMORY_BLOCK_SIZE; + base = page - offset; + while (page < end) { + unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE); + unsigned long num = next - base; + unsigned long found = find_next_zero_bit(blocks->blocks[idx], num, offset); + if (found < num) { + dirty = false; + break; + } + + page = next; + idx++; + offset = 0; + base += DIRTY_MEMORY_BLOCK_SIZE; + } + + rcu_read_unlock(); + + return dirty; } static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, @@ -113,28 +236,73 @@ static inline uint8_t cpu_physical_memory_range_includes_clean(ram_addr_t start, static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, unsigned client) { + unsigned long page, idx, offset; + DirtyMemoryBlocks *blocks; + assert(client < DIRTY_MEMORY_NUM); - set_bit_atomic(addr >> TARGET_PAGE_BITS, ram_list.dirty_memory[client]); + + page = addr >> TARGET_PAGE_BITS; + idx = page / DIRTY_MEMORY_BLOCK_SIZE; + offset = page % DIRTY_MEMORY_BLOCK_SIZE; + + rcu_read_lock(); + + blocks = atomic_rcu_read(&ram_list.dirty_memory[client]); + + set_bit_atomic(offset, blocks->blocks[idx]); + + rcu_read_unlock(); } static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, ram_addr_t length, uint8_t mask) { + DirtyMemoryBlocks *blocks[DIRTY_MEMORY_NUM]; unsigned long end, page; - unsigned long **d = ram_list.dirty_memory; + unsigned long idx, offset, base; + int i; + + if (!mask && !xen_enabled()) { + return; + } end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; page = start >> TARGET_PAGE_BITS; - if (likely(mask & (1 << DIRTY_MEMORY_MIGRATION))) { - bitmap_set_atomic(d[DIRTY_MEMORY_MIGRATION], page, end - page); - } - if (unlikely(mask & (1 << DIRTY_MEMORY_VGA))) { - bitmap_set_atomic(d[DIRTY_MEMORY_VGA], page, end - page); + + rcu_read_lock(); + + for (i = 0; i < DIRTY_MEMORY_NUM; i++) { + blocks[i] = atomic_rcu_read(&ram_list.dirty_memory[i]); } - if (unlikely(mask & (1 << DIRTY_MEMORY_CODE))) { - bitmap_set_atomic(d[DIRTY_MEMORY_CODE], page, end - page); + + idx = page / DIRTY_MEMORY_BLOCK_SIZE; + offset = page % DIRTY_MEMORY_BLOCK_SIZE; + base = page - offset; + while (page < end) { + unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE); + + if (likely(mask & (1 << DIRTY_MEMORY_MIGRATION))) { + bitmap_set_atomic(blocks[DIRTY_MEMORY_MIGRATION]->blocks[idx], + offset, next - page); + } + if (unlikely(mask & (1 << DIRTY_MEMORY_VGA))) { + bitmap_set_atomic(blocks[DIRTY_MEMORY_VGA]->blocks[idx], + offset, next - page); + } + if (unlikely(mask & (1 << DIRTY_MEMORY_CODE))) { + bitmap_set_atomic(blocks[DIRTY_MEMORY_CODE]->blocks[idx], + offset, next - page); + } + + page = next; + idx++; + offset = 0; + base += DIRTY_MEMORY_BLOCK_SIZE; } + + rcu_read_unlock(); + xen_modified_memory(start, length); } @@ -154,21 +322,41 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, /* start address is aligned at the start of a word? */ if ((((page * BITS_PER_LONG) << TARGET_PAGE_BITS) == start) && (hpratio == 1)) { + unsigned long **blocks[DIRTY_MEMORY_NUM]; + unsigned long idx; + unsigned long offset; long k; long nr = BITS_TO_LONGS(pages); + idx = (start >> TARGET_PAGE_BITS) / DIRTY_MEMORY_BLOCK_SIZE; + offset = BIT_WORD((start >> TARGET_PAGE_BITS) % + DIRTY_MEMORY_BLOCK_SIZE); + + rcu_read_lock(); + + for (i = 0; i < DIRTY_MEMORY_NUM; i++) { + blocks[i] = atomic_rcu_read(&ram_list.dirty_memory[i])->blocks; + } + for (k = 0; k < nr; k++) { if (bitmap[k]) { unsigned long temp = leul_to_cpu(bitmap[k]); - unsigned long **d = ram_list.dirty_memory; - atomic_or(&d[DIRTY_MEMORY_MIGRATION][page + k], temp); - atomic_or(&d[DIRTY_MEMORY_VGA][page + k], temp); + atomic_or(&blocks[DIRTY_MEMORY_MIGRATION][idx][offset], temp); + atomic_or(&blocks[DIRTY_MEMORY_VGA][idx][offset], temp); if (tcg_enabled()) { - atomic_or(&d[DIRTY_MEMORY_CODE][page + k], temp); + atomic_or(&blocks[DIRTY_MEMORY_CODE][idx][offset], temp); } } + + if (++offset >= BITS_TO_LONGS(DIRTY_MEMORY_BLOCK_SIZE)) { + offset = 0; + idx++; + } } + + rcu_read_unlock(); + xen_modified_memory(start, pages << TARGET_PAGE_BITS); } else { uint8_t clients = tcg_enabled() ? DIRTY_CLIENTS_ALL : DIRTY_CLIENTS_NOCODE; @@ -220,18 +408,33 @@ uint64_t cpu_physical_memory_sync_dirty_bitmap(unsigned long *dest, if (((page * BITS_PER_LONG) << TARGET_PAGE_BITS) == start) { int k; int nr = BITS_TO_LONGS(length >> TARGET_PAGE_BITS); - unsigned long *src = ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION]; + unsigned long * const *src; + unsigned long idx = (page * BITS_PER_LONG) / DIRTY_MEMORY_BLOCK_SIZE; + unsigned long offset = BIT_WORD((page * BITS_PER_LONG) % + DIRTY_MEMORY_BLOCK_SIZE); + + rcu_read_lock(); + + src = atomic_rcu_read( + &ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION])->blocks; for (k = page; k < page + nr; k++) { - if (src[k]) { - unsigned long bits = atomic_xchg(&src[k], 0); + if (src[idx][offset]) { + unsigned long bits = atomic_xchg(&src[idx][offset], 0); unsigned long new_dirty; new_dirty = ~dest[k]; dest[k] |= bits; new_dirty &= bits; num_dirty += ctpopl(new_dirty); } + + if (++offset >= BITS_TO_LONGS(DIRTY_MEMORY_BLOCK_SIZE)) { + offset = 0; + idx++; + } } + + rcu_read_unlock(); } else { for (addr = 0; addr < length; addr += TARGET_PAGE_SIZE) { if (cpu_physical_memory_test_and_clear_dirty( @@ -249,5 +452,6 @@ uint64_t cpu_physical_memory_sync_dirty_bitmap(unsigned long *dest, return num_dirty; } +void migration_bitmap_extend(ram_addr_t old, ram_addr_t new); #endif #endif diff --git a/qemu/include/exec/softmmu-semi.h b/qemu/include/exec/softmmu-semi.h index 1819cc249..3a58c3f08 100644 --- a/qemu/include/exec/softmmu-semi.h +++ b/qemu/include/exec/softmmu-semi.h @@ -9,6 +9,14 @@ #ifndef SOFTMMU_SEMI_H #define SOFTMMU_SEMI_H 1 +static inline uint64_t softmmu_tget64(CPUArchState *env, target_ulong addr) +{ + uint64_t val; + + cpu_memory_rw_debug(ENV_GET_CPU(env), addr, (uint8_t *)&val, 8, 0); + return tswap64(val); +} + static inline uint32_t softmmu_tget32(CPUArchState *env, target_ulong addr) { uint32_t val; @@ -16,6 +24,7 @@ static inline uint32_t softmmu_tget32(CPUArchState *env, target_ulong addr) cpu_memory_rw_debug(ENV_GET_CPU(env), addr, (uint8_t *)&val, 4, 0); return tswap32(val); } + static inline uint32_t softmmu_tget8(CPUArchState *env, target_ulong addr) { uint8_t val; @@ -24,16 +33,25 @@ static inline uint32_t softmmu_tget8(CPUArchState *env, target_ulong addr) return val; } +#define get_user_u64(arg, p) ({ arg = softmmu_tget64(env, p); 0; }) #define get_user_u32(arg, p) ({ arg = softmmu_tget32(env, p) ; 0; }) #define get_user_u8(arg, p) ({ arg = softmmu_tget8(env, p) ; 0; }) #define get_user_ual(arg, p) get_user_u32(arg, p) +static inline void softmmu_tput64(CPUArchState *env, + target_ulong addr, uint64_t val) +{ + val = tswap64(val); + cpu_memory_rw_debug(ENV_GET_CPU(env), addr, (uint8_t *)&val, 8, 1); +} + static inline void softmmu_tput32(CPUArchState *env, target_ulong addr, uint32_t val) { val = tswap32(val); cpu_memory_rw_debug(ENV_GET_CPU(env), addr, (uint8_t *)&val, 4, 1); } +#define put_user_u64(arg, p) ({ softmmu_tput64(env, p, arg) ; 0; }) #define put_user_u32(arg, p) ({ softmmu_tput32(env, p, arg) ; 0; }) #define put_user_ual(arg, p) put_user_u32(arg, p) diff --git a/qemu/include/exec/spinlock.h b/qemu/include/exec/spinlock.h deleted file mode 100644 index a72edda1d..000000000 --- a/qemu/include/exec/spinlock.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2003 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 - */ - -/* configure guarantees us that we have pthreads on any host except - * mingw32, which doesn't support any of the user-only targets. - * So we can simply assume we have pthread mutexes here. - */ -#if defined(CONFIG_USER_ONLY) - -#include -#define spin_lock pthread_mutex_lock -#define spin_unlock pthread_mutex_unlock -#define spinlock_t pthread_mutex_t -#define SPIN_LOCK_UNLOCKED PTHREAD_MUTEX_INITIALIZER - -#else - -/* Empty implementations, on the theory that system mode emulation - * is single-threaded. This means that these functions should only - * be used from code run in the TCG cpu thread, and cannot protect - * data structures which might also be accessed from the IO thread - * or from signal handlers. - */ -typedef int spinlock_t; -#define SPIN_LOCK_UNLOCKED 0 - -static inline void spin_lock(spinlock_t *lock) -{ -} - -static inline void spin_unlock(spinlock_t *lock) -{ -} - -#endif diff --git a/qemu/include/exec/user/thunk.h b/qemu/include/exec/user/thunk.h index 3b6746272..ad1d60266 100644 --- a/qemu/include/exec/user/thunk.h +++ b/qemu/include/exec/user/thunk.h @@ -19,7 +19,6 @@ #ifndef THUNK_H #define THUNK_H -#include #include "cpu.h" /* types enums definitions */ diff --git a/qemu/include/fpu/softfloat.h b/qemu/include/fpu/softfloat.h index ded34eb00..c93706253 100644 --- a/qemu/include/fpu/softfloat.h +++ b/qemu/include/fpu/softfloat.h @@ -86,25 +86,12 @@ this code that are retained. #include #endif -#include -#include "config-host.h" -#include "qemu/osdep.h" -/*---------------------------------------------------------------------------- -| Each of the following `typedef's defines the most convenient type that holds -| integers of at least as many bits as specified. For example, `uint8' should -| be the most convenient type that can hold unsigned integers of as many as -| 8 bits. The `flag' type must be able to hold either a 0 or 1. For most -| implementations of C, `flag', `uint8', and `int8' should all be `typedef'ed -| to the same as `int'. -*----------------------------------------------------------------------------*/ +/* This 'flag' type must be able to hold at least 0 and 1. It should + * probably be replaced with 'bool' but the uses would need to be audited + * to check that they weren't accidentally relying on it being a larger type. + */ typedef uint8_t flag; -typedef uint8_t uint8; -typedef int8_t int8; -typedef unsigned int uint32; -typedef signed int int32; -typedef uint64_t uint64; -typedef int64_t int64; #define LIT64( a ) a##LL @@ -282,7 +269,7 @@ static inline flag get_default_nan_mode(float_status *status) | Routine to raise any or all of the software IEC/IEEE floating-point | exception flags. *----------------------------------------------------------------------------*/ -void float_raise(int8 flags, float_status *status); +void float_raise(int8_t flags, float_status *status); /*---------------------------------------------------------------------------- | If `a' is denormal and we are in flush-to-zero mode then set the @@ -372,18 +359,18 @@ extern const float16 float16_default_nan; /*---------------------------------------------------------------------------- | Software IEC/IEEE single-precision conversion routines. *----------------------------------------------------------------------------*/ -int_fast16_t float32_to_int16(float32, float_status *status); -uint_fast16_t float32_to_uint16(float32, float_status *status); -int_fast16_t float32_to_int16_round_to_zero(float32, float_status *status); -uint_fast16_t float32_to_uint16_round_to_zero(float32, float_status *status); -int32 float32_to_int32(float32, float_status *status); -int32 float32_to_int32_round_to_zero(float32, float_status *status); -uint32 float32_to_uint32(float32, float_status *status); -uint32 float32_to_uint32_round_to_zero(float32, float_status *status); -int64 float32_to_int64(float32, float_status *status); -uint64 float32_to_uint64(float32, float_status *status); -uint64 float32_to_uint64_round_to_zero(float32, float_status *status); -int64 float32_to_int64_round_to_zero(float32, float_status *status); +int16_t float32_to_int16(float32, float_status *status); +uint16_t float32_to_uint16(float32, float_status *status); +int16_t float32_to_int16_round_to_zero(float32, float_status *status); +uint16_t float32_to_uint16_round_to_zero(float32, float_status *status); +int32_t float32_to_int32(float32, float_status *status); +int32_t float32_to_int32_round_to_zero(float32, float_status *status); +uint32_t float32_to_uint32(float32, float_status *status); +uint32_t float32_to_uint32_round_to_zero(float32, float_status *status); +int64_t float32_to_int64(float32, float_status *status); +uint64_t float32_to_uint64(float32, float_status *status); +uint64_t float32_to_uint64_round_to_zero(float32, float_status *status); +int64_t float32_to_int64_round_to_zero(float32, float_status *status); float64 float32_to_float64(float32, float_status *status); floatx80 float32_to_floatx80(float32, float_status *status); float128 float32_to_float128(float32, float_status *status); @@ -484,18 +471,18 @@ extern const float32 float32_default_nan; /*---------------------------------------------------------------------------- | Software IEC/IEEE double-precision conversion routines. *----------------------------------------------------------------------------*/ -int_fast16_t float64_to_int16(float64, float_status *status); -uint_fast16_t float64_to_uint16(float64, float_status *status); -int_fast16_t float64_to_int16_round_to_zero(float64, float_status *status); -uint_fast16_t float64_to_uint16_round_to_zero(float64, float_status *status); -int32 float64_to_int32(float64, float_status *status); -int32 float64_to_int32_round_to_zero(float64, float_status *status); -uint32 float64_to_uint32(float64, float_status *status); -uint32 float64_to_uint32_round_to_zero(float64, float_status *status); -int64 float64_to_int64(float64, float_status *status); -int64 float64_to_int64_round_to_zero(float64, float_status *status); -uint64 float64_to_uint64(float64 a, float_status *status); -uint64 float64_to_uint64_round_to_zero(float64 a, float_status *status); +int16_t float64_to_int16(float64, float_status *status); +uint16_t float64_to_uint16(float64, float_status *status); +int16_t float64_to_int16_round_to_zero(float64, float_status *status); +uint16_t float64_to_uint16_round_to_zero(float64, float_status *status); +int32_t float64_to_int32(float64, float_status *status); +int32_t float64_to_int32_round_to_zero(float64, float_status *status); +uint32_t float64_to_uint32(float64, float_status *status); +uint32_t float64_to_uint32_round_to_zero(float64, float_status *status); +int64_t float64_to_int64(float64, float_status *status); +int64_t float64_to_int64_round_to_zero(float64, float_status *status); +uint64_t float64_to_uint64(float64 a, float_status *status); +uint64_t float64_to_uint64_round_to_zero(float64 a, float_status *status); float32 float64_to_float32(float64, float_status *status); floatx80 float64_to_floatx80(float64, float_status *status); float128 float64_to_float128(float64, float_status *status); @@ -596,10 +583,10 @@ extern const float64 float64_default_nan; /*---------------------------------------------------------------------------- | Software IEC/IEEE extended double-precision conversion routines. *----------------------------------------------------------------------------*/ -int32 floatx80_to_int32(floatx80, float_status *status); -int32 floatx80_to_int32_round_to_zero(floatx80, float_status *status); -int64 floatx80_to_int64(floatx80, float_status *status); -int64 floatx80_to_int64_round_to_zero(floatx80, float_status *status); +int32_t floatx80_to_int32(floatx80, float_status *status); +int32_t floatx80_to_int32_round_to_zero(floatx80, float_status *status); +int64_t floatx80_to_int64(floatx80, float_status *status); +int64_t floatx80_to_int64_round_to_zero(floatx80, float_status *status); float32 floatx80_to_float32(floatx80, float_status *status); float64 floatx80_to_float64(floatx80, float_status *status); float128 floatx80_to_float128(floatx80, float_status *status); @@ -681,10 +668,10 @@ extern const floatx80 floatx80_default_nan; /*---------------------------------------------------------------------------- | Software IEC/IEEE quadruple-precision conversion routines. *----------------------------------------------------------------------------*/ -int32 float128_to_int32(float128, float_status *status); -int32 float128_to_int32_round_to_zero(float128, float_status *status); -int64 float128_to_int64(float128, float_status *status); -int64 float128_to_int64_round_to_zero(float128, float_status *status); +int32_t float128_to_int32(float128, float_status *status); +int32_t float128_to_int32_round_to_zero(float128, float_status *status); +int64_t float128_to_int64(float128, float_status *status); +int64_t float128_to_int64_round_to_zero(float128, float_status *status); float32 float128_to_float32(float128, float_status *status); float64 float128_to_float64(float128, float_status *status); floatx80 float128_to_floatx80(float128, float_status *status); diff --git a/qemu/include/glib-compat.h b/qemu/include/glib-compat.h index 318e00036..03d8b1267 100644 --- a/qemu/include/glib-compat.h +++ b/qemu/include/glib-compat.h @@ -165,4 +165,73 @@ static inline GThread *g_thread_new(const char *name, #define CompatGCond GCond #endif /* glib 2.31 */ +#if !GLIB_CHECK_VERSION(2, 32, 0) +/* Beware, function returns gboolean since 2.39.2, see GLib commit 9101915 */ +static inline void g_hash_table_add(GHashTable *hash_table, gpointer key) +{ + g_hash_table_replace(hash_table, key, key); +} +#endif + +#ifndef g_assert_true +#define g_assert_true(expr) \ + do { \ + if (G_LIKELY(expr)) { \ + } else { \ + g_assertion_message(G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + "'" #expr "' should be TRUE"); \ + } \ + } while (0) +#endif + +#ifndef g_assert_false +#define g_assert_false(expr) \ + do { \ + if (G_LIKELY(!(expr))) { \ + } else { \ + g_assertion_message(G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + "'" #expr "' should be FALSE"); \ + } \ + } while (0) +#endif + +#ifndef g_assert_null +#define g_assert_null(expr) \ + do { \ + if (G_LIKELY((expr) == NULL)) { \ + } else { \ + g_assertion_message(G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + "'" #expr "' should be NULL"); \ + } \ + } while (0) +#endif + +#ifndef g_assert_nonnull +#define g_assert_nonnull(expr) \ + do { \ + if (G_LIKELY((expr) != NULL)) { \ + } else { \ + g_assertion_message(G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + "'" #expr "' should not be NULL"); \ + } \ + } while (0) +#endif + +#ifndef g_assert_cmpmem +#define g_assert_cmpmem(m1, l1, m2, l2) \ + do { \ + gconstpointer __m1 = m1, __m2 = m2; \ + int __l1 = l1, __l2 = l2; \ + if (__l1 != __l2) { \ + g_assertion_message_cmpnum( \ + G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + #l1 " (len(" #m1 ")) == " #l2 " (len(" #m2 "))", __l1, "==", \ + __l2, 'i'); \ + } else if (memcmp(__m1, __m2, __l1) != 0) { \ + g_assertion_message(G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + "assertion failed (" #m1 " == " #m2 ")"); \ + } \ + } while (0) +#endif + #endif diff --git a/qemu/include/hw/acpi/acpi-defs.h b/qemu/include/hw/acpi/acpi-defs.h index 2b431e624..c7a03d43b 100644 --- a/qemu/include/hw/acpi/acpi-defs.h +++ b/qemu/include/hw/acpi/acpi-defs.h @@ -384,6 +384,15 @@ struct AcpiMadtGenericMsiFrame { typedef struct AcpiMadtGenericMsiFrame AcpiMadtGenericMsiFrame; +struct AcpiMadtGenericRedistributor { + ACPI_SUB_HEADER_DEF + uint16_t reserved; + uint64_t base_address; + uint32_t range_length; +} QEMU_PACKED; + +typedef struct AcpiMadtGenericRedistributor AcpiMadtGenericRedistributor; + /* * Generic Timer Description Table (GTDT) */ diff --git a/qemu/include/hw/acpi/acpi.h b/qemu/include/hw/acpi/acpi.h index b20bd55a6..e0978c8b9 100644 --- a/qemu/include/hw/acpi/acpi.h +++ b/qemu/include/hw/acpi/acpi.h @@ -19,8 +19,6 @@ * . */ -#include "qapi/error.h" -#include "qemu/typedefs.h" #include "qemu/notify.h" #include "qemu/option.h" #include "exec/memory.h" @@ -156,7 +154,7 @@ void acpi_pm_tmr_reset(ACPIREGS *ar); static inline int64_t acpi_pm_tmr_get_clock(void) { return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), PM_TIMER_FREQUENCY, - get_ticks_per_sec()); + NANOSECONDS_PER_SECOND); } /* PM1a_EVT: piix and ich9 don't implement PM1b. */ @@ -196,4 +194,11 @@ unsigned acpi_table_len(void *current); void acpi_table_add(const QemuOpts *opts, Error **errp); void acpi_table_add_builtin(const QemuOpts *opts, Error **errp); +typedef struct AcpiSlicOem AcpiSlicOem; +struct AcpiSlicOem { + char *id; + char *table_id; +}; +int acpi_get_slic_oem(AcpiSlicOem *oem); + #endif /* !QEMU_HW_ACPI_H */ diff --git a/qemu/include/hw/acpi/aml-build.h b/qemu/include/hw/acpi/aml-build.h index e3afa1367..2c994b351 100644 --- a/qemu/include/hw/acpi/aml-build.h +++ b/qemu/include/hw/acpi/aml-build.h @@ -1,15 +1,12 @@ #ifndef HW_ACPI_GEN_UTILS_H #define HW_ACPI_GEN_UTILS_H -#include #include -#include "qemu/compiler.h" #include "hw/acpi/acpi-defs.h" /* Reserve RAM space for tables: add another order of magnitude. */ #define ACPI_BUILD_TABLE_MAX_SIZE 0x200000 -#define ACPI_BUILD_APPNAME "Bochs" #define ACPI_BUILD_APPNAME6 "BOCHS " #define ACPI_BUILD_APPNAME4 "BXPC" @@ -17,6 +14,8 @@ #define ACPI_BUILD_RSDP_FILE "etc/acpi/rsdp" #define ACPI_BUILD_TPMLOG_FILE "etc/tpm/log" +#define AML_NOTIFY_METHOD "NTFY" + typedef enum { AML_NO_OPCODE = 0,/* has only data */ AML_OPCODE, /* has opcode optionally followed by data */ @@ -35,6 +34,24 @@ struct Aml { }; typedef struct Aml Aml; +typedef enum { + AML_COMPATIBILITY = 0, + AML_TYPEA = 1, + AML_TYPEB = 2, + AML_TYPEF = 3, +} AmlDmaType; + +typedef enum { + AML_NOTBUSMASTER = 0, + AML_BUSMASTER = 1, +} AmlDmaBusMaster; + +typedef enum { + AML_TRANSFER8 = 0, + AML_TRANSFER8_16 = 1, + AML_TRANSFER16 = 2, +} AmlTransferSize; + typedef enum { AML_DECODE10 = 0, AML_DECODE16 = 1, @@ -49,6 +66,11 @@ typedef enum { AML_BUFFER_ACC = 5, } AmlAccessType; +typedef enum { + AML_NOLOCK = 0, + AML_LOCK = 1, +} AmlLockRule; + typedef enum { AML_PRESERVE = 0, AML_WRITE_AS_ONES = 1, @@ -58,6 +80,7 @@ typedef enum { typedef enum { AML_SYSTEM_MEMORY = 0X00, AML_SYSTEM_IO = 0X01, + AML_PCI_CONFIG = 0X02, } AmlRegionSpace; typedef enum { @@ -149,6 +172,32 @@ typedef enum { AML_SHARED_AND_WAKE = 3, } AmlShared; +/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: MethodFlags */ +typedef enum { + AML_NOTSERIALIZED = 0, + AML_SERIALIZED = 1, +} AmlSerializeFlag; + +/* + * ACPI 5.0: Table 6-189 GPIO Connection Descriptor Definition + * GPIO Connection Type + */ +typedef enum { + AML_INTERRUPT_CONNECTION = 0, + AML_IO_CONNECTION = 1, +} AmlGpioConnectionType; + +/* + * ACPI 5.0: Table 6-189 GPIO Connection Descriptor Definition + * _PPI field definition + */ +typedef enum { + AML_PULL_DEFAULT = 0, + AML_PULL_UP = 1, + AML_PULL_DOWN = 2, + AML_PULL_NONE = 3, +} AmlPinConfig; + typedef struct AcpiBuildTables { GArray *table_data; @@ -199,30 +248,44 @@ Aml *aml_name_decl(const char *name, Aml *val); Aml *aml_return(Aml *val); Aml *aml_int(const uint64_t val); Aml *aml_arg(int pos); +Aml *aml_to_integer(Aml *arg); +Aml *aml_to_hexstring(Aml *src, Aml *dst); +Aml *aml_to_buffer(Aml *src, Aml *dst); Aml *aml_store(Aml *val, Aml *target); -Aml *aml_and(Aml *arg1, Aml *arg2); -Aml *aml_or(Aml *arg1, Aml *arg2); +Aml *aml_and(Aml *arg1, Aml *arg2, Aml *dst); +Aml *aml_or(Aml *arg1, Aml *arg2, Aml *dst); +Aml *aml_lor(Aml *arg1, Aml *arg2); Aml *aml_shiftleft(Aml *arg1, Aml *count); -Aml *aml_shiftright(Aml *arg1, Aml *count); +Aml *aml_shiftright(Aml *arg1, Aml *count, Aml *dst); Aml *aml_lless(Aml *arg1, Aml *arg2); -Aml *aml_add(Aml *arg1, Aml *arg2); +Aml *aml_add(Aml *arg1, Aml *arg2, Aml *dst); +Aml *aml_subtract(Aml *arg1, Aml *arg2, Aml *dst); Aml *aml_increment(Aml *arg); +Aml *aml_decrement(Aml *arg); Aml *aml_index(Aml *arg1, Aml *idx); Aml *aml_notify(Aml *arg1, Aml *arg2); +Aml *aml_call0(const char *method); Aml *aml_call1(const char *method, Aml *arg1); Aml *aml_call2(const char *method, Aml *arg1, Aml *arg2); Aml *aml_call3(const char *method, Aml *arg1, Aml *arg2, Aml *arg3); Aml *aml_call4(const char *method, Aml *arg1, Aml *arg2, Aml *arg3, Aml *arg4); +Aml *aml_gpio_int(AmlConsumerAndProducer con_and_pro, + AmlLevelAndEdge edge_level, + AmlActiveHighAndLow active_level, AmlShared shared, + AmlPinConfig pin_config, uint16_t debounce_timeout, + const uint32_t pin_list[], uint32_t pin_count, + const char *resource_source_name, + const uint8_t *vendor_data, uint16_t vendor_data_len); Aml *aml_memory32_fixed(uint32_t addr, uint32_t size, AmlReadAndWrite read_and_write); Aml *aml_interrupt(AmlConsumerAndProducer con_and_pro, AmlLevelAndEdge level_and_edge, AmlActiveHighAndLow high_and_low, AmlShared shared, - uint32_t irq); + uint32_t *irq_list, uint8_t irq_count); Aml *aml_io(AmlIODecode dec, uint16_t min_base, uint16_t max_base, uint8_t aln, uint8_t len); Aml *aml_operation_region(const char *name, AmlRegionSpace rs, - uint32_t offset, uint32_t len); + Aml *offset, uint32_t len); Aml *aml_irq_no_flags(uint8_t irq); Aml *aml_named_field(const char *name, unsigned length); Aml *aml_reserved_field(unsigned length); @@ -230,6 +293,8 @@ Aml *aml_local(int num); Aml *aml_string(const char *name_format, ...) GCC_FMT_ATTR(1, 2); Aml *aml_lnot(Aml *arg); Aml *aml_equal(Aml *arg1, Aml *arg2); +Aml *aml_lgreater(Aml *arg1, Aml *arg2); +Aml *aml_lgreater_equal(Aml *arg1, Aml *arg2); Aml *aml_processor(uint8_t proc_id, uint32_t pblk_addr, uint8_t pblk_len, const char *name_format, ...) GCC_FMT_ATTR(4, 5); Aml *aml_eisaid(const char *str); @@ -259,32 +324,52 @@ Aml *aml_qword_memory(AmlDecode dec, AmlMinFixed min_fixed, uint64_t addr_gran, uint64_t addr_min, uint64_t addr_max, uint64_t addr_trans, uint64_t len); +Aml *aml_dma(AmlDmaType typ, AmlDmaBusMaster bm, AmlTransferSize sz, + uint8_t channel); +Aml *aml_sleep(uint64_t msec); /* Block AML object primitives */ Aml *aml_scope(const char *name_format, ...) GCC_FMT_ATTR(1, 2); Aml *aml_device(const char *name_format, ...) GCC_FMT_ATTR(1, 2); -Aml *aml_method(const char *name, int arg_count); +Aml *aml_method(const char *name, int arg_count, AmlSerializeFlag sflag); Aml *aml_if(Aml *predicate); Aml *aml_else(void); Aml *aml_while(Aml *predicate); Aml *aml_package(uint8_t num_elements); Aml *aml_buffer(int buffer_size, uint8_t *byte_list); Aml *aml_resource_template(void); -Aml *aml_field(const char *name, AmlAccessType type, AmlUpdateRule rule); +Aml *aml_field(const char *name, AmlAccessType type, AmlLockRule lock, + AmlUpdateRule rule); +Aml *aml_mutex(const char *name, uint8_t sync_level); +Aml *aml_acquire(Aml *mutex, uint16_t timeout); +Aml *aml_release(Aml *mutex); +Aml *aml_alias(const char *source_object, const char *alias_object); +Aml *aml_create_field(Aml *srcbuf, Aml *bit_index, Aml *num_bits, + const char *name); Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name); +Aml *aml_create_qword_field(Aml *srcbuf, Aml *index, const char *name); Aml *aml_varpackage(uint32_t num_elements); Aml *aml_touuid(const char *uuid); Aml *aml_unicode(const char *str); +Aml *aml_derefof(Aml *arg); +Aml *aml_sizeof(Aml *arg); +Aml *aml_concatenate(Aml *source1, Aml *source2, Aml *target); void build_header(GArray *linker, GArray *table_data, - AcpiTableHeader *h, const char *sig, int len, uint8_t rev); + AcpiTableHeader *h, const char *sig, int len, uint8_t rev, + const char *oem_id, const char *oem_table_id); void *acpi_data_push(GArray *table_data, unsigned size); unsigned acpi_data_len(GArray *table); void acpi_add_table(GArray *table_offsets, GArray *table_data); void acpi_build_tables_init(AcpiBuildTables *tables); void acpi_build_tables_cleanup(AcpiBuildTables *tables, bool mfre); void -build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets); +build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets, + const char *oem_id, const char *oem_table_id); + +int +build_append_named_dword(GArray *array, const char *name_format, ...) +GCC_FMT_ATTR(2, 3); #endif diff --git a/qemu/include/hw/acpi/bios-linker-loader.h b/qemu/include/hw/acpi/bios-linker-loader.h index 498c0af77..82f1af643 100644 --- a/qemu/include/hw/acpi/bios-linker-loader.h +++ b/qemu/include/hw/acpi/bios-linker-loader.h @@ -2,8 +2,6 @@ #define BIOS_LINKER_LOADER_H #include -#include -#include GArray *bios_linker_loader_init(void); @@ -13,7 +11,7 @@ void bios_linker_loader_alloc(GArray *linker, bool alloc_fseg); void bios_linker_loader_add_checksum(GArray *linker, const char *file, - void *table, + GArray *table, void *start, unsigned size, uint8_t *checksum); diff --git a/qemu/include/hw/acpi/cpu_hotplug.h b/qemu/include/hw/acpi/cpu_hotplug.h index f6d358def..f22640e38 100644 --- a/qemu/include/hw/acpi/cpu_hotplug.h +++ b/qemu/include/hw/acpi/cpu_hotplug.h @@ -14,6 +14,7 @@ #include "hw/acpi/acpi.h" #include "hw/acpi/pc-hotplug.h" +#include "hw/acpi/aml-build.h" typedef struct AcpiCpuHotplug { MemoryRegion io; @@ -25,4 +26,13 @@ void acpi_cpu_plug_cb(ACPIREGS *ar, qemu_irq irq, void acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner, AcpiCpuHotplug *gpe_cpu, uint16_t base); + +#define CPU_EJECT_METHOD "CPEJ" +#define CPU_MAT_METHOD "CPMA" +#define CPU_ON_BITMAP "CPON" +#define CPU_STATUS_METHOD "CPST" +#define CPU_STATUS_MAP "PRS" +#define CPU_SCAN_METHOD "PRSC" + +void build_cpu_hotplug_aml(Aml *ctx); #endif diff --git a/qemu/include/hw/acpi/ich9.h b/qemu/include/hw/acpi/ich9.h index 345fd8d92..63fa19814 100644 --- a/qemu/include/hw/acpi/ich9.h +++ b/qemu/include/hw/acpi/ich9.h @@ -62,7 +62,6 @@ typedef struct ICH9LPCPMRegs { void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, bool smm_enabled, - bool enable_tco, qemu_irq sci_irq); void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base); diff --git a/qemu/include/hw/acpi/memory_hotplug.h b/qemu/include/hw/acpi/memory_hotplug.h index 1342adb08..3a646b12e 100644 --- a/qemu/include/hw/acpi/memory_hotplug.h +++ b/qemu/include/hw/acpi/memory_hotplug.h @@ -4,6 +4,7 @@ #include "hw/qdev-core.h" #include "hw/acpi/acpi.h" #include "migration/vmstate.h" +#include "hw/acpi/aml-build.h" /** * MemStatus: @@ -45,4 +46,12 @@ extern const VMStateDescription vmstate_memory_hotplug; vmstate_memory_hotplug, MemHotplugState) void acpi_memory_ospm_status(MemHotplugState *mem_st, ACPIOSTInfoList ***list); + +#define MEMORY_HOTPLUG_DEVICE "MHPD" +#define MEMORY_SLOT_SCAN_METHOD "MSCN" +#define MEMORY_HOTPLUG_HANDLER_PATH "\\_SB.PCI0." \ + MEMORY_HOTPLUG_DEVICE "." MEMORY_SLOT_SCAN_METHOD + +void build_memory_hotplug_aml(Aml *ctx, uint32_t nr_mem, + uint16_t io_base, uint16_t io_len); #endif diff --git a/qemu/include/hw/acpi/pc-hotplug.h b/qemu/include/hw/acpi/pc-hotplug.h index 77b156900..6a8d268f8 100644 --- a/qemu/include/hw/acpi/pc-hotplug.h +++ b/qemu/include/hw/acpi/pc-hotplug.h @@ -32,28 +32,26 @@ #define ACPI_MEMORY_HOTPLUG_IO_LEN 24 #define ACPI_MEMORY_HOTPLUG_BASE 0x0a00 -#define MEMORY_HOTPLUG_DEVICE MHPD -#define MEMORY_SLOTS_NUMBER MDNR -#define MEMORY_HOTPLUG_IO_REGION HPMR -#define MEMORY_SLOT_ADDR_LOW MRBL -#define MEMORY_SLOT_ADDR_HIGH MRBH -#define MEMORY_SLOT_SIZE_LOW MRLL -#define MEMORY_SLOT_SIZE_HIGH MRLH -#define MEMORY_SLOT_PROXIMITY MPX -#define MEMORY_SLOT_ENABLED MES -#define MEMORY_SLOT_INSERT_EVENT MINS -#define MEMORY_SLOT_REMOVE_EVENT MRMV -#define MEMORY_SLOT_EJECT MEJ -#define MEMORY_SLOT_SLECTOR MSEL -#define MEMORY_SLOT_OST_EVENT MOEV -#define MEMORY_SLOT_OST_STATUS MOSC -#define MEMORY_SLOT_LOCK MLCK -#define MEMORY_SLOT_STATUS_METHOD MRST -#define MEMORY_SLOT_CRS_METHOD MCRS -#define MEMORY_SLOT_OST_METHOD MOST -#define MEMORY_SLOT_PROXIMITY_METHOD MPXM -#define MEMORY_SLOT_EJECT_METHOD MEJ0 -#define MEMORY_SLOT_NOTIFY_METHOD MTFY -#define MEMORY_SLOT_SCAN_METHOD MSCN +#define MEMORY_SLOTS_NUMBER "MDNR" +#define MEMORY_HOTPLUG_IO_REGION "HPMR" +#define MEMORY_SLOT_ADDR_LOW "MRBL" +#define MEMORY_SLOT_ADDR_HIGH "MRBH" +#define MEMORY_SLOT_SIZE_LOW "MRLL" +#define MEMORY_SLOT_SIZE_HIGH "MRLH" +#define MEMORY_SLOT_PROXIMITY "MPX" +#define MEMORY_SLOT_ENABLED "MES" +#define MEMORY_SLOT_INSERT_EVENT "MINS" +#define MEMORY_SLOT_REMOVE_EVENT "MRMV" +#define MEMORY_SLOT_EJECT "MEJ" +#define MEMORY_SLOT_SLECTOR "MSEL" +#define MEMORY_SLOT_OST_EVENT "MOEV" +#define MEMORY_SLOT_OST_STATUS "MOSC" +#define MEMORY_SLOT_LOCK "MLCK" +#define MEMORY_SLOT_STATUS_METHOD "MRST" +#define MEMORY_SLOT_CRS_METHOD "MCRS" +#define MEMORY_SLOT_OST_METHOD "MOST" +#define MEMORY_SLOT_PROXIMITY_METHOD "MPXM" +#define MEMORY_SLOT_EJECT_METHOD "MEJ0" +#define MEMORY_SLOT_NOTIFY_METHOD "MTFY" #endif diff --git a/qemu/include/hw/acpi/pcihp.h b/qemu/include/hw/acpi/pcihp.h index f3526d4aa..79a43923e 100644 --- a/qemu/include/hw/acpi/pcihp.h +++ b/qemu/include/hw/acpi/pcihp.h @@ -27,8 +27,6 @@ #ifndef HW_ACPI_PCIHP_H #define HW_ACPI_PCIHP_H -#include -#include #include "hw/acpi/acpi.h" #include "migration/vmstate.h" diff --git a/qemu/include/hw/acpi/piix4.h b/qemu/include/hw/acpi/piix4.h index 65e6fd7aa..26c2370e3 100644 --- a/qemu/include/hw/acpi/piix4.h +++ b/qemu/include/hw/acpi/piix4.h @@ -1,8 +1,6 @@ #ifndef HW_ACPI_PIIX4_H #define HW_ACPI_PIIX4_H -#include "qemu/typedefs.h" - Object *piix4_pm_find(void); #endif diff --git a/qemu/include/hw/acpi/tco.h b/qemu/include/hw/acpi/tco.h index c63afc8ca..52ad767dd 100644 --- a/qemu/include/hw/acpi/tco.h +++ b/qemu/include/hw/acpi/tco.h @@ -9,7 +9,6 @@ #ifndef HW_ACPI_TCO_H #define HW_ACPI_TCO_H -#include "qemu/typedefs.h" #include "qemu-common.h" /* As per ICH9 spec, the internal timer has an error of ~0.6s on every tick */ diff --git a/qemu/include/hw/arm/allwinner-a10.h b/qemu/include/hw/arm/allwinner-a10.h index 01a189bcd..6b32a99e2 100644 --- a/qemu/include/hw/arm/allwinner-a10.h +++ b/qemu/include/hw/arm/allwinner-a10.h @@ -7,6 +7,8 @@ #include "hw/timer/allwinner-a10-pit.h" #include "hw/intc/allwinner-a10-pic.h" #include "hw/net/allwinner_emac.h" +#include "hw/ide/pci.h" +#include "hw/ide/ahci.h" #include "sysemu/sysemu.h" #include "exec/address-spaces.h" @@ -16,6 +18,7 @@ #define AW_A10_PIT_REG_BASE 0x01c20c00 #define AW_A10_UART0_REG_BASE 0x01c28000 #define AW_A10_EMAC_BASE 0x01c0b000 +#define AW_A10_SATA_BASE 0x01c18000 #define AW_A10_SDRAM_BASE 0x40000000 @@ -32,6 +35,7 @@ typedef struct AwA10State { AwA10PITState timer; AwA10PICState intc; AwEmacState emac; + AllwinnerAHCIState sata; } AwA10State; #define ALLWINNER_H_ diff --git a/qemu/include/hw/arm/arm.h b/qemu/include/hw/arm/arm.h index 4dcd4f9b6..b2517f9a4 100644 --- a/qemu/include/hw/arm/arm.h +++ b/qemu/include/hw/arm/arm.h @@ -16,8 +16,15 @@ #include "qemu/notify.h" #include "cpu.h" +typedef enum { + ARM_ENDIANNESS_UNKNOWN = 0, + ARM_ENDIANNESS_LE, + ARM_ENDIANNESS_BE8, + ARM_ENDIANNESS_BE32, +} arm_endianness; + /* armv7m.c */ -qemu_irq *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, +DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, const char *kernel_filename, const char *cpu_model); /* @@ -87,6 +94,24 @@ struct arm_boot_info { * -pflash. It also implies that fw_cfg_find() will succeed. */ bool firmware_loaded; + + /* Address at which board specific loader/setup code exists. If enabled, + * this code-blob will run before anything else. It must return to the + * caller via the link register. There is no stack set up. Enabled by + * defining write_board_setup, which is responsible for loading the blob + * to the specified address. + */ + hwaddr board_setup_addr; + void (*write_board_setup)(ARMCPU *cpu, + const struct arm_boot_info *info); + + /* If set, the board specific loader/setup blob will be run from secure + * mode, regardless of secure_boot. The blob becomes responsible for + * changing to non-secure state if implementing a non-secure boot + */ + bool secure_board_setup; + + arm_endianness endianness; }; /** @@ -106,6 +131,11 @@ struct arm_boot_info { */ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info); +/* Write a secure board setup routine with a dummy handler for SMCs */ +void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu, + const struct arm_boot_info *info, + hwaddr mvbar_addr); + /* Multiplication factor to convert from system clock ticks to qemu timer ticks. */ extern int system_clock_scale; diff --git a/qemu/include/hw/arm/ast2400.h b/qemu/include/hw/arm/ast2400.h new file mode 100644 index 000000000..f16a1ed25 --- /dev/null +++ b/qemu/include/hw/arm/ast2400.h @@ -0,0 +1,35 @@ +/* + * ASPEED AST2400 SoC + * + * Andrew Jeffery + * + * Copyright 2016 IBM Corp. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#ifndef AST2400_H +#define AST2400_H + +#include "hw/arm/arm.h" +#include "hw/intc/aspeed_vic.h" +#include "hw/timer/aspeed_timer.h" + +typedef struct AST2400State { + /*< private >*/ + DeviceState parent; + + /*< public >*/ + ARMCPU *cpu; + MemoryRegion iomem; + AspeedVICState vic; + AspeedTimerCtrlState timerctrl; +} AST2400State; + +#define TYPE_AST2400 "ast2400" +#define AST2400(obj) OBJECT_CHECK(AST2400State, (obj), TYPE_AST2400) + +#define AST2400_SDRAM_BASE 0x40000000 + +#endif /* AST2400_H */ diff --git a/qemu/include/hw/arm/bcm2835_peripherals.h b/qemu/include/hw/arm/bcm2835_peripherals.h new file mode 100644 index 000000000..e12ae3721 --- /dev/null +++ b/qemu/include/hw/arm/bcm2835_peripherals.h @@ -0,0 +1,48 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous + * + * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2835_PERIPHERALS_H +#define BCM2835_PERIPHERALS_H + +#include "qemu-common.h" +#include "exec/address-spaces.h" +#include "hw/sysbus.h" +#include "hw/char/bcm2835_aux.h" +#include "hw/display/bcm2835_fb.h" +#include "hw/dma/bcm2835_dma.h" +#include "hw/intc/bcm2835_ic.h" +#include "hw/misc/bcm2835_property.h" +#include "hw/misc/bcm2835_mbox.h" +#include "hw/sd/sdhci.h" + +#define TYPE_BCM2835_PERIPHERALS "bcm2835-peripherals" +#define BCM2835_PERIPHERALS(obj) \ + OBJECT_CHECK(BCM2835PeripheralState, (obj), TYPE_BCM2835_PERIPHERALS) + +typedef struct BCM2835PeripheralState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion peri_mr, peri_mr_alias, gpu_bus_mr, mbox_mr; + MemoryRegion ram_alias[4]; + qemu_irq irq, fiq; + + SysBusDevice *uart0; + BCM2835AuxState aux; + BCM2835FBState fb; + BCM2835DMAState dma; + BCM2835ICState ic; + BCM2835PropertyState property; + BCM2835MboxState mboxes; + SDHCIState sdhci; +} BCM2835PeripheralState; + +#endif /* BCM2835_PERIPHERALS_H */ diff --git a/qemu/include/hw/arm/bcm2836.h b/qemu/include/hw/arm/bcm2836.h new file mode 100644 index 000000000..76de1996a --- /dev/null +++ b/qemu/include/hw/arm/bcm2836.h @@ -0,0 +1,35 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous + * + * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2836_H +#define BCM2836_H + +#include "hw/arm/arm.h" +#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 BCM2836_NCPUS 4 + +typedef struct BCM2836State { + /*< private >*/ + DeviceState parent_obj; + /*< public >*/ + + uint32_t enabled_cpus; + + ARMCPU cpus[BCM2836_NCPUS]; + BCM2836ControlState control; + BCM2835PeripheralState peripherals; +} BCM2836State; + +#endif /* BCM2836_H */ diff --git a/qemu/include/hw/arm/fsl-imx25.h b/qemu/include/hw/arm/fsl-imx25.h new file mode 100644 index 000000000..d0e8e9d95 --- /dev/null +++ b/qemu/include/hw/arm/fsl-imx25.h @@ -0,0 +1,249 @@ +/* + * Freescale i.MX25 SoC emulation + * + * Copyright (C) 2015 Jean-Christophe Dubois + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef FSL_IMX25_H +#define FSL_IMX25_H + +#include "hw/arm/arm.h" +#include "hw/intc/imx_avic.h" +#include "hw/misc/imx25_ccm.h" +#include "hw/char/imx_serial.h" +#include "hw/timer/imx_gpt.h" +#include "hw/timer/imx_epit.h" +#include "hw/net/imx_fec.h" +#include "hw/i2c/imx_i2c.h" +#include "hw/gpio/imx_gpio.h" +#include "exec/memory.h" + +#define TYPE_FSL_IMX25 "fsl,imx25" +#define FSL_IMX25(obj) OBJECT_CHECK(FslIMX25State, (obj), TYPE_FSL_IMX25) + +#define FSL_IMX25_NUM_UARTS 5 +#define FSL_IMX25_NUM_GPTS 4 +#define FSL_IMX25_NUM_EPITS 2 +#define FSL_IMX25_NUM_I2CS 3 +#define FSL_IMX25_NUM_GPIOS 4 + +typedef struct FslIMX25State { + /*< private >*/ + DeviceState parent_obj; + + /*< public >*/ + ARMCPU cpu; + IMXAVICState avic; + IMX25CCMState ccm; + IMXSerialState uart[FSL_IMX25_NUM_UARTS]; + IMXGPTState gpt[FSL_IMX25_NUM_GPTS]; + IMXEPITState epit[FSL_IMX25_NUM_EPITS]; + IMXFECState fec; + IMXI2CState i2c[FSL_IMX25_NUM_I2CS]; + IMXGPIOState gpio[FSL_IMX25_NUM_GPIOS]; + MemoryRegion rom[2]; + MemoryRegion iram; + MemoryRegion iram_alias; +} FslIMX25State; + +/** + * i.MX25 memory map + **************************************************************** + * 0x0000_0000 0x0000_3FFF 16 Kbytes ROM (36 Kbytes) + * 0x0000_4000 0x0040_3FFF 4 Mbytes Reserved + * 0x0040_4000 0x0040_8FFF 20 Kbytes ROM (36 Kbytes) + * 0x0040_9000 0x0FFF_FFFF 252 Mbytes (minus 36 Kbytes) Reserved + * 0x1000_0000 0x1FFF_FFFF 256 Mbytes Reserved + * 0x2000_0000 0x2FFF_FFFF 256 Mbytes Reserved + * 0x3000_0000 0x3FFF_FFFF 256 Mbytes Reserved + * 0x4000_0000 0x43EF_FFFF 63 Mbytes Reserved + * 0x43F0_0000 0x43F0_3FFF 16 Kbytes AIPS A control registers + * 0x43F0_4000 0x43F0_7FFF 16 Kbytes ARM926 platform MAX + * 0x43F0_8000 0x43F0_BFFF 16 Kbytes ARM926 platform CLKCTL + * 0x43F0_C000 0x43F0_FFFF 16 Kbytes ARM926 platform ETB registers + * 0x43F1_0000 0x43F1_3FFF 16 Kbytes ARM926 platform ETB memory + * 0x43F1_4000 0x43F1_7FFF 16 Kbytes ARM926 platform AAPE registers + * 0x43F1_8000 0x43F7_FFFF 416 Kbytes Reserved + * 0x43F8_0000 0x43F8_3FFF 16 Kbytes I2C-1 + * 0x43F8_4000 0x43F8_7FFF 16 Kbytes I2C-3 + * 0x43F8_8000 0x43F8_BFFF 16 Kbytes CAN-1 + * 0x43F8_C000 0x43F8_FFFF 16 Kbytes CAN-2 + * 0x43F9_0000 0x43F9_3FFF 16 Kbytes UART-1 + * 0x43F9_4000 0x43F9_7FFF 16 Kbytes UART-2 + * 0x43F9_8000 0x43F9_BFFF 16 Kbytes I2C-2 + * 0x43F9_C000 0x43F9_FFFF 16 Kbytes 1-Wire + * 0x43FA_0000 0x43FA_3FFF 16 Kbytes ATA (CPU side) + * 0x43FA_4000 0x43FA_7FFF 16 Kbytes CSPI-1 + * 0x43FA_8000 0x43FA_BFFF 16 Kbytes KPP + * 0x43FA_C000 0x43FA_FFFF 16 Kbytes IOMUXC + * 0x43FB_0000 0x43FB_3FFF 16 Kbytes AUDMUX + * 0x43FB_4000 0x43FB_7FFF 16 Kbytes Reserved + * 0x43FB_8000 0x43FB_BFFF 16 Kbytes ECT (IP BUS A) + * 0x43FB_C000 0x43FB_FFFF 16 Kbytes ECT (IP BUS B) + * 0x43FC_0000 0x43FF_FFFF 256 Kbytes Reserved AIPS A off-platform slots + * 0x4400_0000 0x4FFF_FFFF 192 Mbytes Reserved + * 0x5000_0000 0x5000_3FFF 16 Kbytes SPBA base address + * 0x5000_4000 0x5000_7FFF 16 Kbytes CSPI-3 + * 0x5000_8000 0x5000_BFFF 16 Kbytes UART-4 + * 0x5000_C000 0x5000_FFFF 16 Kbytes UART-3 + * 0x5001_0000 0x5001_3FFF 16 Kbytes CSPI-2 + * 0x5001_4000 0x5001_7FFF 16 Kbytes SSI-2 + * 0x5001_C000 0x5001_FFFF 16 Kbytes Reserved + * 0x5002_0000 0x5002_3FFF 16 Kbytes ATA + * 0x5002_4000 0x5002_7FFF 16 Kbytes SIM-1 + * 0x5002_8000 0x5002_BFFF 16 Kbytes SIM-2 + * 0x5002_C000 0x5002_FFFF 16 Kbytes UART-5 + * 0x5003_0000 0x5003_3FFF 16 Kbytes TSC + * 0x5003_4000 0x5003_7FFF 16 Kbytes SSI-1 + * 0x5003_8000 0x5003_BFFF 16 Kbytes FEC + * 0x5003_C000 0x5003_FFFF 16 Kbytes SPBA registers + * 0x5004_0000 0x51FF_FFFF 32 Mbytes (minus 256 Kbytes) + * 0x5200_0000 0x53EF_FFFF 31 Mbytes Reserved + * 0x53F0_0000 0x53F0_3FFF 16 Kbytes AIPS B control registers + * 0x53F0_4000 0x53F7_FFFF 496 Kbytes Reserved + * 0x53F8_0000 0x53F8_3FFF 16 Kbytes CCM + * 0x53F8_4000 0x53F8_7FFF 16 Kbytes GPT-4 + * 0x53F8_8000 0x53F8_BFFF 16 Kbytes GPT-3 + * 0x53F8_C000 0x53F8_FFFF 16 Kbytes GPT-2 + * 0x53F9_0000 0x53F9_3FFF 16 Kbytes GPT-1 + * 0x53F9_4000 0x53F9_7FFF 16 Kbytes EPIT-1 + * 0x53F9_8000 0x53F9_BFFF 16 Kbytes EPIT-2 + * 0x53F9_C000 0x53F9_FFFF 16 Kbytes GPIO-4 + * 0x53FA_0000 0x53FA_3FFF 16 Kbytes PWM-2 + * 0x53FA_4000 0x53FA_7FFF 16 Kbytes GPIO-3 + * 0x53FA_8000 0x53FA_BFFF 16 Kbytes PWM-3 + * 0x53FA_C000 0x53FA_FFFF 16 Kbytes SCC + * 0x53FB_0000 0x53FB_3FFF 16 Kbytes RNGB + * 0x53FB_4000 0x53FB_7FFF 16 Kbytes eSDHC-1 + * 0x53FB_8000 0x53FB_BFFF 16 Kbytes eSDHC-2 + * 0x53FB_C000 0x53FB_FFFF 16 Kbytes LCDC + * 0x53FC_0000 0x53FC_3FFF 16 Kbytes SLCDC + * 0x53FC_4000 0x53FC_7FFF 16 Kbytes Reserved + * 0x53FC_8000 0x53FC_BFFF 16 Kbytes PWM-4 + * 0x53FC_C000 0x53FC_FFFF 16 Kbytes GPIO-1 + * 0x53FD_0000 0x53FD_3FFF 16 Kbytes GPIO-2 + * 0x53FD_4000 0x53FD_7FFF 16 Kbytes SDMA + * 0x53FD_8000 0x53FD_BFFF 16 Kbytes Reserved + * 0x53FD_C000 0x53FD_FFFF 16 Kbytes WDOG + * 0x53FE_0000 0x53FE_3FFF 16 Kbytes PWM-1 + * 0x53FE_4000 0x53FE_7FFF 16 Kbytes Reserved + * 0x53FE_8000 0x53FE_BFFF 16 Kbytes Reserved + * 0x53FE_C000 0x53FE_FFFF 16 Kbytes RTICv3 + * 0x53FF_0000 0x53FF_3FFF 16 Kbytes IIM + * 0x53FF_4000 0x53FF_7FFF 16 Kbytes USB + * 0x53FF_8000 0x53FF_BFFF 16 Kbytes CSI + * 0x53FF_C000 0x53FF_FFFF 16 Kbytes DryIce + * 0x5400_0000 0x5FFF_FFFF 192 Mbytes Reserved (aliased AIPS B slots) + * 0x6000_0000 0x67FF_FFFF 128 Mbytes ARM926 platform ROMPATCH + * 0x6800_0000 0x6FFF_FFFF 128 Mbytes ARM926 platform ASIC + * 0x7000_0000 0x77FF_FFFF 128 Mbytes Reserved + * 0x7800_0000 0x7801_FFFF 128 Kbytes RAM + * 0x7802_0000 0x7FFF_FFFF 128 Mbytes (minus 128 Kbytes) + * 0x8000_0000 0x8FFF_FFFF 256 Mbytes SDRAM bank 0 + * 0x9000_0000 0x9FFF_FFFF 256 Mbytes SDRAM bank 1 + * 0xA000_0000 0xA7FF_FFFF 128 Mbytes WEIM CS0 (flash 128) 1 + * 0xA800_0000 0xAFFF_FFFF 128 Mbytes WEIM CS1 (flash 64) 1 + * 0xB000_0000 0xB1FF_FFFF 32 Mbytes WEIM CS2 (SRAM) + * 0xB200_0000 0xB3FF_FFFF 32 Mbytes WEIM CS3 (SRAM) + * 0xB400_0000 0xB5FF_FFFF 32 Mbytes WEIM CS4 + * 0xB600_0000 0xB7FF_FFFF 32 Mbytes Reserved + * 0xB800_0000 0xB800_0FFF 4 Kbytes Reserved + * 0xB800_1000 0xB800_1FFF 4 Kbytes SDRAM control registers + * 0xB800_2000 0xB800_2FFF 4 Kbytes WEIM control registers + * 0xB800_3000 0xB800_3FFF 4 Kbytes M3IF control registers + * 0xB800_4000 0xB800_4FFF 4 Kbytes EMI control registers + * 0xB800_5000 0xBAFF_FFFF 32 Mbytes (minus 20 Kbytes) + * 0xBB00_0000 0xBB00_0FFF 4 Kbytes NAND flash main area buffer + * 0xBB00_1000 0xBB00_11FF 512 B NAND flash spare area buffer + * 0xBB00_1200 0xBB00_1DFF 3 Kbytes Reserved + * 0xBB00_1E00 0xBB00_1FFF 512 B NAND flash control regisers + * 0xBB01_2000 0xBFFF_FFFF 96 Mbytes (minus 8 Kbytes) Reserved + * 0xC000_0000 0xFFFF_FFFF 1024 Mbytes Reserved + */ + +#define FSL_IMX25_ROM0_ADDR 0x00000000 +#define FSL_IMX25_ROM0_SIZE 0x4000 +#define FSL_IMX25_ROM1_ADDR 0x00404000 +#define FSL_IMX25_ROM1_SIZE 0x4000 +#define FSL_IMX25_I2C1_ADDR 0x43F80000 +#define FSL_IMX25_I2C1_SIZE 0x4000 +#define FSL_IMX25_I2C3_ADDR 0x43F84000 +#define FSL_IMX25_I2C3_SIZE 0x4000 +#define FSL_IMX25_UART1_ADDR 0x43F90000 +#define FSL_IMX25_UART1_SIZE 0x4000 +#define FSL_IMX25_UART2_ADDR 0x43F94000 +#define FSL_IMX25_UART2_SIZE 0x4000 +#define FSL_IMX25_I2C2_ADDR 0x43F98000 +#define FSL_IMX25_I2C2_SIZE 0x4000 +#define FSL_IMX25_UART4_ADDR 0x50008000 +#define FSL_IMX25_UART4_SIZE 0x4000 +#define FSL_IMX25_UART3_ADDR 0x5000C000 +#define FSL_IMX25_UART3_SIZE 0x4000 +#define FSL_IMX25_UART5_ADDR 0x5002C000 +#define FSL_IMX25_UART5_SIZE 0x4000 +#define FSL_IMX25_FEC_ADDR 0x50038000 +#define FSL_IMX25_FEC_SIZE 0x4000 +#define FSL_IMX25_CCM_ADDR 0x53F80000 +#define FSL_IMX25_CCM_SIZE 0x4000 +#define FSL_IMX25_GPT4_ADDR 0x53F84000 +#define FSL_IMX25_GPT4_SIZE 0x4000 +#define FSL_IMX25_GPT3_ADDR 0x53F88000 +#define FSL_IMX25_GPT3_SIZE 0x4000 +#define FSL_IMX25_GPT2_ADDR 0x53F8C000 +#define FSL_IMX25_GPT2_SIZE 0x4000 +#define FSL_IMX25_GPT1_ADDR 0x53F90000 +#define FSL_IMX25_GPT1_SIZE 0x4000 +#define FSL_IMX25_EPIT1_ADDR 0x53F94000 +#define FSL_IMX25_EPIT1_SIZE 0x4000 +#define FSL_IMX25_EPIT2_ADDR 0x53F98000 +#define FSL_IMX25_EPIT2_SIZE 0x4000 +#define FSL_IMX25_GPIO4_ADDR 0x53F9C000 +#define FSL_IMX25_GPIO4_SIZE 0x4000 +#define FSL_IMX25_GPIO3_ADDR 0x53FA4000 +#define FSL_IMX25_GPIO3_SIZE 0x4000 +#define FSL_IMX25_GPIO1_ADDR 0x53FCC000 +#define FSL_IMX25_GPIO1_SIZE 0x4000 +#define FSL_IMX25_GPIO2_ADDR 0x53FD0000 +#define FSL_IMX25_GPIO2_SIZE 0x4000 +#define FSL_IMX25_AVIC_ADDR 0x68000000 +#define FSL_IMX25_AVIC_SIZE 0x4000 +#define FSL_IMX25_IRAM_ADDR 0x78000000 +#define FSL_IMX25_IRAM_SIZE 0x20000 +#define FSL_IMX25_IRAM_ALIAS_ADDR 0x78020000 +#define FSL_IMX25_IRAM_ALIAS_SIZE 0x7FE0000 +#define FSL_IMX25_SDRAM0_ADDR 0x80000000 +#define FSL_IMX25_SDRAM0_SIZE 0x10000000 +#define FSL_IMX25_SDRAM1_ADDR 0x90000000 +#define FSL_IMX25_SDRAM1_SIZE 0x10000000 + +#define FSL_IMX25_UART1_IRQ 45 +#define FSL_IMX25_UART2_IRQ 32 +#define FSL_IMX25_UART3_IRQ 18 +#define FSL_IMX25_UART4_IRQ 5 +#define FSL_IMX25_UART5_IRQ 40 +#define FSL_IMX25_GPT1_IRQ 54 +#define FSL_IMX25_GPT2_IRQ 53 +#define FSL_IMX25_GPT3_IRQ 29 +#define FSL_IMX25_GPT4_IRQ 1 +#define FSL_IMX25_EPIT1_IRQ 28 +#define FSL_IMX25_EPIT2_IRQ 27 +#define FSL_IMX25_FEC_IRQ 57 +#define FSL_IMX25_I2C1_IRQ 3 +#define FSL_IMX25_I2C2_IRQ 4 +#define FSL_IMX25_I2C3_IRQ 10 +#define FSL_IMX25_GPIO1_IRQ 52 +#define FSL_IMX25_GPIO2_IRQ 51 +#define FSL_IMX25_GPIO3_IRQ 16 +#define FSL_IMX25_GPIO4_IRQ 23 + +#endif /* FSL_IMX25_H */ diff --git a/qemu/include/hw/arm/fsl-imx31.h b/qemu/include/hw/arm/fsl-imx31.h new file mode 100644 index 000000000..d408abbba --- /dev/null +++ b/qemu/include/hw/arm/fsl-imx31.h @@ -0,0 +1,122 @@ +/* + * Freescale i.MX31 SoC emulation + * + * Copyright (C) 2015 Jean-Christophe Dubois + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef FSL_IMX31_H +#define FSL_IMX31_H + +#include "hw/arm/arm.h" +#include "hw/intc/imx_avic.h" +#include "hw/misc/imx31_ccm.h" +#include "hw/char/imx_serial.h" +#include "hw/timer/imx_gpt.h" +#include "hw/timer/imx_epit.h" +#include "hw/i2c/imx_i2c.h" +#include "hw/gpio/imx_gpio.h" +#include "exec/memory.h" + +#define TYPE_FSL_IMX31 "fsl,imx31" +#define FSL_IMX31(obj) OBJECT_CHECK(FslIMX31State, (obj), TYPE_FSL_IMX31) + +#define FSL_IMX31_NUM_UARTS 2 +#define FSL_IMX31_NUM_EPITS 2 +#define FSL_IMX31_NUM_I2CS 3 +#define FSL_IMX31_NUM_GPIOS 3 + +typedef struct FslIMX31State { + /*< private >*/ + DeviceState parent_obj; + + /*< public >*/ + ARMCPU cpu; + IMXAVICState avic; + IMX31CCMState ccm; + IMXSerialState uart[FSL_IMX31_NUM_UARTS]; + IMXGPTState gpt; + IMXEPITState epit[FSL_IMX31_NUM_EPITS]; + IMXI2CState i2c[FSL_IMX31_NUM_I2CS]; + IMXGPIOState gpio[FSL_IMX31_NUM_GPIOS]; + MemoryRegion secure_rom; + MemoryRegion rom; + MemoryRegion iram; + MemoryRegion iram_alias; +} FslIMX31State; + +#define FSL_IMX31_SECURE_ROM_ADDR 0x00000000 +#define FSL_IMX31_SECURE_ROM_SIZE 0x4000 +#define FSL_IMX31_ROM_ADDR 0x00404000 +#define FSL_IMX31_ROM_SIZE 0x4000 +#define FSL_IMX31_IRAM_ALIAS_ADDR 0x10000000 +#define FSL_IMX31_IRAM_ALIAS_SIZE 0xFFC0000 +#define FSL_IMX31_IRAM_ADDR 0x1FFFC000 +#define FSL_IMX31_IRAM_SIZE 0x4000 +#define FSL_IMX31_I2C1_ADDR 0x43F80000 +#define FSL_IMX31_I2C1_SIZE 0x4000 +#define FSL_IMX31_I2C3_ADDR 0x43F84000 +#define FSL_IMX31_I2C3_SIZE 0x4000 +#define FSL_IMX31_UART1_ADDR 0x43F90000 +#define FSL_IMX31_UART1_SIZE 0x4000 +#define FSL_IMX31_UART2_ADDR 0x43F94000 +#define FSL_IMX31_UART2_SIZE 0x4000 +#define FSL_IMX31_I2C2_ADDR 0x43F98000 +#define FSL_IMX31_I2C2_SIZE 0x4000 +#define FSL_IMX31_CCM_ADDR 0x53F80000 +#define FSL_IMX31_CCM_SIZE 0x4000 +#define FSL_IMX31_GPT_ADDR 0x53F90000 +#define FSL_IMX31_GPT_SIZE 0x4000 +#define FSL_IMX31_EPIT1_ADDR 0x53F94000 +#define FSL_IMX31_EPIT1_SIZE 0x4000 +#define FSL_IMX31_EPIT2_ADDR 0x53F98000 +#define FSL_IMX31_EPIT2_SIZE 0x4000 +#define FSL_IMX31_GPIO3_ADDR 0x53FA4000 +#define FSL_IMX31_GPIO3_SIZE 0x4000 +#define FSL_IMX31_GPIO1_ADDR 0x53FCC000 +#define FSL_IMX31_GPIO1_SIZE 0x4000 +#define FSL_IMX31_GPIO2_ADDR 0x53FD0000 +#define FSL_IMX31_GPIO2_SIZE 0x4000 +#define FSL_IMX31_AVIC_ADDR 0x68000000 +#define FSL_IMX31_AVIC_SIZE 0x100 +#define FSL_IMX31_SDRAM0_ADDR 0x80000000 +#define FSL_IMX31_SDRAM0_SIZE 0x10000000 +#define FSL_IMX31_SDRAM1_ADDR 0x90000000 +#define FSL_IMX31_SDRAM1_SIZE 0x10000000 +#define FSL_IMX31_FLASH0_ADDR 0xA0000000 +#define FSL_IMX31_FLASH0_SIZE 0x8000000 +#define FSL_IMX31_FLASH1_ADDR 0xA8000000 +#define FSL_IMX31_FLASH1_SIZE 0x8000000 +#define FSL_IMX31_CS2_ADDR 0xB0000000 +#define FSL_IMX31_CS2_SIZE 0x2000000 +#define FSL_IMX31_CS3_ADDR 0xB2000000 +#define FSL_IMX31_CS3_SIZE 0x2000000 +#define FSL_IMX31_CS4_ADDR 0xB4000000 +#define FSL_IMX31_CS4_SIZE 0x2000000 +#define FSL_IMX31_CS5_ADDR 0xB6000000 +#define FSL_IMX31_CS5_SIZE 0x2000000 +#define FSL_IMX31_NAND_ADDR 0xB8000000 +#define FSL_IMX31_NAND_SIZE 0x1000 + +#define FSL_IMX31_EPIT2_IRQ 27 +#define FSL_IMX31_EPIT1_IRQ 28 +#define FSL_IMX31_GPT_IRQ 29 +#define FSL_IMX31_UART2_IRQ 32 +#define FSL_IMX31_UART1_IRQ 45 +#define FSL_IMX31_I2C1_IRQ 10 +#define FSL_IMX31_I2C2_IRQ 4 +#define FSL_IMX31_I2C3_IRQ 3 +#define FSL_IMX31_GPIO1_IRQ 52 +#define FSL_IMX31_GPIO2_IRQ 51 +#define FSL_IMX31_GPIO3_IRQ 56 + +#endif /* FSL_IMX31_H */ diff --git a/qemu/include/hw/arm/imx.h b/qemu/include/hw/arm/imx.h deleted file mode 100644 index ea9e09327..000000000 --- a/qemu/include/hw/arm/imx.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * i.MX31 emulation - * - * Copyright (C) 2012 Peter Chubb - * NICTA - * - * This code is released under the GPL, version 2.0 or later - * See the file `../COPYING' for details. - */ - -#ifndef IMX_H -#define IMX_H - -void imx_serial_create(int uart, const hwaddr addr, qemu_irq irq); - -typedef enum { - NOCLK, - MCU, - HSP, - IPG, - CLK_32k -} IMXClk; - -uint32_t imx_clock_frequency(DeviceState *s, IMXClk clock); - -void imx_timerp_create(const hwaddr addr, - qemu_irq irq, - DeviceState *ccm); -void imx_timerg_create(const hwaddr addr, - qemu_irq irq, - DeviceState *ccm); - - -#endif /* IMX_H */ diff --git a/qemu/include/hw/arm/linux-boot-if.h b/qemu/include/hw/arm/linux-boot-if.h new file mode 100644 index 000000000..aba4479a1 --- /dev/null +++ b/qemu/include/hw/arm/linux-boot-if.h @@ -0,0 +1,43 @@ +/* + * hw/arm/linux-boot-if.h : interface for devices which need to behave + * specially for direct boot of an ARM Linux kernel + */ + +#ifndef HW_ARM_LINUX_BOOT_IF_H +#define HW_ARM_LINUX_BOOT_IF_H + +#include "qom/object.h" + +#define TYPE_ARM_LINUX_BOOT_IF "arm-linux-boot-if" +#define ARM_LINUX_BOOT_IF_CLASS(klass) \ + OBJECT_CLASS_CHECK(ARMLinuxBootIfClass, (klass), TYPE_ARM_LINUX_BOOT_IF) +#define ARM_LINUX_BOOT_IF_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ARMLinuxBootIfClass, (obj), TYPE_ARM_LINUX_BOOT_IF) +#define ARM_LINUX_BOOT_IF(obj) \ + INTERFACE_CHECK(ARMLinuxBootIf, (obj), TYPE_ARM_LINUX_BOOT_IF) + +typedef struct ARMLinuxBootIf { + /*< private >*/ + Object parent_obj; +} ARMLinuxBootIf; + +typedef struct ARMLinuxBootIfClass { + /*< private >*/ + InterfaceClass parent_class; + + /*< public >*/ + /** arm_linux_init: configure the device for a direct boot + * of an ARM Linux kernel (so that device reset puts it into + * the state the kernel expects after firmware initialization, + * rather than the true hardware reset state). This callback is + * called once after machine construction is complete (before the + * first system reset). + * + * @obj: the object implementing this interface + * @secure_boot: true if we are booting Secure, false for NonSecure + * (or for a CPU which doesn't support TrustZone) + */ + void (*arm_linux_init)(ARMLinuxBootIf *obj, bool secure_boot); +} ARMLinuxBootIfClass; + +#endif diff --git a/qemu/include/hw/arm/raspi_platform.h b/qemu/include/hw/arm/raspi_platform.h new file mode 100644 index 000000000..6467e88ae --- /dev/null +++ b/qemu/include/hw/arm/raspi_platform.h @@ -0,0 +1,128 @@ +/* + * bcm2708 aka bcm2835/2836 aka Raspberry Pi/Pi2 SoC platform defines + * + * These definitions are derived from those in Raspbian Linux at + * arch/arm/mach-{bcm2708,bcm2709}/include/mach/platform.h + * where they carry the following notice: + * + * Copyright (C) 2010 Broadcom + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MCORE_OFFSET 0x0000 /* Fake frame buffer device + * (the multicore sync block) */ +#define IC0_OFFSET 0x2000 +#define ST_OFFSET 0x3000 /* System Timer */ +#define MPHI_OFFSET 0x6000 /* Message-based Parallel Host Intf. */ +#define DMA_OFFSET 0x7000 /* DMA controller, channels 0-14 */ +#define ARM_OFFSET 0xB000 /* BCM2708 ARM control block */ +#define ARMCTRL_OFFSET (ARM_OFFSET + 0x000) +#define ARMCTRL_IC_OFFSET (ARM_OFFSET + 0x200) /* Interrupt controller */ +#define ARMCTRL_TIMER0_1_OFFSET (ARM_OFFSET + 0x400) /* Timer 0 and 1 */ +#define ARMCTRL_0_SBM_OFFSET (ARM_OFFSET + 0x800) /* User 0 (ARM) Semaphores + * Doorbells & Mailboxes */ +#define PM_OFFSET 0x100000 /* Power Management, Reset controller + * and Watchdog registers */ +#define PCM_CLOCK_OFFSET 0x101098 +#define RNG_OFFSET 0x104000 +#define GPIO_OFFSET 0x200000 +#define UART0_OFFSET 0x201000 +#define MMCI0_OFFSET 0x202000 +#define I2S_OFFSET 0x203000 +#define SPI0_OFFSET 0x204000 +#define BSC0_OFFSET 0x205000 /* BSC0 I2C/TWI */ +#define UART1_OFFSET 0x215000 +#define EMMC_OFFSET 0x300000 +#define SMI_OFFSET 0x600000 +#define BSC1_OFFSET 0x804000 /* BSC1 I2C/TWI */ +#define USB_OFFSET 0x980000 /* DTC_OTG USB controller */ +#define DMA15_OFFSET 0xE05000 /* DMA controller, channel 15 */ + +/* GPU interrupts */ +#define INTERRUPT_TIMER0 0 +#define INTERRUPT_TIMER1 1 +#define INTERRUPT_TIMER2 2 +#define INTERRUPT_TIMER3 3 +#define INTERRUPT_CODEC0 4 +#define INTERRUPT_CODEC1 5 +#define INTERRUPT_CODEC2 6 +#define INTERRUPT_JPEG 7 +#define INTERRUPT_ISP 8 +#define INTERRUPT_USB 9 +#define INTERRUPT_3D 10 +#define INTERRUPT_TRANSPOSER 11 +#define INTERRUPT_MULTICORESYNC0 12 +#define INTERRUPT_MULTICORESYNC1 13 +#define INTERRUPT_MULTICORESYNC2 14 +#define INTERRUPT_MULTICORESYNC3 15 +#define INTERRUPT_DMA0 16 +#define INTERRUPT_DMA1 17 +#define INTERRUPT_DMA2 18 +#define INTERRUPT_DMA3 19 +#define INTERRUPT_DMA4 20 +#define INTERRUPT_DMA5 21 +#define INTERRUPT_DMA6 22 +#define INTERRUPT_DMA7 23 +#define INTERRUPT_DMA8 24 +#define INTERRUPT_DMA9 25 +#define INTERRUPT_DMA10 26 +#define INTERRUPT_DMA11 27 +#define INTERRUPT_DMA12 28 +#define INTERRUPT_AUX 29 +#define INTERRUPT_ARM 30 +#define INTERRUPT_VPUDMA 31 +#define INTERRUPT_HOSTPORT 32 +#define INTERRUPT_VIDEOSCALER 33 +#define INTERRUPT_CCP2TX 34 +#define INTERRUPT_SDC 35 +#define INTERRUPT_DSI0 36 +#define INTERRUPT_AVE 37 +#define INTERRUPT_CAM0 38 +#define INTERRUPT_CAM1 39 +#define INTERRUPT_HDMI0 40 +#define INTERRUPT_HDMI1 41 +#define INTERRUPT_PIXELVALVE1 42 +#define INTERRUPT_I2CSPISLV 43 +#define INTERRUPT_DSI1 44 +#define INTERRUPT_PWA0 45 +#define INTERRUPT_PWA1 46 +#define INTERRUPT_CPR 47 +#define INTERRUPT_SMI 48 +#define INTERRUPT_GPIO0 49 +#define INTERRUPT_GPIO1 50 +#define INTERRUPT_GPIO2 51 +#define INTERRUPT_GPIO3 52 +#define INTERRUPT_I2C 53 +#define INTERRUPT_SPI 54 +#define INTERRUPT_I2SPCM 55 +#define INTERRUPT_SDIO 56 +#define INTERRUPT_UART 57 +#define INTERRUPT_SLIMBUS 58 +#define INTERRUPT_VEC 59 +#define INTERRUPT_CPG 60 +#define INTERRUPT_RNG 61 +#define INTERRUPT_ARASANSDIO 62 +#define INTERRUPT_AVSPMON 63 + +/* ARM CPU IRQs use a private number space */ +#define INTERRUPT_ARM_TIMER 0 +#define INTERRUPT_ARM_MAILBOX 1 +#define INTERRUPT_ARM_DOORBELL_0 2 +#define INTERRUPT_ARM_DOORBELL_1 3 +#define INTERRUPT_VPU0_HALTED 4 +#define INTERRUPT_VPU1_HALTED 5 +#define INTERRUPT_ILLEGAL_TYPE0 6 +#define INTERRUPT_ILLEGAL_TYPE1 7 diff --git a/qemu/include/hw/arm/virt-acpi-build.h b/qemu/include/hw/arm/virt-acpi-build.h index 04f174d52..7d3700ebf 100644 --- a/qemu/include/hw/arm/virt-acpi-build.h +++ b/qemu/include/hw/arm/virt-acpi-build.h @@ -23,7 +23,6 @@ #include "qemu-common.h" #include "hw/arm/virt.h" -#define VIRT_ACPI_CPU_ID_LIMIT 8 #define ACPI_GICC_ENABLED 1 typedef struct VirtGuestInfo { @@ -31,6 +30,8 @@ typedef struct VirtGuestInfo { FWCfgState *fw_cfg; const MemMapEntry *memmap; const int *irqmap; + bool use_highmem; + int gic_version; } VirtGuestInfo; diff --git a/qemu/include/hw/arm/virt.h b/qemu/include/hw/arm/virt.h index d22fd8e50..ecd858960 100644 --- a/qemu/include/hw/arm/virt.h +++ b/qemu/include/hw/arm/virt.h @@ -46,6 +46,9 @@ enum { VIRT_CPUPERIPHS, VIRT_GIC_DIST, VIRT_GIC_CPU, + VIRT_GIC_V2M, + VIRT_GIC_ITS, + VIRT_GIC_REDIST, VIRT_UART, VIRT_MMIO, VIRT_RTC, @@ -54,8 +57,11 @@ enum { VIRT_PCIE_MMIO, VIRT_PCIE_PIO, VIRT_PCIE_ECAM, - VIRT_GIC_V2M, VIRT_PLATFORM_BUS, + VIRT_PCIE_MMIO_HIGH, + VIRT_GPIO, + VIRT_SECURE_UART, + VIRT_SECURE_MEM, }; typedef struct MemMapEntry { diff --git a/qemu/include/hw/arm/xlnx-zynqmp.h b/qemu/include/hw/arm/xlnx-zynqmp.h index c379632f2..2332596b4 100644 --- a/qemu/include/hw/arm/xlnx-zynqmp.h +++ b/qemu/include/hw/arm/xlnx-zynqmp.h @@ -22,6 +22,10 @@ #include "hw/intc/arm_gic.h" #include "hw/net/cadence_gem.h" #include "hw/char/cadence_uart.h" +#include "hw/ide/pci.h" +#include "hw/ide/ahci.h" +#include "hw/sd/sdhci.h" +#include "hw/ssi/xilinx_spips.h" #define TYPE_XLNX_ZYNQMP "xlnx,zynqmp" #define XLNX_ZYNQMP(obj) OBJECT_CHECK(XlnxZynqMPState, (obj), \ @@ -31,6 +35,12 @@ #define XLNX_ZYNQMP_NUM_RPU_CPUS 2 #define XLNX_ZYNQMP_NUM_GEMS 4 #define XLNX_ZYNQMP_NUM_UARTS 2 +#define XLNX_ZYNQMP_NUM_SDHCI 2 +#define XLNX_ZYNQMP_NUM_SPIS 2 + +#define XLNX_ZYNQMP_NUM_OCM_BANKS 4 +#define XLNX_ZYNQMP_OCM_RAM_0_ADDRESS 0xFFFC0000 +#define XLNX_ZYNQMP_OCM_RAM_SIZE 0x10000 #define XLNX_ZYNQMP_GIC_REGIONS 2 @@ -40,9 +50,17 @@ * number of memory region aliases. */ -#define XLNX_ZYNQMP_GIC_REGION_SIZE 0x4000 +#define XLNX_ZYNQMP_GIC_REGION_SIZE 0x1000 #define XLNX_ZYNQMP_GIC_ALIASES (0x10000 / XLNX_ZYNQMP_GIC_REGION_SIZE - 1) +#define XLNX_ZYNQMP_MAX_LOW_RAM_SIZE 0x80000000ull + +#define XLNX_ZYNQMP_MAX_HIGH_RAM_SIZE 0x800000000ull +#define XLNX_ZYNQMP_HIGH_RAM_START 0x800000000ull + +#define XLNX_ZYNQMP_MAX_RAM_SIZE (XLNX_ZYNQMP_MAX_LOW_RAM_SIZE + \ + XLNX_ZYNQMP_MAX_HIGH_RAM_SIZE) + typedef struct XlnxZynqMPState { /*< private >*/ DeviceState parent_obj; @@ -52,8 +70,17 @@ typedef struct XlnxZynqMPState { ARMCPU rpu_cpu[XLNX_ZYNQMP_NUM_RPU_CPUS]; GICState gic; MemoryRegion gic_mr[XLNX_ZYNQMP_GIC_REGIONS][XLNX_ZYNQMP_GIC_ALIASES]; + + MemoryRegion ocm_ram[XLNX_ZYNQMP_NUM_OCM_BANKS]; + + MemoryRegion *ddr_ram; + MemoryRegion ddr_ram_low, ddr_ram_high; + CadenceGEMState gem[XLNX_ZYNQMP_NUM_GEMS]; CadenceUARTState uart[XLNX_ZYNQMP_NUM_UARTS]; + SysbusAHCIState sata; + SDHCIState sdhci[XLNX_ZYNQMP_NUM_SDHCI]; + XilinxSPIPS spi[XLNX_ZYNQMP_NUM_SPIS]; char *boot_cpu; ARMCPU *boot_cpu_ptr; diff --git a/qemu/include/hw/block/block.h b/qemu/include/hw/block/block.h index 8d7c4b413..984660efd 100644 --- a/qemu/include/hw/block/block.h +++ b/qemu/include/hw/block/block.h @@ -12,7 +12,6 @@ #define HW_BLOCK_COMMON_H #include "qemu-common.h" -#include "qapi/error.h" /* Configuration */ diff --git a/qemu/include/hw/block/fdc.h b/qemu/include/hw/block/fdc.h index d48b2f860..1749dabf2 100644 --- a/qemu/include/hw/block/fdc.h +++ b/qemu/include/hw/block/fdc.h @@ -6,13 +6,6 @@ /* fdc.c */ #define MAX_FD 2 -typedef enum FDriveType { - FDRIVE_DRV_144 = 0x00, /* 1.44 MB 3"5 drive */ - FDRIVE_DRV_288 = 0x01, /* 2.88 MB 3"5 drive */ - FDRIVE_DRV_120 = 0x02, /* 1.2 MB 5"25 drive */ - FDRIVE_DRV_NONE = 0x03, /* No drive connected */ -} FDriveType; - #define TYPE_ISA_FDC "isa-fdc" ISADevice *fdctrl_init_isa(ISABus *bus, DriveInfo **fds); @@ -21,6 +14,8 @@ void fdctrl_init_sysbus(qemu_irq irq, int dma_chann, void sun4m_fdctrl_init(qemu_irq irq, hwaddr io_base, DriveInfo **fds, qemu_irq *fdc_tc); -FDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i); +FloppyDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i); +void isa_fdc_get_drive_max_chs(FloppyDriveType type, + uint8_t *maxc, uint8_t *maxh, uint8_t *maxs); #endif diff --git a/qemu/include/hw/boards.h b/qemu/include/hw/boards.h index 2aec9cbb1..8d4fe56b5 100644 --- a/qemu/include/hw/boards.h +++ b/qemu/include/hw/boards.h @@ -3,43 +3,23 @@ #ifndef HW_BOARDS_H #define HW_BOARDS_H -#include "qemu/typedefs.h" #include "sysemu/blockdev.h" #include "sysemu/accel.h" #include "hw/qdev.h" #include "qom/object.h" - - -typedef void QEMUMachineInitFunc(MachineState *ms); - -typedef void QEMUMachineResetFunc(void); - -typedef void QEMUMachineHotAddCPUFunc(const int64_t id, Error **errp); - -typedef int QEMUMachineGetKvmtypeFunc(const char *arg); - -struct QEMUMachine { - const char *name; - const char *desc; - QEMUMachineInitFunc *init; - QEMUMachineGetKvmtypeFunc *kvm_type; - BlockInterfaceType block_default_type; - int max_cpus; - unsigned int - no_sdcard:1, - has_dynamic_sysbus:1; - int is_default; - const char *default_machine_opts; - const char *default_boot_order; -}; +#include "qom/cpu.h" void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner, const char *name, uint64_t ram_size); -int qemu_register_machine(QEMUMachine *m); - #define TYPE_MACHINE_SUFFIX "-machine" + +/* Machine class name that needs to be used for class-name-based machine + * type lookup to work. + */ +#define MACHINE_TYPE_NAME(machinename) (machinename TYPE_MACHINE_SUFFIX) + #define TYPE_MACHINE "machine" #undef MACHINE /* BSD defines it and QEMU does not use it */ #define MACHINE(obj) \ @@ -53,17 +33,36 @@ MachineClass *find_default_machine(void); extern MachineState *current_machine; bool machine_usb(MachineState *machine); -bool machine_iommu(MachineState *machine); bool machine_kernel_irqchip_allowed(MachineState *machine); bool machine_kernel_irqchip_required(MachineState *machine); +bool machine_kernel_irqchip_split(MachineState *machine); int machine_kvm_shadow_mem(MachineState *machine); int machine_phandle_start(MachineState *machine); bool machine_dump_guest_core(MachineState *machine); bool machine_mem_merge(MachineState *machine); +/** + * CPUArchId: + * @arch_id - architecture-dependent CPU ID of present or possible CPU + * @cpu - pointer to corresponding CPU object if it's present on NULL otherwise + */ +typedef struct { + uint64_t arch_id; + struct CPUState *cpu; +} CPUArchId; + +/** + * CPUArchIdList: + * @len - number of @CPUArchId items in @cpus array + * @cpus - array of present or possible CPUs for current machine configuration + */ +typedef struct { + int len; + CPUArchId cpus[0]; +} CPUArchIdList; + /** * MachineClass: - * @qemu_machine: #QEMUMachine * @get_hotplug_handler: this function is called during bus-less * device hotplug. If defined it returns pointer to an instance * of HotplugHandler object, which handles hotplug operation @@ -73,6 +72,15 @@ bool machine_mem_merge(MachineState *machine); * used to provide @cpu_index to socket number mapping, allowing * a machine to group CPU threads belonging to the same socket/package * Returns: socket number given cpu_index belongs to. + * @hw_version: + * Value of QEMU_VERSION when the machine was added to QEMU. + * Set only by old machines because they need to keep + * compatibility on code that exposed QEMU_VERSION to guests in + * the past (and now use qemu_hw_version()). + * @possible_cpu_arch_ids: + * Returns an array of @CPUArchId architecture-dependent CPU IDs + * which includes CPU IDs for present and possible to hotplug CPUs. + * Caller is responsible for freeing returned list. */ struct MachineClass { /*< private >*/ @@ -100,7 +108,8 @@ struct MachineClass { no_cdrom:1, no_sdcard:1, has_dynamic_sysbus:1, - no_tco:1; + pci_allow_0_address:1, + legacy_fw_cfg_order:1; int is_default; const char *default_machine_opts; const char *default_boot_order; @@ -108,10 +117,13 @@ struct MachineClass { GlobalProperty *compat_props; const char *hw_version; ram_addr_t default_ram_size; + bool option_rom_has_mr; + bool rom_file_has_mr; HotplugHandler *(*get_hotplug_handler)(MachineState *machine, DeviceState *dev); unsigned (*cpu_index_to_socket_id)(unsigned cpu_index); + CPUArchIdList *(*possible_cpu_arch_ids)(MachineState *machine); }; /** @@ -127,6 +139,7 @@ struct MachineState { char *accel; bool kernel_irqchip_allowed; bool kernel_irqchip_required; + bool kernel_irqchip_split; int kvm_shadow_mem; char *dtb; char *dumpdtb; @@ -136,9 +149,11 @@ struct MachineState { bool mem_merge; bool usb; bool usb_disabled; + bool igd_gfx_passthru; char *firmware; bool iommu; bool suppress_vmdesc; + bool enforce_config_section; ram_addr_t ram_size; ram_addr_t maxram_size; @@ -151,4 +166,30 @@ struct MachineState { AccelState *accelerator; }; +#define DEFINE_MACHINE(namestr, machine_initfn) \ + static void machine_initfn##_class_init(ObjectClass *oc, void *data) \ + { \ + MachineClass *mc = MACHINE_CLASS(oc); \ + machine_initfn(mc); \ + } \ + static const TypeInfo machine_initfn##_typeinfo = { \ + .name = MACHINE_TYPE_NAME(namestr), \ + .parent = TYPE_MACHINE, \ + .class_init = machine_initfn##_class_init, \ + }; \ + static void machine_initfn##_register_types(void) \ + { \ + type_register_static(&machine_initfn##_typeinfo); \ + } \ + type_init(machine_initfn##_register_types) + +#define SET_MACHINE_COMPAT(m, COMPAT) \ + do { \ + static GlobalProperty props[] = { \ + COMPAT \ + { /* end of list */ } \ + }; \ + (m)->compat_props = props; \ + } while (0) + #endif diff --git a/qemu/include/hw/bt.h b/qemu/include/hw/bt.h index cb2a7e657..c7c7909a3 100644 --- a/qemu/include/hw/bt.h +++ b/qemu/include/hw/bt.h @@ -504,7 +504,6 @@ typedef struct { #define OCF_CREATE_CONN_CANCEL 0x0008 typedef struct { - uint8_t status; bdaddr_t bdaddr; } QEMU_PACKED create_conn_cancel_cp; #define CREATE_CONN_CANCEL_CP_SIZE 6 @@ -1266,13 +1265,13 @@ typedef struct { uint8_t status; uint16_t handle; } QEMU_PACKED reset_failed_contact_counter_rp; -#define RESET_FAILED_CONTACT_COUNTER_RP_SIZE 4 +#define RESET_FAILED_CONTACT_COUNTER_RP_SIZE 3 #define OCF_READ_LINK_QUALITY 0x0003 typedef struct { uint16_t handle; } QEMU_PACKED read_link_quality_cp; -#define READ_LINK_QUALITY_CP_SIZE 4 +#define READ_LINK_QUALITY_CP_SIZE 2 typedef struct { uint8_t status; @@ -1332,7 +1331,7 @@ typedef struct { uint8_t dev_class[3]; uint16_t clock_offset; } QEMU_PACKED inquiry_info; -#define INQUIRY_INFO_SIZE 14 +#define INQUIRY_INFO_SIZE 15 #define EVT_CONN_COMPLETE 0x03 typedef struct { @@ -1381,7 +1380,7 @@ typedef struct { uint16_t handle; uint8_t encrypt; } QEMU_PACKED evt_encrypt_change; -#define EVT_ENCRYPT_CHANGE_SIZE 5 +#define EVT_ENCRYPT_CHANGE_SIZE 4 #define EVT_CHANGE_CONN_LINK_KEY_COMPLETE 0x09 typedef struct { @@ -1629,18 +1628,6 @@ typedef struct { } QEMU_PACKED evt_sniff_subrate; #define EVT_SNIFF_SUBRATE_SIZE 11 -#define EVT_EXTENDED_INQUIRY_RESULT 0x2F -typedef struct { - bdaddr_t bdaddr; - uint8_t pscan_rep_mode; - uint8_t pscan_period_mode; - uint8_t dev_class[3]; - uint16_t clock_offset; - int8_t rssi; - uint8_t data[240]; -} QEMU_PACKED extended_inquiry_info; -#define EXTENDED_INQUIRY_INFO_SIZE 254 - #define EVT_TESTING 0xFE #define EVT_VENDOR 0xFF diff --git a/qemu/include/hw/char/bcm2835_aux.h b/qemu/include/hw/char/bcm2835_aux.h new file mode 100644 index 000000000..42f0ee7a9 --- /dev/null +++ b/qemu/include/hw/char/bcm2835_aux.h @@ -0,0 +1,33 @@ +/* + * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2835_AUX_H +#define BCM2835_AUX_H + +#include "hw/sysbus.h" +#include "sysemu/char.h" + +#define TYPE_BCM2835_AUX "bcm2835-aux" +#define BCM2835_AUX(obj) OBJECT_CHECK(BCM2835AuxState, (obj), TYPE_BCM2835_AUX) + +#define BCM2835_AUX_RX_FIFO_LEN 8 + +typedef struct { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion iomem; + CharDriverState *chr; + qemu_irq irq; + + uint8_t read_fifo[BCM2835_AUX_RX_FIFO_LEN]; + uint8_t read_pos, read_count; + uint8_t ier, iir; +} BCM2835AuxState; + +#endif diff --git a/qemu/include/hw/char/digic-uart.h b/qemu/include/hw/char/digic-uart.h index ef83a3059..7b3f14537 100644 --- a/qemu/include/hw/char/digic-uart.h +++ b/qemu/include/hw/char/digic-uart.h @@ -19,7 +19,6 @@ #define HW_CHAR_DIGIC_UART_H #include "hw/sysbus.h" -#include "qemu/typedefs.h" #define TYPE_DIGIC_UART "digic-uart" #define DIGIC_UART(obj) \ diff --git a/qemu/include/hw/char/imx_serial.h b/qemu/include/hw/char/imx_serial.h new file mode 100644 index 000000000..6cd75c0ba --- /dev/null +++ b/qemu/include/hw/char/imx_serial.h @@ -0,0 +1,102 @@ +/* + * Device model for i.MX UART + * + * Copyright (c) 2008 OKL + * Originally Written by Hans Jiang + * Copyright (c) 2011 NICTA Pty Ltd. + * Updated by Jean-Christophe Dubois + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef IMX_SERIAL_H +#define IMX_SERIAL_H + +#include "hw/sysbus.h" + +#define TYPE_IMX_SERIAL "imx.serial" +#define IMX_SERIAL(obj) OBJECT_CHECK(IMXSerialState, (obj), TYPE_IMX_SERIAL) + +#define URXD_CHARRDY (1<<15) /* character read is valid */ +#define URXD_ERR (1<<14) /* Character has error */ +#define URXD_BRK (1<<11) /* Break received */ + +#define USR1_PARTYER (1<<15) /* Parity Error */ +#define USR1_RTSS (1<<14) /* RTS pin status */ +#define USR1_TRDY (1<<13) /* Tx ready */ +#define USR1_RTSD (1<<12) /* RTS delta: pin changed state */ +#define USR1_ESCF (1<<11) /* Escape sequence interrupt */ +#define USR1_FRAMERR (1<<10) /* Framing error */ +#define USR1_RRDY (1<<9) /* receiver ready */ +#define USR1_AGTIM (1<<8) /* Aging timer interrupt */ +#define USR1_DTRD (1<<7) /* DTR changed */ +#define USR1_RXDS (1<<6) /* Receiver is idle */ +#define USR1_AIRINT (1<<5) /* Aysnch IR interrupt */ +#define USR1_AWAKE (1<<4) /* Falling edge detected on RXd pin */ + +#define USR2_ADET (1<<15) /* Autobaud complete */ +#define USR2_TXFE (1<<14) /* Transmit FIFO empty */ +#define USR2_DTRF (1<<13) /* DTR/DSR transition */ +#define USR2_IDLE (1<<12) /* UART has been idle for too long */ +#define USR2_ACST (1<<11) /* Autobaud counter stopped */ +#define USR2_RIDELT (1<<10) /* Ring Indicator delta */ +#define USR2_RIIN (1<<9) /* Ring Indicator Input */ +#define USR2_IRINT (1<<8) /* Serial Infrared Interrupt */ +#define USR2_WAKE (1<<7) /* Start bit detected */ +#define USR2_DCDDELT (1<<6) /* Data Carrier Detect delta */ +#define USR2_DCDIN (1<<5) /* Data Carrier Detect Input */ +#define USR2_RTSF (1<<4) /* RTS transition */ +#define USR2_TXDC (1<<3) /* Transmission complete */ +#define USR2_BRCD (1<<2) /* Break condition detected */ +#define USR2_ORE (1<<1) /* Overrun error */ +#define USR2_RDR (1<<0) /* Receive data ready */ + +#define UCR1_TRDYEN (1<<13) /* Tx Ready Interrupt Enable */ +#define UCR1_RRDYEN (1<<9) /* Rx Ready Interrupt Enable */ +#define UCR1_TXMPTYEN (1<<6) /* Tx Empty Interrupt Enable */ +#define UCR1_UARTEN (1<<0) /* UART Enable */ + +#define UCR2_TXEN (1<<2) /* Transmitter enable */ +#define UCR2_RXEN (1<<1) /* Receiver enable */ +#define UCR2_SRST (1<<0) /* Reset complete */ + +#define UTS1_TXEMPTY (1<<6) +#define UTS1_RXEMPTY (1<<5) +#define UTS1_TXFULL (1<<4) +#define UTS1_RXFULL (1<<3) + +typedef struct IMXSerialState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + int32_t readbuff; + + uint32_t usr1; + uint32_t usr2; + uint32_t ucr1; + uint32_t ucr2; + uint32_t uts1; + + /* + * The registers below are implemented just so that the + * guest OS sees what it has written + */ + uint32_t onems; + uint32_t ufcr; + uint32_t ubmr; + uint32_t ubrc; + uint32_t ucr3; + + qemu_irq irq; + CharDriverState *chr; +} IMXSerialState; + +#endif diff --git a/qemu/include/hw/compat.h b/qemu/include/hw/compat.h index 94c8097d0..a5dbbf898 100644 --- a/qemu/include/hw/compat.h +++ b/qemu/include/hw/compat.h @@ -1,61 +1,112 @@ #ifndef HW_COMPAT_H #define HW_COMPAT_H +#define HW_COMPAT_2_5 \ + {\ + .driver = "isa-fdc",\ + .property = "fallback",\ + .value = "144",\ + },{\ + .driver = "pvscsi",\ + .property = "x-old-pci-configuration",\ + .value = "on",\ + },{\ + .driver = "pvscsi",\ + .property = "x-disable-pcie",\ + .value = "on",\ + },\ + {\ + .driver = "vmxnet3",\ + .property = "x-old-msi-offsets",\ + .value = "on",\ + },{\ + .driver = "vmxnet3",\ + .property = "x-disable-pcie",\ + .value = "on",\ + }, + +#define HW_COMPAT_2_4 \ + {\ + .driver = "virtio-blk-device",\ + .property = "scsi",\ + .value = "true",\ + },{\ + .driver = "e1000",\ + .property = "extra_mac_registers",\ + .value = "off",\ + },{\ + .driver = "virtio-pci",\ + .property = "x-disable-pcie",\ + .value = "on",\ + },{\ + .driver = "virtio-pci",\ + .property = "migrate-extra",\ + .value = "off",\ + },{\ + .driver = "fw_cfg_mem",\ + .property = "dma_enabled",\ + .value = "off",\ + },{\ + .driver = "fw_cfg_io",\ + .property = "dma_enabled",\ + .value = "off",\ + }, + #define HW_COMPAT_2_3 \ - {\ - .driver = "virtio-blk-pci",\ - .property = "any_layout",\ - .value = "off",\ - },{\ - .driver = "virtio-balloon-pci",\ - .property = "any_layout",\ - .value = "off",\ - },{\ - .driver = "virtio-serial-pci",\ - .property = "any_layout",\ - .value = "off",\ - },{\ - .driver = "virtio-9p-pci",\ - .property = "any_layout",\ - .value = "off",\ - },{\ - .driver = "virtio-rng-pci",\ - .property = "any_layout",\ - .value = "off",\ - }, + {\ + .driver = "virtio-blk-pci",\ + .property = "any_layout",\ + .value = "off",\ + },{\ + .driver = "virtio-balloon-pci",\ + .property = "any_layout",\ + .value = "off",\ + },{\ + .driver = "virtio-serial-pci",\ + .property = "any_layout",\ + .value = "off",\ + },{\ + .driver = "virtio-9p-pci",\ + .property = "any_layout",\ + .value = "off",\ + },{\ + .driver = "virtio-rng-pci",\ + .property = "any_layout",\ + .value = "off",\ + }, #define HW_COMPAT_2_2 \ - /* empty */ + /* empty */ #define HW_COMPAT_2_1 \ - {\ - .driver = "intel-hda",\ - .property = "old_msi_addr",\ - .value = "on",\ - },{\ - .driver = "VGA",\ - .property = "qemu-extended-regs",\ - .value = "off",\ - },{\ - .driver = "secondary-vga",\ - .property = "qemu-extended-regs",\ - .value = "off",\ - },{\ - .driver = "virtio-scsi-pci",\ - .property = "any_layout",\ - .value = "off",\ - },{\ - .driver = "usb-mouse",\ - .property = "usb_version",\ - .value = stringify(1),\ - },{\ - .driver = "usb-kbd",\ - .property = "usb_version",\ - .value = stringify(1),\ - },{\ - .driver = "virtio-pci",\ - .property = "virtio-pci-bus-master-bug-migration",\ - .value = "on",\ - }, + {\ + .driver = "intel-hda",\ + .property = "old_msi_addr",\ + .value = "on",\ + },{\ + .driver = "VGA",\ + .property = "qemu-extended-regs",\ + .value = "off",\ + },{\ + .driver = "secondary-vga",\ + .property = "qemu-extended-regs",\ + .value = "off",\ + },{\ + .driver = "virtio-scsi-pci",\ + .property = "any_layout",\ + .value = "off",\ + },{\ + .driver = "usb-mouse",\ + .property = "usb_version",\ + .value = stringify(1),\ + },{\ + .driver = "usb-kbd",\ + .property = "usb_version",\ + .value = stringify(1),\ + },{\ + .driver = "virtio-pci",\ + .property = "virtio-pci-bus-master-bug-migration",\ + .value = "on",\ + }, #endif /* HW_COMPAT_H */ diff --git a/qemu/include/hw/cpu/icc_bus.h b/qemu/include/hw/cpu/icc_bus.h deleted file mode 100644 index 98a979fa1..000000000 --- a/qemu/include/hw/cpu/icc_bus.h +++ /dev/null @@ -1,82 +0,0 @@ -/* icc_bus.h - * emulate x86 ICC (Interrupt Controller Communications) bus - * - * Copyright (c) 2013 Red Hat, Inc - * - * Authors: - * Igor Mammedov - * - * 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 - */ -#ifndef ICC_BUS_H -#define ICC_BUS_H - -#include "exec/memory.h" -#include "hw/qdev-core.h" - -#define TYPE_ICC_BUS "icc-bus" - -#ifndef CONFIG_USER_ONLY - -/** - * ICCBus: - * - * ICC bus - */ -typedef struct ICCBus { - /*< private >*/ - BusState parent_obj; - /*< public >*/ - - MemoryRegion *apic_address_space; -} ICCBus; - -#define ICC_BUS(obj) OBJECT_CHECK(ICCBus, (obj), TYPE_ICC_BUS) - -/** - * ICCDevice: - * - * ICC device - */ -typedef struct ICCDevice { - /*< private >*/ - DeviceState qdev; - /*< public >*/ -} ICCDevice; - -/** - * ICCDeviceClass: - * @init: Initialization callback for derived classes. - * - * ICC device class - */ -typedef struct ICCDeviceClass { - /*< private >*/ - DeviceClass parent_class; - /*< public >*/ - - DeviceRealize realize; -} ICCDeviceClass; - -#define TYPE_ICC_DEVICE "icc-device" -#define ICC_DEVICE(obj) OBJECT_CHECK(ICCDevice, (obj), TYPE_ICC_DEVICE) -#define ICC_DEVICE_CLASS(klass) \ - OBJECT_CLASS_CHECK(ICCDeviceClass, (klass), TYPE_ICC_DEVICE) -#define ICC_DEVICE_GET_CLASS(obj) \ - OBJECT_GET_CLASS(ICCDeviceClass, (obj), TYPE_ICC_DEVICE) - -#define TYPE_ICC_BRIDGE "icc-bridge" - -#endif /* CONFIG_USER_ONLY */ -#endif diff --git a/qemu/include/hw/display/bcm2835_fb.h b/qemu/include/hw/display/bcm2835_fb.h new file mode 100644 index 000000000..9a12d7afa --- /dev/null +++ b/qemu/include/hw/display/bcm2835_fb.h @@ -0,0 +1,47 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous + * + * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2835_FB_H +#define BCM2835_FB_H + +#include "hw/sysbus.h" +#include "exec/address-spaces.h" +#include "ui/console.h" + +#define TYPE_BCM2835_FB "bcm2835-fb" +#define BCM2835_FB(obj) OBJECT_CHECK(BCM2835FBState, (obj), TYPE_BCM2835_FB) + +typedef struct { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + uint32_t vcram_base, vcram_size; + MemoryRegion *dma_mr; + AddressSpace dma_as; + MemoryRegion iomem; + MemoryRegionSection fbsection; + QemuConsole *con; + qemu_irq mbox_irq; + + bool lock, invalidate, pending; + uint32_t xres, yres; + uint32_t xres_virtual, yres_virtual; + uint32_t xoffset, yoffset; + uint32_t bpp; + uint32_t base, pitch, size; + uint32_t pixo, alpha; +} BCM2835FBState; + +void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres, + uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp, + uint32_t *pixo, uint32_t *alpha); + +#endif diff --git a/qemu/include/hw/dma/bcm2835_dma.h b/qemu/include/hw/dma/bcm2835_dma.h new file mode 100644 index 000000000..75312e2e1 --- /dev/null +++ b/qemu/include/hw/dma/bcm2835_dma.h @@ -0,0 +1,47 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2835_DMA_H +#define BCM2835_DMA_H + +#include "qemu-common.h" +#include "exec/address-spaces.h" +#include "hw/sysbus.h" + +typedef struct { + uint32_t cs; + uint32_t conblk_ad; + uint32_t ti; + uint32_t source_ad; + uint32_t dest_ad; + uint32_t txfr_len; + uint32_t stride; + uint32_t nextconbk; + uint32_t debug; + + qemu_irq irq; +} BCM2835DMAChan; + +#define TYPE_BCM2835_DMA "bcm2835-dma" +#define BCM2835_DMA(obj) \ + OBJECT_CHECK(BCM2835DMAState, (obj), TYPE_BCM2835_DMA) + +#define BCM2835_DMA_NCHANS 16 + +typedef struct { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + MemoryRegion iomem0, iomem15; + MemoryRegion *dma_mr; + AddressSpace dma_as; + + BCM2835DMAChan chan[BCM2835_DMA_NCHANS]; + uint32_t int_status; + uint32_t enable; +} BCM2835DMAState; + +#endif diff --git a/qemu/include/hw/elf_ops.h b/qemu/include/hw/elf_ops.h index bd7196814..f510e7ec2 100644 --- a/qemu/include/hw/elf_ops.h +++ b/qemu/include/hw/elf_ops.h @@ -263,7 +263,7 @@ static int glue(load_elf, SZ)(const char *name, int fd, void *translate_opaque, int must_swab, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, - int elf_machine, int clear_lsb) + int elf_machine, int clear_lsb, int data_swab) { struct elfhdr ehdr; struct elf_phdr *phdr = NULL, *ph; @@ -282,25 +282,36 @@ static int glue(load_elf, SZ)(const char *name, int fd, switch (elf_machine) { case EM_PPC64: - if (EM_PPC64 != ehdr.e_machine) - if (EM_PPC != ehdr.e_machine) { + if (ehdr.e_machine != EM_PPC64) { + if (ehdr.e_machine != EM_PPC) { ret = ELF_LOAD_WRONG_ARCH; goto fail; } + } break; case EM_X86_64: - if (EM_X86_64 != ehdr.e_machine) - if (EM_386 != ehdr.e_machine) { + if (ehdr.e_machine != EM_X86_64) { + if (ehdr.e_machine != EM_386) { ret = ELF_LOAD_WRONG_ARCH; goto fail; } + } break; case EM_MICROBLAZE: - if (EM_MICROBLAZE != ehdr.e_machine) - if (EM_MICROBLAZE_OLD != ehdr.e_machine) { + if (ehdr.e_machine != EM_MICROBLAZE) { + if (ehdr.e_machine != EM_MICROBLAZE_OLD) { ret = ELF_LOAD_WRONG_ARCH; goto fail; } + } + break; + case EM_MOXIE: + if (ehdr.e_machine != EM_MOXIE) { + if (ehdr.e_machine != EM_MOXIE_OLD) { + ret = ELF_LOAD_WRONG_ARCH; + goto fail; + } + } break; default: if (elf_machine != ehdr.e_machine) { @@ -355,6 +366,26 @@ static int glue(load_elf, SZ)(const char *name, int fd, addr = ph->p_paddr; } + if (data_swab) { + int j; + for (j = 0; j < file_size; j += (1 << data_swab)) { + uint8_t *dp = data + j; + switch (data_swab) { + case (1): + *(uint16_t *)dp = bswap16(*(uint16_t *)dp); + break; + case (2): + *(uint32_t *)dp = bswap32(*(uint32_t *)dp); + break; + case (3): + *(uint64_t *)dp = bswap64(*(uint64_t *)dp); + break; + default: + g_assert_not_reached(); + } + } + } + /* the entry pointer in the ELF header is a virtual * address, if the text segments paddr and vaddr differ * we need to adjust the entry */ diff --git a/qemu/include/hw/gpio/imx_gpio.h b/qemu/include/hw/gpio/imx_gpio.h new file mode 100644 index 000000000..b15a09fbc --- /dev/null +++ b/qemu/include/hw/gpio/imx_gpio.h @@ -0,0 +1,63 @@ +/* + * i.MX processors GPIO registers definition. + * + * Copyright (C) 2015 Jean-Christophe Dubois + * + * 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 . + */ + +#ifndef __IMX_GPIO_H_ +#define __IMX_GPIO_H_ + +#include + +#define TYPE_IMX_GPIO "imx.gpio" +#define IMX_GPIO(obj) OBJECT_CHECK(IMXGPIOState, (obj), TYPE_IMX_GPIO) + +#define IMX_GPIO_MEM_SIZE 0x20 + +/* i.MX GPIO memory map */ +#define DR_ADDR 0x00 /* DATA REGISTER */ +#define GDIR_ADDR 0x04 /* DIRECTION REGISTER */ +#define PSR_ADDR 0x08 /* PAD STATUS REGISTER */ +#define ICR1_ADDR 0x0c /* INTERRUPT CONFIGURATION REGISTER 1 */ +#define ICR2_ADDR 0x10 /* INTERRUPT CONFIGURATION REGISTER 2 */ +#define IMR_ADDR 0x14 /* INTERRUPT MASK REGISTER */ +#define ISR_ADDR 0x18 /* INTERRUPT STATUS REGISTER */ +#define EDGE_SEL_ADDR 0x1c /* EDGE SEL REGISTER */ + +#define IMX_GPIO_PIN_COUNT 32 + +typedef struct IMXGPIOState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + + uint32_t dr; + uint32_t gdir; + uint32_t psr; + uint64_t icr; + uint32_t imr; + uint32_t isr; + bool has_edge_sel; + uint32_t edge_sel; + bool has_upper_pin_irq; + + qemu_irq irq[2]; + qemu_irq output[IMX_GPIO_PIN_COUNT]; +} IMXGPIOState; + +#endif /* __IMX_GPIO_H_ */ diff --git a/qemu/include/hw/hotplug.h b/qemu/include/hw/hotplug.h index 2db025d0b..da1d0e4ab 100644 --- a/qemu/include/hw/hotplug.h +++ b/qemu/include/hw/hotplug.h @@ -13,7 +13,6 @@ #define HOTPLUG_H #include "qom/object.h" -#include "qemu/typedefs.h" #define TYPE_HOTPLUG_HANDLER "hotplug-handler" diff --git a/qemu/include/hw/hw.h b/qemu/include/hw/hw.h index c78adae06..2cb69d5f5 100644 --- a/qemu/include/hw/hw.h +++ b/qemu/include/hw/hw.h @@ -2,7 +2,6 @@ #ifndef QEMU_HW_H #define QEMU_HW_H -#include "qemu-common.h" #if !defined(CONFIG_USER_ONLY) && !defined(NEED_CPU_H) #include "exec/cpu-common.h" @@ -13,6 +12,7 @@ #include "block/aio.h" #include "migration/vmstate.h" #include "qemu/log.h" +#include "qemu/module.h" #ifdef NEED_CPU_H #if TARGET_LONG_BITS == 64 @@ -41,6 +41,8 @@ typedef void QEMUResetHandler(void *opaque); void qemu_register_reset(QEMUResetHandler *func, void *opaque); void qemu_unregister_reset(QEMUResetHandler *func, void *opaque); +void QEMU_NORETURN hw_error(const char *fmt, ...) GCC_FMT_ATTR(1, 2); + #ifdef NEED_CPU_H #if TARGET_LONG_BITS == 64 #define VMSTATE_UINTTL_V(_f, _s, _v) \ @@ -49,6 +51,7 @@ void qemu_unregister_reset(QEMUResetHandler *func, void *opaque); VMSTATE_UINT64_EQUAL_V(_f, _s, _v) #define VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, _v) \ VMSTATE_UINT64_ARRAY_V(_f, _s, _n, _v) +#define vmstate_info_uinttl vmstate_info_uint64 #else #define VMSTATE_UINTTL_V(_f, _s, _v) \ VMSTATE_UINT32_V(_f, _s, _v) @@ -56,6 +59,7 @@ void qemu_unregister_reset(QEMUResetHandler *func, void *opaque); VMSTATE_UINT32_EQUAL_V(_f, _s, _v) #define VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, _v) \ VMSTATE_UINT32_ARRAY_V(_f, _s, _n, _v) +#define vmstate_info_uinttl vmstate_info_uint32 #endif #define VMSTATE_UINTTL(_f, _s) \ VMSTATE_UINTTL_V(_f, _s, 0) diff --git a/qemu/include/hw/i2c/imx_i2c.h b/qemu/include/hw/i2c/imx_i2c.h new file mode 100644 index 000000000..e2ee8eaee --- /dev/null +++ b/qemu/include/hw/i2c/imx_i2c.h @@ -0,0 +1,87 @@ +/* + * i.MX I2C Bus Serial Interface registers definition + * + * Copyright (C) 2013 Jean-Christophe Dubois. + * + * 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 . + * + */ + +#ifndef __IMX_I2C_H_ +#define __IMX_I2C_H_ + +#include + +#define TYPE_IMX_I2C "imx.i2c" +#define IMX_I2C(obj) OBJECT_CHECK(IMXI2CState, (obj), TYPE_IMX_I2C) + +#define IMX_I2C_MEM_SIZE 0x14 + +/* i.MX I2C memory map */ +#define IADR_ADDR 0x00 /* address register */ +#define IFDR_ADDR 0x04 /* frequency divider register */ +#define I2CR_ADDR 0x08 /* control register */ +#define I2SR_ADDR 0x0c /* status register */ +#define I2DR_ADDR 0x10 /* data register */ + +#define IADR_MASK 0xFE +#define IADR_RESET 0 + +#define IFDR_MASK 0x3F +#define IFDR_RESET 0 + +#define I2CR_IEN (1 << 7) +#define I2CR_IIEN (1 << 6) +#define I2CR_MSTA (1 << 5) +#define I2CR_MTX (1 << 4) +#define I2CR_TXAK (1 << 3) +#define I2CR_RSTA (1 << 2) +#define I2CR_MASK 0xFC +#define I2CR_RESET 0 + +#define I2SR_ICF (1 << 7) +#define I2SR_IAAF (1 << 6) +#define I2SR_IBB (1 << 5) +#define I2SR_IAL (1 << 4) +#define I2SR_SRW (1 << 2) +#define I2SR_IIF (1 << 1) +#define I2SR_RXAK (1 << 0) +#define I2SR_MASK 0xE9 +#define I2SR_RESET 0x81 + +#define I2DR_MASK 0xFF +#define I2DR_RESET 0 + +#define ADDR_RESET 0xFF00 + +typedef struct IMXI2CState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + I2CBus *bus; + qemu_irq irq; + + uint16_t address; + + uint16_t iadr; + uint16_t ifdr; + uint16_t i2cr; + uint16_t i2sr; + uint16_t i2dr_read; + uint16_t i2dr_write; +} IMXI2CState; + +#endif /* __IMX_I2C_H_ */ diff --git a/qemu/include/hw/i386/apic_internal.h b/qemu/include/hw/i386/apic_internal.h index dc7a89d98..74fe935e8 100644 --- a/qemu/include/hw/i386/apic_internal.h +++ b/qemu/include/hw/i386/apic_internal.h @@ -20,8 +20,8 @@ #ifndef QEMU_APIC_INTERNAL_H #define QEMU_APIC_INTERNAL_H +#include "cpu.h" #include "exec/memory.h" -#include "hw/cpu/icc_bus.h" #include "qemu/timer.h" /* APIC Local Vector Table */ @@ -49,17 +49,73 @@ #define APIC_TRIGGER_EDGE 0 #define APIC_TRIGGER_LEVEL 1 -#define APIC_LVT_TIMER_PERIODIC (1<<17) -#define APIC_LVT_MASKED (1<<16) -#define APIC_LVT_LEVEL_TRIGGER (1<<15) -#define APIC_LVT_REMOTE_IRR (1<<14) -#define APIC_INPUT_POLARITY (1<<13) -#define APIC_SEND_PENDING (1<<12) - -#define ESR_ILLEGAL_ADDRESS (1 << 7) - -#define APIC_SV_DIRECTED_IO (1<<12) -#define APIC_SV_ENABLE (1<<8) +#define APIC_VECTOR_MASK 0xff +#define APIC_DCR_MASK 0xf + +#define APIC_LVT_TIMER_SHIFT 17 +#define APIC_LVT_MASKED_SHIFT 16 +#define APIC_LVT_LEVEL_TRIGGER_SHIFT 15 +#define APIC_LVT_REMOTE_IRR_SHIFT 14 +#define APIC_LVT_INT_POLARITY_SHIFT 13 +#define APIC_LVT_DELIV_STS_SHIFT 12 +#define APIC_LVT_DELIV_MOD_SHIFT 8 + +#define APIC_LVT_TIMER_TSCDEADLINE (2 << APIC_LVT_TIMER_SHIFT) +#define APIC_LVT_TIMER_PERIODIC (1 << APIC_LVT_TIMER_SHIFT) +#define APIC_LVT_MASKED (1 << APIC_LVT_MASKED_SHIFT) +#define APIC_LVT_LEVEL_TRIGGER (1 << APIC_LVT_LEVEL_TRIGGER_SHIFT) +#define APIC_LVT_REMOTE_IRR (1 << APIC_LVT_REMOTE_IRR_SHIFT) +#define APIC_LVT_INT_POLARITY (1 << APIC_LVT_INT_POLARITY_SHIFT) +#define APIC_LVT_DELIV_STS (1 << APIC_LVT_DELIV_STS_SHIFT) +#define APIC_LVT_DELIV_MOD (7 << APIC_LVT_DELIV_MOD_SHIFT) + +#define APIC_ESR_ILL_ADDRESS_SHIFT 7 +#define APIC_ESR_RECV_ILL_VECT_SHIFT 6 +#define APIC_ESR_SEND_ILL_VECT_SHIFT 5 +#define APIC_ESR_RECV_ACCEPT_SHIFT 3 +#define APIC_ESR_SEND_ACCEPT_SHIFT 2 +#define APIC_ESR_RECV_CHECK_SUM_SHIFT 1 + +#define APIC_ESR_ILLEGAL_ADDRESS (1 << APIC_ESR_ILL_ADDRESS_SHIFT) +#define APIC_ESR_RECV_ILLEGAL_VECT (1 << APIC_ESR_RECV_ILL_VECT_SHIFT) +#define APIC_ESR_SEND_ILLEGAL_VECT (1 << APIC_ESR_SEND_ILL_VECT_SHIFT) +#define APIC_ESR_RECV_ACCEPT (1 << APIC_ESR_RECV_ACCEPT_SHIFT) +#define APIC_ESR_SEND_ACCEPT (1 << APIC_ESR_SEND_ACCEPT_SHIFT) +#define APIC_ESR_RECV_CHECK_SUM (1 << APIC_ESR_RECV_CHECK_SUM_SHIFT) +#define APIC_ESR_SEND_CHECK_SUM 1 + +#define APIC_ICR_DEST_SHIFT 24 +#define APIC_ICR_DEST_SHORT_SHIFT 18 +#define APIC_ICR_TRIGGER_MOD_SHIFT 15 +#define APIC_ICR_LEVEL_SHIFT 14 +#define APIC_ICR_DELIV_STS_SHIFT 12 +#define APIC_ICR_DEST_MOD_SHIFT 11 +#define APIC_ICR_DELIV_MOD_SHIFT 8 + +#define APIC_ICR_DEST_SHORT (3 << APIC_ICR_DEST_SHORT_SHIFT) +#define APIC_ICR_TRIGGER_MOD (1 << APIC_ICR_TRIGGER_MOD_SHIFT) +#define APIC_ICR_LEVEL (1 << APIC_ICR_LEVEL_SHIFT) +#define APIC_ICR_DELIV_STS (1 << APIC_ICR_DELIV_STS_SHIFT) +#define APIC_ICR_DEST_MOD (1 << APIC_ICR_DEST_MOD_SHIFT) +#define APIC_ICR_DELIV_MOD (7 << APIC_ICR_DELIV_MOD_SHIFT) + +#define APIC_PR_CLASS_SHIFT 4 +#define APIC_PR_SUB_CLASS 0xf + +#define APIC_LOGDEST_XAPIC_SHIFT 4 +#define APIC_LOGDEST_XAPIC_ID 0xf + +#define APIC_LOGDEST_X2APIC_SHIFT 16 +#define APIC_LOGDEST_X2APIC_ID 0xffff + +#define APIC_SPURIO_FOCUS_SHIFT 9 +#define APIC_SPURIO_ENABLED_SHIFT 8 + +#define APIC_SPURIO_FOCUS (1 << APIC_SPURIO_FOCUS_SHIFT) +#define APIC_SPURIO_ENABLED (1 << APIC_SPURIO_ENABLED_SHIFT) + +#define APIC_SV_DIRECTED_IO (1 << 12) +#define APIC_SV_ENABLE (1 << 8) #define VAPIC_ENABLE_BIT 0 #define VAPIC_ENABLE_MASK (1 << VAPIC_ENABLE_BIT) @@ -78,7 +134,7 @@ typedef struct APICCommonState APICCommonState; typedef struct APICCommonClass { - ICCDeviceClass parent_class; + DeviceClass parent_class; DeviceRealize realize; void (*set_base)(APICCommonState *s, uint64_t val); @@ -93,7 +149,9 @@ typedef struct APICCommonClass } APICCommonClass; struct APICCommonState { - ICCDevice busdev; + /*< private >*/ + DeviceState parent_obj; + /*< public >*/ MemoryRegion io_memory; X86CPU *cpu; @@ -146,4 +204,22 @@ void apic_enable_vapic(DeviceState *d, hwaddr paddr); void vapic_report_tpr_access(DeviceState *dev, CPUState *cpu, target_ulong ip, TPRAccess access); +int apic_get_ppr(APICCommonState *s); + +static inline void apic_set_bit(uint32_t *tab, int index) +{ + int i, mask; + i = index >> 5; + mask = 1 << (index & 0x1f); + tab[i] |= mask; +} + +static inline int apic_get_bit(uint32_t *tab, int index) +{ + int i, mask; + i = index >> 5; + mask = 1 << (index & 0x1f); + return !!(tab[i] & mask); +} + #endif /* !QEMU_APIC_INTERNAL_H */ diff --git a/qemu/include/hw/i386/ich9.h b/qemu/include/hw/i386/ich9.h index b9d2b04b6..d04dcdcfb 100644 --- a/qemu/include/hw/i386/ich9.h +++ b/qemu/include/hw/i386/ich9.h @@ -17,13 +17,13 @@ void ich9_lpc_set_irq(void *opaque, int irq_num, int level); int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx); PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin); -void ich9_lpc_pm_init(PCIDevice *pci_lpc, bool smm_enabled, bool enable_tco); +void ich9_lpc_pm_init(PCIDevice *pci_lpc, bool smm_enabled); I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base); void ich9_generate_smi(void); void ich9_generate_nmi(void); -#define ICH9_CC_SIZE (16 * 1024) /* 16KB */ +#define ICH9_CC_SIZE (16 * 1024) /* 16KB. Chipset configuration registers */ #define TYPE_ICH9_LPC_DEVICE "ICH9-LPC" #define ICH9_LPC_DEVICE(obj) \ @@ -65,7 +65,7 @@ typedef struct ICH9LPCState { /* isa bus */ ISABus *isa_bus; - MemoryRegion rbca_mem; + MemoryRegion rcrb_mem; /* root complex register block */ Notifier machine_ready; qemu_irq *pic; diff --git a/qemu/include/hw/i386/intel_iommu.h b/qemu/include/hw/i386/intel_iommu.h index e321ee4fb..b024ffa72 100644 --- a/qemu/include/hw/i386/intel_iommu.h +++ b/qemu/include/hw/i386/intel_iommu.h @@ -49,6 +49,7 @@ typedef struct VTDContextCacheEntry VTDContextCacheEntry; typedef struct IntelIOMMUState IntelIOMMUState; typedef struct VTDAddressSpace VTDAddressSpace; typedef struct VTDIOTLBEntry VTDIOTLBEntry; +typedef struct VTDBus VTDBus; /* Context-Entry */ struct VTDContextEntry { @@ -65,7 +66,7 @@ struct VTDContextCacheEntry { }; struct VTDAddressSpace { - uint8_t bus_num; + PCIBus *bus; uint8_t devfn; AddressSpace as; MemoryRegion iommu; @@ -73,10 +74,16 @@ struct VTDAddressSpace { VTDContextCacheEntry context_cache_entry; }; +struct VTDBus { + PCIBus* bus; /* A reference to the bus to provide translation for */ + VTDAddressSpace *dev_as[0]; /* A table of VTDAddressSpace objects indexed by devfn */ +}; + struct VTDIOTLBEntry { uint64_t gfn; uint16_t domain_id; uint64_t slpte; + uint64_t mask; bool read_flags; bool write_flags; }; @@ -114,7 +121,13 @@ struct IntelIOMMUState { GHashTable *iotlb; /* IOTLB */ MemoryRegionIOMMUOps iommu_ops; - VTDAddressSpace **address_spaces[VTD_PCI_BUS_MAX]; + GHashTable *vtd_as_by_busptr; /* VTDBus objects indexed by PCIBus* reference */ + VTDBus *vtd_as_by_bus_num[VTD_PCI_BUS_MAX]; /* VTDBus objects indexed by bus number */ }; +/* Find the VTD Address space associated with the given bus pointer, + * create a new one if none exists + */ +VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn); + #endif diff --git a/qemu/include/hw/i386/ioapic_internal.h b/qemu/include/hw/i386/ioapic_internal.h index 3be335218..797ed4730 100644 --- a/qemu/include/hw/i386/ioapic_internal.h +++ b/qemu/include/hw/i386/ioapic_internal.h @@ -40,7 +40,12 @@ #define IOAPIC_LVT_DELIV_MODE_SHIFT 8 #define IOAPIC_LVT_MASKED (1 << IOAPIC_LVT_MASKED_SHIFT) +#define IOAPIC_LVT_TRIGGER_MODE (1 << IOAPIC_LVT_TRIGGER_MODE_SHIFT) #define IOAPIC_LVT_REMOTE_IRR (1 << IOAPIC_LVT_REMOTE_IRR_SHIFT) +#define IOAPIC_LVT_POLARITY (1 << IOAPIC_LVT_POLARITY_SHIFT) +#define IOAPIC_LVT_DELIV_STATUS (1 << IOAPIC_LVT_DELIV_STATUS_SHIFT) +#define IOAPIC_LVT_DEST_MODE (1 << IOAPIC_LVT_DEST_MODE_SHIFT) +#define IOAPIC_LVT_DELIV_MODE (7 << IOAPIC_LVT_DELIV_MODE_SHIFT) #define IOAPIC_TRIGGER_EDGE 0 #define IOAPIC_TRIGGER_LEVEL 1 @@ -100,4 +105,6 @@ struct IOAPICCommonState { void ioapic_reset_common(DeviceState *dev); +void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s); + #endif /* !QEMU_IOAPIC_INTERNAL_H */ diff --git a/qemu/include/hw/i386/pc.h b/qemu/include/hw/i386/pc.h index 954203d22..96f0b66c7 100644 --- a/qemu/include/hw/i386/pc.h +++ b/qemu/include/hw/i386/pc.h @@ -16,29 +16,62 @@ #include "hw/boards.h" #include "hw/compat.h" #include "hw/mem/pc-dimm.h" +#include "hw/mem/nvdimm.h" #define HPET_INTCAP "hpet-intcap" +#ifdef CONFIG_KVM +#define kvm_pit_in_kernel() \ + (kvm_irqchip_in_kernel() && !kvm_irqchip_is_split()) +#define kvm_pic_in_kernel() \ + (kvm_irqchip_in_kernel() && !kvm_irqchip_is_split()) +#define kvm_ioapic_in_kernel() \ + (kvm_irqchip_in_kernel() && !kvm_irqchip_is_split()) +#else +#define kvm_pit_in_kernel() 0 +#define kvm_pic_in_kernel() 0 +#define kvm_ioapic_in_kernel() 0 +#endif + /** * PCMachineState: * @acpi_dev: link to ACPI PM device that performs ACPI hotplug handling - * @enforce_aligned_dimm: check that DIMM's address/size is aligned by - * backend's alignment value if provided */ struct PCMachineState { /*< private >*/ MachineState parent_obj; /* */ + + /* State for other subsystems/APIs: */ MemoryHotplugState hotplug_memory; + Notifier machine_done; + /* Pointers to devices and objects: */ HotplugHandler *acpi_dev; ISADevice *rtc; + PCIBus *bus; + FWCfgState *fw_cfg; + /* Configuration options: */ uint64_t max_ram_below_4g; OnOffAuto vmport; OnOffAuto smm; - bool enforce_aligned_dimm; + + AcpiNVDIMMState acpi_nvdimm_state; + + /* RAM information (sizes, addresses, configuration): */ + ram_addr_t below_4g_mem_size, above_4g_mem_size; + + /* CPU and apic information: */ + bool apic_xrupt_override; + unsigned apic_id_limit; + CPUArchIdList *possible_cpus; + + /* NUMA information: */ + uint64_t numa_nodes; + uint64_t *node_mem; + uint64_t *node_cpu; }; #define PC_MACHINE_ACPI_DEVICE_PROP "acpi-device" @@ -46,23 +79,64 @@ struct PCMachineState { #define PC_MACHINE_MAX_RAM_BELOW_4G "max-ram-below-4g" #define PC_MACHINE_VMPORT "vmport" #define PC_MACHINE_SMM "smm" -#define PC_MACHINE_ENFORCE_ALIGNED_DIMM "enforce-aligned-dimm" +#define PC_MACHINE_NVDIMM "nvdimm" /** * PCMachineClass: + * + * Methods: + * * @get_hotplug_handler: pointer to parent class callback @get_hotplug_handler + * + * Compat fields: + * + * @enforce_aligned_dimm: check that DIMM's address/size is aligned by + * backend's alignment value if provided + * @acpi_data_size: Size of the chunk of memory at the top of RAM + * for the BIOS ACPI tables and other BIOS + * datastructures. + * @gigabyte_align: Make sure that guest addresses aligned at + * 1Gbyte boundaries get mapped to host + * addresses aligned at 1Gbyte boundaries. This + * way we can use 1GByte pages in the host. + * */ struct PCMachineClass { /*< private >*/ MachineClass parent_class; /*< public >*/ + + /* Methods: */ HotplugHandler *(*get_hotplug_handler)(MachineState *machine, DeviceState *dev); -}; -typedef struct PCMachineState PCMachineState; -typedef struct PCMachineClass PCMachineClass; + /* Device configuration: */ + bool pci_enabled; + bool kvmclock_enabled; + + /* Compat options: */ + + /* ACPI compat: */ + bool has_acpi_build; + bool rsdp_in_ram; + int legacy_acpi_table_size; + unsigned acpi_data_size; + + /* SMBIOS compat: */ + bool smbios_defaults; + bool smbios_legacy_mode; + bool smbios_uuid_encoded; + + /* RAM / address space compat: */ + bool gigabyte_align; + bool has_reserved_memory; + bool enforce_aligned_dimm; + bool broken_reserved_end; + + /* TSC rate migration: */ + bool save_tsc_khz; +}; #define TYPE_PC_MACHINE "generic-pc-machine" #define PC_MACHINE(obj) \ @@ -90,21 +164,6 @@ typedef struct PcPciInfo { #define ACPI_PM_PROP_GPE0_BLK_LEN "gpe0_blk_len" #define ACPI_PM_PROP_TCO_ENABLED "enable_tco" -struct PcGuestInfo { - bool isapc_ram_fw; - hwaddr ram_size, ram_size_below_4g; - unsigned apic_id_limit; - bool apic_xrupt_override; - uint64_t numa_nodes; - uint64_t *node_mem; - uint64_t *node_cpu; - FWCfgState *fw_cfg; - int legacy_acpi_table_size; - bool has_acpi_build; - bool has_reserved_memory; - bool rsdp_in_ram; -}; - /* parallel.c */ void parallel_hds_isa_init(ISABus *bus, int n); @@ -123,6 +182,11 @@ int pic_get_output(DeviceState *d); void hmp_info_pic(Monitor *mon, const QDict *qdict); void hmp_info_irq(Monitor *mon, const QDict *qdict); +/* ioapic.c */ + +void kvm_ioapic_dump_state(Monitor *mon, const QDict *qdict); +void ioapic_dump_state(Monitor *mon, const QDict *qdict); + /* Global System Interrupts */ #define GSI_NUM_PINS IOAPIC_NUM_PINS @@ -162,14 +226,11 @@ bool pc_machine_is_smm_enabled(PCMachineState *pcms); void pc_register_ferr_irq(qemu_irq irq); void pc_acpi_smi_interrupt(void *opaque, int irq, int level); -void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge); +void pc_cpus_init(PCMachineState *pcms); void pc_hot_add_cpu(const int64_t id, Error **errp); void pc_acpi_init(const char *default_dsdt); -PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size, - ram_addr_t above_4g_mem_size); - -void pc_set_legacy_acpi_data_size(void); +void pc_guest_info_init(PCMachineState *pcms); #define PCI_HOST_PROP_PCI_HOLE_START "pci-hole-start" #define PCI_HOST_PROP_PCI_HOLE_END "pci-hole-end" @@ -182,28 +243,20 @@ void pc_set_legacy_acpi_data_size(void); void pc_pci_as_mapping_init(Object *owner, MemoryRegion *system_memory, MemoryRegion *pci_address_space); -FWCfgState *xen_load_linux(const char *kernel_filename, - const char *kernel_cmdline, - const char *initrd_filename, - ram_addr_t below_4g_mem_size, - PcGuestInfo *guest_info); -FWCfgState *pc_memory_init(MachineState *machine, - MemoryRegion *system_memory, - ram_addr_t below_4g_mem_size, - ram_addr_t above_4g_mem_size, - MemoryRegion *rom_memory, - MemoryRegion **ram_memory, - PcGuestInfo *guest_info); +void xen_load_linux(PCMachineState *pcms); +void pc_memory_init(PCMachineState *pcms, + MemoryRegion *system_memory, + MemoryRegion *rom_memory, + MemoryRegion **ram_memory); qemu_irq pc_allocate_cpu_irq(void); DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus); void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, ISADevice **rtc_state, bool create_fdctrl, bool no_vmport, - uint32 hpet_irqs); + uint32_t hpet_irqs); void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd); -void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, - const char *boot_device, MachineState *machine, +void pc_cmos_init(PCMachineState *pcms, BusState *ide0, BusState *ide1, ISADevice *s); void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus); @@ -213,6 +266,11 @@ typedef void (*cpu_set_smm_t)(int smm, void *arg); void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name); +ISADevice *pc_find_fdc0(void); +int cmos_get_fd_drive_type(FloppyDriveType fd0); + +#define FW_CFG_IO_BASE 0x510 + /* acpi_piix.c */ I2CBus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, @@ -227,7 +285,13 @@ extern int no_hpet; struct PCII440FXState; typedef struct PCII440FXState PCII440FXState; -PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn, +#define TYPE_I440FX_PCI_HOST_BRIDGE "i440FX-pcihost" +#define TYPE_I440FX_PCI_DEVICE "i440FX" + +#define TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE "igd-passthrough-i440FX" + +PCIBus *i440fx_init(const char *host_type, const char *pci_type, + PCII440FXState **pi440fx_state, int *piix_devfn, ISABus **isa_bus, qemu_irq *pic, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, @@ -292,316 +356,489 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t); int e820_get_num_entries(void); bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); +#define PC_COMPAT_2_5 \ + HW_COMPAT_2_5 + +#define PC_COMPAT_2_4 \ + PC_COMPAT_2_5 \ + HW_COMPAT_2_4 \ + {\ + .driver = "Haswell-" TYPE_X86_CPU,\ + .property = "abm",\ + .value = "off",\ + },\ + {\ + .driver = "Haswell-noTSX-" TYPE_X86_CPU,\ + .property = "abm",\ + .value = "off",\ + },\ + {\ + .driver = "Broadwell-" TYPE_X86_CPU,\ + .property = "abm",\ + .value = "off",\ + },\ + {\ + .driver = "Broadwell-noTSX-" TYPE_X86_CPU,\ + .property = "abm",\ + .value = "off",\ + },\ + {\ + .driver = "host" "-" TYPE_X86_CPU,\ + .property = "host-cache-info",\ + .value = "on",\ + },\ + {\ + .driver = TYPE_X86_CPU,\ + .property = "check",\ + .value = "off",\ + },\ + {\ + .driver = "qemu64" "-" TYPE_X86_CPU,\ + .property = "sse4a",\ + .value = "on",\ + },\ + {\ + .driver = "qemu64" "-" TYPE_X86_CPU,\ + .property = "abm",\ + .value = "on",\ + },\ + {\ + .driver = "qemu64" "-" TYPE_X86_CPU,\ + .property = "popcnt",\ + .value = "on",\ + },\ + {\ + .driver = "qemu32" "-" TYPE_X86_CPU,\ + .property = "popcnt",\ + .value = "on",\ + },{\ + .driver = "Opteron_G2" "-" TYPE_X86_CPU,\ + .property = "rdtscp",\ + .value = "on",\ + },{\ + .driver = "Opteron_G3" "-" TYPE_X86_CPU,\ + .property = "rdtscp",\ + .value = "on",\ + },{\ + .driver = "Opteron_G4" "-" TYPE_X86_CPU,\ + .property = "rdtscp",\ + .value = "on",\ + },{\ + .driver = "Opteron_G5" "-" TYPE_X86_CPU,\ + .property = "rdtscp",\ + .value = "on",\ + }, + + #define PC_COMPAT_2_3 \ - HW_COMPAT_2_3 \ - {\ - .driver = TYPE_X86_CPU,\ - .property = "arat",\ - .value = "off",\ - },{\ - .driver = "qemu64" "-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(4),\ - },{\ - .driver = "kvm64" "-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(5),\ - },{\ - .driver = "pentium3" "-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(2),\ - },{\ - .driver = "n270" "-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(5),\ - },{\ - .driver = "Conroe" "-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(4),\ - },{\ - .driver = "Penryn" "-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(4),\ - },{\ - .driver = "Nehalem" "-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(4),\ - },{\ - .driver = "n270" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Penryn" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Conroe" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Nehalem" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Westmere" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "SandyBridge" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "IvyBridge" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Haswell" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Haswell-noTSX" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Broadwell" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Broadwell-noTSX" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - }, + PC_COMPAT_2_4 \ + HW_COMPAT_2_3 \ + {\ + .driver = TYPE_X86_CPU,\ + .property = "arat",\ + .value = "off",\ + },{\ + .driver = "qemu64" "-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(4),\ + },{\ + .driver = "kvm64" "-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(5),\ + },{\ + .driver = "pentium3" "-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(2),\ + },{\ + .driver = "n270" "-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(5),\ + },{\ + .driver = "Conroe" "-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(4),\ + },{\ + .driver = "Penryn" "-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(4),\ + },{\ + .driver = "Nehalem" "-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(4),\ + },{\ + .driver = "n270" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "Penryn" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "Conroe" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "Nehalem" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "Westmere" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "SandyBridge" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "IvyBridge" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "Haswell" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "Haswell-noTSX" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "Broadwell" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "Broadwell-noTSX" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + }, #define PC_COMPAT_2_2 \ - PC_COMPAT_2_3 \ - HW_COMPAT_2_2 + PC_COMPAT_2_3 \ + HW_COMPAT_2_2 \ + {\ + .driver = "kvm64" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "kvm32" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Conroe" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Penryn" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Nehalem" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Westmere" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "SandyBridge" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Haswell" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Broadwell" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Opteron_G1" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Opteron_G2" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Opteron_G3" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Opteron_G4" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Opteron_G5" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Haswell" "-" TYPE_X86_CPU,\ + .property = "f16c",\ + .value = "off",\ + },\ + {\ + .driver = "Haswell" "-" TYPE_X86_CPU,\ + .property = "rdrand",\ + .value = "off",\ + },\ + {\ + .driver = "Broadwell" "-" TYPE_X86_CPU,\ + .property = "f16c",\ + .value = "off",\ + },\ + {\ + .driver = "Broadwell" "-" TYPE_X86_CPU,\ + .property = "rdrand",\ + .value = "off",\ + }, #define PC_COMPAT_2_1 \ - PC_COMPAT_2_2 \ - HW_COMPAT_2_1 + PC_COMPAT_2_2 \ + HW_COMPAT_2_1 \ + {\ + .driver = "coreduo" "-" TYPE_X86_CPU,\ + .property = "vmx",\ + .value = "on",\ + },\ + {\ + .driver = "core2duo" "-" TYPE_X86_CPU,\ + .property = "vmx",\ + .value = "on",\ + }, #define PC_COMPAT_2_0 \ - PC_COMPAT_2_1 \ - {\ - .driver = "virtio-scsi-pci",\ - .property = "any_layout",\ - .value = "off",\ - },{\ - .driver = "PIIX4_PM",\ - .property = "memory-hotplug-support",\ - .value = "off",\ - },\ - {\ - .driver = "apic",\ - .property = "version",\ - .value = stringify(0x11),\ - },\ - {\ - .driver = "nec-usb-xhci",\ - .property = "superspeed-ports-first",\ - .value = "off",\ - },\ - {\ - .driver = "nec-usb-xhci",\ - .property = "force-pcie-endcap",\ - .value = "on",\ - },\ - {\ - .driver = "pci-serial",\ - .property = "prog_if",\ - .value = stringify(0),\ - },\ - {\ - .driver = "pci-serial-2x",\ - .property = "prog_if",\ - .value = stringify(0),\ - },\ - {\ - .driver = "pci-serial-4x",\ - .property = "prog_if",\ - .value = stringify(0),\ - },\ - {\ - .driver = "virtio-net-pci",\ - .property = "guest_announce",\ - .value = "off",\ - },\ - {\ - .driver = "ICH9-LPC",\ - .property = "memory-hotplug-support",\ - .value = "off",\ - },{\ - .driver = "xio3130-downstream",\ - .property = COMPAT_PROP_PCP,\ - .value = "off",\ - },{\ - .driver = "ioh3420",\ - .property = COMPAT_PROP_PCP,\ - .value = "off",\ - }, + PC_COMPAT_2_1 \ + {\ + .driver = "virtio-scsi-pci",\ + .property = "any_layout",\ + .value = "off",\ + },{\ + .driver = "PIIX4_PM",\ + .property = "memory-hotplug-support",\ + .value = "off",\ + },\ + {\ + .driver = "apic",\ + .property = "version",\ + .value = stringify(0x11),\ + },\ + {\ + .driver = "nec-usb-xhci",\ + .property = "superspeed-ports-first",\ + .value = "off",\ + },\ + {\ + .driver = "nec-usb-xhci",\ + .property = "force-pcie-endcap",\ + .value = "on",\ + },\ + {\ + .driver = "pci-serial",\ + .property = "prog_if",\ + .value = stringify(0),\ + },\ + {\ + .driver = "pci-serial-2x",\ + .property = "prog_if",\ + .value = stringify(0),\ + },\ + {\ + .driver = "pci-serial-4x",\ + .property = "prog_if",\ + .value = stringify(0),\ + },\ + {\ + .driver = "virtio-net-pci",\ + .property = "guest_announce",\ + .value = "off",\ + },\ + {\ + .driver = "ICH9-LPC",\ + .property = "memory-hotplug-support",\ + .value = "off",\ + },{\ + .driver = "xio3130-downstream",\ + .property = COMPAT_PROP_PCP,\ + .value = "off",\ + },{\ + .driver = "ioh3420",\ + .property = COMPAT_PROP_PCP,\ + .value = "off",\ + }, #define PC_COMPAT_1_7 \ - PC_COMPAT_2_0 \ - {\ - .driver = TYPE_USB_DEVICE,\ - .property = "msos-desc",\ - .value = "no",\ - },\ - {\ - .driver = "PIIX4_PM",\ - .property = "acpi-pci-hotplug-with-bridge-support",\ - .value = "off",\ - },\ - {\ - .driver = "hpet",\ - .property = HPET_INTCAP,\ - .value = stringify(4),\ - }, + PC_COMPAT_2_0 \ + {\ + .driver = TYPE_USB_DEVICE,\ + .property = "msos-desc",\ + .value = "no",\ + },\ + {\ + .driver = "PIIX4_PM",\ + .property = "acpi-pci-hotplug-with-bridge-support",\ + .value = "off",\ + },\ + {\ + .driver = "hpet",\ + .property = HPET_INTCAP,\ + .value = stringify(4),\ + }, #define PC_COMPAT_1_6 \ - PC_COMPAT_1_7 \ - {\ - .driver = "e1000",\ - .property = "mitigation",\ - .value = "off",\ - },{\ - .driver = "qemu64-" TYPE_X86_CPU,\ - .property = "model",\ - .value = stringify(2),\ - },{\ - .driver = "qemu32-" TYPE_X86_CPU,\ - .property = "model",\ - .value = stringify(3),\ - },{\ - .driver = "i440FX-pcihost",\ - .property = "short_root_bus",\ - .value = stringify(1),\ - },{\ - .driver = "q35-pcihost",\ - .property = "short_root_bus",\ - .value = stringify(1),\ - }, + PC_COMPAT_1_7 \ + {\ + .driver = "e1000",\ + .property = "mitigation",\ + .value = "off",\ + },{\ + .driver = "qemu64-" TYPE_X86_CPU,\ + .property = "model",\ + .value = stringify(2),\ + },{\ + .driver = "qemu32-" TYPE_X86_CPU,\ + .property = "model",\ + .value = stringify(3),\ + },{\ + .driver = "i440FX-pcihost",\ + .property = "short_root_bus",\ + .value = stringify(1),\ + },{\ + .driver = "q35-pcihost",\ + .property = "short_root_bus",\ + .value = stringify(1),\ + }, #define PC_COMPAT_1_5 \ - PC_COMPAT_1_6 \ - {\ - .driver = "Conroe-" TYPE_X86_CPU,\ - .property = "model",\ - .value = stringify(2),\ - },{\ - .driver = "Conroe-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(2),\ - },{\ - .driver = "Penryn-" TYPE_X86_CPU,\ - .property = "model",\ - .value = stringify(2),\ - },{\ - .driver = "Penryn-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(2),\ - },{\ - .driver = "Nehalem-" TYPE_X86_CPU,\ - .property = "model",\ - .value = stringify(2),\ - },{\ - .driver = "Nehalem-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(2),\ - },{\ - .driver = "virtio-net-pci",\ - .property = "any_layout",\ - .value = "off",\ - },{\ - .driver = TYPE_X86_CPU,\ - .property = "pmu",\ - .value = "on",\ - },{\ - .driver = "i440FX-pcihost",\ - .property = "short_root_bus",\ - .value = stringify(0),\ - },{\ - .driver = "q35-pcihost",\ - .property = "short_root_bus",\ - .value = stringify(0),\ - }, + PC_COMPAT_1_6 \ + {\ + .driver = "Conroe-" TYPE_X86_CPU,\ + .property = "model",\ + .value = stringify(2),\ + },{\ + .driver = "Conroe-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(2),\ + },{\ + .driver = "Penryn-" TYPE_X86_CPU,\ + .property = "model",\ + .value = stringify(2),\ + },{\ + .driver = "Penryn-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(2),\ + },{\ + .driver = "Nehalem-" TYPE_X86_CPU,\ + .property = "model",\ + .value = stringify(2),\ + },{\ + .driver = "Nehalem-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(2),\ + },{\ + .driver = "virtio-net-pci",\ + .property = "any_layout",\ + .value = "off",\ + },{\ + .driver = TYPE_X86_CPU,\ + .property = "pmu",\ + .value = "on",\ + },{\ + .driver = "i440FX-pcihost",\ + .property = "short_root_bus",\ + .value = stringify(0),\ + },{\ + .driver = "q35-pcihost",\ + .property = "short_root_bus",\ + .value = stringify(0),\ + }, #define PC_COMPAT_1_4 \ - PC_COMPAT_1_5 \ - {\ - .driver = "scsi-hd",\ - .property = "discard_granularity",\ - .value = stringify(0),\ - },{\ - .driver = "scsi-cd",\ - .property = "discard_granularity",\ - .value = stringify(0),\ - },{\ - .driver = "scsi-disk",\ - .property = "discard_granularity",\ - .value = stringify(0),\ - },{\ - .driver = "ide-hd",\ - .property = "discard_granularity",\ - .value = stringify(0),\ - },{\ - .driver = "ide-cd",\ - .property = "discard_granularity",\ - .value = stringify(0),\ - },{\ - .driver = "ide-drive",\ - .property = "discard_granularity",\ - .value = stringify(0),\ - },{\ - .driver = "virtio-blk-pci",\ - .property = "discard_granularity",\ - .value = stringify(0),\ - },{\ - .driver = "virtio-serial-pci",\ - .property = "vectors",\ - /* DEV_NVECTORS_UNSPECIFIED as a uint32_t string */\ - .value = stringify(0xFFFFFFFF),\ - },{ \ - .driver = "virtio-net-pci", \ - .property = "ctrl_guest_offloads", \ - .value = "off", \ - },{\ - .driver = "e1000",\ - .property = "romfile",\ - .value = "pxe-e1000.rom",\ - },{\ - .driver = "ne2k_pci",\ - .property = "romfile",\ - .value = "pxe-ne2k_pci.rom",\ - },{\ - .driver = "pcnet",\ - .property = "romfile",\ - .value = "pxe-pcnet.rom",\ - },{\ - .driver = "rtl8139",\ - .property = "romfile",\ - .value = "pxe-rtl8139.rom",\ - },{\ - .driver = "virtio-net-pci",\ - .property = "romfile",\ - .value = "pxe-virtio.rom",\ - },{\ - .driver = "486-" TYPE_X86_CPU,\ - .property = "model",\ - .value = stringify(0),\ - }, - -static inline void pc_common_machine_options(MachineClass *m) -{ - m->default_boot_order = "cad"; -} - -static inline void pc_default_machine_options(MachineClass *m) -{ - pc_common_machine_options(m); - m->hot_add_cpu = pc_hot_add_cpu; - m->max_cpus = 255; -} + PC_COMPAT_1_5 \ + {\ + .driver = "scsi-hd",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "scsi-cd",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "scsi-disk",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "ide-hd",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "ide-cd",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "ide-drive",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "virtio-blk-pci",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "virtio-serial-pci",\ + .property = "vectors",\ + /* DEV_NVECTORS_UNSPECIFIED as a uint32_t string */\ + .value = stringify(0xFFFFFFFF),\ + },{ \ + .driver = "virtio-net-pci", \ + .property = "ctrl_guest_offloads", \ + .value = "off", \ + },{\ + .driver = "e1000",\ + .property = "romfile",\ + .value = "pxe-e1000.rom",\ + },{\ + .driver = "ne2k_pci",\ + .property = "romfile",\ + .value = "pxe-ne2k_pci.rom",\ + },{\ + .driver = "pcnet",\ + .property = "romfile",\ + .value = "pxe-pcnet.rom",\ + },{\ + .driver = "rtl8139",\ + .property = "romfile",\ + .value = "pxe-rtl8139.rom",\ + },{\ + .driver = "virtio-net-pci",\ + .property = "romfile",\ + .value = "pxe-virtio.rom",\ + },{\ + .driver = "486-" TYPE_X86_CPU,\ + .property = "model",\ + .value = stringify(0),\ + },\ + {\ + .driver = "n270" "-" TYPE_X86_CPU,\ + .property = "movbe",\ + .value = "off",\ + },\ + {\ + .driver = "Westmere" "-" TYPE_X86_CPU,\ + .property = "pclmulqdq",\ + .value = "off",\ + }, #define DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn) \ static void pc_machine_##suffix##_class_init(ObjectClass *oc, void *data) \ @@ -620,14 +857,7 @@ static inline void pc_default_machine_options(MachineClass *m) { \ type_register(&pc_machine_type_##suffix); \ } \ - machine_init(pc_machine_init_##suffix) - -#define SET_MACHINE_COMPAT(m, COMPAT) do { \ - static GlobalProperty props[] = { \ - COMPAT \ - { /* end of list */ } \ - }; \ - (m)->compat_props = props; \ -} while (0) + type_init(pc_machine_init_##suffix) +extern void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id); #endif diff --git a/qemu/include/hw/i386/smbios.h b/qemu/include/hw/i386/smbios.h deleted file mode 100644 index d2850bed2..000000000 --- a/qemu/include/hw/i386/smbios.h +++ /dev/null @@ -1,227 +0,0 @@ -#ifndef QEMU_SMBIOS_H -#define QEMU_SMBIOS_H -/* - * SMBIOS Support - * - * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. - * - * Authors: - * Alex Williamson - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include "qemu/option.h" - -#define SMBIOS_MAX_TYPE 127 - -void smbios_entry_add(QemuOpts *opts); -void smbios_set_cpuid(uint32_t version, uint32_t features); -void smbios_set_defaults(const char *manufacturer, const char *product, - const char *version, bool legacy_mode, - bool uuid_encoded); -uint8_t *smbios_get_table_legacy(size_t *length); -void smbios_get_tables(uint8_t **tables, size_t *tables_len, - uint8_t **anchor, size_t *anchor_len); - -/* - * SMBIOS spec defined tables - */ - -/* SMBIOS entry point (anchor). - * BIOS must place this at a 16-bit-aligned address between 0xf0000 and 0xfffff. - */ -struct smbios_entry_point { - uint8_t anchor_string[4]; - uint8_t checksum; - uint8_t length; - uint8_t smbios_major_version; - uint8_t smbios_minor_version; - uint16_t max_structure_size; - uint8_t entry_point_revision; - uint8_t formatted_area[5]; - uint8_t intermediate_anchor_string[5]; - uint8_t intermediate_checksum; - uint16_t structure_table_length; - uint32_t structure_table_address; - uint16_t number_of_structures; - uint8_t smbios_bcd_revision; -} QEMU_PACKED; - -/* This goes at the beginning of every SMBIOS structure. */ -struct smbios_structure_header { - uint8_t type; - uint8_t length; - uint16_t handle; -} QEMU_PACKED; - -/* SMBIOS type 0 - BIOS Information */ -struct smbios_type_0 { - struct smbios_structure_header header; - uint8_t vendor_str; - uint8_t bios_version_str; - uint16_t bios_starting_address_segment; - uint8_t bios_release_date_str; - uint8_t bios_rom_size; - uint64_t bios_characteristics; - uint8_t bios_characteristics_extension_bytes[2]; - uint8_t system_bios_major_release; - uint8_t system_bios_minor_release; - uint8_t embedded_controller_major_release; - uint8_t embedded_controller_minor_release; -} QEMU_PACKED; - -/* UUID encoding. The time_* fields are little-endian, as specified by SMBIOS - * version 2.6. - */ -struct smbios_uuid { - uint32_t time_low; - uint16_t time_mid; - uint16_t time_hi_and_version; - uint8_t clock_seq_hi_and_reserved; - uint8_t clock_seq_low; - uint8_t node[6]; -} QEMU_PACKED; - -/* SMBIOS type 1 - System Information */ -struct smbios_type_1 { - struct smbios_structure_header header; - uint8_t manufacturer_str; - uint8_t product_name_str; - uint8_t version_str; - uint8_t serial_number_str; - struct smbios_uuid uuid; - uint8_t wake_up_type; - uint8_t sku_number_str; - uint8_t family_str; -} QEMU_PACKED; - -/* SMBIOS type 2 - Base Board */ -struct smbios_type_2 { - struct smbios_structure_header header; - uint8_t manufacturer_str; - uint8_t product_str; - uint8_t version_str; - uint8_t serial_number_str; - uint8_t asset_tag_number_str; - uint8_t feature_flags; - uint8_t location_str; - uint16_t chassis_handle; - uint8_t board_type; - uint8_t contained_element_count; - /* contained elements follow */ -} QEMU_PACKED; - -/* SMBIOS type 3 - System Enclosure (v2.7) */ -struct smbios_type_3 { - struct smbios_structure_header header; - uint8_t manufacturer_str; - uint8_t type; - uint8_t version_str; - uint8_t serial_number_str; - uint8_t asset_tag_number_str; - uint8_t boot_up_state; - uint8_t power_supply_state; - uint8_t thermal_state; - uint8_t security_status; - uint32_t oem_defined; - uint8_t height; - uint8_t number_of_power_cords; - uint8_t contained_element_count; - uint8_t sku_number_str; - /* contained elements follow */ -} QEMU_PACKED; - -/* SMBIOS type 4 - Processor Information (v2.6) */ -struct smbios_type_4 { - struct smbios_structure_header header; - uint8_t socket_designation_str; - uint8_t processor_type; - uint8_t processor_family; - uint8_t processor_manufacturer_str; - uint32_t processor_id[2]; - uint8_t processor_version_str; - uint8_t voltage; - uint16_t external_clock; - uint16_t max_speed; - uint16_t current_speed; - uint8_t status; - uint8_t processor_upgrade; - uint16_t l1_cache_handle; - uint16_t l2_cache_handle; - uint16_t l3_cache_handle; - uint8_t serial_number_str; - uint8_t asset_tag_number_str; - uint8_t part_number_str; - uint8_t core_count; - uint8_t core_enabled; - uint8_t thread_count; - uint16_t processor_characteristics; - uint16_t processor_family2; -} QEMU_PACKED; - -/* SMBIOS type 16 - Physical Memory Array (v2.7) */ -struct smbios_type_16 { - struct smbios_structure_header header; - uint8_t location; - uint8_t use; - uint8_t error_correction; - uint32_t maximum_capacity; - uint16_t memory_error_information_handle; - uint16_t number_of_memory_devices; - uint64_t extended_maximum_capacity; -} QEMU_PACKED; - -/* SMBIOS type 17 - Memory Device (v2.8) */ -struct smbios_type_17 { - struct smbios_structure_header header; - uint16_t physical_memory_array_handle; - uint16_t memory_error_information_handle; - uint16_t total_width; - uint16_t data_width; - uint16_t size; - uint8_t form_factor; - uint8_t device_set; - uint8_t device_locator_str; - uint8_t bank_locator_str; - uint8_t memory_type; - uint16_t type_detail; - uint16_t speed; - uint8_t manufacturer_str; - uint8_t serial_number_str; - uint8_t asset_tag_number_str; - uint8_t part_number_str; - uint8_t attributes; - uint32_t extended_size; - uint16_t configured_clock_speed; - uint16_t minimum_voltage; - uint16_t maximum_voltage; - uint16_t configured_voltage; -} QEMU_PACKED; - -/* SMBIOS type 19 - Memory Array Mapped Address (v2.7) */ -struct smbios_type_19 { - struct smbios_structure_header header; - uint32_t starting_address; - uint32_t ending_address; - uint16_t memory_array_handle; - uint8_t partition_width; - uint64_t extended_starting_address; - uint64_t extended_ending_address; -} QEMU_PACKED; - -/* SMBIOS type 32 - System Boot Information */ -struct smbios_type_32 { - struct smbios_structure_header header; - uint8_t reserved[6]; - uint8_t boot_status; -} QEMU_PACKED; - -/* SMBIOS type 127 -- End-of-table */ -struct smbios_type_127 { - struct smbios_structure_header header; -} QEMU_PACKED; - -#endif /*QEMU_SMBIOS_H */ diff --git a/qemu/include/hw/i386/topology.h b/qemu/include/hw/i386/topology.h index 9c6f3a937..fc9557239 100644 --- a/qemu/include/hw/i386/topology.h +++ b/qemu/include/hw/i386/topology.h @@ -38,8 +38,6 @@ * CPUID Fn8000_0008_ECX[ApicIdCoreIdSize[3:0]] is set to apicid_core_width(). */ -#include -#include #include "qemu/bitops.h" @@ -47,6 +45,12 @@ */ typedef uint32_t apic_id_t; +typedef struct X86CPUTopoInfo { + unsigned pkg_id; + unsigned core_id; + unsigned smt_id; +} X86CPUTopoInfo; + /* Return the bit width needed for 'count' IDs */ static unsigned apicid_bitwidth_for_count(unsigned count) @@ -92,13 +96,11 @@ static inline unsigned apicid_pkg_offset(unsigned nr_cores, unsigned nr_threads) */ static inline apic_id_t apicid_from_topo_ids(unsigned nr_cores, unsigned nr_threads, - unsigned pkg_id, - unsigned core_id, - unsigned smt_id) + const X86CPUTopoInfo *topo) { - return (pkg_id << apicid_pkg_offset(nr_cores, nr_threads)) | - (core_id << apicid_core_offset(nr_cores, nr_threads)) | - smt_id; + return (topo->pkg_id << apicid_pkg_offset(nr_cores, nr_threads)) | + (topo->core_id << apicid_core_offset(nr_cores, nr_threads)) | + topo->smt_id; } /* Calculate thread/core/package IDs for a specific topology, @@ -107,14 +109,12 @@ static inline apic_id_t apicid_from_topo_ids(unsigned nr_cores, static inline void x86_topo_ids_from_idx(unsigned nr_cores, unsigned nr_threads, unsigned cpu_index, - unsigned *pkg_id, - unsigned *core_id, - unsigned *smt_id) + X86CPUTopoInfo *topo) { unsigned core_index = cpu_index / nr_threads; - *smt_id = cpu_index % nr_threads; - *core_id = core_index % nr_cores; - *pkg_id = core_index / nr_cores; + topo->smt_id = cpu_index % nr_threads; + topo->core_id = core_index % nr_cores; + topo->pkg_id = core_index / nr_cores; } /* Make APIC ID for the CPU 'cpu_index' @@ -125,10 +125,9 @@ static inline apic_id_t x86_apicid_from_cpu_idx(unsigned nr_cores, unsigned nr_threads, unsigned cpu_index) { - unsigned pkg_id, core_id, smt_id; - x86_topo_ids_from_idx(nr_cores, nr_threads, cpu_index, - &pkg_id, &core_id, &smt_id); - return apicid_from_topo_ids(nr_cores, nr_threads, pkg_id, core_id, smt_id); + X86CPUTopoInfo topo; + x86_topo_ids_from_idx(nr_cores, nr_threads, cpu_index, &topo); + return apicid_from_topo_ids(nr_cores, nr_threads, &topo); } #endif /* HW_I386_TOPOLOGY_H */ diff --git a/qemu/include/hw/input/adb.h b/qemu/include/hw/input/adb.h index bdfccd404..db51d0380 100644 --- a/qemu/include/hw/input/adb.h +++ b/qemu/include/hw/input/adb.h @@ -79,7 +79,7 @@ struct ADBBusState { int adb_request(ADBBusState *s, uint8_t *buf_out, const uint8_t *buf, int len); -int adb_poll(ADBBusState *s, uint8_t *buf_out); +int adb_poll(ADBBusState *s, uint8_t *buf_out, uint16_t poll_mask); #define TYPE_ADB_KEYBOARD "adb-keyboard" #define TYPE_ADB_MOUSE "adb-mouse" diff --git a/qemu/include/hw/intc/arm_gic_common.h b/qemu/include/hw/intc/arm_gic_common.h index 899db3d7a..f4c349a2e 100644 --- a/qemu/include/hw/intc/arm_gic_common.h +++ b/qemu/include/hw/intc/arm_gic_common.h @@ -68,7 +68,6 @@ typedef struct GICState { uint8_t irq_target[GIC_MAXIRQ]; uint8_t priority1[GIC_INTERNAL][GIC_NCPU]; uint8_t priority2[GIC_MAXIRQ - GIC_INTERNAL]; - uint16_t last_active[GIC_MAXIRQ][GIC_NCPU]; /* For each SGI on the target CPU, we store 8 bits * indicating which source CPUs have made this SGI * pending on the target CPU. These correspond to @@ -78,7 +77,6 @@ typedef struct GICState { uint8_t sgi_pending[GIC_NR_SGIS][GIC_NCPU]; uint16_t priority_mask[GIC_NCPU]; - uint16_t running_irq[GIC_NCPU]; uint16_t running_priority[GIC_NCPU]; uint16_t current_pending[GIC_NCPU]; @@ -96,16 +94,9 @@ typedef struct GICState { * If an interrupt for preemption level X is active, then * APRn[X mod 32] == 0b1, where n = X / 32 * otherwise the bit is clear. - * - * TODO: rewrite the interrupt acknowlege/complete routines to use - * the APR registers to track the necessary information to update - * s->running_priority[] on interrupt completion (ie completely remove - * last_active[][] and running_irq[]). This will be necessary if we ever - * want to support TCG<->KVM migration, or TCG guests which can - * do power management involving powering down and restarting - * the GIC. */ uint32_t apr[GIC_NR_APRS][GIC_NCPU]; + uint32_t nsapr[GIC_NR_APRS][GIC_NCPU]; uint32_t num_cpu; @@ -118,7 +109,9 @@ typedef struct GICState { uint32_t num_irq; uint32_t revision; bool security_extn; + bool irq_reset_nonsecure; /* configure IRQs as group 1 (NS) on reset? */ int dev_fd; /* kvm device fd if backed by kvm vgic support */ + Error *migration_blocker; } GICState; #define TYPE_ARM_GIC_COMMON "arm_gic_common" @@ -138,4 +131,7 @@ typedef struct ARMGICCommonClass { void (*post_load)(GICState *s); } ARMGICCommonClass; +void gic_init_irqs_and_mmio(GICState *s, qemu_irq_handler handler, + const MemoryRegionOps *ops); + #endif diff --git a/qemu/include/hw/intc/arm_gicv3_common.h b/qemu/include/hw/intc/arm_gicv3_common.h new file mode 100644 index 000000000..c2fd8da4e --- /dev/null +++ b/qemu/include/hw/intc/arm_gicv3_common.h @@ -0,0 +1,68 @@ +/* + * ARM GIC support + * + * Copyright (c) 2012 Linaro Limited + * Copyright (c) 2015 Huawei. + * Written by Peter Maydell + * Extended to 64 cores by Shlomo Pongratz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef HW_ARM_GICV3_COMMON_H +#define HW_ARM_GICV3_COMMON_H + +#include "hw/sysbus.h" +#include "hw/intc/arm_gic_common.h" + +typedef struct GICv3State { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + qemu_irq *parent_irq; + qemu_irq *parent_fiq; + + MemoryRegion iomem_dist; /* Distributor */ + MemoryRegion iomem_redist; /* Redistributors */ + + uint32_t num_cpu; + uint32_t num_irq; + uint32_t revision; + bool security_extn; + + int dev_fd; /* kvm device fd if backed by kvm vgic support */ +} GICv3State; + +#define TYPE_ARM_GICV3_COMMON "arm-gicv3-common" +#define ARM_GICV3_COMMON(obj) \ + OBJECT_CHECK(GICv3State, (obj), TYPE_ARM_GICV3_COMMON) +#define ARM_GICV3_COMMON_CLASS(klass) \ + OBJECT_CLASS_CHECK(ARMGICv3CommonClass, (klass), TYPE_ARM_GICV3_COMMON) +#define ARM_GICV3_COMMON_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ARMGICv3CommonClass, (obj), TYPE_ARM_GICV3_COMMON) + +typedef struct ARMGICv3CommonClass { + /*< private >*/ + SysBusDeviceClass parent_class; + /*< public >*/ + + void (*pre_save)(GICv3State *s); + void (*post_load)(GICv3State *s); +} ARMGICv3CommonClass; + +void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, + const MemoryRegionOps *ops); + +#endif diff --git a/qemu/include/hw/intc/aspeed_vic.h b/qemu/include/hw/intc/aspeed_vic.h new file mode 100644 index 000000000..107ff17c3 --- /dev/null +++ b/qemu/include/hw/intc/aspeed_vic.h @@ -0,0 +1,48 @@ +/* + * ASPEED Interrupt Controller (New) + * + * Andrew Jeffery + * + * Copyright 2016 IBM Corp. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + * + * Need to add SVIC and CVIC support + */ +#ifndef ASPEED_VIC_H +#define ASPEED_VIC_H + +#include "hw/sysbus.h" + +#define TYPE_ASPEED_VIC "aspeed.vic" +#define ASPEED_VIC(obj) OBJECT_CHECK(AspeedVICState, (obj), TYPE_ASPEED_VIC) + +#define ASPEED_VIC_NR_IRQS 51 + +typedef struct AspeedVICState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + qemu_irq irq; + qemu_irq fiq; + + uint64_t level; + uint64_t raw; + uint64_t select; + uint64_t enable; + uint64_t trigger; + + /* 0=edge, 1=level */ + uint64_t sense; + + /* 0=single-edge, 1=dual-edge */ + uint64_t dual_edge; + + /* 0=low-sensitive/falling-edge, 1=high-sensitive/rising-edge */ + uint64_t event; +} AspeedVICState; + +#endif /* ASPEED_VIC_H */ diff --git a/qemu/include/hw/intc/bcm2835_ic.h b/qemu/include/hw/intc/bcm2835_ic.h new file mode 100644 index 000000000..fb75fa006 --- /dev/null +++ b/qemu/include/hw/intc/bcm2835_ic.h @@ -0,0 +1,33 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2835_IC_H +#define BCM2835_IC_H + +#include "hw/sysbus.h" + +#define TYPE_BCM2835_IC "bcm2835-ic" +#define BCM2835_IC(obj) OBJECT_CHECK(BCM2835ICState, (obj), TYPE_BCM2835_IC) + +#define BCM2835_IC_GPU_IRQ "gpu-irq" +#define BCM2835_IC_ARM_IRQ "arm-irq" + +typedef struct BCM2835ICState { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + MemoryRegion iomem; + qemu_irq irq; + qemu_irq fiq; + + /* 64 GPU IRQs + 8 ARM IRQs = 72 total (GPU first) */ + uint64_t gpu_irq_level, gpu_irq_enable; + uint8_t arm_irq_level, arm_irq_enable; + bool fiq_enable; + uint8_t fiq_select; +} BCM2835ICState; + +#endif diff --git a/qemu/include/hw/intc/bcm2836_control.h b/qemu/include/hw/intc/bcm2836_control.h new file mode 100644 index 000000000..613f3c418 --- /dev/null +++ b/qemu/include/hw/intc/bcm2836_control.h @@ -0,0 +1,51 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous + * + * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2836_CONTROL_H +#define BCM2836_CONTROL_H + +#include "hw/sysbus.h" + +/* 4 mailboxes per core, for 16 total */ +#define BCM2836_NCORES 4 +#define BCM2836_MBPERCORE 4 + +#define TYPE_BCM2836_CONTROL "bcm2836-control" +#define BCM2836_CONTROL(obj) \ + OBJECT_CHECK(BCM2836ControlState, (obj), TYPE_BCM2836_CONTROL) + +typedef struct BCM2836ControlState { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + MemoryRegion iomem; + + /* mailbox state */ + uint32_t mailboxes[BCM2836_NCORES * BCM2836_MBPERCORE]; + + /* interrupt routing/control registers */ + uint8_t route_gpu_irq, route_gpu_fiq; + uint32_t timercontrol[BCM2836_NCORES]; + uint32_t mailboxcontrol[BCM2836_NCORES]; + + /* interrupt status regs (derived from input pins; not visible to user) */ + bool gpu_irq, gpu_fiq; + uint8_t timerirqs[BCM2836_NCORES]; + + /* interrupt source registers, post-routing (also input-derived; visible) */ + uint32_t irqsrc[BCM2836_NCORES]; + uint32_t fiqsrc[BCM2836_NCORES]; + + /* outputs to CPU cores */ + qemu_irq irq[BCM2836_NCORES]; + qemu_irq fiq[BCM2836_NCORES]; +} BCM2836ControlState; + +#endif diff --git a/qemu/include/hw/intc/imx_avic.h b/qemu/include/hw/intc/imx_avic.h new file mode 100644 index 000000000..1b8076901 --- /dev/null +++ b/qemu/include/hw/intc/imx_avic.h @@ -0,0 +1,55 @@ +/* + * i.MX31 Vectored Interrupt Controller + * + * Note this is NOT the PL192 provided by ARM, but + * a custom implementation by Freescale. + * + * Copyright (c) 2008 OKL + * Copyright (c) 2011 NICTA Pty Ltd + * Originally written by Hans Jiang + * Updated by Jean-Christophe Dubois + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + * + * TODO: implement vectors. + */ +#ifndef IMX_AVIC_H +#define IMX_AVIC_H + +#include "hw/sysbus.h" + +#define TYPE_IMX_AVIC "imx.avic" +#define IMX_AVIC(obj) OBJECT_CHECK(IMXAVICState, (obj), TYPE_IMX_AVIC) + +#define IMX_AVIC_NUM_IRQS 64 + +/* Interrupt Control Bits */ +#define ABFLAG (1<<25) +#define ABFEN (1<<24) +#define NIDIS (1<<22) /* Normal Interrupt disable */ +#define FIDIS (1<<21) /* Fast interrupt disable */ +#define NIAD (1<<20) /* Normal Interrupt Arbiter Rise ARM level */ +#define FIAD (1<<19) /* Fast Interrupt Arbiter Rise ARM level */ +#define NM (1<<18) /* Normal interrupt mode */ + +#define PRIO_PER_WORD (sizeof(uint32_t) * 8 / 4) +#define PRIO_WORDS (IMX_AVIC_NUM_IRQS/PRIO_PER_WORD) + +typedef struct IMXAVICState{ + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + uint64_t pending; + uint64_t enabled; + uint64_t is_fiq; + uint32_t intcntl; + uint32_t intmask; + qemu_irq irq; + qemu_irq fiq; + uint32_t prio[PRIO_WORDS]; /* Priorities are 4-bits each */ +} IMXAVICState; + +#endif /* IMX_AVIC_H */ diff --git a/qemu/include/hw/ipmi/ipmi.h b/qemu/include/hw/ipmi/ipmi.h new file mode 100644 index 000000000..74a2b5af9 --- /dev/null +++ b/qemu/include/hw/ipmi/ipmi.h @@ -0,0 +1,258 @@ +/* + * IPMI base class + * + * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_IPMI_H +#define HW_IPMI_H + +#include "exec/memory.h" +#include "qemu-common.h" +#include "hw/qdev.h" + +#define MAX_IPMI_MSG_SIZE 300 + +enum ipmi_op { + IPMI_RESET_CHASSIS, + IPMI_POWEROFF_CHASSIS, + IPMI_POWERON_CHASSIS, + IPMI_POWERCYCLE_CHASSIS, + IPMI_PULSE_DIAG_IRQ, + IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP, + IPMI_SEND_NMI +}; + +#define IPMI_CC_INVALID_CMD 0xc1 +#define IPMI_CC_COMMAND_INVALID_FOR_LUN 0xc2 +#define IPMI_CC_TIMEOUT 0xc3 +#define IPMI_CC_OUT_OF_SPACE 0xc4 +#define IPMI_CC_INVALID_RESERVATION 0xc5 +#define IPMI_CC_REQUEST_DATA_TRUNCATED 0xc6 +#define IPMI_CC_REQUEST_DATA_LENGTH_INVALID 0xc7 +#define IPMI_CC_PARM_OUT_OF_RANGE 0xc9 +#define IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES 0xca +#define IPMI_CC_REQ_ENTRY_NOT_PRESENT 0xcb +#define IPMI_CC_INVALID_DATA_FIELD 0xcc +#define IPMI_CC_BMC_INIT_IN_PROGRESS 0xd2 +#define IPMI_CC_COMMAND_NOT_SUPPORTED 0xd5 + +#define IPMI_NETFN_APP 0x06 + +#define IPMI_DEBUG 1 + +/* Specified in the SMBIOS spec. */ +#define IPMI_SMBIOS_KCS 0x01 +#define IPMI_SMBIOS_SMIC 0x02 +#define IPMI_SMBIOS_BT 0x03 +#define IPMI_SMBIOS_SSIF 0x04 + +/* IPMI Interface types (KCS, SMIC, BT) are prefixed with this */ +#define TYPE_IPMI_INTERFACE_PREFIX "ipmi-interface-" + +/* + * An IPMI Interface, the interface for talking between the target + * and the BMC. + */ +#define TYPE_IPMI_INTERFACE "ipmi-interface" +#define IPMI_INTERFACE(obj) \ + INTERFACE_CHECK(IPMIInterface, (obj), TYPE_IPMI_INTERFACE) +#define IPMI_INTERFACE_CLASS(class) \ + OBJECT_CLASS_CHECK(IPMIInterfaceClass, (class), TYPE_IPMI_INTERFACE) +#define IPMI_INTERFACE_GET_CLASS(class) \ + OBJECT_GET_CLASS(IPMIInterfaceClass, (class), TYPE_IPMI_INTERFACE) + +typedef struct IPMIInterface { + Object parent; +} IPMIInterface; + +typedef struct IPMIInterfaceClass { + InterfaceClass parent; + + void (*init)(struct IPMIInterface *s, Error **errp); + + /* + * Perform various operations on the hardware. If checkonly is + * true, it will return if the operation can be performed, but it + * will not do the operation. + */ + int (*do_hw_op)(struct IPMIInterface *s, enum ipmi_op op, int checkonly); + + /* + * Enable/disable irqs on the interface when the BMC requests this. + */ + void (*set_irq_enable)(struct IPMIInterface *s, int val); + + /* + * Handle an event that occurred on the interface, generally the. + * target writing to a register. + */ + void (*handle_if_event)(struct IPMIInterface *s); + + /* + * The interfaces use this to perform certain ops + */ + void (*set_atn)(struct IPMIInterface *s, int val, int irq); + + /* + * Got an IPMI warm/cold reset. + */ + void (*reset)(struct IPMIInterface *s, bool is_cold); + + /* + * Handle a response from the bmc. + */ + void (*handle_rsp)(struct IPMIInterface *s, uint8_t msg_id, + unsigned char *rsp, unsigned int rsp_len); + + /* + * Set by the owner to hold the backend data for the interface. + */ + void *(*get_backend_data)(struct IPMIInterface *s); +} IPMIInterfaceClass; + +/* + * Define a BMC simulator (or perhaps a connection to a real BMC) + */ +#define TYPE_IPMI_BMC "ipmi-bmc" +#define IPMI_BMC(obj) \ + OBJECT_CHECK(IPMIBmc, (obj), TYPE_IPMI_BMC) +#define IPMI_BMC_CLASS(obj_class) \ + OBJECT_CLASS_CHECK(IPMIBmcClass, (obj_class), TYPE_IPMI_BMC) +#define IPMI_BMC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(IPMIBmcClass, (obj), TYPE_IPMI_BMC) + +typedef struct IPMIBmc { + DeviceState parent; + + uint8_t slave_addr; + + IPMIInterface *intf; +} IPMIBmc; + +typedef struct IPMIBmcClass { + DeviceClass parent; + + /* Called when the system resets to report to the bmc. */ + void (*handle_reset)(struct IPMIBmc *s); + + /* + * Handle a command to the bmc. + */ + void (*handle_command)(struct IPMIBmc *s, + uint8_t *cmd, unsigned int cmd_len, + unsigned int max_cmd_len, + uint8_t msg_id); +} IPMIBmcClass; + +/* + * Add a link property to obj that points to a BMC. + */ +void ipmi_bmc_find_and_link(Object *obj, Object **bmc); + +/* + * Used for transferring information to interfaces that add + * entries to firmware tables. + */ +typedef struct IPMIFwInfo { + const char *interface_name; + int interface_type; + uint8_t ipmi_spec_major_revision; + uint8_t ipmi_spec_minor_revision; + uint8_t i2c_slave_address; + uint32_t uuid; + + uint64_t base_address; + uint64_t register_length; + uint8_t register_spacing; + enum { + IPMI_MEMSPACE_IO, + IPMI_MEMSPACE_MEM32, + IPMI_MEMSPACE_MEM64, + IPMI_MEMSPACE_SMBUS + } memspace; + + int interrupt_number; + enum { + IPMI_LEVEL_IRQ, + IPMI_EDGE_IRQ + } irq_type; + + const char *acpi_parent; +} IPMIFwInfo; + +void ipmi_add_fwinfo(IPMIFwInfo *info, Error **errp); +IPMIFwInfo *ipmi_first_fwinfo(void); +IPMIFwInfo *ipmi_next_fwinfo(IPMIFwInfo *current); + +#ifdef IPMI_DEBUG +#define ipmi_debug(fs, ...) \ + fprintf(stderr, "IPMI (%s): " fs, __func__, ##__VA_ARGS__) +#else +#define ipmi_debug(fs, ...) +#endif + +struct ipmi_sdr_header { + uint8_t rec_id[2]; + uint8_t sdr_version; /* 0x51 */ + uint8_t rec_type; + uint8_t rec_length; +}; +#define IPMI_SDR_HEADER_SIZE sizeof(struct ipmi_sdr_header) + +#define ipmi_sdr_recid(sdr) ((sdr)->rec_id[0] | ((sdr)->rec_id[1] << 8)) +#define ipmi_sdr_length(sdr) ((sdr)->rec_length + IPMI_SDR_HEADER_SIZE) + +/* + * 43.2 SDR Type 02h. Compact Sensor Record + */ +#define IPMI_SDR_COMPACT_TYPE 2 + +struct ipmi_sdr_compact { + struct ipmi_sdr_header header; + + uint8_t sensor_owner_id; + uint8_t sensor_owner_lun; + uint8_t sensor_owner_number; /* byte 8 */ + uint8_t entity_id; + uint8_t entity_instance; + uint8_t sensor_init; + uint8_t sensor_caps; + uint8_t sensor_type; + uint8_t reading_type; + uint8_t assert_mask[2]; /* byte 16 */ + uint8_t deassert_mask[2]; + uint8_t discrete_mask[2]; + uint8_t sensor_unit1; + uint8_t sensor_unit2; + uint8_t sensor_unit3; + uint8_t sensor_direction[2]; /* byte 24 */ + uint8_t positive_threshold; + uint8_t negative_threshold; + uint8_t reserved[3]; + uint8_t oem; + uint8_t id_str_len; /* byte 32 */ + uint8_t id_string[16]; +}; + +typedef uint8_t ipmi_sdr_compact_buffer[sizeof(struct ipmi_sdr_compact)]; + +#endif diff --git a/qemu/include/hw/isa/apm.h b/qemu/include/hw/isa/apm.h index 3edea5f62..4839ff1df 100644 --- a/qemu/include/hw/isa/apm.h +++ b/qemu/include/hw/isa/apm.h @@ -1,7 +1,6 @@ #ifndef APM_H #define APM_H -#include #include "qemu-common.h" #include "hw/hw.h" #include "exec/memory.h" diff --git a/qemu/include/hw/isa/i8257.h b/qemu/include/hw/isa/i8257.h new file mode 100644 index 000000000..8c44d3628 --- /dev/null +++ b/qemu/include/hw/isa/i8257.h @@ -0,0 +1,42 @@ +#ifndef HW_I8257_H +#define HW_I8257_H + +#define TYPE_I8257 "i8257" + +typedef struct I8257Regs { + int now[2]; + uint16_t base[2]; + uint8_t mode; + uint8_t page; + uint8_t pageh; + uint8_t dack; + uint8_t eop; + IsaDmaTransferHandler transfer_handler; + void *opaque; +} I8257Regs; + +typedef struct I8257State { + /* */ + ISADevice parent_obj; + + /* */ + int32_t base; + int32_t page_base; + int32_t pageh_base; + int32_t dshift; + + uint8_t status; + uint8_t command; + uint8_t mask; + uint8_t flip_flop; + I8257Regs regs[4]; + MemoryRegion channel_io; + MemoryRegion cont_io; + + QEMUBH *dma_bh; + bool dma_bh_scheduled; + int running; +} I8257State; + +#endif + diff --git a/qemu/include/hw/isa/isa.h b/qemu/include/hw/isa/isa.h index f21ceaafc..ffb2ea7cd 100644 --- a/qemu/include/hw/isa/isa.h +++ b/qemu/include/hw/isa/isa.h @@ -34,6 +34,44 @@ static inline uint16_t applesmc_port(void) return 0; } +#define TYPE_ISADMA "isa-dma" + +#define ISADMA_CLASS(klass) \ + OBJECT_CLASS_CHECK(IsaDmaClass, (klass), TYPE_ISADMA) +#define ISADMA_GET_CLASS(obj) \ + OBJECT_GET_CLASS(IsaDmaClass, (obj), TYPE_ISADMA) +#define ISADMA(obj) \ + INTERFACE_CHECK(IsaDma, (obj), TYPE_ISADMA) + +struct IsaDma { + Object parent; +}; + +typedef enum { + ISADMA_TRANSFER_VERIFY, + ISADMA_TRANSFER_READ, + ISADMA_TRANSFER_WRITE, + ISADMA_TRANSFER_ILLEGAL, +} IsaDmaTransferMode; + +typedef int (*IsaDmaTransferHandler)(void *opaque, int nchan, int pos, + int size); + +typedef struct IsaDmaClass { + InterfaceClass parent; + + IsaDmaTransferMode (*get_transfer_mode)(IsaDma *obj, int nchan); + bool (*has_autoinitialization)(IsaDma *obj, int nchan); + int (*read_memory)(IsaDma *obj, int nchan, void *buf, int pos, int len); + int (*write_memory)(IsaDma *obj, int nchan, void *buf, int pos, int len); + void (*hold_DREQ)(IsaDma *obj, int nchan); + void (*release_DREQ)(IsaDma *obj, int nchan); + void (*schedule)(IsaDma *obj); + void (*register_channel)(IsaDma *obj, int nchan, + IsaDmaTransferHandler transfer_handler, + void *opaque); +} IsaDmaClass; + typedef struct ISADeviceClass { DeviceClass parent_class; } ISADeviceClass; @@ -46,6 +84,7 @@ struct ISABus { MemoryRegion *address_space; MemoryRegion *address_space_io; qemu_irq *irqs; + IsaDma *dma[2]; }; struct ISADevice { @@ -59,10 +98,12 @@ struct ISADevice { }; ISABus *isa_bus_new(DeviceState *dev, MemoryRegion *address_space, - MemoryRegion *address_space_io); + MemoryRegion *address_space_io, Error **errp); void isa_bus_irqs(ISABus *bus, qemu_irq *irqs); qemu_irq isa_get_irq(ISADevice *dev, int isairq); void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq); +void isa_bus_dma(ISABus *bus, IsaDma *dma8, IsaDma *dma16); +IsaDma *isa_get_dma(ISABus *bus, int nchan); MemoryRegion *isa_address_space(ISADevice *dev); MemoryRegion *isa_address_space_io(ISADevice *dev); ISADevice *isa_create(ISABus *bus, const char *name); @@ -106,15 +147,6 @@ static inline ISABus *isa_bus_from_device(ISADevice *d) return ISA_BUS(qdev_get_parent_bus(DEVICE(d))); } -/* dma.c */ -int DMA_get_channel_mode (int nchan); -int DMA_read_memory (int nchan, void *buf, int pos, int size); -int DMA_write_memory (int nchan, void *buf, int pos, int size); -void DMA_hold_DREQ (int nchan); -void DMA_release_DREQ (int nchan); -void DMA_schedule(int nchan); -void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit); -void DMA_register_channel (int nchan, - DMA_transfer_handler transfer_handler, - void *opaque); +/* i8257.c */ +void DMA_init(ISABus *bus, int high_page_enable); #endif diff --git a/qemu/include/hw/loader.h b/qemu/include/hw/loader.h index f7b43ab62..4879b63a2 100644 --- a/qemu/include/hw/loader.h +++ b/qemu/include/hw/loader.h @@ -16,6 +16,18 @@ int load_image(const char *filename, uint8_t *addr); /* deprecated */ ssize_t load_image_size(const char *filename, void *addr, size_t size); int load_image_targphys(const char *filename, hwaddr, uint64_t max_sz); +/** + * load_image_mr: load an image into a memory region + * @filename: Path to the image file + * @mr: Memory Region to load into + * + * Load the specified file into the memory region. + * The file loaded is registered as a ROM, so its contents will be + * reinstated whenever the system is reset. + * If the file is larger than the memory region's size the call will fail. + * Returns -1 on failure, or the size of the file. + */ +int load_image_mr(const char *filename, MemoryRegion *mr); /* This is the limit on the maximum uncompressed image size that * load_image_gzipped_buffer() and load_image_gzipped() will read. It prevents @@ -32,10 +44,49 @@ int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz); #define ELF_LOAD_WRONG_ARCH -3 #define ELF_LOAD_WRONG_ENDIAN -4 const char *load_elf_strerror(int error); + +/** load_elf: + * @filename: Path of ELF file + * @translate_fn: optional function to translate load addresses + * @translate_opaque: opaque data passed to @translate_fn + * @pentry: Populated with program entry point. Ignored if NULL. + * @lowaddr: Populated with lowest loaded address. Ignored if NULL. + * @highaddr: Populated with highest loaded address. Ignored if NULL. + * @bigendian: Expected ELF endianness. 0 for LE otherwise BE + * @elf_machine: Expected ELF machine type + * @clear_lsb: Set to mask off LSB of addresses (Some architectures use + * this for non-address data) + * @data_swab: Set to order of byte swapping for data. 0 for no swap, 1 + * for swapping bytes within halfwords, 2 for bytes within + * words and 3 for within doublewords. + * + * Load an ELF file's contents to the emulated system's address space. + * Clients may optionally specify a callback to perform address + * translations. @pentry, @lowaddr and @highaddr are optional pointers + * which will be populated with various load information. @bigendian and + * @elf_machine give the expected endianness and machine for the ELF the + * load will fail if the target ELF does not match. Some architectures + * have some architecture-specific behaviours that come into effect when + * their particular values for @elf_machine are set. + */ + int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, int big_endian, int elf_machine, - int clear_lsb); + int clear_lsb, int data_swab); + +/** load_elf_hdr: + * @filename: Path of ELF file + * @hdr: Buffer to populate with header data. Header data will not be + * filled if set to NULL. + * @is64: Set to true if the ELF is 64bit. Ignored if set to NULL + * @errp: Populated with an error in failure cases + * + * Inspect an ELF file's header. Read its full header contents into a + * buffer and/or determine if the ELF is 64bit. + */ +void load_elf_hdr(const char *filename, void *hdr, bool *is64, Error **errp); + int load_aout(const char *filename, hwaddr addr, int max_sz, int bswap_needed, hwaddr target_page_size); int load_uimage(const char *filename, hwaddr *ep, @@ -67,7 +118,7 @@ extern bool rom_file_has_mr; int rom_add_file(const char *file, const char *fw_dir, hwaddr addr, int32_t bootindex, - bool option_rom); + bool option_rom, MemoryRegion *mr); MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len, size_t max_len, hwaddr addr, const char *fw_file_name, @@ -77,14 +128,18 @@ int rom_add_elf_program(const char *name, void *data, size_t datasize, size_t romsize, hwaddr addr); int rom_check_and_register_reset(void); void rom_set_fw(FWCfgState *f); +void rom_set_order_override(int order); +void rom_reset_order_override(void); int rom_copy(uint8_t *dest, hwaddr addr, size_t size); void *rom_ptr(hwaddr addr); void hmp_info_roms(Monitor *mon, const QDict *qdict); #define rom_add_file_fixed(_f, _a, _i) \ - rom_add_file(_f, NULL, _a, _i, false) + rom_add_file(_f, NULL, _a, _i, false, NULL) #define rom_add_blob_fixed(_f, _b, _l, _a) \ rom_add_blob(_f, _b, _l, _l, _a, NULL, NULL, NULL) +#define rom_add_file_mr(_f, _mr, _i) \ + rom_add_file(_f, NULL, 0, _i, false, _mr) #define PC_ROM_MIN_VGA 0xc0000 #define PC_ROM_MIN_OPTION 0xc8000 diff --git a/qemu/include/hw/mem/nvdimm.h b/qemu/include/hw/mem/nvdimm.h new file mode 100644 index 000000000..517de9c36 --- /dev/null +++ b/qemu/include/hw/mem/nvdimm.h @@ -0,0 +1,62 @@ +/* + * Non-Volatile Dual In-line Memory Module Virtualization Implementation + * + * Copyright(C) 2015 Intel Corporation. + * + * Author: + * Xiao Guangrong + * + * NVDIMM specifications and some documents can be found at: + * NVDIMM ACPI device and NFIT are introduced in ACPI 6: + * http://www.uefi.org/sites/default/files/resources/ACPI_6.0.pdf + * NVDIMM Namespace specification: + * http://pmem.io/documents/NVDIMM_Namespace_Spec.pdf + * DSM Interface Example: + * http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf + * Driver Writer's Guide: + * http://pmem.io/documents/NVDIMM_Driver_Writers_Guide.pdf + * + * 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_NVDIMM_H +#define QEMU_NVDIMM_H + +#include "hw/mem/pc-dimm.h" + +#define NVDIMM_DEBUG 0 +#define nvdimm_debug(fmt, ...) \ + do { \ + if (NVDIMM_DEBUG) { \ + fprintf(stderr, "nvdimm: " fmt, ## __VA_ARGS__); \ + } \ + } while (0) + +#define TYPE_NVDIMM "nvdimm" + +#define NVDIMM_DSM_MEM_FILE "etc/acpi/nvdimm-mem" + +/* + * 32 bits IO port starting from 0x0a18 in guest is reserved for + * NVDIMM ACPI emulation. + */ +#define NVDIMM_ACPI_IO_BASE 0x0a18 +#define NVDIMM_ACPI_IO_LEN 4 + +struct AcpiNVDIMMState { + /* detect if NVDIMM support is enabled. */ + bool is_enabled; + + /* the data of the fw_cfg file NVDIMM_DSM_MEM_FILE. */ + GArray *dsm_mem; + /* the IO region used by OSPM to transfer control to QEMU. */ + MemoryRegion io_mr; +}; +typedef struct AcpiNVDIMMState AcpiNVDIMMState; + +void nvdimm_init_acpi_state(AcpiNVDIMMState *state, MemoryRegion *io, + FWCfgState *fw_cfg, Object *owner); +void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data, + GArray *linker); +#endif diff --git a/qemu/include/hw/mem/pc-dimm.h b/qemu/include/hw/mem/pc-dimm.h index d83bf30ea..218dfb0ed 100644 --- a/qemu/include/hw/mem/pc-dimm.h +++ b/qemu/include/hw/mem/pc-dimm.h @@ -77,7 +77,7 @@ typedef struct PCDIMMDeviceClass { * @mr: hotplug memory address space container */ typedef struct MemoryHotplugState { - ram_addr_t base; + hwaddr base; MemoryRegion mr; } MemoryHotplugState; diff --git a/qemu/include/hw/mips/cps.h b/qemu/include/hw/mips/cps.h new file mode 100644 index 000000000..4dbae9c8c --- /dev/null +++ b/qemu/include/hw/mips/cps.h @@ -0,0 +1,46 @@ +/* + * Coherent Processing System emulation. + * + * Copyright (c) 2016 Imagination Technologies + * + * 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 . + */ + +#ifndef MIPS_CPS_H +#define MIPS_CPS_H + +#include "hw/sysbus.h" +#include "hw/misc/mips_cmgcr.h" +#include "hw/misc/mips_cpc.h" +#include "hw/misc/mips_itu.h" + +#define TYPE_MIPS_CPS "mips-cps" +#define MIPS_CPS(obj) OBJECT_CHECK(MIPSCPSState, (obj), TYPE_MIPS_CPS) + +typedef struct MIPSCPSState { + SysBusDevice parent_obj; + + uint32_t num_vp; + uint32_t num_irq; + char *cpu_model; + + MemoryRegion container; + MIPSGCRState gcr; + MIPSCPCState cpc; + MIPSITUState itu; +} MIPSCPSState; + +qemu_irq get_cps_irq(MIPSCPSState *cps, int pin_number); + +#endif diff --git a/qemu/include/hw/misc/bcm2835_mbox.h b/qemu/include/hw/misc/bcm2835_mbox.h new file mode 100644 index 000000000..f4e9ff9ef --- /dev/null +++ b/qemu/include/hw/misc/bcm2835_mbox.h @@ -0,0 +1,38 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2835_MBOX_H +#define BCM2835_MBOX_H + +#include "bcm2835_mbox_defs.h" +#include "hw/sysbus.h" +#include "exec/address-spaces.h" + +#define TYPE_BCM2835_MBOX "bcm2835-mbox" +#define BCM2835_MBOX(obj) \ + OBJECT_CHECK(BCM2835MboxState, (obj), TYPE_BCM2835_MBOX) + +typedef struct { + uint32_t reg[MBOX_SIZE]; + uint32_t count; + uint32_t status; + uint32_t config; +} BCM2835Mbox; + +typedef struct { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + MemoryRegion *mbox_mr; + AddressSpace mbox_as; + MemoryRegion iomem; + qemu_irq arm_irq; + + bool mbox_irq_disabled; + bool available[MBOX_CHAN_COUNT]; + BCM2835Mbox mbox[2]; +} BCM2835MboxState; + +#endif diff --git a/qemu/include/hw/misc/bcm2835_mbox_defs.h b/qemu/include/hw/misc/bcm2835_mbox_defs.h new file mode 100644 index 000000000..a18e520b2 --- /dev/null +++ b/qemu/include/hw/misc/bcm2835_mbox_defs.h @@ -0,0 +1,27 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2835_MBOX_DEFS_H +#define BCM2835_MBOX_DEFS_H + +/* Constants shared with the ARM identifying separate mailbox channels */ +#define MBOX_CHAN_POWER 0 /* for use by the power management interface */ +#define MBOX_CHAN_FB 1 /* for use by the frame buffer */ +#define MBOX_CHAN_VCHIQ 3 /* for use by the VCHIQ interface */ +#define MBOX_CHAN_PROPERTY 8 /* for use by the property channel */ +#define MBOX_CHAN_COUNT 9 + +#define MBOX_SIZE 32 +#define MBOX_INVALID_DATA 0x0f + +/* Layout of the private address space used for communication between + * the mbox device emulation, and child devices: each channel occupies + * 16 bytes of address space, but only two registers are presently defined. + */ +#define MBOX_AS_CHAN_SHIFT 4 +#define MBOX_AS_DATA 0 /* request / response data (RW at offset 0) */ +#define MBOX_AS_PENDING 4 /* pending response status (RO at offset 4) */ + +#endif /* BCM2835_MBOX_DEFS_H */ diff --git a/qemu/include/hw/misc/bcm2835_property.h b/qemu/include/hw/misc/bcm2835_property.h new file mode 100644 index 000000000..edcab603c --- /dev/null +++ b/qemu/include/hw/misc/bcm2835_property.h @@ -0,0 +1,35 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2835_PROPERTY_H +#define BCM2835_PROPERTY_H + +#include "hw/sysbus.h" +#include "exec/address-spaces.h" +#include "net/net.h" +#include "hw/display/bcm2835_fb.h" + +#define TYPE_BCM2835_PROPERTY "bcm2835-property" +#define BCM2835_PROPERTY(obj) \ + OBJECT_CHECK(BCM2835PropertyState, (obj), TYPE_BCM2835_PROPERTY) + +typedef struct { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + MemoryRegion *dma_mr; + AddressSpace dma_as; + MemoryRegion iomem; + qemu_irq mbox_irq; + BCM2835FBState *fbdev; + + MACAddr macaddr; + uint32_t board_rev; + uint32_t addr; + bool pending; +} BCM2835PropertyState; + +#endif diff --git a/qemu/include/hw/misc/imx25_ccm.h b/qemu/include/hw/misc/imx25_ccm.h new file mode 100644 index 000000000..296321c61 --- /dev/null +++ b/qemu/include/hw/misc/imx25_ccm.h @@ -0,0 +1,79 @@ +/* + * IMX25 Clock Control Module + * + * Copyright (C) 2012 NICTA + * Updated by Jean-Christophe Dubois + * + * 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 IMX25_CCM_H +#define IMX25_CCM_H + +#include "hw/misc/imx_ccm.h" + +#define IMX25_CCM_MPCTL_REG 0 +#define IMX25_CCM_UPCTL_REG 1 +#define IMX25_CCM_CCTL_REG 2 +#define IMX25_CCM_CGCR0_REG 3 +#define IMX25_CCM_CGCR1_REG 4 +#define IMX25_CCM_CGCR2_REG 5 +#define IMX25_CCM_PCDR0_REG 6 +#define IMX25_CCM_PCDR1_REG 7 +#define IMX25_CCM_PCDR2_REG 8 +#define IMX25_CCM_PCDR3_REG 9 +#define IMX25_CCM_RCSR_REG 10 +#define IMX25_CCM_CRDR_REG 11 +#define IMX25_CCM_DCVR0_REG 12 +#define IMX25_CCM_DCVR1_REG 13 +#define IMX25_CCM_DCVR2_REG 14 +#define IMX25_CCM_DCVR3_REG 15 +#define IMX25_CCM_LTR0_REG 16 +#define IMX25_CCM_LTR1_REG 17 +#define IMX25_CCM_LTR2_REG 18 +#define IMX25_CCM_LTR3_REG 19 +#define IMX25_CCM_LTBR0_REG 20 +#define IMX25_CCM_LTBR1_REG 21 +#define IMX25_CCM_PMCR0_REG 22 +#define IMX25_CCM_PMCR1_REG 23 +#define IMX25_CCM_PMCR2_REG 24 +#define IMX25_CCM_MCR_REG 25 +#define IMX25_CCM_LPIMR0_REG 26 +#define IMX25_CCM_LPIMR1_REG 27 +#define IMX25_CCM_MAX_REG 28 + +/* CCTL */ +#define CCTL_ARM_CLK_DIV_SHIFT (30) +#define CCTL_ARM_CLK_DIV_MASK (0x3) +#define CCTL_AHB_CLK_DIV_SHIFT (28) +#define CCTL_AHB_CLK_DIV_MASK (0x3) +#define CCTL_MPLL_BYPASS_SHIFT (22) +#define CCTL_MPLL_BYPASS_MASK (0x1) +#define CCTL_USB_DIV_SHIFT (16) +#define CCTL_USB_DIV_MASK (0x3F) +#define CCTL_ARM_SRC_SHIFT (13) +#define CCTL_ARM_SRC_MASK (0x1) +#define CCTL_UPLL_DIS_SHIFT (23) +#define CCTL_UPLL_DIS_MASK (0x1) + +#define EXTRACT(value, name) (((value) >> CCTL_##name##_SHIFT) \ + & CCTL_##name##_MASK) +#define INSERT(value, name) (((value) & CCTL_##name##_MASK) << \ + CCTL_##name##_SHIFT) + +#define TYPE_IMX25_CCM "imx25.ccm" +#define IMX25_CCM(obj) OBJECT_CHECK(IMX25CCMState, (obj), TYPE_IMX25_CCM) + +typedef struct IMX25CCMState { + /* */ + IMXCCMState parent_obj; + + /* */ + MemoryRegion iomem; + + uint32_t reg[IMX25_CCM_MAX_REG]; + +} IMX25CCMState; + +#endif /* IMX25_CCM_H */ diff --git a/qemu/include/hw/misc/imx31_ccm.h b/qemu/include/hw/misc/imx31_ccm.h new file mode 100644 index 000000000..c376fad14 --- /dev/null +++ b/qemu/include/hw/misc/imx31_ccm.h @@ -0,0 +1,88 @@ +/* + * IMX31 Clock Control Module + * + * Copyright (C) 2012 NICTA + * Updated by Jean-Christophe Dubois + * + * 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 IMX31_CCM_H +#define IMX31_CCM_H + +#include "hw/misc/imx_ccm.h" + +#define IMX31_CCM_CCMR_REG 0 +#define IMX31_CCM_PDR0_REG 1 +#define IMX31_CCM_PDR1_REG 2 +#define IMX31_CCM_RCSR_REG 3 +#define IMX31_CCM_MPCTL_REG 4 +#define IMX31_CCM_UPCTL_REG 5 +#define IMX31_CCM_SPCTL_REG 6 +#define IMX31_CCM_COSR_REG 7 +#define IMX31_CCM_CGR0_REG 8 +#define IMX31_CCM_CGR1_REG 9 +#define IMX31_CCM_CGR2_REG 10 +#define IMX31_CCM_WIMR_REG 11 +#define IMX31_CCM_LDC_REG 12 +#define IMX31_CCM_DCVR0_REG 13 +#define IMX31_CCM_DCVR1_REG 14 +#define IMX31_CCM_DCVR2_REG 15 +#define IMX31_CCM_DCVR3_REG 16 +#define IMX31_CCM_LTR0_REG 17 +#define IMX31_CCM_LTR1_REG 18 +#define IMX31_CCM_LTR2_REG 19 +#define IMX31_CCM_LTR3_REG 20 +#define IMX31_CCM_LTBR0_REG 21 +#define IMX31_CCM_LTBR1_REG 22 +#define IMX31_CCM_PMCR0_REG 23 +#define IMX31_CCM_PMCR1_REG 24 +#define IMX31_CCM_PDR2_REG 25 +#define IMX31_CCM_MAX_REG 26 + +/* CCMR */ +#define CCMR_FPME (1<<0) +#define CCMR_MPE (1<<3) +#define CCMR_MDS (1<<7) +#define CCMR_FPMF (1<<26) +#define CCMR_PRCS (3<<1) + +#define PMCR0_DFSUP1 (1<<31) + +/* PDR0 */ +#define PDR0_MCU_PODF_SHIFT (0) +#define PDR0_MCU_PODF_MASK (0x7) +#define PDR0_MAX_PODF_SHIFT (3) +#define PDR0_MAX_PODF_MASK (0x7) +#define PDR0_IPG_PODF_SHIFT (6) +#define PDR0_IPG_PODF_MASK (0x3) +#define PDR0_NFC_PODF_SHIFT (8) +#define PDR0_NFC_PODF_MASK (0x7) +#define PDR0_HSP_PODF_SHIFT (11) +#define PDR0_HSP_PODF_MASK (0x7) +#define PDR0_PER_PODF_SHIFT (16) +#define PDR0_PER_PODF_MASK (0x1f) +#define PDR0_CSI_PODF_SHIFT (23) +#define PDR0_CSI_PODF_MASK (0x1ff) + +#define EXTRACT(value, name) (((value) >> PDR0_##name##_PODF_SHIFT) \ + & PDR0_##name##_PODF_MASK) +#define INSERT(value, name) (((value) & PDR0_##name##_PODF_MASK) << \ + PDR0_##name##_PODF_SHIFT) + +#define TYPE_IMX31_CCM "imx31.ccm" +#define IMX31_CCM(obj) OBJECT_CHECK(IMX31CCMState, (obj), TYPE_IMX31_CCM) + +typedef struct IMX31CCMState { + /* */ + IMXCCMState parent_obj; + + /* */ + MemoryRegion iomem; + + uint32_t reg[IMX31_CCM_MAX_REG]; + +} IMX31CCMState; + +#endif /* IMX31_CCM_H */ diff --git a/qemu/include/hw/misc/imx6_ccm.h b/qemu/include/hw/misc/imx6_ccm.h new file mode 100644 index 000000000..80505809b --- /dev/null +++ b/qemu/include/hw/misc/imx6_ccm.h @@ -0,0 +1,197 @@ +/* + * IMX6 Clock Control Module + * + * Copyright (C) 2012 NICTA + * Updated by Jean-Christophe Dubois + * + * 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 IMX6_CCM_H +#define IMX6_CCM_H + +#include "hw/misc/imx_ccm.h" +#include "qemu/bitops.h" + +#define CCM_CCR 0 +#define CCM_CCDR 1 +#define CCM_CSR 2 +#define CCM_CCSR 3 +#define CCM_CACRR 4 +#define CCM_CBCDR 5 +#define CCM_CBCMR 6 +#define CCM_CSCMR1 7 +#define CCM_CSCMR2 8 +#define CCM_CSCDR1 9 +#define CCM_CS1CDR 10 +#define CCM_CS2CDR 11 +#define CCM_CDCDR 12 +#define CCM_CHSCCDR 13 +#define CCM_CSCDR2 14 +#define CCM_CSCDR3 15 +#define CCM_CDHIPR 18 +#define CCM_CTOR 20 +#define CCM_CLPCR 21 +#define CCM_CISR 22 +#define CCM_CIMR 23 +#define CCM_CCOSR 24 +#define CCM_CGPR 25 +#define CCM_CCGR0 26 +#define CCM_CCGR1 27 +#define CCM_CCGR2 28 +#define CCM_CCGR3 29 +#define CCM_CCGR4 30 +#define CCM_CCGR5 31 +#define CCM_CCGR6 32 +#define CCM_CMEOR 34 +#define CCM_MAX 35 + +#define CCM_ANALOG_PLL_ARM 0 +#define CCM_ANALOG_PLL_ARM_SET 1 +#define CCM_ANALOG_PLL_ARM_CLR 2 +#define CCM_ANALOG_PLL_ARM_TOG 3 +#define CCM_ANALOG_PLL_USB1 4 +#define CCM_ANALOG_PLL_USB1_SET 5 +#define CCM_ANALOG_PLL_USB1_CLR 6 +#define CCM_ANALOG_PLL_USB1_TOG 7 +#define CCM_ANALOG_PLL_USB2 8 +#define CCM_ANALOG_PLL_USB2_SET 9 +#define CCM_ANALOG_PLL_USB2_CLR 10 +#define CCM_ANALOG_PLL_USB2_TOG 11 +#define CCM_ANALOG_PLL_SYS 12 +#define CCM_ANALOG_PLL_SYS_SET 13 +#define CCM_ANALOG_PLL_SYS_CLR 14 +#define CCM_ANALOG_PLL_SYS_TOG 15 +#define CCM_ANALOG_PLL_SYS_SS 16 +#define CCM_ANALOG_PLL_SYS_NUM 20 +#define CCM_ANALOG_PLL_SYS_DENOM 24 +#define CCM_ANALOG_PLL_AUDIO 28 +#define CCM_ANALOG_PLL_AUDIO_SET 29 +#define CCM_ANALOG_PLL_AUDIO_CLR 30 +#define CCM_ANALOG_PLL_AUDIO_TOG 31 +#define CCM_ANALOG_PLL_AUDIO_NUM 32 +#define CCM_ANALOG_PLL_AUDIO_DENOM 36 +#define CCM_ANALOG_PLL_VIDEO 40 +#define CCM_ANALOG_PLL_VIDEO_SET 41 +#define CCM_ANALOG_PLL_VIDEO_CLR 42 +#define CCM_ANALOG_PLL_VIDEO_TOG 44 +#define CCM_ANALOG_PLL_VIDEO_NUM 46 +#define CCM_ANALOG_PLL_VIDEO_DENOM 48 +#define CCM_ANALOG_PLL_MLB 52 +#define CCM_ANALOG_PLL_MLB_SET 53 +#define CCM_ANALOG_PLL_MLB_CLR 54 +#define CCM_ANALOG_PLL_MLB_TOG 55 +#define CCM_ANALOG_PLL_ENET 56 +#define CCM_ANALOG_PLL_ENET_SET 57 +#define CCM_ANALOG_PLL_ENET_CLR 58 +#define CCM_ANALOG_PLL_ENET_TOG 59 +#define CCM_ANALOG_PFD_480 60 +#define CCM_ANALOG_PFD_480_SET 61 +#define CCM_ANALOG_PFD_480_CLR 62 +#define CCM_ANALOG_PFD_480_TOG 63 +#define CCM_ANALOG_PFD_528 64 +#define CCM_ANALOG_PFD_528_SET 65 +#define CCM_ANALOG_PFD_528_CLR 66 +#define CCM_ANALOG_PFD_528_TOG 67 + +/* PMU registers */ +#define PMU_REG_1P1 68 +#define PMU_REG_3P0 72 +#define PMU_REG_2P5 76 +#define PMU_REG_CORE 80 + +#define CCM_ANALOG_MISC0 84 +#define PMU_MISC0 84 +#define CCM_ANALOG_MISC0_SET 85 +#define CCM_ANALOG_MISC0_CLR 86 +#define CCM_ANALOG_MISC0_TOG 87 + +#define PMU_MISC1 88 +#define PMU_MISC1_SET 89 +#define PMU_MISC1_CLR 90 +#define PMU_MISC1_TOG 91 + +#define CCM_ANALOG_MISC2 92 +#define PMU_MISC2 92 +#define CCM_ANALOG_MISC2_SET 93 +#define CCM_ANALOG_MISC2_CLR 94 +#define CCM_ANALOG_MISC2_TOG 95 + +#define USB_ANALOG_USB1_VBUS_DETECT 104 +#define USB_ANALOG_USB1_VBUS_DETECT_SET 105 +#define USB_ANALOG_USB1_VBUS_DETECT_CLR 106 +#define USB_ANALOG_USB1_VBUS_DETECT_TOG 107 +#define USB_ANALOG_USB1_CHRG_DETECT 108 +#define USB_ANALOG_USB1_CHRG_DETECT_SET 109 +#define USB_ANALOG_USB1_CHRG_DETECT_CLR 110 +#define USB_ANALOG_USB1_CHRG_DETECT_TOG 111 +#define USB_ANALOG_USB1_VBUS_DETECT_STAT 112 +#define USB_ANALOG_USB1_CHRG_DETECT_STAT 116 +#define USB_ANALOG_USB1_MISC 124 +#define USB_ANALOG_USB1_MISC_SET 125 +#define USB_ANALOG_USB1_MISC_CLR 126 +#define USB_ANALOG_USB1_MISC_TOG 127 +#define USB_ANALOG_USB2_VBUS_DETECT 128 +#define USB_ANALOG_USB2_VBUS_DETECT_SET 129 +#define USB_ANALOG_USB2_VBUS_DETECT_CLR 130 +#define USB_ANALOG_USB2_VBUS_DETECT_TOG 131 +#define USB_ANALOG_USB2_CHRG_DETECT 132 +#define USB_ANALOG_USB2_CHRG_DETECT_SET 133 +#define USB_ANALOG_USB2_CHRG_DETECT_CLR 134 +#define USB_ANALOG_USB2_CHRG_DETECT_TOG 135 +#define USB_ANALOG_USB2_VBUS_DETECT_STAT 136 +#define USB_ANALOG_USB2_CHRG_DETECT_STAT 140 +#define USB_ANALOG_USB2_MISC 148 +#define USB_ANALOG_USB2_MISC_SET 149 +#define USB_ANALOG_USB2_MISC_CLR 150 +#define USB_ANALOG_USB2_MISC_TOG 151 +#define USB_ANALOG_DIGPROG 152 +#define CCM_ANALOG_MAX 153 + +/* CCM_CBCMR */ +#define PRE_PERIPH_CLK_SEL_SHIFT (18) +#define PRE_PERIPH_CLK_SEL_LENGTH (2) + +/* CCM_CBCDR */ +#define AHB_PODF_SHIFT (10) +#define AHB_PODF_LENGTH (3) +#define IPG_PODF_SHIFT (8) +#define IPG_PODF_LENGTH (2) + +/* CCM_CSCMR1 */ +#define PERCLK_PODF_SHIFT (0) +#define PERCLK_PODF_LENGTH (6) + +/* CCM_ANALOG_PFD_528 */ +#define PFD0_FRAC_SHIFT (0) +#define PFD0_FRAC_LENGTH (6) +#define PFD2_FRAC_SHIFT (16) +#define PFD2_FRAC_LENGTH (6) + +/* CCM_ANALOG_PLL_SYS */ +#define DIV_SELECT_SHIFT (0) +#define DIV_SELECT_LENGTH (1) + +#define CCM_ANALOG_PLL_LOCK (1 << 31); + +#define EXTRACT(value, name) extract32(value, name##_SHIFT, name##_LENGTH) + +#define TYPE_IMX6_CCM "imx6.ccm" +#define IMX6_CCM(obj) OBJECT_CHECK(IMX6CCMState, (obj), TYPE_IMX6_CCM) + +typedef struct IMX6CCMState { + /* */ + IMXCCMState parent_obj; + + /* */ + MemoryRegion container; + MemoryRegion ioccm; + MemoryRegion ioanalog; + + uint32_t ccm[CCM_MAX]; + uint32_t analog[CCM_ANALOG_MAX]; + +} IMX6CCMState; + +#endif /* IMX6_CCM_H */ diff --git a/qemu/include/hw/misc/imx_ccm.h b/qemu/include/hw/misc/imx_ccm.h new file mode 100644 index 000000000..48a7afad5 --- /dev/null +++ b/qemu/include/hw/misc/imx_ccm.h @@ -0,0 +1,64 @@ +/* + * IMX Clock Control Module base class + * + * Copyright (C) 2012 NICTA + * Updated by Jean-Christophe Dubois + * + * 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 IMX_CCM_H +#define IMX_CCM_H + +#include "hw/sysbus.h" + +#define CKIL_FREQ 32768 /* nominal 32khz clock */ + +/* PLL control registers */ +#define PD(v) (((v) >> 26) & 0xf) +#define MFD(v) (((v) >> 16) & 0x3ff) +#define MFI(v) (((v) >> 10) & 0xf); +#define MFN(v) ((v) & 0x3ff) + +#define PLL_PD(x) (((x) & 0xf) << 26) +#define PLL_MFD(x) (((x) & 0x3ff) << 16) +#define PLL_MFI(x) (((x) & 0xf) << 10) +#define PLL_MFN(x) (((x) & 0x3ff) << 0) + +#define TYPE_IMX_CCM "imx.ccm" +#define IMX_CCM(obj) \ + OBJECT_CHECK(IMXCCMState, (obj), TYPE_IMX_CCM) +#define IMX_CCM_CLASS(klass) \ + OBJECT_CLASS_CHECK(IMXCCMClass, (klass), TYPE_IMX_CCM) +#define IMX_GET_CLASS(obj) \ + OBJECT_GET_CLASS(IMXCCMClass, (obj), TYPE_IMX_CCM) + +typedef struct IMXCCMState { + /* */ + SysBusDevice parent_obj; + + /* */ + +} IMXCCMState; + +typedef enum { + CLK_NONE, + CLK_IPG, + CLK_IPG_HIGH, + CLK_32k +} IMXClk; + +typedef struct IMXCCMClass { + /* */ + SysBusDeviceClass parent_class; + + /* */ + uint32_t (*get_clock_frequency)(IMXCCMState *s, IMXClk clk); +} IMXCCMClass; + +uint32_t imx_ccm_calc_pll(uint32_t pllreg, uint32_t base_freq); + +uint32_t imx_ccm_get_clock_frequency(IMXCCMState *s, IMXClk clock); + +#endif /* IMX_CCM_H */ diff --git a/qemu/include/hw/misc/ivshmem.h b/qemu/include/hw/misc/ivshmem.h new file mode 100644 index 000000000..433ef53d7 --- /dev/null +++ b/qemu/include/hw/misc/ivshmem.h @@ -0,0 +1,25 @@ + +/* + * Inter-VM Shared Memory PCI device. + * + * Author: + * Cam Macdonell + * + * Based On: cirrus_vga.c + * Copyright (c) 2004 Fabrice Bellard + * Copyright (c) 2004 Makoto Suzuki (suzu) + * + * and rtl8139.c + * Copyright (c) 2006 Igor Kovalenko + * + * This code is licensed under the GNU GPL v2. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ +#ifndef IVSHMEM_H +#define IVSHMEM_H + +#define IVSHMEM_PROTOCOL_VERSION 0 + +#endif /* IVSHMEM_H */ diff --git a/qemu/include/hw/misc/mips_cmgcr.h b/qemu/include/hw/misc/mips_cmgcr.h new file mode 100644 index 000000000..cc60eefa5 --- /dev/null +++ b/qemu/include/hw/misc/mips_cmgcr.h @@ -0,0 +1,59 @@ +/* + * 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) 2015 Imagination Technologies + * + */ + +#ifndef _MIPS_GCR_H +#define _MIPS_GCR_H + +#define TYPE_MIPS_GCR "mips-gcr" +#define MIPS_GCR(obj) OBJECT_CHECK(MIPSGCRState, (obj), TYPE_MIPS_GCR) + +#define GCR_BASE_ADDR 0x1fbf8000ULL +#define GCR_ADDRSPACE_SZ 0x8000 + +/* Offsets to register blocks */ +#define MIPS_GCB_OFS 0x0000 /* Global Control Block */ +#define MIPS_CLCB_OFS 0x2000 /* Core Local Control Block */ +#define MIPS_COCB_OFS 0x4000 /* Core Other Control Block */ +#define MIPS_GDB_OFS 0x6000 /* Global Debug Block */ + +/* Global Control Block Register Map */ +#define GCR_CONFIG_OFS 0x0000 +#define GCR_BASE_OFS 0x0008 +#define GCR_REV_OFS 0x0030 +#define GCR_CPC_BASE_OFS 0x0088 +#define GCR_CPC_STATUS_OFS 0x00F0 +#define GCR_L2_CONFIG_OFS 0x0130 + +/* Core Local and Core Other Block Register Map */ +#define GCR_CL_CONFIG_OFS 0x0010 +#define GCR_CL_OTHER_OFS 0x0018 + +/* GCR_L2_CONFIG register fields */ +#define GCR_L2_CONFIG_BYPASS_SHF 20 +#define GCR_L2_CONFIG_BYPASS_MSK ((0x1ULL) << GCR_L2_CONFIG_BYPASS_SHF) + +/* GCR_CPC_BASE register fields */ +#define GCR_CPC_BASE_CPCEN_MSK 1 +#define GCR_CPC_BASE_CPCBASE_MSK 0xFFFFFFFF8000ULL +#define GCR_CPC_BASE_MSK (GCR_CPC_BASE_CPCEN_MSK | GCR_CPC_BASE_CPCBASE_MSK) + +typedef struct MIPSGCRState MIPSGCRState; +struct MIPSGCRState { + SysBusDevice parent_obj; + + int32_t gcr_rev; + int32_t num_vps; + hwaddr gcr_base; + MemoryRegion iomem; + MemoryRegion *cpc_mr; + + uint64_t cpc_base; +}; + +#endif /* _MIPS_GCR_H */ diff --git a/qemu/include/hw/misc/mips_cpc.h b/qemu/include/hw/misc/mips_cpc.h new file mode 100644 index 000000000..72c834e03 --- /dev/null +++ b/qemu/include/hw/misc/mips_cpc.h @@ -0,0 +1,47 @@ +/* + * Cluster Power Controller emulation + * + * Copyright (c) 2016 Imagination Technologies + * + * 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 . + */ + +#ifndef MIPS_CPC_H +#define MIPS_CPC_H + +#define CPC_ADDRSPACE_SZ 0x6000 + +/* CPC blocks offsets relative to base address */ +#define CPC_CL_BASE_OFS 0x2000 +#define CPC_CO_BASE_OFS 0x4000 + +/* CPC register offsets relative to block offsets */ +#define CPC_VP_STOP_OFS 0x20 +#define CPC_VP_RUN_OFS 0x28 +#define CPC_VP_RUNNING_OFS 0x30 + +#define TYPE_MIPS_CPC "mips-cpc" +#define MIPS_CPC(obj) OBJECT_CHECK(MIPSCPCState, (obj), TYPE_MIPS_CPC) + +typedef struct MIPSCPCState { + SysBusDevice parent_obj; + + uint32_t num_vp; + uint64_t vp_start_running; /* VPs running from restart */ + + MemoryRegion mr; + uint64_t vp_running; /* Indicates which VPs are in the run state */ +} MIPSCPCState; + +#endif /* MIPS_CPC_H */ diff --git a/qemu/include/hw/misc/mips_itu.h b/qemu/include/hw/misc/mips_itu.h new file mode 100644 index 000000000..b3a453203 --- /dev/null +++ b/qemu/include/hw/misc/mips_itu.h @@ -0,0 +1,72 @@ +/* + * Inter-Thread Communication Unit emulation. + * + * Copyright (c) 2016 Imagination Technologies + * + * 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 . + */ + +#ifndef MIPS_ITU_H +#define MIPS_ITU_H + +#define TYPE_MIPS_ITU "mips-itu" +#define MIPS_ITU(obj) OBJECT_CHECK(MIPSITUState, (obj), TYPE_MIPS_ITU) + +#define ITC_CELL_DEPTH_SHIFT 2 +#define ITC_CELL_DEPTH (1u << ITC_CELL_DEPTH_SHIFT) + +typedef struct ITCStorageCell { + struct { + uint8_t FIFODepth; /* Log2 of the cell depth */ + uint8_t FIFOPtr; /* Number of elements in a FIFO cell */ + uint8_t FIFO; /* 1 - FIFO cell, 0 - Semaphore cell */ + uint8_t T; /* Trap Bit */ + uint8_t F; /* Full Bit */ + uint8_t E; /* Empty Bit */ + } tag; + + /* Index of the oldest element in the queue */ + uint8_t fifo_out; + + /* Circular buffer for FIFO. Semaphore cells use index 0 only */ + uint64_t data[ITC_CELL_DEPTH]; + + /* Bitmap tracking blocked threads on the cell. + TODO: support >64 threads ? */ + uint64_t blocked_threads; +} ITCStorageCell; + +#define ITC_ADDRESSMAP_NUM 2 + +typedef struct MIPSITUState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + int32_t num_fifo; + int32_t num_semaphores; + + /* ITC Storage */ + ITCStorageCell *cell; + MemoryRegion storage_io; + + /* ITC Configuration Tags */ + uint64_t ITCAddressMap[ITC_ADDRESSMAP_NUM]; + MemoryRegion tag_io; +} MIPSITUState; + +/* Get ITC Configuration Tag memory region. */ +MemoryRegion *mips_itu_get_tag_region(MIPSITUState *itu); + +#endif /* MIPS_ITU_H */ diff --git a/qemu/include/hw/misc/zynq-xadc.h b/qemu/include/hw/misc/zynq-xadc.h new file mode 100644 index 000000000..f1a410a37 --- /dev/null +++ b/qemu/include/hw/misc/zynq-xadc.h @@ -0,0 +1,46 @@ +/* + * Device model for Zynq ADC controller + * + * Copyright (c) 2015 Guenter Roeck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef ZYNQ_XADC_H +#define ZYNQ_XADC_H + +#include "hw/sysbus.h" + +#define ZYNQ_XADC_MMIO_SIZE 0x0020 +#define ZYNQ_XADC_NUM_IO_REGS (ZYNQ_XADC_MMIO_SIZE / 4) +#define ZYNQ_XADC_NUM_ADC_REGS 128 +#define ZYNQ_XADC_FIFO_DEPTH 15 + +#define TYPE_ZYNQ_XADC "xlnx,zynq-xadc" +#define ZYNQ_XADC(obj) \ + OBJECT_CHECK(ZynqXADCState, (obj), TYPE_ZYNQ_XADC) + +typedef struct ZynqXADCState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + + uint32_t regs[ZYNQ_XADC_NUM_IO_REGS]; + uint16_t xadc_regs[ZYNQ_XADC_NUM_ADC_REGS]; + uint16_t xadc_read_reg_previous; + uint16_t xadc_dfifo[ZYNQ_XADC_FIFO_DEPTH]; + uint16_t xadc_dfifo_entries; + + struct IRQState *qemu_irq; + +} ZynqXADCState; + +#endif /* ZYNQ_XADC_H */ diff --git a/qemu/include/hw/net/imx_fec.h b/qemu/include/hw/net/imx_fec.h new file mode 100644 index 000000000..cbf86509e --- /dev/null +++ b/qemu/include/hw/net/imx_fec.h @@ -0,0 +1,113 @@ +/* + * i.MX Fast Ethernet Controller emulation. + * + * Copyright (c) 2013 Jean-Christophe Dubois. + * + * Based on Coldfire Fast Ethernet Controller emulation. + * + * Copyright (c) 2007 CodeSourcery. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef IMX_FEC_H +#define IMX_FEC_H + +#define TYPE_IMX_FEC "imx.fec" +#define IMX_FEC(obj) OBJECT_CHECK(IMXFECState, (obj), TYPE_IMX_FEC) + +#include "hw/sysbus.h" +#include "net/net.h" + +#define FEC_MAX_FRAME_SIZE 2032 + +#define FEC_INT_HB (1 << 31) +#define FEC_INT_BABR (1 << 30) +#define FEC_INT_BABT (1 << 29) +#define FEC_INT_GRA (1 << 28) +#define FEC_INT_TXF (1 << 27) +#define FEC_INT_TXB (1 << 26) +#define FEC_INT_RXF (1 << 25) +#define FEC_INT_RXB (1 << 24) +#define FEC_INT_MII (1 << 23) +#define FEC_INT_EBERR (1 << 22) +#define FEC_INT_LC (1 << 21) +#define FEC_INT_RL (1 << 20) +#define FEC_INT_UN (1 << 19) + +#define FEC_EN 2 +#define FEC_RESET 1 + +/* Buffer Descriptor. */ +typedef struct { + uint16_t length; + uint16_t flags; + uint32_t data; +} IMXFECBufDesc; + +#define FEC_BD_R (1 << 15) +#define FEC_BD_E (1 << 15) +#define FEC_BD_O1 (1 << 14) +#define FEC_BD_W (1 << 13) +#define FEC_BD_O2 (1 << 12) +#define FEC_BD_L (1 << 11) +#define FEC_BD_TC (1 << 10) +#define FEC_BD_ABC (1 << 9) +#define FEC_BD_M (1 << 8) +#define FEC_BD_BC (1 << 7) +#define FEC_BD_MC (1 << 6) +#define FEC_BD_LG (1 << 5) +#define FEC_BD_NO (1 << 4) +#define FEC_BD_CR (1 << 2) +#define FEC_BD_OV (1 << 1) +#define FEC_BD_TR (1 << 0) + +typedef struct IMXFECState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + NICState *nic; + NICConf conf; + qemu_irq irq; + MemoryRegion iomem; + + uint32_t irq_state; + uint32_t eir; + uint32_t eimr; + uint32_t rx_enabled; + uint32_t rx_descriptor; + uint32_t tx_descriptor; + uint32_t ecr; + uint32_t mmfr; + uint32_t mscr; + uint32_t mibc; + uint32_t rcr; + uint32_t tcr; + uint32_t tfwr; + uint32_t frsr; + uint32_t erdsr; + uint32_t etdsr; + uint32_t emrbr; + uint32_t miigsk_cfgr; + uint32_t miigsk_enr; + + uint32_t phy_status; + uint32_t phy_control; + uint32_t phy_advertise; + uint32_t phy_int; + uint32_t phy_int_mask; +} IMXFECState; + +#endif diff --git a/qemu/include/hw/nvram/fw_cfg.h b/qemu/include/hw/nvram/fw_cfg.h index e60d3ca21..d00811258 100644 --- a/qemu/include/hw/nvram/fw_cfg.h +++ b/qemu/include/hw/nvram/fw_cfg.h @@ -1,54 +1,9 @@ #ifndef FW_CFG_H #define FW_CFG_H -#ifndef NO_QEMU_PROTOS -#include -#include - #include "exec/hwaddr.h" -#include "qemu/typedefs.h" -#endif +#include "hw/nvram/fw_cfg_keys.h" -#define FW_CFG_SIGNATURE 0x00 -#define FW_CFG_ID 0x01 -#define FW_CFG_UUID 0x02 -#define FW_CFG_RAM_SIZE 0x03 -#define FW_CFG_NOGRAPHIC 0x04 -#define FW_CFG_NB_CPUS 0x05 -#define FW_CFG_MACHINE_ID 0x06 -#define FW_CFG_KERNEL_ADDR 0x07 -#define FW_CFG_KERNEL_SIZE 0x08 -#define FW_CFG_KERNEL_CMDLINE 0x09 -#define FW_CFG_INITRD_ADDR 0x0a -#define FW_CFG_INITRD_SIZE 0x0b -#define FW_CFG_BOOT_DEVICE 0x0c -#define FW_CFG_NUMA 0x0d -#define FW_CFG_BOOT_MENU 0x0e -#define FW_CFG_MAX_CPUS 0x0f -#define FW_CFG_KERNEL_ENTRY 0x10 -#define FW_CFG_KERNEL_DATA 0x11 -#define FW_CFG_INITRD_DATA 0x12 -#define FW_CFG_CMDLINE_ADDR 0x13 -#define FW_CFG_CMDLINE_SIZE 0x14 -#define FW_CFG_CMDLINE_DATA 0x15 -#define FW_CFG_SETUP_ADDR 0x16 -#define FW_CFG_SETUP_SIZE 0x17 -#define FW_CFG_SETUP_DATA 0x18 -#define FW_CFG_FILE_DIR 0x19 - -#define FW_CFG_FILE_FIRST 0x20 -#define FW_CFG_FILE_SLOTS 0x10 -#define FW_CFG_MAX_ENTRY (FW_CFG_FILE_FIRST+FW_CFG_FILE_SLOTS) - -#define FW_CFG_WRITE_CHANNEL 0x4000 -#define FW_CFG_ARCH_LOCAL 0x8000 -#define FW_CFG_ENTRY_MASK ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL) - -#define FW_CFG_INVALID 0xffff - -#define FW_CFG_MAX_FILE_PATH 56 - -#ifndef NO_QEMU_PROTOS typedef struct FWCfgFile { uint32_t size; /* file size */ uint16_t select; /* write this to 0x510 to read it */ @@ -56,34 +11,176 @@ typedef struct FWCfgFile { char name[FW_CFG_MAX_FILE_PATH]; } FWCfgFile; +#define FW_CFG_ORDER_OVERRIDE_VGA 70 +#define FW_CFG_ORDER_OVERRIDE_NIC 80 +#define FW_CFG_ORDER_OVERRIDE_USER 100 +#define FW_CFG_ORDER_OVERRIDE_DEVICE 110 + +void fw_cfg_set_order_override(FWCfgState *fw_cfg, int order); +void fw_cfg_reset_order_override(FWCfgState *fw_cfg); + typedef struct FWCfgFiles { uint32_t count; FWCfgFile f[]; } FWCfgFiles; -typedef void (*FWCfgCallback)(void *opaque, uint8_t *data); -typedef void (*FWCfgReadCallback)(void *opaque, uint32_t offset); +/* Control as first field allows for different structures selected by this + * field, which might be useful in the future + */ +typedef struct FWCfgDmaAccess { + uint32_t control; + uint32_t length; + uint64_t address; +} QEMU_PACKED FWCfgDmaAccess; +typedef void (*FWCfgReadCallback)(void *opaque); + +/** + * fw_cfg_add_bytes: + * @s: fw_cfg device being modified + * @key: selector key value for new fw_cfg item + * @data: pointer to start of item data + * @len: size of item data + * + * Add a new fw_cfg item, available by selecting the given key, as a raw + * "blob" of the given size. The data referenced by the starting pointer + * is only linked, NOT copied, into the data structure of the fw_cfg device. + */ void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len); + +/** + * fw_cfg_add_string: + * @s: fw_cfg device being modified + * @key: selector key value for new fw_cfg item + * @value: NUL-terminated ascii string + * + * Add a new fw_cfg item, available by selecting the given key. The item + * data will consist of a dynamically allocated copy of the provided string, + * including its NUL terminator. + */ void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value); + +/** + * fw_cfg_add_i16: + * @s: fw_cfg device being modified + * @key: selector key value for new fw_cfg item + * @value: 16-bit integer + * + * Add a new fw_cfg item, available by selecting the given key. The item + * data will consist of a dynamically allocated copy of the given 16-bit + * value, converted to little-endian representation. + */ void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value); + +/** + * fw_cfg_modify_i16: + * @s: fw_cfg device being modified + * @key: selector key value for new fw_cfg item + * @value: 16-bit integer + * + * Replace the fw_cfg item available by selecting the given key. The new + * data will consist of a dynamically allocated copy of the given 16-bit + * value, converted to little-endian representation. The data being replaced, + * assumed to have been dynamically allocated during an earlier call to + * either fw_cfg_add_i16() or fw_cfg_modify_i16(), is freed before returning. + */ void fw_cfg_modify_i16(FWCfgState *s, uint16_t key, uint16_t value); + +/** + * fw_cfg_add_i32: + * @s: fw_cfg device being modified + * @key: selector key value for new fw_cfg item + * @value: 32-bit integer + * + * Add a new fw_cfg item, available by selecting the given key. The item + * data will consist of a dynamically allocated copy of the given 32-bit + * value, converted to little-endian representation. + */ void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value); + +/** + * fw_cfg_add_i64: + * @s: fw_cfg device being modified + * @key: selector key value for new fw_cfg item + * @value: 64-bit integer + * + * Add a new fw_cfg item, available by selecting the given key. The item + * data will consist of a dynamically allocated copy of the given 64-bit + * value, converted to little-endian representation. + */ void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value); + +/** + * fw_cfg_add_file: + * @s: fw_cfg device being modified + * @filename: name of new fw_cfg file item + * @data: pointer to start of item data + * @len: size of item data + * + * Add a new NAMED fw_cfg item as a raw "blob" of the given size. The data + * referenced by the starting pointer is only linked, NOT copied, into the + * data structure of the fw_cfg device. + * The next available (unused) selector key starting at FW_CFG_FILE_FIRST + * will be used; also, a new entry will be added to the file directory + * structure residing at key value FW_CFG_FILE_DIR, containing the item name, + * data size, and assigned selector key value. + */ void fw_cfg_add_file(FWCfgState *s, const char *filename, void *data, size_t len); + +/** + * fw_cfg_add_file_callback: + * @s: fw_cfg device being modified + * @filename: name of new fw_cfg file item + * @callback: callback function + * @callback_opaque: argument to be passed into callback function + * @data: pointer to start of item data + * @len: size of item data + * + * Add a new NAMED fw_cfg item as a raw "blob" of the given size. The data + * referenced by the starting pointer is only linked, NOT copied, into the + * data structure of the fw_cfg device. + * The next available (unused) selector key starting at FW_CFG_FILE_FIRST + * will be used; also, a new entry will be added to the file directory + * structure residing at key value FW_CFG_FILE_DIR, containing the item name, + * data size, and assigned selector key value. + * Additionally, set a callback function (and argument) to be called each + * time this item is selected (by having its selector key either written to + * the fw_cfg control register, or passed to QEMU in FWCfgDmaAccess.control + * with FW_CFG_DMA_CTL_SELECT). + */ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, FWCfgReadCallback callback, void *callback_opaque, void *data, size_t len); + +/** + * fw_cfg_modify_file: + * @s: fw_cfg device being modified + * @filename: name of new fw_cfg file item + * @data: pointer to start of item data + * @len: size of item data + * + * Replace a NAMED fw_cfg item. If an existing item is found, its callback + * information will be cleared, and a pointer to its data will be returned + * to the caller, so that it may be freed if necessary. If an existing item + * is not found, this call defaults to fw_cfg_add_file(), and NULL is + * returned to the caller. + * In either case, the new item data is only linked, NOT copied, into the + * data structure of the fw_cfg device. + * + * Returns: pointer to old item's data, or NULL if old item does not exist. + */ void *fw_cfg_modify_file(FWCfgState *s, const char *filename, void *data, size_t len); + +FWCfgState *fw_cfg_init_io_dma(uint32_t iobase, uint32_t dma_iobase, + AddressSpace *dma_as); FWCfgState *fw_cfg_init_io(uint32_t iobase); FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr); -FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, hwaddr data_addr, - uint32_t data_width); +FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, + hwaddr data_addr, uint32_t data_width, + hwaddr dma_addr, AddressSpace *dma_as); FWCfgState *fw_cfg_find(void); -#endif /* NO_QEMU_PROTOS */ - #endif diff --git a/qemu/include/hw/nvram/fw_cfg_keys.h b/qemu/include/hw/nvram/fw_cfg_keys.h new file mode 100644 index 000000000..0f3e87188 --- /dev/null +++ b/qemu/include/hw/nvram/fw_cfg_keys.h @@ -0,0 +1,46 @@ +#ifndef FW_CFG_KEYS_H +#define FW_CFG_KEYS_H + +#define FW_CFG_SIGNATURE 0x00 +#define FW_CFG_ID 0x01 +#define FW_CFG_UUID 0x02 +#define FW_CFG_RAM_SIZE 0x03 +#define FW_CFG_NOGRAPHIC 0x04 +#define FW_CFG_NB_CPUS 0x05 +#define FW_CFG_MACHINE_ID 0x06 +#define FW_CFG_KERNEL_ADDR 0x07 +#define FW_CFG_KERNEL_SIZE 0x08 +#define FW_CFG_KERNEL_CMDLINE 0x09 +#define FW_CFG_INITRD_ADDR 0x0a +#define FW_CFG_INITRD_SIZE 0x0b +#define FW_CFG_BOOT_DEVICE 0x0c +#define FW_CFG_NUMA 0x0d +#define FW_CFG_BOOT_MENU 0x0e +#define FW_CFG_MAX_CPUS 0x0f +#define FW_CFG_KERNEL_ENTRY 0x10 +#define FW_CFG_KERNEL_DATA 0x11 +#define FW_CFG_INITRD_DATA 0x12 +#define FW_CFG_CMDLINE_ADDR 0x13 +#define FW_CFG_CMDLINE_SIZE 0x14 +#define FW_CFG_CMDLINE_DATA 0x15 +#define FW_CFG_SETUP_ADDR 0x16 +#define FW_CFG_SETUP_SIZE 0x17 +#define FW_CFG_SETUP_DATA 0x18 +#define FW_CFG_FILE_DIR 0x19 + +#define FW_CFG_FILE_FIRST 0x20 +#define FW_CFG_FILE_SLOTS 0x10 +#define FW_CFG_MAX_ENTRY (FW_CFG_FILE_FIRST + FW_CFG_FILE_SLOTS) + +#define FW_CFG_WRITE_CHANNEL 0x4000 +#define FW_CFG_ARCH_LOCAL 0x8000 +#define FW_CFG_ENTRY_MASK (~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)) + +#define FW_CFG_INVALID 0xffff + +/* width in bytes of fw_cfg control register */ +#define FW_CFG_CTL_SIZE 0x02 + +#define FW_CFG_MAX_FILE_PATH 56 + +#endif diff --git a/qemu/include/hw/pci-host/q35.h b/qemu/include/hw/pci-host/q35.h index dbe6dc05b..c5c073dde 100644 --- a/qemu/include/hw/pci-host/q35.h +++ b/qemu/include/hw/pci-host/q35.h @@ -59,7 +59,6 @@ typedef struct MCHPCIState { ram_addr_t below_4g_mem_size; ram_addr_t above_4g_mem_size; uint64_t pci_hole64_size; - PcGuestInfo *guest_info; uint32_t short_root_bus; IntelIOMMUState *iommu; } MCHPCIState; diff --git a/qemu/include/hw/pci-host/spapr.h b/qemu/include/hw/pci-host/spapr.h index 5322b560e..03ee00640 100644 --- a/qemu/include/hw/pci-host/spapr.h +++ b/qemu/include/hw/pci-host/spapr.h @@ -28,32 +28,11 @@ #include "hw/ppc/xics.h" #define TYPE_SPAPR_PCI_HOST_BRIDGE "spapr-pci-host-bridge" -#define TYPE_SPAPR_PCI_VFIO_HOST_BRIDGE "spapr-pci-vfio-host-bridge" #define SPAPR_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(sPAPRPHBState, (obj), TYPE_SPAPR_PCI_HOST_BRIDGE) -#define SPAPR_PCI_VFIO_HOST_BRIDGE(obj) \ - OBJECT_CHECK(sPAPRPHBVFIOState, (obj), TYPE_SPAPR_PCI_VFIO_HOST_BRIDGE) - -#define SPAPR_PCI_HOST_BRIDGE_CLASS(klass) \ - OBJECT_CLASS_CHECK(sPAPRPHBClass, (klass), TYPE_SPAPR_PCI_HOST_BRIDGE) -#define SPAPR_PCI_HOST_BRIDGE_GET_CLASS(obj) \ - OBJECT_GET_CLASS(sPAPRPHBClass, (obj), TYPE_SPAPR_PCI_HOST_BRIDGE) - -typedef struct sPAPRPHBClass sPAPRPHBClass; typedef struct sPAPRPHBState sPAPRPHBState; -typedef struct sPAPRPHBVFIOState sPAPRPHBVFIOState; - -struct sPAPRPHBClass { - PCIHostBridgeClass parent_class; - - void (*finish_realize)(sPAPRPHBState *sphb, Error **errp); - int (*eeh_set_option)(sPAPRPHBState *sphb, unsigned int addr, int option); - int (*eeh_get_state)(sPAPRPHBState *sphb, int *state); - int (*eeh_reset)(sPAPRPHBState *sphb, int option); - int (*eeh_configure)(sPAPRPHBState *sphb); -}; typedef struct spapr_pci_msi { uint32_t first_irq; @@ -78,6 +57,7 @@ struct sPAPRPHBState { MemoryRegion memwindow, iowindow, msiwindow; uint32_t dma_liobn; + hwaddr dma_win_addr, dma_win_size; AddressSpace iommu_as; MemoryRegion iommu_root; @@ -93,12 +73,6 @@ struct sPAPRPHBState { QLIST_ENTRY(sPAPRPHBState) list; }; -struct sPAPRPHBVFIOState { - sPAPRPHBState phb; - - int32_t iommugroupid; -}; - #define SPAPR_PCI_MAX_INDEX 255 #define SPAPR_PCI_BASE_BUID 0x800000020000000ULL @@ -115,8 +89,6 @@ struct sPAPRPHBVFIOState { #define SPAPR_PCI_MSI_WINDOW 0x40000000000ULL -#define SPAPR_PCI_DMA32_SIZE 0x40000000 - static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin) { sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); @@ -138,4 +110,41 @@ sPAPRPHBState *spapr_pci_find_phb(sPAPRMachineState *spapr, uint64_t buid); PCIDevice *spapr_pci_find_dev(sPAPRMachineState *spapr, uint64_t buid, uint32_t config_addr); +/* VFIO EEH hooks */ +#ifdef CONFIG_LINUX +bool spapr_phb_eeh_available(sPAPRPHBState *sphb); +int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb, + unsigned int addr, int option); +int spapr_phb_vfio_eeh_get_state(sPAPRPHBState *sphb, int *state); +int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option); +int spapr_phb_vfio_eeh_configure(sPAPRPHBState *sphb); +void spapr_phb_vfio_reset(DeviceState *qdev); +#else +static inline bool spapr_phb_eeh_available(sPAPRPHBState *sphb) +{ + return false; +} +static inline int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb, + unsigned int addr, int option) +{ + return RTAS_OUT_HW_ERROR; +} +static inline int spapr_phb_vfio_eeh_get_state(sPAPRPHBState *sphb, + int *state) +{ + return RTAS_OUT_HW_ERROR; +} +static inline int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option) +{ + return RTAS_OUT_HW_ERROR; +} +static inline int spapr_phb_vfio_eeh_configure(sPAPRPHBState *sphb) +{ + return RTAS_OUT_HW_ERROR; +} +static inline void spapr_phb_vfio_reset(DeviceState *qdev) +{ +} +#endif + #endif /* __HW_SPAPR_PCI_H__ */ diff --git a/qemu/include/hw/pci/msi.h b/qemu/include/hw/pci/msi.h index 50e452bd0..8124908ab 100644 --- a/qemu/include/hw/pci/msi.h +++ b/qemu/include/hw/pci/msi.h @@ -29,7 +29,7 @@ struct MSIMessage { uint32_t data; }; -extern bool msi_supported; +extern bool msi_nonbroken; void msi_set_message(PCIDevice *dev, MSIMessage msg); MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector); diff --git a/qemu/include/hw/pci/msix.h b/qemu/include/hw/pci/msix.h index 954d82b35..72e5f931c 100644 --- a/qemu/include/hw/pci/msix.h +++ b/qemu/include/hw/pci/msix.h @@ -46,12 +46,16 @@ void msix_unset_vector_notifiers(PCIDevice *dev); extern const VMStateDescription vmstate_msix; -#define VMSTATE_MSIX(_field, _state) { \ - .name = (stringify(_field)), \ - .size = sizeof(PCIDevice), \ - .vmsd = &vmstate_msix, \ - .flags = VMS_STRUCT, \ - .offset = vmstate_offset_value(_state, _field, PCIDevice), \ +#define VMSTATE_MSIX_TEST(_field, _state, _test) { \ + .name = (stringify(_field)), \ + .size = sizeof(PCIDevice), \ + .vmsd = &vmstate_msix, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, PCIDevice), \ + .field_exists = (_test) \ } +#define VMSTATE_MSIX(_f, _s) \ + VMSTATE_MSIX_TEST(_f, _s, NULL) + #endif diff --git a/qemu/include/hw/pci/pci-assign.h b/qemu/include/hw/pci/pci-assign.h new file mode 100644 index 000000000..55f42c56f --- /dev/null +++ b/qemu/include/hw/pci/pci-assign.h @@ -0,0 +1,27 @@ +/* + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Just split from hw/i386/kvm/pci-assign.c. + */ +#ifndef PCI_ASSIGN_H +#define PCI_ASSIGN_H + +#include "hw/pci/pci.h" + +//#define DEVICE_ASSIGNMENT_DEBUG + +#ifdef DEVICE_ASSIGNMENT_DEBUG +#define DEBUG(fmt, ...) \ + do { \ + fprintf(stderr, "%s: " fmt, __func__ , __VA_ARGS__); \ + } while (0) +#else +#define DEBUG(fmt, ...) +#endif + +void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner, + int *size, unsigned int domain, + unsigned int bus, unsigned int slot, + unsigned int function); +#endif /* PCI_ASSIGN_H */ diff --git a/qemu/include/hw/pci/pci.h b/qemu/include/hw/pci/pci.h index 551cb3d60..ef6ba51f6 100644 --- a/qemu/include/hw/pci/pci.h +++ b/qemu/include/hw/pci/pci.h @@ -1,12 +1,9 @@ #ifndef QEMU_PCI_H #define QEMU_PCI_H -#include "qemu-common.h" - #include "hw/qdev.h" #include "exec/memory.h" #include "sysemu/dma.h" -#include "qapi/error.h" /* PCI includes legacy ISA access. */ #include "hw/isa/isa.h" @@ -93,10 +90,20 @@ #define PCI_DEVICE_ID_REDHAT_PCIE_HOST 0x0008 #define PCI_DEVICE_ID_REDHAT_PXB 0x0009 #define PCI_DEVICE_ID_REDHAT_BRIDGE_SEAT 0x000a +#define PCI_DEVICE_ID_REDHAT_PXB_PCIE 0x000b #define PCI_DEVICE_ID_REDHAT_QXL 0x0100 #define FMT_PCIBUS PRIx64 +typedef uint64_t pcibus_t; + +struct PCIHostDeviceAddress { + unsigned int domain; + unsigned int bus; + unsigned int slot; + unsigned int function; +}; + typedef void PCIConfigWriteFunc(PCIDevice *pci_dev, uint32_t address, uint32_t data, int len); typedef uint32_t PCIConfigReadFunc(PCIDevice *pci_dev, @@ -397,6 +404,7 @@ void pci_for_each_bus_depth_first(PCIBus *bus, void *(*begin)(PCIBus *bus, void *parent_state), void (*end)(PCIBus *bus, void *state), void *parent_state); +PCIDevice *pci_get_function_0(PCIDevice *pci_dev); /* Use this wrapper when specific scan order is not required. */ static inline @@ -677,6 +685,11 @@ static inline uint32_t pci_config_size(const PCIDevice *d) return pci_is_express(d) ? PCIE_CONFIG_SPACE_SIZE : PCI_CONFIG_SPACE_SIZE; } +static inline uint16_t pci_requester_id(PCIDevice *dev) +{ + return (pci_bus_num(dev->bus) << 8) | dev->devfn; +} + /* DMA access functions */ static inline AddressSpace *pci_get_address_space(PCIDevice *dev) { diff --git a/qemu/include/hw/pci/pci_bridge.h b/qemu/include/hw/pci/pci_bridge.h index 93b621cef..ed4aff6cd 100644 --- a/qemu/include/hw/pci/pci_bridge.h +++ b/qemu/include/hw/pci/pci_bridge.h @@ -48,7 +48,7 @@ void pci_bridge_disable_base_limit(PCIDevice *dev); void pci_bridge_reset_reg(PCIDevice *dev); void pci_bridge_reset(DeviceState *qdev); -int pci_bridge_initfn(PCIDevice *pci_dev, const char *typename); +void pci_bridge_initfn(PCIDevice *pci_dev, const char *typename); void pci_bridge_exitfn(PCIDevice *pci_dev); diff --git a/qemu/include/hw/pci/pci_ids.h b/qemu/include/hw/pci/pci_ids.h index d98e6c915..db85afa03 100644 --- a/qemu/include/hw/pci/pci_ids.h +++ b/qemu/include/hw/pci/pci_ids.h @@ -64,6 +64,7 @@ #define PCI_VENDOR_ID_LSI_LOGIC 0x1000 #define PCI_DEVICE_ID_LSI_53C810 0x0001 #define PCI_DEVICE_ID_LSI_53C895A 0x0012 +#define PCI_DEVICE_ID_LSI_SAS1068 0x0054 #define PCI_DEVICE_ID_LSI_SAS1078 0x0060 #define PCI_DEVICE_ID_LSI_SAS0079 0x0079 diff --git a/qemu/include/hw/pci/pcie_aer.h b/qemu/include/hw/pci/pcie_aer.h index 2fb83882b..c2ee4e2bd 100644 --- a/qemu/include/hw/pci/pcie_aer.h +++ b/qemu/include/hw/pci/pcie_aer.h @@ -87,7 +87,7 @@ struct PCIEAERErr { extern const VMStateDescription vmstate_pcie_aer_log; -int pcie_aer_init(PCIDevice *dev, uint16_t offset); +int pcie_aer_init(PCIDevice *dev, uint16_t offset, uint16_t size); void pcie_aer_exit(PCIDevice *dev); void pcie_aer_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len); @@ -102,5 +102,6 @@ void pcie_aer_root_write_config(PCIDevice *dev, /* error injection */ int pcie_aer_inject_error(PCIDevice *dev, const PCIEAERErr *err); +void pcie_aer_msg(PCIDevice *dev, const PCIEAERMsg *msg); #endif /* QEMU_PCIE_AER_H */ diff --git a/qemu/include/hw/pci/shpc.h b/qemu/include/hw/pci/shpc.h index 2c871b947..b2085543d 100644 --- a/qemu/include/hw/pci/shpc.h +++ b/qemu/include/hw/pci/shpc.h @@ -4,7 +4,6 @@ #include "qemu-common.h" #include "exec/memory.h" #include "migration/vmstate.h" -#include "qapi/error.h" #include "hw/hotplug.h" #include "hw/pci/pci.h" diff --git a/qemu/include/hw/ppc/mac_dbdma.h b/qemu/include/hw/ppc/mac_dbdma.h index c6870212e..0cce4e8bb 100644 --- a/qemu/include/hw/ppc/mac_dbdma.h +++ b/qemu/include/hw/ppc/mac_dbdma.h @@ -23,6 +23,7 @@ #define HW_MAC_DBDMA_H 1 #include "exec/memory.h" +#include "qemu/iov.h" typedef struct DBDMA_io DBDMA_io; diff --git a/qemu/include/hw/ppc/spapr.h b/qemu/include/hw/ppc/spapr.h index 91a61abbc..815d5eec4 100644 --- a/qemu/include/hw/ppc/spapr.h +++ b/qemu/include/hw/ppc/spapr.h @@ -5,6 +5,7 @@ #include "hw/boards.h" #include "hw/ppc/xics.h" #include "hw/ppc/spapr_drc.h" +#include "hw/mem/pc-dimm.h" struct VIOsPAPRBus; struct sPAPRPHBState; @@ -34,6 +35,8 @@ struct sPAPRMachineClass { MachineClass parent_class; /*< public >*/ + bool dr_lmb_enabled; /* enable dynamic-reconfig/hotplug of LMBs */ + bool use_ohci_by_default; /* use USB-OHCI instead of XHCI */ }; /** @@ -69,13 +72,13 @@ struct sPAPRMachineState { int htab_save_index; bool htab_first_pass; int htab_fd; - bool htab_fd_stale; /* RTAS state */ QTAILQ_HEAD(, sPAPRConfigureConnectorState) ccs_list; /*< public >*/ char *kvm_type; + MemoryHotplugState hotplug_memory; }; #define H_SUCCESS 0 @@ -201,11 +204,6 @@ struct sPAPRMachineState { #define H_SET_MODE_ENDIAN_BIG 0 #define H_SET_MODE_ENDIAN_LITTLE 1 -/* Flags for H_SET_MODE_RESOURCE_ADDR_TRANS_MODE */ -#define H_SET_MODE_ADDR_TRANS_NONE 0 -#define H_SET_MODE_ADDR_TRANS_0001_8000 2 -#define H_SET_MODE_ADDR_TRANS_C000_0000_0000_4000 3 - /* VASI States */ #define H_VASI_INVALID 0 #define H_VASI_ENABLED 1 @@ -331,6 +329,7 @@ struct sPAPRMachineState { #define H_SET_MPP 0x2D0 #define H_GET_MPP 0x2D4 #define H_XIRR_X 0x2FC +#define H_RANDOM 0x300 #define H_SET_MODE 0x31C #define MAX_HCALL_OPCODE H_SET_MODE @@ -353,15 +352,10 @@ typedef struct sPAPRDeviceTreeUpdateHeader { uint32_t version_id; } sPAPRDeviceTreeUpdateHeader; -/*#define DEBUG_SPAPR_HCALLS*/ - -#ifdef DEBUG_SPAPR_HCALLS -#define hcall_dprintf(fmt, ...) \ - do { fprintf(stderr, "%s: " fmt, __func__, ## __VA_ARGS__); } while (0) -#else #define hcall_dprintf(fmt, ...) \ - do { } while (0) -#endif + do { \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: " fmt, __func__, ## __VA_ARGS__); \ + } while (0) typedef target_ulong (*spapr_hcall_fn)(PowerPCCPU *cpu, sPAPRMachineState *sm, target_ulong opcode, @@ -408,13 +402,15 @@ int spapr_allocate_irq_block(int num, bool lsi, bool msi); #define RTAS_SLOT_PERM_ERR_LOG 2 /* RTAS return codes */ -#define RTAS_OUT_SUCCESS 0 -#define RTAS_OUT_NO_ERRORS_FOUND 1 -#define RTAS_OUT_HW_ERROR -1 -#define RTAS_OUT_BUSY -2 -#define RTAS_OUT_PARAM_ERROR -3 -#define RTAS_OUT_NOT_SUPPORTED -3 -#define RTAS_OUT_NOT_AUTHORIZED -9002 +#define RTAS_OUT_SUCCESS 0 +#define RTAS_OUT_NO_ERRORS_FOUND 1 +#define RTAS_OUT_HW_ERROR -1 +#define RTAS_OUT_BUSY -2 +#define RTAS_OUT_PARAM_ERROR -3 +#define RTAS_OUT_NOT_SUPPORTED -3 +#define RTAS_OUT_NO_SUCH_INDICATOR -3 +#define RTAS_OUT_NOT_AUTHORIZED -9002 +#define RTAS_OUT_SYSPARM_PARAM_ERROR -9999 /* RTAS tokens */ #define RTAS_TOKEN_BASE 0x2000 @@ -494,28 +490,14 @@ static inline uint32_t rtas_ld(target_ulong phys, int n) return ldl_be_phys(&address_space_memory, ppc64_phys_to_real(phys + 4*n)); } -static inline void rtas_st(target_ulong phys, int n, uint32_t val) -{ - stl_be_phys(&address_space_memory, ppc64_phys_to_real(phys + 4*n), val); -} - -static inline void rtas_st_buffer_direct(target_ulong phys, - target_ulong phys_len, - uint8_t *buffer, uint16_t buffer_len) +static inline uint64_t rtas_ldq(target_ulong phys, int n) { - cpu_physical_memory_write(ppc64_phys_to_real(phys), buffer, - MIN(buffer_len, phys_len)); + return (uint64_t)rtas_ld(phys, n) << 32 | rtas_ld(phys, n + 1); } -static inline void rtas_st_buffer(target_ulong phys, target_ulong phys_len, - uint8_t *buffer, uint16_t buffer_len) +static inline void rtas_st(target_ulong phys, int n, uint32_t val) { - if (phys_len < 2) { - return; - } - stw_be_phys(&address_space_memory, - ppc64_phys_to_real(phys), buffer_len); - rtas_st_buffer_direct(phys + 2, phys_len - 2, buffer, buffer_len); + stl_be_phys(&address_space_memory, ppc64_phys_to_real(phys + 4*n), val); } typedef void (*spapr_rtas_fn)(PowerPCCPU *cpu, sPAPRMachineState *sm, @@ -558,7 +540,7 @@ struct sPAPRTCETable { uint32_t page_shift; uint64_t *table; bool bypass; - bool vfio_accel; + bool need_vfio; int fd; MemoryRegion iommu; struct VIOsPAPRDevice *vdev; /* for @bypass migration compatibility only */ @@ -577,20 +559,27 @@ struct sPAPREventLogEntry { void spapr_events_init(sPAPRMachineState *sm); void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq); int spapr_h_cas_compose_response(sPAPRMachineState *sm, - target_ulong addr, target_ulong size); + target_ulong addr, target_ulong size, + bool cpu_update, bool memory_update); sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, uint64_t bus_offset, uint32_t page_shift, uint32_t nb_table, - bool vfio_accel); + bool need_vfio); +void spapr_tce_set_need_vfio(sPAPRTCETable *tcet, bool need_vfio); + MemoryRegion *spapr_tce_get_iommu(sPAPRTCETable *tcet); int spapr_dma_dt(void *fdt, int node_off, const char *propname, uint32_t liobn, uint64_t window, uint32_t size); int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname, sPAPRTCETable *tcet); void spapr_pci_switch_vga(bool big_endian); -void spapr_hotplug_req_add_event(sPAPRDRConnector *drc); -void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc); +void spapr_hotplug_req_add_by_index(sPAPRDRConnector *drc); +void spapr_hotplug_req_remove_by_index(sPAPRDRConnector *drc); +void spapr_hotplug_req_add_by_count(sPAPRDRConnectorType drc_type, + uint32_t count); +void spapr_hotplug_req_remove_by_count(sPAPRDRConnectorType drc_type, + uint32_t count); /* rtas-configure-connector state */ struct sPAPRConfigureConnectorState { @@ -603,10 +592,35 @@ struct sPAPRConfigureConnectorState { void spapr_ccs_reset_hook(void *opaque); #define TYPE_SPAPR_RTC "spapr-rtc" +#define TYPE_SPAPR_RNG "spapr-rng" void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns); int spapr_rtc_import_offset(DeviceState *dev, int64_t legacy_offset); +int spapr_rng_populate_dt(void *fdt); + #define SPAPR_MEMORY_BLOCK_SIZE (1 << 28) /* 256MB */ +/* + * This defines the maximum number of DIMM slots we can have for sPAPR + * guest. This is not defined by sPAPR but we are defining it to 32 slots + * based on default number of slots provided by PowerPC kernel. + */ +#define SPAPR_MAX_RAM_SLOTS 32 + +/* 1GB alignment for hotplug memory region */ +#define SPAPR_HOTPLUG_MEM_ALIGN (1ULL << 30) + +/* + * Number of 32 bit words in each LMB list entry in ibm,dynamic-memory + * property under ibm,dynamic-reconfiguration-memory node. + */ +#define SPAPR_DR_LMB_LIST_ENTRY_SIZE 6 + +/* + * This flag value defines the LMB as assigned in ibm,dynamic-memory + * property under ibm,dynamic-reconfiguration-memory node. + */ +#define SPAPR_LMB_FLAGS_ASSIGNED 0x00000008 + #endif /* !defined (__HW_SPAPR_H__) */ diff --git a/qemu/include/hw/ppc/spapr_drc.h b/qemu/include/hw/ppc/spapr_drc.h index 60cda35ed..fa21ba044 100644 --- a/qemu/include/hw/ppc/spapr_drc.h +++ b/qemu/include/hw/ppc/spapr_drc.h @@ -119,13 +119,14 @@ typedef enum { } sPAPRDREntitySense; typedef enum { - SPAPR_DR_CC_RESPONSE_NEXT_SIB = 1, /* currently unused */ - SPAPR_DR_CC_RESPONSE_NEXT_CHILD = 2, - SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY = 3, - SPAPR_DR_CC_RESPONSE_PREV_PARENT = 4, - SPAPR_DR_CC_RESPONSE_SUCCESS = 0, - SPAPR_DR_CC_RESPONSE_ERROR = -1, - SPAPR_DR_CC_RESPONSE_CONTINUE = -2, + SPAPR_DR_CC_RESPONSE_NEXT_SIB = 1, /* currently unused */ + SPAPR_DR_CC_RESPONSE_NEXT_CHILD = 2, + SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY = 3, + SPAPR_DR_CC_RESPONSE_PREV_PARENT = 4, + SPAPR_DR_CC_RESPONSE_SUCCESS = 0, + SPAPR_DR_CC_RESPONSE_ERROR = -1, + SPAPR_DR_CC_RESPONSE_CONTINUE = -2, + SPAPR_DR_CC_RESPONSE_NOT_CONFIGURABLE = -9003, } sPAPRDRCCResponse; typedef void (spapr_drc_detach_cb)(DeviceState *d, void *opaque); @@ -150,6 +151,7 @@ typedef struct sPAPRDRConnector { bool configured; bool awaiting_release; + bool signalled; /* device pointer, via link property */ DeviceState *dev; @@ -164,17 +166,17 @@ typedef struct sPAPRDRConnectorClass { /*< public >*/ /* accessors for guest-visible (generally via RTAS) DR state */ - int (*set_isolation_state)(sPAPRDRConnector *drc, - sPAPRDRIsolationState state); - int (*set_indicator_state)(sPAPRDRConnector *drc, - sPAPRDRIndicatorState state); - int (*set_allocation_state)(sPAPRDRConnector *drc, - sPAPRDRAllocationState state); + uint32_t (*set_isolation_state)(sPAPRDRConnector *drc, + sPAPRDRIsolationState state); + uint32_t (*set_indicator_state)(sPAPRDRConnector *drc, + sPAPRDRIndicatorState state); + uint32_t (*set_allocation_state)(sPAPRDRConnector *drc, + sPAPRDRAllocationState state); uint32_t (*get_index)(sPAPRDRConnector *drc); uint32_t (*get_type)(sPAPRDRConnector *drc); const char *(*get_name)(sPAPRDRConnector *drc); - sPAPRDREntitySense (*entity_sense)(sPAPRDRConnector *drc); + uint32_t (*entity_sense)(sPAPRDRConnector *drc, sPAPRDREntitySense *state); /* QEMU interfaces for managing FDT/configure-connector */ const void *(*get_fdt)(sPAPRDRConnector *drc, int *fdt_start_offset); @@ -187,6 +189,7 @@ typedef struct sPAPRDRConnectorClass { spapr_drc_detach_cb *detach_cb, void *detach_cb_opaque, Error **errp); bool (*release_pending)(sPAPRDRConnector *drc); + void (*set_signalled)(sPAPRDRConnector *drc); } sPAPRDRConnectorClass; sPAPRDRConnector *spapr_dr_connector_new(Object *owner, diff --git a/qemu/include/hw/ppc/spapr_vio.h b/qemu/include/hw/ppc/spapr_vio.h index 2299a5405..c9733e755 100644 --- a/qemu/include/hw/ppc/spapr_vio.h +++ b/qemu/include/hw/ppc/spapr_vio.h @@ -34,7 +34,7 @@ #define TYPE_SPAPR_VIO_BUS "spapr-vio-bus" #define SPAPR_VIO_BUS(obj) OBJECT_CHECK(VIOsPAPRBus, (obj), TYPE_SPAPR_VIO_BUS) -struct VIOsPAPRDevice; +#define TYPE_SPAPR_VIO_BRIDGE "spapr-vio-bridge" typedef struct VIOsPAPR_CRQ { uint64_t qladdr; diff --git a/qemu/include/hw/ppc/xics.h b/qemu/include/hw/ppc/xics.h index 355a96623..f60b06ae8 100644 --- a/qemu/include/hw/ppc/xics.h +++ b/qemu/include/hw/ppc/xics.h @@ -161,8 +161,9 @@ struct ICSIRQState { qemu_irq xics_get_qirq(XICSState *icp, int irq); void xics_set_irq_type(XICSState *icp, int irq, bool lsi); -int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi); -int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align); +int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi, Error **errp); +int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align, + Error **errp); void xics_free(XICSState *icp, int irq, int num); void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu); diff --git a/qemu/include/hw/qdev-core.h b/qemu/include/hw/qdev-core.h index 038b54d94..1ce02b20d 100644 --- a/qemu/include/hw/qdev-core.h +++ b/qemu/include/hw/qdev-core.h @@ -3,11 +3,9 @@ #include "qemu/queue.h" #include "qemu/option.h" -#include "qemu/typedefs.h" #include "qemu/bitmap.h" #include "qom/object.h" #include "hw/irq.h" -#include "qapi/error.h" #include "hw/hotplug.h" enum { @@ -114,6 +112,19 @@ typedef struct DeviceClass { * TODO remove once we're there */ bool cannot_instantiate_with_device_add_yet; + /* + * Does this device model survive object_unref(object_new(TNAME))? + * All device models should, and this flag shouldn't exist. Some + * devices crash in object_new(), some crash or hang in + * object_unref(). Makes introspecting properties with + * qmp_device_list_properties() dangerous. Bad, because it's used + * by -device FOO,help. This flag serves to protect that code. + * It should never be set without a comment explaining why it is + * set. + * TODO remove once we're there + */ + bool cannot_destroy_with_object_finalize_yet; + bool hotpluggable; /* callbacks */ @@ -224,9 +235,9 @@ struct BusState { struct Property { const char *name; PropertyInfo *info; - int offset; + ptrdiff_t offset; uint8_t bitnr; - qtype_code qtype; + QType qtype; int64_t defval; int arrayoffset; PropertyInfo *arrayinfo; @@ -324,6 +335,7 @@ int qdev_walk_children(DeviceState *dev, void *opaque); void qdev_reset_all(DeviceState *dev); +void qdev_reset_all_fn(void *opaque); /** * @qbus_reset_all: diff --git a/qemu/include/hw/qdev-properties.h b/qemu/include/hw/qdev-properties.h index 77538a8ca..0586cacce 100644 --- a/qemu/include/hw/qdev-properties.h +++ b/qemu/include/hw/qdev-properties.h @@ -18,8 +18,10 @@ extern PropertyInfo qdev_prop_string; extern PropertyInfo qdev_prop_chr; extern PropertyInfo qdev_prop_ptr; extern PropertyInfo qdev_prop_macaddr; +extern PropertyInfo qdev_prop_on_off_auto; extern PropertyInfo qdev_prop_losttickpolicy; extern PropertyInfo qdev_prop_bios_chs_trans; +extern PropertyInfo qdev_prop_fdc_drive_type; extern PropertyInfo qdev_prop_drive; extern PropertyInfo qdev_prop_netdev; extern PropertyInfo qdev_prop_vlan; @@ -154,6 +156,8 @@ extern PropertyInfo qdev_prop_arraylen; DEFINE_PROP(_n, _s, _f, qdev_prop_drive, BlockBackend *) #define DEFINE_PROP_MACADDR(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, qdev_prop_macaddr, MACAddr) +#define DEFINE_PROP_ON_OFF_AUTO(_n, _s, _f, _d) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_on_off_auto, OnOffAuto) #define DEFINE_PROP_LOSTTICKPOLICY(_n, _s, _f, _d) \ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_losttickpolicy, \ LostTickPolicy) @@ -180,8 +184,6 @@ void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *valu void qdev_prop_set_netdev(DeviceState *dev, const char *name, NetClientState *value); void qdev_prop_set_drive(DeviceState *dev, const char *name, BlockBackend *value, Error **errp); -void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, - BlockBackend *value); void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value); void qdev_prop_set_enum(DeviceState *dev, const char *name, int value); /* FIXME: Remove opaque pointer properties. */ diff --git a/qemu/include/hw/s390x/event-facility.h b/qemu/include/hw/s390x/event-facility.h index 6a062b668..dd8881838 100644 --- a/qemu/include/hw/s390x/event-facility.h +++ b/qemu/include/hw/s390x/event-facility.h @@ -47,6 +47,7 @@ OBJECT_GET_CLASS(SCLPEventClass, (obj), TYPE_SCLP_EVENT) #define TYPE_SCLP_CPU_HOTPLUG "sclp-cpu-hotplug" +#define TYPE_SCLP_QUIESCE "sclpquiesce" typedef struct WriteEventMask { SCCBHeader h; @@ -146,8 +147,10 @@ typedef struct WriteEventData { typedef struct ReadEventData { SCCBHeader h; - EventBufferHeader ebh; - uint32_t mask; + union { + uint32_t mask; + EventBufferHeader ebh; + }; } QEMU_PACKED ReadEventData; typedef struct SCLPEvent { @@ -186,11 +189,8 @@ typedef struct SCLPEventClass { OBJECT_GET_CLASS(SCLPEventFacilityClass, (obj), \ TYPE_SCLP_EVENT_FACILITY) -typedef struct SCLPEventFacility SCLPEventFacility; - typedef struct SCLPEventFacilityClass { - DeviceClass parent_class; - int (*init)(SCLPEventFacility *ef); + SysBusDeviceClass parent_class; void (*command_handler)(SCLPEventFacility *ef, SCCB *sccb, uint64_t code); bool (*event_pending)(SCLPEventFacility *ef); } SCLPEventFacilityClass; diff --git a/qemu/include/hw/s390x/s390-virtio-ccw.h b/qemu/include/hw/s390x/s390-virtio-ccw.h new file mode 100644 index 000000000..ab08332fe --- /dev/null +++ b/qemu/include/hw/s390x/s390-virtio-ccw.h @@ -0,0 +1,40 @@ +/* + * virtio ccw machine definitions + * + * Copyright 2012, 2016 IBM Corp. + * Author(s): Cornelia Huck + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ +#ifndef HW_S390X_S390_VIRTIO_CCW_H +#define HW_S390X_S390_VIRTIO_CCW_H + +#include "hw/boards.h" + +#define TYPE_S390_CCW_MACHINE "s390-ccw-machine" + +#define S390_CCW_MACHINE(obj) \ + OBJECT_CHECK(S390CcwMachineState, (obj), TYPE_S390_CCW_MACHINE) + +#define S390_MACHINE_CLASS(klass) \ + OBJECT_CLASS_CHECK(S390CcwMachineClass, (klass), TYPE_S390_CCW_MACHINE) + +typedef struct S390CcwMachineState { + /*< private >*/ + MachineState parent_obj; + + /*< public >*/ + bool aes_key_wrap; + bool dea_key_wrap; +} S390CcwMachineState; + +typedef struct S390CcwMachineClass { + /*< private >*/ + MachineClass parent_class; + + /*< public >*/ +} S390CcwMachineClass; + +#endif diff --git a/qemu/include/hw/s390x/sclp.h b/qemu/include/hw/s390x/sclp.h index e8a64e25b..b0c71b555 100644 --- a/qemu/include/hw/s390x/sclp.h +++ b/qemu/include/hw/s390x/sclp.h @@ -163,6 +163,39 @@ typedef struct SCCB { char data[SCCB_DATA_LEN]; } QEMU_PACKED SCCB; +#define TYPE_SCLP "sclp" +#define SCLP(obj) OBJECT_CHECK(SCLPDevice, (obj), TYPE_SCLP) +#define SCLP_CLASS(oc) OBJECT_CLASS_CHECK(SCLPDeviceClass, (oc), TYPE_SCLP) +#define SCLP_GET_CLASS(obj) OBJECT_GET_CLASS(SCLPDeviceClass, (obj), TYPE_SCLP) + +typedef struct SCLPEventFacility SCLPEventFacility; + +typedef struct SCLPDevice { + /* private */ + DeviceState parent_obj; + SCLPEventFacility *event_facility; + int increment_size; + + /* public */ +} SCLPDevice; + +typedef struct SCLPDeviceClass { + /* private */ + DeviceClass parent_class; + void (*read_SCP_info)(SCLPDevice *sclp, SCCB *sccb); + void (*read_storage_element0_info)(SCLPDevice *sclp, SCCB *sccb); + void (*read_storage_element1_info)(SCLPDevice *sclp, SCCB *sccb); + void (*attach_storage_element)(SCLPDevice *sclp, SCCB *sccb, + uint16_t element); + void (*assign_storage)(SCLPDevice *sclp, SCCB *sccb); + void (*unassign_storage)(SCLPDevice *sclp, SCCB *sccb); + void (*read_cpu_info)(SCLPDevice *sclp, SCCB *sccb); + + /* public */ + void (*execute)(SCLPDevice *sclp, SCCB *sccb, uint32_t code); + void (*service_interrupt)(SCLPDevice *sclp, uint32_t sccb); +} SCLPDeviceClass; + typedef struct sclpMemoryHotplugDev sclpMemoryHotplugDev; #define TYPE_SCLP_MEMORY_HOTPLUG_DEV "sclp-memory-hotplug-dev" diff --git a/qemu/include/hw/s390x/storage-keys.h b/qemu/include/hw/s390x/storage-keys.h new file mode 100644 index 000000000..72b850cb1 --- /dev/null +++ b/qemu/include/hw/s390x/storage-keys.h @@ -0,0 +1,60 @@ +/* + * s390 storage key device + * + * Copyright 2015 IBM Corp. + * Author(s): Jason J. Herne + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#ifndef __S390_STORAGE_KEYS_H +#define __S390_STORAGE_KEYS_H + +#include +#include "monitor/monitor.h" + +#define TYPE_S390_SKEYS "s390-skeys" +#define S390_SKEYS(obj) \ + OBJECT_CHECK(S390SKeysState, (obj), TYPE_S390_SKEYS) + +typedef struct S390SKeysState { + DeviceState parent_obj; + bool migration_enabled; + +} S390SKeysState; + +#define S390_SKEYS_CLASS(klass) \ + OBJECT_CLASS_CHECK(S390SKeysClass, (klass), TYPE_S390_SKEYS) +#define S390_SKEYS_GET_CLASS(obj) \ + OBJECT_GET_CLASS(S390SKeysClass, (obj), TYPE_S390_SKEYS) + +typedef struct S390SKeysClass { + DeviceClass parent_class; + int (*skeys_enabled)(S390SKeysState *ks); + int (*get_skeys)(S390SKeysState *ks, uint64_t start_gfn, uint64_t count, + uint8_t *keys); + int (*set_skeys)(S390SKeysState *ks, uint64_t start_gfn, uint64_t count, + uint8_t *keys); +} S390SKeysClass; + +#define TYPE_KVM_S390_SKEYS "s390-skeys-kvm" +#define TYPE_QEMU_S390_SKEYS "s390-skeys-qemu" +#define QEMU_S390_SKEYS(obj) \ + OBJECT_CHECK(QEMUS390SKeysState, (obj), TYPE_QEMU_S390_SKEYS) + +typedef struct QEMUS390SKeysState { + S390SKeysState parent_obj; + uint8_t *keydata; + uint32_t key_count; +} QEMUS390SKeysState; + +void s390_skeys_init(void); + +S390SKeysState *s390_get_skeys_device(void); + +void hmp_dump_skeys(Monitor *mon, const QDict *qdict); +void hmp_info_skeys(Monitor *mon, const QDict *qdict); + +#endif /* __S390_STORAGE_KEYS_H */ diff --git a/qemu/include/hw/scsi/scsi.h b/qemu/include/hw/scsi/scsi.h index cdaf0f8eb..8acd3fa99 100644 --- a/qemu/include/hw/scsi/scsi.h +++ b/qemu/include/hw/scsi/scsi.h @@ -2,7 +2,6 @@ #define QEMU_HW_SCSI_H #include "hw/qdev.h" -#include "qemu/typedefs.h" #include "hw/block/block.h" #include "sysemu/sysemu.h" #include "qemu/notify.h" @@ -108,6 +107,8 @@ struct SCSIDevice int blocksize; int type; uint64_t max_lba; + uint64_t wwn; + uint64_t port_wwn; }; extern const VMStateDescription vmstate_scsi_device; @@ -250,7 +251,6 @@ SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d, SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, uint8_t *buf, void *hba_private); int32_t scsi_req_enqueue(SCSIRequest *req); -void scsi_req_free(SCSIRequest *req); SCSIRequest *scsi_req_ref(SCSIRequest *req); void scsi_req_unref(SCSIRequest *req); @@ -272,6 +272,7 @@ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense); void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense); void scsi_device_report_change(SCSIDevice *dev, SCSISense sense); void scsi_device_unit_attention_reported(SCSIDevice *dev); +void scsi_generic_read_device_identification(SCSIDevice *dev); int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed); SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun); diff --git a/qemu/include/hw/sd.h b/qemu/include/hw/sd.h deleted file mode 100644 index 79adb5bb4..000000000 --- a/qemu/include/hw/sd.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * SD Memory Card emulation. Mostly correct for MMC too. - * - * Copyright (c) 2006 Andrzej Zaborowski - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef __hw_sd_h -#define __hw_sd_h 1 - -#define OUT_OF_RANGE (1 << 31) -#define ADDRESS_ERROR (1 << 30) -#define BLOCK_LEN_ERROR (1 << 29) -#define ERASE_SEQ_ERROR (1 << 28) -#define ERASE_PARAM (1 << 27) -#define WP_VIOLATION (1 << 26) -#define CARD_IS_LOCKED (1 << 25) -#define LOCK_UNLOCK_FAILED (1 << 24) -#define COM_CRC_ERROR (1 << 23) -#define ILLEGAL_COMMAND (1 << 22) -#define CARD_ECC_FAILED (1 << 21) -#define CC_ERROR (1 << 20) -#define SD_ERROR (1 << 19) -#define CID_CSD_OVERWRITE (1 << 16) -#define WP_ERASE_SKIP (1 << 15) -#define CARD_ECC_DISABLED (1 << 14) -#define ERASE_RESET (1 << 13) -#define CURRENT_STATE (7 << 9) -#define READY_FOR_DATA (1 << 8) -#define APP_CMD (1 << 5) -#define AKE_SEQ_ERROR (1 << 3) -#define OCR_CCS_BITN 30 - -typedef enum { - sd_none = -1, - sd_bc = 0, /* broadcast -- no response */ - sd_bcr, /* broadcast with response */ - sd_ac, /* addressed -- no data transfer */ - sd_adtc, /* addressed with data transfer */ -} sd_cmd_type_t; - -typedef struct { - uint8_t cmd; - uint32_t arg; - uint8_t crc; -} SDRequest; - -typedef struct SDState SDState; - -SDState *sd_init(BlockBackend *bs, bool is_spi); -int sd_do_command(SDState *sd, SDRequest *req, - uint8_t *response); -void sd_write_data(SDState *sd, uint8_t value); -uint8_t sd_read_data(SDState *sd); -void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert); -bool sd_data_ready(SDState *sd); -void sd_enable(SDState *sd, bool enable); - -#endif /* __hw_sd_h */ diff --git a/qemu/include/hw/sd/sd.h b/qemu/include/hw/sd/sd.h new file mode 100644 index 000000000..d5d273a44 --- /dev/null +++ b/qemu/include/hw/sd/sd.h @@ -0,0 +1,145 @@ +/* + * SD Memory Card emulation. Mostly correct for MMC too. + * + * Copyright (c) 2006 Andrzej Zaborowski + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __hw_sd_h +#define __hw_sd_h 1 + +#define OUT_OF_RANGE (1 << 31) +#define ADDRESS_ERROR (1 << 30) +#define BLOCK_LEN_ERROR (1 << 29) +#define ERASE_SEQ_ERROR (1 << 28) +#define ERASE_PARAM (1 << 27) +#define WP_VIOLATION (1 << 26) +#define CARD_IS_LOCKED (1 << 25) +#define LOCK_UNLOCK_FAILED (1 << 24) +#define COM_CRC_ERROR (1 << 23) +#define ILLEGAL_COMMAND (1 << 22) +#define CARD_ECC_FAILED (1 << 21) +#define CC_ERROR (1 << 20) +#define SD_ERROR (1 << 19) +#define CID_CSD_OVERWRITE (1 << 16) +#define WP_ERASE_SKIP (1 << 15) +#define CARD_ECC_DISABLED (1 << 14) +#define ERASE_RESET (1 << 13) +#define CURRENT_STATE (7 << 9) +#define READY_FOR_DATA (1 << 8) +#define APP_CMD (1 << 5) +#define AKE_SEQ_ERROR (1 << 3) +#define OCR_CCS_BITN 30 + +typedef enum { + sd_none = -1, + sd_bc = 0, /* broadcast -- no response */ + sd_bcr, /* broadcast with response */ + sd_ac, /* addressed -- no data transfer */ + sd_adtc, /* addressed with data transfer */ +} sd_cmd_type_t; + +typedef struct { + uint8_t cmd; + uint32_t arg; + uint8_t crc; +} SDRequest; + +typedef struct SDState SDState; +typedef struct SDBus SDBus; + +#define TYPE_SD_CARD "sd-card" +#define SD_CARD(obj) OBJECT_CHECK(SDState, (obj), TYPE_SD_CARD) +#define SD_CARD_CLASS(klass) \ + OBJECT_CLASS_CHECK(SDCardClass, (klass), TYPE_SD_CARD) +#define SD_CARD_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SDCardClass, (obj), TYPE_SD_CARD) + +typedef struct { + /*< private >*/ + DeviceClass parent_class; + /*< public >*/ + + int (*do_command)(SDState *sd, SDRequest *req, uint8_t *response); + void (*write_data)(SDState *sd, uint8_t value); + uint8_t (*read_data)(SDState *sd); + bool (*data_ready)(SDState *sd); + void (*enable)(SDState *sd, bool enable); + bool (*get_inserted)(SDState *sd); + bool (*get_readonly)(SDState *sd); +} SDCardClass; + +#define TYPE_SD_BUS "sd-bus" +#define SD_BUS(obj) OBJECT_CHECK(SDBus, (obj), TYPE_SD_BUS) +#define SD_BUS_CLASS(klass) OBJECT_CLASS_CHECK(SDBusClass, (klass), TYPE_SD_BUS) +#define SD_BUS_GET_CLASS(obj) OBJECT_GET_CLASS(SDBusClass, (obj), TYPE_SD_BUS) + +struct SDBus { + BusState qbus; +}; + +typedef struct { + /*< private >*/ + BusClass parent_class; + /*< public >*/ + + /* These methods are called by the SD device to notify the controller + * when the card insertion or readonly status changes + */ + void (*set_inserted)(DeviceState *dev, bool inserted); + void (*set_readonly)(DeviceState *dev, bool readonly); +} SDBusClass; + +/* Legacy functions to be used only by non-qdevified callers */ +SDState *sd_init(BlockBackend *bs, bool is_spi); +int sd_do_command(SDState *sd, SDRequest *req, + uint8_t *response); +void sd_write_data(SDState *sd, uint8_t value); +uint8_t sd_read_data(SDState *sd); +void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert); +bool sd_data_ready(SDState *sd); +/* sd_enable should not be used -- it is only used on the nseries boards, + * where it is part of a broken implementation of the MMC card slot switch + * (there should be two card slots which are multiplexed to a single MMC + * controller, but instead we model it with one card and controller and + * disable the card when the second slot is selected, so it looks like the + * second slot is always empty). + */ +void sd_enable(SDState *sd, bool enable); + +/* Functions to be used by qdevified callers (working via + * an SDBus rather than directly with SDState) + */ +int sdbus_do_command(SDBus *sd, SDRequest *req, uint8_t *response); +void sdbus_write_data(SDBus *sd, uint8_t value); +uint8_t sdbus_read_data(SDBus *sd); +bool sdbus_data_ready(SDBus *sd); +bool sdbus_get_inserted(SDBus *sd); +bool sdbus_get_readonly(SDBus *sd); + +/* Functions to be used by SD devices to report back to qdevified controllers */ +void sdbus_set_inserted(SDBus *sd, bool inserted); +void sdbus_set_readonly(SDBus *sd, bool inserted); + +#endif /* __hw_sd_h */ diff --git a/qemu/include/hw/sd/sdhci.h b/qemu/include/hw/sd/sdhci.h new file mode 100644 index 000000000..0f0c3f1e6 --- /dev/null +++ b/qemu/include/hw/sd/sdhci.h @@ -0,0 +1,95 @@ +/* + * SD Association Host Standard Specification v2.0 controller emulation + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Mitsyanko Igor + * Peter A.G. Crosthwaite + * + * Based on MMC controller for Samsung S5PC1xx-based board emulation + * by Alexey Merkulov and Vladimir Monakhov. + * + * 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 . + */ + +#ifndef SDHCI_H +#define SDHCI_H + +#include "qemu-common.h" +#include "hw/block/block.h" +#include "hw/pci/pci.h" +#include "hw/sysbus.h" +#include "hw/sd/sd.h" + +/* SD/MMC host controller state */ +typedef struct SDHCIState { + union { + PCIDevice pcidev; + SysBusDevice busdev; + }; + SDBus sdbus; + MemoryRegion iomem; + + QEMUTimer *insert_timer; /* timer for 'changing' sd card. */ + QEMUTimer *transfer_timer; + qemu_irq eject_cb; + qemu_irq ro_cb; + qemu_irq irq; + + uint32_t sdmasysad; /* SDMA System Address register */ + uint16_t blksize; /* Host DMA Buff Boundary and Transfer BlkSize Reg */ + uint16_t blkcnt; /* Blocks count for current transfer */ + uint32_t argument; /* Command Argument Register */ + uint16_t trnmod; /* Transfer Mode Setting Register */ + uint16_t cmdreg; /* Command Register */ + uint32_t rspreg[4]; /* Response Registers 0-3 */ + uint32_t prnsts; /* Present State Register */ + uint8_t hostctl; /* Host Control Register */ + uint8_t pwrcon; /* Power control Register */ + uint8_t blkgap; /* Block Gap Control Register */ + uint8_t wakcon; /* WakeUp Control Register */ + uint16_t clkcon; /* Clock control Register */ + uint8_t timeoutcon; /* Timeout Control Register */ + uint8_t admaerr; /* ADMA Error Status Register */ + uint16_t norintsts; /* Normal Interrupt Status Register */ + uint16_t errintsts; /* Error Interrupt Status Register */ + uint16_t norintstsen; /* Normal Interrupt Status Enable Register */ + uint16_t errintstsen; /* Error Interrupt Status Enable Register */ + uint16_t norintsigen; /* Normal Interrupt Signal Enable Register */ + uint16_t errintsigen; /* Error Interrupt Signal Enable Register */ + uint16_t acmd12errsts; /* Auto CMD12 error status register */ + uint64_t admasysaddr; /* ADMA System Address Register */ + + uint32_t capareg; /* Capabilities Register */ + uint32_t maxcurr; /* Maximum Current Capabilities Register */ + uint8_t *fifo_buffer; /* SD host i/o FIFO buffer */ + uint32_t buf_maxsz; + uint16_t data_count; /* current element in FIFO buffer */ + uint8_t stopped_state;/* Current SDHC state */ + bool pending_insert_quirk;/* Quirk for Raspberry Pi card insert int */ + bool pending_insert_state; + /* Buffer Data Port Register - virtual access point to R and W buffers */ + /* Software Reset Register - always reads as 0 */ + /* Force Event Auto CMD12 Error Interrupt Reg - write only */ + /* Force Event Error Interrupt Register- write only */ + /* RO Host Controller Version Register always reads as 0x2401 */ +} SDHCIState; + +#define TYPE_PCI_SDHCI "sdhci-pci" +#define PCI_SDHCI(obj) OBJECT_CHECK(SDHCIState, (obj), TYPE_PCI_SDHCI) + +#define TYPE_SYSBUS_SDHCI "generic-sdhci" +#define SYSBUS_SDHCI(obj) \ + OBJECT_CHECK(SDHCIState, (obj), TYPE_SYSBUS_SDHCI) + +#endif /* SDHCI_H */ diff --git a/qemu/include/hw/smbios/smbios.h b/qemu/include/hw/smbios/smbios.h new file mode 100644 index 000000000..76ccf7098 --- /dev/null +++ b/qemu/include/hw/smbios/smbios.h @@ -0,0 +1,269 @@ +#ifndef QEMU_SMBIOS_H +#define QEMU_SMBIOS_H +/* + * SMBIOS Support + * + * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. + * + * Authors: + * Alex Williamson + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "qemu/option.h" + +#define SMBIOS_MAX_TYPE 127 + +/* memory area description, used by type 19 table */ +struct smbios_phys_mem_area { + uint64_t address; + uint64_t length; +}; + +/* + * SMBIOS spec defined tables + */ +typedef enum SmbiosEntryPointType { + SMBIOS_ENTRY_POINT_21, + SMBIOS_ENTRY_POINT_30, +} SmbiosEntryPointType; + +/* SMBIOS Entry Point + * There are two types of entry points defined in the SMBIOS specification + * (see below). BIOS must place the entry point(s) at a 16-bit-aligned + * address between 0xf0000 and 0xfffff. Note that either entry point type + * can be used in a 64-bit target system, except that SMBIOS 2.1 entry point + * only allows the SMBIOS struct table to reside below 4GB address space. + */ + +/* SMBIOS 2.1 (32-bit) Entry Point + * - introduced since SMBIOS 2.1 + * - supports structure table below 4GB only + */ +struct smbios_21_entry_point { + uint8_t anchor_string[4]; + uint8_t checksum; + uint8_t length; + uint8_t smbios_major_version; + uint8_t smbios_minor_version; + uint16_t max_structure_size; + uint8_t entry_point_revision; + uint8_t formatted_area[5]; + uint8_t intermediate_anchor_string[5]; + uint8_t intermediate_checksum; + uint16_t structure_table_length; + uint32_t structure_table_address; + uint16_t number_of_structures; + uint8_t smbios_bcd_revision; +} QEMU_PACKED; + +/* SMBIOS 3.0 (64-bit) Entry Point + * - introduced since SMBIOS 3.0 + * - supports structure table at 64-bit address space + */ +struct smbios_30_entry_point { + uint8_t anchor_string[5]; + uint8_t checksum; + uint8_t length; + uint8_t smbios_major_version; + uint8_t smbios_minor_version; + uint8_t smbios_doc_rev; + uint8_t entry_point_revision; + uint8_t reserved; + uint32_t structure_table_max_size; + uint64_t structure_table_address; +} QEMU_PACKED; + +typedef union { + struct smbios_21_entry_point ep21; + struct smbios_30_entry_point ep30; +} QEMU_PACKED SmbiosEntryPoint; + +/* This goes at the beginning of every SMBIOS structure. */ +struct smbios_structure_header { + uint8_t type; + uint8_t length; + uint16_t handle; +} QEMU_PACKED; + +/* SMBIOS type 0 - BIOS Information */ +struct smbios_type_0 { + struct smbios_structure_header header; + uint8_t vendor_str; + uint8_t bios_version_str; + uint16_t bios_starting_address_segment; + uint8_t bios_release_date_str; + uint8_t bios_rom_size; + uint64_t bios_characteristics; + uint8_t bios_characteristics_extension_bytes[2]; + uint8_t system_bios_major_release; + uint8_t system_bios_minor_release; + uint8_t embedded_controller_major_release; + uint8_t embedded_controller_minor_release; +} QEMU_PACKED; + +/* UUID encoding. The time_* fields are little-endian, as specified by SMBIOS + * version 2.6. + */ +struct smbios_uuid { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_hi_and_reserved; + uint8_t clock_seq_low; + uint8_t node[6]; +} QEMU_PACKED; + +/* SMBIOS type 1 - System Information */ +struct smbios_type_1 { + struct smbios_structure_header header; + uint8_t manufacturer_str; + uint8_t product_name_str; + uint8_t version_str; + uint8_t serial_number_str; + struct smbios_uuid uuid; + uint8_t wake_up_type; + uint8_t sku_number_str; + uint8_t family_str; +} QEMU_PACKED; + +/* SMBIOS type 2 - Base Board */ +struct smbios_type_2 { + struct smbios_structure_header header; + uint8_t manufacturer_str; + uint8_t product_str; + uint8_t version_str; + uint8_t serial_number_str; + uint8_t asset_tag_number_str; + uint8_t feature_flags; + uint8_t location_str; + uint16_t chassis_handle; + uint8_t board_type; + uint8_t contained_element_count; + /* contained elements follow */ +} QEMU_PACKED; + +/* SMBIOS type 3 - System Enclosure (v2.7) */ +struct smbios_type_3 { + struct smbios_structure_header header; + uint8_t manufacturer_str; + uint8_t type; + uint8_t version_str; + uint8_t serial_number_str; + uint8_t asset_tag_number_str; + uint8_t boot_up_state; + uint8_t power_supply_state; + uint8_t thermal_state; + uint8_t security_status; + uint32_t oem_defined; + uint8_t height; + uint8_t number_of_power_cords; + uint8_t contained_element_count; + uint8_t sku_number_str; + /* contained elements follow */ +} QEMU_PACKED; + +/* SMBIOS type 4 - Processor Information (v2.6) */ +struct smbios_type_4 { + struct smbios_structure_header header; + uint8_t socket_designation_str; + uint8_t processor_type; + uint8_t processor_family; + uint8_t processor_manufacturer_str; + uint32_t processor_id[2]; + uint8_t processor_version_str; + uint8_t voltage; + uint16_t external_clock; + uint16_t max_speed; + uint16_t current_speed; + uint8_t status; + uint8_t processor_upgrade; + uint16_t l1_cache_handle; + uint16_t l2_cache_handle; + uint16_t l3_cache_handle; + uint8_t serial_number_str; + uint8_t asset_tag_number_str; + uint8_t part_number_str; + uint8_t core_count; + uint8_t core_enabled; + uint8_t thread_count; + uint16_t processor_characteristics; + uint16_t processor_family2; +} QEMU_PACKED; + +/* SMBIOS type 16 - Physical Memory Array (v2.7) */ +struct smbios_type_16 { + struct smbios_structure_header header; + uint8_t location; + uint8_t use; + uint8_t error_correction; + uint32_t maximum_capacity; + uint16_t memory_error_information_handle; + uint16_t number_of_memory_devices; + uint64_t extended_maximum_capacity; +} QEMU_PACKED; + +/* SMBIOS type 17 - Memory Device (v2.8) */ +struct smbios_type_17 { + struct smbios_structure_header header; + uint16_t physical_memory_array_handle; + uint16_t memory_error_information_handle; + uint16_t total_width; + uint16_t data_width; + uint16_t size; + uint8_t form_factor; + uint8_t device_set; + uint8_t device_locator_str; + uint8_t bank_locator_str; + uint8_t memory_type; + uint16_t type_detail; + uint16_t speed; + uint8_t manufacturer_str; + uint8_t serial_number_str; + uint8_t asset_tag_number_str; + uint8_t part_number_str; + uint8_t attributes; + uint32_t extended_size; + uint16_t configured_clock_speed; + uint16_t minimum_voltage; + uint16_t maximum_voltage; + uint16_t configured_voltage; +} QEMU_PACKED; + +/* SMBIOS type 19 - Memory Array Mapped Address (v2.7) */ +struct smbios_type_19 { + struct smbios_structure_header header; + uint32_t starting_address; + uint32_t ending_address; + uint16_t memory_array_handle; + uint8_t partition_width; + uint64_t extended_starting_address; + uint64_t extended_ending_address; +} QEMU_PACKED; + +/* SMBIOS type 32 - System Boot Information */ +struct smbios_type_32 { + struct smbios_structure_header header; + uint8_t reserved[6]; + uint8_t boot_status; +} QEMU_PACKED; + +/* SMBIOS type 127 -- End-of-table */ +struct smbios_type_127 { + struct smbios_structure_header header; +} QEMU_PACKED; + +void smbios_entry_add(QemuOpts *opts); +void smbios_set_cpuid(uint32_t version, uint32_t features); +void smbios_set_defaults(const char *manufacturer, const char *product, + const char *version, bool legacy_mode, + bool uuid_encoded, SmbiosEntryPointType ep_type); +uint8_t *smbios_get_table_legacy(size_t *length); +void smbios_get_tables(const struct smbios_phys_mem_area *mem_array, + const unsigned int mem_array_size, + uint8_t **tables, size_t *tables_len, + uint8_t **anchor, size_t *anchor_len); +#endif /*QEMU_SMBIOS_H */ diff --git a/qemu/include/hw/ssi.h b/qemu/include/hw/ssi.h deleted file mode 100644 index df0f83851..000000000 --- a/qemu/include/hw/ssi.h +++ /dev/null @@ -1,94 +0,0 @@ -/* QEMU Synchronous Serial Interface support. */ - -/* In principle SSI is a point-point interface. As such the qemu - implementation has a single slave device on a "bus". - However it is fairly common for boards to have multiple slaves - connected to a single master, and select devices with an external - chip select. This is implemented in qemu by having an explicit mux device. - It is assumed that master and slave are both using the same transfer width. - */ - -#ifndef QEMU_SSI_H -#define QEMU_SSI_H - -#include "hw/qdev.h" - -typedef struct SSISlave SSISlave; - -#define TYPE_SSI_SLAVE "ssi-slave" -#define SSI_SLAVE(obj) \ - OBJECT_CHECK(SSISlave, (obj), TYPE_SSI_SLAVE) -#define SSI_SLAVE_CLASS(klass) \ - OBJECT_CLASS_CHECK(SSISlaveClass, (klass), TYPE_SSI_SLAVE) -#define SSI_SLAVE_GET_CLASS(obj) \ - OBJECT_GET_CLASS(SSISlaveClass, (obj), TYPE_SSI_SLAVE) - -#define SSI_GPIO_CS "ssi-gpio-cs" - -typedef enum { - SSI_CS_NONE = 0, - SSI_CS_LOW, - SSI_CS_HIGH, -} SSICSMode; - -/* Slave devices. */ -typedef struct SSISlaveClass { - DeviceClass parent_class; - - int (*init)(SSISlave *dev); - - /* if you have standard or no CS behaviour, just override transfer. - * This is called when the device cs is active (true by default). - */ - uint32_t (*transfer)(SSISlave *dev, uint32_t val); - /* called when the CS line changes. Optional, devices only need to implement - * this if they have side effects associated with the cs line (beyond - * tristating the txrx lines). - */ - int (*set_cs)(SSISlave *dev, bool select); - /* define whether or not CS exists and is active low/high */ - SSICSMode cs_polarity; - - /* if you have non-standard CS behaviour override this to take control - * of the CS behaviour at the device level. transfer, set_cs, and - * cs_polarity are unused if this is overwritten. Transfer_raw will - * always be called for the device for every txrx access to the parent bus - */ - uint32_t (*transfer_raw)(SSISlave *dev, uint32_t val); -} SSISlaveClass; - -struct SSISlave { - DeviceState parent_obj; - - /* Chip select state */ - bool cs; -}; - -#define FROM_SSI_SLAVE(type, dev) DO_UPCAST(type, ssidev, dev) - -extern const VMStateDescription vmstate_ssi_slave; - -#define VMSTATE_SSI_SLAVE(_field, _state) { \ - .name = (stringify(_field)), \ - .size = sizeof(SSISlave), \ - .vmsd = &vmstate_ssi_slave, \ - .flags = VMS_STRUCT, \ - .offset = vmstate_offset_value(_state, _field, SSISlave), \ -} - -DeviceState *ssi_create_slave(SSIBus *bus, const char *name); -DeviceState *ssi_create_slave_no_init(SSIBus *bus, const char *name); - -/* Master interface. */ -SSIBus *ssi_create_bus(DeviceState *parent, const char *name); - -uint32_t ssi_transfer(SSIBus *bus, uint32_t val); - -/* Automatically connect all children nodes a spi controller as slaves */ -void ssi_auto_connect_slaves(DeviceState *parent, qemu_irq *cs_lines, - SSIBus *bus); - -/* max111x.c */ -void max111x_set_input(DeviceState *dev, int line, uint8_t value); - -#endif diff --git a/qemu/include/hw/ssi/ssi.h b/qemu/include/hw/ssi/ssi.h new file mode 100644 index 000000000..4a0a53903 --- /dev/null +++ b/qemu/include/hw/ssi/ssi.h @@ -0,0 +1,96 @@ +/* QEMU Synchronous Serial Interface support. */ + +/* In principle SSI is a point-point interface. As such the qemu + implementation has a single slave device on a "bus". + However it is fairly common for boards to have multiple slaves + connected to a single master, and select devices with an external + chip select. This is implemented in qemu by having an explicit mux device. + It is assumed that master and slave are both using the same transfer width. + */ + +#ifndef QEMU_SSI_H +#define QEMU_SSI_H + +#include "hw/qdev.h" + +typedef struct SSISlave SSISlave; +typedef struct SSISlaveClass SSISlaveClass; +typedef enum SSICSMode SSICSMode; + +#define TYPE_SSI_SLAVE "ssi-slave" +#define SSI_SLAVE(obj) \ + OBJECT_CHECK(SSISlave, (obj), TYPE_SSI_SLAVE) +#define SSI_SLAVE_CLASS(klass) \ + OBJECT_CLASS_CHECK(SSISlaveClass, (klass), TYPE_SSI_SLAVE) +#define SSI_SLAVE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SSISlaveClass, (obj), TYPE_SSI_SLAVE) + +#define SSI_GPIO_CS "ssi-gpio-cs" + +enum SSICSMode { + SSI_CS_NONE = 0, + SSI_CS_LOW, + SSI_CS_HIGH, +}; + +/* Slave devices. */ +struct SSISlaveClass { + DeviceClass parent_class; + + int (*init)(SSISlave *dev); + + /* if you have standard or no CS behaviour, just override transfer. + * This is called when the device cs is active (true by default). + */ + uint32_t (*transfer)(SSISlave *dev, uint32_t val); + /* called when the CS line changes. Optional, devices only need to implement + * this if they have side effects associated with the cs line (beyond + * tristating the txrx lines). + */ + int (*set_cs)(SSISlave *dev, bool select); + /* define whether or not CS exists and is active low/high */ + SSICSMode cs_polarity; + + /* if you have non-standard CS behaviour override this to take control + * of the CS behaviour at the device level. transfer, set_cs, and + * cs_polarity are unused if this is overwritten. Transfer_raw will + * always be called for the device for every txrx access to the parent bus + */ + uint32_t (*transfer_raw)(SSISlave *dev, uint32_t val); +}; + +struct SSISlave { + DeviceState parent_obj; + + /* Chip select state */ + bool cs; +}; + +#define FROM_SSI_SLAVE(type, dev) DO_UPCAST(type, ssidev, dev) + +extern const VMStateDescription vmstate_ssi_slave; + +#define VMSTATE_SSI_SLAVE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(SSISlave), \ + .vmsd = &vmstate_ssi_slave, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, SSISlave), \ +} + +DeviceState *ssi_create_slave(SSIBus *bus, const char *name); +DeviceState *ssi_create_slave_no_init(SSIBus *bus, const char *name); + +/* Master interface. */ +SSIBus *ssi_create_bus(DeviceState *parent, const char *name); + +uint32_t ssi_transfer(SSIBus *bus, uint32_t val); + +/* Automatically connect all children nodes a spi controller as slaves */ +void ssi_auto_connect_slaves(DeviceState *parent, qemu_irq *cs_lines, + SSIBus *bus); + +/* max111x.c */ +void max111x_set_input(DeviceState *dev, int line, uint8_t value); + +#endif diff --git a/qemu/include/hw/ssi/xilinx_spips.h b/qemu/include/hw/ssi/xilinx_spips.h new file mode 100644 index 000000000..dbb9eefba --- /dev/null +++ b/qemu/include/hw/ssi/xilinx_spips.h @@ -0,0 +1,72 @@ +/* + * Header file for the Xilinx Zynq SPI controller + * + * Copyright (C) 2015 Xilinx Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef XLNX_SPIPS_H +#define XLNX_SPIPS_H + +#include "hw/ssi/ssi.h" +#include "qemu/fifo8.h" + +typedef struct XilinxSPIPS XilinxSPIPS; + +#define XLNX_SPIPS_R_MAX (0x100 / 4) + +struct XilinxSPIPS { + SysBusDevice parent_obj; + + MemoryRegion iomem; + MemoryRegion mmlqspi; + + qemu_irq irq; + int irqline; + + uint8_t num_cs; + uint8_t num_busses; + + uint8_t snoop_state; + qemu_irq *cs_lines; + SSIBus **spi; + + Fifo8 rx_fifo; + Fifo8 tx_fifo; + + uint8_t num_txrx_bytes; + + uint32_t regs[XLNX_SPIPS_R_MAX]; +}; + +#define TYPE_XILINX_SPIPS "xlnx.ps7-spi" +#define TYPE_XILINX_QSPIPS "xlnx.ps7-qspi" + +#define XILINX_SPIPS(obj) \ + OBJECT_CHECK(XilinxSPIPS, (obj), TYPE_XILINX_SPIPS) +#define XILINX_SPIPS_CLASS(klass) \ + OBJECT_CLASS_CHECK(XilinxSPIPSClass, (klass), TYPE_XILINX_SPIPS) +#define XILINX_SPIPS_GET_CLASS(obj) \ + OBJECT_GET_CLASS(XilinxSPIPSClass, (obj), TYPE_XILINX_SPIPS) + +#define XILINX_QSPIPS(obj) \ + OBJECT_CHECK(XilinxQSPIPS, (obj), TYPE_XILINX_QSPIPS) + +#endif /* XLNX_SPIPS_H */ diff --git a/qemu/include/hw/timer/aspeed_timer.h b/qemu/include/hw/timer/aspeed_timer.h new file mode 100644 index 000000000..44dc2f89d --- /dev/null +++ b/qemu/include/hw/timer/aspeed_timer.h @@ -0,0 +1,59 @@ +/* + * ASPEED AST2400 Timer + * + * Andrew Jeffery + * + * Copyright (C) 2016 IBM Corp. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef ASPEED_TIMER_H +#define ASPEED_TIMER_H + +#include "hw/ptimer.h" + +#define ASPEED_TIMER(obj) \ + OBJECT_CHECK(AspeedTimerCtrlState, (obj), TYPE_ASPEED_TIMER); +#define TYPE_ASPEED_TIMER "aspeed.timer" +#define ASPEED_TIMER_NR_TIMERS 8 + +typedef struct AspeedTimer { + qemu_irq irq; + + uint8_t id; + + /** + * Track the line level as the ASPEED timers implement edge triggered + * interrupts, signalling with both the rising and falling edge. + */ + int32_t level; + ptimer_state *timer; + uint32_t reload; + uint32_t match[2]; +} AspeedTimer; + +typedef struct AspeedTimerCtrlState { + /*< private >*/ + SysBusDevice parent; + + /*< public >*/ + MemoryRegion iomem; + + uint32_t ctrl; + uint32_t ctrl2; + AspeedTimer timers[ASPEED_TIMER_NR_TIMERS]; +} AspeedTimerCtrlState; + +#endif /* ASPEED_TIMER_H */ diff --git a/qemu/include/hw/timer/digic-timer.h b/qemu/include/hw/timer/digic-timer.h index ae913482c..d9e67fe29 100644 --- a/qemu/include/hw/timer/digic-timer.h +++ b/qemu/include/hw/timer/digic-timer.h @@ -19,7 +19,6 @@ #define HW_TIMER_DIGIC_TIMER_H #include "hw/sysbus.h" -#include "qemu/typedefs.h" #include "hw/ptimer.h" #define TYPE_DIGIC_TIMER "digic-timer" diff --git a/qemu/include/hw/timer/hpet.h b/qemu/include/hw/timer/hpet.h index 773953be7..f38bcfecd 100644 --- a/qemu/include/hw/timer/hpet.h +++ b/qemu/include/hw/timer/hpet.h @@ -16,9 +16,10 @@ #include "qom/object.h" #define HPET_BASE 0xfed00000 -#define HPET_CLK_PERIOD 10000000ULL /* 10000000 femtoseconds == 10ns*/ +#define HPET_LEN 0x400 +#define HPET_CLK_PERIOD 10 /* 10 ns*/ -#define FS_PER_NS 1000000 +#define FS_PER_NS 1000000 /* 1000000 femtoseconds == 1 ns */ #define HPET_MIN_TIMERS 3 #define HPET_MAX_TIMERS 32 diff --git a/qemu/include/hw/timer/imx_epit.h b/qemu/include/hw/timer/imx_epit.h new file mode 100644 index 000000000..0730ac35e --- /dev/null +++ b/qemu/include/hw/timer/imx_epit.h @@ -0,0 +1,80 @@ +/* + * i.MX EPIT Timer + * + * Copyright (c) 2008 OK Labs + * Copyright (c) 2011 NICTA Pty Ltd + * Originally written by Hans Jiang + * Updated by Peter Chubb + * Updated by Jean-Christophe Dubois + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef IMX_EPIT_H +#define IMX_EPIT_H + +#include "hw/sysbus.h" +#include "hw/ptimer.h" +#include "hw/misc/imx_ccm.h" + +/* + * EPIT: Enhanced periodic interrupt timer + */ + +#define CR_EN (1 << 0) +#define CR_ENMOD (1 << 1) +#define CR_OCIEN (1 << 2) +#define CR_RLD (1 << 3) +#define CR_PRESCALE_SHIFT (4) +#define CR_PRESCALE_MASK (0xfff) +#define CR_SWR (1 << 16) +#define CR_IOVW (1 << 17) +#define CR_DBGEN (1 << 18) +#define CR_WAITEN (1 << 19) +#define CR_DOZEN (1 << 20) +#define CR_STOPEN (1 << 21) +#define CR_CLKSRC_SHIFT (24) +#define CR_CLKSRC_MASK (0x3 << CR_CLKSRC_SHIFT) + +#define EPIT_TIMER_MAX 0XFFFFFFFFUL + +#define TYPE_IMX_EPIT "imx.epit" +#define IMX_EPIT(obj) OBJECT_CHECK(IMXEPITState, (obj), TYPE_IMX_EPIT) + +typedef struct IMXEPITState{ + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + ptimer_state *timer_reload; + ptimer_state *timer_cmp; + MemoryRegion iomem; + IMXCCMState *ccm; + + uint32_t cr; + uint32_t sr; + uint32_t lr; + uint32_t cmp; + uint32_t cnt; + + uint32_t freq; + qemu_irq irq; +} IMXEPITState; + +#endif /* IMX_EPIT_H */ diff --git a/qemu/include/hw/timer/imx_gpt.h b/qemu/include/hw/timer/imx_gpt.h new file mode 100644 index 000000000..461adbe53 --- /dev/null +++ b/qemu/include/hw/timer/imx_gpt.h @@ -0,0 +1,108 @@ +/* + * i.MX GPT Timer + * + * Copyright (c) 2008 OK Labs + * Copyright (c) 2011 NICTA Pty Ltd + * Originally written by Hans Jiang + * Updated by Peter Chubb + * Updated by Jean-Christophe Dubois + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef IMX_GPT_H +#define IMX_GPT_H + +#include "hw/sysbus.h" +#include "hw/ptimer.h" +#include "hw/misc/imx_ccm.h" + +/* + * GPT : General purpose timer + * + * This timer counts up continuously while it is enabled, resetting itself + * to 0 when it reaches GPT_TIMER_MAX (in freerun mode) or when it + * reaches the value of one of the ocrX (in periodic mode). + */ + +#define GPT_TIMER_MAX 0XFFFFFFFFUL + +/* Control register. Not all of these bits have any effect (yet) */ +#define GPT_CR_EN (1 << 0) /* GPT Enable */ +#define GPT_CR_ENMOD (1 << 1) /* GPT Enable Mode */ +#define GPT_CR_DBGEN (1 << 2) /* GPT Debug mode enable */ +#define GPT_CR_WAITEN (1 << 3) /* GPT Wait Mode Enable */ +#define GPT_CR_DOZEN (1 << 4) /* GPT Doze mode enable */ +#define GPT_CR_STOPEN (1 << 5) /* GPT Stop Mode Enable */ +#define GPT_CR_CLKSRC_SHIFT (6) +#define GPT_CR_CLKSRC_MASK (0x7) + +#define GPT_CR_FRR (1 << 9) /* Freerun or Restart */ +#define GPT_CR_SWR (1 << 15) /* Software Reset */ +#define GPT_CR_IM1 (3 << 16) /* Input capture channel 1 mode (2 bits) */ +#define GPT_CR_IM2 (3 << 18) /* Input capture channel 2 mode (2 bits) */ +#define GPT_CR_OM1 (7 << 20) /* Output Compare Channel 1 Mode (3 bits) */ +#define GPT_CR_OM2 (7 << 23) /* Output Compare Channel 2 Mode (3 bits) */ +#define GPT_CR_OM3 (7 << 26) /* Output Compare Channel 3 Mode (3 bits) */ +#define GPT_CR_FO1 (1 << 29) /* Force Output Compare Channel 1 */ +#define GPT_CR_FO2 (1 << 30) /* Force Output Compare Channel 2 */ +#define GPT_CR_FO3 (1 << 31) /* Force Output Compare Channel 3 */ + +#define GPT_SR_OF1 (1 << 0) +#define GPT_SR_OF2 (1 << 1) +#define GPT_SR_OF3 (1 << 2) +#define GPT_SR_ROV (1 << 5) + +#define GPT_IR_OF1IE (1 << 0) +#define GPT_IR_OF2IE (1 << 1) +#define GPT_IR_OF3IE (1 << 2) +#define GPT_IR_ROVIE (1 << 5) + +#define TYPE_IMX_GPT "imx.gpt" +#define IMX_GPT(obj) OBJECT_CHECK(IMXGPTState, (obj), TYPE_IMX_GPT) + +typedef struct IMXGPTState{ + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + ptimer_state *timer; + MemoryRegion iomem; + IMXCCMState *ccm; + + uint32_t cr; + uint32_t pr; + uint32_t sr; + uint32_t ir; + uint32_t ocr1; + uint32_t ocr2; + uint32_t ocr3; + uint32_t icr1; + uint32_t icr2; + uint32_t cnt; + + uint32_t next_timeout; + uint32_t next_int; + + uint32_t freq; + + qemu_irq irq; +} IMXGPTState; + +#endif /* IMX_GPT_H */ diff --git a/qemu/include/hw/usb.h b/qemu/include/hw/usb.h index c8b6e7b57..163fe0490 100644 --- a/qemu/include/hw/usb.h +++ b/qemu/include/hw/usb.h @@ -26,6 +26,7 @@ */ #include "hw/qdev.h" +#include "qemu/iov.h" #include "qemu/queue.h" /* Constants related to the USB / PCI interaction */ diff --git a/qemu/include/hw/vfio/vfio-amd-xgbe.h b/qemu/include/hw/vfio/vfio-amd-xgbe.h new file mode 100644 index 000000000..9fff65e99 --- /dev/null +++ b/qemu/include/hw/vfio/vfio-amd-xgbe.h @@ -0,0 +1,51 @@ +/* + * VFIO AMD XGBE device + * + * Copyright Linaro Limited, 2015 + * + * Authors: + * Eric Auger + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef HW_VFIO_VFIO_AMD_XGBE_H +#define HW_VFIO_VFIO_AMD_XGBE_H + +#include "hw/vfio/vfio-platform.h" + +#define TYPE_VFIO_AMD_XGBE "vfio-amd-xgbe" + +/** + * This device exposes: + * - 5 MMIO regions: MAC, PCS, SerDes Rx/Tx regs, + SerDes Integration Registers 1/2 & 2/2 + * - 2 level sensitive IRQs and optional DMA channel IRQs + */ +struct VFIOAmdXgbeDevice { + VFIOPlatformDevice vdev; +}; + +typedef struct VFIOAmdXgbeDevice VFIOAmdXgbeDevice; + +struct VFIOAmdXgbeDeviceClass { + /*< private >*/ + VFIOPlatformDeviceClass parent_class; + /*< public >*/ + DeviceRealize parent_realize; +}; + +typedef struct VFIOAmdXgbeDeviceClass VFIOAmdXgbeDeviceClass; + +#define VFIO_AMD_XGBE_DEVICE(obj) \ + OBJECT_CHECK(VFIOAmdXgbeDevice, (obj), TYPE_VFIO_AMD_XGBE) +#define VFIO_AMD_XGBE_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(VFIOAmdXgbeDeviceClass, (klass), \ + TYPE_VFIO_AMD_XGBE) +#define VFIO_AMD_XGBE_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VFIOAmdXgbeDeviceClass, (obj), \ + TYPE_VFIO_AMD_XGBE) + +#endif diff --git a/qemu/include/hw/vfio/vfio-common.h b/qemu/include/hw/vfio/vfio-common.h index 59a321d47..eb0e1b034 100644 --- a/qemu/include/hw/vfio/vfio-common.h +++ b/qemu/include/hw/vfio/vfio-common.h @@ -25,6 +25,9 @@ #include "exec/memory.h" #include "qemu/queue.h" #include "qemu/notify.h" +#ifdef CONFIG_LINUX +#include +#endif /*#define DEBUG_VFIO*/ #ifdef DEBUG_VFIO @@ -35,24 +38,26 @@ do { } while (0) #endif -/* Extra debugging, trap acceleration paths for more logging */ -#define VFIO_ALLOW_KVM_INTX 1 -#define VFIO_ALLOW_KVM_MSI 1 -#define VFIO_ALLOW_KVM_MSIX 1 - enum { VFIO_DEVICE_TYPE_PCI = 0, VFIO_DEVICE_TYPE_PLATFORM = 1, }; +typedef struct VFIOMmap { + MemoryRegion mem; + void *mmap; + off_t offset; + size_t size; +} VFIOMmap; + typedef struct VFIORegion { struct VFIODevice *vbasedev; off_t fd_offset; /* offset of region within device fd */ - MemoryRegion mem; /* slow, read/write access */ - MemoryRegion mmap_mem; /* direct mapped access */ - void *mmap; + MemoryRegion *mem; /* slow, read/write access */ size_t size; uint32_t flags; /* VFIO region flags (rd/wr/mmap) */ + uint32_t nr_mmaps; + VFIOMmap *mmaps; uint8_t nr; /* cache the region number for debug */ } VFIORegion; @@ -64,22 +69,19 @@ typedef struct VFIOAddressSpace { struct VFIOGroup; -typedef struct VFIOType1 { - MemoryListener listener; - int error; - bool initialized; -} VFIOType1; - typedef struct VFIOContainer { VFIOAddressSpace *space; int fd; /* /dev/vfio/vfio, empowered by the attached groups */ - struct { - /* enable abstraction to support various iommu backends */ - union { - VFIOType1 type1; - }; - void (*release)(struct VFIOContainer *); - } iommu_data; + MemoryListener listener; + int error; + bool initialized; + /* + * This assumes the host IOMMU can support only a single + * contiguous IOVA window. We may need to generalize that in + * future + */ + hwaddr min_iova, max_iova; + uint64_t iova_pgsizes; QLIST_HEAD(, VFIOGuestIOMMU) giommu_list; QLIST_HEAD(, VFIOGroup) group_list; QLIST_ENTRY(VFIOContainer) next; @@ -97,12 +99,13 @@ typedef struct VFIODeviceOps VFIODeviceOps; typedef struct VFIODevice { QLIST_ENTRY(VFIODevice) next; struct VFIOGroup *group; + char *sysfsdev; char *name; int fd; int type; bool reset_works; bool needs_reset; - bool allow_mmap; + bool no_mmap; VFIODeviceOps *ops; unsigned int num_irqs; unsigned int num_regions; @@ -132,10 +135,12 @@ void vfio_region_write(void *opaque, hwaddr addr, uint64_t data, unsigned size); uint64_t vfio_region_read(void *opaque, hwaddr addr, unsigned size); -int vfio_mmap_region(Object *vdev, VFIORegion *region, - MemoryRegion *mem, MemoryRegion *submem, - void **map, size_t size, off_t offset, - const char *name); +int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region, + int index, const char *name); +int vfio_region_mmap(VFIORegion *region); +void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled); +void vfio_region_exit(VFIORegion *region); +void vfio_region_finalize(VFIORegion *region); void vfio_reset_handler(void *opaque); VFIOGroup *vfio_get_group(int groupid, AddressSpace *as); void vfio_put_group(VFIOGroup *group); @@ -146,4 +151,8 @@ extern const MemoryRegionOps vfio_region_ops; extern QLIST_HEAD(vfio_group_head, VFIOGroup) vfio_group_list; extern QLIST_HEAD(vfio_as_head, VFIOAddressSpace) vfio_address_spaces; +#ifdef CONFIG_LINUX +int vfio_get_region_info(VFIODevice *vbasedev, int index, + struct vfio_region_info **info); +#endif #endif /* !HW_VFIO_VFIO_COMMON_H */ diff --git a/qemu/include/hw/vfio/vfio-platform.h b/qemu/include/hw/vfio/vfio-platform.h index c5cf1d79f..b468f80b1 100644 --- a/qemu/include/hw/vfio/vfio-platform.h +++ b/qemu/include/hw/vfio/vfio-platform.h @@ -34,8 +34,8 @@ enum { typedef struct VFIOINTp { QLIST_ENTRY(VFIOINTp) next; /* entry for IRQ list */ QSIMPLEQ_ENTRY(VFIOINTp) pqnext; /* entry for pending IRQ queue */ - EventNotifier interrupt; /* eventfd triggered on interrupt */ - EventNotifier unmask; /* eventfd for unmask on QEMU bypass */ + EventNotifier *interrupt; /* eventfd triggered on interrupt */ + EventNotifier *unmask; /* eventfd for unmask on QEMU bypass */ qemu_irq qemuirq; struct VFIOPlatformDevice *vdev; /* back pointer to device */ int state; /* inactive, pending, active */ diff --git a/qemu/include/hw/vfio/vfio.h b/qemu/include/hw/vfio/vfio.h index 0b26cd8e1..f27d59922 100644 --- a/qemu/include/hw/vfio/vfio.h +++ b/qemu/include/hw/vfio/vfio.h @@ -1,9 +1,7 @@ #ifndef VFIO_API_H #define VFIO_API_H -#include "qemu/typedefs.h" - -extern int vfio_container_ioctl(AddressSpace *as, int32_t groupid, - int req, void *param); +bool vfio_eeh_as_ok(AddressSpace *as); +int vfio_eeh_as_op(AddressSpace *as, uint32_t op); #endif diff --git a/qemu/include/hw/virtio/dataplane/vring-accessors.h b/qemu/include/hw/virtio/dataplane/vring-accessors.h deleted file mode 100644 index 815c19b6e..000000000 --- a/qemu/include/hw/virtio/dataplane/vring-accessors.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef VRING_ACCESSORS_H -#define VRING_ACCESSORS_H - -#include "standard-headers/linux/virtio_ring.h" -#include "hw/virtio/virtio.h" -#include "hw/virtio/virtio-access.h" - -static inline uint16_t vring_get_used_idx(VirtIODevice *vdev, Vring *vring) -{ - return virtio_tswap16(vdev, vring->vr.used->idx); -} - -static inline void vring_set_used_idx(VirtIODevice *vdev, Vring *vring, - uint16_t idx) -{ - vring->vr.used->idx = virtio_tswap16(vdev, idx); -} - -static inline uint16_t vring_get_avail_idx(VirtIODevice *vdev, Vring *vring) -{ - return virtio_tswap16(vdev, vring->vr.avail->idx); -} - -static inline uint16_t vring_get_avail_ring(VirtIODevice *vdev, Vring *vring, - int i) -{ - return virtio_tswap16(vdev, vring->vr.avail->ring[i]); -} - -static inline void vring_set_used_ring_id(VirtIODevice *vdev, Vring *vring, - int i, uint32_t id) -{ - vring->vr.used->ring[i].id = virtio_tswap32(vdev, id); -} - -static inline void vring_set_used_ring_len(VirtIODevice *vdev, Vring *vring, - int i, uint32_t len) -{ - vring->vr.used->ring[i].len = virtio_tswap32(vdev, len); -} - -static inline uint16_t vring_get_used_flags(VirtIODevice *vdev, Vring *vring) -{ - return virtio_tswap16(vdev, vring->vr.used->flags); -} - -static inline uint16_t vring_get_avail_flags(VirtIODevice *vdev, Vring *vring) -{ - return virtio_tswap16(vdev, vring->vr.avail->flags); -} - -static inline void vring_set_used_flags(VirtIODevice *vdev, Vring *vring, - uint16_t flags) -{ - vring->vr.used->flags |= virtio_tswap16(vdev, flags); -} - -static inline void vring_clear_used_flags(VirtIODevice *vdev, Vring *vring, - uint16_t flags) -{ - vring->vr.used->flags &= virtio_tswap16(vdev, ~flags); -} - -static inline unsigned int vring_get_num(Vring *vring) -{ - return vring->vr.num; -} - -/* Are there more descriptors available? */ -static inline bool vring_more_avail(VirtIODevice *vdev, Vring *vring) -{ - return vring_get_avail_idx(vdev, vring) != vring->last_avail_idx; -} - -#endif diff --git a/qemu/include/hw/virtio/dataplane/vring.h b/qemu/include/hw/virtio/dataplane/vring.h deleted file mode 100644 index 8d97db9e2..000000000 --- a/qemu/include/hw/virtio/dataplane/vring.h +++ /dev/null @@ -1,49 +0,0 @@ -/* Copyright 2012 Red Hat, Inc. and/or its affiliates - * Copyright IBM, Corp. 2012 - * - * Based on Linux 2.6.39 vhost code: - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2006 Rusty Russell IBM Corporation - * - * Author: Michael S. Tsirkin - * Stefan Hajnoczi - * - * Inspiration, some code, and most witty comments come from - * Documentation/virtual/lguest/lguest.c, by Rusty Russell - * - * This work is licensed under the terms of the GNU GPL, version 2. - */ - -#ifndef VRING_H -#define VRING_H - -#include "qemu-common.h" -#include "standard-headers/linux/virtio_ring.h" -#include "hw/virtio/virtio.h" - -typedef struct { - MemoryRegion *mr; /* memory region containing the vring */ - struct vring vr; /* virtqueue vring mapped to host memory */ - uint16_t last_avail_idx; /* last processed avail ring index */ - uint16_t last_used_idx; /* last processed used ring index */ - uint16_t signalled_used; /* EVENT_IDX state */ - bool signalled_used_valid; - bool broken; /* was there a fatal error? */ -} Vring; - -/* Fail future vring_pop() and vring_push() calls until reset */ -static inline void vring_set_broken(Vring *vring) -{ - vring->broken = true; -} - -bool vring_setup(Vring *vring, VirtIODevice *vdev, int n); -void vring_teardown(Vring *vring, VirtIODevice *vdev, int n); -void vring_disable_notification(VirtIODevice *vdev, Vring *vring); -bool vring_enable_notification(VirtIODevice *vdev, Vring *vring); -bool vring_should_notify(VirtIODevice *vdev, Vring *vring); -int vring_pop(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem); -void vring_push(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem, - int len); - -#endif /* VRING_H */ diff --git a/qemu/include/hw/virtio/vhost-backend.h b/qemu/include/hw/virtio/vhost-backend.h index e472f2971..95fcc9667 100644 --- a/qemu/include/hw/virtio/vhost-backend.h +++ b/qemu/include/hw/virtio/vhost-backend.h @@ -11,6 +11,7 @@ #ifndef VHOST_BACKEND_H_ #define VHOST_BACKEND_H_ + typedef enum VhostBackendType { VHOST_BACKEND_TYPE_NONE = 0, VHOST_BACKEND_TYPE_KERNEL = 1, @@ -19,17 +20,86 @@ typedef enum VhostBackendType { } VhostBackendType; struct vhost_dev; +struct vhost_log; +struct vhost_memory; +struct vhost_vring_file; +struct vhost_vring_state; +struct vhost_vring_addr; +struct vhost_scsi_target; -typedef int (*vhost_call)(struct vhost_dev *dev, unsigned long int request, - void *arg); typedef int (*vhost_backend_init)(struct vhost_dev *dev, void *opaque); typedef int (*vhost_backend_cleanup)(struct vhost_dev *dev); +typedef int (*vhost_backend_memslots_limit)(struct vhost_dev *dev); + +typedef int (*vhost_net_set_backend_op)(struct vhost_dev *dev, + struct vhost_vring_file *file); +typedef int (*vhost_scsi_set_endpoint_op)(struct vhost_dev *dev, + struct vhost_scsi_target *target); +typedef int (*vhost_scsi_clear_endpoint_op)(struct vhost_dev *dev, + struct vhost_scsi_target *target); +typedef int (*vhost_scsi_get_abi_version_op)(struct vhost_dev *dev, + int *version); +typedef int (*vhost_set_log_base_op)(struct vhost_dev *dev, uint64_t base, + struct vhost_log *log); +typedef int (*vhost_set_mem_table_op)(struct vhost_dev *dev, + struct vhost_memory *mem); +typedef int (*vhost_set_vring_addr_op)(struct vhost_dev *dev, + struct vhost_vring_addr *addr); +typedef int (*vhost_set_vring_endian_op)(struct vhost_dev *dev, + struct vhost_vring_state *ring); +typedef int (*vhost_set_vring_num_op)(struct vhost_dev *dev, + struct vhost_vring_state *ring); +typedef int (*vhost_set_vring_base_op)(struct vhost_dev *dev, + struct vhost_vring_state *ring); +typedef int (*vhost_get_vring_base_op)(struct vhost_dev *dev, + struct vhost_vring_state *ring); +typedef int (*vhost_set_vring_kick_op)(struct vhost_dev *dev, + struct vhost_vring_file *file); +typedef int (*vhost_set_vring_call_op)(struct vhost_dev *dev, + struct vhost_vring_file *file); +typedef int (*vhost_set_features_op)(struct vhost_dev *dev, + uint64_t features); +typedef int (*vhost_get_features_op)(struct vhost_dev *dev, + uint64_t *features); +typedef int (*vhost_set_owner_op)(struct vhost_dev *dev); +typedef int (*vhost_reset_device_op)(struct vhost_dev *dev); +typedef int (*vhost_get_vq_index_op)(struct vhost_dev *dev, int idx); +typedef int (*vhost_set_vring_enable_op)(struct vhost_dev *dev, + int enable); +typedef bool (*vhost_requires_shm_log_op)(struct vhost_dev *dev); +typedef int (*vhost_migration_done_op)(struct vhost_dev *dev, + char *mac_addr); +typedef bool (*vhost_backend_can_merge_op)(struct vhost_dev *dev, + uint64_t start1, uint64_t size1, + uint64_t start2, uint64_t size2); typedef struct VhostOps { VhostBackendType backend_type; - vhost_call vhost_call; vhost_backend_init vhost_backend_init; vhost_backend_cleanup vhost_backend_cleanup; + vhost_backend_memslots_limit vhost_backend_memslots_limit; + vhost_net_set_backend_op vhost_net_set_backend; + vhost_scsi_set_endpoint_op vhost_scsi_set_endpoint; + vhost_scsi_clear_endpoint_op vhost_scsi_clear_endpoint; + vhost_scsi_get_abi_version_op vhost_scsi_get_abi_version; + vhost_set_log_base_op vhost_set_log_base; + vhost_set_mem_table_op vhost_set_mem_table; + vhost_set_vring_addr_op vhost_set_vring_addr; + vhost_set_vring_endian_op vhost_set_vring_endian; + vhost_set_vring_num_op vhost_set_vring_num; + vhost_set_vring_base_op vhost_set_vring_base; + vhost_get_vring_base_op vhost_get_vring_base; + vhost_set_vring_kick_op vhost_set_vring_kick; + vhost_set_vring_call_op vhost_set_vring_call; + vhost_set_features_op vhost_set_features; + vhost_get_features_op vhost_get_features; + vhost_set_owner_op vhost_set_owner; + vhost_reset_device_op vhost_reset_device; + vhost_get_vq_index_op vhost_get_vq_index; + vhost_set_vring_enable_op vhost_set_vring_enable; + vhost_requires_shm_log_op vhost_requires_shm_log; + vhost_migration_done_op vhost_migration_done; + vhost_backend_can_merge_op vhost_backend_can_merge; } VhostOps; extern const VhostOps user_ops; diff --git a/qemu/include/hw/virtio/vhost-scsi.h b/qemu/include/hw/virtio/vhost-scsi.h index 701bfee61..9fd63df12 100644 --- a/qemu/include/hw/virtio/vhost-scsi.h +++ b/qemu/include/hw/virtio/vhost-scsi.h @@ -19,37 +19,12 @@ #include "hw/virtio/virtio-scsi.h" #include "hw/virtio/vhost.h" -/* - * Used by QEMU userspace to ensure a consistent vhost-scsi ABI. - * - * ABI Rev 0: July 2012 version starting point for v3.6-rc merge candidate + - * RFC-v2 vhost-scsi userspace. Add GET_ABI_VERSION ioctl usage - * ABI Rev 1: January 2013. Ignore vhost_tpgt filed in struct vhost_scsi_target. - * All the targets under vhost_wwpn can be seen and used by guest. - */ - -#define VHOST_SCSI_ABI_VERSION 1 - -/* TODO #include properly */ -/* For VHOST_SCSI_SET_ENDPOINT/VHOST_SCSI_CLEAR_ENDPOINT ioctl */ -struct vhost_scsi_target { - int abi_version; - char vhost_wwpn[224]; - unsigned short vhost_tpgt; - unsigned short reserved; -}; - enum vhost_scsi_vq_list { VHOST_SCSI_VQ_CONTROL = 0, VHOST_SCSI_VQ_EVENT = 1, VHOST_SCSI_VQ_NUM_FIXED = 2, }; -#define VHOST_VIRTIO 0xAF -#define VHOST_SCSI_SET_ENDPOINT _IOW(VHOST_VIRTIO, 0x40, struct vhost_scsi_target) -#define VHOST_SCSI_CLEAR_ENDPOINT _IOW(VHOST_VIRTIO, 0x41, struct vhost_scsi_target) -#define VHOST_SCSI_GET_ABI_VERSION _IOW(VHOST_VIRTIO, 0x42, int) - #define TYPE_VHOST_SCSI "vhost-scsi" #define VHOST_SCSI(obj) \ OBJECT_CHECK(VHostSCSI, (obj), TYPE_VHOST_SCSI) diff --git a/qemu/include/hw/virtio/vhost.h b/qemu/include/hw/virtio/vhost.h index dd510509e..b60d7585b 100644 --- a/qemu/include/hw/virtio/vhost.h +++ b/qemu/include/hw/virtio/vhost.h @@ -31,7 +31,8 @@ typedef unsigned long vhost_log_chunk_t; struct vhost_log { unsigned long long size; int refcnt; - vhost_log_chunk_t log[0]; + int fd; + vhost_log_chunk_t *log; }; struct vhost_memory; @@ -44,12 +45,14 @@ struct vhost_dev { int nvqs; /* the first virtqueue which would be used by this vhost dev */ int vq_index; - unsigned long long features; - unsigned long long acked_features; - unsigned long long backend_features; + uint64_t features; + uint64_t acked_features; + uint64_t backend_features; + uint64_t protocol_features; + uint64_t max_queues; bool started; bool log_enabled; - unsigned long long log_size; + uint64_t log_size; Error *migration_blocker; bool memory_changed; hwaddr mem_changed_start_addr; @@ -57,12 +60,12 @@ struct vhost_dev { const VhostOps *vhost_ops; void *opaque; struct vhost_log *log; + QLIST_ENTRY(vhost_dev) entry; }; int vhost_dev_init(struct vhost_dev *hdev, void *opaque, VhostBackendType backend_type); void vhost_dev_cleanup(struct vhost_dev *hdev); -bool vhost_dev_query(struct vhost_dev *hdev, VirtIODevice *vdev); int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev); void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev); int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev); @@ -81,4 +84,5 @@ uint64_t vhost_get_features(struct vhost_dev *hdev, const int *feature_bits, uint64_t features); void vhost_ack_features(struct vhost_dev *hdev, const int *feature_bits, uint64_t features); +bool vhost_has_free_slot(void); #endif diff --git a/qemu/include/hw/virtio/virtio-9p.h b/qemu/include/hw/virtio/virtio-9p.h deleted file mode 100644 index 65789db13..000000000 --- a/qemu/include/hw/virtio/virtio-9p.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Virtio 9p - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Aneesh Kumar K.V - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#ifndef QEMU_VIRTIO_9P_DEVICE_H -#define QEMU_VIRTIO_9P_DEVICE_H - -typedef struct V9fsConf -{ - /* tag name for the device */ - char *tag; - char *fsdev_id; -} V9fsConf; - -#endif diff --git a/qemu/include/hw/virtio/virtio-access.h b/qemu/include/hw/virtio/virtio-access.h index 1ec1dfdb6..8dc84f520 100644 --- a/qemu/include/hw/virtio/virtio-access.h +++ b/qemu/include/hw/virtio/virtio-access.h @@ -19,32 +19,19 @@ static inline bool virtio_access_is_big_endian(VirtIODevice *vdev) { - if (virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) { - /* Devices conforming to VIRTIO 1.0 or later are always LE. */ - return false; - } #if defined(TARGET_IS_BIENDIAN) return virtio_is_big_endian(vdev); #elif defined(TARGET_WORDS_BIGENDIAN) + if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) { + /* Devices conforming to VIRTIO 1.0 or later are always LE. */ + return false; + } return true; #else return false; #endif } -static inline bool virtio_legacy_is_cross_endian(VirtIODevice *vdev) -{ -#ifdef TARGET_IS_BIENDIAN -#ifdef HOST_WORDS_BIGENDIAN - return !virtio_is_big_endian(vdev); -#else - return virtio_is_big_endian(vdev); -#endif -#else - return false; -#endif -} - static inline uint16_t virtio_lduw_phys(VirtIODevice *vdev, hwaddr pa) { if (virtio_access_is_big_endian(vdev)) { @@ -143,15 +130,6 @@ static inline uint64_t virtio_ldq_p(VirtIODevice *vdev, const void *ptr) } } -static inline bool virtio_needs_swap(VirtIODevice *vdev) -{ -#ifdef HOST_WORDS_BIGENDIAN - return virtio_access_is_big_endian(vdev) ? false : true; -#else - return virtio_access_is_big_endian(vdev) ? true : false; -#endif -} - static inline uint16_t virtio_tswap16(VirtIODevice *vdev, uint16_t s) { #ifdef HOST_WORDS_BIGENDIAN diff --git a/qemu/include/hw/virtio/virtio-balloon.h b/qemu/include/hw/virtio/virtio-balloon.h index 09c2ce4dc..35f62ac11 100644 --- a/qemu/include/hw/virtio/virtio-balloon.h +++ b/qemu/include/hw/virtio/virtio-balloon.h @@ -37,7 +37,7 @@ typedef struct VirtIOBalloon { uint32_t num_pages; uint32_t actual; uint64_t stats[VIRTIO_BALLOON_S_NR]; - VirtQueueElement stats_vq_elem; + VirtQueueElement *stats_vq_elem; size_t stats_vq_offset; QEMUTimer *stats_timer; int64_t stats_last_update; diff --git a/qemu/include/hw/virtio/virtio-blk.h b/qemu/include/hw/virtio/virtio-blk.h index 6bf5905c5..8f2b05651 100644 --- a/qemu/include/hw/virtio/virtio-blk.h +++ b/qemu/include/hw/virtio/virtio-blk.h @@ -37,7 +37,6 @@ struct VirtIOBlkConf char *serial; uint32_t scsi; uint32_t config_wce; - uint32_t data_plane; uint32_t request_merging; }; @@ -54,16 +53,15 @@ typedef struct VirtIOBlock { unsigned short sector_mask; bool original_wce; VMChangeStateEntry *change; - /* Function to push to vq and notify guest */ - void (*complete_request)(struct VirtIOBlockReq *req, unsigned char status); - Notifier migration_state_notifier; + bool dataplane_disabled; + bool dataplane_started; struct VirtIOBlockDataPlane *dataplane; } VirtIOBlock; typedef struct VirtIOBlockReq { + VirtQueueElement elem; int64_t sector_num; VirtIOBlock *dev; - VirtQueueElement elem; struct virtio_blk_inhdr *in; struct virtio_blk_outhdr out; QEMUIOVector qiov; @@ -81,12 +79,13 @@ typedef struct MultiReqBuffer { bool is_write; } MultiReqBuffer; -VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s); - +void virtio_blk_init_request(VirtIOBlock *s, VirtIOBlockReq *req); void virtio_blk_free_request(VirtIOBlockReq *req); void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb); void virtio_blk_submit_multireq(BlockBackend *blk, MultiReqBuffer *mrb); +void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq); + #endif diff --git a/qemu/include/hw/virtio/virtio-bus.h b/qemu/include/hw/virtio/virtio-bus.h index 8811415fa..3f2c1363d 100644 --- a/qemu/include/hw/virtio/virtio-bus.h +++ b/qemu/include/hw/virtio/virtio-bus.h @@ -44,9 +44,12 @@ typedef struct VirtioBusClass { void (*notify)(DeviceState *d, uint16_t vector); void (*save_config)(DeviceState *d, QEMUFile *f); void (*save_queue)(DeviceState *d, int n, QEMUFile *f); + void (*save_extra_state)(DeviceState *d, QEMUFile *f); int (*load_config)(DeviceState *d, QEMUFile *f); int (*load_queue)(DeviceState *d, int n, QEMUFile *f); int (*load_done)(DeviceState *d, QEMUFile *f); + int (*load_extra_state)(DeviceState *d, QEMUFile *f); + bool (*has_extra_state)(DeviceState *d); bool (*query_guest_notifiers)(DeviceState *d); int (*set_guest_notifiers)(DeviceState *d, int nvqs, bool assign); int (*set_host_notifier)(DeviceState *d, int n, bool assigned); @@ -56,6 +59,11 @@ typedef struct VirtioBusClass { * This is called by virtio-bus just after the device is plugged. */ void (*device_plugged)(DeviceState *d, Error **errp); + /* + * Re-evaluate setup after feature bits have been validated + * by the device backend. + */ + void (*post_plugged)(DeviceState *d, Error **errp); /* * transport independent exit function. * This is called by virtio-bus just before the device is unplugged. diff --git a/qemu/include/hw/virtio/virtio-gpu.h b/qemu/include/hw/virtio/virtio-gpu.h index 889676147..13b0ab084 100644 --- a/qemu/include/hw/virtio/virtio-gpu.h +++ b/qemu/include/hw/virtio/virtio-gpu.h @@ -56,8 +56,19 @@ struct virtio_gpu_requested_state { int x, y; }; +enum virtio_gpu_conf_flags { + VIRTIO_GPU_FLAG_VIRGL_ENABLED = 1, + VIRTIO_GPU_FLAG_STATS_ENABLED, +}; + +#define virtio_gpu_virgl_enabled(_cfg) \ + (_cfg.flags & (1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED)) +#define virtio_gpu_stats_enabled(_cfg) \ + (_cfg.flags & (1 << VIRTIO_GPU_FLAG_STATS_ENABLED)) + struct virtio_gpu_conf { uint32_t max_outputs; + uint32_t flags; }; struct virtio_gpu_ctrl_command { @@ -65,6 +76,7 @@ struct virtio_gpu_ctrl_command { VirtQueue *vq; struct virtio_gpu_ctrl_hdr cmd_hdr; uint32_t error; + bool waiting; bool finished; QTAILQ_ENTRY(virtio_gpu_ctrl_command) next; }; @@ -83,6 +95,7 @@ typedef struct VirtIOGPU { DeviceState *qdev; QTAILQ_HEAD(, virtio_gpu_simple_resource) reslist; + QTAILQ_HEAD(, virtio_gpu_ctrl_command) cmdq; QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq; struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUT]; @@ -92,11 +105,14 @@ typedef struct VirtIOGPU { int enabled_output_bitmask; struct virtio_gpu_config virtio_config; + bool use_virgl_renderer; + bool renderer_inited; + bool renderer_blocked; QEMUTimer *fence_poll; QEMUTimer *print_stats; + uint32_t inflight; struct { - uint32_t inflight; uint32_t max_inflight; uint32_t requests; uint32_t req_3d; @@ -138,5 +154,13 @@ int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab, struct virtio_gpu_ctrl_command *cmd, struct iovec **iov); void virtio_gpu_cleanup_mapping_iov(struct iovec *iov, uint32_t count); +void virtio_gpu_process_cmdq(VirtIOGPU *g); + +/* virtio-gpu-3d.c */ +void virtio_gpu_virgl_process_cmd(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd); +void virtio_gpu_virgl_fence_poll(VirtIOGPU *g); +void virtio_gpu_virgl_reset(VirtIOGPU *g); +int virtio_gpu_virgl_init(VirtIOGPU *g); #endif diff --git a/qemu/include/hw/virtio/virtio-input.h b/qemu/include/hw/virtio/virtio-input.h index af1c207ab..bddbd4b28 100644 --- a/qemu/include/hw/virtio/virtio-input.h +++ b/qemu/include/hw/virtio/virtio-input.h @@ -13,20 +13,6 @@ typedef struct virtio_input_absinfo virtio_input_absinfo; typedef struct virtio_input_config virtio_input_config; typedef struct virtio_input_event virtio_input_event; -#if defined(HOST_WORDS_BIGENDIAN) -# define const_le32(_x) \ - (((_x & 0x000000ffU) << 24) | \ - ((_x & 0x0000ff00U) << 8) | \ - ((_x & 0x00ff0000U) >> 8) | \ - ((_x & 0xff000000U) >> 24)) -# define const_le16(_x) \ - (((_x & 0x00ff) << 8) | \ - ((_x & 0xff00) >> 8)) -#else -# define const_le32(_x) (_x) -# define const_le16(_x) (_x) -#endif - /* ----------------------------------------------------------------- */ /* qemu internals */ @@ -111,6 +97,9 @@ struct VirtIOInputHost { void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event); void virtio_input_init_config(VirtIOInput *vinput, virtio_input_config *config); +virtio_input_config *virtio_input_find_config(VirtIOInput *vinput, + uint8_t select, + uint8_t subsel); void virtio_input_add_config(VirtIOInput *vinput, virtio_input_config *config); void virtio_input_idstr_config(VirtIOInput *vinput, diff --git a/qemu/include/hw/virtio/virtio-net.h b/qemu/include/hw/virtio/virtio-net.h index 60b11d5c2..0cabdb682 100644 --- a/qemu/include/hw/virtio/virtio-net.h +++ b/qemu/include/hw/virtio/virtio-net.h @@ -47,8 +47,7 @@ typedef struct VirtIONetQueue { QEMUBH *tx_bh; int tx_waiting; struct { - VirtQueueElement elem; - ssize_t len; + VirtQueueElement *elem; } async_tx; struct VirtIONet *n; } VirtIONetQueue; @@ -95,6 +94,7 @@ typedef struct VirtIONet { uint64_t curr_guest_offloads; QEMUTimer *announce_timer; int announce_counter; + bool needs_vnet_hdr_swap; } VirtIONet; void virtio_net_set_netclient_name(VirtIONet *n, const char *name, diff --git a/qemu/include/hw/virtio/virtio-scsi.h b/qemu/include/hw/virtio/virtio-scsi.h index 088fe9f4b..ba2f5ce07 100644 --- a/qemu/include/hw/virtio/virtio-scsi.h +++ b/qemu/include/hw/virtio/virtio-scsi.h @@ -22,7 +22,6 @@ #include "hw/pci/pci.h" #include "hw/scsi/scsi.h" #include "sysemu/iothread.h" -#include "hw/virtio/dataplane/vring.h" #define TYPE_VIRTIO_SCSI_COMMON "virtio-scsi-common" #define VIRTIO_SCSI_COMMON(obj) \ @@ -58,13 +57,6 @@ struct VirtIOSCSIConf { struct VirtIOSCSI; -typedef struct { - struct VirtIOSCSI *parent; - Vring vring; - EventNotifier host_notifier; - EventNotifier guest_notifier; -} VirtIOSCSIVring; - typedef struct VirtIOSCSICommon { VirtIODevice parent_obj; VirtIOSCSIConf conf; @@ -76,6 +68,13 @@ typedef struct VirtIOSCSICommon { VirtQueue **cmd_vqs; } VirtIOSCSICommon; +typedef struct VirtIOSCSIBlkChangeNotifier { + Notifier n; + struct VirtIOSCSI *s; + SCSIDevice *sd; + QTAILQ_ENTRY(VirtIOSCSIBlkChangeNotifier) next; +} VirtIOSCSIBlkChangeNotifier; + typedef struct VirtIOSCSI { VirtIOSCSICommon parent_obj; @@ -86,37 +85,29 @@ typedef struct VirtIOSCSI { /* Fields for dataplane below */ AioContext *ctx; /* one iothread per virtio-scsi-pci for now */ - /* Vring is used instead of vq in dataplane code, because of the underlying - * memory layer thread safety */ - VirtIOSCSIVring *ctrl_vring; - VirtIOSCSIVring *event_vring; - VirtIOSCSIVring **cmd_vrings; + QTAILQ_HEAD(, VirtIOSCSIBlkChangeNotifier) insert_notifiers; + QTAILQ_HEAD(, VirtIOSCSIBlkChangeNotifier) remove_notifiers; + bool dataplane_started; bool dataplane_starting; bool dataplane_stopping; - bool dataplane_disabled; bool dataplane_fenced; Error *blocker; - Notifier migration_state_notifier; uint32_t host_features; } VirtIOSCSI; typedef struct VirtIOSCSIReq { + /* Note: + * - fields up to resp_iov are initialized by virtio_scsi_init_req; + * - fields starting at vring are zeroed by virtio_scsi_init_req. + * */ + VirtQueueElement elem; + VirtIOSCSI *dev; VirtQueue *vq; QEMUSGList qsgl; QEMUIOVector resp_iov; - /* Note: - * - fields before elem are initialized by virtio_scsi_init_req; - * - elem is uninitialized at the time of allocation. - * - fields after elem are zeroed by virtio_scsi_init_req. - * */ - - VirtQueueElement elem; - /* Set by dataplane code. */ - VirtIOSCSIVring *vring; - union { /* Used for two-stage request submission */ QTAILQ_ENTRY(VirtIOSCSIReq) next; @@ -148,10 +139,10 @@ void virtio_scsi_common_realize(DeviceState *dev, Error **errp, HandleOutput cmd); void virtio_scsi_common_unrealize(DeviceState *dev, Error **errp); -void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req); -bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req); -void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req); -VirtIOSCSIReq *virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq); +void virtio_scsi_handle_event_vq(VirtIOSCSI *s, VirtQueue *vq); +void virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq); +void virtio_scsi_handle_ctrl_vq(VirtIOSCSI *s, VirtQueue *vq); +void virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq, VirtIOSCSIReq *req); void virtio_scsi_free_req(VirtIOSCSIReq *req); void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, uint32_t event, uint32_t reason); @@ -159,8 +150,6 @@ void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, void virtio_scsi_set_iothread(VirtIOSCSI *s, IOThread *iothread); void virtio_scsi_dataplane_start(VirtIOSCSI *s); void virtio_scsi_dataplane_stop(VirtIOSCSI *s); -void virtio_scsi_vring_push_notify(VirtIOSCSIReq *req); -VirtIOSCSIReq *virtio_scsi_pop_req_vring(VirtIOSCSI *s, - VirtIOSCSIVring *vring); +void virtio_scsi_dataplane_notify(VirtIODevice *vdev, VirtIOSCSIReq *req); #endif /* _QEMU_VIRTIO_SCSI_H */ diff --git a/qemu/include/hw/virtio/virtio-serial.h b/qemu/include/hw/virtio/virtio-serial.h index 527d0bf62..12a55a19e 100644 --- a/qemu/include/hw/virtio/virtio-serial.h +++ b/qemu/include/hw/virtio/virtio-serial.h @@ -122,7 +122,7 @@ struct VirtIOSerialPort { * element popped and continue consuming it once the backend * becomes writable again. */ - VirtQueueElement elem; + VirtQueueElement *elem; /* * The index and the offset into the iov buffer that was popped in diff --git a/qemu/include/hw/virtio/virtio.h b/qemu/include/hw/virtio/virtio.h index cccae89d8..6a37065c2 100644 --- a/qemu/include/hw/virtio/virtio.h +++ b/qemu/include/hw/virtio/virtio.h @@ -46,10 +46,10 @@ typedef struct VirtQueueElement unsigned int index; unsigned int out_num; unsigned int in_num; - hwaddr in_addr[VIRTQUEUE_MAX_SIZE]; - hwaddr out_addr[VIRTQUEUE_MAX_SIZE]; - struct iovec in_sg[VIRTQUEUE_MAX_SIZE]; - struct iovec out_sg[VIRTQUEUE_MAX_SIZE]; + hwaddr *in_addr; + hwaddr *out_addr; + struct iovec *in_sg; + struct iovec *out_sg; } VirtQueueElement; #define VIRTIO_QUEUE_MAX 1024 @@ -90,6 +90,7 @@ struct VirtIODevice VMChangeStateEntry *vmstate; char *bus_name; uint8_t device_endian; + bool use_guest_notifier_mask; QLIST_HEAD(, VirtQueue) *vector_queues; }; @@ -143,21 +144,26 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, void virtio_del_queue(VirtIODevice *vdev, int n); +void *virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_num); void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem, unsigned int len); void virtqueue_flush(VirtQueue *vq, unsigned int count); +void virtqueue_discard(VirtQueue *vq, const VirtQueueElement *elem, + unsigned int len); void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, unsigned int len, unsigned int idx); -void virtqueue_map_sg(struct iovec *sg, hwaddr *addr, - size_t num_sg, int is_write); -int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem); +void virtqueue_map(VirtQueueElement *elem); +void *virtqueue_pop(VirtQueue *vq, size_t sz); +void *qemu_get_virtqueue_element(QEMUFile *f, size_t sz); +void qemu_put_virtqueue_element(QEMUFile *f, VirtQueueElement *elem); int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes, unsigned int out_bytes); void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, unsigned int *out_bytes, unsigned max_in_bytes, unsigned max_out_bytes); +bool virtio_should_notify(VirtIODevice *vdev, VirtQueue *vq); void virtio_notify(VirtIODevice *vdev, VirtQueue *vq); void virtio_save(VirtIODevice *vdev, QEMUFile *f); @@ -244,7 +250,9 @@ void virtio_queue_set_guest_notifier_fd_handler(VirtQueue *vq, bool assign, EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq); void virtio_queue_set_host_notifier_fd_handler(VirtQueue *vq, bool assign, bool set_handler); -void virtio_queue_notify_vq(VirtQueue *vq); +void virtio_queue_aio_set_host_notifier_handler(VirtQueue *vq, AioContext *ctx, + void (*fn)(VirtIODevice *, + VirtQueue *)); void virtio_irq(VirtQueue *vq); VirtQueue *virtio_vector_first_queue(VirtIODevice *vdev, uint16_t vector); VirtQueue *virtio_vector_next_queue(VirtQueue *vq); @@ -261,26 +269,27 @@ static inline void virtio_clear_feature(uint64_t *features, unsigned int fbit) *features &= ~(1ULL << fbit); } -static inline bool __virtio_has_feature(uint64_t features, unsigned int fbit) +static inline bool virtio_has_feature(uint64_t features, unsigned int fbit) { assert(fbit < 64); return !!(features & (1ULL << fbit)); } -static inline bool virtio_has_feature(VirtIODevice *vdev, unsigned int fbit) +static inline bool virtio_vdev_has_feature(VirtIODevice *vdev, + unsigned int fbit) { - return __virtio_has_feature(vdev->guest_features, fbit); + return virtio_has_feature(vdev->guest_features, fbit); } static inline bool virtio_host_has_feature(VirtIODevice *vdev, unsigned int fbit) { - return __virtio_has_feature(vdev->host_features, fbit); + return virtio_has_feature(vdev->host_features, fbit); } static inline bool virtio_is_big_endian(VirtIODevice *vdev) { - if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) { + if (!virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) { assert(vdev->device_endian != VIRTIO_DEVICE_ENDIAN_UNKNOWN); return vdev->device_endian == VIRTIO_DEVICE_ENDIAN_BIG; } diff --git a/qemu/include/hw/xen/xen.h b/qemu/include/hw/xen/xen.h index 4356af456..6eb815aac 100644 --- a/qemu/include/hw/xen/xen.h +++ b/qemu/include/hw/xen/xen.h @@ -6,7 +6,6 @@ * must not depend on any xen headers being present in * /usr/include/xen, so it can be included unconditionally. */ -#include #include "hw/irq.h" #include "qemu-common.h" @@ -32,23 +31,19 @@ int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num); void xen_piix3_set_irq(void *opaque, int irq_num, int level); void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len); void xen_hvm_inject_msi(uint64_t addr, uint32_t data); +int xen_is_pirq_msi(uint32_t msi_data); qemu_irq *xen_interrupt_controller_init(void); void xenstore_store_pv_console_info(int i, struct CharDriverState *chr); #if defined(NEED_CPU_H) && !defined(CONFIG_USER_ONLY) -int xen_hvm_init(ram_addr_t *below_4g_mem_size, ram_addr_t *above_4g_mem_size, - MemoryRegion **ram_memory); +void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory); void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, - struct MemoryRegion *mr); + struct MemoryRegion *mr, Error **errp); void xen_modified_memory(ram_addr_t start, ram_addr_t length); #endif void xen_register_framebuffer(struct MemoryRegion *mr); -#if defined(CONFIG_XEN) && CONFIG_XEN_CTRL_INTERFACE_VERSION < 400 -# define HVM_MAX_VCPUS 32 -#endif - #endif /* QEMU_HW_XEN_H */ diff --git a/qemu/include/hw/xen/xen_backend.h b/qemu/include/hw/xen/xen_backend.h index 3b4125e39..c839eeb48 100644 --- a/qemu/include/hw/xen/xen_backend.h +++ b/qemu/include/hw/xen/xen_backend.h @@ -46,8 +46,8 @@ struct XenDevice { int remote_port; int local_port; - XenEvtchn evtchndev; - XenGnttab gnttabdev; + xenevtchn_handle *evtchndev; + xengnttab_handle *gnttabdev; struct XenDevOps *ops; QTAILQ_ENTRY(XenDevice) next; @@ -56,7 +56,8 @@ struct XenDevice { /* ------------------------------------------------------------- */ /* variables */ -extern XenXC xen_xc; +extern xc_interface *xen_xc; +extern xenforeignmemory_handle *xen_fmem; extern struct xs_handle *xenstore; extern const char *xen_protocol; diff --git a/qemu/include/hw/xen/xen_common.h b/qemu/include/hw/xen/xen_common.h index ed5fd3e1a..bd65e6705 100644 --- a/qemu/include/hw/xen/xen_common.h +++ b/qemu/include/hw/xen/xen_common.h @@ -1,17 +1,19 @@ #ifndef QEMU_HW_XEN_COMMON_H #define QEMU_HW_XEN_COMMON_H 1 -#include "config-host.h" -#include -#include + +/* + * If we have new enough libxenctrl then we do not want/need these compat + * interfaces, despite what the user supplied cflags might say. They + * must be undefined before including xenctrl.h + */ +#undef XC_WANT_COMPAT_EVTCHN_API +#undef XC_WANT_COMPAT_GNTTAB_API +#undef XC_WANT_COMPAT_MAP_FOREIGN_API #include -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 420 -# include -#else -# include -#endif +#include #include #include "hw/hw.h" @@ -21,144 +23,54 @@ #include "trace.h" /* - * We don't support Xen prior to 3.3.0. + * We don't support Xen prior to 4.2.0. */ -/* Xen before 4.0 */ -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 400 -static inline void *xc_map_foreign_bulk(int xc_handle, uint32_t dom, int prot, - xen_pfn_t *arr, int *err, - unsigned int num) -{ - return xc_map_foreign_batch(xc_handle, dom, prot, arr, num); -} -#endif - - -/* Xen before 4.1 */ -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 410 - -typedef int XenXC; -typedef int XenEvtchn; -typedef int XenGnttab; - -# define XC_INTERFACE_FMT "%i" -# define XC_HANDLER_INITIAL_VALUE -1 - -static inline XenEvtchn xen_xc_evtchn_open(void *logger, - unsigned int open_flags) -{ - return xc_evtchn_open(); -} - -static inline XenGnttab xen_xc_gnttab_open(void *logger, - unsigned int open_flags) -{ - return xc_gnttab_open(); -} - -static inline XenXC xen_xc_interface_open(void *logger, void *dombuild_logger, - unsigned int open_flags) -{ - return xc_interface_open(); -} - -static inline int xc_fd(int xen_xc) -{ - return xen_xc; -} - - -static inline int xc_domain_populate_physmap_exact - (XenXC xc_handle, uint32_t domid, unsigned long nr_extents, - unsigned int extent_order, unsigned int mem_flags, xen_pfn_t *extent_start) -{ - return xc_domain_memory_populate_physmap - (xc_handle, domid, nr_extents, extent_order, mem_flags, extent_start); -} - -static inline int xc_domain_add_to_physmap(int xc_handle, uint32_t domid, - unsigned int space, unsigned long idx, - xen_pfn_t gpfn) -{ - struct xen_add_to_physmap xatp = { - .domid = domid, - .space = space, - .idx = idx, - .gpfn = gpfn, - }; - - return xc_memory_op(xc_handle, XENMEM_add_to_physmap, &xatp); -} - -static inline struct xs_handle *xs_open(unsigned long flags) -{ - return xs_daemon_open(); -} - -static inline void xs_close(struct xs_handle *xsh) -{ - if (xsh != NULL) { - xs_daemon_close(xsh); - } -} +/* Xen 4.2 thru 4.6 */ +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 471 +typedef xc_interface xenforeignmemory_handle; +typedef xc_evtchn xenevtchn_handle; +typedef xc_gnttab xengnttab_handle; -/* Xen 4.1 */ -#else +#define xenevtchn_open(l, f) xc_evtchn_open(l, f); +#define xenevtchn_close(h) xc_evtchn_close(h) +#define xenevtchn_fd(h) xc_evtchn_fd(h) +#define xenevtchn_pending(h) xc_evtchn_pending(h) +#define xenevtchn_notify(h, p) xc_evtchn_notify(h, p) +#define xenevtchn_bind_interdomain(h, d, p) xc_evtchn_bind_interdomain(h, d, p) +#define xenevtchn_unmask(h, p) xc_evtchn_unmask(h, p) +#define xenevtchn_unbind(h, p) xc_evtchn_unbind(h, p) -typedef xc_interface *XenXC; -typedef xc_evtchn *XenEvtchn; -typedef xc_gnttab *XenGnttab; +#define xengnttab_open(l, f) xc_gnttab_open(l, f) +#define xengnttab_close(h) xc_gnttab_close(h) +#define xengnttab_set_max_grants(h, n) xc_gnttab_set_max_grants(h, n) +#define xengnttab_map_grant_ref(h, d, r, p) xc_gnttab_map_grant_ref(h, d, r, p) +#define xengnttab_unmap(h, a, n) xc_gnttab_munmap(h, a, n) +#define xengnttab_map_grant_refs(h, c, d, r, p) \ + xc_gnttab_map_grant_refs(h, c, d, r, p) -# define XC_INTERFACE_FMT "%p" -# define XC_HANDLER_INITIAL_VALUE NULL +#define xenforeignmemory_open(l, f) xen_xc -static inline XenEvtchn xen_xc_evtchn_open(void *logger, - unsigned int open_flags) +static inline void *xenforeignmemory_map(xc_interface *h, uint32_t dom, + int prot, size_t pages, + const xen_pfn_t arr[/*pages*/], + int err[/*pages*/]) { - return xc_evtchn_open(logger, open_flags); + if (err) + return xc_map_foreign_bulk(h, dom, prot, arr, err, pages); + else + return xc_map_foreign_pages(h, dom, prot, arr, pages); } -static inline XenGnttab xen_xc_gnttab_open(void *logger, - unsigned int open_flags) -{ - return xc_gnttab_open(logger, open_flags); -} +#define xenforeignmemory_unmap(h, p, s) munmap(p, s * XC_PAGE_SIZE) -static inline XenXC xen_xc_interface_open(void *logger, void *dombuild_logger, - unsigned int open_flags) -{ - return xc_interface_open(logger, dombuild_logger, open_flags); -} +#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 471 */ -/* FIXME There is now way to have the xen fd */ -static inline int xc_fd(xc_interface *xen_xc) -{ - return -1; -} -#endif +#include +#include +#include -/* Xen before 4.2 */ -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 420 -static inline int xen_xc_hvm_inject_msi(XenXC xen_xc, domid_t dom, - uint64_t addr, uint32_t data) -{ - return -ENOSYS; -} -/* The followings are only to compile op_discard related code on older - * Xen releases. */ -#define BLKIF_OP_DISCARD 5 -struct blkif_request_discard { - uint64_t nr_sectors; - uint64_t sector_number; -}; -#else -static inline int xen_xc_hvm_inject_msi(XenXC xen_xc, domid_t dom, - uint64_t addr, uint32_t data) -{ - return xc_hvm_inject_msi(xen_xc, dom, addr, data); -} #endif void destroy_hvm_domain(bool reboot); @@ -167,7 +79,7 @@ void destroy_hvm_domain(bool reboot); void xen_shutdown_fatal_error(const char *fmt, ...) GCC_FMT_ATTR(1, 2); #ifdef HVM_PARAM_VMPORT_REGS_PFN -static inline int xen_get_vmport_regs_pfn(XenXC xc, domid_t dom, +static inline int xen_get_vmport_regs_pfn(xc_interface *xc, domid_t dom, xen_pfn_t *vmport_regs_pfn) { int rc; @@ -179,13 +91,22 @@ static inline int xen_get_vmport_regs_pfn(XenXC xc, domid_t dom, return rc; } #else -static inline int xen_get_vmport_regs_pfn(XenXC xc, domid_t dom, +static inline int xen_get_vmport_regs_pfn(xc_interface *xc, domid_t dom, xen_pfn_t *vmport_regs_pfn) { return -ENOSYS; } #endif +/* Xen before 4.6 */ +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 460 + +#ifndef HVM_IOREQSRV_BUFIOREQ_ATOMIC +#define HVM_IOREQSRV_BUFIOREQ_ATOMIC 2 +#endif + +#endif + /* Xen before 4.5 */ #if CONFIG_XEN_CTRL_INTERFACE_VERSION < 450 @@ -197,54 +118,54 @@ static inline int xen_get_vmport_regs_pfn(XenXC xc, domid_t dom, typedef uint16_t ioservid_t; -static inline void xen_map_memory_section(XenXC xc, domid_t dom, +static inline void xen_map_memory_section(xc_interface *xc, domid_t dom, ioservid_t ioservid, MemoryRegionSection *section) { } -static inline void xen_unmap_memory_section(XenXC xc, domid_t dom, +static inline void xen_unmap_memory_section(xc_interface *xc, domid_t dom, ioservid_t ioservid, MemoryRegionSection *section) { } -static inline void xen_map_io_section(XenXC xc, domid_t dom, +static inline void xen_map_io_section(xc_interface *xc, domid_t dom, ioservid_t ioservid, MemoryRegionSection *section) { } -static inline void xen_unmap_io_section(XenXC xc, domid_t dom, +static inline void xen_unmap_io_section(xc_interface *xc, domid_t dom, ioservid_t ioservid, MemoryRegionSection *section) { } -static inline void xen_map_pcidev(XenXC xc, domid_t dom, +static inline void xen_map_pcidev(xc_interface *xc, domid_t dom, ioservid_t ioservid, PCIDevice *pci_dev) { } -static inline void xen_unmap_pcidev(XenXC xc, domid_t dom, +static inline void xen_unmap_pcidev(xc_interface *xc, domid_t dom, ioservid_t ioservid, PCIDevice *pci_dev) { } -static inline int xen_create_ioreq_server(XenXC xc, domid_t dom, +static inline int xen_create_ioreq_server(xc_interface *xc, domid_t dom, ioservid_t *ioservid) { return 0; } -static inline void xen_destroy_ioreq_server(XenXC xc, domid_t dom, +static inline void xen_destroy_ioreq_server(xc_interface *xc, domid_t dom, ioservid_t ioservid) { } -static inline int xen_get_ioreq_server_info(XenXC xc, domid_t dom, +static inline int xen_get_ioreq_server_info(xc_interface *xc, domid_t dom, ioservid_t ioservid, xen_pfn_t *ioreq_pfn, xen_pfn_t *bufioreq_pfn, @@ -281,7 +202,7 @@ static inline int xen_get_ioreq_server_info(XenXC xc, domid_t dom, return 0; } -static inline int xen_set_ioreq_server_state(XenXC xc, domid_t dom, +static inline int xen_set_ioreq_server_state(xc_interface *xc, domid_t dom, ioservid_t ioservid, bool enable) { @@ -291,7 +212,7 @@ static inline int xen_set_ioreq_server_state(XenXC xc, domid_t dom, /* Xen 4.5 */ #else -static inline void xen_map_memory_section(XenXC xc, domid_t dom, +static inline void xen_map_memory_section(xc_interface *xc, domid_t dom, ioservid_t ioservid, MemoryRegionSection *section) { @@ -304,7 +225,7 @@ static inline void xen_map_memory_section(XenXC xc, domid_t dom, start_addr, end_addr); } -static inline void xen_unmap_memory_section(XenXC xc, domid_t dom, +static inline void xen_unmap_memory_section(xc_interface *xc, domid_t dom, ioservid_t ioservid, MemoryRegionSection *section) { @@ -317,7 +238,7 @@ static inline void xen_unmap_memory_section(XenXC xc, domid_t dom, start_addr, end_addr); } -static inline void xen_map_io_section(XenXC xc, domid_t dom, +static inline void xen_map_io_section(xc_interface *xc, domid_t dom, ioservid_t ioservid, MemoryRegionSection *section) { @@ -330,7 +251,7 @@ static inline void xen_map_io_section(XenXC xc, domid_t dom, start_addr, end_addr); } -static inline void xen_unmap_io_section(XenXC xc, domid_t dom, +static inline void xen_unmap_io_section(xc_interface *xc, domid_t dom, ioservid_t ioservid, MemoryRegionSection *section) { @@ -343,7 +264,7 @@ static inline void xen_unmap_io_section(XenXC xc, domid_t dom, start_addr, end_addr); } -static inline void xen_map_pcidev(XenXC xc, domid_t dom, +static inline void xen_map_pcidev(xc_interface *xc, domid_t dom, ioservid_t ioservid, PCIDevice *pci_dev) { @@ -355,7 +276,7 @@ static inline void xen_map_pcidev(XenXC xc, domid_t dom, PCI_FUNC(pci_dev->devfn)); } -static inline void xen_unmap_pcidev(XenXC xc, domid_t dom, +static inline void xen_unmap_pcidev(xc_interface *xc, domid_t dom, ioservid_t ioservid, PCIDevice *pci_dev) { @@ -367,10 +288,11 @@ static inline void xen_unmap_pcidev(XenXC xc, domid_t dom, PCI_FUNC(pci_dev->devfn)); } -static inline int xen_create_ioreq_server(XenXC xc, domid_t dom, +static inline int xen_create_ioreq_server(xc_interface *xc, domid_t dom, ioservid_t *ioservid) { - int rc = xc_hvm_create_ioreq_server(xc, dom, 1, ioservid); + int rc = xc_hvm_create_ioreq_server(xc, dom, HVM_IOREQSRV_BUFIOREQ_ATOMIC, + ioservid); if (rc == 0) { trace_xen_ioreq_server_create(*ioservid); @@ -379,14 +301,14 @@ static inline int xen_create_ioreq_server(XenXC xc, domid_t dom, return rc; } -static inline void xen_destroy_ioreq_server(XenXC xc, domid_t dom, +static inline void xen_destroy_ioreq_server(xc_interface *xc, domid_t dom, ioservid_t ioservid) { trace_xen_ioreq_server_destroy(ioservid); xc_hvm_destroy_ioreq_server(xc, dom, ioservid); } -static inline int xen_get_ioreq_server_info(XenXC xc, domid_t dom, +static inline int xen_get_ioreq_server_info(xc_interface *xc, domid_t dom, ioservid_t ioservid, xen_pfn_t *ioreq_pfn, xen_pfn_t *bufioreq_pfn, @@ -397,7 +319,7 @@ static inline int xen_get_ioreq_server_info(XenXC xc, domid_t dom, bufioreq_evtchn); } -static inline int xen_set_ioreq_server_state(XenXC xc, domid_t dom, +static inline int xen_set_ioreq_server_state(xc_interface *xc, domid_t dom, ioservid_t ioservid, bool enable) { @@ -407,4 +329,44 @@ static inline int xen_set_ioreq_server_state(XenXC xc, domid_t dom, #endif +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 460 +static inline int xen_xc_domain_add_to_physmap(xc_interface *xch, uint32_t domid, + unsigned int space, + unsigned long idx, + xen_pfn_t gpfn) +{ + return xc_domain_add_to_physmap(xch, domid, space, idx, gpfn); +} +#else +static inline int xen_xc_domain_add_to_physmap(xc_interface *xch, uint32_t domid, + unsigned int space, + unsigned long idx, + xen_pfn_t gpfn) +{ + /* In Xen 4.6 rc is -1 and errno contains the error value. */ + int rc = xc_domain_add_to_physmap(xch, domid, space, idx, gpfn); + if (rc == -1) + return errno; + return rc; +} +#endif + +#ifdef CONFIG_XEN_PV_DOMAIN_BUILD +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 470 +static inline int xen_domain_create(xc_interface *xc, uint32_t ssidref, + xen_domain_handle_t handle, uint32_t flags, + uint32_t *pdomid) +{ + return xc_domain_create(xc, ssidref, handle, flags, pdomid); +} +#else +static inline int xen_domain_create(xc_interface *xc, uint32_t ssidref, + xen_domain_handle_t handle, uint32_t flags, + uint32_t *pdomid) +{ + return xc_domain_create(xc, ssidref, handle, flags, pdomid, NULL); +} +#endif +#endif + #endif /* QEMU_HW_XEN_COMMON_H */ diff --git a/qemu/include/io/channel-buffer.h b/qemu/include/io/channel-buffer.h new file mode 100644 index 000000000..65c498b2c --- /dev/null +++ b/qemu/include/io/channel-buffer.h @@ -0,0 +1,60 @@ +/* + * QEMU I/O channels memory buffer driver + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QIO_CHANNEL_BUFFER_H__ +#define QIO_CHANNEL_BUFFER_H__ + +#include "io/channel.h" + +#define TYPE_QIO_CHANNEL_BUFFER "qio-channel-buffer" +#define QIO_CHANNEL_BUFFER(obj) \ + OBJECT_CHECK(QIOChannelBuffer, (obj), TYPE_QIO_CHANNEL_BUFFER) + +typedef struct QIOChannelBuffer QIOChannelBuffer; + +/** + * QIOChannelBuffer: + * + * The QIOChannelBuffer object provides a channel implementation + * that is able to perform I/O to/from a memory buffer. + * + */ + +struct QIOChannelBuffer { + QIOChannel parent; + size_t capacity; /* Total allocated memory */ + size_t usage; /* Current size of data */ + size_t offset; /* Offset for future I/O ops */ + uint8_t *data; +}; + + +/** + * qio_channel_buffer_new: + * @capacity: the initial buffer capacity to allocate + * + * Allocate a new buffer which is initially empty + * + * Returns: the new channel object + */ +QIOChannelBuffer * +qio_channel_buffer_new(size_t capacity); + +#endif /* QIO_CHANNEL_BUFFER_H__ */ diff --git a/qemu/include/io/channel-command.h b/qemu/include/io/channel-command.h new file mode 100644 index 000000000..cfc177e78 --- /dev/null +++ b/qemu/include/io/channel-command.h @@ -0,0 +1,91 @@ +/* + * QEMU I/O channels external command driver + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QIO_CHANNEL_COMMAND_H__ +#define QIO_CHANNEL_COMMAND_H__ + +#include "io/channel.h" + +#define TYPE_QIO_CHANNEL_COMMAND "qio-channel-command" +#define QIO_CHANNEL_COMMAND(obj) \ + OBJECT_CHECK(QIOChannelCommand, (obj), TYPE_QIO_CHANNEL_COMMAND) + +typedef struct QIOChannelCommand QIOChannelCommand; + + +/** + * QIOChannelCommand: + * + * The QIOChannelCommand class provides a channel implementation + * that can transport data with an externally running command + * via its stdio streams. + */ + +struct QIOChannelCommand { + QIOChannel parent; + int writefd; + int readfd; + pid_t pid; +}; + + +/** + * qio_channel_command_new_pid: + * @writefd: the FD connected to the command's stdin + * @readfd: the FD connected to the command's stdout + * @pid: the PID of the running child command + * @errp: pointer to a NULL-initialized error object + * + * Create a channel for performing I/O with the + * previously spawned command identified by @pid. + * The two file descriptors provide the connection + * to command's stdio streams, either one or which + * may be -1 to indicate that stream is not open. + * + * The channel will take ownership of the process + * @pid and will kill it when closing the channel. + * Similarly it will take responsibility for + * closing the file descriptors @writefd and @readfd. + * + * Returns: the command channel object, or NULL on error + */ +QIOChannelCommand * +qio_channel_command_new_pid(int writefd, + int readfd, + pid_t pid); + +/** + * qio_channel_command_new_spawn: + * @argv: the NULL terminated list of command arguments + * @flags: the I/O mode, one of O_RDONLY, O_WRONLY, O_RDWR + * @errp: pointer to a NULL-initialized error object + * + * Create a channel for performing I/O with the + * command to be spawned with arguments @argv. + * + * Returns: the command channel object, or NULL on error + */ +QIOChannelCommand * +qio_channel_command_new_spawn(const char *const argv[], + int flags, + Error **errp); + + +#endif /* QIO_CHANNEL_COMMAND_H__ */ diff --git a/qemu/include/io/channel-file.h b/qemu/include/io/channel-file.h new file mode 100644 index 000000000..308e6d44d --- /dev/null +++ b/qemu/include/io/channel-file.h @@ -0,0 +1,93 @@ +/* + * QEMU I/O channels files driver + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QIO_CHANNEL_FILE_H__ +#define QIO_CHANNEL_FILE_H__ + +#include "io/channel.h" + +#define TYPE_QIO_CHANNEL_FILE "qio-channel-file" +#define QIO_CHANNEL_FILE(obj) \ + OBJECT_CHECK(QIOChannelFile, (obj), TYPE_QIO_CHANNEL_FILE) + +typedef struct QIOChannelFile QIOChannelFile; + +/** + * QIOChannelFile: + * + * The QIOChannelFile object provides a channel implementation + * that is able to perform I/O on block devices, character + * devices, FIFOs, pipes and plain files. While it is technically + * able to work on sockets too on the UNIX platform, this is not + * portable to Windows and lacks some extra sockets specific + * functionality. So the QIOChannelSocket object is recommended + * for that use case. + * + */ + +struct QIOChannelFile { + QIOChannel parent; + int fd; +}; + + +/** + * qio_channel_file_new_fd: + * @fd: the file descriptor + * + * Create a new IO channel object for a file represented + * by the @fd parameter. @fd can be associated with a + * block device, character device, fifo, pipe, or a + * regular file. For sockets, the QIOChannelSocket class + * should be used instead, as this provides greater + * functionality and cross platform portability. + * + * The channel will own the passed in file descriptor + * and will take responsibility for closing it, so the + * caller must not close it. If appropriate the caller + * should dup() its FD before opening the channel. + * + * Returns: the new channel object + */ +QIOChannelFile * +qio_channel_file_new_fd(int fd); + +/** + * qio_channel_file_new_path: + * @fd: the file descriptor + * @flags: the open flags (O_RDONLY|O_WRONLY|O_RDWR, etc) + * @mode: the file creation mode if O_WRONLY is set in @flags + * @errp: pointer to initialized error object + * + * Create a new IO channel object for a file represented + * by the @path parameter. @path can point to any + * type of file on which sequential I/O can be + * performed, whether it be a plain file, character + * device or block device. + * + * Returns: the new channel object + */ +QIOChannelFile * +qio_channel_file_new_path(const char *path, + int flags, + mode_t mode, + Error **errp); + +#endif /* QIO_CHANNEL_FILE_H__ */ diff --git a/qemu/include/io/channel-socket.h b/qemu/include/io/channel-socket.h new file mode 100644 index 000000000..70d06b40d --- /dev/null +++ b/qemu/include/io/channel-socket.h @@ -0,0 +1,251 @@ +/* + * QEMU I/O channels sockets driver + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QIO_CHANNEL_SOCKET_H__ +#define QIO_CHANNEL_SOCKET_H__ + +#include "io/channel.h" +#include "io/task.h" +#include "qemu/sockets.h" + +#define TYPE_QIO_CHANNEL_SOCKET "qio-channel-socket" +#define QIO_CHANNEL_SOCKET(obj) \ + OBJECT_CHECK(QIOChannelSocket, (obj), TYPE_QIO_CHANNEL_SOCKET) + +typedef struct QIOChannelSocket QIOChannelSocket; + +/** + * QIOChannelSocket: + * + * The QIOChannelSocket class provides a channel implementation + * that can transport data over a UNIX socket or TCP socket. + * Beyond the core channel API, it also provides functionality + * for accepting client connections, tuning some socket + * parameters and getting socket address strings. + */ + +struct QIOChannelSocket { + QIOChannel parent; + int fd; + struct sockaddr_storage localAddr; + socklen_t localAddrLen; + struct sockaddr_storage remoteAddr; + socklen_t remoteAddrLen; +}; + + +/** + * qio_channel_socket_new: + * + * Create a channel for performing I/O on a socket + * connection, that is initially closed. After + * creating the socket, it must be setup as a client + * connection or server. + * + * Returns: the socket channel object + */ +QIOChannelSocket * +qio_channel_socket_new(void); + +/** + * qio_channel_socket_new_fd: + * @fd: the socket file descriptor + * @errp: pointer to a NULL-initialized error object + * + * Create a channel for performing I/O on the socket + * connection represented by the file descriptor @fd. + * + * Returns: the socket channel object, or NULL on error + */ +QIOChannelSocket * +qio_channel_socket_new_fd(int fd, + Error **errp); + + +/** + * qio_channel_socket_connect_sync: + * @ioc: the socket channel object + * @addr: the address to connect to + * @errp: pointer to a NULL-initialized error object + * + * Attempt to connect to the address @addr. This method + * will run in the foreground so the caller will not regain + * execution control until the connection is established or + * an error occurs. + */ +int qio_channel_socket_connect_sync(QIOChannelSocket *ioc, + SocketAddress *addr, + Error **errp); + +/** + * qio_channel_socket_connect_async: + * @ioc: the socket channel object + * @addr: the address to connect to + * @callback: the function to invoke on completion + * @opaque: user data to pass to @callback + * @destroy: the function to free @opaque + * + * Attempt to connect to the address @addr. This method + * will run in the background so the caller will regain + * execution control immediately. The function @callback + * will be invoked on completion or failure. The @addr + * parameter will be copied, so may be freed as soon + * as this function returns without waiting for completion. + */ +void qio_channel_socket_connect_async(QIOChannelSocket *ioc, + SocketAddress *addr, + QIOTaskFunc callback, + gpointer opaque, + GDestroyNotify destroy); + + +/** + * qio_channel_socket_listen_sync: + * @ioc: the socket channel object + * @addr: the address to listen to + * @errp: pointer to a NULL-initialized error object + * + * Attempt to listen to the address @addr. This method + * will run in the foreground so the caller will not regain + * execution control until the connection is established or + * an error occurs. + */ +int qio_channel_socket_listen_sync(QIOChannelSocket *ioc, + SocketAddress *addr, + Error **errp); + +/** + * qio_channel_socket_listen_async: + * @ioc: the socket channel object + * @addr: the address to listen to + * @callback: the function to invoke on completion + * @opaque: user data to pass to @callback + * @destroy: the function to free @opaque + * + * Attempt to listen to the address @addr. This method + * will run in the background so the caller will regain + * execution control immediately. The function @callback + * will be invoked on completion or failure. The @addr + * parameter will be copied, so may be freed as soon + * as this function returns without waiting for completion. + */ +void qio_channel_socket_listen_async(QIOChannelSocket *ioc, + SocketAddress *addr, + QIOTaskFunc callback, + gpointer opaque, + GDestroyNotify destroy); + + +/** + * qio_channel_socket_dgram_sync: + * @ioc: the socket channel object + * @localAddr: the address to local bind address + * @remoteAddr: the address to remote peer address + * @errp: pointer to a NULL-initialized error object + * + * Attempt to initialize a datagram socket bound to + * @localAddr and communicating with peer @remoteAddr. + * This method will run in the foreground so the caller + * will not regain execution control until the socket + * is established or an error occurs. + */ +int qio_channel_socket_dgram_sync(QIOChannelSocket *ioc, + SocketAddress *localAddr, + SocketAddress *remoteAddr, + Error **errp); + +/** + * qio_channel_socket_dgram_async: + * @ioc: the socket channel object + * @localAddr: the address to local bind address + * @remoteAddr: the address to remote peer address + * @callback: the function to invoke on completion + * @opaque: user data to pass to @callback + * @destroy: the function to free @opaque + * + * Attempt to initialize a datagram socket bound to + * @localAddr and communicating with peer @remoteAddr. + * This method will run in the background so the caller + * will regain execution control immediately. The function + * @callback will be invoked on completion or failure. + * The @localAddr and @remoteAddr parameters will be copied, + * so may be freed as soon as this function returns without + * waiting for completion. + */ +void qio_channel_socket_dgram_async(QIOChannelSocket *ioc, + SocketAddress *localAddr, + SocketAddress *remoteAddr, + QIOTaskFunc callback, + gpointer opaque, + GDestroyNotify destroy); + + +/** + * qio_channel_socket_get_local_address: + * @ioc: the socket channel object + * @errp: pointer to a NULL-initialized error object + * + * Get the string representation of the local socket + * address. A pointer to the allocated address information + * struct will be returned, which the caller is required to + * release with a call qapi_free_SocketAddress when no + * longer required. + * + * Returns: 0 on success, -1 on error + */ +SocketAddress * +qio_channel_socket_get_local_address(QIOChannelSocket *ioc, + Error **errp); + +/** + * qio_channel_socket_get_remote_address: + * @ioc: the socket channel object + * @errp: pointer to a NULL-initialized error object + * + * Get the string representation of the local socket + * address. A pointer to the allocated address information + * struct will be returned, which the caller is required to + * release with a call qapi_free_SocketAddress when no + * longer required. + * + * Returns: the socket address struct, or NULL on error + */ +SocketAddress * +qio_channel_socket_get_remote_address(QIOChannelSocket *ioc, + Error **errp); + + +/** + * qio_channel_socket_accept: + * @ioc: the socket channel object + * @errp: pointer to a NULL-initialized error object + * + * If the socket represents a server, then this accepts + * a new client connection. The returned channel will + * represent the connected client socket. + * + * Returns: the new client channel, or NULL on error + */ +QIOChannelSocket * +qio_channel_socket_accept(QIOChannelSocket *ioc, + Error **errp); + + +#endif /* QIO_CHANNEL_SOCKET_H__ */ diff --git a/qemu/include/io/channel-tls.h b/qemu/include/io/channel-tls.h new file mode 100644 index 000000000..322eccbaa --- /dev/null +++ b/qemu/include/io/channel-tls.h @@ -0,0 +1,142 @@ +/* + * QEMU I/O channels TLS driver + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QIO_CHANNEL_TLS_H__ +#define QIO_CHANNEL_TLS_H__ + +#include "io/channel.h" +#include "io/task.h" +#include "crypto/tlssession.h" + +#define TYPE_QIO_CHANNEL_TLS "qio-channel-tls" +#define QIO_CHANNEL_TLS(obj) \ + OBJECT_CHECK(QIOChannelTLS, (obj), TYPE_QIO_CHANNEL_TLS) + +typedef struct QIOChannelTLS QIOChannelTLS; + +/** + * QIOChannelTLS + * + * The QIOChannelTLS class provides a channel wrapper which + * can transparently run the TLS encryption protocol. It is + * usually used over a TCP socket, but there is actually no + * technical restriction on which type of master channel is + * used as the transport. + * + * This channel object is capable of running as either a + * TLS server or TLS client. + */ + +struct QIOChannelTLS { + QIOChannel parent; + QIOChannel *master; + QCryptoTLSSession *session; +}; + +/** + * qio_channel_tls_new_server: + * @master: the underlying channel object + * @creds: the credentials to use for TLS handshake + * @aclname: the access control list for validating clients + * @errp: pointer to a NULL-initialized error object + * + * Create a new TLS channel that runs the server side of + * a TLS session. The TLS session handshake will use the + * credentials provided in @creds. If the @aclname parameter + * is non-NULL, then the client will have to provide + * credentials (ie a x509 client certificate) which will + * then be validated against the ACL. + * + * After creating the channel, it is mandatory to call + * the qio_channel_tls_handshake() method before attempting + * todo any I/O on the channel. + * + * Once the handshake has completed, all I/O should be done + * via the new TLS channel object and not the original + * master channel + * + * Returns: the new TLS channel object, or NULL + */ +QIOChannelTLS * +qio_channel_tls_new_server(QIOChannel *master, + QCryptoTLSCreds *creds, + const char *aclname, + Error **errp); + +/** + * qio_channel_tls_new_client: + * @master: the underlying channel object + * @creds: the credentials to use for TLS handshake + * @hostname: the user specified server hostname + * @errp: pointer to a NULL-initialized error object + * + * Create a new TLS channel that runs the client side of + * a TLS session. The TLS session handshake will use the + * credentials provided in @creds. The @hostname parameter + * should provide the user specified hostname of the server + * and will be validated against the server's credentials + * (ie CommonName of the x509 certificate) + * + * After creating the channel, it is mandatory to call + * the qio_channel_tls_handshake() method before attempting + * todo any I/O on the channel. + * + * Once the handshake has completed, all I/O should be done + * via the new TLS channel object and not the original + * master channel + * + * Returns: the new TLS channel object, or NULL + */ +QIOChannelTLS * +qio_channel_tls_new_client(QIOChannel *master, + QCryptoTLSCreds *creds, + const char *hostname, + Error **errp); + +/** + * qio_channel_tls_handshake: + * @ioc: the TLS channel object + * @func: the callback to invoke when completed + * @opaque: opaque data to pass to @func + * @destroy: optional callback to free @opaque + * + * Perform the TLS session handshake. This method + * will return immediately and the handshake will + * continue in the background, provided the main + * loop is running. When the handshake is complete, + * or fails, the @func callback will be invoked. + */ +void qio_channel_tls_handshake(QIOChannelTLS *ioc, + QIOTaskFunc func, + gpointer opaque, + GDestroyNotify destroy); + +/** + * qio_channel_tls_get_session: + * @ioc: the TLS channel object + * + * Get the TLS session used by the channel. + * + * Returns: the TLS session + */ +QCryptoTLSSession * +qio_channel_tls_get_session(QIOChannelTLS *ioc); + +#endif /* QIO_CHANNEL_TLS_H__ */ diff --git a/qemu/include/io/channel-util.h b/qemu/include/io/channel-util.h new file mode 100644 index 000000000..c93af8288 --- /dev/null +++ b/qemu/include/io/channel-util.h @@ -0,0 +1,52 @@ +/* + * QEMU I/O channels utility APIs + * + * Copyright (c) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QIO_CHANNEL_UTIL_H__ +#define QIO_CHANNEL_UTIL_H__ + +#include "io/channel.h" + +/* + * This module provides helper functions that are useful when dealing + * with QIOChannel objects + */ + + +/** + * qio_channel_new_fd: + * @fd: the file descriptor + * @errp: pointer to a NULL-initialized error object + * + * Create a channel for performing I/O on the file + * descriptor @fd. The particular subclass of QIOChannel + * that is returned will depend on what underlying object + * the file descriptor is associated with. It may be either + * a QIOChannelSocket or a QIOChannelFile instance. Upon + * success, the returned QIOChannel instance will own + * the @fd file descriptor, and take responsibility for + * closing it when no longer required. On failure, the + * caller is responsible for closing @fd. + * + * Returns: the channel object, or NULL on error + */ +QIOChannel *qio_channel_new_fd(int fd, + Error **errp); + +#endif /* QIO_CHANNEL_UTIL_H__ */ diff --git a/qemu/include/io/channel-watch.h b/qemu/include/io/channel-watch.h new file mode 100644 index 000000000..76d764223 --- /dev/null +++ b/qemu/include/io/channel-watch.h @@ -0,0 +1,90 @@ +/* + * QEMU I/O channels watch helper APIs + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QIO_CHANNEL_WATCH_H__ +#define QIO_CHANNEL_WATCH_H__ + +#include "io/channel.h" + +/* + * This module provides helper functions that will be needed by + * the various QIOChannel implementations, for creating watches + * on file descriptors / sockets + */ + +/** + * qio_channel_create_fd_watch: + * @ioc: the channel object + * @fd: the file descriptor + * @condition: the I/O condition + * + * Create a new main loop source that is able to + * monitor the file descriptor @fd for the + * I/O conditions in @condition. This is able + * monitor block devices, character devices, + * pipes but not plain files or, on Win32, sockets. + * + * Returns: the new main loop source + */ +GSource *qio_channel_create_fd_watch(QIOChannel *ioc, + int fd, + GIOCondition condition); + +/** + * qio_channel_create_socket_watch: + * @ioc: the channel object + * @fd: the file descriptor + * @condition: the I/O condition + * + * Create a new main loop source that is able to + * monitor the file descriptor @fd for the + * I/O conditions in @condition. This is equivalent + * to qio_channel_create_fd_watch on POSIX systems + * but not on Windows. + * + * Returns: the new main loop source + */ +GSource *qio_channel_create_socket_watch(QIOChannel *ioc, + int fd, + GIOCondition condition); + +/** + * qio_channel_create_fd_pair_watch: + * @ioc: the channel object + * @fdread: the file descriptor for reading + * @fdwrite: the file descriptor for writing + * @condition: the I/O condition + * + * Create a new main loop source that is able to + * monitor the pair of file descriptors @fdread + * and @fdwrite for the I/O conditions in @condition. + * This is intended for monitoring unidirectional + * file descriptors such as pipes, where a pair + * of descriptors is required for bidirectional + * I/O + * + * Returns: the new main loop source + */ +GSource *qio_channel_create_fd_pair_watch(QIOChannel *ioc, + int fdread, + int fdwrite, + GIOCondition condition); + +#endif /* QIO_CHANNEL_WATCH_H__ */ diff --git a/qemu/include/io/channel-websock.h b/qemu/include/io/channel-websock.h new file mode 100644 index 000000000..0dc21cc56 --- /dev/null +++ b/qemu/include/io/channel-websock.h @@ -0,0 +1,108 @@ +/* + * QEMU I/O channels driver websockets + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QIO_CHANNEL_WEBSOCK_H__ +#define QIO_CHANNEL_WEBSOCK_H__ + +#include "io/channel.h" +#include "qemu/buffer.h" +#include "io/task.h" + +#define TYPE_QIO_CHANNEL_WEBSOCK "qio-channel-websock" +#define QIO_CHANNEL_WEBSOCK(obj) \ + OBJECT_CHECK(QIOChannelWebsock, (obj), TYPE_QIO_CHANNEL_WEBSOCK) + +typedef struct QIOChannelWebsock QIOChannelWebsock; +typedef union QIOChannelWebsockMask QIOChannelWebsockMask; + +union QIOChannelWebsockMask { + char c[4]; + uint32_t u; +}; + +/** + * QIOChannelWebsock + * + * The QIOChannelWebsock class provides a channel wrapper which + * can transparently run the HTTP websockets protocol. This is + * usually used over a TCP socket, but there is actually no + * technical restriction on which type of master channel is + * used as the transport. + * + * This channel object is currently only capable of running as + * a websocket server and is a pretty crude implementation + * of it, not supporting the full websockets protocol feature + * set. It is sufficient to use with a simple websockets + * client for encapsulating VNC for noVNC in-browser client. + */ + +struct QIOChannelWebsock { + QIOChannel parent; + QIOChannel *master; + Buffer encinput; + Buffer encoutput; + Buffer rawinput; + Buffer rawoutput; + size_t payload_remain; + QIOChannelWebsockMask mask; + guint io_tag; + Error *io_err; + gboolean io_eof; +}; + +/** + * qio_channel_websock_new_server: + * @master: the underlying channel object + * + * Create a new websockets channel that runs the server + * side of the protocol. + * + * After creating the channel, it is mandatory to call + * the qio_channel_websock_handshake() method before attempting + * todo any I/O on the channel. + * + * Once the handshake has completed, all I/O should be done + * via the new websocket channel object and not the original + * master channel + * + * Returns: the new websockets channel object + */ +QIOChannelWebsock * +qio_channel_websock_new_server(QIOChannel *master); + +/** + * qio_channel_websock_handshake: + * @ioc: the websocket channel object + * @func: the callback to invoke when completed + * @opaque: opaque data to pass to @func + * @destroy: optional callback to free @opaque + * + * Perform the websocket handshake. This method + * will return immediately and the handshake will + * continue in the background, provided the main + * loop is running. When the handshake is complete, + * or fails, the @func callback will be invoked. + */ +void qio_channel_websock_handshake(QIOChannelWebsock *ioc, + QIOTaskFunc func, + gpointer opaque, + GDestroyNotify destroy); + +#endif /* QIO_CHANNEL_WEBSOCK_H__ */ diff --git a/qemu/include/io/channel.h b/qemu/include/io/channel.h new file mode 100644 index 000000000..d37acd29e --- /dev/null +++ b/qemu/include/io/channel.h @@ -0,0 +1,504 @@ +/* + * QEMU I/O channels + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QIO_CHANNEL_H__ +#define QIO_CHANNEL_H__ + +#include "qemu-common.h" +#include "qom/object.h" + +#define TYPE_QIO_CHANNEL "qio-channel" +#define QIO_CHANNEL(obj) \ + OBJECT_CHECK(QIOChannel, (obj), TYPE_QIO_CHANNEL) +#define QIO_CHANNEL_CLASS(klass) \ + OBJECT_CLASS_CHECK(QIOChannelClass, klass, TYPE_QIO_CHANNEL) +#define QIO_CHANNEL_GET_CLASS(obj) \ + OBJECT_GET_CLASS(QIOChannelClass, obj, TYPE_QIO_CHANNEL) + +typedef struct QIOChannel QIOChannel; +typedef struct QIOChannelClass QIOChannelClass; + +#define QIO_CHANNEL_ERR_BLOCK -2 + +typedef enum QIOChannelFeature QIOChannelFeature; + +enum QIOChannelFeature { + QIO_CHANNEL_FEATURE_FD_PASS = (1 << 0), + QIO_CHANNEL_FEATURE_SHUTDOWN = (1 << 1), +}; + + +typedef enum QIOChannelShutdown QIOChannelShutdown; + +enum QIOChannelShutdown { + QIO_CHANNEL_SHUTDOWN_BOTH, + QIO_CHANNEL_SHUTDOWN_READ, + QIO_CHANNEL_SHUTDOWN_WRITE, +}; + +typedef gboolean (*QIOChannelFunc)(QIOChannel *ioc, + GIOCondition condition, + gpointer data); + +/** + * QIOChannel: + * + * The QIOChannel defines the core API for a generic I/O channel + * class hierarchy. It is inspired by GIOChannel, but has the + * following differences + * + * - Use QOM to properly support arbitrary subclassing + * - Support use of iovecs for efficient I/O with multiple blocks + * - None of the character set translation, binary data exclusively + * - Direct support for QEMU Error object reporting + * - File descriptor passing + * + * This base class is abstract so cannot be instantiated. There + * will be subclasses for dealing with sockets, files, and higher + * level protocols such as TLS, WebSocket, etc. + */ + +struct QIOChannel { + Object parent; + unsigned int features; /* bitmask of QIOChannelFeatures */ +#ifdef _WIN32 + HANDLE event; /* For use with GSource on Win32 */ +#endif +}; + +/** + * QIOChannelClass: + * + * This class defines the contract that all subclasses + * must follow to provide specific channel implementations. + * The first five callbacks are mandatory to support, others + * provide additional optional features. + * + * Consult the corresponding public API docs for a description + * of the semantics of each callback + */ +struct QIOChannelClass { + ObjectClass parent; + + /* Mandatory callbacks */ + ssize_t (*io_writev)(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds, + size_t nfds, + Error **errp); + ssize_t (*io_readv)(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, + size_t *nfds, + Error **errp); + int (*io_close)(QIOChannel *ioc, + Error **errp); + GSource * (*io_create_watch)(QIOChannel *ioc, + GIOCondition condition); + int (*io_set_blocking)(QIOChannel *ioc, + bool enabled, + Error **errp); + + /* Optional callbacks */ + int (*io_shutdown)(QIOChannel *ioc, + QIOChannelShutdown how, + Error **errp); + void (*io_set_cork)(QIOChannel *ioc, + bool enabled); + void (*io_set_delay)(QIOChannel *ioc, + bool enabled); + off_t (*io_seek)(QIOChannel *ioc, + off_t offset, + int whence, + Error **errp); +}; + +/* General I/O handling functions */ + +/** + * qio_channel_has_feature: + * @ioc: the channel object + * @feature: the feature to check support of + * + * Determine whether the channel implementation supports + * the optional feature named in @feature. + * + * Returns: true if supported, false otherwise. + */ +bool qio_channel_has_feature(QIOChannel *ioc, + QIOChannelFeature feature); + +/** + * qio_channel_readv_full: + * @ioc: the channel object + * @iov: the array of memory regions to read data into + * @niov: the length of the @iov array + * @fds: pointer to an array that will received file handles + * @nfds: pointer filled with number of elements in @fds on return + * @errp: pointer to a NULL-initialized error object + * + * Read data from the IO channel, storing it in the + * memory regions referenced by @iov. Each element + * in the @iov will be fully populated with data + * before the next one is used. The @niov parameter + * specifies the total number of elements in @iov. + * + * It is not required for all @iov to be filled with + * data. If the channel is in blocking mode, at least + * one byte of data will be read, but no more is + * guaranteed. If the channel is non-blocking and no + * data is available, it will return QIO_CHANNEL_ERR_BLOCK + * + * If the channel has passed any file descriptors, + * the @fds array pointer will be allocated and + * the elements filled with the received file + * descriptors. The @nfds pointer will be updated + * to indicate the size of the @fds array that + * was allocated. It is the callers responsibility + * to call close() on each file descriptor and to + * call g_free() on the array pointer in @fds. + * + * It is an error to pass a non-NULL @fds parameter + * unless qio_channel_has_feature() returns a true + * value for the QIO_CHANNEL_FEATURE_FD_PASS constant. + * + * Returns: the number of bytes read, or -1 on error, + * or QIO_CHANNEL_ERR_BLOCK if no data is available + * and the channel is non-blocking + */ +ssize_t qio_channel_readv_full(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, + size_t *nfds, + Error **errp); + + +/** + * qio_channel_writev_full: + * @ioc: the channel object + * @iov: the array of memory regions to write data from + * @niov: the length of the @iov array + * @fds: an array of file handles to send + * @nfds: number of file handles in @fds + * @errp: pointer to a NULL-initialized error object + * + * Write data to the IO channel, reading it from the + * memory regions referenced by @iov. Each element + * in the @iov will be fully sent, before the next + * one is used. The @niov parameter specifies the + * total number of elements in @iov. + * + * It is not required for all @iov data to be fully + * sent. If the channel is in blocking mode, at least + * one byte of data will be sent, but no more is + * guaranteed. If the channel is non-blocking and no + * data can be sent, it will return QIO_CHANNEL_ERR_BLOCK + * + * If there are file descriptors to send, the @fds + * array should be non-NULL and provide the handles. + * All file descriptors will be sent if at least one + * byte of data was sent. + * + * It is an error to pass a non-NULL @fds parameter + * unless qio_channel_has_feature() returns a true + * value for the QIO_CHANNEL_FEATURE_FD_PASS constant. + * + * Returns: the number of bytes sent, or -1 on error, + * or QIO_CHANNEL_ERR_BLOCK if no data is can be sent + * and the channel is non-blocking + */ +ssize_t qio_channel_writev_full(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds, + size_t nfds, + Error **errp); + +/** + * qio_channel_readv: + * @ioc: the channel object + * @iov: the array of memory regions to read data into + * @niov: the length of the @iov array + * @errp: pointer to a NULL-initialized error object + * + * Behaves as qio_channel_readv_full() but does not support + * receiving of file handles. + */ +ssize_t qio_channel_readv(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + Error **errp); + +/** + * qio_channel_writev: + * @ioc: the channel object + * @iov: the array of memory regions to write data from + * @niov: the length of the @iov array + * @errp: pointer to a NULL-initialized error object + * + * Behaves as qio_channel_writev_full() but does not support + * sending of file handles. + */ +ssize_t qio_channel_writev(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + Error **errp); + +/** + * qio_channel_readv: + * @ioc: the channel object + * @buf: the memory region to read data into + * @buflen: the length of @buf + * @errp: pointer to a NULL-initialized error object + * + * Behaves as qio_channel_readv_full() but does not support + * receiving of file handles, and only supports reading into + * a single memory region. + */ +ssize_t qio_channel_read(QIOChannel *ioc, + char *buf, + size_t buflen, + Error **errp); + +/** + * qio_channel_writev: + * @ioc: the channel object + * @buf: the memory regions to send data from + * @buflen: the length of @buf + * @errp: pointer to a NULL-initialized error object + * + * Behaves as qio_channel_writev_full() but does not support + * sending of file handles, and only supports writing from a + * single memory region. + */ +ssize_t qio_channel_write(QIOChannel *ioc, + const char *buf, + size_t buflen, + Error **errp); + +/** + * qio_channel_set_blocking: + * @ioc: the channel object + * @enabled: the blocking flag state + * @errp: pointer to a NULL-initialized error object + * + * If @enabled is true, then the channel is put into + * blocking mode, otherwise it will be non-blocking. + * + * In non-blocking mode, read/write operations may + * return QIO_CHANNEL_ERR_BLOCK if they would otherwise + * block on I/O + */ +int qio_channel_set_blocking(QIOChannel *ioc, + bool enabled, + Error **errp); + +/** + * qio_channel_close: + * @ioc: the channel object + * @errp: pointer to a NULL-initialized error object + * + * Close the channel, flushing any pending I/O + * + * Returns: 0 on success, -1 on error + */ +int qio_channel_close(QIOChannel *ioc, + Error **errp); + +/** + * qio_channel_shutdown: + * @ioc: the channel object + * @how: the direction to shutdown + * @errp: pointer to a NULL-initialized error object + * + * Shutdowns transmission and/or receiving of data + * without closing the underlying transport. + * + * Not all implementations will support this facility, + * so may report an error. To avoid errors, the + * caller may check for the feature flag + * QIO_CHANNEL_FEATURE_SHUTDOWN prior to calling + * this method. + * + * Returns: 0 on success, -1 on error + */ +int qio_channel_shutdown(QIOChannel *ioc, + QIOChannelShutdown how, + Error **errp); + +/** + * qio_channel_set_delay: + * @ioc: the channel object + * @enabled: the new flag state + * + * Controls whether the underlying transport is + * permitted to delay writes in order to merge + * small packets. If @enabled is true, then the + * writes may be delayed in order to opportunistically + * merge small packets into larger ones. If @enabled + * is false, writes are dispatched immediately with + * no delay. + * + * When @enabled is false, applications may wish to + * use the qio_channel_set_cork() method to explicitly + * control write merging. + * + * On channels which are backed by a socket, this + * API corresponds to the inverse of TCP_NODELAY flag, + * controlling whether the Nagle algorithm is active. + * + * This setting is merely a hint, so implementations are + * free to ignore this without it being considered an + * error. + */ +void qio_channel_set_delay(QIOChannel *ioc, + bool enabled); + +/** + * qio_channel_set_cork: + * @ioc: the channel object + * @enabled: the new flag state + * + * Controls whether the underlying transport is + * permitted to dispatch data that is written. + * If @enabled is true, then any data written will + * be queued in local buffers until @enabled is + * set to false once again. + * + * This feature is typically used when the automatic + * write coalescing facility is disabled via the + * qio_channel_set_delay() method. + * + * On channels which are backed by a socket, this + * API corresponds to the TCP_CORK flag. + * + * This setting is merely a hint, so implementations are + * free to ignore this without it being considered an + * error. + */ +void qio_channel_set_cork(QIOChannel *ioc, + bool enabled); + + +/** + * qio_channel_seek: + * @ioc: the channel object + * @offset: the position to seek to, relative to @whence + * @whence: one of the (POSIX) SEEK_* constants listed below + * @errp: pointer to a NULL-initialized error object + * + * Moves the current I/O position within the channel + * @ioc, to be @offset. The value of @offset is + * interpreted relative to @whence: + * + * SEEK_SET - the position is set to @offset bytes + * SEEK_CUR - the position is moved by @offset bytes + * SEEK_END - the position is set to end of the file plus @offset bytes + * + * Not all implementations will support this facility, + * so may report an error. + * + * Returns: the new position on success, (off_t)-1 on failure + */ +off_t qio_channel_io_seek(QIOChannel *ioc, + off_t offset, + int whence, + Error **errp); + + +/** + * qio_channel_create_watch: + * @ioc: the channel object + * @condition: the I/O condition to monitor + * + * Create a new main loop source that is used to watch + * for the I/O condition @condition. Typically the + * qio_channel_add_watch() method would be used instead + * of this, since it directly attaches a callback to + * the source + * + * Returns: the new main loop source. + */ +GSource *qio_channel_create_watch(QIOChannel *ioc, + GIOCondition condition); + +/** + * qio_channel_add_watch: + * @ioc: the channel object + * @condition: the I/O condition to monitor + * @func: callback to invoke when the source becomes ready + * @user_data: opaque data to pass to @func + * @notify: callback to free @user_data + * + * Create a new main loop source that is used to watch + * for the I/O condition @condition. The callback @func + * will be registered against the source, to be invoked + * when the source becomes ready. The optional @user_data + * will be passed to @func when it is invoked. The @notify + * callback will be used to free @user_data when the + * watch is deleted + * + * The returned source ID can be used with g_source_remove() + * to remove and free the source when no longer required. + * Alternatively the @func callback can return a FALSE + * value. + * + * Returns: the source ID + */ +guint qio_channel_add_watch(QIOChannel *ioc, + GIOCondition condition, + QIOChannelFunc func, + gpointer user_data, + GDestroyNotify notify); + + +/** + * qio_channel_yield: + * @ioc: the channel object + * @condition: the I/O condition to wait for + * + * Yields execution from the current coroutine until + * the condition indicated by @condition becomes + * available. + * + * This must only be called from coroutine context + */ +void qio_channel_yield(QIOChannel *ioc, + GIOCondition condition); + +/** + * qio_channel_wait: + * @ioc: the channel object + * @condition: the I/O condition to wait for + * + * Block execution from the current thread until + * the condition indicated by @condition becomes + * available. + * + * This will enter a nested event loop to perform + * the wait. + */ +void qio_channel_wait(QIOChannel *ioc, + GIOCondition condition); + +#endif /* QIO_CHANNEL_H__ */ diff --git a/qemu/include/io/task.h b/qemu/include/io/task.h new file mode 100644 index 000000000..2e69d8a47 --- /dev/null +++ b/qemu/include/io/task.h @@ -0,0 +1,255 @@ +/* + * QEMU I/O task + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QIO_TASK_H__ +#define QIO_TASK_H__ + +#include "qemu-common.h" +#include "qom/object.h" + +typedef struct QIOTask QIOTask; + +typedef void (*QIOTaskFunc)(Object *source, + Error *err, + gpointer opaque); + +typedef int (*QIOTaskWorker)(QIOTask *task, + Error **errp, + gpointer opaque); + +/** + * QIOTask: + * + * The QIOTask object provides a simple mechanism for reporting + * success / failure of long running background operations. + * + * A object on which the operation is to be performed could have + * a public API which accepts a task callback: + * + * + * Task callback function signature + * + * void myobject_operation(QMyObject *obj, + * QIOTaskFunc *func, + * gpointer opaque, + * GDestroyNotify *notify); + * + * + * + * The 'func' parameter is the callback to be invoked, and 'opaque' + * is data to pass to it. The optional 'notify' function is used + * to free 'opaque' when no longer needed. + * + * Now, lets say the implementation of this method wants to set + * a timer to run once a second checking for completion of some + * activity. It would do something like + * + * + * Task callback function implementation + * + * void myobject_operation(QMyObject *obj, + * QIOTaskFunc *func, + * gpointer opaque, + * GDestroyNotify *notify) + * { + * QIOTask *task; + * + * task = qio_task_new(OBJECT(obj), func, opaque, notify); + * + * g_timeout_add_full(G_PRIORITY_DEFAULT, + * 1000, + * myobject_operation_timer, + * task, + * NULL); + * } + * + * + * + * It could equally have setup a watch on a file descriptor or + * created a background thread, or something else entirely. + * Notice that the source object is passed to the task, and + * QIOTask will hold a reference on that. This ensure that + * the QMyObject instance cannot be garbage collected while + * the async task is still in progress. + * + * In this case, myobject_operation_timer will fire after + * 3 secs and do + * + * + * Task timer function + * + * gboolean myobject_operation_timer(gpointer opaque) + * { + * QIOTask *task = QIO_TASK(opaque); + * Error *err;* + * + * ...check something important... + * if (err) { + * qio_task_abort(task, err); + * error_free(task); + * return FALSE; + * } else if (...work is completed ...) { + * qio_task_complete(task); + * return FALSE; + * } + * ...carry on polling ... + * return TRUE; + * } + * + * + * + * Once this function returns false, object_unref will be called + * automatically on the task causing it to be released and the + * ref on QMyObject dropped too. + * + * The QIOTask module can also be used to perform operations + * in a background thread context, while still reporting the + * results in the main event thread. This allows code which + * cannot easily be rewritten to be asychronous (such as DNS + * lookups) to be easily run non-blocking. Reporting the + * results in the main thread context means that the caller + * typically does not need to be concerned about thread + * safety wrt the QEMU global mutex. + * + * For example, the socket_listen() method will block the caller + * while DNS lookups take place if given a name, instead of IP + * address. The C library often do not provide a practical async + * DNS API, so the to get non-blocking DNS lookups in a portable + * manner requires use of a thread. So achieve a non-blocking + * socket listen using QIOTask would require: + * + * + * static int myobject_listen_worker(QIOTask *task, + * Error **errp, + * gpointer opaque) + * { + * QMyObject obj = QMY_OBJECT(qio_task_get_source(task)); + * SocketAddress *addr = opaque; + * + * obj->fd = socket_listen(addr, errp); + * if (obj->fd < 0) { + * return -1; + * } + * return 0; + * } + * + * void myobject_listen_async(QMyObject *obj, + * SocketAddress *addr, + * QIOTaskFunc *func, + * gpointer opaque, + * GDestroyNotify *notify) + * { + * QIOTask *task; + * SocketAddress *addrCopy; + * + * qapi_copy_SocketAddress(&addrCopy, addr); + * task = qio_task_new(OBJECT(obj), func, opaque, notify); + * + * qio_task_run_in_thread(task, myobject_listen_worker, + * addrCopy, + * qapi_free_SocketAddress); + * } + * + * + * NB, The 'func' callback passed into myobject_listen_async + * will be invoked from the main event thread, despite the + * actual operation being performed in a different thread. + */ + +/** + * qio_task_new: + * @source: the object on which the operation is invoked + * @func: the callback to invoke when the task completes + * @opaque: opaque data to pass to @func when invoked + * @destroy: optional callback to free @opaque + * + * Creates a new task struct to track completion of a + * background operation running on the object @source. + * When the operation completes or fails, the callback + * @func will be invoked. The callback can access the + * 'err' attribute in the task object to determine if + * the operation was successful or not. + * + * The returned task will be released when one of + * qio_task_abort() or qio_task_complete() are invoked. + * + * Returns: the task struct + */ +QIOTask *qio_task_new(Object *source, + QIOTaskFunc func, + gpointer opaque, + GDestroyNotify destroy); + +/** + * qio_task_run_in_thread: + * @task: the task struct + * @worker: the function to invoke in a thread + * @opaque: opaque data to pass to @worker + * @destroy: function to free @opaque + * + * Run a task in a background thread. If @worker + * returns 0 it will call qio_task_complete() in + * the main event thread context. If @worker + * returns -1 it will call qio_task_abort() in + * the main event thread context. + */ +void qio_task_run_in_thread(QIOTask *task, + QIOTaskWorker worker, + gpointer opaque, + GDestroyNotify destroy); + +/** + * qio_task_complete: + * @task: the task struct + * + * Mark the operation as succesfully completed + * and free the memory for @task. + */ +void qio_task_complete(QIOTask *task); + +/** + * qio_task_abort: + * @task: the task struct + * @err: the error to record for the operation + * + * Mark the operation as failed, with @err providing + * details about the failure. The @err may be freed + * afer the function returns, as the notification + * callback is invoked synchronously. The @task will + * be freed when this call completes. + */ +void qio_task_abort(QIOTask *task, + Error *err); + + +/** + * qio_task_get_source: + * @task: the task struct + * + * Get the source object associated with the background + * task. This returns a new reference to the object, + * which the caller must released with object_unref() + * when no longer required. + * + * Returns: the source object + */ +Object *qio_task_get_source(QIOTask *task); + +#endif /* QIO_TASK_H__ */ diff --git a/qemu/include/libdecnumber/dconfig.h b/qemu/include/libdecnumber/dconfig.h index 2f0455a06..0f7dccef1 100644 --- a/qemu/include/libdecnumber/dconfig.h +++ b/qemu/include/libdecnumber/dconfig.h @@ -27,7 +27,6 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "config-host.h" #if defined(HOST_WORDS_BIGENDIAN) #define WORDS_BIGENDIAN 1 diff --git a/qemu/include/libdecnumber/decContext.h b/qemu/include/libdecnumber/decContext.h index c3e46f405..01365e231 100644 --- a/qemu/include/libdecnumber/decContext.h +++ b/qemu/include/libdecnumber/decContext.h @@ -56,9 +56,6 @@ #define DECCFULLNAME "Decimal Context Descriptor" /* Verbose name */ #define DECCAUTHOR "Mike Cowlishaw" /* Who to blame */ - #include - #include /* for printf, etc. */ - #include /* for traps */ /* Extended flags setting -- set this to 0 to use only IEEE flags */ #define DECEXTFLAG 1 /* 1=enable extended flags */ diff --git a/qemu/include/libdecnumber/decNumberLocal.h b/qemu/include/libdecnumber/decNumberLocal.h index 71ed77bf2..94fb51292 100644 --- a/qemu/include/libdecnumber/decNumberLocal.h +++ b/qemu/include/libdecnumber/decNumberLocal.h @@ -42,8 +42,6 @@ #define DECVERSION "decNumber 3.53" /* Package Version [16 max.] */ #define DECNLAUTHOR "Mike Cowlishaw" /* Who to blame */ - #include /* for abs */ - #include /* for memset, strcpy */ #include "libdecnumber/dconfig.h" /* Conditional code flag -- set this to match hardware platform */ diff --git a/qemu/include/migration/migration.h b/qemu/include/migration/migration.h index 83346210b..ac2c12c2a 100644 --- a/qemu/include/migration/migration.h +++ b/qemu/include/migration/migration.h @@ -18,7 +18,6 @@ #include "qemu-common.h" #include "qemu/thread.h" #include "qemu/notify.h" -#include "qapi/error.h" #include "migration/vmstate.h" #include "qapi-types.h" #include "exec/cpu-common.h" @@ -35,6 +34,7 @@ #define QEMU_VM_SUBSECTION 0x05 #define QEMU_VM_VMDESCRIPTION 0x06 #define QEMU_VM_CONFIGURATION 0x07 +#define QEMU_VM_COMMAND 0x08 #define QEMU_VM_SECTION_FOOTER 0x7e struct MigrationParams { @@ -42,14 +42,71 @@ struct MigrationParams { bool shared; }; -typedef struct MigrationState MigrationState; +/* Messages sent on the return path from destination to source */ +enum mig_rp_message_type { + MIG_RP_MSG_INVALID = 0, /* Must be 0 */ + MIG_RP_MSG_SHUT, /* sibling will not send any more RP messages */ + MIG_RP_MSG_PONG, /* Response to a PING; data (seq: be32 ) */ + + MIG_RP_MSG_REQ_PAGES_ID, /* data (start: be64, len: be32, id: string) */ + MIG_RP_MSG_REQ_PAGES, /* data (start: be64, len: be32) */ + + MIG_RP_MSG_MAX +}; typedef QLIST_HEAD(, LoadStateEntry) LoadStateEntry_Head; +/* The current postcopy state is read/set by postcopy_state_get/set + * which update it atomically. + * The state is updated as postcopy messages are received, and + * in general only one thread should be writing to the state at any one + * time, initially the main thread and then the listen thread; + * Corner cases are where either thread finishes early and/or errors. + * The state is checked as messages are received to ensure that + * the source is sending us messages in the correct order. + * The state is also used by the RAM reception code to know if it + * has to place pages atomically, and the cleanup code at the end of + * the main thread to know if it has to delay cleanup until the end + * of postcopy. + */ +typedef enum { + POSTCOPY_INCOMING_NONE = 0, /* Initial state - no postcopy */ + POSTCOPY_INCOMING_ADVISE, + POSTCOPY_INCOMING_DISCARD, + POSTCOPY_INCOMING_LISTENING, + POSTCOPY_INCOMING_RUNNING, + POSTCOPY_INCOMING_END +} PostcopyState; + /* State for the incoming migration */ struct MigrationIncomingState { - QEMUFile *file; + QEMUFile *from_src_file; + + /* + * Free at the start of the main state load, set as the main thread finishes + * loading state. + */ + QemuEvent main_thread_load_event; + + bool have_fault_thread; + QemuThread fault_thread; + QemuSemaphore fault_thread_sem; + bool have_listen_thread; + QemuThread listen_thread; + QemuSemaphore listen_thread_sem; + + /* For the kernel to send us notifications */ + int userfault_fd; + /* To tell the fault_thread to quit */ + int userfault_quit_fd; + QEMUFile *to_src_file; + QemuMutex rp_mutex; /* We send replies from multiple threads */ + void *postcopy_tmp_page; + + QEMUBH *bh; + + int state; /* See savevm.c */ LoadStateEntry_Head loadvm_handlers; }; @@ -58,6 +115,18 @@ MigrationIncomingState *migration_incoming_get_current(void); MigrationIncomingState *migration_incoming_state_new(QEMUFile *f); void migration_incoming_state_destroy(void); +/* + * An outstanding page request, on the source, having been received + * and queued + */ +struct MigrationSrcPageRequest { + RAMBlock *rb; + hwaddr offset; + hwaddr len; + + QSIMPLEQ_ENTRY(MigrationSrcPageRequest) next_req; +}; + struct MigrationState { int64_t bandwidth_limit; @@ -65,23 +134,47 @@ struct MigrationState size_t xfer_limit; QemuThread thread; QEMUBH *cleanup_bh; - QEMUFile *file; - int parameters[MIGRATION_PARAMETER_MAX]; + QEMUFile *to_dst_file; + int parameters[MIGRATION_PARAMETER__MAX]; int state; MigrationParams params; + + /* State related to return path */ + struct { + QEMUFile *from_dst_file; + QemuThread rp_thread; + bool error; + } rp_state; + double mbps; int64_t total_time; int64_t downtime; int64_t expected_downtime; int64_t dirty_pages_rate; int64_t dirty_bytes_rate; - bool enabled_capabilities[MIGRATION_CAPABILITY_MAX]; + bool enabled_capabilities[MIGRATION_CAPABILITY__MAX]; int64_t xbzrle_cache_size; int64_t setup_time; int64_t dirty_sync_count; + + /* Flag set once the migration has been asked to enter postcopy */ + bool start_postcopy; + /* Flag set after postcopy has sent the device state */ + bool postcopy_after_devices; + + /* Flag set once the migration thread is running (and needs joining) */ + bool migration_thread_running; + + /* Queue of outstanding page requests from the destination */ + QemuMutex src_page_req_mutex; + QSIMPLEQ_HEAD(src_page_requests, MigrationSrcPageRequest) src_page_requests; + /* The RAMBlock used in the last src_page_request */ + RAMBlock *last_req_rb; }; +void migrate_set_state(int *state, int old_state, int new_state); + void process_incoming_migration(QEMUFile *f); void qemu_start_incoming_migration(const char *uri, Error **errp); @@ -116,9 +209,14 @@ int migrate_fd_close(MigrationState *s); void add_migration_state_change_notifier(Notifier *notify); void remove_migration_state_change_notifier(Notifier *notify); +MigrationState *migrate_init(const MigrationParams *params); bool migration_in_setup(MigrationState *); bool migration_has_finished(MigrationState *); bool migration_has_failed(MigrationState *); +/* True if outgoing migration has entered postcopy phase */ +bool migration_in_postcopy(MigrationState *); +/* ...and after the device transmission */ +bool migration_in_postcopy_after_devices(MigrationState *); MigrationState *migrate_get_current(void); void migrate_compress_threads_create(void); @@ -145,6 +243,13 @@ uint64_t xbzrle_mig_pages_cache_miss(void); double xbzrle_mig_cache_miss_rate(void); void ram_handle_compressed(void *host, uint8_t ch, uint64_t size); +void ram_debug_dump_bitmap(unsigned long *todump, bool expected); +/* For outgoing discard bitmap */ +int ram_postcopy_send_discard_bitmap(MigrationState *ms); +/* For incoming postcopy discard */ +int ram_discard_range(MigrationIncomingState *mis, const char *block_name, + uint64_t start, size_t length); +int ram_postcopy_incoming_init(MigrationIncomingState *mis); /** * @migrate_add_blocker - prevent migration from proceeding @@ -160,6 +265,7 @@ void migrate_add_blocker(Error *reason); */ void migrate_del_blocker(Error *reason); +bool migrate_postcopy_ram(void); bool migrate_zero_blocks(void); bool migrate_auto_converge(void); @@ -179,6 +285,17 @@ int migrate_compress_threads(void); int migrate_decompress_threads(void); bool migrate_use_events(void); +/* Sending on the return path - generic and then for each message type */ +void migrate_send_rp_message(MigrationIncomingState *mis, + enum mig_rp_message_type message_type, + uint16_t len, void *data); +void migrate_send_rp_shut(MigrationIncomingState *mis, + uint32_t value); +void migrate_send_rp_pong(MigrationIncomingState *mis, + uint32_t value); +void migrate_send_rp_req_pages(MigrationIncomingState *mis, const char* rbname, + ram_addr_t start, size_t len); + void ram_control_before_iterate(QEMUFile *f, uint64_t flags); void ram_control_after_iterate(QEMUFile *f, uint64_t flags); void ram_control_load_hook(QEMUFile *f, uint64_t flags, void *data); @@ -204,4 +321,12 @@ void global_state_set_optional(void); void savevm_skip_configuration(void); int global_state_store(void); void global_state_store_running(void); + +void flush_page_queue(MigrationState *ms); +int ram_save_queue_pages(MigrationState *ms, const char *rbname, + ram_addr_t start, ram_addr_t len); + +PostcopyState postcopy_state_get(void); +/* Set the state and return the old state */ +PostcopyState postcopy_state_set(PostcopyState new_state); #endif diff --git a/qemu/include/migration/postcopy-ram.h b/qemu/include/migration/postcopy-ram.h new file mode 100644 index 000000000..b6a7491f2 --- /dev/null +++ b/qemu/include/migration/postcopy-ram.h @@ -0,0 +1,99 @@ +/* + * Postcopy migration for RAM + * + * Copyright 2013 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Dave Gilbert + * + * 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_POSTCOPY_RAM_H +#define QEMU_POSTCOPY_RAM_H + +/* Return true if the host supports everything we need to do postcopy-ram */ +bool postcopy_ram_supported_by_host(void); + +/* + * Make all of RAM sensitive to accesses to areas that haven't yet been written + * and wire up anything necessary to deal with it. + */ +int postcopy_ram_enable_notify(MigrationIncomingState *mis); + +/* + * Initialise postcopy-ram, setting the RAM to a state where we can go into + * postcopy later; must be called prior to any precopy. + * called from ram.c's similarly named ram_postcopy_incoming_init + */ +int postcopy_ram_incoming_init(MigrationIncomingState *mis, size_t ram_pages); + +/* + * At the end of a migration where postcopy_ram_incoming_init was called. + */ +int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis); + +/* + * Discard the contents of 'length' bytes from 'start' + * We can assume that if we've been called postcopy_ram_hosttest returned true + */ +int postcopy_ram_discard_range(MigrationIncomingState *mis, uint8_t *start, + size_t length); + +/* + * Userfault requires us to mark RAM as NOHUGEPAGE prior to discard + * however leaving it until after precopy means that most of the precopy + * data is still THPd + */ +int postcopy_ram_prepare_discard(MigrationIncomingState *mis); + +/* + * Called at the start of each RAMBlock by the bitmap code. + * 'offset' is the bitmap offset of the named RAMBlock in the migration + * bitmap. + * Returns a new PDS + */ +PostcopyDiscardState *postcopy_discard_send_init(MigrationState *ms, + unsigned long offset, + const char *name); + +/* + * Called by the bitmap code for each chunk to discard. + * May send a discard message, may just leave it queued to + * be sent later. + * @start,@length: a range of pages in the migration bitmap in the + * RAM block passed to postcopy_discard_send_init() (length=1 is one page) + */ +void postcopy_discard_send_range(MigrationState *ms, PostcopyDiscardState *pds, + unsigned long start, unsigned long length); + +/* + * Called at the end of each RAMBlock by the bitmap code. + * Sends any outstanding discard messages, frees the PDS. + */ +void postcopy_discard_send_finish(MigrationState *ms, + PostcopyDiscardState *pds); + +/* + * Place a page (from) at (host) efficiently + * There are restrictions on how 'from' must be mapped, in general best + * to use other postcopy_ routines to allocate. + * returns 0 on success + */ +int postcopy_place_page(MigrationIncomingState *mis, void *host, void *from); + +/* + * Place a zero page at (host) atomically + * returns 0 on success + */ +int postcopy_place_page_zero(MigrationIncomingState *mis, void *host); + +/* + * Allocate a page of memory that can be mapped at a later point in time + * using postcopy_place_page + * Returns: Pointer to allocated page + */ +void *postcopy_get_tmp_page(MigrationIncomingState *mis); + +#endif diff --git a/qemu/include/migration/qemu-file.h b/qemu/include/migration/qemu-file.h index ea49f33fa..3f6b4ed58 100644 --- a/qemu/include/migration/qemu-file.h +++ b/qemu/include/migration/qemu-file.h @@ -25,21 +25,20 @@ #define QEMU_FILE_H 1 #include "exec/cpu-common.h" -#include /* This function writes a chunk of data to a file at the given position. * The pos argument can be ignored if the file is only being used for * streaming. The handler should try to write all of the data it can. */ -typedef int (QEMUFilePutBufferFunc)(void *opaque, const uint8_t *buf, - int64_t pos, int size); +typedef ssize_t (QEMUFilePutBufferFunc)(void *opaque, const uint8_t *buf, + int64_t pos, size_t size); /* Read a chunk of data from a file at the given position. The pos argument * can be ignored if the file is only be used for streaming. The number of * bytes actually read should be returned. */ -typedef int (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf, - int64_t pos, int size); +typedef ssize_t (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf, + int64_t pos, size_t size); /* Close a file * @@ -88,6 +87,11 @@ typedef size_t (QEMURamSaveFunc)(QEMUFile *f, void *opaque, size_t size, uint64_t *bytes_sent); +/* + * Return a QEMUFile for comms in the opposite direction + */ +typedef QEMUFile *(QEMURetPathFunc)(void *opaque); + /* * Stop any read or write (depending on flags) on the underlying * transport on the QEMUFile. @@ -106,6 +110,7 @@ typedef struct QEMUFileOps { QEMURamHookFunc *after_ram_iterate; QEMURamHookFunc *hook_ram_load; QEMURamSaveFunc *save_page; + QEMURetPathFunc *get_return_path; QEMUFileShutdownFunc *shut_down; } QEMUFileOps; @@ -126,13 +131,13 @@ int qemu_get_fd(QEMUFile *f); int qemu_fclose(QEMUFile *f); int64_t qemu_ftell(QEMUFile *f); int64_t qemu_ftell_fast(QEMUFile *f); -void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size); +void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, size_t size); void qemu_put_byte(QEMUFile *f, int v); /* * put_buffer without copying the buffer. * The buffer should be available till it is sent asynchronously. */ -void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, int size); +void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, size_t size); bool qemu_file_mode_is_not_valid(const char *mode); bool qemu_file_is_writable(QEMUFile *f); @@ -161,11 +166,13 @@ static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v) void qemu_put_be16(QEMUFile *f, unsigned int v); void qemu_put_be32(QEMUFile *f, unsigned int v); void qemu_put_be64(QEMUFile *f, uint64_t v); -int qemu_peek_buffer(QEMUFile *f, uint8_t **buf, int size, size_t offset); -int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size); +size_t qemu_peek_buffer(QEMUFile *f, uint8_t **buf, size_t size, size_t offset); +size_t qemu_get_buffer(QEMUFile *f, uint8_t *buf, size_t size); +size_t qemu_get_buffer_in_place(QEMUFile *f, uint8_t **buf, size_t size); ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size, int level); int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src); + /* * Note that you can only peek continuous bytes from where the current pointer * is; you aren't guaranteed to be able to peak to +n bytes unless you've @@ -194,7 +201,9 @@ int64_t qemu_file_get_rate_limit(QEMUFile *f); int qemu_file_get_error(QEMUFile *f); void qemu_file_set_error(QEMUFile *f, int ret); int qemu_file_shutdown(QEMUFile *f); +QEMUFile *qemu_file_get_return_path(QEMUFile *f); void qemu_fflush(QEMUFile *f); +void qemu_file_set_blocking(QEMUFile *f, bool block); static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv) { @@ -237,7 +246,7 @@ static inline void qemu_get_8s(QEMUFile *f, uint8_t *pv) } // Signed versions for type safety -static inline void qemu_put_sbuffer(QEMUFile *f, const int8_t *buf, int size) +static inline void qemu_put_sbuffer(QEMUFile *f, const int8_t *buf, size_t size) { qemu_put_buffer(f, (const uint8_t *)buf, size); } diff --git a/qemu/include/migration/vmstate.h b/qemu/include/migration/vmstate.h index 2e5a97dec..84ee355ce 100644 --- a/qemu/include/migration/vmstate.h +++ b/qemu/include/migration/vmstate.h @@ -39,8 +39,9 @@ typedef struct SaveVMHandlers { void (*set_params)(const MigrationParams *params, void * opaque); SaveStateHandler *save_state; - void (*cancel)(void *opaque); - int (*save_live_complete)(QEMUFile *f, void *opaque); + void (*cleanup)(void *opaque); + int (*save_live_complete_postcopy)(QEMUFile *f, void *opaque); + int (*save_live_complete_precopy)(QEMUFile *f, void *opaque); /* This runs both outside and inside the iothread lock. */ bool (*is_active)(void *opaque); @@ -54,8 +55,9 @@ typedef struct SaveVMHandlers { /* This runs outside the iothread lock! */ int (*save_live_setup)(QEMUFile *f, void *opaque); - uint64_t (*save_live_pending)(QEMUFile *f, void *opaque, uint64_t max_size); - + void (*save_live_pending)(QEMUFile *f, void *opaque, uint64_t max_size, + uint64_t *non_postcopiable_pending, + uint64_t *postcopiable_pending); LoadStateHandler *load_state; } SaveVMHandlers; @@ -86,20 +88,101 @@ struct VMStateInfo { }; enum VMStateFlags { + /* Ignored */ VMS_SINGLE = 0x001, + + /* The struct member at opaque + VMStateField.offset is a pointer + * to the actual field (e.g. struct a { uint8_t *b; + * }). Dereference the pointer before using it as basis for + * further pointer arithmetic (see e.g. VMS_ARRAY). Does not + * affect the meaning of VMStateField.num_offset or + * VMStateField.size_offset; see VMS_VARRAY* and VMS_VBUFFER for + * those. */ VMS_POINTER = 0x002, + + /* The field is an array of fixed size. VMStateField.num contains + * the number of entries in the array. The size of each entry is + * given by VMStateField.size and / or opaque + + * VMStateField.size_offset; see VMS_VBUFFER and + * VMS_MULTIPLY. Each array entry will be processed individually + * (VMStateField.info.get()/put() if VMS_STRUCT is not set, + * recursion into VMStateField.vmsd if VMS_STRUCT is set). May not + * be combined with VMS_VARRAY*. */ VMS_ARRAY = 0x004, + + /* The field is itself a struct, containing one or more + * fields. Recurse into VMStateField.vmsd. Most useful in + * combination with VMS_ARRAY / VMS_VARRAY*, recursing into each + * array entry. */ VMS_STRUCT = 0x008, - VMS_VARRAY_INT32 = 0x010, /* Array with size in int32_t field*/ - VMS_BUFFER = 0x020, /* static sized buffer */ + + /* The field is an array of variable size. The int32_t at opaque + + * VMStateField.num_offset contains the number of entries in the + * array. See the VMS_ARRAY description regarding array handling + * in general. May not be combined with VMS_ARRAY or any other + * VMS_VARRAY*. */ + VMS_VARRAY_INT32 = 0x010, + + /* Ignored */ + VMS_BUFFER = 0x020, + + /* The field is a (fixed-size or variable-size) array of pointers + * (e.g. struct a { uint8_t *b[]; }). Dereference each array entry + * before using it. Note: Does not imply any one of VMS_ARRAY / + * VMS_VARRAY*; these need to be set explicitly. */ VMS_ARRAY_OF_POINTER = 0x040, - VMS_VARRAY_UINT16 = 0x080, /* Array with size in uint16_t field */ - VMS_VBUFFER = 0x100, /* Buffer with size in int32_t field */ - VMS_MULTIPLY = 0x200, /* multiply "size" field by field_size */ - VMS_VARRAY_UINT8 = 0x400, /* Array with size in uint8_t field*/ - VMS_VARRAY_UINT32 = 0x800, /* Array with size in uint32_t field*/ - VMS_MUST_EXIST = 0x1000, /* Field must exist in input */ - VMS_ALLOC = 0x2000, /* Alloc a buffer on the destination */ + + /* The field is an array of variable size. The uint16_t at opaque + * + VMStateField.num_offset (subject to VMS_MULTIPLY_ELEMENTS) + * contains the number of entries in the array. See the VMS_ARRAY + * description regarding array handling in general. May not be + * combined with VMS_ARRAY or any other VMS_VARRAY*. */ + VMS_VARRAY_UINT16 = 0x080, + + /* The size of the individual entries (a single array entry if + * VMS_ARRAY or any of VMS_VARRAY* are set, or the field itself if + * neither is set) is variable (i.e. not known at compile-time), + * but the same for all entries. Use the int32_t at opaque + + * VMStateField.size_offset (subject to VMS_MULTIPLY) to determine + * the size of each (and every) entry. */ + VMS_VBUFFER = 0x100, + + /* Multiply the entry size given by the int32_t at opaque + + * VMStateField.size_offset (see VMS_VBUFFER description) with + * VMStateField.size to determine the number of bytes to be + * allocated. Only valid in combination with VMS_VBUFFER. */ + VMS_MULTIPLY = 0x200, + + /* The field is an array of variable size. The uint8_t at opaque + + * VMStateField.num_offset (subject to VMS_MULTIPLY_ELEMENTS) + * contains the number of entries in the array. See the VMS_ARRAY + * description regarding array handling in general. May not be + * combined with VMS_ARRAY or any other VMS_VARRAY*. */ + VMS_VARRAY_UINT8 = 0x400, + + /* The field is an array of variable size. The uint32_t at opaque + * + VMStateField.num_offset (subject to VMS_MULTIPLY_ELEMENTS) + * contains the number of entries in the array. See the VMS_ARRAY + * description regarding array handling in general. May not be + * combined with VMS_ARRAY or any other VMS_VARRAY*. */ + VMS_VARRAY_UINT32 = 0x800, + + /* Fail loading the serialised VM state if this field is missing + * from the input. */ + VMS_MUST_EXIST = 0x1000, + + /* When loading serialised VM state, allocate memory for the + * (entire) field. Only valid in combination with + * VMS_POINTER. Note: Not all combinations with other flags are + * currently supported, e.g. VMS_ALLOC|VMS_ARRAY_OF_POINTER won't + * cause the individual entries to be allocated. */ + VMS_ALLOC = 0x2000, + + /* Multiply the number of entries given by the integer at opaque + + * VMStateField.num_offset (see VMS_VARRAY*) with VMStateField.num + * to determine the number of entries in the array. Only valid in + * combination with one of VMS_VARRAY*. */ + VMS_MULTIPLY_ELEMENTS = 0x4000, }; typedef struct { @@ -154,6 +237,7 @@ extern const VMStateInfo vmstate_info_uint32; extern const VMStateInfo vmstate_info_uint64; extern const VMStateInfo vmstate_info_float64; +extern const VMStateInfo vmstate_info_cpudouble; extern const VMStateInfo vmstate_info_timer; extern const VMStateInfo vmstate_info_buffer; @@ -243,6 +327,16 @@ extern const VMStateInfo vmstate_info_bitmap; .offset = vmstate_offset_2darray(_state, _field, _type, _n1, _n2), \ } +#define VMSTATE_VARRAY_MULTIPLY(_field, _state, _field_num, _multiply, _info, _type) { \ + .name = (stringify(_field)), \ + .num_offset = vmstate_offset_value(_state, _field_num, uint32_t),\ + .num = (_multiply), \ + .info = &(_info), \ + .size = sizeof(_type), \ + .flags = VMS_VARRAY_UINT32|VMS_MULTIPLY_ELEMENTS, \ + .offset = offsetof(_state, _field), \ +} + #define VMSTATE_ARRAY_TEST(_field, _state, _num, _test, _info, _type) {\ .name = (stringify(_field)), \ .field_exists = (_test), \ @@ -382,6 +476,19 @@ extern const VMStateInfo vmstate_info_bitmap; .offset = offsetof(_state, _field), \ } +/* a variable length array (i.e. _type *_field) but we know the + * length + */ +#define VMSTATE_STRUCT_VARRAY_POINTER_KNOWN(_field, _state, _num, _version, _vmsd, _type) { \ + .name = (stringify(_field)), \ + .num = (_num), \ + .version_id = (_version), \ + .vmsd = &(_vmsd), \ + .size = sizeof(_type), \ + .flags = VMS_STRUCT|VMS_ARRAY|VMS_POINTER, \ + .offset = offsetof(_state, _field), \ +} + #define VMSTATE_STRUCT_VARRAY_POINTER_INT32(_field, _state, _field_num, _vmsd, _type) { \ .name = (stringify(_field)), \ .version_id = 0, \ @@ -754,9 +861,6 @@ extern const VMStateInfo vmstate_info_bitmap; #define VMSTATE_UINT32_SUB_ARRAY(_f, _s, _start, _num) \ VMSTATE_SUB_ARRAY(_f, _s, _start, _num, 0, vmstate_info_uint32, uint32_t) -#define VMSTATE_UINT32_ARRAY(_f, _s, _n) \ - VMSTATE_UINT32_ARRAY_V(_f, _s, _n, 0) - #define VMSTATE_INT64_ARRAY_V(_f, _s, _n, _v) \ VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_int64, int64_t) @@ -769,6 +873,12 @@ extern const VMStateInfo vmstate_info_bitmap; #define VMSTATE_FLOAT64_ARRAY(_f, _s, _n) \ VMSTATE_FLOAT64_ARRAY_V(_f, _s, _n, 0) +#define VMSTATE_CPUDOUBLE_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_cpudouble, CPU_DoubleU) + +#define VMSTATE_CPUDOUBLE_ARRAY(_f, _s, _n) \ + VMSTATE_CPUDOUBLE_ARRAY_V(_f, _s, _n, 0) + #define VMSTATE_BUFFER_V(_f, _s, _v) \ VMSTATE_STATIC_BUFFER(_f, _s, _v, NULL, 0, sizeof(typeof_field(_s, _f))) diff --git a/qemu/include/monitor/hmp-target.h b/qemu/include/monitor/hmp-target.h new file mode 100644 index 000000000..bc2c9c04d --- /dev/null +++ b/qemu/include/monitor/hmp-target.h @@ -0,0 +1,49 @@ +/* + * QEMU monitor + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MONITOR_COMMON_H +#define MONITOR_COMMON_H + +#define MD_TLONG 0 +#define MD_I32 1 + +struct MonitorDef { + const char *name; + int offset; + target_long (*get_value)(const struct MonitorDef *md, int val); + int type; +}; + +const MonitorDef *target_monitor_defs(void); +int target_get_monitor_def(CPUState *cs, const char *name, uint64_t *pval); + +CPUArchState *mon_get_cpu_env(void); +CPUState *mon_get_cpu(void); + +void hmp_info_mem(Monitor *mon, const QDict *qdict); +void hmp_info_tlb(Monitor *mon, const QDict *qdict); +void hmp_mce(Monitor *mon, const QDict *qdict); +void hmp_info_local_apic(Monitor *mon, const QDict *qdict); +void hmp_info_io_apic(Monitor *mon, const QDict *qdict); + +#endif /* MONITOR_COMMON */ diff --git a/qemu/include/monitor/monitor.h b/qemu/include/monitor/monitor.h index 9aff47e69..aa0f37320 100644 --- a/qemu/include/monitor/monitor.h +++ b/qemu/include/monitor/monitor.h @@ -34,6 +34,7 @@ int monitor_fd_param(Monitor *mon, const char *fdname, Error **errp); void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); void monitor_printf(Monitor *mon, const char *fmt, ...) GCC_FMT_ATTR(2, 3); +int monitor_fprintf(FILE *stream, const char *fmt, ...) GCC_FMT_ATTR(2, 3); void monitor_flush(Monitor *mon); int monitor_set_cpu(int cpu_index); int monitor_get_cpu_index(void); @@ -42,12 +43,6 @@ void monitor_read_command(Monitor *mon, int show_prompt); int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, void *opaque); -void qmp_qom_set(QDict *qdict, QObject **ret, Error **errp); -void qmp_qom_get(QDict *qdict, QObject **ret, Error **errp); -void qmp_object_add(QDict *qdict, QObject **ret, Error **errp); -void object_add(const char *type, const char *id, const QDict *qdict, - Visitor *v, Error **errp); - AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, bool has_opaque, const char *opaque, Error **errp); diff --git a/qemu/include/monitor/qdev.h b/qemu/include/monitor/qdev.h index 50ec2ffc1..c4b8a0514 100644 --- a/qemu/include/monitor/qdev.h +++ b/qemu/include/monitor/qdev.h @@ -2,7 +2,6 @@ #define QEMU_QDEV_MONITOR_H #include "hw/qdev-core.h" -#include "qemu/typedefs.h" /*** monitor commands ***/ diff --git a/qemu/include/net/checksum.h b/qemu/include/net/checksum.h index 2d7a363d4..7de1acb79 100644 --- a/qemu/include/net/checksum.h +++ b/qemu/include/net/checksum.h @@ -18,7 +18,6 @@ #ifndef QEMU_NET_CHECKSUM_H #define QEMU_NET_CHECKSUM_H -#include struct iovec; uint32_t net_checksum_add_cont(int len, uint8_t *buf, int seq); diff --git a/qemu/include/net/eth.h b/qemu/include/net/eth.h index b3273b823..18d0be3b1 100644 --- a/qemu/include/net/eth.h +++ b/qemu/include/net/eth.h @@ -26,12 +26,11 @@ #ifndef QEMU_ETH_H #define QEMU_ETH_H -#include -#include #include "qemu/bswap.h" #include "qemu/iov.h" #define ETH_ALEN 6 +#define ETH_HLEN 14 struct eth_header { uint8_t h_dest[ETH_ALEN]; /* destination eth addr */ @@ -170,7 +169,8 @@ struct tcp_hdr { #define IP_HEADER_VERSION(ip) \ ((ip->ip_ver_len >> 4)&0xf) -#define ETH_P_IP (0x0800) +#define ETH_P_IP (0x0800) /* Internet Protocol packet */ +#define ETH_P_ARP (0x0806) /* Address Resolution packet */ #define ETH_P_IPV6 (0x86dd) #define ETH_P_VLAN (0x8100) #define ETH_P_DVLAN (0x88a8) diff --git a/qemu/include/net/filter.h b/qemu/include/net/filter.h new file mode 100644 index 000000000..0c4a2ea6c --- /dev/null +++ b/qemu/include/net/filter.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2015 FUJITSU LIMITED + * Author: Yang Hongyang + * + * 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_NET_FILTER_H +#define QEMU_NET_FILTER_H + +#include "qom/object.h" +#include "qemu-common.h" +#include "net/queue.h" + +#define TYPE_NETFILTER "netfilter" +#define NETFILTER(obj) \ + OBJECT_CHECK(NetFilterState, (obj), TYPE_NETFILTER) +#define NETFILTER_GET_CLASS(obj) \ + OBJECT_GET_CLASS(NetFilterClass, (obj), TYPE_NETFILTER) +#define NETFILTER_CLASS(klass) \ + OBJECT_CLASS_CHECK(NetFilterClass, (klass), TYPE_NETFILTER) + +typedef void (FilterSetup) (NetFilterState *nf, Error **errp); +typedef void (FilterCleanup) (NetFilterState *nf); +/* + * Return: + * 0: finished handling the packet, we should continue + * size: filter stolen this packet, we stop pass this packet further + */ +typedef ssize_t (FilterReceiveIOV)(NetFilterState *nc, + NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + NetPacketSent *sent_cb); + +typedef void (FilterStatusChanged) (NetFilterState *nf, Error **errp); + +typedef struct NetFilterClass { + ObjectClass parent_class; + + /* optional */ + FilterSetup *setup; + FilterCleanup *cleanup; + FilterStatusChanged *status_changed; + /* mandatory */ + FilterReceiveIOV *receive_iov; +} NetFilterClass; + + +struct NetFilterState { + /* private */ + Object parent; + + /* protected */ + char *netdev_id; + NetClientState *netdev; + NetFilterDirection direction; + bool on; + QTAILQ_ENTRY(NetFilterState) next; +}; + +ssize_t qemu_netfilter_receive(NetFilterState *nf, + NetFilterDirection direction, + NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + NetPacketSent *sent_cb); + +/* pass the packet to the next filter */ +ssize_t qemu_netfilter_pass_to_next(NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + void *opaque); + +#endif /* QEMU_NET_FILTER_H */ diff --git a/qemu/include/net/net.h b/qemu/include/net/net.h index 6a6cbef24..73e4c466e 100644 --- a/qemu/include/net/net.h +++ b/qemu/include/net/net.h @@ -92,6 +92,7 @@ struct NetClientState { NetClientDestructor *destructor; unsigned int queue_index; unsigned rxfilter_notify_enabled:1; + QTAILQ_HEAD(NetFilterHead, NetFilterState) filters; }; typedef struct NICState { @@ -151,11 +152,6 @@ void qemu_check_nic_model(NICInfo *nd, const char *model); int qemu_find_nic_model(NICInfo *nd, const char * const *models, const char *default_model); -ssize_t qemu_deliver_packet(NetClientState *sender, - unsigned flags, - const uint8_t *data, - size_t size, - void *opaque); ssize_t qemu_deliver_packet_iov(NetClientState *sender, unsigned flags, const struct iovec *iov, diff --git a/qemu/include/net/queue.h b/qemu/include/net/queue.h index fc02b3391..5469fdbea 100644 --- a/qemu/include/net/queue.h +++ b/qemu/include/net/queue.h @@ -34,7 +34,25 @@ typedef void (NetPacketSent) (NetClientState *sender, ssize_t ret); #define QEMU_NET_PACKET_FLAG_NONE 0 #define QEMU_NET_PACKET_FLAG_RAW (1<<0) -NetQueue *qemu_new_net_queue(void *opaque); +/* Returns: + * >0 - success + * 0 - queue packet for future redelivery + * <0 - failure (discard packet) + */ +typedef ssize_t (NetQueueDeliverFunc)(NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + void *opaque); + +NetQueue *qemu_new_net_queue(NetQueueDeliverFunc *deliver, void *opaque); + +void qemu_net_queue_append_iov(NetQueue *queue, + NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + NetPacketSent *sent_cb); void qemu_del_net_queue(NetQueue *queue); diff --git a/qemu/include/net/vhost_net.h b/qemu/include/net/vhost_net.h index 840d4b16e..3389b410d 100644 --- a/qemu/include/net/vhost_net.h +++ b/qemu/include/net/vhost_net.h @@ -13,6 +13,7 @@ typedef struct VhostNetOptions { void *opaque; } VhostNetOptions; +uint64_t vhost_net_get_max_queues(VHostNetState *net); struct vhost_net *vhost_net_init(VhostNetOptions *options); int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, int total_queues); @@ -26,5 +27,8 @@ void vhost_net_ack_features(VHostNetState *net, uint64_t features); bool vhost_net_virtqueue_pending(VHostNetState *net, int n); void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, int idx, bool mask); +int vhost_net_notify_migration_done(VHostNetState *net, char* mac_addr); VHostNetState *get_vhost_net(NetClientState *nc); + +int vhost_set_vring_enable(NetClientState * nc, int enable); #endif diff --git a/qemu/include/qapi/error.h b/qemu/include/qapi/error.h index f44c45183..11be2327c 100644 --- a/qemu/include/qapi/error.h +++ b/qemu/include/qapi/error.h @@ -2,107 +2,301 @@ * QEMU Error Objects * * Copyright IBM, Corp. 2011 + * Copyright (C) 2011-2015 Red Hat, Inc. * * Authors: * Anthony Liguori + * Markus Armbruster * * This work is licensed under the terms of the GNU LGPL, version 2. See * the COPYING.LIB file in the top-level directory. */ + +/* + * Error reporting system loosely patterned after Glib's GError. + * + * Create an error: + * error_setg(&err, "situation normal, all fouled up"); + * + * Create an error and add additional explanation: + * error_setg(&err, "invalid quark"); + * error_append_hint(&err, "Valid quarks are up, down, strange, " + * "charm, top, bottom.\n"); + * + * Do *not* contract this to + * error_setg(&err, "invalid quark\n" + * "Valid quarks are up, down, strange, charm, top, bottom."); + * + * Report an error to the current monitor if we have one, else stderr: + * error_report_err(err); + * This frees the error object. + * + * Likewise, but with additional text prepended: + * error_reportf_err(err, "Could not frobnicate '%s': ", name); + * + * Report an error somewhere else: + * const char *msg = error_get_pretty(err); + * do with msg what needs to be done... + * error_free(err); + * Note that this loses hints added with error_append_hint(). + * + * Handle an error without reporting it (just for completeness): + * error_free(err); + * + * Assert that an expected error occurred, but clean it up without + * reporting it (primarily useful in testsuites): + * error_free_or_abort(&err); + * + * Pass an existing error to the caller: + * error_propagate(errp, err); + * where Error **errp is a parameter, by convention the last one. + * + * Pass an existing error to the caller with the message modified: + * error_propagate(errp, err); + * error_prepend(errp, "Could not frobnicate '%s': ", name); + * + * Create a new error and pass it to the caller: + * error_setg(errp, "situation normal, all fouled up"); + * + * Call a function and receive an error from it: + * Error *err = NULL; + * foo(arg, &err); + * if (err) { + * handle the error... + * } + * + * Call a function ignoring errors: + * foo(arg, NULL); + * + * Call a function aborting on errors: + * foo(arg, &error_abort); + * + * Call a function treating errors as fatal: + * foo(arg, &error_fatal); + * + * Receive an error and pass it on to the caller: + * Error *err = NULL; + * foo(arg, &err); + * if (err) { + * handle the error... + * error_propagate(errp, err); + * } + * where Error **errp is a parameter, by convention the last one. + * + * Do *not* "optimize" this to + * foo(arg, errp); + * if (*errp) { // WRONG! + * handle the error... + * } + * because errp may be NULL! + * + * But when all you do with the error is pass it on, please use + * foo(arg, errp); + * for readability. + * + * Receive and accumulate multiple errors (first one wins): + * Error *err = NULL, *local_err = NULL; + * foo(arg, &err); + * bar(arg, &local_err); + * error_propagate(&err, local_err); + * if (err) { + * handle the error... + * } + * + * Do *not* "optimize" this to + * foo(arg, &err); + * bar(arg, &err); // WRONG! + * if (err) { + * handle the error... + * } + * because this may pass a non-null err to bar(). + */ + #ifndef ERROR_H #define ERROR_H -#include "qemu/compiler.h" #include "qapi-types.h" -#include -/** - * A class representing internal errors within QEMU. An error has a ErrorClass - * code and a human message. +/* + * Overall category of an error. + * Based on the qapi type QapiErrorClass, but reproduced here for nicer + * enum names. */ -typedef struct Error Error; +typedef enum ErrorClass { + ERROR_CLASS_GENERIC_ERROR = QAPI_ERROR_CLASS_GENERICERROR, + ERROR_CLASS_COMMAND_NOT_FOUND = QAPI_ERROR_CLASS_COMMANDNOTFOUND, + ERROR_CLASS_DEVICE_ENCRYPTED = QAPI_ERROR_CLASS_DEVICEENCRYPTED, + ERROR_CLASS_DEVICE_NOT_ACTIVE = QAPI_ERROR_CLASS_DEVICENOTACTIVE, + ERROR_CLASS_DEVICE_NOT_FOUND = QAPI_ERROR_CLASS_DEVICENOTFOUND, + ERROR_CLASS_KVM_MISSING_CAP = QAPI_ERROR_CLASS_KVMMISSINGCAP, +} ErrorClass; -/** - * Set an indirect pointer to an error given a ErrorClass value and a - * printf-style human message. This function is not meant to be used outside - * of QEMU. +/* + * Get @err's human-readable error message. */ -void error_set(Error **errp, ErrorClass err_class, const char *fmt, ...) - GCC_FMT_ATTR(3, 4); +const char *error_get_pretty(Error *err); -/** - * Set an indirect pointer to an error given a ErrorClass value and a - * printf-style human message, followed by a strerror() string if - * @os_error is not zero. +/* + * Get @err's error class. + * Note: use of error classes other than ERROR_CLASS_GENERIC_ERROR is + * strongly discouraged. */ -void error_set_errno(Error **errp, int os_error, ErrorClass err_class, - const char *fmt, ...) GCC_FMT_ATTR(4, 5); +ErrorClass error_get_class(const Error *err); -#ifdef _WIN32 -/** - * Set an indirect pointer to an error given a ErrorClass value and a - * printf-style human message, followed by a g_win32_error_message() string if - * @win32_err is not zero. +/* + * Create a new error object and assign it to *@errp. + * If @errp is NULL, the error is ignored. Don't bother creating one + * then. + * If @errp is &error_abort, print a suitable message and abort(). + * If @errp is &error_fatal, print a suitable message and exit(1). + * If @errp is anything else, *@errp must be NULL. + * The new error's class is ERROR_CLASS_GENERIC_ERROR, and its + * human-readable error message is made from printf-style @fmt, ... + * The resulting message should be a single phrase, with no newline or + * trailing punctuation. + * Please don't error_setg(&error_fatal, ...), use error_report() and + * exit(), because that's more obvious. + * Likewise, don't error_setg(&error_abort, ...), use assert(). */ -void error_set_win32(Error **errp, int win32_err, ErrorClass err_class, - const char *fmt, ...) GCC_FMT_ATTR(4, 5); -#endif +#define error_setg(errp, fmt, ...) \ + error_setg_internal((errp), __FILE__, __LINE__, __func__, \ + (fmt), ## __VA_ARGS__) +void error_setg_internal(Error **errp, + const char *src, int line, const char *func, + const char *fmt, ...) + GCC_FMT_ATTR(5, 6); -/** - * Same as error_set(), but sets a generic error +/* + * Just like error_setg(), with @os_error info added to the message. + * If @os_error is non-zero, ": " + strerror(os_error) is appended to + * the human-readable error message. */ -#define error_setg(errp, fmt, ...) \ - error_set(errp, ERROR_CLASS_GENERIC_ERROR, fmt, ## __VA_ARGS__) -#define error_setg_errno(errp, os_error, fmt, ...) \ - error_set_errno(errp, os_error, ERROR_CLASS_GENERIC_ERROR, \ - fmt, ## __VA_ARGS__) +#define error_setg_errno(errp, os_error, fmt, ...) \ + error_setg_errno_internal((errp), __FILE__, __LINE__, __func__, \ + (os_error), (fmt), ## __VA_ARGS__) +void error_setg_errno_internal(Error **errp, + const char *fname, int line, const char *func, + int os_error, const char *fmt, ...) + GCC_FMT_ATTR(6, 7); + #ifdef _WIN32 -#define error_setg_win32(errp, win32_err, fmt, ...) \ - error_set_win32(errp, win32_err, ERROR_CLASS_GENERIC_ERROR, \ - fmt, ## __VA_ARGS__) +/* + * Just like error_setg(), with @win32_error info added to the message. + * If @win32_error is non-zero, ": " + g_win32_error_message(win32_err) + * is appended to the human-readable error message. + */ +#define error_setg_win32(errp, win32_err, fmt, ...) \ + error_setg_win32_internal((errp), __FILE__, __LINE__, __func__, \ + (win32_err), (fmt), ## __VA_ARGS__) +void error_setg_win32_internal(Error **errp, + const char *src, int line, const char *func, + int win32_err, const char *fmt, ...) + GCC_FMT_ATTR(6, 7); #endif -/** - * Helper for open() errors +/* + * Propagate error object (if any) from @local_err to @dst_errp. + * If @local_err is NULL, do nothing (because there's nothing to + * propagate). + * Else, if @dst_errp is NULL, errors are being ignored. Free the + * error object. + * Else, if @dst_errp is &error_abort, print a suitable message and + * abort(). + * Else, if @dst_errp is &error_fatal, print a suitable message and + * exit(1). + * Else, if @dst_errp already contains an error, ignore this one: free + * the error object. + * Else, move the error object from @local_err to *@dst_errp. + * On return, @local_err is invalid. + * Please don't error_propagate(&error_fatal, ...), use + * error_report_err() and exit(), because that's more obvious. */ -void error_setg_file_open(Error **errp, int os_errno, const char *filename); +void error_propagate(Error **dst_errp, Error *local_err); /* - * Get the error class of an error object. + * Prepend some text to @errp's human-readable error message. + * The text is made by formatting @fmt, @ap like vprintf(). */ -ErrorClass error_get_class(const Error *err); +void error_vprepend(Error **errp, const char *fmt, va_list ap); -/** - * Returns an exact copy of the error passed as an argument. +/* + * Prepend some text to @errp's human-readable error message. + * The text is made by formatting @fmt, ... like printf(). */ -Error *error_copy(const Error *err); +void error_prepend(Error **errp, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); -/** - * Get a human readable representation of an error object. +/* + * Append a printf-style human-readable explanation to an existing error. + * @errp may be NULL, but not &error_fatal or &error_abort. + * Trivially the case if you call it only after error_setg() or + * error_propagate(). + * May be called multiple times. The resulting hint should end with a + * newline. */ -const char *error_get_pretty(Error *err); +void error_append_hint(Error **errp, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); -/** - * Convenience function to error_report() and free an error object. +/* + * Convenience function to report open() failure. */ -void error_report_err(Error *); +#define error_setg_file_open(errp, os_errno, filename) \ + error_setg_file_open_internal((errp), __FILE__, __LINE__, __func__, \ + (os_errno), (filename)) +void error_setg_file_open_internal(Error **errp, + const char *src, int line, const char *func, + int os_errno, const char *filename); -/** - * Propagate an error to an indirect pointer to an error. This function will - * always transfer ownership of the error reference and handles the case where - * dst_err is NULL correctly. Errors after the first are discarded. +/* + * Return an exact copy of @err. */ -void error_propagate(Error **dst_errp, Error *local_err); +Error *error_copy(const Error *err); -/** - * Free an error object. +/* + * Free @err. + * @err may be NULL. */ void error_free(Error *err); -/** - * If passed to error_set and friends, abort(). +/* + * Convenience function to assert that *@errp is set, then silently free it. + */ +void error_free_or_abort(Error **errp); + +/* + * Convenience function to error_report() and free @err. */ +void error_report_err(Error *err); +/* + * Convenience function to error_prepend(), error_report() and free @err. + */ +void error_reportf_err(Error *err, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); + +/* + * Just like error_setg(), except you get to specify the error class. + * Note: use of error classes other than ERROR_CLASS_GENERIC_ERROR is + * strongly discouraged. + */ +#define error_set(errp, err_class, fmt, ...) \ + error_set_internal((errp), __FILE__, __LINE__, __func__, \ + (err_class), (fmt), ## __VA_ARGS__) +void error_set_internal(Error **errp, + const char *src, int line, const char *func, + ErrorClass err_class, const char *fmt, ...) + GCC_FMT_ATTR(6, 7); + +/* + * Special error destination to abort on error. + * See error_setg() and error_propagate() for details. + */ extern Error *error_abort; +/* + * Special error destination to exit(1) on error. + * See error_setg() and error_propagate() for details. + */ +extern Error *error_fatal; + #endif diff --git a/qemu/include/qapi/qmp-event.h b/qemu/include/qapi/qmp-event.h index 8a8ffb571..40fe3cbc1 100644 --- a/qemu/include/qapi/qmp-event.h +++ b/qemu/include/qapi/qmp-event.h @@ -14,7 +14,6 @@ #ifndef QMP_EVENT_H #define QMP_EVENT_H -#include "qapi/error.h" #include "qapi/qmp/qdict.h" typedef void (*QMPEventFuncEmit)(unsigned event, QDict *dict, Error **errp); diff --git a/qemu/include/qapi/qmp/dispatch.h b/qemu/include/qapi/qmp/dispatch.h index e389697f1..495520994 100644 --- a/qemu/include/qapi/qmp/dispatch.h +++ b/qemu/include/qapi/qmp/dispatch.h @@ -16,7 +16,6 @@ #include "qapi/qmp/qobject.h" #include "qapi/qmp/qdict.h" -#include "qapi/error.h" typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); diff --git a/qemu/include/qapi/qmp/json-lexer.h b/qemu/include/qapi/qmp/json-lexer.h index cdff0460a..afee7828c 100644 --- a/qemu/include/qapi/qmp/json-lexer.h +++ b/qemu/include/qapi/qmp/json-lexer.h @@ -14,11 +14,15 @@ #ifndef QEMU_JSON_LEXER_H #define QEMU_JSON_LEXER_H -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qlist.h" typedef enum json_token_type { - JSON_OPERATOR = 100, + JSON_MIN = 100, + JSON_LCURLY = JSON_MIN, + JSON_RCURLY, + JSON_LSQUARE, + JSON_RSQUARE, + JSON_COLON, + JSON_COMMA, JSON_INTEGER, JSON_FLOAT, JSON_KEYWORD, @@ -30,13 +34,14 @@ typedef enum json_token_type { typedef struct JSONLexer JSONLexer; -typedef void (JSONLexerEmitter)(JSONLexer *, QString *, JSONTokenType, int x, int y); +typedef void (JSONLexerEmitter)(JSONLexer *, GString *, + JSONTokenType, int x, int y); struct JSONLexer { JSONLexerEmitter *emit; int state; - QString *token; + GString *token; int x, y; }; diff --git a/qemu/include/qapi/qmp/json-parser.h b/qemu/include/qapi/qmp/json-parser.h index 44d88f346..9987f8ca8 100644 --- a/qemu/include/qapi/qmp/json-parser.h +++ b/qemu/include/qapi/qmp/json-parser.h @@ -16,9 +16,8 @@ #include "qemu-common.h" #include "qapi/qmp/qlist.h" -#include "qapi/error.h" -QObject *json_parser_parse(QList *tokens, va_list *ap); -QObject *json_parser_parse_err(QList *tokens, va_list *ap, Error **errp); +QObject *json_parser_parse(GQueue *tokens, va_list *ap); +QObject *json_parser_parse_err(GQueue *tokens, va_list *ap, Error **errp); #endif diff --git a/qemu/include/qapi/qmp/json-streamer.h b/qemu/include/qapi/qmp/json-streamer.h index 823f7d7fa..00d8a23af 100644 --- a/qemu/include/qapi/qmp/json-streamer.h +++ b/qemu/include/qapi/qmp/json-streamer.h @@ -14,21 +14,27 @@ #ifndef QEMU_JSON_STREAMER_H #define QEMU_JSON_STREAMER_H -#include "qapi/qmp/qlist.h" #include "qapi/qmp/json-lexer.h" +typedef struct JSONToken { + int type; + int x; + int y; + char str[]; +} JSONToken; + typedef struct JSONMessageParser { - void (*emit)(struct JSONMessageParser *parser, QList *tokens); + void (*emit)(struct JSONMessageParser *parser, GQueue *tokens); JSONLexer lexer; int brace_count; int bracket_count; - QList *tokens; + GQueue *tokens; uint64_t token_size; } JSONMessageParser; void json_message_parser_init(JSONMessageParser *parser, - void (*func)(JSONMessageParser *, QList *)); + void (*func)(JSONMessageParser *, GQueue *)); int json_message_parser_feed(JSONMessageParser *parser, const char *buffer, size_t size); diff --git a/qemu/include/qapi/qmp/qbool.h b/qemu/include/qapi/qmp/qbool.h index 4aa6be3b3..a41111c30 100644 --- a/qemu/include/qapi/qmp/qbool.h +++ b/qemu/include/qapi/qmp/qbool.h @@ -14,16 +14,16 @@ #ifndef QBOOL_H #define QBOOL_H -#include #include "qapi/qmp/qobject.h" typedef struct QBool { - QObject_HEAD; + QObject base; bool value; } QBool; QBool *qbool_from_bool(bool value); bool qbool_get_bool(const QBool *qb); QBool *qobject_to_qbool(const QObject *obj); +void qbool_destroy_obj(QObject *obj); #endif /* QBOOL_H */ diff --git a/qemu/include/qapi/qmp/qdict.h b/qemu/include/qapi/qmp/qdict.h index a37f4c156..71b8eb041 100644 --- a/qemu/include/qapi/qmp/qdict.h +++ b/qemu/include/qapi/qmp/qdict.h @@ -16,8 +16,6 @@ #include "qapi/qmp/qobject.h" #include "qapi/qmp/qlist.h" #include "qemu/queue.h" -#include -#include #define QDICT_BUCKET_MAX 512 @@ -28,7 +26,7 @@ typedef struct QDictEntry { } QDictEntry; typedef struct QDict { - QObject_HEAD; + QObject base; size_t size; QLIST_HEAD(,QDictEntry) table[QDICT_BUCKET_MAX]; } QDict; @@ -48,6 +46,7 @@ void qdict_iter(const QDict *qdict, void *opaque); const QDictEntry *qdict_first(const QDict *qdict); const QDictEntry *qdict_next(const QDict *qdict, const QDictEntry *entry); +void qdict_destroy_obj(QObject *obj); /* Helper to qdict_put_obj(), accepts any object */ #define qdict_put(qdict, key, obj) \ diff --git a/qemu/include/qapi/qmp/qerror.h b/qemu/include/qapi/qmp/qerror.h index 842b27ae1..d08652aaa 100644 --- a/qemu/include/qapi/qmp/qerror.h +++ b/qemu/include/qapi/qmp/qerror.h @@ -100,10 +100,10 @@ #define QERR_UNDEFINED_ERROR \ "An undefined error has occurred" -#define QERR_UNKNOWN_BLOCK_FORMAT_FEATURE \ - "'%s' uses a %s feature which is not supported by this qemu version: %s" - #define QERR_UNSUPPORTED \ "this feature or command is not currently supported" +#define QERR_REPLAY_NOT_SUPPORTED \ + "Record/replay feature is not supported for '%s'" + #endif /* QERROR_H */ diff --git a/qemu/include/qapi/qmp/qfloat.h b/qemu/include/qapi/qmp/qfloat.h index a8658443d..b5d15836b 100644 --- a/qemu/include/qapi/qmp/qfloat.h +++ b/qemu/include/qapi/qmp/qfloat.h @@ -14,16 +14,16 @@ #ifndef QFLOAT_H #define QFLOAT_H -#include #include "qapi/qmp/qobject.h" typedef struct QFloat { - QObject_HEAD; + QObject base; double value; } QFloat; QFloat *qfloat_from_double(double value); double qfloat_get_double(const QFloat *qi); QFloat *qobject_to_qfloat(const QObject *obj); +void qfloat_destroy_obj(QObject *obj); #endif /* QFLOAT_H */ diff --git a/qemu/include/qapi/qmp/qint.h b/qemu/include/qapi/qmp/qint.h index 48a41b0f2..3aaff768d 100644 --- a/qemu/include/qapi/qmp/qint.h +++ b/qemu/include/qapi/qmp/qint.h @@ -13,16 +13,16 @@ #ifndef QINT_H #define QINT_H -#include #include "qapi/qmp/qobject.h" typedef struct QInt { - QObject_HEAD; + QObject base; int64_t value; } QInt; QInt *qint_from_int(int64_t value); int64_t qint_get_int(const QInt *qi); QInt *qobject_to_qint(const QObject *obj); +void qint_destroy_obj(QObject *obj); #endif /* QINT_H */ diff --git a/qemu/include/qapi/qmp/qjson.h b/qemu/include/qapi/qmp/qjson.h index ee4d31a46..02b1f2ce3 100644 --- a/qemu/include/qapi/qmp/qjson.h +++ b/qemu/include/qapi/qmp/qjson.h @@ -14,8 +14,6 @@ #ifndef QJSON_H #define QJSON_H -#include -#include "qemu/compiler.h" #include "qapi/qmp/qobject.h" #include "qapi/qmp/qstring.h" diff --git a/qemu/include/qapi/qmp/qlist.h b/qemu/include/qapi/qmp/qlist.h index 6cc4831df..a84117ecb 100644 --- a/qemu/include/qapi/qmp/qlist.h +++ b/qemu/include/qapi/qmp/qlist.h @@ -22,7 +22,7 @@ typedef struct QListEntry { } QListEntry; typedef struct QList { - QObject_HEAD; + QObject base; QTAILQ_HEAD(,QListEntry) head; } QList; @@ -49,6 +49,7 @@ 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); +void qlist_destroy_obj(QObject *obj); static inline const QListEntry *qlist_first(const QList *qlist) { diff --git a/qemu/include/qapi/qmp/qobject.h b/qemu/include/qapi/qmp/qobject.h index 260d2ed3c..b8ddbca40 100644 --- a/qemu/include/qapi/qmp/qobject.h +++ b/qemu/include/qapi/qmp/qobject.h @@ -32,36 +32,12 @@ #ifndef QOBJECT_H #define QOBJECT_H -#include -#include +#include "qapi-types.h" -typedef enum { - QTYPE_NONE, /* sentinel value, no QObject has this type code */ - QTYPE_QNULL, - QTYPE_QINT, - QTYPE_QSTRING, - QTYPE_QDICT, - QTYPE_QLIST, - QTYPE_QFLOAT, - QTYPE_QBOOL, - QTYPE_MAX, -} qtype_code; - -struct QObject; - -typedef struct QType { - qtype_code code; - void (*destroy)(struct QObject *); -} QType; - -typedef struct QObject { - const QType *type; +struct QObject { + QType type; size_t refcnt; -} QObject; - -/* Objects definitions must include this */ -#define QObject_HEAD \ - QObject base +}; /* Get the 'base' part of an object */ #define QOBJECT(obj) (&(obj)->base) @@ -75,9 +51,12 @@ typedef struct QObject { qobject_decref(obj ? QOBJECT(obj) : NULL) /* Initialize an object to default values */ -#define QOBJECT_INIT(obj, qtype_type) \ - obj->base.refcnt = 1; \ - obj->base.type = qtype_type +static inline void qobject_init(QObject *obj, QType type) +{ + assert(QTYPE_NONE < type && type < QTYPE__MAX); + obj->refcnt = 1; + obj->type = type; +} /** * qobject_incref(): Increment QObject's reference count @@ -88,26 +67,30 @@ static inline void qobject_incref(QObject *obj) obj->refcnt++; } +/** + * qobject_destroy(): Free resources used by the object + */ +void qobject_destroy(QObject *obj); + /** * qobject_decref(): Decrement QObject's reference count, deallocate * when it reaches zero */ static inline void qobject_decref(QObject *obj) { + assert(!obj || obj->refcnt); if (obj && --obj->refcnt == 0) { - assert(obj->type != NULL); - assert(obj->type->destroy != NULL); - obj->type->destroy(obj); + qobject_destroy(obj); } } /** * qobject_type(): Return the QObject's type */ -static inline qtype_code qobject_type(const QObject *obj) +static inline QType qobject_type(const QObject *obj) { - assert(obj->type != NULL); - return obj->type->code; + assert(QTYPE_NONE < obj->type && obj->type < QTYPE__MAX); + return obj->type; } extern QObject qnull_; diff --git a/qemu/include/qapi/qmp/qstring.h b/qemu/include/qapi/qmp/qstring.h index 1bc366610..10076b7c8 100644 --- a/qemu/include/qapi/qmp/qstring.h +++ b/qemu/include/qapi/qmp/qstring.h @@ -13,11 +13,10 @@ #ifndef QSTRING_H #define QSTRING_H -#include #include "qapi/qmp/qobject.h" typedef struct QString { - QObject_HEAD; + QObject base; char *string; size_t length; size_t capacity; @@ -32,5 +31,6 @@ 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); +void qstring_destroy_obj(QObject *obj); #endif /* QSTRING_H */ diff --git a/qemu/include/qapi/visitor-impl.h b/qemu/include/qapi/visitor-impl.h index f4a2f746c..2bd8f292b 100644 --- a/qemu/include/qapi/visitor-impl.h +++ b/qemu/include/qapi/visitor-impl.h @@ -1,7 +1,7 @@ /* * Core Definitions for QAPI Visitor implementations * - * Copyright (C) 2012 Red Hat, Inc. + * Copyright (C) 2012-2016 Red Hat, Inc. * * Author: Paolo Bonizni * @@ -12,56 +12,57 @@ #ifndef QAPI_VISITOR_IMPL_H #define QAPI_VISITOR_IMPL_H -#include "qapi/error.h" #include "qapi/visitor.h" struct Visitor { /* Must be set */ - void (*start_struct)(Visitor *v, void **obj, const char *kind, - const char *name, size_t size, Error **errp); + void (*start_struct)(Visitor *v, const char *name, void **obj, + size_t size, Error **errp); void (*end_struct)(Visitor *v, Error **errp); - void (*start_implicit_struct)(Visitor *v, void **obj, size_t size, - Error **errp); - void (*end_implicit_struct)(Visitor *v, Error **errp); - void (*start_list)(Visitor *v, const char *name, Error **errp); - GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp); - void (*end_list)(Visitor *v, Error **errp); + /* Must be set */ + GenericList *(*next_list)(Visitor *v, GenericList **list, size_t size); + /* Must be set */ + void (*end_list)(Visitor *v); - void (*type_enum)(Visitor *v, int *obj, const char * const strings[], - const char *kind, const char *name, Error **errp); - void (*get_next_type)(Visitor *v, int *kind, const int *qobjects, - const char *name, Error **errp); + /* Optional, needed for input and dealloc visitors. */ + void (*start_alternate)(Visitor *v, const char *name, + GenericAlternate **obj, size_t size, + bool promote_int, Error **errp); - void (*type_int)(Visitor *v, int64_t *obj, const char *name, Error **errp); - void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp); - void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp); - void (*type_number)(Visitor *v, double *obj, const char *name, - Error **errp); + /* Optional, needed for dealloc visitor. */ + void (*end_alternate)(Visitor *v); - /* May be NULL */ - void (*optional)(Visitor *v, bool *present, const char *name, + /* Must be set. */ + void (*type_enum)(Visitor *v, const char *name, int *obj, + const char *const strings[], Error **errp); + + /* Must be set. */ + void (*type_int64)(Visitor *v, const char *name, int64_t *obj, + Error **errp); + /* Must be set. */ + void (*type_uint64)(Visitor *v, const char *name, uint64_t *obj, + Error **errp); + /* Optional; fallback is type_uint64(). */ + void (*type_size)(Visitor *v, const char *name, uint64_t *obj, + Error **errp); + /* Must be set. */ + void (*type_bool)(Visitor *v, const char *name, bool *obj, Error **errp); + void (*type_str)(Visitor *v, const char *name, char **obj, Error **errp); + void (*type_number)(Visitor *v, const char *name, double *obj, + Error **errp); + void (*type_any)(Visitor *v, const char *name, QObject **obj, Error **errp); - void (*type_uint8)(Visitor *v, uint8_t *obj, const char *name, Error **errp); - void (*type_uint16)(Visitor *v, uint16_t *obj, const char *name, Error **errp); - void (*type_uint32)(Visitor *v, uint32_t *obj, const char *name, Error **errp); - void (*type_uint64)(Visitor *v, uint64_t *obj, const char *name, Error **errp); - void (*type_int8)(Visitor *v, int8_t *obj, const char *name, Error **errp); - void (*type_int16)(Visitor *v, int16_t *obj, const char *name, Error **errp); - void (*type_int32)(Visitor *v, int32_t *obj, const char *name, Error **errp); - void (*type_int64)(Visitor *v, int64_t *obj, const char *name, Error **errp); - /* visit_type_size() falls back to (*type_uint64)() if type_size is unset */ - void (*type_size)(Visitor *v, uint64_t *obj, const char *name, Error **errp); - bool (*start_union)(Visitor *v, bool data_present, Error **errp); - void (*end_union)(Visitor *v, bool data_present, Error **errp); + /* May be NULL; most useful for input visitors. */ + void (*optional)(Visitor *v, const char *name, bool *present); }; -void input_type_enum(Visitor *v, int *obj, const char * const strings[], - const char *kind, const char *name, Error **errp); -void output_type_enum(Visitor *v, int *obj, const char * const strings[], - const char *kind, const char *name, Error **errp); +void input_type_enum(Visitor *v, const char *name, int *obj, + const char *const strings[], Error **errp); +void output_type_enum(Visitor *v, const char *name, int *obj, + const char *const strings[], Error **errp); #endif diff --git a/qemu/include/qapi/visitor.h b/qemu/include/qapi/visitor.h index 00ba104cd..9a8d0105f 100644 --- a/qemu/include/qapi/visitor.h +++ b/qemu/include/qapi/visitor.h @@ -1,6 +1,7 @@ /* * Core Definitions for QAPI Visitor Classes * + * Copyright (C) 2012-2016 Red Hat, Inc. * Copyright IBM, Corp. 2011 * * Authors: @@ -13,52 +14,94 @@ #ifndef QAPI_VISITOR_CORE_H #define QAPI_VISITOR_CORE_H -#include "qemu/typedefs.h" #include "qapi/qmp/qobject.h" -#include "qapi/error.h" -#include -typedef struct GenericList -{ - union { - void *value; - uint64_t padding; - }; +/* This struct is layout-compatible with all other *List structs + * created by the qapi generator. It is used as a typical + * singly-linked list. */ +typedef struct GenericList { struct GenericList *next; + char padding[]; } GenericList; -void visit_start_handle(Visitor *v, void **obj, const char *kind, - const char *name, Error **errp); -void visit_end_handle(Visitor *v, Error **errp); -void visit_start_struct(Visitor *v, void **obj, const char *kind, - const char *name, size_t size, Error **errp); +/* This struct is layout-compatible with all Alternate types + * created by the qapi generator. */ +typedef struct GenericAlternate { + QType type; + char padding[]; +} GenericAlternate; + +void visit_start_struct(Visitor *v, const char *name, void **obj, + size_t size, Error **errp); void visit_end_struct(Visitor *v, Error **errp); -void visit_start_implicit_struct(Visitor *v, void **obj, size_t size, - Error **errp); -void visit_end_implicit_struct(Visitor *v, Error **errp); + void visit_start_list(Visitor *v, const char *name, Error **errp); -GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp); -void visit_end_list(Visitor *v, Error **errp); -void visit_optional(Visitor *v, bool *present, const char *name, - Error **errp); -void visit_get_next_type(Visitor *v, int *obj, const int *qtypes, - const char *name, Error **errp); -void visit_type_enum(Visitor *v, int *obj, const char * const strings[], - const char *kind, const char *name, Error **errp); -void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp); -void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp); -void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name, Error **errp); -void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name, Error **errp); -void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp); -void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp); -void visit_type_int16(Visitor *v, int16_t *obj, const char *name, Error **errp); -void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp); -void visit_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp); -void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp); -void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp); -void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp); -void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp); -bool visit_start_union(Visitor *v, bool data_present, Error **errp); -void visit_end_union(Visitor *v, bool data_present, Error **errp); +GenericList *visit_next_list(Visitor *v, GenericList **list, size_t size); +void visit_end_list(Visitor *v); + +/* + * Start the visit of an alternate @obj with the given @size. + * + * @name specifies the relationship to the containing struct (ignored + * for a top level visit, the name of the key if this alternate is + * part of an object, or NULL if this alternate is part of a list). + * + * @obj must not be NULL. Input visitors will allocate @obj and + * determine the qtype of the next thing to be visited, stored in + * (*@obj)->type. Other visitors will leave @obj unchanged. + * + * If @promote_int, treat integers as QTYPE_FLOAT. + * + * If successful, this must be paired with visit_end_alternate(), even + * if visiting the contents of the alternate fails. + */ +void visit_start_alternate(Visitor *v, const char *name, + GenericAlternate **obj, size_t size, + bool promote_int, Error **errp); + +/* + * Finish visiting an alternate type. + * + * Must be called after a successful visit_start_alternate(), even if + * an error occurred in the meantime. + * + * TODO: Should all the visit_end_* interfaces take obj parameter, so + * that dealloc visitor need not track what was passed in visit_start? + */ +void visit_end_alternate(Visitor *v); + +/** + * Check if an optional member @name of an object needs visiting. + * For input visitors, set *@present according to whether the + * corresponding visit_type_*() needs calling; for other visitors, + * leave *@present unchanged. Return *@present for convenience. + */ +bool visit_optional(Visitor *v, const char *name, bool *present); + +void visit_type_enum(Visitor *v, const char *name, int *obj, + const char *const strings[], Error **errp); +void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp); +void visit_type_uint8(Visitor *v, const char *name, uint8_t *obj, + Error **errp); +void visit_type_uint16(Visitor *v, const char *name, uint16_t *obj, + Error **errp); +void visit_type_uint32(Visitor *v, const char *name, uint32_t *obj, + Error **errp); +void visit_type_uint64(Visitor *v, const char *name, uint64_t *obj, + Error **errp); +void visit_type_int8(Visitor *v, const char *name, int8_t *obj, Error **errp); +void visit_type_int16(Visitor *v, const char *name, int16_t *obj, + Error **errp); +void visit_type_int32(Visitor *v, const char *name, int32_t *obj, + Error **errp); +void visit_type_int64(Visitor *v, const char *name, int64_t *obj, + Error **errp); +void visit_type_size(Visitor *v, const char *name, uint64_t *obj, + Error **errp); +void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp); +void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp); +void visit_type_number(Visitor *v, const char *name, double *obj, + Error **errp); +void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp); #endif diff --git a/qemu/include/qemu-common.h b/qemu/include/qemu-common.h index fb3da6ca2..163bcbb86 100644 --- a/qemu/include/qemu-common.h +++ b/qemu/include/qemu-common.h @@ -12,9 +12,6 @@ #ifndef QEMU_COMMON_H #define QEMU_COMMON_H -#include "qemu/compiler.h" -#include "config-host.h" -#include "qemu/typedefs.h" #include "qemu/fprintf-fn.h" #if defined(__arm__) || defined(__sparc__) || defined(__mips__) || defined(__hppa__) || defined(__ia64__) @@ -23,85 +20,8 @@ #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) -/* we put basic includes here to avoid repeating them in device drivers */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "glib-compat.h" #include "qemu/option.h" - -#ifdef _WIN32 -#include "sysemu/os-win32.h" -#endif - -#ifdef CONFIG_POSIX -#include "sysemu/os-posix.h" -#endif - -#ifndef O_LARGEFILE -#define O_LARGEFILE 0 -#endif -#ifndef O_BINARY -#define O_BINARY 0 -#endif -#ifndef MAP_ANONYMOUS -#define MAP_ANONYMOUS MAP_ANON -#endif -#ifndef ENOMEDIUM -#define ENOMEDIUM ENODEV -#endif -#if !defined(ENOTSUP) -#define ENOTSUP 4096 -#endif -#if !defined(ECANCELED) -#define ECANCELED 4097 -#endif -#if !defined(EMEDIUMTYPE) -#define EMEDIUMTYPE 4098 -#endif -#ifndef TIME_MAX -#define TIME_MAX LONG_MAX -#endif - -/* HOST_LONG_BITS is the size of a native pointer in bits. */ -#if UINTPTR_MAX == UINT32_MAX -# define HOST_LONG_BITS 32 -#elif UINTPTR_MAX == UINT64_MAX -# define HOST_LONG_BITS 64 -#else -# error Unknown pointer size -#endif - -#ifdef _WIN32 -#define fsync _commit -#if !defined(lseek) -# define lseek _lseeki64 -#endif -int qemu_ftruncate64(int, int64_t); -#if !defined(ftruncate) -# define ftruncate qemu_ftruncate64 -#endif - -static inline char *realpath(const char *path, char *resolved_path) -{ - _fullpath(resolved_path, path, _MAX_PATH); - return resolved_path; -} -#endif +#include "qemu/host-utils.h" void cpu_ticks_init(void); @@ -114,7 +34,6 @@ extern int64_t max_delay; extern int64_t max_advance; void dump_drift_info(FILE *f, fprintf_function cpu_fprintf); -#include "qemu/osdep.h" #include "qemu/bswap.h" /* FIXME: Remove NEED_CPU_H. */ @@ -130,189 +49,6 @@ int qemu_main(int argc, char **argv, char **envp); void qemu_get_timedate(struct tm *tm, int offset); int qemu_timedate_diff(struct tm *tm); -/** - * is_help_option: - * @s: string to test - * - * Check whether @s is one of the standard strings which indicate - * that the user is asking for a list of the valid values for a - * command option like -cpu or -M. The current accepted strings - * are 'help' and '?'. '?' is deprecated (it is a shell wildcard - * which makes it annoying to use in a reliable way) but provided - * for backwards compatibility. - * - * Returns: true if @s is a request for a list. - */ -static inline bool is_help_option(const char *s) -{ - return !strcmp(s, "?") || !strcmp(s, "help"); -} - -/* util/cutils.c */ -/** - * pstrcpy: - * @buf: buffer to copy string into - * @buf_size: size of @buf in bytes - * @str: string to copy - * - * Copy @str into @buf, including the trailing NUL, but do not - * write more than @buf_size bytes. The resulting buffer is - * always NUL terminated (even if the source string was too long). - * If @buf_size is zero or negative then no bytes are copied. - * - * This function is similar to strncpy(), but avoids two of that - * function's problems: - * * if @str fits in the buffer, pstrcpy() does not zero-fill the - * remaining space at the end of @buf - * * if @str is too long, pstrcpy() will copy the first @buf_size-1 - * bytes and then add a NUL - */ -void pstrcpy(char *buf, int buf_size, const char *str); -/** - * strpadcpy: - * @buf: buffer to copy string into - * @buf_size: size of @buf in bytes - * @str: string to copy - * @pad: character to pad the remainder of @buf with - * - * Copy @str into @buf (but *not* its trailing NUL!), and then pad the - * rest of the buffer with the @pad character. If @str is too large - * for the buffer then it is truncated, so that @buf contains the - * first @buf_size characters of @str, with no terminator. - */ -void strpadcpy(char *buf, int buf_size, const char *str, char pad); -/** - * pstrcat: - * @buf: buffer containing existing string - * @buf_size: size of @buf in bytes - * @s: string to concatenate to @buf - * - * Append a copy of @s to the string already in @buf, but do not - * allow the buffer to overflow. If the existing contents of @buf - * plus @str would total more than @buf_size bytes, then write - * as much of @str as will fit followed by a NUL terminator. - * - * @buf must already contain a NUL-terminated string, or the - * behaviour is undefined. - * - * Returns: @buf. - */ -char *pstrcat(char *buf, int buf_size, const char *s); -/** - * strstart: - * @str: string to test - * @val: prefix string to look for - * @ptr: NULL, or pointer to be written to indicate start of - * the remainder of the string - * - * Test whether @str starts with the prefix @val. - * If it does (including the degenerate case where @str and @val - * are equal) then return true. If @ptr is not NULL then a - * pointer to the first character following the prefix is written - * to it. If @val is not a prefix of @str then return false (and - * @ptr is not written to). - * - * Returns: true if @str starts with prefix @val, false otherwise. - */ -int strstart(const char *str, const char *val, const char **ptr); -/** - * stristart: - * @str: string to test - * @val: prefix string to look for - * @ptr: NULL, or pointer to be written to indicate start of - * the remainder of the string - * - * Test whether @str starts with the case-insensitive prefix @val. - * This function behaves identically to strstart(), except that the - * comparison is made after calling qemu_toupper() on each pair of - * characters. - * - * Returns: true if @str starts with case-insensitive prefix @val, - * false otherwise. - */ -int stristart(const char *str, const char *val, const char **ptr); -/** - * qemu_strnlen: - * @s: string - * @max_len: maximum number of bytes in @s to scan - * - * Return the length of the string @s, like strlen(), but do not - * examine more than @max_len bytes of the memory pointed to by @s. - * If no NUL terminator is found within @max_len bytes, then return - * @max_len instead. - * - * This function has the same behaviour as the POSIX strnlen() - * function. - * - * Returns: length of @s in bytes, or @max_len, whichever is smaller. - */ -int qemu_strnlen(const char *s, int max_len); -/** - * qemu_strsep: - * @input: pointer to string to parse - * @delim: string containing delimiter characters to search for - * - * Locate the first occurrence of any character in @delim within - * the string referenced by @input, and replace it with a NUL. - * The location of the next character after the delimiter character - * is stored into @input. - * If the end of the string was reached without finding a delimiter - * character, then NULL is stored into @input. - * If @input points to a NULL pointer on entry, return NULL. - * The return value is always the original value of *@input (and - * so now points to a NUL-terminated string corresponding to the - * part of the input up to the first delimiter). - * - * This function has the same behaviour as the BSD strsep() function. - * - * Returns: the pointer originally in @input. - */ -char *qemu_strsep(char **input, const char *delim); -time_t mktimegm(struct tm *tm); -int qemu_fls(int i); -int qemu_fdatasync(int fd); -int fcntl_setfl(int fd, int flag); -int qemu_parse_fd(const char *param); - -int parse_uint(const char *s, unsigned long long *value, char **endptr, - int base); -int parse_uint_full(const char *s, unsigned long long *value, int base); - -/* - * strtosz() suffixes used to specify the default treatment of an - * argument passed to strtosz() without an explicit suffix. - * These should be defined using upper case characters in the range - * A-Z, as strtosz() will use qemu_toupper() on the given argument - * prior to comparison. - */ -#define STRTOSZ_DEFSUFFIX_EB 'E' -#define STRTOSZ_DEFSUFFIX_PB 'P' -#define STRTOSZ_DEFSUFFIX_TB 'T' -#define STRTOSZ_DEFSUFFIX_GB 'G' -#define STRTOSZ_DEFSUFFIX_MB 'M' -#define STRTOSZ_DEFSUFFIX_KB 'K' -#define STRTOSZ_DEFSUFFIX_B 'B' -int64_t strtosz(const char *nptr, char **end); -int64_t strtosz_suffix(const char *nptr, char **end, const char default_suffix); -int64_t strtosz_suffix_unit(const char *nptr, char **end, - const char default_suffix, int64_t unit); -#define K_BYTE (1ULL << 10) -#define M_BYTE (1ULL << 20) -#define G_BYTE (1ULL << 30) -#define T_BYTE (1ULL << 40) -#define P_BYTE (1ULL << 50) -#define E_BYTE (1ULL << 60) - -/* used to print char* safely */ -#define STR_OR_NULL(str) ((str) ? (str) : "null") - -/* id.c */ -bool id_wellformed(const char *id); - -/* path.c */ -void init_paths(const char *prefix); -const char *path(const char *pathname); - #define qemu_isalnum(c) isalnum((unsigned char)(c)) #define qemu_isalpha(c) isalpha((unsigned char)(c)) #define qemu_iscntrl(c) iscntrl((unsigned char)(c)) @@ -359,37 +95,11 @@ int qemu_openpty_raw(int *aslave, char *pty_name); sendto(sockfd, buf, len, flags, destaddr, addrlen) #endif -/* Error handling. */ - -void QEMU_NORETURN hw_error(const char *fmt, ...) GCC_FMT_ATTR(1, 2); - -struct ParallelIOArg { - void *buffer; - int count; -}; - -typedef int (*DMA_transfer_handler) (void *opaque, int nchan, int pos, int size); - -typedef uint64_t pcibus_t; - -typedef struct PCIHostDeviceAddress { - unsigned int domain; - unsigned int bus; - unsigned int slot; - unsigned int function; -} PCIHostDeviceAddress; - void tcg_exec_init(unsigned long tb_size); bool tcg_enabled(void); void cpu_exec_init_all(void); -/* CPU save/load. */ -#ifdef CPU_SAVE_VERSION -void cpu_save(QEMUFile *f, void *opaque); -int cpu_load(QEMUFile *f, void *opaque, int version_id); -#endif - /* Unblock cpu */ void qemu_cpu_kick_self(void); @@ -426,36 +136,6 @@ ssize_t qemu_co_send_recv(int sockfd, void *buf, size_t bytes, bool do_send); #define qemu_co_send(sockfd, buf, bytes) \ qemu_co_send_recv(sockfd, buf, bytes, true) -typedef struct QEMUIOVector { - struct iovec *iov; - int niov; - int nalloc; - size_t size; -} QEMUIOVector; - -void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint); -void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov); -void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len); -void qemu_iovec_concat(QEMUIOVector *dst, - QEMUIOVector *src, size_t soffset, size_t sbytes); -size_t qemu_iovec_concat_iov(QEMUIOVector *dst, - struct iovec *src_iov, unsigned int src_cnt, - size_t soffset, size_t sbytes); -bool qemu_iovec_is_zero(QEMUIOVector *qiov); -void qemu_iovec_destroy(QEMUIOVector *qiov); -void qemu_iovec_reset(QEMUIOVector *qiov); -size_t qemu_iovec_to_buf(QEMUIOVector *qiov, size_t offset, - void *buf, size_t bytes); -size_t qemu_iovec_from_buf(QEMUIOVector *qiov, size_t offset, - const void *buf, size_t bytes); -size_t qemu_iovec_memset(QEMUIOVector *qiov, size_t offset, - int fillc, size_t bytes); -ssize_t qemu_iovec_compare(QEMUIOVector *a, QEMUIOVector *b); -void qemu_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, void *buf); -void qemu_iovec_discard_back(QEMUIOVector *qiov, size_t bytes); - -bool buffer_is_zero(const void *buf, size_t len); - void qemu_progress_init(int enabled, float min_skip); void qemu_progress_end(void); void qemu_progress_print(float delta, int max); @@ -470,133 +150,24 @@ void os_setup_early_signal_handling(void); char *os_find_datadir(void); void os_parse_cmd_args(int index, const char *optarg); -/* Convert a byte between binary and BCD. */ -static inline uint8_t to_bcd(uint8_t val) -{ - return ((val / 10) << 4) | (val % 10); -} - -static inline uint8_t from_bcd(uint8_t val) -{ - return ((val >> 4) * 10) + (val & 0x0f); -} - -/* compute with 96 bit intermediate result: (a*b)/c */ -#ifdef CONFIG_INT128 -static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) -{ - return (__int128_t)a * b / c; -} -#else -static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) -{ - union { - uint64_t ll; - struct { -#ifdef HOST_WORDS_BIGENDIAN - uint32_t high, low; -#else - uint32_t low, high; -#endif - } l; - } u, res; - uint64_t rl, rh; - - u.ll = a; - rl = (uint64_t)u.l.low * (uint64_t)b; - rh = (uint64_t)u.l.high * (uint64_t)b; - rh += (rl >> 32); - res.l.high = rh / c; - res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c; - return res.ll; -} -#endif - -/* Round number down to multiple */ -#define QEMU_ALIGN_DOWN(n, m) ((n) / (m) * (m)) - -/* Round number up to multiple */ -#define QEMU_ALIGN_UP(n, m) QEMU_ALIGN_DOWN((n) + (m) - 1, (m)) - -static inline bool is_power_of_2(uint64_t value) -{ - if (!value) { - return 0; - } - - return !(value & (value - 1)); -} - -/* round down to the nearest power of 2*/ -int64_t pow2floor(int64_t value); - -/* round up to the nearest power of 2 (0 if overflow) */ -uint64_t pow2ceil(uint64_t value); - #include "qemu/module.h" -/* - * Implementation of ULEB128 (http://en.wikipedia.org/wiki/LEB128) - * Input is limited to 14-bit numbers - */ - -int uleb128_encode_small(uint8_t *out, uint32_t n); -int uleb128_decode_small(const uint8_t *in, uint32_t *n); - -/* unicode.c */ -int mod_utf8_codepoint(const char *s, size_t n, char **end); - /* * Hexdump a buffer to a file. An optional string prefix is added to every line */ void qemu_hexdump(const char *buf, FILE *fp, const char *prefix, size_t size); -/* vector definitions */ -#ifdef __ALTIVEC__ -#include -/* The altivec.h header says we're allowed to undef these for - * C++ compatibility. Here we don't care about C++, but we - * undef them anyway to avoid namespace pollution. - */ -#undef vector -#undef pixel -#undef bool -#define VECTYPE __vector unsigned char -#define SPLAT(p) vec_splat(vec_ld(0, p), 0) -#define ALL_EQ(v1, v2) vec_all_eq(v1, v2) -#define VEC_OR(v1, v2) ((v1) | (v2)) -/* altivec.h may redefine the bool macro as vector type. - * Reset it to POSIX semantics. */ -#define bool _Bool -#elif defined __SSE2__ -#include -#define VECTYPE __m128i -#define SPLAT(p) _mm_set1_epi8(*(p)) -#define ALL_EQ(v1, v2) (_mm_movemask_epi8(_mm_cmpeq_epi8(v1, v2)) == 0xFFFF) -#define VEC_OR(v1, v2) (_mm_or_si128(v1, v2)) -#else -#define VECTYPE unsigned long -#define SPLAT(p) (*(p) * (~0UL / 255)) -#define ALL_EQ(v1, v2) ((v1) == (v2)) -#define VEC_OR(v1, v2) ((v1) | (v2)) -#endif - -#define BUFFER_FIND_NONZERO_OFFSET_UNROLL_FACTOR 8 -static inline bool -can_use_buffer_find_nonzero_offset(const void *buf, size_t len) -{ - return (len % (BUFFER_FIND_NONZERO_OFFSET_UNROLL_FACTOR - * sizeof(VECTYPE)) == 0 - && ((uintptr_t) buf) % sizeof(VECTYPE) == 0); -} -size_t buffer_find_nonzero_offset(const void *buf, size_t len); - /* * helper to parse debug environment variables */ int parse_debug_env(const char *name, int max, int initial); const char *qemu_ether_ntoa(const MACAddr *mac); +void page_size_init(void); + +/* returns non-zero if dump is in progress, otherwise zero is + * returned. */ +bool dump_in_progress(void); #endif diff --git a/qemu/include/qemu/atomic.h b/qemu/include/qemu/atomic.h index bd2c07534..5bc4d6cc4 100644 --- a/qemu/include/qemu/atomic.h +++ b/qemu/include/qemu/atomic.h @@ -8,19 +8,148 @@ * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. * + * See docs/atomics.txt for discussion about the guarantees each + * atomic primitive is meant to provide. */ #ifndef __QEMU_ATOMIC_H #define __QEMU_ATOMIC_H 1 -#include "qemu/compiler.h" -/* For C11 atomic ops */ /* Compiler barrier */ #define barrier() ({ asm volatile("" ::: "memory"); (void)0; }) -#ifndef __ATOMIC_RELAXED +#ifdef __ATOMIC_RELAXED +/* For C11 atomic ops */ + +/* Manual memory barriers + * + *__atomic_thread_fence does not include a compiler barrier; instead, + * the barrier is part of __atomic_load/__atomic_store's "volatile-like" + * semantics. If smp_wmb() is a no-op, absence of the barrier means that + * the compiler is free to reorder stores on each side of the barrier. + * Add one here, and similarly in smp_rmb() and smp_read_barrier_depends(). + */ + +#define smp_mb() ({ barrier(); __atomic_thread_fence(__ATOMIC_SEQ_CST); barrier(); }) +#define smp_wmb() ({ barrier(); __atomic_thread_fence(__ATOMIC_RELEASE); barrier(); }) +#define smp_rmb() ({ barrier(); __atomic_thread_fence(__ATOMIC_ACQUIRE); barrier(); }) + +#define smp_read_barrier_depends() ({ barrier(); __atomic_thread_fence(__ATOMIC_CONSUME); barrier(); }) + +/* Weak atomic operations prevent the compiler moving other + * loads/stores past the atomic operation load/store. However there is + * no explicit memory barrier for the processor. + */ +#define atomic_read(ptr) \ + ({ \ + QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \ + typeof(*ptr) _val; \ + __atomic_load(ptr, &_val, __ATOMIC_RELAXED); \ + _val; \ + }) + +#define atomic_set(ptr, i) do { \ + QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \ + typeof(*ptr) _val = (i); \ + __atomic_store(ptr, &_val, __ATOMIC_RELAXED); \ +} while(0) + +/* Atomic RCU operations imply weak memory barriers */ + +#define atomic_rcu_read(ptr) \ + ({ \ + QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \ + typeof(*ptr) _val; \ + __atomic_load(ptr, &_val, __ATOMIC_CONSUME); \ + _val; \ + }) + +#define atomic_rcu_set(ptr, i) do { \ + QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \ + typeof(*ptr) _val = (i); \ + __atomic_store(ptr, &_val, __ATOMIC_RELEASE); \ +} while(0) + +/* atomic_mb_read/set semantics map Java volatile variables. They are + * less expensive on some platforms (notably POWER & ARMv7) than fully + * sequentially consistent operations. + * + * As long as they are used as paired operations they are safe to + * use. See docs/atomic.txt for more discussion. + */ + +#if defined(_ARCH_PPC) +#define atomic_mb_read(ptr) \ + ({ \ + QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \ + typeof(*ptr) _val; \ + __atomic_load(ptr, &_val, __ATOMIC_RELAXED); \ + smp_rmb(); \ + _val; \ + }) + +#define atomic_mb_set(ptr, i) do { \ + QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \ + typeof(*ptr) _val = (i); \ + smp_wmb(); \ + __atomic_store(ptr, &_val, __ATOMIC_RELAXED); \ + smp_mb(); \ +} while(0) +#else +#define atomic_mb_read(ptr) \ + ({ \ + QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \ + typeof(*ptr) _val; \ + __atomic_load(ptr, &_val, __ATOMIC_SEQ_CST); \ + _val; \ + }) + +#define atomic_mb_set(ptr, i) do { \ + QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \ + typeof(*ptr) _val = (i); \ + __atomic_store(ptr, &_val, __ATOMIC_SEQ_CST); \ +} while(0) +#endif + + +/* All the remaining operations are fully sequentially consistent */ + +#define atomic_xchg(ptr, i) ({ \ + QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \ + typeof(*ptr) _new = (i), _old; \ + __atomic_exchange(ptr, &_new, &_old, __ATOMIC_SEQ_CST); \ + _old; \ +}) + +/* Returns the eventual value, failed or not */ +#define atomic_cmpxchg(ptr, old, new) \ + ({ \ + QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \ + typeof(*ptr) _old = (old), _new = (new); \ + __atomic_compare_exchange(ptr, &_old, &_new, false, \ + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); \ + _old; \ + }) + +/* Provide shorter names for GCC atomic builtins, return old value */ +#define atomic_fetch_inc(ptr) __atomic_fetch_add(ptr, 1, __ATOMIC_SEQ_CST) +#define atomic_fetch_dec(ptr) __atomic_fetch_sub(ptr, 1, __ATOMIC_SEQ_CST) +#define atomic_fetch_add(ptr, n) __atomic_fetch_add(ptr, n, __ATOMIC_SEQ_CST) +#define atomic_fetch_sub(ptr, n) __atomic_fetch_sub(ptr, n, __ATOMIC_SEQ_CST) +#define atomic_fetch_and(ptr, n) __atomic_fetch_and(ptr, n, __ATOMIC_SEQ_CST) +#define atomic_fetch_or(ptr, n) __atomic_fetch_or(ptr, n, __ATOMIC_SEQ_CST) + +/* And even shorter names that return void. */ +#define atomic_inc(ptr) ((void) __atomic_fetch_add(ptr, 1, __ATOMIC_SEQ_CST)) +#define atomic_dec(ptr) ((void) __atomic_fetch_sub(ptr, 1, __ATOMIC_SEQ_CST)) +#define atomic_add(ptr, n) ((void) __atomic_fetch_add(ptr, n, __ATOMIC_SEQ_CST)) +#define atomic_sub(ptr, n) ((void) __atomic_fetch_sub(ptr, n, __ATOMIC_SEQ_CST)) +#define atomic_and(ptr, n) ((void) __atomic_fetch_and(ptr, n, __ATOMIC_SEQ_CST)) +#define atomic_or(ptr, n) ((void) __atomic_fetch_or(ptr, n, __ATOMIC_SEQ_CST)) + +#else /* __ATOMIC_RELAXED */ /* * We use GCC builtin if it's available, as that can use mfence on @@ -85,8 +214,6 @@ #endif /* _ARCH_PPC */ -#endif /* C11 atomics */ - /* * For (host) platforms we don't have explicit barrier definitions * for, we use the gcc __sync_synchronize() primitive to generate a @@ -98,42 +225,22 @@ #endif #ifndef smp_wmb -#ifdef __ATOMIC_RELEASE -/* __atomic_thread_fence does not include a compiler barrier; instead, - * the barrier is part of __atomic_load/__atomic_store's "volatile-like" - * semantics. If smp_wmb() is a no-op, absence of the barrier means that - * the compiler is free to reorder stores on each side of the barrier. - * Add one here, and similarly in smp_rmb() and smp_read_barrier_depends(). - */ -#define smp_wmb() ({ barrier(); __atomic_thread_fence(__ATOMIC_RELEASE); barrier(); }) -#else #define smp_wmb() __sync_synchronize() #endif -#endif #ifndef smp_rmb -#ifdef __ATOMIC_ACQUIRE -#define smp_rmb() ({ barrier(); __atomic_thread_fence(__ATOMIC_ACQUIRE); barrier(); }) -#else #define smp_rmb() __sync_synchronize() #endif -#endif #ifndef smp_read_barrier_depends -#ifdef __ATOMIC_CONSUME -#define smp_read_barrier_depends() ({ barrier(); __atomic_thread_fence(__ATOMIC_CONSUME); barrier(); }) -#else #define smp_read_barrier_depends() barrier() #endif -#endif -#ifndef atomic_read +/* These will only be atomic if the processor does the fetch or store + * in a single issue memory operation + */ #define atomic_read(ptr) (*(__typeof__(*ptr) volatile*) (ptr)) -#endif - -#ifndef atomic_set #define atomic_set(ptr, i) ((*(__typeof__(*ptr) volatile*) (ptr)) = (i)) -#endif /** * atomic_rcu_read - reads a RCU-protected pointer to a local variable @@ -146,30 +253,18 @@ * Inserts memory barriers on architectures that require them (currently only * Alpha) and documents which pointers are protected by RCU. * - * Unless the __ATOMIC_CONSUME memory order is available, atomic_rcu_read also - * includes a compiler barrier to ensure that value-speculative optimizations - * (e.g. VSS: Value Speculation Scheduling) does not perform the data read - * before the pointer read by speculating the value of the pointer. On new - * enough compilers, atomic_load takes care of such concern about - * dependency-breaking optimizations. + * atomic_rcu_read also includes a compiler barrier to ensure that + * value-speculative optimizations (e.g. VSS: Value Speculation + * Scheduling) does not perform the data read before the pointer read + * by speculating the value of the pointer. * * Should match atomic_rcu_set(), atomic_xchg(), atomic_cmpxchg(). */ -#ifndef atomic_rcu_read -#ifdef __ATOMIC_CONSUME -#define atomic_rcu_read(ptr) ({ \ - typeof(*ptr) _val; \ - __atomic_load(ptr, &_val, __ATOMIC_CONSUME); \ - _val; \ -}) -#else #define atomic_rcu_read(ptr) ({ \ typeof(*ptr) _val = atomic_read(ptr); \ smp_read_barrier_depends(); \ _val; \ }) -#endif -#endif /** * atomic_rcu_set - assigns (publicizes) a pointer to a new data structure @@ -182,19 +277,10 @@ * * Should match atomic_rcu_read(). */ -#ifndef atomic_rcu_set -#ifdef __ATOMIC_RELEASE -#define atomic_rcu_set(ptr, i) do { \ - typeof(*ptr) _val = (i); \ - __atomic_store(ptr, &_val, __ATOMIC_RELEASE); \ -} while(0) -#else #define atomic_rcu_set(ptr, i) do { \ smp_wmb(); \ atomic_set(ptr, i); \ } while (0) -#endif -#endif /* These have the same semantics as Java volatile variables. * See http://gee.cs.oswego.edu/dl/jmm/cookbook.html: @@ -218,13 +304,11 @@ * (see docs/atomics.txt), and I'm not sure that __ATOMIC_ACQ_REL is enough. * Just always use the barriers manually by the rules above. */ -#ifndef atomic_mb_read #define atomic_mb_read(ptr) ({ \ typeof(*ptr) _val = atomic_read(ptr); \ smp_rmb(); \ _val; \ }) -#endif #ifndef atomic_mb_set #define atomic_mb_set(ptr, i) do { \ @@ -237,12 +321,6 @@ #ifndef atomic_xchg #if defined(__clang__) #define atomic_xchg(ptr, i) __sync_swap(ptr, i) -#elif defined(__ATOMIC_SEQ_CST) -#define atomic_xchg(ptr, i) ({ \ - typeof(*ptr) _new = (i), _old; \ - __atomic_exchange(ptr, &_new, &_old, __ATOMIC_SEQ_CST); \ - _old; \ -}) #else /* __sync_lock_test_and_set() is documented to be an acquire barrier only. */ #define atomic_xchg(ptr, i) (smp_mb(), __sync_lock_test_and_set(ptr, i)) @@ -266,4 +344,5 @@ #define atomic_and(ptr, n) ((void) __sync_fetch_and_and(ptr, n)) #define atomic_or(ptr, n) ((void) __sync_fetch_and_or(ptr, n)) -#endif +#endif /* __ATOMIC_RELAXED */ +#endif /* __QEMU_ATOMIC_H */ diff --git a/qemu/include/qemu/base64.h b/qemu/include/qemu/base64.h new file mode 100644 index 000000000..793708dc3 --- /dev/null +++ b/qemu/include/qemu/base64.h @@ -0,0 +1,58 @@ +/* + * QEMU base64 helpers + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QEMU_BASE64_H__ +#define QEMU_BASE64_H__ + +#include "qemu-common.h" + + +/** + * qbase64_decode: + * @input: the (possibly) base64 encoded text + * @in_len: length of @input or -1 if NUL terminated + * @out_len: filled with length of decoded data + * @errp: pointer to a NULL-initialized error object + * + * Attempt to decode the (possibly) base64 encoded + * text provided in @input. If the @input text may + * contain embedded NUL characters, or may not be + * NUL terminated, then @in_len must be set to the + * known size of the @input buffer. + * + * Note that embedded NULs, or lack of a NUL terminator + * are considered invalid base64 data and errors + * will be reported to this effect. + * + * If decoding is successful, the decoded data will + * be returned and @out_len set to indicate the + * number of bytes in the decoded data. The caller + * must use g_free() to free the returned data when + * it is no longer required. + * + * Returns: the decoded data or NULL + */ +uint8_t *qbase64_decode(const char *input, + size_t in_len, + size_t *out_len, + Error **errp); + + +#endif /* QEMU_BUFFER_H__ */ diff --git a/qemu/include/qemu/bcd.h b/qemu/include/qemu/bcd.h new file mode 100644 index 000000000..b4c9b64b8 --- /dev/null +++ b/qemu/include/qemu/bcd.h @@ -0,0 +1,15 @@ +#ifndef QEMU_BCD_H +#define QEMU_BCD_H 1 + +/* Convert a byte between binary and BCD. */ +static inline uint8_t to_bcd(uint8_t val) +{ + return ((val / 10) << 4) | (val % 10); +} + +static inline uint8_t from_bcd(uint8_t val) +{ + return ((val >> 4) * 10) + (val & 0x0f); +} + +#endif diff --git a/qemu/include/qemu/bitmap.h b/qemu/include/qemu/bitmap.h index 86dd9cd5f..0e33fa5d9 100644 --- a/qemu/include/qemu/bitmap.h +++ b/qemu/include/qemu/bitmap.h @@ -13,10 +13,7 @@ #define BITMAP_H #include -#include -#include -#include "qemu/osdep.h" #include "qemu/bitops.h" /* diff --git a/qemu/include/qemu/bitops.h b/qemu/include/qemu/bitops.h index 816422515..755fdd129 100644 --- a/qemu/include/qemu/bitops.h +++ b/qemu/include/qemu/bitops.h @@ -12,8 +12,6 @@ #ifndef BITOPS_H #define BITOPS_H -#include -#include #include "host-utils.h" #include "atomic.h" diff --git a/qemu/include/qemu/bswap.h b/qemu/include/qemu/bswap.h index 07d88de74..ce3c42e4d 100644 --- a/qemu/include/qemu/bswap.h +++ b/qemu/include/qemu/bswap.h @@ -1,15 +1,10 @@ #ifndef BSWAP_H #define BSWAP_H -#include "config-host.h" -#include -#include -#include #include "fpu/softfloat.h" #ifdef CONFIG_MACHINE_BSWAP_H # include -# include # include #elif defined(__FreeBSD__) # include @@ -130,6 +125,25 @@ static inline uint32_t qemu_bswap_len(uint32_t value, int len) return bswap32(value) >> (32 - 8 * len); } +/* + * Same as cpu_to_le{16,23}, except that gcc will figure the result is + * a compile-time constant if you pass in a constant. So this can be + * used to initialize static variables. + */ +#if defined(HOST_WORDS_BIGENDIAN) +# define const_le32(_x) \ + ((((_x) & 0x000000ffU) << 24) | \ + (((_x) & 0x0000ff00U) << 8) | \ + (((_x) & 0x00ff0000U) >> 8) | \ + (((_x) & 0xff000000U) >> 24)) +# define const_le16(_x) \ + ((((_x) & 0x00ff) << 8) | \ + (((_x) & 0xff00) >> 8)) +#else +# define const_le32(_x) (_x) +# define const_le16(_x) (_x) +#endif + /* Unions for reinterpreting between floats and integers. */ typedef union { @@ -424,11 +438,9 @@ static inline void stfq_be_p(void *ptr, float64 v) static inline unsigned long leul_to_cpu(unsigned long v) { - /* In order to break an include loop between here and - qemu-common.h, don't rely on HOST_LONG_BITS. */ -#if ULONG_MAX == UINT32_MAX +#if HOST_LONG_BITS == 32 return le_bswap(v, 32); -#elif ULONG_MAX == UINT64_MAX +#elif HOST_LONG_BITS == 64 return le_bswap(v, 64); #else # error Unknown sizeof long diff --git a/qemu/include/qemu/buffer.h b/qemu/include/qemu/buffer.h new file mode 100644 index 000000000..dead9b77e --- /dev/null +++ b/qemu/include/qemu/buffer.h @@ -0,0 +1,161 @@ +/* + * QEMU generic buffers + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#ifndef QEMU_BUFFER_H__ +#define QEMU_BUFFER_H__ + +#include "qemu-common.h" + +typedef struct Buffer Buffer; + +/** + * Buffer: + * + * The Buffer object provides a simple dynamically resizing + * array, with separate tracking of capacity and usage. This + * is typically useful when buffering I/O or processing data. + */ + +struct Buffer { + char *name; + size_t capacity; + size_t offset; + uint64_t avg_size; + uint8_t *buffer; +}; + +/** + * buffer_init: + * @buffer: the buffer object + * @name: buffer name + * + * Optionally attach a name to the buffer, to make it easier + * to identify in debug traces. + */ +void buffer_init(Buffer *buffer, const char *name, ...) + GCC_FMT_ATTR(2, 3); + +/** + * buffer_shrink: + * @buffer: the buffer object + * + * Try to shrink the buffer. Checks current buffer capacity and size + * and reduces capacity in case only a fraction of the buffer is + * actually used. + */ +void buffer_shrink(Buffer *buffer); + +/** + * buffer_reserve: + * @buffer: the buffer object + * @len: the minimum required free space + * + * Ensure that the buffer has space allocated for at least + * @len bytes. If the current buffer is too small, it will + * be reallocated, possibly to a larger size than requested. + */ +void buffer_reserve(Buffer *buffer, size_t len); + +/** + * buffer_reset: + * @buffer: the buffer object + * + * Reset the length of the stored data to zero, but do + * not free / reallocate the memory buffer + */ +void buffer_reset(Buffer *buffer); + +/** + * buffer_free: + * @buffer: the buffer object + * + * Reset the length of the stored data to zero and also + * free the internal memory buffer + */ +void buffer_free(Buffer *buffer); + +/** + * buffer_append: + * @buffer: the buffer object + * @data: the data block to append + * @len: the length of @data in bytes + * + * Append the contents of @data to the end of the buffer. + * The caller must ensure that the buffer has sufficient + * free space for @len bytes, typically by calling the + * buffer_reserve() method prior to appending. + */ +void buffer_append(Buffer *buffer, const void *data, size_t len); + +/** + * buffer_advance: + * @buffer: the buffer object + * @len: the number of bytes to skip + * + * Remove @len bytes of data from the head of the buffer. + * The internal buffer will not be reallocated, so will + * have at least @len bytes of free space after this + * call completes + */ +void buffer_advance(Buffer *buffer, size_t len); + +/** + * buffer_end: + * @buffer: the buffer object + * + * Get a pointer to the tail end of the internal buffer + * The returned pointer is only valid until the next + * call to buffer_reserve(). + * + * Returns: the tail of the buffer + */ +uint8_t *buffer_end(Buffer *buffer); + +/** + * buffer_empty: + * @buffer: the buffer object + * + * Determine if the buffer contains any current data + * + * Returns: true if the buffer holds data, false otherwise + */ +gboolean buffer_empty(Buffer *buffer); + +/** + * buffer_move_empty: + * @to: destination buffer object + * @from: source buffer object + * + * Moves buffer, without copying data. 'to' buffer must be empty. + * 'from' buffer is empty and zero-sized on return. + */ +void buffer_move_empty(Buffer *to, Buffer *from); + +/** + * buffer_move: + * @to: destination buffer object + * @from: source buffer object + * + * Moves buffer, copying data (unless 'to' buffer happens to be empty). + * 'from' buffer is empty and zero-sized on return. + */ +void buffer_move(Buffer *to, Buffer *from); + +#endif /* QEMU_BUFFER_H__ */ diff --git a/qemu/include/qemu/compatfd.h b/qemu/include/qemu/compatfd.h index fc3791520..aa12ee936 100644 --- a/qemu/include/qemu/compatfd.h +++ b/qemu/include/qemu/compatfd.h @@ -14,7 +14,6 @@ #ifndef QEMU_COMPATFD_H #define QEMU_COMPATFD_H -#include struct qemu_signalfd_siginfo { uint32_t ssi_signo; /* Signal number */ diff --git a/qemu/include/qemu/compiler.h b/qemu/include/qemu/compiler.h index df9dd514f..8f1cc7ba6 100644 --- a/qemu/include/qemu/compiler.h +++ b/qemu/include/qemu/compiler.h @@ -3,7 +3,6 @@ #ifndef COMPILER_H #define COMPILER_H -#include "config-host.h" /*---------------------------------------------------------------------------- | The macro QEMU_GNUC_PREREQ tests for minimum version of the GNU C compiler. @@ -42,10 +41,43 @@ # define QEMU_PACKED __attribute__((packed)) #endif -#define cat(x,y) x ## y -#define cat2(x,y) cat(x,y) +#ifndef glue +#define xglue(x, y) x ## y +#define glue(x, y) xglue(x, y) +#define stringify(s) tostring(s) +#define tostring(s) #s +#endif + +#ifndef likely +#if __GNUC__ < 3 +#define __builtin_expect(x, n) (x) +#endif + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif + +#ifndef container_of +#define container_of(ptr, type, member) ({ \ + const typeof(((type *) 0)->member) *__mptr = (ptr); \ + (type *) ((char *) __mptr - offsetof(type, member));}) +#endif + +/* Convert from a base type to a parent type, with compile time checking. */ +#ifdef __GNUC__ +#define DO_UPCAST(type, field, dev) ( __extension__ ( { \ + char __attribute__((unused)) offset_must_be_zero[ \ + -offsetof(type, field)]; \ + container_of(dev, type, field);})) +#else +#define DO_UPCAST(type, field, dev) container_of(dev, type, field) +#endif + +#define typeof_field(type, field) typeof(((type *)0)->field) +#define type_check(t1,t2) ((t1*)0 - (t2*)0) + #define QEMU_BUILD_BUG_ON(x) \ - typedef char cat2(qemu_build_bug_on__,__LINE__)[(x)?-1:1] __attribute__((unused)); + typedef char glue(qemu_build_bug_on__,__LINE__)[(x)?-1:1] __attribute__((unused)); #if defined __GNUC__ # if !QEMU_GNUC_PREREQ(4, 4) diff --git a/qemu/include/qemu/config-file.h b/qemu/include/qemu/config-file.h index d4ba20e04..3b8ecb095 100644 --- a/qemu/include/qemu/config-file.h +++ b/qemu/include/qemu/config-file.h @@ -1,9 +1,7 @@ #ifndef QEMU_CONFIG_H #define QEMU_CONFIG_H -#include #include "qemu/option.h" -#include "qapi/error.h" #include "qapi/qmp/qdict.h" QemuOptsList *qemu_find_opts(const char *group); diff --git a/qemu/include/qemu/coroutine.h b/qemu/include/qemu/coroutine.h new file mode 100644 index 000000000..305fe76c2 --- /dev/null +++ b/qemu/include/qemu/coroutine.h @@ -0,0 +1,217 @@ +/* + * QEMU coroutine implementation + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Stefan Hajnoczi + * Kevin Wolf + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef QEMU_COROUTINE_H +#define QEMU_COROUTINE_H + +#include "qemu/queue.h" +#include "qemu/timer.h" + +/** + * Coroutines are a mechanism for stack switching and can be used for + * cooperative userspace threading. These functions provide a simple but + * useful flavor of coroutines that is suitable for writing sequential code, + * rather than callbacks, for operations that need to give up control while + * waiting for events to complete. + * + * These functions are re-entrant and may be used outside the global mutex. + */ + +/** + * Mark a function that executes in coroutine context + * + * Functions that execute in coroutine context cannot be called directly from + * normal functions. In the future it would be nice to enable compiler or + * static checker support for catching such errors. This annotation might make + * it possible and in the meantime it serves as documentation. + * + * For example: + * + * static void coroutine_fn foo(void) { + * .... + * } + */ +#define coroutine_fn + +typedef struct Coroutine Coroutine; + +/** + * Coroutine entry point + * + * When the coroutine is entered for the first time, opaque is passed in as an + * argument. + * + * When this function returns, the coroutine is destroyed automatically and + * execution continues in the caller who last entered the coroutine. + */ +typedef void coroutine_fn CoroutineEntry(void *opaque); + +/** + * Create a new coroutine + * + * Use qemu_coroutine_enter() to actually transfer control to the coroutine. + */ +Coroutine *qemu_coroutine_create(CoroutineEntry *entry); + +/** + * Transfer control to a coroutine + * + * The opaque argument is passed as the argument to the entry point when + * entering the coroutine for the first time. It is subsequently ignored. + */ +void qemu_coroutine_enter(Coroutine *coroutine, void *opaque); + +/** + * Transfer control back to a coroutine's caller + * + * This function does not return until the coroutine is re-entered using + * qemu_coroutine_enter(). + */ +void coroutine_fn qemu_coroutine_yield(void); + +/** + * Get the currently executing coroutine + */ +Coroutine *coroutine_fn qemu_coroutine_self(void); + +/** + * Return whether or not currently inside a coroutine + * + * This can be used to write functions that work both when in coroutine context + * and when not in coroutine context. Note that such functions cannot use the + * coroutine_fn annotation since they work outside coroutine context. + */ +bool qemu_in_coroutine(void); + + + +/** + * CoQueues are a mechanism to queue coroutines in order to continue executing + * them later. They provide the fundamental primitives on which coroutine locks + * are built. + */ +typedef struct CoQueue { + QTAILQ_HEAD(, Coroutine) entries; +} CoQueue; + +/** + * Initialise a CoQueue. This must be called before any other operation is used + * on the CoQueue. + */ +void qemu_co_queue_init(CoQueue *queue); + +/** + * Adds the current coroutine to the CoQueue and transfers control to the + * caller of the coroutine. + */ +void coroutine_fn qemu_co_queue_wait(CoQueue *queue); + +/** + * Restarts the next coroutine in the CoQueue and removes it from the queue. + * + * Returns true if a coroutine was restarted, false if the queue is empty. + */ +bool coroutine_fn qemu_co_queue_next(CoQueue *queue); + +/** + * Restarts all coroutines in the CoQueue and leaves the queue empty. + */ +void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue); + +/** + * Enter the next coroutine in the queue + */ +bool qemu_co_enter_next(CoQueue *queue); + +/** + * Checks if the CoQueue is empty. + */ +bool qemu_co_queue_empty(CoQueue *queue); + + +/** + * Provides a mutex that can be used to synchronise coroutines + */ +typedef struct CoMutex { + bool locked; + CoQueue queue; +} CoMutex; + +/** + * Initialises a CoMutex. This must be called before any other operation is used + * on the CoMutex. + */ +void qemu_co_mutex_init(CoMutex *mutex); + +/** + * Locks the mutex. If the lock cannot be taken immediately, control is + * transferred to the caller of the current coroutine. + */ +void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex); + +/** + * Unlocks the mutex and schedules the next coroutine that was waiting for this + * lock to be run. + */ +void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex); + +typedef struct CoRwlock { + bool writer; + int reader; + CoQueue queue; +} CoRwlock; + +/** + * Initialises a CoRwlock. This must be called before any other operation + * is used on the CoRwlock + */ +void qemu_co_rwlock_init(CoRwlock *lock); + +/** + * Read locks the CoRwlock. If the lock cannot be taken immediately because + * of a parallel writer, control is transferred to the caller of the current + * coroutine. + */ +void qemu_co_rwlock_rdlock(CoRwlock *lock); + +/** + * Write Locks the mutex. If the lock cannot be taken immediately because + * of a parallel reader, control is transferred to the caller of the current + * coroutine. + */ +void qemu_co_rwlock_wrlock(CoRwlock *lock); + +/** + * Unlocks the read/write lock and schedules the next coroutine that was + * waiting for this lock to be run. + */ +void qemu_co_rwlock_unlock(CoRwlock *lock); + +/** + * Yield the coroutine for a given duration + * + * Behaves similarly to co_sleep_ns(), but the sleeping coroutine will be + * resumed when using aio_poll(). + */ +void coroutine_fn co_aio_sleep_ns(AioContext *ctx, QEMUClockType type, + int64_t ns); + +/** + * Yield until a file descriptor becomes readable + * + * Note that this function clobbers the handlers for the file descriptor. + */ +void coroutine_fn yield_until_fd_readable(int fd); + +#endif /* QEMU_COROUTINE_H */ diff --git a/qemu/include/qemu/coroutine_int.h b/qemu/include/qemu/coroutine_int.h new file mode 100644 index 000000000..42d683840 --- /dev/null +++ b/qemu/include/qemu/coroutine_int.h @@ -0,0 +1,54 @@ +/* + * Coroutine internals + * + * Copyright (c) 2011 Kevin Wolf + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QEMU_COROUTINE_INT_H +#define QEMU_COROUTINE_INT_H + +#include "qemu/queue.h" +#include "qemu/coroutine.h" + +typedef enum { + COROUTINE_YIELD = 1, + COROUTINE_TERMINATE = 2, + COROUTINE_ENTER = 3, +} CoroutineAction; + +struct Coroutine { + CoroutineEntry *entry; + void *entry_arg; + Coroutine *caller; + QSLIST_ENTRY(Coroutine) pool_next; + + /* Coroutines that should be woken up when we yield or terminate */ + QTAILQ_HEAD(, Coroutine) co_queue_wakeup; + QTAILQ_ENTRY(Coroutine) co_queue_next; +}; + +Coroutine *qemu_coroutine_new(void); +void qemu_coroutine_delete(Coroutine *co); +CoroutineAction qemu_coroutine_switch(Coroutine *from, Coroutine *to, + CoroutineAction action); +void coroutine_fn qemu_co_queue_run_restart(Coroutine *co); + +#endif diff --git a/qemu/include/qemu/cutils.h b/qemu/include/qemu/cutils.h new file mode 100644 index 000000000..db7adadcf --- /dev/null +++ b/qemu/include/qemu/cutils.h @@ -0,0 +1,183 @@ +#ifndef QEMU_CUTILS_H +#define QEMU_CUTILS_H 1 + +#include "qemu/fprintf-fn.h" + +/** + * pstrcpy: + * @buf: buffer to copy string into + * @buf_size: size of @buf in bytes + * @str: string to copy + * + * Copy @str into @buf, including the trailing NUL, but do not + * write more than @buf_size bytes. The resulting buffer is + * always NUL terminated (even if the source string was too long). + * If @buf_size is zero or negative then no bytes are copied. + * + * This function is similar to strncpy(), but avoids two of that + * function's problems: + * * if @str fits in the buffer, pstrcpy() does not zero-fill the + * remaining space at the end of @buf + * * if @str is too long, pstrcpy() will copy the first @buf_size-1 + * bytes and then add a NUL + */ +void pstrcpy(char *buf, int buf_size, const char *str); +/** + * strpadcpy: + * @buf: buffer to copy string into + * @buf_size: size of @buf in bytes + * @str: string to copy + * @pad: character to pad the remainder of @buf with + * + * Copy @str into @buf (but *not* its trailing NUL!), and then pad the + * rest of the buffer with the @pad character. If @str is too large + * for the buffer then it is truncated, so that @buf contains the + * first @buf_size characters of @str, with no terminator. + */ +void strpadcpy(char *buf, int buf_size, const char *str, char pad); +/** + * pstrcat: + * @buf: buffer containing existing string + * @buf_size: size of @buf in bytes + * @s: string to concatenate to @buf + * + * Append a copy of @s to the string already in @buf, but do not + * allow the buffer to overflow. If the existing contents of @buf + * plus @str would total more than @buf_size bytes, then write + * as much of @str as will fit followed by a NUL terminator. + * + * @buf must already contain a NUL-terminated string, or the + * behaviour is undefined. + * + * Returns: @buf. + */ +char *pstrcat(char *buf, int buf_size, const char *s); +/** + * strstart: + * @str: string to test + * @val: prefix string to look for + * @ptr: NULL, or pointer to be written to indicate start of + * the remainder of the string + * + * Test whether @str starts with the prefix @val. + * If it does (including the degenerate case where @str and @val + * are equal) then return true. If @ptr is not NULL then a + * pointer to the first character following the prefix is written + * to it. If @val is not a prefix of @str then return false (and + * @ptr is not written to). + * + * Returns: true if @str starts with prefix @val, false otherwise. + */ +int strstart(const char *str, const char *val, const char **ptr); +/** + * stristart: + * @str: string to test + * @val: prefix string to look for + * @ptr: NULL, or pointer to be written to indicate start of + * the remainder of the string + * + * Test whether @str starts with the case-insensitive prefix @val. + * This function behaves identically to strstart(), except that the + * comparison is made after calling qemu_toupper() on each pair of + * characters. + * + * Returns: true if @str starts with case-insensitive prefix @val, + * false otherwise. + */ +int stristart(const char *str, const char *val, const char **ptr); +/** + * qemu_strnlen: + * @s: string + * @max_len: maximum number of bytes in @s to scan + * + * Return the length of the string @s, like strlen(), but do not + * examine more than @max_len bytes of the memory pointed to by @s. + * If no NUL terminator is found within @max_len bytes, then return + * @max_len instead. + * + * This function has the same behaviour as the POSIX strnlen() + * function. + * + * Returns: length of @s in bytes, or @max_len, whichever is smaller. + */ +int qemu_strnlen(const char *s, int max_len); +/** + * qemu_strsep: + * @input: pointer to string to parse + * @delim: string containing delimiter characters to search for + * + * Locate the first occurrence of any character in @delim within + * the string referenced by @input, and replace it with a NUL. + * The location of the next character after the delimiter character + * is stored into @input. + * If the end of the string was reached without finding a delimiter + * character, then NULL is stored into @input. + * If @input points to a NULL pointer on entry, return NULL. + * The return value is always the original value of *@input (and + * so now points to a NUL-terminated string corresponding to the + * part of the input up to the first delimiter). + * + * This function has the same behaviour as the BSD strsep() function. + * + * Returns: the pointer originally in @input. + */ +char *qemu_strsep(char **input, const char *delim); +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_strtol(const char *nptr, const char **endptr, int base, + long *result); +int qemu_strtoul(const char *nptr, const char **endptr, int base, + unsigned long *result); +int qemu_strtoll(const char *nptr, const char **endptr, int base, + int64_t *result); +int qemu_strtoull(const char *nptr, const char **endptr, int base, + uint64_t *result); + +int parse_uint(const char *s, unsigned long long *value, char **endptr, + int base); +int parse_uint_full(const char *s, unsigned long long *value, int base); + +/* + * qemu_strtosz() suffixes used to specify the default treatment of an + * argument passed to qemu_strtosz() without an explicit suffix. + * These should be defined using upper case characters in the range + * A-Z, as qemu_strtosz() will use qemu_toupper() on the given argument + * prior to comparison. + */ +#define QEMU_STRTOSZ_DEFSUFFIX_EB 'E' +#define QEMU_STRTOSZ_DEFSUFFIX_PB 'P' +#define QEMU_STRTOSZ_DEFSUFFIX_TB 'T' +#define QEMU_STRTOSZ_DEFSUFFIX_GB 'G' +#define QEMU_STRTOSZ_DEFSUFFIX_MB 'M' +#define QEMU_STRTOSZ_DEFSUFFIX_KB 'K' +#define QEMU_STRTOSZ_DEFSUFFIX_B 'B' +int64_t qemu_strtosz(const char *nptr, char **end); +int64_t qemu_strtosz_suffix(const char *nptr, char **end, + const char default_suffix); +int64_t qemu_strtosz_suffix_unit(const char *nptr, char **end, + const char default_suffix, int64_t unit); +#define K_BYTE (1ULL << 10) +#define M_BYTE (1ULL << 20) +#define G_BYTE (1ULL << 30) +#define T_BYTE (1ULL << 40) +#define P_BYTE (1ULL << 50) +#define E_BYTE (1ULL << 60) + +/* used to print char* safely */ +#define STR_OR_NULL(str) ((str) ? (str) : "null") + +bool can_use_buffer_find_nonzero_offset(const void *buf, size_t len); +size_t buffer_find_nonzero_offset(const void *buf, size_t len); +bool buffer_is_zero(const void *buf, size_t len); + +/* + * Implementation of ULEB128 (http://en.wikipedia.org/wiki/LEB128) + * Input is limited to 14-bit numbers + */ + +int uleb128_encode_small(uint8_t *out, uint32_t n); +int uleb128_decode_small(const uint8_t *in, uint32_t *n); + +#endif diff --git a/qemu/include/qemu/error-report.h b/qemu/include/qemu/error-report.h index 7ab235590..7a2a363fb 100644 --- a/qemu/include/qemu/error-report.h +++ b/qemu/include/qemu/error-report.h @@ -13,9 +13,6 @@ #ifndef QEMU_ERROR_H #define QEMU_ERROR_H -#include -#include -#include "qemu/compiler.h" typedef struct Location { /* all members are private to qemu-error.c */ diff --git a/qemu/include/qemu/event_notifier.h b/qemu/include/qemu/event_notifier.h index 88b57af7c..e326990db 100644 --- a/qemu/include/qemu/event_notifier.h +++ b/qemu/include/qemu/event_notifier.h @@ -34,11 +34,13 @@ int event_notifier_init(EventNotifier *, int active); void event_notifier_cleanup(EventNotifier *); int event_notifier_set(EventNotifier *); int event_notifier_test_and_clear(EventNotifier *); -int event_notifier_set_handler(EventNotifier *, EventNotifierHandler *); +int event_notifier_set_handler(EventNotifier *, + bool is_external, + EventNotifierHandler *); #ifdef CONFIG_POSIX void event_notifier_init_fd(EventNotifier *, int fd); -int event_notifier_get_fd(EventNotifier *); +int event_notifier_get_fd(const EventNotifier *); #else HANDLE event_notifier_get_handle(EventNotifier *); #endif diff --git a/qemu/include/qemu/fprintf-fn.h b/qemu/include/qemu/fprintf-fn.h index 9ddc90f1c..b6bad35b1 100644 --- a/qemu/include/qemu/fprintf-fn.h +++ b/qemu/include/qemu/fprintf-fn.h @@ -8,8 +8,6 @@ #ifndef QEMU_FPRINTF_FN_H #define QEMU_FPRINTF_FN_H 1 -#include "qemu/compiler.h" -#include typedef int (*fprintf_function)(FILE *f, const char *fmt, ...) GCC_FMT_ATTR(2, 3); diff --git a/qemu/include/qemu/hbitmap.h b/qemu/include/qemu/hbitmap.h index bb94a00c5..e29188c0a 100644 --- a/qemu/include/qemu/hbitmap.h +++ b/qemu/include/qemu/hbitmap.h @@ -12,9 +12,6 @@ #ifndef HBITMAP_H #define HBITMAP_H 1 -#include -#include -#include #include "bitops.h" #include "host-utils.h" diff --git a/qemu/include/qemu/help_option.h b/qemu/include/qemu/help_option.h new file mode 100644 index 000000000..e39a66e77 --- /dev/null +++ b/qemu/include/qemu/help_option.h @@ -0,0 +1,22 @@ +#ifndef QEMU_HELP_OPTION_H +#define QEMU_HELP_OPTION_H 1 + +/** + * is_help_option: + * @s: string to test + * + * Check whether @s is one of the standard strings which indicate + * that the user is asking for a list of the valid values for a + * command option like -cpu or -M. The current accepted strings + * are 'help' and '?'. '?' is deprecated (it is a shell wildcard + * which makes it annoying to use in a reliable way) but provided + * for backwards compatibility. + * + * Returns: true if @s is a request for a list. + */ +static inline bool is_help_option(const char *s) +{ + return !strcmp(s, "?") || !strcmp(s, "help"); +} + +#endif diff --git a/qemu/include/qemu/host-utils.h b/qemu/include/qemu/host-utils.h index d4f21c947..1cdae0d0e 100644 --- a/qemu/include/qemu/host-utils.h +++ b/qemu/include/qemu/host-utils.h @@ -25,8 +25,7 @@ #ifndef HOST_UTILS_H #define HOST_UTILS_H 1 -#include "qemu/compiler.h" /* QEMU_GNUC_PREREQ */ -#include +#include "qemu/bswap.h" #ifdef CONFIG_INT128 static inline void mulu64(uint64_t *plow, uint64_t *phigh, @@ -45,6 +44,12 @@ static inline void muls64(uint64_t *plow, uint64_t *phigh, *phigh = r >> 64; } +/* compute with 96 bit intermediate result: (a*b)/c */ +static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) +{ + return (__int128_t)a * b / c; +} + static inline int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor) { if (divisor == 0) { @@ -75,6 +80,29 @@ void muls64(uint64_t *phigh, uint64_t *plow, int64_t a, int64_t b); void mulu64(uint64_t *phigh, uint64_t *plow, uint64_t a, uint64_t b); int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor); int divs128(int64_t *plow, int64_t *phigh, int64_t divisor); + +static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) +{ + union { + uint64_t ll; + struct { +#ifdef HOST_WORDS_BIGENDIAN + uint32_t high, low; +#else + uint32_t low, high; +#endif + } l; + } u, res; + uint64_t rl, rh; + + u.ll = a; + rl = (uint64_t)u.l.low * (uint64_t)b; + rh = (uint64_t)u.l.high * (uint64_t)b; + rh += (rl >> 32); + res.l.high = rh / c; + res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c; + return res.ll; +} #endif /** @@ -361,6 +389,80 @@ static inline int ctpop64(uint64_t val) #endif } +/** + * revbit8 - reverse the bits in an 8-bit value. + * @x: The value to modify. + */ +static inline uint8_t revbit8(uint8_t x) +{ + /* Assign the correct nibble position. */ + x = ((x & 0xf0) >> 4) + | ((x & 0x0f) << 4); + /* Assign the correct bit position. */ + x = ((x & 0x88) >> 3) + | ((x & 0x44) >> 1) + | ((x & 0x22) << 1) + | ((x & 0x11) << 3); + return x; +} + +/** + * revbit16 - reverse the bits in a 16-bit value. + * @x: The value to modify. + */ +static inline uint16_t revbit16(uint16_t x) +{ + /* Assign the correct byte position. */ + x = bswap16(x); + /* Assign the correct nibble position. */ + x = ((x & 0xf0f0) >> 4) + | ((x & 0x0f0f) << 4); + /* Assign the correct bit position. */ + x = ((x & 0x8888) >> 3) + | ((x & 0x4444) >> 1) + | ((x & 0x2222) << 1) + | ((x & 0x1111) << 3); + return x; +} + +/** + * revbit32 - reverse the bits in a 32-bit value. + * @x: The value to modify. + */ +static inline uint32_t revbit32(uint32_t x) +{ + /* Assign the correct byte position. */ + x = bswap32(x); + /* Assign the correct nibble position. */ + x = ((x & 0xf0f0f0f0u) >> 4) + | ((x & 0x0f0f0f0fu) << 4); + /* Assign the correct bit position. */ + x = ((x & 0x88888888u) >> 3) + | ((x & 0x44444444u) >> 1) + | ((x & 0x22222222u) << 1) + | ((x & 0x11111111u) << 3); + return x; +} + +/** + * revbit64 - reverse the bits in a 64-bit value. + * @x: The value to modify. + */ +static inline uint64_t revbit64(uint64_t x) +{ + /* Assign the correct byte position. */ + x = bswap64(x); + /* Assign the correct nibble position. */ + x = ((x & 0xf0f0f0f0f0f0f0f0ull) >> 4) + | ((x & 0x0f0f0f0f0f0f0f0full) << 4); + /* Assign the correct bit position. */ + x = ((x & 0x8888888888888888ull) >> 3) + | ((x & 0x4444444444444444ull) >> 1) + | ((x & 0x2222222222222222ull) << 1) + | ((x & 0x1111111111111111ull) << 3); + return x; +} + /* Host type specific sizes of these routines. */ #if ULONG_MAX == UINT32_MAX @@ -369,14 +471,48 @@ static inline int ctpop64(uint64_t val) # define clol clo32 # define ctol cto32 # define ctpopl ctpop32 +# define revbitl revbit32 #elif ULONG_MAX == UINT64_MAX # define clzl clz64 # define ctzl ctz64 # define clol clo64 # define ctol cto64 # define ctpopl ctpop64 +# define revbitl revbit64 #else # error Unknown sizeof long #endif +static inline bool is_power_of_2(uint64_t value) +{ + if (!value) { + return 0; + } + + return !(value & (value - 1)); +} + +/* round down to the nearest power of 2*/ +static inline int64_t pow2floor(int64_t value) +{ + if (!is_power_of_2(value)) { + value = 0x8000000000000000ULL >> clz64(value); + } + return value; +} + +/* round up to the nearest power of 2 (0 if overflow) */ +static inline uint64_t pow2ceil(uint64_t value) +{ + uint8_t nlz = clz64(value); + + if (is_power_of_2(value)) { + return value; + } + if (!nlz) { + return 0; + } + return 1ULL << (64 - nlz); +} + #endif diff --git a/qemu/include/qemu/id.h b/qemu/include/qemu/id.h new file mode 100644 index 000000000..7d90335af --- /dev/null +++ b/qemu/include/qemu/id.h @@ -0,0 +1,13 @@ +#ifndef QEMU_ID_H +#define QEMU_ID_H 1 + +typedef enum IdSubSystems { + ID_QDEV, + ID_BLOCK, + ID_MAX /* last element, used as array size */ +} IdSubSystems; + +char *id_generate(IdSubSystems id); +bool id_wellformed(const char *id); + +#endif diff --git a/qemu/include/qemu/int128.h b/qemu/include/qemu/int128.h index fb782aadd..c5988813d 100644 --- a/qemu/include/qemu/int128.h +++ b/qemu/include/qemu/int128.h @@ -1,9 +1,6 @@ #ifndef INT128_H #define INT128_H -#include -#include -#include typedef struct Int128 Int128; diff --git a/qemu/include/qemu/iov.h b/qemu/include/qemu/iov.h index 569b2c2a2..bd9fd55b0 100644 --- a/qemu/include/qemu/iov.h +++ b/qemu/include/qemu/iov.h @@ -14,8 +14,6 @@ #ifndef IOV_H #define IOV_H -#include "qemu-common.h" - /** * count and return data size, in bytes, of an iovec * starting at `iov' of `iov_cnt' number of elements. @@ -39,10 +37,36 @@ size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt); * such "large" value is -1 (sinice size_t is unsigned), * so specifying `-1' as `bytes' means 'up to the end of iovec'. */ -size_t iov_from_buf(const struct iovec *iov, unsigned int iov_cnt, - size_t offset, const void *buf, size_t bytes); -size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, - size_t offset, void *buf, size_t bytes); +size_t iov_from_buf_full(const struct iovec *iov, unsigned int iov_cnt, + size_t offset, const void *buf, size_t bytes); +size_t iov_to_buf_full(const struct iovec *iov, const unsigned int iov_cnt, + size_t offset, void *buf, size_t bytes); + +static inline size_t +iov_from_buf(const struct iovec *iov, unsigned int iov_cnt, + size_t offset, const void *buf, size_t bytes) +{ + if (__builtin_constant_p(bytes) && iov_cnt && + offset <= iov[0].iov_len && bytes <= iov[0].iov_len - offset) { + memcpy(iov[0].iov_base + offset, buf, bytes); + return bytes; + } else { + return iov_from_buf_full(iov, iov_cnt, offset, buf, bytes); + } +} + +static inline size_t +iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, + size_t offset, void *buf, size_t bytes) +{ + if (__builtin_constant_p(bytes) && iov_cnt && + offset <= iov[0].iov_len && bytes <= iov[0].iov_len - offset) { + memcpy(buf, iov[0].iov_base + offset, bytes); + return bytes; + } else { + return iov_to_buf_full(iov, iov_cnt, offset, buf, bytes); + } +} /** * Set data bytes pointed out by iovec `iov' of size `iov_cnt' elements, @@ -112,4 +136,32 @@ size_t iov_discard_front(struct iovec **iov, unsigned int *iov_cnt, size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt, size_t bytes); +typedef struct QEMUIOVector { + struct iovec *iov; + int niov; + int nalloc; + size_t size; +} QEMUIOVector; + +void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint); +void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov); +void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len); +void qemu_iovec_concat(QEMUIOVector *dst, + QEMUIOVector *src, size_t soffset, size_t sbytes); +size_t qemu_iovec_concat_iov(QEMUIOVector *dst, + struct iovec *src_iov, unsigned int src_cnt, + size_t soffset, size_t sbytes); +bool qemu_iovec_is_zero(QEMUIOVector *qiov); +void qemu_iovec_destroy(QEMUIOVector *qiov); +void qemu_iovec_reset(QEMUIOVector *qiov); +size_t qemu_iovec_to_buf(QEMUIOVector *qiov, size_t offset, + void *buf, size_t bytes); +size_t qemu_iovec_from_buf(QEMUIOVector *qiov, size_t offset, + const void *buf, size_t bytes); +size_t qemu_iovec_memset(QEMUIOVector *qiov, size_t offset, + int fillc, size_t bytes); +ssize_t qemu_iovec_compare(QEMUIOVector *a, QEMUIOVector *b); +void qemu_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, void *buf); +void qemu_iovec_discard_back(QEMUIOVector *qiov, size_t bytes); + #endif diff --git a/qemu/include/qemu/log.h b/qemu/include/qemu/log.h index f880e66db..c52f136ac 100644 --- a/qemu/include/qemu/log.h +++ b/qemu/include/qemu/log.h @@ -1,14 +1,6 @@ #ifndef QEMU_LOG_H #define QEMU_LOG_H -#include -#include -#include -#include "qemu/compiler.h" -#include "qom/cpu.h" -#ifdef NEED_CPU_H -#include "disas/disas.h" -#endif /* Private global variables, don't use */ extern FILE *qemu_logfile; @@ -28,6 +20,13 @@ static inline bool qemu_log_enabled(void) return qemu_logfile != NULL; } +/* Returns true if qemu_log() will write somewhere else than stderr + */ +static inline bool qemu_log_separate(void) +{ + return qemu_logfile != NULL && qemu_logfile != stderr; +} + #define CPU_LOG_TB_OUT_ASM (1 << 0) #define CPU_LOG_TB_IN_ASM (1 << 1) #define CPU_LOG_TB_OP (1 << 2) @@ -35,12 +34,14 @@ static inline bool qemu_log_enabled(void) #define CPU_LOG_INT (1 << 4) #define CPU_LOG_EXEC (1 << 5) #define CPU_LOG_PCALL (1 << 6) -#define CPU_LOG_IOPORT (1 << 7) #define CPU_LOG_TB_CPU (1 << 8) #define CPU_LOG_RESET (1 << 9) #define LOG_UNIMP (1 << 10) #define LOG_GUEST_ERROR (1 << 11) #define CPU_LOG_MMU (1 << 12) +#define CPU_LOG_TB_NOCHAIN (1 << 13) +#define CPU_LOG_PAGE (1 << 14) +#define LOG_TRACE (1 << 15) /* Returns true if a bit is set in the current loglevel mask */ @@ -65,91 +66,35 @@ qemu_log_vprintf(const char *fmt, va_list va) } } -/* log only if a bit is set on the current loglevel mask - */ -void GCC_FMT_ATTR(2, 3) qemu_log_mask(int mask, const char *fmt, ...); - - -/* Special cases: */ - -/* cpu_dump_state() logging functions: */ -/** - * log_cpu_state: - * @cpu: The CPU whose state is to be logged. - * @flags: Flags what to log. - * - * Logs the output of cpu_dump_state(). +/* log only if a bit is set on the current loglevel mask: + * @mask: bit to check in the mask + * @fmt: printf-style format string + * @args: optional arguments for format string */ -static inline void log_cpu_state(CPUState *cpu, int flags) -{ - if (qemu_log_enabled()) { - cpu_dump_state(cpu, qemu_logfile, fprintf, flags); - } -} +#define qemu_log_mask(MASK, FMT, ...) \ + do { \ + if (unlikely(qemu_loglevel_mask(MASK))) { \ + qemu_log(FMT, ## __VA_ARGS__); \ + } \ + } while (0) -/** - * log_cpu_state_mask: - * @mask: Mask when to log. - * @cpu: The CPU whose state is to be logged. - * @flags: Flags what to log. - * - * Logs the output of cpu_dump_state() if loglevel includes @mask. +/* log only if a bit is set on the current loglevel mask + * and we are in the address range we care about: + * @mask: bit to check in the mask + * @addr: address to check in dfilter + * @fmt: printf-style format string + * @args: optional arguments for format string */ -static inline void log_cpu_state_mask(int mask, CPUState *cpu, int flags) -{ - if (qemu_loglevel & mask) { - log_cpu_state(cpu, flags); - } -} - -#ifdef NEED_CPU_H -/* disas() and target_disas() to qemu_logfile: */ -static inline void log_target_disas(CPUState *cpu, target_ulong start, - target_ulong len, int flags) -{ - target_disas(qemu_logfile, cpu, start, len, flags); -} - -static inline void log_disas(void *code, unsigned long size) -{ - disas(qemu_logfile, code, size); -} - -#if defined(CONFIG_USER_ONLY) -/* page_dump() output to the log file: */ -static inline void log_page_dump(void) -{ - page_dump(qemu_logfile); -} -#endif -#endif - +#define qemu_log_mask_and_addr(MASK, ADDR, FMT, ...) \ + do { \ + if (unlikely(qemu_loglevel_mask(MASK)) && \ + qemu_log_in_addr_range(ADDR)) { \ + qemu_log(FMT, ## __VA_ARGS__); \ + } \ + } while (0) /* Maintenance: */ -/* fflush() the log file */ -static inline void qemu_log_flush(void) -{ - fflush(qemu_logfile); -} - -/* Close the log file */ -static inline void qemu_log_close(void) -{ - if (qemu_logfile) { - if (qemu_logfile != stderr) { - fclose(qemu_logfile); - } - qemu_logfile = NULL; - } -} - -/* Set up a new log file */ -static inline void qemu_log_set_file(FILE *f) -{ - qemu_logfile = f; -} - /* define log items */ typedef struct QEMULogItem { int mask; @@ -175,6 +120,8 @@ static inline void qemu_set_log(int log_flags) } void qemu_set_log_filename(const char *filename); +void qemu_set_dfilter_ranges(const char *ranges); +bool qemu_log_in_addr_range(uint64_t addr); int qemu_str_to_log_mask(const char *str); /* Print a usage message listing all the valid logging categories @@ -182,4 +129,9 @@ int qemu_str_to_log_mask(const char *str); */ void qemu_print_log_usage(FILE *f); +/* fflush() the log file */ +void qemu_log_flush(void); +/* Close the log file */ +void qemu_log_close(void); + #endif diff --git a/qemu/include/qemu/main-loop.h b/qemu/include/qemu/main-loop.h index bc18ca30e..19b5de3dd 100644 --- a/qemu/include/qemu/main-loop.h +++ b/qemu/include/qemu/main-loop.h @@ -203,6 +203,8 @@ void qemu_set_fd_handler(int fd, IOHandler *fd_write, void *opaque); +GSource *iohandler_get_g_source(void); +AioContext *iohandler_get_aio_context(void); #ifdef CONFIG_POSIX /** * qemu_add_child_watch: Register a child process for reaping. @@ -265,8 +267,6 @@ void qemu_mutex_unlock_iothread(void); /* internal interfaces */ void qemu_fd_register(int fd); -void qemu_iohandler_fill(GArray *pollfds); -void qemu_iohandler_poll(GArray *pollfds, int rc); QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque); void qemu_bh_schedule_idle(QEMUBH *bh); diff --git a/qemu/include/qemu/memfd.h b/qemu/include/qemu/memfd.h new file mode 100644 index 000000000..745a8c501 --- /dev/null +++ b/qemu/include/qemu/memfd.h @@ -0,0 +1,24 @@ +#ifndef QEMU_MEMFD_H +#define QEMU_MEMFD_H + + +#ifndef F_LINUX_SPECIFIC_BASE +#define F_LINUX_SPECIFIC_BASE 1024 +#endif + +#ifndef F_ADD_SEALS +#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) +#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) + +#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */ +#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */ +#define F_SEAL_GROW 0x0004 /* prevent file from growing */ +#define F_SEAL_WRITE 0x0008 /* prevent writes */ +#endif + +void *qemu_memfd_alloc(const char *name, size_t size, unsigned int seals, + int *fd); +void qemu_memfd_free(void *ptr, size_t size, int fd); +bool qemu_memfd_check(void); + +#endif /* QEMU_MEMFD_H */ diff --git a/qemu/include/qemu/mmap-alloc.h b/qemu/include/qemu/mmap-alloc.h new file mode 100644 index 000000000..0899b2f01 --- /dev/null +++ b/qemu/include/qemu/mmap-alloc.h @@ -0,0 +1,12 @@ +#ifndef QEMU_MMAP_ALLOC +#define QEMU_MMAP_ALLOC + +#include "qemu-common.h" + +size_t qemu_fd_getpagesize(int fd); + +void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared); + +void qemu_ram_munmap(void *ptr, size_t size); + +#endif diff --git a/qemu/include/qemu/module.h b/qemu/include/qemu/module.h index 72d94984a..237070844 100644 --- a/qemu/include/qemu/module.h +++ b/qemu/include/qemu/module.h @@ -14,7 +14,6 @@ #ifndef QEMU_MODULE_H #define QEMU_MODULE_H -#include "qemu/osdep.h" #define DSO_STAMP_FUN glue(qemu_stamp, CONFIG_STAMP) #define DSO_STAMP_FUN_STR stringify(DSO_STAMP_FUN) @@ -42,14 +41,14 @@ static void __attribute__((constructor)) do_qemu_init_ ## function(void) \ typedef enum { MODULE_INIT_BLOCK, - MODULE_INIT_MACHINE, + MODULE_INIT_OPTS, MODULE_INIT_QAPI, MODULE_INIT_QOM, MODULE_INIT_MAX } module_init_type; #define block_init(function) module_init(function, MODULE_INIT_BLOCK) -#define machine_init(function) module_init(function, MODULE_INIT_MACHINE) +#define opts_init(function) module_init(function, MODULE_INIT_OPTS) #define qapi_init(function) module_init(function, MODULE_INIT_QAPI) #define type_init(function) module_init(function, MODULE_INIT_QOM) diff --git a/qemu/include/qemu/option.h b/qemu/include/qemu/option.h index 57e51c962..8542d2dfd 100644 --- a/qemu/include/qemu/option.h +++ b/qemu/include/qemu/option.h @@ -26,9 +26,7 @@ #ifndef QEMU_OPTIONS_H #define QEMU_OPTIONS_H -#include #include "qemu/queue.h" -#include "qapi/error.h" #include "qapi/qmp/qdict.h" const char *get_opt_name(char *buf, int buf_size, const char *p, char delim); @@ -44,10 +42,6 @@ void parse_option_size(const char *name, const char *value, bool has_help_option(const char *param); bool is_valid_option_list(const char *param); -typedef struct QemuOpt QemuOpt; -typedef struct QemuOpts QemuOpts; -typedef struct QemuOptsList QemuOptsList; - enum QemuOptType { QEMU_OPT_STRING = 0, /* no parsing (use string as-is) */ QEMU_OPT_BOOL, /* on/off */ diff --git a/qemu/include/qemu/osdep.h b/qemu/include/qemu/osdep.h index 324736426..408783f53 100644 --- a/qemu/include/qemu/osdep.h +++ b/qemu/include/qemu/osdep.h @@ -1,12 +1,88 @@ +/* + * OS includes and handling of OS dependencies + * + * This header exists to pull in some common system headers that + * most code in QEMU will want, and to fix up some possible issues with + * it (missing defines, Windows weirdness, and so on). + * + * To avoid getting into possible circular include dependencies, this + * file should not include any other QEMU headers, with the exceptions + * of config-host.h, config-target.h, qemu/compiler.h, + * sysemu/os-posix.h, sysemu/os-win32.h, glib-compat.h and + * qemu/typedefs.h, all of which are doing a similar job to this file + * and are under similar constraints. + * + * This header also contains prototypes for functions defined in + * os-*.c and util/oslib-*.c; those would probably be better split + * out into separate header files. + * + * In an ideal world this header would contain only: + * (1) things which everybody needs + * (2) things without which code would work on most platforms but + * fail to compile or misbehave on a minority of host OSes + * + * 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_OSDEP_H #define QEMU_OSDEP_H #include "config-host.h" +#ifdef NEED_CPU_H +#include "config-target.h" +#endif +#include "qemu/compiler.h" + +/* Older versions of C++ don't get definitions of various macros from + * stdlib.h unless we define these macros before first inclusion of + * that system header. + */ +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif + +/* The following block of code temporarily renames the daemon() function so the + * compiler does not see the warning associated with it in stdlib.h on OSX + */ +#ifdef __APPLE__ +#define daemon qemu_fake_daemon_function +#include +#undef daemon +extern int daemon(int, int); +#endif + #include #include #include #include #include +#include +#include +#include +#include +#include +#include +/* Put unistd.h before time.h as that triggers localtime_r/gmtime_r + * function availability on recentish Mingw-w64 platforms. */ +#include +#include +#include +#include +#include +#include +#include +#include +/* setjmp must be declared before sysemu/os-win32.h + * because it is redefined there. */ +#include +#include + #ifdef __OpenBSD__ #include #endif @@ -18,50 +94,51 @@ #define WEXITSTATUS(x) (x) #endif -#include - -#if defined(CONFIG_SOLARIS) && CONFIG_SOLARIS_VERSION < 10 -/* [u]int_fast*_t not in */ -typedef unsigned char uint_fast8_t; -typedef unsigned int uint_fast16_t; -typedef signed int int_fast16_t; +#ifdef _WIN32 +#include "sysemu/os-win32.h" #endif -#ifndef glue -#define xglue(x, y) x ## y -#define glue(x, y) xglue(x, y) -#define stringify(s) tostring(s) -#define tostring(s) #s +#ifdef CONFIG_POSIX +#include "sysemu/os-posix.h" #endif -#ifndef likely -#if __GNUC__ < 3 -#define __builtin_expect(x, n) (x) -#endif +#include "glib-compat.h" +#include "qemu/typedefs.h" -#define likely(x) __builtin_expect(!!(x), 1) -#define unlikely(x) __builtin_expect(!!(x), 0) +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 #endif - -#ifndef container_of -#define container_of(ptr, type, member) ({ \ - const typeof(((type *) 0)->member) *__mptr = (ptr); \ - (type *) ((char *) __mptr - offsetof(type, member));}) +#ifndef O_BINARY +#define O_BINARY 0 +#endif +#ifndef MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif +#ifndef ENOMEDIUM +#define ENOMEDIUM ENODEV +#endif +#if !defined(ENOTSUP) +#define ENOTSUP 4096 +#endif +#if !defined(ECANCELED) +#define ECANCELED 4097 +#endif +#if !defined(EMEDIUMTYPE) +#define EMEDIUMTYPE 4098 +#endif +#ifndef TIME_MAX +#define TIME_MAX LONG_MAX #endif -/* Convert from a base type to a parent type, with compile time checking. */ -#ifdef __GNUC__ -#define DO_UPCAST(type, field, dev) ( __extension__ ( { \ - char __attribute__((unused)) offset_must_be_zero[ \ - -offsetof(type, field)]; \ - container_of(dev, type, field);})) +/* HOST_LONG_BITS is the size of a native pointer in bits. */ +#if UINTPTR_MAX == UINT32_MAX +# define HOST_LONG_BITS 32 +#elif UINTPTR_MAX == UINT64_MAX +# define HOST_LONG_BITS 64 #else -#define DO_UPCAST(type, field, dev) container_of(dev, type, field) +# error Unknown pointer size #endif -#define typeof_field(type, field) typeof(((type *)0)->field) -#define type_check(t1,t2) ((t1*)0 - (t2*)0) - #ifndef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif @@ -75,6 +152,12 @@ typedef signed int int_fast16_t; #define MIN_NON_ZERO(a, b) (((a) != 0 && (a) < (b)) ? (a) : (b)) #endif +/* Round number down to multiple */ +#define QEMU_ALIGN_DOWN(n, m) ((n) / (m) * (m)) + +/* Round number up to multiple */ +#define QEMU_ALIGN_UP(n, m) QEMU_ALIGN_DOWN((n) + (m) - 1, (m)) + #ifndef ROUND_UP #define ROUND_UP(n,d) (((n) + (d) - 1) & -(d)) #endif @@ -87,20 +170,6 @@ typedef signed int int_fast16_t; #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #endif -#ifndef always_inline -#if !((__GNUC__ < 3) || defined(__APPLE__)) -#ifdef __OPTIMIZE__ -#undef inline -#define inline __attribute__ (( always_inline )) __inline__ -#endif -#endif -#else -#undef inline -#define inline always_inline -#endif - -#define qemu_printf printf - int qemu_daemon(int nochdir, int noclose); void *qemu_try_memalign(size_t alignment, size_t size); void *qemu_memalign(size_t alignment, size_t size); @@ -112,6 +181,8 @@ void qemu_anon_ram_free(void *ptr, size_t size); #if defined(CONFIG_MADVISE) +#include + #define QEMU_MADV_WILLNEED MADV_WILLNEED #define QEMU_MADV_DONTNEED MADV_DONTNEED #ifdef MADV_DONTFORK @@ -144,6 +215,11 @@ void qemu_anon_ram_free(void *ptr, size_t size); #else #define QEMU_MADV_HUGEPAGE QEMU_MADV_INVALID #endif +#ifdef MADV_NOHUGEPAGE +#define QEMU_MADV_NOHUGEPAGE MADV_NOHUGEPAGE +#else +#define QEMU_MADV_NOHUGEPAGE QEMU_MADV_INVALID +#endif #elif defined(CONFIG_POSIX_MADVISE) @@ -155,6 +231,7 @@ void qemu_anon_ram_free(void *ptr, size_t size); #define QEMU_MADV_DODUMP QEMU_MADV_INVALID #define QEMU_MADV_DONTDUMP QEMU_MADV_INVALID #define QEMU_MADV_HUGEPAGE QEMU_MADV_INVALID +#define QEMU_MADV_NOHUGEPAGE QEMU_MADV_INVALID #else /* no-op */ @@ -166,6 +243,7 @@ void qemu_anon_ram_free(void *ptr, size_t size); #define QEMU_MADV_DODUMP QEMU_MADV_INVALID #define QEMU_MADV_DONTDUMP QEMU_MADV_INVALID #define QEMU_MADV_HUGEPAGE QEMU_MADV_INVALID +#define QEMU_MADV_NOHUGEPAGE QEMU_MADV_INVALID #endif @@ -220,8 +298,12 @@ static inline void qemu_timersub(const struct timeval *val1, void qemu_set_cloexec(int fd); -void qemu_set_version(const char *); -const char *qemu_get_version(void); +/* QEMU "hardware version" setting. Used to replace code that exposed + * QEMU_VERSION to guests in the past and need to keep compatibilty. + * Do not use qemu_hw_version() in new code. + */ +void qemu_set_hw_version(const char *); +const char *qemu_hw_version(void); void fips_set_state(bool requested); bool fips_get_state(void); @@ -261,4 +343,18 @@ void os_mem_prealloc(int fd, char *area, size_t sz); int qemu_read_password(char *buf, int buf_size); +/** + * qemu_fork: + * + * A version of fork that avoids signal handler race + * conditions that can lead to child process getting + * signals that are otherwise only expected by the + * parent. It also resets all signal handlers to the + * default settings. + * + * Returns 0 to child process, pid number to parent + * or -1 on failure. + */ +pid_t qemu_fork(Error **errp); + #endif diff --git a/qemu/include/qemu/path.h b/qemu/include/qemu/path.h new file mode 100644 index 000000000..ed5fee086 --- /dev/null +++ b/qemu/include/qemu/path.h @@ -0,0 +1,7 @@ +#ifndef QEMU_PATH_H +#define QEMU_PATH_H 1 + +void init_paths(const char *prefix); +const char *path(const char *pathname); + +#endif diff --git a/qemu/include/qemu/queue.h b/qemu/include/qemu/queue.h index a8d3cb8e6..f781aa20a 100644 --- a/qemu/include/qemu/queue.h +++ b/qemu/include/qemu/queue.h @@ -117,12 +117,6 @@ struct { \ } \ } while (/*CONSTCOND*/0) -#define QLIST_FIX_HEAD_PTR(head, field) do { \ - if ((head)->lh_first != NULL) { \ - (head)->lh_first->field.le_prev = &(head)->lh_first; \ - } \ -} while (/*CONSTCOND*/0) - #define QLIST_INSERT_AFTER(listelm, elm, field) do { \ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ (listelm)->field.le_next->field.le_prev = \ diff --git a/qemu/include/qemu/range.h b/qemu/include/qemu/range.h index cfa021fd4..c903eb574 100644 --- a/qemu/include/qemu/range.h +++ b/qemu/include/qemu/range.h @@ -1,8 +1,6 @@ #ifndef QEMU_RANGE_H #define QEMU_RANGE_H -#include -#include #include "qemu/queue.h" /* diff --git a/qemu/include/qemu/rcu.h b/qemu/include/qemu/rcu.h index 7df1e8662..56d3a682a 100644 --- a/qemu/include/qemu/rcu.h +++ b/qemu/include/qemu/rcu.h @@ -23,15 +23,8 @@ * IBM's contributions to this file may be relicensed under LGPLv2 or later. */ -#include -#include -#include -#include -#include -#include #include -#include "qemu/compiler.h" #include "qemu/thread.h" #include "qemu/queue.h" #include "qemu/atomic.h" @@ -71,7 +64,7 @@ struct rcu_reader_data { /* Data used by reader only */ unsigned depth; - /* Data used for registry, protected by rcu_gp_lock */ + /* Data used for registry, protected by rcu_registry_lock */ QLIST_ENTRY(rcu_reader_data) node; }; @@ -88,10 +81,6 @@ static inline void rcu_read_lock(void) ctr = atomic_read(&rcu_gp_ctr); atomic_xchg(&p_rcu_reader->ctr, ctr); - if (atomic_read(&p_rcu_reader->waiting)) { - atomic_set(&p_rcu_reader->waiting, false); - qemu_event_set(&rcu_gp_event); - } } static inline void rcu_read_unlock(void) @@ -104,7 +93,7 @@ static inline void rcu_read_unlock(void) } atomic_xchg(&p_rcu_reader->ctr, 0); - if (atomic_read(&p_rcu_reader->waiting)) { + if (unlikely(atomic_read(&p_rcu_reader->waiting))) { atomic_set(&p_rcu_reader->waiting, false); qemu_event_set(&rcu_gp_event); } diff --git a/qemu/include/qemu/seqlock.h b/qemu/include/qemu/seqlock.h index 3ff118a1a..70b01fd60 100644 --- a/qemu/include/qemu/seqlock.h +++ b/qemu/include/qemu/seqlock.h @@ -55,18 +55,18 @@ static inline void seqlock_write_unlock(QemuSeqLock *sl) static inline unsigned seqlock_read_begin(QemuSeqLock *sl) { /* Always fail if a write is in progress. */ - unsigned ret = sl->sequence & ~1; + unsigned ret = atomic_read(&sl->sequence); /* Read sequence before reading other fields. */ smp_rmb(); - return ret; + return ret & ~1; } -static int seqlock_read_retry(const QemuSeqLock *sl, unsigned start) +static inline int seqlock_read_retry(const QemuSeqLock *sl, unsigned start) { /* Read other fields before reading final sequence. */ smp_rmb(); - return unlikely(sl->sequence != start); + return unlikely(atomic_read(&sl->sequence) != start); } #endif diff --git a/qemu/include/qemu/sockets.h b/qemu/include/qemu/sockets.h index c174b5cbd..1bd92180f 100644 --- a/qemu/include/qemu/sockets.h +++ b/qemu/include/qemu/sockets.h @@ -3,35 +3,13 @@ #define QEMU_SOCKET_H #ifdef _WIN32 -#include -#include -#include - -#define socket_error() WSAGetLastError() int inet_aton(const char *cp, struct in_addr *ia); -#else - -#include -#include -#include -#include -#include -#include -#include - -#define socket_error() errno -#define closesocket(s) close(s) - #endif /* !_WIN32 */ -#include "qemu/option.h" -#include "qapi/error.h" #include "qapi-types.h" -extern QemuOptsList socket_optslist; - /* misc helpers */ int qemu_socket(int domain, int type, int protocol); int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen); @@ -40,8 +18,6 @@ int socket_set_nodelay(int fd); void qemu_set_block(int fd); void qemu_set_nonblock(int fd); int socket_set_fast_reuse(int fd); -int send_all(int fd, const void *buf, int len1); -int recv_all(int fd, void *buf, int len1, bool single_read); #ifdef WIN32 /* Windows has different names for the same constants with the same values */ @@ -53,26 +29,19 @@ int recv_all(int fd, void *buf, int len1, bool single_read); /* callback function for nonblocking connect * valid fd on success, negative error code on failure */ -typedef void NonBlockingConnectHandler(int fd, Error *errp, void *opaque); +typedef void NonBlockingConnectHandler(int fd, Error *err, void *opaque); InetSocketAddress *inet_parse(const char *str, Error **errp); -int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp); int inet_listen(const char *str, char *ostr, int olen, int socktype, int port_offset, Error **errp); -int inet_connect_opts(QemuOpts *opts, Error **errp, - NonBlockingConnectHandler *callback, void *opaque); int inet_connect(const char *str, Error **errp); int inet_nonblocking_connect(const char *str, NonBlockingConnectHandler *callback, void *opaque, Error **errp); -int inet_dgram_opts(QemuOpts *opts, Error **errp); NetworkAddressFamily inet_netfamily(int family); -int unix_listen_opts(QemuOpts *opts, Error **errp); int unix_listen(const char *path, char *ostr, int olen, Error **errp); -int unix_connect_opts(QemuOpts *opts, Error **errp, - NonBlockingConnectHandler *callback, void *opaque); int unix_connect(const char *path, Error **errp); int unix_nonblocking_connect(const char *str, NonBlockingConnectHandler *callback, @@ -88,4 +57,57 @@ int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp); int parse_host_port(struct sockaddr_in *saddr, const char *str); int socket_init(void); +/** + * socket_sockaddr_to_address: + * @sa: socket address struct + * @salen: size of @sa struct + * @errp: pointer to uninitialized error object + * + * Get the string representation of the socket + * address. A pointer to the allocated address information + * struct will be returned, which the caller is required to + * release with a call qapi_free_SocketAddress when no + * longer required. + * + * Returns: the socket address struct, or NULL on error + */ +SocketAddress * +socket_sockaddr_to_address(struct sockaddr_storage *sa, + socklen_t salen, + Error **errp); + +/** + * socket_local_address: + * @fd: the socket file handle + * @errp: pointer to uninitialized error object + * + * Get the string representation of the local socket + * address. A pointer to the allocated address information + * struct will be returned, which the caller is required to + * release with a call qapi_free_SocketAddress when no + * longer required. + * + * Returns: the socket address struct, or NULL on error + */ +SocketAddress *socket_local_address(int fd, Error **errp); + +/** + * socket_remote_address: + * @fd: the socket file handle + * @errp: pointer to uninitialized error object + * + * Get the string representation of the remote socket + * address. A pointer to the allocated address information + * struct will be returned, which the caller is required to + * release with a call qapi_free_SocketAddress when no + * longer required. + * + * Returns: the socket address struct, or NULL on error + */ +SocketAddress *socket_remote_address(int fd, Error **errp); + + +void qapi_copy_SocketAddress(SocketAddress **p_dest, + SocketAddress *src); + #endif /* QEMU_SOCKET_H */ diff --git a/qemu/include/qemu/thread-win32.h b/qemu/include/qemu/thread-win32.h index 3d58081be..385ff5f76 100644 --- a/qemu/include/qemu/thread-win32.h +++ b/qemu/include/qemu/thread-win32.h @@ -18,6 +18,7 @@ struct QemuSemaphore { }; struct QemuEvent { + int value; HANDLE event; }; diff --git a/qemu/include/qemu/thread.h b/qemu/include/qemu/thread.h index 5114ec8e7..bdae6dfdb 100644 --- a/qemu/include/qemu/thread.h +++ b/qemu/include/qemu/thread.h @@ -1,8 +1,6 @@ #ifndef __QEMU_THREAD_H #define __QEMU_THREAD_H 1 -#include -#include typedef struct QemuMutex QemuMutex; typedef struct QemuCond QemuCond; diff --git a/qemu/include/qemu/throttle.h b/qemu/include/qemu/throttle.h index 995b2d595..910965760 100644 --- a/qemu/include/qemu/throttle.h +++ b/qemu/include/qemu/throttle.h @@ -2,7 +2,7 @@ * QEMU throttling infrastructure * * Copyright (C) Nodalink, EURL. 2013-2014 - * Copyright (C) Igalia, S.L. 2015 + * Copyright (C) Igalia, S.L. 2015-2016 * * Authors: * Benoît Canet @@ -25,10 +25,11 @@ #ifndef THROTTLE_H #define THROTTLE_H -#include #include "qemu-common.h" #include "qemu/timer.h" +#define THROTTLE_VALUE_MAX 1000000000000000LL + typedef enum { THROTTLE_BPS_TOTAL, THROTTLE_BPS_READ, @@ -40,16 +41,47 @@ typedef enum { } BucketType; /* - * The max parameter of the leaky bucket throttling algorithm can be used to - * allow the guest to do bursts. - * The max value is a pool of I/O that the guest can use without being throttled - * at all. Throttling is triggered once this pool is empty. + * This module implements I/O limits using the leaky bucket + * algorithm. The code is independent of the I/O units, but it is + * currently used for bytes per second and operations per second. + * + * Three parameters can be set by the user: + * + * - avg: the desired I/O limits in units per second. + * - max: the limit during bursts, also in units per second. + * - burst_length: the maximum length of the burst period, in seconds. + * + * Here's how it works: + * + * - The bucket level (number of performed I/O units) is kept in + * bkt.level and leaks at a rate of bkt.avg units per second. + * + * - The size of the bucket is bkt.max * bkt.burst_length. Once the + * bucket is full no more I/O is performed until the bucket leaks + * again. This is what makes the I/O rate bkt.avg. + * + * - The bkt.avg rate does not apply until the bucket is full, + * allowing the user to do bursts until then. The I/O limit during + * bursts is bkt.max. To enforce this limit we keep an additional + * bucket in bkt.burst_length that leaks at a rate of bkt.max units + * per second. + * + * - Because of all of the above, the user can perform I/O at a + * maximum of bkt.max units per second for at most bkt.burst_length + * seconds in a row. After that the bucket will be full and the I/O + * rate will go down to bkt.avg. + * + * - Since the bucket always leaks at a rate of bkt.avg, this also + * determines how much the user needs to wait before being able to + * do bursts again. */ typedef struct LeakyBucket { double avg; /* average goal in units per second */ double max; /* leaky bucket max burst in units */ double level; /* bucket level in units */ + double burst_level; /* bucket level in units (for computing bursts) */ + unsigned burst_length; /* max length of the burst period, in seconds */ } LeakyBucket; /* The following structure is used to configure a ThrottleState @@ -82,12 +114,6 @@ void throttle_leak_bucket(LeakyBucket *bkt, int64_t delta); int64_t throttle_compute_wait(LeakyBucket *bkt); -/* expose timer computation function for unit tests */ -bool throttle_compute_timer(ThrottleState *ts, - bool is_write, - int64_t now, - int64_t *next_timestamp); - /* init/destroy cycle */ void throttle_init(ThrottleState *ts); @@ -110,9 +136,7 @@ bool throttle_timers_are_initialized(ThrottleTimers *tt); /* configuration */ bool throttle_enabled(ThrottleConfig *cfg); -bool throttle_conflicting(ThrottleConfig *cfg); - -bool throttle_is_valid(ThrottleConfig *cfg); +bool throttle_is_valid(ThrottleConfig *cfg, Error **errp); void throttle_config(ThrottleState *ts, ThrottleTimers *tt, @@ -120,6 +144,8 @@ void throttle_config(ThrottleState *ts, void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg); +void throttle_config_init(ThrottleConfig *cfg); + /* usage */ bool throttle_schedule_timer(ThrottleState *ts, ThrottleTimers *tt, diff --git a/qemu/include/qemu/timed-average.h b/qemu/include/qemu/timed-average.h new file mode 100644 index 000000000..08245e7a1 --- /dev/null +++ b/qemu/include/qemu/timed-average.h @@ -0,0 +1,63 @@ +/* + * QEMU timed average computation + * + * Copyright (C) Nodalink, EURL. 2014 + * Copyright (C) Igalia, S.L. 2015 + * + * Authors: + * Benoît Canet + * Alberto Garcia + * + * 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) version 3 or 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 . + */ + +#ifndef TIMED_AVERAGE_H +#define TIMED_AVERAGE_H + + +#include "qemu/timer.h" + +typedef struct TimedAverageWindow TimedAverageWindow; +typedef struct TimedAverage TimedAverage; + +/* All fields of both structures are private */ + +struct TimedAverageWindow { + uint64_t min; /* minimum value accounted in the window */ + uint64_t max; /* maximum value accounted in the window */ + uint64_t sum; /* sum of all values */ + uint64_t count; /* number of values */ + int64_t expiration; /* the end of the current window in ns */ +}; + +struct TimedAverage { + uint64_t period; /* period in nanoseconds */ + TimedAverageWindow windows[2]; /* two overlapping windows of with + * an offset of period / 2 between them */ + unsigned current; /* the current window index: it's also the + * oldest window index */ + QEMUClockType clock_type; /* the clock used */ +}; + +void timed_average_init(TimedAverage *ta, QEMUClockType clock_type, + uint64_t period); + +void timed_average_account(TimedAverage *ta, uint64_t value); + +uint64_t timed_average_min(TimedAverage *ta); +uint64_t timed_average_avg(TimedAverage *ta); +uint64_t timed_average_max(TimedAverage *ta); +uint64_t timed_average_sum(TimedAverage *ta, uint64_t *elapsed); + +#endif diff --git a/qemu/include/qemu/timer.h b/qemu/include/qemu/timer.h index 5923d600f..471969a24 100644 --- a/qemu/include/qemu/timer.h +++ b/qemu/include/qemu/timer.h @@ -1,9 +1,9 @@ #ifndef QEMU_TIMER_H #define QEMU_TIMER_H -#include "qemu/typedefs.h" #include "qemu-common.h" #include "qemu/notify.h" +#include "qemu/host-utils.h" #define NANOSECONDS_PER_SECOND 1000000000LL @@ -209,12 +209,11 @@ void qemu_clock_notify(QEMUClockType type); void qemu_clock_enable(QEMUClockType type, bool enabled); /** - * qemu_clock_warp: - * @type: the clock type + * qemu_start_warp_timer: * - * Warp a clock to a new value + * Starts a timer for virtual clock update */ -void qemu_clock_warp(QEMUClockType type); +void qemu_start_warp_timer(void); /** * qemu_clock_register_reset_notifier: @@ -784,18 +783,13 @@ void cpu_enable_ticks(void); /* Caller must hold BQL */ void cpu_disable_ticks(void); -static inline int64_t get_ticks_per_sec(void) -{ - return 1000000000LL; -} - static inline int64_t get_max_clock_jump(void) { /* This should be small enough to prevent excessive interrupts from being * generated by the RTC on clock jumps, but large enough to avoid frequent * unnecessary resets in idle VMs. */ - return 60 * get_ticks_per_sec(); + return 60 * NANOSECONDS_PER_SECOND; } /* @@ -821,7 +815,7 @@ static inline int64_t get_clock(void) { LARGE_INTEGER ti; QueryPerformanceCounter(&ti); - return muldiv64(ti.QuadPart, get_ticks_per_sec(), clock_freq); + return muldiv64(ti.QuadPart, NANOSECONDS_PER_SECOND, clock_freq); } #else @@ -856,7 +850,7 @@ int64_t cpu_icount_to_ns(int64_t icount); #if defined(_ARCH_PPC) -static inline int64_t cpu_get_real_ticks(void) +static inline int64_t cpu_get_host_ticks(void) { int64_t retval; #ifdef _ARCH_PPC64 @@ -882,7 +876,7 @@ static inline int64_t cpu_get_real_ticks(void) #elif defined(__i386__) -static inline int64_t cpu_get_real_ticks(void) +static inline int64_t cpu_get_host_ticks(void) { int64_t val; asm volatile ("rdtsc" : "=A" (val)); @@ -891,7 +885,7 @@ static inline int64_t cpu_get_real_ticks(void) #elif defined(__x86_64__) -static inline int64_t cpu_get_real_ticks(void) +static inline int64_t cpu_get_host_ticks(void) { uint32_t low,high; int64_t val; @@ -904,7 +898,7 @@ static inline int64_t cpu_get_real_ticks(void) #elif defined(__hppa__) -static inline int64_t cpu_get_real_ticks(void) +static inline int64_t cpu_get_host_ticks(void) { int val; asm volatile ("mfctl %%cr16, %0" : "=r"(val)); @@ -913,7 +907,7 @@ static inline int64_t cpu_get_real_ticks(void) #elif defined(__ia64) -static inline int64_t cpu_get_real_ticks(void) +static inline int64_t cpu_get_host_ticks(void) { int64_t val; asm volatile ("mov %0 = ar.itc" : "=r"(val) :: "memory"); @@ -922,7 +916,7 @@ static inline int64_t cpu_get_real_ticks(void) #elif defined(__s390__) -static inline int64_t cpu_get_real_ticks(void) +static inline int64_t cpu_get_host_ticks(void) { int64_t val; asm volatile("stck 0(%1)" : "=m" (val) : "a" (&val) : "cc"); @@ -931,7 +925,7 @@ static inline int64_t cpu_get_real_ticks(void) #elif defined(__sparc__) -static inline int64_t cpu_get_real_ticks (void) +static inline int64_t cpu_get_host_ticks (void) { #if defined(_LP64) uint64_t rval; @@ -969,7 +963,7 @@ static inline int64_t cpu_get_real_ticks (void) : "=r" (value)); \ } -static inline int64_t cpu_get_real_ticks(void) +static inline int64_t cpu_get_host_ticks(void) { /* On kernels >= 2.6.25 rdhwr , $2 and $3 are emulated */ uint32_t count; @@ -985,7 +979,7 @@ static inline int64_t cpu_get_real_ticks(void) #elif defined(__alpha__) -static inline int64_t cpu_get_real_ticks(void) +static inline int64_t cpu_get_host_ticks(void) { uint64_t cc; uint32_t cur, ofs; @@ -1000,7 +994,7 @@ static inline int64_t cpu_get_real_ticks(void) /* The host CPU doesn't have an easily accessible cycle counter. Just return a monotonically increasing value. This will be totally wrong, but hopefully better than nothing. */ -static inline int64_t cpu_get_real_ticks (void) +static inline int64_t cpu_get_host_ticks (void) { static int64_t ticks = 0; return ticks++; diff --git a/qemu/include/qemu/tls.h b/qemu/include/qemu/tls.h deleted file mode 100644 index b92ea9d7d..000000000 --- a/qemu/include/qemu/tls.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Abstraction layer for defining and using TLS variables - * - * Copyright (c) 2011 Red Hat, Inc - * Copyright (c) 2011 Linaro Limited - * - * Authors: - * Paolo Bonzini - * Peter Maydell - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#ifndef QEMU_TLS_H -#define QEMU_TLS_H - -/* Per-thread variables. Note that we only have implementations - * which are really thread-local on Linux; the dummy implementations - * define plain global variables. - * - * This means that for the moment use should be restricted to - * per-VCPU variables, which are OK because: - * - the only -user mode supporting multiple VCPU threads is linux-user - * - TCG system mode is single-threaded regarding VCPUs - * - KVM system mode is multi-threaded but limited to Linux - * - * TODO: proper implementations via Win32 .tls sections and - * POSIX pthread_getspecific. - */ -#ifdef __linux__ -#define DECLARE_TLS(type, x) extern DEFINE_TLS(type, x) -#define DEFINE_TLS(type, x) __thread __typeof__(type) tls__##x -#define tls_var(x) tls__##x -#else -/* Dummy implementations which define plain global variables */ -#define DECLARE_TLS(type, x) extern DEFINE_TLS(type, x) -#define DEFINE_TLS(type, x) __typeof__(type) tls__##x -#define tls_var(x) tls__##x -#endif - -#endif diff --git a/qemu/include/qemu/typedefs.h b/qemu/include/qemu/typedefs.h index 6fdcbcd52..1dcf6f5d5 100644 --- a/qemu/include/qemu/typedefs.h +++ b/qemu/include/qemu/typedefs.h @@ -3,25 +3,30 @@ /* A load of opaque types so that device init declarations don't have to pull in all the real definitions. */ -struct Monitor; /* Please keep this list in alphabetical order */ typedef struct AdapterInfo AdapterInfo; typedef struct AddressSpace AddressSpace; typedef struct AioContext AioContext; +typedef struct AllwinnerAHCIState AllwinnerAHCIState; typedef struct AudioState AudioState; +typedef struct BdrvDirtyBitmap BdrvDirtyBitmap; typedef struct BlockBackend BlockBackend; +typedef struct BlockBackendRootState BlockBackendRootState; typedef struct BlockDriverState BlockDriverState; typedef struct BusClass BusClass; typedef struct BusState BusState; typedef struct CharDriverState CharDriverState; typedef struct CompatProperty CompatProperty; -typedef struct DeviceState DeviceState; +typedef struct CPUAddressSpace CPUAddressSpace; +typedef struct CPUState CPUState; typedef struct DeviceListener DeviceListener; +typedef struct DeviceState DeviceState; typedef struct DisplayChangeListener DisplayChangeListener; typedef struct DisplayState DisplayState; typedef struct DisplaySurface DisplaySurface; typedef struct DriveInfo DriveInfo; +typedef struct Error Error; typedef struct EventNotifier EventNotifier; typedef struct FWCfgIoState FWCfgIoState; typedef struct FWCfgMemState FWCfgMemState; @@ -31,6 +36,7 @@ typedef struct I2CBus I2CBus; typedef struct I2SCodec I2SCodec; typedef struct ISABus ISABus; typedef struct ISADevice ISADevice; +typedef struct IsaDma IsaDma; typedef struct LoadStateEntry LoadStateEntry; typedef struct MACAddr MACAddr; typedef struct MachineClass MachineClass; @@ -41,10 +47,13 @@ typedef struct MemoryRegion MemoryRegion; typedef struct MemoryRegionSection MemoryRegionSection; typedef struct MigrationIncomingState MigrationIncomingState; typedef struct MigrationParams MigrationParams; +typedef struct MigrationState MigrationState; typedef struct Monitor Monitor; +typedef struct MonitorDef MonitorDef; typedef struct MouseTransformInfo MouseTransformInfo; typedef struct MSIMessage MSIMessage; typedef struct NetClientState NetClientState; +typedef struct NetFilterState NetFilterState; typedef struct NICInfo NICInfo; typedef struct PcGuestInfo PcGuestInfo; typedef struct PCIBridge PCIBridge; @@ -57,19 +66,27 @@ typedef struct PCIEPort PCIEPort; typedef struct PCIESlot PCIESlot; typedef struct PCIExpressDevice PCIExpressDevice; typedef struct PCIExpressHost PCIExpressHost; +typedef struct PCIHostDeviceAddress PCIHostDeviceAddress; typedef struct PCIHostState PCIHostState; +typedef struct PCMachineClass PCMachineClass; +typedef struct PCMachineState PCMachineState; typedef struct PCMCIACardState PCMCIACardState; typedef struct PixelFormat PixelFormat; -typedef struct PropertyInfo PropertyInfo; +typedef struct PostcopyDiscardState PostcopyDiscardState; typedef struct Property Property; +typedef struct PropertyInfo PropertyInfo; typedef struct QEMUBH QEMUBH; typedef struct QemuConsole QemuConsole; typedef struct QEMUFile QEMUFile; -typedef struct QEMUMachine QEMUMachine; +typedef struct QemuOpt QemuOpt; +typedef struct QemuOpts QemuOpts; +typedef struct QemuOptsList QemuOptsList; typedef struct QEMUSGList QEMUSGList; typedef struct QEMUSizedBuffer QEMUSizedBuffer; -typedef struct QEMUTimerListGroup QEMUTimerListGroup; typedef struct QEMUTimer QEMUTimer; +typedef struct QEMUTimerListGroup QEMUTimerListGroup; +typedef struct QObject QObject; +typedef struct RAMBlock RAMBlock; typedef struct Range Range; typedef struct SerialState SerialState; typedef struct SHPCDevice SHPCDevice; diff --git a/qemu/include/qemu/unicode.h b/qemu/include/qemu/unicode.h new file mode 100644 index 000000000..d8731652d --- /dev/null +++ b/qemu/include/qemu/unicode.h @@ -0,0 +1,6 @@ +#ifndef QEMU_UNICODE_H +#define QEMU_UNICODE_H 1 + +int mod_utf8_codepoint(const char *s, size_t n, char **end); + +#endif diff --git a/qemu/include/qemu/xattr.h b/qemu/include/qemu/xattr.h index f910d96ea..83cf98cbd 100644 --- a/qemu/include/qemu/xattr.h +++ b/qemu/include/qemu/xattr.h @@ -18,7 +18,6 @@ * in /usr/include/sys, and don't have ENOATTR. */ -#include "config-host.h" #ifdef CONFIG_LIBATTR # include diff --git a/qemu/include/qom/cpu.h b/qemu/include/qom/cpu.h index 20aabc9cb..b7a10f791 100644 --- a/qemu/include/qom/cpu.h +++ b/qemu/include/qom/cpu.h @@ -20,16 +20,12 @@ #ifndef QEMU_CPU_H #define QEMU_CPU_H -#include -#include #include "hw/qdev-core.h" #include "disas/bfd.h" #include "exec/hwaddr.h" #include "exec/memattrs.h" #include "qemu/queue.h" #include "qemu/thread.h" -#include "qemu/tls.h" -#include "qemu/typedefs.h" typedef int (*WriteCoreDumpFunction)(const void *buf, size_t size, void *opaque); @@ -64,7 +60,7 @@ typedef uint64_t vaddr; #define CPU_CLASS(class) OBJECT_CLASS_CHECK(CPUClass, (class), TYPE_CPU) #define CPU_GET_CLASS(obj) OBJECT_GET_CLASS(CPUClass, (obj), TYPE_CPU) -typedef struct CPUState CPUState; +typedef struct CPUWatchpoint CPUWatchpoint; typedef void (*CPUUnassignedAccess)(CPUState *cpu, hwaddr addr, bool is_write, bool is_exec, int opaque, @@ -99,8 +95,16 @@ struct TranslationBlock; * #TranslationBlock. * @handle_mmu_fault: Callback for handling an MMU fault. * @get_phys_page_debug: Callback for obtaining a physical address. + * @get_phys_page_attrs_debug: Callback for obtaining a physical address and the + * associated memory transaction attributes to use for the access. + * CPUs which use memory transaction attributes should implement this + * instead of get_phys_page_debug. + * @asidx_from_attrs: Callback to return the CPU AddressSpace to use for + * a memory access with the specified memory transaction attributes. * @gdb_read_register: Callback for letting GDB read a register. * @gdb_write_register: Callback for letting GDB write a register. + * @debug_check_watchpoint: Callback: return true if the architectural + * watchpoint whose address has matched should really fire. * @debug_excp_handler: Callback for handling debug exceptions. * @write_elf64_note: Callback for writing a CPU-specific ELF note to a * 64-bit VM coredump. @@ -115,6 +119,8 @@ struct TranslationBlock; * @gdb_core_xml_file: File name for core registers GDB XML description. * @gdb_stop_before_watchpoint: Indicates whether GDB expects the CPU to stop * before the insn which triggers a watchpoint rather than after it. + * @gdb_arch_name: Optional callback that returns the architecture name known + * to GDB. The caller must free the returned string with g_free. * @cpu_exec_enter: Callback for cpu_exec preparation. * @cpu_exec_exit: Callback for cpu_exec cleanup. * @cpu_exec_interrupt: Callback for processing interrupts in cpu_exec. @@ -153,8 +159,12 @@ typedef struct CPUClass { int (*handle_mmu_fault)(CPUState *cpu, vaddr address, int rw, int mmu_index); hwaddr (*get_phys_page_debug)(CPUState *cpu, vaddr addr); + hwaddr (*get_phys_page_attrs_debug)(CPUState *cpu, vaddr addr, + MemTxAttrs *attrs); + int (*asidx_from_attrs)(CPUState *cpu, MemTxAttrs attrs); int (*gdb_read_register)(CPUState *cpu, uint8_t *buf, int reg); int (*gdb_write_register)(CPUState *cpu, uint8_t *buf, int reg); + bool (*debug_check_watchpoint)(CPUState *cpu, CPUWatchpoint *wp); void (*debug_excp_handler)(CPUState *cpu); int (*write_elf64_note)(WriteCoreDumpFunction f, CPUState *cpu, @@ -169,6 +179,7 @@ typedef struct CPUClass { const struct VMStateDescription *vmsd; int gdb_num_core_regs; const char *gdb_core_xml_file; + gchar * (*gdb_arch_name)(CPUState *cpu); bool gdb_stop_before_watchpoint; void (*cpu_exec_enter)(CPUState *cpu); @@ -196,14 +207,14 @@ typedef struct CPUBreakpoint { QTAILQ_ENTRY(CPUBreakpoint) entry; } CPUBreakpoint; -typedef struct CPUWatchpoint { +struct CPUWatchpoint { vaddr vaddr; vaddr len; vaddr hitaddr; MemTxAttrs hitattrs; int flags; /* BP_* */ QTAILQ_ENTRY(CPUWatchpoint) entry; -} CPUWatchpoint; +}; struct KVMState; struct kvm_run; @@ -224,6 +235,7 @@ struct kvm_run; * @halted: Nonzero if the CPU is in suspended state. * @stop: Indicates a pending stop request. * @stopped: Indicates the CPU has been artificially stopped. + * @crash_occurred: Indicates the OS reported a crash (panic) for this CPU * @tcg_exit_req: Set to force TCG to stop executing linked TBs for this * CPU and return to its top level loop. * @singlestep_enabled: Flags for single-stepping. @@ -231,7 +243,14 @@ struct kvm_run; * @icount_decr: Number of cycles left, with interrupt flag in high bit. * This allows a single read-compare-cbranch-write sequence to test * for both decrementer underflow and exceptions. - * @can_do_io: Nonzero if memory-mapped IO is safe. + * @can_do_io: Nonzero if memory-mapped IO is safe. Deterministic execution + * requires that IO only be performed on the last instruction of a TB + * so that interrupts take effect immediately. + * @cpu_ases: Pointer to array of CPUAddressSpaces (which define the + * AddressSpaces this CPU has) + * @num_ases: number of CPUAddressSpaces in @cpu_ases + * @as: Pointer to the first AddressSpace, for the convenience of targets which + * only have a single AddressSpace * @env_ptr: Pointer to subclass-specific CPUArchState field. * @current_tb: Currently executing TB. * @gdb_regs: Additional GDB registers. @@ -242,6 +261,8 @@ struct kvm_run; * @mem_io_pc: Host Program Counter at which the memory was accessed. * @mem_io_vaddr: Target virtual address at which the memory was accessed. * @kvm_fd: vCPU file descriptor for KVM. + * @work_mutex: Lock to prevent multiple access to queued_work_*. + * @queued_work_first: First asynchronous work pending. * * State of one CPU core or thread. */ @@ -262,20 +283,24 @@ struct CPUState { uint32_t host_tid; bool running; struct QemuCond *halt_cond; - struct qemu_work_item *queued_work_first, *queued_work_last; bool thread_kicked; bool created; bool stop; bool stopped; - volatile sig_atomic_t exit_request; + bool crash_occurred; + bool exit_request; uint32_t interrupt_request; int singlestep_enabled; int64_t icount_extra; sigjmp_buf jmp_env; + QemuMutex work_mutex; + struct qemu_work_item *queued_work_first, *queued_work_last; + + CPUAddressSpace *cpu_ases; + int num_ases; AddressSpace *as; - struct AddressSpaceDispatch *memory_dispatch; - MemoryListener *tcg_as_listener; + MemoryRegion *memory; void *env_ptr; /* CPUArchState */ struct TranslationBlock *current_tb; @@ -314,11 +339,16 @@ struct CPUState { uint32_t can_do_io; int32_t exception_index; /* used by m68k TCG */ + /* Used to keep track of an outstanding cpu throttle thread for migration + * autoconverge + */ + bool throttle_thread_scheduled; + /* Note that this is accessed at the start of every TB via a negative offset from AREG0. Leave this field at the end so as to make the (absolute value) offset as small as possible. This reduces code size, especially for hosts without large memory offsets. */ - volatile sig_atomic_t tcg_exit_req; + uint32_t tcg_exit_req; }; QTAILQ_HEAD(CPUTailQ, CPUState); @@ -331,8 +361,7 @@ extern struct CPUTailQ cpus; QTAILQ_FOREACH_REVERSE(cpu, &cpus, CPUTailQ, node) #define first_cpu QTAILQ_FIRST(&cpus) -DECLARE_TLS(CPUState *, current_cpu); -#define current_cpu tls_var(current_cpu) +extern __thread CPUState *current_cpu; /** * cpu_paging_enabled: @@ -428,6 +457,32 @@ void cpu_dump_statistics(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags); #ifndef CONFIG_USER_ONLY +/** + * cpu_get_phys_page_attrs_debug: + * @cpu: The CPU to obtain the physical page address for. + * @addr: The virtual address. + * @attrs: Updated on return with the memory transaction attributes to use + * for this access. + * + * Obtains the physical page corresponding to a virtual one, together + * with the corresponding memory transaction attributes to use for the access. + * Use it only for debugging because no protection checks are done. + * + * Returns: Corresponding physical page address or -1 if no page found. + */ +static inline hwaddr cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, + MemTxAttrs *attrs) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + + if (cc->get_phys_page_attrs_debug) { + return cc->get_phys_page_attrs_debug(cpu, addr, attrs); + } + /* Fallback for CPUs which don't implement the _attrs_ hook */ + *attrs = MEMTXATTRS_UNSPECIFIED; + return cc->get_phys_page_debug(cpu, addr); +} + /** * cpu_get_phys_page_debug: * @cpu: The CPU to obtain the physical page address for. @@ -439,10 +494,27 @@ void cpu_dump_statistics(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, * Returns: Corresponding physical page address or -1 if no page found. */ static inline hwaddr cpu_get_phys_page_debug(CPUState *cpu, vaddr addr) +{ + MemTxAttrs attrs = {}; + + return cpu_get_phys_page_attrs_debug(cpu, addr, &attrs); +} + +/** cpu_asidx_from_attrs: + * @cpu: CPU + * @attrs: memory transaction attributes + * + * Returns the address space index specifying the CPU AddressSpace + * to use for a memory access with the given transaction attributes. + */ +static inline int cpu_asidx_from_attrs(CPUState *cpu, MemTxAttrs attrs) { CPUClass *cc = CPU_GET_CLASS(cpu); - return cc->get_phys_page_debug(cpu, addr); + if (cc->asidx_from_attrs) { + return cc->asidx_from_attrs(cpu, attrs); + } + return 0; } #endif @@ -559,6 +631,43 @@ CPUState *qemu_get_cpu(int index); */ bool cpu_exists(int64_t id); +/** + * cpu_throttle_set: + * @new_throttle_pct: Percent of sleep time. Valid range is 1 to 99. + * + * Throttles all vcpus by forcing them to sleep for the given percentage of + * time. A throttle_percentage of 25 corresponds to a 75% duty cycle roughly. + * (example: 10ms sleep for every 30ms awake). + * + * cpu_throttle_set can be called as needed to adjust new_throttle_pct. + * Once the throttling starts, it will remain in effect until cpu_throttle_stop + * is called. + */ +void cpu_throttle_set(int new_throttle_pct); + +/** + * cpu_throttle_stop: + * + * Stops the vcpu throttling started by cpu_throttle_set. + */ +void cpu_throttle_stop(void); + +/** + * cpu_throttle_active: + * + * Returns: %true if the vcpus are currently being throttled, %false otherwise. + */ +bool cpu_throttle_active(void); + +/** + * cpu_throttle_get_percentage: + * + * Returns the vcpu throttle percentage. See cpu_throttle_set for details. + * + * Returns: The throttle percentage in range 1 to 99. + */ +int cpu_throttle_get_percentage(void); + #ifndef CONFIG_USER_ONLY typedef void (*CPUInterruptHandler)(CPUState *, int); @@ -673,6 +782,7 @@ void cpu_single_step(CPUState *cpu, int enabled); /* 0x08 currently unused */ #define BP_GDB 0x10 #define BP_CPU 0x20 +#define BP_ANY (BP_GDB | BP_CPU) #define BP_WATCHPOINT_HIT_READ 0x40 #define BP_WATCHPOINT_HIT_WRITE 0x80 #define BP_WATCHPOINT_HIT (BP_WATCHPOINT_HIT_READ | BP_WATCHPOINT_HIT_WRITE) @@ -683,6 +793,21 @@ int cpu_breakpoint_remove(CPUState *cpu, vaddr pc, int flags); void cpu_breakpoint_remove_by_ref(CPUState *cpu, CPUBreakpoint *breakpoint); void cpu_breakpoint_remove_all(CPUState *cpu, int mask); +/* Return true if PC matches an installed breakpoint. */ +static inline bool cpu_breakpoint_test(CPUState *cpu, vaddr pc, int mask) +{ + CPUBreakpoint *bp; + + if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) { + QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { + if (bp->pc == pc && (bp->flags & mask)) { + return true; + } + } + } + return false; +} + int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, int flags, CPUWatchpoint **watchpoint); int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, diff --git a/qemu/include/qom/object.h b/qemu/include/qom/object.h index 807978eec..21bb5ff14 100644 --- a/qemu/include/qom/object.h +++ b/qemu/include/qom/object.h @@ -15,12 +15,8 @@ #define QEMU_OBJECT_H #include -#include -#include +#include "qapi-types.h" #include "qemu/queue.h" -#include "qapi/error.h" - -struct Visitor; struct TypeImpl; typedef struct TypeImpl *Type; @@ -291,16 +287,16 @@ typedef struct InterfaceInfo InterfaceInfo; * ObjectPropertyAccessor: * @obj: the object that owns the property * @v: the visitor that contains the property data - * @opaque: the object property opaque * @name: the name of the property + * @opaque: the object property opaque * @errp: a pointer to an Error that is filled if getting/setting fails. * * Called when trying to get/set a property. */ typedef void (ObjectPropertyAccessor)(Object *obj, - struct Visitor *v, - void *opaque, + Visitor *v, const char *name, + void *opaque, Error **errp); /** @@ -344,8 +340,6 @@ typedef struct ObjectProperty ObjectPropertyResolve *resolve; ObjectPropertyRelease *release; void *opaque; - - QTAILQ_ENTRY(ObjectProperty) node; } ObjectProperty; /** @@ -383,6 +377,8 @@ struct ObjectClass const char *class_cast_cache[OBJECT_CLASS_CAST_CACHE]; ObjectUnparent *unparent; + + GHashTable *properties; }; /** @@ -396,16 +392,13 @@ struct ObjectClass * As a result, #Object contains a reference to the objects type as its * first member. This allows identification of the real type of the object at * run time. - * - * #Object also contains a list of #Interfaces that this object - * implements. */ struct Object { /*< private >*/ ObjectClass *class; ObjectFree *free; - QTAILQ_HEAD(, ObjectProperty) properties; + GHashTable *properties; uint32_t ref; Object *parent; }; @@ -510,16 +503,16 @@ struct TypeInfo /** * OBJECT_CLASS_CHECK: - * @class: The C type to use for the return value. - * @obj: A derivative of @type to cast. - * @name: the QOM typename of @class. + * @class_type: The C type to use for the return value. + * @class: A derivative class of @class_type to cast. + * @name: the QOM typename of @class_type. * * A type safe version of @object_class_dynamic_cast_assert. This macro is * typically wrapped by each type to perform type safe casts of a class to a * specific class type. */ -#define OBJECT_CLASS_CHECK(class, obj, name) \ - ((class *)object_class_dynamic_cast_assert(OBJECT_CLASS(obj), (name), \ +#define OBJECT_CLASS_CHECK(class_type, class, name) \ + ((class_type *)object_class_dynamic_cast_assert(OBJECT_CLASS(class), (name), \ __FILE__, __LINE__, __func__)) /** @@ -949,6 +942,13 @@ ObjectProperty *object_property_add(Object *obj, const char *name, void object_property_del(Object *obj, const char *name, Error **errp); +ObjectProperty *object_class_property_add(ObjectClass *klass, const char *name, + const char *type, + ObjectPropertyAccessor *get, + ObjectPropertyAccessor *set, + ObjectPropertyRelease *release, + void *opaque, Error **errp); + /** * object_property_find: * @obj: the object @@ -959,6 +959,55 @@ void object_property_del(Object *obj, const char *name, Error **errp); */ ObjectProperty *object_property_find(Object *obj, const char *name, Error **errp); +ObjectProperty *object_class_property_find(ObjectClass *klass, const char *name, + Error **errp); + +typedef struct ObjectPropertyIterator { + ObjectClass *nextclass; + GHashTableIter iter; +} ObjectPropertyIterator; + +/** + * object_property_iter_init: + * @obj: the object + * + * Initializes an iterator for traversing all properties + * registered against an object instance, its class and all parent classes. + * + * It is forbidden to modify the property list while iterating, + * whether removing or adding properties. + * + * Typical usage pattern would be + * + * + * Using object property iterators + * + * ObjectProperty *prop; + * ObjectPropertyIterator iter; + * + * object_property_iter_init(&iter, obj); + * while ((prop = object_property_iter_next(&iter))) { + * ... do something with prop ... + * } + * + * + */ +void object_property_iter_init(ObjectPropertyIterator *iter, + Object *obj); + +/** + * object_property_iter_next: + * @iter: the iterator instance + * + * Return the next available property. If no further properties + * are available, a %NULL value will be returned and the @iter + * pointer should not be used again after this point without + * re-initializing it. + * + * Returns: the next property, or %NULL when all properties + * have been traversed. + */ +ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter); void object_unparent(Object *obj); @@ -972,7 +1021,7 @@ void object_unparent(Object *obj); * * Reads a property from a object. */ -void object_property_get(Object *obj, struct Visitor *v, const char *name, +void object_property_get(Object *obj, Visitor *v, const char *name, Error **errp); /** @@ -1063,7 +1112,7 @@ void object_property_set_int(Object *obj, int64_t value, * @name: the name of the property * @errp: returns an error if this function fails * - * Returns: the value of the property, converted to an integer, or NULL if + * Returns: the value of the property, converted to an integer, or negative if * an error occurs (including when the property value is not an integer). */ int64_t object_property_get_int(Object *obj, const char *name, @@ -1108,7 +1157,7 @@ void object_property_get_uint16List(Object *obj, const char *name, * * Writes a property to a object. */ -void object_property_set(Object *obj, struct Visitor *v, const char *name, +void object_property_set(Object *obj, Visitor *v, const char *name, Error **errp); /** @@ -1327,6 +1376,12 @@ void object_property_add_str(Object *obj, const char *name, void (*set)(Object *, const char *, Error **), Error **errp); +void object_class_property_add_str(ObjectClass *klass, const char *name, + char *(*get)(Object *, Error **), + void (*set)(Object *, const char *, + Error **), + Error **errp); + /** * object_property_add_bool: * @obj: the object to add a property to @@ -1343,6 +1398,11 @@ void object_property_add_bool(Object *obj, const char *name, void (*set)(Object *, bool, Error **), Error **errp); +void object_class_property_add_bool(ObjectClass *klass, const char *name, + bool (*get)(Object *, Error **), + void (*set)(Object *, bool, Error **), + Error **errp); + /** * object_property_add_enum: * @obj: the object to add a property to @@ -1362,6 +1422,13 @@ void object_property_add_enum(Object *obj, const char *name, void (*set)(Object *, int, Error **), Error **errp); +void object_class_property_add_enum(ObjectClass *klass, const char *name, + const char *typename, + const char * const *strings, + int (*get)(Object *, Error **), + void (*set)(Object *, int, Error **), + Error **errp); + /** * object_property_add_tm: * @obj: the object to add a property to @@ -1376,6 +1443,10 @@ void object_property_add_tm(Object *obj, const char *name, void (*get)(Object *, struct tm *, Error **), Error **errp); +void object_class_property_add_tm(ObjectClass *klass, const char *name, + void (*get)(Object *, struct tm *, Error **), + Error **errp); + /** * object_property_add_uint8_ptr: * @obj: the object to add a property to @@ -1388,6 +1459,8 @@ void object_property_add_tm(Object *obj, const char *name, */ void object_property_add_uint8_ptr(Object *obj, const char *name, const uint8_t *v, Error **errp); +void object_class_property_add_uint8_ptr(ObjectClass *klass, const char *name, + const uint8_t *v, Error **errp); /** * object_property_add_uint16_ptr: @@ -1401,6 +1474,8 @@ void object_property_add_uint8_ptr(Object *obj, const char *name, */ void object_property_add_uint16_ptr(Object *obj, const char *name, const uint16_t *v, Error **errp); +void object_class_property_add_uint16_ptr(ObjectClass *klass, const char *name, + const uint16_t *v, Error **errp); /** * object_property_add_uint32_ptr: @@ -1414,6 +1489,8 @@ void object_property_add_uint16_ptr(Object *obj, const char *name, */ void object_property_add_uint32_ptr(Object *obj, const char *name, const uint32_t *v, Error **errp); +void object_class_property_add_uint32_ptr(ObjectClass *klass, const char *name, + const uint32_t *v, Error **errp); /** * object_property_add_uint64_ptr: @@ -1427,6 +1504,8 @@ void object_property_add_uint32_ptr(Object *obj, const char *name, */ void object_property_add_uint64_ptr(Object *obj, const char *name, const uint64_t *v, Error **Errp); +void object_class_property_add_uint64_ptr(ObjectClass *klass, const char *name, + const uint64_t *v, Error **Errp); /** * object_property_add_alias: @@ -1478,6 +1557,9 @@ void object_property_add_const_link(Object *obj, const char *name, */ void object_property_set_description(Object *obj, const char *name, const char *description, Error **errp); +void object_class_property_set_description(ObjectClass *klass, const char *name, + const char *description, + Error **errp); /** * object_child_foreach: @@ -1488,11 +1570,32 @@ void object_property_set_description(Object *obj, const char *name, * Call @fn passing each child of @obj and @opaque to it, until @fn returns * non-zero. * + * It is forbidden to add or remove children from @obj from the @fn + * callback. + * * Returns: The last value returned by @fn, or 0 if there is no child. */ int object_child_foreach(Object *obj, int (*fn)(Object *child, void *opaque), void *opaque); +/** + * object_child_foreach_recursive: + * @obj: the object whose children will be navigated + * @fn: the iterator function to be called + * @opaque: an opaque value that will be passed to the iterator + * + * Call @fn passing each child of @obj and @opaque to it, until @fn returns + * non-zero. Calls recursively, all child nodes of @obj will also be passed + * all the way down to the leaf nodes of the tree. Depth first ordering. + * + * It is forbidden to add or remove children from @obj (or its + * child nodes) from the @fn callback. + * + * Returns: The last value returned by @fn, or 0 if there is no child. + */ +int object_child_foreach_recursive(Object *obj, + int (*fn)(Object *child, void *opaque), + void *opaque); /** * container_get: * @root: root of the #path, e.g., object_get_root() diff --git a/qemu/include/qom/object_interfaces.h b/qemu/include/qom/object_interfaces.h index 283ae0db4..8b17f4def 100644 --- a/qemu/include/qom/object_interfaces.h +++ b/qemu/include/qom/object_interfaces.h @@ -2,6 +2,8 @@ #define OBJECT_INTERFACES_H #include "qom/object.h" +#include "qapi/qmp/qdict.h" +#include "qapi/visitor.h" #define TYPE_USER_CREATABLE "user-creatable" @@ -72,4 +74,95 @@ void user_creatable_complete(Object *obj, Error **errp); * from implements USER_CREATABLE interface. */ bool user_creatable_can_be_deleted(UserCreatable *uc, Error **errp); + +/** + * user_creatable_add: + * @qdict: the object definition + * @v: the visitor + * @errp: if an error occurs, a pointer to an area to store the error + * + * Create an instance of the user creatable object whose type + * is defined in @qdict by the 'qom-type' field, placing it + * in the object composition tree with name provided by the + * 'id' field. The remaining fields in @qdict are used to + * initialize the object properties. + * + * Returns: the newly created object or NULL on error + */ +Object *user_creatable_add(const QDict *qdict, + Visitor *v, Error **errp); + +/** + * user_creatable_add_type: + * @type: the object type name + * @id: the unique ID for the object + * @qdict: the object properties + * @v: the visitor + * @errp: if an error occurs, a pointer to an area to store the error + * + * Create an instance of the user creatable object @type, placing + * it in the object composition tree with name @id, initializing + * it with properties from @qdict + * + * Returns: the newly created object or NULL on error + */ +Object *user_creatable_add_type(const char *type, const char *id, + const QDict *qdict, + Visitor *v, Error **errp); + +/** + * user_creatable_add_opts: + * @opts: the object definition + * @errp: if an error occurs, a pointer to an area to store the error + * + * Create an instance of the user creatable object whose type + * is defined in @opts by the 'qom-type' option, placing it + * in the object composition tree with name provided by the + * 'id' field. The remaining options in @opts are used to + * initialize the object properties. + * + * Returns: the newly created object or NULL on error + */ +Object *user_creatable_add_opts(QemuOpts *opts, Error **errp); + + +/** + * user_creatable_add_opts_predicate: + * @type: the QOM type to be added + * + * A callback function to determine whether an object + * of type @type should be created. Instances of this + * callback should be passed to user_creatable_add_opts_foreach + */ +typedef bool (*user_creatable_add_opts_predicate)(const char *type); + +/** + * user_creatable_add_opts_foreach: + * @opaque: a user_creatable_add_opts_predicate callback or NULL + * @opts: options to create + * @errp: unused + * + * An iterator callback to be used in conjunction with + * the qemu_opts_foreach() method for creating a list of + * objects from a set of QemuOpts + * + * The @opaque parameter can be passed a user_creatable_add_opts_predicate + * callback to filter which types of object are created during iteration. + * When it fails, report the error. + * + * Returns: 0 on success, -1 when an error was reported. + */ +int user_creatable_add_opts_foreach(void *opaque, + QemuOpts *opts, Error **errp); + +/** + * user_creatable_del: + * @id: the unique ID for the object + * @errp: if an error occurs, a pointer to an area to store the error + * + * Delete an instance of the user creatable object identified + * by @id. + */ +void user_creatable_del(const char *id, Error **errp); + #endif diff --git a/qemu/include/standard-headers/asm-x86/hyperv.h b/qemu/include/standard-headers/asm-x86/hyperv.h new file mode 100644 index 000000000..47b38fb81 --- /dev/null +++ b/qemu/include/standard-headers/asm-x86/hyperv.h @@ -0,0 +1,366 @@ +#ifndef _ASM_X86_HYPERV_H +#define _ASM_X86_HYPERV_H + +#include "standard-headers/linux/types.h" + +/* + * The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent + * is set by CPUID(HvCpuIdFunctionVersionAndFeatures). + */ +#define HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS 0x40000000 +#define HYPERV_CPUID_INTERFACE 0x40000001 +#define HYPERV_CPUID_VERSION 0x40000002 +#define HYPERV_CPUID_FEATURES 0x40000003 +#define HYPERV_CPUID_ENLIGHTMENT_INFO 0x40000004 +#define HYPERV_CPUID_IMPLEMENT_LIMITS 0x40000005 + +#define HYPERV_HYPERVISOR_PRESENT_BIT 0x80000000 +#define HYPERV_CPUID_MIN 0x40000005 +#define HYPERV_CPUID_MAX 0x4000ffff + +/* + * Feature identification. EAX indicates which features are available + * to the partition based upon the current partition privileges. + */ + +/* VP Runtime (HV_X64_MSR_VP_RUNTIME) available */ +#define HV_X64_MSR_VP_RUNTIME_AVAILABLE (1 << 0) +/* Partition Reference Counter (HV_X64_MSR_TIME_REF_COUNT) available*/ +#define HV_X64_MSR_TIME_REF_COUNT_AVAILABLE (1 << 1) +/* Partition reference TSC MSR is available */ +#define HV_X64_MSR_REFERENCE_TSC_AVAILABLE (1 << 9) + +/* A partition's reference time stamp counter (TSC) page */ +#define HV_X64_MSR_REFERENCE_TSC 0x40000021 + +/* + * There is a single feature flag that signifies the presence of the MSR + * that can be used to retrieve both the local APIC Timer frequency as + * well as the TSC frequency. + */ + +/* Local APIC timer frequency MSR (HV_X64_MSR_APIC_FREQUENCY) is available */ +#define HV_X64_MSR_APIC_FREQUENCY_AVAILABLE (1 << 11) + +/* TSC frequency MSR (HV_X64_MSR_TSC_FREQUENCY) is available */ +#define HV_X64_MSR_TSC_FREQUENCY_AVAILABLE (1 << 11) + +/* + * Basic SynIC MSRs (HV_X64_MSR_SCONTROL through HV_X64_MSR_EOM + * and HV_X64_MSR_SINT0 through HV_X64_MSR_SINT15) available + */ +#define HV_X64_MSR_SYNIC_AVAILABLE (1 << 2) +/* + * Synthetic Timer MSRs (HV_X64_MSR_STIMER0_CONFIG through + * HV_X64_MSR_STIMER3_COUNT) available + */ +#define HV_X64_MSR_SYNTIMER_AVAILABLE (1 << 3) +/* + * APIC access MSRs (HV_X64_MSR_EOI, HV_X64_MSR_ICR and HV_X64_MSR_TPR) + * are available + */ +#define HV_X64_MSR_APIC_ACCESS_AVAILABLE (1 << 4) +/* Hypercall MSRs (HV_X64_MSR_GUEST_OS_ID and HV_X64_MSR_HYPERCALL) available*/ +#define HV_X64_MSR_HYPERCALL_AVAILABLE (1 << 5) +/* Access virtual processor index MSR (HV_X64_MSR_VP_INDEX) available*/ +#define HV_X64_MSR_VP_INDEX_AVAILABLE (1 << 6) +/* Virtual system reset MSR (HV_X64_MSR_RESET) is available*/ +#define HV_X64_MSR_RESET_AVAILABLE (1 << 7) + /* + * Access statistics pages MSRs (HV_X64_MSR_STATS_PARTITION_RETAIL_PAGE, + * HV_X64_MSR_STATS_PARTITION_INTERNAL_PAGE, HV_X64_MSR_STATS_VP_RETAIL_PAGE, + * HV_X64_MSR_STATS_VP_INTERNAL_PAGE) available + */ +#define HV_X64_MSR_STAT_PAGES_AVAILABLE (1 << 8) + +/* + * Feature identification: EBX indicates which flags were specified at + * partition creation. The format is the same as the partition creation + * flag structure defined in section Partition Creation Flags. + */ +#define HV_X64_CREATE_PARTITIONS (1 << 0) +#define HV_X64_ACCESS_PARTITION_ID (1 << 1) +#define HV_X64_ACCESS_MEMORY_POOL (1 << 2) +#define HV_X64_ADJUST_MESSAGE_BUFFERS (1 << 3) +#define HV_X64_POST_MESSAGES (1 << 4) +#define HV_X64_SIGNAL_EVENTS (1 << 5) +#define HV_X64_CREATE_PORT (1 << 6) +#define HV_X64_CONNECT_PORT (1 << 7) +#define HV_X64_ACCESS_STATS (1 << 8) +#define HV_X64_DEBUGGING (1 << 11) +#define HV_X64_CPU_POWER_MANAGEMENT (1 << 12) +#define HV_X64_CONFIGURE_PROFILER (1 << 13) + +/* + * Feature identification. EDX indicates which miscellaneous features + * are available to the partition. + */ +/* The MWAIT instruction is available (per section MONITOR / MWAIT) */ +#define HV_X64_MWAIT_AVAILABLE (1 << 0) +/* Guest debugging support is available */ +#define HV_X64_GUEST_DEBUGGING_AVAILABLE (1 << 1) +/* Performance Monitor support is available*/ +#define HV_X64_PERF_MONITOR_AVAILABLE (1 << 2) +/* Support for physical CPU dynamic partitioning events is available*/ +#define HV_X64_CPU_DYNAMIC_PARTITIONING_AVAILABLE (1 << 3) +/* + * Support for passing hypercall input parameter block via XMM + * registers is available + */ +#define HV_X64_HYPERCALL_PARAMS_XMM_AVAILABLE (1 << 4) +/* Support for a virtual guest idle state is available */ +#define HV_X64_GUEST_IDLE_STATE_AVAILABLE (1 << 5) +/* Guest crash data handler available */ +#define HV_X64_GUEST_CRASH_MSR_AVAILABLE (1 << 10) + +/* + * Implementation recommendations. Indicates which behaviors the hypervisor + * recommends the OS implement for optimal performance. + */ + /* + * Recommend using hypercall for address space switches rather + * than MOV to CR3 instruction + */ +#define HV_X64_MWAIT_RECOMMENDED (1 << 0) +/* Recommend using hypercall for local TLB flushes rather + * than INVLPG or MOV to CR3 instructions */ +#define HV_X64_LOCAL_TLB_FLUSH_RECOMMENDED (1 << 1) +/* + * Recommend using hypercall for remote TLB flushes rather + * than inter-processor interrupts + */ +#define HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED (1 << 2) +/* + * Recommend using MSRs for accessing APIC registers + * EOI, ICR and TPR rather than their memory-mapped counterparts + */ +#define HV_X64_APIC_ACCESS_RECOMMENDED (1 << 3) +/* Recommend using the hypervisor-provided MSR to initiate a system RESET */ +#define HV_X64_SYSTEM_RESET_RECOMMENDED (1 << 4) +/* + * Recommend using relaxed timing for this partition. If used, + * the VM should disable any watchdog timeouts that rely on the + * timely delivery of external interrupts + */ +#define HV_X64_RELAXED_TIMING_RECOMMENDED (1 << 5) + +/* MSR used to identify the guest OS. */ +#define HV_X64_MSR_GUEST_OS_ID 0x40000000 + +/* MSR used to setup pages used to communicate with the hypervisor. */ +#define HV_X64_MSR_HYPERCALL 0x40000001 + +/* MSR used to provide vcpu index */ +#define HV_X64_MSR_VP_INDEX 0x40000002 + +/* MSR used to reset the guest OS. */ +#define HV_X64_MSR_RESET 0x40000003 + +/* MSR used to provide vcpu runtime in 100ns units */ +#define HV_X64_MSR_VP_RUNTIME 0x40000010 + +/* MSR used to read the per-partition time reference counter */ +#define HV_X64_MSR_TIME_REF_COUNT 0x40000020 + +/* MSR used to retrieve the TSC frequency */ +#define HV_X64_MSR_TSC_FREQUENCY 0x40000022 + +/* MSR used to retrieve the local APIC timer frequency */ +#define HV_X64_MSR_APIC_FREQUENCY 0x40000023 + +/* Define the virtual APIC registers */ +#define HV_X64_MSR_EOI 0x40000070 +#define HV_X64_MSR_ICR 0x40000071 +#define HV_X64_MSR_TPR 0x40000072 +#define HV_X64_MSR_APIC_ASSIST_PAGE 0x40000073 + +/* Define synthetic interrupt controller model specific registers. */ +#define HV_X64_MSR_SCONTROL 0x40000080 +#define HV_X64_MSR_SVERSION 0x40000081 +#define HV_X64_MSR_SIEFP 0x40000082 +#define HV_X64_MSR_SIMP 0x40000083 +#define HV_X64_MSR_EOM 0x40000084 +#define HV_X64_MSR_SINT0 0x40000090 +#define HV_X64_MSR_SINT1 0x40000091 +#define HV_X64_MSR_SINT2 0x40000092 +#define HV_X64_MSR_SINT3 0x40000093 +#define HV_X64_MSR_SINT4 0x40000094 +#define HV_X64_MSR_SINT5 0x40000095 +#define HV_X64_MSR_SINT6 0x40000096 +#define HV_X64_MSR_SINT7 0x40000097 +#define HV_X64_MSR_SINT8 0x40000098 +#define HV_X64_MSR_SINT9 0x40000099 +#define HV_X64_MSR_SINT10 0x4000009A +#define HV_X64_MSR_SINT11 0x4000009B +#define HV_X64_MSR_SINT12 0x4000009C +#define HV_X64_MSR_SINT13 0x4000009D +#define HV_X64_MSR_SINT14 0x4000009E +#define HV_X64_MSR_SINT15 0x4000009F + +/* + * Synthetic Timer MSRs. Four timers per vcpu. + */ +#define HV_X64_MSR_STIMER0_CONFIG 0x400000B0 +#define HV_X64_MSR_STIMER0_COUNT 0x400000B1 +#define HV_X64_MSR_STIMER1_CONFIG 0x400000B2 +#define HV_X64_MSR_STIMER1_COUNT 0x400000B3 +#define HV_X64_MSR_STIMER2_CONFIG 0x400000B4 +#define HV_X64_MSR_STIMER2_COUNT 0x400000B5 +#define HV_X64_MSR_STIMER3_CONFIG 0x400000B6 +#define HV_X64_MSR_STIMER3_COUNT 0x400000B7 + +/* Hyper-V guest crash notification MSR's */ +#define HV_X64_MSR_CRASH_P0 0x40000100 +#define HV_X64_MSR_CRASH_P1 0x40000101 +#define HV_X64_MSR_CRASH_P2 0x40000102 +#define HV_X64_MSR_CRASH_P3 0x40000103 +#define HV_X64_MSR_CRASH_P4 0x40000104 +#define HV_X64_MSR_CRASH_CTL 0x40000105 +#define HV_X64_MSR_CRASH_CTL_NOTIFY (1ULL << 63) +#define HV_X64_MSR_CRASH_PARAMS \ + (1 + (HV_X64_MSR_CRASH_P4 - HV_X64_MSR_CRASH_P0)) + +#define HV_X64_MSR_HYPERCALL_ENABLE 0x00000001 +#define HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT 12 +#define HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_MASK \ + (~((1ull << HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT) - 1)) + +/* Declare the various hypercall operations. */ +#define HVCALL_NOTIFY_LONG_SPIN_WAIT 0x0008 +#define HVCALL_POST_MESSAGE 0x005c +#define HVCALL_SIGNAL_EVENT 0x005d + +#define HV_X64_MSR_APIC_ASSIST_PAGE_ENABLE 0x00000001 +#define HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT 12 +#define HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_MASK \ + (~((1ull << HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT) - 1)) + +#define HV_X64_MSR_TSC_REFERENCE_ENABLE 0x00000001 +#define HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT 12 + +#define HV_PROCESSOR_POWER_STATE_C0 0 +#define HV_PROCESSOR_POWER_STATE_C1 1 +#define HV_PROCESSOR_POWER_STATE_C2 2 +#define HV_PROCESSOR_POWER_STATE_C3 3 + +/* hypercall status code */ +#define HV_STATUS_SUCCESS 0 +#define HV_STATUS_INVALID_HYPERCALL_CODE 2 +#define HV_STATUS_INVALID_HYPERCALL_INPUT 3 +#define HV_STATUS_INVALID_ALIGNMENT 4 +#define HV_STATUS_INSUFFICIENT_MEMORY 11 +#define HV_STATUS_INVALID_CONNECTION_ID 18 +#define HV_STATUS_INSUFFICIENT_BUFFERS 19 + +typedef struct _HV_REFERENCE_TSC_PAGE { + uint32_t tsc_sequence; + uint32_t res1; + uint64_t tsc_scale; + int64_t tsc_offset; +} HV_REFERENCE_TSC_PAGE, *PHV_REFERENCE_TSC_PAGE; + +/* Define the number of synthetic interrupt sources. */ +#define HV_SYNIC_SINT_COUNT (16) +/* Define the expected SynIC version. */ +#define HV_SYNIC_VERSION_1 (0x1) + +#define HV_SYNIC_CONTROL_ENABLE (1ULL << 0) +#define HV_SYNIC_SIMP_ENABLE (1ULL << 0) +#define HV_SYNIC_SIEFP_ENABLE (1ULL << 0) +#define HV_SYNIC_SINT_MASKED (1ULL << 16) +#define HV_SYNIC_SINT_AUTO_EOI (1ULL << 17) +#define HV_SYNIC_SINT_VECTOR_MASK (0xFF) + +#define HV_SYNIC_STIMER_COUNT (4) + +/* Define synthetic interrupt controller message constants. */ +#define HV_MESSAGE_SIZE (256) +#define HV_MESSAGE_PAYLOAD_BYTE_COUNT (240) +#define HV_MESSAGE_PAYLOAD_QWORD_COUNT (30) + +/* Define hypervisor message types. */ +enum hv_message_type { + HVMSG_NONE = 0x00000000, + + /* Memory access messages. */ + HVMSG_UNMAPPED_GPA = 0x80000000, + HVMSG_GPA_INTERCEPT = 0x80000001, + + /* Timer notification messages. */ + HVMSG_TIMER_EXPIRED = 0x80000010, + + /* Error messages. */ + HVMSG_INVALID_VP_REGISTER_VALUE = 0x80000020, + HVMSG_UNRECOVERABLE_EXCEPTION = 0x80000021, + HVMSG_UNSUPPORTED_FEATURE = 0x80000022, + + /* Trace buffer complete messages. */ + HVMSG_EVENTLOG_BUFFERCOMPLETE = 0x80000040, + + /* Platform-specific processor intercept messages. */ + HVMSG_X64_IOPORT_INTERCEPT = 0x80010000, + HVMSG_X64_MSR_INTERCEPT = 0x80010001, + HVMSG_X64_CPUID_INTERCEPT = 0x80010002, + HVMSG_X64_EXCEPTION_INTERCEPT = 0x80010003, + HVMSG_X64_APIC_EOI = 0x80010004, + HVMSG_X64_LEGACY_FP_ERROR = 0x80010005 +}; + +/* Define synthetic interrupt controller message flags. */ +union hv_message_flags { + uint8_t asu8; + struct { + uint8_t msg_pending:1; + uint8_t reserved:7; + }; +}; + +/* Define port identifier type. */ +union hv_port_id { + uint32_t asu32; + struct { + uint32_t id:24; + uint32_t reserved:8; + } u; +}; + +/* Define synthetic interrupt controller message header. */ +struct hv_message_header { + uint32_t message_type; + uint8_t payload_size; + union hv_message_flags message_flags; + uint8_t reserved[2]; + union { + uint64_t sender; + union hv_port_id port; + }; +}; + +/* Define synthetic interrupt controller message format. */ +struct hv_message { + struct hv_message_header header; + union { + uint64_t payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; + } u; +}; + +/* Define the synthetic interrupt message page layout. */ +struct hv_message_page { + struct hv_message sint_message[HV_SYNIC_SINT_COUNT]; +}; + +/* Define timer message payload structure. */ +struct hv_timer_message_payload { + uint32_t timer_index; + uint32_t reserved; + uint64_t expiration_time; /* When the timer expired */ + uint64_t delivery_time; /* When the message was delivered */ +}; + +#define HV_STIMER_ENABLE (1ULL << 0) +#define HV_STIMER_PERIODIC (1ULL << 1) +#define HV_STIMER_LAZY (1ULL << 2) +#define HV_STIMER_AUTOENABLE (1ULL << 3) +#define HV_STIMER_SINT(config) (uint8_t)(((config) >> 16) & 0x0F) + +#endif diff --git a/qemu/include/standard-headers/linux/input-event-codes.h b/qemu/include/standard-headers/linux/input-event-codes.h new file mode 100644 index 000000000..354f0decf --- /dev/null +++ b/qemu/include/standard-headers/linux/input-event-codes.h @@ -0,0 +1,805 @@ +/* + * Input event codes + * + * *** IMPORTANT *** + * This file is not only included from C-code but also from devicetree source + * files. As such this file MUST only contain comments and defines. + * + * Copyright (c) 1999-2002 Vojtech Pavlik + * Copyright (c) 2015 Hans de Goede + * + * 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 _INPUT_EVENT_CODES_H +#define _INPUT_EVENT_CODES_H + +/* + * Device properties and quirks + */ + +#define INPUT_PROP_POINTER 0x00 /* needs a pointer */ +#define INPUT_PROP_DIRECT 0x01 /* direct input devices */ +#define INPUT_PROP_BUTTONPAD 0x02 /* has button(s) under pad */ +#define INPUT_PROP_SEMI_MT 0x03 /* touch rectangle only */ +#define INPUT_PROP_TOPBUTTONPAD 0x04 /* softbuttons at top of pad */ +#define INPUT_PROP_POINTING_STICK 0x05 /* is a pointing stick */ +#define INPUT_PROP_ACCELEROMETER 0x06 /* has accelerometer */ + +#define INPUT_PROP_MAX 0x1f +#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1) + +/* + * Event types + */ + +#define EV_SYN 0x00 +#define EV_KEY 0x01 +#define EV_REL 0x02 +#define EV_ABS 0x03 +#define EV_MSC 0x04 +#define EV_SW 0x05 +#define EV_LED 0x11 +#define EV_SND 0x12 +#define EV_REP 0x14 +#define EV_FF 0x15 +#define EV_PWR 0x16 +#define EV_FF_STATUS 0x17 +#define EV_MAX 0x1f +#define EV_CNT (EV_MAX+1) + +/* + * Synchronization events. + */ + +#define SYN_REPORT 0 +#define SYN_CONFIG 1 +#define SYN_MT_REPORT 2 +#define SYN_DROPPED 3 +#define SYN_MAX 0xf +#define SYN_CNT (SYN_MAX+1) + +/* + * Keys and buttons + * + * Most of the keys/buttons are modeled after USB HUT 1.12 + * (see http://www.usb.org/developers/hidpage). + * Abbreviations in the comments: + * AC - Application Control + * AL - Application Launch Button + * SC - System Control + */ + +#define KEY_RESERVED 0 +#define KEY_ESC 1 +#define KEY_1 2 +#define KEY_2 3 +#define KEY_3 4 +#define KEY_4 5 +#define KEY_5 6 +#define KEY_6 7 +#define KEY_7 8 +#define KEY_8 9 +#define KEY_9 10 +#define KEY_0 11 +#define KEY_MINUS 12 +#define KEY_EQUAL 13 +#define KEY_BACKSPACE 14 +#define KEY_TAB 15 +#define KEY_Q 16 +#define KEY_W 17 +#define KEY_E 18 +#define KEY_R 19 +#define KEY_T 20 +#define KEY_Y 21 +#define KEY_U 22 +#define KEY_I 23 +#define KEY_O 24 +#define KEY_P 25 +#define KEY_LEFTBRACE 26 +#define KEY_RIGHTBRACE 27 +#define KEY_ENTER 28 +#define KEY_LEFTCTRL 29 +#define KEY_A 30 +#define KEY_S 31 +#define KEY_D 32 +#define KEY_F 33 +#define KEY_G 34 +#define KEY_H 35 +#define KEY_J 36 +#define KEY_K 37 +#define KEY_L 38 +#define KEY_SEMICOLON 39 +#define KEY_APOSTROPHE 40 +#define KEY_GRAVE 41 +#define KEY_LEFTSHIFT 42 +#define KEY_BACKSLASH 43 +#define KEY_Z 44 +#define KEY_X 45 +#define KEY_C 46 +#define KEY_V 47 +#define KEY_B 48 +#define KEY_N 49 +#define KEY_M 50 +#define KEY_COMMA 51 +#define KEY_DOT 52 +#define KEY_SLASH 53 +#define KEY_RIGHTSHIFT 54 +#define KEY_KPASTERISK 55 +#define KEY_LEFTALT 56 +#define KEY_SPACE 57 +#define KEY_CAPSLOCK 58 +#define KEY_F1 59 +#define KEY_F2 60 +#define KEY_F3 61 +#define KEY_F4 62 +#define KEY_F5 63 +#define KEY_F6 64 +#define KEY_F7 65 +#define KEY_F8 66 +#define KEY_F9 67 +#define KEY_F10 68 +#define KEY_NUMLOCK 69 +#define KEY_SCROLLLOCK 70 +#define KEY_KP7 71 +#define KEY_KP8 72 +#define KEY_KP9 73 +#define KEY_KPMINUS 74 +#define KEY_KP4 75 +#define KEY_KP5 76 +#define KEY_KP6 77 +#define KEY_KPPLUS 78 +#define KEY_KP1 79 +#define KEY_KP2 80 +#define KEY_KP3 81 +#define KEY_KP0 82 +#define KEY_KPDOT 83 + +#define KEY_ZENKAKUHANKAKU 85 +#define KEY_102ND 86 +#define KEY_F11 87 +#define KEY_F12 88 +#define KEY_RO 89 +#define KEY_KATAKANA 90 +#define KEY_HIRAGANA 91 +#define KEY_HENKAN 92 +#define KEY_KATAKANAHIRAGANA 93 +#define KEY_MUHENKAN 94 +#define KEY_KPJPCOMMA 95 +#define KEY_KPENTER 96 +#define KEY_RIGHTCTRL 97 +#define KEY_KPSLASH 98 +#define KEY_SYSRQ 99 +#define KEY_RIGHTALT 100 +#define KEY_LINEFEED 101 +#define KEY_HOME 102 +#define KEY_UP 103 +#define KEY_PAGEUP 104 +#define KEY_LEFT 105 +#define KEY_RIGHT 106 +#define KEY_END 107 +#define KEY_DOWN 108 +#define KEY_PAGEDOWN 109 +#define KEY_INSERT 110 +#define KEY_DELETE 111 +#define KEY_MACRO 112 +#define KEY_MUTE 113 +#define KEY_VOLUMEDOWN 114 +#define KEY_VOLUMEUP 115 +#define KEY_POWER 116 /* SC System Power Down */ +#define KEY_KPEQUAL 117 +#define KEY_KPPLUSMINUS 118 +#define KEY_PAUSE 119 +#define KEY_SCALE 120 /* AL Compiz Scale (Expose) */ + +#define KEY_KPCOMMA 121 +#define KEY_HANGEUL 122 +#define KEY_HANGUEL KEY_HANGEUL +#define KEY_HANJA 123 +#define KEY_YEN 124 +#define KEY_LEFTMETA 125 +#define KEY_RIGHTMETA 126 +#define KEY_COMPOSE 127 + +#define KEY_STOP 128 /* AC Stop */ +#define KEY_AGAIN 129 +#define KEY_PROPS 130 /* AC Properties */ +#define KEY_UNDO 131 /* AC Undo */ +#define KEY_FRONT 132 +#define KEY_COPY 133 /* AC Copy */ +#define KEY_OPEN 134 /* AC Open */ +#define KEY_PASTE 135 /* AC Paste */ +#define KEY_FIND 136 /* AC Search */ +#define KEY_CUT 137 /* AC Cut */ +#define KEY_HELP 138 /* AL Integrated Help Center */ +#define KEY_MENU 139 /* Menu (show menu) */ +#define KEY_CALC 140 /* AL Calculator */ +#define KEY_SETUP 141 +#define KEY_SLEEP 142 /* SC System Sleep */ +#define KEY_WAKEUP 143 /* System Wake Up */ +#define KEY_FILE 144 /* AL Local Machine Browser */ +#define KEY_SENDFILE 145 +#define KEY_DELETEFILE 146 +#define KEY_XFER 147 +#define KEY_PROG1 148 +#define KEY_PROG2 149 +#define KEY_WWW 150 /* AL Internet Browser */ +#define KEY_MSDOS 151 +#define KEY_COFFEE 152 /* AL Terminal Lock/Screensaver */ +#define KEY_SCREENLOCK KEY_COFFEE +#define KEY_ROTATE_DISPLAY 153 /* Display orientation for e.g. tablets */ +#define KEY_DIRECTION KEY_ROTATE_DISPLAY +#define KEY_CYCLEWINDOWS 154 +#define KEY_MAIL 155 +#define KEY_BOOKMARKS 156 /* AC Bookmarks */ +#define KEY_COMPUTER 157 +#define KEY_BACK 158 /* AC Back */ +#define KEY_FORWARD 159 /* AC Forward */ +#define KEY_CLOSECD 160 +#define KEY_EJECTCD 161 +#define KEY_EJECTCLOSECD 162 +#define KEY_NEXTSONG 163 +#define KEY_PLAYPAUSE 164 +#define KEY_PREVIOUSSONG 165 +#define KEY_STOPCD 166 +#define KEY_RECORD 167 +#define KEY_REWIND 168 +#define KEY_PHONE 169 /* Media Select Telephone */ +#define KEY_ISO 170 +#define KEY_CONFIG 171 /* AL Consumer Control Configuration */ +#define KEY_HOMEPAGE 172 /* AC Home */ +#define KEY_REFRESH 173 /* AC Refresh */ +#define KEY_EXIT 174 /* AC Exit */ +#define KEY_MOVE 175 +#define KEY_EDIT 176 +#define KEY_SCROLLUP 177 +#define KEY_SCROLLDOWN 178 +#define KEY_KPLEFTPAREN 179 +#define KEY_KPRIGHTPAREN 180 +#define KEY_NEW 181 /* AC New */ +#define KEY_REDO 182 /* AC Redo/Repeat */ + +#define KEY_F13 183 +#define KEY_F14 184 +#define KEY_F15 185 +#define KEY_F16 186 +#define KEY_F17 187 +#define KEY_F18 188 +#define KEY_F19 189 +#define KEY_F20 190 +#define KEY_F21 191 +#define KEY_F22 192 +#define KEY_F23 193 +#define KEY_F24 194 + +#define KEY_PLAYCD 200 +#define KEY_PAUSECD 201 +#define KEY_PROG3 202 +#define KEY_PROG4 203 +#define KEY_DASHBOARD 204 /* AL Dashboard */ +#define KEY_SUSPEND 205 +#define KEY_CLOSE 206 /* AC Close */ +#define KEY_PLAY 207 +#define KEY_FASTFORWARD 208 +#define KEY_BASSBOOST 209 +#define KEY_PRINT 210 /* AC Print */ +#define KEY_HP 211 +#define KEY_CAMERA 212 +#define KEY_SOUND 213 +#define KEY_QUESTION 214 +#define KEY_EMAIL 215 +#define KEY_CHAT 216 +#define KEY_SEARCH 217 +#define KEY_CONNECT 218 +#define KEY_FINANCE 219 /* AL Checkbook/Finance */ +#define KEY_SPORT 220 +#define KEY_SHOP 221 +#define KEY_ALTERASE 222 +#define KEY_CANCEL 223 /* AC Cancel */ +#define KEY_BRIGHTNESSDOWN 224 +#define KEY_BRIGHTNESSUP 225 +#define KEY_MEDIA 226 + +#define KEY_SWITCHVIDEOMODE 227 /* Cycle between available video + outputs (Monitor/LCD/TV-out/etc) */ +#define KEY_KBDILLUMTOGGLE 228 +#define KEY_KBDILLUMDOWN 229 +#define KEY_KBDILLUMUP 230 + +#define KEY_SEND 231 /* AC Send */ +#define KEY_REPLY 232 /* AC Reply */ +#define KEY_FORWARDMAIL 233 /* AC Forward Msg */ +#define KEY_SAVE 234 /* AC Save */ +#define KEY_DOCUMENTS 235 + +#define KEY_BATTERY 236 + +#define KEY_BLUETOOTH 237 +#define KEY_WLAN 238 +#define KEY_UWB 239 + +#define KEY_UNKNOWN 240 + +#define KEY_VIDEO_NEXT 241 /* drive next video source */ +#define KEY_VIDEO_PREV 242 /* drive previous video source */ +#define KEY_BRIGHTNESS_CYCLE 243 /* brightness up, after max is min */ +#define KEY_BRIGHTNESS_AUTO 244 /* Set Auto Brightness: manual + brightness control is off, + rely on ambient */ +#define KEY_BRIGHTNESS_ZERO KEY_BRIGHTNESS_AUTO +#define KEY_DISPLAY_OFF 245 /* display device to off state */ + +#define KEY_WWAN 246 /* Wireless WAN (LTE, UMTS, GSM, etc.) */ +#define KEY_WIMAX KEY_WWAN +#define KEY_RFKILL 247 /* Key that controls all radios */ + +#define KEY_MICMUTE 248 /* Mute / unmute the microphone */ + +/* Code 255 is reserved for special needs of AT keyboard driver */ + +#define BTN_MISC 0x100 +#define BTN_0 0x100 +#define BTN_1 0x101 +#define BTN_2 0x102 +#define BTN_3 0x103 +#define BTN_4 0x104 +#define BTN_5 0x105 +#define BTN_6 0x106 +#define BTN_7 0x107 +#define BTN_8 0x108 +#define BTN_9 0x109 + +#define BTN_MOUSE 0x110 +#define BTN_LEFT 0x110 +#define BTN_RIGHT 0x111 +#define BTN_MIDDLE 0x112 +#define BTN_SIDE 0x113 +#define BTN_EXTRA 0x114 +#define BTN_FORWARD 0x115 +#define BTN_BACK 0x116 +#define BTN_TASK 0x117 + +#define BTN_JOYSTICK 0x120 +#define BTN_TRIGGER 0x120 +#define BTN_THUMB 0x121 +#define BTN_THUMB2 0x122 +#define BTN_TOP 0x123 +#define BTN_TOP2 0x124 +#define BTN_PINKIE 0x125 +#define BTN_BASE 0x126 +#define BTN_BASE2 0x127 +#define BTN_BASE3 0x128 +#define BTN_BASE4 0x129 +#define BTN_BASE5 0x12a +#define BTN_BASE6 0x12b +#define BTN_DEAD 0x12f + +#define BTN_GAMEPAD 0x130 +#define BTN_SOUTH 0x130 +#define BTN_A BTN_SOUTH +#define BTN_EAST 0x131 +#define BTN_B BTN_EAST +#define BTN_C 0x132 +#define BTN_NORTH 0x133 +#define BTN_X BTN_NORTH +#define BTN_WEST 0x134 +#define BTN_Y BTN_WEST +#define BTN_Z 0x135 +#define BTN_TL 0x136 +#define BTN_TR 0x137 +#define BTN_TL2 0x138 +#define BTN_TR2 0x139 +#define BTN_SELECT 0x13a +#define BTN_START 0x13b +#define BTN_MODE 0x13c +#define BTN_THUMBL 0x13d +#define BTN_THUMBR 0x13e + +#define BTN_DIGI 0x140 +#define BTN_TOOL_PEN 0x140 +#define BTN_TOOL_RUBBER 0x141 +#define BTN_TOOL_BRUSH 0x142 +#define BTN_TOOL_PENCIL 0x143 +#define BTN_TOOL_AIRBRUSH 0x144 +#define BTN_TOOL_FINGER 0x145 +#define BTN_TOOL_MOUSE 0x146 +#define BTN_TOOL_LENS 0x147 +#define BTN_TOOL_QUINTTAP 0x148 /* Five fingers on trackpad */ +#define BTN_TOUCH 0x14a +#define BTN_STYLUS 0x14b +#define BTN_STYLUS2 0x14c +#define BTN_TOOL_DOUBLETAP 0x14d +#define BTN_TOOL_TRIPLETAP 0x14e +#define BTN_TOOL_QUADTAP 0x14f /* Four fingers on trackpad */ + +#define BTN_WHEEL 0x150 +#define BTN_GEAR_DOWN 0x150 +#define BTN_GEAR_UP 0x151 + +#define KEY_OK 0x160 +#define KEY_SELECT 0x161 +#define KEY_GOTO 0x162 +#define KEY_CLEAR 0x163 +#define KEY_POWER2 0x164 +#define KEY_OPTION 0x165 +#define KEY_INFO 0x166 /* AL OEM Features/Tips/Tutorial */ +#define KEY_TIME 0x167 +#define KEY_VENDOR 0x168 +#define KEY_ARCHIVE 0x169 +#define KEY_PROGRAM 0x16a /* Media Select Program Guide */ +#define KEY_CHANNEL 0x16b +#define KEY_FAVORITES 0x16c +#define KEY_EPG 0x16d +#define KEY_PVR 0x16e /* Media Select Home */ +#define KEY_MHP 0x16f +#define KEY_LANGUAGE 0x170 +#define KEY_TITLE 0x171 +#define KEY_SUBTITLE 0x172 +#define KEY_ANGLE 0x173 +#define KEY_ZOOM 0x174 +#define KEY_MODE 0x175 +#define KEY_KEYBOARD 0x176 +#define KEY_SCREEN 0x177 +#define KEY_PC 0x178 /* Media Select Computer */ +#define KEY_TV 0x179 /* Media Select TV */ +#define KEY_TV2 0x17a /* Media Select Cable */ +#define KEY_VCR 0x17b /* Media Select VCR */ +#define KEY_VCR2 0x17c /* VCR Plus */ +#define KEY_SAT 0x17d /* Media Select Satellite */ +#define KEY_SAT2 0x17e +#define KEY_CD 0x17f /* Media Select CD */ +#define KEY_TAPE 0x180 /* Media Select Tape */ +#define KEY_RADIO 0x181 +#define KEY_TUNER 0x182 /* Media Select Tuner */ +#define KEY_PLAYER 0x183 +#define KEY_TEXT 0x184 +#define KEY_DVD 0x185 /* Media Select DVD */ +#define KEY_AUX 0x186 +#define KEY_MP3 0x187 +#define KEY_AUDIO 0x188 /* AL Audio Browser */ +#define KEY_VIDEO 0x189 /* AL Movie Browser */ +#define KEY_DIRECTORY 0x18a +#define KEY_LIST 0x18b +#define KEY_MEMO 0x18c /* Media Select Messages */ +#define KEY_CALENDAR 0x18d +#define KEY_RED 0x18e +#define KEY_GREEN 0x18f +#define KEY_YELLOW 0x190 +#define KEY_BLUE 0x191 +#define KEY_CHANNELUP 0x192 /* Channel Increment */ +#define KEY_CHANNELDOWN 0x193 /* Channel Decrement */ +#define KEY_FIRST 0x194 +#define KEY_LAST 0x195 /* Recall Last */ +#define KEY_AB 0x196 +#define KEY_NEXT 0x197 +#define KEY_RESTART 0x198 +#define KEY_SLOW 0x199 +#define KEY_SHUFFLE 0x19a +#define KEY_BREAK 0x19b +#define KEY_PREVIOUS 0x19c +#define KEY_DIGITS 0x19d +#define KEY_TEEN 0x19e +#define KEY_TWEN 0x19f +#define KEY_VIDEOPHONE 0x1a0 /* Media Select Video Phone */ +#define KEY_GAMES 0x1a1 /* Media Select Games */ +#define KEY_ZOOMIN 0x1a2 /* AC Zoom In */ +#define KEY_ZOOMOUT 0x1a3 /* AC Zoom Out */ +#define KEY_ZOOMRESET 0x1a4 /* AC Zoom */ +#define KEY_WORDPROCESSOR 0x1a5 /* AL Word Processor */ +#define KEY_EDITOR 0x1a6 /* AL Text Editor */ +#define KEY_SPREADSHEET 0x1a7 /* AL Spreadsheet */ +#define KEY_GRAPHICSEDITOR 0x1a8 /* AL Graphics Editor */ +#define KEY_PRESENTATION 0x1a9 /* AL Presentation App */ +#define KEY_DATABASE 0x1aa /* AL Database App */ +#define KEY_NEWS 0x1ab /* AL Newsreader */ +#define KEY_VOICEMAIL 0x1ac /* AL Voicemail */ +#define KEY_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */ +#define KEY_MESSENGER 0x1ae /* AL Instant Messaging */ +#define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */ +#define KEY_BRIGHTNESS_TOGGLE KEY_DISPLAYTOGGLE +#define KEY_SPELLCHECK 0x1b0 /* AL Spell Check */ +#define KEY_LOGOFF 0x1b1 /* AL Logoff */ + +#define KEY_DOLLAR 0x1b2 +#define KEY_EURO 0x1b3 + +#define KEY_FRAMEBACK 0x1b4 /* Consumer - transport controls */ +#define KEY_FRAMEFORWARD 0x1b5 +#define KEY_CONTEXT_MENU 0x1b6 /* GenDesc - system context menu */ +#define KEY_MEDIA_REPEAT 0x1b7 /* Consumer - transport control */ +#define KEY_10CHANNELSUP 0x1b8 /* 10 channels up (10+) */ +#define KEY_10CHANNELSDOWN 0x1b9 /* 10 channels down (10-) */ +#define KEY_IMAGES 0x1ba /* AL Image Browser */ + +#define KEY_DEL_EOL 0x1c0 +#define KEY_DEL_EOS 0x1c1 +#define KEY_INS_LINE 0x1c2 +#define KEY_DEL_LINE 0x1c3 + +#define KEY_FN 0x1d0 +#define KEY_FN_ESC 0x1d1 +#define KEY_FN_F1 0x1d2 +#define KEY_FN_F2 0x1d3 +#define KEY_FN_F3 0x1d4 +#define KEY_FN_F4 0x1d5 +#define KEY_FN_F5 0x1d6 +#define KEY_FN_F6 0x1d7 +#define KEY_FN_F7 0x1d8 +#define KEY_FN_F8 0x1d9 +#define KEY_FN_F9 0x1da +#define KEY_FN_F10 0x1db +#define KEY_FN_F11 0x1dc +#define KEY_FN_F12 0x1dd +#define KEY_FN_1 0x1de +#define KEY_FN_2 0x1df +#define KEY_FN_D 0x1e0 +#define KEY_FN_E 0x1e1 +#define KEY_FN_F 0x1e2 +#define KEY_FN_S 0x1e3 +#define KEY_FN_B 0x1e4 + +#define KEY_BRL_DOT1 0x1f1 +#define KEY_BRL_DOT2 0x1f2 +#define KEY_BRL_DOT3 0x1f3 +#define KEY_BRL_DOT4 0x1f4 +#define KEY_BRL_DOT5 0x1f5 +#define KEY_BRL_DOT6 0x1f6 +#define KEY_BRL_DOT7 0x1f7 +#define KEY_BRL_DOT8 0x1f8 +#define KEY_BRL_DOT9 0x1f9 +#define KEY_BRL_DOT10 0x1fa + +#define KEY_NUMERIC_0 0x200 /* used by phones, remote controls, */ +#define KEY_NUMERIC_1 0x201 /* and other keypads */ +#define KEY_NUMERIC_2 0x202 +#define KEY_NUMERIC_3 0x203 +#define KEY_NUMERIC_4 0x204 +#define KEY_NUMERIC_5 0x205 +#define KEY_NUMERIC_6 0x206 +#define KEY_NUMERIC_7 0x207 +#define KEY_NUMERIC_8 0x208 +#define KEY_NUMERIC_9 0x209 +#define KEY_NUMERIC_STAR 0x20a +#define KEY_NUMERIC_POUND 0x20b +#define KEY_NUMERIC_A 0x20c /* Phone key A - HUT Telephony 0xb9 */ +#define KEY_NUMERIC_B 0x20d +#define KEY_NUMERIC_C 0x20e +#define KEY_NUMERIC_D 0x20f + +#define KEY_CAMERA_FOCUS 0x210 +#define KEY_WPS_BUTTON 0x211 /* WiFi Protected Setup key */ + +#define KEY_TOUCHPAD_TOGGLE 0x212 /* Request switch touchpad on or off */ +#define KEY_TOUCHPAD_ON 0x213 +#define KEY_TOUCHPAD_OFF 0x214 + +#define KEY_CAMERA_ZOOMIN 0x215 +#define KEY_CAMERA_ZOOMOUT 0x216 +#define KEY_CAMERA_UP 0x217 +#define KEY_CAMERA_DOWN 0x218 +#define KEY_CAMERA_LEFT 0x219 +#define KEY_CAMERA_RIGHT 0x21a + +#define KEY_ATTENDANT_ON 0x21b +#define KEY_ATTENDANT_OFF 0x21c +#define KEY_ATTENDANT_TOGGLE 0x21d /* Attendant call on or off */ +#define KEY_LIGHTS_TOGGLE 0x21e /* Reading light on or off */ + +#define BTN_DPAD_UP 0x220 +#define BTN_DPAD_DOWN 0x221 +#define BTN_DPAD_LEFT 0x222 +#define BTN_DPAD_RIGHT 0x223 + +#define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */ + +#define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */ +#define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */ +#define KEY_JOURNAL 0x242 /* AL Log/Journal/Timecard */ +#define KEY_CONTROLPANEL 0x243 /* AL Control Panel */ +#define KEY_APPSELECT 0x244 /* AL Select Task/Application */ +#define KEY_SCREENSAVER 0x245 /* AL Screen Saver */ +#define KEY_VOICECOMMAND 0x246 /* Listening Voice Command */ + +#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */ +#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */ + +#define KEY_KBDINPUTASSIST_PREV 0x260 +#define KEY_KBDINPUTASSIST_NEXT 0x261 +#define KEY_KBDINPUTASSIST_PREVGROUP 0x262 +#define KEY_KBDINPUTASSIST_NEXTGROUP 0x263 +#define KEY_KBDINPUTASSIST_ACCEPT 0x264 +#define KEY_KBDINPUTASSIST_CANCEL 0x265 + +#define BTN_TRIGGER_HAPPY 0x2c0 +#define BTN_TRIGGER_HAPPY1 0x2c0 +#define BTN_TRIGGER_HAPPY2 0x2c1 +#define BTN_TRIGGER_HAPPY3 0x2c2 +#define BTN_TRIGGER_HAPPY4 0x2c3 +#define BTN_TRIGGER_HAPPY5 0x2c4 +#define BTN_TRIGGER_HAPPY6 0x2c5 +#define BTN_TRIGGER_HAPPY7 0x2c6 +#define BTN_TRIGGER_HAPPY8 0x2c7 +#define BTN_TRIGGER_HAPPY9 0x2c8 +#define BTN_TRIGGER_HAPPY10 0x2c9 +#define BTN_TRIGGER_HAPPY11 0x2ca +#define BTN_TRIGGER_HAPPY12 0x2cb +#define BTN_TRIGGER_HAPPY13 0x2cc +#define BTN_TRIGGER_HAPPY14 0x2cd +#define BTN_TRIGGER_HAPPY15 0x2ce +#define BTN_TRIGGER_HAPPY16 0x2cf +#define BTN_TRIGGER_HAPPY17 0x2d0 +#define BTN_TRIGGER_HAPPY18 0x2d1 +#define BTN_TRIGGER_HAPPY19 0x2d2 +#define BTN_TRIGGER_HAPPY20 0x2d3 +#define BTN_TRIGGER_HAPPY21 0x2d4 +#define BTN_TRIGGER_HAPPY22 0x2d5 +#define BTN_TRIGGER_HAPPY23 0x2d6 +#define BTN_TRIGGER_HAPPY24 0x2d7 +#define BTN_TRIGGER_HAPPY25 0x2d8 +#define BTN_TRIGGER_HAPPY26 0x2d9 +#define BTN_TRIGGER_HAPPY27 0x2da +#define BTN_TRIGGER_HAPPY28 0x2db +#define BTN_TRIGGER_HAPPY29 0x2dc +#define BTN_TRIGGER_HAPPY30 0x2dd +#define BTN_TRIGGER_HAPPY31 0x2de +#define BTN_TRIGGER_HAPPY32 0x2df +#define BTN_TRIGGER_HAPPY33 0x2e0 +#define BTN_TRIGGER_HAPPY34 0x2e1 +#define BTN_TRIGGER_HAPPY35 0x2e2 +#define BTN_TRIGGER_HAPPY36 0x2e3 +#define BTN_TRIGGER_HAPPY37 0x2e4 +#define BTN_TRIGGER_HAPPY38 0x2e5 +#define BTN_TRIGGER_HAPPY39 0x2e6 +#define BTN_TRIGGER_HAPPY40 0x2e7 + +/* We avoid low common keys in module aliases so they don't get huge. */ +#define KEY_MIN_INTERESTING KEY_MUTE +#define KEY_MAX 0x2ff +#define KEY_CNT (KEY_MAX+1) + +/* + * Relative axes + */ + +#define REL_X 0x00 +#define REL_Y 0x01 +#define REL_Z 0x02 +#define REL_RX 0x03 +#define REL_RY 0x04 +#define REL_RZ 0x05 +#define REL_HWHEEL 0x06 +#define REL_DIAL 0x07 +#define REL_WHEEL 0x08 +#define REL_MISC 0x09 +#define REL_MAX 0x0f +#define REL_CNT (REL_MAX+1) + +/* + * Absolute axes + */ + +#define ABS_X 0x00 +#define ABS_Y 0x01 +#define ABS_Z 0x02 +#define ABS_RX 0x03 +#define ABS_RY 0x04 +#define ABS_RZ 0x05 +#define ABS_THROTTLE 0x06 +#define ABS_RUDDER 0x07 +#define ABS_WHEEL 0x08 +#define ABS_GAS 0x09 +#define ABS_BRAKE 0x0a +#define ABS_HAT0X 0x10 +#define ABS_HAT0Y 0x11 +#define ABS_HAT1X 0x12 +#define ABS_HAT1Y 0x13 +#define ABS_HAT2X 0x14 +#define ABS_HAT2Y 0x15 +#define ABS_HAT3X 0x16 +#define ABS_HAT3Y 0x17 +#define ABS_PRESSURE 0x18 +#define ABS_DISTANCE 0x19 +#define ABS_TILT_X 0x1a +#define ABS_TILT_Y 0x1b +#define ABS_TOOL_WIDTH 0x1c + +#define ABS_VOLUME 0x20 + +#define ABS_MISC 0x28 + +#define ABS_MT_SLOT 0x2f /* MT slot being modified */ +#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ +#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */ +#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */ +#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */ +#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */ +#define ABS_MT_POSITION_X 0x35 /* Center X touch position */ +#define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */ +#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */ +#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */ +#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */ +#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */ +#define ABS_MT_DISTANCE 0x3b /* Contact hover distance */ +#define ABS_MT_TOOL_X 0x3c /* Center X tool position */ +#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */ + + +#define ABS_MAX 0x3f +#define ABS_CNT (ABS_MAX+1) + +/* + * Switch events + */ + +#define SW_LID 0x00 /* set = lid shut */ +#define SW_TABLET_MODE 0x01 /* set = tablet mode */ +#define SW_HEADPHONE_INSERT 0x02 /* set = inserted */ +#define SW_RFKILL_ALL 0x03 /* rfkill master switch, type "any" + set = radio enabled */ +#define SW_RADIO SW_RFKILL_ALL /* deprecated */ +#define SW_MICROPHONE_INSERT 0x04 /* set = inserted */ +#define SW_DOCK 0x05 /* set = plugged into dock */ +#define SW_LINEOUT_INSERT 0x06 /* set = inserted */ +#define SW_JACK_PHYSICAL_INSERT 0x07 /* set = mechanical switch set */ +#define SW_VIDEOOUT_INSERT 0x08 /* set = inserted */ +#define SW_CAMERA_LENS_COVER 0x09 /* set = lens covered */ +#define SW_KEYPAD_SLIDE 0x0a /* set = keypad slide out */ +#define SW_FRONT_PROXIMITY 0x0b /* set = front proximity sensor active */ +#define SW_ROTATE_LOCK 0x0c /* set = rotate locked/disabled */ +#define SW_LINEIN_INSERT 0x0d /* set = inserted */ +#define SW_MUTE_DEVICE 0x0e /* set = device disabled */ +#define SW_MAX_ 0x0f +#define SW_CNT (SW_MAX_+1) + +/* + * Misc events + */ + +#define MSC_SERIAL 0x00 +#define MSC_PULSELED 0x01 +#define MSC_GESTURE 0x02 +#define MSC_RAW 0x03 +#define MSC_SCAN 0x04 +#define MSC_TIMESTAMP 0x05 +#define MSC_MAX 0x07 +#define MSC_CNT (MSC_MAX+1) + +/* + * LEDs + */ + +#define LED_NUML 0x00 +#define LED_CAPSL 0x01 +#define LED_SCROLLL 0x02 +#define LED_COMPOSE 0x03 +#define LED_KANA 0x04 +#define LED_SLEEP 0x05 +#define LED_SUSPEND 0x06 +#define LED_MUTE 0x07 +#define LED_MISC 0x08 +#define LED_MAIL 0x09 +#define LED_CHARGING 0x0a +#define LED_MAX 0x0f +#define LED_CNT (LED_MAX+1) + +/* + * Autorepeat values + */ + +#define REP_DELAY 0x00 +#define REP_PERIOD 0x01 +#define REP_MAX 0x01 +#define REP_CNT (REP_MAX+1) + +/* + * Sounds + */ + +#define SND_CLICK 0x00 +#define SND_BELL 0x01 +#define SND_TONE 0x02 +#define SND_MAX 0x07 +#define SND_CNT (SND_MAX+1) + +#endif diff --git a/qemu/include/standard-headers/linux/input.h b/qemu/include/standard-headers/linux/input.h index b003c6705..a52b2025b 100644 --- a/qemu/include/standard-headers/linux/input.h +++ b/qemu/include/standard-headers/linux/input.h @@ -13,6 +13,7 @@ #include #include "standard-headers/linux/types.h" +#include "standard-headers/linux/input-event-codes.h" /* * The event structure itself @@ -94,6 +95,12 @@ struct input_keymap_entry { uint8_t scancode[32]; }; +struct input_mask { + uint32_t type; + uint32_t codes_size; + uint64_t codes_ptr; +}; + #define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */ #define EVIOCGID _IOR('E', 0x02, struct input_id) /* get device ID */ #define EVIOCGREP _IOR('E', 0x03, unsigned int[2]) /* get repeat settings */ @@ -144,801 +151,68 @@ struct input_keymap_entry { #define EVIOCGABS(abs) _IOR('E', 0x40 + (abs), struct input_absinfo) /* get abs value/limits */ #define EVIOCSABS(abs) _IOW('E', 0xc0 + (abs), struct input_absinfo) /* set abs value/limits */ -#define EVIOCSFF _IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect)) /* send a force effect to a force feedback device */ +#define EVIOCSFF _IOW('E', 0x80, struct ff_effect) /* send a force effect to a force feedback device */ #define EVIOCRMFF _IOW('E', 0x81, int) /* Erase a force effect */ #define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */ #define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */ #define EVIOCREVOKE _IOW('E', 0x91, int) /* Revoke device access */ -#define EVIOCSCLOCKID _IOW('E', 0xa0, int) /* Set clockid to be used for timestamps */ - -/* - * Device properties and quirks - */ - -#define INPUT_PROP_POINTER 0x00 /* needs a pointer */ -#define INPUT_PROP_DIRECT 0x01 /* direct input devices */ -#define INPUT_PROP_BUTTONPAD 0x02 /* has button(s) under pad */ -#define INPUT_PROP_SEMI_MT 0x03 /* touch rectangle only */ -#define INPUT_PROP_TOPBUTTONPAD 0x04 /* softbuttons at top of pad */ -#define INPUT_PROP_POINTING_STICK 0x05 /* is a pointing stick */ -#define INPUT_PROP_ACCELEROMETER 0x06 /* has accelerometer */ - -#define INPUT_PROP_MAX 0x1f -#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1) - -/* - * Event types - */ - -#define EV_SYN 0x00 -#define EV_KEY 0x01 -#define EV_REL 0x02 -#define EV_ABS 0x03 -#define EV_MSC 0x04 -#define EV_SW 0x05 -#define EV_LED 0x11 -#define EV_SND 0x12 -#define EV_REP 0x14 -#define EV_FF 0x15 -#define EV_PWR 0x16 -#define EV_FF_STATUS 0x17 -#define EV_MAX 0x1f -#define EV_CNT (EV_MAX+1) - -/* - * Synchronization events. - */ - -#define SYN_REPORT 0 -#define SYN_CONFIG 1 -#define SYN_MT_REPORT 2 -#define SYN_DROPPED 3 -#define SYN_MAX 0xf -#define SYN_CNT (SYN_MAX+1) - -/* - * Keys and buttons +/** + * EVIOCGMASK - Retrieve current event mask * - * Most of the keys/buttons are modeled after USB HUT 1.12 - * (see http://www.usb.org/developers/hidpage). - * Abbreviations in the comments: - * AC - Application Control - * AL - Application Launch Button - * SC - System Control - */ - -#define KEY_RESERVED 0 -#define KEY_ESC 1 -#define KEY_1 2 -#define KEY_2 3 -#define KEY_3 4 -#define KEY_4 5 -#define KEY_5 6 -#define KEY_6 7 -#define KEY_7 8 -#define KEY_8 9 -#define KEY_9 10 -#define KEY_0 11 -#define KEY_MINUS 12 -#define KEY_EQUAL 13 -#define KEY_BACKSPACE 14 -#define KEY_TAB 15 -#define KEY_Q 16 -#define KEY_W 17 -#define KEY_E 18 -#define KEY_R 19 -#define KEY_T 20 -#define KEY_Y 21 -#define KEY_U 22 -#define KEY_I 23 -#define KEY_O 24 -#define KEY_P 25 -#define KEY_LEFTBRACE 26 -#define KEY_RIGHTBRACE 27 -#define KEY_ENTER 28 -#define KEY_LEFTCTRL 29 -#define KEY_A 30 -#define KEY_S 31 -#define KEY_D 32 -#define KEY_F 33 -#define KEY_G 34 -#define KEY_H 35 -#define KEY_J 36 -#define KEY_K 37 -#define KEY_L 38 -#define KEY_SEMICOLON 39 -#define KEY_APOSTROPHE 40 -#define KEY_GRAVE 41 -#define KEY_LEFTSHIFT 42 -#define KEY_BACKSLASH 43 -#define KEY_Z 44 -#define KEY_X 45 -#define KEY_C 46 -#define KEY_V 47 -#define KEY_B 48 -#define KEY_N 49 -#define KEY_M 50 -#define KEY_COMMA 51 -#define KEY_DOT 52 -#define KEY_SLASH 53 -#define KEY_RIGHTSHIFT 54 -#define KEY_KPASTERISK 55 -#define KEY_LEFTALT 56 -#define KEY_SPACE 57 -#define KEY_CAPSLOCK 58 -#define KEY_F1 59 -#define KEY_F2 60 -#define KEY_F3 61 -#define KEY_F4 62 -#define KEY_F5 63 -#define KEY_F6 64 -#define KEY_F7 65 -#define KEY_F8 66 -#define KEY_F9 67 -#define KEY_F10 68 -#define KEY_NUMLOCK 69 -#define KEY_SCROLLLOCK 70 -#define KEY_KP7 71 -#define KEY_KP8 72 -#define KEY_KP9 73 -#define KEY_KPMINUS 74 -#define KEY_KP4 75 -#define KEY_KP5 76 -#define KEY_KP6 77 -#define KEY_KPPLUS 78 -#define KEY_KP1 79 -#define KEY_KP2 80 -#define KEY_KP3 81 -#define KEY_KP0 82 -#define KEY_KPDOT 83 - -#define KEY_ZENKAKUHANKAKU 85 -#define KEY_102ND 86 -#define KEY_F11 87 -#define KEY_F12 88 -#define KEY_RO 89 -#define KEY_KATAKANA 90 -#define KEY_HIRAGANA 91 -#define KEY_HENKAN 92 -#define KEY_KATAKANAHIRAGANA 93 -#define KEY_MUHENKAN 94 -#define KEY_KPJPCOMMA 95 -#define KEY_KPENTER 96 -#define KEY_RIGHTCTRL 97 -#define KEY_KPSLASH 98 -#define KEY_SYSRQ 99 -#define KEY_RIGHTALT 100 -#define KEY_LINEFEED 101 -#define KEY_HOME 102 -#define KEY_UP 103 -#define KEY_PAGEUP 104 -#define KEY_LEFT 105 -#define KEY_RIGHT 106 -#define KEY_END 107 -#define KEY_DOWN 108 -#define KEY_PAGEDOWN 109 -#define KEY_INSERT 110 -#define KEY_DELETE 111 -#define KEY_MACRO 112 -#define KEY_MUTE 113 -#define KEY_VOLUMEDOWN 114 -#define KEY_VOLUMEUP 115 -#define KEY_POWER 116 /* SC System Power Down */ -#define KEY_KPEQUAL 117 -#define KEY_KPPLUSMINUS 118 -#define KEY_PAUSE 119 -#define KEY_SCALE 120 /* AL Compiz Scale (Expose) */ - -#define KEY_KPCOMMA 121 -#define KEY_HANGEUL 122 -#define KEY_HANGUEL KEY_HANGEUL -#define KEY_HANJA 123 -#define KEY_YEN 124 -#define KEY_LEFTMETA 125 -#define KEY_RIGHTMETA 126 -#define KEY_COMPOSE 127 - -#define KEY_STOP 128 /* AC Stop */ -#define KEY_AGAIN 129 -#define KEY_PROPS 130 /* AC Properties */ -#define KEY_UNDO 131 /* AC Undo */ -#define KEY_FRONT 132 -#define KEY_COPY 133 /* AC Copy */ -#define KEY_OPEN 134 /* AC Open */ -#define KEY_PASTE 135 /* AC Paste */ -#define KEY_FIND 136 /* AC Search */ -#define KEY_CUT 137 /* AC Cut */ -#define KEY_HELP 138 /* AL Integrated Help Center */ -#define KEY_MENU 139 /* Menu (show menu) */ -#define KEY_CALC 140 /* AL Calculator */ -#define KEY_SETUP 141 -#define KEY_SLEEP 142 /* SC System Sleep */ -#define KEY_WAKEUP 143 /* System Wake Up */ -#define KEY_FILE 144 /* AL Local Machine Browser */ -#define KEY_SENDFILE 145 -#define KEY_DELETEFILE 146 -#define KEY_XFER 147 -#define KEY_PROG1 148 -#define KEY_PROG2 149 -#define KEY_WWW 150 /* AL Internet Browser */ -#define KEY_MSDOS 151 -#define KEY_COFFEE 152 /* AL Terminal Lock/Screensaver */ -#define KEY_SCREENLOCK KEY_COFFEE -#define KEY_ROTATE_DISPLAY 153 /* Display orientation for e.g. tablets */ -#define KEY_DIRECTION KEY_ROTATE_DISPLAY -#define KEY_CYCLEWINDOWS 154 -#define KEY_MAIL 155 -#define KEY_BOOKMARKS 156 /* AC Bookmarks */ -#define KEY_COMPUTER 157 -#define KEY_BACK 158 /* AC Back */ -#define KEY_FORWARD 159 /* AC Forward */ -#define KEY_CLOSECD 160 -#define KEY_EJECTCD 161 -#define KEY_EJECTCLOSECD 162 -#define KEY_NEXTSONG 163 -#define KEY_PLAYPAUSE 164 -#define KEY_PREVIOUSSONG 165 -#define KEY_STOPCD 166 -#define KEY_RECORD 167 -#define KEY_REWIND 168 -#define KEY_PHONE 169 /* Media Select Telephone */ -#define KEY_ISO 170 -#define KEY_CONFIG 171 /* AL Consumer Control Configuration */ -#define KEY_HOMEPAGE 172 /* AC Home */ -#define KEY_REFRESH 173 /* AC Refresh */ -#define KEY_EXIT 174 /* AC Exit */ -#define KEY_MOVE 175 -#define KEY_EDIT 176 -#define KEY_SCROLLUP 177 -#define KEY_SCROLLDOWN 178 -#define KEY_KPLEFTPAREN 179 -#define KEY_KPRIGHTPAREN 180 -#define KEY_NEW 181 /* AC New */ -#define KEY_REDO 182 /* AC Redo/Repeat */ - -#define KEY_F13 183 -#define KEY_F14 184 -#define KEY_F15 185 -#define KEY_F16 186 -#define KEY_F17 187 -#define KEY_F18 188 -#define KEY_F19 189 -#define KEY_F20 190 -#define KEY_F21 191 -#define KEY_F22 192 -#define KEY_F23 193 -#define KEY_F24 194 - -#define KEY_PLAYCD 200 -#define KEY_PAUSECD 201 -#define KEY_PROG3 202 -#define KEY_PROG4 203 -#define KEY_DASHBOARD 204 /* AL Dashboard */ -#define KEY_SUSPEND 205 -#define KEY_CLOSE 206 /* AC Close */ -#define KEY_PLAY 207 -#define KEY_FASTFORWARD 208 -#define KEY_BASSBOOST 209 -#define KEY_PRINT 210 /* AC Print */ -#define KEY_HP 211 -#define KEY_CAMERA 212 -#define KEY_SOUND 213 -#define KEY_QUESTION 214 -#define KEY_EMAIL 215 -#define KEY_CHAT 216 -#define KEY_SEARCH 217 -#define KEY_CONNECT 218 -#define KEY_FINANCE 219 /* AL Checkbook/Finance */ -#define KEY_SPORT 220 -#define KEY_SHOP 221 -#define KEY_ALTERASE 222 -#define KEY_CANCEL 223 /* AC Cancel */ -#define KEY_BRIGHTNESSDOWN 224 -#define KEY_BRIGHTNESSUP 225 -#define KEY_MEDIA 226 - -#define KEY_SWITCHVIDEOMODE 227 /* Cycle between available video - outputs (Monitor/LCD/TV-out/etc) */ -#define KEY_KBDILLUMTOGGLE 228 -#define KEY_KBDILLUMDOWN 229 -#define KEY_KBDILLUMUP 230 - -#define KEY_SEND 231 /* AC Send */ -#define KEY_REPLY 232 /* AC Reply */ -#define KEY_FORWARDMAIL 233 /* AC Forward Msg */ -#define KEY_SAVE 234 /* AC Save */ -#define KEY_DOCUMENTS 235 - -#define KEY_BATTERY 236 - -#define KEY_BLUETOOTH 237 -#define KEY_WLAN 238 -#define KEY_UWB 239 - -#define KEY_UNKNOWN 240 - -#define KEY_VIDEO_NEXT 241 /* drive next video source */ -#define KEY_VIDEO_PREV 242 /* drive previous video source */ -#define KEY_BRIGHTNESS_CYCLE 243 /* brightness up, after max is min */ -#define KEY_BRIGHTNESS_AUTO 244 /* Set Auto Brightness: manual - brightness control is off, - rely on ambient */ -#define KEY_BRIGHTNESS_ZERO KEY_BRIGHTNESS_AUTO -#define KEY_DISPLAY_OFF 245 /* display device to off state */ - -#define KEY_WWAN 246 /* Wireless WAN (LTE, UMTS, GSM, etc.) */ -#define KEY_WIMAX KEY_WWAN -#define KEY_RFKILL 247 /* Key that controls all radios */ - -#define KEY_MICMUTE 248 /* Mute / unmute the microphone */ - -/* Code 255 is reserved for special needs of AT keyboard driver */ - -#define BTN_MISC 0x100 -#define BTN_0 0x100 -#define BTN_1 0x101 -#define BTN_2 0x102 -#define BTN_3 0x103 -#define BTN_4 0x104 -#define BTN_5 0x105 -#define BTN_6 0x106 -#define BTN_7 0x107 -#define BTN_8 0x108 -#define BTN_9 0x109 - -#define BTN_MOUSE 0x110 -#define BTN_LEFT 0x110 -#define BTN_RIGHT 0x111 -#define BTN_MIDDLE 0x112 -#define BTN_SIDE 0x113 -#define BTN_EXTRA 0x114 -#define BTN_FORWARD 0x115 -#define BTN_BACK 0x116 -#define BTN_TASK 0x117 - -#define BTN_JOYSTICK 0x120 -#define BTN_TRIGGER 0x120 -#define BTN_THUMB 0x121 -#define BTN_THUMB2 0x122 -#define BTN_TOP 0x123 -#define BTN_TOP2 0x124 -#define BTN_PINKIE 0x125 -#define BTN_BASE 0x126 -#define BTN_BASE2 0x127 -#define BTN_BASE3 0x128 -#define BTN_BASE4 0x129 -#define BTN_BASE5 0x12a -#define BTN_BASE6 0x12b -#define BTN_DEAD 0x12f - -#define BTN_GAMEPAD 0x130 -#define BTN_SOUTH 0x130 -#define BTN_A BTN_SOUTH -#define BTN_EAST 0x131 -#define BTN_B BTN_EAST -#define BTN_C 0x132 -#define BTN_NORTH 0x133 -#define BTN_X BTN_NORTH -#define BTN_WEST 0x134 -#define BTN_Y BTN_WEST -#define BTN_Z 0x135 -#define BTN_TL 0x136 -#define BTN_TR 0x137 -#define BTN_TL2 0x138 -#define BTN_TR2 0x139 -#define BTN_SELECT 0x13a -#define BTN_START 0x13b -#define BTN_MODE 0x13c -#define BTN_THUMBL 0x13d -#define BTN_THUMBR 0x13e - -#define BTN_DIGI 0x140 -#define BTN_TOOL_PEN 0x140 -#define BTN_TOOL_RUBBER 0x141 -#define BTN_TOOL_BRUSH 0x142 -#define BTN_TOOL_PENCIL 0x143 -#define BTN_TOOL_AIRBRUSH 0x144 -#define BTN_TOOL_FINGER 0x145 -#define BTN_TOOL_MOUSE 0x146 -#define BTN_TOOL_LENS 0x147 -#define BTN_TOOL_QUINTTAP 0x148 /* Five fingers on trackpad */ -#define BTN_TOUCH 0x14a -#define BTN_STYLUS 0x14b -#define BTN_STYLUS2 0x14c -#define BTN_TOOL_DOUBLETAP 0x14d -#define BTN_TOOL_TRIPLETAP 0x14e -#define BTN_TOOL_QUADTAP 0x14f /* Four fingers on trackpad */ - -#define BTN_WHEEL 0x150 -#define BTN_GEAR_DOWN 0x150 -#define BTN_GEAR_UP 0x151 - -#define KEY_OK 0x160 -#define KEY_SELECT 0x161 -#define KEY_GOTO 0x162 -#define KEY_CLEAR 0x163 -#define KEY_POWER2 0x164 -#define KEY_OPTION 0x165 -#define KEY_INFO 0x166 /* AL OEM Features/Tips/Tutorial */ -#define KEY_TIME 0x167 -#define KEY_VENDOR 0x168 -#define KEY_ARCHIVE 0x169 -#define KEY_PROGRAM 0x16a /* Media Select Program Guide */ -#define KEY_CHANNEL 0x16b -#define KEY_FAVORITES 0x16c -#define KEY_EPG 0x16d -#define KEY_PVR 0x16e /* Media Select Home */ -#define KEY_MHP 0x16f -#define KEY_LANGUAGE 0x170 -#define KEY_TITLE 0x171 -#define KEY_SUBTITLE 0x172 -#define KEY_ANGLE 0x173 -#define KEY_ZOOM 0x174 -#define KEY_MODE 0x175 -#define KEY_KEYBOARD 0x176 -#define KEY_SCREEN 0x177 -#define KEY_PC 0x178 /* Media Select Computer */ -#define KEY_TV 0x179 /* Media Select TV */ -#define KEY_TV2 0x17a /* Media Select Cable */ -#define KEY_VCR 0x17b /* Media Select VCR */ -#define KEY_VCR2 0x17c /* VCR Plus */ -#define KEY_SAT 0x17d /* Media Select Satellite */ -#define KEY_SAT2 0x17e -#define KEY_CD 0x17f /* Media Select CD */ -#define KEY_TAPE 0x180 /* Media Select Tape */ -#define KEY_RADIO 0x181 -#define KEY_TUNER 0x182 /* Media Select Tuner */ -#define KEY_PLAYER 0x183 -#define KEY_TEXT 0x184 -#define KEY_DVD 0x185 /* Media Select DVD */ -#define KEY_AUX 0x186 -#define KEY_MP3 0x187 -#define KEY_AUDIO 0x188 /* AL Audio Browser */ -#define KEY_VIDEO 0x189 /* AL Movie Browser */ -#define KEY_DIRECTORY 0x18a -#define KEY_LIST 0x18b -#define KEY_MEMO 0x18c /* Media Select Messages */ -#define KEY_CALENDAR 0x18d -#define KEY_RED 0x18e -#define KEY_GREEN 0x18f -#define KEY_YELLOW 0x190 -#define KEY_BLUE 0x191 -#define KEY_CHANNELUP 0x192 /* Channel Increment */ -#define KEY_CHANNELDOWN 0x193 /* Channel Decrement */ -#define KEY_FIRST 0x194 -#define KEY_LAST 0x195 /* Recall Last */ -#define KEY_AB 0x196 -#define KEY_NEXT 0x197 -#define KEY_RESTART 0x198 -#define KEY_SLOW 0x199 -#define KEY_SHUFFLE 0x19a -#define KEY_BREAK 0x19b -#define KEY_PREVIOUS 0x19c -#define KEY_DIGITS 0x19d -#define KEY_TEEN 0x19e -#define KEY_TWEN 0x19f -#define KEY_VIDEOPHONE 0x1a0 /* Media Select Video Phone */ -#define KEY_GAMES 0x1a1 /* Media Select Games */ -#define KEY_ZOOMIN 0x1a2 /* AC Zoom In */ -#define KEY_ZOOMOUT 0x1a3 /* AC Zoom Out */ -#define KEY_ZOOMRESET 0x1a4 /* AC Zoom */ -#define KEY_WORDPROCESSOR 0x1a5 /* AL Word Processor */ -#define KEY_EDITOR 0x1a6 /* AL Text Editor */ -#define KEY_SPREADSHEET 0x1a7 /* AL Spreadsheet */ -#define KEY_GRAPHICSEDITOR 0x1a8 /* AL Graphics Editor */ -#define KEY_PRESENTATION 0x1a9 /* AL Presentation App */ -#define KEY_DATABASE 0x1aa /* AL Database App */ -#define KEY_NEWS 0x1ab /* AL Newsreader */ -#define KEY_VOICEMAIL 0x1ac /* AL Voicemail */ -#define KEY_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */ -#define KEY_MESSENGER 0x1ae /* AL Instant Messaging */ -#define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */ -#define KEY_BRIGHTNESS_TOGGLE KEY_DISPLAYTOGGLE -#define KEY_SPELLCHECK 0x1b0 /* AL Spell Check */ -#define KEY_LOGOFF 0x1b1 /* AL Logoff */ - -#define KEY_DOLLAR 0x1b2 -#define KEY_EURO 0x1b3 - -#define KEY_FRAMEBACK 0x1b4 /* Consumer - transport controls */ -#define KEY_FRAMEFORWARD 0x1b5 -#define KEY_CONTEXT_MENU 0x1b6 /* GenDesc - system context menu */ -#define KEY_MEDIA_REPEAT 0x1b7 /* Consumer - transport control */ -#define KEY_10CHANNELSUP 0x1b8 /* 10 channels up (10+) */ -#define KEY_10CHANNELSDOWN 0x1b9 /* 10 channels down (10-) */ -#define KEY_IMAGES 0x1ba /* AL Image Browser */ - -#define KEY_DEL_EOL 0x1c0 -#define KEY_DEL_EOS 0x1c1 -#define KEY_INS_LINE 0x1c2 -#define KEY_DEL_LINE 0x1c3 - -#define KEY_FN 0x1d0 -#define KEY_FN_ESC 0x1d1 -#define KEY_FN_F1 0x1d2 -#define KEY_FN_F2 0x1d3 -#define KEY_FN_F3 0x1d4 -#define KEY_FN_F4 0x1d5 -#define KEY_FN_F5 0x1d6 -#define KEY_FN_F6 0x1d7 -#define KEY_FN_F7 0x1d8 -#define KEY_FN_F8 0x1d9 -#define KEY_FN_F9 0x1da -#define KEY_FN_F10 0x1db -#define KEY_FN_F11 0x1dc -#define KEY_FN_F12 0x1dd -#define KEY_FN_1 0x1de -#define KEY_FN_2 0x1df -#define KEY_FN_D 0x1e0 -#define KEY_FN_E 0x1e1 -#define KEY_FN_F 0x1e2 -#define KEY_FN_S 0x1e3 -#define KEY_FN_B 0x1e4 - -#define KEY_BRL_DOT1 0x1f1 -#define KEY_BRL_DOT2 0x1f2 -#define KEY_BRL_DOT3 0x1f3 -#define KEY_BRL_DOT4 0x1f4 -#define KEY_BRL_DOT5 0x1f5 -#define KEY_BRL_DOT6 0x1f6 -#define KEY_BRL_DOT7 0x1f7 -#define KEY_BRL_DOT8 0x1f8 -#define KEY_BRL_DOT9 0x1f9 -#define KEY_BRL_DOT10 0x1fa - -#define KEY_NUMERIC_0 0x200 /* used by phones, remote controls, */ -#define KEY_NUMERIC_1 0x201 /* and other keypads */ -#define KEY_NUMERIC_2 0x202 -#define KEY_NUMERIC_3 0x203 -#define KEY_NUMERIC_4 0x204 -#define KEY_NUMERIC_5 0x205 -#define KEY_NUMERIC_6 0x206 -#define KEY_NUMERIC_7 0x207 -#define KEY_NUMERIC_8 0x208 -#define KEY_NUMERIC_9 0x209 -#define KEY_NUMERIC_STAR 0x20a -#define KEY_NUMERIC_POUND 0x20b -#define KEY_NUMERIC_A 0x20c /* Phone key A - HUT Telephony 0xb9 */ -#define KEY_NUMERIC_B 0x20d -#define KEY_NUMERIC_C 0x20e -#define KEY_NUMERIC_D 0x20f - -#define KEY_CAMERA_FOCUS 0x210 -#define KEY_WPS_BUTTON 0x211 /* WiFi Protected Setup key */ - -#define KEY_TOUCHPAD_TOGGLE 0x212 /* Request switch touchpad on or off */ -#define KEY_TOUCHPAD_ON 0x213 -#define KEY_TOUCHPAD_OFF 0x214 - -#define KEY_CAMERA_ZOOMIN 0x215 -#define KEY_CAMERA_ZOOMOUT 0x216 -#define KEY_CAMERA_UP 0x217 -#define KEY_CAMERA_DOWN 0x218 -#define KEY_CAMERA_LEFT 0x219 -#define KEY_CAMERA_RIGHT 0x21a - -#define KEY_ATTENDANT_ON 0x21b -#define KEY_ATTENDANT_OFF 0x21c -#define KEY_ATTENDANT_TOGGLE 0x21d /* Attendant call on or off */ -#define KEY_LIGHTS_TOGGLE 0x21e /* Reading light on or off */ - -#define BTN_DPAD_UP 0x220 -#define BTN_DPAD_DOWN 0x221 -#define BTN_DPAD_LEFT 0x222 -#define BTN_DPAD_RIGHT 0x223 - -#define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */ - -#define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */ -#define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */ -#define KEY_JOURNAL 0x242 /* AL Log/Journal/Timecard */ -#define KEY_CONTROLPANEL 0x243 /* AL Control Panel */ -#define KEY_APPSELECT 0x244 /* AL Select Task/Application */ -#define KEY_SCREENSAVER 0x245 /* AL Screen Saver */ -#define KEY_VOICECOMMAND 0x246 /* Listening Voice Command */ - -#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */ -#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */ - -#define KEY_KBDINPUTASSIST_PREV 0x260 -#define KEY_KBDINPUTASSIST_NEXT 0x261 -#define KEY_KBDINPUTASSIST_PREVGROUP 0x262 -#define KEY_KBDINPUTASSIST_NEXTGROUP 0x263 -#define KEY_KBDINPUTASSIST_ACCEPT 0x264 -#define KEY_KBDINPUTASSIST_CANCEL 0x265 - -#define BTN_TRIGGER_HAPPY 0x2c0 -#define BTN_TRIGGER_HAPPY1 0x2c0 -#define BTN_TRIGGER_HAPPY2 0x2c1 -#define BTN_TRIGGER_HAPPY3 0x2c2 -#define BTN_TRIGGER_HAPPY4 0x2c3 -#define BTN_TRIGGER_HAPPY5 0x2c4 -#define BTN_TRIGGER_HAPPY6 0x2c5 -#define BTN_TRIGGER_HAPPY7 0x2c6 -#define BTN_TRIGGER_HAPPY8 0x2c7 -#define BTN_TRIGGER_HAPPY9 0x2c8 -#define BTN_TRIGGER_HAPPY10 0x2c9 -#define BTN_TRIGGER_HAPPY11 0x2ca -#define BTN_TRIGGER_HAPPY12 0x2cb -#define BTN_TRIGGER_HAPPY13 0x2cc -#define BTN_TRIGGER_HAPPY14 0x2cd -#define BTN_TRIGGER_HAPPY15 0x2ce -#define BTN_TRIGGER_HAPPY16 0x2cf -#define BTN_TRIGGER_HAPPY17 0x2d0 -#define BTN_TRIGGER_HAPPY18 0x2d1 -#define BTN_TRIGGER_HAPPY19 0x2d2 -#define BTN_TRIGGER_HAPPY20 0x2d3 -#define BTN_TRIGGER_HAPPY21 0x2d4 -#define BTN_TRIGGER_HAPPY22 0x2d5 -#define BTN_TRIGGER_HAPPY23 0x2d6 -#define BTN_TRIGGER_HAPPY24 0x2d7 -#define BTN_TRIGGER_HAPPY25 0x2d8 -#define BTN_TRIGGER_HAPPY26 0x2d9 -#define BTN_TRIGGER_HAPPY27 0x2da -#define BTN_TRIGGER_HAPPY28 0x2db -#define BTN_TRIGGER_HAPPY29 0x2dc -#define BTN_TRIGGER_HAPPY30 0x2dd -#define BTN_TRIGGER_HAPPY31 0x2de -#define BTN_TRIGGER_HAPPY32 0x2df -#define BTN_TRIGGER_HAPPY33 0x2e0 -#define BTN_TRIGGER_HAPPY34 0x2e1 -#define BTN_TRIGGER_HAPPY35 0x2e2 -#define BTN_TRIGGER_HAPPY36 0x2e3 -#define BTN_TRIGGER_HAPPY37 0x2e4 -#define BTN_TRIGGER_HAPPY38 0x2e5 -#define BTN_TRIGGER_HAPPY39 0x2e6 -#define BTN_TRIGGER_HAPPY40 0x2e7 - -/* We avoid low common keys in module aliases so they don't get huge. */ -#define KEY_MIN_INTERESTING KEY_MUTE -#define KEY_MAX 0x2ff -#define KEY_CNT (KEY_MAX+1) - -/* - * Relative axes - */ - -#define REL_X 0x00 -#define REL_Y 0x01 -#define REL_Z 0x02 -#define REL_RX 0x03 -#define REL_RY 0x04 -#define REL_RZ 0x05 -#define REL_HWHEEL 0x06 -#define REL_DIAL 0x07 -#define REL_WHEEL 0x08 -#define REL_MISC 0x09 -#define REL_MAX 0x0f -#define REL_CNT (REL_MAX+1) - -/* - * Absolute axes - */ - -#define ABS_X 0x00 -#define ABS_Y 0x01 -#define ABS_Z 0x02 -#define ABS_RX 0x03 -#define ABS_RY 0x04 -#define ABS_RZ 0x05 -#define ABS_THROTTLE 0x06 -#define ABS_RUDDER 0x07 -#define ABS_WHEEL 0x08 -#define ABS_GAS 0x09 -#define ABS_BRAKE 0x0a -#define ABS_HAT0X 0x10 -#define ABS_HAT0Y 0x11 -#define ABS_HAT1X 0x12 -#define ABS_HAT1Y 0x13 -#define ABS_HAT2X 0x14 -#define ABS_HAT2Y 0x15 -#define ABS_HAT3X 0x16 -#define ABS_HAT3Y 0x17 -#define ABS_PRESSURE 0x18 -#define ABS_DISTANCE 0x19 -#define ABS_TILT_X 0x1a -#define ABS_TILT_Y 0x1b -#define ABS_TOOL_WIDTH 0x1c - -#define ABS_VOLUME 0x20 - -#define ABS_MISC 0x28 - -#define ABS_MT_SLOT 0x2f /* MT slot being modified */ -#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ -#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */ -#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */ -#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */ -#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */ -#define ABS_MT_POSITION_X 0x35 /* Center X touch position */ -#define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */ -#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */ -#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */ -#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */ -#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */ -#define ABS_MT_DISTANCE 0x3b /* Contact hover distance */ -#define ABS_MT_TOOL_X 0x3c /* Center X tool position */ -#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */ - - -#define ABS_MAX 0x3f -#define ABS_CNT (ABS_MAX+1) - -/* - * Switch events - */ - -#define SW_LID 0x00 /* set = lid shut */ -#define SW_TABLET_MODE 0x01 /* set = tablet mode */ -#define SW_HEADPHONE_INSERT 0x02 /* set = inserted */ -#define SW_RFKILL_ALL 0x03 /* rfkill master switch, type "any" - set = radio enabled */ -#define SW_RADIO SW_RFKILL_ALL /* deprecated */ -#define SW_MICROPHONE_INSERT 0x04 /* set = inserted */ -#define SW_DOCK 0x05 /* set = plugged into dock */ -#define SW_LINEOUT_INSERT 0x06 /* set = inserted */ -#define SW_JACK_PHYSICAL_INSERT 0x07 /* set = mechanical switch set */ -#define SW_VIDEOOUT_INSERT 0x08 /* set = inserted */ -#define SW_CAMERA_LENS_COVER 0x09 /* set = lens covered */ -#define SW_KEYPAD_SLIDE 0x0a /* set = keypad slide out */ -#define SW_FRONT_PROXIMITY 0x0b /* set = front proximity sensor active */ -#define SW_ROTATE_LOCK 0x0c /* set = rotate locked/disabled */ -#define SW_LINEIN_INSERT 0x0d /* set = inserted */ -#define SW_MUTE_DEVICE 0x0e /* set = device disabled */ -#define SW_MAX 0x0f -#define SW_CNT (SW_MAX+1) - -/* - * Misc events - */ - -#define MSC_SERIAL 0x00 -#define MSC_PULSELED 0x01 -#define MSC_GESTURE 0x02 -#define MSC_RAW 0x03 -#define MSC_SCAN 0x04 -#define MSC_TIMESTAMP 0x05 -#define MSC_MAX 0x07 -#define MSC_CNT (MSC_MAX+1) - -/* - * LEDs - */ - -#define LED_NUML 0x00 -#define LED_CAPSL 0x01 -#define LED_SCROLLL 0x02 -#define LED_COMPOSE 0x03 -#define LED_KANA 0x04 -#define LED_SLEEP 0x05 -#define LED_SUSPEND 0x06 -#define LED_MUTE 0x07 -#define LED_MISC 0x08 -#define LED_MAIL 0x09 -#define LED_CHARGING 0x0a -#define LED_MAX 0x0f -#define LED_CNT (LED_MAX+1) - -/* - * Autorepeat values + * This ioctl allows user to retrieve the current event mask for specific + * event type. The argument must be of type "struct input_mask" and + * specifies the event type to query, the address of the receive buffer and + * the size of the receive buffer. + * + * The event mask is a per-client mask that specifies which events are + * forwarded to the client. Each event code is represented by a single bit + * in the event mask. If the bit is set, the event is passed to the client + * normally. Otherwise, the event is filtered and will never be queued on + * the client's receive buffer. + * + * Event masks do not affect global state of the input device. They only + * affect the file descriptor they are applied to. + * + * The default event mask for a client has all bits set, i.e. all events + * are forwarded to the client. If the kernel is queried for an unknown + * event type or if the receive buffer is larger than the number of + * event codes known to the kernel, the kernel returns all zeroes for those + * codes. + * + * At maximum, codes_size bytes are copied. + * + * This ioctl may fail with ENODEV in case the file is revoked, EFAULT + * if the receive-buffer points to invalid memory, or EINVAL if the kernel + * does not implement the ioctl. */ +#define EVIOCGMASK _IOR('E', 0x92, struct input_mask) /* Get event-masks */ -#define REP_DELAY 0x00 -#define REP_PERIOD 0x01 -#define REP_MAX 0x01 -#define REP_CNT (REP_MAX+1) - -/* - * Sounds +/** + * EVIOCSMASK - Set event mask + * + * This ioctl is the counterpart to EVIOCGMASK. Instead of receiving the + * current event mask, this changes the client's event mask for a specific + * type. See EVIOCGMASK for a description of event-masks and the + * argument-type. + * + * This ioctl provides full forward compatibility. If the passed event type + * is unknown to the kernel, or if the number of event codes specified in + * the mask is bigger than what is known to the kernel, the ioctl is still + * accepted and applied. However, any unknown codes are left untouched and + * stay cleared. That means, the kernel always filters unknown codes + * regardless of what the client requests. If the new mask doesn't cover + * all known event-codes, all remaining codes are automatically cleared and + * thus filtered. + * + * This ioctl may fail with ENODEV in case the file is revoked. EFAULT is + * returned if the receive-buffer points to invalid memory. EINVAL is returned + * if the kernel does not implement the ioctl. */ +#define EVIOCSMASK _IOW('E', 0x93, struct input_mask) /* Set event-masks */ -#define SND_CLICK 0x00 -#define SND_BELL 0x01 -#define SND_TONE 0x02 -#define SND_MAX 0x07 -#define SND_CNT (SND_MAX+1) +#define EVIOCSCLOCKID _IOW('E', 0xa0, int) /* Set clockid to be used for timestamps */ /* * IDs. @@ -969,6 +243,7 @@ struct input_keymap_entry { #define BUS_GSC 0x1A #define BUS_ATARI 0x1B #define BUS_SPI 0x1C +#define BUS_RMI 0x1D /* * MT_TOOL types @@ -1197,6 +472,14 @@ struct ff_effect { #define FF_GAIN 0x60 #define FF_AUTOCENTER 0x61 +/* + * ff->playback(effect_id = FF_GAIN) is the first effect_id to + * cause a collision with another ff method, in this case ff->set_gain(). + * Therefore the greatest safe value for effect_id is FF_GAIN - 1, + * and thus the total number of effects should never exceed FF_GAIN. + */ +#define FF_MAX_EFFECTS FF_GAIN + #define FF_MAX 0x7f #define FF_CNT (FF_MAX+1) diff --git a/qemu/include/standard-headers/linux/pci_regs.h b/qemu/include/standard-headers/linux/pci_regs.h index 57e8c80c3..1becea86c 100644 --- a/qemu/include/standard-headers/linux/pci_regs.h +++ b/qemu/include/standard-headers/linux/pci_regs.h @@ -13,10 +13,10 @@ * PCI to PCI Bridge Specification * PCI System Design Guide * - * For hypertransport information, please consult the following manuals - * from http://www.hypertransport.org + * For HyperTransport information, please consult the following manuals + * from http://www.hypertransport.org * - * The Hypertransport I/O Link Specification + * The HyperTransport I/O Link Specification */ #ifndef LINUX_PCI_REGS_H @@ -26,6 +26,7 @@ * Under PCI, each device has 256 bytes of configuration address space, * of which the first 64 bytes are standardized as follows: */ +#define PCI_STD_HEADER_SIZEOF 64 #define PCI_VENDOR_ID 0x00 /* 16 bits */ #define PCI_DEVICE_ID 0x02 /* 16 bits */ #define PCI_COMMAND 0x04 /* 16 bits */ @@ -36,7 +37,7 @@ #define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ #define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ #define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */ -#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ +#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ #define PCI_COMMAND_SERR 0x100 /* Enable SERR */ #define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ #define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */ @@ -44,7 +45,7 @@ #define PCI_STATUS 0x06 /* 16 bits */ #define PCI_STATUS_INTERRUPT 0x08 /* Interrupt status */ #define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ -#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ +#define PCI_STATUS_66MHZ 0x20 /* Support 66 MHz PCI 2.1 bus */ #define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */ #define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ #define PCI_STATUS_PARITY 0x100 /* Detected parity error */ @@ -125,7 +126,8 @@ #define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */ #define PCI_IO_RANGE_TYPE_16 0x00 #define PCI_IO_RANGE_TYPE_32 0x01 -#define PCI_IO_RANGE_MASK (~0x0fUL) +#define PCI_IO_RANGE_MASK (~0x0fUL) /* Standard 4K I/O windows */ +#define PCI_IO_1K_RANGE_MASK (~0x03UL) /* Intel 1K I/O windows */ #define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ #define PCI_MEMORY_BASE 0x20 /* Memory range behind */ #define PCI_MEMORY_LIMIT 0x22 @@ -203,16 +205,19 @@ #define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */ #define PCI_CAP_ID_PCIX 0x07 /* PCI-X */ #define PCI_CAP_ID_HT 0x08 /* HyperTransport */ -#define PCI_CAP_ID_VNDR 0x09 /* Vendor specific */ +#define PCI_CAP_ID_VNDR 0x09 /* Vendor-Specific */ #define PCI_CAP_ID_DBG 0x0A /* Debug port */ #define PCI_CAP_ID_CCRC 0x0B /* CompactPCI Central Resource Control */ -#define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */ +#define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */ #define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */ #define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */ -#define PCI_CAP_ID_EXP 0x10 /* PCI Express */ +#define PCI_CAP_ID_SECDEV 0x0F /* Secure Device */ +#define PCI_CAP_ID_EXP 0x10 /* PCI Express */ #define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ -#define PCI_CAP_ID_SATA 0x12 /* Serial ATA */ +#define PCI_CAP_ID_SATA 0x12 /* SATA Data/Index Conf. */ #define PCI_CAP_ID_AF 0x13 /* PCI Advanced Features */ +#define PCI_CAP_ID_EA 0x14 /* PCI Enhanced Allocation */ +#define PCI_CAP_ID_MAX PCI_CAP_ID_EA #define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ #define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ #define PCI_CAP_SIZEOF 4 @@ -264,8 +269,8 @@ #define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */ #define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */ #define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */ -#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow processing of 64-bit addresses */ -#define PCI_AGP_COMMAND_FW 0x0010 /* Force FW transfers */ +#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow processing of 64-bit addresses */ +#define PCI_AGP_COMMAND_FW 0x0010 /* Force FW transfers */ #define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate */ #define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate */ #define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate */ @@ -277,6 +282,7 @@ #define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */ #define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */ #define PCI_VPD_DATA 4 /* 32-bits of data returned here */ +#define PCI_CAP_VPD_SIZEOF 8 /* Slot Identification */ @@ -287,32 +293,37 @@ /* Message Signalled Interrupts registers */ -#define PCI_MSI_FLAGS 2 /* Various flags */ -#define PCI_MSI_FLAGS_64BIT 0x80 /* 64-bit addresses allowed */ -#define PCI_MSI_FLAGS_QSIZE 0x70 /* Message queue size configured */ -#define PCI_MSI_FLAGS_QMASK 0x0e /* Maximum queue size available */ -#define PCI_MSI_FLAGS_ENABLE 0x01 /* MSI feature enabled */ -#define PCI_MSI_FLAGS_MASKBIT 0x100 /* 64-bit mask bits allowed */ +#define PCI_MSI_FLAGS 2 /* Message Control */ +#define PCI_MSI_FLAGS_ENABLE 0x0001 /* MSI feature enabled */ +#define PCI_MSI_FLAGS_QMASK 0x000e /* Maximum queue size available */ +#define PCI_MSI_FLAGS_QSIZE 0x0070 /* Message queue size configured */ +#define PCI_MSI_FLAGS_64BIT 0x0080 /* 64-bit addresses allowed */ +#define PCI_MSI_FLAGS_MASKBIT 0x0100 /* Per-vector masking capable */ #define PCI_MSI_RFU 3 /* Rest of capability flags */ #define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */ #define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ #define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ #define PCI_MSI_MASK_32 12 /* Mask bits register for 32-bit devices */ -#define PCI_MSI_PENDING_32 16 /* Pending bits register for 32-bit devices */ +#define PCI_MSI_PENDING_32 16 /* Pending intrs for 32-bit devices */ #define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ #define PCI_MSI_MASK_64 16 /* Mask bits register for 64-bit devices */ -#define PCI_MSI_PENDING_64 20 /* Pending bits register for 32-bit devices */ +#define PCI_MSI_PENDING_64 20 /* Pending intrs for 64-bit devices */ /* MSI-X registers */ -#define PCI_MSIX_FLAGS 2 -#define PCI_MSIX_FLAGS_QSIZE 0x7FF -#define PCI_MSIX_FLAGS_ENABLE (1 << 15) -#define PCI_MSIX_FLAGS_MASKALL (1 << 14) -#define PCI_MSIX_TABLE 4 -#define PCI_MSIX_PBA 8 -#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) - -/* MSI-X entry's format */ +#define PCI_MSIX_FLAGS 2 /* Message Control */ +#define PCI_MSIX_FLAGS_QSIZE 0x07FF /* Table size */ +#define PCI_MSIX_FLAGS_MASKALL 0x4000 /* Mask all vectors for this function */ +#define PCI_MSIX_FLAGS_ENABLE 0x8000 /* MSI-X enable */ +#define PCI_MSIX_TABLE 4 /* Table offset */ +#define PCI_MSIX_TABLE_BIR 0x00000007 /* BAR index */ +#define PCI_MSIX_TABLE_OFFSET 0xfffffff8 /* Offset into specified BAR */ +#define PCI_MSIX_PBA 8 /* Pending Bit Array offset */ +#define PCI_MSIX_PBA_BIR 0x00000007 /* BAR index */ +#define PCI_MSIX_PBA_OFFSET 0xfffffff8 /* Offset into specified BAR */ +#define PCI_MSIX_FLAGS_BIRMASK PCI_MSIX_PBA_BIR /* deprecated */ +#define PCI_CAP_MSIX_SIZEOF 12 /* size of MSIX registers */ + +/* MSI-X Table entry format */ #define PCI_MSIX_ENTRY_SIZE 16 #define PCI_MSIX_ENTRY_LOWER_ADDR 0 #define PCI_MSIX_ENTRY_UPPER_ADDR 4 @@ -341,8 +352,49 @@ #define PCI_AF_CTRL_FLR 0x01 #define PCI_AF_STATUS 5 #define PCI_AF_STATUS_TP 0x01 - -/* PCI-X registers */ +#define PCI_CAP_AF_SIZEOF 6 /* size of AF registers */ + +/* PCI Enhanced Allocation registers */ + +#define PCI_EA_NUM_ENT 2 /* Number of Capability Entries */ +#define PCI_EA_NUM_ENT_MASK 0x3f /* Num Entries Mask */ +#define PCI_EA_FIRST_ENT 4 /* First EA Entry in List */ +#define PCI_EA_FIRST_ENT_BRIDGE 8 /* First EA Entry for Bridges */ +#define PCI_EA_ES 0x00000007 /* Entry Size */ +#define PCI_EA_BEI 0x000000f0 /* BAR Equivalent Indicator */ +/* 0-5 map to BARs 0-5 respectively */ +#define PCI_EA_BEI_BAR0 0 +#define PCI_EA_BEI_BAR5 5 +#define PCI_EA_BEI_BRIDGE 6 /* Resource behind bridge */ +#define PCI_EA_BEI_ENI 7 /* Equivalent Not Indicated */ +#define PCI_EA_BEI_ROM 8 /* Expansion ROM */ +/* 9-14 map to VF BARs 0-5 respectively */ +#define PCI_EA_BEI_VF_BAR0 9 +#define PCI_EA_BEI_VF_BAR5 14 +#define PCI_EA_BEI_RESERVED 15 /* Reserved - Treat like ENI */ +#define PCI_EA_PP 0x0000ff00 /* Primary Properties */ +#define PCI_EA_SP 0x00ff0000 /* Secondary Properties */ +#define PCI_EA_P_MEM 0x00 /* Non-Prefetch Memory */ +#define PCI_EA_P_MEM_PREFETCH 0x01 /* Prefetchable Memory */ +#define PCI_EA_P_IO 0x02 /* I/O Space */ +#define PCI_EA_P_VF_MEM_PREFETCH 0x03 /* VF Prefetchable Memory */ +#define PCI_EA_P_VF_MEM 0x04 /* VF Non-Prefetch Memory */ +#define PCI_EA_P_BRIDGE_MEM 0x05 /* Bridge Non-Prefetch Memory */ +#define PCI_EA_P_BRIDGE_MEM_PREFETCH 0x06 /* Bridge Prefetchable Memory */ +#define PCI_EA_P_BRIDGE_IO 0x07 /* Bridge I/O Space */ +/* 0x08-0xfc reserved */ +#define PCI_EA_P_MEM_RESERVED 0xfd /* Reserved Memory */ +#define PCI_EA_P_IO_RESERVED 0xfe /* Reserved I/O Space */ +#define PCI_EA_P_UNAVAILABLE 0xff /* Entry Unavailable */ +#define PCI_EA_WRITABLE 0x40000000 /* Writable: 1 = RW, 0 = HwInit */ +#define PCI_EA_ENABLE 0x80000000 /* Enable for this entry */ +#define PCI_EA_BASE 4 /* Base Address Offset */ +#define PCI_EA_MAX_OFFSET 8 /* MaxOffset (resource length) */ +/* bit 0 is reserved */ +#define PCI_EA_IS_64 0x00000002 /* 64-bit field flag */ +#define PCI_EA_FIELD_MASK 0xfffffffc /* For Base & Max Offset */ + +/* PCI-X registers (Type 0 (non-bridge) devices) */ #define PCI_X_CMD 2 /* Modes & Features */ #define PCI_X_CMD_DPERR_E 0x0001 /* Data Parity Error Recovery Enable */ @@ -362,7 +414,7 @@ #define PCI_X_CMD_SPLIT_16 0x0060 /* Max 16 */ #define PCI_X_CMD_SPLIT_32 0x0070 /* Max 32 */ #define PCI_X_CMD_MAX_SPLIT 0x0070 /* Max Outstanding Split Transactions */ -#define PCI_X_CMD_VERSION(x) (((x) >> 12) & 3) /* Version */ +#define PCI_X_CMD_VERSION(x) (((x) >> 12) & 3) /* Version */ #define PCI_X_STATUS 4 /* PCI-X capabilities */ #define PCI_X_STATUS_DEVFN 0x000000ff /* A copy of devfn */ #define PCI_X_STATUS_BUS 0x0000ff00 /* A copy of bus nr */ @@ -377,11 +429,28 @@ #define PCI_X_STATUS_SPL_ERR 0x20000000 /* Rcvd Split Completion Error Msg */ #define PCI_X_STATUS_266MHZ 0x40000000 /* 266 MHz capable */ #define PCI_X_STATUS_533MHZ 0x80000000 /* 533 MHz capable */ +#define PCI_X_ECC_CSR 8 /* ECC control and status */ +#define PCI_CAP_PCIX_SIZEOF_V0 8 /* size of registers for Version 0 */ +#define PCI_CAP_PCIX_SIZEOF_V1 24 /* size for Version 1 */ +#define PCI_CAP_PCIX_SIZEOF_V2 PCI_CAP_PCIX_SIZEOF_V1 /* Same for v2 */ + +/* PCI-X registers (Type 1 (bridge) devices) */ + +#define PCI_X_BRIDGE_SSTATUS 2 /* Secondary Status */ +#define PCI_X_SSTATUS_64BIT 0x0001 /* Secondary AD interface is 64 bits */ +#define PCI_X_SSTATUS_133MHZ 0x0002 /* 133 MHz capable */ +#define PCI_X_SSTATUS_FREQ 0x03c0 /* Secondary Bus Mode and Frequency */ +#define PCI_X_SSTATUS_VERS 0x3000 /* PCI-X Capability Version */ +#define PCI_X_SSTATUS_V1 0x1000 /* Mode 2, not Mode 1 */ +#define PCI_X_SSTATUS_V2 0x2000 /* Mode 1 or Modes 1 and 2 */ +#define PCI_X_SSTATUS_266MHZ 0x4000 /* 266 MHz capable */ +#define PCI_X_SSTATUS_533MHZ 0x8000 /* 533 MHz capable */ +#define PCI_X_BRIDGE_STATUS 4 /* Bridge Status */ /* PCI Bridge Subsystem ID registers */ -#define PCI_SSVID_VENDOR_ID 4 /* PCI-Bridge subsystem vendor id register */ -#define PCI_SSVID_DEVICE_ID 6 /* PCI-Bridge subsystem device id register */ +#define PCI_SSVID_VENDOR_ID 4 /* PCI Bridge subsystem vendor ID */ +#define PCI_SSVID_DEVICE_ID 6 /* PCI Bridge subsystem device ID */ /* PCI Express capability registers */ @@ -393,24 +462,24 @@ #define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */ #define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */ #define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */ -#define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCI/PCI-X Bridge */ -#define PCI_EXP_TYPE_PCIE_BRIDGE 0x8 /* PCI/PCI-X to PCIE Bridge */ +#define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCIe to PCI/PCI-X Bridge */ +#define PCI_EXP_TYPE_PCIE_BRIDGE 0x8 /* PCI/PCI-X to PCIe Bridge */ #define PCI_EXP_TYPE_RC_END 0x9 /* Root Complex Integrated Endpoint */ -#define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */ +#define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */ #define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ #define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */ #define PCI_EXP_DEVCAP 4 /* Device capabilities */ -#define PCI_EXP_DEVCAP_PAYLOAD 0x07 /* Max_Payload_Size */ -#define PCI_EXP_DEVCAP_PHANTOM 0x18 /* Phantom functions */ -#define PCI_EXP_DEVCAP_EXT_TAG 0x20 /* Extended tags */ -#define PCI_EXP_DEVCAP_L0S 0x1c0 /* L0s Acceptable Latency */ -#define PCI_EXP_DEVCAP_L1 0xe00 /* L1 Acceptable Latency */ -#define PCI_EXP_DEVCAP_ATN_BUT 0x1000 /* Attention Button Present */ -#define PCI_EXP_DEVCAP_ATN_IND 0x2000 /* Attention Indicator Present */ -#define PCI_EXP_DEVCAP_PWR_IND 0x4000 /* Power Indicator Present */ -#define PCI_EXP_DEVCAP_RBER 0x8000 /* Role-Based Error Reporting */ -#define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ -#define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ +#define PCI_EXP_DEVCAP_PAYLOAD 0x00000007 /* Max_Payload_Size */ +#define PCI_EXP_DEVCAP_PHANTOM 0x00000018 /* Phantom functions */ +#define PCI_EXP_DEVCAP_EXT_TAG 0x00000020 /* Extended tags */ +#define PCI_EXP_DEVCAP_L0S 0x000001c0 /* L0s Acceptable Latency */ +#define PCI_EXP_DEVCAP_L1 0x00000e00 /* L1 Acceptable Latency */ +#define PCI_EXP_DEVCAP_ATN_BUT 0x00001000 /* Attention Button Present */ +#define PCI_EXP_DEVCAP_ATN_IND 0x00002000 /* Attention Indicator Present */ +#define PCI_EXP_DEVCAP_PWR_IND 0x00004000 /* Power Indicator Present */ +#define PCI_EXP_DEVCAP_RBER 0x00008000 /* Role-Based Error Reporting */ +#define PCI_EXP_DEVCAP_PWR_VAL 0x03fc0000 /* Slot Power Limit Value */ +#define PCI_EXP_DEVCAP_PWR_SCL 0x0c000000 /* Slot Power Limit Scale */ #define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */ #define PCI_EXP_DEVCTL 8 /* Device Control */ #define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */ @@ -424,47 +493,61 @@ #define PCI_EXP_DEVCTL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */ #define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */ #define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */ +#define PCI_EXP_DEVCTL_READRQ_128B 0x0000 /* 128 Bytes */ +#define PCI_EXP_DEVCTL_READRQ_256B 0x1000 /* 256 Bytes */ +#define PCI_EXP_DEVCTL_READRQ_512B 0x2000 /* 512 Bytes */ +#define PCI_EXP_DEVCTL_READRQ_1024B 0x3000 /* 1024 Bytes */ #define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */ #define PCI_EXP_DEVSTA 10 /* Device Status */ -#define PCI_EXP_DEVSTA_CED 0x01 /* Correctable Error Detected */ -#define PCI_EXP_DEVSTA_NFED 0x02 /* Non-Fatal Error Detected */ -#define PCI_EXP_DEVSTA_FED 0x04 /* Fatal Error Detected */ -#define PCI_EXP_DEVSTA_URD 0x08 /* Unsupported Request Detected */ -#define PCI_EXP_DEVSTA_AUXPD 0x10 /* AUX Power Detected */ -#define PCI_EXP_DEVSTA_TRPND 0x20 /* Transactions Pending */ +#define PCI_EXP_DEVSTA_CED 0x0001 /* Correctable Error Detected */ +#define PCI_EXP_DEVSTA_NFED 0x0002 /* Non-Fatal Error Detected */ +#define PCI_EXP_DEVSTA_FED 0x0004 /* Fatal Error Detected */ +#define PCI_EXP_DEVSTA_URD 0x0008 /* Unsupported Request Detected */ +#define PCI_EXP_DEVSTA_AUXPD 0x0010 /* AUX Power Detected */ +#define PCI_EXP_DEVSTA_TRPND 0x0020 /* Transactions Pending */ #define PCI_EXP_LNKCAP 12 /* Link Capabilities */ #define PCI_EXP_LNKCAP_SLS 0x0000000f /* Supported Link Speeds */ +#define PCI_EXP_LNKCAP_SLS_2_5GB 0x00000001 /* LNKCAP2 SLS Vector bit 0 */ +#define PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */ #define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */ #define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */ #define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */ #define PCI_EXP_LNKCAP_L1EL 0x00038000 /* L1 Exit Latency */ -#define PCI_EXP_LNKCAP_CLKPM 0x00040000 /* L1 Clock Power Management */ +#define PCI_EXP_LNKCAP_CLKPM 0x00040000 /* Clock Power Management */ #define PCI_EXP_LNKCAP_SDERC 0x00080000 /* Surprise Down Error Reporting Capable */ #define PCI_EXP_LNKCAP_DLLLARC 0x00100000 /* Data Link Layer Link Active Reporting Capable */ #define PCI_EXP_LNKCAP_LBNC 0x00200000 /* Link Bandwidth Notification Capability */ #define PCI_EXP_LNKCAP_PN 0xff000000 /* Port Number */ #define PCI_EXP_LNKCTL 16 /* Link Control */ #define PCI_EXP_LNKCTL_ASPMC 0x0003 /* ASPM Control */ +#define PCI_EXP_LNKCTL_ASPM_L0S 0x0001 /* L0s Enable */ +#define PCI_EXP_LNKCTL_ASPM_L1 0x0002 /* L1 Enable */ #define PCI_EXP_LNKCTL_RCB 0x0008 /* Read Completion Boundary */ #define PCI_EXP_LNKCTL_LD 0x0010 /* Link Disable */ #define PCI_EXP_LNKCTL_RL 0x0020 /* Retrain Link */ #define PCI_EXP_LNKCTL_CCC 0x0040 /* Common Clock Configuration */ #define PCI_EXP_LNKCTL_ES 0x0080 /* Extended Synch */ -#define PCI_EXP_LNKCTL_CLKREQ_EN 0x100 /* Enable clkreq */ +#define PCI_EXP_LNKCTL_CLKREQ_EN 0x0100 /* Enable clkreq */ #define PCI_EXP_LNKCTL_HAWD 0x0200 /* Hardware Autonomous Width Disable */ #define PCI_EXP_LNKCTL_LBMIE 0x0400 /* Link Bandwidth Management Interrupt Enable */ -#define PCI_EXP_LNKCTL_LABIE 0x0800 /* Lnk Autonomous Bandwidth Interrupt Enable */ +#define PCI_EXP_LNKCTL_LABIE 0x0800 /* Link Autonomous Bandwidth Interrupt Enable */ #define PCI_EXP_LNKSTA 18 /* Link Status */ #define PCI_EXP_LNKSTA_CLS 0x000f /* Current Link Speed */ -#define PCI_EXP_LNKSTA_CLS_2_5GB 0x01 /* Current Link Speed 2.5GT/s */ -#define PCI_EXP_LNKSTA_CLS_5_0GB 0x02 /* Current Link Speed 5.0GT/s */ -#define PCI_EXP_LNKSTA_NLW 0x03f0 /* Nogotiated Link Width */ +#define PCI_EXP_LNKSTA_CLS_2_5GB 0x0001 /* Current Link Speed 2.5GT/s */ +#define PCI_EXP_LNKSTA_CLS_5_0GB 0x0002 /* Current Link Speed 5.0GT/s */ +#define PCI_EXP_LNKSTA_CLS_8_0GB 0x0003 /* Current Link Speed 8.0GT/s */ +#define PCI_EXP_LNKSTA_NLW 0x03f0 /* Negotiated Link Width */ +#define PCI_EXP_LNKSTA_NLW_X1 0x0010 /* Current Link Width x1 */ +#define PCI_EXP_LNKSTA_NLW_X2 0x0020 /* Current Link Width x2 */ +#define PCI_EXP_LNKSTA_NLW_X4 0x0040 /* Current Link Width x4 */ +#define PCI_EXP_LNKSTA_NLW_X8 0x0080 /* Current Link Width x8 */ #define PCI_EXP_LNKSTA_NLW_SHIFT 4 /* start of NLW mask in link status */ #define PCI_EXP_LNKSTA_LT 0x0800 /* Link Training */ #define PCI_EXP_LNKSTA_SLC 0x1000 /* Slot Clock Configuration */ #define PCI_EXP_LNKSTA_DLLLA 0x2000 /* Data Link Layer Link Active */ #define PCI_EXP_LNKSTA_LBMS 0x4000 /* Link Bandwidth Management Status */ #define PCI_EXP_LNKSTA_LABS 0x8000 /* Link Autonomous Bandwidth Status */ +#define PCI_CAP_EXP_ENDPOINT_SIZEOF_V1 20 /* v1 endpoints end here */ #define PCI_EXP_SLTCAP 20 /* Slot Capabilities */ #define PCI_EXP_SLTCAP_ABP 0x00000001 /* Attention Button Present */ #define PCI_EXP_SLTCAP_PCP 0x00000002 /* Power Controller Present */ @@ -486,8 +569,16 @@ #define PCI_EXP_SLTCTL_CCIE 0x0010 /* Command Completed Interrupt Enable */ #define PCI_EXP_SLTCTL_HPIE 0x0020 /* Hot-Plug Interrupt Enable */ #define PCI_EXP_SLTCTL_AIC 0x00c0 /* Attention Indicator Control */ +#define PCI_EXP_SLTCTL_ATTN_IND_ON 0x0040 /* Attention Indicator on */ +#define PCI_EXP_SLTCTL_ATTN_IND_BLINK 0x0080 /* Attention Indicator blinking */ +#define PCI_EXP_SLTCTL_ATTN_IND_OFF 0x00c0 /* Attention Indicator off */ #define PCI_EXP_SLTCTL_PIC 0x0300 /* Power Indicator Control */ +#define PCI_EXP_SLTCTL_PWR_IND_ON 0x0100 /* Power Indicator on */ +#define PCI_EXP_SLTCTL_PWR_IND_BLINK 0x0200 /* Power Indicator blinking */ +#define PCI_EXP_SLTCTL_PWR_IND_OFF 0x0300 /* Power Indicator off */ #define PCI_EXP_SLTCTL_PCC 0x0400 /* Power Controller Control */ +#define PCI_EXP_SLTCTL_PWR_ON 0x0000 /* Power On */ +#define PCI_EXP_SLTCTL_PWR_OFF 0x0400 /* Power Off */ #define PCI_EXP_SLTCTL_EIC 0x0800 /* Electromechanical Interlock Control */ #define PCI_EXP_SLTCTL_DLLSCE 0x1000 /* Data Link Layer State Changed Enable */ #define PCI_EXP_SLTSTA 26 /* Slot Status */ @@ -501,52 +592,94 @@ #define PCI_EXP_SLTSTA_EIS 0x0080 /* Electromechanical Interlock Status */ #define PCI_EXP_SLTSTA_DLLSC 0x0100 /* Data Link Layer State Changed */ #define PCI_EXP_RTCTL 28 /* Root Control */ -#define PCI_EXP_RTCTL_SECEE 0x01 /* System Error on Correctable Error */ -#define PCI_EXP_RTCTL_SENFEE 0x02 /* System Error on Non-Fatal Error */ -#define PCI_EXP_RTCTL_SEFEE 0x04 /* System Error on Fatal Error */ -#define PCI_EXP_RTCTL_PMEIE 0x08 /* PME Interrupt Enable */ -#define PCI_EXP_RTCTL_CRSSVE 0x10 /* CRS Software Visibility Enable */ +#define PCI_EXP_RTCTL_SECEE 0x0001 /* System Error on Correctable Error */ +#define PCI_EXP_RTCTL_SENFEE 0x0002 /* System Error on Non-Fatal Error */ +#define PCI_EXP_RTCTL_SEFEE 0x0004 /* System Error on Fatal Error */ +#define PCI_EXP_RTCTL_PMEIE 0x0008 /* PME Interrupt Enable */ +#define PCI_EXP_RTCTL_CRSSVE 0x0010 /* CRS Software Visibility Enable */ #define PCI_EXP_RTCAP 30 /* Root Capabilities */ +#define PCI_EXP_RTCAP_CRSVIS 0x0001 /* CRS Software Visibility capability */ #define PCI_EXP_RTSTA 32 /* Root Status */ -#define PCI_EXP_RTSTA_PME 0x10000 /* PME status */ -#define PCI_EXP_RTSTA_PENDING 0x20000 /* PME pending */ +#define PCI_EXP_RTSTA_PME 0x00010000 /* PME status */ +#define PCI_EXP_RTSTA_PENDING 0x00020000 /* PME pending */ +/* + * The Device Capabilities 2, Device Status 2, Device Control 2, + * Link Capabilities 2, Link Status 2, Link Control 2, + * Slot Capabilities 2, Slot Status 2, and Slot Control 2 registers + * are only present on devices with PCIe Capability version 2. + * Use pcie_capability_read_word() and similar interfaces to use them + * safely. + */ #define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ -#define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ -#define PCI_EXP_DEVCAP2_LTR 0x800 /* Latency tolerance reporting */ -#define PCI_EXP_OBFF_MASK 0xc0000 /* OBFF support mechanism */ -#define PCI_EXP_OBFF_MSG 0x40000 /* New message signaling */ -#define PCI_EXP_OBFF_WAKE 0x80000 /* Re-use WAKE# for OBFF */ +#define PCI_EXP_DEVCAP2_ARI 0x00000020 /* Alternative Routing-ID */ +#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_ARI 0x20 /* Alternative Routing-ID */ -#define PCI_EXP_IDO_REQ_EN 0x100 /* ID-based ordering request enable */ -#define PCI_EXP_IDO_CMP_EN 0x200 /* ID-based ordering completion enable */ -#define PCI_EXP_LTR_EN 0x400 /* Latency tolerance reporting */ -#define PCI_EXP_OBFF_MSGA_EN 0x2000 /* OBFF enable with Message type A */ -#define PCI_EXP_OBFF_MSGB_EN 0x4000 /* OBFF enable with Message type B */ -#define PCI_EXP_OBFF_WAKE_EN 0x6000 /* OBFF using WAKE# signaling */ +#define PCI_EXP_DEVCTL2_COMP_TIMEOUT 0x000f /* Completion Timeout Value */ +#define PCI_EXP_DEVCTL2_ARI 0x0020 /* Alternative Routing-ID */ +#define PCI_EXP_DEVCTL2_IDO_REQ_EN 0x0100 /* Allow IDO for requests */ +#define PCI_EXP_DEVCTL2_IDO_CMP_EN 0x0200 /* Allow IDO for completions */ +#define PCI_EXP_DEVCTL2_LTR_EN 0x0400 /* Enable LTR mechanism */ +#define PCI_EXP_DEVCTL2_OBFF_MSGA_EN 0x2000 /* Enable OBFF Message type A */ +#define PCI_EXP_DEVCTL2_OBFF_MSGB_EN 0x4000 /* Enable OBFF Message type B */ +#define PCI_EXP_DEVCTL2_OBFF_WAKE_EN 0x6000 /* OBFF using WAKE# signaling */ +#define PCI_EXP_DEVSTA2 42 /* Device Status 2 */ +#define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 44 /* v2 endpoints end here */ +#define PCI_EXP_LNKCAP2 44 /* Link Capabilities 2 */ +#define PCI_EXP_LNKCAP2_SLS_2_5GB 0x00000002 /* Supported Speed 2.5GT/s */ +#define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5.0GT/s */ +#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8.0GT/s */ +#define PCI_EXP_LNKCAP2_CROSSLINK 0x00000100 /* Crosslink supported */ #define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ +#define PCI_EXP_LNKSTA2 50 /* Link Status 2 */ +#define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */ #define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */ +#define PCI_EXP_SLTSTA2 58 /* Slot Status 2 */ /* Extended Capabilities (PCI-X 2.0 and Express) */ #define PCI_EXT_CAP_ID(header) (header & 0x0000ffff) #define PCI_EXT_CAP_VER(header) ((header >> 16) & 0xf) #define PCI_EXT_CAP_NEXT(header) ((header >> 20) & 0xffc) -#define PCI_EXT_CAP_ID_ERR 1 -#define PCI_EXT_CAP_ID_VC 2 -#define PCI_EXT_CAP_ID_DSN 3 -#define PCI_EXT_CAP_ID_PWR 4 -#define PCI_EXT_CAP_ID_VNDR 11 -#define PCI_EXT_CAP_ID_ACS 13 -#define PCI_EXT_CAP_ID_ARI 14 -#define PCI_EXT_CAP_ID_ATS 15 -#define PCI_EXT_CAP_ID_SRIOV 16 -#define PCI_EXT_CAP_ID_LTR 24 +#define PCI_EXT_CAP_ID_ERR 0x01 /* Advanced Error Reporting */ +#define PCI_EXT_CAP_ID_VC 0x02 /* Virtual Channel Capability */ +#define PCI_EXT_CAP_ID_DSN 0x03 /* Device Serial Number */ +#define PCI_EXT_CAP_ID_PWR 0x04 /* Power Budgeting */ +#define PCI_EXT_CAP_ID_RCLD 0x05 /* Root Complex Link Declaration */ +#define PCI_EXT_CAP_ID_RCILC 0x06 /* Root Complex Internal Link Control */ +#define PCI_EXT_CAP_ID_RCEC 0x07 /* Root Complex Event Collector */ +#define PCI_EXT_CAP_ID_MFVC 0x08 /* Multi-Function VC Capability */ +#define PCI_EXT_CAP_ID_VC9 0x09 /* same as _VC */ +#define PCI_EXT_CAP_ID_RCRB 0x0A /* Root Complex RB? */ +#define PCI_EXT_CAP_ID_VNDR 0x0B /* Vendor-Specific */ +#define PCI_EXT_CAP_ID_CAC 0x0C /* Config Access - obsolete */ +#define PCI_EXT_CAP_ID_ACS 0x0D /* Access Control Services */ +#define PCI_EXT_CAP_ID_ARI 0x0E /* Alternate Routing ID */ +#define PCI_EXT_CAP_ID_ATS 0x0F /* Address Translation Services */ +#define PCI_EXT_CAP_ID_SRIOV 0x10 /* Single Root I/O Virtualization */ +#define PCI_EXT_CAP_ID_MRIOV 0x11 /* Multi Root I/O Virtualization */ +#define PCI_EXT_CAP_ID_MCAST 0x12 /* Multicast */ +#define PCI_EXT_CAP_ID_PRI 0x13 /* Page Request Interface */ +#define PCI_EXT_CAP_ID_AMD_XXX 0x14 /* Reserved for AMD */ +#define PCI_EXT_CAP_ID_REBAR 0x15 /* Resizable BAR */ +#define PCI_EXT_CAP_ID_DPA 0x16 /* Dynamic Power Allocation */ +#define PCI_EXT_CAP_ID_TPH 0x17 /* TPH Requester */ +#define PCI_EXT_CAP_ID_LTR 0x18 /* Latency Tolerance Reporting */ +#define PCI_EXT_CAP_ID_SECPCI 0x19 /* Secondary PCIe Capability */ +#define PCI_EXT_CAP_ID_PMUX 0x1A /* Protocol Multiplexing */ +#define PCI_EXT_CAP_ID_PASID 0x1B /* Process Address Space ID */ +#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PASID + +#define PCI_EXT_CAP_DSN_SIZEOF 12 +#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40 /* Advanced Error Reporting */ #define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ -#define PCI_ERR_UNC_TRAIN 0x00000001 /* Training */ +#define PCI_ERR_UNC_UND 0x00000001 /* Undefined */ #define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */ +#define PCI_ERR_UNC_SURPDN 0x00000020 /* Surprise Down */ #define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */ #define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */ #define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */ @@ -556,6 +689,11 @@ #define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */ #define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */ #define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */ +#define PCI_ERR_UNC_ACSV 0x00200000 /* ACS Violation */ +#define PCI_ERR_UNC_INTN 0x00400000 /* internal error */ +#define PCI_ERR_UNC_MCBTLP 0x00800000 /* MC blocked TLP */ +#define PCI_ERR_UNC_ATOMEG 0x01000000 /* Atomic egress blocked */ +#define PCI_ERR_UNC_TLPPRE 0x02000000 /* TLP prefix blocked */ #define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */ /* Same bits as above */ #define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */ @@ -566,6 +704,9 @@ #define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */ #define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */ #define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */ +#define PCI_ERR_COR_ADV_NFAT 0x00002000 /* Advisory Non-Fatal */ +#define PCI_ERR_COR_INTERNAL 0x00004000 /* Corrected Internal */ +#define PCI_ERR_COR_LOG_OVER 0x00008000 /* Header Log Overflow */ #define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */ /* Same bits as above */ #define PCI_ERR_CAP 24 /* Advanced Error Capabilities */ @@ -586,9 +727,9 @@ #define PCI_ERR_ROOT_COR_RCV 0x00000001 /* ERR_COR Received */ /* Multi ERR_COR Received */ #define PCI_ERR_ROOT_MULTI_COR_RCV 0x00000002 -/* ERR_FATAL/NONFATAL Recevied */ +/* ERR_FATAL/NONFATAL Received */ #define PCI_ERR_ROOT_UNCOR_RCV 0x00000004 -/* Multi ERR_FATAL/NONFATAL Recevied */ +/* Multi ERR_FATAL/NONFATAL Received */ #define PCI_ERR_ROOT_MULTI_UNCOR_RCV 0x00000008 #define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First Fatal */ #define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */ @@ -596,13 +737,36 @@ #define PCI_ERR_ROOT_ERR_SRC 52 /* Error Source Identification */ /* Virtual Channel */ -#define PCI_VC_PORT_REG1 4 -#define PCI_VC_PORT_REG2 8 +#define PCI_VC_PORT_CAP1 4 +#define PCI_VC_CAP1_EVCC 0x00000007 /* extended VC count */ +#define PCI_VC_CAP1_LPEVCC 0x00000070 /* low prio extended VC count */ +#define PCI_VC_CAP1_ARB_SIZE 0x00000c00 +#define PCI_VC_PORT_CAP2 8 +#define PCI_VC_CAP2_32_PHASE 0x00000002 +#define PCI_VC_CAP2_64_PHASE 0x00000004 +#define PCI_VC_CAP2_128_PHASE 0x00000008 +#define PCI_VC_CAP2_ARB_OFF 0xff000000 #define PCI_VC_PORT_CTRL 12 +#define PCI_VC_PORT_CTRL_LOAD_TABLE 0x00000001 #define PCI_VC_PORT_STATUS 14 +#define PCI_VC_PORT_STATUS_TABLE 0x00000001 #define PCI_VC_RES_CAP 16 +#define PCI_VC_RES_CAP_32_PHASE 0x00000002 +#define PCI_VC_RES_CAP_64_PHASE 0x00000004 +#define PCI_VC_RES_CAP_128_PHASE 0x00000008 +#define PCI_VC_RES_CAP_128_PHASE_TB 0x00000010 +#define PCI_VC_RES_CAP_256_PHASE 0x00000020 +#define PCI_VC_RES_CAP_ARB_OFF 0xff000000 #define PCI_VC_RES_CTRL 20 +#define PCI_VC_RES_CTRL_LOAD_TABLE 0x00010000 +#define PCI_VC_RES_CTRL_ARB_SELECT 0x000e0000 +#define PCI_VC_RES_CTRL_ID 0x07000000 +#define PCI_VC_RES_CTRL_ENABLE 0x80000000 #define PCI_VC_RES_STATUS 26 +#define PCI_VC_RES_STATUS_TABLE 0x00000001 +#define PCI_VC_RES_STATUS_NEGO 0x00000002 +#define PCI_CAP_VC_BASE_SIZEOF 0x10 +#define PCI_CAP_VC_PER_VC_SIZEOF 0x0C /* Power Budgeting */ #define PCI_PWR_DSR 4 /* Data Select Register */ @@ -615,9 +779,16 @@ #define PCI_PWR_DATA_RAIL(x) (((x) >> 18) & 7) /* Power Rail */ #define PCI_PWR_CAP 12 /* Capability */ #define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */ +#define PCI_EXT_CAP_PWR_SIZEOF 16 + +/* Vendor-Specific (VSEC, PCI_EXT_CAP_ID_VNDR) */ +#define PCI_VNDR_HEADER 4 /* Vendor-Specific Header */ +#define PCI_VNDR_HEADER_ID(x) ((x) & 0xffff) +#define PCI_VNDR_HEADER_REV(x) (((x) >> 16) & 0xf) +#define PCI_VNDR_HEADER_LEN(x) (((x) >> 20) & 0xfff) /* - * Hypertransport sub capability types + * HyperTransport sub capability types * * Unfortunately there are both 3 bit and 5 bit capability types defined * in the HT spec, catering for that is a little messy. You probably don't @@ -645,8 +816,10 @@ #define HT_CAPTYPE_DIRECT_ROUTE 0xB0 /* Direct routing configuration */ #define HT_CAPTYPE_VCSET 0xB8 /* Virtual Channel configuration */ #define HT_CAPTYPE_ERROR_RETRY 0xC0 /* Retry on error configuration */ -#define HT_CAPTYPE_GEN3 0xD0 /* Generation 3 hypertransport configuration */ -#define HT_CAPTYPE_PM 0xE0 /* Hypertransport powermanagement configuration */ +#define HT_CAPTYPE_GEN3 0xD0 /* Generation 3 HyperTransport configuration */ +#define HT_CAPTYPE_PM 0xE0 /* HyperTransport power management configuration */ +#define HT_CAP_SIZEOF_LONG 28 /* slave & primary */ +#define HT_CAP_SIZEOF_SHORT 24 /* host & secondary */ /* Alternative Routing-ID Interpretation */ #define PCI_ARI_CAP 0x04 /* ARI Capability Register */ @@ -657,6 +830,7 @@ #define PCI_ARI_CTRL_MFVC 0x0001 /* MFVC Function Groups Enable */ #define PCI_ARI_CTRL_ACS 0x0002 /* ACS Function Groups Enable */ #define PCI_ARI_CTRL_FG(x) (((x) >> 4) & 7) /* Function Group */ +#define PCI_EXT_CAP_ARI_SIZEOF 8 /* Address Translation Service */ #define PCI_ATS_CAP 0x04 /* ATS Capability Register */ @@ -666,6 +840,29 @@ #define PCI_ATS_CTRL_ENABLE 0x8000 /* ATS Enable */ #define PCI_ATS_CTRL_STU(x) ((x) & 0x1f) /* Smallest Translation Unit */ #define PCI_ATS_MIN_STU 12 /* shift of minimum STU block */ +#define PCI_EXT_CAP_ATS_SIZEOF 8 + +/* Page Request Interface */ +#define PCI_PRI_CTRL 0x04 /* PRI control register */ +#define PCI_PRI_CTRL_ENABLE 0x01 /* Enable */ +#define PCI_PRI_CTRL_RESET 0x02 /* Reset */ +#define PCI_PRI_STATUS 0x06 /* PRI status register */ +#define PCI_PRI_STATUS_RF 0x001 /* Response Failure */ +#define PCI_PRI_STATUS_UPRGI 0x002 /* Unexpected PRG index */ +#define PCI_PRI_STATUS_STOPPED 0x100 /* PRI Stopped */ +#define PCI_PRI_MAX_REQ 0x08 /* PRI max reqs supported */ +#define PCI_PRI_ALLOC_REQ 0x0c /* PRI max reqs allowed */ +#define PCI_EXT_CAP_PRI_SIZEOF 16 + +/* Process Address Space ID */ +#define PCI_PASID_CAP 0x04 /* PASID feature register */ +#define PCI_PASID_CAP_EXEC 0x02 /* Exec permissions Supported */ +#define PCI_PASID_CAP_PRIV 0x04 /* Privilege Mode Supported */ +#define PCI_PASID_CTRL 0x06 /* PASID control register */ +#define PCI_PASID_CTRL_ENABLE 0x01 /* Enable bit */ +#define PCI_PASID_CTRL_EXEC 0x02 /* Exec permissions Enable */ +#define PCI_PASID_CTRL_PRIV 0x04 /* Privilege Mode Enable */ +#define PCI_EXT_CAP_PASID_SIZEOF 8 /* Single Root I/O Virtualization */ #define PCI_SRIOV_CAP 0x04 /* SR-IOV Capabilities */ @@ -697,12 +894,14 @@ #define PCI_SRIOV_VFM_MI 0x1 /* Dormant.MigrateIn */ #define PCI_SRIOV_VFM_MO 0x2 /* Active.MigrateOut */ #define PCI_SRIOV_VFM_AV 0x3 /* Active.Available */ +#define PCI_EXT_CAP_SRIOV_SIZEOF 64 #define PCI_LTR_MAX_SNOOP_LAT 0x4 #define PCI_LTR_MAX_NOSNOOP_LAT 0x6 #define PCI_LTR_VALUE_MASK 0x000003ff #define PCI_LTR_SCALE_MASK 0x00001c00 #define PCI_LTR_SCALE_SHIFT 10 +#define PCI_EXT_CAP_LTR_SIZEOF 8 /* Access Control Service */ #define PCI_ACS_CAP 0x04 /* ACS Capability Register */ @@ -713,7 +912,38 @@ #define PCI_ACS_UF 0x10 /* Upstream Forwarding */ #define PCI_ACS_EC 0x20 /* P2P Egress Control */ #define PCI_ACS_DT 0x40 /* Direct Translated P2P */ +#define PCI_ACS_EGRESS_BITS 0x05 /* ACS Egress Control Vector Size */ #define PCI_ACS_CTRL 0x06 /* ACS Control Register */ #define PCI_ACS_EGRESS_CTL_V 0x08 /* ACS Egress Control Vector */ +#define PCI_VSEC_HDR 4 /* extended cap - vendor-specific */ +#define PCI_VSEC_HDR_LEN_SHIFT 20 /* shift for length field */ + +/* SATA capability */ +#define PCI_SATA_REGS 4 /* SATA REGs specifier */ +#define PCI_SATA_REGS_MASK 0xF /* location - BAR#/inline */ +#define PCI_SATA_REGS_INLINE 0xF /* REGS in config space */ +#define PCI_SATA_SIZEOF_SHORT 8 +#define PCI_SATA_SIZEOF_LONG 16 + +/* Resizable BARs */ +#define PCI_REBAR_CTRL 8 /* control register */ +#define PCI_REBAR_CTRL_NBAR_MASK (7 << 5) /* mask for # bars */ +#define PCI_REBAR_CTRL_NBAR_SHIFT 5 /* shift for # bars */ + +/* Dynamic Power Allocation */ +#define PCI_DPA_CAP 4 /* capability register */ +#define PCI_DPA_CAP_SUBSTATE_MASK 0x1F /* # substates - 1 */ +#define PCI_DPA_BASE_SIZEOF 16 /* size with 0 substates */ + +/* TPH Requester */ +#define PCI_TPH_CAP 4 /* capability register */ +#define PCI_TPH_CAP_LOC_MASK 0x600 /* location mask */ +#define PCI_TPH_LOC_NONE 0x000 /* no location */ +#define PCI_TPH_LOC_CAP 0x200 /* in capability */ +#define PCI_TPH_LOC_MSIX 0x400 /* in MSI-X */ +#define PCI_TPH_CAP_ST_MASK 0x07FF0000 /* st table mask */ +#define PCI_TPH_CAP_ST_SHIFT 16 /* st table shift */ +#define PCI_TPH_BASE_SIZEOF 12 /* size with no st table */ + #endif /* LINUX_PCI_REGS_H */ diff --git a/qemu/include/standard-headers/linux/types.h b/qemu/include/standard-headers/linux/types.h index 0526c2b87..9dbbc73e4 100644 --- a/qemu/include/standard-headers/linux/types.h +++ b/qemu/include/standard-headers/linux/types.h @@ -1,2 +1,3 @@ -#include -#include "qemu/compiler.h" +/* For QEMU all types are already defined via osdep.h, so this + * header does not need to do anything. + */ diff --git a/qemu/include/standard-headers/linux/virtio_balloon.h b/qemu/include/standard-headers/linux/virtio_balloon.h index 2e2a6dcf3..9d06ccd06 100644 --- a/qemu/include/standard-headers/linux/virtio_balloon.h +++ b/qemu/include/standard-headers/linux/virtio_balloon.h @@ -51,7 +51,8 @@ struct virtio_balloon_config { #define VIRTIO_BALLOON_S_MINFLT 3 /* Number of minor faults */ #define VIRTIO_BALLOON_S_MEMFREE 4 /* Total amount of free memory */ #define VIRTIO_BALLOON_S_MEMTOT 5 /* Total amount of memory */ -#define VIRTIO_BALLOON_S_NR 6 +#define VIRTIO_BALLOON_S_AVAIL 6 /* Available memory as in /proc */ +#define VIRTIO_BALLOON_S_NR 7 /* * Memory statistics structure. diff --git a/qemu/include/standard-headers/linux/virtio_blk.h b/qemu/include/standard-headers/linux/virtio_blk.h index cd601f406..ab16ec5fd 100644 --- a/qemu/include/standard-headers/linux/virtio_blk.h +++ b/qemu/include/standard-headers/linux/virtio_blk.h @@ -43,10 +43,10 @@ #ifndef VIRTIO_BLK_NO_LEGACY #define VIRTIO_BLK_F_BARRIER 0 /* Does host support barriers? */ #define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */ -#define VIRTIO_BLK_F_WCE 9 /* Writeback mode enabled after reset */ +#define VIRTIO_BLK_F_FLUSH 9 /* Flush command supported */ #define VIRTIO_BLK_F_CONFIG_WCE 11 /* Writeback mode available in config */ -/* Old (deprecated) name for VIRTIO_BLK_F_WCE. */ -#define VIRTIO_BLK_F_FLUSH VIRTIO_BLK_F_WCE +/* Old (deprecated) name for VIRTIO_BLK_F_FLUSH. */ +#define VIRTIO_BLK_F_WCE VIRTIO_BLK_F_FLUSH #endif /* !VIRTIO_BLK_NO_LEGACY */ #define VIRTIO_BLK_ID_BYTES 20 /* ID string length */ diff --git a/qemu/include/standard-headers/linux/virtio_gpu.h b/qemu/include/standard-headers/linux/virtio_gpu.h index 72ef815f5..c1c8f0751 100644 --- a/qemu/include/standard-headers/linux/virtio_gpu.h +++ b/qemu/include/standard-headers/linux/virtio_gpu.h @@ -40,6 +40,8 @@ #include "standard-headers/linux/types.h" +#define VIRTIO_GPU_F_VIRGL 0 + enum virtio_gpu_ctrl_type { VIRTIO_GPU_UNDEFINED = 0, @@ -52,6 +54,18 @@ enum virtio_gpu_ctrl_type { VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, + VIRTIO_GPU_CMD_GET_CAPSET_INFO, + VIRTIO_GPU_CMD_GET_CAPSET, + + /* 3d commands */ + VIRTIO_GPU_CMD_CTX_CREATE = 0x0200, + VIRTIO_GPU_CMD_CTX_DESTROY, + VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, + VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE, + VIRTIO_GPU_CMD_RESOURCE_CREATE_3D, + VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, + VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D, + VIRTIO_GPU_CMD_SUBMIT_3D, /* cursor commands */ VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300, @@ -60,6 +74,8 @@ enum virtio_gpu_ctrl_type { /* success responses */ VIRTIO_GPU_RESP_OK_NODATA = 0x1100, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, + VIRTIO_GPU_RESP_OK_CAPSET_INFO, + VIRTIO_GPU_RESP_OK_CAPSET, /* error responses */ VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200, @@ -180,13 +196,107 @@ struct virtio_gpu_resp_display_info { } pmodes[VIRTIO_GPU_MAX_SCANOUTS]; }; +/* data passed in the control vq, 3d related */ + +struct virtio_gpu_box { + uint32_t x, y, z; + uint32_t w, h, d; +}; + +/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D */ +struct virtio_gpu_transfer_host_3d { + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_box box; + uint64_t offset; + uint32_t resource_id; + uint32_t level; + uint32_t stride; + uint32_t layer_stride; +}; + +/* VIRTIO_GPU_CMD_RESOURCE_CREATE_3D */ +#define VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP (1 << 0) +struct virtio_gpu_resource_create_3d { + struct virtio_gpu_ctrl_hdr hdr; + uint32_t resource_id; + uint32_t target; + uint32_t format; + uint32_t bind; + uint32_t width; + uint32_t height; + uint32_t depth; + uint32_t array_size; + uint32_t last_level; + uint32_t nr_samples; + uint32_t flags; + uint32_t padding; +}; + +/* VIRTIO_GPU_CMD_CTX_CREATE */ +struct virtio_gpu_ctx_create { + struct virtio_gpu_ctrl_hdr hdr; + uint32_t nlen; + uint32_t padding; + char debug_name[64]; +}; + +/* VIRTIO_GPU_CMD_CTX_DESTROY */ +struct virtio_gpu_ctx_destroy { + struct virtio_gpu_ctrl_hdr hdr; +}; + +/* VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE */ +struct virtio_gpu_ctx_resource { + struct virtio_gpu_ctrl_hdr hdr; + uint32_t resource_id; + uint32_t padding; +}; + +/* VIRTIO_GPU_CMD_SUBMIT_3D */ +struct virtio_gpu_cmd_submit { + struct virtio_gpu_ctrl_hdr hdr; + uint32_t size; + uint32_t padding; +}; + +#define VIRTIO_GPU_CAPSET_VIRGL 1 + +/* VIRTIO_GPU_CMD_GET_CAPSET_INFO */ +struct virtio_gpu_get_capset_info { + struct virtio_gpu_ctrl_hdr hdr; + uint32_t capset_index; + uint32_t padding; +}; + +/* VIRTIO_GPU_RESP_OK_CAPSET_INFO */ +struct virtio_gpu_resp_capset_info { + struct virtio_gpu_ctrl_hdr hdr; + uint32_t capset_id; + uint32_t capset_max_version; + uint32_t capset_max_size; + uint32_t padding; +}; + +/* VIRTIO_GPU_CMD_GET_CAPSET */ +struct virtio_gpu_get_capset { + struct virtio_gpu_ctrl_hdr hdr; + uint32_t capset_id; + uint32_t capset_version; +}; + +/* VIRTIO_GPU_RESP_OK_CAPSET */ +struct virtio_gpu_resp_capset { + struct virtio_gpu_ctrl_hdr hdr; + uint8_t capset_data[]; +}; + #define VIRTIO_GPU_EVENT_DISPLAY (1 << 0) struct virtio_gpu_config { uint32_t events_read; uint32_t events_clear; uint32_t num_scanouts; - uint32_t reserved; + uint32_t num_capsets; }; /* simple formats for fbcon/X use */ diff --git a/qemu/include/standard-headers/linux/virtio_ring.h b/qemu/include/standard-headers/linux/virtio_ring.h index 6fe276faf..023c6db04 100644 --- a/qemu/include/standard-headers/linux/virtio_ring.h +++ b/qemu/include/standard-headers/linux/virtio_ring.h @@ -31,6 +31,7 @@ * SUCH DAMAGE. * * Copyright Rusty Russell IBM Corporation 2007. */ +#include #include "standard-headers/linux/types.h" #include "standard-headers/linux/virtio_types.h" @@ -143,7 +144,7 @@ static inline void vring_init(struct vring *vr, unsigned int num, void *p, vr->num = num; vr->desc = p; vr->avail = p + num*sizeof(struct vring_desc); - vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + sizeof(__virtio16) + vr->used = (void *)(((uintptr_t)&vr->avail->ring[num] + sizeof(__virtio16) + align-1) & ~(align - 1)); } diff --git a/qemu/include/sysemu/accel.h b/qemu/include/sysemu/accel.h index 997720f36..a74b2faf5 100644 --- a/qemu/include/sysemu/accel.h +++ b/qemu/include/sysemu/accel.h @@ -23,7 +23,6 @@ #ifndef HW_ACCEL_H #define HW_ACCEL_H -#include "qemu/typedefs.h" #include "qom/object.h" typedef struct AccelState { diff --git a/qemu/include/sysemu/balloon.h b/qemu/include/sysemu/balloon.h index 17fe30070..3f976b49e 100644 --- a/qemu/include/sysemu/balloon.h +++ b/qemu/include/sysemu/balloon.h @@ -22,5 +22,7 @@ typedef void (QEMUBalloonStatus)(void *opaque, BalloonInfo *info); int qemu_add_balloon_handler(QEMUBalloonEvent *event_func, QEMUBalloonStatus *stat_func, void *opaque); void qemu_remove_balloon_handler(void *opaque); +bool qemu_balloon_is_inhibited(void); +void qemu_balloon_inhibit(bool state); #endif diff --git a/qemu/include/sysemu/block-backend.h b/qemu/include/sysemu/block-backend.h index 8fc960fcb..c62b6fe96 100644 --- a/qemu/include/sysemu/block-backend.h +++ b/qemu/include/sysemu/block-backend.h @@ -13,8 +13,7 @@ #ifndef BLOCK_BACKEND_H #define BLOCK_BACKEND_H -#include "qemu/typedefs.h" -#include "qapi/error.h" +#include "qemu/iov.h" /* * TODO Have to include block/block.h for a bunch of block layer @@ -60,22 +59,32 @@ typedef struct BlockDevOps { void (*resize_cb)(void *opaque); } BlockDevOps; -BlockBackend *blk_new(const char *name, Error **errp); -BlockBackend *blk_new_with_bs(const char *name, Error **errp); -BlockBackend *blk_new_open(const char *name, const char *filename, - const char *reference, QDict *options, int flags, - Error **errp); +BlockBackend *blk_new(Error **errp); +BlockBackend *blk_new_with_bs(Error **errp); +BlockBackend *blk_new_open(const char *filename, const char *reference, + QDict *options, int flags, Error **errp); +int blk_get_refcnt(BlockBackend *blk); void blk_ref(BlockBackend *blk); void blk_unref(BlockBackend *blk); +void blk_remove_all_bs(void); const char *blk_name(BlockBackend *blk); BlockBackend *blk_by_name(const char *name); BlockBackend *blk_next(BlockBackend *blk); +BlockDriverState *blk_next_root_bs(BlockDriverState *bs); +bool monitor_add_blk(BlockBackend *blk, const char *name, Error **errp); +void monitor_remove_blk(BlockBackend *blk); BlockDriverState *blk_bs(BlockBackend *blk); +void blk_remove_bs(BlockBackend *blk); +void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs); -void blk_hide_on_behalf_of_hmp_drive_del(BlockBackend *blk); - +void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow); void blk_iostatus_enable(BlockBackend *blk); +bool blk_iostatus_is_enabled(const BlockBackend *blk); +BlockDeviceIoStatus blk_iostatus(const BlockBackend *blk); +void blk_iostatus_disable(BlockBackend *blk); +void blk_iostatus_reset(BlockBackend *blk); +void blk_iostatus_set_err(BlockBackend *blk, int error); int blk_attach_dev(BlockBackend *blk, void *dev); void blk_attach_dev_nofail(BlockBackend *blk, void *dev); void blk_detach_dev(BlockBackend *blk, void *dev); @@ -118,8 +127,11 @@ int blk_co_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors); int blk_co_flush(BlockBackend *blk); int blk_flush(BlockBackend *blk); int blk_flush_all(void); +int blk_commit_all(void); void blk_drain(BlockBackend *blk); void blk_drain_all(void); +void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, + BlockdevOnError on_write_error); BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read); BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, int error); @@ -130,12 +142,15 @@ int blk_is_sg(BlockBackend *blk); int blk_enable_write_cache(BlockBackend *blk); void blk_set_enable_write_cache(BlockBackend *blk, bool wce); void blk_invalidate_cache(BlockBackend *blk, Error **errp); -int blk_is_inserted(BlockBackend *blk); +bool blk_is_inserted(BlockBackend *blk); +bool blk_is_available(BlockBackend *blk); void blk_lock_medium(BlockBackend *blk, bool locked); void blk_eject(BlockBackend *blk, bool eject_flag); int blk_get_flags(BlockBackend *blk); int blk_get_max_transfer_length(BlockBackend *blk); +int blk_get_max_iov(BlockBackend *blk); void blk_set_guest_block_size(BlockBackend *blk, int align); +void *blk_try_blockalign(BlockBackend *blk, size_t size); void *blk_blockalign(BlockBackend *blk, size_t size); bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp); void blk_op_unblock(BlockBackend *blk, BlockOpType op, Error *reason); @@ -151,10 +166,15 @@ void blk_remove_aio_context_notifier(BlockBackend *blk, void *), void (*detach_aio_context)(void *), void *opaque); -void blk_add_close_notifier(BlockBackend *blk, Notifier *notify); +void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify); +void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify); void blk_io_plug(BlockBackend *blk); void blk_io_unplug(BlockBackend *blk); BlockAcctStats *blk_get_stats(BlockBackend *blk); +BlockBackendRootState *blk_get_root_state(BlockBackend *blk); +void blk_update_root_state(BlockBackend *blk); +void blk_apply_root_state(BlockBackend *blk, BlockDriverState *bs); +int blk_get_open_flags_from_root_state(BlockBackend *blk); void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, BlockCompletionFunc *cb, void *opaque); @@ -169,5 +189,8 @@ int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size); int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz); int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo); +BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, + BlockCompletionFunc *cb, + void *opaque, int ret); #endif diff --git a/qemu/include/sysemu/blockdev.h b/qemu/include/sysemu/blockdev.h index 310415025..16432f350 100644 --- a/qemu/include/sysemu/blockdev.h +++ b/qemu/include/sysemu/blockdev.h @@ -11,7 +11,6 @@ #define BLOCKDEV_H #include "block/block.h" -#include "qapi/error.h" #include "qemu/queue.h" void blockdev_mark_auto_del(BlockBackend *blk); @@ -20,7 +19,7 @@ void blockdev_auto_del(BlockBackend *blk); typedef enum { IF_DEFAULT = -1, /* for use with drive_add() only */ /* - * IF_IDE must be zero, because we want QEMUMachine member + * IF_IDE must be zero, because we want MachineClass member * block_default_type to default-initialize to IF_IDE */ IF_IDE = 0, @@ -63,8 +62,6 @@ DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type); /* device-hotplug */ -void qmp_change_blockdev(const char *device, const char *filename, - const char *format, Error **errp); void hmp_commit(Monitor *mon, const QDict *qdict); void hmp_drive_del(Monitor *mon, const QDict *qdict); #endif diff --git a/qemu/include/sysemu/char.h b/qemu/include/sysemu/char.h index 832b7fead..307fd8fde 100644 --- a/qemu/include/sysemu/char.h +++ b/qemu/include/sysemu/char.h @@ -41,6 +41,11 @@ typedef struct { #define CHR_IOCTL_PP_EPP_WRITE 11 #define CHR_IOCTL_PP_DATA_DIR 12 +struct ParallelIOArg { + void *buffer; + int count; +}; + #define CHR_IOCTL_SERIAL_SET_TIOCM 13 #define CHR_IOCTL_SERIAL_GET_TIOCM 14 @@ -77,6 +82,7 @@ struct CharDriverState { void *opaque; char *label; char *filename; + int logfd; int be_open; int fe_open; int explicit_fe_open; @@ -85,17 +91,20 @@ struct CharDriverState { int is_mux; guint fd_in_tag; QemuOpts *opts; + bool replay; QTAILQ_ENTRY(CharDriverState) next; }; /** - * @qemu_chr_alloc: + * qemu_chr_alloc: + * @backend: the common backend config + * @errp: pointer to a NULL-initialized error object * * Allocate and initialize a new CharDriverState. * - * Returns: a newly allocated CharDriverState. + * Returns: a newly allocated CharDriverState, or NULL on error. */ -CharDriverState *qemu_chr_alloc(void); +CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp); /** * @qemu_chr_new_from_opts: @@ -111,6 +120,16 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, void (*init)(struct CharDriverState *s), Error **errp); +/** + * @qemu_chr_parse_common: + * + * Parse the common options available to all character backends. + * + * @opts the options that still need parsing + * @backend a new backend + */ +void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend); + /** * @qemu_chr_new: * @@ -125,13 +144,37 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*init)(struct CharDriverState *s)); +/** + * @qemu_chr_new_noreplay: + * + * Create a new character backend from a URI. + * Character device communications are not written + * into the replay log. + * + * @label the name of the backend + * @filename the URI + * @init not sure.. + * + * Returns: a new character backend + */ +CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename, + void (*init)(struct CharDriverState *s)); + /** * @qemu_chr_delete: * - * Destroy a character backend. + * Destroy a character backend and remove it from the list of + * identified character backends. */ void qemu_chr_delete(CharDriverState *chr); +/** + * @qemu_chr_free: + * + * Destroy a character backend. + */ +void qemu_chr_free(CharDriverState *chr); + /** * @qemu_chr_fe_set_echo: * @@ -320,6 +363,15 @@ int qemu_chr_be_can_write(CharDriverState *s); */ void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len); +/** + * @qemu_chr_be_write_impl: + * + * Implementation of back end writing. Used by replay module. + * + * @buf a buffer to receive data from the front end + * @len the number of bytes to receive from the front end + */ +void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len); /** * @qemu_chr_be_event: @@ -345,27 +397,16 @@ bool chr_is_ringbuf(const CharDriverState *chr); QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename); void register_char_driver(const char *name, ChardevBackendKind kind, - void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp)); - -/* add an eventfd to the qemu devices that are polled */ -CharDriverState *qemu_chr_open_eventfd(int eventfd); + void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp), + CharDriverState *(*create)(const char *id, ChardevBackend *backend, + ChardevReturn *ret, Error **errp)); extern int term_escape_char; CharDriverState *qemu_char_get_next_serial(void); -/* msmouse */ -CharDriverState *qemu_chr_open_msmouse(void); - -/* testdev.c */ -CharDriverState *chr_testdev_init(void); - -/* baum.c */ -CharDriverState *chr_baum_init(void); - /* console.c */ -typedef CharDriverState *(VcHandler)(ChardevVC *vc); - +typedef CharDriverState *(VcHandler)(ChardevVC *vc, Error **errp); void register_vc_handler(VcHandler *handler); -CharDriverState *vc_init(ChardevVC *vc); + #endif diff --git a/qemu/include/sysemu/cpus.h b/qemu/include/sysemu/cpus.h index 3f162a9e0..3d1e5ba1e 100644 --- a/qemu/include/sysemu/cpus.h +++ b/qemu/include/sysemu/cpus.h @@ -2,6 +2,7 @@ #define QEMU_CPUS_H /* cpus.c */ +bool qemu_in_vcpu_thread(void); void qemu_init_cpu_loop(void); void resume_all_vcpus(void); void pause_all_vcpus(void); @@ -10,7 +11,6 @@ void cpu_stop_current(void); void cpu_synchronize_all_states(void); void cpu_synchronize_all_post_reset(void); void cpu_synchronize_all_post_init(void); -void cpu_clean_all_dirty(void); void qtest_clock_warp(int64_t dest); diff --git a/qemu/include/sysemu/device_tree.h b/qemu/include/sysemu/device_tree.h index 359e14304..705650aad 100644 --- a/qemu/include/sysemu/device_tree.h +++ b/qemu/include/sysemu/device_tree.h @@ -16,6 +16,32 @@ void *create_device_tree(int *sizep); void *load_device_tree(const char *filename_path, int *sizep); +#ifdef CONFIG_LINUX +/** + * load_device_tree_from_sysfs: reads the device tree information in the + * /proc/device-tree directory and return the corresponding binary blob + * buffer pointer. Asserts in case of error. + */ +void *load_device_tree_from_sysfs(void); +#endif + +/** + * qemu_fdt_node_path: return the paths of nodes matching a given + * name and compat string + * @fdt: pointer to the dt blob + * @name: node name + * @compat: compatibility string + * @errp: handle to an error object + * + * returns a newly allocated NULL-terminated array of node paths. + * Use g_strfreev() to free it. If one or more nodes were found, the + * array contains the path of each node and the last element equals to + * NULL. If there is no error but no matching node was found, the + * returned array contains a single element equal to NULL. If an error + * was encountered when parsing the blob, the function returns NULL + */ +char **qemu_fdt_node_path(void *fdt, const char *name, char *compat, + Error **errp); int qemu_fdt_setprop(void *fdt, const char *node_path, const char *property, const void *val, int size); @@ -28,10 +54,33 @@ int qemu_fdt_setprop_string(void *fdt, const char *node_path, int qemu_fdt_setprop_phandle(void *fdt, const char *node_path, const char *property, const char *target_node_path); +/** + * qemu_fdt_getprop: retrieve the value of a given property + * @fdt: pointer to the device tree blob + * @node_path: node path + * @property: name of the property to find + * @lenp: fdt error if any or length of the property on success + * @errp: handle to an error object + * + * returns a pointer to the property on success and NULL on failure + */ const void *qemu_fdt_getprop(void *fdt, const char *node_path, - const char *property, int *lenp); + const char *property, int *lenp, + Error **errp); +/** + * qemu_fdt_getprop_cell: retrieve the value of a given 4 byte property + * @fdt: pointer to the device tree blob + * @node_path: node path + * @property: name of the property to find + * @lenp: fdt error if any or -EINVAL if the property size is different from + * 4 bytes, or 4 (expected length of the property) upon success. + * @errp: handle to an error object + * + * returns the property value on success + */ uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path, - const char *property); + const char *property, int *lenp, + Error **errp); uint32_t qemu_fdt_get_phandle(void *fdt, const char *path); uint32_t qemu_fdt_alloc_phandle(void *fdt); int qemu_fdt_nop_node(void *fdt, const char *node_path); diff --git a/qemu/include/sysemu/dma.h b/qemu/include/sysemu/dma.h index efa8b9993..b0fbb9bb3 100644 --- a/qemu/include/sysemu/dma.h +++ b/qemu/include/sysemu/dma.h @@ -10,7 +10,6 @@ #ifndef DMA_H #define DMA_H -#include #include "exec/memory.h" #include "exec/address-spaces.h" #include "hw/hw.h" diff --git a/qemu/include/sysemu/dump-arch.h b/qemu/include/sysemu/dump-arch.h index 9c95cede3..e25b02e99 100644 --- a/qemu/include/sysemu/dump-arch.h +++ b/qemu/include/sysemu/dump-arch.h @@ -15,9 +15,12 @@ #define DUMP_ARCH_H typedef struct ArchDumpInfo { - int d_machine; /* Architecture */ - int d_endian; /* ELFDATA2LSB or ELFDATA2MSB */ - int d_class; /* ELFCLASS32 or ELFCLASS64 */ + int d_machine; /* Architecture */ + int d_endian; /* ELFDATA2LSB or ELFDATA2MSB */ + int d_class; /* ELFCLASS32 or ELFCLASS64 */ + uint32_t page_size; /* The target's page size. If it's variable and + * unknown, then this should be the maximum. */ + uint64_t phys_base; /* The target's physmem base. */ } ArchDumpInfo; struct GuestPhysBlockList; /* memory_mapping.h */ diff --git a/qemu/include/sysemu/dump.h b/qemu/include/sysemu/dump.h index 7e4ec5c7d..ef931be46 100644 --- a/qemu/include/sysemu/dump.h +++ b/qemu/include/sysemu/dump.h @@ -20,12 +20,9 @@ #define VERSION_FLAT_HEADER (1) /* version of flattened format */ #define END_FLAG_FLAT_HEADER (-1) +#ifndef ARCH_PFN_OFFSET #define ARCH_PFN_OFFSET (0) - -#define paddr_to_pfn(X) \ - (((unsigned long long)(X) >> TARGET_PAGE_BITS) - ARCH_PFN_OFFSET) -#define pfn_to_paddr(X) \ - (((unsigned long long)(X) + ARCH_PFN_OFFSET) << TARGET_PAGE_BITS) +#endif /* * flag for compressed format @@ -36,15 +33,12 @@ #define KDUMP_SIGNATURE "KDUMP " #define SIG_LEN (sizeof(KDUMP_SIGNATURE) - 1) -#define PHYS_BASE (0) #define DUMP_LEVEL (1) #define DISKDUMP_HEADER_BLOCKS (1) -#define BUFSIZE_BITMAP (TARGET_PAGE_SIZE) -#define PFN_BUFBITMAP (CHAR_BIT * BUFSIZE_BITMAP) -#define BUFSIZE_DATA_CACHE (TARGET_PAGE_SIZE * 4) #include "sysemu/dump-arch.h" #include "sysemu/memory_mapping.h" +#include "qapi-types.h" typedef struct QEMU_PACKED MakedumpfileHeader { char signature[16]; /* = "makedumpfile" */ @@ -183,6 +177,20 @@ typedef struct DumpState { off_t offset_page; /* offset of page part in vmcore */ size_t num_dumpable; /* number of page that can be dumped */ uint32_t flag_compress; /* indicate the compression format */ + DumpStatus status; /* current dump status */ + + bool has_format; /* whether format is provided */ + DumpGuestMemoryFormat format; /* valid only if has_format == true */ + QemuThread dump_thread; /* thread for detached dump */ + + int64_t total_size; /* total memory size (in bytes) to + * be dumped. When filter is + * enabled, this will only count + * those to be written. */ + int64_t written_size; /* written memory size (in bytes), + * this could be used to calculate + * how much work we have + * finished. */ } DumpState; uint16_t cpu_to_dump16(DumpState *s, uint16_t val); diff --git a/qemu/include/sysemu/hostmem.h b/qemu/include/sysemu/hostmem.h index 1ce439415..a19801d20 100644 --- a/qemu/include/sysemu/hostmem.h +++ b/qemu/include/sysemu/hostmem.h @@ -14,7 +14,6 @@ #include "sysemu/sysemu.h" /* for MAX_NODES */ #include "qom/object.h" -#include "qapi/error.h" #include "exec/memory.h" #include "qemu/option.h" #include "qemu/bitmap.h" diff --git a/qemu/include/sysemu/kvm.h b/qemu/include/sysemu/kvm.h index 983e99e1e..0e18f15c9 100644 --- a/qemu/include/sysemu/kvm.h +++ b/qemu/include/sysemu/kvm.h @@ -14,8 +14,6 @@ #ifndef QEMU_KVM_H #define QEMU_KVM_H -#include -#include "config-host.h" #include "qemu/queue.h" #include "qom/cpu.h" #include "exec/memattrs.h" @@ -43,6 +41,7 @@ extern bool kvm_allowed; extern bool kvm_kernel_irqchip; +extern bool kvm_split_irqchip; extern bool kvm_async_interrupts_allowed; extern bool kvm_halt_in_kernel_allowed; extern bool kvm_eventfds_allowed; @@ -52,6 +51,8 @@ extern bool kvm_msi_via_irqfd_allowed; extern bool kvm_gsi_routing_allowed; extern bool kvm_gsi_direct_mapping; extern bool kvm_readonly_mem_allowed; +extern bool kvm_direct_msi_allowed; +extern bool kvm_ioeventfd_any_length_allowed; #if defined CONFIG_KVM || !defined NEED_CPU_H #define kvm_enabled() (kvm_allowed) @@ -68,6 +69,16 @@ extern bool kvm_readonly_mem_allowed; */ #define kvm_irqchip_in_kernel() (kvm_kernel_irqchip) +/** + * kvm_irqchip_is_split: + * + * Returns: true if the user asked us to split the irqchip + * implementation between user and kernel space. The details are + * architecture and machine specific. On PC, it means that the PIC, + * IOAPIC, and PIT are in user space while the LAPIC is in the kernel. + */ +#define kvm_irqchip_is_split() (kvm_split_irqchip) + /** * kvm_async_interrupts_enabled: * @@ -145,9 +156,23 @@ extern bool kvm_readonly_mem_allowed; */ #define kvm_readonly_mem_enabled() (kvm_readonly_mem_allowed) +/** + * kvm_direct_msi_enabled: + * + * Returns: true if KVM allows direct MSI injection. + */ +#define kvm_direct_msi_enabled() (kvm_direct_msi_allowed) + +/** + * kvm_ioeventfd_any_length_enabled: + * Returns: true if KVM allows any length io eventfd. + */ +#define kvm_ioeventfd_any_length_enabled() (kvm_ioeventfd_any_length_allowed) + #else #define kvm_enabled() (0) #define kvm_irqchip_in_kernel() (false) +#define kvm_irqchip_is_split() (false) #define kvm_async_interrupts_enabled() (false) #define kvm_halt_in_kernel() (false) #define kvm_eventfds_enabled() (false) @@ -157,6 +182,8 @@ extern bool kvm_readonly_mem_allowed; #define kvm_gsi_routing_allowed() (false) #define kvm_gsi_direct_mapping() (false) #define kvm_readonly_mem_enabled() (false) +#define kvm_direct_msi_enabled() (false) +#define kvm_ioeventfd_any_length_enabled() (false) #endif struct kvm_run; @@ -182,8 +209,6 @@ int kvm_has_sync_mmu(void); int kvm_has_vcpu_events(void); int kvm_has_robust_singlestep(void); int kvm_has_debugregs(void); -int kvm_has_xsave(void); -int kvm_has_xcrs(void); int kvm_has_pit_state2(void); int kvm_has_many_ioeventfds(void); int kvm_has_gsi_routing(void); @@ -210,6 +235,10 @@ int kvm_set_signal_mask(CPUState *cpu, const sigset_t *sigset); int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr); int kvm_on_sigbus(int code, void *addr); +/* interface with exec.c */ + +void phys_mem_set_alloc(void *(*alloc)(size_t, uint64_t *align)); + /* internal API */ int kvm_ioctl(KVMState *s, int type, ...); @@ -239,6 +268,32 @@ int kvm_device_ioctl(int fd, int type, ...); */ int kvm_vm_check_attr(KVMState *s, uint32_t group, uint64_t attr); +/** + * kvm_device_check_attr - check for existence of a specific device attribute + * @fd: The device file descriptor + * @group: the group + * @attr: the attribute of that group to query for + * + * Returns: 1 if the attribute exists + * 0 if the attribute either does not exist or if the vm device + * interface is unavailable + */ +int kvm_device_check_attr(int fd, uint32_t group, uint64_t attr); + +/** + * kvm_device_access - set or get value of a specific vm attribute + * @fd: The device file descriptor + * @group: the group + * @attr: the attribute of that group to set or get + * @val: pointer to a storage area for the value + * @write: true for set and false for get operation + * + * This function is not allowed to fail. Use kvm_device_check_attr() + * in order to check for the availability of optional attributes. + */ +void kvm_device_access(int fd, int group, uint64_t attr, + void *val, bool write); + /** * kvm_create_device - create a KVM device for the device control API * @KVMState: The KVMState pointer @@ -251,6 +306,15 @@ int kvm_vm_check_attr(KVMState *s, uint32_t group, uint64_t attr); */ int kvm_create_device(KVMState *s, uint64_t type, bool test); +/** + * kvm_device_supported - probe whether KVM supports specific device + * + * @vmfd: The fd handler for VM + * @type: type of device + * + * @return: true if supported, otherwise false. + */ +bool kvm_device_supported(int vmfd, uint64_t type); /* Arch specific hooks */ @@ -261,6 +325,8 @@ MemTxAttrs kvm_arch_post_run(CPUState *cpu, struct kvm_run *run); int kvm_arch_handle_exit(CPUState *cpu, struct kvm_run *run); +int kvm_arch_handle_ioapic_eoi(CPUState *cpu, struct kvm_run *run); + int kvm_arch_process_async_events(CPUState *cpu); int kvm_arch_get_registers(CPUState *cpu); @@ -287,7 +353,7 @@ int kvm_arch_on_sigbus(int code, void *addr); void kvm_arch_init_irq_routing(KVMState *s); int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, - uint64_t address, uint32_t data); + uint64_t address, uint32_t data, PCIDevice *dev); int kvm_arch_msi_data_to_gsi(uint32_t data); @@ -380,7 +446,6 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram_addr, void kvm_cpu_synchronize_state(CPUState *cpu); void kvm_cpu_synchronize_post_reset(CPUState *cpu); void kvm_cpu_synchronize_post_init(CPUState *cpu); -void kvm_cpu_clean_state(CPUState *cpu); /* generic hooks - to be moved/refactored once there are more users */ @@ -405,18 +470,13 @@ static inline void cpu_synchronize_post_init(CPUState *cpu) } } -static inline void cpu_clean_state(CPUState *cpu) -{ - if (kvm_enabled()) { - kvm_cpu_clean_state(cpu); - } -} - -int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg); -int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg); +int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg, PCIDevice *dev); +int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg, + PCIDevice *dev); void kvm_irqchip_release_virq(KVMState *s, int virq); int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter); +int kvm_irqchip_add_hv_sint_route(KVMState *s, uint32_t vcpu, uint32_t sint); int kvm_irqchip_add_irqfd_notifier_gsi(KVMState *s, EventNotifier *n, EventNotifier *rn, int virq); @@ -434,6 +494,7 @@ void kvm_init_irq_routing(KVMState *s); /** * kvm_arch_irqchip_create: * @KVMState: The KVMState pointer + * @MachineState: The MachineState pointer * * Allow architectures to create an in-kernel irq chip themselves. * @@ -441,7 +502,7 @@ void kvm_init_irq_routing(KVMState *s); * 0: irq chip was not created * > 0: irq chip was created */ -int kvm_arch_irqchip_create(KVMState *s); +int kvm_arch_irqchip_create(MachineState *ms, KVMState *s); /** * kvm_set_one_reg - set a register value in KVM via KVM_SET_ONE_REG ioctl diff --git a/qemu/include/sysemu/memory_mapping.h b/qemu/include/sysemu/memory_mapping.h index a75d59a55..706152d53 100644 --- a/qemu/include/sysemu/memory_mapping.h +++ b/qemu/include/sysemu/memory_mapping.h @@ -15,7 +15,7 @@ #define MEMORY_MAPPING_H #include "qemu/queue.h" -#include "qemu/typedefs.h" +#include "exec/memory.h" typedef struct GuestPhysBlock { /* visible to guest, reflects PCI hole, etc */ @@ -27,6 +27,9 @@ typedef struct GuestPhysBlock { /* points into host memory */ uint8_t *host_addr; + /* points to the MemoryRegion that this block belongs to */ + MemoryRegion *mr; + QTAILQ_ENTRY(GuestPhysBlock) next; } GuestPhysBlock; diff --git a/qemu/include/sysemu/numa.h b/qemu/include/sysemu/numa.h index a6392bc50..bb184c9cf 100644 --- a/qemu/include/sysemu/numa.h +++ b/qemu/include/sysemu/numa.h @@ -1,7 +1,6 @@ #ifndef SYSEMU_NUMA_H #define SYSEMU_NUMA_H -#include #include "qemu/bitmap.h" #include "qemu/option.h" #include "sysemu/sysemu.h" diff --git a/qemu/include/sysemu/os-posix.h b/qemu/include/sysemu/os-posix.h index f1315213f..07e3e5ae9 100644 --- a/qemu/include/sysemu/os-posix.h +++ b/qemu/include/sysemu/os-posix.h @@ -26,7 +26,12 @@ #ifndef QEMU_OS_POSIX_H #define QEMU_OS_POSIX_H -#include +#include +#include +#include +#include +#include +#include void os_set_line_buffering(void); void os_set_proc_name(const char *s); @@ -35,6 +40,9 @@ void os_daemonize(void); void os_setup_post(void); int os_mlock(void); +#define closesocket(s) close(s) +#define ioctlsocket(s, r, v) ioctl(s, r, v) + typedef struct timeval qemu_timeval; #define qemu_gettimeofday(tp) gettimeofday(tp, NULL) diff --git a/qemu/include/sysemu/os-win32.h b/qemu/include/sysemu/os-win32.h index 4035c4fe5..17aad3b20 100644 --- a/qemu/include/sysemu/os-win32.h +++ b/qemu/include/sysemu/os-win32.h @@ -26,34 +26,9 @@ #ifndef QEMU_OS_WIN32_H #define QEMU_OS_WIN32_H -#include #include - -/* Workaround for older versions of MinGW. */ -#ifndef ECONNREFUSED -# define ECONNREFUSED WSAECONNREFUSED -#endif -#ifndef EINPROGRESS -# define EINPROGRESS WSAEINPROGRESS -#endif -#ifndef EHOSTUNREACH -# define EHOSTUNREACH WSAEHOSTUNREACH -#endif -#ifndef EINTR -# define EINTR WSAEINTR -#endif -#ifndef EINPROGRESS -# define EINPROGRESS WSAEINPROGRESS -#endif -#ifndef ENETUNREACH -# define ENETUNREACH WSAENETUNREACH -#endif -#ifndef ENOTCONN -# define ENOTCONN WSAENOTCONN -#endif -#ifndef EWOULDBLOCK -# define EWOULDBLOCK WSAEWOULDBLOCK -#endif +#include +#include #if defined(_WIN64) /* On w64, setjmp is implemented by _setjmp which needs a second parameter. @@ -73,11 +48,12 @@ #define siglongjmp(env, val) longjmp(env, val) /* Missing POSIX functions. Don't use MinGW-w64 macros. */ +#ifndef CONFIG_LOCALTIME_R #undef gmtime_r struct tm *gmtime_r(const time_t *timep, struct tm *result); #undef localtime_r struct tm *localtime_r(const time_t *timep, struct tm *result); - +#endif /* CONFIG_LOCALTIME_R */ static inline void os_setup_signal_handling(void) {} static inline void os_daemonize(void) {} @@ -85,7 +61,7 @@ static inline void os_setup_post(void) {} void os_set_line_buffering(void); static inline void os_set_proc_name(const char *dummy) {} -size_t getpagesize(void); +int getpagesize(void); #if !defined(EPROTONOSUPPORT) # define EPROTONOSUPPORT EINVAL @@ -109,4 +85,100 @@ static inline int os_mlock(void) return -ENOSYS; } +#define fsync _commit + +#if !defined(lseek) +# define lseek _lseeki64 +#endif + +int qemu_ftruncate64(int, int64_t); + +#if !defined(ftruncate) +# define ftruncate qemu_ftruncate64 +#endif + +static inline char *realpath(const char *path, char *resolved_path) +{ + _fullpath(resolved_path, path, _MAX_PATH); + return resolved_path; +} + + +/* We wrap all the sockets functions so that we can + * set errno based on WSAGetLastError() + */ + +#undef connect +#define connect qemu_connect_wrap +int qemu_connect_wrap(int sockfd, const struct sockaddr *addr, + socklen_t addrlen); + +#undef listen +#define listen qemu_listen_wrap +int qemu_listen_wrap(int sockfd, int backlog); + +#undef bind +#define bind qemu_bind_wrap +int qemu_bind_wrap(int sockfd, const struct sockaddr *addr, + socklen_t addrlen); + +#undef socket +#define socket qemu_socket_wrap +int qemu_socket_wrap(int domain, int type, int protocol); + +#undef accept +#define accept qemu_accept_wrap +int qemu_accept_wrap(int sockfd, struct sockaddr *addr, + socklen_t *addrlen); + +#undef shutdown +#define shutdown qemu_shutdown_wrap +int qemu_shutdown_wrap(int sockfd, int how); + +#undef ioctlsocket +#define ioctlsocket qemu_ioctlsocket_wrap +int qemu_ioctlsocket_wrap(int fd, int req, void *val); + +#undef closesocket +#define closesocket qemu_closesocket_wrap +int qemu_closesocket_wrap(int fd); + +#undef getsockopt +#define getsockopt qemu_getsockopt_wrap +int qemu_getsockopt_wrap(int sockfd, int level, int optname, + void *optval, socklen_t *optlen); + +#undef setsockopt +#define setsockopt qemu_setsockopt_wrap +int qemu_setsockopt_wrap(int sockfd, int level, int optname, + const void *optval, socklen_t optlen); + +#undef getpeername +#define getpeername qemu_getpeername_wrap +int qemu_getpeername_wrap(int sockfd, struct sockaddr *addr, + socklen_t *addrlen); + +#undef getsockname +#define getsockname qemu_getsockname_wrap +int qemu_getsockname_wrap(int sockfd, struct sockaddr *addr, + socklen_t *addrlen); + +#undef send +#define send qemu_send_wrap +ssize_t qemu_send_wrap(int sockfd, const void *buf, size_t len, int flags); + +#undef sendto +#define sendto qemu_sendto_wrap +ssize_t qemu_sendto_wrap(int sockfd, const void *buf, size_t len, int flags, + const struct sockaddr *addr, socklen_t addrlen); + +#undef recv +#define recv qemu_recv_wrap +ssize_t qemu_recv_wrap(int sockfd, void *buf, size_t len, int flags); + +#undef recvfrom +#define recvfrom qemu_recvfrom_wrap +ssize_t qemu_recvfrom_wrap(int sockfd, void *buf, size_t len, int flags, + struct sockaddr *addr, socklen_t *addrlen); + #endif diff --git a/qemu/include/sysemu/qtest.h b/qemu/include/sysemu/qtest.h index 05473b75a..70aa40aa7 100644 --- a/qemu/include/sysemu/qtest.h +++ b/qemu/include/sysemu/qtest.h @@ -15,7 +15,6 @@ #define QTEST_H #include "qemu-common.h" -#include "qapi/error.h" extern bool qtest_allowed; diff --git a/qemu/include/sysemu/replay.h b/qemu/include/sysemu/replay.h new file mode 100644 index 000000000..0a88393d2 --- /dev/null +++ b/qemu/include/sysemu/replay.h @@ -0,0 +1,136 @@ +#ifndef REPLAY_H +#define REPLAY_H + +/* + * replay.h + * + * Copyright (c) 2010-2015 Institute for System Programming + * of the Russian Academy of Sciences. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qapi-types.h" + +/* replay clock kinds */ +enum ReplayClockKind { + /* host_clock */ + REPLAY_CLOCK_HOST, + /* virtual_rt_clock */ + REPLAY_CLOCK_VIRTUAL_RT, + REPLAY_CLOCK_COUNT +}; +typedef enum ReplayClockKind ReplayClockKind; + +/* IDs of the checkpoints */ +enum ReplayCheckpoint { + CHECKPOINT_CLOCK_WARP_START, + CHECKPOINT_CLOCK_WARP_ACCOUNT, + CHECKPOINT_RESET_REQUESTED, + CHECKPOINT_SUSPEND_REQUESTED, + CHECKPOINT_CLOCK_VIRTUAL, + CHECKPOINT_CLOCK_HOST, + CHECKPOINT_CLOCK_VIRTUAL_RT, + CHECKPOINT_INIT, + CHECKPOINT_RESET, + CHECKPOINT_COUNT +}; +typedef enum ReplayCheckpoint ReplayCheckpoint; + +extern ReplayMode replay_mode; + +/* Replay process control functions */ + +/*! Enables recording or saving event log with specified parameters */ +void replay_configure(struct QemuOpts *opts); +/*! Initializes timers used for snapshotting and enables events recording */ +void replay_start(void); +/*! Closes replay log file and frees other resources. */ +void replay_finish(void); +/*! Adds replay blocker with the specified error description */ +void replay_add_blocker(Error *reason); + +/* Processing the instructions */ + +/*! Returns number of executed instructions. */ +uint64_t replay_get_current_step(void); +/*! Returns number of instructions to execute in replay mode. */ +int replay_get_instructions(void); +/*! Updates instructions counter in replay mode. */ +void replay_account_executed_instructions(void); + +/* Interrupts and exceptions */ + +/*! Called by exception handler to write or read + exception processing events. */ +bool replay_exception(void); +/*! Used to determine that exception is pending. + Does not proceed to the next event in the log. */ +bool replay_has_exception(void); +/*! Called by interrupt handlers to write or read + interrupt processing events. + \return true if interrupt should be processed */ +bool replay_interrupt(void); +/*! Tries to read interrupt event from the file. + Returns true, when interrupt request is pending */ +bool replay_has_interrupt(void); + +/* Processing clocks and other time sources */ + +/*! Save the specified clock */ +int64_t replay_save_clock(ReplayClockKind kind, int64_t clock); +/*! Read the specified clock from the log or return cached data */ +int64_t replay_read_clock(ReplayClockKind kind); +/*! Saves or reads the clock depending on the current replay mode. */ +#define REPLAY_CLOCK(clock, value) \ + (replay_mode == REPLAY_MODE_PLAY ? replay_read_clock((clock)) \ + : replay_mode == REPLAY_MODE_RECORD \ + ? replay_save_clock((clock), (value)) \ + : (value)) + +/* Events */ + +/*! Called when qemu shutdown is requested. */ +void replay_shutdown_request(void); +/*! Should be called at check points in the execution. + These check points are skipped, if they were not met. + Saves checkpoint in the SAVE mode and validates in the PLAY mode. + Returns 0 in PLAY mode if checkpoint was not found. + Returns 1 in all other cases. */ +bool replay_checkpoint(ReplayCheckpoint checkpoint); + +/* Asynchronous events queue */ + +/*! Disables storing events in the queue */ +void replay_disable_events(void); +/*! Returns true when saving events is enabled */ +bool replay_events_enabled(void); +/*! Adds bottom half event to the queue */ +void replay_bh_schedule_event(QEMUBH *bh); +/*! Adds input event to the queue */ +void replay_input_event(QemuConsole *src, InputEvent *evt); +/*! Adds input sync event to the queue */ +void replay_input_sync_event(void); +/*! Adds block layer event to the queue */ +void replay_block_event(QEMUBH *bh, uint64_t id); + +/* Character device */ + +/*! Registers char driver to save it's events */ +void replay_register_char_driver(struct CharDriverState *chr); +/*! Saves write to char device event to the log */ +void replay_chr_be_write(struct CharDriverState *s, uint8_t *buf, int len); +/*! Writes char write return value to the replay log. */ +void replay_char_write_event_save(int res, int offset); +/*! Reads char write return value from the replay log. */ +void replay_char_write_event_load(int *res, int *offset); +/*! Reads information about read_all character event. */ +int replay_char_read_all_load(uint8_t *buf); +/*! Writes character read_all error code into the replay log. */ +void replay_char_read_all_save_error(int res); +/*! Writes character read_all execution result into the replay log. */ +void replay_char_read_all_save_buf(uint8_t *buf, int offset); + +#endif diff --git a/qemu/include/sysemu/rng.h b/qemu/include/sysemu/rng.h index 0a27c9b88..45629c4c5 100644 --- a/qemu/include/sysemu/rng.h +++ b/qemu/include/sysemu/rng.h @@ -15,7 +15,6 @@ #include "qom/object.h" #include "qemu-common.h" -#include "qapi/error.h" #define TYPE_RNG_BACKEND "rng-backend" #define RNG_BACKEND(obj) \ @@ -25,6 +24,7 @@ #define RNG_BACKEND_CLASS(klass) \ OBJECT_CLASS_CHECK(RngBackendClass, (klass), TYPE_RNG_BACKEND) +typedef struct RngRequest RngRequest; typedef struct RngBackendClass RngBackendClass; typedef struct RngBackend RngBackend; @@ -32,13 +32,21 @@ typedef void (EntropyReceiveFunc)(void *opaque, const void *data, size_t size); +struct RngRequest +{ + EntropyReceiveFunc *receive_entropy; + uint8_t *data; + void *opaque; + size_t offset; + size_t size; + QSIMPLEQ_ENTRY(RngRequest) next; +}; + struct RngBackendClass { ObjectClass parent_class; - void (*request_entropy)(RngBackend *s, size_t size, - EntropyReceiveFunc *receive_entropy, void *opaque); - void (*cancel_requests)(RngBackend *s); + void (*request_entropy)(RngBackend *s, RngRequest *req); void (*opened)(RngBackend *s, Error **errp); }; @@ -49,8 +57,10 @@ struct RngBackend /*< protected >*/ bool opened; + QSIMPLEQ_HEAD(requests, RngRequest) requests; }; + /** * rng_backend_request_entropy: * @s: the backend to request entropy from @@ -71,12 +81,13 @@ void rng_backend_request_entropy(RngBackend *s, size_t size, void *opaque); /** - * rng_backend_cancel_requests: - * @s: the backend to cancel all pending requests in + * rng_backend_free_request: + * @s: the backend that created the request + * @req: the request to finalize * - * Cancels all pending requests submitted by @rng_backend_request_entropy. This - * should be used by a device during reset or in preparation for live migration - * to stop tracking any request. + * Used by child rng backend classes to finalize requests once they've been + * processed. The request is removed from the list of active requests and + * deleted. */ -void rng_backend_cancel_requests(RngBackend *s); +void rng_backend_finalize_request(RngBackend *s, RngRequest *req); #endif diff --git a/qemu/include/sysemu/seccomp.h b/qemu/include/sysemu/seccomp.h index 1189fa241..cfc06008c 100644 --- a/qemu/include/sysemu/seccomp.h +++ b/qemu/include/sysemu/seccomp.h @@ -16,7 +16,6 @@ #define QEMU_SECCOMP_H #include -#include "qemu/osdep.h" int seccomp_start(void); #endif diff --git a/qemu/include/sysemu/sysemu.h b/qemu/include/sysemu/sysemu.h index 44570d17e..38fb3cad3 100644 --- a/qemu/include/sysemu/sysemu.h +++ b/qemu/include/sysemu/sysemu.h @@ -2,7 +2,6 @@ #define SYSEMU_H /* Misc. things related to the system emulator. */ -#include "qemu/typedefs.h" #include "qemu/option.h" #include "qemu/queue.h" #include "qemu/timer.h" @@ -69,6 +68,8 @@ int qemu_reset_requested_get(void); void qemu_system_killed(int signal, pid_t pid); void qemu_devices_reset(void); void qemu_system_reset(bool report); +void qemu_system_guest_panicked(void); +size_t qemu_target_page_bits(void); void qemu_add_exit_notifier(Notifier *notify); void qemu_remove_exit_notifier(Notifier *notify); @@ -82,14 +83,52 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict); void qemu_announce_self(void); +/* Subcommands for QEMU_VM_COMMAND */ +enum qemu_vm_cmd { + MIG_CMD_INVALID = 0, /* Must be 0 */ + MIG_CMD_OPEN_RETURN_PATH, /* Tell the dest to open the Return path */ + MIG_CMD_PING, /* Request a PONG on the RP */ + + MIG_CMD_POSTCOPY_ADVISE, /* Prior to any page transfers, just + warn we might want to do PC */ + MIG_CMD_POSTCOPY_LISTEN, /* Start listening for incoming + pages as it's running. */ + MIG_CMD_POSTCOPY_RUN, /* Start execution */ + + MIG_CMD_POSTCOPY_RAM_DISCARD, /* A list of pages to discard that + were previously sent during + precopy but are dirty. */ + MIG_CMD_PACKAGED, /* Send a wrapped stream within this stream */ + MIG_CMD_MAX +}; + +#define MAX_VM_CMD_PACKAGED_SIZE (1ul << 24) + bool qemu_savevm_state_blocked(Error **errp); void qemu_savevm_state_begin(QEMUFile *f, const MigrationParams *params); void qemu_savevm_state_header(QEMUFile *f); -int qemu_savevm_state_iterate(QEMUFile *f); -void qemu_savevm_state_complete(QEMUFile *f); -void qemu_savevm_state_cancel(void); -uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size); +int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy); +void qemu_savevm_state_cleanup(void); +void qemu_savevm_state_complete_postcopy(QEMUFile *f); +void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only); +void qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size, + uint64_t *res_non_postcopiable, + uint64_t *res_postcopiable); +void qemu_savevm_command_send(QEMUFile *f, enum qemu_vm_cmd command, + uint16_t len, uint8_t *data); +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 QEMUSizedBuffer *qsb); +void qemu_savevm_send_postcopy_advise(QEMUFile *f); +void qemu_savevm_send_postcopy_listen(QEMUFile *f); +void qemu_savevm_send_postcopy_run(QEMUFile *f); + +void qemu_savevm_send_postcopy_ram_discard(QEMUFile *f, const char *name, + uint16_t len, + uint64_t *start_list, + uint64_t *length_list); + int qemu_loadvm_state(QEMUFile *f); typedef enum DisplayType @@ -132,6 +171,7 @@ extern int boot_menu; extern bool boot_strict; extern uint8_t *boot_splash_filedata; extern size_t boot_splash_filedata_size; +extern bool enable_mlock; extern uint8_t qemu_extra_params_fw[2]; extern QEMUClockType rtc_clock; extern const char *mem_path; @@ -194,7 +234,7 @@ void device_add_bootindex_property(Object *obj, int32_t *bootindex, void restore_boot_order(void *opaque); void validate_bootdevices(const char *devices, Error **errp); -/* handler to set the boot_device order for a specific type of QEMUMachine */ +/* handler to set the boot_device order for a specific type of MachineClass */ typedef void QEMUBootSetHandler(void *opaque, const char *boot_order, Error **errp); void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque); diff --git a/qemu/include/sysemu/tpm_backend.h b/qemu/include/sysemu/tpm_backend.h index 0a366be0f..e3ec80020 100644 --- a/qemu/include/sysemu/tpm_backend.h +++ b/qemu/include/sysemu/tpm_backend.h @@ -15,7 +15,6 @@ #include "qom/object.h" #include "qemu-common.h" -#include "qapi/error.h" #include "qapi-types.h" #include "qemu/option.h" #include "sysemu/tpm.h" diff --git a/qemu/include/sysemu/watchdog.h b/qemu/include/sysemu/watchdog.h index 3e9a97068..72a4da07a 100644 --- a/qemu/include/sysemu/watchdog.h +++ b/qemu/include/sysemu/watchdog.h @@ -24,6 +24,15 @@ #include "qemu/queue.h" +/* Possible values for action parameter. */ +#define WDT_RESET 1 /* Hard reset. */ +#define WDT_SHUTDOWN 2 /* Shutdown. */ +#define WDT_POWEROFF 3 /* Quit. */ +#define WDT_PAUSE 4 /* Pause. */ +#define WDT_DEBUG 5 /* Prints a message and continues running. */ +#define WDT_NONE 6 /* Do nothing. */ +#define WDT_NMI 7 /* Inject nmi into the guest. */ + struct WatchdogTimerModel { QLIST_ENTRY(WatchdogTimerModel) entry; @@ -37,6 +46,7 @@ typedef struct WatchdogTimerModel WatchdogTimerModel; /* in hw/watchdog.c */ int select_watchdog(const char *p); int select_watchdog_action(const char *action); +int get_watchdog_action(void); void watchdog_add_model(WatchdogTimerModel *model); void watchdog_perform_action(void); diff --git a/qemu/include/sysemu/xen-mapcache.h b/qemu/include/sysemu/xen-mapcache.h index c59804060..c849489fb 100644 --- a/qemu/include/sysemu/xen-mapcache.h +++ b/qemu/include/sysemu/xen-mapcache.h @@ -9,7 +9,6 @@ #ifndef XEN_MAPCACHE_H #define XEN_MAPCACHE_H -#include typedef hwaddr (*phys_offset_to_gaddr_t)(hwaddr start_addr, ram_addr_t size, diff --git a/qemu/include/ui/console.h b/qemu/include/ui/console.h index 047a2b464..d5a88d93e 100644 --- a/qemu/include/ui/console.h +++ b/qemu/include/ui/console.h @@ -5,9 +5,7 @@ #include "qom/object.h" #include "qapi/qmp/qdict.h" #include "qemu/notify.h" -#include "qemu/typedefs.h" #include "qapi-types.h" -#include "qapi/error.h" #ifdef CONFIG_OPENGL # include @@ -30,6 +28,21 @@ #define GUI_REFRESH_INTERVAL_DEFAULT 30 #define GUI_REFRESH_INTERVAL_IDLE 3000 +/* Color number is match to standard vga palette */ +enum qemu_color_names { + QEMU_COLOR_BLACK = 0, + QEMU_COLOR_BLUE = 1, + QEMU_COLOR_GREEN = 2, + QEMU_COLOR_CYAN = 3, + QEMU_COLOR_RED = 4, + QEMU_COLOR_MAGENTA = 5, + QEMU_COLOR_YELLOW = 6, + QEMU_COLOR_WHITE = 7 +}; +/* Convert to curses char attributes */ +#define ATTR2CHTYPE(c, fg, bg, bold) \ + ((bold) << 21 | (bg) << 11 | (fg) << 8 | (c)) + typedef void QEMUPutKBDEvent(void *opaque, int keycode); typedef void QEMUPutLEDEvent(void *opaque, int ledstate); typedef void QEMUPutMouseEvent(void *opaque, int dx, int dy, int dz, int buttons_state); @@ -157,6 +170,14 @@ void cursor_set_mono(QEMUCursor *c, void cursor_get_mono_image(QEMUCursor *c, int foreground, uint8_t *mask); void cursor_get_mono_mask(QEMUCursor *c, int transparent, uint8_t *mask); +typedef void *QEMUGLContext; +typedef struct QEMUGLParams QEMUGLParams; + +struct QEMUGLParams { + int major_ver; + int minor_ver; +}; + typedef struct DisplayChangeListenerOps { const char *dpy_name; @@ -183,6 +204,21 @@ typedef struct DisplayChangeListenerOps { int x, int y, int on); void (*dpy_cursor_define)(DisplayChangeListener *dcl, QEMUCursor *cursor); + + QEMUGLContext (*dpy_gl_ctx_create)(DisplayChangeListener *dcl, + QEMUGLParams *params); + void (*dpy_gl_ctx_destroy)(DisplayChangeListener *dcl, + QEMUGLContext ctx); + int (*dpy_gl_ctx_make_current)(DisplayChangeListener *dcl, + QEMUGLContext ctx); + QEMUGLContext (*dpy_gl_ctx_get_current)(DisplayChangeListener *dcl); + + void (*dpy_gl_scanout)(DisplayChangeListener *dcl, + uint32_t backing_id, bool backing_y_0_top, + uint32_t x, uint32_t y, uint32_t w, uint32_t h); + void (*dpy_gl_update)(DisplayChangeListener *dcl, + uint32_t x, uint32_t y, uint32_t w, uint32_t h); + } DisplayChangeListenerOps; struct DisplayChangeListener { @@ -198,6 +234,7 @@ DisplayState *init_displaystate(void); DisplaySurface *qemu_create_displaysurface_from(int width, int height, pixman_format_code_t format, int linesize, uint8_t *data); +DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image); DisplaySurface *qemu_create_displaysurface_guestmem(int width, int height, pixman_format_code_t format, int linesize, @@ -244,6 +281,20 @@ bool dpy_cursor_define_supported(QemuConsole *con); bool dpy_gfx_check_format(QemuConsole *con, pixman_format_code_t format); +void dpy_gl_scanout(QemuConsole *con, + uint32_t backing_id, bool backing_y_0_top, + uint32_t x, uint32_t y, uint32_t w, uint32_t h); +void dpy_gl_update(QemuConsole *con, + uint32_t x, uint32_t y, uint32_t w, uint32_t h); + +QEMUGLContext dpy_gl_ctx_create(QemuConsole *con, + QEMUGLParams *params); +void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx); +int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx); +QEMUGLContext dpy_gl_ctx_get_current(QemuConsole *con); + +bool console_has_gl(QemuConsole *con); + static inline int surface_stride(DisplaySurface *s) { return pixman_image_get_stride(s->image); @@ -284,13 +335,23 @@ static inline pixman_format_code_t surface_format(DisplaySurface *s) #ifdef CONFIG_CURSES #include typedef chtype console_ch_t; +extern chtype vga_to_curses[]; #else typedef unsigned long console_ch_t; #endif static inline void console_write_ch(console_ch_t *dest, uint32_t ch) { - if (!(ch & 0xff)) + uint8_t c = ch; +#ifdef CONFIG_CURSES + if (vga_to_curses[c]) { + ch &= ~(console_ch_t)0xff; + ch |= vga_to_curses[c]; + } +#else + if (c == '\0') { ch |= ' '; + } +#endif *dest = ch; } @@ -300,6 +361,7 @@ typedef struct GraphicHwOps { void (*text_update)(void *opaque, console_ch_t *text); void (*update_interval)(void *opaque, uint64_t interval); int (*ui_info)(void *opaque, uint32_t head, QemuUIInfo *info); + void (*gl_block)(void *opaque, bool block); } GraphicHwOps; QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, @@ -312,9 +374,12 @@ void graphic_console_set_hwops(QemuConsole *con, void graphic_hw_update(QemuConsole *con); void graphic_hw_invalidate(QemuConsole *con); void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata); +void graphic_hw_gl_block(QemuConsole *con, bool block); 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); bool qemu_console_is_visible(QemuConsole *con); bool qemu_console_is_graphic(QemuConsole *con); bool qemu_console_is_fixedsize(QemuConsole *con); @@ -386,7 +451,7 @@ static inline int vnc_display_pw_expire(const char *id, time_t expires) void curses_display_init(DisplayState *ds, int full_screen); /* input.c */ -int index_from_key(const char *key); +int index_from_key(const char *key, size_t key_length); /* gtk.c */ void early_gtk_display_init(int opengl); diff --git a/qemu/include/ui/egl-context.h b/qemu/include/ui/egl-context.h new file mode 100644 index 000000000..f004ce11a --- /dev/null +++ b/qemu/include/ui/egl-context.h @@ -0,0 +1,14 @@ +#ifndef EGL_CONTEXT_H +#define EGL_CONTEXT_H + +#include "ui/console.h" +#include "ui/egl-helpers.h" + +QEMUGLContext qemu_egl_create_context(DisplayChangeListener *dcl, + QEMUGLParams *params); +void qemu_egl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx); +int qemu_egl_make_context_current(DisplayChangeListener *dcl, + QEMUGLContext ctx); +QEMUGLContext qemu_egl_get_current_context(DisplayChangeListener *dcl); + +#endif /* EGL_CONTEXT_H */ diff --git a/qemu/include/ui/egl-helpers.h b/qemu/include/ui/egl-helpers.h index 5ad5dc308..03fcf4bba 100644 --- a/qemu/include/ui/egl-helpers.h +++ b/qemu/include/ui/egl-helpers.h @@ -3,14 +3,26 @@ #include #include +#include extern EGLDisplay *qemu_egl_display; extern EGLConfig qemu_egl_config; +#ifdef CONFIG_OPENGL_DMABUF + +extern int qemu_egl_rn_fd; +extern struct gbm_device *qemu_egl_rn_gbm_dev; +extern EGLContext qemu_egl_rn_ctx; + +int qemu_egl_rendernode_open(void); +int egl_rendernode_init(void); +int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc); + +#endif + EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win); int qemu_egl_init_dpy(EGLNativeDisplayType dpy, bool gles, bool debug); EGLContext qemu_egl_init_ctx(void); -bool qemu_egl_has_ext(const char *haystack, const char *needle); #endif /* EGL_HELPERS_H */ diff --git a/qemu/include/ui/gtk.h b/qemu/include/ui/gtk.h index ee6dffd30..2bf60f3ec 100644 --- a/qemu/include/ui/gtk.h +++ b/qemu/include/ui/gtk.h @@ -1,10 +1,6 @@ #ifndef UI_GTK_H #define UI_GTK_H -#ifdef _WIN32 -# define _WIN32_WINNT 0x0601 /* needed to get definition of MAPVK_VK_TO_VSC */ -#endif - #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE /* Work around an -Wstrict-prototypes warning in GTK headers */ #pragma GCC diagnostic push @@ -24,6 +20,7 @@ #if defined(CONFIG_OPENGL) #include "ui/egl-helpers.h" +#include "ui/egl-context.h" #endif /* Compatibility define to let us build on both Gtk2 and Gtk3 */ @@ -50,6 +47,11 @@ typedef struct VirtualGfxConsole { EGLContext ectx; EGLSurface esurface; int glupdates; + int x, y, w, h; + GLuint tex_id; + GLuint fbo_id; + bool y0_top; + bool scanout_mode; #endif } VirtualGfxConsole; @@ -59,6 +61,7 @@ typedef struct VirtualVteConsole { GtkWidget *scrollbar; GtkWidget *terminal; CharDriverState *chr; + bool echo; } VirtualVteConsole; #endif @@ -94,6 +97,39 @@ void gd_egl_update(DisplayChangeListener *dcl, void gd_egl_refresh(DisplayChangeListener *dcl); void gd_egl_switch(DisplayChangeListener *dcl, DisplaySurface *surface); +QEMUGLContext gd_egl_create_context(DisplayChangeListener *dcl, + QEMUGLParams *params); +void gd_egl_scanout(DisplayChangeListener *dcl, + uint32_t backing_id, bool backing_y_0_top, + uint32_t x, uint32_t y, + uint32_t w, uint32_t h); +void gd_egl_scanout_flush(DisplayChangeListener *dcl, + uint32_t x, uint32_t y, uint32_t w, uint32_t h); void gtk_egl_init(void); +int gd_egl_make_current(DisplayChangeListener *dcl, + QEMUGLContext ctx); + +/* ui/gtk-gl-area.c */ +void gd_gl_area_init(VirtualConsole *vc); +void gd_gl_area_draw(VirtualConsole *vc); +void gd_gl_area_update(DisplayChangeListener *dcl, + int x, int y, int w, int h); +void gd_gl_area_refresh(DisplayChangeListener *dcl); +void gd_gl_area_switch(DisplayChangeListener *dcl, + DisplaySurface *surface); +QEMUGLContext gd_gl_area_create_context(DisplayChangeListener *dcl, + QEMUGLParams *params); +void gd_gl_area_destroy_context(DisplayChangeListener *dcl, + QEMUGLContext ctx); +void gd_gl_area_scanout(DisplayChangeListener *dcl, + uint32_t backing_id, bool backing_y_0_top, + uint32_t x, uint32_t y, + uint32_t w, uint32_t h); +void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, + uint32_t x, uint32_t y, uint32_t w, uint32_t h); +void gtk_gl_area_init(void); +QEMUGLContext gd_gl_area_get_current_context(DisplayChangeListener *dcl); +int gd_gl_area_make_current(DisplayChangeListener *dcl, + QEMUGLContext ctx); #endif /* UI_GTK_H */ diff --git a/qemu/include/ui/input.h b/qemu/include/ui/input.h index 5d5ac0066..102d8a334 100644 --- a/qemu/include/ui/input.h +++ b/qemu/include/ui/input.h @@ -33,7 +33,9 @@ void qemu_input_handler_bind(QemuInputHandlerState *s, const char *device_id, int head, Error **errp); void qemu_input_event_send(QemuConsole *src, InputEvent *evt); +void qemu_input_event_send_impl(QemuConsole *src, InputEvent *evt); void qemu_input_event_sync(void); +void qemu_input_event_sync_impl(void); InputEvent *qemu_input_event_new_key(KeyValue *key, bool down); void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down); @@ -63,4 +65,6 @@ void qemu_input_check_mode_change(void); void qemu_add_mouse_mode_change_notifier(Notifier *notify); void qemu_remove_mouse_mode_change_notifier(Notifier *notify); +int input_linux_init(void *opaque, QemuOpts *opts, Error **errp); + #endif /* INPUT_H */ diff --git a/qemu/include/ui/qemu-pixman.h b/qemu/include/ui/qemu-pixman.h index e34c4effc..4a67e0123 100644 --- a/qemu/include/ui/qemu-pixman.h +++ b/qemu/include/ui/qemu-pixman.h @@ -16,8 +16,6 @@ #pragma GCC diagnostic pop #endif -#include "qemu/typedefs.h" - /* * pixman image formats are defined to be native endian, * that means host byte order on qemu. So we go define diff --git a/qemu/include/ui/qemu-spice.h b/qemu/include/ui/qemu-spice.h index 0dff4229f..aa2436355 100644 --- a/qemu/include/ui/qemu-spice.h +++ b/qemu/include/ui/qemu-spice.h @@ -18,12 +18,11 @@ #ifndef QEMU_SPICE_H #define QEMU_SPICE_H -#include "config-host.h" +#include "qapi/error.h" #ifdef CONFIG_SPICE #include - #include "qemu/option.h" #include "qemu/config-file.h" @@ -43,9 +42,7 @@ int qemu_spice_set_pw_expire(time_t expires); int qemu_spice_migrate_info(const char *hostname, int port, int tls_port, const char *subject); -CharDriverState *qemu_chr_open_spice_vmc(const char *type); #if SPICE_SERVER_VERSION >= 0x000c02 -CharDriverState *qemu_chr_open_spice_port(const char *name); void qemu_spice_register_ports(void); #else static inline CharDriverState *qemu_chr_open_spice_port(const char *name) diff --git a/qemu/include/ui/sdl2.h b/qemu/include/ui/sdl2.h index 2fdad8f30..3f0b57bb1 100644 --- a/qemu/include/ui/sdl2.h +++ b/qemu/include/ui/sdl2.h @@ -15,12 +15,19 @@ struct sdl2_console { SDL_Renderer *real_renderer; int idx; int last_vm_running; /* per console for caption reasons */ - int x, y; + int x, y, w, h; int hidden; int opengl; int updates; + int idle_counter; SDL_GLContext winctx; +#ifdef CONFIG_OPENGL ConsoleGLState *gls; + GLuint tex_id; + GLuint fbo_id; + bool y0_top; + bool scanout_mode; +#endif }; void sdl2_window_create(struct sdl2_console *scon); @@ -48,4 +55,18 @@ void sdl2_gl_switch(DisplayChangeListener *dcl, void sdl2_gl_refresh(DisplayChangeListener *dcl); void sdl2_gl_redraw(struct sdl2_console *scon); +QEMUGLContext sdl2_gl_create_context(DisplayChangeListener *dcl, + QEMUGLParams *params); +void sdl2_gl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx); +int sdl2_gl_make_context_current(DisplayChangeListener *dcl, + QEMUGLContext ctx); +QEMUGLContext sdl2_gl_get_current_context(DisplayChangeListener *dcl); + +void sdl2_gl_scanout(DisplayChangeListener *dcl, + uint32_t backing_id, bool backing_y_0_top, + uint32_t x, uint32_t y, + uint32_t w, uint32_t h); +void sdl2_gl_scanout_flush(DisplayChangeListener *dcl, + uint32_t x, uint32_t y, uint32_t w, uint32_t h); + #endif /* SDL2_H */ diff --git a/qemu/include/ui/shader.h b/qemu/include/ui/shader.h index 8509596ac..f7d86188b 100644 --- a/qemu/include/ui/shader.h +++ b/qemu/include/ui/shader.h @@ -3,7 +3,9 @@ #include -void qemu_gl_run_texture_blit(GLint texture_blit_prog); +GLuint qemu_gl_init_texture_blit(GLint texture_blit_prog); +void qemu_gl_run_texture_blit(GLint texture_blit_prog, + GLint texture_blit_vao); GLuint qemu_gl_create_compile_shader(GLenum type, const GLchar *src); GLuint qemu_gl_create_link_program(GLuint vert, GLuint frag); diff --git a/qemu/include/ui/spice-display.h b/qemu/include/ui/spice-display.h index b25328a6b..30ccfe3da 100644 --- a/qemu/include/ui/spice-display.h +++ b/qemu/include/ui/spice-display.h @@ -24,6 +24,14 @@ #include "ui/console.h" #include "sysemu/sysemu.h" +#if defined(CONFIG_OPENGL_DMABUF) +# if SPICE_SERVER_VERSION >= 0x000d01 /* release 0.13.1 */ +# define HAVE_SPICE_GL 1 +# include "ui/egl-helpers.h" +# include "ui/egl-context.h" +# endif +#endif + #define NUM_MEMSLOTS 8 #define MEMSLOT_GENERATION_BITS 8 #define MEMSLOT_SLOT_BITS 8 @@ -50,6 +58,7 @@ enum { QXL_COOKIE_TYPE_IO, QXL_COOKIE_TYPE_RENDER_UPDATE_AREA, QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG, + QXL_COOKIE_TYPE_GL_DRAW_DONE, }; typedef struct QXLCookie { @@ -104,6 +113,13 @@ struct SimpleSpiceDisplay { QEMUCursor *cursor; int mouse_x, mouse_y; QEMUBH *cursor_bh; + +#ifdef HAVE_SPICE_GL + /* opengl rendering */ + QEMUBH *gl_unblock_bh; + QEMUTimer *gl_unblock_timer; + int dmabuf_fd; +#endif }; struct SimpleSpiceUpdate { diff --git a/qemu/io/Makefile.objs b/qemu/io/Makefile.objs new file mode 100644 index 000000000..9d8337d89 --- /dev/null +++ b/qemu/io/Makefile.objs @@ -0,0 +1,10 @@ +io-obj-y = channel.o +io-obj-y += channel-buffer.o +io-obj-y += channel-command.o +io-obj-y += channel-file.o +io-obj-y += channel-socket.o +io-obj-y += channel-tls.o +io-obj-y += channel-watch.o +io-obj-y += channel-websock.o +io-obj-y += channel-util.o +io-obj-y += task.o diff --git a/qemu/io/channel-buffer.c b/qemu/io/channel-buffer.c new file mode 100644 index 000000000..3e5117bf2 --- /dev/null +++ b/qemu/io/channel-buffer.c @@ -0,0 +1,249 @@ +/* + * QEMU I/O channels memory buffer driver + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "io/channel-buffer.h" +#include "io/channel-watch.h" +#include "qemu/sockets.h" +#include "trace.h" + +QIOChannelBuffer * +qio_channel_buffer_new(size_t capacity) +{ + QIOChannelBuffer *ioc; + + ioc = QIO_CHANNEL_BUFFER(object_new(TYPE_QIO_CHANNEL_BUFFER)); + + if (capacity) { + ioc->data = g_new0(uint8_t, capacity); + ioc->capacity = capacity; + } + + return ioc; +} + + +static void qio_channel_buffer_finalize(Object *obj) +{ + QIOChannelBuffer *ioc = QIO_CHANNEL_BUFFER(obj); + g_free(ioc->data); + ioc->capacity = ioc->usage = ioc->offset = 0; +} + + +static ssize_t qio_channel_buffer_readv(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, + size_t *nfds, + Error **errp) +{ + QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc); + ssize_t ret = 0; + size_t i; + + for (i = 0; i < niov; i++) { + size_t want = iov[i].iov_len; + if (bioc->offset >= bioc->usage) { + break; + } + if ((bioc->offset + want) > bioc->usage) { + want = bioc->usage - bioc->offset; + } + memcpy(iov[i].iov_base, bioc->data + bioc->offset, want); + ret += want; + bioc->offset += want; + } + + return ret; +} + +static ssize_t qio_channel_buffer_writev(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds, + size_t nfds, + Error **errp) +{ + QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc); + ssize_t ret = 0; + size_t i; + size_t towrite = 0; + + for (i = 0; i < niov; i++) { + towrite += iov[i].iov_len; + } + + if ((bioc->offset + towrite) > bioc->capacity) { + bioc->capacity = bioc->offset + towrite; + bioc->data = g_realloc(bioc->data, bioc->capacity); + } + + if (bioc->offset > bioc->usage) { + memset(bioc->data, 0, bioc->offset - bioc->usage); + bioc->usage = bioc->offset; + } + + for (i = 0; i < niov; i++) { + memcpy(bioc->data + bioc->usage, + iov[i].iov_base, + iov[i].iov_len); + bioc->usage += iov[i].iov_len; + bioc->offset += iov[i].iov_len; + ret += iov[i].iov_len; + } + + return ret; +} + +static int qio_channel_buffer_set_blocking(QIOChannel *ioc G_GNUC_UNUSED, + bool enabled G_GNUC_UNUSED, + Error **errp G_GNUC_UNUSED) +{ + return 0; +} + + +static off_t qio_channel_buffer_seek(QIOChannel *ioc, + off_t offset, + int whence, + Error **errp) +{ + QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc); + + bioc->offset = offset; + + return offset; +} + + +static int qio_channel_buffer_close(QIOChannel *ioc, + Error **errp) +{ + QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc); + + g_free(bioc->data); + bioc->capacity = bioc->usage = bioc->offset = 0; + + return 0; +} + + +typedef struct QIOChannelBufferSource QIOChannelBufferSource; +struct QIOChannelBufferSource { + GSource parent; + QIOChannelBuffer *bioc; + GIOCondition condition; +}; + +static gboolean +qio_channel_buffer_source_prepare(GSource *source, + gint *timeout) +{ + QIOChannelBufferSource *bsource = (QIOChannelBufferSource *)source; + + *timeout = -1; + + return (G_IO_IN | G_IO_OUT) & bsource->condition; +} + +static gboolean +qio_channel_buffer_source_check(GSource *source) +{ + QIOChannelBufferSource *bsource = (QIOChannelBufferSource *)source; + + return (G_IO_IN | G_IO_OUT) & bsource->condition; +} + +static gboolean +qio_channel_buffer_source_dispatch(GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + QIOChannelFunc func = (QIOChannelFunc)callback; + QIOChannelBufferSource *bsource = (QIOChannelBufferSource *)source; + + return (*func)(QIO_CHANNEL(bsource->bioc), + ((G_IO_IN | G_IO_OUT) & bsource->condition), + user_data); +} + +static void +qio_channel_buffer_source_finalize(GSource *source) +{ + QIOChannelBufferSource *ssource = (QIOChannelBufferSource *)source; + + object_unref(OBJECT(ssource->bioc)); +} + +GSourceFuncs qio_channel_buffer_source_funcs = { + qio_channel_buffer_source_prepare, + qio_channel_buffer_source_check, + qio_channel_buffer_source_dispatch, + qio_channel_buffer_source_finalize +}; + +static GSource *qio_channel_buffer_create_watch(QIOChannel *ioc, + GIOCondition condition) +{ + QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc); + QIOChannelBufferSource *ssource; + GSource *source; + + source = g_source_new(&qio_channel_buffer_source_funcs, + sizeof(QIOChannelBufferSource)); + ssource = (QIOChannelBufferSource *)source; + + ssource->bioc = bioc; + object_ref(OBJECT(bioc)); + + ssource->condition = condition; + + return source; +} + + +static void qio_channel_buffer_class_init(ObjectClass *klass, + void *class_data G_GNUC_UNUSED) +{ + QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); + + ioc_klass->io_writev = qio_channel_buffer_writev; + ioc_klass->io_readv = qio_channel_buffer_readv; + ioc_klass->io_set_blocking = qio_channel_buffer_set_blocking; + ioc_klass->io_seek = qio_channel_buffer_seek; + ioc_klass->io_close = qio_channel_buffer_close; + ioc_klass->io_create_watch = qio_channel_buffer_create_watch; +} + +static const TypeInfo qio_channel_buffer_info = { + .parent = TYPE_QIO_CHANNEL, + .name = TYPE_QIO_CHANNEL_BUFFER, + .instance_size = sizeof(QIOChannelBuffer), + .instance_finalize = qio_channel_buffer_finalize, + .class_init = qio_channel_buffer_class_init, +}; + +static void qio_channel_buffer_register_types(void) +{ + type_register_static(&qio_channel_buffer_info); +} + +type_init(qio_channel_buffer_register_types); diff --git a/qemu/io/channel-command.c b/qemu/io/channel-command.c new file mode 100644 index 000000000..ad25313be --- /dev/null +++ b/qemu/io/channel-command.c @@ -0,0 +1,368 @@ +/* + * QEMU I/O channels external command driver + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "io/channel-command.h" +#include "io/channel-watch.h" +#include "qapi/error.h" +#include "qemu/sockets.h" +#include "trace.h" + + +QIOChannelCommand * +qio_channel_command_new_pid(int writefd, + int readfd, + pid_t pid) +{ + QIOChannelCommand *ioc; + + ioc = QIO_CHANNEL_COMMAND(object_new(TYPE_QIO_CHANNEL_COMMAND)); + + ioc->readfd = readfd; + ioc->writefd = writefd; + ioc->pid = pid; + + trace_qio_channel_command_new_pid(ioc, writefd, readfd, pid); + return ioc; +} + + +#ifndef WIN32 +QIOChannelCommand * +qio_channel_command_new_spawn(const char *const argv[], + int flags, + Error **errp) +{ + pid_t pid = -1; + int stdinfd[2] = { -1, -1 }; + int stdoutfd[2] = { -1, -1 }; + int devnull = -1; + bool stdinnull = false, stdoutnull = false; + QIOChannelCommand *ioc; + + flags = flags & O_ACCMODE; + + if (flags == O_RDONLY) { + stdinnull = true; + } + if (flags == O_WRONLY) { + stdoutnull = true; + } + + if (stdinnull || stdoutnull) { + devnull = open("/dev/null", O_RDWR); + if (devnull < 0) { + error_setg_errno(errp, errno, + "Unable to open /dev/null"); + goto error; + } + } + + if ((!stdinnull && pipe(stdinfd) < 0) || + (!stdoutnull && pipe(stdoutfd) < 0)) { + error_setg_errno(errp, errno, + "Unable to open pipe"); + goto error; + } + + pid = qemu_fork(errp); + if (pid < 0) { + goto error; + } + + if (pid == 0) { /* child */ + dup2(stdinnull ? devnull : stdinfd[0], STDIN_FILENO); + dup2(stdoutnull ? devnull : stdoutfd[1], STDOUT_FILENO); + /* Leave stderr connected to qemu's stderr */ + + if (!stdinnull) { + close(stdinfd[0]); + close(stdinfd[1]); + } + if (!stdoutnull) { + close(stdoutfd[0]); + close(stdoutfd[1]); + } + if (devnull != -1) { + close(devnull); + } + + execv(argv[0], (char * const *)argv); + _exit(1); + } + + if (!stdinnull) { + close(stdinfd[0]); + } + if (!stdoutnull) { + close(stdoutfd[1]); + } + + ioc = qio_channel_command_new_pid(stdinnull ? devnull : stdinfd[1], + stdoutnull ? devnull : stdoutfd[0], + pid); + trace_qio_channel_command_new_spawn(ioc, argv[0], flags); + return ioc; + + error: + if (devnull != -1) { + close(devnull); + } + if (stdinfd[0] != -1) { + close(stdinfd[0]); + } + if (stdinfd[1] != -1) { + close(stdinfd[1]); + } + if (stdoutfd[0] != -1) { + close(stdoutfd[0]); + } + if (stdoutfd[1] != -1) { + close(stdoutfd[1]); + } + return NULL; +} + +#else /* WIN32 */ +QIOChannelCommand * +qio_channel_command_new_spawn(const char *const argv[], + int flags, + Error **errp) +{ + error_setg_errno(errp, ENOSYS, + "Command spawn not supported on this platform"); + return NULL; +} +#endif /* WIN32 */ + +#ifndef WIN32 +static int qio_channel_command_abort(QIOChannelCommand *ioc, + Error **errp) +{ + pid_t ret; + int status; + int step = 0; + + /* See if intermediate process has exited; if not, try a nice + * SIGTERM followed by a more severe SIGKILL. + */ + rewait: + trace_qio_channel_command_abort(ioc, ioc->pid); + ret = waitpid(ioc->pid, &status, WNOHANG); + trace_qio_channel_command_wait(ioc, ioc->pid, ret, status); + if (ret == (pid_t)-1) { + if (errno == EINTR) { + goto rewait; + } else { + error_setg_errno(errp, errno, + "Cannot wait on pid %llu", + (unsigned long long)ioc->pid); + return -1; + } + } else if (ret == 0) { + if (step == 0) { + kill(ioc->pid, SIGTERM); + } else if (step == 1) { + kill(ioc->pid, SIGKILL); + } else { + error_setg(errp, + "Process %llu refused to die", + (unsigned long long)ioc->pid); + return -1; + } + step++; + usleep(10 * 1000); + goto rewait; + } + + return 0; +} +#endif /* ! WIN32 */ + + +static void qio_channel_command_init(Object *obj) +{ + QIOChannelCommand *ioc = QIO_CHANNEL_COMMAND(obj); + ioc->readfd = -1; + ioc->writefd = -1; + ioc->pid = -1; +} + +static void qio_channel_command_finalize(Object *obj) +{ + QIOChannelCommand *ioc = QIO_CHANNEL_COMMAND(obj); + if (ioc->readfd != -1) { + close(ioc->readfd); + } + if (ioc->writefd != -1 && + ioc->writefd != ioc->readfd) { + close(ioc->writefd); + } + ioc->writefd = ioc->readfd = -1; + if (ioc->pid > 0) { +#ifndef WIN32 + qio_channel_command_abort(ioc, NULL); +#endif + } +} + + +static ssize_t qio_channel_command_readv(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, + size_t *nfds, + Error **errp) +{ + QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); + ssize_t ret; + + retry: + ret = readv(cioc->readfd, iov, niov); + if (ret < 0) { + if (errno == EAGAIN) { + return QIO_CHANNEL_ERR_BLOCK; + } + if (errno == EINTR) { + goto retry; + } + + error_setg_errno(errp, errno, + "Unable to read from command"); + return -1; + } + + return ret; +} + +static ssize_t qio_channel_command_writev(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds, + size_t nfds, + Error **errp) +{ + QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); + ssize_t ret; + + retry: + ret = writev(cioc->writefd, iov, niov); + if (ret <= 0) { + if (errno == EAGAIN) { + return QIO_CHANNEL_ERR_BLOCK; + } + if (errno == EINTR) { + goto retry; + } + error_setg_errno(errp, errno, "%s", + "Unable to write to command"); + return -1; + } + return ret; +} + +static int qio_channel_command_set_blocking(QIOChannel *ioc, + bool enabled, + Error **errp) +{ + QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); + + if (enabled) { + qemu_set_block(cioc->writefd); + qemu_set_block(cioc->readfd); + } else { + qemu_set_nonblock(cioc->writefd); + qemu_set_nonblock(cioc->readfd); + } + + return 0; +} + + +static int qio_channel_command_close(QIOChannel *ioc, + Error **errp) +{ + QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); + int rv = 0; + + /* We close FDs before killing, because that + * gives a better chance of clean shutdown + */ + if (cioc->readfd != -1 && + close(cioc->readfd) < 0) { + rv = -1; + } + if (cioc->writefd != -1 && + cioc->writefd != cioc->readfd && + close(cioc->writefd) < 0) { + rv = -1; + } + cioc->writefd = cioc->readfd = -1; +#ifndef WIN32 + if (qio_channel_command_abort(cioc, errp) < 0) { + return -1; + } +#endif + if (rv < 0) { + error_setg_errno(errp, errno, "%s", + "Unable to close command"); + } + return rv; +} + + +static GSource *qio_channel_command_create_watch(QIOChannel *ioc, + GIOCondition condition) +{ + QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); + return qio_channel_create_fd_pair_watch(ioc, + cioc->readfd, + cioc->writefd, + condition); +} + + +static void qio_channel_command_class_init(ObjectClass *klass, + void *class_data G_GNUC_UNUSED) +{ + QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); + + ioc_klass->io_writev = qio_channel_command_writev; + ioc_klass->io_readv = qio_channel_command_readv; + ioc_klass->io_set_blocking = qio_channel_command_set_blocking; + ioc_klass->io_close = qio_channel_command_close; + ioc_klass->io_create_watch = qio_channel_command_create_watch; +} + +static const TypeInfo qio_channel_command_info = { + .parent = TYPE_QIO_CHANNEL, + .name = TYPE_QIO_CHANNEL_COMMAND, + .instance_size = sizeof(QIOChannelCommand), + .instance_init = qio_channel_command_init, + .instance_finalize = qio_channel_command_finalize, + .class_init = qio_channel_command_class_init, +}; + +static void qio_channel_command_register_types(void) +{ + type_register_static(&qio_channel_command_info); +} + +type_init(qio_channel_command_register_types); diff --git a/qemu/io/channel-file.c b/qemu/io/channel-file.c new file mode 100644 index 000000000..e1da2435e --- /dev/null +++ b/qemu/io/channel-file.c @@ -0,0 +1,225 @@ +/* + * QEMU I/O channels files driver + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "io/channel-file.h" +#include "io/channel-watch.h" +#include "qapi/error.h" +#include "qemu/sockets.h" +#include "trace.h" + +QIOChannelFile * +qio_channel_file_new_fd(int fd) +{ + QIOChannelFile *ioc; + + ioc = QIO_CHANNEL_FILE(object_new(TYPE_QIO_CHANNEL_FILE)); + + ioc->fd = fd; + + trace_qio_channel_file_new_fd(ioc, fd); + + return ioc; +} + + +QIOChannelFile * +qio_channel_file_new_path(const char *path, + int flags, + mode_t mode, + Error **errp) +{ + QIOChannelFile *ioc; + + ioc = QIO_CHANNEL_FILE(object_new(TYPE_QIO_CHANNEL_FILE)); + + if (flags & O_WRONLY) { + ioc->fd = open(path, flags, mode); + } else { + ioc->fd = open(path, flags); + } + if (ioc->fd < 0) { + object_unref(OBJECT(ioc)); + error_setg_errno(errp, errno, + "Unable to open %s", path); + return NULL; + } + + trace_qio_channel_file_new_path(ioc, path, flags, mode, ioc->fd); + + return ioc; +} + + +static void qio_channel_file_init(Object *obj) +{ + QIOChannelFile *ioc = QIO_CHANNEL_FILE(obj); + ioc->fd = -1; +} + +static void qio_channel_file_finalize(Object *obj) +{ + QIOChannelFile *ioc = QIO_CHANNEL_FILE(obj); + if (ioc->fd != -1) { + close(ioc->fd); + ioc->fd = -1; + } +} + + +static ssize_t qio_channel_file_readv(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, + size_t *nfds, + Error **errp) +{ + QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); + ssize_t ret; + + retry: + ret = readv(fioc->fd, iov, niov); + if (ret < 0) { + if (errno == EAGAIN) { + return QIO_CHANNEL_ERR_BLOCK; + } + if (errno == EINTR) { + goto retry; + } + + error_setg_errno(errp, errno, + "Unable to read from file"); + return -1; + } + + return ret; +} + +static ssize_t qio_channel_file_writev(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds, + size_t nfds, + Error **errp) +{ + QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); + ssize_t ret; + + retry: + ret = writev(fioc->fd, iov, niov); + if (ret <= 0) { + if (errno == EAGAIN) { + return QIO_CHANNEL_ERR_BLOCK; + } + if (errno == EINTR) { + goto retry; + } + error_setg_errno(errp, errno, + "Unable to write to file"); + return -1; + } + return ret; +} + +static int qio_channel_file_set_blocking(QIOChannel *ioc, + bool enabled, + Error **errp) +{ + QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); + + if (enabled) { + qemu_set_block(fioc->fd); + } else { + qemu_set_nonblock(fioc->fd); + } + return 0; +} + + +static off_t qio_channel_file_seek(QIOChannel *ioc, + off_t offset, + int whence, + Error **errp) +{ + QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); + off_t ret; + + ret = lseek(fioc->fd, offset, whence); + if (ret == (off_t)-1) { + error_setg_errno(errp, errno, + "Unable to seek to offset %lld whence %d in file", + (long long int)offset, whence); + return -1; + } + return ret; +} + + +static int qio_channel_file_close(QIOChannel *ioc, + Error **errp) +{ + QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); + + if (close(fioc->fd) < 0) { + error_setg_errno(errp, errno, + "Unable to close file"); + return -1; + } + return 0; +} + + +static GSource *qio_channel_file_create_watch(QIOChannel *ioc, + GIOCondition condition) +{ + QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); + return qio_channel_create_fd_watch(ioc, + fioc->fd, + condition); +} + +static void qio_channel_file_class_init(ObjectClass *klass, + void *class_data G_GNUC_UNUSED) +{ + QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); + + ioc_klass->io_writev = qio_channel_file_writev; + ioc_klass->io_readv = qio_channel_file_readv; + ioc_klass->io_set_blocking = qio_channel_file_set_blocking; + ioc_klass->io_seek = qio_channel_file_seek; + ioc_klass->io_close = qio_channel_file_close; + ioc_klass->io_create_watch = qio_channel_file_create_watch; +} + +static const TypeInfo qio_channel_file_info = { + .parent = TYPE_QIO_CHANNEL, + .name = TYPE_QIO_CHANNEL_FILE, + .instance_size = sizeof(QIOChannelFile), + .instance_init = qio_channel_file_init, + .instance_finalize = qio_channel_file_finalize, + .class_init = qio_channel_file_class_init, +}; + +static void qio_channel_file_register_types(void) +{ + type_register_static(&qio_channel_file_info); +} + +type_init(qio_channel_file_register_types); diff --git a/qemu/io/channel-socket.c b/qemu/io/channel-socket.c new file mode 100644 index 000000000..ca8bc20b1 --- /dev/null +++ b/qemu/io/channel-socket.c @@ -0,0 +1,772 @@ +/* + * QEMU I/O channels sockets driver + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "io/channel-socket.h" +#include "io/channel-watch.h" +#include "trace.h" + +#define SOCKET_MAX_FDS 16 + +SocketAddress * +qio_channel_socket_get_local_address(QIOChannelSocket *ioc, + Error **errp) +{ + return socket_sockaddr_to_address(&ioc->localAddr, + ioc->localAddrLen, + errp); +} + +SocketAddress * +qio_channel_socket_get_remote_address(QIOChannelSocket *ioc, + Error **errp) +{ + return socket_sockaddr_to_address(&ioc->remoteAddr, + ioc->remoteAddrLen, + errp); +} + +QIOChannelSocket * +qio_channel_socket_new(void) +{ + QIOChannelSocket *sioc; + QIOChannel *ioc; + + sioc = QIO_CHANNEL_SOCKET(object_new(TYPE_QIO_CHANNEL_SOCKET)); + sioc->fd = -1; + + ioc = QIO_CHANNEL(sioc); + ioc->features |= (1 << QIO_CHANNEL_FEATURE_SHUTDOWN); + +#ifdef WIN32 + ioc->event = CreateEvent(NULL, FALSE, FALSE, NULL); +#endif + + trace_qio_channel_socket_new(sioc); + + return sioc; +} + + +static int +qio_channel_socket_set_fd(QIOChannelSocket *sioc, + int fd, + Error **errp) +{ + if (sioc->fd != -1) { + error_setg(errp, "Socket is already open"); + return -1; + } + + sioc->fd = fd; + sioc->remoteAddrLen = sizeof(sioc->remoteAddr); + sioc->localAddrLen = sizeof(sioc->localAddr); + + + if (getpeername(fd, (struct sockaddr *)&sioc->remoteAddr, + &sioc->remoteAddrLen) < 0) { + if (errno == ENOTCONN) { + memset(&sioc->remoteAddr, 0, sizeof(sioc->remoteAddr)); + sioc->remoteAddrLen = sizeof(sioc->remoteAddr); + } else { + error_setg_errno(errp, errno, + "Unable to query remote socket address"); + goto error; + } + } + + if (getsockname(fd, (struct sockaddr *)&sioc->localAddr, + &sioc->localAddrLen) < 0) { + error_setg_errno(errp, errno, + "Unable to query local socket address"); + goto error; + } + +#ifndef WIN32 + if (sioc->localAddr.ss_family == AF_UNIX) { + QIOChannel *ioc = QIO_CHANNEL(sioc); + ioc->features |= (1 << QIO_CHANNEL_FEATURE_FD_PASS); + } +#endif /* WIN32 */ + + return 0; + + error: + sioc->fd = -1; /* Let the caller close FD on failure */ + return -1; +} + +QIOChannelSocket * +qio_channel_socket_new_fd(int fd, + Error **errp) +{ + QIOChannelSocket *ioc; + + ioc = qio_channel_socket_new(); + if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { + object_unref(OBJECT(ioc)); + return NULL; + } + + trace_qio_channel_socket_new_fd(ioc, fd); + + return ioc; +} + + +int qio_channel_socket_connect_sync(QIOChannelSocket *ioc, + SocketAddress *addr, + Error **errp) +{ + int fd; + + trace_qio_channel_socket_connect_sync(ioc, addr); + fd = socket_connect(addr, errp, NULL, NULL); + if (fd < 0) { + trace_qio_channel_socket_connect_fail(ioc); + return -1; + } + + trace_qio_channel_socket_connect_complete(ioc, fd); + if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { + close(fd); + return -1; + } + + return 0; +} + + +static int qio_channel_socket_connect_worker(QIOTask *task, + Error **errp, + gpointer opaque) +{ + QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task)); + SocketAddress *addr = opaque; + int ret; + + ret = qio_channel_socket_connect_sync(ioc, + addr, + errp); + + object_unref(OBJECT(ioc)); + return ret; +} + + +void qio_channel_socket_connect_async(QIOChannelSocket *ioc, + SocketAddress *addr, + QIOTaskFunc callback, + gpointer opaque, + GDestroyNotify destroy) +{ + QIOTask *task = qio_task_new( + OBJECT(ioc), callback, opaque, destroy); + SocketAddress *addrCopy; + + qapi_copy_SocketAddress(&addrCopy, addr); + + /* socket_connect() does a non-blocking connect(), but it + * still blocks in DNS lookups, so we must use a thread */ + trace_qio_channel_socket_connect_async(ioc, addr); + qio_task_run_in_thread(task, + qio_channel_socket_connect_worker, + addrCopy, + (GDestroyNotify)qapi_free_SocketAddress); +} + + +int qio_channel_socket_listen_sync(QIOChannelSocket *ioc, + SocketAddress *addr, + Error **errp) +{ + int fd; + + trace_qio_channel_socket_listen_sync(ioc, addr); + fd = socket_listen(addr, errp); + if (fd < 0) { + trace_qio_channel_socket_listen_fail(ioc); + return -1; + } + + trace_qio_channel_socket_listen_complete(ioc, fd); + if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { + close(fd); + return -1; + } + + return 0; +} + + +static int qio_channel_socket_listen_worker(QIOTask *task, + Error **errp, + gpointer opaque) +{ + QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task)); + SocketAddress *addr = opaque; + int ret; + + ret = qio_channel_socket_listen_sync(ioc, + addr, + errp); + + object_unref(OBJECT(ioc)); + return ret; +} + + +void qio_channel_socket_listen_async(QIOChannelSocket *ioc, + SocketAddress *addr, + QIOTaskFunc callback, + gpointer opaque, + GDestroyNotify destroy) +{ + QIOTask *task = qio_task_new( + OBJECT(ioc), callback, opaque, destroy); + SocketAddress *addrCopy; + + qapi_copy_SocketAddress(&addrCopy, addr); + + /* socket_listen() blocks in DNS lookups, so we must use a thread */ + trace_qio_channel_socket_listen_async(ioc, addr); + qio_task_run_in_thread(task, + qio_channel_socket_listen_worker, + addrCopy, + (GDestroyNotify)qapi_free_SocketAddress); +} + + +int qio_channel_socket_dgram_sync(QIOChannelSocket *ioc, + SocketAddress *localAddr, + SocketAddress *remoteAddr, + Error **errp) +{ + int fd; + + trace_qio_channel_socket_dgram_sync(ioc, localAddr, remoteAddr); + fd = socket_dgram(remoteAddr, localAddr, errp); + if (fd < 0) { + trace_qio_channel_socket_dgram_fail(ioc); + return -1; + } + + trace_qio_channel_socket_dgram_complete(ioc, fd); + if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { + close(fd); + return -1; + } + + return 0; +} + + +struct QIOChannelSocketDGramWorkerData { + SocketAddress *localAddr; + SocketAddress *remoteAddr; +}; + + +static void qio_channel_socket_dgram_worker_free(gpointer opaque) +{ + struct QIOChannelSocketDGramWorkerData *data = opaque; + qapi_free_SocketAddress(data->localAddr); + qapi_free_SocketAddress(data->remoteAddr); + g_free(data); +} + +static int qio_channel_socket_dgram_worker(QIOTask *task, + Error **errp, + gpointer opaque) +{ + QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task)); + struct QIOChannelSocketDGramWorkerData *data = opaque; + int ret; + + /* socket_dgram() blocks in DNS lookups, so we must use a thread */ + ret = qio_channel_socket_dgram_sync(ioc, + data->localAddr, + data->remoteAddr, + errp); + + object_unref(OBJECT(ioc)); + return ret; +} + + +void qio_channel_socket_dgram_async(QIOChannelSocket *ioc, + SocketAddress *localAddr, + SocketAddress *remoteAddr, + QIOTaskFunc callback, + gpointer opaque, + GDestroyNotify destroy) +{ + QIOTask *task = qio_task_new( + OBJECT(ioc), callback, opaque, destroy); + struct QIOChannelSocketDGramWorkerData *data = g_new0( + struct QIOChannelSocketDGramWorkerData, 1); + + qapi_copy_SocketAddress(&data->localAddr, localAddr); + qapi_copy_SocketAddress(&data->remoteAddr, remoteAddr); + + trace_qio_channel_socket_dgram_async(ioc, localAddr, remoteAddr); + qio_task_run_in_thread(task, + qio_channel_socket_dgram_worker, + data, + qio_channel_socket_dgram_worker_free); +} + + +QIOChannelSocket * +qio_channel_socket_accept(QIOChannelSocket *ioc, + Error **errp) +{ + QIOChannelSocket *cioc; + + cioc = QIO_CHANNEL_SOCKET(object_new(TYPE_QIO_CHANNEL_SOCKET)); + cioc->fd = -1; + cioc->remoteAddrLen = sizeof(ioc->remoteAddr); + cioc->localAddrLen = sizeof(ioc->localAddr); + +#ifdef WIN32 + QIO_CHANNEL(cioc)->event = CreateEvent(NULL, FALSE, FALSE, NULL); +#endif + + + retry: + trace_qio_channel_socket_accept(ioc); + cioc->fd = qemu_accept(ioc->fd, (struct sockaddr *)&cioc->remoteAddr, + &cioc->remoteAddrLen); + if (cioc->fd < 0) { + trace_qio_channel_socket_accept_fail(ioc); + if (errno == EINTR) { + goto retry; + } + goto error; + } + + if (getsockname(cioc->fd, (struct sockaddr *)&cioc->localAddr, + &cioc->localAddrLen) < 0) { + error_setg_errno(errp, errno, + "Unable to query local socket address"); + goto error; + } + +#ifndef WIN32 + if (cioc->localAddr.ss_family == AF_UNIX) { + QIO_CHANNEL(cioc)->features |= (1 << QIO_CHANNEL_FEATURE_FD_PASS); + } +#endif /* WIN32 */ + + trace_qio_channel_socket_accept_complete(ioc, cioc, cioc->fd); + return cioc; + + error: + object_unref(OBJECT(cioc)); + return NULL; +} + +static void qio_channel_socket_init(Object *obj) +{ + QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(obj); + ioc->fd = -1; +} + +static void qio_channel_socket_finalize(Object *obj) +{ + QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(obj); + if (ioc->fd != -1) { +#ifdef WIN32 + WSAEventSelect(ioc->fd, NULL, 0); +#endif + closesocket(ioc->fd); + ioc->fd = -1; + } +} + + +#ifndef WIN32 +static void qio_channel_socket_copy_fds(struct msghdr *msg, + int **fds, size_t *nfds) +{ + struct cmsghdr *cmsg; + + *nfds = 0; + *fds = NULL; + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + int fd_size, i; + int gotfds; + + if (cmsg->cmsg_len < CMSG_LEN(sizeof(int)) || + cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS) { + continue; + } + + fd_size = cmsg->cmsg_len - CMSG_LEN(0); + + if (!fd_size) { + continue; + } + + gotfds = fd_size / sizeof(int); + *fds = g_renew(int, *fds, *nfds + gotfds); + memcpy(*fds + *nfds, CMSG_DATA(cmsg), fd_size); + + for (i = 0; i < gotfds; i++) { + int fd = (*fds)[*nfds + i]; + if (fd < 0) { + continue; + } + + /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */ + qemu_set_block(fd); + +#ifndef MSG_CMSG_CLOEXEC + qemu_set_cloexec(fd); +#endif + } + *nfds += gotfds; + } +} + + +static ssize_t qio_channel_socket_readv(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, + size_t *nfds, + Error **errp) +{ + QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); + ssize_t ret; + struct msghdr msg = { NULL, }; + char control[CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)]; + int sflags = 0; + + memset(control, 0, CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)); + +#ifdef MSG_CMSG_CLOEXEC + sflags |= MSG_CMSG_CLOEXEC; +#endif + + msg.msg_iov = (struct iovec *)iov; + msg.msg_iovlen = niov; + if (fds && nfds) { + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + } + + retry: + ret = recvmsg(sioc->fd, &msg, sflags); + if (ret < 0) { + if (errno == EAGAIN) { + return QIO_CHANNEL_ERR_BLOCK; + } + if (errno == EINTR) { + goto retry; + } + + error_setg_errno(errp, errno, + "Unable to read from socket"); + return -1; + } + + if (fds && nfds) { + qio_channel_socket_copy_fds(&msg, fds, nfds); + } + + return ret; +} + +static ssize_t qio_channel_socket_writev(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds, + size_t nfds, + Error **errp) +{ + QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); + ssize_t ret; + struct msghdr msg = { NULL, }; + char control[CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)]; + size_t fdsize = sizeof(int) * nfds; + struct cmsghdr *cmsg; + + memset(control, 0, CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)); + + msg.msg_iov = (struct iovec *)iov; + msg.msg_iovlen = niov; + + if (nfds) { + if (nfds > SOCKET_MAX_FDS) { + error_setg_errno(errp, EINVAL, + "Only %d FDs can be sent, got %zu", + SOCKET_MAX_FDS, nfds); + return -1; + } + + msg.msg_control = control; + msg.msg_controllen = CMSG_SPACE(sizeof(int) * nfds); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(fdsize); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(cmsg), fds, fdsize); + } + + retry: + ret = sendmsg(sioc->fd, &msg, 0); + if (ret <= 0) { + if (errno == EAGAIN) { + return QIO_CHANNEL_ERR_BLOCK; + } + if (errno == EINTR) { + goto retry; + } + error_setg_errno(errp, errno, + "Unable to write to socket"); + return -1; + } + return ret; +} +#else /* WIN32 */ +static ssize_t qio_channel_socket_readv(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, + size_t *nfds, + Error **errp) +{ + QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); + ssize_t done = 0; + ssize_t i; + + for (i = 0; i < niov; i++) { + ssize_t ret; + retry: + ret = recv(sioc->fd, + iov[i].iov_base, + iov[i].iov_len, + 0); + if (ret < 0) { + if (errno == EAGAIN) { + if (done) { + return done; + } else { + return QIO_CHANNEL_ERR_BLOCK; + } + } else if (errno == EINTR) { + goto retry; + } else { + error_setg_errno(errp, errno, + "Unable to read from socket"); + return -1; + } + } + done += ret; + if (ret < iov[i].iov_len) { + return done; + } + } + + return done; +} + +static ssize_t qio_channel_socket_writev(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds, + size_t nfds, + Error **errp) +{ + QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); + ssize_t done = 0; + ssize_t i; + + for (i = 0; i < niov; i++) { + ssize_t ret; + retry: + ret = send(sioc->fd, + iov[i].iov_base, + iov[i].iov_len, + 0); + if (ret < 0) { + if (errno == EAGAIN) { + if (done) { + return done; + } else { + return QIO_CHANNEL_ERR_BLOCK; + } + } else if (errno == EINTR) { + goto retry; + } else { + error_setg_errno(errp, errno, + "Unable to write to socket"); + return -1; + } + } + done += ret; + if (ret < iov[i].iov_len) { + return done; + } + } + + return done; +} +#endif /* WIN32 */ + +static int +qio_channel_socket_set_blocking(QIOChannel *ioc, + bool enabled, + Error **errp) +{ + QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); + + if (enabled) { + qemu_set_block(sioc->fd); + } else { + qemu_set_nonblock(sioc->fd); +#ifdef WIN32 + WSAEventSelect(sioc->fd, ioc->event, + FD_READ | FD_ACCEPT | FD_CLOSE | + FD_CONNECT | FD_WRITE | FD_OOB); +#endif + } + return 0; +} + + +static void +qio_channel_socket_set_delay(QIOChannel *ioc, + bool enabled) +{ + QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); + int v = enabled ? 0 : 1; + + qemu_setsockopt(sioc->fd, + IPPROTO_TCP, TCP_NODELAY, + &v, sizeof(v)); +} + + +static void +qio_channel_socket_set_cork(QIOChannel *ioc, + bool enabled) +{ + QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); + int v = enabled ? 1 : 0; + + socket_set_cork(sioc->fd, v); +} + + +static int +qio_channel_socket_close(QIOChannel *ioc, + Error **errp) +{ + QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); + + if (sioc->fd != -1) { +#ifdef WIN32 + WSAEventSelect(sioc->fd, NULL, 0); +#endif + if (closesocket(sioc->fd) < 0) { + sioc->fd = -1; + error_setg_errno(errp, errno, + "Unable to close socket"); + return -1; + } + sioc->fd = -1; + } + return 0; +} + +static int +qio_channel_socket_shutdown(QIOChannel *ioc, + QIOChannelShutdown how, + Error **errp) +{ + QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); + int sockhow; + + switch (how) { + case QIO_CHANNEL_SHUTDOWN_READ: + sockhow = SHUT_RD; + break; + case QIO_CHANNEL_SHUTDOWN_WRITE: + sockhow = SHUT_WR; + break; + case QIO_CHANNEL_SHUTDOWN_BOTH: + default: + sockhow = SHUT_RDWR; + break; + } + + if (shutdown(sioc->fd, sockhow) < 0) { + error_setg_errno(errp, errno, + "Unable to shutdown socket"); + return -1; + } + return 0; +} + +static GSource *qio_channel_socket_create_watch(QIOChannel *ioc, + GIOCondition condition) +{ + QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); + return qio_channel_create_socket_watch(ioc, + sioc->fd, + condition); +} + +static void qio_channel_socket_class_init(ObjectClass *klass, + void *class_data G_GNUC_UNUSED) +{ + QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); + + ioc_klass->io_writev = qio_channel_socket_writev; + ioc_klass->io_readv = qio_channel_socket_readv; + ioc_klass->io_set_blocking = qio_channel_socket_set_blocking; + ioc_klass->io_close = qio_channel_socket_close; + ioc_klass->io_shutdown = qio_channel_socket_shutdown; + ioc_klass->io_set_cork = qio_channel_socket_set_cork; + ioc_klass->io_set_delay = qio_channel_socket_set_delay; + ioc_klass->io_create_watch = qio_channel_socket_create_watch; +} + +static const TypeInfo qio_channel_socket_info = { + .parent = TYPE_QIO_CHANNEL, + .name = TYPE_QIO_CHANNEL_SOCKET, + .instance_size = sizeof(QIOChannelSocket), + .instance_init = qio_channel_socket_init, + .instance_finalize = qio_channel_socket_finalize, + .class_init = qio_channel_socket_class_init, +}; + +static void qio_channel_socket_register_types(void) +{ + type_register_static(&qio_channel_socket_info); +} + +type_init(qio_channel_socket_register_types); diff --git a/qemu/io/channel-tls.c b/qemu/io/channel-tls.c new file mode 100644 index 000000000..9a8525c81 --- /dev/null +++ b/qemu/io/channel-tls.c @@ -0,0 +1,395 @@ +/* + * QEMU I/O channels TLS driver + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "io/channel-tls.h" +#include "trace.h" + + +static ssize_t qio_channel_tls_write_handler(const char *buf, + size_t len, + void *opaque) +{ + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque); + ssize_t ret; + + ret = qio_channel_write(tioc->master, buf, len, NULL); + if (ret == QIO_CHANNEL_ERR_BLOCK) { + errno = EAGAIN; + return -1; + } else if (ret < 0) { + errno = EIO; + return -1; + } + return ret; +} + +static ssize_t qio_channel_tls_read_handler(char *buf, + size_t len, + void *opaque) +{ + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque); + ssize_t ret; + + ret = qio_channel_read(tioc->master, buf, len, NULL); + if (ret == QIO_CHANNEL_ERR_BLOCK) { + errno = EAGAIN; + return -1; + } else if (ret < 0) { + errno = EIO; + return -1; + } + return ret; +} + + +QIOChannelTLS * +qio_channel_tls_new_server(QIOChannel *master, + QCryptoTLSCreds *creds, + const char *aclname, + Error **errp) +{ + QIOChannelTLS *ioc; + + ioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS)); + + ioc->master = master; + object_ref(OBJECT(master)); + + ioc->session = qcrypto_tls_session_new( + creds, + NULL, + aclname, + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, + errp); + if (!ioc->session) { + goto error; + } + + qcrypto_tls_session_set_callbacks( + ioc->session, + qio_channel_tls_write_handler, + qio_channel_tls_read_handler, + ioc); + + trace_qio_channel_tls_new_server(ioc, master, creds, aclname); + return ioc; + + error: + object_unref(OBJECT(ioc)); + return NULL; +} + +QIOChannelTLS * +qio_channel_tls_new_client(QIOChannel *master, + QCryptoTLSCreds *creds, + const char *hostname, + Error **errp) +{ + QIOChannelTLS *tioc; + QIOChannel *ioc; + + tioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS)); + ioc = QIO_CHANNEL(tioc); + + tioc->master = master; + if (master->features & (1 << QIO_CHANNEL_FEATURE_SHUTDOWN)) { + ioc->features |= (1 << QIO_CHANNEL_FEATURE_SHUTDOWN); + } + object_ref(OBJECT(master)); + + tioc->session = qcrypto_tls_session_new( + creds, + hostname, + NULL, + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, + errp); + if (!tioc->session) { + goto error; + } + + qcrypto_tls_session_set_callbacks( + tioc->session, + qio_channel_tls_write_handler, + qio_channel_tls_read_handler, + tioc); + + trace_qio_channel_tls_new_client(tioc, master, creds, hostname); + return tioc; + + error: + object_unref(OBJECT(tioc)); + return NULL; +} + + +static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc, + GIOCondition condition, + gpointer user_data); + +static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc, + QIOTask *task) +{ + Error *err = NULL; + QCryptoTLSSessionHandshakeStatus status; + + if (qcrypto_tls_session_handshake(ioc->session, &err) < 0) { + trace_qio_channel_tls_handshake_fail(ioc); + qio_task_abort(task, err); + goto cleanup; + } + + status = qcrypto_tls_session_get_handshake_status(ioc->session); + if (status == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + trace_qio_channel_tls_handshake_complete(ioc); + if (qcrypto_tls_session_check_credentials(ioc->session, + &err) < 0) { + trace_qio_channel_tls_credentials_deny(ioc); + qio_task_abort(task, err); + goto cleanup; + } + trace_qio_channel_tls_credentials_allow(ioc); + qio_task_complete(task); + } else { + GIOCondition condition; + if (status == QCRYPTO_TLS_HANDSHAKE_SENDING) { + condition = G_IO_OUT; + } else { + condition = G_IO_IN; + } + + trace_qio_channel_tls_handshake_pending(ioc, status); + qio_channel_add_watch(ioc->master, + condition, + qio_channel_tls_handshake_io, + task, + NULL); + } + + cleanup: + error_free(err); +} + + +static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc, + GIOCondition condition, + gpointer user_data) +{ + QIOTask *task = user_data; + QIOChannelTLS *tioc = QIO_CHANNEL_TLS( + qio_task_get_source(task)); + + qio_channel_tls_handshake_task( + tioc, task); + + object_unref(OBJECT(tioc)); + + return FALSE; +} + +void qio_channel_tls_handshake(QIOChannelTLS *ioc, + QIOTaskFunc func, + gpointer opaque, + GDestroyNotify destroy) +{ + QIOTask *task; + + task = qio_task_new(OBJECT(ioc), + func, opaque, destroy); + + trace_qio_channel_tls_handshake_start(ioc); + qio_channel_tls_handshake_task(ioc, task); +} + + +static void qio_channel_tls_init(Object *obj G_GNUC_UNUSED) +{ +} + + +static void qio_channel_tls_finalize(Object *obj) +{ + QIOChannelTLS *ioc = QIO_CHANNEL_TLS(obj); + + object_unref(OBJECT(ioc->master)); + qcrypto_tls_session_free(ioc->session); +} + + +static ssize_t qio_channel_tls_readv(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, + size_t *nfds, + Error **errp) +{ + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); + size_t i; + ssize_t got = 0; + + for (i = 0 ; i < niov ; i++) { + ssize_t ret = qcrypto_tls_session_read(tioc->session, + iov[i].iov_base, + iov[i].iov_len); + if (ret < 0) { + if (errno == EAGAIN) { + if (got) { + return got; + } else { + return QIO_CHANNEL_ERR_BLOCK; + } + } + + error_setg_errno(errp, errno, + "Cannot read from TLS channel"); + return -1; + } + got += ret; + if (ret < iov[i].iov_len) { + break; + } + } + return got; +} + + +static ssize_t qio_channel_tls_writev(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds, + size_t nfds, + Error **errp) +{ + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); + size_t i; + ssize_t done = 0; + + for (i = 0 ; i < niov ; i++) { + ssize_t ret = qcrypto_tls_session_write(tioc->session, + iov[i].iov_base, + iov[i].iov_len); + if (ret <= 0) { + if (errno == EAGAIN) { + if (done) { + return done; + } else { + return QIO_CHANNEL_ERR_BLOCK; + } + } + + error_setg_errno(errp, errno, + "Cannot write to TLS channel"); + return -1; + } + done += ret; + if (ret < iov[i].iov_len) { + break; + } + } + return done; +} + +static int qio_channel_tls_set_blocking(QIOChannel *ioc, + bool enabled, + Error **errp) +{ + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); + + return qio_channel_set_blocking(tioc->master, enabled, errp); +} + +static void qio_channel_tls_set_delay(QIOChannel *ioc, + bool enabled) +{ + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); + + qio_channel_set_delay(tioc->master, enabled); +} + +static void qio_channel_tls_set_cork(QIOChannel *ioc, + bool enabled) +{ + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); + + qio_channel_set_cork(tioc->master, enabled); +} + +static int qio_channel_tls_shutdown(QIOChannel *ioc, + QIOChannelShutdown how, + Error **errp) +{ + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); + + return qio_channel_shutdown(tioc->master, how, errp); +} + +static int qio_channel_tls_close(QIOChannel *ioc, + Error **errp) +{ + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); + + return qio_channel_close(tioc->master, errp); +} + +static GSource *qio_channel_tls_create_watch(QIOChannel *ioc, + GIOCondition condition) +{ + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); + + return qio_channel_create_watch(tioc->master, condition); +} + +QCryptoTLSSession * +qio_channel_tls_get_session(QIOChannelTLS *ioc) +{ + return ioc->session; +} + +static void qio_channel_tls_class_init(ObjectClass *klass, + void *class_data G_GNUC_UNUSED) +{ + QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); + + ioc_klass->io_writev = qio_channel_tls_writev; + ioc_klass->io_readv = qio_channel_tls_readv; + ioc_klass->io_set_blocking = qio_channel_tls_set_blocking; + ioc_klass->io_set_delay = qio_channel_tls_set_delay; + ioc_klass->io_set_cork = qio_channel_tls_set_cork; + ioc_klass->io_close = qio_channel_tls_close; + ioc_klass->io_shutdown = qio_channel_tls_shutdown; + ioc_klass->io_create_watch = qio_channel_tls_create_watch; +} + +static const TypeInfo qio_channel_tls_info = { + .parent = TYPE_QIO_CHANNEL, + .name = TYPE_QIO_CHANNEL_TLS, + .instance_size = sizeof(QIOChannelTLS), + .instance_init = qio_channel_tls_init, + .instance_finalize = qio_channel_tls_finalize, + .class_init = qio_channel_tls_class_init, +}; + +static void qio_channel_tls_register_types(void) +{ + type_register_static(&qio_channel_tls_info); +} + +type_init(qio_channel_tls_register_types); diff --git a/qemu/io/channel-util.c b/qemu/io/channel-util.c new file mode 100644 index 000000000..0fb4bd083 --- /dev/null +++ b/qemu/io/channel-util.c @@ -0,0 +1,51 @@ +/* + * QEMU I/O channels utility APIs + * + * Copyright (c) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "io/channel-util.h" +#include "io/channel-file.h" +#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) +{ + QIOChannel *ioc; + + if (fd_is_socket(fd)) { + ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fd, errp)); + } else { + ioc = QIO_CHANNEL(qio_channel_file_new_fd(fd)); + } + return ioc; +} diff --git a/qemu/io/channel-watch.c b/qemu/io/channel-watch.c new file mode 100644 index 000000000..cf1cdff89 --- /dev/null +++ b/qemu/io/channel-watch.c @@ -0,0 +1,347 @@ +/* + * QEMU I/O channels watch helper APIs + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "io/channel-watch.h" + +typedef struct QIOChannelFDSource QIOChannelFDSource; +struct QIOChannelFDSource { + GSource parent; + GPollFD fd; + QIOChannel *ioc; + GIOCondition condition; +}; + + +#ifdef CONFIG_WIN32 +typedef struct QIOChannelSocketSource QIOChannelSocketSource; +struct QIOChannelSocketSource { + GSource parent; + GPollFD fd; + QIOChannel *ioc; + SOCKET socket; + int revents; + GIOCondition condition; +}; + +#endif + + +typedef struct QIOChannelFDPairSource QIOChannelFDPairSource; +struct QIOChannelFDPairSource { + GSource parent; + GPollFD fdread; + GPollFD fdwrite; + QIOChannel *ioc; + GIOCondition condition; +}; + + +static gboolean +qio_channel_fd_source_prepare(GSource *source G_GNUC_UNUSED, + gint *timeout) +{ + *timeout = -1; + + return FALSE; +} + + +static gboolean +qio_channel_fd_source_check(GSource *source) +{ + QIOChannelFDSource *ssource = (QIOChannelFDSource *)source; + + return ssource->fd.revents & ssource->condition; +} + + +static gboolean +qio_channel_fd_source_dispatch(GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + QIOChannelFunc func = (QIOChannelFunc)callback; + QIOChannelFDSource *ssource = (QIOChannelFDSource *)source; + + return (*func)(ssource->ioc, + ssource->fd.revents & ssource->condition, + user_data); +} + + +static void +qio_channel_fd_source_finalize(GSource *source) +{ + QIOChannelFDSource *ssource = (QIOChannelFDSource *)source; + + object_unref(OBJECT(ssource->ioc)); +} + + +#ifdef CONFIG_WIN32 +static gboolean +qio_channel_socket_source_prepare(GSource *source G_GNUC_UNUSED, + gint *timeout) +{ + *timeout = -1; + + return FALSE; +} + + +/* + * NB, this impl only works when the socket is in non-blocking + * mode on Win32 + */ +static gboolean +qio_channel_socket_source_check(GSource *source) +{ + static struct timeval tv0; + + QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source; + WSANETWORKEVENTS ev; + fd_set rfds, wfds, xfds; + + if (!ssource->condition) { + return 0; + } + + WSAEnumNetworkEvents(ssource->socket, ssource->ioc->event, &ev); + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&xfds); + if (ssource->condition & G_IO_IN) { + FD_SET((SOCKET)ssource->socket, &rfds); + } + if (ssource->condition & G_IO_OUT) { + FD_SET((SOCKET)ssource->socket, &wfds); + } + if (ssource->condition & G_IO_PRI) { + FD_SET((SOCKET)ssource->socket, &xfds); + } + ssource->revents = 0; + if (select(0, &rfds, &wfds, &xfds, &tv0) == 0) { + return 0; + } + + if (FD_ISSET(ssource->socket, &rfds)) { + ssource->revents |= G_IO_IN; + } + if (FD_ISSET(ssource->socket, &wfds)) { + ssource->revents |= G_IO_OUT; + } + if (FD_ISSET(ssource->socket, &xfds)) { + ssource->revents |= G_IO_PRI; + } + + return ssource->revents; +} + + +static gboolean +qio_channel_socket_source_dispatch(GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + QIOChannelFunc func = (QIOChannelFunc)callback; + QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source; + + return (*func)(ssource->ioc, ssource->revents, user_data); +} + + +static void +qio_channel_socket_source_finalize(GSource *source) +{ + QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source; + + object_unref(OBJECT(ssource->ioc)); +} + + +GSourceFuncs qio_channel_socket_source_funcs = { + qio_channel_socket_source_prepare, + qio_channel_socket_source_check, + qio_channel_socket_source_dispatch, + qio_channel_socket_source_finalize +}; +#endif + + +static gboolean +qio_channel_fd_pair_source_prepare(GSource *source G_GNUC_UNUSED, + gint *timeout) +{ + *timeout = -1; + + return FALSE; +} + + +static gboolean +qio_channel_fd_pair_source_check(GSource *source) +{ + QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source; + GIOCondition poll_condition = ssource->fdread.revents | + ssource->fdwrite.revents; + + return poll_condition & ssource->condition; +} + + +static gboolean +qio_channel_fd_pair_source_dispatch(GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + QIOChannelFunc func = (QIOChannelFunc)callback; + QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source; + GIOCondition poll_condition = ssource->fdread.revents | + ssource->fdwrite.revents; + + return (*func)(ssource->ioc, + poll_condition & ssource->condition, + user_data); +} + + +static void +qio_channel_fd_pair_source_finalize(GSource *source) +{ + QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source; + + object_unref(OBJECT(ssource->ioc)); +} + + +GSourceFuncs qio_channel_fd_source_funcs = { + qio_channel_fd_source_prepare, + qio_channel_fd_source_check, + qio_channel_fd_source_dispatch, + qio_channel_fd_source_finalize +}; + + +GSourceFuncs qio_channel_fd_pair_source_funcs = { + qio_channel_fd_pair_source_prepare, + qio_channel_fd_pair_source_check, + qio_channel_fd_pair_source_dispatch, + qio_channel_fd_pair_source_finalize +}; + + +GSource *qio_channel_create_fd_watch(QIOChannel *ioc, + int fd, + GIOCondition condition) +{ + GSource *source; + QIOChannelFDSource *ssource; + + source = g_source_new(&qio_channel_fd_source_funcs, + sizeof(QIOChannelFDSource)); + ssource = (QIOChannelFDSource *)source; + + ssource->ioc = ioc; + object_ref(OBJECT(ioc)); + + ssource->condition = condition; + +#ifdef CONFIG_WIN32 + ssource->fd.fd = (gint64)_get_osfhandle(fd); +#else + ssource->fd.fd = fd; +#endif + ssource->fd.events = condition; + + g_source_add_poll(source, &ssource->fd); + + return source; +} + +#ifdef CONFIG_WIN32 +GSource *qio_channel_create_socket_watch(QIOChannel *ioc, + int socket, + GIOCondition condition) +{ + GSource *source; + QIOChannelSocketSource *ssource; + + source = g_source_new(&qio_channel_socket_source_funcs, + sizeof(QIOChannelSocketSource)); + ssource = (QIOChannelSocketSource *)source; + + ssource->ioc = ioc; + object_ref(OBJECT(ioc)); + + ssource->condition = condition; + ssource->socket = socket; + ssource->revents = 0; + + ssource->fd.fd = (gintptr)ioc->event; + ssource->fd.events = G_IO_IN; + + g_source_add_poll(source, &ssource->fd); + + return source; +} +#else +GSource *qio_channel_create_socket_watch(QIOChannel *ioc, + int socket, + GIOCondition condition) +{ + return qio_channel_create_fd_watch(ioc, socket, condition); +} +#endif + +GSource *qio_channel_create_fd_pair_watch(QIOChannel *ioc, + int fdread, + int fdwrite, + GIOCondition condition) +{ + GSource *source; + QIOChannelFDPairSource *ssource; + + source = g_source_new(&qio_channel_fd_pair_source_funcs, + sizeof(QIOChannelFDPairSource)); + ssource = (QIOChannelFDPairSource *)source; + + ssource->ioc = ioc; + object_ref(OBJECT(ioc)); + + ssource->condition = condition; + +#ifdef CONFIG_WIN32 + ssource->fdread.fd = (gint64)_get_osfhandle(fdread); + ssource->fdwrite.fd = (gint64)_get_osfhandle(fdwrite); +#else + ssource->fdread.fd = fdread; + ssource->fdwrite.fd = fdwrite; +#endif + + ssource->fdread.events = condition & G_IO_IN; + ssource->fdwrite.events = condition & G_IO_OUT; + + g_source_add_poll(source, &ssource->fdread); + g_source_add_poll(source, &ssource->fdwrite); + + return source; +} diff --git a/qemu/io/channel-websock.c b/qemu/io/channel-websock.c new file mode 100644 index 000000000..708178779 --- /dev/null +++ b/qemu/io/channel-websock.c @@ -0,0 +1,964 @@ +/* + * QEMU I/O channels driver websockets + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "io/channel-websock.h" +#include "crypto/hash.h" +#include "trace.h" + + +/* Max amount to allow in rawinput/rawoutput buffers */ +#define QIO_CHANNEL_WEBSOCK_MAX_BUFFER 8192 + +#define QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN 24 +#define QIO_CHANNEL_WEBSOCK_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" +#define QIO_CHANNEL_WEBSOCK_GUID_LEN strlen(QIO_CHANNEL_WEBSOCK_GUID) + +#define QIO_CHANNEL_WEBSOCK_HEADER_PROTOCOL "Sec-WebSocket-Protocol" +#define QIO_CHANNEL_WEBSOCK_HEADER_VERSION "Sec-WebSocket-Version" +#define QIO_CHANNEL_WEBSOCK_HEADER_KEY "Sec-WebSocket-Key" + +#define QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY "binary" + +#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RESPONSE \ + "HTTP/1.1 101 Switching Protocols\r\n" \ + "Upgrade: websocket\r\n" \ + "Connection: Upgrade\r\n" \ + "Sec-WebSocket-Accept: %s\r\n" \ + "Sec-WebSocket-Protocol: binary\r\n" \ + "\r\n" +#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM "\r\n" +#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_END "\r\n\r\n" +#define QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION "13" + +/* The websockets packet header is variable length + * depending on the size of the payload... */ + +/* ...length when using 7-bit payload length */ +#define QIO_CHANNEL_WEBSOCK_HEADER_LEN_7_BIT 6 +/* ...length when using 16-bit payload length */ +#define QIO_CHANNEL_WEBSOCK_HEADER_LEN_16_BIT 8 +/* ...length when using 64-bit payload length */ +#define QIO_CHANNEL_WEBSOCK_HEADER_LEN_64_BIT 14 + +/* Length of the optional data mask field in header */ +#define QIO_CHANNEL_WEBSOCK_HEADER_LEN_MASK 4 + +/* Maximum length that can fit in 7-bit payload size */ +#define QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_THRESHOLD_7_BIT 126 +/* Maximum length that can fit in 16-bit payload size */ +#define QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_THRESHOLD_16_BIT 65536 + +/* Magic 7-bit length to indicate use of 16-bit payload length */ +#define QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_16_BIT 126 +/* Magic 7-bit length to indicate use of 64-bit payload length */ +#define QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_64_BIT 127 + +/* Bitmasks & shifts for accessing header fields */ +#define QIO_CHANNEL_WEBSOCK_HEADER_FIELD_FIN 0x80 +#define QIO_CHANNEL_WEBSOCK_HEADER_FIELD_OPCODE 0x0f +#define QIO_CHANNEL_WEBSOCK_HEADER_FIELD_HAS_MASK 0x80 +#define QIO_CHANNEL_WEBSOCK_HEADER_FIELD_PAYLOAD_LEN 0x7f +#define QIO_CHANNEL_WEBSOCK_HEADER_SHIFT_FIN 7 +#define QIO_CHANNEL_WEBSOCK_HEADER_SHIFT_HAS_MASK 7 + +typedef struct QIOChannelWebsockHeader QIOChannelWebsockHeader; + +struct QEMU_PACKED QIOChannelWebsockHeader { + unsigned char b0; + unsigned char b1; + union { + struct QEMU_PACKED { + uint16_t l16; + QIOChannelWebsockMask m16; + } s16; + struct QEMU_PACKED { + uint64_t l64; + QIOChannelWebsockMask m64; + } s64; + QIOChannelWebsockMask m; + } u; +}; + +enum { + QIO_CHANNEL_WEBSOCK_OPCODE_CONTINUATION = 0x0, + QIO_CHANNEL_WEBSOCK_OPCODE_TEXT_FRAME = 0x1, + QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME = 0x2, + QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE = 0x8, + QIO_CHANNEL_WEBSOCK_OPCODE_PING = 0x9, + QIO_CHANNEL_WEBSOCK_OPCODE_PONG = 0xA +}; + +static char *qio_channel_websock_handshake_entry(const char *handshake, + size_t handshake_len, + const char *name) +{ + char *begin, *end, *ret = NULL; + char *line = g_strdup_printf("%s%s: ", + QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM, + name); + begin = g_strstr_len(handshake, handshake_len, line); + if (begin != NULL) { + begin += strlen(line); + end = g_strstr_len(begin, handshake_len - (begin - handshake), + QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM); + if (end != NULL) { + ret = g_strndup(begin, end - begin); + } + } + g_free(line); + return ret; +} + + +static int qio_channel_websock_handshake_send_response(QIOChannelWebsock *ioc, + const char *key, + Error **errp) +{ + char combined_key[QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN + + QIO_CHANNEL_WEBSOCK_GUID_LEN + 1]; + char *accept = NULL, *response = NULL; + size_t responselen; + + g_strlcpy(combined_key, key, QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN + 1); + g_strlcat(combined_key, QIO_CHANNEL_WEBSOCK_GUID, + QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN + + QIO_CHANNEL_WEBSOCK_GUID_LEN + 1); + + /* hash and encode it */ + if (qcrypto_hash_base64(QCRYPTO_HASH_ALG_SHA1, + combined_key, + QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN + + QIO_CHANNEL_WEBSOCK_GUID_LEN, + &accept, + errp) < 0) { + return -1; + } + + response = g_strdup_printf(QIO_CHANNEL_WEBSOCK_HANDSHAKE_RESPONSE, accept); + responselen = strlen(response); + buffer_reserve(&ioc->encoutput, responselen); + buffer_append(&ioc->encoutput, response, responselen); + + g_free(accept); + g_free(response); + + return 0; +} + +static int qio_channel_websock_handshake_process(QIOChannelWebsock *ioc, + const char *line, + size_t size, + Error **errp) +{ + int ret = -1; + char *protocols = qio_channel_websock_handshake_entry( + line, size, QIO_CHANNEL_WEBSOCK_HEADER_PROTOCOL); + char *version = qio_channel_websock_handshake_entry( + line, size, QIO_CHANNEL_WEBSOCK_HEADER_VERSION); + char *key = qio_channel_websock_handshake_entry( + line, size, QIO_CHANNEL_WEBSOCK_HEADER_KEY); + + if (!protocols) { + error_setg(errp, "Missing websocket protocol header data"); + goto cleanup; + } + + if (!version) { + error_setg(errp, "Missing websocket version header data"); + goto cleanup; + } + + if (!key) { + error_setg(errp, "Missing websocket key header data"); + goto cleanup; + } + + if (!g_strrstr(protocols, QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY)) { + error_setg(errp, "No '%s' protocol is supported by client '%s'", + QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY, protocols); + goto cleanup; + } + + if (!g_str_equal(version, QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION)) { + error_setg(errp, "Version '%s' is not supported by client '%s'", + QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION, version); + goto cleanup; + } + + if (strlen(key) != QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN) { + error_setg(errp, "Key length '%zu' was not as expected '%d'", + strlen(key), QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN); + goto cleanup; + } + + ret = qio_channel_websock_handshake_send_response(ioc, key, errp); + + cleanup: + g_free(protocols); + g_free(version); + g_free(key); + return ret; +} + +static int qio_channel_websock_handshake_read(QIOChannelWebsock *ioc, + Error **errp) +{ + char *handshake_end; + ssize_t ret; + /* Typical HTTP headers from novnc are 512 bytes, so limiting + * total header size to 4096 is easily enough. */ + size_t want = 4096 - ioc->encinput.offset; + buffer_reserve(&ioc->encinput, want); + ret = qio_channel_read(ioc->master, + (char *)buffer_end(&ioc->encinput), want, errp); + if (ret < 0) { + return -1; + } + ioc->encinput.offset += ret; + + handshake_end = g_strstr_len((char *)ioc->encinput.buffer, + ioc->encinput.offset, + QIO_CHANNEL_WEBSOCK_HANDSHAKE_END); + if (!handshake_end) { + if (ioc->encinput.offset >= 4096) { + error_setg(errp, + "End of headers not found in first 4096 bytes"); + return -1; + } else { + return 0; + } + } + + if (qio_channel_websock_handshake_process(ioc, + (char *)ioc->encinput.buffer, + ioc->encinput.offset, + errp) < 0) { + return -1; + } + + buffer_advance(&ioc->encinput, + handshake_end - (char *)ioc->encinput.buffer + + strlen(QIO_CHANNEL_WEBSOCK_HANDSHAKE_END)); + return 1; +} + +static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc, + GIOCondition condition, + gpointer user_data) +{ + QIOTask *task = user_data; + QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK( + qio_task_get_source(task)); + Error *err = NULL; + ssize_t ret; + + ret = qio_channel_write(wioc->master, + (char *)wioc->encoutput.buffer, + wioc->encoutput.offset, + &err); + + if (ret < 0) { + trace_qio_channel_websock_handshake_fail(ioc); + qio_task_abort(task, err); + error_free(err); + return FALSE; + } + + buffer_advance(&wioc->encoutput, ret); + if (wioc->encoutput.offset == 0) { + trace_qio_channel_websock_handshake_complete(ioc); + qio_task_complete(task); + return FALSE; + } + trace_qio_channel_websock_handshake_pending(ioc, G_IO_OUT); + return TRUE; +} + +static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc, + GIOCondition condition, + gpointer user_data) +{ + QIOTask *task = user_data; + QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK( + qio_task_get_source(task)); + Error *err = NULL; + int ret; + + ret = qio_channel_websock_handshake_read(wioc, &err); + if (ret < 0) { + trace_qio_channel_websock_handshake_fail(ioc); + qio_task_abort(task, err); + error_free(err); + return FALSE; + } + if (ret == 0) { + trace_qio_channel_websock_handshake_pending(ioc, G_IO_IN); + /* need more data still */ + return TRUE; + } + + object_ref(OBJECT(task)); + trace_qio_channel_websock_handshake_reply(ioc); + qio_channel_add_watch( + wioc->master, + G_IO_OUT, + qio_channel_websock_handshake_send, + task, + (GDestroyNotify)object_unref); + return FALSE; +} + + +static void qio_channel_websock_encode(QIOChannelWebsock *ioc) +{ + size_t header_size; + union { + char buf[QIO_CHANNEL_WEBSOCK_HEADER_LEN_64_BIT]; + QIOChannelWebsockHeader ws; + } header; + + if (!ioc->rawoutput.offset) { + return; + } + + header.ws.b0 = (1 << QIO_CHANNEL_WEBSOCK_HEADER_SHIFT_FIN) | + (QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME & + QIO_CHANNEL_WEBSOCK_HEADER_FIELD_OPCODE); + if (ioc->rawoutput.offset < + QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_THRESHOLD_7_BIT) { + header.ws.b1 = (uint8_t)ioc->rawoutput.offset; + header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_7_BIT; + } else if (ioc->rawoutput.offset < + QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_THRESHOLD_16_BIT) { + header.ws.b1 = QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_16_BIT; + header.ws.u.s16.l16 = cpu_to_be16((uint16_t)ioc->rawoutput.offset); + header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_16_BIT; + } else { + header.ws.b1 = QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_64_BIT; + header.ws.u.s64.l64 = cpu_to_be64(ioc->rawoutput.offset); + header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_64_BIT; + } + header_size -= QIO_CHANNEL_WEBSOCK_HEADER_LEN_MASK; + + buffer_reserve(&ioc->encoutput, header_size + ioc->rawoutput.offset); + buffer_append(&ioc->encoutput, header.buf, header_size); + buffer_append(&ioc->encoutput, ioc->rawoutput.buffer, + ioc->rawoutput.offset); + buffer_reset(&ioc->rawoutput); +} + + +static ssize_t qio_channel_websock_decode_header(QIOChannelWebsock *ioc, + Error **errp) +{ + unsigned char opcode, fin, has_mask; + size_t header_size; + size_t payload_len; + QIOChannelWebsockHeader *header = + (QIOChannelWebsockHeader *)ioc->encinput.buffer; + + if (ioc->payload_remain) { + error_setg(errp, + "Decoding header but %zu bytes of payload remain", + ioc->payload_remain); + return -1; + } + if (ioc->encinput.offset < QIO_CHANNEL_WEBSOCK_HEADER_LEN_7_BIT) { + /* header not complete */ + return QIO_CHANNEL_ERR_BLOCK; + } + + fin = (header->b0 & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_FIN) >> + QIO_CHANNEL_WEBSOCK_HEADER_SHIFT_FIN; + opcode = header->b0 & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_OPCODE; + has_mask = (header->b1 & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_HAS_MASK) >> + QIO_CHANNEL_WEBSOCK_HEADER_SHIFT_HAS_MASK; + payload_len = header->b1 & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_PAYLOAD_LEN; + + if (opcode == QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE) { + /* disconnect */ + return 0; + } + + /* Websocket frame sanity check: + * * Websocket fragmentation is not supported. + * * All websockets frames sent by a client have to be masked. + * * Only binary encoding is supported. + */ + if (!fin) { + error_setg(errp, "websocket fragmentation is not supported"); + return -1; + } + if (!has_mask) { + error_setg(errp, "websocket frames must be masked"); + return -1; + } + if (opcode != QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME) { + error_setg(errp, "only binary websocket frames are supported"); + return -1; + } + + if (payload_len < QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_16_BIT) { + ioc->payload_remain = payload_len; + header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_7_BIT; + ioc->mask = header->u.m; + } else if (payload_len == QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_16_BIT && + ioc->encinput.offset >= QIO_CHANNEL_WEBSOCK_HEADER_LEN_16_BIT) { + ioc->payload_remain = be16_to_cpu(header->u.s16.l16); + header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_16_BIT; + ioc->mask = header->u.s16.m16; + } else if (payload_len == QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_64_BIT && + ioc->encinput.offset >= QIO_CHANNEL_WEBSOCK_HEADER_LEN_64_BIT) { + ioc->payload_remain = be64_to_cpu(header->u.s64.l64); + header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_64_BIT; + ioc->mask = header->u.s64.m64; + } else { + /* header not complete */ + return QIO_CHANNEL_ERR_BLOCK; + } + + buffer_advance(&ioc->encinput, header_size); + return 1; +} + + +static ssize_t qio_channel_websock_decode_payload(QIOChannelWebsock *ioc, + Error **errp) +{ + size_t i; + size_t payload_len; + uint32_t *payload32; + + if (!ioc->payload_remain) { + error_setg(errp, + "Decoding payload but no bytes of payload remain"); + return -1; + } + + /* If we aren't at the end of the payload, then drop + * off the last bytes, so we're always multiple of 4 + * for purpose of unmasking, except at end of payload + */ + if (ioc->encinput.offset < ioc->payload_remain) { + payload_len = ioc->encinput.offset - (ioc->encinput.offset % 4); + } else { + payload_len = ioc->payload_remain; + } + if (payload_len == 0) { + return QIO_CHANNEL_ERR_BLOCK; + } + + ioc->payload_remain -= payload_len; + + /* unmask frame */ + /* process 1 frame (32 bit op) */ + payload32 = (uint32_t *)ioc->encinput.buffer; + for (i = 0; i < payload_len / 4; i++) { + payload32[i] ^= ioc->mask.u; + } + /* process the remaining bytes (if any) */ + for (i *= 4; i < payload_len; i++) { + ioc->encinput.buffer[i] ^= ioc->mask.c[i % 4]; + } + + buffer_reserve(&ioc->rawinput, payload_len); + buffer_append(&ioc->rawinput, ioc->encinput.buffer, payload_len); + buffer_advance(&ioc->encinput, payload_len); + return payload_len; +} + + +QIOChannelWebsock * +qio_channel_websock_new_server(QIOChannel *master) +{ + QIOChannelWebsock *wioc; + QIOChannel *ioc; + + wioc = QIO_CHANNEL_WEBSOCK(object_new(TYPE_QIO_CHANNEL_WEBSOCK)); + ioc = QIO_CHANNEL(wioc); + + wioc->master = master; + if (master->features & (1 << QIO_CHANNEL_FEATURE_SHUTDOWN)) { + ioc->features |= (1 << QIO_CHANNEL_FEATURE_SHUTDOWN); + } + object_ref(OBJECT(master)); + + trace_qio_channel_websock_new_server(wioc, master); + return wioc; +} + +void qio_channel_websock_handshake(QIOChannelWebsock *ioc, + QIOTaskFunc func, + gpointer opaque, + GDestroyNotify destroy) +{ + QIOTask *task; + + task = qio_task_new(OBJECT(ioc), + func, + opaque, + destroy); + + trace_qio_channel_websock_handshake_start(ioc); + trace_qio_channel_websock_handshake_pending(ioc, G_IO_IN); + qio_channel_add_watch(ioc->master, + G_IO_IN, + qio_channel_websock_handshake_io, + task, + NULL); +} + + +static void qio_channel_websock_finalize(Object *obj) +{ + QIOChannelWebsock *ioc = QIO_CHANNEL_WEBSOCK(obj); + + buffer_free(&ioc->encinput); + buffer_free(&ioc->encoutput); + buffer_free(&ioc->rawinput); + buffer_free(&ioc->rawoutput); + object_unref(OBJECT(ioc->master)); + if (ioc->io_tag) { + g_source_remove(ioc->io_tag); + } + if (ioc->io_err) { + error_free(ioc->io_err); + } +} + + +static ssize_t qio_channel_websock_read_wire(QIOChannelWebsock *ioc, + Error **errp) +{ + ssize_t ret; + + if (ioc->encinput.offset < 4096) { + size_t want = 4096 - ioc->encinput.offset; + + buffer_reserve(&ioc->encinput, want); + ret = qio_channel_read(ioc->master, + (char *)ioc->encinput.buffer + + ioc->encinput.offset, + want, + errp); + if (ret < 0) { + return ret; + } + if (ret == 0 && + ioc->encinput.offset == 0) { + return 0; + } + ioc->encinput.offset += ret; + } + + if (ioc->payload_remain == 0) { + ret = qio_channel_websock_decode_header(ioc, errp); + if (ret < 0) { + return ret; + } + if (ret == 0) { + return 0; + } + } + + ret = qio_channel_websock_decode_payload(ioc, errp); + if (ret < 0) { + return ret; + } + return ret; +} + + +static ssize_t qio_channel_websock_write_wire(QIOChannelWebsock *ioc, + Error **errp) +{ + ssize_t ret; + ssize_t done = 0; + qio_channel_websock_encode(ioc); + + while (ioc->encoutput.offset > 0) { + ret = qio_channel_write(ioc->master, + (char *)ioc->encoutput.buffer, + ioc->encoutput.offset, + errp); + if (ret < 0) { + if (ret == QIO_CHANNEL_ERR_BLOCK && + done > 0) { + return done; + } else { + return ret; + } + } + buffer_advance(&ioc->encoutput, ret); + done += ret; + } + return done; +} + + +static void qio_channel_websock_flush_free(gpointer user_data) +{ + QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(user_data); + object_unref(OBJECT(wioc)); +} + +static void qio_channel_websock_set_watch(QIOChannelWebsock *ioc); + +static gboolean qio_channel_websock_flush(QIOChannel *ioc, + GIOCondition condition, + gpointer user_data) +{ + QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(user_data); + ssize_t ret; + + if (condition & G_IO_OUT) { + ret = qio_channel_websock_write_wire(wioc, &wioc->io_err); + if (ret < 0) { + goto cleanup; + } + } + + if (condition & G_IO_IN) { + ret = qio_channel_websock_read_wire(wioc, &wioc->io_err); + if (ret < 0) { + goto cleanup; + } + if (ret == 0) { + wioc->io_eof = TRUE; + } + } + + cleanup: + qio_channel_websock_set_watch(wioc); + return FALSE; +} + + +static void qio_channel_websock_unset_watch(QIOChannelWebsock *ioc) +{ + if (ioc->io_tag) { + g_source_remove(ioc->io_tag); + ioc->io_tag = 0; + } +} + +static void qio_channel_websock_set_watch(QIOChannelWebsock *ioc) +{ + GIOCondition cond = 0; + + qio_channel_websock_unset_watch(ioc); + + if (ioc->io_err) { + return; + } + + if (ioc->encoutput.offset) { + cond |= G_IO_OUT; + } + if (ioc->encinput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER && + !ioc->io_eof) { + cond |= G_IO_IN; + } + + if (cond) { + object_ref(OBJECT(ioc)); + ioc->io_tag = + qio_channel_add_watch(ioc->master, + cond, + qio_channel_websock_flush, + ioc, + qio_channel_websock_flush_free); + } +} + + +static ssize_t qio_channel_websock_readv(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, + size_t *nfds, + Error **errp) +{ + QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc); + size_t i; + ssize_t got = 0; + ssize_t ret; + + if (wioc->io_err) { + *errp = error_copy(wioc->io_err); + return -1; + } + + if (!wioc->rawinput.offset) { + ret = qio_channel_websock_read_wire(QIO_CHANNEL_WEBSOCK(ioc), errp); + if (ret < 0) { + return ret; + } + } + + for (i = 0 ; i < niov ; i++) { + size_t want = iov[i].iov_len; + if (want > (wioc->rawinput.offset - got)) { + want = (wioc->rawinput.offset - got); + } + + memcpy(iov[i].iov_base, + wioc->rawinput.buffer + got, + want); + got += want; + + if (want < iov[i].iov_len) { + break; + } + } + + buffer_advance(&wioc->rawinput, got); + qio_channel_websock_set_watch(wioc); + return got; +} + + +static ssize_t qio_channel_websock_writev(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds, + size_t nfds, + Error **errp) +{ + QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc); + size_t i; + ssize_t done = 0; + ssize_t ret; + + if (wioc->io_err) { + *errp = error_copy(wioc->io_err); + return -1; + } + + if (wioc->io_eof) { + error_setg(errp, "%s", "Broken pipe"); + return -1; + } + + for (i = 0; i < niov; i++) { + size_t want = iov[i].iov_len; + if ((want + wioc->rawoutput.offset) > QIO_CHANNEL_WEBSOCK_MAX_BUFFER) { + want = (QIO_CHANNEL_WEBSOCK_MAX_BUFFER - wioc->rawoutput.offset); + } + if (want == 0) { + goto done; + } + + buffer_reserve(&wioc->rawoutput, want); + buffer_append(&wioc->rawoutput, iov[i].iov_base, want); + done += want; + if (want < iov[i].iov_len) { + break; + } + } + + done: + ret = qio_channel_websock_write_wire(wioc, errp); + if (ret < 0 && + ret != QIO_CHANNEL_ERR_BLOCK) { + qio_channel_websock_unset_watch(wioc); + return -1; + } + + qio_channel_websock_set_watch(wioc); + + if (done == 0) { + return QIO_CHANNEL_ERR_BLOCK; + } + + return done; +} + +static int qio_channel_websock_set_blocking(QIOChannel *ioc, + bool enabled, + Error **errp) +{ + QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc); + + qio_channel_set_blocking(wioc->master, enabled, errp); + return 0; +} + +static void qio_channel_websock_set_delay(QIOChannel *ioc, + bool enabled) +{ + QIOChannelWebsock *tioc = QIO_CHANNEL_WEBSOCK(ioc); + + qio_channel_set_delay(tioc->master, enabled); +} + +static void qio_channel_websock_set_cork(QIOChannel *ioc, + bool enabled) +{ + QIOChannelWebsock *tioc = QIO_CHANNEL_WEBSOCK(ioc); + + qio_channel_set_cork(tioc->master, enabled); +} + +static int qio_channel_websock_shutdown(QIOChannel *ioc, + QIOChannelShutdown how, + Error **errp) +{ + QIOChannelWebsock *tioc = QIO_CHANNEL_WEBSOCK(ioc); + + return qio_channel_shutdown(tioc->master, how, errp); +} + +static int qio_channel_websock_close(QIOChannel *ioc, + Error **errp) +{ + QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc); + + return qio_channel_close(wioc->master, errp); +} + +typedef struct QIOChannelWebsockSource QIOChannelWebsockSource; +struct QIOChannelWebsockSource { + GSource parent; + QIOChannelWebsock *wioc; + GIOCondition condition; +}; + +static gboolean +qio_channel_websock_source_prepare(GSource *source, + gint *timeout) +{ + QIOChannelWebsockSource *wsource = (QIOChannelWebsockSource *)source; + GIOCondition cond = 0; + *timeout = -1; + + if (wsource->wioc->rawinput.offset) { + cond |= G_IO_IN; + } + if (wsource->wioc->rawoutput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER) { + cond |= G_IO_OUT; + } + + return cond & wsource->condition; +} + +static gboolean +qio_channel_websock_source_check(GSource *source) +{ + QIOChannelWebsockSource *wsource = (QIOChannelWebsockSource *)source; + GIOCondition cond = 0; + + if (wsource->wioc->rawinput.offset) { + cond |= G_IO_IN; + } + if (wsource->wioc->rawoutput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER) { + cond |= G_IO_OUT; + } + + return cond & wsource->condition; +} + +static gboolean +qio_channel_websock_source_dispatch(GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + QIOChannelFunc func = (QIOChannelFunc)callback; + QIOChannelWebsockSource *wsource = (QIOChannelWebsockSource *)source; + GIOCondition cond = 0; + + if (wsource->wioc->rawinput.offset) { + cond |= G_IO_IN; + } + if (wsource->wioc->rawoutput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER) { + cond |= G_IO_OUT; + } + + return (*func)(QIO_CHANNEL(wsource->wioc), + (cond & wsource->condition), + user_data); +} + +static void +qio_channel_websock_source_finalize(GSource *source) +{ + QIOChannelWebsockSource *ssource = (QIOChannelWebsockSource *)source; + + object_unref(OBJECT(ssource->wioc)); +} + +GSourceFuncs qio_channel_websock_source_funcs = { + qio_channel_websock_source_prepare, + qio_channel_websock_source_check, + qio_channel_websock_source_dispatch, + qio_channel_websock_source_finalize +}; + +static GSource *qio_channel_websock_create_watch(QIOChannel *ioc, + GIOCondition condition) +{ + QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc); + QIOChannelWebsockSource *ssource; + GSource *source; + + source = g_source_new(&qio_channel_websock_source_funcs, + sizeof(QIOChannelWebsockSource)); + ssource = (QIOChannelWebsockSource *)source; + + ssource->wioc = wioc; + object_ref(OBJECT(wioc)); + + ssource->condition = condition; + + qio_channel_websock_set_watch(wioc); + return source; +} + +static void qio_channel_websock_class_init(ObjectClass *klass, + void *class_data G_GNUC_UNUSED) +{ + QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); + + ioc_klass->io_writev = qio_channel_websock_writev; + ioc_klass->io_readv = qio_channel_websock_readv; + ioc_klass->io_set_blocking = qio_channel_websock_set_blocking; + ioc_klass->io_set_cork = qio_channel_websock_set_cork; + ioc_klass->io_set_delay = qio_channel_websock_set_delay; + ioc_klass->io_close = qio_channel_websock_close; + ioc_klass->io_shutdown = qio_channel_websock_shutdown; + ioc_klass->io_create_watch = qio_channel_websock_create_watch; +} + +static const TypeInfo qio_channel_websock_info = { + .parent = TYPE_QIO_CHANNEL, + .name = TYPE_QIO_CHANNEL_WEBSOCK, + .instance_size = sizeof(QIOChannelWebsock), + .instance_finalize = qio_channel_websock_finalize, + .class_init = qio_channel_websock_class_init, +}; + +static void qio_channel_websock_register_types(void) +{ + type_register_static(&qio_channel_websock_info); +} + +type_init(qio_channel_websock_register_types); diff --git a/qemu/io/channel.c b/qemu/io/channel.c new file mode 100644 index 000000000..692eb179b --- /dev/null +++ b/qemu/io/channel.c @@ -0,0 +1,307 @@ +/* + * QEMU I/O channels + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "io/channel.h" +#include "qapi/error.h" +#include "qemu/coroutine.h" + +bool qio_channel_has_feature(QIOChannel *ioc, + QIOChannelFeature feature) +{ + return ioc->features & (1 << feature); +} + + +ssize_t qio_channel_readv_full(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, + size_t *nfds, + Error **errp) +{ + QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); + + if ((fds || nfds) && + !(ioc->features & (1 << QIO_CHANNEL_FEATURE_FD_PASS))) { + error_setg_errno(errp, EINVAL, + "Channel does not support file descriptor passing"); + return -1; + } + + return klass->io_readv(ioc, iov, niov, fds, nfds, errp); +} + + +ssize_t qio_channel_writev_full(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds, + size_t nfds, + Error **errp) +{ + QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); + + if ((fds || nfds) && + !(ioc->features & (1 << QIO_CHANNEL_FEATURE_FD_PASS))) { + error_setg_errno(errp, EINVAL, + "Channel does not support file descriptor passing"); + return -1; + } + + return klass->io_writev(ioc, iov, niov, fds, nfds, errp); +} + + +ssize_t qio_channel_readv(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + Error **errp) +{ + return qio_channel_readv_full(ioc, iov, niov, NULL, NULL, errp); +} + + +ssize_t qio_channel_writev(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + Error **errp) +{ + return qio_channel_writev_full(ioc, iov, niov, NULL, 0, errp); +} + + +ssize_t qio_channel_read(QIOChannel *ioc, + char *buf, + size_t buflen, + Error **errp) +{ + struct iovec iov = { .iov_base = buf, .iov_len = buflen }; + return qio_channel_readv_full(ioc, &iov, 1, NULL, NULL, errp); +} + + +ssize_t qio_channel_write(QIOChannel *ioc, + const char *buf, + size_t buflen, + Error **errp) +{ + struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen }; + return qio_channel_writev_full(ioc, &iov, 1, NULL, 0, errp); +} + + +int qio_channel_set_blocking(QIOChannel *ioc, + bool enabled, + Error **errp) +{ + QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); + return klass->io_set_blocking(ioc, enabled, errp); +} + + +int qio_channel_close(QIOChannel *ioc, + Error **errp) +{ + QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); + return klass->io_close(ioc, errp); +} + + +GSource *qio_channel_create_watch(QIOChannel *ioc, + GIOCondition condition) +{ + QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); + return klass->io_create_watch(ioc, condition); +} + + +guint qio_channel_add_watch(QIOChannel *ioc, + GIOCondition condition, + QIOChannelFunc func, + gpointer user_data, + GDestroyNotify notify) +{ + GSource *source; + guint id; + + source = qio_channel_create_watch(ioc, condition); + + g_source_set_callback(source, (GSourceFunc)func, user_data, notify); + + id = g_source_attach(source, NULL); + g_source_unref(source); + + return id; +} + + +int qio_channel_shutdown(QIOChannel *ioc, + QIOChannelShutdown how, + Error **errp) +{ + QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); + + if (!klass->io_shutdown) { + error_setg(errp, "Data path shutdown not supported"); + return -1; + } + + return klass->io_shutdown(ioc, how, errp); +} + + +void qio_channel_set_delay(QIOChannel *ioc, + bool enabled) +{ + QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); + + if (klass->io_set_delay) { + klass->io_set_delay(ioc, enabled); + } +} + + +void qio_channel_set_cork(QIOChannel *ioc, + bool enabled) +{ + QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); + + if (klass->io_set_cork) { + klass->io_set_cork(ioc, enabled); + } +} + + +off_t qio_channel_io_seek(QIOChannel *ioc, + off_t offset, + int whence, + Error **errp) +{ + QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); + + if (!klass->io_seek) { + error_setg(errp, "Channel does not support random access"); + return -1; + } + + return klass->io_seek(ioc, offset, whence, errp); +} + + +typedef struct QIOChannelYieldData QIOChannelYieldData; +struct QIOChannelYieldData { + QIOChannel *ioc; + Coroutine *co; +}; + + +static gboolean qio_channel_yield_enter(QIOChannel *ioc, + GIOCondition condition, + gpointer opaque) +{ + QIOChannelYieldData *data = opaque; + qemu_coroutine_enter(data->co, NULL); + return FALSE; +} + + +void coroutine_fn qio_channel_yield(QIOChannel *ioc, + GIOCondition condition) +{ + QIOChannelYieldData data; + + assert(qemu_in_coroutine()); + data.ioc = ioc; + data.co = qemu_coroutine_self(); + qio_channel_add_watch(ioc, + condition, + qio_channel_yield_enter, + &data, + NULL); + qemu_coroutine_yield(); +} + + +static gboolean qio_channel_wait_complete(QIOChannel *ioc, + GIOCondition condition, + gpointer opaque) +{ + GMainLoop *loop = opaque; + + g_main_loop_quit(loop); + return FALSE; +} + + +void qio_channel_wait(QIOChannel *ioc, + GIOCondition condition) +{ + GMainContext *ctxt = g_main_context_new(); + GMainLoop *loop = g_main_loop_new(ctxt, TRUE); + GSource *source; + + source = qio_channel_create_watch(ioc, condition); + + g_source_set_callback(source, + (GSourceFunc)qio_channel_wait_complete, + loop, + NULL); + + g_source_attach(source, ctxt); + + g_main_loop_run(loop); + + g_source_unref(source); + g_main_loop_unref(loop); + g_main_context_unref(ctxt); +} + + +#ifdef _WIN32 +static void qio_channel_finalize(Object *obj) +{ + QIOChannel *ioc = QIO_CHANNEL(obj); + + if (ioc->event) { + CloseHandle(ioc->event); + } +} +#endif + +static const TypeInfo qio_channel_info = { + .parent = TYPE_OBJECT, + .name = TYPE_QIO_CHANNEL, + .instance_size = sizeof(QIOChannel), +#ifdef _WIN32 + .instance_finalize = qio_channel_finalize, +#endif + .abstract = true, + .class_size = sizeof(QIOChannelClass), +}; + + +static void qio_channel_register_types(void) +{ + type_register_static(&qio_channel_info); +} + + +type_init(qio_channel_register_types); diff --git a/qemu/io/task.c b/qemu/io/task.c new file mode 100644 index 000000000..c7f97a9b1 --- /dev/null +++ b/qemu/io/task.c @@ -0,0 +1,161 @@ +/* + * QEMU I/O task + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "io/task.h" +#include "qapi/error.h" +#include "qemu/thread.h" +#include "trace.h" + +struct QIOTask { + Object *source; + QIOTaskFunc func; + gpointer opaque; + GDestroyNotify destroy; +}; + + +QIOTask *qio_task_new(Object *source, + QIOTaskFunc func, + gpointer opaque, + GDestroyNotify destroy) +{ + QIOTask *task; + + task = g_new0(QIOTask, 1); + + task->source = source; + object_ref(source); + task->func = func; + task->opaque = opaque; + task->destroy = destroy; + + trace_qio_task_new(task, source, func, opaque); + + return task; +} + +static void qio_task_free(QIOTask *task) +{ + if (task->destroy) { + task->destroy(task->opaque); + } + object_unref(task->source); + + g_free(task); +} + + +struct QIOTaskThreadData { + QIOTask *task; + QIOTaskWorker worker; + gpointer opaque; + GDestroyNotify destroy; + Error *err; + int ret; +}; + + +static gboolean gio_task_thread_result(gpointer opaque) +{ + struct QIOTaskThreadData *data = opaque; + + trace_qio_task_thread_result(data->task); + if (data->ret == 0) { + qio_task_complete(data->task); + } else { + qio_task_abort(data->task, data->err); + } + + error_free(data->err); + if (data->destroy) { + data->destroy(data->opaque); + } + + g_free(data); + + return FALSE; +} + + +static gpointer qio_task_thread_worker(gpointer opaque) +{ + struct QIOTaskThreadData *data = opaque; + + trace_qio_task_thread_run(data->task); + data->ret = data->worker(data->task, &data->err, data->opaque); + if (data->ret < 0 && data->err == NULL) { + error_setg(&data->err, "Task worker failed but did not set an error"); + } + + /* We're running in the background thread, and must only + * ever report the task results in the main event loop + * thread. So we schedule an idle callback to report + * the worker results + */ + trace_qio_task_thread_exit(data->task); + g_idle_add(gio_task_thread_result, data); + return NULL; +} + + +void qio_task_run_in_thread(QIOTask *task, + QIOTaskWorker worker, + gpointer opaque, + GDestroyNotify destroy) +{ + struct QIOTaskThreadData *data = g_new0(struct QIOTaskThreadData, 1); + QemuThread thread; + + data->task = task; + data->worker = worker; + data->opaque = opaque; + data->destroy = destroy; + + trace_qio_task_thread_start(task, worker, opaque); + qemu_thread_create(&thread, + "io-task-worker", + qio_task_thread_worker, + data, + QEMU_THREAD_DETACHED); +} + + +void qio_task_complete(QIOTask *task) +{ + task->func(task->source, NULL, task->opaque); + trace_qio_task_complete(task); + qio_task_free(task); +} + +void qio_task_abort(QIOTask *task, + Error *err) +{ + task->func(task->source, err, task->opaque); + trace_qio_task_abort(task); + qio_task_free(task); +} + + +Object *qio_task_get_source(QIOTask *task) +{ + object_ref(task->source); + return task->source; +} diff --git a/qemu/iohandler.c b/qemu/iohandler.c index 826f713e9..f2fc8a9bd 100644 --- a/qemu/iohandler.c +++ b/qemu/iohandler.c @@ -22,7 +22,8 @@ * THE SOFTWARE. */ -#include "config-host.h" +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/queue.h" #include "block/aio.h" @@ -32,111 +33,37 @@ #include #endif -typedef struct IOHandlerRecord { - IOHandler *fd_read; - IOHandler *fd_write; - void *opaque; - QLIST_ENTRY(IOHandlerRecord) next; - int fd; - int pollfds_idx; - bool deleted; -} IOHandlerRecord; +/* This context runs on top of main loop. We can't reuse qemu_aio_context + * because iohandlers mustn't be polled by aio_poll(qemu_aio_context). */ +static AioContext *iohandler_ctx; -static QLIST_HEAD(, IOHandlerRecord) io_handlers = - QLIST_HEAD_INITIALIZER(io_handlers); - -void qemu_set_fd_handler(int fd, - IOHandler *fd_read, - IOHandler *fd_write, - void *opaque) +static void iohandler_init(void) { - IOHandlerRecord *ioh; - - assert(fd >= 0); - - if (!fd_read && !fd_write) { - QLIST_FOREACH(ioh, &io_handlers, next) { - if (ioh->fd == fd) { - ioh->deleted = 1; - break; - } - } - } else { - QLIST_FOREACH(ioh, &io_handlers, next) { - if (ioh->fd == fd) - goto found; - } - ioh = g_malloc0(sizeof(IOHandlerRecord)); - QLIST_INSERT_HEAD(&io_handlers, ioh, next); - found: - ioh->fd = fd; - ioh->fd_read = fd_read; - ioh->fd_write = fd_write; - ioh->opaque = opaque; - ioh->pollfds_idx = -1; - ioh->deleted = 0; - qemu_notify_event(); + if (!iohandler_ctx) { + iohandler_ctx = aio_context_new(&error_abort); } } -void qemu_iohandler_fill(GArray *pollfds) +AioContext *iohandler_get_aio_context(void) { - IOHandlerRecord *ioh; - - QLIST_FOREACH(ioh, &io_handlers, next) { - int events = 0; + iohandler_init(); + return iohandler_ctx; +} - if (ioh->deleted) - continue; - if (ioh->fd_read) { - events |= G_IO_IN | G_IO_HUP | G_IO_ERR; - } - if (ioh->fd_write) { - events |= G_IO_OUT | G_IO_ERR; - } - if (events) { - GPollFD pfd = { - .fd = ioh->fd, - .events = events, - }; - ioh->pollfds_idx = pollfds->len; - g_array_append_val(pollfds, pfd); - } else { - ioh->pollfds_idx = -1; - } - } +GSource *iohandler_get_g_source(void) +{ + iohandler_init(); + return aio_get_g_source(iohandler_ctx); } -void qemu_iohandler_poll(GArray *pollfds, int ret) +void qemu_set_fd_handler(int fd, + IOHandler *fd_read, + IOHandler *fd_write, + void *opaque) { - if (ret > 0) { - IOHandlerRecord *pioh, *ioh; - - QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) { - int revents = 0; - - if (!ioh->deleted && ioh->pollfds_idx != -1) { - GPollFD *pfd = &g_array_index(pollfds, GPollFD, - ioh->pollfds_idx); - revents = pfd->revents; - } - - if (!ioh->deleted && ioh->fd_read && - (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR))) { - ioh->fd_read(ioh->opaque); - } - if (!ioh->deleted && ioh->fd_write && - (revents & (G_IO_OUT | G_IO_ERR))) { - ioh->fd_write(ioh->opaque); - } - - /* Do this last in case read/write handlers marked it for deletion */ - if (ioh->deleted) { - QLIST_REMOVE(ioh, next); - g_free(ioh); - } - } - } + iohandler_init(); + aio_set_fd_handler(iohandler_ctx, fd, false, + fd_read, fd_write, opaque); } /* reaping of zombies. right now we're not passing the status to diff --git a/qemu/ioport.c b/qemu/ioport.c index e39093edb..7a84d5444 100644 --- a/qemu/ioport.c +++ b/qemu/ioport.c @@ -25,19 +25,12 @@ * splitted out ioport related stuffs from vl.c. */ +#include "qemu/osdep.h" #include "exec/ioport.h" #include "trace.h" #include "exec/memory.h" #include "exec/address-spaces.h" -//#define DEBUG_IOPORT - -#ifdef DEBUG_IOPORT -# define LOG_IOPORT(...) qemu_log_mask(CPU_LOG_IOPORT, ## __VA_ARGS__) -#else -# define LOG_IOPORT(...) do { } while (0) -#endif - typedef struct MemoryRegionPortioList { MemoryRegion mr; void *portio_opaque; @@ -62,8 +55,7 @@ const MemoryRegionOps unassigned_io_ops = { void cpu_outb(pio_addr_t addr, uint8_t val) { - LOG_IOPORT("outb: %04"FMT_pioaddr" %02"PRIx8"\n", addr, val); - trace_cpu_out(addr, val); + trace_cpu_out(addr, 'b', val); address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, &val, 1); } @@ -72,8 +64,7 @@ void cpu_outw(pio_addr_t addr, uint16_t val) { uint8_t buf[2]; - LOG_IOPORT("outw: %04"FMT_pioaddr" %04"PRIx16"\n", addr, val); - trace_cpu_out(addr, val); + trace_cpu_out(addr, 'w', val); stw_p(buf, val); address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 2); @@ -83,8 +74,7 @@ void cpu_outl(pio_addr_t addr, uint32_t val) { uint8_t buf[4]; - LOG_IOPORT("outl: %04"FMT_pioaddr" %08"PRIx32"\n", addr, val); - trace_cpu_out(addr, val); + trace_cpu_out(addr, 'l', val); stl_p(buf, val); address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 4); @@ -96,8 +86,7 @@ uint8_t cpu_inb(pio_addr_t addr) address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, &val, 1); - trace_cpu_in(addr, val); - LOG_IOPORT("inb : %04"FMT_pioaddr" %02"PRIx8"\n", addr, val); + trace_cpu_in(addr, 'b', val); return val; } @@ -108,8 +97,7 @@ uint16_t cpu_inw(pio_addr_t addr) address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 2); val = lduw_p(buf); - trace_cpu_in(addr, val); - LOG_IOPORT("inw : %04"FMT_pioaddr" %04"PRIx16"\n", addr, val); + trace_cpu_in(addr, 'w', val); return val; } @@ -120,8 +108,7 @@ uint32_t cpu_inl(pio_addr_t addr) address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 4); val = ldl_p(buf); - trace_cpu_in(addr, val); - LOG_IOPORT("inl : %04"FMT_pioaddr" %08"PRIx32"\n", addr, val); + trace_cpu_in(addr, 'l', val); return val; } diff --git a/qemu/iothread.c b/qemu/iothread.c index da6ce7b30..f183d380e 100644 --- a/qemu/iothread.c +++ b/qemu/iothread.c @@ -11,6 +11,7 @@ * */ +#include "qemu/osdep.h" #include "qom/object.h" #include "qom/object_interfaces.h" #include "qemu/module.h" @@ -72,6 +73,7 @@ static void iothread_complete(UserCreatable *obj, Error **errp) { Error *local_error = NULL; IOThread *iothread = IOTHREAD(obj); + char *name, *thread_name; iothread->stopping = false; iothread->thread_id = -1; @@ -87,8 +89,12 @@ static void iothread_complete(UserCreatable *obj, Error **errp) /* This assumes we are called from a thread with useful CPU affinity for us * to inherit. */ - qemu_thread_create(&iothread->thread, "iothread", iothread_run, + name = object_get_canonical_path_component(OBJECT(obj)); + thread_name = g_strdup_printf("IO %s", name); + qemu_thread_create(&iothread->thread, thread_name, iothread_run, iothread, QEMU_THREAD_JOINABLE); + g_free(thread_name); + g_free(name); /* Wait for initialization to complete */ qemu_mutex_lock(&iothread->init_done_lock); diff --git a/qemu/kvm-all.c b/qemu/kvm-all.c index 06e06f2b3..e7b66df19 100644 --- a/qemu/kvm-all.c +++ b/qemu/kvm-all.c @@ -13,10 +13,9 @@ * */ -#include +#include "qemu/osdep.h" #include #include -#include #include @@ -24,6 +23,7 @@ #include "qemu/atomic.h" #include "qemu/option.h" #include "qemu/config-file.h" +#include "qemu/error-report.h" #include "hw/hw.h" #include "hw/pci/msi.h" #include "hw/s390x/adapter.h" @@ -44,8 +44,10 @@ #include #endif -/* KVM uses PAGE_SIZE in its definition of COALESCED_MMIO_MAX */ -#define PAGE_SIZE TARGET_PAGE_SIZE +/* KVM uses PAGE_SIZE in its definition of KVM_COALESCED_MMIO_MAX. We + * need to use the real host PAGE_SIZE, as that's what KVM will use. + */ +#define PAGE_SIZE getpagesize() //#define DEBUG_KVM @@ -76,8 +78,6 @@ struct KVMState #ifdef KVM_CAP_SET_GUEST_DEBUG struct kvm_sw_breakpoint_head kvm_sw_breakpoints; #endif - int pit_state2; - int xsave, xcrs; int many_ioeventfds; int intx_set_mask; /* The man page (and posix) say ioctl numbers are signed int, but @@ -89,16 +89,16 @@ struct KVMState #ifdef KVM_CAP_IRQ_ROUTING struct kvm_irq_routing *irq_routes; int nr_allocated_irq_routes; - uint32_t *used_gsi_bitmap; + unsigned long *used_gsi_bitmap; unsigned int gsi_count; QTAILQ_HEAD(msi_hashtab, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE]; - bool direct_msi; #endif KVMMemoryListener memory_listener; }; KVMState *kvm_state; bool kvm_kernel_irqchip; +bool kvm_split_irqchip; bool kvm_async_interrupts_allowed; bool kvm_halt_in_kernel_allowed; bool kvm_eventfds_allowed; @@ -110,6 +110,8 @@ bool kvm_gsi_direct_mapping; bool kvm_allowed; bool kvm_readonly_mem_allowed; bool kvm_vm_attributes_allowed; +bool kvm_direct_msi_allowed; +bool kvm_ioeventfd_any_length_allowed; static const KVMCapabilityInfo kvm_required_capabilites[] = { KVM_CAP_INFO(USER_MEMORY), @@ -364,7 +366,8 @@ static void kvm_log_stop(MemoryListener *listener, static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section, unsigned long *bitmap) { - ram_addr_t start = section->offset_within_region + section->mr->ram_addr; + ram_addr_t start = section->offset_within_region + + memory_region_get_ram_addr(section->mr); ram_addr_t pages = int128_get64(section->size) / getpagesize(); cpu_physical_memory_set_dirty_lebitmap(bitmap, start, pages); @@ -641,15 +644,15 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, /* kvm works in page size chunks, but the function may be called with sub-page size and unaligned start address. Pad the start address to next and truncate size to previous page boundary. */ - delta = (TARGET_PAGE_SIZE - (start_addr & ~TARGET_PAGE_MASK)); - delta &= ~TARGET_PAGE_MASK; + delta = qemu_real_host_page_size - (start_addr & ~qemu_real_host_page_mask); + delta &= ~qemu_real_host_page_mask; if (delta > size) { return; } start_addr += delta; size -= delta; - size &= TARGET_PAGE_MASK; - if (!size || (start_addr & ~TARGET_PAGE_MASK)) { + size &= qemu_real_host_page_mask; + if (!size || (start_addr & ~qemu_real_host_page_mask)) { return; } @@ -948,12 +951,12 @@ typedef struct KVMMSIRoute { static void set_gsi(KVMState *s, unsigned int gsi) { - s->used_gsi_bitmap[gsi / 32] |= 1U << (gsi % 32); + set_bit(gsi, s->used_gsi_bitmap); } static void clear_gsi(KVMState *s, unsigned int gsi) { - s->used_gsi_bitmap[gsi / 32] &= ~(1U << (gsi % 32)); + clear_bit(gsi, s->used_gsi_bitmap); } void kvm_init_irq_routing(KVMState *s) @@ -962,23 +965,15 @@ void kvm_init_irq_routing(KVMState *s) gsi_count = kvm_check_extension(s, KVM_CAP_IRQ_ROUTING) - 1; if (gsi_count > 0) { - unsigned int gsi_bits, i; - /* Round up so we can search ints using ffs */ - gsi_bits = ALIGN(gsi_count, 32); - s->used_gsi_bitmap = g_malloc0(gsi_bits / 8); + s->used_gsi_bitmap = bitmap_new(gsi_count); s->gsi_count = gsi_count; - - /* Mark any over-allocated bits as already in use */ - for (i = gsi_count; i < gsi_bits; i++) { - set_gsi(s, i); - } } s->irq_routes = g_malloc0(sizeof(*s->irq_routes)); s->nr_allocated_irq_routes = 0; - if (!s->direct_msi) { + if (!kvm_direct_msi_allowed) { for (i = 0; i < KVM_MSI_HASHTAB_SIZE; i++) { QTAILQ_INIT(&s->msi_hashtab[i]); } @@ -1102,9 +1097,7 @@ static void kvm_flush_dynamic_msi_routes(KVMState *s) static int kvm_irqchip_get_virq(KVMState *s) { - uint32_t *word = s->used_gsi_bitmap; - int max_words = ALIGN(s->gsi_count, 32) / 32; - int i, zeroes; + int next_virq; /* * PIC and IOAPIC share the first 16 GSI numbers, thus the available @@ -1112,21 +1105,17 @@ static int kvm_irqchip_get_virq(KVMState *s) * number can succeed even though a new route entry cannot be added. * When this happens, flush dynamic MSI entries to free IRQ route entries. */ - if (!s->direct_msi && s->irq_routes->nr == s->gsi_count) { + if (!kvm_direct_msi_allowed && s->irq_routes->nr == s->gsi_count) { kvm_flush_dynamic_msi_routes(s); } /* Return the lowest unused GSI in the bitmap */ - for (i = 0; i < max_words; i++) { - zeroes = ctz32(~word[i]); - if (zeroes == 32) { - continue; - } - - return zeroes + i * 32; + next_virq = find_first_zero_bit(s->used_gsi_bitmap, s->gsi_count); + if (next_virq >= s->gsi_count) { + return -ENOSPC; + } else { + return next_virq; } - return -ENOSPC; - } static KVMMSIRoute *kvm_lookup_msi_route(KVMState *s, MSIMessage msg) @@ -1149,7 +1138,7 @@ int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg) struct kvm_msi msi; KVMMSIRoute *route; - if (s->direct_msi) { + if (kvm_direct_msi_allowed) { msi.address_lo = (uint32_t)msg.address; msi.address_hi = msg.address >> 32; msi.data = le32_to_cpu(msg.data); @@ -1188,7 +1177,7 @@ int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg) return kvm_set_irq(s, route->kroute.gsi, 1); } -int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg) +int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg, PCIDevice *dev) { struct kvm_irq_routing_entry kroute = {}; int virq; @@ -1212,7 +1201,7 @@ int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg) kroute.u.msi.address_lo = (uint32_t)msg.address; kroute.u.msi.address_hi = msg.address >> 32; kroute.u.msi.data = le32_to_cpu(msg.data); - if (kvm_arch_fixup_msi_route(&kroute, msg.address, msg.data)) { + if (kvm_arch_fixup_msi_route(&kroute, msg.address, msg.data, dev)) { kvm_irqchip_release_virq(s, virq); return -EINVAL; } @@ -1223,7 +1212,8 @@ int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg) return virq; } -int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg) +int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg, + PCIDevice *dev) { struct kvm_irq_routing_entry kroute = {}; @@ -1241,7 +1231,7 @@ int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg) kroute.u.msi.address_lo = (uint32_t)msg.address; kroute.u.msi.address_hi = msg.address >> 32; kroute.u.msi.data = le32_to_cpu(msg.data); - if (kvm_arch_fixup_msi_route(&kroute, msg.address, msg.data)) { + if (kvm_arch_fixup_msi_route(&kroute, msg.address, msg.data, dev)) { return -EINVAL; } @@ -1292,6 +1282,33 @@ int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter) kroute.u.adapter.ind_offset = adapter->ind_offset; kroute.u.adapter.adapter_id = adapter->adapter_id; + kvm_add_routing_entry(s, &kroute); + + return virq; +} + +int kvm_irqchip_add_hv_sint_route(KVMState *s, uint32_t vcpu, uint32_t sint) +{ + struct kvm_irq_routing_entry kroute = {}; + int virq; + + if (!kvm_gsi_routing_enabled()) { + return -ENOSYS; + } + if (!kvm_check_extension(s, KVM_CAP_HYPERV_SYNIC)) { + return -ENOSYS; + } + virq = kvm_irqchip_get_virq(s); + if (virq < 0) { + return virq; + } + + kroute.gsi = virq; + kroute.type = KVM_IRQ_ROUTING_HV_SINT; + kroute.flags = 0; + kroute.u.hv_sint.vcpu = vcpu; + kroute.u.hv_sint.sint = sint; + kvm_add_routing_entry(s, &kroute); kvm_irqchip_commit_routes(s); @@ -1323,6 +1340,11 @@ int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter) return -ENOSYS; } +int kvm_irqchip_add_hv_sint_route(KVMState *s, uint32_t vcpu, uint32_t sint) +{ + return -ENOSYS; +} + static int kvm_irqchip_assign_irqfd(KVMState *s, int fd, int virq, bool assign) { abort(); @@ -1395,9 +1417,14 @@ static void kvm_irqchip_create(MachineState *machine, KVMState *s) /* First probe and see if there's a arch-specific hook to create the * in-kernel irqchip for us */ - ret = kvm_arch_irqchip_create(s); + ret = kvm_arch_irqchip_create(machine, s); if (ret == 0) { - ret = kvm_vm_ioctl(s, KVM_CREATE_IRQCHIP); + if (machine_kernel_irqchip_split(machine)) { + perror("Split IRQ chip mode not supported."); + exit(1); + } else { + ret = kvm_vm_ioctl(s, KVM_CREATE_IRQCHIP); + } } if (ret < 0) { fprintf(stderr, "Create kernel irqchip failed: %s\n", strerror(-ret)); @@ -1462,7 +1489,6 @@ static int kvm_init(MachineState *ms) * page size for the system though. */ assert(TARGET_PAGE_SIZE <= getpagesize()); - page_size_init(); s->sigmask_len = 8; @@ -1585,20 +1611,8 @@ static int kvm_init(MachineState *ms) s->debugregs = kvm_check_extension(s, KVM_CAP_DEBUGREGS); #endif -#ifdef KVM_CAP_XSAVE - s->xsave = kvm_check_extension(s, KVM_CAP_XSAVE); -#endif - -#ifdef KVM_CAP_XCRS - s->xcrs = kvm_check_extension(s, KVM_CAP_XCRS); -#endif - -#ifdef KVM_CAP_PIT_STATE2 - s->pit_state2 = kvm_check_extension(s, KVM_CAP_PIT_STATE2); -#endif - #ifdef KVM_CAP_IRQ_ROUTING - s->direct_msi = (kvm_check_extension(s, KVM_CAP_SIGNAL_MSI) > 0); + kvm_direct_msi_allowed = (kvm_check_extension(s, KVM_CAP_SIGNAL_MSI) > 0); #endif s->intx_set_mask = kvm_check_extension(s, KVM_CAP_PCI_2_3); @@ -1625,6 +1639,9 @@ static int kvm_init(MachineState *ms) kvm_vm_attributes_allowed = (kvm_check_extension(s, KVM_CAP_VM_ATTRIBUTES) > 0); + kvm_ioeventfd_any_length_allowed = + (kvm_check_extension(s, KVM_CAP_IOEVENTFD_ANY_LENGTH) > 0); + ret = kvm_arch_init(ms, s); if (ret < 0) { goto err; @@ -1636,8 +1653,10 @@ static int kvm_init(MachineState *ms) kvm_state = s; - s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add; - s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del; + if (kvm_eventfds_allowed) { + s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add; + s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del; + } s->memory_listener.listener.coalesced_mmio_add = kvm_coalesce_mmio_region; s->memory_listener.listener.coalesced_mmio_del = kvm_uncoalesce_mmio_region; @@ -1779,11 +1798,6 @@ void kvm_cpu_synchronize_post_init(CPUState *cpu) run_on_cpu(cpu, do_kvm_cpu_synchronize_post_init, cpu); } -void kvm_cpu_clean_state(CPUState *cpu) -{ - cpu->kvm_vcpu_dirty = false; -} - int kvm_cpu_exec(CPUState *cpu) { struct kvm_run *run = cpu->kvm_run; @@ -1890,6 +1904,12 @@ int kvm_cpu_exec(CPUState *cpu) qemu_system_reset_request(); ret = EXCP_INTERRUPT; break; + case KVM_SYSTEM_EVENT_CRASH: + qemu_mutex_lock_iothread(); + qemu_system_guest_panicked(); + qemu_mutex_unlock_iothread(); + ret = 0; + break; default: DPRINTF("kvm_arch_handle_exit\n"); ret = kvm_arch_handle_exit(cpu, run); @@ -2003,6 +2023,39 @@ int kvm_vm_check_attr(KVMState *s, uint32_t group, uint64_t attr) return ret ? 0 : 1; } +int kvm_device_check_attr(int dev_fd, uint32_t group, uint64_t attr) +{ + struct kvm_device_attr attribute = { + .group = group, + .attr = attr, + .flags = 0, + }; + + return kvm_device_ioctl(dev_fd, KVM_HAS_DEVICE_ATTR, &attribute) ? 0 : 1; +} + +void kvm_device_access(int fd, int group, uint64_t attr, + void *val, bool write) +{ + struct kvm_device_attr kvmattr; + int err; + + kvmattr.flags = 0; + kvmattr.group = group; + kvmattr.attr = attr; + kvmattr.addr = (uintptr_t)val; + + err = kvm_device_ioctl(fd, + write ? KVM_SET_DEVICE_ATTR : KVM_GET_DEVICE_ATTR, + &kvmattr); + if (err < 0) { + error_report("KVM_%s_DEVICE_ATTR failed: %s", + write ? "SET" : "GET", strerror(-err)); + error_printf("Group %d attr 0x%016" PRIx64, group, attr); + abort(); + } +} + int kvm_has_sync_mmu(void) { return kvm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); @@ -2023,21 +2076,6 @@ int kvm_has_debugregs(void) return kvm_state->debugregs; } -int kvm_has_xsave(void) -{ - return kvm_state->xsave; -} - -int kvm_has_xcrs(void) -{ - return kvm_state->xcrs; -} - -int kvm_has_pit_state2(void) -{ - return kvm_state->pit_state2; -} - int kvm_has_many_ioeventfds(void) { if (!kvm_enabled()) { @@ -2301,6 +2339,21 @@ int kvm_create_device(KVMState *s, uint64_t type, bool test) return test ? 0 : create_dev.fd; } +bool kvm_device_supported(int vmfd, uint64_t type) +{ + struct kvm_create_device create_dev = { + .type = type, + .fd = -1, + .flags = KVM_CREATE_DEVICE_TEST, + }; + + if (ioctl(vmfd, KVM_CHECK_EXTENSION, KVM_CAP_DEVICE_CTRL) <= 0) { + return false; + } + + return (ioctl(vmfd, KVM_CREATE_DEVICE, &create_dev) >= 0); +} + int kvm_set_one_reg(CPUState *cs, uint64_t id, void *source) { struct kvm_one_reg reg; @@ -2310,7 +2363,7 @@ int kvm_set_one_reg(CPUState *cs, uint64_t id, void *source) reg.addr = (uintptr_t) source; r = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); if (r) { - trace_kvm_failed_reg_set(id, strerror(r)); + trace_kvm_failed_reg_set(id, strerror(-r)); } return r; } @@ -2324,7 +2377,7 @@ int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target) reg.addr = (uintptr_t) target; r = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); if (r) { - trace_kvm_failed_reg_get(id, strerror(r)); + trace_kvm_failed_reg_get(id, strerror(-r)); } return r; } diff --git a/qemu/kvm-stub.c b/qemu/kvm-stub.c index d9ad624ee..b962b2483 100644 --- a/qemu/kvm-stub.c +++ b/qemu/kvm-stub.c @@ -10,6 +10,7 @@ * */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/hw.h" #include "cpu.h" @@ -30,6 +31,7 @@ bool kvm_gsi_routing_allowed; bool kvm_gsi_direct_mapping; bool kvm_allowed; bool kvm_readonly_mem_allowed; +bool kvm_ioeventfd_any_length_allowed; int kvm_init_vcpu(CPUState *cpu) { @@ -67,11 +69,6 @@ int kvm_has_many_ioeventfds(void) return 0; } -int kvm_has_pit_state2(void) -{ - return 0; -} - void kvm_setup_guest_memory(void *start, size_t size) { } @@ -115,7 +112,7 @@ int kvm_on_sigbus(int code, void *addr) } #ifndef CONFIG_USER_ONLY -int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg) +int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg, PCIDevice *dev) { return -ENOSYS; } @@ -128,7 +125,8 @@ void kvm_irqchip_release_virq(KVMState *s, int virq) { } -int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg) +int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg, + PCIDevice *dev) { return -ENOSYS; } diff --git a/qemu/libcacard/Makefile b/qemu/libcacard/Makefile deleted file mode 100644 index b5eddff11..000000000 --- a/qemu/libcacard/Makefile +++ /dev/null @@ -1,45 +0,0 @@ -libcacard_includedir=$(includedir)/cacard - -TOOLS += vscclient$(EXESUF) - -# objects linked into a shared library, built with libtool with -fPIC if required -libcacard-obj-y = $(libcacard-y) -libcacard-lobj-y=$(patsubst %.o,%.lo,$(libcacard-obj-y)) - -# libtool will build the .o files, too -$(libcacard-obj-y): | $(libcacard-lobj-y) - -all: libcacard.la libcacard.pc - -vscclient$(EXESUF): libcacard/vscclient.o libcacard.la - $(call LINK,$^) - -######################################################################### -# Rules for building libcacard standalone library - -libcacard.la: LDFLAGS += -rpath $(libdir) -no-undefined \ - -export-symbols $(SRC_PATH)/libcacard/libcacard.syms -# Prevent libcacard.so linking against the entire world of 3rd party libs -libcacard.la: LIBS = -libcacard.la: $(libcacard-lobj-y) - $(call LINK,$^) - -libcacard.pc: $(SRC_PATH)/libcacard/libcacard.pc.in - $(call quiet-command,sed -e 's|@LIBDIR@|$(libdir)|' \ - -e 's|@INCLUDEDIR@|$(libcacard_includedir)|' \ - -e 's|@VERSION@|$(shell cat $(SRC_PATH)/VERSION)|' \ - -e 's|@PREFIX@|$(prefix)|' $< > libcacard.pc,\ - " GEN $@") - -.PHONY: install-libcacard - -install: install-libcacard -install-libcacard: libcacard.pc libcacard.la - $(INSTALL_DIR) "$(DESTDIR)$(libdir)" - $(INSTALL_DIR) "$(DESTDIR)$(libdir)/pkgconfig" - $(INSTALL_DIR) "$(DESTDIR)$(libcacard_includedir)" - $(INSTALL_LIB) libcacard.la "$(DESTDIR)$(libdir)" - $(INSTALL_DATA) libcacard.pc "$(DESTDIR)$(libdir)/pkgconfig" - for inc in $(SRC_PATH)/libcacard/*.h; do \ - $(INSTALL_DATA) $$inc "$(DESTDIR)$(libcacard_includedir)"; \ - done diff --git a/qemu/libcacard/cac.c b/qemu/libcacard/cac.c deleted file mode 100644 index bc84534f9..000000000 --- a/qemu/libcacard/cac.c +++ /dev/null @@ -1,414 +0,0 @@ -/* - * implement the applets for the CAC card. - * - * This code is licensed under the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "glib-compat.h" - -#include -#include - -#include "cac.h" -#include "vcard.h" -#include "vcard_emul.h" -#include "card_7816.h" - -/* private data for PKI applets */ -typedef struct CACPKIAppletDataStruct { - unsigned char *cert; - int cert_len; - unsigned char *cert_buffer; - int cert_buffer_len; - unsigned char *sign_buffer; - int sign_buffer_len; - VCardKey *key; -} CACPKIAppletData; - -/* - * CAC applet private data - */ -struct VCardAppletPrivateStruct { - union { - CACPKIAppletData pki_data; - void *reserved; - } u; -}; - -/* - * handle all the APDU's that are common to all CAC applets - */ -static VCardStatus -cac_common_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response) -{ - int ef; - VCardStatus ret = VCARD_FAIL; - - switch (apdu->a_ins) { - case VCARD7816_INS_SELECT_FILE: - if (apdu->a_p1 != 0x02) { - /* let the 7816 code handle applet switches */ - ret = VCARD_NEXT; - break; - } - /* handle file id setting */ - if (apdu->a_Lc != 2) { - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_DATA_INVALID); - ret = VCARD_DONE; - break; - } - /* CAC 1.0 only supports ef = 0 */ - ef = apdu->a_body[0] | (apdu->a_body[1] << 8); - if (ef != 0) { - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_FILE_NOT_FOUND); - ret = VCARD_DONE; - break; - } - *response = vcard_make_response(VCARD7816_STATUS_SUCCESS); - ret = VCARD_DONE; - break; - case VCARD7816_INS_GET_RESPONSE: - case VCARD7816_INS_VERIFY: - /* let the 7816 code handle these */ - ret = VCARD_NEXT; - break; - case CAC_GET_PROPERTIES: - case CAC_GET_ACR: - /* skip these for now, this will probably be needed */ - *response = vcard_make_response(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); - ret = VCARD_DONE; - break; - default: - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); - ret = VCARD_DONE; - break; - } - return ret; -} - -/* - * reset the inter call state between applet selects - */ -static VCardStatus -cac_applet_pki_reset(VCard *card, int channel) -{ - VCardAppletPrivate *applet_private; - CACPKIAppletData *pki_applet; - applet_private = vcard_get_current_applet_private(card, channel); - assert(applet_private); - pki_applet = &(applet_private->u.pki_data); - - pki_applet->cert_buffer = NULL; - g_free(pki_applet->sign_buffer); - pki_applet->sign_buffer = NULL; - pki_applet->cert_buffer_len = 0; - pki_applet->sign_buffer_len = 0; - return VCARD_DONE; -} - -static VCardStatus -cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu, - VCardResponse **response) -{ - CACPKIAppletData *pki_applet; - VCardAppletPrivate *applet_private; - int size, next; - unsigned char *sign_buffer; - bool retain_sign_buffer = FALSE; - vcard_7816_status_t status; - VCardStatus ret = VCARD_FAIL; - - applet_private = vcard_get_current_applet_private(card, apdu->a_channel); - assert(applet_private); - pki_applet = &(applet_private->u.pki_data); - - switch (apdu->a_ins) { - case CAC_UPDATE_BUFFER: - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED); - ret = VCARD_DONE; - break; - case CAC_GET_CERTIFICATE: - if ((apdu->a_p2 != 0) || (apdu->a_p1 != 0)) { - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); - break; - } - assert(pki_applet->cert != NULL); - size = apdu->a_Le; - if (pki_applet->cert_buffer == NULL) { - pki_applet->cert_buffer = pki_applet->cert; - pki_applet->cert_buffer_len = pki_applet->cert_len; - } - size = MIN(size, pki_applet->cert_buffer_len); - next = MIN(255, pki_applet->cert_buffer_len - size); - *response = vcard_response_new_bytes( - card, pki_applet->cert_buffer, size, - apdu->a_Le, next ? - VCARD7816_SW1_WARNING_CHANGE : - VCARD7816_SW1_SUCCESS, - next); - pki_applet->cert_buffer += size; - pki_applet->cert_buffer_len -= size; - if ((*response == NULL) || (next == 0)) { - pki_applet->cert_buffer = NULL; - } - if (*response == NULL) { - *response = vcard_make_response( - VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); - } - ret = VCARD_DONE; - break; - case CAC_SIGN_DECRYPT: - if (apdu->a_p2 != 0) { - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); - break; - } - size = apdu->a_Lc; - - sign_buffer = g_realloc(pki_applet->sign_buffer, - pki_applet->sign_buffer_len + size); - memcpy(sign_buffer+pki_applet->sign_buffer_len, apdu->a_body, size); - size += pki_applet->sign_buffer_len; - switch (apdu->a_p1) { - case 0x80: - /* p1 == 0x80 means we haven't yet sent the whole buffer, wait for - * the rest */ - pki_applet->sign_buffer = sign_buffer; - pki_applet->sign_buffer_len = size; - *response = vcard_make_response(VCARD7816_STATUS_SUCCESS); - retain_sign_buffer = TRUE; - break; - case 0x00: - /* we now have the whole buffer, do the operation, result will be - * in the sign_buffer */ - status = vcard_emul_rsa_op(card, pki_applet->key, - sign_buffer, size); - if (status != VCARD7816_STATUS_SUCCESS) { - *response = vcard_make_response(status); - break; - } - *response = vcard_response_new(card, sign_buffer, size, apdu->a_Le, - VCARD7816_STATUS_SUCCESS); - if (*response == NULL) { - *response = vcard_make_response( - VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); - } - break; - default: - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); - break; - } - if (!retain_sign_buffer) { - g_free(sign_buffer); - pki_applet->sign_buffer = NULL; - pki_applet->sign_buffer_len = 0; - } - ret = VCARD_DONE; - break; - case CAC_READ_BUFFER: - /* new CAC call, go ahead and use the old version for now */ - /* TODO: implement */ - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); - ret = VCARD_DONE; - break; - default: - ret = cac_common_process_apdu(card, apdu, response); - break; - } - return ret; -} - - -static VCardStatus -cac_applet_id_process_apdu(VCard *card, VCardAPDU *apdu, - VCardResponse **response) -{ - VCardStatus ret = VCARD_FAIL; - - switch (apdu->a_ins) { - case CAC_UPDATE_BUFFER: - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED); - ret = VCARD_DONE; - break; - case CAC_READ_BUFFER: - /* new CAC call, go ahead and use the old version for now */ - /* TODO: implement */ - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); - ret = VCARD_DONE; - break; - default: - ret = cac_common_process_apdu(card, apdu, response); - break; - } - return ret; -} - - -/* - * TODO: if we ever want to support general CAC middleware, we will need to - * implement the various containers. - */ -static VCardStatus -cac_applet_container_process_apdu(VCard *card, VCardAPDU *apdu, - VCardResponse **response) -{ - VCardStatus ret = VCARD_FAIL; - - switch (apdu->a_ins) { - case CAC_READ_BUFFER: - case CAC_UPDATE_BUFFER: - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); - ret = VCARD_DONE; - break; - default: - ret = cac_common_process_apdu(card, apdu, response); - break; - } - return ret; -} - -/* - * utilities for creating and destroying the private applet data - */ -static void -cac_delete_pki_applet_private(VCardAppletPrivate *applet_private) -{ - CACPKIAppletData *pki_applet_data; - - if (applet_private == NULL) { - return; - } - pki_applet_data = &(applet_private->u.pki_data); - g_free(pki_applet_data->cert); - g_free(pki_applet_data->sign_buffer); - if (pki_applet_data->key != NULL) { - vcard_emul_delete_key(pki_applet_data->key); - } - g_free(applet_private); -} - -static VCardAppletPrivate * -cac_new_pki_applet_private(const unsigned char *cert, - int cert_len, VCardKey *key) -{ - CACPKIAppletData *pki_applet_data; - VCardAppletPrivate *applet_private; - - applet_private = g_new0(VCardAppletPrivate, 1); - pki_applet_data = &(applet_private->u.pki_data); - pki_applet_data->cert = (unsigned char *)g_malloc(cert_len+1); - /* - * if we want to support compression, then we simply change the 0 to a 1 - * and compress the cert data with libz - */ - pki_applet_data->cert[0] = 0; /* not compressed */ - memcpy(&pki_applet_data->cert[1], cert, cert_len); - pki_applet_data->cert_len = cert_len+1; - - pki_applet_data->key = key; - return applet_private; -} - - -/* - * create a new cac applet which links to a given cert - */ -static VCardApplet * -cac_new_pki_applet(int i, const unsigned char *cert, - int cert_len, VCardKey *key) -{ - VCardAppletPrivate *applet_private; - VCardApplet *applet; - unsigned char pki_aid[] = { 0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00 }; - int pki_aid_len = sizeof(pki_aid); - - pki_aid[pki_aid_len-1] = i; - - applet_private = cac_new_pki_applet_private(cert, cert_len, key); - if (applet_private == NULL) { - goto failure; - } - applet = vcard_new_applet(cac_applet_pki_process_apdu, cac_applet_pki_reset, - pki_aid, pki_aid_len); - if (applet == NULL) { - goto failure; - } - vcard_set_applet_private(applet, applet_private, - cac_delete_pki_applet_private); - applet_private = NULL; - - return applet; - -failure: - if (applet_private != NULL) { - cac_delete_pki_applet_private(applet_private); - } - return NULL; -} - - -static unsigned char cac_default_container_aid[] = { - 0xa0, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00 }; -static unsigned char cac_id_aid[] = { - 0xa0, 0x00, 0x00, 0x00, 0x79, 0x03, 0x00 }; -/* - * Initialize the cac card. This is the only public function in this file. All - * the rest are connected through function pointers. - */ -VCardStatus -cac_card_init(VReader *reader, VCard *card, - const char *params, - unsigned char * const *cert, - int cert_len[], - VCardKey *key[] /* adopt the keys*/, - int cert_count) -{ - int i; - VCardApplet *applet; - - /* CAC Cards are VM Cards */ - vcard_set_type(card, VCARD_VM); - - /* create one PKI applet for each cert */ - for (i = 0; i < cert_count; i++) { - applet = cac_new_pki_applet(i, cert[i], cert_len[i], key[i]); - if (applet == NULL) { - goto failure; - } - vcard_add_applet(card, applet); - } - - /* create a default blank container applet */ - applet = vcard_new_applet(cac_applet_container_process_apdu, - NULL, cac_default_container_aid, - sizeof(cac_default_container_aid)); - if (applet == NULL) { - goto failure; - } - vcard_add_applet(card, applet); - - /* create a default blank container applet */ - applet = vcard_new_applet(cac_applet_id_process_apdu, - NULL, cac_id_aid, - sizeof(cac_id_aid)); - if (applet == NULL) { - goto failure; - } - vcard_add_applet(card, applet); - return VCARD_DONE; - -failure: - return VCARD_FAIL; -} - diff --git a/qemu/libcacard/cac.h b/qemu/libcacard/cac.h deleted file mode 100644 index d24a2a846..000000000 --- a/qemu/libcacard/cac.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * defines the entry point for the cac card. Only used by cac.c anc - * vcard_emul_type.c - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ -#ifndef CAC_H -#define CAC_H 1 -#include "vcard.h" -#include "vreader.h" - -#define CAC_GET_PROPERTIES 0x56 -#define CAC_GET_ACR 0x4c -#define CAC_READ_BUFFER 0x52 -#define CAC_UPDATE_BUFFER 0x58 -#define CAC_SIGN_DECRYPT 0x42 -#define CAC_GET_CERTIFICATE 0x36 - -/* - * Initialize the cac card. This is the only public function in this file. All - * the rest are connected through function pointers. - */ -VCardStatus cac_card_init(VReader *reader, VCard *card, const char *params, - unsigned char * const *cert, int cert_len[], - VCardKey *key[] /* adopt the keys*/, - int cert_count); - -/* not yet implemented */ -VCardStatus cac_is_cac_card(VReader *reader); -#endif diff --git a/qemu/libcacard/card_7816.c b/qemu/libcacard/card_7816.c deleted file mode 100644 index 22fd334d1..000000000 --- a/qemu/libcacard/card_7816.c +++ /dev/null @@ -1,757 +0,0 @@ -/* - * Implement the 7816 portion of the card spec - * - * This code is licensed under the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "glib-compat.h" - -#include - -#include "vcard.h" -#include "vcard_emul.h" -#include "card_7816.h" - -/* - * set the status bytes based on the status word - */ -static void -vcard_response_set_status(VCardResponse *response, vcard_7816_status_t status) -{ - unsigned char sw1, sw2; - response->b_status = status; /* make sure the status and swX representations - * are consistent */ - sw1 = (status >> 8) & 0xff; - sw2 = status & 0xff; - response->b_sw1 = sw1; - response->b_sw2 = sw2; - response->b_data[response->b_len] = sw1; - response->b_data[response->b_len+1] = sw2; -} - -/* - * set the status bytes in a response buffer - */ -static void -vcard_response_set_status_bytes(VCardResponse *response, - unsigned char sw1, unsigned char sw2) -{ - response->b_status = sw1 << 8 | sw2; - response->b_sw1 = sw1; - response->b_sw2 = sw2; - response->b_data[response->b_len] = sw1; - response->b_data[response->b_len+1] = sw2; -} - -/* - * allocate a VCardResponse structure, plus space for the data buffer, and - * set up everything but the resonse bytes. - */ -VCardResponse * -vcard_response_new_data(unsigned char *buf, int len) -{ - VCardResponse *new_response; - - new_response = g_new(VCardResponse, 1); - new_response->b_data = g_malloc(len + 2); - memcpy(new_response->b_data, buf, len); - new_response->b_total_len = len+2; - new_response->b_len = len; - new_response->b_type = VCARD_MALLOC; - return new_response; -} - -static VCardResponse * -vcard_init_buffer_response(VCard *card, unsigned char *buf, int len) -{ - VCardResponse *response; - VCardBufferResponse *buffer_response; - - buffer_response = vcard_get_buffer_response(card); - if (buffer_response) { - vcard_set_buffer_response(card, NULL); - vcard_buffer_response_delete(buffer_response); - } - buffer_response = vcard_buffer_response_new(buf, len); - if (buffer_response == NULL) { - return NULL; - } - response = vcard_response_new_status_bytes(VCARD7816_SW1_RESPONSE_BYTES, - len > 255 ? 0 : len); - if (response == NULL) { - return NULL; - } - vcard_set_buffer_response(card, buffer_response); - return response; -} - -/* - * general buffer to hold results from APDU calls - */ -VCardResponse * -vcard_response_new(VCard *card, unsigned char *buf, - int len, int Le, vcard_7816_status_t status) -{ - VCardResponse *new_response; - - if (len > Le) { - return vcard_init_buffer_response(card, buf, len); - } - new_response = vcard_response_new_data(buf, len); - if (new_response == NULL) { - return NULL; - } - vcard_response_set_status(new_response, status); - return new_response; -} - -/* - * general buffer to hold results from APDU calls - */ -VCardResponse * -vcard_response_new_bytes(VCard *card, unsigned char *buf, int len, int Le, - unsigned char sw1, unsigned char sw2) -{ - VCardResponse *new_response; - - if (len > Le) { - return vcard_init_buffer_response(card, buf, len); - } - new_response = vcard_response_new_data(buf, len); - if (new_response == NULL) { - return NULL; - } - vcard_response_set_status_bytes(new_response, sw1, sw2); - return new_response; -} - -/* - * get a new Response buffer that only has a status. - */ -static VCardResponse * -vcard_response_new_status(vcard_7816_status_t status) -{ - VCardResponse *new_response; - - new_response = g_new(VCardResponse, 1); - new_response->b_data = &new_response->b_sw1; - new_response->b_len = 0; - new_response->b_total_len = 2; - new_response->b_type = VCARD_MALLOC_STRUCT; - vcard_response_set_status(new_response, status); - return new_response; -} - -/* - * same as above, but specify the status as separate bytes - */ -VCardResponse * -vcard_response_new_status_bytes(unsigned char sw1, unsigned char sw2) -{ - VCardResponse *new_response; - - new_response = g_new(VCardResponse, 1); - new_response->b_data = &new_response->b_sw1; - new_response->b_len = 0; - new_response->b_total_len = 2; - new_response->b_type = VCARD_MALLOC_STRUCT; - vcard_response_set_status_bytes(new_response, sw1, sw2); - return new_response; -} - - -/* - * free the response buffer. The Buffer has a type to handle the buffer - * allocated in other ways than through malloc. - */ -void -vcard_response_delete(VCardResponse *response) -{ - if (response == NULL) { - return; - } - switch (response->b_type) { - case VCARD_MALLOC: - /* everything was malloc'ed */ - g_free(response->b_data); - g_free(response); - break; - case VCARD_MALLOC_DATA: - /* only the data buffer was malloc'ed */ - g_free(response->b_data); - break; - case VCARD_MALLOC_STRUCT: - /* only the structure was malloc'ed */ - g_free(response); - break; - case VCARD_STATIC: - break; - } -} - -/* - * decode the class bit and set our generic type field, channel, and - * secure messaging values. - */ -static vcard_7816_status_t -vcard_apdu_set_class(VCardAPDU *apdu) { - apdu->a_channel = 0; - apdu->a_secure_messaging = 0; - apdu->a_type = apdu->a_cla & 0xf0; - apdu->a_gen_type = VCARD_7816_ISO; - - /* parse the class tables 8 & 9 of the 7816-4 Part 4 spec */ - switch (apdu->a_type) { - /* we only support the basic types */ - case 0x00: - case 0x80: - case 0x90: - case 0xa0: - apdu->a_channel = apdu->a_cla & 3; - apdu->a_secure_messaging = apdu->a_cla & 0xe; - break; - case 0xb0: - case 0xc0: - break; - - case 0x10: - case 0x20: - case 0x30: - case 0x40: - case 0x50: - case 0x60: - case 0x70: - /* Reserved for future use */ - apdu->a_gen_type = VCARD_7816_RFU; - break; - case 0xd0: - case 0xe0: - case 0xf0: - default: - apdu->a_gen_type = - (apdu->a_cla == 0xff) ? VCARD_7816_PTS : VCARD_7816_PROPRIETARY; - break; - } - return VCARD7816_STATUS_SUCCESS; -} - -/* - * set the Le and Lc fields according to table 5 of the - * 7816-4 part 4 spec - */ -static vcard_7816_status_t -vcard_apdu_set_length(VCardAPDU *apdu) -{ - int L, Le; - - /* process according to table 5 of the 7816-4 Part 4 spec. - * variable names match the variables in the spec */ - L = apdu->a_len-4; /* fixed APDU header */ - apdu->a_Lc = 0; - apdu->a_Le = 0; - apdu->a_body = NULL; - switch (L) { - case 0: - /* 1 minimal apdu */ - return VCARD7816_STATUS_SUCCESS; - case 1: - /* 2S only return values apdu */ - /* zero maps to 256 here */ - apdu->a_Le = apdu->a_header->ah_Le ? - apdu->a_header->ah_Le : 256; - return VCARD7816_STATUS_SUCCESS; - default: - /* if the ah_Le byte is zero and we have more than - * 1 byte in the header, then we must be using extended Le and Lc. - * process the extended now. */ - if (apdu->a_header->ah_Le == 0) { - if (L < 3) { - /* coding error, need at least 3 bytes */ - return VCARD7816_STATUS_ERROR_WRONG_LENGTH; - } - /* calculate the first extended value. Could be either Le or Lc */ - Le = (apdu->a_header->ah_body[0] << 8) - || apdu->a_header->ah_body[1]; - if (L == 3) { - /* 2E extended, return data only */ - /* zero maps to 65536 */ - apdu->a_Le = Le ? Le : 65536; - return VCARD7816_STATUS_SUCCESS; - } - if (Le == 0) { - /* reserved for future use, probably for next time we need - * to extend the lengths */ - return VCARD7816_STATUS_ERROR_WRONG_LENGTH; - } - /* we know that the first extended value is Lc now */ - apdu->a_Lc = Le; - apdu->a_body = &apdu->a_header->ah_body[2]; - if (L == Le+3) { - /* 3E extended, only body parameters */ - return VCARD7816_STATUS_SUCCESS; - } - if (L == Le+5) { - /* 4E extended, parameters and return data */ - Le = (apdu->a_data[apdu->a_len-2] << 8) - || apdu->a_data[apdu->a_len-1]; - apdu->a_Le = Le ? Le : 65536; - return VCARD7816_STATUS_SUCCESS; - } - return VCARD7816_STATUS_ERROR_WRONG_LENGTH; - } - /* not extended */ - apdu->a_Lc = apdu->a_header->ah_Le; - apdu->a_body = &apdu->a_header->ah_body[0]; - if (L == apdu->a_Lc + 1) { - /* 3S only body parameters */ - return VCARD7816_STATUS_SUCCESS; - } - if (L == apdu->a_Lc + 2) { - /* 4S parameters and return data */ - Le = apdu->a_data[apdu->a_len-1]; - apdu->a_Le = Le ? Le : 256; - return VCARD7816_STATUS_SUCCESS; - } - break; - } - return VCARD7816_STATUS_ERROR_WRONG_LENGTH; -} - -/* - * create a new APDU from a raw set of bytes. This will decode all the - * above fields. users of VCARDAPDU's can then depend on the already decoded - * values. - */ -VCardAPDU * -vcard_apdu_new(unsigned char *raw_apdu, int len, vcard_7816_status_t *status) -{ - VCardAPDU *new_apdu; - - *status = VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE; - if (len < 4) { - *status = VCARD7816_STATUS_ERROR_WRONG_LENGTH; - return NULL; - } - - new_apdu = g_new(VCardAPDU, 1); - new_apdu->a_data = g_memdup(raw_apdu, len); - new_apdu->a_len = len; - *status = vcard_apdu_set_class(new_apdu); - if (*status != VCARD7816_STATUS_SUCCESS) { - g_free(new_apdu); - return NULL; - } - *status = vcard_apdu_set_length(new_apdu); - if (*status != VCARD7816_STATUS_SUCCESS) { - g_free(new_apdu); - new_apdu = NULL; - } - return new_apdu; -} - -void -vcard_apdu_delete(VCardAPDU *apdu) -{ - if (apdu == NULL) { - return; - } - g_free(apdu->a_data); - g_free(apdu); -} - - -/* - * declare response buffers for all the 7816 defined error codes - */ -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_SUCCESS) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_RET_CORUPT) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_CHANGE) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FILE_FILLED) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_CHANGE) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_LENGTH) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED) -VCARD_RESPONSE_NEW_STATIC_STATUS( - VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_INVALID) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NO_EF) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS) -VCARD_RESPONSE_NEW_STATIC_STATUS( - VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FILE_NOT_FOUND) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NOT_FOUND) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_INS_CODE_INVALID) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_INVALID) -VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_GENERAL) - -/* - * return a single response code. This function cannot fail. It will always - * return a response. - */ -VCardResponse * -vcard_make_response(vcard_7816_status_t status) -{ - VCardResponse *response; - - switch (status) { - /* known 7816 response codes */ - case VCARD7816_STATUS_SUCCESS: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_SUCCESS); - case VCARD7816_STATUS_WARNING: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_WARNING); - case VCARD7816_STATUS_WARNING_RET_CORUPT: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_WARNING_RET_CORUPT); - case VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE); - case VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED); - case VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID); - case VCARD7816_STATUS_WARNING_CHANGE: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_WARNING_CHANGE); - case VCARD7816_STATUS_WARNING_FILE_FILLED: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_WARNING_FILE_FILLED); - case VCARD7816_STATUS_EXC_ERROR: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_EXC_ERROR); - case VCARD7816_STATUS_EXC_ERROR_CHANGE: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_EXC_ERROR_CHANGE); - case VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); - case VCARD7816_STATUS_ERROR_WRONG_LENGTH: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_WRONG_LENGTH); - case VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED); - case VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED); - case VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED); - case VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); - case VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE); - case VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED); - case VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED); - case VCARD7816_STATUS_ERROR_DATA_INVALID: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_DATA_INVALID); - case VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED); - case VCARD7816_STATUS_ERROR_DATA_NO_EF: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_DATA_NO_EF); - case VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING); - case VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT); - case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_WRONG_PARAMETERS); - case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA); - case VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED); - case VCARD7816_STATUS_ERROR_FILE_NOT_FOUND: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_FILE_NOT_FOUND); - case VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND); - case VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE); - case VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT); - case VCARD7816_STATUS_ERROR_P1_P2_INCORRECT: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); - case VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT); - case VCARD7816_STATUS_ERROR_DATA_NOT_FOUND: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_DATA_NOT_FOUND); - case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2); - case VCARD7816_STATUS_ERROR_INS_CODE_INVALID: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_INS_CODE_INVALID); - case VCARD7816_STATUS_ERROR_CLA_INVALID: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_CLA_INVALID); - case VCARD7816_STATUS_ERROR_GENERAL: - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_ERROR_GENERAL); - default: - /* we don't know this status code, create a response buffer to - * hold it */ - response = vcard_response_new_status(status); - if (response == NULL) { - /* couldn't allocate the buffer, return memmory error */ - return VCARD_RESPONSE_GET_STATIC( - VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); - } - return response; - } -} - -/* - * Add File card support here if you need it. - */ -static VCardStatus -vcard7816_file_system_process_apdu(VCard *card, VCardAPDU *apdu, - VCardResponse **response) -{ - /* TODO: if we want to support a virtual file system card, we do it here. - * It would probably be a pkcs #15 card type */ - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); - return VCARD_DONE; -} - -/* - * VM card (including java cards) - */ -static VCardStatus -vcard7816_vm_process_apdu(VCard *card, VCardAPDU *apdu, - VCardResponse **response) -{ - int bytes_to_copy, next_byte_count, count; - VCardApplet *current_applet; - VCardBufferResponse *buffer_response; - vcard_7816_status_t status; - - /* parse the class first */ - if (apdu->a_gen_type != VCARD_7816_ISO) { - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); - return VCARD_DONE; - } - - /* use a switch so that if we need to support secure channel stuff later, - * we know where to put it */ - switch (apdu->a_secure_messaging) { - case 0x0: /* no SM */ - break; - case 0x4: /* proprietary SM */ - case 0x8: /* header not authenticated */ - case 0xc: /* header authenticated */ - default: - /* for now, don't try to support secure channel stuff in the - * virtual card. */ - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED); - return VCARD_DONE; - } - - /* now parse the instruction */ - switch (apdu->a_ins) { - case VCARD7816_INS_MANAGE_CHANNEL: /* secure channel op */ - case VCARD7816_INS_EXTERNAL_AUTHENTICATE: /* secure channel op */ - case VCARD7816_INS_GET_CHALLENGE: /* secure channel op */ - case VCARD7816_INS_INTERNAL_AUTHENTICATE: /* secure channel op */ - case VCARD7816_INS_ERASE_BINARY: /* applet control op */ - case VCARD7816_INS_READ_BINARY: /* applet control op */ - case VCARD7816_INS_WRITE_BINARY: /* applet control op */ - case VCARD7816_INS_UPDATE_BINARY: /* applet control op */ - case VCARD7816_INS_READ_RECORD: /* file op */ - case VCARD7816_INS_WRITE_RECORD: /* file op */ - case VCARD7816_INS_UPDATE_RECORD: /* file op */ - case VCARD7816_INS_APPEND_RECORD: /* file op */ - case VCARD7816_INS_ENVELOPE: - case VCARD7816_INS_PUT_DATA: - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); - break; - - case VCARD7816_INS_SELECT_FILE: - if (apdu->a_p1 != 0x04) { - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED); - break; - } - - /* side effect, deselect the current applet if no applet has been found - * */ - current_applet = vcard_find_applet(card, apdu->a_body, apdu->a_Lc); - vcard_select_applet(card, apdu->a_channel, current_applet); - if (current_applet) { - unsigned char *aid; - int aid_len; - aid = vcard_applet_get_aid(current_applet, &aid_len); - *response = vcard_response_new(card, aid, aid_len, apdu->a_Le, - VCARD7816_STATUS_SUCCESS); - } else { - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_FILE_NOT_FOUND); - } - break; - - case VCARD7816_INS_VERIFY: - if ((apdu->a_p1 != 0x00) || (apdu->a_p2 != 0x00)) { - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_WRONG_PARAMETERS); - } else { - if (apdu->a_Lc == 0) { - /* handle pin count if possible */ - count = vcard_emul_get_login_count(card); - if (count < 0) { - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_DATA_NOT_FOUND); - } else { - if (count > 0xf) { - count = 0xf; - } - *response = vcard_response_new_status_bytes( - VCARD7816_SW1_WARNING_CHANGE, - 0xc0 | count); - if (*response == NULL) { - *response = vcard_make_response( - VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); - } - } - } else { - status = vcard_emul_login(card, apdu->a_body, apdu->a_Lc); - *response = vcard_make_response(status); - } - } - break; - - case VCARD7816_INS_GET_RESPONSE: - buffer_response = vcard_get_buffer_response(card); - if (!buffer_response) { - *response = vcard_make_response( - VCARD7816_STATUS_ERROR_DATA_NOT_FOUND); - /* handle error */ - break; - } - bytes_to_copy = MIN(buffer_response->len, apdu->a_Le); - next_byte_count = MIN(256, buffer_response->len - bytes_to_copy); - *response = vcard_response_new_bytes( - card, buffer_response->current, bytes_to_copy, - apdu->a_Le, - next_byte_count ? - VCARD7816_SW1_RESPONSE_BYTES : VCARD7816_SW1_SUCCESS, - next_byte_count); - buffer_response->current += bytes_to_copy; - buffer_response->len -= bytes_to_copy; - if (*response == NULL || (next_byte_count == 0)) { - vcard_set_buffer_response(card, NULL); - vcard_buffer_response_delete(buffer_response); - } - if (*response == NULL) { - *response = - vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); - } - break; - - case VCARD7816_INS_GET_DATA: - *response = - vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); - break; - - default: - *response = - vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); - break; - } - - /* response should have been set somewhere */ - assert(*response != NULL); - return VCARD_DONE; -} - - -/* - * APDU processing starts here. This routes the card processing stuff to the - * right location. - */ -VCardStatus -vcard_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response) -{ - VCardStatus status; - VCardBufferResponse *buffer_response; - - /* first handle any PTS commands, which aren't really APDU's */ - if (apdu->a_type == VCARD_7816_PTS) { - /* the PTS responses aren't really responses either */ - *response = vcard_response_new_data(apdu->a_data, apdu->a_len); - /* PTS responses have no status bytes */ - (*response)->b_total_len = (*response)->b_len; - return VCARD_DONE; - } - buffer_response = vcard_get_buffer_response(card); - if (buffer_response && apdu->a_ins != VCARD7816_INS_GET_RESPONSE) { - /* clear out buffer_response, return an error */ - vcard_set_buffer_response(card, NULL); - vcard_buffer_response_delete(buffer_response); - *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR); - return VCARD_DONE; - } - - status = vcard_process_applet_apdu(card, apdu, response); - if (status != VCARD_NEXT) { - return status; - } - switch (vcard_get_type(card)) { - case VCARD_FILE_SYSTEM: - return vcard7816_file_system_process_apdu(card, apdu, response); - case VCARD_VM: - return vcard7816_vm_process_apdu(card, apdu, response); - case VCARD_DIRECT: - /* if we are type direct, then the applet should handle everything */ - assert(!"VCARD_DIRECT: applet failure"); - break; - } - *response = - vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); - return VCARD_DONE; -} diff --git a/qemu/libcacard/card_7816.h b/qemu/libcacard/card_7816.h deleted file mode 100644 index 4a01993d2..000000000 --- a/qemu/libcacard/card_7816.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Implement the 7816 portion of the card spec - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ -#ifndef CARD_7816_H -#define CARD_7816_H 1 - -#include "card_7816t.h" -#include "vcardt.h" - -/* - * constructors for VCardResponse's - */ -/* response from a return buffer and a status */ -VCardResponse *vcard_response_new(VCard *card, unsigned char *buf, int len, - int Le, vcard_7816_status_t status); -/* response from a return buffer and status bytes */ -VCardResponse *vcard_response_new_bytes(VCard *card, unsigned char *buf, - int len, int Le, - unsigned char sw1, unsigned char sw2); -/* response from just status bytes */ -VCardResponse *vcard_response_new_status_bytes(unsigned char sw1, - unsigned char sw2); -/* response from just status: NOTE this cannot fail, it will always return a - * valid response, if it can't allocate memory, the response will be - * VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE */ -VCardResponse *vcard_make_response(vcard_7816_status_t status); - -/* create a raw response (status has already been encoded */ -VCardResponse *vcard_response_new_data(unsigned char *buf, int len); - - - - -/* - * destructor for VCardResponse. - * Can be called with a NULL response - */ -void vcard_response_delete(VCardResponse *response); - -/* - * constructor for VCardAPDU - */ -VCardAPDU *vcard_apdu_new(unsigned char *raw_apdu, int len, - unsigned short *status); - -/* - * destructor for VCardAPDU - * Can be called with a NULL apdu - */ -void vcard_apdu_delete(VCardAPDU *apdu); - -/* - * APDU processing starts here. This routes the card processing stuff to the - * right location. Always returns a valid response. - */ -VCardStatus vcard_process_apdu(VCard *card, VCardAPDU *apdu, - VCardResponse **response); - -#endif diff --git a/qemu/libcacard/card_7816t.h b/qemu/libcacard/card_7816t.h deleted file mode 100644 index 8eef0ce42..000000000 --- a/qemu/libcacard/card_7816t.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Implement the 7816 portion of the card spec - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ -#ifndef CARD_7816T_H -#define CARD_7816T_H 1 - -typedef unsigned short vcard_7816_status_t; - -struct VCardResponseStruct { - unsigned char *b_data; - vcard_7816_status_t b_status; - unsigned char b_sw1; - unsigned char b_sw2; - int b_len; - int b_total_len; - enum VCardResponseBufferType { - VCARD_MALLOC, - VCARD_MALLOC_DATA, - VCARD_MALLOC_STRUCT, - VCARD_STATIC - } b_type; -}; - -#define VCARD_RESPONSE_NEW_STATIC_STATUS(stat) \ -static const VCardResponse VCardResponse##stat = \ - {(unsigned char *)&VCardResponse##stat.b_sw1, (stat), ((stat) >> 8), \ - ((stat) & 0xff), 0, 2, VCARD_STATIC}; - -#define VCARD_RESPONSE_NEW_STATIC_STATUS_BYTES(sw1, sw2) \ -static const VCardResponse VCARDResponse##sw1 = \ - {(unsigned char *)&VCardResponse##name.b_sw1, ((sw1) << 8 | (sw2)), \ - (sw1), (sw2), 0, 2, VCARD_STATIC}; - -/* cast away the const, callers need may need to 'free' the - * result, and const implies that they don't */ -#define VCARD_RESPONSE_GET_STATIC(name) \ - ((VCardResponse *)(&VCardResponse##name)) - -typedef enum { - VCARD_7816_ISO, - VCARD_7816_RFU, - VCARD_7816_PTS, - VCARD_7816_PROPRIETARY -} VCardAPDUType; - - -/* - * 7816 header. All APDU's have this header. - * They must be laid out in this order. - */ -struct VCardAPDUHeader { - unsigned char ah_cla; - unsigned char ah_ins; - unsigned char ah_p1; - unsigned char ah_p2; - unsigned char ah_Le; - unsigned char ah_body[1]; /* indefinate length */ -}; - -/* - * 7816 APDU structure. The raw bytes are stored in the union and can be - * accessed directly through u.data (which is aliased as a_data). - * - * Names of the fields match the 7816 documentation. - */ -struct VCardAPDUStruct { - int a_len; /* length of the whole buffer, including header */ - int a_Lc; /* 7816 Lc (parameter length) value */ - int a_Le; /* 7816 Le (expected result length) value */ - unsigned char *a_body; /* pointer to the parameter */ - int a_channel; /* decoded channel */ - int a_secure_messaging; /* decoded secure messaging type */ - int a_type; /* decoded type from cla (top nibble of class) */ - VCardAPDUType a_gen_type; /* generic type (7816, PROPRIETARY, RFU, etc) */ - union { - struct VCardAPDUHeader *header; - unsigned char *data; - } u; -/* give the subfields a unified look */ -#define a_header u.header -#define a_data u.data -#define a_cla a_header->ah_cla /* class */ -#define a_ins a_header->ah_ins /* instruction */ -#define a_p1 a_header->ah_p1 /* parameter 1 */ -#define a_p2 a_header->ah_p2 /* parameter 2 */ -}; - -/* 7816 status codes */ -#define VCARD7816_STATUS_SUCCESS 0x9000 -#define VCARD7816_STATUS_WARNING 0x6200 -#define VCARD7816_STATUS_WARNING_RET_CORUPT 0x6281 -#define VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE 0x6282 -#define VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED 0x6283 -#define VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID 0x6284 -#define VCARD7816_STATUS_WARNING_CHANGE 0x6300 -#define VCARD7816_STATUS_WARNING_FILE_FILLED 0x6381 -#define VCARD7816_STATUS_EXC_ERROR 0x6400 -#define VCARD7816_STATUS_EXC_ERROR_CHANGE 0x6500 -#define VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE 0x6581 -#define VCARD7816_STATUS_ERROR_WRONG_LENGTH 0x6700 -#define VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED 0x6800 -#define VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED 0x6881 -#define VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED 0x6882 -#define VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED 0x6900 -#define VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE 0x6981 -#define VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED 0x6982 -#define VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED 0x6983 -#define VCARD7816_STATUS_ERROR_DATA_INVALID 0x6984 -#define VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED 0x6985 -#define VCARD7816_STATUS_ERROR_DATA_NO_EF 0x6986 -#define VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING 0x6987 -#define VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT 0x6988 -#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS 0x6a00 -#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA 0x6a80 -#define VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED 0x6a81 -#define VCARD7816_STATUS_ERROR_FILE_NOT_FOUND 0x6a82 -#define VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND 0x6a83 -#define VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE 0x6a84 -#define VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT 0x6a85 -#define VCARD7816_STATUS_ERROR_P1_P2_INCORRECT 0x6a86 -#define VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT 0x6a87 -#define VCARD7816_STATUS_ERROR_DATA_NOT_FOUND 0x6a88 -#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2 0x6b00 -#define VCARD7816_STATUS_ERROR_INS_CODE_INVALID 0x6d00 -#define VCARD7816_STATUS_ERROR_CLA_INVALID 0x6e00 -#define VCARD7816_STATUS_ERROR_GENERAL 0x6f00 -/* 7816 sw1 codes */ -#define VCARD7816_SW1_SUCCESS 0x90 -#define VCARD7816_SW1_RESPONSE_BYTES 0x61 -#define VCARD7816_SW1_WARNING 0x62 -#define VCARD7816_SW1_WARNING_CHANGE 0x63 -#define VCARD7816_SW1_EXC_ERROR 0x64 -#define VCARD7816_SW1_EXC_ERROR_CHANGE 0x65 -#define VCARD7816_SW1_ERROR_WRONG_LENGTH 0x67 -#define VCARD7816_SW1_CLA_ERROR 0x68 -#define VCARD7816_SW1_COMMAND_ERROR 0x69 -#define VCARD7816_SW1_P1_P2_ERROR 0x6a -#define VCARD7816_SW1_LE_ERROR 0x6c -#define VCARD7816_SW1_INS_ERROR 0x6d -#define VCARD7816_SW1_CLA_NOT_SUPPORTED 0x6e - -/* 7816 Instructions */ -#define VCARD7816_INS_MANAGE_CHANNEL 0x70 -#define VCARD7816_INS_EXTERNAL_AUTHENTICATE 0x82 -#define VCARD7816_INS_GET_CHALLENGE 0x84 -#define VCARD7816_INS_INTERNAL_AUTHENTICATE 0x88 -#define VCARD7816_INS_ERASE_BINARY 0x0e -#define VCARD7816_INS_READ_BINARY 0xb0 -#define VCARD7816_INS_WRITE_BINARY 0xd0 -#define VCARD7816_INS_UPDATE_BINARY 0xd6 -#define VCARD7816_INS_READ_RECORD 0xb2 -#define VCARD7816_INS_WRITE_RECORD 0xd2 -#define VCARD7816_INS_UPDATE_RECORD 0xdc -#define VCARD7816_INS_APPEND_RECORD 0xe2 -#define VCARD7816_INS_ENVELOPE 0xc2 -#define VCARD7816_INS_PUT_DATA 0xda -#define VCARD7816_INS_GET_DATA 0xca -#define VCARD7816_INS_SELECT_FILE 0xa4 -#define VCARD7816_INS_VERIFY 0x20 -#define VCARD7816_INS_GET_RESPONSE 0xc0 - -#endif diff --git a/qemu/libcacard/event.c b/qemu/libcacard/event.c deleted file mode 100644 index 63f4057fe..000000000 --- a/qemu/libcacard/event.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * event queue implementation. - * - * This code is licensed under the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "glib-compat.h" - -#include "vcard.h" -#include "vreader.h" -#include "vevent.h" - -VEvent * -vevent_new(VEventType type, VReader *reader, VCard *card) -{ - VEvent *new_vevent; - - new_vevent = g_new(VEvent, 1); - new_vevent->next = NULL; - new_vevent->type = type; - new_vevent->reader = vreader_reference(reader); - new_vevent->card = vcard_reference(card); - - return new_vevent; -} - -void -vevent_delete(VEvent *vevent) -{ - if (vevent == NULL) { - return; - } - vreader_free(vevent->reader); - vcard_free(vevent->card); - g_free(vevent); -} - -/* - * VEvent queue management - */ - -static VEvent *vevent_queue_head; -static VEvent *vevent_queue_tail; -static CompatGMutex vevent_queue_lock; -static CompatGCond vevent_queue_condition; - -void vevent_queue_init(void) -{ - vevent_queue_head = vevent_queue_tail = NULL; -} - -void -vevent_queue_vevent(VEvent *vevent) -{ - vevent->next = NULL; - g_mutex_lock(&vevent_queue_lock); - if (vevent_queue_head) { - assert(vevent_queue_tail); - vevent_queue_tail->next = vevent; - } else { - vevent_queue_head = vevent; - } - vevent_queue_tail = vevent; - g_cond_signal(&vevent_queue_condition); - g_mutex_unlock(&vevent_queue_lock); -} - -/* must have lock */ -static VEvent * -vevent_dequeue_vevent(void) -{ - VEvent *vevent = NULL; - if (vevent_queue_head) { - vevent = vevent_queue_head; - vevent_queue_head = vevent->next; - vevent->next = NULL; - } - return vevent; -} - -VEvent *vevent_wait_next_vevent(void) -{ - VEvent *vevent; - - g_mutex_lock(&vevent_queue_lock); - while ((vevent = vevent_dequeue_vevent()) == NULL) { - g_cond_wait(&vevent_queue_condition, &vevent_queue_lock); - } - g_mutex_unlock(&vevent_queue_lock); - return vevent; -} - -VEvent *vevent_get_next_vevent(void) -{ - VEvent *vevent; - - g_mutex_lock(&vevent_queue_lock); - vevent = vevent_dequeue_vevent(); - g_mutex_unlock(&vevent_queue_lock); - return vevent; -} - diff --git a/qemu/libcacard/eventt.h b/qemu/libcacard/eventt.h deleted file mode 100644 index 0dc7bd468..000000000 --- a/qemu/libcacard/eventt.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#ifndef EVENTT_H -#define EVENTT_H 1 -#include "vreadert.h" -#include "vcardt.h" - -typedef struct VEventStruct VEvent; - -typedef enum { - VEVENT_READER_INSERT, - VEVENT_READER_REMOVE, - VEVENT_CARD_INSERT, - VEVENT_CARD_REMOVE, - VEVENT_LAST, -} VEventType; - -struct VEventStruct { - VEvent *next; - VEventType type; - VReader *reader; - VCard *card; -}; -#endif - - diff --git a/qemu/libcacard/libcacard.pc.in b/qemu/libcacard/libcacard.pc.in deleted file mode 100644 index 4b60023ce..000000000 --- a/qemu/libcacard/libcacard.pc.in +++ /dev/null @@ -1,13 +0,0 @@ -prefix=@PREFIX@ -exec_prefix=${prefix} -libdir=@LIBDIR@ -includedir=@INCLUDEDIR@ - -Name: cacard -Description: CA Card library -Version: @VERSION@ - -Requires.private: nss glib-2.0 -Libs: -L${libdir} -lcacard -Libs.private: -Cflags: -I${includedir} diff --git a/qemu/libcacard/libcacard.syms b/qemu/libcacard/libcacard.syms deleted file mode 100644 index 1697515a7..000000000 --- a/qemu/libcacard/libcacard.syms +++ /dev/null @@ -1,77 +0,0 @@ -cac_card_init -cac_is_cac_card -vcard_add_applet -vcard_apdu_delete -vcard_apdu_new -vcard_applet_get_aid -vcard_buffer_response_delete -vcard_buffer_response_new -vcard_delete_applet -vcard_emul_delete_key -vcard_emul_force_card_insert -vcard_emul_force_card_remove -vcard_emul_get_atr -vcard_emul_get_login_count -vcard_emul_init -vcard_emul_login -vcard_emul_options -vcard_emul_replay_insertion_events -vcard_emul_reset -vcard_emul_rsa_op -vcard_emul_type_from_string -vcard_emul_type_select -vcard_emul_usage -vcard_find_applet -vcard_free -vcard_get_atr -vcard_get_buffer_response -vcard_get_current_applet_private -vcard_get_private -vcard_get_type -vcard_init -vcard_make_response -vcard_new -vcard_new_applet -vcard_process_apdu -vcard_process_applet_apdu -vcard_reference -vcard_reset -vcard_response_delete -vcard_response_new -vcard_response_new_bytes -vcard_response_new_data -vcard_response_new_status_bytes -vcard_select_applet -vcard_set_applet_private -vcard_set_atr_func -vcard_set_buffer_response -vcard_set_type -vevent_delete -vevent_get_next_vevent -vevent_new -vevent_queue_init -vevent_queue_vevent -vevent_wait_next_vevent -vreader_add_reader -vreader_card_is_present -vreader_free -vreader_get_id -vreader_get_name -vreader_get_private -vreader_get_reader_by_id -vreader_get_reader_by_name -vreader_get_reader_list -vreader_init -vreader_insert_card -vreader_list_delete -vreader_list_get_first -vreader_list_get_next -vreader_list_get_reader -vreader_new -vreader_power_off -vreader_power_on -vreader_queue_card_event -vreader_reference -vreader_remove_reader -vreader_set_id -vreader_xfr_bytes diff --git a/qemu/libcacard/link_test.c b/qemu/libcacard/link_test.c deleted file mode 100644 index 6f67a23d9..000000000 --- a/qemu/libcacard/link_test.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include -#include "vcard.h" - -VCardStatus cac_card_init(const char *flags, VCard *card, - const unsigned char *cert[], - int cert_len[], VCardKey *key[] /* adopt the keys*/, - int cert_count); -/* - * this will crash... just test the linkage right now - */ - -main(int argc, char **argv) -{ - VCard *card; /* no constructor yet */ - cac_card_init("", card, NULL, 0, NULL, 0); -} - diff --git a/qemu/libcacard/vcard.c b/qemu/libcacard/vcard.c deleted file mode 100644 index 1a87208f3..000000000 --- a/qemu/libcacard/vcard.c +++ /dev/null @@ -1,325 +0,0 @@ -/* - * implement the Java card standard. - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "glib-compat.h" - -#include - -#include "vcard.h" -#include "vcard_emul.h" -#include "card_7816t.h" - -struct VCardAppletStruct { - VCardApplet *next; - VCardProcessAPDU process_apdu; - VCardResetApplet reset_applet; - unsigned char *aid; - int aid_len; - void *applet_private; - VCardAppletPrivateFree applet_private_free; -}; - -struct VCardStruct { - int reference_count; - VCardApplet *applet_list; - VCardApplet *current_applet[MAX_CHANNEL]; - VCardBufferResponse *vcard_buffer_response; - VCardType type; - VCardEmul *vcard_private; - VCardEmulFree vcard_private_free; - VCardGetAtr vcard_get_atr; -}; - -VCardBufferResponse * -vcard_buffer_response_new(unsigned char *buffer, int size) -{ - VCardBufferResponse *new_buffer; - - new_buffer = g_new(VCardBufferResponse, 1); - new_buffer->buffer = (unsigned char *)g_memdup(buffer, size); - new_buffer->buffer_len = size; - new_buffer->current = new_buffer->buffer; - new_buffer->len = size; - return new_buffer; -} - -void -vcard_buffer_response_delete(VCardBufferResponse *buffer_response) -{ - if (buffer_response == NULL) { - return; - } - g_free(buffer_response->buffer); - g_free(buffer_response); -} - - -/* - * clean up state after a reset - */ -void -vcard_reset(VCard *card, VCardPower power) -{ - int i; - VCardApplet *applet = NULL; - - if (card->type == VCARD_DIRECT) { - /* select the last applet */ - VCardApplet *current_applet = NULL; - for (current_applet = card->applet_list; current_applet; - current_applet = current_applet->next) { - applet = current_applet; - } - } - for (i = 0; i < MAX_CHANNEL; i++) { - card->current_applet[i] = applet; - } - if (card->vcard_buffer_response) { - vcard_buffer_response_delete(card->vcard_buffer_response); - card->vcard_buffer_response = NULL; - } - vcard_emul_reset(card, power); - if (applet) { - applet->reset_applet(card, 0); - } -} - -/* applet utilities */ - -/* - * applet utilities - */ -/* constructor */ -VCardApplet * -vcard_new_applet(VCardProcessAPDU applet_process_function, - VCardResetApplet applet_reset_function, - unsigned char *aid, int aid_len) -{ - VCardApplet *applet; - - applet = g_new0(VCardApplet, 1); - applet->process_apdu = applet_process_function; - applet->reset_applet = applet_reset_function; - - applet->aid = g_memdup(aid, aid_len); - applet->aid_len = aid_len; - return applet; -} - -/* destructor */ -void -vcard_delete_applet(VCardApplet *applet) -{ - if (applet == NULL) { - return; - } - if (applet->applet_private_free) { - applet->applet_private_free(applet->applet_private); - } - g_free(applet->aid); - g_free(applet); -} - -/* accessor */ -void -vcard_set_applet_private(VCardApplet *applet, VCardAppletPrivate *private, - VCardAppletPrivateFree private_free) -{ - if (applet->applet_private_free) { - applet->applet_private_free(applet->applet_private); - } - applet->applet_private = private; - applet->applet_private_free = private_free; -} - -VCard * -vcard_new(VCardEmul *private, VCardEmulFree private_free) -{ - VCard *new_card; - - new_card = g_new0(VCard, 1); - new_card->type = VCARD_VM; - new_card->vcard_private = private; - new_card->vcard_private_free = private_free; - new_card->reference_count = 1; - return new_card; -} - -VCard * -vcard_reference(VCard *vcard) -{ - if (vcard == NULL) { - return NULL; - } - vcard->reference_count++; - return vcard; -} - -void -vcard_free(VCard *vcard) -{ - VCardApplet *current_applet; - VCardApplet *next_applet; - - if (vcard == NULL) { - return; - } - vcard->reference_count--; - if (vcard->reference_count != 0) { - return; - } - if (vcard->vcard_private_free) { - (*vcard->vcard_private_free)(vcard->vcard_private); - } - for (current_applet = vcard->applet_list; current_applet; - current_applet = next_applet) { - next_applet = current_applet->next; - vcard_delete_applet(current_applet); - } - vcard_buffer_response_delete(vcard->vcard_buffer_response); - g_free(vcard); -} - -void -vcard_get_atr(VCard *vcard, unsigned char *atr, int *atr_len) -{ - if (vcard->vcard_get_atr) { - (*vcard->vcard_get_atr)(vcard, atr, atr_len); - return; - } - vcard_emul_get_atr(vcard, atr, atr_len); -} - -void -vcard_set_atr_func(VCard *card, VCardGetAtr vcard_get_atr) -{ - card->vcard_get_atr = vcard_get_atr; -} - - -VCardStatus -vcard_add_applet(VCard *card, VCardApplet *applet) -{ - applet->next = card->applet_list; - card->applet_list = applet; - /* if our card-type is direct, always call the applet */ - if (card->type == VCARD_DIRECT) { - int i; - - for (i = 0; i < MAX_CHANNEL; i++) { - card->current_applet[i] = applet; - } - } - return VCARD_DONE; -} - -/* - * manage applets - */ -VCardApplet * -vcard_find_applet(VCard *card, unsigned char *aid, int aid_len) -{ - VCardApplet *current_applet; - - for (current_applet = card->applet_list; current_applet; - current_applet = current_applet->next) { - if (current_applet->aid_len != aid_len) { - continue; - } - if (memcmp(current_applet->aid, aid, aid_len) == 0) { - break; - } - } - return current_applet; -} - -unsigned char * -vcard_applet_get_aid(VCardApplet *applet, int *aid_len) -{ - if (applet == NULL) { - return NULL; - } - *aid_len = applet->aid_len; - return applet->aid; -} - - -void -vcard_select_applet(VCard *card, int channel, VCardApplet *applet) -{ - assert(channel < MAX_CHANNEL); - - /* If using an emulated card, make sure to log out of any already logged in - * session. */ - vcard_emul_logout(card); - - card->current_applet[channel] = applet; - /* reset the applet */ - if (applet && applet->reset_applet) { - applet->reset_applet(card, channel); - } -} - -VCardAppletPrivate * -vcard_get_current_applet_private(VCard *card, int channel) -{ - VCardApplet *applet = card->current_applet[channel]; - - if (applet == NULL) { - return NULL; - } - return applet->applet_private; -} - -VCardStatus -vcard_process_applet_apdu(VCard *card, VCardAPDU *apdu, - VCardResponse **response) -{ - if (card->current_applet[apdu->a_channel]) { - return card->current_applet[apdu->a_channel]->process_apdu( - card, apdu, response); - } - return VCARD_NEXT; -} - -/* - * Accessor functions - */ -/* accessor functions for the response buffer */ -VCardBufferResponse * -vcard_get_buffer_response(VCard *card) -{ - return card->vcard_buffer_response; -} - -void -vcard_set_buffer_response(VCard *card, VCardBufferResponse *buffer) -{ - card->vcard_buffer_response = buffer; -} - - -/* accessor functions for the type */ -VCardType -vcard_get_type(VCard *card) -{ - return card->type; -} - -void -vcard_set_type(VCard *card, VCardType type) -{ - card->type = type; -} - -/* accessor for private data */ -VCardEmul * -vcard_get_private(VCard *vcard) -{ - return vcard->vcard_private; -} - diff --git a/qemu/libcacard/vcard.h b/qemu/libcacard/vcard.h deleted file mode 100644 index 47dc70382..000000000 --- a/qemu/libcacard/vcard.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ -#ifndef VCARD_H -#define VCARD_H 1 - -#include "vcardt.h" - -/* - * response buffer constructors and destructors. - * - * response buffers are used when we need to return more data than will fit in - * a normal APDU response (nominally 254 bytes). - */ -VCardBufferResponse *vcard_buffer_response_new(unsigned char *buffer, int size); -void vcard_buffer_response_delete(VCardBufferResponse *buffer_response); - - -/* - * clean up state on reset - */ -void vcard_reset(VCard *card, VCardPower power); - -/* - * applet utilities - */ -/* - * Constructor for a VCardApplet - */ -VCardApplet *vcard_new_applet(VCardProcessAPDU applet_process_function, - VCardResetApplet applet_reset_function, - unsigned char *aid, int aid_len); - -/* - * destructor for a VCardApplet - * Can be called with a NULL applet - */ -void vcard_delete_applet(VCardApplet *applet); - -/* accessor - set the card type specific private data */ -void vcard_set_applet_private(VCardApplet *applet, VCardAppletPrivate *_private, - VCardAppletPrivateFree private_free); - -/* set type of vcard */ -void vcard_set_type(VCard *card, VCardType type); - -/* - * utilities interacting with the current applet - */ -/* add a new applet to a card */ -VCardStatus vcard_add_applet(VCard *card, VCardApplet *applet); -/* find the applet on the card with the given aid */ -VCardApplet *vcard_find_applet(VCard *card, unsigned char *aid, int aid_len); -/* set the following applet to be current on the given channel */ -void vcard_select_applet(VCard *card, int channel, VCardApplet *applet); -/* get the card type specific private data on the given channel */ -VCardAppletPrivate *vcard_get_current_applet_private(VCard *card, int channel); -/* fetch the applet's id */ -unsigned char *vcard_applet_get_aid(VCardApplet *applet, int *aid_len); - -/* process the apdu for the current selected applet/file */ -VCardStatus vcard_process_applet_apdu(VCard *card, VCardAPDU *apdu, - VCardResponse **response); -/* - * VCard utilities - */ -/* constructor */ -VCard *vcard_new(VCardEmul *_private, VCardEmulFree private_free); -/* get a reference */ -VCard *vcard_reference(VCard *); -/* destructor (reference counted) */ -void vcard_free(VCard *); -/* get the atr from the card */ -void vcard_get_atr(VCard *card, unsigned char *atr, int *atr_len); -void vcard_set_atr_func(VCard *card, VCardGetAtr vcard_get_atr); - -/* accessor functions for the response buffer */ -VCardBufferResponse *vcard_get_buffer_response(VCard *card); -void vcard_set_buffer_response(VCard *card, VCardBufferResponse *buffer); -/* accessor functions for the type */ -VCardType vcard_get_type(VCard *card); -/* get the private data */ -VCardEmul *vcard_get_private(VCard *card); - -#endif diff --git a/qemu/libcacard/vcard_emul.h b/qemu/libcacard/vcard_emul.h deleted file mode 100644 index f09ee98dc..000000000 --- a/qemu/libcacard/vcard_emul.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This is the actual card emulator. - * - * These functions can be implemented in different ways on different platforms - * using the underlying system primitives. For Linux it uses NSS, though direct - * to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be - * used. On Windows CAPI could be used. - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#ifndef VCARD_EMUL_H -#define VCARD_EMUL_H 1 - -#include "card_7816t.h" -#include "vcard.h" -#include "vcard_emul_type.h" - -/* - * types - */ -typedef enum { - VCARD_EMUL_OK = 0, - VCARD_EMUL_FAIL, - /* return values by vcard_emul_init */ - VCARD_EMUL_INIT_ALREADY_INITED, -} VCardEmulError; - -/* options are emul specific. call card_emul_parse_args to change a string - * To an options struct */ -typedef struct VCardEmulOptionsStruct VCardEmulOptions; - -/* - * Login functions - */ -/* return the number of login attempts still possible on the card. if unknown, - * return -1 */ -int vcard_emul_get_login_count(VCard *card); -/* login into the card, return the 7816 status word (sw2 || sw1) */ -vcard_7816_status_t vcard_emul_login(VCard *card, unsigned char *pin, - int pin_len); -void vcard_emul_logout(VCard *card); - -/* - * key functions - */ -/* delete a key */ -void vcard_emul_delete_key(VCardKey *key); -/* RSA sign/decrypt with the key, signature happens 'in place' */ -vcard_7816_status_t vcard_emul_rsa_op(VCard *card, VCardKey *key, - unsigned char *buffer, int buffer_size); - -void vcard_emul_reset(VCard *card, VCardPower power); -void vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len); - -/* Re-insert of a card that has been removed by force removal */ -VCardEmulError vcard_emul_force_card_insert(VReader *vreader); -/* Force a card removal even if the card is not physically removed */ -VCardEmulError vcard_emul_force_card_remove(VReader *vreader); - -VCardEmulOptions *vcard_emul_options(const char *args); -VCardEmulError vcard_emul_init(const VCardEmulOptions *options); -void vcard_emul_replay_insertion_events(void); -void vcard_emul_usage(void); -#endif diff --git a/qemu/libcacard/vcard_emul_nss.c b/qemu/libcacard/vcard_emul_nss.c deleted file mode 100644 index d9761eedc..000000000 --- a/qemu/libcacard/vcard_emul_nss.c +++ /dev/null @@ -1,1274 +0,0 @@ -/* - * This is the actual card emulator. - * - * These functions can be implemented in different ways on different platforms - * using the underlying system primitives. For Linux it uses NSS, though direct - * to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be - * used. On Windows CAPI could be used. - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -/* - * NSS headers - */ - -/* avoid including prototypes.h that redefines uint32 */ -#define NO_NSPR_10_SUPPORT - -#include -#include -#include -#include -#include -#include -#include - -#include "glib-compat.h" - -#include "vcard.h" -#include "card_7816t.h" -#include "vcard_emul.h" -#include "vreader.h" -#include "vevent.h" - -#include "vcardt_internal.h" - - -typedef enum { - VCardEmulUnknown = -1, - VCardEmulFalse = 0, - VCardEmulTrue = 1 -} VCardEmulTriState; - -struct VCardKeyStruct { - CERTCertificate *cert; - PK11SlotInfo *slot; - SECKEYPrivateKey *key; - VCardEmulTriState failedX509; -}; - - -typedef struct VirtualReaderOptionsStruct VirtualReaderOptions; - -struct VReaderEmulStruct { - PK11SlotInfo *slot; - VCardEmulType default_type; - char *type_params; - PRBool present; - int series; - VCard *saved_vcard; -}; - -/* - * NSS Specific options - */ -struct VirtualReaderOptionsStruct { - char *name; - char *vname; - VCardEmulType card_type; - char *type_params; - char **cert_name; - int cert_count; -}; - -struct VCardEmulOptionsStruct { - void *nss_db; - VirtualReaderOptions *vreader; - int vreader_count; - VCardEmulType hw_card_type; - const char *hw_type_params; - PRBool use_hw; -}; - -static int nss_emul_init; - -/* if we have more that just the slot, define - * VCardEmulStruct here */ - -/* - * allocate the set of arrays for certs, cert_len, key - */ -static void -vcard_emul_alloc_arrays(unsigned char ***certsp, int **cert_lenp, - VCardKey ***keysp, int cert_count) -{ - *certsp = g_new(unsigned char *, cert_count); - *cert_lenp = g_new(int, cert_count); - *keysp = g_new(VCardKey *, cert_count); -} - -/* - * Emulator specific card information - */ -typedef struct CardEmulCardStruct CardEmulPrivate; - -static VCardEmul * -vcard_emul_new_card(PK11SlotInfo *slot) -{ - PK11_ReferenceSlot(slot); - /* currently we don't need anything other than the slot */ - return (VCardEmul *)slot; -} - -static void -vcard_emul_delete_card(VCardEmul *vcard_emul) -{ - PK11SlotInfo *slot = (PK11SlotInfo *)vcard_emul; - if (slot == NULL) { - return; - } - PK11_FreeSlot(slot); -} - -static PK11SlotInfo * -vcard_emul_card_get_slot(VCard *card) -{ - /* note, the card is holding the reference, no need to get another one */ - return (PK11SlotInfo *)vcard_get_private(card); -} - - -/* - * key functions - */ -/* private constructure */ -static VCardKey * -vcard_emul_make_key(PK11SlotInfo *slot, CERTCertificate *cert) -{ - VCardKey *key; - - key = g_new(VCardKey, 1); - key->slot = PK11_ReferenceSlot(slot); - key->cert = CERT_DupCertificate(cert); - /* NOTE: if we aren't logged into the token, this could return NULL */ - /* NOTE: the cert is a temp cert, not necessarily the cert in the token, - * use the DER version of this function */ - key->key = PK11_FindKeyByDERCert(slot, cert, NULL); - key->failedX509 = VCardEmulUnknown; - return key; -} - -/* destructor */ -void -vcard_emul_delete_key(VCardKey *key) -{ - if (!nss_emul_init || (key == NULL)) { - return; - } - if (key->key) { - SECKEY_DestroyPrivateKey(key->key); - key->key = NULL; - } - if (key->cert) { - CERT_DestroyCertificate(key->cert); - } - if (key->slot) { - PK11_FreeSlot(key->slot); - } -} - -/* - * grab the nss key from a VCardKey. If it doesn't exist, try to look it up - */ -static SECKEYPrivateKey * -vcard_emul_get_nss_key(VCardKey *key) -{ - if (key->key) { - return key->key; - } - /* NOTE: if we aren't logged into the token, this could return NULL */ - key->key = PK11_FindPrivateKeyFromCert(key->slot, key->cert, NULL); - return key->key; -} - -/* - * Map NSS errors to 7816 errors - */ -static vcard_7816_status_t -vcard_emul_map_error(int error) -{ - switch (error) { - case SEC_ERROR_TOKEN_NOT_LOGGED_IN: - return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; - case SEC_ERROR_BAD_DATA: - case SEC_ERROR_OUTPUT_LEN: - case SEC_ERROR_INPUT_LEN: - case SEC_ERROR_INVALID_ARGS: - case SEC_ERROR_INVALID_ALGORITHM: - case SEC_ERROR_NO_KEY: - case SEC_ERROR_INVALID_KEY: - case SEC_ERROR_DECRYPTION_DISALLOWED: - return VCARD7816_STATUS_ERROR_DATA_INVALID; - case SEC_ERROR_NO_MEMORY: - return VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE; - } - return VCARD7816_STATUS_EXC_ERROR_CHANGE; -} - -/* RSA sign/decrypt with the key, signature happens 'in place' */ -vcard_7816_status_t -vcard_emul_rsa_op(VCard *card, VCardKey *key, - unsigned char *buffer, int buffer_size) -{ - SECKEYPrivateKey *priv_key; - unsigned signature_len; - PK11SlotInfo *slot; - SECStatus rv; - unsigned char buf[2048]; - unsigned char *bp = NULL; - int pad_len; - vcard_7816_status_t ret = VCARD7816_STATUS_SUCCESS; - - if ((!nss_emul_init) || (key == NULL)) { - /* couldn't get the key, indicate that we aren't logged in */ - return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; - } - priv_key = vcard_emul_get_nss_key(key); - if (priv_key == NULL) { - /* couldn't get the key, indicate that we aren't logged in */ - return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; - } - slot = vcard_emul_card_get_slot(card); - - /* - * this is only true of the rsa signature - */ - signature_len = PK11_SignatureLen(priv_key); - if (buffer_size != signature_len) { - return VCARD7816_STATUS_ERROR_DATA_INVALID; - } - /* be able to handle larger keys if necessariy */ - bp = &buf[0]; - if (sizeof(buf) < signature_len) { - bp = g_malloc(signature_len); - } - - /* - * do the raw operations. Some tokens claim to do CKM_RSA_X_509, but then - * choke when they try to do the actual operations. Try to detect - * those cases and treat them as if the token didn't claim support for - * X_509. - */ - if (key->failedX509 != VCardEmulTrue - && PK11_DoesMechanism(slot, CKM_RSA_X_509)) { - rv = PK11_PrivDecryptRaw(priv_key, bp, &signature_len, signature_len, - buffer, buffer_size); - if (rv == SECSuccess) { - assert(buffer_size == signature_len); - memcpy(buffer, bp, signature_len); - key->failedX509 = VCardEmulFalse; - goto cleanup; - } - /* - * we've had a successful X509 operation, this failure must be - * somethine else - */ - if (key->failedX509 == VCardEmulFalse) { - ret = vcard_emul_map_error(PORT_GetError()); - goto cleanup; - } - /* - * key->failedX509 must be Unknown at this point, try the - * non-x_509 case - */ - } - /* token does not support CKM_RSA_X509, emulate that with CKM_RSA_PKCS */ - /* is this a PKCS #1 formatted signature? */ - if ((buffer[0] == 0) && (buffer[1] == 1)) { - int i; - - for (i = 2; i < buffer_size; i++) { - /* rsa signature pad */ - if (buffer[i] != 0xff) { - break; - } - } - if ((i < buffer_size) && (buffer[i] == 0)) { - /* yes, we have a properly formatted PKCS #1 signature */ - /* - * NOTE: even if we accidentally got an encrypt buffer, which - * through sheer luck started with 00, 01, ff, 00, it won't matter - * because the resulting Sign operation will effectively decrypt - * the real buffer. - */ - SECItem signature; - SECItem hash; - - i++; - hash.data = &buffer[i]; - hash.len = buffer_size - i; - signature.data = bp; - signature.len = signature_len; - rv = PK11_Sign(priv_key, &signature, &hash); - if (rv != SECSuccess) { - ret = vcard_emul_map_error(PORT_GetError()); - goto cleanup; - } - assert(buffer_size == signature.len); - memcpy(buffer, bp, signature.len); - /* - * we got here because either the X509 attempt failed, or the - * token couldn't do the X509 operation, in either case stay - * with the PKCS version for future operations on this key - */ - key->failedX509 = VCardEmulTrue; - goto cleanup; - } - } - pad_len = buffer_size - signature_len; - assert(pad_len < 4); - /* - * OK now we've decrypted the payload, package it up in PKCS #1 for the - * upper layer. - */ - buffer[0] = 0; - buffer[1] = 2; /* RSA_encrypt */ - pad_len -= 3; /* format is 0 || 2 || pad || 0 || data */ - /* - * padding for PKCS #1 encrypted data is a string of random bytes. The - * random butes protect against potential decryption attacks against RSA. - * Since PrivDecrypt has already stripped those bytes, we can't reconstruct - * them. This shouldn't matter to the upper level code which should just - * strip this code out anyway, so We'll pad with a constant 3. - */ - memset(&buffer[2], 0x03, pad_len); - pad_len += 2; /* index to the end of the pad */ - buffer[pad_len] = 0; - pad_len++; /* index to the start of the data */ - memcpy(&buffer[pad_len], bp, signature_len); - /* - * we got here because either the X509 attempt failed, or the - * token couldn't do the X509 operation, in either case stay - * with the PKCS version for future operations on this key - */ - key->failedX509 = VCardEmulTrue; -cleanup: - if (bp != buf) { - g_free(bp); - } - return ret; -} - -/* - * Login functions - */ -/* return the number of login attempts still possible on the card. if unknown, - * return -1 */ -int -vcard_emul_get_login_count(VCard *card) -{ - return -1; -} - -/* login into the card, return the 7816 status word (sw2 || sw1) */ -vcard_7816_status_t -vcard_emul_login(VCard *card, unsigned char *pin, int pin_len) -{ - PK11SlotInfo *slot; - unsigned char *pin_string; - int i; - SECStatus rv; - - if (!nss_emul_init) { - return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; - } - slot = vcard_emul_card_get_slot(card); - /* We depend on the PKCS #11 module internal login state here because we - * create a separate process to handle each guest instance. If we needed - * to handle multiple guests from one process, then we would need to keep - * a lot of extra state in our card structure - * */ - pin_string = g_malloc(pin_len+1); - memcpy(pin_string, pin, pin_len); - pin_string[pin_len] = 0; - - /* handle CAC expanded pins correctly */ - for (i = pin_len-1; i >= 0 && (pin_string[i] == 0xff); i--) { - pin_string[i] = 0; - } - - rv = PK11_Authenticate(slot, PR_FALSE, pin_string); - memset(pin_string, 0, pin_len); /* don't let the pin hang around in memory - to be snooped */ - g_free(pin_string); - if (rv == SECSuccess) { - return VCARD7816_STATUS_SUCCESS; - } - /* map the error from port get error */ - return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; -} - -void -vcard_emul_logout(VCard *card) -{ - PK11SlotInfo *slot; - - if (!nss_emul_init) { - return; - } - - slot = vcard_emul_card_get_slot(card); - if (PK11_IsLoggedIn(slot, NULL)) { - PK11_Logout(slot); /* NOTE: ignoring SECStatus return value */ - } -} - -void -vcard_emul_reset(VCard *card, VCardPower power) -{ - /* - * if we reset the card (either power on or power off), we lose our login - * state - */ - vcard_emul_logout(card); - - /* TODO: we may also need to send insertion/removal events? */ -} - -static VReader * -vcard_emul_find_vreader_from_slot(PK11SlotInfo *slot) -{ - VReaderList *reader_list = vreader_get_reader_list(); - VReaderListEntry *current_entry; - - if (reader_list == NULL) { - return NULL; - } - for (current_entry = vreader_list_get_first(reader_list); current_entry; - current_entry = vreader_list_get_next(current_entry)) { - VReader *reader = vreader_list_get_reader(current_entry); - VReaderEmul *reader_emul = vreader_get_private(reader); - if (reader_emul->slot == slot) { - vreader_list_delete(reader_list); - return reader; - } - vreader_free(reader); - } - - vreader_list_delete(reader_list); - return NULL; -} - -/* - * create a new reader emul - */ -static VReaderEmul * -vreader_emul_new(PK11SlotInfo *slot, VCardEmulType type, const char *params) -{ - VReaderEmul *new_reader_emul; - - new_reader_emul = g_new(VReaderEmul, 1); - - new_reader_emul->slot = PK11_ReferenceSlot(slot); - new_reader_emul->default_type = type; - new_reader_emul->type_params = g_strdup(params); - new_reader_emul->present = PR_FALSE; - new_reader_emul->series = 0; - new_reader_emul->saved_vcard = NULL; - return new_reader_emul; -} - -static void -vreader_emul_delete(VReaderEmul *vreader_emul) -{ - if (vreader_emul == NULL) { - return; - } - if (vreader_emul->slot) { - PK11_FreeSlot(vreader_emul->slot); - } - g_free(vreader_emul->type_params); - g_free(vreader_emul); -} - -/* - * TODO: move this to emulater non-specific file - */ -static VCardEmulType -vcard_emul_get_type(VReader *vreader) -{ - VReaderEmul *vreader_emul; - - vreader_emul = vreader_get_private(vreader); - if (vreader_emul && vreader_emul->default_type != VCARD_EMUL_NONE) { - return vreader_emul->default_type; - } - - return vcard_emul_type_select(vreader); -} -/* - * TODO: move this to emulater non-specific file - */ -static const char * -vcard_emul_get_type_params(VReader *vreader) -{ - VReaderEmul *vreader_emul; - - vreader_emul = vreader_get_private(vreader); - if (vreader_emul && vreader_emul->type_params) { - return vreader_emul->type_params; - } - - return ""; -} - -/* pull the slot out of the reader private data */ -static PK11SlotInfo * -vcard_emul_reader_get_slot(VReader *vreader) -{ - VReaderEmul *vreader_emul = vreader_get_private(vreader); - if (vreader_emul == NULL) { - return NULL; - } - return vreader_emul->slot; -} - -/* - * Card ATR's map to physical cards. vcard_alloc_atr will set appropriate - * historical bytes for any software emulated card. The remaining bytes can be - * used to indicate the actual emulator - */ -static unsigned char *nss_atr; -static int nss_atr_len; - -void -vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len) -{ - int len; - assert(atr != NULL); - - if (nss_atr == NULL) { - nss_atr = vcard_alloc_atr("NSS", &nss_atr_len); - } - len = MIN(nss_atr_len, *atr_len); - memcpy(atr, nss_atr, len); - *atr_len = len; -} - -/* - * create a new card from certs and keys - */ -static VCard * -vcard_emul_make_card(VReader *reader, - unsigned char * const *certs, int *cert_len, - VCardKey *keys[], int cert_count) -{ - VCardEmul *vcard_emul; - VCard *vcard; - PK11SlotInfo *slot; - VCardEmulType type; - const char *params; - - type = vcard_emul_get_type(reader); - - /* ignore the inserted card */ - if (type == VCARD_EMUL_NONE) { - return NULL; - } - slot = vcard_emul_reader_get_slot(reader); - if (slot == NULL) { - return NULL; - } - - params = vcard_emul_get_type_params(reader); - /* params these can be NULL */ - - vcard_emul = vcard_emul_new_card(slot); - if (vcard_emul == NULL) { - return NULL; - } - vcard = vcard_new(vcard_emul, vcard_emul_delete_card); - if (vcard == NULL) { - vcard_emul_delete_card(vcard_emul); - return NULL; - } - vcard_init(reader, vcard, type, params, certs, cert_len, keys, cert_count); - return vcard; -} - - -/* - * 'clone' a physical card as a virtual card - */ -static VCard * -vcard_emul_mirror_card(VReader *vreader) -{ - /* - * lookup certs using the C_FindObjects. The Stan Cert handle won't give - * us the real certs until we log in. - */ - PK11GenericObject *firstObj, *thisObj; - int cert_count; - unsigned char **certs; - int *cert_len; - VCardKey **keys; - PK11SlotInfo *slot; - VCard *card; - - slot = vcard_emul_reader_get_slot(vreader); - if (slot == NULL) { - return NULL; - } - - firstObj = PK11_FindGenericObjects(slot, CKO_CERTIFICATE); - if (firstObj == NULL) { - return NULL; - } - - /* count the certs */ - cert_count = 0; - for (thisObj = firstObj; thisObj; - thisObj = PK11_GetNextGenericObject(thisObj)) { - cert_count++; - } - - /* allocate the arrays */ - vcard_emul_alloc_arrays(&certs, &cert_len, &keys, cert_count); - - /* fill in the arrays */ - cert_count = 0; - for (thisObj = firstObj; thisObj; - thisObj = PK11_GetNextGenericObject(thisObj)) { - SECItem derCert; - CERTCertificate *cert; - SECStatus rv; - - rv = PK11_ReadRawAttribute(PK11_TypeGeneric, thisObj, - CKA_VALUE, &derCert); - if (rv != SECSuccess) { - continue; - } - /* create floating temp cert. This gives us a cert structure even if - * the token isn't logged in */ - cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert, - NULL, PR_FALSE, PR_TRUE); - SECITEM_FreeItem(&derCert, PR_FALSE); - if (cert == NULL) { - continue; - } - - certs[cert_count] = cert->derCert.data; - cert_len[cert_count] = cert->derCert.len; - keys[cert_count] = vcard_emul_make_key(slot, cert); - cert_count++; - CERT_DestroyCertificate(cert); /* key obj still has a reference */ - } - - /* now create the card */ - card = vcard_emul_make_card(vreader, certs, cert_len, keys, cert_count); - g_free(certs); - g_free(cert_len); - g_free(keys); - - return card; -} - -static VCardEmulType default_card_type = VCARD_EMUL_NONE; -static const char *default_type_params = ""; - -/* - * This thread looks for card and reader insertions and puts events on the - * event queue - */ -static void -vcard_emul_event_thread(void *arg) -{ - PK11SlotInfo *slot; - VReader *vreader; - VReaderEmul *vreader_emul; - VCard *vcard; - SECMODModule *module = (SECMODModule *)arg; - - do { - /* - * XXX - the latency value doesn't matter one bit. you only get no - * blocking (flags |= CKF_DONT_BLOCK) or PKCS11_WAIT_LATENCY (==500), - * hard coded in coolkey. And it isn't coolkey's fault - the timeout - * value we pass get's dropped on the floor before C_WaitForSlotEvent - * is called. - */ - slot = SECMOD_WaitForAnyTokenEvent(module, 0, 500); - if (slot == NULL) { - /* this could be just a no event indication */ - if (PORT_GetError() == SEC_ERROR_NO_EVENT) { - continue; - } - break; - } - vreader = vcard_emul_find_vreader_from_slot(slot); - if (vreader == NULL) { - /* new vreader */ - vreader_emul = vreader_emul_new(slot, default_card_type, - default_type_params); - vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul, - vreader_emul_delete); - PK11_FreeSlot(slot); - slot = NULL; - vreader_add_reader(vreader); - vreader_free(vreader); - continue; - } - /* card remove/insert */ - vreader_emul = vreader_get_private(vreader); - if (PK11_IsPresent(slot)) { - int series = PK11_GetSlotSeries(slot); - if (series != vreader_emul->series) { - if (vreader_emul->present) { - vreader_insert_card(vreader, NULL); - } - vcard = vcard_emul_mirror_card(vreader); - vreader_insert_card(vreader, vcard); - vcard_free(vcard); - } - vreader_emul->series = series; - vreader_emul->present = 1; - vreader_free(vreader); - PK11_FreeSlot(slot); - continue; - } - if (vreader_emul->present) { - vreader_insert_card(vreader, NULL); - } - vreader_emul->series = 0; - vreader_emul->present = 0; - PK11_FreeSlot(slot); - vreader_free(vreader); - } while (1); -} - -/* if the card is inserted when we start up, make sure our state is correct */ -static void -vcard_emul_init_series(VReader *vreader, VCard *vcard) -{ - VReaderEmul *vreader_emul = vreader_get_private(vreader); - PK11SlotInfo *slot = vreader_emul->slot; - - vreader_emul->present = PK11_IsPresent(slot); - vreader_emul->series = PK11_GetSlotSeries(slot); - if (vreader_emul->present == 0) { - vreader_insert_card(vreader, NULL); - } -} - -/* - * each module has a separate wait call, create a thread for each module that - * we are using. - */ -static void -vcard_emul_new_event_thread(SECMODModule *module) -{ - PR_CreateThread(PR_SYSTEM_THREAD, vcard_emul_event_thread, - module, PR_PRIORITY_HIGH, PR_GLOBAL_THREAD, - PR_UNJOINABLE_THREAD, 0); -} - -static const VCardEmulOptions default_options = { - .nss_db = NULL, - .vreader = NULL, - .vreader_count = 0, - .hw_card_type = VCARD_EMUL_CAC, - .hw_type_params = "", - .use_hw = PR_TRUE -}; - - -/* - * NSS needs the app to supply a password prompt. In our case the only time - * the password is supplied is as part of the Login APDU. The actual password - * is passed in the pw_arg in that case. In all other cases pw_arg should be - * NULL. - */ -static char * -vcard_emul_get_password(PK11SlotInfo *slot, PRBool retries, void *pw_arg) -{ - /* if it didn't work the first time, don't keep trying */ - if (retries) { - return NULL; - } - /* we are looking up a password when we don't have one in hand */ - if (pw_arg == NULL) { - return NULL; - } - /* TODO: we really should verify that were are using the right slot */ - return PORT_Strdup(pw_arg); -} - -/* Force a card removal even if the card is not physically removed */ -VCardEmulError -vcard_emul_force_card_remove(VReader *vreader) -{ - if (!nss_emul_init || (vreader_card_is_present(vreader) != VREADER_OK)) { - return VCARD_EMUL_FAIL; /* card is already removed */ - } - - /* OK, remove it */ - vreader_insert_card(vreader, NULL); - return VCARD_EMUL_OK; -} - -/* Re-insert of a card that has been removed by force removal */ -VCardEmulError -vcard_emul_force_card_insert(VReader *vreader) -{ - VReaderEmul *vreader_emul; - VCard *vcard; - - if (!nss_emul_init || (vreader_card_is_present(vreader) == VREADER_OK)) { - return VCARD_EMUL_FAIL; /* card is already removed */ - } - vreader_emul = vreader_get_private(vreader); - - /* if it's a softcard, get the saved vcard from the reader emul structure */ - if (vreader_emul->saved_vcard) { - vcard = vcard_reference(vreader_emul->saved_vcard); - } else { - /* it must be a physical card, rebuild it */ - if (!PK11_IsPresent(vreader_emul->slot)) { - /* physical card has been removed, not way to reinsert it */ - return VCARD_EMUL_FAIL; - } - vcard = vcard_emul_mirror_card(vreader); - } - vreader_insert_card(vreader, vcard); - vcard_free(vcard); - - return VCARD_EMUL_OK; -} - - -static PRBool -module_has_removable_hw_slots(SECMODModule *mod) -{ - int i; - PRBool ret = PR_FALSE; - SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); - - if (!moduleLock) { - PORT_SetError(SEC_ERROR_NOT_INITIALIZED); - return ret; - } - SECMOD_GetReadLock(moduleLock); - for (i = 0; i < mod->slotCount; i++) { - PK11SlotInfo *slot = mod->slots[i]; - if (PK11_IsRemovable(slot) && PK11_IsHW(slot)) { - ret = PR_TRUE; - break; - } - } - SECMOD_ReleaseReadLock(moduleLock); - return ret; -} - -/* Previously we returned FAIL if no readers found. This makes - * no sense when using hardware, since there may be no readers connected - * at the time vcard_emul_init is called, but they will be properly - * recognized later. So Instead return FAIL only if no_hw==1 and no - * vcards can be created (indicates error with certificates provided - * or db), or if any other higher level error (NSS error, missing coolkey). */ -static int vcard_emul_init_called; - -VCardEmulError -vcard_emul_init(const VCardEmulOptions *options) -{ - SECStatus rv; - PRBool has_readers = PR_FALSE; - VReader *vreader; - VReaderEmul *vreader_emul; - SECMODListLock *module_lock; - SECMODModuleList *module_list; - SECMODModuleList *mlp; - int i; - - if (vcard_emul_init_called) { - return VCARD_EMUL_INIT_ALREADY_INITED; - } - vcard_emul_init_called = 1; - vreader_init(); - vevent_queue_init(); - - if (options == NULL) { - options = &default_options; - } - - /* first initialize NSS */ - if (options->nss_db) { - rv = NSS_Init(options->nss_db); - } else { - gchar *path; -#ifndef _WIN32 - path = g_strdup("/etc/pki/nssdb"); -#else - if (g_get_system_config_dirs() == NULL || - g_get_system_config_dirs()[0] == NULL) { - return VCARD_EMUL_FAIL; - } - - path = g_build_filename( - g_get_system_config_dirs()[0], "pki", "nssdb", NULL); -#endif - - rv = NSS_Init(path); - g_free(path); - } - if (rv != SECSuccess) { - return VCARD_EMUL_FAIL; - } - /* Set password callback function */ - PK11_SetPasswordFunc(vcard_emul_get_password); - - /* set up soft cards emulated by software certs rather than physical cards - * */ - for (i = 0; i < options->vreader_count; i++) { - int j; - int cert_count; - unsigned char **certs; - int *cert_len; - VCardKey **keys; - PK11SlotInfo *slot; - - slot = PK11_FindSlotByName(options->vreader[i].name); - if (slot == NULL) { - continue; - } - vreader_emul = vreader_emul_new(slot, options->vreader[i].card_type, - options->vreader[i].type_params); - vreader = vreader_new(options->vreader[i].vname, vreader_emul, - vreader_emul_delete); - vreader_add_reader(vreader); - - vcard_emul_alloc_arrays(&certs, &cert_len, &keys, - options->vreader[i].cert_count); - - cert_count = 0; - for (j = 0; j < options->vreader[i].cert_count; j++) { - /* we should have a better way of identifying certs than by - * nickname here */ - CERTCertificate *cert = PK11_FindCertFromNickname( - options->vreader[i].cert_name[j], - NULL); - if (cert == NULL) { - continue; - } - certs[cert_count] = cert->derCert.data; - cert_len[cert_count] = cert->derCert.len; - keys[cert_count] = vcard_emul_make_key(slot, cert); - /* this is safe because the key is still holding a cert reference */ - CERT_DestroyCertificate(cert); - cert_count++; - } - if (cert_count) { - VCard *vcard = vcard_emul_make_card(vreader, certs, cert_len, - keys, cert_count); - vreader_insert_card(vreader, vcard); - vcard_emul_init_series(vreader, vcard); - /* allow insertion and removal of soft cards */ - vreader_emul->saved_vcard = vcard_reference(vcard); - vcard_free(vcard); - vreader_free(vreader); - has_readers = PR_TRUE; - } - g_free(certs); - g_free(cert_len); - g_free(keys); - } - - /* if we aren't suppose to use hw, skip looking up hardware tokens */ - if (!options->use_hw) { - nss_emul_init = has_readers; - return has_readers ? VCARD_EMUL_OK : VCARD_EMUL_FAIL; - } - - /* make sure we have some PKCS #11 module loaded */ - module_lock = SECMOD_GetDefaultModuleListLock(); - module_list = SECMOD_GetDefaultModuleList(); - SECMOD_GetReadLock(module_lock); - for (mlp = module_list; mlp; mlp = mlp->next) { - SECMODModule *module = mlp->module; - if (module_has_removable_hw_slots(module)) { - break; - } - } - SECMOD_ReleaseReadLock(module_lock); - - /* now examine all the slots, finding which should be readers */ - /* We should control this with options. For now we mirror out any - * removable hardware slot */ - default_card_type = options->hw_card_type; - default_type_params = g_strdup(options->hw_type_params); - - SECMOD_GetReadLock(module_lock); - for (mlp = module_list; mlp; mlp = mlp->next) { - SECMODModule *module = mlp->module; - - /* Ignore the internal module */ - if (module == NULL || module == SECMOD_GetInternalModule()) { - continue; - } - - for (i = 0; i < module->slotCount; i++) { - PK11SlotInfo *slot = module->slots[i]; - - /* only map removable HW slots */ - if (slot == NULL || !PK11_IsRemovable(slot) || !PK11_IsHW(slot)) { - continue; - } - if (strcmp("E-Gate 0 0", PK11_GetSlotName(slot)) == 0) { - /* - * coolkey <= 1.1.0-20 emulates this reader if it can't find - * any hardware readers. This causes problems, warn user of - * problems. - */ - fprintf(stderr, "known bad coolkey version - see " - "https://bugzilla.redhat.com/show_bug.cgi?id=802435\n"); - continue; - } - vreader_emul = vreader_emul_new(slot, options->hw_card_type, - options->hw_type_params); - vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul, - vreader_emul_delete); - vreader_add_reader(vreader); - - if (PK11_IsPresent(slot)) { - VCard *vcard; - vcard = vcard_emul_mirror_card(vreader); - vreader_insert_card(vreader, vcard); - vcard_emul_init_series(vreader, vcard); - vcard_free(vcard); - } - } - vcard_emul_new_event_thread(module); - } - SECMOD_ReleaseReadLock(module_lock); - nss_emul_init = PR_TRUE; - - return VCARD_EMUL_OK; -} - -/* Recreate card insert events for all readers (user should - * deduce implied reader insert. perhaps do a reader insert as well?) - */ -void -vcard_emul_replay_insertion_events(void) -{ - VReaderListEntry *current_entry; - VReaderListEntry *next_entry; - VReaderList *list = vreader_get_reader_list(); - - for (current_entry = vreader_list_get_first(list); current_entry; - current_entry = next_entry) { - VReader *vreader = vreader_list_get_reader(current_entry); - next_entry = vreader_list_get_next(current_entry); - vreader_queue_card_event(vreader); - } - - vreader_list_delete(list); -} - -/* - * Silly little functions to help parsing our argument string - */ -static int -count_tokens(const char *str, char token, char token_end) -{ - int count = 0; - - for (; *str; str++) { - if (*str == token) { - count++; - } - if (*str == token_end) { - break; - } - } - return count; -} - -static const char * -strip(const char *str) -{ - for (; *str && isspace(*str); str++) { - } - return str; -} - -static const char * -find_blank(const char *str) -{ - for (; *str && !isspace(*str); str++) { - } - return str; -} - - -/* - * We really want to use some existing argument parsing library here. That - * would give us a consistent look */ -static VCardEmulOptions options; -#define READER_STEP 4 - -/* Expects "args" to be at the beginning of a token (ie right after the ',' - * ending the previous token), and puts the next token start in "token", - * and its length in "token_length". "token" will not be nul-terminated. - * After calling the macro, "args" will be advanced to the beginning of - * the next token. - * This macro may call continue or break. - */ -#define NEXT_TOKEN(token) \ - (token) = args; \ - args = strpbrk(args, ",)"); \ - if (*args == 0) { \ - break; \ - } \ - if (*args == ')') { \ - args++; \ - continue; \ - } \ - (token##_length) = args - (token); \ - args = strip(args+1); - -VCardEmulOptions * -vcard_emul_options(const char *args) -{ - int reader_count = 0; - VCardEmulOptions *opts; - - /* Allow the future use of allocating the options structure on the fly */ - memcpy(&options, &default_options, sizeof(options)); - opts = &options; - - do { - args = strip(args); /* strip off the leading spaces */ - if (*args == ',') { - continue; - } - /* soft=(slot_name,virt_name,emul_type,emul_flags,cert_1, (no eol) - * cert_2,cert_3...) */ - if (strncmp(args, "soft=", 5) == 0) { - const char *name; - size_t name_length; - const char *vname; - size_t vname_length; - const char *type_params; - size_t type_params_length; - char type_str[100]; - VCardEmulType type; - int count, i; - VirtualReaderOptions *vreaderOpt; - - args = strip(args + 5); - if (*args != '(') { - continue; - } - args = strip(args+1); - - NEXT_TOKEN(name) - NEXT_TOKEN(vname) - NEXT_TOKEN(type_params) - type_params_length = MIN(type_params_length, sizeof(type_str)-1); - memcpy(type_str, type_params, type_params_length); - type_str[type_params_length] = '\0'; - type = vcard_emul_type_from_string(type_str); - - NEXT_TOKEN(type_params) - - if (*args == 0) { - break; - } - - if (opts->vreader_count >= reader_count) { - reader_count += READER_STEP; - opts->vreader = g_renew(VirtualReaderOptions, opts->vreader, - reader_count); - } - vreaderOpt = &opts->vreader[opts->vreader_count]; - vreaderOpt->name = g_strndup(name, name_length); - vreaderOpt->vname = g_strndup(vname, vname_length); - vreaderOpt->card_type = type; - vreaderOpt->type_params = - g_strndup(type_params, type_params_length); - count = count_tokens(args, ',', ')') + 1; - vreaderOpt->cert_count = count; - vreaderOpt->cert_name = g_new(char *, count); - for (i = 0; i < count; i++) { - const char *cert = args; - args = strpbrk(args, ",)"); - vreaderOpt->cert_name[i] = g_strndup(cert, args - cert); - args = strip(args+1); - } - if (*args == ')') { - args++; - } - opts->vreader_count++; - /* use_hw= */ - } else if (strncmp(args, "use_hw=", 7) == 0) { - args = strip(args+7); - if (*args == '0' || *args == 'N' || *args == 'n' || *args == 'F') { - opts->use_hw = PR_FALSE; - } else { - opts->use_hw = PR_TRUE; - } - args = find_blank(args); - /* hw_type= */ - } else if (strncmp(args, "hw_type=", 8) == 0) { - args = strip(args+8); - opts->hw_card_type = vcard_emul_type_from_string(args); - args = find_blank(args); - /* hw_params= */ - } else if (strncmp(args, "hw_params=", 10) == 0) { - const char *params; - args = strip(args+10); - params = args; - args = find_blank(args); - opts->hw_type_params = g_strndup(params, args-params); - /* db="/data/base/path" */ - } else if (strncmp(args, "db=", 3) == 0) { - const char *db; - args = strip(args+3); - if (*args != '"') { - continue; - } - args++; - db = args; - args = strpbrk(args, "\"\n"); - opts->nss_db = g_strndup(db, args-db); - if (*args != 0) { - args++; - } - } else { - args = find_blank(args); - } - } while (*args != 0); - - return opts; -} - -void -vcard_emul_usage(void) -{ - fprintf(stderr, -"emul args: comma separated list of the following arguments\n" -" db={nss_database} (default sql:/etc/pki/nssdb)\n" -" use_hw=[yes|no] (default yes)\n" -" hw_type={card_type_to_emulate} (default CAC)\n" -" hw_param={param_for_card} (default \"\")\n" -" soft=({slot_name},{vreader_name},{card_type_to_emulate},{params_for_card},\n" -" {cert1},{cert2},{cert3} (default none)\n" -"\n" -" {nss_database} The location of the NSS cert & key database\n" -" {card_type_to_emulate} What card interface to present to the guest\n" -" {param_for_card} Card interface specific parameters\n" -" {slot_name} NSS slot that contains the certs\n" -" {vreader_name} Virtual reader name to present to the guest\n" -" {certN} Nickname of the certificate n on the virtual card\n" -"\n" -"These parameters come as a single string separated by blanks or newlines." -"\n" -"Unless use_hw is set to no, all tokens that look like removable hardware\n" -"tokens will be presented to the guest using the emulator specified by\n" -"hw_type, and parameters of hw_param.\n" -"\n" -"If more one or more soft= parameters are specified, these readers will be\n" -"presented to the guest\n"); -} diff --git a/qemu/libcacard/vcard_emul_type.c b/qemu/libcacard/vcard_emul_type.c deleted file mode 100644 index 59a145820..000000000 --- a/qemu/libcacard/vcard_emul_type.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This file contains utility functions which abstract the different card - * types. The goal is that new card types can easily be added by simply - * changing this file and vcard_emul_type.h. It is currently not a requirement - * to dynamically add new card types. - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include -#include "vcardt.h" -#include "vcard_emul_type.h" -#include "cac.h" - -VCardStatus vcard_init(VReader *vreader, VCard *vcard, - VCardEmulType type, const char *params, - unsigned char *const *cert, int cert_len[], - VCardKey *key[], int cert_count) -{ - switch (type) { - case VCARD_EMUL_NONE: - break; - case VCARD_EMUL_CAC: - return cac_card_init(vreader, vcard, params, - cert, cert_len, key, cert_count); - /* add new ones here */ - default: - break; - } - return VCARD_FAIL; -} - -VCardEmulType vcard_emul_type_select(VReader *vreader) -{ -#ifdef notdef - /* since there is only one emulator no need to call this function */ - if (cac_is_cac_card(vreader) == VCARD_DONE) { - return VCARD_EMUL_CAC; - } -#endif - /* return the default */ - return VCARD_EMUL_CAC; -} - -VCardEmulType vcard_emul_type_from_string(const char *type_string) -{ - if (strcasecmp(type_string, "CAC") == 0) { - return VCARD_EMUL_CAC; - } -#ifdef USE_PASSTHRU - if (strcasecmp(type_string, "PASSTHRU") == 0) { - return VCARD_EMUL_PASSTHRU; - } -#endif - return VCARD_EMUL_NONE; -} diff --git a/qemu/libcacard/vcard_emul_type.h b/qemu/libcacard/vcard_emul_type.h deleted file mode 100644 index 0242f40eb..000000000 --- a/qemu/libcacard/vcard_emul_type.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This header file abstracts the different card types. The goal is new card - * types can easily be added by simply changing this file and - * vcard_emul_type.c. It is currently not a requirement to dynamically add new - * card types. - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#ifndef VCARD_EMUL_TYPE_H -#define VCARD_EMUL_TYPE_H 1 -#include "vcardt.h" -#include "vreadert.h" - -/* - * types - */ -typedef enum { - VCARD_EMUL_NONE = 0, - VCARD_EMUL_CAC, - VCARD_EMUL_PASSTHRU -} VCardEmulType; - -/* functions used by the rest of the emulator */ -VCardStatus vcard_init(VReader *vreader, VCard *vcard, VCardEmulType type, - const char *params, unsigned char * const *cert, - int cert_len[], VCardKey *key[], int cert_count); -VCardEmulType vcard_emul_type_select(VReader *vreader); -VCardEmulType vcard_emul_type_from_string(const char *type_string); - -#endif diff --git a/qemu/libcacard/vcardt.c b/qemu/libcacard/vcardt.c deleted file mode 100644 index c67de2f3c..000000000 --- a/qemu/libcacard/vcardt.c +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include -#include - -#include "vcardt.h" - -#include "vcardt_internal.h" - -/* create an ATR with appropriate historical bytes */ -#define ATR_TS_DIRECT_CONVENTION 0x3b -#define ATR_TA_PRESENT 0x10 -#define ATR_TB_PRESENT 0x20 -#define ATR_TC_PRESENT 0x40 -#define ATR_TD_PRESENT 0x80 - -unsigned char *vcard_alloc_atr(const char *postfix, int *atr_len) -{ - int postfix_len; - const char prefix[] = "VCARD_"; - const char default_postfix[] = "DEFAULT"; - const int prefix_len = sizeof(prefix) - 1; - int total_len; - unsigned char *atr; - - if (postfix == NULL) { - postfix = default_postfix; - } - postfix_len = strlen(postfix); - total_len = 3 + prefix_len + postfix_len; - atr = g_malloc(total_len); - atr[0] = ATR_TS_DIRECT_CONVENTION; - atr[1] = ATR_TD_PRESENT + prefix_len + postfix_len; - atr[2] = 0x00; - memcpy(&atr[3], prefix, prefix_len); - memcpy(&atr[3 + prefix_len], postfix, postfix_len); - if (atr_len) { - *atr_len = total_len; - } - return atr; -} diff --git a/qemu/libcacard/vcardt.h b/qemu/libcacard/vcardt.h deleted file mode 100644 index 795e26530..000000000 --- a/qemu/libcacard/vcardt.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ -#ifndef VCARDT_H -#define VCARDT_H 1 - -/* - * these should come from some common spice header file - */ -#include -#ifndef MIN -#define MIN(x, y) ((x) > (y) ? (y) : (x)) -#define MAX(x, y) ((x) > (y) ? (x) : (y)) -#endif - -typedef struct VCardStruct VCard; -typedef struct VCardAPDUStruct VCardAPDU; -typedef struct VCardResponseStruct VCardResponse; -typedef struct VCardBufferResponseStruct VCardBufferResponse; -typedef struct VCardAppletStruct VCardApplet; -typedef struct VCardAppletPrivateStruct VCardAppletPrivate; -typedef struct VCardKeyStruct VCardKey; /* opaque */ -typedef struct VCardEmulStruct VCardEmul; - -#define MAX_CHANNEL 4 - -typedef enum { - VCARD_DONE, - VCARD_NEXT, - VCARD_FAIL -} VCardStatus; - -typedef enum { - VCARD_FILE_SYSTEM, - VCARD_VM, - VCARD_DIRECT -} VCardType; - -typedef enum { - VCARD_POWER_ON, - VCARD_POWER_OFF -} VCardPower; - -typedef VCardStatus (*VCardProcessAPDU)(VCard *card, VCardAPDU *apdu, - VCardResponse **response); -typedef VCardStatus (*VCardResetApplet)(VCard *card, int channel); -typedef void (*VCardAppletPrivateFree) (VCardAppletPrivate *); -typedef void (*VCardEmulFree) (VCardEmul *); -typedef void (*VCardGetAtr) (VCard *, unsigned char *atr, int *atr_len); - -struct VCardBufferResponseStruct { - unsigned char *buffer; - int buffer_len; - unsigned char *current; - int len; -}; - -#endif diff --git a/qemu/libcacard/vcardt_internal.h b/qemu/libcacard/vcardt_internal.h deleted file mode 100644 index e5c8d2dd3..000000000 --- a/qemu/libcacard/vcardt_internal.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef VCARDT_INTERNAL_H -#define VCARDT_INTERNAL_H - -unsigned char *vcard_alloc_atr(const char *postfix, int *atr_len); - -#endif diff --git a/qemu/libcacard/vevent.h b/qemu/libcacard/vevent.h deleted file mode 100644 index 38c3482c3..000000000 --- a/qemu/libcacard/vevent.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ -#ifndef EVENT_H -#define EVENT_H 1 -#include "eventt.h" -#include "vreadert.h" -#include "vcardt.h" - -VEvent *vevent_new(VEventType type, VReader *reader, VCard *card); -void vevent_delete(VEvent *); - -/* - * VEvent queueing services - */ -void vevent_queue_vevent(VEvent *); -void vevent_queue_init(void); - -/* - * VEvent dequeing services - */ -VEvent *vevent_wait_next_vevent(void); -VEvent *vevent_get_next_vevent(void); - - -#endif diff --git a/qemu/libcacard/vreader.c b/qemu/libcacard/vreader.c deleted file mode 100644 index 9725f46a7..000000000 --- a/qemu/libcacard/vreader.c +++ /dev/null @@ -1,578 +0,0 @@ -/* - * emulate the reader - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#ifdef G_LOG_DOMAIN -#undef G_LOG_DOMAIN -#endif -#define G_LOG_DOMAIN "libcacard" - -#include "glib-compat.h" - -#include - -#include "vcard.h" -#include "vcard_emul.h" -#include "card_7816.h" -#include "vreader.h" -#include "vevent.h" -#include "cac.h" /* just for debugging defines */ - -#define LIBCACARD_LOG_DOMAIN "libcacard" - -struct VReaderStruct { - int reference_count; - VCard *card; - char *name; - vreader_id_t id; - CompatGMutex lock; - VReaderEmul *reader_private; - VReaderEmulFree reader_private_free; -}; - -/* - * Debug helpers - */ - -static const char * -apdu_ins_to_string(int ins) -{ - switch (ins) { - case VCARD7816_INS_MANAGE_CHANNEL: - return "manage channel"; - case VCARD7816_INS_EXTERNAL_AUTHENTICATE: - return "external authenticate"; - case VCARD7816_INS_GET_CHALLENGE: - return "get challenge"; - case VCARD7816_INS_INTERNAL_AUTHENTICATE: - return "internal authenticate"; - case VCARD7816_INS_ERASE_BINARY: - return "erase binary"; - case VCARD7816_INS_READ_BINARY: - return "read binary"; - case VCARD7816_INS_WRITE_BINARY: - return "write binary"; - case VCARD7816_INS_UPDATE_BINARY: - return "update binary"; - case VCARD7816_INS_READ_RECORD: - return "read record"; - case VCARD7816_INS_WRITE_RECORD: - return "write record"; - case VCARD7816_INS_UPDATE_RECORD: - return "update record"; - case VCARD7816_INS_APPEND_RECORD: - return "append record"; - case VCARD7816_INS_ENVELOPE: - return "envelope"; - case VCARD7816_INS_PUT_DATA: - return "put data"; - case VCARD7816_INS_GET_DATA: - return "get data"; - case VCARD7816_INS_SELECT_FILE: - return "select file"; - case VCARD7816_INS_VERIFY: - return "verify"; - case VCARD7816_INS_GET_RESPONSE: - return "get response"; - case CAC_GET_PROPERTIES: - return "get properties"; - case CAC_GET_ACR: - return "get acr"; - case CAC_READ_BUFFER: - return "read buffer"; - case CAC_UPDATE_BUFFER: - return "update buffer"; - case CAC_SIGN_DECRYPT: - return "sign decrypt"; - case CAC_GET_CERTIFICATE: - return "get certificate"; - } - return "unknown"; -} - -/* manage locking */ -static inline void -vreader_lock(VReader *reader) -{ - g_mutex_lock(&reader->lock); -} - -static inline void -vreader_unlock(VReader *reader) -{ - g_mutex_unlock(&reader->lock); -} - -/* - * vreader constructor - */ -VReader * -vreader_new(const char *name, VReaderEmul *private, - VReaderEmulFree private_free) -{ - VReader *reader; - - reader = g_new(VReader, 1); - g_mutex_init(&reader->lock); - reader->reference_count = 1; - reader->name = g_strdup(name); - reader->card = NULL; - reader->id = (vreader_id_t)-1; - reader->reader_private = private; - reader->reader_private_free = private_free; - return reader; -} - -/* get a reference */ -VReader* -vreader_reference(VReader *reader) -{ - if (reader == NULL) { - return NULL; - } - vreader_lock(reader); - reader->reference_count++; - vreader_unlock(reader); - return reader; -} - -/* free a reference */ -void -vreader_free(VReader *reader) -{ - if (reader == NULL) { - return; - } - vreader_lock(reader); - if (reader->reference_count-- > 1) { - vreader_unlock(reader); - return; - } - vreader_unlock(reader); - g_mutex_clear(&reader->lock); - if (reader->card) { - vcard_free(reader->card); - } - g_free(reader->name); - if (reader->reader_private_free) { - reader->reader_private_free(reader->reader_private); - } - g_free(reader); -} - -static VCard * -vreader_get_card(VReader *reader) -{ - VCard *card; - - vreader_lock(reader); - card = vcard_reference(reader->card); - vreader_unlock(reader); - return card; -} - -VReaderStatus -vreader_card_is_present(VReader *reader) -{ - VCard *card = vreader_get_card(reader); - - if (card == NULL) { - return VREADER_NO_CARD; - } - vcard_free(card); - return VREADER_OK; -} - -vreader_id_t -vreader_get_id(VReader *reader) -{ - if (reader == NULL) { - return (vreader_id_t)-1; - } - return reader->id; -} - -VReaderStatus -vreader_set_id(VReader *reader, vreader_id_t id) -{ - if (reader == NULL) { - return VREADER_NO_CARD; - } - reader->id = id; - return VREADER_OK; -} - -const char * -vreader_get_name(VReader *reader) -{ - if (reader == NULL) { - return NULL; - } - return reader->name; -} - -VReaderEmul * -vreader_get_private(VReader *reader) -{ - return reader->reader_private; -} - -static VReaderStatus -vreader_reset(VReader *reader, VCardPower power, unsigned char *atr, int *len) -{ - VCard *card = vreader_get_card(reader); - - if (card == NULL) { - return VREADER_NO_CARD; - } - /* - * clean up our state - */ - vcard_reset(card, power); - if (atr) { - vcard_get_atr(card, atr, len); - } - vcard_free(card); /* free our reference */ - return VREADER_OK; -} - -VReaderStatus -vreader_power_on(VReader *reader, unsigned char *atr, int *len) -{ - return vreader_reset(reader, VCARD_POWER_ON, atr, len); -} - -VReaderStatus -vreader_power_off(VReader *reader) -{ - return vreader_reset(reader, VCARD_POWER_OFF, NULL, 0); -} - - -VReaderStatus -vreader_xfr_bytes(VReader *reader, - unsigned char *send_buf, int send_buf_len, - unsigned char *receive_buf, int *receive_buf_len) -{ - VCardAPDU *apdu; - VCardResponse *response = NULL; - VCardStatus card_status; - unsigned short status; - VCard *card = vreader_get_card(reader); - - if (card == NULL) { - return VREADER_NO_CARD; - } - - apdu = vcard_apdu_new(send_buf, send_buf_len, &status); - if (apdu == NULL) { - response = vcard_make_response(status); - card_status = VCARD_DONE; - } else { - g_debug("%s: CLS=0x%x,INS=0x%x,P1=0x%x,P2=0x%x,Lc=%d,Le=%d %s", - __func__, apdu->a_cla, apdu->a_ins, apdu->a_p1, apdu->a_p2, - apdu->a_Lc, apdu->a_Le, apdu_ins_to_string(apdu->a_ins)); - card_status = vcard_process_apdu(card, apdu, &response); - if (response) { - g_debug("%s: status=%d sw1=0x%x sw2=0x%x len=%d (total=%d)", - __func__, response->b_status, response->b_sw1, - response->b_sw2, response->b_len, response->b_total_len); - } - } - assert(card_status == VCARD_DONE && response); - int size = MIN(*receive_buf_len, response->b_total_len); - memcpy(receive_buf, response->b_data, size); - *receive_buf_len = size; - vcard_response_delete(response); - vcard_apdu_delete(apdu); - vcard_free(card); /* free our reference */ - return VREADER_OK; -} - -struct VReaderListStruct { - VReaderListEntry *head; - VReaderListEntry *tail; -}; - -struct VReaderListEntryStruct { - VReaderListEntry *next; - VReaderListEntry *prev; - VReader *reader; -}; - - -static VReaderListEntry * -vreader_list_entry_new(VReader *reader) -{ - VReaderListEntry *new_reader_list_entry; - - new_reader_list_entry = g_new0(VReaderListEntry, 1); - new_reader_list_entry->reader = vreader_reference(reader); - return new_reader_list_entry; -} - -static void -vreader_list_entry_delete(VReaderListEntry *entry) -{ - if (entry == NULL) { - return; - } - vreader_free(entry->reader); - g_free(entry); -} - - -static VReaderList * -vreader_list_new(void) -{ - VReaderList *new_reader_list; - - new_reader_list = g_new0(VReaderList, 1); - return new_reader_list; -} - -void -vreader_list_delete(VReaderList *list) -{ - VReaderListEntry *current_entry; - VReaderListEntry *next_entry; - for (current_entry = vreader_list_get_first(list); current_entry; - current_entry = next_entry) { - next_entry = vreader_list_get_next(current_entry); - vreader_list_entry_delete(current_entry); - } - g_free(list); -} - - -VReaderListEntry * -vreader_list_get_first(VReaderList *list) -{ - return list ? list->head : NULL; -} - -VReaderListEntry * -vreader_list_get_next(VReaderListEntry *current) -{ - return current ? current->next : NULL; -} - -VReader * -vreader_list_get_reader(VReaderListEntry *entry) -{ - return entry ? vreader_reference(entry->reader) : NULL; -} - -static void -vreader_queue(VReaderList *list, VReaderListEntry *entry) -{ - if (entry == NULL) { - return; - } - entry->next = NULL; - entry->prev = list->tail; - if (list->head) { - list->tail->next = entry; - } else { - list->head = entry; - } - list->tail = entry; -} - -static void -vreader_dequeue(VReaderList *list, VReaderListEntry *entry) -{ - if (entry == NULL) { - return; - } - if (entry->next == NULL) { - list->tail = entry->prev; - } else if (entry->prev == NULL) { - list->head = entry->next; - } else { - entry->prev->next = entry->next; - entry->next->prev = entry->prev; - } - if ((list->tail == NULL) || (list->head == NULL)) { - list->head = list->tail = NULL; - } - entry->next = entry->prev = NULL; -} - -static VReaderList *vreader_list; -static CompatGMutex vreader_list_mutex; - -static void -vreader_list_init(void) -{ - vreader_list = vreader_list_new(); -} - -static void -vreader_list_lock(void) -{ - g_mutex_lock(&vreader_list_mutex); -} - -static void -vreader_list_unlock(void) -{ - g_mutex_unlock(&vreader_list_mutex); -} - -static VReaderList * -vreader_copy_list(VReaderList *list) -{ - VReaderList *new_list; - VReaderListEntry *current_entry; - - new_list = vreader_list_new(); - if (new_list == NULL) { - return NULL; - } - for (current_entry = vreader_list_get_first(list); current_entry; - current_entry = vreader_list_get_next(current_entry)) { - VReader *reader = vreader_list_get_reader(current_entry); - VReaderListEntry *new_entry = vreader_list_entry_new(reader); - - vreader_free(reader); - vreader_queue(new_list, new_entry); - } - return new_list; -} - -VReaderList * -vreader_get_reader_list(void) -{ - VReaderList *new_reader_list; - - vreader_list_lock(); - new_reader_list = vreader_copy_list(vreader_list); - vreader_list_unlock(); - return new_reader_list; -} - -VReader * -vreader_get_reader_by_id(vreader_id_t id) -{ - VReader *reader = NULL; - VReaderListEntry *current_entry; - - if (id == (vreader_id_t) -1) { - return NULL; - } - - vreader_list_lock(); - for (current_entry = vreader_list_get_first(vreader_list); current_entry; - current_entry = vreader_list_get_next(current_entry)) { - VReader *creader = vreader_list_get_reader(current_entry); - if (creader->id == id) { - reader = creader; - break; - } - vreader_free(creader); - } - vreader_list_unlock(); - return reader; -} - -VReader * -vreader_get_reader_by_name(const char *name) -{ - VReader *reader = NULL; - VReaderListEntry *current_entry; - - vreader_list_lock(); - for (current_entry = vreader_list_get_first(vreader_list); current_entry; - current_entry = vreader_list_get_next(current_entry)) { - VReader *creader = vreader_list_get_reader(current_entry); - if (strcmp(creader->name, name) == 0) { - reader = creader; - break; - } - vreader_free(creader); - } - vreader_list_unlock(); - return reader; -} - -/* called from card_emul to initialize the readers */ -VReaderStatus -vreader_add_reader(VReader *reader) -{ - VReaderListEntry *reader_entry; - - reader_entry = vreader_list_entry_new(reader); - if (reader_entry == NULL) { - return VREADER_OUT_OF_MEMORY; - } - vreader_list_lock(); - vreader_queue(vreader_list, reader_entry); - vreader_list_unlock(); - vevent_queue_vevent(vevent_new(VEVENT_READER_INSERT, reader, NULL)); - return VREADER_OK; -} - - -VReaderStatus -vreader_remove_reader(VReader *reader) -{ - VReaderListEntry *current_entry; - - vreader_list_lock(); - for (current_entry = vreader_list_get_first(vreader_list); current_entry; - current_entry = vreader_list_get_next(current_entry)) { - if (current_entry->reader == reader) { - break; - } - } - vreader_dequeue(vreader_list, current_entry); - vreader_list_unlock(); - vreader_list_entry_delete(current_entry); - vevent_queue_vevent(vevent_new(VEVENT_READER_REMOVE, reader, NULL)); - return VREADER_OK; -} - -/* - * Generate VEVENT_CARD_INSERT or VEVENT_CARD_REMOVE based on vreader - * state. Separated from vreader_insert_card to allow replaying events - * for a given state. - */ -void -vreader_queue_card_event(VReader *reader) -{ - vevent_queue_vevent(vevent_new( - reader->card ? VEVENT_CARD_INSERT : VEVENT_CARD_REMOVE, reader, - reader->card)); -} - -/* - * insert/remove a new card. for removal, card == NULL - */ -VReaderStatus -vreader_insert_card(VReader *reader, VCard *card) -{ - vreader_lock(reader); - if (reader->card) { - /* decrement reference count */ - vcard_free(reader->card); - reader->card = NULL; - } - reader->card = vcard_reference(card); - vreader_unlock(reader); - vreader_queue_card_event(reader); - return VREADER_OK; -} - -/* - * initialize all the static reader structures - */ -void -vreader_init(void) -{ - vreader_list_init(); -} - diff --git a/qemu/libcacard/vreader.h b/qemu/libcacard/vreader.h deleted file mode 100644 index ec2042136..000000000 --- a/qemu/libcacard/vreader.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#ifndef VREADER_H -#define VREADER_H 1 - -#include "eventt.h" -#include "vreadert.h" -#include "vcardt.h" - -/* - * calls for reader front end - */ -VReaderStatus vreader_power_on(VReader *reader, unsigned char *atr, int *len); -VReaderStatus vreader_power_off(VReader *reader); -VReaderStatus vreader_xfr_bytes(VReader *reader, unsigned char *send_buf, - int send_buf_len, unsigned char *receive_buf, - int *receive_buf_len); - -/* constructor */ -VReader *vreader_new(const char *readerName, VReaderEmul *emul_private, - VReaderEmulFree private_free); -/* get a new reference to a reader */ -VReader *vreader_reference(VReader *reader); -/* "destructor" (readers are reference counted) */ -void vreader_free(VReader *reader); - -/* accessors */ -VReaderEmul *vreader_get_private(VReader *); -VReaderStatus vreader_card_is_present(VReader *reader); -void vreader_queue_card_event(VReader *reader); -const char *vreader_get_name(VReader *reader); -vreader_id_t vreader_get_id(VReader *reader); -VReaderStatus vreader_set_id(VReader *reader, vreader_id_t id); - -/* list operations */ -VReaderList *vreader_get_reader_list(void); -void vreader_list_delete(VReaderList *list); -VReader *vreader_list_get_reader(VReaderListEntry *entry); -VReaderListEntry *vreader_list_get_first(VReaderList *list); -VReaderListEntry *vreader_list_get_next(VReaderListEntry *list); -VReader *vreader_get_reader_by_id(vreader_id_t id); -VReader *vreader_get_reader_by_name(const char *name); - -/* - * list tools for vcard_emul - */ -void vreader_init(void); -VReaderStatus vreader_add_reader(VReader *reader); -VReaderStatus vreader_remove_reader(VReader *reader); -VReaderStatus vreader_insert_card(VReader *reader, VCard *card); - -#endif diff --git a/qemu/libcacard/vreadert.h b/qemu/libcacard/vreadert.h deleted file mode 100644 index f97e0a79e..000000000 --- a/qemu/libcacard/vreadert.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#ifndef VREADERT_H -#define VREADERT_H 1 - -typedef enum { - VREADER_OK = 0, - VREADER_NO_CARD, - VREADER_OUT_OF_MEMORY -} VReaderStatus; - -typedef unsigned int vreader_id_t; -typedef struct VReaderStruct VReader; -typedef struct VReaderListStruct VReaderList; -typedef struct VReaderListEntryStruct VReaderListEntry; - -typedef struct VReaderEmulStruct VReaderEmul; -typedef void (*VReaderEmulFree)(VReaderEmul *); - -#endif - diff --git a/qemu/libcacard/vscard_common.h b/qemu/libcacard/vscard_common.h deleted file mode 100644 index 08f68e4dd..000000000 --- a/qemu/libcacard/vscard_common.h +++ /dev/null @@ -1,178 +0,0 @@ -/* Virtual Smart Card protocol definition - * - * This protocol is between a host using virtual smart card readers, - * and a client providing the smart cards, perhaps by emulating them or by - * access to real cards. - * - * Definitions for this protocol: - * Host - user of the card - * Client - owner of the card - * - * The current implementation passes the raw APDU's from 7816 and additionally - * contains messages to setup and teardown readers, handle insertion and - * removal of cards, negotiate the protocol via capabilities and provide - * for error responses. - * - * Copyright (c) 2011 Red Hat. - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#ifndef VSCARD_COMMON_H -#define VSCARD_COMMON_H - -#include - -#define VERSION_MAJOR_BITS 11 -#define VERSION_MIDDLE_BITS 11 -#define VERSION_MINOR_BITS 10 - -#define MAKE_VERSION(major, middle, minor) \ - ((major << (VERSION_MINOR_BITS + VERSION_MIDDLE_BITS)) \ - | (middle << VERSION_MINOR_BITS) \ - | (minor)) - -/* - * IMPORTANT NOTE on VERSION - * - * The version below MUST be changed whenever a change in this file is made. - * - * The last digit, the minor, is for bug fix changes only. - * - * The middle digit is for backward / forward compatible changes, updates - * to the existing messages, addition of fields. - * - * The major digit is for a breaking change of protocol, presumably - * something that cannot be accommodated with the existing protocol. - */ - -#define VSCARD_VERSION MAKE_VERSION(0, 0, 2) - -typedef enum VSCMsgType { - VSC_Init = 1, - VSC_Error, - VSC_ReaderAdd, - VSC_ReaderRemove, - VSC_ATR, - VSC_CardRemove, - VSC_APDU, - VSC_Flush, - VSC_FlushComplete -} VSCMsgType; - -typedef enum VSCErrorCode { - VSC_SUCCESS = 0, - VSC_GENERAL_ERROR = 1, - VSC_CANNOT_ADD_MORE_READERS, - VSC_CARD_ALREAY_INSERTED, -} VSCErrorCode; - -#define VSCARD_UNDEFINED_READER_ID 0xffffffff -#define VSCARD_MINIMAL_READER_ID 0 - -#define VSCARD_MAGIC (*(uint32_t *)"VSCD") - -/* - * Header - * Each message starts with the header. - * type - message type - * reader_id - used by messages that are reader specific - * length - length of payload (not including header, i.e. zero for - * messages containing empty payloads) - */ -typedef struct VSCMsgHeader { - uint32_t type; - uint32_t reader_id; - uint32_t length; - uint8_t data[0]; -} VSCMsgHeader; - -/* - * VSCMsgInit Client <-> Host - * Client sends it on connection, with its own capabilities. - * Host replies with VSCMsgInit filling in its capabilities. - * - * It is not meant to be used for negotiation, i.e. sending more then - * once from any side, but could be used for that in the future. - */ -typedef struct VSCMsgInit { - uint32_t magic; - uint32_t version; - uint32_t capabilities[1]; /* receiver must check length, - array may grow in the future*/ -} VSCMsgInit; - -/* - * VSCMsgError Client <-> Host - * This message is a response to any of: - * Reader Add - * Reader Remove - * Card Remove - * If the operation was successful then VSC_SUCCESS - * is returned, other wise a specific error code. - */ -typedef struct VSCMsgError { - uint32_t code; -} VSCMsgError; - -/* - * VSCMsgReaderAdd Client -> Host - * Host replies with allocated reader id in VSCMsgError with code==SUCCESS. - * - * name - name of the reader on client side, UTF-8 encoded. Only used - * for client presentation (may be translated to the device presented to the - * guest), protocol wise only reader_id is important. - */ -typedef struct VSCMsgReaderAdd { - uint8_t name[0]; -} VSCMsgReaderAdd; - -/* - * VSCMsgReaderRemove Client -> Host - * The client's reader has been removed. - */ -typedef struct VSCMsgReaderRemove { -} VSCMsgReaderRemove; - -/* - * VSCMsgATR Client -> Host - * Answer to reset. Sent for card insertion or card reset. The reset/insertion - * happens on the client side, they do not require any action from the host. - */ -typedef struct VSCMsgATR { - uint8_t atr[0]; -} VSCMsgATR; - -/* - * VSCMsgCardRemove Client -> Host - * The client card has been removed. - */ -typedef struct VSCMsgCardRemove { -} VSCMsgCardRemove; - -/* - * VSCMsgAPDU Client <-> Host - * Main reason of existence. Transfer a single APDU in either direction. - */ -typedef struct VSCMsgAPDU { - uint8_t data[0]; -} VSCMsgAPDU; - -/* - * VSCMsgFlush Host -> Client - * Request client to send a FlushComplete message when it is done - * servicing all outstanding APDUs - */ -typedef struct VSCMsgFlush { -} VSCMsgFlush; - -/* - * VSCMsgFlush Client -> Host - * Client response to Flush after all APDUs have been processed and - * responses sent. - */ -typedef struct VSCMsgFlushComplete { -} VSCMsgFlushComplete; - -#endif /* VSCARD_COMMON_H */ diff --git a/qemu/libcacard/vscclient.c b/qemu/libcacard/vscclient.c deleted file mode 100644 index 065268443..000000000 --- a/qemu/libcacard/vscclient.c +++ /dev/null @@ -1,785 +0,0 @@ -/* - * Tester for VSCARD protocol, client side. - * - * Can be used with ccid-card-passthru. - * - * Copyright (c) 2011 Red Hat. - * Written by Alon Levy. - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include -#include -#include -#ifndef _WIN32 -#include -#include -#include -#include -#define closesocket(x) close(x) -#else -#include -#endif - -#include "glib-compat.h" - -#include "vscard_common.h" - -#include "vreader.h" -#include "vcard_emul.h" -#include "vevent.h" - -static int verbose; - -static void -print_byte_array( - uint8_t *arrBytes, - unsigned int nSize -) { - int i; - for (i = 0; i < nSize; i++) { - printf("%02X ", arrBytes[i]); - } - printf("\n"); -} - -static void -print_usage(void) { - printf("vscclient [-c .. -e -d %s] " - " \n", -#ifdef USE_PASSTHRU - " -p"); - printf(" -p use passthrough mode\n"); -#else - ""); -#endif - vcard_emul_usage(); -} - -static GIOChannel *channel_socket; -static GByteArray *socket_to_send; -static CompatGMutex socket_to_send_lock; -static guint socket_tag; - -static void -update_socket_watch(void); - -static gboolean -do_socket_send(GIOChannel *source, - GIOCondition condition, - gpointer data) -{ - gsize bw; - GError *err = NULL; - - g_return_val_if_fail(socket_to_send->len != 0, FALSE); - g_return_val_if_fail(condition & G_IO_OUT, FALSE); - - g_io_channel_write_chars(channel_socket, - (gchar *)socket_to_send->data, socket_to_send->len, &bw, &err); - if (err != NULL) { - g_error("Error while sending socket %s", err->message); - return FALSE; - } - g_byte_array_remove_range(socket_to_send, 0, bw); - - if (socket_to_send->len == 0) { - update_socket_watch(); - return FALSE; - } - return TRUE; -} - -static gboolean -socket_prepare_sending(gpointer user_data) -{ - update_socket_watch(); - - return FALSE; -} - -static int -send_msg( - VSCMsgType type, - uint32_t reader_id, - const void *msg, - unsigned int length -) { - VSCMsgHeader mhHeader; - - g_mutex_lock(&socket_to_send_lock); - - if (verbose > 10) { - printf("sending type=%d id=%u, len =%u (0x%x)\n", - type, reader_id, length, length); - } - - mhHeader.type = htonl(type); - mhHeader.reader_id = 0; - mhHeader.length = htonl(length); - g_byte_array_append(socket_to_send, (guint8 *)&mhHeader, sizeof(mhHeader)); - g_byte_array_append(socket_to_send, (guint8 *)msg, length); - g_idle_add(socket_prepare_sending, NULL); - - g_mutex_unlock(&socket_to_send_lock); - - return 0; -} - -static VReader *pending_reader; -static CompatGMutex pending_reader_lock; -static CompatGCond pending_reader_condition; - -#define MAX_ATR_LEN 40 -static gpointer -event_thread(gpointer arg) -{ - unsigned char atr[MAX_ATR_LEN]; - int atr_len; - VEvent *event; - unsigned int reader_id; - - - while (1) { - const char *reader_name; - - event = vevent_wait_next_vevent(); - if (event == NULL) { - break; - } - reader_id = vreader_get_id(event->reader); - if (reader_id == VSCARD_UNDEFINED_READER_ID && - event->type != VEVENT_READER_INSERT) { - /* ignore events from readers qemu has rejected */ - /* if qemu is still deciding on this reader, wait to see if need to - * forward this event */ - g_mutex_lock(&pending_reader_lock); - if (!pending_reader || (pending_reader != event->reader)) { - /* wasn't for a pending reader, this reader has already been - * rejected by qemu */ - g_mutex_unlock(&pending_reader_lock); - vevent_delete(event); - continue; - } - /* this reader hasn't been told its status from qemu yet, wait for - * that status */ - while (pending_reader != NULL) { - g_cond_wait(&pending_reader_condition, &pending_reader_lock); - } - g_mutex_unlock(&pending_reader_lock); - /* now recheck the id */ - reader_id = vreader_get_id(event->reader); - if (reader_id == VSCARD_UNDEFINED_READER_ID) { - /* this reader was rejected */ - vevent_delete(event); - continue; - } - /* reader was accepted, now forward the event */ - } - switch (event->type) { - case VEVENT_READER_INSERT: - /* tell qemu to insert a new CCID reader */ - /* wait until qemu has responded to our first reader insert - * before we send a second. That way we won't confuse the responses - * */ - g_mutex_lock(&pending_reader_lock); - while (pending_reader != NULL) { - g_cond_wait(&pending_reader_condition, &pending_reader_lock); - } - pending_reader = vreader_reference(event->reader); - g_mutex_unlock(&pending_reader_lock); - reader_name = vreader_get_name(event->reader); - if (verbose > 10) { - printf(" READER INSERT: %s\n", reader_name); - } - send_msg(VSC_ReaderAdd, - reader_id, /* currerntly VSCARD_UNDEFINED_READER_ID */ - NULL, 0 /* TODO reader_name, strlen(reader_name) */); - break; - case VEVENT_READER_REMOVE: - /* future, tell qemu that an old CCID reader has been removed */ - if (verbose > 10) { - printf(" READER REMOVE: %u\n", reader_id); - } - send_msg(VSC_ReaderRemove, reader_id, NULL, 0); - break; - case VEVENT_CARD_INSERT: - /* get the ATR (intended as a response to a power on from the - * reader */ - atr_len = MAX_ATR_LEN; - vreader_power_on(event->reader, atr, &atr_len); - /* ATR call functions as a Card Insert event */ - if (verbose > 10) { - printf(" CARD INSERT %u: ", reader_id); - print_byte_array(atr, atr_len); - } - send_msg(VSC_ATR, reader_id, atr, atr_len); - break; - case VEVENT_CARD_REMOVE: - /* Card removed */ - if (verbose > 10) { - printf(" CARD REMOVE %u:\n", reader_id); - } - send_msg(VSC_CardRemove, reader_id, NULL, 0); - break; - default: - break; - } - vevent_delete(event); - } - return NULL; -} - - -static unsigned int -get_id_from_string(char *string, unsigned int default_id) -{ - unsigned int id = atoi(string); - - /* don't accidentally swith to zero because no numbers have been supplied */ - if ((id == 0) && *string != '0') { - return default_id; - } - return id; -} - -static int -on_host_init(VSCMsgHeader *mhHeader, VSCMsgInit *incoming) -{ - uint32_t *capabilities = (incoming->capabilities); - int num_capabilities = - 1 + ((mhHeader->length - sizeof(VSCMsgInit)) / sizeof(uint32_t)); - int i; - - incoming->version = ntohl(incoming->version); - if (incoming->version != VSCARD_VERSION) { - if (verbose > 0) { - printf("warning: host has version %d, we have %d\n", - verbose, VSCARD_VERSION); - } - } - if (incoming->magic != VSCARD_MAGIC) { - printf("unexpected magic: got %d, expected %d\n", - incoming->magic, VSCARD_MAGIC); - return -1; - } - for (i = 0 ; i < num_capabilities; ++i) { - capabilities[i] = ntohl(capabilities[i]); - } - /* Future: check capabilities */ - /* remove whatever reader might be left in qemu, - * in case of an unclean previous exit. */ - send_msg(VSC_ReaderRemove, VSCARD_MINIMAL_READER_ID, NULL, 0); - /* launch the event_thread. This will trigger reader adds for all the - * existing readers */ - g_thread_new("vsc/event", event_thread, NULL); - return 0; -} - - -enum { - STATE_HEADER, - STATE_MESSAGE, -}; - -#define APDUBufSize 270 - -static gboolean -do_socket_read(GIOChannel *source, - GIOCondition condition, - gpointer data) -{ - int rv; - int dwSendLength; - int dwRecvLength; - uint8_t pbRecvBuffer[APDUBufSize]; - static uint8_t pbSendBuffer[APDUBufSize]; - VReaderStatus reader_status; - VReader *reader = NULL; - static VSCMsgHeader mhHeader; - VSCMsgError *error_msg; - GError *err = NULL; - - static gchar *buf; - static gsize br, to_read; - static int state = STATE_HEADER; - - if (state == STATE_HEADER && to_read == 0) { - buf = (gchar *)&mhHeader; - to_read = sizeof(mhHeader); - } - - if (to_read > 0) { - g_io_channel_read_chars(source, (gchar *)buf, to_read, &br, &err); - if (err != NULL) { - g_error("error while reading: %s", err->message); - } - buf += br; - to_read -= br; - if (to_read != 0) { - return TRUE; - } - } - - if (state == STATE_HEADER) { - mhHeader.type = ntohl(mhHeader.type); - mhHeader.reader_id = ntohl(mhHeader.reader_id); - mhHeader.length = ntohl(mhHeader.length); - if (verbose) { - printf("Header: type=%d, reader_id=%u length=%d (0x%x)\n", - mhHeader.type, mhHeader.reader_id, mhHeader.length, - mhHeader.length); - } - switch (mhHeader.type) { - case VSC_APDU: - case VSC_Flush: - case VSC_Error: - case VSC_Init: - buf = (gchar *)pbSendBuffer; - to_read = mhHeader.length; - state = STATE_MESSAGE; - return TRUE; - default: - fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type); - return FALSE; - } - } - - if (state == STATE_MESSAGE) { - switch (mhHeader.type) { - case VSC_APDU: - if (verbose) { - printf(" recv APDU: "); - print_byte_array(pbSendBuffer, mhHeader.length); - } - /* Transmit received APDU */ - dwSendLength = mhHeader.length; - dwRecvLength = sizeof(pbRecvBuffer); - reader = vreader_get_reader_by_id(mhHeader.reader_id); - reader_status = vreader_xfr_bytes(reader, - pbSendBuffer, dwSendLength, - pbRecvBuffer, &dwRecvLength); - if (reader_status == VREADER_OK) { - mhHeader.length = dwRecvLength; - if (verbose) { - printf(" send response: "); - print_byte_array(pbRecvBuffer, mhHeader.length); - } - send_msg(VSC_APDU, mhHeader.reader_id, - pbRecvBuffer, dwRecvLength); - } else { - rv = reader_status; /* warning: not meaningful */ - send_msg(VSC_Error, mhHeader.reader_id, &rv, sizeof(uint32_t)); - } - vreader_free(reader); - reader = NULL; /* we've freed it, don't use it by accident - again */ - break; - case VSC_Flush: - /* TODO: actually flush */ - send_msg(VSC_FlushComplete, mhHeader.reader_id, NULL, 0); - break; - case VSC_Error: - error_msg = (VSCMsgError *) pbSendBuffer; - if (error_msg->code == VSC_SUCCESS) { - g_mutex_lock(&pending_reader_lock); - if (pending_reader) { - vreader_set_id(pending_reader, mhHeader.reader_id); - vreader_free(pending_reader); - pending_reader = NULL; - g_cond_signal(&pending_reader_condition); - } - g_mutex_unlock(&pending_reader_lock); - break; - } - printf("warning: qemu refused to add reader\n"); - if (error_msg->code == VSC_CANNOT_ADD_MORE_READERS) { - /* clear pending reader, qemu can't handle any more */ - g_mutex_lock(&pending_reader_lock); - if (pending_reader) { - pending_reader = NULL; - /* make sure the event loop doesn't hang */ - g_cond_signal(&pending_reader_condition); - } - g_mutex_unlock(&pending_reader_lock); - } - break; - case VSC_Init: - if (on_host_init(&mhHeader, (VSCMsgInit *)pbSendBuffer) < 0) { - return FALSE; - } - break; - default: - g_assert_not_reached(); - return FALSE; - } - - state = STATE_HEADER; - } - - - return TRUE; -} - -static gboolean -do_socket(GIOChannel *source, - GIOCondition condition, - gpointer data) -{ - /* not sure if two watches work well with a single win32 sources */ - if (condition & G_IO_OUT) { - if (!do_socket_send(source, condition, data)) { - return FALSE; - } - } - - if (condition & G_IO_IN) { - if (!do_socket_read(source, condition, data)) { - return FALSE; - } - } - - return TRUE; -} - -static void -update_socket_watch(void) -{ - gboolean out = socket_to_send->len > 0; - - if (socket_tag != 0) { - g_source_remove(socket_tag); - } - - socket_tag = g_io_add_watch(channel_socket, - G_IO_IN | (out ? G_IO_OUT : 0), do_socket, NULL); -} - -static gboolean -do_command(GIOChannel *source, - GIOCondition condition, - gpointer data) -{ - char *string; - VCardEmulError error; - static unsigned int default_reader_id; - unsigned int reader_id; - VReader *reader = NULL; - GError *err = NULL; - - g_assert(condition & G_IO_IN); - - reader_id = default_reader_id; - g_io_channel_read_line(source, &string, NULL, NULL, &err); - if (err != NULL) { - g_error("Error while reading command: %s", err->message); - } - - if (string != NULL) { - if (strncmp(string, "exit", 4) == 0) { - /* remove all the readers */ - VReaderList *list = vreader_get_reader_list(); - VReaderListEntry *reader_entry; - printf("Active Readers:\n"); - for (reader_entry = vreader_list_get_first(list); reader_entry; - reader_entry = vreader_list_get_next(reader_entry)) { - VReader *reader = vreader_list_get_reader(reader_entry); - vreader_id_t reader_id; - reader_id = vreader_get_id(reader); - if (reader_id == -1) { - continue; - } - /* be nice and signal card removal first (qemu probably should - * do this itself) */ - if (vreader_card_is_present(reader) == VREADER_OK) { - send_msg(VSC_CardRemove, reader_id, NULL, 0); - } - send_msg(VSC_ReaderRemove, reader_id, NULL, 0); - } - exit(0); - } else if (strncmp(string, "insert", 6) == 0) { - if (string[6] == ' ') { - reader_id = get_id_from_string(&string[7], reader_id); - } - reader = vreader_get_reader_by_id(reader_id); - if (reader != NULL) { - error = vcard_emul_force_card_insert(reader); - printf("insert %s, returned %d\n", - vreader_get_name(reader), error); - } else { - printf("no reader by id %u found\n", reader_id); - } - } else if (strncmp(string, "remove", 6) == 0) { - if (string[6] == ' ') { - reader_id = get_id_from_string(&string[7], reader_id); - } - reader = vreader_get_reader_by_id(reader_id); - if (reader != NULL) { - error = vcard_emul_force_card_remove(reader); - printf("remove %s, returned %d\n", - vreader_get_name(reader), error); - } else { - printf("no reader by id %u found\n", reader_id); - } - } else if (strncmp(string, "select", 6) == 0) { - if (string[6] == ' ') { - reader_id = get_id_from_string(&string[7], - VSCARD_UNDEFINED_READER_ID); - } - if (reader_id != VSCARD_UNDEFINED_READER_ID) { - reader = vreader_get_reader_by_id(reader_id); - } - if (reader) { - printf("Selecting reader %u, %s\n", reader_id, - vreader_get_name(reader)); - default_reader_id = reader_id; - } else { - printf("Reader with id %u not found\n", reader_id); - } - } else if (strncmp(string, "debug", 5) == 0) { - if (string[5] == ' ') { - verbose = get_id_from_string(&string[6], 0); - } - printf("debug level = %d\n", verbose); - } else if (strncmp(string, "list", 4) == 0) { - VReaderList *list = vreader_get_reader_list(); - VReaderListEntry *reader_entry; - printf("Active Readers:\n"); - for (reader_entry = vreader_list_get_first(list); reader_entry; - reader_entry = vreader_list_get_next(reader_entry)) { - VReader *reader = vreader_list_get_reader(reader_entry); - vreader_id_t reader_id; - reader_id = vreader_get_id(reader); - if (reader_id == -1) { - continue; - } - printf("%3u %s %s\n", reader_id, - vreader_card_is_present(reader) == VREADER_OK ? - "CARD_PRESENT" : " ", - vreader_get_name(reader)); - } - printf("Inactive Readers:\n"); - for (reader_entry = vreader_list_get_first(list); reader_entry; - reader_entry = vreader_list_get_next(reader_entry)) { - VReader *reader = vreader_list_get_reader(reader_entry); - vreader_id_t reader_id; - reader_id = vreader_get_id(reader); - if (reader_id != -1) { - continue; - } - - printf("INA %s %s\n", - vreader_card_is_present(reader) == VREADER_OK ? - "CARD_PRESENT" : " ", - vreader_get_name(reader)); - } - vreader_list_delete(list); - } else if (*string != 0) { - printf("valid commands:\n"); - printf("insert [reader_id]\n"); - printf("remove [reader_id]\n"); - printf("select reader_id\n"); - printf("list\n"); - printf("debug [level]\n"); - printf("exit\n"); - } - } - vreader_free(reader); - printf("> "); - fflush(stdout); - - return TRUE; -} - - -/* just for ease of parsing command line arguments. */ -#define MAX_CERTS 100 - -static int -connect_to_qemu( - const char *host, - const char *port -) { - struct addrinfo hints; - struct addrinfo *server = NULL; - int ret, sock; - - sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) { - /* Error */ - fprintf(stderr, "Error opening socket!\n"); - return -1; - } - - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = 0; - hints.ai_protocol = 0; /* Any protocol */ - - ret = getaddrinfo(host, port, &hints, &server); - - if (ret != 0) { - /* Error */ - fprintf(stderr, "getaddrinfo failed\n"); - goto cleanup_socket; - } - - if (connect(sock, server->ai_addr, server->ai_addrlen) < 0) { - /* Error */ - fprintf(stderr, "Could not connect\n"); - goto cleanup_socket; - } - if (verbose) { - printf("Connected (sizeof Header=%zd)!\n", sizeof(VSCMsgHeader)); - } - - freeaddrinfo(server); - return sock; - -cleanup_socket: - if (server) { - freeaddrinfo(server); - } - closesocket(sock); - return -1; -} - -int -main( - int argc, - char *argv[] -) { - GMainLoop *loop; - GIOChannel *channel_stdin; - char *qemu_host; - char *qemu_port; - - VCardEmulOptions *command_line_options = NULL; - - char *cert_names[MAX_CERTS]; - char *emul_args = NULL; - int cert_count = 0; - int c, sock; - -#ifdef _WIN32 - WSADATA Data; - - if (WSAStartup(MAKEWORD(2, 2), &Data) != 0) { - c = WSAGetLastError(); - fprintf(stderr, "WSAStartup: %d\n", c); - return 1; - } -#endif -#if !GLIB_CHECK_VERSION(2, 31, 0) - if (!g_thread_supported()) { - g_thread_init(NULL); - } -#endif - - while ((c = getopt(argc, argv, "c:e:pd:")) != -1) { - switch (c) { - case 'c': - if (cert_count >= MAX_CERTS) { - printf("too many certificates (max = %d)\n", MAX_CERTS); - exit(5); - } - cert_names[cert_count++] = optarg; - break; - case 'e': - emul_args = optarg; - break; - case 'p': - print_usage(); - exit(4); - break; - case 'd': - verbose = get_id_from_string(optarg, 1); - break; - } - } - - if (argc - optind != 2) { - print_usage(); - exit(4); - } - - if (cert_count > 0) { - char *new_args; - int len, i; - /* if we've given some -c options, we clearly we want do so some - * software emulation. add that emulation now. this is NSS Emulator - * specific */ - if (emul_args == NULL) { - emul_args = (char *)"db=\"/etc/pki/nssdb\""; - } -#define SOFT_STRING ",soft=(,Virtual Reader,CAC,," - /* 2 == close paren & null */ - len = strlen(emul_args) + strlen(SOFT_STRING) + 2; - for (i = 0; i < cert_count; i++) { - len += strlen(cert_names[i])+1; /* 1 == comma */ - } - new_args = g_malloc(len); - strcpy(new_args, emul_args); - strcat(new_args, SOFT_STRING); - for (i = 0; i < cert_count; i++) { - strcat(new_args, cert_names[i]); - strcat(new_args, ","); - } - strcat(new_args, ")"); - emul_args = new_args; - } - if (emul_args) { - command_line_options = vcard_emul_options(emul_args); - } - - qemu_host = g_strdup(argv[argc - 2]); - qemu_port = g_strdup(argv[argc - 1]); - sock = connect_to_qemu(qemu_host, qemu_port); - if (sock == -1) { - fprintf(stderr, "error opening socket, exiting.\n"); - exit(5); - } - - socket_to_send = g_byte_array_new(); - vcard_emul_init(command_line_options); - loop = g_main_loop_new(NULL, TRUE); - - printf("> "); - fflush(stdout); - -#ifdef _WIN32 - channel_stdin = g_io_channel_win32_new_fd(STDIN_FILENO); -#else - channel_stdin = g_io_channel_unix_new(STDIN_FILENO); -#endif - g_io_add_watch(channel_stdin, G_IO_IN, do_command, NULL); -#ifdef _WIN32 - channel_socket = g_io_channel_win32_new_socket(sock); -#else - channel_socket = g_io_channel_unix_new(sock); -#endif - g_io_channel_set_encoding(channel_socket, NULL, NULL); - /* we buffer ourself for thread safety reasons */ - g_io_channel_set_buffered(channel_socket, FALSE); - - /* Send init message, Host responds (and then we send reader attachments) */ - VSCMsgInit init = { - .version = htonl(VSCARD_VERSION), - .magic = VSCARD_MAGIC, - .capabilities = {0} - }; - send_msg(VSC_Init, 0, &init, sizeof(init)); - - g_main_loop_run(loop); - g_main_loop_unref(loop); - - g_io_channel_unref(channel_stdin); - g_io_channel_unref(channel_socket); - g_byte_array_free(socket_to_send, TRUE); - - closesocket(sock); - return 0; -} diff --git a/qemu/libdecnumber/decContext.c b/qemu/libdecnumber/decContext.c index 8b6ae21be..7d97a65ac 100644 --- a/qemu/libdecnumber/decContext.c +++ b/qemu/libdecnumber/decContext.c @@ -35,8 +35,7 @@ /* context structures. */ /* ------------------------------------------------------------------ */ -#include /* for strcmp */ -#include /* for printf if DECCHECK */ +#include "qemu/osdep.h" #include "libdecnumber/dconfig.h" #include "libdecnumber/decContext.h" #include "libdecnumber/decNumberLocal.h" @@ -186,7 +185,7 @@ uInt decContextGetStatus(decContext *context) { /* newstatus is the source for the bits to be restored */ /* mask indicates the bits to be restored (the status bit that */ /* corresponds to each 1 bit in the mask is set to the value of */ -/* the correspnding bit in newstatus) */ +/* the corresponding bit in newstatus) */ /* returns context */ /* */ /* No error is possible. */ diff --git a/qemu/libdecnumber/decNumber.c b/qemu/libdecnumber/decNumber.c index 58211e7af..c9e7807f8 100644 --- a/qemu/libdecnumber/decNumber.c +++ b/qemu/libdecnumber/decNumber.c @@ -166,10 +166,7 @@ /* ** -- raise to the power */ /* ------------------------------------------------------------------ */ -#include /* for malloc, free, etc. */ -#include /* for printf [if needed] */ -#include /* for strcpy */ -#include /* for lower */ +#include "qemu/osdep.h" #include "libdecnumber/dconfig.h" #include "libdecnumber/decNumber.h" #include "libdecnumber/decNumberLocal.h" @@ -2238,7 +2235,7 @@ decNumber * decNumberPower(decNumber *res, const decNumber *lhs, /* if a negative power the constant 1 is needed, and if not subset */ /* invert the lhs now rather than inverting the result later */ if (decNumberIsNegative(rhs)) { /* was a **-n [hence digits>0] */ - decNumber *inv=invbuff; /* asssume use fixed buffer */ + decNumber *inv=invbuff; /* assume use fixed buffer */ decNumberCopy(&dnOne, dac); /* dnOne=1; [needed now or later] */ #if DECSUBSET if (set->extended) { /* need to calculate 1/lhs */ @@ -3849,7 +3846,7 @@ static void decToString(const decNumber *dn, char *string, Flag eng) { /* */ /* Addition, especially x=x+1, is speed-critical. */ /* The static buffer is larger than might be expected to allow for */ -/* calls from higher-level funtions (notable exp). */ +/* calls from higher-level functions (notably exp). */ /* ------------------------------------------------------------------ */ static decNumber * decAddOp(decNumber *res, const decNumber *lhs, const decNumber *rhs, decContext *set, @@ -4263,7 +4260,7 @@ static decNumber * decAddOp(decNumber *res, const decNumber *lhs, /* long subtractions. These are acc and var1 respectively. */ /* var1 is a copy of the lhs coefficient, var2 is the rhs coefficient.*/ /* The static buffers may be larger than might be expected to allow */ -/* for calls from higher-level funtions (notable exp). */ +/* for calls from higher-level functions (notably exp). */ /* ------------------------------------------------------------------ */ static decNumber * decDivideOp(decNumber *res, const decNumber *lhs, const decNumber *rhs, @@ -5254,7 +5251,7 @@ static decNumber * decMultiplyOp(decNumber *res, const decNumber *lhs, /* exp(-x) where x can be the tiniest number (Ntiny). */ /* */ /* 2. Normalizing x to be <=0.1 (instead of <=1) reduces loop */ -/* iterations by appoximately a third with additional (although */ +/* iterations by approximately a third with additional (although */ /* diminishing) returns as the range is reduced to even smaller */ /* fractions. However, h (the power of 10 used to correct the */ /* result at the end, see below) must be kept <=8 as otherwise */ diff --git a/qemu/libdecnumber/dpd/decimal128.c b/qemu/libdecnumber/dpd/decimal128.c index 7551b7caa..ca4764e54 100644 --- a/qemu/libdecnumber/dpd/decimal128.c +++ b/qemu/libdecnumber/dpd/decimal128.c @@ -39,8 +39,7 @@ /* */ /* Error handling is the same as decNumber (qv.). */ /* ------------------------------------------------------------------ */ -#include /* [for memset/memcpy] */ -#include /* [for printf] */ +#include "qemu/osdep.h" #include "libdecnumber/dconfig.h" #define DECNUMDIGITS 34 /* make decNumbers with space for 34 */ diff --git a/qemu/libdecnumber/dpd/decimal32.c b/qemu/libdecnumber/dpd/decimal32.c index 095ab7565..53f29789d 100644 --- a/qemu/libdecnumber/dpd/decimal32.c +++ b/qemu/libdecnumber/dpd/decimal32.c @@ -39,8 +39,7 @@ /* */ /* Error handling is the same as decNumber (qv.). */ /* ------------------------------------------------------------------ */ -#include /* [for memset/memcpy] */ -#include /* [for printf] */ +#include "qemu/osdep.h" #include "libdecnumber/dconfig.h" #define DECNUMDIGITS 7 /* make decNumbers with space for 7 */ diff --git a/qemu/libdecnumber/dpd/decimal64.c b/qemu/libdecnumber/dpd/decimal64.c index 8256084e9..481617641 100644 --- a/qemu/libdecnumber/dpd/decimal64.c +++ b/qemu/libdecnumber/dpd/decimal64.c @@ -39,8 +39,7 @@ /* */ /* Error handling is the same as decNumber (qv.). */ /* ------------------------------------------------------------------ */ -#include /* [for memset/memcpy] */ -#include /* [for printf] */ +#include "qemu/osdep.h" #include "libdecnumber/dconfig.h" #define DECNUMDIGITS 16 /* make decNumbers with space for 16 */ diff --git a/qemu/linux-headers/asm-arm/unistd.h b/qemu/linux-headers/asm-arm/unistd.h new file mode 100644 index 000000000..3f6f72792 --- /dev/null +++ b/qemu/linux-headers/asm-arm/unistd.h @@ -0,0 +1,450 @@ +/* + * arch/arm/include/asm/unistd.h + * + * Copyright (C) 2001-2005 Russell King + * + * 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. + * + * Please forward _all_ changes to this file to rmk@arm.linux.org.uk, + * no matter what the change is. Thanks! + */ +#ifndef __ASM_ARM_UNISTD_H +#define __ASM_ARM_UNISTD_H + +#define __NR_OABI_SYSCALL_BASE 0x900000 + +#if defined(__thumb__) || defined(__ARM_EABI__) +#define __NR_SYSCALL_BASE 0 +#else +#define __NR_SYSCALL_BASE __NR_OABI_SYSCALL_BASE +#endif + +/* + * This file contains the system call numbers. + */ + +#define __NR_restart_syscall (__NR_SYSCALL_BASE+ 0) +#define __NR_exit (__NR_SYSCALL_BASE+ 1) +#define __NR_fork (__NR_SYSCALL_BASE+ 2) +#define __NR_read (__NR_SYSCALL_BASE+ 3) +#define __NR_write (__NR_SYSCALL_BASE+ 4) +#define __NR_open (__NR_SYSCALL_BASE+ 5) +#define __NR_close (__NR_SYSCALL_BASE+ 6) + /* 7 was sys_waitpid */ +#define __NR_creat (__NR_SYSCALL_BASE+ 8) +#define __NR_link (__NR_SYSCALL_BASE+ 9) +#define __NR_unlink (__NR_SYSCALL_BASE+ 10) +#define __NR_execve (__NR_SYSCALL_BASE+ 11) +#define __NR_chdir (__NR_SYSCALL_BASE+ 12) +#define __NR_time (__NR_SYSCALL_BASE+ 13) +#define __NR_mknod (__NR_SYSCALL_BASE+ 14) +#define __NR_chmod (__NR_SYSCALL_BASE+ 15) +#define __NR_lchown (__NR_SYSCALL_BASE+ 16) + /* 17 was sys_break */ + /* 18 was sys_stat */ +#define __NR_lseek (__NR_SYSCALL_BASE+ 19) +#define __NR_getpid (__NR_SYSCALL_BASE+ 20) +#define __NR_mount (__NR_SYSCALL_BASE+ 21) +#define __NR_umount (__NR_SYSCALL_BASE+ 22) +#define __NR_setuid (__NR_SYSCALL_BASE+ 23) +#define __NR_getuid (__NR_SYSCALL_BASE+ 24) +#define __NR_stime (__NR_SYSCALL_BASE+ 25) +#define __NR_ptrace (__NR_SYSCALL_BASE+ 26) +#define __NR_alarm (__NR_SYSCALL_BASE+ 27) + /* 28 was sys_fstat */ +#define __NR_pause (__NR_SYSCALL_BASE+ 29) +#define __NR_utime (__NR_SYSCALL_BASE+ 30) + /* 31 was sys_stty */ + /* 32 was sys_gtty */ +#define __NR_access (__NR_SYSCALL_BASE+ 33) +#define __NR_nice (__NR_SYSCALL_BASE+ 34) + /* 35 was sys_ftime */ +#define __NR_sync (__NR_SYSCALL_BASE+ 36) +#define __NR_kill (__NR_SYSCALL_BASE+ 37) +#define __NR_rename (__NR_SYSCALL_BASE+ 38) +#define __NR_mkdir (__NR_SYSCALL_BASE+ 39) +#define __NR_rmdir (__NR_SYSCALL_BASE+ 40) +#define __NR_dup (__NR_SYSCALL_BASE+ 41) +#define __NR_pipe (__NR_SYSCALL_BASE+ 42) +#define __NR_times (__NR_SYSCALL_BASE+ 43) + /* 44 was sys_prof */ +#define __NR_brk (__NR_SYSCALL_BASE+ 45) +#define __NR_setgid (__NR_SYSCALL_BASE+ 46) +#define __NR_getgid (__NR_SYSCALL_BASE+ 47) + /* 48 was sys_signal */ +#define __NR_geteuid (__NR_SYSCALL_BASE+ 49) +#define __NR_getegid (__NR_SYSCALL_BASE+ 50) +#define __NR_acct (__NR_SYSCALL_BASE+ 51) +#define __NR_umount2 (__NR_SYSCALL_BASE+ 52) + /* 53 was sys_lock */ +#define __NR_ioctl (__NR_SYSCALL_BASE+ 54) +#define __NR_fcntl (__NR_SYSCALL_BASE+ 55) + /* 56 was sys_mpx */ +#define __NR_setpgid (__NR_SYSCALL_BASE+ 57) + /* 58 was sys_ulimit */ + /* 59 was sys_olduname */ +#define __NR_umask (__NR_SYSCALL_BASE+ 60) +#define __NR_chroot (__NR_SYSCALL_BASE+ 61) +#define __NR_ustat (__NR_SYSCALL_BASE+ 62) +#define __NR_dup2 (__NR_SYSCALL_BASE+ 63) +#define __NR_getppid (__NR_SYSCALL_BASE+ 64) +#define __NR_getpgrp (__NR_SYSCALL_BASE+ 65) +#define __NR_setsid (__NR_SYSCALL_BASE+ 66) +#define __NR_sigaction (__NR_SYSCALL_BASE+ 67) + /* 68 was sys_sgetmask */ + /* 69 was sys_ssetmask */ +#define __NR_setreuid (__NR_SYSCALL_BASE+ 70) +#define __NR_setregid (__NR_SYSCALL_BASE+ 71) +#define __NR_sigsuspend (__NR_SYSCALL_BASE+ 72) +#define __NR_sigpending (__NR_SYSCALL_BASE+ 73) +#define __NR_sethostname (__NR_SYSCALL_BASE+ 74) +#define __NR_setrlimit (__NR_SYSCALL_BASE+ 75) +#define __NR_getrlimit (__NR_SYSCALL_BASE+ 76) /* Back compat 2GB limited rlimit */ +#define __NR_getrusage (__NR_SYSCALL_BASE+ 77) +#define __NR_gettimeofday (__NR_SYSCALL_BASE+ 78) +#define __NR_settimeofday (__NR_SYSCALL_BASE+ 79) +#define __NR_getgroups (__NR_SYSCALL_BASE+ 80) +#define __NR_setgroups (__NR_SYSCALL_BASE+ 81) +#define __NR_select (__NR_SYSCALL_BASE+ 82) +#define __NR_symlink (__NR_SYSCALL_BASE+ 83) + /* 84 was sys_lstat */ +#define __NR_readlink (__NR_SYSCALL_BASE+ 85) +#define __NR_uselib (__NR_SYSCALL_BASE+ 86) +#define __NR_swapon (__NR_SYSCALL_BASE+ 87) +#define __NR_reboot (__NR_SYSCALL_BASE+ 88) +#define __NR_readdir (__NR_SYSCALL_BASE+ 89) +#define __NR_mmap (__NR_SYSCALL_BASE+ 90) +#define __NR_munmap (__NR_SYSCALL_BASE+ 91) +#define __NR_truncate (__NR_SYSCALL_BASE+ 92) +#define __NR_ftruncate (__NR_SYSCALL_BASE+ 93) +#define __NR_fchmod (__NR_SYSCALL_BASE+ 94) +#define __NR_fchown (__NR_SYSCALL_BASE+ 95) +#define __NR_getpriority (__NR_SYSCALL_BASE+ 96) +#define __NR_setpriority (__NR_SYSCALL_BASE+ 97) + /* 98 was sys_profil */ +#define __NR_statfs (__NR_SYSCALL_BASE+ 99) +#define __NR_fstatfs (__NR_SYSCALL_BASE+100) + /* 101 was sys_ioperm */ +#define __NR_socketcall (__NR_SYSCALL_BASE+102) +#define __NR_syslog (__NR_SYSCALL_BASE+103) +#define __NR_setitimer (__NR_SYSCALL_BASE+104) +#define __NR_getitimer (__NR_SYSCALL_BASE+105) +#define __NR_stat (__NR_SYSCALL_BASE+106) +#define __NR_lstat (__NR_SYSCALL_BASE+107) +#define __NR_fstat (__NR_SYSCALL_BASE+108) + /* 109 was sys_uname */ + /* 110 was sys_iopl */ +#define __NR_vhangup (__NR_SYSCALL_BASE+111) + /* 112 was sys_idle */ +#define __NR_syscall (__NR_SYSCALL_BASE+113) /* syscall to call a syscall! */ +#define __NR_wait4 (__NR_SYSCALL_BASE+114) +#define __NR_swapoff (__NR_SYSCALL_BASE+115) +#define __NR_sysinfo (__NR_SYSCALL_BASE+116) +#define __NR_ipc (__NR_SYSCALL_BASE+117) +#define __NR_fsync (__NR_SYSCALL_BASE+118) +#define __NR_sigreturn (__NR_SYSCALL_BASE+119) +#define __NR_clone (__NR_SYSCALL_BASE+120) +#define __NR_setdomainname (__NR_SYSCALL_BASE+121) +#define __NR_uname (__NR_SYSCALL_BASE+122) + /* 123 was sys_modify_ldt */ +#define __NR_adjtimex (__NR_SYSCALL_BASE+124) +#define __NR_mprotect (__NR_SYSCALL_BASE+125) +#define __NR_sigprocmask (__NR_SYSCALL_BASE+126) + /* 127 was sys_create_module */ +#define __NR_init_module (__NR_SYSCALL_BASE+128) +#define __NR_delete_module (__NR_SYSCALL_BASE+129) + /* 130 was sys_get_kernel_syms */ +#define __NR_quotactl (__NR_SYSCALL_BASE+131) +#define __NR_getpgid (__NR_SYSCALL_BASE+132) +#define __NR_fchdir (__NR_SYSCALL_BASE+133) +#define __NR_bdflush (__NR_SYSCALL_BASE+134) +#define __NR_sysfs (__NR_SYSCALL_BASE+135) +#define __NR_personality (__NR_SYSCALL_BASE+136) + /* 137 was sys_afs_syscall */ +#define __NR_setfsuid (__NR_SYSCALL_BASE+138) +#define __NR_setfsgid (__NR_SYSCALL_BASE+139) +#define __NR__llseek (__NR_SYSCALL_BASE+140) +#define __NR_getdents (__NR_SYSCALL_BASE+141) +#define __NR__newselect (__NR_SYSCALL_BASE+142) +#define __NR_flock (__NR_SYSCALL_BASE+143) +#define __NR_msync (__NR_SYSCALL_BASE+144) +#define __NR_readv (__NR_SYSCALL_BASE+145) +#define __NR_writev (__NR_SYSCALL_BASE+146) +#define __NR_getsid (__NR_SYSCALL_BASE+147) +#define __NR_fdatasync (__NR_SYSCALL_BASE+148) +#define __NR__sysctl (__NR_SYSCALL_BASE+149) +#define __NR_mlock (__NR_SYSCALL_BASE+150) +#define __NR_munlock (__NR_SYSCALL_BASE+151) +#define __NR_mlockall (__NR_SYSCALL_BASE+152) +#define __NR_munlockall (__NR_SYSCALL_BASE+153) +#define __NR_sched_setparam (__NR_SYSCALL_BASE+154) +#define __NR_sched_getparam (__NR_SYSCALL_BASE+155) +#define __NR_sched_setscheduler (__NR_SYSCALL_BASE+156) +#define __NR_sched_getscheduler (__NR_SYSCALL_BASE+157) +#define __NR_sched_yield (__NR_SYSCALL_BASE+158) +#define __NR_sched_get_priority_max (__NR_SYSCALL_BASE+159) +#define __NR_sched_get_priority_min (__NR_SYSCALL_BASE+160) +#define __NR_sched_rr_get_interval (__NR_SYSCALL_BASE+161) +#define __NR_nanosleep (__NR_SYSCALL_BASE+162) +#define __NR_mremap (__NR_SYSCALL_BASE+163) +#define __NR_setresuid (__NR_SYSCALL_BASE+164) +#define __NR_getresuid (__NR_SYSCALL_BASE+165) + /* 166 was sys_vm86 */ + /* 167 was sys_query_module */ +#define __NR_poll (__NR_SYSCALL_BASE+168) +#define __NR_nfsservctl (__NR_SYSCALL_BASE+169) +#define __NR_setresgid (__NR_SYSCALL_BASE+170) +#define __NR_getresgid (__NR_SYSCALL_BASE+171) +#define __NR_prctl (__NR_SYSCALL_BASE+172) +#define __NR_rt_sigreturn (__NR_SYSCALL_BASE+173) +#define __NR_rt_sigaction (__NR_SYSCALL_BASE+174) +#define __NR_rt_sigprocmask (__NR_SYSCALL_BASE+175) +#define __NR_rt_sigpending (__NR_SYSCALL_BASE+176) +#define __NR_rt_sigtimedwait (__NR_SYSCALL_BASE+177) +#define __NR_rt_sigqueueinfo (__NR_SYSCALL_BASE+178) +#define __NR_rt_sigsuspend (__NR_SYSCALL_BASE+179) +#define __NR_pread64 (__NR_SYSCALL_BASE+180) +#define __NR_pwrite64 (__NR_SYSCALL_BASE+181) +#define __NR_chown (__NR_SYSCALL_BASE+182) +#define __NR_getcwd (__NR_SYSCALL_BASE+183) +#define __NR_capget (__NR_SYSCALL_BASE+184) +#define __NR_capset (__NR_SYSCALL_BASE+185) +#define __NR_sigaltstack (__NR_SYSCALL_BASE+186) +#define __NR_sendfile (__NR_SYSCALL_BASE+187) + /* 188 reserved */ + /* 189 reserved */ +#define __NR_vfork (__NR_SYSCALL_BASE+190) +#define __NR_ugetrlimit (__NR_SYSCALL_BASE+191) /* SuS compliant getrlimit */ +#define __NR_mmap2 (__NR_SYSCALL_BASE+192) +#define __NR_truncate64 (__NR_SYSCALL_BASE+193) +#define __NR_ftruncate64 (__NR_SYSCALL_BASE+194) +#define __NR_stat64 (__NR_SYSCALL_BASE+195) +#define __NR_lstat64 (__NR_SYSCALL_BASE+196) +#define __NR_fstat64 (__NR_SYSCALL_BASE+197) +#define __NR_lchown32 (__NR_SYSCALL_BASE+198) +#define __NR_getuid32 (__NR_SYSCALL_BASE+199) +#define __NR_getgid32 (__NR_SYSCALL_BASE+200) +#define __NR_geteuid32 (__NR_SYSCALL_BASE+201) +#define __NR_getegid32 (__NR_SYSCALL_BASE+202) +#define __NR_setreuid32 (__NR_SYSCALL_BASE+203) +#define __NR_setregid32 (__NR_SYSCALL_BASE+204) +#define __NR_getgroups32 (__NR_SYSCALL_BASE+205) +#define __NR_setgroups32 (__NR_SYSCALL_BASE+206) +#define __NR_fchown32 (__NR_SYSCALL_BASE+207) +#define __NR_setresuid32 (__NR_SYSCALL_BASE+208) +#define __NR_getresuid32 (__NR_SYSCALL_BASE+209) +#define __NR_setresgid32 (__NR_SYSCALL_BASE+210) +#define __NR_getresgid32 (__NR_SYSCALL_BASE+211) +#define __NR_chown32 (__NR_SYSCALL_BASE+212) +#define __NR_setuid32 (__NR_SYSCALL_BASE+213) +#define __NR_setgid32 (__NR_SYSCALL_BASE+214) +#define __NR_setfsuid32 (__NR_SYSCALL_BASE+215) +#define __NR_setfsgid32 (__NR_SYSCALL_BASE+216) +#define __NR_getdents64 (__NR_SYSCALL_BASE+217) +#define __NR_pivot_root (__NR_SYSCALL_BASE+218) +#define __NR_mincore (__NR_SYSCALL_BASE+219) +#define __NR_madvise (__NR_SYSCALL_BASE+220) +#define __NR_fcntl64 (__NR_SYSCALL_BASE+221) + /* 222 for tux */ + /* 223 is unused */ +#define __NR_gettid (__NR_SYSCALL_BASE+224) +#define __NR_readahead (__NR_SYSCALL_BASE+225) +#define __NR_setxattr (__NR_SYSCALL_BASE+226) +#define __NR_lsetxattr (__NR_SYSCALL_BASE+227) +#define __NR_fsetxattr (__NR_SYSCALL_BASE+228) +#define __NR_getxattr (__NR_SYSCALL_BASE+229) +#define __NR_lgetxattr (__NR_SYSCALL_BASE+230) +#define __NR_fgetxattr (__NR_SYSCALL_BASE+231) +#define __NR_listxattr (__NR_SYSCALL_BASE+232) +#define __NR_llistxattr (__NR_SYSCALL_BASE+233) +#define __NR_flistxattr (__NR_SYSCALL_BASE+234) +#define __NR_removexattr (__NR_SYSCALL_BASE+235) +#define __NR_lremovexattr (__NR_SYSCALL_BASE+236) +#define __NR_fremovexattr (__NR_SYSCALL_BASE+237) +#define __NR_tkill (__NR_SYSCALL_BASE+238) +#define __NR_sendfile64 (__NR_SYSCALL_BASE+239) +#define __NR_futex (__NR_SYSCALL_BASE+240) +#define __NR_sched_setaffinity (__NR_SYSCALL_BASE+241) +#define __NR_sched_getaffinity (__NR_SYSCALL_BASE+242) +#define __NR_io_setup (__NR_SYSCALL_BASE+243) +#define __NR_io_destroy (__NR_SYSCALL_BASE+244) +#define __NR_io_getevents (__NR_SYSCALL_BASE+245) +#define __NR_io_submit (__NR_SYSCALL_BASE+246) +#define __NR_io_cancel (__NR_SYSCALL_BASE+247) +#define __NR_exit_group (__NR_SYSCALL_BASE+248) +#define __NR_lookup_dcookie (__NR_SYSCALL_BASE+249) +#define __NR_epoll_create (__NR_SYSCALL_BASE+250) +#define __NR_epoll_ctl (__NR_SYSCALL_BASE+251) +#define __NR_epoll_wait (__NR_SYSCALL_BASE+252) +#define __NR_remap_file_pages (__NR_SYSCALL_BASE+253) + /* 254 for set_thread_area */ + /* 255 for get_thread_area */ +#define __NR_set_tid_address (__NR_SYSCALL_BASE+256) +#define __NR_timer_create (__NR_SYSCALL_BASE+257) +#define __NR_timer_settime (__NR_SYSCALL_BASE+258) +#define __NR_timer_gettime (__NR_SYSCALL_BASE+259) +#define __NR_timer_getoverrun (__NR_SYSCALL_BASE+260) +#define __NR_timer_delete (__NR_SYSCALL_BASE+261) +#define __NR_clock_settime (__NR_SYSCALL_BASE+262) +#define __NR_clock_gettime (__NR_SYSCALL_BASE+263) +#define __NR_clock_getres (__NR_SYSCALL_BASE+264) +#define __NR_clock_nanosleep (__NR_SYSCALL_BASE+265) +#define __NR_statfs64 (__NR_SYSCALL_BASE+266) +#define __NR_fstatfs64 (__NR_SYSCALL_BASE+267) +#define __NR_tgkill (__NR_SYSCALL_BASE+268) +#define __NR_utimes (__NR_SYSCALL_BASE+269) +#define __NR_arm_fadvise64_64 (__NR_SYSCALL_BASE+270) +#define __NR_pciconfig_iobase (__NR_SYSCALL_BASE+271) +#define __NR_pciconfig_read (__NR_SYSCALL_BASE+272) +#define __NR_pciconfig_write (__NR_SYSCALL_BASE+273) +#define __NR_mq_open (__NR_SYSCALL_BASE+274) +#define __NR_mq_unlink (__NR_SYSCALL_BASE+275) +#define __NR_mq_timedsend (__NR_SYSCALL_BASE+276) +#define __NR_mq_timedreceive (__NR_SYSCALL_BASE+277) +#define __NR_mq_notify (__NR_SYSCALL_BASE+278) +#define __NR_mq_getsetattr (__NR_SYSCALL_BASE+279) +#define __NR_waitid (__NR_SYSCALL_BASE+280) +#define __NR_socket (__NR_SYSCALL_BASE+281) +#define __NR_bind (__NR_SYSCALL_BASE+282) +#define __NR_connect (__NR_SYSCALL_BASE+283) +#define __NR_listen (__NR_SYSCALL_BASE+284) +#define __NR_accept (__NR_SYSCALL_BASE+285) +#define __NR_getsockname (__NR_SYSCALL_BASE+286) +#define __NR_getpeername (__NR_SYSCALL_BASE+287) +#define __NR_socketpair (__NR_SYSCALL_BASE+288) +#define __NR_send (__NR_SYSCALL_BASE+289) +#define __NR_sendto (__NR_SYSCALL_BASE+290) +#define __NR_recv (__NR_SYSCALL_BASE+291) +#define __NR_recvfrom (__NR_SYSCALL_BASE+292) +#define __NR_shutdown (__NR_SYSCALL_BASE+293) +#define __NR_setsockopt (__NR_SYSCALL_BASE+294) +#define __NR_getsockopt (__NR_SYSCALL_BASE+295) +#define __NR_sendmsg (__NR_SYSCALL_BASE+296) +#define __NR_recvmsg (__NR_SYSCALL_BASE+297) +#define __NR_semop (__NR_SYSCALL_BASE+298) +#define __NR_semget (__NR_SYSCALL_BASE+299) +#define __NR_semctl (__NR_SYSCALL_BASE+300) +#define __NR_msgsnd (__NR_SYSCALL_BASE+301) +#define __NR_msgrcv (__NR_SYSCALL_BASE+302) +#define __NR_msgget (__NR_SYSCALL_BASE+303) +#define __NR_msgctl (__NR_SYSCALL_BASE+304) +#define __NR_shmat (__NR_SYSCALL_BASE+305) +#define __NR_shmdt (__NR_SYSCALL_BASE+306) +#define __NR_shmget (__NR_SYSCALL_BASE+307) +#define __NR_shmctl (__NR_SYSCALL_BASE+308) +#define __NR_add_key (__NR_SYSCALL_BASE+309) +#define __NR_request_key (__NR_SYSCALL_BASE+310) +#define __NR_keyctl (__NR_SYSCALL_BASE+311) +#define __NR_semtimedop (__NR_SYSCALL_BASE+312) +#define __NR_vserver (__NR_SYSCALL_BASE+313) +#define __NR_ioprio_set (__NR_SYSCALL_BASE+314) +#define __NR_ioprio_get (__NR_SYSCALL_BASE+315) +#define __NR_inotify_init (__NR_SYSCALL_BASE+316) +#define __NR_inotify_add_watch (__NR_SYSCALL_BASE+317) +#define __NR_inotify_rm_watch (__NR_SYSCALL_BASE+318) +#define __NR_mbind (__NR_SYSCALL_BASE+319) +#define __NR_get_mempolicy (__NR_SYSCALL_BASE+320) +#define __NR_set_mempolicy (__NR_SYSCALL_BASE+321) +#define __NR_openat (__NR_SYSCALL_BASE+322) +#define __NR_mkdirat (__NR_SYSCALL_BASE+323) +#define __NR_mknodat (__NR_SYSCALL_BASE+324) +#define __NR_fchownat (__NR_SYSCALL_BASE+325) +#define __NR_futimesat (__NR_SYSCALL_BASE+326) +#define __NR_fstatat64 (__NR_SYSCALL_BASE+327) +#define __NR_unlinkat (__NR_SYSCALL_BASE+328) +#define __NR_renameat (__NR_SYSCALL_BASE+329) +#define __NR_linkat (__NR_SYSCALL_BASE+330) +#define __NR_symlinkat (__NR_SYSCALL_BASE+331) +#define __NR_readlinkat (__NR_SYSCALL_BASE+332) +#define __NR_fchmodat (__NR_SYSCALL_BASE+333) +#define __NR_faccessat (__NR_SYSCALL_BASE+334) +#define __NR_pselect6 (__NR_SYSCALL_BASE+335) +#define __NR_ppoll (__NR_SYSCALL_BASE+336) +#define __NR_unshare (__NR_SYSCALL_BASE+337) +#define __NR_set_robust_list (__NR_SYSCALL_BASE+338) +#define __NR_get_robust_list (__NR_SYSCALL_BASE+339) +#define __NR_splice (__NR_SYSCALL_BASE+340) +#define __NR_arm_sync_file_range (__NR_SYSCALL_BASE+341) +#define __NR_sync_file_range2 __NR_arm_sync_file_range +#define __NR_tee (__NR_SYSCALL_BASE+342) +#define __NR_vmsplice (__NR_SYSCALL_BASE+343) +#define __NR_move_pages (__NR_SYSCALL_BASE+344) +#define __NR_getcpu (__NR_SYSCALL_BASE+345) +#define __NR_epoll_pwait (__NR_SYSCALL_BASE+346) +#define __NR_kexec_load (__NR_SYSCALL_BASE+347) +#define __NR_utimensat (__NR_SYSCALL_BASE+348) +#define __NR_signalfd (__NR_SYSCALL_BASE+349) +#define __NR_timerfd_create (__NR_SYSCALL_BASE+350) +#define __NR_eventfd (__NR_SYSCALL_BASE+351) +#define __NR_fallocate (__NR_SYSCALL_BASE+352) +#define __NR_timerfd_settime (__NR_SYSCALL_BASE+353) +#define __NR_timerfd_gettime (__NR_SYSCALL_BASE+354) +#define __NR_signalfd4 (__NR_SYSCALL_BASE+355) +#define __NR_eventfd2 (__NR_SYSCALL_BASE+356) +#define __NR_epoll_create1 (__NR_SYSCALL_BASE+357) +#define __NR_dup3 (__NR_SYSCALL_BASE+358) +#define __NR_pipe2 (__NR_SYSCALL_BASE+359) +#define __NR_inotify_init1 (__NR_SYSCALL_BASE+360) +#define __NR_preadv (__NR_SYSCALL_BASE+361) +#define __NR_pwritev (__NR_SYSCALL_BASE+362) +#define __NR_rt_tgsigqueueinfo (__NR_SYSCALL_BASE+363) +#define __NR_perf_event_open (__NR_SYSCALL_BASE+364) +#define __NR_recvmmsg (__NR_SYSCALL_BASE+365) +#define __NR_accept4 (__NR_SYSCALL_BASE+366) +#define __NR_fanotify_init (__NR_SYSCALL_BASE+367) +#define __NR_fanotify_mark (__NR_SYSCALL_BASE+368) +#define __NR_prlimit64 (__NR_SYSCALL_BASE+369) +#define __NR_name_to_handle_at (__NR_SYSCALL_BASE+370) +#define __NR_open_by_handle_at (__NR_SYSCALL_BASE+371) +#define __NR_clock_adjtime (__NR_SYSCALL_BASE+372) +#define __NR_syncfs (__NR_SYSCALL_BASE+373) +#define __NR_sendmmsg (__NR_SYSCALL_BASE+374) +#define __NR_setns (__NR_SYSCALL_BASE+375) +#define __NR_process_vm_readv (__NR_SYSCALL_BASE+376) +#define __NR_process_vm_writev (__NR_SYSCALL_BASE+377) +#define __NR_kcmp (__NR_SYSCALL_BASE+378) +#define __NR_finit_module (__NR_SYSCALL_BASE+379) +#define __NR_sched_setattr (__NR_SYSCALL_BASE+380) +#define __NR_sched_getattr (__NR_SYSCALL_BASE+381) +#define __NR_renameat2 (__NR_SYSCALL_BASE+382) +#define __NR_seccomp (__NR_SYSCALL_BASE+383) +#define __NR_getrandom (__NR_SYSCALL_BASE+384) +#define __NR_memfd_create (__NR_SYSCALL_BASE+385) +#define __NR_bpf (__NR_SYSCALL_BASE+386) +#define __NR_execveat (__NR_SYSCALL_BASE+387) +#define __NR_userfaultfd (__NR_SYSCALL_BASE+388) +#define __NR_membarrier (__NR_SYSCALL_BASE+389) +#define __NR_mlock2 (__NR_SYSCALL_BASE+390) +#define __NR_copy_file_range (__NR_SYSCALL_BASE+391) + +/* + * The following SWIs are ARM private. + */ +#define __ARM_NR_BASE (__NR_SYSCALL_BASE+0x0f0000) +#define __ARM_NR_breakpoint (__ARM_NR_BASE+1) +#define __ARM_NR_cacheflush (__ARM_NR_BASE+2) +#define __ARM_NR_usr26 (__ARM_NR_BASE+3) +#define __ARM_NR_usr32 (__ARM_NR_BASE+4) +#define __ARM_NR_set_tls (__ARM_NR_BASE+5) + +/* + * The following syscalls are obsolete and no longer available for EABI. + */ +#if defined(__ARM_EABI__) +#undef __NR_time +#undef __NR_umount +#undef __NR_stime +#undef __NR_alarm +#undef __NR_utime +#undef __NR_getrlimit +#undef __NR_select +#undef __NR_readdir +#undef __NR_mmap +#undef __NR_socketcall +#undef __NR_syscall +#undef __NR_ipc +#endif + +#endif /* __ASM_ARM_UNISTD_H */ diff --git a/qemu/linux-headers/asm-arm64/kvm.h b/qemu/linux-headers/asm-arm64/kvm.h index c8abf257c..7d82d1f9d 100644 --- a/qemu/linux-headers/asm-arm64/kvm.h +++ b/qemu/linux-headers/asm-arm64/kvm.h @@ -32,7 +32,7 @@ #ifndef __ASSEMBLY__ #include -#include +#include #include #define __KVM_HAVE_GUEST_DEBUG @@ -53,14 +53,20 @@ struct kvm_regs { struct user_fpsimd_state fp_regs; }; -/* Supported Processor Types */ +/* + * Supported CPU Targets - Adding a new target type is not recommended, + * unless there are some special registers not supported by the + * genericv8 syreg table. + */ #define KVM_ARM_TARGET_AEM_V8 0 #define KVM_ARM_TARGET_FOUNDATION_V8 1 #define KVM_ARM_TARGET_CORTEX_A57 2 #define KVM_ARM_TARGET_XGENE_POTENZA 3 #define KVM_ARM_TARGET_CORTEX_A53 4 +/* Generic ARM v8 target */ +#define KVM_ARM_TARGET_GENERIC_V8 5 -#define KVM_ARM_NUM_TARGETS 5 +#define KVM_ARM_NUM_TARGETS 6 /* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */ #define KVM_ARM_DEVICE_TYPE_SHIFT 0 @@ -88,6 +94,7 @@ struct kvm_regs { #define KVM_ARM_VCPU_POWER_OFF 0 /* CPU is started in OFF state */ #define KVM_ARM_VCPU_EL1_32BIT 1 /* CPU running a 32bit VM */ #define KVM_ARM_VCPU_PSCI_0_2 2 /* CPU uses PSCI v0.2 */ +#define KVM_ARM_VCPU_PMU_V3 3 /* Support guest PMUv3 */ struct kvm_vcpu_init { __u32 target; @@ -100,12 +107,39 @@ struct kvm_sregs { struct kvm_fpu { }; +/* + * See v8 ARM ARM D7.3: Debug Registers + * + * The architectural limit is 16 debug registers of each type although + * in practice there are usually less (see ID_AA64DFR0_EL1). + * + * Although the control registers are architecturally defined as 32 + * bits wide we use a 64 bit structure here to keep parity with + * KVM_GET/SET_ONE_REG behaviour which treats all system registers as + * 64 bit values. It also allows for the possibility of the + * architecture expanding the control registers without having to + * change the userspace ABI. + */ +#define KVM_ARM_MAX_DBG_REGS 16 struct kvm_guest_debug_arch { + __u64 dbg_bcr[KVM_ARM_MAX_DBG_REGS]; + __u64 dbg_bvr[KVM_ARM_MAX_DBG_REGS]; + __u64 dbg_wcr[KVM_ARM_MAX_DBG_REGS]; + __u64 dbg_wvr[KVM_ARM_MAX_DBG_REGS]; }; struct kvm_debug_exit_arch { + __u32 hsr; + __u64 far; /* used for watchpoints */ }; +/* + * Architecture specific defines for kvm_guest_debug->control + */ + +#define KVM_GUESTDBG_USE_SW_BP (1 << 16) +#define KVM_GUESTDBG_USE_HW (1 << 17) + struct kvm_sync_regs { }; @@ -171,6 +205,11 @@ struct kvm_arch_memory_slot { #define KVM_DEV_ARM_VGIC_GRP_CTRL 4 #define KVM_DEV_ARM_VGIC_CTRL_INIT 0 +/* Device Control API on vcpu fd */ +#define KVM_ARM_VCPU_PMU_V3_CTRL 0 +#define KVM_ARM_VCPU_PMU_V3_IRQ 0 +#define KVM_ARM_VCPU_PMU_V3_INIT 1 + /* KVM_IRQ_LINE irq field index values */ #define KVM_ARM_IRQ_TYPE_SHIFT 24 #define KVM_ARM_IRQ_TYPE_MASK 0xff diff --git a/qemu/linux-headers/asm-arm64/unistd.h b/qemu/linux-headers/asm-arm64/unistd.h new file mode 100644 index 000000000..1caadc24e --- /dev/null +++ b/qemu/linux-headers/asm-arm64/unistd.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2012 ARM Ltd. + * + * 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. + * + * 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 . + */ +#include diff --git a/qemu/linux-headers/asm-mips/unistd.h b/qemu/linux-headers/asm-mips/unistd.h new file mode 100644 index 000000000..2a2020938 --- /dev/null +++ b/qemu/linux-headers/asm-mips/unistd.h @@ -0,0 +1,1063 @@ +/* + * 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) 1995, 96, 97, 98, 99, 2000 by Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * + * Changed system calls macros _syscall5 - _syscall7 to push args 5 to 7 onto + * the stack. Robin Farine for ACN S.A, Copyright (C) 1996 by ACN S.A + */ +#ifndef _ASM_UNISTD_H +#define _ASM_UNISTD_H + +#include + +#if _MIPS_SIM == _MIPS_SIM_ABI32 + +/* + * Linux o32 style syscalls are in the range from 4000 to 4999. + */ +#define __NR_Linux 4000 +#define __NR_syscall (__NR_Linux + 0) +#define __NR_exit (__NR_Linux + 1) +#define __NR_fork (__NR_Linux + 2) +#define __NR_read (__NR_Linux + 3) +#define __NR_write (__NR_Linux + 4) +#define __NR_open (__NR_Linux + 5) +#define __NR_close (__NR_Linux + 6) +#define __NR_waitpid (__NR_Linux + 7) +#define __NR_creat (__NR_Linux + 8) +#define __NR_link (__NR_Linux + 9) +#define __NR_unlink (__NR_Linux + 10) +#define __NR_execve (__NR_Linux + 11) +#define __NR_chdir (__NR_Linux + 12) +#define __NR_time (__NR_Linux + 13) +#define __NR_mknod (__NR_Linux + 14) +#define __NR_chmod (__NR_Linux + 15) +#define __NR_lchown (__NR_Linux + 16) +#define __NR_break (__NR_Linux + 17) +#define __NR_unused18 (__NR_Linux + 18) +#define __NR_lseek (__NR_Linux + 19) +#define __NR_getpid (__NR_Linux + 20) +#define __NR_mount (__NR_Linux + 21) +#define __NR_umount (__NR_Linux + 22) +#define __NR_setuid (__NR_Linux + 23) +#define __NR_getuid (__NR_Linux + 24) +#define __NR_stime (__NR_Linux + 25) +#define __NR_ptrace (__NR_Linux + 26) +#define __NR_alarm (__NR_Linux + 27) +#define __NR_unused28 (__NR_Linux + 28) +#define __NR_pause (__NR_Linux + 29) +#define __NR_utime (__NR_Linux + 30) +#define __NR_stty (__NR_Linux + 31) +#define __NR_gtty (__NR_Linux + 32) +#define __NR_access (__NR_Linux + 33) +#define __NR_nice (__NR_Linux + 34) +#define __NR_ftime (__NR_Linux + 35) +#define __NR_sync (__NR_Linux + 36) +#define __NR_kill (__NR_Linux + 37) +#define __NR_rename (__NR_Linux + 38) +#define __NR_mkdir (__NR_Linux + 39) +#define __NR_rmdir (__NR_Linux + 40) +#define __NR_dup (__NR_Linux + 41) +#define __NR_pipe (__NR_Linux + 42) +#define __NR_times (__NR_Linux + 43) +#define __NR_prof (__NR_Linux + 44) +#define __NR_brk (__NR_Linux + 45) +#define __NR_setgid (__NR_Linux + 46) +#define __NR_getgid (__NR_Linux + 47) +#define __NR_signal (__NR_Linux + 48) +#define __NR_geteuid (__NR_Linux + 49) +#define __NR_getegid (__NR_Linux + 50) +#define __NR_acct (__NR_Linux + 51) +#define __NR_umount2 (__NR_Linux + 52) +#define __NR_lock (__NR_Linux + 53) +#define __NR_ioctl (__NR_Linux + 54) +#define __NR_fcntl (__NR_Linux + 55) +#define __NR_mpx (__NR_Linux + 56) +#define __NR_setpgid (__NR_Linux + 57) +#define __NR_ulimit (__NR_Linux + 58) +#define __NR_unused59 (__NR_Linux + 59) +#define __NR_umask (__NR_Linux + 60) +#define __NR_chroot (__NR_Linux + 61) +#define __NR_ustat (__NR_Linux + 62) +#define __NR_dup2 (__NR_Linux + 63) +#define __NR_getppid (__NR_Linux + 64) +#define __NR_getpgrp (__NR_Linux + 65) +#define __NR_setsid (__NR_Linux + 66) +#define __NR_sigaction (__NR_Linux + 67) +#define __NR_sgetmask (__NR_Linux + 68) +#define __NR_ssetmask (__NR_Linux + 69) +#define __NR_setreuid (__NR_Linux + 70) +#define __NR_setregid (__NR_Linux + 71) +#define __NR_sigsuspend (__NR_Linux + 72) +#define __NR_sigpending (__NR_Linux + 73) +#define __NR_sethostname (__NR_Linux + 74) +#define __NR_setrlimit (__NR_Linux + 75) +#define __NR_getrlimit (__NR_Linux + 76) +#define __NR_getrusage (__NR_Linux + 77) +#define __NR_gettimeofday (__NR_Linux + 78) +#define __NR_settimeofday (__NR_Linux + 79) +#define __NR_getgroups (__NR_Linux + 80) +#define __NR_setgroups (__NR_Linux + 81) +#define __NR_reserved82 (__NR_Linux + 82) +#define __NR_symlink (__NR_Linux + 83) +#define __NR_unused84 (__NR_Linux + 84) +#define __NR_readlink (__NR_Linux + 85) +#define __NR_uselib (__NR_Linux + 86) +#define __NR_swapon (__NR_Linux + 87) +#define __NR_reboot (__NR_Linux + 88) +#define __NR_readdir (__NR_Linux + 89) +#define __NR_mmap (__NR_Linux + 90) +#define __NR_munmap (__NR_Linux + 91) +#define __NR_truncate (__NR_Linux + 92) +#define __NR_ftruncate (__NR_Linux + 93) +#define __NR_fchmod (__NR_Linux + 94) +#define __NR_fchown (__NR_Linux + 95) +#define __NR_getpriority (__NR_Linux + 96) +#define __NR_setpriority (__NR_Linux + 97) +#define __NR_profil (__NR_Linux + 98) +#define __NR_statfs (__NR_Linux + 99) +#define __NR_fstatfs (__NR_Linux + 100) +#define __NR_ioperm (__NR_Linux + 101) +#define __NR_socketcall (__NR_Linux + 102) +#define __NR_syslog (__NR_Linux + 103) +#define __NR_setitimer (__NR_Linux + 104) +#define __NR_getitimer (__NR_Linux + 105) +#define __NR_stat (__NR_Linux + 106) +#define __NR_lstat (__NR_Linux + 107) +#define __NR_fstat (__NR_Linux + 108) +#define __NR_unused109 (__NR_Linux + 109) +#define __NR_iopl (__NR_Linux + 110) +#define __NR_vhangup (__NR_Linux + 111) +#define __NR_idle (__NR_Linux + 112) +#define __NR_vm86 (__NR_Linux + 113) +#define __NR_wait4 (__NR_Linux + 114) +#define __NR_swapoff (__NR_Linux + 115) +#define __NR_sysinfo (__NR_Linux + 116) +#define __NR_ipc (__NR_Linux + 117) +#define __NR_fsync (__NR_Linux + 118) +#define __NR_sigreturn (__NR_Linux + 119) +#define __NR_clone (__NR_Linux + 120) +#define __NR_setdomainname (__NR_Linux + 121) +#define __NR_uname (__NR_Linux + 122) +#define __NR_modify_ldt (__NR_Linux + 123) +#define __NR_adjtimex (__NR_Linux + 124) +#define __NR_mprotect (__NR_Linux + 125) +#define __NR_sigprocmask (__NR_Linux + 126) +#define __NR_create_module (__NR_Linux + 127) +#define __NR_init_module (__NR_Linux + 128) +#define __NR_delete_module (__NR_Linux + 129) +#define __NR_get_kernel_syms (__NR_Linux + 130) +#define __NR_quotactl (__NR_Linux + 131) +#define __NR_getpgid (__NR_Linux + 132) +#define __NR_fchdir (__NR_Linux + 133) +#define __NR_bdflush (__NR_Linux + 134) +#define __NR_sysfs (__NR_Linux + 135) +#define __NR_personality (__NR_Linux + 136) +#define __NR_afs_syscall (__NR_Linux + 137) /* Syscall for Andrew File System */ +#define __NR_setfsuid (__NR_Linux + 138) +#define __NR_setfsgid (__NR_Linux + 139) +#define __NR__llseek (__NR_Linux + 140) +#define __NR_getdents (__NR_Linux + 141) +#define __NR__newselect (__NR_Linux + 142) +#define __NR_flock (__NR_Linux + 143) +#define __NR_msync (__NR_Linux + 144) +#define __NR_readv (__NR_Linux + 145) +#define __NR_writev (__NR_Linux + 146) +#define __NR_cacheflush (__NR_Linux + 147) +#define __NR_cachectl (__NR_Linux + 148) +#define __NR_sysmips (__NR_Linux + 149) +#define __NR_unused150 (__NR_Linux + 150) +#define __NR_getsid (__NR_Linux + 151) +#define __NR_fdatasync (__NR_Linux + 152) +#define __NR__sysctl (__NR_Linux + 153) +#define __NR_mlock (__NR_Linux + 154) +#define __NR_munlock (__NR_Linux + 155) +#define __NR_mlockall (__NR_Linux + 156) +#define __NR_munlockall (__NR_Linux + 157) +#define __NR_sched_setparam (__NR_Linux + 158) +#define __NR_sched_getparam (__NR_Linux + 159) +#define __NR_sched_setscheduler (__NR_Linux + 160) +#define __NR_sched_getscheduler (__NR_Linux + 161) +#define __NR_sched_yield (__NR_Linux + 162) +#define __NR_sched_get_priority_max (__NR_Linux + 163) +#define __NR_sched_get_priority_min (__NR_Linux + 164) +#define __NR_sched_rr_get_interval (__NR_Linux + 165) +#define __NR_nanosleep (__NR_Linux + 166) +#define __NR_mremap (__NR_Linux + 167) +#define __NR_accept (__NR_Linux + 168) +#define __NR_bind (__NR_Linux + 169) +#define __NR_connect (__NR_Linux + 170) +#define __NR_getpeername (__NR_Linux + 171) +#define __NR_getsockname (__NR_Linux + 172) +#define __NR_getsockopt (__NR_Linux + 173) +#define __NR_listen (__NR_Linux + 174) +#define __NR_recv (__NR_Linux + 175) +#define __NR_recvfrom (__NR_Linux + 176) +#define __NR_recvmsg (__NR_Linux + 177) +#define __NR_send (__NR_Linux + 178) +#define __NR_sendmsg (__NR_Linux + 179) +#define __NR_sendto (__NR_Linux + 180) +#define __NR_setsockopt (__NR_Linux + 181) +#define __NR_shutdown (__NR_Linux + 182) +#define __NR_socket (__NR_Linux + 183) +#define __NR_socketpair (__NR_Linux + 184) +#define __NR_setresuid (__NR_Linux + 185) +#define __NR_getresuid (__NR_Linux + 186) +#define __NR_query_module (__NR_Linux + 187) +#define __NR_poll (__NR_Linux + 188) +#define __NR_nfsservctl (__NR_Linux + 189) +#define __NR_setresgid (__NR_Linux + 190) +#define __NR_getresgid (__NR_Linux + 191) +#define __NR_prctl (__NR_Linux + 192) +#define __NR_rt_sigreturn (__NR_Linux + 193) +#define __NR_rt_sigaction (__NR_Linux + 194) +#define __NR_rt_sigprocmask (__NR_Linux + 195) +#define __NR_rt_sigpending (__NR_Linux + 196) +#define __NR_rt_sigtimedwait (__NR_Linux + 197) +#define __NR_rt_sigqueueinfo (__NR_Linux + 198) +#define __NR_rt_sigsuspend (__NR_Linux + 199) +#define __NR_pread64 (__NR_Linux + 200) +#define __NR_pwrite64 (__NR_Linux + 201) +#define __NR_chown (__NR_Linux + 202) +#define __NR_getcwd (__NR_Linux + 203) +#define __NR_capget (__NR_Linux + 204) +#define __NR_capset (__NR_Linux + 205) +#define __NR_sigaltstack (__NR_Linux + 206) +#define __NR_sendfile (__NR_Linux + 207) +#define __NR_getpmsg (__NR_Linux + 208) +#define __NR_putpmsg (__NR_Linux + 209) +#define __NR_mmap2 (__NR_Linux + 210) +#define __NR_truncate64 (__NR_Linux + 211) +#define __NR_ftruncate64 (__NR_Linux + 212) +#define __NR_stat64 (__NR_Linux + 213) +#define __NR_lstat64 (__NR_Linux + 214) +#define __NR_fstat64 (__NR_Linux + 215) +#define __NR_pivot_root (__NR_Linux + 216) +#define __NR_mincore (__NR_Linux + 217) +#define __NR_madvise (__NR_Linux + 218) +#define __NR_getdents64 (__NR_Linux + 219) +#define __NR_fcntl64 (__NR_Linux + 220) +#define __NR_reserved221 (__NR_Linux + 221) +#define __NR_gettid (__NR_Linux + 222) +#define __NR_readahead (__NR_Linux + 223) +#define __NR_setxattr (__NR_Linux + 224) +#define __NR_lsetxattr (__NR_Linux + 225) +#define __NR_fsetxattr (__NR_Linux + 226) +#define __NR_getxattr (__NR_Linux + 227) +#define __NR_lgetxattr (__NR_Linux + 228) +#define __NR_fgetxattr (__NR_Linux + 229) +#define __NR_listxattr (__NR_Linux + 230) +#define __NR_llistxattr (__NR_Linux + 231) +#define __NR_flistxattr (__NR_Linux + 232) +#define __NR_removexattr (__NR_Linux + 233) +#define __NR_lremovexattr (__NR_Linux + 234) +#define __NR_fremovexattr (__NR_Linux + 235) +#define __NR_tkill (__NR_Linux + 236) +#define __NR_sendfile64 (__NR_Linux + 237) +#define __NR_futex (__NR_Linux + 238) +#define __NR_sched_setaffinity (__NR_Linux + 239) +#define __NR_sched_getaffinity (__NR_Linux + 240) +#define __NR_io_setup (__NR_Linux + 241) +#define __NR_io_destroy (__NR_Linux + 242) +#define __NR_io_getevents (__NR_Linux + 243) +#define __NR_io_submit (__NR_Linux + 244) +#define __NR_io_cancel (__NR_Linux + 245) +#define __NR_exit_group (__NR_Linux + 246) +#define __NR_lookup_dcookie (__NR_Linux + 247) +#define __NR_epoll_create (__NR_Linux + 248) +#define __NR_epoll_ctl (__NR_Linux + 249) +#define __NR_epoll_wait (__NR_Linux + 250) +#define __NR_remap_file_pages (__NR_Linux + 251) +#define __NR_set_tid_address (__NR_Linux + 252) +#define __NR_restart_syscall (__NR_Linux + 253) +#define __NR_fadvise64 (__NR_Linux + 254) +#define __NR_statfs64 (__NR_Linux + 255) +#define __NR_fstatfs64 (__NR_Linux + 256) +#define __NR_timer_create (__NR_Linux + 257) +#define __NR_timer_settime (__NR_Linux + 258) +#define __NR_timer_gettime (__NR_Linux + 259) +#define __NR_timer_getoverrun (__NR_Linux + 260) +#define __NR_timer_delete (__NR_Linux + 261) +#define __NR_clock_settime (__NR_Linux + 262) +#define __NR_clock_gettime (__NR_Linux + 263) +#define __NR_clock_getres (__NR_Linux + 264) +#define __NR_clock_nanosleep (__NR_Linux + 265) +#define __NR_tgkill (__NR_Linux + 266) +#define __NR_utimes (__NR_Linux + 267) +#define __NR_mbind (__NR_Linux + 268) +#define __NR_get_mempolicy (__NR_Linux + 269) +#define __NR_set_mempolicy (__NR_Linux + 270) +#define __NR_mq_open (__NR_Linux + 271) +#define __NR_mq_unlink (__NR_Linux + 272) +#define __NR_mq_timedsend (__NR_Linux + 273) +#define __NR_mq_timedreceive (__NR_Linux + 274) +#define __NR_mq_notify (__NR_Linux + 275) +#define __NR_mq_getsetattr (__NR_Linux + 276) +#define __NR_vserver (__NR_Linux + 277) +#define __NR_waitid (__NR_Linux + 278) +/* #define __NR_sys_setaltroot (__NR_Linux + 279) */ +#define __NR_add_key (__NR_Linux + 280) +#define __NR_request_key (__NR_Linux + 281) +#define __NR_keyctl (__NR_Linux + 282) +#define __NR_set_thread_area (__NR_Linux + 283) +#define __NR_inotify_init (__NR_Linux + 284) +#define __NR_inotify_add_watch (__NR_Linux + 285) +#define __NR_inotify_rm_watch (__NR_Linux + 286) +#define __NR_migrate_pages (__NR_Linux + 287) +#define __NR_openat (__NR_Linux + 288) +#define __NR_mkdirat (__NR_Linux + 289) +#define __NR_mknodat (__NR_Linux + 290) +#define __NR_fchownat (__NR_Linux + 291) +#define __NR_futimesat (__NR_Linux + 292) +#define __NR_fstatat64 (__NR_Linux + 293) +#define __NR_unlinkat (__NR_Linux + 294) +#define __NR_renameat (__NR_Linux + 295) +#define __NR_linkat (__NR_Linux + 296) +#define __NR_symlinkat (__NR_Linux + 297) +#define __NR_readlinkat (__NR_Linux + 298) +#define __NR_fchmodat (__NR_Linux + 299) +#define __NR_faccessat (__NR_Linux + 300) +#define __NR_pselect6 (__NR_Linux + 301) +#define __NR_ppoll (__NR_Linux + 302) +#define __NR_unshare (__NR_Linux + 303) +#define __NR_splice (__NR_Linux + 304) +#define __NR_sync_file_range (__NR_Linux + 305) +#define __NR_tee (__NR_Linux + 306) +#define __NR_vmsplice (__NR_Linux + 307) +#define __NR_move_pages (__NR_Linux + 308) +#define __NR_set_robust_list (__NR_Linux + 309) +#define __NR_get_robust_list (__NR_Linux + 310) +#define __NR_kexec_load (__NR_Linux + 311) +#define __NR_getcpu (__NR_Linux + 312) +#define __NR_epoll_pwait (__NR_Linux + 313) +#define __NR_ioprio_set (__NR_Linux + 314) +#define __NR_ioprio_get (__NR_Linux + 315) +#define __NR_utimensat (__NR_Linux + 316) +#define __NR_signalfd (__NR_Linux + 317) +#define __NR_timerfd (__NR_Linux + 318) +#define __NR_eventfd (__NR_Linux + 319) +#define __NR_fallocate (__NR_Linux + 320) +#define __NR_timerfd_create (__NR_Linux + 321) +#define __NR_timerfd_gettime (__NR_Linux + 322) +#define __NR_timerfd_settime (__NR_Linux + 323) +#define __NR_signalfd4 (__NR_Linux + 324) +#define __NR_eventfd2 (__NR_Linux + 325) +#define __NR_epoll_create1 (__NR_Linux + 326) +#define __NR_dup3 (__NR_Linux + 327) +#define __NR_pipe2 (__NR_Linux + 328) +#define __NR_inotify_init1 (__NR_Linux + 329) +#define __NR_preadv (__NR_Linux + 330) +#define __NR_pwritev (__NR_Linux + 331) +#define __NR_rt_tgsigqueueinfo (__NR_Linux + 332) +#define __NR_perf_event_open (__NR_Linux + 333) +#define __NR_accept4 (__NR_Linux + 334) +#define __NR_recvmmsg (__NR_Linux + 335) +#define __NR_fanotify_init (__NR_Linux + 336) +#define __NR_fanotify_mark (__NR_Linux + 337) +#define __NR_prlimit64 (__NR_Linux + 338) +#define __NR_name_to_handle_at (__NR_Linux + 339) +#define __NR_open_by_handle_at (__NR_Linux + 340) +#define __NR_clock_adjtime (__NR_Linux + 341) +#define __NR_syncfs (__NR_Linux + 342) +#define __NR_sendmmsg (__NR_Linux + 343) +#define __NR_setns (__NR_Linux + 344) +#define __NR_process_vm_readv (__NR_Linux + 345) +#define __NR_process_vm_writev (__NR_Linux + 346) +#define __NR_kcmp (__NR_Linux + 347) +#define __NR_finit_module (__NR_Linux + 348) +#define __NR_sched_setattr (__NR_Linux + 349) +#define __NR_sched_getattr (__NR_Linux + 350) +#define __NR_renameat2 (__NR_Linux + 351) +#define __NR_seccomp (__NR_Linux + 352) +#define __NR_getrandom (__NR_Linux + 353) +#define __NR_memfd_create (__NR_Linux + 354) +#define __NR_bpf (__NR_Linux + 355) +#define __NR_execveat (__NR_Linux + 356) + +/* + * Offset of the last Linux o32 flavoured syscall + */ +#define __NR_Linux_syscalls 356 + +#endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */ + +#define __NR_O32_Linux 4000 +#define __NR_O32_Linux_syscalls 356 + +#if _MIPS_SIM == _MIPS_SIM_ABI64 + +/* + * Linux 64-bit syscalls are in the range from 5000 to 5999. + */ +#define __NR_Linux 5000 +#define __NR_read (__NR_Linux + 0) +#define __NR_write (__NR_Linux + 1) +#define __NR_open (__NR_Linux + 2) +#define __NR_close (__NR_Linux + 3) +#define __NR_stat (__NR_Linux + 4) +#define __NR_fstat (__NR_Linux + 5) +#define __NR_lstat (__NR_Linux + 6) +#define __NR_poll (__NR_Linux + 7) +#define __NR_lseek (__NR_Linux + 8) +#define __NR_mmap (__NR_Linux + 9) +#define __NR_mprotect (__NR_Linux + 10) +#define __NR_munmap (__NR_Linux + 11) +#define __NR_brk (__NR_Linux + 12) +#define __NR_rt_sigaction (__NR_Linux + 13) +#define __NR_rt_sigprocmask (__NR_Linux + 14) +#define __NR_ioctl (__NR_Linux + 15) +#define __NR_pread64 (__NR_Linux + 16) +#define __NR_pwrite64 (__NR_Linux + 17) +#define __NR_readv (__NR_Linux + 18) +#define __NR_writev (__NR_Linux + 19) +#define __NR_access (__NR_Linux + 20) +#define __NR_pipe (__NR_Linux + 21) +#define __NR__newselect (__NR_Linux + 22) +#define __NR_sched_yield (__NR_Linux + 23) +#define __NR_mremap (__NR_Linux + 24) +#define __NR_msync (__NR_Linux + 25) +#define __NR_mincore (__NR_Linux + 26) +#define __NR_madvise (__NR_Linux + 27) +#define __NR_shmget (__NR_Linux + 28) +#define __NR_shmat (__NR_Linux + 29) +#define __NR_shmctl (__NR_Linux + 30) +#define __NR_dup (__NR_Linux + 31) +#define __NR_dup2 (__NR_Linux + 32) +#define __NR_pause (__NR_Linux + 33) +#define __NR_nanosleep (__NR_Linux + 34) +#define __NR_getitimer (__NR_Linux + 35) +#define __NR_setitimer (__NR_Linux + 36) +#define __NR_alarm (__NR_Linux + 37) +#define __NR_getpid (__NR_Linux + 38) +#define __NR_sendfile (__NR_Linux + 39) +#define __NR_socket (__NR_Linux + 40) +#define __NR_connect (__NR_Linux + 41) +#define __NR_accept (__NR_Linux + 42) +#define __NR_sendto (__NR_Linux + 43) +#define __NR_recvfrom (__NR_Linux + 44) +#define __NR_sendmsg (__NR_Linux + 45) +#define __NR_recvmsg (__NR_Linux + 46) +#define __NR_shutdown (__NR_Linux + 47) +#define __NR_bind (__NR_Linux + 48) +#define __NR_listen (__NR_Linux + 49) +#define __NR_getsockname (__NR_Linux + 50) +#define __NR_getpeername (__NR_Linux + 51) +#define __NR_socketpair (__NR_Linux + 52) +#define __NR_setsockopt (__NR_Linux + 53) +#define __NR_getsockopt (__NR_Linux + 54) +#define __NR_clone (__NR_Linux + 55) +#define __NR_fork (__NR_Linux + 56) +#define __NR_execve (__NR_Linux + 57) +#define __NR_exit (__NR_Linux + 58) +#define __NR_wait4 (__NR_Linux + 59) +#define __NR_kill (__NR_Linux + 60) +#define __NR_uname (__NR_Linux + 61) +#define __NR_semget (__NR_Linux + 62) +#define __NR_semop (__NR_Linux + 63) +#define __NR_semctl (__NR_Linux + 64) +#define __NR_shmdt (__NR_Linux + 65) +#define __NR_msgget (__NR_Linux + 66) +#define __NR_msgsnd (__NR_Linux + 67) +#define __NR_msgrcv (__NR_Linux + 68) +#define __NR_msgctl (__NR_Linux + 69) +#define __NR_fcntl (__NR_Linux + 70) +#define __NR_flock (__NR_Linux + 71) +#define __NR_fsync (__NR_Linux + 72) +#define __NR_fdatasync (__NR_Linux + 73) +#define __NR_truncate (__NR_Linux + 74) +#define __NR_ftruncate (__NR_Linux + 75) +#define __NR_getdents (__NR_Linux + 76) +#define __NR_getcwd (__NR_Linux + 77) +#define __NR_chdir (__NR_Linux + 78) +#define __NR_fchdir (__NR_Linux + 79) +#define __NR_rename (__NR_Linux + 80) +#define __NR_mkdir (__NR_Linux + 81) +#define __NR_rmdir (__NR_Linux + 82) +#define __NR_creat (__NR_Linux + 83) +#define __NR_link (__NR_Linux + 84) +#define __NR_unlink (__NR_Linux + 85) +#define __NR_symlink (__NR_Linux + 86) +#define __NR_readlink (__NR_Linux + 87) +#define __NR_chmod (__NR_Linux + 88) +#define __NR_fchmod (__NR_Linux + 89) +#define __NR_chown (__NR_Linux + 90) +#define __NR_fchown (__NR_Linux + 91) +#define __NR_lchown (__NR_Linux + 92) +#define __NR_umask (__NR_Linux + 93) +#define __NR_gettimeofday (__NR_Linux + 94) +#define __NR_getrlimit (__NR_Linux + 95) +#define __NR_getrusage (__NR_Linux + 96) +#define __NR_sysinfo (__NR_Linux + 97) +#define __NR_times (__NR_Linux + 98) +#define __NR_ptrace (__NR_Linux + 99) +#define __NR_getuid (__NR_Linux + 100) +#define __NR_syslog (__NR_Linux + 101) +#define __NR_getgid (__NR_Linux + 102) +#define __NR_setuid (__NR_Linux + 103) +#define __NR_setgid (__NR_Linux + 104) +#define __NR_geteuid (__NR_Linux + 105) +#define __NR_getegid (__NR_Linux + 106) +#define __NR_setpgid (__NR_Linux + 107) +#define __NR_getppid (__NR_Linux + 108) +#define __NR_getpgrp (__NR_Linux + 109) +#define __NR_setsid (__NR_Linux + 110) +#define __NR_setreuid (__NR_Linux + 111) +#define __NR_setregid (__NR_Linux + 112) +#define __NR_getgroups (__NR_Linux + 113) +#define __NR_setgroups (__NR_Linux + 114) +#define __NR_setresuid (__NR_Linux + 115) +#define __NR_getresuid (__NR_Linux + 116) +#define __NR_setresgid (__NR_Linux + 117) +#define __NR_getresgid (__NR_Linux + 118) +#define __NR_getpgid (__NR_Linux + 119) +#define __NR_setfsuid (__NR_Linux + 120) +#define __NR_setfsgid (__NR_Linux + 121) +#define __NR_getsid (__NR_Linux + 122) +#define __NR_capget (__NR_Linux + 123) +#define __NR_capset (__NR_Linux + 124) +#define __NR_rt_sigpending (__NR_Linux + 125) +#define __NR_rt_sigtimedwait (__NR_Linux + 126) +#define __NR_rt_sigqueueinfo (__NR_Linux + 127) +#define __NR_rt_sigsuspend (__NR_Linux + 128) +#define __NR_sigaltstack (__NR_Linux + 129) +#define __NR_utime (__NR_Linux + 130) +#define __NR_mknod (__NR_Linux + 131) +#define __NR_personality (__NR_Linux + 132) +#define __NR_ustat (__NR_Linux + 133) +#define __NR_statfs (__NR_Linux + 134) +#define __NR_fstatfs (__NR_Linux + 135) +#define __NR_sysfs (__NR_Linux + 136) +#define __NR_getpriority (__NR_Linux + 137) +#define __NR_setpriority (__NR_Linux + 138) +#define __NR_sched_setparam (__NR_Linux + 139) +#define __NR_sched_getparam (__NR_Linux + 140) +#define __NR_sched_setscheduler (__NR_Linux + 141) +#define __NR_sched_getscheduler (__NR_Linux + 142) +#define __NR_sched_get_priority_max (__NR_Linux + 143) +#define __NR_sched_get_priority_min (__NR_Linux + 144) +#define __NR_sched_rr_get_interval (__NR_Linux + 145) +#define __NR_mlock (__NR_Linux + 146) +#define __NR_munlock (__NR_Linux + 147) +#define __NR_mlockall (__NR_Linux + 148) +#define __NR_munlockall (__NR_Linux + 149) +#define __NR_vhangup (__NR_Linux + 150) +#define __NR_pivot_root (__NR_Linux + 151) +#define __NR__sysctl (__NR_Linux + 152) +#define __NR_prctl (__NR_Linux + 153) +#define __NR_adjtimex (__NR_Linux + 154) +#define __NR_setrlimit (__NR_Linux + 155) +#define __NR_chroot (__NR_Linux + 156) +#define __NR_sync (__NR_Linux + 157) +#define __NR_acct (__NR_Linux + 158) +#define __NR_settimeofday (__NR_Linux + 159) +#define __NR_mount (__NR_Linux + 160) +#define __NR_umount2 (__NR_Linux + 161) +#define __NR_swapon (__NR_Linux + 162) +#define __NR_swapoff (__NR_Linux + 163) +#define __NR_reboot (__NR_Linux + 164) +#define __NR_sethostname (__NR_Linux + 165) +#define __NR_setdomainname (__NR_Linux + 166) +#define __NR_create_module (__NR_Linux + 167) +#define __NR_init_module (__NR_Linux + 168) +#define __NR_delete_module (__NR_Linux + 169) +#define __NR_get_kernel_syms (__NR_Linux + 170) +#define __NR_query_module (__NR_Linux + 171) +#define __NR_quotactl (__NR_Linux + 172) +#define __NR_nfsservctl (__NR_Linux + 173) +#define __NR_getpmsg (__NR_Linux + 174) +#define __NR_putpmsg (__NR_Linux + 175) +#define __NR_afs_syscall (__NR_Linux + 176) +#define __NR_reserved177 (__NR_Linux + 177) +#define __NR_gettid (__NR_Linux + 178) +#define __NR_readahead (__NR_Linux + 179) +#define __NR_setxattr (__NR_Linux + 180) +#define __NR_lsetxattr (__NR_Linux + 181) +#define __NR_fsetxattr (__NR_Linux + 182) +#define __NR_getxattr (__NR_Linux + 183) +#define __NR_lgetxattr (__NR_Linux + 184) +#define __NR_fgetxattr (__NR_Linux + 185) +#define __NR_listxattr (__NR_Linux + 186) +#define __NR_llistxattr (__NR_Linux + 187) +#define __NR_flistxattr (__NR_Linux + 188) +#define __NR_removexattr (__NR_Linux + 189) +#define __NR_lremovexattr (__NR_Linux + 190) +#define __NR_fremovexattr (__NR_Linux + 191) +#define __NR_tkill (__NR_Linux + 192) +#define __NR_reserved193 (__NR_Linux + 193) +#define __NR_futex (__NR_Linux + 194) +#define __NR_sched_setaffinity (__NR_Linux + 195) +#define __NR_sched_getaffinity (__NR_Linux + 196) +#define __NR_cacheflush (__NR_Linux + 197) +#define __NR_cachectl (__NR_Linux + 198) +#define __NR_sysmips (__NR_Linux + 199) +#define __NR_io_setup (__NR_Linux + 200) +#define __NR_io_destroy (__NR_Linux + 201) +#define __NR_io_getevents (__NR_Linux + 202) +#define __NR_io_submit (__NR_Linux + 203) +#define __NR_io_cancel (__NR_Linux + 204) +#define __NR_exit_group (__NR_Linux + 205) +#define __NR_lookup_dcookie (__NR_Linux + 206) +#define __NR_epoll_create (__NR_Linux + 207) +#define __NR_epoll_ctl (__NR_Linux + 208) +#define __NR_epoll_wait (__NR_Linux + 209) +#define __NR_remap_file_pages (__NR_Linux + 210) +#define __NR_rt_sigreturn (__NR_Linux + 211) +#define __NR_set_tid_address (__NR_Linux + 212) +#define __NR_restart_syscall (__NR_Linux + 213) +#define __NR_semtimedop (__NR_Linux + 214) +#define __NR_fadvise64 (__NR_Linux + 215) +#define __NR_timer_create (__NR_Linux + 216) +#define __NR_timer_settime (__NR_Linux + 217) +#define __NR_timer_gettime (__NR_Linux + 218) +#define __NR_timer_getoverrun (__NR_Linux + 219) +#define __NR_timer_delete (__NR_Linux + 220) +#define __NR_clock_settime (__NR_Linux + 221) +#define __NR_clock_gettime (__NR_Linux + 222) +#define __NR_clock_getres (__NR_Linux + 223) +#define __NR_clock_nanosleep (__NR_Linux + 224) +#define __NR_tgkill (__NR_Linux + 225) +#define __NR_utimes (__NR_Linux + 226) +#define __NR_mbind (__NR_Linux + 227) +#define __NR_get_mempolicy (__NR_Linux + 228) +#define __NR_set_mempolicy (__NR_Linux + 229) +#define __NR_mq_open (__NR_Linux + 230) +#define __NR_mq_unlink (__NR_Linux + 231) +#define __NR_mq_timedsend (__NR_Linux + 232) +#define __NR_mq_timedreceive (__NR_Linux + 233) +#define __NR_mq_notify (__NR_Linux + 234) +#define __NR_mq_getsetattr (__NR_Linux + 235) +#define __NR_vserver (__NR_Linux + 236) +#define __NR_waitid (__NR_Linux + 237) +/* #define __NR_sys_setaltroot (__NR_Linux + 238) */ +#define __NR_add_key (__NR_Linux + 239) +#define __NR_request_key (__NR_Linux + 240) +#define __NR_keyctl (__NR_Linux + 241) +#define __NR_set_thread_area (__NR_Linux + 242) +#define __NR_inotify_init (__NR_Linux + 243) +#define __NR_inotify_add_watch (__NR_Linux + 244) +#define __NR_inotify_rm_watch (__NR_Linux + 245) +#define __NR_migrate_pages (__NR_Linux + 246) +#define __NR_openat (__NR_Linux + 247) +#define __NR_mkdirat (__NR_Linux + 248) +#define __NR_mknodat (__NR_Linux + 249) +#define __NR_fchownat (__NR_Linux + 250) +#define __NR_futimesat (__NR_Linux + 251) +#define __NR_newfstatat (__NR_Linux + 252) +#define __NR_unlinkat (__NR_Linux + 253) +#define __NR_renameat (__NR_Linux + 254) +#define __NR_linkat (__NR_Linux + 255) +#define __NR_symlinkat (__NR_Linux + 256) +#define __NR_readlinkat (__NR_Linux + 257) +#define __NR_fchmodat (__NR_Linux + 258) +#define __NR_faccessat (__NR_Linux + 259) +#define __NR_pselect6 (__NR_Linux + 260) +#define __NR_ppoll (__NR_Linux + 261) +#define __NR_unshare (__NR_Linux + 262) +#define __NR_splice (__NR_Linux + 263) +#define __NR_sync_file_range (__NR_Linux + 264) +#define __NR_tee (__NR_Linux + 265) +#define __NR_vmsplice (__NR_Linux + 266) +#define __NR_move_pages (__NR_Linux + 267) +#define __NR_set_robust_list (__NR_Linux + 268) +#define __NR_get_robust_list (__NR_Linux + 269) +#define __NR_kexec_load (__NR_Linux + 270) +#define __NR_getcpu (__NR_Linux + 271) +#define __NR_epoll_pwait (__NR_Linux + 272) +#define __NR_ioprio_set (__NR_Linux + 273) +#define __NR_ioprio_get (__NR_Linux + 274) +#define __NR_utimensat (__NR_Linux + 275) +#define __NR_signalfd (__NR_Linux + 276) +#define __NR_timerfd (__NR_Linux + 277) +#define __NR_eventfd (__NR_Linux + 278) +#define __NR_fallocate (__NR_Linux + 279) +#define __NR_timerfd_create (__NR_Linux + 280) +#define __NR_timerfd_gettime (__NR_Linux + 281) +#define __NR_timerfd_settime (__NR_Linux + 282) +#define __NR_signalfd4 (__NR_Linux + 283) +#define __NR_eventfd2 (__NR_Linux + 284) +#define __NR_epoll_create1 (__NR_Linux + 285) +#define __NR_dup3 (__NR_Linux + 286) +#define __NR_pipe2 (__NR_Linux + 287) +#define __NR_inotify_init1 (__NR_Linux + 288) +#define __NR_preadv (__NR_Linux + 289) +#define __NR_pwritev (__NR_Linux + 290) +#define __NR_rt_tgsigqueueinfo (__NR_Linux + 291) +#define __NR_perf_event_open (__NR_Linux + 292) +#define __NR_accept4 (__NR_Linux + 293) +#define __NR_recvmmsg (__NR_Linux + 294) +#define __NR_fanotify_init (__NR_Linux + 295) +#define __NR_fanotify_mark (__NR_Linux + 296) +#define __NR_prlimit64 (__NR_Linux + 297) +#define __NR_name_to_handle_at (__NR_Linux + 298) +#define __NR_open_by_handle_at (__NR_Linux + 299) +#define __NR_clock_adjtime (__NR_Linux + 300) +#define __NR_syncfs (__NR_Linux + 301) +#define __NR_sendmmsg (__NR_Linux + 302) +#define __NR_setns (__NR_Linux + 303) +#define __NR_process_vm_readv (__NR_Linux + 304) +#define __NR_process_vm_writev (__NR_Linux + 305) +#define __NR_kcmp (__NR_Linux + 306) +#define __NR_finit_module (__NR_Linux + 307) +#define __NR_getdents64 (__NR_Linux + 308) +#define __NR_sched_setattr (__NR_Linux + 309) +#define __NR_sched_getattr (__NR_Linux + 310) +#define __NR_renameat2 (__NR_Linux + 311) +#define __NR_seccomp (__NR_Linux + 312) +#define __NR_getrandom (__NR_Linux + 313) +#define __NR_memfd_create (__NR_Linux + 314) +#define __NR_bpf (__NR_Linux + 315) +#define __NR_execveat (__NR_Linux + 316) + +/* + * Offset of the last Linux 64-bit flavoured syscall + */ +#define __NR_Linux_syscalls 316 + +#endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */ + +#define __NR_64_Linux 5000 +#define __NR_64_Linux_syscalls 316 + +#if _MIPS_SIM == _MIPS_SIM_NABI32 + +/* + * Linux N32 syscalls are in the range from 6000 to 6999. + */ +#define __NR_Linux 6000 +#define __NR_read (__NR_Linux + 0) +#define __NR_write (__NR_Linux + 1) +#define __NR_open (__NR_Linux + 2) +#define __NR_close (__NR_Linux + 3) +#define __NR_stat (__NR_Linux + 4) +#define __NR_fstat (__NR_Linux + 5) +#define __NR_lstat (__NR_Linux + 6) +#define __NR_poll (__NR_Linux + 7) +#define __NR_lseek (__NR_Linux + 8) +#define __NR_mmap (__NR_Linux + 9) +#define __NR_mprotect (__NR_Linux + 10) +#define __NR_munmap (__NR_Linux + 11) +#define __NR_brk (__NR_Linux + 12) +#define __NR_rt_sigaction (__NR_Linux + 13) +#define __NR_rt_sigprocmask (__NR_Linux + 14) +#define __NR_ioctl (__NR_Linux + 15) +#define __NR_pread64 (__NR_Linux + 16) +#define __NR_pwrite64 (__NR_Linux + 17) +#define __NR_readv (__NR_Linux + 18) +#define __NR_writev (__NR_Linux + 19) +#define __NR_access (__NR_Linux + 20) +#define __NR_pipe (__NR_Linux + 21) +#define __NR__newselect (__NR_Linux + 22) +#define __NR_sched_yield (__NR_Linux + 23) +#define __NR_mremap (__NR_Linux + 24) +#define __NR_msync (__NR_Linux + 25) +#define __NR_mincore (__NR_Linux + 26) +#define __NR_madvise (__NR_Linux + 27) +#define __NR_shmget (__NR_Linux + 28) +#define __NR_shmat (__NR_Linux + 29) +#define __NR_shmctl (__NR_Linux + 30) +#define __NR_dup (__NR_Linux + 31) +#define __NR_dup2 (__NR_Linux + 32) +#define __NR_pause (__NR_Linux + 33) +#define __NR_nanosleep (__NR_Linux + 34) +#define __NR_getitimer (__NR_Linux + 35) +#define __NR_setitimer (__NR_Linux + 36) +#define __NR_alarm (__NR_Linux + 37) +#define __NR_getpid (__NR_Linux + 38) +#define __NR_sendfile (__NR_Linux + 39) +#define __NR_socket (__NR_Linux + 40) +#define __NR_connect (__NR_Linux + 41) +#define __NR_accept (__NR_Linux + 42) +#define __NR_sendto (__NR_Linux + 43) +#define __NR_recvfrom (__NR_Linux + 44) +#define __NR_sendmsg (__NR_Linux + 45) +#define __NR_recvmsg (__NR_Linux + 46) +#define __NR_shutdown (__NR_Linux + 47) +#define __NR_bind (__NR_Linux + 48) +#define __NR_listen (__NR_Linux + 49) +#define __NR_getsockname (__NR_Linux + 50) +#define __NR_getpeername (__NR_Linux + 51) +#define __NR_socketpair (__NR_Linux + 52) +#define __NR_setsockopt (__NR_Linux + 53) +#define __NR_getsockopt (__NR_Linux + 54) +#define __NR_clone (__NR_Linux + 55) +#define __NR_fork (__NR_Linux + 56) +#define __NR_execve (__NR_Linux + 57) +#define __NR_exit (__NR_Linux + 58) +#define __NR_wait4 (__NR_Linux + 59) +#define __NR_kill (__NR_Linux + 60) +#define __NR_uname (__NR_Linux + 61) +#define __NR_semget (__NR_Linux + 62) +#define __NR_semop (__NR_Linux + 63) +#define __NR_semctl (__NR_Linux + 64) +#define __NR_shmdt (__NR_Linux + 65) +#define __NR_msgget (__NR_Linux + 66) +#define __NR_msgsnd (__NR_Linux + 67) +#define __NR_msgrcv (__NR_Linux + 68) +#define __NR_msgctl (__NR_Linux + 69) +#define __NR_fcntl (__NR_Linux + 70) +#define __NR_flock (__NR_Linux + 71) +#define __NR_fsync (__NR_Linux + 72) +#define __NR_fdatasync (__NR_Linux + 73) +#define __NR_truncate (__NR_Linux + 74) +#define __NR_ftruncate (__NR_Linux + 75) +#define __NR_getdents (__NR_Linux + 76) +#define __NR_getcwd (__NR_Linux + 77) +#define __NR_chdir (__NR_Linux + 78) +#define __NR_fchdir (__NR_Linux + 79) +#define __NR_rename (__NR_Linux + 80) +#define __NR_mkdir (__NR_Linux + 81) +#define __NR_rmdir (__NR_Linux + 82) +#define __NR_creat (__NR_Linux + 83) +#define __NR_link (__NR_Linux + 84) +#define __NR_unlink (__NR_Linux + 85) +#define __NR_symlink (__NR_Linux + 86) +#define __NR_readlink (__NR_Linux + 87) +#define __NR_chmod (__NR_Linux + 88) +#define __NR_fchmod (__NR_Linux + 89) +#define __NR_chown (__NR_Linux + 90) +#define __NR_fchown (__NR_Linux + 91) +#define __NR_lchown (__NR_Linux + 92) +#define __NR_umask (__NR_Linux + 93) +#define __NR_gettimeofday (__NR_Linux + 94) +#define __NR_getrlimit (__NR_Linux + 95) +#define __NR_getrusage (__NR_Linux + 96) +#define __NR_sysinfo (__NR_Linux + 97) +#define __NR_times (__NR_Linux + 98) +#define __NR_ptrace (__NR_Linux + 99) +#define __NR_getuid (__NR_Linux + 100) +#define __NR_syslog (__NR_Linux + 101) +#define __NR_getgid (__NR_Linux + 102) +#define __NR_setuid (__NR_Linux + 103) +#define __NR_setgid (__NR_Linux + 104) +#define __NR_geteuid (__NR_Linux + 105) +#define __NR_getegid (__NR_Linux + 106) +#define __NR_setpgid (__NR_Linux + 107) +#define __NR_getppid (__NR_Linux + 108) +#define __NR_getpgrp (__NR_Linux + 109) +#define __NR_setsid (__NR_Linux + 110) +#define __NR_setreuid (__NR_Linux + 111) +#define __NR_setregid (__NR_Linux + 112) +#define __NR_getgroups (__NR_Linux + 113) +#define __NR_setgroups (__NR_Linux + 114) +#define __NR_setresuid (__NR_Linux + 115) +#define __NR_getresuid (__NR_Linux + 116) +#define __NR_setresgid (__NR_Linux + 117) +#define __NR_getresgid (__NR_Linux + 118) +#define __NR_getpgid (__NR_Linux + 119) +#define __NR_setfsuid (__NR_Linux + 120) +#define __NR_setfsgid (__NR_Linux + 121) +#define __NR_getsid (__NR_Linux + 122) +#define __NR_capget (__NR_Linux + 123) +#define __NR_capset (__NR_Linux + 124) +#define __NR_rt_sigpending (__NR_Linux + 125) +#define __NR_rt_sigtimedwait (__NR_Linux + 126) +#define __NR_rt_sigqueueinfo (__NR_Linux + 127) +#define __NR_rt_sigsuspend (__NR_Linux + 128) +#define __NR_sigaltstack (__NR_Linux + 129) +#define __NR_utime (__NR_Linux + 130) +#define __NR_mknod (__NR_Linux + 131) +#define __NR_personality (__NR_Linux + 132) +#define __NR_ustat (__NR_Linux + 133) +#define __NR_statfs (__NR_Linux + 134) +#define __NR_fstatfs (__NR_Linux + 135) +#define __NR_sysfs (__NR_Linux + 136) +#define __NR_getpriority (__NR_Linux + 137) +#define __NR_setpriority (__NR_Linux + 138) +#define __NR_sched_setparam (__NR_Linux + 139) +#define __NR_sched_getparam (__NR_Linux + 140) +#define __NR_sched_setscheduler (__NR_Linux + 141) +#define __NR_sched_getscheduler (__NR_Linux + 142) +#define __NR_sched_get_priority_max (__NR_Linux + 143) +#define __NR_sched_get_priority_min (__NR_Linux + 144) +#define __NR_sched_rr_get_interval (__NR_Linux + 145) +#define __NR_mlock (__NR_Linux + 146) +#define __NR_munlock (__NR_Linux + 147) +#define __NR_mlockall (__NR_Linux + 148) +#define __NR_munlockall (__NR_Linux + 149) +#define __NR_vhangup (__NR_Linux + 150) +#define __NR_pivot_root (__NR_Linux + 151) +#define __NR__sysctl (__NR_Linux + 152) +#define __NR_prctl (__NR_Linux + 153) +#define __NR_adjtimex (__NR_Linux + 154) +#define __NR_setrlimit (__NR_Linux + 155) +#define __NR_chroot (__NR_Linux + 156) +#define __NR_sync (__NR_Linux + 157) +#define __NR_acct (__NR_Linux + 158) +#define __NR_settimeofday (__NR_Linux + 159) +#define __NR_mount (__NR_Linux + 160) +#define __NR_umount2 (__NR_Linux + 161) +#define __NR_swapon (__NR_Linux + 162) +#define __NR_swapoff (__NR_Linux + 163) +#define __NR_reboot (__NR_Linux + 164) +#define __NR_sethostname (__NR_Linux + 165) +#define __NR_setdomainname (__NR_Linux + 166) +#define __NR_create_module (__NR_Linux + 167) +#define __NR_init_module (__NR_Linux + 168) +#define __NR_delete_module (__NR_Linux + 169) +#define __NR_get_kernel_syms (__NR_Linux + 170) +#define __NR_query_module (__NR_Linux + 171) +#define __NR_quotactl (__NR_Linux + 172) +#define __NR_nfsservctl (__NR_Linux + 173) +#define __NR_getpmsg (__NR_Linux + 174) +#define __NR_putpmsg (__NR_Linux + 175) +#define __NR_afs_syscall (__NR_Linux + 176) +#define __NR_reserved177 (__NR_Linux + 177) +#define __NR_gettid (__NR_Linux + 178) +#define __NR_readahead (__NR_Linux + 179) +#define __NR_setxattr (__NR_Linux + 180) +#define __NR_lsetxattr (__NR_Linux + 181) +#define __NR_fsetxattr (__NR_Linux + 182) +#define __NR_getxattr (__NR_Linux + 183) +#define __NR_lgetxattr (__NR_Linux + 184) +#define __NR_fgetxattr (__NR_Linux + 185) +#define __NR_listxattr (__NR_Linux + 186) +#define __NR_llistxattr (__NR_Linux + 187) +#define __NR_flistxattr (__NR_Linux + 188) +#define __NR_removexattr (__NR_Linux + 189) +#define __NR_lremovexattr (__NR_Linux + 190) +#define __NR_fremovexattr (__NR_Linux + 191) +#define __NR_tkill (__NR_Linux + 192) +#define __NR_reserved193 (__NR_Linux + 193) +#define __NR_futex (__NR_Linux + 194) +#define __NR_sched_setaffinity (__NR_Linux + 195) +#define __NR_sched_getaffinity (__NR_Linux + 196) +#define __NR_cacheflush (__NR_Linux + 197) +#define __NR_cachectl (__NR_Linux + 198) +#define __NR_sysmips (__NR_Linux + 199) +#define __NR_io_setup (__NR_Linux + 200) +#define __NR_io_destroy (__NR_Linux + 201) +#define __NR_io_getevents (__NR_Linux + 202) +#define __NR_io_submit (__NR_Linux + 203) +#define __NR_io_cancel (__NR_Linux + 204) +#define __NR_exit_group (__NR_Linux + 205) +#define __NR_lookup_dcookie (__NR_Linux + 206) +#define __NR_epoll_create (__NR_Linux + 207) +#define __NR_epoll_ctl (__NR_Linux + 208) +#define __NR_epoll_wait (__NR_Linux + 209) +#define __NR_remap_file_pages (__NR_Linux + 210) +#define __NR_rt_sigreturn (__NR_Linux + 211) +#define __NR_fcntl64 (__NR_Linux + 212) +#define __NR_set_tid_address (__NR_Linux + 213) +#define __NR_restart_syscall (__NR_Linux + 214) +#define __NR_semtimedop (__NR_Linux + 215) +#define __NR_fadvise64 (__NR_Linux + 216) +#define __NR_statfs64 (__NR_Linux + 217) +#define __NR_fstatfs64 (__NR_Linux + 218) +#define __NR_sendfile64 (__NR_Linux + 219) +#define __NR_timer_create (__NR_Linux + 220) +#define __NR_timer_settime (__NR_Linux + 221) +#define __NR_timer_gettime (__NR_Linux + 222) +#define __NR_timer_getoverrun (__NR_Linux + 223) +#define __NR_timer_delete (__NR_Linux + 224) +#define __NR_clock_settime (__NR_Linux + 225) +#define __NR_clock_gettime (__NR_Linux + 226) +#define __NR_clock_getres (__NR_Linux + 227) +#define __NR_clock_nanosleep (__NR_Linux + 228) +#define __NR_tgkill (__NR_Linux + 229) +#define __NR_utimes (__NR_Linux + 230) +#define __NR_mbind (__NR_Linux + 231) +#define __NR_get_mempolicy (__NR_Linux + 232) +#define __NR_set_mempolicy (__NR_Linux + 233) +#define __NR_mq_open (__NR_Linux + 234) +#define __NR_mq_unlink (__NR_Linux + 235) +#define __NR_mq_timedsend (__NR_Linux + 236) +#define __NR_mq_timedreceive (__NR_Linux + 237) +#define __NR_mq_notify (__NR_Linux + 238) +#define __NR_mq_getsetattr (__NR_Linux + 239) +#define __NR_vserver (__NR_Linux + 240) +#define __NR_waitid (__NR_Linux + 241) +/* #define __NR_sys_setaltroot (__NR_Linux + 242) */ +#define __NR_add_key (__NR_Linux + 243) +#define __NR_request_key (__NR_Linux + 244) +#define __NR_keyctl (__NR_Linux + 245) +#define __NR_set_thread_area (__NR_Linux + 246) +#define __NR_inotify_init (__NR_Linux + 247) +#define __NR_inotify_add_watch (__NR_Linux + 248) +#define __NR_inotify_rm_watch (__NR_Linux + 249) +#define __NR_migrate_pages (__NR_Linux + 250) +#define __NR_openat (__NR_Linux + 251) +#define __NR_mkdirat (__NR_Linux + 252) +#define __NR_mknodat (__NR_Linux + 253) +#define __NR_fchownat (__NR_Linux + 254) +#define __NR_futimesat (__NR_Linux + 255) +#define __NR_newfstatat (__NR_Linux + 256) +#define __NR_unlinkat (__NR_Linux + 257) +#define __NR_renameat (__NR_Linux + 258) +#define __NR_linkat (__NR_Linux + 259) +#define __NR_symlinkat (__NR_Linux + 260) +#define __NR_readlinkat (__NR_Linux + 261) +#define __NR_fchmodat (__NR_Linux + 262) +#define __NR_faccessat (__NR_Linux + 263) +#define __NR_pselect6 (__NR_Linux + 264) +#define __NR_ppoll (__NR_Linux + 265) +#define __NR_unshare (__NR_Linux + 266) +#define __NR_splice (__NR_Linux + 267) +#define __NR_sync_file_range (__NR_Linux + 268) +#define __NR_tee (__NR_Linux + 269) +#define __NR_vmsplice (__NR_Linux + 270) +#define __NR_move_pages (__NR_Linux + 271) +#define __NR_set_robust_list (__NR_Linux + 272) +#define __NR_get_robust_list (__NR_Linux + 273) +#define __NR_kexec_load (__NR_Linux + 274) +#define __NR_getcpu (__NR_Linux + 275) +#define __NR_epoll_pwait (__NR_Linux + 276) +#define __NR_ioprio_set (__NR_Linux + 277) +#define __NR_ioprio_get (__NR_Linux + 278) +#define __NR_utimensat (__NR_Linux + 279) +#define __NR_signalfd (__NR_Linux + 280) +#define __NR_timerfd (__NR_Linux + 281) +#define __NR_eventfd (__NR_Linux + 282) +#define __NR_fallocate (__NR_Linux + 283) +#define __NR_timerfd_create (__NR_Linux + 284) +#define __NR_timerfd_gettime (__NR_Linux + 285) +#define __NR_timerfd_settime (__NR_Linux + 286) +#define __NR_signalfd4 (__NR_Linux + 287) +#define __NR_eventfd2 (__NR_Linux + 288) +#define __NR_epoll_create1 (__NR_Linux + 289) +#define __NR_dup3 (__NR_Linux + 290) +#define __NR_pipe2 (__NR_Linux + 291) +#define __NR_inotify_init1 (__NR_Linux + 292) +#define __NR_preadv (__NR_Linux + 293) +#define __NR_pwritev (__NR_Linux + 294) +#define __NR_rt_tgsigqueueinfo (__NR_Linux + 295) +#define __NR_perf_event_open (__NR_Linux + 296) +#define __NR_accept4 (__NR_Linux + 297) +#define __NR_recvmmsg (__NR_Linux + 298) +#define __NR_getdents64 (__NR_Linux + 299) +#define __NR_fanotify_init (__NR_Linux + 300) +#define __NR_fanotify_mark (__NR_Linux + 301) +#define __NR_prlimit64 (__NR_Linux + 302) +#define __NR_name_to_handle_at (__NR_Linux + 303) +#define __NR_open_by_handle_at (__NR_Linux + 304) +#define __NR_clock_adjtime (__NR_Linux + 305) +#define __NR_syncfs (__NR_Linux + 306) +#define __NR_sendmmsg (__NR_Linux + 307) +#define __NR_setns (__NR_Linux + 308) +#define __NR_process_vm_readv (__NR_Linux + 309) +#define __NR_process_vm_writev (__NR_Linux + 310) +#define __NR_kcmp (__NR_Linux + 311) +#define __NR_finit_module (__NR_Linux + 312) +#define __NR_sched_setattr (__NR_Linux + 313) +#define __NR_sched_getattr (__NR_Linux + 314) +#define __NR_renameat2 (__NR_Linux + 315) +#define __NR_seccomp (__NR_Linux + 316) +#define __NR_getrandom (__NR_Linux + 317) +#define __NR_memfd_create (__NR_Linux + 318) +#define __NR_bpf (__NR_Linux + 319) +#define __NR_execveat (__NR_Linux + 320) + +/* + * Offset of the last N32 flavoured syscall + */ +#define __NR_Linux_syscalls 320 + +#endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */ + +#define __NR_N32_Linux 6000 +#define __NR_N32_Linux_syscalls 320 + +#endif /* _ASM_UNISTD_H */ diff --git a/qemu/linux-headers/asm-powerpc/epapr_hcalls.h b/qemu/linux-headers/asm-powerpc/epapr_hcalls.h index 06f724786..33b3f89f5 100644 --- a/qemu/linux-headers/asm-powerpc/epapr_hcalls.h +++ b/qemu/linux-headers/asm-powerpc/epapr_hcalls.h @@ -78,7 +78,7 @@ #define EV_SUCCESS 0 #define EV_EPERM 1 /* Operation not permitted */ #define EV_ENOENT 2 /* Entry Not Found */ -#define EV_EIO 3 /* I/O error occured */ +#define EV_EIO 3 /* I/O error occurred */ #define EV_EAGAIN 4 /* The operation had insufficient * resources to complete and should be * retried @@ -89,7 +89,7 @@ #define EV_ENODEV 7 /* No such device */ #define EV_EINVAL 8 /* An argument supplied to the hcall was out of range or invalid */ -#define EV_INTERNAL 9 /* An internal error occured */ +#define EV_INTERNAL 9 /* An internal error occurred */ #define EV_CONFIG 10 /* A configuration error was detected */ #define EV_INVALID_STATE 11 /* The object is in an invalid state */ #define EV_UNIMPLEMENTED 12 /* Unimplemented hypercall */ diff --git a/qemu/linux-headers/asm-powerpc/kvm.h b/qemu/linux-headers/asm-powerpc/kvm.h index ab4d4732c..c93cf35ce 100644 --- a/qemu/linux-headers/asm-powerpc/kvm.h +++ b/qemu/linux-headers/asm-powerpc/kvm.h @@ -333,6 +333,15 @@ struct kvm_create_spapr_tce { __u32 window_size; }; +/* for KVM_CAP_SPAPR_TCE_64 */ +struct kvm_create_spapr_tce_64 { + __u64 liobn; + __u32 page_shift; + __u32 flags; + __u64 offset; /* in pages */ + __u64 size; /* in pages */ +}; + /* for KVM_ALLOCATE_RMA */ struct kvm_allocate_rma { __u64 rma_size; diff --git a/qemu/linux-headers/asm-powerpc/unistd.h b/qemu/linux-headers/asm-powerpc/unistd.h new file mode 100644 index 000000000..cd92d982f --- /dev/null +++ b/qemu/linux-headers/asm-powerpc/unistd.h @@ -0,0 +1,394 @@ +/* + * This file contains the system call numbers. + * + * 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. + */ +#ifndef _ASM_POWERPC_UNISTD_H_ +#define _ASM_POWERPC_UNISTD_H_ + + +#define __NR_restart_syscall 0 +#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_waitpid 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_break 17 +#define __NR_oldstat 18 +#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_oldfstat 28 +#define __NR_pause 29 +#define __NR_utime 30 +#define __NR_stty 31 +#define __NR_gtty 32 +#define __NR_access 33 +#define __NR_nice 34 +#define __NR_ftime 35 +#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_prof 44 +#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_lock 53 +#define __NR_ioctl 54 +#define __NR_fcntl 55 +#define __NR_mpx 56 +#define __NR_setpgid 57 +#define __NR_ulimit 58 +#define __NR_oldolduname 59 +#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_sgetmask 68 +#define __NR_ssetmask 69 +#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_select 82 +#define __NR_symlink 83 +#define __NR_oldlstat 84 +#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_profil 98 +#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_olduname 109 +#define __NR_iopl 110 +#define __NR_vhangup 111 +#define __NR_idle 112 +#define __NR_vm86 113 +#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_modify_ldt 123 +#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_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 166 +#define __NR_poll 167 +#define __NR_nfsservctl 168 +#define __NR_setresgid 169 +#define __NR_getresgid 170 +#define __NR_prctl 171 +#define __NR_rt_sigreturn 172 +#define __NR_rt_sigaction 173 +#define __NR_rt_sigprocmask 174 +#define __NR_rt_sigpending 175 +#define __NR_rt_sigtimedwait 176 +#define __NR_rt_sigqueueinfo 177 +#define __NR_rt_sigsuspend 178 +#define __NR_pread64 179 +#define __NR_pwrite64 180 +#define __NR_chown 181 +#define __NR_getcwd 182 +#define __NR_capget 183 +#define __NR_capset 184 +#define __NR_sigaltstack 185 +#define __NR_sendfile 186 +#define __NR_getpmsg 187 /* some people actually want streams */ +#define __NR_putpmsg 188 /* some people actually want streams */ +#define __NR_vfork 189 +#define __NR_ugetrlimit 190 /* SuS compliant getrlimit */ +#define __NR_readahead 191 +#ifndef __powerpc64__ /* these are 32-bit only */ +#define __NR_mmap2 192 +#define __NR_truncate64 193 +#define __NR_ftruncate64 194 +#define __NR_stat64 195 +#define __NR_lstat64 196 +#define __NR_fstat64 197 +#endif +#define __NR_pciconfig_read 198 +#define __NR_pciconfig_write 199 +#define __NR_pciconfig_iobase 200 +#define __NR_multiplexer 201 +#define __NR_getdents64 202 +#define __NR_pivot_root 203 +#ifndef __powerpc64__ +#define __NR_fcntl64 204 +#endif +#define __NR_madvise 205 +#define __NR_mincore 206 +#define __NR_gettid 207 +#define __NR_tkill 208 +#define __NR_setxattr 209 +#define __NR_lsetxattr 210 +#define __NR_fsetxattr 211 +#define __NR_getxattr 212 +#define __NR_lgetxattr 213 +#define __NR_fgetxattr 214 +#define __NR_listxattr 215 +#define __NR_llistxattr 216 +#define __NR_flistxattr 217 +#define __NR_removexattr 218 +#define __NR_lremovexattr 219 +#define __NR_fremovexattr 220 +#define __NR_futex 221 +#define __NR_sched_setaffinity 222 +#define __NR_sched_getaffinity 223 +/* 224 currently unused */ +#define __NR_tuxcall 225 +#ifndef __powerpc64__ +#define __NR_sendfile64 226 +#endif +#define __NR_io_setup 227 +#define __NR_io_destroy 228 +#define __NR_io_getevents 229 +#define __NR_io_submit 230 +#define __NR_io_cancel 231 +#define __NR_set_tid_address 232 +#define __NR_fadvise64 233 +#define __NR_exit_group 234 +#define __NR_lookup_dcookie 235 +#define __NR_epoll_create 236 +#define __NR_epoll_ctl 237 +#define __NR_epoll_wait 238 +#define __NR_remap_file_pages 239 +#define __NR_timer_create 240 +#define __NR_timer_settime 241 +#define __NR_timer_gettime 242 +#define __NR_timer_getoverrun 243 +#define __NR_timer_delete 244 +#define __NR_clock_settime 245 +#define __NR_clock_gettime 246 +#define __NR_clock_getres 247 +#define __NR_clock_nanosleep 248 +#define __NR_swapcontext 249 +#define __NR_tgkill 250 +#define __NR_utimes 251 +#define __NR_statfs64 252 +#define __NR_fstatfs64 253 +#ifndef __powerpc64__ +#define __NR_fadvise64_64 254 +#endif +#define __NR_rtas 255 +#define __NR_sys_debug_setcontext 256 +/* Number 257 is reserved for vserver */ +#define __NR_migrate_pages 258 +#define __NR_mbind 259 +#define __NR_get_mempolicy 260 +#define __NR_set_mempolicy 261 +#define __NR_mq_open 262 +#define __NR_mq_unlink 263 +#define __NR_mq_timedsend 264 +#define __NR_mq_timedreceive 265 +#define __NR_mq_notify 266 +#define __NR_mq_getsetattr 267 +#define __NR_kexec_load 268 +#define __NR_add_key 269 +#define __NR_request_key 270 +#define __NR_keyctl 271 +#define __NR_waitid 272 +#define __NR_ioprio_set 273 +#define __NR_ioprio_get 274 +#define __NR_inotify_init 275 +#define __NR_inotify_add_watch 276 +#define __NR_inotify_rm_watch 277 +#define __NR_spu_run 278 +#define __NR_spu_create 279 +#define __NR_pselect6 280 +#define __NR_ppoll 281 +#define __NR_unshare 282 +#define __NR_splice 283 +#define __NR_tee 284 +#define __NR_vmsplice 285 +#define __NR_openat 286 +#define __NR_mkdirat 287 +#define __NR_mknodat 288 +#define __NR_fchownat 289 +#define __NR_futimesat 290 +#ifdef __powerpc64__ +#define __NR_newfstatat 291 +#else +#define __NR_fstatat64 291 +#endif +#define __NR_unlinkat 292 +#define __NR_renameat 293 +#define __NR_linkat 294 +#define __NR_symlinkat 295 +#define __NR_readlinkat 296 +#define __NR_fchmodat 297 +#define __NR_faccessat 298 +#define __NR_get_robust_list 299 +#define __NR_set_robust_list 300 +#define __NR_move_pages 301 +#define __NR_getcpu 302 +#define __NR_epoll_pwait 303 +#define __NR_utimensat 304 +#define __NR_signalfd 305 +#define __NR_timerfd_create 306 +#define __NR_eventfd 307 +#define __NR_sync_file_range2 308 +#define __NR_fallocate 309 +#define __NR_subpage_prot 310 +#define __NR_timerfd_settime 311 +#define __NR_timerfd_gettime 312 +#define __NR_signalfd4 313 +#define __NR_eventfd2 314 +#define __NR_epoll_create1 315 +#define __NR_dup3 316 +#define __NR_pipe2 317 +#define __NR_inotify_init1 318 +#define __NR_perf_event_open 319 +#define __NR_preadv 320 +#define __NR_pwritev 321 +#define __NR_rt_tgsigqueueinfo 322 +#define __NR_fanotify_init 323 +#define __NR_fanotify_mark 324 +#define __NR_prlimit64 325 +#define __NR_socket 326 +#define __NR_bind 327 +#define __NR_connect 328 +#define __NR_listen 329 +#define __NR_accept 330 +#define __NR_getsockname 331 +#define __NR_getpeername 332 +#define __NR_socketpair 333 +#define __NR_send 334 +#define __NR_sendto 335 +#define __NR_recv 336 +#define __NR_recvfrom 337 +#define __NR_shutdown 338 +#define __NR_setsockopt 339 +#define __NR_getsockopt 340 +#define __NR_sendmsg 341 +#define __NR_recvmsg 342 +#define __NR_recvmmsg 343 +#define __NR_accept4 344 +#define __NR_name_to_handle_at 345 +#define __NR_open_by_handle_at 346 +#define __NR_clock_adjtime 347 +#define __NR_syncfs 348 +#define __NR_sendmmsg 349 +#define __NR_setns 350 +#define __NR_process_vm_readv 351 +#define __NR_process_vm_writev 352 +#define __NR_finit_module 353 +#define __NR_kcmp 354 +#define __NR_sched_setattr 355 +#define __NR_sched_getattr 356 +#define __NR_renameat2 357 +#define __NR_seccomp 358 +#define __NR_getrandom 359 +#define __NR_memfd_create 360 +#define __NR_bpf 361 +#define __NR_execveat 362 +#define __NR_switch_endian 363 +#define __NR_userfaultfd 364 +#define __NR_membarrier 365 +#define __NR_mlock2 378 +#define __NR_copy_file_range 379 + +#endif /* _ASM_POWERPC_UNISTD_H_ */ diff --git a/qemu/linux-headers/asm-s390/kvm.h b/qemu/linux-headers/asm-s390/kvm.h index 512d8f1d4..a59499be0 100644 --- a/qemu/linux-headers/asm-s390/kvm.h +++ b/qemu/linux-headers/asm-s390/kvm.h @@ -66,6 +66,8 @@ struct kvm_s390_io_adapter_req { #define KVM_S390_VM_MEM_CLR_CMMA 1 #define KVM_S390_VM_MEM_LIMIT_SIZE 2 +#define KVM_S390_NO_MEM_LIMIT U64_MAX + /* kvm attributes for KVM_S390_VM_TOD */ #define KVM_S390_VM_TOD_LOW 0 #define KVM_S390_VM_TOD_HIGH 1 @@ -151,6 +153,8 @@ struct kvm_guest_debug_arch { #define KVM_SYNC_ARCH0 (1UL << 4) #define KVM_SYNC_PFAULT (1UL << 5) #define KVM_SYNC_VRS (1UL << 6) +#define KVM_SYNC_RICCB (1UL << 7) +#define KVM_SYNC_FPRS (1UL << 8) /* definition of registers in kvm_run */ struct kvm_sync_regs { __u64 prefix; /* prefix register */ @@ -165,9 +169,14 @@ struct kvm_sync_regs { __u64 pft; /* pfault token [PFAULT] */ __u64 pfs; /* pfault select [PFAULT] */ __u64 pfc; /* pfault compare [PFAULT] */ - __u64 vrs[32][2]; /* vector registers */ + union { + __u64 vrs[32][2]; /* vector registers (KVM_SYNC_VRS) */ + __u64 fprs[16]; /* fp registers (KVM_SYNC_FPRS) */ + }; __u8 reserved[512]; /* for future vector expansion */ - __u32 fpc; /* only valid with vector registers */ + __u32 fpc; /* valid on KVM_SYNC_VRS or KVM_SYNC_FPRS */ + __u8 padding[52]; /* riccb needs to be 64byte aligned */ + __u8 riccb[64]; /* runtime instrumentation controls block */ }; #define KVM_REG_S390_TODPR (KVM_REG_S390 | KVM_REG_SIZE_U32 | 0x1) diff --git a/qemu/linux-headers/asm-s390/unistd.h b/qemu/linux-headers/asm-s390/unistd.h new file mode 100644 index 000000000..885837ed5 --- /dev/null +++ b/qemu/linux-headers/asm-s390/unistd.h @@ -0,0 +1,406 @@ +/* + * S390 version + * + * Derived from "include/asm-i386/unistd.h" + */ + +#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_syscalls 376 + +/* + * 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 + +#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 + +#endif + +#endif /* _ASM_S390_UNISTD_H_ */ diff --git a/qemu/linux-headers/asm-x86/hyperv.h b/qemu/linux-headers/asm-x86/hyperv.h index 8fba544e9..01af4d859 100644 --- a/qemu/linux-headers/asm-x86/hyperv.h +++ b/qemu/linux-headers/asm-x86/hyperv.h @@ -1,250 +1 @@ -#ifndef _ASM_X86_HYPERV_H -#define _ASM_X86_HYPERV_H - -#include - -/* - * The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent - * is set by CPUID(HvCpuIdFunctionVersionAndFeatures). - */ -#define HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS 0x40000000 -#define HYPERV_CPUID_INTERFACE 0x40000001 -#define HYPERV_CPUID_VERSION 0x40000002 -#define HYPERV_CPUID_FEATURES 0x40000003 -#define HYPERV_CPUID_ENLIGHTMENT_INFO 0x40000004 -#define HYPERV_CPUID_IMPLEMENT_LIMITS 0x40000005 - -#define HYPERV_HYPERVISOR_PRESENT_BIT 0x80000000 -#define HYPERV_CPUID_MIN 0x40000005 -#define HYPERV_CPUID_MAX 0x4000ffff - -/* - * Feature identification. EAX indicates which features are available - * to the partition based upon the current partition privileges. - */ - -/* VP Runtime (HV_X64_MSR_VP_RUNTIME) available */ -#define HV_X64_MSR_VP_RUNTIME_AVAILABLE (1 << 0) -/* Partition Reference Counter (HV_X64_MSR_TIME_REF_COUNT) available*/ -#define HV_X64_MSR_TIME_REF_COUNT_AVAILABLE (1 << 1) - -/* A partition's reference time stamp counter (TSC) page */ -#define HV_X64_MSR_REFERENCE_TSC 0x40000021 - -/* - * There is a single feature flag that signifies the presence of the MSR - * that can be used to retrieve both the local APIC Timer frequency as - * well as the TSC frequency. - */ - -/* Local APIC timer frequency MSR (HV_X64_MSR_APIC_FREQUENCY) is available */ -#define HV_X64_MSR_APIC_FREQUENCY_AVAILABLE (1 << 11) - -/* TSC frequency MSR (HV_X64_MSR_TSC_FREQUENCY) is available */ -#define HV_X64_MSR_TSC_FREQUENCY_AVAILABLE (1 << 11) - -/* - * Basic SynIC MSRs (HV_X64_MSR_SCONTROL through HV_X64_MSR_EOM - * and HV_X64_MSR_SINT0 through HV_X64_MSR_SINT15) available - */ -#define HV_X64_MSR_SYNIC_AVAILABLE (1 << 2) -/* - * Synthetic Timer MSRs (HV_X64_MSR_STIMER0_CONFIG through - * HV_X64_MSR_STIMER3_COUNT) available - */ -#define HV_X64_MSR_SYNTIMER_AVAILABLE (1 << 3) -/* - * APIC access MSRs (HV_X64_MSR_EOI, HV_X64_MSR_ICR and HV_X64_MSR_TPR) - * are available - */ -#define HV_X64_MSR_APIC_ACCESS_AVAILABLE (1 << 4) -/* Hypercall MSRs (HV_X64_MSR_GUEST_OS_ID and HV_X64_MSR_HYPERCALL) available*/ -#define HV_X64_MSR_HYPERCALL_AVAILABLE (1 << 5) -/* Access virtual processor index MSR (HV_X64_MSR_VP_INDEX) available*/ -#define HV_X64_MSR_VP_INDEX_AVAILABLE (1 << 6) -/* Virtual system reset MSR (HV_X64_MSR_RESET) is available*/ -#define HV_X64_MSR_RESET_AVAILABLE (1 << 7) - /* - * Access statistics pages MSRs (HV_X64_MSR_STATS_PARTITION_RETAIL_PAGE, - * HV_X64_MSR_STATS_PARTITION_INTERNAL_PAGE, HV_X64_MSR_STATS_VP_RETAIL_PAGE, - * HV_X64_MSR_STATS_VP_INTERNAL_PAGE) available - */ -#define HV_X64_MSR_STAT_PAGES_AVAILABLE (1 << 8) - -/* - * Feature identification: EBX indicates which flags were specified at - * partition creation. The format is the same as the partition creation - * flag structure defined in section Partition Creation Flags. - */ -#define HV_X64_CREATE_PARTITIONS (1 << 0) -#define HV_X64_ACCESS_PARTITION_ID (1 << 1) -#define HV_X64_ACCESS_MEMORY_POOL (1 << 2) -#define HV_X64_ADJUST_MESSAGE_BUFFERS (1 << 3) -#define HV_X64_POST_MESSAGES (1 << 4) -#define HV_X64_SIGNAL_EVENTS (1 << 5) -#define HV_X64_CREATE_PORT (1 << 6) -#define HV_X64_CONNECT_PORT (1 << 7) -#define HV_X64_ACCESS_STATS (1 << 8) -#define HV_X64_DEBUGGING (1 << 11) -#define HV_X64_CPU_POWER_MANAGEMENT (1 << 12) -#define HV_X64_CONFIGURE_PROFILER (1 << 13) - -/* - * Feature identification. EDX indicates which miscellaneous features - * are available to the partition. - */ -/* The MWAIT instruction is available (per section MONITOR / MWAIT) */ -#define HV_X64_MWAIT_AVAILABLE (1 << 0) -/* Guest debugging support is available */ -#define HV_X64_GUEST_DEBUGGING_AVAILABLE (1 << 1) -/* Performance Monitor support is available*/ -#define HV_X64_PERF_MONITOR_AVAILABLE (1 << 2) -/* Support for physical CPU dynamic partitioning events is available*/ -#define HV_X64_CPU_DYNAMIC_PARTITIONING_AVAILABLE (1 << 3) -/* - * Support for passing hypercall input parameter block via XMM - * registers is available - */ -#define HV_X64_HYPERCALL_PARAMS_XMM_AVAILABLE (1 << 4) -/* Support for a virtual guest idle state is available */ -#define HV_X64_GUEST_IDLE_STATE_AVAILABLE (1 << 5) - -/* - * Implementation recommendations. Indicates which behaviors the hypervisor - * recommends the OS implement for optimal performance. - */ - /* - * Recommend using hypercall for address space switches rather - * than MOV to CR3 instruction - */ -#define HV_X64_MWAIT_RECOMMENDED (1 << 0) -/* Recommend using hypercall for local TLB flushes rather - * than INVLPG or MOV to CR3 instructions */ -#define HV_X64_LOCAL_TLB_FLUSH_RECOMMENDED (1 << 1) -/* - * Recommend using hypercall for remote TLB flushes rather - * than inter-processor interrupts - */ -#define HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED (1 << 2) -/* - * Recommend using MSRs for accessing APIC registers - * EOI, ICR and TPR rather than their memory-mapped counterparts - */ -#define HV_X64_APIC_ACCESS_RECOMMENDED (1 << 3) -/* Recommend using the hypervisor-provided MSR to initiate a system RESET */ -#define HV_X64_SYSTEM_RESET_RECOMMENDED (1 << 4) -/* - * Recommend using relaxed timing for this partition. If used, - * the VM should disable any watchdog timeouts that rely on the - * timely delivery of external interrupts - */ -#define HV_X64_RELAXED_TIMING_RECOMMENDED (1 << 5) - -/* MSR used to identify the guest OS. */ -#define HV_X64_MSR_GUEST_OS_ID 0x40000000 - -/* MSR used to setup pages used to communicate with the hypervisor. */ -#define HV_X64_MSR_HYPERCALL 0x40000001 - -/* MSR used to provide vcpu index */ -#define HV_X64_MSR_VP_INDEX 0x40000002 - -/* MSR used to read the per-partition time reference counter */ -#define HV_X64_MSR_TIME_REF_COUNT 0x40000020 - -/* MSR used to retrieve the TSC frequency */ -#define HV_X64_MSR_TSC_FREQUENCY 0x40000022 - -/* MSR used to retrieve the local APIC timer frequency */ -#define HV_X64_MSR_APIC_FREQUENCY 0x40000023 - -/* Define the virtual APIC registers */ -#define HV_X64_MSR_EOI 0x40000070 -#define HV_X64_MSR_ICR 0x40000071 -#define HV_X64_MSR_TPR 0x40000072 -#define HV_X64_MSR_APIC_ASSIST_PAGE 0x40000073 - -/* Define synthetic interrupt controller model specific registers. */ -#define HV_X64_MSR_SCONTROL 0x40000080 -#define HV_X64_MSR_SVERSION 0x40000081 -#define HV_X64_MSR_SIEFP 0x40000082 -#define HV_X64_MSR_SIMP 0x40000083 -#define HV_X64_MSR_EOM 0x40000084 -#define HV_X64_MSR_SINT0 0x40000090 -#define HV_X64_MSR_SINT1 0x40000091 -#define HV_X64_MSR_SINT2 0x40000092 -#define HV_X64_MSR_SINT3 0x40000093 -#define HV_X64_MSR_SINT4 0x40000094 -#define HV_X64_MSR_SINT5 0x40000095 -#define HV_X64_MSR_SINT6 0x40000096 -#define HV_X64_MSR_SINT7 0x40000097 -#define HV_X64_MSR_SINT8 0x40000098 -#define HV_X64_MSR_SINT9 0x40000099 -#define HV_X64_MSR_SINT10 0x4000009A -#define HV_X64_MSR_SINT11 0x4000009B -#define HV_X64_MSR_SINT12 0x4000009C -#define HV_X64_MSR_SINT13 0x4000009D -#define HV_X64_MSR_SINT14 0x4000009E -#define HV_X64_MSR_SINT15 0x4000009F - -/* - * Synthetic Timer MSRs. Four timers per vcpu. - */ -#define HV_X64_MSR_STIMER0_CONFIG 0x400000B0 -#define HV_X64_MSR_STIMER0_COUNT 0x400000B1 -#define HV_X64_MSR_STIMER1_CONFIG 0x400000B2 -#define HV_X64_MSR_STIMER1_COUNT 0x400000B3 -#define HV_X64_MSR_STIMER2_CONFIG 0x400000B4 -#define HV_X64_MSR_STIMER2_COUNT 0x400000B5 -#define HV_X64_MSR_STIMER3_CONFIG 0x400000B6 -#define HV_X64_MSR_STIMER3_COUNT 0x400000B7 - -/* Hyper-V guest crash notification MSR's */ -#define HV_X64_MSR_CRASH_P0 0x40000100 -#define HV_X64_MSR_CRASH_P1 0x40000101 -#define HV_X64_MSR_CRASH_P2 0x40000102 -#define HV_X64_MSR_CRASH_P3 0x40000103 -#define HV_X64_MSR_CRASH_P4 0x40000104 -#define HV_X64_MSR_CRASH_CTL 0x40000105 -#define HV_X64_MSR_CRASH_CTL_NOTIFY (1ULL << 63) -#define HV_X64_MSR_CRASH_PARAMS \ - (1 + (HV_X64_MSR_CRASH_P4 - HV_X64_MSR_CRASH_P0)) - -#define HV_X64_MSR_HYPERCALL_ENABLE 0x00000001 -#define HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT 12 -#define HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_MASK \ - (~((1ull << HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT) - 1)) - -/* Declare the various hypercall operations. */ -#define HV_X64_HV_NOTIFY_LONG_SPIN_WAIT 0x0008 - -#define HV_X64_MSR_APIC_ASSIST_PAGE_ENABLE 0x00000001 -#define HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT 12 -#define HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_MASK \ - (~((1ull << HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT) - 1)) - -#define HV_X64_MSR_TSC_REFERENCE_ENABLE 0x00000001 -#define HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT 12 - -#define HV_PROCESSOR_POWER_STATE_C0 0 -#define HV_PROCESSOR_POWER_STATE_C1 1 -#define HV_PROCESSOR_POWER_STATE_C2 2 -#define HV_PROCESSOR_POWER_STATE_C3 3 - -/* hypercall status code */ -#define HV_STATUS_SUCCESS 0 -#define HV_STATUS_INVALID_HYPERCALL_CODE 2 -#define HV_STATUS_INVALID_HYPERCALL_INPUT 3 -#define HV_STATUS_INVALID_ALIGNMENT 4 -#define HV_STATUS_INSUFFICIENT_MEMORY 11 -#define HV_STATUS_INVALID_CONNECTION_ID 18 -#define HV_STATUS_INSUFFICIENT_BUFFERS 19 - -typedef struct _HV_REFERENCE_TSC_PAGE { - __u32 tsc_sequence; - __u32 res1; - __u64 tsc_scale; - __s64 tsc_offset; -} HV_REFERENCE_TSC_PAGE, *PHV_REFERENCE_TSC_PAGE; - -#endif +#include "standard-headers/asm-x86/hyperv.h" diff --git a/qemu/linux-headers/asm-x86/kvm.h b/qemu/linux-headers/asm-x86/kvm.h index a4ae82eb8..cd54147cb 100644 --- a/qemu/linux-headers/asm-x86/kvm.h +++ b/qemu/linux-headers/asm-x86/kvm.h @@ -354,7 +354,7 @@ struct kvm_xcrs { struct kvm_sync_regs { }; -#define KVM_QUIRK_LINT0_REENABLED (1 << 0) -#define KVM_QUIRK_CD_NW_CLEARED (1 << 1) +#define KVM_X86_QUIRK_LINT0_REENABLED (1 << 0) +#define KVM_X86_QUIRK_CD_NW_CLEARED (1 << 1) #endif /* _ASM_X86_KVM_H */ diff --git a/qemu/linux-headers/asm-x86/unistd.h b/qemu/linux-headers/asm-x86/unistd.h new file mode 100644 index 000000000..1f99b1284 --- /dev/null +++ b/qemu/linux-headers/asm-x86/unistd.h @@ -0,0 +1,15 @@ +#ifndef _ASM_X86_UNISTD_H +#define _ASM_X86_UNISTD_H + +/* x32 syscall flag bit */ +#define __X32_SYSCALL_BIT 0x40000000 + +# ifdef __i386__ +# include +# elif defined(__ILP32__) +# include +# else +# include +# endif + +#endif /* _ASM_X86_UNISTD_H */ diff --git a/qemu/linux-headers/asm-x86/unistd_32.h b/qemu/linux-headers/asm-x86/unistd_32.h new file mode 100644 index 000000000..abeaf40d3 --- /dev/null +++ b/qemu/linux-headers/asm-x86/unistd_32.h @@ -0,0 +1,381 @@ +#ifndef _ASM_X86_UNISTD_32_H +#define _ASM_X86_UNISTD_32_H 1 + +#define __NR_restart_syscall 0 +#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_waitpid 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_break 17 +#define __NR_oldstat 18 +#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_oldfstat 28 +#define __NR_pause 29 +#define __NR_utime 30 +#define __NR_stty 31 +#define __NR_gtty 32 +#define __NR_access 33 +#define __NR_nice 34 +#define __NR_ftime 35 +#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_prof 44 +#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_lock 53 +#define __NR_ioctl 54 +#define __NR_fcntl 55 +#define __NR_mpx 56 +#define __NR_setpgid 57 +#define __NR_ulimit 58 +#define __NR_oldolduname 59 +#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_sgetmask 68 +#define __NR_ssetmask 69 +#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_select 82 +#define __NR_symlink 83 +#define __NR_oldlstat 84 +#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_profil 98 +#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_olduname 109 +#define __NR_iopl 110 +#define __NR_vhangup 111 +#define __NR_idle 112 +#define __NR_vm86old 113 +#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_modify_ldt 123 +#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_vm86 166 +#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_gettid 224 +#define __NR_readahead 225 +#define __NR_setxattr 226 +#define __NR_lsetxattr 227 +#define __NR_fsetxattr 228 +#define __NR_getxattr 229 +#define __NR_lgetxattr 230 +#define __NR_fgetxattr 231 +#define __NR_listxattr 232 +#define __NR_llistxattr 233 +#define __NR_flistxattr 234 +#define __NR_removexattr 235 +#define __NR_lremovexattr 236 +#define __NR_fremovexattr 237 +#define __NR_tkill 238 +#define __NR_sendfile64 239 +#define __NR_futex 240 +#define __NR_sched_setaffinity 241 +#define __NR_sched_getaffinity 242 +#define __NR_set_thread_area 243 +#define __NR_get_thread_area 244 +#define __NR_io_setup 245 +#define __NR_io_destroy 246 +#define __NR_io_getevents 247 +#define __NR_io_submit 248 +#define __NR_io_cancel 249 +#define __NR_fadvise64 250 +#define __NR_exit_group 252 +#define __NR_lookup_dcookie 253 +#define __NR_epoll_create 254 +#define __NR_epoll_ctl 255 +#define __NR_epoll_wait 256 +#define __NR_remap_file_pages 257 +#define __NR_set_tid_address 258 +#define __NR_timer_create 259 +#define __NR_timer_settime 260 +#define __NR_timer_gettime 261 +#define __NR_timer_getoverrun 262 +#define __NR_timer_delete 263 +#define __NR_clock_settime 264 +#define __NR_clock_gettime 265 +#define __NR_clock_getres 266 +#define __NR_clock_nanosleep 267 +#define __NR_statfs64 268 +#define __NR_fstatfs64 269 +#define __NR_tgkill 270 +#define __NR_utimes 271 +#define __NR_fadvise64_64 272 +#define __NR_vserver 273 +#define __NR_mbind 274 +#define __NR_get_mempolicy 275 +#define __NR_set_mempolicy 276 +#define __NR_mq_open 277 +#define __NR_mq_unlink 278 +#define __NR_mq_timedsend 279 +#define __NR_mq_timedreceive 280 +#define __NR_mq_notify 281 +#define __NR_mq_getsetattr 282 +#define __NR_kexec_load 283 +#define __NR_waitid 284 +#define __NR_add_key 286 +#define __NR_request_key 287 +#define __NR_keyctl 288 +#define __NR_ioprio_set 289 +#define __NR_ioprio_get 290 +#define __NR_inotify_init 291 +#define __NR_inotify_add_watch 292 +#define __NR_inotify_rm_watch 293 +#define __NR_migrate_pages 294 +#define __NR_openat 295 +#define __NR_mkdirat 296 +#define __NR_mknodat 297 +#define __NR_fchownat 298 +#define __NR_futimesat 299 +#define __NR_fstatat64 300 +#define __NR_unlinkat 301 +#define __NR_renameat 302 +#define __NR_linkat 303 +#define __NR_symlinkat 304 +#define __NR_readlinkat 305 +#define __NR_fchmodat 306 +#define __NR_faccessat 307 +#define __NR_pselect6 308 +#define __NR_ppoll 309 +#define __NR_unshare 310 +#define __NR_set_robust_list 311 +#define __NR_get_robust_list 312 +#define __NR_splice 313 +#define __NR_sync_file_range 314 +#define __NR_tee 315 +#define __NR_vmsplice 316 +#define __NR_move_pages 317 +#define __NR_getcpu 318 +#define __NR_epoll_pwait 319 +#define __NR_utimensat 320 +#define __NR_signalfd 321 +#define __NR_timerfd_create 322 +#define __NR_eventfd 323 +#define __NR_fallocate 324 +#define __NR_timerfd_settime 325 +#define __NR_timerfd_gettime 326 +#define __NR_signalfd4 327 +#define __NR_eventfd2 328 +#define __NR_epoll_create1 329 +#define __NR_dup3 330 +#define __NR_pipe2 331 +#define __NR_inotify_init1 332 +#define __NR_preadv 333 +#define __NR_pwritev 334 +#define __NR_rt_tgsigqueueinfo 335 +#define __NR_perf_event_open 336 +#define __NR_recvmmsg 337 +#define __NR_fanotify_init 338 +#define __NR_fanotify_mark 339 +#define __NR_prlimit64 340 +#define __NR_name_to_handle_at 341 +#define __NR_open_by_handle_at 342 +#define __NR_clock_adjtime 343 +#define __NR_syncfs 344 +#define __NR_sendmmsg 345 +#define __NR_setns 346 +#define __NR_process_vm_readv 347 +#define __NR_process_vm_writev 348 +#define __NR_kcmp 349 +#define __NR_finit_module 350 +#define __NR_sched_setattr 351 +#define __NR_sched_getattr 352 +#define __NR_renameat2 353 +#define __NR_seccomp 354 +#define __NR_getrandom 355 +#define __NR_memfd_create 356 +#define __NR_bpf 357 +#define __NR_execveat 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_userfaultfd 374 +#define __NR_membarrier 375 +#define __NR_mlock2 376 +#define __NR_copy_file_range 377 +#define __NR_preadv2 378 +#define __NR_pwritev2 379 + +#endif /* _ASM_X86_UNISTD_32_H */ diff --git a/qemu/linux-headers/asm-x86/unistd_64.h b/qemu/linux-headers/asm-x86/unistd_64.h new file mode 100644 index 000000000..73c3d1f66 --- /dev/null +++ b/qemu/linux-headers/asm-x86/unistd_64.h @@ -0,0 +1,334 @@ +#ifndef _ASM_X86_UNISTD_64_H +#define _ASM_X86_UNISTD_64_H 1 + +#define __NR_read 0 +#define __NR_write 1 +#define __NR_open 2 +#define __NR_close 3 +#define __NR_stat 4 +#define __NR_fstat 5 +#define __NR_lstat 6 +#define __NR_poll 7 +#define __NR_lseek 8 +#define __NR_mmap 9 +#define __NR_mprotect 10 +#define __NR_munmap 11 +#define __NR_brk 12 +#define __NR_rt_sigaction 13 +#define __NR_rt_sigprocmask 14 +#define __NR_rt_sigreturn 15 +#define __NR_ioctl 16 +#define __NR_pread64 17 +#define __NR_pwrite64 18 +#define __NR_readv 19 +#define __NR_writev 20 +#define __NR_access 21 +#define __NR_pipe 22 +#define __NR_select 23 +#define __NR_sched_yield 24 +#define __NR_mremap 25 +#define __NR_msync 26 +#define __NR_mincore 27 +#define __NR_madvise 28 +#define __NR_shmget 29 +#define __NR_shmat 30 +#define __NR_shmctl 31 +#define __NR_dup 32 +#define __NR_dup2 33 +#define __NR_pause 34 +#define __NR_nanosleep 35 +#define __NR_getitimer 36 +#define __NR_alarm 37 +#define __NR_setitimer 38 +#define __NR_getpid 39 +#define __NR_sendfile 40 +#define __NR_socket 41 +#define __NR_connect 42 +#define __NR_accept 43 +#define __NR_sendto 44 +#define __NR_recvfrom 45 +#define __NR_sendmsg 46 +#define __NR_recvmsg 47 +#define __NR_shutdown 48 +#define __NR_bind 49 +#define __NR_listen 50 +#define __NR_getsockname 51 +#define __NR_getpeername 52 +#define __NR_socketpair 53 +#define __NR_setsockopt 54 +#define __NR_getsockopt 55 +#define __NR_clone 56 +#define __NR_fork 57 +#define __NR_vfork 58 +#define __NR_execve 59 +#define __NR_exit 60 +#define __NR_wait4 61 +#define __NR_kill 62 +#define __NR_uname 63 +#define __NR_semget 64 +#define __NR_semop 65 +#define __NR_semctl 66 +#define __NR_shmdt 67 +#define __NR_msgget 68 +#define __NR_msgsnd 69 +#define __NR_msgrcv 70 +#define __NR_msgctl 71 +#define __NR_fcntl 72 +#define __NR_flock 73 +#define __NR_fsync 74 +#define __NR_fdatasync 75 +#define __NR_truncate 76 +#define __NR_ftruncate 77 +#define __NR_getdents 78 +#define __NR_getcwd 79 +#define __NR_chdir 80 +#define __NR_fchdir 81 +#define __NR_rename 82 +#define __NR_mkdir 83 +#define __NR_rmdir 84 +#define __NR_creat 85 +#define __NR_link 86 +#define __NR_unlink 87 +#define __NR_symlink 88 +#define __NR_readlink 89 +#define __NR_chmod 90 +#define __NR_fchmod 91 +#define __NR_chown 92 +#define __NR_fchown 93 +#define __NR_lchown 94 +#define __NR_umask 95 +#define __NR_gettimeofday 96 +#define __NR_getrlimit 97 +#define __NR_getrusage 98 +#define __NR_sysinfo 99 +#define __NR_times 100 +#define __NR_ptrace 101 +#define __NR_getuid 102 +#define __NR_syslog 103 +#define __NR_getgid 104 +#define __NR_setuid 105 +#define __NR_setgid 106 +#define __NR_geteuid 107 +#define __NR_getegid 108 +#define __NR_setpgid 109 +#define __NR_getppid 110 +#define __NR_getpgrp 111 +#define __NR_setsid 112 +#define __NR_setreuid 113 +#define __NR_setregid 114 +#define __NR_getgroups 115 +#define __NR_setgroups 116 +#define __NR_setresuid 117 +#define __NR_getresuid 118 +#define __NR_setresgid 119 +#define __NR_getresgid 120 +#define __NR_getpgid 121 +#define __NR_setfsuid 122 +#define __NR_setfsgid 123 +#define __NR_getsid 124 +#define __NR_capget 125 +#define __NR_capset 126 +#define __NR_rt_sigpending 127 +#define __NR_rt_sigtimedwait 128 +#define __NR_rt_sigqueueinfo 129 +#define __NR_rt_sigsuspend 130 +#define __NR_sigaltstack 131 +#define __NR_utime 132 +#define __NR_mknod 133 +#define __NR_uselib 134 +#define __NR_personality 135 +#define __NR_ustat 136 +#define __NR_statfs 137 +#define __NR_fstatfs 138 +#define __NR_sysfs 139 +#define __NR_getpriority 140 +#define __NR_setpriority 141 +#define __NR_sched_setparam 142 +#define __NR_sched_getparam 143 +#define __NR_sched_setscheduler 144 +#define __NR_sched_getscheduler 145 +#define __NR_sched_get_priority_max 146 +#define __NR_sched_get_priority_min 147 +#define __NR_sched_rr_get_interval 148 +#define __NR_mlock 149 +#define __NR_munlock 150 +#define __NR_mlockall 151 +#define __NR_munlockall 152 +#define __NR_vhangup 153 +#define __NR_modify_ldt 154 +#define __NR_pivot_root 155 +#define __NR__sysctl 156 +#define __NR_prctl 157 +#define __NR_arch_prctl 158 +#define __NR_adjtimex 159 +#define __NR_setrlimit 160 +#define __NR_chroot 161 +#define __NR_sync 162 +#define __NR_acct 163 +#define __NR_settimeofday 164 +#define __NR_mount 165 +#define __NR_umount2 166 +#define __NR_swapon 167 +#define __NR_swapoff 168 +#define __NR_reboot 169 +#define __NR_sethostname 170 +#define __NR_setdomainname 171 +#define __NR_iopl 172 +#define __NR_ioperm 173 +#define __NR_create_module 174 +#define __NR_init_module 175 +#define __NR_delete_module 176 +#define __NR_get_kernel_syms 177 +#define __NR_query_module 178 +#define __NR_quotactl 179 +#define __NR_nfsservctl 180 +#define __NR_getpmsg 181 +#define __NR_putpmsg 182 +#define __NR_afs_syscall 183 +#define __NR_tuxcall 184 +#define __NR_security 185 +#define __NR_gettid 186 +#define __NR_readahead 187 +#define __NR_setxattr 188 +#define __NR_lsetxattr 189 +#define __NR_fsetxattr 190 +#define __NR_getxattr 191 +#define __NR_lgetxattr 192 +#define __NR_fgetxattr 193 +#define __NR_listxattr 194 +#define __NR_llistxattr 195 +#define __NR_flistxattr 196 +#define __NR_removexattr 197 +#define __NR_lremovexattr 198 +#define __NR_fremovexattr 199 +#define __NR_tkill 200 +#define __NR_time 201 +#define __NR_futex 202 +#define __NR_sched_setaffinity 203 +#define __NR_sched_getaffinity 204 +#define __NR_set_thread_area 205 +#define __NR_io_setup 206 +#define __NR_io_destroy 207 +#define __NR_io_getevents 208 +#define __NR_io_submit 209 +#define __NR_io_cancel 210 +#define __NR_get_thread_area 211 +#define __NR_lookup_dcookie 212 +#define __NR_epoll_create 213 +#define __NR_epoll_ctl_old 214 +#define __NR_epoll_wait_old 215 +#define __NR_remap_file_pages 216 +#define __NR_getdents64 217 +#define __NR_set_tid_address 218 +#define __NR_restart_syscall 219 +#define __NR_semtimedop 220 +#define __NR_fadvise64 221 +#define __NR_timer_create 222 +#define __NR_timer_settime 223 +#define __NR_timer_gettime 224 +#define __NR_timer_getoverrun 225 +#define __NR_timer_delete 226 +#define __NR_clock_settime 227 +#define __NR_clock_gettime 228 +#define __NR_clock_getres 229 +#define __NR_clock_nanosleep 230 +#define __NR_exit_group 231 +#define __NR_epoll_wait 232 +#define __NR_epoll_ctl 233 +#define __NR_tgkill 234 +#define __NR_utimes 235 +#define __NR_vserver 236 +#define __NR_mbind 237 +#define __NR_set_mempolicy 238 +#define __NR_get_mempolicy 239 +#define __NR_mq_open 240 +#define __NR_mq_unlink 241 +#define __NR_mq_timedsend 242 +#define __NR_mq_timedreceive 243 +#define __NR_mq_notify 244 +#define __NR_mq_getsetattr 245 +#define __NR_kexec_load 246 +#define __NR_waitid 247 +#define __NR_add_key 248 +#define __NR_request_key 249 +#define __NR_keyctl 250 +#define __NR_ioprio_set 251 +#define __NR_ioprio_get 252 +#define __NR_inotify_init 253 +#define __NR_inotify_add_watch 254 +#define __NR_inotify_rm_watch 255 +#define __NR_migrate_pages 256 +#define __NR_openat 257 +#define __NR_mkdirat 258 +#define __NR_mknodat 259 +#define __NR_fchownat 260 +#define __NR_futimesat 261 +#define __NR_newfstatat 262 +#define __NR_unlinkat 263 +#define __NR_renameat 264 +#define __NR_linkat 265 +#define __NR_symlinkat 266 +#define __NR_readlinkat 267 +#define __NR_fchmodat 268 +#define __NR_faccessat 269 +#define __NR_pselect6 270 +#define __NR_ppoll 271 +#define __NR_unshare 272 +#define __NR_set_robust_list 273 +#define __NR_get_robust_list 274 +#define __NR_splice 275 +#define __NR_tee 276 +#define __NR_sync_file_range 277 +#define __NR_vmsplice 278 +#define __NR_move_pages 279 +#define __NR_utimensat 280 +#define __NR_epoll_pwait 281 +#define __NR_signalfd 282 +#define __NR_timerfd_create 283 +#define __NR_eventfd 284 +#define __NR_fallocate 285 +#define __NR_timerfd_settime 286 +#define __NR_timerfd_gettime 287 +#define __NR_accept4 288 +#define __NR_signalfd4 289 +#define __NR_eventfd2 290 +#define __NR_epoll_create1 291 +#define __NR_dup3 292 +#define __NR_pipe2 293 +#define __NR_inotify_init1 294 +#define __NR_preadv 295 +#define __NR_pwritev 296 +#define __NR_rt_tgsigqueueinfo 297 +#define __NR_perf_event_open 298 +#define __NR_recvmmsg 299 +#define __NR_fanotify_init 300 +#define __NR_fanotify_mark 301 +#define __NR_prlimit64 302 +#define __NR_name_to_handle_at 303 +#define __NR_open_by_handle_at 304 +#define __NR_clock_adjtime 305 +#define __NR_syncfs 306 +#define __NR_sendmmsg 307 +#define __NR_setns 308 +#define __NR_getcpu 309 +#define __NR_process_vm_readv 310 +#define __NR_process_vm_writev 311 +#define __NR_kcmp 312 +#define __NR_finit_module 313 +#define __NR_sched_setattr 314 +#define __NR_sched_getattr 315 +#define __NR_renameat2 316 +#define __NR_seccomp 317 +#define __NR_getrandom 318 +#define __NR_memfd_create 319 +#define __NR_kexec_file_load 320 +#define __NR_bpf 321 +#define __NR_execveat 322 +#define __NR_userfaultfd 323 +#define __NR_membarrier 324 +#define __NR_mlock2 325 +#define __NR_copy_file_range 326 +#define __NR_preadv2 327 +#define __NR_pwritev2 328 + +#endif /* _ASM_X86_UNISTD_64_H */ diff --git a/qemu/linux-headers/asm-x86/unistd_x32.h b/qemu/linux-headers/asm-x86/unistd_x32.h new file mode 100644 index 000000000..8f77ee868 --- /dev/null +++ b/qemu/linux-headers/asm-x86/unistd_x32.h @@ -0,0 +1,321 @@ +#ifndef _ASM_X86_UNISTD_X32_H +#define _ASM_X86_UNISTD_X32_H 1 + +#define __NR_read (__X32_SYSCALL_BIT + 0) +#define __NR_write (__X32_SYSCALL_BIT + 1) +#define __NR_open (__X32_SYSCALL_BIT + 2) +#define __NR_close (__X32_SYSCALL_BIT + 3) +#define __NR_stat (__X32_SYSCALL_BIT + 4) +#define __NR_fstat (__X32_SYSCALL_BIT + 5) +#define __NR_lstat (__X32_SYSCALL_BIT + 6) +#define __NR_poll (__X32_SYSCALL_BIT + 7) +#define __NR_lseek (__X32_SYSCALL_BIT + 8) +#define __NR_mmap (__X32_SYSCALL_BIT + 9) +#define __NR_mprotect (__X32_SYSCALL_BIT + 10) +#define __NR_munmap (__X32_SYSCALL_BIT + 11) +#define __NR_brk (__X32_SYSCALL_BIT + 12) +#define __NR_rt_sigprocmask (__X32_SYSCALL_BIT + 14) +#define __NR_pread64 (__X32_SYSCALL_BIT + 17) +#define __NR_pwrite64 (__X32_SYSCALL_BIT + 18) +#define __NR_access (__X32_SYSCALL_BIT + 21) +#define __NR_pipe (__X32_SYSCALL_BIT + 22) +#define __NR_select (__X32_SYSCALL_BIT + 23) +#define __NR_sched_yield (__X32_SYSCALL_BIT + 24) +#define __NR_mremap (__X32_SYSCALL_BIT + 25) +#define __NR_msync (__X32_SYSCALL_BIT + 26) +#define __NR_mincore (__X32_SYSCALL_BIT + 27) +#define __NR_madvise (__X32_SYSCALL_BIT + 28) +#define __NR_shmget (__X32_SYSCALL_BIT + 29) +#define __NR_shmat (__X32_SYSCALL_BIT + 30) +#define __NR_shmctl (__X32_SYSCALL_BIT + 31) +#define __NR_dup (__X32_SYSCALL_BIT + 32) +#define __NR_dup2 (__X32_SYSCALL_BIT + 33) +#define __NR_pause (__X32_SYSCALL_BIT + 34) +#define __NR_nanosleep (__X32_SYSCALL_BIT + 35) +#define __NR_getitimer (__X32_SYSCALL_BIT + 36) +#define __NR_alarm (__X32_SYSCALL_BIT + 37) +#define __NR_setitimer (__X32_SYSCALL_BIT + 38) +#define __NR_getpid (__X32_SYSCALL_BIT + 39) +#define __NR_sendfile (__X32_SYSCALL_BIT + 40) +#define __NR_socket (__X32_SYSCALL_BIT + 41) +#define __NR_connect (__X32_SYSCALL_BIT + 42) +#define __NR_accept (__X32_SYSCALL_BIT + 43) +#define __NR_sendto (__X32_SYSCALL_BIT + 44) +#define __NR_shutdown (__X32_SYSCALL_BIT + 48) +#define __NR_bind (__X32_SYSCALL_BIT + 49) +#define __NR_listen (__X32_SYSCALL_BIT + 50) +#define __NR_getsockname (__X32_SYSCALL_BIT + 51) +#define __NR_getpeername (__X32_SYSCALL_BIT + 52) +#define __NR_socketpair (__X32_SYSCALL_BIT + 53) +#define __NR_clone (__X32_SYSCALL_BIT + 56) +#define __NR_fork (__X32_SYSCALL_BIT + 57) +#define __NR_vfork (__X32_SYSCALL_BIT + 58) +#define __NR_exit (__X32_SYSCALL_BIT + 60) +#define __NR_wait4 (__X32_SYSCALL_BIT + 61) +#define __NR_kill (__X32_SYSCALL_BIT + 62) +#define __NR_uname (__X32_SYSCALL_BIT + 63) +#define __NR_semget (__X32_SYSCALL_BIT + 64) +#define __NR_semop (__X32_SYSCALL_BIT + 65) +#define __NR_semctl (__X32_SYSCALL_BIT + 66) +#define __NR_shmdt (__X32_SYSCALL_BIT + 67) +#define __NR_msgget (__X32_SYSCALL_BIT + 68) +#define __NR_msgsnd (__X32_SYSCALL_BIT + 69) +#define __NR_msgrcv (__X32_SYSCALL_BIT + 70) +#define __NR_msgctl (__X32_SYSCALL_BIT + 71) +#define __NR_fcntl (__X32_SYSCALL_BIT + 72) +#define __NR_flock (__X32_SYSCALL_BIT + 73) +#define __NR_fsync (__X32_SYSCALL_BIT + 74) +#define __NR_fdatasync (__X32_SYSCALL_BIT + 75) +#define __NR_truncate (__X32_SYSCALL_BIT + 76) +#define __NR_ftruncate (__X32_SYSCALL_BIT + 77) +#define __NR_getdents (__X32_SYSCALL_BIT + 78) +#define __NR_getcwd (__X32_SYSCALL_BIT + 79) +#define __NR_chdir (__X32_SYSCALL_BIT + 80) +#define __NR_fchdir (__X32_SYSCALL_BIT + 81) +#define __NR_rename (__X32_SYSCALL_BIT + 82) +#define __NR_mkdir (__X32_SYSCALL_BIT + 83) +#define __NR_rmdir (__X32_SYSCALL_BIT + 84) +#define __NR_creat (__X32_SYSCALL_BIT + 85) +#define __NR_link (__X32_SYSCALL_BIT + 86) +#define __NR_unlink (__X32_SYSCALL_BIT + 87) +#define __NR_symlink (__X32_SYSCALL_BIT + 88) +#define __NR_readlink (__X32_SYSCALL_BIT + 89) +#define __NR_chmod (__X32_SYSCALL_BIT + 90) +#define __NR_fchmod (__X32_SYSCALL_BIT + 91) +#define __NR_chown (__X32_SYSCALL_BIT + 92) +#define __NR_fchown (__X32_SYSCALL_BIT + 93) +#define __NR_lchown (__X32_SYSCALL_BIT + 94) +#define __NR_umask (__X32_SYSCALL_BIT + 95) +#define __NR_gettimeofday (__X32_SYSCALL_BIT + 96) +#define __NR_getrlimit (__X32_SYSCALL_BIT + 97) +#define __NR_getrusage (__X32_SYSCALL_BIT + 98) +#define __NR_sysinfo (__X32_SYSCALL_BIT + 99) +#define __NR_times (__X32_SYSCALL_BIT + 100) +#define __NR_getuid (__X32_SYSCALL_BIT + 102) +#define __NR_syslog (__X32_SYSCALL_BIT + 103) +#define __NR_getgid (__X32_SYSCALL_BIT + 104) +#define __NR_setuid (__X32_SYSCALL_BIT + 105) +#define __NR_setgid (__X32_SYSCALL_BIT + 106) +#define __NR_geteuid (__X32_SYSCALL_BIT + 107) +#define __NR_getegid (__X32_SYSCALL_BIT + 108) +#define __NR_setpgid (__X32_SYSCALL_BIT + 109) +#define __NR_getppid (__X32_SYSCALL_BIT + 110) +#define __NR_getpgrp (__X32_SYSCALL_BIT + 111) +#define __NR_setsid (__X32_SYSCALL_BIT + 112) +#define __NR_setreuid (__X32_SYSCALL_BIT + 113) +#define __NR_setregid (__X32_SYSCALL_BIT + 114) +#define __NR_getgroups (__X32_SYSCALL_BIT + 115) +#define __NR_setgroups (__X32_SYSCALL_BIT + 116) +#define __NR_setresuid (__X32_SYSCALL_BIT + 117) +#define __NR_getresuid (__X32_SYSCALL_BIT + 118) +#define __NR_setresgid (__X32_SYSCALL_BIT + 119) +#define __NR_getresgid (__X32_SYSCALL_BIT + 120) +#define __NR_getpgid (__X32_SYSCALL_BIT + 121) +#define __NR_setfsuid (__X32_SYSCALL_BIT + 122) +#define __NR_setfsgid (__X32_SYSCALL_BIT + 123) +#define __NR_getsid (__X32_SYSCALL_BIT + 124) +#define __NR_capget (__X32_SYSCALL_BIT + 125) +#define __NR_capset (__X32_SYSCALL_BIT + 126) +#define __NR_rt_sigsuspend (__X32_SYSCALL_BIT + 130) +#define __NR_utime (__X32_SYSCALL_BIT + 132) +#define __NR_mknod (__X32_SYSCALL_BIT + 133) +#define __NR_personality (__X32_SYSCALL_BIT + 135) +#define __NR_ustat (__X32_SYSCALL_BIT + 136) +#define __NR_statfs (__X32_SYSCALL_BIT + 137) +#define __NR_fstatfs (__X32_SYSCALL_BIT + 138) +#define __NR_sysfs (__X32_SYSCALL_BIT + 139) +#define __NR_getpriority (__X32_SYSCALL_BIT + 140) +#define __NR_setpriority (__X32_SYSCALL_BIT + 141) +#define __NR_sched_setparam (__X32_SYSCALL_BIT + 142) +#define __NR_sched_getparam (__X32_SYSCALL_BIT + 143) +#define __NR_sched_setscheduler (__X32_SYSCALL_BIT + 144) +#define __NR_sched_getscheduler (__X32_SYSCALL_BIT + 145) +#define __NR_sched_get_priority_max (__X32_SYSCALL_BIT + 146) +#define __NR_sched_get_priority_min (__X32_SYSCALL_BIT + 147) +#define __NR_sched_rr_get_interval (__X32_SYSCALL_BIT + 148) +#define __NR_mlock (__X32_SYSCALL_BIT + 149) +#define __NR_munlock (__X32_SYSCALL_BIT + 150) +#define __NR_mlockall (__X32_SYSCALL_BIT + 151) +#define __NR_munlockall (__X32_SYSCALL_BIT + 152) +#define __NR_vhangup (__X32_SYSCALL_BIT + 153) +#define __NR_modify_ldt (__X32_SYSCALL_BIT + 154) +#define __NR_pivot_root (__X32_SYSCALL_BIT + 155) +#define __NR_prctl (__X32_SYSCALL_BIT + 157) +#define __NR_arch_prctl (__X32_SYSCALL_BIT + 158) +#define __NR_adjtimex (__X32_SYSCALL_BIT + 159) +#define __NR_setrlimit (__X32_SYSCALL_BIT + 160) +#define __NR_chroot (__X32_SYSCALL_BIT + 161) +#define __NR_sync (__X32_SYSCALL_BIT + 162) +#define __NR_acct (__X32_SYSCALL_BIT + 163) +#define __NR_settimeofday (__X32_SYSCALL_BIT + 164) +#define __NR_mount (__X32_SYSCALL_BIT + 165) +#define __NR_umount2 (__X32_SYSCALL_BIT + 166) +#define __NR_swapon (__X32_SYSCALL_BIT + 167) +#define __NR_swapoff (__X32_SYSCALL_BIT + 168) +#define __NR_reboot (__X32_SYSCALL_BIT + 169) +#define __NR_sethostname (__X32_SYSCALL_BIT + 170) +#define __NR_setdomainname (__X32_SYSCALL_BIT + 171) +#define __NR_iopl (__X32_SYSCALL_BIT + 172) +#define __NR_ioperm (__X32_SYSCALL_BIT + 173) +#define __NR_init_module (__X32_SYSCALL_BIT + 175) +#define __NR_delete_module (__X32_SYSCALL_BIT + 176) +#define __NR_quotactl (__X32_SYSCALL_BIT + 179) +#define __NR_getpmsg (__X32_SYSCALL_BIT + 181) +#define __NR_putpmsg (__X32_SYSCALL_BIT + 182) +#define __NR_afs_syscall (__X32_SYSCALL_BIT + 183) +#define __NR_tuxcall (__X32_SYSCALL_BIT + 184) +#define __NR_security (__X32_SYSCALL_BIT + 185) +#define __NR_gettid (__X32_SYSCALL_BIT + 186) +#define __NR_readahead (__X32_SYSCALL_BIT + 187) +#define __NR_setxattr (__X32_SYSCALL_BIT + 188) +#define __NR_lsetxattr (__X32_SYSCALL_BIT + 189) +#define __NR_fsetxattr (__X32_SYSCALL_BIT + 190) +#define __NR_getxattr (__X32_SYSCALL_BIT + 191) +#define __NR_lgetxattr (__X32_SYSCALL_BIT + 192) +#define __NR_fgetxattr (__X32_SYSCALL_BIT + 193) +#define __NR_listxattr (__X32_SYSCALL_BIT + 194) +#define __NR_llistxattr (__X32_SYSCALL_BIT + 195) +#define __NR_flistxattr (__X32_SYSCALL_BIT + 196) +#define __NR_removexattr (__X32_SYSCALL_BIT + 197) +#define __NR_lremovexattr (__X32_SYSCALL_BIT + 198) +#define __NR_fremovexattr (__X32_SYSCALL_BIT + 199) +#define __NR_tkill (__X32_SYSCALL_BIT + 200) +#define __NR_time (__X32_SYSCALL_BIT + 201) +#define __NR_futex (__X32_SYSCALL_BIT + 202) +#define __NR_sched_setaffinity (__X32_SYSCALL_BIT + 203) +#define __NR_sched_getaffinity (__X32_SYSCALL_BIT + 204) +#define __NR_io_destroy (__X32_SYSCALL_BIT + 207) +#define __NR_io_getevents (__X32_SYSCALL_BIT + 208) +#define __NR_io_cancel (__X32_SYSCALL_BIT + 210) +#define __NR_lookup_dcookie (__X32_SYSCALL_BIT + 212) +#define __NR_epoll_create (__X32_SYSCALL_BIT + 213) +#define __NR_remap_file_pages (__X32_SYSCALL_BIT + 216) +#define __NR_getdents64 (__X32_SYSCALL_BIT + 217) +#define __NR_set_tid_address (__X32_SYSCALL_BIT + 218) +#define __NR_restart_syscall (__X32_SYSCALL_BIT + 219) +#define __NR_semtimedop (__X32_SYSCALL_BIT + 220) +#define __NR_fadvise64 (__X32_SYSCALL_BIT + 221) +#define __NR_timer_settime (__X32_SYSCALL_BIT + 223) +#define __NR_timer_gettime (__X32_SYSCALL_BIT + 224) +#define __NR_timer_getoverrun (__X32_SYSCALL_BIT + 225) +#define __NR_timer_delete (__X32_SYSCALL_BIT + 226) +#define __NR_clock_settime (__X32_SYSCALL_BIT + 227) +#define __NR_clock_gettime (__X32_SYSCALL_BIT + 228) +#define __NR_clock_getres (__X32_SYSCALL_BIT + 229) +#define __NR_clock_nanosleep (__X32_SYSCALL_BIT + 230) +#define __NR_exit_group (__X32_SYSCALL_BIT + 231) +#define __NR_epoll_wait (__X32_SYSCALL_BIT + 232) +#define __NR_epoll_ctl (__X32_SYSCALL_BIT + 233) +#define __NR_tgkill (__X32_SYSCALL_BIT + 234) +#define __NR_utimes (__X32_SYSCALL_BIT + 235) +#define __NR_mbind (__X32_SYSCALL_BIT + 237) +#define __NR_set_mempolicy (__X32_SYSCALL_BIT + 238) +#define __NR_get_mempolicy (__X32_SYSCALL_BIT + 239) +#define __NR_mq_open (__X32_SYSCALL_BIT + 240) +#define __NR_mq_unlink (__X32_SYSCALL_BIT + 241) +#define __NR_mq_timedsend (__X32_SYSCALL_BIT + 242) +#define __NR_mq_timedreceive (__X32_SYSCALL_BIT + 243) +#define __NR_mq_getsetattr (__X32_SYSCALL_BIT + 245) +#define __NR_add_key (__X32_SYSCALL_BIT + 248) +#define __NR_request_key (__X32_SYSCALL_BIT + 249) +#define __NR_keyctl (__X32_SYSCALL_BIT + 250) +#define __NR_ioprio_set (__X32_SYSCALL_BIT + 251) +#define __NR_ioprio_get (__X32_SYSCALL_BIT + 252) +#define __NR_inotify_init (__X32_SYSCALL_BIT + 253) +#define __NR_inotify_add_watch (__X32_SYSCALL_BIT + 254) +#define __NR_inotify_rm_watch (__X32_SYSCALL_BIT + 255) +#define __NR_migrate_pages (__X32_SYSCALL_BIT + 256) +#define __NR_openat (__X32_SYSCALL_BIT + 257) +#define __NR_mkdirat (__X32_SYSCALL_BIT + 258) +#define __NR_mknodat (__X32_SYSCALL_BIT + 259) +#define __NR_fchownat (__X32_SYSCALL_BIT + 260) +#define __NR_futimesat (__X32_SYSCALL_BIT + 261) +#define __NR_newfstatat (__X32_SYSCALL_BIT + 262) +#define __NR_unlinkat (__X32_SYSCALL_BIT + 263) +#define __NR_renameat (__X32_SYSCALL_BIT + 264) +#define __NR_linkat (__X32_SYSCALL_BIT + 265) +#define __NR_symlinkat (__X32_SYSCALL_BIT + 266) +#define __NR_readlinkat (__X32_SYSCALL_BIT + 267) +#define __NR_fchmodat (__X32_SYSCALL_BIT + 268) +#define __NR_faccessat (__X32_SYSCALL_BIT + 269) +#define __NR_pselect6 (__X32_SYSCALL_BIT + 270) +#define __NR_ppoll (__X32_SYSCALL_BIT + 271) +#define __NR_unshare (__X32_SYSCALL_BIT + 272) +#define __NR_splice (__X32_SYSCALL_BIT + 275) +#define __NR_tee (__X32_SYSCALL_BIT + 276) +#define __NR_sync_file_range (__X32_SYSCALL_BIT + 277) +#define __NR_utimensat (__X32_SYSCALL_BIT + 280) +#define __NR_epoll_pwait (__X32_SYSCALL_BIT + 281) +#define __NR_signalfd (__X32_SYSCALL_BIT + 282) +#define __NR_timerfd_create (__X32_SYSCALL_BIT + 283) +#define __NR_eventfd (__X32_SYSCALL_BIT + 284) +#define __NR_fallocate (__X32_SYSCALL_BIT + 285) +#define __NR_timerfd_settime (__X32_SYSCALL_BIT + 286) +#define __NR_timerfd_gettime (__X32_SYSCALL_BIT + 287) +#define __NR_accept4 (__X32_SYSCALL_BIT + 288) +#define __NR_signalfd4 (__X32_SYSCALL_BIT + 289) +#define __NR_eventfd2 (__X32_SYSCALL_BIT + 290) +#define __NR_epoll_create1 (__X32_SYSCALL_BIT + 291) +#define __NR_dup3 (__X32_SYSCALL_BIT + 292) +#define __NR_pipe2 (__X32_SYSCALL_BIT + 293) +#define __NR_inotify_init1 (__X32_SYSCALL_BIT + 294) +#define __NR_perf_event_open (__X32_SYSCALL_BIT + 298) +#define __NR_fanotify_init (__X32_SYSCALL_BIT + 300) +#define __NR_fanotify_mark (__X32_SYSCALL_BIT + 301) +#define __NR_prlimit64 (__X32_SYSCALL_BIT + 302) +#define __NR_name_to_handle_at (__X32_SYSCALL_BIT + 303) +#define __NR_open_by_handle_at (__X32_SYSCALL_BIT + 304) +#define __NR_clock_adjtime (__X32_SYSCALL_BIT + 305) +#define __NR_syncfs (__X32_SYSCALL_BIT + 306) +#define __NR_setns (__X32_SYSCALL_BIT + 308) +#define __NR_getcpu (__X32_SYSCALL_BIT + 309) +#define __NR_kcmp (__X32_SYSCALL_BIT + 312) +#define __NR_finit_module (__X32_SYSCALL_BIT + 313) +#define __NR_sched_setattr (__X32_SYSCALL_BIT + 314) +#define __NR_sched_getattr (__X32_SYSCALL_BIT + 315) +#define __NR_renameat2 (__X32_SYSCALL_BIT + 316) +#define __NR_seccomp (__X32_SYSCALL_BIT + 317) +#define __NR_getrandom (__X32_SYSCALL_BIT + 318) +#define __NR_memfd_create (__X32_SYSCALL_BIT + 319) +#define __NR_kexec_file_load (__X32_SYSCALL_BIT + 320) +#define __NR_bpf (__X32_SYSCALL_BIT + 321) +#define __NR_userfaultfd (__X32_SYSCALL_BIT + 323) +#define __NR_membarrier (__X32_SYSCALL_BIT + 324) +#define __NR_mlock2 (__X32_SYSCALL_BIT + 325) +#define __NR_copy_file_range (__X32_SYSCALL_BIT + 326) +#define __NR_rt_sigaction (__X32_SYSCALL_BIT + 512) +#define __NR_rt_sigreturn (__X32_SYSCALL_BIT + 513) +#define __NR_ioctl (__X32_SYSCALL_BIT + 514) +#define __NR_readv (__X32_SYSCALL_BIT + 515) +#define __NR_writev (__X32_SYSCALL_BIT + 516) +#define __NR_recvfrom (__X32_SYSCALL_BIT + 517) +#define __NR_sendmsg (__X32_SYSCALL_BIT + 518) +#define __NR_recvmsg (__X32_SYSCALL_BIT + 519) +#define __NR_execve (__X32_SYSCALL_BIT + 520) +#define __NR_ptrace (__X32_SYSCALL_BIT + 521) +#define __NR_rt_sigpending (__X32_SYSCALL_BIT + 522) +#define __NR_rt_sigtimedwait (__X32_SYSCALL_BIT + 523) +#define __NR_rt_sigqueueinfo (__X32_SYSCALL_BIT + 524) +#define __NR_sigaltstack (__X32_SYSCALL_BIT + 525) +#define __NR_timer_create (__X32_SYSCALL_BIT + 526) +#define __NR_mq_notify (__X32_SYSCALL_BIT + 527) +#define __NR_kexec_load (__X32_SYSCALL_BIT + 528) +#define __NR_waitid (__X32_SYSCALL_BIT + 529) +#define __NR_set_robust_list (__X32_SYSCALL_BIT + 530) +#define __NR_get_robust_list (__X32_SYSCALL_BIT + 531) +#define __NR_vmsplice (__X32_SYSCALL_BIT + 532) +#define __NR_move_pages (__X32_SYSCALL_BIT + 533) +#define __NR_preadv (__X32_SYSCALL_BIT + 534) +#define __NR_pwritev (__X32_SYSCALL_BIT + 535) +#define __NR_rt_tgsigqueueinfo (__X32_SYSCALL_BIT + 536) +#define __NR_recvmmsg (__X32_SYSCALL_BIT + 537) +#define __NR_sendmmsg (__X32_SYSCALL_BIT + 538) +#define __NR_process_vm_readv (__X32_SYSCALL_BIT + 539) +#define __NR_process_vm_writev (__X32_SYSCALL_BIT + 540) +#define __NR_setsockopt (__X32_SYSCALL_BIT + 541) +#define __NR_getsockopt (__X32_SYSCALL_BIT + 542) +#define __NR_io_setup (__X32_SYSCALL_BIT + 543) +#define __NR_io_submit (__X32_SYSCALL_BIT + 544) +#define __NR_execveat (__X32_SYSCALL_BIT + 545) + +#endif /* _ASM_X86_UNISTD_X32_H */ diff --git a/qemu/linux-headers/linux/kvm.h b/qemu/linux-headers/linux/kvm.h index 3bac8736d..3bae71a87 100644 --- a/qemu/linux-headers/linux/kvm.h +++ b/qemu/linux-headers/linux/kvm.h @@ -154,6 +154,26 @@ struct kvm_s390_skeys { __u32 flags; __u32 reserved[9]; }; + +struct kvm_hyperv_exit { +#define KVM_EXIT_HYPERV_SYNIC 1 +#define KVM_EXIT_HYPERV_HCALL 2 + __u32 type; + union { + struct { + __u32 msr; + __u64 control; + __u64 evt_page; + __u64 msg_page; + } synic; + struct { + __u64 input; + __u64 result; + __u64 params[2]; + } hcall; + } u; +}; + #define KVM_S390_GET_SKEYS_NONE 1 #define KVM_S390_SKEYS_MAX 1048576 @@ -183,6 +203,8 @@ struct kvm_s390_skeys { #define KVM_EXIT_EPR 23 #define KVM_EXIT_SYSTEM_EVENT 24 #define KVM_EXIT_S390_STSI 25 +#define KVM_EXIT_IOAPIC_EOI 26 +#define KVM_EXIT_HYPERV 27 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -237,6 +259,7 @@ struct kvm_run { __u32 count; __u64 data_offset; /* relative to kvm_run start */ } io; + /* KVM_EXIT_DEBUG */ struct { struct kvm_debug_exit_arch arch; } debug; @@ -285,6 +308,7 @@ struct kvm_run { __u32 data; __u8 is_write; } dcr; + /* KVM_EXIT_INTERNAL_ERROR */ struct { __u32 suberror; /* Available with KVM_CAP_INTERNAL_ERROR_DATA: */ @@ -295,6 +319,7 @@ struct kvm_run { struct { __u64 gprs[32]; } osi; + /* KVM_EXIT_PAPR_HCALL */ struct { __u64 nr; __u64 ret; @@ -317,6 +342,7 @@ struct kvm_run { struct { #define KVM_SYSTEM_EVENT_SHUTDOWN 1 #define KVM_SYSTEM_EVENT_RESET 2 +#define KVM_SYSTEM_EVENT_CRASH 3 __u32 type; __u64 flags; } system_event; @@ -329,6 +355,12 @@ struct kvm_run { __u8 sel1; __u16 sel2; } s390_stsi; + /* KVM_EXIT_IOAPIC_EOI */ + struct { + __u8 vector; + } eoi; + /* KVM_EXIT_HYPERV */ + struct kvm_hyperv_exit hyperv; /* Fix the size of the union. */ char padding[256]; }; @@ -481,6 +513,7 @@ struct kvm_s390_psw { ((ai) << 26)) #define KVM_S390_INT_IO_MIN 0x00000000u #define KVM_S390_INT_IO_MAX 0xfffdffffu +#define KVM_S390_INT_IO_AI_MASK 0x04000000u struct kvm_s390_interrupt { @@ -514,7 +547,13 @@ struct kvm_s390_pgm_info { __u8 exc_access_id; __u8 per_access_id; __u8 op_access_id; - __u8 pad[3]; +#define KVM_S390_PGM_FLAGS_ILC_VALID 0x01 +#define KVM_S390_PGM_FLAGS_ILC_0 0x02 +#define KVM_S390_PGM_FLAGS_ILC_1 0x04 +#define KVM_S390_PGM_FLAGS_ILC_MASK 0x06 +#define KVM_S390_PGM_FLAGS_NO_REWIND 0x08 + __u8 flags; + __u8 pad[2]; }; struct kvm_s390_prefix_info { @@ -817,6 +856,15 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_DISABLE_QUIRKS 116 #define KVM_CAP_X86_SMM 117 #define KVM_CAP_MULTI_ADDRESS_SPACE 118 +#define KVM_CAP_GUEST_DEBUG_HW_BPS 119 +#define KVM_CAP_GUEST_DEBUG_HW_WPS 120 +#define KVM_CAP_SPLIT_IRQCHIP 121 +#define KVM_CAP_IOEVENTFD_ANY_LENGTH 122 +#define KVM_CAP_HYPERV_SYNIC 123 +#define KVM_CAP_S390_RI 124 +#define KVM_CAP_SPAPR_TCE_64 125 +#define KVM_CAP_ARM_PMU_V3 126 +#define KVM_CAP_VCPU_ATTRIBUTES 127 #ifdef KVM_CAP_IRQ_ROUTING @@ -840,10 +888,16 @@ struct kvm_irq_routing_s390_adapter { __u32 adapter_id; }; +struct kvm_irq_routing_hv_sint { + __u32 vcpu; + __u32 sint; +}; + /* gsi routing entry types */ #define KVM_IRQ_ROUTING_IRQCHIP 1 #define KVM_IRQ_ROUTING_MSI 2 #define KVM_IRQ_ROUTING_S390_ADAPTER 3 +#define KVM_IRQ_ROUTING_HV_SINT 4 struct kvm_irq_routing_entry { __u32 gsi; @@ -854,6 +908,7 @@ struct kvm_irq_routing_entry { struct kvm_irq_routing_irqchip irqchip; struct kvm_irq_routing_msi msi; struct kvm_irq_routing_s390_adapter adapter; + struct kvm_irq_routing_hv_sint hv_sint; __u32 pad[8]; } u; }; @@ -1102,6 +1157,8 @@ struct kvm_s390_ucas_mapping { /* Available with KVM_CAP_PPC_ALLOC_HTAB */ #define KVM_PPC_ALLOCATE_HTAB _IOWR(KVMIO, 0xa7, __u32) #define KVM_CREATE_SPAPR_TCE _IOW(KVMIO, 0xa8, struct kvm_create_spapr_tce) +#define KVM_CREATE_SPAPR_TCE_64 _IOW(KVMIO, 0xa8, \ + struct kvm_create_spapr_tce_64) /* Available with KVM_CAP_RMA */ #define KVM_ALLOCATE_RMA _IOR(KVMIO, 0xa9, struct kvm_allocate_rma) /* Available with KVM_CAP_PPC_HTAB_FD */ diff --git a/qemu/linux-headers/linux/psci.h b/qemu/linux-headers/linux/psci.h index 5a7676307..08d443f7c 100644 --- a/qemu/linux-headers/linux/psci.h +++ b/qemu/linux-headers/linux/psci.h @@ -46,6 +46,11 @@ #define PSCI_0_2_FN64_MIGRATE PSCI_0_2_FN64(5) #define PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU PSCI_0_2_FN64(7) +#define PSCI_1_0_FN_PSCI_FEATURES PSCI_0_2_FN(10) +#define PSCI_1_0_FN_SYSTEM_SUSPEND PSCI_0_2_FN(14) + +#define PSCI_1_0_FN64_SYSTEM_SUSPEND PSCI_0_2_FN64(14) + /* PSCI v0.2 power state encoding for CPU_SUSPEND function */ #define PSCI_0_2_POWER_STATE_ID_MASK 0xffff #define PSCI_0_2_POWER_STATE_ID_SHIFT 0 @@ -56,6 +61,13 @@ #define PSCI_0_2_POWER_STATE_AFFL_MASK \ (0x3 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) +/* PSCI extended power state encoding for CPU_SUSPEND function */ +#define PSCI_1_0_EXT_POWER_STATE_ID_MASK 0xfffffff +#define PSCI_1_0_EXT_POWER_STATE_ID_SHIFT 0 +#define PSCI_1_0_EXT_POWER_STATE_TYPE_SHIFT 30 +#define PSCI_1_0_EXT_POWER_STATE_TYPE_MASK \ + (0x1 << PSCI_1_0_EXT_POWER_STATE_TYPE_SHIFT) + /* PSCI v0.2 affinity level state returned by AFFINITY_INFO */ #define PSCI_0_2_AFFINITY_LEVEL_ON 0 #define PSCI_0_2_AFFINITY_LEVEL_OFF 1 @@ -76,6 +88,11 @@ #define PSCI_VERSION_MINOR(ver) \ ((ver) & PSCI_VERSION_MINOR_MASK) +/* PSCI features decoding (>=1.0) */ +#define PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT 1 +#define PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK \ + (0x1 << PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT) + /* PSCI return values (inclusive of all PSCI versions) */ #define PSCI_RET_SUCCESS 0 #define PSCI_RET_NOT_SUPPORTED -1 @@ -86,5 +103,6 @@ #define PSCI_RET_INTERNAL_FAILURE -6 #define PSCI_RET_NOT_PRESENT -7 #define PSCI_RET_DISABLED -8 +#define PSCI_RET_INVALID_ADDRESS -9 #endif /* _LINUX_PSCI_H */ diff --git a/qemu/linux-headers/linux/userfaultfd.h b/qemu/linux-headers/linux/userfaultfd.h new file mode 100644 index 000000000..19e845324 --- /dev/null +++ b/qemu/linux-headers/linux/userfaultfd.h @@ -0,0 +1,167 @@ +/* + * include/linux/userfaultfd.h + * + * Copyright (C) 2007 Davide Libenzi + * Copyright (C) 2015 Red Hat, Inc. + * + */ + +#ifndef _LINUX_USERFAULTFD_H +#define _LINUX_USERFAULTFD_H + +#include + +#define UFFD_API ((__u64)0xAA) +/* + * After implementing the respective features it will become: + * #define UFFD_API_FEATURES (UFFD_FEATURE_PAGEFAULT_FLAG_WP | \ + * UFFD_FEATURE_EVENT_FORK) + */ +#define UFFD_API_FEATURES (0) +#define UFFD_API_IOCTLS \ + ((__u64)1 << _UFFDIO_REGISTER | \ + (__u64)1 << _UFFDIO_UNREGISTER | \ + (__u64)1 << _UFFDIO_API) +#define UFFD_API_RANGE_IOCTLS \ + ((__u64)1 << _UFFDIO_WAKE | \ + (__u64)1 << _UFFDIO_COPY | \ + (__u64)1 << _UFFDIO_ZEROPAGE) + +/* + * Valid ioctl command number range with this API is from 0x00 to + * 0x3F. UFFDIO_API is the fixed number, everything else can be + * changed by implementing a different UFFD_API. If sticking to the + * same UFFD_API more ioctl can be added and userland will be aware of + * which ioctl the running kernel implements through the ioctl command + * bitmask written by the UFFDIO_API. + */ +#define _UFFDIO_REGISTER (0x00) +#define _UFFDIO_UNREGISTER (0x01) +#define _UFFDIO_WAKE (0x02) +#define _UFFDIO_COPY (0x03) +#define _UFFDIO_ZEROPAGE (0x04) +#define _UFFDIO_API (0x3F) + +/* userfaultfd ioctl ids */ +#define UFFDIO 0xAA +#define UFFDIO_API _IOWR(UFFDIO, _UFFDIO_API, \ + struct uffdio_api) +#define UFFDIO_REGISTER _IOWR(UFFDIO, _UFFDIO_REGISTER, \ + struct uffdio_register) +#define UFFDIO_UNREGISTER _IOR(UFFDIO, _UFFDIO_UNREGISTER, \ + struct uffdio_range) +#define UFFDIO_WAKE _IOR(UFFDIO, _UFFDIO_WAKE, \ + struct uffdio_range) +#define UFFDIO_COPY _IOWR(UFFDIO, _UFFDIO_COPY, \ + struct uffdio_copy) +#define UFFDIO_ZEROPAGE _IOWR(UFFDIO, _UFFDIO_ZEROPAGE, \ + struct uffdio_zeropage) + +/* read() structure */ +struct uffd_msg { + __u8 event; + + __u8 reserved1; + __u16 reserved2; + __u32 reserved3; + + union { + struct { + __u64 flags; + __u64 address; + } pagefault; + + struct { + /* unused reserved fields */ + __u64 reserved1; + __u64 reserved2; + __u64 reserved3; + } reserved; + } arg; +} __attribute__((packed)); + +/* + * Start at 0x12 and not at 0 to be more strict against bugs. + */ +#define UFFD_EVENT_PAGEFAULT 0x12 +#if 0 /* not available yet */ +#define UFFD_EVENT_FORK 0x13 +#endif + +/* flags for UFFD_EVENT_PAGEFAULT */ +#define UFFD_PAGEFAULT_FLAG_WRITE (1<<0) /* If this was a write fault */ +#define UFFD_PAGEFAULT_FLAG_WP (1<<1) /* If reason is VM_UFFD_WP */ + +struct uffdio_api { + /* userland asks for an API number and the features to enable */ + __u64 api; + /* + * Kernel answers below with the all available features for + * the API, this notifies userland of which events and/or + * which flags for each event are enabled in the current + * kernel. + * + * Note: UFFD_EVENT_PAGEFAULT and UFFD_PAGEFAULT_FLAG_WRITE + * are to be considered implicitly always enabled in all kernels as + * long as the uffdio_api.api requested matches UFFD_API. + */ +#if 0 /* not available yet */ +#define UFFD_FEATURE_PAGEFAULT_FLAG_WP (1<<0) +#define UFFD_FEATURE_EVENT_FORK (1<<1) +#endif + __u64 features; + + __u64 ioctls; +}; + +struct uffdio_range { + __u64 start; + __u64 len; +}; + +struct uffdio_register { + struct uffdio_range range; +#define UFFDIO_REGISTER_MODE_MISSING ((__u64)1<<0) +#define UFFDIO_REGISTER_MODE_WP ((__u64)1<<1) + __u64 mode; + + /* + * kernel answers which ioctl commands are available for the + * range, keep at the end as the last 8 bytes aren't read. + */ + __u64 ioctls; +}; + +struct uffdio_copy { + __u64 dst; + __u64 src; + __u64 len; + /* + * There will be a wrprotection flag later that allows to map + * pages wrprotected on the fly. And such a flag will be + * available if the wrprotection ioctl are implemented for the + * range according to the uffdio_register.ioctls. + */ +#define UFFDIO_COPY_MODE_DONTWAKE ((__u64)1<<0) + __u64 mode; + + /* + * "copy" is written by the ioctl and must be at the end: the + * copy_from_user will not read the last 8 bytes. + */ + __s64 copy; +}; + +struct uffdio_zeropage { + struct uffdio_range range; +#define UFFDIO_ZEROPAGE_MODE_DONTWAKE ((__u64)1<<0) + __u64 mode; + + /* + * "zeropage" is written by the ioctl and must be at the end: + * the copy_from_user will not read the last 8 bytes. + */ + __s64 zeropage; +}; + +#endif /* _LINUX_USERFAULTFD_H */ diff --git a/qemu/linux-headers/linux/vfio.h b/qemu/linux-headers/linux/vfio.h index aa276bce3..759b850a3 100644 --- a/qemu/linux-headers/linux/vfio.h +++ b/qemu/linux-headers/linux/vfio.h @@ -38,6 +38,13 @@ #define VFIO_SPAPR_TCE_v2_IOMMU 7 +/* + * The No-IOMMU IOMMU offers no translation or isolation for devices and + * supports no ioctls outside of VFIO_CHECK_EXTENSION. Use of VFIO's No-IOMMU + * code will taint the host kernel and should be used with extreme caution. + */ +#define VFIO_NOIOMMU_IOMMU 8 + /* * The IOCTL interface is designed for extensibility by embedding the * structure length (argsz) and flags into structures passed between @@ -52,6 +59,33 @@ #define VFIO_TYPE (';') #define VFIO_BASE 100 +/* + * For extension of INFO ioctls, VFIO makes use of a capability chain + * designed after PCI/e capabilities. A flag bit indicates whether + * this capability chain is supported and a field defined in the fixed + * structure defines the offset of the first capability in the chain. + * This field is only valid when the corresponding bit in the flags + * bitmap is set. This offset field is relative to the start of the + * INFO buffer, as is the next field within each capability header. + * The id within the header is a shared address space per INFO ioctl, + * while the version field is specific to the capability id. The + * contents following the header are specific to the capability id. + */ +struct vfio_info_cap_header { + __u16 id; /* Identifies capability */ + __u16 version; /* Version specific to the capability ID */ + __u32 next; /* Offset of next capability */ +}; + +/* + * Callers of INFO ioctls passing insufficiently sized buffers will see + * the capability chain flag bit set, a zero value for the first capability + * offset (if available within the provided argsz), and argsz will be + * updated to report the necessary buffer size. For compatibility, the + * INFO ioctl will not report error in this case, but the capability chain + * will not be available. + */ + /* -------- IOCTLs for VFIO file descriptor (/dev/vfio/vfio) -------- */ /** @@ -187,13 +221,73 @@ struct vfio_region_info { #define VFIO_REGION_INFO_FLAG_READ (1 << 0) /* Region supports read */ #define VFIO_REGION_INFO_FLAG_WRITE (1 << 1) /* Region supports write */ #define VFIO_REGION_INFO_FLAG_MMAP (1 << 2) /* Region supports mmap */ +#define VFIO_REGION_INFO_FLAG_CAPS (1 << 3) /* Info supports caps */ __u32 index; /* Region index */ - __u32 resv; /* Reserved for alignment */ + __u32 cap_offset; /* Offset within info struct of first cap */ __u64 size; /* Region size (bytes) */ __u64 offset; /* Region offset from start of device fd */ }; #define VFIO_DEVICE_GET_REGION_INFO _IO(VFIO_TYPE, VFIO_BASE + 8) +/* + * The sparse mmap capability allows finer granularity of specifying areas + * within a region with mmap support. When specified, the user should only + * mmap the offset ranges specified by the areas array. mmaps outside of the + * areas specified may fail (such as the range covering a PCI MSI-X table) or + * may result in improper device behavior. + * + * The structures below define version 1 of this capability. + */ +#define VFIO_REGION_INFO_CAP_SPARSE_MMAP 1 + +struct vfio_region_sparse_mmap_area { + __u64 offset; /* Offset of mmap'able area within region */ + __u64 size; /* Size of mmap'able area */ +}; + +struct vfio_region_info_cap_sparse_mmap { + struct vfio_info_cap_header header; + __u32 nr_areas; + __u32 reserved; + struct vfio_region_sparse_mmap_area areas[]; +}; + +/* + * The device specific type capability allows regions unique to a specific + * device or class of devices to be exposed. This helps solve the problem for + * vfio bus drivers of defining which region indexes correspond to which region + * on the device, without needing to resort to static indexes, as done by + * vfio-pci. For instance, if we were to go back in time, we might remove + * VFIO_PCI_VGA_REGION_INDEX and let vfio-pci simply define that all indexes + * greater than or equal to VFIO_PCI_NUM_REGIONS are device specific and we'd + * make a "VGA" device specific type to describe the VGA access space. This + * means that non-VGA devices wouldn't need to waste this index, and thus the + * address space associated with it due to implementation of device file + * descriptor offsets in vfio-pci. + * + * The current implementation is now part of the user ABI, so we can't use this + * for VGA, but there are other upcoming use cases, such as opregions for Intel + * IGD devices and framebuffers for vGPU devices. We missed VGA, but we'll + * use this for future additions. + * + * The structure below defines version 1 of this capability. + */ +#define VFIO_REGION_INFO_CAP_TYPE 2 + +struct vfio_region_info_cap_type { + struct vfio_info_cap_header header; + __u32 type; /* global per bus driver */ + __u32 subtype; /* type specific */ +}; + +#define VFIO_REGION_TYPE_PCI_VENDOR_TYPE (1 << 31) +#define VFIO_REGION_TYPE_PCI_VENDOR_MASK (0xffff) + +/* 8086 Vendor sub-types */ +#define VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION (1) +#define VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG (2) +#define VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG (3) + /** * VFIO_DEVICE_GET_IRQ_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 9, * struct vfio_irq_info) @@ -329,7 +423,8 @@ enum { * between described ranges are unimplemented. */ VFIO_PCI_VGA_REGION_INDEX, - VFIO_PCI_NUM_REGIONS + VFIO_PCI_NUM_REGIONS = 9 /* Fixed user ABI, region indexes >=9 use */ + /* device specific cap to define content. */ }; enum { @@ -568,8 +663,10 @@ struct vfio_iommu_spapr_tce_create { __u32 flags; /* in */ __u32 page_shift; + __u32 __resv1; __u64 window_size; __u32 levels; + __u32 __resv2; /* out */ __u64 start_addr; }; diff --git a/qemu/linux-headers/linux/vhost.h b/qemu/linux-headers/linux/vhost.h index ead86db91..571294cea 100644 --- a/qemu/linux-headers/linux/vhost.h +++ b/qemu/linux-headers/linux/vhost.h @@ -126,6 +126,12 @@ struct vhost_memory { #define VHOST_SET_VRING_CALL _IOW(VHOST_VIRTIO, 0x21, struct vhost_vring_file) /* Set eventfd to signal an error */ #define VHOST_SET_VRING_ERR _IOW(VHOST_VIRTIO, 0x22, struct vhost_vring_file) +/* Set busy loop timeout (in us) */ +#define VHOST_SET_VRING_BUSYLOOP_TIMEOUT _IOW(VHOST_VIRTIO, 0x23, \ + struct vhost_vring_state) +/* Get busy loop timeout (in us) */ +#define VHOST_GET_VRING_BUSYLOOP_TIMEOUT _IOW(VHOST_VIRTIO, 0x24, \ + struct vhost_vring_state) /* VHOST_NET specific defines */ diff --git a/qemu/linux-user/aarch64/syscall.h b/qemu/linux-user/aarch64/syscall.h deleted file mode 100644 index dc72a15c5..000000000 --- a/qemu/linux-user/aarch64/syscall.h +++ /dev/null @@ -1,13 +0,0 @@ -struct target_pt_regs { - uint64_t regs[31]; - uint64_t sp; - uint64_t pc; - uint64_t pstate; -}; - -#define UNAME_MACHINE "aarch64" -#define UNAME_MINIMUM_RELEASE "3.8.0" -#define TARGET_CLONE_BACKWARDS -#define TARGET_MINSIGSTKSZ 2048 -#define TARGET_MLOCKALL_MCL_CURRENT 1 -#define TARGET_MLOCKALL_MCL_FUTURE 2 diff --git a/qemu/linux-user/aarch64/syscall_nr.h b/qemu/linux-user/aarch64/syscall_nr.h index 743255db0..59511d855 100644 --- a/qemu/linux-user/aarch64/syscall_nr.h +++ b/qemu/linux-user/aarch64/syscall_nr.h @@ -262,62 +262,16 @@ #define TARGET_NR_process_vm_writev 271 #define TARGET_NR_kcmp 272 #define TARGET_NR_finit_module 273 -#define TARGET_NR_open 1024 -#define TARGET_NR_link 1025 -#define TARGET_NR_unlink 1026 -#define TARGET_NR_mknod 1027 -#define TARGET_NR_chmod 1028 -#define TARGET_NR_chown 1029 -#define TARGET_NR_mkdir 1030 -#define TARGET_NR_rmdir 1031 -#define TARGET_NR_lchown 1032 -#define TARGET_NR_access 1033 -#define TARGET_NR_rename 1034 -#define TARGET_NR_readlink 1035 -#define TARGET_NR_symlink 1036 -#define TARGET_NR_utimes 1037 -#define TARGET_NR_stat 1038 -#define TARGET_NR_lstat 1039 -#define TARGET_NR_pipe 1040 -#define TARGET_NR_dup2 1041 -#define TARGET_NR_epoll_create 1042 -#define TARGET_NR_inotify_init 1043 -#define TARGET_NR_eventfd 1044 -#define TARGET_NR_signalfd 1045 -#define TARGET_NR_sendfile64 1046 -#define TARGET_NR_ftruncate64 1047 -#define TARGET_NR_truncate64 1048 -#define TARGET_NR_stat64 1049 -#define TARGET_NR_lstat64 1050 -#define TARGET_NR_fstat64 1051 -#define TARGET_NR_fcntl64 1052 -/* #define TARGET_NR_fadvise64 1053 */ -#define TARGET_NR_newfstatat 1054 -#define TARGET_NR_fstatfs64 1055 -#define TARGET_NR_statfs64 1056 -#define TARGET_NR_lseek64 1057 -#define TARGET_NR_mmap64 1058 -#define TARGET_NR_alarm 1059 -#define TARGET_NR_getpgrp 1060 -#define TARGET_NR_pause 1061 -#define TARGET_NR_time 1062 -#define TARGET_NR_utime 1063 -#define TARGET_NR_creat 1064 -#define TARGET_NR_getdents 1065 -#define TARGET_NR_futimesat 1066 -#define TARGET_NR_select 1067 -#define TARGET_NR_poll 1068 -#define TARGET_NR_epoll_wait 1069 -#define TARGET_NR_ustat 1070 -#define TARGET_NR_vfork 1071 -#define TARGET_NR_oldwait4 1072 -#define TARGET_NR_recv 1073 -#define TARGET_NR_send 1074 -#define TARGET_NR_bdflush 1075 -#define TARGET_NR_umount 1076 -#define TARGET_NR_uselib 1077 -#define TARGET_NR__sysctl 1078 -#define TARGET_NR_fork 1079 -#define TARGET_NR_syscalls (__NR_fork+1) +#define TARGET_NR_sched_setattr 274 +#define TARGET_NR_sched_getattr 275 +#define TARGET_NR_renameat2 276 +#define TARGET_NR_seccomp 277 +#define TARGET_NR_getrandom 278 +#define TARGET_NR_memfd_create 279 +#define TARGET_NR_bpf 280 +#define TARGET_NR_execveat 281 +#define TARGET_NR_userfaultfd 282 +#define TARGET_NR_membarrier 283 +#define TARGET_NR_mlock2 284 +#define TARGET_NR_copy_file_range 285 -#define TARGET_NR_sigreturn 1999 diff --git a/qemu/linux-user/aarch64/target_syscall.h b/qemu/linux-user/aarch64/target_syscall.h new file mode 100644 index 000000000..f45801804 --- /dev/null +++ b/qemu/linux-user/aarch64/target_syscall.h @@ -0,0 +1,18 @@ +#ifndef TARGET_SYSCALL_H +#define TARGET_SYSCALL_H + +struct target_pt_regs { + uint64_t regs[31]; + uint64_t sp; + uint64_t pc; + uint64_t pstate; +}; + +#define UNAME_MACHINE "aarch64" +#define UNAME_MINIMUM_RELEASE "3.8.0" +#define TARGET_CLONE_BACKWARDS +#define TARGET_MINSIGSTKSZ 2048 +#define TARGET_MLOCKALL_MCL_CURRENT 1 +#define TARGET_MLOCKALL_MCL_FUTURE 2 + +#endif /* TARGET_SYSCALL_H */ diff --git a/qemu/linux-user/alpha/syscall.h b/qemu/linux-user/alpha/syscall.h deleted file mode 100644 index 245cff254..000000000 --- a/qemu/linux-user/alpha/syscall.h +++ /dev/null @@ -1,257 +0,0 @@ -/* default linux values for the selectors */ -#define __USER_DS (1) - -struct target_pt_regs { - abi_ulong r0; - abi_ulong r1; - abi_ulong r2; - abi_ulong r3; - abi_ulong r4; - abi_ulong r5; - abi_ulong r6; - abi_ulong r7; - abi_ulong r8; - abi_ulong r19; - abi_ulong r20; - abi_ulong r21; - abi_ulong r22; - abi_ulong r23; - abi_ulong r24; - abi_ulong r25; - abi_ulong r26; - abi_ulong r27; - abi_ulong r28; - abi_ulong hae; -/* JRP - These are the values provided to a0-a2 by PALcode */ - abi_ulong trap_a0; - abi_ulong trap_a1; - abi_ulong trap_a2; -/* These are saved by PAL-code: */ - abi_ulong ps; - abi_ulong pc; - abi_ulong gp; - abi_ulong r16; - abi_ulong r17; - abi_ulong r18; -/* Those is needed by qemu to temporary store the user stack pointer */ - abi_ulong usp; - abi_ulong unique; -}; - -#define UNAME_MACHINE "alpha" -#define UNAME_MINIMUM_RELEASE "2.6.32" - -#undef TARGET_EDEADLK -#define TARGET_EDEADLK 11 -#undef TARGET_EAGAIN -#define TARGET_EAGAIN 35 -#undef TARGET_EINPROGRESS -#define TARGET_EINPROGRESS 36 -#undef TARGET_EALREADY -#define TARGET_EALREADY 37 -#undef TARGET_ENOTSOCK -#define TARGET_ENOTSOCK 38 -#undef TARGET_EDESTADDRREQ -#define TARGET_EDESTADDRREQ 39 -#undef TARGET_EMSGSIZE -#define TARGET_EMSGSIZE 40 -#undef TARGET_EPROTOTYPE -#define TARGET_EPROTOTYPE 41 -#undef TARGET_ENOPROTOOPT -#define TARGET_ENOPROTOOPT 42 -#undef TARGET_EPROTONOSUPPORT -#define TARGET_EPROTONOSUPPORT 43 -#undef TARGET_ESOCKTNOSUPPORT -#define TARGET_ESOCKTNOSUPPORT 44 -#undef TARGET_EOPNOTSUPP -#define TARGET_EOPNOTSUPP 45 -#undef TARGET_EPFNOSUPPORT -#define TARGET_EPFNOSUPPORT 46 -#undef TARGET_EAFNOSUPPORT -#define TARGET_EAFNOSUPPORT 47 -#undef TARGET_EADDRINUSE -#define TARGET_EADDRINUSE 48 -#undef TARGET_EADDRNOTAVAIL -#define TARGET_EADDRNOTAVAIL 49 -#undef TARGET_ENETDOWN -#define TARGET_ENETDOWN 50 -#undef TARGET_ENETUNREACH -#define TARGET_ENETUNREACH 51 -#undef TARGET_ENETRESET -#define TARGET_ENETRESET 52 -#undef TARGET_ECONNABORTED -#define TARGET_ECONNABORTED 53 -#undef TARGET_ECONNRESET -#define TARGET_ECONNRESET 54 -#undef TARGET_ENOBUFS -#define TARGET_ENOBUFS 55 -#undef TARGET_EISCONN -#define TARGET_EISCONN 56 -#undef TARGET_ENOTCONN -#define TARGET_ENOTCONN 57 -#undef TARGET_ESHUTDOWN -#define TARGET_ESHUTDOWN 58 -#undef TARGET_ETOOMANYREFS -#define TARGET_ETOOMANYREFS 59 -#undef TARGET_ETIMEDOUT -#define TARGET_ETIMEDOUT 60 -#undef TARGET_ECONNREFUSED -#define TARGET_ECONNREFUSED 61 -#undef TARGET_ELOOP -#define TARGET_ELOOP 62 -#undef TARGET_ENAMETOOLONG -#define TARGET_ENAMETOOLONG 63 -#undef TARGET_EHOSTDOWN -#define TARGET_EHOSTDOWN 64 -#undef TARGET_EHOSTUNREACH -#define TARGET_EHOSTUNREACH 65 -#undef TARGET_ENOTEMPTY -#define TARGET_ENOTEMPTY 66 -// Unused 67 -#undef TARGET_EUSERS -#define TARGET_EUSERS 68 -#undef TARGET_EDQUOT -#define TARGET_EDQUOT 69 -#undef TARGET_ESTALE -#define TARGET_ESTALE 70 -#undef TARGET_EREMOTE -#define TARGET_EREMOTE 71 -// Unused 72-76 -#undef TARGET_ENOLCK -#define TARGET_ENOLCK 77 -#undef TARGET_ENOSYS -#define TARGET_ENOSYS 78 -// Unused 79 -#undef TARGET_ENOMSG -#define TARGET_ENOMSG 80 -#undef TARGET_EIDRM -#define TARGET_EIDRM 81 -#undef TARGET_ENOSR -#define TARGET_ENOSR 82 -#undef TARGET_ETIME -#define TARGET_ETIME 83 -#undef TARGET_EBADMSG -#define TARGET_EBADMSG 84 -#undef TARGET_EPROTO -#define TARGET_EPROTO 85 -#undef TARGET_ENODATA -#define TARGET_ENODATA 86 -#undef TARGET_ENOSTR -#define TARGET_ENOSTR 87 -#undef TARGET_ECHRNG -#define TARGET_ECHRNG 88 -#undef TARGET_EL2NSYNC -#define TARGET_EL2NSYNC 89 -#undef TARGET_EL3HLT -#define TARGET_EL3HLT 90 -#undef TARGET_EL3RST -#define TARGET_EL3RST 91 -#undef TARGET_ENOPKG -#define TARGET_ENOPKG 92 -#undef TARGET_ELNRNG -#define TARGET_ELNRNG 93 -#undef TARGET_EUNATCH -#define TARGET_EUNATCH 94 -#undef TARGET_ENOCSI -#define TARGET_ENOCSI 95 -#undef TARGET_EL2HLT -#define TARGET_EL2HLT 96 -#undef TARGET_EBADE -#define TARGET_EBADE 97 -#undef TARGET_EBADR -#define TARGET_EBADR 98 -#undef TARGET_EXFULL -#define TARGET_EXFULL 99 -#undef TARGET_ENOANO -#define TARGET_ENOANO 100 -#undef TARGET_EBADRQC -#define TARGET_EBADRQC 101 -#undef TARGET_EBADSLT -#define TARGET_EBADSLT 102 -// Unused 103 -#undef TARGET_EBFONT -#define TARGET_EBFONT 104 -#undef TARGET_ENONET -#define TARGET_ENONET 105 -#undef TARGET_ENOLINK -#define TARGET_ENOLINK 106 -#undef TARGET_EADV -#define TARGET_EADV 107 -#undef TARGET_ESRMNT -#define TARGET_ESRMNT 108 -#undef TARGET_ECOMM -#define TARGET_ECOMM 109 -#undef TARGET_EMULTIHOP -#define TARGET_EMULTIHOP 110 -#undef TARGET_EDOTDOT -#define TARGET_EDOTDOT 111 -#undef TARGET_EOVERFLOW -#define TARGET_EOVERFLOW 112 -#undef TARGET_ENOTUNIQ -#define TARGET_ENOTUNIQ 113 -#undef TARGET_EBADFD -#define TARGET_EBADFD 114 -#undef TARGET_EREMCHG -#define TARGET_EREMCHG 115 -#undef TARGET_EILSEQ -#define TARGET_EILSEQ 116 - -// Same as default 117-121 - -#undef TARGET_ELIBACC -#define TARGET_ELIBACC 122 -#undef TARGET_ELIBBAD -#define TARGET_ELIBBAD 123 -#undef TARGET_ELIBSCN -#define TARGET_ELIBSCN 124 -#undef TARGET_ELIBMAX -#define TARGET_ELIBMAX 125 -#undef TARGET_ELIBEXEC -#define TARGET_ELIBEXEC 126 -#undef TARGET_ERESTART -#define TARGET_ERESTART 127 -#undef TARGET_ESTRPIPE -#define TARGET_ESTRPIPE 128 -#undef TARGET_ENOMEDIUM -#define TARGET_ENOMEDIUM 129 -#undef TARGET_EMEDIUMTYPE -#define TARGET_EMEDIUMTYPE 130 -#undef TARGET_ECANCELED -#define TARGET_ECANCELED 131 -#undef TARGET_ENOKEY -#define TARGET_ENOKEY 132 -#undef TARGET_EKEYEXPIRED -#define TARGET_EKEYEXPIRED 133 -#undef TARGET_EKEYREVOKED -#define TARGET_EKEYREVOKED 134 -#undef TARGET_EKEYREJECTED -#define TARGET_EKEYREJECTED 135 -#undef TARGET_EOWNERDEAD -#define TARGET_EOWNERDEAD 136 -#undef TARGET_ENOTRECOVERABLE -#define TARGET_ENOTRECOVERABLE 137 -#undef TARGET_ERFKILL -#define TARGET_ERFKILL 138 - -// For sys_osf_getsysinfo -#define TARGET_GSI_UACPROC 8 -#define TARGET_GSI_IEEE_FP_CONTROL 45 -#define TARGET_GSI_IEEE_STATE_AT_SIGNAL 46 -#define TARGET_GSI_PROC_TYPE 60 -#define TARGET_GSI_GET_HWRPB 101 - -// For sys_ofs_setsysinfo -#define TARGET_SSI_NVPAIRS 1 -#define TARGET_SSI_IEEE_FP_CONTROL 14 -#define TARGET_SSI_IEEE_STATE_AT_SIGNAL 15 -#define TARGET_SSI_IEEE_IGNORE_STATE_AT_SIGNAL 16 -#define TARGET_SSI_IEEE_RAISE_EXCEPTION 1001 - -#define TARGET_SSIN_UACPROC 6 - -#define TARGET_UAC_NOPRINT 1 -#define TARGET_UAC_NOFIX 2 -#define TARGET_UAC_SIGBUS 4 -#define TARGET_MINSIGSTKSZ 4096 -#define TARGET_MLOCKALL_MCL_CURRENT 0x2000 -#define TARGET_MLOCKALL_MCL_FUTURE 0x4000 diff --git a/qemu/linux-user/alpha/syscall_nr.h b/qemu/linux-user/alpha/syscall_nr.h index dde8d5c6a..00e14bb6b 100644 --- a/qemu/linux-user/alpha/syscall_nr.h +++ b/qemu/linux-user/alpha/syscall_nr.h @@ -444,3 +444,9 @@ #define TARGET_NR_process_vm_writev 505 #define TARGET_NR_kcmp 506 #define TARGET_NR_finit_module 507 +#define TARGET_NR_sched_setattr 508 +#define TARGET_NR_sched_getattr 509 +#define TARGET_NR_renameat2 510 +#define TARGET_NR_getrandom 511 +#define TARGET_NR_memfd_create 512 +#define TARGET_NR_execveat 513 diff --git a/qemu/linux-user/alpha/target_syscall.h b/qemu/linux-user/alpha/target_syscall.h new file mode 100644 index 000000000..3db4b16f6 --- /dev/null +++ b/qemu/linux-user/alpha/target_syscall.h @@ -0,0 +1,262 @@ +#ifndef TARGET_SYSCALL_H +#define TARGET_SYSCALL_H + +/* default linux values for the selectors */ +#define __USER_DS (1) + +struct target_pt_regs { + abi_ulong r0; + abi_ulong r1; + abi_ulong r2; + abi_ulong r3; + abi_ulong r4; + abi_ulong r5; + abi_ulong r6; + abi_ulong r7; + abi_ulong r8; + abi_ulong r19; + abi_ulong r20; + abi_ulong r21; + abi_ulong r22; + abi_ulong r23; + abi_ulong r24; + abi_ulong r25; + abi_ulong r26; + abi_ulong r27; + abi_ulong r28; + abi_ulong hae; +/* JRP - These are the values provided to a0-a2 by PALcode */ + abi_ulong trap_a0; + abi_ulong trap_a1; + abi_ulong trap_a2; +/* These are saved by PAL-code: */ + abi_ulong ps; + abi_ulong pc; + abi_ulong gp; + abi_ulong r16; + abi_ulong r17; + abi_ulong r18; +/* Those is needed by qemu to temporary store the user stack pointer */ + abi_ulong usp; + abi_ulong unique; +}; + +#define UNAME_MACHINE "alpha" +#define UNAME_MINIMUM_RELEASE "2.6.32" + +#undef TARGET_EDEADLK +#define TARGET_EDEADLK 11 +#undef TARGET_EAGAIN +#define TARGET_EAGAIN 35 +#undef TARGET_EINPROGRESS +#define TARGET_EINPROGRESS 36 +#undef TARGET_EALREADY +#define TARGET_EALREADY 37 +#undef TARGET_ENOTSOCK +#define TARGET_ENOTSOCK 38 +#undef TARGET_EDESTADDRREQ +#define TARGET_EDESTADDRREQ 39 +#undef TARGET_EMSGSIZE +#define TARGET_EMSGSIZE 40 +#undef TARGET_EPROTOTYPE +#define TARGET_EPROTOTYPE 41 +#undef TARGET_ENOPROTOOPT +#define TARGET_ENOPROTOOPT 42 +#undef TARGET_EPROTONOSUPPORT +#define TARGET_EPROTONOSUPPORT 43 +#undef TARGET_ESOCKTNOSUPPORT +#define TARGET_ESOCKTNOSUPPORT 44 +#undef TARGET_EOPNOTSUPP +#define TARGET_EOPNOTSUPP 45 +#undef TARGET_EPFNOSUPPORT +#define TARGET_EPFNOSUPPORT 46 +#undef TARGET_EAFNOSUPPORT +#define TARGET_EAFNOSUPPORT 47 +#undef TARGET_EADDRINUSE +#define TARGET_EADDRINUSE 48 +#undef TARGET_EADDRNOTAVAIL +#define TARGET_EADDRNOTAVAIL 49 +#undef TARGET_ENETDOWN +#define TARGET_ENETDOWN 50 +#undef TARGET_ENETUNREACH +#define TARGET_ENETUNREACH 51 +#undef TARGET_ENETRESET +#define TARGET_ENETRESET 52 +#undef TARGET_ECONNABORTED +#define TARGET_ECONNABORTED 53 +#undef TARGET_ECONNRESET +#define TARGET_ECONNRESET 54 +#undef TARGET_ENOBUFS +#define TARGET_ENOBUFS 55 +#undef TARGET_EISCONN +#define TARGET_EISCONN 56 +#undef TARGET_ENOTCONN +#define TARGET_ENOTCONN 57 +#undef TARGET_ESHUTDOWN +#define TARGET_ESHUTDOWN 58 +#undef TARGET_ETOOMANYREFS +#define TARGET_ETOOMANYREFS 59 +#undef TARGET_ETIMEDOUT +#define TARGET_ETIMEDOUT 60 +#undef TARGET_ECONNREFUSED +#define TARGET_ECONNREFUSED 61 +#undef TARGET_ELOOP +#define TARGET_ELOOP 62 +#undef TARGET_ENAMETOOLONG +#define TARGET_ENAMETOOLONG 63 +#undef TARGET_EHOSTDOWN +#define TARGET_EHOSTDOWN 64 +#undef TARGET_EHOSTUNREACH +#define TARGET_EHOSTUNREACH 65 +#undef TARGET_ENOTEMPTY +#define TARGET_ENOTEMPTY 66 +// Unused 67 +#undef TARGET_EUSERS +#define TARGET_EUSERS 68 +#undef TARGET_EDQUOT +#define TARGET_EDQUOT 69 +#undef TARGET_ESTALE +#define TARGET_ESTALE 70 +#undef TARGET_EREMOTE +#define TARGET_EREMOTE 71 +// Unused 72-76 +#undef TARGET_ENOLCK +#define TARGET_ENOLCK 77 +#undef TARGET_ENOSYS +#define TARGET_ENOSYS 78 +// Unused 79 +#undef TARGET_ENOMSG +#define TARGET_ENOMSG 80 +#undef TARGET_EIDRM +#define TARGET_EIDRM 81 +#undef TARGET_ENOSR +#define TARGET_ENOSR 82 +#undef TARGET_ETIME +#define TARGET_ETIME 83 +#undef TARGET_EBADMSG +#define TARGET_EBADMSG 84 +#undef TARGET_EPROTO +#define TARGET_EPROTO 85 +#undef TARGET_ENODATA +#define TARGET_ENODATA 86 +#undef TARGET_ENOSTR +#define TARGET_ENOSTR 87 +#undef TARGET_ECHRNG +#define TARGET_ECHRNG 88 +#undef TARGET_EL2NSYNC +#define TARGET_EL2NSYNC 89 +#undef TARGET_EL3HLT +#define TARGET_EL3HLT 90 +#undef TARGET_EL3RST +#define TARGET_EL3RST 91 +#undef TARGET_ENOPKG +#define TARGET_ENOPKG 92 +#undef TARGET_ELNRNG +#define TARGET_ELNRNG 93 +#undef TARGET_EUNATCH +#define TARGET_EUNATCH 94 +#undef TARGET_ENOCSI +#define TARGET_ENOCSI 95 +#undef TARGET_EL2HLT +#define TARGET_EL2HLT 96 +#undef TARGET_EBADE +#define TARGET_EBADE 97 +#undef TARGET_EBADR +#define TARGET_EBADR 98 +#undef TARGET_EXFULL +#define TARGET_EXFULL 99 +#undef TARGET_ENOANO +#define TARGET_ENOANO 100 +#undef TARGET_EBADRQC +#define TARGET_EBADRQC 101 +#undef TARGET_EBADSLT +#define TARGET_EBADSLT 102 +// Unused 103 +#undef TARGET_EBFONT +#define TARGET_EBFONT 104 +#undef TARGET_ENONET +#define TARGET_ENONET 105 +#undef TARGET_ENOLINK +#define TARGET_ENOLINK 106 +#undef TARGET_EADV +#define TARGET_EADV 107 +#undef TARGET_ESRMNT +#define TARGET_ESRMNT 108 +#undef TARGET_ECOMM +#define TARGET_ECOMM 109 +#undef TARGET_EMULTIHOP +#define TARGET_EMULTIHOP 110 +#undef TARGET_EDOTDOT +#define TARGET_EDOTDOT 111 +#undef TARGET_EOVERFLOW +#define TARGET_EOVERFLOW 112 +#undef TARGET_ENOTUNIQ +#define TARGET_ENOTUNIQ 113 +#undef TARGET_EBADFD +#define TARGET_EBADFD 114 +#undef TARGET_EREMCHG +#define TARGET_EREMCHG 115 +#undef TARGET_EILSEQ +#define TARGET_EILSEQ 116 + +// Same as default 117-121 + +#undef TARGET_ELIBACC +#define TARGET_ELIBACC 122 +#undef TARGET_ELIBBAD +#define TARGET_ELIBBAD 123 +#undef TARGET_ELIBSCN +#define TARGET_ELIBSCN 124 +#undef TARGET_ELIBMAX +#define TARGET_ELIBMAX 125 +#undef TARGET_ELIBEXEC +#define TARGET_ELIBEXEC 126 +#undef TARGET_ERESTART +#define TARGET_ERESTART 127 +#undef TARGET_ESTRPIPE +#define TARGET_ESTRPIPE 128 +#undef TARGET_ENOMEDIUM +#define TARGET_ENOMEDIUM 129 +#undef TARGET_EMEDIUMTYPE +#define TARGET_EMEDIUMTYPE 130 +#undef TARGET_ECANCELED +#define TARGET_ECANCELED 131 +#undef TARGET_ENOKEY +#define TARGET_ENOKEY 132 +#undef TARGET_EKEYEXPIRED +#define TARGET_EKEYEXPIRED 133 +#undef TARGET_EKEYREVOKED +#define TARGET_EKEYREVOKED 134 +#undef TARGET_EKEYREJECTED +#define TARGET_EKEYREJECTED 135 +#undef TARGET_EOWNERDEAD +#define TARGET_EOWNERDEAD 136 +#undef TARGET_ENOTRECOVERABLE +#define TARGET_ENOTRECOVERABLE 137 +#undef TARGET_ERFKILL +#define TARGET_ERFKILL 138 + +// For sys_osf_getsysinfo +#define TARGET_GSI_UACPROC 8 +#define TARGET_GSI_IEEE_FP_CONTROL 45 +#define TARGET_GSI_IEEE_STATE_AT_SIGNAL 46 +#define TARGET_GSI_PROC_TYPE 60 +#define TARGET_GSI_GET_HWRPB 101 + +// For sys_ofs_setsysinfo +#define TARGET_SSI_NVPAIRS 1 +#define TARGET_SSI_IEEE_FP_CONTROL 14 +#define TARGET_SSI_IEEE_STATE_AT_SIGNAL 15 +#define TARGET_SSI_IEEE_IGNORE_STATE_AT_SIGNAL 16 +#define TARGET_SSI_IEEE_RAISE_EXCEPTION 1001 + +#define TARGET_SSIN_UACPROC 6 + +#define TARGET_UAC_NOPRINT 1 +#define TARGET_UAC_NOFIX 2 +#define TARGET_UAC_SIGBUS 4 +#define TARGET_MINSIGSTKSZ 4096 +#define TARGET_MLOCKALL_MCL_CURRENT 0x2000 +#define TARGET_MLOCKALL_MCL_FUTURE 0x4000 + +#endif /* TARGET_SYSCALL_H */ diff --git a/qemu/linux-user/arm/nwfpe/double_cpdo.c b/qemu/linux-user/arm/nwfpe/double_cpdo.c index 41c28f309..1cef38085 100644 --- a/qemu/linux-user/arm/nwfpe/double_cpdo.c +++ b/qemu/linux-user/arm/nwfpe/double_cpdo.c @@ -18,6 +18,7 @@ along with this program; if not, see . */ +#include "qemu/osdep.h" #include "fpa11.h" #include "fpu/softfloat.h" #include "fpopcode.h" diff --git a/qemu/linux-user/arm/nwfpe/extended_cpdo.c b/qemu/linux-user/arm/nwfpe/extended_cpdo.c index 48eca3b4a..1c8a41229 100644 --- a/qemu/linux-user/arm/nwfpe/extended_cpdo.c +++ b/qemu/linux-user/arm/nwfpe/extended_cpdo.c @@ -18,6 +18,7 @@ along with this program; if not, see . */ +#include "qemu/osdep.h" #include "fpa11.h" #include "fpu/softfloat.h" #include "fpopcode.h" diff --git a/qemu/linux-user/arm/nwfpe/fpa11.c b/qemu/linux-user/arm/nwfpe/fpa11.c index eebd93fc0..441e3b1cf 100644 --- a/qemu/linux-user/arm/nwfpe/fpa11.c +++ b/qemu/linux-user/arm/nwfpe/fpa11.c @@ -18,6 +18,7 @@ along with this program; if not, see . */ +#include "qemu/osdep.h" #include "fpa11.h" #include "fpopcode.h" @@ -27,7 +28,6 @@ //#include -#include FPA11* qemufpa = NULL; CPUARMState* user_registers; diff --git a/qemu/linux-user/arm/nwfpe/fpa11.h b/qemu/linux-user/arm/nwfpe/fpa11.h index bb9ac6554..0b072843d 100644 --- a/qemu/linux-user/arm/nwfpe/fpa11.h +++ b/qemu/linux-user/arm/nwfpe/fpa11.h @@ -21,9 +21,6 @@ #ifndef __FPA11_H__ #define __FPA11_H__ -#include -#include -#include #include @@ -108,7 +105,7 @@ static inline void writeRegister(unsigned int x, unsigned int y) static inline void writeConditionCodes(unsigned int x) { - cpsr_write(user_registers,x,CPSR_NZCV); + cpsr_write(user_registers, x, CPSR_NZCV, CPSRWriteByInstr); } #define ARM_REG_PC 15 diff --git a/qemu/linux-user/arm/nwfpe/fpa11_cpdo.c b/qemu/linux-user/arm/nwfpe/fpa11_cpdo.c index 5f4a6a413..94ac98aef 100644 --- a/qemu/linux-user/arm/nwfpe/fpa11_cpdo.c +++ b/qemu/linux-user/arm/nwfpe/fpa11_cpdo.c @@ -18,6 +18,7 @@ along with this program; if not, see . */ +#include "qemu/osdep.h" #include "fpa11.h" #include "fpopcode.h" diff --git a/qemu/linux-user/arm/nwfpe/fpa11_cpdt.c b/qemu/linux-user/arm/nwfpe/fpa11_cpdt.c index 007a3d650..c32b0c2fa 100644 --- a/qemu/linux-user/arm/nwfpe/fpa11_cpdt.c +++ b/qemu/linux-user/arm/nwfpe/fpa11_cpdt.c @@ -19,6 +19,7 @@ along with this program; if not, see . */ +#include "qemu/osdep.h" #include "fpa11.h" #include "fpu/softfloat.h" #include "fpopcode.h" diff --git a/qemu/linux-user/arm/nwfpe/fpa11_cprt.c b/qemu/linux-user/arm/nwfpe/fpa11_cprt.c index 7be93fa54..04dc2ebb0 100644 --- a/qemu/linux-user/arm/nwfpe/fpa11_cprt.c +++ b/qemu/linux-user/arm/nwfpe/fpa11_cprt.c @@ -19,6 +19,7 @@ along with this program; if not, see . */ +#include "qemu/osdep.h" #include "fpa11.h" #include "fpu/softfloat.h" #include "fpopcode.h" diff --git a/qemu/linux-user/arm/nwfpe/fpopcode.c b/qemu/linux-user/arm/nwfpe/fpopcode.c index 0ada30c6b..6784256d2 100644 --- a/qemu/linux-user/arm/nwfpe/fpopcode.c +++ b/qemu/linux-user/arm/nwfpe/fpopcode.c @@ -18,6 +18,7 @@ along with this program; if not, see . */ +#include "qemu/osdep.h" #include "fpa11.h" #include "fpu/softfloat.h" #include "fpopcode.h" diff --git a/qemu/linux-user/arm/nwfpe/single_cpdo.c b/qemu/linux-user/arm/nwfpe/single_cpdo.c index 2bfb359eb..21e177bae 100644 --- a/qemu/linux-user/arm/nwfpe/single_cpdo.c +++ b/qemu/linux-user/arm/nwfpe/single_cpdo.c @@ -18,6 +18,7 @@ along with this program; if not, see . */ +#include "qemu/osdep.h" #include "fpa11.h" #include "fpu/softfloat.h" #include "fpopcode.h" diff --git a/qemu/linux-user/arm/syscall.h b/qemu/linux-user/arm/syscall.h deleted file mode 100644 index 3844a9611..000000000 --- a/qemu/linux-user/arm/syscall.h +++ /dev/null @@ -1,50 +0,0 @@ - -/* this struct defines the way the registers are stored on the - stack during a system call. */ - -struct target_pt_regs { - abi_long uregs[18]; -}; - -#define ARM_cpsr uregs[16] -#define ARM_pc uregs[15] -#define ARM_lr uregs[14] -#define ARM_sp uregs[13] -#define ARM_ip uregs[12] -#define ARM_fp uregs[11] -#define ARM_r10 uregs[10] -#define ARM_r9 uregs[9] -#define ARM_r8 uregs[8] -#define ARM_r7 uregs[7] -#define ARM_r6 uregs[6] -#define ARM_r5 uregs[5] -#define ARM_r4 uregs[4] -#define ARM_r3 uregs[3] -#define ARM_r2 uregs[2] -#define ARM_r1 uregs[1] -#define ARM_r0 uregs[0] -#define ARM_ORIG_r0 uregs[17] - -#define ARM_SYSCALL_BASE 0x900000 -#define ARM_THUMB_SYSCALL 0 - -#define ARM_NR_BASE 0xf0000 -#define ARM_NR_breakpoint (ARM_NR_BASE + 1) -#define ARM_NR_cacheflush (ARM_NR_BASE + 2) -#define ARM_NR_set_tls (ARM_NR_BASE + 5) - -#define ARM_NR_semihosting 0x123456 -#define ARM_NR_thumb_semihosting 0xAB - -#if defined(TARGET_WORDS_BIGENDIAN) -#define UNAME_MACHINE "armv5teb" -#else -#define UNAME_MACHINE "armv5tel" -#endif -#define UNAME_MINIMUM_RELEASE "2.6.32" - -#define TARGET_CLONE_BACKWARDS - -#define TARGET_MINSIGSTKSZ 2048 -#define TARGET_MLOCKALL_MCL_CURRENT 1 -#define TARGET_MLOCKALL_MCL_FUTURE 2 diff --git a/qemu/linux-user/arm/syscall_nr.h b/qemu/linux-user/arm/syscall_nr.h index 53552beab..cc9089ccd 100644 --- a/qemu/linux-user/arm/syscall_nr.h +++ b/qemu/linux-user/arm/syscall_nr.h @@ -384,3 +384,15 @@ #define TARGET_NR_process_vm_writev (377) #define TARGET_NR_kcmp (378) #define TARGET_NR_finit_module (379) + +#define TARGET_NR_sched_setattr (380) +#define TARGET_NR_sched_getattr (381) +#define TARGET_NR_renameat2 (382) +#define TARGET_NR_seccomp (383) +#define TARGET_NR_getrandom (384) +#define TARGET_NR_memfd_create (385) +#define TARGET_NR_bpf (386) +#define TARGET_NR_execveat (387) +#define TARGET_NR_userfaultfd (388) +#define TARGET_NR_membarrier (389) +#define TARGET_NR_mlock2 (390) diff --git a/qemu/linux-user/arm/target_syscall.h b/qemu/linux-user/arm/target_syscall.h new file mode 100644 index 000000000..ea863db0b --- /dev/null +++ b/qemu/linux-user/arm/target_syscall.h @@ -0,0 +1,54 @@ +#ifndef TARGET_SYSCALL_H +#define TARGET_SYSCALL_H + +/* this struct defines the way the registers are stored on the + stack during a system call. */ + +struct target_pt_regs { + abi_long uregs[18]; +}; + +#define ARM_cpsr uregs[16] +#define ARM_pc uregs[15] +#define ARM_lr uregs[14] +#define ARM_sp uregs[13] +#define ARM_ip uregs[12] +#define ARM_fp uregs[11] +#define ARM_r10 uregs[10] +#define ARM_r9 uregs[9] +#define ARM_r8 uregs[8] +#define ARM_r7 uregs[7] +#define ARM_r6 uregs[6] +#define ARM_r5 uregs[5] +#define ARM_r4 uregs[4] +#define ARM_r3 uregs[3] +#define ARM_r2 uregs[2] +#define ARM_r1 uregs[1] +#define ARM_r0 uregs[0] +#define ARM_ORIG_r0 uregs[17] + +#define ARM_SYSCALL_BASE 0x900000 +#define ARM_THUMB_SYSCALL 0 + +#define ARM_NR_BASE 0xf0000 +#define ARM_NR_breakpoint (ARM_NR_BASE + 1) +#define ARM_NR_cacheflush (ARM_NR_BASE + 2) +#define ARM_NR_set_tls (ARM_NR_BASE + 5) + +#define ARM_NR_semihosting 0x123456 +#define ARM_NR_thumb_semihosting 0xAB + +#if defined(TARGET_WORDS_BIGENDIAN) +#define UNAME_MACHINE "armv5teb" +#else +#define UNAME_MACHINE "armv5tel" +#endif +#define UNAME_MINIMUM_RELEASE "2.6.32" + +#define TARGET_CLONE_BACKWARDS + +#define TARGET_MINSIGSTKSZ 2048 +#define TARGET_MLOCKALL_MCL_CURRENT 1 +#define TARGET_MLOCKALL_MCL_FUTURE 2 + +#endif /* TARGET_SYSCALL_H */ diff --git a/qemu/linux-user/cris/syscall_nr.h b/qemu/linux-user/cris/syscall_nr.h index 694bd02fa..44f0b645b 100644 --- a/qemu/linux-user/cris/syscall_nr.h +++ b/qemu/linux-user/cris/syscall_nr.h @@ -336,3 +336,27 @@ #define TARGET_NR_preadv 333 #define TARGET_NR_pwritev 334 #define TARGET_NR_setns 335 +#define TARGET_NR_name_to_handle_at 336 +#define TARGET_NR_open_by_handle_at 337 +#define TARGET_NR_rt_tgsigqueueinfo 338 +#define TARGET_NR_perf_event_open 339 +#define TARGET_NR_recvmmsg 340 +#define TARGET_NR_accept4 341 +#define TARGET_NR_fanotify_init 342 +#define TARGET_NR_fanotify_mark 343 +#define TARGET_NR_prlimit64 344 +#define TARGET_NR_clock_adjtime 345 +#define TARGET_NR_syncfs 346 +#define TARGET_NR_sendmmsg 347 +#define TARGET_NR_process_vm_readv 348 +#define TARGET_NR_process_vm_writev 349 +#define TARGET_NR_kcmp 350 +#define TARGET_NR_finit_module 351 +#define TARGET_NR_sched_setattr 352 +#define TARGET_NR_sched_getattr 353 +#define TARGET_NR_renameat2 354 +#define TARGET_NR_seccomp 355 +#define TARGET_NR_getrandom 356 +#define TARGET_NR_memfd_create 357 +#define TARGET_NR_bpf 358 +#define TARGET_NR_execveat 359 diff --git a/qemu/linux-user/cris/syscall.h b/qemu/linux-user/cris/target_syscall.h similarity index 100% rename from qemu/linux-user/cris/syscall.h rename to qemu/linux-user/cris/target_syscall.h diff --git a/qemu/linux-user/elfload.c b/qemu/linux-user/elfload.c index 17883686f..e47caff7a 100644 --- a/qemu/linux-user/elfload.c +++ b/qemu/linux-user/elfload.c @@ -1,20 +1,13 @@ /* This is the Linux kernel elf-loading code, ported into user space */ -#include +#include "qemu/osdep.h" #include -#include -#include -#include -#include -#include #include #include -#include -#include -#include #include "qemu.h" #include "disas/disas.h" +#include "qemu/path.h" #ifdef _ARCH_PPC64 #undef ARCH_DLINFO @@ -145,7 +138,6 @@ static uint32_t get_elf_hwcap(void) #ifdef TARGET_X86_64 #define ELF_START_MMAP 0x2aaaaab000ULL -#define elf_check_arch(x) ( ((x) == ELF_ARCH) ) #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_X86_64 @@ -273,9 +265,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en #define ELF_START_MMAP 0x80000000 -#define elf_check_arch(x) ((x) == ELF_MACHINE) - -#define ELF_ARCH ELF_MACHINE +#define ELF_ARCH EM_ARM #define ELF_CLASS ELFCLASS32 static inline void init_thread(struct target_pt_regs *regs, @@ -481,9 +471,7 @@ static uint32_t get_elf_hwcap2(void) /* 64 bit ARM definitions */ #define ELF_START_MMAP 0x80000000 -#define elf_check_arch(x) ((x) == ELF_MACHINE) - -#define ELF_ARCH ELF_MACHINE +#define ELF_ARCH EM_AARCH64 #define ELF_CLASS ELFCLASS64 #define ELF_PLATFORM "aarch64" @@ -556,8 +544,6 @@ static uint32_t get_elf_hwcap(void) #define ELF_START_MMAP 0x80000000 -#define elf_check_arch(x) ((x) == EM_UNICORE32) - #define ELF_CLASS ELFCLASS32 #define ELF_DATA ELFDATA2LSB #define ELF_ARCH EM_UNICORE32 @@ -666,7 +652,6 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_START_MMAP 0x80000000 #define ELF_HWCAP (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | HWCAP_SPARC_SWAP \ | HWCAP_SPARC_MULDIV) -#define elf_check_arch(x) ( (x) == EM_SPARC ) #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_SPARC @@ -686,6 +671,7 @@ static inline void init_thread(struct target_pt_regs *regs, #ifdef TARGET_PPC +#define ELF_MACHINE PPC_ELF_MACHINE #define ELF_START_MMAP 0x80000000 #if defined(TARGET_PPC64) && !defined(TARGET_ABI32) @@ -696,8 +682,6 @@ static inline void init_thread(struct target_pt_regs *regs, #else -#define elf_check_arch(x) ( (x) == EM_PPC ) - #define ELF_CLASS ELFCLASS32 #endif @@ -875,8 +859,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en #define ELF_START_MMAP 0x80000000 -#define elf_check_arch(x) ( (x) == EM_MIPS ) - #ifdef TARGET_MIPS64 #define ELF_CLASS ELFCLASS64 #else @@ -985,8 +967,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMBState *env #define ELF_START_MMAP 0x08000000 -#define elf_check_arch(x) ((x) == EM_OPENRISC) - #define ELF_ARCH EM_OPENRISC #define ELF_CLASS ELFCLASS32 #define ELF_DATA ELFDATA2MSB @@ -1026,8 +1006,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define ELF_START_MMAP 0x80000000 -#define elf_check_arch(x) ( (x) == EM_SH ) - #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_SH @@ -1110,8 +1088,6 @@ static uint32_t get_elf_hwcap(void) #define ELF_START_MMAP 0x80000000 -#define elf_check_arch(x) ( (x) == EM_CRIS ) - #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_CRIS @@ -1129,8 +1105,6 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_START_MMAP 0x80000000 -#define elf_check_arch(x) ( (x) == EM_68K ) - #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_68K @@ -1182,8 +1156,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUM68KState *e #define ELF_START_MMAP (0x30000000000ULL) -#define elf_check_arch(x) ( (x) == ELF_ARCH ) - #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_ALPHA @@ -1203,8 +1175,6 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_START_MMAP (0x20000000000ULL) -#define elf_check_arch(x) ( (x) == ELF_ARCH ) - #define ELF_CLASS ELFCLASS64 #define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_S390 @@ -1218,10 +1188,41 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i #endif /* TARGET_S390X */ +#ifdef TARGET_TILEGX + +/* 42 bits real used address, a half for user mode */ +#define ELF_START_MMAP (0x00000020000000000ULL) + +#define elf_check_arch(x) ((x) == EM_TILEGX) + +#define ELF_CLASS ELFCLASS64 +#define ELF_DATA ELFDATA2LSB +#define ELF_ARCH EM_TILEGX + +static inline void init_thread(struct target_pt_regs *regs, + struct image_info *infop) +{ + regs->pc = infop->entry; + regs->sp = infop->start_stack; + +} + +#define ELF_EXEC_PAGESIZE 65536 /* TILE-Gx page size is 64KB */ + +#endif /* TARGET_TILEGX */ + #ifndef ELF_PLATFORM #define ELF_PLATFORM (NULL) #endif +#ifndef ELF_MACHINE +#define ELF_MACHINE ELF_ARCH +#endif + +#ifndef elf_check_arch +#define elf_check_arch(x) ((x) == ELF_ARCH) +#endif + #ifndef ELF_HWCAP #define ELF_HWCAP 0 #endif @@ -1365,66 +1366,69 @@ static bool elf_check_ehdr(struct elfhdr *ehdr) * to be put directly into the top of new user memory. * */ -static abi_ulong copy_elf_strings(int argc,char ** argv, void **page, - abi_ulong p) +static abi_ulong copy_elf_strings(int argc, char **argv, char *scratch, + abi_ulong p, abi_ulong stack_limit) { - char *tmp, *tmp1, *pag = NULL; - int len, offset = 0; + char *tmp; + int len, offset; + abi_ulong top = p; if (!p) { return 0; /* bullet-proofing */ } + + offset = ((p - 1) % TARGET_PAGE_SIZE) + 1; + while (argc-- > 0) { tmp = argv[argc]; if (!tmp) { fprintf(stderr, "VFS: argc is wrong"); exit(-1); } - tmp1 = tmp; - while (*tmp++); - len = tmp - tmp1; - if (p < len) { /* this shouldn't happen - 128kB */ + len = strlen(tmp) + 1; + tmp += len; + + if (len > (p - stack_limit)) { return 0; } while (len) { - --p; --tmp; --len; - if (--offset < 0) { - offset = p % TARGET_PAGE_SIZE; - pag = (char *)page[p/TARGET_PAGE_SIZE]; - if (!pag) { - pag = g_try_malloc0(TARGET_PAGE_SIZE); - page[p/TARGET_PAGE_SIZE] = pag; - if (!pag) - return 0; - } - } - if (len == 0 || offset == 0) { - *(pag + offset) = *tmp; - } - else { - int bytes_to_copy = (len > offset) ? offset : len; - tmp -= bytes_to_copy; - p -= bytes_to_copy; - offset -= bytes_to_copy; - len -= bytes_to_copy; - memcpy_fromfs(pag + offset, tmp, bytes_to_copy + 1); + int bytes_to_copy = (len > offset) ? offset : len; + tmp -= bytes_to_copy; + p -= bytes_to_copy; + offset -= bytes_to_copy; + len -= bytes_to_copy; + + memcpy_fromfs(scratch + offset, tmp, bytes_to_copy); + + if (offset == 0) { + memcpy_to_target(p, scratch, top - p); + top = p; + offset = TARGET_PAGE_SIZE; } } } + if (offset) { + memcpy_to_target(p, scratch + offset, top - p); + } + return p; } -static abi_ulong setup_arg_pages(abi_ulong p, struct linux_binprm *bprm, +/* Older linux kernels provide up to MAX_ARG_PAGES (default: 32) of + * argument/environment space. Newer kernels (>2.6.33) allow more, + * dependent on stack size, but guarantee at least 32 pages for + * backwards compatibility. + */ +#define STACK_LOWER_LIMIT (32 * TARGET_PAGE_SIZE) + +static abi_ulong setup_arg_pages(struct linux_binprm *bprm, struct image_info *info) { - abi_ulong stack_base, size, error, guard; - int i; + abi_ulong size, error, guard; - /* Create enough stack to hold everything. If we don't use - it for args, we'll use it for something else. */ size = guest_stack_size; - if (size < MAX_ARG_PAGES*TARGET_PAGE_SIZE) { - size = MAX_ARG_PAGES*TARGET_PAGE_SIZE; + if (size < STACK_LOWER_LIMIT) { + size = STACK_LOWER_LIMIT; } guard = TARGET_PAGE_SIZE; if (guard < qemu_real_host_page_size) { @@ -1442,19 +1446,8 @@ static abi_ulong setup_arg_pages(abi_ulong p, struct linux_binprm *bprm, target_mprotect(error, guard, PROT_NONE); info->stack_limit = error + guard; - stack_base = info->stack_limit + size - MAX_ARG_PAGES*TARGET_PAGE_SIZE; - p += stack_base; - - for (i = 0 ; i < MAX_ARG_PAGES ; i++) { - if (bprm->page[i]) { - info->rss++; - /* FIXME - check return value of memcpy_to_target() for failure */ - memcpy_to_target(stack_base, bprm->page[i], TARGET_PAGE_SIZE); - g_free(bprm->page[i]); - } - stack_base += TARGET_PAGE_SIZE; - } - return p; + + return info->stack_limit + size - sizeof(void *); } /* Map and zero the bss. We need to explicitly zero any fractional pages @@ -1478,8 +1471,7 @@ static void zero_bss(abi_ulong elf_bss, abi_ulong last_bss, int prot) host_start = (uintptr_t) g2h(elf_bss); host_end = (uintptr_t) g2h(last_bss); - host_map_start = (host_start + qemu_real_host_page_size - 1); - host_map_start &= -qemu_real_host_page_size; + host_map_start = REAL_HOST_PAGE_ALIGN(host_start); if (host_map_start < host_end) { void *p = mmap((void *)host_map_start, host_end - host_map_start, @@ -1744,7 +1736,7 @@ unsigned long init_guest_space(unsigned long host_start, } } - qemu_log("Reserved 0x%lx bytes of guest address space\n", host_size); + qemu_log_mask(CPU_LOG_PAGE, "Reserved 0x%lx bytes of guest address space\n", host_size); return real_start; } @@ -1756,7 +1748,6 @@ static void probe_guest_base(const char *image_name, * it explicitly, and set guest_base appropriately. * In case of error we will print a suitable message and exit. */ -#if defined(CONFIG_USE_GUEST_BASE) const char *errmsg; if (!have_guest_base && !reserved_va) { unsigned long host_start, real_start, host_size; @@ -1786,16 +1777,15 @@ static void probe_guest_base(const char *image_name, } guest_base = real_start - loaddr; - qemu_log("Relocating guest address space from 0x" - TARGET_ABI_FMT_lx " to 0x%lx\n", - loaddr, real_start); + qemu_log_mask(CPU_LOG_PAGE, "Relocating guest address space from 0x" + TARGET_ABI_FMT_lx " to 0x%lx\n", + loaddr, real_start); } return; exit_errmsg: fprintf(stderr, "%s: %s\n", image_name, errmsg); exit(-1); -#endif } @@ -2198,10 +2188,9 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) struct image_info interp_info; struct elfhdr elf_ex; char *elf_interpreter = NULL; + char *scratch; info->start_mmap = (abi_ulong)ELF_START_MMAP; - info->mmap = 0; - info->rss = 0; load_elf_image(bprm->filename, bprm->fd, info, &elf_interpreter, bprm->buf); @@ -2211,18 +2200,24 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) when we load the interpreter. */ elf_ex = *(struct elfhdr *)bprm->buf; - bprm->p = copy_elf_strings(1, &bprm->filename, bprm->page, bprm->p); - bprm->p = copy_elf_strings(bprm->envc,bprm->envp,bprm->page,bprm->p); - bprm->p = copy_elf_strings(bprm->argc,bprm->argv,bprm->page,bprm->p); + /* Do this so that we can load the interpreter, if need be. We will + change some of these later */ + bprm->p = setup_arg_pages(bprm, info); + + scratch = g_new0(char, TARGET_PAGE_SIZE); + bprm->p = copy_elf_strings(1, &bprm->filename, scratch, + bprm->p, info->stack_limit); + bprm->p = copy_elf_strings(bprm->envc, bprm->envp, scratch, + bprm->p, info->stack_limit); + bprm->p = copy_elf_strings(bprm->argc, bprm->argv, scratch, + bprm->p, info->stack_limit); + g_free(scratch); + if (!bprm->p) { fprintf(stderr, "%s: %s\n", bprm->filename, strerror(E2BIG)); exit(-1); } - /* Do this so that we can load the interpreter, if need be. We will - change some of these later */ - bprm->p = setup_arg_pages(bprm->p, bprm, info); - if (elf_interpreter) { load_elf_interp(elf_interpreter, &interp_info, bprm->buf); @@ -2850,7 +2845,7 @@ static int fill_note_info(struct elf_note_info *info, TaskState *ts = (TaskState *)cpu->opaque; int i; - info->notes = g_malloc0(NUMNOTES * sizeof (struct memelfnote)); + info->notes = g_new0(struct memelfnote, NUMNOTES); if (info->notes == NULL) return (-ENOMEM); info->prstatus = g_malloc0(sizeof (*info->prstatus)); @@ -3016,7 +3011,7 @@ static int elf_core_dump(int signr, const CPUArchState *env) if (dump_write(fd, &elf, sizeof (elf)) != 0) goto out; - /* fill in in-memory version of notes */ + /* fill in the in-memory version of notes */ if (fill_note_info(&info, signr, env) < 0) goto out; diff --git a/qemu/linux-user/flatload.c b/qemu/linux-user/flatload.c index 566a7a87a..f9139c399 100644 --- a/qemu/linux-user/flatload.c +++ b/qemu/linux-user/flatload.c @@ -33,15 +33,11 @@ /****************************************************************************/ -#include -#include -#include +#include "qemu/osdep.h" #include -#include #include "qemu.h" #include "flat.h" -#define ntohl(x) be32_to_cpu(x) #include //#define DEBUG @@ -707,7 +703,7 @@ static int load_flat_shared_library(int id, struct lib_info *libs) int load_flt_binary(struct linux_binprm *bprm, struct image_info *info) { struct lib_info libinfo[MAX_SHARED_LIBS]; - abi_ulong p = bprm->p; + abi_ulong p; abi_ulong stack_len; abi_ulong start_addr; abi_ulong sp; diff --git a/qemu/linux-user/i386/syscall.h b/qemu/linux-user/i386/syscall.h deleted file mode 100644 index 906aaac0b..000000000 --- a/qemu/linux-user/i386/syscall.h +++ /dev/null @@ -1,152 +0,0 @@ -/* default linux values for the selectors */ -#define __USER_CS (0x23) -#define __USER_DS (0x2B) - -struct target_pt_regs { - long ebx; - long ecx; - long edx; - long esi; - long edi; - long ebp; - long eax; - int xds; - int xes; - long orig_eax; - long eip; - int xcs; - long eflags; - long esp; - int xss; -}; - -/* ioctls */ - -#define TARGET_LDT_ENTRIES 8192 -#define TARGET_LDT_ENTRY_SIZE 8 - -#define TARGET_GDT_ENTRIES 9 -#define TARGET_GDT_ENTRY_TLS_ENTRIES 3 -#define TARGET_GDT_ENTRY_TLS_MIN 6 -#define TARGET_GDT_ENTRY_TLS_MAX (TARGET_GDT_ENTRY_TLS_MIN + TARGET_GDT_ENTRY_TLS_ENTRIES - 1) - -struct target_modify_ldt_ldt_s { - unsigned int entry_number; - abi_ulong base_addr; - unsigned int limit; - unsigned int flags; -}; - -/* vm86 defines */ - -#define TARGET_BIOSSEG 0x0f000 - -#define TARGET_CPU_086 0 -#define TARGET_CPU_186 1 -#define TARGET_CPU_286 2 -#define TARGET_CPU_386 3 -#define TARGET_CPU_486 4 -#define TARGET_CPU_586 5 - -#define TARGET_VM86_SIGNAL 0 /* return due to signal */ -#define TARGET_VM86_UNKNOWN 1 /* unhandled GP fault - IO-instruction or similar */ -#define TARGET_VM86_INTx 2 /* int3/int x instruction (ARG = x) */ -#define TARGET_VM86_STI 3 /* sti/popf/iret instruction enabled virtual interrupts */ - -/* - * Additional return values when invoking new vm86() - */ -#define TARGET_VM86_PICRETURN 4 /* return due to pending PIC request */ -#define TARGET_VM86_TRAP 6 /* return due to DOS-debugger request */ - -/* - * function codes when invoking new vm86() - */ -#define TARGET_VM86_PLUS_INSTALL_CHECK 0 -#define TARGET_VM86_ENTER 1 -#define TARGET_VM86_ENTER_NO_BYPASS 2 -#define TARGET_VM86_REQUEST_IRQ 3 -#define TARGET_VM86_FREE_IRQ 4 -#define TARGET_VM86_GET_IRQ_BITS 5 -#define TARGET_VM86_GET_AND_RESET_IRQ 6 - -/* - * This is the stack-layout seen by the user space program when we have - * done a translation of "SAVE_ALL" from vm86 mode. The real kernel layout - * is 'kernel_vm86_regs' (see below). - */ - -struct target_vm86_regs { -/* - * normal regs, with special meaning for the segment descriptors.. - */ - abi_long ebx; - abi_long ecx; - abi_long edx; - abi_long esi; - abi_long edi; - abi_long ebp; - abi_long eax; - abi_long __null_ds; - abi_long __null_es; - abi_long __null_fs; - abi_long __null_gs; - abi_long orig_eax; - abi_long eip; - unsigned short cs, __csh; - abi_long eflags; - abi_long esp; - unsigned short ss, __ssh; -/* - * these are specific to v86 mode: - */ - unsigned short es, __esh; - unsigned short ds, __dsh; - unsigned short fs, __fsh; - unsigned short gs, __gsh; -}; - -struct target_revectored_struct { - abi_ulong __map[8]; /* 256 bits */ -}; - -struct target_vm86_struct { - struct target_vm86_regs regs; - abi_ulong flags; - abi_ulong screen_bitmap; - abi_ulong cpu_type; - struct target_revectored_struct int_revectored; - struct target_revectored_struct int21_revectored; -}; - -/* - * flags masks - */ -#define TARGET_VM86_SCREEN_BITMAP 0x0001 - -struct target_vm86plus_info_struct { - abi_ulong flags; -#define TARGET_force_return_for_pic (1 << 0) -#define TARGET_vm86dbg_active (1 << 1) /* for debugger */ -#define TARGET_vm86dbg_TFpendig (1 << 2) /* for debugger */ -#define TARGET_is_vm86pus (1 << 31) /* for vm86 internal use */ - unsigned char vm86dbg_intxxtab[32]; /* for debugger */ -}; - -struct target_vm86plus_struct { - struct target_vm86_regs regs; - abi_ulong flags; - abi_ulong screen_bitmap; - abi_ulong cpu_type; - struct target_revectored_struct int_revectored; - struct target_revectored_struct int21_revectored; - struct target_vm86plus_info_struct vm86plus; -}; - -#define UNAME_MACHINE "i686" -#define UNAME_MINIMUM_RELEASE "2.6.32" - -#define TARGET_CLONE_BACKWARDS -#define TARGET_MINSIGSTKSZ 2048 -#define TARGET_MLOCKALL_MCL_CURRENT 1 -#define TARGET_MLOCKALL_MCL_FUTURE 2 diff --git a/qemu/linux-user/i386/syscall_nr.h b/qemu/linux-user/i386/syscall_nr.h index c8f730219..bc1bc233e 100644 --- a/qemu/linux-user/i386/syscall_nr.h +++ b/qemu/linux-user/i386/syscall_nr.h @@ -324,7 +324,7 @@ #define TARGET_NR_epoll_pwait 319 #define TARGET_NR_utimensat 320 #define TARGET_NR_signalfd 321 -#define TARGET_NR_timerfd 322 +#define TARGET_NR_timerfd_create 322 #define TARGET_NR_eventfd 323 #define TARGET_NR_fallocate 324 #define TARGET_NR_timerfd_settime 325 @@ -353,3 +353,30 @@ #define TARGET_NR_process_vm_writev 348 #define TARGET_NR_kcmp 349 #define TARGET_NR_finit_module 350 +#define TARGET_NR_sched_setattr 351 +#define TARGET_NR_sched_getattr 352 +#define TARGET_NR_renameat2 353 +#define TARGET_NR_seccomp 354 +#define TARGET_NR_getrandom 355 +#define TARGET_NR_memfd_create 356 +#define TARGET_NR_bpf 357 +#define TARGET_NR_execveat 358 +#define TARGET_NR_socket 359 +#define TARGET_NR_socketpair 360 +#define TARGET_NR_bind 361 +#define TARGET_NR_connect 362 +#define TARGET_NR_listen 363 +#define TARGET_NR_accept4 364 +#define TARGET_NR_getsockopt 365 +#define TARGET_NR_setsockopt 366 +#define TARGET_NR_getsockname 367 +#define TARGET_NR_getpeername 368 +#define TARGET_NR_sendto 369 +#define TARGET_NR_sendmsg 370 +#define TARGET_NR_recvfrom 371 +#define TARGET_NR_recvmsg 372 +#define TARGET_NR_shutdown 373 +#define TARGET_NR_userfaultfd 374 +#define TARGET_NR_membarrier 375 +#define TARGET_NR_mlock2 376 +#define TARGET_NR_copy_file_range 377 diff --git a/qemu/linux-user/i386/target_syscall.h b/qemu/linux-user/i386/target_syscall.h new file mode 100644 index 000000000..0ac84dc02 --- /dev/null +++ b/qemu/linux-user/i386/target_syscall.h @@ -0,0 +1,157 @@ +#ifndef TARGET_SYSCALL_H +#define TARGET_SYSCALL_H + +/* default linux values for the selectors */ +#define __USER_CS (0x23) +#define __USER_DS (0x2B) + +struct target_pt_regs { + long ebx; + long ecx; + long edx; + long esi; + long edi; + long ebp; + long eax; + int xds; + int xes; + long orig_eax; + long eip; + int xcs; + long eflags; + long esp; + int xss; +}; + +/* ioctls */ + +#define TARGET_LDT_ENTRIES 8192 +#define TARGET_LDT_ENTRY_SIZE 8 + +#define TARGET_GDT_ENTRIES 9 +#define TARGET_GDT_ENTRY_TLS_ENTRIES 3 +#define TARGET_GDT_ENTRY_TLS_MIN 6 +#define TARGET_GDT_ENTRY_TLS_MAX (TARGET_GDT_ENTRY_TLS_MIN + TARGET_GDT_ENTRY_TLS_ENTRIES - 1) + +struct target_modify_ldt_ldt_s { + unsigned int entry_number; + abi_ulong base_addr; + unsigned int limit; + unsigned int flags; +}; + +/* vm86 defines */ + +#define TARGET_BIOSSEG 0x0f000 + +#define TARGET_CPU_086 0 +#define TARGET_CPU_186 1 +#define TARGET_CPU_286 2 +#define TARGET_CPU_386 3 +#define TARGET_CPU_486 4 +#define TARGET_CPU_586 5 + +#define TARGET_VM86_SIGNAL 0 /* return due to signal */ +#define TARGET_VM86_UNKNOWN 1 /* unhandled GP fault - IO-instruction or similar */ +#define TARGET_VM86_INTx 2 /* int3/int x instruction (ARG = x) */ +#define TARGET_VM86_STI 3 /* sti/popf/iret instruction enabled virtual interrupts */ + +/* + * Additional return values when invoking new vm86() + */ +#define TARGET_VM86_PICRETURN 4 /* return due to pending PIC request */ +#define TARGET_VM86_TRAP 6 /* return due to DOS-debugger request */ + +/* + * function codes when invoking new vm86() + */ +#define TARGET_VM86_PLUS_INSTALL_CHECK 0 +#define TARGET_VM86_ENTER 1 +#define TARGET_VM86_ENTER_NO_BYPASS 2 +#define TARGET_VM86_REQUEST_IRQ 3 +#define TARGET_VM86_FREE_IRQ 4 +#define TARGET_VM86_GET_IRQ_BITS 5 +#define TARGET_VM86_GET_AND_RESET_IRQ 6 + +/* + * This is the stack-layout seen by the user space program when we have + * done a translation of "SAVE_ALL" from vm86 mode. The real kernel layout + * is 'kernel_vm86_regs' (see below). + */ + +struct target_vm86_regs { +/* + * normal regs, with special meaning for the segment descriptors.. + */ + abi_long ebx; + abi_long ecx; + abi_long edx; + abi_long esi; + abi_long edi; + abi_long ebp; + abi_long eax; + abi_long __null_ds; + abi_long __null_es; + abi_long __null_fs; + abi_long __null_gs; + abi_long orig_eax; + abi_long eip; + unsigned short cs, __csh; + abi_long eflags; + abi_long esp; + unsigned short ss, __ssh; +/* + * these are specific to v86 mode: + */ + unsigned short es, __esh; + unsigned short ds, __dsh; + unsigned short fs, __fsh; + unsigned short gs, __gsh; +}; + +struct target_revectored_struct { + abi_ulong __map[8]; /* 256 bits */ +}; + +struct target_vm86_struct { + struct target_vm86_regs regs; + abi_ulong flags; + abi_ulong screen_bitmap; + abi_ulong cpu_type; + struct target_revectored_struct int_revectored; + struct target_revectored_struct int21_revectored; +}; + +/* + * flags masks + */ +#define TARGET_VM86_SCREEN_BITMAP 0x0001 + +struct target_vm86plus_info_struct { + abi_ulong flags; +#define TARGET_force_return_for_pic (1 << 0) +#define TARGET_vm86dbg_active (1 << 1) /* for debugger */ +#define TARGET_vm86dbg_TFpendig (1 << 2) /* for debugger */ +#define TARGET_is_vm86pus (1 << 31) /* for vm86 internal use */ + unsigned char vm86dbg_intxxtab[32]; /* for debugger */ +}; + +struct target_vm86plus_struct { + struct target_vm86_regs regs; + abi_ulong flags; + abi_ulong screen_bitmap; + abi_ulong cpu_type; + struct target_revectored_struct int_revectored; + struct target_revectored_struct int21_revectored; + struct target_vm86plus_info_struct vm86plus; +}; + +#define UNAME_MACHINE "i686" +#define UNAME_MINIMUM_RELEASE "2.6.32" + +#define TARGET_CLONE_BACKWARDS +#define TARGET_MINSIGSTKSZ 2048 +#define TARGET_MLOCKALL_MCL_CURRENT 1 +#define TARGET_MLOCKALL_MCL_FUTURE 2 + +#endif /* TARGET_SYSCALL_H */ diff --git a/qemu/linux-user/linuxload.c b/qemu/linux-user/linuxload.c index 506e837ae..6f0d6054c 100644 --- a/qemu/linux-user/linuxload.c +++ b/qemu/linux-user/linuxload.c @@ -1,12 +1,6 @@ /* Code for loading Linux executables. Mostly linux kernel code. */ -#include -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include "qemu.h" @@ -135,10 +129,7 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp, struct linux_binprm *bprm) { int retval; - int i; - bprm->p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int); - memset(bprm->page, 0, sizeof(bprm->page)); bprm->fd = fdexec; bprm->filename = (char *)filename; bprm->argc = count(argv); @@ -172,9 +163,5 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp, return retval; } - /* Something went wrong, return the inode and free the argument pages*/ - for (i=0 ; ipage[i]); - } return(retval); } diff --git a/qemu/linux-user/m68k-sim.c b/qemu/linux-user/m68k-sim.c index 1994e4000..34d332d8b 100644 --- a/qemu/linux-user/m68k-sim.c +++ b/qemu/linux-user/m68k-sim.c @@ -17,14 +17,7 @@ * along with this program; if not, see . */ -#include -#include -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include "qemu.h" diff --git a/qemu/linux-user/m68k/syscall.h b/qemu/linux-user/m68k/syscall.h deleted file mode 100644 index 9218493a4..000000000 --- a/qemu/linux-user/m68k/syscall.h +++ /dev/null @@ -1,25 +0,0 @@ - -/* this struct defines the way the registers are stored on the - stack during a system call. */ - -struct target_pt_regs { - abi_long d1, d2, d3, d4, d5, d6, d7; - abi_long a0, a1, a2, a3, a4, a5, a6; - abi_ulong d0; - abi_ulong usp; - abi_ulong orig_d0; - int16_t stkadj; - uint16_t sr; - abi_ulong pc; - uint16_t fntvex; - uint16_t __fill; -}; - -#define UNAME_MACHINE "m68k" -#define UNAME_MINIMUM_RELEASE "2.6.32" - -#define TARGET_MINSIGSTKSZ 2048 -#define TARGET_MLOCKALL_MCL_CURRENT 1 -#define TARGET_MLOCKALL_MCL_FUTURE 2 - -void do_m68k_simcall(CPUM68KState *, int); diff --git a/qemu/linux-user/m68k/syscall_nr.h b/qemu/linux-user/m68k/syscall_nr.h index 25f8521ec..4b50fb29b 100644 --- a/qemu/linux-user/m68k/syscall_nr.h +++ b/qemu/linux-user/m68k/syscall_nr.h @@ -317,7 +317,7 @@ #define TARGET_NR_epoll_pwait 315 #define TARGET_NR_utimensat 316 #define TARGET_NR_signalfd 317 -#define TARGET_NR_timerfd 318 +#define TARGET_NR_timerfd_create 318 #define TARGET_NR_eventfd 319 #define TARGET_NR_fallocate 320 #define TARGET_NR_timerfd_settime 321 @@ -349,3 +349,30 @@ #define TARGET_NR_process_vm_writev 346 #define TARGET_NR_kcmp 347 #define TARGET_NR_finit_module 348 +#define TARGET_NR_sched_setattr 349 +#define TARGET_NR_sched_getattr 350 +#define TARGET_NR_renameat2 351 +#define TARGET_NR_getrandom 352 +#define TARGET_NR_memfd_create 353 +#define TARGET_NR_bpf 354 +#define TARGET_NR_execveat 355 +#define TARGET_NR_socket 356 +#define TARGET_NR_socketpair 357 +#define TARGET_NR_bind 358 +#define TARGET_NR_connect 359 +#define TARGET_NR_listen 360 +#define TARGET_NR_accept4 361 +#define TARGET_NR_getsockopt 362 +#define TARGET_NR_setsockopt 363 +#define TARGET_NR_getsockname 364 +#define TARGET_NR_getpeername 365 +#define TARGET_NR_sendto 366 +#define TARGET_NR_sendmsg 367 +#define TARGET_NR_recvfrom 368 +#define TARGET_NR_recvmsg 369 +#define TARGET_NR_shutdown 370 +#define TARGET_NR_recvmmsg 371 +#define TARGET_NR_sendmmsg 372 +#define TARGET_NR_userfaultfd 373 +#define TARGET_NR_membarrier 374 +#define TARGET_NR_mlock2 375 diff --git a/qemu/linux-user/m68k/target_syscall.h b/qemu/linux-user/m68k/target_syscall.h new file mode 100644 index 000000000..97a4cc0cb --- /dev/null +++ b/qemu/linux-user/m68k/target_syscall.h @@ -0,0 +1,29 @@ +#ifndef TARGET_SYSCALL_H +#define TARGET_SYSCALL_H + +/* this struct defines the way the registers are stored on the + stack during a system call. */ + +struct target_pt_regs { + abi_long d1, d2, d3, d4, d5, d6, d7; + abi_long a0, a1, a2, a3, a4, a5, a6; + abi_ulong d0; + abi_ulong usp; + abi_ulong orig_d0; + int16_t stkadj; + uint16_t sr; + abi_ulong pc; + uint16_t fntvex; + uint16_t __fill; +}; + +#define UNAME_MACHINE "m68k" +#define UNAME_MINIMUM_RELEASE "2.6.32" + +#define TARGET_MINSIGSTKSZ 2048 +#define TARGET_MLOCKALL_MCL_CURRENT 1 +#define TARGET_MLOCKALL_MCL_FUTURE 2 + +void do_m68k_simcall(CPUM68KState *, int); + +#endif /* TARGET_SYSCALL_H */ diff --git a/qemu/linux-user/main.c b/qemu/linux-user/main.c index fdee98135..5f3ec9747 100644 --- a/qemu/linux-user/main.c +++ b/qemu/linux-user/main.c @@ -16,36 +16,45 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include #include #include #include "qemu.h" -#include "qemu-common.h" +#include "qemu/path.h" +#include "qemu/cutils.h" +#include "qemu/help_option.h" #include "cpu.h" #include "tcg.h" #include "qemu/timer.h" #include "qemu/envlist.h" #include "elf.h" +#include "exec/log.h" char *exec_path; int singlestep; -const char *filename; -const char *argv0; -int gdbstub_port; -envlist_t *envlist; +static const char *filename; +static const char *argv0; +static int gdbstub_port; +static envlist_t *envlist; static const char *cpu_model; unsigned long mmap_min_addr; -#if defined(CONFIG_USE_GUEST_BASE) unsigned long guest_base; int have_guest_base; + +#define EXCP_DUMP(env, fmt, ...) \ +do { \ + CPUState *cs = ENV_GET_CPU(env); \ + fprintf(stderr, fmt , ## __VA_ARGS__); \ + cpu_dump_state(cs, stderr, fprintf, 0); \ + if (qemu_log_separate()) { \ + qemu_log(fmt, ## __VA_ARGS__); \ + log_cpu_state(cs, 0); \ + } \ +} while (0) + #if (TARGET_LONG_BITS == 32) && (HOST_LONG_BITS == 64) /* * When running 32-on-64 we should make sure we can fit all of the possible @@ -63,9 +72,8 @@ unsigned long reserved_va = 0xf7000000; #else unsigned long reserved_va; #endif -#endif -static void usage(void); +static void usage(int exitcode); static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX; const char *qemu_uname_release; @@ -107,7 +115,7 @@ static int pending_cpus; /* Make sure everything is in a consistent state for calling fork(). */ void fork_start(void) { - pthread_mutex_lock(&tcg_ctx.tb_ctx.tb_lock); + qemu_mutex_lock(&tcg_ctx.tb_ctx.tb_lock); pthread_mutex_lock(&exclusive_lock); mmap_fork_start(); } @@ -129,11 +137,11 @@ void fork_end(int child) pthread_mutex_init(&cpu_list_mutex, NULL); pthread_cond_init(&exclusive_cond, NULL); pthread_cond_init(&exclusive_resume, NULL); - pthread_mutex_init(&tcg_ctx.tb_ctx.tb_lock, NULL); + qemu_mutex_init(&tcg_ctx.tb_ctx.tb_lock); gdbserver_fork(thread_cpu); } else { pthread_mutex_unlock(&exclusive_lock); - pthread_mutex_unlock(&tcg_ctx.tb_ctx.tb_lock); + qemu_mutex_unlock(&tcg_ctx.tb_ctx.tb_lock); } } @@ -217,7 +225,7 @@ void cpu_list_unlock(void) uint64_t cpu_get_tsc(CPUX86State *env) { - return cpu_get_real_ticks(); + return cpu_get_host_ticks(); } static void write_dt(void *ptr, unsigned long addr, unsigned long limit, @@ -418,8 +426,8 @@ void cpu_loop(CPUX86State *env) break; default: pc = env->segs[R_CS].base + env->eip; - fprintf(stderr, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n", - (long)pc, trapnr); + EXCP_DUMP(env, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n", + (long)pc, trapnr); abort(); } process_pending_signals(env); @@ -429,22 +437,54 @@ void cpu_loop(CPUX86State *env) #ifdef TARGET_ARM -#define get_user_code_u32(x, gaddr, doswap) \ +#define get_user_code_u32(x, gaddr, env) \ ({ abi_long __r = get_user_u32((x), (gaddr)); \ - if (!__r && (doswap)) { \ + if (!__r && bswap_code(arm_sctlr_b(env))) { \ (x) = bswap32(x); \ } \ __r; \ }) -#define get_user_code_u16(x, gaddr, doswap) \ +#define get_user_code_u16(x, gaddr, env) \ ({ abi_long __r = get_user_u16((x), (gaddr)); \ - if (!__r && (doswap)) { \ + if (!__r && bswap_code(arm_sctlr_b(env))) { \ (x) = bswap16(x); \ } \ __r; \ }) +#define get_user_data_u32(x, gaddr, env) \ + ({ abi_long __r = get_user_u32((x), (gaddr)); \ + if (!__r && arm_cpu_bswap_data(env)) { \ + (x) = bswap32(x); \ + } \ + __r; \ + }) + +#define get_user_data_u16(x, gaddr, env) \ + ({ abi_long __r = get_user_u16((x), (gaddr)); \ + if (!__r && arm_cpu_bswap_data(env)) { \ + (x) = bswap16(x); \ + } \ + __r; \ + }) + +#define put_user_data_u32(x, gaddr, env) \ + ({ typeof(x) __x = (x); \ + if (arm_cpu_bswap_data(env)) { \ + __x = bswap32(__x); \ + } \ + put_user_u32(__x, (gaddr)); \ + }) + +#define put_user_data_u16(x, gaddr, env) \ + ({ typeof(x) __x = (x); \ + if (arm_cpu_bswap_data(env)) { \ + __x = bswap16(__x); \ + } \ + put_user_u16(__x, (gaddr)); \ + }) + #ifdef TARGET_ABI32 /* Commpage handling -- there is no commpage for AArch64 */ @@ -507,7 +547,7 @@ static void arm_kernel_cmpxchg64_helper(CPUARMState *env) env->regs[0] = -1; cpsr &= ~CPSR_C; } - cpsr_write(env, cpsr, CPSR_C); + cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr); end_exclusive(); return; @@ -556,7 +596,7 @@ do_kernel_trap(CPUARMState *env) env->regs[0] = -1; cpsr &= ~CPSR_C; } - cpsr_write(env, cpsr, CPSR_C); + cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr); end_exclusive(); break; case 0xffff0fe0: /* __kernel_get_tls */ @@ -604,11 +644,11 @@ static int do_strex(CPUARMState *env) segv = get_user_u8(val, addr); break; case 1: - segv = get_user_u16(val, addr); + segv = get_user_data_u16(val, addr, env); break; case 2: case 3: - segv = get_user_u32(val, addr); + segv = get_user_data_u32(val, addr, env); break; default: abort(); @@ -619,12 +659,16 @@ static int do_strex(CPUARMState *env) } if (size == 3) { uint32_t valhi; - segv = get_user_u32(valhi, addr + 4); + segv = get_user_data_u32(valhi, addr + 4, env); if (segv) { env->exception.vaddress = addr + 4; goto done; } - val = deposit64(val, 32, 32, valhi); + if (arm_cpu_bswap_data(env)) { + val = deposit64((uint64_t)valhi, 32, 32, val); + } else { + val = deposit64(val, 32, 32, valhi); + } } if (val != env->exclusive_val) { goto fail; @@ -636,11 +680,11 @@ static int do_strex(CPUARMState *env) segv = put_user_u8(val, addr); break; case 1: - segv = put_user_u16(val, addr); + segv = put_user_data_u16(val, addr, env); break; case 2: case 3: - segv = put_user_u32(val, addr); + segv = put_user_data_u32(val, addr, env); break; } if (segv) { @@ -649,7 +693,7 @@ static int do_strex(CPUARMState *env) } if (size == 3) { val = env->regs[(env->exclusive_info >> 12) & 0xf]; - segv = put_user_u32(val, addr + 4); + segv = put_user_data_u32(val, addr + 4, env); if (segv) { env->exception.vaddress = addr + 4; goto done; @@ -686,7 +730,7 @@ void cpu_loop(CPUARMState *env) /* we handle the FPU emulation here, as Linux */ /* we get the opcode */ /* FIXME - what to do if get_user() fails? */ - get_user_code_u32(opcode, env->regs[15], env->bswap_code); + get_user_code_u32(opcode, env->regs[15], env); rc = EmulateAll(opcode, &ts->fpa, env); if (rc == 0) { /* illegal instruction */ @@ -756,25 +800,23 @@ void cpu_loop(CPUARMState *env) if (trapnr == EXCP_BKPT) { if (env->thumb) { /* FIXME - what to do if get_user() fails? */ - get_user_code_u16(insn, env->regs[15], env->bswap_code); + get_user_code_u16(insn, env->regs[15], env); n = insn & 0xff; env->regs[15] += 2; } else { /* FIXME - what to do if get_user() fails? */ - get_user_code_u32(insn, env->regs[15], env->bswap_code); + get_user_code_u32(insn, env->regs[15], env); n = (insn & 0xf) | ((insn >> 4) & 0xff0); env->regs[15] += 4; } } else { if (env->thumb) { /* FIXME - what to do if get_user() fails? */ - get_user_code_u16(insn, env->regs[15] - 2, - env->bswap_code); + get_user_code_u16(insn, env->regs[15] - 2, env); n = insn & 0xff; } else { /* FIXME - what to do if get_user() fails? */ - get_user_code_u32(insn, env->regs[15] - 4, - env->bswap_code); + get_user_code_u32(insn, env->regs[15] - 4, env); n = insn & 0xffffff; } } @@ -865,11 +907,12 @@ void cpu_loop(CPUARMState *env) if (do_kernel_trap(env)) goto error; break; + case EXCP_YIELD: + /* nothing to do here for user-mode, just resume guest code */ + break; default: error: - fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", - trapnr); - cpu_dump_state(cs, stderr, fprintf, 0); + EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); abort(); } process_pending_signals(env); @@ -1054,10 +1097,14 @@ void cpu_loop(CPUARMState *env) queue_signal(env, info.si_signo, &info); } break; + case EXCP_SEMIHOST: + env->xregs[0] = do_arm_semihosting(env); + break; + case EXCP_YIELD: + /* nothing to do here for user-mode, just resume guest code */ + break; default: - fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", - trapnr); - cpu_dump_state(cs, stderr, fprintf, 0); + EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); abort(); } process_pending_signals(env); @@ -1147,8 +1194,7 @@ void cpu_loop(CPUUniCore32State *env) } error: - fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); - cpu_dump_state(cs, stderr, fprintf, 0); + EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); abort(); } #endif @@ -1413,7 +1459,7 @@ void cpu_loop (CPUSPARCState *env) default: printf ("Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); - exit (1); + exit(EXIT_FAILURE); } process_pending_signals (env); } @@ -1424,7 +1470,7 @@ void cpu_loop (CPUSPARCState *env) #ifdef TARGET_PPC static inline uint64_t cpu_ppc_get_tb(CPUPPCState *env) { - return cpu_get_real_ticks(); + return cpu_get_host_ticks(); } uint64_t cpu_ppc_load_tbl(CPUPPCState *env) @@ -1466,17 +1512,6 @@ int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val) return -1; } -#define EXCP_DUMP(env, fmt, ...) \ -do { \ - CPUState *cs = ENV_GET_CPU(env); \ - fprintf(stderr, fmt , ## __VA_ARGS__); \ - cpu_dump_state(cs, stderr, fprintf, 0); \ - qemu_log(fmt, ## __VA_ARGS__); \ - if (qemu_log_enabled()) { \ - log_cpu_state(cs, 0); \ - } \ -} while (0) - static int do_store_exclusive(CPUPPCState *env) { target_ulong addr; @@ -1649,7 +1684,7 @@ void cpu_loop(CPUPPCState *env) info.si_signo = TARGET_SIGBUS; info.si_errno = 0; info.si_code = TARGET_BUS_ADRALN; - info._sifields._sigfault._addr = env->nip - 4; + info._sifields._sigfault._addr = env->nip; queue_signal(env, info.si_signo, &info); break; case POWERPC_EXCP_PROGRAM: /* Program exception */ @@ -2635,9 +2670,7 @@ done_syscall: break; default: error: - fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", - trapnr); - cpu_dump_state(cs, stderr, fprintf, 0); + EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); abort(); } process_pending_signals(env); @@ -2660,11 +2693,11 @@ void cpu_loop(CPUOpenRISCState *env) switch (trapnr) { case EXCP_RESET: - qemu_log("\nReset request, exit, pc is %#x\n", env->pc); - exit(1); + qemu_log_mask(CPU_LOG_INT, "\nReset request, exit, pc is %#x\n", env->pc); + exit(EXIT_FAILURE); break; case EXCP_BUSERR: - qemu_log("\nBus error, exit, pc is %#x\n", env->pc); + qemu_log_mask(CPU_LOG_INT, "\nBus error, exit, pc is %#x\n", env->pc); gdbsig = TARGET_SIGBUS; break; case EXCP_DPF: @@ -2673,25 +2706,25 @@ void cpu_loop(CPUOpenRISCState *env) gdbsig = TARGET_SIGSEGV; break; case EXCP_TICK: - qemu_log("\nTick time interrupt pc is %#x\n", env->pc); + qemu_log_mask(CPU_LOG_INT, "\nTick time interrupt pc is %#x\n", env->pc); break; case EXCP_ALIGN: - qemu_log("\nAlignment pc is %#x\n", env->pc); + qemu_log_mask(CPU_LOG_INT, "\nAlignment pc is %#x\n", env->pc); gdbsig = TARGET_SIGBUS; break; case EXCP_ILLEGAL: - qemu_log("\nIllegal instructionpc is %#x\n", env->pc); + qemu_log_mask(CPU_LOG_INT, "\nIllegal instructionpc is %#x\n", env->pc); gdbsig = TARGET_SIGILL; break; case EXCP_INT: - qemu_log("\nExternal interruptpc is %#x\n", env->pc); + qemu_log_mask(CPU_LOG_INT, "\nExternal interruptpc is %#x\n", env->pc); break; case EXCP_DTLBMISS: case EXCP_ITLBMISS: - qemu_log("\nTLB miss\n"); + qemu_log_mask(CPU_LOG_INT, "\nTLB miss\n"); break; case EXCP_RANGE: - qemu_log("\nRange\n"); + qemu_log_mask(CPU_LOG_INT, "\nRange\n"); gdbsig = TARGET_SIGSEGV; break; case EXCP_SYSCALL: @@ -2706,26 +2739,25 @@ void cpu_loop(CPUOpenRISCState *env) env->gpr[8], 0, 0); break; case EXCP_FPE: - qemu_log("\nFloating point error\n"); + qemu_log_mask(CPU_LOG_INT, "\nFloating point error\n"); break; case EXCP_TRAP: - qemu_log("\nTrap\n"); + qemu_log_mask(CPU_LOG_INT, "\nTrap\n"); gdbsig = TARGET_SIGTRAP; break; case EXCP_NR: - qemu_log("\nNR\n"); + qemu_log_mask(CPU_LOG_INT, "\nNR\n"); break; default: - qemu_log("\nqemu: unhandled CPU exception %#x - aborting\n", + EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n", trapnr); - cpu_dump_state(cs, stderr, fprintf, 0); gdbsig = TARGET_SIGILL; break; } if (gdbsig) { gdb_handlesig(cs, gdbsig); if (gdbsig != TARGET_SIGTRAP) { - exit(1); + exit(EXIT_FAILURE); } } @@ -2790,7 +2822,7 @@ void cpu_loop(CPUSH4State *env) default: printf ("Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); - exit (1); + exit(EXIT_FAILURE); } process_pending_signals (env); } @@ -2851,7 +2883,7 @@ void cpu_loop(CPUCRISState *env) default: printf ("Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); - exit (1); + exit(EXIT_FAILURE); } process_pending_signals (env); } @@ -2932,7 +2964,7 @@ void cpu_loop(CPUMBState *env) printf ("Unhandled hw-exception: 0x%x\n", env->sregs[SR_ESR] & ESR_EC_MASK); cpu_dump_state(cs, stderr, fprintf, 0); - exit (1); + exit(EXIT_FAILURE); break; } break; @@ -2953,7 +2985,7 @@ void cpu_loop(CPUMBState *env) default: printf ("Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); - exit (1); + exit(EXIT_FAILURE); } process_pending_signals (env); } @@ -3046,9 +3078,7 @@ void cpu_loop(CPUM68KState *env) } break; default: - fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", - trapnr); - cpu_dump_state(cs, stderr, fprintf, 0); + EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); abort(); } process_pending_signals(env); @@ -3122,17 +3152,17 @@ void cpu_loop(CPUAlphaState *env) switch (trapnr) { case EXCP_RESET: fprintf(stderr, "Reset requested. Exit\n"); - exit(1); + exit(EXIT_FAILURE); break; case EXCP_MCHK: fprintf(stderr, "Machine check exception. Exit\n"); - exit(1); + exit(EXIT_FAILURE); break; case EXCP_SMP_INTERRUPT: case EXCP_CLK_INTERRUPT: case EXCP_DEV_INTERRUPT: fprintf(stderr, "External interrupt. Exit\n"); - exit(1); + exit(EXIT_FAILURE); break; case EXCP_MMFAULT: env->lock_addr = -1; @@ -3282,7 +3312,7 @@ void cpu_loop(CPUAlphaState *env) default: printf ("Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); - exit (1); + exit(EXIT_FAILURE); } process_pending_signals (env); } @@ -3386,7 +3416,7 @@ void cpu_loop(CPUS390XState *env) default: fprintf(stderr, "Unhandled program exception: %#x\n", n); cpu_dump_state(cs, stderr, fprintf, 0); - exit(1); + exit(EXIT_FAILURE); } break; @@ -3403,7 +3433,7 @@ void cpu_loop(CPUS390XState *env) default: fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); - exit(1); + exit(EXIT_FAILURE); } process_pending_signals (env); } @@ -3411,6 +3441,252 @@ void cpu_loop(CPUS390XState *env) #endif /* TARGET_S390X */ +#ifdef TARGET_TILEGX + +static void gen_sigill_reg(CPUTLGState *env) +{ + target_siginfo_t info; + + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_PRVREG; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, &info); +} + +static void do_signal(CPUTLGState *env, int signo, int sigcode) +{ + target_siginfo_t info; + + info.si_signo = signo; + info.si_errno = 0; + info._sifields._sigfault._addr = env->pc; + + if (signo == TARGET_SIGSEGV) { + /* The passed in sigcode is a dummy; check for a page mapping + and pass either MAPERR or ACCERR. */ + target_ulong addr = env->excaddr; + info._sifields._sigfault._addr = addr; + if (page_check_range(addr, 1, PAGE_VALID) < 0) { + sigcode = TARGET_SEGV_MAPERR; + } else { + sigcode = TARGET_SEGV_ACCERR; + } + } + info.si_code = sigcode; + + queue_signal(env, info.si_signo, &info); +} + +static void gen_sigsegv_maperr(CPUTLGState *env, target_ulong addr) +{ + env->excaddr = addr; + do_signal(env, TARGET_SIGSEGV, 0); +} + +static void set_regval(CPUTLGState *env, uint8_t reg, uint64_t val) +{ + if (unlikely(reg >= TILEGX_R_COUNT)) { + switch (reg) { + case TILEGX_R_SN: + case TILEGX_R_ZERO: + return; + case TILEGX_R_IDN0: + case TILEGX_R_IDN1: + case TILEGX_R_UDN0: + case TILEGX_R_UDN1: + case TILEGX_R_UDN2: + case TILEGX_R_UDN3: + gen_sigill_reg(env); + return; + default: + g_assert_not_reached(); + } + } + env->regs[reg] = val; +} + +/* + * Compare the 8-byte contents of the CmpValue SPR with the 8-byte value in + * memory at the address held in the first source register. If the values are + * not equal, then no memory operation is performed. If the values are equal, + * the 8-byte quantity from the second source register is written into memory + * at the address held in the first source register. In either case, the result + * of the instruction is the value read from memory. The compare and write to + * memory are atomic and thus can be used for synchronization purposes. This + * instruction only operates for addresses aligned to a 8-byte boundary. + * Unaligned memory access causes an Unaligned Data Reference interrupt. + * + * Functional Description (64-bit) + * uint64_t memVal = memoryReadDoubleWord (rf[SrcA]); + * rf[Dest] = memVal; + * if (memVal == SPR[CmpValueSPR]) + * memoryWriteDoubleWord (rf[SrcA], rf[SrcB]); + * + * Functional Description (32-bit) + * uint64_t memVal = signExtend32 (memoryReadWord (rf[SrcA])); + * rf[Dest] = memVal; + * if (memVal == signExtend32 (SPR[CmpValueSPR])) + * memoryWriteWord (rf[SrcA], rf[SrcB]); + * + * + * This function also processes exch and exch4 which need not process SPR. + */ +static void do_exch(CPUTLGState *env, bool quad, bool cmp) +{ + target_ulong addr; + target_long val, sprval; + + start_exclusive(); + + addr = env->atomic_srca; + if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) { + goto sigsegv_maperr; + } + + if (cmp) { + if (quad) { + sprval = env->spregs[TILEGX_SPR_CMPEXCH]; + } else { + sprval = sextract64(env->spregs[TILEGX_SPR_CMPEXCH], 0, 32); + } + } + + if (!cmp || val == sprval) { + target_long valb = env->atomic_srcb; + if (quad ? put_user_u64(valb, addr) : put_user_u32(valb, addr)) { + goto sigsegv_maperr; + } + } + + set_regval(env, env->atomic_dstr, val); + end_exclusive(); + return; + + sigsegv_maperr: + end_exclusive(); + gen_sigsegv_maperr(env, addr); +} + +static void do_fetch(CPUTLGState *env, int trapnr, bool quad) +{ + int8_t write = 1; + target_ulong addr; + target_long val, valb; + + start_exclusive(); + + addr = env->atomic_srca; + valb = env->atomic_srcb; + if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) { + goto sigsegv_maperr; + } + + switch (trapnr) { + case TILEGX_EXCP_OPCODE_FETCHADD: + case TILEGX_EXCP_OPCODE_FETCHADD4: + valb += val; + break; + case TILEGX_EXCP_OPCODE_FETCHADDGEZ: + valb += val; + if (valb < 0) { + write = 0; + } + break; + case TILEGX_EXCP_OPCODE_FETCHADDGEZ4: + valb += val; + if ((int32_t)valb < 0) { + write = 0; + } + break; + case TILEGX_EXCP_OPCODE_FETCHAND: + case TILEGX_EXCP_OPCODE_FETCHAND4: + valb &= val; + break; + case TILEGX_EXCP_OPCODE_FETCHOR: + case TILEGX_EXCP_OPCODE_FETCHOR4: + valb |= val; + break; + default: + g_assert_not_reached(); + } + + if (write) { + if (quad ? put_user_u64(valb, addr) : put_user_u32(valb, addr)) { + goto sigsegv_maperr; + } + } + + set_regval(env, env->atomic_dstr, val); + end_exclusive(); + return; + + sigsegv_maperr: + end_exclusive(); + gen_sigsegv_maperr(env, addr); +} + +void cpu_loop(CPUTLGState *env) +{ + CPUState *cs = CPU(tilegx_env_get_cpu(env)); + int trapnr; + + while (1) { + cpu_exec_start(cs); + trapnr = cpu_tilegx_exec(cs); + cpu_exec_end(cs); + switch (trapnr) { + case TILEGX_EXCP_SYSCALL: + env->regs[TILEGX_R_RE] = do_syscall(env, env->regs[TILEGX_R_NR], + env->regs[0], env->regs[1], + env->regs[2], env->regs[3], + env->regs[4], env->regs[5], + env->regs[6], env->regs[7]); + env->regs[TILEGX_R_ERR] = TILEGX_IS_ERRNO(env->regs[TILEGX_R_RE]) + ? - env->regs[TILEGX_R_RE] + : 0; + break; + case TILEGX_EXCP_OPCODE_EXCH: + do_exch(env, true, false); + break; + case TILEGX_EXCP_OPCODE_EXCH4: + do_exch(env, false, false); + break; + case TILEGX_EXCP_OPCODE_CMPEXCH: + do_exch(env, true, true); + break; + case TILEGX_EXCP_OPCODE_CMPEXCH4: + do_exch(env, false, true); + break; + case TILEGX_EXCP_OPCODE_FETCHADD: + case TILEGX_EXCP_OPCODE_FETCHADDGEZ: + case TILEGX_EXCP_OPCODE_FETCHAND: + case TILEGX_EXCP_OPCODE_FETCHOR: + do_fetch(env, trapnr, true); + break; + case TILEGX_EXCP_OPCODE_FETCHADD4: + case TILEGX_EXCP_OPCODE_FETCHADDGEZ4: + case TILEGX_EXCP_OPCODE_FETCHAND4: + case TILEGX_EXCP_OPCODE_FETCHOR4: + do_fetch(env, trapnr, false); + break; + case TILEGX_EXCP_SIGNAL: + do_signal(env, env->signo, env->sigcode); + break; + case TILEGX_EXCP_REG_IDN_ACCESS: + case TILEGX_EXCP_REG_UDN_ACCESS: + gen_sigill_reg(env); + break; + default: + fprintf(stderr, "trapnr is %d[0x%x].\n", trapnr, trapnr); + g_assert_not_reached(); + } + process_pending_signals(env); + } +} + +#endif + THREAD CPUState *thread_cpu; void task_settid(TaskState *ts) @@ -3472,7 +3748,7 @@ CPUArchState *cpu_copy(CPUArchState *env) static void handle_arg_help(const char *arg) { - usage(); + usage(EXIT_SUCCESS); } static void handle_arg_log(const char *arg) @@ -3482,7 +3758,7 @@ static void handle_arg_log(const char *arg) mask = qemu_str_to_log_mask(arg); if (!mask) { qemu_print_log_usage(stdout); - exit(1); + exit(EXIT_FAILURE); } qemu_set_log(mask); } @@ -3498,7 +3774,7 @@ static void handle_arg_set_env(const char *arg) r = p = strdup(arg); while ((token = strsep(&p, ",")) != NULL) { if (envlist_setenv(envlist, token) != 0) { - usage(); + usage(EXIT_FAILURE); } } free(r); @@ -3510,7 +3786,7 @@ static void handle_arg_unset_env(const char *arg) r = p = strdup(arg); while ((token = strsep(&p, ",")) != NULL) { if (envlist_unsetenv(envlist, token) != 0) { - usage(); + usage(EXIT_FAILURE); } } free(r); @@ -3526,7 +3802,7 @@ static void handle_arg_stack_size(const char *arg) char *p; guest_stack_size = strtoul(arg, &p, 0); if (guest_stack_size == 0) { - usage(); + usage(EXIT_FAILURE); } if (*p == 'M') { @@ -3547,7 +3823,7 @@ static void handle_arg_pagesize(const char *arg) if (qemu_host_page_size == 0 || (qemu_host_page_size & (qemu_host_page_size - 1)) != 0) { fprintf(stderr, "page size must be a power of two\n"); - exit(1); + exit(EXIT_FAILURE); } } @@ -3557,7 +3833,7 @@ static void handle_arg_randseed(const char *arg) if (parse_uint_full(arg, &seed, 0) != 0 || seed > UINT_MAX) { fprintf(stderr, "Invalid seed number: %s\n", arg); - exit(1); + exit(EXIT_FAILURE); } srand(seed); } @@ -3580,11 +3856,10 @@ static void handle_arg_cpu(const char *arg) #if defined(cpu_list) cpu_list(stdout, &fprintf); #endif - exit(1); + exit(EXIT_FAILURE); } } -#if defined(CONFIG_USE_GUEST_BASE) static void handle_arg_guest_base(const char *arg) { guest_base = strtol(arg, NULL, 0); @@ -3618,15 +3893,14 @@ static void handle_arg_reserved_va(const char *arg) #endif ) { fprintf(stderr, "Reserved virtual address too big\n"); - exit(1); + exit(EXIT_FAILURE); } } if (*p) { fprintf(stderr, "Unrecognised -R size suffix '%s'\n", p); - exit(1); + exit(EXIT_FAILURE); } } -#endif static void handle_arg_singlestep(const char *arg) { @@ -3642,7 +3916,7 @@ static void handle_arg_version(const char *arg) { printf("qemu-" TARGET_NAME " version " QEMU_VERSION QEMU_PKGVERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n"); - exit(0); + exit(EXIT_SUCCESS); } struct qemu_argument { @@ -3657,6 +3931,8 @@ struct qemu_argument { static const struct qemu_argument arg_table[] = { {"h", "", false, handle_arg_help, "", "print this help"}, + {"help", "", false, handle_arg_help, + "", ""}, {"g", "QEMU_GDB", true, handle_arg_gdb, "port", "wait gdb connection to 'port'"}, {"L", "QEMU_LD_PREFIX", true, handle_arg_ld_prefix, @@ -3673,12 +3949,10 @@ static const struct qemu_argument arg_table[] = { "argv0", "forces target process argv[0] to be 'argv0'"}, {"r", "QEMU_UNAME", true, handle_arg_uname, "uname", "set qemu uname release string to 'uname'"}, -#if defined(CONFIG_USE_GUEST_BASE) {"B", "QEMU_GUEST_BASE", true, handle_arg_guest_base, "address", "set guest_base address to 'address'"}, {"R", "QEMU_RESERVED_VA", true, handle_arg_reserved_va, "size", "reserve 'size' bytes for guest virtual address space"}, -#endif {"d", "QEMU_LOG", true, handle_arg_log, "item[,...]", "enable logging of specified items " "(use '-d help' for a list of items)"}, @@ -3697,7 +3971,7 @@ static const struct qemu_argument arg_table[] = { {NULL, NULL, false, NULL, NULL, NULL} }; -static void usage(void) +static void usage(int exitcode) { const struct qemu_argument *arginfo; int maxarglen; @@ -3764,7 +4038,7 @@ static void usage(void) "Note that if you provide several changes to a single variable\n" "the last change will stay in effect.\n"); - exit(1); + exit(exitcode); } static int parse_args(int argc, char **argv) @@ -3798,12 +4072,18 @@ static int parse_args(int argc, char **argv) if (!strcmp(r, "-")) { break; } + /* Treat --foo the same as -foo. */ + if (r[0] == '-') { + r++; + } for (arginfo = arg_table; arginfo->handle_opt != NULL; arginfo++) { if (!strcmp(r, arginfo->argv)) { if (arginfo->has_arg) { if (optind >= argc) { - usage(); + (void) fprintf(stderr, + "qemu: missing argument for option '%s'\n", r); + exit(EXIT_FAILURE); } arginfo->handle_opt(argv[optind]); optind++; @@ -3816,12 +4096,14 @@ static int parse_args(int argc, char **argv) /* no option matched the current argv */ if (arginfo->handle_opt == NULL) { - usage(); + (void) fprintf(stderr, "qemu: unknown option '%s'\n", r); + exit(EXIT_FAILURE); } } if (optind >= argc) { - usage(); + (void) fprintf(stderr, "qemu: no user program specified\n"); + exit(EXIT_FAILURE); } filename = argv[optind]; @@ -3850,7 +4132,7 @@ int main(int argc, char **argv, char **envp) if ((envlist = envlist_create()) == NULL) { (void) fprintf(stderr, "Unable to allocate envlist\n"); - exit(1); + exit(EXIT_FAILURE); } /* add current environment into the list */ @@ -3920,7 +4202,7 @@ int main(int argc, char **argv, char **envp) cpu_model = "or1200"; #elif defined(TARGET_PPC) # ifdef TARGET_PPC64 - cpu_model = "POWER7"; + cpu_model = "POWER8"; # else cpu_model = "750"; # endif @@ -3936,7 +4218,7 @@ int main(int argc, char **argv, char **envp) cpu = cpu_init(cpu_model); if (!cpu) { fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); + exit(EXIT_FAILURE); } env = cpu->env_ptr; cpu_reset(cpu); @@ -3954,7 +4236,6 @@ int main(int argc, char **argv, char **envp) target_environ = envlist_to_environ(envlist, NULL); envlist_free(envlist); -#if defined(CONFIG_USE_GUEST_BASE) /* * Now that page sizes are configured in cpu_init() we can do * proper page alignment for guest_base. @@ -3969,14 +4250,13 @@ int main(int argc, char **argv, char **envp) "space for use as guest address space (check your virtual " "memory ulimit setting or reserve less using -R option)\n", reserved_va); - exit(1); + exit(EXIT_FAILURE); } if (reserved_va) { mmap_next_start = reserved_va; } } -#endif /* CONFIG_USE_GUEST_BASE */ /* * Read in mmap_min_addr kernel parameter. This value is used @@ -3990,7 +4270,7 @@ int main(int argc, char **argv, char **envp) unsigned long tmp; if (fscanf(fp, "%lu", &tmp) == 1) { mmap_min_addr = tmp; - qemu_log("host mmap_min_addr=0x%lx\n", mmap_min_addr); + qemu_log_mask(CPU_LOG_PAGE, "host mmap_min_addr=0x%lx\n", mmap_min_addr); } fclose(fp); } @@ -4003,7 +4283,7 @@ int main(int argc, char **argv, char **envp) target_argv = calloc(target_argc + 1, sizeof (char *)); if (target_argv == NULL) { (void) fprintf(stderr, "Unable to allocate memory for target_argv\n"); - exit(1); + exit(EXIT_FAILURE); } /* @@ -4019,7 +4299,7 @@ int main(int argc, char **argv, char **envp) } target_argv[target_argc] = NULL; - ts = g_malloc0 (sizeof(TaskState)); + ts = g_new0(TaskState, 1); init_task_state(ts); /* build Task State */ ts->info = info; @@ -4032,7 +4312,7 @@ int main(int argc, char **argv, char **envp) execfd = open(filename, O_RDONLY); if (execfd < 0) { printf("Error while loading %s: %s\n", filename, strerror(errno)); - _exit(1); + _exit(EXIT_FAILURE); } } @@ -4040,7 +4320,7 @@ int main(int argc, char **argv, char **envp) info, &bprm); if (ret != 0) { printf("Error while loading %s: %s\n", filename, strerror(-ret)); - _exit(1); + _exit(EXIT_FAILURE); } for (wrk = target_environ; *wrk; wrk++) { @@ -4049,10 +4329,8 @@ int main(int argc, char **argv, char **envp) free(target_environ); - if (qemu_log_enabled()) { -#if defined(CONFIG_USE_GUEST_BASE) + if (qemu_loglevel_mask(CPU_LOG_PAGE)) { qemu_log("guest_base 0x%lx\n", guest_base); -#endif log_page_dump(); qemu_log("start_brk 0x" TARGET_ABI_FMT_lx "\n", info->start_brk); @@ -4072,12 +4350,10 @@ int main(int argc, char **argv, char **envp) syscall_init(); signal_init(); -#if defined(CONFIG_USE_GUEST_BASE) /* Now that we've loaded the binary, GUEST_BASE is fixed. Delay generating the prologue until now so that the prologue can take the real value of GUEST_BASE into account. */ tcg_prologue_init(&tcg_ctx); -#endif #if defined(TARGET_I386) env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK; @@ -4090,7 +4366,7 @@ int main(int argc, char **argv, char **envp) /* enable 64 bit mode if possible */ if (!(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM)) { fprintf(stderr, "The selected x86 CPU does not support 64 bit mode\n"); - exit(1); + exit(EXIT_FAILURE); } env->cr[4] |= CR4_PAE_MASK; env->efer |= MSR_EFER_LMA | MSR_EFER_LME; @@ -4200,7 +4476,7 @@ int main(int argc, char **argv, char **envp) if (!(arm_feature(env, ARM_FEATURE_AARCH64))) { fprintf(stderr, "The selected ARM CPU does not support 64 bit mode\n"); - exit(1); + exit(EXIT_FAILURE); } for (i = 0; i < 31; i++) { @@ -4212,15 +4488,21 @@ int main(int argc, char **argv, char **envp) #elif defined(TARGET_ARM) { int i; - cpsr_write(env, regs->uregs[16], 0xffffffff); + cpsr_write(env, regs->uregs[16], CPSR_USER | CPSR_EXEC, + CPSRWriteByInstr); for(i = 0; i < 16; i++) { env->regs[i] = regs->uregs[i]; } +#ifdef TARGET_WORDS_BIGENDIAN /* Enable BE8. */ if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4 && (info->elf_flags & EF_ARM_BE8)) { - env->bswap_code = 1; + env->uncached_cpsr |= CPSR_E; + env->cp15.sctlr_el[1] |= SCTLR_E0E; + } else { + env->cp15.sctlr_el[1] |= SCTLR_B; } +#endif } #elif defined(TARGET_UNICORE32) { @@ -4386,6 +4668,17 @@ int main(int argc, char **argv, char **envp) env->psw.mask = regs->psw.mask; env->psw.addr = regs->psw.addr; } +#elif defined(TARGET_TILEGX) + { + int i; + for (i = 0; i < TILEGX_R_COUNT; i++) { + env->regs[i] = regs->regs[i]; + } + for (i = 0; i < TILEGX_SPR_COUNT; i++) { + env->spregs[i] = 0; + } + env->pc = regs->pc; + } #else #error unsupported target CPU #endif @@ -4401,7 +4694,7 @@ int main(int argc, char **argv, char **envp) if (gdbserver_start(gdbstub_port) < 0) { fprintf(stderr, "qemu: could not open gdbserver on port %d\n", gdbstub_port); - exit(1); + exit(EXIT_FAILURE); } gdb_handlesig(cpu, 0); } diff --git a/qemu/linux-user/microblaze/syscall_nr.h b/qemu/linux-user/microblaze/syscall_nr.h index 6f530f9d1..0704449ba 100644 --- a/qemu/linux-user/microblaze/syscall_nr.h +++ b/qemu/linux-user/microblaze/syscall_nr.h @@ -382,3 +382,11 @@ #define TARGET_NR_process_vm_writev 378 #define TARGET_NR_kcmp 379 #define TARGET_NR_finit_module 380 +#define TARGET_NR_sched_setattr 381 +#define TARGET_NR_sched_getattr 382 +#define TARGET_NR_renameat2 383 +#define TARGET_NR_seccomp 384 +#define TARGET_NR_getrandom 385 +#define TARGET_NR_memfd_create 386 +#define TARGET_NR_bpf 387 +#define TARGET_NR_execveat 388 diff --git a/qemu/linux-user/microblaze/syscall.h b/qemu/linux-user/microblaze/target_syscall.h similarity index 100% rename from qemu/linux-user/microblaze/syscall.h rename to qemu/linux-user/microblaze/target_syscall.h diff --git a/qemu/linux-user/mips/syscall.h b/qemu/linux-user/mips/syscall.h deleted file mode 100644 index 35ca23b16..000000000 --- a/qemu/linux-user/mips/syscall.h +++ /dev/null @@ -1,233 +0,0 @@ - -/* this struct defines the way the registers are stored on the - stack during a system call. */ - -struct target_pt_regs { - /* Pad bytes for argument save space on the stack. */ - abi_ulong pad0[6]; - - /* Saved main processor registers. */ - abi_ulong regs[32]; - - /* Saved special registers. */ - abi_ulong cp0_status; - abi_ulong lo; - abi_ulong hi; - abi_ulong cp0_badvaddr; - abi_ulong cp0_cause; - abi_ulong cp0_epc; -}; - -/* Target errno definitions taken from asm-mips/errno.h */ -#undef TARGET_ENOMSG -#define TARGET_ENOMSG 35 /* Identifier removed */ -#undef TARGET_EIDRM -#define TARGET_EIDRM 36 /* Identifier removed */ -#undef TARGET_ECHRNG -#define TARGET_ECHRNG 37 /* Channel number out of range */ -#undef TARGET_EL2NSYNC -#define TARGET_EL2NSYNC 38 /* Level 2 not synchronized */ -#undef TARGET_EL3HLT -#define TARGET_EL3HLT 39 /* Level 3 halted */ -#undef TARGET_EL3RST -#define TARGET_EL3RST 40 /* Level 3 reset */ -#undef TARGET_ELNRNG -#define TARGET_ELNRNG 41 /* Link number out of range */ -#undef TARGET_EUNATCH -#define TARGET_EUNATCH 42 /* Protocol driver not attached */ -#undef TARGET_ENOCSI -#define TARGET_ENOCSI 43 /* No CSI structure available */ -#undef TARGET_EL2HLT -#define TARGET_EL2HLT 44 /* Level 2 halted */ -#undef TARGET_EDEADLK -#define TARGET_EDEADLK 45 /* Resource deadlock would occur */ -#undef TARGET_ENOLCK -#define TARGET_ENOLCK 46 /* No record locks available */ -#undef TARGET_EBADE -#define TARGET_EBADE 50 /* Invalid exchange */ -#undef TARGET_EBADR -#define TARGET_EBADR 51 /* Invalid request descriptor */ -#undef TARGET_EXFULL -#define TARGET_EXFULL 52 /* TARGET_Exchange full */ -#undef TARGET_ENOANO -#define TARGET_ENOANO 53 /* No anode */ -#undef TARGET_EBADRQC -#define TARGET_EBADRQC 54 /* Invalid request code */ -#undef TARGET_EBADSLT -#define TARGET_EBADSLT 55 /* Invalid slot */ -#undef TARGET_EDEADLOCK -#define TARGET_EDEADLOCK 56 /* File locking deadlock error */ -#undef TARGET_EBFONT -#define TARGET_EBFONT 59 /* Bad font file format */ -#undef TARGET_ENOSTR -#define TARGET_ENOSTR 60 /* Device not a stream */ -#undef TARGET_ENODATA -#define TARGET_ENODATA 61 /* No data available */ -#undef TARGET_ETIME -#define TARGET_ETIME 62 /* Timer expired */ -#undef TARGET_ENOSR -#define TARGET_ENOSR 63 /* Out of streams resources */ -#undef TARGET_ENONET -#define TARGET_ENONET 64 /* Machine is not on the network */ -#undef TARGET_ENOPKG -#define TARGET_ENOPKG 65 /* Package not installed */ -#undef TARGET_EREMOTE -#define TARGET_EREMOTE 66 /* Object is remote */ -#undef TARGET_ENOLINK -#define TARGET_ENOLINK 67 /* Link has been severed */ -#undef TARGET_EADV -#define TARGET_EADV 68 /* Advertise error */ -#undef TARGET_ESRMNT -#define TARGET_ESRMNT 69 /* Srmount error */ -#undef TARGET_ECOMM -#define TARGET_ECOMM 70 /* Communication error on send */ -#undef TARGET_EPROTO -#define TARGET_EPROTO 71 /* Protocol error */ -#undef TARGET_EDOTDOT -#define TARGET_EDOTDOT 73 /* RFS specific error */ -#undef TARGET_EMULTIHOP -#define TARGET_EMULTIHOP 74 /* Multihop attempted */ -#undef TARGET_EBADMSG -#define TARGET_EBADMSG 77 /* Not a data message */ -#undef TARGET_ENAMETOOLONG -#define TARGET_ENAMETOOLONG 78 /* File name too long */ -#undef TARGET_EOVERFLOW -#define TARGET_EOVERFLOW 79 /* Value too large for defined data type */ -#undef TARGET_ENOTUNIQ -#define TARGET_ENOTUNIQ 80 /* Name not unique on network */ -#undef TARGET_EBADFD -#define TARGET_EBADFD 81 /* File descriptor in bad state */ -#undef TARGET_EREMCHG -#define TARGET_EREMCHG 82 /* Remote address changed */ -#undef TARGET_ELIBACC -#define TARGET_ELIBACC 83 /* Can not access a needed shared library */ -#undef TARGET_ELIBBAD -#define TARGET_ELIBBAD 84 /* Accessing a corrupted shared library */ -#undef TARGET_ELIBSCN -#define TARGET_ELIBSCN 85 /* .lib section in a.out corrupted */ -#undef TARGET_ELIBMAX -#define TARGET_ELIBMAX 86 /* Attempting to link in too many shared libraries */ -#undef TARGET_ELIBEXEC -#define TARGET_ELIBEXEC 87 /* Cannot exec a shared library directly */ -#undef TARGET_EILSEQ -#define TARGET_EILSEQ 88 /* Illegal byte sequence */ -#undef TARGET_ENOSYS -#define TARGET_ENOSYS 89 /* Function not implemented */ -#undef TARGET_ELOOP -#define TARGET_ELOOP 90 /* Too many symbolic links encountered */ -#undef TARGET_ERESTART -#define TARGET_ERESTART 91 /* Interrupted system call should be restarted */ -#undef TARGET_ESTRPIPE -#define TARGET_ESTRPIPE 92 /* Streams pipe error */ -#undef TARGET_ENOTEMPTY -#define TARGET_ENOTEMPTY 93 /* Directory not empty */ -#undef TARGET_EUSERS -#define TARGET_EUSERS 94 /* Too many users */ -#undef TARGET_ENOTSOCK -#define TARGET_ENOTSOCK 95 /* Socket operation on non-socket */ -#undef TARGET_EDESTADDRREQ -#define TARGET_EDESTADDRREQ 96 /* Destination address required */ -#undef TARGET_EMSGSIZE -#define TARGET_EMSGSIZE 97 /* Message too long */ -#undef TARGET_EPROTOTYPE -#define TARGET_EPROTOTYPE 98 /* Protocol wrong type for socket */ -#undef TARGET_ENOPROTOOPT -#define TARGET_ENOPROTOOPT 99 /* Protocol not available */ -#undef TARGET_EPROTONOSUPPORT -#define TARGET_EPROTONOSUPPORT 120 /* Protocol not supported */ -#undef TARGET_ESOCKTNOSUPPORT -#define TARGET_ESOCKTNOSUPPORT 121 /* Socket type not supported */ -#undef TARGET_EOPNOTSUPP -#define TARGET_EOPNOTSUPP 122 /* Operation not supported on transport endpoint */ -#undef TARGET_EPFNOSUPPORT -#define TARGET_EPFNOSUPPORT 123 /* Protocol family not supported */ -#undef TARGET_EAFNOSUPPORT -#define TARGET_EAFNOSUPPORT 124 /* Address family not supported by protocol */ -#undef TARGET_EADDRINUSE -#define TARGET_EADDRINUSE 125 /* Address already in use */ -#undef TARGET_EADDRNOTAVAIL -#define TARGET_EADDRNOTAVAIL 126 /* Cannot assign requested address */ -#undef TARGET_ENETDOWN -#define TARGET_ENETDOWN 127 /* Network is down */ -#undef TARGET_ENETUNREACH -#define TARGET_ENETUNREACH 128 /* Network is unreachable */ -#undef TARGET_ENETRESET -#define TARGET_ENETRESET 129 /* Network dropped connection because of reset */ -#undef TARGET_ECONNABORTED -#define TARGET_ECONNABORTED 130 /* Software caused connection abort */ -#undef TARGET_ECONNRESET -#define TARGET_ECONNRESET 131 /* Connection reset by peer */ -#undef TARGET_ENOBUFS -#define TARGET_ENOBUFS 132 /* No buffer space available */ -#undef TARGET_EISCONN -#define TARGET_EISCONN 133 /* Transport endpoint is already connected */ -#undef TARGET_ENOTCONN -#define TARGET_ENOTCONN 134 /* Transport endpoint is not connected */ -#undef TARGET_EUCLEAN -#define TARGET_EUCLEAN 135 /* Structure needs cleaning */ -#undef TARGET_ENOTNAM -#define TARGET_ENOTNAM 137 /* Not a XENIX named type file */ -#undef TARGET_ENAVAIL -#define TARGET_ENAVAIL 138 /* No XENIX semaphores available */ -#undef TARGET_EISNAM -#define TARGET_EISNAM 139 /* Is a named type file */ -#undef TARGET_EREMOTEIO -#define TARGET_EREMOTEIO 140 /* Remote I/O error */ -#undef TARGET_EINIT -#define TARGET_EINIT 141 /* Reserved */ -#undef TARGET_EREMDEV -#define TARGET_EREMDEV 142 /* TARGET_Error 142 */ -#undef TARGET_ESHUTDOWN -#define TARGET_ESHUTDOWN 143 /* Cannot send after transport endpoint shutdown */ -#undef TARGET_ETOOMANYREFS -#define TARGET_ETOOMANYREFS 144 /* Too many references: cannot splice */ -#undef TARGET_ETIMEDOUT -#define TARGET_ETIMEDOUT 145 /* Connection timed out */ -#undef TARGET_ECONNREFUSED -#define TARGET_ECONNREFUSED 146 /* Connection refused */ -#undef TARGET_EHOSTDOWN -#define TARGET_EHOSTDOWN 147 /* Host is down */ -#undef TARGET_EHOSTUNREACH -#define TARGET_EHOSTUNREACH 148 /* No route to host */ -#undef TARGET_EALREADY -#define TARGET_EALREADY 149 /* Operation already in progress */ -#undef TARGET_EINPROGRESS -#define TARGET_EINPROGRESS 150 /* Operation now in progress */ -#undef TARGET_ESTALE -#define TARGET_ESTALE 151 /* Stale NFS file handle */ -#undef TARGET_ECANCELED -#define TARGET_ECANCELED 158 /* AIO operation canceled */ -/* - * These error are Linux extensions. - */ -#undef TARGET_ENOMEDIUM -#define TARGET_ENOMEDIUM 159 /* No medium found */ -#undef TARGET_EMEDIUMTYPE -#define TARGET_EMEDIUMTYPE 160 /* Wrong medium type */ -#undef TARGET_ENOKEY -#define TARGET_ENOKEY 161 /* Required key not available */ -#undef TARGET_EKEYEXPIRED -#define TARGET_EKEYEXPIRED 162 /* Key has expired */ -#undef TARGET_EKEYREVOKED -#define TARGET_EKEYREVOKED 163 /* Key has been revoked */ -#undef TARGET_EKEYREJECTED -#define TARGET_EKEYREJECTED 164 /* Key was rejected by service */ - -/* for robust mutexes */ -#undef TARGET_EOWNERDEAD -#define TARGET_EOWNERDEAD 165 /* Owner died */ -#undef TARGET_ENOTRECOVERABLE -#define TARGET_ENOTRECOVERABLE 166 /* State not recoverable */ - - - -/* Nasty hack: define a fake errno value for use by sigreturn. */ -#define TARGET_QEMU_ESIGRETURN 255 - -#define UNAME_MACHINE "mips" -#define UNAME_MINIMUM_RELEASE "2.6.32" - -#define TARGET_CLONE_BACKWARDS -#define TARGET_MINSIGSTKSZ 2048 -#define TARGET_MLOCKALL_MCL_CURRENT 1 -#define TARGET_MLOCKALL_MCL_FUTURE 2 diff --git a/qemu/linux-user/mips/syscall_nr.h b/qemu/linux-user/mips/syscall_nr.h index 2d1a13ee2..6819f865e 100644 --- a/qemu/linux-user/mips/syscall_nr.h +++ b/qemu/linux-user/mips/syscall_nr.h @@ -351,3 +351,15 @@ #define TARGET_NR_process_vm_writev (TARGET_NR_Linux + 346) #define TARGET_NR_kcmp (TARGET_NR_Linux + 347) #define TARGET_NR_finit_module (TARGET_NR_Linux + 348) + +#define TARGET_NR_sched_setattr (TARGET_NR_Linux + 349) +#define TARGET_NR_sched_getattr (TARGET_NR_Linux + 350) +#define TARGET_NR_renameat2 (TARGET_NR_Linux + 351) +#define TARGET_NR_seccomp (TARGET_NR_Linux + 352) +#define TARGET_NR_getrandom (TARGET_NR_Linux + 353) +#define TARGET_NR_memfd_create (TARGET_NR_Linux + 354) +#define TARGET_NR_bpf (TARGET_NR_Linux + 355) +#define TARGET_NR_execveat (TARGET_NR_Linux + 356) +#define TARGET_NR_userfaultfd (TARGET_NR_Linux + 357) +#define TARGET_NR_membarrier (TARGET_NR_Linux + 358) +#define TARGET_NR_mlock2 (TARGET_NR_Linux + 359) diff --git a/qemu/linux-user/mips/target_syscall.h b/qemu/linux-user/mips/target_syscall.h new file mode 100644 index 000000000..68db160e5 --- /dev/null +++ b/qemu/linux-user/mips/target_syscall.h @@ -0,0 +1,237 @@ +#ifndef TARGET_SYSCALL_H +#define TARGET_SYSCALL_H + +/* this struct defines the way the registers are stored on the + stack during a system call. */ + +struct target_pt_regs { + /* Pad bytes for argument save space on the stack. */ + abi_ulong pad0[6]; + + /* Saved main processor registers. */ + abi_ulong regs[32]; + + /* Saved special registers. */ + abi_ulong cp0_status; + abi_ulong lo; + abi_ulong hi; + abi_ulong cp0_badvaddr; + abi_ulong cp0_cause; + abi_ulong cp0_epc; +}; + +/* Target errno definitions taken from asm-mips/errno.h */ +#undef TARGET_ENOMSG +#define TARGET_ENOMSG 35 /* Identifier removed */ +#undef TARGET_EIDRM +#define TARGET_EIDRM 36 /* Identifier removed */ +#undef TARGET_ECHRNG +#define TARGET_ECHRNG 37 /* Channel number out of range */ +#undef TARGET_EL2NSYNC +#define TARGET_EL2NSYNC 38 /* Level 2 not synchronized */ +#undef TARGET_EL3HLT +#define TARGET_EL3HLT 39 /* Level 3 halted */ +#undef TARGET_EL3RST +#define TARGET_EL3RST 40 /* Level 3 reset */ +#undef TARGET_ELNRNG +#define TARGET_ELNRNG 41 /* Link number out of range */ +#undef TARGET_EUNATCH +#define TARGET_EUNATCH 42 /* Protocol driver not attached */ +#undef TARGET_ENOCSI +#define TARGET_ENOCSI 43 /* No CSI structure available */ +#undef TARGET_EL2HLT +#define TARGET_EL2HLT 44 /* Level 2 halted */ +#undef TARGET_EDEADLK +#define TARGET_EDEADLK 45 /* Resource deadlock would occur */ +#undef TARGET_ENOLCK +#define TARGET_ENOLCK 46 /* No record locks available */ +#undef TARGET_EBADE +#define TARGET_EBADE 50 /* Invalid exchange */ +#undef TARGET_EBADR +#define TARGET_EBADR 51 /* Invalid request descriptor */ +#undef TARGET_EXFULL +#define TARGET_EXFULL 52 /* TARGET_Exchange full */ +#undef TARGET_ENOANO +#define TARGET_ENOANO 53 /* No anode */ +#undef TARGET_EBADRQC +#define TARGET_EBADRQC 54 /* Invalid request code */ +#undef TARGET_EBADSLT +#define TARGET_EBADSLT 55 /* Invalid slot */ +#undef TARGET_EDEADLOCK +#define TARGET_EDEADLOCK 56 /* File locking deadlock error */ +#undef TARGET_EBFONT +#define TARGET_EBFONT 59 /* Bad font file format */ +#undef TARGET_ENOSTR +#define TARGET_ENOSTR 60 /* Device not a stream */ +#undef TARGET_ENODATA +#define TARGET_ENODATA 61 /* No data available */ +#undef TARGET_ETIME +#define TARGET_ETIME 62 /* Timer expired */ +#undef TARGET_ENOSR +#define TARGET_ENOSR 63 /* Out of streams resources */ +#undef TARGET_ENONET +#define TARGET_ENONET 64 /* Machine is not on the network */ +#undef TARGET_ENOPKG +#define TARGET_ENOPKG 65 /* Package not installed */ +#undef TARGET_EREMOTE +#define TARGET_EREMOTE 66 /* Object is remote */ +#undef TARGET_ENOLINK +#define TARGET_ENOLINK 67 /* Link has been severed */ +#undef TARGET_EADV +#define TARGET_EADV 68 /* Advertise error */ +#undef TARGET_ESRMNT +#define TARGET_ESRMNT 69 /* Srmount error */ +#undef TARGET_ECOMM +#define TARGET_ECOMM 70 /* Communication error on send */ +#undef TARGET_EPROTO +#define TARGET_EPROTO 71 /* Protocol error */ +#undef TARGET_EDOTDOT +#define TARGET_EDOTDOT 73 /* RFS specific error */ +#undef TARGET_EMULTIHOP +#define TARGET_EMULTIHOP 74 /* Multihop attempted */ +#undef TARGET_EBADMSG +#define TARGET_EBADMSG 77 /* Not a data message */ +#undef TARGET_ENAMETOOLONG +#define TARGET_ENAMETOOLONG 78 /* File name too long */ +#undef TARGET_EOVERFLOW +#define TARGET_EOVERFLOW 79 /* Value too large for defined data type */ +#undef TARGET_ENOTUNIQ +#define TARGET_ENOTUNIQ 80 /* Name not unique on network */ +#undef TARGET_EBADFD +#define TARGET_EBADFD 81 /* File descriptor in bad state */ +#undef TARGET_EREMCHG +#define TARGET_EREMCHG 82 /* Remote address changed */ +#undef TARGET_ELIBACC +#define TARGET_ELIBACC 83 /* Can not access a needed shared library */ +#undef TARGET_ELIBBAD +#define TARGET_ELIBBAD 84 /* Accessing a corrupted shared library */ +#undef TARGET_ELIBSCN +#define TARGET_ELIBSCN 85 /* .lib section in a.out corrupted */ +#undef TARGET_ELIBMAX +#define TARGET_ELIBMAX 86 /* Attempting to link in too many shared libraries */ +#undef TARGET_ELIBEXEC +#define TARGET_ELIBEXEC 87 /* Cannot exec a shared library directly */ +#undef TARGET_EILSEQ +#define TARGET_EILSEQ 88 /* Illegal byte sequence */ +#undef TARGET_ENOSYS +#define TARGET_ENOSYS 89 /* Function not implemented */ +#undef TARGET_ELOOP +#define TARGET_ELOOP 90 /* Too many symbolic links encountered */ +#undef TARGET_ERESTART +#define TARGET_ERESTART 91 /* Interrupted system call should be restarted */ +#undef TARGET_ESTRPIPE +#define TARGET_ESTRPIPE 92 /* Streams pipe error */ +#undef TARGET_ENOTEMPTY +#define TARGET_ENOTEMPTY 93 /* Directory not empty */ +#undef TARGET_EUSERS +#define TARGET_EUSERS 94 /* Too many users */ +#undef TARGET_ENOTSOCK +#define TARGET_ENOTSOCK 95 /* Socket operation on non-socket */ +#undef TARGET_EDESTADDRREQ +#define TARGET_EDESTADDRREQ 96 /* Destination address required */ +#undef TARGET_EMSGSIZE +#define TARGET_EMSGSIZE 97 /* Message too long */ +#undef TARGET_EPROTOTYPE +#define TARGET_EPROTOTYPE 98 /* Protocol wrong type for socket */ +#undef TARGET_ENOPROTOOPT +#define TARGET_ENOPROTOOPT 99 /* Protocol not available */ +#undef TARGET_EPROTONOSUPPORT +#define TARGET_EPROTONOSUPPORT 120 /* Protocol not supported */ +#undef TARGET_ESOCKTNOSUPPORT +#define TARGET_ESOCKTNOSUPPORT 121 /* Socket type not supported */ +#undef TARGET_EOPNOTSUPP +#define TARGET_EOPNOTSUPP 122 /* Operation not supported on transport endpoint */ +#undef TARGET_EPFNOSUPPORT +#define TARGET_EPFNOSUPPORT 123 /* Protocol family not supported */ +#undef TARGET_EAFNOSUPPORT +#define TARGET_EAFNOSUPPORT 124 /* Address family not supported by protocol */ +#undef TARGET_EADDRINUSE +#define TARGET_EADDRINUSE 125 /* Address already in use */ +#undef TARGET_EADDRNOTAVAIL +#define TARGET_EADDRNOTAVAIL 126 /* Cannot assign requested address */ +#undef TARGET_ENETDOWN +#define TARGET_ENETDOWN 127 /* Network is down */ +#undef TARGET_ENETUNREACH +#define TARGET_ENETUNREACH 128 /* Network is unreachable */ +#undef TARGET_ENETRESET +#define TARGET_ENETRESET 129 /* Network dropped connection because of reset */ +#undef TARGET_ECONNABORTED +#define TARGET_ECONNABORTED 130 /* Software caused connection abort */ +#undef TARGET_ECONNRESET +#define TARGET_ECONNRESET 131 /* Connection reset by peer */ +#undef TARGET_ENOBUFS +#define TARGET_ENOBUFS 132 /* No buffer space available */ +#undef TARGET_EISCONN +#define TARGET_EISCONN 133 /* Transport endpoint is already connected */ +#undef TARGET_ENOTCONN +#define TARGET_ENOTCONN 134 /* Transport endpoint is not connected */ +#undef TARGET_EUCLEAN +#define TARGET_EUCLEAN 135 /* Structure needs cleaning */ +#undef TARGET_ENOTNAM +#define TARGET_ENOTNAM 137 /* Not a XENIX named type file */ +#undef TARGET_ENAVAIL +#define TARGET_ENAVAIL 138 /* No XENIX semaphores available */ +#undef TARGET_EISNAM +#define TARGET_EISNAM 139 /* Is a named type file */ +#undef TARGET_EREMOTEIO +#define TARGET_EREMOTEIO 140 /* Remote I/O error */ +#undef TARGET_EINIT +#define TARGET_EINIT 141 /* Reserved */ +#undef TARGET_EREMDEV +#define TARGET_EREMDEV 142 /* TARGET_Error 142 */ +#undef TARGET_ESHUTDOWN +#define TARGET_ESHUTDOWN 143 /* Cannot send after transport endpoint shutdown */ +#undef TARGET_ETOOMANYREFS +#define TARGET_ETOOMANYREFS 144 /* Too many references: cannot splice */ +#undef TARGET_ETIMEDOUT +#define TARGET_ETIMEDOUT 145 /* Connection timed out */ +#undef TARGET_ECONNREFUSED +#define TARGET_ECONNREFUSED 146 /* Connection refused */ +#undef TARGET_EHOSTDOWN +#define TARGET_EHOSTDOWN 147 /* Host is down */ +#undef TARGET_EHOSTUNREACH +#define TARGET_EHOSTUNREACH 148 /* No route to host */ +#undef TARGET_EALREADY +#define TARGET_EALREADY 149 /* Operation already in progress */ +#undef TARGET_EINPROGRESS +#define TARGET_EINPROGRESS 150 /* Operation now in progress */ +#undef TARGET_ESTALE +#define TARGET_ESTALE 151 /* Stale NFS file handle */ +#undef TARGET_ECANCELED +#define TARGET_ECANCELED 158 /* AIO operation canceled */ +/* + * These error are Linux extensions. + */ +#undef TARGET_ENOMEDIUM +#define TARGET_ENOMEDIUM 159 /* No medium found */ +#undef TARGET_EMEDIUMTYPE +#define TARGET_EMEDIUMTYPE 160 /* Wrong medium type */ +#undef TARGET_ENOKEY +#define TARGET_ENOKEY 161 /* Required key not available */ +#undef TARGET_EKEYEXPIRED +#define TARGET_EKEYEXPIRED 162 /* Key has expired */ +#undef TARGET_EKEYREVOKED +#define TARGET_EKEYREVOKED 163 /* Key has been revoked */ +#undef TARGET_EKEYREJECTED +#define TARGET_EKEYREJECTED 164 /* Key was rejected by service */ + +/* for robust mutexes */ +#undef TARGET_EOWNERDEAD +#define TARGET_EOWNERDEAD 165 /* Owner died */ +#undef TARGET_ENOTRECOVERABLE +#define TARGET_ENOTRECOVERABLE 166 /* State not recoverable */ + + + +/* Nasty hack: define a fake errno value for use by sigreturn. */ +#define TARGET_QEMU_ESIGRETURN 255 + +#define UNAME_MACHINE "mips" +#define UNAME_MINIMUM_RELEASE "2.6.32" + +#define TARGET_CLONE_BACKWARDS +#define TARGET_MINSIGSTKSZ 2048 +#define TARGET_MLOCKALL_MCL_CURRENT 1 +#define TARGET_MLOCKALL_MCL_FUTURE 2 + +#endif /* TARGET_SYSCALL_H */ diff --git a/qemu/linux-user/mips64/syscall.h b/qemu/linux-user/mips64/syscall.h deleted file mode 100644 index 6733107dd..000000000 --- a/qemu/linux-user/mips64/syscall.h +++ /dev/null @@ -1,230 +0,0 @@ - -/* this struct defines the way the registers are stored on the - stack during a system call. */ - -struct target_pt_regs { - /* Saved main processor registers. */ - target_ulong regs[32]; - - /* Saved special registers. */ - target_ulong cp0_status; - target_ulong lo; - target_ulong hi; - target_ulong cp0_badvaddr; - target_ulong cp0_cause; - target_ulong cp0_epc; -}; - -/* Target errno definitions taken from asm-mips/errno.h */ -#undef TARGET_ENOMSG -#define TARGET_ENOMSG 35 /* Identifier removed */ -#undef TARGET_EIDRM -#define TARGET_EIDRM 36 /* Identifier removed */ -#undef TARGET_ECHRNG -#define TARGET_ECHRNG 37 /* Channel number out of range */ -#undef TARGET_EL2NSYNC -#define TARGET_EL2NSYNC 38 /* Level 2 not synchronized */ -#undef TARGET_EL3HLT -#define TARGET_EL3HLT 39 /* Level 3 halted */ -#undef TARGET_EL3RST -#define TARGET_EL3RST 40 /* Level 3 reset */ -#undef TARGET_ELNRNG -#define TARGET_ELNRNG 41 /* Link number out of range */ -#undef TARGET_EUNATCH -#define TARGET_EUNATCH 42 /* Protocol driver not attached */ -#undef TARGET_ENOCSI -#define TARGET_ENOCSI 43 /* No CSI structure available */ -#undef TARGET_EL2HLT -#define TARGET_EL2HLT 44 /* Level 2 halted */ -#undef TARGET_EDEADLK -#define TARGET_EDEADLK 45 /* Resource deadlock would occur */ -#undef TARGET_ENOLCK -#define TARGET_ENOLCK 46 /* No record locks available */ -#undef TARGET_EBADE -#define TARGET_EBADE 50 /* Invalid exchange */ -#undef TARGET_EBADR -#define TARGET_EBADR 51 /* Invalid request descriptor */ -#undef TARGET_EXFULL -#define TARGET_EXFULL 52 /* TARGET_Exchange full */ -#undef TARGET_ENOANO -#define TARGET_ENOANO 53 /* No anode */ -#undef TARGET_EBADRQC -#define TARGET_EBADRQC 54 /* Invalid request code */ -#undef TARGET_EBADSLT -#define TARGET_EBADSLT 55 /* Invalid slot */ -#undef TARGET_EDEADLOCK -#define TARGET_EDEADLOCK 56 /* File locking deadlock error */ -#undef TARGET_EBFONT -#define TARGET_EBFONT 59 /* Bad font file format */ -#undef TARGET_ENOSTR -#define TARGET_ENOSTR 60 /* Device not a stream */ -#undef TARGET_ENODATA -#define TARGET_ENODATA 61 /* No data available */ -#undef TARGET_ETIME -#define TARGET_ETIME 62 /* Timer expired */ -#undef TARGET_ENOSR -#define TARGET_ENOSR 63 /* Out of streams resources */ -#undef TARGET_ENONET -#define TARGET_ENONET 64 /* Machine is not on the network */ -#undef TARGET_ENOPKG -#define TARGET_ENOPKG 65 /* Package not installed */ -#undef TARGET_EREMOTE -#define TARGET_EREMOTE 66 /* Object is remote */ -#undef TARGET_ENOLINK -#define TARGET_ENOLINK 67 /* Link has been severed */ -#undef TARGET_EADV -#define TARGET_EADV 68 /* Advertise error */ -#undef TARGET_ESRMNT -#define TARGET_ESRMNT 69 /* Srmount error */ -#undef TARGET_ECOMM -#define TARGET_ECOMM 70 /* Communication error on send */ -#undef TARGET_EPROTO -#define TARGET_EPROTO 71 /* Protocol error */ -#undef TARGET_EDOTDOT -#define TARGET_EDOTDOT 73 /* RFS specific error */ -#undef TARGET_EMULTIHOP -#define TARGET_EMULTIHOP 74 /* Multihop attempted */ -#undef TARGET_EBADMSG -#define TARGET_EBADMSG 77 /* Not a data message */ -#undef TARGET_ENAMETOOLONG -#define TARGET_ENAMETOOLONG 78 /* File name too long */ -#undef TARGET_EOVERFLOW -#define TARGET_EOVERFLOW 79 /* Value too large for defined data type */ -#undef TARGET_ENOTUNIQ -#define TARGET_ENOTUNIQ 80 /* Name not unique on network */ -#undef TARGET_EBADFD -#define TARGET_EBADFD 81 /* File descriptor in bad state */ -#undef TARGET_EREMCHG -#define TARGET_EREMCHG 82 /* Remote address changed */ -#undef TARGET_ELIBACC -#define TARGET_ELIBACC 83 /* Can not access a needed shared library */ -#undef TARGET_ELIBBAD -#define TARGET_ELIBBAD 84 /* Accessing a corrupted shared library */ -#undef TARGET_ELIBSCN -#define TARGET_ELIBSCN 85 /* .lib section in a.out corrupted */ -#undef TARGET_ELIBMAX -#define TARGET_ELIBMAX 86 /* Attempting to link in too many shared libraries */ -#undef TARGET_ELIBEXEC -#define TARGET_ELIBEXEC 87 /* Cannot exec a shared library directly */ -#undef TARGET_EILSEQ -#define TARGET_EILSEQ 88 /* Illegal byte sequence */ -#undef TARGET_ENOSYS -#define TARGET_ENOSYS 89 /* Function not implemented */ -#undef TARGET_ELOOP -#define TARGET_ELOOP 90 /* Too many symbolic links encountered */ -#undef TARGET_ERESTART -#define TARGET_ERESTART 91 /* Interrupted system call should be restarted */ -#undef TARGET_ESTRPIPE -#define TARGET_ESTRPIPE 92 /* Streams pipe error */ -#undef TARGET_ENOTEMPTY -#define TARGET_ENOTEMPTY 93 /* Directory not empty */ -#undef TARGET_EUSERS -#define TARGET_EUSERS 94 /* Too many users */ -#undef TARGET_ENOTSOCK -#define TARGET_ENOTSOCK 95 /* Socket operation on non-socket */ -#undef TARGET_EDESTADDRREQ -#define TARGET_EDESTADDRREQ 96 /* Destination address required */ -#undef TARGET_EMSGSIZE -#define TARGET_EMSGSIZE 97 /* Message too long */ -#undef TARGET_EPROTOTYPE -#define TARGET_EPROTOTYPE 98 /* Protocol wrong type for socket */ -#undef TARGET_ENOPROTOOPT -#define TARGET_ENOPROTOOPT 99 /* Protocol not available */ -#undef TARGET_EPROTONOSUPPORT -#define TARGET_EPROTONOSUPPORT 120 /* Protocol not supported */ -#undef TARGET_ESOCKTNOSUPPORT -#define TARGET_ESOCKTNOSUPPORT 121 /* Socket type not supported */ -#undef TARGET_EOPNOTSUPP -#define TARGET_EOPNOTSUPP 122 /* Operation not supported on transport endpoint */ -#undef TARGET_EPFNOSUPPORT -#define TARGET_EPFNOSUPPORT 123 /* Protocol family not supported */ -#undef TARGET_EAFNOSUPPORT -#define TARGET_EAFNOSUPPORT 124 /* Address family not supported by protocol */ -#undef TARGET_EADDRINUSE -#define TARGET_EADDRINUSE 125 /* Address already in use */ -#undef TARGET_EADDRNOTAVAIL -#define TARGET_EADDRNOTAVAIL 126 /* Cannot assign requested address */ -#undef TARGET_ENETDOWN -#define TARGET_ENETDOWN 127 /* Network is down */ -#undef TARGET_ENETUNREACH -#define TARGET_ENETUNREACH 128 /* Network is unreachable */ -#undef TARGET_ENETRESET -#define TARGET_ENETRESET 129 /* Network dropped connection because of reset */ -#undef TARGET_ECONNABORTED -#define TARGET_ECONNABORTED 130 /* Software caused connection abort */ -#undef TARGET_ECONNRESET -#define TARGET_ECONNRESET 131 /* Connection reset by peer */ -#undef TARGET_ENOBUFS -#define TARGET_ENOBUFS 132 /* No buffer space available */ -#undef TARGET_EISCONN -#define TARGET_EISCONN 133 /* Transport endpoint is already connected */ -#undef TARGET_ENOTCONN -#define TARGET_ENOTCONN 134 /* Transport endpoint is not connected */ -#undef TARGET_EUCLEAN -#define TARGET_EUCLEAN 135 /* Structure needs cleaning */ -#undef TARGET_ENOTNAM -#define TARGET_ENOTNAM 137 /* Not a XENIX named type file */ -#undef TARGET_ENAVAIL -#define TARGET_ENAVAIL 138 /* No XENIX semaphores available */ -#undef TARGET_EISNAM -#define TARGET_EISNAM 139 /* Is a named type file */ -#undef TARGET_EREMOTEIO -#define TARGET_EREMOTEIO 140 /* Remote I/O error */ -#undef TARGET_EINIT -#define TARGET_EINIT 141 /* Reserved */ -#undef TARGET_EREMDEV -#define TARGET_EREMDEV 142 /* TARGET_Error 142 */ -#undef TARGET_ESHUTDOWN -#define TARGET_ESHUTDOWN 143 /* Cannot send after transport endpoint shutdown */ -#undef TARGET_ETOOMANYREFS -#define TARGET_ETOOMANYREFS 144 /* Too many references: cannot splice */ -#undef TARGET_ETIMEDOUT -#define TARGET_ETIMEDOUT 145 /* Connection timed out */ -#undef TARGET_ECONNREFUSED -#define TARGET_ECONNREFUSED 146 /* Connection refused */ -#undef TARGET_EHOSTDOWN -#define TARGET_EHOSTDOWN 147 /* Host is down */ -#undef TARGET_EHOSTUNREACH -#define TARGET_EHOSTUNREACH 148 /* No route to host */ -#undef TARGET_EALREADY -#define TARGET_EALREADY 149 /* Operation already in progress */ -#undef TARGET_EINPROGRESS -#define TARGET_EINPROGRESS 150 /* Operation now in progress */ -#undef TARGET_ESTALE -#define TARGET_ESTALE 151 /* Stale NFS file handle */ -#undef TARGET_ECANCELED -#define TARGET_ECANCELED 158 /* AIO operation canceled */ -/* - * These error are Linux extensions. - */ -#undef TARGET_ENOMEDIUM -#define TARGET_ENOMEDIUM 159 /* No medium found */ -#undef TARGET_EMEDIUMTYPE -#define TARGET_EMEDIUMTYPE 160 /* Wrong medium type */ -#undef TARGET_ENOKEY -#define TARGET_ENOKEY 161 /* Required key not available */ -#undef TARGET_EKEYEXPIRED -#define TARGET_EKEYEXPIRED 162 /* Key has expired */ -#undef TARGET_EKEYREVOKED -#define TARGET_EKEYREVOKED 163 /* Key has been revoked */ -#undef TARGET_EKEYREJECTED -#define TARGET_EKEYREJECTED 164 /* Key was rejected by service */ - -/* for robust mutexes */ -#undef TARGET_EOWNERDEAD -#define TARGET_EOWNERDEAD 165 /* Owner died */ -#undef TARGET_ENOTRECOVERABLE -#define TARGET_ENOTRECOVERABLE 166 /* State not recoverable */ - - - -/* Nasty hack: define a fake errno value for use by sigreturn. */ -#define TARGET_QEMU_ESIGRETURN 255 - -#define UNAME_MACHINE "mips64" -#define UNAME_MINIMUM_RELEASE "2.6.32" - -#define TARGET_CLONE_BACKWARDS -#define TARGET_MINSIGSTKSZ 2048 -#define TARGET_MLOCKALL_MCL_CURRENT 1 -#define TARGET_MLOCKALL_MCL_FUTURE 2 diff --git a/qemu/linux-user/mips64/syscall_nr.h b/qemu/linux-user/mips64/syscall_nr.h index 004232a8a..746cc267e 100644 --- a/qemu/linux-user/mips64/syscall_nr.h +++ b/qemu/linux-user/mips64/syscall_nr.h @@ -316,6 +316,18 @@ #define TARGET_NR_process_vm_writev (TARGET_NR_Linux + 310) #define TARGET_NR_kcmp (TARGET_NR_Linux + 311) #define TARGET_NR_finit_module (TARGET_NR_Linux + 312) +#define TARGET_NR_sched_setattr (TARGET_NR_Linux + 313) +#define TARGET_NR_sched_getattr (TARGET_NR_Linux + 314) +#define TARGET_NR_renameat2 (TARGET_NR_Linux + 315) +#define TARGET_NR_seccomp (TARGET_NR_Linux + 316) +#define TARGET_NR_getrandom (TARGET_NR_Linux + 317) +#define TARGET_NR_memfd_create (TARGET_NR_Linux + 318) +#define TARGET_NR_bpf (TARGET_NR_Linux + 319) +#define TARGET_NR_execveat (TARGET_NR_Linux + 320) +#define TARGET_NR_userfaultfd (TARGET_NR_Linux + 321) +#define TARGET_NR_membarrier (TARGET_NR_Linux + 322) +#define TARGET_NR_mlock2 (TARGET_NR_Linux + 323) + #else /* * Linux 64-bit syscalls are in the range from 5000 to 5999. @@ -630,4 +642,15 @@ #define TARGET_NR_kcmp (TARGET_NR_Linux + 306) #define TARGET_NR_finit_module (TARGET_NR_Linux + 307) #define TARGET_NR_getdents64 (TARGET_NR_Linux + 308) +#define TARGET_NR_sched_setattr (TARGET_NR_Linux + 309) +#define TARGET_NR_sched_getattr (TARGET_NR_Linux + 310) +#define TARGET_NR_renameat2 (TARGET_NR_Linux + 311) +#define TARGET_NR_seccomp (TARGET_NR_Linux + 312) +#define TARGET_NR_getrandom (TARGET_NR_Linux + 313) +#define TARGET_NR_memfd_create (TARGET_NR_Linux + 314) +#define TARGET_NR_bpf (TARGET_NR_Linux + 315) +#define TARGET_NR_execveat (TARGET_NR_Linux + 316) +#define TARGET_NR_userfaultfd (TARGET_NR_Linux + 317) +#define TARGET_NR_membarrier (TARGET_NR_Linux + 318) +#define TARGET_NR_mlock2 (TARGET_NR_Linux + 319) #endif diff --git a/qemu/linux-user/mips64/target_syscall.h b/qemu/linux-user/mips64/target_syscall.h new file mode 100644 index 000000000..0e0c2d232 --- /dev/null +++ b/qemu/linux-user/mips64/target_syscall.h @@ -0,0 +1,234 @@ +#ifndef TARGET_SYSCALL_H +#define TARGET_SYSCALL_H + +/* this struct defines the way the registers are stored on the + stack during a system call. */ + +struct target_pt_regs { + /* Saved main processor registers. */ + target_ulong regs[32]; + + /* Saved special registers. */ + target_ulong cp0_status; + target_ulong lo; + target_ulong hi; + target_ulong cp0_badvaddr; + target_ulong cp0_cause; + target_ulong cp0_epc; +}; + +/* Target errno definitions taken from asm-mips/errno.h */ +#undef TARGET_ENOMSG +#define TARGET_ENOMSG 35 /* Identifier removed */ +#undef TARGET_EIDRM +#define TARGET_EIDRM 36 /* Identifier removed */ +#undef TARGET_ECHRNG +#define TARGET_ECHRNG 37 /* Channel number out of range */ +#undef TARGET_EL2NSYNC +#define TARGET_EL2NSYNC 38 /* Level 2 not synchronized */ +#undef TARGET_EL3HLT +#define TARGET_EL3HLT 39 /* Level 3 halted */ +#undef TARGET_EL3RST +#define TARGET_EL3RST 40 /* Level 3 reset */ +#undef TARGET_ELNRNG +#define TARGET_ELNRNG 41 /* Link number out of range */ +#undef TARGET_EUNATCH +#define TARGET_EUNATCH 42 /* Protocol driver not attached */ +#undef TARGET_ENOCSI +#define TARGET_ENOCSI 43 /* No CSI structure available */ +#undef TARGET_EL2HLT +#define TARGET_EL2HLT 44 /* Level 2 halted */ +#undef TARGET_EDEADLK +#define TARGET_EDEADLK 45 /* Resource deadlock would occur */ +#undef TARGET_ENOLCK +#define TARGET_ENOLCK 46 /* No record locks available */ +#undef TARGET_EBADE +#define TARGET_EBADE 50 /* Invalid exchange */ +#undef TARGET_EBADR +#define TARGET_EBADR 51 /* Invalid request descriptor */ +#undef TARGET_EXFULL +#define TARGET_EXFULL 52 /* TARGET_Exchange full */ +#undef TARGET_ENOANO +#define TARGET_ENOANO 53 /* No anode */ +#undef TARGET_EBADRQC +#define TARGET_EBADRQC 54 /* Invalid request code */ +#undef TARGET_EBADSLT +#define TARGET_EBADSLT 55 /* Invalid slot */ +#undef TARGET_EDEADLOCK +#define TARGET_EDEADLOCK 56 /* File locking deadlock error */ +#undef TARGET_EBFONT +#define TARGET_EBFONT 59 /* Bad font file format */ +#undef TARGET_ENOSTR +#define TARGET_ENOSTR 60 /* Device not a stream */ +#undef TARGET_ENODATA +#define TARGET_ENODATA 61 /* No data available */ +#undef TARGET_ETIME +#define TARGET_ETIME 62 /* Timer expired */ +#undef TARGET_ENOSR +#define TARGET_ENOSR 63 /* Out of streams resources */ +#undef TARGET_ENONET +#define TARGET_ENONET 64 /* Machine is not on the network */ +#undef TARGET_ENOPKG +#define TARGET_ENOPKG 65 /* Package not installed */ +#undef TARGET_EREMOTE +#define TARGET_EREMOTE 66 /* Object is remote */ +#undef TARGET_ENOLINK +#define TARGET_ENOLINK 67 /* Link has been severed */ +#undef TARGET_EADV +#define TARGET_EADV 68 /* Advertise error */ +#undef TARGET_ESRMNT +#define TARGET_ESRMNT 69 /* Srmount error */ +#undef TARGET_ECOMM +#define TARGET_ECOMM 70 /* Communication error on send */ +#undef TARGET_EPROTO +#define TARGET_EPROTO 71 /* Protocol error */ +#undef TARGET_EDOTDOT +#define TARGET_EDOTDOT 73 /* RFS specific error */ +#undef TARGET_EMULTIHOP +#define TARGET_EMULTIHOP 74 /* Multihop attempted */ +#undef TARGET_EBADMSG +#define TARGET_EBADMSG 77 /* Not a data message */ +#undef TARGET_ENAMETOOLONG +#define TARGET_ENAMETOOLONG 78 /* File name too long */ +#undef TARGET_EOVERFLOW +#define TARGET_EOVERFLOW 79 /* Value too large for defined data type */ +#undef TARGET_ENOTUNIQ +#define TARGET_ENOTUNIQ 80 /* Name not unique on network */ +#undef TARGET_EBADFD +#define TARGET_EBADFD 81 /* File descriptor in bad state */ +#undef TARGET_EREMCHG +#define TARGET_EREMCHG 82 /* Remote address changed */ +#undef TARGET_ELIBACC +#define TARGET_ELIBACC 83 /* Can not access a needed shared library */ +#undef TARGET_ELIBBAD +#define TARGET_ELIBBAD 84 /* Accessing a corrupted shared library */ +#undef TARGET_ELIBSCN +#define TARGET_ELIBSCN 85 /* .lib section in a.out corrupted */ +#undef TARGET_ELIBMAX +#define TARGET_ELIBMAX 86 /* Attempting to link in too many shared libraries */ +#undef TARGET_ELIBEXEC +#define TARGET_ELIBEXEC 87 /* Cannot exec a shared library directly */ +#undef TARGET_EILSEQ +#define TARGET_EILSEQ 88 /* Illegal byte sequence */ +#undef TARGET_ENOSYS +#define TARGET_ENOSYS 89 /* Function not implemented */ +#undef TARGET_ELOOP +#define TARGET_ELOOP 90 /* Too many symbolic links encountered */ +#undef TARGET_ERESTART +#define TARGET_ERESTART 91 /* Interrupted system call should be restarted */ +#undef TARGET_ESTRPIPE +#define TARGET_ESTRPIPE 92 /* Streams pipe error */ +#undef TARGET_ENOTEMPTY +#define TARGET_ENOTEMPTY 93 /* Directory not empty */ +#undef TARGET_EUSERS +#define TARGET_EUSERS 94 /* Too many users */ +#undef TARGET_ENOTSOCK +#define TARGET_ENOTSOCK 95 /* Socket operation on non-socket */ +#undef TARGET_EDESTADDRREQ +#define TARGET_EDESTADDRREQ 96 /* Destination address required */ +#undef TARGET_EMSGSIZE +#define TARGET_EMSGSIZE 97 /* Message too long */ +#undef TARGET_EPROTOTYPE +#define TARGET_EPROTOTYPE 98 /* Protocol wrong type for socket */ +#undef TARGET_ENOPROTOOPT +#define TARGET_ENOPROTOOPT 99 /* Protocol not available */ +#undef TARGET_EPROTONOSUPPORT +#define TARGET_EPROTONOSUPPORT 120 /* Protocol not supported */ +#undef TARGET_ESOCKTNOSUPPORT +#define TARGET_ESOCKTNOSUPPORT 121 /* Socket type not supported */ +#undef TARGET_EOPNOTSUPP +#define TARGET_EOPNOTSUPP 122 /* Operation not supported on transport endpoint */ +#undef TARGET_EPFNOSUPPORT +#define TARGET_EPFNOSUPPORT 123 /* Protocol family not supported */ +#undef TARGET_EAFNOSUPPORT +#define TARGET_EAFNOSUPPORT 124 /* Address family not supported by protocol */ +#undef TARGET_EADDRINUSE +#define TARGET_EADDRINUSE 125 /* Address already in use */ +#undef TARGET_EADDRNOTAVAIL +#define TARGET_EADDRNOTAVAIL 126 /* Cannot assign requested address */ +#undef TARGET_ENETDOWN +#define TARGET_ENETDOWN 127 /* Network is down */ +#undef TARGET_ENETUNREACH +#define TARGET_ENETUNREACH 128 /* Network is unreachable */ +#undef TARGET_ENETRESET +#define TARGET_ENETRESET 129 /* Network dropped connection because of reset */ +#undef TARGET_ECONNABORTED +#define TARGET_ECONNABORTED 130 /* Software caused connection abort */ +#undef TARGET_ECONNRESET +#define TARGET_ECONNRESET 131 /* Connection reset by peer */ +#undef TARGET_ENOBUFS +#define TARGET_ENOBUFS 132 /* No buffer space available */ +#undef TARGET_EISCONN +#define TARGET_EISCONN 133 /* Transport endpoint is already connected */ +#undef TARGET_ENOTCONN +#define TARGET_ENOTCONN 134 /* Transport endpoint is not connected */ +#undef TARGET_EUCLEAN +#define TARGET_EUCLEAN 135 /* Structure needs cleaning */ +#undef TARGET_ENOTNAM +#define TARGET_ENOTNAM 137 /* Not a XENIX named type file */ +#undef TARGET_ENAVAIL +#define TARGET_ENAVAIL 138 /* No XENIX semaphores available */ +#undef TARGET_EISNAM +#define TARGET_EISNAM 139 /* Is a named type file */ +#undef TARGET_EREMOTEIO +#define TARGET_EREMOTEIO 140 /* Remote I/O error */ +#undef TARGET_EINIT +#define TARGET_EINIT 141 /* Reserved */ +#undef TARGET_EREMDEV +#define TARGET_EREMDEV 142 /* TARGET_Error 142 */ +#undef TARGET_ESHUTDOWN +#define TARGET_ESHUTDOWN 143 /* Cannot send after transport endpoint shutdown */ +#undef TARGET_ETOOMANYREFS +#define TARGET_ETOOMANYREFS 144 /* Too many references: cannot splice */ +#undef TARGET_ETIMEDOUT +#define TARGET_ETIMEDOUT 145 /* Connection timed out */ +#undef TARGET_ECONNREFUSED +#define TARGET_ECONNREFUSED 146 /* Connection refused */ +#undef TARGET_EHOSTDOWN +#define TARGET_EHOSTDOWN 147 /* Host is down */ +#undef TARGET_EHOSTUNREACH +#define TARGET_EHOSTUNREACH 148 /* No route to host */ +#undef TARGET_EALREADY +#define TARGET_EALREADY 149 /* Operation already in progress */ +#undef TARGET_EINPROGRESS +#define TARGET_EINPROGRESS 150 /* Operation now in progress */ +#undef TARGET_ESTALE +#define TARGET_ESTALE 151 /* Stale NFS file handle */ +#undef TARGET_ECANCELED +#define TARGET_ECANCELED 158 /* AIO operation canceled */ +/* + * These error are Linux extensions. + */ +#undef TARGET_ENOMEDIUM +#define TARGET_ENOMEDIUM 159 /* No medium found */ +#undef TARGET_EMEDIUMTYPE +#define TARGET_EMEDIUMTYPE 160 /* Wrong medium type */ +#undef TARGET_ENOKEY +#define TARGET_ENOKEY 161 /* Required key not available */ +#undef TARGET_EKEYEXPIRED +#define TARGET_EKEYEXPIRED 162 /* Key has expired */ +#undef TARGET_EKEYREVOKED +#define TARGET_EKEYREVOKED 163 /* Key has been revoked */ +#undef TARGET_EKEYREJECTED +#define TARGET_EKEYREJECTED 164 /* Key was rejected by service */ + +/* for robust mutexes */ +#undef TARGET_EOWNERDEAD +#define TARGET_EOWNERDEAD 165 /* Owner died */ +#undef TARGET_ENOTRECOVERABLE +#define TARGET_ENOTRECOVERABLE 166 /* State not recoverable */ + + + +/* Nasty hack: define a fake errno value for use by sigreturn. */ +#define TARGET_QEMU_ESIGRETURN 255 + +#define UNAME_MACHINE "mips64" +#define UNAME_MINIMUM_RELEASE "2.6.32" + +#define TARGET_CLONE_BACKWARDS +#define TARGET_MINSIGSTKSZ 2048 +#define TARGET_MLOCKALL_MCL_CURRENT 1 +#define TARGET_MLOCKALL_MCL_FUTURE 2 + +#endif /* TARGET_SYSCALL_H */ diff --git a/qemu/linux-user/mmap.c b/qemu/linux-user/mmap.c index 78e1b2df4..3519147bc 100644 --- a/qemu/linux-user/mmap.c +++ b/qemu/linux-user/mmap.c @@ -16,14 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ -#include -#include -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include #include #include @@ -186,10 +179,12 @@ static int mmap_frag(abi_ulong real_start, if (prot_new != (prot1 | PROT_WRITE)) mprotect(host_start, qemu_host_page_size, prot_new); } else { - /* just update the protection */ if (prot_new != prot1) { mprotect(host_start, qemu_host_page_size, prot_new); } + if (prot_new & PROT_WRITE) { + memset(g2h(start), 0, end - start); + } } return 0; } @@ -206,7 +201,6 @@ abi_ulong mmap_next_start = TASK_UNMAPPED_BASE; unsigned long last_brk; -#ifdef CONFIG_USE_GUEST_BASE /* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk of guest address space. */ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size) @@ -216,14 +210,14 @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size) int prot; int looped = 0; - if (size > RESERVED_VA) { + if (size > reserved_va) { return (abi_ulong)-1; } size = HOST_PAGE_ALIGN(size); end_addr = start + size; - if (end_addr > RESERVED_VA) { - end_addr = RESERVED_VA; + if (end_addr > reserved_va) { + end_addr = reserved_va; } addr = end_addr - qemu_host_page_size; @@ -232,7 +226,7 @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size) if (looped) { return (abi_ulong)-1; } - end_addr = RESERVED_VA; + end_addr = reserved_va; addr = end_addr - qemu_host_page_size; looped = 1; continue; @@ -253,7 +247,6 @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size) return addr; } -#endif /* * Find and reserve a free memory area of size 'size'. The search @@ -276,11 +269,9 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size) size = HOST_PAGE_ALIGN(size); -#ifdef CONFIG_USE_GUEST_BASE - if (RESERVED_VA) { + if (reserved_va) { return mmap_find_vma_reserved(start, size); } -#endif addr = start; wrapped = repeat = 0; @@ -448,9 +439,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, /* If so, truncate the file map at eof aligned with the hosts real pagesize. Additional anonymous maps will be created beyond EOF. */ - len = (sb.st_size - offset); - len += qemu_real_host_page_size - 1; - len &= ~(qemu_real_host_page_size - 1); + len = REAL_HOST_PAGE_ALIGN(sb.st_size - offset); } } @@ -518,10 +507,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, goto fail; if (!(prot & PROT_WRITE)) { ret = target_mprotect(start, len, prot); - if (ret != 0) { - start = ret; - goto the_end; - } + assert(ret == 0); } goto the_end; } @@ -545,7 +531,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, /* handle the end of the mapping */ if (end < real_end) { ret = mmap_frag(real_end - qemu_host_page_size, - real_end - qemu_host_page_size, real_end, + real_end - qemu_host_page_size, end, prot, flags, fd, offset + real_end - qemu_host_page_size - start); if (ret == -1) @@ -671,7 +657,7 @@ int target_munmap(abi_ulong start, abi_ulong len) ret = 0; /* unmap what we can */ if (real_start < real_end) { - if (RESERVED_VA) { + if (reserved_va) { mmap_reserve(real_start, real_end - real_start); } else { ret = munmap(g2h(real_start), real_end - real_start); @@ -701,7 +687,7 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, flags, g2h(new_addr)); - if (RESERVED_VA && host_addr != MAP_FAILED) { + if (reserved_va && host_addr != MAP_FAILED) { /* If new and old addresses overlap then the above mremap will already have failed with EINVAL. */ mmap_reserve(old_addr, old_size); @@ -719,13 +705,13 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, old_size, new_size, flags | MREMAP_FIXED, g2h(mmap_start)); - if ( RESERVED_VA ) { + if (reserved_va) { mmap_reserve(old_addr, old_size); } } } else { int prot = 0; - if (RESERVED_VA && old_size < new_size) { + if (reserved_va && old_size < new_size) { abi_ulong addr; for (addr = old_addr + old_size; addr < old_addr + new_size; @@ -735,7 +721,7 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, } if (prot == 0) { host_addr = mremap(g2h(old_addr), old_size, new_size, flags); - if (host_addr != MAP_FAILED && RESERVED_VA && old_size > new_size) { + if (host_addr != MAP_FAILED && reserved_va && old_size > new_size) { mmap_reserve(old_addr + old_size, new_size - old_size); } } else { diff --git a/qemu/linux-user/openrisc/syscall.h b/qemu/linux-user/openrisc/syscall.h deleted file mode 100644 index 8ac03656d..000000000 --- a/qemu/linux-user/openrisc/syscall.h +++ /dev/null @@ -1,29 +0,0 @@ -struct target_pt_regs { - union { - struct { - /* Named registers */ - uint32_t sr; /* Stored in place of r0 */ - target_ulong sp; /* r1 */ - }; - struct { - /* Old style */ - target_ulong offset[2]; - target_ulong gprs[30]; - }; - struct { - /* New style */ - target_ulong gpr[32]; - }; - }; - target_ulong pc; - target_ulong orig_gpr11; /* For restarting system calls */ - uint32_t syscallno; /* Syscall number (used by strace) */ - target_ulong dummy; /* Cheap alignment fix */ -}; - -#define UNAME_MACHINE "openrisc" -#define UNAME_MINIMUM_RELEASE "2.6.32" - -#define TARGET_MINSIGSTKSZ 2048 -#define TARGET_MLOCKALL_MCL_CURRENT 1 -#define TARGET_MLOCKALL_MCL_FUTURE 2 diff --git a/qemu/linux-user/openrisc/syscall_nr.h b/qemu/linux-user/openrisc/syscall_nr.h index 4c386eae9..6b1c7d265 100644 --- a/qemu/linux-user/openrisc/syscall_nr.h +++ b/qemu/linux-user/openrisc/syscall_nr.h @@ -382,9 +382,18 @@ #define TARGET_NR_process_vm_writev 271 #define TARGET_NR_kcmp 272 #define TARGET_NR_finit_module 273 - -#undef TARGET_NR_syscalls -#define TARGET_NR_syscalls 274 +#define TARGET_NR_sched_setattr 274 +#define TARGET_NR_sched_getattr 275 +#define TARGET_NR_renameat2 276 +#define TARGET_NR_seccomp 277 +#define TARGET_NR_getrandom 278 +#define TARGET_NR_memfd_create 279 +#define TARGET_NR_bpf 280 +#define TARGET_NR_execveat 281 +#define TARGET_NR_userfaultfd 282 +#define TARGET_NR_membarrier 283 +#define TARGET_NR_mlock2 284 +#define TARGET_NR_copy_file_range 285 /* * All syscalls below here should go away really, @@ -411,9 +420,6 @@ #define TARGET_NR_3264_stat 1038 #define TARGET_NR_3264_lstat 1039 -#undef TARGET_NR_syscalls -#define TARGET_NR_syscalls (TARGET_NR_3264_lstat+1) - #define TARGET_NR_pipe 1040 #define TARGET_NR_dup2 1041 #define TARGET_NR_epoll_create 1042 @@ -421,10 +427,6 @@ #define TARGET_NR_eventfd 1044 #define TARGET_NR_signalfd 1045 -#undef TARGET_NR_syscalls -#define TARGET_NR_syscalls (TARGET_NR_signalfd+1) - - #define TARGET_NR_sendfile 1046 #define TARGET_NR_ftruncate 1047 #define TARGET_NR_truncate 1048 @@ -441,9 +443,6 @@ #define TARGET_NR_lseek 1057 #define TARGET_NR_mmap 1058 -#undef TARGET_NR_syscalls -#define TARGET_NR_syscalls (TARGET_NR_mmap+1) - #define TARGET_NR_alarm 1059 #define __ARCH_WANT_SYS_ALARM #define TARGET_NR_getpgrp 1060 @@ -477,9 +476,6 @@ #define TARGET_NR_fork 1079 -#undef TARGET_NR_syscalls -#define TARGET_NR_syscalls (TARGET_NR_fork+1) - /* * 32 bit systems traditionally used different diff --git a/qemu/linux-user/openrisc/target_syscall.h b/qemu/linux-user/openrisc/target_syscall.h new file mode 100644 index 000000000..19aeffc95 --- /dev/null +++ b/qemu/linux-user/openrisc/target_syscall.h @@ -0,0 +1,34 @@ +#ifndef TARGET_SYSCALL_H +#define TARGET_SYSCALL_H + +struct target_pt_regs { + union { + struct { + /* Named registers */ + uint32_t sr; /* Stored in place of r0 */ + target_ulong sp; /* r1 */ + }; + struct { + /* Old style */ + target_ulong offset[2]; + target_ulong gprs[30]; + }; + struct { + /* New style */ + target_ulong gpr[32]; + }; + }; + target_ulong pc; + target_ulong orig_gpr11; /* For restarting system calls */ + uint32_t syscallno; /* Syscall number (used by strace) */ + target_ulong dummy; /* Cheap alignment fix */ +}; + +#define UNAME_MACHINE "openrisc" +#define UNAME_MINIMUM_RELEASE "2.6.32" + +#define TARGET_MINSIGSTKSZ 2048 +#define TARGET_MLOCKALL_MCL_CURRENT 1 +#define TARGET_MLOCKALL_MCL_FUTURE 2 + +#endif /* TARGET_SYSCALL_H */ diff --git a/qemu/linux-user/ppc/syscall.h b/qemu/linux-user/ppc/syscall.h deleted file mode 100644 index 0daf5cd2d..000000000 --- a/qemu/linux-user/ppc/syscall.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * PPC emulation for qemu: syscall definitions. - * - * Copyright (c) 2003 Jocelyn Mayer - * - * 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 . - */ - -/* XXX: ABSOLUTELY BUGGY: - * for now, this is quite just a cut-and-paste from i386 target... - */ - -/* default linux values for the selectors */ -#define __USER_DS (1) - -struct target_pt_regs { - abi_ulong gpr[32]; - abi_ulong nip; - abi_ulong msr; - abi_ulong orig_gpr3; /* Used for restarting system calls */ - abi_ulong ctr; - abi_ulong link; - abi_ulong xer; - abi_ulong ccr; -#if defined(TARGET_PPC64) && !defined(TARGET_ABI32) - abi_ulong softe; -#else - abi_ulong mq; /* 601 only (not used at present) */ -#endif - /* Used on APUS to hold IPL value. */ - abi_ulong trap; /* Reason for being here */ - abi_ulong dar; /* Fault registers */ - abi_ulong dsisr; - abi_ulong result; /* Result of a system call */ -}; - -/* ioctls */ -struct target_revectored_struct { - abi_ulong __map[8]; /* 256 bits */ -}; - -/* Nasty hack: define a fake errno value for use by sigreturn. */ -#define TARGET_QEMU_ESIGRETURN 255 - -/* - * flags masks - */ - -#if defined(TARGET_PPC64) && !defined(TARGET_ABI32) -#ifdef TARGET_WORDS_BIGENDIAN -#define UNAME_MACHINE "ppc64" -#else -#define UNAME_MACHINE "ppc64le" -#endif -#else -#define UNAME_MACHINE "ppc" -#endif -#define UNAME_MINIMUM_RELEASE "2.6.32" - -#define TARGET_CLONE_BACKWARDS - -#define TARGET_MINSIGSTKSZ 2048 -#define TARGET_MLOCKALL_MCL_CURRENT 0x2000 -#define TARGET_MLOCKALL_MCL_FUTURE 0x4000 diff --git a/qemu/linux-user/ppc/syscall_nr.h b/qemu/linux-user/ppc/syscall_nr.h index 1e1736e11..46ed8a68c 100644 --- a/qemu/linux-user/ppc/syscall_nr.h +++ b/qemu/linux-user/ppc/syscall_nr.h @@ -319,7 +319,7 @@ #define TARGET_NR_epoll_pwait 303 #define TARGET_NR_utimensat 304 #define TARGET_NR_signalfd 305 -#define TARGET_NR_timerfd 306 +#define TARGET_NR_timerfd_create 306 #define TARGET_NR_eventfd 307 #define TARGET_NR_sync_file_range2 308 #define TARGET_NR_fallocate 309 @@ -368,3 +368,27 @@ #define TARGET_NR_process_vm_writev 352 #define TARGET_NR_finit_module 353 #define TARGET_NR_kcmp 354 +#define TARGET_NR_sched_setattr 355 +#define TARGET_NR_sched_getattr 356 +#define TARGET_NR_renameat2 357 +#define TARGET_NR_seccomp 358 +#define TARGET_NR_getrandom 359 +#define TARGET_NR_memfd_create 360 +#define TARGET_NR_bpf 361 +#define TARGET_NR_execveat 362 +#define TARGET_NR_switch_endian 363 +#define TARGET_NR_userfaultfd 364 +#define TARGET_NR_membarrier 365 +#define TARGET_NR_semop 366 +#define TARGET_NR_semget 367 +#define TARGET_NR_semctl 368 +#define TARGET_NR_semtimedop 369 +#define TARGET_NR_msgsnd 370 +#define TARGET_NR_msgrcv 371 +#define TARGET_NR_msgget 372 +#define TARGET_NR_msgctl 373 +#define TARGET_NR_shmat 374 +#define TARGET_NR_shmdt 375 +#define TARGET_NR_shmget 376 +#define TARGET_NR_shmctl 377 +#define TARGET_NR_mlock2 378 diff --git a/qemu/linux-user/ppc/target_syscall.h b/qemu/linux-user/ppc/target_syscall.h new file mode 100644 index 000000000..35cab5946 --- /dev/null +++ b/qemu/linux-user/ppc/target_syscall.h @@ -0,0 +1,80 @@ +/* + * PPC emulation for qemu: syscall definitions. + * + * Copyright (c) 2003 Jocelyn Mayer + * + * 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 . + */ + +#ifndef TARGET_SYSCALL_H +#define TARGET_SYSCALL_H + +/* XXX: ABSOLUTELY BUGGY: + * for now, this is quite just a cut-and-paste from i386 target... + */ + +/* default linux values for the selectors */ +#define __USER_DS (1) + +struct target_pt_regs { + abi_ulong gpr[32]; + abi_ulong nip; + abi_ulong msr; + abi_ulong orig_gpr3; /* Used for restarting system calls */ + abi_ulong ctr; + abi_ulong link; + abi_ulong xer; + abi_ulong ccr; +#if defined(TARGET_PPC64) && !defined(TARGET_ABI32) + abi_ulong softe; +#else + abi_ulong mq; /* 601 only (not used at present) */ +#endif + /* Used on APUS to hold IPL value. */ + abi_ulong trap; /* Reason for being here */ + abi_ulong dar; /* Fault registers */ + abi_ulong dsisr; + abi_ulong result; /* Result of a system call */ +}; + +/* ioctls */ +struct target_revectored_struct { + abi_ulong __map[8]; /* 256 bits */ +}; + +/* Nasty hack: define a fake errno value for use by sigreturn. */ +#define TARGET_QEMU_ESIGRETURN 255 + +/* + * flags masks + */ + +#if defined(TARGET_PPC64) && !defined(TARGET_ABI32) +#ifdef TARGET_WORDS_BIGENDIAN +#define UNAME_MACHINE "ppc64" +#else +#define UNAME_MACHINE "ppc64le" +#endif +#else +#define UNAME_MACHINE "ppc" +#endif +#define UNAME_MINIMUM_RELEASE "2.6.32" + +#define TARGET_CLONE_BACKWARDS + +#define TARGET_MINSIGSTKSZ 2048 +#define TARGET_MLOCKALL_MCL_CURRENT 0x2000 +#define TARGET_MLOCKALL_MCL_FUTURE 0x4000 + +#endif /* TARGET_SYSCALL_H */ diff --git a/qemu/linux-user/qemu.h b/qemu/linux-user/qemu.h index 8012cc2f5..26b0ba273 100644 --- a/qemu/linux-user/qemu.h +++ b/qemu/linux-user/qemu.h @@ -1,22 +1,19 @@ #ifndef QEMU_H #define QEMU_H -#include -#include #include "cpu.h" #include "exec/cpu_ldst.h" #undef DEBUG_REMAP #ifdef DEBUG_REMAP -#include #endif /* DEBUG_REMAP */ #include "exec/user/abitypes.h" #include "exec/user/thunk.h" #include "syscall_defs.h" -#include "syscall.h" +#include "target_syscall.h" #include "exec/gdbstub.h" #include "qemu/queue.h" @@ -36,8 +33,6 @@ struct image_info { abi_ulong start_brk; abi_ulong brk; abi_ulong start_mmap; - abi_ulong mmap; - abi_ulong rss; abi_ulong start_stack; abi_ulong stack_limit; abi_ulong entry; @@ -145,12 +140,6 @@ extern const char *qemu_uname_release; extern unsigned long mmap_min_addr; /* ??? See if we can avoid exposing so much of the loader internals. */ -/* - * MAX_ARG_PAGES defines the number of pages allocated for arguments - * and envelope for the new program. 32 should suffice, this gives - * a maximum env+arg of 128kB w/4KB pages! - */ -#define MAX_ARG_PAGES 33 /* Read a good amount of data initially, to hopefully get all the program headers loaded. */ @@ -162,7 +151,6 @@ extern unsigned long mmap_min_addr; */ struct linux_binprm { char buf[BPRM_BUF_SIZE] __attribute__((aligned)); - void *page[MAX_ARG_PAGES]; abi_ulong p; int fd; int e_uid, e_gid; @@ -261,8 +249,6 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, int target_msync(abi_ulong start, abi_ulong len, int flags); extern unsigned long last_brk; extern abi_ulong mmap_next_start; -void mmap_lock(void); -void mmap_unlock(void); abi_ulong mmap_find_vma(abi_ulong, abi_ulong); void cpu_list_lock(void); void cpu_list_unlock(void); diff --git a/qemu/linux-user/s390x/syscall.h b/qemu/linux-user/s390x/syscall.h deleted file mode 100644 index 35f170af2..000000000 --- a/qemu/linux-user/s390x/syscall.h +++ /dev/null @@ -1,29 +0,0 @@ -/* this typedef defines how a Program Status Word looks like */ -typedef struct { - abi_ulong mask; - abi_ulong addr; -} __attribute__ ((aligned(8))) target_psw_t; - -/* - * The pt_regs struct defines the way the registers are stored on - * the stack during a system call. - */ - -#define TARGET_NUM_GPRS 16 - -struct target_pt_regs { - abi_ulong args[1]; - target_psw_t psw; - abi_ulong gprs[TARGET_NUM_GPRS]; - abi_ulong orig_gpr2; - unsigned short ilen; - unsigned short trap; -}; - -#define UNAME_MACHINE "s390x" -#define UNAME_MINIMUM_RELEASE "2.6.32" - -#define TARGET_CLONE_BACKWARDS2 -#define TARGET_MINSIGSTKSZ 2048 -#define TARGET_MLOCKALL_MCL_CURRENT 1 -#define TARGET_MLOCKALL_MCL_FUTURE 2 diff --git a/qemu/linux-user/s390x/syscall_nr.h b/qemu/linux-user/s390x/syscall_nr.h index 7c0b8b2ea..1a66c5561 100644 --- a/qemu/linux-user/s390x/syscall_nr.h +++ b/qemu/linux-user/s390x/syscall_nr.h @@ -271,6 +271,36 @@ #define TARGET_NR_s390_runtime_instr 342 #define TARGET_NR_kcmp 343 #define TARGET_NR_finit_module 344 +#define TARGET_NR_sched_setattr 345 +#define TARGET_NR_sched_getattr 346 +#define TARGET_NR_renameat2 347 +#define TARGET_NR_seccomp 348 +#define TARGET_NR_getrandom 349 +#define TARGET_NR_memfd_create 350 +#define TARGET_NR_bpf 351 +#define TARGET_NR_s390_pci_mmio_write 352 +#define TARGET_NR_s390_pci_mmio_read 353 +#define TARGET_NR_execveat 354 +#define TARGET_NR_userfaultfd 355 +#define TARGET_NR_membarrier 356 +#define TARGET_NR_recvmmsg 357 +#define TARGET_NR_sendmmsg 358 +#define TARGET_NR_socket 359 +#define TARGET_NR_socketpair 360 +#define TARGET_NR_bind 361 +#define TARGET_NR_connect 362 +#define TARGET_NR_listen 363 +#define TARGET_NR_accept4 364 +#define TARGET_NR_getsockopt 365 +#define TARGET_NR_setsockopt 366 +#define TARGET_NR_getsockname 367 +#define TARGET_NR_getpeername 368 +#define TARGET_NR_sendto 369 +#define TARGET_NR_sendmsg 370 +#define TARGET_NR_recvfrom 371 +#define TARGET_NR_recvmsg 372 +#define TARGET_NR_shutdown 373 +#define TARGET_NR_mlock2 374 /* * There are some system calls that are not present on 64 bit, some diff --git a/qemu/linux-user/s390x/target_syscall.h b/qemu/linux-user/s390x/target_syscall.h new file mode 100644 index 000000000..02061efc7 --- /dev/null +++ b/qemu/linux-user/s390x/target_syscall.h @@ -0,0 +1,34 @@ +#ifndef TARGET_SYSCALL_H +#define TARGET_SYSCALL_H + +/* this typedef defines how a Program Status Word looks like */ +typedef struct { + abi_ulong mask; + abi_ulong addr; +} __attribute__ ((aligned(8))) target_psw_t; + +/* + * The pt_regs struct defines the way the registers are stored on + * the stack during a system call. + */ + +#define TARGET_NUM_GPRS 16 + +struct target_pt_regs { + abi_ulong args[1]; + target_psw_t psw; + abi_ulong gprs[TARGET_NUM_GPRS]; + abi_ulong orig_gpr2; + unsigned short ilen; + unsigned short trap; +}; + +#define UNAME_MACHINE "s390x" +#define UNAME_MINIMUM_RELEASE "2.6.32" + +#define TARGET_CLONE_BACKWARDS2 +#define TARGET_MINSIGSTKSZ 2048 +#define TARGET_MLOCKALL_MCL_CURRENT 1 +#define TARGET_MLOCKALL_MCL_FUTURE 2 + +#endif /* TARGET_SYSCALL_H */ diff --git a/qemu/linux-user/sh4/syscall.h b/qemu/linux-user/sh4/syscall.h deleted file mode 100644 index 7aa4f239c..000000000 --- a/qemu/linux-user/sh4/syscall.h +++ /dev/null @@ -1,17 +0,0 @@ -struct target_pt_regs { - unsigned long regs[16]; - unsigned long pc; - unsigned long pr; - unsigned long sr; - unsigned long gbr; - unsigned long mach; - unsigned long macl; - long tra; -}; - -#define UNAME_MACHINE "sh4" -#define UNAME_MINIMUM_RELEASE "2.6.32" - -#define TARGET_MINSIGSTKSZ 2048 -#define TARGET_MLOCKALL_MCL_CURRENT 1 -#define TARGET_MLOCKALL_MCL_FUTURE 2 diff --git a/qemu/linux-user/sh4/syscall_nr.h b/qemu/linux-user/sh4/syscall_nr.h index bdf8742c6..50099846d 100644 --- a/qemu/linux-user/sh4/syscall_nr.h +++ b/qemu/linux-user/sh4/syscall_nr.h @@ -323,7 +323,7 @@ #define TARGET_NR_epoll_pwait 319 #define TARGET_NR_utimensat 320 #define TARGET_NR_signalfd 321 -#define TARGET_NR_timerfd 322 +#define TARGET_NR_timerfd_create 322 #define TARGET_NR_eventfd 323 #define TARGET_NR_fallocate 324 #define TARGET_NR_timerfd_settime 325 diff --git a/qemu/linux-user/sh4/target_syscall.h b/qemu/linux-user/sh4/target_syscall.h new file mode 100644 index 000000000..9f3381bc9 --- /dev/null +++ b/qemu/linux-user/sh4/target_syscall.h @@ -0,0 +1,22 @@ +#ifndef TARGET_SYSCALL_H +#define TARGET_SYSCALL_H + +struct target_pt_regs { + unsigned long regs[16]; + unsigned long pc; + unsigned long pr; + unsigned long sr; + unsigned long gbr; + unsigned long mach; + unsigned long macl; + long tra; +}; + +#define UNAME_MACHINE "sh4" +#define UNAME_MINIMUM_RELEASE "2.6.32" + +#define TARGET_MINSIGSTKSZ 2048 +#define TARGET_MLOCKALL_MCL_CURRENT 1 +#define TARGET_MLOCKALL_MCL_FUTURE 2 + +#endif /* TARGET_SYSCALL_H */ diff --git a/qemu/linux-user/signal.c b/qemu/linux-user/signal.c index 9d4cef409..96e86c0a2 100644 --- a/qemu/linux-user/signal.c +++ b/qemu/linux-user/signal.c @@ -16,21 +16,14 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ -#include -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include #include #include "qemu.h" #include "qemu-common.h" #include "target_signal.h" - -//#define DEBUG_SIGNAL +#include "trace.h" static struct target_sigaltstack target_sigaltstack_used = { .ss_sp = 0, @@ -445,7 +438,9 @@ static void QEMU_NORETURN force_sig(int target_sig) TaskState *ts = (TaskState *)cpu->opaque; int host_sig, core_dumped = 0; struct sigaction act; + host_sig = target_to_host_signal(target_sig); + trace_user_force_sig(env, target_sig, host_sig); gdb_signalled(env, target_sig); /* dump core if supported by target binary format */ @@ -500,10 +495,7 @@ int queue_signal(CPUArchState *env, int sig, target_siginfo_t *info) abi_ulong handler; int queue; -#if defined(DEBUG_SIGNAL) - fprintf(stderr, "queue_signal: sig=%d\n", - sig); -#endif + trace_user_queue_signal(env, sig); k = &ts->sigtab[sig - 1]; queue = gdb_queuesig (); handler = sigact_table[sig - 1]._sa_handler; @@ -588,9 +580,7 @@ static void host_signal_handler(int host_signum, siginfo_t *info, sig = host_to_target_signal(host_signum); if (sig < 1 || sig > TARGET_NSIG) return; -#if defined(DEBUG_SIGNAL) - fprintf(stderr, "qemu: got signal %d\n", sig); -#endif + trace_user_host_signal(env, host_signum, sig); host_to_target_siginfo_noswap(&tinfo, info); if (queue_signal(env, sig, &tinfo) == 1) { /* interrupt the virtual CPU as soon as possible */ @@ -683,10 +673,6 @@ int do_sigaction(int sig, const struct target_sigaction *act, if (sig < 1 || sig > TARGET_NSIG || sig == TARGET_SIGKILL || sig == TARGET_SIGSTOP) return -EINVAL; k = &sigact_table[sig - 1]; -#if defined(DEBUG_SIGNAL) - fprintf(stderr, "sigaction sig=%d act=0x%p, oact=0x%p\n", - sig, act, oact); -#endif if (oact) { __put_user(k->_sa_handler, &oact->_sa_handler); __put_user(k->sa_flags, &oact->sa_flags); @@ -910,6 +896,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, int i; frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_frame(env, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) goto give_sigsegv; @@ -971,6 +958,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, 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; @@ -1082,9 +1070,7 @@ long do_sigreturn(CPUX86State *env) sigset_t set; int eax, i; -#if defined(DEBUG_SIGNAL) - fprintf(stderr, "do_sigreturn\n"); -#endif + trace_user_do_sigreturn(env, frame_addr); if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) goto badframe; /* set blocked signals */ @@ -1116,6 +1102,7 @@ long do_rt_sigreturn(CPUX86State *env) int eax; frame_addr = env->regs[R_ESP] - 4; + 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); @@ -1319,6 +1306,7 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, abi_ulong frame_addr, return_addr; frame_addr = get_sigframe(ka, env); + trace_user_setup_frame(env, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { goto give_sigsegv; } @@ -1378,6 +1366,7 @@ long do_rt_sigreturn(CPUARMState *env) struct target_rt_sigframe *frame = NULL; abi_ulong frame_addr = env->xregs[31]; + trace_user_do_rt_sigreturn(env, frame_addr); if (frame_addr & 15) { goto badframe; } @@ -1547,82 +1536,84 @@ static void setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/ CPUARMState *env, abi_ulong mask) { - __put_user(env->regs[0], &sc->arm_r0); - __put_user(env->regs[1], &sc->arm_r1); - __put_user(env->regs[2], &sc->arm_r2); - __put_user(env->regs[3], &sc->arm_r3); - __put_user(env->regs[4], &sc->arm_r4); - __put_user(env->regs[5], &sc->arm_r5); - __put_user(env->regs[6], &sc->arm_r6); - __put_user(env->regs[7], &sc->arm_r7); - __put_user(env->regs[8], &sc->arm_r8); - __put_user(env->regs[9], &sc->arm_r9); - __put_user(env->regs[10], &sc->arm_r10); - __put_user(env->regs[11], &sc->arm_fp); - __put_user(env->regs[12], &sc->arm_ip); - __put_user(env->regs[13], &sc->arm_sp); - __put_user(env->regs[14], &sc->arm_lr); - __put_user(env->regs[15], &sc->arm_pc); + __put_user(env->regs[0], &sc->arm_r0); + __put_user(env->regs[1], &sc->arm_r1); + __put_user(env->regs[2], &sc->arm_r2); + __put_user(env->regs[3], &sc->arm_r3); + __put_user(env->regs[4], &sc->arm_r4); + __put_user(env->regs[5], &sc->arm_r5); + __put_user(env->regs[6], &sc->arm_r6); + __put_user(env->regs[7], &sc->arm_r7); + __put_user(env->regs[8], &sc->arm_r8); + __put_user(env->regs[9], &sc->arm_r9); + __put_user(env->regs[10], &sc->arm_r10); + __put_user(env->regs[11], &sc->arm_fp); + __put_user(env->regs[12], &sc->arm_ip); + __put_user(env->regs[13], &sc->arm_sp); + __put_user(env->regs[14], &sc->arm_lr); + __put_user(env->regs[15], &sc->arm_pc); #ifdef TARGET_CONFIG_CPU_32 - __put_user(cpsr_read(env), &sc->arm_cpsr); + __put_user(cpsr_read(env), &sc->arm_cpsr); #endif - __put_user(/* current->thread.trap_no */ 0, &sc->trap_no); - __put_user(/* current->thread.error_code */ 0, &sc->error_code); - __put_user(/* current->thread.address */ 0, &sc->fault_address); - __put_user(mask, &sc->oldmask); + __put_user(/* current->thread.trap_no */ 0, &sc->trap_no); + __put_user(/* current->thread.error_code */ 0, &sc->error_code); + __put_user(/* current->thread.address */ 0, &sc->fault_address); + __put_user(mask, &sc->oldmask); } static inline abi_ulong get_sigframe(struct target_sigaction *ka, CPUARMState *regs, int framesize) { - unsigned long sp = regs->regs[13]; + unsigned long sp = regs->regs[13]; - /* - * This is the X/Open sanctioned signal stack switching. - */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - /* - * ATPCS B01 mandates 8-byte alignment - */ - return (sp - framesize) & ~7; + /* + * This is the X/Open sanctioned signal stack switching. + */ + if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) { + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + /* + * ATPCS B01 mandates 8-byte alignment + */ + return (sp - framesize) & ~7; } static void setup_return(CPUARMState *env, struct target_sigaction *ka, abi_ulong *rc, abi_ulong frame_addr, int usig, abi_ulong rc_addr) { - abi_ulong handler = ka->_sa_handler; - abi_ulong retcode; - int thumb = handler & 1; - uint32_t cpsr = cpsr_read(env); + abi_ulong handler = ka->_sa_handler; + abi_ulong retcode; + int thumb = handler & 1; + uint32_t cpsr = cpsr_read(env); - cpsr &= ~CPSR_IT; - if (thumb) { - cpsr |= CPSR_T; - } else { - cpsr &= ~CPSR_T; - } + cpsr &= ~CPSR_IT; + if (thumb) { + cpsr |= CPSR_T; + } else { + cpsr &= ~CPSR_T; + } - if (ka->sa_flags & TARGET_SA_RESTORER) { - retcode = ka->sa_restorer; - } else { - unsigned int idx = thumb; + if (ka->sa_flags & TARGET_SA_RESTORER) { + retcode = ka->sa_restorer; + } else { + unsigned int idx = thumb; - if (ka->sa_flags & TARGET_SA_SIGINFO) - idx += 2; + if (ka->sa_flags & TARGET_SA_SIGINFO) { + idx += 2; + } __put_user(retcodes[idx], rc); - retcode = rc_addr + thumb; - } + retcode = rc_addr + thumb; + } - env->regs[0] = usig; - env->regs[13] = frame_addr; - env->regs[14] = retcode; - env->regs[15] = handler & (thumb ? ~1 : ~3); - cpsr_write(env, cpsr, 0xffffffff); + env->regs[0] = usig; + env->regs[13] = frame_addr; + env->regs[14] = retcode; + env->regs[15] = handler & (thumb ? ~1 : ~3); + cpsr_write(env, cpsr, CPSR_IT | CPSR_T, CPSRWriteByInstr); } static abi_ulong *setup_sigframe_v2_vfp(abi_ulong *regspace, CPUARMState *env) @@ -1704,6 +1695,7 @@ static void setup_frame_v1(int usig, struct target_sigaction *ka, abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame)); int i; + trace_user_setup_frame(regs, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) return; @@ -1725,6 +1717,7 @@ static void setup_frame_v2(int usig, struct target_sigaction *ka, struct sigframe_v2 *frame; abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame)); + trace_user_setup_frame(regs, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) return; @@ -1757,6 +1750,7 @@ static void setup_rt_frame_v1(int usig, struct target_sigaction *ka, int i; abi_ulong info_addr, uc_addr; + trace_user_setup_rt_frame(env, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) return /* 1 */; @@ -1797,6 +1791,7 @@ static void setup_rt_frame_v2(int usig, struct target_sigaction *ka, abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame)); abi_ulong info_addr, uc_addr; + trace_user_setup_rt_frame(env, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) return /* 1 */; @@ -1850,7 +1845,7 @@ restore_sigcontext(CPUARMState *env, struct target_sigcontext *sc) __get_user(env->regs[15], &sc->arm_pc); #ifdef TARGET_CONFIG_CPU_32 __get_user(cpsr, &sc->arm_cpsr); - cpsr_write(env, cpsr, CPSR_USER | CPSR_EXEC); + cpsr_write(env, cpsr, CPSR_USER | CPSR_EXEC, CPSRWriteByInstr); #endif err |= !valid_user_regs(env); @@ -1872,6 +1867,7 @@ static long do_sigreturn_v1(CPUARMState *env) * not, then the user is trying to mess with us. */ frame_addr = env->regs[13]; + trace_user_do_sigreturn(env, frame_addr); if (frame_addr & 7) { goto badframe; } @@ -2008,6 +2004,7 @@ static long do_sigreturn_v2(CPUARMState *env) * not, then the user is trying to mess with us. */ frame_addr = env->regs[13]; + trace_user_do_sigreturn(env, frame_addr); if (frame_addr & 7) { goto badframe; } @@ -2048,6 +2045,7 @@ static long do_rt_sigreturn_v1(CPUARMState *env) * not, then the user is trying to mess with us. */ frame_addr = env->regs[13]; + trace_user_do_rt_sigreturn(env, frame_addr); if (frame_addr & 7) { goto badframe; } @@ -2089,6 +2087,7 @@ static long do_rt_sigreturn_v2(CPUARMState *env) * not, then the user is trying to mess with us. */ frame_addr = env->regs[13]; + trace_user_do_rt_sigreturn(env, frame_addr); if (frame_addr & 7) { goto badframe; } @@ -2284,13 +2283,13 @@ static void setup_frame(int sig, struct target_sigaction *ka, sigframe_size = NF_ALIGNEDSZ; sf_addr = get_sigframe(ka, env, sigframe_size); + trace_user_setup_frame(env, sf_addr); sf = lock_user(VERIFY_WRITE, sf_addr, sizeof(struct target_signal_frame), 0); if (!sf) goto sigsegv; - //fprintf(stderr, "sf: %x pc %x fp %x sp %x\n", sf, env->pc, env->regwptr[UREG_FP], env->regwptr[UREG_SP]); #if 0 if (invalid_frame_pointer(sf, sigframe_size)) goto sigill_and_return; @@ -2357,7 +2356,6 @@ sigill_and_return: force_sig(TARGET_SIGILL); #endif sigsegv: - //fprintf(stderr, "force_sig\n"); unlock_user(sf, sf_addr, sizeof(struct target_signal_frame)); force_sig(TARGET_SIGSEGV); } @@ -2379,13 +2377,9 @@ long do_sigreturn(CPUSPARCState *env) int err=0, i; sf_addr = env->regwptr[UREG_FP]; + trace_user_do_sigreturn(env, sf_addr); if (!lock_user_struct(VERIFY_READ, sf, sf_addr, 1)) goto segv_and_exit; -#if 0 - fprintf(stderr, "sigreturn\n"); - fprintf(stderr, "sf: %x pc %x fp %x sp %x\n", sf, env->pc, env->regwptr[UREG_FP], env->regwptr[UREG_SP]); -#endif - //cpu_dump_state(env, stderr, fprintf, 0); /* 1. Make sure we are not getting garbage from the user */ @@ -2444,6 +2438,7 @@ segv_and_exit: long do_rt_sigreturn(CPUSPARCState *env) { + trace_user_do_rt_sigreturn(env, 0); fprintf(stderr, "do_rt_sigreturn: not implemented\n"); return -TARGET_ENOSYS; } @@ -2903,6 +2898,7 @@ static void setup_frame(int sig, struct target_sigaction * ka, int i; frame_addr = get_sigframe(ka, regs, sizeof(*frame)); + trace_user_setup_frame(regs, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) goto give_sigsegv; @@ -2949,10 +2945,8 @@ long do_sigreturn(CPUMIPSState *regs) target_sigset_t target_set; int i; -#if defined(DEBUG_SIGNAL) - fprintf(stderr, "do_sigreturn\n"); -#endif frame_addr = regs->active_tc.gpr[29]; + trace_user_do_sigreturn(regs, frame_addr); if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) goto badframe; @@ -2999,6 +2993,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, 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; @@ -3056,10 +3051,8 @@ long do_rt_sigreturn(CPUMIPSState *env) abi_ulong frame_addr; sigset_t blocked; -#if defined(DEBUG_SIGNAL) - fprintf(stderr, "do_rt_sigreturn\n"); -#endif frame_addr = env->active_tc.gpr[29]; + trace_user_do_rt_sigreturn(env, frame_addr); if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) goto badframe; @@ -3214,9 +3207,9 @@ static void setup_frame(int sig, struct target_sigaction *ka, struct target_sigframe *frame; abi_ulong frame_addr; int i; - int err = 0; frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); + trace_user_setup_frame(regs, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) goto give_sigsegv; @@ -3232,15 +3225,14 @@ static void setup_frame(int sig, struct target_sigaction *ka, regs->pr = (unsigned long) ka->sa_restorer; } else { /* Generate return code (system call to sigreturn) */ + abi_ulong retcode_addr = frame_addr + + offsetof(struct target_sigframe, retcode); __put_user(MOVW(2), &frame->retcode[0]); __put_user(TRAP_NOARG, &frame->retcode[1]); __put_user((TARGET_NR_sigreturn), &frame->retcode[2]); - regs->pr = (unsigned long) frame->retcode; + regs->pr = (unsigned long) retcode_addr; } - if (err) - goto give_sigsegv; - /* Set up registers for signal handler */ regs->gregs[15] = frame_addr; regs->gregs[4] = sig; /* Arg for signal handler */ @@ -3263,9 +3255,9 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, struct target_rt_sigframe *frame; abi_ulong frame_addr; int i; - int err = 0; frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); + trace_user_setup_rt_frame(regs, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) goto give_sigsegv; @@ -3292,15 +3284,14 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, regs->pr = (unsigned long) ka->sa_restorer; } else { /* Generate return code (system call to sigreturn) */ + abi_ulong retcode_addr = frame_addr + + offsetof(struct target_rt_sigframe, retcode); __put_user(MOVW(2), &frame->retcode[0]); __put_user(TRAP_NOARG, &frame->retcode[1]); __put_user((TARGET_NR_rt_sigreturn), &frame->retcode[2]); - regs->pr = (unsigned long) frame->retcode; + regs->pr = (unsigned long) retcode_addr; } - if (err) - goto give_sigsegv; - /* Set up registers for signal handler */ regs->gregs[15] = frame_addr; regs->gregs[4] = sig; /* Arg for signal handler */ @@ -3326,10 +3317,8 @@ long do_sigreturn(CPUSH4State *regs) int i; int err = 0; -#if defined(DEBUG_SIGNAL) - fprintf(stderr, "do_sigreturn\n"); -#endif frame_addr = regs->gregs[15]; + trace_user_do_sigreturn(regs, frame_addr); if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) goto badframe; @@ -3362,10 +3351,8 @@ long do_rt_sigreturn(CPUSH4State *regs) sigset_t blocked; target_ulong r0; -#if defined(DEBUG_SIGNAL) - fprintf(stderr, "do_rt_sigreturn\n"); -#endif frame_addr = regs->gregs[15]; + trace_user_do_rt_sigreturn(regs, frame_addr); if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) goto badframe; @@ -3515,6 +3502,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, int i; frame_addr = get_sigframe(ka, env, sizeof *frame); + trace_user_setup_frame(env, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) goto badframe; @@ -3580,6 +3568,7 @@ long do_sigreturn(CPUMBState *env) int i; frame_addr = env->regs[R_SP]; + trace_user_do_sigreturn(env, frame_addr); /* Make sure the guest isn't playing games. */ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) goto badframe; @@ -3605,6 +3594,7 @@ long do_sigreturn(CPUMBState *env) long do_rt_sigreturn(CPUMBState *env) { + trace_user_do_rt_sigreturn(env, 0); fprintf(stderr, "Microblaze do_rt_sigreturn: not implemented\n"); return -TARGET_ENOSYS; } @@ -3694,6 +3684,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, int i; frame_addr = get_sigframe(env, sizeof *frame); + trace_user_setup_frame(env, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) goto badframe; @@ -3747,6 +3738,7 @@ long do_sigreturn(CPUCRISState *env) int i; frame_addr = env->regs[R_SP]; + trace_user_do_sigreturn(env, frame_addr); /* Make sure the guest isn't playing games. */ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) goto badframe; @@ -3768,6 +3760,7 @@ long do_sigreturn(CPUCRISState *env) long do_rt_sigreturn(CPUCRISState *env) { + trace_user_do_rt_sigreturn(env, 0); fprintf(stderr, "CRIS do_rt_sigreturn: not implemented\n"); return -TARGET_ENOSYS; } @@ -3901,12 +3894,6 @@ static inline abi_ulong get_sigframe(struct target_sigaction *ka, return sp; } -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUOpenRISCState *env) -{ - qemu_log("Not implement.\n"); -} - static void setup_rt_frame(int sig, struct target_sigaction *ka, target_siginfo_t *info, target_sigset_t *set, CPUOpenRISCState *env) @@ -3918,6 +3905,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, abi_ulong info_addr, uc_addr; 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; } @@ -3979,14 +3967,15 @@ give_sigsegv: long do_sigreturn(CPUOpenRISCState *env) { - - qemu_log("do_sigreturn: not implemented\n"); + trace_user_do_sigreturn(env, 0); + fprintf(stderr, "do_sigreturn: not implemented\n"); return -TARGET_ENOSYS; } long do_rt_sigreturn(CPUOpenRISCState *env) { - qemu_log("do_rt_sigreturn: not implemented\n"); + trace_user_do_rt_sigreturn(env, 0); + fprintf(stderr, "do_rt_sigreturn: not implemented\n"); return -TARGET_ENOSYS; } /* TARGET_OPENRISC */ @@ -4109,13 +4098,11 @@ static void setup_frame(int sig, struct target_sigaction *ka, abi_ulong frame_addr; frame_addr = get_sigframe(ka, env, sizeof(*frame)); - qemu_log("%s: frame_addr 0x%llx\n", __FUNCTION__, - (unsigned long long)frame_addr); + trace_user_setup_frame(env, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { goto give_sigsegv; } - qemu_log("%s: 1\n", __FUNCTION__); __put_user(set->sig[0], &frame->sc.oldmask[0]); save_sigregs(env, &frame->sregs); @@ -4156,7 +4143,6 @@ static void setup_frame(int sig, struct target_sigaction *ka, return; give_sigsegv: - qemu_log("%s: give_sigsegv\n", __FUNCTION__); force_sig(TARGET_SIGSEGV); } @@ -4169,13 +4155,11 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, abi_ulong frame_addr; frame_addr = get_sigframe(ka, env, sizeof *frame); - qemu_log("%s: frame_addr 0x%llx\n", __FUNCTION__, - (unsigned long long)frame_addr); + trace_user_setup_rt_frame(env, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { goto give_sigsegv; } - qemu_log("%s: 1\n", __FUNCTION__); tswap_siginfo(&frame->info, info); /* Create the ucontext. */ @@ -4214,7 +4198,6 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, return; give_sigsegv: - qemu_log("%s: give_sigsegv\n", __FUNCTION__); force_sig(TARGET_SIGSEGV); } @@ -4229,9 +4212,8 @@ restore_sigregs(CPUS390XState *env, target_sigregs *sc) } __get_user(env->psw.mask, &sc->regs.psw.mask); - qemu_log("%s: sc->regs.psw.addr 0x%llx env->psw.addr 0x%llx\n", - __FUNCTION__, (unsigned long long)sc->regs.psw.addr, - (unsigned long long)env->psw.addr); + trace_user_s390x_restore_sigregs(env, (unsigned long long)sc->regs.psw.addr, + (unsigned long long)env->psw.addr); __get_user(env->psw.addr, &sc->regs.psw.addr); /* FIXME: 31-bit -> | PSW_ADDR_AMODE */ @@ -4249,11 +4231,10 @@ long do_sigreturn(CPUS390XState *env) { sigframe *frame; abi_ulong frame_addr = env->regs[15]; - qemu_log("%s: frame_addr 0x%llx\n", __FUNCTION__, - (unsigned long long)frame_addr); target_sigset_t target_set; sigset_t set; + trace_user_do_sigreturn(env, frame_addr); if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { goto badframe; } @@ -4278,10 +4259,9 @@ long do_rt_sigreturn(CPUS390XState *env) { rt_sigframe *frame; abi_ulong frame_addr = env->regs[15]; - qemu_log("%s: frame_addr 0x%llx\n", __FUNCTION__, - (unsigned long long)frame_addr); sigset_t set; + trace_user_do_rt_sigreturn(env, frame_addr); if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { goto badframe; } @@ -4666,6 +4646,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, #endif frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_frame(env, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) goto sigsegv; sc = &frame->sctx; @@ -4730,7 +4711,6 @@ static void setup_frame(int sig, struct target_sigaction *ka, sigsegv: unlock_user_struct(frame, frame_addr, 1); - qemu_log("segfaulting from setup_frame\n"); force_sig(TARGET_SIGSEGV); } @@ -4826,7 +4806,6 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, sigsegv: unlock_user_struct(rt_sf, rt_sf_addr, 1); - qemu_log("segfaulting from setup_rt_frame\n"); force_sig(TARGET_SIGSEGV); } @@ -4864,7 +4843,6 @@ long do_sigreturn(CPUPPCState *env) sigsegv: unlock_user_struct(sr, sr_addr, 1); unlock_user_struct(sc, sc_addr, 1); - qemu_log("segfaulting from do_sigreturn\n"); force_sig(TARGET_SIGSEGV); return 0; } @@ -4920,7 +4898,6 @@ long do_rt_sigreturn(CPUPPCState *env) sigsegv: unlock_user_struct(rt_sf, rt_sf_addr, 1); - qemu_log("segfaulting from do_rt_sigreturn\n"); force_sig(TARGET_SIGSEGV); return 0; } @@ -5044,6 +5021,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, int i; frame_addr = get_sigframe(ka, env, sizeof *frame); + trace_user_setup_frame(env, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) goto give_sigsegv; @@ -5160,6 +5138,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, 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; @@ -5227,6 +5206,7 @@ long do_sigreturn(CPUM68KState *env) sigset_t set; int d0, i; + trace_user_do_sigreturn(env, frame_addr); if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) goto badframe; @@ -5261,6 +5241,7 @@ long do_rt_sigreturn(CPUM68KState *env) sigset_t set; int d0; + trace_user_do_rt_sigreturn(env, frame_addr); if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) goto badframe; @@ -5400,6 +5381,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, int err = 0; frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_frame(env, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { goto give_sigsegv; } @@ -5444,6 +5426,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, int i, err = 0; 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; } @@ -5522,6 +5505,7 @@ long do_rt_sigreturn(CPUAlphaState *env) 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; } @@ -5544,6 +5528,165 @@ long do_rt_sigreturn(CPUAlphaState *env) force_sig(TARGET_SIGSEGV); } +#elif defined(TARGET_TILEGX) + +struct target_sigcontext { + union { + /* General-purpose registers. */ + abi_ulong gregs[56]; + struct { + abi_ulong __gregs[53]; + abi_ulong tp; /* Aliases gregs[TREG_TP]. */ + abi_ulong sp; /* Aliases gregs[TREG_SP]. */ + abi_ulong lr; /* Aliases gregs[TREG_LR]. */ + }; + }; + abi_ulong pc; /* Program counter. */ + abi_ulong ics; /* In Interrupt Critical Section? */ + abi_ulong faultnum; /* Fault number. */ + abi_ulong pad[5]; +}; + +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; /* mask last for extensibility */ +}; + +struct target_rt_sigframe { + unsigned char save_area[16]; /* caller save area */ + struct target_siginfo info; + struct target_ucontext uc; +}; + +static void setup_sigcontext(struct target_sigcontext *sc, + CPUArchState *env, int signo) +{ + int i; + + for (i = 0; i < TILEGX_R_COUNT; ++i) { + __put_user(env->regs[i], &sc->gregs[i]); + } + + __put_user(env->pc, &sc->pc); + __put_user(0, &sc->ics); + __put_user(signo, &sc->faultnum); +} + +static void restore_sigcontext(CPUTLGState *env, struct target_sigcontext *sc) +{ + int i; + + for (i = 0; i < TILEGX_R_COUNT; ++i) { + __get_user(env->regs[i], &sc->gregs[i]); + } + + __get_user(env->pc, &sc->pc); +} + +static abi_ulong get_sigframe(struct target_sigaction *ka, CPUArchState *env, + size_t frame_size) +{ + unsigned long sp = env->regs[TILEGX_R_SP]; + + if (on_sig_stack(sp) && !likely(on_sig_stack(sp - frame_size))) { + return -1UL; + } + + if ((ka->sa_flags & SA_ONSTACK) && !sas_ss_flags(sp)) { + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + + sp -= frame_size; + sp &= -16UL; + return sp; +} + +static void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUArchState *env) +{ + abi_ulong frame_addr; + struct target_rt_sigframe *frame; + unsigned long restorer; + + 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; + } + + /* Always write at least the signal number for the stack backtracer. */ + if (ka->sa_flags & TARGET_SA_SIGINFO) { + /* At sigreturn time, restore the callee-save registers too. */ + tswap_siginfo(&frame->info, info); + /* regs->flags |= PT_FLAGS_RESTORE_REGS; FIXME: we can skip it? */ + } else { + __put_user(info->si_signo, &frame->info.si_signo); + } + + /* Create the ucontext. */ + __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[TILEGX_R_SP]), + &frame->uc.tuc_stack.ss_flags); + __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); + setup_sigcontext(&frame->uc.tuc_mcontext, env, info->si_signo); + + restorer = (unsigned long) do_rt_sigreturn; + if (ka->sa_flags & TARGET_SA_RESTORER) { + restorer = (unsigned long) ka->sa_restorer; + } + env->pc = (unsigned long) ka->_sa_handler; + env->regs[TILEGX_R_SP] = (unsigned long) frame; + env->regs[TILEGX_R_LR] = restorer; + env->regs[0] = (unsigned long) sig; + env->regs[1] = (unsigned long) &frame->info; + env->regs[2] = (unsigned long) &frame->uc; + /* regs->flags |= PT_FLAGS_CALLER_SAVES; FIXME: we can skip it? */ + + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + if (sig == TARGET_SIGSEGV) { + ka->_sa_handler = TARGET_SIG_DFL; + } + force_sig(TARGET_SIGSEGV /* , current */); +} + +long do_rt_sigreturn(CPUTLGState *env) +{ + abi_ulong frame_addr = env->regs[TILEGX_R_SP]; + 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); + do_sigprocmask(SIG_SETMASK, &set, NULL); + + restore_sigcontext(env, &frame->uc.tuc_mcontext); + if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, + uc.tuc_stack), + 0, env->regs[TILEGX_R_SP]) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return env->regs[TILEGX_R_RE]; + + + badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); +} + #else static void setup_frame(int sig, struct target_sigaction *ka, @@ -5600,9 +5743,7 @@ void process_pending_signals(CPUArchState *cpu_env) return; handle_signal: -#ifdef DEBUG_SIGNAL - fprintf(stderr, "qemu: process signal %d\n", sig); -#endif + trace_user_handle_signal(cpu_env, sig); /* dequeue signal */ q = k->first; k->first = q->next; @@ -5663,7 +5804,8 @@ void process_pending_signals(CPUArchState *cpu_env) } #endif /* prepare the stack frame of the virtual CPU */ -#if defined(TARGET_ABI_MIPSN32) || defined(TARGET_ABI_MIPSN64) +#if defined(TARGET_ABI_MIPSN32) || defined(TARGET_ABI_MIPSN64) \ + || defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) /* These targets do not have traditional signals. */ setup_rt_frame(sig, sa, &q->info, &target_old_set, cpu_env); #else diff --git a/qemu/linux-user/sparc/syscall.h b/qemu/linux-user/sparc/syscall.h deleted file mode 100644 index 58573b92e..000000000 --- a/qemu/linux-user/sparc/syscall.h +++ /dev/null @@ -1,20 +0,0 @@ -struct target_pt_regs { - abi_ulong psr; - abi_ulong pc; - abi_ulong npc; - abi_ulong y; - abi_ulong u_regs[16]; -}; - -#define UNAME_MACHINE "sun4" -#define UNAME_MINIMUM_RELEASE "2.6.32" - -/* SPARC kernels don't define this in their Kconfig, but they have the - * same ABI as if they did, implemented by sparc-specific code which fishes - * directly in the u_regs() struct for half the parameters in sparc_do_fork() - * and copy_thread(). - */ -#define TARGET_CLONE_BACKWARDS -#define TARGET_MINSIGSTKSZ 4096 -#define TARGET_MLOCKALL_MCL_CURRENT 0x2000 -#define TARGET_MLOCKALL_MCL_FUTURE 0x4000 diff --git a/qemu/linux-user/sparc/syscall_nr.h b/qemu/linux-user/sparc/syscall_nr.h index 181cd3265..732b1052a 100644 --- a/qemu/linux-user/sparc/syscall_nr.h +++ b/qemu/linux-user/sparc/syscall_nr.h @@ -278,7 +278,7 @@ #define TARGET_NR_epoll_pwait 309 #define TARGET_NR_utimensat 310 #define TARGET_NR_signalfd 311 -#define TARGET_NR_timerfd 312 +#define TARGET_NR_timerfd_create 312 #define TARGET_NR_eventfd 313 #define TARGET_NR_fallocate 314 #define TARGET_NR_timerfd_settime 315 @@ -309,3 +309,17 @@ #define TARGET_NR_kern_features 340 #define TARGET_NR_kcmp 341 #define TARGET_NR_finit_module 342 +#define TARGET_NR_sched_setattr 343 +#define TARGET_NR_sched_getattr 344 +#define TARGET_NR_renameat2 345 +#define TARGET_NR_seccomp 346 +#define TARGET_NR_getrandom 347 +#define TARGET_NR_memfd_create 348 +#define TARGET_NR_bpf 349 +#define TARGET_NR_execveat 350 +#define TARGET_NR_membarrier 351 +#define TARGET_NR_userfaultfd 352 +#define TARGET_NR_bind 353 +#define TARGET_NR_listen 354 +#define TARGET_NR_setsockopt 355 +#define TARGET_NR_mlock2 356 diff --git a/qemu/linux-user/sparc/target_syscall.h b/qemu/linux-user/sparc/target_syscall.h new file mode 100644 index 000000000..a73fa6dae --- /dev/null +++ b/qemu/linux-user/sparc/target_syscall.h @@ -0,0 +1,25 @@ +#ifndef TARGET_SYSCALL_H +#define TARGET_SYSCALL_H + +struct target_pt_regs { + abi_ulong psr; + abi_ulong pc; + abi_ulong npc; + abi_ulong y; + abi_ulong u_regs[16]; +}; + +#define UNAME_MACHINE "sun4" +#define UNAME_MINIMUM_RELEASE "2.6.32" + +/* SPARC kernels don't define this in their Kconfig, but they have the + * same ABI as if they did, implemented by sparc-specific code which fishes + * directly in the u_regs() struct for half the parameters in sparc_do_fork() + * and copy_thread(). + */ +#define TARGET_CLONE_BACKWARDS +#define TARGET_MINSIGSTKSZ 4096 +#define TARGET_MLOCKALL_MCL_CURRENT 0x2000 +#define TARGET_MLOCKALL_MCL_FUTURE 0x4000 + +#endif /* TARGET_SYSCALL_H */ diff --git a/qemu/linux-user/sparc64/syscall.h b/qemu/linux-user/sparc64/syscall.h deleted file mode 100644 index 8398d3f46..000000000 --- a/qemu/linux-user/sparc64/syscall.h +++ /dev/null @@ -1,21 +0,0 @@ -struct target_pt_regs { - abi_ulong u_regs[16]; - abi_ulong tstate; - abi_ulong pc; - abi_ulong npc; - abi_ulong y; - abi_ulong fprs; -}; - -#define UNAME_MACHINE "sun4u" -#define UNAME_MINIMUM_RELEASE "2.6.32" - -/* SPARC kernels don't define this in their Kconfig, but they have the - * same ABI as if they did, implemented by sparc-specific code which fishes - * directly in the u_regs() struct for half the parameters in sparc_do_fork() - * and copy_thread(). - */ -#define TARGET_CLONE_BACKWARDS -#define TARGET_MINSIGSTKSZ 4096 -#define TARGET_MLOCKALL_MCL_CURRENT 0x2000 -#define TARGET_MLOCKALL_MCL_FUTURE 0x4000 diff --git a/qemu/linux-user/sparc64/syscall_nr.h b/qemu/linux-user/sparc64/syscall_nr.h index 34a984cc8..2b49ead26 100644 --- a/qemu/linux-user/sparc64/syscall_nr.h +++ b/qemu/linux-user/sparc64/syscall_nr.h @@ -341,3 +341,17 @@ #define TARGET_NR_kern_features 340 #define TARGET_NR_kcmp 341 #define TARGET_NR_finit_module 342 +#define TARGET_NR_sched_setattr 343 +#define TARGET_NR_sched_getattr 344 +#define TARGET_NR_renameat2 345 +#define TARGET_NR_seccomp 346 +#define TARGET_NR_getrandom 347 +#define TARGET_NR_memfd_create 348 +#define TARGET_NR_bpf 349 +#define TARGET_NR_execveat 350 +#define TARGET_NR_membarrier 351 +#define TARGET_NR_userfaultfd 352 +#define TARGET_NR_bind 353 +#define TARGET_NR_listen 354 +#define TARGET_NR_setsockopt 355 +#define TARGET_NR_mlock2 356 diff --git a/qemu/linux-user/sparc64/target_syscall.h b/qemu/linux-user/sparc64/target_syscall.h new file mode 100644 index 000000000..eb827fcac --- /dev/null +++ b/qemu/linux-user/sparc64/target_syscall.h @@ -0,0 +1,26 @@ +#ifndef TARGET_SYSCALL_H +#define TARGET_SYSCALL_H + +struct target_pt_regs { + abi_ulong u_regs[16]; + abi_ulong tstate; + abi_ulong pc; + abi_ulong npc; + abi_ulong y; + abi_ulong fprs; +}; + +#define UNAME_MACHINE "sun4u" +#define UNAME_MINIMUM_RELEASE "2.6.32" + +/* SPARC kernels don't define this in their Kconfig, but they have the + * same ABI as if they did, implemented by sparc-specific code which fishes + * directly in the u_regs() struct for half the parameters in sparc_do_fork() + * and copy_thread(). + */ +#define TARGET_CLONE_BACKWARDS +#define TARGET_MINSIGSTKSZ 4096 +#define TARGET_MLOCKALL_MCL_CURRENT 0x2000 +#define TARGET_MLOCKALL_MCL_FUTURE 0x4000 + +#endif /* TARGET_SYSCALL_H */ diff --git a/qemu/linux-user/strace.c b/qemu/linux-user/strace.c index ea6c1d24e..0810c85fb 100644 --- a/qemu/linux-user/strace.c +++ b/qemu/linux-user/strace.c @@ -1,13 +1,11 @@ -#include +#include "qemu/osdep.h" #include #include #include #include #include -#include #include #include -#include #include #include "qemu.h" diff --git a/qemu/linux-user/syscall.c b/qemu/linux-user/syscall.c index f62c69894..032d33886 100644 --- a/qemu/linux-user/syscall.c +++ b/qemu/linux-user/syscall.c @@ -17,24 +17,15 @@ * along with this program; if not, see . */ #define _ATFILE_SOURCE -#include -#include -#include -#include +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "qemu/path.h" #include #include -#include -#include -#include -#include -#include #include -#include #include #include #include -#include -#include #include #include #include @@ -44,7 +35,6 @@ #include #include #include -#include #include #ifdef __ia64__ int __clone2(int (*fn)(void *), void *child_stack_base, @@ -60,6 +50,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base, #include #include #include +#include //#include #include #include @@ -213,7 +204,7 @@ static int gettid(void) { return -ENOSYS; } #endif -#ifdef __NR_getdents +#if defined(TARGET_NR_getdents) && defined(__NR_getdents) _syscall3(int, sys_getdents, uint, fd, struct linux_dirent *, dirp, uint, count); #endif #if !defined(__NR_getdents) || \ @@ -260,6 +251,9 @@ _syscall2(int, ioprio_get, int, which, int, who) #if defined(TARGET_NR_ioprio_set) && defined(__NR_ioprio_set) _syscall3(int, ioprio_set, int, which, int, who, int, ioprio) #endif +#if defined(TARGET_NR_getrandom) && defined(__NR_getrandom) +_syscall3(int, getrandom, void *, buf, size_t, buflen, unsigned int, flags) +#endif static bitmask_transtbl fcntl_flags_tbl[] = { { TARGET_O_ACCMODE, TARGET_O_WRONLY, O_ACCMODE, O_WRONLY, }, @@ -294,6 +288,64 @@ static bitmask_transtbl fcntl_flags_tbl[] = { { 0, 0, 0, 0 } }; +typedef abi_long (*TargetFdDataFunc)(void *, size_t); +typedef abi_long (*TargetFdAddrFunc)(void *, abi_ulong, socklen_t); +typedef struct TargetFdTrans { + TargetFdDataFunc host_to_target_data; + TargetFdDataFunc target_to_host_data; + TargetFdAddrFunc target_to_host_addr; +} TargetFdTrans; + +static TargetFdTrans **target_fd_trans; + +static unsigned int target_fd_max; + +static TargetFdDataFunc fd_trans_host_to_target_data(int fd) +{ + if (fd >= 0 && fd < target_fd_max && target_fd_trans[fd]) { + return target_fd_trans[fd]->host_to_target_data; + } + return NULL; +} + +static TargetFdAddrFunc fd_trans_target_to_host_addr(int fd) +{ + if (fd >= 0 && fd < target_fd_max && target_fd_trans[fd]) { + return target_fd_trans[fd]->target_to_host_addr; + } + return NULL; +} + +static void fd_trans_register(int fd, TargetFdTrans *trans) +{ + unsigned int oldmax; + + if (fd >= target_fd_max) { + oldmax = target_fd_max; + target_fd_max = ((fd >> 6) + 1) << 6; /* by slice of 64 entries */ + target_fd_trans = g_renew(TargetFdTrans *, + target_fd_trans, target_fd_max); + memset((void *)(target_fd_trans + oldmax), 0, + (target_fd_max - oldmax) * sizeof(TargetFdTrans *)); + } + target_fd_trans[fd] = trans; +} + +static void fd_trans_unregister(int fd) +{ + if (fd >= 0 && fd < target_fd_max) { + target_fd_trans[fd] = NULL; + } +} + +static void fd_trans_dup(int oldfd, int newfd) +{ + fd_trans_unregister(newfd); + if (oldfd < target_fd_max && target_fd_trans[oldfd]) { + fd_trans_register(newfd, target_fd_trans[oldfd]); + } +} + static int sys_getcwd1(char *buf, size_t size) { if (getcwd(buf, size) == NULL) { @@ -457,6 +509,7 @@ static uint16_t target_to_host_errno_table[ERRNO_TABLE_SIZE] = { * minus the errnos that are not actually generic to all archs. */ static uint16_t host_to_target_errno_table[ERRNO_TABLE_SIZE] = { + [EAGAIN] = TARGET_EAGAIN, [EIDRM] = TARGET_EIDRM, [ECHRNG] = TARGET_ECHRNG, [EL2NSYNC] = TARGET_EL2NSYNC, @@ -1112,7 +1165,7 @@ static inline abi_long target_to_host_ip_mreq(struct ip_mreqn *mreqn, return 0; } -static inline abi_long target_to_host_sockaddr(struct sockaddr *addr, +static inline abi_long target_to_host_sockaddr(int fd, struct sockaddr *addr, abi_ulong target_addr, socklen_t len) { @@ -1120,6 +1173,10 @@ static inline abi_long target_to_host_sockaddr(struct sockaddr *addr, sa_family_t sa_family; struct target_sockaddr *target_saddr; + if (fd_trans_target_to_host_addr(fd)) { + return fd_trans_target_to_host_addr(fd)(addr, target_addr, len); + } + target_saddr = lock_user(VERIFY_READ, target_addr, len, 1); if (!target_saddr) return -TARGET_EFAULT; @@ -1181,7 +1238,7 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh); abi_long msg_controllen; abi_ulong target_cmsg_addr; - struct target_cmsghdr *target_cmsg; + struct target_cmsghdr *target_cmsg, *target_cmsg_start; socklen_t space = 0; msg_controllen = tswapal(target_msgh->msg_controllen); @@ -1189,6 +1246,7 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, goto the_end; target_cmsg_addr = tswapal(target_msgh->msg_control); target_cmsg = lock_user(VERIFY_READ, target_cmsg_addr, msg_controllen, 1); + target_cmsg_start = target_cmsg; if (!target_cmsg) return -TARGET_EFAULT; @@ -1247,7 +1305,8 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, } cmsg = CMSG_NXTHDR(msgh, cmsg); - target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg); + target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg, + target_cmsg_start); } unlock_user(target_cmsg, target_cmsg_addr, 0); the_end: @@ -1261,7 +1320,7 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh); abi_long msg_controllen; abi_ulong target_cmsg_addr; - struct target_cmsghdr *target_cmsg; + struct target_cmsghdr *target_cmsg, *target_cmsg_start; socklen_t space = 0; msg_controllen = tswapal(target_msgh->msg_controllen); @@ -1269,6 +1328,7 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, goto the_end; target_cmsg_addr = tswapal(target_msgh->msg_control); target_cmsg = lock_user(VERIFY_WRITE, target_cmsg_addr, msg_controllen, 0); + target_cmsg_start = target_cmsg; if (!target_cmsg) return -TARGET_EFAULT; @@ -1382,14 +1442,15 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, } target_cmsg->cmsg_len = tswapal(tgt_len); - tgt_space = TARGET_CMSG_SPACE(tgt_len); + tgt_space = TARGET_CMSG_SPACE(len); if (msg_controllen < tgt_space) { tgt_space = msg_controllen; } msg_controllen -= tgt_space; space += tgt_space; cmsg = CMSG_NXTHDR(msgh, cmsg); - target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg); + target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg, + target_cmsg_start); } unlock_user(target_cmsg, target_cmsg_addr, space); the_end: @@ -1554,7 +1615,7 @@ set_timeout: } fprog.len = tswap16(tfprog->len); - filter = malloc(fprog.len * sizeof(*filter)); + filter = g_try_new(struct sock_filter, fprog.len); if (filter == NULL) { unlock_user_struct(tfilter, tfprog->filter, 1); unlock_user_struct(tfprog, optval_addr, 1); @@ -1570,7 +1631,7 @@ set_timeout: ret = get_errno(setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog))); - free(filter); + g_free(filter); unlock_user_struct(tfilter, tfprog->filter, 1); unlock_user_struct(tfprog, optval_addr, 1); @@ -1591,7 +1652,8 @@ set_timeout: addr_ifname = alloca(IFNAMSIZ); memcpy(addr_ifname, dev_ifname, optlen); addr_ifname[optlen] = 0; - ret = get_errno(setsockopt(sockfd, level, optname, addr_ifname, optlen)); + ret = get_errno(setsockopt(sockfd, SOL_SOCKET, optname, + addr_ifname, optlen)); unlock_user (dev_ifname, optval_addr, 0); return ret; } @@ -1881,7 +1943,7 @@ static struct iovec *lock_iovec(int type, abi_ulong target_addr, return NULL; } - vec = calloc(count, sizeof(struct iovec)); + vec = g_try_new0(struct iovec, count); if (vec == NULL) { errno = ENOMEM; return NULL; @@ -1945,7 +2007,7 @@ static struct iovec *lock_iovec(int type, abi_ulong target_addr, } unlock_user(target_vec, target_addr, 0); fail2: - free(vec); + g_free(vec); errno = err; return NULL; } @@ -1970,7 +2032,7 @@ static void unlock_iovec(struct iovec *vec, abi_ulong target_addr, unlock_user(target_vec, target_addr, 0); } - free(vec); + g_free(vec); } static inline int target_to_host_sock_type(int *type) @@ -2022,6 +2084,30 @@ static int sock_flags_fixup(int fd, int target_type) return fd; } +static abi_long packet_target_to_host_sockaddr(void *host_addr, + abi_ulong target_addr, + socklen_t len) +{ + struct sockaddr *addr = host_addr; + struct target_sockaddr *target_saddr; + + target_saddr = lock_user(VERIFY_READ, target_addr, len, 1); + if (!target_saddr) { + return -TARGET_EFAULT; + } + + memcpy(addr, target_saddr, len); + addr->sa_family = tswap16(target_saddr->sa_family); + /* spkt_protocol is big-endian */ + + unlock_user(target_saddr, target_addr, 0); + return 0; +} + +static TargetFdTrans target_packet_trans = { + .target_to_host_addr = packet_target_to_host_sockaddr, +}; + /* do_socket() Must return target values and target errnos. */ static abi_long do_socket(int domain, int type, int protocol) { @@ -2035,9 +2121,21 @@ static abi_long do_socket(int domain, int type, int protocol) if (domain == PF_NETLINK) return -TARGET_EAFNOSUPPORT; + + if (domain == AF_PACKET || + (domain == AF_INET && type == SOCK_PACKET)) { + protocol = tswap16(protocol); + } + ret = get_errno(socket(domain, type, protocol)); if (ret >= 0) { ret = sock_flags_fixup(ret, target_type); + if (type == SOCK_PACKET) { + /* Manage an obsolete case : + * if socket type is SOCK_PACKET, bind by name + */ + fd_trans_register(ret, &target_packet_trans); + } } return ret; } @@ -2055,7 +2153,7 @@ static abi_long do_bind(int sockfd, abi_ulong target_addr, addr = alloca(addrlen+1); - ret = target_to_host_sockaddr(addr, target_addr, addrlen); + ret = target_to_host_sockaddr(sockfd, addr, target_addr, addrlen); if (ret) return ret; @@ -2075,7 +2173,7 @@ static abi_long do_connect(int sockfd, abi_ulong target_addr, addr = alloca(addrlen+1); - ret = target_to_host_sockaddr(addr, target_addr, addrlen); + ret = target_to_host_sockaddr(sockfd, addr, target_addr, addrlen); if (ret) return ret; @@ -2095,8 +2193,9 @@ static abi_long do_sendrecvmsg_locked(int fd, struct target_msghdr *msgp, if (msgp->msg_name) { msg.msg_namelen = tswap32(msgp->msg_namelen); msg.msg_name = alloca(msg.msg_namelen+1); - ret = target_to_host_sockaddr(msg.msg_name, tswapal(msgp->msg_name), - msg.msg_namelen); + ret = target_to_host_sockaddr(fd, msg.msg_name, + tswapal(msgp->msg_name), + msg.msg_namelen); if (ret) { goto out2; } @@ -2166,7 +2265,6 @@ static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg, return ret; } -#ifdef TARGET_NR_sendmmsg /* We don't rely on the C library to have sendmmsg/recvmmsg support, * so it might not have this *mmsg-specific flag either. */ @@ -2213,7 +2311,6 @@ static abi_long do_sendrecvmmsg(int fd, abi_ulong target_msgvec, } return ret; } -#endif /* If we don't have a system accept4() then just call accept. * The callsites to do_accept4() will ensure that they don't @@ -2358,7 +2455,7 @@ static abi_long do_sendto(int fd, abi_ulong msg, size_t len, int flags, return -TARGET_EFAULT; if (target_addr) { addr = alloca(addrlen+1); - ret = target_to_host_sockaddr(addr, target_addr, addrlen); + ret = target_to_host_sockaddr(fd, addr, target_addr, addrlen); if (ret) { unlock_user(host_msg, msg, 0); return ret; @@ -2436,6 +2533,8 @@ static abi_long do_socketcall(int num, abi_ulong vptr) [SOCKOP_shutdown] = 2, /* sockfd, how */ [SOCKOP_sendmsg] = 3, /* sockfd, msg, flags */ [SOCKOP_recvmsg] = 3, /* sockfd, msg, flags */ + [SOCKOP_sendmmsg] = 4, /* sockfd, msgvec, vlen, flags */ + [SOCKOP_recvmmsg] = 4, /* sockfd, msgvec, vlen, flags */ [SOCKOP_setsockopt] = 5, /* sockfd, level, optname, optval, optlen */ [SOCKOP_getsockopt] = 5, /* sockfd, level, optname, optval, optlen */ }; @@ -2486,6 +2585,10 @@ static abi_long do_socketcall(int num, abi_ulong vptr) return do_sendrecvmsg(a[0], a[1], a[2], 1); case SOCKOP_recvmsg: /* sockfd, msg, flags */ return do_sendrecvmsg(a[0], a[1], a[2], 0); + case SOCKOP_sendmmsg: /* sockfd, msgvec, vlen, flags */ + return do_sendrecvmmsg(a[0], a[1], a[2], a[3], 1); + case SOCKOP_recvmmsg: /* sockfd, msgvec, vlen, flags */ + return do_sendrecvmmsg(a[0], a[1], a[2], a[3], 0); case SOCKOP_setsockopt: /* sockfd, level, optname, optval, optlen */ return do_setsockopt(a[0], a[1], a[2], a[3], a[4]); case SOCKOP_getsockopt: /* sockfd, level, optname, optval, optlen */ @@ -2500,8 +2603,9 @@ static abi_long do_socketcall(int num, abi_ulong vptr) #define N_SHM_REGIONS 32 static struct shm_region { - abi_ulong start; - abi_ulong size; + abi_ulong start; + abi_ulong size; + bool in_use; } shm_regions[N_SHM_REGIONS]; struct target_semid_ds @@ -2672,14 +2776,14 @@ static inline abi_long target_to_host_semarray(int semid, unsigned short **host_ nsems = semid_ds.sem_nsems; - *host_array = malloc(nsems*sizeof(unsigned short)); + *host_array = g_try_new(unsigned short, nsems); if (!*host_array) { return -TARGET_ENOMEM; } array = lock_user(VERIFY_READ, target_addr, nsems*sizeof(unsigned short), 1); if (!array) { - free(*host_array); + g_free(*host_array); return -TARGET_EFAULT; } @@ -2716,15 +2820,16 @@ static inline abi_long host_to_target_semarray(int semid, abi_ulong target_addr, for(i=0; imtype = (abi_long) tswapal(target_mb->mtype); memcpy(host_mb->mtext, target_mb->mtext, msgsz); ret = get_errno(msgsnd(msqid, host_mb, msgsz, msgflg)); - free(host_mb); + g_free(host_mb); unlock_user_struct(target_mb, msgp, 0); return ret; @@ -3192,7 +3297,8 @@ static inline abi_ulong do_shmat(int shmid, abi_ulong shmaddr, int shmflg) ((shmflg & SHM_RDONLY)? 0 : PAGE_WRITE)); for (i = 0; i < N_SHM_REGIONS; i++) { - if (shm_regions[i].start == 0) { + if (!shm_regions[i].in_use) { + shm_regions[i].in_use = true; shm_regions[i].start = raddr; shm_regions[i].size = shm_info.shm_segsz; break; @@ -3209,8 +3315,8 @@ static inline abi_long do_shmdt(abi_ulong shmaddr) int i; for (i = 0; i < N_SHM_REGIONS; ++i) { - if (shm_regions[i].start == shmaddr) { - shm_regions[i].start = 0; + if (shm_regions[i].in_use && shm_regions[i].start == shmaddr) { + shm_regions[i].in_use = false; page_set_flags(shmaddr, shmaddr + shm_regions[i].size, 0); break; } @@ -3246,8 +3352,7 @@ static abi_long do_ipc(unsigned int call, abi_long first, * ptr argument. */ abi_ulong atptr; get_user_ual(atptr, ptr); - ret = do_semctl(first, second, third, - (union target_semun) atptr); + ret = do_semctl(first, second, third, atptr); break; } @@ -3411,7 +3516,7 @@ static abi_long do_ioctl_fs_ioc_fiemap(const IOCTLEntry *ie, uint8_t *buf_temp, /* We can't fit all the extents into the fixed size buffer. * Allocate one that is large enough and use it instead. */ - fm = malloc(outbufsz); + fm = g_try_malloc(outbufsz); if (!fm) { return -TARGET_ENOMEM; } @@ -3446,7 +3551,7 @@ static abi_long do_ioctl_fs_ioc_fiemap(const IOCTLEntry *ie, uint8_t *buf_temp, } } if (free_fm) { - free(fm); + g_free(fm); } return ret; } @@ -3939,7 +4044,6 @@ static abi_long do_ioctl(int fd, int cmd, abi_long arg) break; case TYPE_PTRVOID: case TYPE_INT: - /* int argment */ ret = get_errno(ioctl(fd, ie->host_cmd, arg)); break; case TYPE_PTR: @@ -4513,6 +4617,7 @@ static void *clone_func(void *arg) CPUState *cpu; TaskState *ts; + rcu_register_thread(); env = info->env; cpu = ENV_GET_CPU(env); thread_cpu = cpu; @@ -4561,7 +4666,7 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, new_thread_info info; pthread_attr_t attr; - ts = g_malloc0(sizeof(TaskState)); + ts = g_new0(TaskState, 1); init_task_state(ts); /* we create a new CPU instance. */ new_env = cpu_copy(env); @@ -4622,8 +4727,9 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, pthread_mutex_unlock(&clone_lock); } else { /* if no CLONE_VM, we consider it is a fork */ - if ((flags & ~(CSIGNAL | CLONE_NPTL_FLAGS2)) != 0) - return -EINVAL; + if ((flags & ~(CSIGNAL | CLONE_NPTL_FLAGS2)) != 0) { + return -TARGET_EINVAL; + } fork_start(); ret = fork(); if (ret == 0) { @@ -5130,7 +5236,6 @@ static inline int target_to_host_mlockall_arg(int arg) } #endif -#if defined(TARGET_NR_stat64) || defined(TARGET_NR_newfstatat) static inline abi_long host_to_target_stat64(void *cpu_env, abi_ulong target_addr, struct stat *host_st) @@ -5193,7 +5298,6 @@ static inline abi_long host_to_target_stat64(void *cpu_env, return 0; } -#endif /* ??? Using host futex calls even when target atomic operations are not really atomic probably breaks things. However implementing @@ -5246,6 +5350,179 @@ static int do_futex(target_ulong uaddr, int op, int val, target_ulong timeout, return -TARGET_ENOSYS; } } +#if defined(TARGET_NR_name_to_handle_at) && defined(CONFIG_OPEN_BY_HANDLE) +static abi_long do_name_to_handle_at(abi_long dirfd, abi_long pathname, + abi_long handle, abi_long mount_id, + abi_long flags) +{ + struct file_handle *target_fh; + struct file_handle *fh; + int mid = 0; + abi_long ret; + char *name; + unsigned int size, total_size; + + if (get_user_s32(size, handle)) { + return -TARGET_EFAULT; + } + + name = lock_user_string(pathname); + if (!name) { + return -TARGET_EFAULT; + } + + total_size = sizeof(struct file_handle) + size; + target_fh = lock_user(VERIFY_WRITE, handle, total_size, 0); + if (!target_fh) { + unlock_user(name, pathname, 0); + return -TARGET_EFAULT; + } + + fh = g_malloc0(total_size); + fh->handle_bytes = size; + + ret = get_errno(name_to_handle_at(dirfd, path(name), fh, &mid, flags)); + unlock_user(name, pathname, 0); + + /* man name_to_handle_at(2): + * Other than the use of the handle_bytes field, the caller should treat + * the file_handle structure as an opaque data type + */ + + memcpy(target_fh, fh, total_size); + target_fh->handle_bytes = tswap32(fh->handle_bytes); + target_fh->handle_type = tswap32(fh->handle_type); + g_free(fh); + unlock_user(target_fh, handle, total_size); + + if (put_user_s32(mid, mount_id)) { + return -TARGET_EFAULT; + } + + return ret; + +} +#endif + +#if defined(TARGET_NR_open_by_handle_at) && defined(CONFIG_OPEN_BY_HANDLE) +static abi_long do_open_by_handle_at(abi_long mount_fd, abi_long handle, + abi_long flags) +{ + struct file_handle *target_fh; + struct file_handle *fh; + unsigned int size, total_size; + abi_long ret; + + if (get_user_s32(size, handle)) { + return -TARGET_EFAULT; + } + + total_size = sizeof(struct file_handle) + size; + target_fh = lock_user(VERIFY_READ, handle, total_size, 1); + if (!target_fh) { + return -TARGET_EFAULT; + } + + fh = g_memdup(target_fh, total_size); + fh->handle_bytes = size; + fh->handle_type = tswap32(target_fh->handle_type); + + ret = get_errno(open_by_handle_at(mount_fd, fh, + target_to_host_bitmask(flags, fcntl_flags_tbl))); + + g_free(fh); + + unlock_user(target_fh, handle, total_size); + + return ret; +} +#endif + +#if defined(TARGET_NR_signalfd) || defined(TARGET_NR_signalfd4) + +/* signalfd siginfo conversion */ + +static void +host_to_target_signalfd_siginfo(struct signalfd_siginfo *tinfo, + const struct signalfd_siginfo *info) +{ + int sig = host_to_target_signal(info->ssi_signo); + + /* linux/signalfd.h defines a ssi_addr_lsb + * not defined in sys/signalfd.h but used by some kernels + */ + +#ifdef BUS_MCEERR_AO + if (tinfo->ssi_signo == SIGBUS && + (tinfo->ssi_code == BUS_MCEERR_AR || + tinfo->ssi_code == BUS_MCEERR_AO)) { + uint16_t *ssi_addr_lsb = (uint16_t *)(&info->ssi_addr + 1); + uint16_t *tssi_addr_lsb = (uint16_t *)(&tinfo->ssi_addr + 1); + *tssi_addr_lsb = tswap16(*ssi_addr_lsb); + } +#endif + + tinfo->ssi_signo = tswap32(sig); + tinfo->ssi_errno = tswap32(tinfo->ssi_errno); + tinfo->ssi_code = tswap32(info->ssi_code); + tinfo->ssi_pid = tswap32(info->ssi_pid); + tinfo->ssi_uid = tswap32(info->ssi_uid); + tinfo->ssi_fd = tswap32(info->ssi_fd); + tinfo->ssi_tid = tswap32(info->ssi_tid); + tinfo->ssi_band = tswap32(info->ssi_band); + tinfo->ssi_overrun = tswap32(info->ssi_overrun); + tinfo->ssi_trapno = tswap32(info->ssi_trapno); + tinfo->ssi_status = tswap32(info->ssi_status); + tinfo->ssi_int = tswap32(info->ssi_int); + tinfo->ssi_ptr = tswap64(info->ssi_ptr); + tinfo->ssi_utime = tswap64(info->ssi_utime); + tinfo->ssi_stime = tswap64(info->ssi_stime); + tinfo->ssi_addr = tswap64(info->ssi_addr); +} + +static abi_long host_to_target_data_signalfd(void *buf, size_t len) +{ + int i; + + for (i = 0; i < len; i += sizeof(struct signalfd_siginfo)) { + host_to_target_signalfd_siginfo(buf + i, buf + i); + } + + return len; +} + +static TargetFdTrans target_signalfd_trans = { + .host_to_target_data = host_to_target_data_signalfd, +}; + +static abi_long do_signalfd4(int fd, abi_long mask, int flags) +{ + int host_flags; + target_sigset_t *target_mask; + sigset_t host_mask; + abi_long ret; + + if (flags & ~(TARGET_O_NONBLOCK | TARGET_O_CLOEXEC)) { + return -TARGET_EINVAL; + } + if (!lock_user_struct(VERIFY_READ, target_mask, mask, 1)) { + return -TARGET_EFAULT; + } + + target_to_host_sigset(&host_mask, target_mask); + + host_flags = target_to_host_bitmask(flags, fcntl_flags_tbl); + + ret = get_errno(signalfd(fd, &host_mask, host_flags)); + if (ret >= 0) { + fd_trans_register(ret, &target_signalfd_trans); + } + + unlock_user_struct(target_mask, mask, 0); + + return ret; +} +#endif /* Map host to target signal numbers for the wait family of syscalls. Assume all other status bits are the same. */ @@ -5614,6 +5891,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, thread_cpu = NULL; object_unref(OBJECT(cpu)); g_free(ts); + rcu_unregister_thread(); pthread_exit(NULL); } #ifdef TARGET_GPROF @@ -5630,6 +5908,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) goto efault; ret = get_errno(read(arg1, p, arg3)); + if (ret >= 0 && + fd_trans_host_to_target_data(arg1)) { + ret = fd_trans_host_to_target_data(arg1)(p, ret); + } unlock_user(p, arg2, ret); } break; @@ -5639,31 +5921,49 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(write(arg1, p, arg3)); unlock_user(p, arg2, 0); break; +#ifdef TARGET_NR_open case TARGET_NR_open: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(do_openat(cpu_env, AT_FDCWD, p, target_to_host_bitmask(arg2, fcntl_flags_tbl), arg3)); + fd_trans_unregister(ret); unlock_user(p, arg1, 0); break; +#endif case TARGET_NR_openat: if (!(p = lock_user_string(arg2))) goto efault; ret = get_errno(do_openat(cpu_env, arg1, p, target_to_host_bitmask(arg3, fcntl_flags_tbl), arg4)); + fd_trans_unregister(ret); unlock_user(p, arg2, 0); break; +#if defined(TARGET_NR_name_to_handle_at) && defined(CONFIG_OPEN_BY_HANDLE) + case TARGET_NR_name_to_handle_at: + ret = do_name_to_handle_at(arg1, arg2, arg3, arg4, arg5); + break; +#endif +#if defined(TARGET_NR_open_by_handle_at) && defined(CONFIG_OPEN_BY_HANDLE) + case TARGET_NR_open_by_handle_at: + ret = do_open_by_handle_at(arg1, arg2, arg3); + fd_trans_unregister(ret); + break; +#endif case TARGET_NR_close: + fd_trans_unregister(arg1); ret = get_errno(close(arg1)); break; case TARGET_NR_brk: ret = do_brk(arg1); break; +#ifdef TARGET_NR_fork case TARGET_NR_fork: ret = get_errno(do_fork(cpu_env, SIGCHLD, 0, 0, 0, 0)); break; +#endif #ifdef TARGET_NR_waitpid case TARGET_NR_waitpid: { @@ -5695,9 +5995,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(creat(p, arg2)); + fd_trans_unregister(ret); unlock_user(p, arg1, 0); break; #endif +#ifdef TARGET_NR_link case TARGET_NR_link: { void * p2; @@ -5711,6 +6013,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); } break; +#endif #if defined(TARGET_NR_linkat) case TARGET_NR_linkat: { @@ -5728,12 +6031,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } break; #endif +#ifdef TARGET_NR_unlink case TARGET_NR_unlink: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(unlink(p)); unlock_user(p, arg1, 0); break; +#endif #if defined(TARGET_NR_unlinkat) case TARGET_NR_unlinkat: if (!(p = lock_user_string(arg2))) @@ -5799,12 +6104,6 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } *q = NULL; - /* This case will not be caught by the host's execve() if its - page size is bigger than the target's. */ - if (total_size > MAX_ARG_PAGES * TARGET_PAGE_SIZE) { - ret = -TARGET_E2BIG; - goto execve_end; - } if (!(p = lock_user_string(arg1))) goto execve_efault; ret = get_errno(execve(p, argp, envp)); @@ -5850,12 +6149,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } break; #endif +#ifdef TARGET_NR_mknod case TARGET_NR_mknod: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(mknod(p, arg2, arg3)); unlock_user(p, arg1, 0); break; +#endif #if defined(TARGET_NR_mknodat) case TARGET_NR_mknodat: if (!(p = lock_user_string(arg2))) @@ -5864,12 +6165,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg2, 0); break; #endif +#ifdef TARGET_NR_chmod case TARGET_NR_chmod: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(chmod(p, arg2)); unlock_user(p, arg1, 0); break; +#endif #ifdef TARGET_NR_break case TARGET_NR_break: goto unimplemented; @@ -6004,6 +6307,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } break; #endif +#ifdef TARGET_NR_utimes case TARGET_NR_utimes: { struct timeval *tvp, tv[2]; @@ -6022,6 +6326,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); } break; +#endif #if defined(TARGET_NR_futimesat) case TARGET_NR_futimesat: { @@ -6050,12 +6355,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_gtty: goto unimplemented; #endif +#ifdef TARGET_NR_access case TARGET_NR_access: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(access(path(p), arg2)); unlock_user(p, arg1, 0); break; +#endif #if defined(TARGET_NR_faccessat) && defined(__NR_faccessat) case TARGET_NR_faccessat: if (!(p = lock_user_string(arg2))) @@ -6080,6 +6387,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_kill: ret = get_errno(kill(arg1, target_to_host_signal(arg2))); break; +#ifdef TARGET_NR_rename case TARGET_NR_rename: { void *p2; @@ -6093,6 +6401,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); } break; +#endif #if defined(TARGET_NR_renameat) case TARGET_NR_renameat: { @@ -6108,12 +6417,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } break; #endif +#ifdef TARGET_NR_mkdir case TARGET_NR_mkdir: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(mkdir(p, arg2)); unlock_user(p, arg1, 0); break; +#endif #if defined(TARGET_NR_mkdirat) case TARGET_NR_mkdirat: if (!(p = lock_user_string(arg2))) @@ -6122,18 +6433,25 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg2, 0); break; #endif +#ifdef TARGET_NR_rmdir case TARGET_NR_rmdir: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(rmdir(p)); unlock_user(p, arg1, 0); break; +#endif case TARGET_NR_dup: ret = get_errno(dup(arg1)); + if (ret >= 0) { + fd_trans_dup(arg1, ret); + } break; +#ifdef TARGET_NR_pipe case TARGET_NR_pipe: ret = do_pipe(cpu_env, arg1, 0, 0); break; +#endif #ifdef TARGET_NR_pipe2 case TARGET_NR_pipe2: ret = do_pipe(cpu_env, arg1, @@ -6218,14 +6536,24 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(chroot(p)); unlock_user(p, arg1, 0); break; +#ifdef TARGET_NR_ustat case TARGET_NR_ustat: goto unimplemented; +#endif +#ifdef TARGET_NR_dup2 case TARGET_NR_dup2: ret = get_errno(dup2(arg1, arg2)); + if (ret >= 0) { + fd_trans_dup(arg1, arg2); + } break; +#endif #if defined(CONFIG_DUP3) && defined(TARGET_NR_dup3) case TARGET_NR_dup3: ret = get_errno(dup3(arg1, arg2, arg3)); + if (ret >= 0) { + fd_trans_dup(arg1, arg2); + } break; #endif #ifdef TARGET_NR_getppid /* not on alpha */ @@ -6233,9 +6561,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(getppid()); break; #endif +#ifdef TARGET_NR_getpgrp case TARGET_NR_getpgrp: ret = get_errno(getpgrp()); break; +#endif case TARGET_NR_setsid: ret = get_errno(setsid()); break; @@ -6811,6 +7141,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } break; #endif +#ifdef TARGET_NR_symlink case TARGET_NR_symlink: { void *p2; @@ -6824,6 +7155,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); } break; +#endif #if defined(TARGET_NR_symlinkat) case TARGET_NR_symlinkat: { @@ -6843,6 +7175,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_oldlstat: goto unimplemented; #endif +#ifdef TARGET_NR_readlink case TARGET_NR_readlink: { void *p2; @@ -6873,6 +7206,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); } break; +#endif #if defined(TARGET_NR_readlinkat) case TARGET_NR_readlinkat: { @@ -7212,9 +7546,20 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(shutdown(arg1, arg2)); break; #endif +#if defined(TARGET_NR_getrandom) && defined(__NR_getrandom) + case TARGET_NR_getrandom: + p = lock_user(VERIFY_WRITE, arg1, arg2, 0); + if (!p) { + goto efault; + } + ret = get_errno(getrandom(p, arg2, arg3)); + unlock_user(p, arg1, ret); + break; +#endif #ifdef TARGET_NR_socket case TARGET_NR_socket: ret = do_socket(arg1, arg2, arg3); + fd_trans_unregister(ret); break; #endif #ifdef TARGET_NR_socketpair @@ -7272,22 +7617,28 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } } break; +#ifdef TARGET_NR_stat case TARGET_NR_stat: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(stat(path(p), &st)); unlock_user(p, arg1, 0); goto do_stat; +#endif +#ifdef TARGET_NR_lstat case TARGET_NR_lstat: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(lstat(path(p), &st)); unlock_user(p, arg1, 0); goto do_stat; +#endif case TARGET_NR_fstat: { ret = get_errno(fstat(arg1, &st)); +#if defined(TARGET_NR_stat) || defined(TARGET_NR_lstat) do_stat: +#endif if (!is_error(ret)) { struct target_stat *target_st; @@ -7411,7 +7762,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_semctl case TARGET_NR_semctl: - ret = do_semctl(arg1, arg2, arg3, (union target_semun)(abi_ulong)arg4); + ret = do_semctl(arg1, arg2, arg3, arg4); break; #endif #ifdef TARGET_NR_msgctl @@ -7575,6 +7926,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } break; #endif +#ifdef TARGET_NR_getdents case TARGET_NR_getdents: #ifdef __NR_getdents #if TARGET_ABI_BITS == 32 && HOST_LONG_BITS == 64 @@ -7583,8 +7935,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, struct linux_dirent *dirp; abi_long count = arg3; - dirp = malloc(count); - if (!dirp) { + dirp = g_try_malloc(count); + if (!dirp) { ret = -TARGET_ENOMEM; goto fail; } @@ -7620,7 +7972,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = count1; unlock_user(target_dirp, arg2, ret); } - free(dirp); + g_free(dirp); } #else { @@ -7705,6 +8057,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } #endif break; +#endif /* TARGET_NR_getdents */ #if defined(TARGET_NR_getdents64) && defined(__NR_getdents64) case TARGET_NR_getdents64: { @@ -7752,14 +8105,20 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, struct pollfd *pfd; unsigned int i; - target_pfd = lock_user(VERIFY_WRITE, arg1, sizeof(struct target_pollfd) * nfds, 1); - if (!target_pfd) - goto efault; + pfd = NULL; + target_pfd = NULL; + if (nfds) { + target_pfd = lock_user(VERIFY_WRITE, arg1, + sizeof(struct target_pollfd) * nfds, 1); + if (!target_pfd) { + goto efault; + } - pfd = alloca(sizeof(struct pollfd) * nfds); - for(i = 0; i < nfds; i++) { - pfd[i].fd = tswap32(target_pfd[i].fd); - pfd[i].events = tswap16(target_pfd[i].events); + pfd = alloca(sizeof(struct pollfd) * nfds); + for (i = 0; i < nfds; i++) { + pfd[i].fd = tswap32(target_pfd[i].fd); + pfd[i].events = tswap16(target_pfd[i].events); + } } # ifdef TARGET_NR_ppoll @@ -7844,11 +8203,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(fdatasync(arg1)); break; #endif +#ifdef TARGET_NR__sysctl case TARGET_NR__sysctl: /* We don't implement this, but ENOTDIR is always a safe return value. */ ret = -TARGET_ENOTDIR; break; +#endif case TARGET_NR_sched_getaffinity: { unsigned int mask_size; @@ -8150,14 +8511,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; } case TARGET_NR_sigaltstack: -#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_MIPS) || \ - defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_ALPHA) || \ - defined(TARGET_M68K) || defined(TARGET_S390X) || defined(TARGET_OPENRISC) ret = do_sigaltstack(arg1, arg2, get_sp_from_cpustate((CPUArchState *)cpu_env)); break; -#else - goto unimplemented; -#endif #ifdef CONFIG_SENDFILE case TARGET_NR_sendfile: @@ -8295,12 +8650,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = host_to_target_stat64(cpu_env, arg3, &st); break; #endif +#ifdef TARGET_NR_lchown case TARGET_NR_lchown: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(lchown(p, low2highuid(arg2), low2highgid(arg3))); unlock_user(p, arg1, 0); break; +#endif #ifdef TARGET_NR_getuid case TARGET_NR_getuid: ret = get_errno(high2lowuid(getuid())); @@ -8423,12 +8780,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } break; #endif +#ifdef TARGET_NR_chown case TARGET_NR_chown: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(chown(p, low2highuid(arg2), low2highgid(arg3))); unlock_user(p, arg1, 0); break; +#endif case TARGET_NR_setuid: ret = get_errno(setuid(low2highuid(arg1))); break; @@ -9454,6 +9813,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #if defined(TARGET_NR_eventfd) case TARGET_NR_eventfd: ret = get_errno(eventfd(arg1, 0)); + fd_trans_unregister(ret); break; #endif #if defined(TARGET_NR_eventfd2) @@ -9467,6 +9827,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, host_flags |= O_CLOEXEC; } ret = get_errno(eventfd(arg1, host_flags)); + fd_trans_unregister(ret); break; } #endif @@ -9509,6 +9870,16 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; #endif #endif +#if defined(TARGET_NR_signalfd4) + case TARGET_NR_signalfd4: + ret = do_signalfd4(arg1, arg2, arg4); + break; +#endif +#if defined(TARGET_NR_signalfd) + case TARGET_NR_signalfd: + ret = do_signalfd4(arg1, arg2, 0); + break; +#endif #if defined(CONFIG_EPOLL) #if defined(TARGET_NR_epoll_create) case TARGET_NR_epoll_create: @@ -9780,6 +10151,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, timer_t htimer = g_posix_timers[timerid]; ret = get_errno(timer_getoverrun(htimer)); } + fd_trans_unregister(ret); break; } #endif diff --git a/qemu/linux-user/syscall_defs.h b/qemu/linux-user/syscall_defs.h index edd5f3c80..9e2b3c200 100644 --- a/qemu/linux-user/syscall_defs.h +++ b/qemu/linux-user/syscall_defs.h @@ -28,6 +28,8 @@ #define SOCKOP_sendmsg 16 #define SOCKOP_recvmsg 17 #define SOCKOP_accept4 18 +#define SOCKOP_recvmmsg 19 +#define SOCKOP_sendmmsg 20 #define IPCOP_semop 1 #define IPCOP_semget 2 @@ -64,8 +66,9 @@ #endif #if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SH4) \ - || defined(TARGET_M68K) || defined(TARGET_CRIS) || defined(TARGET_UNICORE32) \ - || defined(TARGET_S390X) || defined(TARGET_OPENRISC) + || defined(TARGET_M68K) || defined(TARGET_CRIS) \ + || defined(TARGET_UNICORE32) || defined(TARGET_S390X) \ + || defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) #define TARGET_IOC_SIZEBITS 14 #define TARGET_IOC_DIRBITS 2 @@ -234,7 +237,8 @@ struct target_cmsghdr { }; #define TARGET_CMSG_DATA(cmsg) ((unsigned char *) ((struct target_cmsghdr *) (cmsg) + 1)) -#define TARGET_CMSG_NXTHDR(mhdr, cmsg) __target_cmsg_nxthdr (mhdr, cmsg) +#define TARGET_CMSG_NXTHDR(mhdr, cmsg, cmsg_start) \ + __target_cmsg_nxthdr(mhdr, cmsg, cmsg_start) #define TARGET_CMSG_ALIGN(len) (((len) + sizeof (abi_long) - 1) \ & (size_t) ~(sizeof (abi_long) - 1)) #define TARGET_CMSG_SPACE(len) (TARGET_CMSG_ALIGN (len) \ @@ -242,17 +246,20 @@ struct target_cmsghdr { #define TARGET_CMSG_LEN(len) (TARGET_CMSG_ALIGN (sizeof (struct target_cmsghdr)) + (len)) static __inline__ struct target_cmsghdr * -__target_cmsg_nxthdr (struct target_msghdr *__mhdr, struct target_cmsghdr *__cmsg) +__target_cmsg_nxthdr(struct target_msghdr *__mhdr, + struct target_cmsghdr *__cmsg, + struct target_cmsghdr *__cmsg_start) { struct target_cmsghdr *__ptr; __ptr = (struct target_cmsghdr *)((unsigned char *) __cmsg + TARGET_CMSG_ALIGN (tswapal(__cmsg->cmsg_len))); - if ((unsigned long)((char *)(__ptr+1) - (char *)(size_t)tswapal(__mhdr->msg_control)) - > tswapal(__mhdr->msg_controllen)) + if ((unsigned long)((char *)(__ptr+1) - (char *)__cmsg_start) + > tswapal(__mhdr->msg_controllen)) { /* No more entries. */ return (struct target_cmsghdr *)0; - return __cmsg; + } + return __ptr; } struct target_mmsghdr { @@ -365,7 +372,8 @@ int do_sigaction(int sig, const struct target_sigaction *act, || 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_S390X) || defined(TARGET_OPENRISC) + || defined(TARGET_S390X) || defined(TARGET_OPENRISC) \ + || defined(TARGET_TILEGX) #if defined(TARGET_SPARC) #define TARGET_SA_NOCLDSTOP 8u @@ -742,6 +750,10 @@ typedef struct target_siginfo { #define TARGET_ILL_PRVREG (6) /* privileged register */ #define TARGET_ILL_COPROC (7) /* coprocessor error */ #define TARGET_ILL_BADSTK (8) /* internal stack error */ +#ifdef TARGET_TILEGX +#define TARGET_ILL_DBLFLT (9) /* double fault */ +#define TARGET_ILL_HARDWALL (10) /* user networks hardwall violation */ +#endif /* * SIGFPE si_codes @@ -761,6 +773,7 @@ typedef struct target_siginfo { */ #define TARGET_SEGV_MAPERR (1) /* address not mapped to object */ #define TARGET_SEGV_ACCERR (2) /* invalid permissions for mapped object */ +#define TARGET_SEGV_BNDERR (3) /* failed address bound checks */ /* * SIGBUS si_codes @@ -768,12 +781,18 @@ typedef struct target_siginfo { #define TARGET_BUS_ADRALN (1) /* invalid address alignment */ #define TARGET_BUS_ADRERR (2) /* non-existent physical address */ #define TARGET_BUS_OBJERR (3) /* object specific hardware error */ +/* hardware memory error consumed on a machine check: action required */ +#define TARGET_BUS_MCEERR_AR (4) +/* hardware memory error detected in process but not consumed: action optional*/ +#define TARGET_BUS_MCEERR_AO (5) /* * SIGTRAP si_codes */ #define TARGET_TRAP_BRKPT (1) /* process breakpoint */ #define TARGET_TRAP_TRACE (2) /* process trace trap */ +#define TARGET_TRAP_BRANCH (3) /* process taken branch trap */ +#define TARGET_TRAP_HWBKPT (4) /* hardware breakpoint/watchpoint */ #endif /* defined(TARGET_I386) || defined(TARGET_ARM) */ @@ -969,7 +988,7 @@ struct target_pollfd { (struct cdrom_multisession) */ #define TARGET_CDROM_GET_MCN 0x5311 /* Obtain the "Universal Product Code" if available (struct cdrom_mcn) */ -#define TARGET_CDROM_GET_UPC TARGET_CDROM_GET_MCN /* This one is depricated, +#define TARGET_CDROM_GET_UPC TARGET_CDROM_GET_MCN /* This one is deprecated, but here anyway for compatibility */ #define TARGET_CDROMRESET 0x5312 /* hard-reset the drive */ #define TARGET_CDROMVOLREAD 0x5313 /* Get the drive's volume setting @@ -1871,7 +1890,7 @@ struct target_stat { abi_ulong target_st_ctime_nsec; unsigned int __unused[2]; }; -#elif defined(TARGET_OPENRISC) +#elif defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) /* These are the asm-generic versions of the stat and stat64 structures */ @@ -2264,7 +2283,9 @@ struct target_flock { struct target_flock64 { short l_type; short l_whence; -#if defined(TARGET_PPC) || defined(TARGET_X86_64) || defined(TARGET_MIPS) || defined(TARGET_SPARC) || defined(TARGET_HPPA) || defined (TARGET_MICROBLAZE) +#if defined(TARGET_PPC) || defined(TARGET_X86_64) || defined(TARGET_MIPS) \ + || defined(TARGET_SPARC) || defined(TARGET_HPPA) \ + || defined(TARGET_MICROBLAZE) || defined(TARGET_TILEGX) int __pad; #endif unsigned long long l_start; @@ -2289,7 +2310,7 @@ struct target_f_owner_ex { }; /* soundcard defines */ -/* XXX: convert them all to arch indepedent entries */ +/* XXX: convert them all to arch independent entries */ #define TARGET_SNDCTL_COPR_HALT TARGET_IOWR('C', 7, int); #define TARGET_SNDCTL_COPR_LOAD 0xcfb04301 #define TARGET_SNDCTL_COPR_RCODE 0xc0144303 @@ -2495,20 +2516,23 @@ struct target_mq_attr { #define FUTEX_CMD_MASK ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME) #ifdef CONFIG_EPOLL +#if defined(TARGET_X86_64) +#define TARGET_EPOLL_PACKED QEMU_PACKED +#else +#define TARGET_EPOLL_PACKED +#endif + typedef union target_epoll_data { abi_ulong ptr; - abi_ulong fd; - uint32_t u32; - uint64_t u64; + abi_int fd; + abi_uint u32; + abi_ullong u64; } target_epoll_data_t; struct target_epoll_event { - uint32_t events; -#if defined(TARGET_ARM) || defined(TARGET_MIPS) || defined(TARGET_MIPS64) - uint32_t __pad; -#endif + abi_uint events; target_epoll_data_t data; -} QEMU_PACKED; +} TARGET_EPOLL_PACKED; #endif struct target_rlimit64 { uint64_t rlim_cur; diff --git a/qemu/linux-user/tilegx/syscall_nr.h b/qemu/linux-user/tilegx/syscall_nr.h new file mode 100644 index 000000000..87fb72c55 --- /dev/null +++ b/qemu/linux-user/tilegx/syscall_nr.h @@ -0,0 +1,328 @@ +#ifndef TILEGX_SYSCALL_NR +#define TILEGX_SYSCALL_NR + +/* + * Copy from linux kernel asm-generic/unistd.h, which tilegx uses. + */ +#define TARGET_NR_io_setup 0 +#define TARGET_NR_io_destroy 1 +#define TARGET_NR_io_submit 2 +#define TARGET_NR_io_cancel 3 +#define TARGET_NR_io_getevents 4 +#define TARGET_NR_setxattr 5 +#define TARGET_NR_lsetxattr 6 +#define TARGET_NR_fsetxattr 7 +#define TARGET_NR_getxattr 8 +#define TARGET_NR_lgetxattr 9 +#define TARGET_NR_fgetxattr 10 +#define TARGET_NR_listxattr 11 +#define TARGET_NR_llistxattr 12 +#define TARGET_NR_flistxattr 13 +#define TARGET_NR_removexattr 14 +#define TARGET_NR_lremovexattr 15 +#define TARGET_NR_fremovexattr 16 +#define TARGET_NR_getcwd 17 +#define TARGET_NR_lookup_dcookie 18 +#define TARGET_NR_eventfd2 19 +#define TARGET_NR_epoll_create1 20 +#define TARGET_NR_epoll_ctl 21 +#define TARGET_NR_epoll_pwait 22 +#define TARGET_NR_dup 23 +#define TARGET_NR_dup3 24 +#define TARGET_NR_fcntl 25 +#define TARGET_NR_inotify_init1 26 +#define TARGET_NR_inotify_add_watch 27 +#define TARGET_NR_inotify_rm_watch 28 +#define TARGET_NR_ioctl 29 +#define TARGET_NR_ioprio_set 30 +#define TARGET_NR_ioprio_get 31 +#define TARGET_NR_flock 32 +#define TARGET_NR_mknodat 33 +#define TARGET_NR_mkdirat 34 +#define TARGET_NR_unlinkat 35 +#define TARGET_NR_symlinkat 36 +#define TARGET_NR_linkat 37 +#define TARGET_NR_renameat 38 +#define TARGET_NR_umount2 39 +#define TARGET_NR_mount 40 +#define TARGET_NR_pivot_root 41 +#define TARGET_NR_nfsservctl 42 +#define TARGET_NR_statfs 43 +#define TARGET_NR_fstatfs 44 +#define TARGET_NR_truncate 45 +#define TARGET_NR_ftruncate 46 +#define TARGET_NR_fallocate 47 +#define TARGET_NR_faccessat 48 +#define TARGET_NR_chdir 49 +#define TARGET_NR_fchdir 50 +#define TARGET_NR_chroot 51 +#define TARGET_NR_fchmod 52 +#define TARGET_NR_fchmodat 53 +#define TARGET_NR_fchownat 54 +#define TARGET_NR_fchown 55 +#define TARGET_NR_openat 56 +#define TARGET_NR_close 57 +#define TARGET_NR_vhangup 58 +#define TARGET_NR_pipe2 59 +#define TARGET_NR_quotactl 60 +#define TARGET_NR_getdents64 61 +#define TARGET_NR_lseek 62 +#define TARGET_NR_read 63 +#define TARGET_NR_write 64 +#define TARGET_NR_readv 65 +#define TARGET_NR_writev 66 +#define TARGET_NR_pread64 67 +#define TARGET_NR_pwrite64 68 +#define TARGET_NR_preadv 69 +#define TARGET_NR_pwritev 70 +#define TARGET_NR_sendfile 71 +#define TARGET_NR_pselect6 72 +#define TARGET_NR_ppoll 73 +#define TARGET_NR_signalfd4 74 +#define TARGET_NR_vmsplice 75 +#define TARGET_NR_splice 76 +#define TARGET_NR_tee 77 +#define TARGET_NR_readlinkat 78 +#define TARGET_NR_fstatat64 79 /* let syscall.c known */ +#define TARGET_NR_fstat 80 +#define TARGET_NR_sync 81 +#define TARGET_NR_fsync 82 +#define TARGET_NR_fdatasync 83 +#define TARGET_NR_sync_file_range 84 /* For tilegx, no range2 */ +#define TARGET_NR_timerfd_create 85 +#define TARGET_NR_timerfd_settime 86 +#define TARGET_NR_timerfd_gettime 87 +#define TARGET_NR_utimensat 88 +#define TARGET_NR_acct 89 +#define TARGET_NR_capget 90 +#define TARGET_NR_capset 91 +#define TARGET_NR_personality 92 +#define TARGET_NR_exit 93 +#define TARGET_NR_exit_group 94 +#define TARGET_NR_waitid 95 +#define TARGET_NR_set_tid_address 96 +#define TARGET_NR_unshare 97 +#define TARGET_NR_futex 98 +#define TARGET_NR_set_robust_list 99 +#define TARGET_NR_get_robust_list 100 +#define TARGET_NR_nanosleep 101 +#define TARGET_NR_getitimer 102 +#define TARGET_NR_setitimer 103 +#define TARGET_NR_kexec_load 104 +#define TARGET_NR_init_module 105 +#define TARGET_NR_delete_module 106 +#define TARGET_NR_timer_create 107 +#define TARGET_NR_timer_gettime 108 +#define TARGET_NR_timer_getoverrun 109 +#define TARGET_NR_timer_settime 110 +#define TARGET_NR_timer_delete 111 +#define TARGET_NR_clock_settime 112 +#define TARGET_NR_clock_gettime 113 +#define TARGET_NR_clock_getres 114 +#define TARGET_NR_clock_nanosleep 115 +#define TARGET_NR_syslog 116 +#define TARGET_NR_ptrace 117 +#define TARGET_NR_sched_setparam 118 +#define TARGET_NR_sched_setscheduler 119 +#define TARGET_NR_sched_getscheduler 120 +#define TARGET_NR_sched_getparam 121 +#define TARGET_NR_sched_setaffinity 122 +#define TARGET_NR_sched_getaffinity 123 +#define TARGET_NR_sched_yield 124 +#define TARGET_NR_sched_get_priority_max 125 +#define TARGET_NR_sched_get_priority_min 126 +#define TARGET_NR_sched_rr_get_interval 127 +#define TARGET_NR_restart_syscall 128 +#define TARGET_NR_kill 129 +#define TARGET_NR_tkill 130 +#define TARGET_NR_tgkill 131 +#define TARGET_NR_sigaltstack 132 +#define TARGET_NR_rt_sigsuspend 133 +#define TARGET_NR_rt_sigaction 134 +#define TARGET_NR_rt_sigprocmask 135 +#define TARGET_NR_rt_sigpending 136 +#define TARGET_NR_rt_sigtimedwait 137 +#define TARGET_NR_rt_sigqueueinfo 138 +#define TARGET_NR_rt_sigreturn 139 +#define TARGET_NR_setpriority 140 +#define TARGET_NR_getpriority 141 +#define TARGET_NR_reboot 142 +#define TARGET_NR_setregid 143 +#define TARGET_NR_setgid 144 +#define TARGET_NR_setreuid 145 +#define TARGET_NR_setuid 146 +#define TARGET_NR_setresuid 147 +#define TARGET_NR_getresuid 148 +#define TARGET_NR_setresgid 149 +#define TARGET_NR_getresgid 150 +#define TARGET_NR_setfsuid 151 +#define TARGET_NR_setfsgid 152 +#define TARGET_NR_times 153 +#define TARGET_NR_setpgid 154 +#define TARGET_NR_getpgid 155 +#define TARGET_NR_getsid 156 +#define TARGET_NR_setsid 157 +#define TARGET_NR_getgroups 158 +#define TARGET_NR_setgroups 159 +#define TARGET_NR_uname 160 +#define TARGET_NR_sethostname 161 +#define TARGET_NR_setdomainname 162 +#define TARGET_NR_getrlimit 163 +#define TARGET_NR_setrlimit 164 +#define TARGET_NR_getrusage 165 +#define TARGET_NR_umask 166 +#define TARGET_NR_prctl 167 +#define TARGET_NR_getcpu 168 +#define TARGET_NR_gettimeofday 169 +#define TARGET_NR_settimeofday 170 +#define TARGET_NR_adjtimex 171 +#define TARGET_NR_getpid 172 +#define TARGET_NR_getppid 173 +#define TARGET_NR_getuid 174 +#define TARGET_NR_geteuid 175 +#define TARGET_NR_getgid 176 +#define TARGET_NR_getegid 177 +#define TARGET_NR_gettid 178 +#define TARGET_NR_sysinfo 179 +#define TARGET_NR_mq_open 180 +#define TARGET_NR_mq_unlink 181 +#define TARGET_NR_mq_timedsend 182 +#define TARGET_NR_mq_timedreceive 183 +#define TARGET_NR_mq_notify 184 +#define TARGET_NR_mq_getsetattr 185 +#define TARGET_NR_msgget 186 +#define TARGET_NR_msgctl 187 +#define TARGET_NR_msgrcv 188 +#define TARGET_NR_msgsnd 189 +#define TARGET_NR_semget 190 +#define TARGET_NR_semctl 191 +#define TARGET_NR_semtimedop 192 +#define TARGET_NR_semop 193 +#define TARGET_NR_shmget 194 +#define TARGET_NR_shmctl 195 +#define TARGET_NR_shmat 196 +#define TARGET_NR_shmdt 197 +#define TARGET_NR_socket 198 +#define TARGET_NR_socketpair 199 +#define TARGET_NR_bind 200 +#define TARGET_NR_listen 201 +#define TARGET_NR_accept 202 +#define TARGET_NR_connect 203 +#define TARGET_NR_getsockname 204 +#define TARGET_NR_getpeername 205 +#define TARGET_NR_sendto 206 +#define TARGET_NR_recvfrom 207 +#define TARGET_NR_setsockopt 208 +#define TARGET_NR_getsockopt 209 +#define TARGET_NR_shutdown 210 +#define TARGET_NR_sendmsg 211 +#define TARGET_NR_recvmsg 212 +#define TARGET_NR_readahead 213 +#define TARGET_NR_brk 214 +#define TARGET_NR_munmap 215 +#define TARGET_NR_mremap 216 +#define TARGET_NR_add_key 217 +#define TARGET_NR_request_key 218 +#define TARGET_NR_keyctl 219 +#define TARGET_NR_clone 220 +#define TARGET_NR_execve 221 +#define TARGET_NR_mmap 222 +#define TARGET_NR_fadvise64 223 +#define TARGET_NR_swapon 224 +#define TARGET_NR_swapoff 225 +#define TARGET_NR_mprotect 226 +#define TARGET_NR_msync 227 +#define TARGET_NR_mlock 228 +#define TARGET_NR_munlock 229 +#define TARGET_NR_mlockall 230 +#define TARGET_NR_munlockall 231 +#define TARGET_NR_mincore 232 +#define TARGET_NR_madvise 233 +#define TARGET_NR_remap_file_pages 234 +#define TARGET_NR_mbind 235 +#define TARGET_NR_get_mempolicy 236 +#define TARGET_NR_set_mempolicy 237 +#define TARGET_NR_migrate_pages 238 +#define TARGET_NR_move_pages 239 +#define TARGET_NR_rt_tgsigqueueinfo 240 +#define TARGET_NR_perf_event_open 241 +#define TARGET_NR_accept4 242 +#define TARGET_NR_recvmmsg 243 + +#define TARGET_NR_arch_specific_syscall 244 +#define TARGET_NR_cacheflush 245 /* tilegx own syscall */ + +#define TARGET_NR_wait4 260 +#define TARGET_NR_prlimit64 261 +#define TARGET_NR_fanotify_init 262 +#define TARGET_NR_fanotify_mark 263 +#define TARGET_NR_name_to_handle_at 264 +#define TARGET_NR_open_by_handle_at 265 +#define TARGET_NR_clock_adjtime 266 +#define TARGET_NR_syncfs 267 +#define TARGET_NR_setns 268 +#define TARGET_NR_sendmmsg 269 +#define TARGET_NR_process_vm_readv 270 +#define TARGET_NR_process_vm_writev 271 +#define TARGET_NR_kcmp 272 +#define TARGET_NR_finit_module 273 +#define TARGET_NR_sched_setattr 274 +#define TARGET_NR_sched_getattr 275 +#define TARGET_NR_renameat2 276 +#define TARGET_NR_seccomp 277 +#define TARGET_NR_getrandom 278 +#define TARGET_NR_memfd_create 279 +#define TARGET_NR_bpf 280 +#define TARGET_NR_execveat 281 +#define TARGET_NR_userfaultfd 282 +#define TARGET_NR_membarrier 283 +#define TARGET_NR_mlock2 284 +#define TARGET_NR_copy_file_range 285 + +#define TARGET_NR_open 1024 +#define TARGET_NR_link 1025 +#define TARGET_NR_unlink 1026 +#define TARGET_NR_mknod 1027 +#define TARGET_NR_chmod 1028 +#define TARGET_NR_chown 1029 +#define TARGET_NR_mkdir 1030 +#define TARGET_NR_rmdir 1031 +#define TARGET_NR_lchown 1032 +#define TARGET_NR_access 1033 +#define TARGET_NR_rename 1034 +#define TARGET_NR_readlink 1035 +#define TARGET_NR_symlink 1036 +#define TARGET_NR_utimes 1037 +#define TARGET_NR_stat64 1038 /* let syscall.c known */ +#define TARGET_NR_lstat 1039 + +#define TARGET_NR_pipe 1040 +#define TARGET_NR_dup2 1041 +#define TARGET_NR_epoll_create 1042 +#define TARGET_NR_inotify_init 1043 +#define TARGET_NR_eventfd 1044 +#define TARGET_NR_signalfd 1045 + +#define TARGET_NR_alarm 1059 +#define TARGET_NR_getpgrp 1060 +#define TARGET_NR_pause 1061 +#define TARGET_NR_time 1062 +#define TARGET_NR_utime 1063 +#define TARGET_NR_creat 1064 +#define TARGET_NR_getdents 1065 +#define TARGET_NR_futimesat 1066 +#define TARGET_NR_select 1067 +#define TARGET_NR_poll 1068 +#define TARGET_NR_epoll_wait 1069 +#define TARGET_NR_ustat 1070 +#define TARGET_NR_vfork 1071 +#define TARGET_NR_oldwait4 1072 +#define TARGET_NR_recv 1073 +#define TARGET_NR_send 1074 +#define TARGET_NR_bdflush 1075 +#define TARGET_NR_umount 1076 +#define TARGET_NR_uselib 1077 +#define TARGET_NR__sysctl 1078 +#define TARGET_NR_fork 1079 + +#endif diff --git a/qemu/linux-user/tilegx/target_cpu.h b/qemu/linux-user/tilegx/target_cpu.h new file mode 100644 index 000000000..c96e81d05 --- /dev/null +++ b/qemu/linux-user/tilegx/target_cpu.h @@ -0,0 +1,35 @@ +/* + * TILE-Gx specific CPU ABI and functions for linux-user + * + * Copyright (c) 2015 Chen Gang + * + * 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 + * 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 . + */ +#ifndef TARGET_CPU_H +#define TARGET_CPU_H + +static inline void cpu_clone_regs(CPUTLGState *env, target_ulong newsp) +{ + if (newsp) { + env->regs[TILEGX_R_SP] = newsp; + } + env->regs[TILEGX_R_RE] = 0; +} + +static inline void cpu_set_tls(CPUTLGState *env, target_ulong newtls) +{ + env->regs[TILEGX_R_TP] = newtls; +} + +#endif diff --git a/qemu/linux-user/tilegx/target_signal.h b/qemu/linux-user/tilegx/target_signal.h new file mode 100644 index 000000000..b595f985c --- /dev/null +++ b/qemu/linux-user/tilegx/target_signal.h @@ -0,0 +1,28 @@ +#ifndef TARGET_SIGNAL_H +#define 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(CPUTLGState *state) +{ + return state->regs[TILEGX_R_SP]; +} + +#endif /* TARGET_SIGNAL_H */ diff --git a/qemu/linux-user/tilegx/target_structs.h b/qemu/linux-user/tilegx/target_structs.h new file mode 100644 index 000000000..7d3ff782f --- /dev/null +++ b/qemu/linux-user/tilegx/target_structs.h @@ -0,0 +1,46 @@ +/* + * TILE-Gx specific structures for linux-user + * + * Copyright (c) 2015 Chen Gang + * + * 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 . + */ +#ifndef TARGET_STRUCTS_H +#define 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_uint mode; /* Read/write permission. */ + abi_ushort __seq; /* Sequence number. */ +}; + +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() */ + abi_ulong shm_dtime; /* time of last shmdt() */ + abi_ulong shm_ctime; /* time of last change by shmctl() */ + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ushort shm_nattch; /* number of current attaches */ + abi_ushort shm_unused; /* compatibility */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/qemu/linux-user/tilegx/target_syscall.h b/qemu/linux-user/tilegx/target_syscall.h new file mode 100644 index 000000000..a938d4e90 --- /dev/null +++ b/qemu/linux-user/tilegx/target_syscall.h @@ -0,0 +1,43 @@ +#ifndef TILEGX_SYSCALLS_H +#define TILEGX_SYSCALLS_H + +#define UNAME_MACHINE "tilegx" +#define UNAME_MINIMUM_RELEASE "3.19" + +#define MMAP_SHIFT TARGET_PAGE_BITS + +#define TILEGX_IS_ERRNO(ret) \ + ((ret) > 0xfffffffffffff000ULL) /* errno is 0 -- 4096 */ + +typedef uint64_t tilegx_reg_t; + +struct target_pt_regs { + + union { + /* Saved main processor registers; 56..63 are special. */ + tilegx_reg_t regs[56]; + struct { + tilegx_reg_t __regs[53]; + tilegx_reg_t tp; /* aliases regs[TREG_TP] */ + tilegx_reg_t sp; /* aliases regs[TREG_SP] */ + tilegx_reg_t lr; /* aliases regs[TREG_LR] */ + }; + }; + + /* Saved special registers. */ + tilegx_reg_t pc; /* stored in EX_CONTEXT_K_0 */ + tilegx_reg_t ex1; /* stored in EX_CONTEXT_K_1 (PL and ICS bit) */ + tilegx_reg_t faultnum; /* fault number (INT_SWINT_1 for syscall) */ + tilegx_reg_t orig_r0; /* r0 at syscall entry, else zero */ + tilegx_reg_t flags; /* flags (see below) */ + tilegx_reg_t cmpexch; /* value of CMPEXCH_VALUE SPR at interrupt */ + tilegx_reg_t pad[2]; +}; + +#define TARGET_MLOCKALL_MCL_CURRENT 1 +#define TARGET_MLOCKALL_MCL_FUTURE 2 + +/* For faultnum */ +#define TARGET_INT_SWINT_1 14 + +#endif diff --git a/qemu/linux-user/tilegx/termbits.h b/qemu/linux-user/tilegx/termbits.h new file mode 100644 index 000000000..91ec23654 --- /dev/null +++ b/qemu/linux-user/tilegx/termbits.h @@ -0,0 +1,274 @@ +#ifndef TILEGX_TERMBITS_H +#define TILEGX_TERMBITS_H + +/* From asm-generic/termbits.h, which is used by tilegx */ + +#define TARGET_NCCS 19 +struct target_termios { + unsigned int c_iflag; /* input mode flags */ + unsigned int c_oflag; /* output mode flags */ + unsigned int c_cflag; /* control mode flags */ + unsigned int c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[TARGET_NCCS]; /* control characters */ +}; + +struct target_termios2 { + unsigned int c_iflag; /* input mode flags */ + unsigned int c_oflag; /* output mode flags */ + unsigned int c_cflag; /* control mode flags */ + unsigned int c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[TARGET_NCCS]; /* control characters */ + unsigned int c_ispeed; /* input speed */ + unsigned int 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 TARGET_B19200 +#define TARGET_EXTB TARGET_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 +#define TARGET_EXTPROC 0200000 + +/* 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 asm-generic/ioctls.h, which is used by tilegx */ + +#define TARGET_TCGETS 0x5401 +#define TARGET_TCSETS 0x5402 +#define TARGET_TCSETSW 0x5403 +#define TARGET_TCSETSF 0x5404 +#define TARGET_TCGETA 0x5405 +#define TARGET_TCSETA 0x5406 +#define TARGET_TCSETAW 0x5407 +#define TARGET_TCSETAF 0x5408 +#define TARGET_TCSBRK 0x5409 +#define TARGET_TCXONC 0x540A +#define TARGET_TCFLSH 0x540B +#define TARGET_TIOCEXCL 0x540C +#define TARGET_TIOCNXCL 0x540D +#define TARGET_TIOCSCTTY 0x540E +#define TARGET_TIOCGPGRP 0x540F +#define TARGET_TIOCSPGRP 0x5410 +#define TARGET_TIOCOUTQ 0x5411 +#define TARGET_TIOCSTI 0x5412 +#define TARGET_TIOCGWINSZ 0x5413 +#define TARGET_TIOCSWINSZ 0x5414 +#define TARGET_TIOCMGET 0x5415 +#define TARGET_TIOCMBIS 0x5416 +#define TARGET_TIOCMBIC 0x5417 +#define TARGET_TIOCMSET 0x5418 +#define TARGET_TIOCGSOFTCAR 0x5419 +#define TARGET_TIOCSSOFTCAR 0x541A +#define TARGET_FIONREAD 0x541B +#define TARGET_TIOCINQ TARGET_FIONREAD +#define TARGET_TIOCLINUX 0x541C +#define TARGET_TIOCCONS 0x541D +#define TARGET_TIOCGSERIAL 0x541E +#define TARGET_TIOCSSERIAL 0x541F +#define TARGET_TIOCPKT 0x5420 +#define TARGET_FIONBIO 0x5421 +#define TARGET_TIOCNOTTY 0x5422 +#define TARGET_TIOCSETD 0x5423 +#define TARGET_TIOCGETD 0x5424 +#define TARGET_TCSBRKP 0x5425 +#define TARGET_TIOCSBRK 0x5427 +#define TARGET_TIOCCBRK 0x5428 +#define TARGET_TIOCGSID 0x5429 +#define TARGET_TCGETS2 TARGET_IOR('T', 0x2A, struct termios2) +#define TARGET_TCSETS2 TARGET_IOW('T', 0x2B, struct termios2) +#define TARGET_TCSETSW2 TARGET_IOW('T', 0x2C, struct termios2) +#define TARGET_TCSETSF2 TARGET_IOW('T', 0x2D, struct termios2) +#define TARGET_TIOCGRS485 0x542E +#define TARGET_TIOCSRS485 0x542F +#define TARGET_TIOCGPTN TARGET_IOR('T', 0x30, unsigned int) +#define TARGET_TIOCSPTLCK TARGET_IOW('T', 0x31, int) +#define TARGET_TIOCGDEV TARGET_IOR('T', 0x32, unsigned int) +#define TARGET_TCGETX 0x5432 +#define TARGET_TCSETX 0x5433 +#define TARGET_TCSETXF 0x5434 +#define TARGET_TCSETXW 0x5435 +#define TARGET_TIOCSIG TARGET_IOW('T', 0x36, int) +#define TARGET_TIOCVHANGUP 0x5437 +#define TARGET_TIOCGPKT TARGET_IOR('T', 0x38, int) +#define TARGET_TIOCGPTLCK TARGET_IOR('T', 0x39, int) +#define TARGET_TIOCGEXCL TARGET_IOR('T', 0x40, int) + +#define TARGET_FIONCLEX 0x5450 +#define TARGET_FIOCLEX 0x5451 +#define TARGET_FIOASYNC 0x5452 +#define TARGET_TIOCSERCONFIG 0x5453 +#define TARGET_TIOCSERGWILD 0x5454 +#define TARGET_TIOCSERSWILD 0x5455 +#define TARGET_TIOCGLCKTRMIOS 0x5456 +#define TARGET_TIOCSLCKTRMIOS 0x5457 +#define TARGET_TIOCSERGSTRUCT 0x5458 +#define TARGET_TIOCSERGETLSR 0x5459 +#define TARGET_TIOCSERGETMULTI 0x545A +#define TARGET_TIOCSERSETMULTI 0x545B + +#define TARGET_TIOCMIWAIT 0x545C +#define TARGET_TIOCGICOUNT 0x545D +#define TARGET_FIOQSIZE 0x5460 + +#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_TIOCSER_TEMT 0x01 + +#endif diff --git a/qemu/linux-user/uaccess.c b/qemu/linux-user/uaccess.c index a4d108c2f..0a5c0b0b2 100644 --- a/qemu/linux-user/uaccess.c +++ b/qemu/linux-user/uaccess.c @@ -1,6 +1,6 @@ /* User memory access */ -#include -#include +#include "qemu/osdep.h" +#include "qemu/cutils.h" #include "qemu.h" diff --git a/qemu/linux-user/uname.c b/qemu/linux-user/uname.c index 1e6560d72..313b79dba 100644 --- a/qemu/linux-user/uname.c +++ b/qemu/linux-user/uname.c @@ -17,7 +17,7 @@ * along with this program; if not, see . */ -#include +#include "qemu/osdep.h" #include "qemu.h" //#include "qemu-common.h" diff --git a/qemu/linux-user/unicore32/target_signal.h b/qemu/linux-user/unicore32/target_signal.h index 8b255c455..7c442381a 100644 --- a/qemu/linux-user/unicore32/target_signal.h +++ b/qemu/linux-user/unicore32/target_signal.h @@ -21,6 +21,10 @@ typedef struct target_sigaltstack { #define TARGET_SS_ONSTACK 1 #define TARGET_SS_DISABLE 2 -#define get_sp_from_cpustate(cpustate) (cpustate->regs[29]) +static inline abi_ulong get_sp_from_cpustate(CPUUniCore32State *state) +{ + return state->regs[29]; +} + #endif /* TARGET_SIGNAL_H */ diff --git a/qemu/linux-user/unicore32/syscall.h b/qemu/linux-user/unicore32/target_syscall.h similarity index 100% rename from qemu/linux-user/unicore32/syscall.h rename to qemu/linux-user/unicore32/target_syscall.h diff --git a/qemu/linux-user/vm86.c b/qemu/linux-user/vm86.c index 22a4eb962..3829b9a67 100644 --- a/qemu/linux-user/vm86.c +++ b/qemu/linux-user/vm86.c @@ -16,12 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include "qemu.h" diff --git a/qemu/linux-user/x86_64/syscall.h b/qemu/linux-user/x86_64/syscall.h deleted file mode 100644 index 88b3c3fe3..000000000 --- a/qemu/linux-user/x86_64/syscall.h +++ /dev/null @@ -1,102 +0,0 @@ -#define __USER_CS (0x33) -#define __USER_DS (0x2B) - -struct target_pt_regs { - abi_ulong r15; - abi_ulong r14; - abi_ulong r13; - abi_ulong r12; - abi_ulong rbp; - abi_ulong rbx; -/* arguments: non interrupts/non tracing syscalls only save up to here */ - abi_ulong r11; - abi_ulong r10; - abi_ulong r9; - abi_ulong r8; - abi_ulong rax; - abi_ulong rcx; - abi_ulong rdx; - abi_ulong rsi; - abi_ulong rdi; - abi_ulong orig_rax; -/* end of arguments */ -/* cpu exception frame or undefined */ - abi_ulong rip; - abi_ulong cs; - abi_ulong eflags; - abi_ulong rsp; - abi_ulong ss; -/* top of stack page */ -}; - -/* Maximum number of LDT entries supported. */ -#define TARGET_LDT_ENTRIES 8192 -/* The size of each LDT entry. */ -#define TARGET_LDT_ENTRY_SIZE 8 - -#define TARGET_GDT_ENTRIES 16 -#define TARGET_GDT_ENTRY_TLS_ENTRIES 3 -#define TARGET_GDT_ENTRY_TLS_MIN 12 -#define TARGET_GDT_ENTRY_TLS_MAX 14 - -#if 0 // Redefine this -struct target_modify_ldt_ldt_s { - unsigned int entry_number; - abi_ulong base_addr; - unsigned int limit; - unsigned int seg_32bit:1; - unsigned int contents:2; - unsigned int read_exec_only:1; - unsigned int limit_in_pages:1; - unsigned int seg_not_present:1; - unsigned int useable:1; - unsigned int lm:1; -}; -#else -struct target_modify_ldt_ldt_s { - unsigned int entry_number; - abi_ulong base_addr; - unsigned int limit; - unsigned int flags; -}; -#endif - -struct target_ipc64_perm -{ - int key; - uint32_t uid; - uint32_t gid; - uint32_t cuid; - uint32_t cgid; - unsigned short mode; - unsigned short __pad1; - unsigned short seq; - unsigned short __pad2; - abi_ulong __unused1; - abi_ulong __unused2; -}; - -struct target_msqid64_ds { - struct target_ipc64_perm msg_perm; - unsigned int msg_stime; /* last msgsnd time */ - unsigned int msg_rtime; /* last msgrcv time */ - unsigned int msg_ctime; /* last change time */ - abi_ulong msg_cbytes; /* current number of bytes on queue */ - abi_ulong msg_qnum; /* number of messages in queue */ - abi_ulong msg_qbytes; /* max number of bytes on queue */ - unsigned int msg_lspid; /* pid of last msgsnd */ - unsigned int msg_lrpid; /* last receive pid */ - abi_ulong __unused4; - abi_ulong __unused5; -}; - -#define UNAME_MACHINE "x86_64" -#define UNAME_MINIMUM_RELEASE "2.6.32" - -#define TARGET_ARCH_SET_GS 0x1001 -#define TARGET_ARCH_SET_FS 0x1002 -#define TARGET_ARCH_GET_FS 0x1003 -#define TARGET_ARCH_GET_GS 0x1004 -#define TARGET_MINSIGSTKSZ 2048 -#define TARGET_MLOCKALL_MCL_CURRENT 1 -#define TARGET_MLOCKALL_MCL_FUTURE 2 diff --git a/qemu/linux-user/x86_64/syscall_nr.h b/qemu/linux-user/x86_64/syscall_nr.h index 7c59e3a09..16397b3e8 100644 --- a/qemu/linux-user/x86_64/syscall_nr.h +++ b/qemu/linux-user/x86_64/syscall_nr.h @@ -281,7 +281,7 @@ #define TARGET_NR_utimensat 280 #define TARGET_NR_epoll_pwait 281 #define TARGET_NR_signalfd 282 -#define TARGET_NR_timerfd 283 +#define TARGET_NR_timerfd_create 283 #define TARGET_NR_eventfd 284 #define TARGET_NR_fallocate 285 #define TARGET_NR_timerfd_settime 286 @@ -312,3 +312,16 @@ #define TARGET_NR_process_vm_writev 311 #define TARGET_NR_kcmp 312 #define TARGET_NR_finit_module 313 +#define TARGET_NR_sched_setattr 314 +#define TARGET_NR_sched_getattr 315 +#define TARGET_NR_renameat2 316 +#define TARGET_NR_seccomp 317 +#define TARGET_NR_getrandom 318 +#define TARGET_NR_memfd_create 319 +#define TARGET_NR_kexec_file_load 320 +#define TARGET_NR_bpf 321 +#define TARGET_NR_execveat 322 +#define TARGET_NR_userfaultfd 323 +#define TARGET_NR_membarrier 324 +#define TARGET_NR_mlock2 325 +#define TARGET_NR_copy_file_range 326 diff --git a/qemu/linux-user/x86_64/target_syscall.h b/qemu/linux-user/x86_64/target_syscall.h new file mode 100644 index 000000000..feecd32d5 --- /dev/null +++ b/qemu/linux-user/x86_64/target_syscall.h @@ -0,0 +1,107 @@ +#ifndef TARGET_SYSCALL_H +#define TARGET_SYSCALL_H + +#define __USER_CS (0x33) +#define __USER_DS (0x2B) + +struct target_pt_regs { + abi_ulong r15; + abi_ulong r14; + abi_ulong r13; + abi_ulong r12; + abi_ulong rbp; + abi_ulong rbx; +/* arguments: non interrupts/non tracing syscalls only save up to here */ + abi_ulong r11; + abi_ulong r10; + abi_ulong r9; + abi_ulong r8; + abi_ulong rax; + abi_ulong rcx; + abi_ulong rdx; + abi_ulong rsi; + abi_ulong rdi; + abi_ulong orig_rax; +/* end of arguments */ +/* cpu exception frame or undefined */ + abi_ulong rip; + abi_ulong cs; + abi_ulong eflags; + abi_ulong rsp; + abi_ulong ss; +/* top of stack page */ +}; + +/* Maximum number of LDT entries supported. */ +#define TARGET_LDT_ENTRIES 8192 +/* The size of each LDT entry. */ +#define TARGET_LDT_ENTRY_SIZE 8 + +#define TARGET_GDT_ENTRIES 16 +#define TARGET_GDT_ENTRY_TLS_ENTRIES 3 +#define TARGET_GDT_ENTRY_TLS_MIN 12 +#define TARGET_GDT_ENTRY_TLS_MAX 14 + +#if 0 // Redefine this +struct target_modify_ldt_ldt_s { + unsigned int entry_number; + abi_ulong base_addr; + unsigned int limit; + unsigned int seg_32bit:1; + unsigned int contents:2; + unsigned int read_exec_only:1; + unsigned int limit_in_pages:1; + unsigned int seg_not_present:1; + unsigned int useable:1; + unsigned int lm:1; +}; +#else +struct target_modify_ldt_ldt_s { + unsigned int entry_number; + abi_ulong base_addr; + unsigned int limit; + unsigned int flags; +}; +#endif + +struct target_ipc64_perm +{ + int key; + uint32_t uid; + uint32_t gid; + uint32_t cuid; + uint32_t cgid; + unsigned short mode; + unsigned short __pad1; + unsigned short seq; + unsigned short __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_msqid64_ds { + struct target_ipc64_perm msg_perm; + unsigned int msg_stime; /* last msgsnd time */ + unsigned int msg_rtime; /* last msgrcv time */ + unsigned int msg_ctime; /* last change time */ + abi_ulong msg_cbytes; /* current number of bytes on queue */ + abi_ulong msg_qnum; /* number of messages in queue */ + abi_ulong msg_qbytes; /* max number of bytes on queue */ + unsigned int msg_lspid; /* pid of last msgsnd */ + unsigned int msg_lrpid; /* last receive pid */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#define UNAME_MACHINE "x86_64" +#define UNAME_MINIMUM_RELEASE "2.6.32" + +#define TARGET_ARCH_SET_GS 0x1001 +#define TARGET_ARCH_SET_FS 0x1002 +#define TARGET_ARCH_GET_FS 0x1003 +#define TARGET_ARCH_GET_GS 0x1004 +#define TARGET_MINSIGSTKSZ 2048 +#define TARGET_MLOCKALL_MCL_CURRENT 1 +#define TARGET_MLOCKALL_MCL_FUTURE 2 + +#endif /* TARGET_SYSCALL_H */ diff --git a/qemu/main-loop.c b/qemu/main-loop.c index 39970437f..89a699419 100644 --- a/qemu/main-loop.c +++ b/qemu/main-loop.c @@ -22,7 +22,9 @@ * THE SOFTWARE. */ -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/cutils.h" #include "qemu/timer.h" #include "qemu/sockets.h" // struct in_addr needed for libslirp.h #include "sysemu/qtest.h" @@ -161,6 +163,9 @@ int qemu_init_main_loop(Error **errp) src = aio_get_g_source(qemu_aio_context); g_source_attach(src, NULL); g_source_unref(src); + src = iohandler_get_g_source(); + g_source_attach(src, NULL); + g_source_unref(src); return 0; } @@ -227,7 +232,7 @@ static int os_host_main_loop_wait(int64_t timeout) if (!timeout && (spin_counter > MAX_MAIN_LOOP_SPIN)) { static bool notified; - if (!notified && !qtest_enabled()) { + if (!notified && !qtest_driver()) { fprintf(stderr, "main-loop: WARNING: I/O thread spun for %d iterations\n", MAX_MAIN_LOOP_SPIN); @@ -487,7 +492,6 @@ int main_loop_wait(int nonblocking) #ifdef CONFIG_SLIRP slirp_pollfds_fill(gpollfds, &timeout); #endif - qemu_iohandler_fill(gpollfds); if (timeout == UINT32_MAX) { timeout_ns = -1; @@ -500,11 +504,13 @@ int main_loop_wait(int nonblocking) &main_loop_tlg)); ret = os_host_main_loop_wait(timeout_ns); - qemu_iohandler_poll(gpollfds, ret); #ifdef CONFIG_SLIRP slirp_pollfds_poll(gpollfds, (ret < 0)); #endif + /* CPU thread can infinitely wait for event after + missing the warp */ + qemu_start_warp_timer(); qemu_clock_run_all_timers(); return ret; diff --git a/qemu/memory.c b/qemu/memory.c index 4eb138a42..f76f85df9 100644 --- a/qemu/memory.c +++ b/qemu/memory.c @@ -13,17 +13,20 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "exec/memory.h" #include "exec/address-spaces.h" #include "exec/ioport.h" #include "qapi/visitor.h" #include "qemu/bitops.h" +#include "qemu/error-report.h" #include "qom/object.h" #include "trace.h" -#include #include "exec/memory-internal.h" #include "exec/ram_addr.h" +#include "sysemu/kvm.h" #include "sysemu/sysemu.h" //#define DEBUG_UNASSIGNED @@ -370,6 +373,28 @@ static void adjust_endianness(MemoryRegion *mr, uint64_t *data, unsigned size) } } +static hwaddr memory_region_to_absolute_addr(MemoryRegion *mr, hwaddr offset) +{ + MemoryRegion *root; + hwaddr abs_addr = offset; + + abs_addr += mr->addr; + for (root = mr; root->container; ) { + root = root->container; + abs_addr += root->addr; + } + + return abs_addr; +} + +static int get_cpu_index(void) +{ + if (current_cpu) { + return current_cpu->cpu_index; + } + return -1; +} + static MemTxResult memory_region_oldmmio_read_accessor(MemoryRegion *mr, hwaddr addr, uint64_t *value, @@ -381,7 +406,17 @@ static MemTxResult memory_region_oldmmio_read_accessor(MemoryRegion *mr, uint64_t tmp; tmp = mr->ops->old_mmio.read[ctz32(size)](mr->opaque, addr); - trace_memory_region_ops_read(mr, addr, tmp, size); + if (mr->subpage) { + trace_memory_region_subpage_read(get_cpu_index(), mr, addr, tmp, size); + } else if (mr == &io_mem_notdirty) { + /* Accesses to code which has previously been translated into a TB show + * up in the MMIO path, as accesses to the io_mem_notdirty + * MemoryRegion. */ + trace_memory_region_tb_read(get_cpu_index(), addr, tmp, size); + } else if (TRACE_MEMORY_REGION_OPS_READ_ENABLED) { + hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr); + trace_memory_region_ops_read(get_cpu_index(), mr, abs_addr, tmp, size); + } *value |= (tmp & mask) << shift; return MEMTX_OK; } @@ -397,7 +432,17 @@ static MemTxResult memory_region_read_accessor(MemoryRegion *mr, uint64_t tmp; tmp = mr->ops->read(mr->opaque, addr, size); - trace_memory_region_ops_read(mr, addr, tmp, size); + if (mr->subpage) { + trace_memory_region_subpage_read(get_cpu_index(), mr, addr, tmp, size); + } else if (mr == &io_mem_notdirty) { + /* Accesses to code which has previously been translated into a TB show + * up in the MMIO path, as accesses to the io_mem_notdirty + * MemoryRegion. */ + trace_memory_region_tb_read(get_cpu_index(), addr, tmp, size); + } else if (TRACE_MEMORY_REGION_OPS_READ_ENABLED) { + hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr); + trace_memory_region_ops_read(get_cpu_index(), mr, abs_addr, tmp, size); + } *value |= (tmp & mask) << shift; return MEMTX_OK; } @@ -414,7 +459,17 @@ static MemTxResult memory_region_read_with_attrs_accessor(MemoryRegion *mr, MemTxResult r; r = mr->ops->read_with_attrs(mr->opaque, addr, &tmp, size, attrs); - trace_memory_region_ops_read(mr, addr, tmp, size); + if (mr->subpage) { + trace_memory_region_subpage_read(get_cpu_index(), mr, addr, tmp, size); + } else if (mr == &io_mem_notdirty) { + /* Accesses to code which has previously been translated into a TB show + * up in the MMIO path, as accesses to the io_mem_notdirty + * MemoryRegion. */ + trace_memory_region_tb_read(get_cpu_index(), addr, tmp, size); + } else if (TRACE_MEMORY_REGION_OPS_READ_ENABLED) { + hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr); + trace_memory_region_ops_read(get_cpu_index(), mr, abs_addr, tmp, size); + } *value |= (tmp & mask) << shift; return r; } @@ -430,7 +485,17 @@ static MemTxResult memory_region_oldmmio_write_accessor(MemoryRegion *mr, uint64_t tmp; tmp = (*value >> shift) & mask; - trace_memory_region_ops_write(mr, addr, tmp, size); + if (mr->subpage) { + trace_memory_region_subpage_write(get_cpu_index(), mr, addr, tmp, size); + } else if (mr == &io_mem_notdirty) { + /* Accesses to code which has previously been translated into a TB show + * up in the MMIO path, as accesses to the io_mem_notdirty + * MemoryRegion. */ + trace_memory_region_tb_write(get_cpu_index(), addr, tmp, size); + } else if (TRACE_MEMORY_REGION_OPS_WRITE_ENABLED) { + hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr); + trace_memory_region_ops_write(get_cpu_index(), mr, abs_addr, tmp, size); + } mr->ops->old_mmio.write[ctz32(size)](mr->opaque, addr, tmp); return MEMTX_OK; } @@ -446,7 +511,17 @@ static MemTxResult memory_region_write_accessor(MemoryRegion *mr, uint64_t tmp; tmp = (*value >> shift) & mask; - trace_memory_region_ops_write(mr, addr, tmp, size); + if (mr->subpage) { + trace_memory_region_subpage_write(get_cpu_index(), mr, addr, tmp, size); + } else if (mr == &io_mem_notdirty) { + /* Accesses to code which has previously been translated into a TB show + * up in the MMIO path, as accesses to the io_mem_notdirty + * MemoryRegion. */ + trace_memory_region_tb_write(get_cpu_index(), addr, tmp, size); + } else if (TRACE_MEMORY_REGION_OPS_WRITE_ENABLED) { + hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr); + trace_memory_region_ops_write(get_cpu_index(), mr, abs_addr, tmp, size); + } mr->ops->write(mr->opaque, addr, tmp, size); return MEMTX_OK; } @@ -462,7 +537,17 @@ static MemTxResult memory_region_write_with_attrs_accessor(MemoryRegion *mr, uint64_t tmp; tmp = (*value >> shift) & mask; - trace_memory_region_ops_write(mr, addr, tmp, size); + if (mr->subpage) { + trace_memory_region_subpage_write(get_cpu_index(), mr, addr, tmp, size); + } else if (mr == &io_mem_notdirty) { + /* Accesses to code which has previously been translated into a TB show + * up in the MMIO path, as accesses to the io_mem_notdirty + * MemoryRegion. */ + trace_memory_region_tb_write(get_cpu_index(), addr, tmp, size); + } else if (TRACE_MEMORY_REGION_OPS_WRITE_ENABLED) { + hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr); + trace_memory_region_ops_write(get_cpu_index(), mr, abs_addr, tmp, size); + } return mr->ops->write_with_attrs(mr->opaque, addr, tmp, size, attrs); } @@ -856,17 +941,12 @@ static void memory_region_destructor_none(MemoryRegion *mr) static void memory_region_destructor_ram(MemoryRegion *mr) { - qemu_ram_free(mr->ram_addr); -} - -static void memory_region_destructor_ram_from_ptr(MemoryRegion *mr) -{ - qemu_ram_free_from_ptr(mr->ram_addr); + qemu_ram_free(mr->ram_block); } static void memory_region_destructor_rom_device(MemoryRegion *mr) { - qemu_ram_free(mr->ram_addr & TARGET_PAGE_MASK); + qemu_ram_free(mr->ram_block); } static bool memory_region_need_escape(char c) @@ -908,20 +988,23 @@ void memory_region_init(MemoryRegion *mr, const char *name, uint64_t size) { - if (!owner) { - owner = container_get(qdev_get_machine(), "/unattached"); - } - object_initialize(mr, sizeof(*mr), TYPE_MEMORY_REGION); mr->size = int128_make64(size); if (size == UINT64_MAX) { mr->size = int128_2_64(); } mr->name = g_strdup(name); + mr->owner = owner; + mr->ram_block = NULL; if (name) { char *escaped_name = memory_region_escape_name(name); char *name_array = g_strdup_printf("%s[*]", escaped_name); + + if (!owner) { + owner = container_get(qdev_get_machine(), "/unattached"); + } + object_property_add_child(owner, name_array, OBJECT(mr), &error_abort); object_unref(OBJECT(mr)); g_free(name_array); @@ -929,17 +1012,18 @@ void memory_region_init(MemoryRegion *mr, } } -static void memory_region_get_addr(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void memory_region_get_addr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { MemoryRegion *mr = MEMORY_REGION(obj); uint64_t value = mr->addr; - visit_type_uint64(v, &value, name, errp); + visit_type_uint64(v, name, &value, errp); } -static void memory_region_get_container(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void memory_region_get_container(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { MemoryRegion *mr = MEMORY_REGION(obj); gchar *path = (gchar *)""; @@ -947,7 +1031,7 @@ static void memory_region_get_container(Object *obj, Visitor *v, void *opaque, if (mr->container) { path = object_get_canonical_path(OBJECT(mr->container)); } - visit_type_str(v, &path, name, errp); + visit_type_str(v, name, &path, errp); if (mr->container) { g_free(path); } @@ -961,13 +1045,14 @@ static Object *memory_region_resolve_container(Object *obj, void *opaque, return OBJECT(mr->container); } -static void memory_region_get_priority(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void memory_region_get_priority(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { MemoryRegion *mr = MEMORY_REGION(obj); int32_t value = mr->priority; - visit_type_int32(v, &value, name, errp); + visit_type_int32(v, name, &value, errp); } static bool memory_region_get_may_overlap(Object *obj, Error **errp) @@ -977,13 +1062,13 @@ static bool memory_region_get_may_overlap(Object *obj, Error **errp) return mr->may_overlap; } -static void memory_region_get_size(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void memory_region_get_size(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { MemoryRegion *mr = MEMORY_REGION(obj); uint64_t value = memory_region_size(mr); - visit_type_uint64(v, &value, name, errp); + visit_type_uint64(v, name, &value, errp); } static void memory_region_initfn(Object *obj) @@ -992,7 +1077,6 @@ static void memory_region_initfn(Object *obj) ObjectProperty *op; mr->ops = &unassigned_mem_ops; - mr->ram_addr = RAM_ADDR_INVALID; mr->enabled = true; mr->romd_mode = true; mr->global_locking = true; @@ -1141,6 +1225,32 @@ MemTxResult memory_region_dispatch_read(MemoryRegion *mr, return r; } +/* Return true if an eventfd was signalled */ +static bool memory_region_dispatch_write_eventfds(MemoryRegion *mr, + hwaddr addr, + uint64_t data, + unsigned size, + MemTxAttrs attrs) +{ + MemoryRegionIoeventfd ioeventfd = { + .addr = addrrange_make(int128_make64(addr), int128_make64(size)), + .data = data, + }; + unsigned i; + + for (i = 0; i < mr->ioeventfd_nb; i++) { + ioeventfd.match_data = mr->ioeventfds[i].match_data; + ioeventfd.e = mr->ioeventfds[i].e; + + if (memory_region_ioeventfd_equal(ioeventfd, mr->ioeventfds[i])) { + event_notifier_set(ioeventfd.e); + return true; + } + } + + return false; +} + MemTxResult memory_region_dispatch_write(MemoryRegion *mr, hwaddr addr, uint64_t data, @@ -1154,6 +1264,11 @@ MemTxResult memory_region_dispatch_write(MemoryRegion *mr, adjust_endianness(mr, &data, size); + if ((!kvm_eventfds_enabled()) && + memory_region_dispatch_write_eventfds(mr, addr, data, size, attrs)) { + return MEMTX_OK; + } + if (mr->ops->write) { return access_with_adjusted_size(addr, &data, size, mr->ops->impl.min_access_size, @@ -1182,7 +1297,7 @@ void memory_region_init_io(MemoryRegion *mr, uint64_t size) { memory_region_init(mr, owner, name, size); - mr->ops = ops; + mr->ops = ops ? ops : &unassigned_mem_ops; mr->opaque = opaque; mr->terminates = true; } @@ -1197,7 +1312,7 @@ void memory_region_init_ram(MemoryRegion *mr, mr->ram = true; mr->terminates = true; mr->destructor = memory_region_destructor_ram; - mr->ram_addr = qemu_ram_alloc(size, mr, errp); + mr->ram_block = qemu_ram_alloc(size, mr, errp); mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; } @@ -1215,7 +1330,8 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr, mr->ram = true; mr->terminates = true; mr->destructor = memory_region_destructor_ram; - mr->ram_addr = qemu_ram_alloc_resizeable(size, max_size, resized, mr, errp); + mr->ram_block = qemu_ram_alloc_resizeable(size, max_size, resized, + mr, errp); mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; } @@ -1232,7 +1348,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, mr->ram = true; mr->terminates = true; mr->destructor = memory_region_destructor_ram; - mr->ram_addr = qemu_ram_alloc_from_file(size, mr, share, path, errp); + mr->ram_block = qemu_ram_alloc_from_file(size, mr, share, path, errp); mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; } #endif @@ -1246,12 +1362,12 @@ void memory_region_init_ram_ptr(MemoryRegion *mr, memory_region_init(mr, owner, name, size); mr->ram = true; mr->terminates = true; - mr->destructor = memory_region_destructor_ram_from_ptr; + mr->destructor = memory_region_destructor_ram; mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; /* qemu_ram_alloc_from_ptr cannot fail with ptr != NULL. */ assert(ptr != NULL); - mr->ram_addr = qemu_ram_alloc_from_ptr(size, ptr, mr, &error_abort); + mr->ram_block = qemu_ram_alloc_from_ptr(size, ptr, mr, &error_fatal); } void memory_region_set_skip_dump(MemoryRegion *mr) @@ -1285,7 +1401,7 @@ void memory_region_init_rom_device(MemoryRegion *mr, mr->terminates = true; mr->rom_device = true; mr->destructor = memory_region_destructor_rom_device; - mr->ram_addr = qemu_ram_alloc(size, mr, errp); + mr->ram_block = qemu_ram_alloc(size, mr, errp); } void memory_region_init_iommu(MemoryRegion *mr, @@ -1300,19 +1416,26 @@ void memory_region_init_iommu(MemoryRegion *mr, notifier_list_init(&mr->iommu_notify); } -void memory_region_init_reservation(MemoryRegion *mr, - Object *owner, - const char *name, - uint64_t size) -{ - memory_region_init_io(mr, owner, &unassigned_mem_ops, mr, name, size); -} - static void memory_region_finalize(Object *obj) { MemoryRegion *mr = MEMORY_REGION(obj); - assert(QTAILQ_EMPTY(&mr->subregions)); + assert(!mr->container); + + /* We know the region is not visible in any address space (it + * does not have a container and cannot be a root either because + * it has no references, so we can blindly clear mr->enabled. + * memory_region_set_enabled instead could trigger a transaction + * and cause an infinite loop. + */ + mr->enabled = false; + memory_region_transaction_begin(); + while (!QTAILQ_EMPTY(&mr->subregions)) { + MemoryRegion *subregion = QTAILQ_FIRST(&mr->subregions); + memory_region_del_subregion(mr, subregion); + } + memory_region_transaction_commit(); + mr->destructor(mr); memory_region_clear_coalescing(mr); g_free((char *)mr->name); @@ -1334,24 +1457,18 @@ void memory_region_ref(MemoryRegion *mr) * The memory region is a child of its owner. As long as the * owner doesn't call unparent itself on the memory region, * ref-ing the owner will also keep the memory region alive. - * Memory regions without an owner are supposed to never go away, - * but we still ref/unref them for debugging purposes. + * Memory regions without an owner are supposed to never go away; + * we do not ref/unref them because it slows down DMA sensibly. */ - Object *obj = OBJECT(mr); - if (obj && obj->parent) { - object_ref(obj->parent); - } else { - object_ref(obj); + if (mr && mr->owner) { + object_ref(mr->owner); } } void memory_region_unref(MemoryRegion *mr) { - Object *obj = OBJECT(mr); - if (obj && obj->parent) { - object_unref(obj->parent); - } else { - object_unref(obj); + if (mr && mr->owner) { + object_unref(mr->owner); } } @@ -1372,11 +1489,6 @@ const char *memory_region_name(const MemoryRegion *mr) return mr->name; } -bool memory_region_is_ram(MemoryRegion *mr) -{ - return mr->ram; -} - bool memory_region_is_skip_dump(MemoryRegion *mr) { return mr->skip_dump; @@ -1396,19 +1508,29 @@ bool memory_region_is_logging(MemoryRegion *mr, uint8_t client) return memory_region_get_dirty_log_mask(mr) & (1 << client); } -bool memory_region_is_rom(MemoryRegion *mr) +void memory_region_register_iommu_notifier(MemoryRegion *mr, Notifier *n) { - return mr->ram && mr->readonly; + notifier_list_add(&mr->iommu_notify, n); } -bool memory_region_is_iommu(MemoryRegion *mr) +void memory_region_iommu_replay(MemoryRegion *mr, Notifier *n, + hwaddr granularity, bool is_write) { - return mr->iommu_ops; -} + hwaddr addr; + IOMMUTLBEntry iotlb; -void memory_region_register_iommu_notifier(MemoryRegion *mr, Notifier *n) -{ - notifier_list_add(&mr->iommu_notify, n); + for (addr = 0; addr < memory_region_size(mr); addr += granularity) { + iotlb = mr->iommu_ops->translate(mr, addr, is_write); + if (iotlb.perm != IOMMU_NONE) { + n->notify(n, &iotlb); + } + + /* if (2^64 - MR size) < granularity, it's possible to get an + * infinite loop here. This should catch such a wraparound */ + if ((addr + granularity) < addr) { + break; + } + } } void memory_region_unregister_iommu_notifier(Notifier *n) @@ -1444,24 +1566,26 @@ void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client) bool memory_region_get_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size, unsigned client) { - assert(mr->ram_addr != RAM_ADDR_INVALID); - return cpu_physical_memory_get_dirty(mr->ram_addr + addr, size, client); + assert(mr->ram_block); + return cpu_physical_memory_get_dirty(memory_region_get_ram_addr(mr) + addr, + size, client); } void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size) { - assert(mr->ram_addr != RAM_ADDR_INVALID); - cpu_physical_memory_set_dirty_range(mr->ram_addr + addr, size, + assert(mr->ram_block); + cpu_physical_memory_set_dirty_range(memory_region_get_ram_addr(mr) + addr, + size, memory_region_get_dirty_log_mask(mr)); } bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size, unsigned client) { - assert(mr->ram_addr != RAM_ADDR_INVALID); - return cpu_physical_memory_test_and_clear_dirty(mr->ram_addr + addr, - size, client); + assert(mr->ram_block); + return cpu_physical_memory_test_and_clear_dirty( + memory_region_get_ram_addr(mr) + addr, size, client); } @@ -1504,9 +1628,9 @@ void memory_region_rom_device_set_romd(MemoryRegion *mr, bool romd_mode) void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size, unsigned client) { - assert(mr->ram_addr != RAM_ADDR_INVALID); - cpu_physical_memory_test_and_clear_dirty(mr->ram_addr + addr, size, - client); + assert(mr->ram_block); + cpu_physical_memory_test_and_clear_dirty( + memory_region_get_ram_addr(mr) + addr, size, client); } int memory_region_get_fd(MemoryRegion *mr) @@ -1515,27 +1639,39 @@ int memory_region_get_fd(MemoryRegion *mr) return memory_region_get_fd(mr->alias); } - assert(mr->ram_addr != RAM_ADDR_INVALID); + assert(mr->ram_block); - return qemu_get_ram_fd(mr->ram_addr & TARGET_PAGE_MASK); + return qemu_get_ram_fd(memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK); } void *memory_region_get_ram_ptr(MemoryRegion *mr) { - if (mr->alias) { - return memory_region_get_ram_ptr(mr->alias) + mr->alias_offset; + void *ptr; + uint64_t offset = 0; + + rcu_read_lock(); + while (mr->alias) { + offset += mr->alias_offset; + mr = mr->alias; } + assert(mr->ram_block); + ptr = qemu_get_ram_ptr(mr->ram_block, + memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK); + rcu_read_unlock(); - assert(mr->ram_addr != RAM_ADDR_INVALID); + return ptr + offset; +} - return qemu_get_ram_ptr(mr->ram_addr & TARGET_PAGE_MASK); +ram_addr_t memory_region_get_ram_addr(MemoryRegion *mr) +{ + return mr->ram_block ? mr->ram_block->offset : RAM_ADDR_INVALID; } void memory_region_ram_resize(MemoryRegion *mr, ram_addr_t newsize, Error **errp) { - assert(mr->ram_addr != RAM_ADDR_INVALID); + assert(mr->ram_block); - qemu_ram_resize(mr->ram_addr, newsize, errp); + qemu_ram_resize(memory_region_get_ram_addr(mr), newsize, errp); } static void memory_region_update_coalesced_range_as(MemoryRegion *mr, AddressSpace *as) @@ -1645,6 +1781,8 @@ void memory_region_clear_global_locking(MemoryRegion *mr) mr->global_locking = false; } +static bool userspace_eventfd_warning; + void memory_region_add_eventfd(MemoryRegion *mr, hwaddr addr, unsigned size, @@ -1661,7 +1799,16 @@ void memory_region_add_eventfd(MemoryRegion *mr, }; unsigned i; - adjust_endianness(mr, &mrfd.data, size); + if (kvm_enabled() && (!(kvm_eventfds_enabled() || + userspace_eventfd_warning))) { + userspace_eventfd_warning = true; + error_report("Using eventfd without MMIO binding in KVM. " + "Suboptimal performance expected"); + } + + if (size) { + adjust_endianness(mr, &mrfd.data, size); + } memory_region_transaction_begin(); for (i = 0; i < mr->ioeventfd_nb; ++i) { if (memory_region_ioeventfd_before(mrfd, mr->ioeventfds[i])) { @@ -1694,7 +1841,9 @@ void memory_region_del_eventfd(MemoryRegion *mr, }; unsigned i; - adjust_endianness(mr, &mrfd.data, size); + if (size) { + adjust_endianness(mr, &mrfd.data, size); + } memory_region_transaction_begin(); for (i = 0; i < mr->ioeventfd_nb; ++i) { if (memory_region_ioeventfd_equal(mrfd, mr->ioeventfds[i])) { @@ -1858,11 +2007,6 @@ void memory_region_set_alias_offset(MemoryRegion *mr, hwaddr offset) memory_region_transaction_commit(); } -ram_addr_t memory_region_get_ram_addr(MemoryRegion *mr) -{ - return mr->ram_addr; -} - uint64_t memory_region_get_alignment(const MemoryRegion *mr) { return mr->align; @@ -2009,6 +2153,9 @@ static void listener_add_address_space(MemoryListener *listener, return; } + if (listener->begin) { + listener->begin(listener); + } if (global_dirty_log) { if (listener->log_global_start) { listener->log_global_start(listener); @@ -2025,10 +2172,16 @@ static void listener_add_address_space(MemoryListener *listener, .offset_within_address_space = int128_get64(fr->addr.start), .readonly = fr->readonly, }; + if (fr->dirty_log_mask && listener->log_start) { + listener->log_start(listener, §ion, 0, fr->dirty_log_mask); + } if (listener->region_add) { listener->region_add(listener, §ion); } } + if (listener->commit) { + listener->commit(listener); + } flatview_unref(view); } @@ -2065,7 +2218,9 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name) { memory_region_ref(root); memory_region_transaction_begin(); + as->ref_count = 1; as->root = root; + as->malloced = false; as->current_map = g_new(FlatView, 1); flatview_init(as->current_map); as->ioeventfd_nb = 0; @@ -2080,6 +2235,7 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name) static void do_address_space_destroy(AddressSpace *as) { MemoryListener *listener; + bool do_free = as->malloced; address_space_destroy_dispatch(as); @@ -2091,12 +2247,36 @@ static void do_address_space_destroy(AddressSpace *as) g_free(as->name); g_free(as->ioeventfds); memory_region_unref(as->root); + if (do_free) { + g_free(as); + } +} + +AddressSpace *address_space_init_shareable(MemoryRegion *root, const char *name) +{ + AddressSpace *as; + + QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) { + if (root == as->root && as->malloced) { + as->ref_count++; + return as; + } + } + + as = g_malloc0(sizeof *as); + address_space_init(as, root, name); + as->malloced = true; + return as; } void address_space_destroy(AddressSpace *as) { MemoryRegion *root = as->root; + as->ref_count--; + if (as->ref_count) { + return; + } /* Flush out anything from MemoryListeners listening in on this */ memory_region_transaction_begin(); as->root = NULL; diff --git a/qemu/memory_mapping.c b/qemu/memory_mapping.c index 36d6b2604..2354b2b7f 100644 --- a/qemu/memory_mapping.c +++ b/qemu/memory_mapping.c @@ -11,6 +11,8 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include #include "qemu-common.h" @@ -177,6 +179,7 @@ void guest_phys_blocks_free(GuestPhysBlockList *list) QTAILQ_FOREACH_SAFE(p, &list->head, next, q) { QTAILQ_REMOVE(&list->head, p, next); + memory_region_unref(p->mr); g_free(p); } list->num = 0; @@ -240,6 +243,8 @@ static void guest_phys_blocks_region_add(MemoryListener *listener, block->target_start = target_start; block->target_end = target_end; block->host_addr = host_addr; + block->mr = section->mr; + memory_region_ref(section->mr); QTAILQ_INSERT_TAIL(&g->list->head, block, next); ++g->list->num; diff --git a/qemu/migration/Makefile.objs b/qemu/migration/Makefile.objs index d929e969a..0cac6d707 100644 --- a/qemu/migration/Makefile.objs +++ b/qemu/migration/Makefile.objs @@ -1,7 +1,7 @@ common-obj-y += migration.o tcp.o common-obj-y += vmstate.o common-obj-y += qemu-file.o qemu-file-buf.o qemu-file-unix.o qemu-file-stdio.o -common-obj-y += xbzrle.o +common-obj-y += xbzrle.o postcopy-ram.o common-obj-$(CONFIG_RDMA) += rdma.o common-obj-$(CONFIG_POSIX) += exec.o unix.o fd.o diff --git a/qemu/migration/block.c b/qemu/migration/block.c index 85496fd80..174331728 100644 --- a/qemu/migration/block.c +++ b/qemu/migration/block.c @@ -13,18 +13,20 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "block/block.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "hw/hw.h" +#include "qemu/cutils.h" #include "qemu/queue.h" #include "qemu/timer.h" #include "migration/block.h" #include "migration/migration.h" #include "sysemu/blockdev.h" #include "sysemu/block-backend.h" -#include #define BLOCK_SIZE (1 << 20) #define BDRV_SECTORS_PER_DIRTY_CHUNK (BLOCK_SIZE >> BDRV_SECTOR_BITS) @@ -36,6 +38,8 @@ #define MAX_IS_ALLOCATED_SEARCH 65536 +#define MAX_INFLIGHT_IO 512 + //#define DEBUG_BLK_MIGRATION #ifdef DEBUG_BLK_MIGRATION @@ -52,17 +56,25 @@ typedef struct BlkMigDevState { int shared_base; int64_t total_sectors; QSIMPLEQ_ENTRY(BlkMigDevState) entry; + Error *blocker; /* Only used by migration thread. Does not need a lock. */ int bulk_completed; int64_t cur_sector; int64_t cur_dirty; - /* Protected by block migration lock. */ + /* Data in the aio_bitmap is protected by block migration lock. + * Allocation and free happen during setup and cleanup respectively. + */ unsigned long *aio_bitmap; + + /* Protected by block migration lock. */ int64_t completed_sectors; + + /* During migration this is protected by iothread lock / AioContext. + * Allocation and free happen during setup and cleanup respectively. + */ BdrvDirtyBitmap *dirty_bitmap; - Error *blocker; } BlkMigDevState; typedef struct BlkMigBlock { @@ -98,7 +110,7 @@ typedef struct BlkMigState { int prev_progress; int bulk_completed; - /* Lock must be taken _inside_ the iothread lock. */ + /* Lock must be taken _inside_ the iothread lock and any AioContexts. */ QemuMutex lock; } BlkMigState; @@ -262,11 +274,13 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) if (bmds->shared_base) { qemu_mutex_lock_iothread(); + aio_context_acquire(bdrv_get_aio_context(bs)); while (cur_sector < total_sectors && !bdrv_is_allocated(bs, cur_sector, MAX_IS_ALLOCATED_SEARCH, &nr_sectors)) { cur_sector += nr_sectors; } + aio_context_release(bdrv_get_aio_context(bs)); qemu_mutex_unlock_iothread(); } @@ -300,11 +314,21 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) block_mig_state.submitted++; blk_mig_unlock(); + /* We do not know if bs is under the main thread (and thus does + * not acquire the AioContext when doing AIO) or rather under + * dataplane. Thus acquire both the iothread mutex and the + * AioContext. + * + * This is ugly and will disappear when we make bdrv_* thread-safe, + * without the need to acquire the AioContext. + */ qemu_mutex_lock_iothread(); + aio_context_acquire(bdrv_get_aio_context(bmds->bs)); blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov, nr_sectors, blk_mig_read_cb, blk); bdrv_reset_dirty_bitmap(bmds->dirty_bitmap, cur_sector, nr_sectors); + aio_context_release(bdrv_get_aio_context(bmds->bs)); qemu_mutex_unlock_iothread(); bmds->cur_sector = cur_sector + nr_sectors; @@ -319,8 +343,10 @@ static int set_dirty_tracking(void) int ret; QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { + aio_context_acquire(bdrv_get_aio_context(bmds->bs)); bmds->dirty_bitmap = bdrv_create_dirty_bitmap(bmds->bs, BLOCK_SIZE, NULL, NULL); + aio_context_release(bdrv_get_aio_context(bmds->bs)); if (!bmds->dirty_bitmap) { ret = -errno; goto fail; @@ -331,18 +357,24 @@ static int set_dirty_tracking(void) fail: QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { if (bmds->dirty_bitmap) { + aio_context_acquire(bdrv_get_aio_context(bmds->bs)); bdrv_release_dirty_bitmap(bmds->bs, bmds->dirty_bitmap); + aio_context_release(bdrv_get_aio_context(bmds->bs)); } } return ret; } +/* Called with iothread lock taken. */ + static void unset_dirty_tracking(void) { BlkMigDevState *bmds; QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { + aio_context_acquire(bdrv_get_aio_context(bmds->bs)); bdrv_release_dirty_bitmap(bmds->bs, bmds->dirty_bitmap); + aio_context_release(bdrv_get_aio_context(bmds->bs)); } } @@ -442,7 +474,7 @@ static void blk_mig_reset_dirty_cursor(void) } } -/* Called with iothread lock taken. */ +/* Called with iothread lock and AioContext taken. */ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds, int is_async) @@ -525,7 +557,9 @@ static int blk_mig_save_dirty_block(QEMUFile *f, int is_async) int ret = 1; QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { + aio_context_acquire(bdrv_get_aio_context(bmds->bs)); ret = mig_save_device_dirty(f, bmds, is_async); + aio_context_release(bdrv_get_aio_context(bmds->bs)); if (ret <= 0) { break; } @@ -583,7 +617,9 @@ static int64_t get_remaining_dirty(void) int64_t dirty = 0; QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { + aio_context_acquire(bdrv_get_aio_context(bmds->bs)); dirty += bdrv_get_dirty_count(bmds->dirty_bitmap); + aio_context_release(bdrv_get_aio_context(bmds->bs)); } return dirty << BDRV_SECTOR_BITS; @@ -591,25 +627,32 @@ static int64_t get_remaining_dirty(void) /* Called with iothread lock taken. */ -static void blk_mig_cleanup(void) +static void block_migration_cleanup(void *opaque) { BlkMigDevState *bmds; BlkMigBlock *blk; + AioContext *ctx; bdrv_drain_all(); unset_dirty_tracking(); - blk_mig_lock(); while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) { QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry); bdrv_op_unblock_all(bmds->bs, bmds->blocker); error_free(bmds->blocker); + + /* Save ctx, because bmds->bs can disappear during bdrv_unref. */ + ctx = bdrv_get_aio_context(bmds->bs); + aio_context_acquire(ctx); bdrv_unref(bmds->bs); + aio_context_release(ctx); + g_free(bmds->aio_bitmap); g_free(bmds); } + blk_mig_lock(); while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) { QSIMPLEQ_REMOVE_HEAD(&block_mig_state.blk_list, entry); g_free(blk->buf); @@ -618,11 +661,6 @@ static void blk_mig_cleanup(void) blk_mig_unlock(); } -static void block_migration_cancel(void *opaque) -{ - blk_mig_cleanup(); -} - static int block_save_setup(QEMUFile *f, void *opaque) { int ret; @@ -636,13 +674,12 @@ static int block_save_setup(QEMUFile *f, void *opaque) /* start track dirty blocks */ ret = set_dirty_tracking(); + qemu_mutex_unlock_iothread(); + if (ret) { - qemu_mutex_unlock_iothread(); return ret; } - qemu_mutex_unlock_iothread(); - ret = flush_blks(f); blk_mig_reset_dirty_cursor(); qemu_put_be64(f, BLK_MIG_FLAG_EOS); @@ -670,7 +707,10 @@ static int block_save_iterate(QEMUFile *f, void *opaque) blk_mig_lock(); while ((block_mig_state.submitted + block_mig_state.read_done) * BLOCK_SIZE < - qemu_file_get_rate_limit(f)) { + qemu_file_get_rate_limit(f) && + (block_mig_state.submitted + + block_mig_state.read_done) < + MAX_INFLIGHT_IO) { blk_mig_unlock(); if (block_mig_state.bulk_completed == 0) { /* first finish the bulk phase */ @@ -753,26 +793,30 @@ static int block_save_complete(QEMUFile *f, void *opaque) return 0; } -static uint64_t block_save_pending(QEMUFile *f, void *opaque, uint64_t max_size) +static void block_save_pending(QEMUFile *f, void *opaque, uint64_t max_size, + uint64_t *non_postcopiable_pending, + uint64_t *postcopiable_pending) { /* Estimate pending number of bytes to send */ uint64_t pending; qemu_mutex_lock_iothread(); + pending = get_remaining_dirty(); + qemu_mutex_unlock_iothread(); + blk_mig_lock(); - pending = get_remaining_dirty() + - block_mig_state.submitted * BLOCK_SIZE + - block_mig_state.read_done * BLOCK_SIZE; + pending += block_mig_state.submitted * BLOCK_SIZE + + block_mig_state.read_done * BLOCK_SIZE; + blk_mig_unlock(); /* Report at least one block pending during bulk phase */ if (pending <= max_size && !block_mig_state.bulk_completed) { pending = max_size + BLOCK_SIZE; } - blk_mig_unlock(); - qemu_mutex_unlock_iothread(); DPRINTF("Enter save live pending %" PRIu64 "\n", pending); - return pending; + /* We don't do postcopy */ + *non_postcopiable_pending += pending; } static int block_load(QEMUFile *f, void *opaque, int version_id) @@ -783,6 +827,7 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) int64_t addr; BlockDriverState *bs, *bs_prev = NULL; BlockBackend *blk; + Error *local_err = NULL; uint8_t *buf; int64_t total_sectors = 0; int nr_sectors; @@ -807,6 +852,11 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) return -EINVAL; } bs = blk_bs(blk); + if (!bs) { + fprintf(stderr, "Block device %s has no medium\n", + device_name); + return -EINVAL; + } if (bs != bs_prev) { bs_prev = bs; @@ -816,6 +866,12 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) device_name); return -EINVAL; } + + bdrv_invalidate_cache(bs, &local_err); + if (local_err) { + error_report_err(local_err); + return -EINVAL; + } } if (total_sectors - addr < BDRV_SECTORS_PER_DIRTY_CHUNK) { @@ -876,10 +932,10 @@ static SaveVMHandlers savevm_block_handlers = { .set_params = block_set_params, .save_live_setup = block_save_setup, .save_live_iterate = block_save_iterate, - .save_live_complete = block_save_complete, + .save_live_complete_precopy = block_save_complete, .save_live_pending = block_save_pending, .load_state = block_load, - .cancel = block_migration_cancel, + .cleanup = block_migration_cleanup, .is_active = block_is_active, }; diff --git a/qemu/migration/exec.c b/qemu/migration/exec.c index 8406d2bbd..559420969 100644 --- a/qemu/migration/exec.c +++ b/qemu/migration/exec.c @@ -15,13 +15,14 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/sockets.h" #include "qemu/main-loop.h" #include "migration/migration.h" #include "migration/qemu-file.h" #include "block/block.h" -#include #include //#define DEBUG_MIGRATION_EXEC @@ -36,8 +37,8 @@ void exec_start_outgoing_migration(MigrationState *s, const char *command, Error **errp) { - s->file = qemu_popen_cmd(command, "w"); - if (s->file == NULL) { + s->to_dst_file = qemu_popen_cmd(command, "w"); + if (s->to_dst_file == NULL) { error_setg_errno(errp, errno, "failed to popen the migration target"); return; } diff --git a/qemu/migration/fd.c b/qemu/migration/fd.c index 3e4bed0e0..3d788bb29 100644 --- a/qemu/migration/fd.c +++ b/qemu/migration/fd.c @@ -13,6 +13,8 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/main-loop.h" #include "qemu/sockets.h" @@ -50,9 +52,9 @@ void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error ** } if (fd_is_socket(fd)) { - s->file = qemu_fopen_socket(fd, "wb"); + s->to_dst_file = qemu_fopen_socket(fd, "wb"); } else { - s->file = qemu_fdopen(fd, "wb"); + s->to_dst_file = qemu_fdopen(fd, "wb"); } migrate_fd_connect(s); diff --git a/qemu/migration/migration.c b/qemu/migration/migration.c index 4ddb9adce..991313a86 100644 --- a/qemu/migration/migration.c +++ b/qemu/migration/migration.c @@ -13,7 +13,8 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "migration/migration.h" @@ -21,16 +22,20 @@ #include "sysemu/sysemu.h" #include "block/block.h" #include "qapi/qmp/qerror.h" +#include "qapi/util.h" #include "qemu/sockets.h" #include "qemu/rcu.h" #include "migration/block.h" +#include "migration/postcopy-ram.h" #include "qemu/thread.h" #include "qmp-commands.h" #include "trace.h" -#include "qapi/util.h" #include "qapi-event.h" +#include "qom/cpu.h" +#include "exec/memory.h" +#include "exec/address-spaces.h" -#define MAX_THROTTLE (32 << 20) /* Migration speed throttling */ +#define MAX_THROTTLE (32 << 20) /* Migration transfer speed throttling */ /* Amount of time to allocate to each "chunk" of bandwidth-throttled * data. */ @@ -44,6 +49,9 @@ #define DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT 2 /*0: means nocompress, 1: best speed, ... 9: best compress ratio */ #define DEFAULT_MIGRATE_COMPRESS_LEVEL 1 +/* Define default autoconverge cpu throttle migration parameters */ +#define DEFAULT_MIGRATE_X_CPU_THROTTLE_INITIAL 20 +#define DEFAULT_MIGRATE_X_CPU_THROTTLE_INCREMENT 10 /* Migration XBZRLE default cache size */ #define DEFAULT_MIGRATE_CACHE_SIZE (64 * 1024 * 1024) @@ -53,6 +61,13 @@ static NotifierList migration_state_notifiers = static bool deferred_incoming; +/* + * Current state of incoming postcopy; note this is not part of + * MigrationIncomingState since it's state is used during cleanup + * at the end as MIS is being freed. + */ +static PostcopyState incoming_postcopy_state; + /* When we add fault tolerance, we could have several migrations at once. For now we don't need to add dynamic creation of migration */ @@ -60,6 +75,7 @@ static bool deferred_incoming; /* For outgoing */ MigrationState *migrate_get_current(void) { + static bool once; static MigrationState current_migration = { .state = MIGRATION_STATUS_NONE, .bandwidth_limit = MAX_THROTTLE, @@ -71,8 +87,16 @@ MigrationState *migrate_get_current(void) DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT, .parameters[MIGRATION_PARAMETER_DECOMPRESS_THREADS] = DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT, + .parameters[MIGRATION_PARAMETER_X_CPU_THROTTLE_INITIAL] = + DEFAULT_MIGRATE_X_CPU_THROTTLE_INITIAL, + .parameters[MIGRATION_PARAMETER_X_CPU_THROTTLE_INCREMENT] = + DEFAULT_MIGRATE_X_CPU_THROTTLE_INCREMENT, }; + if (!once) { + qemu_mutex_init(¤t_migration.src_page_req_mutex); + once = true; + } return ¤t_migration; } @@ -86,15 +110,19 @@ MigrationIncomingState *migration_incoming_get_current(void) MigrationIncomingState *migration_incoming_state_new(QEMUFile* f) { - mis_current = g_malloc0(sizeof(MigrationIncomingState)); - mis_current->file = f; + mis_current = g_new0(MigrationIncomingState, 1); + mis_current->from_src_file = f; + mis_current->state = MIGRATION_STATUS_NONE; QLIST_INIT(&mis_current->loadvm_handlers); + qemu_mutex_init(&mis_current->rp_mutex); + qemu_event_init(&mis_current->main_thread_load_event, false); return mis_current; } void migration_incoming_state_destroy(void) { + qemu_event_destroy(&mis_current->main_thread_load_event); loadvm_free_handlers(mis_current); g_free(mis_current); mis_current = NULL; @@ -176,7 +204,7 @@ static int global_state_post_load(void *opaque, int version_id) s->received = true; trace_migrate_global_state_post_load(runstate); - r = qapi_enum_parse(RunState_lookup, runstate, RUN_STATE_MAX, + r = qapi_enum_parse(RunState_lookup, runstate, RUN_STATE__MAX, -1, &local_err); if (r == -1) { @@ -240,6 +268,35 @@ static void deferred_incoming_migration(Error **errp) deferred_incoming = true; } +/* Request a range of pages from the source VM at the given + * start address. + * rbname: Name of the RAMBlock to request the page in, if NULL it's the same + * as the last request (a name must have been given previously) + * Start: Address offset within the RB + * Len: Length in bytes required - must be a multiple of pagesize + */ +void migrate_send_rp_req_pages(MigrationIncomingState *mis, const char *rbname, + ram_addr_t start, size_t len) +{ + uint8_t bufc[12 + 1 + 255]; /* start (8), len (4), rbname upto 256 */ + size_t msglen = 12; /* start + len */ + + *(uint64_t *)bufc = cpu_to_be64((uint64_t)start); + *(uint32_t *)(bufc + 8) = cpu_to_be32((uint32_t)len); + + if (rbname) { + int rbname_len = strlen(rbname); + assert(rbname_len < 256); + + bufc[msglen++] = rbname_len; + memcpy(bufc + msglen, rbname, rbname_len); + msglen += rbname_len; + migrate_send_rp_message(mis, MIG_RP_MSG_REQ_PAGES_ID, msglen, bufc); + } else { + migrate_send_rp_message(mis, MIG_RP_MSG_REQ_PAGES, msglen, bufc); + } +} + void qemu_start_incoming_migration(const char *uri, Error **errp) { const char *p; @@ -266,37 +323,27 @@ void qemu_start_incoming_migration(const char *uri, Error **errp) } } -static void process_incoming_migration_co(void *opaque) +static void process_incoming_migration_bh(void *opaque) { - QEMUFile *f = opaque; Error *local_err = NULL; - int ret; - - migration_incoming_state_new(f); - migrate_generate_event(MIGRATION_STATUS_ACTIVE); - ret = qemu_loadvm_state(f); - - qemu_fclose(f); - free_xbzrle_decoded_buf(); - migration_incoming_state_destroy(); - - if (ret < 0) { - migrate_generate_event(MIGRATION_STATUS_FAILED); - error_report("load of migration failed: %s", strerror(-ret)); - migrate_decompress_threads_join(); - exit(EXIT_FAILURE); - } - migrate_generate_event(MIGRATION_STATUS_COMPLETED); - qemu_announce_self(); + MigrationIncomingState *mis = opaque; /* Make sure all file formats flush their mutable metadata */ bdrv_invalidate_cache_all(&local_err); if (local_err) { + migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, + MIGRATION_STATUS_FAILED); error_report_err(local_err); migrate_decompress_threads_join(); exit(EXIT_FAILURE); } + /* + * This must happen after all error conditions are dealt with and + * we're sure the VM is going to be running on this host. + */ + qemu_announce_self(); + /* If global state section was not received or we are in running state, we need to obey autostart. Any other state is set with runstate_set. */ @@ -312,6 +359,64 @@ static void process_incoming_migration_co(void *opaque) runstate_set(global_state_get_runstate()); } migrate_decompress_threads_join(); + /* + * This must happen after any state changes since as soon as an external + * observer sees this event they might start to prod at the VM assuming + * it's ready to use. + */ + migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, + MIGRATION_STATUS_COMPLETED); + qemu_bh_delete(mis->bh); + migration_incoming_state_destroy(); +} + +static void process_incoming_migration_co(void *opaque) +{ + QEMUFile *f = opaque; + MigrationIncomingState *mis; + PostcopyState ps; + int ret; + + mis = migration_incoming_state_new(f); + postcopy_state_set(POSTCOPY_INCOMING_NONE); + migrate_set_state(&mis->state, MIGRATION_STATUS_NONE, + MIGRATION_STATUS_ACTIVE); + ret = qemu_loadvm_state(f); + + ps = postcopy_state_get(); + trace_process_incoming_migration_co_end(ret, ps); + if (ps != POSTCOPY_INCOMING_NONE) { + if (ps == POSTCOPY_INCOMING_ADVISE) { + /* + * Where a migration had postcopy enabled (and thus went to advise) + * but managed to complete within the precopy period, we can use + * the normal exit. + */ + postcopy_ram_incoming_cleanup(mis); + } else if (ret >= 0) { + /* + * Postcopy was started, cleanup should happen at the end of the + * postcopy thread. + */ + trace_process_incoming_migration_co_postcopy_end_main(); + return; + } + /* Else if something went wrong then just fall out of the normal exit */ + } + + qemu_fclose(f); + free_xbzrle_decoded_buf(); + + if (ret < 0) { + migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, + MIGRATION_STATUS_FAILED); + error_report("load of migration failed: %s", strerror(-ret)); + migrate_decompress_threads_join(); + exit(EXIT_FAILURE); + } + + mis->bh = qemu_bh_new(process_incoming_migration_bh, mis); + qemu_bh_schedule(mis->bh); } void process_incoming_migration(QEMUFile *f) @@ -325,6 +430,50 @@ void process_incoming_migration(QEMUFile *f) qemu_coroutine_enter(co, f); } +/* + * Send a message on the return channel back to the source + * of the migration. + */ +void migrate_send_rp_message(MigrationIncomingState *mis, + enum mig_rp_message_type message_type, + uint16_t len, void *data) +{ + trace_migrate_send_rp_message((int)message_type, len); + qemu_mutex_lock(&mis->rp_mutex); + qemu_put_be16(mis->to_src_file, (unsigned int)message_type); + qemu_put_be16(mis->to_src_file, len); + qemu_put_buffer(mis->to_src_file, data, len); + qemu_fflush(mis->to_src_file); + qemu_mutex_unlock(&mis->rp_mutex); +} + +/* + * Send a 'SHUT' message on the return channel with the given value + * to indicate that we've finished with the RP. Non-0 value indicates + * error. + */ +void migrate_send_rp_shut(MigrationIncomingState *mis, + uint32_t value) +{ + uint32_t buf; + + buf = cpu_to_be32(value); + migrate_send_rp_message(mis, MIG_RP_MSG_SHUT, sizeof(buf), &buf); +} + +/* + * Send a 'PONG' message on the return channel with the given value + * (normally in response to a 'PING') + */ +void migrate_send_rp_pong(MigrationIncomingState *mis, + uint32_t value) +{ + uint32_t buf; + + buf = cpu_to_be32(value); + migrate_send_rp_message(mis, MIG_RP_MSG_PONG, sizeof(buf), &buf); +} + /* amount of nanoseconds we are willing to wait for migration to be down. * the choice of nanoseconds is because it is the maximum resolution that * get_clock() can achieve. It is an internal measure. All user-visible @@ -344,7 +493,7 @@ MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp) int i; caps = NULL; /* silence compiler warning */ - for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) { + for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { if (head == NULL) { head = g_malloc0(sizeof(*caps)); caps = head; @@ -372,10 +521,32 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) s->parameters[MIGRATION_PARAMETER_COMPRESS_THREADS]; params->decompress_threads = s->parameters[MIGRATION_PARAMETER_DECOMPRESS_THREADS]; + params->x_cpu_throttle_initial = + s->parameters[MIGRATION_PARAMETER_X_CPU_THROTTLE_INITIAL]; + params->x_cpu_throttle_increment = + s->parameters[MIGRATION_PARAMETER_X_CPU_THROTTLE_INCREMENT]; return params; } +/* + * Return true if we're already in the middle of a migration + * (i.e. any of the active or setup states) + */ +static bool migration_is_setup_or_active(int state) +{ + switch (state) { + case MIGRATION_STATUS_ACTIVE: + case MIGRATION_STATUS_POSTCOPY_ACTIVE: + case MIGRATION_STATUS_SETUP: + return true; + + default: + return false; + + } +} + static void get_xbzrle_cache_stats(MigrationInfo *info) { if (migrate_use_xbzrle()) { @@ -435,6 +606,45 @@ MigrationInfo *qmp_query_migrate(Error **errp) info->disk->total = blk_mig_bytes_total(); } + if (cpu_throttle_active()) { + info->has_x_cpu_throttle_percentage = true; + info->x_cpu_throttle_percentage = cpu_throttle_get_percentage(); + } + + get_xbzrle_cache_stats(info); + break; + case MIGRATION_STATUS_POSTCOPY_ACTIVE: + /* Mostly the same as active; TODO add some postcopy stats */ + info->has_status = true; + info->has_total_time = true; + info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + - s->total_time; + info->has_expected_downtime = true; + info->expected_downtime = s->expected_downtime; + info->has_setup_time = true; + info->setup_time = s->setup_time; + + info->has_ram = true; + info->ram = g_malloc0(sizeof(*info->ram)); + info->ram->transferred = ram_bytes_transferred(); + info->ram->remaining = ram_bytes_remaining(); + info->ram->total = ram_bytes_total(); + info->ram->duplicate = dup_mig_pages_transferred(); + info->ram->skipped = skipped_mig_pages_transferred(); + info->ram->normal = norm_mig_pages_transferred(); + info->ram->normal_bytes = norm_mig_bytes_transferred(); + info->ram->dirty_pages_rate = s->dirty_pages_rate; + info->ram->mbps = s->mbps; + info->ram->dirty_sync_count = s->dirty_sync_count; + + if (blk_mig_active()) { + info->has_disk = true; + info->disk = g_malloc0(sizeof(*info->disk)); + info->disk->transferred = blk_mig_bytes_transferred(); + info->disk->remaining = blk_mig_bytes_remaining(); + info->disk->total = blk_mig_bytes_total(); + } + get_xbzrle_cache_stats(info); break; case MIGRATION_STATUS_COMPLETED: @@ -478,8 +688,7 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, MigrationState *s = migrate_get_current(); MigrationCapabilityStatusList *cap; - if (s->state == MIGRATION_STATUS_ACTIVE || - s->state == MIGRATION_STATUS_SETUP) { + if (migration_is_setup_or_active(s->state)) { error_setg(errp, QERR_MIGRATION_ACTIVE); return; } @@ -487,6 +696,20 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, for (cap = params; cap; cap = cap->next) { s->enabled_capabilities[cap->value->capability] = cap->value->state; } + + if (migrate_postcopy_ram()) { + if (migrate_use_compression()) { + /* The decompression threads asynchronously write into RAM + * rather than use the atomic copies needed to avoid + * userfaulting. It should be possible to fix the decompression + * threads for compatibility in future. + */ + error_report("Postcopy is not currently compatible with " + "compression"); + s->enabled_capabilities[MIGRATION_CAPABILITY_POSTCOPY_RAM] = + false; + } + } } void qmp_migrate_set_parameters(bool has_compress_level, @@ -494,7 +717,11 @@ void qmp_migrate_set_parameters(bool has_compress_level, bool has_compress_threads, int64_t compress_threads, bool has_decompress_threads, - int64_t decompress_threads, Error **errp) + int64_t decompress_threads, + bool has_x_cpu_throttle_initial, + int64_t x_cpu_throttle_initial, + bool has_x_cpu_throttle_increment, + int64_t x_cpu_throttle_increment, Error **errp) { MigrationState *s = migrate_get_current(); @@ -517,6 +744,18 @@ void qmp_migrate_set_parameters(bool has_compress_level, "is invalid, it should be in the range of 1 to 255"); return; } + if (has_x_cpu_throttle_initial && + (x_cpu_throttle_initial < 1 || x_cpu_throttle_initial > 99)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "x_cpu_throttle_initial", + "an integer in the range of 1 to 99"); + } + if (has_x_cpu_throttle_increment && + (x_cpu_throttle_increment < 1 || x_cpu_throttle_increment > 99)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "x_cpu_throttle_increment", + "an integer in the range of 1 to 99"); + } if (has_compress_level) { s->parameters[MIGRATION_PARAMETER_COMPRESS_LEVEL] = compress_level; @@ -528,13 +767,44 @@ void qmp_migrate_set_parameters(bool has_compress_level, s->parameters[MIGRATION_PARAMETER_DECOMPRESS_THREADS] = decompress_threads; } + if (has_x_cpu_throttle_initial) { + s->parameters[MIGRATION_PARAMETER_X_CPU_THROTTLE_INITIAL] = + x_cpu_throttle_initial; + } + + if (has_x_cpu_throttle_increment) { + s->parameters[MIGRATION_PARAMETER_X_CPU_THROTTLE_INCREMENT] = + x_cpu_throttle_increment; + } +} + +void qmp_migrate_start_postcopy(Error **errp) +{ + MigrationState *s = migrate_get_current(); + + if (!migrate_postcopy_ram()) { + error_setg(errp, "Enable postcopy with migrate_set_capability before" + " the start of migration"); + return; + } + + if (s->state == MIGRATION_STATUS_NONE) { + error_setg(errp, "Postcopy must be started after migration has been" + " started"); + return; + } + /* + * we don't error if migration has finished since that would be racy + * with issuing this command. + */ + atomic_set(&s->start_postcopy, true); } /* shared migration helpers */ -static void migrate_set_state(MigrationState *s, int old_state, int new_state) +void migrate_set_state(int *state, int old_state, int new_state) { - if (atomic_cmpxchg(&s->state, old_state, new_state) == old_state) { + if (atomic_cmpxchg(state, old_state, new_state) == old_state) { trace_migrate_set_state(new_state); migrate_generate_event(new_state); } @@ -547,21 +817,27 @@ static void migrate_fd_cleanup(void *opaque) qemu_bh_delete(s->cleanup_bh); s->cleanup_bh = NULL; - if (s->file) { + flush_page_queue(s); + + if (s->to_dst_file) { trace_migrate_fd_cleanup(); qemu_mutex_unlock_iothread(); - qemu_thread_join(&s->thread); + if (s->migration_thread_running) { + qemu_thread_join(&s->thread); + s->migration_thread_running = false; + } qemu_mutex_lock_iothread(); migrate_compress_threads_join(); - qemu_fclose(s->file); - s->file = NULL; + qemu_fclose(s->to_dst_file); + s->to_dst_file = NULL; } - assert(s->state != MIGRATION_STATUS_ACTIVE); + assert((s->state != MIGRATION_STATUS_ACTIVE) && + (s->state != MIGRATION_STATUS_POSTCOPY_ACTIVE)); if (s->state == MIGRATION_STATUS_CANCELLING) { - migrate_set_state(s, MIGRATION_STATUS_CANCELLING, + migrate_set_state(&s->state, MIGRATION_STATUS_CANCELLING, MIGRATION_STATUS_CANCELLED); } @@ -571,24 +847,29 @@ static void migrate_fd_cleanup(void *opaque) void migrate_fd_error(MigrationState *s) { trace_migrate_fd_error(); - assert(s->file == NULL); - migrate_set_state(s, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_FAILED); + assert(s->to_dst_file == NULL); + migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, + MIGRATION_STATUS_FAILED); notifier_list_notify(&migration_state_notifiers, s); } static void migrate_fd_cancel(MigrationState *s) { int old_state ; - QEMUFile *f = migrate_get_current()->file; + QEMUFile *f = migrate_get_current()->to_dst_file; trace_migrate_fd_cancel(); + if (s->rp_state.from_dst_file) { + /* shutdown the rp socket, so causing the rp thread to shutdown */ + qemu_file_shutdown(s->rp_state.from_dst_file); + } + do { old_state = s->state; - if (old_state != MIGRATION_STATUS_SETUP && - old_state != MIGRATION_STATUS_ACTIVE) { + if (!migration_is_setup_or_active(old_state)) { break; } - migrate_set_state(s, old_state, MIGRATION_STATUS_CANCELLING); + migrate_set_state(&s->state, old_state, MIGRATION_STATUS_CANCELLING); } while (s->state != MIGRATION_STATUS_CANCELLING); /* @@ -629,34 +910,48 @@ bool migration_has_failed(MigrationState *s) s->state == MIGRATION_STATUS_FAILED); } -static MigrationState *migrate_init(const MigrationParams *params) +bool migration_in_postcopy(MigrationState *s) { - MigrationState *s = migrate_get_current(); - int64_t bandwidth_limit = s->bandwidth_limit; - bool enabled_capabilities[MIGRATION_CAPABILITY_MAX]; - int64_t xbzrle_cache_size = s->xbzrle_cache_size; - int compress_level = s->parameters[MIGRATION_PARAMETER_COMPRESS_LEVEL]; - int compress_thread_count = - s->parameters[MIGRATION_PARAMETER_COMPRESS_THREADS]; - int decompress_thread_count = - s->parameters[MIGRATION_PARAMETER_DECOMPRESS_THREADS]; + return (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE); +} - memcpy(enabled_capabilities, s->enabled_capabilities, - sizeof(enabled_capabilities)); +bool migration_in_postcopy_after_devices(MigrationState *s) +{ + return migration_in_postcopy(s) && s->postcopy_after_devices; +} - memset(s, 0, sizeof(*s)); +MigrationState *migrate_init(const MigrationParams *params) +{ + MigrationState *s = migrate_get_current(); + + /* + * Reinitialise all migration state, except + * parameters/capabilities that the user set, and + * locks. + */ + s->bytes_xfer = 0; + s->xfer_limit = 0; + s->cleanup_bh = 0; + s->to_dst_file = NULL; + s->state = MIGRATION_STATUS_NONE; s->params = *params; - memcpy(s->enabled_capabilities, enabled_capabilities, - sizeof(enabled_capabilities)); - s->xbzrle_cache_size = xbzrle_cache_size; - - s->parameters[MIGRATION_PARAMETER_COMPRESS_LEVEL] = compress_level; - s->parameters[MIGRATION_PARAMETER_COMPRESS_THREADS] = - compress_thread_count; - s->parameters[MIGRATION_PARAMETER_DECOMPRESS_THREADS] = - decompress_thread_count; - s->bandwidth_limit = bandwidth_limit; - migrate_set_state(s, MIGRATION_STATUS_NONE, MIGRATION_STATUS_SETUP); + s->rp_state.from_dst_file = NULL; + s->rp_state.error = false; + s->mbps = 0.0; + s->downtime = 0; + s->expected_downtime = 0; + s->dirty_pages_rate = 0; + s->dirty_bytes_rate = 0; + s->setup_time = 0; + s->dirty_sync_count = 0; + s->start_postcopy = false; + s->postcopy_after_devices = false; + s->migration_thread_running = false; + s->last_req_rb = NULL; + + migrate_set_state(&s->state, MIGRATION_STATUS_NONE, MIGRATION_STATUS_SETUP); + + QSIMPLEQ_INIT(&s->src_page_requests); s->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); return s; @@ -709,8 +1004,7 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, params.blk = has_blk && blk; params.shared = has_inc && inc; - if (s->state == MIGRATION_STATUS_ACTIVE || - s->state == MIGRATION_STATUS_SETUP || + if (migration_is_setup_or_active(s->state) || s->state == MIGRATION_STATUS_CANCELLING) { error_setg(errp, QERR_MIGRATION_ACTIVE); return; @@ -729,12 +1023,6 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, return; } - /* We are starting a new migration, so we want to start in a clean - state. This change is only needed if previous migration - failed/was cancelled. We don't use migrate_set_state() because - we are setting the initial state, not changing it. */ - s->state = MIGRATION_STATUS_NONE; - s = migrate_init(¶ms); if (strstart(uri, "tcp:", &p)) { @@ -754,7 +1042,8 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, } else { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "uri", "a valid migration protocol"); - migrate_set_state(s, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_FAILED); + migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, + MIGRATION_STATUS_FAILED); return; } @@ -817,8 +1106,9 @@ void qmp_migrate_set_speed(int64_t value, Error **errp) s = migrate_get_current(); s->bandwidth_limit = value; - if (s->file) { - qemu_file_set_rate_limit(s->file, s->bandwidth_limit / XFER_LIMIT_RATIO); + if (s->to_dst_file) { + qemu_file_set_rate_limit(s->to_dst_file, + s->bandwidth_limit / XFER_LIMIT_RATIO); } } @@ -829,6 +1119,15 @@ void qmp_migrate_set_downtime(double value, Error **errp) max_downtime = (uint64_t)value; } +bool migrate_postcopy_ram(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->enabled_capabilities[MIGRATION_CAPABILITY_POSTCOPY_RAM]; +} + bool migrate_auto_converge(void) { MigrationState *s; @@ -911,10 +1210,419 @@ int64_t migrate_xbzrle_cache_size(void) } /* migration thread support */ +/* + * Something bad happened to the RP stream, mark an error + * The caller shall print or trace something to indicate why + */ +static void mark_source_rp_bad(MigrationState *s) +{ + s->rp_state.error = true; +} + +static struct rp_cmd_args { + ssize_t len; /* -1 = variable */ + const char *name; +} rp_cmd_args[] = { + [MIG_RP_MSG_INVALID] = { .len = -1, .name = "INVALID" }, + [MIG_RP_MSG_SHUT] = { .len = 4, .name = "SHUT" }, + [MIG_RP_MSG_PONG] = { .len = 4, .name = "PONG" }, + [MIG_RP_MSG_REQ_PAGES] = { .len = 12, .name = "REQ_PAGES" }, + [MIG_RP_MSG_REQ_PAGES_ID] = { .len = -1, .name = "REQ_PAGES_ID" }, + [MIG_RP_MSG_MAX] = { .len = -1, .name = "MAX" }, +}; + +/* + * Process a request for pages received on the return path, + * We're allowed to send more than requested (e.g. to round to our page size) + * and we don't need to send pages that have already been sent. + */ +static void migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname, + ram_addr_t start, size_t len) +{ + long our_host_ps = getpagesize(); + trace_migrate_handle_rp_req_pages(rbname, start, len); + + /* + * Since we currently insist on matching page sizes, just sanity check + * we're being asked for whole host pages. + */ + if (start & (our_host_ps-1) || + (len & (our_host_ps-1))) { + error_report("%s: Misaligned page request, start: " RAM_ADDR_FMT + " len: %zd", __func__, start, len); + mark_source_rp_bad(ms); + return; + } + + if (ram_save_queue_pages(ms, rbname, start, len)) { + mark_source_rp_bad(ms); + } +} + +/* + * Handles messages sent on the return path towards the source VM + * + */ +static void *source_return_path_thread(void *opaque) +{ + MigrationState *ms = opaque; + QEMUFile *rp = ms->rp_state.from_dst_file; + uint16_t header_len, header_type; + uint8_t buf[512]; + uint32_t tmp32, sibling_error; + ram_addr_t start = 0; /* =0 to silence warning */ + size_t len = 0, expected_len; + int res; + + trace_source_return_path_thread_entry(); + while (!ms->rp_state.error && !qemu_file_get_error(rp) && + migration_is_setup_or_active(ms->state)) { + trace_source_return_path_thread_loop_top(); + header_type = qemu_get_be16(rp); + header_len = qemu_get_be16(rp); + + if (header_type >= MIG_RP_MSG_MAX || + header_type == MIG_RP_MSG_INVALID) { + error_report("RP: Received invalid message 0x%04x length 0x%04x", + header_type, header_len); + mark_source_rp_bad(ms); + goto out; + } + + if ((rp_cmd_args[header_type].len != -1 && + header_len != rp_cmd_args[header_type].len) || + header_len > sizeof(buf)) { + error_report("RP: Received '%s' message (0x%04x) with" + "incorrect length %d expecting %zu", + rp_cmd_args[header_type].name, header_type, header_len, + (size_t)rp_cmd_args[header_type].len); + mark_source_rp_bad(ms); + goto out; + } + + /* We know we've got a valid header by this point */ + res = qemu_get_buffer(rp, buf, header_len); + if (res != header_len) { + error_report("RP: Failed reading data for message 0x%04x" + " read %d expected %d", + header_type, res, header_len); + mark_source_rp_bad(ms); + goto out; + } + + /* OK, we have the message and the data */ + switch (header_type) { + case MIG_RP_MSG_SHUT: + sibling_error = be32_to_cpup((uint32_t *)buf); + trace_source_return_path_thread_shut(sibling_error); + if (sibling_error) { + error_report("RP: Sibling indicated error %d", sibling_error); + mark_source_rp_bad(ms); + } + /* + * We'll let the main thread deal with closing the RP + * we could do a shutdown(2) on it, but we're the only user + * anyway, so there's nothing gained. + */ + goto out; + + case MIG_RP_MSG_PONG: + tmp32 = be32_to_cpup((uint32_t *)buf); + trace_source_return_path_thread_pong(tmp32); + break; + + case MIG_RP_MSG_REQ_PAGES: + start = be64_to_cpup((uint64_t *)buf); + len = be32_to_cpup((uint32_t *)(buf + 8)); + migrate_handle_rp_req_pages(ms, NULL, start, len); + break; + + case MIG_RP_MSG_REQ_PAGES_ID: + expected_len = 12 + 1; /* header + termination */ + + if (header_len >= expected_len) { + start = be64_to_cpup((uint64_t *)buf); + len = be32_to_cpup((uint32_t *)(buf + 8)); + /* Now we expect an idstr */ + tmp32 = buf[12]; /* Length of the following idstr */ + buf[13 + tmp32] = '\0'; + expected_len += tmp32; + } + if (header_len != expected_len) { + error_report("RP: Req_Page_id with length %d expecting %zd", + header_len, expected_len); + mark_source_rp_bad(ms); + goto out; + } + migrate_handle_rp_req_pages(ms, (char *)&buf[13], start, len); + break; + + default: + break; + } + } + if (qemu_file_get_error(rp)) { + trace_source_return_path_thread_bad_end(); + mark_source_rp_bad(ms); + } + + trace_source_return_path_thread_end(); +out: + ms->rp_state.from_dst_file = NULL; + qemu_fclose(rp); + return NULL; +} + +static int open_return_path_on_source(MigrationState *ms) +{ + + ms->rp_state.from_dst_file = qemu_file_get_return_path(ms->to_dst_file); + if (!ms->rp_state.from_dst_file) { + return -1; + } + + trace_open_return_path_on_source(); + qemu_thread_create(&ms->rp_state.rp_thread, "return path", + source_return_path_thread, ms, QEMU_THREAD_JOINABLE); + + trace_open_return_path_on_source_continue(); + + return 0; +} + +/* Returns 0 if the RP was ok, otherwise there was an error on the RP */ +static int await_return_path_close_on_source(MigrationState *ms) +{ + /* + * If this is a normal exit then the destination will send a SHUT and the + * rp_thread will exit, however if there's an error we need to cause + * it to exit. + */ + if (qemu_file_get_error(ms->to_dst_file) && ms->rp_state.from_dst_file) { + /* + * shutdown(2), if we have it, will cause it to unblock if it's stuck + * waiting for the destination. + */ + qemu_file_shutdown(ms->rp_state.from_dst_file); + mark_source_rp_bad(ms); + } + trace_await_return_path_close_on_source_joining(); + qemu_thread_join(&ms->rp_state.rp_thread); + trace_await_return_path_close_on_source_close(); + return ms->rp_state.error; +} + +/* + * Switch from normal iteration to postcopy + * Returns non-0 on error + */ +static int postcopy_start(MigrationState *ms, bool *old_vm_running) +{ + int ret; + const QEMUSizedBuffer *qsb; + int64_t time_at_stop = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + migrate_set_state(&ms->state, MIGRATION_STATUS_ACTIVE, + MIGRATION_STATUS_POSTCOPY_ACTIVE); + + trace_postcopy_start(); + qemu_mutex_lock_iothread(); + trace_postcopy_start_set_run(); + + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); + *old_vm_running = runstate_is_running(); + global_state_store(); + ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); + if (ret < 0) { + goto fail; + } + + ret = bdrv_inactivate_all(); + if (ret < 0) { + goto fail; + } + + /* + * Cause any non-postcopiable, but iterative devices to + * send out their final data. + */ + qemu_savevm_state_complete_precopy(ms->to_dst_file, true); + + /* + * in Finish migrate and with the io-lock held everything should + * be quiet, but we've potentially still got dirty pages and we + * need to tell the destination to throw any pages it's already received + * that are dirty + */ + if (ram_postcopy_send_discard_bitmap(ms)) { + error_report("postcopy send discard bitmap failed"); + goto fail; + } + + /* + * send rest of state - note things that are doing postcopy + * will notice we're in POSTCOPY_ACTIVE and not actually + * wrap their state up here + */ + qemu_file_set_rate_limit(ms->to_dst_file, INT64_MAX); + /* Ping just for debugging, helps line traces up */ + qemu_savevm_send_ping(ms->to_dst_file, 2); + + /* + * While loading the device state we may trigger page transfer + * requests and the fd must be free to process those, and thus + * the destination must read the whole device state off the fd before + * it starts processing it. Unfortunately the ad-hoc migration format + * doesn't allow the destination to know the size to read without fully + * parsing it through each devices load-state code (especially the open + * coded devices that use get/put). + * So we wrap the device state up in a package with a length at the start; + * to do this we use a qemu_buf to hold the whole of the device state. + */ + QEMUFile *fb = qemu_bufopen("w", NULL); + if (!fb) { + error_report("Failed to create buffered file"); + goto fail; + } + + /* + * Make sure the receiver can get incoming pages before we send the rest + * of the state + */ + qemu_savevm_send_postcopy_listen(fb); + + qemu_savevm_state_complete_precopy(fb, false); + qemu_savevm_send_ping(fb, 3); + + qemu_savevm_send_postcopy_run(fb); + + /* <><> end of stuff going into the package */ + qsb = qemu_buf_get(fb); + + /* Now send that blob */ + if (qemu_savevm_send_packaged(ms->to_dst_file, qsb)) { + goto fail_closefb; + } + qemu_fclose(fb); + + /* Send a notify to give a chance for anything that needs to happen + * at the transition to postcopy and after the device state; in particular + * spice needs to trigger a transition now + */ + ms->postcopy_after_devices = true; + notifier_list_notify(&migration_state_notifiers, ms); + + ms->downtime = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - time_at_stop; + + qemu_mutex_unlock_iothread(); + + /* + * Although this ping is just for debug, it could potentially be + * used for getting a better measurement of downtime at the source. + */ + qemu_savevm_send_ping(ms->to_dst_file, 4); + + ret = qemu_file_get_error(ms->to_dst_file); + if (ret) { + error_report("postcopy_start: Migration stream errored"); + migrate_set_state(&ms->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, + MIGRATION_STATUS_FAILED); + } + + return ret; + +fail_closefb: + qemu_fclose(fb); +fail: + migrate_set_state(&ms->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, + MIGRATION_STATUS_FAILED); + qemu_mutex_unlock_iothread(); + return -1; +} + +/** + * migration_completion: Used by migration_thread when there's not much left. + * The caller 'breaks' the loop when this returns. + * + * @s: Current migration state + * @current_active_state: The migration state we expect to be in + * @*old_vm_running: Pointer to old_vm_running flag + * @*start_time: Pointer to time to update + */ +static void migration_completion(MigrationState *s, int current_active_state, + bool *old_vm_running, + int64_t *start_time) +{ + int ret; + + if (s->state == MIGRATION_STATUS_ACTIVE) { + qemu_mutex_lock_iothread(); + *start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); + *old_vm_running = runstate_is_running(); + ret = global_state_store(); + + if (!ret) { + ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); + if (ret >= 0) { + ret = bdrv_inactivate_all(); + } + if (ret >= 0) { + qemu_file_set_rate_limit(s->to_dst_file, INT64_MAX); + qemu_savevm_state_complete_precopy(s->to_dst_file, false); + } + } + qemu_mutex_unlock_iothread(); + + if (ret < 0) { + goto fail; + } + } else if (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + trace_migration_completion_postcopy_end(); + + qemu_savevm_state_complete_postcopy(s->to_dst_file); + trace_migration_completion_postcopy_end_after_complete(); + } + + /* + * If rp was opened we must clean up the thread before + * cleaning everything else up (since if there are no failures + * it will wait for the destination to send it's status in + * a SHUT command). + * Postcopy opens rp if enabled (even if it's not avtivated) + */ + if (migrate_postcopy_ram()) { + int rp_error; + trace_migration_completion_postcopy_end_before_rp(); + rp_error = await_return_path_close_on_source(s); + trace_migration_completion_postcopy_end_after_rp(rp_error); + if (rp_error) { + goto fail; + } + } + + if (qemu_file_get_error(s->to_dst_file)) { + trace_migration_completion_file_err(); + goto fail; + } + + migrate_set_state(&s->state, current_active_state, + MIGRATION_STATUS_COMPLETED); + return; + +fail: + migrate_set_state(&s->state, current_active_state, + MIGRATION_STATUS_FAILED); +} + +/* + * Master migration thread on the source VM. + * It drives the migration and pumps the data down the outgoing channel. + */ static void *migration_thread(void *opaque) { MigrationState *s = opaque; + /* Used by the bandwidth calcs, updated later */ int64_t initial_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); int64_t setup_start = qemu_clock_get_ms(QEMU_CLOCK_HOST); int64_t initial_bytes = 0; @@ -922,70 +1630,92 @@ static void *migration_thread(void *opaque) int64_t start_time = initial_time; int64_t end_time; bool old_vm_running = false; + bool entered_postcopy = false; + /* The active state we expect to be in; ACTIVE or POSTCOPY_ACTIVE */ + enum MigrationStatus current_active_state = MIGRATION_STATUS_ACTIVE; rcu_register_thread(); - qemu_savevm_state_header(s->file); - qemu_savevm_state_begin(s->file, &s->params); + qemu_savevm_state_header(s->to_dst_file); + + if (migrate_postcopy_ram()) { + /* Now tell the dest that it should open its end so it can reply */ + qemu_savevm_send_open_return_path(s->to_dst_file); + + /* And do a ping that will make stuff easier to debug */ + qemu_savevm_send_ping(s->to_dst_file, 1); + + /* + * Tell the destination that we *might* want to do postcopy later; + * if the other end can't do postcopy it should fail now, nice and + * early. + */ + qemu_savevm_send_postcopy_advise(s->to_dst_file); + } + + qemu_savevm_state_begin(s->to_dst_file, &s->params); s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start; - migrate_set_state(s, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_ACTIVE); + current_active_state = MIGRATION_STATUS_ACTIVE; + migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, + MIGRATION_STATUS_ACTIVE); + + trace_migration_thread_setup_complete(); - while (s->state == MIGRATION_STATUS_ACTIVE) { + while (s->state == MIGRATION_STATUS_ACTIVE || + s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { int64_t current_time; uint64_t pending_size; - if (!qemu_file_rate_limit(s->file)) { - pending_size = qemu_savevm_state_pending(s->file, max_size); - trace_migrate_pending(pending_size, max_size); + if (!qemu_file_rate_limit(s->to_dst_file)) { + uint64_t pend_post, pend_nonpost; + + qemu_savevm_state_pending(s->to_dst_file, max_size, &pend_nonpost, + &pend_post); + pending_size = pend_nonpost + pend_post; + trace_migrate_pending(pending_size, max_size, + pend_post, pend_nonpost); if (pending_size && pending_size >= max_size) { - qemu_savevm_state_iterate(s->file); - } else { - int ret; - - qemu_mutex_lock_iothread(); - start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); - old_vm_running = runstate_is_running(); - - ret = global_state_store(); - if (!ret) { - ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); - if (ret >= 0) { - qemu_file_set_rate_limit(s->file, INT64_MAX); - qemu_savevm_state_complete(s->file); - } - } - qemu_mutex_unlock_iothread(); + /* Still a significant amount to transfer */ - if (ret < 0) { - migrate_set_state(s, MIGRATION_STATUS_ACTIVE, - MIGRATION_STATUS_FAILED); - break; - } + if (migrate_postcopy_ram() && + s->state != MIGRATION_STATUS_POSTCOPY_ACTIVE && + pend_nonpost <= max_size && + atomic_read(&s->start_postcopy)) { - if (!qemu_file_get_error(s->file)) { - migrate_set_state(s, MIGRATION_STATUS_ACTIVE, - MIGRATION_STATUS_COMPLETED); - break; + if (!postcopy_start(s, &old_vm_running)) { + current_active_state = MIGRATION_STATUS_POSTCOPY_ACTIVE; + entered_postcopy = true; + } + + continue; } + /* Just another iteration step */ + qemu_savevm_state_iterate(s->to_dst_file, entered_postcopy); + } else { + trace_migration_thread_low_pending(pending_size); + migration_completion(s, current_active_state, + &old_vm_running, &start_time); + break; } } - if (qemu_file_get_error(s->file)) { - migrate_set_state(s, MIGRATION_STATUS_ACTIVE, + if (qemu_file_get_error(s->to_dst_file)) { + migrate_set_state(&s->state, current_active_state, MIGRATION_STATUS_FAILED); + trace_migration_thread_file_err(); break; } current_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); if (current_time >= initial_time + BUFFER_DELAY) { - uint64_t transferred_bytes = qemu_ftell(s->file) - initial_bytes; + uint64_t transferred_bytes = qemu_ftell(s->to_dst_file) - + initial_bytes; uint64_t time_spent = current_time - initial_time; - double bandwidth = transferred_bytes / time_spent; + double bandwidth = (double)transferred_bytes / time_spent; max_size = bandwidth * migrate_max_downtime() / 1000000; - s->mbps = time_spent ? (((double) transferred_bytes * 8.0) / - ((double) time_spent / 1000.0)) / 1000.0 / 1000.0 : -1; + s->mbps = (((double) transferred_bytes * 8.0) / + ((double) time_spent / 1000.0)) / 1000.0 / 1000.0; trace_migrate_transferred(transferred_bytes, time_spent, bandwidth, max_size); @@ -995,31 +1725,36 @@ static void *migration_thread(void *opaque) s->expected_downtime = s->dirty_bytes_rate / bandwidth; } - qemu_file_reset_rate_limit(s->file); + qemu_file_reset_rate_limit(s->to_dst_file); initial_time = current_time; - initial_bytes = qemu_ftell(s->file); + initial_bytes = qemu_ftell(s->to_dst_file); } - if (qemu_file_rate_limit(s->file)) { + if (qemu_file_rate_limit(s->to_dst_file)) { /* usleep expects microseconds */ g_usleep((initial_time + BUFFER_DELAY - current_time)*1000); } } + trace_migration_thread_after_loop(); + /* If we enabled cpu throttling for auto-converge, turn it off. */ + cpu_throttle_stop(); end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); qemu_mutex_lock_iothread(); - qemu_savevm_state_cancel(); + qemu_savevm_state_cleanup(); if (s->state == MIGRATION_STATUS_COMPLETED) { - uint64_t transferred_bytes = qemu_ftell(s->file); + uint64_t transferred_bytes = qemu_ftell(s->to_dst_file); s->total_time = end_time - s->total_time; - s->downtime = end_time - start_time; + if (!entered_postcopy) { + s->downtime = end_time - start_time; + } if (s->total_time) { s->mbps = (((double) transferred_bytes * 8.0) / ((double) s->total_time)) / 1000; } runstate_set(RUN_STATE_POSTMIGRATE); } else { - if (old_vm_running) { + if (old_vm_running && !entered_postcopy) { vm_start(); } } @@ -1036,13 +1771,40 @@ void migrate_fd_connect(MigrationState *s) s->expected_downtime = max_downtime/1000000; s->cleanup_bh = qemu_bh_new(migrate_fd_cleanup, s); - qemu_file_set_rate_limit(s->file, + qemu_file_set_rate_limit(s->to_dst_file, s->bandwidth_limit / XFER_LIMIT_RATIO); /* Notify before starting migration thread */ notifier_list_notify(&migration_state_notifiers, s); + /* + * Open the return path; currently for postcopy but other things might + * also want it. + */ + if (migrate_postcopy_ram()) { + if (open_return_path_on_source(s)) { + error_report("Unable to open return-path for postcopy"); + migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, + MIGRATION_STATUS_FAILED); + migrate_fd_cleanup(s); + return; + } + } + migrate_compress_threads_create(); qemu_thread_create(&s->thread, "migration", migration_thread, s, QEMU_THREAD_JOINABLE); + s->migration_thread_running = true; +} + +PostcopyState postcopy_state_get(void) +{ + return atomic_mb_read(&incoming_postcopy_state); } + +/* Set the state and return the old state */ +PostcopyState postcopy_state_set(PostcopyState new_state) +{ + return atomic_xchg(&incoming_postcopy_state, new_state); +} + diff --git a/qemu/migration/postcopy-ram.c b/qemu/migration/postcopy-ram.c new file mode 100644 index 000000000..fbd0064fc --- /dev/null +++ b/qemu/migration/postcopy-ram.c @@ -0,0 +1,761 @@ +/* + * Postcopy migration for RAM + * + * Copyright 2013-2015 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Dave Gilbert + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +/* + * Postcopy is a migration technique where the execution flips from the + * source to the destination before all the data has been copied. + */ + +#include "qemu/osdep.h" +#include + +#include "qemu-common.h" +#include "migration/migration.h" +#include "migration/postcopy-ram.h" +#include "sysemu/sysemu.h" +#include "sysemu/balloon.h" +#include "qemu/error-report.h" +#include "trace.h" + +/* Arbitrary limit on size of each discard command, + * keeps them around ~200 bytes + */ +#define MAX_DISCARDS_PER_COMMAND 12 + +struct PostcopyDiscardState { + const char *ramblock_name; + uint64_t offset; /* Bitmap entry for the 1st bit of this RAMBlock */ + uint16_t cur_entry; + /* + * Start and length of a discard range (bytes) + */ + uint64_t start_list[MAX_DISCARDS_PER_COMMAND]; + uint64_t length_list[MAX_DISCARDS_PER_COMMAND]; + unsigned int nsentwords; + unsigned int nsentcmds; +}; + +/* Postcopy needs to detect accesses to pages that haven't yet been copied + * across, and efficiently map new pages in, the techniques for doing this + * are target OS specific. + */ +#if defined(__linux__) + +#include +#include +#include +#include +#include /* for __u64 */ +#endif + +#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD) +#include +#include + +static bool ufd_version_check(int ufd) +{ + struct uffdio_api api_struct; + uint64_t ioctl_mask; + + api_struct.api = UFFD_API; + api_struct.features = 0; + if (ioctl(ufd, UFFDIO_API, &api_struct)) { + error_report("postcopy_ram_supported_by_host: UFFDIO_API failed: %s", + strerror(errno)); + return false; + } + + ioctl_mask = (__u64)1 << _UFFDIO_REGISTER | + (__u64)1 << _UFFDIO_UNREGISTER; + if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) { + error_report("Missing userfault features: %" PRIx64, + (uint64_t)(~api_struct.ioctls & ioctl_mask)); + return false; + } + + return true; +} + +/* + * Note: This has the side effect of munlock'ing all of RAM, that's + * normally fine since if the postcopy succeeds it gets turned back on at the + * end. + */ +bool postcopy_ram_supported_by_host(void) +{ + long pagesize = getpagesize(); + int ufd = -1; + bool ret = false; /* Error unless we change it */ + void *testarea = NULL; + struct uffdio_register reg_struct; + struct uffdio_range range_struct; + uint64_t feature_mask; + + if ((1ul << qemu_target_page_bits()) > pagesize) { + error_report("Target page size bigger than host page size"); + goto out; + } + + ufd = syscall(__NR_userfaultfd, O_CLOEXEC); + if (ufd == -1) { + error_report("%s: userfaultfd not available: %s", __func__, + strerror(errno)); + goto out; + } + + /* Version and features check */ + if (!ufd_version_check(ufd)) { + goto out; + } + + /* + * userfault and mlock don't go together; we'll put it back later if + * it was enabled. + */ + if (munlockall()) { + error_report("%s: munlockall: %s", __func__, strerror(errno)); + return -1; + } + + /* + * We need to check that the ops we need are supported on anon memory + * To do that we need to register a chunk and see the flags that + * are returned. + */ + testarea = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | + MAP_ANONYMOUS, -1, 0); + if (testarea == MAP_FAILED) { + error_report("%s: Failed to map test area: %s", __func__, + strerror(errno)); + goto out; + } + g_assert(((size_t)testarea & (pagesize-1)) == 0); + + reg_struct.range.start = (uintptr_t)testarea; + reg_struct.range.len = pagesize; + reg_struct.mode = UFFDIO_REGISTER_MODE_MISSING; + + if (ioctl(ufd, UFFDIO_REGISTER, ®_struct)) { + error_report("%s userfault register: %s", __func__, strerror(errno)); + goto out; + } + + range_struct.start = (uintptr_t)testarea; + range_struct.len = pagesize; + if (ioctl(ufd, UFFDIO_UNREGISTER, &range_struct)) { + error_report("%s userfault unregister: %s", __func__, strerror(errno)); + goto out; + } + + feature_mask = (__u64)1 << _UFFDIO_WAKE | + (__u64)1 << _UFFDIO_COPY | + (__u64)1 << _UFFDIO_ZEROPAGE; + if ((reg_struct.ioctls & feature_mask) != feature_mask) { + error_report("Missing userfault map features: %" PRIx64, + (uint64_t)(~reg_struct.ioctls & feature_mask)); + goto out; + } + + /* Success! */ + ret = true; +out: + if (testarea) { + munmap(testarea, pagesize); + } + if (ufd != -1) { + close(ufd); + } + return ret; +} + +/** + * postcopy_ram_discard_range: Discard a range of memory. + * We can assume that if we've been called postcopy_ram_hosttest returned true. + * + * @mis: Current incoming migration state. + * @start, @length: range of memory to discard. + * + * returns: 0 on success. + */ +int postcopy_ram_discard_range(MigrationIncomingState *mis, uint8_t *start, + size_t length) +{ + trace_postcopy_ram_discard_range(start, length); + if (madvise(start, length, MADV_DONTNEED)) { + error_report("%s MADV_DONTNEED: %s", __func__, strerror(errno)); + return -1; + } + + return 0; +} + +/* + * Setup an area of RAM so that it *can* be used for postcopy later; this + * must be done right at the start prior to pre-copy. + * opaque should be the MIS. + */ +static int init_range(const char *block_name, void *host_addr, + ram_addr_t offset, ram_addr_t length, void *opaque) +{ + MigrationIncomingState *mis = opaque; + + trace_postcopy_init_range(block_name, host_addr, offset, length); + + /* + * We need the whole of RAM to be truly empty for postcopy, so things + * like ROMs and any data tables built during init must be zero'd + * - we're going to get the copy from the source anyway. + * (Precopy will just overwrite this data, so doesn't need the discard) + */ + if (postcopy_ram_discard_range(mis, host_addr, length)) { + return -1; + } + + return 0; +} + +/* + * At the end of migration, undo the effects of init_range + * opaque should be the MIS. + */ +static int cleanup_range(const char *block_name, void *host_addr, + ram_addr_t offset, ram_addr_t length, void *opaque) +{ + MigrationIncomingState *mis = opaque; + struct uffdio_range range_struct; + trace_postcopy_cleanup_range(block_name, host_addr, offset, length); + + /* + * We turned off hugepage for the precopy stage with postcopy enabled + * we can turn it back on now. + */ + qemu_madvise(host_addr, length, QEMU_MADV_HUGEPAGE); + + /* + * We can also turn off userfault now since we should have all the + * pages. It can be useful to leave it on to debug postcopy + * if you're not sure it's always getting every page. + */ + range_struct.start = (uintptr_t)host_addr; + range_struct.len = length; + + if (ioctl(mis->userfault_fd, UFFDIO_UNREGISTER, &range_struct)) { + error_report("%s: userfault unregister %s", __func__, strerror(errno)); + + return -1; + } + + return 0; +} + +/* + * Initialise postcopy-ram, setting the RAM to a state where we can go into + * postcopy later; must be called prior to any precopy. + * called from arch_init's similarly named ram_postcopy_incoming_init + */ +int postcopy_ram_incoming_init(MigrationIncomingState *mis, size_t ram_pages) +{ + if (qemu_ram_foreach_block(init_range, mis)) { + return -1; + } + + return 0; +} + +/* + * At the end of a migration where postcopy_ram_incoming_init was called. + */ +int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) +{ + trace_postcopy_ram_incoming_cleanup_entry(); + + if (mis->have_fault_thread) { + uint64_t tmp64; + + if (qemu_ram_foreach_block(cleanup_range, mis)) { + return -1; + } + /* + * Tell the fault_thread to exit, it's an eventfd that should + * currently be at 0, we're going to increment it to 1 + */ + tmp64 = 1; + if (write(mis->userfault_quit_fd, &tmp64, 8) == 8) { + trace_postcopy_ram_incoming_cleanup_join(); + qemu_thread_join(&mis->fault_thread); + } else { + /* Not much we can do here, but may as well report it */ + error_report("%s: incrementing userfault_quit_fd: %s", __func__, + strerror(errno)); + } + trace_postcopy_ram_incoming_cleanup_closeuf(); + close(mis->userfault_fd); + close(mis->userfault_quit_fd); + mis->have_fault_thread = false; + } + + qemu_balloon_inhibit(false); + + if (enable_mlock) { + if (os_mlock() < 0) { + error_report("mlock: %s", strerror(errno)); + /* + * It doesn't feel right to fail at this point, we have a valid + * VM state. + */ + } + } + + postcopy_state_set(POSTCOPY_INCOMING_END); + migrate_send_rp_shut(mis, qemu_file_get_error(mis->from_src_file) != 0); + + if (mis->postcopy_tmp_page) { + munmap(mis->postcopy_tmp_page, getpagesize()); + mis->postcopy_tmp_page = NULL; + } + trace_postcopy_ram_incoming_cleanup_exit(); + return 0; +} + +/* + * Disable huge pages on an area + */ +static int nhp_range(const char *block_name, void *host_addr, + ram_addr_t offset, ram_addr_t length, void *opaque) +{ + trace_postcopy_nhp_range(block_name, host_addr, offset, length); + + /* + * Before we do discards we need to ensure those discards really + * do delete areas of the page, even if THP thinks a hugepage would + * be a good idea, so force hugepages off. + */ + qemu_madvise(host_addr, length, QEMU_MADV_NOHUGEPAGE); + + return 0; +} + +/* + * Userfault requires us to mark RAM as NOHUGEPAGE prior to discard + * however leaving it until after precopy means that most of the precopy + * data is still THPd + */ +int postcopy_ram_prepare_discard(MigrationIncomingState *mis) +{ + if (qemu_ram_foreach_block(nhp_range, mis)) { + return -1; + } + + postcopy_state_set(POSTCOPY_INCOMING_DISCARD); + + return 0; +} + +/* + * Mark the given area of RAM as requiring notification to unwritten areas + * Used as a callback on qemu_ram_foreach_block. + * host_addr: Base of area to mark + * offset: Offset in the whole ram arena + * length: Length of the section + * opaque: MigrationIncomingState pointer + * Returns 0 on success + */ +static int ram_block_enable_notify(const char *block_name, void *host_addr, + ram_addr_t offset, ram_addr_t length, + void *opaque) +{ + MigrationIncomingState *mis = opaque; + struct uffdio_register reg_struct; + + reg_struct.range.start = (uintptr_t)host_addr; + reg_struct.range.len = length; + reg_struct.mode = UFFDIO_REGISTER_MODE_MISSING; + + /* Now tell our userfault_fd that it's responsible for this area */ + if (ioctl(mis->userfault_fd, UFFDIO_REGISTER, ®_struct)) { + error_report("%s userfault register: %s", __func__, strerror(errno)); + return -1; + } + + return 0; +} + +/* + * Handle faults detected by the USERFAULT markings + */ +static void *postcopy_ram_fault_thread(void *opaque) +{ + MigrationIncomingState *mis = opaque; + struct uffd_msg msg; + int ret; + size_t hostpagesize = getpagesize(); + RAMBlock *rb = NULL; + RAMBlock *last_rb = NULL; /* last RAMBlock we sent part of */ + + trace_postcopy_ram_fault_thread_entry(); + qemu_sem_post(&mis->fault_thread_sem); + + while (true) { + ram_addr_t rb_offset; + ram_addr_t in_raspace; + struct pollfd pfd[2]; + + /* + * We're mainly waiting for the kernel to give us a faulting HVA, + * however we can be told to quit via userfault_quit_fd which is + * an eventfd + */ + pfd[0].fd = mis->userfault_fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + pfd[1].fd = mis->userfault_quit_fd; + pfd[1].events = POLLIN; /* Waiting for eventfd to go positive */ + pfd[1].revents = 0; + + if (poll(pfd, 2, -1 /* Wait forever */) == -1) { + error_report("%s: userfault poll: %s", __func__, strerror(errno)); + break; + } + + if (pfd[1].revents) { + trace_postcopy_ram_fault_thread_quit(); + break; + } + + ret = read(mis->userfault_fd, &msg, sizeof(msg)); + if (ret != sizeof(msg)) { + if (errno == EAGAIN) { + /* + * if a wake up happens on the other thread just after + * the poll, there is nothing to read. + */ + continue; + } + if (ret < 0) { + error_report("%s: Failed to read full userfault message: %s", + __func__, strerror(errno)); + break; + } else { + error_report("%s: Read %d bytes from userfaultfd expected %zd", + __func__, ret, sizeof(msg)); + break; /* Lost alignment, don't know what we'd read next */ + } + } + if (msg.event != UFFD_EVENT_PAGEFAULT) { + error_report("%s: Read unexpected event %ud from userfaultfd", + __func__, msg.event); + continue; /* It's not a page fault, shouldn't happen */ + } + + rb = qemu_ram_block_from_host( + (void *)(uintptr_t)msg.arg.pagefault.address, + true, &in_raspace, &rb_offset); + if (!rb) { + error_report("postcopy_ram_fault_thread: Fault outside guest: %" + PRIx64, (uint64_t)msg.arg.pagefault.address); + break; + } + + rb_offset &= ~(hostpagesize - 1); + trace_postcopy_ram_fault_thread_request(msg.arg.pagefault.address, + qemu_ram_get_idstr(rb), + rb_offset); + + /* + * Send the request to the source - we want to request one + * of our host page sizes (which is >= TPS) + */ + if (rb != last_rb) { + last_rb = rb; + migrate_send_rp_req_pages(mis, qemu_ram_get_idstr(rb), + rb_offset, hostpagesize); + } else { + /* Save some space */ + migrate_send_rp_req_pages(mis, NULL, + rb_offset, hostpagesize); + } + } + trace_postcopy_ram_fault_thread_exit(); + return NULL; +} + +int postcopy_ram_enable_notify(MigrationIncomingState *mis) +{ + /* Open the fd for the kernel to give us userfaults */ + mis->userfault_fd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); + if (mis->userfault_fd == -1) { + error_report("%s: Failed to open userfault fd: %s", __func__, + strerror(errno)); + return -1; + } + + /* + * Although the host check already tested the API, we need to + * do the check again as an ABI handshake on the new fd. + */ + if (!ufd_version_check(mis->userfault_fd)) { + return -1; + } + + /* Now an eventfd we use to tell the fault-thread to quit */ + mis->userfault_quit_fd = eventfd(0, EFD_CLOEXEC); + if (mis->userfault_quit_fd == -1) { + error_report("%s: Opening userfault_quit_fd: %s", __func__, + strerror(errno)); + close(mis->userfault_fd); + return -1; + } + + qemu_sem_init(&mis->fault_thread_sem, 0); + qemu_thread_create(&mis->fault_thread, "postcopy/fault", + postcopy_ram_fault_thread, mis, QEMU_THREAD_JOINABLE); + qemu_sem_wait(&mis->fault_thread_sem); + qemu_sem_destroy(&mis->fault_thread_sem); + mis->have_fault_thread = true; + + /* Mark so that we get notified of accesses to unwritten areas */ + if (qemu_ram_foreach_block(ram_block_enable_notify, mis)) { + return -1; + } + + /* + * Ballooning can mark pages as absent while we're postcopying + * that would cause false userfaults. + */ + qemu_balloon_inhibit(true); + + trace_postcopy_ram_enable_notify(); + + return 0; +} + +/* + * Place a host page (from) at (host) atomically + * returns 0 on success + */ +int postcopy_place_page(MigrationIncomingState *mis, void *host, void *from) +{ + struct uffdio_copy copy_struct; + + copy_struct.dst = (uint64_t)(uintptr_t)host; + copy_struct.src = (uint64_t)(uintptr_t)from; + copy_struct.len = getpagesize(); + copy_struct.mode = 0; + + /* copy also acks to the kernel waking the stalled thread up + * TODO: We can inhibit that ack and only do it if it was requested + * which would be slightly cheaper, but we'd have to be careful + * of the order of updating our page state. + */ + if (ioctl(mis->userfault_fd, UFFDIO_COPY, ©_struct)) { + int e = errno; + error_report("%s: %s copy host: %p from: %p", + __func__, strerror(e), host, from); + + return -e; + } + + trace_postcopy_place_page(host); + return 0; +} + +/* + * Place a zero page at (host) atomically + * returns 0 on success + */ +int postcopy_place_page_zero(MigrationIncomingState *mis, void *host) +{ + struct uffdio_zeropage zero_struct; + + zero_struct.range.start = (uint64_t)(uintptr_t)host; + zero_struct.range.len = getpagesize(); + zero_struct.mode = 0; + + if (ioctl(mis->userfault_fd, UFFDIO_ZEROPAGE, &zero_struct)) { + int e = errno; + error_report("%s: %s zero host: %p", + __func__, strerror(e), host); + + return -e; + } + + trace_postcopy_place_page_zero(host); + return 0; +} + +/* + * Returns a target page of memory that can be mapped at a later point in time + * using postcopy_place_page + * The same address is used repeatedly, postcopy_place_page just takes the + * backing page away. + * Returns: Pointer to allocated page + * + */ +void *postcopy_get_tmp_page(MigrationIncomingState *mis) +{ + if (!mis->postcopy_tmp_page) { + mis->postcopy_tmp_page = mmap(NULL, getpagesize(), + PROT_READ | PROT_WRITE, MAP_PRIVATE | + MAP_ANONYMOUS, -1, 0); + if (!mis->postcopy_tmp_page) { + error_report("%s: %s", __func__, strerror(errno)); + return NULL; + } + } + + return mis->postcopy_tmp_page; +} + +#else +/* No target OS support, stubs just fail */ +bool postcopy_ram_supported_by_host(void) +{ + error_report("%s: No OS support", __func__); + return false; +} + +int postcopy_ram_incoming_init(MigrationIncomingState *mis, size_t ram_pages) +{ + error_report("postcopy_ram_incoming_init: No OS support"); + return -1; +} + +int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) +{ + assert(0); + return -1; +} + +int postcopy_ram_discard_range(MigrationIncomingState *mis, uint8_t *start, + size_t length) +{ + assert(0); + return -1; +} + +int postcopy_ram_prepare_discard(MigrationIncomingState *mis) +{ + assert(0); + return -1; +} + +int postcopy_ram_enable_notify(MigrationIncomingState *mis) +{ + assert(0); + return -1; +} + +int postcopy_place_page(MigrationIncomingState *mis, void *host, void *from) +{ + assert(0); + return -1; +} + +int postcopy_place_page_zero(MigrationIncomingState *mis, void *host) +{ + assert(0); + return -1; +} + +void *postcopy_get_tmp_page(MigrationIncomingState *mis) +{ + assert(0); + return NULL; +} + +#endif + +/* ------------------------------------------------------------------------- */ + +/** + * postcopy_discard_send_init: Called at the start of each RAMBlock before + * asking to discard individual ranges. + * + * @ms: The current migration state. + * @offset: the bitmap offset of the named RAMBlock in the migration + * bitmap. + * @name: RAMBlock that discards will operate on. + * + * returns: a new PDS. + */ +PostcopyDiscardState *postcopy_discard_send_init(MigrationState *ms, + unsigned long offset, + const char *name) +{ + PostcopyDiscardState *res = g_malloc0(sizeof(PostcopyDiscardState)); + + if (res) { + res->ramblock_name = name; + res->offset = offset; + } + + return res; +} + +/** + * postcopy_discard_send_range: Called by the bitmap code for each chunk to + * discard. May send a discard message, may just leave it queued to + * be sent later. + * + * @ms: Current migration state. + * @pds: Structure initialised by postcopy_discard_send_init(). + * @start,@length: a range of pages in the migration bitmap in the + * RAM block passed to postcopy_discard_send_init() (length=1 is one page) + */ +void postcopy_discard_send_range(MigrationState *ms, PostcopyDiscardState *pds, + unsigned long start, unsigned long length) +{ + size_t tp_bits = qemu_target_page_bits(); + /* Convert to byte offsets within the RAM block */ + pds->start_list[pds->cur_entry] = (start - pds->offset) << tp_bits; + pds->length_list[pds->cur_entry] = length << tp_bits; + trace_postcopy_discard_send_range(pds->ramblock_name, start, length); + pds->cur_entry++; + pds->nsentwords++; + + if (pds->cur_entry == MAX_DISCARDS_PER_COMMAND) { + /* Full set, ship it! */ + qemu_savevm_send_postcopy_ram_discard(ms->to_dst_file, + pds->ramblock_name, + pds->cur_entry, + pds->start_list, + pds->length_list); + pds->nsentcmds++; + pds->cur_entry = 0; + } +} + +/** + * postcopy_discard_send_finish: Called at the end of each RAMBlock by the + * bitmap code. Sends any outstanding discard messages, frees the PDS + * + * @ms: Current migration state. + * @pds: Structure initialised by postcopy_discard_send_init(). + */ +void postcopy_discard_send_finish(MigrationState *ms, PostcopyDiscardState *pds) +{ + /* Anything unsent? */ + if (pds->cur_entry) { + qemu_savevm_send_postcopy_ram_discard(ms->to_dst_file, + pds->ramblock_name, + pds->cur_entry, + pds->start_list, + pds->length_list); + pds->nsentcmds++; + } + + trace_postcopy_discard_send_finish(pds->ramblock_name, pds->nsentwords, + pds->nsentcmds); + + g_free(pds); +} diff --git a/qemu/migration/qemu-file-buf.c b/qemu/migration/qemu-file-buf.c index 2de9330ca..7b8e78e99 100644 --- a/qemu/migration/qemu-file-buf.c +++ b/qemu/migration/qemu-file-buf.c @@ -25,11 +25,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/error-report.h" #include "qemu/iov.h" #include "qemu/sockets.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "migration/migration.h" #include "migration/qemu-file.h" #include "migration/qemu-file-internal.h" @@ -372,7 +373,8 @@ typedef struct QEMUBuffer { bool qsb_allocated; } QEMUBuffer; -static int buf_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) +static ssize_t buf_get_buffer(void *opaque, uint8_t *buf, int64_t pos, + size_t size) { QEMUBuffer *s = opaque; ssize_t len = qsb_get_length(s->qsb) - pos; @@ -387,8 +389,8 @@ static int buf_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) return qsb_get_buffer(s->qsb, pos, len, buf); } -static int buf_put_buffer(void *opaque, const uint8_t *buf, - int64_t pos, int size) +static ssize_t buf_put_buffer(void *opaque, const uint8_t *buf, + int64_t pos, size_t size) { QEMUBuffer *s = opaque; @@ -439,7 +441,7 @@ QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input) return NULL; } - s = g_malloc0(sizeof(QEMUBuffer)); + s = g_new0(QEMUBuffer, 1); s->qsb = input; if (s->qsb == NULL) { diff --git a/qemu/migration/qemu-file-stdio.c b/qemu/migration/qemu-file-stdio.c index 285068b30..f402e8f70 100644 --- a/qemu/migration/qemu-file-stdio.c +++ b/qemu/migration/qemu-file-stdio.c @@ -21,8 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "migration/qemu-file.h" typedef struct QEMUFileStdio { @@ -37,11 +38,11 @@ static int stdio_get_fd(void *opaque) return fileno(s->stdio_file); } -static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, - int size) +static ssize_t stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, + size_t size) { QEMUFileStdio *s = opaque; - int res; + size_t res; res = fwrite(buf, 1, size, s->stdio_file); @@ -51,11 +52,12 @@ static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, return res; } -static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) +static ssize_t stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, + size_t size) { QEMUFileStdio *s = opaque; FILE *fp = s->stdio_file; - int bytes; + ssize_t bytes; for (;;) { clearerr(fp); @@ -143,7 +145,7 @@ QEMUFile *qemu_popen_cmd(const char *command, const char *mode) return NULL; } - s = g_malloc0(sizeof(QEMUFileStdio)); + s = g_new0(QEMUFileStdio, 1); s->stdio_file = stdio_file; @@ -175,7 +177,7 @@ QEMUFile *qemu_fopen(const char *filename, const char *mode) return NULL; } - s = g_malloc0(sizeof(QEMUFileStdio)); + s = g_new0(QEMUFileStdio, 1); s->stdio_file = fopen(filename, mode); if (!s->stdio_file) { diff --git a/qemu/migration/qemu-file-unix.c b/qemu/migration/qemu-file-unix.c index bfbc0861a..4474e18ff 100644 --- a/qemu/migration/qemu-file-unix.c +++ b/qemu/migration/qemu-file-unix.c @@ -21,10 +21,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" +#include "qemu/error-report.h" #include "qemu/iov.h" #include "qemu/sockets.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "migration/qemu-file.h" #include "migration/qemu-file-internal.h" @@ -39,12 +41,42 @@ static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, QEMUFileSocket *s = opaque; ssize_t len; ssize_t size = iov_size(iov, iovcnt); + ssize_t offset = 0; + int err; - len = iov_send(s->fd, iov, iovcnt, 0, size); - if (len < size) { - len = -socket_error(); - } - return len; + while (size > 0) { + len = iov_send(s->fd, iov, iovcnt, offset, size); + + if (len > 0) { + size -= len; + offset += len; + } + + if (size > 0) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + error_report("socket_writev_buffer: Got err=%d for (%zu/%zu)", + errno, (size_t)size, (size_t)len); + /* + * If I've already sent some but only just got the error, I + * could return the amount validly sent so far and wait for the + * next call to report the error, but I'd rather flag the error + * immediately. + */ + return -errno; + } + + /* Emulate blocking */ + GPollFD pfd; + + pfd.fd = s->fd; + pfd.events = G_IO_OUT | G_IO_ERR; + pfd.revents = 0; + TFR(err = g_poll(&pfd, 1, -1 /* no timeout */)); + /* Errors other than EINTR intentionally ignored */ + } + } + + return offset; } static int socket_get_fd(void *opaque) @@ -54,7 +86,8 @@ static int socket_get_fd(void *opaque) return s->fd; } -static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) +static ssize_t socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, + size_t size) { QEMUFileSocket *s = opaque; ssize_t len; @@ -64,15 +97,15 @@ static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) if (len != -1) { break; } - if (socket_error() == EAGAIN) { + if (errno == EAGAIN) { yield_until_fd_readable(s->fd); - } else if (socket_error() != EINTR) { + } else if (errno != EINTR) { break; } } if (len == -1) { - len = -socket_error(); + len = -errno; } return len; } @@ -96,6 +129,56 @@ static int socket_shutdown(void *opaque, bool rd, bool wr) } } +static int socket_return_close(void *opaque) +{ + QEMUFileSocket *s = opaque; + /* + * Note: We don't close the socket, that should be done by the forward + * path. + */ + g_free(s); + return 0; +} + +static const QEMUFileOps socket_return_read_ops = { + .get_fd = socket_get_fd, + .get_buffer = socket_get_buffer, + .close = socket_return_close, + .shut_down = socket_shutdown, +}; + +static const QEMUFileOps socket_return_write_ops = { + .get_fd = socket_get_fd, + .writev_buffer = socket_writev_buffer, + .close = socket_return_close, + .shut_down = socket_shutdown, +}; + +/* + * Give a QEMUFile* off the same socket but data in the opposite + * direction. + */ +static QEMUFile *socket_get_return_path(void *opaque) +{ + QEMUFileSocket *forward = opaque; + QEMUFileSocket *reverse; + + if (qemu_file_get_error(forward->file)) { + /* If the forward file is in error, don't try and open a return */ + return NULL; + } + + reverse = g_malloc0(sizeof(QEMUFileSocket)); + reverse->fd = forward->fd; + /* I don't think there's a better way to tell which direction 'this' is */ + if (forward->file->ops->get_buffer != NULL) { + /* being called from the read side, so we need to be able to write */ + return qemu_fopen_ops(reverse, &socket_return_write_ops); + } else { + return qemu_fopen_ops(reverse, &socket_return_read_ops); + } +} + static ssize_t unix_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, int64_t pos) { @@ -138,7 +221,8 @@ static ssize_t unix_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, return total; } -static int unix_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) +static ssize_t unix_get_buffer(void *opaque, uint8_t *buf, int64_t pos, + size_t size) { QEMUFileSocket *s = opaque; ssize_t len; @@ -192,7 +276,7 @@ QEMUFile *qemu_fdopen(int fd, const char *mode) return NULL; } - s = g_malloc0(sizeof(QEMUFileSocket)); + s = g_new0(QEMUFileSocket, 1); s->fd = fd; if (mode[0] == 'r') { @@ -204,18 +288,19 @@ QEMUFile *qemu_fdopen(int fd, const char *mode) } static const QEMUFileOps socket_read_ops = { - .get_fd = socket_get_fd, - .get_buffer = socket_get_buffer, - .close = socket_close, - .shut_down = socket_shutdown - + .get_fd = socket_get_fd, + .get_buffer = socket_get_buffer, + .close = socket_close, + .shut_down = socket_shutdown, + .get_return_path = socket_get_return_path }; static const QEMUFileOps socket_write_ops = { - .get_fd = socket_get_fd, - .writev_buffer = socket_writev_buffer, - .close = socket_close, - .shut_down = socket_shutdown + .get_fd = socket_get_fd, + .writev_buffer = socket_writev_buffer, + .close = socket_close, + .shut_down = socket_shutdown, + .get_return_path = socket_get_return_path }; QEMUFile *qemu_fopen_socket(int fd, const char *mode) @@ -226,7 +311,7 @@ QEMUFile *qemu_fopen_socket(int fd, const char *mode) return NULL; } - s = g_malloc0(sizeof(QEMUFileSocket)); + s = g_new0(QEMUFileSocket, 1); s->fd = fd; if (mode[0] == 'w') { qemu_set_block(s->fd); diff --git a/qemu/migration/qemu-file.c b/qemu/migration/qemu-file.c index 6bb3dc15c..6f4a1299b 100644 --- a/qemu/migration/qemu-file.c +++ b/qemu/migration/qemu-file.c @@ -21,12 +21,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include "qemu-common.h" #include "qemu/error-report.h" #include "qemu/iov.h" #include "qemu/sockets.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "migration/migration.h" #include "migration/qemu-file.h" #include "migration/qemu-file-internal.h" @@ -44,6 +45,18 @@ int qemu_file_shutdown(QEMUFile *f) return f->ops->shut_down(f->opaque, true, true); } +/* + * Result: QEMUFile* for a 'return path' for comms in the opposite direction + * NULL if not available + */ +QEMUFile *qemu_file_get_return_path(QEMUFile *f) +{ + if (!f->ops->get_return_path) { + return NULL; + } + return f->ops->get_return_path(f->opaque); +} + bool qemu_file_mode_is_not_valid(const char *mode) { if (mode == NULL || @@ -60,7 +73,7 @@ QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops) { QEMUFile *f; - f = g_malloc0(sizeof(QEMUFile)); + f = g_new0(QEMUFile, 1); f->opaque = opaque; f->ops = ops; @@ -270,7 +283,7 @@ int qemu_fclose(QEMUFile *f) return ret; } -static void add_to_iovec(QEMUFile *f, const uint8_t *buf, int size) +static void add_to_iovec(QEMUFile *f, const uint8_t *buf, size_t size) { /* check for adjacent buffer and coalesce them */ if (f->iovcnt > 0 && buf == f->iov[f->iovcnt - 1].iov_base + @@ -286,7 +299,7 @@ static void add_to_iovec(QEMUFile *f, const uint8_t *buf, int size) } } -void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, int size) +void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, size_t size) { if (!f->ops->writev_buffer) { qemu_put_buffer(f, buf, size); @@ -301,9 +314,9 @@ void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, int size) add_to_iovec(f, buf, size); } -void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) +void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, size_t size) { - int l; + size_t l; if (f->last_error) { return; @@ -363,10 +376,10 @@ void qemu_file_skip(QEMUFile *f, int size) * return as many as it managed to read (assuming blocking fd's which * all current QEMUFile are) */ -int qemu_peek_buffer(QEMUFile *f, uint8_t **buf, int size, size_t offset) +size_t qemu_peek_buffer(QEMUFile *f, uint8_t **buf, size_t size, size_t offset) { - int pending; - int index; + ssize_t pending; + size_t index; assert(!qemu_file_is_writable(f)); assert(offset < IO_BUF_SIZE); @@ -411,13 +424,13 @@ int qemu_peek_buffer(QEMUFile *f, uint8_t **buf, int size, size_t offset) * return as many as it managed to read (assuming blocking fd's which * all current QEMUFile are) */ -int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size) +size_t qemu_get_buffer(QEMUFile *f, uint8_t *buf, size_t size) { - int pending = size; - int done = 0; + size_t pending = size; + size_t done = 0; while (pending > 0) { - int res; + size_t res; uint8_t *src; res = qemu_peek_buffer(f, &src, MIN(pending, IO_BUF_SIZE), 0); @@ -433,6 +446,43 @@ int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size) return done; } +/* + * Read 'size' bytes of data from the file. + * 'size' can be larger than the internal buffer. + * + * The data: + * may be held on an internal buffer (in which case *buf is updated + * to point to it) that is valid until the next qemu_file operation. + * OR + * will be copied to the *buf that was passed in. + * + * The code tries to avoid the copy if possible. + * + * It will return size bytes unless there was an error, in which case it will + * return as many as it managed to read (assuming blocking fd's which + * all current QEMUFile are) + * + * Note: Since **buf may get changed, the caller should take care to + * keep a pointer to the original buffer if it needs to deallocate it. + */ +size_t qemu_get_buffer_in_place(QEMUFile *f, uint8_t **buf, size_t size) +{ + if (size < IO_BUF_SIZE) { + size_t res; + uint8_t *src; + + res = qemu_peek_buffer(f, &src, size, 0); + + if (res == size) { + qemu_file_skip(f, res); + *buf = src; + return res; + } + } + + return qemu_get_buffer(f, *buf, size); +} + /* * Peeks a single byte from the buffer; this isn't guaranteed to work if * offset leaves a gap after the previous read/peeked data. @@ -611,3 +661,18 @@ size_t qemu_get_counted_string(QEMUFile *f, char buf[256]) return res == len ? res : 0; } + +/* + * 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 + * QEMUFile can also affect the return path. + */ +void qemu_file_set_blocking(QEMUFile *f, bool block) +{ + if (block) { + qemu_set_block(qemu_get_fd(f)); + } else { + qemu_set_nonblock(qemu_get_fd(f)); + } +} diff --git a/qemu/migration/ram.c b/qemu/migration/ram.c index 6249f6e60..3f057388c 100644 --- a/qemu/migration/ram.c +++ b/qemu/migration/ram.c @@ -25,13 +25,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include +#include "qemu/osdep.h" #include +#include "qapi-event.h" +#include "qemu/cutils.h" #include "qemu/bitops.h" #include "qemu/bitmap.h" #include "qemu/timer.h" #include "qemu/main-loop.h" #include "migration/migration.h" +#include "migration/postcopy-ram.h" #include "exec/address-spaces.h" #include "migration/page_cache.h" #include "qemu/error-report.h" @@ -47,9 +50,7 @@ do { } while (0) #endif -static bool mig_throttle_on; static int dirty_rate_high_cnt; -static void check_guest_throttling(void); static uint64_t bitmap_sync_count; @@ -221,12 +222,34 @@ static RAMBlock *last_seen_block; /* This is the last block from where we have sent data */ static RAMBlock *last_sent_block; static ram_addr_t last_offset; -static unsigned long *migration_bitmap; static QemuMutex migration_bitmap_mutex; static uint64_t migration_dirty_pages; static uint32_t last_version; static bool ram_bulk_stage; +/* used by the search for pages to send */ +struct PageSearchStatus { + /* Current block being searched */ + RAMBlock *block; + /* Current offset to search from */ + ram_addr_t offset; + /* Set once we wrap around */ + bool complete_round; +}; +typedef struct PageSearchStatus PageSearchStatus; + +static struct BitmapRcu { + struct rcu_head rcu; + /* Main migration bitmap */ + unsigned long *bmap; + /* bitmap of pages that haven't been sent even once + * only maintained and used in postcopy at the moment + * where it's used to send the dirtymap at the start + * of the postcopy phase + */ + unsigned long *unsentmap; +} *migration_bitmap_rcu; + struct CompressParam { bool start; bool done; @@ -243,7 +266,7 @@ struct DecompressParam { QemuMutex mutex; QemuCond cond; void *des; - uint8 *compbuf; + uint8_t *compbuf; int len; }; typedef struct DecompressParam DecompressParam; @@ -264,7 +287,6 @@ static bool quit_comp_thread; static bool quit_decomp_thread; static DecompressParam *decomp_param; static QemuThread *decompress_threads; -static uint8_t *compressed_data_buf; static int do_compress_ram_page(CompressParam *param); @@ -396,6 +418,29 @@ static size_t save_page_header(QEMUFile *f, RAMBlock *block, ram_addr_t offset) return size; } +/* Reduce amount of guest cpu execution to hopefully slow down memory writes. + * If guest dirty memory rate is reduced below the rate at which we can + * transfer pages to the destination then we should be able to complete + * migration. Some workloads dirty memory way too fast and will not effectively + * converge, even with auto-converge. + */ +static void mig_throttle_guest_down(void) +{ + MigrationState *s = migrate_get_current(); + uint64_t pct_initial = + s->parameters[MIGRATION_PARAMETER_X_CPU_THROTTLE_INITIAL]; + uint64_t pct_icrement = + s->parameters[MIGRATION_PARAMETER_X_CPU_THROTTLE_INCREMENT]; + + /* We have not started throttling yet. Let's start it. */ + if (!cpu_throttle_active()) { + cpu_throttle_set(pct_initial); + } else { + /* Throttling already on, just increase the rate */ + cpu_throttle_set(cpu_throttle_get_percentage() + pct_icrement); + } +} + /* Update the xbzrle cache to reflect a page that's been sent as all 0. * The important thing is that a stale (not-yet-0'd) page be replaced * by the new data. @@ -495,43 +540,60 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t **current_data, return 1; } -/* Called with rcu_read_lock() to protect migration_bitmap */ +/* Called with rcu_read_lock() to protect migration_bitmap + * rb: The RAMBlock to search for dirty pages in + * start: Start address (typically so we can continue from previous page) + * ram_addr_abs: Pointer into which to store the address of the dirty page + * within the global ram_addr space + * + * Returns: byte offset within memory region of the start of a dirty page + */ static inline -ram_addr_t migration_bitmap_find_and_reset_dirty(MemoryRegion *mr, - ram_addr_t start) +ram_addr_t migration_bitmap_find_dirty(RAMBlock *rb, + ram_addr_t start, + ram_addr_t *ram_addr_abs) { - unsigned long base = mr->ram_addr >> TARGET_PAGE_BITS; + unsigned long base = rb->offset >> TARGET_PAGE_BITS; unsigned long nr = base + (start >> TARGET_PAGE_BITS); - uint64_t mr_size = TARGET_PAGE_ALIGN(memory_region_size(mr)); - unsigned long size = base + (mr_size >> TARGET_PAGE_BITS); + uint64_t rb_size = rb->used_length; + unsigned long size = base + (rb_size >> TARGET_PAGE_BITS); unsigned long *bitmap; unsigned long next; - bitmap = atomic_rcu_read(&migration_bitmap); + bitmap = atomic_rcu_read(&migration_bitmap_rcu)->bmap; if (ram_bulk_stage && nr > base) { next = nr + 1; } else { next = find_next_bit(bitmap, size, nr); } - if (next < size) { - clear_bit(next, bitmap); + *ram_addr_abs = next << TARGET_PAGE_BITS; + return (next - base) << TARGET_PAGE_BITS; +} + +static inline bool migration_bitmap_clear_dirty(ram_addr_t addr) +{ + bool ret; + int nr = addr >> TARGET_PAGE_BITS; + unsigned long *bitmap = atomic_rcu_read(&migration_bitmap_rcu)->bmap; + + ret = test_and_clear_bit(nr, bitmap); + + if (ret) { migration_dirty_pages--; } - return (next - base) << TARGET_PAGE_BITS; + return ret; } -/* Called with rcu_read_lock() to protect migration_bitmap */ static void migration_bitmap_sync_range(ram_addr_t start, ram_addr_t length) { unsigned long *bitmap; - bitmap = atomic_rcu_read(&migration_bitmap); + bitmap = atomic_rcu_read(&migration_bitmap_rcu)->bmap; migration_dirty_pages += cpu_physical_memory_sync_dirty_bitmap(bitmap, start, length); } - /* Fix me: there are too many global variables used in migration process. */ static int64_t start_time; static int64_t bytes_xfer_prev; @@ -548,7 +610,6 @@ static void migration_bitmap_sync_init(void) iterations_prev = 0; } -/* Called with iothread lock held, to protect ram_list.dirty_memory[] */ static void migration_bitmap_sync(void) { RAMBlock *block; @@ -573,7 +634,7 @@ static void migration_bitmap_sync(void) qemu_mutex_lock(&migration_bitmap_mutex); rcu_read_lock(); QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { - migration_bitmap_sync_range(block->mr->ram_addr, block->used_length); + migration_bitmap_sync_range(block->offset, block->used_length); } rcu_read_unlock(); qemu_mutex_unlock(&migration_bitmap_mutex); @@ -589,21 +650,21 @@ static void migration_bitmap_sync(void) /* The following detection logic can be refined later. For now: Check to see if the dirtied bytes is 50% more than the approx. amount of bytes that just got transferred since the last time we - were in this routine. If that happens >N times (for now N==4) - we turn on the throttle down logic */ + were in this routine. If that happens twice, start or increase + throttling */ bytes_xfer_now = ram_bytes_transferred(); + if (s->dirty_pages_rate && (num_dirty_pages_period * TARGET_PAGE_SIZE > (bytes_xfer_now - bytes_xfer_prev)/2) && - (dirty_rate_high_cnt++ > 4)) { + (dirty_rate_high_cnt++ >= 2)) { trace_migration_throttle(); - mig_throttle_on = true; dirty_rate_high_cnt = 0; + mig_throttle_guest_down(); } bytes_xfer_prev = bytes_xfer_now; - } else { - mig_throttle_on = false; } + if (migrate_use_xbzrle()) { if (iterations_prev != acct_info.iterations) { acct_info.xbzrle_cache_miss_rate = @@ -621,6 +682,9 @@ static void migration_bitmap_sync(void) num_dirty_pages_period = 0; } s->dirty_sync_count = bitmap_sync_count; + if (migrate_use_events()) { + qapi_event_send_migration_pass(bitmap_sync_count, NULL); + } } /** @@ -655,6 +719,9 @@ static int save_zero_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset, * ram_save_page: Send the given page to the stream * * Returns: Number of pages written. + * < 0 - error + * >=0 - Number of pages written - this might legally be 0 + * if xbzrle noticed the page was the same. * * @f: QEMUFile where to send the data * @block: block that contains the page we want to send @@ -662,18 +729,19 @@ static int save_zero_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset, * @last_stage: if we are at the completion stage * @bytes_transferred: increase it with the number of transferred bytes */ -static int ram_save_page(QEMUFile *f, RAMBlock* block, ram_addr_t offset, +static int ram_save_page(QEMUFile *f, PageSearchStatus *pss, bool last_stage, uint64_t *bytes_transferred) { int pages = -1; uint64_t bytes_xmit; ram_addr_t current_addr; - MemoryRegion *mr = block->mr; uint8_t *p; int ret; bool send_async = true; + RAMBlock *block = pss->block; + ram_addr_t offset = pss->offset; - p = memory_region_get_ram_ptr(mr) + offset; + p = block->host + offset; /* In doubt sent page as normal */ bytes_xmit = 0; @@ -744,7 +812,7 @@ static int do_compress_ram_page(CompressParam *param) RAMBlock *block = param->block; ram_addr_t offset = param->offset; - p = memory_region_get_ram_ptr(block->mr) + (offset & TARGET_PAGE_MASK); + p = block->host + (offset & TARGET_PAGE_MASK); bytes_sent = save_page_header(param->file, block, offset | RAM_SAVE_FLAG_COMPRESS_PAGE); @@ -846,17 +914,18 @@ static int compress_page_with_multi_thread(QEMUFile *f, RAMBlock *block, * @last_stage: if we are at the completion stage * @bytes_transferred: increase it with the number of transferred bytes */ -static int ram_save_compressed_page(QEMUFile *f, RAMBlock *block, - ram_addr_t offset, bool last_stage, +static int ram_save_compressed_page(QEMUFile *f, PageSearchStatus *pss, + bool last_stage, uint64_t *bytes_transferred) { int pages = -1; uint64_t bytes_xmit; - MemoryRegion *mr = block->mr; uint8_t *p; int ret; + RAMBlock *block = pss->block; + ram_addr_t offset = pss->offset; - p = memory_region_get_ram_ptr(mr) + offset; + p = block->host + offset; bytes_xmit = 0; ret = ram_control_save_page(f, block->offset, @@ -909,6 +978,340 @@ static int ram_save_compressed_page(QEMUFile *f, RAMBlock *block, return pages; } +/* + * Find the next dirty page and update any state associated with + * the search process. + * + * Returns: True if a page is found + * + * @f: Current migration stream. + * @pss: Data about the state of the current dirty page scan. + * @*again: Set to false if the search has scanned the whole of RAM + * *ram_addr_abs: Pointer into which to store the address of the dirty page + * within the global ram_addr space + */ +static bool find_dirty_block(QEMUFile *f, PageSearchStatus *pss, + bool *again, ram_addr_t *ram_addr_abs) +{ + pss->offset = migration_bitmap_find_dirty(pss->block, pss->offset, + ram_addr_abs); + if (pss->complete_round && pss->block == last_seen_block && + pss->offset >= last_offset) { + /* + * We've been once around the RAM and haven't found anything. + * Give up. + */ + *again = false; + return false; + } + if (pss->offset >= pss->block->used_length) { + /* Didn't find anything in this RAM Block */ + pss->offset = 0; + pss->block = QLIST_NEXT_RCU(pss->block, next); + if (!pss->block) { + /* Hit the end of the list */ + pss->block = QLIST_FIRST_RCU(&ram_list.blocks); + /* Flag that we've looped */ + pss->complete_round = true; + ram_bulk_stage = false; + if (migrate_use_xbzrle()) { + /* If xbzrle is on, stop using the data compression at this + * point. In theory, xbzrle can do better than compression. + */ + flush_compressed_data(f); + compression_switch = false; + } + } + /* Didn't find anything this time, but try again on the new block */ + *again = true; + return false; + } else { + /* Can go around again, but... */ + *again = true; + /* We've found something so probably don't need to */ + return true; + } +} + +/* + * Helper for 'get_queued_page' - gets a page off the queue + * ms: MigrationState in + * *offset: Used to return the offset within the RAMBlock + * ram_addr_abs: global offset in the dirty/sent bitmaps + * + * Returns: block (or NULL if none available) + */ +static RAMBlock *unqueue_page(MigrationState *ms, ram_addr_t *offset, + ram_addr_t *ram_addr_abs) +{ + RAMBlock *block = NULL; + + qemu_mutex_lock(&ms->src_page_req_mutex); + if (!QSIMPLEQ_EMPTY(&ms->src_page_requests)) { + struct MigrationSrcPageRequest *entry = + QSIMPLEQ_FIRST(&ms->src_page_requests); + block = entry->rb; + *offset = entry->offset; + *ram_addr_abs = (entry->offset + entry->rb->offset) & + TARGET_PAGE_MASK; + + if (entry->len > TARGET_PAGE_SIZE) { + entry->len -= TARGET_PAGE_SIZE; + entry->offset += TARGET_PAGE_SIZE; + } else { + memory_region_unref(block->mr); + QSIMPLEQ_REMOVE_HEAD(&ms->src_page_requests, next_req); + g_free(entry); + } + } + qemu_mutex_unlock(&ms->src_page_req_mutex); + + return block; +} + +/* + * Unqueue a page from the queue fed by postcopy page requests; skips pages + * that are already sent (!dirty) + * + * ms: MigrationState in + * pss: PageSearchStatus structure updated with found block/offset + * ram_addr_abs: global offset in the dirty/sent bitmaps + * + * Returns: true if a queued page is found + */ +static bool get_queued_page(MigrationState *ms, PageSearchStatus *pss, + ram_addr_t *ram_addr_abs) +{ + RAMBlock *block; + ram_addr_t offset; + bool dirty; + + do { + block = unqueue_page(ms, &offset, ram_addr_abs); + /* + * We're sending this page, and since it's postcopy nothing else + * will dirty it, and we must make sure it doesn't get sent again + * even if this queue request was received after the background + * search already sent it. + */ + if (block) { + unsigned long *bitmap; + bitmap = atomic_rcu_read(&migration_bitmap_rcu)->bmap; + dirty = test_bit(*ram_addr_abs >> TARGET_PAGE_BITS, bitmap); + if (!dirty) { + trace_get_queued_page_not_dirty( + block->idstr, (uint64_t)offset, + (uint64_t)*ram_addr_abs, + test_bit(*ram_addr_abs >> TARGET_PAGE_BITS, + atomic_rcu_read(&migration_bitmap_rcu)->unsentmap)); + } else { + trace_get_queued_page(block->idstr, + (uint64_t)offset, + (uint64_t)*ram_addr_abs); + } + } + + } while (block && !dirty); + + if (block) { + /* + * As soon as we start servicing pages out of order, then we have + * to kill the bulk stage, since the bulk stage assumes + * in (migration_bitmap_find_and_reset_dirty) that every page is + * dirty, that's no longer true. + */ + ram_bulk_stage = false; + + /* + * We want the background search to continue from the queued page + * since the guest is likely to want other pages near to the page + * it just requested. + */ + pss->block = block; + pss->offset = offset; + } + + return !!block; +} + +/** + * flush_page_queue: Flush any remaining pages in the ram request queue + * it should be empty at the end anyway, but in error cases there may be + * some left. + * + * ms: MigrationState + */ +void flush_page_queue(MigrationState *ms) +{ + struct MigrationSrcPageRequest *mspr, *next_mspr; + /* This queue generally should be empty - but in the case of a failed + * migration might have some droppings in. + */ + rcu_read_lock(); + QSIMPLEQ_FOREACH_SAFE(mspr, &ms->src_page_requests, next_req, next_mspr) { + memory_region_unref(mspr->rb->mr); + QSIMPLEQ_REMOVE_HEAD(&ms->src_page_requests, next_req); + g_free(mspr); + } + rcu_read_unlock(); +} + +/** + * Queue the pages for transmission, e.g. a request from postcopy destination + * ms: MigrationStatus in which the queue is held + * rbname: The RAMBlock the request is for - may be NULL (to mean reuse last) + * start: Offset from the start of the RAMBlock + * len: Length (in bytes) to send + * Return: 0 on success + */ +int ram_save_queue_pages(MigrationState *ms, const char *rbname, + ram_addr_t start, ram_addr_t len) +{ + RAMBlock *ramblock; + + rcu_read_lock(); + if (!rbname) { + /* Reuse last RAMBlock */ + ramblock = ms->last_req_rb; + + if (!ramblock) { + /* + * Shouldn't happen, we can't reuse the last RAMBlock if + * it's the 1st request. + */ + error_report("ram_save_queue_pages no previous block"); + goto err; + } + } else { + ramblock = qemu_ram_block_by_name(rbname); + + if (!ramblock) { + /* We shouldn't be asked for a non-existent RAMBlock */ + error_report("ram_save_queue_pages no block '%s'", rbname); + goto err; + } + ms->last_req_rb = ramblock; + } + trace_ram_save_queue_pages(ramblock->idstr, start, len); + if (start+len > ramblock->used_length) { + error_report("%s request overrun start=" RAM_ADDR_FMT " len=" + RAM_ADDR_FMT " blocklen=" RAM_ADDR_FMT, + __func__, start, len, ramblock->used_length); + goto err; + } + + struct MigrationSrcPageRequest *new_entry = + g_malloc0(sizeof(struct MigrationSrcPageRequest)); + new_entry->rb = ramblock; + new_entry->offset = start; + new_entry->len = len; + + memory_region_ref(ramblock->mr); + qemu_mutex_lock(&ms->src_page_req_mutex); + QSIMPLEQ_INSERT_TAIL(&ms->src_page_requests, new_entry, next_req); + qemu_mutex_unlock(&ms->src_page_req_mutex); + rcu_read_unlock(); + + return 0; + +err: + rcu_read_unlock(); + return -1; +} + +/** + * ram_save_target_page: Save one target page + * + * + * @f: QEMUFile where to send the data + * @block: pointer to block that contains the page we want to send + * @offset: offset inside the block for the page; + * @last_stage: if we are at the completion stage + * @bytes_transferred: increase it with the number of transferred bytes + * @dirty_ram_abs: Address of the start of the dirty page in ram_addr_t space + * + * Returns: Number of pages written. + */ +static int ram_save_target_page(MigrationState *ms, QEMUFile *f, + PageSearchStatus *pss, + bool last_stage, + uint64_t *bytes_transferred, + ram_addr_t dirty_ram_abs) +{ + int res = 0; + + /* Check the pages is dirty and if it is send it */ + if (migration_bitmap_clear_dirty(dirty_ram_abs)) { + unsigned long *unsentmap; + if (compression_switch && migrate_use_compression()) { + res = ram_save_compressed_page(f, pss, + last_stage, + bytes_transferred); + } else { + res = ram_save_page(f, pss, last_stage, + bytes_transferred); + } + + if (res < 0) { + return res; + } + unsentmap = atomic_rcu_read(&migration_bitmap_rcu)->unsentmap; + if (unsentmap) { + clear_bit(dirty_ram_abs >> TARGET_PAGE_BITS, unsentmap); + } + /* Only update last_sent_block if a block was actually sent; xbzrle + * might have decided the page was identical so didn't bother writing + * to the stream. + */ + if (res > 0) { + last_sent_block = pss->block; + } + } + + return res; +} + +/** + * ram_save_host_page: Starting at *offset send pages upto the end + * of the current host page. It's valid for the initial + * offset to point into the middle of a host page + * in which case the remainder of the hostpage is sent. + * Only dirty target pages are sent. + * + * Returns: Number of pages written. + * + * @f: QEMUFile where to send the data + * @block: pointer to block that contains the page we want to send + * @offset: offset inside the block for the page; updated to last target page + * sent + * @last_stage: if we are at the completion stage + * @bytes_transferred: increase it with the number of transferred bytes + * @dirty_ram_abs: Address of the start of the dirty page in ram_addr_t space + */ +static int ram_save_host_page(MigrationState *ms, QEMUFile *f, + PageSearchStatus *pss, + bool last_stage, + uint64_t *bytes_transferred, + ram_addr_t dirty_ram_abs) +{ + int tmppages, pages = 0; + do { + tmppages = ram_save_target_page(ms, f, pss, last_stage, + bytes_transferred, dirty_ram_abs); + if (tmppages < 0) { + return tmppages; + } + + pages += tmppages; + pss->offset += TARGET_PAGE_SIZE; + dirty_ram_abs += TARGET_PAGE_SIZE; + } while (pss->offset & (qemu_host_page_size - 1)); + + /* The offset we leave with is the last one we looked at */ + pss->offset -= TARGET_PAGE_SIZE; + return pages; +} + /** * ram_find_and_save_block: Finds a dirty page and sends it to f * @@ -920,61 +1323,47 @@ static int ram_save_compressed_page(QEMUFile *f, RAMBlock *block, * @f: QEMUFile where to send the data * @last_stage: if we are at the completion stage * @bytes_transferred: increase it with the number of transferred bytes + * + * On systems where host-page-size > target-page-size it will send all the + * pages in a host page that are dirty. */ static int ram_find_and_save_block(QEMUFile *f, bool last_stage, uint64_t *bytes_transferred) { - RAMBlock *block = last_seen_block; - ram_addr_t offset = last_offset; - bool complete_round = false; + PageSearchStatus pss; + MigrationState *ms = migrate_get_current(); int pages = 0; - MemoryRegion *mr; + bool again, found; + ram_addr_t dirty_ram_abs; /* Address of the start of the dirty page in + ram_addr_t space */ - if (!block) - block = QLIST_FIRST_RCU(&ram_list.blocks); + pss.block = last_seen_block; + pss.offset = last_offset; + pss.complete_round = false; - while (true) { - mr = block->mr; - offset = migration_bitmap_find_and_reset_dirty(mr, offset); - if (complete_round && block == last_seen_block && - offset >= last_offset) { - break; + if (!pss.block) { + pss.block = QLIST_FIRST_RCU(&ram_list.blocks); + } + + do { + again = true; + found = get_queued_page(ms, &pss, &dirty_ram_abs); + + if (!found) { + /* priority queue empty, so just search for something dirty */ + found = find_dirty_block(f, &pss, &again, &dirty_ram_abs); } - if (offset >= block->used_length) { - offset = 0; - block = QLIST_NEXT_RCU(block, next); - if (!block) { - block = QLIST_FIRST_RCU(&ram_list.blocks); - complete_round = true; - ram_bulk_stage = false; - if (migrate_use_xbzrle()) { - /* If xbzrle is on, stop using the data compression at this - * point. In theory, xbzrle can do better than compression. - */ - flush_compressed_data(f); - compression_switch = false; - } - } - } else { - if (compression_switch && migrate_use_compression()) { - pages = ram_save_compressed_page(f, block, offset, last_stage, - bytes_transferred); - } else { - pages = ram_save_page(f, block, offset, last_stage, - bytes_transferred); - } - /* if page is unmodified, continue to the next */ - if (pages > 0) { - last_sent_block = block; - break; - } + if (found) { + pages = ram_save_host_page(ms, f, &pss, + last_stage, bytes_transferred, + dirty_ram_abs); } - } + } while (!pages && again); - last_seen_block = block; - last_offset = offset; + last_seen_block = pss.block; + last_offset = pss.offset; return pages; } @@ -1024,17 +1413,23 @@ void free_xbzrle_decoded_buf(void) xbzrle_decoded_buf = NULL; } -static void migration_end(void) +static void migration_bitmap_free(struct BitmapRcu *bmap) +{ + g_free(bmap->bmap); + g_free(bmap->unsentmap); + g_free(bmap); +} + +static void ram_migration_cleanup(void *opaque) { /* caller have hold iothread lock or is in a bh, so there is * no writing race against this migration_bitmap */ - unsigned long *bitmap = migration_bitmap; - atomic_rcu_set(&migration_bitmap, NULL); + struct BitmapRcu *bitmap = migration_bitmap_rcu; + atomic_rcu_set(&migration_bitmap_rcu, NULL); if (bitmap) { memory_global_dirty_log_stop(); - synchronize_rcu(); - g_free(bitmap); + call_rcu(bitmap, migration_bitmap_free, rcu); } XBZRLE_cache_lock(); @@ -1049,11 +1444,6 @@ static void migration_end(void) XBZRLE_cache_unlock(); } -static void ram_migration_cancel(void *opaque) -{ - migration_end(); -} - static void reset_ram_globals(void) { last_seen_block = NULL; @@ -1070,9 +1460,10 @@ void migration_bitmap_extend(ram_addr_t old, ram_addr_t new) /* called in qemu main thread, so there is * no writing race against this migration_bitmap */ - if (migration_bitmap) { - unsigned long *old_bitmap = migration_bitmap, *bitmap; - bitmap = bitmap_new(new); + if (migration_bitmap_rcu) { + struct BitmapRcu *old_bitmap = migration_bitmap_rcu, *bitmap; + bitmap = g_new(struct BitmapRcu, 1); + bitmap->bmap = bitmap_new(new); /* prevent migration_bitmap content from being set bit * by migration_bitmap_sync_range() at the same time. @@ -1080,16 +1471,410 @@ void migration_bitmap_extend(ram_addr_t old, ram_addr_t new) * at the same time. */ qemu_mutex_lock(&migration_bitmap_mutex); - bitmap_copy(bitmap, old_bitmap, old); - bitmap_set(bitmap, old, new - old); - atomic_rcu_set(&migration_bitmap, bitmap); + bitmap_copy(bitmap->bmap, old_bitmap->bmap, old); + bitmap_set(bitmap->bmap, old, new - old); + + /* We don't have a way to safely extend the sentmap + * with RCU; so mark it as missing, entry to postcopy + * will fail. + */ + bitmap->unsentmap = NULL; + + atomic_rcu_set(&migration_bitmap_rcu, bitmap); qemu_mutex_unlock(&migration_bitmap_mutex); migration_dirty_pages += new - old; - synchronize_rcu(); - g_free(old_bitmap); + call_rcu(old_bitmap, migration_bitmap_free, rcu); + } +} + +/* + * 'expected' is the value you expect the bitmap mostly to be full + * of; it won't bother printing lines that are all this value. + * If 'todump' is null the migration bitmap is dumped. + */ +void ram_debug_dump_bitmap(unsigned long *todump, bool expected) +{ + int64_t ram_pages = last_ram_offset() >> TARGET_PAGE_BITS; + + int64_t cur; + int64_t linelen = 128; + char linebuf[129]; + + if (!todump) { + todump = atomic_rcu_read(&migration_bitmap_rcu)->bmap; + } + + for (cur = 0; cur < ram_pages; cur += linelen) { + int64_t curb; + bool found = false; + /* + * Last line; catch the case where the line length + * is longer than remaining ram + */ + if (cur + linelen > ram_pages) { + linelen = ram_pages - cur; + } + for (curb = 0; curb < linelen; curb++) { + bool thisbit = test_bit(cur + curb, todump); + linebuf[curb] = thisbit ? '1' : '.'; + found = found || (thisbit != expected); + } + if (found) { + linebuf[curb] = '\0'; + fprintf(stderr, "0x%08" PRIx64 " : %s\n", cur, linebuf); + } + } +} + +/* **** functions for postcopy ***** */ + +/* + * Callback from postcopy_each_ram_send_discard for each RAMBlock + * Note: At this point the 'unsentmap' is the processed bitmap combined + * with the dirtymap; so a '1' means it's either dirty or unsent. + * start,length: Indexes into the bitmap for the first bit + * representing the named block and length in target-pages + */ +static int postcopy_send_discard_bm_ram(MigrationState *ms, + PostcopyDiscardState *pds, + unsigned long start, + unsigned long length) +{ + unsigned long end = start + length; /* one after the end */ + unsigned long current; + unsigned long *unsentmap; + + unsentmap = atomic_rcu_read(&migration_bitmap_rcu)->unsentmap; + for (current = start; current < end; ) { + unsigned long one = find_next_bit(unsentmap, end, current); + + if (one <= end) { + unsigned long zero = find_next_zero_bit(unsentmap, end, one + 1); + unsigned long discard_length; + + if (zero >= end) { + discard_length = end - one; + } else { + discard_length = zero - one; + } + postcopy_discard_send_range(ms, pds, one, discard_length); + current = one + discard_length; + } else { + current = one; + } + } + + return 0; +} + +/* + * Utility for the outgoing postcopy code. + * Calls postcopy_send_discard_bm_ram for each RAMBlock + * passing it bitmap indexes and name. + * Returns: 0 on success + * (qemu_ram_foreach_block ends up passing unscaled lengths + * which would mean postcopy code would have to deal with target page) + */ +static int postcopy_each_ram_send_discard(MigrationState *ms) +{ + struct RAMBlock *block; + int ret; + + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + unsigned long first = block->offset >> TARGET_PAGE_BITS; + PostcopyDiscardState *pds = postcopy_discard_send_init(ms, + first, + block->idstr); + + /* + * Postcopy sends chunks of bitmap over the wire, but it + * just needs indexes at this point, avoids it having + * target page specific code. + */ + ret = postcopy_send_discard_bm_ram(ms, pds, first, + block->used_length >> TARGET_PAGE_BITS); + postcopy_discard_send_finish(ms, pds); + if (ret) { + return ret; + } + } + + return 0; +} + +/* + * Helper for postcopy_chunk_hostpages; it's called twice to cleanup + * the two bitmaps, that are similar, but one is inverted. + * + * We search for runs of target-pages that don't start or end on a + * host page boundary; + * unsent_pass=true: Cleans up partially unsent host pages by searching + * the unsentmap + * unsent_pass=false: Cleans up partially dirty host pages by searching + * the main migration bitmap + * + */ +static void postcopy_chunk_hostpages_pass(MigrationState *ms, bool unsent_pass, + RAMBlock *block, + PostcopyDiscardState *pds) +{ + unsigned long *bitmap; + unsigned long *unsentmap; + unsigned int host_ratio = qemu_host_page_size / TARGET_PAGE_SIZE; + unsigned long first = block->offset >> TARGET_PAGE_BITS; + unsigned long len = block->used_length >> TARGET_PAGE_BITS; + unsigned long last = first + (len - 1); + unsigned long run_start; + + bitmap = atomic_rcu_read(&migration_bitmap_rcu)->bmap; + unsentmap = atomic_rcu_read(&migration_bitmap_rcu)->unsentmap; + + if (unsent_pass) { + /* Find a sent page */ + run_start = find_next_zero_bit(unsentmap, last + 1, first); + } else { + /* Find a dirty page */ + run_start = find_next_bit(bitmap, last + 1, first); + } + + while (run_start <= last) { + bool do_fixup = false; + unsigned long fixup_start_addr; + unsigned long host_offset; + + /* + * If the start of this run of pages is in the middle of a host + * page, then we need to fixup this host page. + */ + host_offset = run_start % host_ratio; + if (host_offset) { + do_fixup = true; + run_start -= host_offset; + fixup_start_addr = run_start; + /* For the next pass */ + run_start = run_start + host_ratio; + } else { + /* Find the end of this run */ + unsigned long run_end; + if (unsent_pass) { + run_end = find_next_bit(unsentmap, last + 1, run_start + 1); + } else { + run_end = find_next_zero_bit(bitmap, last + 1, run_start + 1); + } + /* + * If the end isn't at the start of a host page, then the + * run doesn't finish at the end of a host page + * and we need to discard. + */ + host_offset = run_end % host_ratio; + if (host_offset) { + do_fixup = true; + fixup_start_addr = run_end - host_offset; + /* + * This host page has gone, the next loop iteration starts + * from after the fixup + */ + run_start = fixup_start_addr + host_ratio; + } else { + /* + * No discards on this iteration, next loop starts from + * next sent/dirty page + */ + run_start = run_end + 1; + } + } + + if (do_fixup) { + unsigned long page; + + /* Tell the destination to discard this page */ + if (unsent_pass || !test_bit(fixup_start_addr, unsentmap)) { + /* For the unsent_pass we: + * discard partially sent pages + * For the !unsent_pass (dirty) we: + * discard partially dirty pages that were sent + * (any partially sent pages were already discarded + * by the previous unsent_pass) + */ + postcopy_discard_send_range(ms, pds, fixup_start_addr, + host_ratio); + } + + /* Clean up the bitmap */ + for (page = fixup_start_addr; + page < fixup_start_addr + host_ratio; page++) { + /* All pages in this host page are now not sent */ + set_bit(page, unsentmap); + + /* + * Remark them as dirty, updating the count for any pages + * that weren't previously dirty. + */ + migration_dirty_pages += !test_and_set_bit(page, bitmap); + } + } + + if (unsent_pass) { + /* Find the next sent page for the next iteration */ + run_start = find_next_zero_bit(unsentmap, last + 1, + run_start); + } else { + /* Find the next dirty page for the next iteration */ + run_start = find_next_bit(bitmap, last + 1, run_start); + } + } +} + +/* + * Utility for the outgoing postcopy code. + * + * Discard any partially sent host-page size chunks, mark any partially + * dirty host-page size chunks as all dirty. + * + * Returns: 0 on success + */ +static int postcopy_chunk_hostpages(MigrationState *ms) +{ + struct RAMBlock *block; + + if (qemu_host_page_size == TARGET_PAGE_SIZE) { + /* Easy case - TPS==HPS - nothing to be done */ + return 0; + } + + /* Easiest way to make sure we don't resume in the middle of a host-page */ + last_seen_block = NULL; + last_sent_block = NULL; + last_offset = 0; + + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + unsigned long first = block->offset >> TARGET_PAGE_BITS; + + PostcopyDiscardState *pds = + postcopy_discard_send_init(ms, first, block->idstr); + + /* First pass: Discard all partially sent host pages */ + postcopy_chunk_hostpages_pass(ms, true, block, pds); + /* + * Second pass: Ensure that all partially dirty host pages are made + * fully dirty. + */ + postcopy_chunk_hostpages_pass(ms, false, block, pds); + + postcopy_discard_send_finish(ms, pds); + } /* ram_list loop */ + + return 0; +} + +/* + * Transmit the set of pages to be discarded after precopy to the target + * these are pages that: + * a) Have been previously transmitted but are now dirty again + * b) Pages that have never been transmitted, this ensures that + * any pages on the destination that have been mapped by background + * tasks get discarded (transparent huge pages is the specific concern) + * Hopefully this is pretty sparse + */ +int ram_postcopy_send_discard_bitmap(MigrationState *ms) +{ + int ret; + unsigned long *bitmap, *unsentmap; + + rcu_read_lock(); + + /* This should be our last sync, the src is now paused */ + migration_bitmap_sync(); + + unsentmap = atomic_rcu_read(&migration_bitmap_rcu)->unsentmap; + if (!unsentmap) { + /* We don't have a safe way to resize the sentmap, so + * if the bitmap was resized it will be NULL at this + * point. + */ + error_report("migration ram resized during precopy phase"); + rcu_read_unlock(); + return -EINVAL; + } + + /* Deal with TPS != HPS */ + ret = postcopy_chunk_hostpages(ms); + if (ret) { + rcu_read_unlock(); + return ret; + } + + /* + * Update the unsentmap to be unsentmap = unsentmap | dirty + */ + bitmap = atomic_rcu_read(&migration_bitmap_rcu)->bmap; + bitmap_or(unsentmap, unsentmap, bitmap, + last_ram_offset() >> TARGET_PAGE_BITS); + + + trace_ram_postcopy_send_discard_bitmap(); +#ifdef DEBUG_POSTCOPY + ram_debug_dump_bitmap(unsentmap, true); +#endif + + ret = postcopy_each_ram_send_discard(ms); + rcu_read_unlock(); + + return ret; +} + +/* + * At the start of the postcopy phase of migration, any now-dirty + * precopied pages are discarded. + * + * start, length describe a byte address range within the RAMBlock + * + * Returns 0 on success. + */ +int ram_discard_range(MigrationIncomingState *mis, + const char *block_name, + uint64_t start, size_t length) +{ + int ret = -1; + + rcu_read_lock(); + RAMBlock *rb = qemu_ram_block_by_name(block_name); + + if (!rb) { + error_report("ram_discard_range: Failed to find block '%s'", + block_name); + goto err; } + + uint8_t *host_startaddr = rb->host + start; + + if ((uintptr_t)host_startaddr & (qemu_host_page_size - 1)) { + error_report("ram_discard_range: Unaligned start address: %p", + host_startaddr); + goto err; + } + + if ((start + length) <= rb->used_length) { + uint8_t *host_endaddr = host_startaddr + length; + if ((uintptr_t)host_endaddr & (qemu_host_page_size - 1)) { + error_report("ram_discard_range: Unaligned end address: %p", + host_endaddr); + goto err; + } + ret = postcopy_ram_discard_range(mis, host_startaddr, length); + } else { + error_report("ram_discard_range: Overrun block '%s' (%" PRIu64 + "/%zx/" RAM_ADDR_FMT")", + block_name, start, length, rb->used_length); + } + +err: + rcu_read_unlock(); + + return ret; } + /* Each of ram_save_setup, ram_save_iterate and ram_save_complete has * long-running RCU critical section. When rcu-reclaims in the code * start to become numerous it will be necessary to reduce the @@ -1101,7 +1886,6 @@ static int ram_save_setup(QEMUFile *f, void *opaque) RAMBlock *block; int64_t ram_bitmap_pages; /* Size of bitmap in pages, including gaps */ - mig_throttle_on = false; dirty_rate_high_cnt = 0; bitmap_sync_count = 0; migration_bitmap_sync_init(); @@ -1137,16 +1921,23 @@ static int ram_save_setup(QEMUFile *f, void *opaque) acct_clear(); } - /* iothread lock needed for ram_list.dirty_memory[] */ + /* For memory_global_dirty_log_start below. */ qemu_mutex_lock_iothread(); + qemu_mutex_lock_ramlist(); rcu_read_lock(); bytes_transferred = 0; reset_ram_globals(); ram_bitmap_pages = last_ram_offset() >> TARGET_PAGE_BITS; - migration_bitmap = bitmap_new(ram_bitmap_pages); - bitmap_set(migration_bitmap, 0, ram_bitmap_pages); + migration_bitmap_rcu = g_new0(struct BitmapRcu, 1); + migration_bitmap_rcu->bmap = bitmap_new(ram_bitmap_pages); + bitmap_set(migration_bitmap_rcu->bmap, 0, ram_bitmap_pages); + + if (migrate_postcopy_ram()) { + migration_bitmap_rcu->unsentmap = bitmap_new(ram_bitmap_pages); + bitmap_set(migration_bitmap_rcu->unsentmap, 0, ram_bitmap_pages); + } /* * Count the total number of pages used by ram blocks not including any @@ -1206,7 +1997,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) } pages_sent += pages; acct_info.iterations++; - check_guest_throttling(); + /* we want to check in the 1st loop, just in case it was the 1st time and we had to sync the dirty bitmap. qemu_get_clock_ns() is a bit expensive, so we only check each some @@ -1247,7 +2038,9 @@ static int ram_save_complete(QEMUFile *f, void *opaque) { rcu_read_lock(); - migration_bitmap_sync(); + if (!migration_in_postcopy(migrate_get_current())) { + migration_bitmap_sync(); + } ram_control_before_iterate(f, RAM_CONTROL_FINISH); @@ -1274,13 +2067,16 @@ static int ram_save_complete(QEMUFile *f, void *opaque) return 0; } -static uint64_t ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size) +static void ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size, + uint64_t *non_postcopiable_pending, + uint64_t *postcopiable_pending) { uint64_t remaining_size; remaining_size = ram_save_remaining() * TARGET_PAGE_SIZE; - if (remaining_size < max_size) { + if (!migration_in_postcopy(migrate_get_current()) && + remaining_size < max_size) { qemu_mutex_lock_iothread(); rcu_read_lock(); migration_bitmap_sync(); @@ -1288,17 +2084,21 @@ static uint64_t ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size) qemu_mutex_unlock_iothread(); remaining_size = ram_save_remaining() * TARGET_PAGE_SIZE; } - return remaining_size; + + /* We can do postcopy, and all the data is postcopiable */ + *postcopiable_pending += remaining_size; } static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host) { unsigned int xh_len; int xh_flags; + uint8_t *loaded_data; if (!xbzrle_decoded_buf) { xbzrle_decoded_buf = g_malloc(TARGET_PAGE_SIZE); } + loaded_data = xbzrle_decoded_buf; /* extract RLE header */ xh_flags = qemu_get_byte(f); @@ -1314,10 +2114,10 @@ static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host) return -1; } /* load data and decode */ - qemu_get_buffer(f, xbzrle_decoded_buf, xh_len); + qemu_get_buffer_in_place(f, &loaded_data, xh_len); /* decode RLE */ - if (xbzrle_decode_buffer(xbzrle_decoded_buf, xh_len, host, + if (xbzrle_decode_buffer(loaded_data, xh_len, host, TARGET_PAGE_SIZE) == -1) { error_report("Failed to load XBZRLE page - decode error!"); return -1; @@ -1329,36 +2129,48 @@ static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host) /* Must be called from within a rcu critical section. * Returns a pointer from within the RCU-protected ram_list. */ -static inline void *host_from_stream_offset(QEMUFile *f, - ram_addr_t offset, - int flags) +/* + * Read a RAMBlock ID from the stream f. + * + * f: Stream to read from + * flags: Page flags (mostly to see if it's a continuation of previous block) + */ +static inline RAMBlock *ram_block_from_stream(QEMUFile *f, + int flags) { static RAMBlock *block = NULL; char id[256]; uint8_t len; if (flags & RAM_SAVE_FLAG_CONTINUE) { - if (!block || block->max_length <= offset) { + if (!block) { error_report("Ack, bad migration stream!"); return NULL; } - - return memory_region_get_ram_ptr(block->mr) + offset; + return block; } len = qemu_get_byte(f); qemu_get_buffer(f, (uint8_t *)id, len); id[len] = 0; - QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { - if (!strncmp(id, block->idstr, sizeof(id)) && - block->max_length > offset) { - return memory_region_get_ram_ptr(block->mr) + offset; - } + block = qemu_ram_block_by_name(id); + if (!block) { + error_report("Can't find block %s", id); + return NULL; } - error_report("Can't find block %s!", id); - return NULL; + return block; +} + +static inline void *host_from_ram_block_offset(RAMBlock *block, + ram_addr_t offset) +{ + if (!offset_in_ramblock(block, offset)) { + return NULL; + } + + return block->host + offset; } /* @@ -1406,7 +2218,6 @@ void migrate_decompress_threads_create(void) thread_count = migrate_decompress_threads(); decompress_threads = g_new0(QemuThread, thread_count); decomp_param = g_new0(DecompressParam, thread_count); - compressed_data_buf = g_malloc0(compressBound(TARGET_PAGE_SIZE)); quit_decomp_thread = false; for (i = 0; i < thread_count; i++) { qemu_mutex_init(&decomp_param[i].mutex); @@ -1437,13 +2248,11 @@ void migrate_decompress_threads_join(void) } g_free(decompress_threads); g_free(decomp_param); - g_free(compressed_data_buf); decompress_threads = NULL; decomp_param = NULL; - compressed_data_buf = NULL; } -static void decompress_data_with_multi_threads(uint8_t *compbuf, +static void decompress_data_with_multi_threads(QEMUFile *f, void *host, int len) { int idx, thread_count; @@ -1452,7 +2261,7 @@ static void decompress_data_with_multi_threads(uint8_t *compbuf, while (true) { for (idx = 0; idx < thread_count; idx++) { if (!decomp_param[idx].start) { - memcpy(decomp_param[idx].compbuf, compbuf, len); + qemu_get_buffer(f, decomp_param[idx].compbuf, len); decomp_param[idx].des = host; decomp_param[idx].len = len; start_decompression(&decomp_param[idx]); @@ -1465,11 +2274,150 @@ static void decompress_data_with_multi_threads(uint8_t *compbuf, } } +/* + * Allocate data structures etc needed by incoming migration with postcopy-ram + * postcopy-ram's similarly names postcopy_ram_incoming_init does the work + */ +int ram_postcopy_incoming_init(MigrationIncomingState *mis) +{ + size_t ram_pages = last_ram_offset() >> TARGET_PAGE_BITS; + + return postcopy_ram_incoming_init(mis, ram_pages); +} + +/* + * Called in postcopy mode by ram_load(). + * rcu_read_lock is taken prior to this being called. + */ +static int ram_load_postcopy(QEMUFile *f) +{ + int flags = 0, ret = 0; + bool place_needed = false; + bool matching_page_sizes = qemu_host_page_size == TARGET_PAGE_SIZE; + MigrationIncomingState *mis = migration_incoming_get_current(); + /* Temporary page that is later 'placed' */ + void *postcopy_host_page = postcopy_get_tmp_page(mis); + void *last_host = NULL; + bool all_zero = false; + + while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) { + ram_addr_t addr; + void *host = NULL; + void *page_buffer = NULL; + void *place_source = NULL; + uint8_t ch; + + addr = qemu_get_be64(f); + flags = addr & ~TARGET_PAGE_MASK; + addr &= TARGET_PAGE_MASK; + + trace_ram_load_postcopy_loop((uint64_t)addr, flags); + place_needed = false; + if (flags & (RAM_SAVE_FLAG_COMPRESS | RAM_SAVE_FLAG_PAGE)) { + RAMBlock *block = ram_block_from_stream(f, flags); + + host = host_from_ram_block_offset(block, addr); + if (!host) { + error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); + ret = -EINVAL; + break; + } + page_buffer = host; + /* + * Postcopy requires that we place whole host pages atomically. + * To make it atomic, the data is read into a temporary page + * that's moved into place later. + * The migration protocol uses, possibly smaller, target-pages + * however the source ensures it always sends all the components + * of a host page in order. + */ + page_buffer = postcopy_host_page + + ((uintptr_t)host & ~qemu_host_page_mask); + /* If all TP are zero then we can optimise the place */ + if (!((uintptr_t)host & ~qemu_host_page_mask)) { + all_zero = true; + } else { + /* not the 1st TP within the HP */ + if (host != (last_host + TARGET_PAGE_SIZE)) { + error_report("Non-sequential target page %p/%p", + host, last_host); + ret = -EINVAL; + break; + } + } + + + /* + * If it's the last part of a host page then we place the host + * page + */ + place_needed = (((uintptr_t)host + TARGET_PAGE_SIZE) & + ~qemu_host_page_mask) == 0; + place_source = postcopy_host_page; + } + last_host = host; + + switch (flags & ~RAM_SAVE_FLAG_CONTINUE) { + case RAM_SAVE_FLAG_COMPRESS: + ch = qemu_get_byte(f); + memset(page_buffer, ch, TARGET_PAGE_SIZE); + if (ch) { + all_zero = false; + } + break; + + case RAM_SAVE_FLAG_PAGE: + all_zero = false; + if (!place_needed || !matching_page_sizes) { + qemu_get_buffer(f, page_buffer, TARGET_PAGE_SIZE); + } else { + /* Avoids the qemu_file copy during postcopy, which is + * going to do a copy later; can only do it when we + * do this read in one go (matching page sizes) + */ + qemu_get_buffer_in_place(f, (uint8_t **)&place_source, + TARGET_PAGE_SIZE); + } + break; + case RAM_SAVE_FLAG_EOS: + /* normal exit */ + break; + default: + error_report("Unknown combination of migration flags: %#x" + " (postcopy mode)", flags); + ret = -EINVAL; + } + + if (place_needed) { + /* This gets called at the last target page in the host page */ + if (all_zero) { + ret = postcopy_place_page_zero(mis, + host + TARGET_PAGE_SIZE - + qemu_host_page_size); + } else { + ret = postcopy_place_page(mis, host + TARGET_PAGE_SIZE - + qemu_host_page_size, + place_source); + } + } + if (!ret) { + ret = qemu_file_get_error(f); + } + } + + return ret; +} + static int ram_load(QEMUFile *f, void *opaque, int version_id) { int flags = 0, ret = 0; static uint64_t seq_iter; int len = 0; + /* + * If system is running in postcopy mode, page inserts to host memory must + * be atomic + */ + bool postcopy_running = postcopy_state_get() >= POSTCOPY_INCOMING_LISTENING; seq_iter++; @@ -1483,15 +2431,32 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) * critical section. */ rcu_read_lock(); - while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) { + + if (postcopy_running) { + ret = ram_load_postcopy(f); + } + + while (!postcopy_running && !ret && !(flags & RAM_SAVE_FLAG_EOS)) { ram_addr_t addr, total_ram_bytes; - void *host; + void *host = NULL; uint8_t ch; addr = qemu_get_be64(f); flags = addr & ~TARGET_PAGE_MASK; addr &= TARGET_PAGE_MASK; + if (flags & (RAM_SAVE_FLAG_COMPRESS | RAM_SAVE_FLAG_PAGE | + RAM_SAVE_FLAG_COMPRESS_PAGE | RAM_SAVE_FLAG_XBZRLE)) { + RAMBlock *block = ram_block_from_stream(f, flags); + + host = host_from_ram_block_offset(block, addr); + if (!host) { + error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); + ret = -EINVAL; + break; + } + } + switch (flags & ~RAM_SAVE_FLAG_CONTINUE) { case RAM_SAVE_FLAG_MEM_SIZE: /* Synchronize RAM block list */ @@ -1506,23 +2471,20 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) id[len] = 0; length = qemu_get_be64(f); - QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { - if (!strncmp(id, block->idstr, sizeof(id))) { - if (length != block->used_length) { - Error *local_err = NULL; + block = qemu_ram_block_by_name(id); + if (block) { + if (length != block->used_length) { + Error *local_err = NULL; - ret = qemu_ram_resize(block->offset, length, &local_err); - if (local_err) { - error_report_err(local_err); - } + ret = qemu_ram_resize(block->offset, length, + &local_err); + if (local_err) { + error_report_err(local_err); } - ram_control_load_hook(f, RAM_CONTROL_BLOCK_REG, - block->idstr); - break; } - } - - if (!block) { + ram_control_load_hook(f, RAM_CONTROL_BLOCK_REG, + block->idstr); + } else { error_report("Unknown ramblock \"%s\", cannot " "accept migration", id); ret = -EINVAL; @@ -1531,49 +2493,27 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) total_ram_bytes -= length; } break; + case RAM_SAVE_FLAG_COMPRESS: - host = host_from_stream_offset(f, addr, flags); - if (!host) { - error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); - ret = -EINVAL; - break; - } ch = qemu_get_byte(f); ram_handle_compressed(host, ch, TARGET_PAGE_SIZE); break; + case RAM_SAVE_FLAG_PAGE: - host = host_from_stream_offset(f, addr, flags); - if (!host) { - error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); - ret = -EINVAL; - break; - } qemu_get_buffer(f, host, TARGET_PAGE_SIZE); break; - case RAM_SAVE_FLAG_COMPRESS_PAGE: - host = host_from_stream_offset(f, addr, flags); - if (!host) { - error_report("Invalid RAM offset " RAM_ADDR_FMT, addr); - ret = -EINVAL; - break; - } + case RAM_SAVE_FLAG_COMPRESS_PAGE: len = qemu_get_be32(f); if (len < 0 || len > compressBound(TARGET_PAGE_SIZE)) { error_report("Invalid compressed data length: %d", len); ret = -EINVAL; break; } - qemu_get_buffer(f, compressed_data_buf, len); - decompress_data_with_multi_threads(compressed_data_buf, host, len); + decompress_data_with_multi_threads(f, host, len); break; + case RAM_SAVE_FLAG_XBZRLE: - host = host_from_stream_offset(f, addr, flags); - if (!host) { - error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); - ret = -EINVAL; - break; - } if (load_xbzrle(f, addr, host) < 0) { error_report("Failed to decompress XBZRLE page at " RAM_ADDR_FMT, addr); @@ -1607,10 +2547,11 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) static SaveVMHandlers savevm_ram_handlers = { .save_live_setup = ram_save_setup, .save_live_iterate = ram_save_iterate, - .save_live_complete = ram_save_complete, + .save_live_complete_postcopy = ram_save_complete, + .save_live_complete_precopy = ram_save_complete, .save_live_pending = ram_save_pending, .load_state = ram_load, - .cancel = ram_migration_cancel, + .cleanup = ram_migration_cleanup, }; void ram_mig_init(void) @@ -1618,52 +2559,3 @@ void ram_mig_init(void) qemu_mutex_init(&XBZRLE.lock); register_savevm_live(NULL, "ram", 0, 4, &savevm_ram_handlers, NULL); } -/* Stub function that's gets run on the vcpu when its brought out of the - VM to run inside qemu via async_run_on_cpu()*/ - -static void mig_sleep_cpu(void *opq) -{ - qemu_mutex_unlock_iothread(); - g_usleep(30*1000); - qemu_mutex_lock_iothread(); -} - -/* To reduce the dirty rate explicitly disallow the VCPUs from spending - much time in the VM. The migration thread will try to catchup. - Workload will experience a performance drop. -*/ -static void mig_throttle_guest_down(void) -{ - CPUState *cpu; - - qemu_mutex_lock_iothread(); - CPU_FOREACH(cpu) { - async_run_on_cpu(cpu, mig_sleep_cpu, NULL); - } - qemu_mutex_unlock_iothread(); -} - -static void check_guest_throttling(void) -{ - static int64_t t0; - int64_t t1; - - if (!mig_throttle_on) { - return; - } - - if (!t0) { - t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - return; - } - - t1 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - - /* If it has been more than 40 ms since the last time the guest - * was throttled then do it again. - */ - if (40 < (t1-t0)/1000000) { - mig_throttle_guest_down(); - t0 = t1; - } -} diff --git a/qemu/migration/rdma.c b/qemu/migration/rdma.c index 74876fd7a..f6a9992b3 100644 --- a/qemu/migration/rdma.c +++ b/qemu/migration/rdma.c @@ -11,7 +11,10 @@ * later. See the COPYING file in the top-level directory. * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" +#include "qemu/cutils.h" #include "migration/migration.h" #include "migration/qemu-file.h" #include "exec/cpu-common.h" @@ -19,13 +22,10 @@ #include "qemu/main-loop.h" #include "qemu/sockets.h" #include "qemu/bitmap.h" -#include "block/coroutine.h" -#include -#include +#include "qemu/coroutine.h" #include #include #include -#include #include #include "trace.h" @@ -541,7 +541,7 @@ static int rdma_add_block(RDMAContext *rdma, const char *block_name, RDMALocalBlock *block; RDMALocalBlock *old = local->block; - local->block = g_malloc0(sizeof(RDMALocalBlock) * (local->nb_blocks + 1)); + local->block = g_new0(RDMALocalBlock, local->nb_blocks + 1); if (local->nb_blocks) { int x; @@ -572,12 +572,12 @@ static int rdma_add_block(RDMAContext *rdma, const char *block_name, bitmap_clear(block->transit_bitmap, 0, block->nb_chunks); block->unregister_bitmap = bitmap_new(block->nb_chunks); bitmap_clear(block->unregister_bitmap, 0, block->nb_chunks); - block->remote_keys = g_malloc0(block->nb_chunks * sizeof(uint32_t)); + block->remote_keys = g_new0(uint32_t, block->nb_chunks); block->is_ram_block = local->init ? false : true; if (rdma->blockmap) { - g_hash_table_insert(rdma->blockmap, (void *) block_offset, block); + g_hash_table_insert(rdma->blockmap, (void *)(uintptr_t)block_offset, block); } trace_rdma_add_block(block_name, local->nb_blocks, @@ -617,8 +617,8 @@ static int qemu_rdma_init_ram_blocks(RDMAContext *rdma) memset(local, 0, sizeof *local); qemu_ram_foreach_block(qemu_rdma_init_one_block, rdma); trace_qemu_rdma_init_ram_blocks(local->nb_blocks); - rdma->dest_blocks = (RDMADestBlock *) g_malloc0(sizeof(RDMADestBlock) * - rdma->local_ram_blocks.nb_blocks); + rdma->dest_blocks = g_new0(RDMADestBlock, + rdma->local_ram_blocks.nb_blocks); local->init = true; return 0; } @@ -677,8 +677,7 @@ static int rdma_delete_block(RDMAContext *rdma, RDMALocalBlock *block) if (local->nb_blocks > 1) { - local->block = g_malloc0(sizeof(RDMALocalBlock) * - (local->nb_blocks - 1)); + local->block = g_new0(RDMALocalBlock, local->nb_blocks - 1); if (block->index) { memcpy(local->block, old, sizeof(RDMALocalBlock) * block->index); @@ -778,7 +777,7 @@ static void qemu_rdma_dump_gid(const char *who, struct rdma_cm_id *id) * * If the source VM connects with an IPv4 address without knowing that the * destination has bound to '[::]' the migration will unconditionally fail - * unless the management software is explicitly listening on the the IPv4 + * unless the management software is explicitly listening on the IPv4 * address while using a RoCE-based device. * * If the source VM connects with an IPv6 address, then we're OK because we can @@ -1164,7 +1163,7 @@ static int qemu_rdma_register_and_get_keys(RDMAContext *rdma, /* allocate memory to store chunk MRs */ if (!block->pmr) { - block->pmr = g_malloc0(block->nb_chunks * sizeof(struct ibv_mr *)); + block->pmr = g_new0(struct ibv_mr *, block->nb_chunks); } /* @@ -2494,7 +2493,7 @@ static void *qemu_rdma_data_init(const char *host_port, Error **errp) InetSocketAddress *addr; if (host_port) { - rdma = g_malloc0(sizeof(RDMAContext)); + rdma = g_new0(RDMAContext, 1); rdma->current_index = -1; rdma->current_chunk = -1; @@ -2519,8 +2518,8 @@ static void *qemu_rdma_data_init(const char *host_port, Error **errp) * SEND messages for control only. * VM's ram is handled with regular RDMA messages. */ -static int qemu_rdma_put_buffer(void *opaque, const uint8_t *buf, - int64_t pos, int size) +static ssize_t qemu_rdma_put_buffer(void *opaque, const uint8_t *buf, + int64_t pos, size_t size) { QEMUFileRDMA *r = opaque; QEMUFile *f = r->file; @@ -2547,7 +2546,8 @@ static int qemu_rdma_put_buffer(void *opaque, const uint8_t *buf, r->len = MIN(remaining, RDMA_SEND_INCREMENT); remaining -= r->len; - head.len = r->len; + /* Guaranteed to fit due to RDMA_SEND_INCREMENT MIN above */ + head.len = (uint32_t)r->len; head.type = RDMA_CONTROL_QEMU_FILE; ret = qemu_rdma_exchange_send(rdma, &head, data, NULL, NULL, NULL); @@ -2564,7 +2564,7 @@ static int qemu_rdma_put_buffer(void *opaque, const uint8_t *buf, } static size_t qemu_rdma_fill(RDMAContext *rdma, uint8_t *buf, - int size, int idx) + size_t size, int idx) { size_t len = 0; @@ -2585,8 +2585,8 @@ static size_t qemu_rdma_fill(RDMAContext *rdma, uint8_t *buf, * RDMA links don't use bytestreams, so we have to * return bytes to QEMUFile opportunistically. */ -static int qemu_rdma_get_buffer(void *opaque, uint8_t *buf, - int64_t pos, int size) +static ssize_t qemu_rdma_get_buffer(void *opaque, uint8_t *buf, + int64_t pos, size_t size) { QEMUFileRDMA *r = opaque; RDMAContext *rdma = r->rdma; @@ -3399,7 +3399,7 @@ static void *qemu_fopen_rdma(RDMAContext *rdma, const char *mode) return NULL; } - r = g_malloc0(sizeof(QEMUFileRDMA)); + r = g_new0(QEMUFileRDMA, 1); r->rdma = rdma; if (mode[0] == 'w') { @@ -3506,7 +3506,7 @@ void rdma_start_outgoing_migration(void *opaque, trace_rdma_start_outgoing_migration_after_rdma_connect(); - s->file = qemu_fopen_rdma(rdma, "wb"); + s->to_dst_file = qemu_fopen_rdma(rdma, "wb"); migrate_fd_connect(s); return; err: diff --git a/qemu/migration/savevm.c b/qemu/migration/savevm.c index a42874b10..16ba44379 100644 --- a/qemu/migration/savevm.c +++ b/qemu/migration/savevm.c @@ -26,8 +26,7 @@ * THE SOFTWARE. */ -#include "config-host.h" -#include "qemu-common.h" +#include "qemu/osdep.h" #include "hw/boards.h" #include "hw/hw.h" #include "hw/qdev.h" @@ -37,6 +36,7 @@ #include "qemu/timer.h" #include "audio/audio.h" #include "migration/migration.h" +#include "migration/postcopy-ram.h" #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "qemu/sockets.h" @@ -45,10 +45,11 @@ #include "exec/memory.h" #include "qmp-commands.h" #include "trace.h" +#include "qemu/bitops.h" #include "qemu/iov.h" #include "block/snapshot.h" #include "block/qapi.h" - +#include "qemu/cutils.h" #ifndef ETH_P_RARP #define ETH_P_RARP 0x8035 @@ -57,8 +58,26 @@ #define ARP_PTYPE_IP 0x0800 #define ARP_OP_REQUEST_REV 0x3 +const unsigned int postcopy_ram_discard_version = 0; + static bool skip_section_footers; +static struct mig_cmd_args { + ssize_t len; /* -1 = variable */ + const char *name; +} mig_cmd_args[] = { + [MIG_CMD_INVALID] = { .len = -1, .name = "INVALID" }, + [MIG_CMD_OPEN_RETURN_PATH] = { .len = 0, .name = "OPEN_RETURN_PATH" }, + [MIG_CMD_PING] = { .len = sizeof(uint32_t), .name = "PING" }, + [MIG_CMD_POSTCOPY_ADVISE] = { .len = 16, .name = "POSTCOPY_ADVISE" }, + [MIG_CMD_POSTCOPY_LISTEN] = { .len = 0, .name = "POSTCOPY_LISTEN" }, + [MIG_CMD_POSTCOPY_RUN] = { .len = 0, .name = "POSTCOPY_RUN" }, + [MIG_CMD_POSTCOPY_RAM_DISCARD] = { + .len = -1, .name = "POSTCOPY_RAM_DISCARD" }, + [MIG_CMD_PACKAGED] = { .len = 4, .name = "PACKAGED" }, + [MIG_CMD_MAX] = { .len = -1, .name = "MAX" }, +}; + static int announce_self_create(uint8_t *buf, uint8_t *mac_addr) { @@ -138,14 +157,15 @@ static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, return qiov.size; } -static int block_put_buffer(void *opaque, const uint8_t *buf, - int64_t pos, int size) +static ssize_t block_put_buffer(void *opaque, const uint8_t *buf, + int64_t pos, size_t size) { bdrv_save_vmstate(opaque, buf, pos, size); return size; } -static int block_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) +static ssize_t block_get_buffer(void *opaque, uint8_t *buf, int64_t pos, + size_t size) { return bdrv_load_vmstate(opaque, buf, pos, size); } @@ -278,8 +298,8 @@ static int configuration_post_load(void *opaque, int version_id) const char *current_name = MACHINE_GET_CLASS(current_machine)->name; if (strncmp(state->name, current_name, state->len) != 0) { - error_report("Machine type received is '%s' and local is '%s'", - state->name, current_name); + error_report("Machine type received is '%.*s' and local is '%s'", + (int) state->len, state->name, current_name); return -EINVAL; } return 0; @@ -480,7 +500,7 @@ int register_savevm_live(DeviceState *dev, { SaveStateEntry *se; - se = g_malloc0(sizeof(SaveStateEntry)); + se = g_new0(SaveStateEntry, 1); se->version_id = version_id; se->section_id = savevm_state.global_section_id++; se->ops = ops; @@ -498,7 +518,7 @@ int register_savevm_live(DeviceState *dev, pstrcat(se->idstr, sizeof(se->idstr), "/"); g_free(id); - se->compat = g_malloc0(sizeof(CompatEntry)); + se->compat = g_new0(CompatEntry, 1); pstrcpy(se->compat->idstr, sizeof(se->compat->idstr), idstr); se->compat->instance_id = instance_id == -1 ? calculate_compat_instance_id(idstr) : instance_id; @@ -526,7 +546,7 @@ int register_savevm(DeviceState *dev, LoadStateHandler *load_state, void *opaque) { - SaveVMHandlers *ops = g_malloc0(sizeof(SaveVMHandlers)); + SaveVMHandlers *ops = g_new0(SaveVMHandlers, 1); ops->save_state = save_state; ops->load_state = load_state; return register_savevm_live(dev, idstr, instance_id, version_id, @@ -551,9 +571,7 @@ void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque) QTAILQ_FOREACH_SAFE(se, &savevm_state.handlers, entry, new_se) { if (strcmp(se->idstr, id) == 0 && se->opaque == opaque) { QTAILQ_REMOVE(&savevm_state.handlers, se, entry); - if (se->compat) { - g_free(se->compat); - } + g_free(se->compat); g_free(se->ops); g_free(se); } @@ -570,7 +588,7 @@ int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, /* If this triggers, alias support can be dropped for the vmsd. */ assert(alias_id == -1 || required_for_version >= vmsd->minimum_version_id); - se = g_malloc0(sizeof(SaveStateEntry)); + se = g_new0(SaveStateEntry, 1); se->version_id = vmsd->version_id; se->section_id = savevm_state.global_section_id++; se->opaque = opaque; @@ -584,7 +602,7 @@ int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, pstrcat(se->idstr, sizeof(se->idstr), "/"); g_free(id); - se->compat = g_malloc0(sizeof(CompatEntry)); + se->compat = g_new0(CompatEntry, 1); pstrcpy(se->compat->idstr, sizeof(se->compat->idstr), vmsd->name); se->compat->instance_id = instance_id == -1 ? calculate_compat_instance_id(vmsd->name) : instance_id; @@ -612,9 +630,7 @@ void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd, QTAILQ_FOREACH_SAFE(se, &savevm_state.handlers, entry, new_se) { if (se->vmsd == vmsd && se->opaque == opaque) { QTAILQ_REMOVE(&savevm_state.handlers, se, entry); - if (se->compat) { - g_free(se->compat); - } + g_free(se->compat); g_free(se); } } @@ -697,6 +713,156 @@ static void save_section_footer(QEMUFile *f, SaveStateEntry *se) } } +/** + * qemu_savevm_command_send: Send a 'QEMU_VM_COMMAND' type element with the + * command and associated data. + * + * @f: File to send command on + * @command: Command type to send + * @len: Length of associated data + * @data: Data associated with command. + */ +void qemu_savevm_command_send(QEMUFile *f, + enum qemu_vm_cmd command, + uint16_t len, + uint8_t *data) +{ + trace_savevm_command_send(command, len); + qemu_put_byte(f, QEMU_VM_COMMAND); + qemu_put_be16(f, (uint16_t)command); + qemu_put_be16(f, len); + qemu_put_buffer(f, data, len); + qemu_fflush(f); +} + +void qemu_savevm_send_ping(QEMUFile *f, uint32_t value) +{ + uint32_t buf; + + trace_savevm_send_ping(value); + buf = cpu_to_be32(value); + qemu_savevm_command_send(f, MIG_CMD_PING, sizeof(value), (uint8_t *)&buf); +} + +void qemu_savevm_send_open_return_path(QEMUFile *f) +{ + trace_savevm_send_open_return_path(); + qemu_savevm_command_send(f, MIG_CMD_OPEN_RETURN_PATH, 0, NULL); +} + +/* We have a buffer of data to send; we don't want that all to be loaded + * by the command itself, so the command contains just the length of the + * extra buffer that we then send straight after it. + * TODO: Must be a better way to organise that + * + * Returns: + * 0 on success + * -ve on error + */ +int qemu_savevm_send_packaged(QEMUFile *f, const QEMUSizedBuffer *qsb) +{ + size_t cur_iov; + size_t len = qsb_get_length(qsb); + uint32_t tmp; + + if (len > MAX_VM_CMD_PACKAGED_SIZE) { + error_report("%s: Unreasonably large packaged state: %zu", + __func__, len); + return -1; + } + + tmp = cpu_to_be32(len); + + trace_qemu_savevm_send_packaged(); + qemu_savevm_command_send(f, MIG_CMD_PACKAGED, 4, (uint8_t *)&tmp); + + /* all the data follows (concatinating the iov's) */ + for (cur_iov = 0; cur_iov < qsb->n_iov; cur_iov++) { + /* The iov entries are partially filled */ + size_t towrite = MIN(qsb->iov[cur_iov].iov_len, len); + len -= towrite; + + if (!towrite) { + break; + } + + qemu_put_buffer(f, qsb->iov[cur_iov].iov_base, towrite); + } + + return 0; +} + +/* Send prior to any postcopy transfer */ +void qemu_savevm_send_postcopy_advise(QEMUFile *f) +{ + uint64_t tmp[2]; + tmp[0] = cpu_to_be64(getpagesize()); + tmp[1] = cpu_to_be64(1ul << qemu_target_page_bits()); + + trace_qemu_savevm_send_postcopy_advise(); + qemu_savevm_command_send(f, MIG_CMD_POSTCOPY_ADVISE, 16, (uint8_t *)tmp); +} + +/* Sent prior to starting the destination running in postcopy, discard pages + * that have already been sent but redirtied on the source. + * CMD_POSTCOPY_RAM_DISCARD consist of: + * byte version (0) + * byte Length of name field (not including 0) + * n x byte RAM block name + * byte 0 terminator (just for safety) + * n x Byte ranges within the named RAMBlock + * be64 Start of the range + * be64 Length + * + * name: RAMBlock name that these entries are part of + * len: Number of page entries + * start_list: 'len' addresses + * length_list: 'len' addresses + * + */ +void qemu_savevm_send_postcopy_ram_discard(QEMUFile *f, const char *name, + uint16_t len, + uint64_t *start_list, + uint64_t *length_list) +{ + uint8_t *buf; + uint16_t tmplen; + uint16_t t; + size_t name_len = strlen(name); + + trace_qemu_savevm_send_postcopy_ram_discard(name, len); + assert(name_len < 256); + buf = g_malloc0(1 + 1 + name_len + 1 + (8 + 8) * len); + buf[0] = postcopy_ram_discard_version; + buf[1] = name_len; + memcpy(buf + 2, name, name_len); + tmplen = 2 + name_len; + buf[tmplen++] = '\0'; + + for (t = 0; t < len; t++) { + cpu_to_be64w((uint64_t *)(buf + tmplen), start_list[t]); + tmplen += 8; + cpu_to_be64w((uint64_t *)(buf + tmplen), length_list[t]); + tmplen += 8; + } + qemu_savevm_command_send(f, MIG_CMD_POSTCOPY_RAM_DISCARD, tmplen, buf); + g_free(buf); +} + +/* Get the destination into a state where it can receive postcopy data. */ +void qemu_savevm_send_postcopy_listen(QEMUFile *f) +{ + trace_savevm_send_postcopy_listen(); + qemu_savevm_command_send(f, MIG_CMD_POSTCOPY_LISTEN, 0, NULL); +} + +/* Kick the destination into running */ +void qemu_savevm_send_postcopy_run(QEMUFile *f) +{ + trace_savevm_send_postcopy_run(); + qemu_savevm_command_send(f, MIG_CMD_POSTCOPY_RUN, 0, NULL); +} + bool qemu_savevm_state_blocked(Error **errp) { SaveStateEntry *se; @@ -711,11 +877,23 @@ bool qemu_savevm_state_blocked(Error **errp) return false; } +static bool enforce_config_section(void) +{ + MachineState *machine = MACHINE(qdev_get_machine()); + return machine->enforce_config_section; +} + void qemu_savevm_state_header(QEMUFile *f) { trace_savevm_state_header(); qemu_put_be32(f, QEMU_VM_FILE_MAGIC); qemu_put_be32(f, QEMU_VM_FILE_VERSION); + + if (!savevm_state.skip_configuration || enforce_config_section()) { + qemu_put_byte(f, QEMU_VM_CONFIGURATION); + vmstate_save_state(f, &vmstate_configuration, &savevm_state, 0); + } + } void qemu_savevm_state_begin(QEMUFile *f, @@ -732,11 +910,6 @@ void qemu_savevm_state_begin(QEMUFile *f, se->ops->set_params(params, se->opaque); } - if (!savevm_state.skip_configuration) { - qemu_put_byte(f, QEMU_VM_CONFIGURATION); - vmstate_save_state(f, &vmstate_configuration, &savevm_state, 0); - } - QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (!se->ops || !se->ops->save_live_setup) { continue; @@ -763,7 +936,7 @@ void qemu_savevm_state_begin(QEMUFile *f, * 0 : We haven't finished, caller have to go again * 1 : We have finished, we can go to complete phase */ -int qemu_savevm_state_iterate(QEMUFile *f) +int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy) { SaveStateEntry *se; int ret = 1; @@ -778,6 +951,15 @@ int qemu_savevm_state_iterate(QEMUFile *f) continue; } } + /* + * In the postcopy phase, any device that doesn't know how to + * do postcopy should have saved it's state in the _complete + * call that's already run, it might get confused if we call + * iterate afterwards. + */ + if (postcopy && !se->ops->save_live_complete_postcopy) { + continue; + } if (qemu_file_rate_limit(f)) { return 0; } @@ -806,24 +988,69 @@ int qemu_savevm_state_iterate(QEMUFile *f) static bool should_send_vmdesc(void) { MachineState *machine = MACHINE(qdev_get_machine()); - return !machine->suppress_vmdesc; + bool in_postcopy = migration_in_postcopy(migrate_get_current()); + return !machine->suppress_vmdesc && !in_postcopy; } -void qemu_savevm_state_complete(QEMUFile *f) +/* + * Calls the save_live_complete_postcopy methods + * causing the last few pages to be sent immediately and doing any associated + * cleanup. + * Note postcopy also calls qemu_savevm_state_complete_precopy to complete + * all the other devices, but that happens at the point we switch to postcopy. + */ +void qemu_savevm_state_complete_postcopy(QEMUFile *f) +{ + SaveStateEntry *se; + int ret; + + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { + if (!se->ops || !se->ops->save_live_complete_postcopy) { + continue; + } + if (se->ops && se->ops->is_active) { + if (!se->ops->is_active(se->opaque)) { + continue; + } + } + trace_savevm_section_start(se->idstr, se->section_id); + /* Section type */ + qemu_put_byte(f, QEMU_VM_SECTION_END); + qemu_put_be32(f, se->section_id); + + ret = se->ops->save_live_complete_postcopy(f, se->opaque); + trace_savevm_section_end(se->idstr, se->section_id, ret); + save_section_footer(f, se); + if (ret < 0) { + qemu_file_set_error(f, ret); + return; + } + } + + qemu_put_byte(f, QEMU_VM_EOF); + qemu_fflush(f); +} + +void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only) { QJSON *vmdesc; int vmdesc_len; SaveStateEntry *se; int ret; + bool in_postcopy = migration_in_postcopy(migrate_get_current()); - trace_savevm_state_complete(); + trace_savevm_state_complete_precopy(); cpu_synchronize_all_states(); QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { - if (!se->ops || !se->ops->save_live_complete) { + if (!se->ops || + (in_postcopy && se->ops->save_live_complete_postcopy) || + (in_postcopy && !iterable_only) || + !se->ops->save_live_complete_precopy) { continue; } + if (se->ops && se->ops->is_active) { if (!se->ops->is_active(se->opaque)) { continue; @@ -833,7 +1060,7 @@ void qemu_savevm_state_complete(QEMUFile *f) save_section_header(f, se, QEMU_VM_SECTION_END); - ret = se->ops->save_live_complete(f, se->opaque); + ret = se->ops->save_live_complete_precopy(f, se->opaque); trace_savevm_section_end(se->idstr, se->section_id, ret); save_section_footer(f, se); if (ret < 0) { @@ -842,6 +1069,10 @@ void qemu_savevm_state_complete(QEMUFile *f) } } + if (iterable_only) { + return; + } + vmdesc = qjson_new(); json_prop_int(vmdesc, "page_size", TARGET_PAGE_SIZE); json_start_array(vmdesc, "devices"); @@ -862,15 +1093,17 @@ void qemu_savevm_state_complete(QEMUFile *f) json_prop_int(vmdesc, "instance_id", se->instance_id); save_section_header(f, se, QEMU_VM_SECTION_FULL); - vmstate_save(f, se, vmdesc); - - json_end_object(vmdesc); trace_savevm_section_end(se->idstr, se->section_id, 0); save_section_footer(f, se); + + json_end_object(vmdesc); } - qemu_put_byte(f, QEMU_VM_EOF); + if (!in_postcopy) { + /* Postcopy stream will still be going */ + qemu_put_byte(f, QEMU_VM_EOF); + } json_end_array(vmdesc); qjson_finish(vmdesc); @@ -886,10 +1119,19 @@ void qemu_savevm_state_complete(QEMUFile *f) qemu_fflush(f); } -uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size) +/* Give an estimate of the amount left to be transferred, + * the result is split into the amount for units that can and + * for units that can't do postcopy. + */ +void qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size, + uint64_t *res_non_postcopiable, + uint64_t *res_postcopiable) { SaveStateEntry *se; - uint64_t ret = 0; + + *res_non_postcopiable = 0; + *res_postcopiable = 0; + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (!se->ops || !se->ops->save_live_pending) { @@ -900,19 +1142,19 @@ uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size) continue; } } - ret += se->ops->save_live_pending(f, se->opaque, max_size); + se->ops->save_live_pending(f, se->opaque, max_size, + res_non_postcopiable, res_postcopiable); } - return ret; } -void qemu_savevm_state_cancel(void) +void qemu_savevm_state_cleanup(void) { SaveStateEntry *se; - trace_savevm_state_cancel(); + trace_savevm_state_cleanup(); QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { - if (se->ops && se->ops->cancel) { - se->ops->cancel(se->opaque); + if (se->ops && se->ops->cleanup) { + se->ops->cleanup(se->opaque); } } } @@ -924,6 +1166,8 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) .blk = 0, .shared = 0 }; + MigrationState *ms = migrate_init(¶ms); + ms->to_dst_file = f; if (qemu_savevm_state_blocked(errp)) { return -EINVAL; @@ -935,17 +1179,17 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) qemu_mutex_lock_iothread(); while (qemu_file_get_error(f) == 0) { - if (qemu_savevm_state_iterate(f) > 0) { + if (qemu_savevm_state_iterate(f, false) > 0) { break; } } ret = qemu_file_get_error(f); if (ret == 0) { - qemu_savevm_state_complete(f); + qemu_savevm_state_complete_precopy(f, false); ret = qemu_file_get_error(f); } - qemu_savevm_state_cancel(); + qemu_savevm_state_cleanup(); if (ret != 0) { error_setg_errno(errp, -ret, "Error while writing VM state"); } @@ -1004,6 +1248,445 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id) return NULL; } +enum LoadVMExitCodes { + /* Allow a command to quit all layers of nested loadvm loops */ + LOADVM_QUIT = 1, +}; + +static int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis); + +/* ------ incoming postcopy messages ------ */ +/* 'advise' arrives before any transfers just to tell us that a postcopy + * *might* happen - it might be skipped if precopy transferred everything + * quickly. + */ +static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis) +{ + PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_ADVISE); + uint64_t remote_hps, remote_tps; + + trace_loadvm_postcopy_handle_advise(); + if (ps != POSTCOPY_INCOMING_NONE) { + error_report("CMD_POSTCOPY_ADVISE in wrong postcopy state (%d)", ps); + return -1; + } + + if (!postcopy_ram_supported_by_host()) { + return -1; + } + + remote_hps = qemu_get_be64(mis->from_src_file); + if (remote_hps != getpagesize()) { + /* + * Some combinations of mismatch are probably possible but it gets + * a bit more complicated. In particular we need to place whole + * host pages on the dest at once, and we need to ensure that we + * handle dirtying to make sure we never end up sending part of + * a hostpage on it's own. + */ + error_report("Postcopy needs matching host page sizes (s=%d d=%d)", + (int)remote_hps, getpagesize()); + return -1; + } + + remote_tps = qemu_get_be64(mis->from_src_file); + if (remote_tps != (1ul << qemu_target_page_bits())) { + /* + * Again, some differences could be dealt with, but for now keep it + * simple. + */ + error_report("Postcopy needs matching target page sizes (s=%d d=%d)", + (int)remote_tps, 1 << qemu_target_page_bits()); + return -1; + } + + if (ram_postcopy_incoming_init(mis)) { + return -1; + } + + postcopy_state_set(POSTCOPY_INCOMING_ADVISE); + + return 0; +} + +/* After postcopy we will be told to throw some pages away since they're + * dirty and will have to be demand fetched. Must happen before CPU is + * started. + * There can be 0..many of these messages, each encoding multiple pages. + */ +static int loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis, + uint16_t len) +{ + int tmp; + char ramid[256]; + PostcopyState ps = postcopy_state_get(); + + trace_loadvm_postcopy_ram_handle_discard(); + + switch (ps) { + case POSTCOPY_INCOMING_ADVISE: + /* 1st discard */ + tmp = postcopy_ram_prepare_discard(mis); + if (tmp) { + return tmp; + } + break; + + case POSTCOPY_INCOMING_DISCARD: + /* Expected state */ + break; + + default: + error_report("CMD_POSTCOPY_RAM_DISCARD in wrong postcopy state (%d)", + ps); + return -1; + } + /* We're expecting a + * Version (0) + * a RAM ID string (length byte, name, 0 term) + * then at least 1 16 byte chunk + */ + if (len < (1 + 1 + 1 + 1 + 2 * 8)) { + error_report("CMD_POSTCOPY_RAM_DISCARD invalid length (%d)", len); + return -1; + } + + tmp = qemu_get_byte(mis->from_src_file); + if (tmp != postcopy_ram_discard_version) { + error_report("CMD_POSTCOPY_RAM_DISCARD invalid version (%d)", tmp); + return -1; + } + + if (!qemu_get_counted_string(mis->from_src_file, ramid)) { + error_report("CMD_POSTCOPY_RAM_DISCARD Failed to read RAMBlock ID"); + return -1; + } + tmp = qemu_get_byte(mis->from_src_file); + if (tmp != 0) { + error_report("CMD_POSTCOPY_RAM_DISCARD missing nil (%d)", tmp); + return -1; + } + + len -= 3 + strlen(ramid); + if (len % 16) { + error_report("CMD_POSTCOPY_RAM_DISCARD invalid length (%d)", len); + return -1; + } + trace_loadvm_postcopy_ram_handle_discard_header(ramid, len); + while (len) { + uint64_t start_addr, block_length; + start_addr = qemu_get_be64(mis->from_src_file); + block_length = qemu_get_be64(mis->from_src_file); + + len -= 16; + int ret = ram_discard_range(mis, ramid, start_addr, + block_length); + if (ret) { + return ret; + } + } + trace_loadvm_postcopy_ram_handle_discard_end(); + + return 0; +} + +/* + * Triggered by a postcopy_listen command; this thread takes over reading + * the input stream, leaving the main thread free to carry on loading the rest + * of the device state (from RAM). + * (TODO:This could do with being in a postcopy file - but there again it's + * just another input loop, not that postcopy specific) + */ +static void *postcopy_ram_listen_thread(void *opaque) +{ + QEMUFile *f = opaque; + MigrationIncomingState *mis = migration_incoming_get_current(); + int load_res; + + migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, + MIGRATION_STATUS_POSTCOPY_ACTIVE); + qemu_sem_post(&mis->listen_thread_sem); + trace_postcopy_ram_listen_thread_start(); + + /* + * Because we're a thread and not a coroutine we can't yield + * in qemu_file, and thus we must be blocking now. + */ + qemu_file_set_blocking(f, true); + load_res = qemu_loadvm_state_main(f, mis); + /* And non-blocking again so we don't block in any cleanup */ + qemu_file_set_blocking(f, false); + + trace_postcopy_ram_listen_thread_exit(); + if (load_res < 0) { + error_report("%s: loadvm failed: %d", __func__, load_res); + qemu_file_set_error(f, load_res); + migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, + MIGRATION_STATUS_FAILED); + } else { + /* + * This looks good, but it's possible that the device loading in the + * main thread hasn't finished yet, and so we might not be in 'RUN' + * state yet; wait for the end of the main thread. + */ + qemu_event_wait(&mis->main_thread_load_event); + } + postcopy_ram_incoming_cleanup(mis); + + if (load_res < 0) { + /* + * If something went wrong then we have a bad state so exit; + * depending how far we got it might be possible at this point + * to leave the guest running and fire MCEs for pages that never + * arrived as a desperate recovery step. + */ + exit(EXIT_FAILURE); + } + + migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, + MIGRATION_STATUS_COMPLETED); + /* + * If everything has worked fine, then the main thread has waited + * for us to start, and we're the last use of the mis. + * (If something broke then qemu will have to exit anyway since it's + * got a bad migration state). + */ + migration_incoming_state_destroy(); + + + return NULL; +} + +/* After this message we must be able to immediately receive postcopy data */ +static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis) +{ + PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_LISTENING); + trace_loadvm_postcopy_handle_listen(); + if (ps != POSTCOPY_INCOMING_ADVISE && ps != POSTCOPY_INCOMING_DISCARD) { + error_report("CMD_POSTCOPY_LISTEN in wrong postcopy state (%d)", ps); + return -1; + } + if (ps == POSTCOPY_INCOMING_ADVISE) { + /* + * A rare case, we entered listen without having to do any discards, + * so do the setup that's normally done at the time of the 1st discard. + */ + postcopy_ram_prepare_discard(mis); + } + + /* + * Sensitise RAM - can now generate requests for blocks that don't exist + * However, at this point the CPU shouldn't be running, and the IO + * shouldn't be doing anything yet so don't actually expect requests + */ + if (postcopy_ram_enable_notify(mis)) { + return -1; + } + + if (mis->have_listen_thread) { + error_report("CMD_POSTCOPY_RAM_LISTEN already has a listen thread"); + return -1; + } + + mis->have_listen_thread = true; + /* Start up the listening thread and wait for it to signal ready */ + qemu_sem_init(&mis->listen_thread_sem, 0); + qemu_thread_create(&mis->listen_thread, "postcopy/listen", + postcopy_ram_listen_thread, mis->from_src_file, + QEMU_THREAD_DETACHED); + qemu_sem_wait(&mis->listen_thread_sem); + qemu_sem_destroy(&mis->listen_thread_sem); + + return 0; +} + + +typedef struct { + QEMUBH *bh; +} HandleRunBhData; + +static void loadvm_postcopy_handle_run_bh(void *opaque) +{ + Error *local_err = NULL; + HandleRunBhData *data = opaque; + + /* TODO we should move all of this lot into postcopy_ram.c or a shared code + * in migration.c + */ + cpu_synchronize_all_post_init(); + + qemu_announce_self(); + + /* Make sure all file formats flush their mutable metadata */ + bdrv_invalidate_cache_all(&local_err); + if (local_err) { + error_report_err(local_err); + } + + trace_loadvm_postcopy_handle_run_cpu_sync(); + cpu_synchronize_all_post_init(); + + trace_loadvm_postcopy_handle_run_vmstart(); + + if (autostart) { + /* Hold onto your hats, starting the CPU */ + vm_start(); + } else { + /* leave it paused and let management decide when to start the CPU */ + runstate_set(RUN_STATE_PAUSED); + } + + qemu_bh_delete(data->bh); + g_free(data); +} + +/* After all discards we can start running and asking for pages */ +static int loadvm_postcopy_handle_run(MigrationIncomingState *mis) +{ + PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_RUNNING); + HandleRunBhData *data; + + trace_loadvm_postcopy_handle_run(); + if (ps != POSTCOPY_INCOMING_LISTENING) { + error_report("CMD_POSTCOPY_RUN in wrong postcopy state (%d)", ps); + return -1; + } + + data = g_new(HandleRunBhData, 1); + data->bh = qemu_bh_new(loadvm_postcopy_handle_run_bh, data); + qemu_bh_schedule(data->bh); + + /* We need to finish reading the stream from the package + * and also stop reading anything more from the stream that loaded the + * package (since it's now being read by the listener thread). + * LOADVM_QUIT will quit all the layers of nested loadvm loops. + */ + return LOADVM_QUIT; +} + +/** + * Immediately following this command is a blob of data containing an embedded + * chunk of migration stream; read it and load it. + * + * @mis: Incoming state + * @length: Length of packaged data to read + * + * Returns: Negative values on error + * + */ +static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis) +{ + int ret; + uint8_t *buffer; + uint32_t length; + QEMUSizedBuffer *qsb; + + length = qemu_get_be32(mis->from_src_file); + trace_loadvm_handle_cmd_packaged(length); + + if (length > MAX_VM_CMD_PACKAGED_SIZE) { + error_report("Unreasonably large packaged state: %u", length); + return -1; + } + buffer = g_malloc0(length); + ret = qemu_get_buffer(mis->from_src_file, buffer, (int)length); + if (ret != length) { + g_free(buffer); + error_report("CMD_PACKAGED: Buffer receive fail ret=%d length=%d", + ret, length); + return (ret < 0) ? ret : -EAGAIN; + } + trace_loadvm_handle_cmd_packaged_received(ret); + + /* Setup a dummy QEMUFile that actually reads from the buffer */ + qsb = qsb_create(buffer, length); + g_free(buffer); /* Because qsb_create copies */ + if (!qsb) { + error_report("Unable to create qsb"); + } + QEMUFile *packf = qemu_bufopen("r", qsb); + + ret = qemu_loadvm_state_main(packf, mis); + trace_loadvm_handle_cmd_packaged_main(ret); + qemu_fclose(packf); + qsb_free(qsb); + + return ret; +} + +/* + * Process an incoming 'QEMU_VM_COMMAND' + * 0 just a normal return + * LOADVM_QUIT All good, but exit the loop + * <0 Error + */ +static int loadvm_process_command(QEMUFile *f) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + uint16_t cmd; + uint16_t len; + uint32_t tmp32; + + cmd = qemu_get_be16(f); + len = qemu_get_be16(f); + + trace_loadvm_process_command(cmd, len); + if (cmd >= MIG_CMD_MAX || cmd == MIG_CMD_INVALID) { + error_report("MIG_CMD 0x%x unknown (len 0x%x)", cmd, len); + return -EINVAL; + } + + if (mig_cmd_args[cmd].len != -1 && mig_cmd_args[cmd].len != len) { + error_report("%s received with bad length - expecting %zu, got %d", + mig_cmd_args[cmd].name, + (size_t)mig_cmd_args[cmd].len, len); + return -ERANGE; + } + + switch (cmd) { + case MIG_CMD_OPEN_RETURN_PATH: + if (mis->to_src_file) { + error_report("CMD_OPEN_RETURN_PATH called when RP already open"); + /* Not really a problem, so don't give up */ + return 0; + } + mis->to_src_file = qemu_file_get_return_path(f); + if (!mis->to_src_file) { + error_report("CMD_OPEN_RETURN_PATH failed"); + return -1; + } + break; + + case MIG_CMD_PING: + tmp32 = qemu_get_be32(f); + trace_loadvm_process_command_ping(tmp32); + if (!mis->to_src_file) { + error_report("CMD_PING (0x%x) received with no return path", + tmp32); + return -1; + } + migrate_send_rp_pong(mis, tmp32); + break; + + case MIG_CMD_PACKAGED: + return loadvm_handle_cmd_packaged(mis); + + case MIG_CMD_POSTCOPY_ADVISE: + return loadvm_postcopy_handle_advise(mis); + + case MIG_CMD_POSTCOPY_LISTEN: + return loadvm_postcopy_handle_listen(mis); + + case MIG_CMD_POSTCOPY_RUN: + return loadvm_postcopy_handle_run(mis); + + case MIG_CMD_POSTCOPY_RAM_DISCARD: + return loadvm_postcopy_ram_handle_discard(mis, len); + } + + return 0; +} + struct LoadStateEntry { QLIST_ENTRY(LoadStateEntry) entry; SaveStateEntry *se; @@ -1056,14 +1739,141 @@ void loadvm_free_handlers(MigrationIncomingState *mis) } } +static int +qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis) +{ + uint32_t instance_id, version_id, section_id; + SaveStateEntry *se; + LoadStateEntry *le; + char idstr[256]; + int ret; + + /* Read section start */ + section_id = qemu_get_be32(f); + if (!qemu_get_counted_string(f, idstr)) { + error_report("Unable to read ID string for section %u", + section_id); + return -EINVAL; + } + instance_id = qemu_get_be32(f); + version_id = qemu_get_be32(f); + + trace_qemu_loadvm_state_section_startfull(section_id, idstr, + instance_id, version_id); + /* Find savevm section */ + se = find_se(idstr, instance_id); + if (se == NULL) { + error_report("Unknown savevm section or instance '%s' %d", + idstr, instance_id); + return -EINVAL; + } + + /* Validate version */ + if (version_id > se->version_id) { + error_report("savevm: unsupported version %d for '%s' v%d", + version_id, idstr, se->version_id); + return -EINVAL; + } + + /* Add entry */ + le = g_malloc0(sizeof(*le)); + + le->se = se; + le->section_id = section_id; + le->version_id = version_id; + QLIST_INSERT_HEAD(&mis->loadvm_handlers, le, entry); + + ret = vmstate_load(f, le->se, le->version_id); + if (ret < 0) { + error_report("error while loading state for instance 0x%x of" + " device '%s'", instance_id, idstr); + return ret; + } + if (!check_section_footer(f, le)) { + return -EINVAL; + } + + return 0; +} + +static int +qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis) +{ + uint32_t section_id; + LoadStateEntry *le; + int ret; + + section_id = qemu_get_be32(f); + + trace_qemu_loadvm_state_section_partend(section_id); + QLIST_FOREACH(le, &mis->loadvm_handlers, entry) { + if (le->section_id == section_id) { + break; + } + } + if (le == NULL) { + error_report("Unknown savevm section %d", section_id); + return -EINVAL; + } + + ret = vmstate_load(f, le->se, le->version_id); + if (ret < 0) { + error_report("error while loading state section id %d(%s)", + section_id, le->se->idstr); + return ret; + } + if (!check_section_footer(f, le)) { + return -EINVAL; + } + + return 0; +} + +static int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis) +{ + uint8_t section_type; + int ret; + + while ((section_type = qemu_get_byte(f)) != QEMU_VM_EOF) { + + trace_qemu_loadvm_state_section(section_type); + switch (section_type) { + case QEMU_VM_SECTION_START: + case QEMU_VM_SECTION_FULL: + ret = qemu_loadvm_section_start_full(f, mis); + if (ret < 0) { + return ret; + } + break; + case QEMU_VM_SECTION_PART: + case QEMU_VM_SECTION_END: + ret = qemu_loadvm_section_part_end(f, mis); + if (ret < 0) { + return ret; + } + break; + case QEMU_VM_COMMAND: + ret = loadvm_process_command(f); + trace_qemu_loadvm_state_section_command(ret); + if ((ret < 0) || (ret & LOADVM_QUIT)) { + return ret; + } + break; + default: + error_report("Unknown savevm section type %d", section_type); + return -EINVAL; + } + } + + return 0; +} + int qemu_loadvm_state(QEMUFile *f) { MigrationIncomingState *mis = migration_incoming_get_current(); Error *local_err = NULL; - uint8_t section_type; unsigned int v; int ret; - int file_error_after_eof = -1; if (qemu_savevm_state_blocked(&local_err)) { error_report_err(local_err); @@ -1086,7 +1896,7 @@ int qemu_loadvm_state(QEMUFile *f) return -ENOTSUP; } - if (!savevm_state.skip_configuration) { + if (!savevm_state.skip_configuration || enforce_config_section()) { if (qemu_get_byte(f) != QEMU_VM_CONFIGURATION) { error_report("Configuration section missing"); return -EINVAL; @@ -1098,99 +1908,19 @@ int qemu_loadvm_state(QEMUFile *f) } } - while ((section_type = qemu_get_byte(f)) != QEMU_VM_EOF) { - uint32_t instance_id, version_id, section_id; - SaveStateEntry *se; - LoadStateEntry *le; - char idstr[256]; - - trace_qemu_loadvm_state_section(section_type); - switch (section_type) { - case QEMU_VM_SECTION_START: - case QEMU_VM_SECTION_FULL: - /* Read section start */ - section_id = qemu_get_be32(f); - if (!qemu_get_counted_string(f, idstr)) { - error_report("Unable to read ID string for section %u", - section_id); - return -EINVAL; - } - instance_id = qemu_get_be32(f); - version_id = qemu_get_be32(f); - - trace_qemu_loadvm_state_section_startfull(section_id, idstr, - instance_id, version_id); - /* Find savevm section */ - se = find_se(idstr, instance_id); - if (se == NULL) { - error_report("Unknown savevm section or instance '%s' %d", - idstr, instance_id); - ret = -EINVAL; - goto out; - } - - /* Validate version */ - if (version_id > se->version_id) { - error_report("savevm: unsupported version %d for '%s' v%d", - version_id, idstr, se->version_id); - ret = -EINVAL; - goto out; - } - - /* Add entry */ - le = g_malloc0(sizeof(*le)); - - le->se = se; - le->section_id = section_id; - le->version_id = version_id; - QLIST_INSERT_HEAD(&mis->loadvm_handlers, le, entry); - - ret = vmstate_load(f, le->se, le->version_id); - if (ret < 0) { - error_report("error while loading state for instance 0x%x of" - " device '%s'", instance_id, idstr); - goto out; - } - if (!check_section_footer(f, le)) { - ret = -EINVAL; - goto out; - } - break; - case QEMU_VM_SECTION_PART: - case QEMU_VM_SECTION_END: - section_id = qemu_get_be32(f); + ret = qemu_loadvm_state_main(f, mis); + qemu_event_set(&mis->main_thread_load_event); - trace_qemu_loadvm_state_section_partend(section_id); - QLIST_FOREACH(le, &mis->loadvm_handlers, entry) { - if (le->section_id == section_id) { - break; - } - } - if (le == NULL) { - error_report("Unknown savevm section %d", section_id); - ret = -EINVAL; - goto out; - } + trace_qemu_loadvm_state_post_main(ret); - ret = vmstate_load(f, le->se, le->version_id); - if (ret < 0) { - error_report("error while loading state section id %d(%s)", - section_id, le->se->idstr); - goto out; - } - if (!check_section_footer(f, le)) { - ret = -EINVAL; - goto out; - } - break; - default: - error_report("Unknown savevm section type %d", section_type); - ret = -EINVAL; - goto out; - } + if (mis->have_listen_thread) { + /* Listen thread still going, can't clean up yet */ + return ret; } - file_error_after_eof = qemu_file_get_error(f); + if (ret == 0) { + ret = qemu_file_get_error(f); + } /* * Try to read in the VMDESC section as well, so that dumping tools that @@ -1202,10 +1932,10 @@ int qemu_loadvm_state(QEMUFile *f) * We also mustn't read data that isn't there; some transports (RDMA) * will stall waiting for that data when the source has already closed. */ - if (should_send_vmdesc()) { + if (ret == 0 && should_send_vmdesc()) { uint8_t *buf; uint32_t size; - section_type = qemu_get_byte(f); + uint8_t section_type = qemu_get_byte(f); if (section_type != QEMU_VM_VMDESCRIPTION) { error_report("Expected vmdescription section, but got %d", @@ -1229,57 +1959,9 @@ int qemu_loadvm_state(QEMUFile *f) cpu_synchronize_all_post_init(); - ret = 0; - -out: - if (ret == 0) { - /* We may not have a VMDESC section, so ignore relative errors */ - ret = file_error_after_eof; - } - return ret; } -static BlockDriverState *find_vmstate_bs(void) -{ - BlockDriverState *bs = NULL; - while ((bs = bdrv_next(bs))) { - if (bdrv_can_snapshot(bs)) { - return bs; - } - } - return NULL; -} - -/* - * Deletes snapshots of a given name in all opened images. - */ -static int del_existing_snapshots(Monitor *mon, const char *name) -{ - BlockDriverState *bs; - QEMUSnapshotInfo sn1, *snapshot = &sn1; - Error *err = NULL; - - bs = NULL; - while ((bs = bdrv_next(bs))) { - if (bdrv_can_snapshot(bs) && - bdrv_snapshot_find(bs, snapshot, name) >= 0) { - bdrv_snapshot_delete_by_id_or_name(bs, name, &err); - if (err) { - monitor_printf(mon, - "Error while deleting snapshot on device '%s':" - " %s\n", - bdrv_get_device_name(bs), - error_get_pretty(err)); - error_free(err); - return -1; - } - } - } - - return 0; -} - void hmp_savevm(Monitor *mon, const QDict *qdict) { BlockDriverState *bs, *bs1; @@ -1292,27 +1974,28 @@ void hmp_savevm(Monitor *mon, const QDict *qdict) struct tm tm; const char *name = qdict_get_try_str(qdict, "name"); Error *local_err = NULL; + AioContext *aio_context; - /* Verify if there is a device that doesn't support snapshots and is writable */ - bs = NULL; - while ((bs = bdrv_next(bs))) { - - if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { - continue; - } + if (!bdrv_all_can_snapshot(&bs)) { + monitor_printf(mon, "Device '%s' is writable but does not " + "support snapshots.\n", bdrv_get_device_name(bs)); + return; + } - if (!bdrv_can_snapshot(bs)) { - monitor_printf(mon, "Device '%s' is writable but does not support snapshots.\n", - bdrv_get_device_name(bs)); - return; - } + /* Delete old snapshots of the same name */ + if (name && bdrv_all_delete_snapshot(name, &bs1, &local_err) < 0) { + error_reportf_err(local_err, + "Error while deleting snapshot on device '%s': ", + bdrv_get_device_name(bs1)); + return; } - bs = find_vmstate_bs(); - if (!bs) { + bs = bdrv_all_find_vmstate_bs(); + if (bs == NULL) { monitor_printf(mon, "No block device can accept snapshots\n"); return; } + aio_context = bdrv_get_aio_context(bs); saved_vm_running = runstate_is_running(); @@ -1323,6 +2006,8 @@ void hmp_savevm(Monitor *mon, const QDict *qdict) } vm_stop(RUN_STATE_SAVE_VM); + aio_context_acquire(aio_context); + memset(sn, 0, sizeof(*sn)); /* fill auxiliary fields */ @@ -1345,11 +2030,6 @@ void hmp_savevm(Monitor *mon, const QDict *qdict) strftime(sn->name, sizeof(sn->name), "vm-%Y%m%d%H%M%S", &tm); } - /* Delete old snapshots of the same name */ - if (name && del_existing_snapshots(mon, name) < 0) { - goto the_end; - } - /* save the VM state */ f = qemu_fopen_bdrv(bs, 1); if (!f) { @@ -1360,27 +2040,18 @@ void hmp_savevm(Monitor *mon, const QDict *qdict) vm_state_size = qemu_ftell(f); qemu_fclose(f); if (ret < 0) { - monitor_printf(mon, "%s\n", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); goto the_end; } - /* create the snapshots */ - - bs1 = NULL; - while ((bs1 = bdrv_next(bs1))) { - if (bdrv_can_snapshot(bs1)) { - /* Write VM state size only to the image that contains the state */ - sn->vm_state_size = (bs == bs1 ? vm_state_size : 0); - ret = bdrv_snapshot_create(bs1, sn); - if (ret < 0) { - monitor_printf(mon, "Error while creating snapshot on '%s'\n", - bdrv_get_device_name(bs1)); - } - } + ret = bdrv_all_create_snapshot(sn, bs, vm_state_size, &bs); + if (ret < 0) { + monitor_printf(mon, "Error while creating snapshot on '%s'\n", + bdrv_get_device_name(bs)); } the_end: + aio_context_release(aio_context); if (saved_vm_running) { vm_start(); } @@ -1419,15 +2090,31 @@ int load_vmstate(const char *name) QEMUSnapshotInfo sn; QEMUFile *f; int ret; + AioContext *aio_context; + + if (!bdrv_all_can_snapshot(&bs)) { + error_report("Device '%s' is writable but does not support snapshots.", + bdrv_get_device_name(bs)); + return -ENOTSUP; + } + ret = bdrv_all_find_snapshot(name, &bs); + if (ret < 0) { + error_report("Device '%s' does not have the requested snapshot '%s'", + bdrv_get_device_name(bs), name); + return ret; + } - bs_vm_state = find_vmstate_bs(); + bs_vm_state = bdrv_all_find_vmstate_bs(); if (!bs_vm_state) { error_report("No block device supports snapshots"); return -ENOTSUP; } + aio_context = bdrv_get_aio_context(bs_vm_state); /* Don't even try to load empty VM states */ + aio_context_acquire(aio_context); ret = bdrv_snapshot_find(bs_vm_state, &sn, name); + aio_context_release(aio_context); if (ret < 0) { return ret; } else if (sn.vm_state_size == 0) { @@ -1436,42 +2123,14 @@ int load_vmstate(const char *name) return -EINVAL; } - /* Verify if there is any device that doesn't support snapshots and is - writable and check if the requested snapshot is available too. */ - bs = NULL; - while ((bs = bdrv_next(bs))) { - - if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { - continue; - } - - if (!bdrv_can_snapshot(bs)) { - error_report("Device '%s' is writable but does not support snapshots.", - bdrv_get_device_name(bs)); - return -ENOTSUP; - } - - ret = bdrv_snapshot_find(bs, &sn, name); - if (ret < 0) { - error_report("Device '%s' does not have the requested snapshot '%s'", - bdrv_get_device_name(bs), name); - return ret; - } - } - /* Flush all IO requests so they don't interfere with the new state. */ bdrv_drain_all(); - bs = NULL; - while ((bs = bdrv_next(bs))) { - if (bdrv_can_snapshot(bs)) { - ret = bdrv_snapshot_goto(bs, name); - if (ret < 0) { - error_report("Error %d while activating snapshot '%s' on '%s'", - ret, name, bdrv_get_device_name(bs)); - return ret; - } - } + ret = bdrv_all_goto_snapshot(name, &bs); + if (ret < 0) { + error_report("Error %d while activating snapshot '%s' on '%s'", + ret, name, bdrv_get_device_name(bs)); + return ret; } /* restore the VM state */ @@ -1483,9 +2142,12 @@ int load_vmstate(const char *name) qemu_system_reset(VMRESET_SILENT); migration_incoming_state_new(f); - ret = qemu_loadvm_state(f); + aio_context_acquire(aio_context); + ret = qemu_loadvm_state(f); qemu_fclose(f); + aio_context_release(aio_context); + migration_incoming_state_destroy(); if (ret < 0) { error_report("Error %d while loading VM state", ret); @@ -1501,43 +2163,33 @@ void hmp_delvm(Monitor *mon, const QDict *qdict) Error *err; const char *name = qdict_get_str(qdict, "name"); - if (!find_vmstate_bs()) { - monitor_printf(mon, "No block device supports snapshots\n"); - return; - } - - bs = NULL; - while ((bs = bdrv_next(bs))) { - if (bdrv_can_snapshot(bs)) { - err = NULL; - bdrv_snapshot_delete_by_id_or_name(bs, name, &err); - if (err) { - monitor_printf(mon, - "Error while deleting snapshot on device '%s':" - " %s\n", - bdrv_get_device_name(bs), - error_get_pretty(err)); - error_free(err); - } - } + if (bdrv_all_delete_snapshot(name, &bs, &err) < 0) { + error_reportf_err(err, + "Error while deleting snapshot on device '%s': ", + bdrv_get_device_name(bs)); } } void hmp_info_snapshots(Monitor *mon, const QDict *qdict) { BlockDriverState *bs, *bs1; - QEMUSnapshotInfo *sn_tab, *sn, s, *sn_info = &s; - int nb_sns, i, ret, available; + QEMUSnapshotInfo *sn_tab, *sn; + int nb_sns, i; int total; int *available_snapshots; + AioContext *aio_context; - bs = find_vmstate_bs(); + bs = bdrv_all_find_vmstate_bs(); if (!bs) { monitor_printf(mon, "No available block device supports snapshots\n"); return; } + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); nb_sns = bdrv_snapshot_list(bs, &sn_tab); + aio_context_release(aio_context); + if (nb_sns < 0) { monitor_printf(mon, "bdrv_snapshot_list: error %d\n", nb_sns); return; @@ -1548,24 +2200,10 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict) return; } - available_snapshots = g_malloc0(sizeof(int) * nb_sns); + available_snapshots = g_new0(int, nb_sns); total = 0; for (i = 0; i < nb_sns; i++) { - sn = &sn_tab[i]; - available = 1; - bs1 = NULL; - - while ((bs1 = bdrv_next(bs1))) { - if (bdrv_can_snapshot(bs1) && bs1 != bs) { - ret = bdrv_snapshot_find(bs1, sn_info, sn->id_str); - if (ret < 0) { - available = 0; - break; - } - } - } - - if (available) { + if (bdrv_all_find_snapshot(sn_tab[i].id_str, &bs1) == 0) { available_snapshots[total] = i; total++; } diff --git a/qemu/migration/tcp.c b/qemu/migration/tcp.c index ae891728e..e1fa7f8f1 100644 --- a/qemu/migration/tcp.c +++ b/qemu/migration/tcp.c @@ -13,7 +13,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/error-report.h" @@ -39,11 +39,11 @@ static void tcp_wait_for_connect(int fd, Error *err, void *opaque) if (fd < 0) { DPRINTF("migrate connect error: %s\n", error_get_pretty(err)); - s->file = NULL; + s->to_dst_file = NULL; migrate_fd_error(s); } else { DPRINTF("migrate connect success\n"); - s->file = qemu_fopen_socket(fd, "wb"); + s->to_dst_file = qemu_fopen_socket(fd, "wb"); migrate_fd_connect(s); } } @@ -59,12 +59,11 @@ static void tcp_accept_incoming_migration(void *opaque) socklen_t addrlen = sizeof(addr); int s = (intptr_t)opaque; QEMUFile *f; - int c, err; + int c; do { c = qemu_accept(s, (struct sockaddr *)&addr, &addrlen); - err = socket_error(); - } while (c < 0 && err == EINTR); + } while (c < 0 && errno == EINTR); qemu_set_fd_handler(s, NULL, NULL, NULL); closesocket(s); @@ -72,7 +71,7 @@ static void tcp_accept_incoming_migration(void *opaque) if (c < 0) { error_report("could not accept migration connection (%s)", - strerror(err)); + strerror(errno)); return; } diff --git a/qemu/migration/unix.c b/qemu/migration/unix.c index b591813eb..d9aac36b9 100644 --- a/qemu/migration/unix.c +++ b/qemu/migration/unix.c @@ -13,7 +13,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/error-report.h" @@ -39,11 +39,11 @@ static void unix_wait_for_connect(int fd, Error *err, void *opaque) if (fd < 0) { DPRINTF("migrate connect error: %s\n", error_get_pretty(err)); - s->file = NULL; + s->to_dst_file = NULL; migrate_fd_error(s); } else { DPRINTF("migrate connect success\n"); - s->file = qemu_fopen_socket(fd, "wb"); + s->to_dst_file = qemu_fopen_socket(fd, "wb"); migrate_fd_connect(s); } } diff --git a/qemu/migration/vmstate.c b/qemu/migration/vmstate.c index e8ccf22f6..bf3d5db30 100644 --- a/qemu/migration/vmstate.c +++ b/qemu/migration/vmstate.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "migration/migration.h" #include "migration/qemu-file.h" @@ -28,6 +29,10 @@ static int vmstate_n_elems(void *opaque, VMStateField *field) n_elems = *(uint8_t *)(opaque+field->num_offset); } + if (field->flags & VMS_MULTIPLY_ELEMENTS) { + n_elems *= field->num; + } + return n_elems; } @@ -794,6 +799,29 @@ const VMStateInfo vmstate_info_float64 = { .put = put_float64, }; +/* CPU_DoubleU type */ + +static int get_cpudouble(QEMUFile *f, void *pv, size_t size) +{ + CPU_DoubleU *v = pv; + qemu_get_be32s(f, &v->l.upper); + qemu_get_be32s(f, &v->l.lower); + return 0; +} + +static void put_cpudouble(QEMUFile *f, void *pv, size_t size) +{ + CPU_DoubleU *v = pv; + qemu_put_be32s(f, &v->l.upper); + qemu_put_be32s(f, &v->l.lower); +} + +const VMStateInfo vmstate_info_cpudouble = { + .name = "CPU_Double_U", + .get = get_cpudouble, + .put = put_cpudouble, +}; + /* uint8_t buffers */ static int get_buffer(QEMUFile *f, void *pv, size_t size) diff --git a/qemu/migration/xbzrle.c b/qemu/migration/xbzrle.c index 8e220bf25..c85833925 100644 --- a/qemu/migration/xbzrle.c +++ b/qemu/migration/xbzrle.c @@ -10,7 +10,8 @@ * See the COPYING file in the top-level directory. * */ -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "qemu/cutils.h" #include "include/migration/migration.h" /* diff --git a/qemu/module-common.c b/qemu/module-common.c index 50c67500b..0a3cb8aea 100644 --- a/qemu/module-common.c +++ b/qemu/module-common.c @@ -1,4 +1,4 @@ -#include "config-host.h" +#include "qemu/osdep.h" #include "qemu/module.h" void qemu_module_dummy(void) diff --git a/qemu/monitor.c b/qemu/monitor.c index aeea2b5f9..d1c193013 100644 --- a/qemu/monitor.c +++ b/qemu/monitor.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include "hw/hw.h" #include "monitor/qdev.h" @@ -41,6 +42,7 @@ #include "ui/console.h" #include "ui/input.h" #include "sysemu/blockdev.h" +#include "sysemu/block-backend.h" #include "audio/audio.h" #include "disas/disas.h" #include "sysemu/balloon.h" @@ -59,22 +61,24 @@ #include "qapi/qmp/json-streamer.h" #include "qapi/qmp/json-parser.h" #include -#include "qemu/osdep.h" #include "cpu.h" #include "trace.h" #include "trace/control.h" +#include "monitor/hmp-target.h" #ifdef CONFIG_TRACE_SIMPLE #include "trace/simple.h" #endif #include "exec/memory.h" -#include "exec/cpu_ldst.h" #include "qmp-commands.h" #include "hmp.h" #include "qemu/thread.h" #include "block/qapi.h" #include "qapi/qmp-event.h" #include "qapi-event.h" +#include "qmp-introspect.h" #include "sysemu/block-backend.h" +#include "sysemu/qtest.h" +#include "qemu/cutils.h" /* for hmp_info_irq/pic */ #if defined(TARGET_SPARC) @@ -82,6 +86,10 @@ #endif #include "hw/lm32/lm32_pic.h" +#if defined(TARGET_S390X) +#include "hw/s390x/storage-keys.h" +#endif + /* * Supported types: * @@ -176,13 +184,16 @@ typedef struct { * instance. */ typedef struct MonitorQAPIEventState { - QAPIEvent event; /* Event being tracked */ - int64_t rate; /* Minimum time (in ns) between two events */ - int64_t last; /* QEMU_CLOCK_REALTIME value at last emission */ + QAPIEvent event; /* Throttling state for this event type and... */ + QDict *data; /* ... data, see qapi_event_throttle_equal() */ QEMUTimer *timer; /* Timer for handling delayed events */ - QObject *data; /* Event pending delayed dispatch */ + QDict *qdict; /* Delayed event (if any) */ } MonitorQAPIEventState; +typedef struct { + int64_t rate; /* Minimum time (in ns) between two events */ +} MonitorQAPIEventConf; + struct Monitor { CharDriverState *chr; int reset_seen; @@ -224,6 +235,8 @@ static const mon_cmd_t qmp_cmds[]; Monitor *cur_mon; +static QEMUClockType event_clock_type = QEMU_CLOCK_REALTIME; + static void monitor_command_cb(void *opaque, const char *cmdline, void *readline_opaque); @@ -367,8 +380,7 @@ void monitor_printf(Monitor *mon, const char *fmt, ...) va_end(ap); } -static int GCC_FMT_ATTR(2, 3) monitor_fprintf(FILE *stream, - const char *fmt, ...) +int monitor_fprintf(FILE *stream, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -396,7 +408,7 @@ static QDict *build_qmp_error_dict(Error *err) QObject *obj; obj = qobject_from_jsonf("{ 'error': { 'class': %s, 'desc': %s } }", - ErrorClass_lookup[error_get_class(err)], + QapiErrorClass_lookup[error_get_class(err)], error_get_pretty(err)); return qobject_to_qdict(obj); @@ -434,132 +446,174 @@ static void monitor_protocol_emitter(Monitor *mon, QObject *data, } -static MonitorQAPIEventState monitor_qapi_event_state[QAPI_EVENT_MAX]; +static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = { + /* Limit guest-triggerable events to 1 per second */ + [QAPI_EVENT_RTC_CHANGE] = { 1000 * SCALE_MS }, + [QAPI_EVENT_WATCHDOG] = { 1000 * SCALE_MS }, + [QAPI_EVENT_BALLOON_CHANGE] = { 1000 * SCALE_MS }, + [QAPI_EVENT_QUORUM_REPORT_BAD] = { 1000 * SCALE_MS }, + [QAPI_EVENT_QUORUM_FAILURE] = { 1000 * SCALE_MS }, + [QAPI_EVENT_VSERPORT_CHANGE] = { 1000 * SCALE_MS }, +}; + +GHashTable *monitor_qapi_event_state; /* * Emits the event to every monitor instance, @event is only used for trace * Called with monitor_lock held. */ -static void monitor_qapi_event_emit(QAPIEvent event, QObject *data) +static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict) { Monitor *mon; - trace_monitor_protocol_event_emit(event, data); + trace_monitor_protocol_event_emit(event, qdict); QLIST_FOREACH(mon, &mon_list, entry) { if (monitor_is_qmp(mon) && mon->qmp.in_command_mode) { - monitor_json_emitter(mon, data); + monitor_json_emitter(mon, QOBJECT(qdict)); } } } +static void monitor_qapi_event_handler(void *opaque); + /* * Queue a new event for emission to Monitor instances, * applying any rate limiting if required. */ static void -monitor_qapi_event_queue(QAPIEvent event, QDict *data, Error **errp) +monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) { + MonitorQAPIEventConf *evconf; MonitorQAPIEventState *evstate; - assert(event < QAPI_EVENT_MAX); - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - evstate = &(monitor_qapi_event_state[event]); - trace_monitor_protocol_event_queue(event, - data, - evstate->rate, - evstate->last, - now); + assert(event < QAPI_EVENT__MAX); + evconf = &monitor_qapi_event_conf[event]; + trace_monitor_protocol_event_queue(event, qdict, evconf->rate); - /* Rate limit of 0 indicates no throttling */ qemu_mutex_lock(&monitor_lock); - if (!evstate->rate) { - monitor_qapi_event_emit(event, QOBJECT(data)); - evstate->last = now; + + if (!evconf->rate) { + /* Unthrottled event */ + monitor_qapi_event_emit(event, qdict); } else { - int64_t delta = now - evstate->last; - if (evstate->data || - delta < evstate->rate) { - /* If there's an existing event pending, replace - * it with the new event, otherwise schedule a - * timer for delayed emission + 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); + assert(!evstate || timer_pending(evstate->timer)); + + if (evstate) { + /* + * Timer is pending for (at least) evconf->rate ns after + * last send. Store event for sending when timer fires, + * replacing a prior stored event if any. */ - if (evstate->data) { - qobject_decref(evstate->data); - } else { - int64_t then = evstate->last + evstate->rate; - timer_mod_ns(evstate->timer, then); - } - evstate->data = QOBJECT(data); - qobject_incref(evstate->data); + QDECREF(evstate->qdict); + evstate->qdict = qdict; + QINCREF(evstate->qdict); } else { - monitor_qapi_event_emit(event, QOBJECT(data)); - evstate->last = now; + /* + * Last send was (at least) evconf->rate ns ago. + * Send immediately, and arm the timer to call + * monitor_qapi_event_handler() in evconf->rate ns. Any + * events arriving before then will be delayed until then. + */ + int64_t now = qemu_clock_get_ns(event_clock_type); + + monitor_qapi_event_emit(event, qdict); + + evstate = g_new(MonitorQAPIEventState, 1); + evstate->event = event; + evstate->data = data; + QINCREF(evstate->data); + evstate->qdict = NULL; + evstate->timer = timer_new_ns(event_clock_type, + monitor_qapi_event_handler, + evstate); + g_hash_table_add(monitor_qapi_event_state, evstate); + timer_mod_ns(evstate->timer, now + evconf->rate); } } + qemu_mutex_unlock(&monitor_lock); } /* - * The callback invoked by QemuTimer when a delayed - * event is ready to be emitted + * This function runs evconf->rate ns after sending a throttled + * event. + * If another event has since been stored, send it. */ static void monitor_qapi_event_handler(void *opaque) { MonitorQAPIEventState *evstate = opaque; - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + MonitorQAPIEventConf *evconf = &monitor_qapi_event_conf[evstate->event]; - trace_monitor_protocol_event_handler(evstate->event, - evstate->data, - evstate->last, - now); + trace_monitor_protocol_event_handler(evstate->event, evstate->qdict); qemu_mutex_lock(&monitor_lock); - if (evstate->data) { - monitor_qapi_event_emit(evstate->event, evstate->data); - qobject_decref(evstate->data); - evstate->data = NULL; + + if (evstate->qdict) { + int64_t now = qemu_clock_get_ns(event_clock_type); + + monitor_qapi_event_emit(evstate->event, evstate->qdict); + QDECREF(evstate->qdict); + evstate->qdict = NULL; + timer_mod_ns(evstate->timer, now + evconf->rate); + } else { + g_hash_table_remove(monitor_qapi_event_state, evstate); + QDECREF(evstate->data); + timer_free(evstate->timer); + g_free(evstate); } - evstate->last = now; + qemu_mutex_unlock(&monitor_lock); } -/* - * @event: the event ID to be limited - * @rate: the rate limit in milliseconds - * - * Sets a rate limit on a particular event, so no - * more than 1 event will be emitted within @rate - * milliseconds - */ -static void -monitor_qapi_event_throttle(QAPIEvent event, int64_t rate) +static unsigned int qapi_event_throttle_hash(const void *key) { - MonitorQAPIEventState *evstate; - assert(event < QAPI_EVENT_MAX); + const MonitorQAPIEventState *evstate = key; + unsigned int hash = evstate->event * 255; - evstate = &(monitor_qapi_event_state[event]); + if (evstate->event == QAPI_EVENT_VSERPORT_CHANGE) { + hash += g_str_hash(qdict_get_str(evstate->data, "id")); + } - trace_monitor_protocol_event_throttle(event, rate); - evstate->event = event; - assert(rate * SCALE_MS <= INT64_MAX); - evstate->rate = rate * SCALE_MS; - evstate->last = 0; - evstate->data = NULL; - evstate->timer = timer_new(QEMU_CLOCK_REALTIME, - SCALE_MS, - monitor_qapi_event_handler, - evstate); + if (evstate->event == QAPI_EVENT_QUORUM_REPORT_BAD) { + hash += g_str_hash(qdict_get_str(evstate->data, "node-name")); + } + + return hash; +} + +static gboolean qapi_event_throttle_equal(const void *a, const void *b) +{ + const MonitorQAPIEventState *eva = a; + const MonitorQAPIEventState *evb = b; + + if (eva->event != evb->event) { + return FALSE; + } + + if (eva->event == QAPI_EVENT_VSERPORT_CHANGE) { + return !strcmp(qdict_get_str(eva->data, "id"), + qdict_get_str(evb->data, "id")); + } + + if (eva->event == QAPI_EVENT_QUORUM_REPORT_BAD) { + return !strcmp(qdict_get_str(eva->data, "node-name"), + qdict_get_str(evb->data, "node-name")); + } + + return TRUE; } static void monitor_qapi_event_init(void) { - /* Limit guest-triggerable events to 1 per second */ - monitor_qapi_event_throttle(QAPI_EVENT_RTC_CHANGE, 1000); - monitor_qapi_event_throttle(QAPI_EVENT_WATCHDOG, 1000); - monitor_qapi_event_throttle(QAPI_EVENT_BALLOON_CHANGE, 1000); - monitor_qapi_event_throttle(QAPI_EVENT_QUORUM_REPORT_BAD, 1000); - monitor_qapi_event_throttle(QAPI_EVENT_QUORUM_FAILURE, 1000); - monitor_qapi_event_throttle(QAPI_EVENT_VSERPORT_CHANGE, 1000); + if (qtest_enabled()) { + event_clock_type = QEMU_CLOCK_VIRTUAL; + } + monitor_qapi_event_state = g_hash_table_new(qapi_event_throttle_hash, + qapi_event_throttle_equal); qmp_event_set_func_emit(monitor_qapi_event_queue); } @@ -678,7 +732,7 @@ static int get_str(char *buf, int buf_size, const char **pp) case '\"': break; default: - qemu_printf("unsupported escape code: '\\%c'\n", c); + printf("unsupported escape code: '\\%c'\n", c); goto fail; } if ((q - buf) < buf_size - 1) { @@ -692,7 +746,7 @@ static int get_str(char *buf, int buf_size, const char **pp) } } if (*p != '\"') { - qemu_printf("unterminated string\n"); + printf("unterminated string\n"); goto fail; } p++; @@ -910,7 +964,7 @@ EventInfoList *qmp_query_events(Error **errp) EventInfoList *info, *ev_list = NULL; QAPIEvent e; - for (e = 0 ; e < QAPI_EVENT_MAX ; e++) { + for (e = 0 ; e < QAPI_EVENT__MAX ; e++) { const char *event_name = QAPIEvent_lookup[e]; assert(event_name != NULL); info = g_malloc0(sizeof(*info)); @@ -924,6 +978,21 @@ EventInfoList *qmp_query_events(Error **errp) return ev_list; } +/* + * Minor hack: generated marshalling suppressed for this command + * ('gen': false in the schema) so we can parse the JSON string + * directly into QObject instead of first parsing it with + * visit_type_SchemaInfoList() into a SchemaInfoList, then marshal it + * to QObject with generated output marshallers, every time. Instead, + * we do it in test-qmp-input-visitor.c, just to make sure + * qapi-introspect.py's output actually conforms to the schema. + */ +static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data, + Error **errp) +{ + *ret_data = qobject_from_json(qmp_schema_json); +} + /* set the current CPU defined by the user */ int monitor_set_cpu(int cpu_index) { @@ -937,7 +1006,7 @@ int monitor_set_cpu(int cpu_index) return 0; } -static CPUState *mon_get_cpu(void) +CPUState *mon_get_cpu(void) { if (!cur_mon->mon_cpu) { monitor_set_cpu(0); @@ -946,7 +1015,7 @@ static CPUState *mon_get_cpu(void) return cur_mon->mon_cpu; } -static CPUArchState *mon_get_cpu_env(void) +CPUArchState *mon_get_cpu_env(void) { return mon_get_cpu()->env_ptr; } @@ -1335,7 +1404,7 @@ static void hmp_mouse_move(Monitor *mon, const QDict *qdict) static void hmp_mouse_button(Monitor *mon, const QDict *qdict) { - static uint32_t bmap[INPUT_BUTTON_MAX] = { + static uint32_t bmap[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON, [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON, [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON, @@ -1413,449 +1482,12 @@ static void hmp_boot_set(Monitor *mon, const QDict *qdict) qemu_boot_set(bootdevice, &local_err); if (local_err) { - monitor_printf(mon, "%s\n", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); } else { monitor_printf(mon, "boot device list now set to %s\n", bootdevice); } } -#if defined(TARGET_I386) -static void print_pte(Monitor *mon, hwaddr addr, - hwaddr pte, - hwaddr mask) -{ -#ifdef TARGET_X86_64 - if (addr & (1ULL << 47)) { - addr |= -1LL << 48; - } -#endif - monitor_printf(mon, TARGET_FMT_plx ": " TARGET_FMT_plx - " %c%c%c%c%c%c%c%c%c\n", - addr, - pte & mask, - pte & PG_NX_MASK ? 'X' : '-', - pte & PG_GLOBAL_MASK ? 'G' : '-', - pte & PG_PSE_MASK ? 'P' : '-', - pte & PG_DIRTY_MASK ? 'D' : '-', - pte & PG_ACCESSED_MASK ? 'A' : '-', - pte & PG_PCD_MASK ? 'C' : '-', - pte & PG_PWT_MASK ? 'T' : '-', - pte & PG_USER_MASK ? 'U' : '-', - pte & PG_RW_MASK ? 'W' : '-'); -} - -static void tlb_info_32(Monitor *mon, CPUArchState *env) -{ - unsigned int l1, l2; - uint32_t pgd, pde, pte; - - pgd = env->cr[3] & ~0xfff; - for(l1 = 0; l1 < 1024; l1++) { - cpu_physical_memory_read(pgd + l1 * 4, &pde, 4); - pde = le32_to_cpu(pde); - if (pde & PG_PRESENT_MASK) { - if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { - /* 4M pages */ - print_pte(mon, (l1 << 22), pde, ~((1 << 21) - 1)); - } else { - for(l2 = 0; l2 < 1024; l2++) { - cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, &pte, 4); - pte = le32_to_cpu(pte); - if (pte & PG_PRESENT_MASK) { - print_pte(mon, (l1 << 22) + (l2 << 12), - pte & ~PG_PSE_MASK, - ~0xfff); - } - } - } - } - } -} - -static void tlb_info_pae32(Monitor *mon, CPUArchState *env) -{ - unsigned int l1, l2, l3; - uint64_t pdpe, pde, pte; - uint64_t pdp_addr, pd_addr, pt_addr; - - pdp_addr = env->cr[3] & ~0x1f; - for (l1 = 0; l1 < 4; l1++) { - cpu_physical_memory_read(pdp_addr + l1 * 8, &pdpe, 8); - pdpe = le64_to_cpu(pdpe); - if (pdpe & PG_PRESENT_MASK) { - pd_addr = pdpe & 0x3fffffffff000ULL; - for (l2 = 0; l2 < 512; l2++) { - cpu_physical_memory_read(pd_addr + l2 * 8, &pde, 8); - pde = le64_to_cpu(pde); - if (pde & PG_PRESENT_MASK) { - if (pde & PG_PSE_MASK) { - /* 2M pages with PAE, CR4.PSE is ignored */ - print_pte(mon, (l1 << 30 ) + (l2 << 21), pde, - ~((hwaddr)(1 << 20) - 1)); - } else { - pt_addr = pde & 0x3fffffffff000ULL; - for (l3 = 0; l3 < 512; l3++) { - cpu_physical_memory_read(pt_addr + l3 * 8, &pte, 8); - pte = le64_to_cpu(pte); - if (pte & PG_PRESENT_MASK) { - print_pte(mon, (l1 << 30 ) + (l2 << 21) - + (l3 << 12), - pte & ~PG_PSE_MASK, - ~(hwaddr)0xfff); - } - } - } - } - } - } - } -} - -#ifdef TARGET_X86_64 -static void tlb_info_64(Monitor *mon, CPUArchState *env) -{ - uint64_t l1, l2, l3, l4; - uint64_t pml4e, pdpe, pde, pte; - uint64_t pml4_addr, pdp_addr, pd_addr, pt_addr; - - pml4_addr = env->cr[3] & 0x3fffffffff000ULL; - for (l1 = 0; l1 < 512; l1++) { - cpu_physical_memory_read(pml4_addr + l1 * 8, &pml4e, 8); - pml4e = le64_to_cpu(pml4e); - if (pml4e & PG_PRESENT_MASK) { - pdp_addr = pml4e & 0x3fffffffff000ULL; - for (l2 = 0; l2 < 512; l2++) { - cpu_physical_memory_read(pdp_addr + l2 * 8, &pdpe, 8); - pdpe = le64_to_cpu(pdpe); - if (pdpe & PG_PRESENT_MASK) { - if (pdpe & PG_PSE_MASK) { - /* 1G pages, CR4.PSE is ignored */ - print_pte(mon, (l1 << 39) + (l2 << 30), pdpe, - 0x3ffffc0000000ULL); - } else { - pd_addr = pdpe & 0x3fffffffff000ULL; - for (l3 = 0; l3 < 512; l3++) { - cpu_physical_memory_read(pd_addr + l3 * 8, &pde, 8); - pde = le64_to_cpu(pde); - if (pde & PG_PRESENT_MASK) { - if (pde & PG_PSE_MASK) { - /* 2M pages, CR4.PSE is ignored */ - print_pte(mon, (l1 << 39) + (l2 << 30) + - (l3 << 21), pde, - 0x3ffffffe00000ULL); - } else { - pt_addr = pde & 0x3fffffffff000ULL; - for (l4 = 0; l4 < 512; l4++) { - cpu_physical_memory_read(pt_addr - + l4 * 8, - &pte, 8); - pte = le64_to_cpu(pte); - if (pte & PG_PRESENT_MASK) { - print_pte(mon, (l1 << 39) + - (l2 << 30) + - (l3 << 21) + (l4 << 12), - pte & ~PG_PSE_MASK, - 0x3fffffffff000ULL); - } - } - } - } - } - } - } - } - } - } -} -#endif - -static void hmp_info_tlb(Monitor *mon, const QDict *qdict) -{ - CPUArchState *env; - - env = mon_get_cpu_env(); - - if (!(env->cr[0] & CR0_PG_MASK)) { - monitor_printf(mon, "PG disabled\n"); - return; - } - if (env->cr[4] & CR4_PAE_MASK) { -#ifdef TARGET_X86_64 - if (env->hflags & HF_LMA_MASK) { - tlb_info_64(mon, env); - } else -#endif - { - tlb_info_pae32(mon, env); - } - } else { - tlb_info_32(mon, env); - } -} - -static void mem_print(Monitor *mon, hwaddr *pstart, - int *plast_prot, - hwaddr end, int prot) -{ - int prot1; - prot1 = *plast_prot; - if (prot != prot1) { - if (*pstart != -1) { - monitor_printf(mon, TARGET_FMT_plx "-" TARGET_FMT_plx " " - TARGET_FMT_plx " %c%c%c\n", - *pstart, end, end - *pstart, - prot1 & PG_USER_MASK ? 'u' : '-', - 'r', - prot1 & PG_RW_MASK ? 'w' : '-'); - } - if (prot != 0) - *pstart = end; - else - *pstart = -1; - *plast_prot = prot; - } -} - -static void mem_info_32(Monitor *mon, CPUArchState *env) -{ - unsigned int l1, l2; - int prot, last_prot; - uint32_t pgd, pde, pte; - hwaddr start, end; - - pgd = env->cr[3] & ~0xfff; - last_prot = 0; - start = -1; - for(l1 = 0; l1 < 1024; l1++) { - cpu_physical_memory_read(pgd + l1 * 4, &pde, 4); - pde = le32_to_cpu(pde); - end = l1 << 22; - if (pde & PG_PRESENT_MASK) { - if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { - prot = pde & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); - mem_print(mon, &start, &last_prot, end, prot); - } else { - for(l2 = 0; l2 < 1024; l2++) { - cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, &pte, 4); - pte = le32_to_cpu(pte); - end = (l1 << 22) + (l2 << 12); - if (pte & PG_PRESENT_MASK) { - prot = pte & pde & - (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); - } else { - prot = 0; - } - mem_print(mon, &start, &last_prot, end, prot); - } - } - } else { - prot = 0; - mem_print(mon, &start, &last_prot, end, prot); - } - } - /* Flush last range */ - mem_print(mon, &start, &last_prot, (hwaddr)1 << 32, 0); -} - -static void mem_info_pae32(Monitor *mon, CPUArchState *env) -{ - unsigned int l1, l2, l3; - int prot, last_prot; - uint64_t pdpe, pde, pte; - uint64_t pdp_addr, pd_addr, pt_addr; - hwaddr start, end; - - pdp_addr = env->cr[3] & ~0x1f; - last_prot = 0; - start = -1; - for (l1 = 0; l1 < 4; l1++) { - cpu_physical_memory_read(pdp_addr + l1 * 8, &pdpe, 8); - pdpe = le64_to_cpu(pdpe); - end = l1 << 30; - if (pdpe & PG_PRESENT_MASK) { - pd_addr = pdpe & 0x3fffffffff000ULL; - for (l2 = 0; l2 < 512; l2++) { - cpu_physical_memory_read(pd_addr + l2 * 8, &pde, 8); - pde = le64_to_cpu(pde); - end = (l1 << 30) + (l2 << 21); - if (pde & PG_PRESENT_MASK) { - if (pde & PG_PSE_MASK) { - prot = pde & (PG_USER_MASK | PG_RW_MASK | - PG_PRESENT_MASK); - mem_print(mon, &start, &last_prot, end, prot); - } else { - pt_addr = pde & 0x3fffffffff000ULL; - for (l3 = 0; l3 < 512; l3++) { - cpu_physical_memory_read(pt_addr + l3 * 8, &pte, 8); - pte = le64_to_cpu(pte); - end = (l1 << 30) + (l2 << 21) + (l3 << 12); - if (pte & PG_PRESENT_MASK) { - prot = pte & pde & (PG_USER_MASK | PG_RW_MASK | - PG_PRESENT_MASK); - } else { - prot = 0; - } - mem_print(mon, &start, &last_prot, end, prot); - } - } - } else { - prot = 0; - mem_print(mon, &start, &last_prot, end, prot); - } - } - } else { - prot = 0; - mem_print(mon, &start, &last_prot, end, prot); - } - } - /* Flush last range */ - mem_print(mon, &start, &last_prot, (hwaddr)1 << 32, 0); -} - - -#ifdef TARGET_X86_64 -static void mem_info_64(Monitor *mon, CPUArchState *env) -{ - int prot, last_prot; - uint64_t l1, l2, l3, l4; - uint64_t pml4e, pdpe, pde, pte; - uint64_t pml4_addr, pdp_addr, pd_addr, pt_addr, start, end; - - pml4_addr = env->cr[3] & 0x3fffffffff000ULL; - last_prot = 0; - start = -1; - for (l1 = 0; l1 < 512; l1++) { - cpu_physical_memory_read(pml4_addr + l1 * 8, &pml4e, 8); - pml4e = le64_to_cpu(pml4e); - end = l1 << 39; - if (pml4e & PG_PRESENT_MASK) { - pdp_addr = pml4e & 0x3fffffffff000ULL; - for (l2 = 0; l2 < 512; l2++) { - cpu_physical_memory_read(pdp_addr + l2 * 8, &pdpe, 8); - pdpe = le64_to_cpu(pdpe); - end = (l1 << 39) + (l2 << 30); - if (pdpe & PG_PRESENT_MASK) { - if (pdpe & PG_PSE_MASK) { - prot = pdpe & (PG_USER_MASK | PG_RW_MASK | - PG_PRESENT_MASK); - prot &= pml4e; - mem_print(mon, &start, &last_prot, end, prot); - } else { - pd_addr = pdpe & 0x3fffffffff000ULL; - for (l3 = 0; l3 < 512; l3++) { - cpu_physical_memory_read(pd_addr + l3 * 8, &pde, 8); - pde = le64_to_cpu(pde); - end = (l1 << 39) + (l2 << 30) + (l3 << 21); - if (pde & PG_PRESENT_MASK) { - if (pde & PG_PSE_MASK) { - prot = pde & (PG_USER_MASK | PG_RW_MASK | - PG_PRESENT_MASK); - prot &= pml4e & pdpe; - mem_print(mon, &start, &last_prot, end, prot); - } else { - pt_addr = pde & 0x3fffffffff000ULL; - for (l4 = 0; l4 < 512; l4++) { - cpu_physical_memory_read(pt_addr - + l4 * 8, - &pte, 8); - pte = le64_to_cpu(pte); - end = (l1 << 39) + (l2 << 30) + - (l3 << 21) + (l4 << 12); - if (pte & PG_PRESENT_MASK) { - prot = pte & (PG_USER_MASK | PG_RW_MASK | - PG_PRESENT_MASK); - prot &= pml4e & pdpe & pde; - } else { - prot = 0; - } - mem_print(mon, &start, &last_prot, end, prot); - } - } - } else { - prot = 0; - mem_print(mon, &start, &last_prot, end, prot); - } - } - } - } else { - prot = 0; - mem_print(mon, &start, &last_prot, end, prot); - } - } - } else { - prot = 0; - mem_print(mon, &start, &last_prot, end, prot); - } - } - /* Flush last range */ - mem_print(mon, &start, &last_prot, (hwaddr)1 << 48, 0); -} -#endif - -static void hmp_info_mem(Monitor *mon, const QDict *qdict) -{ - CPUArchState *env; - - env = mon_get_cpu_env(); - - if (!(env->cr[0] & CR0_PG_MASK)) { - monitor_printf(mon, "PG disabled\n"); - return; - } - if (env->cr[4] & CR4_PAE_MASK) { -#ifdef TARGET_X86_64 - if (env->hflags & HF_LMA_MASK) { - mem_info_64(mon, env); - } else -#endif - { - mem_info_pae32(mon, env); - } - } else { - mem_info_32(mon, env); - } -} -#endif - -#if defined(TARGET_SH4) - -static void print_tlb(Monitor *mon, int idx, tlb_t *tlb) -{ - monitor_printf(mon, " tlb%i:\t" - "asid=%hhu vpn=%x\tppn=%x\tsz=%hhu size=%u\t" - "v=%hhu shared=%hhu cached=%hhu prot=%hhu " - "dirty=%hhu writethrough=%hhu\n", - idx, - tlb->asid, tlb->vpn, tlb->ppn, tlb->sz, tlb->size, - tlb->v, tlb->sh, tlb->c, tlb->pr, - tlb->d, tlb->wt); -} - -static void hmp_info_tlb(Monitor *mon, const QDict *qdict) -{ - CPUArchState *env = mon_get_cpu_env(); - int i; - - monitor_printf (mon, "ITLB:\n"); - for (i = 0 ; i < ITLB_SIZE ; i++) - print_tlb (mon, i, &env->itlb[i]); - monitor_printf (mon, "UTLB:\n"); - for (i = 0 ; i < UTLB_SIZE ; i++) - print_tlb (mon, i, &env->utlb[i]); -} - -#endif - -#if defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_XTENSA) -static void hmp_info_tlb(Monitor *mon, const QDict *qdict) -{ - CPUArchState *env1 = mon_get_cpu_env(); - - dump_mmu((FILE*)mon, (fprintf_function)monitor_printf, env1); -} -#endif - static void hmp_info_mtree(Monitor *mon, const QDict *qdict) { mtree_info((fprintf_function)monitor_printf, mon); @@ -1892,9 +1524,9 @@ int64_t dev_time; static void hmp_info_profile(Monitor *mon, const QDict *qdict) { monitor_printf(mon, "async time %" PRId64 " (%0.3f)\n", - dev_time, dev_time / (double)get_ticks_per_sec()); + dev_time, dev_time / (double)NANOSECONDS_PER_SECOND); monitor_printf(mon, "qemu time %" PRId64 " (%0.3f)\n", - tcg_time, tcg_time / (double)get_ticks_per_sec()); + tcg_time, tcg_time / (double)NANOSECONDS_PER_SECOND); tcg_time = 0; dev_time = 0; } @@ -2066,31 +1698,6 @@ static void hmp_acl_remove(Monitor *mon, const QDict *qdict) } } -#if defined(TARGET_I386) -static void hmp_mce(Monitor *mon, const QDict *qdict) -{ - X86CPU *cpu; - CPUState *cs; - int cpu_index = qdict_get_int(qdict, "cpu_index"); - int bank = qdict_get_int(qdict, "bank"); - uint64_t status = qdict_get_int(qdict, "status"); - uint64_t mcg_status = qdict_get_int(qdict, "mcg_status"); - uint64_t addr = qdict_get_int(qdict, "addr"); - uint64_t misc = qdict_get_int(qdict, "misc"); - int flags = MCE_INJECT_UNCOND_AO; - - if (qdict_get_try_bool(qdict, "broadcast", false)) { - flags |= MCE_INJECT_BROADCAST; - } - cs = qemu_get_cpu(cpu_index); - if (cs != NULL) { - cpu = X86_CPU(cs); - cpu_x86_inject_mce(mon, cpu, bank, status, mcg_status, addr, misc, - flags); - } -} -#endif - void qmp_getfd(const char *fdname, Error **errp) { mon_fd_t *monfd; @@ -2509,377 +2116,8 @@ int monitor_fd_param(Monitor *mon, const char *fdname, Error **errp) /* Please update hmp-commands.hx when adding or changing commands */ static mon_cmd_t info_cmds[] = { - { - .name = "version", - .args_type = "", - .params = "", - .help = "show the version of QEMU", - .mhandler.cmd = hmp_info_version, - }, - { - .name = "network", - .args_type = "", - .params = "", - .help = "show the network state", - .mhandler.cmd = hmp_info_network, - }, - { - .name = "chardev", - .args_type = "", - .params = "", - .help = "show the character devices", - .mhandler.cmd = hmp_info_chardev, - }, - { - .name = "block", - .args_type = "nodes:-n,verbose:-v,device:B?", - .params = "[-n] [-v] [device]", - .help = "show info of one block device or all block devices " - "(-n: show named nodes; -v: show details)", - .mhandler.cmd = hmp_info_block, - }, - { - .name = "blockstats", - .args_type = "", - .params = "", - .help = "show block device statistics", - .mhandler.cmd = hmp_info_blockstats, - }, - { - .name = "block-jobs", - .args_type = "", - .params = "", - .help = "show progress of ongoing block device operations", - .mhandler.cmd = hmp_info_block_jobs, - }, - { - .name = "registers", - .args_type = "", - .params = "", - .help = "show the cpu registers", - .mhandler.cmd = hmp_info_registers, - }, - { - .name = "cpus", - .args_type = "", - .params = "", - .help = "show infos for each CPU", - .mhandler.cmd = hmp_info_cpus, - }, - { - .name = "history", - .args_type = "", - .params = "", - .help = "show the command line history", - .mhandler.cmd = hmp_info_history, - }, -#if defined(TARGET_I386) || defined(TARGET_PPC) || defined(TARGET_MIPS) || \ - defined(TARGET_LM32) || (defined(TARGET_SPARC) && !defined(TARGET_SPARC64)) - { - .name = "irq", - .args_type = "", - .params = "", - .help = "show the interrupts statistics (if available)", -#ifdef TARGET_SPARC - .mhandler.cmd = sun4m_hmp_info_irq, -#elif defined(TARGET_LM32) - .mhandler.cmd = lm32_hmp_info_irq, -#else - .mhandler.cmd = hmp_info_irq, -#endif - }, - { - .name = "pic", - .args_type = "", - .params = "", - .help = "show i8259 (PIC) state", -#ifdef TARGET_SPARC - .mhandler.cmd = sun4m_hmp_info_pic, -#elif defined(TARGET_LM32) - .mhandler.cmd = lm32_hmp_info_pic, -#else - .mhandler.cmd = hmp_info_pic, -#endif - }, -#endif - { - .name = "pci", - .args_type = "", - .params = "", - .help = "show PCI info", - .mhandler.cmd = hmp_info_pci, - }, -#if defined(TARGET_I386) || defined(TARGET_SH4) || defined(TARGET_SPARC) || \ - defined(TARGET_PPC) || defined(TARGET_XTENSA) - { - .name = "tlb", - .args_type = "", - .params = "", - .help = "show virtual to physical memory mappings", - .mhandler.cmd = hmp_info_tlb, - }, -#endif -#if defined(TARGET_I386) - { - .name = "mem", - .args_type = "", - .params = "", - .help = "show the active virtual memory mappings", - .mhandler.cmd = hmp_info_mem, - }, -#endif - { - .name = "mtree", - .args_type = "", - .params = "", - .help = "show memory tree", - .mhandler.cmd = hmp_info_mtree, - }, - { - .name = "jit", - .args_type = "", - .params = "", - .help = "show dynamic compiler info", - .mhandler.cmd = hmp_info_jit, - }, - { - .name = "opcount", - .args_type = "", - .params = "", - .help = "show dynamic compiler opcode counters", - .mhandler.cmd = hmp_info_opcount, - }, - { - .name = "kvm", - .args_type = "", - .params = "", - .help = "show KVM information", - .mhandler.cmd = hmp_info_kvm, - }, - { - .name = "numa", - .args_type = "", - .params = "", - .help = "show NUMA information", - .mhandler.cmd = hmp_info_numa, - }, - { - .name = "usb", - .args_type = "", - .params = "", - .help = "show guest USB devices", - .mhandler.cmd = hmp_info_usb, - }, - { - .name = "usbhost", - .args_type = "", - .params = "", - .help = "show host USB devices", - .mhandler.cmd = hmp_info_usbhost, - }, - { - .name = "profile", - .args_type = "", - .params = "", - .help = "show profiling information", - .mhandler.cmd = hmp_info_profile, - }, - { - .name = "capture", - .args_type = "", - .params = "", - .help = "show capture information", - .mhandler.cmd = hmp_info_capture, - }, - { - .name = "snapshots", - .args_type = "", - .params = "", - .help = "show the currently saved VM snapshots", - .mhandler.cmd = hmp_info_snapshots, - }, - { - .name = "status", - .args_type = "", - .params = "", - .help = "show the current VM status (running|paused)", - .mhandler.cmd = hmp_info_status, - }, - { - .name = "mice", - .args_type = "", - .params = "", - .help = "show which guest mouse is receiving events", - .mhandler.cmd = hmp_info_mice, - }, - { - .name = "vnc", - .args_type = "", - .params = "", - .help = "show the vnc server status", - .mhandler.cmd = hmp_info_vnc, - }, -#if defined(CONFIG_SPICE) - { - .name = "spice", - .args_type = "", - .params = "", - .help = "show the spice server status", - .mhandler.cmd = hmp_info_spice, - }, -#endif - { - .name = "name", - .args_type = "", - .params = "", - .help = "show the current VM name", - .mhandler.cmd = hmp_info_name, - }, - { - .name = "uuid", - .args_type = "", - .params = "", - .help = "show the current VM UUID", - .mhandler.cmd = hmp_info_uuid, - }, - { - .name = "cpustats", - .args_type = "", - .params = "", - .help = "show CPU statistics", - .mhandler.cmd = hmp_info_cpustats, - }, -#if defined(CONFIG_SLIRP) - { - .name = "usernet", - .args_type = "", - .params = "", - .help = "show user network stack connection states", - .mhandler.cmd = hmp_info_usernet, - }, -#endif - { - .name = "migrate", - .args_type = "", - .params = "", - .help = "show migration status", - .mhandler.cmd = hmp_info_migrate, - }, - { - .name = "migrate_capabilities", - .args_type = "", - .params = "", - .help = "show current migration capabilities", - .mhandler.cmd = hmp_info_migrate_capabilities, - }, - { - .name = "migrate_parameters", - .args_type = "", - .params = "", - .help = "show current migration parameters", - .mhandler.cmd = hmp_info_migrate_parameters, - }, - { - .name = "migrate_cache_size", - .args_type = "", - .params = "", - .help = "show current migration xbzrle cache size", - .mhandler.cmd = hmp_info_migrate_cache_size, - }, - { - .name = "balloon", - .args_type = "", - .params = "", - .help = "show balloon information", - .mhandler.cmd = hmp_info_balloon, - }, - { - .name = "qtree", - .args_type = "", - .params = "", - .help = "show device tree", - .mhandler.cmd = hmp_info_qtree, - }, - { - .name = "qdm", - .args_type = "", - .params = "", - .help = "show qdev device model list", - .mhandler.cmd = hmp_info_qdm, - }, - { - .name = "qom-tree", - .args_type = "path:s?", - .params = "[path]", - .help = "show QOM composition tree", - .mhandler.cmd = hmp_info_qom_tree, - }, - { - .name = "roms", - .args_type = "", - .params = "", - .help = "show roms", - .mhandler.cmd = hmp_info_roms, - }, - { - .name = "trace-events", - .args_type = "", - .params = "", - .help = "show available trace-events & their state", - .mhandler.cmd = hmp_info_trace_events, - }, - { - .name = "tpm", - .args_type = "", - .params = "", - .help = "show the TPM device", - .mhandler.cmd = hmp_info_tpm, - }, - { - .name = "memdev", - .args_type = "", - .params = "", - .help = "show memory backends", - .mhandler.cmd = hmp_info_memdev, - }, - { - .name = "memory-devices", - .args_type = "", - .params = "", - .help = "show memory devices", - .mhandler.cmd = hmp_info_memory_devices, - }, - { - .name = "rocker", - .args_type = "name:s", - .params = "name", - .help = "Show rocker switch", - .mhandler.cmd = hmp_rocker, - }, - { - .name = "rocker-ports", - .args_type = "name:s", - .params = "name", - .help = "Show rocker ports", - .mhandler.cmd = hmp_rocker_ports, - }, - { - .name = "rocker-of-dpa-flows", - .args_type = "name:s,tbl_id:i?", - .params = "name [tbl_id]", - .help = "Show rocker OF-DPA flow tables", - .mhandler.cmd = hmp_rocker_of_dpa_flows, - }, - { - .name = "rocker-of-dpa-groups", - .args_type = "name:s,type:i?", - .params = "name [type]", - .help = "Show rocker OF-DPA groups", - .mhandler.cmd = hmp_rocker_of_dpa_groups, - }, - { - .name = NULL, - }, +#include "hmp-commands-info.h" + { NULL, NULL, }, }; /* mon_cmds and info_cmds would be sorted at runtime */ @@ -2898,394 +2136,6 @@ static const mon_cmd_t qmp_cmds[] = { static const char *pch; static sigjmp_buf expr_env; -#define MD_TLONG 0 -#define MD_I32 1 - -typedef struct MonitorDef { - const char *name; - int offset; - target_long (*get_value)(const struct MonitorDef *md, int val); - int type; -} MonitorDef; - -#if defined(TARGET_I386) -static target_long monitor_get_pc (const struct MonitorDef *md, int val) -{ - CPUArchState *env = mon_get_cpu_env(); - return env->eip + env->segs[R_CS].base; -} -#endif - -#if defined(TARGET_PPC) -static target_long monitor_get_ccr (const struct MonitorDef *md, int val) -{ - CPUArchState *env = mon_get_cpu_env(); - unsigned int u; - int i; - - u = 0; - for (i = 0; i < 8; i++) - u |= env->crf[i] << (32 - (4 * (i + 1))); - - return u; -} - -static target_long monitor_get_msr (const struct MonitorDef *md, int val) -{ - CPUArchState *env = mon_get_cpu_env(); - return env->msr; -} - -static target_long monitor_get_xer (const struct MonitorDef *md, int val) -{ - CPUArchState *env = mon_get_cpu_env(); - return env->xer; -} - -static target_long monitor_get_decr (const struct MonitorDef *md, int val) -{ - CPUArchState *env = mon_get_cpu_env(); - return cpu_ppc_load_decr(env); -} - -static target_long monitor_get_tbu (const struct MonitorDef *md, int val) -{ - CPUArchState *env = mon_get_cpu_env(); - return cpu_ppc_load_tbu(env); -} - -static target_long monitor_get_tbl (const struct MonitorDef *md, int val) -{ - CPUArchState *env = mon_get_cpu_env(); - return cpu_ppc_load_tbl(env); -} -#endif - -#if defined(TARGET_SPARC) -#ifndef TARGET_SPARC64 -static target_long monitor_get_psr (const struct MonitorDef *md, int val) -{ - CPUArchState *env = mon_get_cpu_env(); - - return cpu_get_psr(env); -} -#endif - -static target_long monitor_get_reg(const struct MonitorDef *md, int val) -{ - CPUArchState *env = mon_get_cpu_env(); - return env->regwptr[val]; -} -#endif - -static const MonitorDef monitor_defs[] = { -#ifdef TARGET_I386 - -#define SEG(name, seg) \ - { name, offsetof(CPUX86State, segs[seg].selector), NULL, MD_I32 },\ - { name ".base", offsetof(CPUX86State, segs[seg].base) },\ - { name ".limit", offsetof(CPUX86State, segs[seg].limit), NULL, MD_I32 }, - - { "eax", offsetof(CPUX86State, regs[0]) }, - { "ecx", offsetof(CPUX86State, regs[1]) }, - { "edx", offsetof(CPUX86State, regs[2]) }, - { "ebx", offsetof(CPUX86State, regs[3]) }, - { "esp|sp", offsetof(CPUX86State, regs[4]) }, - { "ebp|fp", offsetof(CPUX86State, regs[5]) }, - { "esi", offsetof(CPUX86State, regs[6]) }, - { "edi", offsetof(CPUX86State, regs[7]) }, -#ifdef TARGET_X86_64 - { "r8", offsetof(CPUX86State, regs[8]) }, - { "r9", offsetof(CPUX86State, regs[9]) }, - { "r10", offsetof(CPUX86State, regs[10]) }, - { "r11", offsetof(CPUX86State, regs[11]) }, - { "r12", offsetof(CPUX86State, regs[12]) }, - { "r13", offsetof(CPUX86State, regs[13]) }, - { "r14", offsetof(CPUX86State, regs[14]) }, - { "r15", offsetof(CPUX86State, regs[15]) }, -#endif - { "eflags", offsetof(CPUX86State, eflags) }, - { "eip", offsetof(CPUX86State, eip) }, - SEG("cs", R_CS) - SEG("ds", R_DS) - SEG("es", R_ES) - SEG("ss", R_SS) - SEG("fs", R_FS) - SEG("gs", R_GS) - { "pc", 0, monitor_get_pc, }, -#elif defined(TARGET_PPC) - /* General purpose registers */ - { "r0", offsetof(CPUPPCState, gpr[0]) }, - { "r1", offsetof(CPUPPCState, gpr[1]) }, - { "r2", offsetof(CPUPPCState, gpr[2]) }, - { "r3", offsetof(CPUPPCState, gpr[3]) }, - { "r4", offsetof(CPUPPCState, gpr[4]) }, - { "r5", offsetof(CPUPPCState, gpr[5]) }, - { "r6", offsetof(CPUPPCState, gpr[6]) }, - { "r7", offsetof(CPUPPCState, gpr[7]) }, - { "r8", offsetof(CPUPPCState, gpr[8]) }, - { "r9", offsetof(CPUPPCState, gpr[9]) }, - { "r10", offsetof(CPUPPCState, gpr[10]) }, - { "r11", offsetof(CPUPPCState, gpr[11]) }, - { "r12", offsetof(CPUPPCState, gpr[12]) }, - { "r13", offsetof(CPUPPCState, gpr[13]) }, - { "r14", offsetof(CPUPPCState, gpr[14]) }, - { "r15", offsetof(CPUPPCState, gpr[15]) }, - { "r16", offsetof(CPUPPCState, gpr[16]) }, - { "r17", offsetof(CPUPPCState, gpr[17]) }, - { "r18", offsetof(CPUPPCState, gpr[18]) }, - { "r19", offsetof(CPUPPCState, gpr[19]) }, - { "r20", offsetof(CPUPPCState, gpr[20]) }, - { "r21", offsetof(CPUPPCState, gpr[21]) }, - { "r22", offsetof(CPUPPCState, gpr[22]) }, - { "r23", offsetof(CPUPPCState, gpr[23]) }, - { "r24", offsetof(CPUPPCState, gpr[24]) }, - { "r25", offsetof(CPUPPCState, gpr[25]) }, - { "r26", offsetof(CPUPPCState, gpr[26]) }, - { "r27", offsetof(CPUPPCState, gpr[27]) }, - { "r28", offsetof(CPUPPCState, gpr[28]) }, - { "r29", offsetof(CPUPPCState, gpr[29]) }, - { "r30", offsetof(CPUPPCState, gpr[30]) }, - { "r31", offsetof(CPUPPCState, gpr[31]) }, - /* Floating point registers */ - { "f0", offsetof(CPUPPCState, fpr[0]) }, - { "f1", offsetof(CPUPPCState, fpr[1]) }, - { "f2", offsetof(CPUPPCState, fpr[2]) }, - { "f3", offsetof(CPUPPCState, fpr[3]) }, - { "f4", offsetof(CPUPPCState, fpr[4]) }, - { "f5", offsetof(CPUPPCState, fpr[5]) }, - { "f6", offsetof(CPUPPCState, fpr[6]) }, - { "f7", offsetof(CPUPPCState, fpr[7]) }, - { "f8", offsetof(CPUPPCState, fpr[8]) }, - { "f9", offsetof(CPUPPCState, fpr[9]) }, - { "f10", offsetof(CPUPPCState, fpr[10]) }, - { "f11", offsetof(CPUPPCState, fpr[11]) }, - { "f12", offsetof(CPUPPCState, fpr[12]) }, - { "f13", offsetof(CPUPPCState, fpr[13]) }, - { "f14", offsetof(CPUPPCState, fpr[14]) }, - { "f15", offsetof(CPUPPCState, fpr[15]) }, - { "f16", offsetof(CPUPPCState, fpr[16]) }, - { "f17", offsetof(CPUPPCState, fpr[17]) }, - { "f18", offsetof(CPUPPCState, fpr[18]) }, - { "f19", offsetof(CPUPPCState, fpr[19]) }, - { "f20", offsetof(CPUPPCState, fpr[20]) }, - { "f21", offsetof(CPUPPCState, fpr[21]) }, - { "f22", offsetof(CPUPPCState, fpr[22]) }, - { "f23", offsetof(CPUPPCState, fpr[23]) }, - { "f24", offsetof(CPUPPCState, fpr[24]) }, - { "f25", offsetof(CPUPPCState, fpr[25]) }, - { "f26", offsetof(CPUPPCState, fpr[26]) }, - { "f27", offsetof(CPUPPCState, fpr[27]) }, - { "f28", offsetof(CPUPPCState, fpr[28]) }, - { "f29", offsetof(CPUPPCState, fpr[29]) }, - { "f30", offsetof(CPUPPCState, fpr[30]) }, - { "f31", offsetof(CPUPPCState, fpr[31]) }, - { "fpscr", offsetof(CPUPPCState, fpscr) }, - /* Next instruction pointer */ - { "nip|pc", offsetof(CPUPPCState, nip) }, - { "lr", offsetof(CPUPPCState, lr) }, - { "ctr", offsetof(CPUPPCState, ctr) }, - { "decr", 0, &monitor_get_decr, }, - { "ccr", 0, &monitor_get_ccr, }, - /* Machine state register */ - { "msr", 0, &monitor_get_msr, }, - { "xer", 0, &monitor_get_xer, }, - { "tbu", 0, &monitor_get_tbu, }, - { "tbl", 0, &monitor_get_tbl, }, - /* Segment registers */ - { "sdr1", offsetof(CPUPPCState, spr[SPR_SDR1]) }, - { "sr0", offsetof(CPUPPCState, sr[0]) }, - { "sr1", offsetof(CPUPPCState, sr[1]) }, - { "sr2", offsetof(CPUPPCState, sr[2]) }, - { "sr3", offsetof(CPUPPCState, sr[3]) }, - { "sr4", offsetof(CPUPPCState, sr[4]) }, - { "sr5", offsetof(CPUPPCState, sr[5]) }, - { "sr6", offsetof(CPUPPCState, sr[6]) }, - { "sr7", offsetof(CPUPPCState, sr[7]) }, - { "sr8", offsetof(CPUPPCState, sr[8]) }, - { "sr9", offsetof(CPUPPCState, sr[9]) }, - { "sr10", offsetof(CPUPPCState, sr[10]) }, - { "sr11", offsetof(CPUPPCState, sr[11]) }, - { "sr12", offsetof(CPUPPCState, sr[12]) }, - { "sr13", offsetof(CPUPPCState, sr[13]) }, - { "sr14", offsetof(CPUPPCState, sr[14]) }, - { "sr15", offsetof(CPUPPCState, sr[15]) }, - /* Too lazy to put BATs... */ - { "pvr", offsetof(CPUPPCState, spr[SPR_PVR]) }, - - { "srr0", offsetof(CPUPPCState, spr[SPR_SRR0]) }, - { "srr1", offsetof(CPUPPCState, spr[SPR_SRR1]) }, - { "dar", offsetof(CPUPPCState, spr[SPR_DAR]) }, - { "dsisr", offsetof(CPUPPCState, spr[SPR_DSISR]) }, - { "cfar", offsetof(CPUPPCState, spr[SPR_CFAR]) }, - { "sprg0", offsetof(CPUPPCState, spr[SPR_SPRG0]) }, - { "sprg1", offsetof(CPUPPCState, spr[SPR_SPRG1]) }, - { "sprg2", offsetof(CPUPPCState, spr[SPR_SPRG2]) }, - { "sprg3", offsetof(CPUPPCState, spr[SPR_SPRG3]) }, - { "sprg4", offsetof(CPUPPCState, spr[SPR_SPRG4]) }, - { "sprg5", offsetof(CPUPPCState, spr[SPR_SPRG5]) }, - { "sprg6", offsetof(CPUPPCState, spr[SPR_SPRG6]) }, - { "sprg7", offsetof(CPUPPCState, spr[SPR_SPRG7]) }, - { "pid", offsetof(CPUPPCState, spr[SPR_BOOKE_PID]) }, - { "csrr0", offsetof(CPUPPCState, spr[SPR_BOOKE_CSRR0]) }, - { "csrr1", offsetof(CPUPPCState, spr[SPR_BOOKE_CSRR1]) }, - { "esr", offsetof(CPUPPCState, spr[SPR_BOOKE_ESR]) }, - { "dear", offsetof(CPUPPCState, spr[SPR_BOOKE_DEAR]) }, - { "mcsr", offsetof(CPUPPCState, spr[SPR_BOOKE_MCSR]) }, - { "tsr", offsetof(CPUPPCState, spr[SPR_BOOKE_TSR]) }, - { "tcr", offsetof(CPUPPCState, spr[SPR_BOOKE_TCR]) }, - { "vrsave", offsetof(CPUPPCState, spr[SPR_VRSAVE]) }, - { "pir", offsetof(CPUPPCState, spr[SPR_BOOKE_PIR]) }, - { "mcsrr0", offsetof(CPUPPCState, spr[SPR_BOOKE_MCSRR0]) }, - { "mcsrr1", offsetof(CPUPPCState, spr[SPR_BOOKE_MCSRR1]) }, - { "decar", offsetof(CPUPPCState, spr[SPR_BOOKE_DECAR]) }, - { "ivpr", offsetof(CPUPPCState, spr[SPR_BOOKE_IVPR]) }, - { "epcr", offsetof(CPUPPCState, spr[SPR_BOOKE_EPCR]) }, - { "sprg8", offsetof(CPUPPCState, spr[SPR_BOOKE_SPRG8]) }, - { "ivor0", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR0]) }, - { "ivor1", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR1]) }, - { "ivor2", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR2]) }, - { "ivor3", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR3]) }, - { "ivor4", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR4]) }, - { "ivor5", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR5]) }, - { "ivor6", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR6]) }, - { "ivor7", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR7]) }, - { "ivor8", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR8]) }, - { "ivor9", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR9]) }, - { "ivor10", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR10]) }, - { "ivor11", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR11]) }, - { "ivor12", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR12]) }, - { "ivor13", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR13]) }, - { "ivor14", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR14]) }, - { "ivor15", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR15]) }, - { "ivor32", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR32]) }, - { "ivor33", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR33]) }, - { "ivor34", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR34]) }, - { "ivor35", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR35]) }, - { "ivor36", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR36]) }, - { "ivor37", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR37]) }, - { "mas0", offsetof(CPUPPCState, spr[SPR_BOOKE_MAS0]) }, - { "mas1", offsetof(CPUPPCState, spr[SPR_BOOKE_MAS1]) }, - { "mas2", offsetof(CPUPPCState, spr[SPR_BOOKE_MAS2]) }, - { "mas3", offsetof(CPUPPCState, spr[SPR_BOOKE_MAS3]) }, - { "mas4", offsetof(CPUPPCState, spr[SPR_BOOKE_MAS4]) }, - { "mas6", offsetof(CPUPPCState, spr[SPR_BOOKE_MAS6]) }, - { "mas7", offsetof(CPUPPCState, spr[SPR_BOOKE_MAS7]) }, - { "mmucfg", offsetof(CPUPPCState, spr[SPR_MMUCFG]) }, - { "tlb0cfg", offsetof(CPUPPCState, spr[SPR_BOOKE_TLB0CFG]) }, - { "tlb1cfg", offsetof(CPUPPCState, spr[SPR_BOOKE_TLB1CFG]) }, - { "epr", offsetof(CPUPPCState, spr[SPR_BOOKE_EPR]) }, - { "eplc", offsetof(CPUPPCState, spr[SPR_BOOKE_EPLC]) }, - { "epsc", offsetof(CPUPPCState, spr[SPR_BOOKE_EPSC]) }, - { "svr", offsetof(CPUPPCState, spr[SPR_E500_SVR]) }, - { "mcar", offsetof(CPUPPCState, spr[SPR_Exxx_MCAR]) }, - { "pid1", offsetof(CPUPPCState, spr[SPR_BOOKE_PID1]) }, - { "pid2", offsetof(CPUPPCState, spr[SPR_BOOKE_PID2]) }, - { "hid0", offsetof(CPUPPCState, spr[SPR_HID0]) }, - -#elif defined(TARGET_SPARC) - { "g0", offsetof(CPUSPARCState, gregs[0]) }, - { "g1", offsetof(CPUSPARCState, gregs[1]) }, - { "g2", offsetof(CPUSPARCState, gregs[2]) }, - { "g3", offsetof(CPUSPARCState, gregs[3]) }, - { "g4", offsetof(CPUSPARCState, gregs[4]) }, - { "g5", offsetof(CPUSPARCState, gregs[5]) }, - { "g6", offsetof(CPUSPARCState, gregs[6]) }, - { "g7", offsetof(CPUSPARCState, gregs[7]) }, - { "o0", 0, monitor_get_reg }, - { "o1", 1, monitor_get_reg }, - { "o2", 2, monitor_get_reg }, - { "o3", 3, monitor_get_reg }, - { "o4", 4, monitor_get_reg }, - { "o5", 5, monitor_get_reg }, - { "o6", 6, monitor_get_reg }, - { "o7", 7, monitor_get_reg }, - { "l0", 8, monitor_get_reg }, - { "l1", 9, monitor_get_reg }, - { "l2", 10, monitor_get_reg }, - { "l3", 11, monitor_get_reg }, - { "l4", 12, monitor_get_reg }, - { "l5", 13, monitor_get_reg }, - { "l6", 14, monitor_get_reg }, - { "l7", 15, monitor_get_reg }, - { "i0", 16, monitor_get_reg }, - { "i1", 17, monitor_get_reg }, - { "i2", 18, monitor_get_reg }, - { "i3", 19, monitor_get_reg }, - { "i4", 20, monitor_get_reg }, - { "i5", 21, monitor_get_reg }, - { "i6", 22, monitor_get_reg }, - { "i7", 23, monitor_get_reg }, - { "pc", offsetof(CPUSPARCState, pc) }, - { "npc", offsetof(CPUSPARCState, npc) }, - { "y", offsetof(CPUSPARCState, y) }, -#ifndef TARGET_SPARC64 - { "psr", 0, &monitor_get_psr, }, - { "wim", offsetof(CPUSPARCState, wim) }, -#endif - { "tbr", offsetof(CPUSPARCState, tbr) }, - { "fsr", offsetof(CPUSPARCState, fsr) }, - { "f0", offsetof(CPUSPARCState, fpr[0].l.upper) }, - { "f1", offsetof(CPUSPARCState, fpr[0].l.lower) }, - { "f2", offsetof(CPUSPARCState, fpr[1].l.upper) }, - { "f3", offsetof(CPUSPARCState, fpr[1].l.lower) }, - { "f4", offsetof(CPUSPARCState, fpr[2].l.upper) }, - { "f5", offsetof(CPUSPARCState, fpr[2].l.lower) }, - { "f6", offsetof(CPUSPARCState, fpr[3].l.upper) }, - { "f7", offsetof(CPUSPARCState, fpr[3].l.lower) }, - { "f8", offsetof(CPUSPARCState, fpr[4].l.upper) }, - { "f9", offsetof(CPUSPARCState, fpr[4].l.lower) }, - { "f10", offsetof(CPUSPARCState, fpr[5].l.upper) }, - { "f11", offsetof(CPUSPARCState, fpr[5].l.lower) }, - { "f12", offsetof(CPUSPARCState, fpr[6].l.upper) }, - { "f13", offsetof(CPUSPARCState, fpr[6].l.lower) }, - { "f14", offsetof(CPUSPARCState, fpr[7].l.upper) }, - { "f15", offsetof(CPUSPARCState, fpr[7].l.lower) }, - { "f16", offsetof(CPUSPARCState, fpr[8].l.upper) }, - { "f17", offsetof(CPUSPARCState, fpr[8].l.lower) }, - { "f18", offsetof(CPUSPARCState, fpr[9].l.upper) }, - { "f19", offsetof(CPUSPARCState, fpr[9].l.lower) }, - { "f20", offsetof(CPUSPARCState, fpr[10].l.upper) }, - { "f21", offsetof(CPUSPARCState, fpr[10].l.lower) }, - { "f22", offsetof(CPUSPARCState, fpr[11].l.upper) }, - { "f23", offsetof(CPUSPARCState, fpr[11].l.lower) }, - { "f24", offsetof(CPUSPARCState, fpr[12].l.upper) }, - { "f25", offsetof(CPUSPARCState, fpr[12].l.lower) }, - { "f26", offsetof(CPUSPARCState, fpr[13].l.upper) }, - { "f27", offsetof(CPUSPARCState, fpr[13].l.lower) }, - { "f28", offsetof(CPUSPARCState, fpr[14].l.upper) }, - { "f29", offsetof(CPUSPARCState, fpr[14].l.lower) }, - { "f30", offsetof(CPUSPARCState, fpr[15].l.upper) }, - { "f31", offsetof(CPUSPARCState, fpr[15].l.lower) }, -#ifdef TARGET_SPARC64 - { "f32", offsetof(CPUSPARCState, fpr[16]) }, - { "f34", offsetof(CPUSPARCState, fpr[17]) }, - { "f36", offsetof(CPUSPARCState, fpr[18]) }, - { "f38", offsetof(CPUSPARCState, fpr[19]) }, - { "f40", offsetof(CPUSPARCState, fpr[20]) }, - { "f42", offsetof(CPUSPARCState, fpr[21]) }, - { "f44", offsetof(CPUSPARCState, fpr[22]) }, - { "f46", offsetof(CPUSPARCState, fpr[23]) }, - { "f48", offsetof(CPUSPARCState, fpr[24]) }, - { "f50", offsetof(CPUSPARCState, fpr[25]) }, - { "f52", offsetof(CPUSPARCState, fpr[26]) }, - { "f54", offsetof(CPUSPARCState, fpr[27]) }, - { "f56", offsetof(CPUSPARCState, fpr[28]) }, - { "f58", offsetof(CPUSPARCState, fpr[29]) }, - { "f60", offsetof(CPUSPARCState, fpr[30]) }, - { "f62", offsetof(CPUSPARCState, fpr[31]) }, - { "asi", offsetof(CPUSPARCState, asi) }, - { "pstate", offsetof(CPUSPARCState, pstate) }, - { "cansave", offsetof(CPUSPARCState, cansave) }, - { "canrestore", offsetof(CPUSPARCState, canrestore) }, - { "otherwin", offsetof(CPUSPARCState, otherwin) }, - { "wstate", offsetof(CPUSPARCState, wstate) }, - { "cleanwin", offsetof(CPUSPARCState, cleanwin) }, - { "fprs", offsetof(CPUSPARCState, fprs) }, -#endif -#endif - { NULL }, -}; static void GCC_FMT_ATTR(2, 3) QEMU_NORETURN expr_error(Monitor *mon, const char *fmt, ...) @@ -3301,10 +2151,16 @@ expr_error(Monitor *mon, const char *fmt, ...) /* return 0 if OK, -1 if not found */ static int get_monitor_def(target_long *pval, const char *name) { - const MonitorDef *md; + const MonitorDef *md = target_monitor_defs(); void *ptr; + uint64_t tmp = 0; + int ret; - for(md = monitor_defs; md->name != NULL; md++) { + if (md == NULL) { + return -1; + } + + for(; md->name != NULL; md++) { if (compare_cmd(name, md->name)) { if (md->get_value) { *pval = md->get_value(md, md->offset); @@ -3326,7 +2182,13 @@ static int get_monitor_def(target_long *pval, const char *name) return 0; } } - return -1; + + ret = target_get_monitor_def(mon_get_cpu(), name, &tmp); + if (!ret) { + *pval = (target_long) tmp; + } + + return ret; } static void next(void) @@ -3891,7 +2753,7 @@ static QDict *monitor_parse_arguments(Monitor *mon, break; } } - val = strtosz(p, &end); + val = qemu_strtosz(p, &end); if (val < 0) { monitor_printf(mon, "invalid size\n"); goto fail; @@ -4372,7 +3234,7 @@ void sendkey_completion(ReadLineState *rs, int nb_args, const char *str) } len = strlen(str); readline_set_completion_index(rs, len); - for (i = 0; i < Q_KEY_CODE_MAX; i++) { + for (i = 0; i < Q_KEY_CODE__MAX; i++) { if (!strncmp(str, QKeyCode_lookup[i], len)) { readline_add_completion(rs, QKeyCode_lookup[i]); } @@ -4429,6 +3291,26 @@ void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str) } } +void trace_event_completion(ReadLineState *rs, int nb_args, const char *str) +{ + size_t len; + + len = strlen(str); + readline_set_completion_index(rs, len); + if (nb_args == 2) { + TraceEventID id; + for (id = 0; id < trace_event_count(); id++) { + const char *event_name = trace_event_get_name(trace_event_id(id)); + if (!strncmp(str, event_name, len)) { + readline_add_completion(rs, event_name); + } + } + } else if (nb_args == 3) { + add_completion_option(rs, str, "on"); + add_completion_option(rs, str, "off"); + } +} + void watchdog_action_completion(ReadLineState *rs, int nb_args, const char *str) { int i; @@ -4451,7 +3333,7 @@ void migrate_set_capability_completion(ReadLineState *rs, int nb_args, readline_set_completion_index(rs, len); if (nb_args == 2) { int i; - for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) { + for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { const char *name = MigrationCapability_lookup[i]; if (!strncmp(str, name, len)) { readline_add_completion(rs, name); @@ -4472,7 +3354,7 @@ void migrate_set_parameter_completion(ReadLineState *rs, int nb_args, readline_set_completion_index(rs, len); if (nb_args == 2) { int i; - for (i = 0; i < MIGRATION_PARAMETER_MAX; i++) { + for (i = 0; i < MIGRATION_PARAMETER__MAX; i++) { const char *name = MigrationParameter_lookup[i]; if (!strncmp(str, name, len)) { readline_add_completion(rs, name); @@ -4551,13 +3433,18 @@ static void vm_completion(ReadLineState *rs, const char *str) readline_set_completion_index(rs, len); while ((bs = bdrv_next(bs))) { SnapshotInfoList *snapshots, *snapshot; + AioContext *ctx = bdrv_get_aio_context(bs); + bool ok = false; - if (!bdrv_can_snapshot(bs)) { - continue; + aio_context_acquire(ctx); + if (bdrv_can_snapshot(bs)) { + ok = bdrv_query_snapshot_info_list(bs, &snapshots, NULL) == 0; } - if (bdrv_query_snapshot_info_list(bs, &snapshots, NULL)) { + aio_context_release(ctx); + if (!ok) { continue; } + snapshot = snapshots; while (snapshot) { char *completion = snapshot->value->name; @@ -4598,7 +3485,7 @@ static void monitor_find_completion_by_table(Monitor *mon, int i; const char *ptype, *str, *name; const mon_cmd_t *cmd; - BlockDriverState *bs; + BlockBackend *blk = NULL; if (nb_args <= 1) { /* command completion */ @@ -4653,8 +3540,8 @@ static void monitor_find_completion_by_table(Monitor *mon, case 'B': /* block device name completion */ readline_set_completion_index(mon->rs, strlen(str)); - for (bs = bdrv_next(NULL); bs; bs = bdrv_next(bs)) { - name = bdrv_get_device_name(bs); + while ((blk = blk_next(blk)) != NULL) { + name = blk_name(blk); if (str[0] == '\0' || !strncmp(name, str, strlen(str))) { readline_add_completion(mon->rs, name); @@ -4979,7 +3866,7 @@ static QDict *qmp_check_input_obj(QObject *input_obj, Error **errp) return input_dict; } -static void handle_qmp_command(JSONMessageParser *parser, QList *tokens) +static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) { Error *local_err = NULL; QObject *obj, *data; @@ -5037,6 +3924,7 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens) err_out: monitor_protocol_emitter(mon, data, local_err); qobject_decref(data); + error_free(local_err); QDECREF(input); QDECREF(args); } @@ -5102,7 +3990,7 @@ static QObject *get_qmp_greeting(void) { QObject *ver = NULL; - qmp_marshal_input_query_version(NULL, &ver, NULL); + qmp_marshal_query_version(NULL, &ver, NULL); return qobject_from_jsonf("{'QMP':{'version': %p,'capabilities': []}}",ver); } @@ -5278,8 +4166,7 @@ static void bdrv_password_cb(void *opaque, const char *password, bdrv_add_key(bs, password, &local_err); if (local_err) { - monitor_printf(mon, "%s\n", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); ret = -EPERM; } if (mon->password_completion_cb) @@ -5320,6 +4207,10 @@ int monitor_read_block_device_key(Monitor *mon, const char *device, monitor_printf(mon, "Device not found %s\n", device); return -1; } + if (!blk_bs(blk)) { + monitor_printf(mon, "Device '%s' has no medium\n", device); + return -1; + } bdrv_add_key(blk_bs(blk), NULL, &err); if (err) { @@ -5361,3 +4252,18 @@ void qmp_rtc_reset_reinjection(Error **errp) error_setg(errp, QERR_FEATURE_DISABLED, "rtc-reset-reinjection"); } #endif + +#ifndef TARGET_S390X +void qmp_dump_skeys(const char *filename, Error **errp) +{ + error_setg(errp, QERR_FEATURE_DISABLED, "dump-skeys"); +} +#endif + +#ifndef TARGET_ARM +GICCapabilityList *qmp_query_gic_capabilities(Error **errp) +{ + error_setg(errp, QERR_FEATURE_DISABLED, "query-gic-capabilities"); + return NULL; +} +#endif diff --git a/qemu/nbd.c b/qemu/nbd.c deleted file mode 100644 index 06b501ba6..000000000 --- a/qemu/nbd.c +++ /dev/null @@ -1,1489 +0,0 @@ -/* - * Copyright (C) 2005 Anthony Liguori - * - * Network Block Device - * - * 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; under version 2 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 . - */ - -#include "block/nbd.h" -#include "sysemu/block-backend.h" - -#include "block/coroutine.h" - -#include -#include -#ifndef _WIN32 -#include -#endif -#if defined(__sun__) || defined(__HAIKU__) -#include -#endif -#include -#include - -#ifdef __linux__ -#include -#endif - -#include "qemu/sockets.h" -#include "qemu/queue.h" -#include "qemu/main-loop.h" - -//#define DEBUG_NBD - -#ifdef DEBUG_NBD -#define TRACE(msg, ...) do { \ - LOG(msg, ## __VA_ARGS__); \ -} while(0) -#else -#define TRACE(msg, ...) \ - do { } while (0) -#endif - -#define LOG(msg, ...) do { \ - fprintf(stderr, "%s:%s():L%d: " msg "\n", \ - __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \ -} while(0) - -/* This is all part of the "official" NBD API. - * - * The most up-to-date documentation is available at: - * https://github.com/yoe/nbd/blob/master/doc/proto.txt - */ - -#define NBD_REQUEST_SIZE (4 + 4 + 8 + 8 + 4) -#define NBD_REPLY_SIZE (4 + 4 + 8) -#define NBD_REQUEST_MAGIC 0x25609513 -#define NBD_REPLY_MAGIC 0x67446698 -#define NBD_OPTS_MAGIC 0x49484156454F5054LL -#define NBD_CLIENT_MAGIC 0x0000420281861253LL -#define NBD_REP_MAGIC 0x3e889045565a9LL - -#define NBD_SET_SOCK _IO(0xab, 0) -#define NBD_SET_BLKSIZE _IO(0xab, 1) -#define NBD_SET_SIZE _IO(0xab, 2) -#define NBD_DO_IT _IO(0xab, 3) -#define NBD_CLEAR_SOCK _IO(0xab, 4) -#define NBD_CLEAR_QUE _IO(0xab, 5) -#define NBD_PRINT_DEBUG _IO(0xab, 6) -#define NBD_SET_SIZE_BLOCKS _IO(0xab, 7) -#define NBD_DISCONNECT _IO(0xab, 8) -#define NBD_SET_TIMEOUT _IO(0xab, 9) -#define NBD_SET_FLAGS _IO(0xab, 10) - -#define NBD_OPT_EXPORT_NAME (1) -#define NBD_OPT_ABORT (2) -#define NBD_OPT_LIST (3) - -/* NBD errors are based on errno numbers, so there is a 1:1 mapping, - * but only a limited set of errno values is specified in the protocol. - * Everything else is squashed to EINVAL. - */ -#define NBD_SUCCESS 0 -#define NBD_EPERM 1 -#define NBD_EIO 5 -#define NBD_ENOMEM 12 -#define NBD_EINVAL 22 -#define NBD_ENOSPC 28 - -static int system_errno_to_nbd_errno(int err) -{ - switch (err) { - case 0: - return NBD_SUCCESS; - case EPERM: - return NBD_EPERM; - case EIO: - return NBD_EIO; - case ENOMEM: - return NBD_ENOMEM; -#ifdef EDQUOT - case EDQUOT: -#endif - case EFBIG: - case ENOSPC: - return NBD_ENOSPC; - case EINVAL: - default: - return NBD_EINVAL; - } -} - -static int nbd_errno_to_system_errno(int err) -{ - switch (err) { - case NBD_SUCCESS: - return 0; - case NBD_EPERM: - return EPERM; - case NBD_EIO: - return EIO; - case NBD_ENOMEM: - return ENOMEM; - case NBD_ENOSPC: - return ENOSPC; - case NBD_EINVAL: - default: - return EINVAL; - } -} - -/* Definitions for opaque data types */ - -typedef struct NBDRequest NBDRequest; - -struct NBDRequest { - QSIMPLEQ_ENTRY(NBDRequest) entry; - NBDClient *client; - uint8_t *data; -}; - -struct NBDExport { - int refcount; - void (*close)(NBDExport *exp); - - BlockBackend *blk; - char *name; - off_t dev_offset; - off_t size; - uint32_t nbdflags; - QTAILQ_HEAD(, NBDClient) clients; - QTAILQ_ENTRY(NBDExport) next; - - AioContext *ctx; -}; - -static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); - -struct NBDClient { - int refcount; - void (*close)(NBDClient *client); - - NBDExport *exp; - int sock; - - Coroutine *recv_coroutine; - - CoMutex send_lock; - Coroutine *send_coroutine; - - bool can_read; - - QTAILQ_ENTRY(NBDClient) next; - int nb_requests; - bool closing; -}; - -/* That's all folks */ - -static void nbd_set_handlers(NBDClient *client); -static void nbd_unset_handlers(NBDClient *client); -static void nbd_update_can_read(NBDClient *client); - -ssize_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read) -{ - size_t offset = 0; - int err; - - if (qemu_in_coroutine()) { - if (do_read) { - return qemu_co_recv(fd, buffer, size); - } else { - return qemu_co_send(fd, buffer, size); - } - } - - while (offset < size) { - ssize_t len; - - if (do_read) { - len = qemu_recv(fd, buffer + offset, size - offset, 0); - } else { - len = send(fd, buffer + offset, size - offset, 0); - } - - if (len < 0) { - err = socket_error(); - - /* recoverable error */ - if (err == EINTR || (offset > 0 && (err == EAGAIN || err == EWOULDBLOCK))) { - continue; - } - - /* unrecoverable error */ - return -err; - } - - /* eof */ - if (len == 0) { - break; - } - - offset += len; - } - - return offset; -} - -static ssize_t read_sync(int fd, void *buffer, size_t size) -{ - /* Sockets are kept in blocking mode in the negotiation phase. After - * that, a non-readable socket simply means that another thread stole - * our request/reply. Synchronization is done with recv_coroutine, so - * that this is coroutine-safe. - */ - return nbd_wr_sync(fd, buffer, size, true); -} - -static ssize_t drop_sync(int fd, size_t size) -{ - ssize_t ret, dropped = size; - uint8_t *buffer = g_malloc(MIN(65536, size)); - - while (size > 0) { - ret = read_sync(fd, buffer, MIN(65536, size)); - if (ret < 0) { - g_free(buffer); - return ret; - } - - assert(ret <= size); - size -= ret; - } - - g_free(buffer); - return dropped; -} - -static ssize_t write_sync(int fd, void *buffer, size_t size) -{ - int ret; - do { - /* For writes, we do expect the socket to be writable. */ - ret = nbd_wr_sync(fd, buffer, size, false); - } while (ret == -EAGAIN); - return ret; -} - -/* Basic flow for negotiation - - Server Client - Negotiate - - or - - Server Client - Negotiate #1 - Option - Negotiate #2 - - ---- - - followed by - - Server Client - Request - Response - Request - Response - ... - ... - Request (type == 2) - -*/ - -static int nbd_send_rep(int csock, uint32_t type, uint32_t opt) -{ - uint64_t magic; - uint32_t len; - - magic = cpu_to_be64(NBD_REP_MAGIC); - if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { - LOG("write failed (rep magic)"); - return -EINVAL; - } - opt = cpu_to_be32(opt); - if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) { - LOG("write failed (rep opt)"); - return -EINVAL; - } - type = cpu_to_be32(type); - if (write_sync(csock, &type, sizeof(type)) != sizeof(type)) { - LOG("write failed (rep type)"); - return -EINVAL; - } - len = cpu_to_be32(0); - if (write_sync(csock, &len, sizeof(len)) != sizeof(len)) { - LOG("write failed (rep data length)"); - return -EINVAL; - } - return 0; -} - -static int nbd_send_rep_list(int csock, NBDExport *exp) -{ - uint64_t magic, name_len; - uint32_t opt, type, len; - - name_len = strlen(exp->name); - magic = cpu_to_be64(NBD_REP_MAGIC); - if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { - LOG("write failed (magic)"); - return -EINVAL; - } - opt = cpu_to_be32(NBD_OPT_LIST); - if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) { - LOG("write failed (opt)"); - return -EINVAL; - } - type = cpu_to_be32(NBD_REP_SERVER); - if (write_sync(csock, &type, sizeof(type)) != sizeof(type)) { - LOG("write failed (reply type)"); - return -EINVAL; - } - len = cpu_to_be32(name_len + sizeof(len)); - if (write_sync(csock, &len, sizeof(len)) != sizeof(len)) { - LOG("write failed (length)"); - return -EINVAL; - } - len = cpu_to_be32(name_len); - if (write_sync(csock, &len, sizeof(len)) != sizeof(len)) { - LOG("write failed (length)"); - return -EINVAL; - } - if (write_sync(csock, exp->name, name_len) != name_len) { - LOG("write failed (buffer)"); - return -EINVAL; - } - return 0; -} - -static int nbd_handle_list(NBDClient *client, uint32_t length) -{ - int csock; - NBDExport *exp; - - csock = client->sock; - if (length) { - if (drop_sync(csock, length) != length) { - return -EIO; - } - return nbd_send_rep(csock, NBD_REP_ERR_INVALID, NBD_OPT_LIST); - } - - /* For each export, send a NBD_REP_SERVER reply. */ - QTAILQ_FOREACH(exp, &exports, next) { - if (nbd_send_rep_list(csock, exp)) { - return -EINVAL; - } - } - /* Finish with a NBD_REP_ACK. */ - return nbd_send_rep(csock, NBD_REP_ACK, NBD_OPT_LIST); -} - -static int nbd_handle_export_name(NBDClient *client, uint32_t length) -{ - int rc = -EINVAL, csock = client->sock; - char name[256]; - - /* Client sends: - [20 .. xx] export name (length bytes) - */ - TRACE("Checking length"); - if (length > 255) { - LOG("Bad length received"); - goto fail; - } - if (read_sync(csock, name, length) != length) { - LOG("read failed"); - goto fail; - } - name[length] = '\0'; - - client->exp = nbd_export_find(name); - if (!client->exp) { - LOG("export not found"); - goto fail; - } - - QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); - nbd_export_get(client->exp); - rc = 0; -fail: - return rc; -} - -static int nbd_receive_options(NBDClient *client) -{ - int csock = client->sock; - uint32_t flags; - - /* Client sends: - [ 0 .. 3] client flags - - [ 0 .. 7] NBD_OPTS_MAGIC - [ 8 .. 11] NBD option - [12 .. 15] Data length - ... Rest of request - - [ 0 .. 7] NBD_OPTS_MAGIC - [ 8 .. 11] Second NBD option - [12 .. 15] Data length - ... Rest of request - */ - - if (read_sync(csock, &flags, sizeof(flags)) != sizeof(flags)) { - LOG("read failed"); - return -EIO; - } - TRACE("Checking client flags"); - be32_to_cpus(&flags); - if (flags != 0 && flags != NBD_FLAG_C_FIXED_NEWSTYLE) { - LOG("Bad client flags received"); - return -EIO; - } - - while (1) { - int ret; - uint32_t tmp, length; - uint64_t magic; - - if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { - LOG("read failed"); - return -EINVAL; - } - TRACE("Checking opts magic"); - if (magic != be64_to_cpu(NBD_OPTS_MAGIC)) { - LOG("Bad magic received"); - return -EINVAL; - } - - if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { - LOG("read failed"); - return -EINVAL; - } - - if (read_sync(csock, &length, sizeof(length)) != sizeof(length)) { - LOG("read failed"); - return -EINVAL; - } - length = be32_to_cpu(length); - - TRACE("Checking option"); - switch (be32_to_cpu(tmp)) { - case NBD_OPT_LIST: - ret = nbd_handle_list(client, length); - if (ret < 0) { - return ret; - } - break; - - case NBD_OPT_ABORT: - return -EINVAL; - - case NBD_OPT_EXPORT_NAME: - return nbd_handle_export_name(client, length); - - default: - tmp = be32_to_cpu(tmp); - LOG("Unsupported option 0x%x", tmp); - nbd_send_rep(client->sock, NBD_REP_ERR_UNSUP, tmp); - return -EINVAL; - } - } -} - -static int nbd_send_negotiate(NBDClient *client) -{ - int csock = client->sock; - char buf[8 + 8 + 8 + 128]; - int rc; - const int myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM | - NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA); - - /* Negotiation header without options: - [ 0 .. 7] passwd ("NBDMAGIC") - [ 8 .. 15] magic (NBD_CLIENT_MAGIC) - [16 .. 23] size - [24 .. 25] server flags (0) - [26 .. 27] export flags - [28 .. 151] reserved (0) - - Negotiation header with options, part 1: - [ 0 .. 7] passwd ("NBDMAGIC") - [ 8 .. 15] magic (NBD_OPTS_MAGIC) - [16 .. 17] server flags (0) - - part 2 (after options are sent): - [18 .. 25] size - [26 .. 27] export flags - [28 .. 151] reserved (0) - */ - - qemu_set_block(csock); - rc = -EINVAL; - - TRACE("Beginning negotiation."); - memset(buf, 0, sizeof(buf)); - memcpy(buf, "NBDMAGIC", 8); - if (client->exp) { - assert ((client->exp->nbdflags & ~65535) == 0); - cpu_to_be64w((uint64_t*)(buf + 8), NBD_CLIENT_MAGIC); - cpu_to_be64w((uint64_t*)(buf + 16), client->exp->size); - cpu_to_be16w((uint16_t*)(buf + 26), client->exp->nbdflags | myflags); - } else { - cpu_to_be64w((uint64_t*)(buf + 8), NBD_OPTS_MAGIC); - cpu_to_be16w((uint16_t *)(buf + 16), NBD_FLAG_FIXED_NEWSTYLE); - } - - if (client->exp) { - if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { - LOG("write failed"); - goto fail; - } - } else { - if (write_sync(csock, buf, 18) != 18) { - LOG("write failed"); - goto fail; - } - rc = nbd_receive_options(client); - if (rc != 0) { - LOG("option negotiation failed"); - goto fail; - } - - assert ((client->exp->nbdflags & ~65535) == 0); - cpu_to_be64w((uint64_t*)(buf + 18), client->exp->size); - cpu_to_be16w((uint16_t*)(buf + 26), client->exp->nbdflags | myflags); - if (write_sync(csock, buf + 18, sizeof(buf) - 18) != sizeof(buf) - 18) { - LOG("write failed"); - goto fail; - } - } - - TRACE("Negotiation succeeded."); - rc = 0; -fail: - qemu_set_nonblock(csock); - return rc; -} - -int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, - off_t *size, Error **errp) -{ - char buf[256]; - uint64_t magic, s; - uint16_t tmp; - int rc; - - TRACE("Receiving negotiation."); - - rc = -EINVAL; - - if (read_sync(csock, buf, 8) != 8) { - error_setg(errp, "Failed to read data"); - goto fail; - } - - buf[8] = '\0'; - if (strlen(buf) == 0) { - error_setg(errp, "Server connection closed unexpectedly"); - goto fail; - } - - TRACE("Magic is %c%c%c%c%c%c%c%c", - qemu_isprint(buf[0]) ? buf[0] : '.', - qemu_isprint(buf[1]) ? buf[1] : '.', - qemu_isprint(buf[2]) ? buf[2] : '.', - qemu_isprint(buf[3]) ? buf[3] : '.', - qemu_isprint(buf[4]) ? buf[4] : '.', - qemu_isprint(buf[5]) ? buf[5] : '.', - qemu_isprint(buf[6]) ? buf[6] : '.', - qemu_isprint(buf[7]) ? buf[7] : '.'); - - if (memcmp(buf, "NBDMAGIC", 8) != 0) { - error_setg(errp, "Invalid magic received"); - goto fail; - } - - if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { - error_setg(errp, "Failed to read magic"); - goto fail; - } - magic = be64_to_cpu(magic); - TRACE("Magic is 0x%" PRIx64, magic); - - if (name) { - uint32_t reserved = 0; - uint32_t opt; - uint32_t namesize; - - TRACE("Checking magic (opts_magic)"); - if (magic != NBD_OPTS_MAGIC) { - if (magic == NBD_CLIENT_MAGIC) { - error_setg(errp, "Server does not support export names"); - } else { - error_setg(errp, "Bad magic received"); - } - goto fail; - } - if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { - error_setg(errp, "Failed to read server flags"); - goto fail; - } - *flags = be16_to_cpu(tmp) << 16; - /* reserved for future use */ - if (write_sync(csock, &reserved, sizeof(reserved)) != - sizeof(reserved)) { - error_setg(errp, "Failed to read reserved field"); - goto fail; - } - /* write the export name */ - magic = cpu_to_be64(magic); - if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { - error_setg(errp, "Failed to send export name magic"); - goto fail; - } - opt = cpu_to_be32(NBD_OPT_EXPORT_NAME); - if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) { - error_setg(errp, "Failed to send export name option number"); - goto fail; - } - namesize = cpu_to_be32(strlen(name)); - if (write_sync(csock, &namesize, sizeof(namesize)) != - sizeof(namesize)) { - error_setg(errp, "Failed to send export name length"); - goto fail; - } - if (write_sync(csock, (char*)name, strlen(name)) != strlen(name)) { - error_setg(errp, "Failed to send export name"); - goto fail; - } - } else { - TRACE("Checking magic (cli_magic)"); - - if (magic != NBD_CLIENT_MAGIC) { - if (magic == NBD_OPTS_MAGIC) { - error_setg(errp, "Server requires an export name"); - } else { - error_setg(errp, "Bad magic received"); - } - goto fail; - } - } - - if (read_sync(csock, &s, sizeof(s)) != sizeof(s)) { - error_setg(errp, "Failed to read export length"); - goto fail; - } - *size = be64_to_cpu(s); - TRACE("Size is %" PRIu64, *size); - - if (!name) { - if (read_sync(csock, flags, sizeof(*flags)) != sizeof(*flags)) { - error_setg(errp, "Failed to read export flags"); - goto fail; - } - *flags = be32_to_cpup(flags); - } else { - if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { - error_setg(errp, "Failed to read export flags"); - goto fail; - } - *flags |= be16_to_cpu(tmp); - } - if (read_sync(csock, &buf, 124) != 124) { - error_setg(errp, "Failed to read reserved block"); - goto fail; - } - rc = 0; - -fail: - return rc; -} - -#ifdef __linux__ -int nbd_init(int fd, int csock, uint32_t flags, off_t size) -{ - TRACE("Setting NBD socket"); - - if (ioctl(fd, NBD_SET_SOCK, csock) < 0) { - int serrno = errno; - LOG("Failed to set NBD socket"); - return -serrno; - } - - TRACE("Setting block size to %lu", (unsigned long)BDRV_SECTOR_SIZE); - - if (ioctl(fd, NBD_SET_BLKSIZE, (size_t)BDRV_SECTOR_SIZE) < 0) { - int serrno = errno; - LOG("Failed setting NBD block size"); - return -serrno; - } - - TRACE("Setting size to %zd block(s)", (size_t)(size / BDRV_SECTOR_SIZE)); - - if (ioctl(fd, NBD_SET_SIZE_BLOCKS, (size_t)(size / BDRV_SECTOR_SIZE)) < 0) { - int serrno = errno; - LOG("Failed setting size (in blocks)"); - return -serrno; - } - - if (ioctl(fd, NBD_SET_FLAGS, flags) < 0) { - if (errno == ENOTTY) { - int read_only = (flags & NBD_FLAG_READ_ONLY) != 0; - TRACE("Setting readonly attribute"); - - if (ioctl(fd, BLKROSET, (unsigned long) &read_only) < 0) { - int serrno = errno; - LOG("Failed setting read-only attribute"); - return -serrno; - } - } else { - int serrno = errno; - LOG("Failed setting flags"); - return -serrno; - } - } - - TRACE("Negotiation ended"); - - return 0; -} - -int nbd_disconnect(int fd) -{ - ioctl(fd, NBD_CLEAR_QUE); - ioctl(fd, NBD_DISCONNECT); - ioctl(fd, NBD_CLEAR_SOCK); - return 0; -} - -int nbd_client(int fd) -{ - int ret; - int serrno; - - TRACE("Doing NBD loop"); - - ret = ioctl(fd, NBD_DO_IT); - if (ret < 0 && errno == EPIPE) { - /* NBD_DO_IT normally returns EPIPE when someone has disconnected - * the socket via NBD_DISCONNECT. We do not want to return 1 in - * that case. - */ - ret = 0; - } - serrno = errno; - - TRACE("NBD loop returned %d: %s", ret, strerror(serrno)); - - TRACE("Clearing NBD queue"); - ioctl(fd, NBD_CLEAR_QUE); - - TRACE("Clearing NBD socket"); - ioctl(fd, NBD_CLEAR_SOCK); - - errno = serrno; - return ret; -} -#else -int nbd_init(int fd, int csock, uint32_t flags, off_t size) -{ - return -ENOTSUP; -} - -int nbd_disconnect(int fd) -{ - return -ENOTSUP; -} - -int nbd_client(int fd) -{ - return -ENOTSUP; -} -#endif - -ssize_t nbd_send_request(int csock, struct nbd_request *request) -{ - uint8_t buf[NBD_REQUEST_SIZE]; - ssize_t ret; - - cpu_to_be32w((uint32_t*)buf, NBD_REQUEST_MAGIC); - cpu_to_be32w((uint32_t*)(buf + 4), request->type); - cpu_to_be64w((uint64_t*)(buf + 8), request->handle); - cpu_to_be64w((uint64_t*)(buf + 16), request->from); - cpu_to_be32w((uint32_t*)(buf + 24), request->len); - - TRACE("Sending request to client: " - "{ .from = %" PRIu64", .len = %u, .handle = %" PRIu64", .type=%i}", - request->from, request->len, request->handle, request->type); - - ret = write_sync(csock, buf, sizeof(buf)); - if (ret < 0) { - return ret; - } - - if (ret != sizeof(buf)) { - LOG("writing to socket failed"); - return -EINVAL; - } - return 0; -} - -static ssize_t nbd_receive_request(int csock, struct nbd_request *request) -{ - uint8_t buf[NBD_REQUEST_SIZE]; - uint32_t magic; - ssize_t ret; - - ret = read_sync(csock, buf, sizeof(buf)); - if (ret < 0) { - return ret; - } - - if (ret != sizeof(buf)) { - LOG("read failed"); - return -EINVAL; - } - - /* Request - [ 0 .. 3] magic (NBD_REQUEST_MAGIC) - [ 4 .. 7] type (0 == READ, 1 == WRITE) - [ 8 .. 15] handle - [16 .. 23] from - [24 .. 27] len - */ - - magic = be32_to_cpup((uint32_t*)buf); - request->type = be32_to_cpup((uint32_t*)(buf + 4)); - request->handle = be64_to_cpup((uint64_t*)(buf + 8)); - request->from = be64_to_cpup((uint64_t*)(buf + 16)); - request->len = be32_to_cpup((uint32_t*)(buf + 24)); - - TRACE("Got request: " - "{ magic = 0x%x, .type = %d, from = %" PRIu64" , len = %u }", - magic, request->type, request->from, request->len); - - if (magic != NBD_REQUEST_MAGIC) { - LOG("invalid magic (got 0x%x)", magic); - return -EINVAL; - } - return 0; -} - -ssize_t nbd_receive_reply(int csock, struct nbd_reply *reply) -{ - uint8_t buf[NBD_REPLY_SIZE]; - uint32_t magic; - ssize_t ret; - - ret = read_sync(csock, buf, sizeof(buf)); - if (ret < 0) { - return ret; - } - - if (ret != sizeof(buf)) { - LOG("read failed"); - return -EINVAL; - } - - /* Reply - [ 0 .. 3] magic (NBD_REPLY_MAGIC) - [ 4 .. 7] error (0 == no error) - [ 7 .. 15] handle - */ - - magic = be32_to_cpup((uint32_t*)buf); - reply->error = be32_to_cpup((uint32_t*)(buf + 4)); - reply->handle = be64_to_cpup((uint64_t*)(buf + 8)); - - reply->error = nbd_errno_to_system_errno(reply->error); - - TRACE("Got reply: " - "{ magic = 0x%x, .error = %d, handle = %" PRIu64" }", - magic, reply->error, reply->handle); - - if (magic != NBD_REPLY_MAGIC) { - LOG("invalid magic (got 0x%x)", magic); - return -EINVAL; - } - return 0; -} - -static ssize_t nbd_send_reply(int csock, struct nbd_reply *reply) -{ - uint8_t buf[NBD_REPLY_SIZE]; - ssize_t ret; - - reply->error = system_errno_to_nbd_errno(reply->error); - - /* Reply - [ 0 .. 3] magic (NBD_REPLY_MAGIC) - [ 4 .. 7] error (0 == no error) - [ 7 .. 15] handle - */ - cpu_to_be32w((uint32_t*)buf, NBD_REPLY_MAGIC); - cpu_to_be32w((uint32_t*)(buf + 4), reply->error); - cpu_to_be64w((uint64_t*)(buf + 8), reply->handle); - - TRACE("Sending response to client"); - - ret = write_sync(csock, buf, sizeof(buf)); - if (ret < 0) { - return ret; - } - - if (ret != sizeof(buf)) { - LOG("writing to socket failed"); - return -EINVAL; - } - return 0; -} - -#define MAX_NBD_REQUESTS 16 - -void nbd_client_get(NBDClient *client) -{ - client->refcount++; -} - -void nbd_client_put(NBDClient *client) -{ - if (--client->refcount == 0) { - /* The last reference should be dropped by client->close, - * which is called by client_close. - */ - assert(client->closing); - - nbd_unset_handlers(client); - close(client->sock); - client->sock = -1; - if (client->exp) { - QTAILQ_REMOVE(&client->exp->clients, client, next); - nbd_export_put(client->exp); - } - g_free(client); - } -} - -static void client_close(NBDClient *client) -{ - if (client->closing) { - return; - } - - client->closing = true; - - /* Force requests to finish. They will drop their own references, - * then we'll close the socket and free the NBDClient. - */ - shutdown(client->sock, 2); - - /* Also tell the client, so that they release their reference. */ - if (client->close) { - client->close(client); - } -} - -static NBDRequest *nbd_request_get(NBDClient *client) -{ - NBDRequest *req; - - assert(client->nb_requests <= MAX_NBD_REQUESTS - 1); - client->nb_requests++; - nbd_update_can_read(client); - - req = g_slice_new0(NBDRequest); - nbd_client_get(client); - req->client = client; - return req; -} - -static void nbd_request_put(NBDRequest *req) -{ - NBDClient *client = req->client; - - if (req->data) { - qemu_vfree(req->data); - } - g_slice_free(NBDRequest, req); - - client->nb_requests--; - nbd_update_can_read(client); - nbd_client_put(client); -} - -static void blk_aio_attached(AioContext *ctx, void *opaque) -{ - NBDExport *exp = opaque; - NBDClient *client; - - TRACE("Export %s: Attaching clients to AIO context %p\n", exp->name, ctx); - - exp->ctx = ctx; - - QTAILQ_FOREACH(client, &exp->clients, next) { - nbd_set_handlers(client); - } -} - -static void blk_aio_detach(void *opaque) -{ - NBDExport *exp = opaque; - NBDClient *client; - - TRACE("Export %s: Detaching clients from AIO context %p\n", exp->name, exp->ctx); - - QTAILQ_FOREACH(client, &exp->clients, next) { - nbd_unset_handlers(client); - } - - exp->ctx = NULL; -} - -NBDExport *nbd_export_new(BlockBackend *blk, off_t dev_offset, off_t size, - uint32_t nbdflags, void (*close)(NBDExport *), - Error **errp) -{ - NBDExport *exp = g_malloc0(sizeof(NBDExport)); - exp->refcount = 1; - QTAILQ_INIT(&exp->clients); - exp->blk = blk; - exp->dev_offset = dev_offset; - exp->nbdflags = nbdflags; - exp->size = size < 0 ? blk_getlength(blk) : size; - if (exp->size < 0) { - error_setg_errno(errp, -exp->size, - "Failed to determine the NBD export's length"); - goto fail; - } - exp->size -= exp->size % BDRV_SECTOR_SIZE; - - exp->close = close; - exp->ctx = blk_get_aio_context(blk); - blk_ref(blk); - blk_add_aio_context_notifier(blk, blk_aio_attached, blk_aio_detach, exp); - /* - * NBD exports are used for non-shared storage migration. Make sure - * that BDRV_O_INCOMING is cleared and the image is ready for write - * access since the export could be available before migration handover. - */ - blk_invalidate_cache(blk, NULL); - return exp; - -fail: - g_free(exp); - return NULL; -} - -NBDExport *nbd_export_find(const char *name) -{ - NBDExport *exp; - QTAILQ_FOREACH(exp, &exports, next) { - if (strcmp(name, exp->name) == 0) { - return exp; - } - } - - return NULL; -} - -void nbd_export_set_name(NBDExport *exp, const char *name) -{ - if (exp->name == name) { - return; - } - - nbd_export_get(exp); - if (exp->name != NULL) { - g_free(exp->name); - exp->name = NULL; - QTAILQ_REMOVE(&exports, exp, next); - nbd_export_put(exp); - } - if (name != NULL) { - nbd_export_get(exp); - exp->name = g_strdup(name); - QTAILQ_INSERT_TAIL(&exports, exp, next); - } - nbd_export_put(exp); -} - -void nbd_export_close(NBDExport *exp) -{ - NBDClient *client, *next; - - nbd_export_get(exp); - QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) { - client_close(client); - } - nbd_export_set_name(exp, NULL); - nbd_export_put(exp); - if (exp->blk) { - blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, - blk_aio_detach, exp); - blk_unref(exp->blk); - exp->blk = NULL; - } -} - -void nbd_export_get(NBDExport *exp) -{ - assert(exp->refcount > 0); - exp->refcount++; -} - -void nbd_export_put(NBDExport *exp) -{ - assert(exp->refcount > 0); - if (exp->refcount == 1) { - nbd_export_close(exp); - } - - if (--exp->refcount == 0) { - assert(exp->name == NULL); - - if (exp->close) { - exp->close(exp); - } - - g_free(exp); - } -} - -BlockBackend *nbd_export_get_blockdev(NBDExport *exp) -{ - return exp->blk; -} - -void nbd_export_close_all(void) -{ - NBDExport *exp, *next; - - QTAILQ_FOREACH_SAFE(exp, &exports, next, next) { - nbd_export_close(exp); - } -} - -static ssize_t nbd_co_send_reply(NBDRequest *req, struct nbd_reply *reply, - int len) -{ - NBDClient *client = req->client; - int csock = client->sock; - ssize_t rc, ret; - - qemu_co_mutex_lock(&client->send_lock); - client->send_coroutine = qemu_coroutine_self(); - nbd_set_handlers(client); - - if (!len) { - rc = nbd_send_reply(csock, reply); - } else { - socket_set_cork(csock, 1); - rc = nbd_send_reply(csock, reply); - if (rc >= 0) { - ret = qemu_co_send(csock, req->data, len); - if (ret != len) { - rc = -EIO; - } - } - socket_set_cork(csock, 0); - } - - client->send_coroutine = NULL; - nbd_set_handlers(client); - qemu_co_mutex_unlock(&client->send_lock); - return rc; -} - -static ssize_t nbd_co_receive_request(NBDRequest *req, struct nbd_request *request) -{ - NBDClient *client = req->client; - int csock = client->sock; - uint32_t command; - ssize_t rc; - - client->recv_coroutine = qemu_coroutine_self(); - nbd_update_can_read(client); - - rc = nbd_receive_request(csock, request); - if (rc < 0) { - if (rc != -EAGAIN) { - rc = -EIO; - } - goto out; - } - - if (request->len > NBD_MAX_BUFFER_SIZE) { - LOG("len (%u) is larger than max len (%u)", - request->len, NBD_MAX_BUFFER_SIZE); - rc = -EINVAL; - goto out; - } - - if ((request->from + request->len) < request->from) { - LOG("integer overflow detected! " - "you're probably being attacked"); - rc = -EINVAL; - goto out; - } - - TRACE("Decoding type"); - - command = request->type & NBD_CMD_MASK_COMMAND; - if (command == NBD_CMD_READ || command == NBD_CMD_WRITE) { - req->data = blk_blockalign(client->exp->blk, request->len); - } - if (command == NBD_CMD_WRITE) { - TRACE("Reading %u byte(s)", request->len); - - if (qemu_co_recv(csock, req->data, request->len) != request->len) { - LOG("reading from socket failed"); - rc = -EIO; - goto out; - } - } - rc = 0; - -out: - client->recv_coroutine = NULL; - nbd_update_can_read(client); - - return rc; -} - -static void nbd_trip(void *opaque) -{ - NBDClient *client = opaque; - NBDExport *exp = client->exp; - NBDRequest *req; - struct nbd_request request; - struct nbd_reply reply; - ssize_t ret; - uint32_t command; - - TRACE("Reading request."); - if (client->closing) { - return; - } - - req = nbd_request_get(client); - ret = nbd_co_receive_request(req, &request); - if (ret == -EAGAIN) { - goto done; - } - if (ret == -EIO) { - goto out; - } - - reply.handle = request.handle; - reply.error = 0; - - if (ret < 0) { - reply.error = -ret; - goto error_reply; - } - command = request.type & NBD_CMD_MASK_COMMAND; - if (command != NBD_CMD_DISC && (request.from + request.len) > exp->size) { - LOG("From: %" PRIu64 ", Len: %u, Size: %" PRIu64 - ", Offset: %" PRIu64 "\n", - request.from, request.len, - (uint64_t)exp->size, (uint64_t)exp->dev_offset); - LOG("requested operation past EOF--bad client?"); - goto invalid_request; - } - - switch (command) { - case NBD_CMD_READ: - TRACE("Request type is READ"); - - if (request.type & NBD_CMD_FLAG_FUA) { - ret = blk_co_flush(exp->blk); - if (ret < 0) { - LOG("flush failed"); - reply.error = -ret; - goto error_reply; - } - } - - ret = blk_read(exp->blk, - (request.from + exp->dev_offset) / BDRV_SECTOR_SIZE, - req->data, request.len / BDRV_SECTOR_SIZE); - if (ret < 0) { - LOG("reading from file failed"); - reply.error = -ret; - goto error_reply; - } - - TRACE("Read %u byte(s)", request.len); - if (nbd_co_send_reply(req, &reply, request.len) < 0) - goto out; - break; - case NBD_CMD_WRITE: - TRACE("Request type is WRITE"); - - if (exp->nbdflags & NBD_FLAG_READ_ONLY) { - TRACE("Server is read-only, return error"); - reply.error = EROFS; - goto error_reply; - } - - TRACE("Writing to device"); - - ret = blk_write(exp->blk, - (request.from + exp->dev_offset) / BDRV_SECTOR_SIZE, - req->data, request.len / BDRV_SECTOR_SIZE); - if (ret < 0) { - LOG("writing to file failed"); - reply.error = -ret; - goto error_reply; - } - - if (request.type & NBD_CMD_FLAG_FUA) { - ret = blk_co_flush(exp->blk); - if (ret < 0) { - LOG("flush failed"); - reply.error = -ret; - goto error_reply; - } - } - - if (nbd_co_send_reply(req, &reply, 0) < 0) { - goto out; - } - break; - case NBD_CMD_DISC: - TRACE("Request type is DISCONNECT"); - errno = 0; - goto out; - case NBD_CMD_FLUSH: - TRACE("Request type is FLUSH"); - - ret = blk_co_flush(exp->blk); - if (ret < 0) { - LOG("flush failed"); - reply.error = -ret; - } - if (nbd_co_send_reply(req, &reply, 0) < 0) { - goto out; - } - break; - case NBD_CMD_TRIM: - TRACE("Request type is TRIM"); - ret = blk_co_discard(exp->blk, (request.from + exp->dev_offset) - / BDRV_SECTOR_SIZE, - request.len / BDRV_SECTOR_SIZE); - if (ret < 0) { - LOG("discard failed"); - reply.error = -ret; - } - if (nbd_co_send_reply(req, &reply, 0) < 0) { - goto out; - } - break; - default: - LOG("invalid request type (%u) received", request.type); - invalid_request: - reply.error = EINVAL; - error_reply: - if (nbd_co_send_reply(req, &reply, 0) < 0) { - goto out; - } - break; - } - - TRACE("Request/Reply complete"); - -done: - nbd_request_put(req); - return; - -out: - nbd_request_put(req); - client_close(client); -} - -static void nbd_read(void *opaque) -{ - NBDClient *client = opaque; - - if (client->recv_coroutine) { - qemu_coroutine_enter(client->recv_coroutine, NULL); - } else { - qemu_coroutine_enter(qemu_coroutine_create(nbd_trip), client); - } -} - -static void nbd_restart_write(void *opaque) -{ - NBDClient *client = opaque; - - qemu_coroutine_enter(client->send_coroutine, NULL); -} - -static void nbd_set_handlers(NBDClient *client) -{ - if (client->exp && client->exp->ctx) { - aio_set_fd_handler(client->exp->ctx, client->sock, - client->can_read ? nbd_read : NULL, - client->send_coroutine ? nbd_restart_write : NULL, - client); - } -} - -static void nbd_unset_handlers(NBDClient *client) -{ - if (client->exp && client->exp->ctx) { - aio_set_fd_handler(client->exp->ctx, client->sock, NULL, NULL, NULL); - } -} - -static void nbd_update_can_read(NBDClient *client) -{ - bool can_read = client->recv_coroutine || - client->nb_requests < MAX_NBD_REQUESTS; - - if (can_read != client->can_read) { - client->can_read = can_read; - nbd_set_handlers(client); - - /* There is no need to invoke aio_notify(), since aio_set_fd_handler() - * in nbd_set_handlers() will have taken care of that */ - } -} - -NBDClient *nbd_client_new(NBDExport *exp, int csock, - void (*close)(NBDClient *)) -{ - NBDClient *client; - client = g_malloc0(sizeof(NBDClient)); - client->refcount = 1; - client->exp = exp; - client->sock = csock; - client->can_read = true; - if (nbd_send_negotiate(client)) { - g_free(client); - return NULL; - } - client->close = close; - qemu_co_mutex_init(&client->send_lock); - nbd_set_handlers(client); - - if (exp) { - QTAILQ_INSERT_TAIL(&exp->clients, client, next); - nbd_export_get(exp); - } - return client; -} diff --git a/qemu/nbd/Makefile.objs b/qemu/nbd/Makefile.objs new file mode 100644 index 000000000..eb3dd4461 --- /dev/null +++ b/qemu/nbd/Makefile.objs @@ -0,0 +1 @@ +block-obj-y += server.o client.o common.o diff --git a/qemu/nbd/client.c b/qemu/nbd/client.c new file mode 100644 index 000000000..48f2a21f3 --- /dev/null +++ b/qemu/nbd/client.c @@ -0,0 +1,745 @@ +/* + * Copyright (C) 2005 Anthony Liguori + * + * Network Block Device Client Side + * + * 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; under version 2 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 . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "nbd-internal.h" + +static int nbd_errno_to_system_errno(int err) +{ + switch (err) { + case NBD_SUCCESS: + return 0; + case NBD_EPERM: + return EPERM; + case NBD_EIO: + return EIO; + case NBD_ENOMEM: + return ENOMEM; + case NBD_ENOSPC: + return ENOSPC; + case NBD_EINVAL: + default: + return EINVAL; + } +} + +/* Definitions for opaque data types */ + +static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); + +/* That's all folks */ + +/* Basic flow for negotiation + + Server Client + Negotiate + + or + + Server Client + Negotiate #1 + Option + Negotiate #2 + + ---- + + followed by + + Server Client + Request + Response + Request + Response + ... + ... + Request (type == 2) + +*/ + + +/* If type represents success, return 1 without further action. + * If type represents an error reply, consume the rest of the packet on ioc. + * Then return 0 for unsupported (so the client can fall back to + * other approaches), or -1 with errp set for other errors. + */ +static int nbd_handle_reply_err(QIOChannel *ioc, uint32_t opt, uint32_t type, + Error **errp) +{ + uint32_t len; + char *msg = NULL; + int result = -1; + + if (!(type & (1 << 31))) { + return 1; + } + + if (read_sync(ioc, &len, sizeof(len)) != sizeof(len)) { + error_setg(errp, "failed to read option length"); + return -1; + } + len = be32_to_cpu(len); + if (len) { + if (len > NBD_MAX_BUFFER_SIZE) { + error_setg(errp, "server's error message is too long"); + goto cleanup; + } + msg = g_malloc(len + 1); + if (read_sync(ioc, msg, len) != len) { + error_setg(errp, "failed to read option error message"); + goto cleanup; + } + msg[len] = '\0'; + } + + switch (type) { + case NBD_REP_ERR_UNSUP: + TRACE("server doesn't understand request %d, attempting fallback", + opt); + result = 0; + goto cleanup; + + case NBD_REP_ERR_POLICY: + error_setg(errp, "Denied by server for option %x", opt); + break; + + case NBD_REP_ERR_INVALID: + error_setg(errp, "Invalid data length for option %x", opt); + break; + + case NBD_REP_ERR_TLS_REQD: + error_setg(errp, "TLS negotiation required before option %x", opt); + break; + + default: + error_setg(errp, "Unknown error code when asking for option %x", opt); + break; + } + + if (msg) { + error_append_hint(errp, "%s\n", msg); + } + + cleanup: + g_free(msg); + return result; +} + +static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp) +{ + uint64_t magic; + uint32_t opt; + uint32_t type; + uint32_t len; + uint32_t namelen; + int error; + + *name = NULL; + if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) { + error_setg(errp, "failed to read list option magic"); + return -1; + } + magic = be64_to_cpu(magic); + if (magic != NBD_REP_MAGIC) { + error_setg(errp, "Unexpected option list magic"); + return -1; + } + if (read_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) { + error_setg(errp, "failed to read list option"); + return -1; + } + opt = be32_to_cpu(opt); + if (opt != NBD_OPT_LIST) { + error_setg(errp, "Unexpected option type %x expected %x", + opt, NBD_OPT_LIST); + return -1; + } + + if (read_sync(ioc, &type, sizeof(type)) != sizeof(type)) { + error_setg(errp, "failed to read list option type"); + return -1; + } + type = be32_to_cpu(type); + error = nbd_handle_reply_err(ioc, opt, type, errp); + if (error <= 0) { + return error; + } + + if (read_sync(ioc, &len, sizeof(len)) != sizeof(len)) { + error_setg(errp, "failed to read option length"); + return -1; + } + len = be32_to_cpu(len); + + if (type == NBD_REP_ACK) { + if (len != 0) { + error_setg(errp, "length too long for option end"); + return -1; + } + } else if (type == NBD_REP_SERVER) { + if (len < sizeof(namelen) || len > NBD_MAX_BUFFER_SIZE) { + error_setg(errp, "incorrect option length"); + return -1; + } + if (read_sync(ioc, &namelen, sizeof(namelen)) != sizeof(namelen)) { + error_setg(errp, "failed to read option name length"); + return -1; + } + namelen = be32_to_cpu(namelen); + len -= sizeof(namelen); + if (len < namelen) { + error_setg(errp, "incorrect option name length"); + return -1; + } + if (namelen > 255) { + error_setg(errp, "export name length too long %d", namelen); + return -1; + } + + *name = g_new0(char, namelen + 1); + if (read_sync(ioc, *name, namelen) != namelen) { + error_setg(errp, "failed to read export name"); + g_free(*name); + *name = NULL; + return -1; + } + (*name)[namelen] = '\0'; + len -= namelen; + if (len) { + char *buf = g_malloc(len + 1); + if (read_sync(ioc, buf, len) != len) { + error_setg(errp, "failed to read export description"); + g_free(*name); + g_free(buf); + *name = NULL; + return -1; + } + buf[len] = '\0'; + TRACE("Ignoring export description: %s", buf); + g_free(buf); + } + } else { + error_setg(errp, "Unexpected reply type %x expected %x", + type, NBD_REP_SERVER); + return -1; + } + return 1; +} + + +static int nbd_receive_query_exports(QIOChannel *ioc, + const char *wantname, + Error **errp) +{ + uint64_t magic = cpu_to_be64(NBD_OPTS_MAGIC); + uint32_t opt = cpu_to_be32(NBD_OPT_LIST); + uint32_t length = 0; + bool foundExport = false; + + TRACE("Querying export list"); + if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) { + error_setg(errp, "Failed to send list option magic"); + return -1; + } + + if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) { + error_setg(errp, "Failed to send list option number"); + return -1; + } + + if (write_sync(ioc, &length, sizeof(length)) != sizeof(length)) { + error_setg(errp, "Failed to send list option length"); + return -1; + } + + TRACE("Reading available export names"); + while (1) { + char *name = NULL; + int ret = nbd_receive_list(ioc, &name, errp); + + if (ret < 0) { + g_free(name); + name = NULL; + return -1; + } + if (ret == 0) { + /* Server doesn't support export listing, so + * we will just assume an export with our + * wanted name exists */ + foundExport = true; + break; + } + if (name == NULL) { + TRACE("End of export name list"); + break; + } + if (g_str_equal(name, wantname)) { + foundExport = true; + TRACE("Found desired export name '%s'", name); + } else { + TRACE("Ignored export name '%s'", name); + } + g_free(name); + } + + if (!foundExport) { + error_setg(errp, "No export with name '%s' available", wantname); + return -1; + } + + return 0; +} + +static QIOChannel *nbd_receive_starttls(QIOChannel *ioc, + QCryptoTLSCreds *tlscreds, + const char *hostname, Error **errp) +{ + uint64_t magic = cpu_to_be64(NBD_OPTS_MAGIC); + uint32_t opt = cpu_to_be32(NBD_OPT_STARTTLS); + uint32_t length = 0; + uint32_t type; + QIOChannelTLS *tioc; + struct NBDTLSHandshakeData data = { 0 }; + + TRACE("Requesting TLS from server"); + if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) { + error_setg(errp, "Failed to send option magic"); + return NULL; + } + + if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) { + error_setg(errp, "Failed to send option number"); + return NULL; + } + + if (write_sync(ioc, &length, sizeof(length)) != sizeof(length)) { + error_setg(errp, "Failed to send option length"); + return NULL; + } + + TRACE("Getting TLS reply from server1"); + if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) { + error_setg(errp, "failed to read option magic"); + return NULL; + } + magic = be64_to_cpu(magic); + if (magic != NBD_REP_MAGIC) { + error_setg(errp, "Unexpected option magic"); + return NULL; + } + TRACE("Getting TLS reply from server2"); + if (read_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) { + error_setg(errp, "failed to read option"); + return NULL; + } + opt = be32_to_cpu(opt); + if (opt != NBD_OPT_STARTTLS) { + error_setg(errp, "Unexpected option type %x expected %x", + opt, NBD_OPT_STARTTLS); + return NULL; + } + + TRACE("Getting TLS reply from server"); + if (read_sync(ioc, &type, sizeof(type)) != sizeof(type)) { + error_setg(errp, "failed to read option type"); + return NULL; + } + type = be32_to_cpu(type); + if (type != NBD_REP_ACK) { + error_setg(errp, "Server rejected request to start TLS %x", + type); + return NULL; + } + + TRACE("Getting TLS reply from server"); + if (read_sync(ioc, &length, sizeof(length)) != sizeof(length)) { + error_setg(errp, "failed to read option length"); + return NULL; + } + length = be32_to_cpu(length); + if (length != 0) { + error_setg(errp, "Start TLS reponse was not zero %x", + length); + return NULL; + } + + TRACE("TLS request approved, setting up TLS"); + tioc = qio_channel_tls_new_client(ioc, tlscreds, hostname, errp); + if (!tioc) { + return NULL; + } + data.loop = g_main_loop_new(g_main_context_default(), FALSE); + TRACE("Starting TLS hanshake"); + qio_channel_tls_handshake(tioc, + nbd_tls_handshake, + &data, + NULL); + + if (!data.complete) { + g_main_loop_run(data.loop); + } + g_main_loop_unref(data.loop); + if (data.error) { + error_propagate(errp, data.error); + object_unref(OBJECT(tioc)); + return NULL; + } + + return QIO_CHANNEL(tioc); +} + + +int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags, + QCryptoTLSCreds *tlscreds, const char *hostname, + QIOChannel **outioc, + off_t *size, Error **errp) +{ + char buf[256]; + uint64_t magic, s; + int rc; + + TRACE("Receiving negotiation tlscreds=%p hostname=%s.", + tlscreds, hostname ? hostname : ""); + + rc = -EINVAL; + + if (outioc) { + *outioc = NULL; + } + if (tlscreds && !outioc) { + error_setg(errp, "Output I/O channel required for TLS"); + goto fail; + } + + if (read_sync(ioc, buf, 8) != 8) { + error_setg(errp, "Failed to read data"); + goto fail; + } + + buf[8] = '\0'; + if (strlen(buf) == 0) { + error_setg(errp, "Server connection closed unexpectedly"); + goto fail; + } + + TRACE("Magic is %c%c%c%c%c%c%c%c", + qemu_isprint(buf[0]) ? buf[0] : '.', + qemu_isprint(buf[1]) ? buf[1] : '.', + qemu_isprint(buf[2]) ? buf[2] : '.', + qemu_isprint(buf[3]) ? buf[3] : '.', + qemu_isprint(buf[4]) ? buf[4] : '.', + qemu_isprint(buf[5]) ? buf[5] : '.', + qemu_isprint(buf[6]) ? buf[6] : '.', + qemu_isprint(buf[7]) ? buf[7] : '.'); + + if (memcmp(buf, "NBDMAGIC", 8) != 0) { + error_setg(errp, "Invalid magic received"); + goto fail; + } + + if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) { + error_setg(errp, "Failed to read magic"); + goto fail; + } + magic = be64_to_cpu(magic); + TRACE("Magic is 0x%" PRIx64, magic); + + if (magic == NBD_OPTS_MAGIC) { + uint32_t clientflags = 0; + uint32_t opt; + uint32_t namesize; + uint16_t globalflags; + uint16_t exportflags; + bool fixedNewStyle = false; + + if (read_sync(ioc, &globalflags, sizeof(globalflags)) != + sizeof(globalflags)) { + error_setg(errp, "Failed to read server flags"); + goto fail; + } + globalflags = be16_to_cpu(globalflags); + *flags = globalflags << 16; + TRACE("Global flags are %x", globalflags); + if (globalflags & NBD_FLAG_FIXED_NEWSTYLE) { + fixedNewStyle = true; + TRACE("Server supports fixed new style"); + clientflags |= NBD_FLAG_C_FIXED_NEWSTYLE; + } + /* client requested flags */ + clientflags = cpu_to_be32(clientflags); + if (write_sync(ioc, &clientflags, sizeof(clientflags)) != + sizeof(clientflags)) { + error_setg(errp, "Failed to send clientflags field"); + goto fail; + } + if (tlscreds) { + if (fixedNewStyle) { + *outioc = nbd_receive_starttls(ioc, tlscreds, hostname, errp); + if (!*outioc) { + goto fail; + } + ioc = *outioc; + } else { + error_setg(errp, "Server does not support STARTTLS"); + goto fail; + } + } + if (!name) { + TRACE("Using default NBD export name \"\""); + name = ""; + } + if (fixedNewStyle) { + /* Check our desired export is present in the + * server export list. Since NBD_OPT_EXPORT_NAME + * cannot return an error message, running this + * query gives us good error reporting if the + * server required TLS + */ + if (nbd_receive_query_exports(ioc, name, errp) < 0) { + goto fail; + } + } + /* write the export name */ + magic = cpu_to_be64(magic); + if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) { + error_setg(errp, "Failed to send export name magic"); + goto fail; + } + opt = cpu_to_be32(NBD_OPT_EXPORT_NAME); + if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) { + error_setg(errp, "Failed to send export name option number"); + goto fail; + } + namesize = cpu_to_be32(strlen(name)); + if (write_sync(ioc, &namesize, sizeof(namesize)) != + sizeof(namesize)) { + error_setg(errp, "Failed to send export name length"); + goto fail; + } + if (write_sync(ioc, (char *)name, strlen(name)) != strlen(name)) { + error_setg(errp, "Failed to send export name"); + goto fail; + } + + if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) { + error_setg(errp, "Failed to read export length"); + goto fail; + } + *size = be64_to_cpu(s); + TRACE("Size is %" PRIu64, *size); + + if (read_sync(ioc, &exportflags, sizeof(exportflags)) != + sizeof(exportflags)) { + error_setg(errp, "Failed to read export flags"); + goto fail; + } + exportflags = be16_to_cpu(exportflags); + *flags |= exportflags; + TRACE("Export flags are %x", exportflags); + } else if (magic == NBD_CLIENT_MAGIC) { + if (name) { + error_setg(errp, "Server does not support export names"); + goto fail; + } + if (tlscreds) { + error_setg(errp, "Server does not support STARTTLS"); + goto fail; + } + + if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) { + error_setg(errp, "Failed to read export length"); + goto fail; + } + *size = be64_to_cpu(s); + TRACE("Size is %" PRIu64, *size); + + if (read_sync(ioc, flags, sizeof(*flags)) != sizeof(*flags)) { + error_setg(errp, "Failed to read export flags"); + goto fail; + } + *flags = be32_to_cpup(flags); + } else { + error_setg(errp, "Bad magic received"); + goto fail; + } + + if (read_sync(ioc, &buf, 124) != 124) { + error_setg(errp, "Failed to read reserved block"); + goto fail; + } + rc = 0; + +fail: + return rc; +} + +#ifdef __linux__ +int nbd_init(int fd, QIOChannelSocket *sioc, uint32_t flags, off_t size) +{ + TRACE("Setting NBD socket"); + + if (ioctl(fd, NBD_SET_SOCK, sioc->fd) < 0) { + int serrno = errno; + LOG("Failed to set NBD socket"); + return -serrno; + } + + TRACE("Setting block size to %lu", (unsigned long)BDRV_SECTOR_SIZE); + + if (ioctl(fd, NBD_SET_BLKSIZE, (size_t)BDRV_SECTOR_SIZE) < 0) { + int serrno = errno; + LOG("Failed setting NBD block size"); + return -serrno; + } + + TRACE("Setting size to %zd block(s)", (size_t)(size / BDRV_SECTOR_SIZE)); + + if (ioctl(fd, NBD_SET_SIZE_BLOCKS, (size_t)(size / BDRV_SECTOR_SIZE)) < 0) { + int serrno = errno; + LOG("Failed setting size (in blocks)"); + return -serrno; + } + + if (ioctl(fd, NBD_SET_FLAGS, flags) < 0) { + if (errno == ENOTTY) { + int read_only = (flags & NBD_FLAG_READ_ONLY) != 0; + TRACE("Setting readonly attribute"); + + if (ioctl(fd, BLKROSET, (unsigned long) &read_only) < 0) { + int serrno = errno; + LOG("Failed setting read-only attribute"); + return -serrno; + } + } else { + int serrno = errno; + LOG("Failed setting flags"); + return -serrno; + } + } + + TRACE("Negotiation ended"); + + return 0; +} + +int nbd_client(int fd) +{ + int ret; + int serrno; + + TRACE("Doing NBD loop"); + + ret = ioctl(fd, NBD_DO_IT); + if (ret < 0 && errno == EPIPE) { + /* NBD_DO_IT normally returns EPIPE when someone has disconnected + * the socket via NBD_DISCONNECT. We do not want to return 1 in + * that case. + */ + ret = 0; + } + serrno = errno; + + TRACE("NBD loop returned %d: %s", ret, strerror(serrno)); + + TRACE("Clearing NBD queue"); + ioctl(fd, NBD_CLEAR_QUE); + + TRACE("Clearing NBD socket"); + ioctl(fd, NBD_CLEAR_SOCK); + + errno = serrno; + return ret; +} +#else +int nbd_init(int fd, QIOChannelSocket *ioc, uint32_t flags, off_t size) +{ + return -ENOTSUP; +} + +int nbd_client(int fd) +{ + return -ENOTSUP; +} +#endif + +ssize_t nbd_send_request(QIOChannel *ioc, struct nbd_request *request) +{ + uint8_t buf[NBD_REQUEST_SIZE]; + ssize_t ret; + + TRACE("Sending request to server: " + "{ .from = %" PRIu64", .len = %u, .handle = %" PRIu64", .type=%i}", + request->from, request->len, request->handle, request->type); + + cpu_to_be32w((uint32_t*)buf, NBD_REQUEST_MAGIC); + cpu_to_be32w((uint32_t*)(buf + 4), request->type); + cpu_to_be64w((uint64_t*)(buf + 8), request->handle); + cpu_to_be64w((uint64_t*)(buf + 16), request->from); + cpu_to_be32w((uint32_t*)(buf + 24), request->len); + + ret = write_sync(ioc, buf, sizeof(buf)); + if (ret < 0) { + return ret; + } + + if (ret != sizeof(buf)) { + LOG("writing to socket failed"); + return -EINVAL; + } + return 0; +} + +ssize_t nbd_receive_reply(QIOChannel *ioc, struct nbd_reply *reply) +{ + uint8_t buf[NBD_REPLY_SIZE]; + uint32_t magic; + ssize_t ret; + + ret = read_sync(ioc, buf, sizeof(buf)); + if (ret < 0) { + return ret; + } + + if (ret != sizeof(buf)) { + LOG("read failed"); + return -EINVAL; + } + + /* Reply + [ 0 .. 3] magic (NBD_REPLY_MAGIC) + [ 4 .. 7] error (0 == no error) + [ 7 .. 15] handle + */ + + magic = be32_to_cpup((uint32_t*)buf); + reply->error = be32_to_cpup((uint32_t*)(buf + 4)); + reply->handle = be64_to_cpup((uint64_t*)(buf + 8)); + + reply->error = nbd_errno_to_system_errno(reply->error); + + TRACE("Got reply: " + "{ magic = 0x%x, .error = %d, handle = %" PRIu64" }", + magic, reply->error, reply->handle); + + if (magic != NBD_REPLY_MAGIC) { + LOG("invalid magic (got 0x%x)", magic); + return -EINVAL; + } + return 0; +} + diff --git a/qemu/nbd/common.c b/qemu/nbd/common.c new file mode 100644 index 000000000..8ddb2dd2f --- /dev/null +++ b/qemu/nbd/common.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005 Anthony Liguori + * + * Network Block Device Common Code + * + * 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; under version 2 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 . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "nbd-internal.h" + +ssize_t nbd_wr_syncv(QIOChannel *ioc, + struct iovec *iov, + size_t niov, + size_t offset, + size_t length, + bool do_read) +{ + ssize_t done = 0; + Error *local_err = NULL; + struct iovec *local_iov = g_new(struct iovec, niov); + struct iovec *local_iov_head = local_iov; + unsigned int nlocal_iov = niov; + + nlocal_iov = iov_copy(local_iov, nlocal_iov, + iov, niov, + offset, length); + + while (nlocal_iov > 0) { + ssize_t len; + if (do_read) { + len = qio_channel_readv(ioc, local_iov, nlocal_iov, &local_err); + } else { + len = qio_channel_writev(ioc, local_iov, nlocal_iov, &local_err); + } + if (len == QIO_CHANNEL_ERR_BLOCK) { + if (qemu_in_coroutine()) { + /* XXX figure out if we can create a variant on + * qio_channel_yield() that works with AIO contexts + * and consider using that in this branch */ + qemu_coroutine_yield(); + } else if (done) { + /* XXX this is needed by nbd_reply_ready. */ + qio_channel_wait(ioc, + do_read ? G_IO_IN : G_IO_OUT); + } else { + return -EAGAIN; + } + continue; + } + if (len < 0) { + TRACE("I/O error: %s", error_get_pretty(local_err)); + error_free(local_err); + /* XXX handle Error objects */ + done = -EIO; + goto cleanup; + } + + if (do_read && len == 0) { + break; + } + + iov_discard_front(&local_iov, &nlocal_iov, len); + done += len; + } + + cleanup: + g_free(local_iov_head); + return done; +} + + +void nbd_tls_handshake(Object *src, + Error *err, + void *opaque) +{ + struct NBDTLSHandshakeData *data = opaque; + + if (err) { + TRACE("TLS failed %s", error_get_pretty(err)); + data->error = error_copy(err); + } + data->complete = true; + g_main_loop_quit(data->loop); +} diff --git a/qemu/nbd/nbd-internal.h b/qemu/nbd/nbd-internal.h new file mode 100644 index 000000000..379153561 --- /dev/null +++ b/qemu/nbd/nbd-internal.h @@ -0,0 +1,124 @@ +/* + * NBD Internal Declarations + * + * Copyright (C) 2016 Red Hat, Inc. + * + * 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 NBD_INTERNAL_H +#define NBD_INTERNAL_H +#include "block/nbd.h" +#include "sysemu/block-backend.h" +#include "io/channel-tls.h" + +#include "qemu/coroutine.h" +#include "qemu/iov.h" + +#ifndef _WIN32 +#include +#endif +#if defined(__sun__) || defined(__HAIKU__) +#include +#endif + +#ifdef __linux__ +#include +#endif + +#include "qemu/queue.h" +#include "qemu/main-loop.h" + +/* #define DEBUG_NBD */ + +#ifdef DEBUG_NBD +#define DEBUG_NBD_PRINT 1 +#else +#define DEBUG_NBD_PRINT 0 +#endif + +#define TRACE(msg, ...) do { \ + if (DEBUG_NBD_PRINT) { \ + LOG(msg, ## __VA_ARGS__); \ + } \ +} while (0) + +#define LOG(msg, ...) do { \ + fprintf(stderr, "%s:%s():L%d: " msg "\n", \ + __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \ +} while (0) + +/* This is all part of the "official" NBD API. + * + * The most up-to-date documentation is available at: + * https://github.com/yoe/nbd/blob/master/doc/proto.txt + */ + +#define NBD_REQUEST_SIZE (4 + 4 + 8 + 8 + 4) +#define NBD_REPLY_SIZE (4 + 4 + 8) +#define NBD_REQUEST_MAGIC 0x25609513 +#define NBD_REPLY_MAGIC 0x67446698 +#define NBD_OPTS_MAGIC 0x49484156454F5054LL +#define NBD_CLIENT_MAGIC 0x0000420281861253LL +#define NBD_REP_MAGIC 0x3e889045565a9LL + +#define NBD_SET_SOCK _IO(0xab, 0) +#define NBD_SET_BLKSIZE _IO(0xab, 1) +#define NBD_SET_SIZE _IO(0xab, 2) +#define NBD_DO_IT _IO(0xab, 3) +#define NBD_CLEAR_SOCK _IO(0xab, 4) +#define NBD_CLEAR_QUE _IO(0xab, 5) +#define NBD_PRINT_DEBUG _IO(0xab, 6) +#define NBD_SET_SIZE_BLOCKS _IO(0xab, 7) +#define NBD_DISCONNECT _IO(0xab, 8) +#define NBD_SET_TIMEOUT _IO(0xab, 9) +#define NBD_SET_FLAGS _IO(0xab, 10) + +#define NBD_OPT_EXPORT_NAME (1) +#define NBD_OPT_ABORT (2) +#define NBD_OPT_LIST (3) +#define NBD_OPT_PEEK_EXPORT (4) +#define NBD_OPT_STARTTLS (5) + +/* NBD errors are based on errno numbers, so there is a 1:1 mapping, + * but only a limited set of errno values is specified in the protocol. + * Everything else is squashed to EINVAL. + */ +#define NBD_SUCCESS 0 +#define NBD_EPERM 1 +#define NBD_EIO 5 +#define NBD_ENOMEM 12 +#define NBD_EINVAL 22 +#define NBD_ENOSPC 28 + +static inline ssize_t read_sync(QIOChannel *ioc, void *buffer, size_t size) +{ + struct iovec iov = { .iov_base = buffer, .iov_len = size }; + /* Sockets are kept in blocking mode in the negotiation phase. After + * that, a non-readable socket simply means that another thread stole + * our request/reply. Synchronization is done with recv_coroutine, so + * that this is coroutine-safe. + */ + return nbd_wr_syncv(ioc, &iov, 1, 0, size, true); +} + +static inline ssize_t write_sync(QIOChannel *ioc, void *buffer, size_t size) +{ + struct iovec iov = { .iov_base = buffer, .iov_len = size }; + + return nbd_wr_syncv(ioc, &iov, 1, 0, size, false); +} + +struct NBDTLSHandshakeData { + GMainLoop *loop; + bool complete; + Error *error; +}; + + +void nbd_tls_handshake(Object *src, + Error *err, + void *opaque); + +#endif diff --git a/qemu/nbd/server.c b/qemu/nbd/server.c new file mode 100644 index 000000000..2184c64fe --- /dev/null +++ b/qemu/nbd/server.c @@ -0,0 +1,1290 @@ +/* + * Copyright (C) 2005 Anthony Liguori + * + * Network Block Device Server Side + * + * 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; under version 2 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 . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "nbd-internal.h" + +static int system_errno_to_nbd_errno(int err) +{ + switch (err) { + case 0: + return NBD_SUCCESS; + case EPERM: + case EROFS: + return NBD_EPERM; + case EIO: + return NBD_EIO; + case ENOMEM: + return NBD_ENOMEM; +#ifdef EDQUOT + case EDQUOT: +#endif + case EFBIG: + case ENOSPC: + return NBD_ENOSPC; + case EINVAL: + default: + return NBD_EINVAL; + } +} + +/* Definitions for opaque data types */ + +typedef struct NBDRequest NBDRequest; + +struct NBDRequest { + QSIMPLEQ_ENTRY(NBDRequest) entry; + NBDClient *client; + uint8_t *data; +}; + +struct NBDExport { + int refcount; + void (*close)(NBDExport *exp); + + BlockBackend *blk; + char *name; + off_t dev_offset; + off_t size; + uint32_t nbdflags; + QTAILQ_HEAD(, NBDClient) clients; + QTAILQ_ENTRY(NBDExport) next; + + AioContext *ctx; + + Notifier eject_notifier; +}; + +static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); + +struct NBDClient { + int refcount; + void (*close)(NBDClient *client); + + NBDExport *exp; + QCryptoTLSCreds *tlscreds; + char *tlsaclname; + QIOChannelSocket *sioc; /* The underlying data channel */ + QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */ + + Coroutine *recv_coroutine; + + CoMutex send_lock; + Coroutine *send_coroutine; + + bool can_read; + + QTAILQ_ENTRY(NBDClient) next; + int nb_requests; + bool closing; +}; + +/* That's all folks */ + +static void nbd_set_handlers(NBDClient *client); +static void nbd_unset_handlers(NBDClient *client); +static void nbd_update_can_read(NBDClient *client); + +static gboolean nbd_negotiate_continue(QIOChannel *ioc, + GIOCondition condition, + void *opaque) +{ + qemu_coroutine_enter(opaque, NULL); + return TRUE; +} + +static ssize_t nbd_negotiate_read(QIOChannel *ioc, void *buffer, size_t size) +{ + ssize_t ret; + guint watch; + + assert(qemu_in_coroutine()); + /* Negotiation are always in main loop. */ + watch = qio_channel_add_watch(ioc, + G_IO_IN, + nbd_negotiate_continue, + qemu_coroutine_self(), + NULL); + ret = read_sync(ioc, buffer, size); + g_source_remove(watch); + return ret; + +} + +static ssize_t nbd_negotiate_write(QIOChannel *ioc, void *buffer, size_t size) +{ + ssize_t ret; + guint watch; + + assert(qemu_in_coroutine()); + /* Negotiation are always in main loop. */ + watch = qio_channel_add_watch(ioc, + G_IO_OUT, + nbd_negotiate_continue, + qemu_coroutine_self(), + NULL); + ret = write_sync(ioc, buffer, size); + g_source_remove(watch); + return ret; +} + +static ssize_t nbd_negotiate_drop_sync(QIOChannel *ioc, size_t size) +{ + ssize_t ret, dropped = size; + uint8_t *buffer = g_malloc(MIN(65536, size)); + + while (size > 0) { + ret = nbd_negotiate_read(ioc, buffer, MIN(65536, size)); + if (ret < 0) { + g_free(buffer); + return ret; + } + + assert(ret <= size); + size -= ret; + } + + g_free(buffer); + return dropped; +} + +/* Basic flow for negotiation + + Server Client + Negotiate + + or + + Server Client + Negotiate #1 + Option + Negotiate #2 + + ---- + + followed by + + Server Client + Request + Response + Request + Response + ... + ... + Request (type == 2) + +*/ + +static int nbd_negotiate_send_rep(QIOChannel *ioc, uint32_t type, uint32_t opt) +{ + uint64_t magic; + uint32_t len; + + TRACE("Reply opt=%x type=%x", type, opt); + + magic = cpu_to_be64(NBD_REP_MAGIC); + if (nbd_negotiate_write(ioc, &magic, sizeof(magic)) != sizeof(magic)) { + LOG("write failed (rep magic)"); + return -EINVAL; + } + opt = cpu_to_be32(opt); + if (nbd_negotiate_write(ioc, &opt, sizeof(opt)) != sizeof(opt)) { + LOG("write failed (rep opt)"); + return -EINVAL; + } + type = cpu_to_be32(type); + if (nbd_negotiate_write(ioc, &type, sizeof(type)) != sizeof(type)) { + LOG("write failed (rep type)"); + return -EINVAL; + } + len = cpu_to_be32(0); + if (nbd_negotiate_write(ioc, &len, sizeof(len)) != sizeof(len)) { + LOG("write failed (rep data length)"); + return -EINVAL; + } + return 0; +} + +static int nbd_negotiate_send_rep_list(QIOChannel *ioc, NBDExport *exp) +{ + uint64_t magic, name_len; + uint32_t opt, type, len; + + TRACE("Advertizing export name '%s'", exp->name ? exp->name : ""); + name_len = strlen(exp->name); + magic = cpu_to_be64(NBD_REP_MAGIC); + if (nbd_negotiate_write(ioc, &magic, sizeof(magic)) != sizeof(magic)) { + LOG("write failed (magic)"); + return -EINVAL; + } + opt = cpu_to_be32(NBD_OPT_LIST); + if (nbd_negotiate_write(ioc, &opt, sizeof(opt)) != sizeof(opt)) { + LOG("write failed (opt)"); + return -EINVAL; + } + type = cpu_to_be32(NBD_REP_SERVER); + if (nbd_negotiate_write(ioc, &type, sizeof(type)) != sizeof(type)) { + LOG("write failed (reply type)"); + return -EINVAL; + } + len = cpu_to_be32(name_len + sizeof(len)); + if (nbd_negotiate_write(ioc, &len, sizeof(len)) != sizeof(len)) { + LOG("write failed (length)"); + return -EINVAL; + } + len = cpu_to_be32(name_len); + if (nbd_negotiate_write(ioc, &len, sizeof(len)) != sizeof(len)) { + LOG("write failed (length)"); + return -EINVAL; + } + if (nbd_negotiate_write(ioc, exp->name, name_len) != name_len) { + LOG("write failed (buffer)"); + return -EINVAL; + } + return 0; +} + +static int nbd_negotiate_handle_list(NBDClient *client, uint32_t length) +{ + NBDExport *exp; + + if (length) { + if (nbd_negotiate_drop_sync(client->ioc, length) != length) { + return -EIO; + } + return nbd_negotiate_send_rep(client->ioc, + NBD_REP_ERR_INVALID, NBD_OPT_LIST); + } + + /* For each export, send a NBD_REP_SERVER reply. */ + QTAILQ_FOREACH(exp, &exports, next) { + if (nbd_negotiate_send_rep_list(client->ioc, exp)) { + return -EINVAL; + } + } + /* Finish with a NBD_REP_ACK. */ + return nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, NBD_OPT_LIST); +} + +static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length) +{ + int rc = -EINVAL; + char name[256]; + + /* Client sends: + [20 .. xx] export name (length bytes) + */ + TRACE("Checking length"); + if (length > 255) { + LOG("Bad length received"); + goto fail; + } + if (nbd_negotiate_read(client->ioc, name, length) != length) { + LOG("read failed"); + goto fail; + } + name[length] = '\0'; + + TRACE("Client requested export '%s'", name); + + client->exp = nbd_export_find(name); + if (!client->exp) { + LOG("export not found"); + goto fail; + } + + QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); + nbd_export_get(client->exp); + rc = 0; +fail: + return rc; +} + + +static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, + uint32_t length) +{ + QIOChannel *ioc; + QIOChannelTLS *tioc; + struct NBDTLSHandshakeData data = { 0 }; + + TRACE("Setting up TLS"); + ioc = client->ioc; + if (length) { + if (nbd_negotiate_drop_sync(ioc, length) != length) { + return NULL; + } + nbd_negotiate_send_rep(ioc, NBD_REP_ERR_INVALID, NBD_OPT_STARTTLS); + return NULL; + } + + nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, NBD_OPT_STARTTLS); + + tioc = qio_channel_tls_new_server(ioc, + client->tlscreds, + client->tlsaclname, + NULL); + if (!tioc) { + return NULL; + } + + TRACE("Starting TLS handshake"); + data.loop = g_main_loop_new(g_main_context_default(), FALSE); + qio_channel_tls_handshake(tioc, + nbd_tls_handshake, + &data, + NULL); + + if (!data.complete) { + g_main_loop_run(data.loop); + } + g_main_loop_unref(data.loop); + if (data.error) { + object_unref(OBJECT(tioc)); + error_free(data.error); + return NULL; + } + + return QIO_CHANNEL(tioc); +} + + +static int nbd_negotiate_options(NBDClient *client) +{ + uint32_t flags; + bool fixedNewstyle = false; + + /* Client sends: + [ 0 .. 3] client flags + + [ 0 .. 7] NBD_OPTS_MAGIC + [ 8 .. 11] NBD option + [12 .. 15] Data length + ... Rest of request + + [ 0 .. 7] NBD_OPTS_MAGIC + [ 8 .. 11] Second NBD option + [12 .. 15] Data length + ... Rest of request + */ + + if (nbd_negotiate_read(client->ioc, &flags, sizeof(flags)) != + sizeof(flags)) { + LOG("read failed"); + return -EIO; + } + TRACE("Checking client flags"); + be32_to_cpus(&flags); + if (flags & NBD_FLAG_C_FIXED_NEWSTYLE) { + TRACE("Support supports fixed newstyle handshake"); + fixedNewstyle = true; + flags &= ~NBD_FLAG_C_FIXED_NEWSTYLE; + } + if (flags != 0) { + TRACE("Unknown client flags 0x%x received", flags); + return -EIO; + } + + while (1) { + int ret; + uint32_t clientflags, length; + uint64_t magic; + + if (nbd_negotiate_read(client->ioc, &magic, sizeof(magic)) != + sizeof(magic)) { + LOG("read failed"); + return -EINVAL; + } + TRACE("Checking opts magic"); + if (magic != be64_to_cpu(NBD_OPTS_MAGIC)) { + LOG("Bad magic received"); + return -EINVAL; + } + + if (nbd_negotiate_read(client->ioc, &clientflags, + sizeof(clientflags)) != sizeof(clientflags)) { + LOG("read failed"); + return -EINVAL; + } + clientflags = be32_to_cpu(clientflags); + + if (nbd_negotiate_read(client->ioc, &length, sizeof(length)) != + sizeof(length)) { + LOG("read failed"); + return -EINVAL; + } + length = be32_to_cpu(length); + + TRACE("Checking option 0x%x", clientflags); + if (client->tlscreds && + client->ioc == (QIOChannel *)client->sioc) { + QIOChannel *tioc; + if (!fixedNewstyle) { + TRACE("Unsupported option 0x%x", clientflags); + return -EINVAL; + } + switch (clientflags) { + case NBD_OPT_STARTTLS: + tioc = nbd_negotiate_handle_starttls(client, length); + if (!tioc) { + return -EIO; + } + object_unref(OBJECT(client->ioc)); + client->ioc = QIO_CHANNEL(tioc); + break; + + case NBD_OPT_EXPORT_NAME: + /* No way to return an error to client, so drop connection */ + TRACE("Option 0x%x not permitted before TLS", clientflags); + return -EINVAL; + + default: + TRACE("Option 0x%x not permitted before TLS", clientflags); + if (nbd_negotiate_drop_sync(client->ioc, length) != length) { + return -EIO; + } + nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_TLS_REQD, + clientflags); + break; + } + } else if (fixedNewstyle) { + switch (clientflags) { + case NBD_OPT_LIST: + ret = nbd_negotiate_handle_list(client, length); + if (ret < 0) { + return ret; + } + break; + + case NBD_OPT_ABORT: + return -EINVAL; + + case NBD_OPT_EXPORT_NAME: + return nbd_negotiate_handle_export_name(client, length); + + case NBD_OPT_STARTTLS: + if (nbd_negotiate_drop_sync(client->ioc, length) != length) { + return -EIO; + } + if (client->tlscreds) { + TRACE("TLS already enabled"); + nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_INVALID, + clientflags); + } else { + TRACE("TLS not configured"); + nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_POLICY, + clientflags); + } + break; + default: + TRACE("Unsupported option 0x%x", clientflags); + if (nbd_negotiate_drop_sync(client->ioc, length) != length) { + return -EIO; + } + nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_UNSUP, + clientflags); + break; + } + } else { + /* + * If broken new-style we should drop the connection + * for anything except NBD_OPT_EXPORT_NAME + */ + switch (clientflags) { + case NBD_OPT_EXPORT_NAME: + return nbd_negotiate_handle_export_name(client, length); + + default: + TRACE("Unsupported option 0x%x", clientflags); + return -EINVAL; + } + } + } +} + +typedef struct { + NBDClient *client; + Coroutine *co; +} NBDClientNewData; + +static coroutine_fn int nbd_negotiate(NBDClientNewData *data) +{ + NBDClient *client = data->client; + char buf[8 + 8 + 8 + 128]; + int rc; + const int myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM | + NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA); + bool oldStyle; + + /* Old style negotiation header without options + [ 0 .. 7] passwd ("NBDMAGIC") + [ 8 .. 15] magic (NBD_CLIENT_MAGIC) + [16 .. 23] size + [24 .. 25] server flags (0) + [26 .. 27] export flags + [28 .. 151] reserved (0) + + New style negotiation header with options + [ 0 .. 7] passwd ("NBDMAGIC") + [ 8 .. 15] magic (NBD_OPTS_MAGIC) + [16 .. 17] server flags (0) + ....options sent.... + [18 .. 25] size + [26 .. 27] export flags + [28 .. 151] reserved (0) + */ + + qio_channel_set_blocking(client->ioc, false, NULL); + rc = -EINVAL; + + TRACE("Beginning negotiation."); + memset(buf, 0, sizeof(buf)); + memcpy(buf, "NBDMAGIC", 8); + + oldStyle = client->exp != NULL && !client->tlscreds; + if (oldStyle) { + assert ((client->exp->nbdflags & ~65535) == 0); + stq_be_p(buf + 8, NBD_CLIENT_MAGIC); + stq_be_p(buf + 16, client->exp->size); + stw_be_p(buf + 26, client->exp->nbdflags | myflags); + } else { + stq_be_p(buf + 8, NBD_OPTS_MAGIC); + stw_be_p(buf + 16, NBD_FLAG_FIXED_NEWSTYLE); + } + + if (oldStyle) { + if (client->tlscreds) { + TRACE("TLS cannot be enabled with oldstyle protocol"); + goto fail; + } + if (nbd_negotiate_write(client->ioc, buf, sizeof(buf)) != sizeof(buf)) { + LOG("write failed"); + goto fail; + } + } else { + if (nbd_negotiate_write(client->ioc, buf, 18) != 18) { + LOG("write failed"); + goto fail; + } + rc = nbd_negotiate_options(client); + if (rc != 0) { + LOG("option negotiation failed"); + goto fail; + } + + assert ((client->exp->nbdflags & ~65535) == 0); + stq_be_p(buf + 18, client->exp->size); + stw_be_p(buf + 26, client->exp->nbdflags | myflags); + if (nbd_negotiate_write(client->ioc, buf + 18, sizeof(buf) - 18) != + sizeof(buf) - 18) { + LOG("write failed"); + goto fail; + } + } + + TRACE("Negotiation succeeded."); + rc = 0; +fail: + return rc; +} + +#ifdef __linux__ + +int nbd_disconnect(int fd) +{ + ioctl(fd, NBD_CLEAR_QUE); + ioctl(fd, NBD_DISCONNECT); + ioctl(fd, NBD_CLEAR_SOCK); + return 0; +} + +#else + +int nbd_disconnect(int fd) +{ + return -ENOTSUP; +} +#endif + +static ssize_t nbd_receive_request(QIOChannel *ioc, struct nbd_request *request) +{ + uint8_t buf[NBD_REQUEST_SIZE]; + uint32_t magic; + ssize_t ret; + + ret = read_sync(ioc, buf, sizeof(buf)); + if (ret < 0) { + return ret; + } + + if (ret != sizeof(buf)) { + LOG("read failed"); + return -EINVAL; + } + + /* Request + [ 0 .. 3] magic (NBD_REQUEST_MAGIC) + [ 4 .. 7] type (0 == READ, 1 == WRITE) + [ 8 .. 15] handle + [16 .. 23] from + [24 .. 27] len + */ + + magic = be32_to_cpup((uint32_t*)buf); + request->type = be32_to_cpup((uint32_t*)(buf + 4)); + request->handle = be64_to_cpup((uint64_t*)(buf + 8)); + request->from = be64_to_cpup((uint64_t*)(buf + 16)); + request->len = be32_to_cpup((uint32_t*)(buf + 24)); + + TRACE("Got request: " + "{ magic = 0x%x, .type = %d, from = %" PRIu64" , len = %u }", + magic, request->type, request->from, request->len); + + if (magic != NBD_REQUEST_MAGIC) { + LOG("invalid magic (got 0x%x)", magic); + return -EINVAL; + } + return 0; +} + +static ssize_t nbd_send_reply(QIOChannel *ioc, struct nbd_reply *reply) +{ + uint8_t buf[NBD_REPLY_SIZE]; + ssize_t ret; + + reply->error = system_errno_to_nbd_errno(reply->error); + + TRACE("Sending response to client: { .error = %d, handle = %" PRIu64 " }", + reply->error, reply->handle); + + /* Reply + [ 0 .. 3] magic (NBD_REPLY_MAGIC) + [ 4 .. 7] error (0 == no error) + [ 7 .. 15] handle + */ + stl_be_p(buf, NBD_REPLY_MAGIC); + stl_be_p(buf + 4, reply->error); + stq_be_p(buf + 8, reply->handle); + + ret = write_sync(ioc, buf, sizeof(buf)); + if (ret < 0) { + return ret; + } + + if (ret != sizeof(buf)) { + LOG("writing to socket failed"); + return -EINVAL; + } + return 0; +} + +#define MAX_NBD_REQUESTS 16 + +void nbd_client_get(NBDClient *client) +{ + client->refcount++; +} + +void nbd_client_put(NBDClient *client) +{ + if (--client->refcount == 0) { + /* The last reference should be dropped by client->close, + * which is called by client_close. + */ + assert(client->closing); + + nbd_unset_handlers(client); + object_unref(OBJECT(client->sioc)); + object_unref(OBJECT(client->ioc)); + if (client->tlscreds) { + object_unref(OBJECT(client->tlscreds)); + } + g_free(client->tlsaclname); + if (client->exp) { + QTAILQ_REMOVE(&client->exp->clients, client, next); + nbd_export_put(client->exp); + } + g_free(client); + } +} + +static void client_close(NBDClient *client) +{ + if (client->closing) { + return; + } + + client->closing = true; + + /* Force requests to finish. They will drop their own references, + * then we'll close the socket and free the NBDClient. + */ + qio_channel_shutdown(client->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, + NULL); + + /* Also tell the client, so that they release their reference. */ + if (client->close) { + client->close(client); + } +} + +static NBDRequest *nbd_request_get(NBDClient *client) +{ + NBDRequest *req; + + assert(client->nb_requests <= MAX_NBD_REQUESTS - 1); + client->nb_requests++; + nbd_update_can_read(client); + + req = g_new0(NBDRequest, 1); + nbd_client_get(client); + req->client = client; + return req; +} + +static void nbd_request_put(NBDRequest *req) +{ + NBDClient *client = req->client; + + if (req->data) { + qemu_vfree(req->data); + } + g_free(req); + + client->nb_requests--; + nbd_update_can_read(client); + nbd_client_put(client); +} + +static void blk_aio_attached(AioContext *ctx, void *opaque) +{ + NBDExport *exp = opaque; + NBDClient *client; + + TRACE("Export %s: Attaching clients to AIO context %p\n", exp->name, ctx); + + exp->ctx = ctx; + + QTAILQ_FOREACH(client, &exp->clients, next) { + nbd_set_handlers(client); + } +} + +static void blk_aio_detach(void *opaque) +{ + NBDExport *exp = opaque; + NBDClient *client; + + TRACE("Export %s: Detaching clients from AIO context %p\n", exp->name, exp->ctx); + + QTAILQ_FOREACH(client, &exp->clients, next) { + nbd_unset_handlers(client); + } + + exp->ctx = NULL; +} + +static void nbd_eject_notifier(Notifier *n, void *data) +{ + NBDExport *exp = container_of(n, NBDExport, eject_notifier); + nbd_export_close(exp); +} + +NBDExport *nbd_export_new(BlockBackend *blk, off_t dev_offset, off_t size, + uint32_t nbdflags, void (*close)(NBDExport *), + Error **errp) +{ + NBDExport *exp = g_malloc0(sizeof(NBDExport)); + exp->refcount = 1; + QTAILQ_INIT(&exp->clients); + exp->blk = blk; + exp->dev_offset = dev_offset; + exp->nbdflags = nbdflags; + exp->size = size < 0 ? blk_getlength(blk) : size; + if (exp->size < 0) { + error_setg_errno(errp, -exp->size, + "Failed to determine the NBD export's length"); + goto fail; + } + exp->size -= exp->size % BDRV_SECTOR_SIZE; + + exp->close = close; + exp->ctx = blk_get_aio_context(blk); + blk_ref(blk); + blk_add_aio_context_notifier(blk, blk_aio_attached, blk_aio_detach, exp); + + exp->eject_notifier.notify = nbd_eject_notifier; + blk_add_remove_bs_notifier(blk, &exp->eject_notifier); + + /* + * NBD exports are used for non-shared storage migration. Make sure + * that BDRV_O_INACTIVE is cleared and the image is ready for write + * access since the export could be available before migration handover. + */ + aio_context_acquire(exp->ctx); + blk_invalidate_cache(blk, NULL); + aio_context_release(exp->ctx); + return exp; + +fail: + g_free(exp); + return NULL; +} + +NBDExport *nbd_export_find(const char *name) +{ + NBDExport *exp; + QTAILQ_FOREACH(exp, &exports, next) { + if (strcmp(name, exp->name) == 0) { + return exp; + } + } + + return NULL; +} + +void nbd_export_set_name(NBDExport *exp, const char *name) +{ + if (exp->name == name) { + return; + } + + nbd_export_get(exp); + if (exp->name != NULL) { + g_free(exp->name); + exp->name = NULL; + QTAILQ_REMOVE(&exports, exp, next); + nbd_export_put(exp); + } + if (name != NULL) { + nbd_export_get(exp); + exp->name = g_strdup(name); + QTAILQ_INSERT_TAIL(&exports, exp, next); + } + nbd_export_put(exp); +} + +void nbd_export_close(NBDExport *exp) +{ + NBDClient *client, *next; + + nbd_export_get(exp); + QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) { + client_close(client); + } + nbd_export_set_name(exp, NULL); + nbd_export_put(exp); +} + +void nbd_export_get(NBDExport *exp) +{ + assert(exp->refcount > 0); + exp->refcount++; +} + +void nbd_export_put(NBDExport *exp) +{ + assert(exp->refcount > 0); + if (exp->refcount == 1) { + nbd_export_close(exp); + } + + if (--exp->refcount == 0) { + assert(exp->name == NULL); + + if (exp->close) { + exp->close(exp); + } + + if (exp->blk) { + notifier_remove(&exp->eject_notifier); + blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, + blk_aio_detach, exp); + blk_unref(exp->blk); + exp->blk = NULL; + } + + g_free(exp); + } +} + +BlockBackend *nbd_export_get_blockdev(NBDExport *exp) +{ + return exp->blk; +} + +void nbd_export_close_all(void) +{ + NBDExport *exp, *next; + + QTAILQ_FOREACH_SAFE(exp, &exports, next, next) { + nbd_export_close(exp); + } +} + +static ssize_t nbd_co_send_reply(NBDRequest *req, struct nbd_reply *reply, + int len) +{ + NBDClient *client = req->client; + ssize_t rc, ret; + + g_assert(qemu_in_coroutine()); + qemu_co_mutex_lock(&client->send_lock); + client->send_coroutine = qemu_coroutine_self(); + nbd_set_handlers(client); + + if (!len) { + rc = nbd_send_reply(client->ioc, reply); + } else { + qio_channel_set_cork(client->ioc, true); + rc = nbd_send_reply(client->ioc, reply); + if (rc >= 0) { + ret = write_sync(client->ioc, req->data, len); + if (ret != len) { + rc = -EIO; + } + } + qio_channel_set_cork(client->ioc, false); + } + + client->send_coroutine = NULL; + nbd_set_handlers(client); + qemu_co_mutex_unlock(&client->send_lock); + return rc; +} + +static ssize_t nbd_co_receive_request(NBDRequest *req, struct nbd_request *request) +{ + NBDClient *client = req->client; + uint32_t command; + ssize_t rc; + + g_assert(qemu_in_coroutine()); + client->recv_coroutine = qemu_coroutine_self(); + nbd_update_can_read(client); + + rc = nbd_receive_request(client->ioc, request); + if (rc < 0) { + if (rc != -EAGAIN) { + rc = -EIO; + } + goto out; + } + + if ((request->from + request->len) < request->from) { + LOG("integer overflow detected! " + "you're probably being attacked"); + rc = -EINVAL; + goto out; + } + + TRACE("Decoding type"); + + command = request->type & NBD_CMD_MASK_COMMAND; + if (command == NBD_CMD_READ || command == NBD_CMD_WRITE) { + if (request->len > NBD_MAX_BUFFER_SIZE) { + LOG("len (%u) is larger than max len (%u)", + request->len, NBD_MAX_BUFFER_SIZE); + rc = -EINVAL; + goto out; + } + + req->data = blk_try_blockalign(client->exp->blk, request->len); + if (req->data == NULL) { + rc = -ENOMEM; + goto out; + } + } + if (command == NBD_CMD_WRITE) { + TRACE("Reading %u byte(s)", request->len); + + if (read_sync(client->ioc, req->data, request->len) != request->len) { + LOG("reading from socket failed"); + rc = -EIO; + goto out; + } + } + rc = 0; + +out: + client->recv_coroutine = NULL; + nbd_update_can_read(client); + + return rc; +} + +static void nbd_trip(void *opaque) +{ + NBDClient *client = opaque; + NBDExport *exp = client->exp; + NBDRequest *req; + struct nbd_request request; + struct nbd_reply reply; + ssize_t ret; + uint32_t command; + + TRACE("Reading request."); + if (client->closing) { + return; + } + + req = nbd_request_get(client); + ret = nbd_co_receive_request(req, &request); + if (ret == -EAGAIN) { + goto done; + } + if (ret == -EIO) { + goto out; + } + + reply.handle = request.handle; + reply.error = 0; + + if (ret < 0) { + reply.error = -ret; + goto error_reply; + } + command = request.type & NBD_CMD_MASK_COMMAND; + if (command != NBD_CMD_DISC && (request.from + request.len) > exp->size) { + LOG("From: %" PRIu64 ", Len: %u, Size: %" PRIu64 + ", Offset: %" PRIu64 "\n", + request.from, request.len, + (uint64_t)exp->size, (uint64_t)exp->dev_offset); + LOG("requested operation past EOF--bad client?"); + goto invalid_request; + } + + if (client->closing) { + /* + * The client may be closed when we are blocked in + * nbd_co_receive_request() + */ + goto done; + } + + switch (command) { + case NBD_CMD_READ: + TRACE("Request type is READ"); + + if (request.type & NBD_CMD_FLAG_FUA) { + ret = blk_co_flush(exp->blk); + if (ret < 0) { + LOG("flush failed"); + reply.error = -ret; + goto error_reply; + } + } + + ret = blk_pread(exp->blk, request.from + exp->dev_offset, + req->data, request.len); + if (ret < 0) { + LOG("reading from file failed"); + reply.error = -ret; + goto error_reply; + } + + TRACE("Read %u byte(s)", request.len); + if (nbd_co_send_reply(req, &reply, request.len) < 0) + goto out; + break; + case NBD_CMD_WRITE: + TRACE("Request type is WRITE"); + + if (exp->nbdflags & NBD_FLAG_READ_ONLY) { + TRACE("Server is read-only, return error"); + reply.error = EROFS; + goto error_reply; + } + + TRACE("Writing to device"); + + ret = blk_pwrite(exp->blk, request.from + exp->dev_offset, + req->data, request.len); + if (ret < 0) { + LOG("writing to file failed"); + reply.error = -ret; + goto error_reply; + } + + if (request.type & NBD_CMD_FLAG_FUA) { + ret = blk_co_flush(exp->blk); + if (ret < 0) { + LOG("flush failed"); + reply.error = -ret; + goto error_reply; + } + } + + if (nbd_co_send_reply(req, &reply, 0) < 0) { + goto out; + } + break; + case NBD_CMD_DISC: + TRACE("Request type is DISCONNECT"); + errno = 0; + goto out; + case NBD_CMD_FLUSH: + TRACE("Request type is FLUSH"); + + ret = blk_co_flush(exp->blk); + if (ret < 0) { + LOG("flush failed"); + reply.error = -ret; + } + if (nbd_co_send_reply(req, &reply, 0) < 0) { + goto out; + } + break; + case NBD_CMD_TRIM: + TRACE("Request type is TRIM"); + ret = blk_co_discard(exp->blk, (request.from + exp->dev_offset) + / BDRV_SECTOR_SIZE, + request.len / BDRV_SECTOR_SIZE); + if (ret < 0) { + LOG("discard failed"); + reply.error = -ret; + } + if (nbd_co_send_reply(req, &reply, 0) < 0) { + goto out; + } + break; + default: + LOG("invalid request type (%u) received", request.type); + invalid_request: + reply.error = EINVAL; + error_reply: + if (nbd_co_send_reply(req, &reply, 0) < 0) { + goto out; + } + break; + } + + TRACE("Request/Reply complete"); + +done: + nbd_request_put(req); + return; + +out: + nbd_request_put(req); + client_close(client); +} + +static void nbd_read(void *opaque) +{ + NBDClient *client = opaque; + + if (client->recv_coroutine) { + qemu_coroutine_enter(client->recv_coroutine, NULL); + } else { + qemu_coroutine_enter(qemu_coroutine_create(nbd_trip), client); + } +} + +static void nbd_restart_write(void *opaque) +{ + NBDClient *client = opaque; + + qemu_coroutine_enter(client->send_coroutine, NULL); +} + +static void nbd_set_handlers(NBDClient *client) +{ + if (client->exp && client->exp->ctx) { + aio_set_fd_handler(client->exp->ctx, client->sioc->fd, + true, + client->can_read ? nbd_read : NULL, + client->send_coroutine ? nbd_restart_write : NULL, + client); + } +} + +static void nbd_unset_handlers(NBDClient *client) +{ + if (client->exp && client->exp->ctx) { + aio_set_fd_handler(client->exp->ctx, client->sioc->fd, + true, NULL, NULL, NULL); + } +} + +static void nbd_update_can_read(NBDClient *client) +{ + bool can_read = client->recv_coroutine || + client->nb_requests < MAX_NBD_REQUESTS; + + if (can_read != client->can_read) { + client->can_read = can_read; + nbd_set_handlers(client); + + /* There is no need to invoke aio_notify(), since aio_set_fd_handler() + * in nbd_set_handlers() will have taken care of that */ + } +} + +static coroutine_fn void nbd_co_client_start(void *opaque) +{ + NBDClientNewData *data = opaque; + NBDClient *client = data->client; + NBDExport *exp = client->exp; + + if (exp) { + nbd_export_get(exp); + } + if (nbd_negotiate(data)) { + client_close(client); + goto out; + } + qemu_co_mutex_init(&client->send_lock); + nbd_set_handlers(client); + + if (exp) { + QTAILQ_INSERT_TAIL(&exp->clients, client, next); + } +out: + g_free(data); +} + +void nbd_client_new(NBDExport *exp, + QIOChannelSocket *sioc, + QCryptoTLSCreds *tlscreds, + const char *tlsaclname, + void (*close_fn)(NBDClient *)) +{ + NBDClient *client; + NBDClientNewData *data = g_new(NBDClientNewData, 1); + + client = g_malloc0(sizeof(NBDClient)); + client->refcount = 1; + client->exp = exp; + client->tlscreds = tlscreds; + if (tlscreds) { + object_ref(OBJECT(client->tlscreds)); + } + client->tlsaclname = g_strdup(tlsaclname); + client->sioc = sioc; + object_ref(OBJECT(client->sioc)); + client->ioc = QIO_CHANNEL(sioc); + object_ref(OBJECT(client->ioc)); + client->can_read = true; + client->close = close_fn; + + data->client = client; + data->co = qemu_coroutine_create(nbd_co_client_start); + qemu_coroutine_enter(data->co, data); +} diff --git a/qemu/net/Makefile.objs b/qemu/net/Makefile.objs index ec19cb31d..b7c22fddb 100644 --- a/qemu/net/Makefile.objs +++ b/qemu/net/Makefile.objs @@ -13,3 +13,6 @@ common-obj-$(CONFIG_HAIKU) += tap-haiku.o common-obj-$(CONFIG_SLIRP) += slirp.o common-obj-$(CONFIG_VDE) += vde.o common-obj-$(CONFIG_NETMAP) += netmap.o +common-obj-y += filter.o +common-obj-y += filter-buffer.o +common-obj-y += filter-mirror.o diff --git a/qemu/net/checksum.c b/qemu/net/checksum.c index 14c08550e..d0fa424cc 100644 --- a/qemu/net/checksum.c +++ b/qemu/net/checksum.c @@ -15,6 +15,7 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "net/checksum.h" @@ -59,6 +60,11 @@ void net_checksum_calculate(uint8_t *data, int length) int hlen, plen, proto, csum_offset; uint16_t csum; + /* Ensure data has complete L2 & L3 headers. */ + if (length < 14 + 20) { + return; + } + if ((data[14] & 0xf0) != 0x40) return; /* not IPv4 */ hlen = (data[14] & 0x0f) * 4; @@ -76,8 +82,9 @@ void net_checksum_calculate(uint8_t *data, int length) return; } - if (plen < csum_offset+2) - return; + if (plen < csum_offset + 2 || 14 + hlen + plen > length) { + return; + } data[14+hlen+csum_offset] = 0; data[14+hlen+csum_offset+1] = 0; diff --git a/qemu/net/dump.c b/qemu/net/dump.c index 02c8064be..41f7673ef 100644 --- a/qemu/net/dump.c +++ b/qemu/net/dump.c @@ -22,15 +22,18 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "clients.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/error-report.h" +#include "qemu/iov.h" #include "qemu/log.h" #include "qemu/timer.h" -#include "hub.h" +#include "qapi/visitor.h" +#include "net/filter.h" typedef struct DumpState { - NetClientState nc; int64_t start_ts; int fd; int pcap_caplen; @@ -57,28 +60,33 @@ struct pcap_sf_pkthdr { uint32_t len; }; -static ssize_t dump_receive(NetClientState *nc, const uint8_t *buf, size_t size) +static ssize_t dump_receive_iov(DumpState *s, const struct iovec *iov, int cnt) { - DumpState *s = DO_UPCAST(DumpState, nc, nc); struct pcap_sf_pkthdr hdr; int64_t ts; int caplen; + size_t size = iov_size(iov, cnt); + struct iovec dumpiov[cnt + 1]; /* Early return in case of previous error. */ if (s->fd < 0) { return size; } - ts = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 1000000, get_ticks_per_sec()); + ts = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL); caplen = size > s->pcap_caplen ? s->pcap_caplen : size; hdr.ts.tv_sec = ts / 1000000 + s->start_ts; hdr.ts.tv_usec = ts % 1000000; hdr.caplen = caplen; hdr.len = size; - if (write(s->fd, &hdr, sizeof(hdr)) != sizeof(hdr) || - write(s->fd, buf, caplen) != caplen) { - qemu_log("-net dump write error - stop dump\n"); + + dumpiov[0].iov_base = &hdr; + dumpiov[0].iov_len = sizeof(hdr); + cnt = iov_copy(&dumpiov[1], cnt, iov, cnt, 0, caplen); + + if (writev(s->fd, dumpiov, cnt + 1) != sizeof(hdr) + caplen) { + error_report("network dump write error - stopping dump"); close(s->fd); s->fd = -1; } @@ -86,27 +94,16 @@ static ssize_t dump_receive(NetClientState *nc, const uint8_t *buf, size_t size) return size; } -static void dump_cleanup(NetClientState *nc) +static void dump_cleanup(DumpState *s) { - DumpState *s = DO_UPCAST(DumpState, nc, nc); - close(s->fd); + s->fd = -1; } -static NetClientInfo net_dump_info = { - .type = NET_CLIENT_OPTIONS_KIND_DUMP, - .size = sizeof(DumpState), - .receive = dump_receive, - .cleanup = dump_cleanup, -}; - -static int net_dump_init(NetClientState *peer, const char *device, - const char *name, const char *filename, int len, - Error **errp) +static int net_dump_state_init(DumpState *s, const char *filename, + int len, Error **errp) { struct pcap_file_hdr hdr; - NetClientState *nc; - DumpState *s; struct tm tm; int fd; @@ -130,13 +127,6 @@ static int net_dump_init(NetClientState *peer, const char *device, return -1; } - nc = qemu_new_net_client(&net_dump_info, peer, device, name); - - snprintf(nc->info_str, sizeof(nc->info_str), - "dump to %s (len=%d)", filename, len); - - s = DO_UPCAST(DumpState, nc, nc); - s->fd = fd; s->pcap_caplen = len; @@ -146,16 +136,61 @@ static int net_dump_init(NetClientState *peer, const char *device, return 0; } +/* Dumping via VLAN netclient */ + +struct DumpNetClient { + NetClientState nc; + DumpState ds; +}; +typedef struct DumpNetClient DumpNetClient; + +static ssize_t dumpclient_receive(NetClientState *nc, const uint8_t *buf, + size_t size) +{ + DumpNetClient *dc = DO_UPCAST(DumpNetClient, nc, nc); + struct iovec iov = { + .iov_base = (void *)buf, + .iov_len = size + }; + + return dump_receive_iov(&dc->ds, &iov, 1); +} + +static ssize_t dumpclient_receive_iov(NetClientState *nc, + const struct iovec *iov, int cnt) +{ + DumpNetClient *dc = DO_UPCAST(DumpNetClient, nc, nc); + + return dump_receive_iov(&dc->ds, iov, cnt); +} + +static void dumpclient_cleanup(NetClientState *nc) +{ + DumpNetClient *dc = DO_UPCAST(DumpNetClient, nc, nc); + + dump_cleanup(&dc->ds); +} + +static NetClientInfo net_dump_info = { + .type = NET_CLIENT_OPTIONS_KIND_DUMP, + .size = sizeof(DumpNetClient), + .receive = dumpclient_receive, + .receive_iov = dumpclient_receive_iov, + .cleanup = dumpclient_cleanup, +}; + int net_init_dump(const NetClientOptions *opts, const char *name, NetClientState *peer, Error **errp) { - int len; + int len, rc; const char *file; char def_file[128]; const NetdevDumpOptions *dump; + NetClientState *nc; + DumpNetClient *dnc; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP); - dump = opts->dump; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_DUMP); + dump = opts->u.dump.data; assert(peer); @@ -182,5 +217,148 @@ int net_init_dump(const NetClientOptions *opts, const char *name, len = 65536; } - return net_dump_init(peer, "dump", name, file, len, errp); + nc = qemu_new_net_client(&net_dump_info, peer, "dump", name); + snprintf(nc->info_str, sizeof(nc->info_str), + "dump to %s (len=%d)", file, len); + + dnc = DO_UPCAST(DumpNetClient, nc, nc); + rc = net_dump_state_init(&dnc->ds, file, len, errp); + if (rc) { + qemu_del_net_client(nc); + } + return rc; +} + +/* Dumping via filter */ + +#define TYPE_FILTER_DUMP "filter-dump" + +#define FILTER_DUMP(obj) \ + OBJECT_CHECK(NetFilterDumpState, (obj), TYPE_FILTER_DUMP) + +struct NetFilterDumpState { + NetFilterState nfs; + DumpState ds; + char *filename; + uint32_t maxlen; +}; +typedef struct NetFilterDumpState NetFilterDumpState; + +static ssize_t filter_dump_receive_iov(NetFilterState *nf, NetClientState *sndr, + unsigned flags, const struct iovec *iov, + int iovcnt, NetPacketSent *sent_cb) +{ + NetFilterDumpState *nfds = FILTER_DUMP(nf); + + dump_receive_iov(&nfds->ds, iov, iovcnt); + return 0; } + +static void filter_dump_cleanup(NetFilterState *nf) +{ + NetFilterDumpState *nfds = FILTER_DUMP(nf); + + dump_cleanup(&nfds->ds); +} + +static void filter_dump_setup(NetFilterState *nf, Error **errp) +{ + NetFilterDumpState *nfds = FILTER_DUMP(nf); + + if (!nfds->filename) { + error_setg(errp, "dump filter needs 'file' property set!"); + return; + } + + net_dump_state_init(&nfds->ds, nfds->filename, nfds->maxlen, errp); +} + +static void filter_dump_get_maxlen(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + NetFilterDumpState *nfds = FILTER_DUMP(obj); + uint32_t value = nfds->maxlen; + + visit_type_uint32(v, name, &value, errp); +} + +static void filter_dump_set_maxlen(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + NetFilterDumpState *nfds = FILTER_DUMP(obj); + Error *local_err = NULL; + uint32_t value; + + visit_type_uint32(v, name, &value, &local_err); + if (local_err) { + goto out; + } + if (value == 0) { + error_setg(&local_err, "Property '%s.%s' doesn't take value '%u'", + object_get_typename(obj), name, value); + goto out; + } + nfds->maxlen = value; + +out: + error_propagate(errp, local_err); +} + +static char *file_dump_get_filename(Object *obj, Error **errp) +{ + NetFilterDumpState *nfds = FILTER_DUMP(obj); + + return g_strdup(nfds->filename); +} + +static void file_dump_set_filename(Object *obj, const char *value, Error **errp) +{ + NetFilterDumpState *nfds = FILTER_DUMP(obj); + + g_free(nfds->filename); + nfds->filename = g_strdup(value); +} + +static void filter_dump_instance_init(Object *obj) +{ + NetFilterDumpState *nfds = FILTER_DUMP(obj); + + nfds->maxlen = 65536; + + object_property_add(obj, "maxlen", "int", filter_dump_get_maxlen, + filter_dump_set_maxlen, NULL, NULL, NULL); + object_property_add_str(obj, "file", file_dump_get_filename, + file_dump_set_filename, NULL); +} + +static void filter_dump_instance_finalize(Object *obj) +{ + NetFilterDumpState *nfds = FILTER_DUMP(obj); + + g_free(nfds->filename); +} + +static void filter_dump_class_init(ObjectClass *oc, void *data) +{ + NetFilterClass *nfc = NETFILTER_CLASS(oc); + + nfc->setup = filter_dump_setup; + nfc->cleanup = filter_dump_cleanup; + nfc->receive_iov = filter_dump_receive_iov; +} + +static const TypeInfo filter_dump_info = { + .name = TYPE_FILTER_DUMP, + .parent = TYPE_NETFILTER, + .class_init = filter_dump_class_init, + .instance_init = filter_dump_instance_init, + .instance_finalize = filter_dump_instance_finalize, + .instance_size = sizeof(NetFilterDumpState), +}; + +static void filter_dump_register_types(void) +{ + type_register_static(&filter_dump_info); +} + +type_init(filter_dump_register_types); diff --git a/qemu/net/eth.c b/qemu/net/eth.c index 7c61132cb..7e32d274c 100644 --- a/qemu/net/eth.c +++ b/qemu/net/eth.c @@ -15,6 +15,7 @@ * */ +#include "qemu/osdep.h" #include "net/eth.h" #include "net/checksum.h" #include "qemu-common.h" diff --git a/qemu/net/filter-buffer.c b/qemu/net/filter-buffer.c new file mode 100644 index 000000000..cc6bd9444 --- /dev/null +++ b/qemu/net/filter-buffer.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2015 FUJITSU LIMITED + * Author: Yang Hongyang + * + * 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 "net/filter.h" +#include "net/queue.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "qemu/timer.h" +#include "qemu/iov.h" +#include "qapi/qmp/qerror.h" +#include "qapi-visit.h" +#include "qom/object.h" + +#define TYPE_FILTER_BUFFER "filter-buffer" + +#define FILTER_BUFFER(obj) \ + OBJECT_CHECK(FilterBufferState, (obj), TYPE_FILTER_BUFFER) + +typedef struct FilterBufferState { + NetFilterState parent_obj; + + NetQueue *incoming_queue; + uint32_t interval; + QEMUTimer release_timer; +} FilterBufferState; + +static void filter_buffer_flush(NetFilterState *nf) +{ + FilterBufferState *s = FILTER_BUFFER(nf); + + if (!qemu_net_queue_flush(s->incoming_queue)) { + /* Unable to empty the queue, purge remaining packets */ + qemu_net_queue_purge(s->incoming_queue, nf->netdev); + } +} + +static void filter_buffer_release_timer(void *opaque) +{ + NetFilterState *nf = opaque; + FilterBufferState *s = FILTER_BUFFER(nf); + + /* + * Note: filter_buffer_flush() drops packets that can't be sent + * TODO: We should leave them queued. But currently there's no way + * for the next filter or receiver to notify us that it can receive + * more packets. + */ + filter_buffer_flush(nf); + /* Timer rearmed to fire again in s->interval microseconds. */ + timer_mod(&s->release_timer, + qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + s->interval); +} + +/* filter APIs */ +static ssize_t filter_buffer_receive_iov(NetFilterState *nf, + NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + NetPacketSent *sent_cb) +{ + FilterBufferState *s = FILTER_BUFFER(nf); + + /* + * We return size when buffer a packet, the sender will take it as + * a already sent packet, so sent_cb should not be called later. + * + * FIXME: Even if the guest can't receive packets for some reasons, + * the filter can still accept packets until its internal queue is full. + * For example: + * For some reason, receiver could not receive more packets + * (.can_receive() returns zero). Without a filter, at most one packet + * will be queued in incoming queue and sender's poll will be disabled + * unit its sent_cb() was called. With a filter, it will keep receiving + * the packets without caring about the receiver. This is suboptimal. + * May need more thoughts (e.g keeping sent_cb). + */ + qemu_net_queue_append_iov(s->incoming_queue, sender, flags, + iov, iovcnt, NULL); + return iov_size(iov, iovcnt); +} + +static void filter_buffer_cleanup(NetFilterState *nf) +{ + FilterBufferState *s = FILTER_BUFFER(nf); + + if (s->interval) { + timer_del(&s->release_timer); + } + + /* flush packets */ + if (s->incoming_queue) { + filter_buffer_flush(nf); + g_free(s->incoming_queue); + } +} + +static void filter_buffer_setup_timer(NetFilterState *nf) +{ + FilterBufferState *s = FILTER_BUFFER(nf); + + if (s->interval) { + timer_init_us(&s->release_timer, QEMU_CLOCK_VIRTUAL, + filter_buffer_release_timer, nf); + /* Timer armed to fire in s->interval microseconds. */ + timer_mod(&s->release_timer, + qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + s->interval); + } +} + +static void filter_buffer_setup(NetFilterState *nf, Error **errp) +{ + FilterBufferState *s = FILTER_BUFFER(nf); + + /* + * We may want to accept zero interval when VM FT solutions like MC + * or COLO use this filter to release packets on demand. + */ + if (!s->interval) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "interval", + "a non-zero interval"); + return; + } + + s->incoming_queue = qemu_new_net_queue(qemu_netfilter_pass_to_next, nf); + filter_buffer_setup_timer(nf); +} + +static void filter_buffer_status_changed(NetFilterState *nf, Error **errp) +{ + FilterBufferState *s = FILTER_BUFFER(nf); + + if (!nf->on) { + if (s->interval) { + timer_del(&s->release_timer); + } + filter_buffer_flush(nf); + } else { + filter_buffer_setup_timer(nf); + } +} + +static void filter_buffer_class_init(ObjectClass *oc, void *data) +{ + NetFilterClass *nfc = NETFILTER_CLASS(oc); + + nfc->setup = filter_buffer_setup; + nfc->cleanup = filter_buffer_cleanup; + nfc->receive_iov = filter_buffer_receive_iov; + nfc->status_changed = filter_buffer_status_changed; +} + +static void filter_buffer_get_interval(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + FilterBufferState *s = FILTER_BUFFER(obj); + uint32_t value = s->interval; + + visit_type_uint32(v, name, &value, errp); +} + +static void filter_buffer_set_interval(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + FilterBufferState *s = FILTER_BUFFER(obj); + Error *local_err = NULL; + uint32_t value; + + visit_type_uint32(v, name, &value, &local_err); + if (local_err) { + goto out; + } + if (!value) { + error_setg(&local_err, "Property '%s.%s' requires a positive value", + object_get_typename(obj), name); + goto out; + } + s->interval = value; + +out: + error_propagate(errp, local_err); +} + +static void filter_buffer_init(Object *obj) +{ + object_property_add(obj, "interval", "int", + filter_buffer_get_interval, + filter_buffer_set_interval, NULL, NULL, NULL); +} + +static const TypeInfo filter_buffer_info = { + .name = TYPE_FILTER_BUFFER, + .parent = TYPE_NETFILTER, + .class_init = filter_buffer_class_init, + .instance_init = filter_buffer_init, + .instance_size = sizeof(FilterBufferState), +}; + +static void register_types(void) +{ + type_register_static(&filter_buffer_info); +} + +type_init(register_types); diff --git a/qemu/net/filter-mirror.c b/qemu/net/filter-mirror.c new file mode 100644 index 000000000..c0c4dc60b --- /dev/null +++ b/qemu/net/filter-mirror.c @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. + * Copyright (c) 2016 FUJITSU LIMITED + * Copyright (c) 2016 Intel Corporation + * + * Author: Zhang Chen + * + * 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 "net/filter.h" +#include "net/net.h" +#include "qemu-common.h" +#include "qapi/error.h" +#include "qapi/qmp/qerror.h" +#include "qapi-visit.h" +#include "qom/object.h" +#include "qemu/main-loop.h" +#include "qemu/error-report.h" +#include "trace.h" +#include "sysemu/char.h" +#include "qemu/iov.h" +#include "qemu/sockets.h" + +#define FILTER_MIRROR(obj) \ + OBJECT_CHECK(MirrorState, (obj), TYPE_FILTER_MIRROR) + +#define FILTER_REDIRECTOR(obj) \ + OBJECT_CHECK(MirrorState, (obj), TYPE_FILTER_REDIRECTOR) + +#define TYPE_FILTER_MIRROR "filter-mirror" +#define TYPE_FILTER_REDIRECTOR "filter-redirector" +#define REDIRECTOR_MAX_LEN NET_BUFSIZE + +typedef struct MirrorState { + NetFilterState parent_obj; + char *indev; + char *outdev; + CharDriverState *chr_in; + CharDriverState *chr_out; + int state; /* 0 = getting length, 1 = getting data */ + unsigned int index; + unsigned int packet_len; + uint8_t buf[REDIRECTOR_MAX_LEN]; +} MirrorState; + +static int filter_mirror_send(CharDriverState *chr_out, + const struct iovec *iov, + int iovcnt) +{ + int ret = 0; + ssize_t size = 0; + uint32_t len = 0; + char *buf; + + size = iov_size(iov, iovcnt); + if (!size) { + return 0; + } + + len = htonl(size); + ret = qemu_chr_fe_write_all(chr_out, (uint8_t *)&len, sizeof(len)); + if (ret != sizeof(len)) { + goto err; + } + + buf = g_malloc(size); + iov_to_buf(iov, iovcnt, 0, buf, size); + ret = qemu_chr_fe_write_all(chr_out, (uint8_t *)buf, size); + g_free(buf); + if (ret != size) { + goto err; + } + + return 0; + +err: + return ret < 0 ? ret : -EIO; +} + +static void +redirector_to_filter(NetFilterState *nf, const uint8_t *buf, int len) +{ + struct iovec iov = { + .iov_base = (void *)buf, + .iov_len = len, + }; + + if (nf->direction == NET_FILTER_DIRECTION_ALL || + nf->direction == NET_FILTER_DIRECTION_TX) { + qemu_netfilter_pass_to_next(nf->netdev, 0, &iov, 1, nf); + } + + if (nf->direction == NET_FILTER_DIRECTION_ALL || + nf->direction == NET_FILTER_DIRECTION_RX) { + qemu_netfilter_pass_to_next(nf->netdev->peer, 0, &iov, 1, nf); + } +} + +static int redirector_chr_can_read(void *opaque) +{ + return REDIRECTOR_MAX_LEN; +} + +static void redirector_chr_read(void *opaque, const uint8_t *buf, int size) +{ + NetFilterState *nf = opaque; + MirrorState *s = FILTER_REDIRECTOR(nf); + unsigned int l; + + while (size > 0) { + /* reassemble a packet from the network */ + switch (s->state) { /* 0 = getting length, 1 = getting data */ + case 0: + l = 4 - s->index; + if (l > size) { + l = size; + } + memcpy(s->buf + s->index, buf, l); + buf += l; + size -= l; + s->index += l; + if (s->index == 4) { + /* got length */ + s->packet_len = ntohl(*(uint32_t *)s->buf); + s->index = 0; + s->state = 1; + } + break; + case 1: + l = s->packet_len - s->index; + if (l > size) { + l = size; + } + if (s->index + l <= sizeof(s->buf)) { + memcpy(s->buf + s->index, buf, l); + } else { + error_report("serious error: oversized packet received."); + s->index = s->state = 0; + qemu_chr_add_handlers(s->chr_in, NULL, NULL, NULL, NULL); + return; + } + + s->index += l; + buf += l; + size -= l; + if (s->index >= s->packet_len) { + s->index = 0; + s->state = 0; + redirector_to_filter(nf, s->buf, s->packet_len); + } + break; + } + } +} + +static void redirector_chr_event(void *opaque, int event) +{ + NetFilterState *nf = opaque; + MirrorState *s = FILTER_REDIRECTOR(nf); + + switch (event) { + case CHR_EVENT_CLOSED: + qemu_chr_add_handlers(s->chr_in, NULL, NULL, NULL, NULL); + break; + default: + break; + } +} + +static ssize_t filter_mirror_receive_iov(NetFilterState *nf, + NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + NetPacketSent *sent_cb) +{ + MirrorState *s = FILTER_MIRROR(nf); + int ret; + + ret = filter_mirror_send(s->chr_out, iov, iovcnt); + if (ret) { + error_report("filter_mirror_send failed(%s)", strerror(-ret)); + } + + /* + * we don't hope this error interrupt the normal + * path of net packet, so we always return zero. + */ + return 0; +} + +static ssize_t filter_redirector_receive_iov(NetFilterState *nf, + NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + NetPacketSent *sent_cb) +{ + MirrorState *s = FILTER_REDIRECTOR(nf); + int ret; + + if (s->chr_out) { + ret = filter_mirror_send(s->chr_out, iov, iovcnt); + if (ret) { + error_report("filter_mirror_send failed(%s)", strerror(-ret)); + } + return iov_size(iov, iovcnt); + } else { + return 0; + } +} + +static void filter_mirror_cleanup(NetFilterState *nf) +{ + MirrorState *s = FILTER_MIRROR(nf); + + if (s->chr_out) { + qemu_chr_fe_release(s->chr_out); + } +} + +static void filter_redirector_cleanup(NetFilterState *nf) +{ + MirrorState *s = FILTER_REDIRECTOR(nf); + + if (s->chr_in) { + qemu_chr_add_handlers(s->chr_in, NULL, NULL, NULL, NULL); + qemu_chr_fe_release(s->chr_in); + } + if (s->chr_out) { + qemu_chr_fe_release(s->chr_out); + } +} + +static void filter_mirror_setup(NetFilterState *nf, Error **errp) +{ + MirrorState *s = FILTER_MIRROR(nf); + + if (!s->outdev) { + error_setg(errp, "filter filter mirror needs 'outdev' " + "property set"); + return; + } + + s->chr_out = qemu_chr_find(s->outdev); + if (s->chr_out == NULL) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", s->outdev); + return; + } + + if (qemu_chr_fe_claim(s->chr_out) != 0) { + error_setg(errp, QERR_DEVICE_IN_USE, s->outdev); + return; + } +} + +static void filter_redirector_setup(NetFilterState *nf, Error **errp) +{ + MirrorState *s = FILTER_REDIRECTOR(nf); + + if (!s->indev && !s->outdev) { + error_setg(errp, "filter redirector needs 'indev' or " + "'outdev' at least one property set"); + return; + } else if (s->indev && s->outdev) { + if (!strcmp(s->indev, s->outdev)) { + error_setg(errp, "'indev' and 'outdev' could not be same " + "for filter redirector"); + return; + } + } + + s->state = s->index = 0; + + if (s->indev) { + s->chr_in = qemu_chr_find(s->indev); + if (s->chr_in == NULL) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "IN Device '%s' not found", s->indev); + return; + } + + qemu_chr_fe_claim_no_fail(s->chr_in); + qemu_chr_add_handlers(s->chr_in, redirector_chr_can_read, + redirector_chr_read, redirector_chr_event, nf); + } + + if (s->outdev) { + s->chr_out = qemu_chr_find(s->outdev); + if (s->chr_out == NULL) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "OUT Device '%s' not found", s->outdev); + return; + } + qemu_chr_fe_claim_no_fail(s->chr_out); + } +} + +static void filter_mirror_class_init(ObjectClass *oc, void *data) +{ + NetFilterClass *nfc = NETFILTER_CLASS(oc); + + nfc->setup = filter_mirror_setup; + nfc->cleanup = filter_mirror_cleanup; + nfc->receive_iov = filter_mirror_receive_iov; +} + +static void filter_redirector_class_init(ObjectClass *oc, void *data) +{ + NetFilterClass *nfc = NETFILTER_CLASS(oc); + + nfc->setup = filter_redirector_setup; + nfc->cleanup = filter_redirector_cleanup; + nfc->receive_iov = filter_redirector_receive_iov; +} + +static char *filter_redirector_get_indev(Object *obj, Error **errp) +{ + MirrorState *s = FILTER_REDIRECTOR(obj); + + return g_strdup(s->indev); +} + +static void +filter_redirector_set_indev(Object *obj, const char *value, Error **errp) +{ + MirrorState *s = FILTER_REDIRECTOR(obj); + + g_free(s->indev); + s->indev = g_strdup(value); +} + +static char *filter_mirror_get_outdev(Object *obj, Error **errp) +{ + MirrorState *s = FILTER_MIRROR(obj); + + return g_strdup(s->outdev); +} + +static void +filter_mirror_set_outdev(Object *obj, const char *value, Error **errp) +{ + MirrorState *s = FILTER_MIRROR(obj); + + g_free(s->outdev); + s->outdev = g_strdup(value); + if (!s->outdev) { + error_setg(errp, "filter filter mirror needs 'outdev' " + "property set"); + return; + } +} + +static char *filter_redirector_get_outdev(Object *obj, Error **errp) +{ + MirrorState *s = FILTER_REDIRECTOR(obj); + + return g_strdup(s->outdev); +} + +static void +filter_redirector_set_outdev(Object *obj, const char *value, Error **errp) +{ + MirrorState *s = FILTER_REDIRECTOR(obj); + + g_free(s->outdev); + s->outdev = g_strdup(value); +} + +static void filter_mirror_init(Object *obj) +{ + object_property_add_str(obj, "outdev", filter_mirror_get_outdev, + filter_mirror_set_outdev, NULL); +} + +static void filter_redirector_init(Object *obj) +{ + object_property_add_str(obj, "indev", filter_redirector_get_indev, + filter_redirector_set_indev, NULL); + object_property_add_str(obj, "outdev", filter_redirector_get_outdev, + filter_redirector_set_outdev, NULL); +} + +static void filter_mirror_fini(Object *obj) +{ + MirrorState *s = FILTER_MIRROR(obj); + + g_free(s->outdev); +} + +static void filter_redirector_fini(Object *obj) +{ + MirrorState *s = FILTER_REDIRECTOR(obj); + + g_free(s->indev); + g_free(s->outdev); +} + +static const TypeInfo filter_redirector_info = { + .name = TYPE_FILTER_REDIRECTOR, + .parent = TYPE_NETFILTER, + .class_init = filter_redirector_class_init, + .instance_init = filter_redirector_init, + .instance_finalize = filter_redirector_fini, + .instance_size = sizeof(MirrorState), +}; + +static const TypeInfo filter_mirror_info = { + .name = TYPE_FILTER_MIRROR, + .parent = TYPE_NETFILTER, + .class_init = filter_mirror_class_init, + .instance_init = filter_mirror_init, + .instance_finalize = filter_mirror_fini, + .instance_size = sizeof(MirrorState), +}; + +static void register_types(void) +{ + type_register_static(&filter_mirror_info); + type_register_static(&filter_redirector_info); +} + +type_init(register_types); diff --git a/qemu/net/filter.c b/qemu/net/filter.c new file mode 100644 index 000000000..8ac79f3b7 --- /dev/null +++ b/qemu/net/filter.c @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2015 FUJITSU LIMITED + * Author: Yang Hongyang + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "qapi/qmp/qerror.h" +#include "qemu/error-report.h" + +#include "net/filter.h" +#include "net/net.h" +#include "net/vhost_net.h" +#include "qom/object_interfaces.h" +#include "qemu/iov.h" + +static inline bool qemu_can_skip_netfilter(NetFilterState *nf) +{ + return !nf->on; +} + +ssize_t qemu_netfilter_receive(NetFilterState *nf, + NetFilterDirection direction, + NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + NetPacketSent *sent_cb) +{ + if (qemu_can_skip_netfilter(nf)) { + return 0; + } + if (nf->direction == direction || + nf->direction == NET_FILTER_DIRECTION_ALL) { + return NETFILTER_GET_CLASS(OBJECT(nf))->receive_iov( + nf, sender, flags, iov, iovcnt, sent_cb); + } + + return 0; +} + +static NetFilterState *netfilter_next(NetFilterState *nf, + NetFilterDirection dir) +{ + NetFilterState *next; + + if (dir == NET_FILTER_DIRECTION_TX) { + /* forward walk through filters */ + next = QTAILQ_NEXT(nf, next); + } else { + /* reverse order */ + next = QTAILQ_PREV(nf, NetFilterHead, next); + } + + return next; +} + +ssize_t qemu_netfilter_pass_to_next(NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + void *opaque) +{ + int ret = 0; + int direction; + NetFilterState *nf = opaque; + NetFilterState *next = NULL; + + if (!sender || !sender->peer) { + /* no receiver, or sender been deleted, no need to pass it further */ + goto out; + } + + if (nf->direction == NET_FILTER_DIRECTION_ALL) { + if (sender == nf->netdev) { + /* This packet is sent by netdev itself */ + direction = NET_FILTER_DIRECTION_TX; + } else { + direction = NET_FILTER_DIRECTION_RX; + } + } else { + direction = nf->direction; + } + + next = netfilter_next(nf, direction); + while (next) { + /* + * if qemu_netfilter_pass_to_next been called, means that + * the packet has been hold by filter and has already retured size + * to the sender, so sent_cb shouldn't be called later, just + * pass NULL to next. + */ + ret = qemu_netfilter_receive(next, direction, sender, flags, iov, + iovcnt, NULL); + if (ret) { + return ret; + } + next = netfilter_next(next, direction); + } + + /* + * We have gone through all filters, pass it to receiver. + * Do the valid check again incase sender or receiver been + * deleted while we go through filters. + */ + if (sender && sender->peer) { + qemu_net_queue_send_iov(sender->peer->incoming_queue, + sender, flags, iov, iovcnt, NULL); + } + +out: + /* no receiver, or sender been deleted */ + return iov_size(iov, iovcnt); +} + +static char *netfilter_get_netdev_id(Object *obj, Error **errp) +{ + NetFilterState *nf = NETFILTER(obj); + + return g_strdup(nf->netdev_id); +} + +static void netfilter_set_netdev_id(Object *obj, const char *str, Error **errp) +{ + NetFilterState *nf = NETFILTER(obj); + + nf->netdev_id = g_strdup(str); +} + +static int netfilter_get_direction(Object *obj, Error **errp G_GNUC_UNUSED) +{ + NetFilterState *nf = NETFILTER(obj); + return nf->direction; +} + +static void netfilter_set_direction(Object *obj, int direction, Error **errp) +{ + NetFilterState *nf = NETFILTER(obj); + nf->direction = direction; +} + +static char *netfilter_get_status(Object *obj, Error **errp) +{ + NetFilterState *nf = NETFILTER(obj); + + return nf->on ? g_strdup("on") : g_strdup("off"); +} + +static void netfilter_set_status(Object *obj, const char *str, Error **errp) +{ + NetFilterState *nf = NETFILTER(obj); + NetFilterClass *nfc = NETFILTER_GET_CLASS(obj); + + if (strcmp(str, "on") && strcmp(str, "off")) { + error_setg(errp, "Invalid value for netfilter status, " + "should be 'on' or 'off'"); + return; + } + if (nf->on == !strcmp(str, "on")) { + return; + } + nf->on = !nf->on; + if (nf->netdev && nfc->status_changed) { + nfc->status_changed(nf, errp); + } +} + +static void netfilter_init(Object *obj) +{ + NetFilterState *nf = NETFILTER(obj); + + nf->on = true; + + object_property_add_str(obj, "netdev", + netfilter_get_netdev_id, netfilter_set_netdev_id, + NULL); + object_property_add_enum(obj, "queue", "NetFilterDirection", + NetFilterDirection_lookup, + netfilter_get_direction, netfilter_set_direction, + NULL); + object_property_add_str(obj, "status", + netfilter_get_status, netfilter_set_status, + NULL); +} + +static void netfilter_complete(UserCreatable *uc, Error **errp) +{ + NetFilterState *nf = NETFILTER(uc); + NetClientState *ncs[MAX_QUEUE_NUM]; + NetFilterClass *nfc = NETFILTER_GET_CLASS(uc); + int queues; + Error *local_err = NULL; + + if (!nf->netdev_id) { + error_setg(errp, "Parameter 'netdev' is required"); + return; + } + + queues = qemu_find_net_clients_except(nf->netdev_id, ncs, + NET_CLIENT_OPTIONS_KIND_NIC, + MAX_QUEUE_NUM); + if (queues < 1) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "netdev", + "a network backend id"); + return; + } else if (queues > 1) { + error_setg(errp, "multiqueue is not supported"); + return; + } + + if (get_vhost_net(ncs[0])) { + error_setg(errp, "Vhost is not supported"); + return; + } + + nf->netdev = ncs[0]; + + if (nfc->setup) { + nfc->setup(nf, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + QTAILQ_INSERT_TAIL(&nf->netdev->filters, nf, next); +} + +static void netfilter_finalize(Object *obj) +{ + NetFilterState *nf = NETFILTER(obj); + NetFilterClass *nfc = NETFILTER_GET_CLASS(obj); + + if (nfc->cleanup) { + nfc->cleanup(nf); + } + + if (nf->netdev && !QTAILQ_EMPTY(&nf->netdev->filters) && + nf->next.tqe_prev) { + QTAILQ_REMOVE(&nf->netdev->filters, nf, next); + } + g_free(nf->netdev_id); +} + +static void netfilter_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = netfilter_complete; +} + +static const TypeInfo netfilter_info = { + .name = TYPE_NETFILTER, + .parent = TYPE_OBJECT, + .abstract = true, + .class_size = sizeof(NetFilterClass), + .class_init = netfilter_class_init, + .instance_size = sizeof(NetFilterState), + .instance_init = netfilter_init, + .instance_finalize = netfilter_finalize, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +static void register_types(void) +{ + type_register_static(&netfilter_info); +} + +type_init(register_types); diff --git a/qemu/net/hub.c b/qemu/net/hub.c index 3047f1276..6d90c6ee6 100644 --- a/qemu/net/hub.c +++ b/qemu/net/hub.c @@ -12,6 +12,7 @@ * */ +#include "qemu/osdep.h" #include "monitor/monitor.h" #include "net/net.h" #include "clients.h" @@ -285,9 +286,9 @@ int net_init_hubport(const NetClientOptions *opts, const char *name, { const NetdevHubPortOptions *hubport; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT); + assert(opts->type == NET_CLIENT_OPTIONS_KIND_HUBPORT); assert(!peer); - hubport = opts->hubport; + hubport = opts->u.hubport.data; net_hub_add_port(hubport->hubid, name); return 0; diff --git a/qemu/net/l2tpv3.c b/qemu/net/l2tpv3.c index 4f9bceecc..5c668f737 100644 --- a/qemu/net/l2tpv3.c +++ b/qemu/net/l2tpv3.c @@ -23,9 +23,9 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include -#include "config-host.h" #include "net/net.h" #include "clients.h" #include "qemu-common.h" @@ -325,7 +325,7 @@ static int l2tpv3_verify_header(NetL2TPV3State *s, uint8_t *buf) if (s->cookie_is_64) { cookie = ldq_be_p(buf + s->cookie_offset); } else { - cookie = ldl_be_p(buf + s->cookie_offset); + cookie = ldl_be_p(buf + s->cookie_offset) & 0xffffffffULL; } if (cookie != s->rx_cookie) { if (!s->header_mismatch) { @@ -545,8 +545,8 @@ int net_init_l2tpv3(const NetClientOptions *opts, s->queue_tail = 0; s->header_mismatch = false; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_L2TPV3); - l2tpv3 = opts->l2tpv3; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_L2TPV3); + l2tpv3 = opts->u.l2tpv3.data; if (l2tpv3->has_ipv6 && l2tpv3->ipv6) { s->ipv6 = l2tpv3->ipv6; diff --git a/qemu/net/net.c b/qemu/net/net.c index 28a5597b8..0ad6217cb 100644 --- a/qemu/net/net.c +++ b/qemu/net/net.c @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "config-host.h" +#include "qemu/osdep.h" #include "net/net.h" #include "clients.h" @@ -32,9 +32,11 @@ #include "monitor/monitor.h" #include "qemu-common.h" +#include "qemu/help_option.h" #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "qemu/sockets.h" +#include "qemu/cutils.h" #include "qemu/config-file.h" #include "qmp-commands.h" #include "hw/qdev.h" @@ -42,8 +44,9 @@ #include "qemu/main-loop.h" #include "qapi-visit.h" #include "qapi/opts-visitor.h" -#include "qapi/dealloc-visitor.h" #include "sysemu/sysemu.h" +#include "net/filter.h" +#include "qapi/string-output-visitor.h" /* Net bridge is currently not supported for W32. */ #if !defined(_WIN32) @@ -78,34 +81,6 @@ int default_net = 1; /***********************************************************/ /* network device redirectors */ -#if defined(DEBUG_NET) -static void hex_dump(FILE *f, const uint8_t *buf, int size) -{ - int len, i, j, c; - - for(i=0;i 16) - len = 16; - fprintf(f, "%08x ", i); - for(j=0;j<16;j++) { - if (j < len) - fprintf(f, " %02x", buf[i+j]); - else - fprintf(f, " "); - } - fprintf(f, " "); - for(j=0;j '~') - c = '.'; - fprintf(f, "%c", c); - } - fprintf(f, "\n"); - } -} -#endif - static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) { const char *p, *p1; @@ -285,8 +260,9 @@ static void qemu_net_client_setup(NetClientState *nc, } QTAILQ_INSERT_TAIL(&net_clients, nc, next); - nc->incoming_queue = qemu_new_net_queue(nc); + nc->incoming_queue = qemu_new_net_queue(qemu_deliver_packet_iov, nc); nc->destructor = destructor; + QTAILQ_INIT(&nc->filters); } NetClientState *qemu_new_net_client(NetClientInfo *info, @@ -384,6 +360,7 @@ void qemu_del_net_client(NetClientState *nc) { NetClientState *ncs[MAX_QUEUE_NUM]; int queues, i; + NetFilterState *nf, *next; assert(nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC); @@ -395,6 +372,10 @@ void qemu_del_net_client(NetClientState *nc) MAX_QUEUE_NUM); assert(queues != 0); + QTAILQ_FOREACH_SAFE(nf, &nc->filters, next, next) { + object_unparent(OBJECT(nf)); + } + /* If there is a peer NIC, delete and cleanup client, but do not free. */ if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) { NICState *nic = qemu_get_nic(nc->peer); @@ -517,20 +498,28 @@ void qemu_set_vnet_hdr_len(NetClientState *nc, int len) int qemu_set_vnet_le(NetClientState *nc, bool is_le) { +#ifdef HOST_WORDS_BIGENDIAN if (!nc || !nc->info->set_vnet_le) { return -ENOSYS; } return nc->info->set_vnet_le(nc, is_le); +#else + return 0; +#endif } int qemu_set_vnet_be(NetClientState *nc, bool is_be) { +#ifdef HOST_WORDS_BIGENDIAN + return 0; +#else if (!nc || !nc->info->set_vnet_be) { return -ENOSYS; } return nc->info->set_vnet_be(nc, is_be); +#endif } int qemu_can_send_packet(NetClientState *sender) @@ -554,36 +543,54 @@ int qemu_can_send_packet(NetClientState *sender) return 1; } -ssize_t qemu_deliver_packet(NetClientState *sender, - unsigned flags, - const uint8_t *data, - size_t size, - void *opaque) +static ssize_t filter_receive_iov(NetClientState *nc, + NetFilterDirection direction, + NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + NetPacketSent *sent_cb) { - NetClientState *nc = opaque; - ssize_t ret; - - if (nc->link_down) { - return size; - } + ssize_t ret = 0; + NetFilterState *nf = NULL; - if (nc->receive_disabled) { - return 0; - } - - if (flags & QEMU_NET_PACKET_FLAG_RAW && nc->info->receive_raw) { - ret = nc->info->receive_raw(nc, data, size); + if (direction == NET_FILTER_DIRECTION_TX) { + QTAILQ_FOREACH(nf, &nc->filters, next) { + ret = qemu_netfilter_receive(nf, direction, sender, flags, iov, + iovcnt, sent_cb); + if (ret) { + return ret; + } + } } else { - ret = nc->info->receive(nc, data, size); - } - - if (ret == 0) { - nc->receive_disabled = 1; + QTAILQ_FOREACH_REVERSE(nf, &nc->filters, NetFilterHead, next) { + ret = qemu_netfilter_receive(nf, direction, sender, flags, iov, + iovcnt, sent_cb); + if (ret) { + return ret; + } + } } return ret; } +static ssize_t filter_receive(NetClientState *nc, + NetFilterDirection direction, + NetClientState *sender, + unsigned flags, + const uint8_t *data, + size_t size, + NetPacketSent *sent_cb) +{ + struct iovec iov = { + .iov_base = (void *)data, + .iov_len = size + }; + + return filter_receive_iov(nc, direction, sender, flags, &iov, 1, sent_cb); +} + void qemu_purge_queued_packets(NetClientState *nc) { if (!nc->peer) { @@ -625,16 +632,30 @@ static ssize_t qemu_send_packet_async_with_flags(NetClientState *sender, NetPacketSent *sent_cb) { NetQueue *queue; + int ret; #ifdef DEBUG_NET printf("qemu_send_packet_async:\n"); - hex_dump(stdout, buf, size); + qemu_hexdump((const char *)buf, stdout, "net", size); #endif if (sender->link_down || !sender->peer) { return size; } + /* Let filters handle the packet first */ + ret = filter_receive(sender, NET_FILTER_DIRECTION_TX, + sender, flags, buf, size, sent_cb); + if (ret) { + return ret; + } + + ret = filter_receive(sender->peer, NET_FILTER_DIRECTION_RX, + sender, flags, buf, size, sent_cb); + if (ret) { + return ret; + } + queue = sender->peer->incoming_queue; return qemu_net_queue_send(queue, sender, flags, buf, size, sent_cb); @@ -660,14 +681,30 @@ ssize_t qemu_send_packet_raw(NetClientState *nc, const uint8_t *buf, int size) } static ssize_t nc_sendv_compat(NetClientState *nc, const struct iovec *iov, - int iovcnt) + int iovcnt, unsigned flags) { - uint8_t buffer[NET_BUFSIZE]; + uint8_t *buf = NULL; + uint8_t *buffer; size_t offset; + ssize_t ret; - offset = iov_to_buf(iov, iovcnt, 0, buffer, sizeof(buffer)); + if (iovcnt == 1) { + buffer = iov[0].iov_base; + offset = iov[0].iov_len; + } else { + buf = g_new(uint8_t, NET_BUFSIZE); + buffer = buf; + offset = iov_to_buf(iov, iovcnt, 0, buf, NET_BUFSIZE); + } - return nc->info->receive(nc, buffer, offset); + if (flags & QEMU_NET_PACKET_FLAG_RAW && nc->info->receive_raw) { + ret = nc->info->receive_raw(nc, buffer, offset); + } else { + ret = nc->info->receive(nc, buffer, offset); + } + + g_free(buf); + return ret; } ssize_t qemu_deliver_packet_iov(NetClientState *sender, @@ -690,7 +727,7 @@ ssize_t qemu_deliver_packet_iov(NetClientState *sender, if (nc->info->receive_iov) { ret = nc->info->receive_iov(nc, iov, iovcnt); } else { - ret = nc_sendv_compat(nc, iov, iovcnt); + ret = nc_sendv_compat(nc, iov, iovcnt, flags); } if (ret == 0) { @@ -705,11 +742,25 @@ ssize_t qemu_sendv_packet_async(NetClientState *sender, NetPacketSent *sent_cb) { NetQueue *queue; + int ret; if (sender->link_down || !sender->peer) { return iov_size(iov, iovcnt); } + /* Let filters handle the packet first */ + ret = filter_receive_iov(sender, NET_FILTER_DIRECTION_TX, sender, + QEMU_NET_PACKET_FLAG_NONE, iov, iovcnt, sent_cb); + if (ret) { + return ret; + } + + ret = filter_receive_iov(sender->peer, NET_FILTER_DIRECTION_RX, sender, + QEMU_NET_PACKET_FLAG_NONE, iov, iovcnt, sent_cb); + if (ret) { + return ret; + } + queue = sender->peer->incoming_queue; return qemu_net_queue_send_iov(queue, sender, @@ -820,8 +871,8 @@ static int net_init_nic(const NetClientOptions *opts, const char *name, NICInfo *nd; const NetLegacyNicOptions *nic; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_NIC); - nic = opts->nic; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_NIC); + nic = opts->u.nic.data; idx = nic_get_free_idx(); if (idx == -1 || nb_nics >= MAX_NICS) { @@ -881,7 +932,7 @@ static int net_init_nic(const NetClientOptions *opts, const char *name, } -static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])( +static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND__MAX])( const NetClientOptions *opts, const char *name, NetClientState *peer, Error **errp) = { @@ -922,9 +973,9 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp) opts = netdev->opts; name = netdev->id; - if (opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP || - opts->kind == NET_CLIENT_OPTIONS_KIND_NIC || - !net_client_init_fun[opts->kind]) { + if (opts->type == NET_CLIENT_OPTIONS_KIND_DUMP || + opts->type == NET_CLIENT_OPTIONS_KIND_NIC || + !net_client_init_fun[opts->type]) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type", "a netdev backend type"); return -1; @@ -935,16 +986,16 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp) /* missing optional values have been initialized to "all bits zero" */ name = net->has_id ? net->id : net->name; - if (opts->kind == NET_CLIENT_OPTIONS_KIND_NONE) { + if (opts->type == NET_CLIENT_OPTIONS_KIND_NONE) { return 0; /* nothing to do */ } - if (opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT) { + if (opts->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type", "a net type"); return -1; } - if (!net_client_init_fun[opts->kind]) { + if (!net_client_init_fun[opts->type]) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type", "a net backend type (maybe it is not compiled " "into this binary)"); @@ -952,17 +1003,17 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp) } /* Do not add to a vlan if it's a nic with a netdev= parameter. */ - if (opts->kind != NET_CLIENT_OPTIONS_KIND_NIC || - !opts->nic->has_netdev) { + if (opts->type != NET_CLIENT_OPTIONS_KIND_NIC || + !opts->u.nic.data->has_netdev) { peer = net_hub_add_port(net->has_vlan ? net->vlan : 0, NULL); } } - if (net_client_init_fun[opts->kind](opts, name, peer, errp) < 0) { + if (net_client_init_fun[opts->type](opts, name, peer, errp) < 0) { /* FIXME drop when all init functions store an Error */ if (errp && !*errp) { error_setg(errp, QERR_DEVICE_INIT_FAILED, - NetClientOptionsKind_lookup[opts->kind]); + NetClientOptionsKind_lookup[opts->type]); } return -1; } @@ -970,41 +1021,63 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp) } -static void net_visit(Visitor *v, int is_netdev, void **object, Error **errp) -{ - if (is_netdev) { - visit_type_Netdev(v, (Netdev **)object, NULL, errp); - } else { - visit_type_NetLegacy(v, (NetLegacy **)object, NULL, errp); - } -} - - int net_client_init(QemuOpts *opts, int is_netdev, Error **errp) { void *object = NULL; Error *err = NULL; int ret = -1; + OptsVisitor *ov = opts_visitor_new(opts); + Visitor *v = opts_get_visitor(ov); { - OptsVisitor *ov = opts_visitor_new(opts); + /* Parse convenience option format ip6-net=fec0::0[/64] */ + const char *ip6_net = qemu_opt_get(opts, "ipv6-net"); + + if (ip6_net) { + char buf[strlen(ip6_net) + 1]; + + if (get_str_sep(buf, sizeof(buf), &ip6_net, '/') < 0) { + /* Default 64bit prefix length. */ + qemu_opt_set(opts, "ipv6-prefix", ip6_net, &error_abort); + qemu_opt_set_number(opts, "ipv6-prefixlen", 64, &error_abort); + } else { + /* User-specified prefix length. */ + unsigned long len; + int err; + + qemu_opt_set(opts, "ipv6-prefix", buf, &error_abort); + err = qemu_strtoul(ip6_net, NULL, 10, &len); + + if (err) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "ipv6-prefix", "a number"); + } else { + qemu_opt_set_number(opts, "ipv6-prefixlen", len, + &error_abort); + } + } + qemu_opt_unset(opts, "ipv6-net"); + } + } - net_visit(opts_get_visitor(ov), is_netdev, &object, &err); - opts_visitor_cleanup(ov); + if (is_netdev) { + visit_type_Netdev(v, NULL, (Netdev **)&object, &err); + } else { + visit_type_NetLegacy(v, NULL, (NetLegacy **)&object, &err); } if (!err) { ret = net_client_init1(object, is_netdev, &err); } - if (object) { - QapiDeallocVisitor *dv = qapi_dealloc_visitor_new(); - - net_visit(qapi_dealloc_get_visitor(dv), is_netdev, &object, NULL); - qapi_dealloc_visitor_cleanup(dv); + if (is_netdev) { + qapi_free_Netdev(object); + } else { + qapi_free_NetLegacy(object); } error_propagate(errp, err); + opts_visitor_cleanup(ov); return ret; } @@ -1123,12 +1196,49 @@ void qmp_netdev_del(const char *id, Error **errp) qemu_opts_del(opts); } +static void netfilter_print_info(Monitor *mon, NetFilterState *nf) +{ + char *str; + ObjectProperty *prop; + ObjectPropertyIterator iter; + StringOutputVisitor *ov; + + /* generate info str */ + object_property_iter_init(&iter, OBJECT(nf)); + while ((prop = object_property_iter_next(&iter))) { + if (!strcmp(prop->name, "type")) { + continue; + } + ov = string_output_visitor_new(false); + object_property_get(OBJECT(nf), string_output_get_visitor(ov), + prop->name, NULL); + str = string_output_get_string(ov); + string_output_visitor_cleanup(ov); + monitor_printf(mon, ",%s=%s", prop->name, str); + g_free(str); + } + monitor_printf(mon, "\n"); +} + void print_net_client(Monitor *mon, NetClientState *nc) { + NetFilterState *nf; + monitor_printf(mon, "%s: index=%d,type=%s,%s\n", nc->name, nc->queue_index, NetClientOptionsKind_lookup[nc->info->type], nc->info_str); + if (!QTAILQ_EMPTY(&nc->filters)) { + monitor_printf(mon, "filters:\n"); + } + QTAILQ_FOREACH(nf, &nc->filters, next) { + char *path = object_get_canonical_path_component(OBJECT(nf)); + + monitor_printf(mon, " - %s: type=%s", path, + object_get_typename(OBJECT(nf))); + netfilter_print_info(mon, nf); + g_free(path); + } } RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name, @@ -1154,6 +1264,12 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name, continue; } + /* only query information on queue 0 since the info is per nic, + * not per queue + */ + if (nc->queue_index != 0) + continue; + if (nc->info->query_rx_filter) { info = nc->info->query_rx_filter(nc); entry = g_malloc0(sizeof(*entry)); @@ -1216,7 +1332,7 @@ void qmp_set_link(const char *name, bool up, Error **errp) int queues, i; queues = qemu_find_net_clients_except(name, ncs, - NET_CLIENT_OPTIONS_KIND_MAX, + NET_CLIENT_OPTIONS_KIND__MAX, MAX_QUEUE_NUM); if (queues == 0) { diff --git a/qemu/net/netmap.c b/qemu/net/netmap.c index 508b82947..6cc0db5ee 100644 --- a/qemu/net/netmap.c +++ b/qemu/net/netmap.c @@ -23,11 +23,10 @@ */ +#include "qemu/osdep.h" #include #include #include -#include -#include #define NETMAP_WITH_LIBS #include #include @@ -37,23 +36,16 @@ #include "clients.h" #include "sysemu/sysemu.h" #include "qemu/error-report.h" +#include "qapi/error.h" #include "qemu/iov.h" - -/* Private netmap device info. */ -typedef struct NetmapPriv { - int fd; - size_t memsize; - void *mem; - struct netmap_if *nifp; - struct netmap_ring *rx; - struct netmap_ring *tx; - char fdname[PATH_MAX]; /* Normally "/dev/netmap". */ - char ifname[IFNAMSIZ]; -} NetmapPriv; +#include "qemu/cutils.h" typedef struct NetmapState { NetClientState nc; - NetmapPriv me; + struct nm_desc *nmd; + char ifname[IFNAMSIZ]; + struct netmap_ring *tx; + struct netmap_ring *rx; bool read_poll; bool write_poll; struct iovec iov[IOV_MAX]; @@ -90,46 +82,23 @@ pkt_copy(const void *_src, void *_dst, int l) * Open a netmap device. We assume there is only one queue * (which is the case for the VALE bridge). */ -static int netmap_open(NetmapPriv *me) +static struct nm_desc *netmap_open(const NetdevNetmapOptions *nm_opts, + Error **errp) { - int fd; - int err; - size_t l; + struct nm_desc *nmd; struct nmreq req; - me->fd = fd = open(me->fdname, O_RDWR); - if (fd < 0) { - error_report("Unable to open netmap device '%s' (%s)", - me->fdname, strerror(errno)); - return -1; - } memset(&req, 0, sizeof(req)); - pstrcpy(req.nr_name, sizeof(req.nr_name), me->ifname); - req.nr_ringid = NETMAP_NO_TX_POLL; - req.nr_version = NETMAP_API; - err = ioctl(fd, NIOCREGIF, &req); - if (err) { - error_report("Unable to register %s: %s", me->ifname, strerror(errno)); - goto error; - } - l = me->memsize = req.nr_memsize; - - me->mem = mmap(0, l, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0); - if (me->mem == MAP_FAILED) { - error_report("Unable to mmap netmap shared memory: %s", - strerror(errno)); - me->mem = NULL; - goto error; - } - me->nifp = NETMAP_IF(me->mem, req.nr_offset); - me->tx = NETMAP_TXRING(me->nifp, 0); - me->rx = NETMAP_RXRING(me->nifp, 0); - return 0; + nmd = nm_open(nm_opts->ifname, &req, NETMAP_NO_TX_POLL, + NULL); + if (nmd == NULL) { + error_setg_errno(errp, errno, "Failed to nm_open() %s", + nm_opts->ifname); + return NULL; + } -error: - close(me->fd); - return -1; + return nmd; } static void netmap_send(void *opaque); @@ -138,7 +107,7 @@ static void netmap_writable(void *opaque); /* Set the event-loop handlers for the netmap backend. */ static void netmap_update_fd_handler(NetmapState *s) { - qemu_set_fd_handler(s->me.fd, + qemu_set_fd_handler(s->nmd->fd, s->read_poll ? netmap_send : NULL, s->write_poll ? netmap_writable : NULL, s); @@ -190,7 +159,7 @@ static ssize_t netmap_receive(NetClientState *nc, const uint8_t *buf, size_t size) { NetmapState *s = DO_UPCAST(NetmapState, nc, nc); - struct netmap_ring *ring = s->me.tx; + struct netmap_ring *ring = s->tx; uint32_t i; uint32_t idx; uint8_t *dst; @@ -220,7 +189,7 @@ static ssize_t netmap_receive(NetClientState *nc, ring->slot[i].flags = 0; pkt_copy(buf, dst, size); ring->cur = ring->head = nm_ring_next(ring, i); - ioctl(s->me.fd, NIOCTXSYNC, NULL); + ioctl(s->nmd->fd, NIOCTXSYNC, NULL); return size; } @@ -229,7 +198,7 @@ static ssize_t netmap_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) { NetmapState *s = DO_UPCAST(NetmapState, nc, nc); - struct netmap_ring *ring = s->me.tx; + struct netmap_ring *ring = s->tx; uint32_t last; uint32_t idx; uint8_t *dst; @@ -286,7 +255,7 @@ static ssize_t netmap_receive_iov(NetClientState *nc, /* Now update ring->cur and ring->head. */ ring->cur = ring->head = i; - ioctl(s->me.fd, NIOCTXSYNC, NULL); + ioctl(s->nmd->fd, NIOCTXSYNC, NULL); return iov_size(iov, iovcnt); } @@ -303,7 +272,7 @@ static void netmap_send_completed(NetClientState *nc, ssize_t len) static void netmap_send(void *opaque) { NetmapState *s = opaque; - struct netmap_ring *ring = s->me.rx; + struct netmap_ring *ring = s->rx; /* Keep sending while there are available packets into the netmap RX ring and the forwarding path towards the peer is open. */ @@ -351,27 +320,52 @@ static void netmap_cleanup(NetClientState *nc) qemu_purge_queued_packets(nc); netmap_poll(nc, false); - munmap(s->me.mem, s->me.memsize); - close(s->me.fd); - - s->me.fd = -1; + nm_close(s->nmd); + s->nmd = NULL; } /* Offloading manipulation support callbacks. */ -static bool netmap_has_ufo(NetClientState *nc) +static int netmap_fd_set_vnet_hdr_len(NetmapState *s, int len) { - return true; + struct nmreq req; + + /* Issue a NETMAP_BDG_VNET_HDR command to change the virtio-net header + * length for the netmap adapter associated to 's->ifname'. + */ + memset(&req, 0, sizeof(req)); + pstrcpy(req.nr_name, sizeof(req.nr_name), s->ifname); + req.nr_version = NETMAP_API; + req.nr_cmd = NETMAP_BDG_VNET_HDR; + req.nr_arg1 = len; + + return ioctl(s->nmd->fd, NIOCREGIF, &req); } -static bool netmap_has_vnet_hdr(NetClientState *nc) +static bool netmap_has_vnet_hdr_len(NetClientState *nc, int len) { + NetmapState *s = DO_UPCAST(NetmapState, nc, nc); + int prev_len = s->vnet_hdr_len; + + /* Check that we can set the new length. */ + if (netmap_fd_set_vnet_hdr_len(s, len)) { + return false; + } + + /* Restore the previous length. */ + if (netmap_fd_set_vnet_hdr_len(s, prev_len)) { + error_report("Failed to restore vnet-hdr length %d on %s: %s", + prev_len, s->ifname, strerror(errno)); + abort(); + } + return true; } -static bool netmap_has_vnet_hdr_len(NetClientState *nc, int len) +/* A netmap interface that supports virtio-net headers always + * supports UFO, so we use this callback also for the has_ufo hook. */ +static bool netmap_has_vnet_hdr(NetClientState *nc) { - return len == 0 || len == sizeof(struct virtio_net_hdr) || - len == sizeof(struct virtio_net_hdr_mrg_rxbuf); + return netmap_has_vnet_hdr_len(nc, sizeof(struct virtio_net_hdr)); } static void netmap_using_vnet_hdr(NetClientState *nc, bool enable) @@ -382,20 +376,11 @@ static void netmap_set_vnet_hdr_len(NetClientState *nc, int len) { NetmapState *s = DO_UPCAST(NetmapState, nc, nc); int err; - struct nmreq req; - /* Issue a NETMAP_BDG_VNET_HDR command to change the virtio-net header - * length for the netmap adapter associated to 'me->ifname'. - */ - memset(&req, 0, sizeof(req)); - pstrcpy(req.nr_name, sizeof(req.nr_name), s->me.ifname); - req.nr_version = NETMAP_API; - req.nr_cmd = NETMAP_BDG_VNET_HDR; - req.nr_arg1 = len; - err = ioctl(s->me.fd, NIOCREGIF, &req); + err = netmap_fd_set_vnet_hdr_len(s, len); if (err) { - error_report("Unable to execute NETMAP_BDG_VNET_HDR on %s: %s", - s->me.ifname, strerror(errno)); + error_report("Unable to set vnet-hdr length %d on %s: %s", + len, s->ifname, strerror(errno)); } else { /* Keep track of the current length. */ s->vnet_hdr_len = len; @@ -408,8 +393,7 @@ static void netmap_set_offload(NetClientState *nc, int csum, int tso4, int tso6, NetmapState *s = DO_UPCAST(NetmapState, nc, nc); /* Setting a virtio-net header length greater than zero automatically - * enables the offloadings. - */ + * enables the offloadings. */ if (!s->vnet_hdr_len) { netmap_set_vnet_hdr_len(nc, sizeof(struct virtio_net_hdr)); } @@ -423,7 +407,7 @@ static NetClientInfo net_netmap_info = { .receive_iov = netmap_receive_iov, .poll = netmap_poll, .cleanup = netmap_cleanup, - .has_ufo = netmap_has_ufo, + .has_ufo = netmap_has_vnet_hdr, .has_vnet_hdr = netmap_has_vnet_hdr, .has_vnet_hdr_len = netmap_has_vnet_hdr_len, .using_vnet_hdr = netmap_using_vnet_hdr, @@ -438,24 +422,25 @@ static NetClientInfo net_netmap_info = { int net_init_netmap(const NetClientOptions *opts, const char *name, NetClientState *peer, Error **errp) { - /* FIXME error_setg(errp, ...) on failure */ - const NetdevNetmapOptions *netmap_opts = opts->netmap; + const NetdevNetmapOptions *netmap_opts = opts->u.netmap.data; + struct nm_desc *nmd; NetClientState *nc; - NetmapPriv me; + Error *err = NULL; NetmapState *s; - pstrcpy(me.fdname, sizeof(me.fdname), - netmap_opts->has_devname ? netmap_opts->devname : "/dev/netmap"); - /* Set default name for the port if not supplied. */ - pstrcpy(me.ifname, sizeof(me.ifname), netmap_opts->ifname); - if (netmap_open(&me)) { + nmd = netmap_open(netmap_opts, &err); + if (err) { + error_propagate(errp, err); return -1; } /* Create the object. */ nc = qemu_new_net_client(&net_netmap_info, peer, "netmap", name); s = DO_UPCAST(NetmapState, nc, nc); - s->me = me; + s->nmd = nmd; + s->tx = NETMAP_TXRING(nmd->nifp, 0); + s->rx = NETMAP_RXRING(nmd->nifp, 0); s->vnet_hdr_len = 0; + pstrcpy(s->ifname, sizeof(s->ifname), netmap_opts->ifname); netmap_read_poll(s, true); /* Initially only poll for reads. */ return 0; diff --git a/qemu/net/queue.c b/qemu/net/queue.c index ebbe2bb93..9c32abdb8 100644 --- a/qemu/net/queue.c +++ b/qemu/net/queue.c @@ -21,6 +21,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "net/queue.h" #include "qemu/queue.h" #include "net/net.h" @@ -52,13 +53,14 @@ struct NetQueue { void *opaque; uint32_t nq_maxlen; uint32_t nq_count; + NetQueueDeliverFunc *deliver; QTAILQ_HEAD(packets, NetPacket) packets; unsigned delivering : 1; }; -NetQueue *qemu_new_net_queue(void *opaque) +NetQueue *qemu_new_net_queue(NetQueueDeliverFunc *deliver, void *opaque) { NetQueue *queue; @@ -67,6 +69,7 @@ NetQueue *qemu_new_net_queue(void *opaque) queue->opaque = opaque; queue->nq_maxlen = 10000; queue->nq_count = 0; + queue->deliver = deliver; QTAILQ_INIT(&queue->packets); @@ -110,12 +113,12 @@ static void qemu_net_queue_append(NetQueue *queue, QTAILQ_INSERT_TAIL(&queue->packets, packet, entry); } -static void qemu_net_queue_append_iov(NetQueue *queue, - NetClientState *sender, - unsigned flags, - const struct iovec *iov, - int iovcnt, - NetPacketSent *sent_cb) +void qemu_net_queue_append_iov(NetQueue *queue, + NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + NetPacketSent *sent_cb) { NetPacket *packet; size_t max_len = 0; @@ -152,9 +155,13 @@ static ssize_t qemu_net_queue_deliver(NetQueue *queue, size_t size) { ssize_t ret = -1; + struct iovec iov = { + .iov_base = (void *)data, + .iov_len = size + }; queue->delivering = 1; - ret = qemu_deliver_packet(sender, flags, data, size, queue->opaque); + ret = queue->deliver(sender, flags, &iov, 1, queue->opaque); queue->delivering = 0; return ret; @@ -169,7 +176,7 @@ static ssize_t qemu_net_queue_deliver_iov(NetQueue *queue, ssize_t ret = -1; queue->delivering = 1; - ret = qemu_deliver_packet_iov(sender, flags, iov, iovcnt, queue->opaque); + ret = queue->deliver(sender, flags, iov, iovcnt, queue->opaque); queue->delivering = 0; return ret; diff --git a/qemu/net/slirp.c b/qemu/net/slirp.c index 7657b38fd..31630f005 100644 --- a/qemu/net/slirp.c +++ b/qemu/net/slirp.c @@ -21,9 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "net/slirp.h" -#include "config-host.h" #ifndef _WIN32 #include @@ -36,7 +36,9 @@ #include "qemu/error-report.h" #include "qemu/sockets.h" #include "slirp/libslirp.h" +#include "slirp/ip6.h" #include "sysemu/char.h" +#include "qemu/cutils.h" static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) { @@ -134,11 +136,14 @@ static NetClientInfo net_slirp_info = { static int net_slirp_init(NetClientState *peer, const char *model, const char *name, int restricted, - const char *vnetwork, const char *vhost, + bool ipv4, const char *vnetwork, const char *vhost, + bool ipv6, const char *vprefix6, int vprefix6_len, + const char *vhost6, const char *vhostname, const char *tftp_export, const char *bootfile, const char *vdhcp_start, - const char *vnameserver, const char *smb_export, - const char *vsmbserver, const char **dnssearch) + const char *vnameserver, const char *vnameserver6, + const char *smb_export, const char *vsmbserver, + const char **dnssearch) { /* default settings according to historic slirp */ struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */ @@ -146,6 +151,9 @@ static int net_slirp_init(NetClientState *peer, const char *model, struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */ struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */ struct in_addr dns = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */ + struct in6_addr ip6_prefix; + struct in6_addr ip6_host; + struct in6_addr ip6_dns; #ifndef _WIN32 struct in_addr smbsrv = { .s_addr = 0 }; #endif @@ -157,6 +165,19 @@ static int net_slirp_init(NetClientState *peer, const char *model, char *end; struct slirp_config_str *config; + if (!ipv4 && (vnetwork || vhost || vnameserver)) { + return -1; + } + + if (!ipv6 && (vprefix6 || vhost6 || vnameserver6)) { + return -1; + } + + if (!ipv4 && !ipv6) { + /* It doesn't make sense to disable both */ + return -1; + } + if (!tftp_export) { tftp_export = legacy_tftp_prefix; } @@ -235,6 +256,64 @@ static int net_slirp_init(NetClientState *peer, const char *model, } #endif +#if defined(_WIN32) && (_WIN32_WINNT < 0x0600) + /* No inet_pton helper before Vista... */ + if (vprefix6) { + /* Unsupported */ + return -1; + } + memset(&ip6_prefix, 0, sizeof(ip6_prefix)); + ip6_prefix.s6_addr[0] = 0xfe; + ip6_prefix.s6_addr[1] = 0xc0; +#else + if (!vprefix6) { + vprefix6 = "fec0::"; + } + if (!inet_pton(AF_INET6, vprefix6, &ip6_prefix)) { + return -1; + } +#endif + + if (!vprefix6_len) { + vprefix6_len = 64; + } + if (vprefix6_len < 0 || vprefix6_len > 126) { + return -1; + } + + if (vhost6) { +#if defined(_WIN32) && (_WIN32_WINNT < 0x0600) + return -1; +#else + if (!inet_pton(AF_INET6, vhost6, &ip6_host)) { + return -1; + } + if (!in6_equal_net(&ip6_prefix, &ip6_host, vprefix6_len)) { + return -1; + } +#endif + } else { + ip6_host = ip6_prefix; + ip6_host.s6_addr[15] |= 2; + } + + if (vnameserver6) { +#if defined(_WIN32) && (_WIN32_WINNT < 0x0600) + return -1; +#else + if (!inet_pton(AF_INET6, vnameserver6, &ip6_dns)) { + return -1; + } + if (!in6_equal_net(&ip6_prefix, &ip6_dns, vprefix6_len)) { + return -1; + } +#endif + } else { + ip6_dns = ip6_prefix; + ip6_dns.s6_addr[15] |= 3; + } + + nc = qemu_new_net_client(&net_slirp_info, peer, model, name); snprintf(nc->info_str, sizeof(nc->info_str), @@ -243,8 +322,10 @@ static int net_slirp_init(NetClientState *peer, const char *model, s = DO_UPCAST(SlirpState, nc, nc); - s->slirp = slirp_init(restricted, net, mask, host, vhostname, - tftp_export, bootfile, dhcp, dns, dnssearch, s); + s->slirp = slirp_init(restricted, ipv4, net, mask, host, + ipv6, ip6_prefix, vprefix6_len, ip6_host, + vhostname, tftp_export, bootfile, dhcp, + dns, ip6_dns, dnssearch, s); QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry); for (config = slirp_configs; config; config = config->next) { @@ -745,9 +826,19 @@ int net_init_slirp(const NetClientOptions *opts, const char *name, int ret; const NetdevUserOptions *user; const char **dnssearch; + bool ipv4 = true, ipv6 = true; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_USER); - user = opts->user; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_USER); + user = opts->u.user.data; + + if ((user->has_ipv6 && user->ipv6 && !user->has_ipv4) || + (user->has_ipv4 && !user->ipv4)) { + ipv4 = 0; + } + if ((user->has_ipv4 && user->ipv4 && !user->has_ipv6) || + (user->has_ipv6 && !user->ipv6)) { + ipv6 = 0; + } vnet = user->has_net ? g_strdup(user->net) : user->has_ip ? g_strdup_printf("%s/24", user->ip) : @@ -760,9 +851,12 @@ int net_init_slirp(const NetClientOptions *opts, const char *name, net_init_slirp_configs(user->hostfwd, SLIRP_CFG_HOSTFWD); net_init_slirp_configs(user->guestfwd, 0); - ret = net_slirp_init(peer, "user", name, user->q_restrict, vnet, - user->host, user->hostname, user->tftp, - user->bootfile, user->dhcpstart, user->dns, user->smb, + ret = net_slirp_init(peer, "user", name, user->q_restrict, + ipv4, vnet, user->host, + ipv6, user->ipv6_prefix, user->ipv6_prefixlen, + user->ipv6_host, user->hostname, user->tftp, + user->bootfile, user->dhcpstart, + user->dns, user->ipv6_dns, user->smb, user->smbserver, dnssearch); while (slirp_configs) { @@ -784,6 +878,9 @@ int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret return 0; } + error_report("The '-net channel' option is deprecated. " + "Please use '-netdev user,guestfwd=...' instead."); + /* handle legacy -net channel,port:chr */ optarg += strlen("channel,"); diff --git a/qemu/net/socket.c b/qemu/net/socket.c index b1e3b1c8d..9fa2cd8d5 100644 --- a/qemu/net/socket.c +++ b/qemu/net/socket.c @@ -21,11 +21,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "config-host.h" +#include "qemu/osdep.h" #include "net/net.h" #include "clients.h" #include "monitor/monitor.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/error-report.h" #include "qemu/option.h" @@ -145,15 +146,14 @@ static void net_socket_send_completed(NetClientState *nc, ssize_t len) static void net_socket_send(void *opaque) { NetSocketState *s = opaque; - int size, err; + int size; unsigned l; uint8_t buf1[NET_BUFSIZE]; const uint8_t *buf; size = qemu_recv(s->fd, buf1, sizeof(buf1), 0); if (size < 0) { - err = socket_error(); - if (err != EWOULDBLOCK) + if (errno != EWOULDBLOCK) goto eoc; } else if (size == 0) { /* end of connection */ @@ -566,7 +566,7 @@ static int net_socket_connect_init(NetClientState *peer, const char *host_str) { NetSocketState *s; - int fd, connected, ret, err; + int fd, connected, ret; struct sockaddr_in saddr; if (parse_host_port(&saddr, host_str) < 0) @@ -583,14 +583,12 @@ static int net_socket_connect_init(NetClientState *peer, for(;;) { ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)); if (ret < 0) { - err = socket_error(); - if (err == EINTR || err == EWOULDBLOCK) { - } else if (err == EINPROGRESS) { - break; -#ifdef _WIN32 - } else if (err == WSAEALREADY || err == WSAEINVAL) { + if (errno == EINTR || errno == EWOULDBLOCK) { + /* continue */ + } else if (errno == EINPROGRESS || + errno == EALREADY || + errno == EINVAL) { break; -#endif } else { perror("connect"); closesocket(fd); @@ -706,8 +704,8 @@ int net_init_socket(const NetClientOptions *opts, const char *name, Error *err = NULL; const NetdevSocketOptions *sock; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_SOCKET); - sock = opts->socket; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_SOCKET); + sock = opts->u.socket.data; if (sock->has_fd + sock->has_listen + sock->has_connect + sock->has_mcast + sock->has_udp != 1) { diff --git a/qemu/net/tap-aix.c b/qemu/net/tap-aix.c index e84fc3913..0e6da6396 100644 --- a/qemu/net/tap-aix.c +++ b/qemu/net/tap-aix.c @@ -22,8 +22,9 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "tap_int.h" -#include int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required, int mq_required, Error **errp) diff --git a/qemu/net/tap-bsd.c b/qemu/net/tap-bsd.c index 7028d9be9..c506ac31d 100644 --- a/qemu/net/tap-bsd.c +++ b/qemu/net/tap-bsd.c @@ -22,8 +22,10 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "tap_int.h" -#include "qemu-common.h" +#include "qemu/cutils.h" #include "sysemu/sysemu.h" #include "qemu/error-report.h" @@ -109,8 +111,7 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, #define PATH_NET_TAP "/dev/tap" -int tap_open(char *ifname, int ifname_size, int *vnet_hdr, - int vnet_hdr_required, int mq_required, Error **errp) +static int tap_open_clone(char *ifname, int ifname_size, Error **errp) { int fd, s, ret; struct ifreq ifr; @@ -126,7 +127,8 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, ret = ioctl(fd, TAPGIFNAME, (void *)&ifr); if (ret < 0) { error_setg_errno(errp, errno, "could not get tap interface name"); - goto error; + close(fd); + return -1; } if (ifname[0] != '\0') { @@ -135,19 +137,47 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, if (s < 0) { error_setg_errno(errp, errno, "could not open socket to set interface name"); - goto error; + close(fd); + return -1; } ifr.ifr_data = ifname; ret = ioctl(s, SIOCSIFNAME, (void *)&ifr); close(s); if (ret < 0) { error_setg(errp, "could not set tap interface name"); - goto error; + close(fd); + return -1; } } else { pstrcpy(ifname, ifname_size, ifr.ifr_name); } + return fd; +} + +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, + int vnet_hdr_required, int mq_required, Error **errp) +{ + int fd = -1; + + /* If the specified tap device already exists just use it. */ + if (ifname[0] != '\0') { + char dname[100]; + snprintf(dname, sizeof dname, "/dev/%s", ifname); + TFR(fd = open(dname, O_RDWR)); + if (fd < 0 && errno != ENOENT) { + error_setg_errno(errp, errno, "could not open %s", dname); + return -1; + } + } + + if (fd < 0) { + /* Tap device not specified or does not exist. */ + if ((fd = tap_open_clone(ifname, ifname_size, errp)) < 0) { + return -1; + } + } + if (*vnet_hdr) { /* BSD doesn't have IFF_VNET_HDR */ *vnet_hdr = 0; diff --git a/qemu/net/tap-haiku.c b/qemu/net/tap-haiku.c index 2e738ec6a..b27e57e95 100644 --- a/qemu/net/tap-haiku.c +++ b/qemu/net/tap-haiku.c @@ -22,8 +22,9 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "tap_int.h" -#include int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required, int mq_required, Error **errp) diff --git a/qemu/net/tap-linux.c b/qemu/net/tap-linux.c index 394f2a646..a503fa9c6 100644 --- a/qemu/net/tap-linux.c +++ b/qemu/net/tap-linux.c @@ -23,6 +23,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "tap_int.h" #include "tap-linux.h" #include "net/tap.h" @@ -31,8 +32,9 @@ #include #include "sysemu/sysemu.h" -#include "qemu-common.h" +#include "qapi/error.h" #include "qemu/error-report.h" +#include "qemu/cutils.h" #define PATH_NET_TUN "/dev/net/tun" @@ -211,7 +213,7 @@ int tap_fd_set_vnet_le(int fd, int is_le) return -errno; } - error_report("TUNSETVNETLE ioctl() failed: %s.\n", strerror(errno)); + error_report("TUNSETVNETLE ioctl() failed: %s.", strerror(errno)); abort(); } @@ -228,7 +230,7 @@ int tap_fd_set_vnet_be(int fd, int is_be) return -errno; } - error_report("TUNSETVNETBE ioctl() failed: %s.\n", strerror(errno)); + error_report("TUNSETVNETBE ioctl() failed: %s.", strerror(errno)); abort(); } diff --git a/qemu/net/tap-linux.h b/qemu/net/tap-linux.h index 01dc6f8a2..1dc3a9f27 100644 --- a/qemu/net/tap-linux.h +++ b/qemu/net/tap-linux.h @@ -16,7 +16,6 @@ #ifndef QEMU_TAP_LINUX_H #define QEMU_TAP_LINUX_H -#include #ifdef __linux__ #include diff --git a/qemu/net/tap-solaris.c b/qemu/net/tap-solaris.c index 0f60f78dd..a2a92356c 100644 --- a/qemu/net/tap-solaris.c +++ b/qemu/net/tap-solaris.c @@ -22,10 +22,12 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "tap_int.h" #include "sysemu/sysemu.h" +#include "qemu/cutils.h" -#include #include #include #include diff --git a/qemu/net/tap-win32.c b/qemu/net/tap-win32.c index 625d53c64..f1e142ace 100644 --- a/qemu/net/tap-win32.c +++ b/qemu/net/tap-win32.c @@ -26,6 +26,7 @@ * distribution); if not, see . */ +#include "qemu/osdep.h" #include "tap_int.h" #include "qemu-common.h" @@ -34,7 +35,6 @@ #include "net/tap.h" /* tap_has_ufo, ... */ #include "sysemu/sysemu.h" #include "qemu/error-report.h" -#include #include #include @@ -77,7 +77,12 @@ //#define DEBUG_TAP_WIN32 -#define TUN_ASYNCHRONOUS_WRITES 1 +/* FIXME: The asynch write path appears to be broken at + * present. WriteFile() ignores the lpNumberOfBytesWritten parameter + * for overlapped writes, with the result we return zero bytes sent, + * and after handling a single packet, receive is disabled for this + * interface. */ +/* #define TUN_ASYNCHRONOUS_WRITES 1 */ #define TUN_BUFFER_SIZE 1560 #define TUN_MAX_BUFFER_COUNT 32 @@ -356,7 +361,8 @@ static int get_device_guid( &len); if (status != ERROR_SUCCESS || name_type != REG_SZ) { - return -1; + ++i; + continue; } else { if (is_tap_win32_dev(enum_name)) { @@ -460,27 +466,48 @@ static int tap_win32_write(tap_win32_overlapped_t *overlapped, BOOL result; DWORD error; +#ifdef TUN_ASYNCHRONOUS_WRITES result = GetOverlappedResult( overlapped->handle, &overlapped->write_overlapped, &write_size, FALSE); if (!result && GetLastError() == ERROR_IO_INCOMPLETE) WaitForSingleObject(overlapped->write_event, INFINITE); +#endif result = WriteFile(overlapped->handle, buffer, size, &write_size, &overlapped->write_overlapped); +#ifdef TUN_ASYNCHRONOUS_WRITES + /* FIXME: we can't sensibly set write_size here, without waiting + * for the IO to complete! Moreover, we can't return zero, + * because that will disable receive on this interface, and we + * also can't assume it will succeed and return the full size, + * because that will result in the buffer being reclaimed while + * the IO is in progress. */ +#error Async writes are broken. Please disable TUN_ASYNCHRONOUS_WRITES. +#else /* !TUN_ASYNCHRONOUS_WRITES */ if (!result) { - switch (error = GetLastError()) - { - case ERROR_IO_PENDING: -#ifndef TUN_ASYNCHRONOUS_WRITES - WaitForSingleObject(overlapped->write_event, INFINITE); -#endif - break; - default: - return -1; + error = GetLastError(); + if (error == ERROR_IO_PENDING) { + result = GetOverlappedResult(overlapped->handle, + &overlapped->write_overlapped, + &write_size, TRUE); } } +#endif + + if (!result) { +#ifdef DEBUG_TAP_WIN32 + LPTSTR msgbuf; + error = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, + NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + &msgbuf, 0, NULL); + fprintf(stderr, "Tap-Win32: Error WriteFile %d - %s\n", error, msgbuf); + LocalFree(msgbuf); +#endif + return 0; + } return write_size; } @@ -767,8 +794,8 @@ int net_init_tap(const NetClientOptions *opts, const char *name, /* FIXME error_setg(errp, ...) on failure */ const NetdevTapOptions *tap; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP); - tap = opts->tap; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_TAP); + tap = opts->u.tap.data; if (!tap->has_ifname) { error_report("tap: no interface name"); diff --git a/qemu/net/tap.c b/qemu/net/tap.c index bd01590e8..740e8a261 100644 --- a/qemu/net/tap.c +++ b/qemu/net/tap.c @@ -23,12 +23,11 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "tap_int.h" -#include "config-host.h" #include -#include #include #include #include @@ -37,7 +36,9 @@ #include "clients.h" #include "monitor/monitor.h" #include "sysemu/sysemu.h" +#include "qapi/error.h" #include "qemu-common.h" +#include "qemu/cutils.h" #include "qemu/error-report.h" #include "net/tap.h" @@ -565,8 +566,8 @@ int net_init_bridge(const NetClientOptions *opts, const char *name, TAPState *s; int fd, vnet_hdr; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_BRIDGE); - bridge = opts->bridge; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_BRIDGE); + bridge = opts->u.bridge.data; helper = bridge->has_helper ? bridge->helper : DEFAULT_BRIDGE_HELPER; br = bridge->has_br ? bridge->br : DEFAULT_BRIDGE_INTERFACE; @@ -663,7 +664,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, options.backend_type = VHOST_BACKEND_TYPE_KERNEL; options.net_backend = &s->nc; - if (tap->has_vhostfd || tap->has_vhostfds) { + if (vhostfdname) { vhostfd = monitor_fd_param(cur_mon, vhostfdname, &err); if (vhostfd == -1) { error_propagate(errp, err); @@ -685,7 +686,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, "vhost-net requested but could not be initialized"); return; } - } else if (tap->has_vhostfd || tap->has_vhostfds) { + } else if (vhostfdname) { error_setg(errp, "vhostfd= is not valid without vhost"); } } @@ -728,8 +729,8 @@ int net_init_tap(const NetClientOptions *opts, const char *name, const char *vhostfdname; char ifname[128]; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP); - tap = opts->tap; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_TAP); + tap = opts->u.tap.data; queues = tap->has_queues ? tap->queues : 1; vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL; diff --git a/qemu/net/util.c b/qemu/net/util.c index 7e9507679..0b3dbfe5d 100644 --- a/qemu/net/util.c +++ b/qemu/net/util.c @@ -22,9 +22,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "util.h" -#include -#include int net_parse_macaddr(uint8_t *macaddr, const char *p) { diff --git a/qemu/net/util.h b/qemu/net/util.h index 10c7da95f..60b73d372 100644 --- a/qemu/net/util.h +++ b/qemu/net/util.h @@ -25,7 +25,6 @@ #ifndef QEMU_NET_UTIL_H #define QEMU_NET_UTIL_H -#include int net_parse_macaddr(uint8_t *macaddr, const char *p); diff --git a/qemu/net/vde.c b/qemu/net/vde.c index dacaa64b4..9427eaa16 100644 --- a/qemu/net/vde.c +++ b/qemu/net/vde.c @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "config-host.h" +#include "qemu/osdep.h" #include @@ -115,8 +115,8 @@ int net_init_vde(const NetClientOptions *opts, const char *name, /* FIXME error_setg(errp, ...) on failure */ const NetdevVdeOptions *vde; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VDE); - vde = opts->vde; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_VDE); + vde = opts->u.vde.data; /* missing optional values have been initialized to "all bits zero" */ if (net_vde_init(peer, "vde", name, vde->sock, vde->port, vde->group, diff --git a/qemu/net/vhost-user.c b/qemu/net/vhost-user.c index 93dcecd66..1b9e73a2d 100644 --- a/qemu/net/vhost-user.c +++ b/qemu/net/vhost-user.c @@ -8,12 +8,15 @@ * */ +#include "qemu/osdep.h" #include "clients.h" #include "net/vhost_net.h" #include "net/vhost-user.h" #include "sysemu/char.h" #include "qemu/config-file.h" #include "qemu/error-report.h" +#include "qmp-commands.h" +#include "trace.h" typedef struct VhostUserState { NetClientState nc; @@ -24,7 +27,6 @@ typedef struct VhostUserState { typedef struct VhostUserChardevProps { bool is_socket; bool is_unix; - bool is_server; } VhostUserChardevProps; VHostNetState *vhost_user_get_vhost_net(NetClientState *nc) @@ -39,37 +41,106 @@ static int vhost_user_running(VhostUserState *s) return (s->vhost_net) ? 1 : 0; } -static int vhost_user_start(VhostUserState *s) +static void vhost_user_stop(int queues, NetClientState *ncs[]) { - VhostNetOptions options; + VhostUserState *s; + int i; - if (vhost_user_running(s)) { - return 0; + for (i = 0; i < queues; i++) { + assert (ncs[i]->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER); + + s = DO_UPCAST(VhostUserState, nc, ncs[i]); + if (!vhost_user_running(s)) { + continue; + } + + if (s->vhost_net) { + vhost_net_cleanup(s->vhost_net); + s->vhost_net = NULL; + } } +} + +static int vhost_user_start(int queues, NetClientState *ncs[]) +{ + VhostNetOptions options; + VhostUserState *s; + int max_queues; + int i; options.backend_type = VHOST_BACKEND_TYPE_USER; - options.net_backend = &s->nc; - options.opaque = s->chr; - s->vhost_net = vhost_net_init(&options); + for (i = 0; i < queues; i++) { + assert (ncs[i]->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER); + + s = DO_UPCAST(VhostUserState, nc, ncs[i]); + if (vhost_user_running(s)) { + continue; + } + + options.net_backend = ncs[i]; + options.opaque = s->chr; + s->vhost_net = vhost_net_init(&options); + if (!s->vhost_net) { + error_report("failed to init vhost_net for queue %d", i); + goto err; + } + + if (i == 0) { + max_queues = vhost_net_get_max_queues(s->vhost_net); + if (queues > max_queues) { + error_report("you are asking more queues than supported: %d", + max_queues); + goto err; + } + } + } - return vhost_user_running(s) ? 0 : -1; + return 0; + +err: + vhost_user_stop(i + 1, ncs); + return -1; } -static void vhost_user_stop(VhostUserState *s) +static ssize_t vhost_user_receive(NetClientState *nc, const uint8_t *buf, + size_t size) { - if (vhost_user_running(s)) { - vhost_net_cleanup(s->vhost_net); + /* In case of RARP (message size is 60) notify backup to send a fake RARP. + This fake RARP will be sent by backend only for guest + without GUEST_ANNOUNCE capability. + */ + if (size == 60) { + VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); + int r; + static int display_rarp_failure = 1; + char mac_addr[6]; + + /* extract guest mac address from the RARP message */ + memcpy(mac_addr, &buf[6], 6); + + r = vhost_net_notify_migration_done(s->vhost_net, mac_addr); + + if ((r != 0) && (display_rarp_failure)) { + fprintf(stderr, + "Vhost user backend fails to broadcast fake RARP\n"); + fflush(stderr); + display_rarp_failure = 0; + } } - s->vhost_net = 0; + return size; } static void vhost_user_cleanup(NetClientState *nc) { VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); - vhost_user_stop(s); + if (s->vhost_net) { + vhost_net_cleanup(s->vhost_net); + s->vhost_net = NULL; + } + qemu_purge_queued_packets(nc); } @@ -90,64 +161,69 @@ static bool vhost_user_has_ufo(NetClientState *nc) static NetClientInfo net_vhost_user_info = { .type = NET_CLIENT_OPTIONS_KIND_VHOST_USER, .size = sizeof(VhostUserState), + .receive = vhost_user_receive, .cleanup = vhost_user_cleanup, .has_vnet_hdr = vhost_user_has_vnet_hdr, .has_ufo = vhost_user_has_ufo, }; -static void net_vhost_link_down(VhostUserState *s, bool link_down) -{ - s->nc.link_down = link_down; - - if (s->nc.peer) { - s->nc.peer->link_down = link_down; - } - - if (s->nc.info->link_status_changed) { - s->nc.info->link_status_changed(&s->nc); - } - - if (s->nc.peer && s->nc.peer->info->link_status_changed) { - s->nc.peer->info->link_status_changed(s->nc.peer); - } -} - static void net_vhost_user_event(void *opaque, int event) { - VhostUserState *s = opaque; + const char *name = opaque; + NetClientState *ncs[MAX_QUEUE_NUM]; + VhostUserState *s; + Error *err = NULL; + int queues; + + queues = qemu_find_net_clients_except(name, ncs, + NET_CLIENT_OPTIONS_KIND_NIC, + MAX_QUEUE_NUM); + assert(queues < MAX_QUEUE_NUM); + s = DO_UPCAST(VhostUserState, nc, ncs[0]); + trace_vhost_user_event(s->chr->label, event); switch (event) { case CHR_EVENT_OPENED: - vhost_user_start(s); - net_vhost_link_down(s, false); - error_report("chardev \"%s\" went up", s->chr->label); + if (vhost_user_start(queues, ncs) < 0) { + exit(1); + } + qmp_set_link(name, true, &err); break; case CHR_EVENT_CLOSED: - net_vhost_link_down(s, true); - vhost_user_stop(s); - error_report("chardev \"%s\" went down", s->chr->label); + qmp_set_link(name, false, &err); + vhost_user_stop(queues, ncs); break; } + + if (err) { + error_report_err(err); + } } static int net_vhost_user_init(NetClientState *peer, const char *device, - const char *name, CharDriverState *chr) + const char *name, CharDriverState *chr, + int queues) { NetClientState *nc; VhostUserState *s; + int i; - nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name); + assert(name); + assert(queues > 0); - snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user to %s", - chr->label); + for (i = 0; i < queues; i++) { + nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name); - s = DO_UPCAST(VhostUserState, nc, nc); + snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s", + i, chr->label); - /* We don't provide a receive callback */ - s->nc.receive_disabled = 1; - s->chr = chr; + nc->queue_index = i; - qemu_chr_add_handlers(s->chr, NULL, NULL, net_vhost_user_event, s); + s = DO_UPCAST(VhostUserState, nc, nc); + s->chr = chr; + } + + qemu_chr_add_handlers(chr, NULL, NULL, net_vhost_user_event, nc[0].name); return 0; } @@ -163,7 +239,6 @@ static int net_vhost_chardev_opts(void *opaque, } else if (strcmp(name, "path") == 0) { props->is_unix = true; } else if (strcmp(name, "server") == 0) { - props->is_server = true; } else { error_setg(errp, "vhost-user does not support a chardev with option %s=%s", @@ -226,11 +301,12 @@ static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp) int net_init_vhost_user(const NetClientOptions *opts, const char *name, NetClientState *peer, Error **errp) { + int queues; const NetdevVhostUserOptions *vhost_user_opts; CharDriverState *chr; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VHOST_USER); - vhost_user_opts = opts->vhost_user; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER); + vhost_user_opts = opts->u.vhost_user.data; chr = net_vhost_parse_chardev(vhost_user_opts, errp); if (!chr) { @@ -243,6 +319,13 @@ int net_init_vhost_user(const NetClientOptions *opts, const char *name, return -1; } + queues = vhost_user_opts->has_queues ? vhost_user_opts->queues : 1; + if (queues < 1 || queues > MAX_QUEUE_NUM) { + error_setg(errp, + "vhost-user number of queues must be in range [1, %d]", + MAX_QUEUE_NUM); + return -1; + } - return net_vhost_user_init(peer, "vhost_user", name, chr); + return net_vhost_user_init(peer, "vhost_user", name, chr, queues); } diff --git a/qemu/numa.c b/qemu/numa.c index 402804bdf..572712ccf 100644 --- a/qemu/numa.c +++ b/qemu/numa.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "sysemu/numa.h" #include "exec/cpu-common.h" #include "qemu/bitmap.h" @@ -30,7 +31,6 @@ #include "include/exec/cpu-common.h" /* for RAM_ADDR_FMT */ #include "qapi-visit.h" #include "qapi/opts-visitor.h" -#include "qapi/dealloc-visitor.h" #include "hw/boards.h" #include "sysemu/hostmem.h" #include "qmp-commands.h" @@ -218,7 +218,7 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp) { OptsVisitor *ov = opts_visitor_new(opts); - visit_type_NumaOptions(opts_get_visitor(ov), &object, NULL, &err); + visit_type_NumaOptions(opts_get_visitor(ov), NULL, &object, &err); opts_visitor_cleanup(ov); } @@ -226,9 +226,9 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp) goto error; } - switch (object->kind) { + switch (object->type) { case NUMA_OPTIONS_KIND_NODE: - numa_node_parse(object->node, opts, &err); + numa_node_parse(object->u.node.data, opts, &err); if (err) { goto error; } @@ -242,13 +242,7 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp) error: error_report_err(err); - - if (object) { - QapiDeallocVisitor *dv = qapi_dealloc_visitor_new(); - visit_type_NumaOptions(qapi_dealloc_get_visitor(dv), - &object, NULL, NULL); - qapi_dealloc_visitor_cleanup(dv); - } + qapi_free_NumaOptions(object); return -1; } @@ -280,7 +274,7 @@ static void validate_numa_cpus(void) bitmap_and(seen_cpus, seen_cpus, numa_info[i].node_cpu, MAX_CPUMASK_BITS); error_report("CPU(s) present in multiple NUMA nodes: %s", - enumerate_cpus(seen_cpus, max_cpus));; + enumerate_cpus(seen_cpus, max_cpus)); exit(EXIT_FAILURE); } bitmap_or(seen_cpus, seen_cpus, @@ -418,20 +412,23 @@ static void allocate_system_memory_nonnuma(MemoryRegion *mr, Object *owner, Error *err = NULL; memory_region_init_ram_from_file(mr, owner, name, ram_size, false, mem_path, &err); - - /* Legacy behavior: if allocation failed, fall back to - * regular RAM allocation. - */ if (err) { error_report_err(err); - memory_region_init_ram(mr, owner, name, ram_size, &error_abort); + if (mem_prealloc) { + exit(1); + } + + /* Legacy behavior: if allocation failed, fall back to + * regular RAM allocation. + */ + memory_region_init_ram(mr, owner, name, ram_size, &error_fatal); } #else fprintf(stderr, "-mem-path not supported on this host\n"); exit(1); #endif } else { - memory_region_init_ram(mr, owner, name, ram_size, &error_abort); + memory_region_init_ram(mr, owner, name, ram_size, &error_fatal); } vmstate_register_ram_global(mr); } @@ -450,17 +447,13 @@ void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner, memory_region_init(mr, owner, name, ram_size); for (i = 0; i < MAX_NODES; i++) { - Error *local_err = NULL; uint64_t size = numa_info[i].node_mem; HostMemoryBackend *backend = numa_info[i].node_memdev; if (!backend) { continue; } - MemoryRegion *seg = host_memory_backend_get_memory(backend, &local_err); - if (local_err) { - error_report_err(local_err); - exit(1); - } + MemoryRegion *seg = host_memory_backend_get_memory(backend, + &error_fatal); if (memory_region_is_mapped(seg)) { char *path = object_get_canonical_path_component(OBJECT(backend)); @@ -487,9 +480,9 @@ static void numa_stat_memory_devices(uint64_t node_mem[]) MemoryDeviceInfo *value = info->value; if (value) { - switch (value->kind) { + switch (value->type) { case MEMORY_DEVICE_INFO_KIND_DIMM: - node_mem[value->dimm->node] += value->dimm->size; + node_mem[value->u.dimm.data->node] += value->u.dimm.data->size; break; default: break; @@ -517,7 +510,6 @@ static int query_memdev(Object *obj, void *opaque) { MemdevList **list = opaque; MemdevList *m = NULL; - Error *err = NULL; if (object_dynamic_cast(obj, TYPE_MEMORY_BACKEND)) { m = g_malloc0(sizeof(*m)); @@ -525,72 +517,34 @@ static int query_memdev(Object *obj, void *opaque) m->value = g_malloc0(sizeof(*m->value)); m->value->size = object_property_get_int(obj, "size", - &err); - if (err) { - goto error; - } - + &error_abort); m->value->merge = object_property_get_bool(obj, "merge", - &err); - if (err) { - goto error; - } - + &error_abort); m->value->dump = object_property_get_bool(obj, "dump", - &err); - if (err) { - goto error; - } - + &error_abort); m->value->prealloc = object_property_get_bool(obj, - "prealloc", &err); - if (err) { - goto error; - } - + "prealloc", + &error_abort); m->value->policy = object_property_get_enum(obj, "policy", "HostMemPolicy", - &err); - if (err) { - goto error; - } - + &error_abort); object_property_get_uint16List(obj, "host-nodes", - &m->value->host_nodes, &err); - if (err) { - goto error; - } + &m->value->host_nodes, + &error_abort); m->next = *list; *list = m; } return 0; -error: - g_free(m->value); - g_free(m); - - return -1; } MemdevList *qmp_query_memdev(Error **errp) { - Object *obj; + Object *obj = object_get_objects_root(); MemdevList *list = NULL; - obj = object_get_objects_root(); - if (obj == NULL) { - return NULL; - } - - if (object_child_foreach(obj, query_memdev, &list) != 0) { - goto error; - } - + object_child_foreach(obj, query_memdev, &list); return list; - -error: - qapi_free_MemdevList(list); - return NULL; } diff --git a/qemu/os-posix.c b/qemu/os-posix.c index e4da406f3..107fde38b 100644 --- a/qemu/os-posix.c +++ b/qemu/os-posix.c @@ -23,10 +23,7 @@ * THE SOFTWARE. */ -#include -#include -#include -#include +#include "qemu/osdep.h" #include /*needed for MAP_POPULATE before including qemu-options.h */ #include @@ -35,11 +32,13 @@ #include /* Needed early for CONFIG_BSD etc. */ -#include "config-host.h" #include "sysemu/sysemu.h" #include "net/slirp.h" #include "qemu-options.h" #include "qemu/rcu.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "qemu/cutils.h" #ifdef CONFIG_LINUX #include @@ -139,6 +138,8 @@ void os_parse_cmd_args(int index, const char *optarg) switch (index) { #ifdef CONFIG_SLIRP case QEMU_OPTION_smb: + error_report("The -smb option is deprecated. " + "Please use '-netdev user,smb=...' instead."); if (net_slirp_smb(optarg) < 0) exit(1); break; @@ -276,7 +277,10 @@ void os_setup_post(void) dup2(fd, 0); dup2(fd, 1); - dup2(fd, 2); + /* In case -D is given do not redirect stderr to /dev/null */ + if (!qemu_logfile) { + dup2(fd, 2); + } close(fd); diff --git a/qemu/os-win32.c b/qemu/os-win32.c index c0daf8e18..ae9857448 100644 --- a/qemu/os-win32.c +++ b/qemu/os-win32.c @@ -22,15 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include -#include -#include -#include -#include -#include -#include -#include "config-host.h" #include "sysemu/sysemu.h" #include "qemu-options.h" diff --git a/qemu/page_cache.c b/qemu/page_cache.c index cf8878d1d..cb8a69e96 100644 --- a/qemu/page_cache.c +++ b/qemu/page_cache.c @@ -12,14 +12,7 @@ * */ -#include -#include -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include #include "qemu-common.h" diff --git a/qemu/pc-bios/README b/qemu/pc-bios/README index 05cf0421b..5a8a93193 100644 --- a/qemu/pc-bios/README +++ b/qemu/pc-bios/README @@ -17,7 +17,7 @@ - SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware implementation for certain IBM POWER hardware. The sources are at https://github.com/aik/SLOF, and the image currently in qemu is - built from git tag qemu-slof-20150429. + built from git tag qemu-slof-20160223. - sgabios (the Serial Graphics Adapter option ROM) provides a means for legacy x86 software to communicate with an attached serial console as @@ -35,10 +35,6 @@ 10ec:8139 -> pxe-rtl8139.rom 1af4:1000 -> pxe-virtio.rom -- The S390 zipl loader is an addition to the official IBM s390-tools - package. That fork is maintained in its own git repository at: - git://repo.or.cz/s390-tools.git - - The sources for the Alpha palcode image is available from: git://github.com/rth7680/qemu-palcode.git diff --git a/qemu/pc-bios/bios-256k.bin b/qemu/pc-bios/bios-256k.bin index f86adff128edd22b48f3ea9ee27960532d4d8a36..e7a7e72e327a9f3a8aa6971e82d92cc4985a22ab 100644 GIT binary patch delta 79728 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 RH)FeX?9{nq&z?A8{tq~S+M@sf delta 68904 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

uDqm|TfjN%te*@MY9xr?q87&Q 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 JObiaN{{bZeZrlI> diff --git a/qemu/pc-bios/bios.bin b/qemu/pc-bios/bios.bin index db835fb5f2b30ba674e60a95177b2a476d24c8f5..b0ae502f6e0e7c1646609d474c99f7e7efaedbc2 100644 GIT binary patch delta 61404 zcmagH3tUuH_y2zw1{id7P)9|%l1IM5BGfPvsnFsZ4cOanujDT<{9@!($YK}$us0y@ShF6Ge}McD)9-lQmtz|+73 zUIA}`&ENp=gUjIgI7N9A6oFmfYj6QX#4Ab*kPh;|&8-#XZmyx>`;^mV0}A9d8dP->;)IWl#YsW5PT2*1bsUx%3yFGm=B%-?|=_F z)2c*8ISsnrqA0h4cfsRb6r~g#1bJN*1^$_pSb1A>8l0A+)zU?!*lEix75 z<2w|^HE6J+JUE01a0*NuNDaz;fE6U2L z2-$-W2A&0Lz^}lYr6>W=;vq%p1Re#$rzy%Z@H2StVMW;n%E5k+J{@5Ozkq6RZMsWQ zI?SLyK^nLhOa{|HK3D;Y!9nmN2%U*Qf|ieP1=Wu#%15(d^&Ca1KUYzf=P1e#zz=={ zGoN6@fHH6qTn0G{6y+%R7W8#3RFrXx6lEXqEmo8>U;;!v0E&S*S5Y>A%OE=smVqBY z6}SK*o>Y`%aO5dP>Giatq=NTB5qPzbAq(cJ2q1U~tOaMld62h+{{Ig@2baRWWl#wY zJj?S6biztSdFVx0wi-fSMq*!4lm%-UA+OPw;At>m9bEvftfw2mJ+CWDAt(hW!P++z zc#EPu@g710%E47I z^L;A$h;a{2gP(zEE5ZqCz~7+NHpV*mvzR=<;3gC9W+um(_ApgR}@CV<%>AG`oI zf)X%R2~lQ(A45VEiz!60f+*kwIbb2k1+IL4R)aNQJ=g#?f-RsZR8ju2C`yA!1gf5* zTmqK*j2O@x3<3RrqgcRiEfgiPB^&`gKqii;v;0CXN*Fh2Z6dVCRg3G|&1%U%aU>`UEE&^8{EYUWgB%xryCZKe~wt!V& z8yIvewgEK7F!ceCfLu@v&Vvp;Xf?PCJP783Lhv@&4=TYW5YdxS13H2sU}8`D|50uh zfaTy5@D(@%s)4l^IvVutttiid9pHgvDgZ}8cpvBn8DK7W27CmLfuF%uFs(1);8kEx zK~})sU>bNlh5lc{&DWquD*XZ;1qXoTHY68J2f3gaRD+)V=oat}@PVdjijoM1fCs=c z;1CFa`2NTQcny32egl@<@eRN*kn7@SEqD)1OveKPuYym(X%Lcu5&_-7IPetcz0dU*HjBC(hI;;Vf8T3EM0qZ~+_!cyn zNhM%9C)<=ka48lL z^Z^-QGHiDd1VD4IqO=DY;31F)UU-ID3nH}8OpAo6(x2J{0? z&|wu54zLky2fu*07pNTMgB@Vae-vfTi;QKE@e+iC!{7oadzrkiFrr>XR={^_aB$b+ zO@lYUX%{~|ULzxT2K==SCA3~q{s8-4$Gn2bHxL+b2MB)?`2elKEubG50jj`&4TQbL z!~*ODx4un0xCnZ_Lsx;Bpx;Ia25$jZ5kChuVJJYecTo)B-p#ZEJOfUHwp-|T@EUjz zoO_QxexER~=p$IW4T}k)i_r&Qjt}_&hryqq!^a3NcpLQi1epN6wxfQx)Bj7kSqWzD zpi*!W{0drsO5cNtU^aLfya>v`5s*-Vkb>uc4;%(VOL02DtetcRSOrdi*fNB*0u>Ct z1ZP0EJv{GmVdFo;h*Tn_pvf^519%v`5AHv%Fec#&xceks1D1m|px$@n1G(Tu5OE4S z0FuFNU^zGp&Vr{;lLwT7v*0RN|2>AP`w#RrSoR~@X=sS@?Y

QIPs0sIX%oe5C_ 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

Y4#y~ri50c1mpT_Lq&?_s{|%48jwdf6hW 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 delta 62270 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 ZO2Ha)hhIfMVEABQrf+1dZ)pjvU;vbWbZ!6u diff --git a/qemu/pc-bios/efi-e1000.rom b/qemu/pc-bios/efi-e1000.rom index 4e29d9d1adba205aaa8759136c0697035aba7870..4bc89a3d25f46fafebb098c005a00b594c13336b 100644 GIT binary patch literal 196608 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

    vR>`t-_MjJ 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 diff --git a/qemu/pc-bios/efi-eepro100.rom b/qemu/pc-bios/efi-eepro100.rom index 2a92d6f32ff303b330fc5ee0cea80260a76db45d..85b7f9bc7f190c02e6bc75ac47b4c871c21b5c9b 100644 GIT binary patch literal 197120 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

    6{4jY2jp>t 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

    C^s9+KS{ETG$;AV$^s z(&a)jwXF(I&+?$m9X0=iT&4KTeqhI5R!s_aD5wbzq_3QZ(v@S zxt|W~J^1hL5$x(CM5;Cr?3@kfh6*wLgGQQ!f0{tIBpf^K6R z=2Ifd3#$TkRg3YCFNeEgBi~o{kRXJKnDGV+mNQ#+p5a=lU(iVx4HlA8ypYo&0@Y^A zYsiWG%aaduCxGa5M*=$`wdlnC1p{}Sp2|HFTS8&YMo}{bZBq)I8Y^W94EF;tuL&`#*8Yf7aZQBu>Fzw}eC#g=w8x4FN-Gd*0NEGntBBu2^%wU1pJZ}(kX_Ek3@ zLN#8ytB^QNr~AhB`~4Dea}mF$NYm9g3eH;fOV+wZf+(+acY_W4mtLHlW45f>e4K^5 zJ#>hYCEVM1U44dFGK8Oyj!rB@BZy5YA>0+PQykGtLpvUGktivx7ovC5jglCX-->*{ z!r&LViyY}Z@50?0zt$Is=S2`BOqONevXSl|DSX-%{y#Wq%4HnQF&oYz=EFWqQxhs& zGt)RdQ9Yl~&!>RB(2CGU7uxQ|rn9_geT}TBNXubGhiRWC36~*ux@=d|dV;Jm8pSnN4DEGnehrlnFCbL|zHOPNlkN zU7b9YD4P?`VH|2o22)R;5WM?_7^R2m@Cv;!m1@IYDOl`B$2Im{n3T7H-UQo8a%v7S z>Sa$oj1%Drr;nA#uR`dDqz4;9Lh!jDXx=pm=`KJ`Fs`5mg}|uvi&y^NV7UcM!0q@{ zVhd)}AiXMk1tdbL(w03E@a4;d>DJaP;0~fjo4+N}PT^sOsNu01M}iW4?rk{O#xkkQ zBn8nJ;fouv^vS@|9;b-SGE)gq1sIr-wzdL=e~+0KkKpvP^LyWea`T)U?EIx%)(O7hp8V3C2PfuBF2mrZnvk?=UG86?H)A&CCNl* zllJKvDw6xg4u5VwVxx{mcBP0E@^lfPxwu8H!Z=g)`b|py@Q9J~w00xjYL{stIJvuC zL71ZpK^vT1$bQei*8}o-uyMnp4NDaV<eAkksY~d2f^dezI5plJ3a)THqo3JFd!I?^}({Ojn=8h=^6x{ zY9gZF?DRyeO1P5bnYwrl!>Z(V+MP=K)t#x5)fDRRT=QQ^wuL3O@~ao<%zVoY z}I~N`WW@%EytHx^9wrBJY~Dm7 z%r0K-O!S{cX^%%xQyxHOY&lSYQKuZO&^J~TT2y?1jCA$q@^n+x(R8$0a96bfj;WQC zW~=7*C7mL+c%0w1rl=-y%Cd#nR`jZhE=(~@7TB8_YME7pT&|Y?vIFTYRwbRkH^*bd z>;?nDn@B!Qo+Hf^ZJMQuPn$8us4dOJ5bfHA;S|Zp?Mm5l6P2xP?C@js8Vyferr~YXNdlaUT(U?FOLfycI>zRvZY5I>O`mQDH{Utg0d1KtnkQtp zI19dBnZ(ZPMU4)T{lnYA4!7kQ1&HUnvm$*u))9!p}b z;fnAZ_YfO*5F93(j&}P5Ra=UqyNvO#{|M9u{+{pAZ1u-WjbpI3dh17*?cG~O2@Nj0 z&49rD^;=*^N@lUo9P0gohR#L)x{-1wTfDBzxdn*G`kmx2j4D7{De~Wyv9j8?4^+H& z3+E5|<=L#+S04v3nje-0Tig`sE4yRCY6=(x>*U)x{?Od)``HF#6LLik5qm<=Uu8Sf zwz;>C8p?m0amJRpzb;YE&+}IbTl-df@K_m7{^EH~lhU465~2)OACZ{Q<3YBC%ZMO( z9E#1St-UZI_(yLs#dGQIn%VqqMwe_bX7J};HPt79BV@G zX)zFcn-6yop@4>y0hpbdGBa*WR`-Oj(i>uK=l#JKO#cCwk{RL(h%m|+|_5O5#5IVJZg-V{Cz(mr9eCT~+4Efo?Ll?XH0^qRoaH$Rkix7lk00I7epO4e6N|wQ3oJ z4f74BmMXKd9cn*3^kf9}S@|}8=(b@-^tS;Tqx1_I0*{q$YtV$XzG)#R1u$8EJFzV~ z5CpGI8>Sk;6Y$kv!xY?zh~S%edItMig169JoSh-<>0=+Y2eDGs#clVdJ#e_@pl$-4 z`nrSiYxOA~leLPG0N0~$9DSzAIgmEo^w`A2gU<6K6<_6fiAag{y?A*|WqT?CV_Y^H|xQ91t@m$!0IQ{8Zzf*abQA z8aWFI3;ug};{~sfNph{zgGbvku6IV1c&9zEx06}T#}?ndR?@0XT(J4V;Sg%d1b-5* zP>@0q7L|aRq#~u4B^@9CDE`)}GLMBQeA8eEMiZ!=ci>7|y^_|ebroDc{Z(?6T`+_~ zp0BI;ma?_AW9g5Sz@k^wz@xD)Zgs)|;myoQ=H`3zbzoKaZ~4HUC6P78_n=f~1T?7c z{Y1Zue|AA*W_%agtvc+W;OaA>bfdQ(x@E5Qi|J%*{G4CG>@C(z^Pu2|HUST~zk`|f z53x%}=%7S8u7>irrF1Ak#cgKv1=ok8=B7vuJB9-Dt@HpsRQcPj#A8NM(bQ+lDBOP~ z*YIXIyJV3C(n%0gYxh+i5o!)21m26|z2d@ov!S`S<=dh)q`>T>BPtP$gs zXued3vj5gc_Z^q7Nu|@DDpJnvSN^&)ROF6J9bguU6=)Zsu=RqAM#ngr6r>UPLl3tW zpw6u`tyqD;7K3YH?olU<_2tHVKAQi$5~if!8iL^YkYrdq^w&0MbPHxyg5?ufQU#sP zvus?s@po=^bR0EuZF??*G?FRDRrrSHHHd&7bF=3I(Mkl7GXH4jXDN_g5Iip6+(NYQ zlWD5a9+QBWSAP|><9%0*Be2f1`40bqTU6elD>z-Mm(HO|qp-s=Ws;B^^0HA{zDYR1 z5Ul3hOX*K%K!)Svlp#Olj)Kha$8z{(%zK1#e~Bc10}%p;)mP{VoCooDeNJlkbZps1 zo5p=6uG^`3*=Zy|e)e32vh?a{TC#%CYzC`KAk2Ac|8Z1maG!gs$lHg#Z;GpQ>Y27J&eCO5~;+<7xuyNwhaEY9mnAhx*jW^&gW zB+=1M06pP+Do?s?noG2(#32se8{MxUr%<IbTWJ zJ(S0e#Ve{vWGXT8K`t;B!_n(E?DW%Ir(E_i6DJh>^!vfUPo=A*?oK^yY&$4&!=pD zj^t&?M|tJ3F;!P|XU|v-s4a%|1OOG`XRt7X781K&qWd00fj65IWJz87o`#yGp$220f6i<~u&$xnVh+Z;ei zG3NJAur2y|t7gvJl;u zJL7g*IL0DyNqr0ntqxyD*d6TuWXYpOSt9sztN)UaT##HsF+9`s!HpGw2gP+noRNG@ zLVZ3_v9)uv=ySMTj_`&_bKk~LyY~Zo03_((TRzK{`p`2HK9sK0G}OrcbC z)wiVoeH?{}eP|gC0dkKBhe8BHKA&^5;0DF>sW=^r{sA)<>8h8E!1YR8j$Y-*ATUEm zHiYUY@n~GSPWIpUqkBT#nkrZ*iClSMmjqqKXtGwla9(QHu8ZqH$$_Jh%IKFN-J{(7 z;g|b7uaF8EWXpfYt-WN)v1!u!nY0Va(M~@?W{*2k)YN&<)$_e@32NrQ;52iq!wh&P z#5O!DgHJezljt;uljw*^Urv`K@_i%EI93Nh)UF}D=re}*qShPUkpEst^sW`tbPZVH zFo>pE&R?qzbEMiF;_VA1(70B!m};jM4LY9r?rV5+-0TW0u)|r~ntJf>stfO+5Sw^d zhdVIkYQ5OuKP;h!+_#9gPrLIe`Gdw*7Z~424`?7C<$936zeSLDNa^{$C0|K`cu$z} zX0sY7J+Z+iE+LCPr8amtsf#J6_b^?H;qw0f8fDx#QVY;rtkvq@D(~)8wr@38w*VT| z+_iY#v4pP?oec3xjaF zyVh}}g0sylry7Z9vev#!ox!r!jfEW&Xt#PatS^!xAWUv7ZTylYxgY0RmX2Z6&pN(X1LEnUona285QnttLrzTe5gSZ%Yj3AT zC_SP4?S1;9w1b7}%CVyDS6+HFNgnYI4H$McKzch2aRM^wnCX}EWIDFzS^ijWn`Rs4 zUq?DlHU@(#p4FTYFnr1K-xIcPOU^7(Kp#Wnb3HJO5mwyJVGMGj@69ORL0vA!(D!E z-J~ZWO8|z_z|XGquAN3e_KY#_8p9sU`z;$N-sWSueK=j*ixe$wwL&~K(tc7}OXe2+X?8t5nQ)^(H98RF zpdXgyqpTVdX4a>8iP5;CelhDvoq4q@{dAUJArt;WB(*h~x~gKFmwg6K+b8HJGs*gC z%>12~*X%_1Lpb7prVdy|mqmeif6EokaIc(6bsu-QB#KMX9;gcVC=WP~w-XmD<|RTW z<`-mn@PLWBGaKN63AwJ%0y}eLlHAcGuJ{B^9l;Y~eDKA34`}b)20CcmAH8)g^r_Fo z0)}Mu)T6xhzba*%tK(Vu9A=7D@vz!Q7SH&}cLuyrF|wS=`%bI51b6fPE^`CgCj9)RyjcbBx*A zE&N74kVIaz8jbdRHRuxp&V9q3onHx0m7v8*3;7!_uG%J{npGZkhnVwU@SQo;`(-1> z(f

    &M^S8qD;S;>G=W#bV=UNj^pv|5A)Vu85)9md}b@sIcyFnCSda>5fpbDr{XyNu{1BV(XtTHZaHjuX@ ztTZ4pmleEp;ys!4`LogIS>DXjU1@slbtefL4-d8~AAI|eW#~0vF-xs=SV@rW7D?Qr) ze7%F=R^*6<;0PGQxexb;SB=k>v(|1kIV1h^_w9|~HZ;z=zHePY!asiDQ1v@|JaelftfGI} zGx|6@qG%Y*I-!o+W=keY)+hxXuTM7c1 z_U%^fz$i7nEkS7Q@^K!)IE*A0UNOy9x)W7xb?HS?DjnG4>uB>`Uxqad_2ps_=FP9& z)O}!!Y7iLx5gA2U`U}5@)VC5j%n)SrJE0(TOJiHhT(jZ`#v-r665?PP-2H(s$C6kJ zI1IN0D^uG(Xj6wemf{5Q+}0E#@0uQhDweGj(tsU^liC*4dInj5{RpW2Rexk4f-R%d zI+o?BeIuiBuJBsdVt|=@0(bZ1cmeCI*+b+9b|MqRfGTfA|R}u6niDA z?3a)r5p7h+vVz@w8B<-naM}RR-pHLrTB%Cg;-Lq~nW=9JJ{dlbH?&E$CIEsXbFW~% z%ChmW9JbS5L;Sl_P71`sk)D5nKEE`r->`c88eleGHSS%UEEPt;yqeME%&-{toKmI( zY#;s1@j3{^;G1-&O1UNq;3h1lH_K7il~htQAcbJv#8oYa=@4{Yl)#V=99yx zJlwT7^WiD=i>uzVg%er0gWj8YFE;Sna7a|p1Qom=ysk~0;Ahkg_^T%%{K7qvP|zZf0<`%VO6YLj7l@+$(}o;x3gcO#r)go(@l_`d>1=9 zW^4q`d^ut5U=a|yJ$hg(@Z)ZP{W2t6t!-(W0+w0u!0>_z_Uvs1y9K6|cP^4P8>7q8 zlr4S$BE`HM61ldl=A|BJu9oXfXEX?((TMpT5~4?YuMUx<=IVFd2m~V(ii(v+x*-Au zAZF%fW@ct)W|Bz)LckzMBnS#@-PtCyX70PY0sBK&`}e)?dGDR?^ZW1@-L#HNBW^nno62t{H03+Z z?a|CBF5~7F+M6qQ@$Q;(kV|@=UT8t7|3{&t=d}gxBlauqTiIOdy+tl7sFQWsW;#vp zv*>8?H?0u5;(7fQ2zI*@dmU>xj~f$p^S%)_I_-D#>a;_wGiT@$ z9uUw9gu)UDDv1b?2^^VcE%2!+=nFf{A(%ryQ@Nwym~-q2C!*KGfbJw&F62JTL3bhy znJ}VN$W`3TiDEyC5~>whDi@IcENCp{2>vyECQKKmE6?KhKF}E0|H19 zBuIuy{9P#V&!fMrcMcSZC8b5mLf?uxMkFW?3^hikIB2Pij+{rg5!DFyB09kx zaE*wL!FNRe_r>dRnUG(}L5HGjvVjnRnv|qngA?FMON*o=OteyBG4xa9#m-`=2}wCk zLP?lyUl=XG!|Vv%$ffvF->;7ZzAFz3J$7C23~1yE_wrrDeO&~YvdPG*1koZCScK`E zpOH}lJR+1N`*Tpfj{&-9IcQJdl6|?WB*~1f7Yhg&9uh2iyes;uyeV>&O0Zfrgo6@p zzCkbJH_VuAg1_EEP)V$V9v~+SWN9M;JLzU*bgtvU>Z{>LgCX1Kj^9VCudag~Uyuv&u zGb}=&B$zjCZYgv1XejBo(8in+r!gi7+FZ;Du@Eri}*FPEFD ziypdezC1WFAH{(N0Uy>^#H|zKpvjMzo1Cf$T`um)7@}@~wlIxOMFkufm2zteG)OPY z-=Bo02%aW6^h&rS$Am#m&Q*$1P{jzEq{dRy6A?j7 zN*W2t@PT1b3Qk~37^A@A_$&Dm(ofwC{UI=bctawm`tOId#{fAOjzW_#diw1 z^;uIt3}YxTC9V?WQm1aYw6R<#N(`Px*U0>J+WWTH> za4@)I7DR<6JOQ;}o*|8GeF`CLZQ$aA4lC#doOZ z1evS~&Ikx@^%qi8xp;NdUG7bSX82$da(|vJR!>arErz(xKdT_<4FJLO_Q%q^r(|^A z9SxkP5ePlmABs}v5Gp}ZMo>+TE0&rZjsw+GcZo#(z4MZK!rs~)UmQZ-R~@MwuRHwB zB87M$5o=+i%DK#Bn5JDE^47@y-il<9VjranZ-k?X?GN)?4$Ua)=%myzWMgU!k4#$W zvEkmFY*zia)tzRTbVxHJAu35c^?TJF)jwQ zDc424P>s5;gbcI8^2I88-<$e_!sSC1L+2#MUB zNuGoNEhRZsD7ihIxFLs;u!kw^)v*iGZwnj7LhsGsSFxZQQm^G-SwfJ6RZ1f>PMmU%|C=*_yN)Q-EGr?~CGoAEI2u z(7VlJ#*F!e+)2fQ)P%%uPaQu1@MGy-j}e;oDLQ?NUp2k!qQBIo;F)E%&9}UhQkN0@qm;&3M}M28t4O zNg+^Y6b|`+>_~CDSrcE)Y0^#EO6O`1HPnYN;&h`^K-iEzN%fgzKze`K<|A#6BDM#i z(fephAehhS!-2OuSQU3!6SW*uS3%8-`)OgOz==xF`|b!Fo3@4%_Oe+t{4C@$J1fYg zX57cCx{OC*ZZT)>Jvft9Lx+}D9c}KKAOwNVn(VZ;29D$S7uHMLA4J2Gr7&HZh)fM+ z`2IR)`Ia?Im)XAvQbHq}>Lbf6ZR0SLz-$uFuCT|@z{+oFS=qse(Z9Zp(f+xf^?XH* zh|5DPlnWMJO#JY%@2z#3@-u=n+lG2 ziJ9jy{X#;DjGRPs&1TD7OF3NGsSP=>v*S*0ic5(&C1}#+6|*VKR3gj7e`nn|+brK6 zvqwq3dJ{>O`(=XchnMnbc1Q<{6X6|`qC|vTQCaZxOk1YxeYvaB8=+++mD<`+B3d6& z0^_<|EX-I4KKO=To8#0=K(Y4+L?3Im?vXF_|$Bs*Mr zit}O+(xlChQVN}o5L>H|qZ90Y7^G^*#)D*ZUp;48#>Z2s``Z^0xe_OYw0hq&(b6z< zNamKlp{jX(N$WM>aBIaP?c)uLEZo#SoI`>Q7a~-OX!0U_(+HV93x}Kh5XrS__TOJWSLND9*!gfY z^7u_iaeS(j%9MyLyiO|@$@0>&e7dy@*JG-njSfrKoyxQ^IAJT|`Xu6abaPXTn*F~u zS#{C7B@{~ABaD83hGTE(VS%IsKRuBfHJw=Z`+k|$p-o^KmqCqcb0`cM+a+(keo1&G zyP4e&#?(`C&iLuurgiv03J%n@jgFmA*BGFbQ$u-fwNzPW?jJdUFge+?>^eg81s|2+*JGa%nt|L z-68?AAA9GsCzePpTObid+0L7LN9}EK(4q}T(ia1C*yjD6%Jwr5@Tt0D*}fAhQHX5W zn)a1rj_f!3{E%_=&B|%~%jIG32Y%|Y_ECF!;^6&in-LWWtye$g$}635^Xdg}gXX^p z;c;3aN(vfJhvdcN;IjPT#IRBdfmCM!ARIGg6tS&8M+2`u$=cDsJDRhFrt6l{@96FW zTV*y#r_-Zh1%M+LJnkExcA@Q`+K5Y->%X=Twql-81n7>G+c2L-EVakd^xUa%6|@2? zW2Etf_{gAXQGa%2x{{-V=TmkF)4mBoXm1q_hg#3fP1ngZR0pGoQHmbWkqqbPP2VH}TEREiF87}XR z_!#hghj%}#pg1mfUcQ@o=SG{>OEWp6uo{n=;^;)3RdiAbBiBFZmbfWqp;OlM91ghj zwCn3=9kK9>w#%G}STkp0g+#m*PM>pk4RW7z9|>a5e~5u{5?*P>xrR=+V8cZgnzLvo zvYNAGBXXa!Y$m$1V8=xsvS8Oj9Uqv7)eB*$Knj-L~@@+8`zDrx+W&+J8lbv6cIUx(G0XODLnPRpB% zU=7{cMjF7c;1Vsf56SkLYFo%lIqy3yB@C8SWnmx#T*6)k|G?zdbFC+5kdQN5d#M&;fR)>^ zt704iAx*NqhC}r?7Pj$ilkE;3a9A@)C#0?IcRwT1!P^@s8QJ1J*=3=6hdX#_U%PK8 znW#w#+bEezg8P^8IW3TVeXU(@09|x5(q`0eBLVpI?{L7aQTE0ik(7T->x@R)t$iuM>T8+2#)KSt+$-G2j>JxHwL8Br&MM7!JMw7Raq4Eu z7ye#SI@Ht6ozR2pkVQEgI8(Z!_8{B4`LXM;EN4n3!b1tp{{V_}+;iNLRP_4HWM7Y>i(x(#mqqoB5HUS2Q$mLJz+54#UZ+MAz-&aXCe``E{?qRg-) z=OtVGk>hu7qzk<*-t6`{8EAQE$28}QJMgn|I9X%I5#TP!AN-w{aoa}Pq$HcLvk)(n zT!{$5L?tgb_BayZ*LcEqy-9+ggr@N=jhE}`!%IKHZ^y3=EXPsTBlj8IS0i77(V%{- zF+jQgBTpe3Fu84=<8B>y$aUi+S9w?G#+TKzOv4N{bzgAtn0_7k*4y98PGzLMZ1+vn z_$&t~6=OOkpz1?Tfau=;@=GCupOb3G(3;-_@F``2#73BF=aR_?4T zC7rHL$-jqi?OfQ~`@rxoZNTXGzp^Ye%0?_RQbll0z-GjUFqW7d(5ty*eR)MOO}(Vc zOEm6h_$5?3#_fp0zGy8~AG06` z{v8qyYYeoFvX@;!_s`9fUBrfu{Mk?3vv$2;?*;vnpVKo(|99iTtjaKaBI?0C{kZLp z`d-y*&Bhqz!zVImp7x$rRQvN60D14}+kXDmCoo-d6AvhvV#n;efBwCIZV2>?x$g5h zqJY0`BhqB}+#||GJqxza^NL_CD-LE*=(~z|6YxC;ev>|fI6+uUa@}v(F~dHh>3Ujxj4qNYtRJSPi=tzR60UO(qIowZfC zepOQ*zjveUc0WAa{o<0Qo_S-AY|N8xR)5ebe6*(@)55MuInvq~dUK zR|XPR@>OP+T4#E~kx3(Iyf@XhuLlJumRgtcDlt!kP6^l~zua>TCq9p5ZN-z*cDVJk z(9f;3W11knaMSd!yLoxC93NMnKy6*ob^N+R%iswCi$acnM@d5+`=ot-AZd5tdfiHJY$RPV z_YWeznelj5ZhZ2QhWqaWSIZo@6$y$0jzi*fzS>WVbbmG%uOa_4Zgu3%iqdamIq2TX z-Bf#>y0<0x{qTbLTIBZY1~*DW(kw`~|q9Z$!L+ z`uf3`+rFUAxp_0qaM4Y6=alDCqI`0Zx1 z5O1%80zIKAueS19(BBSxH1kX-Z?=dGj$TCkKmcaP9y%&?A3_4!#OMr^Uk4ube?1*w z7Oi}6%%syhu7C$ zHOseGHn&gVU8CSVwCOxgrVI%C^vu!&3srW$JMqM6dGeVz8;(pJEGvuXhif~fx0!n% zf}Vn_X~@Ok8;47|5DY1Veg?)~4RL5(`UK`MkA<#933KCd0Q+R@(i-@uVf;qy%YFNX z9$qik(ZI&n+ksaecH~g0oRaSk-rH4BSD}4M+%R@sKd$;289-^elay_5SmnYKo_k*|^gh1WD z=6`(?2;C*#)g=gY?YbA!<;rgKyZ7ccz;}Vh;ecLziC6t4T=YK!0tl{$Q=spAVO=zd zjd4^WEou9(utLgjA3(hQYFZu9j{yH@%1Z@=M)jJ<=5mj3 z8qzjVT}4~bQitUGv-*=?yVCtYwfB3JC%=2qE66whFk!#a2I!B^Fi|+w=bhl6Ka$-T z-fNFQVMzq4xIV%km$&(iZ)ZQ=WG~;How)taGayk8Q7qLfDBY*t#xCS~q@WPmE^g^g z+zf^dd>-%S7Q^jh(OztdFLCT-MH{JLKe97y+@_AwqrY#kB)7J`KF;tK+7vu_LCYMO<=^?N zlYPo%{2WIQy~Z93;NOJwzEx~V--J5fJySP#=^S#soH`vi6;uw*avZrJC-CHGmr-)( z%x+Al*WQ`(Q%|*869|)7s3-x={5HorR2HHfo9zxMV9$3M0zGOdeIs#qqKffI>>8S29rg zX0buiET%G3^*Q1)sR3Y+JIqV=BE4RkX^5$XXh<;95Bq_HD!3BuOpe4u5yb}tOp_-t{7Xn<=hfUm8fQD2RsI{23M{Je;^(PS%pEVniQNmwMpnWJR66FEy_kg ztH?ZJCsw(g;x>t9HrpX(0YK5W`pfYB@CvX-)F3eSR_b7rK4Ej4zQ;Ud96d4|MZqCw zv)1zO6n!3Zh^AR@%r=0NY^Ih2k1N4eZMC+-Tx)F5JM2aNIC-YAz*w%Y-rL|lIj%S0 zbMSHe3cpC3q~4%8ags7ao2wyHo1-hzd2FM$M%$=9t()0pYKys9WmS8uyU^9*7I~Yp zM?0huS%;%cPt{zxa4ARcq4m^qZEmffnyC%d5JlK%&CrTt`CP7Eq?Kcnxvo}FgJVf+ zjx|ePpru6ZTt}_9&_^C5^^_*#L&H{)I9>uuWhpJomg+&jf%lhkeXFU{+;!$Y=aCP} zxx!)npef0$At#DMZlZ#rcreT5T4SrON#CM(*=v%s)ZXScevhOTV3#`|Q-x;+V^WY< zDx2ZSyq{e zdQ>#7k$6i=N{$ENB(+iGsr*ugh(v>L0ux=xQa)M46myKXz*0Je=fUw}-DDOskm*s8 z70Thg!RlgZjkZdzV5hnUwStGR3GNhn8Z(zw%qe4+y@}BZYopv`ZZ`efGsq?MDrP;a znbXQ{ZR`FW{poZgtDDo?9%Kin+t?H-jpxDT?3%|((qM+^a7ONmd7ZVfOo*rc%v^#; zdcCMA%tB#-po~}1wf<7P^-`;*Ok1(F#u~E~-bTGi+pKloc4Kq6ZPGfnf#)Tv*?w%A zw4Uel?;`H1Uc8!aN2iH`1Qn+OO1=6v{M*l%h7Etx-8hb7!^msKs(|URuT_egBltJY9i_0)YYPa;YCftYp zrWn{}2w@qFxBvmW0=_*1-n9hYv=)@L5tOt|(tCVsb+(`2AI&7vQS3B!iMmQ^6gxl= z$su)>&;)b3LE0i|6H~|s;=e;By>hKo3$!uYdv`%hNF(@6D zCFhX&ucAf8rlc^n(oS`|qFcqk3{in4SY7Qu7kiqW6xW(J9o$ly%&IB@3|XlP#n}`X z%h2lh&>Xl#MORAo=IiE8id1D#s)ZTvq>qgYEWyu9v(S+hGb)%8R3rJ zPVUamN$x4(Y3}*XCE*p}Rqi$6b?yfLW>1S(mshV>zt@o0u-Ay^t~0j>?WzXC%IJJc zBio_L#454(RS%+>Gn6n(^DNG3<^#?ahQJbnBzCMW0h+*u0F9ob-k{!t4n_&6Xg=(B z4bRh4MGC!F@+^OTYAVzN!A`vCUQvV1H3p@+r2uibIM3$8nBJ`Wl3d-(YJtcvMefAiic-``dx{SPs zeo(|;fzB(;6O{6*y0%}Eb`SL)b{0L2zDwQ$xQQ;K}%f+P}emk$D_BTKt$; zFFYsql%X(NpMfjAsI&kM0Z-+b=_xESzQgQ7qd$SUV7bK1ebTZA?f$yl{>|URCFK`t zUlYKKw~HTgi&S8M^kdZSs<^iWMIWrsE7@Br#hUl1GR&#wNIxUsz*qsO2i*30;HQ=3 z*^fJp?#2-_KI2@&fau-~pe3T)rU`n2~il)rA`=ohSjHc+gD!f_$2S ze2bV@&o*c4hiM|CDn8*^c`T?|j|{gACkNA0cr~28oP(T;oI4zk-+t|0iDnwvh)#J;gk`wp7|(2B=6OGiACtTp`2E!XU=t&u@i^PV9;nnRBxuNP zYn=Q#`!xX0{-|-Qc{?Ef7C_V6gcmBn!7g{3CCPQDO`Ucby&n4KHPoeO@~w{lSFYiQ z=y%gwFz*we7$?jhl)vEU~{2cXO={x*G>Z3owdPtV{ zsFUp>;xWMMtRfU9<0IL>F1osMUVS!NyGnalDbg>OWc#|&qWjt-*LWDYf*XnYnY)Q$ja#L@A5tFdPQYyLUAVPgo%?} zDu6isLZQkx$#K>9VwqpQ^V<@x>l zTS#9J*0-1ux)nyZqe*MVCC6GINRXCy#dqZ&cNE|;7uV?@+QoACXQIvOV0I@%$ON1x z{y^W|JQ!Vr?GfV<|FO!$;_PC&>*^LYA=K7G0#KqQSQB9 zY`1;gAVzS`d;PV(%dh*}29tqIV0N*fLNqY!0#@ZJ;ZErR^$GPE^+dGy6{FL+>D;xT zE~lK7Fq%C{8H2|t-Qk^%oR!8^#!6(-mBwSh!!^=7(n0lOsiSJ4@?NhVjsN#r$+^Zw z;4iOII?X51UGpZAJpQCm!2+)x{znPHoiyUudBp!bKc~iVQt=4YGC%y+Z{oPiqPmB= zzwC*+r@8|P(xz}577Lbg{_L*Z#M5jm?$ z2q^CLh%hD06y+mv<+eVDu9fh^IxL=*GTVq~l zQrFo~Qlb!@??p}LOp0R!X}bfR{`6=WIi8-$D&lOz9X-w};(ZCh0+MWeg&XDG;cqiN z&6@CpMGF%9+I)Bav%?S1kIf*E`YmWnltXCZKzIu7-%34#ApSn#4d+a=yDY_eIaeLI zkuTggZ69Jo%&-yA51*x9kLdI^`7ZlGlIa^wiAY?drA`z|>P5jW=LfR9TRYl6y1|9z zCrTMwZkh3o@H%ymGuU%M2t;<7^;WLwc1E$39W`@v_G{!aeTDAAXLyaD5YXhw+bTFE zxh5GDy@3P#hYRt?Vi+m=W$a=es5B1-`mvA(0Lv9ap(XZrvNF%y-R{?(d6V;OnHWVV z0q$LV9{V^1cTEhBeK~HYd%3B$_Sl2k*I31;=EA+k&-TXT8K6jeNj(mqaI%<8p~y07 znwxOm?y{aBfIh}C0+^5q`8*$FX9iAcPHS;PI`KRav{wjpFo6ZUjl7-kIcqsbIcEe{ zIk#bHH(_ad=0Q}x?|ja7=W^t8CEl`F1gk*NR{nbJI;mdWJc3-G%eIEx6KxHm(-+Ut zRYo6loggO>ZoWq!H}{MG-u0i`N{uYK*@l_^l}!o7rT(lE+=2Z3fBawA0kz5qkb5K$ z5O@%iP*9Nn4K5J;e?vu4Y2{FVSTvA-Gw^@&h<_d2|KI0dOrd~+7_5PUP$-7<+%%Ms z^?NPT1Qru7!w3ZiDsR&&33bRIjzzbYr-=PXpVb?J5l3MmG~SD$sX^F)Bs>-8&Fa@M z^ebt?jgaL)bf9o5ZwkBMBpa`i6)7{3r#1_hEyo+5e3F&NV)-4X-CwdKM(%hNV6Eq+*_0`d45^z!b29s(aM zI={}vB6M4`-Yq9%5PJK$?iS-Vi1OYoKQ=NG9?`MF?j^rP2sF1`Y2#6Xk- z!iisYR<7yWe=DKXz3M_Ek;`nGY7j~trj#blQ1OSxy9=t)DunD6^eda6LIHkx0#V&I}AtRGQ7!KO56mIY)Lv zFPRqlm;+~ThPtxG*7-+@>r~$8ltkh|5D;iVK?EaJy56%5Q>N!}bOdWbGIz{0MaSO_ z&gK5R=S|>9zn5(KL(^DI((KVRyo9QiSUM1)mKNd29#AMf(93yC`L)INPi4PEZGnBk zg~Lb2P=jY9{j@Bk`4$z?%2u#=qgY7 z;!cfO6VB6LIYdC{y`AOzg2p}2_xAm!RDRwvaTJbgzeg3`p0dm12*d%|k1pjy6UB%q zbK_1`x<^5FNG{pFwAI$mf{FL^A2%5R02H6a_;k~h`^@mKu7_oXB-UOMj-z7&ZV-7p zr<&_#Ew6zG@J!I=J4*Byz6oGWc5!x1Olms*9&P-+lH|CjJ)jdO=<;EHWo;j%HmkZG zyyB%pX02&cXwXHPNGZaL&vS%$YsQxxm!!C1*J!AN9!Mt$twRHgn;)nJVUW1p0%*!t zx2_3Sh}0V`GAbBbqw5*&b!^-vJL+2l=ecT+O!f6AVO^e^(gv2>*?3*=+yBhE?l65a zQ94;E$Hc}rYLl;Iay#cxZ(+FGN+FXNjc~qlO|yN`d)Cv!c>v+{>&PYm7qvYmue$-0UAguM?li~r?w?F5deY7?51NN(&qQ4f? zDpT5zoo;Zd8wx7_4gU=f=v$m6QP*D2NWWl@)NPQ&5D@{j(6-#7qEVCEVlQ~=b(Ie& z4;zr0D6*MQ6IchpY;ilm*JXp1ZMudbThaY>-QvndoQ`&`@cGP=nix)C@r@4j5*6JW zu)(&Z6^W7GSWtm8y~??>F!$BUnvi^3$lN67H3w}0fz8j~TncK-@OCiOl9Yl-Hm;f1 z4ssArtDwSW`qd=LP_tfVs$eJeu1N|prAV^hmEHGYi4|Q z9TKPXhb~?s|6yPl{&&t_Hsw9jAwWW7xq{d7eaK9j)#qrMy`c#e$sK>Ozwdv-8ArovvCXS^{iOZy(+OMZk3^9|Y&@P)2s1(5D7JLtyW1|E*UmUDRi$uYmVlJNzM zO?3AB$%?)!Fi9_i>*@dQ5eLR_g7&LYjz}P$3H&G0yCg0rErJd6h~}LTN3Wl=xVbLz z7@j|FE0rEkC@Nb?K#URmY+4I~62r(=1Rb9~+5a-j4Pj*|p4v6X1oU5p9skeK;D}*b zPVnq;o)R=j&g_S{Y1p1qMDahL{$clsZV{5ByjLGrVIw(KelO+Sa2J0F>#)P9$@(#EE`d5MV!KTi`r~P%l>* z!Thg0CpB<^U2{*}m%`#VVG3D0A$XbyK_{EWjM+FO2x9gx5nnLpEUOFR#S(x+V&}f- z8SDnD6f{nKI5FE}L5r>S9@?8j>d-$#u4LvSHi=obv_|6Kn1|5Wp2C)ymc&uyO+|$D z!Brl|ORM`oVj%KQH8b!i4S$e-07s~Ox;;op{dlW-DdIjk%~5eSDoQ&lwp3g6Em&VE zMeG5QcqwcK458h-aD%9w+0cav{je1bM@vpsPD4C?sE!UE43&)J9Qy1PWkCiisq5tY zpEOFYVR4bEvXg@N$F7x(wAam%y6KHU1lb_40218}-18p&kc->H%zhJLCU@792QY{$ zbN>`WBay+ZS3ydJUaziXC9F+CQMrJ>Df$c>Po&xUZpUuXcZJzwTb2wKv@xTJ z)MN_u4_f%OhRlNjUu{?cL%hKMlXDU6H7st{o*ue@Dm6KNz|c-lxgU?5=^P-XF&%0= z)mBdcor?>1{}lTP+rW~ya4d~r0;3O&FR~`sOp*wBX?AQ=Zi``dA}Co;Ngalma3JN5 z!s74D&9Dc1$w{1ja?0I~uFxG}>_llaE?~s@nnEChFB2LryW|kWIc7)Wm=hZF927rZHp+WM~?oPD;NjthEgiJCEuC3oHia}=uo!)=>C?APA;4Y{k~@VJ+A z?x0yu0-^6G>TJLu^EXc{(#R`)`?4#<8PRD(8G>A_6hxUDqgVP`VGN19U}g})Ye1Iq zt(@+r9Ai8KDvnl6LQErU$~kC+Zb$N0wVS{Wv?5=kYT>%p!jn*z$OpKL9_%_hi+37I z&mnbA;Tp13+tqED(091gxd_n(v$U_Y{Nyy%?{aw05L9>fB?l`mA$cJzu}H!(ZN`L{ zG-S#44H>rNi|0uhxCfgIaAx1qjg~+{-zX!coC}A&?A#;9vfnPD|FyNR=8rj8L?jQ; zHyBo}padMU+3XT-1{2xjYS5u^5t-p`M8UgCe<7w^Ym@`x+@}HId;vY1_RCZlTKW4C z851^AY-JA|CTaD)xGE6QY1fu+mEn4&?atiTOTuS}WySIK_i5rdUrNa(e-w*Lgm|=W zOdg%6grTo&j6UxBL=f20wZxUMe3HAynjX&7+M%PCrix3|KrB1XHj}F(F0aKZ34Pz7 zi=&WMgris57+?ZbJ`-*&i2{@cmLR%9;+bdeQr|(EM;!zN>fQU*6SCT~Ng#*DQ^ZYK z$zOQVuepYe8HW*27xufyW@P?z1%|c)!9_$?!Ng<57mvYijc0K z^9Jv^ILFvD79~S*kbG~#PA@=Ij21!fJB9vy4aq928 znETs|Q-Q5Ru{=kzA}g1pP=qYeRwrU6nVSJCtH@w0zpU^wbDDE)Axs`^6~AvdvsRB@ zb{Dh!>ynSbW{XSQoT|H|44^I5xiwd*p-byE>+11SI^SCf)k9PxQF~Bo+Fb zNKccFk`UgA(q%_sWCwSX$usz?lgV)qR*gV5B#09;;`6OUG5Ob6QT0|I?>$1xHi~PJ zZ75G$G$?~1H)gm?gqsD)Oc4N^j&hc^h(d1#DjS5*M&!}hwhV1$68P@!ibZxXf*{`v z`SITnI!Y`Gf)1vLtRP=C^kPaxN%i(hn73=eQgtG@Evv{1iXFqZEZfBzebxvg?)%;R z`>~ek{Ith@l}ZfAX}2^7pliFMYh-2l{80<^_M)j2+A8b)YPn+* zRxXhB?g&E>&>Z|wW(5-eGR+R&xRKM(mo-X9J>uph&?wrvu4wuG%(rq%$6_R-2=u2R z(?^BNk2*AW(O`A+pSNQ7nmq#XU56iENH6@Fcbj&a4OyPA7C-d>W4C>#D9COY#o^@f8e6L-v)(Y5Kv#$EQPear-?@>3UPn2CEkzkjJ-(rv;`K zzAd`qXqs<3EIkQ~EaXX_;t_yqm(NbWT9Od}_w7L$r}#S(-K`7Mtf&`8st-GVN4+D| zNEAH*i||Ae4~yuw^B!3I64s!>1Y3<~(VmL0`wN|A&{VTiz-Vo;OXcOKfAsowt{0qE z43oo>Ciaobc=Fpm&FCfbGAF&Vk(zgu?U+>_Cfp(XN zF#Mw)R$u-O!w|gl4t+BnM&Jx;&ZYgpyt*xqzZCT7J61i|A)#6r$eLtJq8Mbgtk?E9 zDh%|xNcT9&eFs?7%FLc~-^W3I*q~f8M&X%FuSO_l&J0(WV4q z*fg`_M(c>y3N&>5qVJckP zNVCre&+}(#KEc-cAmiE5c^rG$K6rcREMHL#oLPbXefki)Zl7*(ye`^Z+~<+^&{>s2 zjRl$}ce?H{nJP?N4gQ&IZ##BR_-uecHgZiiTqW}SaB z|Mq5O_;HJ3mEPR;Vyam6v41EoheV!5L)*(WW_w%$}5 z4ROyJpU~JDI}q=mVc36mtE22#lfkSB-g6@EPg8eb5||O1@PuJgepW`s=o>7nNz6ub|D@n6tTVW8H*?xp$(Z>%QPJ*$=!$* z7^&1*XYXx|cO-J158jzW290<8OElSYImR8rIXOt-aUTixv~__ zm(XO>?;D^}QD}>PioMYtromHfDBy~RRXVB9spdtp37TUKK8Fy5Lq(eF3h`&d))F7# zm>Ei6lRJg$28($_X>jvRTOkC8lKGEqG-v(BD&&1Q_ zY7aa}!||@|840XeD)7qgOl?MvG)klKYUSB00zzxL#pZW3R;Xhr;Q~TA9y?2cKEr?H z_Vj*mREoiW2G*gyxD{x8reGRG=2;lRQoK)+Igvr0QqaUH=zTm?$?$Yi4#8kkK4>-x053~0wI&E$F#PNwCQ1Q(O18H6Z7^(HyvemwUzT=fUxSfnirtPS zs7ZMpD*?rckg4~Xb1v~yG)F_fz2PnKxgz5EV* z@^GCADs)3_Dm*9nC3V&*DZaqNkg$~dzb!Vg$%JG?vJ;dzLY)huAT+0W1!pW=eoVwc z1Y;cr03(9ROCjRsFXCD$m2;h@+~$oc-|CEySGc$R3+k+$Q+%M40dd~JE-J`{GRS!p z3S=#SeIqWG-^T4I{vX7`@lP0@T zhHdeC-n2*7p^6byGDBFEy6yCsJbb((tKzcU>nGDR;uP?pIH6)XlDV|k$GWx@6%A`> zs5{J`HQLpqN9PmIEToZo4JYZxrYaiLH|J;6F_+6!)exY)=WD1}K6!3ZWlZ&ry$sFz zU{|J$esU>cB5y>lFI+bK0cn}(@!d$pv?U`$!O?W-X0i;!uYt9MT3XKr-KelB_+KCO zTm>++es=~}ecV&7Bd|4G2gIV{kJFZ6)Zb^-;vE@1$5>vt&CRPD=Uo*H)+kVwd zKFMloKr=)iP0gPM_~%E`WLul!Z2i+*v?CQ>Fs_v`@p^v+CaG}5z+axjv@0d(D^ zc4&Sz`fYA}_%*|rg_}7~HD)_Bb)Px*jr%vTA|p{pj0}|$GxA9IOcmOqMOLapZd?j@ z-a+9#Mrb+^EoEl|E1I<>x1d;Pp9t)T#{H5)P@_+6G*=ND-eCFG)bI+1%P>f*36@p_ z=3CfYMOrwYUJ~7qN?gkfs--0x&zN;Aa#d-4p`op^XOOAI?dRZF^UVa}yzw|(d?M>4 zLyS(7xF=XV`;8EINM35;Cdca7Hfo|BB*XqZZ5lb>{{db=p}#te{^&6ZyqSRYYTekh zF}SfM!7(Omn3C|AlSE8OrY0oi$)PoJS`(s@#KM$OOr;bqk?NC$cA+7ok(b4b!H3i; z)h9jtKTwdpWRx+5g{$PEk@l$`(*C+}SuaTG-p~YV_ZHOl0-8T738S<%hsp_~U@c7s z0PMw;jtP~MWXi{c%E}^TWic|dE=^0TlGMEvmRI?#GQPcrrR1!!{M5dZ)fDHu0tr;` z*8di+1Tz#+oEoyhU|kyB+t%8=Wcp2?*cmvqz&R~JxENdi_}9{g%WaP*b=Q!c_%#Ty zc?j_&r=sL(jsJn_2!W`%r0KLgkuy(s0RIgM_P{h^pF#WPov{X1;GjHw5b!}*eI`+Sk&i(*=uYAt)hU&Y=TwkM1P@#5X1 z**9K=v&}}M)(9@Y*o?)4-fUhJ2Nof3?rJDWgk*DbCX~&%?G1FMot)-pl{X(^o{dh zsf;#EVakplJp#QT$H$pp4%hu)qKjDGQGf+*)EpoxT*N9L-P#m^25-K=!K`94zp9j(;i_UZ zp?Me5A;d-m8rM6PJU<(oW;`^GZW!=lyxB+!-5C-BxJE-nCee^U1}NdNg+8*HA=4jp zH(O~`uD%U`J*oEx-6^6Q9V%7!M8rUl0Kr0+E`8EAFe@hn6+Bb!M1gq@a=5FvJ zpgBV_72*Po(mvs9;91^zTfu|Xk6W#DLU!oN*SET|kDFL*56lRBOaqXlb>!s0Y|s8-BU`E(gHZS+>v%V{6Bh#=_*xjgNMS_? z(ic%Nay05<<48C59E1*pvKJm7MmcZ}gmFR=vji?2VT%9h2({D+MPF5ebP+ydyz+_g zDK@;O7`|$jh{+igf?HO*2)H&i!xt}}ZhgT)e9ifz6-F1$3;E%cS_jDjX?N2hT`7-f zbWF-U8Y;S}_ZgeR-F|lkZ)FaGEKPT1!Hoc z6p-z4h@ka7fj8e;%&7E388yZ8DJxS<+er*u5VvjU5mk5>G~QyfAF` zWNfBATzG3G4JyD|>OX28))kd8dQy@G&F?q`upOnm5ZnS&glN6lQ z5w}TN*E()>EzqT(U#BWr^GTvjT5!IKOzc1NJH6>tH}=bkTlP!}9Yss6W8MO}`K7I& z$V?bxEwEU$*OFsOmfuubaj3)+wRo08QOjOBZD&$VX$y^9KTtSUX&&Y6rQ9kzP$PH` zhFwWm=vs=(L1$OlrC^l#o&c5F5Sp&@P8F?EQH0wH=TBnL4=4xu?R5i?Oi?KT#q}Xl zii4^OlvEvCG?mws*kGGsj^s8eG$LvS1n0}kem%U%B8#;;J`5S7!^D=ORTRXm!98#SEp8_ z?D^+#0t#^j6lwE$a-d}Pi|B>Vk-ioM9MctQoLveW;PqR~)PT;EW2|6-%tNaYhy{ry zNrFhLNq%t$(N(eLBgT@i{)594q1qG(-c=Za)jzcRPsw}sU@}4lG`E}AU-y~*DOULmHnddYChb`Xa~YBf#W$B zlr#^dc$rC)POS%~GL1sm+fsNoFg~T0qU(hf!k~9cANYkmfP-to({v!({n8`$SGcwR zc>UXBSboQ`^|5ou4gRvxb7SMwc^v^Ge1Stpw&sbNkM$EVG;9f1~E8K8j^&1kt z`BNI=QL%R~vT~#J0`1HjwL*MusuP$BZVuK%;W5a`wntI_V;DICzDzY-rS@#)C|MMDV1xG+Hz`_z3 zOc7GdLt8$?JXYu;^AJ_@1y}ftOkeJ>Q#%ltzpBy%b1>IQN@`%fY4|;%y$4i`c|f-i z5;833#}U%ZF7S4W_!G>Qz7M)KV*kfl!jnZ@ZV)981dkiPj)RJlK*9O;LGSb{yP*ssc`P{NrZ4H_cC4OJ)oXlWdkzi3M27ukY1;54$oh>_~M)hqrm zT+ECsE!fi}C}+J$|60~nawC;(Tr_LnDr8=(oUBFhQi7*lZS3XMb{cTPl3nv~DeT0> zw}5+4NNGT`dvZPG0Ir3Ws~esCXUDjjwS79acMcPWbU&-0eeNW ze!!#H`X>>beX2^&ThIfLLbz`Ti03hHJC;v{J(}3L$q4dpd9N$YTW7e`mGgz16p0K1 zT-I`GWDp5(khWZ?lK-6>*bz};t2ORJKRVK9=TZ#(*MM6>dxIV^Dc{nze~v8uh2P0% zHrYJj0O5Lu+dwzAF!M^H7cyPMkKG01w@FU$>UeMzhnM7w({S(}jKVeKjeLkqSa=DW zHq=ClJ98=5iS+~L7<#9?vTH}R|Jw&TpXC*YK9K~Qh>a&SK)tEYA^8eQRw)p=g)Ocm z#eh?E=sePzqm}~>*CfQPAv2s`NWUbeR4&c28aA?Qr%D6;LZ^K5hw4?&YG;Z`Fpb<7 zuTMNh3(GH{s*q+a78s?*p=|o$ZXg%bfj&ZE?}T3@@oo6voprZ?_W?)YM-w|t6gj{O zU$YP@!6W^F*Mkeao;wLN6AoeHhFj8S_@&*7-<-1Xr8AFLk{oGoD0Do*^7;8gFi@<+&^$UGK`~=3iWM`E2lYI1!^)5q zXvQ7sj6AnE9$S~j9w+MnC58oLIYl@nS7a!chrnWq(_yFg_{F=N=kfLoVd44wf9vRR z^4y`D&?To0M&10LH$cYbQ=i7vv4+3j=W)H?^W#9@b$S#~TiN86t33Qi4f7hTGaKl< z{qu$}?28X%#vHkFTjZ7Me;sNe;~2S@?)WQrVam|X{jHPti6-BIEMfSQ+|^V4Ie{95SbUdbhA1OL$pilu>s^HBh8bYTdM{jj zpsRW9!I1MPF-qyW;@hL~gUP93FotKb6!1UJwh#~&J?=x`WvWOZhm;7;;3u+m8}2T| zlijx&giN4djapAh3qISC9UzSj8H)BXPhhpTB14b_p2C$|^@qE?+;*#aJcJ0n!v-hm zYuSz2U2wOyHj#zc`??WMbz&6#FTyTs1;TkKH`7zvn!-Hft&?qD^0VAR+1`JPypEAQ zfE?abv=kNKR%0cW7VR-e>^D7_?o`r`Ajbe5Ce-Mm&f&+T3Aj|(h#t3T*A-3b`7?vD zTB!EC=LZ8Gd{dJiT7K0fNstg|=+ctME=dA29`}M!_5}c**~FSUEjwe4C4CtI(mQQ& zcHZl;?UTCgli4`PfLB1&*oq07kkez^)E^Cr7}(l;1nC$nBWs}Q)@EiN#)38(rr19=;+ zqg&e!w5CKDP%XTWE<7@HH>zJ}<5kXQ;LJAF(MmSe znU#k5tgh75`?DjtkMvnF4O>sta;z*hmB2m<#QGq>5QcC>p{#z?yO=U-7=zP*G`<)X z7Q=x!4>;BCYc{A`KhN=gFt;`kK1m`yvB-$PKh_lddlouxRBGW7A}W6(M@Az_u-7;( zaK(P5Ei&Appr_y5OB7lwYQ12t>qgb&ufdj0eIS}((_!G{pOjRbk6#&08K zWbP%DY0d##Roo06vL65ct5Z|frFk|L@K7zP(eMF=*8;^%cE9NYHG3A`K;+_rDT<(X z26|;qxcPsvPTieB=&n?~9*c~oF`Yx>yygZ6CCtx*uRku+G}fU^ ze?%Kb_DQac8{%uSh1Xg#>+LZp_3C1!&xvIn#iJxc*$YfdF6%>z3xXN?>`bPI6w132 z&qIoouEewfzP~%C`zU7i6}*b86>HC8}j$`s4u zj5-A;)gYzAr7)FVD5bo+lT+Ew^3qkEn-w6L+BR3L3n2A#m`2e0aqy}{Ja8zFh}wF~ zvJ0lrp9IW!OgL~+xoC@*zLAk@04&dLw_9By@3tW@GYHw{Xa&O};`ZY5`~Wn8J0NlY z+!@cTBWciL7iP2i7I)stdKKRmtLy49?$xm&d@kZCM%7qIpATscRS0{yIB`r$C)2xp z?e`X>8@pP+zl4J>6mE=rgW){z(XX|H>@;ElD?PL95kePH9`q!?r4N6PuoxnKfItqQ z5CXg=^g=ggPAH=W&!={o(0*3vAj9}-`y3=0W$X`m&JOjEv`2Re{=VWA zzze*rIwNjAU(=99V}+dwj9BV0&9)B1LCY-x@bm$Ox6TwatHglEzKelxMP0bvZUM=x zIV~QaB_ZASPrn4Z;n{yLlONjiMY7Qao@II<{9S1@jX$UzhXfGUq69TMcM>eMwW|qY z<)W9nK`ttNq2kOsfWA4AvXMfJA}#2uQ9!8^gt4rH&lr^M{-$hfR*D>g@^#@tO>eyn zr>p49e=fW@ckM3jyJ$Di4Qga%Lpbx8`rJ`H#U&IYfyCQ~gBiK>n zaMdqt_Z|~+Uc;`DM8s$^eWOYs7L)123#o1B@qu6UVotpU!AObpg?_Lui+UmHPOxZ$ zS+osiEndq8l-0OpZG$KwPe~Z*D}=*l0*BP^E~X2;+q;le$E(schZ3tl2PFYY5V^J4 zU;}^n*y{pH>93J*$UE&YKI|4H5H4#A-=|~1R}FwOY)<5O0INt{zA$#^fsHO4H+{Ko z!wLd+ZR6+zOCzD`$5S=(06X_XM2)UP`B7&EMzRLL1*O3uC01{iMqe}G371z1;D^{# z!voJUl<&_{5h*_-Gu8x8!Xk9TDblkXw{m)2r=UPWx1C>JK@@w~!g>~-$k+{6Hn4%g zmUA)xXdrCQ@`&L*kCZDH3RKC(ZyDdHNaDfH*wu`Mae8rG&371l^~2_-xM5FQ5sxFo zk&WwhVINH(mli32@^Ond7xL4r5+_#`pfz%+(9?xmjuE9b!vO--(=ua>g?HEHwEQrl zv}H2x5AatDO?nX zdeoKlD%fk9*N&XN^_4=wHMoADJ3s3i|EDCMk9P|4Qvd|yGACGwjgSa8Ntu^PT6c?I z`*I(O$p_}hG1s2mZ>svoLzpZ7*|$N3@dgtTS#B?-h}`z#S|dacXm>E0Y&-ow4__Pe zggk5#V=I=W96z*aZ~f8Psq9-wsuREF62A0+824frtDDQn4M7tlPGbYA8G8b*q+=9T zoFNEfdI9EDce!HhR_QB~7`*|7V+(j9BJg_vj{q&BP&k8w3%rE1!)1&Xn30a}uF83w zKW;tzojyo>dnh(B8Eppp?Hx3~BnU1JTu(>F$6wms(dJ*`@MkXVFEiT z8mZvW%#C?9_RML+ZW^s9YN%Aop(9r{XCKLtunXfU1=!{(JIHV`UPc?rp?NBYrW7cu z7E+@>GA-!-=N0d&#EU1X=eV-(U!L-~d-(FKIHi$R6K*akVY6hJ5bq1#?GjJgMcju6 z0uOSa+^b|;nhdQ1q`fI8CllvS5-5_(ROzucT3I`lWmIlSe0<$bs?+UK+g7HXwCUMN zm_ZZ6 z;T~;s*OewKp0PigjNrI86?_GeFOo-%D8R2EUUOi~K9PYSCP54eD^l7qn`Yb-0hCsL zSrI*E0D)Qw*R#bqi;CAB3FPp!APWvjw#{A9kl^|j!qnKch;jgGN33Mm0fed5H$8HZ zti2wJfg3f*Up`!4&^}5s6UY;T%#ia?y8hsO+%Euw&7j&R|HNq-J(Q(wXl=Hu8zpuZA;}@17GJ!xEOe5)Lhj3wRY; zGf-PejToY*>ixQyeL*o>J0DS(^?AQ?_;Xge=;V-9^digFG*xn*h`0lkmRQ*g>0-aY zxSqlJ`hu8@AEF(RQ@QBV$(mTw0}WAhUY&}HsSlFAn3>*M|9tesnb2*Hv)D zc2DDR7tp3rHxBhRtP-HC`z8Hi%zZE}8uEYc>Pu?*R4_y6?qrbw`WLiNb%K95omST2 zk@Iw7vo83<7{AY4l=aiMIj-3@r;jR?wh4r*l#kz_N}Tatt6D}Iqv^IVLxeO>!&>I_ zvkcj7S}@O^R($uR!F+4FZ4;K0FYk{*5bXt9&<0n6H26G>^@%6mk zqVtoUb-%4j{iNrfpIqj*n_|84o27&vd4EhKbgef^Y`py;=OMKaZvDkB&N3EeZI~Vt znMeTd^P;Zz?i$RyrSvSzMsNHPv;S1OS^ZI;Vk-*Z>1`*fH6?Md`uyx*6{b6GaiisV z3)DSZ2;{Ck4DqM8T=bq)2;Bz0wJwu?{-OTTc8GgY`>>R?3rIfgq(mdAB`Jgn5?pWG8Ropma!$O|;28SRC)B~$n*$T{BVdI> zAf@w(0)rg^6g_7#+ngCdt!X0*y|z?gbaPyVP-kxGxb~;M46CAvOpjG4n47*=fwgIDa+``*SOOkLih>NY~tZ~V} zqqZdTg{+r1+!PBMBG$YI%HEv4g@6kYxbC53FU*LuiF8fEE^ON#p)zQ>D88K&PotO! zk%aIpwAaG^FG_ zDrrlNxcH;;p&nPn(&UGhbu_tjJOF5n(>5saSfa{5rlgTYT^?+I=iaMMLO>$xK6;mf z%`!`clj%DpJ%Lt0dXh-o5C<*aEH}!&t(n+ArKA;eq?V)n(!q#F65Svk6XmHgMI9^z zq8Akx{&5;9pJr^~h>n|xr}4#_4;D5Wg;~W!LgQFXJm)OUI>@hlUMQ6-j5IJMEQ7To zG=h4LL`TQ?#XhrI;HGL^JzJPAcbriWH&r#eN48cU_z#;6v2LQ>shY4D2;8thM3Odl z@d7yTOmZ|R`8o-OHiZ=|c*F8XfZd!oc@_k|3s|q>Ye-`I;)F64O|QoUT)->PD$nbv zM}3GsbKEwk_6#^I4zI_MWj^bM{}}fy0g9ln4&$JOh~KEK2F7|5>983G2S?;%7Q_i1 zU~EvP7}=^HhXSBS6JHXE!1j@Yp_XL+G!~tYdlI#J;6JyCi!M38csQ~c`2%au*VJe) ze|Gq;ibBY@eCzIi*cJ+$1~{-76)G)9UKn)TNOUe#b?evyBcXo3rCSW7UN!|0!j;TB zX388wbqpzAbiDbg^WPVr6+V3e^ZLh}J|9e)a}To;B%c$eG(Vau6p0hAkBQSymHZJo zhe}Ddr^6Q8s_lJE62OET8qrdB(Srsz?}P&)!35LD+H#KD*L^ zg-aVnc}zFywLjQG2j2m+{+CUQ%xjneXSvJ|U=9lRywMjoz;bpGRHjl0l5Qn|sNpy( zurG92T9%;(v_HCs#c{JnE8Vs9l5(|qf`RsAtmMj!M@B8HuNJMkZCr%frhusnqHt!5 zy@R52dSoM(4Ge;$%Tmw|3qVYPbCaOV0DIuUJdDw52jqAx!+gaCD8?0q;Z^sNi%`jvI1~EYxbF zh?N>$q&UusWgxd-|DA#F+%18q@uV3CP)54yKe;~3bHt`|Ute_#5ooy=XZ6t#DtI55 zzLp|lxoSaNHkTINM@WAx?kCP1(A?ftlI5-pz^)Wfo^|Z7?;OVHfiH{UdzewUF)`hd zPxlH?D){b1ew$SG!9lG6KXeVJIsET~w?1&=>Wt;@pJH0OtiUlS+^&-nT#e77VjJ*f zLu72ytmFj8`8V2fL-+2{4Txg?cYfplcYP;T@1p2MyYMZo?)Kt29~8b@!vJV>$)vXs z&fX>Pu?$?fK$nHoMNP-b<)zH=E|&5iyl;Y>=VnNez5+A)n{e@%uqwnvIZH+=XT&mx zRKJKgFDz|CtwkkLRg}{UvhhtN^Ebq%6wxD~M1I!t!-qqeG;ERQ+OcwQE?n%%+_E>u z6Dbfp!bxFf_AyC|!_>tl9V?aB@;s)oC_0eDm6URgpOY29iWmq&o&iI(0|K{2& zb}^WuZo?dnZriJTB4oFLU`5}jNpOKCO}#lcY17v&(hU096%JKg@PGz$`%4)L|KH0v zHz-M6u4*kZDt(mH`a-7CdS*5Fhdy&ynm;KtcLSsI6Gxm)8&jjyCXc^7R+kJ+Jt}Py zX|aA;OWz7{2@S6e<+3rJJ|6WTC_o>0@&Q4tOQ-O#PEo*Hl+kA_iVubh-%hzIQ+E5$4k zcI)v#v>V@PFX^sn^<~+d>A;b=O(r{YO1A0frL?V4{AWwNew?LYdc*Z6P2;@w0}Y`^ z%v}Vw9UKDtR{xVVL@COZ=Fp-&F`6W23y)YF$>5362n#n*M!q)h>8(bExJ7u zQ|P4aPH0xl{wIE5Z2@{Wi*owvihiB_a90zRa> zi+#jW4+_;xwVZXwkx)h!{2SB+5rpaajH;-`+O!B^K4uvO~57 zSa1n}JBc%iChDU#$c1747ym96UnMS`wFV|8qIAKU?kz!nYTHBoN6~Q%0h{>m*>H+N z;aa4{vW~u(5;;X6*38541~88bO@cwcmB=MHN4$%LFZmF?3OeRew5ZQnbpCNj)9S;N zA8-TR9}_ z$C80^q+1mmNa@^aPmwDlibbbmpxMhd+I4-rt<7DWW%|}2SA>ASi*U6JmZ`01%b5py z#)Pbc!EIN;xD&v&R5BWzgOCO0L|~T6Ol}ph+Q|)}F<<3wX$GMTg%3g}0qK_iZ@erexC*5;;C+rrAlqg{ogdf=oPyAvDkyY)q{UaF1VU=dXbm2|jJN~YV zB_`}>L97}f?%PgtOG2BL@7bnd{!%0pSmBfj!39L7zM2GyYxso^_5EGOo9-)=9mHB; zPQ0b~yVh{3AT>=aLdATJ`GFdE$k}6AGTY0Xc7r&0Qr-p5y(0T0!?1*sPw`};+RQrx zNYnBL1>ia?3nbm*x$vJA&lSzyf`dPgL@*bM?Ga%dR@|dfThGo) z_jwtrss(;j^<@IPn58oaR6GI7nT|W zRYe~RAGx%Jhn2aYr-(GCE&F$EwwO}|j0opQ?O6JmMhFeic#N3~6`@8p?n1TPO3(`g zMv(jwHn25^KE4|KOMf-HoHf7wB^o6ONUeGnU6+DUH;s$8W_WipB@DU2x8MmJ;_%%7 zNbIP#c*SgH(==Z|smVbOU)M%lq;4m$mK|$?d}(R{dZ2|tBp9*eJ1l}%W!1Cozh927YVi8*tOhB z)>0WVUzS#4+W+*UoVaw`f?!D6j=xbITgFkGt`vB?w5qrPuA(&lBhh&=?92-V5u_5b z!RcLI5@t5?j6jL@TAH{#9$hy^b+tZF%lyJqlEXpbVibdIbtqt*Hu9aA%0EMGMj3$e z_kI}a@|D-J6cme3+&rzwW~zcb2MxTNsjkfwv&D{led?F|U@B_AJ0vt7&@Z$;i7uqY zT9Y3Wx@#~j9Tc<<)SQ{kG2P$(m8b(#+D7)L!hK1z;WpZTpu2I_SMuaOEhj>LI};K6 zl6d%zl5^H8V2tj2?TZu3Z9k-J(aW1AWwx@!h{B_K5!;*YEM30w%vMR0(xcxH;W zNZ5X`g}s2^zr(;SJ!Foc9HOI1uIX@>lMD|os`CX(|9C`fzgVc=dj~enePrRYIlgMe zgn`X?ObfuHubeifaGC#}6C26O-ogOtM5(%~hiw~o5tBr$)71z#A%4bd)tet#vP*{8 z%1RnYre#SZPZ0VJ;!^@EJk;(5s#5~7Xm@J#L^YGGwtp>ODZ_1^#)D);m_JeYVebq#fXAia6S13YW?6*`?BpVqyEkCn%D=!#z+$h?su9Mj-C*Kr^GU z?F|G(hKI$SdXRdNyI9k4F91NH^O72O+Ho)BYIc(lr$OmPXJ*FM_|5p8x%QGBjG+2{ zEQaG)cwKRHc;Q#d*2A9~kn(bT;8(u5o9_xr;Qsog9Vnd2^G%>98oP)ZBI?6Ca;DG3 zWJF?Dugk15Pzo}vN;*vlsp!LYU3BwSKFG~s1`Ju!x%DJHLH75~OP=_QA^0+}M+PFE zH&3QH3Pjxyx`Y_AJyh0_%Jy9z68wV`|BQjYMk7(KW4|h;e+X zjGtMXq;(RY@kL|QS3EwuaYNh>y&(S$-W+zW7;tncTz(0g2a*)$v$F>|?fD&xF0{>> zj{N3qR_hjK#=2t?6)|G51>k3%n~q|G!v5(SHWb@V@JhWC-<>hcKbxBarOc2*e8N(i z6n`99+5g5%t7i}B{i2l+Mn<%)kchozpE&uN-KKG1yhqjm^YV1g^&8$Z$X(5&$Wyl4 zQos?u{>6CqPVwq7c;tkIE5kqhyhf(+>3?I~&G2I9VsXQw%tb(t7yn03YB;8(8j#5! z-k7o%g^tX6M$+ulqbG;AJBY8u>+k?x~y=kAAK+KU1>xDvcvPVmh)kK@+7 zR@@Qt$=lu@rtzjL$MwLTf`J!7JKN@OXyK!o6g2NQr07jSAK39aloHUomHPz%#Pf9)|^@HP( zJZI_RJXY2QA@Dqk`e?B?!0yYxg7O3xMJk8DH=@Q)QygNK_cP2NqE{G`0X+XBNkMeX1{vv8Ovma#z^N#vr%}Io3!{Tjkebg)m zVV${y0&pyeULe;uL-7rp*guWzE^c|SToCg~=L|Z1sqI;b=Q zC)P8_qI{zg$S~&2du=|r-eS{b*_3*8m$9uzPxslLr&xE-WI7Y~_G}FHHNuXqpU)_) zH6thrLZ_=CropK;uj(VZB8PBroZHLV7*vy=P0qkzP8Ykw-8eBDH4s4v3P?Ev_B3f%mpitwV z-p0mi!Wj}=cj-0x`=`pw0=NMX6T*DUUZ#5f`E13iX4-oSj?_JDoXn1njF7KGYE4J5 z7qToX?;M8skH{?wt+dogdSRad!hDF44sFD76ESEP5WrtiU-dOq3wHyz3Fm`Hve*(S z%baq_Wl5xM(RyB0 z8Z^(lkuIMtDd%r(^rU)pr)ZK2Yf*PXa)v3}4cgU_gffpGik7KvdWjhnvy+BJTVc(- z90XJRF0EK?*n{E}=Im3!_@ZSY)%HW7E8=A<8*TaoCw2guGY`rK{!#5t9%3s6B+Ces z7?9>&W}ki#aI`GgHHWxtrCK9YtFw&-W2?)*;>4meo+bd2XYYem7_Dlv`(%0%uZKniKk!tbu3t~hdym12SZ9T}0oG44+NbS#D_#r`FVe<{(UQC8m zW6VGK$m|4Hcf)EO#7f4Fh8N*DN#{ z&A>N6Eaieb-yn@qrErd?#u#%6{B%TfM~MX38?DPzq4++91L7T#y||3ZIM@ZyIDJu% z&k6hsP4+55(70s&_NH3FW|i17kSBtqz>tp83N8b;+1n$Zpf?xINt@1bck=>3V0obi zam?_H`I6B&fWO7tHhJ*tV69V^aYT;f6O6PCu=bH2$J5Bf5Cg!tF0pV14OIw)IGpYM zTO>hb@>g6$-soirhlAv|t*80O`9d6kX5%M-Eu5S@^pedZ2ET=$wtCwY&IN~iqP;qQ z=_^Z47=DBlGKdD2Wtp}_w zp80i4$XRfRGyjqx%?O}=@hQ7aCJ4BK2F7c5K(>$wF9N;n8rfl-D?d>{>!1x0TRhx{ zIqj+Ua3hwV+bcg*1f>G*g7~pEa>+H-;L|sQ_tpcdLvA>BQ1N!yvmVsXJZ5>iDTfg?01BITe^Z{KU=t)2s_E{pvNmHUvqfcST|W^w$#!KX)~Rb-XXT7Y*Kb{MZ) zECtwqaRAGQDcvG&z#q0JiND>vb)yZbpa@}qcp5f1((%(N;WT>mXRMDEAt#Yj_}lWQ zNav)W*sVq}*JjI51SrRBOC4ns;viPVgVvvrWC7ot zQ3C{7B<;hXr-)@#f|gCTnYi;P8g=-PIi?IJ83DC(P3AU1mSW1+ zoey})FR36xsQVDdJmCTk9rQQMkaSnnpaW&Q=F|Mw(;USx-~-&{=|s9VP@WM{<5^nl zm1*RRJ|B`;SUbNokL6lKM}1GGI!Zd+XFLh;W`Pym<;Nc9$x^2xmnadXl&gmx0FJ9f zc?j36ENlFzS5Ag;=`A@778oNh-9f!D%)}x-Wuej9e+Q%C$P?W zSKtfZ0TiN+E`XCxfZyj6sxSbM%fRn>x)WE-Y;;+ac4EM}Tsch+;q2!Q3Y<4o%aWT> zTTwmg)G~`^+(uBzMrPZQ6HzSa(>(z{@lphRC3MCJL!^k8iW!xsr6DNK4~)GljFf=n zMRC`Nt1G9BY{->ZdFf?moIsMUo-x>0j8rzACUXE#Z7wB;(YDXG91>VBIO%wm! z(6*_;>zR$~yaBftR?%$;g$6ts<#D__2Rs^U~C^xO|Q3mA*M#jH0>c2Ol7kIv1%tn|4(OBo}Y4Dk3!!Dj`i-F)diDErGwF-4vM_)Od zJ7(b3o71R2s5=s9H_2F6{n9r@_g09(8$XIs^8|f@zzmwcz_>mMkqWNESexQ~V3ShO z%jB7xabhnF7Beq$EzfOYz;>RxNW#+Q;_$b(*%ESWs*R}JNAkq*OUjFO;G`z1@sw8@ zEUtksQpn5)`mggB3=Zvw*#-wYqo=emL!o#Yw|MX{4#o1`9Yf`_NPjBtydf&QZFPRh zIx-$;aQZp@i63`9B;v*i80e;5>P4y`;5F6z#s5$+mRttUk{bAF zTVb=W2>!XG?CGmSev9yK55Qdf$^)0n0vy8z2UE_Dp%6v=zzSP+D~H;(E4onBWxB38 zFE>u`FaA*Tcjl4Ifm?ms~^?U7}CU3~7mA z0N9paig5^5QLukZEh~x?aIC=|2|O|;E;IoU)q!MfT};4@i8~uuwl+1=k*YSuT7%}A z#Q30b6~fnI<5ET-fla5lL$yM`dgJPjYmr2jI8O#RS---L+2xv;K|iNe&+i4?60COw zP1IFJ_xL1`j4@)XD5?%CxVoorsTbN5P6i7ru+yHOvKq=CP@|*MR~2kMZ(u<7r_NBP zc(EqDKx-3F-I=(@Ooy(IQ$wO36q3IlXs7ez@OL$|QuClr7cWO;rbW zHYW%f3!W&>)j{<@ltE*|l!d6XV?s!lglP{JETuMYx>V(sW7Ku9FE{O~a~kf4IIxmx zdg#d}Xbzc5?F(ecpor_nl!*k1`^r{DczV4Iayw9gLEaK6t1HG$NM8YYtH~VK_Jt8) zi^D@LivToExQS*I>(MM&a`Al;`JExe!WnC#fXXEnkzu4ElS)<@NoBjehxeU8D9M4C|DoIsH+t^J#Cq$uB77A6`#(0%m z4|Cl26wG}f5Y4t8sII-IB=e@)e$2}tlk(3QP&G?do@|@?iCp1H=L|CmJYd7Csfisk zI^Ks3MQMy_wNq?YR)e|f%?VZ5*PpJ&sLNQ?>ksLQy$EeOUU~pVu+-t=%tdqPGPV?n6^$VqM4pn+MRk z|M7YrGaS0?Q+&^=Hmn-A50%Hb?p1jlBU;={_e%G%((Bc!3(s(!m#e5s7n1M*;MOz< z*Rz4z!LMei`Vt95ZW&ZiDT1!1{E?9(Tg6XX(FpgKkb~=$rrBNAOqYb7zeK_*3{n1s zb?4fXPO3-H2oXJq^Te|*#dzz7OL|oXoB@X!ilXf}&!oFg3HA(j$c&{5yD!yQ#xnGL z=ON_shOUPq&foD%0Lk%Q7)_Lg%2U>5FsYuhmkOEd5#5nZPPlo%)hYJ|)gr-zRJj4F zN14{@6l;AHj@I&UTB)e2cH&hKOS))N#AvrE5iBzog%B?xLsQ_yX{PNEu zJJBg*k~J4$Xwf;6nyy3(vRB61?#VFULYq?X5hjy{LdU*1&XLOO&MNN^bw0vumYQ$x zFQ;7kQ483Fcxx#m>QkK}1d5P%Qp?$n7s88HfR0ujdM)4m?Lx?Xd4$iugO^OmEEyb6 zza#j+mE6*wTX#wPO#tVT#|TImDsT1!#E-C1DL#6hK+&IcZdYmKUCrh6w}1KDB&OIw z0a7g9etns6o`YpeA=QL!B@u7|kwc1OX%gHv7Hkhb(Rm z6ssfWFR&eIM+-3}_(`wfge@`G-X_U}f)I+i^=scc`e|ip9ZmcmfF4F(1dH z73DFoGUY-1x-3Hhx_^=_!$7YFfC*r);Q3yXb|`uEIUW=en?wGB|L)suG7e;32y#f~ z!Z>llhA}v>euSP6?18%N3muIZ>iWpM!w>dENX0#9Ut!D{*-S#c++foR_hW-hE5Z1< zaHL4WPajo;U#xJy6Y}bQs0xbnamN9Wv&RxhKcgmdm~Wp^1op4{bxl;u=w8D@Yo!3= zmj=$#h>N!0jT{V^Z<+N8(4r+CR7xV?2`6K-n}hYwqHW~42*dlZOfev9j}(Kzb;6}2 zEj<(ld*v35fSq^38tt(S95SbA;>5&U6OwBLZ&$?^)Wv`RCf`>B&Myi?+$os&rgYn8 z#Oy-Tun=+#l7|cLG{bIb8~>Nyxr^)+-&sIj-t|$S07*c$zt>uCt0MhY6l2;+W>7Aa zD`^}f^$B!xVfdbfQ*D^ZYl;{)M6bj@7kPGIig={QC`41n4m>!mQBNZjiv7Y#mZ-#} znP%}xmaLD9VfB+?{rHn39pkPxi}WOwV3}`#K8;8{f58|2(AS>sz7gu$Ey#f_`L zG+>u+k$&|fnNf|a!@QEhMTocC{l77n?jK~y|!{Llof^F~^o~S{-4>^+r+w1>P zf&0~(%y(fQi@qS*xZ&a0k*AAPI7fSdi+}>ItU##}7etig;MgADhi<_6Y@xb4kWU960`H>mDHYV#-+_@k zf$MM1)3@sH&)3b1zcv|E>;|Z!`^|7VybK{><{0=Axrs(>q2NvEBcBgo8pYx7VLUzH zP7}U!E=ly`R05U!Mv-hlCHR7aP@Ol}>k^E=;t?_GV5n_~DwS%Prh6K1(l zD9RvYu)!Z39@93PjYU_Xa)!3rf z4eL4P;p7=2?~!1-^PaxS(|EsTqS^N7)35klDkZ)M<#lGdK z$zHY!A9OTN|Lp%IxBsl`HCqM$%MA^^h2p^z?vCKv%+i4{s@;GCl#Eb$5n{_I66x66 zv!gMPhsPj?&u;}G`bN;x)e|)4NEggA;m7nO{O0?%C?xuInB11^&FG{ zt)@MPI{_b`s_qKA7;I!^jEpf4UhGZe&iL33!F#D33iab(1BLJInS{Q&+ znzV{A8d}X|@{}^=gQgP?@Xz z${lC@xpV97+4?S11i&kpyXIKC;IfPl2b$#*-I0APA30?X#W+25b2Pp=wM>4TLF-MW zX2=0+vK*wWa~z_apLcrvvU1a1n;93&lNgqg${z~fO$Acs9o0x((^6P}j$JJwJvDJn z+-T20UQ;soZC4az5ccNYsaxO(h91bF*6zfBzTY6V6B4@yL=n3K139=>?NN;wC>huy zcr|Y_s^Ypl_hOlkt;5*M)0WjVp*n7;>_wwhJs>we5^B>B4qjyoRj7XzVR~?Jz4== zeAA^dKRvGSFH>&XO+_uLpn+EyudA{V;i*24UtSs9%YVI9;O7CfMYGyhvA1xSI3+8a>7L5yz z&FZ$Vqa!EVZ1PeM74I6)wyDWA9u6Jqlxsbyk^z_8TWs`Bq!s)jJi{{u*a)ku;#JR9 z#>e){^5I7I@izxS-m{kVX68>rpu3i1&4XJ^*q zhr(Yh+hbE#IeHFxX%U(rq$AJy|nnMO~b-k`@cbmHCEJQB=mVC}L6o=)vw(g9W(^pTrm1L20=wxP$RW z(h0kpw%S;+Zn8(W>?N?$buwv}GPUEGIEcZDYHbslJKlQfwJ0TG)}uk}99B)hNwIdl zBHr(sEK|0dsQ2?EZFWJ;#-`+5w`XcF+l)#@bp>EY1J^uK;>wX-flz1e3(OQBvQ;Iy z`rF{40RG;(cdRNHka}HA?2Sf)Z`0pHApNNof^Z2!$8 zuvX)^JQ(ndaBS~r?{Wd+yFrlc8C-;AgJ4AoaR+@x34J#Z#M zzS|Pdz>NkM$V9APe0=o8$?ZM&B?&`MsJ@+5Qsvg6lEL~Vrs?Qgz{orhB+YK!y4~kq zn#0C7TR7H|4YWt{6qtRXZCDrjMYED6MF2j#P;tMkAw%?a=bqx_gP4V=x3l{oxa<4E z7U^~T>Wt>!r(yxTV&X$y^?fCxS=)v}wl6B8z^m+*!~`EgTrfb;U<cTTU4 z_xPP)x71DeO737yT?CZ!3Qr0>RCO?y@SDI_muVo#?BGpcBcHG?s`-uLk#&yBN*8gu zH=-z=fZw->U)Rk&)L>tnSB5R$#xF`QV zZObgutGaz~j%~_cxHQ9;aq*obh}%K`a&hXK5rHfu=l}7L65LV7Kr~g_nJK}Z0tstl z)Sc%<>I2vB5#PxjA_lU#0pF&L@PV6C6HvK2T!Kq}3C(Z{Cb{Hil0@Qhtx1veTm-GjZ`^Ld!KG^txgkJz2Z4BUB{kYo zU1b@u=U}U5P*7fD2qV?XZsgX}q430P8vYuxUlffpT z++4?YKVlw{ne%|G9CpL%vxr0Uvh(*%n|{7$H}UR!oTDtr;^zks@z~OCS!eOwcD`t1 z7jxjaap|(oxmOs`$_M~-!}XwP@#CNr1-~=M3w|tO-Pl>@?;|u1M+I9&sGY!_Vgszp z5GQV+I-$BBUjk~tk}79zkIDGOjxN>S*ctaHc;qL1>t|K-@(kG`kaHkT@AVf`+82HX z8b4*(A7z4emZ6_hr63;)Ixyi)YfV4@Dn9Bo7aYP5y^$6ty;|#TCNK z>*hJz^Ax9H_s)eKzX7sR0~{`+@11|m2x732EAcl;S->Q|9ur$4`Lkkoz_?-JT49gK z|1{Xocu#4vj_c7supa;EPasp^>E`|~KMM~juEP)9j5s6nEe>IhrT>S|emuFTZ4d6n zwq$TSG4zM_f|@xL1HXkN3W)#EppuPr@_| zK37$Lb8j3>_&6X~$pbmmRTjYTq0< za}}3*jW$uF5`?}(%B?Q-Z`58kYi$>NJZAWK&yhvEAr#oBs8@7WM#Vi)!!#*70be?- z>V1J5a5NUeSgd#4ej@TC68Pim1#Mw%BtF)w?=}e3o+*!sm+T}_cav-1Q8tIyr<@sw z{~a=e!lMXY_ST5nmJPqHtI0V>Gjd!w86#5QGLS0|esFJfYZk38TQ*_QXalbk2vvXV zl6K;QxC-5Lt5|gm#r$pCnr0ZpbH1atRvaZCj5K3=pWA|rXsE!-atz8J@2Of*n`LDg zu?{99NVc;iiz_!x8JtqP>ooPFjQJnVz_o`~TDxS3 zRtLS%+?xt^tnSjHe*(PJPc}v(+PjVGGFDPw^E)@&BI~qkq9Ltr-&+lg1NMb}qw8 zgc;+iN;;&VU1tvrcE~$p`6IVEw>hqlhKmQw40Z}%7ggSkWubt!a9JnmuaQV*VLGC* z6HmkYfE&%KiSs!bs1qFHwiTJDmFCrctu!=MSIb0&+rsr+z(uMq#Ci{+0O#_`VLjy|B}Nm5}r zwr#9ifqY{YEK9pj$gg^aS{b_TJN~5c-2ouys!^^8Gt- z1x|S{jE`g7PDq(Nki@Zk@q^~7I$2R(S!I8OvZuOGnP5i)G>^ywE>WfTqvcX-naq5( z`V*GAgUePi^3(-tBkX0)1`b~OxwtubRPkpamL-7JQ?y0LXh8?>TeeQ^jLaQ;Lb1gd zoThGAduhsjzdZ7u5WZ>;n4izF(`e}X2|oeZ#tE5pHDMa7QkeZxh?gCmtqZeek8kD| zlhTcwp_?e?N@t5vhBSM!cD^Pgf}G_a7nvS&HfWF!VoBFZ9CUpOI?U}9o!+SGhmLp> zHnl76bCIVVlwD8(lqP^=&q~m|2hY{O_xzCwK+ix`5$`;?kFKG>H2=0 z1#boK@kN1A<6WPJCU?9yvaO%a+3RtTmeX01N2vW-cC6E95?;4ckGb421@+w%;0v&W zsPUX=w#~yoFB>|d-mATfZ6rRJ!umgz`Kc=|-@1YGK4o8xAH%mu0LvBNb8uRhnZYHt zEyekyDY7bSM$4r={5avit&%HUTgK~C!^XAK$(K)vWTJpxbH}l=&MVHaA@L*3^9rF= z#^dax>w$GoBWO(YjP)D!`Gil$`RXU!yb$2N`AF zHWJ7}og09r~oy{Qi48+3wdfj#Y{x^(ivv4SFAY@8^*mYk#jP8ip zfN-Eb9pw||YNxXCGH{pGH-xV^bLcABi0-O*Xb$!tOgcM4SXC1T+C<@(Y4Qge$@>7N zky)UDsO_D|YeLArPYvh{sDqKL0tu1%?TRtnapomVlLAC2lu~uY(EHQ@?6Dipz%k;Z zM{JkuKEnl#pgG|F-kHnqQf>d`yAxj`<`UT-);uyYW%i4z&$d?161g7`aH1Ip^k38$ zxGZQ$QAO7&C#jRI*K4o$QRwlh7o6^1hL5g)l|Sy1%+3^@M{{+Eb_Tv_CZ;cg(m?RDVT`~@>jdl4WS(y28(xj*V zC{^tic?PvSxr|CEt~ShbQfN-A7?VPCx~A>3WN1ltaUvH&o7PTPyfi%QgHk-JCch%z z8{B{Vq4v$JY&bygIR#sBKcJv6mD!OoV9xGgE3||-PN?^2uAwv_%Wc)>Bo4mwTWn1Y zROAxvLzO2*i}-@J1H9pon*2Hz-6nL#RJMYy=xMCTgFkHU&T!V~dY4;)<0aIZ-6^w; zBfH;=XU^IW6f5tvI&i5q#|)kM5SSSW~%`C-bjZQegvZBz5_dH%Z;yBPHsI3vi`^Vo*Q-Alr-)}lS z|D}W9n->609I{NOC%U~AZSBr*zS+Y?D{~l7&yjApJST3Xl0?YrLZWeM&wz$Kt7o=`dKIF z4HL3IiS$vs9P4Js&yClQi{@i>gY*;u<`4vfy-MuS{4=H3VDO`Ttnp_)NRa8lr?jZ! zAxDi&1Tm zt_vz;EmY-D#@SuSmVdk?Klhf>&059bw@2wKm0SaecDs?z&b}3Ov(6`aj)jg<-eQufw&#_<4>5^NR7efWATj0u(H=L>;(g5F>!#nJG5qx5UCk_Ql0`Jb59W7ehxTU> zQM%!R)1V|%r89vMnl%(z;F~yjpdn;=lByX!`Rzu#9SiW?MG|PPL73QA3%`|I` znbYE=pogu$NsA^-U4TSWDo)-*!l@SV;04`WLz zH{=<4V|85O^Ugg_ttiJjfE0S80}R=1B#9P&6g7(GW^L_v|ZYX1kCqx z5zEE2Y9x5cUEWxrhkyZ?z>j~R2&5lp@j+UB>N~;cAuptF1$|ykM0$1~VbOV8hBV;T zF6*XJWavui0w7`eqF)VypBMpt%>;k#W`c3C{(aZCaa&h*Zu6A1(((8szwOA;m24EPV=2(;FrvW!gm zCNo4AOXL+3oTtBLlt7}pI+^tW9;QtkR~fuBa}#vQZFH0+d-KY=7Q9I9cxOqCpo^lU zlN2M%Ou>t0C2pk2n^cDpEM~^%lO(NaH%0>JnF}bbOT=K3M zkW^l8YNy$-6pH)h4it*noaFn>KZiIqx(%vG(z!#(DI%4C6dxe?qKWmmup)}aMqWAQ zDsqstd~pCT+4zp%hNrZ6XA$o_F(d1D*9AaF7!l7+Inlvzm%`qJDdTAkb^Xb8k&sGf zFNNOn`Wz@J=Kz+zxCL?99{Aru8EIdL!fd?*FRj86FqQa}bwzC$FBrf7ePJkyqW>IZKj%3PT2%mLb*%?D-Mgr@zCg zqX(~yHdX+NKU(!83`>#%b+%38vQm#g=ExvY_&El4yPTFgwuxjh^oc-Ph6%kXh z7A5^T|FD~+Cbq6-i_U2jBSw}xg{kb(f6Y26!!iTw3+S}+He5peg?2qs_%9G(%}}cr zmD)Oeicz%cMg>Q>6^EwKi_`Z#4C2x?gVtuz=k&%K;g-?h7nwCm#O|ZT=Co=}tE$3S zP73vZpJb^l9yb9*Cy&Sw2EXBPF{ad08Z*AzU7vS%}x2=@7KE| zlPrUxCtH}#c}IHpWb%&>p#2~kZsP?*!dPP`>vP)~Mn5M!yaN>zN-g%vwLdlK`Y_Zz z3zHdlE<84XCx(dFxKG^aVromm3wZsCObrJY5QRI+7guRO&k+2mK*(GtgjjA-H1T{( za1TuecWVXKWDhH7dK|nlYwai8AqQU>zLJ+2H6$?R|BN|)HeWXaXNZqTC14)-K}L6&+86DHLlCYkTpDmvW-j_S~L~j zcsn2P3rwh@(9{7{ZJ~Aj@uJhlJ_pD=+AcZgMlnKvUt{(y#sAHXkFg#R$Nf)Qdj@*| zb-zX@1EH`I=+NwNR2osJ`@nr}aYFjyJDBEmEorX#!c6RTs%ZD6D;TKxLe4%=>|m^K z(fntWc*yokL`&<$I?VGB*z$^0n7Q_FeeXeTGi0!()*cZkM`Hu!WiGh3rPf0ZXkkkU zVVPru7cP-eqyxxK&OPYF@P{#`Ur)w_B8n9DQ!OwCDj^_OUL9`-YT31ZRVM@{_mL%s zN{bToitud+PC6#+NQBmF^6ea_e((Goq$=+Z8qilX+;*X&Oa{Bbx#e!-Zp*hItHM{k zdhkU^(g&M6rzHe92cX`@9irFa2iIRMS8opwRn5u)xihpiMHu&ASgS~xLdN!3)h)kBveZp&3)Ya%#d+1f!2d_${^{;iX=;_CTr&@#28qhb|QTUh~! z5w5>!Igqv#!Mmf1IcKC*5$GL^=O*Q0quef=XjIb6dEVh(ALIq!8*>PtXtFR5P|gl# z%h&4O%nOM79;KtL?3Al^aE5?Q4sKjezNDjuwWBMNal* zwmUNYYTn_=7pGKQba*N8cyf_>+8Ka|0ed)jakdh_IJ_%n(oKY*Uq;be^_zR7=!e`e3h((=XT;l8`+gY z*U9&LG5~;c9F{#rh50?LiX~vrV4%99`jM#|Kt1Ui5_R%=hFxbK#{jk>`U|el>J7Me z1D>6xsxGC+lfWtm%Ffx;Os!FhZ^oXem#D-Q)%}igOkAb`G@`z;MHHduHf-42$55Qi zRZ5KTX$_~bBvGv$s-^9I?*XU|X-)fD6L~axpk9PHzDcWpLkFT8FO;HM%{nKjnrzsV z%@gO$_Rf!4D-+rolKm?f{HPJO-POUr7k52_xV zWJgK@lfnbG$#Y}xN0fap_1`9A_29M9eaE$> zv8!f0p~Z)Z9mBEgqTT^BBa%7>GHJiO=%c9E4Z+4UjU{pHKT%*>|3$e^U1iYm?fQmd z@bDtq$sy}dd4%W3z?g!-iRqB{+2~*>f00G>x%&cYfgEe&8(KW9{CXvP!-5%?pYY3d zZxyt)t52vdUwNPy#`anpsJFOSa?x*OVtR3`n%A+eKdCdXQ8I)8;YM7=t)p^0ZyKn^ zGOeH`H|6n+N564fLBU&0_H7=>;LeYOs=vUM`jMIE5Hk2^_tVw)g~RWLSYtmH{#qY< zu9S;^B@Yn9IJfv%dFZ$A9aa943U_-R1TgIx4B!}owvSCVfOsimM3;#BX{yhw#_4G$WAFf_W^c~0R>vFd z7@*s9p6g@lMMpICqhITUhW}sPOrhJ4OaqnM^{qMj3d??Uj;_Ft_0ss?Bz*I5r+4)o zQI9t9oa3@X8Ej2Dxz@}CF07t0bz802TJQ7`%|+Z%63%8$=p+glOa3Q$*VkrFm8BK3 zi*($rM7)%)>4>}X`Qn#;$y#oEO!pM(ulSGz#I4JdjY^|*iLILwO2*wrR@wRJv|pxB zH4guGbQiKS8^p*FJn)a*Y|e1d+^2qPxNp)S4DiB^|@ zU>V3*zvgaXH{`(T*939szB>vD@%I=@1;;aPS4CC(%azLz)P7Oia=%XhG8Vn$rmB){%#TCCNio zEe|*tA)U(6CQU5D+^uO|OA>g7F8VS3$ zhy{t^7m&#?t;+KDWk6#+PfHm4rhjs5%7M>ADVC~7j3$%sar2rGlx}R5z0HDDeSBuk zws*Mq*0WZlndKU2cEXr{IOEhhot%dzPca7L#8YHd=8le_bbcSw%Vyf*bt|!*2(=e6 z%M(p?D=6jod)a(l5!3@Qpulq4zJV=od*ZsxW5+nWRxj?Spq=RSAK2d5an_C&g33Ef zWM+PIs`=z_XU>Xl0p*yye`_T4v@>5LKa+`t93tQx^Wi7*PP}mI9g2U?>A0Bz^C07E=v&3Qxu4Ic#+1EC4oY=FC^!W{CwEZsF19jQXan%+Ms{!}tYPsA zi1tc1zEXGMhc4@5DLcmtDl=-u;cf5nd>+ue9@uy>PQiw7l~eMKi!s=-#~xd7%x(bk+WALY9n9}09Iai9Ia0@b zV#f)`;P+tUqp#(<*t;DV@^;Ej-qdmjJM3HCAJo|3+SN}shtq)ABmpJ_%ER*3OmI3# z;1-MAYvuZH7G2oH)pqeYO=(iU6eZ={sWtDGi{hi{gIXQ*zPMsZ^DJJeJADpW{h&t( zcdT+6Nh>&mIXd+%$$n46=U}d_h)PnVgsjd$I;ka+!QF$jVql;Pfrp4J=`?>^s_?=u^d%T) zQBj71>(u%cu~C~b5G-&B@vdg{S?3r)3Eq=uvQSm*9*LirYYC-4VIq3Ji78^dQF2pj zn`l~?6M^f>;%-p)$0-3Bd#P5{U!5J7Ji*(nt&ec}+;UC2MQSPT{r^9zMEZvcD*9e!!I+_dZk;aiy#J~2SvavSq7d|x<0%WMbMCA$jz zL_2Xd95MCfdQQEn9Xi==&v;@s8N_3$A!ON_DX^w`lW5o6N4){FxL%ol?MhPFi!ahG z5tfub=o3ma{PT|2=}OFEF)sfkk3^2!=xNUX{66#YLie3Prl(w3N=T z8V7OM9btHA&hxN$f?Bs*=H2h(dj=_uBYQ@=QdU=NCW#0Xc=r_An_gh2HRcf2Ih6t^p8dJ=twW6_ZLl<(7 z!Q~mL$6flzU6me()hpKCBX&LUX{8p_c`sG3CFPEf2-E&n*am7ls${|DhRX%S8~982 z(_>RM1Uh8s?aDPb2^t6P#-(bxV&Y>55s70l!afbtEYro-Lu~4?gB}eO%q6sh(c(Iz zFAkrtF8{s9M_Vc*h}XxWn#X8U8d@NiV8QuC2I>8RNYccZmR_boMRijOO<+@nXd8+r zUDcb0sIhyz_yl^9~R`ubv79Pe7XFIrOvf;RyS1bUNAZv!S!n7wbIGmw`F6u zDE>zC$%i(CWbo5ie&bvFZujQ5<(K{Tnt1)7cOM6{{n7h4_mrOYy;GBZcY!=Y^HE=4 z)o6B^EOGwda?y|<6|-6(x2P4$=R}Ha3Y^YE{^fl;c-Og7`+bjd$EdKc|8y%``DP%^ zeDGr5w{_xBNEAKl0-#*CRex-KDdOv)k9~h#FmTzfhZ0dA)Is|gRDZ`Tr18~cxpd1& z)8-pEbyw_w)(?!FH$(Fx>ay^C<9dXQ(JzAqzs^CEgc=(gqCiZrtzNH$=yE* z8&XJdMcO-J+7i5`KhRQNNITFakt+S5q4aFibEyJ9IfH>)CsmLK1&f+NYRQNz9l3cV zx+Gia9ZZ8ALd%p%ubXd9vXT_q?xJ@bi^Yk-{#D&8(mEO3I6PZC8id`c(W1Y7kf0Q5)s zrTZSpv1QHrabQ=mf9_k|07Yk`luk*gBpItc0mYrHELRZd!+42X^wJ82Vq!iVuvR>A zc|$=5q+*zF{E6oAnR68DP!-8y-pE7556qN2O*=NxF?ITlh26s#n*Z|HUEi@UU#!?& z?HF)ht0M7UfbmbhRxszGEH&r<>&mkV&|5`%97h5*iXMnL5wt=x;*rYog+^sQxVC0#OS9ly@ z2z(>>d0h|ymd!@xo;7hBmT7*dtuSXMns9$WBj`fyjxy%U*O=JmwuFVVr1wwcHIc?O zEX$3kakNhqCt~7JJW!yMjaqoQ@$&VIvwYsHGffz z+0F{8#C$vGO7<^9q>vT*Yn&f1jf2j_SQMikYRQ%GlbD_0#!AkODf?P7WwbNo`PT*^5K0L%w?~!UfROP;&{gLSJ{b7pjk_IV9AJnk+eQkw7%wZS32)4L z3kqopMZJ_1%*4T0;HQ4Jc1y(GI`+7VMZc8Pluq&Y@v#L7v$kTfmz3|~>mZvH}^0=iH%_Yd~uE4rRKi-@NUi@{!uMD7=_ z+nO$r`^W21&Qs4w!*D!2PSAa4>A{l|_MW6(j)6tH7q!n4&EXYP3Q;PJWQeE4(??C| zjpl2iMEZG{Xe)j2`A37tWAr}du_7()1#HX38`+CHgo`lYtA$!YX9zR z^Uu}X8pBoVP6Hckuy0$+^pk20*-v`eskO4->uO}U zP_|Eo(XEm6SslaX4IYM$Z=XlUV$sU`PxUKEK?ixyG`f5Lb3}U*zva;-9*2-thmRp@ z!+Zx4otd)Yo$uSyH~NWjw}-Rp74tbL+hK~=8f{J6HPt-Rm`X5cBKycP^{;)A}7xT~&RNht-Ww!`hXc zxhn1gDAMXJKee0|2Up(qFGL6}|(^L3J?MTM>(c)RvT=5UleJ+S#?S=scxhb*U72eTfg*Lov zf88*_@35KG%06bQq=Q>aD`;(n-y^ctB-?n9g7~1lD3A=;)cIn&&3{zbesFtN4fSAggB&BuVkBv>FKtYkZvtpn|Uz7gFMa zR+LP(V-_%l1Z*R6WQ%})osu=B170sspBE3XBs5Phvtnkzp__!Rj$s|MT=#f5!exLV z{T$e)eA!|dk6r* zD(>I-zm<|=7WI$i1}@di7`*i&euf-TKD%N>Xdxt;Q3=svXb!6uCa!S468W;fYCI9> zG!{zn#84?&anfuM;-@zbM$$~RRn=W_0D|rJ%LXp5itGsb@zr0DnwrA%!e4jtPOIhS zVqu3l?7&3W+;Mdl{!S#Z6CQ%9b1M(4tG9=ag1AZinQsV-4&WKYXP;%uQhx!yxALy5KKZ(1y<-dn3oWuJ z5RI!GEeF`xIcP#w=jIr5^o`4SJRi@3w>}sG0VrxC;W1bxZanE!w3Q^o8_Slc?m8Wi z6vL0E`Jtcv2RBGBvfciCyyu_omn%!3&>xGkH1UgETCF3rdptghcgarbs(UI&DY-}Q zKsitGO>G2`%yV$%A~9^FQGbp<3dLz=?4ZEU>a}uAiTuLfe#74GpI~IuK%9QJ$S$vXOiVhW zwi^TF;)vswGGQ|W8^LS$+;E(phofi>5VQlf(Evq;fZwMm`4z(``H2|ic8R4XeiGc( zss#1$3dJ+(Eu92N%z1yfsPAxe{O^zuo?Y8kQN1Cr5_1CrX_?uE- zm~wEVlVbYC)wui254^bjydS(E{ktErgR>upeC4vZ$EA4!X9XYUB$Vn#Ow5i_Q#>B? zmd+(cXk60(GSRtGdX42kVNq>2Y9KKEojK40u8t!whqPZ z1z1aHss}+;fsu!W0xm!}t1-Oc!WXN3ZvS5-_J6F1;w`u~bo|^$F>T$BLjg9Y?yKaU z#YEXEL??Cb^^GYe3hqm}E%1*>HVnabUtAksO(Wkuep) zOR_-#B+p3X@#o5`gP86v0;`(ps;i1H6@-KdASfURAQ~V=Q`T4jg1|u~|9<`d>yTY@ zYiql!tE#ttf%oC|?`?Hgd=9GWuKE)1iQU7sl1ST1a-=IHb2pMAnXSYyD;%$gA;>$% zU?==dd^t)U+`Khl%YO0*DT-DOsPX7~svNz;*Ig4xJ|>M=H%7Y zly}jz?p2M3APuJx{}gKUZW7b}A*vrQ+;mJs2uljsJ1_k%KGa~z+LRDBO`SsUMZ$+S z1_@5fX%{s7ALoH4w@pY3q{RQ7mHOY1w7o5iM666h<=fqwnDEm{>kzP|oMxc+Yhe`S zmJPB~RxoNeD&Y$TDfa}^3e%DI1%n5>h4}M}0Ew_t?JXRp2~B*ucFZ=isA&g&`Ff6t zJh*5s$U)Q%3eW>2b{7&(^2?Mbdw#sH z2gnE)Wz2Y{{2aZo>f~_pDcf*cJ+jL*gYmv!K#hURPG^Fg{~tgN64+T%w1>m-flhANp9b@q=DY zM0l>b;MhTuIoRN4v%hJnZKsxIHJ6Mv6YftbO`n$XDUjZDreC_MN@pbN zG?dSd=%%7dXUuirpWmYEOn-jEuT=j1$5y8WrGu^U{gI)g4{}i5%Yk~_6#FCYN^PP9 zSe9cLP$Adr3qX6j+=DIl z@=GTj&HwXUK>oVn$N1}xyYA&WwJ3V9OL&bMAJcEi+s<0_b&f6MD;xvPgO{-_1bMOm z_mxF0T9>XXB?T&4tIKbl#a!jAT9q!M2S&Gv>k<2l^gN$fvalOFm7CG(Obt?pomTgv zp=S8OM5}%v^I}1CL!=MOqP7$n5g4_MLqF5k^^h2$`|5}?mOil$8qNNVL2imK4C~j; zL}OXs9GI9v2&2y=>hpz-`F@Gdaoa`FlGB#CP(1F;x}w??XCuv0?lgOHJj0}4uZUFy zH3beAo5=G7_>=sa#_+x3c!unJa(*emMprIAoiA&9oB%2P3G#O;Z{yS1hk3S;e!Fvy zMkTN$goIMs0G5M5-YIt;rvpU#+7x^3p9MQ^ENkeN&!TG7yK0~6h!yH1_P0E!ZR-6h zycNCQ*8N>{P6B?Ccq@PXd|mo))!E-+mpXpay96- z6cZm_qB)Wn0FnU5@n7)ii4KSoCC?eVD03WgJkeFJE+kjrW6&6z@F5VIT%f7&hp_=z zkrqfMo4I)tY_7H(@kuZu*6E8ttwQMQSZ}64L6C4oM#g3$271f0_dd-K0hfAC~o!rUMUn%)NbNlZ_AtT*C29?^`Bl)wxbKau@6rZ!$x=flHAp z3_GTKy~`L*;RsUXyS~r2L1Cd=r*|CFN&zsN9A4bSn=wAXVlv6kzjWpZ0uO-`ZO%qn zp52@&j*GXyM);x^tG7wHe#!AL=`LDm5kF(Ejcp9j5 z59m8r1bfFyByyG{LvUe>N;-(-VAyie;ua}E1s+|(2L>`hshk+8j#-x63@pr%=n^3^ z9-JQJ1Gl`4Y9k{2!+*j~H!9$w`d8yMrY%gHSZ|ff0QI;AoPGCk{YHFcQqwjiagVsK zl`q=N-zr_Z9hPMbu0o~p4VIAG+2}XCD_iwL`TK9ZP}IE>=uve(BSxJ45}6C+!8B=1 zvw5%RMFAYL*Dd-1=-A2=Y;+OkQ+o7T-wd8({5}^2hWh~!Cn7P#63C(4!u~>) zUN}W=HGSD&llauM|K9_oQMt$a4YKaL=V-|tE;{xy0%V20|qpYmyB$pfq0InVq9K zYK?@>&lvAB!V0g)ygh@8MHdUgJ|x~=6ZbuhsGJeMJTX8R&n#B%&bAh)jSpSFq#u~9 zN_$LM-mvwlY3LZ+4JekMJl4|~TQ(3eHX^)bs~kR8ltc+B%EKjN>>h+Q)znHI??ol* z#N>JJE?toDEg|&Ea;jirSEku1%aE+-*T;;px9w84Nv3u_<=l~2vnk0)wb!dnBv5uK z%AkTdAxqZvjl>`j;j_FU-LFl)Q2$MUe_h$qN<+j0viHXshVJVD&e4t-+~k^1Xjh=%`d+oy76#vUoQ!QHVu6}|J2`xQ4 zZ^=zZ1;&pb&`(jffJO)2{E)o^;ylP+ZYpg+Z}6Dz0q9kA`RL}2U`LLR(UXHT^lgRw zkM$8eH)v3vHMn^YG>|Q>=`B7$S5by8@oMg9TOG} zDce%7NmOJwTp+)kmElvffG;bF2Vj|if&U%tg}d-X&{-$7S$?;g>ul{Fv*hbKmw+39=dSl4|)hAC;qkg2}cW!0jm zu8VI-sYszew%x?Ow_|>yW|b{#_IFwgb`|ox-B?g7>4BkaSBmhhE$+wd72crXfi}n+ zO;y{NL||5-2MhJjnl5=BY~|$O<-2A*AX;11>YCE!{!$Kib(~~^3Py(UxSJSwB-Pxe z$tAZeXg-6@*_ucH@IlD^vevIf&xq?OgB>#*c##2 zn&FRQE<&v(`>j&bi4g4c9T(Ydhi8J&Nx**nL{mBh^}mr_yB#Uq0MUI2;NI)tIW#F( zJTPXwal_EM!zMb5uoSBC37_O;e-5^3<>s-h97qx~h58x5<%S2-j0~M#0urKjE>MGt zS`=U3oZyrruDGl|{O1=fx>i1!YlDs^&-61LYMpf3hCa-rIG;c*2WDw#x+kjnAZUwEGpgO!rGlblJ_$o#eT!xA1hPwd@ z+pz^19~ZtY_p3e8^oP(xloX9uJeTD!cEf#8Jg)g$L7f=?E+-dW zFv6jO18Ae7@z0mWLUN#D2bp7%;Y)%v_goD?oYzyuj@`il{NNzWp}IJD6#DHL=KBT5 z6}Y5HtjsTO1_Hy6YAl_DnTE8Trz&2*+C(ct%|%q9!k7mWQG_hoNGeZN9c-~RC^gnXHh2WYPH{N+f zl9_;Z^&^+_`{)=l99KElm0xfP3*otk^Aul`oq3`*wJRQ`Fz4Q5CMZyL2iH#)uLvsG z-c=SNP^~a(zWsjD)&zj3GV5~!H*r`P6LvZ>ZiHApX%tcB1qM<&X%rKYE^G~gk_SM? ziR_msKy>UOEYTq<^SHH)#}Hp*9!9|8H_65C@B%bKKf9j(huqlbmESvv7;$s#^_S>K zRz^ObT&y|XgmkwLMrk{b;v!-w@gs7&*jVPKlRw$bWuQ95tT9Bn3p;i_(y&N+S5Gub z!Z`-Ezb9NP!51q}VF+iK7~U$r{S5fN?fI>u@@OSxql$$WJTi9g6^ZTn{ghaBebN?G zyMr*sQ@S{`Qm|OvZJr24B2r@R*w$&07hK4H^anx6Zdg~pLa1}YBgKr3jK>W2=(hN% zw!!=*0cT07?(rXH)7>wIKc>9W?bY^@#tWP|WYp$0M6X=<#B?seMoEf$|D4BxJdPBFE4Y zo)@-D`@;Imy%Mqh>*ppfJKAecx#T~jRsJJy*s^%bRfj`wMD$?I-1$o*BxWm`5X0<7 zYvSooW%vW@-Olu1X4N(FfmEo?RHlV~-s!>10VG^o+oJCvQkd+8BsA8k3fB$HKYVQN zl~dW;pnk&?V^jvBFbgM1V^yGIX~YxiKZ!R zP4I3V*=OBi441%jAS7feRv?aG5CBjL?A+}lT6eOegJkRUB$ZpY%h2K9t9mkg2+|Ld zq}Z9hz-p}F9!y63L)DWQ6N=bWK+%&%gAlwtvimA0IBb~^iIFsMwid7|NE|v#50nCX z65Z_f`UGS*DOyl5jsd)>%1Pq48OZxR4)=-jL&c?Oo@)Jk^kW?=D)W(${$VkN0K&6! zZ)~{s*KLGtc3CMlkpM(o$a+;+znF~!i@sM~Icx5eaDf%@|4}#f?q=3G>bA+x?sI*m zBDUkn<+M1^nly^;9z?gtrKgmSd9}NJKgi9>4@^Hq@m3y8-4&l}%%qUrKcBkI^Up1~ zF@uYFzqCA+OT2egqmsc{Nk$N=x?-#y1Q2b4F2=1!k?2JZ) z+02*@ee%B$Gh?v7frc#dlH89xDpcm}^i6FhjtojdF8y+Rz#w2o1prNX2{hlw?Rjo8Q=m>8f~3K#y3`jUfj6dhdtLK~v#k`jj)Xy|E4a2|HoN4H zfZTPr7v%f;2A6|!P9EnYYkFC9K5sM|mQkz^#Ys#9jhrgKlJoY9d(@;P2`EVrGC zGHD<3#ZU_posIA;NE9lfo$B4FgKwC(>z1F)%R#|TZ7n6*A_cuP0xU7=w`LNn8)5BJ zmK_Dz04`QpgZPL$|&~yh&c^er9S!y|p7XPRa z<`S5Vz!AqI9vH>LjN zz?1=T;oBZ*EkhAfr^oT~g(B5|9ivHhoqv(z<$Q_FmL$CG&T_EfeaE0bG4P=w=#|_h z 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ÐoOcYG`rj%qqutQRqA30E2`f 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 literal 197632 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

    iCr5%w>vX3Z8$z)y#1QBa zFBvL;c~1Rp7Ccd>g+IND)6EXaKSqdq9gaSZjCi@Qb7s21f2m1kUL?ubw44%?X7Dyo5vTyaKiip1BxZOHEhjPI8A&{%PTd6753gv%}JJK^Uo0* zUDa8=;(GHbl$lIMmrTHruO~^cS=tM@v;OtoWqo(*%SG36;)D1W@AON57R9);#)T9B zLqNR0tAikal;;o|Kpcr7z)18Ov~Jn#8@1nA)^b^j&0CuYwIqqh=*0ES)1++bjl2Np z8lr&YHhVM{1yxeIJMy)Mamgm4=>DD-WV^^D8*&>7e$wEjU%Sc0c9f9p0Y3k4_=Y_*@?7Ai^}*kWrhNo{uC%wt0m@Hi z%_?4Mba?cxrMfcP`ii}#23ol&cN{v)%w{&ZTm-d}nZ_cPF^zImc<>g=R#iRn@QFN! zT!Ur_21v{WdGdTBgQ8oa8A9sFp%b@vF2(ynajET(Z&v`Le5nRi*?2_)44Dkd?o9j% zpp{O1Fm6oR4zbi~N+>u>QOTR~^-nl*bG+7C2~m%)X~vY{x5Bs`Qq4pnsWdP0S#51z zFLxq$1Kf0dn86Wd{JYDRD73%^KQ62yU4$B%TxVJgMM$9xRIy8fAS>;X;2VoE5N^iC z{T8J#2S{I`VFK#VzQN|k6VS$#g!LBmlJ@Tzv4PqL>-$k|eUY^VbD@2aD^J6XJMf`; zq$di`{|b|$Afd(DhHKlJ)Oja&95OZ31NjpKZfrU8jUixVD>W9GR;%txGW8QM@x)tu zdVzcz3Ni%kSI4S6azF0e@w)cZFT7&CyvCkKtP%9-tA3N|pU_n~1L>CPh#Y;=_r#Xd z@0CDpUf^O#K$ohiUlPMb~+;{~#80nVs zt$+$EpBNxKgH91plODM2}nDr>HG*M~89xQwDFPse7DJ({qF4d*gnGHX%D zVs;2`nM4f_n1ouI(ilSuQ=8J%?#NsOq4fP~_n!t}ma>=Q_G3%d(mNntw#)kSWCyOY zMM>XbN(z%)V9IgtjCEwYaxgBdDmTiHJ%e|?Db+_lzdV3Bfk2mM8E%uiN0!a}oLYnQ z0KfeQtl0KXvxUcT*u(29B8iH+iWVT@;2RToxtI-Uo8iYCu5$J^XTd*3 z@yMvBBg_(kvPWh*;y`KNm%j~E6V5AH_gl8yYys^_UVYce>hs_+4-OKN6GpOIRs^Fj( zq9ZeR0)ad(-hqn^F52wAgqF4-?MLz^`m%r{#EJ$69a8Q0@#e#81SVS*Cwi4lJM;6u zywthhs4E(q#O=@KYjDxEbafW*48Oaw(6ZQJ2ddJf#QNI(&ds0eO6)} zVlE4yo}HR1vsdSQ!#G8M$__3yHQCXAnjuMPR;@hVB9D#!mk@Q9kLhR{uZ$^XX1N3Io8AHkT?H;NSI9H%0ruCwFY9ZBe?rhESc!W3!;#f+#IwpbagHAu& zA3E;GdPya7+xS{`EGsT!6WqZfSCzsG3VUOqz+u+&WsB(bM`S}b*!WooLlCRN#LfPm zL_>3M=Kb7Jr_rvIp?&#FPa!FKZ29*LA}_ux03^as(|;mb_mI;^&_9PYH-14!y6%t^ z!ByxC4IP~m053P5Jh$GE__ueXV*WRLkUZ#1JRbI(+h7L3BP~aHg|tnBzgB_G6ZnH! z6QJZzX(&NYFI0CZ_4oT0lJT)68HEx*1i8ec=UNhBbPA_Qi6p{N-6c{2SCc;>3(uvh znU8p!xeHTxeS*9!M!H;dJt*S$8zBnwx*>|_50v6aLFY+;gze8OlSyZ;^Y^sfs9T(*10&AoFTF&wELyPzHFJ_Qyy-KLMS5z2lgrrXia+l9jTD((JE^~EV2 z4FBH)E-!gGs3#z3N6i>+Qqn=k3iKjC>BhYb;BKxWTon7iR0>ZxX+`gO)ZPhyPeGvRl9DJ1`@0% zy#3Cb&vRprFE!gX=Z+-0Y6ecnmk#6UN9|>0&ruHUTCpwqo!~k7 z9$s#-gfFl#dtW9u35%xUFrU@w^>jj$R*wmRex+$~Vi45)iPVDT z&sL6M=sU+ct-4k)XZM*-{JTbEe9_<*k|kU&)8_@x5>#6B;E2`N`(_`xw%IoKg3pOE zV9dhDgy7OorST`^0B5u=8x+RH_UnozhT@5X8XNi0o#-9#z7R8f5eox%2I7@06ssmC zoHhW^_~;eXD|I35x@|tFW@oo1&ut2tXgzupBZfV~`r7aa9quf~p~GeF$0N2|jK`8M z`D^?9{)s$QS!%C|DMtxeBX)<68@o6SoRc;dbD2@5U0MQHsPg6M1d0U;q{A%&<@O!f z3W(8Y49$sdxaJ{5hR6S_h9>&q%#!==5gh--HLU=6#N!ZB+(D5<16IbS6uY@S7(Wi( z{J3!SRz&f{neT$U1BuSVet<)#VbnYIxQSK-)Il$$TM&=^i3UJaNYb=8$;`#QE*gkP zhNC&-%s__!BG+^xHxO*E7AUf+7{Qww*C6YIM}bS3mf;3B?CpAcwOLH7 z@oBH7O-+YxNce|2rg8yjO_y^ZRRI?qI2-;C^Zq?S$RSt`_dG4sh* z)^iyn$;MAL-Fy#UOJ5-O#e*aslZb@vh-49|9`h26nK2X}g>iPkLPY|ZL!>*J3I&q2T&`NOx zn2WZUl-G=&B;PC9(zSLZPa?u3;NXG zQ3~Bc3ZYY9)K{1#3Qd8(r1&}k*~1tFuz_Hb&Y+WI*&FqWfy|J$r;!XKc_-R$WfQ^( z3ZoXYJ9PMG#Obq4ANL8)H&aX0Qjxlj+B&%@T2KXfbGzf;WJaeLU$$BUPSF)wE5*Hy ziE{Rh=9bNaT^lM;WLHN10{T*elqI;xrNrKqFQh-GlAyL)jR($$RO=f)Dq~8ADU^@D z18`PCyc&|dMOQ5mGb=J#wB(|Hg#eKe;TqPmRy6K!(?K+;Rt>Bn{9kLQIM7+JGa2@f z8K<9Y>T$FDGGu>*k@waFrk7_4zkGOEPlCmC7!|lTxi`)ct8kJICMDcIl2PL^z}}kf z?2k{xV5yw*ff&9+iB=E)$vES9+9R>FgPl@oi-CZ%H6D&56vv@KX=~HqtEWnGQE>33 zQ~}F$i-Ub79k?L<@P_2QDCZ> zHakYV8Ld9>XJrZ@o2VO=c*&k5N4ML;^?`q{i2+C`^ zh{0~8+ecQ9{{X_5STS@hM-118&h|JEZZK1oc$GT)UY(p)u(V-)F2@980Kf)H4L-2? z1t!k4uVWM}T_Rl*dsm0-K0PCas2e9h(I~=wvi+BXpL#&5rFJ{~x8?J3kCHy~Ky9%Y zJ+}yR5s;c9xtoww2NPFh2FYHJ!HY&NE70XH3hfuO=fju806~;rDqR!yO}q9=O@jMb zhydP20%jFb?i`;_QqHsMjGs5EyGx`!Hc#VD%wxQ?_^z>utm5XY+t8mj$}PRG{_Wxj z_?%dRleZ>zz1syom12|^A>78Sx(S_od(mqcsXNv+Y0sJ>1)@oLT%M_zOszdt&wO)F zN+l$|5!<0(13AibgR?OG?xVKi9wZdnG??-nApTA*({e73z8$MLo#$#gQ3nRZ1(grH z{l}1GF&%iz>_Jv=?$06C%>=H^bZ|G{#+&mIZcL4+?+4Uzs0Y$iY}BD9g`4GiUBGPc z$<5qb#UF-(EZxDoo-FBWabxn!8$rNfhwoeym&!cL2q6vNJ%|Ji0+g9WCs>6kxi>#T zO}atoZ_0x$8+^Yc#Pf+4=qnO?7Ba7pdSa!Af8WhSGG(Nv7vC?)nJV}u$u#; zWZ;AN&;SB9fJi9fdX(ESX1;3-`gBasfggFZU3ymGU&VHv5*OA8+v?c2=HL3QY5#Sn zW$;?zVPvMKlo~lhK4gw1@a*hRfK7IaqzS6FGtxo&;uTwsI=Gl8gJGrniNE^LnuV83 z%1n_Pyam0qH~I`XS~kUeCzIub|U(`=9f z!7utcLZ&#P6{*Q(fd> zN9PI>C4Ofm2<)m^#)-!Q7}Cx*N?7nOM1gA57 zWn98|V8yKM0rr*zS8cBg3D=ktyimC7nSllPhgAe~4g^27&lbrCJRw}|Y`^$~?Jg2r z6MR9FCYAodIVahtmrnszXPII|e}Q@nk#w3V=dZKJk+Gc#yZF+O22=h^B5-bH0VS=V zRZO?68HmO0Y8F@iO5nnfpm zW5_yY=Yw_rlD+33@%KEmN#%%z{`8m17qjobuXIHX37NGAEhM3y`hN*G>creX!a{C0 zsN=QU_~iJlO`!;*3ubkxRl1Kk5mx!<%x*AZn!A{(9Gv5Xq(@UHGT=T*aVJNyyNvSk zVQj`UNo1D^NtWiyggR1fnm{VJSqc^ANGaxR*s&og4vfj@kYzsMDfbEN8)P;O6qmyW zu}nU3t`k>!NQ};0KzFO+*`K z>rW0JB0owfZxrtuy?@<$Tso3^8LcN5O=Ggo^MbCnzGVq3KsThzI=uNF>e9YtwGdPQ zDq{2!b@MWmnlrm2F*Z;PN~c2DKD5kY7sCSzPGP$?qegfNF>_@B7$S&Fm~L6(9W;#G zO&VNq*L7yJlFrc6j#ffqaithg277kw#1rtUbSv?4JlwHC+D$1MfVjb~>kUt4)}Q%^ z@9D1b&+tJuP&cqQEF}dL+nzq+J2Bb7jYr~)RzES5fgF#-+HF;0E~8!Sawf^6b0M&4 zt#0B)jfjGlZCF)5ux)+D3YzKk22+aRDXzRY0(r?AYps+9bv}5jn^)?tnsJq3O?BYF z%VvLQp;Nv>j}F&$hdzChE@ZV+P~$5K&_;OI-H+RV1qH{lV0vjdRfHdP)f^0x_ssGX zY}3XD5%ZO1o=Gb5y;O6W)OzB~5?-A__X$JX_0Tjafy0W!ph~eHsjlHK-ar#rO6~9s zf7-oO*gh5wB*ffZPR}rD~Y0ONzavvOim7wGJ91p>Wbv zw3gpO;}4)q;YaLixD~Q07MlCCzt#QpyoQ5t1F74xSDuxnxM<8{T5@16<3{5Sj{=Kt zKCsl)(>IwoUQsY^>+y$;-FQ$b6w(bAmk_hhplGWC(M>DG{JTs^(IataUZo|u_@8$o zJbdemq-%0}auJkeqfaxY`k23vsOLb9&{s^Y+yCcFhC)e+n;X|M?bT{F?{+Z`?O5IC znl{5-)R1lLvHQy~9=h#baGFn6CQicbz&wF7&sB{TNc=I1{c~`;(d1NV%%G3Kmc1Wg zu`q=$07*?R6f$&$vD6$*X+RwWZ(Q?iU~~yqy0j3+@vD%-5GyAUq&96wjoyi}1hGXU z53hS9Vq7bZ6=zo&jo|=S#3b;)O7VOB*6tDQ!S!+mTwo3#J33D#y^rq?UVk?KLZ?|4E$x4CkHR(;}@6VL147< zMlMPKw3Y&LD%2R-ad@lOUy6=b;3dEnbJ6cb>=NV}|Edl8@>V!mr;ar*s;Xn@4SxMS z>EWCb(D$9{;A5q9HE#$*?;E_OwdU28S4+HzGZ1NVbW*BLI8p4^*AO?`xHY58xo@RGD8$ zn#2d{qq(G^32y!j$@CXPuM#ylk8o7+nH$&59qtkX%lMYUhxckhIZw!&_Y#ZoU8K+d zH3eVzf8U@_`+F)NS;IxsgGo8el=qWIYtWwB5e?dIRp;HPGwbRGm)e1i_Mj4qL;_Vg z^eQaiW0ASUwW#!n_y=eo7umBsct6o@pQ>GMKb6xXbEg0zPFSOLA+Q1$ex9;4jM!{M zDyMLswd*++Hc3u1RmwLr5F6)FyN%dJq$elFj8OOtnv<5u-8D}^DdWbYbDM#ItooVA zRwba6M}D%sv`Z{nH2ajqeGmCgx8Y2549Gh)72%^?&0bm7bXB%Wwj)2z%g_X89e;c= zb*3o1j!*;H=hY-bua$O9a%o`(eND>-lmzA4bN$tKCDY^+BVE(BH9*Xk8hB7*CxGmK zoH;Q{EM;P`s%!C;ocPyWH~5ts`-h1#JlVjt6aB&OnHZ>QR2t~;fE(Cae~sa;agN8{Xrb9cS6WaZiu%voe0$OceXhMH>uH_ z4zq5T7;qUaM$KMiNO=>Pmot3A@s8?qQDM|*cy&H?HTgF@efCVX$vl%s=CcX)EF=vL zTQsb~%S^Ga4~)Z-^41ev0^dpz4RHA8&#U`2^XW9MQrb2?%Qv*WOI7My zstj~6u&1h%85O=*6L;h?TT=fI7kvJizwfdo3DD0Bq};ksErsOdgHI&$lsboTCr#DI zK+0(jR$j4^k2%jx`h@L=KZQyNVu098;TBH~N#1m~0gn(M2@=J(g0s$2Z#i3fC8^2n zw<`N6yfOe1r%w$7&Q%912uAJ_xr;I3FpvK1EH=9J|&84-H5lmkz^lOMx`}HG{!~*!jtTG4%pMxz(Fn&yzItEOkoBwURGlx0#|XJDtPhk z22%KiRtRU@Oc7p@g2`I3>rgQD!3y#tmdQi}GJbl7AP%tJo9&PwLc|x;k)x4FX`A4* zQRd{*;UlskRSxn(cz-ZrD(tsp(4)s8#_oz42~dpr5eoUh*(|)wSBww?nwX#7X~Trw z{;*4Ir zk5^$4>9Gx;)83Ks1n^28^BQXM+fb^CTcq;@Gv2+~^0L;kvezONgy_&?&r*emB8Sz= zuEx{5qU(aLd>c&0-`U{jc!;f)5u^;5Dy}$`2UBhf~y5s%Rpm z2T*8x$uPBq8=NU9heBO>)D=IU+;6ruXDvO zbH*O$GA@bDBi1%dHKEO!A}}X&ue%X6M88BxVS#ifw{nZ%TxH$2r(ZTLSq$91==6Ex zOytfuZ7efBFBmO<-qh0L##$SzO^0Z2ct51t)95$&Gi67DH!OltYi)Y{MPv-9omF3! z`GHr=$Fg&7j$_#he>Sba=nar;bbU_J*RS=f;Mc~Mh%UabjrPZhe{UL56yA^YqE`HI z4)r7yyFZ(XRIUU;lMGBqG%s+kWK+}dw2zyHQ5sShLJc)e*4M9Otc4_hqgF8ma7k9& zagxHH&i$1hiFLFTt5d+2gPONiO?>*jtcv}dU(j!$@~;iX{pXTZL@Eh+CB6ZqdYQ5p z*OYO{b{4&U3@Orq)9Xu;9%^lHw^yO%`DLhBcA-a0i~Ad#p3RNZ2uKSE2VJOz>GM-SK8A;sX>g#R7GqtpJm%c@gYJphmR? zc|(J^{lrLW-3K;w(_}n*ecn7kYU`&0H+D|Ud!l`p1MBM+o-M!mdB@xAYf*v%$8N94 zmshY-dg~2yJ-g7ZsDG&IxdS?NLs0}xs9OGck;7Q}oX&Vbk4thJ@Nw*b_9d5_XU;su zJqD`Om{4!tQwU8ixo`%_a0!bD0p7G2x(XcUg>o3znnCF05$q zr}yyj>}pTjTlA|qU3m_LXF@V<9$s&8`TT*K?fb=%gB-5v-VA&B8M^7UvnB1(u8v_9 zz{z29!(u|kW{w(Z^h~H~B?X}p#`X8SO0DqoR$EoySH_j6!?n5l4JR&i`5nk2a3tP2 z6+3LqTLW5-DR!@t7}joXK)Nzv=PCX;*&>3Xmy|^@8t1xQhs{^T^|rS?NM4EJljhX; zLz&Ixj_C-Zl<&;ycaR%tOKg?iLW0_2g417yMwZco_Q!zV#kINqzKLKez6dF$H?UcF zv>DZIzd$F=z&@=lhQw?=D2LoJ0Q^|gKzn>&t+*WW2vEtMu6rAlq-BMPmb zLkM7Q0aA)UQxngbC!B~_a!k|igs#`r^;d@?J|<7+3x=p@jBs82Fi5ukqRTyfswg)$ zf~!rXR{;CxD@M5Z&Fp8sj$oM_j%2==K#@1O+bpr@_kYUCgAd?dAYUKHW2nfivn(ll zemg$%a>0k=tDUhCaMU`n#-LFV@C#NwL_1+A@;k@W>*~Eol;roD` zj{k50fmu5Fc%k}gSDgL1vBN_HV}6hlA1-~TfaUH!iIeEr`$=zH(q_w9S$_iH4Pwv*y!V8-{H5xx22 zS&pqJ+~-RbcEHgu*#U4zBic^~iU=b0plYuvxk_b$|)CXh&$)AwM z|5#4M8b=WE#?m7}BHA|o*-`PJP1UH)E<$Z$9rNF#4F=J(@4jg$BS8Bbkcf$D@GG!( z!@%%Z{9uyXS)Mk7T#WkHuhzf%JEKHzue`v;+RVAnGPT$&1l$hs@LnJy4D+(@@sDlT zKl-e_A4Ufpo#Lck=;$~B1wsFSYA;3wv;3MC>HCl%@+LVQABdikNX(qjm)G9$J4Iuu zs2gBcb`7W(J=aFzBdA*Up0)kxw`^c31~`sT(fUNGo2+{`8m@i{7F(mBAcz8oeEu4p z$NNJx>m)fNBe_@4X zyH81kU{wXu+5SX({L&-dZZ%L4>ddpG^+b$zn2{7hknt&I2yt0ppWzA2!q&6_mj)#7 zr3u#Br1>9M!LAjQ-jkQ1a3B9$g_?7_PPFYw1I7g1M9N?8iZ0pEp9{fJkszLEmYM6L z)xTa*m34|9&-QzOzNn#<=zTBqBnQZH=KZ3i{z)=zI{Khbgs2sH+)@v%es!3uUCQqs zWf-gMlftBBluz|Z~1#ES*|HTE@&=ZD5Hz9P%vaeQLO z!#JkerTflnb^Fd1pHWA6T#*8fXoO%->MlU>tc%hX-vm{^w6}xwr^rf`0jaArS*L?I zNlbAm{_v+|c77~^X1E|xU#>LHfRB=IQ4r+!&;U9xM?|?l$C4u6V92@?2j{czy2AtC zXJIq4t2${W;tb8WXTJ#}f^2iDGjiNPSM%A;mBkL##$ii}wIs{x=L$@?Jy5_kJK;!6 zo4;e3RCe^HRQtbBz)vg(9i23ChM3Ik>73UThsK9K2P|hkIvs?JaR|eTkg=^d*9HjO zX?l@8Cb3b%@b`0uQRLlc6r5p%8SVreqcg)S&y$=sW-OnBIBdot50VEh`70lN;=;)h#ed);&4N*jP;S5vBj`vC#iWmnBH@$sg|M;p z5`K)aC?+eZiQXs2z*7#a?*MxdghyW{1TD+yW#MjK?w++Lg-;qm+i(J0I*#)8fsW># zn^Gaa0VYRdy5Kzc3Zh4uKEBG!MDsk6QvPyx=XjPN;YtV;j!1i8~=&Ict8slVFT zeSb^;3w071AsH;S=YPHD2WE%~uG8?c0vC?(-daNP#ei}y)89#`XnBcBP@doR2oAbG z*!(IyOc&xT@k25RmblAk2Mvx{J5{BqL{TAy{Xt@m%#28ur6(`O3 zyb}l66$R|y#t~m8f@Okv`1K%nd*Hz$df>&z?TLxiUJMu&bz%ivUJ1pALr0ng6gF{? zbL2Vc^m3&EZBNWr8S~R$lrE-*L8i4egUKa~&}l@IO2iBEXigL@G@p&&!4VhDf(EZG z7Rym)roBm^4hI}X9M&Sz;W9lXo1w4^tGkh1X*f$Pe9q+1H39r}e^(vl8H%B0D zfze9{6*AP-lUSjwM@*K`x&gVO)}}&7aEQL`mv@QC-CJ<+OY<=sZ7fh&aEpY zTHc?M9Wjwa5SFmSCBS%`@Q>s|v6%rWzxgo@YM#o%;g;k|usaPIz+RZ_&&7oWsM4^g zdiYq9^H^|rujdAwMs;!x<%XOc+|3!*3BY1)J|ex%jY9Wlt2yRC28#CIF5Hg;6Mm=r zfKG4NB;K&SCxRK-dKMfWydqlI0@Uz~mMd(XSa5j(mcgQmG(u9)da7-#Mr&Hjk5v)~ zftuF81`6v;0=sG?8&SQQV6(btrI7%asy(mVSCvd+@=8YP49SNLS~hl>quWo_&;$*G}tI9$1Db; zo`HT=B-3aU$p@sV$va|WT$F3>`zA}|pWBQl!U%opY&yKh`d6ugwx%rPF+(OzHO3pX zGp9O(Mrl3u33^F5`Bw6crix7yH%}fR+T3s)pw{nTbO&*%PI8gRdde|Aqvynw<`edZU4y1| zXQL_{IAIf}j{{t-hfPWP7YX^6pF)du!ENMjgRPbbeeG(Mjl1@ORGP3!bea={x?iIt z9B;Ibk2wDbGbL5g$5*V^9`&08_6H$q7;AQ)P%dIF>Ow^) z@AXLUS-NXD5Dq(&^fKv={(c*Ko);){ukYy)Ajy?Qd1D+rTb-wR#JB zd3=P*>xq8KyNP@yi}#Ht2_a?h(V1M6Zps>^*9`kAt{LG&TrF)Izaw zjNmg_e(&9JGk&(>XRQ>QY>3hP6ee&HX+~dtmG(2YrV!d0?+t7#_32-uAgBnkf9+A9D->tReInCnjG9|L zqbvjR7>J$M>_xPMWN{B(sp`V;@6C?kMKKoYi|hCTe$|-pqeztKlzgP$wR{# z^%v%81l{>>a&0>6An5GfPad zIW-mzHARC#rjbR4HR|Xl3AybYfpWcfV7^bvA#_jA$lU0en||n~3+m5fVb$s zk8g6%(=}5+!e3dBP%kt$mLMzl8H3 zi(VlMC{>q4$v$h$yZq;p@DG>FmWDnqS!n%M4ndvb33B<5;gFrWUt^+Fs@eYW5@r$N zCN*P6w4z0?%8chZRMO;YZr=Th#u8iwO<~o!eXCSxUTaL$E35XmY!~gwTb299yYRhq zyqm!)EbXANdfj*=kgwbQ@?-Jjl6P_DgK{170>SN()vhL&C9~G|7|dLh61W zBrPQ}u?CcP4Jlh(G^UJma?+e8=7@E2I)Y+%+Yn4nX;SvyrMP^<&IThBe6dvf*cs*f zPbpYfro5?8%H6$yp$mku_xTrg;0Yue5WKB5k85v^JOzTmSa69LiMnR5a?+G$*rhLT zG4^Jcn(aD(uvOD@OaeQKrem~62rNas(fWS1u`pcugj(K7G}cPA*1OREW)BjO5g1>v zCSc{l^&IS<3`10l&U5PZP;{^Feue$Qx0c1yWf+R;@1WSsiZgn|TkBPD*-qYUqqkGH zZ+?YK?0XV73b4@YNb!*C!%kjSECNyYg^N4O67cN?p(sUK-M`tTHGqrHl?^K7US1(n zfw^tjMZVESi^U`56)dc#$|$dX_k0%;*&K8JaDWbN9Z-U+Sd3B=oaq}{{uiU4M?#L3 zVj5+LGTnJgH#46qI#tPO)`v)=wP7R7l55Uofa@?BUwk2ZziaZUc`S>)q^rxvp6VuJ z0eNYlvmsEAegBfG*k4fWNR;KPx}+LXEkh|fBCmf9Th5i)>mGk7T?Kp8H$fZ>MKm+| zu?M^V^d20Ju?W~8(^RNH96lao()a(8jY0ildldfuJ_HI&tK}$O!e$RE48Nrhe2Jf6EpEpMGt>(WlLez?5Q#K0&23QC?I&xP|bZsrd&v2 z0h<(>Ur)ZxMyWEHN!4%x|6Lwx$kbUQl4bWFjS?0K!XRpjcF2T#^DxsW57-ei zs^F)k$|zYn{lE2th0Cszms+bvx*JBp4I2b>>yh$J76gwRfm)tmX(Bub!L_FtNlq?D zM#2p|>g3`InWEJ3Ws2<9i0G9XTA?~HPQewh7<0`>W=@O%+aRmE6{b)Uu8hdv@yK95 z^2YL+vX&$bVcx-=mpU5NlP^t*(%@kwa_g3oeo&Au<)@5d%$?_x_pzeEZO7-9Y48Bi zK?0?Tu^KJo5`aJ!=%9!G49cUsVzxCQSVJOQqS0UU;7EVN6HhkJ56 zbPjnHzOAss-#+)Cn~cs)8kR0uc&{;`p9?edS!auPS}bOHq&e|h?X89%()j~PwzXGF zR8sZjx?#n2+c<-j9t+-oPzBOhlIVr@E#cL0)dk^3i z6h`?!lN?5+-z8ZrS$U?E{{cljSVF6UwW_$)Z+BpUAsfhL+I~R>yKe;MBA})zR<7|A zKN5EFzPe6FMPx1*@e~uuzepnMM6U>QVY^!WK zTkJ{edt=!lIt;G9Mzv^jon#U*PmF-^fWn9e^hJ1GCB_sN>F^Z7yAZIY#}G^@`4Emw zB9>!Foq{0q?nST!3(6e(tdZc!Gy}L>p~phn$fQYqVPy%4Yf%28*&5{<#cok%ut1_c zv0@1*9Dpn2#Gps-u;qzp#zN#Ud0Km~q?iXOe!qEcM>+xcmQ}{39ielU)<>%=$Sd{$ z#P&;OU;P{ETMn0GhB8|UH}|!Hb#c~rr8>^r9!vlQ@h}2e^qaC&=A}Zo;S}=(;WXu^fTqAyHx_$|4>H6isuGc{=;Xl?e=esqhAE8GUi*+L) zMr^U;Cc}8)5$s2lgLvoo@j5~ryIzDek4{H}$4ynx#YU9^rP8n{uEs!~tYN751+6Qv zOXT_Tx?0Ii#As$r>pjEF6XdNab9F8?Jk^j4hGmj00n76oW{_X5%F}45rsF z{Fc>1L-E@X8?h-0pJ6W6^^RZxeCjhX&ShGhd^olmp3{*F!Bxx zd^{3HJsun0im=Vw(A0D~R(6FM7~B{*h`Wd(?-t8gP76VPAcv$U<@Xi+Wy>}S2yYp3 z&NYDS5U^2cGnO!AiQr<)Pr~$?M;d@2$JL84SD{cc)^>Q~24ADWQKXwJSrRI^xrzQv zwWC1^Arq0&$fzCBA&Pl5i!vzb@DR1!d{y0pB6MS!Unz^ofeeNB zX5I`l{O|w6=Dz>1`Mgd~{D;ldP(E$X#r?zP2Sxz&)+0u+a4A1-asIg-3a^=cj7n2b zAe1Y%>)}L*Z)M04lqYV`oZ?)F6(AE`gdDem zMp9;L+R)UICQ{e7io~<;Yi`bY6~C>p!nS?38lXyX0Yr|;lVM6wEtEktKtDj8iFPVs z;3*N^W>pwvSPSfA($xtt(>CVpVO8V7x+;y1rUsu-ITy9JhaV(KgR+d+D&FeJY^QGq z7cy*?_yxaTe!p{uP?=1tEZPNRG7zTd5{RLFBvXEJtFX&Y20cuPT%n6g{GugxSQS|! zfGz-TP(>y6|HPPGk5$+aY)JLc<`m~wLs(>?fR)XaLr(IPNZzpNR?BLl zEQ?SPBePlzA;Zi6$j3_#?11rVYe<29K;9qeZPxiRms}xqP`j=yrwKne#3YU95{0t+ z9a^9=>jqyK5yRtqY^)|uooo`B$y7z#P)FVPz=1NFd})`mz-G+SIpths_C7jP@HfVl z&z@by628apDhW3u9Uoa1tK&nbx8xsmgwn_V4y?%*8X6>OA-`KLGxLT;b1x7{80O|( zVa*gLw8g631HZJo&4SB3=`ikIFv0#VM^&sryg+#}#s1PxFem))HA{nZIc{!R-;>Vi zQO6;th~F>vpg_?)hu<8~fCKW@BF>C0$5UXjDbA&yj!4q)zXm|T!A>6zA5I78=ys zslboF&MFh>Uf#zLDEz@?3O*y|3U_eVq6S;OG!63MQ$A9RXgn{Y^uG3rjnfGyOOlyp zp0X+wXB+yv4v!V`M4AlQqdd6|kYZ|o?l=t&l8TM@;|Otdr#d!y#TCZzYEstM#^9kr z#>bR*34ym+G?))m?|=!!SXwi@=eE+xG}b5mBNRBe+AaTnR?3-x-L>{DdM%Nh`B3=< z5Sbvxrt9KV5mO)An_3BsT*Mp9C!km=9{T2z*zv}@Tk>n3+tJ2{%crO{VW+0itWppY z6h0bWM$kTIe~HKur4BL^mw}9w ziRM%Q-GuTve*LTIR+^orJq@0b8r8^b+X42+5A)Dzt9x3w+Onw#M}G%5oCMoK^Yz#wyO}VLDU21E_A|moWhRg zYxHq7*q}KiVol@q8`+_LQQxV#w^8=Qo#S&RU9`o6i6(qwXOc{8+cqb*ZQHhO+qP{d zPoCJeb5nKe{tb6mpRPV1`opQ}s;*jl?cb7POz|pa)A@#xF<+C_#n%TC=1*y&voA~? zpmMMkG`7V+^o&(7%f;#)bsAxKA6aPNKKe<_Li1Z9QeLaw|4oVD@;sc1;|w2NOL_-q zRxzE{*iCXLj_Vt>`lNrx%wtd$1A4!UaB_Nu1jRL1Ni}cp7lW$e)Y$6u zDB|a<9P@N936PHYk&Ya#H9ZmTMPaZU#;fnAp}`}1bGddKpy6y5>`K*Tc1ZGwuhi@ zmbhDwB|D0c%5X`^W835A6e}Q`s6)fD|6pay0)i8JO;eST&^v6XtTfHhh!X?(<#Nw$ z)d%7>99R(J)Glr#7%5*x_km=Z=(5%d9{XIHHqynPx`qROH-2bq<|m7|NOD8|Aq5w7 zwZw<~fCIcv;}xCWl(LJ_5?&lzsiWB~CA{${XDL_m4E0kC-3!8ntX=gtAF~UB$*VrU z($J>mM-O*1Iz3(Sa+D_G$WM5r2xWA)>6-I*h>De;Gp@&1!x z-qp_NoaZfjiJpj4V#P*d1*$Ib+Kq-?wYt(c8NPus_F<>)2vM3F7S>pwRVUJkM75MO z2{}iZ%I~_{=gOR@>(5ceLbU5H7`ecao&os=X({Od(|i7@X~Q>1NC>~Ld$zIH#-2}Q z!PGk@v(b!~HS*9@pwtTz`sS`*XSUlCl|B(Pd7sk`UwBSt?UF|~=Js7|X@2?o?fq^XAkMbcj$BDmJ zi7thWJFdE_c=o;$Y@2eWc>+}+g%uf;KtrJjDcxG^fnSF=e=abi>&wqYk?~=x3yDrx zI5^}-(*W-)!H?A`*)pLx2k?LPRHbqI4>Z(V<(6m7;jvT0R&$ug*1#z2Z79fG7x9Lc z_*`3K$LKDVT;&7CL$hJgTdB8k;PapEmz&lMK0filLgvlF7IMXU70h04_Xpf+-R*(K z)zD&-xomV?4$wtW%jQQmkO=WBe)`vSJ$FjNgS;E)vesyH{9wKI3AgLJs`OJv#S>kaei@T<68gYk({Mq&Q_T@r7i9g%^8s8ZAUa z48A}n0lEORjNqe~eNeg%90-c|hhp=o<*0c7k>{ zWZD9S@nX?ip&-}dUuMEW2Q8(feL}w(i4?I7^n91T>7>vT3_Se?h846sk-3Y@Y7-y9 zMXcsx2LmdsAb%$vZukzHGmCipe8g*yA~B}!OX^??HK2t3`JRi5$54$>96rZN>o$Jk zo_ib4+wg_=j&hB{Er~i@ARyFmI%-Jpo-BQZXca)f)@_cFY|VZPz1iQhzGi3LWwgHLv5?~!urVeTBQ*q&MO+{#TTamT z&lBpldJG5}Y1uQ7j>{v5K!$MpQ?mE(yAIrhzJo>2f%fsnHw#5g7BSc~AqikaDjvLL z+sDL$f{_&9ezg)|bqs3<(PC-C%25ZlgQArZ`&GOq*evIz{g%`btD#ud@@GcRpZ&M? z&#RpI8&q#@ZjkB}vnpErS^3iJocQm{$_NE<%yTH{QxXm43btLcSCXJ#^0`Gbe8mB4 z71Bm9VVCFHeZ@Z89|z6wuopwW5W<1$Fq_pj_U3oD{N0m@?tZmi+Yd;JljmBye{`EW zi4Jx@orXZl8Sf~T20}^WuiQ0@;nKODvsmm4EPt(D+exY`<10juURx~-laLZ-R(1}E z#}A^o-^Lb+tAuOxn9FNSq75D<_Z#7Nv$s@rCXz9|cM)t~QKhUsV4v+WOeI1VO+r!S zq5*QKDevUG*ALLokWB)6Fsq2faLYRfDtuQy!5i$6ezm|Z)8n+?} zW}ER%i!U?i_ni-=)Y~m+Z24W+gmvp-ddrx#qDs4BHWDUy5eOK9mzGwETGEf}RB3Z! z`l=J02Yg>m{`%w54_p%}R<{rJxJ~D)m{dpS%}pbe$@HCX_;X)uSO^#*M(XuxVBtY3 zHJgN3U8OpvsfbiuO=qdihh_!?V~#fa4*KrWng-C5gXSfRk@MbKv}6)6dQ z-n#5ND$Sh4FLrh6?&L0m;rL}VLwEKN=h+hnk|?S2Ef_4=T~TXw8!>^OD^M}J64n(v zREAik{tWRF@js9J9z_W>T+jx{`2dRw%K3N?wKHM_TM#@Sv}1;!9g?FN52KgD-~sT%6xQ z%a@=00LS@z=WiGN2f^N3bMbW1` zZL{hxgoUB6I$5vXf6OifJ@<6le`fyuARseer2?pW#M5u8+!M>Ma@I^Ud;9!!K0Bgt_&^jBq|kIv@L6kv-utEO zBcPu7n?|d+jCa!& zSo%5@Et9T|@$lZ?RhJw-81YOyvL%<4Y>F{#@6-EXx)*m4EiEkYjhbVSrGAJ=qeg><`4eW7^AD)u9DrHX>U~wg@aE&k zU>(3oGX6cv-H-NG7mRm_$Vf0eC=Y`7T*d~Cj+Vr52Lh11K<2AI7NAHTrtKUqv+t+5 zacBjm>7PF*+QXI7SriD*etZXiBzzuC8_}DG2@ZV{8EYvr}Oj-R(5 zmT;A+#pF9K?YGHg@elcdgugS4NCZZ;R+wk5NXABbT6A>Ql%)Hn=yu(&z#GCVHv8Pm zAt4|xR#O|BdE!Pw1IK|pAXb+?CrR<-hcie;Pq%J6mV?c5Q$Z}n`6Ns;<8uj2pUDW z9pD)m^!d3Mh8I}{KV)=*#IugZmggS0o|p>zz3 z6sygH5H00~i>pGE@z{Sb$1#Zn?Tf_-)3NA3Z2~*T#BziJj`?MI>xbxOYv~oM zsT>noi5MN>Gf4^w?oDEEw}S7?S&0exIVIfPVX2C62@dYQlIdq}FP-b!Xu#u)tG8n} zP!~@+mYb3L$dz^Hk*AE%)3UQ=0{^-i!*#iTaHhOphbbY)o1tf&-AUt`D~qj$)E)}2 zMCqD=0_vI#GI#FE5NqsTclTh{bQEIyIYsz+=Iu$DOe!eEGH%^rDOE;(xx0k0Q%gtO zC(H~LB42(wl4vX+k z#G;gyToEwmbG&7JSu%>stL)RuoNK4$7Zps3etkG2q_w-9yfK9TNC6ra;HQ)ndohLf zQi?7aMctSwIR>YrId}1kp;l%TYjd2dT{uZ*2!}y$@L@GL7|0zi*^3d_(cV17t*Fu# ze>NuN4n18f2tG#nuHI;F#l$6!&Y8YnZyXiZ_nTzn3Z@1=I1La9&rcBTRRtQ@RTt&C z*0KOcZSD+gxe4ph5NJsA2&2iWQM?HMT3Xl-txqi|C-&&=?<=t_4doP6Wu5xHWEZ=z zgRV()LJBfUlALa*Gu+ACX%08dHF1}`v)LE4E~PsS%gjzGWzBT4k7EYZ&2qR552i4R zX+&VU#nwB1mCddzx3Lg*QIwI(W~G{MEFt+8-&@33@HquO%kr5*U7UQ<-f&tfm0P(C zQEclG`)N*Kvn9R%R}qoR>{yD5+Oz>zzVwLtKnn#Bk%7v&>0-K&oo`;sW?s?JJK8XL zOKj*NM#)fCT5QXwVaqiP4;LB6E6Xc|`8BHDz%$tWD>Ppuji4Yyd-uT^p`7P81g!=o zlv5HRiSJQ~L88v?`h^%_M=4Ba-e2ik%>KoY`@k~zqub@b1XxhUU+lCc1=$I=pTGPS@|)kGri(%VRkN| zp#7Ef5Db)iVKWnF%9QcrGD0kLvh+U)O=-*jbn^8_aiE9#W!&IqxEac$LCi8MBopmF z6gX@$itbAfW8r(}Uh}D+#0Vs$_wsj-Ezc0kI5koNwf0g49a8yoUad;Hn5cWkGfXLF zM_JPEWXto5Nc=A8xtIvmB;}`Lngh)_6DbOY?SJR*78n?q9?s;>a#pI@Q z`m!~1`VJGc_rGUvWB_JoD7h{PS_hx62wL}_Yxq2gA1+Ru$Uil+m0iDm+p7QJ@ra3Y z<|IVK*)^ktI%T}s%?zHL;?<{GAH4qNcQT3!NKki&GRjpXV}sL*D~5x*5k=;P^*w>e z$f?M?#62;nNzt({pDuo65-9Fr(Vx&ckie6^XoU!$THq|H%)>X#BhQh~8A|zeN#W&E z5rD<@w{ThD{Zd(hv*@pEn$nqt|1xsYhzF9Tdr$AfaVY1z*IxkE48=Vn+*H@G zXQb}=n{JHmwgYf+)-zE{!-9Huo~pk4*zt(~Eh_5Dr)s$F+B3KQKdC#s*^Sx7x+9C$ zREm2Gwb2Lm-kH@c-KMh_SNxP%0w%JHiboT>nosLyvi6lUF+i?AxbM^oVe-fqUhMw; zrxjh_qVd~Zqz@vQHeo`q>2+h%;%km;;Cqdgu6q@eR?BVT@AIBsgX_S+@ps4qcsINrAG=>-#2 z6{~c|f3x8;)mnbZ`1nm=0-VgX|Kb(_?rJWb^zFZPdVI33u9tPaeMqy9MD&W)M{5?p zzpH#&9n0~3Ja5IAhQ73Og&uC}0&@@k-Y+MRu{fRZ9SKqM@ZAlUl-r$5jTyphEQ$R3 zgm&CaO={{S&SZC%5xZG429ZP&aZK(A6^#yY8sdaN zS_?guO9~QmGyvrHZ){F@XR3f8)VY{`ku{wea|$g69Lg|$Z$ZNBb=;NuVOX?3saxC`BAy#M_CvNtC(lCi!& z%g>~OW+}aeLXE-AAJO2 z84+rOkP`TxOV9}eWcoX3i6pae6lwNrspZMl=LvGLQ6%Xk&p9Cr6)DFWmPD^LE^kt479 z!T>0sAZI`DT_as`{QGmI-1ED#8hr2!K8e#_NmAVH#JMwO`@W#)zbi;T23n1PCn6Dc zEp)Vft&3BG_{YU~KQpySfd*904HP5DQ}r)E)cP`UJYn808#8l}A8L#;5J6IWu`@ve z_bMQZkaVbLIvBAvLVk9V6BR2W1N>wF;^`BC$yS)p-S1fbQN}U$yE7qpF@h+7GavPvD)@^N9|)6iy!bQ>XmXhmZ|44a~@i;ppZ5=}p zv_}?`vhv3F8tDGY07NEC9QBK$#t3mD3d&y2$H~6Y27QbG(JGQSua^%~zO5QGe@iJ#Z1%fkTxn>l-a6h}FYCj4m-)q~@tks}lG zK*1#{G$Ey7W!uvMawQq;K}@7}%sni_<6Rq5K22@+ISuPafd!QGpyS9i?Uj3hN@O_P z%5eIOK=B{D+9$>f1(#Fl{2a1bZAV+YL;;sHJ`Zh@AFkA7v{TG~+*MIWuI%-i3`r?! z?(Gs*`X&=nTol6i`RClo#U9bKxD*g&0Ryr<2KIo7TsqhiR}L&O{_8?g9rzKV^fqf1 zga8M_?8x4YVOy0;*tV12VRj%XW>IovBCUwMaG*%t(BSUPHz43N>V*w7A3~k()}vCx`aB1w2`}D;)wV@aSkFH6-C0xAwyapmJ55_pFRXec$lo)_qmf=D>Z7o#ab(FYu(hLzN2Hw{kbZK3 znDvAt7;x70^*iyv&E7&FWd%S(?dT?h6@C!~y@ zLxM&ndsG@I`L`=*WMjRD!de-#x5tz!j2ot}A_$d)X z#DoO10NJL}K)KCvV5#T>qlUA};*!i8w z`xDdI{2(*aMwG;ztsm3sVRMKj?2u-9z#$w)nF;Qyw1P2iQ+l0=NJ(IJVa^mH{{1&e zzg-)M=)FQn!EgKaJ0m^@5~;qXeMBXrlX?x1*1b+18zE)l401>I)aW||h|qHE-!62F zK2V>pnGtiWCN3_(qUtg_GBqaa%(?dIMl_D>H2dTB{&PFvWA!IwYIo9<0Rc*D@t`or z!4}*oc%B`T7NYQuDLa07->e;vrGMUz50ihvjwc(qMBk?>xJ=*k3}m7IIy-g^l*x`I zJHf<`WgA|lf8CDPD7bOoj)5eOER=^Gccwou3$f0QJr%seDHwsK@1_RQ(SMj7I|eGm zj$6rpzd#b71BkHW&IUf&MxeLjAnX1b8EebC`bM zj5ut^H$i;A?rz~7qr^$c8x}&bLYt0&YJ5KvtesUyr*%AZ6k@Jo3Z1m zI$J8>IlN}xgxC6S=j1*5iAw2q;;zg=``MLw)UjYgze~xsU2CLCchlv2JKb_sd<)U> zoRjJ76^lAHn&WtO<%YPKSE1Rp?XvowuzpJjK%e><;>nXJw9)Y<2u$$!&OX12o)zr> z*lLMw+O;oZ)Jg3XJH!fns%1y;q07SybtOm4b>Ek`_$5ZFz`F%~p$R(0jeh{e$k{f5 zC*GRE!(T~>D>b&M0Yz=O9kYJ&<(+uov1)STmL&iaA4SOS=P;UaJVdHR;bSCfCf4-d z40PXUj{{d90lnywsD{CbI8jtR-I|qBj}P9p96@aG>DzFa1QLfK`GYTCN#9{K_qI|g ztRxtWA&M|(+&t~Q{ztC^(!?s?RxbB8hMo8!vSyXzt-4{E5ze`L#J)VhCY!euEYBsgyKYVgQTBe_ z>!-*5mE$CPbeyaHM0}q4OG+n*Rze(%hl78UYeakj3F%r`q{rODf3eOB!s{w5=s-Ra zKmV2UHK%_!YFhG3hj_(2yOb8tJ1)4!+H`ffEv|a`G0RZ>4gVARd3oKE#b+3~ zJ*H8S=_Myt;mfPmcUDH%dluhSN82zcyd=O4FR18tpG8 z#czVZ*N)VwLTHb}lZ>@9*VAbP*k)+sX6H`Q`j*GNx!T6TCPTN1>cxIMG}G+~^eZxqO^_*fi@!B{Xa378x-3lbD2rhUsh&e=h&pe5E3$5HTBp4#3XD^*m zO~rX1=;_((b>k+m=)Au6tp}aC@|AIIt$k1ZWB?u)-CUsATpjhKLFIb#Z;#q|o!HGS z81w)sPC&f81Do$S#F*Zbd|mnhqg9|&eiSy$xXqs=y2l5A!XBR{H;l5U;d{HAz;>bd z=LV_?TxPE0tv>`){o-)8?32Fyh@4N89sIs;;|Jc@(>-$D@KVrhFwr}&9ar8Q33jd3 z05Rf>SUR%ruQ~71JV7OEf8VfUKX(Sc6te37lKyKoq-}h>JAC9)Ex+mVGx7{m`P|AP zalNsjc8}G4xN#|{sz2z)xQpxNemY&h-#&h$!T+m1iP>6+{|NBs!oWw|-;Tcm+Jk>59jp9)g6XiT$>Qo;&UAg zHf`;_-L9npE_-x;4_*44hg|V>XmetG!k%Z|X>FzE3+#a_n{gsydWEvH&GtI7?#4eE zDNRat7&z(iwxTK~y4Se2Rl#>oUmcJ8ECyzC7!1W58=DA zBCq9-{;3ZNm05bV{Lx@H2HLhihuAsa2=i1=aO)Jh;vuQKB!wXZn_H&tsll4 z=CC`Cl)Jf(l>W!X;S1jyO%0R@VH$c#A7F2M7|4D1=WQHB-ShRM{SG3#72(rDT@Cx) zd$FC%{BS?u7SDr}^FlV?#ly6CHgpT-ky<%XdpD~LwG)2+>sh|Ir-;lupU;R*ck4It z7@G8s`!3%D%2`CYz1%jcJ)a+0Bg)qbUhBosyDut-4%waXS6yyD%CHX`kU9KA0h48BacU#U8S>Dgv zmvfJ?;ZuX~lPtHy^fT84g4m5_qLVx@wZ3`9;|o3Bi$52yH}dNf+Wo#EpY6OHCOg=* z>~n5)Cl`rcRTa7sX=#%GHcL-zUM=j>Yw9r_^eI)D9+ z`OkW^Q*6@{1BS{Y_NJ|H>^J94xHs<|8g5=;CHTCS!l4KrKLLxqm^}HX!ruKZ*2kX_ zUS%k`xA}#yChzklx5^0K{l~&ZpC<3L!b!jTpEew=nZgAm;6^!EjGg==-mti>aptSZ zk=Wz-m*w4Dx>6qaiLfghs#Bl0S~RH+8i5|vMf-!t9AEJ4?Kd=)4@gvs_HuGtfxY1e zej#PfJ!o&Qv{&St~1huc!is#^p+fG6If0+4Vpyzz@Py^j!P98DTZcRE* zH_yMmeIRXx=|6;_RONahYx_pJOK)gp6;}P9o*um6n3C)Uu3gXZ3w602)tCxU{HKmFUNu-m5}e0yDz=N z@Pi@Ks3TtY4!3__TUV>2)*CK^~nOi{+=S&oELVT^Z1 zC`8f`@DxZBi&Rku>647|x9B=`J$fP3oQY7w zbe=jQ>$)s%&&TM9x)SapZ`ThRCrzG~U(0CtEWqV2ZIl3|^4*eehjTjB9>ZhB5l>P# zs*&mtX&O9c#m`pP>h-27(?TrN%Z+N&mRu?>Q)^5n(}mokRw_vr>Q#*j)nd(mmJ$`L zF&E&)>&ZuHJdH0>cBKl%xarmtXJA}+-O4(Gb1v>UEqAOXOHuf#2 zH*xfAb~aJmNY^um*rIH4wc zF3UF!TcK=Zx3djgf1MjcSEtV|`_*Dzam<|!QMs!;RlllH4ppL7X_QcnbFS?XfV?FG_k~qT8zaZfB27M4)ASmKZdNVwo-EKBmPSiB1!ro%3{E7Y8e=5Cbg> z4*x*8m<{MI|8V?4&&{^*g8c`rK{g~8m0jWG^h$BJ*diAO#OhRerCDj!0(g3wTxd4f z1^@~jtLNB+fi#^arpHCTo$qn0{R ztP~iE46>s&(cu0us0X3Jn+zA?in32>)Q=h_jM7A!Ku8etFov+8>1qsN@$3fq)JiI*1TgNA0{#&CmLWV8zUx)DqtL&EM$&4V-8y= zl#b@)Y;v3AS4{SBezaU7R*Y6;FXMt?Fy+;0D?OHbmjA}$f+UxG=HOJmTLnZV)_C@y7qT?=?J54h_vIGuHI zkj|ZeV|h!@j#=Al$>0o#a$G?G=Q1d4$6=))hUEapQ$HM6aI2*{jY)ICmPp&o4_ETQ zd9W6#`+$`8qYG_MH4-L1DDBnwXtJV)_$t|>6a`$P+u?BS9ReV0u7qR8s+-b|=`#dL zxE5w`H+&g+kE9l^H-f4k@)t7!#y-W6dZ-hHzr$254`Ne%m%1 z&IXq*^N)5hZG&TE3QIbqkroYyYous$hKc#MJVJX$iL1WAlPuYH)O49*xN{?5UJMd>3n+-}R0LP|E+m-Ekk)$F@v|E@_W|>WO z$7mc>Gc<%#^1vb-9+ZK_e&v=Zjh`rwBAS^BxKfSXvH@~eKxZ$zYQ2O=k%2e~YdjPZ zZ(O(7kuNdZh%Xbnz72OEohV@DG^Rg0my`M<{%-WibR&0)R|Fz)q{NNw!=jzU@%IVZ z6)~1l1CgkmjW@!&9uzJ}-$(`;($#_$VY70zdD`GJ*)LPCPy=}p7*u10XT^b_wbx3G zYEaLH_u-Efo)n(CXx;JPk=D;kj6{CB!{h`edPsw*d91DU)8N1_NbTvfY4){6hmskb zVm3uvFEqJBoDZ2Nc;@T0OK92e1m7I*{O2ZmXydtA!V>85r@2GIY5tqA4-Jk77xHHb z*KsT4cf}(TU#MK^T(9hDX+5=AG%3CA?QEMOY@2E8w!zBUR9)rv3(@*@hBO2F7(=>2 zlp`>K+l`kEz0OrLc7(~*7^Cn66XU52rbDJvxX>?AJp^V8Tz^3LdSDOuE4bAG(j*~V zEIhVRrQ%n(ESZbqO9_MwInTC11FZQMf5J1eH^ro@YwNxYVV7{HWSgMxo;1bC1)7OS+?EUC-o6p`C^nqkl zBNP2j;7AaO3F%z*Vzn<=yA!?si1QSAHp;`}mrl$AX$N-(AI=7c8K21}YwKYfVcTUp zVv7o_1Ga7JcP)A_mCRfttb@Zp$v*W%eL!izHy&g=@m~0Q z#Uyn_HKcl`qHjxR6YpO9op2lVXUGxR%7t34e*PEK(FU`*JJl#*+o-D}Jt?V)_yp3V z59(HUd!nb1Gf0Xh{39es3+tJytP99t%rx!?eQN-ZeuRVRRL%x+zH+C0G0>dOiNbal zr*c&4R`MiQF$axVNu9TjJXb9@F9*Q0;1d2B4(w+-9&q()=7FF_75JAtOIgMVU|hEJ zlJfmmOGx`#{0LgB%iycv>xP85-Ckv|Ho=f#s5lxh$`F~zbHYX7oH?l`FW+(0tx{|K zNVB6k)DmgUmS9_Nqu;IRlP1jj*jv`g%K`ODvbzX>wSsk(ev>}Tbip9x0(l9V@qY9L z`AC@2B%2Pk+QjAJy!IgXS^8S~Ui#TL_ZHVLksqLCKv0ixL67)I=m8l?8l#PS#xub= z*U3kSA5QI|QPfq}qK^8`e3H0OS}iv$w=5TwDJfUJ31M=se7p1@B*4aEa|T>KTNb85 zDApA?ApNK~2sC_q?8>Vo(6$Y>jkmpM-l0@Q=%gB@tDx{~5Us7&#~x)NU%jwYTj{Qj z)Ff@W^o4A_^xS3c3Tc<&w=J_Bv-Jb6X&tcx*^2jT8P9Nwt!!pZOtVa@j2J56*FTRg z0@Zuw06u ztBEzt1KN5HNLV6el2Wei(0S-ac>)G}oOJWI;jPp31+h`bI0u`vgxtSM1=h~jN5ewg z`4@~g0va1l&rNO^bKt9_lrGIRt^%@O3WPyx3f;fs=cwBr$TG3@jg0NcO4K zSae>teYPxDEF~{ z0RQD0j*`s7b-;P+EA3wHQtk?Ig`k?Z>if@qT&z$Zy!22ZLi|4MJlSdKU5T5lH)HRz zPQ1;s&8t6oGzYm1Gav% z5wmfaQacdQyWo&t;%11r*eH`Ns*@BWOlMJPY|J)x0GRIaUc%P_|Yg$erxN41nMgio=Ka%MqCGBKU<1yH0#ucv{#a zwk@_Jb}E(-d%!|hBK0fDK%rc+c)gf8)uZsnl>@@#(euw@*ITpX*P}to5RJDb_G%UG zH6H8m)}C}ayJ-~rFF6(^$KCy0d0-j0W-uo`*QUI4`DWia?9KknA!0aeg3fPADnR97 zWuFQ)^Zg;6^6+8W$Uiuu_o)@dlG{ zAgw)Jh*Wwmy@W~Gw9MpW!q7ACi9iGGoYxr(Q2rKItX=-KW^|1iOs~hsA5&zl`yg_s z1~PdrAyJ$nnNE91M@SPV%ZEkRO_kx;@1OgNEpw6FBkhs-w@beCw?US%Pb$+)w{^rK zp+D~pi_m=ILa2_iNpvm}W95AFLUW?Uzp6IZSYgcB4@eOs_TDtxAzK^Z4I0kT)gi$NCF3ZUSf{kddyp+>R|z}jLHqN}+f z5VdT@z1mU>vMx~Vo%KHYG=!kqb{gLBo6PRz*f!G>#*5kMcqiiXnb5$RIyE@SlC)&jR=Rx&71zI4FqU z2q*}tUf8wHbEP?#C+IkJ%A_Q2fc&5Q2x^rr;wt%Zh)Uyz)OcgHqpAi{p^;_D{PHzhobka2T@V#FHZ-A{1>~wuVO!oD+IG-id zwOL+X+Yx_|!1G_Rvl#cQRe{47pabHTfGN0GDR!6quLroEZXPgbji=Iuyx?yu=C8Eb z%aLrk_r=eRiGlmIfKe2n@s}9m;tB6_;^DD8`;#!@3SB4S>;A%}mu>g6zV`!y=ve$Q z?A78|)18YsMdd6bUCAk#$uw^!wX$T*FoLy`GO0yPK~ZTDz&Hg5dvHDL%`NBTr*}M8It%vJbk~uS#YH_A z1gf!G5=7p0Vh7p;Hn57L5i`BY>I0RO*DBjnCQks5Okg_qaU77IG&JXjoT_d0#NY zRterPZMw*k?sL>U1}iet=m9aHrc264dyAai;(%X?2d!sGD8^eYs_UnI@B;h)9RE4N zltHB9=3kBk1}ouLd2mlIF7hcgZc>p4ASP}$`q+?>;3YJ#(%Yr{wO9^i41o2#X?rA9 zWxXH#b-CM<;pnC3I8tfA{`(}PAqYV3G>xCJcjDccw?Z{hKAo{-X;IZKy|_<|*sD>_ z>jEnzqNA0dxfyF9PaTCD%>eaVEsJ^PO{J7yoR38C-)fyb*T44#*riSMkP>dtQzf)R zF39ixKJq1xze#186^e=#9e*i|5*OG7;f8%eM%YFP?sJo-e(0c80A&kON9O;^O603E zjp8eD`IIIEC^ik1*e+DAkp%c$eMID!l;l0umN%c^OqSbUh=WRY#s>M=h$@ctkvQyW;yw{VgC5SzxN{(#&b!?NEL-sFW1D%oY*Ciw=>o|Lu#3;XJ!m(==V!)em!C!oq`=2QQ zv1Q|1^eY%UiJ$3#r@dC?-qvxnH`_~_M2pNuE^vrgyZe;AU;rP@l?v|ga7^M8J^B1iJES$ zqH;sSm2Rv|K7(jq!@p&uAt+2NRHWbP=;%l(1E;#+m;VXHN73;S=eeN291)#6i7fx4 z_s3wnllML{zD;pY)B3Tt2qL!W^C{Pmu+^Cf_I%#~ofc-^XLp}63#4+s2%_U)%ZnZ5 z16Y9RU^GU;}#f7J#kr7Pd;4P)_9tJ(#i2ym1&#*`2Xjb+Ey@sTC} z?%sC3MU)Nkkr{1QFwGeJN6g&|%Ut(xlIQn&dY3Wc{!laZ8>ZzENGU2Ygx`L_N2IXl zD_rVPxEbFs4<`EJybS3#Z&v<*O|1PC)4g;8sY2Fj_c2M_*s0~$b&Ky-|Lyj7L7e!J z&wRcN05u`QQPJ)ZGp$;poIZE=Zr8kn#lrO067_RNs5-7fF&d=*gmys@CBP8wzS0xW z=|Bx`VP941hl?RLJ)BDsLUp^(E?m%V~F^L(tepP`jv`xgroUZ3jbT zGPxG#6g=miEXh_;(;E0f*^Dbq%OeSsQ7y&;(?-7Ig70ddP?OH}Hd6u`Z`x=-PH&E+xC98sbH^OqWHq*wnqk_dSrCVF<22 z(>F~Us6EsFS07AD@#vFSpSZziYpW+YZr~bWa-?SBjSDTat%ky^tu^2Ay5C|VC0F$r z)8stlK&-0CFfvl}LeK3(!INLLdje(&-(-QdrGOngaGGq&ZVfRY`?+u2+aNlrI{+j> zfD9*AI*Ar27GLg3pZaeMe3>bBJ2U*B@t9REN|#(N14M<_Q(U9|y&THxZT=yvDyD-? zn?D`$#yQ~Ya%-5S&*tb1oh4uW1NIG$@ng z%@^e_YojTwA$MIGZRZ6miFu*Fx!9^o_TDx8K|2!kZM5jT-=`+k#V;n6Huo;=3#*Pu z4j8l@$Ay5MTk9=!e18MUB&jXIOm|eHucJ%6IuLqj4&rNqBn1|#&KLJeFYn>oqhVdgzxubOu z+`X#p`{{xZvFsoE~HI(TarA)i{d|9-kG;+E- z@Rp`*2os^cer$`HQwC>n_N`Bs%D~R+EJ(MU&}IS#s60Nfl|)Yq9BN7A4tQN&?`{&# z+Q;t79=yZjZ-V?AniuTgA-(jgzF04mr);Gv4~_kUD{%1WR(p)Gowx!q17iHw6s)+<=Xj}aRd~yBBlxR?CD2m?%@g0_3st#8i z(|EG!dqjtSG!l6Dk4>d>G_F`h8%JGoBSWR0dKTHk_SrT z$vSvSlNY$iml!l2HwdYncezc zJ+VqpQjxIuL987du=*2CwOjWOyvwDt6v=8iF%IB{wICrzb_em+ z<^cK2f<%n(hUTn%{-eOaJU?@ta{eF9&#b?PxaVSiGNU-JI57=c0HUhh|KPB&f1Gw} zQ&polIww&PoIgo8ev)wgB;oo=!}ODqdP&JWB;=lwagLKww;K2FM8j&{AB%F+-$yFO%exeiAhQANP_5I zP$379lAnV(kYN7|d(VW(s7%6v6m- zvvNK`w%~q-MIhbbeLIU;)Rs{-K@{}3mgBPc$&C!3{_|9jmJ8E42&f$de z;L^%xD&LIknK2t%))azo!bIfZOFkb>y1(**@}C6CM2N*wj1yTws>0*Puq^ z^u4J~9Fu`0q2c`9AQBB7Z$-la>p322Co)9kIBk0ns9RE#&uGY@6*FIB_e+qGmi9_V zVaF{`0e!6!y+2fem_3#D{3A=DpeER9^&DE$#pm*+WyQ&*FBNKWFZTXK&`#Xh___P? zhJe~CgS_&i>Rcjuwfa_3+1`G%_>zO#^YtGR9$8|@WFbxNu{|r#)zONRyX^~D{`efD z{G>aK(WhH;qQPrsu$rl++R3aWi?Fskm7PT9e*~|G2JvXGm(v>g`K=3B+xLG6O|>y0 zUI|$mVxmm4i2!NwGF`*q%CUP#rcP9X2zEO^UOu#cY+B*GWyY z#P8)4Z2{r4L{G%>rH6(+2{G(TGTf8aZ0YMfboHP*dcZn*=MJ8=XIV(UGXU3GFhsf?yhvB)KCykR^ zJB|Q!Xl~Hc(>6Q@D81f{3|b0ztr4S`4Xc_**WPFv*XS|2dvI^(s$!*Fv=(biAMdSF zS!+{EEf7)fQjqPhX0^nB>Jc(6I#BW-kSKr&v7PAq%OvhaV`Z?#O9n;ZQm0OTm-|Vq z9l2__|FL~nxBD2?4n|RaKsWX)c@iTvu^Q$~Qshp5`Ihnx5j}&VTnr{S7=N&q7Dj*$ z^h>!3>h402-bWqM%G=8>_K3}VzuLV=!-ai6MSb@KefT5*Os$3l!Lh>oqtTd)mR8*K zxFKIu9(Vxx*YNU9Jl04&r+*Ld3TJPV-D5`^<>NylzKelML9ZJ`OdF`XN*!Z&?(bmc zsZG%W)9K&sOOE8fow!PMT^Dqcclh4^A8FWmm{-vBxd3#9D>%*7*_5nF^l`7v*l1Bn zN?=6joFr-x^aklp^QXRAoko zR9Jq?vC3>E&|!r`aX-ENY>==jGm-K|6`@n^Pv8p6dnORi1`ad`&APDec%3t>G#78p zHvS%QvQX%QORxG`9DDUM;ey-|q!i+>(s%Ksj#uMX%RLtTCHwPom9{E(a)M&K(N)#B zTm?5x($Vz;zIA&aMtZzP=qtup{V14<48V`XIGpsr&1@8zNl?#L#BxocA6?kN%8$J; z|LzJ4WQ-tVH+JeIQW0HSu@0kxTPDivWXs@cF-T6gD1;fHLMU@Yz1$Uiz)-97+!*{) zk$@z*&c`)*5Qh3?UtxN%@nK@#spAiSv?T7@6csM5-y`kq(!S-1#tIR(=HHQLi-1y9 z$94s!UUQ@&7i%BFU$sDXyI(hjA9`zIP|l{y+;jdmUlSWG*D!3n?i6^x<7I@F`nFT0 zBc>E>>)6}+BX1)JFgC*h)J1EHhtP1N+pojMl_j_Y=^F`cNQVBjSnYz=Kk^3P(ySE~ z`hU;z|A1>o$3@#0C8ps=;@|^T$s3rxQYd}+GuDdxTyxW)+#DLrueShY|1AVFMO*YF z-XGx|1bY+L+rzTr4?pC2Rl^0bzkq|LBD3BeFue{-8JjBeIS$XNh?v=Wq~TCwTIrbhc%W zqeo>?F7)0LYBX#r8xIE$HK{hD)=c73Qv{C7O72|TxNmZ7o!Y^fcxzRxsI3}HWh7n0 zY2B@7BmLI}8uwsT85o@K=blXpVP_yDn7AAj#AL)?^j2>S|wBIy-ER5S}@|$j80M zu<%e)mt4etm# zNMhXHdoRtrdTtbXz!ej{<$ogX_ucvIJKwFnlw>0^Cdd?{Yg-2>%m;$cNa2W11T^JZ z3^B5A3b=`dkfh{aGWVcIb7G8Ezm*1$$X+ zs_cO9Rr1$rWDzVq19F85WnLw?<~$K0Y>pb%+gXE{yc#cT^HE?cK)$(qeVM4F_zZ?n zcCu~d!^U)Vz)_6W5bes?+-ARgc;blG5Xu0Fvg@!%nShs@sxsjzYrc3g!UE6MI#Vkq zyP|7n4S(h|OEO^dZq_kNVLY3uj zbC889JsxbCQP+I%oYwa<&p({REMB&Wl6JFw)`|;a%>Hep5FX9vxKrjjxuwqZON{eC zAobnFW)L?X=+OqHAw{3)NXn&szoQHXMvYRT=+W`BVU(#J-6WU+1sBWH)#nU@J%cvo zK7d>eE0)j0iW?lUhk=vOvI{YAvHQLxA!_mp3lSo!_ z1D-q4Z3s||t;%+=c#=L7#o=3%LKfj4eDr5t8|-~_rXFTO$#cax*M|5-GBeQSCu96{ z$9Oe$xQa1T%bDFdf*>5eNW}4+N76vB@ekHycherp0cCVA3foqdr0TvU`wy6l1@jur z^yMoqNi)`Dtv2|L2xrfTCQdc@4GHbp7a>z0n>yj?w3Ba5Smh+`Ce?aeSd&e9f6hL- zJ7sv!5_^_(gQCvO$%buBJlaZqr|$UhacL+OFEZJPM3whSq#z6h#)%vvy66n_eSuW` z6nDEH>KtahIE68rufTOef)b}lA%MDrhXG={)5nfVN!MU3dDQ`W_#~fX_C_+yDMt^_9Q047MxBFuGE@Wa#9T>q_R%$ z;PuOwVOA8((l>aKwBhO6cM<4^ID4VsxA~)z_5m*p;7d@_P7GH@5HAQeJFJ`%nN z=U*FepPbzS<}gAlcFVQ$N!Z2!jzLn{S4uT+%BZVi;h16Ee}ENvF=?N>^Stuzz?Rd` zB3<(nONGgOB>)|{eoG`3+H*+S(Lg^)UsP2AmYL;>t6-&N7OqFY4y38~ztt(&=_xeG zE2}2VBrK1V=rElB$HLgc#ML_-C#2(1HBCNOx zx|B%)o`E3* z#XasBQXj>Gq@lD6w{r`(k&q%*NZP%LQI`j;u`1Mx{y(L8(9vExb$h^K0(D`MWB`ot zMEvkYeDE^$2ddI1_*=ZMD^Q|5tMaLj(~tKKpT~}t4m=|`s6nS23q+@SX*=J-n5hf3 zJs*ra)p%zHI8`zF|D=(WA*+P3`Ig~(UGpm%*XVd%4~{A0oCq+DiAIQ$-uk+*?)e;= zk$#_`G4O}S7~9fFUQkXk9@ql4vp9`p%XF5r)b3$k9}9Amz2p}38s6&(L9FhBdMv0l zD?*VJwOE~Hui0scg)F|h=^F@eA6tmj+rdTEoo^F_CXZS&OIk?!HMoTCI73FC z5PaLt+YMa?PVNarPcAfaF3DAjFu5^>eB7Wjvft2T=LD6DT7nH(c)Z%lGU=5SCwFjW z18fP^5hQZbog3qYxMX%s5fCLb!T`DK>_|LRB_Rc|WL!G8CS`CJQUM%UFnO0fCx0XyB?eEp zN*yC3vnoZ%?nqf-{L8=%4@4aGxK}gGz-5P7Xwq;fw;=sgWp&plW9tZ8yIMcGHqiVF z=TQ^qVstoTx_vF}$FYq;|NU&B_?;sw5-e~KXF2YCKoH)s1M@1gI`)me=5V!F!%Z7@nq-?IQ zluUlbxhmr-63oyy7G^~6vw?vkxlDR0F6uMhP-Fr@DfC3t#};1VAW}N$T`Sp-aN{#@PH!G9@e@dHmHnsF2ptvvoio`nZn>F;)^j{f-y)kx-Cs#& z7H8rbQnDM)d~4!&Jt?;gRqdQpA&m6OuT0C4e3jox{5FTMu9A%{5r_6gD2m*kK^p9F zcxagq9AhH!W*P@p94qg_dbZ%%4hN+!gnpAIEd)Pviz>10)%K(lasfEAeo#J@JpF`% zOERlI-RQLqixUk8K0i~i11u{jwb?(z_3Q``Vr?AZ!Iqg&pD=xE3Shd-+CnIQ|Wk%cGf zb;IA&?~1P17)y7a;)(&eLbPk%9-}~_qGqdqyt)UE3f&z^T1+roDEQObbDz?mb$aR~ zX1_SocZAnIz;KbujLLKW2*X29F&V5;;7T&}M!w+keVw&Qm6A3cu$N7hb?Q2w-o5eT ze3P-Oa+yd$hW2tfh}bfBcMJNwS|ignt{ZC6OnML4b#4)xv=JCX@L5J{KA47$UMK#hKf`L~1f9*g7f-XV4oZ$rx(m%S=QDMj)*MVM;T( zl(FGjthxoQSZ@n1tDh&cE+pbDVAweXcwwsO#=N-1*KEdP;|xzL^IG_K9y3CXJuM_UT4Xw zG_JOhp0g^=N=ZS8*IP)|sy0?pZl1F%EKM&8PL9fvsBRyC%zRQZM`VbhU%Gr$_;+P> ziMYjRNy0wm0VAvYlIR(ukWu1ri45BKqXBzqWErWZn@V;td%h1tD_^G9^mw6e^jJa> ziWbOl1uwiUQjjKUo*BNixwoBs23>pxY4FF)r(Xf5UjeJAojWvr2#lgj+PpI$5+<`K zGfAw@7j0mV*=ByZD=~=4VQAz2FdWzvf;BHjkT~P^_h1f zcWlVvI%!VXn%iqK1u5HIR^F%tB9I%`Eee-vc4ccvZsCcs&lMZ&>Eb3m)(mHKJq4yY zqFO=r;g7sp?Qh4FXS0PRTD(3vH+u|u`ib4rVm?+UAhHCXJJPg2oh3x^it^~ZsJfvJ zbs&z!sbIwSIn$VwbtN;hQ2nOoX7td`$yb}G1(TFn?vG<8X#{mUgX=O@1}1WG3p42W z7_rR?gf`-78(Qd^DS{A7myz0<&rlc$giS|0eAdEWBxOjN>_!E|`DMavzADAdVT@1< zT?^mp>qj+XIChba_2`+Yz<2g7UxGUjMFmoWNKkg|_{2SrVJrkZmEYVh3Zc8xXo8tW zdTP8_i)IY6yWzNy)n9bC>@dzf&rI=uh@$oU{onUweKPicn5*yB-JOltBD0oiDO2q< z+WvnO?_xcRFM5>U4$^4#Pggv&rrWN}Gv6ceL5S#qamdFz`K22;n>Rp*0jd7PN#S;%nLtLGXU&ibhd@(bnYQv@fz(WMDr9s}?l>Of~JW zII&*CuQ6y7hpy@3zc(&PDy0dhoEpz;wTznVOX|CFRr7HDr{ZLT5X<1g$w=*=oj!%U zYFKo$BJA}}gqb0SY1GrAD|`w+1gN8oUu|#5F@rc3NT;;{9>*r^(LEcGt;?$RpgP&qeEF_pAPP~oj{FsN#K>p z*AnHY#umc*cw(cT;T==L$#=d-SCeGA^G@P{u@wC)yD@=Or5im~+%#z}x%$$qEl(~{N49#7&ebDiT%NW$E46%%%? z4&6=(TbLs~z2N}V_;?Z&C(o4gGC`kS`QG2%oSrK1sJoo~hIW*O;4tZTgk~&?x7DP6 zZuqU64xTH_u}{qS4C+z{GDT>M|0_csh`xGrDC52i-GAQ|gp!Ta&q0 zN8LJ!wC#tL=d-kCz79}%H($%_itCW*S=vzjnM+C>co_6}f!ltv{j?MZgcw+XnTZSc zqwL1jc0)!DUZE&Br6#JQP_zWCN*v*+Wk=VHFeXrBK5&REK3kwrgtDqN492#HvQFWe zcD>fJhpdq?Tcc*m7hpJMZ_V)1!=cUxo@Rm?Jn(@6`aI%B4fKCtR)$qxQQW=5i%SDofbHU=-TQkjD{JyXn&{fzKlE4RuUtz*v2dR!oFH*el z2}9ZIhpF0hNt4duthMTAiAEH+yH~Hi`6OY=n=0#GvrtG zF(nVjB@b7wU<8;T5v+Par7EM|*<+VjDvf(Z=LH)K{=WO^E+nXU$W*G2u|l;df6HQ0 z{P@5WSv5`?)0KDA=J+c6ypT;UVN5TUTa40B_QmFU7wCqXUh3D3bXvNk6$+>vEpQyG z=@%>t_QiI2JLiH-ChqGJP!bg;7%IY#9996X60w+!m#o^Z7g`n}S`d-LsXmcku(_mH z-aQj-u|yQD1&Jd#HyP`L-G5I;Or2}!Xvs1H>RDgPc=0{2XW8~r&+q4whwgVZWwoKW zUeU&<6n8t|{X_F9HcB43a=ViMZwT@PWOQVb*g7%w8nlrg$@OoAmWtEG3}y9(*Lr8! z>yCoP*ra|>VqHLy)&nEWqFQD1_1gvYyA}Od`mbLSaeiHospIiF<=yiq>VbSph5cCi z&L8WT{odxYD0(=d zBYi2XBYpUS(hDac{NYY#I*RbeUax7;C9M^%SFMHs{x3y!-zzOD3#BYmAPjNxG(|oH zJcr`ZFG{rmYr3raa+AFL2vb4)6hO0Q1-WH&{EzQJ zJA2f-B)x^4z>i$=-TnOJm~2EEo?HP}P33NtqR0z(Dc{XPx*U~2W4hGt7>RDFg@c+s ze>ZR_Ak0_jwfE8^@xX4T53d3ZZj&MuM?aB<@sYdf<2AwzI9Pyi@7=OO!ew~XgaNb{6pyCHbs(OCI}v6(CK`ghH&7GUVlc^+iug1RHc?%(bv#bl_GJAtZ)<`oUwnX#__ zCp@|ymeln971QkmxI*e5)pCFtFJmrrqWw8_;~#30YR#2(K07c2Dz$t@QxpY-nzoa4tF_=#KxRz=Ccl=MdGO{+E=huvU`5@fa@}#ouaQhd z+(s9wL37*h^Q621q$0G+uJ4@04zeCKi$bzc*Veq67|xRJ*C?z*pf%;vAbJOL#~tdc?qu$3n0fY?iid{7;gwNfRI7bTo_E4^Xp>kjkW!F^8K3aWSkbGvUdtrd3C z&->Hbk8u)qp#!j$USaa3>kkhszV*ld``k+#)Ysmj3PuL%Kk)vDAoDO5zj2lkSO$#z zeOniB@tww4mlT~Biomsdci;S#&x|A-M% zQy0rES7yemOnp#>JRmhB2t9H;Dhec>*}*9`WN~CT5sc>*Y&4QENkh=*2em@QPxCc_ z40uoEq1I;@+$4=9vb+0pyD|kyVo9gnK==VcTChSx#S(F>e&+;^%^^>EfzGRSs@sW2 z8lIlbdsXgrwmhaW@6IUE$~GA>dP)m* zer_b0=6^-!7Dk076FgMsdz-aFYv;X7&Loc&N-DjBF?!J6(PwNM!tbBM&V!l!tI%ER zU$2w|E7I}&q?}61=3OHTh6xir5N5q)kja&mx8NQ_lf^?R_XCNN!heFqDUhpz1e-D^l~J^w>%+RpeKO09SEau zSdkf9ABhAPuDvw6>gg&HFuB}a?ky!LXOziZebz`(fq1Thiqy)r-HD|UJ=9c`g*k86nDC%f2o$}}A^fKYm;4d&C{@n@udY2#80Hk! z=Yd<;R*{1C@4}=x)0p^OSD#?~Dv|1IK>}$}!c6-X@Nuc!T}Gnyk0yLQC22?)e4yya z^Wqy)n1oO2NUCk|)>BbpMMuvGhY7-zm3s-X1!9?+1e4Ltp$1huS>sYwl}2j4edjhU zHJ1)uD=s9v5m~UY1otrvlzsW%bsDM3*EO!VXB3eBxRgd%bC zy~H25D@%*L_18LSOG!@2C8EAtZj#%l%6Y-Yal`qYUaR|l5~#(v3nclL-HlZE(2D8N zMy-7~+l^L~YNt{7z_62mUFjw^e#Ao-_L-KkP^S6T8eP((=V zovB#9${_|@-P3+>oB3i!+(PU|J({G*lM`^HcL8Br!t7F%O!Gj)&y!4w&>uUHOxylj z7A&grITCioO884i9Bfo0V#!bE3!W?DJXu4t5P_0ab$MIn7^Er4R^-rO(UfY2dpB2K zYBLe`)eJQ}*n^h_KQ?(rdPiMpD{D?n^~ZeDVkIU=dVpNY2OM~B#lcbw`*NZS*%I-5 zAK2`(nmdez_OS`~gu8o=;wJJf!`v_-JR-H!N-!v+UHM0)?Il>PEBM!&oF#6GY+6-b zZ)*~7s0UTO_(@u_U-6J+b}eT4>;wsxf)hC%oR&i2Hmr=1lM?=h)PQ>Sa46um`^6o$jtZUf7J7@mT6I-sNConO! zdlnEr5P~01I1(cj!3)Y8i!}=bc+IL~!xN$G8x*@bN`aui`H!GZ;`(N^)Ky^!4s9Xr zn-FKKW>;8KsA7j*OjFX5??(k6EK#m?Y#%&;68#$OnbFe3?@gZGhPl&Kr^Igy?kJfU zx&O2{oem~^5Hu?(XUR2S~2$v9*~8NPGUNpGbti`u7>$V7o@P)f7$~Xotf4o z%GGO!p6DKZ$n=NZ9fQeE^96WRk0x0}#g(&sZNF9fGS8ju-;yv!;d|b(r0n%eGD(Dx zgfkT)#Hf+34algnDh$M8<})C710u$N14LPATxF|jA(#;WNI5Z1y}f)@3rlTiwOW-` zMxqb^goFtI5J0sVQBomP{>KtXg=7Fjzn{Oq-RywZ-~ap1ZtuCf@NdPu`}cj$dv~7a zJ?*>Bdv|^hcfH!l9F3OnDwo$(CAJ4%`-XAKMH=;Js2v2gZTNz zn_DRJn$MV_`fAWvLzw>nqE3U#xsST#COFUH0rHWtFG>C!C2o;zxjX~FnJ@|>DJKe} zdRfw`^EWKxUyxf?@()pfkIT63F$FEw`HS*ETyt3zSRfqINcD7bJTJ5;m6|DTnK*Xs?)`}j@e!hENZtUH&Sfa;zg1#e~g1%x=NY=Ls zPwYpush6;&(08Qf$E_v_fYjxqnL%l?0-Z!dGsw;X3q|ZYH-1y`+k0iH`s&oJp2#yO zik{!2lW0p-P%k&mDzf7T@(?*Fv8wK4;zBPYu9ZF0(!O(8UP<6I->&qw7tT=E^Kn{> zEKXsBx#4h?UWn@Io(MJr5e?MiLZ2T@7bTz`Urgo)7~<4cJz1l%0>ED4DQ0{#W5Hiz zD0giYN&Rc7&mrKkInu&C21Zw6T5m_u!ZP0sJkO05zd6!hVHj#yp}cyeo^SD^WOZ8N z%+ZBA-iY({sO@n~;9nX%q~G@#qrc5_FY*3zKp00J#Oz*&d16$ zpT{f>Z4oH7&W{6O9dEpFH?cY09ro!SnsCnHqz8srFV;#41t=$n_Bm)HWT=JCba&@_ z(Pz@4eQU)Nd;ISZTjEe!o?;8@Z&^Wi*pjT*5=4%TIBromZr>AkhOhk2hgpdPE!utCG$_HcV#s@2>7^!m(zkO z6OJqH4t1VCbx~pK4Bkp_T{ZJX9t7zZsN~YZ&>HY9gyFTQbbrEH;yy)hwK4qDs)E;!D z0<;4gNgUugHcY~Hnog}n`>RZgLe;Wm$N%#U>qX2(u7+aQQqK`onW+;nO`ao)*)x}? z=@RWsK93J?%$R5F?)j4p-a`W$!JrUG0P(n-2^{lskN@b`k#29*-a%Jxjvb3Y$qOOz zdxn@|>$pccvWnzJ%Va}U3Q0E|CK$EU31+*FMH%@Ms+}g3Pb%XZ*dLV=JbLr#x6juX zO|^-eSG;`zT(CI5yb=l0WCde8#ztSGowqPgc0wkR1aWtxt=%RSd3y7V8vK)JA*TdK&97+cMoBlvg?A3z z5@VN?lZFCT!j+eP=TR|pVP6yLikU>z^ibcd0VcsQ*pb(Qk7&Jg{brtIWM6lr`)hZ?J3HNX;nG`r<_F}8(u2`!(10`(3oF^oLY2db%Jyn8 zX8Xm=F#59QCqnwKmDEMT4z@pYp|DZz&Lq7pX3zxj&{g&=qVr%>#_C421}fL(x}hM% zoEcX0NmfX4Xxx6sFD)kA4r0Z5Sv=R-`T~jue|ewhWd2b}KeHqDr!b|x3*kE7witZD z68}36&Bs017Tcqx^G%-9LWRJglx-wyG~}XgKK(&lQETQUqe924m-_-(LyDE6&g~R6;{&kFcBqX-yi6xS$s)J;Fk+UkSrbMS$}QSd1=q zB0nL=EICEQny#5lnhG+lMSDFeacd4`u^7@;!Xi5dz7z=Y&5xpuACkQL7RUm)_Ccr& zbSaZRfu%+=+Cvs20%W_LS454$M@yeG+23GY0#T=W; zH>Go^*b|>`fZp7(0_3VB_w=+oK`UHGB90RD&GOf)DPEuhZ$rYSd+_C^;Vbb%pWDKF zvV!Lj6wv5x@wLhVxW{+sL^ZOFUjhQ5`0aY@MM=}3SkHV`PRqHQa3x=U@`pREOp0izVH_V{)B`H3HK zr9ModZGSCKg00atC7rkW48wWS+b;MC%iI>*OBUNpMEi;osli2mAuM{T$0?JzgDciM zf-R$?Z)`$?g@V6k-z_#{Z0rYGK_pwEojzBwG7UzyBhS?r5J#hZx{dbK!Gan^Z6Ea5 ziHR;6*BRbgLY5JDoK(hPF(^!kSf+IZY~anV{<%wbB-Y}TW@ZsAZOJr_c*QHK?FU0; zPK8V~YeDswmrjJ?R#_pf=w(fek*npDSID&{KH2tCjhn+%8KoGR8XoUvcn$EuJ%<{P zvg?QqTh(q?$+oaDDEFMO#rlRr7cPMZg`vjPJyUZ72l_H)-0gJePtn`!zL!edZX|uv z2F=IpGHuPpI<$tzbIWxJI$IcWCVqsuNZmV?>n`0p-MvevN0!`z%T(pWM^jo_?}V8& z2Uti6^oP+sQdd}7z=@$_S;R4bLj$kG=7-FrUUnUsi3r=ElQ`Ftc{i|4MYW8NEcFGZ z))vHt5i!AZZqjOnazi-Yr zi?vqKo?+eNhFgc{K13_9s-A!i2@Ju>+%GMAfcC#QfMybhp~X?Q?tb{d=PVAxaA)A6qiRQ0;WBs z@!5;WMs?pEnE3?a_eA5#*D?Q*E9*)_qY-vxhtEM0_&!^?kG3HuFi>N0oQ({uHY!HY z7-i}&mA#I@ls$fM{*!af>0G5rH% z2h2^rppjT|kY=AF6bsvi|2@eItpWY?5ijTV7oQa(2&Jh4KuJ9RFV(!6sCnZOd9#SO z!by0)Irfw9GLNYA(MY%I?62y!uqy_TSSw4?O(@f2Wj~$tG?=oXZ#c$Szp8P1t1Rsw zd|d&hK*h6in8C6iJ1+tZpeqesiP%Ru)aALkY(+yyDZECrPG=XCbBOcxESz8Zhk%E- zzM!GGNj|pa*+gaRi$<|=AJ&s>qj{PU^U5j10*CGhYSrJUuLOGWJqJ3POrqFNwqjme zq{um75hKc40mdaL5#;*DOLM{}5J1LRGX2VwiE_2zoE#s=KE?yn;x%l9PBpr-i_gr#aoK zTaGlHl%_lDQ)Md`=oT-=e4L9~qV-MacXmZ9-^Ps&wKh4I@x0e(p&_Ofpr$Z%pM&}M zTnRnJ4SVt|qa9b8ZrUI<`y@1`>K&aec{iIadPfsl(c52`Te^{u>54uZbUhUXrj(#@ zN?kKJmW)`(E`9=ne`b86?yXsxS0#BdMe)d1AZA&ppiJHAGGLQZ=1_Q?~X_EewD zO;aZJp0#2{f*fq*KsH2^=FKSwK=_cnAFNqcQ>82apwOt+F@dDHAn46Rs=t9QP|m z1z>+#Un2|O-+-sCaQ0Sm&e8;~(0|vAJszK)OcWi|Z%ED!Efb?rY|}_CzWj0*wFd3RnO$~Z)P2yyU&U3n=FxI$?o?FQxfrFoyrHC!$+iC=9Q>Y_s^BPVT;!OA( z2x0a`UcM$+fp3RrDB4+H(X=_S*GjCS1q#1fE)irAq)S_EVZn-VfPB`&^a{kW5Y zMTe?h!=WI<0kVE4Y*5VMGq$pF%U?K`6Z3K!dQT^fM_k9x-CAY#Oj6jO7IOCi! z`OMgX+yTT7W6n=+pc^b2oqL6@z?>UsS?Qjj^63MuxoiY~cZDWJ!eX z@Qs`H^JGR2%7C|;c@$otoxOU82IOCjAG>LHl(WAWQ{RQ~nNfCv+!X7X545N~{BUus zwnv3rN&YdJmTl2xv_1mR7Xf~#8jc2k!URnGGtg)FP-Ajix$w>d)v~UEcg|}elnU{) zu?u33GT;J)NV4eceJ~X~+Bdbyc0Xb)!W%+;Lz=?@ijB4EBa!k7l=|YZ!{dUtdhT`$ zaC$b%QWM@5SaWvRo5MeBj{>-teA|Zf&u#MExTgXqLLEN)VxF0JlH#7*F#v3@2dF9T zdZICIfWll0Iz7E|U_u^oZ^rmN^B-nlrALW1BhWM0HV&kLSpIoFAnhGUzQvunj9q?o zJ90M196|tG$nP7ca3bu}3&CDI1|v zOd%bf0v@S8KyJ$BB_#9fEM3xHwQmh&B_Yu6;m6uVzJpmWdhZ8F8_oI!XNR|QsoE5z z&=>D-Y%PnZgFj^ir(dO3-UFXkj|J}V1ZV9Zb~R573Pii@AyEiUieIejNkHRSRM9lR>`rrY(+Drh` zicykD$c`CGo`eOKjvCLU1CK3iYGUbFP#^;zSXpbKwPROc1!vu3z znO>=z2kZ3LVytRTvXC*+#%(mDWv`Z+RR@+8G^8qDs$=#>A&ZeY=#jm*Cz$t-Ea@r= zAeaj43to}FOF6*uXzeafMta~pbY75gT%4{oi!1QnOku~?CSwh+pm9adj#ihpaPKiT z$%#~^LGC!ow0VAo+{rw^D<9UD;n3|hWwNpe>i6}$-!6u*=xd$*3 zIE6ls0#*{Xu&4N=KszJc&hmE>5@!ro%1YrYzRb&P zO8tERFTo$T&nK<8^hFU8JiG~T0Nan!J)(6qm3h1q6Jm|q5oQZCzk_{_leZ0E6q9<- z2Rq5&NYEtnN=qmhB4TWpHP~cs^+&Z%3TxYOIt5-Eu8V^O7=v_q0=@NP5nWZ`hJe~c z=S7T#=DwrJJEUF-T|Lzm{vjYq4P%N$FB=<`Y}K(ZDb_b4(bR~22Cxw;8b3tTeg|tZ zb6Nn(=~PkckKR4<(*YBa=5mrtSAFQ42i_}b%;7#T=R`-PtvGS-=_%i=K5lQJErR?* z21X3Mqh_)VmDxg91>g!HqN9&`YT*I0s3(;v8#<-yF?y}e=XpD;6A7_JvjeBSySb5R z^P&0PXvLxcSoL?N$!aIFsXZsaihOS}pH7;2bHb#$b6uNu^wH93Md}sRrvb z!*RsVg%5pGZKaG@DrDSsUu2-f&Fq^aN$fKK#>yA+ZOZ`_e8}SBr(*L?0BfQao}Jy8ULKB z6h-sRI857Ki9~!%RF+3|rDey#WOG<^`3us9ajlva^99CNy#U>(v2TLcFcl^&pXc$i zuk5K?OF>^bEIb>_Qs?u6L|M&jsY2gO@|nLS8JYmlVPwvgfr0TUEJ&Iod4<~SvN41n99fXh zZ~^0ndj+`TCm^&SZSFu`9}Vd+sg|7N_Gw`&!(l0Id-gvgTcOX0&Xll|MKfsUQE8sr z9_a666=-c6!+=b98KW?UHBSj?zfZG1VRje-ULiZk3^buxEH|C{u-dZ2WcTU_wdvH2 zoSkhJWT6Za%u;~4M0h`fi4)n;55xx2lq@StK#u1Cl8CYh=KAt$J$RDooRf0OWq$gy zPYAL^^n0Qg*!$R*#;#VZfNN9x5>dSpJCE2$xtA4sj1yd6dxIv&h7$5O5_k#v-Hu|^ z{Vg=H5C6?V3(MR*@OZfA@S&X2-iQMlKFE)=xsj_>o(v^YW<($0mQ?4yQh%Tlo)8hu z(UBFZC{i5iE?nw8(2wyw2n+R{4Ts3;8oC#LIi`F1R{t8im=pG|Z8F^wm|)&Kwf++M zmf{C1DMD`40UzW%0b027cY{{lHj6Y8Et~RVl`Ew)+Y-$_l?Y9_p4I_Ca)J}$ z#6%1tbYY`sCL>zQh^FH8YsHo;0R~Jgcfzrz{EKdY?Ki=(lV>*dg{<;`$@JX;31~dk*hbpR!+W`i>CUHi&AWVyj)1v~jCVI(HkS_B zKzBC6Ga{O=Htuf&nC8w_cB@fe&>KX9h#DAbb{gKVge*_4c2e;u6+vOMRoBuCV%Lf= z*`^_@;uV1yB;u1m-ycM%Dy0Ai18Fan#wk_e@B-&Y7waW;UY&^CeokKZTi|QME_uT^ zzLKiJ19pB&>4e?I4rQy}xT7ydt()qL`2 zo^&30=J{tP`ZDBdW@BC%N!vh7g&&uclX?Uho9e1GZWE~eNe)zCAa|7r@w~LibAd90 z2Nj7PB$+7SbUyX_8yPv|b{<(r_4bm(Qh|jE$lH)aaOA<=Cg40BdN8px0KP zYQ)cI>Vx>dGLtQq#LE+HA3kGntQE`V5K1`7E$ufJ;~2s*QIg`)Ew|NZF05C{@<|X^ zV5^Fm;(CVB`yH#!Vo_m;nQU@>U8S$NmEWvaC3EIgt%S_rN#t%L3g9z%3TB{(W34$* z=Tm!v>5sEi9jqLtS5k7mECWD6gDm3`FA%^M9XM7y3<(*d0pCzaCPDHx|D2%}s+uNl zjy-=CF>?kMA!-jdh%Hx~&s}tZWokbK?|*n}~S+W4*DCIXk6<&s|HM zNXQ|M9^jnnMO6)aZa+Pk=f<7f8~bnDp}`n}J=xDFV7hOtGYTTh{{1Y;zH6u~TnrF4 z^^{t{hf#ul+9Q!>q@_J3Eh3mZ)d(_OOfW2eEi5(@i5iXIu(s8l#iunu{;LOE>?~tZ z&6QG`xmKxvrFxO90Hv^1m^n-a-N>s^Q4P@}uFGLWC1vy++ccxvr%@%;S+tvhU@-Y= zg`3|c5o(%*Z5V5>06Mc5Lbps=v_p{Vn(E77DJMW=;B5M;lwDwogPas#2Fxd0ek~}) zPeNXi;AMqLdnc@b>WX<(&I|xd@-zkJz7rmH0TlU-j2Y9sF;%gS>JBVHXsS)4)GBem z^(~n)sLZ_tUQT_}xSU`SlNiZIXQHP$!1aYfj_=7PJ}gZAV;htJ=(;6;v8hb9r$h7DAqIijATNE0SqTh_`bJhv6ZAFk$ zb8>&2`Wwc86Xx-hA2|ZYG5Fz*e(?yMq4hP#cLHUzneb|>nf%+g&>YW*nFq`#bpz)Z z?NXxIO4r|5@n2SOA8ePmxO~nG#TNb&kY86Xfwu;XN@4w8bO{kgFIyh&umFu z$oCz}R|8wjpJkt-vvx~!qFbkpZlE=}@vX$YcILf((_XzMm#sTX*0l35v{S{uzgmcn zFcbKL6moijCECU63|Eau@INk>YMSXJUZSs%0FBzhR?DAaHJqau*OWU!+a7xA(OU@C zx!m`{O5kb2mLClglQspRVhs(MXms)DbphyeA#yaYL)>m8PfIz1AeJrbZj5`20kPI@F-G2WhxY1+|^Y33qqI(W0g zkSG#aR&L+9)ONIxoYL4S8qT0DNYiME+Rk1Kk>TWz4$CzwlM} zDV5lwljtcn{1hYeRPOM3F*Q+ERapkISJrE9aEysSFLr zgxh)I!C? zQ#QZt+IzsqF95jPr*^APcD!!Gm%C1(E#|wPIq0%8Giv$XKE4y}Q<+(`UF)_L~5+07%cgs$R#%#Ivy znw=wvF)PbUkm!iT_~*Ehez*gT1BH}3)O02k1X?yD--A{dk2@+s4N0dwKu7eV+7nQL zzM?t*f*bX(MSSq~FdXV@-?5JcO~mzxZ(z-gXh_018BwrjfbqBa(%?{;$k|Q(Sx0zE z4!(&lAjCS0HD}^!Q?o6sS#rBnWw2rioVQ+1V);`?D4IUxsQH_w4H|8aF4(ls%RKX` zRN!CY&$M=If2%hmQU1I){Zq!@C9Q`$=OcCpN=q681m*x>+43utD@@tr6$~;>lzstNa#kDLkqQ2PN;+pnIn~ zI{$jo?M2f6qKi?dOwV5t4QfUPx6_=+-1ql(PEG8sdatAZd#-RF#-p14)yHH0+hv0F z|K_dD9_IVpkMmbAH#e!G`jx)?FlQPiANJC)YLgoe%aAuVCD`f||J)X!Aqs_Ky~=z) z6T4LSXkFYV!}Pl*H5?5to3jH<^LAYsK;4}HC)xwdR-{7O0>stb6lR$Pv*oQV_Nx0z z?v~ILqDxi$rhq~cKnbVvitd~2Am^xw6$M&uOinVW0`$u}%ge#DY*j=yWYiUbo}SV= zAH>5Ct2j|U!DYI)hlcKyUbelHmkW&LL!PrNzlrEH`@+{|mj{h54;ow^G`KuzaCp+- z@uk7zOM}F&17XWmS>q?{!u?wj*f%h*y|lCao;FsS#DJZ@g1t8kw_p7B5O+*~hC!C%TUj41O$N zUMg@S`ip)OD83q0#?A_~3Z14&Z85hgQ`s!f}YNMsdc5iEZ*3~Ihx!0Y}a-?_4y7+qQ9Uy+V z=?ciKdbIlc97C(mk5!y&uZ_l$=nv1>KRpS=+KD&f)YwkyPi^5ZSno&6D_j+m!0ODb zos6S%W>oVU28zi3{oU`3!yReiD38t;4z^IbZ%F;d!Ox;w@iWTEbUu1&Yj;* zc66B8Os%mWmCpEtW0$a2%~lARbgCgMb;R@_ui%3j??A^)J9(k;DS|RRfqjVJcxmUT zv0$}Dx8O-X4`Tm6hjuT4rz@VC3!VrW5r^pkkr;omEeU^4ojSXsZjhX5%b_MV>h3p9 z$aU>;g(d=3$bBI+0HGXE6@gy$*cnGQ?x4ItRdmzFYB;Xwj{i5AFQt>!@s#bGH)zM0 z_hC#YsG0>o>8&XUek}19M^cI|Z70}SMWZMec=z+>P9|x&kag<#rO~#xq-Q@fVnQ5B zbyFi~HjHQqzcM|83Dwp4f4|M-SvN_N+uics^?OI!{Ne>SjTKYhWdBWGKv-lVy#z;ujZww3}>+ zgJ#gJ=QvR`LYA-xxB05XJKy6AOiqm^+IOrZiN5O|J8Hr*MeSQS)8GHBEMWZ=c&>Ff zFzh}_S2~T1ZVbF_{sc^oI;DpM8H2xcA>L758k0Ox=mJXR8-X0s3LA?Pb=i+6fbqJ5 z8?Sp7Uss&K+t|r_4Q+Z#!TG_y2>gjKvI@2EmHVN_Al*m$qafgip`rtRjgN7wsleOs z$a>XxCQw1+fLrVZ31fq>4>@@b8uKAXS~1~`k^G=v@001rk}tL`_tlZn7Df~W72_F9 zge&Uvl>CwaC*=Z`xcG)<8X3GVp^MQ%TB4y+2Na>de#V8{O%$CY9V|XdAv?CBzHPU{ z>pOL5o~kT-_Jl)g;|r*wA!5RmHmaEY`9i=s->CbWQGoAc=1%+wHGf)K<*Sr6I9%$v zbrPTPsFwO_0}AQIT8rPgCBzF0Q4}uvXLB?=pAAzpjT_H}h7|-n$KJ%~N&f3N_poN* zYCoW`Sp>M3U6r&Nc~Fxd(oZh5tm^TgtHu{kJvT{@pvn`dXSGC6zBd*NA?`v)-&B`1 zwOj(~dquZfv|x6WZco}s?|WF_F1%uPa2{C+ZtkTf0M^I5Fa}&f@o%`3l^X?JKy0(% zq2f#E?8_zoJM;7ElwK`47w~2J=;NoWGyDUry$DIFr1dHj=hnr9GqoB!dl*P%P4OJ? zL=5Tx3#ohck=gen*2_Uh4tRCR)}udB8eU1_&YHOUe6C%_#ikdDDN$f%J4G3H1szCA zTI{usJ)jgn@hz+`8~s2=R^dI6?I;A?Q3$Mo(6x31IY#{>ly1WbMBTdvG2??munDcx zqa)-56p2Tok@-06G5@7>4T3~phmyaUxDgdx6UlJ7+pmEb#y`vjW`@_s(<`!njHXxF z#^d$$Lpi$?0OaQ;Ow50bou9wLE=ky_TQbpi(f6)v6-(y2UO9C%WQ_5je5IWv!eEA# z70XJK*r^I9EAN0%2dlD)fx7q0oL(5kl|*lgm@!h5D3jHt%yS%K>?L8$6frr}zuUY6 zg5<+F=_m@U+03lzN%8;T6$4+OtNha^^ly|x1&0H{{u$*b_E$2E!HU*+grX~AW+6)A zr(mjq+DZioS3Fef7`gB6Y33A~d3sU5c{@YBCI?{GGVrIBv}u-um<}c9$n?#iWm4xC zHf)FD1M`VH`U0RfU6e72akcxF-aXEHYI!fZC(tu`63}m9;j|cHg9Ef~X06cO;Z*H3 z8A{oPcZ3YK{-{kQ7HvyB^;FpxD|>VeYEO$KcQph`vzx&S{}4yP=PP z;#)(F#qHwF!Hr)iCBM24lanA(brBpxHukRLfGEhJ83uUj-L+8e7(arx`p&LM-Je&4 z?Ot@~#NjI*V0|MCk1lZ^nv3NdsT#e~k*#hA54fA*Qoa*=Ha9+hV?rT26OQm;j%t0* z{J<)(n<*FSZXmBp>J!_DmJtBc;!nQ`Ss@NlW6qq`LoBv7 zxRti|oIs1!>&iW}W4sbV@P!?IuBjjoiF6U3@DsSy8KQ-|B~?tXPJ5N?Pa9kH)C1^k zGt;7OsOWlRzUgK{~AR2GQxoj z)Ft8(HzHuN2RJS-lqoEWtO9LpgTjZs(R@zH8!3R?@QIs3znV)3Uh&I%u2Swt=?0DNLqwIliLzY zw&%yA1kW1?7G4<*jvAelVY6&pAj;&LFa1y6N8(iAN9TtA)f8XK{0P6}4e>#$uGD)p$e(jVSv6OB$<%7nS|zbd zYtiNlW+v@@;!JabD#^^pxK*DR){eAApF^}WI9WaS|E6+Sn3;djf*35b^J)Kfenstu zq+i6z5EfaP|F4BIvh`O3hYR+OlzX~~(?5G*7oP?tjw+$IB;wtRi&2Qhag&Jx20BrJ z#%p&{eU%v$B25a5dgq9&{Y5A46niO`l@0Y$SHBTsV@ZtY-9;zw1CCq9t7Kw7>O;OX z04d|#7Y&?dxdyB0$EkLNPio^!(M+4w6ep?<5meL$kNW(C_9}^>P(USaHx})g089Ws z$YcRFG))2z&;M)QD1+W4XpMI)WqHZFgoSvJ-X{7^i89yZ5*5~pcJ26iGJvId%$2_} z1rQvCBWZ4GL1*(ehl~gl0wLvyUJ3;|EbB>kv@JJV){_sn$Gc1kkLvrOfmJ6mz+f*J zKpHat!ERbCaBz(XapO-E%FZ*h#4jaHLmDHLfp-{QKP?srmK7+LcaD0urMA19ERa|{ zy)^gu-%18cNjxP$f(iC&=(DjW9O@~=`)W#9Z{{X;r5zi6%%6_MltYt&Ic#@_kcn`n z|CR-ryh;m_)tQ6Z=#2;w{|H2BT7*SLp%L>iEX*2eI$=yUB6lq? zqS*IP)$2PV1}(?LU>Owi97Pr00fkQ$98(S4;E<(CE8B)M&BG9P)nTgbjk$j+MoD-<(m^pQc0kOP?ED!YsTc`NyS{s&KycqrSqz=`3_hYs~1vruTPjJ{l$Fg#L1tR;%2VzG&~fYmUIi#W$k&UO99;!P|THn2oe zwTdl=n0FIqB!sLHVyoesoMHw<7zY?`m~jKSJ>17Y<_=eI!^O?73f+I}Fv5#Q z5>Y$L!NW3tI2AMYhaXTmsS)>uq7G`%bie*HFkjd4ZV>)Y{4~UqHO}w1N0$YYEi22* zB_ahWwQ&QFk;$Z>$Ex&+1G%XvOKO=ayD+Ie#o&Wnh(SR=xS_2PFl#u@9AyKM5-p-T zKNOUzv_9VwzG`;hi}8E2ttG^nF~Ub?U|BneIQXZO0iwi<+cWAdaSUejcI2qL2oeL& zNw+7u)dj52j~XR;LDH{|9+YYf?wg5WDiBbiW8)4F{-yvXpRiatL1ni7t-q90P5IOCemrXj@a!8>dwml`-(0@ z6j8~ducD{pKWywgn8W}A7)VrTC9G>9HD;SW&$(c<(k$OQEn~HX428dQurwhI-5?dW z$nU8Nbf)@Wc-tQQp#=&`n=!#-zI?AK7mSiefUR$(_KE)MsLk5>{Tnxj2XE;P|X ztbLUfQF-4y_bXam7UAlhj3i&&I4f0Zk{?uf@}e|avdFU+a^Qk zHZco91~^FE|NWdiN|asz|HvN0CHSAZ`Jtr zBC~wIu1g+qS#;Jm;o-VEnWQ9uJ{(OdV zE_tAaW-7}45Od>0i1w{pl%KP2B^k1Q+09Ho?MO9~FP(DfxnRP~=RoAb*X;=nstp?Q zc*4wgqD;hgpPn%*f;#q+AG0K@;v*oQy|{_zLh78$c57V)pcuI<&`q5)t27Q7?>W zZx~e*TdBc*&`vvAH9#O+h9rcDPMei`2Dzekiu_2IH4cdqQ%NlZ|Wq|0@ZR{#fwA>FEx1H0E0D8gmAA(o#JzJDeu4BsFKiZ%pv7 z(de0b-4Vt6K2%ty6|eS>@f~EM>40NXgE-L!e?+%`JLhwsm__9_+*P|i2qpv7qw6}eA&N_{Q}UwNPByPPuxMs ztr+jw@VXk<#yv9fCTc->V5ErGB4;D8rMqbZMg(jO_4Mff%N}%ocF43=;^?s}Mg#Wa1#QxCLl{xJM>iBIfMU#~Yq zZHDaI9p?Mg!+uoJ-ujqtyZ$iI*VbWM(Guczt3vFhcqz4cDZfLp>EFC0(eTcXUYb04 z;#?QY-!fuzH;TeMWRBDKMk_??nBET9(r$pYl*Ns^C-r!fo4A=j`~0JG=9AYJknHyK z^yUWan4DH`y|9b9VaF$ZwiLLbN>OdGzvHc~*xshF)QWdxtgjX(U8~0p(=k}b+lYkl zWqHLGLD3})%4uEo61*g4!UDs(u(4NcQR)hO!4~o(XoY)V%I`_CQ8Ug@4RJn*y0f&7 zE$IA<5tQC0m0F}gXMeR~K#lWj4rlLdzMH(OdZ`S)0SG7k!QzOE+V2L5 z0ksyqfg^7w95CxG!{v5GF%7{mpcL2g>6-k^`IPgw={JYk6jMgtl*5~% zl1RGz7LHq*4!(LQyF)zhE^H2}FEXDu7A(hvrRNjG$PK{Xg6ClMUWjkoo-+|KwV1h* z5WY#bB-%_F&s0WaVVInv|Nclk?1>9zR5T%54u^3z=O#pmbt;pYsf;&|_6Sx2onX@# z5QQQych)d7g)Tr9CSy8(!FTu0l3wGfleP}8(jGeE_2L&x&(V+kiWL|$=Ui%Tv@X0#R>P@ofnwrPxJi>) zpB2u+9rR4WFAwRrHp6`s+X;fumf|G|OPCIW{?eGRQ4nfRC<%OF&?lDHywvoGTsXeB z`|}lFL-FP)7q`y7x?uyiA|)W;*Y`mZw7fWW`;r~Ngo^)t(d%1oipyj*|Ba={wTgoe z`oOl{DRy-nuln`pftUZuG3#V9fn}-okK^F^60yX^3dp6n`VvME1}}JFZ)VTwt&#~P zq`YFQTgA!?H&wbwVDl*sc@e6$tUw7Q2y3;TG z^laR^4@Sb>o{jBK6O{M2WtYisBvRWOeHxI!*e@tyOU~oRG{W)+!x1~HYmI}Uz%qe} z3<-T1;0D@a9f`jc+lT9;$jc;MnELtr0ZIK2t`F)`GdwyfFdE|68%^co8NAZqfOhbB z8YuW$Tw^eebvY=kVYiEX2%T;gwm6xy{~>L;;{YF^=dcW$)9V-usG8JdxrWcVh~~_o z9&o^0=?mme5B>u5o#KLd!2e+!LOSQ1QDuJ2Qs9<_^Y;d2G>46q{CFafJP|5KV%uhF z=YUpRr^@jM9oI)m+vVy+uZy8S#&25=Bt@s?!8?HLHUo849XH47iPaj}q45h88na5T zH&q%`C7}ptV$&g6Ekp}tb*`SeMrGB=-Ex18zNhiG>}~Ds6K{8pb^=CMo2|STR4cVPkw-YV zcvDUoEokx>!DmLa!Ms?h!|EFj5*>AsXgFIb8>OR>6}VyNJlK3GA*G{|DNXdWcOmqq zmW{aDjtgpA=247hMHreT7|A5R3|Q_27cQurhpoO_B5&H3#6P_)iV?+U1mc`?MG1mM z0^CjhN$Z4EQ8m?D5RIl#gJjP*M#Cs^sWCIRFHe(UfGLBZ#w~odY;@-2i0U%kiIh$e ze(p@Sybw3`R-*(`9IxUD+d3F0&HG*ZxH=2~Qu#14yGcAs@+SzZXUpeq?dks0OU&)$ zn4L0;Jfv46H`09Kqzkfh;m|L5!;LRKyUZo!;2siSEvanBYxd0^tpK2Qm9C$=TpRp* zZ7lqqQXFY%B?QL$S^lXQjF@1`!sI$?UlP853ELJUD(!jpeXf<}5>#A;zb>om{sT*n zzktzl*Y<_*+3P+omF527T=M@I(MEq#Hu;jAz#i-*v)rD`T35 z3ski2RmWkv3PI;lz~c$5nKSDlR-*Y{bnWmV3Wb6+)HARTlA z8d24Nex8I=(>Ha7e;PG|h(YWs7*ndf(L80&3kFUB-Gz|GRs$A7A zB})aXLtDX3uq$zD0Zf!+B*tB@?_kR%7*fpnP8K8QT8+Xe9wy-a@&JPO&!Ucc`Ps^% zx>b#zgrg3v1R*kk9(f)QEPdycOrXKyqj1h|$Pn(0xmuGaq|xnVBJ58z<}dpO5h}_A zpxz&-aQgIY-z}D{WUQx@Jw+yXeGuRGa8_+=BzoT=MuFYzL~Q|ql3{^v3sb31WlguB+oM_ECI`l+fsmaY?o9R^&9u2iu`Kt+ypLZ6 zec&M-;~4%hX`ThZx_q>zF5gN=CrV(CFIKr8!w_<@PTX$Rz-odAd!AYhXFNQS=Q5?7 zrf(JHK8^BBTD&3w=1?wUab5}B=*W9va0h|S?+c%r2?b{gr!qOTn7~}(Q$R7TLI&Lq z8*(C={oY0Aqnqn0)>`16TTr$$XUk@*L(}mLpWTt4@9nmXh}t~c6C~l!7>QQAC;^ct z0Rozea2CW67Vx3L>i}E+Plm2sUX%!AX0dSuOCrI+kM?ix zW{Dm5guy8f?xTn>Mm_BAH(Xr_Tox#2m^AXo9Tqm$qDpp&wNED0=B47W(6}sy8_~`e zg69RAOhHoy*t7(xXbc$?u6hj9v#b~A!XC%_`LE%&JU8|kd{Cqf;p$Kpk4LF6oK~NC%sQ? z!)pu2%$$r8w$JX{u>R>)us#DQ_CWA9j=C@IPqMtw#D_b)TKIc7WhSS^!#Vjq;`&%H zBRR=Z2ps%FXrrV*0DSGlzwQcO_3}KJVDw7yM7S~}4@T2I&=Q5&VpnN|3~}EE_`P0F zUXzQpo(g+lgld7XIDzM4e53^P=1i(Qd2VjdwRiY8YEpyhN}wBljgqa={Ckw9h^}7( zRormjKB+D?7xTNxf34p$Z+E$PF>d?bm9^c4I`j9bQ>kQjX}C!rEmUB7YHV2-m%Zt( z*tfvrJF2zB8qeH#*)Qu*gtn3ZE| z&{zVWI6G3hD850%=UftCyb6TiF{OaO)~K~Df{S+U6obSUDq9G4LK0Q`{nEBsIH>e| zkCrC1Wy~Cge$^m<9?*s%5L~gg%}MQQ@#3=Dl%1-uW)-t_>W5D!~pW4qh8IK zwSKo&!&lLRP=@77JscV{pHV-*p%u>uhQ}l1il*JT;vGe)DYWePi<#rC{(-5g$*USa zMJpD2@>KhYB*LF{W~bN0=h>TV`{AKAHE_fZDp!+Gh-*WE?`6DV2e`1YC~Lqv)uBYp zjSQ!SY3P_qW?01lx@SJ4mJu+Kn!6|HKVETB21Or%5I7L6!7@%Hku}{wawXRMBg^ z?d_3h5SUJK`ad2)jzxz-*6W8g2)-;9(#)+?C|ue{h|c6Ho~)uSDpq zN{g#a6pBd#-IWw^sFJpWkCC13SP>)#F#)ctH!Zxe3}Q+!75*$+IzBI=$u4n@MTN2( zdnw`Xmz>x$i!?^|anj_5w2^FDGbE2JBU>8>D11!xZ*uCf3fBP>SY)UkcdRu7>p5xQ z9Khfo)lnCKeZd`UKFqoXRQMxPFBVjOB0ed?;nN28cQ$t(IJo|2j7WBm5{zOxJ%6Lq zD!|?{e6RS{NhQR5EL0lHQz(gZwC^uChh zv{iaSZ5y_k&{+c(7HIJ_#Dd(HM2A9-F9T1HSJ}AMmY>eI|J;d2ZK(P)&jr(Es{m|bMm^jTh1VQ?pD>$+C z^J0t;zJ^qtR5`#U2CMV(y4FTp0y*==7zs+v82L6pT4c-8*2_jcT!L|fQ!=Bfx9(m#<3N^r zLAeYb@lr_@l}-)-mKaySFsdc8z(-orzr)st|GMImgPtxRSPT%(a)JUn*VANFT5yLl z@3034#QhaLoD1m4UM~Th0V@pRi{gXlX(iq2OCh3HoL1k{g&MZzBi$##KuzhiI|qvM zXqEd&>R5VZr}4o>uL>{yG{*-3;T7}Q7_FLfuz0$cFc{vbu~Z^V(>iDH84Pv5EnbjD ziH*x9MlympS2IXUs(~e!osksqerTCESrCBmY$9aq8|$qFKQnMj6AJkzWnD!{7vZ0u z`blrWBwK%!kp#zqSy#?;CcSI-jjVh zZZV7%zeDg&4iQA&Njev21A-PeYtr-`!Q)AWa|t6p*7l;WG-|3doD%mgRm00cQG%_j@Fl8ri_mwz02!}6O2-rNhE$&*G%?k<6lbeQF- ziJEuqxedzR*^fIf^fo-*cIBryDD$pI?9$r#C)B=iw!t0Q@e!zQR$l!F710^aw57m7 zCa)@$e%8{G|BMe-wRKeA$|HNkwywD}(d)dpS^-+&@=xFMNI|KOnzAYmK$q9@e^ZS~ z_w+3+eOOPIZu6LoNO#K9zSU9S;*p;^3M1zy+dXeT%!9~{6Wq*|g{&H)Vf=fl+G&-k zoHOIjQ#Pg}f{~=PF`^NB4^@WQeFS19`QyK9VeC>j$+8(+N;G{XBkh@(!5EBKx~o3f zX1DQ+ni-66YSevaD2(DIrca6Yn^M(Psp76sQ#}@d!!zo&M#(PI;y$*VFQs7~4h&ba z3{jZpo1e7XSiH~gxGNs{&6+=$)j1>b$@fsJMV#Md(bpuNjUN`Ir1oFAn3qOVppn@< zW@#OeaQOBK28-a_RRwSWa%nLm!DhYfNhfWpI?uegzL5QT2)dTfFxXq^Jmv$Qo>HD9 z0*jb{Ql^<<^L!Lt@Ta`dw2Q3SI4ohY2yz;BN7Z@13EGBEHyrogdyb;o-(V2iTv6g9 z{VhvESnHDhkqs=~Uru^JU8DZ^t4*RiFS)Kt(`Grq2yJA=K`4^bzIdg7br*}+%%JiB z;)t>q2zU|@Ze2$~QBZL8Copvwk*0<7T2i}JUFgE%@ zG%GC)$`3iCeIY;Q7Uav4l5eE`$Kfj9NkT1V*0yRvpiHPfN-hLCI$j*96 z?#SWQhNnpEEn@h-vj`7Cy25}ggeF&!76H2Uhd;*68$Kh&@iuQ4p6P0f07YDhUNI+l zo?`LG5I~jTpZi`KCh)9^S^r{C3!e_5u%btBntgqYRrB6lOCdm<32D9Vtm{3oS+nA7 z`0#j*Ao1EGD;~rUE6w~r$m}1SSP>l9uO9*4J*3_|PMzYq;EVOWN@EbMhDI4wc@bd=;9T|23WfWLPXX3FWseV@H!r- zEral{aquvTI)*L5)ceSL6Ir+QMypTnka^#d1e`emV1IX@a}poE?_HUeM=~F>Bp?{4 zx#xsU^j|CW!~J7Pe($2+kt+!gWX6|9uy6Vbc#jq5K*PRP(HA^HC!F5XgjzjCYJv-+ z+L=B&yaBY4%Iyxn%i$Ko6}&;MY01>OJ$WNf|4{O>10^f3@GZyWeT z3&;7+AX#Ck=}fPa4vpIZ(Uf|u2k|oTX})fxKv#dzl(@ebySs^E^?upEP7?=f(8dQ< zt6Z%Hu^riUqSqHSFP6^BsRpWD>#dW0CGD94=7;grOJX8a!-qvr5x$b-DIUSlxMc!E zTzc>jKM~~SW=03(Og#9=i5*wxija92@|JrS5O*2FOUu*Gh-Tse^|N5*t~*nl=m-V; z=Sh~3H4mL~UWi$41DJ%FmPnu!@0O;OM(xx{luN&jh>e)>AW(-ia#XYcRKWRzo0^Gn zwXvqNBN3_$*?=BqdT*{wGcw9$Y?f>E)D??>q}I$WJQQ36I&At#-r>)%XMIyjjUL3_ zlrTPvNUUOjU=2>?_Az$P<_Jj)0UJ9=D3R=vnLySLXM@=Kg$-j)8m-q%Mo|rPl_1bG ztN7NmfZ zp{YK0dfyk!N3L+!S^SYSY)P!H<>A;6imC$M) zt&Eo)&0zKoe7omgd;EHfc+LsT^b`awGSn3TA|1r3QlDu5u2Egf#b`9ZV!v1mTkazm z_<&6U;(LlMMe96TgG~bE4mJ*UVUzYRF(?4W(eVAE!z2L<8(pL;F~aEh?Yj5m=o^SU z;XsEeWhR?p7qWk>X`YFw2#bgH3%*IPz}xv!64$UxC+<^&jHNW+8Q#0ny&A$K1lz+) zX!PxUGqB<|35MV0xF5C-+bf=pOQ(5d&_N5wmII(k#h|cww}-4pGqJW!&Tvh^!Vnv& z2SH#8kVfmVh-dICxl(8iqFgtL?h82se`*Vd^Ri){2W@uW_E&+8;S;irqsmv%SR5x$ z#~^DMg9F_tFdBxcLyu)*c*ap@cgO&xaz%nZU5|YFdH7ub1y7-G~Qa z7La#oWb!hCrW|o&ZtXWox5L{Hw`bXb6yet>5k#)k3e^hHMCQ%&uciJ4;@H`G0En$! zfEOA94Z5Wu=Ug&&Ya;DlRRekseFJxuWQF!G6{6M`62K;6v#!qJT-m2fsI?5e0DOb3 z)C>th^_&dxauIdgf8kR=-{YyFUE?T(jk^eaTgO|B{>fVt+m-I!a-V}JgIubi)hh#7 z@z;l}5$BB{<>Fx6?Fub9EMS$?bUprprF?Cxt1lg~Rd20rkWN>gmWM5%)&_Mg+Xl6S!RSn)Rm0?hAS3v1RgG_?`9db5G$UKZcmInVR9iyf&eG$XZ3w4iqgQ4k z55luaJ*gtCuhLDlRE0z$Dw<3qlYT1mE8qWtf@At&f+!SKY(ms>Yk0D;lyWhiC@Y?Z zW+wiXUAqqdl>m)Mqik0-qXb#OtX1(S@M}-WuA@23RpDGzoC%74Gz-ep^0o8kOq@!Y z$@y><>;M;>sZc`!c}cKg9P@vU%((Z{h(!6jBUK)PEf>H!C*`}NZ>_>lX)NE#y855E zc&`wca}u!?!rKq*$gn9C=5Ah^Qgb1Arg4o|AttVFRTxoTT>=o&um-r*xDE_JE?TKW z>6M!FHRUs!_nv0FxFA$GS*)+bnW}bTQ}J_TM4lZsOg9` z?UAmCKnbrquJ#WP@I{Dine7+a$$?RucIfp5j3Gxw7~v2~(SZ~i_`+}CKs0b5qu4+g3;B z+XAoFp>2F+5tAtKE^4BeF9)RCHzp}t>#XX&*4cM8F=|O03Qz}&coJm;$K{oEnU#Ta z5r50FOQlnN-DrSB8J`p zBU=z6*hLkj3&m)Up9nzfh@{%}rGV4%o)Je+3}2S?6`*73a+`7Dv-2+%#b6bm!H;Y* zv-oJI@*HK!i`yR*LUPO2#$etCaywKvKcph)anphsY8)%ocvDr=Uv)my$+(8~%~?Ed z_kRt%)r$D`Zx@(xPqcX77r%I}R|+G^e6K2_ykJ2LAc~;6AmX5~M3C3da=$r6F0=~; zCaeTiB^3>v{u!I_L+>CFF;RXy+&hYl>`Fm>FJM6MkZ!0q1jmB4v`sE@-7Pi>O50{A!^;>C2%7xOnspO_y5JH8hq zf1MRS8U8@VJrBkg8HohkSS(bEM1zdMwnynw+@yqdsnNBT+BU{$Hs}mDJLdalr#jiA z0_%5A#&4zd3IEo5`-A|wQ+eINt3SgkXSS=yfaG000fV7oavA?W5TuY?NLv zyG<}x?@YBBzQ=A*bZiO@jQ~AB!oP(gUrui-91|5IGe)?6$?8C`T;xg1r_6H(d;{Gk0*M%mnlbpp;9joV znsUNz%@pEB?36lP=%vNXn-Nyc3zxUp9u)tr9kY5XRoyV6frcy5f^tr%-i{W<;f~~R zI^6K2<|dN{6KNdAbhfxe(v%UTR#~smlp%2G#EgTZ;+QzgHS!JB`Ho79&^UbSe*Wk{ zFnf&(ufuSp9y5r1B4(~tqD>`lPlC85p31afXY@3Ui&u3i5I?kfNAw0vnyHXMw<12K zRE?s{a{c105QXqZMPTsa!SKi?k1!}QF}L*4Ifw%)AqZp6fghewu~+jG!3ghCsUdgO zIT@aXqRL_of_VtN`bWEoB#|F=KIFtZ(S5C-f}&>byIXW3Gv_#WLa^s=B$ghjq~5cx zPT;zbVRf*v49z9<#QeH(Ee5^5lK@M2wD1|+4O-lKXt2OONYh;@wtG`#c_~zyP{fW( z{@%s981Qc==Ji_F_wji&5W;$~P?~qHEB_%Lo(%rNE297!Buge;!I(wpnn6#yZ=_hn zzt0d01qS}@5;!BNe>Fu<_CJ^n25^Wya#c7=%{#pU)FXR*Od-3pZ+i2#z`l!Y4!2$7 z-ntu4l)e#q+ZtT#w9#~yoLu39A8lELfE?p@9CZhQ9E(B{9<;cx&lp)tUIU_dSy zajG-}(ian?aYPa}+n2N+q9!-?{IlZ^;pLq4o;S=~`exWKJDAfZ9DFa#TZ@X}7tx@f zI{N9Z!OUX5!^})=m5q@kT663ae?njN(U>&Xx>T7OHxl}FWCwYIfgM$Q`2=WZ!%#i~ zU>7UzBsCOVJs_gfGPf==M{IrR$ht*fO*}{UG<`0Z_8mAkKZqY5{y>z!)Cg4%w;8Sj z-7p>RhxL&Ps2_F$51JKDw=IvNORCY~6~a7t#Ih7X-z=C`xNafXjgMvO9><%QAcl`n zx2MTOGp5`eK1q)BodQQo{H+*)8@G72Rz`q#%(B_$$nG|W*p_~whP)u1d~G-1Udxm# z8$|jbSw)ScVe+(#dOAWS`*#93Md4H-kuiViQchhXo!&L_-UO@Vzbv_TH;s=qZlNbZ zUjylXke;dR)obNYrB^yL!KwGO`h7Tho(qDAaEi%S;G+9tS=McOT3N zIrBv5yad8BeQ1bFR(#zN|5T5H$oi(k(~7OrooONNtupz|){1mD=^uZBo;7GqH^u4o z1tW#W44xc$%_InSMvv>#K4|bs8u{QLrJ$Z&QcxIE7bNEWGe%1;u*z@enG^GUp_J`A z`Za~3Zz>n9ds+i1^r%|)!^s;U4ySRGP~R`U47Ki|DcO#TCo?4rV(32Z_)x5JOp#vp zB2ymGk;RJne6AJ#5hed4xuhz$VWG5xyM29;I_i|PKPS>)VXw4mp15k5cjC9{?XCB* zZ}GNA$GkJO)uz|amnle*2~&3z_%EY9lpS6(hpN>;?}G)p<7;-!MRx-CUN3Y?@5SP( zT=+Svcsm919|vbOXTi>lJ6sQDe@$lx(lz~=a8artv+Xp*@d#a+u#mO{mIKm0s&Em8 zAMVLTP9=G&FPp-L5Y(k5=X=p+9A5DA$=LCi1Itz3qYD7TA-Rl*rcq8kyTd1&AkOgi zqEToPG+}G*bBUZ}NwOMiH@G0&Pa4(&laYDs&I6Yqat~JwN;dUdGGXrAq}L0rGA(4| z5{M$} z6*LT>M;%l@1ZgR)Qe#!%O0g8R)LQ-Tp!*lZJodU9l$0EfKV+L63+vUi0y?hKrx>1b zH!kDRil)sagCPpc)@3e?=-*OjFB<%8&5Gx`RL52pOe}Pq!W_*-hXF;T<_Nsj&?Bl3 zSIL&g`O;gewW#;TAQ{A&R+yC~#l8^Cw}D_pBH_|pAW0K#R4K=)*lrLmFkaTkC|C8f zVTJR*)At(2Pu`eH;I#KO7A7kDiK&_uHgotkkHjeRzM5#|6Gw15KRGmP(?;0n)lDB5 zplw>1%%Li8B{bWCO_L&X4b8Bt?<6%lQqdGM=E^|_kV zzCPkPf+f_PqYsm95su0$mPj9NVv6*G_1FomdE1aR*%=$kMj_Wvkc=Ae5AKUS5tH&I z^X&bnr~46Mhrl&PQF)l%hRJ~Y=wV1uIaz9KIDu`X8yvoOqHc*OyTKaCyGZxTwe$-& ziBtyZA@qRLJC~~L-NE3;LE-3L)k2PYv*IaL$c%dusq9L)JQiwrF$C2SA2YA#Lu;4* zS$myl2k(5Y!Y2(+GWk5dL{aqqAnjQf`n#)3 zES+aCF>5ae4?@ZNRRLQZ!j-!NjM%0cOc49VgAS>rVqHTk+ZqP)10%;3RA@Kcy2d_3 z7p3+=hOWD5bk3Prq)PZ$@T!!LRoE!5$whXrh^Ss(o$IS7#N6GIjp%l$f!47Eb~sMr z^ae-)J*2i*w0nJNILMX3(i9Ck05A}WA~TFXT9tE3{*u{P4u}o^<_@9NnAU}YtN@DH zH?bi>0bEK|LX6t|VhPt(!wLp})zmaHErNr;X(EmvS#63C!IHY7ty!zv7R;O2DF>v` zfyx)1!AV^rih*uU02pps3!I|65qFm;8N^L*rX@qB#FUq!fhn;-h9+W^;C{5nP6`UH zM*39m()mCC@BY_P|NJel=2Y@r02i}C@RIft%p<-ns=XUEAk?@cRbcP=ARwg*RqwGJ z$#7ygaSNSqUdbzQ*c_0DH>_=OCuL!T6ZX4_xMEGFuT|bd1~Pa1@es2BzlI2aq3D&a zVk^g>6SHh>5jFo0FhiHNe;HiNF^YG}9Y*Jj7U>F34Y5swQE3@~LPN%)DrGXZq~bN@ zD*W;UZtR)mD9y)M3_|Fd(STM8u@FO z>K4yxxGjeTY-pqQY7&)4(_CHQtzX8}XSdwFO<*umAIzT>@|mwVw{b&G^-#87JRSUV+N2VMLi?|G zSkiLfvn0$WNJwS?K*a%Z7`95$WNKS7!c#Gs1Gu>s2nZxfl@+6+DM^@+KqM|m6Q^!k z-B#Axlx^E?y4ziCZ2($?2uXl77y^ibkU<0X9RL6cASBNJ{rmg5AUj*jyY74Vw|UQV z-vd79J@6&(s>~jrf2Kn+2$5SO+6L-LDvC8BJX-!S>X zRT}3;y;44rq4`wlJBA*;F!j$i*$dhoorI|uXS{G*1HUp8L-ZKnCB-s{pL&iwo6jlX z?7O}t(>NJGzB{pkj%hKj!PUvX7r5t;`m)!!D%9$WeL@GU<#sSU6C)RiA(C00FJ?wC z$0-G0eI_XYrj;c@ll}sV99HQ&@d(+_L(!-X8a%Xs_Gq+T9bLFwY!`eHlmL?ZP*%my zBdx;;;YQ50gBwGMQ5rXrqC!GVx907a&J7ki_2rUnG zA^iU)K#d#oj!}c#Ci{iwHif%_ZeWf+duj{i3}u*7IW7dyqwlz)-J>d0&iBfSMD z5$Xwr1{>=ggrUxT5*xK=%H)XSY(0?D+UY@51>-clE~)@N<`yM^VI*^%tNY=Ed&+$}g%D`0>%CobDm6~lckXk}h zfVcSEdW82BU6mhB#e_;dtT;}8GQnmYv3ZgZLW7Mt;#^+zpr^b8J{2w6NYAmZO$_pTRmxQ`bP5i#cYp4 zSBmHR_@>JSw44A=sYo^H)UuJg^4DhZ9r}Iy`k9Migo7nu4N?A^uA*imddEo`d+79z z4=IvEyE#pb8sh>+S*Jy!JjpDS%)^LfvL)D7oks*|P&$yfc-p zq8s#13UJCKsPZ}mV`&k7>Wl1+@6{S*0>)?rShG%%iNhSR&^4&SJC1eZlJ}Rzlxqc9 zk4M?UADBy~=*pQX`UvP+Bg^PsE%RVY$U%*x&M6m(bdN&|cpyB`**0bfaCCaZ0o@kG z_4qTEKkF=rR4Kn6nx&!bx1tn^G0;;99a9OWANV(ML%{#v4_*KHn|~oMDqvm%|I4F# zHI@ji30?n8I+MQ@iK27bezQb}R%w;-or2MxcKY{yOzp>+g%Lk?of*Ztb?cWx9M7&= zc^kCYt8JCDP7Iu?jied5-|d-40OfHT z6a7DOOBG%~GzEzZIDImWPs4O|9tc@y209CgT5SMFi)QJ4!gcy119$%CecHLJb;^(kON!)tJ@8X@h&a<~&#vzdu#j~GWEg-zQjG+Q-Q_KX_ zpo!g!mAXz^^nxFSr9EuLIJlzJBqfK{cle-O?g+Xx%kR;tgyem>pGcMJ6 zdU`H7n%JxF!-)CMHcZpu*$U@JlFphhfHm3ses1PQsfw?iUD3Ci}`cYXcPVNfVz#j*i(xb6kPgyRKLRZ5O?x%TB z_Dx^VPNWe}&Nxe884K~N*X6ns4ZcB(Yd*iaAf_l8kKR;8EpVbDVFn`7!2{13r&re= zFHM)0DO~8=1;7~0C1N^>yBDb78Wzp4-oLehJjZDbwXHKBSteWgrq%sAV)%565{qD( zzc}LQkaQiI3FUOF$j+D4u?|G{TL4P-2|Dz2BeLe7VmQ1nxMcIAm@|dCXF9I{trv}y zMFgyr8wL%r*+}6^fr&mpK2ME2gp-$=IKBADk~Hp})qdh1UtQ4^j%BP%<#l6S1Ua%M z?A3QiMG?C0j#3C;-yU3#z-!H=%rAqM!xpm*2vt#I%MAmfwDvSGU_Q?9rxX_7Errx7 ziBJy1e5sjRz{Tvfd9iy-45eMX1=<;N2(&bpvG!v4axn>XLExBaRp9 zWMGoFr%)Vbc{Oi?>G06-;>Nf(Es1d2*T_-RCwuz16Kg~~8(Tk+SDR9eCLo+C-zghJ zF@i-{<&;~J_c8Yp%|J!tz>k;PmHSO}R~suYB6zM)eH z7+hJugZYn$WW#+jdZ0Y*{_1+PXqj?ZaS(U30tb1g$h5v2C3cY=$~FEC;zS$6zY&Jl z!5S`z{Apu#7T4)fy*!HvKwJg&*rst*nZ;6Loz;y?WZgco?WL^Mxsmy>Wb3iUKc|rU zlZE7baSp&klZ5@wtG1qfUvTEXToVm_T_>=II8Xue%R|59I#5{I2E-vByug4zTGA)mEK(-^UhGzvtLLXUY` zDUst^)3kWM8rGeP%UXFfAutJPADk6^^@vfL-Su-BM>YiY^j-VOot<~qm|Fwn_35#D zbFqEPHY}1m$FpMZ>%9M;32XYX)z}rWi^O>s<8>Ek@%@d^Up^FR+F+nF`yZA3UyaDK zxaq7k(whz|XcjqUkvGs|Xp?X{Do}A$d(6=|9ubh{vfq5ENqJ6#o6U7 zY1-QM@@Qmj$5$8P!*Pr7oY;FL&^Wl#;^TkTU&}Aof7gYWmKO4fhv47h;Rrlu?}}XR z-)h>G+JCH?C+{zaRIO$q)CFv7tCUR<0haL+T%X2Vjcp+ zX%74{rxLq?O?8$qC%VTvsEr2WVf`XV^e$C@0PA0i;y+vJ_=J+s-rVr)mj4lOBoq;V zmS5u=Ly_Bbi58q|kLxs05LW*YNX$0KQ%4V{tF=fk%q)NwErR-@S%|jzQk5Xe4{1lc zu*nAQ)&?BrU~2j4INPy)YwU?)`Z7ut@eE-|e^t!{3F??TS?T zO_5j=UPj?ZFhobKWMNOS|LZlI>Di>3M(A$4Ku83J77G|{Ow;+!iOs3sQxai>yTE7Y zfB9}9{Py9;_O~71PTXD$$}g&snO}Wh_8FF^33xLnU+gn1k?CyJm!+Kk4jLm#ew6)r zk4ocgzh);g=;Wm+?$;J;%#&E8pkW<=#f!(7ydGmLS6zfYav1b>lRrHpgi72ftzuC( zeOZE-`8lLD<(u^gQ!}5h)G2X-T5VxH+125m(8Rz$bE|OmM$slfHo@F&NbJ^5m$y47 zXBJ3dnI$Sx}zxEAq21zy%ibSzK) z{C{pw{=9XZE!zLTgHw6nzzjC6fPPTPbpc(2^M+c3iJ^GvIE5MbyRU^~Gb4;E6y5Hg zo`3tNOpni}M_=1cpOQ`Poia-E=&lS)5|PM8-w2Xq8bM?xWqBEC9wd@cnUkG?c=Cod z7MxxcV(l3mB26Cbk@x6e*c9g7m7&O){3bE5OSh$r}A7veG4Lj~ORHM3){1_pZM)DmRn6Ybf zCbzzMLMqqcEgjY7M-UuDhqzDbHHrKuP-`7ocbQOJcsTTGFlH_O#D)|CtI+)g8!+)B z+hR_puKYqh!kQZa-&^)ngxs$nQ5$jVlWNkEq)nPDA5S{N0TCFw4+v(ZGypc>N>UK{ zCN2u4WC*p@^`tBCiBTp&$wW!h+(ZR~aL*ISg(#X+}oK`H`&U4a6cDwt|I`u+#ahjak4XZ?5w6)Qh!c5AI4cJ#o?`HKl2@ zDAr(-G22}-VNGv;!z2?2;|^X z+1JY%38ssW)|UB^+80qNnc|@K4{2ce&l$uTIJ1X*)&p3ju)rG`Qp%&{3=EK$~Fk_q*L#@FbO*t3guNHvjb7E65db#u-a zU6Pb9z>QPCi_vnnP3Mx{E!Hw6^gq8dv3{*f#p8}9d})xXe8Rami#P$<|5v+sm-sQN zX2W^-@h(wMa4)W zBU0e%$qZ>8#mTK1ml)`)84Y*SJMV_9bE>3Ayf*IVRiJCG?T1pTMX7EZB(Eq|xX1Yr zLo49NW?*@bTP7mQvBm+!UmlB>=U?VgAtkyajoR?S$Y&?lE@A&)<(7!prk;=0tP8kJ}ixFgP1# zi$o-b7>n%4l!~5ujcnAQv|rFIk-{7Q1ZBK{7EV*=9V1JEeI0PRVj7=hp8-aZtgZh! zucR|j=Cb0)YGjf`d{$FQ1q?^z=@+Yt{h7qFC)}pa!|dpouB>o9YMGK#15J}!{f#Uv=+9JHlw z5iK~%uC|c-AXkL0n+k(~oCkdcUNSv@mP-%8)S3ry0yr`+txiAZQ?Mz$bLMEhRN8EZ z(iZVIb&DjnpF$iic#p!*ebwc#pIklVn4u1B(3PR`MN?g&ZpK<38uJHhCjNWNM#}bH zH1zE~7szT$cMCx4&6;np9439IC&wDTuDk3Bt$Ey4QCaSR&m2sEX%>9dNgDPx*otPF z7VNB%3WJKS-V0xH!*J%DCTD8;zmERL$6_0JO5HE|pvBoZgBPTvPrSQuw#xG30SLtK zuVn~IB!xji59?^lf^Qxkptwd9R2fkma$Tmwas5C=lE`D>%i7-y6zE?Sfy1O;THu_hQA+0)oD6R|3J+en>C z7t+njp<>FROGYtn=3LkrRMp+DeksiBOF|MfCZmT@ZGymqy{Ne%hK+KTaC}9%^sp;_ zB{LPZEhYQLpYtVYqPQZpLM4}FdTS_-7NDn|xTxH5~4qV2^HTzMJl zlH(_6i_c2b_<2h)S)+O&05+eOyw8{61x0!K(zR4pMI7)Vej&8|>{S$yW0_si_Xv)K z2A>#N@zJ41&g{tznHlL8wbMv#fGBM57th8c?Uhp=T-TG2Okl;2Oi&E~b1AA^vdJP@ zi1~5nLN`aUdgtQnrj7>eBh7HkPpwKdwvebU#>9k8Axq`~py?olBif@&(UQzq#)6~S zXRZ1hW&7e}dS5``+uo9n%Hm4_qN;0H9f4CFluO`mO=ezR zx{h!r1pmBKgbqu6OdWJxsNvyv+j+v~ntklzA-jzwK_cy4FPp5ak+|rc} zI%bsSOnLH4CKRXn6w^QDWm-mz3|*Qvo9lxmRX5NN!q`MES>^QqJOHyqZ4uN@)_sra2m0a!4l(WF$j#R^x zD4IhEDGFgoD7FY*VtvKFvpa8hxH2gpv#9y~oAQ00zh8oX)V+e${;>CsozS)>f#Xi-}2OM^~GgTrQuo1^S^NdHbumLM(R z4zNo8+mcaKvx4XSeiV6S#!DFP^WBO(;2NYRb+6HE(y$US$&2!D1h5 zL+0_#e)JBJGg36>fc!(a zp=--Q(c8AvoCKOed(Ep2$Ne&rUz;v@#N-+>0=z~t4h5K_U#v9-een|y^zjT;0yDS; z!cR4}_j`5bs~jrcKjv`Jd-A&*o#L~;!EwuU&k(IH?@-~Tn$x>+^+ja&qUTX%TQwcN zyHJ|~nSSt+EpU4m`4o#=xF~RD1cbRUgrL0jBVesCWdI1g0W3dt9ph$2(ND88aoz2t zdE_BAv|BY5;fVDIrr+zz_93BBG2474R?%-&-PA4gwnRahhu}%UufhjLP;W1P;iM&h zc3AKpW)y5rte2+6j@)*8dCT74m|d`P{8y;YZzFr#(-&>9*;n8IY;#^tHI>Cz!?L2> z70YbR%Q?rw5d)ZmutdLg8y01wd%S+G>C9z`yp^$j>?LXaRTar=5s3K80XZ#XC8_;b zDz^8-Ze(1WXgP~oE$u{U=;O`9KL-nnyM$*s;%+fyu@j2_Tv$A!-W`(olKD;w=8W~R zFr|SwH8UI73f$C2@V*xh*!Q2S9!BJ)XfZJp;)j5*DSA~i7GgOtW44Y~kGNSjtsWiO z5o@6QE*eT{t)IFf3jQS(sMaHm&MCaqYZv(h@Aq|5f>oBYyw11(&8l6ayU0OXEYP8~ zffLAs7SX~K5V*+a&SX!iyKAv-1EEiX_+x&T>^;TqeccND$} zAqS67haHigk8x*w|xS>YGeI-0$GM<<;N^ zB-@w@?`S=xeh2%Wg*?v89x7O17I!d2N7%oMzs5zfzIOU~wzfUP3;KSP#5u9nwe8rY z9cB4I*ntPkQ}4*(q>kEIFDSAB17FsR!mJ2{5Xx3axmjGnS|niw!;am!^}K6)t`H>p zJ&q|IOh~>s0V%9PHrYJVQo}k*UEkwRnC8K_&ctd2Ptm0wq$j8$aP|o9PZ>cPq4h|0 zbeMy=j^EK9zG~jH-s2RK}-H@Dh^_lf@YcA zGJ_->G>3v3;%}_+?e4v0TjWI_BknXaHpB7}^?0QjjpQm0h|7ODy^S!9)__kF7PrfeWy7U?}Yi*xy-}W%YYOU~vZ1WA>53H@5{Fb)( zP@pa1m_rra8YG~cA~(eiFvUcEZ|b7Bq7Wb0Be>v%{i9{t)MgKPvH48zHl3lR6vo+c zrjLO}P^6BIuYz|umpwo2jw!C2YUu!+Y*#qCcY*%>5@gB`^`bG-O)PjS*=t=M_ki_= zXMqOqh72+988xp26|)on>4+(m!)MG1YAv9nN1~nwh}7|qf$%$~GLW_ppvoa`Gbx$* zL&eD@e(RiiF*1ygXjsQuF^$Ocr_qrxN#>``J(F%fUPOTgfq%)oGt2P|*oO{|ISzsz zD4})Loq3`Jgv~rC^SniiV$_AXH)#~p)EtoJ(a;*^qF|g&x60KAPLeItBT*o7jlUvE z(=q9&$^}sy1FT@mq0jW0ur$SPUpz(>PFlK5fzEh&w*;!@VMeb;2fVA>SIr+CU32ws`=D0MrX}GNEa~B}j zu!OcJqB}_JlV-xztCu&7ntQQ}OMx8CAxH zPXrxYKB1NkzRJNaAve)@^s06{0=;@wEI{#re8MQ=M2Cz~!12uwv6wW5PBnbXgR3gJfnqa?B_F>?qy0 z)x1nA-IR9#H-NE27H)N2$d0t`c|)wy3Z)|2OC!P+lK%x(TAJ0jjML^<@Y()$DAFM$k(k z`g?v%viv1vDM%=)xyTkg&4&T7WGcraIMItpO8l$pRYmSY9G6R6@0{wh&Q|aoc#CB3 zi({KC=S#W(HlZC`^anb-AUlQ5BfSEZ5OJ2&&pQQZc4PRp0E+XZWy@V26z!H_V zs_FMz7j^FL9N}mp{z|+A_KC1vPk5E(8~+@EB2ZZsqj=IL0;O#mv|;-ejWV_tQkdl# z-E?}H_WFmd)ZIzFJ!}HxN$W`UKvkxC--hKO|4~%2@hXTTFqH>Y)S%ikLAU9jyYi-h zBT@xgItMs}uqp23jR|ce??xZok{3wKUN2bI09A7koAu2dscqmv*sh4aCV*N zP0TO+OykA5>CEE&a&Q1Sl&EtlaCGlF^TQ-$e-Tq`qa{Ae-M;LqcaoZQ;%VFOQl0f~ z6Hd+@=4PNZ4-f||4*SdYZ)Ypo++I#q;m2$L;D(u#dI({)y^r1k)eV^&^1~|kSFKwn zP|B8+tR%{4T$lxM3>YG@;jQf32u}MWPr#L_FbUouEl_B0Z|9^X8)Wm$+Jf!I!Zal0E_|#b@`5T)sw{e7Y&!lrgK}_sNMU!W&PmyHjdegJ%d8bffz<6*|bn>J5M0w2w>Fo%HP#>4xAj~+n@!l+OW0St66i-oX0 zBDp4p5a(;}^_o3QehjCM3=XS#4$NA)=Za8r6pJQbJS8?I=r?YU8wk;-h))Q6Iz3Mh zg1>90ry2DRtHrnp*#JiZ2}YgSfasMUBh2cIPD84E+ODXYP%L2VK@}NNZI_M>s9=nT z-4C#(P&|MW`+XHzXbglmnso{99v^oJ?t$b?WGA_a_>W(?)nsS7u{{vpt->%|vrkvA z0Nnx7$Q1c;m1&(~y|~SnNW}b-O`*BF!6kXik_>G@uH)sbwI>@)SVFFv4IEUn@tWtnB5WwLIL>tlnJKjfXQ*5@T) zm+HZhCOvB-QHO^O;WF=qQE#rI-P3|>3%edG(-Ve~pw|DoSSVSQXKq+8VQ_jcjo^82 z`@y4MRubQ6CFkTK9P9AHwZ6N@)a?Ca*vTG1*HSg}q?tuH4hM1`?8s2R%2gZv{4?;_ z-`r`618rhs2!j7`KJSnevwZ*CD@G@6S;{Q5QZ_iqO@!BH^*cSvFlPz6V&3U!p?&^P zhaVZeF?z3BL+eypml#}ec`l*gG5YNGr)TdY;}I)0+w!E$RHy1W$FH}9A4R-@FACSt zqaF6~^anTnS*KIa+em=a_c5NymR8jXPX1lmcI`gY{;Lnaniz9TGrhloRC~s~>7hY(!!MqTI z>4;YK-B*BaG$dGUy@$t+5cU#hUJ{dQIBmLyOL~P0=cTl^x6`^ooZcyGlPDXRIzaXVe&YEGnmdZ zsC6En)K0eInd4Y}P^i5ou`m&KZCR!jrp_bpl`&;_0l7;&mD2Ab>-j}hIk4*n$;HHNjhmHpVORoDvN1FxJCe35KO^Xxz%%@tImIO?u(-SH9J@L z8!310U|J1b(`aH}MVW09|0CHwsUES>qH;7r8$}Hs-x5-0E^GY)@FKo<417F?MYK^} zj3nRB^Se&DU@hwwFr$^rVTU@UyyDzD3^ZxE;6QuKe&-#!d$Dtlwu`{&J@X45>hFr5 z*s=1^mWb`mf6;1HzBi;A#&-VK8`72^7j2fN%#CHgR~sZqghaNack)i{Uv!$*|-Jxs0e`ZoYRIWp(~qqH{;Fp;dVmNYU#4)V&G44mZpIJVZ)X_PD0ex zg+{=h|Ddom3Jd9C7jp>{uq%)J~E#;Sccaldc~*Ie$s}dZ(#Ph z*K^WoUpDPB8Z`>H1xMH!U|;XU8OgeLK<>BPD0czvM*nP**>m{+Eg%=6jI zvk8)CD8;0$>;8@rPU(>Ze#t_OWj9#CT^`hL;A)aDa`CVFG#b@&VeXv|>=jx9m+G8} zvyGD?*2mlJAwA+l!!uRNs%ZE}8!s!nNr#xOx5*MNSx?5k@wdp4me4_sViF)|q0C`@%RhbN)$NnmT5BcY;z z$(pwDvyD+p@|7p^K%L@XL)CoHAy%3h(4HW$XB<)^uYH*+vA=jf^r4 zo)$jvi|bvQ)bshsSU@p-y;YojGvT6>?`fbj!X$~L@T6uLpJ;xbM8Q z(SB*semqy2A7Dsw5(Pk1Tq_C0Uz&n}vy%S$;N2kFPv0Epc(r^kE8cvDA#&u&8 z=dN(7rDhfz8_0SR)r1&D6kEVE)e_slqx;1o^l%`f_xIX-)Y_X4JhWaGEl9U`9QF17 zyyLU3CcvKi!8<-f_d2_x2j2VCVS*|gvisE*occd#)g$Z2*NC+=wwGiRa)lZHDh#Dd zAH98-hUTCuSp?NAC&8ua6|~dSPC|`Uf`GMKXbWfpdie|S8@he_owbW;>UDXQ5ukQl zXYPH?F6&zEQyT(RbL4!g5*Q7=yY zjig5KL*%~YcS2qr`n#auz9pc!fo8awRl4%J(~$HEdM>lrsrheBs1whB5g-fn<9pB#M0Ov^*s}C-Sd3@M?>v7NO&5AGC13 zuj9k-3D9~rl@4Y_w^ixMbH5-=Kf!cH~?8hMxYHU)qz(Qzkr%~>2b?+_?|q^Qn1 z8qcSV-nAHp+4|Wms4Q3rS#d=*7Q}PH@2=aOG}SlvHmsqA1;Z zd3cD32DKw<)i$s7ulFu3Wuk1foC2kwjV8Q;ElapDzi~uP9UJwd9Z0vFW8B)md$`Z_ z@HG9fcnhMX0DH@Z)MiD)UShkFd|Crty<=+)#7WYsZ@cmfbv*gI1hM5UP1F+5#0wnf zN($e|swfK7-6;2w4rN8LuHaHS&&?OL;Cr%ITog0vFTtn=$IC%mx^^rXSf~qi&=uj( zZmcdVaj;hU;6-jAP;elv_5piy!Xaa#D2+aSuwsY@XWD|VbnWi*JBlS-bL4Ki?ju;8 z#X@V?FL@C^F=EKzey&O!Pp||&aewH7s4tXPbVG=1%U69tx+)HU~<&rNdirJ8M&cA2n(M#y%o-@MCHyTBOkb z5Kl!H?`Uhg#pQCEGHJWv=w6-mkYuz-U*iRT`q-gto5EYr=QZ4iH|866F5aO1etf>E<5&Q#BDVL zJgCFx)lw`s1a78L>`YY2Dv*yxI6_mc9wiJVzKBY~9ug5C`a#2fz>8TExka$6{DiF$ z?(5#{+D_iFeU{s8Cn<0^08GaND1`&)oK1 z@2>kE`tQ5%egu2n+Q%e~w?XZ-g^mjKAhSXoa@|iL>qYB^QRt}V)Ylw zgU+{&0s646t4{+i6+FXOGRZ;^gIkVCl2I>I7{EseF_jXHl%k7XPXg3!Z3vFBo~((% z%!1p=-CUXN<93Wb>6KxQf>7V|5s`=^$5`liQ`y}sp;Gl!DZ^S|fJnF&r%^C0LDtG> z|Bo&;{y-naYkmSowMblS;%ItBa(hJ@<~F8K6MSRB7jFvEk=qP}3<}+SJf=OnC=(jYuiv6&DxAo4lb7$r*Bt@1 zvj;WML(vrdB0>ART_mxVCoe&96Ie-H*Rqrs`BJh&PE^S(;H0y1L;n^@8(I)_*)dv14u0gN+95$t9LDrBP#%K=)fW1>`AGzckm#T zIN;DRUbA+THqxe1XV6njxd*zi_Rk!?VDf@9{Mm#pVDB#?Ez_$vw@%$vUqtq~kr=%r zD(4*7G}Y?DSyxH}t?JT)PQ8pQPzfi|mB0;;lr0R^bR^D_a z+c?Of=g5$skMv|jr~$ds8ds_(FCN~sf|APl*6)%0RnxoDA5-SYRBgsb5`J0W*P>4x?C*Kn4*V3fd@FKuy%i^LG>>DxjkUg663=e2a7 z1L7y&NwgiXX|4ZBMBQ`_fvjwxW}1>=qTPntrL-SvHwa&*qWge4^)n{$f96|`Xz<-iO)BzbFA#YK11br5C=3Ny zvVX8hL@wIQDTR$99HgLvz0-nOj1Yt|zmy>?B%4vIIbZ$;X-@8v#IjwUD48&RWT5i) zr1fM^3&08k&IpW3!5=6K^z2)w(>oJ$ox-pU9TQ5u2VczPFw=x@ii6{KYw(&Oij1#onv<;&-?ZBog|ZFV%y2Y z6Wg|J+jdSe@x-=m+qP}nJkd%1`Mr+&?p05Et$Nhm)z?+E_WtbFxycZ(uM9%Aj*eHq zxjgK?J!j&x4a*g@wNfmGk%$%#2YmhvA5xl5XJCst7|aM8@a^} zkKBS_|{s zil;G%3<@HKro*VodOFOuPtt2L7fkV9O>x9Q5g?> zuiw(aa&LA#)QP>p^2iq#u1*gRR}6H1rve6eL(>63J0FYQc3`yJ-}|vbmt@a91k@i= z+8Xq#VKwx1HPINUet)y__3kIWbG`!z0bpubb~hEvS)rVE_#vP`{q@# z8V?r%1w}*1Rop)NkXxPrM!yN2q?Z=hCaHG4MwEzhB+$H8Ge2yzkA=s%vWPz3#l{=W z%5&aXuk!qmZ-m<9DVFJ`n|BmkyIAsJ8>LX6u96#Ilph}QHXWr8exi~85qJvT5OO!|fb7#?!=?GNPuCla=}qN*kL(W9{&IuP>eKkrgsbShBD~x$0O5hNS2mw$;4fs2E(8Dr%$7*a1~NXavw>I z5NVv$4WRnop0t}f5C!3=f4`gKH0xd z@XofxWAty2|94wpI~AkH(Tt&I$fPFcM6=9bhbuWz$73E6;d+rSJOd^3%2UC;TOL2m zwqf}@)?K#5Z89JoRldw(U_Bs2;`hE3ToRFo+4{ajNC3PAfSQJEDo5Dd4=FhZ!#6%oifIvz}A{&!%cWsGY zX{@<8RvV+WD3t1s#$|`xO;SsE4^DTWZ)2n&M2u*#YAGRQ((vhCu+M)d5WNbJt`RRHT0_ma{Y}p>nfNQ) zNEbrZo-7A3L`(a2u10W_Ojj$!k zNi;^sNT;)fv1-Fu)NgPbm~zq7^qTAu_Uoln946klBE_%$XR?~bwg*Y*&Kge+veER^>8WVp~Gyy|Y zZ+**Ifdk%{*sOhA-AEhfn7vHU(Hb`qnXIL)_1EzyB=(38@{DX(U>6KNdAoxBUnF;P z!wvB9KktnLWG9h{IT~<+c0F|(E&4r3Px0DGyJXyR@;MfWkq_IFrU5H>b?=9wiZI@J zf#*fP8xIX;H0laj0-``Z6`Zc^Ae26z!296A^*@>qEQ^~I=VT{yRM&Uz#5vtfl;nIs zdRA2652E&QX=KBW_HilfSR2{|FVdo`|H!bc0BgRsVuvu_kI) z2g=jBB_VP$H(f{TZQ2%VqX@4}?Lzo#Mun$Au=&AQY}m$cImBx%*!Xa6;uRMDS-H<5 zUn}dl)xd2iW=x#pq;E}+0s9>%Yq$6?7+%@Luc=H~>(G4O^Dqrq)AV|F36PCwp*?$t((*nW`u^i8Im?K@uiNlt0x~|{@w};m|u=06m zcDQeptSxGGS-q7tU?X7LlUe25$yNU0I+Do?2{Ow0J*4@|!8TIyg+FChNoMt&rNNgY z%%iY)G$h+qC`WCWTVYd4p6WzBo43S45%wW8eG|)%ET1K?HG1RB*tlen^=U(j`M$ui zlgLF#+0+MJT-{(|Lu z^VWpCl`;Mc+@&7o%~`L^Z)GKpv5I}JIaebxaO%gaj==L0c|Gp=<0Ud8DwXH>Bl$_8 zQ7HKXXl)Ank5@Jg#nhHa2t^tO20n61%6HRZw$v?gB$Xk~I<|^7TUS%4z9wrDTfSsW zHCDKuIm)gEbw4SCq%}|BMK5iZt0;Lg^)=!zLesDA`+D2OS(~v0)xqvg;bw{lwP6)BDaM_@2>)3*4C zpL?8kwVdX*2ztslB5gb=AS2jDQoyZO*MJr04WdHO0_hb!d7jPhC(7<9?E@RG%TZvM zQ=AB`+-!5_<=S)4#40Q6}6^<1qwQ<_P%Tkh7hvNs8g{Bpz zWGZrn@OGU^3Q9>yo+swRTnfA>sxh`TLxNxta=T8UwYexo%BR(1j)Vye*u%rH$15-mb}3_jqNtbAjHE8~DivAOUIZ z2TrSIhvu!iqxwdQ9l8goGeAGDEsv`=T_(=NtUhR>+JxEg*o z>j#T@ci^?$RLWbRB7XS`vR~ffHI4U3c^O4@Hy^0A*6A5`%Q((nKhj~vG~5K(sa`aN z#xc2=Ge!}9DOcZ*D?L&7h1fDt{)Xp?By8bow<;cZeB?LqcNfH>NhoK;_8Q{-TGvhx z=M(=#aTY#RNeYm|Jm(g!N(w_cacwLTqYJ}o#7*rooA}|!2yBi-Jg5Ti3XAn6 zY5G3q>!>I-*7Aav3 z=W#Henvqka6M2467LFK4$g~<=T1xTkhqUQKs2Avq-zJ}*W6tEn_AE$6EkWQ)%x8)? zO`d{G_naP4;f*Y(aTfI08J-Zdyv1@|42EvavZx@|Zt3{P8%Zy?3#par%3T=RxgBwe ztf{8{S15$iiNO~+i`S#HYlF}3m-DM%Gg*6{c&6w@{7Co3(cOZ4iNsW9%i zTX4DIt7&IeV_+lss)ATiXyb2vsbLvETY!wk`Wn%!EG>SX^Cm(jMW7a*IyAy)#5=C> zMu=#6526t*;o?Y4s|H?CdkT4VQ*(Qwap?;=Y6{kW^rxKb*|Bm@T8Z?3!tT0!&j-mFd?F~tch>8Bb=Zw zk&PS0P3P=)rKy~7Tp(K}NKN}JwBY>59#Z}{*GT-2#}v$eKEaeRJm3=~au`kH9v#jT z1j^Ef#5p+*996N`+zW=X%#7GW9fg%BdfSgW=1#bTiCqGNT=S(pc08) zN_Rvu&f5Ig2i}Oc_T*Igj-_^dfGLeav>|*yo=5J5-kIO%)fmHO8uB54v{QEHG3dW+ z`V*2VvEaT4%8m&a7*ZYIs$J6>nyyo8v35UK*mCx~IpX_A)%93CDm#@GD$gE^EK_`k z(s8zoxe@nt>H@~~h_Gw?=)T;A2cV|Bx!BCVy zpvp4BRkS5ubOMLmaZpaOgCAWBy4%Jf3Z?5^;;Vg7=~DCO-S%?n8ad>O3zsdWr)Wc_ zk?wzJF@q&nX=+ooT)rGagyy|1`j1dLH+?IsJRVrRt=OFwv<}9BM1QtEtTvm>U3UwQ zd#}JDa}uu+pC`+8LQ1l@xfOca<}W1 zTg{r+PqTNe&9$e?eO3Ks2Ob4z6p9hfwX;h{iHQ&Xu8ds2}BF-~Zw zvY8!0Z%$U$;GkYzgq&d8XEz1|gWTTqKyX;|z@_h|8EoWyq0!tQB2}`!`EW<=+~IFX z4zcZ6qITI`2xCdVwv!s&;tm3rq+JOZrO4Tgnxpc&+g}Q24;l@A4y#n$Q^#Mqb1l+tiB3`;+m0Jz zrw2;a5!VmGKnlbkCQ!WdP~EbQfq^Roayw)ILEXabeN(q5z-X1 zV%wtKeXpgwjj6UfuR>HeBT%dBiM|+<95ctTy2))(PQ^%YW&=CE{iEh9ng*ns z$plBl2o%$$yq%+lDTNTqPHplgaiP{@zuyf+xC`5c%(?9jg<^L?e*I!)$RbIabIN0c zfl5XKt;i(A{By^@qoXJ3^CXdLw{?Ily@{TRJLJt8+uaI!l~P5_n?fZ3%Pg6cWY1;~gtr^h}fW(eEB!xiVt( z4Yz{0SKBD<{i%@lHVnzLAFu)mS+;*UJIE{`xot>UGSzU_;ys$pd8YQb(UVW%Z=AoQ zyq4|9-rS;`k47v+X3QMQ&Io;*xi)=U)v9!6+o}{!`;05pm|6tW-J+p*f7QL_Hd~tf z+i6O3AG9FGGSp>D51Pzg-DF0yiM3)KV?&W=;x8$!gaDg z+QyPSerh_~jupbM4UCq}BMI7`3Rtwt@s2N!vK>GO284b`eeMs^Nn?)v_iWxmg#=kP z6IRL_ASk?)S@NHw)*U}`l9x%&A<9@c!z5PwmDD*}$92Mj->a}ousEAef14a)VwJ-F zx1;Z9czq00?DK~5mW<~<(DdbV;ii8W8P?-0JT&m^UNng0a5##1jI1LkrJOCsN1$HfQUbh%0|_lAp)YQSF0XVGBoh8`c`hQTyt zvRzI7@%PVuXsz+IBN&m3$;Im$_+EL6!K%yx!7zpWZ8WE0J8mOOCl23sV(%AtFZY*p zLC+(qD51hLWB37w`|Es1B4-A}DO6;LR?Rw{y8k#di1(O)Kql4uJZ&6Z%v ztY=%jF*;=Mo7OX!xle2ezUS8>9%nPq^MJ|9VH(CC^Aqb+BoQ^P+4OSU^Q8 zIPEu1jp|3uU}AmLbsGZ8-rYn44beD4N|KSh{Z#T4R+B&>TjXuF=SpryXXjMnJ8{w{ z)yCr)rD`gj^IHIuIx4PTeylUj3bDvw?9P8;}yvY39fwr?nbo=}_& zR4}^}B7=3ve>Ls8Sq- z@48(wZHDCcOYUgyP>VfP1^FWl0J;WU2b3kJc&#Vi1Hr%T&Bv<4u1#|CDbV*3@RXK;l4Qrj;Nt~~r+O6e z%oQS&`<>P;Y#{@3`~$`-o!%z)R(yG zJx<=DoDH5f##<3HDGc7PG4;+aVG@)zU>91DXc7cqCrE*87)*>BZ-VcI3m(rk*G;Rf zKKots#wsg3e&2vz;IA@PVIaC)-2*et$}~UITiV$Om*&}i5yxk=cO2yOEOz@of=~Kot^c0opa1eN3BoSCe+w6oaDirT35e4Ex}}Z-I8l~{WH0ur{&R2 zmPIa)&)FLgQN7hy9n4NKmvb3yQ!QY?=mAQF*XL|1SbHUAOO?J>w6gINH^a;MlX2Sj zQTteylRvU?zzbId#Oe&lb6K`e8K;9pK_X?{=iLRmDjIy zFB)DZmH6y(c2C9&h{47>gML7(Tlkcfm3fCj_6W5!9V)KMg_FsGRKapW)lD6t znlq=3?e7`JBv-rGOcd@|oTPR@r5fMOCG8t2+OaPm4aYpk_|>%kcXNm6@m5l^vx1$) z$|3k3+6Q^)UjWv62;pnONUT!j_QVbW(?Y&WC5bhc}4^^p7CxM@?yk2Kw9&egrc~ z@z>PJEU{Py%^f!ERyCT*9(h+l!=zXDaf2(aaLl$>k6~-))q;36L@gdA8kssj%+l0~ z>zr<*LIi>PC3Hxw|3u#)r%ol8mby-?HyJ+>&fcx)@#&6yRpB@A?}3+y!qe|GKD`EQJErs%wV5~d3p5TsH_h2l zdruGEutmHb=nsJ357QeS7cM>Ws%=+JJKi+>RhqB$oi)boi#&I)aC@HUB-$%AtAYat z0CaoD{OdN|4m!f%g-K6Y7v?N_f`qZc)=BdYf`Y;JmBE?np^~juCt2tqPH%RpM zKJE578!GhNJIgJwdhPDgXRCU_xfrAo(@=Kf;r8&MuccnNG|sU7wmmcO#@iEdJ`dbt zuH1=eH4n*ph^Bvht&OuXUepGrkv3`Z0j=e1C zH&;e^c%VD6kln7jy2a$y!Z|QzG%VmhUJrz?3?~PaIi3HUky%iYu)=$grRlF$J9R{Yq$piH73)eoRm{2+etNr5o9zCKz!uAKdvLtNfFH+U6FYAghm{mebpY!3?u)Es}cb%;hm0Pw`6r#>HOIQpN<(NK$Z5H|| zF}YveZVYhoZ!DTH$(M2W;Ez@EY{Nch2esPOR~QFyFyKr8Aqn5RzJY zGr@=s=-|p?FJReq4p|07-L}H`oOu~kyQK=XQLq);hfc3ciVXku+i*$R`>PJNUE_ul zO0Vhi2dfjY^EN}VLz&jE4%VwJ?IC1MW_fT=7wsmuNxt=aweWAY#pv*F)# zfLs*;g?H}m@B6lWFJ=|u%1zIVR=*E2tqKAgk)!WFS36f`Hd7E&#@ynxstE;kJi9o* z9i&=El7{)+ zEHQyazTz{}LMC8tjN)yL?+`U}yscDk{MXUA-2l7(1~KvGo%L4hFR*fT$dW2gVb?}Tb+m8a_jQ`_({KFGC76RS8aQ_ z>eT5`GnY5E@ad*tb?g;vwiPcc;Q3#R;D+l1ald7Dbn)L#PzuLKKuvbGehRUrdVV5H&Zv>B+9+ zB|RF9)Q{XUn6_h11>Zjv4n#|lUA~g@hAQ2&r{pt}pdku;s#{uF75R|wO}&^3gZpf2 zp#qnl+{$Qxy3*Rg`lcsaVEXjIFnjuRBfVzkdAC&VII_8(q@KSI2Lwg|EKEZ*0nM5k zBn0D?%_A(j6SMisgDYybwN1rkHb-1V+cV|<(vRwP`j^0#GcJ*guHt|$l?S?sDy zuIe_J96s>94uZ5P*W(2#m>jFa8198jzCJ@!uXiV`X{9AoEM_WY>ueTdXse56^{+Y; zVt>KqYz4j+pQD*x1&bT`=?tw+-OvG!;g0;mpHJozdw5IH=*zm*ZR?y{ z@E>RrK*4DB)d)Q+E~}8Kda<0`Vb|1kJa=u8zG%e+wlrmB6{W_>CKD-SC&{nOOnlZX zHbMjs&znia!PQPazJ%XAF@fv+#uK0P6rrRt(Rl^c^Znqnv)w6Db_Jzu0y7ukB}i?v zx=r%;Vn~xLVyccR$SV|)M1wnmG%~3$oaK<)T=2%}g_kzsAJxqhWGlC>Q)QO*R}a|f zK1*WuoLLtS(=304b(&mb_~C6`y*FU-QBTf#>l+&9o_(gy!VziWtB;}fwFSLdPBc|H zIZU6?ODsEu#VD95!tY>vuqNc6-pne?r0`ssOOjmEkrJJM9}lh-%T9@nFzt{T?(V#S zev}U`!EK~7_%<(yt7*z_SiZqz=;kqt=fuINGz#tm+nZP7&QvWOc!J$4} zbfQv92`6qiUb{eR^LHzJ&Ein&LktJmUkY36gB9n8$={5Be?Dfi1-J?LfL1-?!CN|3zD-VBcZE={ZUlPE9L{E* zeYZ~Fm6H#*fHC*Ly+iG<80Be1K_Y%SchDSvf@n&Sc>5*EZ{}T9AYqzUb}%O5#lJBn z%MI;^tn&MQmywY(kR z?%9DOYv2IHqUPa-KHGPXoB{F#d-6BkTlO5lL4Er8H@!g1FXQ3a!QI9dv>q+|(Q_E8 zDqcDF1+)DeSWq_mX$Tmr0DL%q$ZXizdfGI7Ro?K~bg}zA=o?n;GPSHIxF^VT*7Qj4 zZtB-$iA!&tHfl!N-M=5h`src<+a`|XdeRCG-0*Yuo>l55Ub7eBT|SN!ib^&*wVg75=p1P4dwA1G^f(ym#4nofiU|8S5`|~WX zogfB;xBl4<`u1tVjlC=TL9>Bn6MBJ^S>Md?Q^a@jl+4Zan#?N81s8L&_Xcgz_r|gr`hW zE3b1NkFk7wUokP4&i~%eW(&5)X(m+k96lWGzC4!C_g)}$pP_5gCrP&dea+$UuZsH4 z)4ZS7JQPs8A?rV>0Gte#sQGQ85tBvzCfH|Mytl?@o<(}Lw(c%F?cQ@{9?;dZ^vUtQ z-rpyD{OtFZxApImbT-HlzlQzVfrN{C2Y2rPpXUd{64kCXNO^(_N6;fk$U@|6tLLk$ z+iD7~r6A)PIpwvq1_u54v}QvC7qm$4yXI(2`*}LN=E{=Y^4FtrX%r&i&XV^P;BRom=*SH}@1mai$9lA% zeDh*1=!rexk#g}m?z-;#onM{vp2l6VcsGi7QS+X2tA>05PmgDSxPF2CU)un@ny2LR z_Ydfr22rm^^<9!KBO(D+tJq#InhmSAmyb?AeNajmGKfE1>vd+hrYE6QeP`npsjr^* z>Rp9&`m{-o7ob(SABQh8@?NSaL~#1 zNtRznZ>IoAis6fU_^w{Bq%GF^txI?37RXC@HYdIVk`D?}e$jb$Qom3I`U-sf;C++; zvc=dkS8c<_&pb(M_Uz?n>j!%4V)=;@>=$fv>xAUR*#qZp2E@DALxPO+dno$8&a8o$ zRX3l~>kpqw*!G}P^PQ2-o+ALi{i+iW!Cpzs0{8Nh?#;zLi+;X*tED~Fil=Tg(>EcP z%75Usd~J<%CjsFh%EpH^#SfSvD!leO{(k@5^HlRzrKD9Y`Y9!hy>(n=x&i%|Qtn>a zuD)YSxPAVBIHIdZ*_BQ+p8L}j{ctYz8B;yvCif1#QHoxb@zaH7YG*&c%M09lOQ`!g zmY~e&qV{W#?=zq7snl+mJO6|7^<%zV|EcF8=K<}8(XGT2M`z`tGv|$nSBuk(!}n+V zo%Wr*^Q--2(9QPAmrP`~(G4Q#bYL0aYA)k89@Eg}u`Of{bVB#5fqGu4zIiEMIlNbh z`jY+(?*@R7o~Y&CgXIznY`%Js{x@;%C_6C!$R)b?`%% zhsD0!D$u)i9~yoBZ^Ey+f6eg+9tJ-&pw+b-db zjvt{n{RQw=>KTXQ$ad^&o6n(V>x1wZ9QXoQxak?6dBxfJ7IgIFp!-xj{Mb?U8~dt( zxqks&f%4;-mN7?eR=$GhKeEYl>{GG3{vLgrqYO8FV)ZN_0yYjJUmw0M1`pr7j=rGY z==+rFVw%%=0q_9;>ku^}9izghnk$C!0iUgW;EQkm>SM z&f+Nk6`$y*RU}^wXy=|(f22o|e)dcJhC+`CKzgx4!Sp(IL-^_bb-r`sTUYw^22Ziy z`O6(e63=dp9j=9Hb`);v^h3Imc6nUcT^4(cmA$EZbs)fSj}r z8vDO~hb5n7^3SOouwO7ieH#W-EzL|@QVwo?0k4L-b=CukKFHS1G^9PWxDPo`ew71{ z`ix&$jj!}e?sSs!8}{@pBc=P?C_CrgPX#l^GQRJ??rj5p&-V#9{>(81fbag(ES>wy z`IMfU&&DzR+NrwkTi36P?nQNlZbAX|{uzx&ciaX03fpEpS&-2Y2qStVrT6EBx~2KT z7h9Yba4pz+*%I^dvl3ynJBQm#XJmlwt-(|AH|jU=Wxu_79fQsEzD4Fv@)zGOxI9*a@bd`SU@S3$&=?U)Av&_&+2sfvyW~2knxtW zmbuM~ND-ILzt=Z*Z#TP1b0>})d>gJphEGg4f!ds%^btw9dX*3M71dg6_WsWT136z#}aPA2ArhZKA3zFFYC{RaX)ua`g>gN z9KRmfg91wVBPt2DIoTiAmw-FRAv>U6LhTc3zDLB}*L;pZ`;S}w8gSr%i{E6y)i2&v zLIWren6Cx_vi%X7?<%1J)SZ-H^X*gUZp3>IkF7rVi@#s0P0Yc6PkS4m#0@=a^mhf8 z-}Ahn(xFbez`kpDEq^prf54ZzPdER&z)Oy(BFMF-4^R*F?EhsV3&4YY%=PO3nLb!wdehmMs6yoHy#N&S}VN@wMF$>Bg0%hm?jV|ex)mQAPXfY??L-@<#$ zTG*{;0+DgM+|Ph;gXk6~KoDoli~0AFw0i5_&1bcB!($x*{R1N~Ed@|p(66WYPOy>p zx%%K24d^|8*#5NJ*gm02DaaXI={$AJv7R6Fpi`Y$#UOgi{<#y;CTyL#W!!-mGmz$* z&d~zYQo|a&*o>iAKht29>v|!$xTydf((2h7M1TY+Si4@?ys8%9~4RXK7I$!WI-&THEtbkuawpJJUrD?-dOwZT88;gmZ#tS!@>i#TS36s+*YRIQ19Vs^i1i@u21ur)>|L)Css`^ zGU1E&Q1VMOB8*GSMAf1mN%e|Ghj>-sp=&?h+tJO{$WOjWHqU`N!`pLUTYo4&D%8h8 zcTb&9kDE8qk2S^FYnqu~pguhYwHfNhq^hgWP{NVrFs>b!0VzNvub$YN zwvWQu$uvKfr|dOE(Q-{MfqVlh{hc#u(xz7=F`!`TPsC0rg0?!KDqpQfMy-F-S{_OM zmt8Gj*U%@f{s^b;oFW1!-{)`zw_W%&QiJyza-a&82 zub`&Zenq!h{y*uLi~Q@ISzx8CS7J)UW>lJ@xWVFd$?A(-<$9d?*3^ zOOTNnycz-0ljldi=Vrd&f0FkYlEBS;-x10jGnm&t`{;&h<1UB~qVt!@rN4mj7H*Ed zhh~TOYDIo5-kxq6%OlMdKa!h3^tO8Wf0!S$)uBK8`0JUzoKC=U&fy3zY-)J`by$3v zY7c?NHeH5*t;FovuC%v0Ks6Qbr}tuLkNwskc{?SaHOY;?J{mo9@HiI&{A4ulhc!#p zE$d({4=dIMpE`w?Lti}qn{@xBl@2}JDd!8ca9|@^gj+8sCW&e08r@e)ccFWXI39e78I6`lt^D^rr@i*=s&K z2=uQJ_dY0xPikv9cgGhAH`PgmfiB&Ixzw1?kY8wppMuJ~j((QP{LxB&x2RiN8s49! zY(9!7?@{%aHHq{w`3#;ZYDC@5gc}i{n5m4n{;&CV-J~h{f=`8M(2ZYD!Q&CdTY*1- z;0v7Hd#+r6!g4qRr1YWjsSy3OIH|W$JjO+Pbcs225M#mr=xcj=d^e(f{gtCX@zM@y z|K+g6pEU{tyIBM0qfk>qK-e)sqtAivvpIhBDw@tO)XDiSXn$$3;tvoZwJ&nu&>Uoa z$Z9Vb20!e*7x*~E2jlGZ2^)J!n;qK)466_r9;YRc(3*HNuM@03pS1FhUC(%UAME5n zJj`VJqFxXf!69+}q+}9IhexssG{Mb1g9_sn`T>>u4>dMB7=kiS@|T5*P&s-m6Y^|o zzbQ=S@7VNyTbND?q|AO-m`#e=oc@4ca8$G9LRG&yN%QJMrg0@~4Ld{La94P9u$&!lp?}V@UYIe~@8?4Q8PPN!8Ja)F6#kl4PN0s@3U;I-_r@ z$LS6EqJL;08w~MLGn$JUV<(!$Wes~{e^@ZaMTxO;ERm{4X|Z|?iyPvdUK-a&PjgFL z8Mj6sbC+HpbjJMPyqk;R;(2*ISd7u*467A6)W|==wCEH)r;qcCgfPhWqAcJ)QU1tl^mb53`Wy}4pbk?7scBOOa3fi$y8Dqx6;RU>J+`#6@ z0+CE0lgl_N!S;8}y zyQjC{j#$JwMWjWk9xTaa!m7)|8po*epQ%YP{*kgBid9QQZIwprQ%3KO@kx+{LAgM6xHtMa#ow`@>b&P&($xQ_jg77Nr}GuslcV6~|7c3aI*{rNQ3B z7x=!hgNIE63H5?~c&=-BzIS-8?6{gk*13bxB!iJClaUyUkvN-?1e+Waqw*?_ZL=l) zxp*$4HqZxWtrr%ypX|g%2x)jx=EUa6gW_aP>?Tg?g--5=PU%WzrTwl&>WL=JDAUQq zn&+Hblu6Jh#mLi|qL%1&cvo|H$6(IbcNSA_Nzjc(ixFyc7ESwU*g@u!Op(jXVbjK3 ziVFtF3+7@uI05)#arkoe(dnnrZouf1M$$RboTV6z=#Y$@deL;cIJ=0yT(T`lr>^ax6{f!$n8oz`N&yN8yNqup}`B zml;5wT`X!PM<<8dbjZqcm|BK_MW#emp(dhY3tgrfrUL^r812OZ-C!wE7{y`* zd0nZeX8d?S;7KaX#R7dRG0H$pAP%1|4qq;gXb5*|BOKBea*kWi5wS6X%7;Vd=n0Hw zR>IehmY^eH4O=I5#Vb-L4@A0`WDAlab3{Ax%S2aA`RBmtk+9Dstm3j4(&^epiPMOPrp vCn3{QTAj#OP8v%g$E&za5)ln4+vIi)9N1Fhg0TAHkeQgT!K3coGI}34e*f@JzvOBq20c=!t zqydZ&$&}vSLf+o&-b?^3Whr@lX8-^SItX@@b(FOf;a%}NA?jBIz;#*KpbV1XJyb>NrBR;ap3 zVpnZo@YB0a52dg@5BYA=cOg$OQ4b(8*jjeb7);bM2pKjA4CBfc2l@yoa3zhK5L|Yr z!DvQ@2J~&iK|vrlytoOdQNd+Tv|1|AH<&mOXch(?qXK9Q)C1~wfX^k}nG*)+fx(~t z8xxOnt~stddWZx#0DTppf9J_#y_@P?ZwSl@paZ-qi4m3$_`e)VN*@3IKOC#w^zR(1 zFhCRFe{6w4*M|x1LkQh|07A?Hb^tJI2)mA7oW3ipgW+!pb@BC2!0v5TO9`E5B=Q(0m$_a9i#zOiwr?aL1R>46e3WVwo(En zfT~}5C;>nXTPS)kIRYSb5MEssXatmTM+GXoBZ3#*NPvafC)(P*8eB}Q15Z^hw7ez0eM1oC~ty(0p8=%3)Q^<1G0en?vD6h z9w7ibu&@RO7~bm+#|~z%fte(Hm-=7z%I})p>};GV&77RvZ5*vAfv!#t|7!t6ne%(5 z@SwVhVL=#BKx;*HY0bFV=pC-rg!xW@KZqY17z-&GVC*MD7?2HgHy12~`~U#i9A#Ct z#)6ulbaOX#{f}fzZ%cCzcN-_iclVs^kO69{jZl;L?vRL1;-B2K0RZyecpyB$e~>qn z4<82H;d9^jzU$}?097#xTEzefTNc1VdqD2<&36l+FuXm${RX2Dk`!%NRZ%YXoZ8_;iBMVPcS+V0FCG($R4nhn6+q=n1=c~1FE%@pj9wrt_p($6*L1r zxr3O{H;M^@1-wrYgCJ-?#9;3zEC>bw8C7X9M36~sI6$TEOpx6@aIFw13mmo83xWat z0YjQc90Z_604=-|sMVr@{K0K108l!3=NaSz)`dt{v$`$@LgJvN%GGOi{Zr?CtqRlv zS_4w20YGao7;+G;Igt_p__ko6buDCp4q4bjKnfs6%PFuA2n`E_a)gE~kYOPMHf-oA zA_kPq14cPU0X#WAy7dj*6A%MR8-PLCiwcE7-Fy5O4iV6b2}qb}B?KfaC-f$4C4j*5 zkZsyG$o2#Q1p-DPq;)}f9zq8hA>n&BQ=`KG1!MsR-v0$c&34$pfpoyY&|wT<=)nar zbObUVUg$$?$0)EB!zi>U!~yhe!Lv<5tFrk4XkS$OK=2ktH~<}ejKcrPDI_HCU5E=J z1(G@hz<;we0E9I27kmm}^mf8L5K?OZ069<%odQw?+7R-%DPZ3LEMyrxyajnFK>`5j zbce9OwlhfO1KZz6kOmm23uL-{H`EB-dK6yOdLJ<1hTJ`e0I6Wwx05$?>vah72!bSm zXos``(l-$78!&nlg!9%7MgRfco^DQFfZ=x@@c*L^Y6{ra4F9g-eFR;;K@>m#S8@M; z6u*z4mA8adNTsO)!@(X5jZu?$(Idd%5dbWNFZkRQ^4^1RAOnLT3B?4s!2^5VXP^$m zvo?_uD2VK+5hO@uyaA1^fEe|(w7@U$2?M~M|0rmC4+N0u5%lL30cc!A1OSs00D$5D zJmBBpO-O-CMP!g7*;a#s;7I_GH_jK04pTT#^Q%74_?@zd3bH`~fE)$W0c1HFD{D#z zO9v-cA4(fXQ*(1jyRtE}x1?mFG&gm0baH?1SdfG5VTlZBW~M&&PNo+Bzl2tI(kNgL zY%5@&B_iVc2#aV{Wfdh2YXyvzMtrYmbHG53DQDi$(Z0R^RD-3Y9J z(b9J3fol##0tU{4{$nS^Cqq~u(||V| z1ca<+=Yg|xL03>atH9_PAcRkU0LZlp@hkw~4o7bl^A+5C4nK2f6`!mBga80=jRify zdt$st1|4`m10g^~Xo3RigROoB9mDuQbIt`lA-KXo)(`K2Q-f4U^ky3jz-mMq6&ez- zcnVO>8)Pzub*Frvph5Zn0dcK{rBRXHQQvX2b%^f}HfZt93Q8Ik;tr%Tb>9OA5d;80 z-sc$2V-O3Z;?Z|;AhSU{GQ<~yc=VmCcNfut+V6Q%g2;;XfaGceBGX*}3=|x8MTM9Q zfp`(<-y4?^B(e~1>JdDl>yd#S^$_781+W#QKZl+`hd#r)!gRm@`gRe52WkLq@Bom^ zn+U9{7%+My^bjg^8aldf=ICt|Ps2*BubAXhK24Gbw3$mOgOB0GS*6#!{- z=NJ&obtR++K(Z77fE+Q13XIYBltE4!SP2o>4zUnoGT7}IWc%`-MM(ZYT42cSD0uKc zIt(G_2U4MsQUF69fO{$?TR>bM>+Orr@H*ok+vqRazrS*{5p-qy?48Xp2?$6?4AzJ= zUc;qEmF1luH>nm%{xw0=sxMMQe)A07TW^K)QL6&|U_Eiak3*@?hSL8NS4M?SgWUm#VmO6C7;f`jygU^g$P@4Wx?EGEB9Te3}jSD7TANBOl1 zDnK~2>nwJAqbM(3u*-w;2(@>`bf1M)yw+YBM|y(+aW+)y_=hFGgHJ4Dud{EbAN%KF zbTQ)vKCV2jc&pgtMu4eu0w)viA1k3&vFtyK2igqFq{Pv%g zQ|dMoLKEjq)5RS}Ff?8V{%3N%zh_?GLt+*kKVtnE7w}Q;cczws?<-;QuXAwe{bZ1` zRryzo-UfR^M|S~SHGz(a#(!#6z?B_(Bj7`kjhN{?HM72K3BuNH-qzVjjYuY|)FZPGDFkeGd=RjzavC8K>u(zim?5NxGaKfNmBaqSPL1P zlSVs!=C7#+5K+L!ueHm@KkA3WKi75k2FMCU6FchA|aLB7ZmQ{^`{GEVW$(PGqu|&6}{{aMO7C&EUKsaOkp=**=2wTXhe?{fBTl|0_wk( z!;NGx7cfU%iWC{L^qh0x(7Q>DoC8!-w0jr23~!~iOm|iaoF_!G*v>+S5Lx<}es7Z` z>lB53)Iih+ks41a1YHR~JvCFP3!wY$1@5Y<{Swc~2VCneGJdBf<)0qd{TL>I&ij;r zQHVX`b9#AQ_4#d#{r>aBz*xj4t^Qt}1RXas`=%Yp16|;=R1&DrMcOkDStAa%rf+eL zyL2Q#%+4z{V*5sOcR?sB_w*P;t{GD}YV9I?~E7)JdAuu1qOX_=_Wp$P&}o$;yWjE`}WsqT|n45}p2+{a&&*aDaLo;&6#Eu$=%O%^W*?sjbKW%eGoDbhYU3TC4a=^BW&EX2sc<0*nK zDN>qna$}K)#mHui+4bf{j7C}6C=dtC6tgpjTq|5c=HeZ*LlQaA+QW*y;%zZa5^?)a zD)R{T0$_y8PJit9!fibwt^yP!=9sl_=n8VHu=Xs}7sUI2*p&E?!)4M0RL`<4U6Z6Z z61Ddn{|<2fO@UNS71g?r$oLuCT!e!{j zCaCkUZY;w;r$t*Vebck?m)llthbMfiwwj*$t{cfu=TDaiK|IQtmS0y`+fp8QtP48l zf{$)K8xM}y3ug#Xpl9=9r^9627z>nOxS^_l?qFZRtIK@D_vsQWOqE7|M3BJRV#L?C zL!9jGi}MLM-B)`g49S0M8v1zfm^*3_=>QOWlA%@X4W5ZXm8$9hjO}HPYxAX%-y$+6 zhbGnSvC%EhF6Ue!O@kjEa$(3PkGk|Hi?85E@XL}3;D7k#i1nxMH4d1kcjxQ(akjZNu7^Qo zKl?pIY&I&1?7FyJe?2R={G{r;x5D8mW&P6cf<~&jh!aGlDIx5GUOPqT z=$ zhpaBDCROxYNGY3C2lMxC29_~%`pb3etLxAGf`85w{00PfF+K7=PcO#Jl}>VPHhBrp z9^!nT;2C*rHEEypiwNrEHu45{ca`C3ON;U)zfEKEZSNR-Z5){Bb(wi%<@j-GjNQfo z*tLt*U|XHL+Urf;Fx#rZ;kIGh+`Y?AdV(>V?!a7isJ0(6y0WmPr?|jWc|$m-&`>F^ z?I>86%}{ek9Vqd?;nPGWm%~CBh7l*1a~V%dr`-=?_?Z9MdhH9eKZQ0Pd{dCk*wL8a z`aoYLUHB`V-i@N<_u96Q;P1G4t^K|XI~SFLHf)?hFZWyg>JIi=Ccr0uP6jX2u+$OJ z3n9anHJnv?BFY(|tJ-AoI0|{tjX{FuAP9jK5xr7Zt*=zm`DOxgc%hUE_k;8hX}Z<~_OjFNCYPd(GPQ zQlm-oovX?yra&SVx{E7y&$;>+cZyJV#ehl!gSslF(68EQF}_nHk$xfacsZw76J_Z> zo@?!RBA&x2IRvWy{E7CP@nPpGyRtZ&ku^v^z2gn>*!gTdpS19i^xZC769%%5)Y^Lt z99|pZO1tNngG4N0=XY^sUqk6*KKz?wN9F1402@0E3MZu_nyDJylw!-MQLn}(UTAKc zkcfHF%wGOPMW+h>b03(OUrjA6K8l>N(0ruIZ_qrh@^>t7C*nM;;AWzvuiB{D@lVCw zZoVlF)udlLxX&l~9#MQsx+1epdf+Cvaj0A6;jbFUA{9n*KXeMoX16 zcJxA@qdkJuWLEZQn*@uibfQ{E4E(?2e0s5+LZWGNL+>Uc@*9t7q}Lj(PaGof^@bnL z60$G+fTS5$p2ZWm3f~hL_GGkU3AJdB{cV_}`o0G0E&re9tfA*}wMZrD=x_Fx^|SCF zv3!EQ>Z6HyaIQyN>^b8t#9w_dkorA1_@+RwnlKF)f$MtfH~vI1pRhywd1J)KyR&w9 zwBQjG;1n*=hiyp3erUGXEIj1iQ_%P@I6ZYI{9;;T*hzJ*3*NJ&qQ#A32?YYd2P!T><`b#EG ziYh$T(99vI4qrB&z$=4dJc@N58P|)&rR^n?K8__cwbF_=KXrc-JzDjlfkuMIL>mLX zsRLV2M)lKSiZo}PxUH^HX0N$g&ongH9X~5g&BoIZO&1Qs!d9@TBKG^Hew8`a+|>_7 z*^X_WRpV&Dq=f6KTQt8U2~wZ%qO!6+vVt-&MaaV!80 z7q1DzRDmyA!QH4M4_mG`V>_JU1J;jI?Vd3~%}p2ybyOJOVt0$SE|}( z9a~}8VhUxLqU2}stvh(sE-BYUj-jewb~R8*v=m2tymg5wg2$o0YkgnNH^R(R*6s%_ z5DSepGjU!AfT~x|*d-^xlk!sEGd}4TBy0%heuELPEL1#|<|MsG_gm|@?^X!mLCqrX zaddV&a=HFAfNyGUe8ce2P#B6R^oZ*6%4Xv%m#+AE;Pt={y_PH6kkWw4Y-}FitBuwa zmxbW-MxW8hW${xMHlG(Qux(e$B~A+VN4bjI)W>g1YK@>GyU4vf^JrkfpvEJKPt;=J z*+*pd$_R1cysK|>D&KNJ+bhIS1bl4ZF5GkLo;dtZ?)rK7SO$1%qt*8evRffBg$(h- zVMaIi6D1B>QHTOAuFK=1hI(5HBKIAQ0sVIBa#4b5KWNxYTyZr&HZ&po{j)qtOA(KD{y=;?~ufw9#`r6frG&r-W8F-WL6NZ;1O^LT0s|NmYYF+ zX+y(-rDuU!cP5FcazLgQFf%DlxzI`A7(`rTBet zA=gZE#gDax=U8r2T2F*Dd1be*^aZ$Sy5G+xYrqb}h!6S}_p3JjFccjfQ^NDRjlx8d zW4JYM!9jktM(|0M)G2=_*26EG^Ux4(7#yXhFVqK zgfQO#IQqQMaw|i%g;tMNF2oL9oQ*~xxV!^SemS8n@b|dOy^J82Gfh!Z5PVS_LtXnr z@c>S4ZvgA{DbnVQM;$@_R3TBwZTZwcXDGD^wJT`F2ioz#kd!B_lr*oji%~X%p63YV zU=bzG(z{&Aj-OkyBtHq*o6NscWRWZIbom*2fhMnu8)4UXs}eqo7xZ#sd3y`PY4raK z@Yb>|fe)L)?G$)WjqNBHf=(d~jd_kDyxb{a_%-JZvsV_`_GY&^Ja7&8So#nfCqC35}eU_w^Q*|VXr$}`uLN}S9ijxpZ`_SxQB zlBJgRw;#9J5e-FW4CbQwVwLrE#uaju_g;cD^mxyXfD$;8J( z3mAJ-#kegH{c3M~g0vWMzD25ZHc3zz(v>WxR5OGUY~Sd;pEl~$U2wv_ zZ5TNk(KZZXQ(Wd7|BAbk>e*+{Plur=)-|z`p3*8UFsp_Rv)NH2&SmSmON(bRcEaGk zXf??Ho1t>f{$=2tK15V1kunDdEeiXFzF!@#pN<#%HHckee{kgE0!)9t`SI&X;ohl{ zMY*S-x`qN)QV~8P0>eFB^ondk>|X2yQoev?w(C9q^xdOb&6&h$LjAfn6X!_c>E+8` zmU*uqofdY^P#M*H`3KP-cI2nhWuuR&5ES}fF@0Ik+FT_&sgy#6;!jXx7Dwx&?7IJk zC#=@rx^4Vb6*tO1m>ZaS`J$=+BBty?9GYUMpQ<}mXGOK>BA1ly(>7tVMmO_W96!Kz zeqq8@gs;5^#wH&cvA0v3p|me%+AVf4eWU|58gy}8C zpG@XoPL{1C!5m=@r>kiiO0RdvBKVDXjYwM=aC*KOQy>$z+Kr z9A5PnM(1BrT~pqGi8SO&!6J4wMKnfId|e!J$o{?KB+e^ekX(ZrMUikmT-Pn%FMWjP zbA+-DD*LhW(vF0K5=NFhQH@%K_c48|=*I+zi}~EP)UGMV-dy)>ZW$r^u=%PbWUTnm zjiH4~dh9SJuD1>A6jN5*=NrRU;tJf4vPAAjYzr|G797vE)%y4gsOM5KL0gC~4^d0f z*7H;TM>8g+Dd-;#*BS7vyL{-ohV@<%xlk1={)yI2L2q2DZCYsEz?S%)926=)HfI0U z_8DNz#c#FwVZjT=T`2jHgqQV5-oL&Zov$xc#`hWzIO-vX2CJ}La}`1>_D3jArn6TI zlO0r`D!(bYA^H#RMh-S>jClCy42NKz$^09;=eXG?HsJ}P^-WiR9DUg(;bXDSZ>L|!8)(Rf(Zg4 zb)0Y$#B>fL8H)0DEcG*tpau6fmx_I-Au@YsludDh@}cS^-~(kYM^|PqPV;zZW@5CPs-kev)G1 zWGdxEipiYRXD8sN>2X_*H|B#&xM;=_Tu&vAncIO$0vxLMImf|4a^aw(hN_vRtk5dr z??!bP7|sjE!DONy1(=)Yn%Cgc@_5msZu|s`?YDIg8x|$XH?#EX0~4;V$v=dqROBm2 zO}gW$g?!DTjc%vEul~W#%oZ2XC5kqT+SK4K9m?umyJcQRd`c7*Q7iJF;h4<0{E1Mv z-$RLxK>tzaI^rakkZM^-uI&@v`IXei%wFN$scu0_43wi{`w5`jPREzPRucJ>O~#S0 zcuH*8f5x5yv;|p4V-h?Xh=th|i+~#kFrEMjH&P=mG3F_mv zwzTzD>{NKw5leyy>HgJM(ClgWgHT!Fq1 z6l;pI)VA;sAiieq61q~~g>hM9#ANgo^-Lra(OZn+e&{3+6@sq7#)GZI2PfkrCk(o| zft5BYqnp0DmX{)M_)nrPv~bb?B8gosVs)fN`#t5v%Y5U=l=fECYun} z@p2&~&T2C_hRBog))kfyEy#%8`7G{Hf@vy93=VB(Ex$sb@>;)Po{D+HT67rBWVp z^XeawH@Z__xsH?+0F?rl>~6*f*^tQ^r?OoPJ6$njyMQm@IZ# z&6@t0MF2`e*xO)lGo?!cQ=w)~q2dCY>S|1?JBFK-F zsvKN+FkDB-a>KGt=19RB;n?Ex2&&GhQ1Uo@tlQ_1T0Pr#u-SUhzFs%Eq?A}(B@u!? zh`p0Mo50NZS{EgxZi<`bt?I~I=2Pxv?LIE&*Mg8+K{8*(Ql$NRu5@Oz3?=JZNgbK_ zHD0ALPX(pOza|R;3B5QOxZM*)$C|6Q`)OO=YlL2&!0)C$T!KZ(+Bxzi)HYnGUnl=k zGvX`2)TLX871jiKPgMHFoDE(`{VF8*1>Umg)BVMgU-t#h{?rIbZTA>H;A=-mR(gC$ zi{@SfS00O7@K;@5 zt@%rH&JRO>)^G=mRKsLmi%ULAReo#_#zcsVr=C`jN^DuyUiL*>9Y=7JC`yQ}E5xF* zmVW-X1;Q4J`uW)^(k|kcwU1$ng1hF#aGCIzM%uJC6Re1#c-$L0^P1;euJg6HC9qi0 zYoyFzSq(4eKbT68ytm6K(9|@&TQ!jt(EIVbl*NkOu(XjLMJ#}qlYVw8xZuvdoQB7o98dN1nxJa7XaPAN&*KFBz6(%m^?)ZHp((6DBBR2QGJy9CpUlmO;!1* zR0PUdoGH6uzM%`e@(NsuAl$?SCC=3EdaHpfvmsJ*!C{6{QIWFarvUQ3DaC zE>_Uw4q#Ne1{k022H927+QVW6-WbG-$uyqYj2sbC5m}pwyBtB%f9KpE{kMjGzH$G_ zN^4h|VV9%<`J85v;a9Joe;Q79y^&N#uTX+ArBA$bcZn5@IO-Nd_TMOe{2s{D83vT; zkASt5y=xtx)@aJMggJ$JQ<4uf>ws~tgl0B99o@4u=Ky>AVUrmF#V%e|WX#}KS1u(| zt`~MgPai(9xE`$sJ}U5}tYM57uX1oPi-V5!ht z#U|*aq@M_|I3b?tuf<<@~kqu*^H5y)aq*{73)bij;gTpFm zuh}wK7GM&I3qD8rkd2Aq4{N&L{Ve`9Il3MffK%|;^>IZDO*r4{M6UV-I*i~K>FT+O z`nL?;!32A7RE}T<`~h1=47^|z2ctp-ucVTxrtRtGNu;*R7>U(c8!!cy{Gasq+2sjx z?M}aKs@k?PRFMTVU{VgYSr{p7T8fuD!oTuxm{(=Q zU%gE#7;>$if;m{Ui;94$a8O-reSFa*7=BQ!c)|~PIeHtZG=C%m&Z`^Lae~vG`4!H% zD`%%*)Q~8Rh?mGJrN_e5(rlr>Mo@+8j-NanPY&_lS*h*u)ht_l4>x)mzf0I=k2+q` z1>v^l@he`*REx?bS`M?6vsq((5o+>crR-@x$LS~Ibv|2oisZEejxJ&5 zP5Flp$Cq!qu#CXry*Y49CwJO7DtI>~m{;Uv_3%us@(M^t!k(_i5vN`10hg$$>HYpU zg8JLuTr6^ZXY4EAf;)L}O?Sz5tE4JPkZ>#168Z7v$^)@fizds{<2L%9WkSzz-RtB9 z+9spg)GL9kjca>xu!{BC@=fx#H|08Y@ybxol$5ZF;S6U<6qWzz6gd<^kJV0MOoF8s zkzetP?hkX`q`}lcHwW|Q6*>#2^j~_s?uMM5Nem+$cP8Eab`o@A(|q{%Bi6p*&AssC zW@xB0cVe_HF_&WE>T9>GXbQPMm(oB_-%h`&kP?<^s56L#0u{}{4L3<{rgf>cb+}rN zi{6x`3hIbCvid>n9?ZAdSF*T`31pqN?)CmsSD!)_`rd$*`6IkJTKm#bY8wuOEB}1G z)?To@y3l@|+qBr5!Urh+tP4gV#WB@#gr$y&tNIXV7Uolj=Mm22o$Ec*z8i8_IHe4` zjiaS{c{j)%3GcOrek+^C$Tl&wv2naEKBHWmrG%1ADk|iG8Ti1znqT-gDKvAfl<6+Mlg*_1{3 z@_5HsFLr`=f4wd>iQm}_bnK>q=X7xS0x9mQR{nZ`M-U$Ia^d>ieB?6q>bLP}SB*fk z`Vh6i2_=d$;flVS{q9YycG2;h@3;_R&%?>W-ZNAw3e3!9D2-jFs=vQMFcO? z^Zp^*#YLEwnwKP;&yNb6lQ`1B4d>!?BSQJlJ3Z7ATbG}jYei@XiMnh(ls)6i1Woaf zkX6g3`Dok|WK-Hhe**=O@`4aWY+zVa@Km-E40FS=p8h^#9_PG)6FfzScY2%khYfQo zCK!U>`g6~7_@5Jojf1JTTNUJRo!d*8743&3shRu6*bg|GmV5Al5g&|uYynkf2IcWh6nwlK2#Vs4#x|^(WNI@2`p?07m7*@cR)8t z`TwDp*iGfZqjBXi$mBt6lJBisSNz<68za#55G#)@0{4l*t0Fd+lvOm!No&oV8|pO6 zXoOcjv)!O=0>-ItBr3~kznA;+ky-6_wv(rbXE!UCBV++_3I4$q@dYqmDGU8rBSAY z2gCD_wGe?aw$E?2*NV5AITZz@p(|`k?AJUdtRRU)%Xl)6A{eD_ zS)<>&UqTm`9Ll3Yqfaeiet4W|m(d)ICGJx>WjT-gSRh7GN9`rNwiXXzuT>9n;{*yqaHl!7=~o_)SNyM7y*R`r~|i#dIb{nvYzufjqXdro{@< zOtDCmM`?OB>4G$>J4{b;^FTD*7k)*4l{b7!>Ep>K>N8pwwH_Tr7K&KZz&qE~E+|=| zX%jzy*~M;691Fp-w@mO)pz2#=?$5WVnuchf($q@Fcg>E{2o&9{}&L9|PiQPsZ6)Mbe&4V+OBnY@tbZ zO|FwBQ_DJ;319(Xa<*<|T||fZKrBqN@dqc>pH7iC<$QwvH&^4#jXWN9+B0!=AF^@0 z@D1I1(8x2m=$CP?c|Vm(FJp)8dpQ6Wjg-ckQ$x}*5O~Cat%P=z{VF=4m=Qr1$ zjtR9L7wlk_`Wl7FM}-ogf2yHm;Z2^Ff$4gsxPz>T`a42-v9Muy0H2;bOUOqzooV!H0`8ycc2nA`#Km!BrH8YU*{NvrRG>>5F@Czv1U zcQr^xU=`t#D1|hAcbD6_h#)_bnO9PCK+D&{V{v>01F?UWWKS;Z%#0JkISrq? zLffCyE*}lklq=Z;*s23yF2htsVE~bjQ9v;|Nf~-|2E~4hl%4t^sS=oQOpI{cJzKe) zNNGvJoOqfJ$9AKlI$z~SC$&B2ejWi6w9aESSII;-+SnN2)T{Q}oT1?sg~mZ>W}XUt=YXFLNbP^aOf@ zme}2Xxlf+C13z;MhjV0QrL)x^Vm&`u4M_u3{A=W-pff`OpPI~jSlbkCg~PQZq(snA z{2bkyCVG^F6}`dTrg^+uNcc4Oyn1T;M*z=(?iCm&tlVZO6L~CC;S5o++Syyboah0*FLeOD03#&5e z%yII&)tsMs(Rs2Ne{C4X=I-2SMF4-}4Dz92a0D9qYZ`*Fnp;bYhv9eOPaj>Ge~rEr z!(D}s-Ce@d^(rMc?rw>hq++=099MX+%wuM+PXtjYu&PO)m4UqU22~BYTxcH9wU?PY z@&jRUNbgx9=rh{Ea)~5$EB<3hU}23YBzvt{`Cm&2x2Up2$845bbLanm2>J^4}S>KF@mHoPH&W zmbZ8wrxeRXYcNHk^j2qU5Wi@lESJs?vWY9|&x-|xLp=1WMt3r~F+T-vOkDEP5|XF+ zvc+>__qV-rjMWaRE5AlD>#!huq@FbQ{4}>nXc3dg3_httnJxYo3D^|XjDs+P8Uy+A zq$mES>gf^1oqN5kzU7U-`0{I%{-mtWPmmbaKjk-=2z44h*4M&MvMFPY_}1_(!1IcX zFGIn*WAJA{12Z`yXIQ+RW?jU)(xw3BCr0M;3}5WJWof_TML=nNBP<4-?UI<&rvd5J zMf^{-93q?-eY!%*+6K!t??)asvok0ZyJ3FtoEt%{+NNNZFa(-QJ>6?-bh(h{l|~ zo-S)Z3@jua`f48GLeAKaDj;pmUcl^B3xD=R6q!+#!olUXRtZZV+2 zAZKu|735YnZ=8s6g7L}A4V%*3aZs3Ats>IsZL89}r2Yn&P0f^!%$3|P;+S8}<_?J@ zF>L;zV^TA#9_6bc&>vz=W*B>_j2=139w>U$ zzzOy?E7vvYWN8fXZ*2DjI8Qi8;C~=s=X(p;WLJGK(Do{2*H&`?v_>v7s%a4XU9w$#plo&8G> zKOa2Nsv1bze=Dd~5`rn*iP+9_bDu#aiB>mvrss_c+n*1Go0KuP0pM)^v} zMa2&+`&;qN?9I(L=-S;`?20F*L_WU6gszv>Q&#`aW3)qeJsMqIn|D(%3`;~~~ zAkCjub=OQ#?@0O)pnkT0=AG#HXN2ol&OVA0jYjTpLg|Q+biLj5yX_O!O`P61_wqpb|4^H164gYf z`-T`HtuV`Sxu>*Wi1z7gfs*BHrZ-gLG1;0qe4OE?7Xnkdhq5?l$Cdj|Vb3phrvfq9 z8C>W=5xGxGN!bERr(5_a-$mz^^Y5KvW(|HnzSMDz-`Kwrc=_J{9ul6bym#DRMU%(_ zC`P->H<~GYl5HSY^{T7}2X|!S*Yew-jMmq^^gM$}dx}jTmOBpm`JrSzo(!b3#`3l@ zPTv;CU;aflc-H#%S1pdNNHGXiBTWs7#GAJdb(_+)(~*V8c%Sg8S94WzGBCF$-u>>w z@yi+7nu{Tu%h^MtkGGnbsk!4${A}H71ufl#9?_OK%bo$Zyj>>b6!<%PTR>>hh=u?o zfIfCH`7~{n6sBXxfXcf&D$3>7*MF_KGLw;Nn>;ieGc?ny3L&uTgwN0UHN4u`f%>CS z_iJ?+iI{C=EIzzSyz_y*=!i1QRpiALf27Z=Pb+)>wMVVt#ox|PQL1bQwnS@k(e#CYSJ1KR$^JA}}D z)@yrli+kJE3sLk<-TAn=ccR39j`oO!P4|}D$~1b9!cdx+wv71%RV`MLUukXp<;i$l z{UCUye0r%k?Kw4sWHU=qZjAG@8F67~OH2Kl<9-K<@aM&eUr5>9CA*dzY0~+>kOPvx zH#V>h-*Npj5T8D9L#EWyTt{yW{sqcxaty_6$7|=_Ec*~|x5fu=^tjGSE#R0$PMT0+bb@PcyQ1OW@lE@iIS@!TAllk&BuNvK)21SBA5P>r9C&v!MX9%D#!` z;86u04@Rm5yK|zOpOZ;}pz-G{>mp>v?zjAt&!IRw0_Qswrs`hx;8T9%8fF>(sczAzMstHE$jBxY zyJPX6c~Qxeh((X}B26hz^^$7+3}wOe?dpjS6uC)0QhPgVIJ-L$9g9&emd4DbtV^9v z`Ct=sFEgi%J`CPI#?2vq>u*21I=yh+!%0K9nEFlQb)5hcI|rPSLVLr~+1v(oQQ20{ zS5+9n?I@VYiqdD5w?QJ~)xC54rSHwaLCTMR&H~=14$zk+8?F=PcOo_Uo0;nm8@)PR zIb^)dv~QXQZ@IMdzH)z~lg76*?$B~*D)pnkt0*@cr0^X){1TQw6{zS!P+|~5URa{u zCgoAzOZumKAh#vugNBVGc4qjdS^pgEkZ!|C6LRH+yA?y6U^C~y)koZm&Rta|EOU(O z&NR6MpIvI`iyW5dOt>;As)}LCF%B$s@v5f72x_K$Oq()pxQlIePHAi@}`h6IZ z3Hgo^n0qsgkyex@AIklZ#ESvo09#%HA8Cox$!jl_G3q!z&X){cHB)%#fs`Yje={2Q*9kQL;d+e{oDek-~&C;&eF<&=OE zfiV^yT~@R7tWgovcI?a5kW8E}@JocBU$kwDtiyh^?iiu>RY_3Ys{VUKgUc*h4tmPH zTQ#&k%iFGgUdVD6CNCw61T+;9eCTb>gjeR^iwRfGpVM( zk#Ry>W@)60D#vSpo6&4}-%-SE0&B%Ty@rp^Nt|l30a{98bf_f(@h9hrFZN4L3^!E* zci*B3J4$PnT&isQm9=9TImy(QO>I=EGOWUo^BN`OX*lnf;31!Mn&7C-wj6$o z!rCt9JrKvl9%pt0N~5?FvRaL|;p;Imy6N+83CqLg-WyJWpeDB$C# zR1Xb5EZh^dj$SSd!=6}f8*>W z#6kzzN{|UNPFO87$BH3_Huysf@T|x(DPQ8KBiV%$6gV)>(PWV{vimh83y!5@KIX~W zYDMl-)3iGy#`PlPYekzF!Q-TCiwT^P32f6W9k1~V{judhJ0uo(G)iA_jR`C`a^CI7 z6e6vOYZA$g55a$yP}9*?eX%=as<)JuVs_;YW^RIA4U5nbR~5PAOqu^J%6#}Wwp*m4pK`=eM#%HBI?Cl?vY0$@h+y(qG~M*7i#u33YNDFr{G=;29_)$&|n`Y#W$7 z>ODLd$~GHOb!D$gCpQl*EUwb%M#u@qloZcR)J1`Gvd)g4^C1-)OM(7Hhuy`3jkcd2 z3TqomzigBI#7)isaaN)z-_4kAX)wpJqxew{t`rYN_3Eo~k~^Twr?k+}gZS@Dsy#*24wAdbZLBxBVH)^Gg`SZJ)Qmm+PD3-<5Q~S5MOQyyG zVUB5lFKokT8)j?8QX$_cdFfX7%0^q8YDSe6{-)&B;prVND=;2g9wVLiHhzw!qOVpk z5E}B({JTVZp#-$IEluT~AnTYZ-O`yiK!wdi>dMMqwZW5YCDLFG z$t*Y%ZfZFHpjl1fU(TQix5%7WZQse_< z4Kc_!_KKor9%Vlex)%AAIh;;x`>BG9I&ailK?*qp44!kW!cL%Vec}(8NvR_JUmouT zqGm7OYROaXY}e;~5NC$VBRa%03z(e9QMupN|IS)uD`gONzW!KDPDb9PQH}oS91sK2 z8i>kDXg|Rh|A5j9Lhlr9Qel_sd3_^&)M1%*KcJsCS{#MUehH%OAq?glxxisj@Shf|!K zmUQldy#dQ^1I1OYR=#hGaj>@`t%P~%CDo4P30*hAeRx%EBo3`^nF4Ue$gYU1P84}8 zDZ&9@ZGvMsA^5PCRnwVEBZ~mBdM@zXZ%@AfXNsW}+D=C+X){4W*V@70qqanj0dAb% zIi}G&252mbSKgQOLeFIvV|B*>b>GxDQ2L1EA#ezrq`E)Of{T;Bu z{OqBPzO0Of0GnIx#(jB--uNyb5RBRIEKTuubV;0(s}#h8HAFAt&P0^Q+)J}@Y=RE> zUd4Z9X%j-tb-sZ(zB=KCMbL?@s(kwR>&(2~Fw$%9sF?d1uZjN#dX^XWCdPJCA&Uv1 zZ`VwIiM_#9TW##ZUDY~bjwPe!Lqu5)wm$P1+u3t2K<}LcR{?`9$E)TmpH=6XevA}1 zOl6u_?6(Ses0!DY$0e0p#W)X^^kV0OF`;b>o>lxqf_S=CCs|n8RLu)nKXKOtIrOz=bA(i0(QM&Oxm~3WP$u z_<0rLLg+jX=tc#GzXB5@(^B!Z*3%MI+D7*|R!T#C_t%QyAhnPny3wA8YGoM)E!6t` zr0CvLGF3`Af?ucIVF$sk&4C`oeq82ekC>x30F7HU$TvJVl@hNmBDP0d(Tx4kK{wx*+=zFGa zD|CaCbXpH6e}XCG+3S2b37*XzO7o{5=T)d3_uZKO@2VM>=AR1h%1k09=hTS~c$Pk7 z6-E-?NH7MK+L%vGU#@B>eFgusMy&^=1f4J2{#LzgVT16V;ZQ^OzUqX5PZ?+BR=u4ouEy*w8niT7wchKLf~)ku})p-`GLfw z|1mv_uYqeVg2+})1=AVU|6CM}4EHt~OFgs#*5O5G9)KLjh$<%vZ}(gQDEKQONxneD zE0x9k%6Tr>h#wiB2ldPNu`#=*)Vh!hi#uL6R3g$C+v6@hUUMAzQT9yAlpiUj7}FDB z(ML;FQR~;a;B!SbJ&a$A;jQn)xF`D!C70&!zd`Rh@OqlbKdSa4)W^?E!JABvaTghi_R|ZiQAh{LDgF*#yty8Ml^u%N>-HjM zmZr)2GQPJFXObvg8G5e$@w9Bunq3mL>fOv?>+4s~GTM*&`sZu#hLe|YFe-|lbQimZ zG3E2_EGe!jDmmZjR5({7Ymr5^N>$tV(v68De36U2NR!;`_s!Tafcx&wTd%bXy&sk% z*P)^}Y~u7i!GTIgnZ$2=Xn6kAv9qjKQgY81IMW!RlXBJ5Rl;iJc7^ZPM5_Bc&Qmyt zgVHvm@xr^c9NIU>5o8$Jxg0LL^Ij_yB`QU;)7`eCx)nI+7Ak=FQH(HM`!x4XCt2bq zY(ylUT6LeUCYAG+*^?6yxD*0X$*c%#gNe`Ub{NX@Dq%F5&v{8?%2tjND$FL7!FivT z`s_6`_(qUPk33;XLSa}wP4TnfZ0B74U;AsA$Lu{E%#vG=$kc&^_j}oi(}INrNbB@h z(j!7Dp#}~++>?W5VR$uBt;hQ(4}I7jV#9Z z&oq_ktW+(FJoAdBE&jUS>Px9fZ@Xf-^1Wzmq-X&{B@rX%YJ&%7cMdbRL!;+F7DbLf zX{p9e@&VHwoXx_m3|bG;WWPiGzDtcrc$uiQtq7?MQFxx?P^ScB3*IgxqqzkJLB*{k z!@tHW1pKP)nxDo1Zj0jK7on8u_zmBuN$6G+_4l4ZU7_CJs#LBt%^8{A0Z{;3QB*b+ z8MnZ8(*h!M1Dk$mtm~56M>ZZ^I+)3VuxRHGFr3*r%FfY|3iW%jIK7%=K=Ej=pdb6` z{;B0+$dHpihc(J66UcDK|3@P|kRT}Pp%C}SmFXDC^RGml4>=@tp~@3zq>Tu2iPq{t zpj~LhH$D{?m9+kqjaKsKR~Wh+X)_B%R1lXPcKIVyztqfNNtQVE)c@FcTJlp{Vovu|XJ@CQC;k$-G=<039v%59eKWPhrD#;l3R zHqq^>Kj;_VeR7Y-T^P9wF~&fOU5|ppT$FHqL)0>q%kjo-oeSVCwmW_{^=GPhNgp#K zqjcWLba5y|&G;{wu3l2PG%ku#K9(H6O3gHckli2kE2fHA@f#n?)Bjr^)hYkW#1H*7 z(I@>t;IoBw-xhMhl^LWm1D{E7#CC{R37@&pDsJ6|hrr~!PIzi}Qa#+CC=|ji!xys+ z9$__prwy}`s__m(Ky#aALpSyYx%)bFsGyoL8+%&Xsah&QZ7)MECV_*N=sko%z6RH) zH$F0J18U5eI7BNwVqcM*buVogc%+f>f{5+lhwsEgs?=qtNa#2JW(MzPuDysOpRIou z&$Hv42!#(m*mIPZE7^XL6j8J5%1vP-_A^!YP4tF~}WFKCFT4Hk;2 z3d%A8wFeMW;XZ=IEIVYL84U(@7UgOIK&mxOYo1A$a`^}nIC%ft=^L0NbvV%yHJNbR zy-JZ0v9ZxOC-rR}wSlRz&FxAj z8;+drHt&@JvC>#k8|ZuUNPd+0%zix<}wz?zOs2qNr#x+ z%L+6_d?Gur2vjj*@<;?}Bz7>w2P!LAblht}-0B@9upJ$pp)y}-&O8Y1fEFqlfy|HX z;~k_g{E3n8QilU^er>zhK!BOosyi0c2GfpQMXbTogGw!PJB~qb)ZekT?-#Uz9Y5+k z3yvhlEG8$6d7{l%qJvSVL>CLKnN+~r3vBFfx#J2<6{Z1^#}b*$^EPJ$G&IL?y&b`-(OP#zOs9) zoq>L&O*JoR^xejL_s68BcSzkqiv?q75U%rM4X+SaUt|9t*9w9LDfn02; ziNH~nl|Eo?Uh71c%^AlX0P=a#P{&!!Pc}qeg;T3fbE=>Ca0{n|SmOE$J~nzM>VjZP z=fpPJLdWI8icFW!o})*tn0}Y)#LC%(U#R376cvn9Fq^*mUYQ%u2|^P3DN5;a_vVA~ zMq-L>s!Sp>XCh1r^nvb_m3hDUgkG593T}VJB`gbay1o-{EequBG0mEU{`{Y*Z#bzW zNHT!CD~!W3HRDV#UAGqP;@V@@Imv=9HPioG_@-fDO3=%%Hb314{C76-2}=m+zWpnK zy6UjxwD}h`IuNi_<{jc`HAd9r$}5sD8>;LN6MF2^HFN+LQ6Gt&*Vaabt&OlNR=iAo zKH13C_S;|2I8lv}?^NIsoFB~zEVwlAee93@ZM8%1D1vQ~L+hMpM5}p|X02N1Fb~uW zY~ewP1+fCZy~7%DsGEnCA)aqKNpm#2bsmIg2lFjatR#yK)l=6=L+B02lQ6c+lBOoN ztixz=dz=VdFweP2UShAh44_ps6Ne!wFDoBlb|V+>#^l1Y=NPk;m&yhQ1Tg6BEDmBm zJ4*96JH$h}lZ1!y+;u5cfy9Z7(8d#&gTTqMYK$xd&5;;nWZTKm7x#?VNUQh~bqie* ze)zo}#lxbLW+`@2It2v?+lJjIV7?z^r1X?p9g-#(SOCh+ijeq|R-$n_F{k8^a z)TEbzUKxqvPRLbMp$GN}-3Fdu*nh0FNoX8vO5_PXMju6^c%z;Jp(hGqwptHh&^UD! z2q_Df*ihcmY2hcP7zacstY9TlF~*uJ`oy(;JWY)OafMKDU?oTrw#Lf@#0=9^w-yzz zPL?Igc#9PTOKno*?~8&0gPZ}~>0l^+l$fsj2RZ1L+7_Nybf1<3q9*4S`yqs==Ny)}HbC_p9m!kY<0 zP3NZtIebsuiQF}!X=uIZOb@(SCt3s~86E{w{A4B#Cb7v)Z)~r zX{fEsKsrD-Sh2gd3A2JaeOr?(f6NvcF|ZJuB(}B6Vq(k60}f-Q_}IZTKCk==L(U(A ztGSSGwPT93oA;Pp*lGGBUl3n+_ADI3c`tyEjP=}qv?t{!S1=qjLSz zc@~NUl_eF$Nk-RMLkU2v0R^Nn2+~*@;==mbz9Ba{J6MZ4i&R?6V^y2Et&Qs}Z#S+I zoQU_T?H(U@BDWMjnv(;9p9r4I*`zd3rtKfEkA&)`Z|m8OZx9q^oAde|HKU=fF|w69 ztQJ-re61)m8|UlH*Fr2M!X^M>BwB&QK*1wbY=_RXs>$P)jXrDk=8XpsPf;N zQEp>Nc%dn3A4KtN__V-a2lO>o)tPEOiftB{@&L0+YUy8Rd!yiB0EELJ^1<#T~FrP3%u9 z>I)vPMSeZ)RpKO&)+-1FHu}V%khhu2v+szWo+ZN62VkhTJnifmS_s&Vrql>#wd+`? zi`(?PCF8qI%ciFq+U&C`s%Ll#lwB4F3o zQba}B2DhezurB5m)=_84s?UQBl+~}VGD7uiUd`n1Tu8DvAK2D2R-+A`hO<$rEH7MY= z!O)j-{flA9#~}CnQ&q#qtE=QTp!#lPHR$n>YLBoq*L*spGY3ZHGF@X6uYR3SiT8$$ z$n^fny_{e;KIwx;1o__duCXe?{0(7gqOno;twzq=d#TW7cYr_JBB2BYFPQ&c&0Zp#YMZG9P+*{v+O#!dB8yfS~~A^5I!>oKwb;F4}Wx zRE)aPUOUpSLZ!^R%oW*X+;8TwYoO`QOHM}UrYXy?xaQ{r>X5RjQ;@r8mByqJ;Vb?| zVi8=%&!L|}SBe&>(F8WXEBGPp#A6diLws?k!`x`mXS3&8bVdvoq4>!{gjzd9XvKwI zg8!;NQh%uf{;NOmBu3d0mbx@|J?<1x(N=74h`zyT(G5|$-<9!7(H$6TR_}wj)7Y*# z$)!OTTb@b{j{CFVUsRZY_!bU$zUwF5aL$wl4_P@8=(+Q=4{S>I;jjxTw9yzpjVZ&h#g25MhMle~&z zCs?iHXJ_Y89Ti9Eq_}lu|G3CCjc^Ovm3dDm93_;@xgPKB(%t02mAe8i+~7c%eQe%6 z5KJ~gf($O=_zXyvZ0Eon7YgXC;ivQ7i$Zlh7GI#ixQ$^iH46;FXr<56HY)D+-tx&- zv?@putCScdJj%-jv{amZ96}J}{aVy9o2jEni3T!n|5_GiowQXdSLreYV6}V?*)EY+ zQ$yXG-!7_JK;D+bu0=Fcx?4*8G8MmsFcffIF_Gh-Vqf%0w=kV}vc;uHIm*udwk~Vk zO)CjrOj6cLFamP^68W~_fyA9^HOea<2HGvS2;HrAub!X3WM5I*9&mzR4^iii9Ah2m zuSM@#_F`dR!sV{IzR+dhlc|LLR$*CH5r$n@Sa5=Sj;Iu0!}tp-Pv`)UN0DYu_mLO` z!&Gr37!2ZaqFw_CbihJ>CHosGAcB+0dpj)B={mwjaGIkl7aou8jJLBsrM^zzeNvpL zM~g5YFV7mJX0^3NHviU{<^AW@AX$|=#^PN(XB^pQZl6ju!xm=wxFo6coB{C0*Tkif zQW8hVcLKnDY(JIG8CIfn9HU@_%QjixCm1^DZ?{H6WvMHxpa;xhu}nl^0@>Aw3a$Y3 z;Hza%b*`W~NjJZKMC>&j0x8dkI)25X3eFllA3%nB*?yPkxkrP1r0Ctf*1R z4p3pov>rq}b4+y-z?lVc5zlc#1?`XM=DR?f3oiON@w(_<3L$D6=}S>&PQaSnWxn5Y zX%OgIA$zUdv~f89@7eLE$Vyos3I~v7m59cucXdJ0p?DKpmJ+6CJv%k5uz+OEb|Hqm&+>qKQ0@3oLX6H&hi!_e~ zy)z8(@i2yi8qxH8Hv#w>N`D;}!qpU-eJu)ZDWs@RtxkE(MulivVt2Y{6C>8D=VpF` zDQ8G_GRfKpe2Eq@PCM4{`vidHA{8}mA=K#)WO5N4*(;yKsL*}NK2GGvf1TBv-BYrp zBO#>5l7qp7?ZJDHa2@Nuv|b-Jn(!9p;;9V8g4#yy2Ym`#>kEPDR9X^Mg>Ue=zBOBG zwC@wR`Ne(hY6jS2_$kvGumN;}38YKnc^!mel!`pg%GGjV8O(oJ$CQSp)N6m@fpYJM zWP#g`r{H9(`6>-!eK{XZNW2fNm4}C>s}}z^UHNP?XM0MfR(e@`0iGL(B@IXS35bna zE7h{y3{TXFq0fT@6g|1QQs*TLI^qnMYLbT#)=(uKSyz03L#U})dgv7A! zj5bq+RH|1%-TPe70eHD;S@!Oe@!t^m_V9Gtu-iAUBb)c_eyNcPhUBEsl8p$$9s2J6 zn;EZAYx_?IYhm@CwmclXHUYCw8c<{SrCKiBE53)m-O@AiQY^}y(p*uh7?mNI(bWS| zl>M7hh7E(Au)pimDHzh6UR{CxcwKYC&sx>p@IT>&syh{aOhw@x(q$qU)ba@7ved5U z$ij6iA(#52PV5o|B--CNRK}QW-T~QhdmBSd)0+)LQXJ+ObWMY?=*5@6h9Zuin+SeX z-&AOlIAXGwg*Lhw(?{XBUuBxuL@-*W?t)C!%m<3Z5QqyUGyoPbk{j@2Yr0-p2@_ph zIxa1gnbLj{9MNbZmq&YhE_jY;#+qFrJlB0Yye497xZs`%O+A3VkTN)8E$Jr6%keQH zNSms6f3V262REUEJ>C(Y`o#x6$AV#@P@|FOyJ#>jo%9@#!~Hbq|A$WPUaE;~qKFHn z+9%6i2zk@;^@x17v+HlAi--*QrvRLKk2HZxC{&@goT2xMXC^DQw+qFyyv~F&@|bk6 zT;QsdrYIW%z0h#J3dw0eLRTzpNosk@RdRb8M?6rUpJnF?yYU&fjRMB25&Ahkk) z1{DvTo@*>15v-IsdH=6vTAdEe7TF#4H?Fvhv)WbfPpp)_GtSf;OJY@-t|ibS^2i=| zw1xMY1=*YMfhps3k(LOqBUKBw1Q-l<2k7zYcuNoS3mU*uneEc11 za950fU#z;;H;ghGN`sFhuarkx%;D|K^Swjq!!{p?piw}*^;M10hK0(J-UXqn`fYyk z=<8g;lo<3 z9I>Q>lSl<`!SFLye+S@T(?dlZb}Sf1ag|TKfyjgNOj-Utc$gCQYMC@75!S&$*a^-7 z`c+r1x%hz0K#zTJQ?En&lf~hLP0zquDB@70d@l)BI#w%1UsLI2ovb*sHrc56Q?CQG zdijklRuu~o(F+6=z5kNwYm!9fX<;Z{6Pr30}|c&MA&bhGvD|(53Cgv!LJ(Pf&f?)sc7Az&BHveAPTaG)v6evt zOfBviC4~%-8|!HIdHzNNA^eDxdIyMSxhzE2LW58GHo(qSKdqB5Lqq!6+mZaz2>h{A zdK_ZDe}YEKbxA@JgaESq_(B|XV4S`gKL`JfI*0?}?3SrjKnqcq+YdkA0B(M~|DU#% zKk242fIznW7(8k1b?Otb4;rzJ-i&`v(&QUn%7^ zK5`dX5cWRK7WC3!$3SZ7h~CPmFep>NMV}0niF%Jjk0w&`_GK@2K6TRsz1SxI7*L#? zi)I6bu3e#|W;Cj=9ibHL?NGu}AK7X+&~eoPWRZtZ$R|3ZT~4*C7u>dq;Q2M#WcPHU z(hLuXbX5TJv%WVA%zSQ;ewewo#Ub$HBS%ejdbpJ)mHe9%x?;2NoP#$HnM|e>H~83I zCwLVUfffU=$x9g0s)E_k(U+mmeP27=f|HlY1wX-#mhm6(S6*S(-N&~5ee=bD)-{GO zO^)s%;Y*SDPHf>l!E^$11w#U3h*=7iwRJD$yw3+rJxSGW4YY6ei*_vcQ0NOfz;|C$ zhzGc#oB;-Payv|I=jn;$%c->}kwfs?jdEBzj-&psER`S9{(5rTUMJOi$2cc0E+vmL zL1;7GpT9{%l*k*t%ny)iKlW8cy?$(x*2Y;9n8tS0q1c{iD<4dfyhRU9jm))e4SBVK zi1XqVLy(QkMV9ruWW{Bd-FQ9@=djtZx8xt_wEhtC`+OJneW!5Di5xVbJ|~GE_M~4U zELVZ9+GQg_bPTl{US?E$I)fD}wC!NR-fPX`?(*gZy7|N(&0_Uk&X*!BU(43AwBlc< zjC=YFV{92ee^|RIc*9(&NC_4wZw5)_w;Lb zuSQ*{jzX4RCwGfx{7X`PHz-5vjMI1a>U=cltEOxZ>KHB_kD>RWg0p}6I&Z1B0`kZ(wSw9uva&T{P*@fR}owt zZtSZ>cIfZyxLzGh!vPHp^F}-zLl5)64xmq1nm3%gv?l0V%~Gmk2|npgU4D4y2BVd9 zNsjOSSgDON;%J^ZA+v}JBhVP;-jc@@z71p8pA?;0T<@MegE^+&kFQX?_|i8o+WH8L zX~^#A6NoZH|ivJW|ff@`E)R~U|BaGt{)vd?8jenoK zCwT2an`uAFlC%|y(pTvPRQ=~^{$>?ChWXOnr{K7XpTX=akEi-gkK4jL_4H!{q#5rI zQB|h>GnO8cJ0@j!aXT-QFi<)nTkT*uWPAWC03@zs0}&~SrSaq&1&$~@URhOo_Fd!^ zK0qc#v`SDsgW+R6Lq_5i-1BGKPD` zzrJnq3YAgB)j}YH3_mld(lW-{Ri27@86d zVpm~5E)(L~K6&L}8?yr0imvS-ATX_~G#i$wN_W5uo~-L&8MCrig*Z+#kG~cos%>FA zy#(@o09JM zkR#oF)WFdq8h~B%%Ut8-`|8IM{7F$ECO-V_-z~G#s2`Btxcb9- zy0JS&W9=YFgYRUojFi+%Ix>eN;}AK6MaVO0;`0xpHdpiM$;(c-YUXzIUX}%AXExJ4 zES_gbWdRJ5I|2AlSUeRJ^;*Do9t=E~5RcanGZt`F>F<;zZubCb_pUiNLFAjdLvtao zWB3k&Z@@+|9co0(lJ5X*J@au@4v$#a$!b~I?q-c1b?EPP_sTmy&5 z%Ii4)=E|oGZvM^(-Dz%U0420Uo_lFb#arvZWses}PSjdt2pObkR{qv;@0pQfNv*ak zAq6+KrDD1f;KHSN6!(B2-&Od7hY7)lWK0WO@ zp-whXJU~gstjR%lz7*3O@WK7G(ZmABp_&xzXyYF2wijd;@Y~_WhT&8e#$Lq2uET2~ zi@AmQSIm1J68FX`P%fEeC2rv<-8Ufrz%e#jS*L}FDqdPL&5c7+h+%oA4UlbeKqgy} zGk9U+V|&o&ibdnnnZ@ItX&+}IfdFqJ>v{nwF7eRbL${3HpQM_vC{sXS58i3U=_rLc zWkForZT-zkW|Ai5-@sK2AwT@jOA0d!oobTAkdiwQa<~a~Q5~8?pY|FnIuYsJvtQ1q zNKAx_0CcFTGvvB(5TH)UlMN7}rP5j$|6gP3s% z$v!Imj`w?}wSt+bnXs9pr;~Xjj8ZE;7=UB&kxadbz$KnT3R1a;i-FqYOt{=S zGb4SSm}okdCd^ot(h>h{G0#k|4m-NL`%=G>z{iU?!Fq9jZ|{lF#f>wn_)}Kqe-Td{ z?Bbv~cmx0{8g^c1+92Yn$CeKk^B=Z){KajsM(jm@w}qVo7(Mj+46{j+s5zhB`lF(# zC;t~E<@Ao}EEnPhy-}GQIrG(8)&w{52Nd*nRcX?lLh^&ydNLKtI7?qsNt~j#%QonT z(hCs>nXp(gkQmMweVAf;M*PjrKDivPrGC~7{t@H(Ma}kNOy~LGy*5M_SD>D85^GIb zl0tf60wpMczBDWZK}?@11+)$s4gksqi;%cfx|<}`ejVr+jk%73t&za+ryD<=#Z{jj zOZFc+^+q&$<+_Tw<2+=LJcnlgW2tNXtAs=fGyvxS>lWi&0RP5q*=Kn{Y>vU!_C_26 zeLS%w{1!lUBDjQEVOH9$s1OcD&;5Bb6zge>%T+nLL}z&1xig|XLQl9~pE0F+7Q&4h zEH?UY=+0+Za1)Qf@(IA}6l#V{MoOK8MSeH|N!$cZVW2wwnt94BDT%y#ICcDGrZT3M zq>y^kLx*m8MX{>`Z^<$_DKqs@^>E4TVUwoHQ;M@L&WBf}@|tOgxs`GW@|lepM1=qx zYL;fq6@dFRJTwPKkU8TxD?S+2wGWd;4z;$uTo2{FBPYk*f-_PCYXxnWCP{Usxhzn{ zm9Hxy7+=6Ua6|JC-II|1X>Vygg|BOjS0f?^?Jt%mMzIg-CmdG_rah)I{gCH<^}G0+ zo84>Gkn1CYkSJTaWAAfftjIQ&o%xT7W}##Mi>B%DR3gzLGc(thl#%}?ds^XtP`Z?R z-?i?YqrhH-+YL=n`4^@GBGgH<9Tw`wFX5_C5%4QvwCq&)ss^ly@}v8p?^w*u0Wc1= z(VKK#Z%0I*3$v5&l~$i=1HPyK4dT;X`)th(H`KG@yQXOX_HT-1U2-Y1;t` zTDo=MRUuOOWo)|9c7>-|ae;8rTx{PaLv$d5!~wDLO)Lnr2>d#ZXUe7%^mwd-c-dzI zfGT>HoT)1dMDujw^01CjsN_>XWi1p+cIj~1qZ*S%9VGEx9j`gR6)saH%^WRS%;p<6 zy%sq^xh@?e;e)v6v5Gs+)ko$q`l-N^9ePu6>d4@b3#$YjY~G#~tHJxL~PHD`khwb`oFi=akk+2`BwguD~Hi_bjHhMf7D_zaH<uc;G zDN!}}9adX)tZy2hcHP1{hI+8h*q%D|9mH-uMV045USPbs6(+oz$w!Y!F%ggw_89B9 zF_E(LyeVI7b+0IJZALwJ?kf}BPWPum1@q!po^P(`*@%1|&_D{1MBe6G5r+O+ z=Ie-UbodNfXuj{od**G7dc;Lu(q8=|`9Or>wj49fhTj$J(s+8y_6Or=HiVlsm}F;K zgl1wm3*DqN;eLf3${ka;qxZrNyi}B(kM7Z}i@p zHr)WcXvZs;egjQ@H}!?-Q~aP_5u1lM#1J~Gy^v>2+>_L7>G)Z)rJD{j%pB}Zj<=%o z^JdUzda-g3cQrH>&?rmuZp)u7#`5o04|i&=^p|5-CNS=ld|^fpqA@NM0f9Cu)996%0_>pOh)DFK653)&H7sjG+_V-=%H%bF)?s2!>=Wr zny=spiAqU&FhLtJO_tR?84sOn2+DBRBoqD4HaQn#$c70qU^~;03lj8lndUd}x>KT-6 z=ph#=)2Y!ma^rs6&F`H|BtD4nVU|Xnun+L||1nztV%aHyEL+B(YJTVsk});oeIjch z?Eb#oA{Vs~9lvjkHZBz?IMb`A;;FnpKrfRkZWa9amf8lH(*zCmyj;M8kkzWn>XL|CN5-} z7>Nz=z~ZykM3u&kx(@tkZSZu&zrviI$$8m<**?aQWxhgh)quWy8bPIDIs4Qb+L_Dw zG50Eo{nHMN4xX_Z0D(p|xw5jD8KK`GWx_))wA zZg4_N)9}J<_+qecYaSI?G`&t(;hSg?v(5@cY5v~mF?L_hwiEd5D3{9-lT5d zcD0XYz@Um(mf1QHkNx_1ri|PifemSrEA@Ev6DdUD{!P>5-L|Roq)xBRosVD4_XfNq z!J$n#_Fr<25&OB?$U(s zg2>;>QF)d?V^`;o5Um)D(hy+}ZPk-bq;nhG71|qRTTu567~ekieT&5P1f2LUss&3b z$4-(jXT;_HCe_<>i8jYrj|yjm6ub)wW4;Sbm<+oFAs^~tkKjn0vdLhp#e5c;e2rIL zZhwoxr+{=-_r~X<=}PstXlDHcsy{1v)W;A(4^<;3%o9_AB~3$s(2Z2n!ilPaBKQ;F~$f3hj29+Wes6TFeTB@U8}6#Fbgd|@f$dYVHsZsbeV3~lP@ zVL8l}6{mMKQ`#P zT4e2?Y|D6L8o@sUx)gQ5o~zx=M?TT9u8W2H+jNI`5PUCMv|1lK_>9g~=;WAVm_Z}k z^nM8Y9_X*D@qfj{Fe^Cqq~RNR4!39I#Ltz61@FtOJMEG3_lz)^{oHIaeL#TxDAO3bC7Mt2V3-C zq({?7h*-2O@bEDlE=fMP*-WelLD|0=IXbsn$%JA|p9-tbeh2F!abrYdVFcUw;GwCM zK<4;gPK9!o_>6CwDgat;m?cudHQJp+oBnnlQoqZ-m9PI-mG4%KeRih&bNZ;c_)wmt zgkxQz?C*tGrcCm^4#$cX|7`Ids@(X4poacQW6@QJ8e%ueyN7iB! zQYKh9q9R57kBpo+l$IlKl1A3m+u$g2g&Wu5sJUU8p8=&1B`0Fa8kudPhVarU?qi|%rh zBvrD{10N719sp(OB4zQZ+SZbSf4km&kxS{05T<6jGIN+d5OWUNt{m(|q*f{MS2pfN z^9Du5NcS{x#xeEl9a^@n_iLa&AjcVry@wNHMMRXm;%KL2Nt+}?B^_zOu_E6yl~LAH zjd*Tz33TtdBoF>I;r0dVtGwJ}rwbh4(WuJyGxUYZ4Wm%dOjbmVOwfuPzsEI{hE)pD zfHiM&0T~U;fp6@;c_kBd(3F_JdyUfSSzJHJxuTm7xc6$zk2q55^V~Op>i0ZBSw-%l z@_DlZ4QXEgh+g7!5R+>)B^P0&`+?7{m->Xs}zMN^|vz&H?0 zjo4)W#~kYgvLQU80KsGc7eTsCGRdS>20khxjj}pGK{$7)D7m^>v`~=eiuS#$n^DjG zbHI!Xc_7Da(#?O2XHVc*haPZyC_kM?B-Q}>EnoX1l%geMq;HJ1`VL?)a;ZMR{>XH1Q?(rEa#jM^dc-s4SU<3d34s87j-~M*;YlfPM0H(q#rM2x z%iBJx@rcHpl5AW#->%*j;~8i))S0TI0vXEotNjSB8h{c=V7rDzW{2MZ+81u(a958A zvR8Re{u3;dM?YFSY`pV$*d7qfa7C_FzW4@|)ir*msLL$Jpwxgo`YeCQ>sig?wzmYM zGvQs=8ymOS>zo{?;KH^4J(;Dy^;@*U9OsR(T%Nv9@Dp|aNaV90$Et`E>)%jsjkfA$ zC&KvEjvAN*_{Hz3RR}_T%Y*qsN31#?*3RrWe_!(Zwa#>d{HI&ZKgAFtlAg9-C+YY= zhA#E&M23K(Q}=5I6DiZVS)6C<$KGz-cajNct!Pq?FQLq3l>=Hy|8ZmZ-{b1w34 za5AhwKfDdv%D~mE9EZM;{2rmBeK=MruyDF6vuFkVnV_Er2p&0=Ufy!G^&$w@cZ|Pb za6VX!wpn~(`{RY{9}?X_X$$r7nzfOU3Z&7wL^wU{-syZ)Dl($;E1~TRv+gzYAk_(m zm8>8-F6u^^YRIgHP0jxe9J!KNGd`(vdj9N!)RV;Qi??zU9cVcpzmp}Wj+5=E+ZNS5 z^^R<-5=s00m4!%b&`C}fNR*iZ2L0H={ouB)#NYp}l23!k4LwWGhSTdA#@lC&kDc>D z$S0YRW;DUXbf@bC2roc?7{$pfviWJz26bE**e$K8k{XKumF=RM6vN8PXUBEP&2(3*r!$ss9WsV z@^>lEt*SnoX@3FG5gT^prnJRI(H*+97q0jq2gg>z>;*>&sv#Z1kAtpTg#+hA#8Y3J z!SXBwYpm>z?L-VmDSXsAo0g3_MEWHQawoV$+}RlM=)YhzQO_YoIR0<{Gy_l6&06a~ zwE4_171Kj!Rr!mzkqtmG`jL}BZyVV4UImgM3otrveQ_WjCas)$DTJe!AhUH4c%v5P z5VL+yiNglrGPkBZz-RGvErqpKKoJ7qnG)cmTct7D<5E`?#3_P+fF+*oHB5b%+Pc7H z(^(YMFWbt&ThZy^VBj16*>VN+Ez5JSy`)Gc!~V9*Q&9L}UnXhA{Mj92*{8djgC5m^ zQXymaXeJ90VtM^r7w^BEHgIEow6+O4hSWvlWLDG$KZ5iw*+ zP78tP9}lf}`~MHf9XcK7c|rIVRD{4gJ)i_c-n$QhlueNO>M~mZ>J=mgB(0uL0O+7M zX-;-;YTJsgFLah3>kbKH=u!9a#@2sWwcAUj7ls$P(=m67iDqzbn;MJp z-}(Y?&-SOrvAsuxZX%(U0q2OP?^5*S8ItT;|An2U>h7nnwG;)XuOhXt|A3ZKe{RS` z2+^?z51>I=I@jq5<(j=!5{gNF5s^sLaOoK98 z{M6vH3W_yB`hRuF@JDp(o%!)qsWSaEk0U%~XLI&%o`inh?0D4oFv3~GLLPqpwP@ZI zsWpea3{vLdOZaFyPf2Grb@skGVKTG2+re{Z66_TSMlm8G_4j;n_SpY2^`1H= zXHQ|s5ENx~T#9&4d^gY_jTvJsyfgZI-o@>3IrCW^1Duh8xT>79rM0KIbNO23L(2OM zOqMNUL|>b&een&}^C<0l?b_63gQmwLiJPj5Th#v|E)&;UXF+dI6Qzzd0eU<=QeSW; zO8CzW9zXg%m##!}v+jfX4bSo70e6(jdB=F(eMb1%?4ulr**^rF-cQc@gng7su)x(; z_}^8W5=^3ZAIxzTaq}*)O*zXV!Yks4Dz~B2P{x1)`syZWm2z{0LH<)qc*eVt6Zac# zSLxB5)+tn#OI?Jm;JY zT8vVwR=b4=q01gGY`g$fK&rn!fIsQBunVI5B)H9h3orZ+em!~!>Cr#{qaS>!KP2T` zmUJOMQn8Kmc$SI#k+>&~469ZncE~%Pdu&>S z|21v8l(4=JPk>gu&Jn`#cNVz7hUWyXmmDY|tjl56mKtAoB%44W3ZDX)?SXiRH=Bc4 ziONQ<5M;wpY(ad?62*=Rh-Q4r+0oe;d(yIhmy?j3DzDUT3Kvv#<7w$-4C8v+Us&NJ zcaeNc$V7a_u;IHX^4^TUM^X$Pm$lS26YL6u$Y>sjCXYV44*n53A9HGa7Qv`Q?pusc zfIisiPKlth*}iBS`KXqbT;|E1l-pCvv;CGy93-~=SHWE7HG*!gSh4hs>yWU0G~lxW zZeImy3k2Bv$Wk+?!MJ?H zoHl8PB=HCp9c7rB!bIWHsSE);N)tHvUHjTN1E}{BDByBQo=n+J?4NY*N4yBo$6YM*tZ zwld_7Q3}%BkdP?~O5?cPk$%2a6AxW4093#Y8PJWa+N`u12DZ~5OS#vj!{l1 zmD18^v|!~5Q%9MDke9E(DABsguXi}IDJ^OLv6zi`3t9|2^UorCDeoMYTvvvFN+=g2 zDH|$UIxrpAEa5>90VoimHR2%lQ+zFmfHCgy{2r<$yk1kJhMFL!7O9Wu5;IYVwpV(h z$saEc1P=5c(!SrMF*oOkF+Xuwikzdl6h=4FReUl#ff{E)gZr`QHt}7kyXiS&SC>9> z$j~?OIm;7<7kk0E-0KpGi$w!GFjt}AOtc`0ds!G4w&vA~dWa@vZ?DJ-VWKp*wj8rb zoKTr(+%y}tIWG^@wLCr~b!8QOv)Y*XBIGDDIWiiC;x%+-cNhmea8HSQGGe-3q)sR4{69z+6)4Ipn8}(#DV_ z1O#~>dgRdYx%Ss2VXXbZyt^9QxRRp)H9xfzq)qfjSDJjs`9)OD1;Z2`l3Avtmoo^T zM$?pRl^bIQ3d~Z#^XVNF2Zz<^;t?MFZ>=x@8H~6DKw(IT&b^JzS1J2H!`hkkk&PxV zL-Ar4Smu+Quhcu; z{15UG^TJib;6-LEB3NIi6qVqakGj@zsD9*XYI>#|AuJ9W=;}o~;pD}DPHcf2(SRX3 z5)1~ln?yXD6a*ypdKg&KCy8|f2mK_|@L9uXm9X=JK64CcNNtVu?(D&T9yr$#RVhT@ zSCuXTh@gJegw1sV1NVzz3CYlf&KZws?-yya(32d#v;Vuu>j(iKcf{g`54^Hpx5lt} ztb@4aTEqMN?cbq9kFp>Y;KJabxbP+~-?Q!}SK@cAO!l7eV;eK|#MTz#?kl43_lvdiqPcf9 z^`Q;~(=9+UALea*abTk~5wkUoM5LV%c^hKy7C_2S-_ec21Rv0ekw}tT0AHsT%(!~w zUF&9~nrGh3cJmVNXeowAt-^0Kv)sivM8tUkYf|iV1R!ngdd<(6Ua)j?!c^dqg^wxW zjpjgkzKuUyDG}I2N$L@Vm$_E0*+^FGy=mp`47=3tJ{IFjt;t1qZaGzP*L zLrJR)RdM}W#y=x6kbYa;2HL|#y6W57Fe z5@N@t>OcNG-}AE=EwyruM=B(tL$ucFDnY3;RB?Pu1s{9$1gyttE@vPJ|E$p`K`-}A z=xh9)_A7xTII_6TVkal0U_2CGhY2PVR zoMU5sIwbv&1p|auueus+@4(?#I2Mp$s;$$;{fR$tl6y!ywE8oenax-Z88}9MiK1|t zdk7pwer^r!X!#tHePrQvbHpScm=hIQkr>^m1EPW44LmA!LEB&FaWl4-+_)9uQo^Fi z4^S5)U-inPU*SP&DCM3>l-Q1JEDqK~D#}N}z5Oxf4vW=!ykTvjC$^Nem-mFoYMe`0 z+5V?x=FErE5rsfFE;A@V9J+uXOoB$-Ntyl5#r%!-zlQ6yST;`eShe<+w{lOgf1kG(7Ca*yCM&cR=&VLYjk>D3fsD>gjHECbsH615x6*K5BnJL=fS{z)6s_nv%Z z>2Ma|`Ob)xvQu#X1?cq3rSB_)8UWLDguT%L0pXoLtRP8>7eM>d z&(+j(!gQ`_UFAv-V3>K&^~xqh9a8^6x{ktD7n-`V54P z8VUAa#_ilpn`w4m83f#PD0=b(a!OC(p83aDy4(P{1Alwd4!yi)Otii|_rqpSrLhcR zWxIOk|M^1AB*8&PPJOIO0bXQEzSd^8@0(3p<9nB*IR! z4#k7z0G3h$h*lchOg{#(-ls_2wgkP4fD}kt;;+^bH*3Y8d=+*biQy4)RF51n+|#M$)s4dqWka>+7iU@HsZYhMNMDZ zHPveor7w?20~-D!=ek#QRxw~r6ExOPPNKggTv)n3vn(eggyS!OV2Tc3D&~o& zvNPqGyyzWvZ4R*0Of$ac4#SOndehltlzEh=gg*S9+thQyVS?9>%Iod38C?4=02sVl z!is*gcovc3evQ0}@gIy=PX}n7mg)QvQ5xw1qQzr!Aaclo6-<3wE?2?$L*@|?+!I|s zJqay)+wFSRZ5AGyXLO=#fqwP@I-C=|{jEaz=rC}lBEgH0u!>`wikCW|qxy6OarX;l zGlMkE;ne&Ix}C~LW@5QF9BIHd+ClG4KPTtwx8U{8=(|T*obE5xvQ*F_D5{H=G}P>q znaM1D%zvcHx5<^9+{u=$SD#Lvz$F%u4KNOwf9eSQq79j|Py)L{Nv+hIIA3-9Xa8B^ z*!MVC7$}$U78>A=nS(53gd@EilAM`dFS7I9eqmL!Qr5l!>dTDbfspF(2+SU=N=k={ei7FE0gob%)oRQ|}w&*VF%h}=9`s1+3 z9dI|wDm9lHy@v0kP=Zq)rIEvp@N6lxNh!g+GCAI7$*q%$>~bl6pwx{^DTZwx>G@bb zD@`H9CJ6fZUjXfnx3;iS0^$O;i|>0pL4sjsm!cc-tmhW-F}ez+RPpEa9z6gqwof{0 zZAqmLeJ2~oTw{r6Qc#$=s$ygGtL};#-z$_nq^KD-*1Y=qhBLPrf%I&%H!))b^8Oh> z{hSWA;wHzDsGKN=9++aRaRfMlHU#p)S6_B~z{1)8;dMzEoY!#wl{52Hug6$W(^TwyVp0EI?|8M`t8CEe3#8bB z?Iv34e;EB(GmAfitTcC-ll%?f__cQ|Y{3n)dMy9gV}p#Wuaf7y++dSpNjCFLUikq(}WEqEb91=s1kBl|5v!Dm)b8`*q0-W^rF5ot(Lk*IJCi_Fat8?dyn~O zn=!Fk`odM)<*jml-giy`yZ2l%$?i=H@>KS|KM;)Upg||pY^dyGqoiT55GcA1b0>Su z%%VZmIta_I-!Ztj6Ug!W_&k6JL~QXL^5b^ULn^2u5Y!=)B{8U5{3H|a4-2x-jue3$ z1Ww|9A~{P88pUT8>9*MhlyL2D0vUzF6?F)(C{gBQ4A{cny-<2f!-xWw@Yws@dVDj^ zc~+M5Zhy1w!n1!_dQomkn`hbbk1IvQFR_DAW6N3#W;P;k$RbR519pOfoXNtyI_w4g zai+;b%UehlTwtzH%h)xz??PuzY=UrOG-+iuP@#>88T3xA9oth$?`72ELZNt=wKVY; z9P2)C&UEn^%l7GOsWa@kQPG%sV&j?_lcD6j=^z7FgrNzM=recv)s+#+H8$9m7aG4- z_E3I$3?1Z=AJ*Lu?V~9j5X@~pgtJuhnn?WmRqEoMPJIs-u5CvR&j$AlL~8MRe;-rv zSdbGZ-5XurgAs&HZEL3!g+4ErPS%rPX@Sb4>gcVkb2R#CIG<4D5^79UsYR{^ zORc0;91C(<>W2^VE6Oo}#C>;~b%Osa<`49IAhWoHT)eMLKb2hfDe80kOduE5gB)M(rvTm}KX&uB7m2XG< z>_NdDeb;+yk$DHp;`xz%ud5!Q$lO5pOC0tZ{o|u_1_|cFMGdd>(e}GaWTAOZKaZ;Q zMqsq7HAb98;%DGXfPZg_F%xSki&op3e=pU8?-WPlmG>&94lH|=10#<-W(P!Vvb6=< zC^8HZwyM*vyTmYwbg+2H2KdAoi3I}PP&UAKj&cjNIsYF6INOlCFO|Z86;)z3rzy83 zD@%L~2`wiyvV)fb2mvahsRH;-G-%on~^N&?4@@6aLyDdV>wg+X0)FvLeuleSFBEYdxO= zT9RoLwW-}rU<&|%0*rz>y$gaP&7lyxoT}LpgH+C;c(l6G76E#tY5JQD;wiEC*DJh1<`X2 zVU_8V@ebdGR-g3ymY>C@ytz3+GwlHhrxZpXO8Z(+Y~uSHlUjE>cm7ykBHt<@kXd>y z2&z>AbcT!~i=AnCh7q}aIGNdQK!BJ)-s;!>n&7{Om_u?)Dm{Q!(pOjI3(_4<&LG!a z&*b}VH)q9rR@#7Qgp7C&+=JcuZhb6?djrvD_9nT=@$Z+P6&rm&DN;>G-lfw()&zX8y`Gov+h&WPg?Yi!-I&tLzgH9f!l^{s=zy};oY2AVM(f0Q`$CZy6ECB789QWeH|J(HcAb* z^@@#rRtx3wJDFD%93xnGP_!}(ukg5acDJPD3;f{=yQ!69c2P7?Yi)t@0~z0#qbPYq z@C|qm`7B)pZ-@5;wpLbVLg+p@T-Y+;{Ygog4>=jpZr)~Xz(X7q&gD+bs+YzeUysd< z87V&qf>=^&1a76;WVmI8*3{12m+h$RX(Q(D%i(gSn@&d^w%zCqFL41b>d| zKh?x=fdLggU8K3fDqogPq@s=FjWe!^qCP{6vwWSuZYJ|M&Dm?bC?APxsO{%OH+-X-btHbikyJ-nlzLa3n zrs!a3w4G6}QvU;>#w3wb^-`S`SaTR-X(Jc4fpabOS?+D`?PtepZK zoXYwCd+b_y?%>P>0!B#9RGcX|I_LNTRrLKZ73rVI#UxO(oT9Y9-;uD zSE9*GWAs&zL#29_Z!x;F#T+J?>L7Ny4s;uyK~nDftKBi5watPdTjs6o<&jS#t>hMq zVwDt?9>$V`_(Zj(=TCA(UrHLBnjk6P&&K`q=nc8B%=N0M>fs4uxvsrER6)Imj)!mE z=?tJRE23qm8V2Payv*=>vvBF|=ncj*%SjFx<)reDYst`;x6fWdVuNi<-H@2|aWL+; zQCpbua5|(BhMpnaw17jIEHRHIh945yiTI%;Y^L-FXI7NeVdjpRyY1t}fFzmQF1kn$ zw@aexp_=bUTiK%Bc4Y2^P6RenTdof7Ps)_3B|Mlnc@0p1fje8ja2G=(|3+U7(i^&O zeyzJ*^x5`zzH_kf*s^IR!EM|%eBk=>+*Az;5jYG@tz*1s-x-F}$-8Pia}5G_768aR z6*}2599U&!@Qu4kl|9BeTN=lV7+u3v?o`U^2qHL%5NtB!qKFPCRLmLWu&8kG3E*QC zj?FMf!lGn4LeWznZjj6QGQYI*@5((X1zEAaWT~f{)^kO4Yld>JOR6b7}E}q$+hRbO{z1MDy3;4@GE(2v(O~$G$Uo0g76?*z#>R z!{?yig-dnk9Yjm0#50MFZX}{lpq*=u$-JTF#CDAw|%1Fgtmj= z-0pJG(`Rl4rVC>`CSOh~K!>e7{PM=czg9Kz8UAX_OW|(#c-t8&pZ{4%N`%&HH1de9 znl1SFuIgOW&3bBkO!2NE$uQ=!ej3|zu0JY}Yg%O{z$EHSN(+nbh>eYTH_X?LOLBs_ zDP}1#ZdHgox)&cS5n~Pn&3dO3WAP^NSkJLVGzUkt`O|pewV}!XaP&2y57LpV$Pj%c zi5v@$h~PXJ6+a5(A*M>RZ(PGkKo@b9moksmMrGUtCH-_y{}zHR%aawQg@f1%?(d3%;;lc+0v^KMvo=O;vDQA1$?M69j}Sw6jHa zv>0d|p>EY)x_2T;FF;1s#5yEqL+Kg=6hLP=OmG+f*+zsfe-*XTl&&js7ya&vg0B^XvN$M*xo9|RU%~8* z)Wal0jW(RM1N0r7?e1k>?00~IIR)($EmEc^_eu*&!jmh-(7%bKIud(Wkrmp3%@7_- zq#TPB$}MnB$SGHKnc&FB zOjfuwfBtvZYZKiW(o^Z1*EGzoduN7V-iHSOq&CIo5H&OR)DQk>dY9e6wp`AoF@BXo zQGAYng-XM!Cx@_Y{7&h#mZ__?nKBAoR*1`4~$9|(OQJ#kLRB&CYm=I?s^t@9+X z!P6HFW73Bdc4CDx2_55lZN)!gakqJl8qB7n6<2Di|+~=`H1uWS0nWG#06^ zViI4S(eOI+niG%UsIW>8Bm*^n&zNZNFwt^`u^!vn&F(UXG&V@vy8@_@KkflR3gsFA zT{cV_x+3ZHw3;3|gjuZR5%Wm?%GHz?GjX0QtYEI`rdFDfx;>^ANZW`Dmb^)Q8(Y~R z4Fw3dLyM}f7wt6KdmLtExrRjK5&kTIK2kq}tG|^EM|rOxc|{whfh}%lJkLV0?Qf-y zvLKol&-yf}DGq`K)ym)pd#x}||8f|a+8bG1p;D%C36LovU+aheN*22R_jZoe4t7UM zg7XfRgY#!ef?;M1Y7UokHxoI@<6KJM6m*L9K8XI5pqE^9%H&X+FatQAaZRY#%O$pp zmE4lHjB3y)oBw};+rXZHtKvYHo^cH7?y{ZSMs(hkMLU_W%&+;d%I{acZz%#!MnX!E z*o8sc$XTkv-HO-P1KyDY-p~-**JJypfrrzSdvcPITZ4A$TnaM4Lx|jpDg0@;KiYjpV6h@8uUJOZL%H$c!?>-yu)>DptcDgX8H(o@_&3Ull$KOcb4N8YOd+sGJm zR6fw+HCv%V4>f59B8bJI9u-Fc&#qnc*k6?Iu^X0-m6&s->$9CPu36u1%G=E5eMGsi zsL;YsWk#1otMS&#kForS47m=7bK^eo{F2vB4|vqarv#sluY33o#~``Fs>3UiHmxx z>U^d=-)aOL!=3-bjg=-!*1;+SSrS-t`Dq4Nv-@d^5DmLu^3wj-mj2+cC5Fk1$Zj|H z>100}N#*7U9;F{ruyETu&?D?G-FV}+O_8Mtza27CCiSbtk%a=D51H}?fmIDB1PBYg zC=y9AlD9rom3=bk*Cx?t!>Ibo?O|K z;FCFQFOK@*c)*$D&-wjssOUeqT*oG0OGdtn?Tr@#h$S?EQ4OdU7AST$RD|F+@#a`} zx0vo!3<0px7wV>Cf8`aiLFI{#CuE=Qmb^x9Q^!8>o|WP_Bb9BJ1KHk}obAj%`g}EP zdDD>X7*euxGWCbgb=qj}8YEcNm?9m8^KD>hjQGe36QZtaM9il^1_$_xqX+XCM*bvH zXtiX%+z$fOZxO!awjrCw?WMuXUWn49N0hKY#AG=Xp-ue(Y6kV`^VD2f ztaNKMQ;VJ19L^#Gok@=6?ih8U_XQyp6xv_s@|x=YqRF!JC>Q)^lPwDBoA5$(76tzI zZUM7fbLpYqV|?;jeBx7M-H0t~ndVp0$al1FGLKQHdv9KdVT`PKu2 zMNckF_b_nN?^U@aD>KmzPszGQ+in9wCGpE0y+C(q~>xhMkHuL z>j$W;EB)XJuL=Nm4KzRfA}>);9uq-B!{Dz4K70i?w1Q6g5iC}EYtf#WvmS-OhBcP$ zIHqUEimkb*&g-bT)FN|FiqpzP?kC*t&}%rUJPCqv`0jc6ITx2yPiJR( z=te~`_NA21VnTH^$%0&izP8;do9!b$nfap2qrn~9G?PPnwo1H^QJ7_jZ`E;x zTr_EMr3muuz5@BEzmmfQUuHeyjwX?_y_+&?0MUFe!FAj0$>c zs!(Fv1#P9cb1hT7CSdI9)EK?ZAMAY6Oq@fG4a8s#pP0`iH5EL&ohvEe5MdoWVrHPO z2#&9x*pPETz81t1{SqF`$6+6_1-4r&ZaxxGw4`nvi*^Ir^?rM{z%!_Ie6aie3kevl zs!7|p%8l_%L;gz9NWW|jPSCbWGMDg`X)LlXLhIJ8w@=YF^nyf<7PDz!?$w1O65g>OCf<9HR<+4WzuNr|TF-dae zYGTVD5SCF(H9Wb%rAK#>KaO5c0$7R5=jMTVLg}gy#{)hN0ges2k4Yv-k(!G4@UE7Nk8xhh9;yX)Hv_Rzf_?0hph1ry9_a?~d|0oD(URIe( z`Vz9Ub}4jLoLc$R^zg=XqMa>;=W~QXvWjv7Evps9(33xQ8 zU&xpamDUdgvrM%J_v4lIoSTUE5@kATrO?#v6VK1gE!R9@eyzIu zSm?7}DX(SU!{X3(CEwCO{^rS&o<{!y$%kif*bF(kvwCcA8OV}w-DRL_r0o+Bgwtc> z7~&hMML>@Zfv1)#&Wf$tr;1#izv>1jVc8t9sY5~4Y4?6x^L;U^Px|l)(;(H7PM(c3 zTCNNkzgN)Bbbv@fIrVWp;Kl-0FD-D?VbK?5-+^8hywlQ4kF-&yZ@%`N*RFv1dhtQ+ zvZd>S9~IhS#rmApYeBTkrUL$SKk|1R2`ekZ;co{VDHQkuyTT7sQG-{^RpKTjY@_wY z=+dIe_j|w=zKo3_6Q5H(+u8R})8^ns7(%5VNksI+poJG3l+DK&@`Q$?5ZQu1fEEKu zM6FPkU2blTN$ak#;SP~Tu9t$xaMGsZOI2^#5P9z*&{5qHna(=Y&r~DHK0+;(-_=bT zx0IOD{xJV8p?&|nvQ1UNM*%Nn@9SIwcrmh~bIH{a^QE2cpi#AGv(Y0QawovHqg)7L z0_j&zLzdkbFT{G6o8XQ~f?)n&XL-X=J+4FEo(w6H=h%)+E&&oiYq11OZvMRU8#m zq_0;@;gfsA6;^l)-icTNAC3s$0!VHHcyTDL1!lVSj!ch7lCBM_JA#-ioF(qkvQ@uW ztg@dDD7Wzux7-662EG`6-(? z(u#qWSw?(5xq-o+?c{wx#1YHU(y<%KO>`x4d1Mu~gB<+CNx-a9WO+K-zyr!Sl-f$l zxZg6@H(bk~4c_Ti#i%C8mAT&1?&PegptLwa<=&|YXkF}}0^BrEc^(<*=;YF2QT>i| zfuEMD*7-4}6w~#%80N^W;{507kz^UE3jGW#J&_oYpenF&8x6SqI0Mjc2km4aAlwcep~Lxd?&cFUxM@aOGfEZ=0vv%* z&;7foz+$Tly_5VrJJJ@bCd)r`Wm>AbQQlmQM%V59eJxNdFfS@#=hMKkuU{}lx3u!?DW-F7V^RcCl(KMP;rHA#cGU-!G5 z@xie<2rNHp$R%!pfsg!=EfasIL+n3Z-ZJZ`W+#UTlgenemYz9bUoUqT^o_FXRNc<^ zuxqL63sP=VJs#m8L2XJjS4Mp}iMFQuT^dVvuGN>LaeG3V7TL-3TA{S4uY7E|jxD{$ zl}}li+~J?%)m7)PpVu7&bB6Jx697lEt@^r&I_ub&!W2-;>ItuXi{kh0Se&~EhyM-T z4;XC^krY=Gr|drO?0{#XJvQxca9BQ|eEA&0*s@{(3&BturX%g;-#2;pDFPEWZxymu zTj*J+rd}l|{UmIT-y`2ve${LZ4{heo;+Z}xLLyGTp32x6%D@>k_<5z91| zUR{#i;mtef>5bjlEog-AaUStas8%Bwf%E+yG`3h`jqD&1A?GE9yO#)<(!f(4&k{gO zAC91;2$&U1SW&BOHSEr9IFR#@%(QT8#X`DhoQ>q~+gP-h$4XZ1yB|1=E3sr;g`sHZj z)LLW@{tIOg`!c3iuxzd%i>?7Oq2y%Kd#>tQqS9q@$tX*s;TXTlj|>x~q0=wn$p0z9 znYy&32u%#4I=#t~`%$77-(NOShg$#cx;jhGJ^w?{1IdN<9hvkZ{V3R&av!}h^by*334bI~Y22UDr0n>l&J6Gsc{gQvCA313e(ODRSHl%Mm9LknT`}$;E z++R-QRFrJ;;7F$oja8oR&HUJ>!}krM(teZ>q+Hq@QMkme*K)V%yQU5!!nV4ol`tx9 zF44uAsjHfCSrec^iWBB%d&V?u0=v4ERMKzzHSv_OMyA|W$t=wSS$F#O zF*m|F-7D4qA}mzaWXm$hSK9=VPgqJ~0@ydgLY8ICB?-n3T-4VPKM$oKhVu7pxUmm;iC59axD174 z&mNG&foxcSm3-klhKjX+ye-g*#%s8kANZn9S<*f!MPyi1QQ{XLM-v4!sN``f7`uPY z68X&5;0gmkFF}dn)@FFFxXBe(!>-6|U4mNSX*+DS*vb!To>rAqnydaE;?9;YKe!y| zyT$eF>H;)&YbR*D?_*b<1zH}cPec!z)NNE}jtUQD0tf$Ehv-M^E&8+^DsEm6OQb8& zjx9E4Le6mC@*Cv(fr!5Drz6Io{&L5XB75H3*}$|7xk`P_K8d4O$wKjEN;$6Ta{k+; zD*fP-si)XH&_rq6tWw~U$7rDmJH|vT{ZvO4w#EUuFZOCo*AzL&i2|htINrv9OX>$1 z_8pm=TMdsyP%;2{MR`+msf;7KF|gHMXr_JBE48~32~JdfN&*f+8e;$W7f)35UW?lb zL;uYN4fi8)X%F~?_!hP1Zx}`?=o*HjM~=ff1*xbG0NqBfjR^G<{4I)YtyEwJ9FY&q zUeFdMS856O!f7SaIbs0v%%TGC($T1)p&8q$Hz4hyQx4OA-0s#m8CP=>4C;+UsWGyW zF*&`O7v$cnxOx37J+C6_`Zsuuhh~BQA^T>zzH*gTJ9m?8*ca8^%pt~1dn~Flc%i)@ z`FMe{Em^t?Pj3XRJ+UpE`;_r3&K`6ZSQB3_KDaBy0;wO1Um*7nr2TEY8@>sN!X;ne zcI3Kj^dF!+lC?>K^0RApI-A7XBu)rwJ0NAimC$NZ*V3xx6RRjYT~*7*Le-(ot^B<0 zD{Pl-lGG;TkQ4@S@H%)w&lhk)H}C~xAEm@$n_0f+ABJ+hT#R_5vLp8=bjHCwK}<&S z(7DOeJ>Q=v4?w#q8i(;!|M{B#%E~)v8NckdPgRI6+#+D!qs>*NPq%_`#)H~nU{PTp z6+N&d*iSq#80(*%X~qS6B4uSpgPHv5G0^;V5+(UagsmyPc)k&#Jpf=bQ}S-JAeS+C z^Egx-Hw22&1-`tPYEG$W;gC(qf{hzK_1BZ!f+I!<@hl+CO+BV>4Ht_p$x0cX2_V9Z z@QF(w*SH+fCx_Jd9J(&>^)=Rx?~$(~ zHhMK0Sct7oNtbM&D40Z87Y)5xV;X+aCoMKHV5Yypl9w-WW^t6F{lQOtsc?0nJKz*I zJ>{scx1AS~NRO#IHr_`*f5vdPWTU*<7K}?4`>GhDx0clQn^t3JVEN${cdVV~HTve+ zO7U5VgV-W{ylTs9I*L;-Vi?;pK;+3iwl_%kLW8f416Nq7`1D*ohElRJyONkx^&-&u z_AixjHjH|J?qZ<<|8^O0yc-KLILzBvfy~U?bMa}fh30Z`e=VjEgAN`k?U-AUpr=t} zZ0=cRP5tf1CkXI?imA}TPom5kZ|otJxA#or{4f@Qx?o-&ifg-b$`I7_ z>|4g%zX(wEUdhx`=yG+@`XM8u5jgB4l|1sZ6nqh9E=7WRrXTg3=+t@l0~q>zom+qn zN2i|1`D`c}OU2WQ>it8iIGp)vlEV_8OG1>msP}}bxJ_w|ci#BitCXUynsTSnKiP>X z=KP=MA^Ab4V_KZOA3e3L3}iR@UWep6KWZ5dl1zpEkE(%Lu$x~ICo?7qA+jjf%{d_} z+Sy?;YF2%!-$4#!mtfb|DI&Bou0$$zo*E&XEaZUn#97zFaC%I!OB;cJ&J^&{(K<5 zVT&{A!YgE+k}@{H1Gfly`cWYO+TdO4MP)+;(PMGct^Sq6=k8gQ&FFWOf~>hb#W}kk z>3*Oil&ksSl8_RI_NS`NRF-!GKi`?bY4$m802)~iomILY4#S_0fcjkVYZif8Z?E2d zd3n@!2*Pn0s$Lah)0<(G%*ASGk(~Byfb)_GaFH(@(pH>NGE3)L?=%|Tm@d+Ez-m?T zi(W%5#BC{Fi(def^Mxup^wD?XxyFKymIPZngO{Sq#uiT*8v%4OFm$9yXa5ajVMD?~ z^Bh>JqL2Z!2E9o(YN}rCTGl-v@L?QfchtY&m0r+D#a<*?XS`+vd#`Bc&E>9&A zb9NMNok9Cd;qGt$4OV9Zp(N@iTLeXgCgulKU7N;wSjV&?QcSk-8rOx)*jau8Mt8SeZa66lk8*M2nhCvQMtwp$Tt)#f%XgqCd{U9q!9-Xeg&UKsLML!*7DfnXZwM&;Um3)aMh%PF_O7V0iEn)! zzbkY({a9p8L?-uZOX#*nVYLp@&I%OP*4NUFDf<>Off4YBE&_>$fHPK1BYDvqhLL&S zTZm2pwP{RL0iQWGm(jV^86+z4 zgOE?ia_9S$GBuE7yNkP@o*TS{_(CrjKhyPVus#1ek0p?U1*{-y|3iZ*HU}tn~ zfMyZ!PUE05-H{F7#4QzNqDtSo5dc)4z49gRY;G*;Fbnb7D;KDN~!Vp*S zf?#ei3hF^9C5?Q+gg{^W|BcW`i{Y<^r%$GLsGxptDcBYf>H{jux08vF z9?|+ligFGj_D7#giFm4N1h@Ls>=o?h1$XfhGfn+#%(spa9ltjXc*}8XCxJ*>IL0r?1vveq zT+)fwb)vc?pJ4OoGe4Bb@xV+vIJ%8)$eY$p##B*(ddWu(X)FF}uCcI34dAQnK@LU5 zzTc_dO5ZUn?~9ce`0@wE+_T@lDiy$5kq>RyBYh3VM&gp3hBU?CcjAtV8RExN6ACtt zdBLwVxdcvas)=fcm-@`)#S>q{aG&`l3inn9p3P-=gy{Lc+RZGXEJD^Ps{%yMH-k0h z?SEWk5z{MT7Z>I4%26@LrL-Q;XJpayG$dc|PWF~?qy~Z04**{)eglfq0vh)rn{U;C zrJCD-K##qHg2#N)$PuyV*%UnM(8AiIg+iQ-HS9qb1rDk2RH9!+15--lOLQVh5Bqa5 zG-{3Vv`tM)pVZQkfx)GDYiE|tE^AO~nWgjS;_lEmseGTs^t(clwxs?!5l}m7<7(D% zS&AO?bBCu5H3@)5!xZSz7o^mR2dRz%Khcazu#X%T9o5ZkQGXyy-Y9AQDwO<*LAmX> zb%%l{o){G7dq}_flvb}T()kQw{CZDz>fVYNPICFg1gVGGX_ucJBl>8CUn z!Ck00cuNPxEbix)GiY1K>+>}M>vOJRr&eyGjXp38EmKok9vA!>FJIlpGz9GkHK?vHwi4Hg^F z{kJX!f(>d~J6@LenVuWZB5mnh0zlMzhn*b4L&ThuhR!5gJ%KCNtLl`?&k zj&GRI1V+_fA=SDOgHjx;<`+|`e$z42IrIA!8tY9J^V8ATm3o?5U@!V5=nWxB6z~y; zd?KuQt*gF$>4Oqh-wqFpmu?`b5Rk_}5OM+ti1}8BDVu8XEny$Nj1)-5ClX90nRB4X zHxHKBLfyz>^hp%?w3*L46e~#6K0TWcO^5)h0_Ql9y*mtDMH={zJt}P6;^r$z12~}t zI`-lf{HLFV_(R22zl$^oJrE~RGz`#Ga&Z~;Io{{f0!MbjcAEf$%L8KfPERF-ZpNF2 zWXHStM7l{f#n8XWp)@lxX2XQ*iw;=xeO4I$s0`^epBj2ZvX`w<(c;=jOA50QFdSht zBzD$|&)^qCrikm!3%t;FNz%--fw$S%>FkdNbg+|%<$>)^RgM|(X&(~zPQ29iRm{Zo z^^xsg$kyqTdrbF0Zp^hhH1MA9)mh_Z9`Q1~Eaa9T26VZjGzd$0{ulJt9O{a%X`%FI z)Q_+>l6YyVL+rKcS`n2L{%pw~GW9(pA!)F!{E(OHkjZWRN`Bw+c1BLf1Ek$TyG;(o z_wB$#0tv{GL$B#pUBK&%OEtYaD+WWdxu|jpdD!asZChfJbvYBinmV47?;bVcO^K9g zEk$)fCc;0==vT2H*P%3Mw-zad;j2DnpOMef$GQ14Xnw4aF9`y69yvoX=aQa^taqFOz?i|ldCZ@e^l z+cVPA-+K?kO1o?ZA(U8}aR4n> zE4c?q)TJj?gCX>GZjW~vtRGcMye2A1i8c}xC44bbXKlIyWaAT%I`k5fzA|_mAbUf} ziY?n@o>kbMP!q<~fPi}WnLbh=2O@JCO<7A7-P$}RWWsGKw8A|H*L~8J6r!^-U%4m# zqx-__PBsnIenai%Ex;l!L~U$Aocb+axt#OY`w+(&kXT-CFUS;(5H4H3-|bKt^ejyN zOcC&A?0v(MFienbyZdX~wr$(CZQHhO+qP}nwr$>j7WWNiIT=w=mD^fWWt=#e92p5d zy3==Y)U*_49J=}(c=#J$@H>FQ)UvJ!_&;|zTI?VbRzXwj)CU9{+GwePx72;D*P5fnSQ6$_)uD(c` zIfbo{>9V?Ht@wBz`p?APtD*D_z(#V`3{R-`KOD!mGxl_z;5=`LV2dM|$=%M!ZejRv zq7o*grXsKe&4~tsh`-WTi7CyL;nhZU9Aq@51SnwvO~KN6&Cd2#w0d3`{>5;{A=^6z}kKW_>@Lb2`GGZM!FHV4rTo zy9q#JnyDFLJ%e15j6&LNCq;I+=@a}&WJWj$jGXgI1Tm$ryJN@PT0D$Xp7K5w?J1mU zHX(hwmWM|4_!Uf8BmsB4ODdrL9vLkvnLy^CLBHSkTm*A+t zot=-yW}Cq@*n0oOY(urvH70+r>mdE*kG(ON^m|-p9QP<;m*8bE(0kVHdQ+oG1@D^L z?FyeX%`q-Dl8s?>?U=}Bem_~U`Q$fAMlOzN04p17Kg0Mf zY)Hq!XKld+i?Y+QFKEug@=V9a%~UVmD#!Y0kv)SqVm3)iCer1bW%J9~l;OBlKDslNGuEvxO#1jY-WSi|9e3$M{i~(ZRM^B+n%5 zYn$EIreUYy;N=HrNRkm+;s_c0MDywvFs{WEvZN6avex_4$hF>Zf?+ARWD2OR+K1J9 z)s)mAcV;cn+BXLnu_fu7yP7vwD<3*GZs5_0M0DViQdV>__hm6X{G73YFKX3~ah1GoVw8Vqz>4eo$D z5g%;%OelKC|9Ri_m&=3ngTGkoQ1&1DdCuvQYkH!@|3ha$JtX+$q66w(K+S*pX3xK~ zvCx*U7q1SW%*Gl6Q>%Y26OAI*TqD##q8Si=RYw<)iJ#N|5X6#ido&ITll2-*IX(nt zKTAObal-Ne1gWh{deEs_+G#vEY72cEQuaL+8)Jtu$q znmZVa(Z>MgK0O-{sEWAewzXRas7joA_brhNRWFxcFxF`Bol53|;8CX=Eh-YWx^dV_ zjyn*Q7qQ&NS&Cr@YN=s3UMS73K$7jag5|Z1YQcDygS?BqY6IvS*o^4+&I)Z)Hya0X zq_LIC$-Na1dJ=60%r2MIjwZH z?xDRHR1(%BK5W%yEvp>^;VE-se*4q;>*7wzp8Pc+G3GMy2DpxrkYR31;ugF8ccQXRO>TCR@r{wSdrI{MR(WQNsp zTjZIIYq@q3VGi93O}AdXHD1x2#H&hdt9Y+X0R$L~*#S5$j~`Fmrth=}dbWV?jhusp zBVcU`oX8Sjts#UOPe$|e=}ku;5lMZ16Ykog)YPb-JTHwqqa0hVy&Pzwnq8PIMoJ<-+%;*deJ)|MR~v!nBwsCsm^ZKJiFR^)!Q ziDM6nrFzEHCQ8!%=+3xLrK!t6m`3lfs&(QTi>?~QWZ8DmTF>B|Pc@amocSKw>)2_Y z@?m|=6lwVhI@;hliE?g8*Ba;Y(=FBCqcp%uc2b$0h~Eq zJUl@YNoTzeNqJ`2az8$p;=owX`GusEPC9j`UX^Zsr-vV!c4o)7cg4EeDXzWmFL7Tg!e;P!qW54AP(XhB ztQ`umEVI47uJrjddye1E+Ik`b^SJqZ=T$|sV+JI3O|{6Yp3G^cF_F1MnUG+wan%Tp z)O)c&%Cg}0o(-uTC%y3olOj<*0A_1l>U)$Sd}Ehm1qvu2ilQ@v@-392=2KKpAzVBl zZ>qD^`C7`s7l_d-DtvM?hdlq!KA$QkEg-UuNZ{_fRJjzR-<{$OK;b2^I@RK@Z59IOqHv4Qs_i|O8%r|&Q>h`vwR$N#~ivU2iTa? z@KzJ__IS#TtSb#j;-f$RB^WVP$y?FK6nWZ`OYIeJu`_e}tn;sce`v(9124VhzmeT{ zFu=eaF7#8mr^)egl2ip>g~ny2Cq%6+l{N~ImsYs7ov4==8eT5SW0>)@Ez13#Y>)!Z zmj!cEo=fea~LymplB{Lwgjb8Zh%XVR?^U;V^zTmZB=ceJxYI<=<+)L_G^x!Oio z0O;+E1QhnQ(#~W>e)>sR6-N8XM5YL=gt#S2@j`0#aLTYH20Wo2%osM=##!?gbx3L$ zBIA96A6y}A8c_*^fS39;4^Hibz|#`gIM$uxw|TaFVwr9MwzN?!p$e|j1@Qr0{0(7EN_z`bq7bgZM63!wWw-r zG&9i1JGZ_pr?ojW9DNUF{t1^VnK}VR1h@!5+X9t(7zUpTZaOeTL5B&2ENY3lK&|Q` z(#OK7`)RL$blNrW!w=-WlXY`&NQ%Nd$bjS_A2E8ZUcnEFAtt0!NibqI@-#4AElum< ze~g}7hJ5!{k-(Uy@tXErS&pm32Mc3q0~K$?IDHG$qhmAA->s#UVAM+Z2SO=|HTxoL z%5JuZj`~C!4>edA$ZoW3%(d6113iuKPeFjM7+j?7YW@e(&zkFce7KwjkDvMsAAFh^ z^E~F}&$9OY7sx+!l;0*tcPV}BFkK87DH(OzwZaVEfdI#b#2>^ZOTZhVZD^8&w_7eG z^J(Vl`8u&GKShT)T7N#3Son5~!X9UwG9KjuY;ZE07tKJ8MVzUAzT>Gy|OK&Me zX8YgQ^*Q5@9c2|Q@gmJ^v`eXR_x7znVy71lreWf)&3zYJHgeGz1R=MVsk?;;wFL?U zkp+exY(_~+w`5HG5%}*|u!%lvjNKAHnX7hi7F%`XT5$t#Uc|LH8EfPw1NEAs38AtG z_pEW}{Rbx06o?+#>-WhMXJ=$Rb`scR-7WZh0!O|X-LA%4Q zYa0-|HpdlEI^(5B*_$z$wz5Je(Nh3u<=wHPFa)(>+$r$+{sxL93U&I(Mx!yBMh9Jm zhZJ#+qpqhh6)+JNDR?GdZt}Avei8$(FUAEt^so}GQ;&y>w$y=2s!vk~EVlP|05|E^ zU-&|MwT@|0Do@5hnV|ThIhmmxEW>F}b0*V|C)9}DQil{h4|{#~ljzL1LzvP}sxU7@ z-|`;ce1l=Ib!R;((qb`BRe=(TMsP_vE(Fp~pq<~4Q=@H?SSq9phOFJ^ShE+cV_MP3 z6j0g?AB*+9+g-*Y+^+mBK_b4) zpst)MR5=tTvaN6mQpo~7&v#}6%-35%%HXS|6SBkXh9~K&%bQ$`d287I(;iAyLZBbN zwwMXP( zMjpDNW7tZy%)Q6|FYMJWZJQ!IoZbDQQz%Z_4FHLbTi05?RH7)xIyX?U!`wBoaB#O# zT(~2?KqhB@cUXK+#b#a}^>lka)gIZ-fo)QCvJfmPm0I3Dm9=g zV*0U;00AjX9sVU)Nc^6HxCA7AP~4n6#cp11&et7pdWh4gi(_FlH9j#e)eQBQC}J{P zsQbrDRL!E_p_e)peI>mKZ67t+r(4(p!&w+Wn0pJHsOpdJ8gNc{(~&pXN<4Kk(UBB* z!~4p6>}9ey^g>|O8tiFPY&$ziPJ}5B1#}Lyu5YOR7sTsagQx1KjM$OGtPC_y#B~YN zvFkn8R-s8RV4R=L_s3vA=!{aF`}rV{R(d2%sk|V&WEHsfIib2+5WU|cl-j#U6}Z)X zpgkb`P29Wu1mlV1xEEY$a0giogE!vDHc$7wSiBQbdD+cMb#3)U!AlbPo?;otIL{>Ey!wlBi$Now z!zcpDCt<9>RAfpQ(YB?W&r2J}DB#)dCi@*hVSHh&j}Cv@6b4Gb=c|+pi5Dul^*ZTNM;Lur4sE-tUGkr{kiY3-_@A*SX*mP*`aMGs0$Q7vj6w>BnpkY~Fq{Gg=o> z;SqR?69@ng52fX}dx7ufsm+LuX3&?7HQb#0nnX+u39#mIO%E~rL84RKGq-O{XP3*| zF9@-gF*_+n;uz^Err;Z{HM!m2TYiEV`;oQ@^ge(Vrn&2?F(PJ!kBLq}N}F^eK9E!` z8EjQM>_0nOO1Qy_sa#b=QdaKVSQiD)9q9R&#)-ulfwI9RU8WUr;?&lJy5BsoAH(Tm zH&*I(Gub8L&0e_j3v_Jny2&lzq+kK_Pekze0K3y$>uN5ft9Lywd0 zKD6Krkwu{Ep8CPFJSMzH7ywvp^=6q(bfbPf?we5Jk{xR7{j$kIV~awtn@|jT_E>sI zFo6}CM=%OkVb~|GY_xe`&e6%Eq|cHAUQ^OGX%L;bIf{6NeVo%=`8863qG)&s({h-Z zPlX7gF(l@xT9BJeeI2i3LRbqSGrN#Htmj;Xm4KaukFj7rS=v(Kx~I;xP)xrS|J(;k zIB9w|ENM9E-NfHi$a-GSW9==^t;$<<(oA?~`Dyr3my21he0(N{PT-hGV+!Zf+8q_l zP$B2^wY=Y8VF}hNz#43~FpNGBugW>gl)4A(AgbneE)oi=7DBb$P-hpa&T5Jvl%e2C z`9qBSrq#y=yZT*e>uA7P6Q0THRekbqMNGO`@#d{Us z_7nkbBJ@rwXrgm1StdXk-JD(97YnuY*5a_o>hw3aau}>nU`uYT7Gf!5Az?}=5#Ein zyx?&C0SI-kG0cs%gX?I2CNZBt)*B}r#i*Ws1&5o@12dTuwtJIvMJ>0<*Sv@Z>s>z- z!s(pfHbeHRhSgIPd&9YiSKBd8Q5?D2{u}mhE5Dv)DW~MLFxH6-d-HX5u`Z#lv-MXNLzEDIkfFV|oX2CNPQ@H(jB= z&q|T&&XD63=zH3I%0d#EgAEA4&~8AE%OwN>2+JN?x9lj|2&b=J%s9ArH@qKY!7FCq zrg$6c8W1R}1lw|ugnpC5^h?jB6K>`hw0%<#wda1_vrEAwHnDu2C$#)@$wr*VESv zyslY$@(|FWP`Dfc&~Ni6%c$+1LS!OXc3!QhA}N0WGAmn{KtqB^zj`pH=iNX-c`lFX z40vLWfgE*+Fi9zZ{cFl+mroD z1N2>Y;>RyeQ|!{?n}=pAlNAalJOmy6mXs&i_R>b4D5-?YgrmJKS_f2<+nKCHhF+~O zUD}p2M-xKOh0ttAh0Xbz1(@sHka03*8$ROJo$6CV&>4F9mPlN)2xXr7WVZruKpo$h zme*J9wpVM@?{f*oeE8CaeU^w*NE7S+c0aHqC3NL9IUMaP$1RW@sKv|PFZ48N7h28q zyhQ4=U5dj~1hY8=B7M)H)8JjtJMD4p&CkdDK<(16PzaMxkTGaJV1+~#P0IH>jgy{c zkp%VjTt$ylc8RoGzPfJK^Od8k!nU&Q?VM4GaC+n_RpBp`;(l&5TEZsqc^)2&&70XP|S6EK8}`? zBp3gk!0eWkH2IMw7IJ?5$j~9B%g%Okp96VHjr|^1s9eQQnMBWAUJ%qH%)9IpB{5O2 zJMFghNLxM{x&K8WGdS?P^jKSvod4Z{MY=8UT)bBVo*_On=(ZVR?zVf49OGh}=_|L> z&OVaLh~rDrrN)G$*>8tg7${ zc?N@6-mdN!UCCS`|?dM8{?Ie;9}0>-2dLn0P_mnNK!K*p(p_2 zZlefYCfmWD6ILOk7fAw(DCbSZ2^xu3c`Gigd~Zw=#ZjJXd?t-8=CVKkQ?wGe-Im9k zLg??@-`$SI_S9tLgF!=uZhaoB>v^~fzg2=KwPEE2V+0zO6m~X&klz$Ih&26h;odUc z$>Qj1@wI1DmoMYs!wW+M&!pyIe5Gcut`iDyCqC`M>{l+RTy0cfSbG!R$DaAqhvA?- z4jiP)js?GK#K9n8@&dSFJ#v^J~Wrf)MfNV`6|RHlFBISKhX7P2QWh3M0zUrC_&X z^3g<3xIbzH;xU@>YumU|=(t949PU5K-8#ls><5akDx2p(Gs=#+6`J9)p*%T^gVl`J z49t0CyCKPQK^pVhQsYm6?#CncmD&4;iBgVi>2E)!R#%Z;z zzH&?Odepn_LoVca(&$Bp@h51Ou*5X^mJX1M`30GUZCC^=aQ2eFzF)J(Ir146Y#=}c znI@`I4^^y<)@?>}=cj@fW5} zK{EQQj) z8es}ucsp;S3LxGho&w1a>CG&Ju^!|!wLLIx_YTju)ZyBk!ItHH$!?HTkoXYzyQACx zu;sbE6eo*-=~b=9fwK@@8W@~%jVaXxmUbV{Y}7v=kd;uYwGw1>1EIr;v8vT1Y>)!~ z(jEqZ=xn6$!}<--64I1k7Wvd>CTNIyL0v)0j~ep8#Q; zFkmV(QYWZUdDtov(~m`Sfl6qPNV=WF?mkUS{2L-bx{S^E*2wdaQ5;yRKu9Bl?(hbe z2#|QkUZk?jE*qM#vFomyONSAr&mLxKjDMw{Svs- zM3ODP4RXlbr5h5Xgs^eGEDDI@g9pALX~C1@&pXD(uZSfL1j*C=>xkAoE8KrBvBu<5 z6OuXr{G9(PH<5+4%W``ipb187}kb>HXQnOH({t= zIt6!XIpK79+TpbE+Gz~B(}=s1rCzBW*VjQXc7XTJaz2?h;K(e(9mhclCE+3Dy=Bj` zT}0cTI9SlbBKzk6mk-9bC(Hf)f8tpT$-qeHOiCqmmrP@Pf2L<9S|;XRbTnt$6K6mV|J&2@N8`I|)n zJGyMhp;!!7;r%5YYFsaWJ{|YqmXi$ZeJWe{7RyZ^@{Q-oLbgp8uxyEz0s zBu(pnpHVde|QGqJzmi0FIDe8=MsPeFud#;OO-c5^sK;R2Khcq|LNI|@;y z;|D^Fs1_%Dec32y9b{@ZfV z6<-Ez-~K@(om(DP)k`d4q2+A&1{|K+gafDF+eAm*qtgA0TN&zxq{9JN>m6zGbVfW$_JISD2T#-q% zYD~~j$5p|ExpOPj@!wmCPH}t}6rdD0D%IO3x#Ne5)|luHaJe~ZURQqNp%o->DC&{J z%)E@gn&O;D{qoVZ0?lS!9jBd2q69b{RV_)AXm&@>#MsT8)EKBykA|_Q{Ai4g5aks6 zS2Abdjh-)BOH-83{o-uG^0GiXTL%Z>6JmNMfXiIos7x@z>SkG-AjS9e;2WvT=52XV z6wf~Fh>v2cVfVU$ix2Se%`eMzZn#ZSqW(!<1O9)&ILC5eB~xO%+k#&KAG54dni!KZ z#I-;@I2iul3aYRYr}R&{5%sOFXSJDLLN1Zb%uBMn2)zlaTT+qR9u=zcZ7wlRQFSjOAG@>Jy=Pt7WFJi!9E6%-mqwNjAC~cDp?mE8ZAJR3W=W9{0C)nq{Yw z<%0d*T}F;K47nw~;4>J+k&;hrWn|_#vEci5YZfIla|h|3GWC%Hp?uym>zAohcg^0#bG!R0cy5JHyyZs_P_(bqRqQ*Y%#%)=wKu+ z^|BW=T)JR+@yz*ze+NO4oIFiBkw-my9KDX=>_0&WVDt|yi`-I)bP#L0*D)K|9wuL z``gJz4s{sBMho(E!3!f2u*xZIg&-?t2d_?37s=zIjZaqrw|Xn&)JYB%XWUv5uz zpzh??4a-@fRiS&~=0rg}D2CeIk|@%1Nq0#|kVwBfG7;gMAGW$QaWAyinLDG3^lb1! z>sG$ak8Gh1DoULIBs^3|+9MJ6Ejiv3w9YH!_N*r)e&1-uG3u_TvtZ^+*cgkw+RT4B(iW)b29$)5~kuyJy)v_&% z2w?r1Bih+E66NC4b%V05{%{LGMUXmw3Q`wnxR4yEb~rdi9P&6l zEkn%hb8%8`$^XQ>#$KA+J40~6pQv$(z`fdMOoV??p0aDcJ*@M0>!-91H@?oL1`C1` zd?&$l7*3O)rf6Pv2^ngM=UXP$CNGkzi*OwydeL>^&HezBph=<*1DZ6|J~0?>ontYim^OzVZ?+TBpow_Y>rKiE&la7+ZN+I;jrP z>TY9@qW#MSUQ3BLx=sX93FsO!YI9fyC}XkHTLRTfbU{8D(UMekQ0}oQ;lC@u1kKD0 zO&PLy5HE<`Tl(Dgob@+$Oc?`6ZAkgB;Js@7mR+AjP9(tAwdiL|3J4ztkmrn~CUHJi8; zMBTld{J@@Q*D+vnR%&czgf+lj8Y>{+N?cMrYW;RG&oP0N(}`g0Mypn0VL@S|aB{WQ zM6wujnRHdm1(M(g#Z0>ngzN34DAlD;KiR3{tP`j9P&H~r{7Iy2!|aGx+HR2b0Vn0v z+HD=hssfprBpp^8>8yz}$}0C(KC69rDvx{a)3s7*od31EQ8+ft#`2@n0%G(NnC=D@ zU9V%hqO32^7uQ|TcfqKn)?Q=02-qI#l zDq8Eg9iS*oc`YiajtB>=ngx^;pkuKaBi^d`lTy@x9*)y zV5|$n(x>gPo5p9aYJV=x$WgfJMvG*VzB8U|rC%7Ot~D~+yY6Tu4n@~2(Jz@Rf8BmT3zFGCYm()m<4}>E+MKgy)rzWPGKN`1ulT>O@4+&IIrR?xw)6L$l7* zUbpo-HQEwP4;!?{wdaT#9dA+i^@9U)9MQ%4?`)eF~8F{X1&y zc}2|9wwBPj0}Ig|Rty8+?_3j|hRmPQpOy>$bE*{%G7Yf5&CL;c^b4Ffc~YXw!D0QMTbIujaKBDf@vn64N7;8{N1HBiam(yJ~XuRfqci4;X$XX5i09k zKyn}KpI57YQ19`W)zHi>dEfxjk<6ZMlXMO%okC+1Ev{q^Bdx^MFT8%(YrB`QOqRqq zIQ+6(T;_*{>_rT>LY!7N;JyW)V#mQBPWX*J3@&A8rY%tH+5XWDD@#Sj^E>qFgP@77 zvkfV;ht#pyF`WMy4S?eo;ZgeS9*4HtRIjT?OF3EzDp^eb9bat~ZQ(-c&pS_q>)8fg zYwFD*kPh1MBofMhp{xb`)S2xEGZ|Qq;zy|6*G$JlI5veh%|=Z664yBcEYxj`K@ zQuurhjnM1gnK{CfA(zA0>E?>lWr&`=QYd(IKA-RfKV=h>eIvx;T6Zd8suu{fxlvL~ zca^4zm(2!FrIcQmVF@6acD9GS_*>tv(?iWQBOL*ticPq&a0O^Tg{QzOPG>LsLzR^= z0d^%%<1D*V))#neuAeHcjGA?lVx#$tF^K<>fPsM zZuVUA^h~{C5i}3x#_>Q2#XQfG7#N!Hk@Di%7ngWUNAF4PkTS3WA z34R;Bfm{12JF^;yjG>W0dl6wkcsn#M9LlBmrJ08(~$ghwpFO-5Iqikad)L?OitMF033Uziv`qjVc*9F&p<$HKps z_FqF!>Cm)P@VGbhYz$-~7)AA9>GEo0pL0A5c7B%5&|6BIqnR9*S1|EI{wAuy#fz82 zy;24i3|ZWkrIQmG5IZfB_pYY4qh*44HwNc9rObj8@sZI8-(nAsz<3w)gZ7^CJxOH} z^kr9PV8~Q5B4Q+*D^9!=96JDiIt_+<5;4=A8NZKoF(gNHt{pEyLhGW2@)nh)lrc|m zuMu5uDR)0g3X9*s^sZpbR_vbW8ABkCO- z>4Z}08O%FH>J%>7^TMJBtIg??_l;#1@&Lz-+CWvs00{ci@S1PZX@04$o!F!W z$i-6_`f)K}^fi+jiwS+<<^qS@Bt!=aduH`RPWGoj-=Bm+(|>Z1+wmaNVDTO*@TA$K zSgqTHeXHDNYx}dDG=ER7ZE7$65%GJxvs~VPEQZqO9Ep)aC#mQQ7z!j|>~MWJS4a2n zXkHXfj)-v@hmqQ8>ktif01x$zyykBlN>G5-`KgDLX4`?Gu#sx~~&m}~ZICBd!i({gBDF7DlpOX2Qy%==+d zUx^@ByPfkwa>!z+GH{<#Yn=~t!rS8|=!yR0sMMnbd*W04D6?9v_U+x>5KfBe=iBaT zW0w;+-LakK$l9SO+CyrJ>p*m%RfzRmS{Hj$>>kO1PVVeCTj1pbV24JRcI)-SSYf_4 z&)emC$QpbfyAWA{o!{y|9Ospl6qi~Z@OI^Ah7ND2-kH>-^>~Q_nv}zHguhF~FO|h> zdGI}Et4l6jSES-?S5Z&d#p)}3pQBTE^+Fi6wvuxSI9smvD7%UEkOSBKkfIAHKo*`X zK?d1o~=+A)3fRrjeyYt8ol)U0?P0(?%?~sj8W=HB5ge&|oj+Kb= z*9><-TYW4EBG~T70~pcgF!-m|8OOx-N=6U zfqwolLkR5jTo#cLu2? zu&1a$M^ExGCT2;>S2*qfNiT`SQ1tVxhO#-yT`p^0CE)Q+A7lh@e*3fgq?p-m`ASimot2`j#}sI@Wj%N(+c}J3cnEwC3Arp z5ma{Fyn{&PM?<2w(zIHb(+PNxFc(#BmBW=v_lHRm0Py#J^}lliekzq701vPL08jwZfPeu1zt{n=|2xVHNhr$0^Zx_*uLJ(C z_xNx9&;CD!dk+B6Zx0ASrXJT;=cT^rqz%p}J;5jh%m9r@1rm5zoFAP6ijQE=XiQkG zO-z6fL0Lqro??jI505W?AHw6~VP#H3TN$8wW7*Z*Vm<+i4o?bHJ_srvP=&zKdo&)+ zF9?s6xA*%*59-y`XXoX0`1bVFi?^$`>$X=&*A-?(Y}0!Fc`<~(r16j$;5Kq9&dkpr-2k47S#&vM|wc@ox)+ulc-gfjK z8)VNn=QGllfyL+VbCB;`R`$23G3(LvAIfz zgIUR&YT&a#{j}fGYS^8{lH5x2i#-Er-#pC0V1KSv(et%Y`sTakqITib*Zkv0V1K8G z;Y+Nh#wW8*;VOBR z+yNQ$@nUwzrwN0V=(NgTO8A92-tkAJnr}?cbFVWEX zC(15lcT{PyNv%wejyRheN8-~*Y(nRWX`{UO^@)3X>y;+madUokh+`x)O>6ig5LA;H ztoGLe&n!|@xatsQl~^ zFkLsb+#Vx*Z|*bTACk)J<rn?G*wyqm-eJZ7(bx9g*Nn=v0Ict8B zosf52CP!~h^_OYWS>{}7T<(U9Um?N=&YO8*?uPs9#%FF>1$4J%a?bXKE=$Yp)ly5! zGmk?2m}3vlNmAW-b{>dvF3i4Q#})Adx2zR)Ci$v?{I+9mfFblYVA+&w4*FUl`4ZZ; z{*Zk2N`R_G;^0{5R+hW%&XaxJneXv3T#jsGYr<0r%6taIyYIrBJg*}sWCuGX-M^-u z=o3Fek~f@U?>!zeD^m2{QC%vx;c>`U7583=hOAy$D=!Uh>3^$o{2$#8jd8hn zt&Z`tDIJ$v{wdV|IF`cZ!bvhdF9H}-b$D7glsyuBrVmp?<7uw#>qFCTV_3g6!SJ@uY_kA zu{xOuDU+dVZ{7U_VBqIoLHk)YI@iYG{e7V7_uBrxOFTYV?X2@RdvZ2_(wn%DF|`P{ zfA~Q(d$g|(Ed>BXv5v)Z(rf7MH0u_YLm-I&AwE2InMkxAHu@hblUJ5w>x-WUI@P*jP;pJIXb@DasLa202gdS>hXvw{ENIiEM1)N0*~Z@fcC$-WsHq zpK-$e+al1ec~k4=EZs887Ks;n9U+ofD1ggqchqAfC||4fq=bDL?Xti>JKwhW^0dE^ z!+EEkR>dBAmtZ1Msf^+nJ%)s*pK}3{wP&NQ-GP`Xby!rsc#I0T$|C_V5~*|-U)ICD zvrSsV&C%`0qOcAP-tUsEx}XZFi$2FdloBIP*(&`bYmP^`)tRepL!J|KY!K5!~RJ7rj-DMPpnIkhca|PmaaSKB8sa{VQdCn$akD{@KH*LLzD6zn72aLm=--k zPUqSo6j+`ZFt1R5^UE#9uLwM4vhnm0%;Sa8zt&sZ1X&2XjS3oz4* zm%ZsAR(q%8hGN7qQLo1f4S$T*N+*l1;)Kw}nYrk(&wmk(s(`p^Ty{695Jy;=HEn6K zMRs`(SPSA3+Z@YYw?8qH?;joKMM%$SI9t7*-ucI25EsPkN1!{t`w7O6DNL8_YK*+5 zD$cxGTy;=~MMzYRIRSC)*2%twi3(O4F3>=EABuVaZ*Onp)qq5h>A8AyO8IZy9d0$GYoK7~(4m`iL6abz%Ll1v?J zHNckFGS6_<`l}5K8i+1%dc#_cPXJfL?&%E;f?HN zTT;`cXbuo-Fz(xe#iohIB%X$&SfZ_V5AYcw3<@2w^QjG#44B{h{tMn}m${!Sw{Qy( zJYFPqS`bZbuR#M~(v zvFm^+MP(WTiZF%CJ^%jzQ$Vc0cxe5Ov17ByGmg4s|&xGt(iQ>)3XEm|?XY z#zQ8)NxUR_69ur~+REvM!JAx^lX0d#Md}}}&ZQyty4 zibgq@mof}t;PBam41D13Fh;c6F)=M@?_wSy;PM{b9g3!g$+SF(=jf~ zbL5jJnmLbt{pD$I82q9Fm9$Qj5JS+sHbv32QzoWBeea9=z#~AUkR6SYm=Vu!ucRp5 zqb>rXMtJ=EB~vVFxBtJK@r#^FsH0jo_51~8;DZ^F%3Vsosx{xO_8<^}=*BOFvMx7@K=&=ebb`(qnk(SY86j$ci%y*)B{%(YP=emY-?<$nav#Rw} zk2C_Z>uFx7?!S&$7PAa|KHcAVHO#p!cYb`6$qFJAIPc$tpo<3Rq)P+D^c$smzXm1K z&_tx_=qyT$+qgH1t9Kz0@pupS{)Upv?)o~ z4P_`9UQXG^_wI+g*1hmH!H>1qrShz__C?CFr@yiF43k?mKu$X>bGY2Fv_?_HRvleuMYvy-{lcSfgZR6z;2ox%dM%CB(+~v?2o!gc4X-tR>3}SfjET@ z=P+M4W96S%nE?&>#$E)(D0>3K&pc<#_0m?chgh@GqsD@Xp2{_s5KU6PqP9$#Yj(buC|Gq=NicmGMcd5}lw0(1!KQ6a%`5tb zK0bt8=kA0lT1V3a`{Td6JzVCWff7{msKMfd_yk}YFX}{_t>uw~vyu?C%$oI_tJ&Sj zEl51uO7E2?o8?L7+8c0DftNX?9HUUx%pbeEK6-M)?&M}P`;}!E{hI&`yV&;~O7Jo0 z>%Vej{Tt?ns7Hw8^kS05+4Mht@!fw=O3PU2Zn966#m_->5%U9%qjjE=V*Isa%8?|P zlv=WAN@MNmaGXz?H_AoAvSCP)%lY+X7G`E(-t66>t`_CL&p5DtN@qqXCqG8B5Yv#4 zM|L-w3?P)}(NKaqcgfRD+ctpeP6Nf>xiCU1XQ z!J4T%%}2sC4*iRIMq*4Jj%3^RY1^k!*|E1iqr4-BwZr*&vG%p@6F(|l=k{X?95in% zvlh9+9*ya|jX_n_Tl8>8$eg{7mUn<5isOo7yq}QQol;C)%);N_O)YsK^&S(ItS-u~ zL`N}Sl~RQ*IT;+IWcrgftt1&=s9+YJ(4wpR&3o9sHNhe%%wHmee-Nict-s?;qu!dZFq!_d7KD)we*PM4@GjAc$t5xH0U0*D;a`QO5Zp_z z0SM~T&E_o^De<^>jQE_!klQC+bV|N{kVNci{CLuzCglDjufY&^BoO;g#%+$lsb4)Y z*`HIcN(E~o!Shnp`vO0Ptk^|@T$+u!Mx*j#!PTcPQc^h?dLhBZukFXmach0z#jo!Z zi<^&%+h}Fqrv`#P4R$oUZ2Y8IBKxo8D(v#>`9-4*y6Ux{h5JMuD6SL&%h(VtWrj#l zZP*I5cXiQI-w#RKI_@+3y7wei zyrPv&m2c-*k%ZE*Q}&}lCLHS$PtPn#Ag&0KZ&jO)lX`WDCJiXWq*+B2{1+&zfb9dX zIq>UUA0dWB4|thwf@}}WI9m@SnjN0S^XOa>zk8!3^_(#Ntf;WgZ5xpW$f1I|$!Dy8 zojGYTQO<{wdWVpahxv?Ut1yXO`scxV+1z{*0Uhx2t|xsM^J zm{{(ag&o;gE_79#TJ6%8JrGRG{rJM`7)PYt8Y?qYKA)isFBO?Pj z76yc*SYIoT4lN=lN5MrP?hloOy55mRRxmOI1nRc~)p{ILH13a*n$&HzD$HKe|TmcXgGRq{FpCyJU;^7M3@a{%EP<7SN z)WSoH@ljM-%otmP)6O$U@bvg$A*Oadx92Tu_11LUwWSSYRf*gl-W0rjapUkO#=v;^ zc+p7BVt7`YttV;DFOn783NYHBXm_dW{HyCxU_Z)LNkWej)ivEBUM~oXs$y6Ws4cd` ze}ebvHfkdt_WDu>7X<{0>D{T%d$WI7XS& zc|iLR-p;YeJ!J;~^LdGoS0k0KfmZWV&^(R#$~|G<@KO>p6662ym_bmU6Yzqs;GO^b zt`KDz)J5ID8E=!$}U9k>Pr8oPXU?E+Y&N zgL0Cu+Y|7WmL2QT3srXSW8C#3O=H2BN9z5g{%M|eA`b>-8bsnvC@D3b56`IE z9xnJ7u|Sk`Xt{p0evw-w4MMn2wn! zw{C+fifpix)`^y~4auhooi!!XNSeE9=|-fwrWDD-%Q;_Q{H%W94<-c;!lhv|6y!O+ zxvA5Q@dyyPT-3td|7QU5Yr>LaY)cfG77Dk7?*SYvs)}k`^zattyotptxN+?!Zw>E@ zFK#Z$%p9U~2a!@ya1~u^FSoHvAv$d#ASj|_S~)CYaOGp+l?6K~R!M+2;b@&>d5m^SbNu@j+ge#&-)^!OqMSbD4)@T60qNw|$GJm!R>2tJ@;04<1;mYiR_xzh4yck`j4v4{%ZJ7W3o> zDh~Cvz~OP#o7H;9dl^_z@IXhBOyV{_KorT|N*ux`@Z^u^X}0`-?^Sk9p<88Z*xoKZ@AslFmX zkIb7C(K78T#m35TUcXwh?L}ooxAeia+Pyua{-5X}c&&yDj|wEwb~0!!?Yayv3}#U8lZhYbs!NuuoT z18&8U)8(9=1{lzM)$2E@gt(9vE?W;m>>VuhtgR9#G1bE)X&3)0peNj{gs z=}vF0JMJjrq0|r<*4OIWmefTd54TRQQK zB<+QfIB^s2tmtlZ!*a||Ha^=**4s#(H;C6;zC#Di=pnz&-WC0%SU^e}_u;NIjP?=w`|^7BbM52%^LE5f{L!DPPiw^6C$$Da znCody%ugYl`=Na%o6Rdn1X8&yUsF-8o4rd7_3K)_jZz;x4;ay5w&^t3QS}egXn!26 zD<9}c3r*;Zu4+tvTe~&Ie=X1Ls{ z?WM5J$+Mt%B)`A4T}SY(H*-2wru#yR`^kp$x+ZgLma{0}b#fztc{{)xZp;3*+Vu4F zdggzbbgfwS9kKs80%+R_j-sc@ECKuLEgI57wntW#dm^Z1W}1Y=0+m2y#0j-qv*l?S4v&uFb0bls?Z54jb)?U!Jit!_o+2T7+*a*Z#J zn&uAsV{K=yGxA2?)WvU=NadK#ZCjH!*MjyDQ)3L^g5@5dH(Q%Z+-8|xiQdK(p3{Wo z-o}bbTik7I$IwE)O!w5W546YCIi!&;Pcl5YEdH*2V;{cHWy}(@*dzK)o-TImO3b%k=Y?FhiEr*ZNPPwa+K4r~rgkR|VLG>^F0^i2j6w()n z$!C(|K|r{ayJMY7$lsQbkj^zEl`MqrSTCMb;UbB)c`^%r=UX=D0t%0`y)qDmnlF8 zdbZ$R?@Ys2%syq#)0W)zj*W!pz(rwMt>ap4UbXjV*U7w;CFJCg6uB^o_SQ1wLO4jC z&Xo%njgpR)q5^q%sr7J0MDIt4yJMHk6*@q=+wGTc-xR2&^PZbOY}^TW6W{_S`ju&D zo5OkKXv8)S?VZ^7`IfYF$H3J$a#SOHE5F1e-zyoXd`Cz^-p)XkuWZj_4?8?fX}giP#z)({o!sNIH+km(>P`Ghz3JN8wri&7Yuv}zx>_riHJuFB z-yT&AYDQp=nb-V5$mmNhZSa5zEdc4*c4D7xB}7ll6Lht!rYyPb-de+kqHCnBX681-$Vuo0t$N+*O@r+8#ZE?G z7Q}x(n5aHgqxg2@;oQV+T+u`#HAG91q9~mr*RtWQ7lIc){K)IE#=V@I=Y(i>wD+?A!66iA}(WOBCm@#CYK9A~T178}ur zv%jSkuMz!AG60Q4aM8UX=p@i_W#H?0-qdg49I9Np^>~87{+YU%(COHl!Tyzyjqo6SD0nt2KH=!XOj^n^Dhn zgHa7=no*p8-I3!phlWlYq~X$^S*J{->fp~>QOYmfNwZ4|8trPn+Vr}lo*>ipYjr9l zJHUdL;s38gL(g3O;kQSG^wigLtgUABtFytno`ZI;a4YG{ z*YY&7cCvk343}^dfIx~Qp9vL+(h`5Ri#KZ4eEf{}O=r62XO<~Y{A1H=QW0W8ixL~# zNT&9W?Jz=)g}}A;RG+^Fg7yEHnhH@EC=pcWEeO8yz9GrHH;C0&lF}y+qivCN z*<#`%rvWwLAWa(e8_&*4?Hk7|4UsSmO-U$@5@uc<96+Sr9tkPj-gl72Ne^6^6f4`J zXE;PzJrJQPk}tE;`GAYhjlORLm3uy@v*gkv3|*k?4 zBJYT!ZSd@^lI30*G*_h#PP<2SRFF~0wYNm%>2WlRA035foyN51xhBYyplatD)yFk$ znCa}5#-}3%BO}l(j%yAqD?IYxKL(gng(np=AiGOV+Dl_vaw}8D1`sPMIIou?Gi}F( z{`^Ie^m535TZWnu5gmfu5?>-SV{MYt)HI|0WGbm3$Vf=+T3F&P&g=E+CyuyBP9}|w zsvf+BlJ>WHeL-(^SBfQ--U|9RpriZ(yHfe-q2F!Y7H`lt#VFHq`fPrDS)2Ye?2JKzdDGC%xg&#r~`bK1c!oQ zxJ1zbZNe~k7Ms%>#x640t0bDx^5@7r!{i-Fq&YHvS|l<`yT>JJGxD|&{O&gij9ghM z3El1^ho?#L!pPP1XE|!;kA_VJq6vC#YgM1*376q|9Vi&$GMw2LhL2iDg*7n4ni2Tc zNy~f=LOQm*<`1e&7xxU`%A3MmFB%sphz~u4!XW8Jf#b=$*{F`U%TDdPGHh`Vj;-v} zSG^QXr2Kc9dv$yR3&xK2dr;(*I8L0Z1`IV@>Gvwn<2}V^g)a`ZZplcPYTMcPok>x; zY-|OobDlfO+upHP6}P=aM-R;>sokDTT=o6qr`OK1#J@jOEwUwEqq8y5G!cJUFOcGc zSGm0@BVE_<8zmI-g!o;o?VWwQy*}WiS;mVXzS%H3L-6FkAkgj2q~&0r*jQ zo=73(F)Lrn$Aq@s%NFO=anioZ%M!x58eFOMS8ZWn8ejhV#*WB*u<)+YtIP|}5UhXD z1|%`oEwZ{wWr*<;;2PL{QFyPBa%H2Xg&?Tr>8PnvfSM6K6T@X6JrAe#UnU=2Bk7A7 z$n@4vLo`+atSK@rihRsr=wA=4c|>FnjKVt&0*XH>??Y=v(AvxlKJfH2BkMOGSwx9s z>jWh~O0ExB4QvA@@@>cr zbatz*%p>^c!V@}tJ=>?R{kigJt9+(BzMR6P0v2J^SPwmvw3+Kz6CCE7%{74fJ5XZ4 z#5I9A76vP~7z@(93}{AzRG8t6F@Iy?z<&Ii+nAt2v*pu6*pH)TGl0_eBGg&4)n_8P zN~jJTo3_x9;Ak2}y_dxqkeRspbohud)+lD=dp~YvlMS!ZuTpp;Frkzi$3pW7EAGiL zwbM5@DIcD)L#0}ZxT71c+DDDY1Q~1q7l^ZzO)t;l{V_RT(@yWs<+MNj zR%cDB>T8qdaRHB=`5a&>;duoiLiA0IxHTUI6whPV6r&cL(*>g#vCWG8pvw&hKII^6&HWpwtqTg3 z&0qHFT5<|cE<1#lmSGaVwICs zsw^1jmrCMC#b#{n{egCM6@7Yhut#cGT2iuCF1CzZBomFX*-)`>ud~Vu=i*{GH)6wo z3wS0>uH{$^HD-p;+lIDsg|mt86}9?DD}4?`VoZN&0_Bb{8<^d&kLJdpymAHOoG%{4 zylYFxtsXJ1cO<|l-WK6bfu$3eFGs`4o!NzmELULBkau3_47&7+aCkM3&g)POdh{BG z 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

    Uv4_FxmLx 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 Zlp$KD@5hu2{8S;oh5G#e^MCpR{{sWq-mm}w literal 195584 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

    u@Pd5qBM_3>iP_ZCa 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

  1. p$f)DmcR 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%0D>FwW+hAspndKi}<_= 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 I!T-POe}HM6UH||9 diff --git a/qemu/pc-bios/efi-pcnet.rom b/qemu/pc-bios/efi-pcnet.rom index a61f586eeb8beec38d06e35edf1b6d9154fa8849..6f19723751d6aa4bd0cc2622ecb3710ee2f24cf5 100644 GIT binary patch literal 195584 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�V#6=O90_lyWSiqw<=oI%lO0Yg*6Zl^9 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 z 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 literal 195584 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

    u1c#6_lSKm+b7IDyt$RQnPsh8k?NW#SjRU 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

    0p6=`jK>HUNRB12e{7@1W5jpp1(C);F9Fg0& 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

    LlPbwl++DK;bOdO`j|=@WQtvCtp$U z_^HX$*67jzp~CmeUz2-*+`zpx=ob<((*_XSr^4Mp4LatplORdHhV zKjKCCIEY|P2DZm|#G#qzf`!eex_acnkZ)eI7L+tqHs#m?3N6to=Lj^jW)Rq!V~k=D zuF5q3OggtzsNvd%JQDb0IOyPnLxz=+xOY1SIhq*iZ+nDX zglY>Fl!EO34abJ*q!XxdPouj+gg;sbzE)lgph?PwZ~7TT7vMv%j1F# z6fT*y9XkERYYkp6(Lb5d8cYK%dQ>??s`5>0utcW0tQBaB2A_JpY?F(PL2`!+94dP5 zw)vy}ujzp@iQ6=L=4StVo^D5hIMkW)mc`_j!%viVo6%2~LZ>X%t+}FbZ7Rv;m--Cf{+UO|PSScU2_+YuPmpQ7zlCW=@qLDrHN$JzvWc6NTu5QUXG~x~Q zZ@rr}&Ir)0dV&Sfu&i#&AKbf2(~DyrR7;jR1jr!%crx)r_?X4bD(<3|45)Kq>Scp$ zhH++AtHsn_H=)7^&=35)P=I&S)rCEm@c%YuE7c%rJ~b+=);!c{W0`FPRxTxA@`a_A z5W;6D>8YS*+V2R{Tzv|z)uDkUvC(UJ>_p@YZK%=)tzpz*pv$i*;l3gW%VDltHrdIh zVc>ecerO)4zji%fE@iAN)-3jQX82*@Q(;8)chR|kR%tYAuOgA9nv|rMvPquWb0DYkB3R#HeLiseH`9C|S79P#!iV0I z&s|2z+!SzV4-?3rD0b&57VFRpA{gUp}ab^ll7}o z5a+a^?fhs|Bkoks3_3b5OUZ)hxpYzZ?0N!HTT=`rq3f6M4|kKw4HM*ZQuW-H7{9OF zlox-c`Na*cH$)0amQauVZjm|)=MlbrXYQojO=rvg4ERMVnon6 z$@~pArvBM}r!2wnOMA6(NKQCYcEn9_|JT4LZs zsVJ;%ZBot-CE{7Za}}H(`a&%S)lCNY@YwQv?~kRxC72P*$KfIO1fUl&9s*$j*Rh79 zUrV+Cc7Zhz{oWYH+n=Y51$ zeX(QivLlT{hxt#Y95fU6P~pOqzvb~4P~mg_A;T3`!Gb6Ev2%w9eaUZ)C@8 zs`@7=^cjk8-8H4Xa}p_oGDC!XiN%gf^`#V0vl}Q9V8{2L>e1_sCCcT>tWpyM_CY|L8Qg zR2R#Q;=MeDaXV5JfUS5Bn5=4}NnGBQZ(t-zv_0;(p^O7-FM zQ+nHF+cw!~?ILehv&*NMEmXa(fBLCeHB$G=rKYrrUH+e*={idq$ zvh6N|{rROZlvI_EptOm+O^;z87@3f%vyjyQJnAar8w4Evv8qc)wW=w(^Nzm@oUqge zt|^kVwDyrWcUWk3OYSYQEFtj{1l1*Hf|L?@>jKiWQzDT$tZ4NK8)qx%O zp4kO!z5|Kna|;nPPoW5bVfaGBGf%JFqtzBDSkQ4iIy0VGf$pR{*sVv>>W(&EX-%C- z=S}SuLXqY@kr|&2J#UH+4mZ9EJqn(1XG&9HY+NV3>g~mQujZ4qFRRD?$t*--yYpga z{jG}E4!U-u?#G!%BJgg6mauQW*{9N%y77 z)iwjX3sP9a?=H~ga%%XSg==_C)rRu*7CEFL<}4kgh~RzF%G8}O5J<+`kl;~4^a6~=M}_7%r<1=~r3 zIw(MC{-~Tdo>W;<+9EW(l^htH<{-7<=5-2<^~B+^;wiAHaXq+qWug6w)Nb7>9(;|x zsk8DNJGg_#Q{B)6tY{!&02t$7*t3&zNdJ+jSi!!*;qzF6GMm{aj_dRTH`86l&O6`e zT(xp}rE+O4B75KE#8=8}JINP|bb&loTjVUoxaRAht-E}+q)D>^gMTnBSgXG7WluRG ze1|R9a}S1@DL{t(yiHyBmbn*mOP~C)Ku}2|uirX;ke7v~{o{%D!re`W&^M!-u6+*; zfUV=X&8_E6e*@AVvWhc`wVUb7P*-ljS@F-Bo9p;~L}TTx>+Zqm?o;W@lzBhIsa+N~ z;pJB?pxwqIn8A=#=$weK*!8EpmeFD1!BW|Yj`)ivr{_LCV-lK%)TrI>iIW3mkc?GT zB1%UEcFmIK4I%WZ)9S6H5{RpEkC;$w9_JFNnKw%gua+eGKlHZ>t&$YrSzyI7+92Kq z4Jeyh^=@akgxbvr4aF+kx$WXTE+ShMGZ@2f^PKcZL4mbGUtUs_`_v09|LI?nNAYET zG*!yDJV?&~Z>!8I@1+N?4Gua-=#DdHjb+#eoGIdHgOfDmuND*3?9vtP_&^Qy!+>fJw~fKu{UzMXu>&wt zAIhEu<3^M`DhVrOWQDpO-!2ptv3Ch)I8Q`c+H`!o(SMq0x>8Jt24g7TtY(ar6aFJj zDgzN})E}p~k!@X)g+ZeqvB#u(pkE6&&kw5Y^JDYxdfT?mzkffWowLk#%(ELUlO8I~{2+|xF&}qoPtez| zMCJ3gA1U3i`Q5AK9@>wthI42|Txa4(J3J$6$yU-%4-RW=bh+T-={KdXKBH~OKhMf_i$<`?_j{c{RORWY& zRj@UG1i`{EQ0PC0AmnGk$(#xpiA8qz>Ay@IP^|D97eX1j`rK znx31%Ynj$yV#Cmb!HpkC9l$@QZ2fL|Kh66)QyWzn16rEBkfgl$DyBD9Q9YCSR7`HU z(FmMn7cU^kpY*A!@yK6GlIoA4%{WS%Vi$0&WEHKoj#e{({e2UPObc6Nr zDtTSds~4A5JbZBHttT{#-)hkMM_(Xz>&eY8`%2B6+`{_>nf41a=v99pa++3Y9j^R@ zS1UA51Dq#cE&O#y6YH3kLqT?uM1M{f=PN{FUpcWl+omm~fsoW=8$hFqtSWIdKGroJ zb`7igiL?&UKH#NS`lBi=RDWR-#cU{zUof`f`dk}?dMD$3gERF78pL7xhr@eY2wJ4D zCfS7xKos(Bv06czz^-XV*dv8@X4WOhgc(A{g;;q|eOj#GaC&Tt2+gZP1`w|M%;5$# zq8x8|wk=`Omaf+$m0Ww>0l}v2V9xa?f%2=2@Ft<$D+e(>rb)Xd=dZYismiZNkPt-tDu^J$+q4H4&W ze+Qi{Io3>Q1h>5S!rdJp-O7QYRN$O+;p%|y>^A<1dX^rgw1Qi2LoTiSsPgeW#G}$1 z{jy2;?aKpRsZ9FhppU14O5_3rYdy{)WjKJsnjTb(4RpfDj-S;8(mhVht6D7U@Flcy zH=2D8OeYr3wTpQTj|~6Fk*cRT$WNU9J**Kh=o|3r{46>Dc#HFsQHfmek?4-EU4-D4$Qdh z&%!U1U_LVJevwp3ik*YkCZ?_j(=0tuHf`+cULZDgvB5w;2$<4?dmyJcME9oBW{TFL z>>1~e*+?HcI4Y^#?NxWcRY$zDz>zfROgp5F$em0rAOVy1fMn1M7p4hbW9LQzC}@X= zWEtG8O^ILuW|`8nmMZShs@wHzZKGA3`<09?&$=vcPZFP`VF?K7_`Z=~>;KytY@l#U z^zVBEI;dka2alare;(^Wl{Wv^9VjMF(Vr@fMP!lSh3@9Q3{f(R-IutQu!6Ch#a-=7 zD^RKXtIGUlnujk=I&a`a`VGU&^B=IZswGLU;BjitMy4%O2c73E#?=1xGWOb~!2`@^+Ffo;%y=<=?NK+mUw> zqQo^OoxB-6ja0>@lN58@nknE5u!c~xErYQ^QnyW%qTnsSg<~8(lI8SyBlS{h`7acG z^-nUl&WdcZKn7T@=b?12Ox@C<(%#+LFpFpJ6g8rENjvt?c;0MdidS2Rm?ZfACaCe{ zN4+v!pc9<)t1GFN>1_ynNPFs(LbgV|1cdhRO;gX>lA~iiM0x6wKOT3~Q`YxE!Jhh0 zM{FDBCgfUtK#rUTcIxSI2kX#dN45I@m0g^X{r^CWC)tmOrJ!v7bE1y#u{|e%-TdM- zaj(|3e>e!WX^@9CJ4UsDS#oMI9=7QF#s$9yQjKq~->R$XSwuJ9SA#;VZXbo&$hPkx z;-^7N5%ZFXOSiHfAq_e>Z=?g&t@eZM1Gjsq(%@N?9yz99oXzjFYi*Con4}j&Fl9ac zJzrCY6If>j;ytf@fiC~Brp|p9o*}9}-BxRuIcUAMQvNVcg1*6*=@V^us)FSK{5}x_JMnXI%LqSDz#(kqxLm*eNEh0JXHzKW+sBWwx7#4 z{JmjodS75j`aP!jJNc8+d4cECejay;^T-{P!<9CAS)~!?cxU8b%lzh?-Bp(Tt&Oba zlfw&`Jms{W4FFa^slOQOPvtEThD*5r9c|ZGWDkN&P8f?1SL_irvub+dTOi0H)8Zn3 zK3j&3_r!W()la${wtVHm_p}k0;|%V(O=C|2>U7Us`=Zfx-)+a^2=4-0nBTM?q-*+S+u)H76R{t4tC*77 z9DBXrNZI&wSa=7&c?LgYpsVU{2MK67lTs~`@Sgn{>4vpDy^KgIwg2fJ)S44J*rd>u zWA~CHLP@iG99;^z*EnE5UP`1?kBVa3>ViR5$9NX|!$Yo0! zHMe6;b^}xW^P4&GtwD6DWGu-wXE$or#EstY#IxPkizr9mYH;CJa6HojI!zoN?&{Pb zue#Y6wRZHwSCp|<70hu(5LS2TSYz1~63qE2i zikbvhVl$EN@I+gC6MQZ9Jei`Q$<`D*;@kVO5iNU&Yy8M)Sw11n77d3`bX8m<)5pps zDlEW+AcM$(Afmz)pLH=pitZZ!3tMm)_WmHHsWeDu=%`eRJIQI+9+!_JPas9Exy^L{X1PjFylL}Z&S)(CcCXG+iY9+HqroKwO z1**5AgIWqz?1hIFeMK&_9WfD%P)DyJ+3*0Sn*Uy(4ZBpE8x2>7g_PvQm;x7Z_A%i} zvTJU>>Ny&+%f6tMEcL3_4pHHh%C~~?4!?9g*|;JuyIj&&QNXd{G>2rSpCX&88JzvU z5!ZMZEH%TYdjjRCc4(J|PK60={y`nIB(thVZ1)=@#3JLNu+7OWUk{H-A1{fH-Vq-& zHTtW5KgtRM4w|@d8+js_^Pn_0jr-W$QcUIb`E`%b#>EPH)5@2vSdzvW-iT*S zW|3wk6i27SDl&#)_-|ZHsc@4ZN#Iwxl?)!qA3hEiDfzhv_m%kk4D5C@4}u(0hc5as ztt&45S`5SZyy-Vc0X!T7)e#_Af&|UvjAuAyCmyZ?3RRrKu-Esc4d?Vqj}TN{qN$>0 zhqD0X&Np=uJVS86azJ7qm{FL}Z-0zbQZL!<;ju*l7;bQnAJI96&wY_q9e&g5y%&$U zYf3J$d$OHP$9OIO)99 zHV5-ADJs2`$Uh5UaAF%sHt7C#1z0LxCznayaEX==%WDg=r(bfO@9Y8#+Z`j06xyci zoPRWvEXuZ}S?R_dJlsZ@qZQZDmqL<`zW~LX0LA4MTTc1Fx{RjnA={SmS5X086N2CQ zgSkdFNUopfLEBUosxA8x7m0H0ntj4zm?cln^zTxQhlT;@YuFk~Qssyp@L%98T5Tc8 zM%V3-V>G~YzA2iJocx-A!i(AI=#>^dPMA2Z^|;q3Q*`Mn=TR5ZA63t{q>Qb>!eS`Y z3KC6H4O(^@+feIzbVFw_9bFN6LxX)f{7?&F!lUCZ3H#5Uceu)SGmoj+{Ag=+U)|gB z_l-*~CpWZL>QEHx`y|d3>+MbQcMX=slnJ^Ws%&XwndDkXt#G1)d_E|qgKQ)Ss`3$) zm3+pZsEMJw3vgKPkah`+q5l-p?@$jC2k3ZNGq zERC9Ee}>mf#ddLu&qvskzewM5@UD^MO2VoB_*h+xCH;+wTH=GNZXw_?X&WIKi<(FpYZ4s4jx z-Xl6NSQe&r9--y)Pk{NS9QMjr3{tB()I?`O`ksT7B(}~@eL0jYNcw`)zna2}ufA{h zbgJzmp7lhDd_3>fX6aPc+|1E+$SXoLDI>T_pK6Q_LBUQb*bk{MqZBCE@Qd~Z*z`-_ z%wfYd0xViLsM&0i4W~LdDm`zY0fCt|5fsv#QdnwZ-x~n;B5yi=z%rqK408Omv$2|DNfpC3G-hXT0@O7GPA3@gAdjV5${%ln{5z#A-A$S+Oi7&|$Q_YRbQ zERR~ek+3^|cyFNFC3ftvK9$><*p#VEIkIClub%9S!50m(CV3BN02t3l&EZ;Txt6pC z;QQCokfB%4lQFeb1M{371HA}*?Ap(qZCImvK>Qz$J5bX}iQW&E3^$FOa%6?kq;Z3d zexCs6_xFcER)RP*MB{TI=N#u=G~a?X#Hi=TD${p3LLLyq85a`iv|_$kf{kjfc~(Br zMyKJvJ&Y9(Nbu_}yp+wNgz!-tTg3g&7A7RTKsS%nB)~x8Vi71f;%e=P8Q`9(05TH^ z;FlYjOgvza+yiEZL9~XZTvwH|G#t!PEADqHRSc26&l2Z!&qgx{Lvu1kG;_CY*DQb> zF9ZLt!29a(X8Cnrs!=@=Ne^xSjU!n~DtpicN+tOwzIup-9&t%S(t)!E4)lt3_Q>w! zq=ToYQ!jz*7*j739GL(A#_3=GqjdE4V%~r88~pT{QNN+3Wmh!%8)0xU@zi8JX5EzI zlzehjbMayMWk)genNiGcO95FI0bd8{KI$v6TAXffq{gbfgKXMm5zpvN!!=J!PbxJ( zNy$e~4km>KH=SRr_0eh6h%E$wBCWG5e{5j1@ejei8rnCUqk|ZMKV|CuLverUM#@ni zjNAWD(VBxb0CB%YGY6KSCeY*6!Vqaz+wTSqv9$~xb3xRRP66jbVmqYwoB>hpa`O6J&fyQ0f>l86xW6~!rVA< zVbrGZLN9q9b|oS#P1CRQ+7(VZ2Q5p0)@(AZ-czi^{dbgsUE<^0rE|HvZJH(pHQnvM zAKH=a7F?-Uh`(zA@M@5VA6f6-6A<8<2B}fIYw+XiteUO1i;Aq|bpp33J2MnfuVM|7 z^v|KOFe&i!!27Wo$>zcO;vpgd{ji61Gx~ZA8orS&Ac}9G;Vp}9m*Xg7kS)(@Vj9+y zt$Sh$V?oipEh8;9G>F{TNV#ZYyzsR8+(D)3vc-*8yAHv4D;bEqXR)`VwHyOLP!-<< zA!Ib3rYoyTNjY9v%Xm|2_BoY&Jw<02m%XYud}E(0+RUKeUE)20xtIl;W|Oj&V-`l? zi8|o>BhsZd%&!;Rou3`Q*+?|)uB<8*mf`17$HZYAKKKm>Wh3X60~n0-Eg9D?#z-&2?HWTwVM;dO=PP!#PI~~bP%E3ptjAyP;(#ifUsZJk! zE#C^}Au!Q&VNX!*8!xt!W?zYj=QagJY?OV$`vb5qKt~f}Op0x%(?=TkH3D^i!r&tv z;y*a52pQj&@yFUnZPy(CRMJZ==`Kg)L8(yAKXY^EYDi#oVq}@oQ!l+L zymtv^hsOnA%{v`ft8m%4jmpio1=He!Lk{)aKv-Haqj1L*6qmc{sWGcrORq^#s}k+e zR~CA@^ZJ=1-(gmLUm*GX=RLSOU|(1f5+Qc##35RigK{#_6NHGyfLcgbnM=cDfzDdU z0uQr`29|>6HbNQAg_+D&_@9$Pog6U`(z3%!r(i+0PSmsQSVU){fih3rdboW80lsXm zDcV)%M8C^{$|rUf#};KV?2A!d)C5j_<4#k&xp0Qd zC>@pa`EBpa2oW-xtZWUQafBv|Icp*3TW=<&M&I9)C2g=V1xp*z=huA1-_jul3g$F4 zQFQnHv|rTF&px(>Vc&tnaN(Uk3N!*=RxZnVm z8qtNUHd+W#WUkDeF5JIe^bAn~^rbc0JJjiTqH%f(N8k_w+~VSbu)PHN0$trWMwn~N zRg^IPQYaYv{@>QZgLmMyI5%4G3y*LSFTE=EGS&E5>GbEJGn;Ze|G0V4;T1nj_Q@K^ zK6hdqGwtq5A#CJsZHj>aW<0sF;EoE~jA;u7Y6=>Z-$FFQ$SdBLq$6J_X(rfr{WwIT zFX5@yeE_!%;P<6dRu(1Si^3=e;ixvXbF5Y4T=6Js=IZffHGfZ%n~RmfB9vLw%c7th z=O=2{uP8!uxT>KYlRzcZorx}uP|B`btrrQl4s1>PT0=QBZ9?9JJHJiUznP;#AHd39 z+NBA=1G*5Iq5S-N$&O%;4 zSS_ww)`4D1HH^3E9$2`+nDT>Et#m48M;Krl$fkbu0uG@-c4pYl)Q)N&_!remZPIbk z`FeYva!S`2D_Xil2H5it%9X(}Wx*4MMXtw}z@q*IS<&ZgNU8*JFV42KbRC?iC43{o z377e+iE7<1X=_@YpuK!N_~tj$QB+#{5f*7{>3H6Bi0!AaoPWVDuU9&R3UH({b!%u| z4L9{y9b+rn3n{s;(sds7OWF?$9n1;zXUB$sd=#zz^pDhxza*&3xJH>czrJ2R*mZ`p zt6)t*PC@GH1vSRV;NMwZNoqdsPCHJ*Xp&lzb<*b<$LVNsXxfQaN^GUAqLiZ)H2Wa zUJxByV9iyd&sL=dQY^t35(zdMZ$!T!VENI5@IFZ{9Czebj!J-DB3{RhPdABSSCD~| ztj=`%Q%ZZej5CW0G?5Fw=)x_?4{>Qzof2>!k23+W=*_sik>bxslt2+!9m!I{g&S_L z*VD0S>sL^%T|g$f)=SZ3V`2x0yauDN^j$JeSyjw+gfOFIZE2heZ5&om&&KEn%%nOY;UD4HZeys<+7Hv&@ z%>~#G?oFv3*BpuuAJpcjDD{+2DRqNAt90^@S4D{$3tSfUesFpeEo>M`wxyoi6}J|2 z)wWT%1)~Zl;>z=CW$fV=QJl`Um6&7Af6hWvFKnF;W?CNH zXknzs8N=35Ot>z`WYyT!w=!gW!&cTzsjuBoaiZa?Y@*?do$(8tC!dR0g_(mNc&5GR z!HrLBtp)X86nnm*Cz1S&Xnmz+3y8efn!00Par3S{T037i(|b7~ede-nn$^v{9GbS; zHyyN8zr7Kew~EhuB!%&^k%MSE#ymXkoHV%9$_ zs65aqJRZ<01e+sEvBgW|+B-n`w4xP2ynU@chx;jD1?=<4Ac_Q4;$f-id|=@zJ5bOq ze8grA?({%|98s{|ksOYpuhYOI;@jeB26Q-@*69JUvwJ-)!6d*5M@mq-c#IDyoCJ05 zb$eC*Bz1hFl&5PRR4t>xO^}6Hh!z#!V&%zB>M?$U*C|xOl~fA64PY!4Ylaqb!nZq3mp2IXz%Dl8hanu(PR%zW9WvvV7OZJnzxY{=pD! zPPdEd)TG(v{gKb7@T)j}Rhhmj%-*EtdZ{TKzoZ(A;tfoWC?atZbHPms`9-31bb0!c zHHzID|JHX|PnOSrQg+K`vna<&!VF>1W{!bUDVW5M1w7OXQfmsXVdTK|x z;zEUXMhF~~Mn|#eg^HQZV(B#SFUR{8OlBQbG5BS?9i>pyrwJ)3u)gKk%O`^d<`Vo$ zuLI|B>JPyR_uZ4Ys)gWiTsoywVRH$byPBchaVVYkNyu#}Xd2UGI>uW^FXr*77v;$Lg2}U;#c>Zde3Qa2+IDZ+> zbaxTXodJXAX{W$W%Fm?tCJf+N)Y?B5_)`h|T#&VsOWPv5x9S!|0GR*GTq; z{7BN9Xf-cIy-UX!JUeF0?XXV7t^G<^^FJR5o{R{L_S4}eYirE)Vnep*-Fs=HpZN4C z5lU#hk%y6~qpy7#5%a2BQx(FM%^fWi)3F%Q5d3K8j<}64N?HHI0#J@Ioyfm5DXDsl z*Fs1^C6xv)DC4rN<%(^VtHbxyrg3D$D90-d7SjJRDr9 zXQB8QJ~+B+6?uL}mxmf%I+=S$=QIaoANwJvqkqr1aVE$FDCUv98Pr~c49hEmqQI}& zPA}8uV%tb}V^3>E2FW?6F>~c{8|{ftQoyRTW6AT%<_|emX`-02bfTm^B2ozq$*sBE ziYOQm9XgnmXRUOq>c6T6TDo7~@OKqU^|HkscgXzx3B?DNNG@%;cr#*3mZ1~=(bK~| z2BygPr}82I{>O@8nra@e#1?g9N7cEK?52{o-jt5sBlnqv1{5_Q>EMANYW}Oqx%o^= zIREjK(#pyL+pw~JRIHq-%24b*6LWq~c|#GN+(lQ?^jjJ8s(^pEH;C!S#g4l`1e^q2 zGH^(#&#QaIh1s*Eb0gr%m)q(xI#fl3fBxu=Z*=|~_r(ot2P$|pa>UPl*b1icO5e0T zl}mT=M*V@Zer(y_#f>tG@W<_95&oBi=ZaTNmKlk9th0w|R{DaP!SR@ip#00JU35P3 zJp#Q1g=mK7;wog~0q}y+Di&3q_|ei{k>S1cyPA3ns=mz3vy%`pl7r7q%vq7=;%TW! zGJkxy@7XHrC(aW@W3ThPNrVJo<}?nDQZVcu9MkafG{tmE+c&LkqA$m5@`y|5hICZ2 z9bc^wK9ScsIFKX4Iye`OrFAIqwQo)(SpFLaE?zMS*0ub;npv)KhDoH^%(<#zS!$>| z%Bt&V*qZI+X|GpRtq`aEquJo@6xdq`g1V(=xtfy3Z$S=v(iu#?nR}{LJq!4B9`AW9 z*RpLgWJ8fwMH|LQpZ*94#q*O~V%EB7KQcYH>7aTvF1ehSR$Q{*0X(n=CeQUH-AWp> z!pSG3)s?}u+TrD(t}a&?;KOu`HUeP<_WwZ)Z5C(-Y&^Vhfd*4y)(o4J&XiGwZSoBT-aa0%8~ob_+s72uu_IzkB?vi*#HxOkBU+;uFsl0H-x z@p|DrtePN|2aL{sjgfatI|u2K;D+Q*buwnkyuyi2889lwihP7xd(Npg!EyHEml3@$ z!?yNNY>p1)95_d0_>MM`e>8M@h)TRnAw@FXHpwC8KSJL@HMW)7P3}jx&*5$o&2>i! zW^#BN5Z^9+)mi**qA#U5O*^W!>jRjhXY?0AI2wFq9&4U_mN^mfkGsiDBR@?@o`SMd z2kgz}W8rxgq&&y0;x=83#{Nx^zJYwk|Gexia~}+{WG1M9wfE+1BgAgE1qQ7`5sucD z=4@pK%g!I`T1xt731ffFdUFeW)*L$Y&3|>Rx%8UD=Y;-lv+^ zq*Z#5JB3bAe?<}WfKfMo2e4d|_P95#q$;+554L|GnH~10?4$~z-Lib36^DI#e4rbb zYbVQ{A1~-;t1Wy~wn*C%yjO>~=;v)|G!7++L3*Uph}AqcCSH?Dh^Gb^*->8{Zj42M zq+}BR+(mR z5`J;!66%uI5CK|p4{YrO$UY++^y^L%z#3xf%^Kil{|Y9aS?LNouN9 z^kS&jb6(j~^QXI2khwtrklD-yw}|8WMg^2#zX^=SQmTetE3u=->qpj#s9O}DwGBHt ziZxitK-oS>)Fot_#BOE+O^XEylVAA&^y3M@f(ya6YRwkT{k>j9`uOB-v$q1 z)C8WF^pT217e7U_2{YtVV)*5e)(-skrblULOM6wK%%k#nNwq*7wMP3yKM(X-=={fZ zz#>F!G)@y;7(zKaNu8e zxiK6==4i-kTHyE8=EXG$q>7!$|j6()4Xs>oiP=@Ic-C6 z0^0FYe8+n>Per^IcLRIi7kQ+j8OP+>zb>sW>N3#=*pKDnw9N9>Umwj(cfF#1_B!eIGI{OM*X*e&JBID_MfYeJp)`1|CK8D0xfmA0J2&)|`X1e6%`sSU_&>1=_3gaWj zoGANuIQOv}|6xyf0$-A>Z&Z;SMfd;ezV8ck+%^xoO_Cc;wyIvzSUP3QIXg&N&c+0g zeBO1QvfWD!y%v8>(pF#eQ9-Bf_uk1y!;HS<0SPv%6kDWT@Xd?>4wGOi)@L{W&_oVo zd?mdzUgSl+k~Vm=J1xw0XSS_*1zU_j3gWZKl{XGuWjv#Z%<4Z+48oE7#dShH4;oWT zDO*BSnKPr%4jrw*DmxMt4be*F4C73FZ@v8z%paXRb}vx|O=g^^>!`z882%UN!&fHZ zoM66mKwh9Y24p1pqLsaeLfcStjpyczDlJV1Em&n+_24w0Upgh2_OnjpR5o|11Ps27 z^pDo$+!h-D5|zFn`PhBAP3YIxsj5X09wWChax#8@`ZjB!WASYq!$pzZw6%v)-I0}v z(IKz3i_;KfE9}Aqi9Ntd2cw3+4Ij%GASG!{CBsvLh*8MHk;paQG3@e6-zqlrypw!G zryv8*uF0}Hf2k+I+mF$WZ{GI{m`=1QUC_^nXU~@*;c$VFee=0F__*vOJAK0X`z}z8 zrx5?{U``6a3Y{mNYZSc+pGo&c>9l&u(J7}ib(kF#Y_Mbt|1`T-BEnrDP=-bM6eo8L z?Xr5<7QRDCw@vg(?h^aBZhe~V;*`jTCen_!<`%EIlN3~0eAn*>iadt5DOlf`r*zP6 zT9*hAfs2Xt9p*t<&IhZ?7s78d2Y4uRp&JHzI`?6y6|UYC?|s(Z(AuXn7GBa|lB$)v zBB0jh55Wy?`-b#V`E;dk=}b+3u(mi7tfhq9>}*K)Kz;ypGF6mY0%A1LSPMs4l4^sC^bqabCR$EF3*bY-tT zaU1E;e(#|xMWU6WlNhamhEluk8A99Eu_BZ?t%)#%j`(=9-%Jeq5S=2>?kR;nUoah;wBU(h-XiWuV%kbC;47>pS@`+o|{CH@ZQZkEG6)xnp2n ztsZwgdRk9wB;wp@9-~Z-U|)#~;TIEV(UY=_KT#Tx z5pV<6at9iAA_mh`T})|$yrr*_i>Qs!YdXU8;g0jA?i$*UZYZ2ZX!7XW^hct#USO!) z2<6sy&ZLL@#CqT6Yr4zLI|NW49Z&HrUi637Qk!fP4u_sAmbCK#Cgd4j4EEPxEgb(c zP#OU!2d*OuMS=mqPf_$h?B{n-71L#)Y(0PSHw~)~*YgyLXq96&D~8KLE&987aO2t< zjoOp7if%=7L*M{|q^K`|7id>PJ{i`7VL^b`*j)3XGO}s@NZ8;rELsb4>~QS2>}lhL@z4r#1V{Ql<{cq(j+x(&RZMnsn7B4vMst|X7@>pmk(&aN;6I|Q zc#pDx0k^eFO*sRp)M>~br;cOQE%q>QzVGlyJ!4z@lT#`;9$_~lzT4VtVTwg+WPdMI zGl?-{8q(KT5*EjVW3W{FWNj?1;{WxRq?PO+c~m^FK&~$hQ^ih zpQg^3ihj3qYynAlVY$z7iiE@gvh*y%-hsO~&A_ew{Rds2o*%zK{85UO!% zLz6r`FeL_jLPV(k&QH`BCXi9LaN1cr&ex=Tj%>F}!gFZ8SgV(Xa<={ZIk#6{jKev+ zSh#JheSVt9%iOii8pZj*2{M)qe|KZ$?-J^=xrd-uGg!}^=DGYJK_-tuYd8=5?W?geW4h?~9=$F@StaT>dsZncKJW%7+L`?IU!_tH0dxPWDWw z9}7DM+7jDLY$kEDd_7yDHZ4`T6A`YOv>H5oKD+&qZS?K~!-1r4v@Tbx9}xw};3hc?Wqu(W46iK@D1IX6{uAdR-FrUFw6!?M{>AiG^qD_4-= z&v}q2&kq2;&x^7kR)mCef$5gpCOfR>9$b|y;|-J7AGUydTHnKGS0zKsav|CLj+efK zzoS1!K=9YR9aUcgkDkD#^5mXTJaxFA?S?t_r2|wWV{3C5yIJ%@cP^-)Dp~ykgatyDxgKcUP2i zZeq+Su%pDaa>tE2Nuhd-N>j~J`vUMsHQSk0ek`RSur2jXhvg+1RYRASj3d#A{$IfA z^z9GzmhQ*s!FwJ1D>6Iw3?1d}z`Hu&cE>}#yEd8l24Zh&sx8h2zI?Q-0a`q_QbbuFyHZj5>b&|xp` z3Ch8;3s2{!Ux7?7$75PZ8Z7)h2pWz%*iv@{gdvJm7^J(Zg=)PSw#EX0~S^ym*PQ7~dw-e@-VM|iE+a(n%q25(5+~!+F5~39;Lii`2b5SjY z;3X}1;mm2ZHXaEw%Q?rjS4VZ&noZ=KVzOx-RF1=#c&!d$^;mO^MM~E^MRdJ&Y(g`szx1+3+ol?5Uk2v4I!)>cFvn*pf z&V@WLc6=|cZ-w>4h2~q~c&4BK&IycC_N&>KFT~9 z!dv7}aooh#I%!)z6RKTaeZ+5$8#W(@p*5)+Cp;w#E7)k^Q`K4sCR$4399e=OHQ|5z;yu;-9u!dy*C+4!Ne zy_i<_dq=QxU1}A1(v$kfB8Pi?w$6Ne$wOSx)?5o;(H9OR!!k++j!sAaDr%Iv*A9-h zOk7xlmLw{ii%$l%&+6%5bLwm3I1d{arwz0&4Mr$V>8=nG7ptlkCG$b{;15YZ;x7fa zu-3H}jR!-Kk{Ez3g>fhf@!!OD^BAQHukNb8+~5QYRTP0-5Y415tK@Er3`3iN z%$1|kkBp7J0S zAqr1JM&NPv10KXWGLD50RG{QMV+yrTF(?tK$l46kN zU5p9;{_}{xUpRUG+~e+jNlvM1EWkbJ1SZ$f9{1gYWzcD!N5g5JL9&h{z0o)UO<}Dd z*)BY_OJKayV@H)s8_PZm%BVI{y&{tlDA#l#MlfbRKkXQecCu<7u_l2s9;^|V}ijaCCZe;lck*tzW*a%I7Q35Y`wxGK{$$5QKYJ&v@CXT)oPXXuTXIB;`zZ1Trw|q11!O8Je<1r1 z?E$n8aXL47ee3frEeIIhw!9= zzF3hF(hx&AKcLV^iU}4Lj>Afn2s;kd7p0XPDrmzywUh2N@U>eH#f5_+=4%TIWy-gE zLHE}6+obES)2{s=hncSaw)NaW_Gb123LYrfEA)2!U+Q?XDVLu7I7Kp~j}BxVf9?() zkJkqWzwHMMHV-l-EY41Eo;Z;w1uPU5U%SgB1+u^v#jR6!ivj^11)5Jqnp<0zZ&+c%fXTP zIV2HCU00)lO1<3Id(=AalDpoL@BTVMhxlM&i{9WHPysj*ztS0P23|lXdGfRoAU%u+ zP`H!sa~YSN(l@0c?vs|y@{0UpAgdH8XRR5%P{=}i4!*lFa z%-8BI?krlNi9_3t@5%2v5o43$WH*LS9ySD*qejOU4-~OuMHG4$i8oA&Xp>Oz%o%#v z4ZzMe?2k!ms*%o&cTd4kmy*s!WL==|@)eiQJSx7I_|55ylPcw?Yv;%ub8bCPb@iH( z+oh^-O1m}1gz0+j=Lyo|qMx};E0XDaLxrPG>-9GLJ1$$H``SPCbTAuOlBcWVxN**2 zL9$zd>1$0CjE(Uu_(u#3)d*=mOlNdDXHjv`g1d6eV##=(Io@wAd_cPNApbMdq+Mcw z`yQzLR50&N>IW`OvevHZhtbh|a}JNeGGSUXmdyC&GZ$X-!qa^)$Pi?sh&@^1>!3Z3 zN0dCP@q=w$_#6V!C_1$@{|sNSwfmL&v4wqw&nG0>Bk>k68TaOpji5OttzII;h-es@ z9x>_tc)<>{M_Ive+4j#n1b)$j6+&Pm`G@6VUQLw?zerCRd3e}qG>)xEp`yEXJwts) z4U&2jeLQQwlxwKtULQf})8f;f;E#&%r?~&!0_eN@<5UFmUypBYo*iR&VP*0J^B0qv zu=_`v$7$5p(4*WOQB8iK&4x(2Y=CcUmG0QnJchmT9lo{qJf(N0d=TGPV~srDe>dpK zc{LV)RVZZkE5pX}Sh-<7{>LR@2S6?`Cai=F0`6R&Hs|-Fk_#qp@r1 z7{Mehg6~Z1QUKFU_XfBOlDJ2AUhh`YmTVM~$!wu%$PJ+s5v!G1P*&7#HZ(PmvQv;H zN_Lb4sO8M7TZpP+NEAQ|S!&)Kc&Mu4xTxaen5&&tTt zJ4dL!)NFLX0mZu~$sDu}I02FC9 zO%l}oscLpi_#@1hAVi0lzw-BqUQ;!sS^Y_#dvgTgjg{e+I~o80$b~*AWr_TO4Bvr5 z8T-PzX3UqF?@EF0A^NplfxXLl5OlsfE&`hpM!yt@adJ*gFjdmh>wD806R^KKL)VCN<1D(gWaQ*|=@u;YsA>LJoxi^Gx^KLprAb=_j@IPGF&Xnd zCBg_kMZnR24(>$4wHLb|#KcN-rUc_4aWzxpFo?m1;*J{odTO8NF7(7q4gZ%DS=09l|P6=;IQY=HW(eqr3bW1%oIE5fT zcAZa&6kUt!dUZZE`F^y$07(xr5N%$-?9lmpH;M8W~Ew1SO95N2L@*A(2)6`p$_$9xs5uPx}t8TXd{_6Dh2# ziSw$MuC^vlb-Mf>sf|yV8lJ}N_`mbM;`x!lqhSUXr^GnPWJ~VSdv9aDO2~&09BHI6 zB02xr?Tq;A;JtHTbvN+mia6s(Ukp7V!e)m%0Seo%*dt@mp2w~}(f0|^Mwl3jcgM2s z%kI>7su#7}wy@{MzqZ&Ozv%yDQvZY+kG=Z|6OUI!Jy^s(Uwt7Q`FDtJR2ka$Gy~*vZR!(k$#4vjts2 zV);I@DOw&jI-bb{`6T~}zwY*LiyG1c{$?3M<^p<{ULn(^j^yd&CY!-ueRT^VPEy$J zDdPO&7j3{gdqE~s3-;gK^r%x|am^Q%hfXDEIwD}5f2PzIkPBxBW18I`s6gm1jf7lX ztl6EGiaf?IJjN`G9rDF%$dT?Z%sC#3?pN=7(_;qQznD!aS$LYy+(CD_g@ht!dm3WP z?1B3m=Mlc5tMi|xHb%Hg$D?S2M(G9$3Z39(WM%ThEqW@l)Jm15&DY?q*3ICE3U?$> z{a~%!W(WlCf{dNUj|0@eBMz`&W+d@Qz@CqFU~m+bJO%!bP?Cfxw}FXt1jMUPlyU?o zAP27Ru8yO8yoLS%Mvg{Pb-t0VZlUdW*4`E%dVFh}_9QP6e0lgf4rD292YPKgN54hH zQi8_iE+X{|&8B=_OA!IrrD2OQuO2)fbgU8|O6mqlSV^-%8OC3}@Oo0-q8QGwYkc2v ze#Ut1TN+iwEKv4Mgr+;$3{iEyDQ@r=&Fzo&Rbl1(R6MCJ3dP`dr&fwq3Y@L9A&9UA zW-i@)Cyt7`=LG+uO_9*6&>+-Ucu}$Id%&k_H^|ebfxW!$w`R&^A2huL_ z7X=eG@~>h$MY7sLVa|3_*(B^!q8p_zcz~2WyU1_wXg?m5-f$ zB*mxMU7F|1`cfbI#ylyN^qBS{tQ0oL00O4gHi)f}mM13uM#M3?p}OqMKAo+MB~e5< zK#C&vJ2*|3wW={_(Q4rASD?x$vYCLHFyl~rm!trQ<(R0d0F?>E5|YH{2HoYBif
    5qN2{w~ekSFef4~sG*}Sjs_`sdu7O{p5GW9$A2KW%S|O<54|98-`H-zC$a6{ zP;Ox~jNvOv75ZC<@#1k(dZo^KQqM`a-~BPMiCSn`!COMncPPuxG>!EOj3`ExotJ%O z!3F3F+U&Bj?4y8)xbd|LFMn|w2N!^*!g|-*D&Yew;y&_^>+IdEo8>K&ck<_Y*Xmo3 zliO(0L9}0~wR;}kjV(;Xqur+J1HKk^V0|$h)72MyId)z?(~kaEZ3W%lcMA5~c+eu% z7850jy6Q3dX>1*ovqBYD$t?D_Em~uq#I=(cFL-vckoS@?MDaEdr1AGJuo2l5XENY9 z_zHZJ)~H7ap@v2Bj(A>pbgAvU2=@>4(Br=%@75{I0tN+OP=(i;2BjC}N6H_{_PX~S z4a3-+hG7);uVd@tMrj(|_<(ar)j^i_{m~k~u0#adMx_gO>|rVM+i&EPoY`A$T0wVu z2{QRXCZ9y6(~RFMJ_O;pEKDMsZBZmpCiLgQwA{CSKFTG$oKg)zO|7!>Ro_gcCc#^9 zKA+7%(^_i@-+?FQPfq`v#U?=}5J{`)1 zb>rG_3-n7~pg|7Vc{i$S%J||tnOwN6YhBa=o8;Y#U_K~753Zp#x?6*vkAK+TgSC0F zj0p>pQ#Jsu7v45x`QGa~Em6RQSe?^ffbH1&z{jjmy*+C~pKqPSrmh+4mzHI6o6)@i zgf^R;jf-7!UI|o9Z%%n9id;MsuCFa^z>`i@uNhp~KfsiSs$g(Jl#g(xZdx++ANB(x zbf|lD9K#S+LpUnUTd(j`A`>o(LRspBN2?D6fr%>xhl{!&JV)dQCiv(Sbas_+b2rwN zPMi>!-9Gj=Mg-^VDmg%&q{hvF|6Nr8op{-s;7*Vy6+JWMdhtb{9bVDPyxW*~IqjXK z(DIR zHg}le$uowLgzOSrnZ*wPBquu?=$^HV2jbf1PlZH}fTtGxL%?fg$$(=S4BXBe);QHX zFVK?5=;6sUhUHo(RxQ3@W!PupHehP)RNzP19;$HPx5_WtM@2L;qJNGKIjfQO>%`NJ zt@KCPYRZo2oLO|AYqK`=tGq{?h8@g?w}Q7r?fRjP$WMxL>OIi<` zmnuxlvW^f&NRclPRWO<>UC-pB{T< z&X>;I?*kF<+WG_ee}xSXj#Fxvls|Ij*GYLwQZiDVt(h9gKAP&e?VW!ZzL4fI#rj8% zSyYYGrf{ITdEzP#>fm2)N>L`@7hP3Z}Lj!$0QlKvOQR6g6HjLdZT zVP?Dn!scfO&Z6Gy9^7$=^>rtm?STiLP}|0$7g9@|uxpa67#Yo`|R@qa(RCnDBa7X$J;^QAtbFRNHngGFsbx7dyYs4C~) zL07PFCijOcsId%;845a4Zdq4Z4+8hu(rP##Ojc~j4;>`b99EIc1+}GD+Wl>0ix-Q= z&+0fE7`tPwO1ik~zJ}%soMFmP3KkFQB1_Kvt}xRoB^ca_Hufal#;dmsmgyYC0AREK=E(YrPPk9Hb?hYji(YUn6numio zh8@|is#JYK;h8VS6mZ{c<7W9}Uy>A_QCzp5+`9tDl7 z07pQ$zt97DDOr>%#(t5hSH`yd_Xt)vRAu^T%U{1Oa>UYOcv#A}wfa!!5c&=q>9)L> zf1xWu#GcU6=(J8=sYjv|@Xg_0saFa5otzTm5=(R5H1v^L(> zfZE=v=-4pC9%%a?kpWL2v&M*Cd)S&tOrbwt(Vj1zrE%WYrNjk%f>I@C1pc2;U9*V1 z_^>`U9t1=~t~N7cHT5CYKJJ#=p?G zQ-4vKi`UbT^P%2#q%WbdI{fcleCOB;GjrdF-+8q6Ala!kDg?Qz08}5s@cEFB%FU$v z^Z6AISbre?Q$<86fuTeuO4@xGv;*a)Lm@6mtx~nEOxwW-gxQoMpt5|v^-(eT|5&A{ zwn;MQB9}kqZj|`5{TUs3gJS+vLXQ<$M(RGF6D)3Ip|>&)o6urez7C3^WrD9Z&iNOpJrOG9G9UAPOlspyZoNlavMW}tU`(9Te0dx z>JwHW2ZVIxs^D3F(7t!=!XddD#seN`!aSMybA$pC|F6qF|8otZ08f>@{Dez;l|EF& zRQXa0%hr?&TFab)9Y7q~5+R+^8kFEK7FhD7?x>iOtq^g<7A`OQ*jiCBQ_oU*7Ob6P zO62lL2SVXpEV&3iCx@ul6;+q$_Cz7XrwUC*iN9*mSd$g&2wF5K$nNiA%P- z@NEgbhQTWm0Cn{Z6>3%^TV+E<3Lc3{AxkwA!MC}uDQVpe>(i+ynf(96!9Ss5TBUd0 z6is2iJU*R$jp_2vY%Md*T4!loVE!#zK);rL)F9G;M1c|Yr}N*yiIsu?95tU&7JzfZ zM~hdJvlT>Wn9pN3m`z@;ZkEV1j0bla+y$IHM+4l8@S%4F+EGPtro{us6trTHL&Dg^D7GmQqFN>7G$pQ8X`!%9D5V z6w3y@fz;O4UL0e+vYD;l%uZ(hmxl2+VYut^SPJ812AOm1Y+#9IYO9Q|J{O*eWDp=; zUIs+gucqQy*{lK3i+tLJC5S6F+jylaEuZ8?C_-op>I#z6Z*^Hl2GVZ6uJ%B^uuLcy z;uKQ-*P4Jk$LiD4Lwr6`vRFcItQb?4%l$H_7zHaFDTPDT&EeoJ?9J4mD(O*F7OAoN z`(^ttAjD-qy{D<^A<=xh?|_6-FlGI39K*?i znx&0ZcWFq*tki14r5t9f25851Y5LKSCL@Zgwu3j(w@U24bIrm-#V_$UBpVC(k4pen zV$Co3b%yI%i{XM?iN(Ti~emN^+m}m!y{M7dOcmFZWu^w_^AVWorqzf4@EZ(O^TTlFmpg&FAd zi&_;rh2fPddH^!Vn$R;vB9?@n;qziYK4`PhK%a@?3W1^@oHj1vSglk8x*2SE7q?P{ zKtu?aO-QGhQiDM9SS?#a0cYR^KU|S|YI%vrF8>yIEM3jwbDH$j-9S@zzDvX>|3{np0#&#p20m=MPVfMOmA{R4{zWz z_uw=5Q=HmH_0v@Lhzj=x%2WW=-C^2Y*$^)gvCmZD0a_cv=ij^AL+29}3N%oX4-O@(Ky#pJ*niG862A61I(ku?zGrs< zOSs#+KG#vn*?~$FytZAuhcEX`Ww@Kv$E=~3F!i7mKM+H9k-}KgT_BeA?08{(YP5Qe zPP#78iObpAM?v8$x{icbplbD8({)SS5d+4NFS6&9^-HSR#QZoCPuV@RF4v}Rwo*NJ z7dLj7(HPOWw@#|w3X#i0f~N&J&2=J>+ow6aO`Ym& z_zU|$B}z&dZaY#bMil)Z@wCT(9_YQvs0(RYJ+hR4W!JM`uO3uxTl3}+`e@fF)dVUh z&p82FadPj=U7~2so_`Wa42!THaP2MEyb(n(7vl$3`9xY1e5Kzi<53n09Q%y(YclB8 z@%{PhTKN2%(J;RX)CV!@k96zR>UK(OvnUn@duA)r_l{V&Gn*xEIGt7Ea#+Ix%d8b8 z?FG}xrAP557oz{qXii3gVpYgEPU|3R=6hyOOFpN-oRUVy546nRteA1X!;_zsOH=!q zk8lh_Id@v&p9T0NGv7|D+f7WHc-J=RsnaH=TuLt{<}dUNK@+HEpD${k6J--}*4{4* z&nK%+CX#8CJS8f+M=SHA!_3GLe%uJU+YzTY-P4cI7imw8H9fNGFLIAvx`l~>64k^9 zcCh#_a{{taKCgi_I5i4y^HE}ngS|`GPE9v{*d^{Bqj~N|#f%Ej{mIE_TyVVe`WMN6 z>$5wi4Mz*%JsZ;E#2b>i;9(a6f-ZD%XSrd)yT0_Dn)K~74gf~i6{ml3*ZSe}xu{qX z4hI0bSbP}MX?qooQuZ^Fa3s4o22og{$Wc0r?~^6h4-Qj8e4qypc!ky>nB)UzLRT0& zyB)NKrb>@wPQ=${iMd~uJAKG{!yCnEe~hnVD{%ATSvprK*Y6IGdi#?7FWDBVu(Zia ziUjVct561^edvNeqNyLU|B6yuLNiLJvy}wFfLIAJ`W`(PA$S*2kWTzVuG31uh0!vi zfO!CI>0JwEGT68IbXyOZN=`!FOGmgy6FFyAjkv%x4r}a1KwYO|6hIw`Y~u1@ic^ppNdBz%@`X% z57Ce-8*T6^ejQ5xZgGG&T*w1EzQ>>oimfN&26q8~GpRI}z-JvK{`RTkCIFv1+6QGZ z8b!d$V-o`R3%^$Rw zk*(jVK)5nC^>TBsmD0F+rp5A?uoe_~TQ1c*ZOc(WlDQ6+0-sdn%;yHjXQaS^G^aLC zl7YF5l;*{3vp364fIXij)b3v?Rnmr$f~VVO`i9C^U*jmQL1*lU1Jxnjuh0&C0;}C9 zsw)ocKz0ke!fbvW6$3a)9eW)sRC~%x9vm)u33YR^G_SrRDHAU|dY3LtIG+p|DyH;|_n`98q3%S$AkHrZp5-@drj-)u$Jare-au#XL1*-2mg#yhbka&@ z@e9G&jZm3SbR?3Tl?v_#1Lk#3KQR!*Ij*t}4e#P?W5wqKdY$cT9Wbv@znlq0RvU$> zw=p=~Vvyz(hUG|jjQq`)S2uzeO{90|;HS88O0#&Jd#N)L}!_gp(@Xm?cKt;pz*3Hz9&to^7AD`@CmJf%s zHbX`)_d_Tvr08g_k41EPE2Gg}9*XGnS4X0{Jr&fA71A_OcL8v|v-Mk%&AZWWu1?NtuH%VfHAGNAx^Iw z8Lc-3-lSHBSWUPfz%HnqvNWrknu4GQm`G%@+?HC7NhJ=}Nj_-OeCee3hLh<{C)Aox zB$FevI+59(BeMjgNY9tfPHSBSOp}=O#&C`lk*&0930E-(2`l{bF`Ay4gXY#||M zl%=c;Q#K&}*gbdGt%o_#Gm*^g=0YEEYfkiYG3xJ9p+h_*?q_HA5YGKPKXC%WU z#u4E7;HmG6WQaGD?!)IaX=!2(b!nc=*$d^5-kR+Y&>Am+HiK3Ng8@&%0K6EkPE1`m zrv$2viLZDTM2`K)^BC{L*_r9DySXySS5!O57oK|5h*B3fiK585$yV0G+9MCBAgfXX zdLa%48&gkX_!D#p;n1;P=(&1gT&|0b2BgNW_CLgT^vfq6;W{41ZiBVmk?O(w-p9(# zrdEpbgws1YY4YL+hWQ`y7LIoH6)Wnd7*45JlXz=SGtPk`r$qHqAAKtX4XsmKbn(=o zt}O^)r-1-Z+Sr%dq53rBs5%}tofRJyYzxqez#zNdXL-=uQR&D_36c}uoTUkYO;13_ zgxE&s(}{648{@TSvNy15bLk4oqLRsLHc|1#QRrHDu6^MU99P!h2_XHta)t9cP5b>%lGJo9!W z(DL~c{WiuGimOYtEVXl$BgTuHDISO}oTROnlTo)kULvixAH<+2+GxJ2Hs;USZLaOm zK=jmq3;$7I>Axtj7Y*@COj>>?ZhSY{us$2+cIOezC4p`7{h@e54)^>tQA;&_7ty6H zj)D_pT;V4hHtaD4ycJ&7(h;M{-B;Hm5tMFiIGkwk+Q3$oI21Ig-^D{eDw>*f71|N? zdKGg*`d{iNcWY~|-bPX8%Sg@Te82OMBeVwCVVF}_L0U}842}P0XoW7n8?;5sNk9Md zBMAboAhjOM=%Oh1)AFuE5pT#nXJj?29th<-@? zX#F@UEa1*mRh3*$FINs+1BM1`m#P=WMv5@HC{3!u!z;dWb>s_kS-7j3JL;=NDX@}G z&ji9;i?cYr-2t~8nA?^lc&hQWq-Z(((_1*q!I2@nmYI1}Oxr)tlQT|h=dDOuB|gXk zh3}WzDo~qCn@3h&0nS_ttYLF7714nqZ&#M^&crML4-v!_y9>zQG!-Kb90u+!=_?Ad z+hTh~*4f{(1UzQ6im_j_qAnLdqzuWctu|=7fThCyC8`!T=^jb))pf-xpzLa<2Lpd~tPIgKowT@_@ir z)i6PyO~u>%FFZNfcNdA1yi%DB>gF97w1xO{7%5I>b1aAxQsB`I_4<$AU2d_Mq7$K| zqy!O}+jN0j=S3IIq(=9k)zX@+AxIZ@ts(97WOtojT?G*-zZHBl<&YqjzNYvtBIVxk zv(~Jwe5Zn^y_XX;0mo1K`cMinQgL6s+OX0Sq|y%!q$y3LDovybqP%Gii~OWlY5h*D z({>L|L$F$suJL>mxMK_#?P_LtIh|4|G>fn~@z1^+(eU!BZ#I5+Dsk36S<@-l&9~J$ zK;z^I6K_hr761T^<@~Yp7sI#>Jh){SfJ}S1SR~|xC{=jiu_Hwg=EY>m4v$HFsM#Q` zQqLqQVw+9g1%VWBAu?*OlP5*1n~-WR0e^&>O=)%dlIA)&u$uVP<(c(_(cz153Te=a znp2v4!M)LjU7lvj-(?;QW2x1BS9$5uj4`q&~YLkhF|Ki7>DM={h|2} zstF>lxQIYjxfQ8qD~t78co^Yqn}V(k6FF@Umx4g$Z`WnXIm<()PCP>NV`M;mstJsi!%3o3@$hAs%t4)g-`OYOotw#(o56^y1xyXOtr8w8fs-i*)hyd>qQ5^2 zNuiNCKLs``g603xbTk5VF?zqX*uR)2a2i9K>Go9>VJl4j z-^+rE_eV&$(H}@H{#R;xz^}k@32}f``_O4*a_?|?JWafgVefX=+`ypzrr1C#u}zT) zo-Di;Ofk2yb6HsJkuE-~Y9A1&GpwnP;LS5SC;YD4mBoh==@G=EcE*Lixy!?bHY7< zhH&+ALiq;b{9QIpo9ix(H8`<8is@LkSvF)L&SpiL>`ko~r=lA+7Nr0=(oi%771xkQ z#W-Ct!DGKsqs?_qT+9J1fSD^Ctahx84>dT#%a}F;PwVvV_?(fp#Rlhm9mMiVHXD z7pHboN(Mf`c69;jr%p;7FFv7&0H77(kN5>cJ{0H3QGZ~FKpqnJOC16o7^+FlpGH*H zE!nw&6OqxuOCPy=6Gw{;4>30ZX0sU;rHmN1@K(@r=~(?{*P0Gwpq%M_r38zuQn2?% zO1RmwJ8{GHu>kq~#S+i4oXg;K6xe%G>5o>U7N$cWIvm{5VVWhW_-v@AEthd+O)?4Y zvZBJTQR|V}W6}z0Ya+Ib8Ae)*m#a}T$Jsu{IUbPn>ePdZ#o1+XOy4Qi;+mJ?yas#Q zC)lQ!FR@ZhQ)lxp`v+nhSZPU#yo%W0(7Qiek*Mh<=!~ zzO2s;qasd4ad#>MN^yi;Qua7?cH7A$wL18pWTZ{kGzCp^owwNBJHv$Kc0wR2%JE0g zL2~txNChe(gL!VkjsaBjH2v_CZI0d?rmsiMa4q9It5;j!8~lCZLQD8YWG3LcQR-*&vIQrakTCr@1LLT& zG%wm#m89Rpo-dq$f;bWa2nEvv#sTN~sJQv~@xw8ipAhweuU}iODcdhQKYVv=rJjG9 z;A~?<3Kr6Jiyw$fK{kA$Y`@S#SbXQxS{+G=6sB)DzrE3-=4=lBVJ# zO)IGZGh6p&K+N{e3LKa)#OZjFX~esa8dRB8&#i~9yvY#uY-R%MJn??10k|K-% z+JtoMq4g!aMvEl;c36`A$LS8nmKH0>yT#Ore<3|QlcqxayA44UzRJGfF;GE9>dIQ^B>ETK)745D>c5ER5~i>1gy+yfs8_Itxlt9DM;hT`eAXnaZqWdQitKu<7EFoX#3 zw5P;=ay+={7+Pl%U5xdI1*{~z6lxe@sCKH8xy%mDUDL9L+RwGza&~ecytK~0v;6&> zB0ZjTdo%^?i~E)7Ljh>5_^bY#00-vG=0Bm_a&<;D`+}+bX%z!Vr>5m;SQYN6k4o#t zmA=yydFX?g2;5Z3-U)_GtzH^JF?`xmum*}#>37*Eh z`>qXrDELI;$q4A?_Z*+PG^+p5RO=hwIxLu~b1Z=CKK3B2^-Xx_&?0EAIIN-YQ?cJA zVPZxESnkG&q}+0TnB9;Lf2V|Eami=Jf(@OHUQ8Wy+Zm=nl5kz{v!b31VFW?ZL_U6y z2)*zs9LaW|jfn)jIF0fLO3aW)CLFs_5Ia3Cr(6vxE|%4fmIj8L*Oa8CAX_gb)UA!UE=cce z^-ql@l`&j#zYG;0`e~_8X1~=4qfbv~1y}x<93$jTKDe(@*+8nfp4-wbH*`GQL)iNi z;zn+~mqI8$iF+O%@$I1@h=ALK)QtsMA{wir5f%O&KuWIT2e{(9H7Nln`x21JNB{Ycf5Q!$ELXUV z?ZH#&zBu$XF+(qZ*d1y#(jk*9zTDqPQqf8uwm)!B+oHH+zB)S$`Ed?YSV?j;>`f4d zppy4flQ5Jpg~ArccCO9Lfp7aEz+o`7N}tr3qC5nE_&!y3{CI0Jen& z%Vf96l7&Ll5~|uuC9_*>gr0a>AOL#`hliSS?VXoxvOBihIeT*UcL1gW(uJbAT7rnL zDk`bvA^^E)A{KxB`{b5^%iDLo^S$}+@8|vA_i1F2wvGji@XC93h~&F=Xykx6G#9L` z$ohOMV?ctyPS$QDL&e*Xn4;s~3y*w0>Vwg@sIc}H{TS%oOTT5YIAT=*?yGDuOKEou zHG=!BXbfRy;X?bU7jK*>cUIv>?H0>^^57KNmK+Cb>H6`%zBJ?-9C7j^l`Lh}r7Cds zO%|l?V#Ny5oDr7`^@&9CH3Jt4VUbh|ZBRKTL2g{xOny*&D6%nV=fg+`3sr9jm?gcb zyc27w03Adf-|Uzxc-^B9yri%;m#FFZ3cw)rogt6~Nm#d~^sk{&^;Q7wt(npV`|2DV zN+Lah*2{RkVG)m|=*;qA=XVQtg?b5yCE3U=kPiV{_ioL?4119v4Tz?;*8E}K>e-K` zJC2}m^tECuK=LW?ZExokoNbOzI<+xaE>@5l?Qaz0RkoOg&8th~hf#YAorkr<*!yy@ z`=)oXJ$PK+*IRoQ>gVBN!-*14agI3K(kDXeNuG@!-4{$`lBJx?%($q8_@Up=>=m{e}o2i`A5{Q1w&laz{b@-B~SP zDx!M+`_UY$eRf>6epiI@cl#sD(#VVseQL33TIc&74hqT4)>n;1V|y!Cec(Bbn<*7_ zyKAFmcF`D}q|z2UKV@C!}QkF$T|jc(L{BSuaDw9yG+X@p6N7wX-;V&C z6UGWA#cIyn<81vECZ<7StUv`dVMHqEi!xG>JmOCQ*&;XRB0T)n#Hpkq-)rhll=)xI z!$=5+4FpjCL+WJv^3$q9GtlA5_hzvWytu)bs+$sVcZ1-?i{I~_4Voa|Z!T*<0KH~Q z<=Qv_7tX7h{x3u&9gjZ9Wq!$Y+3+|7tm^n2vn8u##Wi&to^oG% z`%-8GHHy;fhzm6a&BLL`k$aHwozrqV3*8oPJ8Ev_U=G9)puJJo-@u0Zt0=aad@>@FE~@=i;S2)x4snG z?mF}%8aF)^Y5(TF1y*JV8o#v&rP`pEQ|B;T)UTXBUM@4+t}FwTx6N!^ue}*Jo_-&D zxf8f@d$_z_ks@YdbS>d=>Iuk1l@$1=Oq4fs@ZWOJmwIeel?+te^e?z(%#M>5JD$@C zix9V$tIzUmU`oJysM{TK<(0mE*prWQ8wq)}M`A_Zq?cg-h>X z8o_@GLWD^1bm&XY;|j%Wz4MD2`1I~RWkhP)Tq{RgfVeH*> z{I&cmb?109ojyK-@OgUO3>+>;{&P>DtT4AF$|xl`IAZRNe@C~_>FD3<&^xshYTK@c z4oCc#w0AMRFggUakV5E zC&=8-ET84yklu& z#11u3OJt{*8#ldbTQ_<(Ke_qiU9PXaY`Ht+XkV)?X1{#^O4Z&D<{Zmrr63eJ%4V^R z%7?D3BG#d6r^7+%xT^RCE!$;$t|9$zKbie){J6!o&$(pViwj*aO3^4VATtAyx9eZi zG+zZS zd>xOupI^=WD5Ir8II{ZiSTLG8v^C|ZdPu>Yof&i^%dQg9o~MVL*ijoCUmjH_!Zi)d z{Qcp{0g@>rl*n_+t>mB8-W={j!za!Zmc9nrn^Xm+vn+xg-S?Cqo28C+))s@~@y01* z%Vb&(wBlT4ZgLx2@*Kl;m`g6ajVnm@h3jgj{VfndN-a6fURocW+M00%G8(6U`9xY> zkz4zG8-7Gv<@D#rzcJePtJ?oBtr-g)qM!`+_;W6TUJhajq{Imth%K2Z8w-Igv2G~gC}5X8hjKdNRv zz{K`vE{11vIF=`erNLQ4iDGGQ$&fiGXV!+j28&msbSQ$(F50uD(<{f zncJ<4gM~6~;@4dl(}>7hrj}}0T9z|Qm{LmXysRc8cI#y_7&2A08;vuXJCIPkC7C6! z_$iKiz?~rRAd?Li(s@Un6qE3euf;^Ambn&*=&=N(KK7I#KQ{^T!etQ_p6?#MGkHlq zNN$)GmL@c^8psi}4J7(;4_X$ZR+@LmV3$~mx&beqjhrqn1Y#1Drk%d38CeFXte)Gt zD7U%fzFtpsOXUwq%MkF(qd>7=HB&_^=ZyT~?*~WK%ZQyN;(LQTk&Qi+ao3AfxmIV= z)G;zXxb(5P_PhBW?sBeX1*;Sm;GdkLKFDdUSeXN$e<~?*h%Jj<*1KI7)rpLSoGcjx zrSIt{WRsO^y3^}o^Z-LdN4#6nGNzCvIQ6keia=UkD)9L0X-+R!DrRL1D&zmL)>^)l z_~fl|FND*vOIrlUTn56P!gJWv7K-E!^ww*l?_vn!>8c3?$NMbuCa5$uge4q9IC7(kI8!6#a2=q2_*q)kIObN0_&D zha?&)1;DjzF!7X@=6d7FAH~=bsFT3`NWQt8epZu@!rhJxeG@~Wwipzp5f^Q)>jd*# z3j5Af&LC5{XuJrLYRX8{OJu2- zit<-PNS!p4I*6)rgS3wzm-5Ar|Rf8x>hk zTq(oJTFX#+a99>ML3;*KhXG1yXrDRN)JjdSReUjB9I~SXy~xu^PhOq?R5CQu#a5Y) zUYbiUO)OYHGWQTv*xd~l!y?|k@|sysC(`9!Q%m4~$DQI{Q)2ZMIbaEFp^LOn@sh^j zP2U<%8wW7;a}wnEW-fDK=`6N8<8JG897W$X+Bujw{G!}K_&Z-8wb{AG4x4U*ezf(% zwK8l`g>fZDrA+@k6KPPGbM|KQNMh^zLTkI8Jdi%7OFs`MmIYRz)t1_wi1@0*hUv7; z0v!^V$H;<-BxJU%yst%vssapW3*Tf9UB`-8vG&eIx8G<)uYm4J{Y3Oz|nurGGK8=fqQ@CnK8 zagBS+e^ZQVM63ux;`XB$pHJQ*7zvI(ad+C^z~iHWJRGmuKd^`$C|5r~iY2O^qh8^< zmvNti>#r=KNut?cW7;ZE%n|mYcrlAD_$*5?6kNlsVG=|b&Syp!t57{oA?oC&*4|@q zT-@R{vchVehsrSIrHF5zKE#>mLAsHS3wg(iW+o0YH%!|ps%Y6jLo_S4i)p%oRxTl` zVayTI;=vauR&P?K855?MB7p0Fg;`fL9AvTRsvb9^V;RcJb-2@!c@=EXiSHTa!Z`ds zG>>ThhFV2jhr!$t9iIugd^2N|mN06?vwwKl?S~;oegE#|_~NfE{9`kDi3Yo( zif}CDX3h`t<#H^~6kFzuVdIAnl6eI3dGEY@i6=a_eT>26PT9;i^R(K3*bBPA_8JGRA#ndz2;kVXIX9NvmO7X-|vxy z&XXXJF%qpN0R8IQA%`#J35kP;A`dz<1C;d@qRF!03;>RcmfT53zm@69 zUZk*ZQ1OxP{y-#))w(7W_msgg*wbz%#fA&B8@X(Jyg#5BgPq-*EM{U*EWK zx%@8S0fbU9B*xD4dzr}z%(@{C>cZ}CJO4YHEjhE6TnP6P>jfV78qazq#nf-8-#VSG z{|f3q((wFFRU7qt8mQ|p8Xt!=98|jEhTxWCtR-r7p{F!HsVBmd$%b*cCGvo!%ti z`}c!&@iA843$DLcF^TJ~dY?Fi^^fgZSK42es_vu0yv^diSK$^aEB*YUQ=)r;hT=DP zq2Yza=8QKH72=yBuq^p}QxJp2Jd*HRNJN_`U!}KYx%xZd6*%ir`z!BJ0>mzNLs2WZ z)bBpN74}dgL&YI%#rEVw{-+;y#e2~55ttG19nlAtwg(b4AMpPdf-Bv^FPOW!74}`q z%v6Q<%Fn|Vb>ZTs2-qYY>cBmk^N};|f(E(ZFCwcGtPthfAf6zf@<#C~qPB-sCF995 zZ^wndl7-Ni*7>g$h4@TDJY~QE6Lz@9KJ!%s$M-C@-__$#Y7@*dSvlQ603lNrTR(SfW~Ot#0gf9PkrO2Q}1e%MEL1<@Ic z<;OD>Pwkt?Z9el9Ak3Pb`{Xo>#XnEsj2PEoy!nzrw|H=H_Xzpu(@Z%>72T4LN##rG zg5nK=wG!EX47A<7M7nh?l|_vAp4NlU)Hp`R)n<&0y093$2Joead7Imo)lVexSQ4e; z1)-_3@eUP0P=|(MkSR<6%(@n$-pPB&#kq<6gcs9MKNjK!^XuS|F3a3#nT7)3)Mx_5 z4@|GDC(hGWBCOF6qKqy@L2$t>&P=;>i5v`G(gNL1JS8GVed^uzD7`Jky2Hzf=&tqJ zr!e1EHK`CcM+)SN0-D|zBQ4tEqr9a&Xc*5mid;l3K*)C%yU(xFiQKkc66jb9fP20b zEwhVx%kth-iGMd-Sif7}V!M?3?0hBogc2?Q=Z!|ToYgko0hhWOTPWz^u?|BU&F9iV-qCA%5b#K2kl@yL_eB>B$- z(exCp8F*I=w$}vrZZ=fj?d<2x?%S;KhUg6gaHp_OV^458flr;no|9UWnB*5!*;j%r z-Y`e&o>aZhC759>RlklfgN zNPW=s7I}VKqImd9yo7Y>(*%uoYTp?eR>MG5m677@>6nEl%hSkE(GaRsja=_9BpYyN@ zP|9my{jAJdbP!!mzi5@|ZC}-Z7AxJJi&uQ$4o05yTH#M4mj4cC6E22F5{{|U;e)Gf~{Z*n{U5CMDgs$S( zYxMee*`>-B))7sLRnCJ!7bQ3Iu1-Ja*`L0~h@e!uKc@=+_3QnaZu)&G0#{^lSS1K{7gX?g394m$D#`@qA z$@li_wpE>B?X=}LPxil0u@D|{nenkk5*97VVKkNtHLNlUH}=`UH<1)aJ$@d9wkNbP z#=H{IUN=x#I^q0O^DrXD2dW#9>+57p*U3^}-k=bSy89eX%Y_h2G|?DZ+0qQdTebRr z7?b0lBOIf~C%{i%m~|v2nz`U_zx(HxH{6rwjJd>;OMcABu(hTP>DwaULttrkmqQCI znsT`hf;J3VB@?RSskWGBPOoC;Hk89OwJsIZ3bd^pP4e8(T{&e-bx&?1Yu}}da3;=j zruVjoB`DxNO==y83wu{Fi$I4(S;UPiHvyFH6s2aLVBuVAg=tus=1aXdMa{qyC~mwW z<5)7}H)qI;0u7pOB|5Vc5jE(>it1~obMsRw8Zf(AC)U}w+Py|WT4(bpNJt@^FNRwZ zJvjLO_@DJ}$Wy~8Lm6y#W`LW*>y^Es#EQ~C4*F%b-!+BtNb@gR_50+RM(l)Q`b&F^ zhj8#)DPwJUNvkdy^H((x;spJH^ki4_7PmhW7T9-B<0I%YguavOujv{vOYRJd$6%jR z3xL8lFr_2)wMC3!K8r@xPGNM=ou>#%1WReK3PLuR!@C7`<*@e!D|$N&1Nvo6%bxS+ zxGR0ZqMRwe8BmDuc2t)#reaI42olyCb{FIBMXM&;@Rt;twDAK=Nhj1_WMoT%jWHHe zFjMVriH2)_>--4A;WF68kSwl=nY-^{M9|o&=b0J@z+b4zpabu|1H`i1EBMJ@BE>-t z7s2MBgpwN$aeT6+^e8S_Qrw8hs17SAIA!CTEi)kHRN1oF_wodOoi9JlTvfL0+TGIg zNm@$)n(b7jz!1F$k5mZSDw*BUbJ$(cbR=%*um|i=2enf@!som9!ojR({k)61 zJP9khIHMtU&&`mIBVYpxW9VMM)JX4s#J+iwt9ZAgVQ?CR`sS)}m#97y zWwx%Ek`6qDz+w!Ea;=*-%$tH}DdForS0fP)cp+N6mpLJ{w)B;Ut(*L+(l%>z@<1ZBAbgVE;deQBim4PyUM6um*47r21p+dUfa<31NL2jM>MiZvHXsvDe}lE)JXL zz$Q&X&op8D3U5YM{;{Br1@BUsD z#%t`j?#km2a&46NWlmm`zVMNU+A%icfGyvK#gbWBJ8<>9r#!yHKWAY8C0tl>JIXmZtl- zFLpdaW>tC@SDPW4#XcU{W+PD#IM{NFNnd^(0u@rIf@bD6E(nw5m()99C3P@WY0rGC zP;nB)k?zd4CptR1;nm!!c%SgHf7F`Uz87ya?$rYAMrHUpSGIq!_t0R$4TscUDT&!X z{5Uiy59u)_euQA$d3c}HgjX1PRX3DXhpH&6i|cn|wfcUAxn$LKWc!oQ>nFmAlwZ%p zB$aYO3-bLEg3js8-@>=t8a)~ds6LwCydC3xT^PCxs9VGdLzUZ;8WWSg{DF1uh;v?O zcnKHelRL^SdimbO^Se#2)8=0!Sslb!!Q8y)MmL}RV|YkOIDTQxxu)k~Ay<&aaDD`B z;tmiels^y4U2B7L>=B;h6*tKkfuIQQ5YMOLo z*&n`a{-wuFiJ(<3LA)lnTye#s>}o3H8|t)wYso}@65=YCr3q!%Idrf18CDXfSI1lB;_tH z6*tZzL#1rc%7m8vPAdaq&+UrWErgUoFE}}6d$Vq2pH680^o#M-hO@BsN-c7hbcJ%< z)#jsVO1;{a=qaZ|W#rI!=%_f9t8-@DCNzh%xLy|>PrqMs=n zH6v61E^@1kvV*+}{d5~>_jS=A>Z|cey7DZYE?yKeYaKxcpQfrTHbvPKUB0xYrNX6? zszi&SdKTMsB&VSk`_B9AzD$qu`8PUhN6TZ^!r=OU|AN_V zF>;Dh?t>X2c0bUCKgooMe+(7DP&Yq~=O90^iB?Rbk4h8HDaH$oUCpx&vu$i;2gXD* zM6cCoTQb?5PkvadD+RL?ELRCzppkCwq0?8)gA`l$9OZwb^M6RL>3b+zoF+S6SZ z;J=J(Dy(x&)lNCkFR8D%OdWLLq}lM5X7A~YSAi_~qJ<06xjShWhJy#Q^-~QhN=tMr zA12NG-QRGAX~aWK_K3~`{%CkQb-%@GSr5WhCT}qeI%-L5?NRxleSZ0Xtj)7L=eo-BvM&&tbf89Q!%LnVaKc+PipXdY&QR|yLsd`{V60po{5Sg( z-KP~}e>nzAm#?AA>rJR*m?OBR*r~=%Zf>Y)qj$^j;{CP_xw4_D0Elgjj$m> z_qDM>B0QG*m_Bt@4=auEy;ABhgnsN{4Q(^du9Tw?HQxEtnTN%1Q9v0VK#V~6);d7?+~@R zb@!@wLc=qFOr$(5p?Jzt^`FIl>f)9e{^}m7wiPz-bfHMq>-#T0Qrvu+*aC}^NtUIl z@Wj~JjM|CO!_DuDyaCvcs!ue$uLuQnxCL2wlhTy&CVOqfk@O?BRJO=DQv5zR@_~*u zbA1y=fbEUINkf}(-gVBTZCc9sBeS|{UI6D>PL+yMHa^54Se?}N`*iF{OlGin)BOZ^ z1GVTPTcoF4r+Qa};Q{;Z98j{e2BzQiyfTQ2|5q2BjuvgfSsyz;O1(MkE)n)FN1K_) z-5u{u7ejji73KYQgXf_W?T+8pj`h#lm57;;R=&)`Svj`;r*SzAu z(Ccn(>o*gPtZ|1_kR(QM6^KMHoJ%SN3Gnj78QP)aqw%X;=Pr%=zmm0`6jhg9M@PgZ26V7&wQX+ z#6ck=W2NZ4o1p1Q-;V5yj$FeJF-$`C0*^AfKkOg#ONmk-@j~gbKKu%1{TAsQ6__P@ zM9Sn*ByP8nM;X<5yM(}u(I;p7dO`4r;D(dnnt`U6UBgRjdf@F^+~+xOYMV@|H`w#M zdKE@%46$)%i-&*1(0!hNh};I6pxNy+RB;vNq*YM59G_7bkU+T>C$|Uwpx3_5NUhZf zHb3m2^yNeTP*gV%|Mc(^n7v=cQXL;pZIrmZ;V=EZkl;6^Ea$;sPmf3@_Gx8nvd#2Z zA+A*4ZW}vLiz`ye%>IE%<5y{2^afYz5=u@*8a8w?WXG6zhpR@Oe4Xbzr#A18-}29U z30frI8CjyzG%Czn0(-Tph#7*z+P2$>112g3(op<92ca z&bF2uX|`q_WT8j>aqgZP&kAn3&2u$JOJy?$W42{fJiu!OeXdPQyB-C^Cn2?ViON+l zX0k-;w%O-!!%~f4dLNAors*SJ7dOGR&I?PL$QT=}=o$zZqf8hslVx|t&)=lc2y>BK z`>$xPRGN?A=~Gf%MaE4Q5~h%KK6R{j-^An)<(lI^N#TI^I`%s!fFMOTM$caXxPF`> z?1ye<`gbk1Lk(E0`(L1A4(^?LaiI)eS42&b$Smai-B6{USI44yQDx!Br*$(Zqt`|d z1wgzjK@PvBsFwT^+g$hw9PuM{BUp`xOC~MFJ7$U^3I`*bud+9GB@M0@XF``xC$t** z&(nK@7%9F;_7gz@d*g}RpB1cj zm+dP!|F+Djr-4w&#g2!V>c88#2^-ChXy#vH^}=&NPrq=6Jj=AGk+3C~4Ck!LqQuob znM4w;`CdWtY1LU$30EH9CfI;c$$$l}d+LO#K_(TbxbaS#egSRpb)bwDI^qmD8NV`~s;v z2!j))W`}2vwKVBSYLS^7wyf+gYd#*-Yqn_#Ysy{|D`p? z?66#FG53GuPf5HbsJ-wqQIN*!SN^Q7u3lY10SgMw-TB?>Ei6Wb z3%sY&`;@~a_3C=cW7##J`?n&1L!K5?Eby$o@L-{R zw9o!vJqLUygh+!pricXx^6~NEx9^mjTQ($(n3${5eU_%5hIzGgMYTHav)F~bD=3*O zI&r=CGY|wRBU0#2q?+MyhMG!6>ai7NT_|)?)DL;mS&&%yltR45Q$MH@^^{e1Jp`)C z#}i|Sn#tZGXA|*f*A#;`Xw&6vri%r++UgWy68^Qfx+(B-6^lhLZG88D&+TvpaT)kE zctu)T&)V=5BR}mr#&$Rb)d+R5Y}%?qwZu_9kPX@*dFpf{rl=mY81UU5Fk8tmZ4lSS zq*?SWkOM8h|LaqU2GiOfz2HsFA{a8s{RHLm!eZi?4z}4@ht{v=op?-mn$AlU%35*I z{DuF9nfoLi`NVt7zAvnOn^$FGjkR8<44UtBf_EB78z4ujEMebzn>@{gTAEJ_v=<>m zpGR-)o&BOa(EU1FJPuA6+WwFstu(BL#BsDSYW%#{?D9x)6BNz>n;Rj-D%TN1^9(Hx z8}uY4nJRIdKTn_IcL_z<#~Tiv8ot$`2<7QQiJReGhA15uqrHFEH)D;xcovH6x2VO^Sax>~_)F*xOXN5q1!t4sq`e(%Uv9AdoMAS1XUwr`(o{G*{0R zSe>#6FYz;4CCy?e;bcXIrZms@>1{^pM)y9zMU>_M7Lh3}YMnTvw_bN3$Rk}@{L}ex7a8Abh z*L#*rbvJcM^0P*aHTNOE_XrU(an<0L?AG9vm8Tj6^8$zNf+r0-EX?1=Rhg?!b@~_? zm-uEQZzA;%?8|*9Ftrj^dpmUb%_`lb0|$9Tr$^Kk(_GZY+5oalTk0Ang&lPKl|l!= zh4V-k*W@13toyzmM~KdxbQp6=x~#h4i}0GSd#pgvi(M$J-KbIS^Af_o`q>U?{Zq=$t#~fq9ju}sFVR|Itck&_ zsS>+tKFKGZsLbN~z6^yC^l8`Nc*kg3w6iRZup;nNSRWM`OpnH5b>J@kw%q`~U1vNcr znFfcr5D1Y`y=1I5r?gnTxFpeZX9%=NXVFSzWsUQ=4!aFU{R~8mY~3WTB#llMGZAd;)W1t`)!5*Wv}qxQ?uit z%PwvwDSOc-N$&7QB|Wqa%XErSd*qha1|2?*@k2oEEk%Z}1s-->U^227yfsd>CGP|d zar(kc{-``AzTe6Bako(*FtW!8herS{UpaNZ@oRWvWtVDp<%8-^$aa* z=_7vn%0f^uOpAF%R{DpQ+xJ$SO0oyu7!SN*?++IsCDt;{%UcrQ^<{=hdgx4(BO6}= zGxLd548e`}V3_^eu^PNbVXXeS{8bRwuj4f!4u#Vh7UBn4AzH0RC^jHlv9M7d7Y*~Ami&KU15JR}aZdNv>)-Lj^?rg-B zS#~j>z~Wdz84H6q)z!J%=x;I|@7uE(;Ts`XDZ6W#@|1bQWAv0IRArj4u*hyb#gh*w zd-zppV6kVLAWb1+!ED}Xd?_r7rs|OoFR(2ro6Hx-D`Wij-*+OCPF@kFA`8Lg<%Mt} zyIiOUBRgll9pfn}fp(e_5d`-Yizo4lto>#5x4Bh;uI;Q2QOU4EP%t?UiRjG=|1$4I zQToD7v@(qFZ`_l0{xt{D?(%=Cvll=t-0uECAT0hP0gw=5K0jw%>@H2J8F3Xy@4Fwx zY8N$le@Qqzx!Swkyqg|&ueiG>x{HhI9$<3PCJR1I46BseSK=R#vf#xDYzigmJ#BmUxUXw=vUC)4*zVd8~Cz3L+&f=fW9JVcj*J0^DN1pp7n5b4#nomt0iBq&eLc8=sSp{G;Zd=!l`2 z#{iYZx8YXx`uQ;urJEiSBYyzL>H#{1y$(*=pG&}h={|7A1M4N|#fXjeynjn`keNSZ zA{a6`dO*9hhjZ$JjGD?saR`4r2epp`GzwND$EeO1-OAHkty-1R^(U$m1#d@+BJg){ zxN3rOB;M?nN_w5{7Y+xca&U3LHPJQLYMQsV8ZH&wnbP0py^9iCIy!SBD>r~HsMGPZCxaI347)ft^fl!o-uuv{EG zEWJ7HMC!l(NAEFZJD$_1c=;@1H?mKS7oHy!so;`!TJU8tlfGw#JXdVB7CKsXz1@^K zt3v=qt+novC|Q%}wW^Wyc?S(HH3^cbZ~TUfHy;GL%Fbd-1Af~dt19@ zWcc3`B)FVkf(1Rc>bQ|B;YD4q?9WY)*~LSZk>Z^qKF9nk6nmzsK8It)arwS0cH7ab zwdJ{Jm|=EJ|M5#RSjF74q2#FC($@nRn^rJ*$#?EsV`>hD*036E)UPouxl>}t{hNKr z!M!S)dpYZhyLK+gICebvJ;oBdp}6iLF0%)PcczV9+-SGVkl`lXItv1gFk`N~4G~5I z$*1fs+IdC?J0>^PeXJV;S+GZagoWg-KV-@rCpIYOKVxv|vIkL5$)qlL`+$G@3}Jb3 zE4F%T;p6I$fH?}hRvS}%lklF(I7gAO+oCHnA!f`Cz2%2Nz{bk)))7?MKSZt#Rr%Mcog`pqI+NW@2KseDwHCECi>qy9mRx^5iqJ(3Zv19a$ zb6mmT?P_mL>2@5?I?SIkfLxuPzS z%H(*-8YxZz5kY9oJah0O`~zPOZomrwM%-AyCDPMR-=88>;_AA%mAq5Wk8GAL3lGC! z{pX*Q=M{`_CvZP*xq|BLTwy82XXd`gUO)E*2auDr)yaOeUa%6X`+qZ8p*@72K991n zpu@ADUX`P=vfhj*LwTrprcx{yOUQA*x|01{Ed>V7c5FV!*ssP9j7D7Lri@4U zk=WfwGs(_+L~OwAOL{tXx+(`{j;<}&S0DAVUlu%S@ym111Pu0#hA$m?)`%p79e$+8 zJ+1#Z+v0h-0Cl1$7yH#Eb2Mid+;}9R67tiiE$8ipiClo+!VmDgfk=OB1rMT1xy!xPry~R z7jnA|aLf40F(&xx%^m6Be2)cgbjk4D&u2JOrwSZRycWpc6Lnv*xOX|4XKzHY zStnoY;u6t2X>0i))x#~OHw-N`k{--By=w#KPC8~iYa%q@de8puU6aiqwb+}|oojP3 zP1E%1D&q6_reR|yxC|cJI>eIlrbPSt}W0rlXC(Cx|g~}nC_)oUTuLvzXxMSj_ zX58Kd$F^r-_(QFn>+?fcG{6_d5O1C5X4rIaPxM8uv53zyW<%GI>%kld1Ow*EOCJhU zGWm>d=6#)7@3*p;tl>ekGEaFTpfW{V65+0fTy}G`8WnXW_&oo!KJx4&3p+UP^05rO z^<{J(OowZUk{)oHuOxv8sOvcc*Wx{FixmKoAr07Hbc zBED=|n$iwK*fY^nlOv1;vIM`s>v=ZPjswcMB)?jz*ZuS6I^Cuq_Io?We{kZVNLgb1 z8+D7RJ)cXJe#8Pb3ZATZJp8VEi#Ppf=N>qU!Kua1R+~n?KLl`FrWU2tEQj)>iV;)l zi2HR{e*a5k8i%wW_FY27;1BwbyOeVfP zb71oU_n+P1M#b*V#CAxCq379u*U@^{SRkXYggf1D<~qaLE}9wx%-e@Rw)1 zQ6R0FAd)f_+a8PjJL)T5tifFkSKJE^M&n1=L{={EZKUqf4|DLyVA1FSQ9wWiN z`>2XcQ~cR>%55V9iJHNv+YEdYVc;GMT{sTgaM}!L;cITtbJP3t=S;-YlF!reX+c_3 zP3AFTU?$7v-%VAimXgw)&A4ON0wEapGilr`p{IC}K=!(yVA{WpcH&!vRpAXRz>!PD zA4~74FA9ddaiF(yFw`|N3_FW2G-KN<-+GQH`Cck?@VvT}MtSA7ybgZXVg|vY1WIO< z_7yOK(Sk-zee~5ZdTV;w|EN%tPBp?mPfo2e3X7y%qsz({+Lz9Gb@P{gtBmSvgtcR? zMdjUly2MVN3wT)46tuYh`9%CU19XMqO1;0miC6N>hkc?hNQWBVP2gH~s4yJ441u`3 z_!q%PwQsLBYjZlXdfw7{Np!IN)KANqzkFyNOY~lU`$|Zum z5#uCt5|yUsLv1HRV5Z${KSpGHV&g|OMRlQ z5au5LVf}Hn&${&!DSbRhRXcBjaS&8Ot=+R(#Y=B?+J#9C;u13ZZQW89O9gQXS<%Q_w6ok@w$K9Ta2^T+4kE=uSXMZsE>CIx3AC60z>It%5K8O<+J;4&z-;QyrIEE zKC~RAyW3Lf##gIcy=wu&ZgTDI2w|)}W0{T2bHR6f@nEWaaU{2|MY*ZHdR_BR+?d)< z>e=nPI-aFy^|@W?c~n!k@;|c`xowx|hBpoS%yZ7Wc#{nNLXf<4n%bNC)T$jk!?_pd z=|nqz_`lOAR*V2#_XpA8=8(i{1DzD#tdpkCEIWlBKjpV=de!m2v6FxyF#&0C%>XG^ z?H#$RmWMd?lEdpteGh~iYhj2oqX`NxA)OSO;R^*Ys@8Hq(`Koqphn#*#)|87#{PNo0>Mws+>dX({OC3qEFZC( zfmyALCup5NapQ3}gHF_cuHz=+-XUEDoPY%veXs0PtUhemuVmDfe&@k;;Jg8=mR=y8 z1Y8E}2c6X`fDKpO_aVf&L0>h_td+H}dXFVx!Esnx|03WN2esEVQb>aTz@tRx0Z&f8 z65ux6YrcrB=iYeW8@Y{sy zToqh}`xp#(DeP4i1Ru^MKfME$0U#(Bx)RT-PcS38(kp&6Pz7Kr^2HY@fxB}O={Ewr zwEKj7iJ9krQu_EEJ{{(N0R&ewT?D+^+7E!@xQ&3z0>YPQsaj*K(%XS9KY=nrQELRM zN6#}7?xk135;;&5m)9xoMjPN7be;c_4Gj8&4Zz47nI!a9+eU!=Gxt}8>%~NIXFEzN z4#hxm!iqi0)~!KGhe`r9yzA+|buRup?V;#;$S!z+{E&t)&bZ~S-#2677GdwB%vrtc z)4Vv!M?rTkjam|Yk`|cNRl0*FR(;(kZQzari(hY_szikODzwgh`SV;3S1xPxY-skc z1k3l~d5S-N6izwU=$-riS;eCD6HPEo#cUX_=%Bf{h`IgYE_XThnlWlbtKFjWH!HnN z4m;bS^Hbdpd6e9dI}0H5D|ja5V+OXXtcbJz}sXuOqAC=HpH_<63$=H0=ZN3*qFe60*F{QjhXa35Wmc8|FOF&{u7 zNlXuQ{wKzlp+*^YLX9yXlfrg{Q|Wbm`Rt~P3}r24nzGdIkT`7KQbpqziLS?Q<(toP z=3Sf?pl|5Hh(UH(Xkp<)GPr&f*K_@y_jWi)D~joMAihIAE-~v||AY8q3!c#BN3vdU z8$gq38|8k}f(!(L_)&ru%9jk~!hqhC=c~87rMpIv;zT=zfxq@2p~=H06kv5hS%?+D z-1&&XwRgZD<&nh9nV z31KLKbb9&c?5UNudm3>#&VyTT(u|;oWB#-)LnMd|9=B2-ZxzJF50SYipmZ5t#I1tI zaEDUQ-5KFgUVE112$Q9j0srKP&Kiy`>|xgK(b#f65*FnnZl`n8-_z0BI2R=ztTnT; zJW1^T=FYodqG#s)$})!cqsjao~SjK-4XB?7cIgatPxNQ0KdR*_k zo~V{Re_F!mp?8bv8QXw5^(i4D5%B}9^W?@A%liK7$IPOW>m!|QCQCckt+GGX0PdS~ z2PZPMHCkDFByxx6+kS5_q}=iV%@>yHEyH@xW!T)G>0c7{B>l51McvD9Y{h<9$@8aO zNRE2CzpvIqq|O<$4xH=X@!X9!rbp82-wF=zvu@Gm#$8}{lEW`hMNV_$nvCBSbjm5a zxv_}|W@{^Y^`rY6yU5*w{|^z68OvqffPnJUE_RrI$?!HI1T3L-N!W-8%$z#GKwPO$ z^{7wf5S`kTRKwM_AL{>5q?lOME;DVzwt`NWOX~etK^=<8J%+9usil0F#pR{r{t(>eA9z2xQ zA+MjnOqsOGu65}f|Bp4h(N%f3FttWc$YGBs^PX#UNhp3ds>GpujnB=sY_f7q*8TgD zbmf2Q1`2Mb%?=A9`lmiEI7^*or&%4I_Bm~y+9&&#ozZ7qtDYV>J0kU-A6H9V*3vIE zhT@2S|MKeEW{6(r^;+Hz3lkOF@l2$Y|G~DKG~}q+Rp8xq{9jdWllSs*SaY1R55-AX zgW;Hu+0F3S2AgmDEx{gcdbh=6;oekMcQj1o5Vxp188&(uH{YZE5(3*l-e~lPz_m{E zTK%`4O|SEEUwB~mmm8yCrl;0D%BtJa>AX+g*Gsu0Fjpw$(mf#i$Npj-`6FN{kMYqm z14$pPj`6#igb`Bo+juewN2I}b7pzp;z1+V6O?;J=R+U-{_0^WTGtJGmk+YSi znoA3#)hT-HEn`h1EL78dMrgsroVt?ss0Up6i+yo#`?1owHefQGbG-Z+hSR-Wc zgB84DO!&jsX~RRdpgGdo>|Rfmd6&2onW9dvIlNT_*d`HI1Xr zlulQbNBh;4Bb02i9Ja5xMwPTjUbbt=Dx4i|(R4(R*7N-nif$ls&L@}abn?MHSsob) z-2{o1oKK%jzu#8Q!2j0&vNy?9!mp(dAB|wlG&Lvs`psEW#(+2iTPSoS^UjR#(-M@> zgMTnfgqbLS|Lq@=;XmTN;ijx`8tTYF)gj-vsM4e^)LH7(F=i)^jX9Eqrwi#Q#LWJn;iJvO6>EPI`76#Q;mL>aL%DTznAEcDGp zNhUIJR;PXQVFI31kvy!Cnsbr5fuw&z=fVTmNroxiljc>jWE?(WJM0c*4l@WfxYR?= zO~qgkVT}gCv{KYD)j!RPW6UrJ?`Y&C5EPdaRHN=D zpk?d{ecjbdiPcJATpG0aRIT?uIKtHEy{IDcM^bb{=l9*Yv{Lk#UWpJTcjtG#TJv#f zzrSDcjNQlJM#Fwbt%Ck?8u&4~@^vMwhJtY`1w`PC2I&hGKpU3q6> zJxgL~kq1eb3Dvs<$$q1mPA0_(f?|KB=)duuC?GQbs1?Vh`Gp3rHnKN7bUHjVv}6&1 zrlp7PCXJ5lNF$?Xil4yQ02fwNcM;5RV{PkTLjGb`{WojjD!IAU%P42;t?YH$1oq1!ag9OzDCu@l6;=6JtJva?^ z1ldcF$bp5xX$0v^5Lnf5HC&U<@idvHe!`6J@<04u)~(sUPFQAUnGeGk^TmVq?>2L_ p8VuRIp2BS7U&I10NPIVpf9kipzhnuhWlmf5B$HjdLHs|){{Uvs8AkvB literal 194048 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

    Wr8oZA$ekDc{Rh1 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

    E93tSv+ar&Pt>~-3!Fw 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

    Sc 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�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 S;$msY_(OVgH>baYy#EW;-ZuFF literal 746588 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%0AKlNW#Rvf!9O@ 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∨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 Z1OMxi-)T70EpeZ@8u`uLnfwmn{x=64Nvi+= diff --git a/qemu/pc-bios/openbios-sparc32 b/qemu/pc-bios/openbios-sparc32 index 0da11883bba7d78070e9ddb28dcd65a40e43fc80..e744e898b8461fbecafd0a3d681b467ba2e699c0 100644 GIT binary patch literal 381584 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%0ApPYhMh8ZKSJ!Ou1_85Q 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 literal 381512 zcmd44e|%I|o&SGkGMO1dfJw+NNN8^oN@B&5Qrco$n`^C#Zr7q#7u~K-psT2?qIO+& zKUFRvrLm%>DeYpnyS+e4v0_aT5wV(aj1+g#9a*ecs~y@}vDO+f(v*hG=lQyG2M*Y} zyWh|E5At|S?s=c{e!tKAywCf*&(C}A>epZMhDbDGPX0wrlyIbw3t|?S@*gb;Lyn1< zb`v+{O6RvkI00UH?lDt2BD4g*qyj0g{%t}k{Zd}T?(UQmvEZG+LC%UJeGs_JO5Y9~ zE5Vx<8FSy_0PjV`oq}gnhx9)9i4s2i?gmblU<==QAmvOVeFNzgrF7sv;K>%e9=Oti zdx57|a1ZcQ3-*AgS@2E3DGOc&TxG#G0td1yj{H{wPq)%n0M}UXa^PAEz5%$-g44kD z7JNN$g9R@Eo>7A5b&fVXQ2XTAn(t+TIwJqYq&Jq*fv*8>vfxF)vq~`ZGajhTr!0Dx z^m9w;&|d-USnxvNW(!^b++x8Uz^xWM7kIV>w*#MN!LxzqSg-@!X2DIs?IrkB{Wtj3 z|0|HMhV=7G>A)%AS6Xl-@LUT{0>8?FW55?!umPTT3a%dq?y%r7;0rBy6nMS`j{sj} z!OsFOu;3%W7hCXCz?WEX9{5rVehhe_1s?)_wFMspzRZGiz?WO_e&8!Ccpvaos0e+VSUkChd3tkNT*A{#Y@bwnF2>5R- z_-f$ySg;HHUJJegIBmfTfx9et0q{}_?f_n9!E=Fcu;6y!zqR1m!0)qQ2l)LK+yuPb zf*XK8V8J!OAGF{U@P{n85_p9LCxQRYf@8oRwqOJNkyCKpIPgjf9s~ZU1&;!M%z{UN zKW@R#0^exCM}Ys{f}aBZgazk;|G|PE172mphk*alf)4`!lLhC1|BnUl2foRI_W^&> zf`@@WWx*NX)fW5^uxG)$f!A2@PT+0}9t2)%!P|j*EO;C6%@#ZWyv~As;9D&CZs1-E z-U$3@3*G>Hs|EJ~f5w8>1FyH>Uf|DKa1Ze3EZ777yanF`+-Je7fN!(l8-e>RcqQ=d z7Q6y@g9R@KzQclV0KU_L)4>01!Pf(CwBRMcU$EfofbX*4#lZh!!PfxaZNZCx|J8!8 z2L3k-c7gA);46TA3tkAk$$}REZ?@nL;4KzB7kI#e+kx-3;Mu@iE!Y9R&w`tPw^?ul z@E0w(2KaspP62<(f-8ZyTW}Ki%N861e!zka@K;X3wd25p7CZ+0paqWt@37zz;ICTn zv%oto_z3XVEchwlT^5`N{<;M}2E5yX4*`F}f)4`kvEUr=H!XNS@Iw~75BOUaJPiD8 z3(f!!S@1)^84KPG{2dG437oayLE!IN@OI!~3*H9&JqsQH-fO`=@WU2-H}F0S-U$4C z3*G?y?-tw#{2vy)9(cb6_X7XGf_s2}Xu%%ve_HTOz&Q(E1^gomz7hC<1+N7DFAH7) ze9(fI1OM29ZvcM8g44i1vEb{04_WXM;GbIXb-<5W@M7SfS@1Q$k6G{{;GbLY)xf{7 zU>Ept3%&w4Z@~+J4_oj8;3q7&1Ncb`o(uex1-AqL(t>9LKW)Jd@UJYm3HXQwHvs?I zf@^@EvEUT&Z!EYH_*n~10{_;6W5CZ@umS$vDY#}Fc*KIofPZhnqrgWkcm((l7W^#m zs0AMZ{-Xsy1$@kc^T5wr@MFMZ7JLZ!1q(h1eB6R_z%N?xe&BHn-Uocbf`@@8EI0#P zD8Xf&qc^fp@ZRoJ+VrHhbYxqcrf&1M+j+K^nTQ!~ahx^Z%_o6pmvug#8ycU}L+V*r*I5~z9dW!eIJ~`x9b&kHzHL>saRW~O)ykcBm%5hc~3eR0m+Vsvp zuIj7rTG(Hm-tU@o{^mmBM01hitD6@Q=j^CUX9B#s<|W|mBkt{kgyHe?zEq1T@gV*$ zNgpoec}_L-%ZXdG=14K$S9gxCP@aADEr$3C@1+W&-GQej-N`KH9=ZY?)qkx z@i_1X@Ot18v+zcV?<>Lq-U#q|@c!xayk(9t{Kk+LqynVnczEuJg+3rqP~jlumie}7VT`}Me=Vg%G#wm{Sb6>L%I&=XtT54SCsYJw8_`ak$>2L zT|q}%ylQ8EV#o+zPw>ZFQ}$41-cXFR7r=?^Ntq;c6@=scDKqPi^PQCN`>IDdjKgJl4B8Wg1;GdugsC-R_!>pkKg8y+<-- z;2o9~%<@}-;bpBGXd{AjGNu4Sbk zGm(pcUDK5JO(rGX_E&dpK#w(jbxT)WOxmTz^h3E=*goqvts=b=yuM=jn|?(3dyDma zcnN8=|E~4mC%w&%iF&49WhRecELOx5kER#F>kWBi;E_4Sqlxz4)q_rxo}ZpnJyiH% zkMV0leP0Xi#-hHPc92h{#gq2m<%K*|FZ(Q>kCS#&U&=IPV@~RxjB8ZKuDQ@H%6ERy zS7u{fJJGM1j$as~eyGc}>uzx;^}Nv}!7qb8>Y9aLIcSzr)^ln^3x2uisLS_1WzsiN z_eseBeia;Akt3%%D(lS9_xU?-XYE?`)L-F7RA>9l$$F|&J$-|E5@EVIpnR z&2E!vdIj;XB>XC#^LYBuZ>>4JYFlk<>OjJY-Cx^vBYDQR*KTpB{z(ti9$twKtI^@F z&|%oNqZ9BwA{}}IrYh=1X38F@llQ1?BlmQ|x61|y*Anid+%-JknIjM4>Ir{_xVli+ zi}CXv3QOPUc?IcfiKD$wOo1jD)B}Am@t-7pc}RB^IE$=0`UGkC#|Z;Xv0jTbD~V5s zG#5it^p$n8x26w}hOa#F2WSEtR$ov2GUAtnH0{t7{dJM%deZQlC!U6;#**!w#4iqM z8lmwl*{&rGA9~`aRj2hCO>ZK8QAjft8mlcdT}9f}(CjCl1}mQ{h<8I8`v2BqpIDTQ z{=e%AXojF^vS{f4yA~E{8tMOA2QAs?|GO4IvjZB(qM`rq3hKo*jr9Mmb{nJr@0ttE zz0l0IXljXX5A%5xnnPATQ%Rc*%|>Y2Et(|pp+9W=0W>2PO@y>2Xg&kY+*34-YX1%7 z^L}Kb{fGTmpo2!W{~BmM1x<%V^BnQ1kmjq<6m?pp`4wrE(0mk{1s2U=;?*9N&sJ!P z{E9S>k`{yJ251&qGzW<{Adzh*z?M1r)S9W~xY+M$r{ozQKx=x!zLU^d~n@~85J z!VA)?9iKeANzbK*CXUF@P>xuC;@bs!y&uspM>MX_Ui<9={z?7BB>7tTpLObs>$?u^ zZQtzgZC|<#KbG8$uU4AIsTtxk#DkOHUEh2}KApVgHIsKJ=Un6*4CJVBkgPF{9J6`O z0e4o2gI{*`SvY?Mjw2byoMqSO>6(hCjyp>SigF!Wn{$@-BKvcykFam6%e5_C|LOqO zMGhy?ADvgY6Q6uAv2=ZZ#Y(dw`jtYit>vZ-6@$h#b1nu)V-oyZ%S{Ks=>xw%`jJ9k zMTgR_5Dj_!7`#c!gRzEem->ABMbfWi(KrR2lp_y)&f8z5dRCm$R5r?W$Tc&6gY11O zAGW-mvY#t|PW&&WgP`0&U*0%_^le3*%=`tkM~Z3kt&P>B4It0=kf&1obL~re7+*Bp zcA3+Fe9KgCJU`5{l4sZaqP#&psQ-aq&?X4emo>AmX<|Hs_Q%WIQF$%s-N+ zFM0OVJ1Nq?0iJV8rkTGb{cimG#i#6Tv=SV~iZjQk!_WptEBdRKUPs&?NOQyT(f*qk z6?p}^Q2XE5Pd=0{N;zLi-30z6J1t!f?&5>f(ve4|rK5+YrOO_jmX2i^pB8kYWPOdC;r~b~Y z=}n)EQ`(w`^GS`LOLOZppUk!UWCK~oKAxXGH|Ck?<;X#}ld#5WuMMA9VFy!~!|#-x zGRBGWEYnzrUnJ2v+Qrx=u`YGk>o#r~bNrpch2B(ycR4&eJR{$ABlW>pGfkU#+Z*~4 zLnHbz?z(_BcZ+M1AC}y%iB8rto9NnEYs{n#)tfxV%^gMkT-lEe25FbGuCNn5GLGI) zI^*)hKwo`pzMCIo?xx|kE~B-Fy(aDKb^LMpC&KM}oh^$=pH119NU&demrQI5}=RJ1#@nR?k>)%gc+I{4sTB_FV--!bS(bB(4D zZzFgkf+^cY)Oq6u`&wE($s|zoFT{clgJmH;v30G~Z92&)uH-j`ZuGG3%F)oOCx#_OJ*0juzv zf5RhHVVCIG596LzoXSl4vwM)0=QFYco=31ZJ)g!FG=E5{EQ9qXxuf1?9&9K};v2-z z;JNV&$Q|+t)?uMLRHQ4-O%t8aG#zg_)mB6NAEIaUow&50XLqzyeQIQ(XQCrfBYD16 zoELvRrA4KNd zwQi|S!aAJw&OM2ABsElcQE8=mXN`~cod(xLDyS=!x2*Gd)_sd{Gx7cM9|z3RbbpJP zB>y6PC{8w>BYs`Co9uZVHsJe#P2NQtI?2~}%P7-ibTCEfv>oWpl#_Xo-hlt;i<&8l zFZnOk6Lax8)l(S0)C$j1_&|#>2V0z&Xb!YErFz((i#y5g_)uY|V`lan)mc!#2bsIg z%*LIV%1XJCJECTWx7Cf1f4S`O=o}|mvc=-l32cP1jr!_F>|^Fm!n;zL#+`NUOmxM1 zLE;kR4#Lt$P)ESTSKUclFX_2dF4evK5NR{zi@?W@W;aRi@-MO-@(S&2_Ia{1ezOLN zhi4;s2{-UBVP5!`*^lsl0+X*;kMc={JVqpgh1IlDu{qToYf!ddBh{LD?Gy z`sz0M>JJ!CE)V=;ee*VaW6m`VKfaK-OG#rMI@au{{Y(Vw>bbVAXGy=KudaD_i3a%l z(Cr5&*bCqp>=__WGB@Y&5y_TIh_3Y$*iNO&P}E8M)oQDfL+dQrxNC}QF%6&0wuR&U zV7%7wApG}9U-149$;?O~w2ye!|GI|B^IhOIXl$sw&_R>(uBA*t z-ib6k;`RBqbW&x4?mdKy^I^^XtGhD9-6nmIKjV*yFAdhUH1PbQ_>%UvF#mgV@x#S& zW4+el8hW682)uj1!(SG4Oq}xgWM6gjp<>)>ndeGhdJ#ElSlGjZ*uIL2~$ z;ym!jh)WXwXZokzq;9-w6NY5=>Gf15?$IOF}Kf8$NH<&kppvB&xvOutl7jo zf7y8TP~k+z@ngctw3#se1~Y%CFd>+=G;=Zja93TPw&|Fib-rxttMG5!Imc^MTctjj zBN*z*G;XBcg0>Azd*e-$N;rS6Z^k$EsotBZPwKjZG0M-?#_8{VuR2#- zE80cRF7T_SfO*hd^J#=qY+Gni9po-n$61AYhF+eTb4wgdM^;;puZ zeXbzS@AuVh9>m`6I~vnE=t-J=xjE@Y)CYdO;g1)A4{fl&9nxa2^?O3vjiaa2=5llV zYeL$8zc{c}`ntNMM-C#7Yu@so1NeO9C7iHrWCB?!13cmVmc2#X`VWXsC3sQ%q0TEgpyYY6qGwY_%2tR2@g-fY=t@lO-i zL%Q_+`LOM!i#kI$%mvV!~?J!1u$MIr(1r z{-gNu{D&Rz5U%W3wCG~Vp|+t>aF;;~O~Y%2i@kfB`yF#AF*M59a&lkYrbD&htG{{l z8kctQ*3k{sTf8jcZnOFx#+r%joP1vihxBJj?<4=dZfJ+6ZaOqjvw8W^Ir;V3*-s2U zH76e%z98Q-oMc?&nYn~-F4oO5pR#VD+!ZP-V;0smN0+x!){BsPSItt&NYntlv8o-k?OZJA_u93mu@gOKvB9++>8Qp#jD4o!Gb*u}`*z0t%G{ic z*%42dWm=3Gjt`Ax;~6vTFmH|zjUOY-nDO`MHJOWTdRFn3o2c(< z<)gfjq48z%`^^6)zcR`gw4<6QgS>XuZCNG$vMp@o_c_ybMD}<_UW_k-vaM8^a&tVR za_x3!#$FHM_h&KoSo?&NPCezM8_?_Yr=4_-cVD_3yKOiSf1;N$W)j!{ zD?Y-rS~iZ&v|#6kxD;_!Jgcxb`V5U(C$?Zq8bfU2sdoGUwHxL!9ne)_dn;XI-YOhm z^)+fwA7IUa^%Exv-R&X$ZRjLo(L2!I=$g7)!gi*=GRACob@A7 z&N^7-QrTYW7nEP?F)#5OZyP%DP-19hwhh~B^OJkq{K|c8e(K-b{F?o3e!~yj{H9!+ z?;L3JXCG|y+aGE3=N@YFJ05NG7d+PHFZ@NDe?`8{cb{nUuYQX8-_y+fjxhIohPmIf z%n6@s^RFLi^V7@?Z(v@yoO$62=7lSnFWxxO=HCQ;pd03PmDo%9IG$EI5j<{O)?3sj zkSl>)3FJy3R|2^be$#;jG9{2HflLWxN+46hUzkrIPXc+EpFW*Ho&@qFkSBpW3FJv2 zPXc)o$df>x1o9-1CxJW(zc-ig*RSz{yuI;+ziLg!HEXG`m(p!1?xl2File^15Vose zo$RiDS98*G`i$glIVU)af;}||@R3tKhb{ceE7LLB z{rJW*Cvw+XcZ&SP=OX!t_GAC$qIBf0GFN>^;&aXU2y~Mxh6=%+ByBk=IX<^3ABlVB z6AItil9?j=0>47=9q0Q>D;FQ75g*$S^&@vi!E5m&qOF6U*Pk&Rp7~qJqCApAg^7)8 z9r=*I-Pr0xln$>LyygYGC>!B=D?Hf>7xM#${1%hnv^%?l@Lv+1qVP$Xia2j1esYL& zHQ`Ai&dc$<9GpamzmRbGOYoded|8Myk8mW!`Jc$#x*V8avz+)pgg8;czu&mUQT=R|{^wBNsx!^=v9F3X9-RhttX~lK zF*8YF*)V;m=bAS^l8ZmtyNCJ1^v*&7e_YVmS8=3$6}x0!@#Y^0#%9bQaG17TeiC1= z`3CcfH*Zt?Dc|%^y2$@c$B<)&x4E+r^&;DIbDr$&Pkbu}{wFu2S~|SVEgjfTd6_Zi z%XeZ6wfIhdxPIt|_}6!Mz9;*Ng=y@OIvK_rA7Cw4=WFt=xn=~pVjklo@S3vIhQ31| zF)fL0iw687`1`kB82!ynq1;292|1@EC++mx(nmS3sfm`eVg=gNq`nrz+M|gs94d^6 zkACp!WNhysV|nJ%p+~~F%ZOW;tsi>C^ScV0rB zqd)ObZUb{2;L8O|{ucV?QeH~W%=^}Vl4pzN6N(of;B$bNl=5q*zb(-mm^O5n^g7bn zOBg!r^`@n}#6>0+_Ny;WBh5qp1T-HM-9^mXpkc42S@WR}zQROZbjEz+#n>xJ7tZt` zu6f()!tugDZA(sby(;2VCiMTl4b{H4nebMA$d%oWZ{aM#zU;-#M}hbA)O@fk`zj~S zb1}~Z&lxt zotrSVV@;0MMWfhX*{~q6I@1pHu{L7_yF(LeP95doB4_N)|w|4Z>w$TyT5i>1Lr)F57a)95BbPe*PvI%w106Ib?fYA z&Li2vIE?{k?v8u%E5f-AoY#2wnI!#HxsT6~4NI2p*=8nilb*=eDZNs^ozzpL54+B` zJv0pcVU;bLcxbpg;YBj-H|4U|(C7K-*UXnb+8)}E{v&yw=%dVUGbx={5k0imLOVUo zuMQsjp$lY{j?vFM(9aU+-ncUuw;um|AG~z71{qS&XAeMAs#|awUmahRnd_#YxlA-+ zJ{OZuO_)zBV`BDR$*Wk-1Z_0n0qv{du@Tyt!l6yas=A4^VJ@>gubi=-@ zANVq8*k^9Z6lHgt)K1ZB9r4;{u9t1dCKPud-r7gJb1>ezLHxmuqmP-gmmcbAc%B>4 zntFG_Wql=GK;M5vR|VSW)zY!{cfxY_?1`CKD&I-l87i!m?n<^(3r(n(S&TgwXXd#T zvTmR%{@aoj3Fy zh2=YBtMYw}v*}~r)PRiG`oz2Ck9W72nV}priJw7xX8!TQQj>-YtYjoFjXITUI9H>EjIOnFmYt$PP+=C>VD zzh&Nqp0$=oKey5vgT7&N6up(HZ`C}J zGhJqQk&nH*K|JzC_(f$i`>5|>T5MOC18_U* z65>(L8lmUs1K!}4_av0=X>TDWK0!V_CG*ayg9dqz$VQO8cSAgg|DdPQ(qk5^^r!_ZJz~L| zN@3y)r7-dd@5K_Vwmu!3r@dXn-1Q@az}@k5j5eb)={>Sh=5F%;(&Yu{X-OD~yG5>Ca}JnZA#7t<@H2w}}B{)_6|yU(y<&7(+x^4?-56(rt`qgJ=jRL)x-~Ev|W_B^~U?9coEyKW?Mu{@=WWF$HV# z-<+3@9x(1K@78n~WuESB4SedQeT+RASC01w9h;xS%+Lx^?lm+srF_V@R&QuvyZgZq3l~q zeDHCA;K}(eKeN!g1=G-bkNy+W8Wdg??3YpcUD-xEgZ?x>Wv(Q+deq z{E)A7YL{!6G~RCr%C)*A5B%a6LW?}Xz6j4%Dp#Ph=UPI))>$Z?A*gNDxdG`{yj@dU zHHiJ$dRII=-UH7MO0J8rnZY_IN8G!1)Gf;(!)2i@Z0u*vOZZ1wiyO7hVd#9u>caE= z2ik_lZQhjegXjnT&wqw;9HQ;?(x+)%R(;PA`i{B7j!T_BzgFez@QSvnbmBe*&e-WR z%_(RW3^QjTz1aVsPV-UbJ?(#n<^y&uf9-YT86l-fY3qgr$q z$R6VE1~)jjXO|f|o&8YT(AZ96%MXJ0AaX4xj=js%>Ubw+fJ3t+fke{&jz~OFtf*qi6)q+;KsAlI9}a$*#hnagz^T=}bC9 zmhYaC-!}4N-r2kgS-w}YHP&9XJ}_8$I4{CR$Xr*!tuJXx1dxjawm z33>7LsgL7d2cDSSyudCa_UDzy%xecz%O2xhiUf0kYR&~$mvlVrEPIUcPZ?wL=^7sq zj)J#57)RkUch|S{GM1XLr+!)c@Eq1L=IC7OXcu|;-Kk}arJH@?mk`f5ca*h^rMok8 zSl5{2IKb85>;tX=o{KDNN%w&pfER`Ewy`R{ZOGk#T-%BBL=%EmdF)K)xjMVibXTzr1nXRyp(K;dpX%cy8o~8-6Q>;#qtGr7M#8Eoe186K8xiG z-uXX^<=b_3+uw;`5AZD3-zU#<{VhAoFme=oV&Tjd}-*%SEcllW^U)xzO-`ulYzPhtpz88Z#k+k5a|BZaNrp`;T&Tobve7%2bBpn_8^y_`k z_upFWMS^j72)`cqJ>na}e+eA;i4cAVaImHbud-X4;S=)P2fw}iZfVaadsCTM7*1JX z?#;`@piBI3>BM6_vU{5xoqs8ld{fw^$!zK|>mz>5+vG*!yhEkO-`Eq(@S}B z5W}3ulNU71Jn;0VElm}-!l-FePzqM*v?AC}^KD_2? z%5pXM&1RVQp29S9dRmK}mOMSpiLA+Zyh~-gP=;Xtxa^9>FCoLtdC9%F&g86X z&c`No{-MxY)jB?z^ZmD0rGxW;OvzMHXe1thC z>#>F&>}8~L;D_NQH@o-`;5B{DY4WVe8*|H|;#&E-Wd2;r9ML|@n!faDc%Gk6u+KbG zWthzO0nph($~}Yqn8o|pvES!#;HemYMHs*2jQDL~{LVAtw}0QSb40YYG zCX>Mz81KL1xh6b=z@9<{GBw?Ck(s=q!k^}C_9t0tS65d|!S3G-O z#+2|rGdCZiFOux%}^nibYnXAF`s(pY|Cow?nQh$e%URauX4QL zjF7iAEgNmn8JS$%|?XgPq;>|m6!IOP|`IJ~+)ab0wlPVkVq`qk9c#la# z=QVKVJWM0K`~_+a16HSQ0wqgj~?PTg}LI{BikS+>|Z-w~dLatt&dInIBb$MiTC zDj6j^`Rsr%`_F|(caerp8$HfAQ}*iKIi_)VkxQ9;=_j?$3=QF@W^imVN}uMS)5zXM zs|&kKVaVHBjK{w@=%9@H`GaWD`7e(r++z8EmyStJ?LQ+2=LZV^#UAotHqbXR@jipf zp!AFRk(L4H-*tv-XWgcKyTHrFGdj4mEihb(V#OI*(HR z;LIKQ2j>y3a&cb$zb)5>|E65)U#46?{a=*phjzJ2x?f#*j&oSS-2fjvFjem>KKDKL zk9FQ2owu|De~0)<*{KZkD0>U%i#P|txs1^dQtmSDCgA_>;hA44c=Vp~#eHP&_tL&;E-`NmH(csIjsz#ydNu_K`hA}$|c@QkJEqi zOox48aNaDW+fy-WOgzIU)F6HO$5*I9M2B#e8faFmy~)g()0rC;U!kyGVWxKsxC z-Wbw;%;L8yjQhkOV}&#M)p-W{KE)3n%LGf-lJB>8T^RCO7V5N1;|lQ{Ot?+G_$lu` z$CPA+cThLP&jPPuceJyxyNz(OnZ+1k7Ha@W@hjCGd0rjnebr0k-5BOQ!;&XhZ-=f% zamcYo_NF$%SgzQ%u*qUQOuSI4htLleyPwy3x>E*BgQR5p1s(f z+-s$MsZ>rY@9IACPN*EIC-YiI`}Fxv^tj2y)Gv6OjowpCFb<4y=qP%OA8lOc z*^eJ<>l~M?$DK^1BxA8XPuv#D@OjxCa(-5J7|Nq_kUICcny1b~e$tX}ZAiDKXtxuq zMHBM)cu4orknSTE-QS0F9~Z7@;H&eT;{Uf6-iJcG4}|>Rud%RjL=)!2{ZPSq%)jC( zIo}cTdAr5uT_KE8>|=LhNEu+oRZ^lt^}mj&q$TIpf?Kk>Cd)`}qgi&pwqLi#TU>D58{CM*5EF#X;j zeP)pU1uMOH4sv0juiLHouZm7I=y7ld^|!9uozZiZX&{W>J8=(fAX>Ds6Wc7@dxg7W zvpZvOX^__)7SEngmTvLgfh|4gs?R;K-op7mAZy#uk zabiUf&%B&)+QNTd5MLd{zuSsWTYRpw;@_=!cq~!<=i2B$9=6A3CxWvLq%T%_mN{{s zabF5=QOM^F0iSt)!p9BMuME;J{gd>CVfrO6BS%M=eu2{a&ULwqmNx3@8zd(jt2}e` zo&j;Qg~yybd5LGD#F?-i)mpf;$pCjo33vACxD^&|utx{qsU_UOv?IMmESy9rV|jo> zyDQ?1j|oTZopDGkPUOK0oaA7gp-kgXpUP|e z*^t(fjxl!W*e@qVOskw)kmH2R|OnL3yUaJbqy15w8D@|0v9( zfjr8T2l=J4f&1)_3n@6$9M8>5Nl%%dM~Iy)x!Jp-!{6NRd==Op2{)q z?AcX!ILR1tt!rNMPQ61UpCLTTlO9cUb-3p8fe^l4Whl3Q1`CwQMq^CQg%8ABgA22OGOGQL!EC5`2wk9F(rEZxDyyP4>4{LFbHdS0tJ z96WxaHNtQ_92?~BsF6fEHW-yp9zWUtTloKB!;|HYkP_GOAj9#y`^}4$7;zWrz;TM%AdpR1e)9z^D9b_jxuDDT?9`m;P#WlRG z?#wtkxiDB`c_wk&bFHb8US->^Nqkhi;Q8~?7>>B0j|lWKRNQkVF1Sw{y#FfU5f0Y> zNqd(ScQE94fIg1$zL?v|6C1nE!ugL7=leRD?nqz|bMB@gq- za@BuvEs1p>;_dS2o~h#|JusY*J$YKwo`Q`;hN-JzCq1yb@Jt1A%)*8nu*vvw)nQck zxIGigwxuft=lCfv*0P_^M)9QyX8@lRtl=qt>6kfBJ9|}8)>_)_{B;i*HZh=k!92fM z2gR}$ewxEZG+$7_p`HHmM%jw`9qf~P@mquSBGtX*u=pm$x7cpn255Po z$VB1)f^b$Be*N!#yyq=nG}5B_J^r3P{6tU|%BeEs+S0L1TRM5XO?*!;5A7{jhe7v2 z|Boz>C_m{%au(b11m6T`eWD}}i^245(+pg*G zB5yeZ|INGVwk*jaZ#VXSJm#7#vSKHJ&0(jF*?5M%8seg*I!{Ni#YdGN`Lw9La39Qv z#DjH$^!8dm`5b%(r~}!E_zjom1Dk~3L%DLwb4sqChjO(K2kpjYDYwi8o`TwF=)n*N?7a9Raw0Am)qqbq91;B5Sdt7jO7xh=0wizz^+_ zyP8^pdnMJ@xZ~hb?Nc$A;(c=YnHT=HuXamXxEpvve+c?l9YD9}TIGsTt}QAT@t;SZ z9~^*=dvtPZ=lTt67iu3XeO-%Z7HCf!`Ky56$M0(5T2)8HO#*%czY5Bg=IOxmbK;3! z-ln{Xznb{Bb$CX1F}{}Y`$D=K3BQB5sXT9J;VGWb>CP6>)e`^rJmWk+MtZI4kn}5* z&Qtd-X}#q)D*pq>&bW^ohaqD6BWN#jp?G)>9s_GC~%2j8i|5SAt)L-qBs=vBo{hj!)u>Sra+K@N* zRka?;QAYgJM^#sfr=Ga4s&zzuMeC@0!OzV>4?~5Y>~*@hSEqFq`3xa1bQ9;vFR3gl zpLcIwdwqARzQFI0-iP7uZ8lNbvdexmbvyRp>2@)ucX4M)R``AqTbcFLd3oK#xj^{n z?yt3eTxw%syE0)r)0wNt-k@!fx9$iU@XfyRd~C1s-DJwh_XY6*2OGOG7R+dC)^ zxWV^7dA|D$n%*;L-U`h^@~kb&^g=+#cYuDL!$(Q3;yV4QwRzHhBH6O|(-y(gVo+mQzDnoRl2yBpH`J>L}X%}bwrM_GNG zCp%NW(aKL{L%vlRr^|IsWO3%a=Br(ENhS3W-f?H{&&}?-f<2|+zO74*?oTh>k$1`C z+=qJw_tEF6OGja7zsWtavuJ}3a_DX-^%>mB@AT5A{)oFl@@k)?OUAp=&7DJDsR>#eP4IR9A}KtE2ys4u%_C`X=9Sk5Z~PBH!lAx5Y>KE+YDxL41Yc z1@G}Pjk{fMrnl8Jp_?l2UUwFDG)?LFf#1+CYrj~wL7(xfi@Cc;{o9Ao_lynYt659< z72lb3#?Tp;QFRs$ls8}PZ4Uaypw8J#);B46@Bdf$(`N2kLT2ityP<2c?8Ng;WP|JI ze$8@hhC4F7>Ed0gGi2%r?WyfxU?0I4>_Z0#d%imf`@v^6yYQc?wp{#9%Oy97W)1Tv zjUVL`_69a^IFU1(t|-TLp-c6%Ht+d|CzD5#W|qJ{Mp*;rJ0Y294>HlVrYK%<-K^&) zItybwqy0&@tUv1Vj{YT4WIBQ@NARzSO2%v+yta;;ZxJ*s5@3PdhGfqu?MD>lmRuxyE!I zJV&UZq0sh=2xj`W|E=7$;&GpBwi?8_6`xEttdb>aDoC|e(M?uQf?rpI%HNt>&D za&z3^Oi<{Prb-9!9xlpwg1vWryP@#?z{dImzg3zKE!gRJGe@m;ddYqfvWV}6=n=Xf z?5OL?@STW_gde%%h!d=hzJx4V2R~Dma9w-+d6m6n(|T$zJ=BxV5q-hVlf3YQg|CqB zZl1p^=?MP9{alF4eG`SxfU}e5r%BWIPf9ZT@DS}IC0fFs(q$X+b@;hK;(my3_wnp5 z;n{K!|8e+l=Q+$-ibG-DfA|bOlJz0@AFKia`?n=XM7}k zvetGBA5lKUy^W{tAt}jyH*uUh3GlB|ord)u3vKdRss+cg#`7rk>`?mOi2~^xNMFpFit`e-UQK!*>5EA32x|FOd&p;fY_urx(0SZhNlTE%MR#jwLtq@6+qE2Rx0BUDEO|!H+gL@gw+o z;I$yv;*j5uRS%^7mwZs@VdViS5XGM$Lr8aaUX}Wiw zwm9*5$*DGh%)!0N#N8UiZ4Jf}(Oi+&&634#Cnb51MQ3AYhV3dS_eivednS!l*5%~$ ze#*I$=Wgo#5b_0W7Jc?%H!H%u&j-VGlc4_$ZA!G9J8SiMzPDrpgwOOfHxRc8yg{Dt zD3uc(g>8Rg@tN{bmgiJ1p4f{y?H;j`e8{G{{0w|EJUdJIhxK0aD}k+(enF`Fl0Ukh zxSOCo#IxfJ+H&slQ6KV4$*TOBe;on-wRj8`?Xwd5)Y;A+?Vt85Zm2LOe8wMpz+-$# zU2;}aakhPEZAty>Ma4V5w#znITaqteY}~wr@0IIJ=5@#wQM(A+NMLuw>AqTg#zfg3 z+Dcg0lZ$K9g}`2zE6EQwNe1v<2mVaP7$f3?ylYsKY%I3L!e@`iz0h|~@I8fK?jZYH z;B8IIhAVZB5`LPq%9pPQ>-`$|@@*O?N!nqRPi?umbF@Ws6~D$A^X@Z^jE&FnKId>R zt5>q?sp2a+S{UzV<9>|wq;r%neEw5>6sL1iyA#1!_;=jvs`Ik)1v*#Ia)5Nky>X=< z4Cp7?*|$!G_)iM|lwB3>)mpx-d+sw{fex;HCHmfstjw2^+iIN@cT6w#23*N}(b~D` zXgfra_ocspd z|8(rh*~A#~ zKpzX{d&rIa@v%hL0_14Wd|&55;gjV2rOtyUZl33`hUk{&1?d!LZ>Mp0a&l*#$9plJ{KfBv=VW5t3r(5IkV9{*NuIlodAZgw)+K^FH&d*U zEs(BMKIs)&>&)=@vz*z~yr^Vn!`Rum)QO8teN6StbETelsx2{>M$a+v6Ae67e@n&WI63@?CQdk%XD^^y7~H8Ti1eJtfRsA z&)TEz6J_2N)jCmk+;FdWI>xV@Uz}fpd{5e_WJ8wI&P>C9vUqq~Gdj<;k$IBNo;A&8 zU3phLul!@kTt13zL3`4!!g>ho3jMV2Ot~|6#=XYwxF5ZFz9YOqo?+wxPUVn=a5>>5 z&p37WsQ8~ncEI}iX?C!W{Y%dA>)QYmBdUK7pNQYSih3d3S)dJGt9lwf-4=S$xojaO zeH}ypvIW^hsXe08c-SU&uDapt7vvkTl>|0X_6YSMdl3!zI!`?*w3`ZSBMN@1x5_k< zuJZ=5)rBV>xwASWo0%(Fg1TdU^M77<6W{pX)}46&Z|o{g{~)`Pe5EmA@mXAVly0?7 z(1%`2PxS}9%cpNua!j0 z8$2_ukh^O+zg7G^e!ct#_-&LdrF*4^_e? z&BHw7tQ>VZi#5PG{p`058=blL%km~2^L*c|OBTlcn4@*4GQYBOyqdLCKkDj!(xiMU z?W>%-&})0@n^&n#W?5%hgTAD5zM`qI;v{Tm3ikzSKV}l}44%51pKl&qbT~{eCtdw+ zBXVCRTH5c=*k3)lcV6ma@kK9thp_Y*j(fFVx`H*oh{mPxJdgFNlVc#@_mY?H$8XS> zO>2Y5(mQDN>G0|!PGb$$D1&x+c{k@z_eAkmZ9`*a#&mK=^!OO{qqSPbp_Q4oq0t`B zIjjEAN$MzbqP=wvYoA^jYqryO$2n_gGMsGFO6b{K_9W2Z9rsQDESFL zPuM};S4-Za{V~pFy%4ww`EHQyGVaqjd?ssxW5lU!!FMOQ7k_MnNtg9;E@MN5?hn1_ z+6^uA5y&h%$ksU((kFLM#KD%#Qr<`Y?&xFz_?oHxYMz6 z<9gVLVebRG4bo~=f5WM?_6CGo6~fg$$)Vq=e+c&tlvWX>nRE@fT6+)HDYB{b`RpG! zo^K+3;ogDpRKKNt0L>LHVm+1bU+-5Nc1_2(&_xU%8lxQqcM;2vqQkA|FgUZIF+Y9D ze(7=-eJyyCpw;?4w!(f?a8K;vyLN?kAbicc;r%`3v+kBI^D=t@GZ>3LE}XF5J`JzF zLF~DtQ^~ILDaY&5bGa{_73{;`MvmZnaGJLqjHjD6#7za?Zrx9QUxZfYQk%m3*D2oP zoSNFW@+ht!9R3LT3|e_CDZR&|`4xB{(zzt{F`2gJ9D9Ca?9U%`&AgrDUHaxiEH}G( zKV9^9!?nlz*dy7$Ef|YLzNP&P*2KqX=NNMFJie9xwd9|J_G`sCQsH52bHBc&&KT}l z(Pb{nf$QeeL2 zCZE|V!|)9GT0hvY__s2R6^i>6hi0hFW}38Lp?JalUS>+4>s5LKnP4vFZFMK1kC@W& zRli}JnUbu8e?XgZ_@2?XRlXhk@OL%$^OQWQ6Y7Kc_F)t5X~_1fsOP3TF0+lEBlyG7j%1s5f zRF{i2tBtTH*^F(?`vYmIEI;fi(t^(V;%?wC$(wbUmWA+hdAD~)u5Bno-DJFbz39v# z8dAAmIb81XoorM00DZ)><-YoeYDlQrhOLQS;IEOcbIQCLa%nOa}v=UXUG@}eV#q&V0?|t)dz`#_WYdFbuh$_Vi%J+ zZ${e>@Yf1o{ey*nPR`ks6At<-L!O-S%hqJ{EvXfhF%2$r$^1Q8>gfq|wRyHXgSz|P z=9pWJ&MKrUZ);jUqFlVeJ)}0H@By>wptm`#H3IE5F|IG|HRV}{X$j8f957uu;Xp4O zzM+%nJFLa;PF+M>zy_F`=2vrGV+H3PR&cgu1>ZFp(|3pDf7qWMmaiK~4DF*|*~cB_ z1->oRyS;Yv;Qh5r^IL14+!xrveMaNzH%gA-c&5~DUaPeLbXQ3^wFXlE2;aWkP~ou_ zl3#i#p0AOeNVi2BnfF#_x1@eBe=&KCh#xj2JE;^u(f73k{JsERzA2S|CHYQQ*{REKFphK% z#60?dJpF%b(i;fs<2CSEARnhu%G2mvd|yGG?BKbrwzY5v<0Lkmz8_8R?*b(aU+`d#Zhk$N*HZ$L;pkGfMj5~a*#3YAXxGRA%g}2!^ z!!1rs_hmp=KFqv|dFwa(8B+-lJX{E0X+Rzk_bBNHC?X57u`j+QM-ea^8S<411coJ+UKs4b3ma|c8p z_bYJrCE3jr-8y1=h?^Y*r^Z9AodT|^~K8dyX3(dD_ z@7(9)RuB%((sWbb<$;WR6VK;NAa%z3dQsM+BDaB4;{E4rMmRb6X#C08_ZZ{DTd0HO z==crPQJ(PoS?5?<1GBC-qpcY@}+w!WP3g6XdQE7`C0s_&P-iGUXi=HR~I(AI#B0UYOi|Jpxy3qH!&O(1Tws)0qyrlY2o{Xcs;`nq6WPWB6@rtYTfC*RetWda` zH5_k?TNb1fS6Ub0-04$Uyq{>mp?}UyA@1=Y4m`q-mBOSa;W4>An9n;ZY+}q?kpVa92x=d zi1Jt7l>Z#`RGm9_XypEGw`|*0COUlX(2;FZ(EZj-4dqPUKLy`GSh`xb)~)sKGqp)% zRCpWmagR=u!ac7uH6G(^XzKA>K^U5gy?ZmNBb`mEm2K{46sa zzVU5Ik9oSOrrgsNFZljsM(Zw&`AqGhPMwtt&J(23MQNWjcIe!pe38G-xSlMZ#stAP z7^#ct#j!*>h0P~r^RjW-J$3UC`i_M4F-dsCMH`w$y(WgSTX0^07w>nmCPz8c2G;S7 zP~v~zllD^bPsmu>H`4i#g^$EDB|p&^v|GXGeLmxV&fGH|`T1|DC%$W%F=}TcmpRGN z7S@92WP-IG?R%l42Ih)E-#}g?>htg;N5xO)_DP>{{!rl=#dk0-Dd|}9P^KB7OtZpq zi{3MAM7Ao)rae&fe~m30h7B zmK1gn?Ri}wa}#vIJ8IB86VUjXSgjy?a{)f#YWaz+nKI&jt+?T+#yme?KRgFt5Z1N*ES(?nwg&oRpZVAt`hdTK=5p37 zlk`z*8QbO2eQ-X6efg)fzS~3F?>1@9PX~KY!Jfoun_>KU?Dk`A27Bb4X88xw=|iI< z#1;FNW8Y;@f1m19dzkV=*y4;Q_>OsKv$27gum1c**2wQab(QKpkIv}BqXX||?~`ujKUM|0LCqH~t zz1F6RWqxdeGIwATI!9Y~D4wp|xW!Dyjw%ku)2Z7p$VVj$GM(EW_jqslDV^15*cH!c z%_uc|PC9v%I=$1&RBQ<9hW5c-DcLw}4jWq6T_^RF^qlHc zL=h+=-W8G#c1u1x_*}NDmEJQk5gHDJEF5DFUJoaBjN`i{6^tZ8>csqBiGhq z+ud{|o&ZkDFLft}FCXT>;+IfyUW zlW@7i!fld$D6F$4l!g8K$G)43cZ~&eh&f$j$LF-9_>N~ReAhU=A?@K#ED1bv%SrZY*^`9tD12q>8#bT@9t2G zg|(Eqs>J(vtDi2_;TZJ^4d0t}%tq=`bv;HsFfJTF2oLRZk#`b$twH^ra49q2|8-8T zYf2_7&rgJTzE9;X#+^jE$2f1QrpY|`Izr=$9# ze@siS-=WTDs6N4^?f#54_o6LGCg5)nukTjP$R<{g;~#miH0T$~2GKw9fiHH=+^4ZE z(SKEZN@b!AY@}{K8roPy_9r`JoX0n?9P=pU0AIF!vHYxfinesm1++^UL*w6`_`or6 zvD@drP<%fWpZ>)AumNbFc)#d){*`>C?3MCVsjg%@QevOq{|G&vdlk>$G#E_Q$&Mv8esQqrX#Gc`oKT z;b_z!t0HSv3EtH{y-l9hbH!TuW^DQ=>3I6-81~>1y(>J+-e#=o_f_bv@SyEbkNf{_ zCprRonMod6i*-=>3`Bjo7H-K5HYobWlfE$eNcsXAk%!-w!cQCfI^IKlC>^$E^hli@ z8`W4BnMYQ_!_!Wc{kHM8)y_S)UZ)qChb_W=x9Qe$uiERlUSm7#<3`!ec#TsWgoXSDbtQ8G-3>uOifEJ!SNyR zqaX15@q}KD_wv<0i6=omQRSny1FiqAa<>-aA4|08+xS@8im9(+zrIZU{PXqcR=#_S zsh?aYw0%Z0)K|R1@(3qhYcXrK=3Z~=j(jvms$OgIGkX}kp7C?cSa0)z;NrR^EtV5#mY>xaq1%*V4HQtL3O_jmqOE+umG}WB=zGXwJg#{Yi*aTmS*bpW zFzv_ugZj#z8CG~`hGQ`%jc@gml|4@4M4Mo}t*ImWfZ%MVRN&7$=G^tFT}YeJIGDOf z{dOC5Azp1Yd^@&)cr$Jf;{Wrs_y*$3)aEEF>C^a<`;Un)YTxu3Ig(Sa8~J0rp>o1Q z`iwO8o!?e?-={FXH5X1@rFX--sw_R8kR2_bpvD^)@av4%TO)SUYYKnMZemPOZw=Vp z?Bi>uZho_GU}l}a7vF9g<@>qeyc6Gt`TostUb8tvi}}V8+P67l<*)f1vI#K`#+G9Urc4N)ehx{SDWR#93Noboo88*XNzIacRcw< zBb*V)9SF4bO^J#q^38L+%Cy6FtZBsWsCTR#4GwFU)CL&y;_GR(oMPI*p*DQ>0}(gI zn8>VKQ=1@sUtgYcn!h-0_8(9tb8Z~Ze+tTeyUNb@Z2@m?6>oy_{}W|4?Vb1z-YYrN zj(Wy^w^G!#>e*F2U(31Uh0tx~de$=OGM>#HYh4Osxf-+ZymD)vQ+*;GwjVO_xvjZg zlrYv@fy)nDd@pPN2#c*}Y%-m2vKJxxFFb&H8 zEuW^q2jtYKPDX9VoaG@{>0Id{?4+pDV+X5D72vDoSDb~f#^7THl`?8?Bz5c*7<|+# zK8?2xKHdX-_*k7H;0kyaj#B@!#WMJS|I=Fwfu$_M%lA8qBkkL|S9<`udG8l5vh@jd zX!djRG~+m)1=3kOV?o{L2K8PfzDd@RYu3$q)N?}he5vGK^_lBgANKru{!p7=&yC(g zw(9wV9n8<6qtOFNvu7QiEa=Ehh6#&jh0RAEY6+9yADy1G+OJGTc98L7)N#9&jOq+p zFVU}htQS4Nl_}oaVB3V{uD6oaysI9^tq(Q6sc)_4ev~t5J3@>%H&`{amE*%xla=tQ zjP$V{D_QG3kWxJr2$nQeJt2#88MEN$8+g$b8=ieK4nB|RTj8Pu9`6>NSv+2+aU^RH zSqG#0z~B7O^DWD!FC`0~pMCIe*mgDdkN@M%Ujny+_|1yf*f$90OzA0%OUAl`@C zTzfz|E42Uc%`MDhYOYzh*xOepxCY?92HwgnxNhJSC%Co1)d%>#gdJCw1-A~k3~k>9 zZUJ!3;JXTZZjip6@I{1uzLW885XK(xI+rlcjXV*?-k!RZt?{XLd%SMRV4hppS)OA3 zB);N2&dkRcTC|KnvAWDzvw*5*oU_dIJ4){ zd&tIr?Q86PQrc{jD-`FEtt=5p4O5c z`6hZuX92(K`V&Uxr}UNBt&7O#Z>g_w+yl?N2l^RV;X4oC8S8}Xde(L0ACnLC|1ghX zzD*vI_jblk@5X!dQFdOPs)wMy7Em9_seH3m?(cPu2y5s5e$>d&$m5z9-h!=3UInTn z&G%4d>@|N~Om|uHj!k9zcW<6p*T6cugDNlQwKrf#Q;(s3Z=+-^^py8tLiJi;^fG=K zcN(zl%?RC~G=z^Q z?7RRkV=G&}iNZSIR4-AcrMgH@KmQMbKH%OO+nmeAvzlL=^dIszC9s=9R|Ry4<`(k5 zPk0Icyuu#=59O@wnC8`j?-k|+K;e|n?%&bQjn zHP|2bD}3;B%9cpjwDFAmnzN`R>xvsLru+qsFW^_hHdXzE=F(49Ze(tbybD*q-`1FS zRB(d}WfvJ-<26Pne)7gBVH*=#NAf4}u>n1pfddzNFb&+q)8WARbMR&v7mN++Oc?3J zmh|s$QXQfj%Lb!U&e8a2aM~2@>kAK0_rk<~9k|j!_9ycFHROMmU^#O{UYkay-mq*Zh!ivjUlU=yz%v0)wH!L@4Jb}c4R<(g>aK!cn$QC zA7>BD_%F3D<;8dSHJp>=;{^FE?wp>)?qJU%d9l7e?mgs0$>R__iEw7r_r(+1+pEIQ z_k;TGv^Aa@`v_}yOEUVE`$+a6WA%ieK|b=Cez0?zhfOlJPHEJhkcSV5-(9)=WBBcU z0bC`xBscROAyb}>gwck^Dun~uwb!`DdoYD!vFfRVj2$$CvXk!kLE(T$<b3llN1Mxr?y2qRQ@_gXA-a>;51*L`9#@`=p$?@pO)fnjyYGMhH&{8|HxCc$oF0& z8puayke7)6l+RW9^o@;Ok&#EqR;+*Q^G1J&{{FEg>ZeRSGH-ug|JbJ$CwY@DR^3Vd zC3C7f^1BxLDvlsuL+nJ_)bW=(ZNuLW36K0`bLmGpGk%ut5}`i+p7nUz zzp<=}AMK3t+GWh6uf(q$T30$nbG(tjF8a#dY8@7XP@&*)lHgdqe17o_jL+B#fLS zEbZ;0UTOwOBU*anp3bTwEE(8#jp=9EU!r=2*Vw{m%)j&fsmW{N_J!~{pYg?@V@-bx zzFGUT;MQVK0DJYF7rK*EGyBb60_wFAo%T$a^(EBdVAnJ!pZ1y`#kYljj5}@UXA%}` zTl$CF9b4;9LbSmh^2y(JQEKunUdmEAGQZYuCnt0-aVCI&H)-~6f(G!u2Ku^;U1>VLK zakfR=r*FN`p1i%ysy#v-Y;!#MgFbn9hGqI(!3^XkjX$i;vpyo4T5DYOAEC{{|Db7O ztbH?W-RudVUcNG%n?A<#OYoUCo4$4!9LVQN>QlD#S5;=g3}`<9{+yl6&A|8XvEM-P zUkK8FE=d2`ApLT_X}{?!lzuB?3+(P(Lmza8`_ml?Z|G0|(%a;OsFwoKi@q&uz0wH{ zn!P1X_#DsT9#ShjFe4QsKBO{GHl2qY^By$)biUqq2J{U>TUXc#LEnv{PjU1!%=KBJ zZr0k+$3KMLH*=ND6&wFL@?++Gp>?s=>GPaHoH@5wvPC)2Q|V6-r}9o#+%R(ne%su2 zX4cwj#@264!p>hn8wvadZGQ_c(y zv*(Yz^G#h4uQV;`*EL7AJkXuNnd@(uJ%8wEGq*v$j2Hg%9qO`-_e!;4;@(iZ&C)&C zDs~OJXG@@azJ>0|^~?AB|IstTZuw~{rZPw$%H{~}c^cClPkw&smoxmNd^e7uBY6HI zbG{MpD|{XE`p>e$z5*O|^LjE92CwVAgprSl0k6J1pFH8=ueO#qU*?Cs_8k0M2XE>L z-!T`S5+1D2Ze9ewBI2dHe!)CWg719s&9Ul63ExckpvuB?AfpHIK}_?yUG-&JBOCam zUcUzT-CECxK8?MV=W2i4-!qps4O}k~mjcIT;UJHH37!}ExDS8Xm&K=)ztN@Om0Wx| zIm4dD587YHXEtSp#)rbo_cy?~lKPe}k29BEXPx`CnWnu+W_(@eSxxBQ zkk);%hjNw9uo%eQfZghb@FUUoHW=GgYyOfmlKRoVy*^%JLiDou-V82minQm`oW9XN ze?9%RPUM{U2)bVW6v2KrfW6W9Ji0Mb0v|c)uT?r|zD{-v_R2!wYw%U6{e4NegL9kM zpSFSV;%k4eG7u--BOjqj2Ydr65Sr&^roG*NKLVAL6t&J4@^Cf;QVjn+?vNe&Z^&p)6S07Tkxs;aC8B7}(&x zg*O(TY;VV(Q(owYqyE!Bo5-}tM{Il?G5L)7#4$??wlAd+`mS^V4_pEuVd+O{{B4ANmclc*RIwv+Gzz=8jm=o1t%r zvJX;jWzWmAGITUllJ7TWRx6!#YQJ(My`x(8ax>J6BAG6b@4V_qH zH~sSR;b54)jwzC6rV5c<6G1-{{6$(Veb{vR)*1)&va5J)K81z z24-{a??vw9)|gwo)pDwMkN)@~w{T##&Wuif<*nIvczCw&4-NG5x?nBzb=%>W#_{;M zuA9yeU)Oc)O{+|l)h+2Rb4QXhup7!YEZ9`Gufeej|HwCX!Ok;=XQZNh?;oDo?E1Vt z%w9J;1z*1kfA8o+c9Ph7O zYl3e#rTek3qUf*b;5kMgwu159>(bqai|sIJBcssvul_kYA)Zm@-A2OL&4-~8Io$7$ zRg4U<-b1=6T@L^dN_{VwHv(qC8x5H)K_AYJ+JSKt{J3V()L=QGxk=2&3ImV z9hxXp89tt2-qB}u+@%uXe4p6Xawp7_dBv|5Q|1VB3#=)c#d$UM3f1f2nU?mKVmnUF zK}Ms{QL1m@p>98;u*1BsYP7ZXWm0l>f;`9mG%!2CT((mK&ivuoiBM;*r@rRKt{MJ( zDC;vR_;t&PvJV_VWGI z$?KQhebA0(^q1Np(F%>JBqn> zt^Y9R!AiEb^E*tw1J=HCZpR)NwY+FYaG&N5d!N}CFo;gam#T3vba^%O{p^iN){5uq z^LxuZ{I*t28FJNEo}?eOYOss)z{|c^rxq7TRPU5+gZ45gU)c|gALSdcdMm6+eHBi1 z`)y8s+egisHMMv736Pgp5Bm1up$}%tK-@a%|A=Ls$35{+50*@!U3npN7W;1)_lE26 zKUm@S(>G(|#D_}YXV})BsCQZ`lV<$XWBTayiFp(3%Nn&A+J@}Q>&SO!U|-&Ved!T~ z9ro%+u)}0uN?xy1*HPwdRM~)szM!ye z*pjsQ5BAZwIo6be3RAm;o(ssEbFDn9&1xBDZHoM~I!mE|@B4%AJib-d%pCx;;Kgk5 z7n)>;Xxw&jXWX8pvIVx-w}|@~boD^POrNep)BW{Ew)B5g<9qbh#DJ!sf=B*&(TOeG zm_CZGYt3u9AAJo?ZXWaBIh@r|`9z~De`Oy2!~2y+x&j_Fp&OWEu<$9RWH;-~5uM@x zRdmH!_&;?{LdFh;zpp+i8~3Em`)V)WM(@ZjraU&~;rZ&oaIX0-^~;bB?hfK^5@V~r z+DCoS2GZkvo*Mh~EXfZ0%-JK?{B$5YGbG=w*sDXdH`&Qv!g(M4+wrzaPiKZPPn!^5 zYS>R&OWJKZ+pM+N33XWR`J4k4>B#eh%#aNS6K|c9jXHx4!a-pHFB|Y4(`h%YzQ+bCN z{|(Nt%c$#cYaX^%{M*b%jy=a16TR|ktSqV<%Xxp8_syg+ z`$>a5%2oH|F*hrZ3DWK4RUFXWt*79?@#P@jHK*rGo-2sI$4@hS5%KheW*mIG!ZQ8c zY4)K9~t4Oq=PK5fzA2#eq0y+ zjNvn#6LF$Bh9=Z-5=Jud3-$LIF zSZ>+m#_i^Eo{uUnZA*ZSw{ovPwzvo%9Ls+!6z#a+Gq&o}j6yY8;GPmyzgY)QV`RA|( z#;h@wtwX%oZ|JADYRR*c_B*V6gg+O%Aq%JWFN_eshw{dQG-jQ?`kH`-v8?{I-SnsM zBIsXRgFduV{OyCkNxON=K(ULz&%K1cD>hv0#)thi6;bkyus)&;*&j!{^2B3(8$K4; z@3f?U#n@T>qaUuciWBVtojn;k)i<1MPjxZmdb3Z{E1%Bv>uOJdzTVi9J}CbNvY)Z%Y~NWn`FoiWib$7%I9mXWtW*323*@c(XjJZC}42Z=06W<{1+FcGv0j|^V8R=J!t+hOCn!FvoCN_rj4R!r zs9*E>;A)IA0X{HCWbEE{o)Q0j>}Qk*d-2~{*FL3FG@Q^=z8;Yu40-+4&D4iSovF+R zNgF;or~e?deGS@Pp-fgTHlO0a^=)uD#4XA2inu?BHu|YwDqrzn9%q;eKH!n$)+X%L zAnZ-PH5PeU-;!l?#V}=Np263Vl3`?a1vqcy9->=#er=QK&&K{iYrA^zysv%)QD%e5+dIMcLyy2bt%JNiTR$c)VCe{qZD zm7fmZw!)pZ#diKIymdul(UzP<=yA^e#BSgmWGl3avGi+m`uF$RUQG_Vur1zg{qXEW z{ubLSXYT8O_SsX9FSJITD&{wKwdN+nt+sg|R$mGH@POsX2T(&D&K)f7e*qgb(l^rz z_1R7hJSi5BR1d0GaH(F3UY*mgbsE8%9eg34)wvOU)p^zRiPOu|`M;*B-s0i~_@w7m z$F$F}cZ5%PsKXJ$RfnI?)FCog!YJOT~>MY!=1oJSuxkdu-f{CB2& z)SYCQd9DijkOZ_RkfV15qyCh%l`5wxlV_C${aXrJO9rBejQn%AAoE`jIU&Y2G3h<& z#WU4!^G>}D57&FOL3la5IV7A}>DABM)R$>5S>ZXf^-+C82Yy{!Yf$pYdlF?8e-5^H zB$A}l_$q{d$RRFYaYMzlvqUP+cWyi91jDyiy@VBNWqhS?#wU^68dBl*%B1FeBkfLO znloTHABORy{~q=_+>mgKPn4%zxcg>0oK@%Otg8q-o4~Ku+t_dBFB5IX*4w1A>7BG@ z-9fZJ%{^G$X_=eEu1%I7Tq!=t0k#qPbkH*IQd!$N61$Y@N}`uUvo*K@JaoN@?+FeMR`XN|qi3M*Fjx#YU9lJ}l_phYmEHDk9(CI?va%P+=H^%dnFTj~F}_{95!NaNuPSaZFM z_)5K#S03Lt1mUwX;e;*Zz3lKzm+{!~-Xk+zTk=8K$4gFS=h0?Xkp7dB1z^4bk4*nW zUAzN~#^hS>Q-EBo;EYPomkryZ{}~~j!x+b|lf6j)#P@}w6P(M@8(*R=E$3VP4Rao2 zal*c$d~-c(9^nr8y#y_)-<6VupbtNbxQ`I_JaM9FFV8a3$@||1^o8b8x5^9IUg0GY zvQ2CG{#)Kv?~?mC@c&L)^?yoxaaP)r)6(i*yHBxyB!~2}#S8du)_Z~Bt?jl|;`2i0 zTOG;28>QS=A@`D9)emQ}9XB?wZ^wx4-#vjm^!j6_+5>@{U8{AwT_x_QWD1zYlFf`< z|4)r!WOJdbkBFx{&3P8A5#D*yuay41ud~vxYs?PJA^8oUrMl}jhwry;?)<3Aklq`l zS=W<0Ck-EuX)oBSi)?(n>}9i@%11A<3m==UzJBC8_zyEo~!r{fTmh>~f zPi!ertqQ7*Gc(@&ZIQ7bXQ?l6{?ZwG&v%L5d z8jIskr2f%&UbHQqXh)yE&HC-y!!sOf1X?n61aH@^iF3CwItD(?g&wmW8kviTh6c}K zEb6g#qItB`vTX4TTJw+E&FtlNXg4h{4$fp=DDLJA%yjTgyJhh11HSy*7Boe~58=TV zdJR1L`0z~48y;D;Cf=ZRX3OrF=SFv;Bb0WhnPYfn#Rb6nHq}d7L$7iCHNm*!Fy}&H z|JSYbWiGAruS{HLMbiS}WdDx@X|>jW*yyqf?NxZ`QQ}@h4p?_D-#D$xV z!&{XV{vhE8%y;>|Uea&iI|UqmiK%A16QsYF@V!}Zck#VjG?V5Ed=E(LZrEvcP&o3IDb)$&0FJWw77MT?;UzJdFC z`i4=#h|iSgm6JT2vViz&i9ak{b?6KEt9<&K!~^`jOGg49)^F#-!=P*n2#2>Xarej4 zfaeB&G2!CvFlp$2#B-C!Ckgj?TVdcn&bN3w1YBVj+$Dtjy!GK6zIOwIK1@H+Ls%B< z1;p)whVuivH{tVHbG=Ik37-ouMl-zpOLK-J)+y_j6X;v?$1i`{jZeq4bI-blHq8EF zJKELe(2n0+)_JLG>?CCUs^J+JKaEL?`JQVA>-_mK@s{x_cnrftxq zvszRK@@r+wWG?Zno0P6d-=cc*%PAQ@kS1CjS??Z&&N11HOR85)YbY5a-%5>)Nj6^WLIq-eT;p2yX(mhAIb)uWR zm-K)ulQ-dOi2twos&Die@r#fPFB4DPuYl>yqK*0LrQZeaQp%8#w*|lr65f#om*l&R z@OG7f=aZRndQj)0^bXhdKK|`Tyc&;xpxd_7ml}G$@B~{4Zc!Y?8Nqn#WkPXlTCP zr#Z0_J5l~O(OneKeO^HK^#NVafQP+Iwx|4qupNXgBdjB!bqVj;w3_zi%Xi&DV5bCn zJHyX!5w;jQSCT#xwvsT;ot~D=kvWBxF5lnVDFb{M`shhEDP=ozFpD4 zc|31OZ)fP?eF=D`5Pt>F$r=4Jg*s-A_nOW!mH)+kfgWw`ENiBH%RW6&CY}Uk_?Mvl zxB6v}9ebeA?-$3^{?+%GIKpZFuJi%lQ&fg9-({2mzkywU67I!ggsV&e+>4`p7edR^ zK^t%FEZ+z1FTO>%&KRgyAMevMa_&}Rw~c+N$BY|}v;u?w=EdKMKFW^#zPN?@lRnSj zNdcqvydPqIjlG)*2fO=|JndXB^l{36br-VMS7GK{{;4;w$s_J!_GEmRxMhA^^E!p& zM_~@b?_(GvR&EQs8cSaeZL*6dDa_*xrg_*df`y*z&jkna(gR)X^R2M2Cnz8Fa%@|9 z_h$Jsh)w1 zdyo7W_a6Ci@qCLxrDWdc$Kou0aF^n{@nh^jz>jm^J3nmr5x3w6doxbwM+5vHo9FXm zjB&v!{J8rs@uPwB2mi7x9M@U@e@zyy4rC!RlX7z}meDb~7mG8wujg#;YXV*@;;sy4 z%#3U_aQ60PoDhMh_A05XSnksS6mR-ArCV8<0NR|0m)vB>ct+5#Jx* zS#HArK==Y*Hl$1T|DNx2fcpyR9Y5T_y+U|H7TimG&jS7jfi5w!#rF$@*OQ(#es)e) zde;9eoksX)2oGe@;Qdd+?JT^%=38qgRK7KoPx>W;n|qL#R)XV_({K+Eu63zOa~o-P zXQg?9IHMnk^N3S_o9S=Rh5NNfSbdPv(udk3S!vn-yEH~zrhi8N?SBZE1a-L(8njj> zuDU$Y=EKN;Sn?nGq-fN;o+sng29E#3x>DPuLq62@m#gg~>%soX#&pIG)mZKSW$j1S zT+(Z1E{SqdzE;Yhz96{!aR2{0HEv3rxaI!*&VIB0U*iY(AbFjG&hHI)s6Ih5(5!wO z`J`_4&zuW?v*MA3{ht?)iLaVZ-mG<6LRc+&2YJ~4Y4w-F?bD^XCD#k`e)6=rCFO_R zupj$oDt&x9skX=S>0l1X-~dK;T;(eE(KE-Ni+_%`cBEB(24m#mKJG`+Iz8}g4CbHI zmwgAGED;R>F6NWm8-QWHf9gu|xDWa>bIRgh198)U5gm7C`h4)LQ280ZF^+1gr%w$N zcBXJDOm>^_5VwHvX|f3@`}Oc=VM}`7Zt*9Jcgx6wxx1+gkRf=Mz8RifX82^rru(Dh z?RgLipmW{1<`a5O`JJ z%0qj%hk3rN@(zYsgInO@kF#Ps(V&B2yKsngRGdp1Wly?^-^HZlo$pVWb3=F4v#wDv zwx|7}14=(spD5fz*kHtA&p{#-?&nfS>U5A6G!#q3J7THy?pUJC>qyBbh`nt^z3!BJ z2a`p6dn}cQPd8G>9FyYkg+zy@y5v9AdX~KJdVh-ZQyt#P+u}?HKkrV1pS*$Re23(p zGf7u#8 zZgBI#6Mbm5t9z3!=^>A)@P2c=(PQ1;F!zYmvtLN|fXa7a7xA}vwwYg1-um_4jF2H(@qz>9xyY|w8* zPhsm$;Vzq}4)YsrN&oy1XDMg=PEqE1Ci%WLqa#}>TfD2?tI;@{b#om@?ZmLxZCl9E z+r#*jhmf(H3t8hEONR4oi+b=RD>eAvbUtc9UcJH+;&(}2+>fV@CK9jtp3=dYvO~QH z@r8M_{CM8SxAZ)=s`{YJSU_!m zg2wvPS0%i-<-}N^e8U3TsoBD>o-rF z;;QYBp4~FmMI8A7UAbOzjNelG+G>Y*S2*(s(_XGm>YRzPf%=s8o=*$jnR6a{VP{dx zn4tsSlP~N48q>+MTeOe!;-Rx!j`K?kekcYGzS}!e@?{sGH)NwRFL9Xjdn~P!&^bh9 z?GyKDo*<_q-V`V9`i_Yk&{s{%ho-kMcD5!;=5|bPd6PX9$aLj5yTWcfdA2)&ymLf< zR(ZZWH1ny~RF)lt2W7EdK7+Gd_)eZ-Y{DO9=tu4j4dp%cT9AGmxIgdnqtWB6mgy>k zaDitWJc$hiJlO$H;KTPacKUC z~oyf&p*~~_M{PF8Ege{Y;3{A-F@m&v3 zbT?+?t*gY{LHrf$OI}Vqa&-I|+0t3@gTyb^m@G>s7m+Rr>`zXUUHpIUlUZ<=@-0~} zJWY1-|GC>HSnJk;gTAURz^!(Y!vE*?`2E-mWAtg84L)Dy=MkrMG-par!@o9-%T)Kh ztg-8iH&Ol<-t5VH>ad?iYtvHGNwXQd4qH8?eyI$YgFI!cxAMeRPt^bu*iA3&gC4yb z9uj^Z@!S#O`=OMEuyuqL1GgFaJ@L)pGVMH-Py8CfY5y6XnR2Q9clvN@*Dt93f0yUa zPUX!DYX9=p$d*-JZNMC+KTqJl!lyJvdo+4Azu;Jtw2!-{3K@}1bRipS@}4?2I`gT_ z9*!Pl<0a=q4!GoLz0{$IiWBs)pzn!9)xjET>C>Y(BT4_p)J?Er3x;8YfsfwH6o56qG* zT$g8Hg7UI|#gzB*ESO6L(;7{Mv~M67pR4VB1N-s9%sh|oFqqe$&Q<%Q4?9C`?D<$K zx+9j#(VesAuFcY9l(0PR+$`pqOL$E0=#JNKpKIsuK_3wwy7w&BjGUE<-&5)cE)qP$ zf^R3R<8v1Kt7U!#78zWmTI zXCMbML|b`*`QheD!kM$o$Y7JgwZ=;48PSJKk*qD_I}RLU*i4^f;I1TGetHAPJGRja zpX)(xCI7s$cFW9r(C2Je!uO|uiFyyYrXTKVPntW^TGEHsbYAKS|4)0crLyRw{Wl92 zcKZw1$AJ&Z$JN+X(v&@ir8bA{`a%deTlotQ>FWtQhcJ!(GGWsQV|*hY@AK02m6jqm=xd(h{; zO!<30l_%xBP2&PnUgOWyd4@0KD=)Qu;#-twR{UY&L$dWV^umJ?XgUU;dkMRqbM{~3 zJL%i+&mRfKE9(TWyolort-2Li=?@b2+Nu0$IzZU(#2?}0UH(5OD@}?p)}4u0Y0kd2 zv-sV#m$==~vmi+OJ<|H)m5L^0;Q9Z6=H2w|#)mcl&(J%v3(UFt^ijgO2%Qrez^0R) zrtgmBMD^~3(b*cOU#}m1cP_Y1KDx-ZGAA z8utCj&wn2t(&s(@ka(x@B=fs@^7-$yn$7-E$14B2#;ch)>N}=*_DyBtuQ%gt(rF9< zpN4~RHTu`o>t~?Bl?+H$M5|~K4=OC3v2KSbzva`D{xbCJfSy&N$LmgsAFP)+(z>=F zmB)I;S;MxAjB?()?H1m3W~y+f?M8Q@4+KMe!&=K37s2KosAD~2Fs3ndVLxeXbHYzH zu^Zk$KUemvk-3SQpK2N;Y?*9QUna-)XkN?2m&pg;lqCKN*%3kfPV5SQjPJ)kN&I5; zkLi!FJ$w14|5qP2_H*$dgNtzb|E6vJw}GSmH}wGbl-ghx9Q}Wj=DuXh3g3l%ZwBti z6D-HK1<0H5^9c8C6(5fNzbViY&v7rS-Q(lfw*=U;h|}0VXuqn9XyzQ?S&kKcQFVgc zn)_%ax7cQ?t67p&r2~fbzE+iF)34{T-%`&j{CbVyFSTNV6%4c-_6}i5A5S8|_uIgKS1`OkDLPCXb;>v6 z|E7)z^2uJI-NQLL7inx+C+ov`HV5rFupgS*fImQc&hU--zdG+E-=1UsuL=KtzijRI zVLL9z|8ZUC#22Aq>|XRJYmcl+;sLxW&MN;lm0#m$;GQJUv!FHb>ok3fJbz9;o4df% ziCijuOZv!T!j)CVr^#D<$nd*`G#%ov;Gd3`z+O& zZ~?3H!a6f)!Q*Cd64x#~w5^U(>q4u&-N`2})oFxwc#hgY+odYgD=z@2{gfT>Q)m35u?s_L+=MEjY$3hqAk8C`y&;o^xVgXtc^aCS|7*IibE5Rv-nYb0 zA6D~8hA-3M;W=x{6J=fP8YjQtdIh|C zOMVsFsxwSl)w$nJkFPvQtI@-=p#P|3!KdHwub!|!if4JWEseoCqS0-wqVLR zjj%(M)3^D`5(&y~6ZSH)V{8D)74nsW?oIHM@+7lm=`*rBPx5&0ZqnDtzhz`;w#IMb z_1>M7Y0_%G{kgH@AEhj;bCfLY{pCqn+Pfo=rN)!8wD+f{%F^C`^3a~>7eotmopY)z z?R|`R&5M{mf_G$o?^ezpXzk3?-m~NNr}ED|M*PE!Z4YG9@V$w4I|^*A=*TJ`{BaLw z!QIFA2;)d}?cS!&cvI4kliuEYCvmSye!xMyOvJj%otn;a>4<~R&WE1JWERHE*x#T# z$a{f7ZYD)Il6)I-jqF761t08ze;#+!(0+>ta`!i^o}=|?$KLL=9LW&n*?Tj5WPPfg zk)cY$ZW3=LLmIdEGPJ*;hdfAoP;!)2CfdK7$|@7TC91!S^ooAU*=y+L@O?_Z+;xE8JpikYT%dR>n?#FXNgF%wJlYBfDjo^*OSq0{dop zU?YyhUN6x26n;?l^k`SyE*cn*&D7yW!~MR$S^Kp!v<43(!mK1vBfM1Ktc5z#BiVCE(5a0LOjejbw}Y zew}+|Y;a_&mhz0Wqz5iyykpK2m292B8_)#}FB2Xbp5urfFBp>~ke68Jc+P|y-!h84 zlxmKKx^HbA@8-25n?rW@a>ln&wg${%Uw5cI*OQ<164p;o()Z~;^R^YqNt~rThJUVb zNbuCZ;8+70)3@G9KR$Ry^z`2we-eb{1mCafnUNF8Sr_}yuhk{UYC5 zv+1Avok+aFca*i9V|AVt=6Rg;rL2!nS-CnJAmK`{y8!D$=aX2z?(Roh(R zZ8Ua~@IdF-uc=RDc}#1u)mG8_dvu5H!K`!@q+1NkU;sN1z+(UHVSGHbJ`0w!Cz==e zxZn8`ep__z*!CbDc5=op-SZ35`L>LqZO<6#E(7)l0qpm)U|AnKbzvqC;E=~XZ}s8x zg5JQkZ^l;I^ZKd2!3SJyYJ*Dl!pT~Hb zeFcUN#v?k*rexu1_;w2yb=OOs*Jga1N<&%?`WFZ3-XEm9-lt<}XOK>JOPx4NIOt1O z3^(}W=0U6ZtJrSxIp{oCvp;Mo=2=7Rd!-L}2R$3g*8Q3Gj873=mVRCRxay+RYE0_f z#R|)6>?t<)4TOQUwiY}2YfE0V))iX?_Y^zh*!xtuHkKUsujkpZ$If%x#QX@WUp<}-RE;}82IjGk2_~7rF+M*W@?;MO?(aU zwY}UO+Q;3YjAg89)}*eBd4>1Hl0~byZ{RV~DxW9jvqzu2`r=OQy=SJ(c?qj%`}b5h zMd(n@&2fsr6#-{<-^rSBJJZ)bO1Y0|j_}q7xAIZ;{0laRvbUiZq<{Ty-PQa6>%|@2 z`>3!JIu?B)d|~QFefRgkH#ShwKL%XbD24G^8*!6Q||6d zQ6hBHeVyn(zn767pS`ob5>FuV=meS&CPB*ycl7pNJ^<$gsg6&D}; zGmjEQo;&!?(6CW7pv!}GK2v+p@hkoHG{0Gyu}_3cc#yfT5A#nRYcsU_ac-2j6~p91 z+`nYvgqOPhEPcoh!n;&Po}Iz-pEERPXk~uH?DY*JJ3dUmx#zGY-Tw}9RV&()w(A7X z1=%u9AJpil`AwJKcNCelUbm6Y+-=k`cwPaI&Z4|jd{f(I&8%z>554Eg=9L{koSU@n zA29PdMwaV5>pb6n?`M6TUD?K69K(XOyjsRKS91p%XWu?>MY7IjKN@$~)!Ajp-5k~v zMYvlge6)@=DhsK%L_+;uF3&5#86K*4%vhk6ejvkR>ZVY#B0ANM=3y%z&9&6uu7H*Gt? znL44KW(4(g_I7wDo`SntV_(kX+Jya{&-p`Mz~AUFXFY+pM0AW^l!{}2TrFO5fA1Aq zyMb&(#Z&lbcv`}m4)R~f^QejEY$wMW|Auc zUOdt+3H z@1{=z$G?eB1`jldzBVht8KwL0gI;e{J!6YpOY3wN(O$6O(|tWNkm;}$+c1l9=_V^) z?pce$k)bObMxL{28nl*Ou{GvFQv#Y2mF$T-g(l9p5lweOQ#j!JM$rV{!{Uu-5noh) zk8?)`uy>0mZP15)3a`G%imf`+4pW!hf#ig5!(Mp|J7{}(GLi=$+u2{tp1qj=Z8fqd zPt@Ab|BhX&3mHK2)e2v9V?umJo;-mQ1%5Nao#QnFn89csS_E{S;H)uDn3+h=kmBRCQHctnvW$xCPQze^k zHDx88cpA>sv&shD4q=={XG?!x9`H1>6+Vim3HWyb{5e8dBy;!$3nX)o&6ckQwo(#=IKUE%u^hP4{|=hRLEUpkcOXV0>JO zyj8)&NgC$@#~E^-sSn+;MjU5Z_&f{BRwEo4yo}WgR?l`~-O%>fJX?0F=q=N^6(J6YV=lG!QF3jk>_d@$^)QRebdSM-0 z($KClNe}#Zwi8-)o{PM;aDSt*t0gN{Tlo#-I`Ll8$c_)vPr~*NKYqR~o4rQ$$}`(` zPe|{e53!ki+j=d!hjD3(`hZG<6f9KX2jOFsITJ;m;afbSe zB(MXN2c4sHihe#YBWd~@?2v`@m7!Lrk$XS=dcv;bEW#&rHf6_D_3zXLZBTp)GcORP z9a`OGOM9uC`ZM4mSEDpS_ZgcezG!Vhrs3I{tXoxks#IO6jYyAkK4%EHv)be6GW7lJ zv?bQQ&<>Mj=>1Z)7kwMp30CBEpIz^bUPSfLZlwxtJKKpo*pSj%zl&*4CGF9E-N_l> zQ|y*)EVd%g7rPUIEnnSeY?|84dYbj6$)bRkVqhxTOOug%q2qq&S)m9pnQfxcIkv)(M$B+X;1A%d9&Z+&ujC>8>I#|Bf^D?r^q=Bm0YWQVwU2_%i$h;2+LeW(`hs zYNazp`y(;0f_8l2BSpzD&wB5{q{hrMTXUS+t+_6?m78-8^Q-8vN%SxH9BkI5-HINstOMHgGg?_Z=BvwaHw_er0EY`M00Vu5rE zaiLb`DZRwfG3=%Iu+1N1UT7Fk9@tUj;aQF3VL`Jkn(0?BR(`;kd0XILA4KPRn^Nk# zboOsYYu=L@Ya=t_O%eRHsueE&Y|rzmm47R3eV66wykYMDNEWQ3Uv7oR$=S!KFZ$xJ zTa&F@vh4%umOR>Cwte7rdLi=X%%UgJ*2h0W{sq!2s%y%St8vvWKbJ4TwxDSbGFv}z z`ID5vz>lZKnZ)h`mTIIJBW=yB^dj}1F6LaQ~ zxA+O(W7wb}&hj+MJ%fB z!DI-TxL0*EVDP)DlW_;Bzm6h1y(5;4dbiuQ_@QzWmm?iU-hWiydQZ-_r0=Ya=ro-r zbhi2m>PYS5*J&Cn6X1yNmc-%?-VpppjOSj0jMb3)R1?9Bm z66pec3zla@Ho|SQlHBX-M27G=+?CNuCCOrZeI^a}*hwe(_`&OK@*>#S;~0Y$Q`d#7 zFY+XJwA)5}KHck7=h(V=1IRNlamm3DGK#ETe30@D6$QF9XOt8xq)bdVS=8*FN5hv`3N=bmByOBom)6v7^#ucTu*r1<4A^Sh*hhRTk!9 zij9tam-2Mnk&M*!r+*;cK3`yK&9=3k`$FMMY}dF{EO{1tHu9c0_UuA0vh(8_3naB~ zg1fa$`+rCJnR4YQotJnTdr1(G5xDN9}4k_#_jn)L&vpbgJ*1Vp;iTTh^r) zSXPYwawBc8Ve>?Lk^0kz%cigndc)#JD(r@lsKfr)MtpSPKRv|RBIIAzRk%<3IF4?W zkMlQoyx%qUe2EwSLu=WTFAfzu69+lFllLo;p~?Z?Z#~T}Z7Z8{6)<77JCAL*$a#HE z?q|?78;q}UUVnOjchrrxR!n^jd?B8%8=MtW-*{J?6;pqo!P!C_8hA=*^tFNl>Pd_4ls&3G&p@C`CkzbW#xCoH(W6aEYB~3U(YM4 zyQ?0@A1@r_5$D|Dnjjz9PPHA-dZLauSYh<)8PM0u@5zA1r$nRZBkXCdNmhEL?E_A9 zK40YM{qdmO{m`KAKHAe0%9l1?Df+=z3oUWZKQ10Baw4}cNK6KAOf;E0bG%c_)qYd5 zR_m7A7jYh~BSmd}v@K-th@X#(E}ngVg?A=@<$bzs*l|-*X-||TU9^493b)@zULR$zH}cp~;l@cH(b=uQ0%Ob%5`h zzk=`DUEsr}`{9}rFWOe&vPR16bBURHs&F5Ne%id-hdz74w0pj3v!}FsV7Ti$`DgWo zEQH&ZFjkuJ=W)!<$KeZeg{%p*JfrvKd7&Qp98a(Jwwsdi#1b#A_R>y!K_(+SzX*+b zn)V_&5U(Zcl5^D|bZR^jZAUke_EOr_Vvi%@(B-kjjAZ&MV%s|o8ZpYp+@;JBx>>B5e3_tV%=mgEY#Day#5WJYa%efgA&{`GtZ9^!A^lo)rA zHo$#yBi}#Uj%}WZOqNe+RNBoIRvG*1>NXcUWqsvSt|0tM@YVAEMa4^x2&dX6btRov z41UF5rMO=73GLz1zKW*B9_yM^M}4`8@%#!dpiKCldq@|ij&9${xYrM>Qy6DSfrlS) z-_g8CU%7jOpSOD>d6S2Glk!$RnKnf_qr-a3-LJm^eSH&?wY|5wg@s{RTLiAllC7tA9SR)xy`qYtJE^?h5TJNXtbb&BqP zq{XQakHm`%k23s$uK$)d==)r3k5+!GC~u5%M_0e!o!nQ^+%r6xea_|XvMss&hXWo+ z4~Pdw54;Nxj2=J-Ft1+p@DpdJ{%pL-xV*Nn6&Z#$Q*E_4#>fNcCd$2ZcR%vc(lA2;&}7kZXp#G^-Y(&StOHs1C3K}>U zG|b-mMLQ!_IsAx;AJu+1`)cs>M4a+1{C4IfBp=JR6wL|pcHc999zWMEgg?#V6EsG| z55o)E1+qVxw$#AyZyBG&nByPOxS`8Sg*u&tMLDCyeQHmU6H*&Pmg4Fw)W-z+ownwF z8hu<$9v_oFmTiyUz5=@?unVnR<>7TZ@dch$(m^}cH#miRZji@3;mzc68vgmf=q!>s z9r!GizCFJ0LbY+hgD0YR=}_T7j}}3v#v_7l6wLYwRx$6FtS_{Rcbj~te8kUx%B6Ro z<=ZTz)t;uppj<`wlu!96Fhw1u&>6v3!d`aLs=S{dT+dG`t@6&K4e}+ed%4oeFVc~3 z6>lzTur^Pz$`~I;y$7u*dAKdA$>x*V->JAz$)X8y|O)o?U3G#IAt9J1I8H- z@0ix~8j+Mr$QSU&ob8*-BRwZWY1Q7^F(_A>u?unjLX@SbG(EV>bZvVGx^rI1l#t~O^izh zE~EVZI9KCcjfHc1rzLfF?$J%rR7mgMLv|W@Dxy3!y?I_uXWmovYxz6i4RLOiG_mBI z51RSOF5)P|Wt3rs$|YZd#y&~A=}Bya60MJbm)o^ILUM|0?rq`gG3zGs8@f2xP2u$E zl>_B=w6)y*#$cgUK2T_#;xlY3cfa{Yp%o7FXb8BQ1@lPUso=d~he%Rvi66#;;l{pDJ)qC|rKkTTZjNTW|0p*xaG6Mfd|9V{C^(AF2w_V~Ne7SLtzi>AMTKMbBH4X?^wQ^uftrVc3E)1#D0dfV9Dlu>zQ+1;VQ z?hYBdyWFiY{4aNF1Dx+Wo%eywEWS+^zhqywGVcc+l|kC7Anl~TlD0aNwrzqa7RaX`!ABZGGagXo44~%lZ?*7+F8P zuG($@Cw}3!XQKZCu&t9WFIQ*pArz2@B5BtGJA^eqF+am+AEeGd>))UUm>lYuhOqpDn5{NJ2rZ)js31%5QqeWQG2sz1qr z;eFh_=}55?Wp3E42LR^lg4rHVR;xd_jn|4TU zxi@}za7NQJj0;bhV?IpysNJ;V2yx<#)`q~J8;pO@FBdw(y$Rlb-NT&V91a zWYZ^EmS320xshq~O&pu9a${M-5sjNF?1h^v?2oZ0zxLsB{Meg}pE<^!t&V}5#ECgu z&Btf(`r*KFwsc#C{c+@^6yBB%BWxM@JYjQGaNf7?lf>_9|BYly^fBooJsgm~HXoB!tCFOtUlA$oDls& zDk52fcKQ^vUf^k?b7`B9)3R-bSAkBdI>)onZDwCchvb9)hI>-13fl9e?x-_~^#GG- zCzF_8yKG>3;u7e(R=lA-)U9UkN|LYvWCR^Ii7~-71JgX|+;!?(C6|eKV$mbW<~GJ3 z>L0f-_UNT<)y}u#2c+%vzEx;djezsVvsr^Z+h+Z_Q`v)W@cBYZ^I{q&Rss72FnX?B zUv3qzx(NN{xu@u_o^N}_yM35%?D3!9)c8;MGvhz<(A|zj_f)ty15>oM)T!eA$!B@z z`4s8(d|GpIiYI-1U8yyW^f8S+*HyUv!-cF7#=m+`39w&qDoLyF38dA&_{vNg)~1vI zJ1(db!ITQ7v($=pMw}S=D6R6V3i35`f$x%UwbI^GY88Q}W|yC5ZIJi-?mRP58RQ>d zTaql0{R15_(GDGxfg883Bw2`VTeohuRoHVSKDY{ZsdO7q^xWIv&VPdOK!C%H z2eR^ynz5kDfp42~JO>>Y9u}WACCvQ#HguupxF5+$h4vJAHE-aXeZI(>M7UWya+r78 zi3FN ziJWm$Knw@w&}C zd5rw7g-$b1PQ5K05sy6UtH>cXgI@>g+h_;H;>%9>vNoLJ>@lA=${${x!kY{|@TNd< z>r1@iHI$3@g1gR47I+UN$HAuwwC{7szd_~YiNB6=XYq;nd88Q!KF$v@F!<{{`RmSF z18#7h2R+gg=L$D?#s&O{1@*%jeO?J^1#>=ealx!X&+weE2EkH(T_uj{P<4dOlvJ9s zwS=udE2(s!ToX2VU#7IgiPldDR--=`Jq;1X<+_lgt-uZE>*z5N4k@YWl-3i$_@2roc zLg=Y+jP1Ut`jRcAeFh7`&^-L2+8e-ki0=>ceOB;|ZJoG;@0r0jc1xmx@2SBz zbKK^8a`3G?R^}3K_6py6K0b@s^2I~3<)1mq_&doRSA+GAMYtIU@|`0+$CEV?$tcgf z!Fo4De2ixpdj4)8w*0eyLH}jWrQ)17*M?U=S6O9SX6!}!_)d7yferf4;>E_Y#3I%h zRz93pf8XYr4HkWV$)@OLD>=uG^2_0u%P(*6az{LoKFeYMQ0-Ruf-ZT7v{ge3J*)fw zWAFXrqq?qp(IX**kOhomY>y3&4>3 za44P-#ih4?ElFrf^YN!qQtJ5T`twS2c?mbyz1D~Fc=U!(pEL-JV~UkT#w5WGPWaf0 zO_=xnuC@1>BLRt%_Wtocmw}^m_TFo+wf5R;uf6tc8Ri(``a@WV!XA>T z2d1=4dv{7p;M|m!3Y00he@e?_V}B=n7vmeilz->C{!+kQP22XJKyC(}ClS`daU2Jo zzedJ#=x?@9b_?G@e{*PRzwYUIxo`?}7oof-d!0V=^<&b;9-ir#LzeyuvTsJ*4c{rp z*MgrPcFJ-k@RwZYiwpZO2BIGP@&xo?I`y;OoT&hsVcG`hMH;%-jBsOz46?c(s%%=N!QrsFT|rz7Db&;PZh8ygGfMugWdtcDtpsUoJeu zIz_qd9=F2>U>C9Xzx0`kVb&A+Z)ZZ^N__VUZ3Jbm*gmmk7Gygs*Ed{(b&nf)b}3}{ zU7^?4SBm0Z3;(h7MZA0cgUOgz|GSotfrkL&@IlnlA6)fx0dZ^}S1~U4_kG;YR`FHm zXCy%WQXD?!{z=sf$}uYCn| zY~*`Cb?hg&w;%ElIa5#ee`pi@)doPk+%~43H_k} z>|}+p#gmZ+I~uNpjic>%VWY^yB)%hoaA4<+xe~~!vae-%;p?@K$IFE;g3bl-u+h{h zb!ZjBT@0H#pV9bop#yrz?>;9PPd!3hr|8tzosXd3m~y;wIFS3gbInrh`QtO{n(qc( zgTCz?>ijtB{5rhreGU4#b4&+6<<#|)mBUvbs2u*tP^I{~!GX%*%CD*4QAJw-vTyyDuIW7D&c4Bx~9Jh_sPs>)7h@L-wp9NrxM?g zLmzwqzRw}_!N=$E`**)60JrSBKY;a;L+F>G-(NnCZ`x76A%lp@#R}MytBqgh?Lh#{RFq=^U@*7euYMoi>o@>Ab`W;$PeXkH+$Z8Ldp7Z0 z4%lg+6$Z`#Z7=OEWkNihpMc)seG|u_PNU!X*|(r;#&_j8W%L?qH{NBYHAZJnSmKDi(CxVIc*$(z1|_Y-(#c0X{wU+so@?hNYa zb?TNWH}|@JZol*OhTnEJ$ANO#z`j1(O!$*7--*NiyYK%KbU8S13)2vfwqx+}T!UTx z(3{-zF7x0!P`bXx@8#mXS$=mJeoO@OJ%yj(%nb4GpfjVsv^3Iw#Pc+050Ul``~g|R z`Y!s&y@ALOc*1_*J{TZp635)^yV;M{W6g{(p_dCIPZ$32Ur^sQs3*LK?mA(o6z}%B zHTywx|2($>_w#4xIS!ujy*#&U^p>2wp9}wl-vsVM8S$Q>6Xdt7XTt~NdLa1v7aim4 z`;3s`=M9`W6L5Lq^Jpj7uQIeB?GWvt1iFU3j7|mRW9Pr1kDo_*)0kr)`V!9g^;k#bIiF5|MP!P?szv7>EHiS9_fIScQ|)6!k}e_b4q6xb zKHWH4JAAZU;Mne?RmHM_2I9ZJ+0fX;xZgyB_^F31%YN*orY|=DX`6QCFDK_7Y&p4a zGU1~w^$6OC|NdtT?e&zGmc`67kCj`W4_`I#J$wm#i2we*2H(oxg3tBg<9lIuUjiTE zzh7_g$v27p{OLYXo@>6mfOnriNBzJY?=WRV+?3J5l35MkbB0SWel0yXYgPkf7Q~p9 zX%wx`_leOQ}y@Sc*_<|-&_Lp%8U&;`w#K$~=N z7SaEFU+G>LCEb5qiuqonyO+?b4%Ojkk56{fv1afKD?9Ld{o>^~7YXOCs@(qPk1)R~ z?}l?fFy9Z~KPl3H@5l1p@R|F}yWykr^xg2YcsKmpXov6h9VYVkbYagyw5yx?=h=5cRnNjs$=BH4c3{3U_k6Ab z{@O&!0`Yve{)WAX$Mb1z&;PXR{ds-szlZhnuV9}6?0Jy;kBRq7SYtmouW!w}^ZJ@c z=k={apMU!?oXI;0D#M@~o!M=^z~3o>Z-hSThYje1O3@FLzVC+1 z2HbEd_9x(5mH+ZftnCljI$^ot%ix`9C-@EAzg-R=ZMSp7jqtnN~m706R^5bqWpz=T?jtNr5x|u4FCt`zFUR}3kZmRQ_G7KW<=I6J z^74EJeD}76dsr)3CdlIr`f7|Lo`Kn69S38NtJ+Q@#7^KXqj`98$&Do^GSdH#h@ zp3fV3g4TF>?u9(}PVFm&55YYV7t8Yjl_$z4?*#xq`e>CW%l=)=Bku>^??FcIU)6t8 z?k`&?*PC%yx&CF#hn4F`sI!zS{DBhs1i(SLR()@M?#92tx<1R+`$zo~dMAZTzt`M< zBlj*m^QU3iy8wB=-urRX&GRbnv$uqu&tv~B_Y{A-xxefm0lRH|c)~W+iQ2ia*c9#w zg0FJ}+T5O}3;(c@?K_lnq<_=>LjUk2(nc9ay=2)Yqb(TQ#`Z8_*Y-ZHL(kZc^CZ5y zUB1g2%>A7+Wp)k+dN{)+$0vg?jE$%VX!|b4UQ=>^-XdYNLw6V2(^uIxkO$?R%sd#! z@Z5l(5L0%1(Le895TZ z1iaV;3(vDj_OH$%PYeB{i3fonGCmLfOZa@SM&^>`+^O-jjGV6V= zX_VnX;5h{R+ui;$@L9(3Kb3fboQL~TDZjH#|1mrnx>rWJ!Ut#w{g(@J zeg}9RWjPhU@P+fooXH1;&tWN3t}i!%btLitJ*FYwIodjw%a)V2jb~}VUuQn~_6W-I zQ?{|(H^kSTIB<*O^xrDBxrEQkw>@{8GV9?q+Yb3A-+DbBJe4Oeb+ol4Hf=DzVV+jRLerQwo875$aZKsSN={X;eVm6iwS zpR_&5muF95>;}D>?357q4z8^L-p{l8Fs{gjJU}jg!a9Ds@PB@Va^#o`eJ;j+g(~sn z_Gw(3q0Oh>Lf6l*ZGUk=i>Ko=q|bMquU-?~$1={woD%wNX@lYeXg?~nA&b)O&da1- zBDAjx)8-Xv&vM4M*Gazi`V-MBwllOtf4ij(As>`2#$3*J@N%#sOgXdM>|;*$=(^Q;b&-#(RCDH@`$X8L)MRI%poA)Yp=G)(xXP6ZvfoUTovo(>aW5AJPJp zSzp!i3pkMV0^~CrI`q=EDwq2}(RQ7Z=flej&sPnf1uV;d4fb+|a?kh4JAa5P`%WL{ zn4d1}ZE640l_O64qHjIxx{O2Eq!RIyKn-|6y$O(Q2a}@9q-0ipYm(Zq`fluhq4K(k`|& z%6sxRZ-Z~j@?&nJ1UO1^&q{wfDfjm+m?L&1-^fD8VgJuK$Y%y<26EqYD?k@>E_enU z_6ScW4CS#O@Q_D@HzK{G2!zGYkIeDf`4w6^c3i1khqOZbSG1??( zU~WmuHHm54QGd^1ymYVv@A(^fAm6(@8v?St8g0{sKYa5yus87=s9)lFrVQ@@x&7d| zZw$|ed_3<#J>XmFH-B$hSkKNm6E^c5GG5xzhcU;tavXG94w3G5N-*{)@5d3+ZKy6B=!rT0(jTXUcyENznIDb1BUi2GdB-xQt&k^5#ifbxHE zU{M%|OIC882W4j2U0ijjx9PZtus(wCV)d=r2K%%hbPrYZVULOnUv-$aYXNlgm$av- z?<&C3CcsB;nS=aQ+p$Ig8NGve7vTyMehXpR^E1SY`%e(A*k6?^!#S(WUyk#^V3#la za0k=DFWU<0{Z`0{9R+gSC@8|bO%5vK7%MO6Iy{>a3a0R67tHZDreG8H2#&1a; z^s#`!{9}pO&#IiYVKN^GdjGefch$Er@56Bv@{{Idoax}eMwSj3nv?@!{>xo0H-D?r zxt8>>Z_1%uz94exYoCqs(>89Oy!>_gH-C1gOCM(q$~O`B&j2sxXO|ZUPdLy@lGZ`s zKDe6U;bc8zLENO>8*tZ)YlMrToKMk3L zu^thCEU!o0dR#HY=WtH|{)^9;>S`X4bm+O=IN-_<+U$q~+asN;bbyUVSr#e^qBkCO59CeN`jE8NQ#QFs8iTjc6 zhqXASbFB?|0P{DL!$Icl`+1?lA7!#pwmfdQ6boeZ#hZcpo zelgmMc7y$7CD5mvdn+(@0_`^#$O0$$3Fp?s(ttn*@y8a@f-^Hcd2 z2-a3We{@b?3(EP>F4Vy@NZU3q97LbOF-tSweYbciPsII!k==EuGxllMu-<6{Pa&Q1 z>_xc_QMbPC!d~E99q2U5NIOw_aFNJ;DR6LHUVzP^&Mcu$0v}}Ew+1{zXZn(+|HIiB zJm>S*-Qb(_<_zJCPr#IK1FtL_WKJA6f@ieJmtRNyv0b3Q>T72@%ZT)AnU44=z0=&% zA?AZUHE|QM))M&Z`abIRRfPZRrSe>X?=0R-9E2l0$}@rKf3rH9)aTvn6o5~FE4c_0|(XxosutoMEscFj-y_2u@h%f1bH z1IJ|IuysZl?#l*zsoj@Nzl-pOPtLty&)|QbzX!0)_hr2ILmtYZbIkvhXZmOCyf;TZ zz6$RjW8b7xxhtH5&w;gC_z3VTs=f-i+zuJX&B#5U3&P$_`?H08H}kn3b@c7$!-(&v zk5u{jCBr2Q?`d;x{L1IU6Lzk^8ZmG~Pb-;+>HmF6U&(i_cDM)Rra!AeTnKSE@9r7I zf#xaDyeaohSM>Dka_A{|P~M#>)KkjNFE5OH_U}S_J&QJKWdn7%^1C8|D-yUOfh!WY zB7rLsxFUi7e@Vb~oWkJW_Tbu%Ygb`#+8$??#O*=&5U%5efk3k}TjE9wgI}w_6_mI+ zjQd&$R~T2bDi37f`_2U9g&$HkXl84ngn?u7P9ZZUhR0Uw3gMEMmPo@#aj}HP4sC0hRIN zpzw){_YaL@#aJ8@3=ZBuG>(;HagYaleP|pXRU9GQD2u7)J_&ahR}L3(hRt;;c_i^p z2<1T-;+SUczY`7@nSi4ix8LhW{a|L`X!*SmFfVZIP#o!h4~}0bj@-Wo$1fEJ+4;SG zEQ`$;Fb_}Kh;jEPU5&8mPclOo*8r|zTsv@m5tsS(g!Q2(XYT!aZ`K=ifIk-(M!n9o zVf?{w+BSLt*rk)L(d(PbZ_3p||^tA$yVO)3P z`n~8E@VhMi3gKnySyDlpIa{uGS8g8tdl0;*FtGk~Vc^r-3j?32!F8xG@JM@M;L(c0 z52o%}+uhj^bvq)__C#kq<3_sTZf7d%X1deqR97}0b6YpN52oXt_uTj3T6c3iTkE!U z#p6#UIyX$IbeK8z`(}Q)vpv!oOM=9zo@hLsO{6;AP>(aEvU!Dj7oXe*?9-IWwb@iU z4We$mCm!u4EXWrIk`1_E+mp}YdJz}O*0CMeqquhB+E*CptiZLdF!0oKxRSVP5FRZI zq-Nq;j|=^G3UH~jg@JSkR}-#gT!2jv6$YNH!nG0?!cXqS1sYGDC=7Ige%Ewd$kT=N zt`=Nda6O0XC0u~X)Z#+EOfRlIxEOc7Fp!;zs|nYmxVGSW4i|7|hYJJU;I|w3y1_$t zJFXnAUAU0HdjJ=B-x$Qzh-*DAq;Gr?*Ku6u3j>?n!oX+G-~ykY1C~?Z9;y>D9QxxWHEr^7VkPo_<__-weJw8gMm3e&C}MaeuN4 zcn+z`InD+1OFCN)9z3|kNg}+Z5tFNpTjbujrSZarV}VA7FKh`kI!>^uDF~Zac=MnC z`Az70Q`Yio1boTp!V?Xxpx%y5=P zfT`)3E#!CW)~x~uu!6t`gH8}XguzzTg(}>fP|JlDwtuI;zyAVm)oq2gYTS_l+>&tt z$CLb68ennS1juoc$qV?Gbb`xTn##8T;labfP}@PLG1<8B#NmygC&-y{Rynw; z@a#BkO@*d5paeTWXpjr=Ruf{HQx4#k{v7ZfJa#bI=mZ<31dx9D%uw3}-kZu%4>OyR zhc8rp)Va{#f1csSmj3gsp9=?tGHZ+fdEZ)I)g!ox-&`Tm30Bd-fIR-UAOy;Quafd^ zIe7Bq$^IVlaRC+YoNsH}($;n!)d{(wK1Y+NhvcYpp|!oIz4e0LTh2=QzqT!PkVo=C zdiXUtAB7lD?aryTWKXi~lykPFrKJ_+Iruiz2lx-3diUhN;tqcC+amN@fKS5zx@9Wq zGhb617zO>UTR$Us1sw2lhrDn3N<NyU+CWgzFJ1fuj6by ze0WRM=x|GNV;lGlI*r0-xl>+V4qk>^D1cKEOTHZE%!Omej^Ta+_^BW)G1VF2Ekr2z zK8o;0#lN+GOZj;x6gnRYEtv^8upxXP8_Exbl=95anz<0Z{%Zh=bI#eiXB+=di(F71VNW*{1S1=;%EZH209+L8rfeYgOZxZ3@pC zrF=prf18oVmTiq-pb7DeXmSpMEEER%26)_qp$q+^(m=Ncw;tc>EQ7iqKK!o^@lrmj zDTierY8euk@3q&q zIOe75`99b{AXGPGFvK7qgA~)EP5KM}K61K$H>`l5JV^ z5JCJ@c(^k#4c6{VvT0_LvO>5A2%)a{HQ^@@@~1HPUHArvR^kqy;^21Ng9u|T z9XY3IM@9;R{~7+n5zLSLXZRgQUc^0J7#Qe7I{b}+v$*Fp&#`ry=jcq_j~51xVh)9Q zk0v$m(Q3_mbUo6M_bBP_!X5edA#NY={ym>@{kQdnfgd1_`CskR{I8zZ{IAB44*ah_ zi~K0ZFyQ{%aADvi@gd*Itiqin&&YprFX91z5_m}O^ z0Dn`@;?JezU*PDdnHeVk;ipzTwC>Xn-}hkiXaB^9;qJ97@BR4dPyE4}rL(t8 zDaLZAWuPjq8zXGiVAMLt+ppD!(!8(ScscE}6%40dktmY~8_A5!^M z!*0Q@4bGW~dm49yVc!Pl4B)=2Fjx&6)*rw+1|O~}3_g-841QAj8Ob9p&eiN_HSAj}?5~DD z8z|nf!)MH|%=?+>yX9AMiJt=8Bfrv1{Fq+U5#{QlzvJl}s^0AqbQu*e@s}VgQ30C} z@>C|$8jZ!F4hiT(M`tSiWLGBJy>U~|=BLP15$^7LR;*kFwY?8|d;bFuHvi#6Yu7#e z$xp5S^k)dSu04@)S2RED;x=}YXUxRT!b$?;7w?RwV)42}XI8^e%o4@xl9(u}%OswP zJL#@en!2m0@K+_hlK>fC{!F$DGdIMi0O?3qBD)#wr?oASO!6L0r5QHK?VB@+XaosL ze}U5#XKu&oh-994yeqy@0{D~7ZjSId6UCHNr{g>k-`tvtbj57?id1J?VncUVMCO)a z@r{XS+)c#XxxyVL2RFvMGMsjr>rG-wa?A+eK9s*BjohutxEs9`GJ~KvDJD5>(`-w0 z#_E);u+$pq?2LESbw)bk5`MVziO$rfPCJ3-*4(!etjsTBo286&pcazIVKbr%GfUmM zJ+_=~4~hg(i!Yk)c5Bj^E_ZIS=VENY6W`=#%TX4Kd?CM}Hu2fbY2_Cv>k=L5WV|EZ znH8Zb2aGq8kcjaxHZ2|5U^GYb#l|a?Mmr|nQzR3PWHPB}0(3V*eFzFI8LN;czlrzq zoA5>XjXo9?+T=I!UVan4D1WEW&o~_&-F2POtXCfyXn52o5oI%)YL9n8rQ=@D(UDM9 zg`Olb?Zqldbf&wr#h_E2iD%=T8=X{lc1*&el+2nMZALpvrLuKsQ(9XXWh60a3Q5Hh znI~AZXsj#Mu{7q&9pTQnHf+h~)6;Y^l5IMz=?TB}zx;DFL zyI={^)tfWfc!xDy7w6lU=*o6SlD|2d)BMn?<_FiVgTYH=6OklLTo;S&6vK6?HiSL= zZY&X{ZHa&tGzt7or(i8DA8VWMerQETTZ((TN$?QxZFNqq_2y_(kYnSwYRw+CqrvF*MXmje|+t25;1wFmlQ5Ke69Om)TUVr`M` zWY)%KA{*neI*{4W73rvB1CG1PGU?1hZR6qJ3P&4U$8OUV*vsM*IJ!~>E*ne0jd3=_ zv#>#+i_QWC$hI4TnQRPQfZBOqwAlSf*9Na2U|Lr^Q->>+gn(emA{j`5=A3D;-E}*NSnNBVq+l$#RSb`x@mDelgl(?dBvA|3u??jOVfmvke^u6QK2*(Js}eaQ@Ra(#6J zB?#w>zGODulU>Tp_^pU^&dUOGB<8M3bawZ+?RboLnN-Jl01L7alk|krNRTxPusYVY zmUzU!Po`2&WZY!pi8%7GCfWPD0`InVXQellR3RlIXhwS%9~X`q`qZRh!?`!r-D#hR zzvwTks=>+ZN@Y{gRFaaI@3=KAa$7R8A)^t@grD2`l!#J82y4(q4ULIk8_!DRbf;Z- zP?3%-+;caQ1+JvXd2VR&k~KYU9V(a)w)hS~Q|Y@*ZD5?ylYJZ}Pl=#biy5gd*6XIXh^FwzSNyPH#1uRjEw!Mem2A(;3(t%1muQVWizI4&g!MF&=phu z;k8L6YTRAg=pY2}CZe$URhg);NEw?|;O^@L1ja2)RhO^U7%m;Dd_2V}WYT>vN<21(+fRbreadOjv! zjZL0k6lAKc4W29O#f`*bUGhN2Y)!3!worbG531-&asBN9RzEjW^j*YczFbxD3S$?%hoDv?W@A!}V6=bbriHz%utO{dJ>&;wml``O&HhHHfQ2- z8E@GBq%+mYNrZS;8hFsLYM+933biJlRBO5}n}|MPJFtzZXr#3}iD`m(hJ&R!sV>p;<~UO8`v+) zj~$kck7TkY7I;F&(io1R9m)i3lpwU5te9#x;yz~df2z2s~+d;rm_koAktieQ$?Cn!kM7qEGvh$5F zs#?;q<2cMW*@;sb1)zLr!Vptk7Sp_St#SBnn$I847@fj;+;)$cr_mxoZY%=jnP}~9 zb1+>@be$Li2%>S_nB!RvC4>Gi0b$k&SfUNa%EY7n;lFmbIy#7Vb-|0X#?s{TvOL`B z&5S~gGx08&kOqpHN9M*JbL;BROk}xd#@4wjkc6 zoz5q98r$oE#Hn_vC>lk^Q&FFlpW9vQ&Mo?Z#HYx`_d~a;^%n2JOqY0Q;#ZKoD@~my z64`z2oIHA?79BSfqv$-VFQ|R3j%PeOM?jyIyQKZ_a8ocCfU@{YgJ_sUfdtu4xPt+7{dRek32^7hbIJ}tZESK)vUFiz2#aU!OoP2WCuPN;!h zwY8^njbmf&CFs*1UP{N9CPp4$gyjyrEF8Q#&jI!EVGCMB1v=4W9KKR9gAKcuC4-6l z`0c0mmW~7cNwjTt@4frJHLF&-HBuMg1%+@oqH~9jgAO&OF_b3cFH^%G))oT(!-BGU zvF_tdrE;mt8`r7cXm3iT(SEC_J2mvqX=ylb1KZ*6eZ#<;YD134f19=9I*RS*rQyI< zSW;$;#;ND7NGFC|*6fL|^_%m-QRvKIW*tspW^6ux3d^s2Mw5|5$K#v=!-$tXPYO~L zcdf>loUZtj-Equ*lPmGpWPDcCcay0@o4=7(&0Nnj^wUYCes?O5%zF$DV+P9wo9hzo z;yf=P|BP=#6~I_`x3$3)i^RH$shUoM+?q&b>NX}~@l@TWL=2NFbiaXt`NyTV#}gac zvj#xuV#j!UU27r>1=U=dkB(TK$YE?eCMi-~==749W58mbWPurLYi+D+#rT0si;fAS zNn!>knzdng)mY_}7?w`v5o@rL(uP?a>7kI%hU=73?YR8$Qb5z6&Fxk^x^7uF2fTH6 zbY{#tX5Ae0>S8hCaIy)BAqKlL+|e^pzu^>XJj2xTZh=QP*@1bDtX*LW!9c=O^CG1a zL`E6U@!k=mxSR!xOkbW^WTS{4<4O9P=sL{EOvZP<2uaeVV1e(5^q`N1mZQ!Yk%g3b z_uf*VPE-aI?1jGpmQS{3F?nC(7L>=9fcjnNjm3)vKVY(sY^$RmChZ$AMtmm;aR{hZ8#~_*lkI^5zrh$6*@+r^2Ax&@@Ac>2egD%GNAM2 zZD!OzM&2H2=C|`0Rt4D}P#_z0+Pag)IYIRJZX}8qQ_>6NjuhYko;zgc+iah@$o9(|G)4(gP`L-Z8EhUsYx`;6lW@ z>2J7e@9CDV3x%kQs9NK}V5hs;Q)z!i zJs@%X;+bd^`=mHRqQOD8fmIOL40mpt0qh)7`~Q@7H^M&~d`GHV`aHh*R~M->YooR{ zp}&l8y*OUvx9F)1q%Bi(W5P=#&e(7(_ieS3&xbqX9j);g$0V!oUAw?p#nCh3z5WFD zzBtRkeJotv;B50H`}hrA-K}jHk9!@pmJ;jfoB^YaZsUShrI}~{NXURb?b<;`&=96wCh6}PKq${o-n3%QBO`;>j zvyuKE`fqs53_7qooKb`Uw7oZ$^3ZL-u4dq<%1UA+E8)3VT0yxmX(S%P+)Q!vSmp14 z$Da$k`#FWPOjzL_$jP`$>{$0f^qG7YR8k$;$6AKxQk%Ybp=<(RzEWHhY`lTLa=Rjd zD-yUOfh!WYB7rLsxFUfo61XCPD-yUOfh!WYB7y&95~#^wB_%WePeCAOiPT`xXZ{x) zyn{GUg}Bn1SZaPR(h;7C@T8jMh|u&}ge$PMHI<(K4Ac4M`{bGh5+OJdw+3;QnCAOD z?u2QaGrN|_Gs}HbS6H6;s>VlST$j^4F@$fH@GQ!0kkeBxcJcv0!XWL; z((o)J$HAS5o4w$3l;PkDc%DsUW8ZV1m|&t=S5mG zww$LCuIY?#!1{=!k0LxD>r^H^&v}YlvAATzL4+41F~!oBC49aH;o7cZxDnyHWy>NK zel@}iu}Q*)*CV_rC34EQ<2lrm>auAp|6*NC6x=L-eQW{Ck!QJYi`B_BMKP<*4E&QX1f5;ba;j@Tei}~WP@;L-F&bmZu zlMO$I@WY8z(uQ9|_>+L2W#C^z_)~zl;ll{82fPiRK={)Msh^=Uc>c^E>oe>B5$*+2 zJV(nAeiTb-Ty*N*AZ0%~9dVCUCpx3a?pR!9bd-AVN7ZK1oU^N77;-w=g!sp6Bs2B- z=sJX3G>rG;+UEZ`!iIAhw;)W~4i;vyhL88evj2&2K=JmVW}Sl0};&%b$aB;6hdGOx-5xiSJ5NMBEJ9X?i9}M2bFT$jv*+^34daU0EgZxDpvovn$jR39; z=`T|9D$`de1Mu()aOd&v|3EzN=Q0cg{Cl&P{~xUP zSEvsn7p@v6Gj-VS$9jH+`acnu!+a)h0q}t{Uh*Rv&i5d$0d?S# zzZ!0Tg3i~aI+L5{{{`qD4_qd17&yk{^~g%wI=)8py@E3lDC6VPiQkVN1t)$KN$SGF zH4{$Ws#(tZIL`8uck00~c-^OEApfF6PXoWyiAR4q<$jzpLcZezrksFb`H!p|&xh9E zt5W9%KCC#8;jZwHe9qZc7<{$C@R9~EJFMP5Chg!=_6Ov%#h=E#NLkU>SE&a`KQgLi z7!sYY)x0~4X)NcfyDaW#E!*6ydy46lfuy@EhgfaJKVax1KWPw;t#8VQ^*%n1cm@KU z*ayIu<~DRvZm$|$Ar9)vtGEl#&=J9rMtC>#Z4%yoz;4$${Yz6Bnzl-rTS?k;W;7m~_gZn^FWR4g9Vw*)~ zuT>P&g?F6ELb!ndL6#{D`vcaik!PMTkmW!CM>E7DN$TipYZT__OW@%++HAF@h4rv* z9J;VE{%6kT0GC#{*C1EsXIh;0g=xFS;vhZp^6Fl~cETOkw*T5*g&PRq9r;98s*_9U z%b%6D^x6reO)Lzaa5WEXP!Yt~lh@cUqC5j5;B!>@(Dx?!o|xh}F;iiVF#Q~^-CBO~ z!?KIL5LwnF+Bl9t+c*(6ctLvzdO*IJ|5#ctEk{kZJ>EJ08N!?PaAGBS$abW$DF=VV zU#8tTu^afx*w1)rFp7r11Cn;+{t#A@yYV)S$u0SYl@GSh{BfmC-53fX9(qWgPK;<8 z+aUR3-Sa-!gS10hcdz0ZGS4#44(=^ekfVK_lCiT(i7IqBC3&6@V^&Rp%Ii&fl{7ydV zD>LdHyxDxpJM%$5U$4@9l=;bL4Go|CPon-*wl+IiRsDQ@rRHV-G1!d!pgD+(^)459 zrp|dfQ;qa8Tn!p8Z46=3Alwk;J{@_75GKxjxPX_sL*Aehg~D?zixm9}Ecfd<<>B(> zmMZ)L%Q{@9uu@j=DDq@kXX^9oy|%0(BF)40L^}ewOUb90N2*?U8jg+QX}6CS%1OFX zzbF^WCT(&h%cW{UyD@C+q|klIm*2;0QSY%;d}BOA8$Emiv?0?UowxPH84azkA5K@k zei&4{!1@~4iE@EIYd370727w8bfo!h;YsiUc@+u=vZ=yv>^ThYhx_ zWy(kD?MJ2$6u8)Uxb9A3q>O*mtoW$w_8J&bT?Jj%h2rZe(*4nvi|f!Y6U)EoWMH`< z5n6qUL+Ymrw1G$3i32$VqB?#;8GkgQaFi8gz0;P{ULWo;`DEHo%KS#V=A)ho zFVHD1-(r~`d!x^!hmgLHG#ATU*&F@7_yfX=!XG#Bkhj9uEBsksyuia{rH_Deo~qC^ zp3w;%D-YTz*?jrk@pu30oW0*u$xx5*?7?0SPvclk-GYQ+>Vdr1pwDs@RU#4SG zSG!1i@5VIDa@Pa zD4$!U4o@3hIF0hk7$U%|D&y0JU*w4c=|9nN74 zYFg4uTd8TBk1%PSURsMuL)kPf8d+d;`e$1-?Ps+}+b8lZrkQg6Y_&~G7Sr~cd@-BW zSxg%+Y0xFb8I3Gn0Hs3sXQpoLyczVyOFL7c`J{ZpOR|_&t!Yv|N$V`8g-sgECuwHo zz_hmHx4AS5e@GUisH}?Nbi^Kyyqw)QUoAOh5 z6Mx*qqs$tweuY7OARNW(L6=10)~o!7hI zK%C%)etCT|bd%+ku|*l4TY#&s@Fd z-^gK|gA?44#m|BNtu=rPU_r^~+0UQV{G6*JU6oTx)^>hw>i?Aye_CrY()on6m!BIQ z7y(|Lv&G2*X1zeZqy)Z1=fD^HK-%eysq3@VN>^mY@>5RWopzMZM{0F`cOV7}D{V>b zyqACFCGsQRPoFdS8$la&=gU8qKB|&(8$+M@K}+aglwZ!7kQZ?|kA0RpP=*WVKj8cc zk;gj>v&`EeuV!35dVdG97ky>iuEHS0l(QXk+3?__NXNzT2<3)(OP)!gxQz|ryn)nj zSn&wn7d77)Jg7_I0lZLuJlwAG_gWS4V;vDMq2P5KSyOq3?T2M3!&s5Armo&OF7eX} z1%9mxM;2$Y3Qzk7__@>N9Zh7|jz&}%{6z)gAqVOs+rc|0w7kCnP0_Dj@DKn`2XKK; zi32R#skA2(H@FXZ&zbZ9Ue-wBrBR&8DhF3!IqQipgAVDrN{@L-Lu`TzcHt*5PH;9G zdEhSdFW}3Ye`!}fneQ0VIi{o^P5UFVu0hz;4exAM{&ASJb>yYI#TNzcx4;MaCr)qf z26e%_QpZC@oLpx?alCr?#SSAs;#p&OC@T!oS3ZOClHMfK8RJsD^H3^MD{G=F(0 zTd{-SQOcB77+YSZkzdvs^NCLgnv{j4;m$OFT?riG1aF0=`}5hl75hW}z&Bx;PwbVy zd;z2nY5iGS=;1x5`7kD7)q8sO%NCWBCj;^T9F&vQJJO;K@y`16V9ONF>Y-nEtbV-p zT(SO%j~DTXJoXiJMQr&@@B*B)qn=*PL>|!gm)p|?;AXs~Q34(NjWkvoUYb;PmJeHB zgW$*359Q8hD{IomzwVFrLBt(0RxY`QJT_I5UTbF+cZ#$SaNP zFWx<{3o#s!@aoeHFR+CY{s zva(F?od7I&D{(q;bf_jVew6l~JeQeBgL~9v9Igt?*+<(o(d<(gZ8T}-eSgIU=Y;sy zAq{+wqE1K2&p=`DmoYq}Er>lbe6ft9X&>x*EhFIxOPys|Moql+Gl}!`A&pmjcw^Me zV~sv%_$ueC)ErxAA2)hN=}<1T9h_6AzEKxQgZjWQ+l}}~D~%qb4D{!%-g)|pIO-tt zGY>Cs&KbHddi9*P&4GZt&M$t!uP9H*hJBCJ>kGbep4I2~Y}^6GOMX57bGq^?edMD^ zA2MyM7x4)H3bdvD0nXYo&gohH$tUq2VR|6*L?XRO=JtMtsBN$Fm(Zv*#O|@PTOumS@`DGv~lNbd$37+g^ql zPv3&^;>QH^%&|J2O=sHZ2fLMzqu`(Q0$V9^Z2)b= z6Th^XFz}&1NS}I79;_^F*pn&fQU9bofy`{KbA2$X{D@pg&V-TnJIahSFi#eZ8r}GHi@^{2BAdI-{WI{99yRp}c}N<{$a=xN zK-$aSKQ4d7z&>@E{NR5ez{6}3Jkvt@wF^MOy0JFF>zhH3zO|j(p=><+ITLxx#`#zQ zh4tG19@2`+p5Y5CmBz?A(>I$wIcjY91yip~*M7bxYS(lw95Q*EY+m#UQQZr7VZ@|? z7wPi@k|uVqfFzd7({ISJ061x*=g8X}E`EtF6r?=NFb4;Ys2wV-MzD-=$+k#6Ut-@b z_NnkFqJWojq0FRCQGcR4I}!IR>kT6w$V=+&X{03~JJ>Jj#~Va0+ym2z4TPDvY14(Q z;uU^Ffax9E7B7Hz>YiJjHOq+L}2|B%W?{0Thkyo#7<(Tl=i z<)_dCy3I;k+WcvWB?yz;L6R+C}s zMBXv|&?|d<{wi?3g7SiPp5O2C<^amHuYRS+@V^dlVdc@&Khi!AKF%R+J?JCPOo_?fWb!_2@PaPsldjS>`&Mhw z)&V!clyOX&`q23aEsDeH7I~!139J3~BL>-H+!L6eV9VlyeiL;1w55q7xw7e0C+!R2 zUR1ocZlqj|;1l>+PR`d#T<{XI^OZ$p&>%4RGSHJcon~Ycz{+nNs*WQ{*f?EB>z#Ff z;YGj(=GYH4&=%w;&~Fl3Xrsjz=F8fZSJXqk3^q=EB`>cW>WjU4`LdkC9or7vD2G>9 zke81Zx7TL`{)J1!>pFqu0msEgWKVCt?6|^98Lu*BovQN;+$+`2gKV^~$xo~>u;7F3 zZzA=De&0&)$+GV>VYCI>M$%-x4(`*t$Q63aaqrJ^c*S^}`<(I<&s+I>x}7g~n4aJslzjPiO&_Dvz(smOE5tej9NV)CSWi#k zuO;{~p1P4eH@#Kg4)B_Kf>q@{>pdwu^nT>2LFs zssWQl+&aLoG-XGe_zcW%F52)h{Ve3feByuXMqY&NcvgH3#7SQ4F-_yt9HFmvB|i!6 zl4CNhU*qS)RAHQypR`YT98;%_z#aDSr}a42hLIm!tVgdd%PbEZH>=DholyL!lj3{~ z>VtlTzfLN8h!3z{o#1>UDG7|nst4CBbkugJ-2iMLl8Gh~+~1V1Xc8Lv3h)e>pIoc7Ua409hW2C3KEnot zp)>TYc(-kmw5e|*f7}`Ne4BmNHb*1r1m2FpnVdbkA3Q&K7wApEg|ne!M)xbJ`_S>0@5%>|R^fb#oR&hTh? zssp`4q&-LcoXSy!J33%&_!;mv9WZM=II&Mxxe5ngG=CN9O!n3Es9!Y3pAXF8I&S_d zEfal9uUzEY`uMQ@3(fdHe^rm*&R4#mjR${Cs!MG4F!ruR8AI9de_L>Cbt5pH{qPZ8~37rtq*~hGtFoLhkKHxvES*1zz)2 z6}TFiccI9#s#(i%70O}t6!T0ADUYh8rd6edmd0&2aqIl>3ppOlS3RdZuuQ}!YY@Gt z>qkv}P6r;A`2gAj^v1Ra+ELPmoEDn#VZLf#5myyx{qDFH#>mqYBNr=k_Wj5Dz$?nc zIMv&Q?EgaOw-u@y)gsIPFVIil!c5n z4ISxQhOJGI)+{z~iqReNPhH>`k#}jQcbmI^eqp+aV;cqyk#&tP&z&fHv#FN=V5*Te zqJFiEQ^r zo$A2HN)sK+#UGw(+MFlXg^&?xfsV+zR>Qgnt2(BI)4bD+K2Sa)Bjh68xc$^p2J9(mh)Bm1{@_+|#sE(J4cXrnG=z0428@_`?#zrrJUmbgG{4t?UQjSkbl zvUy>fLF-zc7M{zwxWu zm9DX^7tw{?7*F>j>;5C?riAWCE?3^um!Nz2KZ5RT&e`Wb@~qUi%xkkv&}P_mj*Hee z(uS__`B)#+4_RB-srp6VW&HXL?TxgD7a;TL21kwR)um|3ep~uf&82QQQdVYnV-^AVnY1@(i^9JAz)>-dK6xvJl@RD8f|)u)qr!)u=fzL3J{JCsZH zD?X+Vzb>sZAx&I)!V7O>@_bC%@O8#!jhSl&A5wqdi}7RT9T>NxI0hRC=(D_Ui?mlC z1+CMpBa9`nwW>1<#!*k#9a5e+413iSz@Xe?3b4s#Kk-z?> zG5qJR?>G8|GE+WMrUxKHIIw0ykSGWvO- zu8Cdj1&%cI0l4{2x$QZYB*ojx`1Zs6FH(nYSgmqmpFvwH zYq{W4#`0*_H@skEajlU}&_jcBkAcR_xZ3t@`}HyO@bM55{1WL|+XCD-mKogFU5s0^ zSmZ^2f%{*8OU4E_j9jLUYqTt!&lDL>Db_P>)eWb|)idVQM5me7<2^%W`fS<~zt1al zZ)6*SeWXvqw#PLGv5U2+qiWL@Lz>s%9fM2iw~@RR@-r$7kKTM#QNH8nvSsdW1n_}s z{pf?K?;Bqbe#8bydCQAscx~8U2F*qrKcmqrLw-gRapG*PcxIWMkDZ}oVDVA8)@08J z;4?3^qmo7D#%375Mar~8XA{h(dlI#)P@ZC>n8TOyv+wW%3ekNFwUYxcvG8QMmE z#t!9?_C(rBpxzolu~{=lwcO0dJh;3(Z=)Wy-eeD{ebrvp>}Hz!W1FJ9N*(0grSI2k zx#Y}#HJ)ZpYG%9T8!Ip7`z@T;oJm_R<%#3{#^s_PGqwHYXZHEVCa7onP8|Q}8V=6Y z!6z|T7c&n@IYb|B3Mx;|4Ms<2%J+gr74Vg%R4d*U$I0-3c!DD!?M}vi z&^Z}D$ee_$b<1}Hq;Ls}_?n9R&9?P_wJG=XB7gezh@yQHoI6+^w9k{{aQezTdmUk*<6fCNQ*Nd^ z+HO9K<1e!l25eSv=4XpuAs>8Ap;fi22cHy@Z#B$Oxy!sMb%wT0`UuEbU>_Z8@5Ihj z8@wV1;4L+aqpczz=nL|5R)epX z)KFxoCBjBlBScm+4|u)g?AT=p5dH25B9yjG=Yq&+4L zJV~0+sj(k{ozFhMnsTCyflq9%4;OXH;fcOUF^Q9~OUZs6r z%QM#@U*a#db|;efW;u3ginEjaEA~I~*%;0XpuDmM?V_)(Ryj!==-{581esEH61P|H zoPW?bhz@kDjCi*`3jNAo#sCi)viwIOU&?}bs3XkJHpsCL?-Gx@;051u9HQ}w=k`gN zWc{1_VvmLakFzS|n@T($(dgkVYZQ(?&e8RRY26$*YmT@0#wxe$Aw9riEm3X6z4+MQ zCcOK}?+9!ApxwU!+4CZQgSZa_p6oVz|MRyPf0pAcq~+)IGF{}2HRJ@(G0WdF40L6M z{F3Jwj}J*8nw8l&AI6_#Oen+{a;kk^;hF z!-TfdsFPzJ!60~;=PDheFN428U5sLVXJ!&Quv=*f4MWeP{B=|#W|{RdpUJ)G1{wzML#|kf3xDJ&0#<2^+({J?Hv0wbv_>H z#6g=Qb~}uGXyc^qrztjx{T0d#ABE+R@$`Z{N{8)y!8#v~m6$VTzd6~;#zg>q1uozo zyB=-kz%h1`Iepq_S#y3#?X}>Ug1IFzyO?HOTEz8AKx9?Shp`#Hw21GpX_k)Wf@|rI#2yJ%iKflK7}1Kx6!P5rH>v$eLO1m-Qq}OaMV%E^lP;vlD{ZN zBWo!OX#381-AvnBS8aI1xY*2-iUpgUdY#;Wt-v-H+jc6U)OK)p|7&KWRFZ%Sr;8|$O&b`j$ zYhkhMUNfivx$rrqN8QK19V#aG@8=gD@X4TB`)c-~WA;%6J=shD?)96{3DH+MDUax_(*S-erU1jCw?RE2EXi0$zlt++26d_v|p(U$bjPl*5A<}*OQ^YPuuWa?xwSH__j**w@LcTtzG#RhFmpPyV)AZ5!;{M2FFbGS0pWi82Xax3!Gp%aq5P z+bvTWl3uZF=u6Z5vb^E3A2?(_UE{QPHY9=Y${$M_wBd_x1` zU~5=cyuA7AH3*|zq8rn}|A@|Ccx`?NalOW;C?h)9!5yj-l862abm^TQAKWCstz32Q zW6p3WyWz>1LTD{9f=ctSZQPzKssba8!Yco|Z7pf>V70uc-`5~JvCAyKYv&e_7vtZ5I&JEjeG2g@t$YLWi40L@t_LEl zehcVZ+4|ZJ@mt=YIrlxV>M`g(@$FH5sIRd5`GqJS?Gi5G#oPNpS-XzoI@;e1fj;{Q zsWas{zaV77rc7RXqm)T_gb$N%Kp6RD4R9Qc?YG3&Aq~hfWZ##%1FW)*iL3&TEcW7G zH1VV#;51T(Bj+O#f?jE41fc`Lf&lfx|gHhSr@)Th8%( z$;pGj2>)NdHIlz`r@~Tp;x|J7slTElVWfrejEjB;ZN=a|_4RC=XCu%CJ^voh-aKYM z@}0xgt9jWE+q}|`uS6Q&nZ+w7b&)nqUD`7AD_^8Z+w&jF8}P~4%kqXYv%LN_N6${O z4Gn>Q`)?~lGTsKCkOiUMEM2ese;BzAo&WdCRebAlvi1DkG9yEgHICkIH;fute=KNZ zt#zMY3Oruk>SDQB5BU+5g;(zuH}gPFXy+uPl<1_8ZE8uY;u%w|Ci~IB~nzDcw{{&~E zEVO%|>R{s{vWC*M8|fGW&!kP_xhV_f3?_V`h^L$E5Zlqja#)x$!o)D0fU{XTQ?a<2 zD{4CJgQ*})$7XzHy$cKf(thu%@}*ZPFI>w{W#Tc8L+`5fr3XxY?8P%@F68f8<4d2; z^hnnReBZn?n^F91_m)R|Y6$f}?iR8Xg8x~hQx3JuVvqx)#m3+DqA#DrG-aB4eAn1= zBQ1*K_2oR8{9VJod=-)}9yeovyUzR4sw53ZEyugEiMYI9xr{nzc?~dKR}eEsCzsLo z+w|$0Kd}+#J@F&;8Lg9LY-1KCNSHZzEkBv`aBT6V&on%=#;KfLl*h8?eCaOJ@rrIo znjc9Lc`kd&mtKwfE{aprPavK0Y+i=ty!1l6lqPL|*?C_+$E4xyeadq=boB=^NfBkKdk8!r!Q9kzAPKhE?#2O&oCYBwqWvOI1q_FEA1y-WpI~h zn&35c9j-BHj=>9eq7{vTyoXoXw8u509Hpf1+=g3_hVLYuhYiAM`A%gqb!p~XUhF?iH{Ju0c4ZW+>L-;u2ZE({}?`}_K!Z{-Boy}wb6$Q6)vcZtnaQBT;e|h zmOilL7ki2Pz^7|$d`3Sj5;i1)5|aOJ8wF80@8P}mv$|JZx`=s2tEUU+1W z@fZXQMi3`BVT_d^5{)fDB?62sumF=fpa#)6Hzw$7YzgaQq8Wjm>o$UGsMp1`PU58Q zecdkqq2A@SN?WhgEWEPZR3%QY?^{ZwXO!Ty+}5{oQ#Ub<)3~V{1ap7Cz4tlK^Nb`u z+T8d3qocJRJ@cHi&pvzav(G;Jd}B!q;5h~i8fBj&`c=WYbBfpGxpuqR|2xmB9PdQD zv;jsZb}F>69^FNIPdP&dXcH)Fp-=c%xlq2ooR|)#o_EqodvMo2O@G&ZNiY1MjAhR! z*HZ~UkdgS2JbxH)Tr=g$9r$j%#dQ0wX^$%)KDK>&RX#H6~sd%94Dlh2g`VO}12*+5&T_;g4y%yJ?yh0m(W5Q0+eB}{$K?6LBo6HJv{dwl>n`CMpNGw!ArU&|4TEOdxl2_ zyNaMAoZIko8Z18tx4tjev#l^XI2W9Q*_a#JTI`z}mz9f*#Ts9{TQs;kt+Fj$058)K zUgj(_(dm^HWfriWsvqZlFV5IV8+!LC#qWGV=hJb_+mHJcRX^PNd%%QeCa_#lUhGr+ za7^ntaZDXW_&CDb7m@?+2ZgvYlrV?ReI^m;*mAv zcPkuP_+Evy`gaf81k|Or-H;n-i|t=Vn%OwuKwz>Q8>=Nlwqf_Oz8DU!SJ%9{8*aY= ziuMHJJCz>VwyR$P4`)bocf)OrXZkUXXFZm_rr4?O23vhDYh&&`=y2ejG&tBQTc?hT z`g8|_-SHRPd)#nBwyyu`VbR3mHb7}B?>!&EJMa4DZd`21mFC)xZcu*J=Dedd-rWtd zD%qu1o2TjL%X8fNAxFct$6jY3JK62v@a>(SS2w!g+`658Zam^iuO;Y zpLo|)a9^#Zk$K=y&+coL@Zc_%7j%(x%{mVjfuaA)@eH)bVIAw13z#`}cHd6L0UPAk z_l9j3(q+Q(qt0A!I(6RxONaW-{N38%w@Q1ROKa^NbUvOA@^~FKm-)zzpQ_In& zqFxGL_)U3K-a{q%)#JQ|L_EH9!Pj5y)3yn3cHJ%LA%92SA|I=df3pj?dyOyT@AI+v z%6urlHG^Fv+o+$&;}@;IFrC069bwkm)l;IIgd0=Yyb3y*A7T7G+l)UH(#n4P-!>|Y zUdC;Cov486P#C$-n>fzKf=V$^`MQiMjrqBw8qOtOUBQ}My2y_EiI2H zll=`$Z*}{7C|A-CpIoB?D;Vn4#hT$OHl{NGHUf!n#i1M}ZU=Ug!cvI2u_GUvPTjMBw+h&~A0Ij#$2ot8B8 z;RaCts2k#sWB?Dmyf=jFGi0ydB;KGMcv~Ibj}y+uas?;9lji3DKk>;}4fA6haDKlJ zVW?*^_Vp_9=~>h0_DJ9K99wKd#xh`Q+d0mzH%60+>{VgNzcWZYjb zID@ro<{3+BQ{qRAy`bh>Yh@jIIQnDg=oKSj)K zLq|lev;#=5v~o?6Nz{|PPyB)VXH;&I?>@`7!RH&S123czd<5$T^fz%jyDPR;+N%B` zOz2_2BrKS(pq@N%Sj+l5yRBT1hxg;4f8aH^BW?K|K|XaR14j$WN6P9vVBLc>^U=j} zyTj7>azoyfc}OeA;Y@q1|J<%^&lKo%^aKxp9`03>fdp)`VZCoZs{F8weE$pO$Zx4v zEHjoR zLqF~SpCYe)R+gKrZe6yL!%DeAAuB^-oTnijFw91}e`&+diEiK)n{dfWj!PQRbI=T$Smsfhy<8FJ%TGIq&(a6}1W2T9 zik0CwzIUm;WO+>0;~VLn-Hh6ZIp;N|b&i95HCOi31LBl2haR}ThUi1P!U-(Wif&Lp z_#kiUi_CNI8^47omJw)EeKEc4-hnIJ_efp-#JKY1&io-R$)Ea1JZ<=P`ke3E;l@QD z+$rgTU)pH6Tk(y)DrM^FGQLQ!(9C`^#;9UxG7)&kM-=`P^SAZeR-YbJpNRKy1RtM- zeeJ^={Np=>jg(zSQ^+IESaxZ24*EFSsC}S;vcqqmS42h~oj(1%X>X+kBV+~TmU@A z4IhHOU_WOJ;0Ci*4-qHySL#RR{^87ncRloA27{{w4E<+@hcdWqr;r)!!pQtk&i01s zSV@DkdeYB(s8{hwdF}#@kp2C&74{c8OnV}_G>tdPX7aR_6Was$JYtLK`$(I)N6UnE zoo&^RP^PSBNI!qu4EZ1rf%i5Wc@n44hV-ti1s8Ry*~*%F2D;g%3p{@-U6Lo;3DO9@ zAWz3W{5;r!hn`dZVe`YW%lT|9Xb?LI`8;&k+rZ%d2I57Rbs$&F`9?+eW&g6`*EW5h zrH4$&gX33ZYqYa&#?V-Kf3jBSjN5w9@yWEOL&}8dbq)$c4>soT$qr51(QEpW5yGI3YPyl!*8c)H$ncXB0?%>^I%c$-ot}$+E(EMkhdcIi z1L6mN@?1qeizE(#E)U~{4yDnysR7X#EByKfH!h;(xM{QEdQV@Z93h{z&<)BNYb2!X zG5ueZJI_J)rs5oFT14N1RVP%ZEWPO0+A*yM@Y`igt?09c8UFY@$_gvHO@|~O^AB*o z0BDpxsMCi=(1-lsD>!=DPB0Jmt!p>v&%uTWEcAzcW5OtJ9FtOcu?_x|`Z7|N*Fm1N zt1KUp57M%}&`)4}@xDVF@`KHta~`%%<+ye^;*U}1VEa)&fk*2h@JRavePkHNo;Gy~ zpTQl_Gs*9S)Mwf9@+lE16I zI<3wtnbHRQr-LDlEDw=a!stZ#c{-pST{O3GN<9JL`rm-io0PNehG8{#%8EDt@m0IeOStuVu5_2bjVE8B}Z zP8g5K|0lh=Zq012*f+`$ZH#LZJUsJVgK(!m)H%XRzZ7_*_4uAP$c15JR!&Ik!%is= zOfP-}%3W}SE~kV1_Bsf83xBY?a8ZVLc;;Y4Q4cpasjR(jfiK~6FXm35tLOLVH)*HN z$hVze`>@)4sY?siApbK0E8ncge!WE+;nSl3u0LVt+}iqhD}0VEKDqg`d>b-yme=WP@@y>Tgo1L#AV&9;ndY4hErt|d;@J| z+wM-5GyL7pclx-I+yIqU(|2ke5}hSYwDXmMb z2b>)xP6?NKP5E%GuIL`}Ck`2Fp$s5vmZ9jO{tg~S`KUhBM~Ra*r1PxEAn4p}_}~w9 zp0o78?fAwXivcXHaEwXXE)$uV(_22Yq z?S%G`*iZdoFK`~k0~yBpOppWnfK$g+w!w_TNZ*O&F^z{~CIu9)ofp+jU8;edqpb<^ z65A@UTsviby6&A3*!<-GH`%nb`UbXiaujQIRxuD4=tYv*cPfB5L zM?P*nKCGgQr}S98X1~jiAK9zxSCwpiavg z!AM$<|2W`^kT>Eef4-TI(>=R(>5gTr0 zt75vz#tC|?PSZE;=>V?uU{^kiLq@DTdpD^JyTLQ@iQZmBnEoND_s9#jf_DTy)<04l z?HW*9(d%s9xTRTa$_rc3ek@X_n{7MPD_j+fmU8*a5%Bwbkfc+0!+IEAKN=6c|$|4rjKE4#=+Uo^6T9ng@={ zZx#Db8}0GS-25cIJ5e5}-|(NpxF!jcZ-&#agx+IClaXxgVs3w&X7TAjzu3oF^oGeZ zTX2|01}pc9JGjeyUV5D@IIamj_w@MiG{Pf1uq{+tAHh7()@%XS8u#?++OsViK`Yv^ z*Bi)71DuL&vX5oU1qmleT)R+9_$5wQcDky!aAZ>xF09SkBi3TSYdHhw-hE z_;W6PAdK&f#Lu|+hCCK52t8ZJ5WlEgx)TI)iHD8R_<=C~TqM55#gBB`UCTkA$pYtqxcirbz9vhP9vSd(Z9c4fg^fM*X`Kf> zBai#A0S`Ce!^V8rcEN{vz`lJN7MoLU1aBD}^|A_c<*-Yx?@>9=EOwCP5`kqsr~E+6 zcFC7{Xd4ipw|04~eiwNdY?#MzcRpL}#{wVmvHNSmi`WdDiHat1uz-DLR)_kFh+Bv! z!8ss_(KLGz1bfiGUG0J2Q<@ILgwJL8-Uq&l+Fn2ptWqn&n z&M?j?D|j)rVLU(DXE?Dhvw-Eha1!JME=jk`#o?;h;gKCU9RYr=)G+7Dr4V?QqB#ZcBd2$AvA5+7`tA#ccQq}ar@QjZ#Muv@a`khbn)d$DGx z&cn%lN!wU{oV$e`qI|hN5%R5sy@@!tH5)AK-&?>o3v8#timhi`G$(G`t8?RS;bq&N zxoJ_FF#+BLnLxL6T^v~M^5VX5I$_GoKvBZ!4~pJRLD$7!YPYry zW1BL!N52sHQ9f*&qdqv>dOqk&1)mv5KW+m044oz}`14aG;PQRa3}8`DxaWZ5OB^$x zf8%|d6Z-A&NnZeX(C?T20rPPE1B}fMc4cKgUg|5|73zq{Zi$bhoXnr_{b+Qf0SsLk zqVq2419@^&QyKtjb!N zj>`$Iqri=@1t`bT1>}WqmVvKz=KJ+4~}N^IOw; zJ{Zpa@QoevNPc27S?-9VOe|jXPvX)(jOiFIWj~o-Tj<9=i(;SZ?hwn)t^H3hF21*8 zcta#U#ds?5BXnM+P7L=&;srOiemug#e~m{r9{)0?r*Hg-pEnpj6p7Dx{G9R9^d}F}8dObcP;3^5y+QQMiXjlit&Fg9poFl+~$ELsUugtKR8Y+NVb5rJuQ zFzgoVUYh*&Nc;xIhx=CY`y+813;jb|hGiQB+o1dr#IX*wW7#c^gP{~DMAo7FB*IZY zM9+5N-B%8V(dJ=nFTOWe%Ut{O&D2ke;Go5*-?9`%ZTupjq@u5(?IjQ^ls z`!x~BS8Ofl%%7{U6_K?(MmyTghTL{cp(uSfh>m^sxj22FJ*<4^As^5nV+X!JJ7%=% ze3Qs$7Ok{X%-fwET4!>Ee5dO1G$VdoeHP*0!Otjv(Q`5ckDVbZ^}4}uJ!gG=$S^t{ zB=B~wXvlMfW%FG|HY(3-i$>ZbZ4qgi_BPi1^oGZ=_lhUWnAo(w6k2D?P0Dc|+03Hz zUzK0#gp+lpesJBGPZ!aFkheH#tS<%Udx5vY?ziI&tAH^K=-X$j=S6MngPV^-9c z7y1KS(>DdWr0qYWVMwQ8sH37^8Q@Gpe;Wax#~bY;+e0U7@fldR&ms)?<{dk!M|>R~ z)_tTu+a~a`cEbA`q=oUP@dk~er%1zZ8NadeWuBBl0XWQe4}BW=JnYwVZjg0O;`iY_ zZZZIkp8lie^MGg9rm{~Yb;2Cq@opRf z<%nx^w`KMIN|xhGz8r;@xyEaye(E@emc>rxm-DiIz6WxaazdQRZ0b9ZEBitWA0ANK z&#_*FdtXlC_Jnpp_KN5)i8mTWJiYz`Q2I9Kp7bn>%oLd`Zkwom1#ZGw(nlYi>Yd}G(1PRJ-KWBPoc zFUb4+?5;uYs*V&`mmh!CmmPI47~Bgykd4A;+FS#8^0MD({$MlS$AKU3XntZ-=9sIaU+Z`& zPMYp6LtZRj;3p1-i=IM0gmv}kP0;A|BBijDBga`hT@3>BPUNa|YzF)^o^*xEnt$>+ z_1{Fdf6|LQ+cjV6I`xEW5~4Ev&Tflu4t0m|`AzaI^y6FX+vk9bV-vpq5jW_8Ee&=M zrYLaI4pK*~&Wmr(xkUM{Ra-A@3x11D#(CU7Mj1*!19Tcr@F>%Yey;XcphEPAC_nhi z!LKAE?NCbb5{K{ve$b{UUASj9*MsA#Z7qULI?H*)zvAqB8`8DTftNI%4slbS#6PRe zNk`^O=_7(RX=9@D@pI2OsKMH_Uu{yJ{20fARXhhh-hTsKph0vbqEF#kYsh_8eOH-^ z&WKF%bMO|*%-aPkOG$@5kvOC%09mj3tA~YFg{@Z^{OXaoAMNIh>03qHLCS*X6x&)0 zX`ep7*?5n%o$y_>%{xGNS)YLu5OO?JBG+p={`u!Loz&U2_j3(L2$R9u)8g<*KUNoH zhDW$6HTwWuh|E3xBZH6Q>fRs3jafa@xaKUE_-6ZcPySe6G#z#;Vu;ht`;MtkA85KQBk0O!b?+lyg&phY zbBrB~rq2)CT8o@JcVp~$G#)o^jE)Q$66v>&#pvdkr0mg)ot^MRGKxcBc(Q_&%M%UJ zxb1^D3WpP8KL01$qVYO5Zlk!8{}a84m$C6JCiJXDtN-xCE*FpWuss+zA0Ekac_;g5 zPmD9}uYdpRKf?GprYt#*{KN#pFjt^5Tgx#Z7O&De9gWkqKV5@5FY&~=XdHCU*4aF% z{)^%39q9*k%;(AaDE!uu;n94SWzN3XlWU@JpxgFSJ;`x0Pn(3h30{%Kllf?T111Ky zU@}wpqdp07==i7$=NWdzKDSozN$RX_@Xf^HnM9)UowxABf9QA3nDBq0=2YxGv+z`6G3B=SRAL?8D2Lha z8HP_J)?MIxEb;kMxNzdlL}DG1%6kHD2>u=ZQ9lNTw+?4-Y2JQIv)7-qNWKuddjb8? z+)A16dWyO)I@JSTcW`K>tzUR5qH?+=d@l)+CekC)J zb84TW&PyIEH4~c;d1{vnkJ&vKUDfBO4!C&D1J`*|+oc{ob*v&zYRVq5(@&jragSqP z-Y~bv=UHc;I$sga#Sg{V^idbz4O2WvyqAF_g2Bd0 zemghl=?h33n`^DGGJNYi;j;dtB98E!N2&Z{_#ajTGVB`_ zVbM>!lS{;PK`m&%Gw-xneaYmi2`+KIz z!V@&>;@)cb1{dzuHuU$=Ha^qo!r5o*?jJ6|4eHGen`M>0)-&7Zfe{P4Pwe0`z76Od z>3Td%gNj+1oSYl>%mmV7jgy{f#qDNbMp`3n2cCH~8ebgVx^(~>H%CX{UrHPD%uF;s zH-fGFvGjYEN8`I?5I2f{&zfkww3Chi!M|s7JRXZla7?`8pdfRSdv-=)Z0|)S%)uxO z7P}LVw64nMp5xJY!x4=?9gC+N=^RK~^=vX4x1~FqgL`j2z_S^R`|tbM-!XmQ_D*XT zpC$kBb)VU&V<jWaD@lw2yluzcE(8an@(h$DcZCup1)rosr+i?00%CGZ7!?*$JhK zI63~td@ybuJPo{4pz~>@5xjD)EBA{G3{b~^y=jAhbX(?eZduRZ-1f}ckxVyR{X(X9IMbi$Y0X&aWOyz{W@K&VcF2MED@odA zw`HV;uw)~@VleN$4JVq(?N)sI+5$viL~+S^ts_{9^qBVje%;E<^BAtZo}14IPlAhE zI=H6Ebmli&D`b{(0B?WCDdxkvZ^VrEP^5d${_$_XU8IhP9B%sf@X&Bi9~|Hu)?K1I zt-Wd7*_O}aazfl7o#ArQ2nvp`__aNnN8Hv(9;`#q-QPHavJ^U^5>$V6FKuBACT4S_ zzV98Vgo)Z6!=wB}@`~CXgE>-(CuV;%FZzHnddzl&_C@UQ-kD0cS$0U^zF_(u${=cs zl&&vqoClZKBZJ9T!W0J3He^ThYA3z`(;uhXYqr?cFU-NaR7%saFHFbNw`3u|BFAwI z|9Qmwc4JF-OP052j^ujGPJOYiGG1j%2#!g7vAHsy%-!VIx_q%i^M4w30d^yJ>Vn#l zXHoZQPoA34@TXy8EX?c+X+Z;p#n*z(`cz*m8a~)GrvD=C66R>0Hhgp77|!p|7DDJf zuIXJm_kQZ&Y#f|3=Gr6VBXe~+wk&jE%4bB88eLyX8eKE6Yk3_nMcTmg9pZ=m11!$V zV*D3d@RspE;+qpqZv<~)V1K5Ui>~PVbR}uJ_G6_#?n>S*5GaVzD1pIXHbN&T0d;QNa~*M z@2_YNzS8z{<5%mu2ZbJ2`BZiKruPJU7@LKEwP z=zhG7-9Iig$-2=9pP_AstrFkZ+2jeMnflxowSSN?9cF8rf2&3Dik$ z<71v*f^CqzPifx#{bj)Ly$5gRD`C4VtThb#3iube&M-{vAkvP7VPAp13EX%X_M0sh zb|MVh54r{JOeE}(hCQ1K!+r~LCfu_oU;f^=-_j-&PGogue_{BR=9_NK-gs-v%{tOq zNa&t7X*cRzSwVkrCY`h&Pj~@e z!A8(Rnn0()04M?jngpgb4nx_v^ZG=t7s>c9E|e_?HJ!c8t)W(7^YXwHtm-1b8J)vK|0=a*u=P1mq;;k2uOo;!j(73SC)@u@kfU{l9%!amyNr z?Ihty{A0&cjxgg!ls}Oi9WOfb`1CtCc1#~^lNx7E(ez_Ga53$QwO*V;BkMl z=nP=lu8Vx}mblwNcUwp|^YAd_W4avPPeOjFYsi0!bZ*9TT;D<)bdI#5z6Za31h8q7 zBa6Z8?C+S*LO85@79OdSo`*Y;ZahL$htdE!2EPM({JLrUrfg&9tnr&RxgInDCux#( zqo9jnv_s_mBGe(_{jQ3NZ@|AsXqR>;h9BXc ze*6~t+uk*Pm5=5Tmrn%$qE`+7-4*yZ;d@$S5ig5(As?qFuAO6fEE=%Q~2 z^L9Ie$Mdty@U;Hh@azFEq^D-m@U;IM@tiO`yA97^1s?KC`Od|cE9b=#Ucr~F(W1>o z{ctuBXV8S>6p`>6giD#^EAk~jbCqim^s?X65W$BqwrR|d>(|64;5WnQqe{I!Wchry zA|JrbmybW!o_U=%yzeA0CznD+UYWV`+LJIElNyh988cObGNt0%WzM~0OcwsSwBfFu ztzlaZnnZ?>rPsOUS@lf&3zuVreq;*{oWSWv8Mt7`+wD(<90XTPcXtA}!~f|Be)MO> z|2d8@@Flk2X!B*>G!G3I@$UPGlubr$Ad7Y0X%lrt~>VB<`I5R=QqSY`E|>nUFvG> z&?t_1vHiDdW27FSZ^|{|vbMY3^X$e;bggl_j!WiV%& zW7JR7`Qw#z!gg)#AL6{^b2eV-XbbtlDsA5X$Az?Zf7)}We7Oic&$R$Ac=UUhKo|SO ze(ibutktQhzKVUed6PTRxps`}+}Ri4-rtDqq@T`yEM>vB+Y?dVj{Hs=9^EI$EzPpF zt=TO(TtTCIqW=(SXb*Uo8T}26Q;6(@z6HD#O6#mU*T&oT zWyZtBSVljX?;ptB;9)6$-na5vt>BxYj?lND{~~x7Wj-C{m3&Hg4X(Df{J=NqRZ49= z%O7?k8e>bWi?O?+lI7qJdo@qyadHxQ9|-Svp&oO8vB(}YmCmqU0#{PjZ}@xh{JAm* z%TuqOP+P(E9)9mj17w76#!yqL5&W_ZgdR=>pqKi{yXnMs z0#@{M!oI1i&;!O1hr~7Nx0~B+P0M%i2hWM$@6T!>?{hse{?)Fy4kN8U3#BQ-|MDpR z+k3EZ4SV)7Jzb+%ExbZ?hx#BoQma)!uW8gZFH){37s;zHQeGP)c{D`wz`l_o3K$a` zJ=-$Q5pT@&_HWq&IZ;;~E>|CzKjq7P&)gHkxx7EDhb(u3x3oWdMk}bbi-%9Bz6#w} zc^E6#$A?EM&ZWUwPnc^Sc??%x2$LL7{i@k3!BOYpqbNf8;4bd#A8v#pLb^Xf)~weH z*4TQb^bjLuz0@D+9FD|s3{&g`%7eNiYnwQBc@E#y8@u}ut1r;bs#RpW^+z4RZ)eAC zJBx4B!GG&>-$9&RbrR5UzD#>-NN=~K(N z>ZGn&J97d$K7a>xJwQ3hy@}wTOp@cCQjZXi_EFk5+6&qb z-re*X!q_(Q97LWy#j$Jp`m_lp#+}6*a^pRe^!;7~KHg*akM$-$#L>5?|LBlvCu+!>uC`~Guy?*;#-@F3ssKtFjN-VZ_J4T@0oIN34D@Tlz%OBdVx3QGS1!$8%8YU+_|S>hFQq>eEy(j`9JG zI72SD4>Y5V2^Inu?r(K4|E~8o@U{)OwgKlh;C{P$JOCMNI}YA=mxF;?g&zcM-F0}z zAitC4p!W#g=ieUJlgRs*4@*CTcUkbv|DXeObd2LJ0HlAi0S{!lsS)4EvlsdNdMDQL zwc*)^J^^TW^a38hKZ^J#5&tOg;H)@V8}ey{KX?>pF&-!fTiP*?i@YlN>)nYv7=W)b z{Qs+O8D~9+K3u)@<2Zg6JXM5zp+D@~VO)y+xL{8!o=!Ytc)%COXfYlteRs^ya$Q>R z)T?+P53H*SxE3nP!?SwTyPJd%wn{&dP%jTS7fVpa}$q6tQ z9boP`z+7{%{}ARD*WlUr@6WH2t|wB^wLZw}jN*QD_s>6%$~>#zaT$LS<=RsVTMOBD z@;N7GQ1EOVp6|^w<>bun&p>Y}yKdA^obeHK?LpZ=C!aWk2k9P#uH4rKyRy9;EI~U( zfAPoo{b;+aCEy)G!6yLQ4tzKlm*++WTLHhd7tby{(|FF~tW2c+0otw~oGMS9I|1E5 z`uWPNuNm-pJR9%;N8e^V`;l$~9?>n z)QkD@o;zH9+^ElmE1W-D&Hso%np*OD|(V^f@Nc2GXbC zZ~9QNwGH(1AfL(8c+z9yH~w*>;$S~El~8!sk6m|8eSf~`D~jI;xJ4MpWM6^2{4w)G z9MfNf^n7DSq{M#}azlLZM<^H26Z|pSV)_=7Y5XoG@Yhh;0l$I1<{#$~1z#5V`hKpX z4e4WH%Lyk zzBkIpeGfc{aNwB61HJqDL401#6W;LVUro7mJ9Pi+r}5@ERkHFPHSV{e4^LqHp}#A0 z9(opx`0rnz(Qxk3@aI|Cx`97Y9kBDPY`l&~`MLNeO?%;BIk@ROWsmPh&?7vTgNYPm zbR5qSJVy~1`=k5-I}i)3_y7#)OW4tl5{od%<4@R^NjX`(J)eJa8t~vhq_JFD(N_-s z^f2fgz_SU@X2~G(PkDG78cX*yGYM(HVCEoyBi$Z6uvHVC_*~-XylZ&4_=yKT&Uom| z!~r}go%Gl>V~KXC}Kmxp%|O`J#ADTK)%t|mZN|FN7qa2B0X zr2W(7c#;mrHa--x-5`W#32FrNsO>D^Px+V^8KXs zE~sh70j*lK3cD+MdUCzFULXqlld5;FoRCi&qUX@ZKi#9@VL9O&e7x9z2X*^J{`UMZ zj(@<1a(wG@Qb?8)M+C5*tked%zL>$2LY&tn{DvHy4!?L3@w@SC$FtMnaBqZxu6LHN zE&n6^<9PPt83UfhPB(}XVH0>zUtTrg3H8vcS*Z2ExNR>NOw_-PyoLrrg(@6IV-`{Ko zEcc7n&H-oh4&Uq)eCYe*?f)=%hFKSkR`&NAKi`BX6H?DTx#E|6^Ens3g5#mW2l@Qw zafG8?|K@S=Aj`T@RBJGV?SXJ ziXsz7up+eM97pE`$D~YKEj$`R%=^0zAAVZqUnjWH-Ha*Bm`O)8$ zaeMSLNSn|57{gJocWSu6gBH(Ay@pGkON19HC^AqP8!tgeuhCVp?%QZlno~^AQ?Fqs z>h$hL%p2L=P!Tz0HR!*goc{;)3gt|@LLMk9u|Kf;?p!?j%#8Q)>j2G+M;LYA^U{d$ z@yK@?>6#HXZL&$u{_W?}c3ZxHB|P)=^EpVzaWmZG#7ZbSeZuOelnG&BQ$-f6t4RMn z*ilEf%2w)cH}+wRY%#{Aw1E%w`&52O!XO;7^Y=j237qs3zlrjfGH!{C-LF`$cZg7n zq3vD08B{#ImDFzdV17EsbQ*k+&pyzW(chfU5c=K&mj`i8(jjE~77dPb+_c?IuDkUbPwN{xJreO~WGmPxHg z)Cd1SHg^#J0fd1rkw>%gB7D3ZPLvPpCeub_@GkV5WJ-Fun-p~MoX6nLP&R6#)i(am zovPQTJIYi4x)*xhSq}c^7-4$*N6a4o`GFXW^attt+y;JqeJ@62k8-DO`T3q@v+SJO zA(Soc2p`%4>LUBS{LQzlgF5c)4Cn!z$s>NYXfy1{OOgCCQC&m2S*j*^%DI;~FL;>k z#-C3JkHI$&YhCs0)izlEH{U;;k%p2pPO#K{U4=|H4&8wZUd(jwP?d&8N1F)ajdCDw zQtruyt;09W(=Id`%zLqY-1bQC0QSf}X%HF8i)B=>GJ0uT%V;wF5d>=4{zb0~Th*O6 zxWCx$!afp)p(&$nn@nfJu!BAffrjsh3+v9i_V+JNxG=a&4$j&n-g}YD7sL+z#W@Gp zH|%J6IT;BvnqOY#!nO`O-d=8WVMXi_R$5+ebzyqe8vVDIJ6zbRZg_>;^21mcV}8xc ztg^!6DiS3$y$nzc1+Cyui?fT1btt<{_Az7ZD|H6(zljqAJ|2qcPa^6?>?7!?~ zAYp5fLj0kZ&pW(*@LO{u12}4pe$=;WI=T7XYfWn!kQ4-`^&j{@qtfiyl+3!2+tth8lBTAC#e&KzTwAZl2z#c zRxjcy#|&tOec^j$gN!Kw7WbeyXp{T5Uh?oV?-Ymc9bn!2){McVSMmNqIj>6Skn*Z8 z^l#BKA|{(19E0GD*qVaL4K5ruuSb2k$xau>b7Xt^Zg8+g7cR5UBF{+_IeGfoQLUGh zALj?fwnG+DZ&%_Xl7U`1x4?WgPX3Bz5lmPMgnqxbWo~W4w<#m32YCSO7N2qQpre6Z zkC2b4vbuRr2?Ii%c)dJl-b``fOg%gxnI(d!J!nH8!~+>Gm1~-$ok~yqy9k8IA^Us9A%fG z-c-=qDYPOEqPs3)KcPp&L0uAk-36Q5tiFxQgWuux7~*qMkwS0r-^i*$H~v5f+8v_q5fY zB^(dJ4bDX}VEGnj(<^$B%-oRyUh)h7R@OmD9?O7_$&(fNbVT!ctZSrSdd%-OA1%YY zl_7a&UOWpU%2RJ7uZwVYQf>rL^YcR)3Qr5+tQnju=gP7otdwJulUaVGe^8nb&kOaS zG96_sWtBz|n$PiVZLhfYC*WE&&LPk>?Xoi+Uol?*?Xd=%ZGI(>th7zF2yKsE!Arx3 z9vdAJMAGiOa?pj>`w03fuN-yZ$k%9kWm3b?pO=0F&mhLx_q}Z2UpXyt!7I>Fp_OZ5 zaa|X2okSG#Mw@Hr8*BfL$3jcI>Z^_^6eE2MC&#O|bivH@W;iU0rZl4$4g2$iP*=-k zTc_;W0`@Kcs#f~5mggwW^J1?uykq#}8l!8s_M1*ZXT`7LThoIVvDb1CZ`W46L+PaY zh{4HLUDtrQMZocHviLogC$-%RxVGUW>iT4Q9nMtaG=jm|`m^uWsqEg<2bd`T4W>u% zxiXqEqn#?#*C^jS2MhxD^Gpu6@fgk{v7Z}(v%6EitG=*bJ1shYNdykM@5Tar|2qYG zGv(mB2gn=ox?7TvPs=nYytZ+IOcTl)H@$$aw+ZJ&y0~8isJL zOOZam^wl@xcfF7E>kQqPfY4IY3dUjkE>J%BiM(Lz<=!vm!|@9C)o<)j`dBtMzN)_& z{wdH(d=VNU-~PBP%tt2=v~N*Z(u{E&krCH}N}kFO=ko>k-+@ON-$J<9N6_W`D)jrM z{fhP(gdWI}b4|qO+NAO^q(`{K?~Byk=6Jb)PNB({O|7g;ReQ;D0$$=4e|${JM&K*U z!qGopSvXoq&n)>v#?kVjKD#ntnMgTw2tTq$f^|{oX>#dqlX8u~Nm)j}#r`LipV?)| zI%0Jn?_%FxsV9HsY<5(BkO^&(*dyq>+tc9dUY*Ka`B^P=$=C|Y#-;Q8x1xNGT6t5i zBKXLol;^ndiZP$RWPLE%MDe>YU-pN!+&L~2)nT;-v9UB+d;h=87RB~^RPaf?{kijs z@(WH1-*H|cE0G1>jQ6r_Cm$ z^sT}0S>J}VSN2(Gqlt_1V7o#3nTPaw5yth_o=?bwG?PDHw=$ZRy!rha&=;})7YsMb zs;SfX2YyfY0fkfD`p+mBrH}lvO@3Q?dI3k-fF{x1wA0>!NiW@w{Ij zZFLd#gj+QV>%=$vp7j@`Y!+s-8RMODrSJ}WeU3L>InM( z!S{~om=ERd^u{^-V`*hO*j_WHP7(+Yiy*U07Zv?nZQ%Kp@= zCc~tDf3GF}&AyPxSmT8b!Bw{gtGOcAqj1t6{;lW_Z4BI1j%S}9H=1@SpDbs}MIQWi zdNGBvmU7Pk9(nPbdLwlle1CrQ7VOsN*vFQ$N1H9eEQ0Y*C zE3GipkX;E!2|LFN=$JrGf7b#W&}orvjrcoUzpD8lKMB9Aa*ZBnt&8lXtX16*dx~(@ zEAqpKI#YS}j?~vUO+wLEK+pF(g`VJ-Aa}}8%7f(^gU6Xrg?&Q*_s=Q(_nBV?zmt+i z(nr3hrXWAonNJ|TGMwc&ize&m{XKC)KBhat-`C^!W_?`S716Drf{wX$>10^8o)(n7 z`z`v`pykf;r~UO~>QTMpS{~pc9seHvqrS%Uj`Bdh?^^FbGwp@giag$}kR5bG^pLc& zj3V{}b}q~A=?%8d=I_T;){{p}@AQ7v_+DJBiGXfMIihb7{M{PhM4q%M=x<8jOmy}l zy^kJkMLa!ubNH8ZJ%#}g4vHb;3b^_6(K-=XBhlGx_2rnOdvI_x0UfbA;$?dvRyU*{ z#y+wy+nw)_woE7kSHCJ`5%O3YtvD4tq8<8s=#I(H&G!eV4yuTek69_@ubqrCY@-xuY8_2FTggK)7|%&Q2w)T_NFtlFInlL(*2Lp-~*yxH$XSa9ZCq}_rXSw{&g`KB>mh+X~|M?2N*`N_R9OP-WM(! zO_h7ctt^FK=rr+)y=yY~1sTjV2@hPKFT5ykSjuF)2`#5Vm&^-h3!~j6N$>yuVTaHZ z{BRR|Xut=k*V2B29^ZB^M_SmhA41n>`PtxAWTUt-6&S&NMsZWuNQ2U)wjN`R;_DM9 z$487d(j~Gw+xC`er`+0y*?6En-L_$S9Rv>Bes&DF7UD^09Ej9BUP2K4we;GaEvsk| zFsMp+Iu>pnUxIOVCcn*(o}G!rrx@SUm+RSvkvGlnwdIj`!OeT~5N+vSW53zquamrU z+wI)`*EUDumSK#3OaE5P3ffKPI==AQcEFIIeB_U z9kVjt7b)Y7T0ca`Gr*CAT#lE6*Ps*Fj~KUQDa1ja=%3Qoyav4&y#ZcX4>qpxtd|() z!mX#u21m}hj`Fo>;HTW7Qx5+%YjjWeHDHl(#21NI-AS#s18F)~aFPIrz~9;*&Z^b4^tBuB73h(^YJzp>|A^;Q(Y51PKr!t>&$#SE{~)Q>G0`QKRl=XTg?XjMBDV3o%pA7 zv-RUAlk@3EJr)P%h90vqo%7me&bH&5kiH!c?GbFuYp7eUE%1JKgVo{7?vuIOUM@x) z&CNN_Nj%a z%LPb!PR>tT6_2!inMk{g@)Mq*W7006zSB=3Eh5LHjYUu<4yEbHPeCH>oAV)K?Q?B8 z_~{%{?)$kIr$7Iej6#2Wn3hE2~^i|1gZl8-fj`kS&&GwTOUmG-vhsUpAObuvUP3OJw!8Hs=M}?|-2WNc#;O&X?zyt@r0QYQ7wkWnG_wjw^drS{sxYxh5%#_CohM{L3+=#f=X#KR=FH zhdeijI9aE~r));pIO2|Lyh|(eAwB73-7D?0c;KL2MjwB$YnwKv{afKb*!`{-OkUm> z+74LaLA~|!m&=q6S06}+ua8CiZUP@k$mvz2Yjb^>I}oVfHfQMPADvu@kMwg7lk_pI z%v4NPXf_UUL2Z|e?~(V)xOKECF+25)aFM8sW5A zJ1hM53D|>H#6iBK8$PC%58!eB)@X59wz8}P(>${~_*os^Nxac_{0VUX(>X?jaP-^$ zm3|xgI{&y6a2d7%vRA`7H8Ljsv(1JFzAXH=0Dc`{qi|Z6e#UZixcV3`Ft(=zaZ8Zy z5Z?G(fH8EBOJI3fk*@Ro***X`JV$!_yS8QvkN0E2DLF&^|Jhl*aqigb&5kdG#o&w{ z)@_I9g7X6epDyb_bm9`=S%Z3llj_-ydzvmp;LI+)UJnq%_xc8jkMZ{UuGx4ZaMGuJ zJtFVd_eRfb0&ej3`U$+j+e|v-Ee@x=z0P*e(K79LlPW&agD7k+4`!C}M_ksgPaBN2 zpECq7JTsZ$++emFdrER6qC}9*%qGN>o`WGhF*uXWOr-2*dL2C>K4wFmoSCEiXL>_8 z>f{Xlen-<0#_{quRcq?djFtZzO%a+xIFa*Ar0i#W**jdo5B+VZtlmOA%KwcMc!Rb# zQT|RB-`Ev}Gkk|5_@=kzc61MSjc^lf3?X1H1up6Vep^_;6EMe{n~a9Y1{I4>Bxo4s`{6mjHd5pu9W=D>5u^0KDmLc_81@&oe|Z-o;zm z1Q%&LgkO*y#=Sjlm9V6(yg!2LX!b@No@(C4U{{aZ8c(EG0+xI%Dwh)Np&pix&H-<2 zS=q+WVE0RjTBnbNZTkxv8nCn9uRXw{2P8;HZpV1{|%1Yfng5O)?IPaw*j`0?zoXX(wI9;*PF}zJRV8 z+OS%@r+u9(0oK6@T{Sx+cn;ipOH)&0OXE=Ejg47>>)$#wJOXd27f=c4bgAYP-r#jX zV}!l~aaf`6IN%(8=RAEQ#X`0hgErU#f%r7=P~D+33!rPR9BR%9z6dNB0nZDThd39s zy0QgqcbxA9y8#Ej7rYemy`a^@3EvCy5nTH!`0mcm;(NhKz=Q9F^&y=L_6bf8OTHHz zi{L$De0OIfydWNYFKoaYd@qDA{1M({;omG6ahAlNrqCQe7*$Ft5LGn<@Uvjz7uW&|Jazg~)0JhC- z6Bup`Mg;y)H`j z6DngavdmOopTRCg3&uvqt-~WagL%E|Wg0{^&M*!7B3zH)=UQ%Vc&L!eJaGTUPhrz8 zu66AnDHJoZejSI;cj2HSL~R^yR8!Ehcn{cXBswgPca5JGaQ^9#STpnHPDZ3Oc}Il+_2C6_;H?= zXK-9-*Var!8~$VOTCu+;GXh@&R1|IqVbK>9SrgXQi1>#f;JTBzve1u={@rcdNEX~O%_vP;$cz@a z#r5EkNev{Pdp?H5f)XupFonNJ(>xQJZI8(Djs3qpOshO?n!VeP}2Pdm-mkoZ}@ zLjMw9#xXi1ZoTAXjLEA_odhdF8PY0b91Xp^RzX}?40h?pkHRdSY80HTyf@y~B{=UQ zLtAhn5DFKe9B9#f;+d{cE<(?TByC^sZ8tV<=FiQK4MGD)KL;fkF5a;kQeNHH+XY`H zOgEXPDrj9?46{oeaN|8;Ym)l!*9b@*cl@~e&G`|!;~48JrKJ4=UkQZu z4{^^>X$fs0ZB#;hdkvewCvDa)oj>zq39ZK8rT4MFV49i%u*#5m3T>CjR?02BU3*nT zzT{Ws!*gAPS2<(Aq=<}nyX~x)P>9RD&F88n=pf7w*@p0T*&PU1Ux6Eskr`%wF(RjK{+jU=NV#gZ+e?i7w z0<+57HC_pMOW@rBZ5-j=%f2OjDW0QKQ7ClByluNZUj zNY67R`mrzXbGZ?$FDzZ&s4~19<1TCJ?Y!@_gD1X70zGfCba}g_!8lZy#@Q~69rHXd zqw#VyZ{D{_t(FaexdQCGvn$)hE@;_YI7$z6 zjC#j)7JlAg0x|_X(3{zKjw`(A!8^d?>bk&dJ5ag|^m7l^Pmw2d&grqz)-s1qLO-d$ zwEc^br|2oe1}qF^uDUxWeS*smN6IUdoz+>yC7@5ecCMU^PwtU)S9Br@{jMv{0dLyH z>zFG06Ibll_@$5;e1y?_9%m2W3_~4sReinUC}0-iNsKAZSMd{egJsUg&Lk~eaaO{E zkDj(NzBnTpW^hRW=nGnwS@>0-%nhKsB8OtJX_jLSA46il2aEF};oL|iU8ciJX_rVx zlgO-e#VIE~xj&E&~Lw)Cb{wy7O z4zxvaFY)(33%{I;^ye#Ke#OsSwM#z18_)y88N31Au(`_86$i*O`sI+BUzZ73nU8A? z->g$?w;us+H$P=zes0dqf1*AIGyB5)W*|?W-?7l=()$mj10#LdrKUr58RW}#K0NJK zqE!d4oY8U;c{Tb-cl=H0xI#~geb{FW`%y$UXfWcJS)SsT)^Y3yWm*T`ksprD>leSa zZb0*4n9P@%y{|J{$MDaN8tj1(b_A!h;*vMvrOxUZVMj*QH?BM3VDnvt0{g?-r?0bm zz9#E(4AnTQjU)kN&i^&7nWF7Y`T~+6B*R%I0{9V;zVY{rpT;72w_5!s} zm;vAPd%W)x?yCsbxoxtvOW1RiaYZ17E=2VWc84}n$~a^2b#S$PlDNMuaLXM{gyS5y z(iHXYC(~pFLP!0(YNPTJ=|7dOf^I8JY;9@j>SY2u|GLr!q(faoJEAy><g+mL#<%+&+<<|v=gCZ?rNB{Yiufi9i9$2w7-2Vix4S*y@wz9b{crDbhq z%YcLVGu>1@+9A9}zKePWaBVyiMqnWm^6lVcJt~j?-Jv?PY)tcE|Bq`5*l!p6iZE=uY?$*|}c$t=?(!a#V zl84ZDTNbuatnWDE-Pv*Ftr@xl8ihBHLT&YX`;Dji0V<|GeP+Ep^2*Ys3#x|69G%Vud z$HJ3EY(O$t=nVa*0?gf(A=QQVo={myn~S?KxsQtNw|rl#-`pGY{XYEO9*KkOU6@~2 zYs0tn$=>JM4af-kqkFUT+_mAu#KISwpHnfrA1k5+5t^FId@vI$)p2$PEWWrJu67-@>)ql zerM+i`pJ7N&qAx6o*@% zeaRthqe*w-Htid5{dFuHJ`Vc~Y}aDp84G7x+#?H^()&<;v^iX(0~=MkX4>{P6K1u- zOI&M3+`|^fxmbtd-GTSC(o34dGtA4SCiLCcU<~U67{mC$aXhS- zu|I?rV*zt~luOs98EmbmO%Jhc_yA=8ftL{F&zh%Aqd!1+{Gku>ir5FSTjIlg;DUxv zrrSSpcl!emYX4tt?E905L*MqE1|K19;I)km$CG&QQxA6B{c#yGur~MFHchi~qV2vr z@0A!$)2ZQiZ*uuW#s$pBymmWaP?xWbjSEQL5nyd!u2sLdbS?a1k&)TO)FiC@(=NbV3-A#Ir~gTXbrC4BlOPhuh&6ePO9JP`}VH_cEcQf z`}tsd-!W|P#u4at6KG1IZ?sYFv6FtLZwG6!{|EYU`4RLn4}gxH?;Q8fQ0E-(pY0di z!OC6KZOk5F^J_NWCuN-3fc&vXXGI_0m}{R%;Vpj%o5XVz&oLL~-UjbJ=X{q-S=f>K z4xL2H@=`mj{880M`crr&kTA`?PDJ3QF0)%khvXnl^m7=FKhfKDc7N<{@i|w_Ae`fi z#fWc&x-D&d?fnmJ+!()amg9D~Lk9e>vxAxy&vkWk;Hi({F?eThVK3ZyP>>~el=S3x zh%B$$thmIE0tacM&E))ywB6&-t#&IH=#Th!N7Y`sb(wA*>10~>Zf5(noUS_r+%isr zeU%7gyS9>YiHx1B)IN^NATplM{)fnAq3p>NADC-Ry`C+L?a72a1@7fK-XZBC`$*|O zzinFBYK$)hJo5_YDwb|s1AL8zt}QukR9CsLY*1MV{+c!TF!`dblC{8)o!E8Y@N0By z6-In@%I~z}>#jT7EWP4e8{$iw+GKOvF^XDPXYJ|AY0aPg6Uv@``Cnr!O zE2%$duekn~j~@#``$Qj|Z3b;K{U~oQj%j$g!}>!X+-Nw83LBLR%Y+Yc$o$#9h@Kg* zvG}*3BMK*Vs#)bOemL@G80%J8XNItMk<6-&eGtt9c_m!TPLM|@Z~7Gxos%+eMc6K+ zGhLUk27GT;-duUPI_r4#G6VmBmHoexHlz95y7tm_&*6I-G&Gr<(@KxW2iiEUBdnOr zKh&bU$T^C~l_!x&JAOCIU3p5M@xuK5xObXecXcPJ1J~1Tg!(AsI^Gr}wd~M;lOq{< z`}8on=snwTLY=kw*SAah(sj_|&y15+H!2{Y#XhTJ*PHw+WzRE9oXmW=f?kfjh-|RF z1-rb&*SY@aY+6O87ze)|bxmZ>GK9>DvyyH*2di}bTrzK)x4p-<{2#Xo27n0 zuQ7*fJ}hK&*7HMqqjRI-JyxX~j!C?orOrO}E|KXCuWGu8eh59(H<3j$KEE&gq_r+^ z+{o#LE8oeqzbB@2gZhiw-)T0wQExo%u+JVJ(|k+(?E5S2AJ+O&L9dJrybF9i16}=t zQU^YK*kn;J{IUH|87zS>mlZewAg(Ubu0a1VhBt>CY<-#8m5(5$*hsVk9`7Z@YjAIa zH>`KzIOaz-D^4w^yB>I;!*nH$alYJ$@bnSmNnjS;eSgQ@pAa6XYi#3i2C>3fJFRf* zH{5yO{Si2=k2t|y{I8FMbzFT2=sfSA;GQ9`^Qaqw>nfi%s?;HF=mYgyU@y{ZsGW~O zU1$68?Y)pOZIA5ha{jCK!y47UDP4#B*{6_p7QYpK5a-bMW8i*xs8{!9l^TzT%y?%h z@!@t6Oo!)3g|Eib5~sAh=RwA0*0(>Di5&t2+>{+@qt0`Walef#XOWy`EW9M|-gKXq zhxG?Io}P`_plqwcvpsS)K>VE|{P|7rdEhhoHXr=c_Q1D}kMY)OxT}Y*%|zMCqPnc~ zLR3~OR9~onaU6!5euR|azR+%wPvPhKhtTL_(HU;^aO?G?WZB6Htrae@7y0NJRd%*1 zaS%7l($giTP>7Rxmh35!#aq(GdZsuyHAVjeM~3WB-d622S++w5+N_;!!*8^Ax=)g4 z60pmoKFg|O0w2tE2En(NkxsztiKd#&+S*lSXGsU1(yDV9i(X^!+o4O$!rGYC^_mCA zN~K(#T$4I(D&?L*T09&-cH_yF-=rrTQ|^I5#${04Cg}2Ub})ndR+od@PXOmGj2T0( z%wFqx8>h`qH6XrdacJLHyY?OH6gEM}P>&qE-1+;-eL9}cGjKmuM_!Q+#%pvu2K|l! zjNk0RvlGu4;$nXs+ld9t@c|sek>&6Q{4!$ktkbGjP zw>Q-xUVZ=4jWctMo3ujieUQ<(+GlAin~(-NeYVqV_GYUe&`;^hOh@{8hY^SP(5}h! z8288(cHoAPL93VCwd3fx3AowEjIA|+UEo!yWXVsM(UmD-6GhpZkf08{)F!vG?{K*^>LkDQfwVWC>T;^xgNE82kn8?-?F^U*^6(Yv6mEA!tVo7L9$5L&MCbVqef zc$WB!qdJcHN$8cY^YF_sZX)9h-Y07`9`?bFE7hE z5zt|MH!|Oe*F}7$r0sF1eIZ`XLA-?DfFTa=TR~QGK6o2oh>!1S%_A7cyHo4Gv>Rw62@5#M zTlg;O*o$AsxJL01m(ZCW?8;-UpNQM+>Mb26@Ah)(mQB_c!*0<&sjM^&WX1cuy$!^8 zC2(;OCuO}y%Kf9X_RjBIZ1I*3V+OKUP?m!*iP_7KTAdU6(3YK@w6-aOGCQt*px8y^ zO+KQJ*uqSrhV9lzQ9iIEycb-3jdA%#`v{tTtTI+V2j6e#w?E^y1^&ct@YXJOdn=ES zCB|#yzHj&&I@S=OPs*SLd7O_wBC}=g%#C~2!#m;yVDR91ibB|)TN%zu4|p5HKDfDao`si7ev+(xL>X%P@E9JpgoLg!8&ofZXcr_>mMnOb`7Ark9~SO6c};nC*83TUza;y{OxJIV<+Haw3bWj z(&f^6j1l1M#?l=q<2z>2g|LH~>-_(LMe#P0}X@_6WfCi58Rmg|Y};r=Q;v3=VU{>ki@Bjqgi! ze&G>L@i7`@v+h+qC-5A>_dI2@ZU*sZ@thQxNZHi1t(ydG89cR!XW7rP1qg#~v~IxD zgl9Qn3IoGf9}G87)|&Shg{eeB$_X%=@U#)GdlWlRrJxJATmJ3VT(pwsdCISKm&DC$ ze_9V}yq2H(PftU>trHq9zKZjotdHJGc|a#VD(yagL&vo&QSQg^^y2Bjvk7srKZJ>1 z!~!cm07HFD6@>>5AKHtK|v_W6mpfBqwi#cpe8~AKP`L``Y{G2uh z;mEJ89nTtpiP)I8ulH?Q?r4)Mbq4r$0^eKM?Y8|IuXTale0<9v(w@RYSuWzd3EZ1u z`bPXi_>BI-`Beir^Z}ENnD*oS?idk}fyS|N@TcSW340{I`iC-^4wzltm9P8odvis- z*No@&jtt7V_rM()KV%-|(%sACm@*22XMoE(sk`e0cQ{UqaUbdb?!y~>AL)NBho6G} z6~vj5!jk*|WRzN-xHLOLrHI?)VxNzi-m|cd}n`s;wOS@?qq; z9O;w5D?Zbh^e^u|u5|v(M)R4#&ppdE?jGuclTWyQMdh`4wK+hb<%H3qYhyJ2^3@fN zlER5!#ynae(0r@RPu;p%^Kt!h%zbtPxy0RbK;yKpUD#3>(KPoQML5n;dhd49ZO3}x z9I4TPqR9Uq`hh-vOnANLoZu?mL)qd%-@kOvdGuRTc+N<4^tm6WmZLwg=yT7f5-LQ#3faf{dvn9t$NPJWeW}Fw9e^CaGO_5Zzx&k}=L5<; zpIUHq=~WdN`f~kS`--UqViz7=dU0_|cR`$W{D7 zn5!AZkoWLMYWef2W%!Y2$a`JX`|x9oz}Mghpq@`H|5}>;w&zptM@TDwe1O0SWO41+ z(o~J-QyG_P#n;joNyv3BWaZb=7fVRJOa4KZ{6k+$*Gkm&4!FTZ-SD;aWfJva7xfVr z)%dmNQ`r}qez#`9$(n^VSKe52Ma@zOdDRQ4Yf{To+0^?}*QT;HOH-ND3VC0bS}E`P z)Q9AKed-2ze>l}B@AszOC-2(SW%9l{wM^a@r!JBA<*6&=y);$F_flTAP?JWEtHT^W zAP}>2{GfzipK6fzhf^PscbMa)@_kk6J@O87Ow0Gu)RlaPIX1n}RI~8Lr5CAtxL^s0 zxbcOhZqy9l5)ZQnjg@Q%&DU-Gr|eaG6e^oEXrIfLmWkeKghb;?mTE58eOCM%3~J zHA@$x;o_#-n)f}Qy6uJ3P2+3o5<{t5iMkyBmq^{h0IF8xUs*$Y8G0xG9pS&4)Gevz zZ`5?xJX*62|F2k@dc=J8nsn--)Zz^_gDW=F46LYmhPCYW7g8yvN+Iv-QmayzeIs@A zQaQ!9rf%sHTB8e zFB)RRUEBR3%WAKve=&2~;Iel|WSjRS8rjP?bPc0#ylAB~X<> zRRUEBR3%WAKve=&2~;Iel|WSjRS8rjP?bPc0#ylAB~X<>RRUEBR3%WAKve=&2~;Ie zl|WSjRS8rjP?bPc0#ylAB~X<>RRUEBR3%WAKve=&2~;Iel|WSjRS8rjP?bPc0#ylA zB~X<>RRUEBR3%WAKve=&2~;Iel|WSjRS8rjP?bPc0#ylAB~X<>RRUEBR3%WAKve=& z2~;IemB9aLkM2<#2%-Q0PfUWMjdoi3gLM$`01+%bK-vTa3qddoTBh^}DLsOP7w`s~ zA|jH)0|+*j!Z@33BoeSlDc{1|_jc#K+1+mm5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7csf&VEG^WB#e?=j!oiNkz-(XWi7kNZVuXXj#m&`fa>^TWsZTzx3k z;%j{~=Kb`J{m&F%aqx7R;xvTSI6FI6H@X+y>^f_ALL)oB%|he)>a3l$Lt}F8bh~pS zf81D0Yn2qsF&}reLgl78t|q6IvffC!=v#_|UaESn5RUK4wIWUXwo+a6RpTISSjk(V r7Dh!wdDP0hopLc#jl;JP>Vu=O{OdPO_Z#n3&P@73*#EhnJS%(y1X?=8 diff --git a/qemu/pc-bios/openbios-sparc64 b/qemu/pc-bios/openbios-sparc64 index 9bf3ce5450434a2fcb50089b9421d9d808bc95c3..4d23be3edcfce7c57477e28f5ade696bd56bc601 100644 GIT binary patch literal 1592280 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ȩ@? zQB3Qbq{Go?2<38~zgNS`5a+Oq*kf(+sN$7kpGx8~hUVt5&~lxD}U-l7a! zF-B^GZaj;Am}l_5fed{8J2>A6{YHkv`ZmtmupYBbxQTt;h*N3X@w>gq#1*d{`0Cr7 zv;WvW0@CC(*EL>%HgWv^&p@{YJbgXw_raKE>a(!#g?!m^^7C>I+Z>x0>K^JEwgr&S zKS#c8{|oKm8D+e~z3RE){5qCim4S|2(OOz~DgQS=Z)Mp(g|f#NE5~lS(6Rcg<4C_1elV>|J_l&Cgp~kjJJ{^uzhh z#Dr`66wPQWpsU#CjiA4)zd3?DURkWaF+ThA&B>o5zd7hUyLP*~1#^ao!@-z@-^nP& zJS*qw_+CaV?|SUNVy~{R^Y@QlU0~d-W5_4QwM)kCz9dt*`+`i#ZsY;-K57^G-)I9q zggH9f_W}QCGxX=zFKolytvfM}eM79fp)B}i==T=~%h2y94lP6HB9jMR zIodSB&hH!hdSGbZxcVz(!}baFJLntxx`6SAI)L%}ePh4=pT2Kgz2nq6aL%HzTv!J% z7l?43+mN<+AgAwO`kz1?kW=cWoeYDz>PzQ0QaAl6pKl(A^<~&WUr{%GfMs##puVDR z`ZKzNz8cMNksr$ZZiYpD<=0ogorPbtv+Q%7^Dg?~Guv;2PH0Kij-zY^ZT9<^c7zwU z+2@fL%Z~l1VQuymVLoj=!u&?l=thJ2f*9AXFN7{aIZyc7)$E@PwomsOcVhk%JH4O| zjM_S<-MQ@&%tc2{lJbx7b0f!I9FJpOWCF)oPu;N7yXPEtJjR{lZn?zanKk2fU+qrd zn38So*WhpO%-mSay-mQeZ%;xpgL71o6@zmzcn)_hF7iaTytaCgDCUC>q77s^I0xhH%@)RE zjWyWEkowlbticpcFTSf=;9Q=!$BA)`1M4Z3xjEmpZ2|Vq*u3Ezyd0+ndu?}O?bWUq zzlUS?O=x#{&IZL}WZvS) zh57Lg#(XoYUX0mx{f{0uy#yH>K5jZV(X+av8TsXN^darI3EwC3$4wKx80}$Bz%9R9 zI$@x1LLNl<9~nL#QMfM==ZPuzAoN9h&EOGV~?gaZseAt z{=cs$9NYIV(uHihY0#vePe44L;9 zl1C0xH8@$(9Q`Dn!IUX z-1LW_1V)&^CM`EHBFN=RJ;_u-|utvBI{GS^LDcb6K9u=c#jAVgD3;i80JG zbtl$h?HHhV+(f-a9gd}yd)p>vxff@2XFNMIaBusw_(r=OGf<9-+-KV+?oM^acV9ra z=#%Z}t>k+r8S550k-9$`dsqMWJF%}4Z2;a!HdC`P=vk{XO{8LV{|~9t@T`M+4}KD8 zb8rqW_bvGQkhjho+#kkw2pK-cDt4an62!~36UI+_t~-O5gZ-2_j$^P6x`R6Dfr-bL zK8jA4w`(Kj9L+~??TbUl-v=(x@h738K)}7om(a#ss$NBSiZ?B!N7za7MqiK?3 z(}HJHI!)$YJS)MOYHK;h3-ef+V8+gZ9bJ?NSc? z@z&s+?g;IZ?Xw5{PTM}AZ&(#uY-aAk`T_c&XYInCyP|&R4b+i{C+vq_4)f{yp#x)1 z_Cq;0b>jj%Z(6c>y{`**&7_XvJ7~KLEgXMkF{g|%4&ttAaoj5U*^Rwdcepn8(z%&-mh00_@BXKp%)Klt?p?(5DC1>0F;2*q9m8Y2cS=!t4UFGMD6h3) zTM+gqHZ62G7aeWc0R4$^VAHuK(pTo(+k~@F-)c;px&&$Aexzt$8TtySBV2jt=D ztcSg)z8u+oLF5WOmn2s(Eu-EgPxNDvW@AR-$NfZG#&W!S?&aK5OS!;)T8v+2KD=eD z-A_`|QI0vAa@Ln~?c7fu_e0zLac8k^(H`leKE$}+gL{A6U%a1)@%rUBP^ZH3YCprX zdyDhSF#>e~#{&nEr(k|-2ipXO5!SbGJu2GicIM;9?YOUy!Cg+JTV+=FF$_J;K%!5|+W zVA#7xxl`Hy^6iqd7_OBU%ss=+hv)|L%>(ncd^S+$78%s5-chJmHCyX?k;hs)XTUKm z^jJClGF*Rd+$m+)wtsfY+_*c6ZUbEr<9A&)&0^SYv;t=}LRXBYK4M(VL%?QWo$2Pm zJ#nSzN7gVNtgWyfB%17gPk)_>>n%v@JGeH3dN#W?>Li*lM^~hSE*YkSrkp_sT^jNx zj}~(n=c1vGU~G*v+BF97dmr;v)WJ0z_8Ig!Q1>7=cQ4OzuH3;H;dcLAD92pqr9JkeRa0*rjCj@FO^eIA&gW$!2hrX`p6r?x!{r~ zwQYvKw+d@%F|=D{h@*;ez)eXYUyQ?!(_Vx>q}#4(G5iA)-IC4cyEQ@BtVb1egEkN8 zbz@kE8qIYm*u`L%fP02Dh3>j-SpFJd7w%#IUC5dZdkKF2)RSx#~bwzxfS9OE^-yN@#8TFZH& z-+r9qf~IX~tK3LE>(Ai)j;ZQA-5#VwZ=rY(Qgz1QopY;ymg0PIl%YwoZead0f%^qb z3C_0~hq`Y%21u+A{0*-Y*sH<%+{3;C_b0Iqur9C;v>F$EcwZO$y3*IhjAtZ$L3{WM z#tgo`fIr)gaL*Fs58E%+Erk7FS}Q>N5N*O9mn|lPb9Mc_1A#95qx+XlAH}@Kc{45s zlUFf+7s9m=f)`>$@}bBre^7Rkt{UqeP(M)wx)A&fJPQP-3nKzSjJzvEso-cLjNa&wHC zVVu%)pu^8I&b#kHzXJ8HWaq`{irp7Cmg75m75gskW1C-tGE8DVd2Cp|m_xSnL>TKt zVPB1Jlkt0M6YyQGa`rVwk}0tJ6UY_UtnGLOVU7#K^XHtBr|ow<#xku&`-gtV!GClk z^A?*{>MP9evF{M>i}*m;X0opq_CYbnH!EysVaGWsu0MZ>b4`#7^xORP=P$n9#oi@{ z{f((xF`s)U+HcICNAE0iM|X{KYdS}{N%m#BN4eG1g|_~WB9ow9g}y-_g6CP!Dn~sZ zi2?F&_k8^t?7`!ckR{GfZyIPXF)vDg&>;iu+E%n_Y|lbjW_^N8P(NFF2Ajxjv$1cX znqxVCK9js)hj*c4@p65_n*)+EkZFL5H!wa}|>Y}5IWyWKhHp=WIU zz&M6$MCfyX6Z;742iiUi>+dke1AUny#+Q;Y(o)ibw6PC!m+|bnC(>m5F3ig?8L)jA z+7H$b$F2*6GH~lCyz?~=V<(&|zp?P*m~CLlz6SQj-A#5@F3Q}=`D8Y>*<=&D*ZO%~RytZuPu* zXH!|7=}4@EF3D1+7x3L!m?JR%Da>_&8GY64J7K;!FhkC=X_(W2ITn~N2+XB0zZK7G zX@2O^j2Q*p-I-YVmzyqaOyL}Od^^gC;GSo~m*SehHO}%x+hB6oDa*AxoM*;9ii2x( z3%=cobbT22+=rArS(_>2J`33W73{DFFoXBA+Ig*D{law=ewFx*#%~OMRr$oqk8tb< z8I0$r)nDxG!?zgm&37{5IIU>BlO)sg+3tWT=Dh6O*g3F6e{I5^@l0ENhI{hv#XEVp zU-n?>{x9Qt6@K;jEywR_{2K7H^^on$(`)y=0GV?5p1^^L*q0SwXYf6cXQ@N+4Z&)x zx!`-O`(B{Vtj2pJ*gIMB4d_eg%@feIlUkEM?`cY83hWoRq_SL#i8iM*rA?_!#ICor z`fV!q(2vQ+oXA%%MtJWxjkmj(??OYDXYh{43B2FfYhrF>KF_qk*m5^zbnBdKlyXWL@@H_9= zH~7o*kZ#s7zFQKu*(ei!+mY`Ko{xFa#n>ws*;1C7)>D#Y%-4S_W7$waXrG;urpZ7*5We|yBO>?wCE;r4Q*eJtJf zRP>^)aVX!TY2F^g_fHX4)HP+hVjEw(;&!TSxWs-P9QN*W(+dj=?j|C0@9v!mEaRq$#^3aQ{A-&!_tq{Loh^ z-DM8{0dA-7sm#8De#IoX{nNnhQn+=3uxB9bw*+BV;M1lH#-TrFb$x{w^LBaV;1FjS zz&^=+i?sU=_qIbe5H`ODHJV`sVgDJzE)T+<3ODgPR=5%GAF_q%__jIo&u5HpvytDVSgpM5FfZE-82i_Kr$1vjWW&q5B2x6`e;@#;qO{q23Uc|~KyYt2OvUPWg` zFTP)7@O?K^^Z43*52O3^z1vt`SMGZlXSCP&&mEJm=pDNQ`l!32m+t~6*`LQ+O%?2` zpRDNR_%LzX`F(hIz%7BFaTt$8&|ivE7GbVwD)l(tP2QEvF;0$MIQFfA-zl51=8dcM ze^)i$gQ)05z99Rv(|nJVZbo5l>F(QOxlzy$KWiJ8OK?7^6W3-1#hPloqk1sC-oTP9^T(a`YuDei1yHlpq~HUx`8{qLj+mMV!kL-7Pj|n z->mM3uDYkpObFY1xGm3JSf62ks^h}?6*s2aQ=J#qFUK{B>(#g>N*U&b^$l!as6*g> znQdb(tiOu;L}0GRd+2otYYgJVdB%hNIGF8wlx$z{9N&CJJdVqL)M%ug>ylXe=dS5^I0-21`3(4AHF_1xou zu?6-qAzzp~#dr|$)?bZ0aGxjV!0g^2>SfqBP)^zA!Tk#E-KgktV>1vR(tHKmymP2W z(FRbj!mUG{4f&{^1@j40U$=jP?NbEbKRvl)FTTmTt?Il&^Mz?=7W`rl_o)5G9q;YM zz|S>rLto9@g?wNRwfq6pC-l=DFYDIaRgz1hACmg^@?7M|sN)%~pXEO>W69SR_L{Cy z$Cq)RL+7aDSLLri?jn!r=2?w(9ixudBae1ZOMcYx&zRqtl0$sVH_B`Z^NqQk3HTm$9p`ee z4ill?;rFsoZ@ON_`#g}}fp19$Wqe!M4z<|19D7biq0>3_oD91*1sOwp==1N|&OTf> z=AAj8-0?S_sp#@hR#>A1r;UXJU!#tqxqFRgn!~dl&==%;208R4DvqH}`)%%}4`ME! z>A|-_>^BW@PL=IzAdJ*DjJ2@m|JiNTnRV0`=x^FSev#W($e_sWPvN#TaQllOpI8UT zuzxu)Z?$P|W68l}<`B{zjpn?>cks?8`+K%ug>$|69CDUq+BO!pKi8IYmOy{w+$MWw zO9^zzUqeO`eQR>mVK3ktdx`v--X!}2xTaoS=&^0#g?QqY0qMW0fW~b4Y&K$zoI+zQ0A{|a9!FPw*2V{8R z9DnE+tQ`8qc?%`lVc)Y)(rW^VsRP#C(_~uX<3Q1_-CbHvkTJ_q~TAH z1{Y~~|Ldh8j34!j+y-PIV3_b_*&T`7*>8|rI!No=ac)+7z^#nw3eq|WX~o%gg&91< zh2;c!8Y#XvT270D_@;r4dun|5{Q>bUMto(j6W<%nUtJJiDe{*aA-zB;Vc{&IMH z-d?`fV`hhCK>nB$fGRgMsNFbNt{>QMq*uHV@jJhWuaj@Q&VZSq)pN4O( zV|@dZNQGwUu zdK9GpTt55n&cpo+8y6uzVLmn^AJDD++nA57m!%gCPit}ds2@)!yVOA;*P!0l`sLgI zkC3OfpnN9{%I|$EP`A)#r4WH5!(i}MIDSU%w+TjOPlbX9PIC=9&3XA zx6&B{^NW<(uN?obdvfjmeGg(^-R))jZ$z7pZ;wqkFRtBxBix_$(%SuBczNyqJ5H?K zzXQ7eipSU9ZNJ$>Std)$E_qXKDW{Ze$|>7@oFjP$<+mL=e7M|FoDV@c4fZS-;Tz`DWaeU`oH;VA=_ZeaSCE^(C?LnK2 z@3}I*Fb!nEGqA!sjJ#tVW@k^n!kEn%Lv~{Q4C6;9if$LiEqHIZaFXSOGrE2Z``0k0 z!WiDc92U-+fUH7>QnnnknF}{HWH4?z`~uue;CQLxh*!pNZ-iU4&k5)PoI~H6zO8}h z3T0CC>t!=(j??Hq{~pLgHnVOX&TFGTgd2rF^gHnk=2oPytK5xwH#ucU-y~dP80W@u z#T=d=9{L4w$aeR++n; zXPKATN8D)lwXSKy-Y~X5TcLOUSZ*zrTh?+zJ}ft$ky*^`#v*P^XUL83u96$p#lR^$ zA~(7}9XI<7+*-hZ>;7JA*5}rm^|`fXv6m5NMId~i+u;Fj^wVSx|A1o($X~^#L2glU zqwZ*Zj~62^a$~&;xp7U~&Lh6Z#KQhgxW|@tm-PcOUgOu1+vi}P?GnUqSQf+G57wpI zXTsg~&>hni&3m=19~d87ndaUP=9%Lc?D>db-#+C!c4upb?c7-%6>e-xYnJzuz1?mp zuGL>cAMlApZnSrECgRIX#<}@z8Gg&G)Vs;IYcW}83(hdO6HL6F?r4nnx28D|UhAfL6tUz_9FehlS^>CRWU(fn-8v4K70oplsvDrX1IXASCn z1!Uaz8IkTt2j=YkW82JSEg!hF^wv_}RWgcYAhBO*gq&PEmRs(@|kc5O)dp zw{zWYAg`{$y2e00USS%y&T=r{;iUY!hB-*YkFcZ2e--O~R|Se1%$lYdlK{hFEPC+laj4SD}Tjd-ZDFr!q_mCe~&>vT|1W)@Ul7p`L%j3WNPIXaieS#;rWcy z!Iou7HX;9T&*S}b*!~xO2OXZm7%$Zp2U9<;8Zfiv)E32@#$J=C#TbUq!Zz{q4@1X1 zhy9FEt}z+3rEWQ7v6S%+$j&@4LD^4#SK+lF4X-c_ejK*kD^ZT+owgipImHo93CqiJ zL7aSsw7{bntzq8=VgJ6P)*Z-Wkxbd}QAeYY+h`LRT~RnmMoh<6=r#Vr zG?7iT)1I@5Pz(5;ldpyVSV<;)CK$P6Y>hn@=Q7jbt6W766hGpImSK)?bYGl zBhXQImS+lqPKr_|ZHb^Ab6lL=K1BA5>(TSnKU-&IhRA#=*oSRB^&5{^k6(%C`yk<9eJkoVD8@65vYs52=rqK(=<}L%7Z=#%i7Mr;9QW- zj~TON>@=J^k~2Yj#WYs5Ku2I~9O<~$z-bhC){AMoa@58y5rsKMDA#4Oye3wpJY6Fk2&U*!5lkOwy1E>F_U>N0pjEt`4`%L z%gqkHv%RPQ^ZBqZ<8#{Idb5LdHf!(Dj%M_&E4;nE{1$c$SDw3C3OD?&Z!CiJ(>>kJ zKIX-DT-3{U2D6j4jpBDszs+)newf@I$wUrb<5g{bzgNY1i5ABz!TN6$-+cXc=Pc|S z>p%PozZD9%DQ};f+SK5kg*=SC5qZacrzm(%*o1iSd@AJ;^EM9k68frTXd7#4A+INA zxrSk&Y*4Qb7Z|s<&oMVQz+(x@bWu|pu zJ&^a-&&^H}d8}re~ZFdb8%Hh8*@z9WLDbHi!9&F;B=_ zDbwX;b1BPaSiZ<+vne19gZV~-@3W$9*twCqY}8HQ1AjGj(2r~%AH*K~E!e|$qq82RF?Y>0JARtspJhS!bj$c!5eMa!X}%9>X8E_nZ#XtU-Ep&V zoVOC^zFd>^_L#_IaN_vtfMZfw^jrDuuoeb44t^J+ z9I?KbNn`yv*@3W{pri9hOIIR`Jy8zE57alA^Pz8k%Q}Ga=lP#8gkK5!8R(~0w&E-! z&`B46poyw;5E8*{9J@jSlQjpwMd2W`9DI~!@LI0z0e z#d~8dCL6{1i#YQ$Yrpl~fwZxnBP`4{BJVce**Wl2b0_??K=$aS1M6y=q5nH8U5>S4 z(^2QyCqjG=2Jwx$6Xg{2`x$3B&6Lq@#LGCzfp*?5$9$*A>MfKF7{J* z_s+q%+K$msUdghU!CqFxo9snj%+6JoG^dwTA-{aTc1#o6k4@0kL7b}?C*#P!+lhcB z&2i=l^3QqA_jd6a%t6^+1LikwM!K-aWE|XIhhP14?2+`o;3UUWH~8g#6WF-Wm)x&C z33f~ITMoaixPEdvzJrJ7T+iF=K{jBIwdl-s2ovYYXKui6HS9*?+6cE9Z|`BAQUAHX z?LRPFJX?onH$%qXi)YvK8N6)a!WKX>v(*Zg(JCuBfI zj2H3{dklF*c|{I_JKG-S^Tc)gzsWIGP{!~2;=(NA&szCuIuCP2@B==azkg@G(KAJ6 zH}808CyL5rrMaAWfZGX_3+i&p95|lC^5T69o_DdHfmal5NGJ3S@)*4gW!gE{`kjq1 zpd;EK>4bm8EN*w+f+{BqT$kNB=Ek>M>fyeNJ{TQa5Ke%7X zI?+lXd zI^|}pN52yGOK->dd~jRN?>^uR!D;?GJ^f9=J3a2L(02@jI-h;ra9+(1tMHr0io%NK z-La5Q&IdUyj#pmU<;8ET!#o>`qItIQ=T4w&w$3)?&1t;gp)os9F7CXW6UvAW*MVB9COSagZ>ZYvlR0* z=;CvJPrF<}|;8OsIkqT#z%6~|nD zCknleuWSgy!W^Ds;+-zeA8-ubb*Vw!d<}hpNHpi7k7R1t$GLxgV+7|^^DOiT@2MBC ze>Z}DVzdwIfBqQML@7sD+k1t1!#L+@=Cu#&Wkr1hn}3vd4P|Xir>TT2O+g;*T4M8O zua6)vDe`EWZX$8?L*bTZyi8`{jJlzDDql^xN$xj z#=kzZssr$UAPj%Q^^tajZ_|tL;eIsz(mnF@3f;mk@kMe(-C&%2KAa==6iq>WtD#=R znspp$A=6n0U`ARm2V;Y@k7eJk73U_?jD7l(y`9+ij=dkK`!VR~=t0cI!Vc?o4*FNQ z5lajvQ&i+1iW?ebpMyxzTU){JzMy=C|WJ26$JXwr^2$ zx;=vZi7e~h7TBR2V$p|942(+q*5aEZYaRb=Iq+t=Z^xQ0%arBGa!2_;fU>AT`M>aD zkqq28k8|kgzo*-s=6QWiYr-pUNqD2SyxT-uu;*~|;#{QZ-Pl+DR(~zDDS~uoy+{Jx zXz;Y>#DG<76TT_big36OZPQ!R&bIe>m`6jqgE`9E-(&3A^leeR?-Vs9tm~LxkFqW? z|ACy@`83KIidlamAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F z0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+ zA|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`H zAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F z0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+ zA|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`H zAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F z0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+ zA|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`H zAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F z0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+ zA|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`H zAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F z0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+ zA|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`H zAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F z0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+ zA|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`H zAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F z0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+ zA|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`H zAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F z0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+ zA|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`H zAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F z0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+ zA|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`H zAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F z0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+ zA|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`H zAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F z0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+ zA|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`H zAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F z0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+ zA|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`HAOa#F0wN#+A|L`H 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%0AOXJ7OyG6ls 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 zOt@V6OUxVS^E%7lyPHpF++S$suiLLTISUa$00ck)1V8`;KmY_l00ck)1V8`;KmY_l z00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; zKmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l z00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; zKmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l z00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; zKmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l z00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; zKmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l z00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; zKmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l z00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; zKmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l z00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; zKmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l z00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; zKmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l z00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; zKmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l z00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; zKmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l z00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; zKmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l z00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; zKmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l z00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; zKmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l z00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; zKmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l z00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; zKmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l z00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; zKmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l z00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; zKmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l z00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; 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%0DZD1=zXe+G7x2%~)%Ez_% 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 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=9z<)gi#v0LLsj>Z6wZ-jh%g4&4=JsC+oV?o!Ml^rjSh2eOoxsb-RQ{o{Ba{*A zPpW))tX!?uw!hovG})O^`R1{mE+bVx<<~TSOR-h^7JY7Wn&ekFrtr`{%1G3IRm8zZ zj-=%`R6bI@5Y-d^{zj{kf^~ zpF3QCn?JVspKfQU!!d=QuOCjy*F+qAsy>;NuNzVMFBGF%bHUqRH>UDWJNcv5&+8^t z9@brkm(QsDQIS8I>Hl>#&3|m?Tkcbv|Lu0xU2&-(*qTerud4j<9clRum2Ye9NVQ{C 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 diff --git a/qemu/pc-bios/s390-ccw.img b/qemu/pc-bios/s390-ccw.img index f64380a972f280d66a4df03f029e4e1d42da292d..d3978ba0506cb24050f596b2e4e710dec3486fba 100644 GIT binary patch literal 26424 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 literal 13784 zcmeHOdw5jUwO{i{X7c1Dq%b@zClEA%gyEqAQk{^15fMfn5^6QiA&De2aWYV_)~1?T zP}CT~Yg>C8k$4bLW2_Z!t>*h``>EA7MJe8j3QV!Ip*VwBsUYY6);i}*CQ)zu-FyGM zhi_)>z4lsbujk(T>FPmTrVZgEHhx33Rt8eOI`P&%$g72Yra*X&%&I^{wc!|3k z-zn;bEhdiXT%}pvKRj_@!Tj4gqCX#7e#_gxyy8&SO_T3<17rW~1Hq;vth^QS3z03R zJ1fB-bUzTIhQ=duQ%5W5l(405L5;U4B0<~3< zDypccZfU7#iByFnKm}`QeM30X5UOad3O3YGO(@v1t|?H_Qq$O6(GZMKxFJ|qQ56nU znG=}6XVg7Wrh%~L@u?<(iMQt_~+>%0pOB5rGiQ0s9F=>sq08Z7cF z^a9Jf;NfyrA-_s{UFZ8hj5R~IvwCIkF`~4Iy~lK2e;T@y%hex>d7(`o!uc2|RH0~Q z30)OYFMw0KQrERMx~I1tQk(jRlR}v*ls@u2yIG-}aKQ_rkQ>KRY!CY8o&MdZLdc@NlP z&j4oTZo>DaQ6<(x53FV>Z8S~WMKDhwz3ASb>{Q@o$+Gscxd1jicvyw zC*xgX1v5@Cw?=(&KP;O>)E|l~)V9$&_49q5%E2&zj{J+mh+l7BBd-(OIdcBCmU-pfp zT_XSF{tTeW?TS2v1O4L})4vPfnNe>aQLA7w%anHezD$~n&vr!D9rd*nJ%9*j zM%8vFwU5rzrs(>wu#hiG==x+o;XAX_K|pMqOpK_WCg2wx}=GNd?6#W3TKl z@?&O^4xVn#@O|iyiMvHAOKMg<;uYc37K+d~s439ERSlj8FYQbbVObhov26 zy+7+V^yYZ5-kdcF-AAA|S-Xc6iNe3Z^QZM0z>U^^P72_5dS+UXlJ0 z8M{i_f0cHzwBHoE9LT#-`fteCOliL=?Wxj!iRsRL2D;*bTFp9!^LV=mwP{)zkr6FzA67tLYzKO%?fx-1|70N|Ty@j^v$3X3R8)+i z`eoiW%zFoG{aThbN=2;>=*w9l(j_C^yIBL;u5BRNmq&H|4{?r1+mSJDP)-tQH=0>{ z2P)=K?RFb>KK;#TXL|}gz;?S2jM{G1VTOk>DYuJv@0IojX{U>)%fN;8R=8d|gHU9KTY$ zJW-oLUF8fmrUOwB$9+^2@6c(QX{CE&6(y@hO>I>NR7ww6g`@$(o#f z57}5P{U6F0aXTZG+s;n~zeVU?1KpBx6?^t%vccCqezeUB=UzC?8Sspx_Vf?iKa z+b4S6j+nTxN8HN0IM&dUe4c=Ru$K2M!z$vT(bB^??tHAIvt8C+SA0chpL>Pc=l;re ztw5i&joO{?7fw-#{xQxeeY%xD=e~v(c6i8s&xo)-0lpX}I!G2e*GT&&(XEo(X{SUt zFXn8K{s|ddCUo_(ItjLLCcFi>x4mBRIF;kr!P zy~4FjxY`ky>ck9&Cf!0iM`&fON}DINw=#ZbKDW=-vaKAoY zBahv(!aMvzKSt;maodf(A6^~Z>xXU+@*02>RwtiuGDbk>)aY;ww3pa?G#}?Hb1nF! zgJO@1q!f`f5!OzTmde;PUD~68oy^kDeT37U2fNw*#>o!3lQ>_dA3*N-LD|Enb{jW0iJ8A!kvqGDu6_Q?}C`3hj9N&tj}fZ+4A%s47I#7*>WT9n}l*rNwoer7b81^pX>UoSL= zxb2p8&-Mqw*9-mQ;-NPE7SQh(ysVoxvCI9M@V+Q`ztG<&o^8`tCg>Lm%^smyDs8C* z7xp$-yj~*GrsFiG@9&s_H`LSTU++7kNf!XdrV^&x;PV%;$bW+Bb1KH75ELaXeGc@Os?UX-2h;e!+D1Zb*qB zI}~sp-+y83QM@Iv^evuTZM3ehRQeRHfF{da_Z9nMzr_AH9{HXdr9L-hp{3$8ovFE| ze575Y>$wxKd;P6giM@o~su<^y^bXY+k^T<-Q&irgqG!6yeD?uIE6GZ}%CH)%jCne= z*37OFrgxl9vRdB2=`Uh+Bac#4tX{`ffAXwIzPVg&rq!y*zFaW1W^XDaJDBn__!H}s z)T6ZlrXJX0l(ozAbG< zG~Ui!f9Ly+_t{H;2XW8Q7ppgF!+paxQhBEU#9ix5eXk}C2e_D?Hphu-V z>#m6P${Qy-;2j2@pVr@HE!?T>vv)qk{xjC6LeD~S z0;U7{{Ho%?CljAMe3Ww4&ok#^X7461jb)z?KWg2)V2v>EcFynB+_(*b?w;+Q?x+&S zY0Hy)Fe5%IKC4)bnL&L3zCsLn#X&ToBssWQ7eQKMo z(S12a`YC_CKy$H%Xxx>n2)u}8PIB# z7a9y7tbulglS)+Ia8ORi&ND{4Lgt;oTq{sDJnN`@_{Pu%eiUtnGv2U4yTZ+5+1eHC z6a5&*Gofb`aJsBE9{LL4JLu;F9yH!zw6o_A10!d4b>S} z*-dohY_yZ_z>2+bl9;-1N2O@1jrIQgCS)V~z}KC(*{877Id~T=W6C3^pl7MJ+9tb? z@pg9PIOL3j1RL-;)#C=Jt|uj~uvd@#5x0RVIm^jc7*)7BRx4QLB2-_DyFfq1I}nixY3`BfjUP??Ous^l(LeT7Etdvbd&!3s5= zhSq(AQjH2HXNN654)x!T>Q&?8a9eqQyppg2@JZ%KqiMei`&EVDw^DN7tx_89dbh*t zHQHpGp47jAZ2|rn7yHr1Z|C}@SUcoz44hT*5}cJPC70UB!jKe?g zTl=N+O=NH-;{PXP5FONI1;_0)+k5UE$=h7qopg3xNct+ZkFXo)n^YRt|NbeyX%#y- z50d+^%MZnN@$Ph7-c66UYNHgwDwMdn);vz~DPFFV>40`XJ78*01$mS5Cqk|Rwpn(p z7;WUz3UP;=Hgw*grNmkr*^l$yS)$)RI#UF zqr6p}Jq6v{$S2v{EOw0KwdMV8tOfal;^XEO(vFx_df0D%r7{myi?a#*eO&4le2y1b z(y37cetQ2N%TH%t;JNf5%Xi3q@2M2j!L-z_#$8b-w7e1d-NE$?SkgZeN5)LFz86(HxjvLm*>y(o9*_ZDu~KQ1bRGPMS>Jw~ zHYk(j+ivna)AcjL8w+kPj%oiPw}O)(X*P1|8peK>r{$B5Rbj+n*@jfC$j^Yf5U1fO zmUPiu{&o=WfZiXtTnnoj8UwXzB%}s{Rn?6FwW*~=T~k%l(AW^!m`=1Lq#A!Io?6|w zR;>-JZ>R~VYeMUSwfW{~RbxY~T3;2eRcjksm?RRYi3Dnk(rG|;W2mZDUAkOd6Am>E z&_Vz|4*p8dSFiRjR>OhFx^S>Xg>Yb~q$(H;MSul+J-z~AqFX^HdT~Q-s;a}^ujePm z)yT%?fY7KiX-lB4DG-dnB({@iRX7x^gRPP9Msd4e21DNrsx6{`=?L*zN$Fw)Bw2g-cLs#kgb-VpZzwcXsVGrd@yf6kSx>Gh#T{Cq)OTvZ)t98h%z9s9$Z zaiXh(h%}N^az(}T>kaZoAs0=*kftRNHjJk0mo8quVA6n*w77IJm0Y`=)b)XI z3p-$t*9^?w|cF&e1{X;rwU{$KEVq&}cdZ{c#{kQRNB*0iz(zx+^_mn^>;DN-M33P8&R z;V|^zG~zLTI0U z^vk!7V&u&Of5vzEWK~S7sS2vW0Gxwmqq#W{R41pKI!eYEI;w?9={A|4Nf)cUsw5u# zzDAEFdWql9$nS1+n_WhX-{2$>p_xv$yZ*iCfBC3M{PPN@+;(B2pI< zPpr7&%0#bn?gZaOUmIU!EG>2Q4d%$rYa5$_q2^n{7G+Ch-TH5C*m$ee9|DW#my|A8 zcvaceCVtU1i?3a>)PLPFi*EUfmDjH-U;TBXKd`15eR0Wp{7sRc4JL#`aaY6`K!s@b z48w~yMj^hTb&a)>!*u~`=`hRt`s-9ITWcEX)`ex=;#KF%sfUmbP<}Jq5DBRGMN?#5 z3m02`s3pRC($&+Js*- zOgylkps(OZ)57~x?n^Dr?lq#zM9_yY?;p=W>Jnr8Ue^})J!yBRZ_e11`TeZBvKM8y z%C64jGJhNV&2mK-!QxH>@Dbp7?#jV(2F=HGFN4kx*fyAMbH<_p%~&t^>(Czd<4`(F zukHz;G9J)#J{&Ft{i!dbKR#eL^sp_)tF`GQ&Oe;-P>D7(*+ z$aJv(cpv6B{XE!@i?U^Q_>A+h|78f=OAGI{e)GuqL%M#V7WgHGN%GBdPXqr5dan(h zzcl;!ptx{e|IG)wGqli%AKH*>NBmE!Xq&%RM9aX#{*xT9@q#hFn7$Tx%uADp;BNzd zHgKDU;I{(56S&?aJoxQzD^T=KCCe1@M``U!Nog{EkN87Xx>br9U)qnlzB( z*n;sb=--^gcQNogfIkeo-?R7z*HdZs+g8>{-SGPG5!Mm<+2`q(3{*tYB>&(1mpov` z%HGV>A^rTv2ABP9liSz>$zk7Rcs>uZ-DhX_@UBL6a(Wl>$nf8N_168$y~K?`O4)B)WvuS*)U0+RW!3` zmU=^7P0iF<6*H$+hpU1$^*A}MZ;*5H^h;*UxoqZ*w6vvp)m21AE%hytaHOi5itwA` zK)9KTaOe#b)dklTRj+Gktex6WOVX>aYN@B9+Ks`MjZFrOgsCVTXslu+6-5FYBIt+Y zr_M!z`ieC;p$01I@tTzA313wc-WH4MLZCKi@vCSmstGmmOJbr?#5NTz^{;3d>WKeI zpL4+6a*YEM)~VTsAPXk`UxOvzqUZi_e5^I!Tm|fAeppcUQj3?NwdQb}*JLwq6Z1=c zLk|;DeG(1xxcLhY!wZpjf0rH@3~1GZ1!*4e)*MV?@pIdYk5vaIzf~_5lzo6}$y((s ze$IRLm6h)n|J%SZ+Jt1)n}HtiH}`$`SpG2it$MQ{m4W7q^ym2(;bXLH___)g|8|2+Sf{XdWdzhy5Y)_k6SlIb@KI!t{M z{K@lkj+k~^aj@2jX(of1XVEau;#bh#h>vBz8Al&b{CEwJ)G!eiKf~Mc8Om=mdMyFT z_OgiNejEChJac|O)8O&W5@72UFz}@eG>JB&W6f{PXO5fvMEJ|ZpvR(T*nyALAIjfk eawYqYQ5HQz{#g0XV@Z$KTA;d;kleKR>AwK}MJy`- diff --git a/qemu/pc-bios/s390-ccw/Makefile b/qemu/pc-bios/s390-ccw/Makefile index 746603a31..4208cb429 100644 --- a/qemu/pc-bios/s390-ccw/Makefile +++ b/qemu/pc-bios/s390-ccw/Makefile @@ -9,8 +9,9 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw) .PHONY : all clean build-all -OBJECTS = start.o main.o bootmap.o sclp-ascii.o virtio.o -CFLAGS += -fPIE -fno-stack-protector -ffreestanding -fno-delete-null-pointer-checks +OBJECTS = start.o main.o bootmap.o sclp-ascii.o virtio.o virtio-scsi.o +CFLAGS += -fPIE -fno-stack-protector -ffreestanding -march=z900 +CFLAGS += -fno-delete-null-pointer-checks -msoft-float LDFLAGS += -Wl,-pie -nostdlib build-all: s390-ccw.img diff --git a/qemu/pc-bios/s390-ccw/bootmap.c b/qemu/pc-bios/s390-ccw/bootmap.c index b678d5ebb..611102e3e 100644 --- a/qemu/pc-bios/s390-ccw/bootmap.c +++ b/qemu/pc-bios/s390-ccw/bootmap.c @@ -72,7 +72,7 @@ static void jump_to_IPL_code(uint64_t address) asm volatile("lghi 1,1\n\t" "diag 1,1,0x308\n\t" : : : "1", "memory"); - virtio_panic("\n! IPL returns !\n"); + panic("\n! IPL returns !\n"); } /*********************************************************************** @@ -84,7 +84,7 @@ static const int max_bprs_entries = sizeof(_bprs) / sizeof(ExtEckdBlockPtr); static inline void verify_boot_info(BootInfo *bip) { - IPL_assert(magic_match(bip->magic, ZIPL_MAGIC), "No zIPL magic"); + IPL_assert(magic_match(bip->magic, ZIPL_MAGIC), "No zIPL sig in BootInfo"); IPL_assert(bip->version == BOOT_INFO_VERSION, "Wrong zIPL version"); IPL_assert(bip->bp_type == BOOT_INFO_BP_TYPE_IPL, "DASD is not for IPL"); IPL_assert(bip->dev_type == BOOT_INFO_DEV_TYPE_ECKD, "DASD is not ECKD"); @@ -315,6 +315,40 @@ static void print_eckd_msg(void) sclp_print(msg); } +static void ipl_eckd(void) +{ + ScsiMbr *mbr = (void *)sec; + LDL_VTOC *vlbl = (void *)sec; + + print_eckd_msg(); + + /* Grab the MBR again */ + memset(sec, FREE_SPACE_FILLER, sizeof(sec)); + read_block(0, mbr, "Cannot read block 0 on DASD"); + + if (magic_match(mbr->magic, IPL1_MAGIC)) { + ipl_eckd_cdl(); /* no return */ + } + + /* LDL/CMS? */ + memset(sec, FREE_SPACE_FILLER, sizeof(sec)); + read_block(2, vlbl, "Cannot read block 2"); + + if (magic_match(vlbl->magic, CMS1_MAGIC)) { + ipl_eckd_ldl(ECKD_CMS); /* no return */ + } + if (magic_match(vlbl->magic, LNX1_MAGIC)) { + ipl_eckd_ldl(ECKD_LDL); /* no return */ + } + + ipl_eckd_ldl(ECKD_LDL_UNLABELED); /* it still may return */ + /* + * Ok, it is not a LDL by any means. + * It still might be a CDL with zero record keys for IPL1 and IPL2 + */ + ipl_eckd_cdl(); +} + /*********************************************************************** * IPL a SCSI disk */ @@ -382,7 +416,7 @@ static void zipl_run(ScsiBlockPtr *pte) read_block(pte->blockno, tmp_sec, "Cannot read header"); header = (ComponentHeader *)tmp_sec; - IPL_assert(magic_match(tmp_sec, ZIPL_MAGIC), "No zIPL magic"); + IPL_assert(magic_match(tmp_sec, ZIPL_MAGIC), "No zIPL magic in header"); IPL_assert(header->type == ZIPL_COMP_HEADER_IPL, "Bad header type"); dputs("start loading images\n"); @@ -412,19 +446,29 @@ static void ipl_scsi(void) const int pte_len = sizeof(ScsiBlockPtr); ScsiBlockPtr *prog_table_entry; - /* The 0-th block (MBR) was already read into sec[] */ + /* Grab the MBR */ + memset(sec, FREE_SPACE_FILLER, sizeof(sec)); + read_block(0, mbr, "Cannot read block 0"); + + if (!magic_match(mbr->magic, ZIPL_MAGIC)) { + return; + } sclp_print("Using SCSI scheme.\n"); + debug_print_int("MBR Version", mbr->version_id); + IPL_check(mbr->version_id == 1, + "Unknown MBR layout version, assuming version 1"); debug_print_int("program table", mbr->blockptr.blockno); + IPL_assert(mbr->blockptr.blockno, "No Program Table"); /* Parse the program table */ read_block(mbr->blockptr.blockno, sec, "Error reading Program Table"); - IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic"); + IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT"); ns_end = sec + virtio_get_block_size(); - for (ns = (sec + pte_len); (ns + pte_len) < ns_end; ns++) { + for (ns = (sec + pte_len); (ns + pte_len) < ns_end; ns += pte_len) { prog_table_entry = (ScsiBlockPtr *)ns; if (!prog_table_entry->blockno) { break; @@ -445,51 +489,258 @@ static void ipl_scsi(void) } /*********************************************************************** - * IPL starts here + * IPL El Torito ISO9660 image or DVD */ -void zipl_load(void) +static bool is_iso_bc_entry_compatible(IsoBcSection *s) { - ScsiMbr *mbr = (void *)sec; - LDL_VTOC *vlbl = (void *)sec; + uint8_t *magic_sec = (uint8_t *)(sec + ISO_SECTOR_SIZE); - /* Grab the MBR */ - memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(0, mbr, "Cannot read block 0"); + if (s->unused || !s->sector_count) { + return false; + } + read_iso_sector(bswap32(s->load_rba), magic_sec, + "Failed to read image sector 0"); - dputs("checking magic\n"); + /* Checking bytes 8 - 32 for S390 Linux magic */ + return !_memcmp(magic_sec + 8, linux_s390_magic, 24); +} + +/* Location of the current sector of the directory */ +static uint32_t sec_loc[ISO9660_MAX_DIR_DEPTH]; +/* Offset in the current sector of the directory */ +static uint32_t sec_offset[ISO9660_MAX_DIR_DEPTH]; +/* Remained directory space in bytes */ +static uint32_t dir_rem[ISO9660_MAX_DIR_DEPTH]; - if (magic_match(mbr->magic, ZIPL_MAGIC)) { - ipl_scsi(); /* no return */ +static inline uint32_t iso_get_file_size(uint32_t load_rba) +{ + IsoVolDesc *vd = (IsoVolDesc *)sec; + IsoDirHdr *cur_record = &vd->vd.primary.rootdir; + uint8_t *temp = sec + ISO_SECTOR_SIZE; + int level = 0; + + read_iso_sector(ISO_PRIMARY_VD_SECTOR, sec, + "Failed to read ISO primary descriptor"); + sec_loc[0] = iso_733_to_u32(cur_record->ext_loc); + dir_rem[0] = 0; + sec_offset[0] = 0; + + while (level >= 0) { + IPL_assert(sec_offset[level] <= ISO_SECTOR_SIZE, + "Directory tree structure violation"); + + cur_record = (IsoDirHdr *)(temp + sec_offset[level]); + + if (sec_offset[level] == 0) { + read_iso_sector(sec_loc[level], temp, + "Failed to read ISO directory"); + if (dir_rem[level] == 0) { + /* Skip self and parent records */ + dir_rem[level] = iso_733_to_u32(cur_record->data_len) - + cur_record->dr_len; + sec_offset[level] += cur_record->dr_len; + + cur_record = (IsoDirHdr *)(temp + sec_offset[level]); + dir_rem[level] -= cur_record->dr_len; + sec_offset[level] += cur_record->dr_len; + continue; + } + } + + if (!cur_record->dr_len || sec_offset[level] == ISO_SECTOR_SIZE) { + /* Zero-padding and/or the end of current sector */ + dir_rem[level] -= ISO_SECTOR_SIZE - sec_offset[level]; + sec_offset[level] = 0; + sec_loc[level]++; + } else { + /* The directory record is valid */ + if (load_rba == iso_733_to_u32(cur_record->ext_loc)) { + return iso_733_to_u32(cur_record->data_len); + } + + dir_rem[level] -= cur_record->dr_len; + sec_offset[level] += cur_record->dr_len; + + if (cur_record->file_flags & 0x2) { + /* Subdirectory */ + if (level == ISO9660_MAX_DIR_DEPTH - 1) { + sclp_print("ISO-9660 directory depth limit exceeded\n"); + } else { + level++; + sec_loc[level] = iso_733_to_u32(cur_record->ext_loc); + sec_offset[level] = 0; + dir_rem[level] = 0; + continue; + } + } + } + + if (dir_rem[level] == 0) { + /* Nothing remaining */ + level--; + read_iso_sector(sec_loc[level], temp, + "Failed to read ISO directory"); + } } - /* We have failed to follow the SCSI scheme, so */ - if (virtio_guessed_disk_nature()) { - sclp_print("Using guessed DASD geometry.\n"); - virtio_assume_eckd(); + return 0; +} + +static void load_iso_bc_entry(IsoBcSection *load) +{ + IsoBcSection s = *load; + /* + * According to spec, extent for each file + * is padded and ISO_SECTOR_SIZE bytes aligned + */ + uint32_t blks_to_load = bswap16(s.sector_count) >> ET_SECTOR_SHIFT; + uint32_t real_size = iso_get_file_size(bswap32(s.load_rba)); + + if (real_size) { + /* Round up blocks to load */ + blks_to_load = (real_size + ISO_SECTOR_SIZE - 1) / ISO_SECTOR_SIZE; + sclp_print("ISO boot image size verified\n"); + } else { + sclp_print("ISO boot image size could not be verified\n"); } - print_eckd_msg(); - if (magic_match(mbr->magic, IPL1_MAGIC)) { - ipl_eckd_cdl(); /* no return */ + + read_iso_boot_image(bswap32(s.load_rba), + (void *)((uint64_t)bswap16(s.load_segment)), + blks_to_load); + + /* Trying to get PSW at zero address */ + if (*((uint64_t *)0) & IPL_PSW_MASK) { + jump_to_IPL_code((*((uint64_t *)0)) & 0x7fffffff); } - /* LDL/CMS? */ - memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(2, vlbl, "Cannot read block 2"); + /* Try default linux start address */ + jump_to_IPL_code(KERN_IMAGE_START); +} - if (magic_match(vlbl->magic, CMS1_MAGIC)) { - ipl_eckd_ldl(ECKD_CMS); /* no return */ +static uint32_t find_iso_bc(void) +{ + IsoVolDesc *vd = (IsoVolDesc *)sec; + uint32_t block_num = ISO_PRIMARY_VD_SECTOR; + + if (virtio_read_many(block_num++, sec, 1)) { + /* If primary vd cannot be read, there is no boot catalog */ + return 0; } - if (magic_match(vlbl->magic, LNX1_MAGIC)) { - ipl_eckd_ldl(ECKD_LDL); /* no return */ + + while (is_iso_vd_valid(vd) && vd->type != VOL_DESC_TERMINATOR) { + if (vd->type == VOL_DESC_TYPE_BOOT) { + IsoVdElTorito *et = &vd->vd.boot; + + if (!_memcmp(&et->el_torito[0], el_torito_magic, 32)) { + return bswap32(et->bc_offset); + } + } + read_iso_sector(block_num++, sec, + "Failed to read ISO volume descriptor"); + } + + return 0; +} + +static IsoBcSection *find_iso_bc_entry(void) +{ + IsoBcEntry *e = (IsoBcEntry *)sec; + uint32_t offset = find_iso_bc(); + int i; + + if (!offset) { + return NULL; + } + + read_iso_sector(offset, sec, "Failed to read El Torito boot catalog"); + + if (!is_iso_bc_valid(e)) { + /* The validation entry is mandatory */ + panic("No valid boot catalog found!\n"); + return NULL; } - ipl_eckd_ldl(ECKD_LDL_UNLABELED); /* it still may return */ /* - * Ok, it is not a LDL by any means. - * It still might be a CDL with zero record keys for IPL1 and IPL2 + * Each entry has 32 bytes size, so one sector cannot contain > 64 entries. + * We consider only boot catalogs with no more than 64 entries. */ - ipl_eckd_cdl(); + for (i = 1; i < ISO_BC_ENTRY_PER_SECTOR; i++) { + if (e[i].id == ISO_BC_BOOTABLE_SECTION) { + if (is_iso_bc_entry_compatible(&e[i].body.sect)) { + return &e[i].body.sect; + } + } + } + + panic("No suitable boot entry found on ISO-9660 media!\n"); + + return NULL; +} + +static void ipl_iso_el_torito(void) +{ + IsoBcSection *s = find_iso_bc_entry(); + + if (s) { + load_iso_bc_entry(s); + /* no return */ + } +} + +/*********************************************************************** + * Bus specific IPL sequences + */ + +static void zipl_load_vblk(void) +{ + if (virtio_guessed_disk_nature()) { + virtio_assume_iso9660(); + } + ipl_iso_el_torito(); + + if (virtio_guessed_disk_nature()) { + sclp_print("Using guessed DASD geometry.\n"); + virtio_assume_eckd(); + } + ipl_eckd(); +} + +static void zipl_load_vscsi(void) +{ + if (virtio_get_block_size() == VIRTIO_ISO_BLOCK_SIZE) { + /* Is it an ISO image in non-CD drive? */ + ipl_iso_el_torito(); + } + + sclp_print("Using guessed DASD geometry.\n"); + virtio_assume_eckd(); + ipl_eckd(); +} + +/*********************************************************************** + * IPL starts here + */ + +void zipl_load(void) +{ + if (virtio_get_device()->is_cdrom) { + ipl_iso_el_torito(); + panic("\n! Cannot IPL this ISO image !\n"); + } + + ipl_scsi(); + + switch (virtio_get_device_type()) { + case VIRTIO_ID_BLOCK: + zipl_load_vblk(); + break; + case VIRTIO_ID_SCSI: + zipl_load_vscsi(); + break; + default: + panic("\n! Unknown IPL device type !\n"); + } - virtio_panic("\n* this can never happen *\n"); + panic("\n* this can never happen *\n"); } diff --git a/qemu/pc-bios/s390-ccw/bootmap.h b/qemu/pc-bios/s390-ccw/bootmap.h index ab132e357..bea168714 100644 --- a/qemu/pc-bios/s390-ccw/bootmap.h +++ b/qemu/pc-bios/s390-ccw/bootmap.h @@ -264,15 +264,6 @@ typedef enum { /* utility code below */ -static inline void IPL_assert(bool term, const char *message) -{ - if (!term) { - sclp_print("\n! "); - sclp_print(message); - virtio_panic(" !\n"); /* no return */ - } -} - static const unsigned char ebc2asc[256] = /* 0123456789abcdef0123456789abcdef */ "................................" /* 1F */ @@ -341,4 +332,210 @@ static inline bool magic_match(const void *data, const void *magic) return *((uint32_t *)data) == *((uint32_t *)magic); } +static inline int _memcmp(const void *s1, const void *s2, size_t n) +{ + int i; + const uint8_t *p1 = s1, *p2 = s2; + + for (i = 0; i < n; i++) { + if (p1[i] != p2[i]) { + return p1[i] > p2[i] ? 1 : -1; + } + } + + return 0; +} + +/* from include/qemu/bswap.h */ + +/* El Torito is always little-endian */ +static inline uint16_t bswap16(uint16_t x) +{ + return ((x & 0x00ff) << 8) | ((x & 0xff00) >> 8); +} + +static inline uint32_t bswap32(uint32_t x) +{ + return ((x & 0x000000ffU) << 24) | ((x & 0x0000ff00U) << 8) | + ((x & 0x00ff0000U) >> 8) | ((x & 0xff000000U) >> 24); +} + +static inline uint64_t bswap64(uint64_t x) +{ + return ((x & 0x00000000000000ffULL) << 56) | + ((x & 0x000000000000ff00ULL) << 40) | + ((x & 0x0000000000ff0000ULL) << 24) | + ((x & 0x00000000ff000000ULL) << 8) | + ((x & 0x000000ff00000000ULL) >> 8) | + ((x & 0x0000ff0000000000ULL) >> 24) | + ((x & 0x00ff000000000000ULL) >> 40) | + ((x & 0xff00000000000000ULL) >> 56); +} + +static inline uint32_t iso_733_to_u32(uint64_t x) +{ + return (uint32_t)x; +} + +#define ISO_SECTOR_SIZE 2048 +/* El Torito specifies boot image size in 512 byte blocks */ +#define ET_SECTOR_SHIFT 2 +#define KERN_IMAGE_START 0x010000UL +#define PSW_MASK_64 0x0000000100000000ULL +#define PSW_MASK_32 0x0000000080000000ULL +#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) + +#define ISO_PRIMARY_VD_SECTOR 16 + +static inline void read_iso_sector(uint32_t block_offset, void *buf, + const char *errmsg) +{ + IPL_assert(virtio_read_many(block_offset, buf, 1) == 0, errmsg); +} + +static inline void read_iso_boot_image(uint32_t block_offset, void *load_addr, + uint32_t blks_to_load) +{ + IPL_assert(virtio_read_many(block_offset, load_addr, blks_to_load) == 0, + "Failed to read boot image!"); +} + +const uint8_t el_torito_magic[] = "EL TORITO SPECIFICATION" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + +#define ISO9660_MAX_DIR_DEPTH 8 + +typedef struct IsoDirHdr { + uint8_t dr_len; + uint8_t ear_len; + uint64_t ext_loc; + uint64_t data_len; + uint8_t recording_datetime[7]; + uint8_t file_flags; + uint8_t file_unit_size; + uint8_t gap_size; + uint32_t vol_seqnum; + uint8_t fileid_len; +} __attribute__((packed)) IsoDirHdr; + +typedef struct IsoVdElTorito { + uint8_t el_torito[32]; /* must contain el_torito_magic value */ + uint8_t unused0[32]; + uint32_t bc_offset; + uint8_t unused1[1974]; +} __attribute__((packed)) IsoVdElTorito; + +typedef struct IsoVdPrimary { + uint8_t unused1; + uint8_t sys_id[32]; + uint8_t vol_id[32]; + uint8_t unused2[8]; + uint64_t vol_space_size; + uint8_t unused3[32]; + uint32_t vol_set_size; + uint32_t vol_seqnum; + uint32_t log_block_size; + uint64_t path_table_size; + uint32_t l_path_table; + uint32_t opt_l_path_table; + uint32_t m_path_table; + uint32_t opt_m_path_table; + IsoDirHdr rootdir; + uint8_t root_null; + uint8_t reserved2[1858]; +} __attribute__((packed)) IsoVdPrimary; + +typedef struct IsoVolDesc { + uint8_t type; + uint8_t ident[5]; + uint8_t version; + union { + IsoVdElTorito boot; + IsoVdPrimary primary; + } vd; +} __attribute__((packed)) IsoVolDesc; + +const uint8_t vol_desc_magic[] = "CD001"; +#define VOL_DESC_TYPE_BOOT 0 +#define VOL_DESC_TYPE_PRIMARY 1 +#define VOL_DESC_TYPE_SUPPLEMENT 2 +#define VOL_DESC_TYPE_PARTITION 3 +#define VOL_DESC_TERMINATOR 255 + +static inline bool is_iso_vd_valid(IsoVolDesc *vd) +{ + return !_memcmp(&vd->ident[0], vol_desc_magic, 5) && + vd->version == 0x1 && + vd->type <= VOL_DESC_TYPE_PARTITION; +} + +typedef struct IsoBcValid { + uint8_t platform_id; + uint16_t reserved; + uint8_t id[24]; + uint16_t checksum; + uint8_t key[2]; +} __attribute__((packed)) IsoBcValid; + +typedef struct IsoBcSection { + uint8_t boot_type; + uint16_t load_segment; + uint8_t sys_type; + uint8_t unused; + uint16_t sector_count; + uint32_t load_rba; + uint8_t selection[20]; +} __attribute__((packed)) IsoBcSection; + +typedef struct IsoBcHdr { + uint8_t platform_id; + uint16_t sect_num; + uint8_t id[28]; +} __attribute__((packed)) IsoBcHdr; + +/* + * Match two CCWs located after PSW and eight filler bytes. + * From libmagic and arch/s390/kernel/head.S. + */ +const uint8_t linux_s390_magic[] = "\x02\x00\x00\x18\x60\x00\x00\x50\x02\x00" + "\x00\x68\x60\x00\x00\x50\x40\x40\x40\x40" + "\x40\x40\x40\x40"; + +typedef struct IsoBcEntry { + uint8_t id; + union { + IsoBcValid valid; /* id == 0x01 */ + IsoBcSection sect; /* id == 0x88 || id == 0x0 */ + IsoBcHdr hdr; /* id == 0x90 || id == 0x91 */ + } body; +} __attribute__((packed)) IsoBcEntry; + +#define ISO_BC_ENTRY_PER_SECTOR (ISO_SECTOR_SIZE / sizeof(IsoBcEntry)) +#define ISO_BC_HDR_VALIDATION 0x01 +#define ISO_BC_BOOTABLE_SECTION 0x88 +#define ISO_BC_MAGIC_55 0x55 +#define ISO_BC_MAGIC_AA 0xaa +#define ISO_BC_PLATFORM_X86 0x0 +#define ISO_BC_PLATFORM_PPC 0x1 +#define ISO_BC_PLATFORM_MAC 0x2 + +static inline bool is_iso_bc_valid(IsoBcEntry *e) +{ + IsoBcValid *v = &e->body.valid; + + if (e->id != ISO_BC_HDR_VALIDATION) { + return false; + } + + if (v->platform_id != ISO_BC_PLATFORM_X86 && + v->platform_id != ISO_BC_PLATFORM_PPC && + v->platform_id != ISO_BC_PLATFORM_MAC) { + return false; + } + + return v->key[0] == ISO_BC_MAGIC_55 && + v->key[1] == ISO_BC_MAGIC_AA && + v->reserved == 0x0; +} + #endif /* _PC_BIOS_S390_CCW_BOOTMAP_H */ diff --git a/qemu/pc-bios/s390-ccw/main.c b/qemu/pc-bios/s390-ccw/main.c index 584d4a276..1c9e0791a 100644 --- a/qemu/pc-bios/s390-ccw/main.c +++ b/qemu/pc-bios/s390-ccw/main.c @@ -12,9 +12,8 @@ #include "virtio.h" char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); -char ring_area[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); uint64_t boot_value; -static struct subchannel_id blk_schid = { .one = 1 }; +static SubChannelId blk_schid = { .one = 1 }; /* * Priniciples of Operations (SA22-7832-09) chapter 17 requires that @@ -23,7 +22,7 @@ static struct subchannel_id blk_schid = { .one = 1 }; */ void write_subsystem_identification(void) { - struct subchannel_id *schid = (struct subchannel_id *) 184; + SubChannelId *schid = (SubChannelId *) 184; uint32_t *zeroes = (uint32_t *) 188; *schid = blk_schid; @@ -31,60 +30,72 @@ void write_subsystem_identification(void) } -void virtio_panic(const char *string) +void panic(const char *string) { sclp_print(string); disabled_wait(); while (1) { } } +static bool find_dev(Schib *schib, int dev_no) +{ + int i, r; + + for (i = 0; i < 0x10000; i++) { + blk_schid.sch_no = i; + r = stsch_err(blk_schid, schib); + if ((r == 3) || (r == -EIO)) { + break; + } + if (!schib->pmcw.dnv) { + continue; + } + if (!virtio_is_supported(blk_schid)) { + continue; + } + if ((dev_no < 0) || (schib->pmcw.dev == dev_no)) { + return true; + } + } + + return false; +} + static void virtio_setup(uint64_t dev_info) { - struct schib schib; - int i; - int r; + Schib schib; + int ssid; bool found = false; - bool check_devno = false; - uint16_t dev_no = -1; + uint16_t dev_no; + + /* + * We unconditionally enable mss support. In every sane configuration, + * this will succeed; and even if it doesn't, stsch_err() can deal + * with the consequences. + */ + enable_mss_facility(); if (dev_info != -1) { - check_devno = true; dev_no = dev_info & 0xffff; debug_print_int("device no. ", dev_no); blk_schid.ssid = (dev_info >> 16) & 0x3; - if (blk_schid.ssid != 0) { - debug_print_int("ssid ", blk_schid.ssid); - if (enable_mss_facility() != 0) { - virtio_panic("Failed to enable mss facility\n"); - } - } - } - - for (i = 0; i < 0x10000; i++) { - blk_schid.sch_no = i; - r = stsch_err(blk_schid, &schib); - if (r == 3) { - break; - } - if (schib.pmcw.dnv) { - if (!check_devno || (schib.pmcw.dev == dev_no)) { - if (virtio_is_blk(blk_schid)) { - found = true; - break; - } + debug_print_int("ssid ", blk_schid.ssid); + found = find_dev(&schib, dev_no); + } else { + for (ssid = 0; ssid < 0x3; ssid++) { + blk_schid.ssid = ssid; + found = find_dev(&schib, -1); + if (found) { + break; } } } - if (!found) { - virtio_panic("No virtio-blk device found!\n"); - } + IPL_assert(found, "No virtio device found"); - virtio_setup_block(blk_schid); + virtio_setup_device(blk_schid); - if (!virtio_ipl_disk_is_valid()) { - virtio_panic("No valid hard disk detected.\n"); - } + IPL_assert(virtio_ipl_disk_is_valid(), "No valid IPL device detected"); } int main(void) @@ -95,6 +106,6 @@ int main(void) zipl_load(); /* no return */ - virtio_panic("Failed to load OS from hard disk\n"); + panic("Failed to load OS from hard disk\n"); return 0; /* make compiler happy */ } diff --git a/qemu/pc-bios/s390-ccw/s390-ccw.h b/qemu/pc-bios/s390-ccw/s390-ccw.h index 5484c2a45..616d96738 100644 --- a/qemu/pc-bios/s390-ccw/s390-ccw.h +++ b/qemu/pc-bios/s390-ccw/s390-ccw.h @@ -45,15 +45,22 @@ typedef unsigned long long __u64; #include "cio.h" +typedef struct irb Irb; +typedef struct ccw1 Ccw1; +typedef struct cmd_orb CmdOrb; +typedef struct schib Schib; +typedef struct chsc_area_sda ChscAreaSda; +typedef struct senseid SenseId; +typedef struct subchannel_id SubChannelId; + /* start.s */ void disabled_wait(void); void consume_sclp_int(void); /* main.c */ -void virtio_panic(const char *string); +void panic(const char *string); void write_subsystem_identification(void); extern char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); -extern char ring_area[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); extern uint64_t boot_value; /* sclp-ascii.c */ @@ -63,10 +70,11 @@ void sclp_setup(void); /* virtio.c */ unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, ulong subchan_id, void *load_addr); -bool virtio_is_blk(struct subchannel_id schid); -void virtio_setup_block(struct subchannel_id schid); +bool virtio_is_supported(SubChannelId schid); +void virtio_setup_device(SubChannelId schid); int virtio_read(ulong sector, void *load_addr); int enable_mss_facility(void); +ulong get_second(void); /* bootmap.c */ void zipl_load(void); @@ -143,4 +151,42 @@ static inline void yield(void) #define MAX_SECTOR_SIZE 4096 +static inline void sleep(unsigned int seconds) +{ + ulong target = get_second() + seconds; + + while (get_second() < target) { + yield(); + } +} + +static inline void *memcpy(void *s1, const void *s2, size_t n) +{ + uint8_t *p1 = s1; + const uint8_t *p2 = s2; + + while (n--) { + p1[n] = p2[n]; + } + return s1; +} + +static inline void IPL_assert(bool term, const char *message) +{ + if (!term) { + sclp_print("\n! "); + sclp_print(message); + panic(" !\n"); /* no return */ + } +} + +static inline void IPL_check(bool term, const char *message) +{ + if (!term) { + sclp_print("\n! WARNING: "); + sclp_print(message); + sclp_print(" !\n"); + } +} + #endif /* S390_CCW_H */ diff --git a/qemu/pc-bios/s390-ccw/scsi.h b/qemu/pc-bios/s390-ccw/scsi.h new file mode 100644 index 000000000..fc830f7e5 --- /dev/null +++ b/qemu/pc-bios/s390-ccw/scsi.h @@ -0,0 +1,184 @@ +/* + * SCSI definitions for s390 machine loader for qemu + * + * Copyright 2015 IBM Corp. + * Author: Eugene "jno" Dvurechenski + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#ifndef SCSI_H +#define SCSI_H + +#include "s390-ccw.h" + +#define SCSI_DEFAULT_CDB_SIZE 32 +#define SCSI_DEFAULT_SENSE_SIZE 96 + +#define CDB_STATUS_GOOD 0 +#define CDB_STATUS_CHECK_CONDITION 0x02U +#define CDB_STATUS_VALID(status) (((status) & ~0x3eU) == 0) + +#define SCSI_SENSE_CODE_MASK 0x7fU +#define SCSI_SENSE_KEY_MASK 0x0fU +#define SCSI_SENSE_KEY_NO_SENSE 0 +#define SCSI_SENSE_KEY_UNIT_ATTENTION 6 + +union ScsiLun { + uint64_t v64; /* numeric shortcut */ + uint8_t v8[8]; /* generic 8 bytes representation */ + uint16_t v16[4]; /* 4-level big-endian LUN as specified by SAM-2 */ +}; +typedef union ScsiLun ScsiLun; + +struct ScsiSense70 { + uint8_t b0; /* b0 & 7f = resp code (0x70 or 0x71) */ + uint8_t b1, b2; /* b2 & 0f = sense key */ + uint8_t u1[1 * 4 + 1 + 1 * 4]; /* b7 = N - 7 */ + uint8_t additional_sense_code; /* b12 */ + uint8_t additional_sense_code_qualifier; /* b13 */ + uint8_t u2[1 + 3 + 0]; /* up to N (<=252) bytes */ +} __attribute__((packed)); +typedef struct ScsiSense70 ScsiSense70; + +/* don't confuse with virtio-scsi response/status fields! */ + +static inline uint8_t scsi_sense_response(const void *p) +{ + return ((const ScsiSense70 *)p)->b0 & SCSI_SENSE_CODE_MASK; +} + +static inline uint8_t scsi_sense_key(const void *p) +{ + return ((const ScsiSense70 *)p)->b2 & SCSI_SENSE_KEY_MASK; +} + +#define SCSI_INQ_RDT_CDROM 0x05 + +struct ScsiInquiryStd { + uint8_t peripheral_qdt; /* b0, use (b0 & 0x1f) to get SCSI_INQ_RDT */ + uint8_t b1; /* Removable Media Bit = b1 & 0x80 */ + uint8_t spc_version; /* b2 */ + uint8_t b3; /* b3 & 0x0f == resp_data_fmt == 2, must! */ + uint8_t u1[1 + 1 + 1 + 1 + 8]; /* b4..b15 unused, b4 = (N - 1) */ + char prod_id[16]; /* "QEMU CD-ROM" is here */ + uint8_t u2[4 /* b32..b35 unused, mandatory */ + + 8 + 12 + 1 + 1 + 8 * 2 + 22 /* b36..95 unused, optional*/ + + 0]; /* b96..bN unused, vendor specific */ + /* byte N */ +} __attribute__((packed)); +typedef struct ScsiInquiryStd ScsiInquiryStd; + +struct ScsiCdbInquiry { + uint8_t command; /* b0, == 0x12 */ + uint8_t b1; /* b1, |= 0x01 (evpd) */ + uint8_t b2; /* b2; if evpd==1 */ + uint16_t alloc_len; /* b3, b4 */ + uint8_t control; /* b5 */ +} __attribute__((packed)); +typedef struct ScsiCdbInquiry ScsiCdbInquiry; + +struct ScsiCdbRead10 { + uint8_t command; /* =0x28 */ + uint8_t b1; + uint32_t lba; + uint8_t b6; + uint16_t xfer_length; + uint8_t control; +} __attribute__((packed)); +typedef struct ScsiCdbRead10 ScsiCdbRead10; + +struct ScsiCdbTestUnitReady { + uint8_t command; /* =0x00 */ + uint8_t b1_b4[4]; + uint8_t control; +} __attribute__((packed)); +typedef struct ScsiCdbTestUnitReady ScsiCdbTestUnitReady; + +struct ScsiCdbReportLuns { + uint8_t command; /* =0xa0 */ + uint8_t b1; + uint8_t select_report; /* =0x02, "all" */ + uint8_t b3_b5[3]; + uint32_t alloc_len; + uint8_t b10; + uint8_t control; +} __attribute__((packed)); +typedef struct ScsiCdbReportLuns ScsiCdbReportLuns; + +struct ScsiLunReport { + uint32_t lun_list_len; + uint32_t b4_b7; + ScsiLun lun[1]; /* space for at least 1 lun must be allocated */ +} __attribute__((packed)); +typedef struct ScsiLunReport ScsiLunReport; + +struct ScsiCdbReadCapacity16 { + uint8_t command; /* =0x9e = "service action in 16" */ + uint8_t service_action; /* 5 bits, =0x10 = "read capacity 16" */ + uint64_t b2_b9; + uint32_t alloc_len; + uint8_t b14; + uint8_t control; +} __attribute__((packed)); +typedef struct ScsiCdbReadCapacity16 ScsiCdbReadCapacity16; + +struct ScsiReadCapacity16Data { + uint64_t ret_lba; /* get it, 0..7 */ + uint32_t lb_len; /* bytes, 8..11 */ + uint8_t u1[2 + 1 * 2 + 16]; /* b12..b31, unused */ +} __attribute__((packed)); +typedef struct ScsiReadCapacity16Data ScsiReadCapacity16Data; + +static inline ScsiLun make_lun(uint16_t channel, uint16_t target, uint32_t lun) +{ + ScsiLun r = { .v64 = 0 }; + + /* See QEMU code to choose the way to handle LUNs. + * + * So, a valid LUN must have (always channel #0): + * lun[0] == 1 + * lun[1] - target, any value + * lun[2] == 0 or (LUN, MSB, 0x40 set, 0x80 clear) + * lun[3] - LUN, LSB, any value + */ + r.v8[0] = 1; + r.v8[1] = target & 0xffU; + r.v8[2] = (lun >> 8) & 0x3fU; + if (r.v8[2]) { + r.v8[2] |= 0x40; + } + r.v8[3] = lun & 0xffU; + + return r; +} + +static inline const char *scsi_cdb_status_msg(uint8_t status) +{ + static char err_msg[] = "STATUS=XX"; + uint8_t v = status & 0x3eU; + + fill_hex_val(err_msg + 7, &v, 1); + return err_msg; +} + +static inline const char *scsi_cdb_asc_msg(const void *s) +{ + static char err_msg[] = "RSPN=XX KEY=XX CODE=XX QLFR=XX"; + const ScsiSense70 *p = s; + uint8_t sr = scsi_sense_response(s); + uint8_t sk = scsi_sense_key(s); + uint8_t ac = p->additional_sense_code; + uint8_t cq = p->additional_sense_code_qualifier; + + fill_hex_val(err_msg + 5, &sr, 1); + fill_hex_val(err_msg + 12, &sk, 1); + fill_hex_val(err_msg + 20, &ac, 1); + fill_hex_val(err_msg + 28, &cq, 1); + + return err_msg; +} + +#endif /* SCSI_H */ diff --git a/qemu/pc-bios/s390-ccw/virtio-scsi.c b/qemu/pc-bios/s390-ccw/virtio-scsi.c new file mode 100644 index 000000000..3bb48e917 --- /dev/null +++ b/qemu/pc-bios/s390-ccw/virtio-scsi.c @@ -0,0 +1,342 @@ +/* + * Virtio-SCSI implementation for s390 machine loader for qemu + * + * Copyright 2015 IBM Corp. + * Author: Eugene "jno" Dvurechenski + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include "s390-ccw.h" +#include "virtio.h" +#include "scsi.h" +#include "virtio-scsi.h" + +static ScsiDevice default_scsi_device; +static VirtioScsiCmdReq req; +static VirtioScsiCmdResp resp; + +static uint8_t scsi_inquiry_std_response[256]; + +static inline void vs_assert(bool term, const char **msgs) +{ + if (!term) { + int i = 0; + + sclp_print("\n! "); + while (msgs[i]) { + sclp_print(msgs[i++]); + } + panic(" !\n"); + } +} + +static void virtio_scsi_verify_response(VirtioScsiCmdResp *resp, + const char *title) +{ + const char *mr[] = { + title, ": response ", virtio_scsi_response_msg(resp), 0 + }; + const char *ms[] = { + title, + CDB_STATUS_VALID(resp->status) ? ": " : ": invalid ", + scsi_cdb_status_msg(resp->status), + resp->status == CDB_STATUS_CHECK_CONDITION ? " " : 0, + resp->sense_len ? scsi_cdb_asc_msg(resp->sense) + : "no sense data", + scsi_sense_response(resp->sense) == 0x70 ? ", sure" : "?", + 0 + }; + + vs_assert(resp->response == VIRTIO_SCSI_S_OK, mr); + vs_assert(resp->status == CDB_STATUS_GOOD, ms); +} + +static void prepare_request(VDev *vdev, const void *cdb, int cdb_size, + void *data, uint32_t data_size) +{ + const ScsiDevice *sdev = vdev->scsi_device; + + memset(&req, 0, sizeof(req)); + req.lun = make_lun(sdev->channel, sdev->target, sdev->lun); + memcpy(&req.cdb, cdb, cdb_size); + + memset(&resp, 0, sizeof(resp)); + resp.status = 0xff; /* set invalid */ + resp.response = 0xff; /* */ + + if (data && data_size) { + memset(data, 0, data_size); + } +} + +static inline void vs_io_assert(bool term, const char *msg) +{ + if (!term) { + virtio_scsi_verify_response(&resp, msg); + } +} + +static void vs_run(const char *title, VirtioCmd *cmd, VDev *vdev, + const void *cdb, int cdb_size, + void *data, uint32_t data_size) +{ + prepare_request(vdev, cdb, cdb_size, data, data_size); + vs_io_assert(virtio_run(vdev, VR_REQUEST, cmd) == 0, title); +} + +/* SCSI protocol implementation routines */ + +static bool scsi_inquiry(VDev *vdev, void *data, uint32_t data_size) +{ + ScsiCdbInquiry cdb = { + .command = 0x12, + .alloc_len = data_size < 65535 ? data_size : 65535, + }; + VirtioCmd inquiry[] = { + { &req, sizeof(req), VRING_DESC_F_NEXT }, + { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, + { data, data_size, VRING_DESC_F_WRITE }, + }; + + vs_run("inquiry", inquiry, vdev, &cdb, sizeof(cdb), data, data_size); + + return virtio_scsi_response_ok(&resp); +} + +static bool scsi_test_unit_ready(VDev *vdev) +{ + ScsiCdbTestUnitReady cdb = { + .command = 0x00, + }; + VirtioCmd test_unit_ready[] = { + { &req, sizeof(req), VRING_DESC_F_NEXT }, + { &resp, sizeof(resp), VRING_DESC_F_WRITE }, + }; + + prepare_request(vdev, &cdb, sizeof(cdb), 0, 0); + virtio_run(vdev, VR_REQUEST, test_unit_ready); /* ignore errors here */ + + return virtio_scsi_response_ok(&resp); +} + +static bool scsi_report_luns(VDev *vdev, void *data, uint32_t data_size) +{ + ScsiCdbReportLuns cdb = { + .command = 0xa0, + .select_report = 0x02, /* REPORT ALL */ + .alloc_len = data_size, + }; + VirtioCmd report_luns[] = { + { &req, sizeof(req), VRING_DESC_F_NEXT }, + { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, + { data, data_size, VRING_DESC_F_WRITE }, + }; + + vs_run("report luns", report_luns, + vdev, &cdb, sizeof(cdb), data, data_size); + + return virtio_scsi_response_ok(&resp); +} + +static bool scsi_read_10(VDev *vdev, + ulong sector, int sectors, void *data) +{ + int f = vdev->blk_factor; + unsigned int data_size = sectors * virtio_get_block_size() * f; + ScsiCdbRead10 cdb = { + .command = 0x28, + .lba = sector * f, + .xfer_length = sectors * f, + }; + VirtioCmd read_10[] = { + { &req, sizeof(req), VRING_DESC_F_NEXT }, + { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, + { data, data_size * f, VRING_DESC_F_WRITE }, + }; + + debug_print_int("read_10 sector", sector); + debug_print_int("read_10 sectors", sectors); + + vs_run("read(10)", read_10, vdev, &cdb, sizeof(cdb), data, data_size); + + return virtio_scsi_response_ok(&resp); +} + +static bool scsi_read_capacity(VDev *vdev, + void *data, uint32_t data_size) +{ + ScsiCdbReadCapacity16 cdb = { + .command = 0x9e, /* SERVICE_ACTION_IN_16 */ + .service_action = 0x10, /* SA_READ_CAPACITY */ + .alloc_len = data_size, + }; + VirtioCmd read_capacity_16[] = { + { &req, sizeof(req), VRING_DESC_F_NEXT }, + { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, + { data, data_size, VRING_DESC_F_WRITE }, + }; + + vs_run("read capacity", read_capacity_16, + vdev, &cdb, sizeof(cdb), data, data_size); + + return virtio_scsi_response_ok(&resp); +} + +/* virtio-scsi routines */ + +static void virtio_scsi_locate_device(VDev *vdev) +{ + const uint16_t channel = 0; /* again, it's what QEMU does */ + uint16_t target; + static uint8_t data[16 + 8 * 63]; + ScsiLunReport *r = (void *) data; + ScsiDevice *sdev = vdev->scsi_device; + int i, luns; + + /* QEMU has hardcoded channel #0 in many places. + * If this hardcoded value is ever changed, we'll need to add code for + * vdev->config.scsi.max_channel != 0 here. + */ + debug_print_int("config.scsi.max_channel", vdev->config.scsi.max_channel); + debug_print_int("config.scsi.max_target ", vdev->config.scsi.max_target); + debug_print_int("config.scsi.max_lun ", vdev->config.scsi.max_lun); + + for (target = 0; target <= vdev->config.scsi.max_target; target++) { + sdev->channel = channel; + sdev->target = target; /* sdev->lun will be 0 here */ + if (!scsi_report_luns(vdev, data, sizeof(data))) { + if (resp.response == VIRTIO_SCSI_S_BAD_TARGET) { + continue; + } + print_int("target", target); + virtio_scsi_verify_response(&resp, "SCSI cannot report LUNs"); + } + if (r->lun_list_len == 0) { + print_int("no LUNs for target", target); + continue; + } + luns = r->lun_list_len / 8; + debug_print_int("LUNs reported", luns); + if (luns == 1) { + /* There is no ",lun=#" arg for -device or ",lun=0" given. + * Hence, the only LUN reported. + * Usually, it's 0. + */ + sdev->lun = r->lun[0].v16[0]; /* it's returned this way */ + debug_print_int("Have to use LUN", sdev->lun); + return; /* we have to use this device */ + } + for (i = 0; i < luns; i++) { + if (r->lun[i].v64) { + /* Look for non-zero LUN - we have where to choose from */ + sdev->lun = r->lun[i].v16[0]; + debug_print_int("Will use LUN", sdev->lun); + return; /* we have found a device */ + } + } + } + panic("\n! Cannot locate virtio-scsi device !\n"); +} + +int virtio_scsi_read_many(VDev *vdev, + ulong sector, void *load_addr, int sec_num) +{ + if (!scsi_read_10(vdev, sector, sec_num, load_addr)) { + virtio_scsi_verify_response(&resp, "virtio-scsi:read_many"); + } + + return 0; +} + +static bool virtio_scsi_inquiry_response_is_cdrom(void *data) +{ + const ScsiInquiryStd *response = data; + const int resp_data_fmt = response->b3 & 0x0f; + int i; + + IPL_check(resp_data_fmt == 2, "Wrong INQUIRY response format"); + if (resp_data_fmt != 2) { + return false; /* cannot decode */ + } + + if ((response->peripheral_qdt & 0x1f) == SCSI_INQ_RDT_CDROM) { + return true; + } + + for (i = 0; i < sizeof(response->prod_id); i++) { + if (response->prod_id[i] != QEMU_CDROM_SIGNATURE[i]) { + return false; + } + } + return true; +} + +static void scsi_parse_capacity_report(void *data, + uint64_t *last_lba, uint32_t *lb_len) +{ + ScsiReadCapacity16Data *p = data; + + if (last_lba) { + *last_lba = p->ret_lba; + } + + if (lb_len) { + *lb_len = p->lb_len; + } +} + +void virtio_scsi_setup(VDev *vdev) +{ + int retry_test_unit_ready = 3; + uint8_t data[256]; + uint32_t data_size = sizeof(data); + + vdev->scsi_device = &default_scsi_device; + virtio_scsi_locate_device(vdev); + + /* We have to "ping" the device before it becomes readable */ + while (!scsi_test_unit_ready(vdev)) { + + if (!virtio_scsi_response_ok(&resp)) { + uint8_t code = resp.sense[0] & SCSI_SENSE_CODE_MASK; + uint8_t sense_key = resp.sense[2] & SCSI_SENSE_KEY_MASK; + + IPL_assert(resp.sense_len != 0, "virtio-scsi:setup: no SENSE data"); + + IPL_assert(retry_test_unit_ready && code == 0x70 && + sense_key == SCSI_SENSE_KEY_UNIT_ATTENTION, + "virtio-scsi:setup: cannot retry"); + + /* retry on CHECK_CONDITION/UNIT_ATTENTION as it + * may not designate a real error, but it may be + * a result of device reset, etc. + */ + retry_test_unit_ready--; + sleep(1); + continue; + } + + virtio_scsi_verify_response(&resp, "virtio-scsi:setup"); + } + + /* read and cache SCSI INQUIRY response */ + if (!scsi_inquiry(vdev, scsi_inquiry_std_response, + sizeof(scsi_inquiry_std_response))) { + virtio_scsi_verify_response(&resp, "virtio-scsi:setup:inquiry"); + } + + if (virtio_scsi_inquiry_response_is_cdrom(scsi_inquiry_std_response)) { + sclp_print("SCSI CD-ROM detected.\n"); + vdev->is_cdrom = true; + vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE; + } + + if (!scsi_read_capacity(vdev, data, data_size)) { + virtio_scsi_verify_response(&resp, "virtio-scsi:setup:read_capacity"); + } + scsi_parse_capacity_report(data, &vdev->scsi_last_block, + (uint32_t *) &vdev->scsi_block_size); +} diff --git a/qemu/pc-bios/s390-ccw/virtio-scsi.h b/qemu/pc-bios/s390-ccw/virtio-scsi.h new file mode 100644 index 000000000..f50b38b18 --- /dev/null +++ b/qemu/pc-bios/s390-ccw/virtio-scsi.h @@ -0,0 +1,72 @@ +/* + * Virtio-SCSI definitions for s390 machine loader for qemu + * + * Copyright 2015 IBM Corp. + * Author: Eugene "jno" Dvurechenski + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#ifndef VIRTIO_SCSI_H +#define VIRTIO_SCSI_H + +#include "s390-ccw.h" +#include "virtio.h" +#include "scsi.h" + +#define VIRTIO_SCSI_CDB_SIZE SCSI_DEFAULT_CDB_SIZE +#define VIRTIO_SCSI_SENSE_SIZE SCSI_DEFAULT_SENSE_SIZE + +/* command-specific response values */ +#define VIRTIO_SCSI_S_OK 0x00 +#define VIRTIO_SCSI_S_BAD_TARGET 0x03 + +#define QEMU_CDROM_SIGNATURE "QEMU CD-ROM " + +enum virtio_scsi_vq_id { + VR_CONTROL = 0, + VR_EVENT = 1, + VR_REQUEST = 2, +}; + +struct VirtioScsiCmdReq { + ScsiLun lun; + uint64_t id; + uint8_t task_attr; /* = 0 = VIRTIO_SCSI_S_SIMPLE */ + uint8_t prio; + uint8_t crn; /* = 0 */ + uint8_t cdb[VIRTIO_SCSI_CDB_SIZE]; +} __attribute__((packed)); +typedef struct VirtioScsiCmdReq VirtioScsiCmdReq; + +struct VirtioScsiCmdResp { + uint32_t sense_len; + uint32_t residual; + uint16_t status_qualifier; + uint8_t status; /* first check for .response */ + uint8_t response; /* then for .status */ + uint8_t sense[VIRTIO_SCSI_SENSE_SIZE]; +} __attribute__((packed)); +typedef struct VirtioScsiCmdResp VirtioScsiCmdResp; + +static inline const char *virtio_scsi_response_msg(const VirtioScsiCmdResp *r) +{ + static char err_msg[] = "VS RESP=XX"; + uint8_t v = r->response; + + fill_hex_val(err_msg + 8, &v, 1); + return err_msg; +} + +static inline bool virtio_scsi_response_ok(const VirtioScsiCmdResp *r) +{ + return r->response == VIRTIO_SCSI_S_OK && r->status == CDB_STATUS_GOOD; +} + +void virtio_scsi_setup(VDev *vdev); +int virtio_scsi_read_many(VDev *vdev, + ulong sector, void *load_addr, int sec_num); + +#endif /* VIRTIO_SCSI_H */ diff --git a/qemu/pc-bios/s390-ccw/virtio.c b/qemu/pc-bios/s390-ccw/virtio.c index 57ff1b07e..1d34e8c1a 100644 --- a/qemu/pc-bios/s390-ccw/virtio.c +++ b/qemu/pc-bios/s390-ccw/virtio.c @@ -10,39 +10,68 @@ #include "s390-ccw.h" #include "virtio.h" +#include "virtio-scsi.h" -static struct vring block; +#define VRING_WAIT_REPLY_TIMEOUT 3 + +static VRing block[VIRTIO_MAX_VQS]; +static char ring_area[VIRTIO_RING_SIZE * VIRTIO_MAX_VQS] + __attribute__((__aligned__(PAGE_SIZE))); static char chsc_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); +static VDev vdev = { + .nr_vqs = 1, + .vrings = block, + .cmd_vr_idx = 0, + .ring_area = ring_area, + .wait_reply_timeout = VRING_WAIT_REPLY_TIMEOUT, + .schid = { .one = 1 }, + .scsi_block_size = VIRTIO_SCSI_BLOCK_SIZE, + .blk_factor = 1, +}; + +VDev *virtio_get_device(void) +{ + return &vdev; +} + +VirtioDevType virtio_get_device_type(void) +{ + return vdev.senseid.cu_model; +} + +/* virtio spec v1.0 para 4.3.3.2 */ static long kvm_hypercall(unsigned long nr, unsigned long param1, - unsigned long param2) + unsigned long param2, unsigned long param3) { register ulong r_nr asm("1") = nr; register ulong r_param1 asm("2") = param1; register ulong r_param2 asm("3") = param2; + register ulong r_param3 asm("4") = param3; register long retval asm("2"); asm volatile ("diag 2,4,0x500" : "=d" (retval) - : "d" (r_nr), "0" (r_param1), "r"(r_param2) + : "d" (r_nr), "0" (r_param1), "r"(r_param2), "d"(r_param3) : "memory", "cc"); return retval; } -static void virtio_notify(struct subchannel_id schid) +static long virtio_notify(SubChannelId schid, int vq_idx, long cookie) { - kvm_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, *(u32 *)&schid, 0); + return kvm_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, *(u32 *)&schid, + vq_idx, cookie); } /*********************************************** * Virtio functions * ***********************************************/ -static int drain_irqs(struct subchannel_id schid) +static int drain_irqs(SubChannelId schid) { - struct irb irb = {}; + Irb irb = {}; int r = 0; while (1) { @@ -59,17 +88,17 @@ static int drain_irqs(struct subchannel_id schid) } } -static int run_ccw(struct subchannel_id schid, int cmd, void *ptr, int len) +static int run_ccw(VDev *vdev, int cmd, void *ptr, int len) { - struct ccw1 ccw = {}; - struct cmd_orb orb = {}; - struct schib schib; + Ccw1 ccw = {}; + CmdOrb orb = {}; + Schib schib; int r; /* start command processing */ - stsch_err(schid, &schib); + stsch_err(vdev->schid, &schib); schib.scsw.ctrl = SCSW_FCTL_START_FUNC; - msch(schid, &schib); + msch(vdev->schid, &schib); /* start subchannel command */ orb.fmt = 1; @@ -80,41 +109,29 @@ static int run_ccw(struct subchannel_id schid, int cmd, void *ptr, int len) ccw.cda = (long)ptr; ccw.count = len; - r = ssch(schid, &orb); + r = ssch(vdev->schid, &orb); /* * XXX Wait until device is done processing the CCW. For now we can * assume that a simple tsch will have finished the CCW processing, * but the architecture allows for asynchronous operation */ if (!r) { - r = drain_irqs(schid); + r = drain_irqs(vdev->schid); } return r; } -static void virtio_set_status(struct subchannel_id schid, - unsigned long dev_addr) +static void vring_init(VRing *vr, VqInfo *info) { - unsigned char status = dev_addr; - if (run_ccw(schid, CCW_CMD_WRITE_STATUS, &status, sizeof(status))) { - virtio_panic("Could not write status to host!\n"); - } -} - -static void virtio_reset(struct subchannel_id schid) -{ - run_ccw(schid, CCW_CMD_VDEV_RESET, NULL, 0); -} + void *p = (void *) info->queue; -static void vring_init(struct vring *vr, unsigned int num, void *p, - unsigned long align) -{ debug_print_addr("init p", p); - vr->num = num; + vr->id = info->index; + vr->num = info->num; vr->desc = p; - vr->avail = p + num*sizeof(struct vring_desc); - vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + align-1) - & ~(align - 1)); + vr->avail = p + info->num * sizeof(VRingDesc); + vr->used = (void *)(((unsigned long)&vr->avail->ring[info->num] + + info->align - 1) & ~(info->align - 1)); /* Zero out all relevant field */ vr->avail->flags = 0; @@ -125,16 +142,18 @@ static void vring_init(struct vring *vr, unsigned int num, void *p, vr->used->idx = 0; vr->used_idx = 0; vr->next_idx = 0; + vr->cookie = 0; debug_print_addr("init vr", vr); } -static void vring_notify(struct subchannel_id schid) +static bool vring_notify(VRing *vr) { - virtio_notify(schid); + vr->cookie = virtio_notify(vr->schid, vr->id, vr->cookie); + return vr->cookie >= 0; } -static void vring_send_buf(struct vring *vr, void *p, int len, int flags) +static void vring_send_buf(VRing *vr, void *p, int len, int flags) { /* For follow-up chains we need to keep the first entry point */ if (!(flags & VRING_HIDDEN_IS_CHAIN)) { @@ -162,11 +181,26 @@ static u64 get_clock(void) return r; } -static ulong get_second(void) +ulong get_second(void) { return (get_clock() >> 12) / 1000000; } +static int vr_poll(VRing *vr) +{ + if (vr->used->idx == vr->used_idx) { + vring_notify(vr); + yield(); + return 0; + } + + vr->used_idx = vr->used->idx; + vr->next_idx = 0; + vr->desc[0].len = 0; + vr->desc[0].flags = 0; + return 1; /* vr has been updated */ +} + /* * Wait for the host to reply. * @@ -174,67 +208,92 @@ static ulong get_second(void) * * Returns 0 on success, 1 on timeout. */ -static int vring_wait_reply(struct vring *vr, int timeout) +static int vring_wait_reply(void) { - ulong target_second = get_second() + timeout; - struct subchannel_id schid = vr->schid; - int r = 0; + ulong target_second = get_second() + vdev.wait_reply_timeout; + + /* Wait for any queue to be updated by the host */ + do { + int i, r = 0; - /* Wait until the used index has moved. */ - while (vr->used->idx == vr->used_idx) { - vring_notify(schid); - if (timeout && (get_second() >= target_second)) { - r = 1; - break; + for (i = 0; i < vdev.nr_vqs; i++) { + r += vr_poll(&vdev.vrings[i]); } yield(); - } + if (r) { + return 0; + } + } while (!vdev.wait_reply_timeout || (get_second() < target_second)); - vr->used_idx = vr->used->idx; - vr->next_idx = 0; - vr->desc[0].len = 0; - vr->desc[0].flags = 0; + return 1; +} - return r; +int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd) +{ + VRing *vr = &vdev->vrings[vqid]; + int i = 0; + + do { + vring_send_buf(vr, cmd[i].data, cmd[i].size, + cmd[i].flags | (i ? VRING_HIDDEN_IS_CHAIN : 0)); + } while (cmd[i++].flags & VRING_DESC_F_NEXT); + + vring_wait_reply(); + if (drain_irqs(vr->schid)) { + return -1; + } + return 0; } /*********************************************** * Virtio block * ***********************************************/ -int virtio_read_many(ulong sector, void *load_addr, int sec_num) +static int virtio_blk_read_many(VDev *vdev, + ulong sector, void *load_addr, int sec_num) { - struct virtio_blk_outhdr out_hdr; + VirtioBlkOuthdr out_hdr; u8 status; - int r; + VRing *vr = &vdev->vrings[vdev->cmd_vr_idx]; /* Tell the host we want to read */ out_hdr.type = VIRTIO_BLK_T_IN; out_hdr.ioprio = 99; out_hdr.sector = virtio_sector_adjust(sector); - vring_send_buf(&block, &out_hdr, sizeof(out_hdr), VRING_DESC_F_NEXT); + vring_send_buf(vr, &out_hdr, sizeof(out_hdr), VRING_DESC_F_NEXT); /* This is where we want to receive data */ - vring_send_buf(&block, load_addr, virtio_get_block_size() * sec_num, + vring_send_buf(vr, load_addr, virtio_get_block_size() * sec_num, VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN | VRING_DESC_F_NEXT); /* status field */ - vring_send_buf(&block, &status, sizeof(u8), VRING_DESC_F_WRITE | - VRING_HIDDEN_IS_CHAIN); + vring_send_buf(vr, &status, sizeof(u8), + VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN); /* Now we can tell the host to read */ - vring_wait_reply(&block, 0); + vring_wait_reply(); - r = drain_irqs(block.schid); - if (r) { + if (drain_irqs(vr->schid)) { /* Well, whatever status is supposed to contain... */ status = 1; } return status; } +int virtio_read_many(ulong sector, void *load_addr, int sec_num) +{ + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + return virtio_blk_read_many(&vdev, sector, load_addr, sec_num); + case VIRTIO_ID_SCSI: + return virtio_scsi_read_many(&vdev, sector, load_addr, sec_num); + } + panic("\n! No readable IPL device !\n"); + return -1; +} + unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, ulong subchan_id, void *load_addr) { @@ -251,7 +310,7 @@ unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, sclp_print("."); status = virtio_read_many(sec, (void *)addr, sec_num); if (status) { - virtio_panic("I/O Error"); + panic("I/O Error"); } addr += sec_num * virtio_get_block_size(); @@ -263,42 +322,6 @@ int virtio_read(ulong sector, void *load_addr) return virtio_read_many(sector, load_addr, 1); } -static VirtioBlkConfig blk_cfg = {}; -static bool guessed_disk_nature; - -bool virtio_guessed_disk_nature(void) -{ - return guessed_disk_nature; -} - -void virtio_assume_scsi(void) -{ - guessed_disk_nature = true; - blk_cfg.blk_size = 512; - blk_cfg.physical_block_exp = 0; -} - -void virtio_assume_eckd(void) -{ - guessed_disk_nature = true; - blk_cfg.blk_size = 4096; - blk_cfg.physical_block_exp = 0; - - /* this must be here to calculate code segment position */ - blk_cfg.geometry.heads = 15; - blk_cfg.geometry.sectors = 12; -} - -bool virtio_disk_is_scsi(void) -{ - if (guessed_disk_nature) { - return (virtio_get_block_size() == 512); - } - return (blk_cfg.geometry.heads == 255) - && (blk_cfg.geometry.sectors == 63) - && (virtio_get_block_size() == 512); -} - /* * Other supported value pairs, if any, would need to be added here. * Note: head count is always 15. @@ -318,16 +341,91 @@ static inline u8 virtio_eckd_sectors_for_block_size(int size) return 0; } +VirtioGDN virtio_guessed_disk_nature(void) +{ + return vdev.guessed_disk_nature; +} + +void virtio_assume_scsi(void) +{ + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + vdev.guessed_disk_nature = VIRTIO_GDN_SCSI; + vdev.config.blk.blk_size = VIRTIO_SCSI_BLOCK_SIZE; + vdev.config.blk.physical_block_exp = 0; + vdev.blk_factor = 1; + break; + case VIRTIO_ID_SCSI: + vdev.scsi_block_size = VIRTIO_SCSI_BLOCK_SIZE; + break; + } +} + +void virtio_assume_iso9660(void) +{ + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + vdev.guessed_disk_nature = VIRTIO_GDN_SCSI; + vdev.config.blk.blk_size = VIRTIO_ISO_BLOCK_SIZE; + vdev.config.blk.physical_block_exp = 0; + vdev.blk_factor = VIRTIO_ISO_BLOCK_SIZE / VIRTIO_SECTOR_SIZE; + break; + case VIRTIO_ID_SCSI: + vdev.scsi_block_size = VIRTIO_ISO_BLOCK_SIZE; + break; + } +} + +void virtio_assume_eckd(void) +{ + vdev.guessed_disk_nature = VIRTIO_GDN_DASD; + vdev.blk_factor = 1; + vdev.config.blk.physical_block_exp = 0; + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + vdev.config.blk.blk_size = 4096; + break; + case VIRTIO_ID_SCSI: + vdev.config.blk.blk_size = vdev.scsi_block_size; + break; + } + vdev.config.blk.geometry.heads = 15; + vdev.config.blk.geometry.sectors = + virtio_eckd_sectors_for_block_size(vdev.config.blk.blk_size); +} + +bool virtio_disk_is_scsi(void) +{ + if (vdev.guessed_disk_nature == VIRTIO_GDN_SCSI) { + return true; + } + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + return (vdev.config.blk.geometry.heads == 255) + && (vdev.config.blk.geometry.sectors == 63) + && (virtio_get_block_size() == VIRTIO_SCSI_BLOCK_SIZE); + case VIRTIO_ID_SCSI: + return true; + } + return false; +} + bool virtio_disk_is_eckd(void) { const int block_size = virtio_get_block_size(); - if (guessed_disk_nature) { - return (block_size == 4096); + if (vdev.guessed_disk_nature == VIRTIO_GDN_DASD) { + return true; } - return (blk_cfg.geometry.heads == 15) - && (blk_cfg.geometry.sectors == - virtio_eckd_sectors_for_block_size(block_size)); + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + return (vdev.config.blk.geometry.heads == 15) + && (vdev.config.blk.geometry.sectors == + virtio_eckd_sectors_for_block_size(block_size)); + case VIRTIO_ID_SCSI: + return false; + } + return false; } bool virtio_ipl_disk_is_valid(void) @@ -337,34 +435,80 @@ bool virtio_ipl_disk_is_valid(void) int virtio_get_block_size(void) { - return blk_cfg.blk_size << blk_cfg.physical_block_exp; + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + return vdev.config.blk.blk_size << vdev.config.blk.physical_block_exp; + case VIRTIO_ID_SCSI: + return vdev.scsi_block_size; + } + return 0; } uint8_t virtio_get_heads(void) { - return blk_cfg.geometry.heads; + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + return vdev.config.blk.geometry.heads; + case VIRTIO_ID_SCSI: + return vdev.guessed_disk_nature == VIRTIO_GDN_DASD + ? vdev.config.blk.geometry.heads : 255; + } + return 0; } uint8_t virtio_get_sectors(void) { - return blk_cfg.geometry.sectors; + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + return vdev.config.blk.geometry.sectors; + case VIRTIO_ID_SCSI: + return vdev.guessed_disk_nature == VIRTIO_GDN_DASD + ? vdev.config.blk.geometry.sectors : 63; + } + return 0; } uint64_t virtio_get_blocks(void) { - return blk_cfg.capacity / - (virtio_get_block_size() / VIRTIO_SECTOR_SIZE); + const uint64_t factor = virtio_get_block_size() / VIRTIO_SECTOR_SIZE; + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + return vdev.config.blk.capacity / factor; + case VIRTIO_ID_SCSI: + return vdev.scsi_last_block / factor; + } + return 0; } -void virtio_setup_block(struct subchannel_id schid) +static void virtio_setup_ccw(VDev *vdev) { - struct vq_info_block info; - struct vq_config_block config = {}; - - blk_cfg.blk_size = 0; /* mark "illegal" - setup started... */ - guessed_disk_nature = false; - - virtio_reset(schid); + int i, cfg_size = 0; + unsigned char status = VIRTIO_CONFIG_S_DRIVER_OK; + + IPL_assert(virtio_is_supported(vdev->schid), "PE"); + /* device ID has been established now */ + + vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */ + vdev->guessed_disk_nature = VIRTIO_GDN_NONE; + + run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0); + + switch (vdev->senseid.cu_model) { + case VIRTIO_ID_BLOCK: + vdev->nr_vqs = 1; + vdev->cmd_vr_idx = 0; + cfg_size = sizeof(vdev->config.blk); + break; + case VIRTIO_ID_SCSI: + vdev->nr_vqs = 3; + vdev->cmd_vr_idx = VR_REQUEST; + cfg_size = sizeof(vdev->config.scsi); + break; + default: + panic("Unsupported virtio device\n"); + } + IPL_assert(run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size) == 0, + "Could not get block device configuration"); /* * Skipping CCW_CMD_READ_FEAT. We're not doing anything fancy, and @@ -372,54 +516,84 @@ void virtio_setup_block(struct subchannel_id schid) * expect it. */ - config.index = 0; - if (run_ccw(schid, CCW_CMD_READ_VQ_CONF, &config, sizeof(config))) { - virtio_panic("Could not get block device VQ configuration\n"); - } - if (run_ccw(schid, CCW_CMD_READ_CONF, &blk_cfg, sizeof(blk_cfg))) { - virtio_panic("Could not get block device configuration\n"); - } - vring_init(&block, config.num, ring_area, - KVM_S390_VIRTIO_RING_ALIGN); - - info.queue = (unsigned long long) ring_area; - info.align = KVM_S390_VIRTIO_RING_ALIGN; - info.index = 0; - info.num = config.num; - block.schid = schid; - - if (!run_ccw(schid, CCW_CMD_SET_VQ, &info, sizeof(info))) { - virtio_set_status(schid, VIRTIO_CONFIG_S_DRIVER_OK); + for (i = 0; i < vdev->nr_vqs; i++) { + VqInfo info = { + .queue = (unsigned long long) ring_area + (i * VIRTIO_RING_SIZE), + .align = KVM_S390_VIRTIO_RING_ALIGN, + .index = i, + .num = 0, + }; + VqConfig config = { + .index = i, + .num = 0, + }; + + IPL_assert( + run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config)) == 0, + "Could not get block device VQ configuration"); + info.num = config.num; + vring_init(&vdev->vrings[i], &info); + vdev->vrings[i].schid = vdev->schid; + IPL_assert(run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info)) == 0, + "Cannot set VQ info"); } + IPL_assert( + run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status)) == 0, + "Could not write status to host"); +} - if (!virtio_ipl_disk_is_valid()) { - /* make sure all getters but blocksize return 0 for invalid IPL disk */ - memset(&blk_cfg, 0, sizeof(blk_cfg)); - virtio_assume_scsi(); +void virtio_setup_device(SubChannelId schid) +{ + vdev.schid = schid; + virtio_setup_ccw(&vdev); + + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + sclp_print("Using virtio-blk.\n"); + if (!virtio_ipl_disk_is_valid()) { + /* make sure all getters but blocksize return 0 for + * invalid IPL disk + */ + memset(&vdev.config.blk, 0, sizeof(vdev.config.blk)); + virtio_assume_scsi(); + } + break; + case VIRTIO_ID_SCSI: + IPL_assert(vdev.config.scsi.sense_size == VIRTIO_SCSI_SENSE_SIZE, + "Config: sense size mismatch"); + IPL_assert(vdev.config.scsi.cdb_size == VIRTIO_SCSI_CDB_SIZE, + "Config: CDB size mismatch"); + + sclp_print("Using virtio-scsi.\n"); + virtio_scsi_setup(&vdev); + break; + default: + panic("\n! No IPL device available !\n"); } } -bool virtio_is_blk(struct subchannel_id schid) +bool virtio_is_supported(SubChannelId schid) { - int r; - struct senseid senseid = {}; - + vdev.schid = schid; + memset(&vdev.senseid, 0, sizeof(vdev.senseid)); /* run sense id command */ - r = run_ccw(schid, CCW_CMD_SENSE_ID, &senseid, sizeof(senseid)); - if (r) { + if (run_ccw(&vdev, CCW_CMD_SENSE_ID, &vdev.senseid, sizeof(vdev.senseid))) { return false; } - if ((senseid.cu_type != 0x3832) || (senseid.cu_model != VIRTIO_ID_BLOCK)) { - return false; + if (vdev.senseid.cu_type == 0x3832) { + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + case VIRTIO_ID_SCSI: + return true; + } } - - return true; + return false; } int enable_mss_facility(void) { int ret; - struct chsc_area_sda *sda_area = (struct chsc_area_sda *) chsc_page; + ChscAreaSda *sda_area = (ChscAreaSda *) chsc_page; memset(sda_area, 0, PAGE_SIZE); sda_area->request.length = 0x0400; diff --git a/qemu/pc-bios/s390-ccw/virtio.h b/qemu/pc-bios/s390-ccw/virtio.h index c23466b8d..3c6e91510 100644 --- a/qemu/pc-bios/s390-ccw/virtio.h +++ b/qemu/pc-bios/s390-ccw/virtio.h @@ -23,49 +23,58 @@ /* We've given up on this device. */ #define VIRTIO_CONFIG_S_FAILED 0x80 -enum virtio_dev_type { +enum VirtioDevType { VIRTIO_ID_NET = 1, VIRTIO_ID_BLOCK = 2, VIRTIO_ID_CONSOLE = 3, VIRTIO_ID_BALLOON = 5, + VIRTIO_ID_SCSI = 8, }; - -struct virtio_dev_header { - enum virtio_dev_type type : 8; - u8 num_vq; - u8 feature_len; - u8 config_len; - u8 status; - u8 vqconfig[]; +typedef enum VirtioDevType VirtioDevType; + +struct VirtioDevHeader { + VirtioDevType type:8; + uint8_t num_vq; + uint8_t feature_len; + uint8_t config_len; + uint8_t status; + uint8_t vqconfig[]; } __attribute__((packed)); +typedef struct VirtioDevHeader VirtioDevHeader; -struct virtio_vqconfig { - u64 token; - u64 address; - u16 num; - u8 pad[6]; +struct VirtioVqConfig { + uint64_t token; + uint64_t address; + uint16_t num; + uint8_t pad[6]; } __attribute__((packed)); +typedef struct VirtioVqConfig VirtioVqConfig; -struct vq_info_block { - u64 queue; - u32 align; - u16 index; - u16 num; +struct VqInfo { + uint64_t queue; + uint32_t align; + uint16_t index; + uint16_t num; } __attribute__((packed)); +typedef struct VqInfo VqInfo; -struct vq_config_block { - u16 index; - u16 num; +struct VqConfig { + uint16_t index; + uint16_t num; } __attribute__((packed)); +typedef struct VqConfig VqConfig; -struct virtio_dev { - struct virtio_dev_header *header; - struct virtio_vqconfig *vqconfig; +struct VirtioDev { + VirtioDevHeader *header; + VirtioVqConfig *vqconfig; char *host_features; char *guest_features; char *config; }; +typedef struct VirtioDev VirtioDev; +#define VIRTIO_RING_SIZE (PAGE_SIZE * 8) +#define VIRTIO_MAX_VQS 3 #define KVM_S390_VIRTIO_RING_ALIGN 4096 #define VRING_USED_F_NO_NOTIFY 1 @@ -81,46 +90,53 @@ struct virtio_dev { #define VRING_HIDDEN_IS_CHAIN 256 /* Virtio ring descriptors: 16 bytes. These can chain together via "next". */ -struct vring_desc { +struct VRingDesc { /* Address (guest-physical). */ - u64 addr; + uint64_t addr; /* Length. */ - u32 len; + uint32_t len; /* The flags as indicated above. */ - u16 flags; + uint16_t flags; /* We chain unused descriptors via this, too */ - u16 next; + uint16_t next; } __attribute__((packed)); +typedef struct VRingDesc VRingDesc; -struct vring_avail { - u16 flags; - u16 idx; - u16 ring[]; +struct VRingAvail { + uint16_t flags; + uint16_t idx; + uint16_t ring[]; } __attribute__((packed)); +typedef struct VRingAvail VRingAvail; -/* u32 is used here for ids for padding reasons. */ -struct vring_used_elem { +/* uint32_t is used here for ids for padding reasons. */ +struct VRingUsedElem { /* Index of start of used descriptor chain. */ - u32 id; + uint32_t id; /* Total length of the descriptor chain which was used (written to) */ - u32 len; + uint32_t len; } __attribute__((packed)); +typedef struct VRingUsedElem VRingUsedElem; -struct vring_used { - u16 flags; - u16 idx; - struct vring_used_elem ring[]; +struct VRingUsed { + uint16_t flags; + uint16_t idx; + VRingUsedElem ring[]; } __attribute__((packed)); +typedef struct VRingUsed VRingUsed; -struct vring { +struct VRing { unsigned int num; int next_idx; int used_idx; - struct vring_desc *desc; - struct vring_avail *avail; - struct vring_used *used; - struct subchannel_id schid; + VRingDesc *desc; + VRingAvail *avail; + VRingUsed *used; + SubChannelId schid; + long cookie; + int id; }; +typedef struct VRing VRing; /*********************************************** @@ -152,41 +168,52 @@ struct vring { #define VIRTIO_BLK_T_BARRIER 0x80000000 /* This is the first element of the read scatter-gather list. */ -struct virtio_blk_outhdr { +struct VirtioBlkOuthdr { /* VIRTIO_BLK_T* */ - u32 type; + uint32_t type; /* io priority. */ - u32 ioprio; + uint32_t ioprio; /* Sector (ie. 512 byte offset) */ - u64 sector; + uint64_t sector; }; +typedef struct VirtioBlkOuthdr VirtioBlkOuthdr; -typedef struct VirtioBlkConfig { - u64 capacity; /* in 512-byte sectors */ - u32 size_max; /* max segment size (if VIRTIO_BLK_F_SIZE_MAX) */ - u32 seg_max; /* max number of segments (if VIRTIO_BLK_F_SEG_MAX) */ +struct VirtioBlkConfig { + uint64_t capacity; /* in 512-byte sectors */ + uint32_t size_max; /* max segment size (if VIRTIO_BLK_F_SIZE_MAX) */ + uint32_t seg_max; /* max number of segments (if VIRTIO_BLK_F_SEG_MAX) */ - struct virtio_blk_geometry { - u16 cylinders; - u8 heads; - u8 sectors; + struct VirtioBlkGeometry { + uint16_t cylinders; + uint8_t heads; + uint8_t sectors; } geometry; /* (if VIRTIO_BLK_F_GEOMETRY) */ - u32 blk_size; /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */ + uint32_t blk_size; /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */ /* the next 4 entries are guarded by VIRTIO_BLK_F_TOPOLOGY */ - u8 physical_block_exp; /* exponent for physical block per logical block */ - u8 alignment_offset; /* alignment offset in logical blocks */ - u16 min_io_size; /* min I/O size without performance penalty + uint8_t physical_block_exp; /* exponent for physical blk per logical blk */ + uint8_t alignment_offset; /* alignment offset in logical blocks */ + uint16_t min_io_size; /* min I/O size without performance penalty in logical blocks */ - u32 opt_io_size; /* optimal sustained I/O size in logical blocks */ + uint32_t opt_io_size; /* optimal sustained I/O size in logical blks */ + + uint8_t wce; /* writeback mode (if VIRTIO_BLK_F_CONFIG_WCE) */ +} __attribute__((packed)); +typedef struct VirtioBlkConfig VirtioBlkConfig; - u8 wce; /* writeback mode (if VIRTIO_BLK_F_CONFIG_WCE) */ -} __attribute__((packed)) VirtioBlkConfig; +enum guessed_disk_nature_type { + VIRTIO_GDN_NONE = 0, + VIRTIO_GDN_DASD = 1, + VIRTIO_GDN_CDROM = 2, + VIRTIO_GDN_SCSI = 3, +}; +typedef enum guessed_disk_nature_type VirtioGDN; -bool virtio_guessed_disk_nature(void); +VirtioGDN virtio_guessed_disk_nature(void); void virtio_assume_scsi(void); void virtio_assume_eckd(void); +void virtio_assume_iso9660(void); extern bool virtio_disk_is_scsi(void); extern bool virtio_disk_is_eckd(void); @@ -198,15 +225,68 @@ extern uint64_t virtio_get_blocks(void); extern int virtio_read_many(ulong sector, void *load_addr, int sec_num); #define VIRTIO_SECTOR_SIZE 512 - -static inline ulong virtio_eckd_sector_adjust(ulong sector) -{ - return sector * (virtio_get_block_size() / VIRTIO_SECTOR_SIZE); -} +#define VIRTIO_ISO_BLOCK_SIZE 2048 +#define VIRTIO_SCSI_BLOCK_SIZE 512 static inline ulong virtio_sector_adjust(ulong sector) { - return virtio_disk_is_eckd() ? virtio_eckd_sector_adjust(sector) : sector; + return sector * (virtio_get_block_size() / VIRTIO_SECTOR_SIZE); } +struct VirtioScsiConfig { + uint32_t num_queues; + uint32_t seg_max; + uint32_t max_sectors; + uint32_t cmd_per_lun; + uint32_t event_info_size; + uint32_t sense_size; + uint32_t cdb_size; + uint16_t max_channel; + uint16_t max_target; + uint32_t max_lun; +} __attribute__((packed)); +typedef struct VirtioScsiConfig VirtioScsiConfig; + +struct ScsiDevice { + uint16_t channel; /* Always 0 in QEMU */ + uint16_t target; /* will be scanned over */ + uint32_t lun; /* will be reported */ +}; +typedef struct ScsiDevice ScsiDevice; + +struct VDev { + int nr_vqs; + VRing *vrings; + int cmd_vr_idx; + void *ring_area; + long wait_reply_timeout; + VirtioGDN guessed_disk_nature; + SubChannelId schid; + SenseId senseid; + union { + VirtioBlkConfig blk; + VirtioScsiConfig scsi; + } config; + ScsiDevice *scsi_device; + bool is_cdrom; + int scsi_block_size; + int blk_factor; + uint64_t scsi_last_block; + uint32_t scsi_dev_cyls; + uint8_t scsi_dev_heads; +}; +typedef struct VDev VDev; + +VDev *virtio_get_device(void); +VirtioDevType virtio_get_device_type(void); + +struct VirtioCmd { + void *data; + int size; + int flags; +}; +typedef struct VirtioCmd VirtioCmd; + +int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd); + #endif /* VIRTIO_H */ diff --git a/qemu/pc-bios/s390-zipl.rom b/qemu/pc-bios/s390-zipl.rom deleted file mode 100644 index 3115128efe465a024b2deb780573358ae1b829a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3304 zcmZuzdvH`&8UOC?hP%xJ4gtbake-C44GpDB0K-eVH#`h2rHiy0klJiXfJjJU*wBL2 zu98~fSXolRYISA>mgBTqR@7m3wA0&`I)nZJR#L3~;o=?oM|YfFXzU;XuD|ar0V>{^ z`#9hEp1<#LuDZ07)R6y-Vy9C#g~; zyiW#_?m-n26a}9nAQk{6b4=2>V>KlANSdN%kze)ESiF5IK@-#vl{v!wIqmBgG(I<; zszC{;8J?$xMOxbrnAZjk_ed-V3;$L_-iM}H_`TaqVYS8}c=HzSU*iryU&s+sP{4^w zqAE3m$cYFa?aMt+;anBfG)3%Dg&H!bB3ds3&B6|_MNk4%q3$HZk#>-Rp?R5xYhK7w zYsiR^3`T_wf*i&dZ zg!=^K4YiU)zfE^I615YyB5pbcEVRB2;N6JTVYYGYUyxV`+pF~6YPHVrYx+6J9ETP| z(;GqeY5JgEFJzyzkJUh<7MMu)IHd|l857{Za_s9d)8y{(|E(~MB$XHDP=v}=9aj>_ z$y_;2<2Y`{QW&Q>fSH==U%xj18M5#*7d)|?GgHl#3&vo1Ah`f(d>R}35 z%VMNPlh2wf$U3BJq!(*H`sZ~gk_}OKKedn(%?b)T`7AojNu$xiPAnTl-V^l7>(!?T zwd+$S=rAx9w9kpfkOOpcy%S4j;vbbrdL?*HSJ z_U5Gl16D=ePbpyYVXlvdX9Cur@7j0>06$7^$Pls%Y~TDs2Hwne_4~16qW7~UiqR{wWR%~ z;EO=v|+iL7lUO0ocxH9426A5%)W$j-C)#PM`}H z>pfHRipm=Jjs8WI4(a}^)AVjTLS(#)`a-VV3_7CL0RAARCQSey!NzD>BM&K|9)yKO zxbMCm%Mz_)_3ber_l8C?%yHt3d4tbFK4W}!+}ljeypfdZq%7u?XG0Y#k-I=#d;$62>qdEQUv0se1B;fe{~H!-Ev<~b@&2ISP3wse)E3V0-jy@^$>!5_ zla;k4&z|@C;r$9HmE?L8>^&v~PA7QC=i~ydaS<|*plRwPyqb~%4_5_t*K-WaiTq4a zrwkY8-g##+W!G-mOBg=$E(r1^3I^&Ce%$I6>*!dbiyYtyuCXtEGd zLlMJ4zqnOLmFBHt>w|w*&B%L`k#_{!wIV{-L!C0bG1Ln0q9uhW zo;3PQ46rr&yN!uPXiw@MHW0^G@mQ?t3n6e&_kaU!JwNwyyf_Z|~{< z?$5qwzHsjG@Vzq@edWIU&F_PCZ!%-foc_d;2h0>pnr3&)!fDZaK38MLpWN8mwrTU0 z_ScSreBshpw{~pn?CSnn&k?oH956rl=JrJIj;Eg9x$Bp&vfRr*`RQf6e(|dL$}_zF zy4ARJ+42>iZ(3=-_=Eii&2K#T(5i=5KN4H>1@pkSet77m4WF!i^sz5~sd?S{tLBfu zADLZ0XYRbuHq2jOnm@+6h{Po|TjjR+ z=Jt)WDc;`E)+#%?;;rqSo8|Vl&D+{K6Qv{cp5D$*rtfO+?trcx?LCS1t|xlh;;kbb zBa+OqP}1ryS!B{8a#L4tXX`AvBHq!#YMzX5+$s}Yu-(}k?~s5j-rJFou-LP6#D`K^ gQS=FXtXw{_R&MI)+9tQeds<~{`}VD$3JT%<7a#uHJ^%m! diff --git a/qemu/pc-bios/slof.bin b/qemu/pc-bios/slof.bin index 0398ac67bc8d07410fe2ccc9d62c376ef3cf87c3..f2384939efd167dad1153ac744d0624ad8591cfa 100644 GIT binary patch literal 923832 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 literal 912720 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 z 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%0DxKH-~?4K zz)>$@>q+-bwe#*4V3w)D+bynC_FehU@Nu@Jhba8`J}d~(k_`mt`x>C6cHY#$+ulX(}^~=ZdBBgJo-u7!bAeGqOm1cXW@sY zkES#Uw4$DD#O_s%LSyN2^ZyoQo5c~Yb>U>`nYsKIEe0e=!aO+z^Ck_)4)=>;tC zcH<5}C$QRMr5?3}kLZYYmaqsV@CPTyfxx=j?6q?9c5;-hKr8cT{m2Cajf;N;x5AuJPB zo|L?`B$zsNFaciNPuiBwjr_Si+QOUaw&SJxr^7AOh~L%r^6D43rp73tx~uDWF8Pyz zu)_;@BtX1TH{y^*Woc&)^7-z)mJ>h z{@w)`)Q~>4X$!V%7SH6vchenRxGHK|j)x?$M%w?kagt$r>TJgGoh-=LImm>?t1}9~9AWd3M{wz-+_U`#GNK_RGZt zX7>IpbcVL3DT$ZkEHYT0Fwo4mfc}+&K9y!62*_nMfm?HZ%0P0Y%iG$Fu)V6x74!T@ zLE&Jch7)w&9oL=&`w?rGmmy^%!?ic92-L6=x3ae737ly_+k_)JoX})MVL$6o2SyW| ztw7ev2_{^dGq46GB?J_&J0sl7>@`q)utH;IXR@$;VvEJm$t0S$Xr}U#NtYDzIxU7! ziV~r79F9-uDvdcahvMjpb;PsQG164#N(_TPIUb{bI8(s=E=FKSR4)F;E}*m1Jwl-PX+eH~2|!jS#*`gd-nZ-iu?It8+M�>YnZ9f5W9_6N4T2-N0dTg+8 z*e=wq|K|6k!$%7@=3iB0V>l>{qTZuFJ$m|`By{z3cFwp<80WaVto|%Qo_8S48ba{EIJX2iKhNI zS-?=V6<+li`*kQ->pcyImilO6RLldrq8`b~oB$%pZ6Kit_)8$P9Sl}?qHv^a|A#k2 z1(Ex!6v-O_iB679x*fcnlu=PlWO=c3Mn{V4{IbKwqzD^AWl<2gELg(F6!YKO)2d5} zYSb%DU?_))aG>{6f-O^7d%@c*{~Ma{C8kyHdmA}ChDwN#MtIWrcj<};=-J|=S2d_v zD_@n+H3tpV#N~*>Y-Yi#i1PUSr3!h*Ugc zcuvH2(3$iRVneJCcysB{78;8K0#)kLkHN!QZI-S41zH9iVLHZV$85_ET=qNX8Mx~V z>Lx96oGh8kkihv0AEZ{`jC2A9C2Rw6F%jfgktlqbMUXhHx1tFSzJOM?0!pWYp%q%& z4KFqTCCsF^tZAk5b~cw(6`k8{A&x<>`ymLa`q_nFI+z zQ_3|4bysCc=Qs&gD%tb*Rrg$M(ZadqvhWQ9^@}+gMn-8)WJWbtL`qkkw8ldQMxn#e z5@}L&ek;jlfD&$qBAz240-dOm8PDN z=T#QS#T2h`Xnl={+Nc(8CZAM?p?HAli;v7mi%}35yE8yd0>d$k94T8>T(?Mn9XV~T z%MRxDD!PK3n&W|%^RNpQRnOwV4(YThV>;he;fxe;7b$IMv-7L^Z98GAaAC!EPn~K7 zO~)lgdkX<+^5ma2HBHWu#$1QpTa6RD?HVc+SXx2JGPEL30lDN>MCqo=c`bPt6?b4L z?AdiJZAu|jitYrL4g~X27_bR^4gYz5IGgN0TvQqI(@9u)#5eqfE&_g-5(-VqNFucX zzp_7*#LGO;P+AgWDjam=^dM2KVYq4uOCH>9MI@2{bF2mXvoXM_GRNsNA}na7vd^q* zpg2s{(%Bz{TTuCr%UGq-qHD6hEw&b9jbn3Hqm8cGXD4qV<5bZ!le=@@0#+i3P_r%4 z$Rb-K7-9bZ&1pnBjqjin32U7IeH{JtF z@R%t-{A*PPTWx&aO-(*RS!#LZXH$ocIGzar_RWg;G@#L!ia15_wh5DSj8k?_D*>pv8KY`x9q+1bB`0c)C4JvgRn2_LQ3%tm=`yFyb?)RZn{*7nQ4eS2-YX^Lfab z2*<(bRGnmxx41$d_343oA$ivR$z;ykO*lKpsb&Ktw@GieV)-D4lQ%LpCUwI+_Je3N ziQ&|+&XM@ZKKGGudU~2ymA-yzx%qc{=?K!en<(DjOJY|_*^X8id^BpM_D4|PCGGUh zd%2bK?;2j;;CYa~<2G-e{Y@$H8fGf%q`+oz-b~LoeV~Zr$j!mhG97PV% zsP=ytxn9}%HTaR>6k7gG#MAc=5s%9M2Wht`IUm5y#~Gu=3=4Est}@-b9hE58%d=_2eX>}K>h<M0q+X(f|aonh4iXUjmxN7m!w=6@Vx0p+*KsDPWEoP_t=j0)6$$9)$Vug_$#^V*+ z;X#l~;zK_JB#bOBU`|7b4Fwd+uuJ{G;w$#%VZtc$q@WiYiN%b)&K~0|SR*UKA}X7F zN)vl5Shxw##e)xTRZ1KRZNlQcH$t3ilc%H)r&<~%LW%BYxM@0jtzhF^+66W2H(;h* zNect>+`4YI(de*|ancuHPd6R4gi?mBT#H;`lsUFs{4)(fGmjYh5S&tY2?`=Hyt-|*5d4`J8wKeYw@L`XKPV&?SvRn9OkYb3DSiB>2$^fH!Dxm!`xlrL^nMARI_W5rq{U>=HKkPKCVry}`S z=#rBkbMHW(Z5cHB`|BG_dFkaKew*G4bojD&I=^C$NUuS>x<)s+f=W08`Lzc$ zO}7?LX-Z4zvxMnV7J^Qt#`Q$LyX6f1+ed#HKKkbAqnFrN*yS`)6zAyl!_}DMGc?AC z)$S9^)o?)TpXeBdOae?NZh$3zx=OU=FhajkJMq#A!Q6rCIVSYZ!3`%

      EykIT%$* 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

      vh0#5%a_WY~> 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 bFc9r1ur~V|evJ;ht{uH7ZovP}bymqg{qGJJ delta 127432 zcmc${e_T~nzCXOy=AcKzI@Zxp(GH3Rii(MK_$d_5Yy}%^aI(f9s8~8f4VKoi_F?bC z!3IhT5?z>}SnBAEH9A+n+2jwLbS~a&-)r~k49=)wX`z5(W2J@c@B6d%K0rT_=VyJ^=l5D`pH;O3s;b--!xd*lWz6tVy(5?de~k5J9RC^qhr1ZNVgJdj ze9!K}{idFp&shFK7xM-=(<&Bj^9B{BRZ<$pwAU$hFl`N`;Y_Qdv=`IXQreqo>nL?H zO;Fm0Y1NeWW!f8*-o~^IltwVEhSGjatEKdIrfsD34yJ8dxOWqGDg21orSKGKfT?SIf_bYfSV$7GxT2b0&+@)?`|OUBZ$2=iAlrm_5t z41?w7F;}|(mx?=pvScHoBT&Uy(kk=1mqi?>I++0$Ux_@;scfqNt7wB|>d=PB)R|;N z6#22Z{ujcwY0R;c$N-B};Lm54V`4@+6L+YT8 zkUIS@hHXn)#UcnO2|$m|D+7uD69i05=t7tzvH#zx<^Lr${*U--3>G@XSFYDjv5NG1 z<|zJpm+$p2S=G8BJkML)tZaMc#$>i;0Na&Z&loE0FaFPmwgvBItmJFv&+9@=-;1)-L_2|a@#h8GnbLcB8_O~4lOCx{t2tjE2#6ze^)0*c4@h3@!lGCB+^Sr-AVJA%WA`qP)c+FD4I#o5JZOp76cWXMNO`2Vha!CvJ}k2f~HP0iZ9O& zZoHhY@_}cG_>2RLMT9d(0!R~IMsrUXAGkwA4(I9mho(O{#HW>w@M)JutjK)RY#RS-i4qq9qL->Hb0c`D__Ahf zyli;u7)z~hVj$!uyqC&Oly@d+emQ^#gD#^y0#a4}Fvrf%NBumMm8CKCiE@2Qix0rF z`OYa!SJ}}iA1EGfQNC9o{dYe2bp-Up{@&-+lULSSs$0%f8$E`dZJy zP-!~~`iqE>eA*M2!S`~|T}Wxk_8_G---pzRbU!+tj`VvS-?e&7EdV~i+$gJ;OH&8T zDs_Xr^O;Iit+J>OQFa9F`-%M{`DlIVB+!%(c@4;mLwX!(v=LyrXcL=PdZSoXmiDK2 zf){yCymS4+w?3>4wL78mJFlTUZ%iq>2gq6s7;}+-68Wbez2r)!e*DqX+(4SDDn&=B zZHmdbY~Q>1aw9747KNku@X=}@eoAz4}J=#cEMY&b3&NAY2| zzj793#O9c6N4+c?xWQ1FQmL}RmD%PuvK(cr?6Mr>6QjD2mrp_oZEqK@Xg+xOhA5Re z*5mySq-gu?SoHIu-9JdOBT6Ks0oFGfv&}D~0b8@mUDuGdBE6Jtn#WK-&|UxA6CyU2 zkI>iq`-E+qKYVh2QU-|8Lu*6Nn^JYF(3L(frZ@EPpHO zoBwAPPOKJ^nUE9iiUmwf&3E;)WW^h5wt4-c$_Cw(s9@|co@ZLqRKATD7h?H1Wqq@7 z$MFY;56(>Tgf(jmv)84091Nn8`4fM-tsCxds)ZKEeGxO`*r>{TQJQdh+d9P&DViR3 z^wuG%t54`lf4dD6zy9cp;ps$O!Go7?Kvs6B{0<?ZS$c;4aU8|f~wrup|4E-)Tb)? za(z=HguYn=s1sD>W}d(QZ@0~KhNlyHlbj&2X?OVu?Uln>1qLD^lO@iK=J$;C%#6MA z&CF4~I#I1c`z#?r`)$TJZQDej_HUWv>=mgeNgY5_5kH1MG^dHAK}cY0zZ>H_lxeAs zR|0H|_P2>XEJ#e*VK$x^McXjK=b!UaTZoH7s3ytH?iLRtZxWUs`Aw}eIdlnwichZa)gh>kE#^D2Q+z9iHU$*pn`T^O-vsU`~rZzWh+m9impwy_87hmupGl=$o2}T((%`^3~$1Xx*$QEg4$X zg8}BURdAN}0#-&qo1O(x>>p#V@QkdD9@*;?qf!J3(vJLO1{vt~p@gj>$@vuI5cYWC_eWtrtdUCxa&iZV#leX?`4*2Ae|!V1bba~~ z{qb+y#b1Bp*0(gf^x%1yUfu#l%px90GEJDVULY|;4}wx*0iZz>iLbnsZ>5KDIesPZ zx&-?4rMiJ)7YrkP%@M*~8r(tsrCb6y!m>gP=8ws?000qWo1JOe5?C;#Ll3q%RON-u zL^wng7^i~9AuSOahXDU)f?JaVK{KD)q0MZxY1H(P#Rn>)MJ6@{%f_eFF_vPm-WH{< zi86{?oWbIjh~V57m&*IBz~U8v(^wEToXT^`wT1uY);C4c1fY{!B2Z^0>g4#-KK)B@ zGII=#W4--x&ABND7`Ls`2uYEsI97iMmO#wEgRlEUkFRCXFk9+_CSzb}Daio!EC{DtGF; zQK=M}Q8cr>m9KT?y(BvGe!ZzPuk1ISdF3B>=DqxRCn}cHvLX#U`yT5}HkO2*mYF-B zdUS^?nI@raQpe$a&iM7Hvk@4z+Dyl!?xC53&yH9V`Qo@-GN<@#0*~xlSqBIpg{t<7 z+#e}}?t%qo&7sm~EM(Bxea?%-`=BrDQ7E`~V4C`6ty*Hyg7U_DV z@uW%cyjr5MhK#S41ibs7zXWGP8e=Vqcz^2oOPbPzvK$Nr{aD3vzM4*)z@1x zkdcOr47-CYypP4Z$9`AwJ{0dR`#lHm{qSyEe=WHx?{y3B4!b-L?`?Qj?DtuCKZ|z~ z-vsb%ynje)Fx$L!%vf05l25bJr+{Fd0FVEg)}+_lhMzgWvtI%m_2gB6!urDEqYesG zWz7V^3Y3NW)BA2)KH3S>x@8&YA!_+(2R%#sd--T3gh5T^;iv3=hN9d+KTBjk z_rDfz6KE+2lqdkEa`SOiY=y9#Y5!4&U> z=ZaJV=?+$xU&>%^hYuAUiToa(BbyF;wLlJA+yd*}@u4-`x5q50aJAsw*^~HwMQC3B zn%?aXZvG)J?Bz3o5v-cH8FDy+7!UZIniacm^Y9m`bXnsGD`$>kwgWm(qahXARnZ| zIk8yeWsqRx+uDmmKnamwO6P??bW}go;?rxTJaUp~Nb@AHkVcr?M9<9`88s{h(9}ta z(*NcLgilG~UGR6GcBna5Rc?P<^Ol1)(#&3{o5(A7RrPfc$^%|B9 zlKojXNdDu1Ex zua^bpnnjQtdqgANZ-1#Z*x9nnVvE+(B0r7Cg(;t(ls?jZFI8omZ6gEeExT+nL!|HY z-;c0qddp5g?dmC{&76a8iG@nIDBcMh+GnvN@ zD{WTU&b44IqPm*#H5Q}8;B;4}GA8OL^Lz9{_$(3NUk2OLnX=6^{iE0k{lFt`XeFi} zP`&!^T73RrCb<36;@smw0?>|!fG=+42uoZk&5B+{5c(A2GWjAjLS0*2( zteq+nGWlR-?O2h6cfF+n>i$TI0g6m-*%utva$J8NEWyLZQfw!rUI31R1d4^ns?#J$ zurM^n3G}@i&+RsAWl#_RzHgc9nDJog91F58F0YC+dR#=fOg$72qRp zfJH*5oR@5|kyXj^082m}FV#WW(RgeSVHtdZOz-3q6pB8lmom-<*%m^WCRt>SQ^Bbv919V%9=Ks?f6ZwYd4xC zt?D7LpTZ(Qfqj}Kc0I^P|Dq%cRCiKUD*J=lR=|Wsh^3_w^_F|{&2NF7EKuVa>Ypf5 zRVpTc;>d=L089<9{QR^qiCH3n8D7oNB|<1Qg8D2!u-J8%Vv;U`d3}wcrS!UFUmo z4Irrr;m3Rzqu9nj8}CM)Zv2U@H{yh_&hZB~xO(UQ;E?vtunbxF!vY-A{vhNx5Sp zio|3r*)eh;ysOH&?V|B%(@TRl300=j?dJ1_>A zc3?Dhy~OGo?u7+!1n}fk*%vz=sFD8eb$}I5b*McSn18Q*T+jBvr$<%J?y@PYf>=bH z`Mc6bT>6zV^kK~hBSA({GD~`&2&A_yqRoS&$kATf5M=@_#fIn(7$lUU;I-aaq9qs(^}Ch zNy52INr*ts+g487EpcL*GDOTJ_k4jL;`?7}P6kGAVCrTSCGo0lip z8PWedY};OxVc0ejTOg#?TOw<@as&XiP9pq6pxGqWI35s?`9(`?G%po- z&nX^^-MZB3rUjFJPKgC@t!ZK>$|9jZmcmNPNy@;YgZKRNroNMHetuqDcupCm^qC|g zo>v}9tRsE;J_9KH?~Fu=C*W@N(`hT4ix7~)_$Rm%QioIPxzi!h9$Q;0(}_E zhe;cljc#AgU{GI9%L{Lf>}mLcdLu{|Qy!k9#EIvX=<()f9}$sQvf%RCH39Jdu+9Sg z*0(+ooeRX~P652hbtD@u1%$_^%V zu2Ak*dYux5E9Amo3KtbClz7x^J|*f`C{e&h^L$9W)Ifm6f(wiMMgW}nU-{zt3T4!= zXJ9*a7OamU^nbaRq#l)#zy(K#Nr9_BgZTe&a*?Slfi&-UNV$;a{f^ef3`rkZ5^!lN z7}uL^UYMZQLc_x4kWvPrHRSCemh`9Hd`Fso22(IbIN79B&s#u3@7hzS!?hvO@i7|! zK%{>}Hq4X%+yWq)u>5bX>!Y~!lqSaQ5f>xekKKm*`a>oqs_fh`+ZS2Q& z$hJYb7v($1#-Viq5$v+-Zl{n*W(_&6t*Jy1D*JpTd1b-k)(k+Ghk=2u%j+o~s}%4gUf#&GbXFx=T4wgCw{tThXc z9r8X~k7kL|s$KMVfY2E(@}E-f*6WdtJp`yWXckO|)E$W8#C1}0T?=cgH*z$g_v0-T z|EeA`Gs2!>8G!-Rj?>;7gLRoZpFq&(MCM`^E?U@Z^Za>2(>+O0gNVzf7${E>>t0aq zx=WdJ(T12jK}_`g4zd3QWlWz~Vs$_hFWO&FM)iHV!`RrGYaDFN6P_290b$C2$S}=4 zI+X0w`)Hz2TC`ZRRdx|kmezc0@)!G~Tem#~l^3Y&!Va1_(#@DPg3uSH5LpujROLU) zF#84szq__H4sPyJ@Tn9B(0m|olJ+-KK;i>Q%Rik3%8B$3en?IYoV-}HzNoASGe15p z7Hg!13PqJB8S=sbaYU0$`srrTtSPg=R-aBuc@c_pLjwp7(LtSgWzIWSRqtUs+l@e4Lh zNKQCYsXaTyM>GVvo0|;l7_*yYXavNW<)U3z1`ILJ$p8@uDIWaTpSDdYfESqC>JuJA z$%2IYc!gMOD8mM}YOse-({_hmGgvYY-j9hILx~PE|Grh!8_IG#j1}I1vJ*sdjzY4; zXryj>gL5vuUX8;zf0ayI2bFKYGSgBvgS(p8!@*&#r7}kSC^7NhGPUO{DURn!Bsq#d z*aZ`VW#Juat3-)}*>?7H@u%-YHk|zkB@vd)KYJ8`N~W9t2{Cc@y;1}%Ez&gifiz2z zceD7d-%*=dD?oF0t*9$e#!P5dl?HOy|H&vPTb%6aZh`xxC)+%|M0AuW_l;}K(J!m- z=w|O_BsVRN`q$WvpL~;@~=a8bGk57p+L1hA%VY6_RDk*~y#Q*bgG67NE z%Qgkc{4Nsvf5e9nLoccT6VE_UrimC6d$4(d;Ve(8*}0uN?PmwdBkX4dHP>reC&yje}u%Dw)=ZO963Cz{a8}>^KD&^YGv3O?ob3C47?dJqMJMHI0 zbX09WdyyXkIu+0R?0h*W-tz%a7V3w9R*`45&7R6~z(5s@F3CeKX{+v3r|9xFl`G&!qJJ4 zw?9d;QO;=fROQGCfsOnT(dc9$7WdQXq_L?=

      <(g2c9~trGE5`NaNIEmc(}1thJp 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 z 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 PIYcbNisYe1N(s(8i;|f<(ssUJ&OqHt5m=) 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 diff --git a/qemu/pc-bios/vgabios-qxl.bin b/qemu/pc-bios/vgabios-qxl.bin index 5c43bd222eceebe990c1f69fe3a24dfce404c79d..38d31b6b5b15237036d874048496bbb9fddfc7dd 100644 GIT binary patch delta 3731 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 IS$j15H+!O6q5uE@ delta 3580 zcmb7H4OG+D75~3~A_1W$1yo8A`HU1v06_#B6pOLQheQDdEn;=vWc}D`YpUB>3l{8% zAF1`$uH9%)Te_|7R&}Pc0UZ*girCiAIzZNehm@f}Hx7r@q82kaUVomzbX zoLn7j*Wgr79aJ|Dl6Z587uZcwZXxBS5#<4Hqlb3OiNRF|(^)6S8ZQfL>Rm=Rp%09p z2Z6Ogb*l!J!E&g8@)lXr!{%`zSgzR5431D`J!31eX-s-h#Vt=PGlP2Xl0Z)Pp21rBBK7fc@bm*edCcV$-Us0s;WKE_MrS^!2S1mjzwO2_W|zL4cb z7jUP3qT|i5nF?ldbWVPC3~RurV%G(E%V6V4t1mRRl}l2u4jTK)@Mdge0Dpb@Z0}Ln zaI@AiMH?`N8=XEigK;08N|?o`-3hPrDNh%{rzdskeEOrVhEFt3IT!A^g zSM-}FkYJmBmOM~`$MrLq5Bes~W^*tnF@gOGt%-^L?POH_!Z8M!%W+p?h5T8WLz7g_ zCdFAK$>g@2;c@kbgJmC`Dp-Qc_|t})lJ$XMU^~Q*-^+{31eU#vu{}Agl1JCPHV_&= z=Ne8W6~om%$C8?_!iHPdN&V^ZwAfEp*9CYyY^Yj10ZOpl1Y3v64L^{&dgCpH-n_X; z2KC1++(tLOWh>1#V+Tpv^yW#NQ_5lF+<~SaY=R)Nl=_%7@M*m6(^Q=vBSsS52>?kLbemOgZKZMyDnE_A$1-${ZQNmDYnutG{9p%rf^9Wr;4V2cGXGe}5YHF~L!r4E2;ltsiX>#wpJX9e4 zRTr4a{r%TG^KLq2YY;2}ONPK~y3G5$r_aZJkV+pKB;HPJH@^LV>LwVAvrKYhG5#Sd zhJA-evuaobn&!O3LL5DF4l!SoU>NVgo!Oys^c8H+en7QS$lCB`_Cu86%(;)q@kjA@ zbJNt;Pepw#rTdURf~(jw_W@!XYn@IbysGgr{Cpd?k;`=CR_c&mlRFnnAKnu2smq{H*DPfQrF!GV0BCRu zq+J9@=HEnR<Oy3rln2Ss7O6%#^2{#RECdvb~r#KaTw#E9TGEd_-P}zDUgdCimC4 zbAD+4IuYQv=Xu8#-o+D&-)kDGw9{LmZ{+6mR2~_+;|VodTyUDVuk3qGZZ8Jsrr*1! ztG0y%t-TTEO$8Ldbu7-K^QN%v(@bf z4eyCCWsJ&;3AyySc~ems^={2*>xK?op7%D(-kv{=y^ksRI(8Hv&DXMbu{J+f4n2UU z^CM^8XtE#X&DETmw?G# z<`hTf^d;`m85zVc3!i5(SZ<0+!j_VJ_6n*O&%=R|>>D7|dOnEy_SDl&Wk7$02#w#><>yMM}lazjR=FUOse_>_9ntlYkjd(ck zTAD|A`7hF8Y~_Cu{}@!vqYosQAtwW@mP{}4@$)pQZvc8!{m?DL6}w=P8#<}3gNJl^S*V*;g4vK zJNz2WH8j@K7*|Q)D!EO%8@zTHTsjJ@7TD#C`O?2NL_FdI$JP_Ef#x_GoLDoARSsiE zj&3wIm~d=8&RG$3y9)UBKZ&Y;g(O{|!KnfTRS#48QWcQd(NrT?PQ|f7l2XL(I~@CI zoo5s5|9;1y2MX5}@;Xg`TOCUk)xVGY$j^#OA_EoBor&m9rOV+3B Zr|FV(=9CmzxjH!|V&yL)@Xc3~{sWePCky}p diff --git a/qemu/pc-bios/vgabios-stdvga.bin b/qemu/pc-bios/vgabios-stdvga.bin index b2dd8f91efad329a3cba88f0a668e46649788ba3..e469c107a7a1fa6416005af6012a7cd94989f4e6 100644 GIT binary patch delta 3731 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 H?Xm1XJP%up delta 3580 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 YU6Rh6k^(DNC#OWL{6z%5{&LcP0RSH-@Bjb+ diff --git a/qemu/pc-bios/vgabios-vmware.bin b/qemu/pc-bios/vgabios-vmware.bin index 15e21c2a28ada598bfec66589300a79c9ee46a6e..26bc0b704cb4786ae527bb3ad8fe0fcf778ce5c3 100644 GIT binary patch delta 3731 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 YU6Rh6k^(DNC#OWL{6z%5`D)UC0PpfAlK=n! diff --git a/qemu/pc-bios/vgabios.bin b/qemu/pc-bios/vgabios.bin index 84f1561d9277cd77e02c37bcd5e433c6d22de8a1..2d1a7c6c9508095a25dbe8c0e7e12023af68f918 100644 GIT binary patch delta 3646 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 EzXq#Bs{jB1 delta 3465 zcmaJ@4OCQR8ouA1aejoD0St*IK!gdE83q+Z1Hk}A{<@))p8SXqjPH8HmT{@fF7DbL;MLtvk-9?f6AFKrOk}AVBhZ!6uYNg&iQ!1`+d** zyubIoV}D1qzay%xZ|D0aFZE&4`pGXx28Bey0U=WmhC8300lIzN;5hwqR_@C$WxRK) zlrcTd6jP>v;|uUyS3utO|~hEiD7 zE(&I-35v#5ii0f88KG=qY%R7;NDVE$?TceJ(CuFq!s*_UypvI@DiVip;lrxC*##_D z&0>GYzp8*OM7=sqOX=}|$ESYUDX9%g*6MsBV5&mw+J&zK^>DF4H!Kd7I5+73q+mr( z<)kxA9RAvQ?#?$vR^qgWhcH#n6>=>QrzA|mJrPQ0^Ic~|vG_8+Yo6@Gm_4bFRwffKmVZ?YagF2CkWF!swvEJfBU&ljc;G zgqp+*wgGb!6T~;I_)MZX;1n6vy>OC2I^e#p#f5OMwusNmtbpALc6F0SHIQa4uqS>A>iSJSf=g z&-V)D_wc8%JUX=PB-UKx>s4ICrB+cg$O>4*n-Ts{3aMH2%N#58`;DGGOA!AbkrSp4SFlz_1;E`FThb=qEPGId(@LbSC zt7S9gO84O@&>pLI8^IA4$OeLbxujYQT!19EFF`cJUOBHySiJ{aRIq*{G(}_noT$Wj8$aTy zGd3A$PZ>#xCDTLT@lN8s#?eq_l^LghkGtpmUZl5ZV(Ps?uYAE>yebD|>c))Jr8Bn3 zu=pGBFs!KUtatN>j5db~Dm#K;`!Ij+bT1T-aF4Jpbsnq6A5+cYxpi_$+@S5OZzs>H zciG@b&R{NP%#8}~;j!xWp8^-xruSJ_wUXYU27G4jVKK1-)8>5;&u>CEYiSqQ59bDX4rPZu14?S$>^g9? z`<3cYkv5ZMVoF*QJBqz&39QknP7h#{FOn)1bku7~MV5-!?0~%E zG|=SJe5}e|9`b=7`7HAt`&mAI7YDO9i}xMJM{^dk@31*1ou%Tn9D7&-?}pX}GEW^} zPVNrAS@=Ox-kmPMWN<#~33I~mjpdvoisw3A5@E|OKvghTn zJ1_CEE&Dt|5%G_kp1Sq3I=C_R`SdNt$i-m+F=zP1$_kdeA7jSi)Zm5Zs@v({@IbU} zXCXy#9IF>6vQ_9>oF!V?@#n=+VtPA9=G_$*(+*isd0ScT_4R3?rCE+k#KOGT@Pj99 zJC_(}HtWY53_gpFyuD(0J0|2Ov2D0I-^BJ{ZN8ChL1%s*yN<*8G3*3RDY%Edix~wG zV!8)c6j%bfJ_47XUUuWX~@#}=srX31KU^vRzDCkn`;du*=qAeaLHpn?$`YUK{*iS ze>8DB6GyM`9}k$+d*tdhV~BLWN+wx|+oW(cw?&@_+#*diaOe1kLh|0x%*U>8qXJqQ zMPt< zoS?ZX>jd}W`^B+pNxrb)`QjOr9PN_xtQoH?iHyjW&&%oCPF&;DP>E4XA7z8+SX!(! z*GFdKH%klHE}Umwh%Z{b&aDq=L+PXaY!B{`4+84T8y6K zg}Vf$mP+p$5&j=mB&_rs*gq2w=iN$jjFl8!{m4KXRT<>@I zSt49VL@P<=(n`bN)u9#t^Og`QVzP^!0{btf)KMk_Ne!)^)(C?7R~H4Lc|=$VeyiL= z+6w)IIYG-!>kdI7d#KcCA_Za3uPz>>OYY(xlC`w#=o#nY*3sKFW49!AL}eFOGo}fW zna0d=Y?B}e*(5Er?6mZwSmh{otUBYdhEZ&=jO{00z2C+0;7bDi5%6j$cX4BB=r2vd zsm5-{s6qv$9Ib|Dk%H}`j1arvCGB;zJweh%>m6E;(M7bPL{w_1fjD-w*|v`IHW0=@ zdpoU)b?Yi0zkAc5utsny)@YN%H?7}bN;0RKXPPV~+pJlzc74*U=(Ss-@x?zrhwi## F{{~5A0{s90 diff --git a/qemu/po/de_DE.po b/qemu/po/de_DE.po index dcdcf229d..97ae65516 100644 --- a/qemu/po/de_DE.po +++ b/qemu/po/de_DE.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 1.4.50\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2014-07-17 20:39+0200\n" +"POT-Creation-Date: 2016-04-18 14:50+0100\n" "PO-Revision-Date: 2012-02-28 16:00+0100\n" "Last-Translator: Kevin Wolf \n" "Language-Team: Deutsch \n" @@ -16,70 +16,70 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n" -#: ui/gtk.c:321 +#: ui/gtk.c:273 msgid " - Press Ctrl+Alt+G to release grab" msgstr " - Strg+Alt+G drücken, um Eingabegeräte freizugeben" -#: ui/gtk.c:325 +#: ui/gtk.c:277 msgid " [Paused]" msgstr " [Angehalten]" -#: ui/gtk.c:1601 +#: ui/gtk.c:1833 msgid "_Pause" msgstr "_Angehalten" -#: ui/gtk.c:1607 +#: ui/gtk.c:1839 msgid "_Reset" msgstr "_Reset" -#: ui/gtk.c:1610 +#: ui/gtk.c:1842 msgid "Power _Down" msgstr "_Herunterfahren" -#: ui/gtk.c:1616 +#: ui/gtk.c:1848 msgid "_Quit" msgstr "_Beenden" -#: ui/gtk.c:1692 +#: ui/gtk.c:1940 msgid "_Fullscreen" msgstr "_Vollbild" -#: ui/gtk.c:1702 +#: ui/gtk.c:1954 msgid "Zoom _In" msgstr "_Heranzoomen" -#: ui/gtk.c:1709 +#: ui/gtk.c:1961 msgid "Zoom _Out" msgstr "_Wegzoomen" -#: ui/gtk.c:1716 +#: ui/gtk.c:1968 msgid "Best _Fit" msgstr "_Einpassen" -#: ui/gtk.c:1723 +#: ui/gtk.c:1975 msgid "Zoom To _Fit" msgstr "Auf _Fenstergröße skalieren" -#: ui/gtk.c:1729 +#: ui/gtk.c:1981 msgid "Grab On _Hover" msgstr "Tastatur _automatisch einfangen" -#: ui/gtk.c:1732 +#: ui/gtk.c:1984 msgid "_Grab Input" msgstr "_Eingabegeräte einfangen" -#: ui/gtk.c:1761 +#: ui/gtk.c:2013 msgid "Show _Tabs" msgstr "Reiter anzeigen" -#: ui/gtk.c:1764 +#: ui/gtk.c:2016 msgid "Detach Tab" msgstr "Reiter abtrennen" -#: ui/gtk.c:1778 +#: ui/gtk.c:2028 msgid "_Machine" msgstr "_Maschine" -#: ui/gtk.c:1783 +#: ui/gtk.c:2033 msgid "_View" msgstr "_Ansicht" diff --git a/qemu/po/fr_FR.po b/qemu/po/fr_FR.po index bbb5ef87b..e9fb0402d 100644 --- a/qemu/po/fr_FR.po +++ b/qemu/po/fr_FR.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 1.4.50\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2014-07-28 23:14+0200\n" +"POT-Creation-Date: 2016-04-18 14:50+0100\n" "PO-Revision-Date: 2014-07-28 23:25+0200\n" "Last-Translator: Aurelien Jarno \n" "Language-Team: French \n" @@ -17,70 +17,70 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Lokalize 1.4\n" -#: ui/gtk.c:321 +#: ui/gtk.c:273 msgid " - Press Ctrl+Alt+G to release grab" msgstr "- Appuyer sur Ctrl+Alt+G pour arrêter la capture" -#: ui/gtk.c:325 +#: ui/gtk.c:277 msgid " [Paused]" msgstr " [En pause]" -#: ui/gtk.c:1601 +#: ui/gtk.c:1833 msgid "_Pause" msgstr "_Pause" -#: ui/gtk.c:1607 +#: ui/gtk.c:1839 msgid "_Reset" msgstr "_Réinitialiser" -#: ui/gtk.c:1610 +#: ui/gtk.c:1842 msgid "Power _Down" msgstr "_Éteindre" -#: ui/gtk.c:1616 +#: ui/gtk.c:1848 msgid "_Quit" msgstr "_Quitter" -#: ui/gtk.c:1692 +#: ui/gtk.c:1940 msgid "_Fullscreen" msgstr "Mode _plein écran" -#: ui/gtk.c:1702 +#: ui/gtk.c:1954 msgid "Zoom _In" msgstr "Zoom _avant" -#: ui/gtk.c:1709 +#: ui/gtk.c:1961 msgid "Zoom _Out" msgstr "_Zoom arrière" -#: ui/gtk.c:1716 +#: ui/gtk.c:1968 msgid "Best _Fit" msgstr "Zoom _idéal" -#: ui/gtk.c:1723 +#: ui/gtk.c:1975 msgid "Zoom To _Fit" msgstr "Zoomer pour a_juster" -#: ui/gtk.c:1729 +#: ui/gtk.c:1981 msgid "Grab On _Hover" msgstr "Capturer en _survolant" -#: ui/gtk.c:1732 +#: ui/gtk.c:1984 msgid "_Grab Input" msgstr "_Capturer les entrées" -#: ui/gtk.c:1761 +#: ui/gtk.c:2013 msgid "Show _Tabs" msgstr "Montrer les _onglets" -#: ui/gtk.c:1764 +#: ui/gtk.c:2016 msgid "Detach Tab" msgstr "_Détacher l'onglet" -#: ui/gtk.c:1778 +#: ui/gtk.c:2028 msgid "_Machine" msgstr "_Machine" -#: ui/gtk.c:1783 +#: ui/gtk.c:2033 msgid "_View" msgstr "_Vue" diff --git a/qemu/po/hu.po b/qemu/po/hu.po index 401ed211a..1149adf26 100644 --- a/qemu/po/hu.po +++ b/qemu/po/hu.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 1.4.50\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2013-07-05 22:36+0200\n" +"POT-Creation-Date: 2016-04-18 14:50+0100\n" "PO-Revision-Date: 2013-05-06 20:42+0200\n" "Last-Translator: Ákos Kovács \n" "Language-Team: Hungarian \n" @@ -15,47 +15,73 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: ui/gtk.c:214 +#: ui/gtk.c:273 msgid " - Press Ctrl+Alt+G to release grab" msgstr " - Nyomj Ctrl+Alt+G-t a bemeneti eszközök elengedéséhez" -#: ui/gtk.c:218 +#: ui/gtk.c:277 msgid " [Paused]" msgstr " [Megállítva]" -#: ui/gtk.c:1318 +#: ui/gtk.c:1833 msgid "_Pause" msgstr "_Megállítás" -#: ui/gtk.c:1324 +#: ui/gtk.c:1839 msgid "_Reset" msgstr "Új_raindítás" -#: ui/gtk.c:1327 +#: ui/gtk.c:1842 msgid "Power _Down" msgstr "_Leállítás" -#: ui/gtk.c:1381 +#: ui/gtk.c:1848 +msgid "_Quit" +msgstr "" + +#: ui/gtk.c:1940 +msgid "_Fullscreen" +msgstr "" + +#: ui/gtk.c:1954 +#, fuzzy +msgid "Zoom _In" +msgstr "Ablakmérethez _igazítás" + +#: ui/gtk.c:1961 +#, fuzzy +msgid "Zoom _Out" +msgstr "Ablakmérethez _igazítás" + +#: ui/gtk.c:1968 +msgid "Best _Fit" +msgstr "" + +#: ui/gtk.c:1975 msgid "Zoom To _Fit" msgstr "Ablakmérethez _igazítás" -#: ui/gtk.c:1387 +#: ui/gtk.c:1981 msgid "Grab On _Hover" msgstr "Automatikus _elfogás" -#: ui/gtk.c:1390 +#: ui/gtk.c:1984 msgid "_Grab Input" msgstr "_Bemeneti eszközök megragadása" -#: ui/gtk.c:1416 +#: ui/gtk.c:2013 msgid "Show _Tabs" msgstr "_Fülek megjelenítése" -#: ui/gtk.c:1430 +#: ui/gtk.c:2016 +msgid "Detach Tab" +msgstr "" + +#: ui/gtk.c:2028 msgid "_Machine" msgstr "_Gép" -#: ui/gtk.c:1435 +#: ui/gtk.c:2033 msgid "_View" msgstr "_Nézet" diff --git a/qemu/po/it.po b/qemu/po/it.po index e46fb3a8b..a2c8e558c 100644 --- a/qemu/po/it.po +++ b/qemu/po/it.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 1.4.50\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2014-07-29 08:14+0200\n" +"POT-Creation-Date: 2016-04-18 14:50+0100\n" "PO-Revision-Date: 2014-07-29 08:25+0200\n" "Last-Translator: Paolo Bonzini \n" "Language-Team: Italian \n" @@ -16,66 +16,70 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -#: ui/gtk.c:321 +#: ui/gtk.c:273 msgid " - Press Ctrl+Alt+G to release grab" msgstr " - Premere Ctrl+Alt+G per rilasciare l'input" -#: ui/gtk.c:325 +#: ui/gtk.c:277 msgid " [Paused]" msgstr " [Pausa]" -#: ui/gtk.c:1601 +#: ui/gtk.c:1833 msgid "_Pause" msgstr "_Pausa" -#: ui/gtk.c:1607 +#: ui/gtk.c:1839 msgid "_Reset" msgstr "_Reset" -#: ui/gtk.c:1610 +#: ui/gtk.c:1842 msgid "Power _Down" msgstr "_Spegni" -#: ui/gtk.c:1616 +#: ui/gtk.c:1848 msgid "_Quit" msgstr "_Esci" -#: ui/gtk.c:1702 +#: ui/gtk.c:1940 +msgid "_Fullscreen" +msgstr "" + +#: ui/gtk.c:1954 msgid "Zoom _In" msgstr "_Aumenta zoom" -#: ui/gtk.c:1709 +#: ui/gtk.c:1961 msgid "Zoom _Out" msgstr "_Riduci zoom" -#: ui/gtk.c:1716 +#: ui/gtk.c:1968 msgid "Best _Fit" msgstr "A_nnulla zoom" -#: ui/gtk.c:1723 +#: ui/gtk.c:1975 msgid "Zoom To _Fit" msgstr "Adatta alla _finestra" -#: ui/gtk.c:1729 +#: ui/gtk.c:1981 msgid "Grab On _Hover" msgstr "Cattura _automatica input" -#: ui/gtk.c:1732 +#: ui/gtk.c:1984 msgid "_Grab Input" msgstr "_Cattura input" -#: ui/gtk.c:1761 +#: ui/gtk.c:2013 msgid "Show _Tabs" msgstr "Mostra _tab" -#: ui/gtk.c:1764 +#: ui/gtk.c:2016 msgid "Detach Tab" msgstr "_Sposta in una nuova finestra" -#: ui/gtk.c:1778 +#: ui/gtk.c:2028 msgid "_Machine" msgstr "_Macchina virtuale" -#: ui/gtk.c:1783 +#: ui/gtk.c:2033 msgid "_View" msgstr "_Visualizza" diff --git a/qemu/po/messages.po b/qemu/po/messages.po index 26c76bce8..e8f9ccd38 100644 --- a/qemu/po/messages.po +++ b/qemu/po/messages.po @@ -5,9 +5,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: QEMU 1.5.50\n" +"Project-Id-Version: QEMU 2.5.92\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2013-07-05 22:36+0200\n" +"POT-Creation-Date: 2016-04-18 14:50+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -16,46 +16,70 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: ui/gtk.c:214 +#: ui/gtk.c:273 msgid " - Press Ctrl+Alt+G to release grab" msgstr "" -#: ui/gtk.c:218 +#: ui/gtk.c:277 msgid " [Paused]" msgstr "" -#: ui/gtk.c:1318 +#: ui/gtk.c:1833 msgid "_Pause" msgstr "" -#: ui/gtk.c:1324 +#: ui/gtk.c:1839 msgid "_Reset" msgstr "" -#: ui/gtk.c:1327 +#: ui/gtk.c:1842 msgid "Power _Down" msgstr "" -#: ui/gtk.c:1381 +#: ui/gtk.c:1848 +msgid "_Quit" +msgstr "" + +#: ui/gtk.c:1940 +msgid "_Fullscreen" +msgstr "" + +#: ui/gtk.c:1954 +msgid "Zoom _In" +msgstr "" + +#: ui/gtk.c:1961 +msgid "Zoom _Out" +msgstr "" + +#: ui/gtk.c:1968 +msgid "Best _Fit" +msgstr "" + +#: ui/gtk.c:1975 msgid "Zoom To _Fit" msgstr "" -#: ui/gtk.c:1387 +#: ui/gtk.c:1981 msgid "Grab On _Hover" msgstr "" -#: ui/gtk.c:1390 +#: ui/gtk.c:1984 msgid "_Grab Input" msgstr "" -#: ui/gtk.c:1416 +#: ui/gtk.c:2013 msgid "Show _Tabs" msgstr "" -#: ui/gtk.c:1430 +#: ui/gtk.c:2016 +msgid "Detach Tab" +msgstr "" + +#: ui/gtk.c:2028 msgid "_Machine" msgstr "" -#: ui/gtk.c:1435 +#: ui/gtk.c:2033 msgid "_View" msgstr "" diff --git a/qemu/po/tr.po b/qemu/po/tr.po index d712ced9c..ec9912779 100644 --- a/qemu/po/tr.po +++ b/qemu/po/tr.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 1.4.50\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2013-07-05 22:36+0200\n" +"POT-Creation-Date: 2016-04-18 14:50+0100\n" "PO-Revision-Date: 2013-04-22 18:35+0300\n" "Last-Translator: Ozan Çağlayan \n" "Language-Team: Türkçe <>\n" @@ -17,46 +17,72 @@ msgstr "" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Gtranslator 2.91.6\n" -#: ui/gtk.c:214 +#: ui/gtk.c:273 msgid " - Press Ctrl+Alt+G to release grab" msgstr " - Yakalamayı durdurmak için Ctrl+Alt+G tuşlarına basın" -#: ui/gtk.c:218 +#: ui/gtk.c:277 msgid " [Paused]" msgstr " [Duraklatıldı]" -#: ui/gtk.c:1318 +#: ui/gtk.c:1833 msgid "_Pause" msgstr "_Duraklat" -#: ui/gtk.c:1324 +#: ui/gtk.c:1839 msgid "_Reset" msgstr "_Sıfırla" -#: ui/gtk.c:1327 +#: ui/gtk.c:1842 msgid "Power _Down" msgstr "_Kapat" -#: ui/gtk.c:1381 +#: ui/gtk.c:1848 +msgid "_Quit" +msgstr "" + +#: ui/gtk.c:1940 +msgid "_Fullscreen" +msgstr "" + +#: ui/gtk.c:1954 +#, fuzzy +msgid "Zoom _In" +msgstr "Yakınlaş ve Sığ_dır" + +#: ui/gtk.c:1961 +#, fuzzy +msgid "Zoom _Out" +msgstr "Yakınlaş ve Sığ_dır" + +#: ui/gtk.c:1968 +msgid "Best _Fit" +msgstr "" + +#: ui/gtk.c:1975 msgid "Zoom To _Fit" msgstr "Yakınlaş ve Sığ_dır" -#: ui/gtk.c:1387 +#: ui/gtk.c:1981 msgid "Grab On _Hover" msgstr "Ü_zerindeyken Yakala" -#: ui/gtk.c:1390 +#: ui/gtk.c:1984 msgid "_Grab Input" msgstr "Girdiyi _Yakala" -#: ui/gtk.c:1416 +#: ui/gtk.c:2013 msgid "Show _Tabs" msgstr "Se_kmeleri Göster" -#: ui/gtk.c:1430 +#: ui/gtk.c:2016 +msgid "Detach Tab" +msgstr "" + +#: ui/gtk.c:2028 msgid "_Machine" msgstr "_Makine" -#: ui/gtk.c:1435 +#: ui/gtk.c:2033 msgid "_View" msgstr "_Görüntüle" diff --git a/qemu/po/zh_CN.po b/qemu/po/zh_CN.po index 2b1d42e97..b55b6f375 100644 --- a/qemu/po/zh_CN.po +++ b/qemu/po/zh_CN.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 2.2\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2014-07-31 10:03+0800\n" +"POT-Creation-Date: 2016-04-18 14:50+0100\n" "PO-Revision-Date: 2014-07-31 10:00+0800\n" "Last-Translator: Fam Zheng \n" "Language-Team: Chinese \n" @@ -17,70 +17,70 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Lokalize 1.4\n" -#: ui/gtk.c:321 +#: ui/gtk.c:273 msgid " - Press Ctrl+Alt+G to release grab" msgstr " - 按下 Ctrl+Alt+G 取消捕获" -#: ui/gtk.c:325 +#: ui/gtk.c:277 msgid " [Paused]" msgstr " [已暂停]" -#: ui/gtk.c:1601 +#: ui/gtk.c:1833 msgid "_Pause" msgstr "暂停(_P)" -#: ui/gtk.c:1607 +#: ui/gtk.c:1839 msgid "_Reset" msgstr "重置(_R)" -#: ui/gtk.c:1610 +#: ui/gtk.c:1842 msgid "Power _Down" msgstr "关闭电源(_D)" -#: ui/gtk.c:1616 +#: ui/gtk.c:1848 msgid "_Quit" msgstr "退出(_Q)" -#: ui/gtk.c:1692 +#: ui/gtk.c:1940 msgid "_Fullscreen" msgstr "全屏(_F)" -#: ui/gtk.c:1702 +#: ui/gtk.c:1954 msgid "Zoom _In" msgstr "放大(_I)" -#: ui/gtk.c:1709 +#: ui/gtk.c:1961 msgid "Zoom _Out" msgstr "缩小(_O)" -#: ui/gtk.c:1716 +#: ui/gtk.c:1968 msgid "Best _Fit" msgstr "最合适大小(_F)" -#: ui/gtk.c:1723 +#: ui/gtk.c:1975 msgid "Zoom To _Fit" msgstr "缩放以适应大小(_F)" -#: ui/gtk.c:1729 +#: ui/gtk.c:1981 msgid "Grab On _Hover" msgstr "鼠标经过时捕获(_H)" -#: ui/gtk.c:1732 +#: ui/gtk.c:1984 msgid "_Grab Input" msgstr "捕获输入(_G)" -#: ui/gtk.c:1761 +#: ui/gtk.c:2013 msgid "Show _Tabs" msgstr "显示标签页(_T)" -#: ui/gtk.c:1764 +#: ui/gtk.c:2016 msgid "Detach Tab" msgstr "分离标签页" -#: ui/gtk.c:1778 +#: ui/gtk.c:2028 msgid "_Machine" msgstr "虚拟机(_M)" -#: ui/gtk.c:1783 +#: ui/gtk.c:2033 msgid "_View" msgstr "视图(_V)" diff --git a/qemu/qapi-schema.json b/qemu/qapi-schema.json index 4342a08d3..54634c473 100644 --- a/qemu/qapi-schema.json +++ b/qemu/qapi-schema.json @@ -5,6 +5,9 @@ # QAPI common definitions { 'include': 'qapi/common.json' } +# QAPI crypto definitions +{ 'include': 'qapi/crypto.json' } + # QAPI block definitions { 'include': 'qapi/block.json' } @@ -14,6 +17,9 @@ # Tracing commands { 'include': 'qapi/trace.json' } +# QAPI introspection +{ 'include': 'qapi/introspect.json' } + ## # @LostTickPolicy: # @@ -289,8 +295,6 @@ # @format: #optional data encoding (default 'utf8'). # - base64: data must be base64 encoded text. Its binary # decoding gets written. -# Bug: invalid base64 is currently not rejected. -# Whitespace *is* invalid. # - utf8: data's UTF-8 encoding is written # - data itself is always Unicode regardless of format, like # any other string. @@ -424,6 +428,8 @@ # # @active: in the process of doing migration. # +# @postcopy-active: like active, but now in postcopy mode. (since 2.5) +# # @completed: migration is finished. # # @failed: some error occurred during migration process. @@ -433,7 +439,7 @@ ## { 'enum': 'MigrationStatus', 'data': [ 'none', 'setup', 'cancelling', 'cancelled', - 'active', 'completed', 'failed' ] } + 'active', 'postcopy-active', 'completed', 'failed' ] } ## # @MigrationInfo @@ -474,6 +480,10 @@ # may be expensive, but do not actually occur during the iterative # migration rounds themselves. (since 1.6) # +# @x-cpu-throttle-percentage: #optional percentage of time guest cpus are being +# throttled during auto-converge. This is only present when auto-converge +# has started throttling guest cpus. (Since 2.5) +# # Since: 0.14.0 ## { 'struct': 'MigrationInfo', @@ -483,7 +493,8 @@ '*total-time': 'int', '*expected-downtime': 'int', '*downtime': 'int', - '*setup-time': 'int'} } + '*setup-time': 'int', + '*x-cpu-throttle-percentage': 'int'} } ## # @query-migrate @@ -529,11 +540,15 @@ # @auto-converge: If enabled, QEMU will automatically throttle down the guest # to speed up convergence of RAM migration. (since 1.6) # +# @postcopy-ram: Start executing on the migration target before all of RAM has +# been migrated, pulling the remaining pages along as needed. NOTE: If +# the migration fails during postcopy the VM will fail. (since 2.6) +# # Since: 1.2 ## { 'enum': 'MigrationCapability', 'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks', - 'compress', 'events'] } + 'compress', 'events', 'postcopy-ram'] } ## # @MigrationCapabilityStatus @@ -590,10 +605,18 @@ # compression, so set the decompress-threads to the number about 1/4 # of compress-threads is adequate. # +# @x-cpu-throttle-initial: Initial percentage of time guest cpus are throttled +# when migration auto-converge is activated. The +# default value is 20. (Since 2.5) +# +# @x-cpu-throttle-increment: throttle percentage increase each time +# auto-converge detects that migration is not making +# progress. The default value is 10. (Since 2.5) # Since: 2.4 ## { 'enum': 'MigrationParameter', - 'data': ['compress-level', 'compress-threads', 'decompress-threads'] } + 'data': ['compress-level', 'compress-threads', 'decompress-threads', + 'x-cpu-throttle-initial', 'x-cpu-throttle-increment'] } # # @migrate-set-parameters @@ -606,12 +629,21 @@ # # @decompress-threads: decompression thread count # +# @x-cpu-throttle-initial: Initial percentage of time guest cpus are throttled +# when migration auto-converge is activated. The +# default value is 20. (Since 2.5) +# +# @x-cpu-throttle-increment: throttle percentage increase each time +# auto-converge detects that migration is not making +# progress. The default value is 10. (Since 2.5) # Since: 2.4 ## { 'command': 'migrate-set-parameters', 'data': { '*compress-level': 'int', '*compress-threads': 'int', - '*decompress-threads': 'int'} } + '*decompress-threads': 'int', + '*x-cpu-throttle-initial': 'int', + '*x-cpu-throttle-increment': 'int'} } # # @MigrationParameters @@ -622,12 +654,22 @@ # # @decompress-threads: decompression thread count # +# @x-cpu-throttle-initial: Initial percentage of time guest cpus are throttled +# when migration auto-converge is activated. The +# default value is 20. (Since 2.5) +# +# @x-cpu-throttle-increment: throttle percentage increase each time +# auto-converge detects that migration is not making +# progress. The default value is 10. (Since 2.5) +# # Since: 2.4 ## { 'struct': 'MigrationParameters', 'data': { 'compress-level': 'int', 'compress-threads': 'int', - 'decompress-threads': 'int'} } + 'decompress-threads': 'int', + 'x-cpu-throttle-initial': 'int', + 'x-cpu-throttle-increment': 'int'} } ## # @query-migrate-parameters # @@ -659,6 +701,16 @@ 'data': { 'protocol': 'str', 'hostname': 'str', '*port': 'int', '*tls-port': 'int', '*cert-subject': 'str' } } +## +# @migrate-start-postcopy +# +# Followup to a migration command to switch the migration to postcopy mode. +# The postcopy-ram capability must be set before the original migration +# command. +# +# Since: 2.5 +{ 'command': 'migrate-start-postcopy' } + ## # @MouseInfo: # @@ -689,6 +741,17 @@ ## { 'command': 'query-mice', 'returns': ['MouseInfo'] } +## +# @CpuInfoArch: +# +# An enumeration of cpu types that enable additional information during +# @query-cpus. +# +# Since: 2.6 +## +{ 'enum': 'CpuInfoArch', + 'data': ['x86', 'sparc', 'ppc', 'mips', 'tricore', 'other' ] } + ## # @CpuInfo: # @@ -696,37 +759,100 @@ # # @CPU: the index of the virtual CPU # -# @current: this only exists for backwards compatible and should be ignored +# @current: this only exists for backwards compatibility and should be ignored # # @halted: true if the virtual CPU is in the halt state. Halt usually refers # to a processor specific low power mode. # # @qom_path: path to the CPU object in the QOM tree (since 2.4) # -# @pc: #optional If the target is i386 or x86_64, this is the 64-bit instruction -# pointer. -# If the target is Sparc, this is the PC component of the -# instruction pointer. -# -# @nip: #optional If the target is PPC, the instruction pointer -# -# @npc: #optional If the target is Sparc, the NPC component of the instruction -# pointer -# -# @PC: #optional If the target is MIPS, the instruction pointer -# # @thread_id: ID of the underlying host thread # +# @arch: architecture of the cpu, which determines which additional fields +# will be listed (since 2.6) +# # Since: 0.14.0 # # Notes: @halted is a transient state that changes frequently. By the time the # data is sent to the client, the guest may no longer be halted. ## -{ 'struct': 'CpuInfo', - 'data': {'CPU': 'int', 'current': 'bool', 'halted': 'bool', - 'qom_path': 'str', - '*pc': 'int', '*nip': 'int', '*npc': 'int', '*PC': 'int', - 'thread_id': 'int'} } +{ 'union': 'CpuInfo', + 'base': {'CPU': 'int', 'current': 'bool', 'halted': 'bool', + 'qom_path': 'str', 'thread_id': 'int', 'arch': 'CpuInfoArch' }, + 'discriminator': 'arch', + 'data': { 'x86': 'CpuInfoX86', + 'sparc': 'CpuInfoSPARC', + 'ppc': 'CpuInfoPPC', + 'mips': 'CpuInfoMIPS', + 'tricore': 'CpuInfoTricore', + 'other': 'CpuInfoOther' } } + +## +# @CpuInfoX86: +# +# Additional information about a virtual i386 or x86_64 CPU +# +# @pc: the 64-bit instruction pointer +# +# Since 2.6 +## +{ 'struct': 'CpuInfoX86', 'data': { 'pc': 'int' } } + +## +# @CpuInfoSPARC: +# +# Additional information about a virtual SPARC CPU +# +# @pc: the PC component of the instruction pointer +# +# @npc: the NPC component of the instruction pointer +# +# Since 2.6 +## +{ 'struct': 'CpuInfoSPARC', 'data': { 'pc': 'int', 'npc': 'int' } } + +## +# @CpuInfoPPC: +# +# Additional information about a virtual PPC CPU +# +# @nip: the instruction pointer +# +# Since 2.6 +## +{ 'struct': 'CpuInfoPPC', 'data': { 'nip': 'int' } } + +## +# @CpuInfoMIPS: +# +# Additional information about a virtual MIPS CPU +# +# @PC: the instruction pointer +# +# Since 2.6 +## +{ 'struct': 'CpuInfoMIPS', 'data': { 'PC': 'int' } } + +## +# @CpuInfoTricore: +# +# Additional information about a virtual Tricore CPU +# +# @PC: the instruction pointer +# +# Since 2.6 +## +{ 'struct': 'CpuInfoTricore', 'data': { 'PC': 'int' } } + +## +# @CpuInfoOther: +# +# No additional information is available about the virtual CPU +# +# Since 2.6 +# +## +{ 'struct': 'CpuInfoOther', 'data': { } } ## # @query-cpus: @@ -1481,6 +1607,26 @@ { 'struct': 'Abort', 'data': { } } +## +# @ActionCompletionMode +# +# An enumeration of Transactional completion modes. +# +# @individual: Do not attempt to cancel any other Actions if any Actions fail +# after the Transaction request succeeds. All Actions that +# can complete successfully will do so without waiting on others. +# This is the default. +# +# @grouped: If any Action fails after the Transaction succeeds, cancel all +# Actions. Actions do not complete until all Actions are ready to +# complete. May be rejected by Actions that do not support this +# completion mode. +# +# Since: 2.5 +## +{ 'enum': 'ActionCompletionMode', + 'data': [ 'individual', 'grouped' ] } + ## # @TransactionAction # @@ -1493,16 +1639,39 @@ # abort since 1.6 # blockdev-snapshot-internal-sync since 1.7 # blockdev-backup since 2.3 +# blockdev-snapshot since 2.5 +# block-dirty-bitmap-add since 2.5 +# block-dirty-bitmap-clear since 2.5 ## { 'union': 'TransactionAction', 'data': { - 'blockdev-snapshot-sync': 'BlockdevSnapshot', + 'blockdev-snapshot': 'BlockdevSnapshot', + 'blockdev-snapshot-sync': 'BlockdevSnapshotSync', 'drive-backup': 'DriveBackup', 'blockdev-backup': 'BlockdevBackup', 'abort': 'Abort', - 'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal' + 'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal', + 'block-dirty-bitmap-add': 'BlockDirtyBitmapAdd', + 'block-dirty-bitmap-clear': 'BlockDirtyBitmap' } } +## +# @TransactionProperties +# +# Optional arguments to modify the behavior of a Transaction. +# +# @completion-mode: #optional Controls how jobs launched asynchronously by +# Actions will complete or fail as a group. +# See @ActionCompletionMode for details. +# +# Since: 2.5 +## +{ 'struct': 'TransactionProperties', + 'data': { + '*completion-mode': 'ActionCompletionMode' + } +} + ## # @transaction # @@ -1510,8 +1679,12 @@ # operation fails, then the entire set of actions will be abandoned and the # appropriate error returned. # -# List of: -# @TransactionAction: information needed for the respective operation +# @actions: List of @TransactionAction; +# information needed for the respective operations. +# +# @properties: #optional structure of additional options to control the +# execution of the transaction. See @TransactionProperties +# for additional detail. # # Returns: nothing on success # Errors depend on the operations of the transaction @@ -1523,7 +1696,10 @@ # Since 1.1 ## { 'command': 'transaction', - 'data': { 'actions': [ 'TransactionAction' ] } } + 'data': { 'actions': [ 'TransactionAction' ], + '*properties': 'TransactionProperties' + } +} ## # @human-monitor-command: @@ -1632,13 +1808,10 @@ # 1) A primitive type such as 'u8', 'u16', 'bool', 'str', or 'double'. # These types are mapped to the appropriate JSON type. # -# 2) A legacy type in the form 'legacy' where subtype is the -# legacy qdev typename. These types are always treated as strings. -# -# 3) A child type in the form 'child' where subtype is a qdev +# 2) A child type in the form 'child' where subtype is a qdev # device type name. Child properties create the composition tree. # -# 4) A link type in the form 'link' where subtype is a qdev +# 3) A link type in the form 'link' where subtype is a qdev # device type name. Link properties form the device model graph. # # Since: 1.2 @@ -1689,17 +1862,16 @@ # # @property: The property name to read # -# Returns: The property value. The type depends on the property type. legacy<> -# properties are returned as #str. child<> and link<> properties are -# returns as #str pathnames. All integer property types (u8, u16, etc) -# are returned as #int. +# Returns: The property value. The type depends on the property +# type. child<> and link<> properties are returned as #str +# pathnames. All integer property types (u8, u16, etc) are +# returned as #int. # # Since: 1.2 ## { 'command': 'qom-get', 'data': { 'path': 'str', 'property': 'str' }, - 'returns': '**', - 'gen': false } + 'returns': 'any' } ## # @qom-set: @@ -1716,8 +1888,7 @@ # Since: 1.2 ## { 'command': 'qom-set', - 'data': { 'path': 'str', 'property': 'str', 'value': '**' }, - 'gen': false } + 'data': { 'path': 'str', 'property': 'str', 'value': 'any' } } ## # @set_password: @@ -1809,8 +1980,10 @@ # device's password. The behavior of reads and writes to the block # device between when these calls are executed is undefined. # -# Notes: It is strongly recommended that this interface is not used especially -# for changing block devices. +# Notes: This interface is deprecated, and it is strongly recommended that you +# avoid using it. For changing block devices, use +# blockdev-change-medium; for changing VNC parameters, use +# change-vnc-password. # # Since: 0.14.0 ## @@ -1950,7 +2123,7 @@ # # Remove a device from a guest # -# @id: the name of the device +# @id: the name or QOM path of the device # # Returns: Nothing on success # If @id is not a valid device, DeviceNotFound @@ -1988,8 +2161,7 @@ # @dump-guest-memory # # Dump guest's memory to vmcore. It is a synchronous operation that can take -# very long depending on the amount of guest memory. This command is only -# supported on i386 and x86_64. +# very long depending on the amount of guest memory. # # @paging: if true, do paging to get guest's memory mapping. This allows # using gdb to process the core file. @@ -2005,6 +2177,7 @@ # 2. The guest can be in real-mode even if paging is enabled. For # example, the guest uses ACPI to sleep, and ACPI sleep state # goes in real-mode +# 3. Currently only supported on i386 and x86_64. # # @protocol: the filename or file descriptor of the vmcore. The supported # protocols are: @@ -2014,6 +2187,10 @@ # 2. fd: the protocol starts with "fd:", and the following string # is the fd's name. # +# @detach: #optional if true, QMP will return immediately rather than +# waiting for the dump to finish. The user can track progress +# using "query-dump". (since 2.6). +# # @begin: #optional if specified, the starting physical address. # # @length: #optional if specified, the memory size, in bytes. If you don't @@ -2030,8 +2207,56 @@ # Since: 1.2 ## { 'command': 'dump-guest-memory', - 'data': { 'paging': 'bool', 'protocol': 'str', '*begin': 'int', - '*length': 'int', '*format': 'DumpGuestMemoryFormat' } } + 'data': { 'paging': 'bool', 'protocol': 'str', '*detach': 'bool', + '*begin': 'int', '*length': 'int', + '*format': 'DumpGuestMemoryFormat'} } + +## +# @DumpStatus +# +# Describe the status of a long-running background guest memory dump. +# +# @none: no dump-guest-memory has started yet. +# +# @active: there is one dump running in background. +# +# @completed: the last dump has finished successfully. +# +# @failed: the last dump has failed. +# +# Since 2.6 +## +{ 'enum': 'DumpStatus', + 'data': [ 'none', 'active', 'completed', 'failed' ] } + +## +# @DumpQueryResult +# +# The result format for 'query-dump'. +# +# @status: enum of @DumpStatus, which shows current dump status +# +# @completed: bytes written in latest dump (uncompressed) +# +# @total: total bytes to be written in latest dump (uncompressed) +# +# Since 2.6 +## +{ 'struct': 'DumpQueryResult', + 'data': { 'status': 'DumpStatus', + 'completed': 'int', + 'total': 'int' } } + +## +# @query-dump +# +# Query latest dump status. +# +# Returns: A @DumpStatus object showing the dump status. +# +# Since: 2.6 +## +{ 'command': 'query-dump', 'returns': 'DumpQueryResult' } ## # @DumpGuestMemoryCapability: @@ -2057,6 +2282,20 @@ { 'command': 'query-dump-guest-memory-capability', 'returns': 'DumpGuestMemoryCapability' } +## +# @dump-skeys +# +# Dump guest's storage keys +# +# @filename: the path to the file to dump to +# +# This command is only supported on s390 architecture. +# +# Since: 2.5 +## +{ 'command': 'dump-skeys', + 'data': { 'filename': 'str' } } + ## # @netdev_add: # @@ -2067,11 +2306,12 @@ # # @id: the name of the new network backend # -# @props: #optional a list of properties to be passed to the backend in -# the format 'name=value', like 'ifname=tap0,script=no' +# Additional arguments depend on the type. # -# Notes: The semantics of @props is not well defined. Future commands will be -# introduced that provide stronger typing for backend creation. +# TODO This command effectively bypasses QAPI completely due to its +# "additional arguments" business. It shouldn't have been added to +# the schema in this form. It should be qapified properly, or +# replaced by a properly qapified command. # # Since: 0.14.0 # @@ -2079,8 +2319,8 @@ # If @type is not a valid network backend, DeviceNotFound ## { 'command': 'netdev_add', - 'data': {'type': 'str', 'id': 'str', '*props': '**'}, - 'gen': false } + 'data': {'type': 'str', 'id': 'str'}, + 'gen': false } # so we can get the additional arguments ## # @netdev_del: @@ -2113,8 +2353,7 @@ # Since: 2.0 ## { 'command': 'object-add', - 'data': {'qom-type': 'str', 'id': 'str', '*props': '**'}, - 'gen': false } + 'data': {'qom-type': 'str', 'id': 'str', '*props': 'any'} } ## # @object-del: @@ -2186,9 +2425,18 @@ # # @restrict: #optional isolate the guest from the host # +# @ipv4: #optional whether to support IPv4, default true for enabled +# (since 2.6) +# +# @ipv6: #optional whether to support IPv6, default true for enabled +# (since 2.6) +# # @ip: #optional legacy parameter, use net= instead # -# @net: #optional IP address and optional netmask +# @net: #optional IP network address that the guest will see, in the +# form addr[/netmask] The netmask is optional, and can be +# either in the form a.b.c.d or as a number of valid top-most +# bits. Default is 10.0.2.0/24. # # @host: #optional guest-visible address of the host # @@ -2204,6 +2452,18 @@ # @dnssearch: #optional list of DNS suffixes to search, passed as DHCP option # to the guest # +# @ipv6-prefix: #optional IPv6 network prefix (default is fec0::) (since +# 2.6). The network prefix is given in the usual +# hexadecimal IPv6 address notation. +# +# @ipv6-prefixlen: #optional IPv6 network prefix length (default is 64) +# (since 2.6) +# +# @ipv6-host: #optional guest-visible IPv6 address of the host (since 2.6) +# +# @ipv6-dns: #optional guest-visible IPv6 address of the virtual +# nameserver (since 2.6) +# # @smb: #optional root directory of the built-in SMB server # # @smbserver: #optional IP address of the built-in SMB server @@ -2219,6 +2479,8 @@ 'data': { '*hostname': 'str', '*restrict': 'bool', + '*ipv4': 'bool', + '*ipv6': 'bool', '*ip': 'str', '*net': 'str', '*host': 'str', @@ -2227,6 +2489,10 @@ '*dhcpstart': 'str', '*dns': 'str', '*dnssearch': ['String'], + '*ipv6-prefix': 'str', + '*ipv6-prefixlen': 'int', + '*ipv6-host': 'str', + '*ipv6-dns': 'str', '*smb': 'str', '*smbserver': 'str', '*hostfwd': ['String'], @@ -2466,12 +2732,16 @@ # # @vhostforce: #optional vhost on for non-MSIX virtio guests (default: false). # +# @queues: #optional number of queues to be created for multiqueue vhost-user +# (default: 1) (Since 2.5) +# # Since 2.1 ## { 'struct': 'NetdevVhostUserOptions', 'data': { 'chardev': 'str', - '*vhostforce': 'bool' } } + '*vhostforce': 'bool', + '*queues': 'int' } } ## # @NetClientOptions @@ -2536,6 +2806,26 @@ 'id': 'str', 'opts': 'NetClientOptions' } } +## +# @NetFilterDirection +# +# Indicates whether a netfilter is attached to a netdev's transmit queue or +# receive queue or both. +# +# @all: the filter is attached both to the receive and the transmit +# queue of the netdev (default). +# +# @rx: the filter is attached to the receive queue of the netdev, +# where it will receive packets sent to the netdev. +# +# @tx: the filter is attached to the transmit queue of the netdev, +# where it will receive packets sent by the netdev. +# +# Since 2.5 +## +{ 'enum': 'NetFilterDirection', + 'data': [ 'all', 'rx', 'tx' ] } + ## # @InetSocketAddress # @@ -2807,6 +3097,7 @@ # # 'unmapped' and 'pause' since 2.0 # 'ro' and 'kp_comma' since 2.4 +# 'kp_equals' and 'power' since 2.6 ## { 'enum': 'QKeyCode', 'data': [ 'unmapped', @@ -2825,7 +3116,7 @@ 'left', 'up', 'down', 'right', 'insert', 'delete', 'stop', 'again', 'props', 'undo', 'front', 'copy', 'open', 'paste', 'find', 'cut', 'lf', 'help', 'meta_l', 'meta_r', 'compose', 'pause', 'ro', - 'kp_comma' ] } + 'kp_comma', 'kp_equals', 'power' ] } ## # @KeyValue @@ -2874,6 +3165,21 @@ ## { 'command': 'screendump', 'data': {'filename': 'str'} } + +## +# @ChardevCommon: +# +# Configuration shared across all chardev backends +# +# @logfile: #optional The name of a logfile to save output +# @logappend: #optional true to append instead of truncate +# (default to false to truncate) +# +# Since: 2.6 +## +{ 'struct': 'ChardevCommon', 'data': { '*logfile': 'str', + '*logappend': 'bool' } } + ## # @ChardevFile: # @@ -2881,11 +3187,15 @@ # # @in: #optional The name of the input file # @out: The name of the output file +# @append: #optional Open the file in append mode (default false to +# truncate) (Since 2.6) # # Since: 1.4 ## { 'struct': 'ChardevFile', 'data': { '*in' : 'str', - 'out' : 'str' } } + 'out' : 'str', + '*append': 'bool' }, + 'base': 'ChardevCommon' } ## # @ChardevHostdev: @@ -2898,7 +3208,8 @@ # # Since: 1.4 ## -{ 'struct': 'ChardevHostdev', 'data': { 'device' : 'str' } } +{ 'struct': 'ChardevHostdev', 'data': { 'device' : 'str' }, + 'base': 'ChardevCommon' } ## # @ChardevSocket: @@ -2907,6 +3218,7 @@ # # @addr: socket address to listen on (server=true) # or connect to (server=false) +# @tls-creds: #optional the ID of the TLS credentials object (since 2.6) # @server: #optional create server socket (default: true) # @wait: #optional wait for incoming connection on server # sockets (default: false). @@ -2921,11 +3233,13 @@ # Since: 1.4 ## { 'struct': 'ChardevSocket', 'data': { 'addr' : 'SocketAddress', + '*tls-creds' : 'str', '*server' : 'bool', '*wait' : 'bool', '*nodelay' : 'bool', '*telnet' : 'bool', - '*reconnect' : 'int' } } + '*reconnect' : 'int' }, + 'base': 'ChardevCommon' } ## # @ChardevUdp: @@ -2938,7 +3252,8 @@ # Since: 1.5 ## { 'struct': 'ChardevUdp', 'data': { 'remote' : 'SocketAddress', - '*local' : 'SocketAddress' } } + '*local' : 'SocketAddress' }, + 'base': 'ChardevCommon' } ## # @ChardevMux: @@ -2949,7 +3264,8 @@ # # Since: 1.5 ## -{ 'struct': 'ChardevMux', 'data': { 'chardev' : 'str' } } +{ 'struct': 'ChardevMux', 'data': { 'chardev' : 'str' }, + 'base': 'ChardevCommon' } ## # @ChardevStdio: @@ -2962,7 +3278,9 @@ # # Since: 1.5 ## -{ 'struct': 'ChardevStdio', 'data': { '*signal' : 'bool' } } +{ 'struct': 'ChardevStdio', 'data': { '*signal' : 'bool' }, + 'base': 'ChardevCommon' } + ## # @ChardevSpiceChannel: @@ -2973,7 +3291,8 @@ # # Since: 1.5 ## -{ 'struct': 'ChardevSpiceChannel', 'data': { 'type' : 'str' } } +{ 'struct': 'ChardevSpiceChannel', 'data': { 'type' : 'str' }, + 'base': 'ChardevCommon' } ## # @ChardevSpicePort: @@ -2984,7 +3303,8 @@ # # Since: 1.5 ## -{ 'struct': 'ChardevSpicePort', 'data': { 'fqdn' : 'str' } } +{ 'struct': 'ChardevSpicePort', 'data': { 'fqdn' : 'str' }, + 'base': 'ChardevCommon' } ## # @ChardevVC: @@ -3001,7 +3321,8 @@ { 'struct': 'ChardevVC', 'data': { '*width' : 'int', '*height' : 'int', '*cols' : 'int', - '*rows' : 'int' } } + '*rows' : 'int' }, + 'base': 'ChardevCommon' } ## # @ChardevRingbuf: @@ -3012,7 +3333,8 @@ # # Since: 1.5 ## -{ 'struct': 'ChardevRingbuf', 'data': { '*size' : 'int' } } +{ 'struct': 'ChardevRingbuf', 'data': { '*size' : 'int' }, + 'base': 'ChardevCommon' } ## # @ChardevBackend: @@ -3021,22 +3343,20 @@ # # Since: 1.4 (testdev since 2.2) ## -{ 'struct': 'ChardevDummy', 'data': { } } - { 'union': 'ChardevBackend', 'data': { 'file' : 'ChardevFile', 'serial' : 'ChardevHostdev', 'parallel': 'ChardevHostdev', 'pipe' : 'ChardevHostdev', 'socket' : 'ChardevSocket', 'udp' : 'ChardevUdp', - 'pty' : 'ChardevDummy', - 'null' : 'ChardevDummy', + 'pty' : 'ChardevCommon', + 'null' : 'ChardevCommon', 'mux' : 'ChardevMux', - 'msmouse': 'ChardevDummy', - 'braille': 'ChardevDummy', - 'testdev': 'ChardevDummy', + 'msmouse': 'ChardevCommon', + 'braille': 'ChardevCommon', + 'testdev': 'ChardevCommon', 'stdio' : 'ChardevStdio', - 'console': 'ChardevDummy', + 'console': 'ChardevCommon', 'spicevmc' : 'ChardevSpiceChannel', 'spiceport' : 'ChardevSpicePort', 'vc' : 'ChardevVC', @@ -3344,6 +3664,17 @@ 'cpuid-register': 'X86CPURegister32', 'features': 'int' } } +## +# @DummyForceArrays +# +# Not used by QMP; hack to let us use X86CPUFeatureWordInfoList internally +# +# Since 2.5 +## +{ 'struct': 'DummyForceArrays', + 'data': { 'unused': ['X86CPUFeatureWordInfo'] } } + + ## # @RxState: # @@ -3431,17 +3762,17 @@ # Since: 2.0 ## { 'enum' : 'InputButton', - 'data' : [ 'Left', 'Middle', 'Right', 'WheelUp', 'WheelDown' ] } + 'data' : [ 'left', 'middle', 'right', 'wheel-up', 'wheel-down' ] } ## -# @InputButton +# @InputAxis # # Position axis of a pointer input device (mouse, tablet). # # Since: 2.0 ## { 'enum' : 'InputAxis', - 'data' : [ 'X', 'Y' ] } + 'data' : [ 'x', 'y' ] } ## # @InputKeyEvent @@ -3505,35 +3836,34 @@ 'abs' : 'InputMoveEvent' } } ## -# @x-input-send-event +# @input-send-event # # Send input event(s) to guest. # -# @console: #optional console to send event(s) to. -# This parameter can be used to send the input event to -# specific input devices in case (a) multiple input devices -# of the same kind are added to the virtual machine and (b) -# you have configured input routing (see docs/multiseat.txt) -# for those input devices. If input routing is not -# configured this parameter has no effect. -# If @console is missing, only devices that aren't associated -# with a console are admissible. -# If @console is specified, it must exist, and both devices -# associated with that console and devices not associated with a -# console are admissible, but the former take precedence. - -# +# @device: #optional display device to send event(s) to. +# @head: #optional head to send event(s) to, in case the +# display device supports multiple scanouts. # @events: List of InputEvent union. # # Returns: Nothing on success. # -# Since: 2.2 -# -# Note: this command is experimental, and not a stable API. +# The @display and @head parameters can be used to send the input +# event to specific input devices in case (a) multiple input devices +# of the same kind are added to the virtual machine and (b) you have +# configured input routing (see docs/multiseat.txt) for those input +# devices. The parameters work exactly like the device and head +# properties of input devices. If @device is missing, only devices +# that have no input routing config are admissible. If @device is +# specified, both input devices with and without input routing config +# are admissible, but devices with input routing config take +# precedence. # +# Since: 2.6 ## -{ 'command': 'x-input-send-event', - 'data': { '*console':'int', 'events': [ 'InputEvent' ] } } +{ 'command': 'input-send-event', + 'data': { '*device': 'str', + '*head' : 'int', + 'events' : [ 'InputEvent' ] } } ## # @NumaOptions @@ -3794,3 +4124,57 @@ # Rocker ethernet network switch { 'include': 'qapi/rocker.json' } + +## +# ReplayMode: +# +# Mode of the replay subsystem. +# +# @none: normal execution mode. Replay or record are not enabled. +# +# @record: record mode. All non-deterministic data is written into the +# replay log. +# +# @play: replay mode. Non-deterministic data required for system execution +# is read from the log. +# +# Since: 2.5 +## +{ 'enum': 'ReplayMode', + 'data': [ 'none', 'record', 'play' ] } + +## +# @GICCapability: +# +# The struct describes capability for a specific GIC (Generic +# Interrupt Controller) version. These bits are not only decided by +# QEMU/KVM software version, but also decided by the hardware that +# the program is running upon. +# +# @version: version of GIC to be described. Currently, only 2 and 3 +# are supported. +# +# @emulated: whether current QEMU/hardware supports emulated GIC +# device in user space. +# +# @kernel: whether current QEMU/hardware supports hardware +# accelerated GIC device in kernel. +# +# Since: 2.6 +## +{ 'struct': 'GICCapability', + 'data': { 'version': 'int', + 'emulated': 'bool', + 'kernel': 'bool' } } + +## +# @query-gic-capabilities: +# +# This command is ARM-only. It will return a list of GICCapability +# objects that describe its capability bits. +# +# Returns: a list of GICCapability objects. +# +# Since: 2.6 +## +{ 'command': 'query-gic-capabilities', 'returns': ['GICCapability'] } diff --git a/qemu/qapi/block-core.json b/qemu/qapi/block-core.json index 7b2efb867..1d09079cc 100644 --- a/qemu/qapi/block-core.json +++ b/qemu/qapi/block-core.json @@ -185,6 +185,33 @@ '*total-clusters': 'int', '*allocated-clusters': 'int', '*fragmented-clusters': 'int', '*compressed-clusters': 'int' } } +## +# @MapEntry: +# +# Mapping information from a virtual block range to a host file range +# +# @start: the start byte of the mapped virtual range +# +# @length: the number of bytes of the mapped virtual range +# +# @data: whether the mapped range has data +# +# @zero: whether the virtual blocks are zeroed +# +# @depth: the depth of the mapping +# +# @offset: #optional the offset in file that the virtual sectors are mapped to +# +# @filename: #optional filename that is referred to by @offset +# +# Since: 2.6 +# +## +{ 'struct': 'MapEntry', + 'data': {'start': 'int', 'length': 'int', 'data': 'bool', + 'zero': 'bool', 'depth': 'int', '*offset': 'int', + '*filename': 'str' } } + ## # @BlockdevCacheInfo # @@ -215,10 +242,12 @@ # @drv: the name of the block format used to open the backing device. As of # 0.14.0 this can be: 'blkdebug', 'bochs', 'cloop', 'cow', 'dmg', # 'file', 'file', 'ftp', 'ftps', 'host_cdrom', 'host_device', -# 'host_floppy', 'http', 'https', 'nbd', 'parallels', 'qcow', +# 'http', 'https', 'luks', 'nbd', 'parallels', 'qcow', # 'qcow2', 'raw', 'tftp', 'vdi', 'vmdk', 'vpc', 'vvfat' # 2.2: 'archipelago' added, 'cow' dropped # 2.3: 'host_floppy' deprecated +# 2.5: 'host_floppy' dropped +# 2.6: 'luks' added # # @backing_file: #optional the name of the backing file (for copy-on-write) # @@ -245,17 +274,41 @@ # # @image: the info of image used (since: 1.6) # -# @bps_max: #optional total max in bytes (Since 1.7) +# @bps_max: #optional total throughput limit during bursts, +# in bytes (Since 1.7) +# +# @bps_rd_max: #optional read throughput limit during bursts, +# in bytes (Since 1.7) +# +# @bps_wr_max: #optional write throughput limit during bursts, +# in bytes (Since 1.7) # -# @bps_rd_max: #optional read max in bytes (Since 1.7) +# @iops_max: #optional total I/O operations per second during bursts, +# in bytes (Since 1.7) # -# @bps_wr_max: #optional write max in bytes (Since 1.7) +# @iops_rd_max: #optional read I/O operations per second during bursts, +# in bytes (Since 1.7) # -# @iops_max: #optional total I/O operations max (Since 1.7) +# @iops_wr_max: #optional write I/O operations per second during bursts, +# in bytes (Since 1.7) # -# @iops_rd_max: #optional read I/O operations max (Since 1.7) +# @bps_max_length: #optional maximum length of the @bps_max burst +# period, in seconds. (Since 2.6) # -# @iops_wr_max: #optional write I/O operations max (Since 1.7) +# @bps_rd_max_length: #optional maximum length of the @bps_rd_max +# burst period, in seconds. (Since 2.6) +# +# @bps_wr_max_length: #optional maximum length of the @bps_wr_max +# burst period, in seconds. (Since 2.6) +# +# @iops_max_length: #optional maximum length of the @iops burst +# period, in seconds. (Since 2.6) +# +# @iops_rd_max_length: #optional maximum length of the @iops_rd_max +# burst period, in seconds. (Since 2.6) +# +# @iops_wr_max_length: #optional maximum length of the @iops_wr_max +# burst period, in seconds. (Since 2.6) # # @iops_size: #optional an I/O size in bytes (Since 1.7) # @@ -280,6 +333,9 @@ '*bps_max': 'int', '*bps_rd_max': 'int', '*bps_wr_max': 'int', '*iops_max': 'int', '*iops_rd_max': 'int', '*iops_wr_max': 'int', + '*bps_max_length': 'int', '*bps_rd_max_length': 'int', + '*bps_wr_max_length': 'int', '*iops_max_length': 'int', + '*iops_rd_max_length': 'int', '*iops_wr_max_length': 'int', '*iops_size': 'int', '*group': 'str', 'cache': 'BlockdevCacheInfo', 'write_threshold': 'int' } } @@ -381,8 +437,8 @@ # @locked: True if the guest has locked this device from having its media # removed # -# @tray_open: #optional True if the device has a tray and it is open -# (only present if removable is true) +# @tray_open: #optional True if the device's tray is open +# (only present if it has a tray) # # @dirty-bitmaps: #optional dirty bitmaps information (only present if the # driver has one or more dirty bitmaps) (Since 2.0) @@ -413,6 +469,59 @@ ## { 'command': 'query-block', 'returns': ['BlockInfo'] } + +## +# @BlockDeviceTimedStats: +# +# Statistics of a block device during a given interval of time. +# +# @interval_length: Interval used for calculating the statistics, +# in seconds. +# +# @min_rd_latency_ns: Minimum latency of read operations in the +# defined interval, in nanoseconds. +# +# @min_wr_latency_ns: Minimum latency of write operations in the +# defined interval, in nanoseconds. +# +# @min_flush_latency_ns: Minimum latency of flush operations in the +# defined interval, in nanoseconds. +# +# @max_rd_latency_ns: Maximum latency of read operations in the +# defined interval, in nanoseconds. +# +# @max_wr_latency_ns: Maximum latency of write operations in the +# defined interval, in nanoseconds. +# +# @max_flush_latency_ns: Maximum latency of flush operations in the +# defined interval, in nanoseconds. +# +# @avg_rd_latency_ns: Average latency of read operations in the +# defined interval, in nanoseconds. +# +# @avg_wr_latency_ns: Average latency of write operations in the +# defined interval, in nanoseconds. +# +# @avg_flush_latency_ns: Average latency of flush operations in the +# defined interval, in nanoseconds. +# +# @avg_rd_queue_depth: Average number of pending read operations +# in the defined interval. +# +# @avg_wr_queue_depth: Average number of pending write operations +# in the defined interval. +# +# Since: 2.5 +## + +{ 'struct': 'BlockDeviceTimedStats', + 'data': { 'interval_length': 'int', 'min_rd_latency_ns': 'int', + 'max_rd_latency_ns': 'int', 'avg_rd_latency_ns': 'int', + 'min_wr_latency_ns': 'int', 'max_wr_latency_ns': 'int', + 'avg_wr_latency_ns': 'int', 'min_flush_latency_ns': 'int', + 'max_flush_latency_ns': 'int', 'avg_flush_latency_ns': 'int', + 'avg_rd_queue_depth': 'number', 'avg_wr_queue_depth': 'number' } } + ## # @BlockDeviceStats: # @@ -447,6 +556,37 @@ # @wr_merged: Number of write requests that have been merged into another # request (Since 2.3). # +# @idle_time_ns: #optional Time since the last I/O operation, in +# nanoseconds. If the field is absent it means that +# there haven't been any operations yet (Since 2.5). +# +# @failed_rd_operations: The number of failed read operations +# performed by the device (Since 2.5) +# +# @failed_wr_operations: The number of failed write operations +# performed by the device (Since 2.5) +# +# @failed_flush_operations: The number of failed flush operations +# performed by the device (Since 2.5) +# +# @invalid_rd_operations: The number of invalid read operations +# performed by the device (Since 2.5) +# +# @invalid_wr_operations: The number of invalid write operations +# performed by the device (Since 2.5) +# +# @invalid_flush_operations: The number of invalid flush operations +# performed by the device (Since 2.5) +# +# @account_invalid: Whether invalid operations are included in the +# last access statistics (Since 2.5) +# +# @account_failed: Whether failed operations are included in the +# latency and last access statistics (Since 2.5) +# +# @timed_stats: Statistics specific to the set of previously defined +# intervals of time (Since 2.5) +# # Since: 0.14.0 ## { 'struct': 'BlockDeviceStats', @@ -454,7 +594,12 @@ 'wr_operations': 'int', 'flush_operations': 'int', 'flush_total_time_ns': 'int', 'wr_total_time_ns': 'int', 'rd_total_time_ns': 'int', 'wr_highest_offset': 'int', - 'rd_merged': 'int', 'wr_merged': 'int' } } + 'rd_merged': 'int', 'wr_merged': 'int', '*idle_time_ns': 'int', + 'failed_rd_operations': 'int', 'failed_wr_operations': 'int', + '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'] } } ## # @BlockStats: @@ -681,7 +826,7 @@ 'data': [ 'existing', 'absolute-paths' ] } ## -# @BlockdevSnapshot +# @BlockdevSnapshotSync # # Either @device or @node-name must be set but not both. # @@ -698,11 +843,26 @@ # @mode: #optional whether and how QEMU should create a new image, default is # 'absolute-paths'. ## -{ 'struct': 'BlockdevSnapshot', +{ 'struct': 'BlockdevSnapshotSync', 'data': { '*device': 'str', '*node-name': 'str', 'snapshot-file': 'str', '*snapshot-node-name': 'str', '*format': 'str', '*mode': 'NewImageMode' } } +## +# @BlockdevSnapshot +# +# @node: device or node name that will have a snapshot created. +# +# @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). +# +# Since 2.5 +## +{ 'struct': 'BlockdevSnapshot', + 'data': { 'node': 'str', 'overlay': 'str' } } + ## # @DriveBackup # @@ -789,7 +949,7 @@ # # Generates a synchronous snapshot of a block device. # -# For the arguments, see the documentation of BlockdevSnapshot. +# For the arguments, see the documentation of BlockdevSnapshotSync. # # Returns: nothing on success # If @device is not a valid block device, DeviceNotFound @@ -797,6 +957,19 @@ # Since 0.14.0 ## { 'command': 'blockdev-snapshot-sync', + 'data': 'BlockdevSnapshotSync' } + + +## +# @blockdev-snapshot +# +# Generates a snapshot of a block device. +# +# For the arguments, see the documentation of BlockdevSnapshot. +# +# Since 2.5 +## +{ 'command': 'blockdev-snapshot', 'data': 'BlockdevSnapshot' } ## @@ -1065,6 +1238,54 @@ { 'command': 'block-dirty-bitmap-clear', 'data': 'BlockDirtyBitmap' } +## +# @blockdev-mirror +# +# Start mirroring a block device's writes to a new destination. +# +# @device: the name of the device whose writes should be mirrored. +# +# @target: the id or node-name of the block device to mirror to. This mustn't be +# attached to guest. +# +# @replaces: #optional with sync=full graph node name to be replaced by the new +# image when a whole image copy is done. This can be used to repair +# broken Quorum files. +# +# @speed: #optional the maximum speed, in bytes per second +# +# @sync: what parts of the disk image should be copied to the destination +# (all the disk, only the sectors allocated in the topmost image, or +# only new I/O). +# +# @granularity: #optional granularity of the dirty bitmap, default is 64K +# if the image format doesn't have clusters, 4K if the clusters +# are smaller than that, else the cluster size. Must be a +# power of 2 between 512 and 64M +# +# @buf-size: #optional maximum amount of data in flight from source to +# target +# +# @on-source-error: #optional the action to take on an error on the source, +# default 'report'. 'stop' and 'enospc' can only be used +# if the block device supports io-status (see BlockInfo). +# +# @on-target-error: #optional the action to take on an error on the target, +# default 'report' (no limitations, since this applies to +# a different block device than @device). +# +# Returns: nothing on success. +# +# Since 2.6 +## +{ 'command': 'blockdev-mirror', + 'data': { 'device': 'str', 'target': 'str', + '*replaces': 'str', + 'sync': 'MirrorSyncMode', + '*speed': 'int', '*granularity': 'uint32', + '*buf-size': 'int', '*on-source-error': 'BlockdevOnError', + '*on-target-error': 'BlockdevOnError' } } + ## # @block_set_io_throttle: # @@ -1101,21 +1322,57 @@ # # @iops: total I/O operations per second # -# @ops_rd: read I/O operations per second +# @iops_rd: read I/O operations per second # # @iops_wr: write I/O operations per second # -# @bps_max: #optional total max in bytes (Since 1.7) +# @bps_max: #optional total throughput limit during bursts, +# in bytes (Since 1.7) # -# @bps_rd_max: #optional read max in bytes (Since 1.7) +# @bps_rd_max: #optional read throughput limit during bursts, +# in bytes (Since 1.7) # -# @bps_wr_max: #optional write max in bytes (Since 1.7) +# @bps_wr_max: #optional write throughput limit during bursts, +# in bytes (Since 1.7) # -# @iops_max: #optional total I/O operations max (Since 1.7) +# @iops_max: #optional total I/O operations per second during bursts, +# in bytes (Since 1.7) # -# @iops_rd_max: #optional read I/O operations max (Since 1.7) +# @iops_rd_max: #optional read I/O operations per second during bursts, +# in bytes (Since 1.7) # -# @iops_wr_max: #optional write I/O operations max (Since 1.7) +# @iops_wr_max: #optional write I/O operations per second during bursts, +# in bytes (Since 1.7) +# +# @bps_max_length: #optional maximum length of the @bps_max burst +# period, in seconds. It must only +# be set if @bps_max is set as well. +# Defaults to 1. (Since 2.6) +# +# @bps_rd_max_length: #optional maximum length of the @bps_rd_max +# burst period, in seconds. It must only +# be set if @bps_rd_max is set as well. +# Defaults to 1. (Since 2.6) +# +# @bps_wr_max_length: #optional maximum length of the @bps_wr_max +# burst period, in seconds. It must only +# be set if @bps_wr_max is set as well. +# Defaults to 1. (Since 2.6) +# +# @iops_max_length: #optional maximum length of the @iops burst +# period, in seconds. It must only +# be set if @iops_max is set as well. +# Defaults to 1. (Since 2.6) +# +# @iops_rd_max_length: #optional maximum length of the @iops_rd_max +# burst period, in seconds. It must only +# be set if @iops_rd_max is set as well. +# Defaults to 1. (Since 2.6) +# +# @iops_wr_max_length: #optional maximum length of the @iops_wr_max +# burst period, in seconds. It must only +# be set if @iops_wr_max is set as well. +# Defaults to 1. (Since 2.6) # # @iops_size: #optional an I/O size in bytes (Since 1.7) # @@ -1132,6 +1389,9 @@ '*bps_max': 'int', '*bps_rd_max': 'int', '*bps_wr_max': 'int', '*iops_max': 'int', '*iops_rd_max': 'int', '*iops_wr_max': 'int', + '*bps_max_length': 'int', '*bps_rd_max_length': 'int', + '*bps_wr_max_length': 'int', '*iops_max_length': 'int', + '*iops_rd_max_length': 'int', '*iops_wr_max_length': 'int', '*iops_size': 'int', '*group': 'str' } } ## @@ -1355,7 +1615,6 @@ # # Includes cache-related options for block devices # -# @writeback: #optional enables writeback mode for any caches (default: true) # @direct: #optional enables use of O_DIRECT (bypass the host page cache; # default: false) # @no-flush: #optional ignore any flush requests for the device (default: @@ -1364,8 +1623,7 @@ # Since: 1.7 ## { 'struct': 'BlockdevCacheOptions', - 'data': { '*writeback': 'bool', - '*direct': 'bool', + 'data': { '*direct': 'bool', '*no-flush': 'bool' } } ## @@ -1373,55 +1631,17 @@ # # Drivers that are supported in block device operations. # -# @host_device, @host_cdrom, @host_floppy: Since 2.1 -# @host_floppy: deprecated since 2.3 +# @host_device, @host_cdrom: Since 2.1 # # Since: 2.0 ## { 'enum': 'BlockdevDriver', 'data': [ 'archipelago', 'blkdebug', 'blkverify', 'bochs', 'cloop', 'dmg', 'file', 'ftp', 'ftps', 'host_cdrom', 'host_device', - 'host_floppy', 'http', 'https', 'null-aio', 'null-co', 'parallels', + 'http', 'https', 'luks', 'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'tftp', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] } -## -# @BlockdevOptionsBase -# -# Options that are available for all block devices, independent of the block -# driver. -# -# @driver: block driver name -# @id: #optional id by which the new block device can be referred to. -# This is a required option on the top level of blockdev-add, and -# currently not allowed on any other level. -# @node-name: #optional the name of a block driver state node (Since 2.0) -# @discard: #optional discard-related options (default: ignore) -# @cache: #optional cache-related options -# @aio: #optional AIO backend (default: threads) -# @rerror: #optional how to handle read errors on the device -# (default: report) -# @werror: #optional how to handle write errors on the device -# (default: enospc) -# @read-only: #optional whether the block device should be read-only -# (default: false) -# @detect-zeroes: #optional detect and optimize zero writes (Since 2.1) -# (default: off) -# -# Since: 1.7 -## -{ 'struct': 'BlockdevOptionsBase', - 'data': { 'driver': 'BlockdevDriver', - '*id': 'str', - '*node-name': 'str', - '*discard': 'BlockdevDiscardOptions', - '*cache': 'BlockdevCacheOptions', - '*aio': 'BlockdevAioOptions', - '*rerror': 'BlockdevOnError', - '*werror': 'BlockdevOnError', - '*read-only': 'bool', - '*detect-zeroes': 'BlockdevDetectZeroesOptions' } } - ## # @BlockdevOptionsFile # @@ -1484,6 +1704,22 @@ { 'struct': 'BlockdevOptionsGenericFormat', 'data': { 'file': 'BlockdevRef' } } +## +# @BlockdevOptionsLUKS +# +# Driver specific block device options for LUKS. +# +# @key-secret: #optional the ID of a QCryptoSecret object providing +# the decryption key (since 2.6). Mandatory except when +# doing a metadata-only probe of the image. +# +# Since: 2.6 +## +{ 'struct': 'BlockdevOptionsLUKS', + 'base': 'BlockdevOptionsGenericFormat', + 'data': { '*key-secret': 'str' } } + + ## # @BlockdevOptionsGenericCOWFormat # @@ -1592,6 +1828,10 @@ # @refcount-cache-size: #optional the maximum size of the refcount block cache # in bytes (since 2.2) # +# @cache-clean-interval: #optional clean unused entries in the L2 and refcount +# caches. The interval is in seconds. The default value +# is 0 and it disables this feature (since 2.5) +# # Since: 1.7 ## { 'struct': 'BlockdevOptionsQcow2', @@ -1603,7 +1843,8 @@ '*overlap-check': 'Qcow2OverlapChecks', '*cache-size': 'int', '*l2-cache-size': 'int', - '*refcount-cache-size': 'int' } } + '*refcount-cache-size': 'int', + '*cache-clean-interval': 'int' } } ## @@ -1640,21 +1881,23 @@ # @BlkdebugEvent # # Trigger events supported by blkdebug. +# +# Since: 2.0 ## -{ 'enum': 'BlkdebugEvent', - 'data': [ 'l1_update', 'l1_grow.alloc_table', 'l1_grow.write_table', - 'l1_grow.activate_table', 'l2_load', 'l2_update', - 'l2_update_compressed', 'l2_alloc.cow_read', 'l2_alloc.write', +{ 'enum': 'BlkdebugEvent', 'prefix': 'BLKDBG', + 'data': [ 'l1_update', 'l1_grow_alloc_table', 'l1_grow_write_table', + 'l1_grow_activate_table', 'l2_load', 'l2_update', + 'l2_update_compressed', 'l2_alloc_cow_read', 'l2_alloc_write', 'read_aio', 'read_backing_aio', 'read_compressed', 'write_aio', 'write_compressed', 'vmstate_load', 'vmstate_save', 'cow_read', 'cow_write', 'reftable_load', 'reftable_grow', 'reftable_update', 'refblock_load', 'refblock_update', 'refblock_update_part', - 'refblock_alloc', 'refblock_alloc.hookup', 'refblock_alloc.write', - 'refblock_alloc.write_blocks', 'refblock_alloc.write_table', - 'refblock_alloc.switch_table', 'cluster_alloc', + 'refblock_alloc', 'refblock_alloc_hookup', 'refblock_alloc_write', + 'refblock_alloc_write_blocks', 'refblock_alloc_write_table', + 'refblock_alloc_switch_table', 'cluster_alloc', 'cluster_alloc_bytes', 'cluster_free', 'flush_to_os', - 'flush_to_disk', 'pwritev_rmw.head', 'pwritev_rmw.after_head', - 'pwritev_rmw.tail', 'pwritev_rmw.after_tail', 'pwritev', + 'flush_to_disk', 'pwritev_rmw_head', 'pwritev_rmw_after_head', + 'pwritev_rmw_tail', 'pwritev_rmw_after_tail', 'pwritev', 'pwritev_zero', 'pwritev_done', 'empty_image_prepare' ] } ## @@ -1791,12 +2034,55 @@ ## # @BlockdevOptions # -# Options for creating a block device. +# Options for creating a block device. Many options are available for all +# block devices, independent of the block driver: +# +# @driver: block driver name +# @id: #optional id by which the new block device can be referred to. +# This option is only allowed on the top level of blockdev-add. +# A BlockBackend will be created by blockdev-add if and only if +# this option is given. +# @node-name: #optional the name of a block driver state node (Since 2.0). +# This option is required on the top level of blockdev-add if +# the @id option is not given there. +# @discard: #optional discard-related options (default: ignore) +# @cache: #optional cache-related options +# @aio: #optional AIO backend (default: threads) +# @rerror: #optional how to handle read errors on the device +# (default: report) +# @werror: #optional how to handle write errors on the device +# (default: enospc) +# @read-only: #optional whether the block device should be read-only +# (default: false) +# @stats-account-invalid: #optional whether to include invalid +# operations when computing last access statistics +# (default: true) (Since 2.5) +# @stats-account-failed: #optional whether to include failed +# operations when computing latency and last +# access statistics (default: true) (Since 2.5) +# @stats-intervals: #optional list of intervals for collecting I/O +# statistics, in seconds (default: none) (Since 2.5) +# @detect-zeroes: #optional detect and optimize zero writes (Since 2.1) +# (default: off) +# +# Remaining options are determined by the block driver. # # Since: 1.7 ## { 'union': 'BlockdevOptions', - 'base': 'BlockdevOptionsBase', + 'base': { 'driver': 'BlockdevDriver', + '*id': 'str', + '*node-name': 'str', + '*discard': 'BlockdevDiscardOptions', + '*cache': 'BlockdevCacheOptions', + '*aio': 'BlockdevAioOptions', + '*rerror': 'BlockdevOnError', + '*werror': 'BlockdevOnError', + '*read-only': 'bool', + '*stats-account-invalid': 'bool', + '*stats-account-failed': 'bool', + '*stats-intervals': ['int'], + '*detect-zeroes': 'BlockdevDetectZeroesOptions' }, 'discriminator': 'driver', 'data': { 'archipelago':'BlockdevOptionsArchipelago', @@ -1811,10 +2097,10 @@ # TODO gluster: Wait for structured options 'host_cdrom': 'BlockdevOptionsFile', 'host_device':'BlockdevOptionsFile', - 'host_floppy':'BlockdevOptionsFile', 'http': 'BlockdevOptionsFile', 'https': 'BlockdevOptionsFile', # TODO iscsi: Wait for structured options + 'luks': 'BlockdevOptionsLUKS', # TODO nbd: Should take InetSocketAddress for 'host'? # TODO nfs: Wait for structured options 'null-aio': 'BlockdevOptionsNull', @@ -1855,11 +2141,13 @@ ## # @blockdev-add: # -# Creates a new block device. +# Creates a new block device. If the @id option is given at the top level, a +# BlockBackend will be created; otherwise, @node-name is mandatory at the top +# level and no BlockBackend will be created. # # This command is still a work in progress. It doesn't support all -# block drivers, it lacks a matching blockdev-del, and more. Stay -# away from it unless you want to help with its development. +# block drivers among other things. Stay away from it unless you want +# to help with its development. # # @options: block device options for the new device # @@ -1867,6 +2155,165 @@ ## { 'command': 'blockdev-add', 'data': { 'options': 'BlockdevOptions' } } +## +# @x-blockdev-del: +# +# Deletes a block device that has been added using blockdev-add. +# The selected device can be either a block backend or a graph node. +# +# In the former case the backend will be destroyed, along with its +# inserted medium if there's any. The command will fail if the backend +# or its medium are in use. +# +# In the latter case the node will be destroyed. The command will fail +# if the node is attached to a block backend or is otherwise being +# used. +# +# One of @id or @node-name must be specified, but not both. +# +# This command is still a work in progress and is considered +# experimental. Stay away from it unless you want to help with its +# development. +# +# @id: #optional Name of the block backend device to delete. +# +# @node-name: #optional Name of the graph node to delete. +# +# Since: 2.5 +## +{ 'command': 'x-blockdev-del', 'data': { '*id': 'str', '*node-name': 'str' } } + +## +# @blockdev-open-tray: +# +# Opens a block device's tray. If there is a block driver state tree inserted as +# a medium, it will become inaccessible to the guest (but it will remain +# associated to the block device, so closing the tray will make it accessible +# again). +# +# If the tray was already open before, this will be a no-op. +# +# Once the tray opens, a DEVICE_TRAY_MOVED event is emitted. There are cases in +# which no such event will be generated, these include: +# - if the guest has locked the tray, @force is false and the guest does not +# respond to the eject request +# - if the BlockBackend denoted by @device does not have a guest device attached +# to it +# - if the guest device does not have an actual tray +# +# @device: block device name +# +# @force: #optional if false (the default), an eject request will be sent to +# the guest if it has locked the tray (and the tray will not be opened +# immediately); if true, the tray will be opened regardless of whether +# it is locked +# +# Since: 2.5 +## +{ 'command': 'blockdev-open-tray', + 'data': { 'device': 'str', + '*force': 'bool' } } + +## +# @blockdev-close-tray: +# +# Closes a block device's tray. If there is a block driver state tree associated +# with the block device (which is currently ejected), that tree will be loaded +# as the medium. +# +# If the tray was already closed before, this will be a no-op. +# +# @device: block device name +# +# Since: 2.5 +## +{ 'command': 'blockdev-close-tray', + 'data': { 'device': 'str' } } + +## +# @x-blockdev-remove-medium: +# +# Removes a medium (a block driver state tree) from a block device. That block +# device's tray must currently be open (unless there is no attached guest +# device). +# +# If the tray is open and there is no medium inserted, this will be a no-op. +# +# This command is still a work in progress and is considered experimental. +# Stay away from it unless you want to help with its development. +# +# @device: block device name +# +# Since: 2.5 +## +{ 'command': 'x-blockdev-remove-medium', + 'data': { 'device': 'str' } } + +## +# @x-blockdev-insert-medium: +# +# Inserts a medium (a block driver state tree) into a block device. That block +# device's tray must currently be open (unless there is no attached guest +# device) and there must be no medium inserted already. +# +# This command is still a work in progress and is considered experimental. +# Stay away from it unless you want to help with its development. +# +# @device: block device name +# +# @node-name: name of a node in the block driver state graph +# +# Since: 2.5 +## +{ 'command': 'x-blockdev-insert-medium', + 'data': { 'device': 'str', + 'node-name': 'str'} } + + +## +# @BlockdevChangeReadOnlyMode: +# +# Specifies the new read-only mode of a block device subject to the +# @blockdev-change-medium command. +# +# @retain: Retains the current read-only mode +# +# @read-only: Makes the device read-only +# +# @read-write: Makes the device writable +# +# Since: 2.3 +## +{ 'enum': 'BlockdevChangeReadOnlyMode', + 'data': ['retain', 'read-only', 'read-write'] } + + +## +# @blockdev-change-medium: +# +# Changes the medium inserted into a block device by ejecting the current medium +# and loading a new image file which is inserted as the new medium (this command +# combines blockdev-open-tray, x-blockdev-remove-medium, +# x-blockdev-insert-medium and blockdev-close-tray). +# +# @device: block device name +# +# @filename: filename of the new image to be loaded +# +# @format: #optional, format to open the new image with (defaults to +# the probed format) +# +# @read-only-mode: #optional, change the read-only mode of the device; defaults +# to 'retain' +# +# Since: 2.5 +## +{ 'command': 'blockdev-change-medium', + 'data': { 'device': 'str', + 'filename': 'str', + '*format': 'str', + '*read-only-mode': 'BlockdevChangeReadOnlyMode' } } + ## # @BlockErrorAction diff --git a/qemu/qapi/block.json b/qemu/qapi/block.json index aad645c4a..937337dce 100644 --- a/qemu/qapi/block.json +++ b/qemu/qapi/block.json @@ -6,7 +6,7 @@ { 'include': 'block-core.json' } ## -# BiosAtaTranslation: +# @BiosAtaTranslation: # # Policy that BIOS should use to interpret cylinder/head/sector # addresses. Note that Bochs BIOS and SeaBIOS will not actually @@ -39,6 +39,22 @@ { 'enum': 'BiosAtaTranslation', 'data': ['auto', 'none', 'lba', 'large', 'rechs']} +## +# @FloppyDriveType +# +# Type of Floppy drive to be emulated by the Floppy Disk Controller. +# +# @144: 1.44MB 3.5" drive +# @288: 2.88MB 3.5" drive +# @120: 1.2MB 5.25" drive +# @none: No drive connected +# @auto: Automatically determined by inserted media at boot +# +# Since: 2.6 +## +{ 'enum': 'FloppyDriveType', + 'data': ['144', '288', '120', 'none', 'auto']} + ## # @BlockdevSnapshotInternal # @@ -130,13 +146,15 @@ # QEMU instance could refer to them as "nbd:HOST:PORT:exportname=NAME". # # @addr: Address on which to listen. +# @tls-creds: (optional) ID of the TLS credentials object. Since 2.6 # # Returns: error if the server is already running. # # Since: 1.3.0 ## { 'command': 'nbd-server-start', - 'data': { 'addr': 'SocketAddress' } } + 'data': { 'addr': 'SocketAddress', + '*tls-creds': 'str'} } ## # @nbd-server-add: @@ -178,3 +196,19 @@ ## { 'event': 'DEVICE_TRAY_MOVED', 'data': { 'device': 'str', 'tray-open': 'bool' } } + +## +# @QuorumOpType +# +# An enumeration of the quorum operation types +# +# @read: read operation +# +# @write: write operation +# +# @flush: flush operation +# +# Since: 2.6 +## +{ 'enum': 'QuorumOpType', + 'data': [ 'read', 'write', 'flush' ] } diff --git a/qemu/qapi/common.json b/qemu/qapi/common.json index bad56bf68..9353a7b37 100644 --- a/qemu/qapi/common.json +++ b/qemu/qapi/common.json @@ -3,7 +3,7 @@ # QAPI common definitions ## -# @ErrorClass +# @QapiErrorClass # # QEMU error classes # @@ -24,7 +24,8 @@ # # Since: 1.2 ## -{ 'enum': 'ErrorClass', +{ 'enum': 'QapiErrorClass', + # Keep this in sync with ErrorClass in error.h 'data': [ 'GenericError', 'CommandNotFound', 'DeviceEncrypted', 'DeviceNotActive', 'DeviceNotFound', 'KVMMissingCap' ] } @@ -114,3 +115,19 @@ ## { 'enum': 'OnOffAuto', 'data': [ 'auto', 'on', 'off' ] } + +## +# @OnOffSplit +# +# An enumeration of three values: on, off, and split +# +# @on: Enabled +# +# @off: Disabled +# +# @split: Mixed +# +# Since: 2.6 +## +{ 'enum': 'OnOffSplit', + 'data': [ 'on', 'off', 'split' ] } diff --git a/qemu/qapi/crypto.json b/qemu/qapi/crypto.json new file mode 100644 index 000000000..760d0c057 --- /dev/null +++ b/qemu/qapi/crypto.json @@ -0,0 +1,222 @@ +# -*- Mode: Python -*- +# +# QAPI crypto definitions + +## +# QCryptoTLSCredsEndpoint: +# +# The type of network endpoint that will be using the credentials. +# Most types of credential require different setup / structures +# depending on whether they will be used in a server versus a +# client. +# +# @client: the network endpoint is acting as the client +# +# @server: the network endpoint is acting as the server +# +# Since: 2.5 +## +{ 'enum': 'QCryptoTLSCredsEndpoint', + 'prefix': 'QCRYPTO_TLS_CREDS_ENDPOINT', + 'data': ['client', 'server']} + + +## +# QCryptoSecretFormat: +# +# The data format that the secret is provided in +# +# @raw: raw bytes. When encoded in JSON only valid UTF-8 sequences can be used +# @base64: arbitrary base64 encoded binary data +# Since: 2.6 +## +{ 'enum': 'QCryptoSecretFormat', + 'prefix': 'QCRYPTO_SECRET_FORMAT', + 'data': ['raw', 'base64']} + + +## +# QCryptoHashAlgorithm: +# +# The supported algorithms for computing content digests +# +# @md5: MD5. Should not be used in any new code, legacy compat only +# @sha1: SHA-1. Should not be used in any new code, legacy compat only +# @sha256: SHA-256. Current recommended strong hash. +# Since: 2.6 +## +{ 'enum': 'QCryptoHashAlgorithm', + 'prefix': 'QCRYPTO_HASH_ALG', + 'data': ['md5', 'sha1', 'sha256']} + + +## +# QCryptoCipherAlgorithm: +# +# The supported algorithms for content encryption ciphers +# +# @aes-128: AES with 128 bit / 16 byte keys +# @aes-192: AES with 192 bit / 24 byte keys +# @aes-256: AES with 256 bit / 32 byte keys +# @des-rfb: RFB specific variant of single DES. Do not use except in VNC. +# @cast5-128: Cast5 with 128 bit / 16 byte keys +# @serpent-128: Serpent with 128 bit / 16 byte keys +# @serpent-192: Serpent with 192 bit / 24 byte keys +# @serpent-256: Serpent with 256 bit / 32 byte keys +# @twofish-128: Twofish with 128 bit / 16 byte keys +# @twofish-192: Twofish with 192 bit / 24 byte keys +# @twofish-256: Twofish with 256 bit / 32 byte keys +# Since: 2.6 +## +{ 'enum': 'QCryptoCipherAlgorithm', + 'prefix': 'QCRYPTO_CIPHER_ALG', + 'data': ['aes-128', 'aes-192', 'aes-256', + 'des-rfb', + 'cast5-128', + 'serpent-128', 'serpent-192', 'serpent-256', + 'twofish-128', 'twofish-192', 'twofish-256']} + + +## +# QCryptoCipherMode: +# +# The supported modes for content encryption ciphers +# +# @ecb: Electronic Code Book +# @cbc: Cipher Block Chaining +# @xts: XEX with tweaked code book and ciphertext stealing +# Since: 2.6 +## +{ 'enum': 'QCryptoCipherMode', + 'prefix': 'QCRYPTO_CIPHER_MODE', + 'data': ['ecb', 'cbc', 'xts']} + + +## +# QCryptoIVGenAlgorithm: +# +# The supported algorithms for generating initialization +# vectors for full disk encryption. The 'plain' generator +# should not be used for disks with sector numbers larger +# than 2^32, except where compatibility with pre-existing +# Linux dm-crypt volumes is required. +# +# @plain: 64-bit sector number truncated to 32-bits +# @plain64: 64-bit sector number +# @essiv: 64-bit sector number encrypted with a hash of the encryption key +# Since: 2.6 +## +{ 'enum': 'QCryptoIVGenAlgorithm', + 'prefix': 'QCRYPTO_IVGEN_ALG', + 'data': ['plain', 'plain64', 'essiv']} + +## +# QCryptoBlockFormat: +# +# The supported full disk encryption formats +# +# @qcow: QCow/QCow2 built-in AES-CBC encryption. Use only +# for liberating data from old images. +# @luks: LUKS encryption format. Recommended for new images +# +# Since: 2.6 +## +{ 'enum': 'QCryptoBlockFormat', +# 'prefix': 'QCRYPTO_BLOCK_FORMAT', + 'data': ['qcow', 'luks']} + +## +# QCryptoBlockOptionsBase: +# +# The common options that apply to all full disk +# encryption formats +# +# @format: the encryption format +# +# Since: 2.6 +## +{ 'struct': 'QCryptoBlockOptionsBase', + 'data': { 'format': 'QCryptoBlockFormat' }} + +## +# QCryptoBlockOptionsQCow: +# +# The options that apply to QCow/QCow2 AES-CBC encryption format +# +# @key-secret: #optional the ID of a QCryptoSecret object providing the +# decryption key. Mandatory except when probing image for +# metadata only. +# +# Since: 2.6 +## +{ 'struct': 'QCryptoBlockOptionsQCow', + 'data': { '*key-secret': 'str' }} + +## +# QCryptoBlockOptionsLUKS: +# +# The options that apply to LUKS encryption format +# +# @key-secret: #optional the ID of a QCryptoSecret object providing the +# decryption key. Mandatory except when probing image for +# metadata only. +# Since: 2.6 +## +{ 'struct': 'QCryptoBlockOptionsLUKS', + 'data': { '*key-secret': 'str' }} + + +## +# QCryptoBlockCreateOptionsLUKS: +# +# The options that apply to LUKS encryption format initialization +# +# @cipher-alg: #optional the cipher algorithm for data encryption +# Currently defaults to 'aes'. +# @cipher-mode: #optional the cipher mode for data encryption +# Currently defaults to 'cbc' +# @ivgen-alg: #optional the initialization vector generator +# Currently defaults to 'essiv' +# @ivgen-hash-alg: #optional the initialization vector generator hash +# Currently defaults to 'sha256' +# @hash-alg: #optional the master key hash algorithm +# Currently defaults to 'sha256' +# Since: 2.6 +## +{ 'struct': 'QCryptoBlockCreateOptionsLUKS', + 'base': 'QCryptoBlockOptionsLUKS', + 'data': { '*cipher-alg': 'QCryptoCipherAlgorithm', + '*cipher-mode': 'QCryptoCipherMode', + '*ivgen-alg': 'QCryptoIVGenAlgorithm', + '*ivgen-hash-alg': 'QCryptoHashAlgorithm', + '*hash-alg': 'QCryptoHashAlgorithm'}} + + +## +# QCryptoBlockOpenOptions: +# +# The options that are available for all encryption formats +# when opening an existing volume +# +# Since: 2.6 +## +{ 'union': 'QCryptoBlockOpenOptions', + 'base': 'QCryptoBlockOptionsBase', + 'discriminator': 'format', + 'data': { 'qcow': 'QCryptoBlockOptionsQCow', + 'luks': 'QCryptoBlockOptionsLUKS' } } + + +## +# QCryptoBlockCreateOptions: +# +# The options that are available for all encryption formats +# when initializing a new volume +# +# Since: 2.6 +## +{ 'union': 'QCryptoBlockCreateOptions', + 'base': 'QCryptoBlockOptionsBase', + 'discriminator': 'format', + 'data': { 'qcow': 'QCryptoBlockOptionsQCow', + 'luks': 'QCryptoBlockCreateOptionsLUKS' } } diff --git a/qemu/qapi/event.json b/qemu/qapi/event.json index f0cef010f..8642052eb 100644 --- a/qemu/qapi/event.json +++ b/qemu/qapi/event.json @@ -254,6 +254,19 @@ { 'event': 'MIGRATION', 'data': {'status': 'MigrationStatus'}} +## +# @MIGRATION_PASS +# +# Emitted from the source side of a migration at the start of each pass +# (when it syncs the dirty bitmap) +# +# @pass: An incrementing count (starting at 1 on the first pass) +# +# Since: 2.6 +## +{ 'event': 'MIGRATION_PASS', + 'data': { 'pass': 'int' } } + ## # @ACPI_DEVICE_OST # @@ -312,6 +325,8 @@ # # Emitted to report a corruption of a Quorum file # +# @type: quorum operation type (Since 2.6) +# # @error: #optional, error message. Only present on failure. This field # contains a human-readable error message. There are no semantics other # than that the block layer reported an error and clients should not @@ -326,7 +341,7 @@ # Since: 2.0 ## { 'event': 'QUORUM_REPORT_BAD', - 'data': { '*error': 'str', 'node-name': 'str', + 'data': { 'type': 'QuorumOpType', '*error': 'str', 'node-name': 'str', 'sector-num': 'int', 'sectors-count': 'int' } } ## @@ -356,3 +371,19 @@ ## { 'event': 'MEM_UNPLUG_ERROR', 'data': { 'device': 'str', 'msg': 'str' } } + +## +# @DUMP_COMPLETED +# +# Emitted when background dump has completed +# +# @result: DumpQueryResult type described in qapi-schema.json. +# +# @error: #optional human-readable error string that provides +# hint on why dump failed. Only presents on failure. The +# user should not try to interpret the error string. +# +# Since: 2.6 +## +{ 'event': 'DUMP_COMPLETED' , + 'data': { 'result': 'DumpQueryResult', '*error': 'str' } } diff --git a/qemu/qapi/introspect.json b/qemu/qapi/introspect.json new file mode 100644 index 000000000..3fd81fb54 --- /dev/null +++ b/qemu/qapi/introspect.json @@ -0,0 +1,279 @@ +# -*- Mode: Python -*- +# +# QAPI/QMP introspection +# +# Copyright (C) 2015 Red Hat, Inc. +# +# Authors: +# Markus Armbruster +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. + +## +# @query-qmp-schema +# +# Command query-qmp-schema exposes the QMP wire ABI as an array of +# SchemaInfo. This lets QMP clients figure out what commands and +# events are available in this QEMU, and their parameters and results. +# +# However, the SchemaInfo can't reflect all the rules and restrictions +# that apply to QMP. It's interface introspection (figuring out +# what's there), not interface specification. The specification is in +# the QAPI schema. +# +# Furthermore, while we strive to keep the QMP wire format +# backwards-compatible across qemu versions, the introspection output +# is not guaranteed to have the same stability. For example, one +# version of qemu may list an object member as an optional +# non-variant, while another lists the same member only through the +# object's variants; or the type of a member may change from a generic +# string into a specific enum or from one specific type into an +# alternate that includes the original type alongside something else. +# +# Returns: array of @SchemaInfo, where each element describes an +# entity in the ABI: command, event, type, ... +# +# The order of the various SchemaInfo is unspecified; however, all +# names are guaranteed to be unique (no name will be duplicated with +# different meta-types). +# +# Note: the QAPI schema is also used to help define *internal* +# interfaces, by defining QAPI types. These are not part of the QMP +# wire ABI, and therefore not returned by this command. +# +# Since: 2.5 +## +{ 'command': 'query-qmp-schema', + 'returns': [ 'SchemaInfo' ], + 'gen': false } # just to simplify qmp_query_json() + +## +# @SchemaMetaType +# +# This is a @SchemaInfo's meta type, i.e. the kind of entity it +# describes. +# +# @builtin: a predefined type such as 'int' or 'bool'. +# +# @enum: an enumeration type +# +# @array: an array type +# +# @object: an object type (struct or union) +# +# @alternate: an alternate type +# +# @command: a QMP command +# +# @event: a QMP event +# +# Since: 2.5 +## +{ 'enum': 'SchemaMetaType', + 'data': [ 'builtin', 'enum', 'array', 'object', 'alternate', + 'command', 'event' ] } + +## +# @SchemaInfo +# +# @name: the entity's name, inherited from @base. +# Commands and events have the name defined in the QAPI schema. +# Unlike command and event names, type names are not part of +# the wire ABI. Consequently, type names are meaningless +# strings here, although they are still guaranteed unique +# regardless of @meta-type. +# +# All references to other SchemaInfo are by name. +# +# @meta-type: the entity's meta type, inherited from @base. +# +# Additional members depend on the value of @meta-type. +# +# Since: 2.5 +## +{ 'union': 'SchemaInfo', + 'base': { 'name': 'str', 'meta-type': 'SchemaMetaType' }, + 'discriminator': 'meta-type', + 'data': { + 'builtin': 'SchemaInfoBuiltin', + 'enum': 'SchemaInfoEnum', + 'array': 'SchemaInfoArray', + 'object': 'SchemaInfoObject', + 'alternate': 'SchemaInfoAlternate', + 'command': 'SchemaInfoCommand', + 'event': 'SchemaInfoEvent' } } + +## +# @SchemaInfoBuiltin +# +# Additional SchemaInfo members for meta-type 'builtin'. +# +# @json-type: the JSON type used for this type on the wire. +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoBuiltin', + 'data': { 'json-type': 'JSONType' } } + +## +# @JSONType +# +# The four primitive and two structured types according to RFC 7159 +# section 1, plus 'int' (split off 'number'), plus the obvious top +# type 'value'. +# +# Since: 2.5 +## +{ 'enum': 'JSONType', + 'data': [ 'string', 'number', 'int', 'boolean', 'null', + 'object', 'array', 'value' ] } + +## +# @SchemaInfoEnum +# +# Additional SchemaInfo members for meta-type 'enum'. +# +# @values: the enumeration type's values, in no particular order. +# +# Values of this type are JSON string on the wire. +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoEnum', + 'data': { 'values': ['str'] } } + +## +# @SchemaInfoArray +# +# Additional SchemaInfo members for meta-type 'array'. +# +# @element-type: the array type's element type. +# +# Values of this type are JSON array on the wire. +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoArray', + 'data': { 'element-type': 'str' } } + +## +# @SchemaInfoObject +# +# Additional SchemaInfo members for meta-type 'object'. +# +# @members: the object type's (non-variant) members, in no particular order. +# +# @tag: #optional the name of the member serving as type tag. +# An element of @members with this name must exist. +# +# @variants: #optional variant members, i.e. additional members that +# depend on the type tag's value. Present exactly when +# @tag is present. The variants are in no particular order, +# and may even differ from the order of the values of the +# enum type of the @tag. +# +# Values of this type are JSON object on the wire. +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoObject', + 'data': { 'members': [ 'SchemaInfoObjectMember' ], + '*tag': 'str', + '*variants': [ 'SchemaInfoObjectVariant' ] } } + +## +# @SchemaInfoObjectMember +# +# An object member. +# +# @name: the member's name, as defined in the QAPI schema. +# +# @type: the name of the member's type. +# +# @default: #optional default when used as command parameter. +# If absent, the parameter is mandatory. +# If present, the value must be null. The parameter is +# optional, and behavior when it's missing is not specified +# here. +# Future extension: if present and non-null, the parameter +# is optional, and defaults to this value. +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoObjectMember', + 'data': { 'name': 'str', 'type': 'str', '*default': 'any' } } +# @default's type must be null or match @type + +## +# @SchemaInfoObjectVariant +# +# The variant members for a value of the type tag. +# +# @case: a value of the type tag. +# +# @type: the name of the object type that provides the variant members +# when the type tag has value @case. +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoObjectVariant', + 'data': { 'case': 'str', 'type': 'str' } } + +## +# @SchemaInfoAlternate +# +# Additional SchemaInfo members for meta-type 'alternate'. +# +# @members: the alternate type's members, in no particular order. +# The members' wire encoding is distinct, see +# docs/qapi-code-gen.txt section Alternate types. +# +# On the wire, this can be any of the members. +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoAlternate', + 'data': { 'members': [ 'SchemaInfoAlternateMember' ] } } + +## +# @SchemaInfoAlternateMember +# +# An alternate member. +# +# @type: the name of the member's type. +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoAlternateMember', + 'data': { 'type': 'str' } } + +## +# @SchemaInfoCommand +# +# Additional SchemaInfo members for meta-type 'command'. +# +# @arg-type: the name of the object type that provides the command's +# parameters. +# +# @ret-type: the name of the command's result type. +# +# TODO @success-response (currently irrelevant, because it's QGA, not QMP) +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoCommand', + 'data': { 'arg-type': 'str', 'ret-type': 'str' } } + +## +# @SchemaInfoEvent +# +# Additional SchemaInfo members for meta-type 'event'. +# +# @arg-type: the name of the object type that provides the event's +# parameters. +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoEvent', + 'data': { 'arg-type': 'str' } } diff --git a/qemu/qapi/opts-visitor.c b/qemu/qapi/opts-visitor.c index 7ae33b311..602f2609c 100644 --- a/qemu/qapi/opts-visitor.c +++ b/qemu/qapi/opts-visitor.c @@ -1,7 +1,7 @@ /* * Options Visitor * - * Copyright Red Hat, Inc. 2012, 2013 + * Copyright Red Hat, Inc. 2012-2016 * * Author: Laszlo Ersek * @@ -10,7 +10,9 @@ * */ -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/cutils.h" #include "qapi/qmp/qerror.h" #include "qapi/opts-visitor.h" #include "qemu/queue.h" @@ -89,6 +91,12 @@ struct OptsVisitor }; +static OptsVisitor *to_ov(Visitor *v) +{ + return container_of(v, OptsVisitor, visitor); +} + + static void destroy_list(gpointer list) { @@ -118,10 +126,10 @@ opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt) static void -opts_start_struct(Visitor *v, void **obj, const char *kind, - const char *name, size_t size, Error **errp) +opts_start_struct(Visitor *v, const char *name, void **obj, + size_t size, Error **errp) { - OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); + OptsVisitor *ov = to_ov(v); const QemuOpt *opt; if (obj) { @@ -150,17 +158,11 @@ opts_start_struct(Visitor *v, void **obj, const char *kind, } -static gboolean -ghr_true(gpointer ign_key, gpointer ign_value, gpointer ign_user_data) -{ - return TRUE; -} - - static void opts_end_struct(Visitor *v, Error **errp) { - OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); + OptsVisitor *ov = to_ov(v); + GHashTableIter iter; GQueue *any; if (--ov->depth > 0) { @@ -168,8 +170,8 @@ opts_end_struct(Visitor *v, Error **errp) } /* we should have processed all (distinct) QemuOpt instances */ - any = g_hash_table_find(ov->unprocessed_opts, &ghr_true, NULL); - if (any) { + g_hash_table_iter_init(&iter, ov->unprocessed_opts); + if (g_hash_table_iter_next(&iter, NULL, (void **)&any)) { const QemuOpt *first; first = g_queue_peek_head(any); @@ -202,7 +204,7 @@ lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp) static void opts_start_list(Visitor *v, const char *name, Error **errp) { - OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); + OptsVisitor *ov = to_ov(v); /* we can't traverse a list in a list */ assert(ov->list_mode == LM_NONE); @@ -214,9 +216,9 @@ opts_start_list(Visitor *v, const char *name, Error **errp) static GenericList * -opts_next_list(Visitor *v, GenericList **list, Error **errp) +opts_next_list(Visitor *v, GenericList **list, size_t size) { - OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); + OptsVisitor *ov = to_ov(v); GenericList **link; switch (ov->list_mode) { @@ -257,15 +259,15 @@ opts_next_list(Visitor *v, GenericList **list, Error **errp) abort(); } - *link = g_malloc0(sizeof **link); + *link = g_malloc0(size); return *link; } static void -opts_end_list(Visitor *v, Error **errp) +opts_end_list(Visitor *v) { - OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); + OptsVisitor *ov = to_ov(v); assert(ov->list_mode == LM_STARTED || ov->list_mode == LM_IN_PROGRESS || @@ -305,9 +307,9 @@ processed(OptsVisitor *ov, const char *name) static void -opts_type_str(Visitor *v, char **obj, const char *name, Error **errp) +opts_type_str(Visitor *v, const char *name, char **obj, Error **errp) { - OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); + OptsVisitor *ov = to_ov(v); const QemuOpt *opt; opt = lookup_scalar(ov, name, errp); @@ -321,9 +323,9 @@ opts_type_str(Visitor *v, char **obj, const char *name, Error **errp) /* mimics qemu-option.c::parse_option_bool() */ static void -opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp) +opts_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) { - OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); + OptsVisitor *ov = to_ov(v); const QemuOpt *opt; opt = lookup_scalar(ov, name, errp); @@ -354,9 +356,9 @@ opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp) static void -opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp) +opts_type_int64(Visitor *v, const char *name, int64_t *obj, Error **errp) { - OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); + OptsVisitor *ov = to_ov(v); const QemuOpt *opt; const char *str; long long val; @@ -410,9 +412,9 @@ opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp) static void -opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp) +opts_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp) { - OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); + OptsVisitor *ov = to_ov(v); const QemuOpt *opt; const char *str; unsigned long long val; @@ -462,9 +464,9 @@ opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp) static void -opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp) +opts_type_size(Visitor *v, const char *name, uint64_t *obj, Error **errp) { - OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); + OptsVisitor *ov = to_ov(v); const QemuOpt *opt; int64_t val; char *endptr; @@ -474,8 +476,8 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp) return; } - val = strtosz_suffix(opt->str ? opt->str : "", &endptr, - STRTOSZ_DEFSUFFIX_B); + val = qemu_strtosz_suffix(opt->str ? opt->str : "", &endptr, + QEMU_STRTOSZ_DEFSUFFIX_B); if (val < 0 || *endptr) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, "a size value representible as a non-negative int64"); @@ -488,9 +490,9 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp) static void -opts_optional(Visitor *v, bool *present, const char *name, Error **errp) +opts_optional(Visitor *v, const char *name, bool *present) { - OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); + OptsVisitor *ov = to_ov(v); /* we only support a single mandatory scalar field in a list node */ assert(ov->list_mode == LM_NONE); @@ -522,7 +524,7 @@ opts_visitor_new(const QemuOpts *opts) */ ov->visitor.type_enum = &input_type_enum; - ov->visitor.type_int = &opts_type_int; + ov->visitor.type_int64 = &opts_type_int64; ov->visitor.type_uint64 = &opts_type_uint64; ov->visitor.type_size = &opts_type_size; ov->visitor.type_bool = &opts_type_bool; diff --git a/qemu/qapi/qapi-dealloc-visitor.c b/qemu/qapi/qapi-dealloc-visitor.c index d7f92c5d6..69221794e 100644 --- a/qemu/qapi/qapi-dealloc-visitor.c +++ b/qemu/qapi/qapi-dealloc-visitor.c @@ -1,6 +1,7 @@ /* * Dealloc Visitor * + * Copyright (C) 2012-2016 Red Hat, Inc. * Copyright IBM, Corp. 2011 * * Authors: @@ -11,6 +12,7 @@ * */ +#include "qemu/osdep.h" #include "qapi/dealloc-visitor.h" #include "qemu/queue.h" #include "qemu-common.h" @@ -28,7 +30,6 @@ struct QapiDeallocVisitor { Visitor visitor; QTAILQ_HEAD(, StackEntry) stack; - bool is_list_head; }; static QapiDeallocVisitor *to_qov(Visitor *v) @@ -59,9 +60,8 @@ static void *qapi_dealloc_pop(QapiDeallocVisitor *qov) return value; } -static void qapi_dealloc_start_struct(Visitor *v, void **obj, const char *kind, - const char *name, size_t unused, - Error **errp) +static void qapi_dealloc_start_struct(Visitor *v, const char *name, void **obj, + size_t unused, Error **errp) { QapiDeallocVisitor *qov = to_qov(v); qapi_dealloc_push(qov, obj); @@ -76,16 +76,15 @@ static void qapi_dealloc_end_struct(Visitor *v, Error **errp) } } -static void qapi_dealloc_start_implicit_struct(Visitor *v, - void **obj, - size_t size, - Error **errp) +static void qapi_dealloc_start_alternate(Visitor *v, const char *name, + GenericAlternate **obj, size_t size, + bool promote_int, Error **errp) { QapiDeallocVisitor *qov = to_qov(v); qapi_dealloc_push(qov, obj); } -static void qapi_dealloc_end_implicit_struct(Visitor *v, Error **errp) +static void qapi_dealloc_end_alternate(Visitor *v) { QapiDeallocVisitor *qov = to_qov(v); void **obj = qapi_dealloc_pop(qov); @@ -101,7 +100,7 @@ static void qapi_dealloc_start_list(Visitor *v, const char *name, Error **errp) } static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList **listp, - Error **errp) + size_t size) { GenericList *list = *listp; QapiDeallocVisitor *qov = to_qov(v); @@ -121,14 +120,14 @@ static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList **listp, return NULL; } -static void qapi_dealloc_end_list(Visitor *v, Error **errp) +static void qapi_dealloc_end_list(Visitor *v) { QapiDeallocVisitor *qov = to_qov(v); void *obj = qapi_dealloc_pop(qov); assert(obj == NULL); /* should've been list head tracker with no payload */ } -static void qapi_dealloc_type_str(Visitor *v, char **obj, const char *name, +static void qapi_dealloc_type_str(Visitor *v, const char *name, char **obj, Error **errp) { if (obj) { @@ -136,56 +135,37 @@ static void qapi_dealloc_type_str(Visitor *v, char **obj, const char *name, } } -static void qapi_dealloc_type_int(Visitor *v, int64_t *obj, const char *name, - Error **errp) +static void qapi_dealloc_type_int64(Visitor *v, const char *name, int64_t *obj, + Error **errp) { } -static void qapi_dealloc_type_bool(Visitor *v, bool *obj, const char *name, - Error **errp) +static void qapi_dealloc_type_uint64(Visitor *v, const char *name, + uint64_t *obj, Error **errp) { } -static void qapi_dealloc_type_number(Visitor *v, double *obj, const char *name, - Error **errp) +static void qapi_dealloc_type_bool(Visitor *v, const char *name, bool *obj, + Error **errp) { } -static void qapi_dealloc_type_size(Visitor *v, uint64_t *obj, const char *name, - Error **errp) +static void qapi_dealloc_type_number(Visitor *v, const char *name, double *obj, + Error **errp) { } -static void qapi_dealloc_type_enum(Visitor *v, int *obj, - const char * const strings[], - const char *kind, const char *name, - Error **errp) +static void qapi_dealloc_type_anything(Visitor *v, const char *name, + QObject **obj, Error **errp) { + if (obj) { + qobject_decref(*obj); + } } -/* If there's no data present, the dealloc visitor has nothing to free. - * Thus, indicate to visitor code that the subsequent union fields can - * be skipped. This is not an error condition, since the cleanup of the - * rest of an object can continue unhindered, so leave errp unset in - * these cases. - * - * NOTE: In cases where we're attempting to deallocate an object that - * may have missing fields, the field indicating the union type may - * be missing. In such a case, it's possible we don't have enough - * information to differentiate data_present == false from a case where - * data *is* present but happens to be a scalar with a value of 0. - * This is okay, since in the case of the dealloc visitor there's no - * work that needs to done in either situation. - * - * The current inability in QAPI code to more thoroughly verify a union - * type in such cases will likely need to be addressed if we wish to - * implement this interface for other types of visitors in the future, - * however. - */ -static bool qapi_dealloc_start_union(Visitor *v, bool data_present, - Error **errp) +static void qapi_dealloc_type_enum(Visitor *v, const char *name, int *obj, + const char * const strings[], Error **errp) { - return data_present; } Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v) @@ -206,18 +186,18 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void) v->visitor.start_struct = qapi_dealloc_start_struct; v->visitor.end_struct = qapi_dealloc_end_struct; - v->visitor.start_implicit_struct = qapi_dealloc_start_implicit_struct; - v->visitor.end_implicit_struct = qapi_dealloc_end_implicit_struct; + v->visitor.start_alternate = qapi_dealloc_start_alternate; + v->visitor.end_alternate = qapi_dealloc_end_alternate; v->visitor.start_list = qapi_dealloc_start_list; v->visitor.next_list = qapi_dealloc_next_list; v->visitor.end_list = qapi_dealloc_end_list; v->visitor.type_enum = qapi_dealloc_type_enum; - v->visitor.type_int = qapi_dealloc_type_int; + v->visitor.type_int64 = qapi_dealloc_type_int64; + v->visitor.type_uint64 = qapi_dealloc_type_uint64; v->visitor.type_bool = qapi_dealloc_type_bool; v->visitor.type_str = qapi_dealloc_type_str; v->visitor.type_number = qapi_dealloc_type_number; - v->visitor.type_size = qapi_dealloc_type_size; - v->visitor.start_union = qapi_dealloc_start_union; + v->visitor.type_any = qapi_dealloc_type_anything; QTAILQ_INIT(&v->stack); diff --git a/qemu/qapi/qapi-util.c b/qemu/qapi/qapi-util.c index bcdc94d5a..818730a66 100644 --- a/qemu/qapi/qapi-util.c +++ b/qemu/qapi/qapi-util.c @@ -10,8 +10,9 @@ * */ -#include "qemu-common.h" +#include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu-common.h" #include "qapi/util.h" int qapi_enum_parse(const char * const lookup[], const char *buf, diff --git a/qemu/qapi/qapi-visit-core.c b/qemu/qapi/qapi-visit-core.c index 5a7c90050..fa680c999 100644 --- a/qemu/qapi/qapi-visit-core.c +++ b/qemu/qapi/qapi-visit-core.c @@ -1,6 +1,7 @@ /* * Core Definitions for QAPI Visitor Classes * + * Copyright (C) 2012-2016 Red Hat, Inc. * Copyright IBM, Corp. 2011 * * Authors: @@ -11,16 +12,18 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qapi/qmp/qobject.h" #include "qapi/qmp/qerror.h" #include "qapi/visitor.h" #include "qapi/visitor-impl.h" -void visit_start_struct(Visitor *v, void **obj, const char *kind, - const char *name, size_t size, Error **errp) +void visit_start_struct(Visitor *v, const char *name, void **obj, + size_t size, Error **errp) { - v->start_struct(v, obj, kind, name, size, errp); + v->start_struct(v, name, obj, size, errp); } void visit_end_struct(Visitor *v, Error **errp) @@ -28,241 +31,185 @@ void visit_end_struct(Visitor *v, Error **errp) v->end_struct(v, errp); } -void visit_start_implicit_struct(Visitor *v, void **obj, size_t size, - Error **errp) -{ - if (v->start_implicit_struct) { - v->start_implicit_struct(v, obj, size, errp); - } -} - -void visit_end_implicit_struct(Visitor *v, Error **errp) -{ - if (v->end_implicit_struct) { - v->end_implicit_struct(v, errp); - } -} - void visit_start_list(Visitor *v, const char *name, Error **errp) { v->start_list(v, name, errp); } -GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp) +GenericList *visit_next_list(Visitor *v, GenericList **list, size_t size) { - return v->next_list(v, list, errp); + assert(list && size >= sizeof(GenericList)); + return v->next_list(v, list, size); } -void visit_end_list(Visitor *v, Error **errp) +void visit_end_list(Visitor *v) { - v->end_list(v, errp); + v->end_list(v); } -bool visit_start_union(Visitor *v, bool data_present, Error **errp) +void visit_start_alternate(Visitor *v, const char *name, + GenericAlternate **obj, size_t size, + bool promote_int, Error **errp) { - if (v->start_union) { - return v->start_union(v, data_present, errp); + assert(obj && size >= sizeof(GenericAlternate)); + if (v->start_alternate) { + v->start_alternate(v, name, obj, size, promote_int, errp); } - return true; } -void visit_end_union(Visitor *v, bool data_present, Error **errp) +void visit_end_alternate(Visitor *v) { - if (v->end_union) { - v->end_union(v, data_present, errp); + if (v->end_alternate) { + v->end_alternate(v); } } -void visit_optional(Visitor *v, bool *present, const char *name, - Error **errp) +bool visit_optional(Visitor *v, const char *name, bool *present) { if (v->optional) { - v->optional(v, present, name, errp); + v->optional(v, name, present); } + return *present; } -void visit_get_next_type(Visitor *v, int *obj, const int *qtypes, - const char *name, Error **errp) +void visit_type_enum(Visitor *v, const char *name, int *obj, + const char *const strings[], Error **errp) { - if (v->get_next_type) { - v->get_next_type(v, obj, qtypes, name, errp); - } -} - -void visit_type_enum(Visitor *v, int *obj, const char * const strings[], - const char *kind, const char *name, Error **errp) -{ - v->type_enum(v, obj, strings, kind, name, errp); + v->type_enum(v, name, obj, strings, errp); } -void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp) +void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp) { - v->type_int(v, obj, name, errp); + v->type_int64(v, name, obj, errp); } -void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp) +static void visit_type_uintN(Visitor *v, uint64_t *obj, const char *name, + uint64_t max, const char *type, Error **errp) { - int64_t value; - - if (v->type_uint8) { - v->type_uint8(v, obj, name, errp); + Error *err = NULL; + uint64_t value = *obj; + + v->type_uint64(v, name, &value, &err); + if (err) { + error_propagate(errp, err); + } else if (value > max) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + name ? name : "null", type); } else { - value = *obj; - v->type_int(v, &value, name, errp); - if (value < 0 || value > UINT8_MAX) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - name ? name : "null", "uint8_t"); - return; - } *obj = value; } } -void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name, Error **errp) +void visit_type_uint8(Visitor *v, const char *name, uint8_t *obj, + Error **errp) { - int64_t value; - - if (v->type_uint16) { - v->type_uint16(v, obj, name, errp); - } else { - value = *obj; - v->type_int(v, &value, name, errp); - if (value < 0 || value > UINT16_MAX) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - name ? name : "null", "uint16_t"); - return; - } - *obj = value; - } + uint64_t value = *obj; + visit_type_uintN(v, &value, name, UINT8_MAX, "uint8_t", errp); + *obj = value; } -void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name, Error **errp) +void visit_type_uint16(Visitor *v, const char *name, uint16_t *obj, + Error **errp) { - int64_t value; - - if (v->type_uint32) { - v->type_uint32(v, obj, name, errp); - } else { - value = *obj; - v->type_int(v, &value, name, errp); - if (value < 0 || value > UINT32_MAX) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - name ? name : "null", "uint32_t"); - return; - } - *obj = value; - } + uint64_t value = *obj; + visit_type_uintN(v, &value, name, UINT16_MAX, "uint16_t", errp); + *obj = value; } -void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp) +void visit_type_uint32(Visitor *v, const char *name, uint32_t *obj, + Error **errp) { - int64_t value; - - if (v->type_uint64) { - v->type_uint64(v, obj, name, errp); - } else { - value = *obj; - v->type_int(v, &value, name, errp); - *obj = value; - } + uint64_t value = *obj; + visit_type_uintN(v, &value, name, UINT32_MAX, "uint32_t", errp); + *obj = value; } -void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp) +void visit_type_uint64(Visitor *v, const char *name, uint64_t *obj, + Error **errp) { - int64_t value; - - if (v->type_int8) { - v->type_int8(v, obj, name, errp); - } else { - value = *obj; - v->type_int(v, &value, name, errp); - if (value < INT8_MIN || value > INT8_MAX) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - name ? name : "null", "int8_t"); - return; - } - *obj = value; - } + v->type_uint64(v, name, obj, errp); } -void visit_type_int16(Visitor *v, int16_t *obj, const char *name, Error **errp) +static void visit_type_intN(Visitor *v, int64_t *obj, const char *name, + int64_t min, int64_t max, const char *type, + Error **errp) { - int64_t value; - - if (v->type_int16) { - v->type_int16(v, obj, name, errp); + Error *err = NULL; + int64_t value = *obj; + + v->type_int64(v, name, &value, &err); + if (err) { + error_propagate(errp, err); + } else if (value < min || value > max) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + name ? name : "null", type); } else { - value = *obj; - v->type_int(v, &value, name, errp); - if (value < INT16_MIN || value > INT16_MAX) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - name ? name : "null", "int16_t"); - return; - } *obj = value; } } -void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp) +void visit_type_int8(Visitor *v, const char *name, int8_t *obj, Error **errp) { - int64_t value; + int64_t value = *obj; + visit_type_intN(v, &value, name, INT8_MIN, INT8_MAX, "int8_t", errp); + *obj = value; +} - if (v->type_int32) { - v->type_int32(v, obj, name, errp); - } else { - value = *obj; - v->type_int(v, &value, name, errp); - if (value < INT32_MIN || value > INT32_MAX) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - name ? name : "null", "int32_t"); - return; - } - *obj = value; - } +void visit_type_int16(Visitor *v, const char *name, int16_t *obj, + Error **errp) +{ + int64_t value = *obj; + visit_type_intN(v, &value, name, INT16_MIN, INT16_MAX, "int16_t", errp); + *obj = value; } -void visit_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp) +void visit_type_int32(Visitor *v, const char *name, int32_t *obj, + Error **errp) { - if (v->type_int64) { - v->type_int64(v, obj, name, errp); - } else { - v->type_int(v, obj, name, errp); - } + int64_t value = *obj; + visit_type_intN(v, &value, name, INT32_MIN, INT32_MAX, "int32_t", errp); + *obj = value; } -void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp) +void visit_type_int64(Visitor *v, const char *name, int64_t *obj, + Error **errp) { - int64_t value; + v->type_int64(v, name, obj, errp); +} +void visit_type_size(Visitor *v, const char *name, uint64_t *obj, + Error **errp) +{ if (v->type_size) { - v->type_size(v, obj, name, errp); - } else if (v->type_uint64) { - v->type_uint64(v, obj, name, errp); + v->type_size(v, name, obj, errp); } else { - value = *obj; - v->type_int(v, &value, name, errp); - *obj = value; + v->type_uint64(v, name, obj, errp); } } -void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp) +void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) { - v->type_bool(v, obj, name, errp); + v->type_bool(v, name, obj, errp); } -void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp) +void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp) { - v->type_str(v, obj, name, errp); + v->type_str(v, name, obj, errp); } -void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp) +void visit_type_number(Visitor *v, const char *name, double *obj, + Error **errp) { - v->type_number(v, obj, name, errp); + v->type_number(v, name, obj, errp); } -void output_type_enum(Visitor *v, int *obj, const char * const strings[], - const char *kind, const char *name, - Error **errp) +void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp) +{ + v->type_any(v, name, obj, errp); +} + +void output_type_enum(Visitor *v, const char *name, int *obj, + const char *const strings[], Error **errp) { int i = 0; int value = *obj; @@ -276,12 +223,11 @@ void output_type_enum(Visitor *v, int *obj, const char * const strings[], } enum_str = (char *)strings[value]; - visit_type_str(v, &enum_str, name, errp); + visit_type_str(v, name, &enum_str, errp); } -void input_type_enum(Visitor *v, int *obj, const char * const strings[], - const char *kind, const char *name, - Error **errp) +void input_type_enum(Visitor *v, const char *name, int *obj, + const char *const strings[], Error **errp) { Error *local_err = NULL; int64_t value = 0; @@ -289,7 +235,7 @@ void input_type_enum(Visitor *v, int *obj, const char * const strings[], assert(strings); - visit_type_str(v, &enum_str, name, &local_err); + visit_type_str(v, name, &enum_str, &local_err); if (local_err) { error_propagate(errp, local_err); return; diff --git a/qemu/qapi/qmp-dispatch.c b/qemu/qapi/qmp-dispatch.c index 7bcc86080..510a1aead 100644 --- a/qemu/qapi/qmp-dispatch.c +++ b/qemu/qapi/qmp-dispatch.c @@ -11,11 +11,12 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qapi/qmp/types.h" #include "qapi/qmp/dispatch.h" #include "qapi/qmp/json-parser.h" #include "qapi-types.h" -#include "qapi/error.h" #include "qapi/qmp/qerror.h" static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp) @@ -114,7 +115,7 @@ static QObject *do_qmp_dispatch(QObject *request, Error **errp) QObject *qmp_build_error_object(Error *err) { return qobject_from_jsonf("{ 'class': %s, 'desc': %s }", - ErrorClass_lookup[error_get_class(err)], + QapiErrorClass_lookup[error_get_class(err)], error_get_pretty(err)); } diff --git a/qemu/qapi/qmp-event.c b/qemu/qapi/qmp-event.c index 0d1ce0bd1..8bba165bf 100644 --- a/qemu/qapi/qmp-event.c +++ b/qemu/qapi/qmp-event.c @@ -11,21 +11,13 @@ * */ -#include +#include "qemu/osdep.h" #include "qemu-common.h" #include "qapi/qmp-event.h" #include "qapi/qmp/qstring.h" #include "qapi/qmp/qjson.h" -#ifdef _WIN32 -#include "sysemu/os-win32.h" -#endif - -#ifdef CONFIG_POSIX -#include "sysemu/os-posix.h" -#endif - static QMPEventFuncEmit qmp_emit; void qmp_event_set_func_emit(QMPEventFuncEmit emit) diff --git a/qemu/qapi/qmp-input-visitor.c b/qemu/qapi/qmp-input-visitor.c index e97b8a428..7cd1b777a 100644 --- a/qemu/qapi/qmp-input-visitor.c +++ b/qemu/qapi/qmp-input-visitor.c @@ -1,6 +1,7 @@ /* * Input Visitor * + * Copyright (C) 2012-2016 Red Hat, Inc. * Copyright IBM, Corp. 2011 * * Authors: @@ -11,6 +12,8 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qapi/qmp-input-visitor.h" #include "qapi/visitor-impl.h" #include "qemu/queue.h" @@ -88,12 +91,6 @@ static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp) qiv->nb_stack++; } -/** Only for qmp_input_pop. */ -static gboolean always_true(gpointer key, gpointer val, gpointer user_pkey) -{ - *(const char **)user_pkey = (const char *)key; - return TRUE; -} static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp) { @@ -102,9 +99,11 @@ static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp) if (qiv->strict) { GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h; if (top_ht) { - if (g_hash_table_size(top_ht)) { - const char *key; - g_hash_table_find(top_ht, always_true, &key); + GHashTableIter iter; + const char *key; + + g_hash_table_iter_init(&iter, top_ht); + if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) { error_setg(errp, QERR_QMP_EXTRA_MEMBER, key); } g_hash_table_unref(top_ht); @@ -114,8 +113,8 @@ static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp) qiv->nb_stack--; } -static void qmp_input_start_struct(Visitor *v, void **obj, const char *kind, - const char *name, size_t size, Error **errp) +static void qmp_input_start_struct(Visitor *v, const char *name, void **obj, + size_t size, Error **errp) { QmpInputVisitor *qiv = to_qiv(v); QObject *qobj = qmp_input_get_object(qiv, name, true); @@ -145,18 +144,6 @@ static void qmp_input_end_struct(Visitor *v, Error **errp) qmp_input_pop(qiv, errp); } -static void qmp_input_start_implicit_struct(Visitor *v, void **obj, - size_t size, Error **errp) -{ - if (obj) { - *obj = g_malloc0(size); - } -} - -static void qmp_input_end_implicit_struct(Visitor *v, Error **errp) -{ -} - static void qmp_input_start_list(Visitor *v, const char *name, Error **errp) { QmpInputVisitor *qiv = to_qiv(v); @@ -172,7 +159,7 @@ static void qmp_input_start_list(Visitor *v, const char *name, Error **errp) } static GenericList *qmp_input_next_list(Visitor *v, GenericList **list, - Error **errp) + size_t size) { QmpInputVisitor *qiv = to_qiv(v); GenericList *entry; @@ -191,7 +178,7 @@ static GenericList *qmp_input_next_list(Visitor *v, GenericList **list, return NULL; } - entry = g_malloc0(sizeof(*entry)); + entry = g_malloc0(size); if (first) { *list = entry; } else { @@ -201,97 +188,132 @@ static GenericList *qmp_input_next_list(Visitor *v, GenericList **list, return entry; } -static void qmp_input_end_list(Visitor *v, Error **errp) +static void qmp_input_end_list(Visitor *v) { QmpInputVisitor *qiv = to_qiv(v); - qmp_input_pop(qiv, errp); + qmp_input_pop(qiv, &error_abort); } -static void qmp_input_get_next_type(Visitor *v, int *kind, const int *qobjects, - const char *name, Error **errp) +static void qmp_input_start_alternate(Visitor *v, const char *name, + GenericAlternate **obj, size_t size, + bool promote_int, Error **errp) { QmpInputVisitor *qiv = to_qiv(v); QObject *qobj = qmp_input_get_object(qiv, name, false); if (!qobj) { + *obj = NULL; error_setg(errp, QERR_MISSING_PARAMETER, name ? name : "null"); return; } - *kind = qobjects[qobject_type(qobj)]; + *obj = g_malloc0(size); + (*obj)->type = qobject_type(qobj); + if (promote_int && (*obj)->type == QTYPE_QINT) { + (*obj)->type = QTYPE_QFLOAT; + } } -static void qmp_input_type_int(Visitor *v, int64_t *obj, const char *name, - Error **errp) +static void qmp_input_type_int64(Visitor *v, const char *name, int64_t *obj, + Error **errp) { QmpInputVisitor *qiv = to_qiv(v); - QObject *qobj = qmp_input_get_object(qiv, name, true); + QInt *qint = qobject_to_qint(qmp_input_get_object(qiv, name, true)); - if (!qobj || qobject_type(qobj) != QTYPE_QINT) { + if (!qint) { error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", "integer"); return; } - *obj = qint_get_int(qobject_to_qint(qobj)); + *obj = qint_get_int(qint); } -static void qmp_input_type_bool(Visitor *v, bool *obj, const char *name, +static void qmp_input_type_uint64(Visitor *v, const char *name, uint64_t *obj, + Error **errp) +{ + /* FIXME: qobject_to_qint mishandles values over INT64_MAX */ + QmpInputVisitor *qiv = to_qiv(v); + QInt *qint = qobject_to_qint(qmp_input_get_object(qiv, name, true)); + + if (!qint) { + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", + "integer"); + return; + } + + *obj = qint_get_int(qint); +} + +static void qmp_input_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) { QmpInputVisitor *qiv = to_qiv(v); - QObject *qobj = qmp_input_get_object(qiv, name, true); + QBool *qbool = qobject_to_qbool(qmp_input_get_object(qiv, name, true)); - if (!qobj || qobject_type(qobj) != QTYPE_QBOOL) { + if (!qbool) { error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", "boolean"); return; } - *obj = qbool_get_bool(qobject_to_qbool(qobj)); + *obj = qbool_get_bool(qbool); } -static void qmp_input_type_str(Visitor *v, char **obj, const char *name, +static void qmp_input_type_str(Visitor *v, const char *name, char **obj, Error **errp) { QmpInputVisitor *qiv = to_qiv(v); - QObject *qobj = qmp_input_get_object(qiv, name, true); + QString *qstr = qobject_to_qstring(qmp_input_get_object(qiv, name, true)); - if (!qobj || qobject_type(qobj) != QTYPE_QSTRING) { + if (!qstr) { error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", "string"); return; } - *obj = g_strdup(qstring_get_str(qobject_to_qstring(qobj))); + *obj = g_strdup(qstring_get_str(qstr)); } -static void qmp_input_type_number(Visitor *v, double *obj, const char *name, +static void qmp_input_type_number(Visitor *v, const char *name, double *obj, Error **errp) { QmpInputVisitor *qiv = to_qiv(v); QObject *qobj = qmp_input_get_object(qiv, name, true); + QInt *qint; + QFloat *qfloat; - if (!qobj || (qobject_type(qobj) != QTYPE_QFLOAT && - qobject_type(qobj) != QTYPE_QINT)) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", - "number"); + qint = qobject_to_qint(qobj); + if (qint) { + *obj = qint_get_int(qobject_to_qint(qobj)); return; } - if (qobject_type(qobj) == QTYPE_QINT) { - *obj = qint_get_int(qobject_to_qint(qobj)); - } else { + qfloat = qobject_to_qfloat(qobj); + if (qfloat) { *obj = qfloat_get_double(qobject_to_qfloat(qobj)); + return; } + + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", + "number"); } -static void qmp_input_optional(Visitor *v, bool *present, const char *name, +static void qmp_input_type_any(Visitor *v, const char *name, QObject **obj, Error **errp) { QmpInputVisitor *qiv = to_qiv(v); QObject *qobj = qmp_input_get_object(qiv, name, true); + qobject_incref(qobj); + *obj = qobj; +} + +static void qmp_input_optional(Visitor *v, const char *name, bool *present) +{ + QmpInputVisitor *qiv = to_qiv(v); + QObject *qobj = qmp_input_get_object(qiv, name, true); + if (!qobj) { *present = false; return; @@ -319,18 +341,18 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj) v->visitor.start_struct = qmp_input_start_struct; v->visitor.end_struct = qmp_input_end_struct; - v->visitor.start_implicit_struct = qmp_input_start_implicit_struct; - v->visitor.end_implicit_struct = qmp_input_end_implicit_struct; v->visitor.start_list = qmp_input_start_list; v->visitor.next_list = qmp_input_next_list; v->visitor.end_list = qmp_input_end_list; + v->visitor.start_alternate = qmp_input_start_alternate; v->visitor.type_enum = input_type_enum; - v->visitor.type_int = qmp_input_type_int; + v->visitor.type_int64 = qmp_input_type_int64; + v->visitor.type_uint64 = qmp_input_type_uint64; v->visitor.type_bool = qmp_input_type_bool; v->visitor.type_str = qmp_input_type_str; v->visitor.type_number = qmp_input_type_number; + v->visitor.type_any = qmp_input_type_any; v->visitor.optional = qmp_input_optional; - v->visitor.get_next_type = qmp_input_get_next_type; qmp_input_push(v, obj, NULL); qobject_incref(obj); diff --git a/qemu/qapi/qmp-output-visitor.c b/qemu/qapi/qmp-output-visitor.c index efc19d584..d44c67631 100644 --- a/qemu/qapi/qmp-output-visitor.c +++ b/qemu/qapi/qmp-output-visitor.c @@ -1,6 +1,7 @@ /* * Core Definitions for QAPI/QMP Command Registry * + * Copyright (C) 2012-2016 Red Hat, Inc. * Copyright IBM, Corp. 2011 * * Authors: @@ -11,6 +12,7 @@ * */ +#include "qemu/osdep.h" #include "qapi/qmp-output-visitor.h" #include "qapi/visitor-impl.h" #include "qemu/queue.h" @@ -29,7 +31,8 @@ typedef QTAILQ_HEAD(QStack, QStackEntry) QStack; struct QmpOutputVisitor { Visitor visitor; - QStack stack; + QStack stack; /* Stack of containers that haven't yet been finished */ + QObject *root; /* Root of the output visit */ }; #define qmp_output_add(qov, name, value) \ @@ -41,10 +44,13 @@ static QmpOutputVisitor *to_qov(Visitor *v) return container_of(v, QmpOutputVisitor, visitor); } +/* Push @value onto the stack of current QObjects being built */ static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value) { QStackEntry *e = g_malloc0(sizeof(*e)); + assert(qov->root); + assert(value); e->value = value; if (qobject_type(e->value) == QTYPE_QLIST) { e->is_list_head = true; @@ -52,63 +58,51 @@ static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value) QTAILQ_INSERT_HEAD(&qov->stack, e, node); } +/* Pop a value off the stack of QObjects being built, and return it. */ static QObject *qmp_output_pop(QmpOutputVisitor *qov) { QStackEntry *e = QTAILQ_FIRST(&qov->stack); QObject *value; + + assert(e); QTAILQ_REMOVE(&qov->stack, e, node); value = e->value; + assert(value); g_free(e); return value; } -static QObject *qmp_output_first(QmpOutputVisitor *qov) -{ - QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack); - - /* FIXME - find a better way to deal with NULL values */ - if (!e) { - return NULL; - } - - return e->value; -} - -static QObject *qmp_output_last(QmpOutputVisitor *qov) -{ - QStackEntry *e = QTAILQ_FIRST(&qov->stack); - return e->value; -} - +/* Add @value to the current QObject being built. + * If the stack is visiting a dictionary or list, @value is now owned + * by that container. Otherwise, @value is now the root. */ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name, QObject *value) { - QObject *cur; - - if (QTAILQ_EMPTY(&qov->stack)) { - qmp_output_push_obj(qov, value); - return; - } + QStackEntry *e = QTAILQ_FIRST(&qov->stack); + QObject *cur = e ? e->value : NULL; - cur = qmp_output_last(qov); - - switch (qobject_type(cur)) { - case QTYPE_QDICT: - qdict_put_obj(qobject_to_qdict(cur), name, value); - break; - case QTYPE_QLIST: - qlist_append_obj(qobject_to_qlist(cur), value); - break; - default: - qobject_decref(qmp_output_pop(qov)); - qmp_output_push_obj(qov, value); - break; + if (!cur) { + /* FIXME we should require the user to reset the visitor, rather + * than throwing away the previous root */ + qobject_decref(qov->root); + qov->root = value; + } else { + switch (qobject_type(cur)) { + case QTYPE_QDICT: + assert(name); + qdict_put_obj(qobject_to_qdict(cur), name, value); + break; + case QTYPE_QLIST: + qlist_append_obj(qobject_to_qlist(cur), value); + break; + default: + g_assert_not_reached(); + } } } -static void qmp_output_start_struct(Visitor *v, void **obj, const char *kind, - const char *name, size_t unused, - Error **errp) +static void qmp_output_start_struct(Visitor *v, const char *name, void **obj, + size_t unused, Error **errp) { QmpOutputVisitor *qov = to_qov(v); QDict *dict = qdict_new(); @@ -133,7 +127,7 @@ static void qmp_output_start_list(Visitor *v, const char *name, Error **errp) } static GenericList *qmp_output_next_list(Visitor *v, GenericList **listp, - Error **errp) + size_t size) { GenericList *list = *listp; QmpOutputVisitor *qov = to_qov(v); @@ -148,27 +142,35 @@ static GenericList *qmp_output_next_list(Visitor *v, GenericList **listp, return list ? list->next : NULL; } -static void qmp_output_end_list(Visitor *v, Error **errp) +static void qmp_output_end_list(Visitor *v) { QmpOutputVisitor *qov = to_qov(v); qmp_output_pop(qov); } -static void qmp_output_type_int(Visitor *v, int64_t *obj, const char *name, - Error **errp) +static void qmp_output_type_int64(Visitor *v, const char *name, int64_t *obj, + Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + qmp_output_add(qov, name, qint_from_int(*obj)); +} + +static void qmp_output_type_uint64(Visitor *v, const char *name, uint64_t *obj, + Error **errp) { + /* FIXME: QMP outputs values larger than INT64_MAX as negative */ QmpOutputVisitor *qov = to_qov(v); qmp_output_add(qov, name, qint_from_int(*obj)); } -static void qmp_output_type_bool(Visitor *v, bool *obj, const char *name, +static void qmp_output_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) { QmpOutputVisitor *qov = to_qov(v); qmp_output_add(qov, name, qbool_from_bool(*obj)); } -static void qmp_output_type_str(Visitor *v, char **obj, const char *name, +static void qmp_output_type_str(Visitor *v, const char *name, char **obj, Error **errp) { QmpOutputVisitor *qov = to_qov(v); @@ -179,18 +181,31 @@ static void qmp_output_type_str(Visitor *v, char **obj, const char *name, } } -static void qmp_output_type_number(Visitor *v, double *obj, const char *name, +static void qmp_output_type_number(Visitor *v, const char *name, double *obj, Error **errp) { QmpOutputVisitor *qov = to_qov(v); qmp_output_add(qov, name, qfloat_from_double(*obj)); } +static void qmp_output_type_any(Visitor *v, const char *name, QObject **obj, + Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + qobject_incref(*obj); + qmp_output_add_obj(qov, name, *obj); +} + +/* Finish building, and return the root object. Will not be NULL. */ QObject *qmp_output_get_qobject(QmpOutputVisitor *qov) { - QObject *obj = qmp_output_first(qov); + /* FIXME: we should require that a visit occurred, and that it is + * complete (no starts without a matching end) */ + QObject *obj = qov->root; if (obj) { qobject_incref(obj); + } else { + obj = qnull(); } return obj; } @@ -204,16 +219,12 @@ void qmp_output_visitor_cleanup(QmpOutputVisitor *v) { QStackEntry *e, *tmp; - /* The bottom QStackEntry, if any, owns the root QObject. See the - * qmp_output_push_obj() invocations in qmp_output_add_obj(). */ - QObject *root = QTAILQ_EMPTY(&v->stack) ? NULL : qmp_output_first(v); - QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) { QTAILQ_REMOVE(&v->stack, e, node); g_free(e); } - qobject_decref(root); + qobject_decref(v->root); g_free(v); } @@ -229,10 +240,12 @@ QmpOutputVisitor *qmp_output_visitor_new(void) v->visitor.next_list = qmp_output_next_list; v->visitor.end_list = qmp_output_end_list; v->visitor.type_enum = output_type_enum; - v->visitor.type_int = qmp_output_type_int; + v->visitor.type_int64 = qmp_output_type_int64; + v->visitor.type_uint64 = qmp_output_type_uint64; v->visitor.type_bool = qmp_output_type_bool; v->visitor.type_str = qmp_output_type_str; v->visitor.type_number = qmp_output_type_number; + v->visitor.type_any = qmp_output_type_any; QTAILQ_INIT(&v->stack); diff --git a/qemu/qapi/qmp-registry.c b/qemu/qapi/qmp-registry.c index 3e4498a3f..4ebfbccd4 100644 --- a/qemu/qapi/qmp-registry.c +++ b/qemu/qapi/qmp-registry.c @@ -12,8 +12,8 @@ * */ +#include "qemu/osdep.h" #include -#include #include "qapi/qmp/dispatch.h" static QTAILQ_HEAD(QmpCommandList, QmpCommand) qmp_commands = diff --git a/qemu/qapi/string-input-visitor.c b/qemu/qapi/string-input-visitor.c index bbd6a5456..5ea2d77b5 100644 --- a/qemu/qapi/string-input-visitor.c +++ b/qemu/qapi/string-input-visitor.c @@ -1,7 +1,7 @@ /* * String parsing visitor * - * Copyright Red Hat, Inc. 2012 + * Copyright Red Hat, Inc. 2012-2016 * * Author: Paolo Bonzini * @@ -10,6 +10,8 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qapi/string-input-visitor.h" #include "qapi/visitor-impl.h" @@ -32,6 +34,11 @@ struct StringInputVisitor const char *string; }; +static StringInputVisitor *to_siv(Visitor *v) +{ + return container_of(v, StringInputVisitor, visitor); +} + static void free_range(void *range, void *dummy) { g_free(range); @@ -120,7 +127,7 @@ error: static void start_list(Visitor *v, const char *name, Error **errp) { - StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); + StringInputVisitor *siv = to_siv(v); parse_str(siv, errp); @@ -133,10 +140,9 @@ start_list(Visitor *v, const char *name, Error **errp) } } -static GenericList * -next_list(Visitor *v, GenericList **list, Error **errp) +static GenericList *next_list(Visitor *v, GenericList **list, size_t size) { - StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); + StringInputVisitor *siv = to_siv(v); GenericList **link; Range *r; @@ -168,21 +174,20 @@ next_list(Visitor *v, GenericList **list, Error **errp) link = &(*list)->next; } - *link = g_malloc0(sizeof **link); + *link = g_malloc0(size); return *link; } -static void -end_list(Visitor *v, Error **errp) +static void end_list(Visitor *v) { - StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); + StringInputVisitor *siv = to_siv(v); siv->head = true; } -static void parse_type_int(Visitor *v, int64_t *obj, const char *name, - Error **errp) +static void parse_type_int64(Visitor *v, const char *name, int64_t *obj, + Error **errp) { - StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); + StringInputVisitor *siv = to_siv(v); if (!siv->string) { error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", @@ -217,14 +222,28 @@ static void parse_type_int(Visitor *v, int64_t *obj, const char *name, return; error: - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", "an int64 value or range"); } -static void parse_type_size(Visitor *v, uint64_t *obj, const char *name, +static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj, + Error **errp) +{ + /* FIXME: parse_type_int64 mishandles values over INT64_MAX */ + int64_t i; + Error *err = NULL; + parse_type_int64(v, name, &i, &err); + if (err) { + error_propagate(errp, err); + } else { + *obj = i; + } +} + +static void parse_type_size(Visitor *v, const char *name, uint64_t *obj, Error **errp) { - StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); + StringInputVisitor *siv = to_siv(v); Error *err = NULL; uint64_t val; @@ -243,10 +262,10 @@ static void parse_type_size(Visitor *v, uint64_t *obj, const char *name, *obj = val; } -static void parse_type_bool(Visitor *v, bool *obj, const char *name, +static void parse_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) { - StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); + StringInputVisitor *siv = to_siv(v); if (siv->string) { if (!strcasecmp(siv->string, "on") || @@ -267,10 +286,10 @@ static void parse_type_bool(Visitor *v, bool *obj, const char *name, "boolean"); } -static void parse_type_str(Visitor *v, char **obj, const char *name, +static void parse_type_str(Visitor *v, const char *name, char **obj, Error **errp) { - StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); + StringInputVisitor *siv = to_siv(v); if (siv->string) { *obj = g_strdup(siv->string); } else { @@ -279,10 +298,10 @@ static void parse_type_str(Visitor *v, char **obj, const char *name, } } -static void parse_type_number(Visitor *v, double *obj, const char *name, +static void parse_type_number(Visitor *v, const char *name, double *obj, Error **errp) { - StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); + StringInputVisitor *siv = to_siv(v); char *endp = (char *) siv->string; double val; @@ -299,10 +318,9 @@ static void parse_type_number(Visitor *v, double *obj, const char *name, *obj = val; } -static void parse_optional(Visitor *v, bool *present, const char *name, - Error **errp) +static void parse_optional(Visitor *v, const char *name, bool *present) { - StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); + StringInputVisitor *siv = to_siv(v); if (!siv->string) { *present = false; @@ -331,7 +349,8 @@ StringInputVisitor *string_input_visitor_new(const char *str) v = g_malloc0(sizeof(*v)); v->visitor.type_enum = input_type_enum; - v->visitor.type_int = parse_type_int; + v->visitor.type_int64 = parse_type_int64; + v->visitor.type_uint64 = parse_type_uint64; v->visitor.type_size = parse_type_size; v->visitor.type_bool = parse_type_bool; v->visitor.type_str = parse_type_str; diff --git a/qemu/qapi/string-output-visitor.c b/qemu/qapi/string-output-visitor.c index b86ce2cd0..c2e5c5b92 100644 --- a/qemu/qapi/string-output-visitor.c +++ b/qemu/qapi/string-output-visitor.c @@ -1,7 +1,7 @@ /* * String printing Visitor * - * Copyright Red Hat, Inc. 2012 + * Copyright Red Hat, Inc. 2012-2016 * * Author: Paolo Bonzini * @@ -10,6 +10,7 @@ * */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qapi/string-output-visitor.h" #include "qapi/visitor-impl.h" @@ -66,6 +67,11 @@ struct StringOutputVisitor GList *ranges; }; +static StringOutputVisitor *to_sov(Visitor *v) +{ + return container_of(v, StringOutputVisitor, visitor); +} + static void string_output_set(StringOutputVisitor *sov, char *string) { if (sov->string) { @@ -116,10 +122,10 @@ static void format_string(StringOutputVisitor *sov, Range *r, bool next, } } -static void print_type_int(Visitor *v, int64_t *obj, const char *name, - Error **errp) +static void print_type_int64(Visitor *v, const char *name, int64_t *obj, + Error **errp) { - StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); + StringOutputVisitor *sov = to_sov(v); GList *l; switch (sov->list_mode) { @@ -192,10 +198,18 @@ static void print_type_int(Visitor *v, int64_t *obj, const char *name, } } -static void print_type_size(Visitor *v, uint64_t *obj, const char *name, - Error **errp) +static void print_type_uint64(Visitor *v, const char *name, uint64_t *obj, + Error **errp) { - StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); + /* FIXME: print_type_int64 mishandles values over INT64_MAX */ + int64_t i = *obj; + print_type_int64(v, name, &i, errp); +} + +static void print_type_size(Visitor *v, const char *name, uint64_t *obj, + Error **errp) +{ + StringOutputVisitor *sov = to_sov(v); static const char suffixes[] = { 'B', 'K', 'M', 'G', 'T', 'P', 'E' }; uint64_t div, val; char *out; @@ -223,17 +237,17 @@ static void print_type_size(Visitor *v, uint64_t *obj, const char *name, string_output_set(sov, out); } -static void print_type_bool(Visitor *v, bool *obj, const char *name, +static void print_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) { - StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); + StringOutputVisitor *sov = to_sov(v); string_output_set(sov, g_strdup(*obj ? "true" : "false")); } -static void print_type_str(Visitor *v, char **obj, const char *name, +static void print_type_str(Visitor *v, const char *name, char **obj, Error **errp) { - StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); + StringOutputVisitor *sov = to_sov(v); char *out; if (sov->human) { @@ -244,17 +258,17 @@ static void print_type_str(Visitor *v, char **obj, const char *name, string_output_set(sov, out); } -static void print_type_number(Visitor *v, double *obj, const char *name, +static void print_type_number(Visitor *v, const char *name, double *obj, Error **errp) { - StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); + StringOutputVisitor *sov = to_sov(v); string_output_set(sov, g_strdup_printf("%f", *obj)); } static void start_list(Visitor *v, const char *name, Error **errp) { - StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); + StringOutputVisitor *sov = to_sov(v); /* we can't traverse a list in a list */ assert(sov->list_mode == LM_NONE); @@ -262,10 +276,9 @@ start_list(Visitor *v, const char *name, Error **errp) sov->head = true; } -static GenericList * -next_list(Visitor *v, GenericList **list, Error **errp) +static GenericList *next_list(Visitor *v, GenericList **list, size_t size) { - StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); + StringOutputVisitor *sov = to_sov(v); GenericList *ret = NULL; if (*list) { if (sov->head) { @@ -289,10 +302,9 @@ next_list(Visitor *v, GenericList **list, Error **errp) return ret; } -static void -end_list(Visitor *v, Error **errp) +static void end_list(Visitor *v) { - StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); + StringOutputVisitor *sov = to_sov(v); assert(sov->list_mode == LM_STARTED || sov->list_mode == LM_END || @@ -340,7 +352,8 @@ StringOutputVisitor *string_output_visitor_new(bool human) v->string = g_string_new(NULL); v->human = human; v->visitor.type_enum = output_type_enum; - v->visitor.type_int = print_type_int; + v->visitor.type_int64 = print_type_int64; + v->visitor.type_uint64 = print_type_uint64; v->visitor.type_size = print_type_size; v->visitor.type_bool = print_type_bool; v->visitor.type_str = print_type_str; diff --git a/qemu/qdev-monitor.c b/qemu/qdev-monitor.c index f9e2d6258..e19617fa8 100644 --- a/qemu/qdev-monitor.c +++ b/qemu/qdev-monitor.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "hw/qdev.h" #include "hw/sysbus.h" #include "monitor/monitor.h" @@ -26,6 +27,7 @@ #include "qapi/qmp/qerror.h" #include "qemu/config-file.h" #include "qemu/error-report.h" +#include "qemu/help_option.h" /* * Aliases were a bad idea from the start. Let's keep them @@ -38,18 +40,39 @@ typedef struct QDevAlias uint32_t arch_mask; } QDevAlias; +/* Please keep this table sorted by typename. */ static const QDevAlias qdev_alias_table[] = { - { "virtio-blk-pci", "virtio-blk", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, - { "virtio-net-pci", "virtio-net", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, - { "virtio-serial-pci", "virtio-serial", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, + { "e1000", "e1000-82540em" }, + { "ich9-ahci", "ahci" }, + { "kvm-pci-assign", "pci-assign" }, + { "lsi53c895a", "lsi" }, + { "virtio-9p-ccw", "virtio-9p", QEMU_ARCH_S390X }, + { "virtio-9p-pci", "virtio-9p", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, + { "virtio-balloon-ccw", "virtio-balloon", QEMU_ARCH_S390X }, { "virtio-balloon-pci", "virtio-balloon", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, { "virtio-blk-ccw", "virtio-blk", QEMU_ARCH_S390X }, + { "virtio-blk-pci", "virtio-blk", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, + { "virtio-gpu-ccw", "virtio-gpu", QEMU_ARCH_S390X }, + { "virtio-gpu-pci", "virtio-gpu", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, + { "virtio-input-host-ccw", "virtio-input-host", QEMU_ARCH_S390X }, + { "virtio-input-host-pci", "virtio-input-host", + QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, + { "virtio-keyboard-ccw", "virtio-keyboard", QEMU_ARCH_S390X }, + { "virtio-keyboard-pci", "virtio-keyboard", + QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, + { "virtio-mouse-ccw", "virtio-mouse", QEMU_ARCH_S390X }, + { "virtio-mouse-pci", "virtio-mouse", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, { "virtio-net-ccw", "virtio-net", QEMU_ARCH_S390X }, + { "virtio-net-pci", "virtio-net", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, + { "virtio-rng-ccw", "virtio-rng", QEMU_ARCH_S390X }, + { "virtio-rng-pci", "virtio-rng", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, + { "virtio-scsi-ccw", "virtio-scsi", QEMU_ARCH_S390X }, + { "virtio-scsi-pci", "virtio-scsi", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, { "virtio-serial-ccw", "virtio-serial", QEMU_ARCH_S390X }, - { "lsi53c895a", "lsi" }, - { "ich9-ahci", "ahci" }, - { "kvm-pci-assign", "pci-assign" }, + { "virtio-serial-pci", "virtio-serial", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, + { "virtio-tablet-ccw", "virtio-tablet", QEMU_ARCH_S390X }, + { "virtio-tablet-pci", "virtio-tablet", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, { } }; @@ -186,6 +209,7 @@ static DeviceClass *qdev_get_device_class(const char **driver, Error **errp) { ObjectClass *oc; DeviceClass *dc; + const char *original_name = *driver; oc = object_class_by_name(*driver); if (!oc) { @@ -198,7 +222,12 @@ static DeviceClass *qdev_get_device_class(const char **driver, Error **errp) } if (!object_class_dynamic_cast(oc, TYPE_DEVICE)) { - error_setg(errp, "'%s' is not a valid device model name", *driver); + if (*driver != original_name) { + error_setg(errp, "'%s' (alias '%s') is not a valid device model" + " name", original_name, *driver); + } else { + error_setg(errp, "'%s' is not a valid device model name", *driver); + } return NULL; } @@ -237,9 +266,12 @@ int qdev_device_help(QemuOpts *opts) return 0; } - qdev_get_device_class(&driver, &local_err); - if (local_err) { - goto error; + if (!object_class_by_name(driver)) { + const char *typename = find_typename_by_alias(driver); + + if (typename) { + driver = typename; + } } prop_list = qmp_device_list_properties(driver, &local_err); @@ -262,8 +294,7 @@ int qdev_device_help(QemuOpts *opts) return 1; error: - error_printf("%s\n", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); return 1; } @@ -289,37 +320,37 @@ static Object *qdev_get_peripheral_anon(void) return dev; } -#if 0 /* conversion from qerror_report() to error_set() broke their use */ -static void qbus_list_bus(DeviceState *dev) +static void qbus_list_bus(DeviceState *dev, Error **errp) { BusState *child; const char *sep = " "; - error_printf("child buses at \"%s\":", - dev->id ? dev->id : object_get_typename(OBJECT(dev))); + error_append_hint(errp, "child buses at \"%s\":", + dev->id ? dev->id : object_get_typename(OBJECT(dev))); QLIST_FOREACH(child, &dev->child_bus, sibling) { - error_printf("%s\"%s\"", sep, child->name); + error_append_hint(errp, "%s\"%s\"", sep, child->name); sep = ", "; } - error_printf("\n"); + error_append_hint(errp, "\n"); } -static void qbus_list_dev(BusState *bus) +static void qbus_list_dev(BusState *bus, Error **errp) { BusChild *kid; const char *sep = " "; - error_printf("devices at \"%s\":", bus->name); + error_append_hint(errp, "devices at \"%s\":", bus->name); QTAILQ_FOREACH(kid, &bus->children, sibling) { DeviceState *dev = kid->child; - error_printf("%s\"%s\"", sep, object_get_typename(OBJECT(dev))); - if (dev->id) - error_printf("/\"%s\"", dev->id); + error_append_hint(errp, "%s\"%s\"", sep, + object_get_typename(OBJECT(dev))); + if (dev->id) { + error_append_hint(errp, "/\"%s\"", dev->id); + } sep = ", "; } - error_printf("\n"); + error_append_hint(errp, "\n"); } -#endif static BusState *qbus_find_bus(DeviceState *dev, char *elem) { @@ -461,11 +492,7 @@ static BusState *qbus_find(const char *path, Error **errp) if (!dev) { error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "Device '%s' not found", elem); -#if 0 /* conversion from qerror_report() to error_set() broke this: */ - if (!monitor_cur_is_qmp()) { - qbus_list_dev(bus); - } -#endif + qbus_list_dev(bus, errp); return NULL; } @@ -483,11 +510,7 @@ static BusState *qbus_find(const char *path, Error **errp) if (dev->num_child_bus) { error_setg(errp, "Device '%s' has multiple child buses", elem); -#if 0 /* conversion from qerror_report() to error_set() broke this: */ - if (!monitor_cur_is_qmp()) { - qbus_list_bus(dev); - } -#endif + qbus_list_bus(dev, errp); } else { error_setg(errp, "Device '%s' has no child bus", elem); } @@ -503,11 +526,7 @@ static BusState *qbus_find(const char *path, Error **errp) bus = qbus_find_bus(dev, elem); if (!bus) { error_setg(errp, "Bus '%s' not found", elem); -#if 0 /* conversion from qerror_report() to error_set() broke this: */ - if (!monitor_cur_is_qmp()) { - qbus_list_bus(dev); - } -#endif + qbus_list_bus(dev, errp); return NULL; } } @@ -785,12 +804,17 @@ void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp) void qmp_device_del(const char *id, Error **errp) { Object *obj; - char *root_path = object_get_canonical_path(qdev_get_peripheral()); - char *path = g_strdup_printf("%s/%s", root_path, id); - g_free(root_path); - obj = object_resolve_path_type(path, TYPE_DEVICE, NULL); - g_free(path); + if (id[0] == '/') { + obj = object_resolve_path(id, NULL); + } else { + char *root_path = object_get_canonical_path(qdev_get_peripheral()); + char *path = g_strdup_printf("%s/%s", root_path, id); + + g_free(root_path); + obj = object_resolve_path_type(path, TYPE_DEVICE, NULL); + g_free(path); + } if (!obj) { error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, @@ -798,6 +822,11 @@ void qmp_device_del(const char *id, Error **errp) return; } + if (!object_dynamic_cast(obj, TYPE_DEVICE)) { + error_setg(errp, "%s is not a hotpluggable device", id); + return; + } + qdev_unplug(DEVICE(obj), errp); } diff --git a/qemu/qemu-bridge-helper.c b/qemu/qemu-bridge-helper.c index 36eb3bcfd..830fb9e26 100644 --- a/qemu/qemu-bridge-helper.c +++ b/qemu/qemu-bridge-helper.c @@ -13,19 +13,10 @@ * */ -#include "config-host.h" - -#include -#include -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" + #include -#include #include #include #include diff --git a/qemu/qemu-char.c b/qemu/qemu-char.c index d956f8db6..b597ee19c 100644 --- a/qemu/qemu-char.c +++ b/qemu/qemu-char.c @@ -21,9 +21,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" +#include "qemu/cutils.h" #include "monitor/monitor.h" #include "sysemu/sysemu.h" +#include "sysemu/block-backend.h" #include "qemu/error-report.h" #include "qemu/timer.h" #include "sysemu/char.h" @@ -32,12 +35,12 @@ #include "qapi/qmp-input-visitor.h" #include "qapi/qmp-output-visitor.h" #include "qapi-visit.h" +#include "qemu/base64.h" +#include "io/channel-socket.h" +#include "io/channel-file.h" +#include "io/channel-tls.h" +#include "sysemu/replay.h" -#include -#include -#include -#include -#include #include #ifndef _WIN32 @@ -51,11 +54,9 @@ #include #include #include -#include #include #include #ifdef CONFIG_BSD -#include #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #include #include @@ -69,7 +70,6 @@ #include #endif #ifdef __sun__ -#include #include #include #include @@ -88,64 +88,39 @@ #define READ_BUF_LEN 4096 #define READ_RETRIES 10 -#define CHR_MAX_FILENAME_SIZE 256 #define TCP_MAX_FDS 16 /***********************************************************/ /* Socket address helpers */ -static void qapi_copy_SocketAddress(SocketAddress **p_dest, - SocketAddress *src) -{ - QmpOutputVisitor *qov; - QmpInputVisitor *qiv; - Visitor *ov, *iv; - QObject *obj; - - *p_dest = NULL; - - qov = qmp_output_visitor_new(); - ov = qmp_output_get_visitor(qov); - visit_type_SocketAddress(ov, &src, NULL, &error_abort); - obj = qmp_output_get_qobject(qov); - qmp_output_visitor_cleanup(qov); - if (!obj) { - return; - } - - qiv = qmp_input_visitor_new(obj); - iv = qmp_input_get_visitor(qiv); - visit_type_SocketAddress(iv, p_dest, NULL, &error_abort); - qmp_input_visitor_cleanup(qiv); - qobject_decref(obj); -} -static int SocketAddress_to_str(char *dest, int max_len, - const char *prefix, SocketAddress *addr, - bool is_listen, bool is_telnet) +static char *SocketAddress_to_str(const char *prefix, SocketAddress *addr, + bool is_listen, bool is_telnet) { - switch (addr->kind) { + switch (addr->type) { case SOCKET_ADDRESS_KIND_INET: - return snprintf(dest, max_len, "%s%s:%s:%s%s", prefix, - is_telnet ? "telnet" : "tcp", addr->inet->host, - addr->inet->port, is_listen ? ",server" : ""); + return g_strdup_printf("%s%s:%s:%s%s", prefix, + is_telnet ? "telnet" : "tcp", + addr->u.inet.data->host, + addr->u.inet.data->port, + is_listen ? ",server" : ""); break; case SOCKET_ADDRESS_KIND_UNIX: - return snprintf(dest, max_len, "%sunix:%s%s", prefix, - addr->q_unix->path, is_listen ? ",server" : ""); + return g_strdup_printf("%sunix:%s%s", prefix, + addr->u.q_unix.data->path, + is_listen ? ",server" : ""); break; case SOCKET_ADDRESS_KIND_FD: - return snprintf(dest, max_len, "%sfd:%s%s", prefix, addr->fd->str, - is_listen ? ",server" : ""); + return g_strdup_printf("%sfd:%s%s", prefix, addr->u.fd.data->str, + is_listen ? ",server" : ""); break; default: abort(); } } -static int sockaddr_to_str(char *dest, int max_len, - struct sockaddr_storage *ss, socklen_t ss_len, - struct sockaddr_storage *ps, socklen_t ps_len, - bool is_listen, bool is_telnet) +static char *sockaddr_to_str(struct sockaddr_storage *ss, socklen_t ss_len, + struct sockaddr_storage *ps, socklen_t ps_len, + bool is_listen, bool is_telnet) { char shost[NI_MAXHOST], sserv[NI_MAXSERV]; char phost[NI_MAXHOST], pserv[NI_MAXSERV]; @@ -154,9 +129,9 @@ static int sockaddr_to_str(char *dest, int max_len, switch (ss->ss_family) { #ifndef _WIN32 case AF_UNIX: - return snprintf(dest, max_len, "unix:%s%s", - ((struct sockaddr_un *)(ss))->sun_path, - is_listen ? ",server" : ""); + return g_strdup_printf("unix:%s%s", + ((struct sockaddr_un *)(ss))->sun_path, + is_listen ? ",server" : ""); #endif case AF_INET6: left = "["; @@ -167,14 +142,14 @@ static int sockaddr_to_str(char *dest, int max_len, sserv, sizeof(sserv), NI_NUMERICHOST | NI_NUMERICSERV); getnameinfo((struct sockaddr *) ps, ps_len, phost, sizeof(phost), pserv, sizeof(pserv), NI_NUMERICHOST | NI_NUMERICSERV); - return snprintf(dest, max_len, "%s:%s%s%s:%s%s <-> %s%s%s:%s", - is_telnet ? "telnet" : "tcp", - left, shost, right, sserv, - is_listen ? ",server" : "", - left, phost, right, pserv); + return g_strdup_printf("%s:%s%s%s:%s%s <-> %s%s%s:%s", + is_telnet ? "telnet" : "tcp", + left, shost, right, sserv, + is_listen ? ",server" : "", + left, phost, right, pserv); default: - return snprintf(dest, max_len, "unknown"); + return g_strdup_printf("unknown"); } } @@ -184,10 +159,33 @@ static int sockaddr_to_str(char *dest, int max_len, static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs = QTAILQ_HEAD_INITIALIZER(chardevs); -CharDriverState *qemu_chr_alloc(void) +static void qemu_chr_free_common(CharDriverState *chr); + +CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp) { CharDriverState *chr = g_malloc0(sizeof(CharDriverState)); qemu_mutex_init(&chr->chr_write_lock); + + if (backend->has_logfile) { + int flags = O_WRONLY | O_CREAT; + if (backend->has_logappend && + backend->logappend) { + flags |= O_APPEND; + } else { + flags |= O_TRUNC; + } + chr->logfd = qemu_open(backend->logfile, flags, 0666); + if (chr->logfd < 0) { + error_setg_errno(errp, errno, + "Unable to open logfile %s", + backend->logfile); + g_free(chr); + return NULL; + } + } else { + chr->logfd = -1; + } + return chr; } @@ -213,37 +211,107 @@ void qemu_chr_be_generic_open(CharDriverState *s) qemu_chr_be_event(s, CHR_EVENT_OPENED); } + +/* Not reporting errors from writing to logfile, as logs are + * defined to be "best effort" only */ +static void qemu_chr_fe_write_log(CharDriverState *s, + const uint8_t *buf, size_t len) +{ + size_t done = 0; + ssize_t ret; + + if (s->logfd < 0) { + return; + } + + while (done < len) { + retry: + ret = write(s->logfd, buf + done, len - done); + if (ret == -1 && errno == EAGAIN) { + g_usleep(100); + goto retry; + } + + if (ret <= 0) { + return; + } + done += ret; + } +} + +static int qemu_chr_fe_write_buffer(CharDriverState *s, const uint8_t *buf, int len, int *offset) +{ + int res = 0; + *offset = 0; + + qemu_mutex_lock(&s->chr_write_lock); + while (*offset < len) { + retry: + res = s->chr_write(s, buf + *offset, len - *offset); + if (res < 0 && errno == EAGAIN) { + g_usleep(100); + goto retry; + } + + if (res <= 0) { + break; + } + + *offset += res; + } + if (*offset > 0) { + qemu_chr_fe_write_log(s, buf, *offset); + } + qemu_mutex_unlock(&s->chr_write_lock); + + return res; +} + int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len) { int ret; + if (s->replay && replay_mode == REPLAY_MODE_PLAY) { + int offset; + replay_char_write_event_load(&ret, &offset); + assert(offset <= len); + qemu_chr_fe_write_buffer(s, buf, offset, &offset); + return ret; + } + qemu_mutex_lock(&s->chr_write_lock); ret = s->chr_write(s, buf, len); + + if (ret > 0) { + qemu_chr_fe_write_log(s, buf, ret); + } + qemu_mutex_unlock(&s->chr_write_lock); + + if (s->replay && replay_mode == REPLAY_MODE_RECORD) { + replay_char_write_event_save(ret, ret < 0 ? 0 : ret); + } + return ret; } int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len) { - int offset = 0; - int res = 0; + int offset; + int res; - qemu_mutex_lock(&s->chr_write_lock); - while (offset < len) { - do { - res = s->chr_write(s, buf + offset, len - offset); - if (res == -1 && errno == EAGAIN) { - g_usleep(100); - } - } while (res == -1 && errno == EAGAIN); + if (s->replay && replay_mode == REPLAY_MODE_PLAY) { + replay_char_write_event_load(&res, &offset); + assert(offset <= len); + qemu_chr_fe_write_buffer(s, buf, offset, &offset); + return res; + } - if (res <= 0) { - break; - } + res = qemu_chr_fe_write_buffer(s, buf, len, &offset); - offset += res; + if (s->replay && replay_mode == REPLAY_MODE_RECORD) { + replay_char_write_event_save(res, offset); } - qemu_mutex_unlock(&s->chr_write_lock); if (res < 0) { return res; @@ -259,20 +327,27 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len) if (!s->chr_sync_read) { return 0; } + + if (s->replay && replay_mode == REPLAY_MODE_PLAY) { + return replay_char_read_all_load(buf); + } while (offset < len) { - do { - res = s->chr_sync_read(s, buf + offset, len - offset); - if (res == -1 && errno == EAGAIN) { - g_usleep(100); - } - } while (res == -1 && errno == EAGAIN); + retry: + res = s->chr_sync_read(s, buf + offset, len - offset); + if (res == -1 && errno == EAGAIN) { + g_usleep(100); + goto retry; + } if (res == 0) { break; } if (res < 0) { + if (s->replay && replay_mode == REPLAY_MODE_RECORD) { + replay_char_read_all_save_error(res); + } return res; } @@ -283,14 +358,22 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len) } } + if (s->replay && replay_mode == REPLAY_MODE_RECORD) { + replay_char_read_all_save_buf(buf, offset); + } return offset; } int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg) { - if (!s->chr_ioctl) - return -ENOTSUP; - return s->chr_ioctl(s, cmd, arg); + int res; + if (!s->chr_ioctl || s->replay) { + res = -ENOTSUP; + } else { + res = s->chr_ioctl(s, cmd, arg); + } + + return res; } int qemu_chr_be_can_write(CharDriverState *s) @@ -300,17 +383,35 @@ int qemu_chr_be_can_write(CharDriverState *s) return s->chr_can_read(s->handler_opaque); } -void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len) +void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len) { if (s->chr_read) { s->chr_read(s->handler_opaque, buf, len); } } +void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len) +{ + if (s->replay) { + if (replay_mode == REPLAY_MODE_PLAY) { + return; + } + replay_chr_be_write(s, buf, len); + } else { + qemu_chr_be_write_impl(s, buf, len); + } +} + int qemu_chr_fe_get_msgfd(CharDriverState *s) { int fd; - return (qemu_chr_fe_get_msgfds(s, &fd, 1) == 1) ? fd : -1; + int res = (qemu_chr_fe_get_msgfds(s, &fd, 1) == 1) ? fd : -1; + if (s->replay) { + fprintf(stderr, + "Replay: get msgfd is not supported for serial devices yet\n"); + exit(1); + } + return res; } int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, int len) @@ -384,11 +485,18 @@ static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) return len; } -static CharDriverState *qemu_chr_open_null(void) +static CharDriverState *qemu_chr_open_null(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, + Error **errp) { CharDriverState *chr; + ChardevCommon *common = backend->u.null.data; - chr = qemu_chr_alloc(); + chr = qemu_chr_alloc(common, errp); + if (!chr) { + return NULL; + } chr->chr_write = null_chr_write; chr->explicit_be_open = true; return chr; @@ -524,7 +632,7 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) break; } case 's': - bdrv_commit_all(); + blk_commit_all(); break; case 'b': qemu_chr_be_event(chr, CHR_EVENT_BREAK); @@ -680,13 +788,26 @@ static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond) return d->drv->chr_add_watch(d->drv, cond); } -static CharDriverState *qemu_chr_open_mux(CharDriverState *drv) +static CharDriverState *qemu_chr_open_mux(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, Error **errp) { - CharDriverState *chr; + ChardevMux *mux = backend->u.mux.data; + CharDriverState *chr, *drv; MuxDriver *d; + ChardevCommon *common = qapi_ChardevMux_base(mux); + + drv = qemu_chr_find(mux->chardev); + if (drv == NULL) { + error_setg(errp, "mux: base chardev %s not found", mux->chardev); + return NULL; + } - chr = qemu_chr_alloc(); - d = g_malloc0(sizeof(MuxDriver)); + chr = qemu_chr_alloc(common, errp); + if (!chr) { + return NULL; + } + d = g_new0(MuxDriver, 1); chr->opaque = d; d->drv = drv; @@ -709,82 +830,11 @@ static CharDriverState *qemu_chr_open_mux(CharDriverState *drv) } -#ifdef _WIN32 -int send_all(int fd, const void *buf, int len1) -{ - int ret, len; - - len = len1; - while (len > 0) { - ret = send(fd, buf, len, 0); - if (ret < 0) { - errno = WSAGetLastError(); - if (errno != WSAEWOULDBLOCK) { - return -1; - } - } else if (ret == 0) { - break; - } else { - buf += ret; - len -= ret; - } - } - return len1 - len; -} - -#else - -int send_all(int fd, const void *_buf, int len1) -{ - int ret, len; - const uint8_t *buf = _buf; - - len = len1; - while (len > 0) { - ret = write(fd, buf, len); - if (ret < 0) { - if (errno != EINTR && errno != EAGAIN) - return -1; - } else if (ret == 0) { - break; - } else { - buf += ret; - len -= ret; - } - } - return len1 - len; -} - -int recv_all(int fd, void *_buf, int len1, bool single_read) -{ - int ret, len; - uint8_t *buf = _buf; - - len = len1; - while ((len > 0) && (ret = read(fd, buf, len)) != 0) { - if (ret < 0) { - if (errno != EINTR && errno != EAGAIN) { - return -1; - } - continue; - } else { - if (single_read) { - return ret; - } - buf += ret; - len -= ret; - } - } - return len1 - len; -} - -#endif /* !_WIN32 */ - typedef struct IOWatchPoll { GSource parent; - GIOChannel *channel; + QIOChannel *ioc; GSource *src; IOCanReadHandler *fd_can_read; @@ -807,8 +857,8 @@ static gboolean io_watch_poll_prepare(GSource *source, gint *timeout_) } if (now_active) { - iwp->src = g_io_create_watch(iwp->channel, - G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL); + iwp->src = qio_channel_create_watch( + iwp->ioc, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL); g_source_set_callback(iwp->src, iwp->fd_read, iwp->opaque, NULL); g_source_attach(iwp->src, NULL); } else { @@ -854,9 +904,9 @@ static GSourceFuncs io_watch_poll_funcs = { }; /* Can only be used for read */ -static guint io_add_watch_poll(GIOChannel *channel, +static guint io_add_watch_poll(QIOChannel *ioc, IOCanReadHandler *fd_can_read, - GIOFunc fd_read, + QIOChannelFunc fd_read, gpointer user_data) { IOWatchPoll *iwp; @@ -865,7 +915,7 @@ static guint io_add_watch_poll(GIOChannel *channel, iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs, sizeof(IOWatchPoll)); iwp->fd_can_read = fd_can_read; iwp->opaque = user_data; - iwp->channel = channel; + iwp->ioc = ioc; iwp->fd_read = (GSourceFunc) fd_read; iwp->src = NULL; @@ -901,79 +951,50 @@ static void remove_fd_in_watch(CharDriverState *chr) } } -#ifndef _WIN32 -static GIOChannel *io_channel_from_fd(int fd) -{ - GIOChannel *chan; - - if (fd == -1) { - return NULL; - } - - chan = g_io_channel_unix_new(fd); - - g_io_channel_set_encoding(chan, NULL, NULL); - g_io_channel_set_buffered(chan, FALSE); - return chan; -} -#endif - -static GIOChannel *io_channel_from_socket(int fd) +static int io_channel_send_full(QIOChannel *ioc, + const void *buf, size_t len, + int *fds, size_t nfds) { - GIOChannel *chan; + size_t offset = 0; - if (fd == -1) { - return NULL; - } + while (offset < len) { + ssize_t ret = 0; + struct iovec iov = { .iov_base = (char *)buf + offset, + .iov_len = len - offset }; + + ret = qio_channel_writev_full( + ioc, &iov, 1, + fds, nfds, NULL); + if (ret == QIO_CHANNEL_ERR_BLOCK) { + if (offset) { + return offset; + } -#ifdef _WIN32 - chan = g_io_channel_win32_new_socket(fd); -#else - chan = g_io_channel_unix_new(fd); -#endif + errno = EAGAIN; + return -1; + } else if (ret < 0) { + errno = EINVAL; + return -1; + } - g_io_channel_set_encoding(chan, NULL, NULL); - g_io_channel_set_buffered(chan, FALSE); + offset += ret; + } - return chan; + return offset; } -static int io_channel_send(GIOChannel *fd, const void *buf, size_t len) -{ - size_t offset = 0; - GIOStatus status = G_IO_STATUS_NORMAL; - - while (offset < len && status == G_IO_STATUS_NORMAL) { - gsize bytes_written = 0; - - status = g_io_channel_write_chars(fd, buf + offset, len - offset, - &bytes_written, NULL); - offset += bytes_written; - } - if (offset > 0) { - return offset; - } - switch (status) { - case G_IO_STATUS_NORMAL: - g_assert(len == 0); - return 0; - case G_IO_STATUS_AGAIN: - errno = EAGAIN; - return -1; - default: - break; - } - errno = EINVAL; - return -1; +#ifndef _WIN32 +static int io_channel_send(QIOChannel *ioc, const void *buf, size_t len) +{ + return io_channel_send_full(ioc, buf, len, NULL, 0); } -#ifndef _WIN32 typedef struct FDCharDriver { CharDriverState *chr; - GIOChannel *fd_in, *fd_out; + QIOChannel *ioc_in, *ioc_out; int max_size; } FDCharDriver; @@ -982,17 +1003,16 @@ static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { FDCharDriver *s = chr->opaque; - return io_channel_send(s->fd_out, buf, len); + return io_channel_send(s->ioc_out, buf, len); } -static gboolean fd_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) +static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) { CharDriverState *chr = opaque; FDCharDriver *s = chr->opaque; int len; uint8_t buf[READ_BUF_LEN]; - GIOStatus status; - gsize bytes_read; + ssize_t ret; len = sizeof(buf); if (len > s->max_size) { @@ -1002,15 +1022,15 @@ static gboolean fd_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) return TRUE; } - status = g_io_channel_read_chars(chan, (gchar *)buf, - len, &bytes_read, NULL); - if (status == G_IO_STATUS_EOF) { + ret = qio_channel_read( + chan, (gchar *)buf, len, NULL); + if (ret == 0) { remove_fd_in_watch(chr); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); return FALSE; } - if (status == G_IO_STATUS_NORMAL) { - qemu_chr_be_write(chr, buf, bytes_read); + if (ret > 0) { + qemu_chr_be_write(chr, buf, ret); } return TRUE; @@ -1028,7 +1048,7 @@ static int fd_chr_read_poll(void *opaque) static GSource *fd_chr_add_watch(CharDriverState *chr, GIOCondition cond) { FDCharDriver *s = chr->opaque; - return g_io_create_watch(s->fd_out, cond); + return qio_channel_create_watch(s->ioc_out, cond); } static void fd_chr_update_read_handler(CharDriverState *chr) @@ -1036,8 +1056,9 @@ static void fd_chr_update_read_handler(CharDriverState *chr) FDCharDriver *s = chr->opaque; remove_fd_in_watch(chr); - if (s->fd_in) { - chr->fd_in_tag = io_add_watch_poll(s->fd_in, fd_chr_read_poll, + if (s->ioc_in) { + chr->fd_in_tag = io_add_watch_poll(s->ioc_in, + fd_chr_read_poll, fd_chr_read, chr); } } @@ -1047,11 +1068,11 @@ static void fd_chr_close(struct CharDriverState *chr) FDCharDriver *s = chr->opaque; remove_fd_in_watch(chr); - if (s->fd_in) { - g_io_channel_unref(s->fd_in); + if (s->ioc_in) { + object_unref(OBJECT(s->ioc_in)); } - if (s->fd_out) { - g_io_channel_unref(s->fd_out); + if (s->ioc_out) { + object_unref(OBJECT(s->ioc_out)); } g_free(s); @@ -1059,15 +1080,19 @@ static void fd_chr_close(struct CharDriverState *chr) } /* open a character device to a unix fd */ -static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out) +static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out, + ChardevCommon *backend, Error **errp) { CharDriverState *chr; FDCharDriver *s; - chr = qemu_chr_alloc(); - s = g_malloc0(sizeof(FDCharDriver)); - s->fd_in = io_channel_from_fd(fd_in); - s->fd_out = io_channel_from_fd(fd_out); + chr = qemu_chr_alloc(backend, errp); + if (!chr) { + return NULL; + } + s = g_new0(FDCharDriver, 1); + s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in)); + s->ioc_out = QIO_CHANNEL(qio_channel_file_new_fd(fd_out)); qemu_set_nonblock(fd_out); s->chr = chr; chr->opaque = s; @@ -1079,22 +1104,25 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out) return chr; } -static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts) +static CharDriverState *qemu_chr_open_pipe(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, + Error **errp) { + ChardevHostdev *opts = backend->u.pipe.data; int fd_in, fd_out; - char filename_in[CHR_MAX_FILENAME_SIZE]; - char filename_out[CHR_MAX_FILENAME_SIZE]; + char *filename_in; + char *filename_out; const char *filename = opts->device; + ChardevCommon *common = qapi_ChardevHostdev_base(opts); - if (filename == NULL) { - fprintf(stderr, "chardev: pipe: no filename given\n"); - return NULL; - } - snprintf(filename_in, CHR_MAX_FILENAME_SIZE, "%s.in", filename); - snprintf(filename_out, CHR_MAX_FILENAME_SIZE, "%s.out", filename); + filename_in = g_strdup_printf("%s.in", filename); + filename_out = g_strdup_printf("%s.out", filename); TFR(fd_in = qemu_open(filename_in, O_RDWR | O_BINARY)); TFR(fd_out = qemu_open(filename_out, O_RDWR | O_BINARY)); + g_free(filename_in); + g_free(filename_out); if (fd_in < 0 || fd_out < 0) { if (fd_in >= 0) close(fd_in); @@ -1102,10 +1130,11 @@ static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts) close(fd_out); TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY)); if (fd_in < 0) { + error_setg_file_open(errp, errno, filename); return NULL; } } - return qemu_chr_open_fd(fd_in, fd_out); + return qemu_chr_open_fd(fd_in, fd_out, common, errp); } /* init terminal so that we can grab keys */ @@ -1157,19 +1186,24 @@ static void qemu_chr_close_stdio(struct CharDriverState *chr) fd_chr_close(chr); } -static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) +static CharDriverState *qemu_chr_open_stdio(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, + Error **errp) { + ChardevStdio *opts = backend->u.stdio.data; CharDriverState *chr; struct sigaction act; + ChardevCommon *common = qapi_ChardevStdio_base(opts); if (is_daemonized()) { - error_report("cannot use stdio with -daemonize"); + error_setg(errp, "cannot use stdio with -daemonize"); return NULL; } if (stdio_in_use) { - error_report("cannot use stdio by multiple character devices"); - exit(1); + error_setg(errp, "cannot use stdio by multiple character devices"); + return NULL; } stdio_in_use = true; @@ -1182,7 +1216,7 @@ static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) act.sa_handler = term_stdio_handler; sigaction(SIGCONT, &act, NULL); - chr = qemu_chr_open_fd(0, 1); + chr = qemu_chr_open_fd(0, 1, common, errp); chr->chr_close = qemu_chr_close_stdio; chr->chr_set_echo = qemu_chr_set_echo_stdio; if (opts->has_signal) { @@ -1197,10 +1231,11 @@ static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \ || defined(__GLIBC__) -#define HAVE_CHARDEV_TTY 1 +#define HAVE_CHARDEV_SERIAL 1 +#define HAVE_CHARDEV_PTY 1 typedef struct { - GIOChannel *fd; + QIOChannel *ioc; int read_bytes; /* Protected by the CharDriverState chr_write_lock. */ @@ -1250,11 +1285,17 @@ static void pty_chr_update_read_handler_locked(CharDriverState *chr) { PtyCharDriver *s = chr->opaque; GPollFD pfd; + int rc; + QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc); - pfd.fd = g_io_channel_unix_get_fd(s->fd); + pfd.fd = fioc->fd; pfd.events = G_IO_OUT; pfd.revents = 0; - g_poll(&pfd, 1, 0); + do { + rc = g_poll(&pfd, 1, 0); + } while (rc == -1 && errno == EINTR); + assert(rc >= 0); + if (pfd.revents & G_IO_HUP) { pty_chr_state(chr, 0); } else { @@ -1281,7 +1322,7 @@ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len) return 0; } } - return io_channel_send(s->fd, buf, len); + return io_channel_send(s->ioc, buf, len); } static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond) @@ -1290,7 +1331,7 @@ static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond) if (!s->connected) { return NULL; } - return g_io_create_watch(s->fd, cond); + return qio_channel_create_watch(s->ioc, cond); } static int pty_chr_read_poll(void *opaque) @@ -1302,13 +1343,13 @@ static int pty_chr_read_poll(void *opaque) return s->read_bytes; } -static gboolean pty_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) +static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) { CharDriverState *chr = opaque; PtyCharDriver *s = chr->opaque; - gsize size, len; + gsize len; uint8_t buf[READ_BUF_LEN]; - GIOStatus status; + ssize_t ret; len = sizeof(buf); if (len > s->read_bytes) @@ -1316,13 +1357,13 @@ static gboolean pty_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) if (len == 0) { return TRUE; } - status = g_io_channel_read_chars(s->fd, (gchar *)buf, len, &size, NULL); - if (status != G_IO_STATUS_NORMAL) { + ret = qio_channel_read(s->ioc, (char *)buf, len, NULL); + if (ret <= 0) { pty_chr_state(chr, 0); return FALSE; } else { pty_chr_state(chr, 1); - qemu_chr_be_write(chr, buf, size); + qemu_chr_be_write(chr, buf, ret); } return TRUE; } @@ -1364,7 +1405,8 @@ static void pty_chr_state(CharDriverState *chr, int connected) s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr); } if (!chr->fd_in_tag) { - chr->fd_in_tag = io_add_watch_poll(s->fd, pty_chr_read_poll, + chr->fd_in_tag = io_add_watch_poll(s->ioc, + pty_chr_read_poll, pty_chr_read, chr); } } @@ -1373,13 +1415,10 @@ static void pty_chr_state(CharDriverState *chr, int connected) static void pty_chr_close(struct CharDriverState *chr) { PtyCharDriver *s = chr->opaque; - int fd; qemu_mutex_lock(&chr->chr_write_lock); pty_chr_state(chr, 0); - fd = g_io_channel_unix_get_fd(s->fd); - g_io_channel_unref(s->fd); - close(fd); + object_unref(OBJECT(s->ioc)); if (s->timer_tag) { g_source_remove(s->timer_tag); s->timer_tag = 0; @@ -1390,22 +1429,30 @@ static void pty_chr_close(struct CharDriverState *chr) } static CharDriverState *qemu_chr_open_pty(const char *id, - ChardevReturn *ret) + ChardevBackend *backend, + ChardevReturn *ret, + Error **errp) { CharDriverState *chr; PtyCharDriver *s; int master_fd, slave_fd; char pty_name[PATH_MAX]; + ChardevCommon *common = backend->u.pty.data; master_fd = qemu_openpty_raw(&slave_fd, pty_name); if (master_fd < 0) { + error_setg_errno(errp, errno, "Failed to create PTY"); return NULL; } close(slave_fd); qemu_set_nonblock(master_fd); - chr = qemu_chr_alloc(); + chr = qemu_chr_alloc(common, errp); + if (!chr) { + close(master_fd); + return NULL; + } chr->filename = g_strdup_printf("pty:%s", pty_name); ret->pty = g_strdup(pty_name); @@ -1414,7 +1461,7 @@ static CharDriverState *qemu_chr_open_pty(const char *id, fprintf(stderr, "char device redirected to %s (label %s)\n", pty_name, id); - s = g_malloc0(sizeof(PtyCharDriver)); + s = g_new0(PtyCharDriver, 1); chr->opaque = s; chr->chr_write = pty_chr_write; chr->chr_update_read_handler = pty_chr_update_read_handler; @@ -1422,7 +1469,7 @@ static CharDriverState *qemu_chr_open_pty(const char *id, chr->chr_add_watch = pty_chr_add_watch; chr->explicit_be_open = true; - s->fd = io_channel_from_fd(master_fd); + s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd)); s->timer_tag = 0; return chr; @@ -1546,12 +1593,13 @@ static void tty_serial_init(int fd, int speed, static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) { FDCharDriver *s = chr->opaque; + QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc_in); switch(cmd) { case CHR_IOCTL_SERIAL_SET_PARAMS: { QEMUSerialSetParams *ssp = arg; - tty_serial_init(g_io_channel_unix_get_fd(s->fd_in), + tty_serial_init(fioc->fd, ssp->speed, ssp->parity, ssp->data_bits, ssp->stop_bits); } @@ -1560,7 +1608,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) { int enable = *(int *)arg; if (enable) { - tcsendbreak(g_io_channel_unix_get_fd(s->fd_in), 1); + tcsendbreak(fioc->fd, 1); } } break; @@ -1568,7 +1616,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) { int sarg = 0; int *targ = (int *)arg; - ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMGET, &sarg); + ioctl(fioc->fd, TIOCMGET, &sarg); *targ = 0; if (sarg & TIOCM_CTS) *targ |= CHR_TIOCM_CTS; @@ -1588,7 +1636,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) { int sarg = *(int *)arg; int targ = 0; - ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMGET, &targ); + ioctl(fioc->fd, TIOCMGET, &targ); targ &= ~(CHR_TIOCM_CTS | CHR_TIOCM_CAR | CHR_TIOCM_DSR | CHR_TIOCM_RI | CHR_TIOCM_DTR | CHR_TIOCM_RTS); if (sarg & CHR_TIOCM_CTS) @@ -1603,7 +1651,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) targ |= TIOCM_DTR; if (sarg & CHR_TIOCM_RTS) targ |= TIOCM_RTS; - ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMSET, &targ); + ioctl(fioc->fd, TIOCMSET, &targ); } break; default: @@ -1614,26 +1662,17 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) static void qemu_chr_close_tty(CharDriverState *chr) { - FDCharDriver *s = chr->opaque; - int fd = -1; - - if (s) { - fd = g_io_channel_unix_get_fd(s->fd_in); - } - fd_chr_close(chr); - - if (fd >= 0) { - close(fd); - } } -static CharDriverState *qemu_chr_open_tty_fd(int fd) +static CharDriverState *qemu_chr_open_tty_fd(int fd, + ChardevCommon *backend, + Error **errp) { CharDriverState *chr; tty_serial_init(fd, 115200, 'N', 8, 1); - chr = qemu_chr_open_fd(fd, fd); + chr = qemu_chr_open_fd(fd, fd, backend, errp); chr->chr_ioctl = tty_serial_ioctl; chr->chr_close = qemu_chr_close_tty; return chr; @@ -1753,25 +1792,32 @@ static void pp_close(CharDriverState *chr) qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_pp_fd(int fd) +static CharDriverState *qemu_chr_open_pp_fd(int fd, + ChardevCommon *backend, + Error **errp) { CharDriverState *chr; ParallelCharDriver *drv; if (ioctl(fd, PPCLAIM) < 0) { + error_setg_errno(errp, errno, "not a parallel port"); close(fd); return NULL; } - drv = g_malloc0(sizeof(ParallelCharDriver)); - drv->fd = fd; - drv->mode = IEEE1284_MODE_COMPAT; + chr = qemu_chr_alloc(backend, errp); + if (!chr) { + return NULL; + } - chr = qemu_chr_alloc(); + drv = g_new0(ParallelCharDriver, 1); + chr->opaque = drv; chr->chr_write = null_chr_write; chr->chr_ioctl = pp_ioctl; chr->chr_close = pp_close; - chr->opaque = drv; + + drv->fd = fd; + drv->mode = IEEE1284_MODE_COMPAT; return chr; } @@ -1818,11 +1864,16 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) return 0; } -static CharDriverState *qemu_chr_open_pp_fd(int fd) +static CharDriverState *qemu_chr_open_pp_fd(int fd, + ChardevCommon *backend, + Error **errp) { CharDriverState *chr; - chr = qemu_chr_alloc(); + chr = qemu_chr_alloc(backend, errp); + if (!chr) { + return NULL; + } chr->opaque = (void *)(intptr_t)fd; chr->chr_write = null_chr_write; chr->chr_ioctl = pp_ioctl; @@ -1833,6 +1884,8 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd) #else /* _WIN32 */ +#define HAVE_CHARDEV_SERIAL 1 + typedef struct { int max_size; HANDLE hcom, hrecv, hsend; @@ -1884,7 +1937,7 @@ static void win_chr_close(CharDriverState *chr) qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static int win_chr_init(CharDriverState *chr, const char *filename) +static int win_chr_init(CharDriverState *chr, const char *filename, Error **errp) { WinCharState *s = chr->opaque; COMMCONFIG comcfg; @@ -1895,25 +1948,25 @@ static int win_chr_init(CharDriverState *chr, const char *filename) s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL); if (!s->hsend) { - fprintf(stderr, "Failed CreateEvent\n"); + error_setg(errp, "Failed CreateEvent"); goto fail; } s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL); if (!s->hrecv) { - fprintf(stderr, "Failed CreateEvent\n"); + error_setg(errp, "Failed CreateEvent"); goto fail; } s->hcom = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); if (s->hcom == INVALID_HANDLE_VALUE) { - fprintf(stderr, "Failed CreateFile (%lu)\n", GetLastError()); + error_setg(errp, "Failed CreateFile (%lu)", GetLastError()); s->hcom = NULL; goto fail; } if (!SetupComm(s->hcom, NRECVBUF, NSENDBUF)) { - fprintf(stderr, "Failed SetupComm\n"); + error_setg(errp, "Failed SetupComm"); goto fail; } @@ -1924,23 +1977,23 @@ static int win_chr_init(CharDriverState *chr, const char *filename) CommConfigDialog(filename, NULL, &comcfg); if (!SetCommState(s->hcom, &comcfg.dcb)) { - fprintf(stderr, "Failed SetCommState\n"); + error_setg(errp, "Failed SetCommState"); goto fail; } if (!SetCommMask(s->hcom, EV_ERR)) { - fprintf(stderr, "Failed SetCommMask\n"); + error_setg(errp, "Failed SetCommMask"); goto fail; } cto.ReadIntervalTimeout = MAXDWORD; if (!SetCommTimeouts(s->hcom, &cto)) { - fprintf(stderr, "Failed SetCommTimeouts\n"); + error_setg(errp, "Failed SetCommTimeouts"); goto fail; } if (!ClearCommError(s->hcom, &err, &comstat)) { - fprintf(stderr, "Failed ClearCommError\n"); + error_setg(errp, "Failed ClearCommError"); goto fail; } qemu_add_polling_cb(win_chr_poll, chr); @@ -2045,20 +2098,25 @@ static int win_chr_poll(void *opaque) return 0; } -static CharDriverState *qemu_chr_open_win_path(const char *filename) +static CharDriverState *qemu_chr_open_win_path(const char *filename, + ChardevCommon *backend, + Error **errp) { CharDriverState *chr; WinCharState *s; - chr = qemu_chr_alloc(); - s = g_malloc0(sizeof(WinCharState)); + chr = qemu_chr_alloc(backend, errp); + if (!chr) { + return NULL; + } + s = g_new0(WinCharState, 1); chr->opaque = s; chr->chr_write = win_chr_write; chr->chr_close = win_chr_close; - if (win_chr_init(chr, filename) < 0) { + if (win_chr_init(chr, filename, errp) < 0) { g_free(s); - g_free(chr); + qemu_chr_free_common(chr); return NULL; } return chr; @@ -2080,34 +2138,36 @@ static int win_chr_pipe_poll(void *opaque) return 0; } -static int win_chr_pipe_init(CharDriverState *chr, const char *filename) +static int win_chr_pipe_init(CharDriverState *chr, const char *filename, + Error **errp) { WinCharState *s = chr->opaque; OVERLAPPED ov; int ret; DWORD size; - char openname[CHR_MAX_FILENAME_SIZE]; + char *openname; s->fpipe = TRUE; s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL); if (!s->hsend) { - fprintf(stderr, "Failed CreateEvent\n"); + error_setg(errp, "Failed CreateEvent"); goto fail; } s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL); if (!s->hrecv) { - fprintf(stderr, "Failed CreateEvent\n"); + error_setg(errp, "Failed CreateEvent"); goto fail; } - snprintf(openname, sizeof(openname), "\\\\.\\pipe\\%s", filename); + openname = g_strdup_printf("\\\\.\\pipe\\%s", filename); s->hcom = CreateNamedPipe(openname, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL); + g_free(openname); if (s->hcom == INVALID_HANDLE_VALUE) { - fprintf(stderr, "Failed CreateNamedPipe (%lu)\n", GetLastError()); + error_setg(errp, "Failed CreateNamedPipe (%lu)", GetLastError()); s->hcom = NULL; goto fail; } @@ -2116,13 +2176,13 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename) ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); ret = ConnectNamedPipe(s->hcom, &ov); if (ret) { - fprintf(stderr, "Failed ConnectNamedPipe\n"); + error_setg(errp, "Failed ConnectNamedPipe"); goto fail; } ret = GetOverlappedResult(s->hcom, &ov, &size, TRUE); if (!ret) { - fprintf(stderr, "Failed GetOverlappedResult\n"); + error_setg(errp, "Failed GetOverlappedResult"); if (ov.hEvent) { CloseHandle(ov.hEvent); ov.hEvent = NULL; @@ -2143,42 +2203,60 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename) } -static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts) +static CharDriverState *qemu_chr_open_pipe(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, + Error **errp) { + ChardevHostdev *opts = backend->u.pipe.data; const char *filename = opts->device; CharDriverState *chr; WinCharState *s; + ChardevCommon *common = qapi_ChardevHostdev_base(opts); - chr = qemu_chr_alloc(); - s = g_malloc0(sizeof(WinCharState)); + chr = qemu_chr_alloc(common, errp); + if (!chr) { + return NULL; + } + s = g_new0(WinCharState, 1); chr->opaque = s; chr->chr_write = win_chr_write; chr->chr_close = win_chr_close; - if (win_chr_pipe_init(chr, filename) < 0) { + if (win_chr_pipe_init(chr, filename, errp) < 0) { g_free(s); - g_free(chr); + qemu_chr_free_common(chr); return NULL; } return chr; } -static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out) +static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out, + ChardevCommon *backend, + Error **errp) { CharDriverState *chr; WinCharState *s; - chr = qemu_chr_alloc(); - s = g_malloc0(sizeof(WinCharState)); + chr = qemu_chr_alloc(backend, errp); + if (!chr) { + return NULL; + } + s = g_new0(WinCharState, 1); s->hcom = fd_out; chr->opaque = s; chr->chr_write = win_chr_write; return chr; } -static CharDriverState *qemu_chr_open_win_con(void) +static CharDriverState *qemu_chr_open_win_con(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, + Error **errp) { - return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE)); + ChardevCommon *common = backend->u.console.data; + return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE), + common, errp); } static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len) @@ -2317,20 +2395,27 @@ static void win_stdio_close(CharDriverState *chr) g_free(chr); } -static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) +static CharDriverState *qemu_chr_open_stdio(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, + Error **errp) { CharDriverState *chr; WinStdioCharState *stdio; DWORD dwMode; int is_console = 0; + ChardevCommon *common = qapi_ChardevStdio_base(backend->u.stdio.data); - chr = qemu_chr_alloc(); - stdio = g_malloc0(sizeof(WinStdioCharState)); + chr = qemu_chr_alloc(common, errp); + if (!chr) { + return NULL; + } + stdio = g_new0(WinStdioCharState, 1); stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE); if (stdio->hStdIn == INVALID_HANDLE_VALUE) { - fprintf(stderr, "cannot open stdio: invalid handle\n"); - exit(1); + error_setg(errp, "cannot open stdio: invalid handle"); + return NULL; } is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0; @@ -2342,25 +2427,30 @@ static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) if (is_console) { if (qemu_add_wait_object(stdio->hStdIn, win_stdio_wait_func, chr)) { - fprintf(stderr, "qemu_add_wait_object: failed\n"); + error_setg(errp, "qemu_add_wait_object: failed"); + goto err1; } } else { DWORD dwId; stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); stdio->hInputDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - stdio->hInputThread = CreateThread(NULL, 0, win_stdio_thread, - chr, 0, &dwId); - - if (stdio->hInputThread == INVALID_HANDLE_VALUE - || stdio->hInputReadyEvent == INVALID_HANDLE_VALUE + if (stdio->hInputReadyEvent == INVALID_HANDLE_VALUE || stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) { - fprintf(stderr, "cannot create stdio thread or event\n"); - exit(1); + error_setg(errp, "cannot create event"); + goto err2; } if (qemu_add_wait_object(stdio->hInputReadyEvent, win_stdio_thread_wait_func, chr)) { - fprintf(stderr, "qemu_add_wait_object: failed\n"); + error_setg(errp, "qemu_add_wait_object: failed"); + goto err2; + } + stdio->hInputThread = CreateThread(NULL, 0, win_stdio_thread, + chr, 0, &dwId); + + if (stdio->hInputThread == INVALID_HANDLE_VALUE) { + error_setg(errp, "cannot create stdio thread"); + goto err3; } } @@ -2378,6 +2468,15 @@ static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) qemu_chr_fe_set_echo(chr, false); return chr; + +err3: + qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL); +err2: + CloseHandle(stdio->hInputReadyEvent); + CloseHandle(stdio->hInputDoneEvent); +err1: + qemu_del_wait_object(stdio->hStdIn, NULL, NULL); + return NULL; } #endif /* !_WIN32 */ @@ -2386,8 +2485,7 @@ static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) /* UDP Net console */ typedef struct { - int fd; - GIOChannel *chan; + QIOChannel *ioc; uint8_t buf[READ_BUF_LEN]; int bufcnt; int bufptr; @@ -2398,17 +2496,9 @@ typedef struct { static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { NetCharDriver *s = chr->opaque; - gsize bytes_written; - GIOStatus status; - - status = g_io_channel_write_chars(s->chan, (const gchar *)buf, len, &bytes_written, NULL); - if (status == G_IO_STATUS_EOF) { - return 0; - } else if (status != G_IO_STATUS_NORMAL) { - return -1; - } - return bytes_written; + return qio_channel_write( + s->ioc, (const char *)buf, len, NULL); } static int udp_chr_read_poll(void *opaque) @@ -2429,24 +2519,22 @@ static int udp_chr_read_poll(void *opaque) return s->max_size; } -static gboolean udp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) +static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) { CharDriverState *chr = opaque; NetCharDriver *s = chr->opaque; - gsize bytes_read = 0; - GIOStatus status; + ssize_t ret; if (s->max_size == 0) { return TRUE; } - status = g_io_channel_read_chars(s->chan, (gchar *)s->buf, sizeof(s->buf), - &bytes_read, NULL); - s->bufcnt = bytes_read; - s->bufptr = s->bufcnt; - if (status != G_IO_STATUS_NORMAL) { + ret = qio_channel_read( + s->ioc, (char *)s->buf, sizeof(s->buf), NULL); + if (ret <= 0) { remove_fd_in_watch(chr); return FALSE; } + s->bufcnt = ret; s->bufptr = 0; while (s->max_size > 0 && s->bufptr < s->bufcnt) { @@ -2463,8 +2551,9 @@ static void udp_chr_update_read_handler(CharDriverState *chr) NetCharDriver *s = chr->opaque; remove_fd_in_watch(chr); - if (s->chan) { - chr->fd_in_tag = io_add_watch_poll(s->chan, udp_chr_read_poll, + if (s->ioc) { + chr->fd_in_tag = io_add_watch_poll(s->ioc, + udp_chr_read_poll, udp_chr_read, chr); } } @@ -2474,24 +2563,27 @@ static void udp_chr_close(CharDriverState *chr) NetCharDriver *s = chr->opaque; remove_fd_in_watch(chr); - if (s->chan) { - g_io_channel_unref(s->chan); - closesocket(s->fd); + if (s->ioc) { + object_unref(OBJECT(s->ioc)); } g_free(s); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_udp_fd(int fd) +static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc, + ChardevCommon *backend, + Error **errp) { CharDriverState *chr = NULL; NetCharDriver *s = NULL; - chr = qemu_chr_alloc(); - s = g_malloc0(sizeof(NetCharDriver)); + chr = qemu_chr_alloc(backend, errp); + if (!chr) { + return NULL; + } + s = g_new0(NetCharDriver, 1); - s->fd = fd; - s->chan = io_channel_from_socket(s->fd); + s->ioc = QIO_CHANNEL(sioc); s->bufcnt = 0; s->bufptr = 0; chr->opaque = s; @@ -2507,19 +2599,20 @@ static CharDriverState *qemu_chr_open_udp_fd(int fd) /* TCP Net console */ typedef struct { - - GIOChannel *chan, *listen_chan; + QIOChannel *ioc; /* Client I/O channel */ + QIOChannelSocket *sioc; /* Client master channel */ + QIOChannelSocket *listen_ioc; guint listen_tag; - int fd, listen_fd; + QCryptoTLSCreds *tls_creds; int connected; int max_size; int do_telnetopt; int do_nodelay; int is_unix; int *read_msgfds; - int read_msgfds_num; + size_t read_msgfds_num; int *write_msgfds; - int write_msgfds_num; + size_t write_msgfds_num; SocketAddress *addr; bool is_listen; @@ -2553,68 +2646,27 @@ static void check_report_connect_error(CharDriverState *chr, qemu_chr_socket_restart_timer(chr); } -static gboolean tcp_chr_accept(GIOChannel *chan, GIOCondition cond, void *opaque); +static gboolean tcp_chr_accept(QIOChannel *chan, + GIOCondition cond, + void *opaque); -#ifndef _WIN32 -static int unix_send_msgfds(CharDriverState *chr, const uint8_t *buf, int len) +/* Called with chr_write_lock held. */ +static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { TCPCharDriver *s = chr->opaque; - struct msghdr msgh; - struct iovec iov; - int r; - - size_t fd_size = s->write_msgfds_num * sizeof(int); - char control[CMSG_SPACE(fd_size)]; - struct cmsghdr *cmsg; + if (s->connected) { + int ret = io_channel_send_full(s->ioc, buf, len, + s->write_msgfds, + s->write_msgfds_num); + + /* free the written msgfds, no matter what */ + if (s->write_msgfds_num) { + g_free(s->write_msgfds); + s->write_msgfds = 0; + s->write_msgfds_num = 0; + } - memset(&msgh, 0, sizeof(msgh)); - memset(control, 0, sizeof(control)); - - /* set the payload */ - iov.iov_base = (uint8_t *) buf; - iov.iov_len = len; - - msgh.msg_iov = &iov; - msgh.msg_iovlen = 1; - - msgh.msg_control = control; - msgh.msg_controllen = sizeof(control); - - cmsg = CMSG_FIRSTHDR(&msgh); - - cmsg->cmsg_len = CMSG_LEN(fd_size); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - memcpy(CMSG_DATA(cmsg), s->write_msgfds, fd_size); - - do { - r = sendmsg(s->fd, &msgh, 0); - } while (r < 0 && errno == EINTR); - - /* free the written msgfds, no matter what */ - if (s->write_msgfds_num) { - g_free(s->write_msgfds); - s->write_msgfds = 0; - s->write_msgfds_num = 0; - } - - return r; -} -#endif - -/* Called with chr_write_lock held. */ -static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) -{ - TCPCharDriver *s = chr->opaque; - if (s->connected) { -#ifndef _WIN32 - if (s->is_unix && s->write_msgfds_num) { - return unix_send_msgfds(chr, buf, len); - } else -#endif - { - return io_channel_send(s->chan, buf, len); - } + return ret; } else { /* XXX: indicate an error ? */ return len; @@ -2710,13 +2762,16 @@ static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num) { TCPCharDriver *s = chr->opaque; - /* clear old pending fd array */ - if (s->write_msgfds) { - g_free(s->write_msgfds); + if (!qio_channel_has_feature(s->ioc, + QIO_CHANNEL_FEATURE_FD_PASS)) { + return -1; } + /* clear old pending fd array */ + g_free(s->write_msgfds); + s->write_msgfds = NULL; if (num) { - s->write_msgfds = g_malloc(num * sizeof(int)); + s->write_msgfds = g_new(int, num); memcpy(s->write_msgfds, fds, num * sizeof(int)); } @@ -2725,27 +2780,33 @@ static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num) return 0; } -#ifndef _WIN32 -static void unix_process_msgfd(CharDriverState *chr, struct msghdr *msg) +static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) { TCPCharDriver *s = chr->opaque; - struct cmsghdr *cmsg; - - for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { - int fd_size, i; - - if (cmsg->cmsg_len < CMSG_LEN(sizeof(int)) || - cmsg->cmsg_level != SOL_SOCKET || - cmsg->cmsg_type != SCM_RIGHTS) { - continue; - } - - fd_size = cmsg->cmsg_len - CMSG_LEN(0); + struct iovec iov = { .iov_base = buf, .iov_len = len }; + int ret; + size_t i; + int *msgfds = NULL; + size_t msgfds_num = 0; + + if (qio_channel_has_feature(s->ioc, QIO_CHANNEL_FEATURE_FD_PASS)) { + ret = qio_channel_readv_full(s->ioc, &iov, 1, + &msgfds, &msgfds_num, + NULL); + } else { + ret = qio_channel_readv_full(s->ioc, &iov, 1, + NULL, NULL, + NULL); + } - if (!fd_size) { - continue; - } + if (ret == QIO_CHANNEL_ERR_BLOCK) { + errno = EAGAIN; + ret = -1; + } else if (ret == -1) { + errno = EIO; + } + if (msgfds_num) { /* close and clean read_msgfds */ for (i = 0; i < s->read_msgfds_num; i++) { close(s->read_msgfds[i]); @@ -2755,102 +2816,62 @@ static void unix_process_msgfd(CharDriverState *chr, struct msghdr *msg) g_free(s->read_msgfds); } - s->read_msgfds_num = fd_size / sizeof(int); - s->read_msgfds = g_malloc(fd_size); - memcpy(s->read_msgfds, CMSG_DATA(cmsg), fd_size); - - for (i = 0; i < s->read_msgfds_num; i++) { - int fd = s->read_msgfds[i]; - if (fd < 0) { - continue; - } - - /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */ - qemu_set_block(fd); - - #ifndef MSG_CMSG_CLOEXEC - qemu_set_cloexec(fd); - #endif - } + s->read_msgfds = msgfds; + s->read_msgfds_num = msgfds_num; } -} - -static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) -{ - TCPCharDriver *s = chr->opaque; - struct msghdr msg = { NULL, }; - struct iovec iov[1]; - union { - struct cmsghdr cmsg; - char control[CMSG_SPACE(sizeof(int) * TCP_MAX_FDS)]; - } msg_control; - int flags = 0; - ssize_t ret; - iov[0].iov_base = buf; - iov[0].iov_len = len; + for (i = 0; i < s->read_msgfds_num; i++) { + int fd = s->read_msgfds[i]; + if (fd < 0) { + continue; + } - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_control = &msg_control; - msg.msg_controllen = sizeof(msg_control); + /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */ + qemu_set_block(fd); -#ifdef MSG_CMSG_CLOEXEC - flags |= MSG_CMSG_CLOEXEC; +#ifndef MSG_CMSG_CLOEXEC + qemu_set_cloexec(fd); #endif - do { - ret = recvmsg(s->fd, &msg, flags); - } while (ret == -1 && errno == EINTR); - - if (ret > 0 && s->is_unix) { - unix_process_msgfd(chr, &msg); } return ret; } -#else -static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) -{ - TCPCharDriver *s = chr->opaque; - ssize_t ret; - - do { - ret = qemu_recv(s->fd, buf, len, 0); - } while (ret == -1 && socket_error() == EINTR); - - return ret; -} -#endif static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond) { TCPCharDriver *s = chr->opaque; - return g_io_create_watch(s->chan, cond); + return qio_channel_create_watch(s->ioc, cond); } static void tcp_chr_disconnect(CharDriverState *chr) { TCPCharDriver *s = chr->opaque; + if (!s->connected) { + return; + } + s->connected = 0; - if (s->listen_chan) { - s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, - tcp_chr_accept, chr); + if (s->listen_ioc) { + s->listen_tag = qio_channel_add_watch( + QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL); } + tcp_set_msgfds(chr, NULL, 0); remove_fd_in_watch(chr); - g_io_channel_unref(s->chan); - s->chan = NULL; - closesocket(s->fd); - s->fd = -1; - SocketAddress_to_str(chr->filename, CHR_MAX_FILENAME_SIZE, - "disconnected:", s->addr, s->is_listen, s->is_telnet); + object_unref(OBJECT(s->sioc)); + s->sioc = NULL; + object_unref(OBJECT(s->ioc)); + s->ioc = NULL; + g_free(chr->filename); + chr->filename = SocketAddress_to_str("disconnected:", s->addr, + s->is_listen, s->is_telnet); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); if (s->reconnect_time) { qemu_chr_socket_restart_timer(chr); } } -static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) +static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) { CharDriverState *chr = opaque; TCPCharDriver *s = chr->opaque; @@ -2864,9 +2885,7 @@ static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) if (len > s->max_size) len = s->max_size; size = tcp_chr_recv(chr, (void *)buf, len); - if (size == 0 || - (size < 0 && - socket_error() != EAGAIN && socket_error() != EWOULDBLOCK)) { + if (size == 0 || size == -1) { /* connection closed */ tcp_chr_disconnect(chr); } else if (size > 0) { @@ -2897,42 +2916,21 @@ static int tcp_chr_sync_read(CharDriverState *chr, const uint8_t *buf, int len) return size; } -#ifndef _WIN32 -CharDriverState *qemu_chr_open_eventfd(int eventfd) -{ - CharDriverState *chr = qemu_chr_open_fd(eventfd, eventfd); - - if (chr) { - chr->avail_connections = 1; - } - - return chr; -} -#endif - static void tcp_chr_connect(void *opaque) { CharDriverState *chr = opaque; TCPCharDriver *s = chr->opaque; - struct sockaddr_storage ss, ps; - socklen_t ss_len = sizeof(ss), ps_len = sizeof(ps); - - memset(&ss, 0, ss_len); - if (getsockname(s->fd, (struct sockaddr *) &ss, &ss_len) != 0) { - snprintf(chr->filename, CHR_MAX_FILENAME_SIZE, - "Error in getsockname: %s\n", strerror(errno)); - } else if (getpeername(s->fd, (struct sockaddr *) &ps, &ps_len) != 0) { - snprintf(chr->filename, CHR_MAX_FILENAME_SIZE, - "Error in getpeername: %s\n", strerror(errno)); - } else { - sockaddr_to_str(chr->filename, CHR_MAX_FILENAME_SIZE, - &ss, ss_len, &ps, ps_len, - s->is_listen, s->is_telnet); - } + + g_free(chr->filename); + chr->filename = sockaddr_to_str( + &s->sioc->localAddr, s->sioc->localAddrLen, + &s->sioc->remoteAddr, s->sioc->remoteAddrLen, + s->is_listen, s->is_telnet); s->connected = 1; - if (s->chan) { - chr->fd_in_tag = io_add_watch_poll(s->chan, tcp_chr_read_poll, + if (s->ioc) { + chr->fd_in_tag = io_add_watch_poll(s->ioc, + tcp_chr_read_poll, tcp_chr_read, chr); } qemu_chr_be_generic_open(chr); @@ -2942,83 +2940,201 @@ static void tcp_chr_update_read_handler(CharDriverState *chr) { TCPCharDriver *s = chr->opaque; + if (!s->connected) { + return; + } + remove_fd_in_watch(chr); - if (s->chan) { - chr->fd_in_tag = io_add_watch_poll(s->chan, tcp_chr_read_poll, + if (s->ioc) { + chr->fd_in_tag = io_add_watch_poll(s->ioc, + tcp_chr_read_poll, tcp_chr_read, chr); } } -#define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c; -static void tcp_chr_telnet_init(int fd) +typedef struct { + CharDriverState *chr; + char buf[12]; + size_t buflen; +} TCPCharDriverTelnetInit; + +static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc, + GIOCondition cond G_GNUC_UNUSED, + gpointer user_data) { - char buf[3]; - /* Send the telnet negotion to put telnet in binary, no echo, single char mode */ - IACSET(buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */ - send(fd, (char *)buf, 3, 0); - IACSET(buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */ - send(fd, (char *)buf, 3, 0); - IACSET(buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */ - send(fd, (char *)buf, 3, 0); - IACSET(buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */ - send(fd, (char *)buf, 3, 0); + TCPCharDriverTelnetInit *init = user_data; + ssize_t ret; + + 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); + return FALSE; + } + } + init->buflen -= ret; + + if (init->buflen == 0) { + tcp_chr_connect(init->chr); + return FALSE; + } + + memmove(init->buf, init->buf + ret, init->buflen); + + return TRUE; } -static int tcp_chr_add_client(CharDriverState *chr, int fd) +static void tcp_chr_telnet_init(CharDriverState *chr) +{ + TCPCharDriver *s = chr->opaque; + TCPCharDriverTelnetInit *init = + g_new0(TCPCharDriverTelnetInit, 1); + size_t n = 0; + + init->chr = chr; + init->buflen = 12; + +#define IACSET(x, a, b, c) \ + do { \ + x[n++] = a; \ + x[n++] = b; \ + x[n++] = c; \ + } while (0) + + /* Prep the telnet negotion to put telnet in binary, + * no echo, single char mode */ + IACSET(init->buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */ + IACSET(init->buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */ + IACSET(init->buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */ + IACSET(init->buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */ + +#undef IACSET + + qio_channel_add_watch( + s->ioc, G_IO_OUT, + tcp_chr_telnet_init_io, + init, NULL); +} + + +static void tcp_chr_tls_handshake(Object *source, + Error *err, + gpointer user_data) +{ + CharDriverState *chr = user_data; + TCPCharDriver *s = chr->opaque; + + if (err) { + tcp_chr_disconnect(chr); + } else { + if (s->do_telnetopt) { + tcp_chr_telnet_init(chr); + } else { + tcp_chr_connect(chr); + } + } +} + + +static void tcp_chr_tls_init(CharDriverState *chr) +{ + TCPCharDriver *s = chr->opaque; + QIOChannelTLS *tioc; + Error *err = NULL; + + if (s->is_listen) { + tioc = qio_channel_tls_new_server( + s->ioc, s->tls_creds, + NULL, /* XXX Use an ACL */ + &err); + } else { + tioc = qio_channel_tls_new_client( + s->ioc, s->tls_creds, + s->addr->u.inet.data->host, + &err); + } + if (tioc == NULL) { + error_free(err); + tcp_chr_disconnect(chr); + } + object_unref(OBJECT(s->ioc)); + s->ioc = QIO_CHANNEL(tioc); + + qio_channel_tls_handshake(tioc, + tcp_chr_tls_handshake, + chr, + NULL); +} + + +static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc) { TCPCharDriver *s = chr->opaque; - if (s->fd != -1) + if (s->ioc != NULL) { return -1; + } - qemu_set_nonblock(fd); - if (s->do_nodelay) - socket_set_nodelay(fd); - s->fd = fd; - s->chan = io_channel_from_socket(fd); + s->ioc = QIO_CHANNEL(sioc); + object_ref(OBJECT(sioc)); + s->sioc = sioc; + object_ref(OBJECT(sioc)); + + qio_channel_set_blocking(s->ioc, false, NULL); + + if (s->do_nodelay) { + qio_channel_set_delay(s->ioc, false); + } if (s->listen_tag) { g_source_remove(s->listen_tag); s->listen_tag = 0; } - tcp_chr_connect(chr); + + if (s->tls_creds) { + tcp_chr_tls_init(chr); + } else { + if (s->do_telnetopt) { + tcp_chr_telnet_init(chr); + } else { + tcp_chr_connect(chr); + } + } return 0; } -static gboolean tcp_chr_accept(GIOChannel *channel, GIOCondition cond, void *opaque) + +static int tcp_chr_add_client(CharDriverState *chr, int fd) +{ + int ret; + QIOChannelSocket *sioc; + + sioc = qio_channel_socket_new_fd(fd, NULL); + if (!sioc) { + return -1; + } + ret = tcp_chr_new_client(chr, sioc); + object_unref(OBJECT(sioc)); + return ret; +} + +static gboolean tcp_chr_accept(QIOChannel *channel, + GIOCondition cond, + void *opaque) { CharDriverState *chr = opaque; - TCPCharDriver *s = chr->opaque; - struct sockaddr_in saddr; -#ifndef _WIN32 - struct sockaddr_un uaddr; -#endif - struct sockaddr *addr; - socklen_t len; - int fd; + QIOChannelSocket *sioc; - for(;;) { -#ifndef _WIN32 - if (s->is_unix) { - len = sizeof(uaddr); - addr = (struct sockaddr *)&uaddr; - } else -#endif - { - len = sizeof(saddr); - addr = (struct sockaddr *)&saddr; - } - fd = qemu_accept(s->listen_fd, addr, &len); - if (fd < 0 && errno != EINTR) { - s->listen_tag = 0; - return FALSE; - } else if (fd >= 0) { - if (s->do_telnetopt) - tcp_chr_telnet_init(fd); - break; - } + sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(channel), + NULL); + if (!sioc) { + return TRUE; } - if (tcp_chr_add_client(chr, fd) < 0) - close(fd); + + tcp_chr_new_client(chr, sioc); + + object_unref(OBJECT(sioc)); return TRUE; } @@ -3033,22 +3149,16 @@ static void tcp_chr_close(CharDriverState *chr) s->reconnect_timer = 0; } qapi_free_SocketAddress(s->addr); - if (s->fd >= 0) { - remove_fd_in_watch(chr); - if (s->chan) { - g_io_channel_unref(s->chan); - } - closesocket(s->fd); + remove_fd_in_watch(chr); + if (s->ioc) { + object_unref(OBJECT(s->ioc)); } - if (s->listen_fd >= 0) { - if (s->listen_tag) { - g_source_remove(s->listen_tag); - s->listen_tag = 0; - } - if (s->listen_chan) { - g_io_channel_unref(s->listen_chan); - } - closesocket(s->listen_fd); + if (s->listen_tag) { + g_source_remove(s->listen_tag); + s->listen_tag = 0; + } + if (s->listen_ioc) { + object_unref(OBJECT(s->listen_ioc)); } if (s->read_msgfds_num) { for (i = 0; i < s->read_msgfds_num; i++) { @@ -3056,6 +3166,9 @@ static void tcp_chr_close(CharDriverState *chr) } g_free(s->read_msgfds); } + if (s->tls_creds) { + object_unref(OBJECT(s->tls_creds)); + } if (s->write_msgfds_num) { g_free(s->write_msgfds); } @@ -3063,58 +3176,24 @@ static void tcp_chr_close(CharDriverState *chr) qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static void qemu_chr_finish_socket_connection(CharDriverState *chr, int fd) -{ - TCPCharDriver *s = chr->opaque; - - if (s->is_listen) { - s->listen_fd = fd; - s->listen_chan = io_channel_from_socket(s->listen_fd); - s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, - tcp_chr_accept, chr); - } else { - s->connected = 1; - s->fd = fd; - socket_set_nodelay(fd); - s->chan = io_channel_from_socket(s->fd); - tcp_chr_connect(chr); - } -} -static void qemu_chr_socket_connected(int fd, Error *err, void *opaque) +static void qemu_chr_socket_connected(Object *src, Error *err, void *opaque) { + QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(src); CharDriverState *chr = opaque; TCPCharDriver *s = chr->opaque; - if (fd < 0) { + if (err) { check_report_connect_error(chr, err); + object_unref(src); return; } s->connect_err_reported = false; - qemu_chr_finish_socket_connection(chr, fd); + tcp_chr_new_client(chr, sioc); + object_unref(OBJECT(sioc)); } -static bool qemu_chr_open_socket_fd(CharDriverState *chr, Error **errp) -{ - TCPCharDriver *s = chr->opaque; - int fd; - - if (s->is_listen) { - fd = socket_listen(s->addr, errp); - } else if (s->reconnect_time) { - fd = socket_connect(s->addr, errp, qemu_chr_socket_connected, chr); - return fd >= 0; - } else { - fd = socket_connect(s->addr, errp, NULL, NULL); - } - if (fd < 0) { - return false; - } - - qemu_chr_finish_socket_connection(chr, fd); - return true; -} /*********************************************************/ /* Ring buffer chardev */ @@ -3176,13 +3255,20 @@ static void ringbuf_chr_close(struct CharDriverState *chr) chr->opaque = NULL; } -static CharDriverState *qemu_chr_open_ringbuf(ChardevRingbuf *opts, +static CharDriverState *qemu_chr_open_ringbuf(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, Error **errp) { + ChardevRingbuf *opts = backend->u.ringbuf.data; + ChardevCommon *common = qapi_ChardevRingbuf_base(opts); CharDriverState *chr; RingBufCharDriver *d; - chr = qemu_chr_alloc(); + chr = qemu_chr_alloc(common, errp); + if (!chr) { + return NULL; + } d = g_malloc(sizeof(*d)); d->size = opts->has_size ? opts->size : 65536; @@ -3205,7 +3291,7 @@ static CharDriverState *qemu_chr_open_ringbuf(ChardevRingbuf *opts, fail: g_free(d); - g_free(chr); + qemu_chr_free_common(chr); return NULL; } @@ -3235,7 +3321,12 @@ void qmp_ringbuf_write(const char *device, const char *data, } if (has_format && (format == DATA_FORMAT_BASE64)) { - write_data = g_base64_decode(data, &write_count); + write_data = qbase64_decode(data, -1, + &write_count, + errp); + if (!write_data) { + return; + } } else { write_data = (uint8_t *)data; write_count = strlen(data); @@ -3444,77 +3535,109 @@ fail: return NULL; } +void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend) +{ + const char *logfile = qemu_opt_get(opts, "logfile"); + + backend->has_logfile = logfile != NULL; + backend->logfile = logfile ? g_strdup(logfile) : NULL; + + backend->has_logappend = true; + backend->logappend = qemu_opt_get_bool(opts, "logappend", false); +} + + static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend, Error **errp) { const char *path = qemu_opt_get(opts, "path"); + ChardevFile *file; if (path == NULL) { error_setg(errp, "chardev: file: no filename given"); return; } - backend->file = g_new0(ChardevFile, 1); - backend->file->out = g_strdup(path); + file = backend->u.file.data = g_new0(ChardevFile, 1); + qemu_chr_parse_common(opts, qapi_ChardevFile_base(file)); + file->out = g_strdup(path); + + file->has_append = true; + file->append = qemu_opt_get_bool(opts, "append", false); } static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend, Error **errp) { - backend->stdio = g_new0(ChardevStdio, 1); - backend->stdio->has_signal = true; - backend->stdio->signal = qemu_opt_get_bool(opts, "signal", true); + ChardevStdio *stdio; + + stdio = backend->u.stdio.data = g_new0(ChardevStdio, 1); + qemu_chr_parse_common(opts, qapi_ChardevStdio_base(stdio)); + stdio->has_signal = true; + stdio->signal = qemu_opt_get_bool(opts, "signal", true); } +#ifdef HAVE_CHARDEV_SERIAL static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend, Error **errp) { const char *device = qemu_opt_get(opts, "path"); + ChardevHostdev *serial; if (device == NULL) { error_setg(errp, "chardev: serial/tty: no device path given"); return; } - backend->serial = g_new0(ChardevHostdev, 1); - backend->serial->device = g_strdup(device); + serial = backend->u.serial.data = g_new0(ChardevHostdev, 1); + qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(serial)); + serial->device = g_strdup(device); } +#endif +#ifdef HAVE_CHARDEV_PARPORT static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend, Error **errp) { const char *device = qemu_opt_get(opts, "path"); + ChardevHostdev *parallel; if (device == NULL) { error_setg(errp, "chardev: parallel: no device path given"); return; } - backend->parallel = g_new0(ChardevHostdev, 1); - backend->parallel->device = g_strdup(device); + parallel = backend->u.parallel.data = g_new0(ChardevHostdev, 1); + qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(parallel)); + parallel->device = g_strdup(device); } +#endif static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend, Error **errp) { const char *device = qemu_opt_get(opts, "path"); + ChardevHostdev *dev; if (device == NULL) { error_setg(errp, "chardev: pipe: no device path given"); return; } - backend->pipe = g_new0(ChardevHostdev, 1); - backend->pipe->device = g_strdup(device); + dev = backend->u.pipe.data = g_new0(ChardevHostdev, 1); + qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(dev)); + dev->device = g_strdup(device); } static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend, Error **errp) { int val; + ChardevRingbuf *ringbuf; - backend->ringbuf = g_new0(ChardevRingbuf, 1); + ringbuf = backend->u.ringbuf.data = g_new0(ChardevRingbuf, 1); + qemu_chr_parse_common(opts, qapi_ChardevRingbuf_base(ringbuf)); val = qemu_opt_get_size(opts, "size", 0); if (val != 0) { - backend->ringbuf->has_size = true; - backend->ringbuf->size = val; + ringbuf->has_size = true; + ringbuf->size = val; } } @@ -3522,13 +3645,15 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend, Error **errp) { const char *chardev = qemu_opt_get(opts, "chardev"); + ChardevMux *mux; if (chardev == NULL) { error_setg(errp, "chardev: mux: no chardev given"); return; } - backend->mux = g_new0(ChardevMux, 1); - backend->mux->chardev = g_strdup(chardev); + mux = backend->u.mux.data = g_new0(ChardevMux, 1); + qemu_chr_parse_common(opts, qapi_ChardevMux_base(mux)); + mux->chardev = g_strdup(chardev); } static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, @@ -3542,7 +3667,9 @@ 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 *tls_creds = qemu_opt_get(opts, "tls-creds"); SocketAddress *addr; + ChardevSocket *sock; if (!path) { if (!host) { @@ -3553,39 +3680,49 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, error_setg(errp, "chardev: socket: no port given"); return; } + } else { + if (tls_creds) { + error_setg(errp, "TLS can only be used over TCP socket"); + return; + } } - backend->socket = g_new0(ChardevSocket, 1); + sock = backend->u.socket.data = g_new0(ChardevSocket, 1); + qemu_chr_parse_common(opts, qapi_ChardevSocket_base(sock)); - backend->socket->has_nodelay = true; - backend->socket->nodelay = do_nodelay; - backend->socket->has_server = true; - backend->socket->server = is_listen; - backend->socket->has_telnet = true; - backend->socket->telnet = is_telnet; - backend->socket->has_wait = true; - backend->socket->wait = is_waitconnect; - backend->socket->has_reconnect = true; - backend->socket->reconnect = reconnect; + sock->has_nodelay = true; + sock->nodelay = do_nodelay; + sock->has_server = true; + sock->server = is_listen; + sock->has_telnet = true; + sock->telnet = is_telnet; + sock->has_wait = true; + sock->wait = is_waitconnect; + sock->has_reconnect = true; + sock->reconnect = reconnect; + sock->tls_creds = g_strdup(tls_creds); addr = g_new0(SocketAddress, 1); if (path) { - addr->kind = SOCKET_ADDRESS_KIND_UNIX; - addr->q_unix = g_new0(UnixSocketAddress, 1); - addr->q_unix->path = g_strdup(path); + UnixSocketAddress *q_unix; + addr->type = SOCKET_ADDRESS_KIND_UNIX; + q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1); + q_unix->path = g_strdup(path); } else { - addr->kind = SOCKET_ADDRESS_KIND_INET; - addr->inet = g_new0(InetSocketAddress, 1); - addr->inet->host = g_strdup(host); - addr->inet->port = g_strdup(port); - addr->inet->has_to = qemu_opt_get(opts, "to"); - addr->inet->to = qemu_opt_get_number(opts, "to", 0); - addr->inet->has_ipv4 = qemu_opt_get(opts, "ipv4"); - addr->inet->ipv4 = qemu_opt_get_bool(opts, "ipv4", 0); - addr->inet->has_ipv6 = qemu_opt_get(opts, "ipv6"); - addr->inet->ipv6 = qemu_opt_get_bool(opts, "ipv6", 0); - } - backend->socket->addr = addr; + addr->type = SOCKET_ADDRESS_KIND_INET; + addr->u.inet.data = g_new(InetSocketAddress, 1); + *addr->u.inet.data = (InetSocketAddress) { + .host = g_strdup(host), + .port = g_strdup(port), + .has_to = qemu_opt_get(opts, "to"), + .to = qemu_opt_get_number(opts, "to", 0), + .has_ipv4 = qemu_opt_get(opts, "ipv4"), + .ipv4 = qemu_opt_get_bool(opts, "ipv4", 0), + .has_ipv6 = qemu_opt_get(opts, "ipv6"), + .ipv6 = qemu_opt_get_bool(opts, "ipv6", 0), + }; + } + sock->addr = addr; } static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend, @@ -3597,6 +3734,7 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend, const char *localport = qemu_opt_get(opts, "localport"); bool has_local = false; SocketAddress *addr; + ChardevUdp *udp; if (host == NULL || strlen(host) == 0) { host = "localhost"; @@ -3616,27 +3754,32 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend, has_local = true; } - backend->udp = g_new0(ChardevUdp, 1); + udp = backend->u.udp.data = g_new0(ChardevUdp, 1); + qemu_chr_parse_common(opts, qapi_ChardevUdp_base(udp)); addr = g_new0(SocketAddress, 1); - addr->kind = SOCKET_ADDRESS_KIND_INET; - addr->inet = g_new0(InetSocketAddress, 1); - addr->inet->host = g_strdup(host); - addr->inet->port = g_strdup(port); - addr->inet->has_ipv4 = qemu_opt_get(opts, "ipv4"); - addr->inet->ipv4 = qemu_opt_get_bool(opts, "ipv4", 0); - addr->inet->has_ipv6 = qemu_opt_get(opts, "ipv6"); - addr->inet->ipv6 = qemu_opt_get_bool(opts, "ipv6", 0); - backend->udp->remote = addr; + addr->type = SOCKET_ADDRESS_KIND_INET; + addr->u.inet.data = g_new(InetSocketAddress, 1); + *addr->u.inet.data = (InetSocketAddress) { + .host = g_strdup(host), + .port = g_strdup(port), + .has_ipv4 = qemu_opt_get(opts, "ipv4"), + .ipv4 = qemu_opt_get_bool(opts, "ipv4", 0), + .has_ipv6 = qemu_opt_get(opts, "ipv6"), + .ipv6 = qemu_opt_get_bool(opts, "ipv6", 0), + }; + udp->remote = addr; if (has_local) { - backend->udp->has_local = true; + udp->has_local = true; addr = g_new0(SocketAddress, 1); - addr->kind = SOCKET_ADDRESS_KIND_INET; - addr->inet = g_new0(InetSocketAddress, 1); - addr->inet->host = g_strdup(localaddr); - addr->inet->port = g_strdup(localport); - backend->udp->local = addr; + addr->type = SOCKET_ADDRESS_KIND_INET; + addr->u.inet.data = g_new(InetSocketAddress, 1); + *addr->u.inet.data = (InetSocketAddress) { + .host = g_strdup(localaddr), + .port = g_strdup(localport), + }; + udp->local = addr; } } @@ -3644,12 +3787,16 @@ typedef struct CharDriver { const char *name; ChardevBackendKind kind; void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp); + CharDriverState *(*create)(const char *id, ChardevBackend *backend, + ChardevReturn *ret, Error **errp); } CharDriver; static GSList *backends; void register_char_driver(const char *name, ChardevBackendKind kind, - void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp)) + void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp), + CharDriverState *(*create)(const char *id, ChardevBackend *backend, + ChardevReturn *ret, Error **errp)) { CharDriver *s; @@ -3657,6 +3804,7 @@ void register_char_driver(const char *name, ChardevBackendKind kind, s->name = g_strdup(name); s->kind = kind; s->parse = parse; + s->create = create; backends = g_slist_append(backends, s); } @@ -3704,14 +3852,19 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, } chr = NULL; - backend->kind = cd->kind; + backend->type = cd->kind; if (cd->parse) { cd->parse(opts, backend, &local_err); if (local_err) { error_propagate(errp, local_err); goto qapi_out; } + } else { + ChardevCommon *cc = g_new0(ChardevCommon, 1); + qemu_chr_parse_common(opts, cc); + backend->u.null.data = cc; /* Any ChardevCommon member would work */ } + ret = qmp_chardev_add(bid ? bid : id, backend, errp); if (!ret) { goto qapi_out; @@ -3721,9 +3874,9 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, qapi_free_ChardevBackend(backend); qapi_free_ChardevReturn(ret); backend = g_new0(ChardevBackend, 1); - backend->mux = g_new0(ChardevMux, 1); - backend->kind = CHARDEV_BACKEND_KIND_MUX; - backend->mux->chardev = g_strdup(bid); + backend->u.mux.data = g_new0(ChardevMux, 1); + backend->type = CHARDEV_BACKEND_KIND_MUX; + backend->u.mux.data->chardev = g_strdup(bid); ret = qmp_chardev_add(id, backend, errp); if (!ret) { chr = qemu_chr_find(bid); @@ -3747,7 +3900,8 @@ err: return NULL; } -CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*init)(struct CharDriverState *s)) +CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename, + void (*init)(struct CharDriverState *s)) { const char *p; CharDriverState *chr; @@ -3773,6 +3927,21 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*in return chr; } +CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*init)(struct CharDriverState *s)) +{ + CharDriverState *chr; + chr = qemu_chr_new_noreplay(label, filename, init); + if (chr) { + chr->replay = replay_mode != REPLAY_MODE_NONE; + if (chr->replay && chr->chr_ioctl) { + fprintf(stderr, + "Replay: ioctl is not supported for serial devices yet\n"); + } + replay_register_char_driver(chr); + } + return chr; +} + void qemu_chr_fe_set_echo(struct CharDriverState *chr, bool echo) { if (chr->chr_set_echo) { @@ -3843,18 +4012,32 @@ void qemu_chr_fe_release(CharDriverState *s) s->avail_connections++; } -void qemu_chr_delete(CharDriverState *chr) +static void qemu_chr_free_common(CharDriverState *chr) { - QTAILQ_REMOVE(&chardevs, chr, next); - if (chr->chr_close) { - chr->chr_close(chr); - } g_free(chr->filename); g_free(chr->label); qemu_opts_del(chr->opts); + if (chr->logfd != -1) { + close(chr->logfd); + } + qemu_mutex_destroy(&chr->chr_write_lock); g_free(chr); } +void qemu_chr_free(CharDriverState *chr) +{ + if (chr->chr_close) { + chr->chr_close(chr); + } + qemu_chr_free_common(chr); +} + +void qemu_chr_delete(CharDriverState *chr) +{ + QTAILQ_REMOVE(&chardevs, chr, next); + qemu_chr_free(chr); +} + ChardevInfoList *qmp_query_chardev(Error **errp) { ChardevInfoList *chr_list = NULL; @@ -3968,6 +4151,9 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "telnet", .type = QEMU_OPT_BOOL, + },{ + .name = "tls-creds", + .type = QEMU_OPT_STRING, },{ .name = "width", .type = QEMU_OPT_NUMBER, @@ -3998,6 +4184,15 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "chardev", .type = QEMU_OPT_STRING, + },{ + .name = "append", + .type = QEMU_OPT_BOOL, + },{ + .name = "logfile", + .type = QEMU_OPT_STRING, + },{ + .name = "logappend", + .type = QEMU_OPT_BOOL, }, { /* end of list */ } }, @@ -4005,8 +4200,13 @@ QemuOptsList qemu_chardev_opts = { #ifdef _WIN32 -static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp) +static CharDriverState *qmp_chardev_open_file(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, + Error **errp) { + ChardevFile *file = backend->u.file.data; + ChardevCommon *common = qapi_ChardevFile_base(file); HANDLE out; if (file->has_in) { @@ -4020,20 +4220,17 @@ static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp) error_setg(errp, "open %s failed", file->out); return NULL; } - return qemu_chr_open_win_file(out); + return qemu_chr_open_win_file(out, common, errp); } -static CharDriverState *qmp_chardev_open_serial(ChardevHostdev *serial, +static CharDriverState *qmp_chardev_open_serial(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, Error **errp) { - return qemu_chr_open_win_path(serial->device); -} - -static CharDriverState *qmp_chardev_open_parallel(ChardevHostdev *parallel, - Error **errp) -{ - error_setg(errp, "character device backend type 'parallel' not supported"); - return NULL; + ChardevHostdev *serial = backend->u.serial.data; + ChardevCommon *common = qapi_ChardevHostdev_base(serial); + return qemu_chr_open_win_path(serial->device, common, errp); } #else /* WIN32 */ @@ -4050,11 +4247,22 @@ static int qmp_chardev_open_file_source(char *src, int flags, return fd; } -static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp) +static CharDriverState *qmp_chardev_open_file(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, + Error **errp) { + ChardevFile *file = backend->u.file.data; + ChardevCommon *common = qapi_ChardevFile_base(file); int flags, in = -1, out; - flags = O_WRONLY | O_TRUNC | O_CREAT | O_BINARY; + flags = O_WRONLY | O_CREAT | O_BINARY; + if (file->has_append && file->append) { + flags |= O_APPEND; + } else { + flags |= O_TRUNC; + } + out = qmp_chardev_open_file_source(file->out, flags, errp); if (out < 0) { return NULL; @@ -4069,13 +4277,17 @@ static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp) } } - return qemu_chr_open_fd(in, out); + return qemu_chr_open_fd(in, out, common, errp); } -static CharDriverState *qmp_chardev_open_serial(ChardevHostdev *serial, +#ifdef HAVE_CHARDEV_SERIAL +static CharDriverState *qmp_chardev_open_serial(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, Error **errp) { -#ifdef HAVE_CHARDEV_TTY + ChardevHostdev *serial = backend->u.serial.data; + ChardevCommon *common = qapi_ChardevHostdev_base(serial); int fd; fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp); @@ -4083,45 +4295,35 @@ static CharDriverState *qmp_chardev_open_serial(ChardevHostdev *serial, return NULL; } qemu_set_nonblock(fd); - return qemu_chr_open_tty_fd(fd); -#else - error_setg(errp, "character device backend type 'serial' not supported"); - return NULL; -#endif + return qemu_chr_open_tty_fd(fd, common, errp); } +#endif -static CharDriverState *qmp_chardev_open_parallel(ChardevHostdev *parallel, +#ifdef HAVE_CHARDEV_PARPORT +static CharDriverState *qmp_chardev_open_parallel(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, Error **errp) { -#ifdef HAVE_CHARDEV_PARPORT + ChardevHostdev *parallel = backend->u.parallel.data; + ChardevCommon *common = qapi_ChardevHostdev_base(parallel); int fd; fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp); if (fd < 0) { return NULL; } - return qemu_chr_open_pp_fd(fd); -#else - error_setg(errp, "character device backend type 'parallel' not supported"); - return NULL; -#endif + return qemu_chr_open_pp_fd(fd, common, errp); } +#endif #endif /* WIN32 */ -static void socket_try_connect(CharDriverState *chr) -{ - Error *err = NULL; - - if (!qemu_chr_open_socket_fd(chr, &err)) { - check_report_connect_error(chr, err); - } -} - static gboolean socket_reconnect_timeout(gpointer opaque) { CharDriverState *chr = opaque; TCPCharDriver *s = chr->opaque; + QIOChannelSocket *sioc; s->reconnect_timer = 0; @@ -4129,32 +4331,74 @@ static gboolean socket_reconnect_timeout(gpointer opaque) return false; } - socket_try_connect(chr); + sioc = qio_channel_socket_new(); + qio_channel_socket_connect_async(sioc, s->addr, + qemu_chr_socket_connected, + chr, NULL); return false; } -static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock, +static CharDriverState *qmp_chardev_open_socket(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, Error **errp) { CharDriverState *chr; TCPCharDriver *s; + ChardevSocket *sock = backend->u.socket.data; SocketAddress *addr = sock->addr; bool do_nodelay = sock->has_nodelay ? sock->nodelay : false; bool is_listen = sock->has_server ? sock->server : true; bool is_telnet = sock->has_telnet ? sock->telnet : false; bool is_waitconnect = sock->has_wait ? sock->wait : false; int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0; + ChardevCommon *common = qapi_ChardevSocket_base(sock); + QIOChannelSocket *sioc = NULL; - chr = qemu_chr_alloc(); - s = g_malloc0(sizeof(TCPCharDriver)); + chr = qemu_chr_alloc(common, errp); + if (!chr) { + return NULL; + } + s = g_new0(TCPCharDriver, 1); - s->fd = -1; - s->listen_fd = -1; - s->is_unix = addr->kind == SOCKET_ADDRESS_KIND_UNIX; + s->is_unix = addr->type == SOCKET_ADDRESS_KIND_UNIX; s->is_listen = is_listen; s->is_telnet = is_telnet; s->do_nodelay = do_nodelay; + if (sock->tls_creds) { + Object *creds; + creds = object_resolve_path_component( + object_get_objects_root(), sock->tls_creds); + if (!creds) { + error_setg(errp, "No TLS credentials with id '%s'", + sock->tls_creds); + goto error; + } + s->tls_creds = (QCryptoTLSCreds *) + object_dynamic_cast(creds, + TYPE_QCRYPTO_TLS_CREDS); + if (!s->tls_creds) { + error_setg(errp, "Object with id '%s' is not TLS credentials", + sock->tls_creds); + goto error; + } + object_ref(OBJECT(s->tls_creds)); + if (is_listen) { + if (s->tls_creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + error_setg(errp, "%s", + "Expected TLS credentials for server endpoint"); + goto error; + } + } else { + if (s->tls_creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { + error_setg(errp, "%s", + "Expected TLS credentials for client endpoint"); + goto error; + } + } + } + qapi_copy_SocketAddress(&s->addr, sock->addr); chr->opaque = s; @@ -4169,9 +4413,8 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock, /* be isn't opened until we get a connection */ chr->explicit_be_open = true; - chr->filename = g_malloc(CHR_MAX_FILENAME_SIZE); - SocketAddress_to_str(chr->filename, CHR_MAX_FILENAME_SIZE, "disconnected:", - addr, is_listen, is_telnet); + chr->filename = SocketAddress_to_str("disconnected:", + addr, is_listen, is_telnet); if (is_listen) { if (is_telnet) { @@ -4181,42 +4424,74 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock, s->reconnect_time = reconnect; } + sioc = qio_channel_socket_new(); if (s->reconnect_time) { - socket_try_connect(chr); - } else if (!qemu_chr_open_socket_fd(chr, errp)) { - g_free(s); - g_free(chr->filename); - g_free(chr); - return NULL; - } - - if (is_listen && is_waitconnect) { - fprintf(stderr, "QEMU waiting for connection on: %s\n", - chr->filename); - tcp_chr_accept(s->listen_chan, G_IO_IN, chr); - qemu_set_nonblock(s->listen_fd); + qio_channel_socket_connect_async(sioc, s->addr, + qemu_chr_socket_connected, + chr, NULL); + } else if (s->is_listen) { + if (qio_channel_socket_listen_sync(sioc, s->addr, errp) < 0) { + goto error; + } + s->listen_ioc = sioc; + if (is_waitconnect) { + fprintf(stderr, "QEMU waiting for connection on: %s\n", + chr->filename); + tcp_chr_accept(QIO_CHANNEL(s->listen_ioc), G_IO_IN, chr); + } + qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), false, NULL); + if (!s->ioc) { + s->listen_tag = qio_channel_add_watch( + QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL); + } + } else { + if (qio_channel_socket_connect_sync(sioc, s->addr, errp) < 0) { + goto error; + } + tcp_chr_new_client(chr, sioc); + object_unref(OBJECT(sioc)); } return chr; + + error: + if (sioc) { + object_unref(OBJECT(sioc)); + } + if (s->tls_creds) { + object_unref(OBJECT(s->tls_creds)); + } + g_free(s); + qemu_chr_free_common(chr); + return NULL; } -static CharDriverState *qmp_chardev_open_udp(ChardevUdp *udp, +static CharDriverState *qmp_chardev_open_udp(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, Error **errp) { - int fd; + ChardevUdp *udp = backend->u.udp.data; + ChardevCommon *common = qapi_ChardevUdp_base(udp); + QIOChannelSocket *sioc = qio_channel_socket_new(); - fd = socket_dgram(udp->remote, udp->local, errp); - if (fd < 0) { + if (qio_channel_socket_dgram_sync(sioc, + udp->local, udp->remote, + errp) < 0) { + object_unref(OBJECT(sioc)); return NULL; } - return qemu_chr_open_udp_fd(fd); + return qemu_chr_open_udp(sioc, common, errp); } ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, Error **errp) { ChardevReturn *ret = g_new0(ChardevReturn, 1); - CharDriverState *base, *chr = NULL; + CharDriverState *chr = NULL; + Error *local_err = NULL; + GSList *i; + CharDriver *cd; chr = qemu_chr_find(id); if (chr) { @@ -4225,106 +4500,40 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, return NULL; } - switch (backend->kind) { - case CHARDEV_BACKEND_KIND_FILE: - chr = qmp_chardev_open_file(backend->file, errp); - break; - case CHARDEV_BACKEND_KIND_SERIAL: - chr = qmp_chardev_open_serial(backend->serial, errp); - break; - case CHARDEV_BACKEND_KIND_PARALLEL: - chr = qmp_chardev_open_parallel(backend->parallel, errp); - break; - case CHARDEV_BACKEND_KIND_PIPE: - chr = qemu_chr_open_pipe(backend->pipe); - break; - case CHARDEV_BACKEND_KIND_SOCKET: - chr = qmp_chardev_open_socket(backend->socket, errp); - break; - case CHARDEV_BACKEND_KIND_UDP: - chr = qmp_chardev_open_udp(backend->udp, errp); - break; -#ifdef HAVE_CHARDEV_TTY - case CHARDEV_BACKEND_KIND_PTY: - chr = qemu_chr_open_pty(id, ret); - break; -#endif - case CHARDEV_BACKEND_KIND_NULL: - chr = qemu_chr_open_null(); - break; - case CHARDEV_BACKEND_KIND_MUX: - base = qemu_chr_find(backend->mux->chardev); - if (base == NULL) { - error_setg(errp, "mux: base chardev %s not found", - backend->mux->chardev); + for (i = backends; i; i = i->next) { + cd = i->data; + + if (cd->kind == backend->type) { + chr = cd->create(id, backend, ret, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out_error; + } break; } - chr = qemu_chr_open_mux(base); - break; - case CHARDEV_BACKEND_KIND_MSMOUSE: - chr = qemu_chr_open_msmouse(); - break; -#ifdef CONFIG_BRLAPI - case CHARDEV_BACKEND_KIND_BRAILLE: - chr = chr_baum_init(); - break; -#endif - case CHARDEV_BACKEND_KIND_TESTDEV: - chr = chr_testdev_init(); - break; - case CHARDEV_BACKEND_KIND_STDIO: - chr = qemu_chr_open_stdio(backend->stdio); - break; -#ifdef _WIN32 - case CHARDEV_BACKEND_KIND_CONSOLE: - chr = qemu_chr_open_win_con(); - break; -#endif -#ifdef CONFIG_SPICE - case CHARDEV_BACKEND_KIND_SPICEVMC: - chr = qemu_chr_open_spice_vmc(backend->spicevmc->type); - break; - case CHARDEV_BACKEND_KIND_SPICEPORT: - chr = qemu_chr_open_spice_port(backend->spiceport->fqdn); - break; -#endif - case CHARDEV_BACKEND_KIND_VC: - chr = vc_init(backend->vc); - break; - case CHARDEV_BACKEND_KIND_RINGBUF: - case CHARDEV_BACKEND_KIND_MEMORY: - chr = qemu_chr_open_ringbuf(backend->ringbuf, errp); - break; - default: - error_setg(errp, "unknown chardev backend (%d)", backend->kind); - break; } - /* - * Character backend open hasn't been fully converted to the Error - * API. Some opens fail without setting an error. Set a generic - * error then. - * TODO full conversion to Error API - */ - if (chr == NULL && errp && !*errp) { - error_setg(errp, "Failed to create chardev"); + if (chr == NULL) { + assert(!i); + error_setg(errp, "chardev backend not available"); + goto out_error; } - if (chr) { - chr->label = g_strdup(id); - chr->avail_connections = - (backend->kind == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1; - if (!chr->filename) { - chr->filename = g_strdup(ChardevBackendKind_lookup[backend->kind]); - } - if (!chr->explicit_be_open) { - qemu_chr_be_event(chr, CHR_EVENT_OPENED); - } - QTAILQ_INSERT_TAIL(&chardevs, chr, next); - return ret; - } else { - g_free(ret); - return NULL; + + chr->label = g_strdup(id); + chr->avail_connections = + (backend->type == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1; + if (!chr->filename) { + chr->filename = g_strdup(ChardevBackendKind_lookup[backend->type]); + } + if (!chr->explicit_be_open) { + qemu_chr_be_event(chr, CHR_EVENT_OPENED); } + QTAILQ_INSERT_TAIL(&chardevs, chr, next); + return ret; + +out_error: + g_free(ret); + return NULL; } void qmp_chardev_remove(const char *id, Error **errp) @@ -4341,37 +4550,55 @@ void qmp_chardev_remove(const char *id, Error **errp) error_setg(errp, "Chardev '%s' is busy", id); return; } + if (chr->replay) { + error_setg(errp, + "Chardev '%s' cannot be unplugged in record/replay mode", id); + return; + } qemu_chr_delete(chr); } static void register_types(void) { - register_char_driver("null", CHARDEV_BACKEND_KIND_NULL, NULL); + register_char_driver("null", CHARDEV_BACKEND_KIND_NULL, NULL, + qemu_chr_open_null); register_char_driver("socket", CHARDEV_BACKEND_KIND_SOCKET, - qemu_chr_parse_socket); - register_char_driver("udp", CHARDEV_BACKEND_KIND_UDP, qemu_chr_parse_udp); + qemu_chr_parse_socket, qmp_chardev_open_socket); + register_char_driver("udp", CHARDEV_BACKEND_KIND_UDP, qemu_chr_parse_udp, + qmp_chardev_open_udp); register_char_driver("ringbuf", CHARDEV_BACKEND_KIND_RINGBUF, - qemu_chr_parse_ringbuf); + qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf); register_char_driver("file", CHARDEV_BACKEND_KIND_FILE, - qemu_chr_parse_file_out); + qemu_chr_parse_file_out, qmp_chardev_open_file); register_char_driver("stdio", CHARDEV_BACKEND_KIND_STDIO, - qemu_chr_parse_stdio); + qemu_chr_parse_stdio, qemu_chr_open_stdio); +#if defined HAVE_CHARDEV_SERIAL register_char_driver("serial", CHARDEV_BACKEND_KIND_SERIAL, - qemu_chr_parse_serial); + qemu_chr_parse_serial, qmp_chardev_open_serial); register_char_driver("tty", CHARDEV_BACKEND_KIND_SERIAL, - qemu_chr_parse_serial); + qemu_chr_parse_serial, qmp_chardev_open_serial); +#endif +#ifdef HAVE_CHARDEV_PARPORT register_char_driver("parallel", CHARDEV_BACKEND_KIND_PARALLEL, - qemu_chr_parse_parallel); + qemu_chr_parse_parallel, qmp_chardev_open_parallel); register_char_driver("parport", CHARDEV_BACKEND_KIND_PARALLEL, - qemu_chr_parse_parallel); - register_char_driver("pty", CHARDEV_BACKEND_KIND_PTY, NULL); - register_char_driver("console", CHARDEV_BACKEND_KIND_CONSOLE, NULL); + qemu_chr_parse_parallel, qmp_chardev_open_parallel); +#endif +#ifdef HAVE_CHARDEV_PTY + register_char_driver("pty", CHARDEV_BACKEND_KIND_PTY, NULL, + qemu_chr_open_pty); +#endif +#ifdef _WIN32 + register_char_driver("console", CHARDEV_BACKEND_KIND_CONSOLE, NULL, + qemu_chr_open_win_con); +#endif register_char_driver("pipe", CHARDEV_BACKEND_KIND_PIPE, - qemu_chr_parse_pipe); - register_char_driver("mux", CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux); + qemu_chr_parse_pipe, qemu_chr_open_pipe); + register_char_driver("mux", CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux, + qemu_chr_open_mux); /* Bug-compatibility: */ register_char_driver("memory", CHARDEV_BACKEND_KIND_MEMORY, - qemu_chr_parse_ringbuf); + qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf); /* this must be done after machine init, since we register FEs with muxes * as part of realize functions like serial_isa_realizefn when -nographic * is specified diff --git a/qemu/qemu-coroutine-io.c b/qemu/qemu-coroutine-io.c deleted file mode 100644 index 28dc7351a..000000000 --- a/qemu/qemu-coroutine-io.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Coroutine-aware I/O functions - * - * Copyright (C) 2009-2010 Nippon Telegraph and Telephone Corporation. - * Copyright (c) 2011, Red Hat, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu-common.h" -#include "qemu/sockets.h" -#include "block/coroutine.h" -#include "qemu/iov.h" -#include "qemu/main-loop.h" - -ssize_t coroutine_fn -qemu_co_sendv_recvv(int sockfd, struct iovec *iov, unsigned iov_cnt, - size_t offset, size_t bytes, bool do_send) -{ - size_t done = 0; - ssize_t ret; - int err; - while (done < bytes) { - ret = iov_send_recv(sockfd, iov, iov_cnt, - offset + done, bytes - done, do_send); - if (ret > 0) { - done += ret; - } else if (ret < 0) { - err = socket_error(); - if (err == EAGAIN || err == EWOULDBLOCK) { - qemu_coroutine_yield(); - } else if (done == 0) { - return -err; - } else { - break; - } - } else if (ret == 0 && !do_send) { - /* write (send) should never return 0. - * read (recv) returns 0 for end-of-file (-data). - * In both cases there's little point retrying, - * but we do for write anyway, just in case */ - break; - } - } - return done; -} - -ssize_t coroutine_fn -qemu_co_send_recv(int sockfd, void *buf, size_t bytes, bool do_send) -{ - struct iovec iov = { .iov_base = buf, .iov_len = bytes }; - return qemu_co_sendv_recvv(sockfd, &iov, 1, 0, bytes, do_send); -} - -typedef struct { - Coroutine *co; - int fd; -} FDYieldUntilData; - -static void fd_coroutine_enter(void *opaque) -{ - FDYieldUntilData *data = opaque; - qemu_set_fd_handler(data->fd, NULL, NULL, NULL); - qemu_coroutine_enter(data->co, NULL); -} - -void coroutine_fn yield_until_fd_readable(int fd) -{ - FDYieldUntilData data; - - assert(qemu_in_coroutine()); - data.co = qemu_coroutine_self(); - data.fd = fd; - qemu_set_fd_handler(fd, fd_coroutine_enter, NULL, &data); - qemu_coroutine_yield(); -} diff --git a/qemu/qemu-coroutine-lock.c b/qemu/qemu-coroutine-lock.c deleted file mode 100644 index 6b4903334..000000000 --- a/qemu/qemu-coroutine-lock.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * coroutine queues and locks - * - * Copyright (c) 2011 Kevin Wolf - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu-common.h" -#include "block/coroutine.h" -#include "block/coroutine_int.h" -#include "qemu/queue.h" -#include "trace.h" - -void qemu_co_queue_init(CoQueue *queue) -{ - QTAILQ_INIT(&queue->entries); -} - -void coroutine_fn qemu_co_queue_wait(CoQueue *queue) -{ - Coroutine *self = qemu_coroutine_self(); - QTAILQ_INSERT_TAIL(&queue->entries, self, co_queue_next); - qemu_coroutine_yield(); - assert(qemu_in_coroutine()); -} - -/** - * qemu_co_queue_run_restart: - * - * Enter each coroutine that was previously marked for restart by - * qemu_co_queue_next() or qemu_co_queue_restart_all(). This function is - * invoked by the core coroutine code when the current coroutine yields or - * terminates. - */ -void qemu_co_queue_run_restart(Coroutine *co) -{ - Coroutine *next; - - trace_qemu_co_queue_run_restart(co); - while ((next = QTAILQ_FIRST(&co->co_queue_wakeup))) { - QTAILQ_REMOVE(&co->co_queue_wakeup, next, co_queue_next); - qemu_coroutine_enter(next, NULL); - } -} - -static bool qemu_co_queue_do_restart(CoQueue *queue, bool single) -{ - Coroutine *self = qemu_coroutine_self(); - Coroutine *next; - - if (QTAILQ_EMPTY(&queue->entries)) { - return false; - } - - while ((next = QTAILQ_FIRST(&queue->entries)) != NULL) { - QTAILQ_REMOVE(&queue->entries, next, co_queue_next); - QTAILQ_INSERT_TAIL(&self->co_queue_wakeup, next, co_queue_next); - trace_qemu_co_queue_next(next); - if (single) { - break; - } - } - return true; -} - -bool coroutine_fn qemu_co_queue_next(CoQueue *queue) -{ - assert(qemu_in_coroutine()); - return qemu_co_queue_do_restart(queue, true); -} - -void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue) -{ - assert(qemu_in_coroutine()); - qemu_co_queue_do_restart(queue, false); -} - -bool qemu_co_enter_next(CoQueue *queue) -{ - Coroutine *next; - - next = QTAILQ_FIRST(&queue->entries); - if (!next) { - return false; - } - - QTAILQ_REMOVE(&queue->entries, next, co_queue_next); - qemu_coroutine_enter(next, NULL); - return true; -} - -bool qemu_co_queue_empty(CoQueue *queue) -{ - return QTAILQ_FIRST(&queue->entries) == NULL; -} - -void qemu_co_mutex_init(CoMutex *mutex) -{ - memset(mutex, 0, sizeof(*mutex)); - qemu_co_queue_init(&mutex->queue); -} - -void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex) -{ - Coroutine *self = qemu_coroutine_self(); - - trace_qemu_co_mutex_lock_entry(mutex, self); - - while (mutex->locked) { - qemu_co_queue_wait(&mutex->queue); - } - - mutex->locked = true; - - trace_qemu_co_mutex_lock_return(mutex, self); -} - -void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex) -{ - Coroutine *self = qemu_coroutine_self(); - - trace_qemu_co_mutex_unlock_entry(mutex, self); - - assert(mutex->locked == true); - assert(qemu_in_coroutine()); - - mutex->locked = false; - qemu_co_queue_next(&mutex->queue); - - trace_qemu_co_mutex_unlock_return(mutex, self); -} - -void qemu_co_rwlock_init(CoRwlock *lock) -{ - memset(lock, 0, sizeof(*lock)); - qemu_co_queue_init(&lock->queue); -} - -void qemu_co_rwlock_rdlock(CoRwlock *lock) -{ - while (lock->writer) { - qemu_co_queue_wait(&lock->queue); - } - lock->reader++; -} - -void qemu_co_rwlock_unlock(CoRwlock *lock) -{ - assert(qemu_in_coroutine()); - if (lock->writer) { - lock->writer = false; - qemu_co_queue_restart_all(&lock->queue); - } else { - lock->reader--; - assert(lock->reader >= 0); - /* Wakeup only one waiting writer */ - if (!lock->reader) { - qemu_co_queue_next(&lock->queue); - } - } -} - -void qemu_co_rwlock_wrlock(CoRwlock *lock) -{ - while (lock->writer || lock->reader) { - qemu_co_queue_wait(&lock->queue); - } - lock->writer = true; -} diff --git a/qemu/qemu-coroutine-sleep.c b/qemu/qemu-coroutine-sleep.c deleted file mode 100644 index 9abb7fdf3..000000000 --- a/qemu/qemu-coroutine-sleep.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * QEMU coroutine sleep - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Stefan Hajnoczi - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "block/coroutine.h" -#include "qemu/timer.h" -#include "block/aio.h" - -typedef struct CoSleepCB { - QEMUTimer *ts; - Coroutine *co; -} CoSleepCB; - -static void co_sleep_cb(void *opaque) -{ - CoSleepCB *sleep_cb = opaque; - - qemu_coroutine_enter(sleep_cb->co, NULL); -} - -void coroutine_fn co_aio_sleep_ns(AioContext *ctx, QEMUClockType type, - int64_t ns) -{ - CoSleepCB sleep_cb = { - .co = qemu_coroutine_self(), - }; - sleep_cb.ts = aio_timer_new(ctx, type, SCALE_NS, co_sleep_cb, &sleep_cb); - timer_mod(sleep_cb.ts, qemu_clock_get_ns(type) + ns); - qemu_coroutine_yield(); - timer_del(sleep_cb.ts); - timer_free(sleep_cb.ts); -} diff --git a/qemu/qemu-coroutine.c b/qemu/qemu-coroutine.c deleted file mode 100644 index c17a92b10..000000000 --- a/qemu/qemu-coroutine.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * QEMU coroutines - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Stefan Hajnoczi - * Kevin Wolf - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "trace.h" -#include "qemu-common.h" -#include "qemu/thread.h" -#include "qemu/atomic.h" -#include "block/coroutine.h" -#include "block/coroutine_int.h" - -enum { - POOL_BATCH_SIZE = 64, -}; - -/** Free list to speed up creation */ -static QSLIST_HEAD(, Coroutine) release_pool = QSLIST_HEAD_INITIALIZER(pool); -static unsigned int release_pool_size; -static __thread QSLIST_HEAD(, Coroutine) alloc_pool = QSLIST_HEAD_INITIALIZER(pool); -static __thread unsigned int alloc_pool_size; -static __thread Notifier coroutine_pool_cleanup_notifier; - -static void coroutine_pool_cleanup(Notifier *n, void *value) -{ - Coroutine *co; - Coroutine *tmp; - - QSLIST_FOREACH_SAFE(co, &alloc_pool, pool_next, tmp) { - QSLIST_REMOVE_HEAD(&alloc_pool, pool_next); - qemu_coroutine_delete(co); - } -} - -Coroutine *qemu_coroutine_create(CoroutineEntry *entry) -{ - Coroutine *co = NULL; - - if (CONFIG_COROUTINE_POOL) { - co = QSLIST_FIRST(&alloc_pool); - if (!co) { - if (release_pool_size > POOL_BATCH_SIZE) { - /* Slow path; a good place to register the destructor, too. */ - if (!coroutine_pool_cleanup_notifier.notify) { - coroutine_pool_cleanup_notifier.notify = coroutine_pool_cleanup; - qemu_thread_atexit_add(&coroutine_pool_cleanup_notifier); - } - - /* This is not exact; there could be a little skew between - * release_pool_size and the actual size of release_pool. But - * it is just a heuristic, it does not need to be perfect. - */ - alloc_pool_size = atomic_xchg(&release_pool_size, 0); - QSLIST_MOVE_ATOMIC(&alloc_pool, &release_pool); - co = QSLIST_FIRST(&alloc_pool); - } - } - if (co) { - QSLIST_REMOVE_HEAD(&alloc_pool, pool_next); - alloc_pool_size--; - } - } - - if (!co) { - co = qemu_coroutine_new(); - } - - co->entry = entry; - QTAILQ_INIT(&co->co_queue_wakeup); - return co; -} - -static void coroutine_delete(Coroutine *co) -{ - co->caller = NULL; - - if (CONFIG_COROUTINE_POOL) { - if (release_pool_size < POOL_BATCH_SIZE * 2) { - QSLIST_INSERT_HEAD_ATOMIC(&release_pool, co, pool_next); - atomic_inc(&release_pool_size); - return; - } - if (alloc_pool_size < POOL_BATCH_SIZE) { - QSLIST_INSERT_HEAD(&alloc_pool, co, pool_next); - alloc_pool_size++; - return; - } - } - - qemu_coroutine_delete(co); -} - -void qemu_coroutine_enter(Coroutine *co, void *opaque) -{ - Coroutine *self = qemu_coroutine_self(); - CoroutineAction ret; - - trace_qemu_coroutine_enter(self, co, opaque); - - if (co->caller) { - fprintf(stderr, "Co-routine re-entered recursively\n"); - abort(); - } - - co->caller = self; - co->entry_arg = opaque; - ret = qemu_coroutine_switch(self, co, COROUTINE_ENTER); - - qemu_co_queue_run_restart(co); - - switch (ret) { - case COROUTINE_YIELD: - return; - case COROUTINE_TERMINATE: - trace_qemu_coroutine_terminate(co); - coroutine_delete(co); - return; - default: - abort(); - } -} - -void coroutine_fn qemu_coroutine_yield(void) -{ - Coroutine *self = qemu_coroutine_self(); - Coroutine *to = self->caller; - - trace_qemu_coroutine_yield(self, to); - - if (!to) { - fprintf(stderr, "Co-routine is yielding to no one\n"); - abort(); - } - - self->caller = NULL; - qemu_coroutine_switch(self, to, COROUTINE_YIELD); -} diff --git a/qemu/qemu-doc.texi b/qemu/qemu-doc.texi index 94af8c0f3..79141d358 100644 --- a/qemu/qemu-doc.texi +++ b/qemu/qemu-doc.texi @@ -158,7 +158,8 @@ TODO (no longer available) * pcsys_introduction:: Introduction * pcsys_quickstart:: Quick Start * sec_invocation:: Invocation -* pcsys_keys:: Keys +* pcsys_keys:: Keys in the graphical frontends +* mux_keys:: Keys in the character backend multiplexer * pcsys_monitor:: QEMU Monitor * disk_images:: Disk Images * pcsys_network:: Network emulation @@ -195,6 +196,8 @@ PCI and ISA network adapters @item Serial ports @item +IPMI BMC, either and internal or external one +@item Creative SoundBlaster 16 sound card @item ENSONIQ AudioPCI ES1370 sound card @@ -257,7 +260,7 @@ Linux should boot and give you a prompt. @example @c man begin SYNOPSIS -usage: qemu-system-i386 [options] [@var{disk_image}] +@command{qemu-system-i386} [@var{options}] [@var{disk_image}] @c man end @end example @@ -270,7 +273,7 @@ targets do not need a disk image. @c man end @node pcsys_keys -@section Keys +@section Keys in the graphical frontends @c man begin OPTIONS @@ -320,15 +323,23 @@ Toggle mouse and keyboard grab. In the virtual consoles, you can use @key{Ctrl-Up}, @key{Ctrl-Down}, @key{Ctrl-PageUp} and @key{Ctrl-PageDown} to move in the back log. -@kindex Ctrl-a h -During emulation, if you are using the @option{-nographic} option, use -@key{Ctrl-a h} to get terminal commands: +@c man end + +@node mux_keys +@section Keys in the character backend multiplexer + +@c man begin OPTIONS + +During emulation, if you are using a character backend multiplexer +(which is the default if you are using @option{-nographic}) then +several commands are available via an escape sequence. These +key sequences all start with an escape character, which is @key{Ctrl-a} +by default, but can be changed with @option{-echr}. The list below assumes +you're using the default. @table @key @item Ctrl-a h @kindex Ctrl-a h -@item Ctrl-a ? -@kindex Ctrl-a ? Print this help @item Ctrl-a x @kindex Ctrl-a x @@ -344,10 +355,11 @@ Toggle console timestamps Send break (magic sysrq in Linux) @item Ctrl-a c @kindex Ctrl-a c -Switch between console and monitor +Rotate between the frontends connected to the multiplexer (usually +this switches between the monitor and the console) @item Ctrl-a Ctrl-a -@kindex Ctrl-a a -Send Ctrl-a +@kindex Ctrl-a Ctrl-a +Send the escape character to the frontend @end table @c man end @@ -391,6 +403,8 @@ The following commands are available: @include qemu-monitor.texi +@include qemu-monitor-info.texi + @subsection Integer expressions The monitor understands integers expressions for every integer @@ -412,6 +426,7 @@ snapshots. * vm_snapshots:: VM snapshots * qemu_img_invocation:: qemu-img Invocation * qemu_nbd_invocation:: qemu-nbd Invocation +* qemu_ga_invocation:: qemu-ga Invocation * disk_images_formats:: Disk image file formats * host_drives:: Using host drives * disk_images_fat_images:: Virtual FAT disk images @@ -505,6 +520,11 @@ state is not saved or restored properly (in particular USB). @include qemu-nbd.texi +@node qemu_ga_invocation +@subsection @code{qemu-ga} Invocation + +@include qemu-ga.texi + @node disk_images_formats @subsection Disk image file formats @@ -1227,9 +1247,9 @@ echo 100 100 > /proc/sys/net/ipv4/ping_group_range When using the built-in TFTP server, the router is also the TFTP server. -When using the @option{-redir} option, TCP or UDP connections can be -redirected from the host to the guest. It allows for example to -redirect X11, telnet or SSH connections. +When using the @option{'-netdev user,hostfwd=...'} option, TCP or UDP +connections can be redirected from the host to the guest. It allows for +example to redirect X11, telnet or SSH connections. @subsection Connecting VLANs between QEMU instances @@ -1242,13 +1262,18 @@ basic example. @subsection Inter-VM Shared Memory device -With KVM enabled on a Linux host, a shared memory device is available. Guests -map a POSIX shared memory region into the guest as a PCI device that enables -zero-copy communication to the application level of the guests. The basic -syntax is: +On Linux hosts, a shared memory device is available. The basic syntax +is: @example -qemu-system-i386 -device ivshmem,size=[,shm=] +qemu-system-x86_64 -device ivshmem-plain,memdev=@var{hostmem} +@end example + +where @var{hostmem} names a host memory backend. For a POSIX shared +memory backend, use something like + +@example +-object memory-backend-file,size=1M,share,mem-path=/dev/shm/ivshmem,id=@var{hostmem} @end example If desired, interrupts can be sent between guest VMs accessing the same shared @@ -1258,28 +1283,41 @@ is qemu.git/contrib/ivshmem-server. An example syntax when using the shared memory server is: @example -qemu-system-i386 -device ivshmem,size=[,chardev=] - [,msi=on][,ioeventfd=on][,vectors=n][,role=peer|master] -qemu-system-i386 -chardev socket,path=,id= +# First start the ivshmem server once and for all +ivshmem-server -p @var{pidfile} -S @var{path} -m @var{shm-name} -l @var{shm-size} -n @var{vectors} + +# Then start your qemu instances with matching arguments +qemu-system-x86_64 -device ivshmem-doorbell,vectors=@var{vectors},chardev=@var{id} + -chardev socket,path=@var{path},id=@var{id} @end example When using the server, the guest will be assigned a VM ID (>=0) that allows guests using the same server to communicate via interrupts. Guests can read their -VM ID from a device register (see example code). Since receiving the shared -memory region from the server is asynchronous, there is a (small) chance the -guest may boot before the shared memory is attached. To allow an application -to ensure shared memory is attached, the VM ID register will return -1 (an -invalid VM ID) until the memory is attached. Once the shared memory is -attached, the VM ID will return the guest's valid VM ID. With these semantics, -the guest application can check to ensure the shared memory is attached to the -guest before proceeding. - -The @option{role} argument can be set to either master or peer and will affect -how the shared memory is migrated. With @option{role=master}, the guest will -copy the shared memory on migration to the destination host. With -@option{role=peer}, the guest will not be able to migrate with the device attached. -With the @option{peer} case, the device should be detached and then reattached -after migration using the PCI hotplug support. +VM ID from a device register (see ivshmem-spec.txt). + +@subsubsection Migration with ivshmem + +With device property @option{master=on}, the guest will copy the shared +memory on migration to the destination host. With @option{master=off}, +the guest will not be able to migrate with the device attached. In the +latter case, the device should be detached and then reattached after +migration using the PCI hotplug support. + +At most one of the devices sharing the same memory can be master. The +master must complete migration before you plug back the other devices. + +@subsubsection ivshmem and hugepages + +Instead of specifying the using POSIX shm, you may specify +a memory backend that has hugepage support: + +@example +qemu-system-x86_64 -object memory-backend-file,size=1G,mem-path=/dev/hugepages/my-shmem-file,share,id=mb1 + -device ivshmem-plain,memdev=mb1 +@end example + +ivshmem-server also supports hugepages mount points with the +@option{-m} memory path argument. @node direct_linux_boot @section Direct Linux Boot @@ -1379,7 +1417,7 @@ no type is given, the HCI logic corresponds to @code{-bt hci,vlan=0}. This USB device implements the USB Transport Layer of HCI. Example usage: @example -qemu-system-i386 [...OPTIONS...] -usbdevice bt:hci,vlan=3 -bt device:keyboard,vlan=3 +@command{qemu-system-i386} [...@var{OPTIONS}...] @option{-usbdevice} bt:hci,vlan=3 @option{-bt} device:keyboard,vlan=3 @end example @end table @@ -1766,7 +1804,7 @@ Use @code{set architecture i8086} to dump 16 bit code. Then use Advanced debugging options: -The default single stepping behavior is step with the IRQs and timer service routines off. It is set this way because when gdb executes a single step it expects to advance beyond the current instruction. With the IRQs and and timer service routines on, a single step might jump into the one of the interrupt or exception vectors instead of executing the current instruction. This means you may hit the same breakpoint a number of times before executing the instruction gdb wants to have executed. Because there are rare circumstances where you want to single step into an interrupt vector the behavior can be controlled from GDB. There are three commands you can query and set the single step behavior: +The default single stepping behavior is step with the IRQs and timer service routines off. It is set this way because when gdb executes a single step it expects to advance beyond the current instruction. With the IRQs and timer service routines on, a single step might jump into the one of the interrupt or exception vectors instead of executing the current instruction. This means you may hit the same breakpoint a number of times before executing the instruction gdb wants to have executed. Because there are rare circumstances where you want to single step into an interrupt vector the behavior can be controlled from GDB. There are three commands you can query and set the single step behavior: @table @code @item maintenance packet qqemu.sstepbits @@ -1862,7 +1900,8 @@ correctly instructs QEMU to shutdown at the appropriate moment. @subsubsection Share a directory between Unix and Windows -See @ref{sec_invocation} about the help of the option @option{-smb}. +See @ref{sec_invocation} about the help of the option +@option{'-netdev user,smb=...'}. @subsubsection Windows XP security problem @@ -2728,7 +2767,7 @@ qemu-i386 /usr/local/qemu-i386/wine/bin/wine \ @subsection Command line options @example -usage: qemu-i386 [-h] [-d] [-L path] [-s size] [-cpu model] [-g port] [-B offset] [-R size] program [arguments...] +@command{qemu-i386} [@option{-h]} [@option{-d]} [@option{-L} @var{path}] [@option{-s} @var{size}] [@option{-cpu} @var{model}] [@option{-g} @var{port}] [@option{-B} @var{offset}] [@option{-R} @var{size}] @var{program} [@var{arguments}...] @end example @table @option @@ -2870,7 +2909,7 @@ qemu-sparc64 /bin/ls @subsection Command line options @example -usage: qemu-sparc64 [-h] [-d] [-L path] [-s size] [-bsd type] program [arguments...] +@command{qemu-sparc64} [@option{-h]} [@option{-d]} [@option{-L} @var{path}] [@option{-s} @var{size}] [@option{-bsd} @var{type}] @var{program} [@var{arguments}...] @end example @table @option @@ -3021,9 +3060,62 @@ and all other qemu-system-@var{target}.exe compiled for Win32. @node Mac OS X @section Mac OS X -The Mac OS X patches are not fully merged in QEMU, so you should look -at the QEMU mailing list archive to have all the necessary -information. +System Requirements: +@itemize +@item Mac OS 10.5 or higher +@item The clang compiler shipped with Xcode 4.2 or higher, +or GCC 4.3 or higher +@end itemize + +Additional Requirements (install in order): +@enumerate +@item libffi: @uref{https://sourceware.org/libffi/} +@item gettext: @uref{http://www.gnu.org/software/gettext/} +@item glib: @uref{http://ftp.gnome.org/pub/GNOME/sources/glib/} +@item pkg-config: @uref{http://www.freedesktop.org/wiki/Software/pkg-config/} +@item autoconf: @uref{http://www.gnu.org/software/autoconf/autoconf.html} +@item automake: @uref{http://www.gnu.org/software/automake/} +@item pixman: @uref{http://www.pixman.org/} +@end enumerate + +* You may find it easiest to get these from a third-party packager +such as Homebrew, Macports, or Fink. + +After downloading the QEMU source code, double-click it to expand it. + +Then configure and make QEMU: +@example +./configure +make +@end example + +If you have a recent version of Mac OS X (OSX 10.7 or better +with Xcode 4.2 or better) we recommend building QEMU with the +default compiler provided by Apple, for your version of Mac OS X +(which will be 'clang'). The configure script will +automatically pick this. + +Note: If after the configure step you see a message like this: +@example +ERROR: Your compiler does not support the __thread specifier for + Thread-Local Storage (TLS). Please upgrade to a version that does. +@end example +you may have to build your own version of gcc from source. Expect that to take +several hours. More information can be found here: +@uref{https://gcc.gnu.org/install/} @* + +These are some of the third party binaries of gcc available for download: +@itemize +@item Homebrew: @uref{http://brew.sh/} +@item @uref{https://www.litebeam.net/gcc/gcc_472.pkg} +@item @uref{http://www.macports.org/ports.php?by=name&substr=gcc} +@end itemize + +You can have several versions of GCC on your system. To specify a certain version, +use the --cc and --cxx options. +@example +./configure --cxx= --cc= +@end example @node Make targets @section Make targets diff --git a/qemu/qemu-ga.texi b/qemu/qemu-ga.texi new file mode 100644 index 000000000..0e53bf6b2 --- /dev/null +++ b/qemu/qemu-ga.texi @@ -0,0 +1,137 @@ +@example +@c man begin SYNOPSIS +@command{qemu-ga} [@var{OPTIONS}] +@c man end +@end example + +@c man begin DESCRIPTION + +The QEMU Guest Agent is a daemon intended to be run within virtual +machines. It allows the hypervisor host to perform various operations +in the guest, such as: + +@itemize +@item +get information from the guest +@item +set the guest's system time +@item +read/write a file +@item +sync and freeze the filesystems +@item +suspend the guest +@item +reconfigure guest local processors +@item +set user's password +@item +... +@end itemize + +qemu-ga will read a system configuration file on startup (located at +q@file{/etc/qemu/qemu-ga.conf} by default), then parse remaining +configuration options on the command line. For the same key, the last +option wins, but the lists accumulate (see below for configuration +file format). + +@c man end + +@c man begin OPTIONS +@table @option +@item -m, --method=@var{method} + Transport method: one of @samp{unix-listen}, @samp{virtio-serial}, or + @samp{isa-serial} (@samp{virtio-serial} is the default). + +@item -p, --path=@var{path} + Device/socket path (the default for virtio-serial is + @samp{/dev/virtio-ports/org.qemu.guest_agent.0}, + the default for isa-serial is @samp{/dev/ttyS0}) + +@item -l, --logfile=@var{path} + Set log file path (default is stderr). + +@item -f, --pidfile=@var{path} + Specify pid file (default is @samp{/var/run/qemu-ga.pid}). + +@item -F, --fsfreeze-hook=@var{path} + Enable fsfreeze hook. Accepts an optional argument that specifies + script to run on freeze/thaw. Script will be called with + 'freeze'/'thaw' arguments accordingly (default is + @samp{/etc/qemu/fsfreeze-hook}). If using -F with an argument, do + not follow -F with a space (for example: + @samp{-F/var/run/fsfreezehook.sh}). + +@item -t, --statedir=@var{path} + Specify the directory to store state information (absolute paths only, + default is @samp{/var/run}). + +@item -v, --verbose + Log extra debugging information. + +@item -V, --version + Print version information and exit. + +@item -d, --daemon + Daemonize after startup (detach from terminal). + +@item -b, --blacklist=@var{list} + Comma-separated list of RPCs to disable (no spaces, @samp{?} to list + available RPCs). + +@item -D, --dump-conf + Dump the configuration in a format compatible with @file{qemu-ga.conf} + and exit. + +@item -h, --help + Display this help and exit. +@end table + +@c man end + +@c man begin FILES + +The syntax of the @file{qemu-ga.conf} configuration file follows the +Desktop Entry Specification, here is a quick summary: it consists of +groups of key-value pairs, interspersed with comments. + +@example +# qemu-ga configuration sample +[general] +daemonize = 0 +pidfile = /var/run/qemu-ga.pid +verbose = 0 +method = virtio-serial +path = /dev/virtio-ports/org.qemu.guest_agent.0 +statedir = /var/run +@end example + +The list of keys follows the command line options: +@table @option +@item daemon= boolean +@item method= string +@item path= string +@item logfile= string +@item pidfile= string +@item fsfreeze-hook= string +@item statedir= string +@item verbose= boolean +@item blacklist= string list +@end table + +@c man end + +@ignore + +@setfilename qemu-ga +@settitle QEMU Guest Agent + +@c man begin AUTHOR +Michael Roth +@c man end + +@c man begin SEEALSO +qemu(1) +@c man end + +@end ignore diff --git a/qemu/qemu-img-cmds.hx b/qemu/qemu-img-cmds.hx index 95677745f..e7cded6e2 100644 --- a/qemu/qemu-img-cmds.hx +++ b/qemu/qemu-img-cmds.hx @@ -10,68 +10,68 @@ STEXI ETEXI DEF("check", img_check, - "check [-q] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] filename") + "check [-q] [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] filename") STEXI -@item check [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] @var{filename} +@item check [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] @var{filename} ETEXI DEF("create", img_create, - "create [-q] [-f fmt] [-o options] filename [size]") + "create [-q] [--object objectdef] [--image-opts] [-f fmt] [-o options] filename [size]") STEXI -@item create [-q] [-f @var{fmt}] [-o @var{options}] @var{filename} [@var{size}] +@item create [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [-o @var{options}] @var{filename} [@var{size}] ETEXI DEF("commit", img_commit, - "commit [-q] [-f fmt] [-t cache] [-b base] [-d] [-p] filename") + "commit [-q] [--object objectdef] [--image-opts] [-f fmt] [-t cache] [-b base] [-d] [-p] filename") STEXI -@item commit [-q] [-f @var{fmt}] [-t @var{cache}] [-b @var{base}] [-d] [-p] @var{filename} +@item commit [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [-t @var{cache}] [-b @var{base}] [-d] [-p] @var{filename} ETEXI DEF("compare", img_compare, - "compare [-f fmt] [-F fmt] [-T src_cache] [-p] [-q] [-s] filename1 filename2") + "compare [--object objectdef] [--image-opts] [-f fmt] [-F fmt] [-T src_cache] [-p] [-q] [-s] filename1 filename2") STEXI -@item compare [-f @var{fmt}] [-F @var{fmt}] [-T @var{src_cache}] [-p] [-q] [-s] @var{filename1} @var{filename2} +@item compare [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [-F @var{fmt}] [-T @var{src_cache}] [-p] [-q] [-s] @var{filename1} @var{filename2} ETEXI DEF("convert", img_convert, - "convert [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] filename [filename2 [...]] output_filename") + "convert [--object objectdef] [--image-opts] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] filename [filename2 [...]] output_filename") STEXI -@item convert [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [--object @var{objectdef}] [--image-opts] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename} ETEXI DEF("info", img_info, - "info [-f fmt] [--output=ofmt] [--backing-chain] filename") + "info [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [--backing-chain] filename") STEXI -@item info [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] @var{filename} +@item info [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] @var{filename} ETEXI DEF("map", img_map, - "map [-f fmt] [--output=ofmt] filename") + "map [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] filename") STEXI -@item map [-f @var{fmt}] [--output=@var{ofmt}] @var{filename} +@item map [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] @var{filename} ETEXI DEF("snapshot", img_snapshot, - "snapshot [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename") + "snapshot [--object objectdef] [--image-opts] [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename") STEXI -@item snapshot [-q] [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename} +@item snapshot [--object @var{objectdef}] [--image-opts] [-q] [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename} ETEXI DEF("rebase", img_rebase, - "rebase [-q] [-f fmt] [-t cache] [-T src_cache] [-p] [-u] -b backing_file [-F backing_fmt] filename") + "rebase [--object objectdef] [--image-opts] [-q] [-f fmt] [-t cache] [-T src_cache] [-p] [-u] -b backing_file [-F backing_fmt] filename") STEXI -@item rebase [-q] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} +@item rebase [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} ETEXI DEF("resize", img_resize, - "resize [-q] filename [+ | -]size") + "resize [--object objectdef] [--image-opts] [-q] filename [+ | -]size") STEXI -@item resize [-q] @var{filename} [+ | -]@var{size} +@item resize [--object @var{objectdef}] [--image-opts] [-q] @var{filename} [+ | -]@var{size} ETEXI DEF("amend", img_amend, - "amend [-p] [-q] [-f fmt] [-t cache] -o options filename") + "amend [--object objectdef] [--image-opts] [-p] [-q] [-f fmt] [-t cache] -o options filename") STEXI -@item amend [-p] [-q] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename} +@item amend [--object @var{objectdef}] [--image-opts] [-p] [-q] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename} @end table ETEXI diff --git a/qemu/qemu-img.c b/qemu/qemu-img.c index 75f4ee442..46f2a6def 100644 --- a/qemu/qemu-img.c +++ b/qemu/qemu-img.c @@ -21,19 +21,23 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qapi-visit.h" #include "qapi/qmp-output-visitor.h" #include "qapi/qmp/qerror.h" #include "qapi/qmp/qjson.h" -#include "qemu-common.h" +#include "qemu/cutils.h" +#include "qemu/config-file.h" #include "qemu/option.h" #include "qemu/error-report.h" -#include "qemu/osdep.h" +#include "qom/object_interfaces.h" #include "sysemu/sysemu.h" #include "sysemu/block-backend.h" #include "block/block_int.h" #include "block/blockjob.h" #include "block/qapi.h" +#include "crypto/init.h" #include #define QEMU_IMG_VERSION "qemu-img version " QEMU_VERSION QEMU_PKGVERSION \ @@ -47,6 +51,8 @@ typedef struct img_cmd_t { enum { OPTION_OUTPUT = 256, OPTION_BACKING_CHAIN = 257, + OPTION_OBJECT = 258, + OPTION_IMAGE_OPTS = 259, }; typedef enum OutputFormat { @@ -54,8 +60,7 @@ typedef enum OutputFormat { OFORMAT_HUMAN, } OutputFormat; -/* Default to cache=writeback as data integrity is not important for qemu-tcg. */ -#define BDRV_O_FLAGS BDRV_O_CACHE_WB +/* Default to cache=writeback as data integrity is not important for qemu-img */ #define BDRV_DEFAULT_CACHE "writeback" static void format_print(void *opaque, const char *name) @@ -94,6 +99,10 @@ static void QEMU_NORETURN help(void) "\n" "Command parameters:\n" " 'filename' is a disk image filename\n" + " 'objectdef' is a QEMU user creatable object definition. See the qemu(1)\n" + " manual page for a description of the object properties. The most common\n" + " object type is a 'secret', which is used to supply passwords and/or\n" + " encryption keys.\n" " 'fmt' is the disk image format. It is guessed automatically in most cases\n" " 'cache' is the cache mode used to write the output disk image, the valid\n" " options are: 'none', 'writeback' (default, except for convert), 'writethrough',\n" @@ -154,6 +163,24 @@ static void QEMU_NORETURN help(void) exit(EXIT_SUCCESS); } +static QemuOptsList qemu_object_opts = { + .name = "object", + .implied_opt_name = "qom-type", + .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head), + .desc = { + { } + }, +}; + +static QemuOptsList qemu_source_opts = { + .name = "source", + .implied_opt_name = "file", + .head = QTAILQ_HEAD_INITIALIZER(qemu_source_opts.head), + .desc = { + { } + }, +}; + static int GCC_FMT_ATTR(2, 3) qprintf(bool quiet, const char *fmt, ...) { int ret = 0; @@ -196,13 +223,57 @@ static int print_block_option_help(const char *filename, const char *fmt) return 0; } -static BlockBackend *img_open(const char *id, const char *filename, - const char *fmt, int flags, - bool require_io, bool quiet) + +static int img_open_password(BlockBackend *blk, const char *filename, + int flags, bool quiet) { - BlockBackend *blk; BlockDriverState *bs; char password[256]; + + bs = blk_bs(blk); + if (bdrv_is_encrypted(bs) && bdrv_key_required(bs) && + !(flags & BDRV_O_NO_IO)) { + qprintf(quiet, "Disk image '%s' is encrypted.\n", filename); + if (qemu_read_password(password, sizeof(password)) < 0) { + error_report("No password given"); + return -1; + } + if (bdrv_set_key(bs, password) < 0) { + error_report("invalid password"); + return -1; + } + } + return 0; +} + + +static BlockBackend *img_open_opts(const char *optstr, + QemuOpts *opts, int flags, bool writethrough, + bool quiet) +{ + QDict *options; + Error *local_err = NULL; + BlockBackend *blk; + options = qemu_opts_to_qdict(opts, NULL); + blk = blk_new_open(NULL, NULL, options, flags, &local_err); + if (!blk) { + error_reportf_err(local_err, "Could not open '%s': ", optstr); + return NULL; + } + blk_set_enable_write_cache(blk, !writethrough); + + if (img_open_password(blk, optstr, flags, quiet) < 0) { + blk_unref(blk); + return NULL; + } + return blk; +} + +static BlockBackend *img_open_file(const char *filename, + const char *fmt, int flags, + bool writethrough, bool quiet) +{ + BlockBackend *blk; Error *local_err = NULL; QDict *options = NULL; @@ -211,32 +282,46 @@ static BlockBackend *img_open(const char *id, const char *filename, qdict_put(options, "driver", qstring_from_str(fmt)); } - blk = blk_new_open(id, filename, NULL, options, flags, &local_err); + blk = blk_new_open(filename, NULL, options, flags, &local_err); if (!blk) { - error_report("Could not open '%s': %s", filename, - error_get_pretty(local_err)); - error_free(local_err); - goto fail; + error_reportf_err(local_err, "Could not open '%s': ", filename); + return NULL; } + blk_set_enable_write_cache(blk, !writethrough); - bs = blk_bs(blk); - if (bdrv_is_encrypted(bs) && require_io) { - qprintf(quiet, "Disk image '%s' is encrypted.\n", filename); - if (qemu_read_password(password, sizeof(password)) < 0) { - error_report("No password given"); - goto fail; - } - if (bdrv_set_key(bs, password) < 0) { - error_report("invalid password"); - goto fail; - } + if (img_open_password(blk, filename, flags, quiet) < 0) { + blk_unref(blk); + return NULL; } return blk; -fail: - blk_unref(blk); - return NULL; } + +static BlockBackend *img_open(bool image_opts, + const char *filename, + const char *fmt, int flags, bool writethrough, + bool quiet) +{ + BlockBackend *blk; + if (image_opts) { + QemuOpts *opts; + if (fmt) { + error_report("--image-opts and --format are mutually exclusive"); + return NULL; + } + opts = qemu_opts_parse_noisily(qemu_find_opts("source"), + filename, true); + if (!opts) { + return NULL; + } + blk = img_open_opts(filename, opts, flags, writethrough, quiet); + } else { + blk = img_open_file(filename, fmt, flags, writethrough, quiet); + } + return blk; +} + + static int add_old_style_options(const char *fmt, QemuOpts *opts, const char *base_filename, const char *base_fmt) @@ -277,7 +362,13 @@ static int img_create(int argc, char **argv) bool quiet = false; for(;;) { - c = getopt(argc, argv, "F:b:f:he6o:q"); + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"object", required_argument, 0, OPTION_OBJECT}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "F:b:f:he6o:q", + long_options, NULL); if (c == -1) { break; } @@ -319,6 +410,14 @@ static int img_create(int argc, char **argv) case 'q': quiet = true; break; + case OPTION_OBJECT: { + QemuOpts *opts; + opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!opts) { + goto fail; + } + } break; } } @@ -334,11 +433,18 @@ static int img_create(int argc, char **argv) } optind++; + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + goto fail; + } + /* Get image size, if specified */ if (optind < argc) { int64_t sval; char *end; - sval = strtosz_suffix(argv[optind++], &end, STRTOSZ_DEFSUFFIX_B); + sval = qemu_strtosz_suffix(argv[optind++], &end, + QEMU_STRTOSZ_DEFSUFFIX_B); if (sval < 0 || *end) { if (sval == -ERANGE) { error_report("Image size must be less than 8 EiB!"); @@ -357,10 +463,9 @@ static int img_create(int argc, char **argv) } bdrv_img_create(filename, fmt, base_filename, base_fmt, - options, img_size, BDRV_O_FLAGS, &local_err, quiet); + options, img_size, 0, &local_err, quiet); if (local_err) { - error_report("%s: %s", filename, error_get_pretty(local_err)); - error_free(local_err); + error_reportf_err(local_err, "%s: ", filename); goto fail; } @@ -378,8 +483,8 @@ static void dump_json_image_check(ImageCheck *check, bool quiet) QString *str; QmpOutputVisitor *ov = qmp_output_visitor_new(); QObject *obj; - visit_type_ImageCheck(qmp_output_get_visitor(ov), - &check, NULL, &local_err); + visit_type_ImageCheck(qmp_output_get_visitor(ov), NULL, &check, + &local_err); obj = qmp_output_get_qobject(ov); str = qobject_to_json_pretty(obj); assert(str != NULL); @@ -488,13 +593,16 @@ static int img_check(int argc, char **argv) BlockBackend *blk; BlockDriverState *bs; int fix = 0; - int flags = BDRV_O_FLAGS | BDRV_O_CHECK; + int flags = BDRV_O_CHECK; + bool writethrough; ImageCheck *check; bool quiet = false; + bool image_opts = false; fmt = NULL; output = NULL; cache = BDRV_DEFAULT_CACHE; + for(;;) { int option_index = 0; static const struct option long_options[] = { @@ -502,6 +610,8 @@ static int img_check(int argc, char **argv) {"format", required_argument, 0, 'f'}, {"repair", required_argument, 0, 'r'}, {"output", required_argument, 0, OPTION_OUTPUT}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "hf:r:T:q", @@ -538,6 +648,17 @@ static int img_check(int argc, char **argv) case 'q': quiet = true; break; + case OPTION_OBJECT: { + QemuOpts *opts; + opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!opts) { + return 1; + } + } break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; } } if (optind != argc - 1) { @@ -554,13 +675,19 @@ static int img_check(int argc, char **argv) return 1; } - ret = bdrv_parse_cache_flags(cache, &flags); + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + return 1; + } + + ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); if (ret < 0) { error_report("Invalid source cache option: %s", cache); return 1; } - blk = img_open("image", filename, fmt, flags, true, quiet); + blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet); if (!blk) { return 1; } @@ -644,9 +771,6 @@ static void common_block_job_cb(void *opaque, int ret) if (ret < 0) { error_setg_errno(cbi->errp, -ret, "Block job failed"); } - - /* Drop this block job's reference */ - bdrv_unref(cbi->bs); } static void run_block_job(BlockJob *job, Error **errp) @@ -655,7 +779,8 @@ static void run_block_job(BlockJob *job, Error **errp) do { aio_poll(aio_context, true); - qemu_progress_print((float)job->offset / job->len * 100.f, 0); + qemu_progress_print(job->len ? + ((float)job->offset / job->len * 100.f) : 0.0f, 0); } while (!job->ready); block_job_complete_sync(job, errp); @@ -672,14 +797,23 @@ static int img_commit(int argc, char **argv) BlockBackend *blk; BlockDriverState *bs, *base_bs; bool progress = false, quiet = false, drop = false; + bool writethrough; Error *local_err = NULL; CommonBlockJobCBInfo cbi; + bool image_opts = false; fmt = NULL; cache = BDRV_DEFAULT_CACHE; base = NULL; for(;;) { - c = getopt(argc, argv, "f:ht:b:dpq"); + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "f:ht:b:dpq", + long_options, NULL); if (c == -1) { break; } @@ -708,6 +842,17 @@ static int img_commit(int argc, char **argv) case 'q': quiet = true; break; + case OPTION_OBJECT: { + QemuOpts *opts; + opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!opts) { + return 1; + } + } break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; } } @@ -721,14 +866,20 @@ static int img_commit(int argc, char **argv) } filename = argv[optind++]; + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + return 1; + } + flags = BDRV_O_RDWR | BDRV_O_UNMAP; - ret = bdrv_parse_cache_flags(cache, &flags); + ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); if (ret < 0) { error_report("Invalid cache option: %s", cache); return 1; } - blk = img_open("image", filename, fmt, flags, true, quiet); + blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet); if (!blk) { return 1; } @@ -747,7 +898,7 @@ static int img_commit(int argc, char **argv) /* This is different from QMP, which by default uses the deepest file in * the backing chain (i.e., the very base); however, the traditional * behavior of qemu-img commit is using the immediate backing file. */ - base_bs = bs->backing_hd; + base_bs = backing_bs(bs); if (!base_bs) { error_setg(&local_err, "Image does not have a backing file"); goto done; @@ -765,12 +916,12 @@ static int img_commit(int argc, char **argv) goto done; } - /* The block job will swap base_bs and bs (which is not what we really want - * here, but okay) and unref base_bs (after the swap, i.e., the old top - * image). In order to still be able to empty that top image afterwards, - * increment the reference counter here preemptively. */ + /* When the block job completes, the BlockBackend reference will point to + * the old backing file. In order to avoid that the top image is already + * deleted, so we can still empty it afterwards, increment the reference + * counter here preemptively. */ if (!drop) { - bdrv_ref(base_bs); + bdrv_ref(bs); } run_block_job(bs->job, &local_err); @@ -778,8 +929,8 @@ static int img_commit(int argc, char **argv) goto unref_backing; } - if (!drop && base_bs->drv->bdrv_make_empty) { - ret = base_bs->drv->bdrv_make_empty(base_bs); + if (!drop && bs->drv->bdrv_make_empty) { + ret = bs->drv->bdrv_make_empty(bs); if (ret) { error_setg_errno(&local_err, -ret, "Could not empty %s", filename); @@ -789,7 +940,7 @@ static int img_commit(int argc, char **argv) unref_backing: if (!drop) { - bdrv_unref(base_bs); + bdrv_unref(bs); } done: @@ -972,15 +1123,24 @@ static int img_compare(int argc, char **argv) int ret = 0; /* return value - 0 Ident, 1 Different, >1 Error */ bool progress = false, quiet = false, strict = false; int flags; + bool writethrough; int64_t total_sectors; int64_t sector_num = 0; int64_t nb_sectors; int c, pnum; uint64_t progress_base; + bool image_opts = false; cache = BDRV_DEFAULT_CACHE; for (;;) { - c = getopt(argc, argv, "hf:F:T:pqs"); + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "hf:F:T:pqs", + long_options, NULL); if (c == -1) { break; } @@ -1007,6 +1167,18 @@ static int img_compare(int argc, char **argv) case 's': strict = true; break; + case OPTION_OBJECT: { + QemuOpts *opts; + opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!opts) { + ret = 2; + goto out4; + } + } break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; } } @@ -1022,29 +1194,36 @@ static int img_compare(int argc, char **argv) filename1 = argv[optind++]; filename2 = argv[optind++]; + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + ret = 2; + goto out4; + } + /* Initialize before goto out */ qemu_progress_init(progress, 2.0); - flags = BDRV_O_FLAGS; - ret = bdrv_parse_cache_flags(cache, &flags); + flags = 0; + ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); if (ret < 0) { error_report("Invalid source cache option: %s", cache); ret = 2; goto out3; } - blk1 = img_open("image_1", filename1, fmt1, flags, true, quiet); + blk1 = img_open(image_opts, filename1, fmt1, flags, writethrough, quiet); if (!blk1) { ret = 2; goto out3; } - bs1 = blk_bs(blk1); - blk2 = img_open("image_2", filename2, fmt2, flags, true, quiet); + blk2 = img_open(image_opts, filename2, fmt2, flags, writethrough, quiet); if (!blk2) { ret = 2; goto out2; } + bs1 = blk_bs(blk1); bs2 = blk_bs(blk2); buf1 = blk_blockalign(blk1, IO_BUF_SIZE); @@ -1075,28 +1254,52 @@ static int img_compare(int argc, char **argv) } for (;;) { + int64_t status1, status2; + BlockDriverState *file; + nb_sectors = sectors_to_process(total_sectors, sector_num); if (nb_sectors <= 0) { break; } - allocated1 = bdrv_is_allocated_above(bs1, NULL, sector_num, nb_sectors, - &pnum1); - if (allocated1 < 0) { + status1 = bdrv_get_block_status_above(bs1, NULL, sector_num, + total_sectors1 - sector_num, + &pnum1, &file); + if (status1 < 0) { ret = 3; error_report("Sector allocation test failed for %s", filename1); goto out; } + allocated1 = status1 & BDRV_BLOCK_ALLOCATED; - allocated2 = bdrv_is_allocated_above(bs2, NULL, sector_num, nb_sectors, - &pnum2); - if (allocated2 < 0) { + status2 = bdrv_get_block_status_above(bs2, NULL, sector_num, + total_sectors2 - sector_num, + &pnum2, &file); + if (status2 < 0) { ret = 3; error_report("Sector allocation test failed for %s", filename2); goto out; } - nb_sectors = MIN(pnum1, pnum2); + allocated2 = status2 & BDRV_BLOCK_ALLOCATED; + if (pnum1) { + nb_sectors = MIN(nb_sectors, pnum1); + } + if (pnum2) { + nb_sectors = MIN(nb_sectors, pnum2); + } - if (allocated1 == allocated2) { + if (strict) { + if ((status1 & ~BDRV_BLOCK_OFFSET_MASK) != + (status2 & ~BDRV_BLOCK_OFFSET_MASK)) { + ret = 1; + qprintf(quiet, "Strict mode: Offset %" PRId64 + " block status mismatch!\n", + sectors_to_bytes(sector_num)); + goto out; + } + } + if ((status1 & BDRV_BLOCK_ZERO) && (status2 & BDRV_BLOCK_ZERO)) { + nb_sectors = MIN(pnum1, pnum2); + } else if (allocated1 == allocated2) { if (allocated1) { ret = blk_read(blk1, sector_num, buf1, nb_sectors); if (ret < 0) { @@ -1124,13 +1327,6 @@ static int img_compare(int argc, char **argv) } } } else { - if (strict) { - ret = 1; - qprintf(quiet, "Strict mode: Offset %" PRId64 - " allocation mismatch!\n", - sectors_to_bytes(sector_num)); - goto out; - } if (allocated1) { ret = check_empty_sectors(blk1, sector_num, nb_sectors, @@ -1212,6 +1408,7 @@ out2: blk_unref(blk1); out3: qemu_progress_end(); +out4: return ret; } @@ -1260,9 +1457,10 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) n = MIN(s->total_sectors - sector_num, BDRV_REQUEST_MAX_SECTORS); if (s->sector_next_status <= sector_num) { + BlockDriverState *file; ret = bdrv_get_block_status(blk_bs(s->src[s->src_cur]), sector_num - s->src_cur_offset, - n, &n); + n, &n, &file); if (ret < 0) { return ret; } @@ -1311,10 +1509,6 @@ static int convert_read(ImgConvertState *s, int64_t sector_num, int nb_sectors, int n; int ret; - if (s->status == BLK_ZERO || s->status == BLK_BACKING_FILE) { - return 0; - } - assert(nb_sectors <= s->buf_sectors); while (nb_sectors > 0) { BlockBackend *blk; @@ -1452,7 +1646,8 @@ static int convert_do_copy(ImgConvertState *s) ret = n; goto fail; } - if (s->status == BLK_DATA) { + if (s->status == BLK_DATA || (!s->min_sparse && s->status == BLK_ZERO)) + { s->allocated_sectors += n; } sector_num += n; @@ -1472,17 +1667,24 @@ static int convert_do_copy(ImgConvertState *s) ret = n; goto fail; } - if (s->status == BLK_DATA) { + if (s->status == BLK_DATA || (!s->min_sparse && s->status == BLK_ZERO)) + { allocated_done += n; qemu_progress_print(100.0 * allocated_done / s->allocated_sectors, 0); } - ret = convert_read(s, sector_num, n, buf); - if (ret < 0) { - error_report("error while reading sector %" PRId64 - ": %s", sector_num, strerror(-ret)); - goto fail; + if (s->status == BLK_DATA) { + ret = convert_read(s, sector_num, n, buf); + if (ret < 0) { + error_report("error while reading sector %" PRId64 + ": %s", sector_num, strerror(-ret)); + goto fail; + } + } else if (!s->min_sparse && s->status == BLK_ZERO) { + n = MIN(n, s->buf_sectors); + memset(buf, 0, n * BDRV_SECTOR_SIZE); + s->status = BLK_DATA; } ret = convert_write(s, sector_num, n, buf); @@ -1514,6 +1716,7 @@ static int img_convert(int argc, char **argv) int c, bs_n, bs_i, compress, cluster_sectors, skip_create; int64_t ret = 0; int progress = 0, flags, src_flags; + bool writethrough, src_writethrough; const char *fmt, *out_fmt, *cache, *src_cache, *out_baseimg, *out_filename; BlockDriver *drv, *proto_drv; BlockBackend **blk = NULL, *out_blk = NULL; @@ -1532,6 +1735,7 @@ static int img_convert(int argc, char **argv) Error *local_err = NULL; QemuOpts *sn_opts = NULL; ImgConvertState state; + bool image_opts = false; fmt = NULL; out_fmt = "raw"; @@ -1541,7 +1745,14 @@ static int img_convert(int argc, char **argv) compress = 0; skip_create = 0; for(;;) { - c = getopt(argc, argv, "hf:O:B:ce6o:s:l:S:pt:T:qn"); + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "hf:O:B:ce6o:s:l:S:pt:T:qn", + long_options, NULL); if (c == -1) { break; } @@ -1607,7 +1818,7 @@ static int img_convert(int argc, char **argv) { int64_t sval; char *end; - sval = strtosz_suffix(optarg, &end, STRTOSZ_DEFSUFFIX_B); + sval = qemu_strtosz_suffix(optarg, &end, QEMU_STRTOSZ_DEFSUFFIX_B); if (sval < 0 || *end) { error_report("Invalid minimum zero buffer size for sparse output specified"); ret = -1; @@ -1632,16 +1843,31 @@ static int img_convert(int argc, char **argv) case 'n': skip_create = 1; break; + case OPTION_OBJECT: + opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!opts) { + goto fail_getopt; + } + break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; } } + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + goto fail_getopt; + } + /* Initialize before goto out */ if (quiet) { progress = 0; } qemu_progress_init(progress, 1.0); - bs_n = argc - optind - 1; out_filename = bs_n >= 1 ? argv[argc - 1] : NULL; @@ -1662,8 +1888,8 @@ static int img_convert(int argc, char **argv) goto out; } - src_flags = BDRV_O_FLAGS; - ret = bdrv_parse_cache_flags(src_cache, &src_flags); + src_flags = 0; + ret = bdrv_parse_cache_mode(src_cache, &src_flags, &src_writethrough); if (ret < 0) { error_report("Invalid source cache option: %s", src_cache); goto out; @@ -1677,11 +1903,8 @@ static int img_convert(int argc, char **argv) total_sectors = 0; for (bs_i = 0; bs_i < bs_n; bs_i++) { - char *id = bs_n > 1 ? g_strdup_printf("source_%d", bs_i) - : g_strdup("source"); - blk[bs_i] = img_open(id, argv[optind + bs_i], fmt, src_flags, - true, quiet); - g_free(id); + blk[bs_i] = img_open(image_opts, argv[optind + bs_i], + fmt, src_flags, src_writethrough, quiet); if (!blk[bs_i]) { ret = -1; goto out; @@ -1712,9 +1935,7 @@ static int img_convert(int argc, char **argv) bdrv_snapshot_load_tmp_by_id_or_name(bs[0], snapshot_name, &local_err); } if (local_err) { - error_report("Failed to load snapshot: %s", - error_get_pretty(local_err)); - error_free(local_err); + error_reportf_err(local_err, "Failed to load snapshot: "); ret = -1; goto out; } @@ -1810,21 +2031,25 @@ static int img_convert(int argc, char **argv) /* Create the new image */ ret = bdrv_create(drv, out_filename, opts, &local_err); if (ret < 0) { - error_report("%s: error while converting %s: %s", - out_filename, out_fmt, error_get_pretty(local_err)); - error_free(local_err); + error_reportf_err(local_err, "%s: error while converting %s: ", + out_filename, out_fmt); goto out; } } flags = min_sparse ? (BDRV_O_RDWR | BDRV_O_UNMAP) : BDRV_O_RDWR; - ret = bdrv_parse_cache_flags(cache, &flags); + ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); if (ret < 0) { error_report("Invalid cache option: %s", cache); goto out; } - out_blk = img_open("target", out_filename, out_fmt, flags, true, quiet); + /* XXX we should allow --image-opts to trigger use of + * img_open() here, but then we have trouble with + * the bdrv_create() call which takes different params. + * Not critical right now, so fix can wait... + */ + out_blk = img_open_file(out_filename, out_fmt, flags, writethrough, quiet); if (!out_blk) { ret = -1; goto out; @@ -1931,8 +2156,8 @@ static void dump_json_image_info_list(ImageInfoList *list) QString *str; QmpOutputVisitor *ov = qmp_output_visitor_new(); QObject *obj; - visit_type_ImageInfoList(qmp_output_get_visitor(ov), - &list, NULL, &local_err); + visit_type_ImageInfoList(qmp_output_get_visitor(ov), NULL, &list, + &local_err); obj = qmp_output_get_qobject(ov); str = qobject_to_json_pretty(obj); assert(str != NULL); @@ -1948,8 +2173,7 @@ static void dump_json_image_info(ImageInfo *info) QString *str; QmpOutputVisitor *ov = qmp_output_visitor_new(); QObject *obj; - visit_type_ImageInfo(qmp_output_get_visitor(ov), - &info, NULL, &local_err); + visit_type_ImageInfo(qmp_output_get_visitor(ov), NULL, &info, &local_err); obj = qmp_output_get_qobject(ov); str = qobject_to_json_pretty(obj); assert(str != NULL); @@ -1991,7 +2215,8 @@ static gboolean str_equal_func(gconstpointer a, gconstpointer b) * image file. If there was an error a message will have been printed to * stderr. */ -static ImageInfoList *collect_image_info_list(const char *filename, +static ImageInfoList *collect_image_info_list(bool image_opts, + const char *filename, const char *fmt, bool chain) { @@ -2015,8 +2240,8 @@ static ImageInfoList *collect_image_info_list(const char *filename, } g_hash_table_insert(filenames, (gpointer)filename, NULL); - blk = img_open("image", filename, fmt, - BDRV_O_FLAGS | BDRV_O_NO_BACKING, false, false); + blk = img_open(image_opts, filename, fmt, + BDRV_O_NO_BACKING | BDRV_O_NO_IO, false, false); if (!blk) { goto err; } @@ -2041,7 +2266,10 @@ static ImageInfoList *collect_image_info_list(const char *filename, if (info->has_full_backing_filename) { filename = info->full_backing_filename; } else if (info->has_backing_filename) { - filename = info->backing_filename; + error_report("Could not determine absolute backing filename," + " but backing filename '%s' present", + info->backing_filename); + goto err; } if (info->has_backing_filename_format) { fmt = info->backing_filename_format; @@ -2064,6 +2292,7 @@ static int img_info(int argc, char **argv) bool chain = false; const char *filename, *fmt, *output; ImageInfoList *list; + bool image_opts = false; fmt = NULL; output = NULL; @@ -2074,6 +2303,8 @@ static int img_info(int argc, char **argv) {"format", required_argument, 0, 'f'}, {"output", required_argument, 0, OPTION_OUTPUT}, {"backing-chain", no_argument, 0, OPTION_BACKING_CHAIN}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "f:h", @@ -2095,6 +2326,17 @@ static int img_info(int argc, char **argv) case OPTION_BACKING_CHAIN: chain = true; break; + case OPTION_OBJECT: { + QemuOpts *opts; + opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!opts) { + return 1; + } + } break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; } } if (optind != argc - 1) { @@ -2111,7 +2353,13 @@ static int img_info(int argc, char **argv) return 1; } - list = collect_image_info_list(filename, fmt, chain); + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + return 1; + } + + list = collect_image_info_list(image_opts, filename, fmt, chain); if (!list) { return 1; } @@ -2133,47 +2381,37 @@ static int img_info(int argc, char **argv) return 0; } - -typedef struct MapEntry { - int flags; - int depth; - int64_t start; - int64_t length; - int64_t offset; - BlockDriverState *bs; -} MapEntry; - static void dump_map_entry(OutputFormat output_format, MapEntry *e, MapEntry *next) { switch (output_format) { case OFORMAT_HUMAN: - if ((e->flags & BDRV_BLOCK_DATA) && - !(e->flags & BDRV_BLOCK_OFFSET_VALID)) { + if (e->data && !e->has_offset) { error_report("File contains external, encrypted or compressed clusters."); exit(1); } - if ((e->flags & (BDRV_BLOCK_DATA|BDRV_BLOCK_ZERO)) == BDRV_BLOCK_DATA) { + if (e->data && !e->zero) { printf("%#-16"PRIx64"%#-16"PRIx64"%#-16"PRIx64"%s\n", - e->start, e->length, e->offset, e->bs->filename); + e->start, e->length, + e->has_offset ? e->offset : 0, + e->has_filename ? e->filename : ""); } /* This format ignores the distinction between 0, ZERO and ZERO|DATA. * Modify the flags here to allow more coalescing. */ - if (next && - (next->flags & (BDRV_BLOCK_DATA|BDRV_BLOCK_ZERO)) != BDRV_BLOCK_DATA) { - next->flags &= ~BDRV_BLOCK_DATA; - next->flags |= BDRV_BLOCK_ZERO; + if (next && (!next->data || next->zero)) { + next->data = false; + next->zero = true; } break; case OFORMAT_JSON: - printf("%s{ \"start\": %"PRId64", \"length\": %"PRId64", \"depth\": %d," - " \"zero\": %s, \"data\": %s", + printf("%s{ \"start\": %"PRId64", \"length\": %"PRId64"," + " \"depth\": %"PRId64", \"zero\": %s, \"data\": %s", (e->start == 0 ? "[" : ",\n"), e->start, e->length, e->depth, - (e->flags & BDRV_BLOCK_ZERO) ? "true" : "false", - (e->flags & BDRV_BLOCK_DATA) ? "true" : "false"); - if (e->flags & BDRV_BLOCK_OFFSET_VALID) { + e->zero ? "true" : "false", + e->data ? "true" : "false"); + if (e->has_offset) { printf(", \"offset\": %"PRId64"", e->offset); } putchar('}'); @@ -2190,6 +2428,8 @@ static int get_block_status(BlockDriverState *bs, int64_t sector_num, { int64_t ret; int depth; + BlockDriverState *file; + bool has_offset; /* As an optimization, we could cache the current range of unallocated * clusters in each file of the chain, and avoid querying the same @@ -2198,7 +2438,8 @@ static int get_block_status(BlockDriverState *bs, int64_t sector_num, depth = 0; for (;;) { - ret = bdrv_get_block_status(bs, sector_num, nb_sectors, &nb_sectors); + ret = bdrv_get_block_status(bs, sector_num, nb_sectors, &nb_sectors, + &file); if (ret < 0) { return ret; } @@ -2206,7 +2447,7 @@ static int get_block_status(BlockDriverState *bs, int64_t sector_num, if (ret & (BDRV_BLOCK_ZERO|BDRV_BLOCK_DATA)) { break; } - bs = bs->backing_hd; + bs = backing_bs(bs); if (bs == NULL) { ret = 0; break; @@ -2215,15 +2456,44 @@ static int get_block_status(BlockDriverState *bs, int64_t sector_num, depth++; } - e->start = sector_num * BDRV_SECTOR_SIZE; - e->length = nb_sectors * BDRV_SECTOR_SIZE; - e->flags = ret & ~BDRV_BLOCK_OFFSET_MASK; - e->offset = ret & BDRV_BLOCK_OFFSET_MASK; - e->depth = depth; - e->bs = bs; + has_offset = !!(ret & BDRV_BLOCK_OFFSET_VALID); + + *e = (MapEntry) { + .start = sector_num * BDRV_SECTOR_SIZE, + .length = nb_sectors * BDRV_SECTOR_SIZE, + .data = !!(ret & BDRV_BLOCK_DATA), + .zero = !!(ret & BDRV_BLOCK_ZERO), + .offset = ret & BDRV_BLOCK_OFFSET_MASK, + .has_offset = has_offset, + .depth = depth, + .has_filename = file && has_offset, + .filename = file && has_offset ? file->filename : NULL, + }; + return 0; } +static inline bool entry_mergeable(const MapEntry *curr, const MapEntry *next) +{ + if (curr->length == 0) { + return false; + } + if (curr->zero != next->zero || + curr->data != next->data || + curr->depth != next->depth || + curr->has_filename != next->has_filename || + curr->has_offset != next->has_offset) { + return false; + } + if (curr->has_filename && strcmp(curr->filename, next->filename)) { + return false; + } + if (curr->has_offset && curr->offset + curr->length != next->offset) { + return false; + } + return true; +} + static int img_map(int argc, char **argv) { int c; @@ -2234,6 +2504,7 @@ static int img_map(int argc, char **argv) int64_t length; MapEntry curr = { .length = 0 }, next; int ret = 0; + bool image_opts = false; fmt = NULL; output = NULL; @@ -2243,6 +2514,8 @@ static int img_map(int argc, char **argv) {"help", no_argument, 0, 'h'}, {"format", required_argument, 0, 'f'}, {"output", required_argument, 0, OPTION_OUTPUT}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "f:h", @@ -2261,6 +2534,17 @@ static int img_map(int argc, char **argv) case OPTION_OUTPUT: output = optarg; break; + case OPTION_OBJECT: { + QemuOpts *opts; + opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!opts) { + return 1; + } + } break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; } } if (optind != argc - 1) { @@ -2277,7 +2561,13 @@ static int img_map(int argc, char **argv) return 1; } - blk = img_open("image", filename, fmt, BDRV_O_FLAGS, true, false); + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + return 1; + } + + blk = img_open(image_opts, filename, fmt, 0, false, false); if (!blk) { return 1; } @@ -2305,10 +2595,7 @@ static int img_map(int argc, char **argv) goto out; } - if (curr.length != 0 && curr.flags == next.flags && - curr.depth == next.depth && - ((curr.flags & BDRV_BLOCK_OFFSET_VALID) == 0 || - curr.offset + curr.length == next.offset)) { + if (entry_mergeable(&curr, &next)) { curr.length += next.length; continue; } @@ -2342,11 +2629,19 @@ static int img_snapshot(int argc, char **argv) qemu_timeval tv; bool quiet = false; Error *err = NULL; + bool image_opts = false; - bdrv_oflags = BDRV_O_FLAGS | BDRV_O_RDWR; + bdrv_oflags = BDRV_O_RDWR; /* Parse commandline parameters */ for(;;) { - c = getopt(argc, argv, "la:c:d:hq"); + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "la:c:d:hq", + long_options, NULL); if (c == -1) { break; } @@ -2390,6 +2685,17 @@ static int img_snapshot(int argc, char **argv) case 'q': quiet = true; break; + case OPTION_OBJECT: { + QemuOpts *opts; + opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!opts) { + return 1; + } + } break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; } } @@ -2398,8 +2704,14 @@ static int img_snapshot(int argc, char **argv) } filename = argv[optind++]; + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + return 1; + } + /* Open the image */ - blk = img_open("image", filename, NULL, bdrv_oflags, true, quiet); + blk = img_open(image_opts, filename, NULL, bdrv_oflags, false, quiet); if (!blk) { return 1; } @@ -2437,9 +2749,8 @@ static int img_snapshot(int argc, char **argv) case SNAPSHOT_DELETE: bdrv_snapshot_delete_by_id_or_name(bs, snapshot_name, &err); if (err) { - error_report("Could not delete snapshot '%s': (%s)", - snapshot_name, error_get_pretty(err)); - error_free(err); + error_reportf_err(err, "Could not delete snapshot '%s': ", + snapshot_name); ret = 1; } break; @@ -2456,14 +2767,18 @@ static int img_snapshot(int argc, char **argv) static int img_rebase(int argc, char **argv) { BlockBackend *blk = NULL, *blk_old_backing = NULL, *blk_new_backing = NULL; + uint8_t *buf_old = NULL; + uint8_t *buf_new = NULL; BlockDriverState *bs = NULL; char *filename; const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg; int c, flags, src_flags, ret; + bool writethrough, src_writethrough; int unsafe = 0; int progress = 0; bool quiet = false; Error *local_err = NULL; + bool image_opts = false; /* Parse commandline parameters */ fmt = NULL; @@ -2472,7 +2787,14 @@ static int img_rebase(int argc, char **argv) out_baseimg = NULL; out_basefmt = NULL; for(;;) { - c = getopt(argc, argv, "hf:F:b:upt:T:q"); + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "hf:F:b:upt:T:q", + long_options, NULL); if (c == -1) { break; } @@ -2505,6 +2827,17 @@ static int img_rebase(int argc, char **argv) case 'q': quiet = true; break; + case OPTION_OBJECT: { + QemuOpts *opts; + opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!opts) { + return 1; + } + } break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; } } @@ -2520,30 +2853,40 @@ static int img_rebase(int argc, char **argv) } filename = argv[optind++]; + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + return 1; + } + qemu_progress_init(progress, 2.0); qemu_progress_print(0, 100); flags = BDRV_O_RDWR | (unsafe ? BDRV_O_NO_BACKING : 0); - ret = bdrv_parse_cache_flags(cache, &flags); + ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); if (ret < 0) { error_report("Invalid cache option: %s", cache); goto out; } - src_flags = BDRV_O_FLAGS; - ret = bdrv_parse_cache_flags(src_cache, &src_flags); + src_flags = 0; + ret = bdrv_parse_cache_mode(src_cache, &src_flags, &src_writethrough); if (ret < 0) { error_report("Invalid source cache option: %s", src_cache); goto out; } + /* The source files are opened read-only, don't care about WCE */ + assert((src_flags & BDRV_O_RDWR) == 0); + (void) src_writethrough; + /* * Open the images. * * Ignore the old backing file for unsafe rebase in case we want to correct * the reference to a renamed or moved backing file. */ - blk = img_open("image", filename, fmt, flags, true, quiet); + blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet); if (!blk) { ret = -1; goto out; @@ -2569,12 +2912,12 @@ static int img_rebase(int argc, char **argv) } bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); - blk_old_backing = blk_new_open("old_backing", backing_name, NULL, + blk_old_backing = blk_new_open(backing_name, NULL, options, src_flags, &local_err); if (!blk_old_backing) { - error_report("Could not open old backing file '%s': %s", - backing_name, error_get_pretty(local_err)); - error_free(local_err); + error_reportf_err(local_err, + "Could not open old backing file '%s': ", + backing_name); goto out; } @@ -2586,12 +2929,12 @@ static int img_rebase(int argc, char **argv) options = NULL; } - blk_new_backing = blk_new_open("new_backing", out_baseimg, NULL, + blk_new_backing = blk_new_open(out_baseimg, NULL, options, src_flags, &local_err); if (!blk_new_backing) { - error_report("Could not open new backing file '%s': %s", - out_baseimg, error_get_pretty(local_err)); - error_free(local_err); + error_reportf_err(local_err, + "Could not open new backing file '%s': ", + out_baseimg); goto out; } } @@ -2612,8 +2955,6 @@ static int img_rebase(int argc, char **argv) int64_t new_backing_num_sectors = 0; uint64_t sector; int n; - uint8_t * buf_old; - uint8_t * buf_new; float local_progress = 0; buf_old = blk_blockalign(blk, IO_BUF_SIZE); @@ -2725,9 +3066,6 @@ static int img_rebase(int argc, char **argv) } qemu_progress_print(local_progress, 100); } - - qemu_vfree(buf_old); - qemu_vfree(buf_new); } /* @@ -2763,6 +3101,8 @@ out: blk_unref(blk_old_backing); blk_unref(blk_new_backing); } + qemu_vfree(buf_old); + qemu_vfree(buf_new); blk_unref(blk); if (ret) { @@ -2780,6 +3120,7 @@ static int img_resize(int argc, char **argv) bool quiet = false; BlockBackend *blk = NULL; QemuOpts *param; + static QemuOptsList resize_options = { .name = "resize_options", .head = QTAILQ_HEAD_INITIALIZER(resize_options.head), @@ -2793,6 +3134,7 @@ static int img_resize(int argc, char **argv) } }, }; + bool image_opts = false; /* Remove size from argv manually so that negative numbers are not treated * as options by getopt. */ @@ -2806,7 +3148,14 @@ static int img_resize(int argc, char **argv) /* Parse getopt arguments */ fmt = NULL; for(;;) { - c = getopt(argc, argv, "f:hq"); + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "f:hq", + long_options, NULL); if (c == -1) { break; } @@ -2821,6 +3170,17 @@ static int img_resize(int argc, char **argv) case 'q': quiet = true; break; + case OPTION_OBJECT: { + QemuOpts *opts; + opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!opts) { + return 1; + } + } break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; } } if (optind != argc - 1) { @@ -2828,6 +3188,12 @@ static int img_resize(int argc, char **argv) } filename = argv[optind++]; + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + return 1; + } + /* Choose grow, shrink, or absolute resize mode */ switch (size[0]) { case '+': @@ -2855,8 +3221,8 @@ static int img_resize(int argc, char **argv) n = qemu_opt_get_size(param, BLOCK_OPT_SIZE, 0); qemu_opts_del(param); - blk = img_open("image", filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR, - true, quiet); + blk = img_open(image_opts, filename, fmt, + BDRV_O_RDWR, false, quiet); if (!blk) { ret = -1; goto out; @@ -2897,7 +3263,8 @@ out: } static void amend_status_cb(BlockDriverState *bs, - int64_t offset, int64_t total_work_size) + int64_t offset, int64_t total_work_size, + void *opaque) { qemu_progress_print(100.f * offset / total_work_size, 0); } @@ -2911,13 +3278,22 @@ static int img_amend(int argc, char **argv) QemuOpts *opts = NULL; const char *fmt = NULL, *filename, *cache; int flags; + bool writethrough; bool quiet = false, progress = false; BlockBackend *blk = NULL; BlockDriverState *bs = NULL; + bool image_opts = false; cache = BDRV_DEFAULT_CACHE; for (;;) { - c = getopt(argc, argv, "ho:f:t:pq"); + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "ho:f:t:pq", + long_options, NULL); if (c == -1) { break; } @@ -2931,7 +3307,7 @@ static int img_amend(int argc, char **argv) if (!is_valid_option_list(optarg)) { error_report("Invalid option list: %s", optarg); ret = -1; - goto out; + goto out_no_progress; } if (!options) { options = g_strdup(optarg); @@ -2953,6 +3329,17 @@ static int img_amend(int argc, char **argv) case 'q': quiet = true; break; + case OPTION_OBJECT: + opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!opts) { + ret = -1; + goto out_no_progress; + } + break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; } } @@ -2960,6 +3347,13 @@ static int img_amend(int argc, char **argv) error_exit("Must specify options (-o)"); } + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + ret = -1; + goto out_no_progress; + } + if (quiet) { progress = false; } @@ -2979,14 +3373,14 @@ static int img_amend(int argc, char **argv) goto out; } - flags = BDRV_O_FLAGS | BDRV_O_RDWR; - ret = bdrv_parse_cache_flags(cache, &flags); + flags = BDRV_O_RDWR; + ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); if (ret < 0) { error_report("Invalid cache option: %s", cache); goto out; } - blk = img_open("image", filename, fmt, flags, true, quiet); + blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet); if (!blk) { ret = -1; goto out; @@ -3021,7 +3415,7 @@ static int img_amend(int argc, char **argv) /* In case the driver does not call amend_status_cb() */ qemu_progress_print(0.f, 0); - ret = bdrv_amend_options(bs, opts, &amend_status_cb); + ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL); qemu_progress_print(100.f, 0); if (ret < 0) { error_report("Error while amending options: %s", strerror(-ret)); @@ -3031,6 +3425,7 @@ static int img_amend(int argc, char **argv) out: qemu_progress_end(); +out_no_progress: blk_unref(blk); qemu_opts_del(opts); qemu_opts_free(create_opts); @@ -3075,12 +3470,21 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } + if (qcrypto_init(&local_error) < 0) { + error_reportf_err(local_error, "cannot initialize crypto: "); + exit(1); + } + + module_call_init(MODULE_INIT_QOM); bdrv_init(); if (argc < 2) { error_exit("Not enough arguments"); } cmdname = argv[1]; + qemu_add_opts(&qemu_object_opts); + qemu_add_opts(&qemu_source_opts); + /* find the command */ for (cmd = img_cmds; cmd->name != NULL; cmd++) { if (!strcmp(cmdname, cmd->name)) { diff --git a/qemu/qemu-img.texi b/qemu/qemu-img.texi index 0a1ab3598..afaebdd40 100644 --- a/qemu/qemu-img.texi +++ b/qemu/qemu-img.texi @@ -1,6 +1,6 @@ @example @c man begin SYNOPSIS -usage: qemu-img command [command options] +@command{qemu-img} @var{command} [@var{command} @var{options}] @c man end @end example @@ -24,6 +24,20 @@ Command parameters: @table @var @item filename is a disk image filename + +@item --object @var{objectdef} + +is a QEMU user creatable object definition. See the @code{qemu(1)} manual +page for a description of the object properties. The most common object +type is a @code{secret}, which is used to supply passwords and/or encryption +keys. + +@item --image-opts + +Indicates that the @var{filename} parameter is to be interpreted as a +full option string, not a plain filename. This parameter is mutually +exclusive with the @var{-f} and @var{-F} parameters. + @item fmt is the disk image format. It is guessed automatically in most cases. See below for a description of the supported disk formats. @@ -103,7 +117,7 @@ First image format @item -F Second image format @item -s -Strict mode - fail on on different image size or sector allocation +Strict mode - fail on different image size or sector allocation @end table Parameters to convert subcommand: diff --git a/qemu/qemu-io-cmds.c b/qemu/qemu-io-cmds.c index 53477e1e1..e34f77711 100644 --- a/qemu/qemu-io-cmds.c +++ b/qemu/qemu-io-cmds.c @@ -8,6 +8,8 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-io.h" #include "sysemu/block-backend.h" #include "block/block.h" @@ -17,6 +19,7 @@ #include "qemu/main-loop.h" #include "qemu/timer.h" #include "sysemu/block-backend.h" +#include "qemu/cutils.h" #define CMD_NOFILE_OK 0x01 @@ -136,7 +139,29 @@ static char **breakline(char *input, int *count) static int64_t cvtnum(const char *s) { char *end; - return strtosz_suffix(s, &end, STRTOSZ_DEFSUFFIX_B); + int64_t ret; + + ret = qemu_strtosz_suffix(s, &end, QEMU_STRTOSZ_DEFSUFFIX_B); + if (*end != '\0') { + /* Detritus at the end of the string */ + return -EINVAL; + } + return ret; +} + +static void print_cvtnum_err(int64_t rc, const char *arg) +{ + switch (rc) { + case -EINVAL: + printf("Parsing error: non-numeric argument," + " or extraneous/unrecognized suffix -- %s\n", arg); + break; + case -ERANGE: + printf("Parsing error: argument too large -- %s\n", arg); + break; + default: + printf("Parsing error: %s\n", arg); + } } #define EXABYTES(x) ((long long)(x) << 60) @@ -294,9 +319,10 @@ static void qemu_io_free(void *p) qemu_vfree(p); } -static void dump_buffer(const void *buffer, int64_t offset, int len) +static void dump_buffer(const void *buffer, int64_t offset, int64_t len) { - int i, j; + uint64_t i; + int j; const uint8_t *p; for (i = 0, p = buffer; i < len; i += 16) { @@ -319,7 +345,7 @@ static void dump_buffer(const void *buffer, int64_t offset, int len) } static void print_report(const char *op, struct timeval *t, int64_t offset, - int count, int total, int cnt, int Cflag) + int64_t count, int64_t total, int cnt, int Cflag) { char s1[64], s2[64], ts[64]; @@ -327,12 +353,12 @@ static void print_report(const char *op, struct timeval *t, int64_t offset, if (!Cflag) { cvtstr((double)total, s1, sizeof(s1)); cvtstr(tdiv((double)total, *t), s2, sizeof(s2)); - printf("%s %d/%d bytes at offset %" PRId64 "\n", + printf("%s %"PRId64"/%"PRId64" bytes at offset %" PRId64 "\n", op, total, count, offset); printf("%s, %d ops; %s (%s/sec and %.4f ops/sec)\n", s1, cnt, ts, s2, tdiv((double)cnt, *t)); } else {/* bytes,ops,time,bytes/sec,ops/sec */ - printf("%d,%d,%s,%.3f,%.3f\n", + printf("%"PRId64",%d,%s,%.3f,%.3f\n", total, cnt, ts, tdiv((double)total, *t), tdiv((double)cnt, *t)); @@ -359,13 +385,13 @@ create_iovec(BlockBackend *blk, QEMUIOVector *qiov, char **argv, int nr_iov, len = cvtnum(arg); if (len < 0) { - printf("non-numeric length argument -- %s\n", arg); + print_cvtnum_err(len, arg); goto fail; } /* should be SIZE_T_MAX, but that doesn't exist */ if (len > INT_MAX) { - printf("too large length argument -- %s\n", arg); + printf("Argument '%s' exceeds maximum size %d\n", arg, INT_MAX); goto fail; } @@ -393,11 +419,15 @@ fail: return buf; } -static int do_read(BlockBackend *blk, char *buf, int64_t offset, int count, - int *total) +static int do_read(BlockBackend *blk, char *buf, int64_t offset, int64_t count, + int64_t *total) { int ret; + if (count >> 9 > INT_MAX) { + return -ERANGE; + } + ret = blk_read(blk, offset >> 9, (uint8_t *)buf, count >> 9); if (ret < 0) { return ret; @@ -406,11 +436,15 @@ static int do_read(BlockBackend *blk, char *buf, int64_t offset, int count, return 1; } -static int do_write(BlockBackend *blk, char *buf, int64_t offset, int count, - int *total) +static int do_write(BlockBackend *blk, char *buf, int64_t offset, int64_t count, + int64_t *total) { int ret; + if (count >> 9 > INT_MAX) { + return -ERANGE; + } + ret = blk_write(blk, offset >> 9, (uint8_t *)buf, count >> 9); if (ret < 0) { return ret; @@ -419,9 +453,13 @@ static int do_write(BlockBackend *blk, char *buf, int64_t offset, int count, return 1; } -static int do_pread(BlockBackend *blk, char *buf, int64_t offset, int count, - int *total) +static int do_pread(BlockBackend *blk, char *buf, int64_t offset, + int64_t count, int64_t *total) { + if (count > INT_MAX) { + return -ERANGE; + } + *total = blk_pread(blk, offset, (uint8_t *)buf, count); if (*total < 0) { return *total; @@ -429,9 +467,13 @@ static int do_pread(BlockBackend *blk, char *buf, int64_t offset, int count, return 1; } -static int do_pwrite(BlockBackend *blk, char *buf, int64_t offset, int count, - int *total) +static int do_pwrite(BlockBackend *blk, char *buf, int64_t offset, + int64_t count, int64_t *total) { + if (count > INT_MAX) { + return -ERANGE; + } + *total = blk_pwrite(blk, offset, (uint8_t *)buf, count); if (*total < 0) { return *total; @@ -442,8 +484,8 @@ static int do_pwrite(BlockBackend *blk, char *buf, int64_t offset, int count, typedef struct { BlockBackend *blk; int64_t offset; - int count; - int *total; + int64_t count; + int64_t *total; int ret; bool done; } CoWriteZeroes; @@ -463,8 +505,8 @@ static void coroutine_fn co_write_zeroes_entry(void *opaque) *data->total = data->count; } -static int do_co_write_zeroes(BlockBackend *blk, int64_t offset, int count, - int *total) +static int do_co_write_zeroes(BlockBackend *blk, int64_t offset, int64_t count, + int64_t *total) { Coroutine *co; CoWriteZeroes data = { @@ -475,6 +517,10 @@ static int do_co_write_zeroes(BlockBackend *blk, int64_t offset, int count, .done = false, }; + if (count >> BDRV_SECTOR_BITS > INT_MAX) { + return -ERANGE; + } + co = qemu_coroutine_create(co_write_zeroes_entry); qemu_coroutine_enter(co, &data); while (!data.done) { @@ -488,10 +534,14 @@ static int do_co_write_zeroes(BlockBackend *blk, int64_t offset, int count, } static int do_write_compressed(BlockBackend *blk, char *buf, int64_t offset, - int count, int *total) + int64_t count, int64_t *total) { int ret; + if (count >> 9 > INT_MAX) { + return -ERANGE; + } + ret = blk_write_compressed(blk, offset >> 9, (uint8_t *)buf, count >> 9); if (ret < 0) { return ret; @@ -501,8 +551,12 @@ static int do_write_compressed(BlockBackend *blk, char *buf, int64_t offset, } static int do_load_vmstate(BlockBackend *blk, char *buf, int64_t offset, - int count, int *total) + int64_t count, int64_t *total) { + if (count > INT_MAX) { + return -ERANGE; + } + *total = blk_load_vmstate(blk, (uint8_t *)buf, offset, count); if (*total < 0) { return *total; @@ -511,8 +565,12 @@ static int do_load_vmstate(BlockBackend *blk, char *buf, int64_t offset, } static int do_save_vmstate(BlockBackend *blk, char *buf, int64_t offset, - int count, int *total) + int64_t count, int64_t *total) { + if (count > INT_MAX) { + return -ERANGE; + } + *total = blk_save_vmstate(blk, (uint8_t *)buf, offset, count); if (*total < 0) { return *total; @@ -642,10 +700,11 @@ static int read_f(BlockBackend *blk, int argc, char **argv) int c, cnt; char *buf; int64_t offset; - int count; + int64_t count; /* Some compilers get confused and warn if this is not initialized. */ - int total = 0; - int pattern = 0, pattern_offset = 0, pattern_count = 0; + int64_t total = 0; + int pattern = 0; + int64_t pattern_offset = 0, pattern_count = 0; while ((c = getopt(argc, argv, "bCl:pP:qs:v")) != -1) { switch (c) { @@ -659,7 +718,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv) lflag = 1; pattern_count = cvtnum(optarg); if (pattern_count < 0) { - printf("non-numeric length argument -- %s\n", optarg); + print_cvtnum_err(pattern_count, optarg); return 0; } break; @@ -680,7 +739,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv) sflag = 1; pattern_offset = cvtnum(optarg); if (pattern_offset < 0) { - printf("non-numeric length argument -- %s\n", optarg); + print_cvtnum_err(pattern_offset, optarg); return 0; } break; @@ -703,14 +762,18 @@ static int read_f(BlockBackend *blk, int argc, char **argv) offset = cvtnum(argv[optind]); if (offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); + print_cvtnum_err(offset, argv[optind]); return 0; } optind++; count = cvtnum(argv[optind]); if (count < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); + print_cvtnum_err(count, argv[optind]); + return 0; + } else if (count > SIZE_MAX) { + printf("length cannot exceed %" PRIu64 ", given %s\n", + (uint64_t) SIZE_MAX, argv[optind]); return 0; } @@ -734,7 +797,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv) return 0; } if (count & 0x1ff) { - printf("count %d is not sector aligned\n", + printf("count %"PRId64" is not sector aligned\n", count); return 0; } @@ -762,7 +825,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv) memset(cmp_buf, pattern, pattern_count); if (memcmp(buf + pattern_offset, cmp_buf, pattern_count)) { printf("Pattern verification failed at offset %" - PRId64 ", %d bytes\n", + PRId64 ", %"PRId64" bytes\n", offset + pattern_offset, pattern_count); } g_free(cmp_buf); @@ -861,7 +924,7 @@ static int readv_f(BlockBackend *blk, int argc, char **argv) offset = cvtnum(argv[optind]); if (offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); + print_cvtnum_err(offset, argv[optind]); return 0; } optind++; @@ -957,9 +1020,9 @@ static int write_f(BlockBackend *blk, int argc, char **argv) int c, cnt; char *buf = NULL; int64_t offset; - int count; + int64_t count; /* Some compilers get confused and warn if this is not initialized. */ - int total = 0; + int64_t total = 0; int pattern = 0xcd; while ((c = getopt(argc, argv, "bcCpP:qz")) != -1) { @@ -1010,14 +1073,18 @@ static int write_f(BlockBackend *blk, int argc, char **argv) offset = cvtnum(argv[optind]); if (offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); + print_cvtnum_err(offset, argv[optind]); return 0; } optind++; count = cvtnum(argv[optind]); if (count < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); + print_cvtnum_err(count, argv[optind]); + return 0; + } else if (count > SIZE_MAX) { + printf("length cannot exceed %" PRIu64 ", given %s\n", + (uint64_t) SIZE_MAX, argv[optind]); return 0; } @@ -1029,7 +1096,7 @@ static int write_f(BlockBackend *blk, int argc, char **argv) } if (count & 0x1ff) { - printf("count %d is not sector aligned\n", + printf("count %"PRId64" is not sector aligned\n", count); return 0; } @@ -1142,7 +1209,7 @@ static int writev_f(BlockBackend *blk, int argc, char **argv) offset = cvtnum(argv[optind]); if (offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); + print_cvtnum_err(offset, argv[optind]); return 0; } optind++; @@ -1269,7 +1336,7 @@ static int multiwrite_f(BlockBackend *blk, int argc, char **argv) /* Read the offset of the request */ offset = cvtnum(argv[optind]); if (offset < 0) { - printf("non-numeric offset argument -- %s\n", argv[optind]); + print_cvtnum_err(offset, argv[optind]); goto out; } optind++; @@ -1349,6 +1416,7 @@ struct aio_ctx { int vflag; int Cflag; int Pflag; + int zflag; BlockAcctCookie acct; int pattern; struct timeval t1; @@ -1364,6 +1432,7 @@ static void aio_write_done(void *opaque, int ret) if (ret < 0) { printf("aio_write failed: %s\n", strerror(-ret)); + block_acct_failed(blk_get_stats(ctx->blk), &ctx->acct); goto out; } @@ -1378,8 +1447,10 @@ static void aio_write_done(void *opaque, int ret) print_report("wrote", &t2, ctx->offset, ctx->qiov.size, ctx->qiov.size, 1, ctx->Cflag); out: - qemu_io_free(ctx->buf); - qemu_iovec_destroy(&ctx->qiov); + if (!ctx->zflag) { + qemu_io_free(ctx->buf); + qemu_iovec_destroy(&ctx->qiov); + } g_free(ctx); } @@ -1392,6 +1463,7 @@ static void aio_read_done(void *opaque, int ret) if (ret < 0) { printf("readv failed: %s\n", strerror(-ret)); + block_acct_failed(blk_get_stats(ctx->blk), &ctx->acct); goto out; } @@ -1496,7 +1568,7 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv) ctx->offset = cvtnum(argv[optind]); if (ctx->offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); + print_cvtnum_err(ctx->offset, argv[optind]); g_free(ctx); return 0; } @@ -1505,6 +1577,7 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv) if (ctx->offset & 0x1ff) { printf("offset %" PRId64 " is not sector aligned\n", ctx->offset); + block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_READ); g_free(ctx); return 0; } @@ -1512,6 +1585,7 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv) nr_iov = argc - optind; ctx->buf = create_iovec(blk, &ctx->qiov, &argv[optind], nr_iov, 0xab); if (ctx->buf == NULL) { + block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_READ); g_free(ctx); return 0; } @@ -1541,6 +1615,7 @@ static void aio_write_help(void) " -P, -- use different pattern to fill file\n" " -C, -- report statistics in a machine parsable format\n" " -q, -- quiet mode, do not show I/O statistics\n" +" -z, -- write zeroes using blk_aio_write_zeroes\n" "\n"); } @@ -1551,7 +1626,7 @@ static const cmdinfo_t aio_write_cmd = { .cfunc = aio_write_f, .argmin = 2, .argmax = -1, - .args = "[-Cq] [-P pattern ] off len [len..]", + .args = "[-Cqz] [-P pattern ] off len [len..]", .oneline = "asynchronously writes a number of bytes", .help = aio_write_help, }; @@ -1563,7 +1638,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); ctx->blk = blk; - while ((c = getopt(argc, argv, "CqP:")) != -1) { + while ((c = getopt(argc, argv, "CqP:z")) != -1) { switch (c) { case 'C': ctx->Cflag = 1; @@ -1578,6 +1653,9 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) return 0; } break; + case 'z': + ctx->zflag = 1; + break; default: g_free(ctx); return qemuio_command_usage(&aio_write_cmd); @@ -1589,9 +1667,21 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) return qemuio_command_usage(&aio_write_cmd); } + if (ctx->zflag && optind != argc - 2) { + printf("-z supports only a single length parameter\n"); + g_free(ctx); + return 0; + } + + if (ctx->zflag && ctx->Pflag) { + printf("-z and -P cannot be specified at the same time\n"); + g_free(ctx); + return 0; + } + ctx->offset = cvtnum(argv[optind]); if (ctx->offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); + print_cvtnum_err(ctx->offset, argv[optind]); g_free(ctx); return 0; } @@ -1600,28 +1690,47 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) if (ctx->offset & 0x1ff) { printf("offset %" PRId64 " is not sector aligned\n", ctx->offset); + block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_WRITE); g_free(ctx); return 0; } - nr_iov = argc - optind; - ctx->buf = create_iovec(blk, &ctx->qiov, &argv[optind], nr_iov, pattern); - if (ctx->buf == NULL) { - g_free(ctx); - return 0; - } + if (ctx->zflag) { + int64_t count = cvtnum(argv[optind]); + if (count < 0) { + print_cvtnum_err(count, argv[optind]); + return 0; + } - gettimeofday(&ctx->t1, NULL); - block_acct_start(blk_get_stats(blk), &ctx->acct, ctx->qiov.size, - BLOCK_ACCT_WRITE); - blk_aio_writev(blk, ctx->offset >> 9, &ctx->qiov, - ctx->qiov.size >> 9, aio_write_done, ctx); + ctx->qiov.size = count; + blk_aio_write_zeroes(blk, ctx->offset >> 9, count >> 9, 0, + aio_write_done, ctx); + } else { + nr_iov = argc - optind; + ctx->buf = create_iovec(blk, &ctx->qiov, &argv[optind], nr_iov, + pattern); + if (ctx->buf == NULL) { + block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_WRITE); + g_free(ctx); + return 0; + } + + gettimeofday(&ctx->t1, NULL); + block_acct_start(blk_get_stats(blk), &ctx->acct, ctx->qiov.size, + BLOCK_ACCT_WRITE); + + blk_aio_writev(blk, ctx->offset >> 9, &ctx->qiov, + ctx->qiov.size >> 9, aio_write_done, ctx); + } return 0; } static int aio_flush_f(BlockBackend *blk, int argc, char **argv) { + BlockAcctCookie cookie; + block_acct_start(blk_get_stats(blk), &cookie, 0, BLOCK_ACCT_FLUSH); blk_drain_all(); + block_acct_done(blk_get_stats(blk), &cookie); return 0; } @@ -1651,7 +1760,7 @@ static int truncate_f(BlockBackend *blk, int argc, char **argv) offset = cvtnum(argv[1]); if (offset < 0) { - printf("non-numeric truncate argument -- %s\n", argv[1]); + print_cvtnum_err(offset, argv[1]); return 0; } @@ -1777,8 +1886,7 @@ static int discard_f(BlockBackend *blk, int argc, char **argv) struct timeval t1, t2; int Cflag = 0, qflag = 0; int c, ret; - int64_t offset; - int count; + int64_t offset, count; while ((c = getopt(argc, argv, "Cq")) != -1) { switch (c) { @@ -1799,14 +1907,19 @@ static int discard_f(BlockBackend *blk, int argc, char **argv) offset = cvtnum(argv[optind]); if (offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); + print_cvtnum_err(offset, argv[optind]); return 0; } optind++; count = cvtnum(argv[optind]); if (count < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); + print_cvtnum_err(count, argv[optind]); + return 0; + } else if (count >> BDRV_SECTOR_BITS > INT_MAX) { + printf("length cannot exceed %"PRIu64", given %s\n", + (uint64_t)INT_MAX << BDRV_SECTOR_BITS, + argv[optind]); return 0; } @@ -1833,15 +1946,14 @@ out: static int alloc_f(BlockBackend *blk, int argc, char **argv) { BlockDriverState *bs = blk_bs(blk); - int64_t offset, sector_num; - int nb_sectors, remaining; + int64_t offset, sector_num, nb_sectors, remaining; char s1[64]; - int num, sum_alloc; - int ret; + int num, ret; + int64_t sum_alloc; offset = cvtnum(argv[1]); if (offset < 0) { - printf("non-numeric offset argument -- %s\n", argv[1]); + print_cvtnum_err(offset, argv[1]); return 0; } else if (offset & 0x1ff) { printf("offset %" PRId64 " is not sector aligned\n", @@ -1852,7 +1964,11 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv) if (argc == 3) { nb_sectors = cvtnum(argv[2]); if (nb_sectors < 0) { - printf("non-numeric length argument -- %s\n", argv[2]); + print_cvtnum_err(nb_sectors, argv[2]); + return 0; + } else if (nb_sectors > INT_MAX) { + printf("length argument cannot exceed %d, given %s\n", + INT_MAX, argv[2]); return 0; } } else { @@ -1881,7 +1997,7 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv) cvtstr(offset, s1, sizeof(s1)); - printf("%d/%d sectors allocated at offset %s\n", + printf("%"PRId64"/%"PRId64" sectors allocated at offset %s\n", sum_alloc, nb_sectors, s1); return 0; } @@ -1979,6 +2095,106 @@ static const cmdinfo_t map_cmd = { .oneline = "prints the allocated areas of a file", }; +static void reopen_help(void) +{ + printf( +"\n" +" Changes the open options of an already opened image\n" +"\n" +" Example:\n" +" 'reopen -o lazy-refcounts=on' - activates lazy refcount writeback on a qcow2 image\n" +"\n" +" -r, -- Reopen the image read-only\n" +" -c, -- Change the cache mode to the given value\n" +" -o, -- Changes block driver options (cf. 'open' command)\n" +"\n"); +} + +static int reopen_f(BlockBackend *blk, int argc, char **argv); + +static QemuOptsList reopen_opts = { + .name = "reopen", + .merge_lists = true, + .head = QTAILQ_HEAD_INITIALIZER(reopen_opts.head), + .desc = { + /* no elements => accept any params */ + { /* end of list */ } + }, +}; + +static const cmdinfo_t reopen_cmd = { + .name = "reopen", + .argmin = 0, + .argmax = -1, + .cfunc = reopen_f, + .args = "[-r] [-c cache] [-o options]", + .oneline = "reopens an image with new options", + .help = reopen_help, +}; + +static int reopen_f(BlockBackend *blk, int argc, char **argv) +{ + BlockDriverState *bs = blk_bs(blk); + QemuOpts *qopts; + QDict *opts; + int c; + int flags = bs->open_flags; + bool writethrough = !blk_enable_write_cache(blk); + + BlockReopenQueue *brq; + Error *local_err = NULL; + + while ((c = getopt(argc, argv, "c:o:r")) != -1) { + switch (c) { + case 'c': + if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) { + error_report("Invalid cache option: %s", optarg); + return 0; + } + break; + case 'o': + if (!qemu_opts_parse_noisily(&reopen_opts, optarg, 0)) { + qemu_opts_reset(&reopen_opts); + return 0; + } + break; + case 'r': + flags &= ~BDRV_O_RDWR; + break; + default: + qemu_opts_reset(&reopen_opts); + return qemuio_command_usage(&reopen_cmd); + } + } + + if (optind != argc) { + qemu_opts_reset(&reopen_opts); + return qemuio_command_usage(&reopen_cmd); + } + + if (writethrough != blk_enable_write_cache(blk) && + blk_get_attached_dev(blk)) + { + error_report("Cannot change cache.writeback: Device attached"); + qemu_opts_reset(&reopen_opts); + return 0; + } + + qopts = qemu_opts_find(&reopen_opts, NULL); + opts = qopts ? qemu_opts_to_qdict(qopts, NULL) : NULL; + qemu_opts_reset(&reopen_opts); + + brq = bdrv_reopen_queue(NULL, bs, opts, flags); + bdrv_reopen_multiple(brq, &local_err); + if (local_err) { + error_report_err(local_err); + } else { + blk_set_enable_write_cache(blk, !writethrough); + } + + return 0; +} + static int break_f(BlockBackend *blk, int argc, char **argv) { int ret; @@ -2102,9 +2318,13 @@ static const cmdinfo_t sigraise_cmd = { static int sigraise_f(BlockBackend *blk, int argc, char **argv) { - int sig = cvtnum(argv[1]); + int64_t sig = cvtnum(argv[1]); if (sig < 0) { - printf("non-numeric signal number argument -- %s\n", argv[1]); + print_cvtnum_err(sig, argv[1]); + return 0; + } else if (sig > NSIG) { + printf("signal argument '%s' is too large to be a valid signal\n", + argv[1]); return 0; } @@ -2266,6 +2486,7 @@ static void __attribute((constructor)) init_qemuio_commands(void) qemuio_add_command(&discard_cmd); qemuio_add_command(&alloc_cmd); qemuio_add_command(&map_cmd); + qemuio_add_command(&reopen_cmd); qemuio_add_command(&break_cmd); qemuio_add_command(&remove_break_cmd); qemuio_add_command(&resume_cmd); diff --git a/qemu/qemu-io.c b/qemu/qemu-io.c index f1e3a6713..0598251e7 100644 --- a/qemu/qemu-io.c +++ b/qemu/qemu-io.c @@ -7,13 +7,11 @@ * 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 -#include -#include -#include +#include "qemu/osdep.h" #include #include +#include "qapi/error.h" #include "qemu-io.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" @@ -21,9 +19,11 @@ #include "qemu/config-file.h" #include "qemu/readline.h" #include "qapi/qmp/qstring.h" +#include "qom/object_interfaces.h" #include "sysemu/block-backend.h" #include "block/block_int.h" #include "trace/control.h" +#include "crypto/init.h" #define CMD_NOFILE_OK 0x01 @@ -34,6 +34,7 @@ static BlockBackend *qemuio_blk; /* qemu-io commands passed using -c */ static int ncmdline; static char **cmdline; +static bool imageOpts; static ReadLineState *readline_state; @@ -51,28 +52,26 @@ static const cmdinfo_t close_cmd = { .oneline = "close the current open file", }; -static int openfile(char *name, int flags, QDict *opts) +static int openfile(char *name, int flags, bool writethrough, QDict *opts) { Error *local_err = NULL; BlockDriverState *bs; if (qemuio_blk) { - fprintf(stderr, "file open already, try 'help close'\n"); + error_report("file open already, try 'help close'"); QDECREF(opts); return 1; } - qemuio_blk = blk_new_open("hda", name, NULL, opts, flags, &local_err); + qemuio_blk = blk_new_open(name, NULL, opts, flags, &local_err); if (!qemuio_blk) { - fprintf(stderr, "%s: can't open%s%s: %s\n", progname, - name ? " device " : "", name ?: "", - error_get_pretty(local_err)); - error_free(local_err); + error_reportf_err(local_err, "can't open%s%s: ", + name ? " device " : "", name ?: ""); return 1; } bs = blk_bs(qemuio_blk); - if (bdrv_is_encrypted(bs)) { + if (bdrv_is_encrypted(bs) && bdrv_key_required(bs)) { char password[256]; printf("Disk image '%s' is encrypted.\n", name); if (qemu_read_password(password, sizeof(password)) < 0) { @@ -85,6 +84,7 @@ static int openfile(char *name, int flags, QDict *opts) } } + blk_set_enable_write_cache(qemuio_blk, !writethrough); return 0; @@ -139,6 +139,7 @@ static int open_f(BlockBackend *blk, int argc, char **argv) { int flags = 0; int readonly = 0; + bool writethrough = true; int c; QemuOpts *qopts; QDict *opts; @@ -149,14 +150,18 @@ static int open_f(BlockBackend *blk, int argc, char **argv) flags |= BDRV_O_SNAPSHOT; break; case 'n': - flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; + flags |= BDRV_O_NOCACHE; + writethrough = false; break; case 'r': readonly = 1; break; case 'o': + if (imageOpts) { + printf("--image-opts and 'open -o' are mutually exclusive\n"); + return 0; + } if (!qemu_opts_parse_noisily(&empty_opts, optarg, false)) { - printf("could not parse option list -- %s\n", optarg); qemu_opts_reset(&empty_opts); return 0; } @@ -171,14 +176,22 @@ static int open_f(BlockBackend *blk, int argc, char **argv) flags |= BDRV_O_RDWR; } + if (imageOpts && (optind == argc - 1)) { + if (!qemu_opts_parse_noisily(&empty_opts, argv[optind], false)) { + qemu_opts_reset(&empty_opts); + return 0; + } + optind++; + } + qopts = qemu_opts_find(&empty_opts, NULL); opts = qopts ? qemu_opts_to_qdict(qopts, NULL) : NULL; qemu_opts_reset(&empty_opts); if (optind == argc - 1) { - return openfile(argv[optind], flags, opts); + return openfile(argv[optind], flags, writethrough, opts); } else if (optind == argc) { - return openfile(NULL, flags, opts); + return openfile(NULL, flags, writethrough, opts); } else { QDECREF(opts); return qemuio_command_usage(&open_cmd); @@ -206,6 +219,8 @@ static void usage(const char *name) "Usage: %s [-h] [-V] [-rsnm] [-f FMT] [-c STRING] ... [file]\n" "QEMU Disk exerciser\n" "\n" +" --object OBJECTDEF define an object such as 'secret' for\n" +" passwords and/or encryption keys\n" " -c, --cmd STRING execute command with its arguments\n" " from the given string\n" " -f, --format FMT specifies the block driver to use\n" @@ -367,31 +382,60 @@ static void reenable_tty_echo(void) qemu_set_tty_echo(STDIN_FILENO, true); } +enum { + OPTION_OBJECT = 256, + OPTION_IMAGE_OPTS = 257, +}; + +static QemuOptsList qemu_object_opts = { + .name = "object", + .implied_opt_name = "qom-type", + .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head), + .desc = { + { } + }, +}; + + +static QemuOptsList file_opts = { + .name = "file", + .implied_opt_name = "file", + .head = QTAILQ_HEAD_INITIALIZER(file_opts.head), + .desc = { + /* no elements => accept any params */ + { /* end of list */ } + }, +}; + int main(int argc, char **argv) { int readonly = 0; const char *sopt = "hVc:d:f:rsnmgkt:T:"; const struct option lopt[] = { - { "help", 0, NULL, 'h' }, - { "version", 0, NULL, 'V' }, - { "offset", 1, NULL, 'o' }, - { "cmd", 1, NULL, 'c' }, - { "format", 1, NULL, 'f' }, - { "read-only", 0, NULL, 'r' }, - { "snapshot", 0, NULL, 's' }, - { "nocache", 0, NULL, 'n' }, - { "misalign", 0, NULL, 'm' }, - { "native-aio", 0, NULL, 'k' }, - { "discard", 1, NULL, 'd' }, - { "cache", 1, NULL, 't' }, - { "trace", 1, NULL, 'T' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "offset", required_argument, NULL, 'o' }, + { "cmd", required_argument, NULL, 'c' }, + { "format", required_argument, NULL, 'f' }, + { "read-only", no_argument, NULL, 'r' }, + { "snapshot", no_argument, NULL, 's' }, + { "nocache", no_argument, NULL, 'n' }, + { "misalign", no_argument, NULL, 'm' }, + { "native-aio", no_argument, NULL, 'k' }, + { "discard", required_argument, NULL, 'd' }, + { "cache", required_argument, NULL, 't' }, + { "trace", required_argument, NULL, 'T' }, + { "object", required_argument, NULL, OPTION_OBJECT }, + { "image-opts", no_argument, NULL, OPTION_IMAGE_OPTS }, { NULL, 0, NULL, 0 } }; int c; int opt_index = 0; int flags = BDRV_O_UNMAP; + bool writethrough = true; Error *local_error = NULL; QDict *opts = NULL; + const char *format = NULL; #ifdef CONFIG_POSIX signal(SIGPIPE, SIG_IGN); @@ -400,6 +444,13 @@ int main(int argc, char **argv) progname = basename(argv[0]); qemu_init_exec_dir(argv[0]); + if (qcrypto_init(&local_error) < 0) { + error_reportf_err(local_error, "cannot initialize crypto: "); + exit(1); + } + + module_call_init(MODULE_INIT_QOM); + qemu_add_opts(&qemu_object_opts); bdrv_init(); while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) { @@ -408,7 +459,8 @@ int main(int argc, char **argv) flags |= BDRV_O_SNAPSHOT; break; case 'n': - flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; + flags |= BDRV_O_NOCACHE; + writethrough = false; break; case 'd': if (bdrv_parse_discard_flags(optarg, &flags) < 0) { @@ -417,10 +469,7 @@ int main(int argc, char **argv) } break; case 'f': - if (!opts) { - opts = qdict_new(); - } - qdict_put(opts, "driver", qstring_from_str(optarg)); + format = optarg; break; case 'c': add_user_command(optarg); @@ -435,13 +484,13 @@ int main(int argc, char **argv) flags |= BDRV_O_NATIVE_AIO; break; case 't': - if (bdrv_parse_cache_flags(optarg, &flags) < 0) { + if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) { error_report("Invalid cache option: %s", optarg); exit(1); } break; case 'T': - if (!trace_init_backends(optarg, NULL)) { + if (!trace_init_backends()) { exit(1); /* error message will have been printed */ } break; @@ -451,6 +500,17 @@ int main(int argc, char **argv) case 'h': usage(progname); exit(0); + case OPTION_OBJECT: { + QemuOpts *qopts; + qopts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!qopts) { + exit(1); + } + } break; + case OPTION_IMAGE_OPTS: + imageOpts = true; + break; default: usage(progname); exit(1); @@ -462,11 +522,22 @@ int main(int argc, char **argv) exit(1); } + if (format && imageOpts) { + error_report("--image-opts and -f are mutually exclusive"); + exit(1); + } + if (qemu_init_main_loop(&local_error)) { error_report_err(local_error); exit(1); } + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + exit(1); + } + /* initialize commands */ qemuio_add_command(&quit_cmd); qemuio_add_command(&open_cmd); @@ -487,7 +558,21 @@ int main(int argc, char **argv) } if ((argc - optind) == 1) { - openfile(argv[optind], flags, opts); + if (imageOpts) { + QemuOpts *qopts = NULL; + qopts = qemu_opts_parse_noisily(&file_opts, argv[optind], false); + if (!qopts) { + exit(1); + } + opts = qemu_opts_to_qdict(qopts, NULL); + openfile(NULL, flags, writethrough, opts); + } else { + if (format) { + opts = qdict_new(); + qdict_put(opts, "driver", qstring_from_str(format)); + } + openfile(argv[optind], flags, writethrough, opts); + } } command_loop(); diff --git a/qemu/qemu-log.c b/qemu/qemu-log.c deleted file mode 100644 index 13f3813f6..000000000 --- a/qemu/qemu-log.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Logging support - * - * Copyright (c) 2003 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 . - */ - -#include "qemu-common.h" -#include "qemu/log.h" - -static char *logfilename; -FILE *qemu_logfile; -int qemu_loglevel; -static int log_append = 0; - -void qemu_log(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - if (qemu_logfile) { - vfprintf(qemu_logfile, fmt, ap); - } - va_end(ap); -} - -void qemu_log_mask(int mask, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - if ((qemu_loglevel & mask) && qemu_logfile) { - vfprintf(qemu_logfile, fmt, ap); - } - va_end(ap); -} - -/* enable or disable low levels log */ -void do_qemu_set_log(int log_flags, bool use_own_buffers) -{ - qemu_loglevel = log_flags; - if (qemu_loglevel && !qemu_logfile) { - if (logfilename) { - qemu_logfile = fopen(logfilename, log_append ? "a" : "w"); - if (!qemu_logfile) { - perror(logfilename); - _exit(1); - } - } else { - /* Default to stderr if no log file specified */ - qemu_logfile = stderr; - } - /* must avoid mmap() usage of glibc by setting a buffer "by hand" */ - if (use_own_buffers) { - static char logfile_buf[4096]; - - setvbuf(qemu_logfile, logfile_buf, _IOLBF, sizeof(logfile_buf)); - } else { -#if defined(_WIN32) - /* Win32 doesn't support line-buffering, so use unbuffered output. */ - setvbuf(qemu_logfile, NULL, _IONBF, 0); -#else - setvbuf(qemu_logfile, NULL, _IOLBF, 0); -#endif - log_append = 1; - } - } - if (!qemu_loglevel && qemu_logfile) { - qemu_log_close(); - } -} - -void qemu_set_log_filename(const char *filename) -{ - g_free(logfilename); - logfilename = g_strdup(filename); - qemu_log_close(); - qemu_set_log(qemu_loglevel); -} - -const QEMULogItem qemu_log_items[] = { - { CPU_LOG_TB_OUT_ASM, "out_asm", - "show generated host assembly code for each compiled TB" }, - { CPU_LOG_TB_IN_ASM, "in_asm", - "show target assembly code for each compiled TB" }, - { CPU_LOG_TB_OP, "op", - "show micro ops for each compiled TB" }, - { CPU_LOG_TB_OP_OPT, "op_opt", - "show micro ops (x86 only: before eflags optimization) and\n" - "after liveness analysis" }, - { CPU_LOG_INT, "int", - "show interrupts/exceptions in short format" }, - { CPU_LOG_EXEC, "exec", - "show trace before each executed TB (lots of logs)" }, - { CPU_LOG_TB_CPU, "cpu", - "show CPU state before block translation" }, - { CPU_LOG_MMU, "mmu", - "log MMU-related activities" }, - { CPU_LOG_PCALL, "pcall", - "x86 only: show protected mode far calls/returns/exceptions" }, - { CPU_LOG_RESET, "cpu_reset", - "show CPU state before CPU resets" }, - { CPU_LOG_IOPORT, "ioport", - "show all i/o ports accesses" }, - { LOG_UNIMP, "unimp", - "log unimplemented functionality" }, - { LOG_GUEST_ERROR, "guest_errors", - "log when the guest OS does something invalid (eg accessing a\n" - "non-existent register)" }, - { 0, NULL, NULL }, -}; - -static int cmp1(const char *s1, int n, const char *s2) -{ - if (strlen(s2) != n) { - return 0; - } - return memcmp(s1, s2, n) == 0; -} - -/* takes a comma separated list of log masks. Return 0 if error. */ -int qemu_str_to_log_mask(const char *str) -{ - const QEMULogItem *item; - int mask; - const char *p, *p1; - - p = str; - mask = 0; - for (;;) { - p1 = strchr(p, ','); - if (!p1) { - p1 = p + strlen(p); - } - if (cmp1(p,p1-p,"all")) { - for (item = qemu_log_items; item->mask != 0; item++) { - mask |= item->mask; - } - } else { - for (item = qemu_log_items; item->mask != 0; item++) { - if (cmp1(p, p1 - p, item->name)) { - goto found; - } - } - return 0; - } - found: - mask |= item->mask; - if (*p1 != ',') { - break; - } - p = p1 + 1; - } - return mask; -} - -void qemu_print_log_usage(FILE *f) -{ - const QEMULogItem *item; - fprintf(f, "Log items (comma separated):\n"); - for (item = qemu_log_items; item->mask != 0; item++) { - fprintf(f, "%-10s %s\n", item->name, item->help); - } -} diff --git a/qemu/qemu-nbd.c b/qemu/qemu-nbd.c index 5106b802e..c55b40ffc 100644 --- a/qemu/qemu-nbd.c +++ b/qemu/qemu-nbd.c @@ -16,45 +16,48 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" +#include "qemu/cutils.h" #include "sysemu/block-backend.h" #include "block/block_int.h" #include "block/nbd.h" #include "qemu/main-loop.h" -#include "qemu/sockets.h" #include "qemu/error-report.h" +#include "qemu/config-file.h" #include "block/snapshot.h" #include "qapi/util.h" #include "qapi/qmp/qstring.h" +#include "qom/object_interfaces.h" +#include "io/channel-socket.h" +#include "crypto/init.h" -#include -#include #include -#include -#include -#include -#include -#include -#include -#include #include #include #define SOCKET_PATH "/var/lock/qemu-nbd-%s" -#define QEMU_NBD_OPT_CACHE 1 -#define QEMU_NBD_OPT_AIO 2 -#define QEMU_NBD_OPT_DISCARD 3 -#define QEMU_NBD_OPT_DETECT_ZEROES 4 +#define QEMU_NBD_OPT_CACHE 256 +#define QEMU_NBD_OPT_AIO 257 +#define QEMU_NBD_OPT_DISCARD 258 +#define QEMU_NBD_OPT_DETECT_ZEROES 259 +#define QEMU_NBD_OPT_OBJECT 260 +#define QEMU_NBD_OPT_TLSCREDS 261 +#define QEMU_NBD_OPT_IMAGE_OPTS 262 static NBDExport *exp; +static bool newproto; static int verbose; static char *srcpath; -static char *sockpath; +static SocketAddress *saddr; static int persistent = 0; static enum { RUNNING, TERMINATE, TERMINATING, TERMINATED } state; static int shared = 1; static int nb_fds; -static int server_fd; +static QIOChannelSocket *server_ioc; +static int server_watch = -1; +static QCryptoTLSCreds *tlscreds; static void usage(const char *name) { @@ -73,11 +76,15 @@ static void usage(const char *name) " -e, --shared=NUM device can be shared by NUM clients (default '1')\n" " -t, --persistent don't exit on the last connection\n" " -v, --verbose display extra debugging information\n" +" -x, --export-name=NAME expose export by name\n" "\n" "Exposing part of the image:\n" " -o, --offset=OFFSET offset into the image\n" " -P, --partition=NUM only expose partition NUM\n" "\n" +"General purpose options:\n" +" --object type,id=ID,... define an object such as 'secret' for providing\n" +" passwords and/or encryption keys\n" #ifdef __linux__ "Kernel NBD client support:\n" " -c, --connect=DEV connect FILE to the local NBD device DEV\n" @@ -98,11 +105,10 @@ static void usage(const char *name) " '[ID_OR_NAME]'\n" " -n, --nocache disable host cache\n" " --cache=MODE set cache mode (none, writeback, ...)\n" -#ifdef CONFIG_LINUX_AIO " --aio=MODE set AIO mode (native or threads)\n" -#endif " --discard=MODE set discard mode (ignore, unmap)\n" -" --detect-zeroes=MODE set detect-zeroes mode (off, on, discard)\n" +" --detect-zeroes=MODE set detect-zeroes mode (off, on, unmap)\n" +" --image-opts treat FILE as a full set of image options\n" "\n" "Report bugs to \n" , name, NBD_DEFAULT_PORT, "DEVICE"); @@ -159,8 +165,8 @@ static int find_partition(BlockBackend *blk, int partition, int ret; if ((ret = blk_read(blk, 0, data, 1)) < 0) { - errno = -ret; - err(EXIT_FAILURE, "error while reading"); + error_report("error while reading: %s", strerror(-ret)); + exit(EXIT_FAILURE); } if (data[510] != 0x55 || data[511] != 0xaa) { @@ -180,8 +186,8 @@ static int find_partition(BlockBackend *blk, int partition, int j; if ((ret = blk_read(blk, mbr[i].start_sector_abs, data1, 1)) < 0) { - errno = -ret; - err(EXIT_FAILURE, "error while reading"); + error_report("error while reading: %s", strerror(-ret)); + exit(EXIT_FAILURE); } for (j = 0; j < 4; j++) { @@ -209,56 +215,10 @@ static int find_partition(BlockBackend *blk, int partition, static void termsig_handler(int signum) { - state = TERMINATE; + atomic_cmpxchg(&state, RUNNING, TERMINATE); qemu_notify_event(); } -static void combine_addr(char *buf, size_t len, const char* address, - uint16_t port) -{ - /* If the address-part contains a colon, it's an IPv6 IP so needs [] */ - if (strstr(address, ":")) { - snprintf(buf, len, "[%s]:%u", address, port); - } else { - snprintf(buf, len, "%s:%u", address, port); - } -} - -static int tcp_socket_incoming(const char *address, uint16_t port) -{ - char address_and_port[128]; - Error *local_err = NULL; - - combine_addr(address_and_port, 128, address, port); - int fd = inet_listen(address_and_port, NULL, 0, SOCK_STREAM, 0, &local_err); - - if (local_err != NULL) { - error_report_err(local_err); - } - return fd; -} - -static int unix_socket_incoming(const char *path) -{ - Error *local_err = NULL; - int fd = unix_listen(path, NULL, 0, &local_err); - - if (local_err != NULL) { - error_report_err(local_err); - } - return fd; -} - -static int unix_socket_outgoing(const char *path) -{ - Error *local_err = NULL; - int fd = unix_connect(path, &local_err); - - if (local_err != NULL) { - error_report_err(local_err); - } - return fd; -} static void *show_parts(void *arg) { @@ -282,22 +242,26 @@ static void *nbd_client_thread(void *arg) char *device = arg; off_t size; uint32_t nbdflags; - int fd, sock; + QIOChannelSocket *sioc; + int fd; int ret; pthread_t show_parts_thread; Error *local_error = NULL; - sock = unix_socket_outgoing(sockpath); - if (sock < 0) { + sioc = qio_channel_socket_new(); + if (qio_channel_socket_connect_sync(sioc, + saddr, + &local_error) < 0) { + error_report_err(local_error); goto out; } - ret = nbd_receive_negotiate(sock, NULL, &nbdflags, + ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, &nbdflags, + NULL, NULL, NULL, &size, &local_error); if (ret < 0) { if (local_error) { - fprintf(stderr, "%s\n", error_get_pretty(local_error)); - error_free(local_error); + error_report_err(local_error); } goto out_socket; } @@ -305,11 +269,11 @@ static void *nbd_client_thread(void *arg) fd = open(device, O_RDWR); if (fd < 0) { /* Linux-only, we can use %m in printf. */ - fprintf(stderr, "Failed to open %s: %m\n", device); + error_report("Failed to open %s: %m", device); goto out_socket; } - ret = nbd_init(fd, sock, nbdflags, size); + ret = nbd_init(fd, sioc, nbdflags, size); if (ret < 0) { goto out_fd; } @@ -330,13 +294,14 @@ static void *nbd_client_thread(void *arg) goto out_fd; } close(fd); + object_unref(OBJECT(sioc)); kill(getpid(), SIGTERM); return (void *) EXIT_SUCCESS; out_fd: close(fd); out_socket: - closesocket(sock); + object_unref(OBJECT(sioc)); out: kill(getpid(), SIGTERM); return (void *) EXIT_FAILURE; @@ -353,7 +318,7 @@ static void nbd_export_closed(NBDExport *exp) state = TERMINATED; } -static void nbd_update_server_fd_handler(int fd); +static void nbd_update_server_watch(void); static void nbd_client_closed(NBDClient *client) { @@ -361,45 +326,130 @@ static void nbd_client_closed(NBDClient *client) if (nb_fds == 0 && !persistent && state == RUNNING) { state = TERMINATE; } - nbd_update_server_fd_handler(server_fd); - qemu_notify_event(); + nbd_update_server_watch(); nbd_client_put(client); } -static void nbd_accept(void *opaque) +static gboolean nbd_accept(QIOChannel *ioc, GIOCondition cond, gpointer opaque) { - struct sockaddr_in addr; - socklen_t addr_len = sizeof(addr); + QIOChannelSocket *cioc; - int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len); - if (fd < 0) { - perror("accept"); - return; + cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), + NULL); + if (!cioc) { + return TRUE; } if (state >= TERMINATE) { - close(fd); - return; + object_unref(OBJECT(cioc)); + return TRUE; } - if (nbd_client_new(exp, fd, nbd_client_closed)) { - nb_fds++; - nbd_update_server_fd_handler(server_fd); + nb_fds++; + nbd_update_server_watch(); + nbd_client_new(newproto ? NULL : exp, cioc, + tlscreds, NULL, nbd_client_closed); + object_unref(OBJECT(cioc)); + + return TRUE; +} + +static void nbd_update_server_watch(void) +{ + if (nbd_can_accept()) { + if (server_watch == -1) { + server_watch = qio_channel_add_watch(QIO_CHANNEL(server_ioc), + G_IO_IN, + nbd_accept, + NULL, NULL); + } } else { - shutdown(fd, 2); - close(fd); + if (server_watch != -1) { + g_source_remove(server_watch); + server_watch = -1; + } } } -static void nbd_update_server_fd_handler(int fd) + +static SocketAddress *nbd_build_socket_address(const char *sockpath, + const char *bindto, + const char *port) { - if (nbd_can_accept()) { - qemu_set_fd_handler(fd, nbd_accept, NULL, (void *)(uintptr_t)fd); + SocketAddress *saddr; + + saddr = g_new0(SocketAddress, 1); + if (sockpath) { + saddr->type = SOCKET_ADDRESS_KIND_UNIX; + saddr->u.q_unix.data = g_new0(UnixSocketAddress, 1); + saddr->u.q_unix.data->path = g_strdup(sockpath); } else { - qemu_set_fd_handler(fd, NULL, NULL, NULL); + InetSocketAddress *inet; + saddr->type = SOCKET_ADDRESS_KIND_INET; + inet = saddr->u.inet.data = g_new0(InetSocketAddress, 1); + inet->host = g_strdup(bindto); + if (port) { + inet->port = g_strdup(port); + } else { + inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT); + } } + + return saddr; } + +static QemuOptsList file_opts = { + .name = "file", + .implied_opt_name = "file", + .head = QTAILQ_HEAD_INITIALIZER(file_opts.head), + .desc = { + /* no elements => accept any params */ + { /* end of list */ } + }, +}; + +static QemuOptsList qemu_object_opts = { + .name = "object", + .implied_opt_name = "qom-type", + .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head), + .desc = { + { } + }, +}; + + + +static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) +{ + Object *obj; + QCryptoTLSCreds *creds; + + obj = object_resolve_path_component( + object_get_objects_root(), id); + if (!obj) { + error_setg(errp, "No TLS credentials with id '%s'", + id); + return NULL; + } + creds = (QCryptoTLSCreds *) + object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS); + if (!creds) { + error_setg(errp, "Object with id '%s' is not TLS credentials", + id); + return NULL; + } + + if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + error_setg(errp, + "Expecting TLS credentials with a server endpoint"); + return NULL; + } + object_ref(obj); + return creds; +} + + int main(int argc, char **argv) { BlockBackend *blk; @@ -408,56 +458,60 @@ int main(int argc, char **argv) uint32_t nbdflags = 0; bool disconnect = false; const char *bindto = "0.0.0.0"; + const char *port = NULL; + char *sockpath = NULL; char *device = NULL; - int port = NBD_DEFAULT_PORT; off_t fd_size; QemuOpts *sn_opts = NULL; const char *sn_id_or_name = NULL; - const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:"; + const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:"; struct option lopt[] = { - { "help", 0, NULL, 'h' }, - { "version", 0, NULL, 'V' }, - { "bind", 1, NULL, 'b' }, - { "port", 1, NULL, 'p' }, - { "socket", 1, NULL, 'k' }, - { "offset", 1, NULL, 'o' }, - { "read-only", 0, NULL, 'r' }, - { "partition", 1, NULL, 'P' }, - { "connect", 1, NULL, 'c' }, - { "disconnect", 0, NULL, 'd' }, - { "snapshot", 0, NULL, 's' }, - { "load-snapshot", 1, NULL, 'l' }, - { "nocache", 0, NULL, 'n' }, - { "cache", 1, NULL, QEMU_NBD_OPT_CACHE }, -#ifdef CONFIG_LINUX_AIO - { "aio", 1, NULL, QEMU_NBD_OPT_AIO }, -#endif - { "discard", 1, NULL, QEMU_NBD_OPT_DISCARD }, - { "detect-zeroes", 1, NULL, QEMU_NBD_OPT_DETECT_ZEROES }, - { "shared", 1, NULL, 'e' }, - { "format", 1, NULL, 'f' }, - { "persistent", 0, NULL, 't' }, - { "verbose", 0, NULL, 'v' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "bind", required_argument, NULL, 'b' }, + { "port", required_argument, NULL, 'p' }, + { "socket", required_argument, NULL, 'k' }, + { "offset", required_argument, NULL, 'o' }, + { "read-only", no_argument, NULL, 'r' }, + { "partition", required_argument, NULL, 'P' }, + { "connect", required_argument, NULL, 'c' }, + { "disconnect", no_argument, NULL, 'd' }, + { "snapshot", no_argument, NULL, 's' }, + { "load-snapshot", required_argument, NULL, 'l' }, + { "nocache", no_argument, NULL, 'n' }, + { "cache", required_argument, NULL, QEMU_NBD_OPT_CACHE }, + { "aio", required_argument, NULL, QEMU_NBD_OPT_AIO }, + { "discard", required_argument, NULL, QEMU_NBD_OPT_DISCARD }, + { "detect-zeroes", required_argument, NULL, + QEMU_NBD_OPT_DETECT_ZEROES }, + { "shared", required_argument, NULL, 'e' }, + { "format", required_argument, NULL, 'f' }, + { "persistent", no_argument, NULL, 't' }, + { "verbose", no_argument, NULL, 'v' }, + { "object", required_argument, NULL, QEMU_NBD_OPT_OBJECT }, + { "export-name", required_argument, NULL, 'x' }, + { "tls-creds", required_argument, NULL, QEMU_NBD_OPT_TLSCREDS }, + { "image-opts", no_argument, NULL, QEMU_NBD_OPT_IMAGE_OPTS }, { NULL, 0, NULL, 0 } }; int ch; int opt_ind = 0; - int li; char *end; int flags = BDRV_O_RDWR; int partition = -1; int ret = 0; - int fd; bool seen_cache = false; bool seen_discard = false; -#ifdef CONFIG_LINUX_AIO bool seen_aio = false; -#endif pthread_t client_thread; const char *fmt = NULL; Error *local_err = NULL; BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; QDict *options = NULL; + const char *export_name = NULL; + const char *tlscredsid = NULL; + bool imageOpts = false; + bool writethrough = true; /* The client thread uses SIGTERM to interrupt the server. A signal * handler ensures that "qemu-nbd -v -c" exits with a nice status code. @@ -466,6 +520,14 @@ int main(int argc, char **argv) memset(&sa_sigterm, 0, sizeof(sa_sigterm)); sa_sigterm.sa_handler = termsig_handler; sigaction(SIGTERM, &sa_sigterm, NULL); + + if (qcrypto_init(&local_err) < 0) { + error_reportf_err(local_err, "cannot initialize crypto: "); + exit(1); + } + + module_call_init(MODULE_INIT_QOM); + qemu_add_opts(&qemu_object_opts); qemu_init_exec_dir(argv[0]); while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { @@ -478,17 +540,19 @@ int main(int argc, char **argv) /* fallthrough */ case QEMU_NBD_OPT_CACHE: if (seen_cache) { - errx(EXIT_FAILURE, "-n and --cache can only be specified once"); + error_report("-n and --cache can only be specified once"); + exit(EXIT_FAILURE); } seen_cache = true; - if (bdrv_parse_cache_flags(optarg, &flags) == -1) { - errx(EXIT_FAILURE, "Invalid cache mode `%s'", optarg); + if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) == -1) { + error_report("Invalid cache mode `%s'", optarg); + exit(EXIT_FAILURE); } break; -#ifdef CONFIG_LINUX_AIO case QEMU_NBD_OPT_AIO: if (seen_aio) { - errx(EXIT_FAILURE, "--aio can only be specified once"); + error_report("--aio can only be specified once"); + exit(EXIT_FAILURE); } seen_aio = true; if (!strcmp(optarg, "native")) { @@ -496,56 +560,55 @@ int main(int argc, char **argv) } else if (!strcmp(optarg, "threads")) { /* this is the default */ } else { - errx(EXIT_FAILURE, "invalid aio mode `%s'", optarg); + error_report("invalid aio mode `%s'", optarg); + exit(EXIT_FAILURE); } break; -#endif case QEMU_NBD_OPT_DISCARD: if (seen_discard) { - errx(EXIT_FAILURE, "--discard can only be specified once"); + error_report("--discard can only be specified once"); + exit(EXIT_FAILURE); } seen_discard = true; if (bdrv_parse_discard_flags(optarg, &flags) == -1) { - errx(EXIT_FAILURE, "Invalid discard mode `%s'", optarg); + error_report("Invalid discard mode `%s'", optarg); + exit(EXIT_FAILURE); } break; case QEMU_NBD_OPT_DETECT_ZEROES: detect_zeroes = qapi_enum_parse(BlockdevDetectZeroesOptions_lookup, optarg, - BLOCKDEV_DETECT_ZEROES_OPTIONS_MAX, + BLOCKDEV_DETECT_ZEROES_OPTIONS__MAX, BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, &local_err); if (local_err) { - errx(EXIT_FAILURE, "Failed to parse detect_zeroes mode: %s", - error_get_pretty(local_err)); + error_reportf_err(local_err, + "Failed to parse detect_zeroes mode: "); + exit(EXIT_FAILURE); } if (detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP && !(flags & BDRV_O_UNMAP)) { - errx(EXIT_FAILURE, "setting detect-zeroes to unmap is not allowed " - "without setting discard operation to unmap"); + error_report("setting detect-zeroes to unmap is not allowed " + "without setting discard operation to unmap"); + exit(EXIT_FAILURE); } break; case 'b': bindto = optarg; break; case 'p': - li = strtol(optarg, &end, 0); - if (*end) { - errx(EXIT_FAILURE, "Invalid port `%s'", optarg); - } - if (li < 1 || li > 65535) { - errx(EXIT_FAILURE, "Port out of range `%s'", optarg); - } - port = (uint16_t)li; + port = optarg; break; case 'o': dev_offset = strtoll (optarg, &end, 0); if (*end) { - errx(EXIT_FAILURE, "Invalid offset `%s'", optarg); + error_report("Invalid offset `%s'", optarg); + exit(EXIT_FAILURE); } if (dev_offset < 0) { - errx(EXIT_FAILURE, "Offset must be positive `%s'", optarg); + error_report("Offset must be positive `%s'", optarg); + exit(EXIT_FAILURE); } break; case 'l': @@ -553,8 +616,9 @@ int main(int argc, char **argv) sn_opts = qemu_opts_parse_noisily(&internal_snapshot_opts, optarg, false); if (!sn_opts) { - errx(EXIT_FAILURE, "Failed in parsing snapshot param `%s'", - optarg); + error_report("Failed in parsing snapshot param `%s'", + optarg); + exit(EXIT_FAILURE); } } else { sn_id_or_name = optarg; @@ -567,16 +631,19 @@ int main(int argc, char **argv) case 'P': partition = strtol(optarg, &end, 0); if (*end) { - errx(EXIT_FAILURE, "Invalid partition `%s'", optarg); + error_report("Invalid partition `%s'", optarg); + exit(EXIT_FAILURE); } if (partition < 1 || partition > 8) { - errx(EXIT_FAILURE, "Invalid partition %d", partition); + error_report("Invalid partition %d", partition); + exit(EXIT_FAILURE); } break; case 'k': sockpath = optarg; if (sockpath[0] != '/') { - errx(EXIT_FAILURE, "socket path must be absolute\n"); + error_report("socket path must be absolute"); + exit(EXIT_FAILURE); } break; case 'd': @@ -588,10 +655,12 @@ int main(int argc, char **argv) case 'e': shared = strtol(optarg, &end, 0); if (*end) { - errx(EXIT_FAILURE, "Invalid shared device number '%s'", optarg); + error_report("Invalid shared device number '%s'", optarg); + exit(EXIT_FAILURE); } if (shared < 1) { - errx(EXIT_FAILURE, "Shared device number must be greater than 0\n"); + error_report("Shared device number must be greater than 0"); + exit(EXIT_FAILURE); } break; case 'f': @@ -600,6 +669,9 @@ int main(int argc, char **argv) case 't': persistent = 1; break; + case 'x': + export_name = optarg; + break; case 'v': verbose = 1; break; @@ -612,25 +684,69 @@ int main(int argc, char **argv) exit(0); break; case '?': - errx(EXIT_FAILURE, "Try `%s --help' for more information.", - argv[0]); + error_report("Try `%s --help' for more information.", argv[0]); + exit(EXIT_FAILURE); + case QEMU_NBD_OPT_OBJECT: { + QemuOpts *opts; + opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!opts) { + exit(EXIT_FAILURE); + } + } break; + case QEMU_NBD_OPT_TLSCREDS: + tlscredsid = optarg; + break; + case QEMU_NBD_OPT_IMAGE_OPTS: + imageOpts = true; + break; } } if ((argc - optind) != 1) { - errx(EXIT_FAILURE, "Invalid number of argument.\n" - "Try `%s --help' for more information.", - argv[0]); + error_report("Invalid number of arguments"); + error_printf("Try `%s --help' for more information.\n", argv[0]); + exit(EXIT_FAILURE); + } + + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + exit(EXIT_FAILURE); + } + + if (tlscredsid) { + if (sockpath) { + error_report("TLS is only supported with IPv4/IPv6"); + exit(EXIT_FAILURE); + } + if (device) { + error_report("TLS is not supported with a host device"); + exit(EXIT_FAILURE); + } + if (!export_name) { + /* Set the default NBD protocol export name, since + * we *must* use new style protocol for TLS */ + export_name = ""; + } + tlscreds = nbd_get_tls_creds(tlscredsid, &local_err); + if (local_err) { + error_report("Failed to get TLS creds %s", + error_get_pretty(local_err)); + exit(EXIT_FAILURE); + } } if (disconnect) { - fd = open(argv[optind], O_RDWR); - if (fd < 0) { - err(EXIT_FAILURE, "Cannot open %s", argv[optind]); + int nbdfd = open(argv[optind], O_RDWR); + if (nbdfd < 0) { + error_report("Cannot open %s: %s", argv[optind], + strerror(errno)); + exit(EXIT_FAILURE); } - nbd_disconnect(fd); + nbd_disconnect(nbdfd); - close(fd); + close(nbdfd); printf("%s disconnected\n", argv[optind]); @@ -643,7 +759,9 @@ int main(int argc, char **argv) int ret; if (qemu_pipe(stderr_fd) < 0) { - err(EXIT_FAILURE, "Error setting up communication pipe"); + error_report("Error setting up communication pipe: %s", + strerror(errno)); + exit(EXIT_FAILURE); } /* Now daemonize, but keep a communication channel open to @@ -651,7 +769,8 @@ int main(int argc, char **argv) */ pid = fork(); if (pid < 0) { - err(EXIT_FAILURE, "Failed to fork"); + error_report("Failed to fork: %s", strerror(errno)); + exit(EXIT_FAILURE); } else if (pid == 0) { close(stderr_fd[0]); ret = qemu_daemon(1, 0); @@ -659,7 +778,8 @@ int main(int argc, char **argv) /* Temporarily redirect stderr to the parent's pipe... */ dup2(stderr_fd[1], STDERR_FILENO); if (ret < 0) { - err(EXIT_FAILURE, "Failed to daemonize"); + error_report("Failed to daemonize: %s", strerror(errno)); + exit(EXIT_FAILURE); } /* ... close the descriptor we inherited and go on. */ @@ -681,7 +801,9 @@ int main(int argc, char **argv) } } if (ret < 0) { - err(EXIT_FAILURE, "Cannot read from daemon"); + error_report("Cannot read from daemon: %s", + strerror(errno)); + exit(EXIT_FAILURE); } /* Usually the daemon should not print any message. @@ -696,6 +818,8 @@ int main(int argc, char **argv) snprintf(sockpath, 128, SOCKET_PATH, basename(device)); } + saddr = nbd_build_socket_address(sockpath, bindto, port); + if (qemu_init_main_loop(&local_err)) { error_report_err(local_err); exit(EXIT_FAILURE); @@ -703,19 +827,38 @@ int main(int argc, char **argv) bdrv_init(); atexit(bdrv_close_all); - if (fmt) { - options = qdict_new(); - qdict_put(options, "driver", qstring_from_str(fmt)); + srcpath = argv[optind]; + if (imageOpts) { + QemuOpts *opts; + if (fmt) { + error_report("--image-opts and -f are mutually exclusive"); + exit(EXIT_FAILURE); + } + opts = qemu_opts_parse_noisily(&file_opts, srcpath, true); + if (!opts) { + qemu_opts_reset(&file_opts); + exit(EXIT_FAILURE); + } + options = qemu_opts_to_qdict(opts, NULL); + qemu_opts_reset(&file_opts); + blk = blk_new_open(NULL, NULL, options, flags, &local_err); + } else { + if (fmt) { + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str(fmt)); + } + blk = blk_new_open(srcpath, NULL, options, flags, &local_err); } - srcpath = argv[optind]; - blk = blk_new_open("hda", srcpath, NULL, options, flags, &local_err); if (!blk) { - errx(EXIT_FAILURE, "Failed to blk_new_open '%s': %s", argv[optind], - error_get_pretty(local_err)); + error_reportf_err(local_err, "Failed to blk_new_open '%s': ", + argv[optind]); + exit(EXIT_FAILURE); } bs = blk_bs(blk); + blk_set_enable_write_cache(blk, !writethrough); + if (sn_opts) { ret = bdrv_snapshot_load_tmp(bs, qemu_opt_get(sn_opts, SNAPSHOT_OPT_ID), @@ -726,40 +869,42 @@ int main(int argc, char **argv) &local_err); } if (ret < 0) { - errno = -ret; - err(EXIT_FAILURE, - "Failed to load snapshot: %s", - error_get_pretty(local_err)); + error_reportf_err(local_err, "Failed to load snapshot: "); + exit(EXIT_FAILURE); } bs->detect_zeroes = detect_zeroes; fd_size = blk_getlength(blk); if (fd_size < 0) { - errx(EXIT_FAILURE, "Failed to determine the image length: %s", - strerror(-fd_size)); + error_report("Failed to determine the image length: %s", + strerror(-fd_size)); + exit(EXIT_FAILURE); } if (partition != -1) { ret = find_partition(blk, partition, &dev_offset, &fd_size); if (ret < 0) { - errno = -ret; - err(EXIT_FAILURE, "Could not find partition %d", partition); + error_report("Could not find partition %d: %s", partition, + strerror(-ret)); + exit(EXIT_FAILURE); } } exp = nbd_export_new(blk, dev_offset, fd_size, nbdflags, nbd_export_closed, &local_err); if (!exp) { - errx(EXIT_FAILURE, "%s", error_get_pretty(local_err)); + error_report_err(local_err); + exit(EXIT_FAILURE); } - - if (sockpath) { - fd = unix_socket_incoming(sockpath); - } else { - fd = tcp_socket_incoming(bindto, port); + if (export_name) { + nbd_export_set_name(exp, export_name); + newproto = true; } - if (fd < 0) { + server_ioc = qio_channel_socket_new(); + if (qio_channel_socket_listen_sync(server_ioc, saddr, &local_err) < 0) { + object_unref(OBJECT(server_ioc)); + error_report_err(local_err); return 1; } @@ -768,21 +913,22 @@ int main(int argc, char **argv) ret = pthread_create(&client_thread, NULL, nbd_client_thread, device); if (ret != 0) { - errx(EXIT_FAILURE, "Failed to create client thread: %s", - strerror(ret)); + error_report("Failed to create client thread: %s", strerror(ret)); + exit(EXIT_FAILURE); } } else { /* Shut up GCC warnings. */ memset(&client_thread, 0, sizeof(client_thread)); } - server_fd = fd; - nbd_update_server_fd_handler(fd); + nbd_update_server_watch(); /* now when the initialization is (almost) complete, chdir("/") * to free any busy filesystems */ if (chdir("/") < 0) { - err(EXIT_FAILURE, "Could not chdir to root directory"); + error_report("Could not chdir to root directory: %s", + strerror(errno)); + exit(EXIT_FAILURE); } state = RUNNING; diff --git a/qemu/qemu-nbd.texi b/qemu/qemu-nbd.texi index 46fd483eb..9f2334345 100644 --- a/qemu/qemu-nbd.texi +++ b/qemu/qemu-nbd.texi @@ -1,68 +1,97 @@ @example @c man begin SYNOPSIS -usage: qemu-nbd [OPTION]... @var{filename} +@command{qemu-nbd} [OPTION]... @var{filename} + +@command{qemu-nbd} @option{-d} @var{dev} @c man end @end example @c man begin DESCRIPTION -Export QEMU disk image using NBD protocol. +Export a QEMU disk image using the NBD protocol. @c man end @c man begin OPTIONS +@var{filename} is a disk image filename, or a set of block +driver options if @var{--image-opts} is specified. + +@var{dev} is an NBD device. + @table @option -@item @var{filename} - is a disk image filename +@item --object type,id=@var{id},...props... +Define a new instance of the @var{type} object class identified by @var{id}. +See the @code{qemu(1)} manual page for full details of the properties +supported. The common object types that it makes sense to define are the +@code{secret} object, which is used to supply passwords and/or encryption +keys, and the @code{tls-creds} object, which is used to supply TLS +credentials for the qemu-nbd server. @item -p, --port=@var{port} - port to listen on (default @samp{10809}) +The TCP port to listen on (default @samp{10809}) @item -o, --offset=@var{offset} - offset into the image +The offset into the image @item -b, --bind=@var{iface} - interface to bind to (default @samp{0.0.0.0}) +The interface to bind to (default @samp{0.0.0.0}) @item -k, --socket=@var{path} - Use a unix socket with path @var{path} -@item -f, --format=@var{format} - Set image format as @var{format} +Use a unix socket with path @var{path} +@item --image-opts +Treat @var{filename} as a set of image options, instead of a plain +filename. If this flag is specified, the @var{-f} flag should +not be used, instead the '@code{format=}' option should be set. +@item -f, --format=@var{fmt} +Force the use of the block driver for format @var{fmt} instead of +auto-detecting @item -r, --read-only - export read-only +Export the disk as read-only @item -P, --partition=@var{num} - only expose partition @var{num} +Only expose partition @var{num} @item -s, --snapshot - use @var{filename} as an external snapshot, create a temporary - file with backing_file=@var{filename}, redirect the write to - the temporary one +Use @var{filename} as an external snapshot, create a temporary +file with backing_file=@var{filename}, redirect the write to +the temporary one @item -l, --load-snapshot=@var{snapshot_param} - load an internal snapshot inside @var{filename} and export it - as an read-only device, @var{snapshot_param} format is - 'snapshot.id=[ID],snapshot.name=[NAME]' or '[ID_OR_NAME]' +Load an internal snapshot inside @var{filename} and export it +as an read-only device, @var{snapshot_param} format is +'snapshot.id=[ID],snapshot.name=[NAME]' or '[ID_OR_NAME]' @item -n, --nocache @itemx --cache=@var{cache} - set cache mode to be used with the file. See the documentation of - the emulator's @code{-drive cache=...} option for allowed values. +The cache mode to be used with the file. See the documentation of +the emulator's @code{-drive cache=...} option for allowed values. @item --aio=@var{aio} - choose asynchronous I/O mode between @samp{threads} (the default) - and @samp{native} (Linux only). +Set the asynchronous I/O mode between @samp{threads} (the default) +and @samp{native} (Linux only). @item --discard=@var{discard} - toggles whether @dfn{discard} (also known as @dfn{trim} or @dfn{unmap}) - requests are ignored or passed to the filesystem. The default is no - (@samp{--discard=ignore}). +Control whether @dfn{discard} (also known as @dfn{trim} or @dfn{unmap}) +requests are ignored or passed to the filesystem. @var{discard} is one of +@samp{ignore} (or @samp{off}), @samp{unmap} (or @samp{on}). The default is +@samp{ignore}. +@item --detect-zeroes=@var{detect-zeroes} +Control the automatic conversion of plain zero writes by the OS to +driver-specific optimized zero write commands. @var{detect-zeroes} is one of +@samp{off}, @samp{on} or @samp{unmap}. @samp{unmap} +converts a zero write to an unmap operation and can only be used if +@var{discard} is set to @samp{unmap}. The default is @samp{off}. @item -c, --connect=@var{dev} - connect @var{filename} to NBD device @var{dev} +Connect @var{filename} to NBD device @var{dev} @item -d, --disconnect - disconnect the specified device +Disconnect the device @var{dev} @item -e, --shared=@var{num} - device can be shared by @var{num} clients (default @samp{1}) -@item -f, --format=@var{fmt} - force block driver for format @var{fmt} instead of auto-detecting +Allow up to @var{num} clients to share the device (default @samp{1}) @item -t, --persistent - don't exit on the last connection +Don't exit on the last connection +@item -x NAME, --export-name=NAME +Set the NBD volume export name. This switches the server to use +the new style NBD protocol negotiation +@item --tls-creds=ID +Enable mandatory TLS encryption for the server by setting the ID +of the TLS credentials object previously created with the --object +option. @item -v, --verbose - display extra debugging information +Display extra debugging information @item -h, --help - display this help and exit +Display this help and exit @item -V, --version - output version information and exit +Display version information and exit @end table @c man end @@ -79,7 +108,7 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. @c man end @c man begin SEEALSO -qemu-img(1) +qemu(1), qemu-img(1) @c man end @end ignore diff --git a/qemu/qemu-options.hx b/qemu/qemu-options.hx index 77f5853d5..6106520c5 100644 --- a/qemu/qemu-options.hx +++ b/qemu/qemu-options.hx @@ -33,14 +33,18 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ " property accel=accel1[:accel2[:...]] selects accelerator\n" " supported accelerators are kvm, xen, tcg (default: tcg)\n" " kernel_irqchip=on|off controls accelerated irqchip support\n" + " kernel_irqchip=on|off|split controls accelerated irqchip support (default=off)\n" " vmport=on|off|auto controls emulation of vmport (default: auto)\n" " kvm_shadow_mem=size of KVM shadow MMU\n" " dump-guest-core=on|off include guest memory in a core dump (default=on)\n" " mem-merge=on|off controls memory merge support (default: on)\n" " iommu=on|off controls emulated Intel IOMMU (VT-d) support (default=off)\n" + " igd-passthru=on|off controls IGD GFX passthrough support (default=off)\n" " aes-key-wrap=on|off controls support for AES key wrapping (default=on)\n" " dea-key-wrap=on|off controls support for DEA key wrapping (default=on)\n" - " suppress-vmdesc=on|off disables self-describing migration (default=off)\n", + " 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", QEMU_ARCH_ALL) STEXI @item -machine [type=]@var{name}[,prop=@var{value}[,...]] @@ -54,7 +58,9 @@ kvm, xen, or tcg can be available. By default, tcg is used. If there is more than one accelerator specified, the next one is used if the previous one fails to initialize. @item kernel_irqchip=on|off -Enables in-kernel irqchip support for the chosen accelerator when available. +Controls in-kernel irqchip support for the chosen accelerator when available. +@item gfx_passthru=on|off +Enables IGD GFX passthrough support for the chosen machine when available. @item vmport=on|off|auto Enables emulation of VMWare IO port, for vmmouse etc. auto says to select the value based on accel. For accel=xen the default is off otherwise the default @@ -77,6 +83,8 @@ execution of AES cryptographic functions. The default is on. Enables or disables DEA key wrapping support on s390-ccw hosts. This feature controls whether DEA wrapping keys will be created to allow execution of DEA cryptographic functions. The default is on. +@item nvdimm=on|off +Enables or disables NVDIMM support. The default is off. @end table ETEXI @@ -378,6 +386,58 @@ Add device @var{driver}. @var{prop}=@var{value} sets driver properties. Valid properties depend on the driver. To get help on possible drivers and properties, use @code{-device help} and @code{-device @var{driver},help}. + +Some drivers are: +@item -device ipmi-bmc-sim,id=@var{id}[,slave_addr=@var{val}] + +Add an IPMI BMC. This is a simulation of a hardware management +interface processor that normally sits on a system. It provides +a watchdog and the ability to reset and power control the system. +You need to connect this to an IPMI interface to make it useful + +The IPMI slave address to use for the BMC. The default is 0x20. +This address is the BMC's address on the I2C network of management +controllers. If you don't know what this means, it is safe to ignore +it. + +@item -device ipmi-bmc-extern,id=@var{id},chardev=@var{id}[,slave_addr=@var{val}] + +Add a connection to an external IPMI BMC simulator. Instead of +locally emulating the BMC like the above item, instead connect +to an external entity that provides the IPMI services. + +A connection is made to an external BMC simulator. If you do this, it +is strongly recommended that you use the "reconnect=" chardev option +to reconnect to the simulator if the connection is lost. Note that if +this is not used carefully, it can be a security issue, as the +interface has the ability to send resets, NMIs, and power off the VM. +It's best if QEMU makes a connection to an external simulator running +on a secure port on localhost, so neither the simulator nor QEMU is +exposed to any outside network. + +See the "lanserv/README.vm" file in the OpenIPMI library for more +details on the external interface. + +@item -device isa-ipmi-kcs,bmc=@var{id}[,ioport=@var{val}][,irq=@var{val}] + +Add a KCS IPMI interafce on the ISA bus. This also adds a +corresponding ACPI and SMBIOS entries, if appropriate. + +@table @option +@item bmc=@var{id} +The BMC to connect to, one of ipmi-bmc-sim or ipmi-bmc-extern above. +@item ioport=@var{val} +Define the I/O address of the interface. The default is 0xca0 for KCS. +@item irq=@var{val} +Define the interrupt to use. The default is 5. To disable interrupts, +set this to 0. +@end table + +@item -device isa-ipmi-bt,bmc=@var{id}[,ioport=@var{val}][,irq=@var{val}] + +Like the KCS interface, but defines a BT interface. The default port is +0xe4 and the default interrupt is 5. + ETEXI DEF("name", HAS_ARG, QEMU_OPTION_name, @@ -992,6 +1052,7 @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice, " [,streaming-video=[off|all|filter]][,disable-copy-paste]\n" " [,disable-agent-file-xfer][,agent-mouse=[on|off]]\n" " [,playback-compression=[on|off]][,seamless-migration=[on|off]]\n" + " [,gl=[on|off]]\n" " enable spice\n" " at least one of {port, tls-port} is mandatory\n", QEMU_ARCH_ALL) @@ -1083,6 +1144,9 @@ Enable/disable audio stream compression (using celt 0.5.1). Default is on. @item seamless-migration=[on|off] Enable/disable spice seamless migration. Default is off. +@item gl=[on|off] +Enable/disable OpenGL context. Default is off. + @end table ETEXI @@ -1214,8 +1278,9 @@ By definition the Websocket port is 5700+@var{display}. If @var{host} is specified connections will only be allowed from this host. As an alternative the Websocket port could be specified by using @code{websocket}=@var{port}. -TLS encryption for the Websocket connection is supported if the required -certificates are specified with the VNC option @option{x509}. +If no TLS credentials are provided, the websocket connection runs in +unencrypted mode. If TLS credentials are provided, the websocket connection +requires encrypted client connections. @item password @@ -1236,6 +1301,20 @@ date and time). You can also use keywords "now" or "never" for the expiration time to allow password to expire immediately or never expire. +@item tls-creds=@var{ID} + +Provides the ID of a set of TLS credentials to use to secure the +VNC server. They will apply to both the normal VNC server socket +and the websocket socket (if enabled). Setting TLS credentials +will cause the VNC server socket to enable the VeNCrypt auth +mechanism. The credentials should have been previously created +using the @option{-object tls-creds} argument. + +The @option{tls-creds} parameter obsoletes the @option{tls}, +@option{x509}, and @option{x509verify} options, and as such +it is not permitted to set both new and old type options at +the same time. + @item tls Require that client use TLS when communicating with the VNC server. This @@ -1243,6 +1322,9 @@ uses anonymous TLS credentials so is susceptible to a man-in-the-middle attack. It is recommended that this option be combined with either the @option{x509} or @option{x509verify} options. +This option is now deprecated in favor of using the @option{tls-creds} +argument. + @item x509=@var{/path/to/certificate/dir} Valid if @option{tls} is specified. Require that x509 credentials are used @@ -1252,6 +1334,9 @@ to provide authentication of the client when this is used. The path following this option specifies where the x509 certificates are to be loaded from. See the @ref{vnc_security} section for details on generating certificates. +This option is now deprecated in favour of using the @option{tls-creds} +argument. + @item x509verify=@var{/path/to/certificate/dir} Valid if @option{tls} is specified. Require that x509 credentials are used @@ -1265,6 +1350,9 @@ path following this option specifies where the x509 certificates are to be loaded from. See the @ref{vnc_security} section for details on generating certificates. +This option is now deprecated in favour of using the @option{tls-creds} +argument. + @item sasl Require that the client use SASL to authenticate with the VNC server. @@ -1389,6 +1477,10 @@ ACPI headers (possible overridden by other options). For data=, only data portion of the table is used, all header information is specified in the command line. +If a SLIC table is supplied to QEMU, then the SLIC's oem_id and oem_table_id +fields will override the same in the RSDT and the FADT (a.k.a. FACP), in order +to ensure the field matches required by the Microsoft SLIC spec and the ACPI +spec. ETEXI DEF("smbios", HAS_ARG, QEMU_OPTION_smbios, @@ -1412,7 +1504,7 @@ DEF("smbios", HAS_ARG, QEMU_OPTION_smbios, "-smbios type=17[,loc_pfx=str][,bank=str][,manufacturer=str][,serial=str]\n" " [,asset=str][,part=str][,speed=%d]\n" " specify SMBIOS type 17 fields\n", - QEMU_ARCH_I386) + QEMU_ARCH_I386 | QEMU_ARCH_ARM) STEXI @item -smbios file=@var{binary} @findex -smbios @@ -1459,8 +1551,10 @@ DEF("smb", HAS_ARG, QEMU_OPTION_smb, "", QEMU_ARCH_ALL) DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, #ifdef CONFIG_SLIRP - "-netdev user,id=str[,net=addr[/mask]][,host=addr][,restrict=on|off]\n" - " [,hostname=host][,dhcpstart=addr][,dns=addr][,dnssearch=domain][,tftp=dir]\n" + "-netdev user,id=str[,ipv4[=on|off]][,net=addr[/mask]][,host=addr]\n" + " [,ipv6[=on|off]][,ipv6-net=addr[/int]][,ipv6-host=addr]\n" + " [,restrict=on|off][,hostname=host][,dhcpstart=addr]\n" + " [,dns=addr][,ipv6-dns=addr][,dnssearch=domain][,tftp=dir]\n" " [,bootfile=f][,hostfwd=rule][,guestfwd=rule]" #ifndef _WIN32 "[,smb=dir[,smbserver=addr]]\n" @@ -1608,6 +1702,9 @@ Connect user mode stack to VLAN @var{n} (@var{n} = 0 is the default). @itemx name=@var{name} Assign symbolic name for use in monitor commands. +@option{ipv4} and @option{ipv6} specify that either IPv4 or IPv6 must +be enabled. If neither is specified both protocols are enabled. + @item net=@var{addr}[/@var{mask}] Set IP network address the guest will see. Optionally specify the netmask, either in the form a.b.c.d or as number of valid top-most bits. Default is @@ -1617,6 +1714,16 @@ either in the form a.b.c.d or as number of valid top-most bits. Default is Specify the guest-visible address of the host. Default is the 2nd IP in the guest network, i.e. x.x.x.2. +@item ipv6-net=@var{addr}[/@var{int}] +Set IPv6 network address the guest will see (default is fec0::/64). The +network prefix is given in the usual hexadecimal IPv6 address +notation. The prefix size is optional, and is given as the number of +valid top-most bits (default is 64). + +@item ipv6-host=@var{addr} +Specify the guest-visible IPv6 address of the host. Default is the 2nd IPv6 in +the guest network, i.e. xxxx::2. + @item restrict=on|off If this option is enabled, the guest will be isolated, i.e. it will not be able to contact the host and no guest IP packets will be routed over the host @@ -1634,6 +1741,11 @@ Specify the guest-visible address of the virtual nameserver. The address must be different from the host address. Default is the 3rd IP in the guest network, i.e. x.x.x.3. +@item ipv6-dns=@var{addr} +Specify the guest-visible address of the IPv6 virtual nameserver. The address +must be different from the host address. Default is the 3rd IP in the guest +network, i.e. xxxx::3. + @item dnssearch=@var{domain} Provides an entry for the domain-search list sent by the built-in DHCP server. More than one domain suffix can be transmitted by specifying @@ -1963,13 +2075,14 @@ The hubport netdev lets you connect a NIC to a QEMU "vlan" instead of a single netdev. @code{-net} and @code{-device} with parameter @option{vlan} create the required hub automatically. -@item -netdev vhost-user,chardev=@var{id}[,vhostforce=on|off] +@item -netdev vhost-user,chardev=@var{id}[,vhostforce=on|off][,queues=n] Establish a vhost-user netdev, backed by a chardev @var{id}. The chardev should be a unix domain socket backed one. The vhost-user uses a specifically defined protocol to pass vhost ioctl replacement messages to an application on the other end of the socket. On non-MSIX guests, the feature can be forced with -@var{vhostforce}. +@var{vhostforce}. Use 'queues=@var{n}' to specify the number of queues to +be created for multiqueue vhost-user. Example: @example @@ -1984,6 +2097,7 @@ qemu -m 512 -object memory-backend-file,id=mem,size=512M,mem-path=/hugetlbfs,sha Dump network traffic on VLAN @var{n} to file @var{file} (@file{qemu-vlan0.pcap} by default). At most @var{len} bytes (64k by default) per packet are stored. The file format is libpcap, so it can be analyzed with tools such as tcpdump or Wireshark. +Note: For devices created with '-netdev', use '-object filter-dump,...' instead. @item -net none Indicate that no network devices should be configured. It is used to @@ -2004,40 +2118,43 @@ The general form of a character device option is: ETEXI DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, - "-chardev null,id=id[,mux=on|off]\n" + "-chardev null,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" "-chardev socket,id=id[,host=host],port=port[,to=to][,ipv4][,ipv6][,nodelay][,reconnect=seconds]\n" - " [,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off] (tcp)\n" - "-chardev socket,id=id,path=path[,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off] (unix)\n" + " [,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off]\n" + " [,logfile=PATH][,logappend=on|off][,tls-creds=ID] (tcp)\n" + "-chardev socket,id=id,path=path[,server][,nowait][,telnet][,reconnect=seconds]\n" + " [,mux=on|off][,logfile=PATH][,logappend=on|off] (unix)\n" "-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n" " [,localport=localport][,ipv4][,ipv6][,mux=on|off]\n" - "-chardev msmouse,id=id[,mux=on|off]\n" + " [,logfile=PATH][,logappend=on|off]\n" + "-chardev msmouse,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" "-chardev vc,id=id[[,width=width][,height=height]][[,cols=cols][,rows=rows]]\n" - " [,mux=on|off]\n" - "-chardev ringbuf,id=id[,size=size]\n" - "-chardev file,id=id,path=path[,mux=on|off]\n" - "-chardev pipe,id=id,path=path[,mux=on|off]\n" + " [,mux=on|off][,logfile=PATH][,logappend=on|off]\n" + "-chardev ringbuf,id=id[,size=size][,logfile=PATH][,logappend=on|off]\n" + "-chardev file,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" + "-chardev pipe,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" #ifdef _WIN32 - "-chardev console,id=id[,mux=on|off]\n" - "-chardev serial,id=id,path=path[,mux=on|off]\n" + "-chardev console,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" + "-chardev serial,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" #else - "-chardev pty,id=id[,mux=on|off]\n" - "-chardev stdio,id=id[,mux=on|off][,signal=on|off]\n" + "-chardev pty,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" + "-chardev stdio,id=id[,mux=on|off][,signal=on|off][,logfile=PATH][,logappend=on|off]\n" #endif #ifdef CONFIG_BRLAPI - "-chardev braille,id=id[,mux=on|off]\n" + "-chardev braille,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" #endif #if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) - "-chardev serial,id=id,path=path[,mux=on|off]\n" - "-chardev tty,id=id,path=path[,mux=on|off]\n" + "-chardev serial,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" + "-chardev tty,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" #endif #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) - "-chardev parallel,id=id,path=path[,mux=on|off]\n" - "-chardev parport,id=id,path=path[,mux=on|off]\n" + "-chardev parallel,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" + "-chardev parport,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" #endif #if defined(CONFIG_SPICE) - "-chardev spicevmc,id=id,name=name[,debug=debug]\n" - "-chardev spiceport,id=id,name=name[,debug=debug]\n" + "-chardev spicevmc,id=id,name=name[,debug=debug][,logfile=PATH][,logappend=on|off]\n" + "-chardev spiceport,id=id,name=name[,debug=debug][,logfile=PATH][,logappend=on|off]\n" #endif , QEMU_ARCH_ALL ) @@ -2070,16 +2187,62 @@ All devices must have an id, which can be any string up to 127 characters long. It is used to uniquely identify this device in other command line directives. A character device may be used in multiplexing mode by multiple front-ends. -The key sequence of @key{Control-a} and @key{c} will rotate the input focus -between attached front-ends. Specify @option{mux=on} to enable this mode. +Specify @option{mux=on} to enable this mode. +A multiplexer is a "1:N" device, and here the "1" end is your specified chardev +backend, and the "N" end is the various parts of QEMU that can talk to a chardev. +If you create a chardev with @option{id=myid} and @option{mux=on}, QEMU will +create a multiplexer with your specified ID, and you can then configure multiple +front ends to use that chardev ID for their input/output. Up to four different +front ends can be connected to a single multiplexed chardev. (Without +multiplexing enabled, a chardev can only be used by a single front end.) +For instance you could use this to allow a single stdio chardev to be used by +two serial ports and the QEMU monitor: -Options to each backend are described below. +@example +-chardev stdio,mux=on,id=char0 \ +-mon chardev=char0,mode=readline,default \ +-serial chardev:char0 \ +-serial chardev:char0 +@end example + +You can have more than one multiplexer in a system configuration; for instance +you could have a TCP port multiplexed between UART 0 and UART 1, and stdio +multiplexed between the QEMU monitor and a parallel port: + +@example +-chardev stdio,mux=on,id=char0 \ +-mon chardev=char0,mode=readline,default \ +-parallel chardev:char0 \ +-chardev tcp,...,mux=on,id=char1 \ +-serial chardev:char1 \ +-serial chardev:char1 +@end example + +When you're using a multiplexed character device, some escape sequences are +interpreted in the input. @xref{mux_keys, Keys in the character backend +multiplexer}. + +Note that some other command line options may implicitly create multiplexed +character backends; for instance @option{-serial mon:stdio} creates a +multiplexed stdio backend connected to the serial port and the QEMU monitor, +and @option{-nographic} also multiplexes the console and the monitor to +stdio. + +There is currently no support for multiplexing in the other direction +(where a single QEMU front end takes input and output from multiple chardevs). + +Every backend supports the @option{logfile} option, which supplies the path +to a file to record all data transmitted via the backend. The @option{logappend} +option controls whether the log file will be truncated or appended to when +opened. + +Further options to each backend are described below. @item -chardev null ,id=@var{id} A void device. This device will not emit any data, and will drop any data it receives. The null backend does not take any options. -@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet] [,reconnect=@var{seconds}] +@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet] [,reconnect=@var{seconds}] [,tls-creds=@var{id}] Create a two-way stream socket, which can be either a TCP or a unix socket. A unix socket will be created if @option{path} is specified. Behaviour is @@ -2097,6 +2260,11 @@ escape sequences. the remote end goes away. qemu will delay this many seconds and then attempt to reconnect. Zero disables reconnecting, and is the default. +@option{tls-creds} requests enablement of the TLS protocol for encryption, +and specifies the id of the TLS credentials to use for the handshake. The +credentials must be previously created with the @option{-object tls-creds} +argument. + TCP and unix socket options are given below: @table @option @@ -2696,13 +2864,32 @@ ETEXI DEF("fw_cfg", HAS_ARG, QEMU_OPTION_fwcfg, "-fw_cfg [name=],file=\n" - " add named fw_cfg entry from file\n", + " add named fw_cfg entry with contents from file\n" + "-fw_cfg [name=],string=\n" + " add named fw_cfg entry with contents from string\n", QEMU_ARCH_ALL) STEXI + @item -fw_cfg [name=]@var{name},file=@var{file} @findex -fw_cfg -Add named fw_cfg entry from file. @var{name} determines the name of -the entry in the fw_cfg file directory exposed to the guest. +Add named fw_cfg entry with contents from file @var{file}. + +@item -fw_cfg [name=]@var{name},string=@var{str} +Add named fw_cfg entry with contents from string @var{str}. + +The terminating NUL character of the contents of @var{str} will not be +included as part of the fw_cfg item data. To insert contents with +embedded NUL characters, you have to use the @var{file} parameter. + +The fw_cfg entries are passed by QEMU through to the guest. + +Example: +@example + -fw_cfg name=opt/com.mycompany/blob,file=./my_blob.bin +@end example +creates an fw_cfg entry named opt/com.mycompany/blob with contents +from ./my_blob.bin. + ETEXI DEF("serial", HAS_ARG, QEMU_OPTION_serial, \ @@ -2987,6 +3174,24 @@ STEXI Output log in @var{logfile} instead of to stderr ETEXI +DEF("dfilter", HAS_ARG, QEMU_OPTION_DFILTER, \ + "-dfilter range,.. filter debug output to range of addresses (useful for -d cpu,exec,etc..)\n", + QEMU_ARCH_ALL) +STEXI +@item -dfilter @var{range1}[,...] +@findex -dfilter +Filter debug output to that relevant to a range of target addresses. The filter +spec can be either @var{start}+@var{size}, @var{start}-@var{size} or +@var{start}..@var{end} where @var{start} @var{end} and @var{size} are the +addresses and sizes required. For example: +@example + -dfilter 0x8000..0x8fff,0xffffffc000080000+0x200,0xffffffc000060000-0x1000 +@end example +Will dump output for any code in the 0x1000 sized block starting at 0x8000 and +the 0x200 sized block starting at 0xffffffc000080000 and another 0x1000 sized +block starting at 0xffffffc00005f000. +ETEXI + DEF("L", HAS_ARG, QEMU_OPTION_L, \ "-L path set the directory for the BIOS, VGA BIOS and keymaps\n", QEMU_ARCH_ALL) @@ -3123,12 +3328,12 @@ re-inject them. ETEXI DEF("icount", HAS_ARG, QEMU_OPTION_icount, \ - "-icount [shift=N|auto][,align=on|off][,sleep=no]\n" \ + "-icount [shift=N|auto][,align=on|off][,sleep=on|off,rr=record|replay,rrfile=]\n" \ " enable virtual instruction counter with 2^N clock ticks per\n" \ " instruction, enable aligning the host and virtual clocks\n" \ " or disable real time cpu sleeping\n", QEMU_ARCH_ALL) STEXI -@item -icount [shift=@var{N}|auto] +@item -icount [shift=@var{N}|auto][,rr=record|replay,rrfile=@var{filename}] @findex -icount Enable virtual instruction counter. The virtual cpu will execute one instruction every 2^@var{N} ns of virtual time. If @code{auto} is specified @@ -3136,8 +3341,8 @@ then the virtual cpu speed will be automatically adjusted to keep virtual time within a few seconds of real time. When the virtual cpu is sleeping, the virtual time will advance at default -speed unless @option{sleep=no} is specified. -With @option{sleep=no}, the virtual time will jump to the next timer deadline +speed unless @option{sleep=on|off} is specified. +With @option{sleep=on|off}, the virtual time will jump to the next timer deadline instantly whenever the virtual cpu goes to sleep mode and will not advance if no timer is enabled. This behavior give deterministic execution times from the guest point of view. @@ -3147,7 +3352,7 @@ provide cycle accurate emulation. Modern CPUs contain superscalar out of order cores with complex cache hierarchies. The number of instructions executed often has little or no correlation with actual performance. -@option{align=on} will activate the delay algorithm which will try to +@option{align=on} will activate the delay algorithm which will try to synchronise the host clock and the virtual clock. The goal is to have a guest running at the real frequency imposed by the shift option. Whenever the guest clock is behind the host clock and if @@ -3157,6 +3362,10 @@ Currently this option does not work when @option{shift} is @code{auto}. Note: The sync algorithm will work for those shift values for which the guest clock runs ahead of the host clock. Typically this happens when the shift value is high (how high depends on the host machine). + +When @option{rr} option is specified deterministic record/replay is enabled. +Replay log is written into @var{filename} file in record mode and +read from this file in replay mode. ETEXI DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \ @@ -3437,7 +3646,7 @@ config files on @var{sysconfdir}, but won't make it skip the QEMU-provided confi files from @var{datadir}. ETEXI DEF("trace", HAS_ARG, QEMU_OPTION_trace, - "-trace [events=][,file=]\n" + "-trace [[enable=]][,events=][,file=]\n" " specify tracing options\n", QEMU_ARCH_ALL) STEXI @@ -3449,15 +3658,25 @@ HXCOMM HX does not support conditional compilation of text. Specify tracing options. @table @option +@item [enable=]@var{pattern} +Immediately enable events matching @var{pattern}. +The file must contain one event name (as listed in the @file{trace-events} file) +per line; globbing patterns are accepted too. This option is only +available if QEMU has been compiled with the @var{simple}, @var{stderr} +or @var{ftrace} tracing backend. To specify multiple events or patterns, +specify the @option{-trace} option multiple times. + +Use @code{-trace help} to print a list of names of trace points. + @item events=@var{file} Immediately enable events listed in @var{file}. -The file must contain one event name (as listed in the @var{trace-events} file) -per line. -This option is only available if QEMU has been compiled with -either @var{simple} or @var{stderr} tracing backend. +The file must contain one event name (as listed in the @file{trace-events} file) +per line; globbing patterns are accepted too. This option is only +available if QEMU has been compiled with the @var{simple}, @var{stderr} or +@var{ftrace} tracing backend. + @item file=@var{file} Log output traces to @var{file}. - This option is only available if QEMU has been compiled with the @var{simple} tracing backend. @end table @@ -3510,7 +3729,7 @@ DEF("dump-vmstate", HAS_ARG, QEMU_OPTION_dump_vmstate, " Output vmstate information in JSON format to file.\n" " Use the scripts/vmstate-static-checker.py file to\n" " check for possible regressions in migration code\n" - " by comparing two such vmstate dumps.", + " by comparing two such vmstate dumps.\n", QEMU_ARCH_ALL) STEXI @item -dump-vmstate @var{file} @@ -3568,6 +3787,176 @@ the @option{virtio-rng} device. The @option{chardev} parameter is the unique ID of a character device backend that provides the connection to the RNG daemon. +@item -object tls-creds-anon,id=@var{id},endpoint=@var{endpoint},dir=@var{/path/to/cred/dir},verify-peer=@var{on|off} + +Creates a TLS anonymous credentials object, which can be used to provide +TLS support on network backends. The @option{id} parameter is a unique +ID which network backends will use to access the credentials. The +@option{endpoint} is either @option{server} or @option{client} depending +on whether the QEMU network backend that uses the credentials will be +acting as a client or as a server. If @option{verify-peer} is enabled +(the default) then once the handshake is completed, the peer credentials +will be verified, though this is a no-op for anonymous credentials. + +The @var{dir} parameter tells QEMU where to find the credential +files. For server endpoints, this directory may contain a file +@var{dh-params.pem} providing diffie-hellman parameters to use +for the TLS server. If the file is missing, QEMU will generate +a set of DH parameters at startup. This is a computationally +expensive operation that consumes random pool entropy, so it is +recommended that a persistent set of parameters be generated +upfront and saved. + +@item -object tls-creds-x509,id=@var{id},endpoint=@var{endpoint},dir=@var{/path/to/cred/dir},verify-peer=@var{on|off},passwordid=@var{id} + +Creates a TLS anonymous credentials object, which can be used to provide +TLS support on network backends. The @option{id} parameter is a unique +ID which network backends will use to access the credentials. The +@option{endpoint} is either @option{server} or @option{client} depending +on whether the QEMU network backend that uses the credentials will be +acting as a client or as a server. If @option{verify-peer} is enabled +(the default) then once the handshake is completed, the peer credentials +will be verified. With x509 certificates, this implies that the clients +must be provided with valid client certificates too. + +The @var{dir} parameter tells QEMU where to find the credential +files. For server endpoints, this directory may contain a file +@var{dh-params.pem} providing diffie-hellman parameters to use +for the TLS server. If the file is missing, QEMU will generate +a set of DH parameters at startup. This is a computationally +expensive operation that consumes random pool entropy, so it is +recommended that a persistent set of parameters be generated +upfront and saved. + +For x509 certificate credentials the directory will contain further files +providing the x509 certificates. The certificates must be stored +in PEM format, in filenames @var{ca-cert.pem}, @var{ca-crl.pem} (optional), +@var{server-cert.pem} (only servers), @var{server-key.pem} (only servers), +@var{client-cert.pem} (only clients), and @var{client-key.pem} (only clients). + +For the @var{server-key.pem} and @var{client-key.pem} files which +contain sensitive private keys, it is possible to use an encrypted +version by providing the @var{passwordid} parameter. This provides +the ID of a previously created @code{secret} object containing the +password for decryption. + +@item -object filter-buffer,id=@var{id},netdev=@var{netdevid},interval=@var{t}[,queue=@var{all|rx|tx}][,status=@var{on|off}] + +Interval @var{t} can't be 0, this filter batches the packet delivery: all +packets arriving in a given interval on netdev @var{netdevid} are delayed +until the end of the interval. Interval is in microseconds. +@option{status} is optional that indicate whether the netfilter is +on (enabled) or off (disabled), the default status for netfilter will be 'on'. + +queue @var{all|rx|tx} is an option that can be applied to any netfilter. + +@option{all}: the filter is attached both to the receive and the transmit + queue of the netdev (default). + +@option{rx}: the filter is attached to the receive queue of the netdev, + where it will receive packets sent to the netdev. + +@option{tx}: the filter is attached to the transmit queue of the netdev, + where it will receive packets sent by the netdev. + +@item -object filter-mirror,id=@var{id},netdev=@var{netdevid},outdev=@var{chardevid}[,queue=@var{all|rx|tx}] + +filter-mirror on netdev @var{netdevid},mirror net packet to chardev +@var{chardevid} + +@item -object filter-redirector,id=@var{id},netdev=@var{netdevid},indev=@var{chardevid}, +outdev=@var{chardevid}[,queue=@var{all|rx|tx}] + +filter-redirector on netdev @var{netdevid},redirect filter's net packet to chardev +@var{chardevid},and redirect indev's packet to filter. +Create a filter-redirector we need to differ outdev id from indev id, id can not +be the same. we can just use indev or outdev, but at least one of indev or outdev +need to be specified. + +@item -object filter-dump,id=@var{id},netdev=@var{dev},file=@var{filename}][,maxlen=@var{len}] + +Dump the network traffic on netdev @var{dev} to the file specified by +@var{filename}. At most @var{len} bytes (64k by default) per packet are stored. +The file format is libpcap, so it can be analyzed with tools such as tcpdump +or Wireshark. + +@item -object secret,id=@var{id},data=@var{string},format=@var{raw|base64}[,keyid=@var{secretid},iv=@var{string}] +@item -object secret,id=@var{id},file=@var{filename},format=@var{raw|base64}[,keyid=@var{secretid},iv=@var{string}] + +Defines a secret to store a password, encryption key, or some other sensitive +data. The sensitive data can either be passed directly via the @var{data} +parameter, or indirectly via the @var{file} parameter. Using the @var{data} +parameter is insecure unless the sensitive data is encrypted. + +The sensitive data can be provided in raw format (the default), or base64. +When encoded as JSON, the raw format only supports valid UTF-8 characters, +so base64 is recommended for sending binary data. QEMU will convert from +which ever format is provided to the format it needs internally. eg, an +RBD password can be provided in raw format, even though it will be base64 +encoded when passed onto the RBD sever. + +For added protection, it is possible to encrypt the data associated with +a secret using the AES-256-CBC cipher. Use of encryption is indicated +by providing the @var{keyid} and @var{iv} parameters. The @var{keyid} +parameter provides the ID of a previously defined secret that contains +the AES-256 decryption key. This key should be 32-bytes long and be +base64 encoded. The @var{iv} parameter provides the random initialization +vector used for encryption of this particular secret and should be a +base64 encrypted string of the 16-byte IV. + +The simplest (insecure) usage is to provide the secret inline + +@example + + # $QEMU -object secret,id=sec0,data=letmein,format=raw + +@end example + +The simplest secure usage is to provide the secret via a file + + # echo -n "letmein" > mypasswd.txt + # $QEMU -object secret,id=sec0,file=mypasswd.txt,format=raw + +For greater security, AES-256-CBC should be used. To illustrate usage, +consider the openssl command line tool which can encrypt the data. Note +that when encrypting, the plaintext must be padded to the cipher block +size (32 bytes) using the standard PKCS#5/6 compatible padding algorithm. + +First a master key needs to be created in base64 encoding: + +@example + # openssl rand -base64 32 > key.b64 + # KEY=$(base64 -d key.b64 | hexdump -v -e '/1 "%02X"') +@end example + +Each secret to be encrypted needs to have a random initialization vector +generated. These do not need to be kept secret + +@example + # openssl rand -base64 16 > iv.b64 + # IV=$(base64 -d iv.b64 | hexdump -v -e '/1 "%02X"') +@end example + +The secret to be defined can now be encrypted, in this case we're +telling openssl to base64 encode the result, but it could be left +as raw bytes if desired. + +@example + # SECRET=$(echo -n "letmein" | + openssl enc -aes-256-cbc -a -K $KEY -iv $IV) +@end example + +When launching QEMU, create a master secret pointing to @code{key.b64} +and specify that to be used to decrypt the user password. Pass the +contents of @code{iv.b64} to the second secret + +@example + # $QEMU \ + -object secret,id=secmaster0,format=base64,file=key.b64 \ + -object secret,id=sec0,keyid=secmaster0,format=base64,\ + data=$SECRET,iv=$( +#include "qemu/osdep.h" #include #include "sysemu/seccomp.h" +/* For some architectures (notably ARM) cacheflush is not supported until + * libseccomp 2.2.3, but configure enforces that we are using a more recent + * version on those hosts, so it is OK for this check to be less strict. + */ +#if SCMP_VER_MAJOR >= 3 + #define HAVE_CACHEFLUSH +#elif SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR >= 2 + #define HAVE_CACHEFLUSH +#endif + struct QemuSeccompSyscall { int32_t num; uint8_t priority; @@ -237,7 +247,12 @@ static const struct QemuSeccompSyscall seccomp_whitelist[] = { { SCMP_SYS(fadvise64), 240 }, { SCMP_SYS(inotify_init1), 240 }, { SCMP_SYS(inotify_add_watch), 240 }, - { SCMP_SYS(mbind), 240 } + { SCMP_SYS(mbind), 240 }, + { SCMP_SYS(memfd_create), 240 }, +#ifdef HAVE_CACHEFLUSH + { SCMP_SYS(cacheflush), 240 }, +#endif + { SCMP_SYS(sysinfo), 240 }, }; int seccomp_start(void) diff --git a/qemu/qemu-tech.texi b/qemu/qemu-tech.texi index b6fcb2d61..bdb2285f4 100644 --- a/qemu/qemu-tech.texi +++ b/qemu/qemu-tech.texi @@ -320,7 +320,7 @@ SH4 @node QEMU compared to other emulators @section QEMU compared to other emulators -Like bochs [3], QEMU emulates an x86 CPU. But QEMU is much faster than +Like bochs [1], QEMU emulates an x86 CPU. But QEMU is much faster than bochs as it uses dynamic compilation. Bochs is closely tied to x86 PC emulation while QEMU can emulate several processors. @@ -333,25 +333,25 @@ than QEMU (in particular it does register allocation) but it is closely tied to an x86 host and target and has no support for precise exceptions and system emulation. -EM86 [4] is the closest project to user space QEMU (and QEMU still uses +EM86 [3] is the closest project to user space QEMU (and QEMU still uses some of its code, in particular the ELF file loader). EM86 was limited to an alpha host and used a proprietary and slow interpreter (the -interpreter part of the FX!32 Digital Win32 code translator [5]). +interpreter part of the FX!32 Digital Win32 code translator [4]). -TWIN [6] is a Windows API emulator like Wine. It is less accurate than -Wine but includes a protected mode x86 interpreter to launch x86 Windows -executables. Such an approach has greater potential because most of the -Windows API is executed natively but it is far more difficult to develop -because all the data structures and function parameters exchanged +TWIN from Willows Software was a Windows API emulator like Wine. It is less +accurate than Wine but includes a protected mode x86 interpreter to launch +x86 Windows executables. Such an approach has greater potential because most +of the Windows API is executed natively but it is far more difficult to +develop because all the data structures and function parameters exchanged between the API and the x86 code must be converted. -User mode Linux [7] was the only solution before QEMU to launch a +User mode Linux [5] was the only solution before QEMU to launch a Linux kernel as a process while not needing any host kernel patches. However, user mode Linux requires heavy kernel patches while QEMU accepts unpatched Linux kernels. The price to pay is that QEMU is slower. -The Plex86 [8] PC virtualizer is done in the same spirit as the now +The Plex86 [6] PC virtualizer is done in the same spirit as the now obsolete qemu-fast system emulator. It requires a patched Linux kernel to work (you cannot launch the same kernel on your PC), but the patches are really small. As it is a PC virtualizer (no emulation is @@ -359,13 +359,13 @@ done except for some privileged instructions), it has the potential of being faster than QEMU. The downside is that a complicated (and potentially unsafe) host kernel patch is needed. -The commercial PC Virtualizers (VMWare [9], VirtualPC [10], TwoOStwo -[11]) are faster than QEMU, but they all need specific, proprietary +The commercial PC Virtualizers (VMWare [7], VirtualPC [8]) are faster +than QEMU (without virtualization), but they all need specific, proprietary and potentially unsafe host drivers. Moreover, they are unable to provide cycle exact simulation as an emulator can. -VirtualBox [12], Xen [13] and KVM [14] are based on QEMU. QEMU-SystemC -[15] uses QEMU to simulate a system where some hardware devices are +VirtualBox [9], Xen [10] and KVM [11] are based on QEMU. QEMU-SystemC +[12] uses QEMU to simulate a system where some hardware devices are developed in SystemC. @node Portable dynamic translation @@ -385,7 +385,7 @@ ops (see @code{target-i386/translate.c}). Some optimizations can be performed at this stage, including liveness analysis and trivial constant expression evaluation. TCG ops are then implemented in the host CPU back end, also known as TCG target (see -@code{tcg/i386/tcg-target.c}). For more information, please take a +@code{tcg/i386/tcg-target.inc.c}). For more information, please take a look at @code{tcg/README}. @node Condition code optimisations @@ -608,64 +608,51 @@ way, it can be relocated at load time. @table @asis @item [1] -@url{http://citeseer.nj.nec.com/piumarta98optimizing.html}, Optimizing -direct threaded code by selective inlining (1998) by Ian Piumarta, Fabio -Riccardi. +@url{http://bochs.sourceforge.net/}, the Bochs IA-32 Emulator Project, +by Kevin Lawton et al. @item [2] -@url{http://developer.kde.org/~sewardj/}, Valgrind, an open-source -memory debugger for x86-GNU/Linux, by Julian Seward. +@url{http://www.valgrind.org/}, Valgrind, an open-source memory debugger +for GNU/Linux. @item [3] -@url{http://bochs.sourceforge.net/}, the Bochs IA-32 Emulator Project, -by Kevin Lawton et al. +@url{http://ftp.dreamtime.org/pub/linux/Linux-Alpha/em86/v0.2/docs/em86.html}, +the EM86 x86 emulator on Alpha-Linux. @item [4] -@url{http://www.cs.rose-hulman.edu/~donaldlf/em86/index.html}, the EM86 -x86 emulator on Alpha-Linux. - -@item [5] @url{http://www.usenix.org/publications/library/proceedings/usenix-nt97/@/full_papers/chernoff/chernoff.pdf}, DIGITAL FX!32: Running 32-Bit x86 Applications on Alpha NT, by Anton Chernoff and Ray Hookway. -@item [6] -@url{http://www.willows.com/}, Windows API library emulation from -Willows Software. - -@item [7] +@item [5] @url{http://user-mode-linux.sourceforge.net/}, The User-mode Linux Kernel. -@item [8] +@item [6] @url{http://www.plex86.org/}, The new Plex86 project. -@item [9] +@item [7] @url{http://www.vmware.com/}, The VMWare PC virtualizer. -@item [10] -@url{http://www.microsoft.com/windowsxp/virtualpc/}, +@item [8] +@url{https://www.microsoft.com/download/details.aspx?id=3702}, The VirtualPC PC virtualizer. -@item [11] -@url{http://www.twoostwo.org/}, -The TwoOStwo PC virtualizer. - -@item [12] +@item [9] @url{http://virtualbox.org/}, The VirtualBox PC virtualizer. -@item [13] +@item [10] @url{http://www.xen.org/}, The Xen hypervisor. -@item [14] -@url{http://kvm.qumranet.com/kvmwiki/Front_Page}, +@item [11] +@url{http://www.linux-kvm.org/}, Kernel Based Virtual Machine (KVM). -@item [15] +@item [12] @url{http://www.greensocs.com/projects/QEMUSystemC}, QEMU-SystemC, a hardware co-simulator. diff --git a/qemu/qemu-timer.c b/qemu/qemu-timer.c index 2463fe6f6..4441fe66f 100644 --- a/qemu/qemu-timer.c +++ b/qemu/qemu-timer.c @@ -22,8 +22,11 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qemu/timer.h" +#include "sysemu/replay.h" +#include "sysemu/sysemu.h" #ifdef CONFIG_POSIX #include @@ -391,7 +394,9 @@ static bool timer_mod_ns_locked(QEMUTimerList *timer_list, static void timerlist_rearm(QEMUTimerList *timer_list) { /* Interrupt execution to force deadline recalculation. */ - qemu_clock_warp(timer_list->clock->type); + if (timer_list->clock->type == QEMU_CLOCK_VIRTUAL) { + qemu_start_warp_timer(); + } timerlist_notify(timer_list); } @@ -477,10 +482,31 @@ bool timerlist_run_timers(QEMUTimerList *timer_list) void *opaque; qemu_event_reset(&timer_list->timers_done_ev); - if (!timer_list->clock->enabled) { + if (!timer_list->clock->enabled || !timer_list->active_timers) { goto out; } + switch (timer_list->clock->type) { + case QEMU_CLOCK_REALTIME: + break; + default: + case QEMU_CLOCK_VIRTUAL: + if (!replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL)) { + goto out; + } + break; + case QEMU_CLOCK_HOST: + if (!replay_checkpoint(CHECKPOINT_CLOCK_HOST)) { + goto out; + } + break; + case QEMU_CLOCK_VIRTUAL_RT: + if (!replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL_RT)) { + goto out; + } + break; + } + current_time = qemu_clock_get_ns(timer_list->clock->type); for(;;) { qemu_mutex_lock(&timer_list->active_timers_lock); @@ -544,11 +570,17 @@ int64_t timerlistgroup_deadline_ns(QEMUTimerListGroup *tlg) { int64_t deadline = -1; QEMUClockType type; + bool play = replay_mode == REPLAY_MODE_PLAY; for (type = 0; type < QEMU_CLOCK_MAX; type++) { - if (qemu_clock_use_for_deadline(tlg->tl[type]->clock->type)) { - deadline = qemu_soonest_timeout(deadline, - timerlist_deadline_ns( - tlg->tl[type])); + if (qemu_clock_use_for_deadline(type)) { + if (!play || type == QEMU_CLOCK_REALTIME) { + deadline = qemu_soonest_timeout(deadline, + timerlist_deadline_ns(tlg->tl[type])); + } else { + /* Read clock from the replay file and + do not calculate the deadline, based on virtual clock. */ + qemu_clock_get_ns(type); + } } } return deadline; @@ -570,7 +602,7 @@ int64_t qemu_clock_get_ns(QEMUClockType type) return cpu_get_clock(); } case QEMU_CLOCK_HOST: - now = get_clock_realtime(); + now = REPLAY_CLOCK(REPLAY_CLOCK_HOST, get_clock_realtime()); last = clock->last; clock->last = now; if (now < last || now > (last + get_max_clock_jump())) { @@ -578,7 +610,7 @@ int64_t qemu_clock_get_ns(QEMUClockType type) } return now; case QEMU_CLOCK_VIRTUAL_RT: - return cpu_get_clock(); + return REPLAY_CLOCK(REPLAY_CLOCK_VIRTUAL_RT, cpu_get_clock()); } } diff --git a/qemu/qemu.nsi b/qemu/qemu.nsi index cc5fafd57..a20f6ef35 100644 --- a/qemu/qemu.nsi +++ b/qemu/qemu.nsi @@ -139,6 +139,9 @@ Section "${PRODUCT} (required)" ; Write the uninstall keys for Windows WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "QEMU" +!ifdef DISPLAYVERSION + WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "${DISPLAYVERSION}" +!endif WriteRegStr HKLM "${UNINST_KEY}" "UninstallString" '"${UNINST_EXE}"' WriteRegDWORD HKLM "${UNINST_KEY}" "NoModify" 1 WriteRegDWORD HKLM "${UNINST_KEY}" "NoRepair" 1 diff --git a/qemu/qga/channel-posix.c b/qemu/qga/channel-posix.c index 8aad4fee9..63458c663 100644 --- a/qemu/qga/channel-posix.c +++ b/qemu/qga/channel-posix.c @@ -1,11 +1,7 @@ +#include "qemu/osdep.h" #include #include -#include -#include -#include -#include -#include -#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/sockets.h" #include "qga/channel.h" @@ -217,25 +213,24 @@ GIOStatus ga_channel_write_all(GAChannel *c, const gchar *buf, gsize size) GIOStatus status = G_IO_STATUS_NORMAL; while (size) { + g_debug("sending data, count: %d", (int)size); status = g_io_channel_write_chars(c->client_channel, buf, size, &written, &err); - g_debug("sending data, count: %d", (int)size); - if (err != NULL) { + if (status == G_IO_STATUS_NORMAL) { + size -= written; + buf += written; + } else if (status != G_IO_STATUS_AGAIN) { g_warning("error writing to channel: %s", err->message); - return G_IO_STATUS_ERROR; + return status; } - if (status != G_IO_STATUS_NORMAL) { - break; - } - size -= written; } - if (status == G_IO_STATUS_NORMAL) { + do { status = g_io_channel_flush(c->client_channel, &err); - if (err != NULL) { - g_warning("error flushing channel: %s", err->message); - return G_IO_STATUS_ERROR; - } + } while (status == G_IO_STATUS_AGAIN); + + if (status != G_IO_STATUS_NORMAL) { + g_warning("error flushing channel: %s", err->message); } return status; @@ -249,7 +244,7 @@ GIOStatus ga_channel_read(GAChannel *c, gchar *buf, gsize size, gsize *count) GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path, GAChannelCallback cb, gpointer opaque) { - GAChannel *c = g_malloc0(sizeof(GAChannel)); + GAChannel *c = g_new0(GAChannel, 1); c->event_cb = cb; c->user_data = opaque; diff --git a/qemu/qga/channel-win32.c b/qemu/qga/channel-win32.c index 04fa5e4d1..bb5966124 100644 --- a/qemu/qga/channel-win32.c +++ b/qemu/qga/channel-win32.c @@ -1,9 +1,6 @@ -#include -#include -#include +#include "qemu/osdep.h" #include #include -#include #include #include "qga/guest-agent-core.h" #include "qga/channel.h" @@ -269,7 +266,7 @@ static GIOStatus ga_channel_write(GAChannel *c, const char *buf, size_t size, GIOStatus ga_channel_write_all(GAChannel *c, const char *buf, size_t size) { GIOStatus status = G_IO_STATUS_NORMAL; - size_t count; + size_t count = 0; while (size) { status = ga_channel_write(c, buf, size, &count); @@ -322,7 +319,7 @@ static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method, GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path, GAChannelCallback cb, gpointer opaque) { - GAChannel *c = g_malloc0(sizeof(GAChannel)); + GAChannel *c = g_new0(GAChannel, 1); SECURITY_ATTRIBUTES sec_attrs; if (!ga_channel_open(c, method, path)) { diff --git a/qemu/qga/commands-posix.c b/qemu/qga/commands-posix.c index 675f4b4c6..2ae37255d 100644 --- a/qemu/qga/commands-posix.c +++ b/qemu/qga/commands-posix.c @@ -11,23 +11,19 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include #include -#include -#include -#include #include -#include -#include -#include -#include #include "qga/guest-agent-core.h" #include "qga-qmp-commands.h" #include "qapi/qmp/qerror.h" #include "qemu/queue.h" #include "qemu/host-utils.h" +#include "qemu/sockets.h" +#include "qemu/base64.h" +#include "qemu/cutils.h" #ifndef CONFIG_HAS_ENVIRON #ifdef __APPLE__ @@ -215,15 +211,24 @@ void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) } } +typedef enum { + RW_STATE_NEW, + RW_STATE_READING, + RW_STATE_WRITING, +} RwState; + typedef struct GuestFileHandle { uint64_t id; FILE *fh; + RwState state; QTAILQ_ENTRY(GuestFileHandle) next; } GuestFileHandle; static struct { QTAILQ_HEAD(, GuestFileHandle) filehandles; -} guest_file_state; +} guest_file_state = { + .filehandles = QTAILQ_HEAD_INITIALIZER(guest_file_state.filehandles), +}; static int64_t guest_file_handle_add(FILE *fh, Error **errp) { @@ -235,7 +240,7 @@ static int64_t guest_file_handle_add(FILE *fh, Error **errp) return -1; } - gfh = g_malloc0(sizeof(GuestFileHandle)); + gfh = g_new0(GuestFileHandle, 1); gfh->id = handle; gfh->fh = fh; QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next); @@ -383,27 +388,6 @@ safe_open_or_create(const char *path, const char *mode, Error **errp) return NULL; } -static int guest_file_toggle_flags(int fd, int flags, bool set, Error **err) -{ - int ret, old_flags; - - old_flags = fcntl(fd, F_GETFL); - if (old_flags == -1) { - error_setg_errno(err, errno, QERR_QGA_COMMAND_FAILED, - "failed to fetch filehandle flags"); - return -1; - } - - ret = fcntl(fd, F_SETFL, set ? (old_flags | flags) : (old_flags & ~flags)); - if (ret == -1) { - error_setg_errno(err, errno, QERR_QGA_COMMAND_FAILED, - "failed to set filehandle flags"); - return -1; - } - - return ret; -} - int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **errp) { @@ -424,10 +408,7 @@ int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, /* set fd non-blocking to avoid common use cases (like reading from a * named pipe) from hanging the agent */ - if (guest_file_toggle_flags(fileno(fh), O_NONBLOCK, true, errp) < 0) { - fclose(fh); - return -1; - } + qemu_set_nonblock(fileno(fh)); handle = guest_file_handle_add(fh, errp); if (handle < 0) { @@ -481,6 +462,17 @@ struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, } fh = gfh->fh; + + /* explicitly flush when switching from writing to reading */ + if (gfh->state == RW_STATE_WRITING) { + int ret = fflush(fh); + if (ret == EOF) { + error_setg_errno(errp, errno, "failed to flush file"); + return NULL; + } + gfh->state = RW_STATE_NEW; + } + buf = g_malloc0(count+1); read_count = fread(buf, 1, count, fh); if (ferror(fh)) { @@ -488,12 +480,13 @@ struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, slog("guest-file-read failed, handle: %" PRId64, handle); } else { buf[read_count] = 0; - read_data = g_malloc0(sizeof(GuestFileRead)); + read_data = g_new0(GuestFileRead, 1); read_data->count = read_count; read_data->eof = feof(fh); if (read_count) { read_data->buf_b64 = g_base64_encode(buf, read_count); } + gfh->state = RW_STATE_READING; } g_free(buf); clearerr(fh); @@ -517,7 +510,20 @@ GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, } fh = gfh->fh; - buf = g_base64_decode(buf_b64, &buf_len); + + if (gfh->state == RW_STATE_READING) { + int ret = fseek(fh, 0, SEEK_CUR); + if (ret == -1) { + error_setg_errno(errp, errno, "failed to seek file"); + return NULL; + } + gfh->state = RW_STATE_NEW; + } + + buf = qbase64_decode(buf_b64, -1, &buf_len, errp); + if (!buf) { + return NULL; + } if (!has_count) { count = buf_len; @@ -533,9 +539,10 @@ GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, error_setg_errno(errp, errno, "failed to write to file"); slog("guest-file-write failed, handle: %" PRId64, handle); } else { - write_data = g_malloc0(sizeof(GuestFileWrite)); + write_data = g_new0(GuestFileWrite, 1); write_data->count = write_count; write_data->eof = feof(fh); + gfh->state = RW_STATE_WRITING; } g_free(buf); clearerr(fh); @@ -544,25 +551,40 @@ GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, } struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, - int64_t whence, Error **errp) + GuestFileWhence *whence_code, + Error **errp) { GuestFileHandle *gfh = guest_file_handle_find(handle, errp); GuestFileSeek *seek_data = NULL; FILE *fh; int ret; + int whence; + Error *err = NULL; if (!gfh) { return NULL; } + /* We stupidly exposed 'whence':'int' in our qapi */ + whence = ga_parse_whence(whence_code, &err); + if (err) { + error_propagate(errp, err); + return NULL; + } + fh = gfh->fh; ret = fseek(fh, offset, whence); if (ret == -1) { error_setg_errno(errp, errno, "failed to seek file"); + if (errno == ESPIPE) { + /* file is non-seekable, stdio shouldn't be buffering anyways */ + gfh->state = RW_STATE_NEW; + } } else { seek_data = g_new0(GuestFileSeek, 1); seek_data->position = ftell(fh); seek_data->eof = feof(fh); + gfh->state = RW_STATE_NEW; } clearerr(fh); @@ -583,14 +605,11 @@ void qmp_guest_file_flush(int64_t handle, Error **errp) ret = fflush(fh); if (ret == EOF) { error_setg_errno(errp, errno, "failed to flush file"); + } else { + gfh->state = RW_STATE_NEW; } } -static void guest_file_init(void) -{ - QTAILQ_INIT(&guest_file_state.filehandles); -} - /* linux-specific implementations. avoid this if at all possible. */ #if defined(__linux__) @@ -678,7 +697,7 @@ static void build_fs_mount_list_from_mtab(FsMountList *mounts, Error **errp) continue; } - mount = g_malloc0(sizeof(FsMount)); + mount = g_new0(FsMount, 1); mount->dirname = g_strdup(ment->mnt_dir); mount->devtype = g_strdup(ment->mnt_type); mount->devmajor = devmajor; @@ -757,7 +776,7 @@ static void build_fs_mount_list(FsMountList *mounts, Error **errp) } } - mount = g_malloc0(sizeof(FsMount)); + mount = g_new0(FsMount, 1); mount->dirname = g_strdup(line + dir_s); mount->devtype = g_strdup(dash + type_s); mount->devmajor = devmajor; @@ -1935,7 +1954,10 @@ void qmp_guest_set_user_password(const char *username, char *chpasswddata = NULL; size_t chpasswdlen; - rawpasswddata = (char *)g_base64_decode(password, &rawpasswdlen); + rawpasswddata = (char *)qbase64_decode(password, -1, &rawpasswdlen, errp); + if (!rawpasswddata) { + return; + } rawpasswddata = g_renew(char, rawpasswddata, rawpasswdlen + 1); rawpasswddata[rawpasswdlen] = '\0'; @@ -2158,7 +2180,7 @@ static void transfer_memory_block(GuestMemoryBlock *mem_blk, bool sys2memblk, ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err); if (local_err) { - /* if no 'removable' file, it does't support offline mem blk */ + /* if no 'removable' file, it doesn't support offline mem blk */ if (errno == ENOENT) { error_free(local_err); mem_blk->can_offline = false; @@ -2213,8 +2235,14 @@ GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) dp = opendir("/sys/devices/system/memory/"); if (!dp) { - error_setg_errno(errp, errno, "Can't open directory" - "\"/sys/devices/system/memory/\"\n"); + /* it's ok if this happens to be a system that doesn't expose + * memory blocks via sysfs, but otherwise we should report + * an error + */ + if (errno != ENOENT) { + error_setg_errno(errp, errno, "Can't open directory" + "\"/sys/devices/system/memory/\""); + } return NULL; } @@ -2454,7 +2482,7 @@ GList *ga_command_blacklist_init(GList *blacklist) char **p = (char **)list; while (*p) { - blacklist = g_list_append(blacklist, *p++); + blacklist = g_list_append(blacklist, g_strdup(*p++)); } } #endif @@ -2468,13 +2496,13 @@ GList *ga_command_blacklist_init(GList *blacklist) char **p = (char **)list; while (*p) { - blacklist = g_list_append(blacklist, *p++); + blacklist = g_list_append(blacklist, g_strdup(*p++)); } } #endif #if !defined(CONFIG_FSTRIM) - blacklist = g_list_append(blacklist, (char *)"guest-fstrim"); + blacklist = g_list_append(blacklist, g_strdup("guest-fstrim")); #endif return blacklist; @@ -2486,5 +2514,4 @@ void ga_command_state_init(GAState *s, GACommandState *cs) #if defined(CONFIG_FSFREEZE) ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup); #endif - ga_command_state_add(cs, guest_file_init, NULL); } diff --git a/qemu/qga/commands-win32.c b/qemu/qga/commands-win32.c index a7822d5ff..d76327f5a 100644 --- a/qemu/qga/commands-win32.c +++ b/qemu/qga/commands-win32.c @@ -11,11 +11,10 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include #include #include -#include -#include #include #include #include @@ -26,12 +25,15 @@ #include #include #endif +#include + #include "qga/guest-agent-core.h" #include "qga/vss-win32.h" #include "qga-qmp-commands.h" #include "qapi/qmp/qerror.h" #include "qemu/queue.h" #include "qemu/host-utils.h" +#include "qemu/base64.h" #ifndef SHTDN_REASON_FLAG_PLANNED #define SHTDN_REASON_FLAG_PLANNED 0x80000000 @@ -53,8 +55,11 @@ typedef struct GuestFileHandle { static struct { QTAILQ_HEAD(, GuestFileHandle) filehandles; -} guest_file_state; +} guest_file_state = { + .filehandles = QTAILQ_HEAD_INITIALIZER(guest_file_state.filehandles), +}; +#define FILE_GENERIC_APPEND (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA) typedef struct OpenFlags { const char *forms; @@ -62,20 +67,20 @@ typedef struct OpenFlags { DWORD creation_disposition; } OpenFlags; static OpenFlags guest_file_open_modes[] = { - {"r", GENERIC_READ, OPEN_EXISTING}, - {"rb", GENERIC_READ, OPEN_EXISTING}, - {"w", GENERIC_WRITE, CREATE_ALWAYS}, - {"wb", GENERIC_WRITE, CREATE_ALWAYS}, - {"a", GENERIC_WRITE, OPEN_ALWAYS }, - {"r+", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING}, - {"rb+", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING}, - {"r+b", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING}, - {"w+", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS}, - {"wb+", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS}, - {"w+b", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS}, - {"a+", GENERIC_WRITE|GENERIC_READ, OPEN_ALWAYS }, - {"ab+", GENERIC_WRITE|GENERIC_READ, OPEN_ALWAYS }, - {"a+b", GENERIC_WRITE|GENERIC_READ, OPEN_ALWAYS } + {"r", GENERIC_READ, OPEN_EXISTING}, + {"rb", GENERIC_READ, OPEN_EXISTING}, + {"w", GENERIC_WRITE, CREATE_ALWAYS}, + {"wb", GENERIC_WRITE, CREATE_ALWAYS}, + {"a", FILE_GENERIC_APPEND, OPEN_ALWAYS }, + {"r+", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING}, + {"rb+", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING}, + {"r+b", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING}, + {"w+", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS}, + {"wb+", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS}, + {"w+b", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS}, + {"a+", FILE_GENERIC_APPEND|GENERIC_READ, OPEN_ALWAYS }, + {"ab+", FILE_GENERIC_APPEND|GENERIC_READ, OPEN_ALWAYS }, + {"a+b", FILE_GENERIC_APPEND|GENERIC_READ, OPEN_ALWAYS } }; static OpenFlags *find_open_flag(const char *mode_str) @@ -104,7 +109,7 @@ static int64_t guest_file_handle_add(HANDLE fh, Error **errp) if (handle < 0) { return -1; } - gfh = g_malloc0(sizeof(GuestFileHandle)); + gfh = g_new0(GuestFileHandle, 1); gfh->id = handle; gfh->fh = fh; QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next); @@ -124,6 +129,28 @@ static GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp) return NULL; } +static void handle_set_nonblocking(HANDLE fh) +{ + DWORD file_type, pipe_state; + file_type = GetFileType(fh); + if (file_type != FILE_TYPE_PIPE) { + return; + } + /* If file_type == FILE_TYPE_PIPE, according to MSDN + * the specified file is socket or named pipe */ + if (!GetNamedPipeHandleState(fh, &pipe_state, NULL, + NULL, NULL, NULL, 0)) { + return; + } + /* The fd is named pipe fd */ + if (pipe_state & PIPE_NOWAIT) { + return; + } + + pipe_state |= PIPE_NOWAIT; + SetNamedPipeHandleState(fh, &pipe_state, NULL, NULL); +} + int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **errp) { @@ -154,9 +181,14 @@ int64_t qmp_guest_file_open(const char *path, bool has_mode, return -1; } + /* set fd non-blocking to avoid common use cases (like reading from a + * named pipe) from hanging the agent + */ + handle_set_nonblocking(fh); + fd = guest_file_handle_add(fh, errp); if (fd < 0) { - CloseHandle(&fh); + CloseHandle(fh); error_setg(errp, "failed to add handle to qmp handle table"); return -1; } @@ -296,7 +328,7 @@ GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, slog("guest-file-read failed, handle %" PRId64, handle); } else { buf[read_count] = 0; - read_data = g_malloc0(sizeof(GuestFileRead)); + read_data = g_new0(GuestFileRead, 1); read_data->count = (size_t)read_count; read_data->eof = read_count == 0; @@ -325,7 +357,10 @@ GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, return NULL; } fh = gfh->fh; - buf = g_base64_decode(buf_b64, &buf_len); + buf = qbase64_decode(buf_b64, -1, &buf_len, errp); + if (!buf) { + return NULL; + } if (!has_count) { count = buf_len; @@ -340,7 +375,7 @@ GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, error_setg_win32(errp, GetLastError(), "failed to write to file"); slog("guest-file-write-failed, handle: %" PRId64, handle); } else { - write_data = g_malloc0(sizeof(GuestFileWrite)); + write_data = g_new0(GuestFileWrite, 1); write_data->count = (size_t) write_count; } @@ -350,7 +385,8 @@ done: } GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, - int64_t whence, Error **errp) + GuestFileWhence *whence_code, + Error **errp) { GuestFileHandle *gfh; GuestFileSeek *seek_data; @@ -358,11 +394,21 @@ GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, LARGE_INTEGER new_pos, off_pos; off_pos.QuadPart = offset; BOOL res; + int whence; + Error *err = NULL; + gfh = guest_file_handle_find(handle, errp); if (!gfh) { return NULL; } + /* We stupidly exposed 'whence':'int' in our qapi */ + whence = ga_parse_whence(whence_code, &err); + if (err) { + error_propagate(errp, err); + return NULL; + } + fh = gfh->fh; res = SetFilePointerEx(fh, off_pos, &new_pos, whence); if (!res) { @@ -388,11 +434,6 @@ void qmp_guest_file_flush(int64_t handle, Error **errp) } } -static void guest_file_init(void) -{ - QTAILQ_INIT(&guest_file_state.filehandles); -} - #ifdef CONFIG_QGA_NTDDSCSI static STORAGE_BUS_TYPE win2qemu[] = { @@ -657,7 +698,7 @@ static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp) fs->mountpoint = g_strndup(mnt_point, len); } fs->type = g_strdup(fs_name); - fs->disk = build_guest_disk_info(guid, errp);; + fs->disk = build_guest_disk_info(guid, errp); free: g_free(mnt_point); return fs; @@ -863,7 +904,7 @@ static DWORD WINAPI do_suspend(LPVOID opaque) void qmp_guest_suspend_disk(Error **errp) { Error *local_err = NULL; - GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode)); + GuestSuspendMode *mode = g_new(GuestSuspendMode, 1); *mode = GUEST_SUSPEND_MODE_DISK; check_suspend_mode(*mode, &local_err); @@ -879,7 +920,7 @@ void qmp_guest_suspend_disk(Error **errp) void qmp_guest_suspend_ram(Error **errp) { Error *local_err = NULL; - GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode)); + GuestSuspendMode *mode = g_new(GuestSuspendMode, 1); *mode = GUEST_SUSPEND_MODE_RAM; check_suspend_mode(*mode, &local_err); @@ -1182,7 +1223,71 @@ void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) { - error_setg(errp, QERR_UNSUPPORTED); + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION pslpi, ptr; + DWORD length; + GuestLogicalProcessorList *head, **link; + Error *local_err = NULL; + int64_t current; + + ptr = pslpi = NULL; + length = 0; + current = 0; + head = NULL; + link = &head; + + if ((GetLogicalProcessorInformation(pslpi, &length) == FALSE) && + (GetLastError() == ERROR_INSUFFICIENT_BUFFER) && + (length > sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION))) { + ptr = pslpi = g_malloc0(length); + if (GetLogicalProcessorInformation(pslpi, &length) == FALSE) { + error_setg(&local_err, "Failed to get processor information: %d", + (int)GetLastError()); + } + } else { + error_setg(&local_err, + "Failed to get processor information buffer length: %d", + (int)GetLastError()); + } + + while ((local_err == NULL) && (length > 0)) { + if (pslpi->Relationship == RelationProcessorCore) { + ULONG_PTR cpu_bits = pslpi->ProcessorMask; + + while (cpu_bits > 0) { + if (!!(cpu_bits & 1)) { + GuestLogicalProcessor *vcpu; + GuestLogicalProcessorList *entry; + + vcpu = g_malloc0(sizeof *vcpu); + vcpu->logical_id = current++; + vcpu->online = true; + vcpu->has_can_offline = false; + + entry = g_malloc0(sizeof *entry); + entry->value = vcpu; + + *link = entry; + link = &entry->next; + } + cpu_bits >>= 1; + } + } + length -= sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); + pslpi++; /* next entry */ + } + + g_free(ptr); + + if (local_err == NULL) { + if (head != NULL) { + return head; + } + /* there's no guest with zero VCPUs */ + error_setg(&local_err, "Guest reported zero VCPUs"); + } + + qapi_free_GuestLogicalProcessorList(head); + error_propagate(errp, local_err); return NULL; } @@ -1192,12 +1297,103 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) return -1; } +static gchar * +get_net_error_message(gint error) +{ + HMODULE module = NULL; + gchar *retval = NULL; + wchar_t *msg = NULL; + int flags; + size_t nchars; + + flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM; + + if (error >= NERR_BASE && error <= MAX_NERR) { + module = LoadLibraryExW(L"netmsg.dll", NULL, LOAD_LIBRARY_AS_DATAFILE); + + if (module != NULL) { + flags |= FORMAT_MESSAGE_FROM_HMODULE; + } + } + + FormatMessageW(flags, module, error, 0, (LPWSTR)&msg, 0, NULL); + + if (msg != NULL) { + nchars = wcslen(msg); + + if (nchars >= 2 && + msg[nchars - 1] == L'\n' && + msg[nchars - 2] == L'\r') { + msg[nchars - 2] = L'\0'; + } + + retval = g_utf16_to_utf8(msg, -1, NULL, NULL, NULL); + + LocalFree(msg); + } + + if (module != NULL) { + FreeLibrary(module); + } + + return retval; +} + void qmp_guest_set_user_password(const char *username, const char *password, bool crypted, Error **errp) { - error_setg(errp, QERR_UNSUPPORTED); + NET_API_STATUS nas; + char *rawpasswddata = NULL; + size_t rawpasswdlen; + wchar_t *user = NULL, *wpass = NULL; + USER_INFO_1003 pi1003 = { 0, }; + GError *gerr = NULL; + + if (crypted) { + error_setg(errp, QERR_UNSUPPORTED); + return; + } + + rawpasswddata = (char *)qbase64_decode(password, -1, &rawpasswdlen, errp); + if (!rawpasswddata) { + return; + } + rawpasswddata = g_renew(char, rawpasswddata, rawpasswdlen + 1); + rawpasswddata[rawpasswdlen] = '\0'; + + user = g_utf8_to_utf16(username, -1, NULL, NULL, &gerr); + if (!user) { + goto done; + } + + wpass = g_utf8_to_utf16(rawpasswddata, -1, NULL, NULL, &gerr); + if (!wpass) { + goto done; + } + + pi1003.usri1003_password = wpass; + nas = NetUserSetInfo(NULL, user, + 1003, (LPBYTE)&pi1003, + NULL); + + if (nas != NERR_Success) { + gchar *msg = get_net_error_message(nas); + error_setg(errp, "failed to set password: %s", msg); + g_free(msg); + } + +done: + if (gerr) { + error_setg(errp, QERR_QGA_COMMAND_FAILED, gerr->message); + g_error_free(gerr); + } + g_free(user); + g_free(wpass); + g_free(rawpasswddata); } GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) @@ -1224,8 +1420,7 @@ GList *ga_command_blacklist_init(GList *blacklist) { const char *list_unsupported[] = { "guest-suspend-hybrid", - "guest-get-vcpus", "guest-set-vcpus", - "guest-set-user-password", + "guest-set-vcpus", "guest-get-memory-blocks", "guest-set-memory-blocks", "guest-get-memory-block-size", "guest-fsfreeze-freeze-list", @@ -1233,7 +1428,7 @@ GList *ga_command_blacklist_init(GList *blacklist) char **p = (char **)list_unsupported; while (*p) { - blacklist = g_list_append(blacklist, *p++); + blacklist = g_list_append(blacklist, g_strdup(*p++)); } if (!vss_init(true)) { @@ -1244,7 +1439,7 @@ GList *ga_command_blacklist_init(GList *blacklist) p = (char **)list; while (*p) { - blacklist = g_list_append(blacklist, *p++); + blacklist = g_list_append(blacklist, g_strdup(*p++)); } } @@ -1257,5 +1452,4 @@ void ga_command_state_init(GAState *s, GACommandState *cs) if (!vss_initialized()) { ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup); } - ga_command_state_add(cs, guest_file_init, NULL); } diff --git a/qemu/qga/commands.c b/qemu/qga/commands.c index 783496791..b653a460b 100644 --- a/qemu/qga/commands.c +++ b/qemu/qga/commands.c @@ -10,10 +10,18 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include #include "qga/guest-agent-core.h" #include "qga-qmp-commands.h" #include "qapi/qmp/qerror.h" +#include "qemu/base64.h" +#include "qemu/cutils.h" + +/* Maximum captured guest-exec out_data/err_data - 16MB */ +#define GUEST_EXEC_MAX_OUTPUT (16*1024*1024) +/* Allocation and I/O buffer for reading guest-exec out_data/err_data - 4KB */ +#define GUEST_EXEC_IO_SIZE (4*1024) /* Note: in some situations, like with the fsfreeze, logging may be * temporarilly disabled. if it is necessary that a command be able @@ -51,12 +59,12 @@ static void qmp_command_info(QmpCommand *cmd, void *opaque) GuestAgentCommandInfo *cmd_info; GuestAgentCommandInfoList *cmd_info_list; - cmd_info = g_malloc0(sizeof(GuestAgentCommandInfo)); + cmd_info = g_new0(GuestAgentCommandInfo, 1); cmd_info->name = g_strdup(qmp_command_name(cmd)); cmd_info->enabled = qmp_command_is_enabled(cmd); cmd_info->success_response = qmp_has_success_response(cmd); - cmd_info_list = g_malloc0(sizeof(GuestAgentCommandInfoList)); + cmd_info_list = g_new0(GuestAgentCommandInfoList, 1); cmd_info_list->value = cmd_info; cmd_info_list->next = info->supported_commands; info->supported_commands = cmd_info_list; @@ -64,9 +72,430 @@ static void qmp_command_info(QmpCommand *cmd, void *opaque) struct GuestAgentInfo *qmp_guest_info(Error **errp) { - GuestAgentInfo *info = g_malloc0(sizeof(GuestAgentInfo)); + GuestAgentInfo *info = g_new0(GuestAgentInfo, 1); info->version = g_strdup(QEMU_VERSION); qmp_for_each_command(qmp_command_info, info); return info; } + +struct GuestExecIOData { + guchar *data; + gsize size; + gsize length; + gint closed; + bool truncated; + const char *name; +}; +typedef struct GuestExecIOData GuestExecIOData; + +struct GuestExecInfo { + GPid pid; + int64_t pid_numeric; + gint status; + bool has_output; + gint finished; + GuestExecIOData in; + GuestExecIOData out; + GuestExecIOData err; + QTAILQ_ENTRY(GuestExecInfo) next; +}; +typedef struct GuestExecInfo GuestExecInfo; + +static struct { + QTAILQ_HEAD(, GuestExecInfo) processes; +} guest_exec_state = { + .processes = QTAILQ_HEAD_INITIALIZER(guest_exec_state.processes), +}; + +static int64_t gpid_to_int64(GPid pid) +{ +#ifdef G_OS_WIN32 + return GetProcessId(pid); +#else + return (int64_t)pid; +#endif +} + +static GuestExecInfo *guest_exec_info_add(GPid pid) +{ + GuestExecInfo *gei; + + gei = g_new0(GuestExecInfo, 1); + gei->pid = pid; + gei->pid_numeric = gpid_to_int64(pid); + QTAILQ_INSERT_TAIL(&guest_exec_state.processes, gei, next); + + return gei; +} + +static GuestExecInfo *guest_exec_info_find(int64_t pid_numeric) +{ + GuestExecInfo *gei; + + QTAILQ_FOREACH(gei, &guest_exec_state.processes, next) { + if (gei->pid_numeric == pid_numeric) { + return gei; + } + } + + return NULL; +} + +GuestExecStatus *qmp_guest_exec_status(int64_t pid, Error **err) +{ + GuestExecInfo *gei; + GuestExecStatus *ges; + + slog("guest-exec-status called, pid: %u", (uint32_t)pid); + + gei = guest_exec_info_find(pid); + if (gei == NULL) { + error_setg(err, QERR_INVALID_PARAMETER, "pid"); + return NULL; + } + + ges = g_new0(GuestExecStatus, 1); + + bool finished = g_atomic_int_get(&gei->finished); + + /* need to wait till output channels are closed + * to be sure we captured all output at this point */ + if (gei->has_output) { + finished = finished && g_atomic_int_get(&gei->out.closed); + finished = finished && g_atomic_int_get(&gei->err.closed); + } + + ges->exited = finished; + if (finished) { + /* Glib has no portable way to parse exit status. + * On UNIX, we can get either exit code from normal termination + * or signal number. + * On Windows, it is either the same exit code or the exception + * value for an unhandled exception that caused the process + * to terminate. + * See MSDN for GetExitCodeProcess() and ntstatus.h for possible + * well-known codes, e.g. C0000005 ACCESS_DENIED - analog of SIGSEGV + * References: + * https://msdn.microsoft.com/en-us/library/windows/desktop/ms683189(v=vs.85).aspx + * https://msdn.microsoft.com/en-us/library/aa260331(v=vs.60).aspx + */ +#ifdef G_OS_WIN32 + /* Additionally WIN32 does not provide any additional information + * on whetherthe child exited or terminated via signal. + * We use this simple range check to distingish application exit code + * (usually value less then 256) and unhandled exception code with + * ntstatus (always value greater then 0xC0000005). */ + if ((uint32_t)gei->status < 0xC0000000U) { + ges->has_exitcode = true; + ges->exitcode = gei->status; + } else { + ges->has_signal = true; + ges->signal = gei->status; + } +#else + if (WIFEXITED(gei->status)) { + ges->has_exitcode = true; + ges->exitcode = WEXITSTATUS(gei->status); + } else if (WIFSIGNALED(gei->status)) { + ges->has_signal = true; + ges->signal = WTERMSIG(gei->status); + } +#endif + if (gei->out.length > 0) { + ges->has_out_data = true; + ges->out_data = g_base64_encode(gei->out.data, gei->out.length); + g_free(gei->out.data); + ges->has_out_truncated = gei->out.truncated; + } + + if (gei->err.length > 0) { + ges->has_err_data = true; + ges->err_data = g_base64_encode(gei->err.data, gei->err.length); + g_free(gei->err.data); + ges->has_err_truncated = gei->err.truncated; + } + + QTAILQ_REMOVE(&guest_exec_state.processes, gei, next); + g_free(gei); + } + + return ges; +} + +/* Get environment variables or arguments array for execve(). */ +static char **guest_exec_get_args(const strList *entry, bool log) +{ + const strList *it; + int count = 1, i = 0; /* reserve for NULL terminator */ + char **args; + char *str; /* for logging array of arguments */ + size_t str_size = 1; + + for (it = entry; it != NULL; it = it->next) { + count++; + str_size += 1 + strlen(it->value); + } + + str = g_malloc(str_size); + *str = 0; + args = g_malloc(count * sizeof(char *)); + for (it = entry; it != NULL; it = it->next) { + args[i++] = it->value; + pstrcat(str, str_size, it->value); + if (it->next) { + pstrcat(str, str_size, " "); + } + } + args[i] = NULL; + + if (log) { + slog("guest-exec called: \"%s\"", str); + } + g_free(str); + + return args; +} + +static void guest_exec_child_watch(GPid pid, gint status, gpointer data) +{ + GuestExecInfo *gei = (GuestExecInfo *)data; + + g_debug("guest_exec_child_watch called, pid: %d, status: %u", + (int32_t)gpid_to_int64(pid), (uint32_t)status); + + gei->status = status; + gei->finished = true; + + g_spawn_close_pid(pid); +} + +/** Reset ignored signals back to default. */ +static void guest_exec_task_setup(gpointer data) +{ +#if !defined(G_OS_WIN32) + struct sigaction sigact; + + memset(&sigact, 0, sizeof(struct sigaction)); + sigact.sa_handler = SIG_DFL; + + if (sigaction(SIGPIPE, &sigact, NULL) != 0) { + slog("sigaction() failed to reset child process's SIGPIPE: %s", + strerror(errno)); + } +#endif +} + +static gboolean guest_exec_input_watch(GIOChannel *ch, + GIOCondition cond, gpointer p_) +{ + GuestExecIOData *p = (GuestExecIOData *)p_; + gsize bytes_written = 0; + GIOStatus status; + GError *gerr = NULL; + + /* nothing left to write */ + if (p->size == p->length) { + goto done; + } + + status = g_io_channel_write_chars(ch, (gchar *)p->data + p->length, + p->size - p->length, &bytes_written, &gerr); + + /* can be not 0 even if not G_IO_STATUS_NORMAL */ + if (bytes_written != 0) { + p->length += bytes_written; + } + + /* continue write, our callback will be called again */ + if (status == G_IO_STATUS_NORMAL || status == G_IO_STATUS_AGAIN) { + return true; + } + + if (gerr) { + g_warning("qga: i/o error writing to input_data channel: %s", + gerr->message); + g_error_free(gerr); + } + +done: + g_io_channel_shutdown(ch, true, NULL); + g_io_channel_unref(ch); + g_atomic_int_set(&p->closed, 1); + g_free(p->data); + + return false; +} + +static gboolean guest_exec_output_watch(GIOChannel *ch, + GIOCondition cond, gpointer p_) +{ + GuestExecIOData *p = (GuestExecIOData *)p_; + gsize bytes_read; + GIOStatus gstatus; + + if (cond == G_IO_HUP || cond == G_IO_ERR) { + goto close; + } + + if (p->size == p->length) { + gpointer t = NULL; + if (!p->truncated && p->size < GUEST_EXEC_MAX_OUTPUT) { + t = g_try_realloc(p->data, p->size + GUEST_EXEC_IO_SIZE); + } + if (t == NULL) { + /* ignore truncated output */ + gchar buf[GUEST_EXEC_IO_SIZE]; + + p->truncated = true; + gstatus = g_io_channel_read_chars(ch, buf, sizeof(buf), + &bytes_read, NULL); + if (gstatus == G_IO_STATUS_EOF || gstatus == G_IO_STATUS_ERROR) { + goto close; + } + + return true; + } + p->size += GUEST_EXEC_IO_SIZE; + p->data = t; + } + + /* Calling read API once. + * On next available data our callback will be called again */ + gstatus = g_io_channel_read_chars(ch, (gchar *)p->data + p->length, + p->size - p->length, &bytes_read, NULL); + if (gstatus == G_IO_STATUS_EOF || gstatus == G_IO_STATUS_ERROR) { + goto close; + } + + p->length += bytes_read; + + return true; + +close: + g_io_channel_shutdown(ch, true, NULL); + g_io_channel_unref(ch); + g_atomic_int_set(&p->closed, 1); + return false; +} + +GuestExec *qmp_guest_exec(const char *path, + bool has_arg, strList *arg, + bool has_env, strList *env, + bool has_input_data, const char *input_data, + bool has_capture_output, bool capture_output, + Error **err) +{ + GPid pid; + GuestExec *ge = NULL; + GuestExecInfo *gei; + char **argv, **envp; + strList arglist; + gboolean ret; + GError *gerr = NULL; + gint in_fd, out_fd, err_fd; + GIOChannel *in_ch, *out_ch, *err_ch; + GSpawnFlags flags; + bool has_output = (has_capture_output && capture_output); + uint8_t *input = NULL; + size_t ninput = 0; + + arglist.value = (char *)path; + arglist.next = has_arg ? arg : NULL; + + if (has_input_data) { + input = qbase64_decode(input_data, -1, &ninput, err); + if (!input) { + return NULL; + } + } + + argv = guest_exec_get_args(&arglist, true); + envp = has_env ? guest_exec_get_args(env, false) : NULL; + + flags = G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD; +#if GLIB_CHECK_VERSION(2, 33, 2) + flags |= G_SPAWN_SEARCH_PATH_FROM_ENVP; +#endif + if (!has_output) { + flags |= G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL; + } + + ret = g_spawn_async_with_pipes(NULL, argv, envp, flags, + guest_exec_task_setup, NULL, &pid, has_input_data ? &in_fd : NULL, + has_output ? &out_fd : NULL, has_output ? &err_fd : NULL, &gerr); + if (!ret) { + error_setg(err, QERR_QGA_COMMAND_FAILED, gerr->message); + g_error_free(gerr); + goto done; + } + + ge = g_new0(GuestExec, 1); + ge->pid = gpid_to_int64(pid); + + gei = guest_exec_info_add(pid); + gei->has_output = has_output; + g_child_watch_add(pid, guest_exec_child_watch, gei); + + if (has_input_data) { + gei->in.data = input; + gei->in.size = ninput; +#ifdef G_OS_WIN32 + in_ch = g_io_channel_win32_new_fd(in_fd); +#else + in_ch = g_io_channel_unix_new(in_fd); +#endif + g_io_channel_set_encoding(in_ch, NULL, NULL); + g_io_channel_set_buffered(in_ch, false); + g_io_channel_set_flags(in_ch, G_IO_FLAG_NONBLOCK, NULL); + g_io_channel_set_close_on_unref(in_ch, true); + g_io_add_watch(in_ch, G_IO_OUT, guest_exec_input_watch, &gei->in); + } + + if (has_output) { +#ifdef G_OS_WIN32 + out_ch = g_io_channel_win32_new_fd(out_fd); + err_ch = g_io_channel_win32_new_fd(err_fd); +#else + out_ch = g_io_channel_unix_new(out_fd); + err_ch = g_io_channel_unix_new(err_fd); +#endif + g_io_channel_set_encoding(out_ch, NULL, NULL); + g_io_channel_set_encoding(err_ch, NULL, NULL); + g_io_channel_set_buffered(out_ch, false); + g_io_channel_set_buffered(err_ch, false); + g_io_channel_set_close_on_unref(out_ch, true); + g_io_channel_set_close_on_unref(err_ch, true); + g_io_add_watch(out_ch, G_IO_IN | G_IO_HUP, + guest_exec_output_watch, &gei->out); + g_io_add_watch(err_ch, G_IO_IN | G_IO_HUP, + guest_exec_output_watch, &gei->err); + } + +done: + g_free(argv); + g_free(envp); + + return ge; +} + +/* Convert GuestFileWhence (either a raw integer or an enum value) into + * the guest's SEEK_ constants. */ +int ga_parse_whence(GuestFileWhence *whence, Error **errp) +{ + /* Exploit the fact that we picked values to match QGA_SEEK_*. */ + if (whence->type == QTYPE_QSTRING) { + whence->type = QTYPE_QINT; + whence->u.value = whence->u.name; + } + switch (whence->u.value) { + case QGA_SEEK_SET: + return SEEK_SET; + case QGA_SEEK_CUR: + return SEEK_CUR; + case QGA_SEEK_END: + return SEEK_END; + } + error_setg(errp, "invalid whence code %"PRId64, whence->u.value); + return -1; +} diff --git a/qemu/qga/guest-agent-command-state.c b/qemu/qga/guest-agent-command-state.c index 969da2328..20b9b2222 100644 --- a/qemu/qga/guest-agent-command-state.c +++ b/qemu/qga/guest-agent-command-state.c @@ -9,6 +9,7 @@ * 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 #include "qga/guest-agent-core.h" @@ -27,7 +28,7 @@ void ga_command_state_add(GACommandState *cs, void (*init)(void), void (*cleanup)(void)) { - GACommandGroup *cg = g_malloc0(sizeof(GACommandGroup)); + GACommandGroup *cg = g_new0(GACommandGroup, 1); cg->init = init; cg->cleanup = cleanup; cs->groups = g_slist_append(cs->groups, cg); @@ -67,7 +68,7 @@ void ga_command_state_cleanup_all(GACommandState *cs) GACommandState *ga_command_state_new(void) { - GACommandState *cs = g_malloc0(sizeof(GACommandState)); + GACommandState *cs = g_new0(GACommandState, 1); cs->groups = NULL; return cs; } diff --git a/qemu/qga/guest-agent-core.h b/qemu/qga/guest-agent-core.h index e92c6abaf..0a4951604 100644 --- a/qemu/qga/guest-agent-core.h +++ b/qemu/qga/guest-agent-core.h @@ -12,6 +12,7 @@ */ #include "qapi/qmp/dispatch.h" #include "qemu-common.h" +#include "qga-qmp-commands.h" #define QGA_READ_COUNT_DEFAULT 4096 @@ -37,6 +38,7 @@ void ga_set_frozen(GAState *s); void ga_unset_frozen(GAState *s); const char *ga_fsfreeze_hook(GAState *s); int64_t ga_get_fd_handle(GAState *s, Error **errp); +int ga_parse_whence(GuestFileWhence *whence, Error **errp); #ifndef _WIN32 void reopen_fd_to_null(int fd); diff --git a/qemu/qga/installer/qemu-ga.wxs b/qemu/qga/installer/qemu-ga.wxs index 2c43f1b5a..fa2260caf 100644 --- a/qemu/qga/installer/qemu-ga.wxs +++ b/qemu/qga/installer/qemu-ga.wxs @@ -41,8 +41,8 @@ @@ -58,29 +58,15 @@ /> - - - - + - - - - - - - - - - - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -110,10 +137,11 @@ + - + + > + + + + + + + + + + + + + - - - NOT Installed + Installed + NOT REMOVE diff --git a/qemu/qga/main.c b/qemu/qga/main.c index 791982ef0..c55278210 100644 --- a/qemu/qga/main.c +++ b/qemu/qga/main.c @@ -10,16 +10,13 @@ * 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 -#include -#include +#include "qemu/osdep.h" #include #include #include #ifndef _WIN32 #include #include -#include #endif #include "qapi/qmp/json-streamer.h" #include "qapi/qmp/json-parser.h" @@ -27,11 +24,11 @@ #include "qapi/qmp/qjson.h" #include "qga/guest-agent-core.h" #include "qemu/module.h" -#include "signal.h" #include "qapi/qmp/qerror.h" #include "qapi/qmp/dispatch.h" #include "qga/channel.h" #include "qemu/bswap.h" +#include "qemu/help_option.h" #ifdef _WIN32 #include "qga/service-win32.h" #include "qga/vss-win32.h" @@ -56,6 +53,7 @@ #define QGA_FSFREEZE_HOOK_DEFAULT CONFIG_QEMU_CONFDIR "/fsfreeze-hook" #endif #define QGA_SENTINEL_BYTE 0xFF +#define QGA_CONF_DEFAULT CONFIG_QEMU_CONFDIR G_DIR_SEPARATOR_S "qemu-ga.conf" static struct { const char *state_dir; @@ -82,7 +80,7 @@ struct GAState { bool delimit_response; bool frozen; GList *blacklist; - const char *state_filepath_isfrozen; + char *state_filepath_isfrozen; struct { const char *log_filepath; const char *pid_filepath; @@ -90,7 +88,7 @@ struct GAState { #ifdef CONFIG_FSFREEZE const char *fsfreeze_hook; #endif - const gchar *pstate_filepath; + gchar *pstate_filepath; GAPersistentState pstate; }; @@ -160,6 +158,12 @@ static gboolean register_signal_handlers(void) g_error("error configuring signal handler: %s", strerror(errno)); } + sigact.sa_handler = SIG_IGN; + if (sigaction(SIGPIPE, &sigact, NULL) != 0) { + g_error("error configuring SIGPIPE signal handler: %s", + strerror(errno)); + } + return true; } @@ -215,6 +219,8 @@ static void usage(const char *cmd) #endif " -b, --blacklist comma-separated list of RPCs to disable (no spaces, \"?\"\n" " to list available RPCs)\n" +" -D, --dump-conf dump a qemu-ga config file based on current config\n" +" options / command-line parameters to stdout\n" " -h, --help display this help and exit\n" "\n" "Report bugs to \n" @@ -561,10 +567,9 @@ static void process_command(GAState *s, QDict *req) } /* handle requests/control events coming in over the channel */ -static void process_event(JSONMessageParser *parser, QList *tokens) +static void process_event(JSONMessageParser *parser, GQueue *tokens) { GAState *s = container_of(parser, GAState, parser); - QObject *obj; QDict *qdict; Error *err = NULL; int ret; @@ -572,9 +577,9 @@ static void process_event(JSONMessageParser *parser, QList *tokens) g_assert(s && parser); g_debug("process_event: called"); - obj = json_parser_parse_err(tokens, NULL, &err); - if (err || !obj || qobject_type(obj) != QTYPE_QDICT) { - qobject_decref(obj); + qdict = qobject_to_qdict(json_parser_parse_err(tokens, NULL, &err)); + if (err || !qdict) { + QDECREF(qdict); qdict = qdict_new(); if (!err) { g_warning("failed to parse event: unknown error"); @@ -584,12 +589,8 @@ static void process_event(JSONMessageParser *parser, QList *tokens) } qdict_put_obj(qdict, "error", qmp_build_error_object(err)); error_free(err); - } else { - qdict = qobject_to_qdict(obj); } - g_assert(qdict); - /* handle host->guest commands */ if (qdict_haskey(qdict, "execute")) { process_command(s, qdict); @@ -617,13 +618,7 @@ static gboolean channel_event_cb(GIOCondition condition, gpointer data) GAState *s = data; gchar buf[QGA_READ_COUNT_DEFAULT+1]; gsize count; - GError *err = NULL; GIOStatus status = ga_channel_read(s->channel, buf, QGA_READ_COUNT_DEFAULT, &count); - if (err != NULL) { - g_warning("error reading channel: %s", err->message); - g_error_free(err); - return false; - } switch (status) { case G_IO_STATUS_ERROR: g_warning("error reading channel"); @@ -658,23 +653,6 @@ static gboolean channel_init(GAState *s, const gchar *method, const gchar *path) { GAChannelMethod channel_method; - if (method == NULL) { - method = "virtio-serial"; - } - - if (path == NULL) { - if (strcmp(method, "virtio-serial") == 0 ) { - /* try the default path for the virtio-serial port */ - path = QGA_VIRTIO_PATH_DEFAULT; - } else if (strcmp(method, "isa-serial") == 0){ - /* try the default path for the serial port - COM1 */ - path = QGA_SERIAL_PATH_DEFAULT; - } else { - g_critical("must specify a path for this channel"); - return false; - } - } - if (strcmp(method, "virtio-serial") == 0) { s->virtio = true; /* virtio requires special handling in some cases */ channel_method = GA_CHANNEL_VIRTIO_SERIAL; @@ -921,22 +899,165 @@ static void ga_print_cmd(QmpCommand *cmd, void *opaque) printf("%s\n", qmp_command_name(cmd)); } -int main(int argc, char **argv) +static GList *split_list(const gchar *str, const gchar *delim) { - const char *sopt = "hVvdm:p:l:f:F::b:s:t:"; - const char *method = NULL, *path = NULL; - const char *log_filepath = NULL; - const char *pid_filepath; + GList *list = NULL; + int i; + gchar **strv; + + strv = g_strsplit(str, delim, -1); + for (i = 0; strv[i]; i++) { + list = g_list_prepend(list, strv[i]); + } + g_free(strv); + + return list; +} + +typedef struct GAConfig { + char *channel_path; + char *method; + char *log_filepath; + char *pid_filepath; #ifdef CONFIG_FSFREEZE - const char *fsfreeze_hook = NULL; + char *fsfreeze_hook; #endif - const char *state_dir; + char *state_dir; #ifdef _WIN32 - const char *service = NULL; + const char *service; +#endif + gchar *bliststr; /* blacklist may point to this string */ + GList *blacklist; + int daemonize; + GLogLevelFlags log_level; + int dumpconf; +} GAConfig; + +static void config_load(GAConfig *config) +{ + GError *gerr = NULL; + GKeyFile *keyfile; + const char *conf = g_getenv("QGA_CONF") ?: QGA_CONF_DEFAULT; + + /* read system config */ + keyfile = g_key_file_new(); + if (!g_key_file_load_from_file(keyfile, conf, 0, &gerr)) { + goto end; + } + if (g_key_file_has_key(keyfile, "general", "daemon", NULL)) { + config->daemonize = + g_key_file_get_boolean(keyfile, "general", "daemon", &gerr); + } + if (g_key_file_has_key(keyfile, "general", "method", NULL)) { + config->method = + g_key_file_get_string(keyfile, "general", "method", &gerr); + } + if (g_key_file_has_key(keyfile, "general", "path", NULL)) { + config->channel_path = + g_key_file_get_string(keyfile, "general", "path", &gerr); + } + if (g_key_file_has_key(keyfile, "general", "logfile", NULL)) { + config->log_filepath = + g_key_file_get_string(keyfile, "general", "logfile", &gerr); + } + if (g_key_file_has_key(keyfile, "general", "pidfile", NULL)) { + config->pid_filepath = + g_key_file_get_string(keyfile, "general", "pidfile", &gerr); + } +#ifdef CONFIG_FSFREEZE + if (g_key_file_has_key(keyfile, "general", "fsfreeze-hook", NULL)) { + config->fsfreeze_hook = + g_key_file_get_string(keyfile, + "general", "fsfreeze-hook", &gerr); + } +#endif + if (g_key_file_has_key(keyfile, "general", "statedir", NULL)) { + config->state_dir = + g_key_file_get_string(keyfile, "general", "statedir", &gerr); + } + if (g_key_file_has_key(keyfile, "general", "verbose", NULL) && + g_key_file_get_boolean(keyfile, "general", "verbose", &gerr)) { + /* enable all log levels */ + config->log_level = G_LOG_LEVEL_MASK; + } + if (g_key_file_has_key(keyfile, "general", "blacklist", NULL)) { + config->bliststr = + g_key_file_get_string(keyfile, "general", "blacklist", &gerr); + config->blacklist = g_list_concat(config->blacklist, + split_list(config->bliststr, ",")); + } + +end: + g_key_file_free(keyfile); + if (gerr && + !(gerr->domain == G_FILE_ERROR && gerr->code == G_FILE_ERROR_NOENT)) { + g_critical("error loading configuration from path: %s, %s", + QGA_CONF_DEFAULT, gerr->message); + exit(EXIT_FAILURE); + } + g_clear_error(&gerr); +} + +static gchar *list_join(GList *list, const gchar separator) +{ + GString *str = g_string_new(""); + + while (list) { + str = g_string_append(str, (gchar *)list->data); + list = g_list_next(list); + if (list) { + str = g_string_append_c(str, separator); + } + } + + return g_string_free(str, FALSE); +} + +static void config_dump(GAConfig *config) +{ + GError *error = NULL; + GKeyFile *keyfile; + gchar *tmp; + + keyfile = g_key_file_new(); + g_assert(keyfile); + + g_key_file_set_boolean(keyfile, "general", "daemon", config->daemonize); + g_key_file_set_string(keyfile, "general", "method", config->method); + g_key_file_set_string(keyfile, "general", "path", config->channel_path); + if (config->log_filepath) { + g_key_file_set_string(keyfile, "general", "logfile", + config->log_filepath); + } + g_key_file_set_string(keyfile, "general", "pidfile", config->pid_filepath); +#ifdef CONFIG_FSFREEZE + if (config->fsfreeze_hook) { + g_key_file_set_string(keyfile, "general", "fsfreeze-hook", + config->fsfreeze_hook); + } #endif + g_key_file_set_string(keyfile, "general", "statedir", config->state_dir); + g_key_file_set_boolean(keyfile, "general", "verbose", + config->log_level == G_LOG_LEVEL_MASK); + tmp = list_join(config->blacklist, ','); + g_key_file_set_string(keyfile, "general", "blacklist", tmp); + g_free(tmp); + + tmp = g_key_file_to_data(keyfile, NULL, &error); + printf("%s", tmp); + + g_free(tmp); + g_key_file_free(keyfile); +} + +static void config_parse(GAConfig *config, int argc, char **argv) +{ + const char *sopt = "hVvdm:p:l:f:F::b:s:t:D"; + int opt_ind = 0, ch; const struct option lopt[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, + { "dump-conf", 0, NULL, 'D' }, { "logfile", 1, NULL, 'l' }, { "pidfile", 1, NULL, 'f' }, #ifdef CONFIG_FSFREEZE @@ -953,141 +1074,113 @@ int main(int argc, char **argv) { "statedir", 1, NULL, 't' }, { NULL, 0, NULL, 0 } }; - int opt_ind = 0, ch, daemonize = 0, i, j, len; - GLogLevelFlags log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL; - GList *blacklist = NULL; - GAState *s; - - module_call_init(MODULE_INIT_QAPI); - - init_dfl_pathnames(); - pid_filepath = dfl_pathnames.pidfile; - state_dir = dfl_pathnames.state_dir; while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { switch (ch) { case 'm': - method = optarg; + g_free(config->method); + config->method = g_strdup(optarg); break; case 'p': - path = optarg; + g_free(config->channel_path); + config->channel_path = g_strdup(optarg); break; case 'l': - log_filepath = optarg; + g_free(config->log_filepath); + config->log_filepath = g_strdup(optarg); break; case 'f': - pid_filepath = optarg; + g_free(config->pid_filepath); + config->pid_filepath = g_strdup(optarg); break; #ifdef CONFIG_FSFREEZE case 'F': - fsfreeze_hook = optarg ? optarg : QGA_FSFREEZE_HOOK_DEFAULT; + g_free(config->fsfreeze_hook); + config->fsfreeze_hook = g_strdup(optarg ?: QGA_FSFREEZE_HOOK_DEFAULT); break; #endif case 't': - state_dir = optarg; - break; + g_free(config->state_dir); + config->state_dir = g_strdup(optarg); + break; case 'v': /* enable all log levels */ - log_level = G_LOG_LEVEL_MASK; + config->log_level = G_LOG_LEVEL_MASK; break; case 'V': printf("QEMU Guest Agent %s\n", QEMU_VERSION); - return 0; + exit(EXIT_SUCCESS); case 'd': - daemonize = 1; + config->daemonize = 1; + break; + case 'D': + config->dumpconf = 1; break; case 'b': { if (is_help_option(optarg)) { qmp_for_each_command(ga_print_cmd, NULL); - return 0; - } - for (j = 0, i = 0, len = strlen(optarg); i < len; i++) { - if (optarg[i] == ',') { - optarg[i] = 0; - blacklist = g_list_append(blacklist, &optarg[j]); - j = i + 1; - } - } - if (j < i) { - blacklist = g_list_append(blacklist, &optarg[j]); + exit(EXIT_SUCCESS); } + config->blacklist = g_list_concat(config->blacklist, + split_list(optarg, ",")); break; } #ifdef _WIN32 case 's': - service = optarg; - if (strcmp(service, "install") == 0) { - const char *fixed_state_dir; - - /* If the user passed the "-t" option, we save that state dir - * in the service. Otherwise we let the service fetch the state - * dir from the environment when it starts. - */ - fixed_state_dir = (state_dir == dfl_pathnames.state_dir) ? - NULL : - state_dir; + config->service = optarg; + if (strcmp(config->service, "install") == 0) { if (ga_install_vss_provider()) { - return EXIT_FAILURE; + exit(EXIT_FAILURE); } - if (ga_install_service(path, log_filepath, fixed_state_dir)) { - return EXIT_FAILURE; + if (ga_install_service(config->channel_path, + config->log_filepath, config->state_dir)) { + exit(EXIT_FAILURE); } - return 0; - } else if (strcmp(service, "uninstall") == 0) { + exit(EXIT_SUCCESS); + } else if (strcmp(config->service, "uninstall") == 0) { ga_uninstall_vss_provider(); - return ga_uninstall_service(); - } else if (strcmp(service, "vss-install") == 0) { + exit(ga_uninstall_service()); + } else if (strcmp(config->service, "vss-install") == 0) { if (ga_install_vss_provider()) { - return EXIT_FAILURE; + exit(EXIT_FAILURE); } - return EXIT_SUCCESS; - } else if (strcmp(service, "vss-uninstall") == 0) { + exit(EXIT_SUCCESS); + } else if (strcmp(config->service, "vss-uninstall") == 0) { ga_uninstall_vss_provider(); - return EXIT_SUCCESS; + exit(EXIT_SUCCESS); } else { printf("Unknown service command.\n"); - return EXIT_FAILURE; + exit(EXIT_FAILURE); } break; #endif case 'h': usage(argv[0]); - return 0; + exit(EXIT_SUCCESS); case '?': g_print("Unknown option, try '%s --help' for more information.\n", argv[0]); - return EXIT_FAILURE; + exit(EXIT_FAILURE); } } +} -#ifdef _WIN32 - /* On win32 the state directory is application specific (be it the default - * or a user override). We got past the command line parsing; let's create - * the directory (with any intermediate directories). If we run into an - * error later on, we won't try to clean up the directory, it is considered - * persistent. - */ - if (g_mkdir_with_parents(state_dir, S_IRWXU) == -1) { - g_critical("unable to create (an ancestor of) the state directory" - " '%s': %s", state_dir, strerror(errno)); - return EXIT_FAILURE; - } -#endif - - s = g_malloc0(sizeof(GAState)); - s->log_level = log_level; - s->log_file = stderr; +static void config_free(GAConfig *config) +{ + g_free(config->method); + g_free(config->log_filepath); + g_free(config->pid_filepath); + g_free(config->state_dir); + g_free(config->channel_path); + g_free(config->bliststr); #ifdef CONFIG_FSFREEZE - s->fsfreeze_hook = fsfreeze_hook; + g_free(config->fsfreeze_hook); #endif - g_log_set_default_handler(ga_log, s); - g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR); - ga_enable_logging(s); - s->state_filepath_isfrozen = g_strdup_printf("%s/qga.state.isfrozen", - state_dir); - s->pstate_filepath = g_strdup_printf("%s/qga.state", state_dir); - s->frozen = false; + g_free(config); +} +static bool check_is_frozen(GAState *s) +{ #ifndef _WIN32 /* check if a previous instance of qemu-ga exited with filesystems' state * marked as frozen. this could be a stale value (a non-qemu-ga process @@ -1113,32 +1206,56 @@ int main(int argc, char **argv) " guest-fsfreeze-thaw is issued, or filesystems are" " manually unfrozen and the file %s is removed", s->state_filepath_isfrozen); - s->frozen = true; + return true; + } +#endif + return false; +} + +static int run_agent(GAState *s, GAConfig *config) +{ + ga_state = s; + + g_log_set_default_handler(ga_log, s); + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR); + ga_enable_logging(s); + +#ifdef _WIN32 + /* On win32 the state directory is application specific (be it the default + * or a user override). We got past the command line parsing; let's create + * the directory (with any intermediate directories). If we run into an + * error later on, we won't try to clean up the directory, it is considered + * persistent. + */ + if (g_mkdir_with_parents(config->state_dir, S_IRWXU) == -1) { + g_critical("unable to create (an ancestor of) the state directory" + " '%s': %s", config->state_dir, strerror(errno)); + return EXIT_FAILURE; } #endif if (ga_is_frozen(s)) { - if (daemonize) { + if (config->daemonize) { /* delay opening/locking of pidfile till filesystems are unfrozen */ - s->deferred_options.pid_filepath = pid_filepath; + s->deferred_options.pid_filepath = config->pid_filepath; become_daemon(NULL); } - if (log_filepath) { + if (config->log_filepath) { /* delay opening the log file till filesystems are unfrozen */ - s->deferred_options.log_filepath = log_filepath; + s->deferred_options.log_filepath = config->log_filepath; } ga_disable_logging(s); qmp_for_each_command(ga_disable_non_whitelisted, NULL); } else { - if (daemonize) { - become_daemon(pid_filepath); + if (config->daemonize) { + become_daemon(config->pid_filepath); } - if (log_filepath) { - FILE *log_file = ga_open_logfile(log_filepath); + if (config->log_filepath) { + FILE *log_file = ga_open_logfile(config->log_filepath); if (!log_file) { g_critical("unable to open specified log file: %s", strerror(errno)); - goto out_bad; + return EXIT_FAILURE; } s->log_file = log_file; } @@ -1149,17 +1266,18 @@ int main(int argc, char **argv) s->pstate_filepath, ga_is_frozen(s))) { g_critical("failed to load persistent state"); - goto out_bad; + return EXIT_FAILURE; } - blacklist = ga_command_blacklist_init(blacklist); - if (blacklist) { - s->blacklist = blacklist; + config->blacklist = ga_command_blacklist_init(config->blacklist); + if (config->blacklist) { + GList *l = config->blacklist; + s->blacklist = config->blacklist; do { - g_debug("disabling command: %s", (char *)blacklist->data); - qmp_disable_command(blacklist->data); - blacklist = g_list_next(blacklist); - } while (blacklist); + g_debug("disabling command: %s", (char *)l->data); + qmp_disable_command(l->data); + l = g_list_next(l); + } while (l); } s->command_state = ga_command_state_new(); ga_command_state_init(s, s->command_state); @@ -1169,19 +1287,19 @@ int main(int argc, char **argv) #ifndef _WIN32 if (!register_signal_handlers()) { g_critical("failed to register signal handlers"); - goto out_bad; + return EXIT_FAILURE; } #endif s->main_loop = g_main_loop_new(NULL, false); - if (!channel_init(ga_state, method, path)) { + if (!channel_init(ga_state, config->method, config->channel_path)) { g_critical("failed to initialize guest agent channel"); - goto out_bad; + return EXIT_FAILURE; } #ifndef _WIN32 g_main_loop_run(ga_state->main_loop); #else - if (daemonize) { + if (config->daemonize) { SERVICE_TABLE_ENTRY service_table[] = { { (char *)QGA_SERVICE_NAME, service_main }, { NULL, NULL } }; StartServiceCtrlDispatcher(service_table); @@ -1190,17 +1308,87 @@ int main(int argc, char **argv) } #endif - ga_command_state_cleanup_all(ga_state->command_state); - ga_channel_free(ga_state->channel); + return EXIT_SUCCESS; +} + +static void free_blacklist_entry(gpointer entry, gpointer unused) +{ + g_free(entry); +} + +int main(int argc, char **argv) +{ + int ret = EXIT_SUCCESS; + GAState *s = g_new0(GAState, 1); + GAConfig *config = g_new0(GAConfig, 1); + + config->log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL; - if (daemonize) { - unlink(pid_filepath); + module_call_init(MODULE_INIT_QAPI); + + init_dfl_pathnames(); + config_load(config); + config_parse(config, argc, argv); + + if (config->pid_filepath == NULL) { + config->pid_filepath = g_strdup(dfl_pathnames.pidfile); } - return 0; -out_bad: - if (daemonize) { - unlink(pid_filepath); + if (config->state_dir == NULL) { + config->state_dir = g_strdup(dfl_pathnames.state_dir); + } + + if (config->method == NULL) { + config->method = g_strdup("virtio-serial"); + } + + if (config->channel_path == NULL) { + if (strcmp(config->method, "virtio-serial") == 0) { + /* try the default path for the virtio-serial port */ + config->channel_path = g_strdup(QGA_VIRTIO_PATH_DEFAULT); + } else if (strcmp(config->method, "isa-serial") == 0) { + /* try the default path for the serial port - COM1 */ + config->channel_path = g_strdup(QGA_SERIAL_PATH_DEFAULT); + } else { + g_critical("must specify a path for this channel"); + ret = EXIT_FAILURE; + goto end; + } + } + + s->log_level = config->log_level; + s->log_file = stderr; +#ifdef CONFIG_FSFREEZE + s->fsfreeze_hook = config->fsfreeze_hook; +#endif + s->pstate_filepath = g_strdup_printf("%s/qga.state", config->state_dir); + s->state_filepath_isfrozen = g_strdup_printf("%s/qga.state.isfrozen", + config->state_dir); + s->frozen = check_is_frozen(s); + + if (config->dumpconf) { + config_dump(config); + goto end; + } + + ret = run_agent(s, config); + +end: + if (s->command_state) { + ga_command_state_cleanup_all(s->command_state); + } + if (s->channel) { + ga_channel_free(s->channel); + } + g_list_foreach(config->blacklist, free_blacklist_entry, NULL); + g_free(s->pstate_filepath); + g_free(s->state_filepath_isfrozen); + + if (config->daemonize) { + unlink(config->pid_filepath); } - return EXIT_FAILURE; + + config_free(config); + + return ret; } diff --git a/qemu/qga/qapi-schema.json b/qemu/qga/qapi-schema.json index 18e3cc37d..c21f3084d 100644 --- a/qemu/qga/qapi-schema.json +++ b/qemu/qga/qapi-schema.json @@ -12,6 +12,7 @@ ## ## +# @guest-sync-delimited: # # Echo back a unique integer value, and prepend to response a # leading sentinel byte (0xFF) the client can check scan for. @@ -41,7 +42,7 @@ # Returns: The unique integer id passed in by the client # # Since: 1.1 -# ## +## { 'command': 'guest-sync-delimited', 'data': { 'id': 'int' }, 'returns': 'int' } @@ -312,25 +313,54 @@ { 'struct': 'GuestFileSeek', 'data': { 'position': 'int', 'eof': 'bool' } } +## +# @QGASeek: +# +# Symbolic names for use in @guest-file-seek +# +# @set: Set to the specified offset (same effect as 'whence':0) +# @cur: Add offset to the current location (same effect as 'whence':1) +# @end: Add offset to the end of the file (same effect as 'whence':2) +# +# Since: 2.6 +## +{ 'enum': 'QGASeek', 'data': [ 'set', 'cur', 'end' ] } + +## +# @GuestFileWhence: +# +# Controls the meaning of offset to @guest-file-seek. +# +# @value: Integral value (0 for set, 1 for cur, 2 for end), available +# for historical reasons, and might differ from the host's or +# guest's SEEK_* values (since: 0.15) +# @name: Symbolic name, and preferred interface +# +# Since: 2.6 +## +{ 'alternate': 'GuestFileWhence', + 'data': { 'value': 'int', 'name': 'QGASeek' } } + ## # @guest-file-seek: # # Seek to a position in the file, as with fseek(), and return the # current file position afterward. Also encapsulates ftell()'s -# functionality, just Set offset=0, whence=SEEK_CUR. +# functionality, with offset=0 and whence=1. # # @handle: filehandle returned by guest-file-open # # @offset: bytes to skip over in the file stream # -# @whence: SEEK_SET, SEEK_CUR, or SEEK_END, as with fseek() +# @whence: Symbolic or numeric code for interpreting offset # # Returns: @GuestFileSeek on success. # # Since: 0.15.0 ## { 'command': 'guest-file-seek', - 'data': { 'handle': 'int', 'offset': 'int', 'whence': 'int' }, + 'data': { 'handle': 'int', 'offset': 'int', + 'whence': 'GuestFileWhence' }, 'returns': 'GuestFileSeek' } ## @@ -793,7 +823,7 @@ # scheme. Refer to the documentation of the guest operating system # in question to determine what is supported. # -# Note all guest operating systems will support use of the +# Not all guest operating systems will support use of the # @crypted flag, as they may require the clear-text password # # The @password parameter must always be base64 encoded before @@ -929,3 +959,70 @@ ## { 'command': 'guest-get-memory-block-info', 'returns': 'GuestMemoryBlockInfo' } + +# @GuestExecStatus: +# +# @exited: true if process has already terminated. +# @exitcode: #optional process exit code if it was normally terminated. +# @signal: #optional signal number (linux) or unhandled exception code +# (windows) if the process was abnormally terminated. +# @out-data: #optional base64-encoded stdout of the process +# @err-data: #optional base64-encoded stderr of the process +# Note: @out-data and @err-data are present only +# if 'capture-output' was specified for 'guest-exec' +# @out-truncated: #optional true if stdout was not fully captured +# due to size limitation. +# @err-truncated: #optional true if stderr was not fully captured +# due to size limitation. +# +# Since: 2.5 +## +{ 'struct': 'GuestExecStatus', + 'data': { 'exited': 'bool', '*exitcode': 'int', '*signal': 'int', + '*out-data': 'str', '*err-data': 'str', + '*out-truncated': 'bool', '*err-truncated': 'bool' }} +## +# @guest-exec-status +# +# Check status of process associated with PID retrieved via guest-exec. +# Reap the process and associated metadata if it has exited. +# +# @pid: pid returned from guest-exec +# +# Returns: GuestExecStatus on success. +# +# Since 2.5 +## +{ 'command': 'guest-exec-status', + 'data': { 'pid': 'int' }, + 'returns': 'GuestExecStatus' } + +## +# @GuestExec: +# @pid: pid of child process in guest OS +# +#Since: 2.5 +## +{ 'struct': 'GuestExec', + 'data': { 'pid': 'int'} } + +## +# @guest-exec: +# +# Execute a command in the guest +# +# @path: path or executable name to execute +# @arg: #optional argument list to pass to executable +# @env: #optional environment variables to pass to executable +# @input-data: #optional data to be passed to process stdin (base64 encoded) +# @capture-output: #optional bool flag to enable capture of +# stdout/stderr of running process. defaults to false. +# +# Returns: PID on success. +# +# Since: 2.5 +## +{ 'command': 'guest-exec', + 'data': { 'path': 'str', '*arg': ['str'], '*env': ['str'], + '*input-data': 'str', '*capture-output': 'bool' }, + 'returns': 'GuestExec' } diff --git a/qemu/qga/service-win32.c b/qemu/qga/service-win32.c index aef41f04f..72437587b 100644 --- a/qemu/qga/service-win32.c +++ b/qemu/qga/service-win32.c @@ -10,8 +10,7 @@ * 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 -#include +#include "qemu/osdep.h" #include #include #include "qga/service-win32.h" diff --git a/qemu/qga/vss-win32.c b/qemu/qga/vss-win32.c index 0e4095736..9a0e46356 100644 --- a/qemu/qga/vss-win32.c +++ b/qemu/qga/vss-win32.c @@ -10,7 +10,7 @@ * See the COPYING file in the top-level directory. */ -#include +#include "qemu/osdep.h" #include #include "qga/guest-agent-core.h" #include "qga/vss-win32.h" @@ -150,11 +150,11 @@ void qga_vss_fsfreeze(int *nr_volume, Error **errp, bool freeze) const char *func_name = freeze ? "requester_freeze" : "requester_thaw"; QGAVSSRequesterFunc func; ErrorSet errset = { - .error_set = (ErrorSetFunc)error_set_win32, - .errp = (void **)errp, - .err_class = ERROR_CLASS_GENERIC_ERROR + .error_setg_win32_wrapper = error_setg_win32_internal, + .errp = errp, }; + g_assert(errp); /* requester.cpp requires it */ func = (QGAVSSRequesterFunc)GetProcAddress(provider_lib, func_name); if (!func) { error_setg_win32(errp, GetLastError(), "failed to load %s from %s", diff --git a/qemu/qga/vss-win32.h b/qemu/qga/vss-win32.h index 298927dfa..4d1d15081 100644 --- a/qemu/qga/vss-win32.h +++ b/qemu/qga/vss-win32.h @@ -13,7 +13,6 @@ #ifndef VSS_WIN32_H #define VSS_WIN32_H -#include "qapi/error.h" bool vss_init(bool init_requester); void vss_deinit(bool deinit_requester); diff --git a/qemu/qga/vss-win32/install.cpp b/qemu/qga/vss-win32/install.cpp index b0e4426c7..cd9cdb4a2 100644 --- a/qemu/qga/vss-win32/install.cpp +++ b/qemu/qga/vss-win32/install.cpp @@ -10,8 +10,7 @@ * See the COPYING file in the top-level directory. */ -#include -#include +#include "qemu/osdep.h" #include "vss-common.h" #include "inc/win2003/vscoordint.h" diff --git a/qemu/qga/vss-win32/provider.cpp b/qemu/qga/vss-win32/provider.cpp index d5129f8f6..d977393e3 100644 --- a/qemu/qga/vss-win32/provider.cpp +++ b/qemu/qga/vss-win32/provider.cpp @@ -10,7 +10,7 @@ * See the COPYING file in the top-level directory. */ -#include +#include "qemu/osdep.h" #include "vss-common.h" #include "inc/win2003/vscoordint.h" #include "inc/win2003/vsprov.h" diff --git a/qemu/qga/vss-win32/requester.cpp b/qemu/qga/vss-win32/requester.cpp index 922e74ddf..889052ded 100644 --- a/qemu/qga/vss-win32/requester.cpp +++ b/qemu/qga/vss-win32/requester.cpp @@ -10,10 +10,9 @@ * See the COPYING file in the top-level directory. */ -#include +#include "qemu/osdep.h" #include "vss-common.h" #include "requester.h" -#include "assert.h" #include "inc/win2003/vswriter.h" #include "inc/win2003/vsbackup.h" @@ -23,10 +22,12 @@ /* Call QueryStatus every 10 ms while waiting for frozen event */ #define VSS_TIMEOUT_EVENT_MSEC 10 -#define err_set(e, err, fmt, ...) \ - ((e)->error_set((e)->errp, err, (e)->err_class, fmt, ## __VA_ARGS__)) +#define err_set(e, err, fmt, ...) \ + ((e)->error_setg_win32_wrapper((e)->errp, __FILE__, __LINE__, __func__, \ + err, fmt, ## __VA_ARGS__)) +/* Bad idea, works only when (e)->errp != NULL: */ #define err_is_set(e) ((e)->errp && *(e)->errp) - +/* To lift this restriction, error_propagate(), like we do in QEMU code */ /* Handle to VSSAPI.DLL */ static HMODULE hLib; diff --git a/qemu/qga/vss-win32/requester.h b/qemu/qga/vss-win32/requester.h index 374f9b8d1..2a39d734a 100644 --- a/qemu/qga/vss-win32/requester.h +++ b/qemu/qga/vss-win32/requester.h @@ -14,19 +14,21 @@ #define VSS_WIN32_REQUESTER_H #include /* STDAPI */ -#include "qemu/compiler.h" #ifdef __cplusplus extern "C" { #endif +struct Error; + /* Callback to set Error; used to avoid linking glib to the DLL */ -typedef void (*ErrorSetFunc)(void **errp, int win32_err, int err_class, - const char *fmt, ...) GCC_FMT_ATTR(4, 5); +typedef void (*ErrorSetFunc)(struct Error **errp, + const char *src, int line, const char *func, + int win32_err, const char *fmt, ...) + GCC_FMT_ATTR(6, 7); typedef struct ErrorSet { - ErrorSetFunc error_set; - void **errp; - int err_class; + ErrorSetFunc error_setg_win32_wrapper; + struct Error **errp; /* restriction: must not be null */ } ErrorSet; STDAPI requester_init(void); diff --git a/qemu/qga/vss-win32/vss-common.h b/qemu/qga/vss-win32/vss-common.h index ce14e1429..91dae0c38 100644 --- a/qemu/qga/vss-win32/vss-common.h +++ b/qemu/qga/vss-win32/vss-common.h @@ -14,7 +14,6 @@ #define VSS_WIN32_H #define __MIDL_user_allocate_free_DEFINED__ -#include "config-host.h" #include #include diff --git a/qemu/qjson.c b/qemu/qjson.c index e478802a4..b65ca6ee5 100644 --- a/qemu/qjson.c +++ b/qemu/qjson.c @@ -11,8 +11,8 @@ * */ +#include "qemu/osdep.h" #include -#include #include #include #include diff --git a/qemu/qmp-commands.hx b/qemu/qmp-commands.hx index ba630b1e7..de896a5a3 100644 --- a/qemu/qmp-commands.hx +++ b/qemu/qmp-commands.hx @@ -63,7 +63,7 @@ EQMP { .name = "quit", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_quit, + .mhandler.cmd_new = qmp_marshal_quit, }, SQMP @@ -84,7 +84,7 @@ EQMP { .name = "eject", .args_type = "force:-f,device:B", - .mhandler.cmd_new = qmp_marshal_input_eject, + .mhandler.cmd_new = qmp_marshal_eject, }, SQMP @@ -110,7 +110,7 @@ EQMP { .name = "change", .args_type = "device:B,target:F,arg:s?", - .mhandler.cmd_new = qmp_marshal_input_change, + .mhandler.cmd_new = qmp_marshal_change, }, SQMP @@ -146,7 +146,7 @@ EQMP { .name = "screendump", .args_type = "filename:F", - .mhandler.cmd_new = qmp_marshal_input_screendump, + .mhandler.cmd_new = qmp_marshal_screendump, }, SQMP @@ -169,7 +169,7 @@ EQMP { .name = "stop", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_stop, + .mhandler.cmd_new = qmp_marshal_stop, }, SQMP @@ -190,7 +190,7 @@ EQMP { .name = "cont", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_cont, + .mhandler.cmd_new = qmp_marshal_cont, }, SQMP @@ -211,7 +211,7 @@ EQMP { .name = "system_wakeup", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_system_wakeup, + .mhandler.cmd_new = qmp_marshal_system_wakeup, }, SQMP @@ -232,7 +232,7 @@ EQMP { .name = "system_reset", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_system_reset, + .mhandler.cmd_new = qmp_marshal_system_reset, }, SQMP @@ -253,7 +253,7 @@ EQMP { .name = "system_powerdown", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_system_powerdown, + .mhandler.cmd_new = qmp_marshal_system_powerdown, }, SQMP @@ -310,7 +310,7 @@ EQMP { .name = "device_del", .args_type = "id:s", - .mhandler.cmd_new = qmp_marshal_input_device_del, + .mhandler.cmd_new = qmp_marshal_device_del, }, SQMP @@ -321,19 +321,24 @@ Remove a device. Arguments: -- "id": the device's ID (json-string) +- "id": the device's ID or QOM path (json-string) Example: -> { "execute": "device_del", "arguments": { "id": "net1" } } <- { "return": {} } +Example: + +-> { "execute": "device_del", "arguments": { "id": "/machine/peripheral-anon/device[0]" } } +<- { "return": {} } + EQMP { .name = "send-key", .args_type = "keys:q,hold-time:i?", - .mhandler.cmd_new = qmp_marshal_input_send_key, + .mhandler.cmd_new = qmp_marshal_send_key, }, SQMP @@ -364,7 +369,7 @@ EQMP { .name = "cpu", .args_type = "index:i", - .mhandler.cmd_new = qmp_marshal_input_cpu, + .mhandler.cmd_new = qmp_marshal_cpu, }, SQMP @@ -389,7 +394,7 @@ EQMP { .name = "cpu-add", .args_type = "id:i", - .mhandler.cmd_new = qmp_marshal_input_cpu_add, + .mhandler.cmd_new = qmp_marshal_cpu_add, }, SQMP @@ -412,7 +417,7 @@ EQMP { .name = "memsave", .args_type = "val:l,size:i,filename:s,cpu:i?", - .mhandler.cmd_new = qmp_marshal_input_memsave, + .mhandler.cmd_new = qmp_marshal_memsave, }, SQMP @@ -441,7 +446,7 @@ EQMP { .name = "pmemsave", .args_type = "val:l,size:i,filename:s", - .mhandler.cmd_new = qmp_marshal_input_pmemsave, + .mhandler.cmd_new = qmp_marshal_pmemsave, }, SQMP @@ -469,7 +474,7 @@ EQMP { .name = "inject-nmi", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_inject_nmi, + .mhandler.cmd_new = qmp_marshal_inject_nmi, }, SQMP @@ -492,7 +497,7 @@ EQMP { .name = "ringbuf-write", .args_type = "device:s,data:s,format:s?", - .mhandler.cmd_new = qmp_marshal_input_ringbuf_write, + .mhandler.cmd_new = qmp_marshal_ringbuf_write, }, SQMP @@ -507,8 +512,6 @@ Arguments: - "data": data to write (json-string) - "format": data format (json-string, optional) - Possible values: "utf8" (default), "base64" - Bug: invalid base64 is currently not rejected. - Whitespace *is* invalid. Example: @@ -523,7 +526,7 @@ EQMP { .name = "ringbuf-read", .args_type = "device:s,size:i,format:s?", - .mhandler.cmd_new = qmp_marshal_input_ringbuf_read, + .mhandler.cmd_new = qmp_marshal_ringbuf_read, }, SQMP @@ -559,7 +562,7 @@ EQMP { .name = "xen-save-devices-state", .args_type = "filename:F", - .mhandler.cmd_new = qmp_marshal_input_xen_save_devices_state, + .mhandler.cmd_new = qmp_marshal_xen_save_devices_state, }, SQMP @@ -586,7 +589,7 @@ EQMP { .name = "xen-set-global-dirty-log", .args_type = "enable:b", - .mhandler.cmd_new = qmp_marshal_input_xen_set_global_dirty_log, + .mhandler.cmd_new = qmp_marshal_xen_set_global_dirty_log, }, SQMP @@ -610,7 +613,7 @@ EQMP { .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", - .mhandler.cmd_new = qmp_marshal_input_migrate, + .mhandler.cmd_new = qmp_marshal_migrate, }, SQMP @@ -643,7 +646,7 @@ EQMP { .name = "migrate_cancel", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_migrate_cancel, + .mhandler.cmd_new = qmp_marshal_migrate_cancel, }, SQMP @@ -664,7 +667,7 @@ EQMP { .name = "migrate-incoming", .args_type = "uri:s", - .mhandler.cmd_new = qmp_marshal_input_migrate_incoming, + .mhandler.cmd_new = qmp_marshal_migrate_incoming, }, SQMP @@ -692,7 +695,7 @@ EQMP { .name = "migrate-set-cache-size", .args_type = "value:o", - .mhandler.cmd_new = qmp_marshal_input_migrate_set_cache_size, + .mhandler.cmd_new = qmp_marshal_migrate_set_cache_size, }, SQMP @@ -712,10 +715,29 @@ Example: <- { "return": {} } EQMP + { + .name = "migrate-start-postcopy", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_migrate_start_postcopy, + }, + +SQMP +migrate-start-postcopy +---------------------- + +Switch an in-progress migration to postcopy mode. Ignored after the end of +migration (or once already in postcopy). + +Example: +-> { "execute": "migrate-start-postcopy" } +<- { "return": {} } + +EQMP + { .name = "query-migrate-cache-size", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_migrate_cache_size, + .mhandler.cmd_new = qmp_marshal_query_migrate_cache_size, }, SQMP @@ -737,7 +759,7 @@ EQMP { .name = "migrate_set_speed", .args_type = "value:o", - .mhandler.cmd_new = qmp_marshal_input_migrate_set_speed, + .mhandler.cmd_new = qmp_marshal_migrate_set_speed, }, SQMP @@ -760,7 +782,7 @@ EQMP { .name = "migrate_set_downtime", .args_type = "value:T", - .mhandler.cmd_new = qmp_marshal_input_migrate_set_downtime, + .mhandler.cmd_new = qmp_marshal_migrate_set_downtime, }, SQMP @@ -785,7 +807,7 @@ EQMP .args_type = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?", .params = "protocol hostname port tls-port cert-subject", .help = "set migration information for remote display", - .mhandler.cmd_new = qmp_marshal_input_client_migrate_info, + .mhandler.cmd_new = qmp_marshal_client_migrate_info, }, SQMP @@ -816,10 +838,10 @@ EQMP { .name = "dump-guest-memory", - .args_type = "paging:b,protocol:s,begin:i?,end:i?,format:s?", - .params = "-p protocol [begin] [length] [format]", + .args_type = "paging:b,protocol:s,detach:b?,begin:i?,end:i?,format:s?", + .params = "-p protocol [-d] [begin] [length] [format]", .help = "dump guest memory to file", - .mhandler.cmd_new = qmp_marshal_input_dump_guest_memory, + .mhandler.cmd_new = qmp_marshal_dump_guest_memory, }, SQMP @@ -833,6 +855,9 @@ Arguments: - "paging": do paging to get guest's memory mapping (json-bool) - "protocol": destination file(started with "file:") or destination file descriptor (started with "fd:") (json-string) +- "detach": if specified, command will return immediately, without waiting + for the dump to finish. The user can track progress using + "query-dump". (json-bool) - "begin": the starting physical address. It's optional, and should be specified with length together (json-int) - "length": the memory size, in bytes. It's optional, and should be specified @@ -855,7 +880,7 @@ EQMP { .name = "query-dump-guest-memory-capability", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_dump_guest_memory_capability, + .mhandler.cmd_new = qmp_marshal_query_dump_guest_memory_capability, }, SQMP @@ -870,6 +895,55 @@ Example: <- { "return": { "formats": ["elf", "kdump-zlib", "kdump-lzo", "kdump-snappy"] } +EQMP + + { + .name = "query-dump", + .args_type = "", + .params = "", + .help = "query background dump status", + .mhandler.cmd_new = qmp_marshal_query_dump, + }, + +SQMP +query-dump +---------- + +Query background dump status. + +Arguments: None. + +Example: + +-> { "execute": "query-dump" } +<- { "return": { "status": "active", "completed": 1024000, + "total": 2048000 } } + +EQMP + +#if defined TARGET_S390X + { + .name = "dump-skeys", + .args_type = "filename:F", + .mhandler.cmd_new = qmp_marshal_dump_skeys, + }, +#endif + +SQMP +dump-skeys +---------- + +Save guest storage keys to file. + +Arguments: + +- "filename": file path (json-string) + +Example: + +-> { "execute": "dump-skeys", "arguments": { "filename": "/tmp/skeys" } } +<- { "return": {} } + EQMP { @@ -892,7 +966,9 @@ Arguments: Example: --> { "execute": "netdev_add", "arguments": { "type": "user", "id": "netdev1" } } +-> { "execute": "netdev_add", + "arguments": { "type": "user", "id": "netdev1", + "dnssearch": "example.org" } } <- { "return": {} } Note: The supported device options are the same ones supported by the '-netdev' @@ -904,7 +980,7 @@ EQMP { .name = "netdev_del", .args_type = "id:s", - .mhandler.cmd_new = qmp_marshal_input_netdev_del, + .mhandler.cmd_new = qmp_marshal_netdev_del, }, SQMP @@ -928,7 +1004,7 @@ EQMP { .name = "object-add", .args_type = "qom-type:s,id:s,props:q?", - .mhandler.cmd_new = qmp_object_add, + .mhandler.cmd_new = qmp_marshal_object_add, }, SQMP @@ -954,7 +1030,7 @@ EQMP { .name = "object-del", .args_type = "id:s", - .mhandler.cmd_new = qmp_marshal_input_object_del, + .mhandler.cmd_new = qmp_marshal_object_del, }, SQMP @@ -979,7 +1055,7 @@ EQMP { .name = "block_resize", .args_type = "device:s?,node-name:s?,size:o", - .mhandler.cmd_new = qmp_marshal_input_block_resize, + .mhandler.cmd_new = qmp_marshal_block_resize, }, SQMP @@ -1004,7 +1080,7 @@ EQMP { .name = "block-stream", .args_type = "device:B,base:s?,speed:o?,backing-file:s?,on-error:s?", - .mhandler.cmd_new = qmp_marshal_input_block_stream, + .mhandler.cmd_new = qmp_marshal_block_stream, }, SQMP @@ -1047,7 +1123,7 @@ EQMP { .name = "block-commit", .args_type = "device:B,base:s?,top:s?,backing-file:s?,speed:o?", - .mhandler.cmd_new = qmp_marshal_input_block_commit, + .mhandler.cmd_new = qmp_marshal_block_commit, }, SQMP @@ -1111,7 +1187,7 @@ EQMP .name = "drive-backup", .args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?," "bitmap:s?,on-source-error:s?,on-target-error:s?", - .mhandler.cmd_new = qmp_marshal_input_drive_backup, + .mhandler.cmd_new = qmp_marshal_drive_backup, }, SQMP @@ -1165,7 +1241,7 @@ EQMP .name = "blockdev-backup", .args_type = "sync:s,device:B,target:B,speed:i?," "on-source-error:s?,on-target-error:s?", - .mhandler.cmd_new = qmp_marshal_input_blockdev_backup, + .mhandler.cmd_new = qmp_marshal_blockdev_backup, }, SQMP @@ -1205,44 +1281,55 @@ EQMP { .name = "block-job-set-speed", .args_type = "device:B,speed:o", - .mhandler.cmd_new = qmp_marshal_input_block_job_set_speed, + .mhandler.cmd_new = qmp_marshal_block_job_set_speed, }, { .name = "block-job-cancel", .args_type = "device:B,force:b?", - .mhandler.cmd_new = qmp_marshal_input_block_job_cancel, + .mhandler.cmd_new = qmp_marshal_block_job_cancel, }, { .name = "block-job-pause", .args_type = "device:B", - .mhandler.cmd_new = qmp_marshal_input_block_job_pause, + .mhandler.cmd_new = qmp_marshal_block_job_pause, }, { .name = "block-job-resume", .args_type = "device:B", - .mhandler.cmd_new = qmp_marshal_input_block_job_resume, + .mhandler.cmd_new = qmp_marshal_block_job_resume, }, { .name = "block-job-complete", .args_type = "device:B", - .mhandler.cmd_new = qmp_marshal_input_block_job_complete, + .mhandler.cmd_new = qmp_marshal_block_job_complete, }, { .name = "transaction", - .args_type = "actions:q", - .mhandler.cmd_new = qmp_marshal_input_transaction, + .args_type = "actions:q,properties:q?", + .mhandler.cmd_new = qmp_marshal_transaction, }, SQMP transaction ----------- -Atomically operate on one or more block devices. The only supported operations -for now are drive-backup, internal and external snapshotting. A list of -dictionaries is accepted, that contains the actions to be performed. -If there is any failure performing any of the operations, all operations -for the group are abandoned. +Atomically operate on one or more block devices. Operations that are +currently supported: + + - drive-backup + - blockdev-backup + - blockdev-snapshot-sync + - blockdev-snapshot-internal-sync + - abort + - block-dirty-bitmap-add + - block-dirty-bitmap-clear + +Refer to the qemu/qapi-schema.json file for minimum required QEMU +versions for these operations. A list of dictionaries is accepted, +that contains the actions to be performed. If there is any failure +performing any of the operations, all operations for the group are +abandoned. For external snapshots, the dictionary contains the device, the file to use for the new snapshot, and the format. The default format, if not specified, is @@ -1269,8 +1356,12 @@ it later with qemu-img or other command. Arguments: actions array: - - "type": the operation to perform. The only supported - value is "blockdev-snapshot-sync". (json-string) + - "type": the operation to perform (json-string). Possible + values: "drive-backup", "blockdev-backup", + "blockdev-snapshot-sync", + "blockdev-snapshot-internal-sync", + "abort", "block-dirty-bitmap-add", + "block-dirty-bitmap-clear" - "data": a dictionary. The contents depend on the value of "type". When "type" is "blockdev-snapshot-sync": - "device": device name to snapshot (json-string) @@ -1310,7 +1401,7 @@ EQMP { .name = "block-dirty-bitmap-add", .args_type = "node:B,name:s,granularity:i?", - .mhandler.cmd_new = qmp_marshal_input_block_dirty_bitmap_add, + .mhandler.cmd_new = qmp_marshal_block_dirty_bitmap_add, }, SQMP @@ -1338,7 +1429,7 @@ EQMP { .name = "block-dirty-bitmap-remove", .args_type = "node:B,name:s", - .mhandler.cmd_new = qmp_marshal_input_block_dirty_bitmap_remove, + .mhandler.cmd_new = qmp_marshal_block_dirty_bitmap_remove, }, SQMP @@ -1366,7 +1457,7 @@ EQMP { .name = "block-dirty-bitmap-clear", .args_type = "node:B,name:s", - .mhandler.cmd_new = qmp_marshal_input_block_dirty_bitmap_clear, + .mhandler.cmd_new = qmp_marshal_block_dirty_bitmap_clear, }, SQMP @@ -1395,7 +1486,7 @@ EQMP { .name = "blockdev-snapshot-sync", .args_type = "device:s?,node-name:s?,snapshot-file:s,snapshot-node-name:s?,format:s?,mode:s?", - .mhandler.cmd_new = qmp_marshal_input_blockdev_snapshot_sync, + .mhandler.cmd_new = qmp_marshal_blockdev_snapshot_sync, }, SQMP @@ -1426,12 +1517,50 @@ Example: "format": "qcow2" } } <- { "return": {} } +EQMP + + { + .name = "blockdev-snapshot", + .args_type = "node:s,overlay:s", + .mhandler.cmd_new = qmp_marshal_blockdev_snapshot, + }, + +SQMP +blockdev-snapshot +----------------- +Since 2.5 + +Create a snapshot, by installing 'node' as the backing image of +'overlay'. Additionally, if 'node' is associated with a block +device, the block device changes to using 'overlay' as its new active +image. + +Arguments: + +- "node": device that will have a snapshot created (json-string) +- "overlay": device that will have 'node' as its backing image (json-string) + +Example: + +-> { "execute": "blockdev-add", + "arguments": { "options": { "driver": "qcow2", + "node-name": "node1534", + "file": { "driver": "file", + "filename": "hd1.qcow2" }, + "backing": "" } } } + +<- { "return": {} } + +-> { "execute": "blockdev-snapshot", "arguments": { "node": "ide-hd0", + "overlay": "node1534" } } +<- { "return": {} } + EQMP { .name = "blockdev-snapshot-internal-sync", .args_type = "device:B,name:s", - .mhandler.cmd_new = qmp_marshal_input_blockdev_snapshot_internal_sync, + .mhandler.cmd_new = qmp_marshal_blockdev_snapshot_internal_sync, }, SQMP @@ -1461,7 +1590,7 @@ EQMP .name = "blockdev-snapshot-delete-internal-sync", .args_type = "device:B,id:s?,name:s?", .mhandler.cmd_new = - qmp_marshal_input_blockdev_snapshot_delete_internal_sync, + qmp_marshal_blockdev_snapshot_delete_internal_sync, }, SQMP @@ -1505,7 +1634,7 @@ EQMP "on-source-error:s?,on-target-error:s?," "unmap:b?," "granularity:i?,buf-size:i?", - .mhandler.cmd_new = qmp_marshal_input_drive_mirror, + .mhandler.cmd_new = qmp_marshal_drive_mirror, }, SQMP @@ -1533,7 +1662,7 @@ Arguments: - "speed": maximum speed of the streaming job, in bytes per second (json-int) - "granularity": granularity of the dirty bitmap, in bytes (json-int, optional) -- "buf_size": maximum amount of data in flight from source to target, in bytes +- "buf-size": maximum amount of data in flight from source to target, in bytes (json-int, default 10M) - "sync": what parts of the disk image should be copied to the destination; possibilities include "full" for all the disk, "top" for only the sectors @@ -1562,10 +1691,58 @@ Example: EQMP + { + .name = "blockdev-mirror", + .args_type = "sync:s,device:B,target:B,replaces:s?,speed:i?," + "on-source-error:s?,on-target-error:s?," + "granularity:i?,buf-size:i?", + .mhandler.cmd_new = qmp_marshal_blockdev_mirror, + }, + +SQMP +blockdev-mirror +------------ + +Start mirroring a block device's writes to another block device. target +specifies the target of mirror operation. + +Arguments: + +- "device": device name to operate on (json-string) +- "target": device name to mirror to (json-string) +- "replaces": the block driver node name to replace when finished + (json-string, optional) +- "speed": maximum speed of the streaming job, in bytes per second + (json-int) +- "granularity": granularity of the dirty bitmap, in bytes (json-int, optional) +- "buf_size": maximum amount of data in flight from source to target, in bytes + (json-int, default 10M) +- "sync": what parts of the disk image should be copied to the destination; + possibilities include "full" for all the disk, "top" for only the sectors + allocated in the topmost image, or "none" to only replicate new I/O + (MirrorSyncMode). +- "on-source-error": the action to take on an error on the source + (BlockdevOnError, default 'report') +- "on-target-error": the action to take on an error on the target + (BlockdevOnError, default 'report') + +The default value of the granularity is the image cluster size clamped +between 4096 and 65536, if the image format defines one. If the format +does not define a cluster size, the default value of the granularity +is 65536. + +Example: + +-> { "execute": "blockdev-mirror", "arguments": { "device": "ide-hd0", + "target": "target0", + "sync": "full" } } +<- { "return": {} } + +EQMP { .name = "change-backing-file", .args_type = "device:s,image-node-name:s,backing-file:s", - .mhandler.cmd_new = qmp_marshal_input_change_backing_file, + .mhandler.cmd_new = qmp_marshal_change_backing_file, }, SQMP @@ -1604,7 +1781,7 @@ EQMP { .name = "balloon", .args_type = "value:M", - .mhandler.cmd_new = qmp_marshal_input_balloon, + .mhandler.cmd_new = qmp_marshal_balloon, }, SQMP @@ -1627,7 +1804,7 @@ EQMP { .name = "set_link", .args_type = "name:s,up:b", - .mhandler.cmd_new = qmp_marshal_input_set_link, + .mhandler.cmd_new = qmp_marshal_set_link, }, SQMP @@ -1653,7 +1830,7 @@ EQMP .args_type = "fdname:s", .params = "getfd name", .help = "receive a file descriptor via SCM rights and assign it a name", - .mhandler.cmd_new = qmp_marshal_input_getfd, + .mhandler.cmd_new = qmp_marshal_getfd, }, SQMP @@ -1686,7 +1863,7 @@ EQMP .args_type = "fdname:s", .params = "closefd name", .help = "close a file descriptor previously passed via SCM rights", - .mhandler.cmd_new = qmp_marshal_input_closefd, + .mhandler.cmd_new = qmp_marshal_closefd, }, SQMP @@ -1711,7 +1888,7 @@ EQMP .args_type = "fdset-id:i?,opaque:s?", .params = "add-fd fdset-id opaque", .help = "Add a file descriptor, that was passed via SCM rights, to an fd set", - .mhandler.cmd_new = qmp_marshal_input_add_fd, + .mhandler.cmd_new = qmp_marshal_add_fd, }, SQMP @@ -1750,7 +1927,7 @@ EQMP .args_type = "fdset-id:i,fd:i?", .params = "remove-fd fdset-id fd", .help = "Remove a file descriptor from an fd set", - .mhandler.cmd_new = qmp_marshal_input_remove_fd, + .mhandler.cmd_new = qmp_marshal_remove_fd, }, SQMP @@ -1782,7 +1959,7 @@ EQMP .name = "query-fdsets", .args_type = "", .help = "Return information describing all fd sets", - .mhandler.cmd_new = qmp_marshal_input_query_fdsets, + .mhandler.cmd_new = qmp_marshal_query_fdsets, }, SQMP @@ -1831,7 +2008,7 @@ EQMP { .name = "block_passwd", .args_type = "device:s?,node-name:s?,password:s", - .mhandler.cmd_new = qmp_marshal_input_block_passwd, + .mhandler.cmd_new = qmp_marshal_block_passwd, }, SQMP @@ -1856,8 +2033,8 @@ EQMP { .name = "block_set_io_throttle", - .args_type = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l,bps_max:l?,bps_rd_max:l?,bps_wr_max:l?,iops_max:l?,iops_rd_max:l?,iops_wr_max:l?,iops_size:l?,group:s?", - .mhandler.cmd_new = qmp_marshal_input_block_set_io_throttle, + .args_type = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l,bps_max:l?,bps_rd_max:l?,bps_wr_max:l?,iops_max:l?,iops_rd_max:l?,iops_wr_max:l?,bps_max_length:l?,bps_rd_max_length:l?,bps_wr_max_length:l?,iops_max_length:l?,iops_rd_max_length:l?,iops_wr_max_length:l?,iops_size:l?,group:s?", + .mhandler.cmd_new = qmp_marshal_block_set_io_throttle, }, SQMP @@ -1875,14 +2052,20 @@ Arguments: - "iops": total I/O operations per second (json-int) - "iops_rd": read I/O operations per second (json-int) - "iops_wr": write I/O operations per second (json-int) -- "bps_max": total max in bytes (json-int) -- "bps_rd_max": read max in bytes (json-int) -- "bps_wr_max": write max in bytes (json-int) -- "iops_max": total I/O operations max (json-int) -- "iops_rd_max": read I/O operations max (json-int) -- "iops_wr_max": write I/O operations max (json-int) -- "iops_size": I/O size in bytes when limiting (json-int) -- "group": throttle group name (json-string) +- "bps_max": total throughput limit during bursts, in bytes (json-int, optional) +- "bps_rd_max": read throughput limit during bursts, in bytes (json-int, optional) +- "bps_wr_max": write throughput limit during bursts, in bytes (json-int, optional) +- "iops_max": total I/O operations per second during bursts (json-int, optional) +- "iops_rd_max": read I/O operations per second during bursts (json-int, optional) +- "iops_wr_max": write I/O operations per second during bursts (json-int, optional) +- "bps_max_length": maximum length of the @bps_max burst period, in seconds (json-int, optional) +- "bps_rd_max_length": maximum length of the @bps_rd_max burst period, in seconds (json-int, optional) +- "bps_wr_max_length": maximum length of the @bps_wr_max burst period, in seconds (json-int, optional) +- "iops_max_length": maximum length of the @iops_max burst period, in seconds (json-int, optional) +- "iops_rd_max_length": maximum length of the @iops_rd_max burst period, in seconds (json-int, optional) +- "iops_wr_max_length": maximum length of the @iops_wr_max burst period, in seconds (json-int, optional) +- "iops_size": I/O size in bytes when limiting (json-int, optional) +- "group": throttle group name (json-string, optional) Example: @@ -1899,6 +2082,7 @@ Example: "iops_max": 0, "iops_rd_max": 0, "iops_wr_max": 0, + "bps_max_length": 60, "iops_size": 0 } } <- { "return": {} } @@ -1907,7 +2091,7 @@ EQMP { .name = "set_password", .args_type = "protocol:s,password:s,connected:s?", - .mhandler.cmd_new = qmp_marshal_input_set_password, + .mhandler.cmd_new = qmp_marshal_set_password, }, SQMP @@ -1933,7 +2117,7 @@ EQMP { .name = "expire_password", .args_type = "protocol:s,time:s", - .mhandler.cmd_new = qmp_marshal_input_expire_password, + .mhandler.cmd_new = qmp_marshal_expire_password, }, SQMP @@ -1958,7 +2142,7 @@ EQMP { .name = "add_client", .args_type = "protocol:s,fdname:s,skipauth:b?,tls:b?", - .mhandler.cmd_new = qmp_marshal_input_add_client, + .mhandler.cmd_new = qmp_marshal_add_client, }, SQMP @@ -2009,7 +2193,7 @@ EQMP { .name = "human-monitor-command", .args_type = "command-line:s,cpu-index:i?", - .mhandler.cmd_new = qmp_marshal_input_human_monitor_command, + .mhandler.cmd_new = qmp_marshal_human_monitor_command, }, SQMP @@ -2088,7 +2272,7 @@ EQMP { .name = "query-version", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_version, + .mhandler.cmd_new = qmp_marshal_query_version, }, SQMP @@ -2125,7 +2309,7 @@ EQMP { .name = "query-commands", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_commands, + .mhandler.cmd_new = qmp_marshal_query_commands, }, SQMP @@ -2162,7 +2346,24 @@ EQMP { .name = "query-events", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_events, + .mhandler.cmd_new = qmp_marshal_query_events, + }, + +SQMP +query-qmp-schema +---------------- + +Return the QMP wire schema. The returned value is a json-array of +named schema entities. Entities are commands, events and various +types. See docs/qapi-code-gen.txt for information on their structure +and intended use. + +EQMP + + { + .name = "query-qmp-schema", + .args_type = "", + .mhandler.cmd_new = qmp_query_qmp_schema, }, SQMP @@ -2207,7 +2408,7 @@ EQMP { .name = "query-chardev", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_chardev, + .mhandler.cmd_new = qmp_marshal_query_chardev, }, SQMP @@ -2248,7 +2449,7 @@ EQMP { .name = "query-chardev-backends", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_chardev_backends, + .mhandler.cmd_new = qmp_marshal_query_chardev_backends, }, SQMP @@ -2432,7 +2633,7 @@ EQMP { .name = "query-block", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_block, + .mhandler.cmd_new = qmp_marshal_query_block, }, SQMP @@ -2456,12 +2657,70 @@ Each json-object contain the following: - "wr_total_time_ns": total time spend on writes in nano-seconds (json-int) - "rd_total_time_ns": total time spend on reads in nano-seconds (json-int) - "flush_total_time_ns": total time spend on cache flushes in nano-seconds (json-int) - - "wr_highest_offset": Highest offset of a sector written since the - BlockDriverState has been opened (json-int) + - "wr_highest_offset": The offset after the greatest byte written to the + BlockDriverState since it has been opened (json-int) - "rd_merged": number of read requests that have been merged into another request (json-int) - "wr_merged": number of write requests that have been merged into another request (json-int) + - "idle_time_ns": time since the last I/O operation, in + nanoseconds. If the field is absent it means + that there haven't been any operations yet + (json-int, optional) + - "failed_rd_operations": number of failed read operations + (json-int) + - "failed_wr_operations": number of failed write operations + (json-int) + - "failed_flush_operations": number of failed flush operations + (json-int) + - "invalid_rd_operations": number of invalid read operations + (json-int) + - "invalid_wr_operations": number of invalid write operations + (json-int) + - "invalid_flush_operations": number of invalid flush operations + (json-int) + - "account_invalid": whether invalid operations are included in + the last access statistics (json-bool) + - "account_failed": whether failed operations are included in the + latency and last access statistics + (json-bool) + - "timed_stats": A json-array containing statistics collected in + specific intervals, with the following members: + - "interval_length": interval used for calculating the + statistics, in seconds (json-int) + - "min_rd_latency_ns": minimum latency of read operations in + the defined interval, in nanoseconds + (json-int) + - "min_wr_latency_ns": minimum latency of write operations in + the defined interval, in nanoseconds + (json-int) + - "min_flush_latency_ns": minimum latency of flush operations + in the defined interval, in + nanoseconds (json-int) + - "max_rd_latency_ns": maximum latency of read operations in + the defined interval, in nanoseconds + (json-int) + - "max_wr_latency_ns": maximum latency of write operations in + the defined interval, in nanoseconds + (json-int) + - "max_flush_latency_ns": maximum latency of flush operations + in the defined interval, in + nanoseconds (json-int) + - "avg_rd_latency_ns": average latency of read operations in + the defined interval, in nanoseconds + (json-int) + - "avg_wr_latency_ns": average latency of write operations in + the defined interval, in nanoseconds + (json-int) + - "avg_flush_latency_ns": average latency of flush operations + in the defined interval, in + nanoseconds (json-int) + - "avg_rd_queue_depth": average number of pending read + operations in the defined interval + (json-number) + - "avg_wr_queue_depth": average number of pending write + operations in the defined interval + (json-number). - "parent": Contains recursively the statistics of the underlying protocol (e.g. the host file for a qcow2 image). If there is no underlying protocol, this field is omitted @@ -2486,7 +2745,10 @@ Example: "flush_total_times_ns":49653 "flush_operations":61, "rd_merged":0, - "wr_merged":0 + "wr_merged":0, + "idle_time_ns":2953431879, + "account_invalid":true, + "account_failed":false } }, "stats":{ @@ -2500,7 +2762,10 @@ Example: "rd_total_times_ns":3465673657 "flush_total_times_ns":49653, "rd_merged":0, - "wr_merged":0 + "wr_merged":0, + "idle_time_ns":2953431879, + "account_invalid":true, + "account_failed":false } }, { @@ -2516,7 +2781,9 @@ Example: "rd_total_times_ns":0 "flush_total_times_ns":0, "rd_merged":0, - "wr_merged":0 + "wr_merged":0, + "account_invalid":false, + "account_failed":false } }, { @@ -2532,7 +2799,9 @@ Example: "rd_total_times_ns":0 "flush_total_times_ns":0, "rd_merged":0, - "wr_merged":0 + "wr_merged":0, + "account_invalid":false, + "account_failed":false } }, { @@ -2548,7 +2817,9 @@ Example: "rd_total_times_ns":0 "flush_total_times_ns":0, "rd_merged":0, - "wr_merged":0 + "wr_merged":0, + "account_invalid":false, + "account_failed":false } } ] @@ -2559,7 +2830,7 @@ EQMP { .name = "query-blockstats", .args_type = "query-nodes:b?", - .mhandler.cmd_new = qmp_marshal_input_query_blockstats, + .mhandler.cmd_new = qmp_marshal_query_blockstats, }, SQMP @@ -2574,6 +2845,8 @@ Return a json-array. Each CPU is represented by a json-object, which contains: - "current": true if this is the current CPU, false otherwise (json-bool) - "halted": true if the cpu is halted, false otherwise (json-bool) - "qom_path": path to the CPU object in the QOM tree (json-str) +- "arch": architecture of the cpu, which determines what additional + keys will be present (json-str) - Current program counter. The key's name depends on the architecture: "pc": i386/x86_64 (json-int) "nip": PPC (json-int) @@ -2591,6 +2864,7 @@ Example: "current":true, "halted":false, "qom_path":"/machine/unattached/device[0]", + "arch":"x86", "pc":3227107138, "thread_id":3134 }, @@ -2599,6 +2873,7 @@ Example: "current":false, "halted":true, "qom_path":"/machine/unattached/device[2]", + "arch":"x86", "pc":7108165, "thread_id":3135 } @@ -2610,7 +2885,7 @@ EQMP { .name = "query-cpus", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_cpus, + .mhandler.cmd_new = qmp_marshal_query_cpus, }, SQMP @@ -2649,7 +2924,7 @@ EQMP { .name = "query-iothreads", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_iothreads, + .mhandler.cmd_new = qmp_marshal_query_iothreads, }, SQMP @@ -2866,7 +3141,7 @@ EQMP { .name = "query-pci", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_pci, + .mhandler.cmd_new = qmp_marshal_query_pci, }, SQMP @@ -2890,7 +3165,7 @@ EQMP { .name = "query-kvm", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_kvm, + .mhandler.cmd_new = qmp_marshal_query_kvm, }, SQMP @@ -2930,7 +3205,7 @@ EQMP { .name = "query-status", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_status, + .mhandler.cmd_new = qmp_marshal_query_status, }, SQMP @@ -2974,7 +3249,7 @@ EQMP { .name = "query-mice", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_mice, + .mhandler.cmd_new = qmp_marshal_query_mice, }, SQMP @@ -3037,12 +3312,12 @@ EQMP { .name = "query-vnc", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_vnc, + .mhandler.cmd_new = qmp_marshal_query_vnc, }, { .name = "query-vnc-servers", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_vnc_servers, + .mhandler.cmd_new = qmp_marshal_query_vnc_servers, }, SQMP @@ -3119,7 +3394,7 @@ EQMP { .name = "query-spice", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_spice, + .mhandler.cmd_new = qmp_marshal_query_spice, }, #endif @@ -3143,7 +3418,7 @@ EQMP { .name = "query-name", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_name, + .mhandler.cmd_new = qmp_marshal_query_name, }, SQMP @@ -3166,7 +3441,7 @@ EQMP { .name = "query-uuid", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_uuid, + .mhandler.cmd_new = qmp_marshal_query_uuid, }, SQMP @@ -3215,7 +3490,7 @@ EQMP { .name = "query-command-line-options", .args_type = "option:s?", - .mhandler.cmd_new = qmp_marshal_input_query_command_line_options, + .mhandler.cmd_new = qmp_marshal_query_command_line_options, }, SQMP @@ -3393,7 +3668,7 @@ EQMP { .name = "query-migrate", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_migrate, + .mhandler.cmd_new = qmp_marshal_query_migrate, }, SQMP @@ -3406,7 +3681,9 @@ Enable/Disable migration capabilities - "rdma-pin-all": pin all pages when using RDMA during migration - "auto-converge": throttle down guest to help convergence of migration - "zero-blocks": compress zero blocks during block migration +- "compress": use multiple compression threads to accelerate live migration - "events": generate events for each migration state change +- "postcopy-ram": postcopy mode for live migration Arguments: @@ -3421,7 +3698,7 @@ EQMP .name = "migrate-set-capabilities", .args_type = "capabilities:q", .params = "capability:s,state:b", - .mhandler.cmd_new = qmp_marshal_input_migrate_set_capabilities, + .mhandler.cmd_new = qmp_marshal_migrate_set_capabilities, }, SQMP query-migrate-capabilities @@ -3434,20 +3711,31 @@ Query current migration capabilities - "rdma-pin-all" : RDMA Pin Page state (json-bool) - "auto-converge" : Auto Converge state (json-bool) - "zero-blocks" : Zero Blocks state (json-bool) + - "compress": Multiple compression threads state (json-bool) + - "events": Migration state change event state (json-bool) + - "postcopy-ram": postcopy ram state (json-bool) Arguments: Example: -> { "execute": "query-migrate-capabilities" } -<- { "return": [ { "state": false, "capability": "xbzrle" } ] } +<- {"return": [ + {"state": false, "capability": "xbzrle"}, + {"state": false, "capability": "rdma-pin-all"}, + {"state": false, "capability": "auto-converge"}, + {"state": false, "capability": "zero-blocks"}, + {"state": false, "capability": "compress"}, + {"state": true, "capability": "events"}, + {"state": false, "capability": "postcopy-ram"} + ]} EQMP { .name = "query-migrate-capabilities", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_migrate_capabilities, + .mhandler.cmd_new = qmp_marshal_query_migrate_capabilities, }, SQMP @@ -3459,6 +3747,10 @@ Set migration parameters - "compress-level": set compression level during migration (json-int) - "compress-threads": set compression thread count for migration (json-int) - "decompress-threads": set decompression thread count for migration (json-int) +- "x-cpu-throttle-initial": set initial percentage of time guest cpus are + throttled for auto-converge (json-int) +- "x-cpu-throttle-increment": set throttle increasing percentage for + auto-converge (json-int) Arguments: @@ -3472,8 +3764,8 @@ EQMP { .name = "migrate-set-parameters", .args_type = - "compress-level:i?,compress-threads:i?,decompress-threads:i?", - .mhandler.cmd_new = qmp_marshal_input_migrate_set_parameters, + "compress-level:i?,compress-threads:i?,decompress-threads:i?,x-cpu-throttle-initial:i?,x-cpu-throttle-increment:i?", + .mhandler.cmd_new = qmp_marshal_migrate_set_parameters, }, SQMP query-migrate-parameters @@ -3485,6 +3777,10 @@ Query current migration parameters - "compress-level" : compression level value (json-int) - "compress-threads" : compression thread count value (json-int) - "decompress-threads" : decompression thread count value (json-int) + - "x-cpu-throttle-initial" : initial percentage of time guest cpus are + throttled (json-int) + - "x-cpu-throttle-increment" : throttle increasing percentage for + auto-converge (json-int) Arguments: @@ -3493,9 +3789,11 @@ Example: -> { "execute": "query-migrate-parameters" } <- { "return": { - "decompress-threads", 2, - "compress-threads", 8, - "compress-level", 1 + "decompress-threads": 2, + "x-cpu-throttle-increment": 10, + "compress-threads": 8, + "compress-level": 1, + "x-cpu-throttle-initial": 20 } } @@ -3504,7 +3802,7 @@ EQMP { .name = "query-migrate-parameters", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_migrate_parameters, + .mhandler.cmd_new = qmp_marshal_query_migrate_parameters, }, SQMP @@ -3532,88 +3830,88 @@ EQMP { .name = "query-balloon", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_balloon, + .mhandler.cmd_new = qmp_marshal_query_balloon, }, { .name = "query-block-jobs", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_block_jobs, + .mhandler.cmd_new = qmp_marshal_query_block_jobs, }, { .name = "qom-list", .args_type = "path:s", - .mhandler.cmd_new = qmp_marshal_input_qom_list, + .mhandler.cmd_new = qmp_marshal_qom_list, }, { .name = "qom-set", .args_type = "path:s,property:s,value:q", - .mhandler.cmd_new = qmp_qom_set, + .mhandler.cmd_new = qmp_marshal_qom_set, }, { .name = "qom-get", .args_type = "path:s,property:s", - .mhandler.cmd_new = qmp_qom_get, + .mhandler.cmd_new = qmp_marshal_qom_get, }, { .name = "nbd-server-start", - .args_type = "addr:q", - .mhandler.cmd_new = qmp_marshal_input_nbd_server_start, + .args_type = "addr:q,tls-creds:s?", + .mhandler.cmd_new = qmp_marshal_nbd_server_start, }, { .name = "nbd-server-add", .args_type = "device:B,writable:b?", - .mhandler.cmd_new = qmp_marshal_input_nbd_server_add, + .mhandler.cmd_new = qmp_marshal_nbd_server_add, }, { .name = "nbd-server-stop", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_nbd_server_stop, + .mhandler.cmd_new = qmp_marshal_nbd_server_stop, }, { .name = "change-vnc-password", .args_type = "password:s", - .mhandler.cmd_new = qmp_marshal_input_change_vnc_password, + .mhandler.cmd_new = qmp_marshal_change_vnc_password, }, { .name = "qom-list-types", .args_type = "implements:s?,abstract:b?", - .mhandler.cmd_new = qmp_marshal_input_qom_list_types, + .mhandler.cmd_new = qmp_marshal_qom_list_types, }, { .name = "device-list-properties", .args_type = "typename:s", - .mhandler.cmd_new = qmp_marshal_input_device_list_properties, + .mhandler.cmd_new = qmp_marshal_device_list_properties, }, { .name = "query-machines", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_machines, + .mhandler.cmd_new = qmp_marshal_query_machines, }, { .name = "query-cpu-definitions", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_cpu_definitions, + .mhandler.cmd_new = qmp_marshal_query_cpu_definitions, }, { .name = "query-target", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_target, + .mhandler.cmd_new = qmp_marshal_query_target, }, { .name = "query-tpm", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_tpm, + .mhandler.cmd_new = qmp_marshal_query_tpm, }, SQMP @@ -3647,7 +3945,7 @@ EQMP { .name = "query-tpm-models", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_tpm_models, + .mhandler.cmd_new = qmp_marshal_query_tpm_models, }, SQMP @@ -3668,7 +3966,7 @@ EQMP { .name = "query-tpm-types", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_tpm_types, + .mhandler.cmd_new = qmp_marshal_query_tpm_types, }, SQMP @@ -3689,7 +3987,7 @@ EQMP { .name = "chardev-add", .args_type = "id:s,backend:q", - .mhandler.cmd_new = qmp_marshal_input_chardev_add, + .mhandler.cmd_new = qmp_marshal_chardev_add, }, SQMP @@ -3726,7 +4024,7 @@ EQMP { .name = "chardev-remove", .args_type = "id:s", - .mhandler.cmd_new = qmp_marshal_input_chardev_remove, + .mhandler.cmd_new = qmp_marshal_chardev_remove, }, @@ -3749,7 +4047,7 @@ EQMP { .name = "query-rx-filter", .args_type = "name:s?", - .mhandler.cmd_new = qmp_marshal_input_query_rx_filter, + .mhandler.cmd_new = qmp_marshal_query_rx_filter, }, SQMP @@ -3815,7 +4113,7 @@ EQMP { .name = "blockdev-add", .args_type = "options:q", - .mhandler.cmd_new = qmp_marshal_input_blockdev_add, + .mhandler.cmd_new = qmp_marshal_blockdev_add, }, SQMP @@ -3825,8 +4123,8 @@ blockdev-add Add a block device. This command is still a work in progress. It doesn't support all -block drivers, it lacks a matching blockdev-del, and more. Stay away -from it unless you want to help with its development. +block drivers among other things. Stay away from it unless you want +to help with its development. Arguments: @@ -3869,12 +4167,240 @@ Example (2): <- { "return": {} } +EQMP + + { + .name = "x-blockdev-del", + .args_type = "id:s?,node-name:s?", + .mhandler.cmd_new = qmp_marshal_x_blockdev_del, + }, + +SQMP +x-blockdev-del +------------ +Since 2.5 + +Deletes a block device thas has been added using blockdev-add. +The selected device can be either a block backend or a graph node. + +In the former case the backend will be destroyed, along with its +inserted medium if there's any. The command will fail if the backend +or its medium are in use. + +In the latter case the node will be destroyed. The command will fail +if the node is attached to a block backend or is otherwise being +used. + +One of "id" or "node-name" must be specified, but not both. + +This command is still a work in progress and is considered +experimental. Stay away from it unless you want to help with its +development. + +Arguments: + +- "id": Name of the block backend device to delete (json-string, optional) +- "node-name": Name of the graph node to delete (json-string, optional) + +Example: + +-> { "execute": "blockdev-add", + "arguments": { + "options": { + "driver": "qcow2", + "id": "drive0", + "file": { + "driver": "file", + "filename": "test.qcow2" + } + } + } + } + +<- { "return": {} } + +-> { "execute": "x-blockdev-del", + "arguments": { "id": "drive0" } + } +<- { "return": {} } + +EQMP + + { + .name = "blockdev-open-tray", + .args_type = "device:s,force:b?", + .mhandler.cmd_new = qmp_marshal_blockdev_open_tray, + }, + +SQMP +blockdev-open-tray +------------------ + +Opens a block device's tray. If there is a block driver state tree inserted as a +medium, it will become inaccessible to the guest (but it will remain associated +to the block device, so closing the tray will make it accessible again). + +If the tray was already open before, this will be a no-op. + +Once the tray opens, a DEVICE_TRAY_MOVED event is emitted. There are cases in +which no such event will be generated, these include: +- if the guest has locked the tray, @force is false and the guest does not + respond to the eject request +- if the BlockBackend denoted by @device does not have a guest device attached + to it +- if the guest device does not have an actual tray and is empty, for instance + for floppy disk drives + +Arguments: + +- "device": block device name (json-string) +- "force": if false (the default), an eject request will be sent to the guest if + it has locked the tray (and the tray will not be opened immediately); + if true, the tray will be opened regardless of whether it is locked + (json-bool, optional) + +Example: + +-> { "execute": "blockdev-open-tray", + "arguments": { "device": "ide1-cd0" } } + +<- { "timestamp": { "seconds": 1418751016, + "microseconds": 716996 }, + "event": "DEVICE_TRAY_MOVED", + "data": { "device": "ide1-cd0", + "tray-open": true } } + +<- { "return": {} } + +EQMP + + { + .name = "blockdev-close-tray", + .args_type = "device:s", + .mhandler.cmd_new = qmp_marshal_blockdev_close_tray, + }, + +SQMP +blockdev-close-tray +------------------- + +Closes a block device's tray. If there is a block driver state tree associated +with the block device (which is currently ejected), that tree will be loaded as +the medium. + +If the tray was already closed before, this will be a no-op. + +Arguments: + +- "device": block device name (json-string) + +Example: + +-> { "execute": "blockdev-close-tray", + "arguments": { "device": "ide1-cd0" } } + +<- { "timestamp": { "seconds": 1418751345, + "microseconds": 272147 }, + "event": "DEVICE_TRAY_MOVED", + "data": { "device": "ide1-cd0", + "tray-open": false } } + +<- { "return": {} } + +EQMP + + { + .name = "x-blockdev-remove-medium", + .args_type = "device:s", + .mhandler.cmd_new = qmp_marshal_x_blockdev_remove_medium, + }, + +SQMP +x-blockdev-remove-medium +------------------------ + +Removes a medium (a block driver state tree) from a block device. That block +device's tray must currently be open (unless there is no attached guest device). + +If the tray is open and there is no medium inserted, this will be a no-op. + +This command is still a work in progress and is considered experimental. +Stay away from it unless you want to help with its development. + +Arguments: + +- "device": block device name (json-string) + +Example: + +-> { "execute": "x-blockdev-remove-medium", + "arguments": { "device": "ide1-cd0" } } + +<- { "error": { "class": "GenericError", + "desc": "Tray of device 'ide1-cd0' is not open" } } + +-> { "execute": "blockdev-open-tray", + "arguments": { "device": "ide1-cd0" } } + +<- { "timestamp": { "seconds": 1418751627, + "microseconds": 549958 }, + "event": "DEVICE_TRAY_MOVED", + "data": { "device": "ide1-cd0", + "tray-open": true } } + +<- { "return": {} } + +-> { "execute": "x-blockdev-remove-medium", + "arguments": { "device": "ide1-cd0" } } + +<- { "return": {} } + +EQMP + + { + .name = "x-blockdev-insert-medium", + .args_type = "device:s,node-name:s", + .mhandler.cmd_new = qmp_marshal_x_blockdev_insert_medium, + }, + +SQMP +x-blockdev-insert-medium +------------------------ + +Inserts a medium (a block driver state tree) into a block device. That block +device's tray must currently be open (unless there is no attached guest device) +and there must be no medium inserted already. + +This command is still a work in progress and is considered experimental. +Stay away from it unless you want to help with its development. + +Arguments: + +- "device": block device name (json-string) +- "node-name": root node of the BDS tree to insert into the block device + +Example: + +-> { "execute": "blockdev-add", + "arguments": { "options": { "node-name": "node0", + "driver": "raw", + "file": { "driver": "file", + "filename": "fedora.iso" } } } } + +<- { "return": {} } + +-> { "execute": "x-blockdev-insert-medium", + "arguments": { "device": "ide1-cd0", + "node-name": "node0" } } + +<- { "return": {} } + EQMP { .name = "query-named-block-nodes", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_named_block_nodes, + .mhandler.cmd_new = qmp_marshal_query_named_block_nodes, }, SQMP @@ -3931,12 +4457,65 @@ Example: } } } ] } +EQMP + + { + .name = "blockdev-change-medium", + .args_type = "device:B,filename:F,format:s?,read-only-mode:s?", + .mhandler.cmd_new = qmp_marshal_blockdev_change_medium, + }, + +SQMP +blockdev-change-medium +---------------------- + +Changes the medium inserted into a block device by ejecting the current medium +and loading a new image file which is inserted as the new medium. + +Arguments: + +- "device": device name (json-string) +- "filename": filename of the new image (json-string) +- "format": format of the new image (json-string, optional) +- "read-only-mode": new read-only mode (json-string, optional) + - Possible values: "retain" (default), "read-only", "read-write" + +Examples: + +1. Change a removable medium + +-> { "execute": "blockdev-change-medium", + "arguments": { "device": "ide1-cd0", + "filename": "/srv/images/Fedora-12-x86_64-DVD.iso", + "format": "raw" } } +<- { "return": {} } + +2. Load a read-only medium into a writable drive + +-> { "execute": "blockdev-change-medium", + "arguments": { "device": "isa-fd0", + "filename": "/srv/images/ro.img", + "format": "raw", + "read-only-mode": "retain" } } + +<- { "error": + { "class": "GenericError", + "desc": "Could not open '/srv/images/ro.img': Permission denied" } } + +-> { "execute": "blockdev-change-medium", + "arguments": { "device": "isa-fd0", + "filename": "/srv/images/ro.img", + "format": "raw", + "read-only-mode": "read-only" } } + +<- { "return": {} } + EQMP { .name = "query-memdev", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_memdev, + .mhandler.cmd_new = qmp_marshal_query_memdev, }, SQMP @@ -3974,7 +4553,7 @@ EQMP { .name = "query-memory-devices", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_memory_devices, + .mhandler.cmd_new = qmp_marshal_query_memory_devices, }, SQMP @@ -4001,7 +4580,7 @@ EQMP { .name = "query-acpi-ospm-status", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_acpi_ospm_status, + .mhandler.cmd_new = qmp_marshal_query_acpi_ospm_status, }, SQMP @@ -4024,7 +4603,7 @@ EQMP { .name = "rtc-reset-reinjection", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_rtc_reset_reinjection, + .mhandler.cmd_new = qmp_marshal_rtc_reset_reinjection, }, #endif @@ -4045,7 +4624,7 @@ EQMP { .name = "trace-event-get-state", .args_type = "name:s", - .mhandler.cmd_new = qmp_marshal_input_trace_event_get_state, + .mhandler.cmd_new = qmp_marshal_trace_event_get_state, }, SQMP @@ -4063,7 +4642,7 @@ EQMP { .name = "trace-event-set-state", .args_type = "name:s,enable:b,ignore-unavailable:b?", - .mhandler.cmd_new = qmp_marshal_input_trace_event_set_state, + .mhandler.cmd_new = qmp_marshal_trace_event_set_state, }, SQMP @@ -4079,21 +4658,22 @@ Example: EQMP { - .name = "x-input-send-event", + .name = "input-send-event", .args_type = "console:i?,events:q", - .mhandler.cmd_new = qmp_marshal_input_x_input_send_event, + .mhandler.cmd_new = qmp_marshal_input_send_event, }, SQMP -@x-input-send-event +@input-send-event ----------------- Send input event to guest. Arguments: -- "console": console index. (json-int, optional) -- "events": list of input events. +- "device": display device (json-string, optional) +- "head": display head (json-int, optional) +- "events": list of input events The consoles are visible in the qom tree, under /backend/console[$index]. They have a device link and head property, so @@ -4105,24 +4685,24 @@ Example (1): Press left mouse button. --> { "execute": "x-input-send-event", - "arguments": { "console": 0, +-> { "execute": "input-send-event", + "arguments": { "device": "video0", "events": [ { "type": "btn", - "data" : { "down": true, "button": "Left" } } ] } } + "data" : { "down": true, "button": "left" } } ] } } <- { "return": {} } --> { "execute": "x-input-send-event", - "arguments": { "console": 0, +-> { "execute": "input-send-event", + "arguments": { "device": "video0", "events": [ { "type": "btn", - "data" : { "down": false, "button": "Left" } } ] } } + "data" : { "down": false, "button": "left" } } ] } } <- { "return": {} } Example (2): Press ctrl-alt-del. --> { "execute": "x-input-send-event", - "arguments": { "console": 0, "events": [ +-> { "execute": "input-send-event", + "arguments": { "events": [ { "type": "key", "data" : { "down": true, "key": {"type": "qcode", "data": "ctrl" } } }, { "type": "key", "data" : { "down": true, @@ -4135,10 +4715,10 @@ Example (3): Move mouse pointer to absolute coordinates (20000, 400). --> { "execute": "x-input-send-event" , - "arguments": { "console": 0, "events": [ - { "type": "abs", "data" : { "axis": "X", "value" : 20000 } }, - { "type": "abs", "data" : { "axis": "Y", "value" : 400 } } ] } } +-> { "execute": "input-send-event" , + "arguments": { "events": [ + { "type": "abs", "data" : { "axis": "x", "value" : 20000 } }, + { "type": "abs", "data" : { "axis": "y", "value" : 400 } } ] } } <- { "return": {} } EQMP @@ -4146,7 +4726,7 @@ EQMP { .name = "block-set-write-threshold", .args_type = "node-name:s,write-threshold:l", - .mhandler.cmd_new = qmp_marshal_input_block_set_write_threshold, + .mhandler.cmd_new = qmp_marshal_block_set_write_threshold, }, SQMP @@ -4174,7 +4754,7 @@ EQMP { .name = "query-rocker", .args_type = "name:s", - .mhandler.cmd_new = qmp_marshal_input_query_rocker, + .mhandler.cmd_new = qmp_marshal_query_rocker, }, SQMP @@ -4195,7 +4775,7 @@ EQMP { .name = "query-rocker-ports", .args_type = "name:s", - .mhandler.cmd_new = qmp_marshal_input_query_rocker_ports, + .mhandler.cmd_new = qmp_marshal_query_rocker_ports, }, SQMP @@ -4220,7 +4800,7 @@ EQMP { .name = "query-rocker-of-dpa-flows", .args_type = "name:s,tbl-id:i?", - .mhandler.cmd_new = qmp_marshal_input_query_rocker_of_dpa_flows, + .mhandler.cmd_new = qmp_marshal_query_rocker_of_dpa_flows, }, SQMP @@ -4249,7 +4829,7 @@ EQMP { .name = "query-rocker-of-dpa-groups", .args_type = "name:s,type:i?", - .mhandler.cmd_new = qmp_marshal_input_query_rocker_of_dpa_groups, + .mhandler.cmd_new = qmp_marshal_query_rocker_of_dpa_groups, }, SQMP @@ -4273,3 +4853,30 @@ Example: {"type": 0, "out-pport": 0, "pport": 0, "vlan-id": 3840, "pop-vlan": 1, "id": 251658240} ]} + +EQMP + +#if defined TARGET_ARM + { + .name = "query-gic-capabilities", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_query_gic_capabilities, + }, +#endif + +SQMP +query-gic-capabilities +--------------- + +Return a list of GICCapability objects, describing supported GIC +(Generic Interrupt Controller) versions. + +Arguments: None + +Example: + +-> { "execute": "query-gic-capabilities" } +<- { "return": [{ "version": 2, "emulated": true, "kernel": false }, + { "version": 3, "emulated": false, "kernel": true } ] } + +EQMP diff --git a/qemu/qmp.c b/qemu/qmp.c index 403805a55..9d0953bc2 100644 --- a/qemu/qmp.c +++ b/qemu/qmp.c @@ -13,7 +13,8 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "qemu/cutils.h" #include "monitor/monitor.h" #include "sysemu/sysemu.h" #include "qmp-commands.h" @@ -24,6 +25,7 @@ #include "sysemu/arch_init.h" #include "hw/qdev.h" #include "sysemu/blockdev.h" +#include "sysemu/block-backend.h" #include "qom/qom-qobject.h" #include "qapi/qmp/qerror.h" #include "qapi/qmp/qobject.h" @@ -49,14 +51,20 @@ VersionInfo *qmp_query_version(Error **errp) { VersionInfo *info = g_new0(VersionInfo, 1); const char *version = QEMU_VERSION; - char *tmp; + const char *tmp; + int err; info->qemu = g_new0(VersionTriple, 1); - info->qemu->major = strtol(version, &tmp, 10); + err = qemu_strtoll(version, &tmp, 10, &info->qemu->major); + assert(err == 0); tmp++; - info->qemu->minor = strtol(tmp, &tmp, 10); + + err = qemu_strtoll(tmp, &tmp, 10, &info->qemu->minor); + assert(err == 0); tmp++; - info->qemu->micro = strtol(tmp, &tmp, 10); + + err = qemu_strtoll(tmp, &tmp, 10, &info->qemu->micro); + assert(err == 0); info->package = g_strdup(QEMU_PKGVERSION); return info; @@ -95,6 +103,13 @@ void qmp_quit(Error **errp) void qmp_stop(Error **errp) { + /* if there is a dump in background, we should wait until the dump + * finished */ + if (dump_in_progress()) { + error_setg(errp, "There is a dump in process, please wait."); + return; + } + if (runstate_check(RUN_STATE_INMIGRATE)) { autostart = 0; } else { @@ -151,9 +166,9 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp) * #ifdef CONFIG_SPICE. Necessary for an accurate query-commands * result. However, the QAPI schema is blissfully unaware of that, * and the QAPI code generator happily generates a dead - * qmp_marshal_input_query_spice() that calls qmp_query_spice(). - * Provide it one, or else linking fails. - * FIXME Educate the QAPI schema on CONFIG_SPICE. + * qmp_marshal_query_spice() that calls qmp_query_spice(). Provide it + * one, or else linking fails. FIXME Educate the QAPI schema on + * CONFIG_SPICE. */ SpiceInfo *qmp_query_spice(Error **errp) { @@ -164,8 +179,16 @@ SpiceInfo *qmp_query_spice(Error **errp) void qmp_cont(Error **errp) { Error *local_err = NULL; + BlockBackend *blk; BlockDriverState *bs; + /* if there is a dump in background, we should wait until the dump + * finished */ + if (dump_in_progress()) { + error_setg(errp, "There is a dump in process, please wait."); + return; + } + if (runstate_needs_reset()) { error_setg(errp, "Resetting the Virtual Machine is required"); return; @@ -173,8 +196,8 @@ void qmp_cont(Error **errp) return; } - for (bs = bdrv_next(NULL); bs; bs = bdrv_next(bs)) { - bdrv_iostatus_reset(bs); + for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { + blk_iostatus_reset(blk); } for (bs = bdrv_next(NULL); bs; bs = bdrv_next(bs)) { bdrv_add_key(bs, NULL, &local_err); @@ -184,6 +207,18 @@ void qmp_cont(Error **errp) } } + /* Continuing after completed migration. Images have been inactivated to + * allow the destination to take control. Need to get control back now. */ + if (runstate_check(RUN_STATE_FINISH_MIGRATE) || + runstate_check(RUN_STATE_POSTMIGRATE)) + { + bdrv_invalidate_cache_all(&local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + if (runstate_check(RUN_STATE_INMIGRATE)) { autostart = 1; } else { @@ -202,6 +237,7 @@ ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp) bool ambiguous = false; ObjectPropertyInfoList *props = NULL; ObjectProperty *prop; + ObjectPropertyIterator iter; obj = object_resolve_path(path, &ambiguous); if (obj == NULL) { @@ -214,7 +250,8 @@ ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp) return NULL; } - QTAILQ_FOREACH(prop, &obj->properties, node) { + object_property_iter_init(&iter, obj); + while ((prop = object_property_iter_next(&iter))) { ObjectPropertyInfoList *entry = g_malloc0(sizeof(*entry)); entry->value = g_malloc0(sizeof(ObjectPropertyInfo)); @@ -228,12 +265,9 @@ ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp) return props; } -/* FIXME: teach qapi about how to pass through Visitors */ -void qmp_qom_set(QDict *qdict, QObject **ret, Error **errp) +void qmp_qom_set(const char *path, const char *property, QObject *value, + Error **errp) { - const char *path = qdict_get_str(qdict, "path"); - const char *property = qdict_get_str(qdict, "property"); - QObject *value = qdict_get(qdict, "value"); Object *obj; obj = object_resolve_path(path, NULL); @@ -246,20 +280,18 @@ void qmp_qom_set(QDict *qdict, QObject **ret, Error **errp) object_property_set_qobject(obj, value, property, errp); } -void qmp_qom_get(QDict *qdict, QObject **ret, Error **errp) +QObject *qmp_qom_get(const char *path, const char *property, Error **errp) { - const char *path = qdict_get_str(qdict, "path"); - const char *property = qdict_get_str(qdict, "property"); Object *obj; obj = object_resolve_path(path, NULL); if (!obj) { error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "Device '%s' not found", path); - return; + return NULL; } - *ret = object_property_get_qobject(obj, property, errp); + return object_property_get_qobject(obj, property, errp); } void qmp_set_password(const char *protocol, const char *password, @@ -411,7 +443,8 @@ void qmp_change(const char *device, const char *target, if (strcmp(device, "vnc") == 0) { qmp_change_vnc(target, has_arg, arg, errp); } else { - qmp_change_blockdev(device, target, arg, errp); + qmp_blockdev_change_medium(device, target, has_arg, arg, false, 0, + errp); } } @@ -499,6 +532,7 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename, ObjectClass *klass; Object *obj; ObjectProperty *prop; + ObjectPropertyIterator iter; DevicePropertyInfoList *prop_list = NULL; klass = object_class_by_name(typename); @@ -514,9 +548,21 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename, return NULL; } + if (object_class_is_abstract(klass)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "name", + "non-abstract device type"); + return NULL; + } + + if (DEVICE_CLASS(klass)->cannot_destroy_with_object_finalize_yet) { + error_setg(errp, "Can't list properties of device '%s'", typename); + return NULL; + } + obj = object_new(typename); - QTAILQ_FOREACH(prop, &obj->properties, node) { + object_property_iter_init(&iter, obj); + while ((prop = object_property_iter_next(&iter))) { DevicePropertyInfo *info; DevicePropertyInfoList *entry; @@ -601,67 +647,13 @@ void qmp_add_client(const char *protocol, const char *fdname, close(fd); } -void object_add(const char *type, const char *id, const QDict *qdict, - Visitor *v, Error **errp) -{ - Object *obj; - ObjectClass *klass; - const QDictEntry *e; - Error *local_err = NULL; - - klass = object_class_by_name(type); - if (!klass) { - error_setg(errp, "invalid object type: %s", type); - return; - } - - if (!object_class_dynamic_cast(klass, TYPE_USER_CREATABLE)) { - error_setg(errp, "object type '%s' isn't supported by object-add", - type); - return; - } - - if (object_class_is_abstract(klass)) { - error_setg(errp, "object type '%s' is abstract", type); - return; - } - - obj = object_new(type); - if (qdict) { - for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) { - object_property_set(obj, v, e->key, &local_err); - if (local_err) { - goto out; - } - } - } - - object_property_add_child(object_get_objects_root(), - id, obj, &local_err); - if (local_err) { - goto out; - } - - user_creatable_complete(obj, &local_err); - if (local_err) { - object_property_del(object_get_objects_root(), - id, &error_abort); - goto out; - } -out: - if (local_err) { - error_propagate(errp, local_err); - } - object_unref(obj); -} -void qmp_object_add(QDict *qdict, QObject **ret, Error **errp) +void qmp_object_add(const char *type, const char *id, + bool has_props, QObject *props, Error **errp) { - const char *type = qdict_get_str(qdict, "qom-type"); - const char *id = qdict_get_str(qdict, "id"); - QObject *props = qdict_get(qdict, "props"); const QDict *pdict = NULL; QmpInputVisitor *qiv; + Object *obj; if (props) { pdict = qobject_to_qdict(props); @@ -672,27 +664,17 @@ void qmp_object_add(QDict *qdict, QObject **ret, Error **errp) } qiv = qmp_input_visitor_new(props); - object_add(type, id, pdict, qmp_input_get_visitor(qiv), errp); + obj = user_creatable_add_type(type, id, pdict, + qmp_input_get_visitor(qiv), errp); qmp_input_visitor_cleanup(qiv); + if (obj) { + object_unref(obj); + } } void qmp_object_del(const char *id, Error **errp) { - Object *container; - Object *obj; - - container = object_get_objects_root(); - obj = object_resolve_path_component(container, id); - if (!obj) { - error_setg(errp, "object id not found"); - return; - } - - if (!user_creatable_can_be_deleted(USER_CREATABLE(obj), errp)) { - error_setg(errp, "%s is in use, can not be deleted", id); - return; - } - object_unparent(obj); + user_creatable_del(id, errp); } MemoryDeviceInfoList *qmp_query_memory_devices(Error **errp) diff --git a/qemu/qobject/Makefile.objs b/qemu/qobject/Makefile.objs index 0031e8b69..bed55084b 100644 --- a/qemu/qobject/Makefile.objs +++ b/qemu/qobject/Makefile.objs @@ -1,2 +1,2 @@ util-obj-y = qnull.o qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o -util-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o +util-obj-y += qjson.o qobject.o json-lexer.o json-streamer.o json-parser.o diff --git a/qemu/qobject/json-lexer.c b/qemu/qobject/json-lexer.c index b19623e22..496374d9a 100644 --- a/qemu/qobject/json-lexer.c +++ b/qemu/qobject/json-lexer.c @@ -11,10 +11,7 @@ * */ -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qint.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "qapi/qmp/json-lexer.h" @@ -30,7 +27,7 @@ */ enum json_lexer_state { - IN_ERROR = 0, + IN_ERROR = 0, /* must really be 0, see json_lexer[] */ IN_DQ_UCODE3, IN_DQ_UCODE2, IN_DQ_UCODE1, @@ -62,6 +59,8 @@ enum json_lexer_state { IN_START, }; +QEMU_BUILD_BUG_ON((int)JSON_MIN <= (int)IN_START); + #define TERMINAL(state) [0 ... 0x7F] = (state) /* Return whether TERMINAL is a terminal state and the transition to it @@ -71,6 +70,8 @@ enum json_lexer_state { (json_lexer[(old_state)][0] == (terminal)) static const uint8_t json_lexer[][256] = { + /* Relies on default initialization to IN_ERROR! */ + /* double quote string */ [IN_DQ_UCODE3] = { ['0' ... '9'] = IN_DQ_STRING, @@ -253,12 +254,12 @@ static const uint8_t json_lexer[][256] = { ['0'] = IN_ZERO, ['1' ... '9'] = IN_NONZERO_NUMBER, ['-'] = IN_NEG_NONZERO_NUMBER, - ['{'] = JSON_OPERATOR, - ['}'] = JSON_OPERATOR, - ['['] = JSON_OPERATOR, - [']'] = JSON_OPERATOR, - [','] = JSON_OPERATOR, - [':'] = JSON_OPERATOR, + ['{'] = JSON_LCURLY, + ['}'] = JSON_RCURLY, + ['['] = JSON_LSQUARE, + [']'] = JSON_RSQUARE, + [','] = JSON_COMMA, + [':'] = JSON_COLON, ['a' ... 'z'] = IN_KEYWORD, ['%'] = IN_ESCAPE, [' '] = IN_WHITESPACE, @@ -272,7 +273,7 @@ void json_lexer_init(JSONLexer *lexer, JSONLexerEmitter func) { lexer->emit = func; lexer->state = IN_START; - lexer->token = qstring_new(); + lexer->token = g_string_sized_new(3); lexer->x = lexer->y = 0; } @@ -287,14 +288,20 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) } do { + assert(lexer->state <= ARRAY_SIZE(json_lexer)); new_state = json_lexer[lexer->state][(uint8_t)ch]; char_consumed = !TERMINAL_NEEDED_LOOKAHEAD(lexer->state, new_state); if (char_consumed) { - qstring_append_chr(lexer->token, ch); + g_string_append_c(lexer->token, ch); } switch (new_state) { - case JSON_OPERATOR: + case JSON_LCURLY: + case JSON_RCURLY: + case JSON_LSQUARE: + case JSON_RSQUARE: + case JSON_COLON: + case JSON_COMMA: case JSON_ESCAPE: case JSON_INTEGER: case JSON_FLOAT: @@ -303,8 +310,7 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) lexer->emit(lexer, lexer->token, new_state, lexer->x, lexer->y); /* fall through */ case JSON_SKIP: - QDECREF(lexer->token); - lexer->token = qstring_new(); + g_string_truncate(lexer->token, 0); new_state = IN_START; break; case IN_ERROR: @@ -322,8 +328,7 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) * induce an error/flush state. */ lexer->emit(lexer, lexer->token, JSON_ERROR, lexer->x, lexer->y); - QDECREF(lexer->token); - lexer->token = qstring_new(); + g_string_truncate(lexer->token, 0); new_state = IN_START; lexer->state = new_state; return 0; @@ -336,10 +341,9 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) /* Do not let a single token grow to an arbitrarily large size, * this is a security consideration. */ - if (lexer->token->length > MAX_TOKEN_SIZE) { + if (lexer->token->len > MAX_TOKEN_SIZE) { lexer->emit(lexer, lexer->token, lexer->state, lexer->x, lexer->y); - QDECREF(lexer->token); - lexer->token = qstring_new(); + g_string_truncate(lexer->token, 0); lexer->state = IN_START; } @@ -369,5 +373,5 @@ int json_lexer_flush(JSONLexer *lexer) void json_lexer_destroy(JSONLexer *lexer) { - QDECREF(lexer->token); + g_string_free(lexer->token, true); } diff --git a/qemu/qobject/json-parser.c b/qemu/qobject/json-parser.c index ac991ba3c..67ed72731 100644 --- a/qemu/qobject/json-parser.c +++ b/qemu/qobject/json-parser.c @@ -1,5 +1,5 @@ /* - * JSON Parser + * JSON Parser * * Copyright IBM, Corp. 2009 * @@ -11,8 +11,8 @@ * */ -#include - +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qapi/qmp/qstring.h" #include "qapi/qmp/qint.h" @@ -22,15 +22,13 @@ #include "qapi/qmp/qbool.h" #include "qapi/qmp/json-parser.h" #include "qapi/qmp/json-lexer.h" +#include "qapi/qmp/json-streamer.h" typedef struct JSONParserContext { Error *err; - struct { - QObject **buf; - size_t pos; - size_t count; - } tokens; + JSONToken *current; + GQueue *buf; } JSONParserContext; #define BUG_ON(cond) assert(!(cond)) @@ -46,59 +44,11 @@ typedef struct JSONParserContext static QObject *parse_value(JSONParserContext *ctxt, va_list *ap); -/** - * Token manipulators - * - * tokens are dictionaries that contain a type, a string value, and geometry information - * about a token identified by the lexer. These are routines that make working with - * these objects a bit easier. - */ -static const char *token_get_value(QObject *obj) -{ - return qdict_get_str(qobject_to_qdict(obj), "token"); -} - -static JSONTokenType token_get_type(QObject *obj) -{ - return qdict_get_int(qobject_to_qdict(obj), "type"); -} - -static int token_is_operator(QObject *obj, char op) -{ - const char *val; - - if (token_get_type(obj) != JSON_OPERATOR) { - return 0; - } - - val = token_get_value(obj); - - return (val[0] == op) && (val[1] == 0); -} - -static int token_is_keyword(QObject *obj, const char *value) -{ - if (token_get_type(obj) != JSON_KEYWORD) { - return 0; - } - - return strcmp(token_get_value(obj), value) == 0; -} - -static int token_is_escape(QObject *obj, const char *value) -{ - if (token_get_type(obj) != JSON_ESCAPE) { - return 0; - } - - return (strcmp(token_get_value(obj), value) == 0); -} - /** * Error handler */ static void GCC_FMT_ATTR(3, 4) parse_error(JSONParserContext *ctxt, - QObject *token, const char *msg, ...) + JSONToken *token, const char *msg, ...) { va_list ap; char message[1024]; @@ -176,9 +126,10 @@ static int hex2decimal(char ch) * \t * \u four-hex-digits */ -static QString *qstring_from_escaped_str(JSONParserContext *ctxt, QObject *token) +static QString *qstring_from_escaped_str(JSONParserContext *ctxt, + JSONToken *token) { - const char *ptr = token_get_value(token); + const char *ptr = token->str; QString *str; int double_quote = 1; @@ -274,73 +225,34 @@ out: return NULL; } -static QObject *parser_context_pop_token(JSONParserContext *ctxt) -{ - QObject *token; - g_assert(ctxt->tokens.pos < ctxt->tokens.count); - token = ctxt->tokens.buf[ctxt->tokens.pos]; - ctxt->tokens.pos++; - return token; -} - -/* Note: parser_context_{peek|pop}_token do not increment the - * token object's refcount. In both cases the references will continue - * to be tracked and cleaned up in parser_context_free(), so do not - * attempt to free the token object. +/* Note: the token object returned by parser_context_peek_token or + * parser_context_pop_token is deleted as soon as parser_context_pop_token + * is called again. */ -static QObject *parser_context_peek_token(JSONParserContext *ctxt) -{ - QObject *token; - g_assert(ctxt->tokens.pos < ctxt->tokens.count); - token = ctxt->tokens.buf[ctxt->tokens.pos]; - return token; -} - -static JSONParserContext parser_context_save(JSONParserContext *ctxt) -{ - JSONParserContext saved_ctxt = {0}; - saved_ctxt.tokens.pos = ctxt->tokens.pos; - saved_ctxt.tokens.count = ctxt->tokens.count; - saved_ctxt.tokens.buf = ctxt->tokens.buf; - return saved_ctxt; -} - -static void parser_context_restore(JSONParserContext *ctxt, - JSONParserContext saved_ctxt) +static JSONToken *parser_context_pop_token(JSONParserContext *ctxt) { - ctxt->tokens.pos = saved_ctxt.tokens.pos; - ctxt->tokens.count = saved_ctxt.tokens.count; - ctxt->tokens.buf = saved_ctxt.tokens.buf; + g_free(ctxt->current); + assert(!g_queue_is_empty(ctxt->buf)); + ctxt->current = g_queue_pop_head(ctxt->buf); + return ctxt->current; } -static void tokens_append_from_iter(QObject *obj, void *opaque) +static JSONToken *parser_context_peek_token(JSONParserContext *ctxt) { - JSONParserContext *ctxt = opaque; - g_assert(ctxt->tokens.pos < ctxt->tokens.count); - ctxt->tokens.buf[ctxt->tokens.pos++] = obj; - qobject_incref(obj); + assert(!g_queue_is_empty(ctxt->buf)); + return g_queue_peek_head(ctxt->buf); } -static JSONParserContext *parser_context_new(QList *tokens) +static JSONParserContext *parser_context_new(GQueue *tokens) { JSONParserContext *ctxt; - size_t count; if (!tokens) { return NULL; } - count = qlist_size(tokens); - if (count == 0) { - return NULL; - } - ctxt = g_malloc0(sizeof(JSONParserContext)); - ctxt->tokens.pos = 0; - ctxt->tokens.count = count; - ctxt->tokens.buf = g_malloc(count * sizeof(QObject *)); - qlist_iter(tokens, tokens_append_from_iter, ctxt); - ctxt->tokens.pos = 0; + ctxt->buf = tokens; return ctxt; } @@ -348,12 +260,12 @@ static JSONParserContext *parser_context_new(QList *tokens) /* to support error propagation, ctxt->err must be freed separately */ static void parser_context_free(JSONParserContext *ctxt) { - int i; if (ctxt) { - for (i = 0; i < ctxt->tokens.count; i++) { - qobject_decref(ctxt->tokens.buf[i]); + while (!g_queue_is_empty(ctxt->buf)) { + parser_context_pop_token(ctxt); } - g_free(ctxt->tokens.buf); + g_free(ctxt->current); + g_queue_free(ctxt->buf); g_free(ctxt); } } @@ -363,8 +275,8 @@ static void parser_context_free(JSONParserContext *ctxt) */ static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap) { - QObject *key = NULL, *token = NULL, *value, *peek; - JSONParserContext saved_ctxt = parser_context_save(ctxt); + QObject *key = NULL, *value; + JSONToken *peek, *token; peek = parser_context_peek_token(ctxt); if (peek == NULL) { @@ -384,7 +296,7 @@ static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap) goto out; } - if (!token_is_operator(token, ':')) { + if (token->type != JSON_COLON) { parse_error(ctxt, token, "missing : in object pair"); goto out; } @@ -402,7 +314,6 @@ static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap) return 0; out: - parser_context_restore(ctxt, saved_ctxt); qobject_decref(key); return -1; @@ -411,17 +322,10 @@ out: static QObject *parse_object(JSONParserContext *ctxt, va_list *ap) { QDict *dict = NULL; - QObject *token, *peek; - JSONParserContext saved_ctxt = parser_context_save(ctxt); + JSONToken *token, *peek; token = parser_context_pop_token(ctxt); - if (token == NULL) { - goto out; - } - - if (!token_is_operator(token, '{')) { - goto out; - } + assert(token && token->type == JSON_LCURLY); dict = qdict_new(); @@ -431,7 +335,7 @@ static QObject *parse_object(JSONParserContext *ctxt, va_list *ap) goto out; } - if (!token_is_operator(peek, '}')) { + if (peek->type != JSON_RCURLY) { if (parse_pair(ctxt, dict, ap) == -1) { goto out; } @@ -442,8 +346,8 @@ static QObject *parse_object(JSONParserContext *ctxt, va_list *ap) goto out; } - while (!token_is_operator(token, '}')) { - if (!token_is_operator(token, ',')) { + while (token->type != JSON_RCURLY) { + if (token->type != JSON_COMMA) { parse_error(ctxt, token, "expected separator in dict"); goto out; } @@ -465,7 +369,6 @@ static QObject *parse_object(JSONParserContext *ctxt, va_list *ap) return QOBJECT(dict); out: - parser_context_restore(ctxt, saved_ctxt); QDECREF(dict); return NULL; } @@ -473,17 +376,10 @@ out: static QObject *parse_array(JSONParserContext *ctxt, va_list *ap) { QList *list = NULL; - QObject *token, *peek; - JSONParserContext saved_ctxt = parser_context_save(ctxt); + JSONToken *token, *peek; token = parser_context_pop_token(ctxt); - if (token == NULL) { - goto out; - } - - if (!token_is_operator(token, '[')) { - goto out; - } + assert(token && token->type == JSON_LSQUARE); list = qlist_new(); @@ -493,7 +389,7 @@ static QObject *parse_array(JSONParserContext *ctxt, va_list *ap) goto out; } - if (!token_is_operator(peek, ']')) { + if (peek->type != JSON_RSQUARE) { QObject *obj; obj = parse_value(ctxt, ap); @@ -510,8 +406,8 @@ static QObject *parse_array(JSONParserContext *ctxt, va_list *ap) goto out; } - while (!token_is_operator(token, ']')) { - if (!token_is_operator(token, ',')) { + while (token->type != JSON_RSQUARE) { + if (token->type != JSON_COMMA) { parse_error(ctxt, token, "expected separator in list"); goto out; } @@ -537,99 +433,68 @@ static QObject *parse_array(JSONParserContext *ctxt, va_list *ap) return QOBJECT(list); out: - parser_context_restore(ctxt, saved_ctxt); QDECREF(list); return NULL; } static QObject *parse_keyword(JSONParserContext *ctxt) { - QObject *token, *ret; - JSONParserContext saved_ctxt = parser_context_save(ctxt); + JSONToken *token; token = parser_context_pop_token(ctxt); - if (token == NULL) { - goto out; - } - - if (token_get_type(token) != JSON_KEYWORD) { - goto out; - } + assert(token && token->type == JSON_KEYWORD); - if (token_is_keyword(token, "true")) { - ret = QOBJECT(qbool_from_bool(true)); - } else if (token_is_keyword(token, "false")) { - ret = QOBJECT(qbool_from_bool(false)); - } else if (token_is_keyword(token, "null")) { - ret = qnull(); - } else { - parse_error(ctxt, token, "invalid keyword `%s'", token_get_value(token)); - goto out; + if (!strcmp(token->str, "true")) { + return QOBJECT(qbool_from_bool(true)); + } else if (!strcmp(token->str, "false")) { + return QOBJECT(qbool_from_bool(false)); + } else if (!strcmp(token->str, "null")) { + return qnull(); } - - return ret; - -out: - parser_context_restore(ctxt, saved_ctxt); - + parse_error(ctxt, token, "invalid keyword '%s'", token->str); return NULL; } static QObject *parse_escape(JSONParserContext *ctxt, va_list *ap) { - QObject *token = NULL, *obj; - JSONParserContext saved_ctxt = parser_context_save(ctxt); + JSONToken *token; if (ap == NULL) { - goto out; + return NULL; } token = parser_context_pop_token(ctxt); - if (token == NULL) { - goto out; - } - - if (token_is_escape(token, "%p")) { - obj = va_arg(*ap, QObject *); - } else if (token_is_escape(token, "%i")) { - obj = QOBJECT(qbool_from_bool(va_arg(*ap, int))); - } else if (token_is_escape(token, "%d")) { - obj = QOBJECT(qint_from_int(va_arg(*ap, int))); - } else if (token_is_escape(token, "%ld")) { - obj = QOBJECT(qint_from_int(va_arg(*ap, long))); - } else if (token_is_escape(token, "%lld") || - token_is_escape(token, "%I64d")) { - obj = QOBJECT(qint_from_int(va_arg(*ap, long long))); - } else if (token_is_escape(token, "%s")) { - obj = QOBJECT(qstring_from_str(va_arg(*ap, const char *))); - } else if (token_is_escape(token, "%f")) { - obj = QOBJECT(qfloat_from_double(va_arg(*ap, double))); - } else { - goto out; + assert(token && token->type == JSON_ESCAPE); + + if (!strcmp(token->str, "%p")) { + return va_arg(*ap, QObject *); + } else if (!strcmp(token->str, "%i")) { + return QOBJECT(qbool_from_bool(va_arg(*ap, int))); + } else if (!strcmp(token->str, "%d")) { + return QOBJECT(qint_from_int(va_arg(*ap, int))); + } else if (!strcmp(token->str, "%ld")) { + return QOBJECT(qint_from_int(va_arg(*ap, long))); + } else if (!strcmp(token->str, "%lld") || + !strcmp(token->str, "%I64d")) { + return QOBJECT(qint_from_int(va_arg(*ap, long long))); + } else if (!strcmp(token->str, "%s")) { + return QOBJECT(qstring_from_str(va_arg(*ap, const char *))); + } else if (!strcmp(token->str, "%f")) { + return QOBJECT(qfloat_from_double(va_arg(*ap, double))); } - - return obj; - -out: - parser_context_restore(ctxt, saved_ctxt); - return NULL; } static QObject *parse_literal(JSONParserContext *ctxt) { - QObject *token, *obj; - JSONParserContext saved_ctxt = parser_context_save(ctxt); + JSONToken *token; token = parser_context_pop_token(ctxt); - if (token == NULL) { - goto out; - } + assert(token); - switch (token_get_type(token)) { + switch (token->type) { case JSON_STRING: - obj = QOBJECT(qstring_from_escaped_str(ctxt, token)); - break; + return QOBJECT(qstring_from_escaped_str(ctxt, token)); case JSON_INTEGER: { /* A possibility exists that this is a whole-valued float where the * fractional part was left out due to being 0 (.0). It's not a big @@ -646,56 +511,57 @@ static QObject *parse_literal(JSONParserContext *ctxt) int64_t value; errno = 0; /* strtoll doesn't set errno on success */ - value = strtoll(token_get_value(token), NULL, 10); + value = strtoll(token->str, NULL, 10); if (errno != ERANGE) { - obj = QOBJECT(qint_from_int(value)); - break; + return QOBJECT(qint_from_int(value)); } /* fall through to JSON_FLOAT */ } case JSON_FLOAT: - /* FIXME dependent on locale */ - obj = QOBJECT(qfloat_from_double(strtod(token_get_value(token), NULL))); - break; + /* FIXME dependent on locale; a pervasive issue in QEMU */ + /* FIXME our lexer matches RFC 7159 in forbidding Inf or NaN, + * but those might be useful extensions beyond JSON */ + return QOBJECT(qfloat_from_double(strtod(token->str, NULL))); default: - goto out; + abort(); } - - return obj; - -out: - parser_context_restore(ctxt, saved_ctxt); - - return NULL; } static QObject *parse_value(JSONParserContext *ctxt, va_list *ap) { - QObject *obj; + JSONToken *token; - obj = parse_object(ctxt, ap); - if (obj == NULL) { - obj = parse_array(ctxt, ap); - } - if (obj == NULL) { - obj = parse_escape(ctxt, ap); - } - if (obj == NULL) { - obj = parse_keyword(ctxt); - } - if (obj == NULL) { - obj = parse_literal(ctxt); + token = parser_context_peek_token(ctxt); + if (token == NULL) { + parse_error(ctxt, NULL, "premature EOI"); + return NULL; } - return obj; + switch (token->type) { + case JSON_LCURLY: + return parse_object(ctxt, ap); + case JSON_LSQUARE: + return parse_array(ctxt, ap); + case JSON_ESCAPE: + return parse_escape(ctxt, ap); + case JSON_INTEGER: + case JSON_FLOAT: + case JSON_STRING: + return parse_literal(ctxt); + case JSON_KEYWORD: + return parse_keyword(ctxt); + default: + parse_error(ctxt, token, "expecting value"); + return NULL; + } } -QObject *json_parser_parse(QList *tokens, va_list *ap) +QObject *json_parser_parse(GQueue *tokens, va_list *ap) { return json_parser_parse_err(tokens, ap, NULL); } -QObject *json_parser_parse_err(QList *tokens, va_list *ap, Error **errp) +QObject *json_parser_parse_err(GQueue *tokens, va_list *ap, Error **errp) { JSONParserContext *ctxt = parser_context_new(tokens); QObject *result; diff --git a/qemu/qobject/json-streamer.c b/qemu/qobject/json-streamer.c index 1b2f9b1d1..02516853a 100644 --- a/qemu/qobject/json-streamer.c +++ b/qemu/qobject/json-streamer.c @@ -11,50 +11,56 @@ * */ -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qint.h" -#include "qapi/qmp/qdict.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "qapi/qmp/json-lexer.h" #include "qapi/qmp/json-streamer.h" #define MAX_TOKEN_SIZE (64ULL << 20) +#define MAX_TOKEN_COUNT (2ULL << 20) #define MAX_NESTING (1ULL << 10) -static void json_message_process_token(JSONLexer *lexer, QString *token, JSONTokenType type, int x, int y) +static void json_message_free_tokens(JSONMessageParser *parser) +{ + if (parser->tokens) { + g_queue_free(parser->tokens); + parser->tokens = NULL; + } +} + +static void json_message_process_token(JSONLexer *lexer, GString *input, + JSONTokenType type, int x, int y) { JSONMessageParser *parser = container_of(lexer, JSONMessageParser, lexer); - QDict *dict; - - if (type == JSON_OPERATOR) { - switch (qstring_get_str(token)[0]) { - case '{': - parser->brace_count++; - break; - case '}': - parser->brace_count--; - break; - case '[': - parser->bracket_count++; - break; - case ']': - parser->bracket_count--; - break; - default: - break; - } + JSONToken *token; + + switch (type) { + case JSON_LCURLY: + parser->brace_count++; + break; + case JSON_RCURLY: + parser->brace_count--; + break; + case JSON_LSQUARE: + parser->bracket_count++; + break; + case JSON_RSQUARE: + parser->bracket_count--; + break; + default: + break; } - dict = qdict_new(); - qdict_put(dict, "type", qint_from_int(type)); - QINCREF(token); - qdict_put(dict, "token", token); - qdict_put(dict, "x", qint_from_int(x)); - qdict_put(dict, "y", qint_from_int(y)); + token = g_malloc(sizeof(JSONToken) + input->len + 1); + token->type = type; + memcpy(token->str, input->str, input->len); + token->str[input->len] = 0; + token->x = x; + token->y = y; - parser->token_size += token->length; + parser->token_size += input->len; - qlist_append(parser->tokens, dict); + g_queue_push_tail(parser->tokens, token); if (type == JSON_ERROR) { goto out_emit_bad; @@ -64,41 +70,39 @@ static void json_message_process_token(JSONLexer *lexer, QString *token, JSONTok parser->bracket_count == 0)) { goto out_emit; } else if (parser->token_size > MAX_TOKEN_SIZE || - parser->bracket_count > MAX_NESTING || - parser->brace_count > MAX_NESTING) { + g_queue_get_length(parser->tokens) > MAX_TOKEN_COUNT || + parser->bracket_count + parser->brace_count > MAX_NESTING) { /* Security consideration, we limit total memory allocated per object * and the maximum recursion depth that a message can force. */ - goto out_emit; + goto out_emit_bad; } return; out_emit_bad: - /* clear out token list and tell the parser to emit and error + /* + * Clear out token list and tell the parser to emit an error * indication by passing it a NULL list */ - QDECREF(parser->tokens); - parser->tokens = NULL; + json_message_free_tokens(parser); out_emit: /* send current list of tokens to parser and reset tokenizer */ parser->brace_count = 0; parser->bracket_count = 0; + /* parser->emit takes ownership of parser->tokens. */ parser->emit(parser, parser->tokens); - if (parser->tokens) { - QDECREF(parser->tokens); - } - parser->tokens = qlist_new(); + parser->tokens = g_queue_new(); parser->token_size = 0; } void json_message_parser_init(JSONMessageParser *parser, - void (*func)(JSONMessageParser *, QList *)) + void (*func)(JSONMessageParser *, GQueue *)) { parser->emit = func; parser->brace_count = 0; parser->bracket_count = 0; - parser->tokens = qlist_new(); + parser->tokens = g_queue_new(); parser->token_size = 0; json_lexer_init(&parser->lexer, json_message_process_token); @@ -118,5 +122,5 @@ int json_message_parser_flush(JSONMessageParser *parser) void json_message_parser_destroy(JSONMessageParser *parser) { json_lexer_destroy(&parser->lexer); - QDECREF(parser->tokens); + json_message_free_tokens(parser); } diff --git a/qemu/qobject/qbool.c b/qemu/qobject/qbool.c index 5ff69f0b2..0606bbd2a 100644 --- a/qemu/qobject/qbool.c +++ b/qemu/qobject/qbool.c @@ -11,17 +11,11 @@ * */ +#include "qemu/osdep.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qobject.h" #include "qemu-common.h" -static void qbool_destroy_obj(QObject *obj); - -static const QType qbool_type = { - .code = QTYPE_QBOOL, - .destroy = qbool_destroy_obj, -}; - /** * qbool_from_bool(): Create a new QBool from a bool * @@ -32,8 +26,8 @@ QBool *qbool_from_bool(bool value) QBool *qb; qb = g_malloc(sizeof(*qb)); + qobject_init(QOBJECT(qb), QTYPE_QBOOL); qb->value = value; - QOBJECT_INIT(qb, &qbool_type); return qb; } @@ -51,9 +45,9 @@ bool qbool_get_bool(const QBool *qb) */ QBool *qobject_to_qbool(const QObject *obj) { - if (qobject_type(obj) != QTYPE_QBOOL) + if (!obj || qobject_type(obj) != QTYPE_QBOOL) { return NULL; - + } return container_of(obj, QBool, base); } @@ -61,7 +55,7 @@ QBool *qobject_to_qbool(const QObject *obj) * qbool_destroy_obj(): Free all memory allocated by a * QBool object */ -static void qbool_destroy_obj(QObject *obj) +void qbool_destroy_obj(QObject *obj) { assert(obj != NULL); g_free(qobject_to_qbool(obj)); diff --git a/qemu/qobject/qdict.c b/qemu/qobject/qdict.c index 67b1a58ab..a1285361c 100644 --- a/qemu/qobject/qdict.c +++ b/qemu/qobject/qdict.c @@ -10,6 +10,7 @@ * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include "qapi/qmp/qint.h" #include "qapi/qmp/qfloat.h" #include "qapi/qmp/qdict.h" @@ -18,13 +19,7 @@ #include "qapi/qmp/qobject.h" #include "qemu/queue.h" #include "qemu-common.h" - -static void qdict_destroy_obj(QObject *obj); - -static const QType qdict_type = { - .code = QTYPE_QDICT, - .destroy = qdict_destroy_obj, -}; +#include "qemu/cutils.h" /** * qdict_new(): Create a new QDict @@ -36,7 +31,7 @@ QDict *qdict_new(void) QDict *qdict; qdict = g_malloc0(sizeof(*qdict)); - QOBJECT_INIT(qdict, &qdict_type); + qobject_init(QOBJECT(qdict), QTYPE_QDICT); return qdict; } @@ -46,9 +41,9 @@ QDict *qdict_new(void) */ QDict *qobject_to_qdict(const QObject *obj) { - if (qobject_type(obj) != QTYPE_QDICT) + if (!obj || qobject_type(obj) != QTYPE_QDICT) { return NULL; - + } return container_of(obj, QDict, base); } @@ -184,8 +179,7 @@ size_t qdict_size(const QDict *qdict) /** * qdict_get_obj(): Get a QObject of a specific type */ -static QObject *qdict_get_obj(const QDict *qdict, const char *key, - qtype_code type) +static QObject *qdict_get_obj(const QDict *qdict, const char *key, QType type) { QObject *obj; @@ -229,8 +223,7 @@ double qdict_get_double(const QDict *qdict, const char *key) */ int64_t qdict_get_int(const QDict *qdict, const char *key) { - QObject *obj = qdict_get_obj(qdict, key, QTYPE_QINT); - return qint_get_int(qobject_to_qint(obj)); + return qint_get_int(qobject_to_qint(qdict_get(qdict, key))); } /** @@ -243,8 +236,7 @@ int64_t qdict_get_int(const QDict *qdict, const char *key) */ bool qdict_get_bool(const QDict *qdict, const char *key) { - QObject *obj = qdict_get_obj(qdict, key, QTYPE_QBOOL); - return qbool_get_bool(qobject_to_qbool(obj)); + return qbool_get_bool(qobject_to_qbool(qdict_get(qdict, key))); } /** @@ -270,7 +262,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_obj(qdict, key, QTYPE_QDICT)); + return qobject_to_qdict(qdict_get(qdict, key)); } /** @@ -284,8 +276,7 @@ QDict *qdict_get_qdict(const QDict *qdict, const char *key) */ const char *qdict_get_str(const QDict *qdict, const char *key) { - QObject *obj = qdict_get_obj(qdict, key, QTYPE_QSTRING); - return qstring_get_str(qobject_to_qstring(obj)); + return qstring_get_str(qobject_to_qstring(qdict_get(qdict, key))); } /** @@ -298,13 +289,9 @@ 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) { - QObject *obj; - - obj = qdict_get(qdict, key); - if (!obj || qobject_type(obj) != QTYPE_QINT) - return def_value; + QInt *qint = qobject_to_qint(qdict_get(qdict, key)); - return qint_get_int(qobject_to_qint(obj)); + return qint ? qint_get_int(qint) : def_value; } /** @@ -316,13 +303,9 @@ 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) { - QObject *obj; - - obj = qdict_get(qdict, key); - if (!obj || qobject_type(obj) != QTYPE_QBOOL) - return def_value; + QBool *qbool = qobject_to_qbool(qdict_get(qdict, key)); - return qbool_get_bool(qobject_to_qbool(obj)); + return qbool ? qbool_get_bool(qbool) : def_value; } /** @@ -335,13 +318,9 @@ 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) { - QObject *obj; - - obj = qdict_get(qdict, key); - if (!obj || qobject_type(obj) != QTYPE_QSTRING) - return NULL; + QString *qstr = qobject_to_qstring(qdict_get(qdict, key)); - return qstring_get_str(qobject_to_qstring(obj)); + return qstr ? qstring_get_str(qstr) : NULL; } /** @@ -456,7 +435,7 @@ void qdict_del(QDict *qdict, const char *key) /** * qdict_destroy_obj(): Free all the memory allocated by a QDict */ -static void qdict_destroy_obj(QObject *obj) +void qdict_destroy_obj(QObject *obj) { int i; QDict *qdict; diff --git a/qemu/qobject/qfloat.c b/qemu/qobject/qfloat.c index 7de0992db..d5da84770 100644 --- a/qemu/qobject/qfloat.c +++ b/qemu/qobject/qfloat.c @@ -11,17 +11,11 @@ * */ +#include "qemu/osdep.h" #include "qapi/qmp/qfloat.h" #include "qapi/qmp/qobject.h" #include "qemu-common.h" -static void qfloat_destroy_obj(QObject *obj); - -static const QType qfloat_type = { - .code = QTYPE_QFLOAT, - .destroy = qfloat_destroy_obj, -}; - /** * qfloat_from_int(): Create a new QFloat from a float * @@ -32,8 +26,8 @@ QFloat *qfloat_from_double(double value) QFloat *qf; qf = g_malloc(sizeof(*qf)); + qobject_init(QOBJECT(qf), QTYPE_QFLOAT); qf->value = value; - QOBJECT_INIT(qf, &qfloat_type); return qf; } @@ -51,9 +45,9 @@ double qfloat_get_double(const QFloat *qf) */ QFloat *qobject_to_qfloat(const QObject *obj) { - if (qobject_type(obj) != QTYPE_QFLOAT) + if (!obj || qobject_type(obj) != QTYPE_QFLOAT) { return NULL; - + } return container_of(obj, QFloat, base); } @@ -61,7 +55,7 @@ QFloat *qobject_to_qfloat(const QObject *obj) * qfloat_destroy_obj(): Free all memory allocated by a * QFloat object */ -static void qfloat_destroy_obj(QObject *obj) +void qfloat_destroy_obj(QObject *obj) { assert(obj != NULL); g_free(qobject_to_qfloat(obj)); diff --git a/qemu/qobject/qint.c b/qemu/qobject/qint.c index 86b9b04f0..d7d1b3021 100644 --- a/qemu/qobject/qint.c +++ b/qemu/qobject/qint.c @@ -10,17 +10,11 @@ * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include "qapi/qmp/qint.h" #include "qapi/qmp/qobject.h" #include "qemu-common.h" -static void qint_destroy_obj(QObject *obj); - -static const QType qint_type = { - .code = QTYPE_QINT, - .destroy = qint_destroy_obj, -}; - /** * qint_from_int(): Create a new QInt from an int64_t * @@ -31,8 +25,8 @@ QInt *qint_from_int(int64_t value) QInt *qi; qi = g_malloc(sizeof(*qi)); + qobject_init(QOBJECT(qi), QTYPE_QINT); qi->value = value; - QOBJECT_INIT(qi, &qint_type); return qi; } @@ -50,9 +44,9 @@ int64_t qint_get_int(const QInt *qi) */ QInt *qobject_to_qint(const QObject *obj) { - if (qobject_type(obj) != QTYPE_QINT) + if (!obj || qobject_type(obj) != QTYPE_QINT) { return NULL; - + } return container_of(obj, QInt, base); } @@ -60,7 +54,7 @@ QInt *qobject_to_qint(const QObject *obj) * qint_destroy_obj(): Free all memory allocated by a * QInt object */ -static void qint_destroy_obj(QObject *obj) +void qint_destroy_obj(QObject *obj) { assert(obj != NULL); g_free(qobject_to_qint(obj)); diff --git a/qemu/qobject/qjson.c b/qemu/qobject/qjson.c index 33f8ef530..ef160d211 100644 --- a/qemu/qobject/qjson.c +++ b/qemu/qobject/qjson.c @@ -11,6 +11,7 @@ * */ +#include "qemu/osdep.h" #include "qapi/qmp/json-lexer.h" #include "qapi/qmp/json-parser.h" #include "qapi/qmp/json-streamer.h" @@ -20,6 +21,7 @@ #include "qapi/qmp/qbool.h" #include "qapi/qmp/qfloat.h" #include "qapi/qmp/qdict.h" +#include "qemu/unicode.h" typedef struct JSONParsingState { @@ -28,7 +30,7 @@ typedef struct JSONParsingState QObject *result; } JSONParsingState; -static void parse_json(JSONMessageParser *parser, QList *tokens) +static void parse_json(JSONMessageParser *parser, GQueue *tokens) { JSONParsingState *s = container_of(parser, JSONParsingState, parser); s->result = json_parser_parse(tokens, s->ap); @@ -237,6 +239,15 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent) char buffer[1024]; int len; + /* FIXME: snprintf() is locale dependent; but JSON requires + * numbers to be formatted as if in the C locale. Dependence + * on C locale is a pervasive issue in QEMU. */ + /* FIXME: This risks printing Inf or NaN, which are not valid + * JSON values. */ + /* FIXME: the default precision of 6 for %f often causes + * rounding errors; we should be using DBL_DECIMAL_DIG (17), + * and only rounding to a shorter number if the result would + * still produce the same floating point value. */ len = snprintf(buffer, sizeof(buffer), "%f", qfloat_get_double(val)); while (len > 0 && buffer[len - 1] == '0') { len--; @@ -247,7 +258,7 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent) } else { buffer[len] = 0; } - + qstring_append(str, buffer); break; } diff --git a/qemu/qobject/qlist.c b/qemu/qobject/qlist.c index 1ced0de58..1ec74de2b 100644 --- a/qemu/qobject/qlist.c +++ b/qemu/qobject/qlist.c @@ -10,18 +10,12 @@ * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include "qapi/qmp/qlist.h" #include "qapi/qmp/qobject.h" #include "qemu/queue.h" #include "qemu-common.h" -static void qlist_destroy_obj(QObject *obj); - -static const QType qlist_type = { - .code = QTYPE_QLIST, - .destroy = qlist_destroy_obj, -}; - /** * qlist_new(): Create a new QList * @@ -32,8 +26,8 @@ QList *qlist_new(void) QList *qlist; qlist = g_malloc(sizeof(*qlist)); + qobject_init(QOBJECT(qlist), QTYPE_QLIST); QTAILQ_INIT(&qlist->head); - QOBJECT_INIT(qlist, &qlist_type); return qlist; } @@ -142,17 +136,16 @@ size_t qlist_size(const QList *qlist) */ QList *qobject_to_qlist(const QObject *obj) { - if (qobject_type(obj) != QTYPE_QLIST) { + if (!obj || qobject_type(obj) != QTYPE_QLIST) { return NULL; } - return container_of(obj, QList, base); } /** * qlist_destroy_obj(): Free all the memory allocated by a QList */ -static void qlist_destroy_obj(QObject *obj) +void qlist_destroy_obj(QObject *obj) { QList *qlist; QListEntry *entry, *next_entry; diff --git a/qemu/qobject/qnull.c b/qemu/qobject/qnull.c index 9873e266e..c124d0585 100644 --- a/qemu/qobject/qnull.c +++ b/qemu/qobject/qnull.c @@ -10,20 +10,11 @@ * or later. See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qapi/qmp/qobject.h" -static void qnull_destroy_obj(QObject *obj) -{ - assert(0); -} - -static const QType qnull_type = { - .code = QTYPE_QNULL, - .destroy = qnull_destroy_obj, -}; - QObject qnull_ = { - .type = &qnull_type, + .type = QTYPE_QNULL, .refcnt = 1, }; diff --git a/qemu/qobject/qobject.c b/qemu/qobject/qobject.c new file mode 100644 index 000000000..cd41fb940 --- /dev/null +++ b/qemu/qobject/qobject.c @@ -0,0 +1,35 @@ +/* + * QObject + * + * Copyright (C) 2015 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 + * or later. See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qfloat.h" +#include "qapi/qmp/qint.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qstring.h" + +static void (*qdestroy[QTYPE__MAX])(QObject *) = { + [QTYPE_NONE] = NULL, /* No such object exists */ + [QTYPE_QNULL] = NULL, /* qnull_ is indestructible */ + [QTYPE_QINT] = qint_destroy_obj, + [QTYPE_QSTRING] = qstring_destroy_obj, + [QTYPE_QDICT] = qdict_destroy_obj, + [QTYPE_QLIST] = qlist_destroy_obj, + [QTYPE_QFLOAT] = qfloat_destroy_obj, + [QTYPE_QBOOL] = qbool_destroy_obj, +}; + +void qobject_destroy(QObject *obj) +{ + assert(!obj->refcnt); + assert(QTYPE_QNULL < obj->type && obj->type < QTYPE__MAX); + qdestroy[obj->type](obj); +} diff --git a/qemu/qobject/qstring.c b/qemu/qobject/qstring.c index 607b7a142..5da7b5f37 100644 --- a/qemu/qobject/qstring.c +++ b/qemu/qobject/qstring.c @@ -10,17 +10,11 @@ * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include "qapi/qmp/qobject.h" #include "qapi/qmp/qstring.h" #include "qemu-common.h" -static void qstring_destroy_obj(QObject *obj); - -static const QType qstring_type = { - .code = QTYPE_QSTRING, - .destroy = qstring_destroy_obj, -}; - /** * qstring_new(): Create a new empty QString * @@ -49,6 +43,7 @@ QString *qstring_from_substr(const char *str, int start, int end) QString *qstring; qstring = g_malloc(sizeof(*qstring)); + qobject_init(QOBJECT(qstring), QTYPE_QSTRING); qstring->length = end - start + 1; qstring->capacity = qstring->length; @@ -57,7 +52,6 @@ QString *qstring_from_substr(const char *str, int start, int end) memcpy(qstring->string, str + start, qstring->length); qstring->string[qstring->length] = 0; - QOBJECT_INIT(qstring, &qstring_type); return qstring; } @@ -117,9 +111,9 @@ void qstring_append_chr(QString *qstring, int c) */ QString *qobject_to_qstring(const QObject *obj) { - if (qobject_type(obj) != QTYPE_QSTRING) + if (!obj || qobject_type(obj) != QTYPE_QSTRING) { return NULL; - + } return container_of(obj, QString, base); } @@ -138,7 +132,7 @@ const char *qstring_get_str(const QString *qstring) * qstring_destroy_obj(): Free all memory allocated by a QString * object */ -static void qstring_destroy_obj(QObject *obj) +void qstring_destroy_obj(QObject *obj) { QString *qs; diff --git a/qemu/qom/Makefile.objs b/qemu/qom/Makefile.objs index 985003bd0..516349eec 100644 --- a/qemu/qom/Makefile.objs +++ b/qemu/qom/Makefile.objs @@ -1,3 +1,4 @@ -common-obj-y = object.o container.o qom-qobject.o -common-obj-y += cpu.o -common-obj-y += object_interfaces.o +qom-obj-y = object.o container.o qom-qobject.o +qom-obj-y += object_interfaces.o + +common-obj-y = cpu.o diff --git a/qemu/qom/container.c b/qemu/qom/container.c index 62b1648ad..c9eb49b01 100644 --- a/qemu/qom/container.c +++ b/qemu/qom/container.c @@ -10,9 +10,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "qom/object.h" #include "qemu/module.h" -#include static const TypeInfo container_info = { .name = "container", diff --git a/qemu/qom/cpu.c b/qemu/qom/cpu.c index eb9cfeca1..c9007d3d0 100644 --- a/qemu/qom/cpu.c +++ b/qemu/qom/cpu.c @@ -18,11 +18,14 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qom/cpu.h" #include "sysemu/kvm.h" #include "qemu/notify.h" #include "qemu/log.h" +#include "exec/log.h" #include "qemu/error-report.h" #include "sysemu/sysemu.h" @@ -114,6 +117,8 @@ void cpu_reset_interrupt(CPUState *cpu, int mask) void cpu_exit(CPUState *cpu) { cpu->exit_request = 1; + /* Ensure cpu_exec will see the exit request after TCG has exited. */ + smp_wmb(); cpu->tcg_exit_req = 1; } @@ -128,7 +133,7 @@ int cpu_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cpu, static int cpu_common_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cpu, void *opaque) { - return -1; + return 0; } int cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cpu, @@ -157,7 +162,7 @@ int cpu_write_elf64_qemunote(WriteCoreDumpFunction f, CPUState *cpu, static int cpu_common_write_elf64_qemunote(WriteCoreDumpFunction f, CPUState *cpu, void *opaque) { - return -1; + return 0; } int cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu, @@ -186,6 +191,14 @@ static int cpu_common_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg) return 0; } +static bool cpu_common_debug_check_watchpoint(CPUState *cpu, CPUWatchpoint *wp) +{ + /* If no extra check is required, QEMU watchpoint match can be considered + * as an architectural match. + */ + return true; +} + bool target_words_bigendian(void); static bool cpu_common_virtio_is_big_endian(CPUState *cpu) { @@ -247,8 +260,9 @@ static void cpu_common_reset(CPUState *cpu) cpu->mem_io_vaddr = 0; cpu->icount_extra = 0; cpu->icount_decr.u32 = 0; - cpu->can_do_io = 0; + cpu->can_do_io = 1; cpu->exception_index = -1; + cpu->crash_occurred = false; memset(cpu->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof(void *)); } @@ -314,6 +328,7 @@ static void cpu_common_initfn(Object *obj) cpu->cpu_index = -1; cpu->gdb_num_regs = cpu->gdb_num_g_regs = cc->gdb_num_core_regs; + qemu_mutex_init(&cpu->work_mutex); QTAILQ_INIT(&cpu->breakpoints); QTAILQ_INIT(&cpu->watchpoints); } @@ -348,6 +363,7 @@ static void cpu_class_init(ObjectClass *klass, void *data) k->gdb_write_register = cpu_common_gdb_write_register; k->virtio_is_big_endian = cpu_common_virtio_is_big_endian; k->debug_excp_handler = cpu_common_noop; + k->debug_check_watchpoint = cpu_common_debug_check_watchpoint; k->cpu_exec_enter = cpu_common_noop; k->cpu_exec_exit = cpu_common_noop; k->cpu_exec_interrupt = cpu_common_exec_interrupt; diff --git a/qemu/qom/object.c b/qemu/qom/object.c index eea8edf3d..8e6e68dff 100644 --- a/qemu/qom/object.c +++ b/qemu/qom/object.c @@ -10,9 +10,11 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qom/object.h" #include "qom/object_interfaces.h" -#include "qemu-common.h" +#include "qemu/cutils.h" #include "qapi/visitor.h" #include "qapi-visit.h" #include "qapi/string-input-visitor.h" @@ -204,7 +206,7 @@ static bool type_is_ancestor(TypeImpl *type, TypeImpl *target_type) { assert(target_type); - /* Check if typename is a direct ancestor of type */ + /* Check if target_type is a direct ancestor of type */ while (type) { if (type == target_type) { return true; @@ -242,6 +244,16 @@ static void type_initialize_interface(TypeImpl *ti, TypeImpl *interface_type, iface_impl->class); } +static void object_property_free(gpointer data) +{ + ObjectProperty *prop = data; + + g_free(prop->name); + g_free(prop->type); + g_free(prop->description); + g_free(prop); +} + static void type_initialize(TypeImpl *ti) { TypeImpl *parent; @@ -261,9 +273,11 @@ static void type_initialize(TypeImpl *ti) GSList *e; int i; - g_assert(parent->class_size <= ti->class_size); + g_assert_cmpint(parent->class_size, <=, ti->class_size); memcpy(ti->class, parent->class, parent->class_size); ti->class->interfaces = NULL; + ti->class->properties = g_hash_table_new_full( + g_str_hash, g_str_equal, g_free, object_property_free); for (e = parent->class->interfaces; e; e = e->next) { InterfaceClass *iface = e->data; @@ -288,6 +302,9 @@ static void type_initialize(TypeImpl *ti) type_initialize_interface(ti, t, t); } + } else { + ti->class->properties = g_hash_table_new_full( + g_str_hash, g_str_equal, g_free, object_property_free); } ti->class->type = ti; @@ -333,14 +350,15 @@ void object_initialize_with_type(void *data, size_t size, TypeImpl *type) g_assert(type != NULL); type_initialize(type); - g_assert(type->instance_size >= sizeof(Object)); + g_assert_cmpint(type->instance_size, >=, sizeof(Object)); g_assert(type->abstract == false); - g_assert(size >= type->instance_size); + g_assert_cmpint(size, >=, type->instance_size); memset(obj, 0, type->instance_size); obj->class = type->class; object_ref(obj); - QTAILQ_INIT(&obj->properties); + obj->properties = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, object_property_free); object_init_with_type(obj, type); object_post_init_with_type(obj, type); } @@ -359,29 +377,51 @@ static inline bool object_property_is_child(ObjectProperty *prop) static void object_property_del_all(Object *obj) { - while (!QTAILQ_EMPTY(&obj->properties)) { - ObjectProperty *prop = QTAILQ_FIRST(&obj->properties); - - QTAILQ_REMOVE(&obj->properties, prop, node); - - if (prop->release) { - prop->release(obj, prop->name, prop->opaque); + ObjectProperty *prop; + GHashTableIter iter; + gpointer key, value; + bool released; + + do { + released = false; + g_hash_table_iter_init(&iter, obj->properties); + while (g_hash_table_iter_next(&iter, &key, &value)) { + prop = value; + if (prop->release) { + prop->release(obj, prop->name, prop->opaque); + prop->release = NULL; + released = true; + break; + } + g_hash_table_iter_remove(&iter); } + } while (released); - g_free(prop->name); - g_free(prop->type); - g_free(prop->description); - g_free(prop); - } + g_hash_table_unref(obj->properties); } static void object_property_del_child(Object *obj, Object *child, Error **errp) { ObjectProperty *prop; + GHashTableIter iter; + gpointer key, value; - QTAILQ_FOREACH(prop, &obj->properties, node) { + g_hash_table_iter_init(&iter, obj->properties); + while (g_hash_table_iter_next(&iter, &key, &value)) { + prop = value; + if (object_property_is_child(prop) && prop->opaque == child) { + if (prop->release) { + prop->release(obj, prop->name, prop->opaque); + prop->release = NULL; + } + break; + } + } + g_hash_table_iter_init(&iter, obj->properties); + while (g_hash_table_iter_next(&iter, &key, &value)) { + prop = value; if (object_property_is_child(prop) && prop->opaque == child) { - object_property_del(obj, prop->name, errp); + g_hash_table_iter_remove(&iter); break; } } @@ -413,7 +453,7 @@ static void object_finalize(void *data) object_property_del_all(obj); object_deinit(obj, ti); - g_assert(obj->ref == 0); + g_assert_cmpint(obj->ref, ==, 0); if (obj->free) { obj->free(obj); } @@ -775,23 +815,44 @@ void object_class_foreach(void (*fn)(ObjectClass *klass, void *opaque), enumerating_types = false; } -int object_child_foreach(Object *obj, int (*fn)(Object *child, void *opaque), - void *opaque) +static int do_object_child_foreach(Object *obj, + int (*fn)(Object *child, void *opaque), + void *opaque, bool recurse) { - ObjectProperty *prop, *next; + GHashTableIter iter; + ObjectProperty *prop; int ret = 0; - QTAILQ_FOREACH_SAFE(prop, &obj->properties, node, next) { + g_hash_table_iter_init(&iter, obj->properties); + while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) { if (object_property_is_child(prop)) { - ret = fn(prop->opaque, opaque); + Object *child = prop->opaque; + + ret = fn(child, opaque); if (ret != 0) { break; } + if (recurse) { + do_object_child_foreach(child, fn, opaque, true); + } } } return ret; } +int object_child_foreach(Object *obj, int (*fn)(Object *child, void *opaque), + void *opaque) +{ + return do_object_child_foreach(obj, fn, opaque, false); +} + +int object_child_foreach_recursive(Object *obj, + int (*fn)(Object *child, void *opaque), + void *opaque) +{ + return do_object_child_foreach(obj, fn, opaque, true); +} + static void object_class_get_list_tramp(ObjectClass *klass, void *opaque) { GSList **list = opaque; @@ -814,7 +875,7 @@ void object_ref(Object *obj) if (!obj) { return; } - atomic_inc(&obj->ref); + atomic_inc(&obj->ref); } void object_unref(Object *obj) @@ -822,7 +883,7 @@ void object_unref(Object *obj) if (!obj) { return; } - g_assert(obj->ref > 0); + g_assert_cmpint(obj->ref, >, 0); /* parent always holds a reference to its children */ if (atomic_fetch_dec(&obj->ref) == 1) { @@ -860,13 +921,11 @@ object_property_add(Object *obj, const char *name, const char *type, return ret; } - QTAILQ_FOREACH(prop, &obj->properties, node) { - if (strcmp(prop->name, name) == 0) { - error_setg(errp, "attempt to add duplicate property '%s'" - " to object (type '%s')", name, - object_get_typename(obj)); - return NULL; - } + if (object_property_find(obj, name, NULL) != NULL) { + error_setg(errp, "attempt to add duplicate property '%s'" + " to object (type '%s')", name, + object_get_typename(obj)); + return NULL; } prop = g_malloc0(sizeof(*prop)); @@ -879,7 +938,41 @@ object_property_add(Object *obj, const char *name, const char *type, prop->release = release; prop->opaque = opaque; - QTAILQ_INSERT_TAIL(&obj->properties, prop, node); + g_hash_table_insert(obj->properties, prop->name, prop); + return prop; +} + +ObjectProperty * +object_class_property_add(ObjectClass *klass, + const char *name, + const char *type, + ObjectPropertyAccessor *get, + ObjectPropertyAccessor *set, + ObjectPropertyRelease *release, + void *opaque, + Error **errp) +{ + ObjectProperty *prop; + + if (object_class_property_find(klass, name, NULL) != NULL) { + error_setg(errp, "attempt to add duplicate property '%s'" + " to object (type '%s')", name, + object_class_get_name(klass)); + return NULL; + } + + prop = g_malloc0(sizeof(*prop)); + + prop->name = g_strdup(name); + prop->type = g_strdup(type); + + prop->get = get; + prop->set = set; + prop->release = release; + prop->opaque = opaque; + + g_hash_table_insert(klass->properties, g_strdup(name), prop); + return prop; } @@ -887,34 +980,76 @@ ObjectProperty *object_property_find(Object *obj, const char *name, Error **errp) { ObjectProperty *prop; + ObjectClass *klass = object_get_class(obj); - QTAILQ_FOREACH(prop, &obj->properties, node) { - if (strcmp(prop->name, name) == 0) { - return prop; - } + prop = object_class_property_find(klass, name, NULL); + if (prop) { + return prop; + } + + prop = g_hash_table_lookup(obj->properties, name); + if (prop) { + return prop; } error_setg(errp, "Property '.%s' not found", name); return NULL; } +void object_property_iter_init(ObjectPropertyIterator *iter, + Object *obj) +{ + g_hash_table_iter_init(&iter->iter, obj->properties); + iter->nextclass = object_get_class(obj); +} + +ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter) +{ + gpointer key, val; + while (!g_hash_table_iter_next(&iter->iter, &key, &val)) { + if (!iter->nextclass) { + return NULL; + } + g_hash_table_iter_init(&iter->iter, iter->nextclass->properties); + iter->nextclass = object_class_get_parent(iter->nextclass); + } + return val; +} + +ObjectProperty *object_class_property_find(ObjectClass *klass, const char *name, + Error **errp) +{ + ObjectProperty *prop; + ObjectClass *parent_klass; + + parent_klass = object_class_get_parent(klass); + if (parent_klass) { + prop = object_class_property_find(parent_klass, name, NULL); + if (prop) { + return prop; + } + } + + prop = g_hash_table_lookup(klass->properties, name); + if (!prop) { + error_setg(errp, "Property '.%s' not found", name); + } + return prop; +} + void object_property_del(Object *obj, const char *name, Error **errp) { - ObjectProperty *prop = object_property_find(obj, name, errp); - if (prop == NULL) { + ObjectProperty *prop = g_hash_table_lookup(obj->properties, name); + + if (!prop) { + error_setg(errp, "Property '.%s' not found", name); return; } if (prop->release) { prop->release(obj, name, prop->opaque); } - - QTAILQ_REMOVE(&obj->properties, prop, node); - - g_free(prop->name); - g_free(prop->type); - g_free(prop->description); - g_free(prop); + g_hash_table_remove(obj->properties, name); } void object_property_get(Object *obj, Visitor *v, const char *name, @@ -928,7 +1063,7 @@ void object_property_get(Object *obj, Visitor *v, const char *name, if (!prop->get) { error_setg(errp, QERR_PERMISSION_DENIED); } else { - prop->get(obj, v, prop->opaque, name, errp); + prop->get(obj, v, name, prop->opaque, errp); } } @@ -943,7 +1078,7 @@ void object_property_set(Object *obj, Visitor *v, const char *name, if (!prop->set) { error_setg(errp, QERR_PERMISSION_DENIED); } else { - prop->set(obj, v, prop->opaque, name, errp); + prop->set(obj, v, name, prop->opaque, errp); } } @@ -1079,6 +1214,7 @@ typedef struct EnumProperty { int object_property_get_enum(Object *obj, const char *name, const char *typename, Error **errp) { + Error *err = NULL; StringOutputVisitor *sov; StringInputVisitor *siv; char *str; @@ -1100,12 +1236,17 @@ int object_property_get_enum(Object *obj, const char *name, enumprop = prop->opaque; sov = string_output_visitor_new(false); - object_property_get(obj, string_output_get_visitor(sov), name, errp); + object_property_get(obj, string_output_get_visitor(sov), name, &err); + if (err) { + error_propagate(errp, err); + string_output_visitor_cleanup(sov); + return 0; + } str = string_output_get_string(sov); siv = string_input_visitor_new(str); string_output_visitor_cleanup(sov); - visit_type_enum(string_input_get_visitor(siv), - &ret, enumprop->strings, NULL, name, errp); + visit_type_enum(string_input_get_visitor(siv), name, &ret, + enumprop->strings, errp); g_free(str); string_input_visitor_cleanup(siv); @@ -1116,51 +1257,56 @@ int object_property_get_enum(Object *obj, const char *name, void object_property_get_uint16List(Object *obj, const char *name, uint16List **list, Error **errp) { + Error *err = NULL; StringOutputVisitor *ov; StringInputVisitor *iv; char *str; ov = string_output_visitor_new(false); object_property_get(obj, string_output_get_visitor(ov), - name, errp); + name, &err); + if (err) { + error_propagate(errp, err); + goto out; + } str = string_output_get_string(ov); iv = string_input_visitor_new(str); - visit_type_uint16List(string_input_get_visitor(iv), - list, NULL, errp); + visit_type_uint16List(string_input_get_visitor(iv), NULL, list, errp); g_free(str); - string_output_visitor_cleanup(ov); string_input_visitor_cleanup(iv); +out: + string_output_visitor_cleanup(ov); } void object_property_parse(Object *obj, const char *string, const char *name, Error **errp) { - StringInputVisitor *mi; - mi = string_input_visitor_new(string); - object_property_set(obj, string_input_get_visitor(mi), name, errp); + StringInputVisitor *siv; + siv = string_input_visitor_new(string); + object_property_set(obj, string_input_get_visitor(siv), name, errp); - string_input_visitor_cleanup(mi); + string_input_visitor_cleanup(siv); } char *object_property_print(Object *obj, const char *name, bool human, Error **errp) { - StringOutputVisitor *mo; + StringOutputVisitor *sov; char *string = NULL; Error *local_err = NULL; - mo = string_output_visitor_new(human); - object_property_get(obj, string_output_get_visitor(mo), name, &local_err); + sov = string_output_visitor_new(human); + object_property_get(obj, string_output_get_visitor(sov), name, &local_err); if (local_err) { error_propagate(errp, local_err); goto out; } - string = string_output_get_string(mo); + string = string_output_get_string(sov); out: - string_output_visitor_cleanup(mo); + string_output_visitor_cleanup(sov); return string; } @@ -1190,14 +1336,15 @@ Object *object_get_objects_root(void) return container_get(object_get_root(), "/objects"); } -static void object_get_child_property(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void object_get_child_property(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { Object *child = opaque; gchar *path; path = object_get_canonical_path(child); - visit_type_str(v, &path, name, errp); + visit_type_str(v, name, &path, errp); g_free(path); } @@ -1259,8 +1406,9 @@ typedef struct { ObjectPropertyLinkFlags flags; } LinkProperty; -static void object_get_link_property(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void object_get_link_property(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { LinkProperty *lprop = opaque; Object **child = lprop->child; @@ -1268,11 +1416,11 @@ static void object_get_link_property(Object *obj, Visitor *v, void *opaque, if (*child) { path = object_get_canonical_path(*child); - visit_type_str(v, &path, name, errp); + visit_type_str(v, name, &path, errp); g_free(path); } else { path = (gchar *)""; - visit_type_str(v, &path, name, errp); + visit_type_str(v, name, &path, errp); } } @@ -1299,8 +1447,8 @@ static Object *object_resolve_link(Object *obj, const char *name, target = object_resolve_path_type(path, target_type, &ambiguous); if (ambiguous) { - error_set(errp, ERROR_CLASS_GENERIC_ERROR, - "Path '%s' does not uniquely identify an object", path); + error_setg(errp, "Path '%s' does not uniquely identify an object", + path); } else if (!target) { target = object_resolve_path(path, &ambiguous); if (target || ambiguous) { @@ -1316,8 +1464,9 @@ static Object *object_resolve_link(Object *obj, const char *name, return target; } -static void object_set_link_property(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void object_set_link_property(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { Error *local_err = NULL; LinkProperty *prop = opaque; @@ -1326,7 +1475,7 @@ static void object_set_link_property(Object *obj, Visitor *v, void *opaque, Object *new_target = NULL; char *path = NULL; - visit_type_str(v, &path, name, &local_err); + visit_type_str(v, name, &path, &local_err); if (!local_err && strcmp(path, "") != 0) { new_target = object_resolve_link(obj, name, path, &local_err); @@ -1422,11 +1571,13 @@ void object_property_add_const_link(Object *obj, const char *name, gchar *object_get_canonical_path_component(Object *obj) { ObjectProperty *prop = NULL; + GHashTableIter iter; g_assert(obj); g_assert(obj->parent != NULL); - QTAILQ_FOREACH(prop, &obj->parent->properties, node) { + g_hash_table_iter_init(&iter, obj->parent->properties); + while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) { if (!object_property_is_child(prop)) { continue; } @@ -1510,11 +1661,13 @@ static Object *object_resolve_partial_path(Object *parent, bool *ambiguous) { Object *obj; + GHashTableIter iter; ObjectProperty *prop; obj = object_resolve_abs_path(parent, parts, typename, 0); - QTAILQ_FOREACH(prop, &parent->properties, node) { + g_hash_table_iter_init(&iter, parent->properties); + while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) { Object *found; if (!object_property_is_child(prop)) { @@ -1576,27 +1729,31 @@ typedef struct StringProperty void (*set)(Object *, const char *, Error **); } StringProperty; -static void property_get_str(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void property_get_str(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { StringProperty *prop = opaque; char *value; + Error *err = NULL; - value = prop->get(obj, errp); - if (value) { - visit_type_str(v, &value, name, errp); - g_free(value); + value = prop->get(obj, &err); + if (err) { + error_propagate(errp, err); + return; } + + visit_type_str(v, name, &value, errp); + g_free(value); } -static void property_set_str(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void property_set_str(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { StringProperty *prop = opaque; char *value; Error *local_err = NULL; - visit_type_str(v, &value, name, &local_err); + visit_type_str(v, name, &value, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -1635,30 +1792,59 @@ void object_property_add_str(Object *obj, const char *name, } } +void object_class_property_add_str(ObjectClass *klass, const char *name, + char *(*get)(Object *, Error **), + void (*set)(Object *, const char *, + Error **), + Error **errp) +{ + Error *local_err = NULL; + StringProperty *prop = g_malloc0(sizeof(*prop)); + + prop->get = get; + prop->set = set; + + object_class_property_add(klass, name, "string", + get ? property_get_str : NULL, + set ? property_set_str : NULL, + property_release_str, + prop, &local_err); + if (local_err) { + error_propagate(errp, local_err); + g_free(prop); + } +} + typedef struct BoolProperty { bool (*get)(Object *, Error **); void (*set)(Object *, bool, Error **); } BoolProperty; -static void property_get_bool(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void property_get_bool(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { BoolProperty *prop = opaque; bool value; + Error *err = NULL; + + value = prop->get(obj, &err); + if (err) { + error_propagate(errp, err); + return; + } - value = prop->get(obj, errp); - visit_type_bool(v, &value, name, errp); + visit_type_bool(v, name, &value, errp); } -static void property_set_bool(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void property_set_bool(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { BoolProperty *prop = opaque; bool value; Error *local_err = NULL; - visit_type_bool(v, &value, name, &local_err); + visit_type_bool(v, name, &value, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -1696,23 +1882,56 @@ void object_property_add_bool(Object *obj, const char *name, } } -static void property_get_enum(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +void object_class_property_add_bool(ObjectClass *klass, const char *name, + bool (*get)(Object *, Error **), + void (*set)(Object *, bool, Error **), + Error **errp) +{ + Error *local_err = NULL; + BoolProperty *prop = g_malloc0(sizeof(*prop)); + + prop->get = get; + prop->set = set; + + object_class_property_add(klass, name, "bool", + get ? property_get_bool : NULL, + set ? property_set_bool : NULL, + property_release_bool, + prop, &local_err); + if (local_err) { + error_propagate(errp, local_err); + g_free(prop); + } +} + +static void property_get_enum(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { EnumProperty *prop = opaque; int value; + Error *err = NULL; - value = prop->get(obj, errp); - visit_type_enum(v, &value, prop->strings, NULL, name, errp); + value = prop->get(obj, &err); + if (err) { + error_propagate(errp, err); + return; + } + + visit_type_enum(v, name, &value, prop->strings, errp); } -static void property_set_enum(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void property_set_enum(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { EnumProperty *prop = opaque; int value; + Error *err = NULL; - visit_type_enum(v, &value, prop->strings, NULL, name, errp); + visit_type_enum(v, name, &value, prop->strings, &err); + if (err) { + error_propagate(errp, err); + return; + } prop->set(obj, value, errp); } @@ -1748,12 +1967,37 @@ void object_property_add_enum(Object *obj, const char *name, } } +void object_class_property_add_enum(ObjectClass *klass, const char *name, + const char *typename, + const char * const *strings, + int (*get)(Object *, Error **), + void (*set)(Object *, int, Error **), + Error **errp) +{ + Error *local_err = NULL; + EnumProperty *prop = g_malloc(sizeof(*prop)); + + prop->strings = strings; + prop->get = get; + prop->set = set; + + object_class_property_add(klass, name, typename, + get ? property_get_enum : NULL, + set ? property_set_enum : NULL, + property_release_enum, + prop, &local_err); + if (local_err) { + error_propagate(errp, local_err); + g_free(prop); + } +} + typedef struct TMProperty { void (*get)(Object *, struct tm *, Error **); } TMProperty; -static void property_get_tm(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void property_get_tm(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { TMProperty *prop = opaque; Error *err = NULL; @@ -1764,31 +2008,31 @@ static void property_get_tm(Object *obj, Visitor *v, void *opaque, goto out; } - visit_start_struct(v, NULL, "struct tm", name, 0, &err); + visit_start_struct(v, name, NULL, 0, &err); if (err) { goto out; } - visit_type_int32(v, &value.tm_year, "tm_year", &err); + visit_type_int32(v, "tm_year", &value.tm_year, &err); if (err) { goto out_end; } - visit_type_int32(v, &value.tm_mon, "tm_mon", &err); + visit_type_int32(v, "tm_mon", &value.tm_mon, &err); if (err) { goto out_end; } - visit_type_int32(v, &value.tm_mday, "tm_mday", &err); + visit_type_int32(v, "tm_mday", &value.tm_mday, &err); if (err) { goto out_end; } - visit_type_int32(v, &value.tm_hour, "tm_hour", &err); + visit_type_int32(v, "tm_hour", &value.tm_hour, &err); if (err) { goto out_end; } - visit_type_int32(v, &value.tm_min, "tm_min", &err); + visit_type_int32(v, "tm_min", &value.tm_min, &err); if (err) { goto out_end; } - visit_type_int32(v, &value.tm_sec, "tm_sec", &err); + visit_type_int32(v, "tm_sec", &value.tm_sec, &err); if (err) { goto out_end; } @@ -1827,41 +2071,56 @@ void object_property_add_tm(Object *obj, const char *name, } } +void object_class_property_add_tm(ObjectClass *klass, const char *name, + void (*get)(Object *, struct tm *, Error **), + Error **errp) +{ + Error *local_err = NULL; + TMProperty *prop = g_malloc0(sizeof(*prop)); + + prop->get = get; + + object_class_property_add(klass, name, "struct tm", + get ? property_get_tm : NULL, NULL, + property_release_tm, + prop, &local_err); + if (local_err) { + error_propagate(errp, local_err); + g_free(prop); + } +} + static char *qdev_get_type(Object *obj, Error **errp) { return g_strdup(object_get_typename(obj)); } -static void property_get_uint8_ptr(Object *obj, Visitor *v, - void *opaque, const char *name, - Error **errp) +static void property_get_uint8_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { uint8_t value = *(uint8_t *)opaque; - visit_type_uint8(v, &value, name, errp); + visit_type_uint8(v, name, &value, errp); } -static void property_get_uint16_ptr(Object *obj, Visitor *v, - void *opaque, const char *name, - Error **errp) +static void property_get_uint16_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { uint16_t value = *(uint16_t *)opaque; - visit_type_uint16(v, &value, name, errp); + visit_type_uint16(v, name, &value, errp); } -static void property_get_uint32_ptr(Object *obj, Visitor *v, - void *opaque, const char *name, - Error **errp) +static void property_get_uint32_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { uint32_t value = *(uint32_t *)opaque; - visit_type_uint32(v, &value, name, errp); + visit_type_uint32(v, name, &value, errp); } -static void property_get_uint64_ptr(Object *obj, Visitor *v, - void *opaque, const char *name, - Error **errp) +static void property_get_uint64_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { uint64_t value = *(uint64_t *)opaque; - visit_type_uint64(v, &value, name, errp); + visit_type_uint64(v, name, &value, errp); } void object_property_add_uint8_ptr(Object *obj, const char *name, @@ -1871,6 +2130,13 @@ void object_property_add_uint8_ptr(Object *obj, const char *name, NULL, NULL, (void *)v, errp); } +void object_class_property_add_uint8_ptr(ObjectClass *klass, const char *name, + const uint8_t *v, Error **errp) +{ + object_class_property_add(klass, name, "uint8", property_get_uint8_ptr, + NULL, NULL, (void *)v, errp); +} + void object_property_add_uint16_ptr(Object *obj, const char *name, const uint16_t *v, Error **errp) { @@ -1878,6 +2144,13 @@ void object_property_add_uint16_ptr(Object *obj, const char *name, NULL, NULL, (void *)v, errp); } +void object_class_property_add_uint16_ptr(ObjectClass *klass, const char *name, + const uint16_t *v, Error **errp) +{ + object_class_property_add(klass, name, "uint16", property_get_uint16_ptr, + NULL, NULL, (void *)v, errp); +} + void object_property_add_uint32_ptr(Object *obj, const char *name, const uint32_t *v, Error **errp) { @@ -1885,6 +2158,13 @@ void object_property_add_uint32_ptr(Object *obj, const char *name, NULL, NULL, (void *)v, errp); } +void object_class_property_add_uint32_ptr(ObjectClass *klass, const char *name, + const uint32_t *v, Error **errp) +{ + object_class_property_add(klass, name, "uint32", property_get_uint32_ptr, + NULL, NULL, (void *)v, errp); +} + void object_property_add_uint64_ptr(Object *obj, const char *name, const uint64_t *v, Error **errp) { @@ -1892,21 +2172,28 @@ void object_property_add_uint64_ptr(Object *obj, const char *name, NULL, NULL, (void *)v, errp); } +void object_class_property_add_uint64_ptr(ObjectClass *klass, const char *name, + const uint64_t *v, Error **errp) +{ + object_class_property_add(klass, name, "uint64", property_get_uint64_ptr, + NULL, NULL, (void *)v, errp); +} + typedef struct { Object *target_obj; char *target_name; } AliasProperty; -static void property_get_alias(Object *obj, struct Visitor *v, void *opaque, - const char *name, Error **errp) +static void property_get_alias(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { AliasProperty *prop = opaque; object_property_get(prop->target_obj, v, prop->target_name, errp); } -static void property_set_alias(Object *obj, struct Visitor *v, void *opaque, - const char *name, Error **errp) +static void property_set_alias(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { AliasProperty *prop = opaque; @@ -1989,6 +2276,23 @@ void object_property_set_description(Object *obj, const char *name, op->description = g_strdup(description); } +void object_class_property_set_description(ObjectClass *klass, + const char *name, + const char *description, + Error **errp) +{ + ObjectProperty *op; + + op = g_hash_table_lookup(klass->properties, name); + if (!op) { + error_setg(errp, "Property '.%s' not found", name); + return; + } + + g_free(op->description); + op->description = g_strdup(description); +} + static void object_instance_init(Object *obj) { object_property_add_str(obj, "type", qdev_get_type, NULL, NULL); diff --git a/qemu/qom/object_interfaces.c b/qemu/qom/object_interfaces.c index a66cd6026..393189024 100644 --- a/qemu/qom/object_interfaces.c +++ b/qemu/qom/object_interfaces.c @@ -1,5 +1,10 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qom/object_interfaces.h" #include "qemu/module.h" +#include "qapi-visit.h" +#include "qapi/qmp-output-visitor.h" +#include "qapi/opts-visitor.h" void user_creatable_complete(Object *obj, Error **errp) { @@ -30,6 +35,179 @@ bool user_creatable_can_be_deleted(UserCreatable *uc, Error **errp) } } + +Object *user_creatable_add(const QDict *qdict, + Visitor *v, Error **errp) +{ + char *type = NULL; + char *id = NULL; + Object *obj = NULL; + Error *local_err = NULL, *end_err = NULL; + QDict *pdict; + + pdict = qdict_clone_shallow(qdict); + + visit_start_struct(v, NULL, NULL, 0, &local_err); + if (local_err) { + goto out; + } + + qdict_del(pdict, "qom-type"); + visit_type_str(v, "qom-type", &type, &local_err); + if (local_err) { + goto out_visit; + } + + qdict_del(pdict, "id"); + visit_type_str(v, "id", &id, &local_err); + if (local_err) { + goto out_visit; + } + + obj = user_creatable_add_type(type, id, pdict, v, &local_err); + if (local_err) { + goto out_visit; + } + + out_visit: + visit_end_struct(v, &end_err); + if (end_err) { + error_propagate(&local_err, end_err); + if (obj) { + user_creatable_del(id, NULL); + } + goto out; + } + +out: + QDECREF(pdict); + g_free(id); + g_free(type); + if (local_err) { + error_propagate(errp, local_err); + object_unref(obj); + return NULL; + } + return obj; +} + + +Object *user_creatable_add_type(const char *type, const char *id, + const QDict *qdict, + Visitor *v, Error **errp) +{ + Object *obj; + ObjectClass *klass; + const QDictEntry *e; + Error *local_err = NULL; + + klass = object_class_by_name(type); + if (!klass) { + error_setg(errp, "invalid object type: %s", type); + return NULL; + } + + if (!object_class_dynamic_cast(klass, TYPE_USER_CREATABLE)) { + error_setg(errp, "object type '%s' isn't supported by object-add", + type); + return NULL; + } + + if (object_class_is_abstract(klass)) { + error_setg(errp, "object type '%s' is abstract", type); + return NULL; + } + + obj = object_new(type); + if (qdict) { + for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) { + object_property_set(obj, v, e->key, &local_err); + if (local_err) { + goto out; + } + } + } + + object_property_add_child(object_get_objects_root(), + id, obj, &local_err); + if (local_err) { + goto out; + } + + user_creatable_complete(obj, &local_err); + if (local_err) { + object_property_del(object_get_objects_root(), + id, &error_abort); + goto out; + } +out: + if (local_err) { + error_propagate(errp, local_err); + object_unref(obj); + return NULL; + } + return obj; +} + + +Object *user_creatable_add_opts(QemuOpts *opts, Error **errp) +{ + OptsVisitor *ov; + QDict *pdict; + Object *obj = NULL; + + ov = opts_visitor_new(opts); + pdict = qemu_opts_to_qdict(opts, NULL); + + obj = user_creatable_add(pdict, opts_get_visitor(ov), errp); + opts_visitor_cleanup(ov); + QDECREF(pdict); + return obj; +} + + +int user_creatable_add_opts_foreach(void *opaque, QemuOpts *opts, Error **errp) +{ + bool (*type_predicate)(const char *) = opaque; + Object *obj = NULL; + Error *err = NULL; + const char *type; + + type = qemu_opt_get(opts, "qom-type"); + if (type && type_predicate && + !type_predicate(type)) { + return 0; + } + + obj = user_creatable_add_opts(opts, &err); + if (!obj) { + error_report_err(err); + return -1; + } + object_unref(obj); + return 0; +} + + +void user_creatable_del(const char *id, Error **errp) +{ + Object *container; + Object *obj; + + container = object_get_objects_root(); + obj = object_resolve_path_component(container, id); + if (!obj) { + error_setg(errp, "object '%s' not found", id); + return; + } + + if (!user_creatable_can_be_deleted(USER_CREATABLE(obj), errp)) { + error_setg(errp, "object '%s' is in use, can not be deleted", id); + return; + } + object_unparent(obj); +} + static void register_types(void) { static const TypeInfo uc_interface_info = { diff --git a/qemu/qom/qom-qobject.c b/qemu/qom/qom-qobject.c index 6384b8e98..e6b17c1f1 100644 --- a/qemu/qom/qom-qobject.c +++ b/qemu/qom/qom-qobject.c @@ -9,6 +9,8 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qom/object.h" #include "qom/qom-qobject.h" @@ -19,11 +21,11 @@ void object_property_set_qobject(Object *obj, QObject *value, const char *name, Error **errp) { - QmpInputVisitor *mi; - mi = qmp_input_visitor_new(value); - object_property_set(obj, qmp_input_get_visitor(mi), name, errp); + QmpInputVisitor *qiv; + qiv = qmp_input_visitor_new(value); + object_property_set(obj, qmp_input_get_visitor(qiv), name, errp); - qmp_input_visitor_cleanup(mi); + qmp_input_visitor_cleanup(qiv); } QObject *object_property_get_qobject(Object *obj, const char *name, @@ -31,14 +33,14 @@ QObject *object_property_get_qobject(Object *obj, const char *name, { QObject *ret = NULL; Error *local_err = NULL; - QmpOutputVisitor *mo; + QmpOutputVisitor *qov; - mo = qmp_output_visitor_new(); - object_property_get(obj, qmp_output_get_visitor(mo), name, &local_err); + qov = qmp_output_visitor_new(); + object_property_get(obj, qmp_output_get_visitor(qov), name, &local_err); if (!local_err) { - ret = qmp_output_get_qobject(mo); + ret = qmp_output_get_qobject(qov); } error_propagate(errp, local_err); - qmp_output_visitor_cleanup(mo); + qmp_output_visitor_cleanup(qov); return ret; } diff --git a/qemu/qtest.c b/qemu/qtest.c index 05cefd280..87575bc0b 100644 --- a/qemu/qtest.c +++ b/qemu/qtest.c @@ -11,6 +11,8 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "sysemu/qtest.h" #include "hw/qdev.h" #include "sysemu/char.h" diff --git a/qemu/replay/Makefile.objs b/qemu/replay/Makefile.objs new file mode 100644 index 000000000..fcb3f74d6 --- /dev/null +++ b/qemu/replay/Makefile.objs @@ -0,0 +1,6 @@ +common-obj-y += replay.o +common-obj-y += replay-internal.o +common-obj-y += replay-events.o +common-obj-y += replay-time.o +common-obj-y += replay-input.o +common-obj-y += replay-char.o diff --git a/qemu/replay/replay-char.c b/qemu/replay/replay-char.c new file mode 100755 index 000000000..23b692297 --- /dev/null +++ b/qemu/replay/replay-char.c @@ -0,0 +1,168 @@ +/* + * replay-char.c + * + * Copyright (c) 2010-2016 Institute for System Programming + * of the Russian Academy of Sciences. + * + * 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 +#include +#include + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "sysemu/replay.h" +#include "replay-internal.h" +#include "sysemu/sysemu.h" +#include "sysemu/char.h" + +/* Char drivers that generate qemu_chr_be_write events + that should be saved into the log. */ +static CharDriverState **char_drivers; +static int drivers_count; + +/* Char event attributes. */ +typedef struct CharEvent { + int id; + uint8_t *buf; + size_t len; +} CharEvent; + +static int find_char_driver(CharDriverState *chr) +{ + int i = 0; + for ( ; i < drivers_count ; ++i) { + if (char_drivers[i] == chr) { + return i; + } + } + return -1; +} + +void replay_register_char_driver(CharDriverState *chr) +{ + if (replay_mode == REPLAY_MODE_NONE) { + return; + } + char_drivers = g_realloc(char_drivers, + sizeof(*char_drivers) * (drivers_count + 1)); + char_drivers[drivers_count++] = chr; +} + +void replay_chr_be_write(CharDriverState *s, uint8_t *buf, int len) +{ + CharEvent *event = g_malloc0(sizeof(CharEvent)); + + event->id = find_char_driver(s); + if (event->id < 0) { + fprintf(stderr, "Replay: cannot find char driver\n"); + exit(1); + } + event->buf = g_malloc(len); + memcpy(event->buf, buf, len); + event->len = len; + + replay_add_event(REPLAY_ASYNC_EVENT_CHAR_READ, event, NULL, 0); +} + +void replay_event_char_read_run(void *opaque) +{ + CharEvent *event = (CharEvent *)opaque; + + qemu_chr_be_write_impl(char_drivers[event->id], event->buf, + (int)event->len); + + g_free(event->buf); + g_free(event); +} + +void replay_event_char_read_save(void *opaque) +{ + CharEvent *event = (CharEvent *)opaque; + + replay_put_byte(event->id); + replay_put_array(event->buf, event->len); +} + +void *replay_event_char_read_load(void) +{ + CharEvent *event = g_malloc0(sizeof(CharEvent)); + + event->id = replay_get_byte(); + replay_get_array_alloc(&event->buf, &event->len); + + return event; +} + +void replay_char_write_event_save(int res, int offset) +{ + 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) +{ + 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); + } +} + +int replay_char_read_all_load(uint8_t *buf) +{ + replay_mutex_lock(); + 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); + } +} + +void replay_char_read_all_save_error(int res) +{ + 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) +{ + replay_save_instructions(); + replay_mutex_lock(); + replay_put_event(EVENT_CHAR_READ_ALL); + replay_put_array(buf, offset); + replay_mutex_unlock(); +} diff --git a/qemu/replay/replay-events.c b/qemu/replay/replay-events.c new file mode 100644 index 000000000..3807245ae --- /dev/null +++ b/qemu/replay/replay-events.c @@ -0,0 +1,311 @@ +/* + * replay-events.c + * + * Copyright (c) 2010-2015 Institute for System Programming + * of the Russian Academy of Sciences. + * + * 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/error-report.h" +#include "sysemu/replay.h" +#include "replay-internal.h" +#include "block/aio.h" +#include "ui/input.h" + +typedef struct Event { + ReplayAsyncEventKind event_kind; + void *opaque; + void *opaque2; + uint64_t id; + + QTAILQ_ENTRY(Event) events; +} 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 */ + +static void replay_run_event(Event *event) +{ + switch (event->event_kind) { + case REPLAY_ASYNC_EVENT_BH: + aio_bh_call(event->opaque); + break; + case REPLAY_ASYNC_EVENT_INPUT: + qemu_input_event_send_impl(NULL, (InputEvent *)event->opaque); + qapi_free_InputEvent((InputEvent *)event->opaque); + break; + case REPLAY_ASYNC_EVENT_INPUT_SYNC: + qemu_input_event_sync_impl(); + break; + case REPLAY_ASYNC_EVENT_CHAR_READ: + replay_event_char_read_run(event->opaque); + break; + case REPLAY_ASYNC_EVENT_BLOCK: + aio_bh_call(event->opaque); + break; + default: + error_report("Replay: invalid async event ID (%d) in the queue", + event->event_kind); + exit(1); + break; + } +} + +void replay_enable_events(void) +{ + events_enabled = true; +} + +bool replay_has_events(void) +{ + return !QTAILQ_EMPTY(&events_list); +} + +void replay_flush_events(void) +{ + replay_mutex_lock(); + 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) +{ + if (replay_mode != REPLAY_MODE_NONE) { + events_enabled = false; + /* Flush events queue before waiting of completion */ + replay_flush_events(); + } +} + +void replay_clear_events(void) +{ + replay_mutex_lock(); + 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 */ +void replay_add_event(ReplayAsyncEventKind event_kind, + void *opaque, + void *opaque2, uint64_t id) +{ + assert(event_kind < REPLAY_ASYNC_COUNT); + + if (!replay_file || replay_mode == REPLAY_MODE_NONE + || !events_enabled) { + Event e; + e.event_kind = event_kind; + e.opaque = opaque; + e.opaque2 = opaque2; + e.id = id; + replay_run_event(&e); + return; + } + + Event *event = g_malloc0(sizeof(Event)); + event->event_kind = event_kind; + event->opaque = opaque; + event->opaque2 = opaque2; + event->id = id; + + replay_mutex_lock(); + 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) { + uint64_t id = replay_get_current_step(); + replay_add_event(REPLAY_ASYNC_EVENT_BH, bh, NULL, id); + } else { + qemu_bh_schedule(bh); + } +} + +void replay_add_input_event(struct InputEvent *event) +{ + replay_add_event(REPLAY_ASYNC_EVENT_INPUT, event, NULL, 0); +} + +void replay_add_input_sync_event(void) +{ + replay_add_event(REPLAY_ASYNC_EVENT_INPUT_SYNC, NULL, NULL, 0); +} + +void replay_block_event(QEMUBH *bh, uint64_t id) +{ + if (replay_mode != REPLAY_MODE_NONE && events_enabled) { + replay_add_event(REPLAY_ASYNC_EVENT_BLOCK, bh, NULL, id); + } else { + qemu_bh_schedule(bh); + } +} + +static void replay_save_event(Event *event, int checkpoint) +{ + if (replay_mode != REPLAY_MODE_PLAY) { + /* put the event into the file */ + replay_put_event(EVENT_ASYNC); + replay_put_byte(checkpoint); + replay_put_byte(event->event_kind); + + /* save event-specific data */ + switch (event->event_kind) { + case REPLAY_ASYNC_EVENT_BH: + replay_put_qword(event->id); + break; + case REPLAY_ASYNC_EVENT_INPUT: + replay_save_input_event(event->opaque); + break; + case REPLAY_ASYNC_EVENT_INPUT_SYNC: + break; + case REPLAY_ASYNC_EVENT_CHAR_READ: + replay_event_char_read_save(event->opaque); + break; + case REPLAY_ASYNC_EVENT_BLOCK: + replay_put_qword(event->id); + break; + default: + error_report("Unknown ID %" PRId64 " of replay event", event->id); + exit(1); + } + } +} + +/* Called with replay mutex locked */ +void replay_save_events(int checkpoint) +{ + 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); + } +} + +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; + replay_check_error(); + } + + if (checkpoint != read_checkpoint) { + return NULL; + } + + /* Events that has not to be in the queue */ + switch (read_event_kind) { + case REPLAY_ASYNC_EVENT_BH: + if (read_id == -1) { + read_id = replay_get_qword(); + } + break; + case REPLAY_ASYNC_EVENT_INPUT: + event = g_malloc0(sizeof(Event)); + event->event_kind = 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->opaque = 0; + return event; + case REPLAY_ASYNC_EVENT_CHAR_READ: + event = g_malloc0(sizeof(Event)); + event->event_kind = 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(); + } + break; + default: + error_report("Unknown ID %d of replay event", 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)) { + break; + } + } + + if (event) { + QTAILQ_REMOVE(&events_list, event, events); + } else { + return NULL; + } + + /* Read event-specific data */ + + return event; +} + +/* Called with replay mutex locked */ +void replay_read_events(int checkpoint) +{ + while (replay_data_kind == EVENT_ASYNC) { + Event *event = replay_read_event(checkpoint); + if (!event) { + break; + } + replay_mutex_unlock(); + 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; +} + +void replay_finish_events(void) +{ + events_enabled = false; + replay_clear_events(); +} + +bool replay_events_enabled(void) +{ + return events_enabled; +} diff --git a/qemu/replay/replay-input.c b/qemu/replay/replay-input.c new file mode 100644 index 000000000..06babe0ec --- /dev/null +++ b/qemu/replay/replay-input.c @@ -0,0 +1,169 @@ +/* + * replay-input.c + * + * Copyright (c) 2010-2015 Institute for System Programming + * of the Russian Academy of Sciences. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "sysemu/replay.h" +#include "replay-internal.h" +#include "qemu/notify.h" +#include "ui/input.h" +#include "qapi/qmp-output-visitor.h" +#include "qapi/qmp-input-visitor.h" +#include "qapi-visit.h" + +static InputEvent *qapi_clone_InputEvent(InputEvent *src) +{ + QmpOutputVisitor *qov; + QmpInputVisitor *qiv; + Visitor *ov, *iv; + QObject *obj; + InputEvent *dst = NULL; + + qov = qmp_output_visitor_new(); + ov = qmp_output_get_visitor(qov); + visit_type_InputEvent(ov, NULL, &src, &error_abort); + obj = qmp_output_get_qobject(qov); + qmp_output_visitor_cleanup(qov); + if (!obj) { + return NULL; + } + + qiv = qmp_input_visitor_new(obj); + iv = qmp_input_get_visitor(qiv); + visit_type_InputEvent(iv, NULL, &dst, &error_abort); + qmp_input_visitor_cleanup(qiv); + qobject_decref(obj); + + return dst; +} + +void replay_save_input_event(InputEvent *evt) +{ + InputKeyEvent *key; + InputBtnEvent *btn; + InputMoveEvent *move; + replay_put_dword(evt->type); + + switch (evt->type) { + case INPUT_EVENT_KIND_KEY: + key = evt->u.key.data; + replay_put_dword(key->key->type); + + switch (key->key->type) { + case KEY_VALUE_KIND_NUMBER: + replay_put_qword(key->key->u.number.data); + replay_put_byte(key->down); + break; + case KEY_VALUE_KIND_QCODE: + replay_put_dword(key->key->u.qcode.data); + replay_put_byte(key->down); + break; + case KEY_VALUE_KIND__MAX: + /* keep gcc happy */ + break; + } + break; + case INPUT_EVENT_KIND_BTN: + btn = evt->u.btn.data; + replay_put_dword(btn->button); + replay_put_byte(btn->down); + break; + case INPUT_EVENT_KIND_REL: + move = evt->u.rel.data; + replay_put_dword(move->axis); + replay_put_qword(move->value); + break; + case INPUT_EVENT_KIND_ABS: + move = evt->u.abs.data; + replay_put_dword(move->axis); + replay_put_qword(move->value); + break; + case INPUT_EVENT_KIND__MAX: + /* keep gcc happy */ + break; + } +} + +InputEvent *replay_read_input_event(void) +{ + InputEvent evt; + KeyValue keyValue; + InputKeyEvent key; + key.key = &keyValue; + InputBtnEvent btn; + InputMoveEvent rel; + InputMoveEvent abs; + + evt.type = replay_get_dword(); + switch (evt.type) { + case INPUT_EVENT_KIND_KEY: + evt.u.key.data = &key; + evt.u.key.data->key->type = replay_get_dword(); + + switch (evt.u.key.data->key->type) { + case KEY_VALUE_KIND_NUMBER: + evt.u.key.data->key->u.number.data = replay_get_qword(); + evt.u.key.data->down = replay_get_byte(); + break; + case KEY_VALUE_KIND_QCODE: + evt.u.key.data->key->u.qcode.data = (QKeyCode)replay_get_dword(); + evt.u.key.data->down = replay_get_byte(); + break; + case KEY_VALUE_KIND__MAX: + /* keep gcc happy */ + break; + } + break; + case INPUT_EVENT_KIND_BTN: + evt.u.btn.data = &btn; + evt.u.btn.data->button = (InputButton)replay_get_dword(); + evt.u.btn.data->down = replay_get_byte(); + break; + case INPUT_EVENT_KIND_REL: + evt.u.rel.data = &rel; + evt.u.rel.data->axis = (InputAxis)replay_get_dword(); + evt.u.rel.data->value = replay_get_qword(); + break; + case INPUT_EVENT_KIND_ABS: + evt.u.abs.data = &abs; + evt.u.abs.data->axis = (InputAxis)replay_get_dword(); + evt.u.abs.data->value = replay_get_qword(); + break; + case INPUT_EVENT_KIND__MAX: + /* keep gcc happy */ + break; + } + + return qapi_clone_InputEvent(&evt); +} + +void replay_input_event(QemuConsole *src, InputEvent *evt) +{ + if (replay_mode == REPLAY_MODE_PLAY) { + /* Nothing */ + } else if (replay_mode == REPLAY_MODE_RECORD) { + replay_add_input_event(qapi_clone_InputEvent(evt)); + } else { + qemu_input_event_send_impl(src, evt); + } +} + +void replay_input_sync_event(void) +{ + if (replay_mode == REPLAY_MODE_PLAY) { + /* Nothing */ + } else if (replay_mode == REPLAY_MODE_RECORD) { + replay_add_input_sync_event(); + } else { + qemu_input_event_sync_impl(); + } +} diff --git a/qemu/replay/replay-internal.c b/qemu/replay/replay-internal.c new file mode 100644 index 000000000..5835e8def --- /dev/null +++ b/qemu/replay/replay-internal.c @@ -0,0 +1,207 @@ +/* + * replay-internal.c + * + * Copyright (c) 2010-2015 Institute for System Programming + * of the Russian Academy of Sciences. + * + * 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/replay.h" +#include "replay-internal.h" +#include "qemu/error-report.h" +#include "sysemu/sysemu.h" + +unsigned int replay_data_kind = -1; +static unsigned int replay_has_unread_data; + +/* Mutex to protect reading and writing events to the log. + replay_data_kind and replay_has_unread_data are also protected + by this mutex. + It also protects replay events queue which stores events to be + written or read to the log. */ +static QemuMutex lock; + +/* File for replay writing */ +FILE *replay_file; + +void replay_put_byte(uint8_t byte) +{ + if (replay_file) { + putc(byte, replay_file); + } +} + +void replay_put_event(uint8_t event) +{ + assert(event < EVENT_COUNT); + replay_put_byte(event); +} + + +void replay_put_word(uint16_t word) +{ + replay_put_byte(word >> 8); + replay_put_byte(word); +} + +void replay_put_dword(uint32_t dword) +{ + replay_put_word(dword >> 16); + replay_put_word(dword); +} + +void replay_put_qword(int64_t qword) +{ + replay_put_dword(qword >> 32); + replay_put_dword(qword); +} + +void replay_put_array(const uint8_t *buf, size_t size) +{ + if (replay_file) { + replay_put_dword(size); + fwrite(buf, 1, size, replay_file); + } +} + +uint8_t replay_get_byte(void) +{ + uint8_t byte = 0; + if (replay_file) { + byte = getc(replay_file); + } + return byte; +} + +uint16_t replay_get_word(void) +{ + uint16_t word = 0; + if (replay_file) { + word = replay_get_byte(); + word = (word << 8) + replay_get_byte(); + } + + return word; +} + +uint32_t replay_get_dword(void) +{ + uint32_t dword = 0; + if (replay_file) { + dword = replay_get_word(); + dword = (dword << 16) + replay_get_word(); + } + + return dword; +} + +int64_t replay_get_qword(void) +{ + int64_t qword = 0; + if (replay_file) { + qword = replay_get_dword(); + qword = (qword << 32) + replay_get_dword(); + } + + return qword; +} + +void replay_get_array(uint8_t *buf, size_t *size) +{ + if (replay_file) { + *size = replay_get_dword(); + if (fread(buf, 1, *size, replay_file) != *size) { + error_report("replay read error"); + } + } +} + +void replay_get_array_alloc(uint8_t **buf, size_t *size) +{ + if (replay_file) { + *size = replay_get_dword(); + *buf = g_malloc(*size); + if (fread(*buf, 1, *size, replay_file) != *size) { + error_report("replay read error"); + } + } +} + +void replay_check_error(void) +{ + if (replay_file) { + if (feof(replay_file)) { + error_report("replay file is over"); + qemu_system_vmstop_request_prepare(); + qemu_system_vmstop_request(RUN_STATE_PAUSED); + } else if (ferror(replay_file)) { + error_report("replay file is over or something goes wrong"); + qemu_system_vmstop_request_prepare(); + qemu_system_vmstop_request(RUN_STATE_INTERNAL_ERROR); + } + } +} + +void replay_fetch_data_kind(void) +{ + if (replay_file) { + if (!replay_has_unread_data) { + replay_data_kind = replay_get_byte(); + if (replay_data_kind == EVENT_INSTRUCTION) { + replay_state.instructions_count = replay_get_dword(); + } + replay_check_error(); + replay_has_unread_data = 1; + if (replay_data_kind >= EVENT_COUNT) { + error_report("Replay: unknown event kind %d", replay_data_kind); + exit(1); + } + } + } +} + +void replay_finish_event(void) +{ + replay_has_unread_data = 0; + replay_fetch_data_kind(); +} + +void replay_mutex_init(void) +{ + qemu_mutex_init(&lock); +} + +void replay_mutex_destroy(void) +{ + qemu_mutex_destroy(&lock); +} + +void replay_mutex_lock(void) +{ + qemu_mutex_lock(&lock); +} + +void replay_mutex_unlock(void) +{ + qemu_mutex_unlock(&lock); +} + +/*! Saves cached instructions. */ +void replay_save_instructions(void) +{ + if (replay_file && replay_mode == REPLAY_MODE_RECORD) { + replay_mutex_lock(); + int diff = (int)(replay_get_current_step() - replay_state.current_step); + if (diff > 0) { + replay_put_event(EVENT_INSTRUCTION); + replay_put_dword(diff); + replay_state.current_step += diff; + } + replay_mutex_unlock(); + } +} diff --git a/qemu/replay/replay-internal.h b/qemu/replay/replay-internal.h new file mode 100644 index 000000000..efbf14c8a --- /dev/null +++ b/qemu/replay/replay-internal.h @@ -0,0 +1,158 @@ +#ifndef REPLAY_INTERNAL_H +#define REPLAY_INTERNAL_H + +/* + * replay-internal.h + * + * Copyright (c) 2010-2015 Institute for System Programming + * of the Russian Academy of Sciences. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + + +enum ReplayEvents { + /* for instruction event */ + EVENT_INSTRUCTION, + /* for software interrupt */ + EVENT_INTERRUPT, + /* for emulated exceptions */ + EVENT_EXCEPTION, + /* for async events */ + EVENT_ASYNC, + /* for shutdown request */ + EVENT_SHUTDOWN, + /* for character device write event */ + EVENT_CHAR_WRITE, + /* for character device read all event */ + EVENT_CHAR_READ_ALL, + EVENT_CHAR_READ_ALL_ERROR, + /* for clock read/writes */ + /* some of greater codes are reserved for clocks */ + EVENT_CLOCK, + EVENT_CLOCK_LAST = EVENT_CLOCK + REPLAY_CLOCK_COUNT - 1, + /* for checkpoint event */ + /* some of greater codes are reserved for checkpoints */ + EVENT_CHECKPOINT, + EVENT_CHECKPOINT_LAST = EVENT_CHECKPOINT + CHECKPOINT_COUNT - 1, + /* end of log event */ + EVENT_END, + EVENT_COUNT +}; + +/* Asynchronous events IDs */ + +enum ReplayAsyncEventKind { + REPLAY_ASYNC_EVENT_BH, + REPLAY_ASYNC_EVENT_INPUT, + REPLAY_ASYNC_EVENT_INPUT_SYNC, + REPLAY_ASYNC_EVENT_CHAR_READ, + REPLAY_ASYNC_EVENT_BLOCK, + REPLAY_ASYNC_COUNT +}; + +typedef enum ReplayAsyncEventKind ReplayAsyncEventKind; + +typedef struct ReplayState { + /*! Cached clock values. */ + int64_t cached_clock[REPLAY_CLOCK_COUNT]; + /*! Current step - number of processed instructions and timer events. */ + uint64_t current_step; + /*! Number of instructions to be executed before other events happen. */ + int instructions_count; +} ReplayState; +extern ReplayState replay_state; + +extern unsigned int replay_data_kind; + +/* File for replay writing */ +extern FILE *replay_file; + +void replay_put_byte(uint8_t byte); +void replay_put_event(uint8_t event); +void replay_put_word(uint16_t word); +void replay_put_dword(uint32_t dword); +void replay_put_qword(int64_t qword); +void replay_put_array(const uint8_t *buf, size_t size); + +uint8_t replay_get_byte(void); +uint16_t replay_get_word(void); +uint32_t replay_get_dword(void); +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 */ + +void replay_mutex_init(void); +void replay_mutex_destroy(void); +void replay_mutex_lock(void); +void replay_mutex_unlock(void); + +/*! Checks error status of the file. */ +void replay_check_error(void); + +/*! Finishes processing of the replayed event and fetches + the next event from the log. */ +void replay_finish_event(void); +/*! Reads data type from the file and stores it in the + replay_data_kind variable. */ +void replay_fetch_data_kind(void); + +/*! Saves queued events (like instructions and sound). */ +void replay_save_instructions(void); + +/*! Skips async events until some sync event will be found. + \return true, if event was found */ +bool replay_next_event_is(int event); + +/*! Reads next clock value from the file. + If clock kind read from the file is different from the parameter, + the value is not used. */ +void replay_read_next_clock(unsigned int kind); + +/* Asynchronous events queue */ + +/*! Initializes events' processing internals */ +void replay_init_events(void); +/*! Clears internal data structures for events handling */ +void replay_finish_events(void); +/*! Enables storing events in the queue */ +void replay_enable_events(void); +/*! Flushes events queue */ +void replay_flush_events(void); +/*! Clears events list before loading new VM state */ +void replay_clear_events(void); +/*! Returns true if there are any unsaved events in the queue */ +bool replay_has_events(void); +/*! Saves events from queue into the file */ +void replay_save_events(int checkpoint); +/*! Read events from the file into the input queue */ +void replay_read_events(int checkpoint); +/*! Adds specified async event to the queue */ +void replay_add_event(ReplayAsyncEventKind event_kind, void *opaque, + void *opaque2, uint64_t id); + +/* Input events */ + +/*! Saves input event to the log */ +void replay_save_input_event(InputEvent *evt); +/*! Reads input event from the log */ +InputEvent *replay_read_input_event(void); +/*! Adds input event to the queue */ +void replay_add_input_event(struct InputEvent *event); +/*! Adds input sync event to the queue */ +void replay_add_input_sync_event(void); + +/* Character devices */ + +/*! Called to run char device read event. */ +void replay_event_char_read_run(void *opaque); +/*! Writes char read event to the file. */ +void replay_event_char_read_save(void *opaque); +/*! Reads char event read from the file. */ +void *replay_event_char_read_load(void); + +#endif diff --git a/qemu/replay/replay-time.c b/qemu/replay/replay-time.c new file mode 100644 index 000000000..fffe072c5 --- /dev/null +++ b/qemu/replay/replay-time.c @@ -0,0 +1,65 @@ +/* + * replay-time.c + * + * Copyright (c) 2010-2015 Institute for System Programming + * of the Russian Academy of Sciences. + * + * 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/replay.h" +#include "replay-internal.h" +#include "qemu/error-report.h" + +int64_t replay_save_clock(ReplayClockKind kind, int64_t clock) +{ + replay_save_instructions(); + + if (replay_file) { + replay_mutex_lock(); + replay_put_event(EVENT_CLOCK + kind); + replay_put_qword(clock); + replay_mutex_unlock(); + } + + return clock; +} + +void replay_read_next_clock(ReplayClockKind kind) +{ + unsigned int read_kind = replay_data_kind - EVENT_CLOCK; + + assert(read_kind == kind); + + int64_t clock = replay_get_qword(); + + replay_check_error(); + replay_finish_event(); + + replay_state.cached_clock[read_kind] = clock; +} + +/*! Reads next clock event from the input. */ +int64_t replay_read_clock(ReplayClockKind kind) +{ + 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; + } + + error_report("REPLAY INTERNAL ERROR %d", __LINE__); + exit(1); +} diff --git a/qemu/replay/replay.c b/qemu/replay/replay.c new file mode 100644 index 000000000..167fd2942 --- /dev/null +++ b/qemu/replay/replay.c @@ -0,0 +1,354 @@ +/* + * replay.c + * + * Copyright (c) 2010-2015 Institute for System Programming + * of the Russian Academy of Sciences. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "sysemu/replay.h" +#include "replay-internal.h" +#include "qemu/timer.h" +#include "qemu/main-loop.h" +#include "sysemu/sysemu.h" +#include "qemu/error-report.h" + +/* Current version of the replay mechanism. + Increase it when file format changes. */ +#define REPLAY_VERSION 0xe02004 +/* Size of replay log header */ +#define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t)) + +ReplayMode replay_mode = REPLAY_MODE_NONE; + +/* Name of replay file */ +static char *replay_filename; +ReplayState replay_state; +static GSList *replay_blockers; + +bool replay_next_event_is(int event) +{ + bool res = false; + + /* nothing to skip - not all instructions used */ + if (replay_state.instructions_count != 0) { + assert(replay_data_kind == EVENT_INSTRUCTION); + return event == EVENT_INSTRUCTION; + } + + while (true) { + if (event == replay_data_kind) { + res = true; + } + switch (replay_data_kind) { + case EVENT_SHUTDOWN: + replay_finish_event(); + qemu_system_shutdown_request(); + break; + default: + /* clock, time_t, checkpoint and other events */ + return res; + } + } + return res; +} + +uint64_t replay_get_current_step(void) +{ + return cpu_get_icount_raw(); +} + +int replay_get_instructions(void) +{ + int res = 0; + replay_mutex_lock(); + if (replay_next_event_is(EVENT_INSTRUCTION)) { + res = replay_state.instructions_count; + } + replay_mutex_unlock(); + return res; +} + +void replay_account_executed_instructions(void) +{ + if (replay_mode == REPLAY_MODE_PLAY) { + replay_mutex_lock(); + if (replay_state.instructions_count > 0) { + int count = (int)(replay_get_current_step() + - replay_state.current_step); + replay_state.instructions_count -= count; + replay_state.current_step += count; + if (replay_state.instructions_count == 0) { + assert(replay_data_kind == EVENT_INSTRUCTION); + replay_finish_event(); + /* Wake up iothread. This is required because + timers will not expire until clock counters + will be read from the log. */ + qemu_notify_event(); + } + } + replay_mutex_unlock(); + } +} + +bool replay_exception(void) +{ + if (replay_mode == REPLAY_MODE_RECORD) { + replay_save_instructions(); + replay_mutex_lock(); + replay_put_event(EVENT_EXCEPTION); + replay_mutex_unlock(); + return true; + } else if (replay_mode == REPLAY_MODE_PLAY) { + bool res = replay_has_exception(); + if (res) { + replay_mutex_lock(); + replay_finish_event(); + replay_mutex_unlock(); + } + return res; + } + + return true; +} + +bool replay_has_exception(void) +{ + bool res = false; + if (replay_mode == REPLAY_MODE_PLAY) { + replay_account_executed_instructions(); + replay_mutex_lock(); + res = replay_next_event_is(EVENT_EXCEPTION); + replay_mutex_unlock(); + } + + return res; +} + +bool replay_interrupt(void) +{ + if (replay_mode == REPLAY_MODE_RECORD) { + replay_save_instructions(); + replay_mutex_lock(); + replay_put_event(EVENT_INTERRUPT); + replay_mutex_unlock(); + return true; + } else if (replay_mode == REPLAY_MODE_PLAY) { + bool res = replay_has_interrupt(); + if (res) { + replay_mutex_lock(); + replay_finish_event(); + replay_mutex_unlock(); + } + return res; + } + + return true; +} + +bool replay_has_interrupt(void) +{ + bool res = false; + if (replay_mode == REPLAY_MODE_PLAY) { + replay_account_executed_instructions(); + replay_mutex_lock(); + res = replay_next_event_is(EVENT_INTERRUPT); + replay_mutex_unlock(); + } + return res; +} + +void replay_shutdown_request(void) +{ + if (replay_mode == REPLAY_MODE_RECORD) { + replay_mutex_lock(); + replay_put_event(EVENT_SHUTDOWN); + replay_mutex_unlock(); + } +} + +bool replay_checkpoint(ReplayCheckpoint checkpoint) +{ + bool res = false; + assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST); + replay_save_instructions(); + + if (!replay_file) { + return true; + } + + replay_mutex_lock(); + + if (replay_mode == REPLAY_MODE_PLAY) { + if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) { + replay_finish_event(); + } else if (replay_data_kind != EVENT_ASYNC) { + res = false; + goto out; + } + replay_read_events(checkpoint); + /* replay_read_events may leave some unread events. + Return false if not all of the events associated with + checkpoint were processed */ + res = replay_data_kind != EVENT_ASYNC; + } else if (replay_mode == REPLAY_MODE_RECORD) { + replay_put_event(EVENT_CHECKPOINT + checkpoint); + replay_save_events(checkpoint); + res = true; + } +out: + replay_mutex_unlock(); + return res; +} + +static void replay_enable(const char *fname, int mode) +{ + const char *fmode = NULL; + assert(!replay_file); + + switch (mode) { + case REPLAY_MODE_RECORD: + fmode = "wb"; + break; + case REPLAY_MODE_PLAY: + fmode = "rb"; + break; + default: + fprintf(stderr, "Replay: internal error: invalid replay mode\n"); + exit(1); + } + + 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)); + exit(1); + } + + replay_filename = g_strdup(fname); + + replay_mode = mode; + replay_data_kind = -1; + replay_state.instructions_count = 0; + replay_state.current_step = 0; + + /* skip file header for RECORD and check it for PLAY */ + if (replay_mode == REPLAY_MODE_RECORD) { + fseek(replay_file, HEADER_SIZE, SEEK_SET); + } else if (replay_mode == REPLAY_MODE_PLAY) { + unsigned int version = replay_get_dword(); + if (version != REPLAY_VERSION) { + fprintf(stderr, "Replay: invalid input log file version\n"); + exit(1); + } + /* go to the beginning */ + fseek(replay_file, HEADER_SIZE, SEEK_SET); + replay_fetch_data_kind(); + } + + replay_init_events(); +} + +void replay_configure(QemuOpts *opts) +{ + const char *fname; + const char *rr; + ReplayMode mode = REPLAY_MODE_NONE; + Location loc; + + if (!opts) { + return; + } + + loc_push_none(&loc); + qemu_opts_loc_restore(opts); + + rr = qemu_opt_get(opts, "rr"); + if (!rr) { + /* Just enabling icount */ + goto out; + } else if (!strcmp(rr, "record")) { + mode = REPLAY_MODE_RECORD; + } else if (!strcmp(rr, "replay")) { + mode = REPLAY_MODE_PLAY; + } else { + error_report("Invalid icount rr option: %s", rr); + exit(1); + } + + fname = qemu_opt_get(opts, "rrfile"); + if (!fname) { + error_report("File name not specified for replay"); + exit(1); + } + + replay_enable(fname, mode); + +out: + loc_pop(&loc); +} + +void replay_start(void) +{ + if (replay_mode == REPLAY_MODE_NONE) { + return; + } + + if (replay_blockers) { + error_reportf_err(replay_blockers->data, "Record/replay: "); + exit(1); + } + if (!use_icount) { + error_report("Please enable icount to use record/replay"); + exit(1); + } + + /* Timer for snapshotting will be set up here. */ + + replay_enable_events(); +} + +void replay_finish(void) +{ + if (replay_mode == REPLAY_MODE_NONE) { + return; + } + + replay_save_instructions(); + + /* finalize the file */ + if (replay_file) { + if (replay_mode == REPLAY_MODE_RECORD) { + /* write end event */ + replay_put_event(EVENT_END); + + /* write header */ + fseek(replay_file, 0, SEEK_SET); + replay_put_dword(REPLAY_VERSION); + } + + fclose(replay_file); + replay_file = NULL; + } + if (replay_filename) { + g_free(replay_filename); + replay_filename = NULL; + } + + replay_finish_events(); + replay_mutex_destroy(); +} + +void replay_add_blocker(Error *reason) +{ + replay_blockers = g_slist_prepend(replay_blockers, reason); +} diff --git a/qemu/roms/Makefile b/qemu/roms/Makefile index 7b3f15632..7bd125273 100644 --- a/qemu/roms/Makefile +++ b/qemu/roms/Makefile @@ -35,7 +35,7 @@ powerpc_cross_prefix := $(call find-cross-prefix,powerpc) x86_64_cross_prefix := $(call find-cross-prefix,x86_64) # tag our seabios builds -SEABIOS_VERSION="$(shell cd seabios; git describe --tags --long) by qemu-project.org" +SEABIOS_EXTRAVERSION="-prebuilt.qemu-project.org" # # EfiRom utility is shipped with edk2 / tianocore, in BaseTools/ @@ -64,7 +64,6 @@ default: bios: build-seabios-config-seabios-128k build-seabios-config-seabios-256k cp seabios/builds/seabios-128k/bios.bin ../pc-bios/bios.bin cp seabios/builds/seabios-256k/bios.bin ../pc-bios/bios-256k.bin - cp seabios/builds/seabios-256k/src/fw/*dsdt.aml ../pc-bios/ seavgabios: $(patsubst %,seavgabios-%,$(vgabios_variants)) @@ -78,12 +77,12 @@ build-seabios-config-%: config.% mkdir -p seabios/builds/$* cp $< seabios/builds/$*/.config $(MAKE) -C seabios \ - VERSION=$(SEABIOS_VERSION) \ + EXTRAVERSION=$(SEABIOS_EXTRAVERSION) \ CROSS_COMPILE=$(x86_64_cross_prefix) \ KCONFIG_CONFIG=$(CURDIR)/seabios/builds/$*/.config \ OUT=$(CURDIR)/seabios/builds/$*/ oldnoconfig $(MAKE) -C seabios \ - VERSION=$(SEABIOS_VERSION) \ + EXTRAVERSION=$(SEABIOS_EXTRAVERSION) \ CROSS_COMPILE=$(x86_64_cross_prefix) \ KCONFIG_CONFIG=$(CURDIR)/seabios/builds/$*/.config \ OUT=$(CURDIR)/seabios/builds/$*/ all @@ -120,20 +119,17 @@ efi-rom-%: build-pxe-roms build-efi-roms -ec ipxe/src/bin-x86_64-efi/$(VID)$(DID).efidrv \ -o ../pc-bios/efi-$*.rom -build-pxe-roms: ipxe/src/config/local/general.h - $(MAKE) -C ipxe/src GITVERSION="" \ +build-pxe-roms: + $(MAKE) -C ipxe/src CONFIG=qemu \ CROSS_COMPILE=$(x86_64_cross_prefix) \ $(patsubst %,bin/%.rom,$(pxerom_targets)) -build-efi-roms: build-pxe-roms ipxe/src/config/local/general.h - $(MAKE) -C ipxe/src GITVERSION="" \ +build-efi-roms: build-pxe-roms + $(MAKE) -C ipxe/src CONFIG=qemu \ CROSS_COMPILE=$(x86_64_cross_prefix) \ $(patsubst %,bin-i386-efi/%.efidrv,$(pxerom_targets)) \ $(patsubst %,bin-x86_64-efi/%.efidrv,$(pxerom_targets)) -ipxe/src/config/local/%: config.ipxe.% - cp $< $@ - slof: $(MAKE) -C SLOF CROSS=$(powerpc64_cross_prefix) qemu diff --git a/qemu/roms/SLOF/README b/qemu/roms/SLOF/README index 58e929427..294458880 100644 --- a/qemu/roms/SLOF/README +++ b/qemu/roms/SLOF/README @@ -11,6 +11,8 @@ Index 2.2 Overview of the source code 2.4 Extending the Forth engine 3.0 Limitations +4.0 Submitting patches +5.0 Coding style 1.0 Introduction to Slimline Open Firmware @@ -236,6 +238,34 @@ To add primitives: On a JS21 all memory configurations should work. +4.0 Submitting patches +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Patches for SLOF should be made against https://github.com/aik/SLOF, +the master branch and posted to slof@lists.ozlabs.org. +The patches must be signed using "Signed-off-by" tag with a real name to +confirm that you certify the Developer Certificate of Origin Version 1.1, +see [3] for details. + + +5.0 Coding style +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +New C code submitted to SLOF should follow the coding style guidelines +for the Linux kernel [4] with the following exceptions: + +- in the event that you require a specific width, use a standard type + like int32_t, uint32_t, uint64_t, etc. Don't use Linux kernel internal + types like u32, __u32 or __le32. + +New Forth code should use 4 space indentations and no tabs. Patches for +the old code should keep the existing style which usually is +3 space indentation. + +New assembly code submitted to SLOF should follow the coding style +guidelines for the Linux kernel [4], i.e. indent with tabs, not with spaces. + + Documentation +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -244,3 +274,9 @@ Documentation [2] PAPR Standard, Power.org(TM) Standard for Power Architecture(R) Platform Requirements (Workstation, Server), Version 2.4, December 7, 2009 + +[3] Developer Certificate of Origin Version 1.1 + http://developercertificate.org/ + +[4] Linux kernel coding style + https://github.com/torvalds/linux/blob/master/Documentation/CodingStyle diff --git a/qemu/roms/SLOF/VERSION b/qemu/roms/SLOF/VERSION index 20bdb2eb7..aded5537b 100644 --- a/qemu/roms/SLOF/VERSION +++ b/qemu/roms/SLOF/VERSION @@ -1 +1 @@ -20150429 +20160223 diff --git a/qemu/roms/SLOF/board-js2x/llfw/stage2.lds b/qemu/roms/SLOF/board-js2x/llfw/stage2.lds index f91f0658a..e6315c3c8 100644 --- a/qemu/roms/SLOF/board-js2x/llfw/stage2.lds +++ b/qemu/roms/SLOF/board-js2x/llfw/stage2.lds @@ -45,7 +45,8 @@ SECTIONS { __bss_end = .; __bss_size = (__bss_end - __bss_start); - __toc_start = .; + . = ALIGN(256); + __toc_start = DEFINED (.TOC.) ? .TOC. : ADDR (.got) + 0x8000; .got : { *(.toc .got) diff --git a/qemu/roms/SLOF/board-js2x/llfw/stage2_head.S b/qemu/roms/SLOF/board-js2x/llfw/stage2_head.S index 5460bfebb..f3f5e0c8c 100644 --- a/qemu/roms/SLOF/board-js2x/llfw/stage2_head.S +++ b/qemu/roms/SLOF/board-js2x/llfw/stage2_head.S @@ -79,8 +79,6 @@ bsscdone: /* ------------------------------------ */ ASM_ENTRY(toc_init) LOAD64(r2, __toc_start) - addi r2,r2,0x4000 - addi r2,r2,0x4000 blr /* ------------------------------------ */ diff --git a/qemu/roms/SLOF/board-js2x/slof/Makefile b/qemu/roms/SLOF/board-js2x/slof/Makefile index ab3e683a4..4cdd5fa36 100644 --- a/qemu/roms/SLOF/board-js2x/slof/Makefile +++ b/qemu/roms/SLOF/board-js2x/slof/Makefile @@ -57,6 +57,7 @@ OF_FFS_FILES = \ $(SLOFBRDDIR)/attu.fs \ $(SLOFBRDDIR)/cpu.fs \ $(SLOFBRDDIR)/ioapic.fs \ + $(SLOFBRDDIR)/dma-function.fs \ $(SLOFBRDDIR)/pci-bridge_1022_7460.fs \ $(SLOFBRDDIR)/pci-device_1014_028c.fs \ $(SLOFBRDDIR)/pci-device_1014_02bd.fs \ diff --git a/qemu/roms/SLOF/board-js2x/slof/dma-function.fs b/qemu/roms/SLOF/board-js2x/slof/dma-function.fs new file mode 100644 index 000000000..2e314cdaa --- /dev/null +++ b/qemu/roms/SLOF/board-js2x/slof/dma-function.fs @@ -0,0 +1,31 @@ +\ ***************************************************************************** +\ * Copyright (c) 2004, 2011 IBM Corporation +\ * All rights reserved. +\ * This program and the accompanying materials +\ * are made available under the terms of the BSD License +\ * which accompanies this distribution, and is available at +\ * http://www.opensource.org/licenses/bsd-license.php +\ * +\ * Contributors: +\ * IBM Corporation - initial implementation +\ ****************************************************************************/ + +: dma-alloc ( ... size -- virt ) + \ ." dma-alloc called: " .s cr + alloc-mem +; + +: dma-free ( virt size -- ) + \ ." dma-free called: " .s cr + free-mem +; + +: dma-map-in ( ... virt size cacheable? -- devaddr ) + \ ." dma-map-in called: " .s cr + 2drop +; + +: dma-map-out ( virt devaddr size -- ) + \ ." dma-map-out called: " .s cr + 2drop drop +; diff --git a/qemu/roms/SLOF/board-js2x/slof/helper.fs b/qemu/roms/SLOF/board-js2x/slof/helper.fs index 34d60da1f..1e2b03063 100644 --- a/qemu/roms/SLOF/board-js2x/slof/helper.fs +++ b/qemu/roms/SLOF/board-js2x/slof/helper.fs @@ -26,3 +26,14 @@ s" , " $cat bdate2human $cat encode-string THEN ; + +: invert-region ( addr len -- ) + 2dup or 7 and CASE + 0 OF 3 rshift 0 ?DO dup dup rx@ -1 xor swap rx! xa1+ LOOP ENDOF + 4 OF 2 rshift 0 ?DO dup dup rl@ -1 xor swap rl! la1+ LOOP ENDOF + 3 and + 2 OF 1 rshift 0 ?DO dup dup rw@ -1 xor swap rw! wa1+ LOOP ENDOF + dup OF 0 ?DO dup dup rb@ -1 xor swap rb! 1+ LOOP ENDOF + ENDCASE + drop +; diff --git a/qemu/roms/SLOF/board-qemu/llfw/stage2.lds b/qemu/roms/SLOF/board-qemu/llfw/stage2.lds index e060dd189..28c9dca93 100644 --- a/qemu/roms/SLOF/board-qemu/llfw/stage2.lds +++ b/qemu/roms/SLOF/board-qemu/llfw/stage2.lds @@ -49,7 +49,8 @@ SECTIONS { __bss_end = .; __bss_size = (__bss_end - __bss_start); - __toc_start = .; + . = ALIGN(256); + __toc_start = DEFINED (.TOC.) ? .TOC. : ADDR (.got) + 0x8000; .got : { *(.toc .got) diff --git a/qemu/roms/SLOF/board-qemu/llfw/stage2_head.S b/qemu/roms/SLOF/board-qemu/llfw/stage2_head.S index c56b117ce..adf75547b 100644 --- a/qemu/roms/SLOF/board-qemu/llfw/stage2_head.S +++ b/qemu/roms/SLOF/board-qemu/llfw/stage2_head.S @@ -77,8 +77,6 @@ bsscdone: /* ------------------------------------ */ ASM_ENTRY(toc_init) LOAD64(r2, __toc_start) - addi r2,r2,0x4000 - addi r2,r2,0x4000 blr /* ------------------------------------ */ diff --git a/qemu/roms/SLOF/board-qemu/slof/Makefile b/qemu/roms/SLOF/board-qemu/slof/Makefile index 283f77d32..a000a2519 100644 --- a/qemu/roms/SLOF/board-qemu/slof/Makefile +++ b/qemu/roms/SLOF/board-qemu/slof/Makefile @@ -66,9 +66,13 @@ USB_FFS_FILES = \ VIO_FFS_FILES = \ $(SLOFBRDDIR)/pci-device_1af4_1000.fs \ + $(SLOFBRDDIR)/pci-device_1af4_1041.fs \ $(SLOFBRDDIR)/pci-device_1af4_1001.fs \ + $(SLOFBRDDIR)/pci-device_1af4_1042.fs \ $(SLOFBRDDIR)/pci-device_1af4_1004.fs \ + $(SLOFBRDDIR)/pci-device_1af4_1048.fs \ $(SLOFBRDDIR)/pci-device_1af4_1009.fs \ + $(SLOFBRDDIR)/pci-device_1af4_1050.fs \ $(SLOFBRDDIR)/vio-hvterm.fs \ $(SLOFBRDDIR)/vio-vscsi.fs \ $(SLOFBRDDIR)/vio-veth.fs \ @@ -103,6 +107,7 @@ OF_FFS_FILES = \ $(SLOFBRDDIR)/pci-device_1013_00b8.fs \ $(SLOFBRDDIR)/pci-device_8086_100e.fs \ $(SLOFBRDDIR)/e1k.fs \ + $(SLOFBRDDIR)/qemu-vga.fs \ $(FCODE_FFS_FILES) # Uncomment the following line to enable the USB code: diff --git a/qemu/roms/SLOF/board-qemu/slof/OF.fs b/qemu/roms/SLOF/board-qemu/slof/OF.fs index 561d89225..69ee5c122 100644 --- a/qemu/roms/SLOF/board-qemu/slof/OF.fs +++ b/qemu/roms/SLOF/board-qemu/slof/OF.fs @@ -134,10 +134,6 @@ check-boot-menu \ Grab rtas from qemu #include "rtas.fs" -390 cp - -#include "virtio.fs" - 3f0 cp #include "tree.fs" diff --git a/qemu/roms/SLOF/board-qemu/slof/archsupport.fs b/qemu/roms/SLOF/board-qemu/slof/archsupport.fs new file mode 100644 index 000000000..a8ace3cc5 --- /dev/null +++ b/qemu/roms/SLOF/board-qemu/slof/archsupport.fs @@ -0,0 +1,39 @@ +\ ***************************************************************************** +\ * Copyright (c) 2011 IBM Corporation +\ * All rights reserved. +\ * This program and the accompanying materials +\ * are made available under the terms of the BSD License +\ * which accompanies this distribution, and is available at +\ * http://www.opensource.org/licenses/bsd-license.php +\ * +\ * Contributors: +\ * IBM Corporation - initial implementation +\ ****************************************************************************/ + +\ 2 MiB FDT buffer size is enough to accommodate 255 CPU cores +\ and 16 TiB of maxmem specification. +200000 CONSTANT cas-buffer-size +: ibm,client-architecture-support ( vec -- err? ) + \ Store require parameters in nvram + \ to come back to right boot device + \ Allocate memory for H_CALL + cas-buffer-size alloc-mem ( vec memaddr ) + dup 0= IF ." out of memory during ibm,client-architecture-support" cr THEN + swap over cas-buffer-size ( memaddr vec memaddr size ) + \ make h_call to hypervisor + hv-cas 0= IF ( memaddr ) + dup l@ 1 >= IF \ Version number >= 1 + \ Make required changes + " /" find-node set-node + dup 4 + fdt-init + fdt-check-header + fdt-struct fdt-fix-cas-node + fdt-fix-cas-success NOT + ELSE + FALSE + THEN + ELSE + TRUE + THEN + >r cas-buffer-size free-mem r> +; diff --git a/qemu/roms/SLOF/board-qemu/slof/helper.fs b/qemu/roms/SLOF/board-qemu/slof/helper.fs index 96da49894..40d4abc3a 100644 --- a/qemu/roms/SLOF/board-qemu/slof/helper.fs +++ b/qemu/roms/SLOF/board-qemu/slof/helper.fs @@ -33,3 +33,16 @@ swap - ; +: invert-region-cs ( addr len cellsize -- ) + >r over swap r@ rshift r> swap 1 hv-logical-memop drop +; + +: invert-region ( addr len -- ) + 2dup or 7 and CASE + 0 OF 3 invert-region-cs ENDOF + 4 OF 2 invert-region-cs ENDOF + 3 and + 2 OF 1 invert-region-cs ENDOF + dup OF 0 invert-region-cs ENDOF + ENDCASE +; diff --git a/qemu/roms/SLOF/board-qemu/slof/pci-device_1234_1111.fs b/qemu/roms/SLOF/board-qemu/slof/pci-device_1234_1111.fs index a5c3584f9..22ea45d5c 100644 --- a/qemu/roms/SLOF/board-qemu/slof/pci-device_1234_1111.fs +++ b/qemu/roms/SLOF/board-qemu/slof/pci-device_1234_1111.fs @@ -10,233 +10,6 @@ \ * IBM Corporation - initial implementation \ ****************************************************************************/ -my-space pci-device-generic-setup - -\ Defaults, overriden from qemu -d# 800 VALUE disp-width -d# 600 VALUE disp-height -d# 8 VALUE disp-depth - -\ Determine base address -10 config-l@ translate-my-address f not AND VALUE fb-base - -\ Fixed up later --1 VALUE io-base - -\ We support only one instance -false VALUE is-installed? - -: vga-io-xlate ( port -- addr ) - io-base -1 = IF - dup translate-my-address fff not and to io-base - THEN - io-base + -; - -: vga-w! ( value port -- ) - vga-io-xlate rw!-le -; - -: vga-w@ ( port -- value ) - vga-io-xlate rw@-le -; - -: vga-b! ( value port -- ) - vga-io-xlate rb! -; - -: vga-b@ ( port -- value ) - vga-io-xlate rb@ -; - -: vbe! ( value index -- ) - 1ce vga-w! 1d0 vga-w! -; - -: vbe@ ( index -- value ) - 1ce vga-w! 1d0 vga-w@ -; - -: color! ( r g b number -- ) - 3c8 vga-b! - rot 3c9 vga-b! - swap 3c9 vga-b! - 3c9 vga-b! -; - -: color@ ( number -- r g b ) - 3c8 vga-b! - 3c9 vga-b@ - 3c9 vga-b@ - 3c9 vga-b@ -; - -: set-colors ( adr number #numbers -- ) - over 3c8 vga-b! - swap DO - rb@ 3c9 vga-b! - rb@ 3c9 vga-b! - rb@ 3c9 vga-b! - LOOP - 3drop -; - -: get-colors ( adr number #numbers -- ) - 3drop -; - -include graphics.fs - -\ qemu fake VBE IO registers -0 CONSTANT VBE_DISPI_INDEX_ID -1 CONSTANT VBE_DISPI_INDEX_XRES -2 CONSTANT VBE_DISPI_INDEX_YRES -3 CONSTANT VBE_DISPI_INDEX_BPP -4 CONSTANT VBE_DISPI_INDEX_ENABLE -5 CONSTANT VBE_DISPI_INDEX_BANK -6 CONSTANT VBE_DISPI_INDEX_VIRT_WIDTH -7 CONSTANT VBE_DISPI_INDEX_VIRT_HEIGHT -8 CONSTANT VBE_DISPI_INDEX_X_OFFSET -9 CONSTANT VBE_DISPI_INDEX_Y_OFFSET -a CONSTANT VBE_DISPI_INDEX_NB - -\ ENABLE register -00 CONSTANT VBE_DISPI_DISABLED -01 CONSTANT VBE_DISPI_ENABLED -02 CONSTANT VBE_DISPI_GETCAPS -20 CONSTANT VBE_DISPI_8BIT_DAC -40 CONSTANT VBE_DISPI_LFB_ENABLED -80 CONSTANT VBE_DISPI_NOCLEARMEM - -: init-mode - 0 3c0 vga-b! - VBE_DISPI_DISABLED VBE_DISPI_INDEX_ENABLE vbe! - 0 VBE_DISPI_INDEX_X_OFFSET vbe! - 0 VBE_DISPI_INDEX_Y_OFFSET vbe! - disp-width VBE_DISPI_INDEX_XRES vbe! - disp-height VBE_DISPI_INDEX_YRES vbe! - disp-depth VBE_DISPI_INDEX_BPP vbe! - VBE_DISPI_ENABLED VBE_DISPI_8BIT_DAC or VBE_DISPI_INDEX_ENABLE vbe! - 0 3c0 vga-b! - 20 3c0 vga-b! -; - -: clear-screen - fb-base disp-width disp-height disp-depth 7 + 8 / * * 0 rfill -; - -: read-settings - s" qemu,graphic-width" get-chosen IF - decode-int to disp-width 2drop - THEN - s" qemu,graphic-height" get-chosen IF - decode-int to disp-height 2drop - THEN - s" qemu,graphic-depth" get-chosen IF - decode-int nip nip - dup 8 = - over f = or - over 10 = or - over 20 = or IF - to disp-depth - ELSE - ." Unsupported bit depth, using 8bpp " drop cr - THEN - THEN -; - -: add-legacy-reg - \ add legacy I/O Ports / Memory regions to assigned-addresses - \ see PCI Bus Binding Revision 2.1 Section 7. - s" reg" get-node get-property IF - \ "reg" does not exist, create new - encode-start - ELSE - \ "reg" does exist, copy it - encode-bytes - THEN - \ I/O Range 0x1ce-0x1d2 - my-space a1000000 or encode-int+ \ non-relocatable, aliased I/O space - 1ce encode-64+ 4 encode-64+ \ addr size - \ I/O Range 0x3B0-0x3BB - my-space a1000000 or encode-int+ \ non-relocatable, aliased I/O space - 3b0 encode-64+ c encode-64+ \ addr size - \ I/O Range 0x3C0-0x3DF - my-space a1000000 or encode-int+ \ non-relocatable, aliased I/O space - 3c0 encode-64+ 20 encode-64+ \ addr size - \ Memory Range 0xA0000-0xBFFFF - my-space a2000000 or encode-int+ \ non-relocatable, <1MB Memory space - a0000 encode-64+ 20000 encode-64+ \ addr size - s" reg" property \ store "reg" property -; - -: setup-properties - \ Shouldn't this be done from open ? - disp-width encode-int s" width" property - disp-height encode-int s" height" property - disp-width disp-depth 7 + 8 / * encode-int s" linebytes" property - disp-depth encode-int s" depth" property - s" ISO8859-1" encode-string s" character-set" property \ i hope this is ok... - \ add "device_type" property - s" display" device-type - s" qemu,std-vga" encode-string s" compatible" property - \ XXX We don't create an "address" property because Linux doesn't know what - \ to do with it for >32-bit -; - -\ words for installation/removal, needed by is-install/is-remove, see display.fs -: display-remove ( -- ) -; - -: hcall-invert-screen ( -- ) - frame-buffer-adr frame-buffer-adr 3 - screen-height screen-width * screen-depth * /x / - 1 hv-logical-memop - drop -; - -: hcall-blink-screen ( -- ) - \ 32 msec delay for visually noticing the blink - hcall-invert-screen 20 ms hcall-invert-screen -; - -: display-install ( -- ) - is-installed? NOT IF - ." Installing QEMU fb" cr - fb-base to frame-buffer-adr - clear-screen - default-font - set-font - disp-width disp-height - disp-width char-width / disp-height char-height / - disp-depth 7 + 8 / ( width height #lines #cols depth ) - fb-install - ['] hcall-invert-screen to invert-screen - ['] hcall-blink-screen to blink-screen - true to is-installed? - THEN -; - -: set-alias - s" screen" find-alias 0= IF - \ no previous screen alias defined, define it... - s" screen" get-node node>path set-alias - ELSE - drop - THEN -; - - ." qemu vga" cr -pci-master-enable -pci-mem-enable -pci-io-enable -add-legacy-reg -read-settings -init-mode -init-default-palette -setup-properties -' display-install is-install -' display-remove is-remove -set-alias +s" qemu-vga.fs" included diff --git a/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1001.fs b/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1001.fs index fb2463467..db0bb3fb8 100644 --- a/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1001.fs +++ b/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1001.fs @@ -22,13 +22,4 @@ pci-io-enable s" virtio-block.fs" included -\ Allocate memory for virtio queue: -virtiodev 0 virtio-get-qsize virtio-vring-size -1000 CLAIM VALUE queue-addr - -\ Write queue address into device: -queue-addr c rshift -virtiodev vd>base @ 8 + -rl!-le - pci-device-disable diff --git a/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1009.fs b/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1009.fs index 03964a6db..9c8be249d 100644 --- a/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1009.fs +++ b/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1009.fs @@ -22,13 +22,4 @@ pci-io-enable s" virtio-fs.fs" included -\ Allocate memory for virtio queue: -virtiodev 0 virtio-get-qsize virtio-vring-size -1000 CLAIM VALUE queue-addr - -\ Write queue address into device: -queue-addr c rshift -virtiodev vd>base @ 8 + -rl!-le - pci-device-disable diff --git a/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1041.fs b/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1041.fs new file mode 100644 index 000000000..552b0ef8a --- /dev/null +++ b/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1041.fs @@ -0,0 +1,15 @@ +\ ***************************************************************************** +\ * Copyright (c) 2016 IBM Corporation +\ * All rights reserved. +\ * This program and the accompanying materials +\ * are made available under the terms of the BSD License +\ * which accompanies this distribution, and is available at +\ * http://www.opensource.org/licenses/bsd-license.php +\ * +\ * Contributors: +\ * IBM Corporation - initial implementation +\ ****************************************************************************/ + +\ Device ID 1041 is for virtio-net non-transitional device. +\ Include the driver for virtio-net +s" pci-device_1af4_1000.fs" included diff --git a/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1042.fs b/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1042.fs new file mode 100644 index 000000000..2b0a848d4 --- /dev/null +++ b/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1042.fs @@ -0,0 +1,15 @@ +\ ***************************************************************************** +\ * Copyright (c) 2016 IBM Corporation +\ * All rights reserved. +\ * This program and the accompanying materials +\ * are made available under the terms of the BSD License +\ * which accompanies this distribution, and is available at +\ * http://www.opensource.org/licenses/bsd-license.php +\ * +\ * Contributors: +\ * IBM Corporation - initial implementation +\ ****************************************************************************/ + +\ Device ID 1042 is for virtio-blk non-transitional device. +\ Include the driver for virtio-blk +s" pci-device_1af4_1001.fs" included diff --git a/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1048.fs b/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1048.fs new file mode 100644 index 000000000..055ad8960 --- /dev/null +++ b/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1048.fs @@ -0,0 +1,15 @@ +\ ***************************************************************************** +\ * Copyright (c) 2016 IBM Corporation +\ * All rights reserved. +\ * This program and the accompanying materials +\ * are made available under the terms of the BSD License +\ * which accompanies this distribution, and is available at +\ * http://www.opensource.org/licenses/bsd-license.php +\ * +\ * Contributors: +\ * IBM Corporation - initial implementation +\ ****************************************************************************/ + +\ Device ID 1048 is for virtio-scsi non-transitional device. +\ Include the driver for virtio-scsi +s" pci-device_1af4_1004.fs" included diff --git a/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1050.fs b/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1050.fs new file mode 100644 index 000000000..516056aad --- /dev/null +++ b/qemu/roms/SLOF/board-qemu/slof/pci-device_1af4_1050.fs @@ -0,0 +1,15 @@ +\ ***************************************************************************** +\ * Copyright (c) 2015 IBM Corporation +\ * All rights reserved. +\ * This program and the accompanying materials +\ * are made available under the terms of the BSD License +\ * which accompanies this distribution, and is available at +\ * http://www.opensource.org/licenses/bsd-license.php +\ * +\ * Contributors: +\ * IBM Corporation - initial implementation +\ ****************************************************************************/ + +s" virtio [ vga ]" type cr + +s" qemu-vga.fs" included diff --git a/qemu/roms/SLOF/board-qemu/slof/qemu-vga.fs b/qemu/roms/SLOF/board-qemu/slof/qemu-vga.fs new file mode 100644 index 000000000..3f4c237fc --- /dev/null +++ b/qemu/roms/SLOF/board-qemu/slof/qemu-vga.fs @@ -0,0 +1,198 @@ +\ ***************************************************************************** +\ * Copyright (c) 2015 IBM Corporation +\ * All rights reserved. +\ * This program and the accompanying materials +\ * are made available under the terms of the BSD License +\ * which accompanies this distribution, and is available at +\ * http://www.opensource.org/licenses/bsd-license.php +\ * +\ * Contributors: +\ * IBM Corporation - initial implementation +\ ****************************************************************************/ + +my-space pci-device-generic-setup + +\ Defaults, overriden from qemu +d# 800 VALUE disp-width +d# 600 VALUE disp-height +d# 8 VALUE disp-depth + +: map-in " map-in" my-phandle parent $call-static ; +: map-out " map-out" my-phandle parent $call-static ; + +\ Determine base address +0 0 my-space h# 02000010 + 1 map-in VALUE fb-base +0 0 my-space h# 02000018 + 1 map-in VALUE reg-base + +\ We support only one instance +false VALUE is-installed? + +: vga-w! ( value port -- ) + 3c0 - reg-base 400 + + rw!-le +; + +: vga-w@ ( port -- value ) + 3c0 - reg-base 400 + + rw@-le +; + +: vga-b! ( value port -- ) + 3c0 - reg-base 400 + + rb! +; + +: vga-b@ ( port -- value ) + 3c0 - reg-base 400 + + rb@ +; + +: vbe! ( value index -- ) + 1 << reg-base 500 + + rw!-le +; + +: vbe@ ( index -- value ) + 1 << reg-base 500 + + rw@-le +; + +: color! ( r g b number -- ) + 3c8 vga-b! + rot 3c9 vga-b! + swap 3c9 vga-b! + 3c9 vga-b! +; + +: color@ ( number -- r g b ) + 3c8 vga-b! + 3c9 vga-b@ + 3c9 vga-b@ + 3c9 vga-b@ +; + +: set-colors ( adr number #numbers -- ) + over 3c8 vga-b! + swap DO + rb@ 3c9 vga-b! + rb@ 3c9 vga-b! + rb@ 3c9 vga-b! + LOOP + 3drop +; + +: get-colors ( adr number #numbers -- ) + 3drop +; + +include graphics.fs + +\ qemu fake VBE IO registers +0 CONSTANT VBE_DISPI_INDEX_ID +1 CONSTANT VBE_DISPI_INDEX_XRES +2 CONSTANT VBE_DISPI_INDEX_YRES +3 CONSTANT VBE_DISPI_INDEX_BPP +4 CONSTANT VBE_DISPI_INDEX_ENABLE +5 CONSTANT VBE_DISPI_INDEX_BANK +6 CONSTANT VBE_DISPI_INDEX_VIRT_WIDTH +7 CONSTANT VBE_DISPI_INDEX_VIRT_HEIGHT +8 CONSTANT VBE_DISPI_INDEX_X_OFFSET +9 CONSTANT VBE_DISPI_INDEX_Y_OFFSET +a CONSTANT VBE_DISPI_INDEX_NB + +\ ENABLE register +00 CONSTANT VBE_DISPI_DISABLED +01 CONSTANT VBE_DISPI_ENABLED +02 CONSTANT VBE_DISPI_GETCAPS +20 CONSTANT VBE_DISPI_8BIT_DAC +40 CONSTANT VBE_DISPI_LFB_ENABLED +80 CONSTANT VBE_DISPI_NOCLEARMEM + +: init-mode + 0 3c0 vga-b! + VBE_DISPI_DISABLED VBE_DISPI_INDEX_ENABLE vbe! + 0 VBE_DISPI_INDEX_X_OFFSET vbe! + 0 VBE_DISPI_INDEX_Y_OFFSET vbe! + disp-width VBE_DISPI_INDEX_XRES vbe! + disp-height VBE_DISPI_INDEX_YRES vbe! + disp-depth VBE_DISPI_INDEX_BPP vbe! + VBE_DISPI_ENABLED VBE_DISPI_8BIT_DAC or VBE_DISPI_INDEX_ENABLE vbe! + 0 3c0 vga-b! + 20 3c0 vga-b! +; + +: clear-screen + fb-base disp-width disp-height disp-depth 7 + 8 / * * 0 rfill +; + +: read-settings + s" qemu,graphic-width" get-chosen IF + decode-int to disp-width 2drop + THEN + s" qemu,graphic-height" get-chosen IF + decode-int to disp-height 2drop + THEN + s" qemu,graphic-depth" get-chosen IF + decode-int nip nip + dup 8 = + over f = or + over 10 = or + over 20 = or IF + to disp-depth + ELSE + ." Unsupported bit depth, using 8bpp " drop cr + THEN + THEN +; + +: setup-properties + \ Shouldn't this be done from open ? + disp-width encode-int s" width" property + disp-height encode-int s" height" property + disp-width disp-depth 7 + 8 / * encode-int s" linebytes" property + disp-depth encode-int s" depth" property + s" ISO8859-1" encode-string s" character-set" property \ i hope this is ok... + \ add "device_type" property + s" display" device-type + s" qemu,std-vga" encode-string s" compatible" property + \ XXX We don't create an "address" property because Linux doesn't know what + \ to do with it for >32-bit +; + +\ words for installation/removal, needed by is-install/is-remove, see display.fs +: display-remove ( -- ) +; + +: slow-blink-screen ( -- ) + \ 32 msec delay for visually noticing the blink + invert-screen 20 ms invert-screen +; + +: display-install ( -- ) + is-installed? NOT IF + ." Installing QEMU fb" cr + fb-base to frame-buffer-adr + clear-screen + default-font + set-font + disp-width disp-height + disp-width char-width / disp-height char-height / + disp-depth 7 + 8 / ( width height #lines #cols depth ) + fb-install + ['] slow-blink-screen to blink-screen + true to is-installed? + THEN +; + +: set-alias + s" screen" find-alias 0= IF + \ no previous screen alias defined, define it... + s" screen" get-node node>path set-alias + ELSE + drop + THEN +; + +pci-master-enable +pci-mem-enable +read-settings +init-mode +init-default-palette +setup-properties +' display-install is-install +' display-remove is-remove +set-alias diff --git a/qemu/roms/SLOF/board-qemu/slof/tree.fs b/qemu/roms/SLOF/board-qemu/slof/tree.fs index 4aba4c53f..78dafab71 100644 --- a/qemu/roms/SLOF/board-qemu/slof/tree.fs +++ b/qemu/roms/SLOF/board-qemu/slof/tree.fs @@ -26,6 +26,8 @@ \ 2 encode-int s" #size-cells" property \ s" chrp" device-type +#include "archsupport.fs" + 480 cp \ See 3.6.5, and the PowerPC OF binding document. diff --git a/qemu/roms/SLOF/board-qemu/slof/virtio-block.fs b/qemu/roms/SLOF/board-qemu/slof/virtio-block.fs index ea388fb00..bc9013eea 100644 --- a/qemu/roms/SLOF/board-qemu/slof/virtio-block.fs +++ b/qemu/roms/SLOF/board-qemu/slof/virtio-block.fs @@ -23,8 +23,7 @@ FALSE VALUE initialized? INSTANCE VARIABLE deblocker -/vd-len BUFFER: virtiodev -virtiodev virtio-setup-vd +virtio-setup-vd VALUE virtiodev \ Quiesce the virtqueue of this device so that no more background \ transactions can be pending. diff --git a/qemu/roms/SLOF/board-qemu/slof/virtio-fs.fs b/qemu/roms/SLOF/board-qemu/slof/virtio-fs.fs index 8632b465f..3898d0b7d 100644 --- a/qemu/roms/SLOF/board-qemu/slof/virtio-fs.fs +++ b/qemu/roms/SLOF/board-qemu/slof/virtio-fs.fs @@ -20,8 +20,7 @@ FALSE VALUE initialized? 2000 CONSTANT VIRTFS-BUF-SIZE \ 8k -/vd-len BUFFER: virtiodev -virtiodev virtio-setup-vd +virtio-setup-vd VALUE virtiodev \ \ Support methods. diff --git a/qemu/roms/SLOF/board-qemu/slof/virtio-net.fs b/qemu/roms/SLOF/board-qemu/slof/virtio-net.fs index 412b34fa6..b16fffe39 100644 --- a/qemu/roms/SLOF/board-qemu/slof/virtio-net.fs +++ b/qemu/roms/SLOF/board-qemu/slof/virtio-net.fs @@ -16,20 +16,28 @@ s" network" device-type INSTANCE VARIABLE obp-tftp-package -/vd-len BUFFER: virtiodev -virtiodev virtio-setup-vd +virtio-setup-vd VALUE virtiodev 0 VALUE virtio-net-priv 0 VALUE open-count +\ Set up MAC address from config virtqueue +6 BUFFER: local-mac +: setup-mac ( -- ) + s" local-mac-address" get-node get-property not IF 2drop EXIT THEN + 6 0 DO + virtiodev i 1 virtio-get-config + local-mac i + c! + LOOP + local-mac 6 encode-bytes s" local-mac-address" property +; + : open ( -- okay? ) open-count 0= IF open IF \ my-unit 1 rtas-set-tce-bypass - s" local-mac-address" get-node get-property not IF - virtiodev virtio-net-open dup not IF ." virtio-net-open failed" EXIT THEN - drop TO virtio-net-priv - THEN - true + virtiodev virtio-net-open not IF ." virtio-net-open failed" false EXIT THEN + TO virtio-net-priv + setup-mac true ELSE false THEN @@ -77,17 +85,6 @@ virtiodev virtio-setup-vd s" ping" obp-tftp-package @ $call-method ; -\ Set up MAC address from config virtqueue -6 BUFFER: local-mac -: setup-mac ( -- ) - 6 0 DO - virtiodev i 1 virtio-get-config - local-mac i + c! - LOOP - local-mac 6 encode-bytes s" local-mac-address" property -; -setup-mac - : setup-alias ( -- ) " net" get-next-alias ?dup IF get-node node>path set-alias diff --git a/qemu/roms/SLOF/board-qemu/slof/virtio-scsi.fs b/qemu/roms/SLOF/board-qemu/slof/virtio-scsi.fs index ca5fb13aa..4fedeeeb1 100644 --- a/qemu/roms/SLOF/board-qemu/slof/virtio-scsi.fs +++ b/qemu/roms/SLOF/board-qemu/slof/virtio-scsi.fs @@ -22,8 +22,7 @@ FALSE CONSTANT virtio-scsi-debug FALSE VALUE initialized? -/vd-len BUFFER: virtiodev -virtiodev virtio-setup-vd +virtio-setup-vd VALUE virtiodev STRUCT \ virtio-scsi-config /l FIELD vs-cfg>num-queues diff --git a/qemu/roms/SLOF/board-qemu/slof/virtio.fs b/qemu/roms/SLOF/board-qemu/slof/virtio.fs deleted file mode 100644 index 818c1320e..000000000 --- a/qemu/roms/SLOF/board-qemu/slof/virtio.fs +++ /dev/null @@ -1,35 +0,0 @@ -\ ***************************************************************************** -\ * Copyright (c) 2011 IBM Corporation -\ * All rights reserved. -\ * This program and the accompanying materials -\ * are made available under the terms of the BSD License -\ * which accompanies this distribution, and is available at -\ * http://www.opensource.org/licenses/bsd-license.php -\ * -\ * Contributors: -\ * IBM Corporation - initial implementation -\ ****************************************************************************/ - -\ This struct must match "struct virtio_device" in virtio.h! -STRUCT - /n FIELD vd>base - /l FIELD vd>type -CONSTANT /vd-len - - -\ Initialize virtiodev structure for the current node -: virtio-setup-vd ( vdstruct -- ) - >r - \ Does it have a "class-code" property? If yes, assume we're a PCI device - s" class-code" get-node get-property 0= IF - \ Set up for PCI device interface - 2drop - s" 10 config-l@ translate-my-address 3 not AND" evaluate - ( io-base ) r@ vd>base ! - 0 r@ vd>type l! - ELSE - ." unsupported virtio interface!" cr - 1 r@ vd>type l! - THEN - r> drop -; diff --git a/qemu/roms/SLOF/clients/net-snk/app/netapps/netboot.c b/qemu/roms/SLOF/clients/net-snk/app/netapps/netboot.c index cf20b5915..bb1db03e3 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netapps/netboot.c +++ b/qemu/roms/SLOF/clients/net-snk/app/netapps/netboot.c @@ -332,7 +332,13 @@ int dhcp(char *ret_buffer, filename_ip_t * fn_ip, unsigned int retries, int flag int i = (int) retries+1; int rc = -1; - printf(" "); + printf(" Requesting information via DHCP%s: ", + flags == F_IPV4 ? "v4" : flags == F_IPV6 ? "v6" : ""); + + if (flags != F_IPV6) + dhcpv4_generate_transaction_id(); + if (flags != F_IPV4) + dhcpv6_generate_transaction_id(); do { printf("\b\b\b%03d", i-1); @@ -353,7 +359,6 @@ int dhcp(char *ret_buffer, filename_ip_t * fn_ip, unsigned int retries, int flag set_ipv6_address(fn_ip->fd, 0); rc = dhcpv6(ret_buffer, fn_ip); if (rc == 0) { - printf("\n"); memcpy(&fn_ip->own_ip6, get_ipv6_address(), 16); break; } @@ -362,11 +367,23 @@ int dhcp(char *ret_buffer, filename_ip_t * fn_ip, unsigned int retries, int flag if (rc != -1) /* either success or non-dhcp failure */ break; } while (1); - printf("\b\b\b\b"); + printf("\b\b\b\bdone\n"); return rc; } +/** + * Seed the random number generator with our mac and current timestamp + */ +static void seed_rng(uint8_t mac[]) +{ + unsigned int seed; + + asm volatile("mftbl %0" : "=r"(seed)); + seed ^= (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5]; + srand(seed); +} + int netboot(int argc, char *argv[]) { @@ -388,8 +405,7 @@ netboot(int argc, char *argv[]) int32_t block_size = strtol(argv[5], 0, 10); uint8_t own_mac[6]; - printf("\n"); - printf(" Bootloader 1.6 \n"); + puts("\n Initializing NIC"); memset(&fn_ip, 0, sizeof(filename_ip_t)); /*********************************************************** @@ -438,6 +454,8 @@ netboot(int argc, char *argv[]) // init ethernet layer set_mac_address(own_mac); + seed_rng(own_mac); + if (argc > 6) { parse_args(argv[6], &obp_tftp_args); if(obp_tftp_args.bootp_retries - rc < DEFAULT_BOOT_RETRIES) @@ -468,10 +486,8 @@ netboot(int argc, char *argv[]) } } else if (ip_version == 6) { - if (memcmp(&obp_tftp_args.ci6addr, null_ip6, 16) != 0 - && memcmp(&obp_tftp_args.si6addr, null_ip6, 16) != 0 + if (memcmp(&obp_tftp_args.si6addr, null_ip6, 16) != 0 && obp_tftp_args.filename[0] != 0) { - memcpy(&fn_ip.server_ip6.addr[0], &obp_tftp_args.si6addr.addr, 16); obp_tftp_args.ip_init = IP_INIT_IPV6_MANUAL; @@ -484,7 +500,6 @@ netboot(int argc, char *argv[]) // construction of fn_ip from parameter switch(obp_tftp_args.ip_init) { case IP_INIT_BOOTP: - printf(" Requesting IP address via BOOTP: "); // if giaddr in not specified, then we have to identify // the BOOTP server via broadcasts if(memcmp(obp_tftp_args.giaddr, null_ip, 4) == 0) { @@ -499,19 +514,25 @@ netboot(int argc, char *argv[]) rc = bootp(ret_buffer, &fn_ip, obp_tftp_args.bootp_retries); break; case IP_INIT_DHCP: - printf(" Requesting IP address via DHCPv4: "); rc = dhcp(ret_buffer, &fn_ip, obp_tftp_args.bootp_retries, F_IPV4); break; case IP_INIT_DHCPV6_STATELESS: - printf(" Requesting information via DHCPv6: "); rc = dhcp(ret_buffer, &fn_ip, obp_tftp_args.bootp_retries, F_IPV6); break; case IP_INIT_IPV6_MANUAL: - set_ipv6_address(fn_ip.fd, &obp_tftp_args.ci6addr); + if (memcmp(&obp_tftp_args.ci6addr, null_ip6, 16)) { + set_ipv6_address(fn_ip.fd, &obp_tftp_args.ci6addr); + } else { + /* + * If no client address has been specified, then + * use a link-local or stateless autoconfig address + */ + set_ipv6_address(fn_ip.fd, NULL); + memcpy(&fn_ip.own_ip6, get_ipv6_address(), 16); + } break; case IP_INIT_DEFAULT: - printf(" Requesting IP address via DHCP: "); rc = dhcp(ret_buffer, &fn_ip, obp_tftp_args.bootp_retries, 0); break; case IP_INIT_NONE: @@ -548,10 +569,15 @@ netboot(int argc, char *argv[]) return -101; } - if(ip_version == 4) - printf("%d.%d.%d.%d\n", + if (ip_version == 4) { + printf(" Using IPv4 address: %d.%d.%d.%d\n", ((fn_ip.own_ip >> 24) & 0xFF), ((fn_ip.own_ip >> 16) & 0xFF), ((fn_ip.own_ip >> 8) & 0xFF), ( fn_ip.own_ip & 0xFF)); + } else if (ip_version == 6) { + char ip6_str[40]; + ipv6_to_str(fn_ip.own_ip6.addr, ip6_str); + printf(" Using IPv6 address: %s\n", ip6_str); + } if (rc == -2) { sprintf(buf, @@ -818,7 +844,7 @@ int parse_tftp_args(char buffer[], char *server_ip, char filename[], int fd, tmp = raw + 7; tmp[j] = '\0'; strcpy(domainname, tmp); - if (dns_get_ip(fd, (int8_t *)domainname, server_ip6, 6) == 0) { + if (dns_get_ip(fd, domainname, server_ip6, 6) == 0) { printf("\n DNS failed for IPV6\n"); return -1; } diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/bootp.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/bootp.c index 1bc6efe5b..6d58cef7d 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netlib/bootp.c +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/bootp.c @@ -232,7 +232,7 @@ bootp(char *ret_buffer, filename_ip_t * fn_ip, unsigned int retries) int i = (int) retries+1; fn_ip->own_ip = 0; - printf(" "); + printf(" Requesting IP address via BOOTP: "); response_buffer = ret_buffer; @@ -249,6 +249,7 @@ bootp(char *ret_buffer, filename_ip_t * fn_ip, unsigned int retries) * in case the previous one was lost. And because we don't * trust the network cable we keep on doing this 30 times */ } while (receive_bootp(fn_ip) != 0); - printf("\b\b\b"); + + printf("\b\b\bdone\n"); return 0; } diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcp.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcp.c index 5f26f3afb..7e2e88ccf 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcp.c +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcp.c @@ -11,7 +11,7 @@ *****************************************************************************/ -/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ALGORITHMS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ +/******************************* ALGORITHMS ******************************/ /** \file dhcp.c

        * **************** State-transition diagram for DHCP client  *************
      @@ -41,13 +41,14 @@
        * 
      */ -/*>>>>>>>>>>>>>>>>>>>>> DEFINITIONS & DECLARATIONS <<<<<<<<<<<<<<<<<<<<<<*/ +/********************** DEFINITIONS & DECLARATIONS ***********************/ #include #include #include #include #include +#include #include #include @@ -110,11 +111,11 @@ static uint8_t dhcp_magic[] = {0x63, 0x82, 0x53, 0x63}; * If flag[i] == TRUE then field for i-th option retains valid value and * information from this field may retrived (in case of receiving) or will * be transmitted (in case of transmitting). - * + * */ typedef struct { uint8_t flag[256]; /**< Show if corresponding opt. is valid */ - uint8_t request_list[256]; /**< o.55 If i-th member is TRUE, then i-th + uint8_t request_list[256]; /**< o.55 If i-th member is TRUE, then i-th option will be requested from server */ uint32_t server_ID; /**< o.54 Identifies DHCP-server */ uint32_t requested_IP; /**< o.50 Must be filled in DHCP-Request */ @@ -132,65 +133,57 @@ typedef struct { static uint8_t dhcp_state; -/*>>>>>>>>>>>>>>>>>>>>>>>>>>>> PROTOTYPES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - -static int32_t -dhcp_attempt(int fd); +/***************************** PROTOTYPES ********************************/ -static int32_t -dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_struct); +static int32_t dhcp_attempt(int fd); -static int32_t -dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len, - dhcp_options_t * opt_struct); +static int32_t dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_struct); -static int8_t -dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len, - uint8_t src_options[], uint32_t src_len); +static int32_t dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len, + dhcp_options_t * opt_struct); -static int8_t -dhcp_find_option(uint8_t options[], uint32_t len, - uint8_t op_code, uint32_t * op_offset); - -static void -dhcp_append_option(uint8_t dst_options[], uint32_t * dst_len, - uint8_t * new_option); +static int8_t dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len, + uint8_t src_options[], uint32_t src_len); -static void -dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len, - uint32_t dst_offset, uint8_t * new_option); +static int8_t dhcp_find_option(uint8_t options[], uint32_t len, + uint8_t op_code, uint32_t * op_offset); -static void -dhcp_send_discover(int fd); +static void dhcp_append_option(uint8_t dst_options[], uint32_t * dst_len, + uint8_t * new_option); -static void -dhcp_send_request(int fd); +static void dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len, + uint32_t dst_offset, uint8_t * new_option); -static uint8_t -strtoip(int8_t * str, uint32_t * ip); +static void dhcp_send_discover(int fd); +static void dhcp_send_request(int fd); -/*>>>>>>>>>>>>>>>>>>>>>>>>>>>> LOCAL VARIABLES <<<<<<<<<<<<<<<<<<<<<<<<<<*/ +/***************************** LOCAL VARIABLES ***************************/ static uint8_t ether_packet[ETH_MTU_SIZE]; static uint32_t dhcp_own_ip = 0; static uint32_t dhcp_server_ip = 0; static uint32_t dhcp_siaddr_ip = 0; -static int8_t dhcp_filename[256]; -static int8_t dhcp_tftp_name[256]; +static char dhcp_filename[256]; +static char dhcp_tftp_name[256]; +static uint32_t dhcp_xid; static char * response_buffer; -/*>>>>>>>>>>>>>>>>>>>>>>>>>>>> IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<*/ +/***************************** IMPLEMENTATION ****************************/ -int32_t -dhcpv4(char *ret_buffer, filename_ip_t * fn_ip) { +void dhcpv4_generate_transaction_id(void) +{ + dhcp_xid = (rand() << 16) ^ rand(); +} +int32_t dhcpv4(char *ret_buffer, filename_ip_t *fn_ip) +{ uint32_t dhcp_tftp_ip = 0; int fd = fn_ip->fd; - strcpy((char *) dhcp_filename, ""); - strcpy((char *) dhcp_tftp_name, ""); + strcpy(dhcp_filename, ""); + strcpy(dhcp_tftp_name, ""); response_buffer = ret_buffer; @@ -204,11 +197,11 @@ dhcpv4(char *ret_buffer, filename_ip_t * fn_ip) { dhcp_siaddr_ip = fn_ip->server_ip; } if(fn_ip->filename[0] != 0) { - strcpy((char *) dhcp_filename, (char *) fn_ip->filename); + strcpy(dhcp_filename, (char *) fn_ip->filename); } // TFTP SERVER - if (!strlen((char *) dhcp_tftp_name)) { + if (!strlen(dhcp_tftp_name)) { if (!dhcp_siaddr_ip) { // ERROR: TFTP name is not presented return -3; @@ -219,9 +212,9 @@ dhcpv4(char *ret_buffer, filename_ip_t * fn_ip) { } else { // TFTP server defined by its name - if (!strtoip(dhcp_tftp_name, &(dhcp_tftp_ip))) { - if (!dns_get_ip(fd, dhcp_tftp_name, (uint8_t *)&(dhcp_tftp_ip), 4)) { - // DNS error - can't obtain TFTP-server name + if (!strtoip(dhcp_tftp_name, (char *)&dhcp_tftp_ip)) { + if (!dns_get_ip(fd, dhcp_tftp_name, (uint8_t *)&dhcp_tftp_ip, 4)) { + // DNS error - can't obtain TFTP-server name // Use TFTP-ip from siaddr field, if presented if (dhcp_siaddr_ip) { dhcp_tftp_ip = dhcp_siaddr_ip; @@ -237,7 +230,7 @@ dhcpv4(char *ret_buffer, filename_ip_t * fn_ip) { // Store configuration info into filename_ip strucutre fn_ip -> own_ip = dhcp_own_ip; fn_ip -> server_ip = dhcp_tftp_ip; - strcpy((char *) fn_ip -> filename, (char *) dhcp_filename); + strcpy((char *) fn_ip -> filename, dhcp_filename); return 0; } @@ -245,8 +238,8 @@ dhcpv4(char *ret_buffer, filename_ip_t * fn_ip) { /** * DHCP: Tries o obtain DHCP parameters, refer to state-transition diagram */ -static int32_t -dhcp_attempt(int fd) { +static int32_t dhcp_attempt(int fd) +{ int sec; // Send DISCOVER message and switch DHCP-client to SELECT state @@ -270,7 +263,7 @@ dhcp_attempt(int fd) { } while (get_timer() > 0); } - // timeout + // timeout return 0; } @@ -278,7 +271,7 @@ dhcp_attempt(int fd) { * DHCP: Supplements DHCP-message with options stored in structure. * For more information about option coding see dhcp_options_t. * - * @param opt_field Points to the "vend" field of DHCP-message + * @param opt_field Points to the "vend" field of DHCP-message * (destination) * @param opt_struct this structure stores info about the options which * will be added to DHCP-message (source) @@ -286,8 +279,8 @@ dhcp_attempt(int fd) { * FALSE - error condition occurs. * @see dhcp_options_t */ -static int32_t -dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_struct) { +static int32_t dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_struct) +{ uint8_t * options = opt_field; uint16_t i, sum; // used to define is any options set @@ -380,7 +373,7 @@ dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_struct) { * DHCP: Extracts encoded options from DHCP-message into the structure. * For more information about option coding see dhcp_options_t. * - * @param opt_field Points to the "options" field of DHCP-message + * @param opt_field Points to the "options" field of DHCP-message * (source). * @param opt_len Length of "options" field. * @param opt_struct this structure stores info about the options which @@ -389,10 +382,10 @@ dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_struct) { * FALSE - error condition occurs. * @see dhcp_options_t */ -static int32_t -dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len, - dhcp_options_t * opt_struct) { - int32_t offset = 0; +static int32_t dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len, + dhcp_options_t * opt_struct) +{ + uint32_t offset = 0; memset(opt_struct, 0, sizeof(dhcp_options_t)); @@ -407,30 +400,30 @@ dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len, switch(opt_field[offset]) { case DHCP_OVERLOAD : opt_struct -> overload = opt_field[offset + 2]; - offset += 2 + opt_field[offset + 1]; + offset += 2 + opt_field[offset + 1]; break; case DHCP_REQUESTED_IP : opt_struct -> requested_IP = htonl(* (uint32_t *) (opt_field + offset + 2)); - offset += 2 + opt_field[offset + 1]; + offset += 2 + opt_field[offset + 1]; break; case DHCP_MASK : opt_struct -> flag[DHCP_MASK] = 1; opt_struct -> subnet_mask = htonl(* (uint32_t *) (opt_field + offset + 2)); - offset += 2 + opt_field[offset + 1]; + offset += 2 + opt_field[offset + 1]; break; case DHCP_DNS : opt_struct -> flag[DHCP_DNS] = 1; opt_struct -> dns_IP = htonl(* (uint32_t *) (opt_field + offset + 2)); - offset += 2 + opt_field[offset + 1]; + offset += 2 + opt_field[offset + 1]; break; case DHCP_ROUTER : opt_struct -> flag[DHCP_ROUTER] = 1; opt_struct -> router_IP = htonl(* (uint32_t *) (opt_field + offset + 2)); - offset += 2 + opt_field[offset + 1]; + offset += 2 + opt_field[offset + 1]; break; case DHCP_MSG_TYPE : @@ -492,11 +485,12 @@ dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len, * FALSE - error condition occurs. */ static int8_t dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len, - uint8_t src_options[], uint32_t src_len) { - int32_t dst_offset, src_offset = 0; + uint8_t src_options[], uint32_t src_len) +{ + uint32_t dst_offset, src_offset = 0; // remove ENDOPT if presented - if (dhcp_find_option(dst_options, * dst_len, DHCP_ENDOPT, (uint32_t *) &dst_offset)) + if (dhcp_find_option(dst_options, * dst_len, DHCP_ENDOPT, &dst_offset)) * dst_len = dst_offset; while (src_offset < src_len) { @@ -509,7 +503,7 @@ static int8_t dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len, default: if (dhcp_find_option(dst_options, * dst_len, src_options[src_offset], - (uint32_t *) &dst_offset)) { + &dst_offset)) { dhcp_combine_option(dst_options, dst_len, dst_offset, (uint8_t *) src_options + @@ -522,7 +516,7 @@ static int8_t dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len, } } - if (src_offset == src_len) + if (src_offset == src_len) return 1; return 0; } @@ -540,7 +534,8 @@ static int8_t dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len, * FALSE - option wasn't find. */ static int8_t dhcp_find_option(uint8_t options[], uint32_t len, - uint8_t op_code, uint32_t * op_offset) { + uint8_t op_code, uint32_t * op_offset) +{ uint32_t srch_offset = 0; * op_offset = 0; @@ -568,9 +563,9 @@ static int8_t dhcp_find_option(uint8_t options[], uint32_t len, * @param dst_len length of the "options" field (modified) * @param new_option points to an option in another list (src) */ -static void -dhcp_append_option(uint8_t dst_options[], uint32_t * dst_len, - uint8_t * new_option) { +static void dhcp_append_option(uint8_t dst_options[], uint32_t * dst_len, + uint8_t * new_option) +{ memcpy(dst_options + ( * dst_len), new_option, 2 + (* (new_option + 1))); * dst_len += 2 + *(new_option + 1); } @@ -586,10 +581,9 @@ dhcp_append_option(uint8_t dst_options[], uint32_t * dst_len, * @param dst_offset offset of the option from beginning of the list * @param new_option points to an option in another list (src) */ -static void -dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len, - uint32_t dst_offset, uint8_t * new_option) { - +static void dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len, + uint32_t dst_offset, uint8_t * new_option) +{ uint8_t tmp_buffer[1024]; // use to provide safe memcpy uint32_t tail_len; @@ -612,8 +606,8 @@ dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len, /** * DHCP: Sends DHCP-Discover message. Looks for DHCP servers. */ -static void -dhcp_send_discover(int fd) { +static void dhcp_send_discover(int fd) +{ uint32_t packetsize = sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct btphdr); struct btphdr *btph; @@ -627,6 +621,7 @@ dhcp_send_discover(int fd) { btph -> op = 1; btph -> htype = 1; btph -> hlen = 6; + btph -> xid = dhcp_xid; memcpy(btph -> chaddr, get_mac_address(), 6); memset(&opt, 0, sizeof(dhcp_options_t)); @@ -655,8 +650,8 @@ dhcp_send_discover(int fd) { /** * DHCP: Sends DHCP-Request message. Asks for acknowledgment to occupy IP. */ -static void -dhcp_send_request(int fd) { +static void dhcp_send_request(int fd) +{ uint32_t packetsize = sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct btphdr); struct btphdr *btph; @@ -670,6 +665,7 @@ dhcp_send_request(int fd) { btph -> op = 1; btph -> htype = 1; btph -> hlen = 6; + btph -> xid = dhcp_xid; memcpy(btph -> chaddr, get_mac_address(), 6); memset(&opt, 0, sizeof(dhcp_options_t)); @@ -704,7 +700,8 @@ dhcp_send_request(int fd) { /** * DHCP: Sends DHCP-Release message. Releases occupied IP. */ -void dhcp_send_release(int fd) { +void dhcp_send_release(int fd) +{ uint32_t packetsize = sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct btphdr); struct btphdr *btph; @@ -718,6 +715,7 @@ void dhcp_send_release(int fd) { btph -> op = 1; btph -> htype = 1; btph -> hlen = 6; + btph -> xid = dhcp_xid; strcpy((char *) btph -> file, ""); memcpy(btph -> chaddr, get_mac_address(), 6); btph -> ciaddr = htonl(dhcp_own_ip); @@ -730,7 +728,7 @@ void dhcp_send_release(int fd) { dhcp_encode_options(btph -> vend, &opt); - fill_udphdr(ðer_packet[sizeof(struct iphdr)], + fill_udphdr(ðer_packet[sizeof(struct iphdr)], sizeof(struct btphdr) + sizeof(struct udphdr), UDPPORT_BOOTPC, UDPPORT_BOOTPS); fill_iphdr(ether_packet, sizeof(struct btphdr) + @@ -753,18 +751,21 @@ void dhcp_send_release(int fd) { * @see btphdr */ -int8_t -handle_dhcp(int fd, uint8_t * packet, int32_t packetsize) { +int8_t handle_dhcp(int fd, uint8_t * packet, int32_t packetsize) +{ struct btphdr * btph; struct iphdr * iph; dhcp_options_t opt; - memset(&opt, 0, sizeof(dhcp_options_t)); + memset(&opt, 0, sizeof(dhcp_options_t)); btph = (struct btphdr *) packet; iph = (struct iphdr *) packet - sizeof(struct udphdr) - sizeof(struct iphdr); - if (btph -> op != 2) - return -1; // it is not Boot Reply + + if (btph->op != 2) + return -1; /* It is not a Bootp/DHCP reply */ + if (btph->xid != dhcp_xid) + return -1; /* The transaction ID does not match */ if (memcmp(btph -> vend, dhcp_magic, 4)) { // It is BootP - RFC 951 @@ -788,7 +789,7 @@ handle_dhcp(int fd, uint8_t * packet, int32_t packetsize) { } - // decode options + // decode options if (!dhcp_decode_options(btph -> vend, packetsize - sizeof(struct btphdr) + sizeof(btph -> vend), &opt)) { @@ -902,7 +903,7 @@ handle_dhcp(int fd, uint8_t * packet, int32_t packetsize) { else { strcpy((char *) dhcp_filename, ""); if (opt.overload != DHCP_OVERLOAD_FILE && - opt.overload != DHCP_OVERLOAD_BOTH && + opt.overload != DHCP_OVERLOAD_BOTH && strlen((char *) btph -> file)) { strncpy((char *) dhcp_filename, (char *) btph->file, @@ -952,47 +953,3 @@ handle_dhcp(int fd, uint8_t * packet, int32_t packetsize) { return 0; } - -/** - * DHCP: Converts "255.255.255.255" -> 32-bit long IP - * - * @param str string to be converted - * @param ip in case of SUCCESS - 32-bit long IP - in case of FAULT - zero - * @return TRUE - IP converted successfully; - * FALSE - error condition occurs (e.g. bad format) - */ -static uint8_t -strtoip(int8_t * str, uint32_t * ip) { - int8_t ** ptr = &str; - int16_t i = 0, res, len; - char octet[256]; - - * ip = 0; - - while (**ptr != 0) { - if (i > 3 || !isdigit(**ptr)) - return 0; - if (strstr((char *) * ptr, ".") != NULL) { - len = (int16_t) ((int8_t *) strstr((char *) * ptr, ".") - - (int8_t *) (* ptr)); - strncpy(octet, (char *) * ptr, len); octet[len] = 0; - * ptr += len; - } - else { - strcpy(octet, (char *) * ptr); - * ptr += strlen(octet); - } - res = strtol(octet, NULL, 10); - if ((res > 255) || (res < 0)) - return 0; - * ip = ((* ip) << 8) + res; - i++; - if (** ptr == '.') - (*ptr)++; - } - - if (i != 4) - return 0; - return 1; -} diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcp.h b/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcp.h index 69dd49d4a..54fb1eed3 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcp.h +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcp.h @@ -43,6 +43,7 @@ struct btphdr { uint8_t vend[64]; /**< Optional parameters field (DHCP-options) */ }; +void dhcpv4_generate_transaction_id(void); int bootp(char *ret_buffer, filename_ip_t *, unsigned int); int dhcpv4(char *ret_buffer, filename_ip_t *); void dhcp_send_release(int fd); diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.c index 4deef30f2..d0a22d555 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.c +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.c @@ -27,13 +27,15 @@ static uint8_t tid[3]; static uint32_t dhcpv6_state = -1; static filename_ip_t *my_fn_ip; -static void -generate_transaction_id(void) +static struct ip6addr_list_entry all_dhcpv6_ll; /* All DHCPv6 servers address */ + +void +dhcpv6_generate_transaction_id(void) { - /* TODO: as per RFC 3315 transaction IDs should be generated randomly */ - tid[0] = 1; - tid[1] = 2; - tid[2] = 4; + /* As per RFC 3315 transaction IDs should be generated randomly */ + tid[0] = rand(); + tid[1] = rand(); + tid[2] = rand(); } static void @@ -45,8 +47,6 @@ send_info_request(int fd) memset(ether_packet, 0, ETH_MTU_SIZE); - generate_transaction_id(); - /* Get an IPv6 packet */ payload_length = sizeof(struct udphdr) + sizeof(struct dhcp_message_header); fill_ip6hdr (ether_packet + sizeof(struct ethhdr), @@ -72,16 +72,14 @@ send_info_request(int fd) dhcph->option.el_time.length = 2; dhcph->option.el_time.time = 0x190; /* 4000 ms */ dhcph->option.option_request_option.code = DHCPV6_OPTION_ORO; - dhcph->option.option_request_option.length= 6; + dhcph->option.option_request_option.length = DHCPV6_OPTREQUEST_NUMOPTS * 2; dhcph->option.option_request_option.option_code[0] = DHCPV6_OPTION_DNS_SERVERS; dhcph->option.option_request_option.option_code[1] = DHCPV6_OPTION_DOMAIN_LIST; dhcph->option.option_request_option.option_code[2] = DHCPV6_OPTION_BOOT_URL; - send_ipv6(fd, ether_packet + sizeof(struct ethhdr), - sizeof(struct ethhdr)+ sizeof(struct ip6hdr) - + sizeof(struct udphdr) - + sizeof( struct dhcp_message_header) ); + sizeof(struct ip6hdr) + sizeof(struct udphdr) + + sizeof(struct dhcp_message_header)); } static int32_t @@ -119,6 +117,9 @@ dhcpv6 ( char *ret_buffer, void *fn_ip) { int fd; + all_dhcpv6_ll.addr.part.prefix = 0xff02000000000000ULL; + all_dhcpv6_ll.addr.part.interface_id = 0x10002ULL; + my_fn_ip = (filename_ip_t *) fn_ip; fd = my_fn_ip->fd; @@ -129,8 +130,7 @@ dhcpv6 ( char *ret_buffer, void *fn_ip) return 0; } -static struct dhcp6_received_options * -dhcp6_process_options (uint8_t *option, int32_t option_length) +static void dhcp6_process_options (uint8_t *option, int32_t option_length) { struct dhcp_boot_url *option_boot_url; struct client_identifier *option_clientid; @@ -138,24 +138,19 @@ dhcp6_process_options (uint8_t *option, int32_t option_length) struct dhcp_dns *option_dns; struct dhcp_dns_list *option_dns_list; struct dhcp6_gen_option *option_gen; - struct dhcp6_received_options *received_options; char buffer[256]; - - received_options = malloc (sizeof(struct dhcp6_received_options)); while (option_length > 0) { switch ((uint16_t) *(option+1)) { case DHCPV6_OPTION_CLIENTID: option_clientid = (struct client_identifier *) option; option = option + option_clientid->length + 4; option_length = option_length - option_clientid->length - 4; - received_options->client_id = 1; break; case DHCPV6_OPTION_SERVERID: option_serverid = (struct server_identifier *) option; option = option + option_serverid->length + 4; option_length = option_length - option_serverid->length - 4; - received_options->server_id = 1; break; case DHCPV6_OPTION_DNS_SERVERS: option_dns = (struct dhcp_dns *) option; @@ -184,7 +179,7 @@ dhcp6_process_options (uint8_t *option, int32_t option_length) (char *)my_fn_ip->filename, (int)my_fn_ip->fd, option_boot_url->length) == -1) - return NULL; + return; break; default: option_gen = (struct dhcp6_gen_option *) option; @@ -192,8 +187,6 @@ dhcp6_process_options (uint8_t *option, int32_t option_length) option_length = option_length - option_gen->length - 4; } } - - return received_options; } uint32_t @@ -205,6 +198,9 @@ handle_dhcpv6(uint8_t * packet, int32_t packetsize) struct dhcp_message_reply *reply; reply = (struct dhcp_message_reply *) packet; + if (memcmp(reply->transaction_id, tid, 3)) + return -1; /* Wrong transaction ID */ + if (reply->type == 7) dhcpv6_state = DHCP_STATUSCODE_SUCCESS; diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.h b/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.h index 078a9f11f..fb77da648 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.h +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.h @@ -77,6 +77,7 @@ #define DUID_LL 3 /* DUID based on Link-layer Address */ /* Prototypes */ +void dhcpv6_generate_transaction_id(void); int32_t dhcpv6 ( char *ret_buffer, void *fn_ip); uint32_t handle_dhcpv6(uint8_t * , int32_t); @@ -102,6 +103,8 @@ struct server_identifier { uint8_t mac[6]; }; +#define DHCPV6_OPTREQUEST_NUMOPTS 3 + struct dhcp_info_request { struct client_identifier client_id; struct elapsed_time { @@ -112,7 +115,7 @@ struct dhcp_info_request { struct option_request { uint16_t code; uint16_t length; - uint16_t option_code[5]; + uint16_t option_code[DHCPV6_OPTREQUEST_NUMOPTS]; } option_request_option; }; @@ -141,12 +144,6 @@ struct dhcp_boot_url { uint8_t url[256]; }; -struct dhcp6_received_options { - uint8_t filename; - uint8_t ip; - uint8_t client_id; - uint8_t server_id; -}; struct dhcp_message_reply { uint8_t type; /* Message type */ uint8_t transaction_id[3]; /* Transaction id */ diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/dns.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/dns.c index 0ab1346c9..a5a36a18e 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netlib/dns.c +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/dns.c @@ -133,7 +133,7 @@ dns_init(uint32_t _dns_server_ip, uint8_t _dns_server_ipv6[16], uint8_t ip_versi * FALSE - error condition occurs. */ int8_t -dns_get_ip(int fd, int8_t * url, uint8_t * domain_ip, uint8_t ip_version) +dns_get_ip(int fd, char* url, uint8_t * domain_ip, uint8_t ip_version) { /* this counter is used so that we abort after 30 DNS request */ int32_t i; @@ -143,7 +143,7 @@ dns_get_ip(int fd, int8_t * url, uint8_t * domain_ip, uint8_t ip_version) (* domain_ip) = 0; // Retrieve host name from URL - if (!urltohost((char *) url, (char *) host_name)) { + if (!urltohost(url, (char *) host_name)) { printf("\nERROR:\t\t\tBad URL!\n"); return 0; } diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/dns.h b/qemu/roms/SLOF/clients/net-snk/app/netlib/dns.h index 82eea4e4d..b8756afca 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netlib/dns.h +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/dns.h @@ -20,7 +20,7 @@ extern int8_t dns_init(uint32_t _dns_server_ip, uint8_t _dns_server_ipv6[16], uint8_t ip_version); /* For given URL retrieves IPv4 from DNS-server. */ -extern int8_t dns_get_ip(int fd, int8_t * url, uint8_t * domain_ip, uint8_t ip_version); +extern int8_t dns_get_ip(int fd, char * url, uint8_t * domain_ip, uint8_t ip_version); /* Handles DNS-packets, which are detected by receive_ether. */ extern int32_t handle_dns(uint8_t * packet, int32_t packetsize); diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/ethernet.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/ethernet.c index bbfd6d1c3..1e03a0bf3 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netlib/ethernet.c +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/ethernet.c @@ -11,7 +11,7 @@ *****************************************************************************/ -/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ALGORITHMS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ +/******************************* ALGORITHMS ******************************/ /** \file netbase.c
        * *********************** Receive-handle diagram *************************
      @@ -36,12 +36,12 @@
        *  | APPLICATION        +----------------+-----------+
        *  V                    |                            |
        * upper               DNS (handle_dns)      BootP / DHCP (handle_bootp_client)
      - * 
      + *
        * ************************************************************************
        * 
      */ -/*>>>>>>>>>>>>>>>>>>>>>>> DEFINITIONS & DECLARATIONS <<<<<<<<<<<<<<<<<<<<*/ +/************************ DEFINITIONS & DECLARATIONS *********************/ #include #include @@ -50,22 +50,22 @@ #include -/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>> LOCAL VARIABLES <<<<<<<<<<<<<<<<<<<<<<<<<*/ +/****************************** LOCAL VARIABLES **************************/ static uint8_t ether_packet[ETH_MTU_SIZE]; static uint8_t own_mac[6] = {0, 0, 0, 0, 0, 0}; static uint8_t multicast_mac[] = {0x01, 0x00, 0x5E}; static const uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; -/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>> IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<*/ +/****************************** IMPLEMENTATION ***************************/ /** * Ethernet: Set the own MAC address to initializes ethernet layer. * * @param own_mac own hardware-address (MAC) */ -void -set_mac_address(const uint8_t * _own_mac) { +void set_mac_address(const uint8_t * _own_mac) +{ if (_own_mac) memcpy(own_mac, _own_mac, 6); else @@ -77,19 +77,19 @@ set_mac_address(const uint8_t * _own_mac) { * * @return own hardware-address (MAC) */ -const uint8_t * -get_mac_address(void) { +const uint8_t *get_mac_address(void) +{ return own_mac; } /** * Ethernet: Check if given multicast address is a multicast MAC address - * starting with 0x3333 + * starting with 0x3333 * - * @return true or false + * @return true or false */ -static uint8_t -is_multicast_mac(uint8_t * mac) { +static uint8_t is_multicast_mac(uint8_t * mac) +{ uint16_t mc = 0x3333; if (memcmp(mac, &mc, 2) == 0) @@ -98,7 +98,6 @@ is_multicast_mac(uint8_t * mac) { return 0; } - /** * Ethernet: Receives an ethernet-packet and handles it according to * Receive-handle diagram. @@ -107,8 +106,8 @@ is_multicast_mac(uint8_t * mac) { * @return ZERO - packet was handled or no packets received; * NON ZERO - error condition occurs. */ -int32_t -receive_ether(int fd) { +int32_t receive_ether(int fd) +{ int32_t bytes_received; struct ethhdr * ethh; @@ -118,7 +117,10 @@ receive_ether(int fd) { if (!bytes_received) // No messages return 0; - if (bytes_received < sizeof(struct ethhdr)) + if (bytes_received < 0) + return -1; /* recv() failed */ + + if ((size_t) bytes_received < sizeof(struct ethhdr)) return -1; // packet is too small ethh = (struct ethhdr *) ether_packet; @@ -176,9 +178,9 @@ send_ether(int fd, void* buffer, int len) * @see fill_dnshdr * @see fill_btphdr */ -void -fill_ethhdr(uint8_t * packet, uint16_t eth_type, - const uint8_t * src_mac, const uint8_t * dest_mac) { +void fill_ethhdr(uint8_t * packet, uint16_t eth_type, + const uint8_t * src_mac, const uint8_t * dest_mac) +{ struct ethhdr * ethh = (struct ethhdr *) packet; ethh -> type = htons(eth_type); diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/icmpv6.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/icmpv6.c index be6cc110f..c104f7015 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netlib/icmpv6.c +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/icmpv6.c @@ -40,9 +40,8 @@ send_router_solicitation (int fd) sizeof(struct ip6hdr)); /* Destination is "All routers multicast address" (link-local) */ - dest_addr.part.prefix = all_routers_ll.addr.part.prefix; - dest_addr.part.interface_id = all_routers_ll.addr.part.interface_id; - + dest_addr.part.prefix = 0xff02000000000000ULL; + dest_addr.part.interface_id = 2; /* Fill IPv6 header */ fill_ip6hdr (ether_packet + sizeof(struct ethhdr), @@ -78,8 +77,8 @@ handle_prefixoption (uint8_t *option) prefix_option = (struct option_prefix *) option; memcpy( &(prefix.addr), &(prefix_option->prefix.addr), IPV6_ADDR_LENGTH); - /* Link-local adresses in RAs are nonsense */ - if ( (IPV6_LL_PREFIX & (prefix_option->prefix.part.prefix)) == IPV6_LL_PREFIX ) + /* Link-local adresses in RAs are nonsense */ + if (ip6_is_linklocal(&prefix)) return; if (prefix_option->preferred_lifetime > prefix_option->valid_lifetime) diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv4.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv4.c index 8185de5e1..2b92c77c4 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv4.c +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv4.c @@ -11,7 +11,7 @@ *****************************************************************************/ -/*>>>>>>>>>>>>>>>>>>>>> DEFINITIONS & DECLARATIONS <<<<<<<<<<<<<<<<<<<<<<*/ +/********************** DEFINITIONS & DECLARATIONS ***********************/ #include #include @@ -81,32 +81,26 @@ struct icmphdr { } payload; }; -/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>> PROTOTYPES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ +/****************************** PROTOTYPES *******************************/ -static unsigned short -checksum(unsigned short *packet, int words); +static unsigned short checksum(unsigned short *packet, int words); -static void -arp_send_request(int fd, uint32_t dest_ip); +static void arp_send_request(int fd, uint32_t dest_ip); -static void -arp_send_reply(int fd, uint32_t src_ip, uint8_t * src_mac); +static void arp_send_reply(int fd, uint32_t src_ip, uint8_t * src_mac); -static void -fill_arphdr(uint8_t * packet, uint8_t opcode, - const uint8_t * src_mac, uint32_t src_ip, - const uint8_t * dest_mac, uint32_t dest_ip); +static void fill_arphdr(uint8_t * packet, uint8_t opcode, + const uint8_t * src_mac, uint32_t src_ip, + const uint8_t * dest_mac, uint32_t dest_ip); -static arp_entry_t* -lookup_mac_addr(uint32_t ipv4_addr); +static arp_entry_t *lookup_mac_addr(uint32_t ipv4_addr); -static void -fill_udp_checksum(struct iphdr *ipv4_hdr); +static void fill_udp_checksum(struct iphdr *ipv4_hdr); -static int8_t -handle_icmp(int fd, struct iphdr * iph, uint8_t * packet, int32_t packetsize); +static int8_t handle_icmp(int fd, struct iphdr * iph, uint8_t * packet, + int32_t packetsize); -/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>> LOCAL VARIABLES <<<<<<<<<<<<<<<<<<<<<<<<<*/ +/****************************** LOCAL VARIABLES **************************/ /* Routing parameters */ static uint32_t own_ip = 0; @@ -126,18 +120,19 @@ static uint8_t multicast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; static unsigned int arp_consumer = 0; static unsigned int arp_producer = 0; static arp_entry_t arp_table[ARP_ENTRIES]; -static arp_entry_t pending_pkt; + +static uint8_t pending_pkt_frame[ETH_MTU_SIZE]; +static int pending_pkt_len; /* Function pointer send_ip. Points either to send_ipv4() or send_ipv6() */ int (*send_ip) (int fd, void *, int); -/*>>>>>>>>>>>>>>>>>>>>>>>>>>>> IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<*/ +/***************************** IMPLEMENTATION ****************************/ /** * IPv4: Initialize the environment for the IPv4 layer. */ -static void -ipv4_init(void) +static void ipv4_init(void) { int i; @@ -153,7 +148,7 @@ ipv4_init(void) arp_table[i].pkt_pending = 0; } - /* Set IP send function to send_ipv4() */ + /* Set IP send function to send_ipv4() */ send_ip = &send_ipv4; } @@ -162,8 +157,7 @@ ipv4_init(void) * * @param _own_ip client IPv4 address (e.g. 127.0.0.1) */ -void -set_ipv4_address(uint32_t _own_ip) +void set_ipv4_address(uint32_t _own_ip) { own_ip = _own_ip; ipv4_init(); @@ -174,8 +168,7 @@ set_ipv4_address(uint32_t _own_ip) * * @return client IPv4 address (e.g. 127.0.0.1) */ -uint32_t -get_ipv4_address(void) +uint32_t get_ipv4_address(void) { return own_ip; } @@ -185,8 +178,7 @@ get_ipv4_address(void) * * @param _own_ip multicast IPv4 address (224.0.0.0 - 239.255.255.255) */ -void -set_ipv4_multicast(uint32_t _multicast_ip) +void set_ipv4_multicast(uint32_t _multicast_ip) { // is this IP Multicast out of range (224.0.0.0 - 239.255.255.255) if((htonl(_multicast_ip) < 0xE0000000) @@ -210,8 +202,7 @@ set_ipv4_multicast(uint32_t _multicast_ip) * * @return multicast IPv4 address (224.0.0.0 - 239.255.255.255 or 0 if not set) */ -uint32_t -get_ipv4_multicast(void) +uint32_t get_ipv4_multicast(void) { return multicast_ip; } @@ -221,8 +212,7 @@ get_ipv4_multicast(void) * * @param _router_ip router IPv4 address */ -void -set_ipv4_router(uint32_t _router_ip) +void set_ipv4_router(uint32_t _router_ip) { router_ip = _router_ip; ipv4_init(); @@ -233,8 +223,7 @@ set_ipv4_router(uint32_t _router_ip) * * @return router IPv4 address */ -uint32_t -get_ipv4_router(void) +uint32_t get_ipv4_router(void) { return router_ip; } @@ -244,8 +233,7 @@ get_ipv4_router(void) * * @param _subnet_mask netmask of the own IPv4 address */ -void -set_ipv4_netmask(uint32_t _subnet_mask) +void set_ipv4_netmask(uint32_t _subnet_mask) { subnet_mask = _subnet_mask; ipv4_init(); @@ -256,8 +244,7 @@ set_ipv4_netmask(uint32_t _subnet_mask) * * @return netmask of the own IPv4 address */ -uint32_t -get_ipv4_netmask(void) +uint32_t get_ipv4_netmask(void) { return subnet_mask; } @@ -280,9 +267,9 @@ get_ipv4_netmask(void) * @see fill_dnshdr * @see fill_btphdr */ -void -fill_iphdr(uint8_t * packet, uint16_t packetsize, - uint8_t ip_proto, uint32_t ip_src, uint32_t ip_dst) { +void fill_iphdr(uint8_t * packet, uint16_t packetsize, + uint8_t ip_proto, uint32_t ip_src, uint32_t ip_dst) +{ struct iphdr * iph = (struct iphdr *) packet; iph -> ip_hlv = 0x45; @@ -308,8 +295,7 @@ fill_iphdr(uint8_t * packet, uint16_t packetsize, * @see receive_ether * @see iphdr */ -int8_t -handle_ipv4(int fd, uint8_t * ip_packet, int32_t packetsize) +int8_t handle_ipv4(int fd, uint8_t * ip_packet, uint32_t packetsize) { struct iphdr * iph; int32_t old_sum; @@ -422,8 +408,7 @@ handle_ipv4(int fd, uint8_t * ip_packet, int32_t packetsize) * @see receive_ether * @see iphdr */ -int -send_ipv4(int fd, void* buffer, int len) +int send_ipv4(int fd, void* buffer, int len) { arp_entry_t *arp_entry = 0; struct iphdr *ip; @@ -506,13 +491,11 @@ send_ipv4(int fd, void* buffer, int len) arp_entry->pkt_pending = 1; arp_entry->ipv4_addr = ip_dst; memset(arp_entry->mac_addr, 0, 6); - pending_pkt.ipv4_addr = ip_dst; - memset(pending_pkt.mac_addr, 0, 6); - fill_ethhdr (pending_pkt.eth_frame, htons(ETHERTYPE_IP), + fill_ethhdr (pending_pkt_frame, htons(ETHERTYPE_IP), get_mac_address(), null_mac_addr); - memcpy(&pending_pkt.eth_frame[sizeof(struct ethhdr)], + memcpy(&pending_pkt_frame[sizeof(struct ethhdr)], buffer, len); - pending_pkt.eth_len = len + sizeof(struct ethhdr); + pending_pkt_len = len + sizeof(struct ethhdr); set_timer(TICKS_SEC); do { @@ -538,11 +521,9 @@ send_ipv4(int fd, void* buffer, int len) * * @param ipv4_hdr Points to the place where IPv4-header starts. */ - -static void -fill_udp_checksum(struct iphdr *ipv4_hdr) +static void fill_udp_checksum(struct iphdr *ipv4_hdr) { - int i; + unsigned i; unsigned long checksum = 0; struct iphdr ip_hdr; char *ptr; @@ -585,8 +566,7 @@ fill_udp_checksum(struct iphdr *ipv4_hdr) * @return Checksum * @see iphdr */ -static unsigned short -checksum(unsigned short * packet, int words) +static unsigned short checksum(unsigned short * packet, int words) { unsigned long checksum; @@ -598,8 +578,7 @@ checksum(unsigned short * packet, int words) return ~checksum; } -static arp_entry_t* -lookup_mac_addr(uint32_t ipv4_addr) +static arp_entry_t* lookup_mac_addr(uint32_t ipv4_addr) { unsigned int i; @@ -618,8 +597,7 @@ lookup_mac_addr(uint32_t ipv4_addr) * @param fd socket fd * @param dest_ip IP of the host which MAC should be obtained */ -static void -arp_send_request(int fd, uint32_t dest_ip) +static void arp_send_request(int fd, uint32_t dest_ip) { arp_entry_t *arp_entry = &arp_table[arp_producer]; @@ -642,8 +620,7 @@ arp_send_request(int fd, uint32_t dest_ip) * @param src_ip requester IP address (foreign IP) * @param src_mac requester MAC address (foreign MAC) */ -static void -arp_send_reply(int fd, uint32_t src_ip, uint8_t * src_mac) +static void arp_send_reply(int fd, uint32_t src_ip, uint8_t * src_mac) { arp_entry_t *arp_entry = &arp_table[arp_producer]; @@ -674,10 +651,9 @@ arp_send_reply(int fd, uint32_t src_ip, uint8_t * src_mac) * @see arphdr * @see fill_ethhdr */ -static void -fill_arphdr(uint8_t * packet, uint8_t opcode, - const uint8_t * src_mac, uint32_t src_ip, - const uint8_t * dest_mac, uint32_t dest_ip) +static void fill_arphdr(uint8_t * packet, uint8_t opcode, + const uint8_t * src_mac, uint32_t src_ip, + const uint8_t * dest_mac, uint32_t dest_ip) { struct arphdr * arph = (struct arphdr *) packet; @@ -706,8 +682,7 @@ fill_arphdr(uint8_t * packet, uint8_t opcode, * @see receive_ether * @see arphdr */ -int8_t -handle_arp(int fd, uint8_t * packet, int32_t packetsize) +int8_t handle_arp(int fd, uint8_t * packet, uint32_t packetsize) { struct arphdr * arph = (struct arphdr *) packet; @@ -754,11 +729,11 @@ handle_arp(int fd, uint8_t * packet, int32_t packetsize) // do we have something to send if (arp_table[i].pkt_pending) { - struct ethhdr * ethh = (struct ethhdr *) pending_pkt.eth_frame; + struct ethhdr * ethh = (struct ethhdr *) pending_pkt_frame; memcpy(ethh -> dest_mac, arp_table[i].mac_addr, 6); - send_ether(fd, pending_pkt.eth_frame, pending_pkt.eth_len); - pending_pkt.pkt_pending = 0; + send_ether(fd, pending_pkt_frame, pending_pkt_len); + arp_table[i].pkt_pending = 0; arp_table[i].eth_len = 0; } return 0; // no error @@ -780,8 +755,7 @@ handle_arp(int fd, uint8_t * packet, int32_t packetsize) * @param fd socket descriptor * @param _ping_dst_ip destination IPv4 address */ -void -ping_ipv4(int fd, uint32_t _ping_dst_ip) +void ping_ipv4(int fd, uint32_t _ping_dst_ip) { unsigned char packet[sizeof(struct iphdr) + sizeof(struct icmphdr)]; struct icmphdr *icmp; @@ -814,8 +788,7 @@ ping_ipv4(int fd, uint32_t _ping_dst_ip) * * @return ping_dst_ip host IPv4 address */ -uint32_t -pong_ipv4(void) +uint32_t pong_ipv4(void) { return ping_dst_ip; } @@ -830,8 +803,8 @@ pong_ipv4(void) * NON ZERO - packet was not handled (e.g. bad format) * @see handle_ipv4 */ -static int8_t -handle_icmp(int fd, struct iphdr * iph, uint8_t * packet, int32_t packetsize) +static int8_t handle_icmp(int fd, struct iphdr * iph, uint8_t * packet, + int32_t packetsize) { struct icmphdr *icmp = (struct icmphdr *) packet; diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv4.h b/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv4.h index eb719f8b2..18821ea74 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv4.h +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv4.h @@ -60,7 +60,7 @@ struct arphdr { uint32_t dest_ip; /**< Proto address of target of this packet */ } __attribute((packed)); -/*>>>>>>>>>>>>> Initialization of the IPv4 network layer. <<<<<<<<<<<<<*/ +/************** Initialization of the IPv4 network layer. **************/ extern void set_ipv4_address(uint32_t own_ip); extern uint32_t get_ipv4_address(void); extern void set_ipv4_multicast(uint32_t multicast_ip); @@ -88,9 +88,9 @@ extern void ping_ipv4(int fd, uint32_t _ping_dst_ip); extern uint32_t pong_ipv4(void); /* Handles IPv4-packets that are detected by receive_ether. */ -extern int8_t handle_ipv4(int fd, uint8_t * packet, int32_t packetsize); +extern int8_t handle_ipv4(int fd, uint8_t * packet, uint32_t packetsize); /* Handles ARP-packets that are detected by receive_ether. */ -extern int8_t handle_arp(int fd, uint8_t * packet, int32_t packetsize); +extern int8_t handle_arp(int fd, uint8_t * packet, uint32_t packetsize); #endif diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.c index 0cb0a2e7b..62d29ea86 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.c +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.c @@ -37,15 +37,23 @@ static int ip6_is_multicast (ip6_addr_t * ip); /****************************** LOCAL VARIABLES **************************/ +/* List of Ipv6 Addresses */ +static struct ip6addr_list_entry *first_ip6; +static struct ip6addr_list_entry *last_ip6; + /* Own IPv6 address */ static struct ip6addr_list_entry *own_ip6; +/* All nodes link-local address */ +struct ip6addr_list_entry all_nodes_ll; + /* Null IPv6 address */ static ip6_addr_t null_ip6; /* helper variables */ static uint8_t null_mac[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +struct ip6_config ip6_state; /****************************** IMPLEMENTATION ***************************/ @@ -55,9 +63,10 @@ static uint8_t null_mac[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; * @param fd Socket descriptor * @param _own_ip client IPv6 address (e.g. ::1) */ -void -set_ipv6_address (int fd, ip6_addr_t *_own_ip6) +void set_ipv6_address(int fd, ip6_addr_t *_own_ip6) { + struct ip6addr_list_entry *ile; + own_ip6 = malloc (sizeof(struct ip6addr_list_entry)); /* If no address was passed as a parameter generate a link-local @@ -73,6 +82,20 @@ set_ipv6_address (int fd, ip6_addr_t *_own_ip6) ip6addr_add (own_ip6); ipv6_init(fd); + + /* + * Check whether we've got a non-link-local address during + * ipv6_init() and use that as preferred address if possible + */ + if (_own_ip6 == NULL) { + for (ile = first_ip6; ile != NULL ; ile = ile->next) { + if (!ip6_is_multicast(&ile->addr) && + !ip6_is_linklocal(&ile->addr)) { + own_ip6 = ile; + break; + } + } + } } /** @@ -80,8 +103,7 @@ set_ipv6_address (int fd, ip6_addr_t *_own_ip6) * * @return pointer to client IPv6 address (e.g. ::1) */ -ip6_addr_t * -get_ipv6_address (void) +ip6_addr_t *get_ipv6_address(void) { return (ip6_addr_t *) &(own_ip6->addr); } @@ -92,8 +114,7 @@ get_ipv6_address (void) * @return 0 - IPv6 address is not in list * 1 - IPv6 address is in list */ -static int8_t -find_ip6addr (ip6_addr_t *ip) +static int8_t find_ip6addr(ip6_addr_t *ip) { struct ip6addr_list_entry *n = NULL; @@ -119,8 +140,7 @@ find_ip6addr (ip6_addr_t *ip) * @see handle_udp * @see ip6hdr */ -int8_t -handle_ipv6 (int fd, uint8_t * ip6_packet, int32_t packetsize) +int8_t handle_ipv6(int fd, uint8_t * ip6_packet, uint32_t packetsize) { struct ip6hdr *ip6 = NULL; @@ -164,11 +184,9 @@ handle_ipv6 (int fd, uint8_t * ip6_packet, int32_t packetsize) * @see fill_dnshdr * @see fill_btphdr */ -void -fill_ip6hdr (uint8_t * packet, uint16_t packetsize, - uint8_t ip_proto, ip6_addr_t *ip6_src, ip6_addr_t *ip6_dst) +void fill_ip6hdr(uint8_t * packet, uint16_t packetsize, + uint8_t ip_proto, ip6_addr_t *ip6_src, ip6_addr_t *ip6_dst) { - struct ip6hdr * ip6h = (struct ip6hdr *) packet; ip6h->ver_tc_fl = 6 << 28; // set version to 6 @@ -184,8 +202,7 @@ fill_ip6hdr (uint8_t * packet, uint16_t packetsize, * See RFC 4291 "IP Version 6 Addressing Architecture" * */ -uint64_t -mac2eui64 (const uint8_t *mac) +uint64_t mac2eui64(const uint8_t *mac) { uint8_t eui64id[8]; uint64_t retid; @@ -205,8 +222,7 @@ mac2eui64 (const uint8_t *mac) * @param own_mac MAC of NIC * @return ll_addr pointer to newly created link-local address */ -ip6_addr_t * -ip6_create_ll_address (const uint8_t *own_mac) +ip6_addr_t *ip6_create_ll_address(const uint8_t *own_mac) { ip6_addr_t *ll_addr; @@ -223,8 +239,7 @@ ip6_create_ll_address (const uint8_t *own_mac) * @param struct ip6_addr_list_entry *ip6 * @return true or false */ -int8_t -unknown_prefix (ip6_addr_t *ip) +int8_t unknown_prefix(ip6_addr_t *ip) { struct ip6addr_list_entry *node; @@ -240,8 +255,7 @@ unknown_prefix (ip6_addr_t *ip) * @return NULL - malloc failed * ! NULL - pointer to new prefix_info */ -struct prefix_info * -ip6_create_prefix_info () +struct prefix_info *ip6_create_prefix_info() { struct prefix_info *prfx_info; @@ -259,8 +273,7 @@ ip6_create_prefix_info () * @param ip6_addr prefix (as received in RA) * @return NULL - pointer to new ip6addr_list entry */ -void * -ip6_prefix2addr (ip6_addr_t prefix) +void *ip6_prefix2addr(ip6_addr_t prefix) { struct ip6addr_list_entry *new_address; uint64_t interface_id; @@ -287,8 +300,7 @@ ip6_prefix2addr (ip6_addr_t prefix) * @return 0 - passed pointer = NULL; * 1 - ok */ -int8_t -ip6addr_add (struct ip6addr_list_entry *new_address) +int8_t ip6addr_add(struct ip6addr_list_entry *new_address) { struct ip6addr_list_entry *solicited_node; @@ -332,8 +344,7 @@ ip6addr_add (struct ip6addr_list_entry *new_address) * * @param fd socket fd */ -static void -ipv6_init (int fd) +static void ipv6_init(int fd) { int i = 0; @@ -349,21 +360,9 @@ ipv6_init (int fd) /* Multicast addresses */ all_nodes_ll.addr.part.prefix = 0xff02000000000000; all_nodes_ll.addr.part.interface_id = 1; - all_dhcpv6_ll.addr.part.prefix = 0xff02000000000000ULL; - all_dhcpv6_ll.addr.part.interface_id = 0x10002ULL; - all_routers_ll.addr.part.prefix = 0xff02000000000000; - all_routers_ll.addr.part.interface_id = 2; - ip6addr_add(&all_nodes_ll); - /* ... */ - /* Router list */ - first_router = NULL; - last_router = first_router; - - /* Init Neighbour cache */ - first_neighbor = NULL; - last_neighbor = first_neighbor; + ndp_init(); send_router_solicitation (fd); for(i=0; i < 4 && !is_ra_received(); i++) { @@ -382,8 +381,7 @@ ipv6_init (int fd) * @param ip6_addr ip_1 * @param ip6_addr ip_2 */ -int8_t -ip6_cmp (ip6_addr_t *ip_1, ip6_addr_t *ip_2) +int8_t ip6_cmp(ip6_addr_t *ip_1, ip6_addr_t *ip_2) { return ((int8_t) !memcmp( &(ip_1->addr[0]), &(ip_2->addr[0]), IPV6_ADDR_LENGTH )); @@ -396,11 +394,9 @@ ip6_cmp (ip6_addr_t *ip_1, ip6_addr_t *ip_2) * @param *ip - pointer to IPv6 address * @return true or false */ -int -ip6_is_multicast (ip6_addr_t * ip) +int ip6_is_multicast(ip6_addr_t * ip) { - uint8_t mc = 0xFF; - return ! memcmp(&ip->addr[0], &mc, 1); + return ip->addr[0] == 0xFF; } /** @@ -408,17 +404,11 @@ ip6_is_multicast (ip6_addr_t * ip) * (e.g. UDP or ICMPv6) * * @param *ip - pointer to IPv6 address + * @param *mc_mac pointer to an array with 6 bytes (for the MAC address) * @return pointer to Multicast MAC address */ -static uint8_t * -ip6_to_multicast_mac (ip6_addr_t * ip) +static uint8_t *ip6_to_multicast_mac(ip6_addr_t * ip, uint8_t *mc_mac) { - uint8_t *mc_mac; - - mc_mac = malloc(ETH_ALEN); - if (!mc_mac) - return NULL; - mc_mac[0] = 0x33; mc_mac[1] = 0x33; memcpy (mc_mac+2, (uint8_t *) &(ip->addr)+12, 4); @@ -437,8 +427,8 @@ ip6_to_multicast_mac (ip6_addr_t * ip) * starting from *packet * @return checksum */ -static unsigned short -ip6_checksum (struct ip6hdr *ip6h, unsigned short *packet, int words) +static unsigned short ip6_checksum(struct ip6hdr *ip6h, unsigned short *packet, + int words) { int i=0; unsigned long checksum; @@ -478,8 +468,7 @@ ip6_checksum (struct ip6hdr *ip6h, unsigned short *packet, int words) * @see receive_ether * @see ip6hdr */ -int -send_ipv6 (int fd, void* buffer, int len) +int send_ipv6(int fd, void* buffer, int len) { struct neighbor *n; struct ip6hdr *ip6h; @@ -519,17 +508,9 @@ send_ipv6 (int fd, void* buffer, int len) n = find_neighbor (&ip_dst); - // If packet is a neighbor solicitation - if (icmp6h->type == ICMPV6_NEIGHBOUR_SOLICITATION) { - mac_addr = ip6_to_multicast_mac (&ip_dst); - fill_ethhdr( buffer-sizeof(struct ethhdr), htons(ETHERTYPE_IPv6), - get_mac_address(), - mac_addr); - } - // If address is a multicast address, create a proper mac address - else if (ip6_is_multicast (&ip_dst)) { - mac_addr = ip6_to_multicast_mac (&ip_dst); + if (ip6_is_multicast (&ip_dst)) { + mac_addr = ip6_to_multicast_mac (&ip_dst, mac); } else { // Check if the MAC address is already cached @@ -572,8 +553,7 @@ send_ipv6 (int fd, void* buffer, int len) return send_ether (fd, n->eth_frame, len + sizeof(struct ethhdr)); } -static int -check_colons(const char *str) +static int check_colons(const char *str) { char *pch, *prv; int col = 0; @@ -595,7 +575,7 @@ check_colons(const char *str) dprintf("The number of col : %d \n",col); dprintf("The number of dcol : %d \n",dcol); - if((dcol > 1) || /* Cannot have 2 "::" */ + if((dcol > 1) || /* Cannot have 2 "::" */ ((dcol == 1) && (col > 5)) || /* Too many ':'s */ ((dcol == 0) && (col != 7)) ) { /* Too few ':'s */ dprintf(" exiting for check_colons \n"); @@ -605,8 +585,7 @@ check_colons(const char *str) return (col+dcol); } -static int -ipv6str_to_bytes(const char *str, char *ip) +static int ipv6str_to_bytes(const char *str, char *ip) { char block[5]; int res; diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.h b/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.h index b496364f3..72c6ee22f 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.h +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.h @@ -26,6 +26,7 @@ #define IPV6_ADDR_LENGTH 16 /* Size of IPv6 adress in bytes */ #define IPV6_LL_PREFIX 0xFE80000000000000ULL +#define IPV6_LL_PREFIX_MASK 0xFFC0000000000000ULL #define IPV6_SOLIC_NODE_PREFIX 0xFF02000000000000ULL #define IPV6_SOLIC_NODE_IFACE_ID 0x00000001FF000000ULL @@ -126,32 +127,17 @@ struct ip6_config { uint8_t managed_mode:1, other_config:1, reserved:6; -} ip6_state; +}; /******************** VARIABLES **********************************************/ /* Function pointer send_ip. Points either to send_ipv4() or send_ipv6() */ extern int (*send_ip) (int fd, void *, int); -/* IPv6 link-local multicast addresses */ -struct ip6addr_list_entry all_routers_ll; // Routers -struct ip6addr_list_entry all_dhcpv6_ll; // DHCPv6 servers -struct ip6addr_list_entry all_nodes_ll; // All IPv6 nodes - -/* List of Ipv6 Addresses */ -struct ip6addr_list_entry *first_ip6; -struct ip6addr_list_entry *last_ip6; - -/* Neighbor cache */ -struct neighbor *first_neighbor; -struct neighbor *last_neighbor; - -/* Router list */ -struct router *first_router; -struct router *last_router; +extern struct ip6_config ip6_state; /******************** FUNCTIONS *********************************************/ /* Handles IPv6-packets that are detected by receive_ether. */ -int8_t handle_ipv6(int fd, uint8_t * ip6_packet, int32_t packetsize); +int8_t handle_ipv6(int fd, uint8_t * ip6_packet, uint32_t packetsize); /* Fill IPv6 header */ void fill_ip6hdr(uint8_t * packet, uint16_t packetsize, @@ -179,6 +165,12 @@ void * ip6_prefix2addr (ip6_addr_t prefix); /* Compare IPv6 adresses */ int8_t ip6_cmp( ip6_addr_t *ip_1, ip6_addr_t *ip_2 ); +/* Check if it is a link-local address */ +static inline int ip6_is_linklocal(ip6_addr_t *ip) +{ + return (ip->part.prefix & IPV6_LL_PREFIX_MASK) == IPV6_LL_PREFIX; +} + /* Check if prefix is already in our list */ int8_t unknown_prefix (ip6_addr_t *ip); diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/ndp.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/ndp.c index ed9d61f4a..96faa8716 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netlib/ndp.c +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/ndp.c @@ -17,6 +17,14 @@ #include #include +/* Neighbor cache */ +static struct neighbor *first_neighbor; +static struct neighbor *last_neighbor; + +/* Router list */ +static struct router *first_router; +static struct router *last_router; + /* * NET: add new router to list * @param struct router nghb - new router @@ -145,3 +153,14 @@ find_neighbor (ip6_addr_t *ip) return NULL; /* neighbor is unknown */ } + +void ndp_init(void) +{ + /* Router list */ + first_router = NULL; + last_router = first_router; + + /* Init Neighbour cache */ + first_neighbor = NULL; + last_neighbor = first_neighbor; +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/ndp.h b/qemu/roms/SLOF/clients/net-snk/app/netlib/ndp.h index ee5235fe1..c785c4897 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netlib/ndp.h +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/ndp.h @@ -59,6 +59,7 @@ struct neighbor { }; /******************** FUNCTIONS *********************************************/ +void ndp_init(void); int8_t neighbor_add (struct neighbor *); void * neighbor_create (uint8_t *packet, struct packeth *headers); struct neighbor * find_neighbor (ip6_addr_t *); diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/tcp.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/tcp.c index 5511aa00a..faa0b83ac 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netlib/tcp.c +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/tcp.c @@ -10,16 +10,14 @@ * IBM Corporation - initial implementation *****************************************************************************/ -/*>>>>>>>>>>>>>>>>>>>>>>> DEFINITIONS & DECLARATIONS <<<<<<<<<<<<<<<<<<<<*/ +/************************ DEFINITIONS & DECLARATIONS *********************/ #include #include +/****************************** LOCAL VARIABLES **************************/ -/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>> LOCAL VARIABLES <<<<<<<<<<<<<<<<<<<<<<<<<*/ - -/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>> IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<*/ - +/****************************** IMPLEMENTATION ***************************/ /** * TCP: Handles TCP-packets according to Receive-handle diagram. @@ -29,13 +27,11 @@ * @return ZERO - packet handled successfully; * NON ZERO - packet was not handled (e.g. bad format) */ -int8_t -handle_tcp(uint8_t * tcp_packet, int32_t packetsize) +int8_t handle_tcp(uint8_t * tcp_packet, int32_t packetsize) { return -1; } - /** * NET: This function handles situation when "Destination unreachable" * ICMP-error occurs during sending TCP-packet. @@ -45,6 +41,6 @@ handle_tcp(uint8_t * tcp_packet, int32_t packetsize) * @param packetsize length of the packet * @see handle_icmp */ -void -handle_tcp_dun(uint8_t * tcp_packet, uint32_t packetsize, uint8_t err_code) { +void handle_tcp_dun(uint8_t * tcp_packet, uint32_t packetsize, uint8_t err_code) +{ } diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/tftp.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/tftp.c index 0a7c0ec63..c1197cf17 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netlib/tftp.c +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/tftp.c @@ -50,13 +50,13 @@ static unsigned short block = 0; static unsigned short blocksize; static char blocksize_str[6]; /* Blocksize string for read request */ static int received_len = 0; -static int retries = 0; +static unsigned int retries = 0; static int huge_load; static int len; static int tftp_finished = 0; static int lost_packets = 0; -static int tftp_errno = 0; -static int ip_version = 0; +static int tftp_errno = 0; +static int ip_version = 0; static short port_number = -1; static tftp_err_t *tftp_err; static filename_ip_t *fn_ip; @@ -69,8 +69,7 @@ static filename_ip_t *fn_ip; */ #ifdef __DEBUG__ -static void -dump_package(unsigned char *buffer, unsigned int len) +static void dump_package(unsigned char *buffer, unsigned int len) { int i; @@ -89,8 +88,7 @@ dump_package(unsigned char *buffer, unsigned int len) * * @fd: Socket Descriptor */ -static void -send_rrq(int fd) +static void send_rrq(int fd) { int ip_len = 0; int ip6_payload_len = 0; @@ -121,7 +119,7 @@ send_rrq(int fd) + strlen("blksize") + strlen(blocksize_str) + 2; ip_len = sizeof(struct ip6hdr) + ip6_payload_len; fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, get_ipv6_address(), - &(fn_ip->server_ip6)); + &(fn_ip->server_ip6)); } udp_len = htons(sizeof(struct udphdr) @@ -158,8 +156,7 @@ send_rrq(int fd) * @blckno: block number * @dport: UDP destination port */ -static void -send_ack(int fd, int blckno, unsigned short dport) +static void send_ack(int fd, int blckno, unsigned short dport) { int ip_len = 0; int ip6_payload_len = 0; @@ -182,8 +179,7 @@ send_ack(int fd, int blckno, unsigned short dport) ip6 = (struct ip6hdr *) packet; udph = (struct udphdr *) (ip6 + 1); ip6_payload_len = sizeof(struct udphdr) + 4; - ip_len = sizeof(struct ethhdr) + sizeof(struct ip6hdr) + - ip6_payload_len; + ip_len = sizeof(struct ip6hdr) + ip6_payload_len; fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, get_ipv6_address(), &(fn_ip->server_ip6)); } @@ -210,8 +206,7 @@ send_ack(int fd, int blckno, unsigned short dport) * @error_code: Used sub code for error packet * @dport: UDP destination port */ -static void -send_error(int fd, int error_code, unsigned short dport) +static void send_error(int fd, int error_code, unsigned short dport) { int ip_len = 0; int ip6_payload_len = 0; @@ -234,8 +229,7 @@ send_error(int fd, int error_code, unsigned short dport) ip6 = (struct ip6hdr *) packet; udph = (struct udphdr *) (ip6 + 1); ip6_payload_len = sizeof(struct udphdr) + 5; - ip_len = sizeof(struct ethhdr) + sizeof(struct ip6hdr) + - ip6_payload_len; + ip_len = sizeof(struct ip6hdr) + ip6_payload_len; fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, get_ipv6_address(), &(fn_ip->server_ip6)); } @@ -256,8 +250,7 @@ send_error(int fd, int error_code, unsigned short dport) return; } -static void -print_progress(int urgent, int received_bytes) +static void print_progress(int urgent, int received_bytes) { static unsigned int i = 1; static int first = -1; @@ -265,7 +258,7 @@ print_progress(int urgent, int received_bytes) char buffer[100]; char *ptr; - // 1MB steps or 0x400 times or urgent + // 1MB steps or 0x400 times or urgent if(((received_bytes - last_bytes) >> 20) > 0 || (i & 0x3FF) == 0 || urgent) { if(!first) { @@ -295,8 +288,7 @@ print_progress(int urgent, int received_bytes) * @param len the length of the network packet * @return the blocksize the server supports or 0 for error */ -static int -get_blksize(unsigned char *buffer, unsigned int len) +static int get_blksize(unsigned char *buffer, unsigned int len) { unsigned char *orig = buffer; /* skip all headers until tftp has been reached */ @@ -325,7 +317,7 @@ get_blksize(unsigned char *buffer, unsigned int len) } /** - * Handle incoming tftp packets after read request was sent + * Handle incoming tftp packets after read request was sent * * this function also prints out some status characters * \|-/ for each packet received @@ -334,13 +326,12 @@ get_blksize(unsigned char *buffer, unsigned int len) * #+* for different unexpected TFTP packets (not very good) * * @param fd socket descriptor - * @param packet points to the UDP header of the packet + * @param packet points to the UDP header of the packet * @param len the length of the network packet * @return ZERO if packet was handled successfully - * ERRORCODE if error occurred + * ERRORCODE if error occurred */ -int32_t -handle_tftp(int fd, uint8_t *pkt, int32_t packetsize) +int32_t handle_tftp(int fd, uint8_t *pkt, int32_t packetsize) { struct udphdr *udph; struct tftphdr *tftp; @@ -397,7 +388,7 @@ handle_tftp(int fd, uint8_t *pkt, int32_t packetsize) case ENOUSER: tftp_errno = -7; // ERROR: no such user break; - default: + default: tftp_errno = -1; // ERROR: unknown error } goto error; @@ -489,8 +480,7 @@ error: * * @param err_code Error Code (e.g. "Host unreachable") */ -void -handle_tftp_dun(uint8_t err_code) +void handle_tftp_dun(uint8_t err_code) { tftp_errno = - err_code - 10; tftp_finished = 1; @@ -510,10 +500,9 @@ handle_tftp_dun(uint8_t err_code) * @return ZERO - error condition occurs * NON ZERO - size of received file */ -int -tftp(filename_ip_t * _fn_ip, unsigned char *_buffer, int _len, - unsigned int _retries, tftp_err_t * _tftp_err, - int32_t _mode, int32_t _blocksize, int _ip_version) +int tftp(filename_ip_t * _fn_ip, unsigned char *_buffer, int _len, + unsigned int _retries, tftp_err_t * _tftp_err, + int32_t _mode, int32_t _blocksize, int _ip_version) { retries = _retries; fn_ip = _fn_ip; @@ -592,6 +581,6 @@ tftp(filename_ip_t * _fn_ip, unsigned char *_buffer, int _len, printf("\n"); if (lost_packets) printf("Lost ACK packets: %d\n", lost_packets); - + return received_len; } diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/udp.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/udp.c index db29bc90f..5d16e52c6 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netlib/udp.c +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/udp.c @@ -10,7 +10,7 @@ * IBM Corporation - initial implementation *****************************************************************************/ -/*>>>>>>>>>>>>>>>>>>>>>>> DEFINITIONS & DECLARATIONS <<<<<<<<<<<<<<<<<<<<*/ +/************************ DEFINITIONS & DECLARATIONS *********************/ #include #include @@ -25,7 +25,7 @@ -/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>> LOCAL VARIABLES <<<<<<<<<<<<<<<<<<<<<<<<<*/ +/****************************** LOCAL VARIABLES **************************/ #ifdef USE_MTFTP @@ -33,17 +33,19 @@ uint16_t net_tftp_uport; uint16_t net_mtftp_uport; -void net_set_tftp_port(uint16_t tftp_port) { +void net_set_tftp_port(uint16_t tftp_port) +{ net_tftp_uport = tftp_port; } -void net_set_mtftp_port(uint16_t tftp_port) { +void net_set_mtftp_port(uint16_t tftp_port) +{ net_mtftp_uport = tftp_port; } #endif -/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>> IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<*/ +/****************************** IMPLEMENTATION ***************************/ /** @@ -56,8 +58,8 @@ void net_set_mtftp_port(uint16_t tftp_port) { * @see receive_ether * @see udphdr */ -int8_t -handle_udp(int fd, uint8_t * udp_packet, int32_t packetsize) { +int8_t handle_udp(int fd, uint8_t * udp_packet, uint32_t packetsize) +{ struct udphdr * udph = (struct udphdr *) udp_packet; if (packetsize < sizeof(struct udphdr)) @@ -108,8 +110,8 @@ handle_udp(int fd, uint8_t * udp_packet, int32_t packetsize) { * @param packetsize length of the packet * @see handle_icmp */ -void -handle_udp_dun(uint8_t * udp_packet, uint32_t packetsize, uint8_t err_code) { +void handle_udp_dun(uint8_t * udp_packet, uint32_t packetsize, uint8_t err_code) +{ struct udphdr * udph = (struct udphdr *) udp_packet; if (packetsize < sizeof(struct udphdr)) @@ -139,9 +141,9 @@ handle_udp_dun(uint8_t * udp_packet, uint32_t packetsize, uint8_t err_code) { * @see fill_dnshdr * @see fill_btphdr */ -void -fill_udphdr(uint8_t * packet, uint16_t packetsize, - uint16_t src_port, uint16_t dest_port) { +void fill_udphdr(uint8_t * packet, uint16_t packetsize, + uint16_t src_port, uint16_t dest_port) +{ struct udphdr * udph = (struct udphdr *) packet; udph -> uh_sport = htons(src_port); diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/udp.h b/qemu/roms/SLOF/clients/net-snk/app/netlib/udp.h index 1ba9332ce..f154542dd 100644 --- a/qemu/roms/SLOF/clients/net-snk/app/netlib/udp.h +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/udp.h @@ -40,7 +40,7 @@ typedef int32_t *(*handle_upper_udp_t)(uint8_t *, int32_t); typedef void *(*handle_upper_udp_dun_t)(uint8_t); /* Handles UDP-packets that are detected by any network layer. */ -extern int8_t handle_udp(int fd, uint8_t * udp_packet, int32_t packetsize); +extern int8_t handle_udp(int fd, uint8_t * udp_packet, uint32_t packetsize); /* Handles UDP related ICMP-Dest.Unreachable packets that are detected by * the network layers. */ diff --git a/qemu/roms/SLOF/clients/net-snk/client.lds b/qemu/roms/SLOF/clients/net-snk/client.lds index 39d04594e..c2086445b 100644 --- a/qemu/roms/SLOF/clients/net-snk/client.lds +++ b/qemu/roms/SLOF/clients/net-snk/client.lds @@ -44,10 +44,10 @@ SECTIONS { *(.opd) } - . = ALIGN(0x10); + . = ALIGN(256); .got : { - _got = .; + _got = DEFINED (.TOC.) ? .TOC. : ADDR (.got) + 0x8000; *(.got) *(.toc) _got_end = .; diff --git a/qemu/roms/SLOF/clients/net-snk/kernel/entry.S b/qemu/roms/SLOF/clients/net-snk/kernel/entry.S index 8849fb9d1..bf10542bd 100644 --- a/qemu/roms/SLOF/clients/net-snk/kernel/entry.S +++ b/qemu/roms/SLOF/clients/net-snk/kernel/entry.S @@ -44,7 +44,7 @@ C_ENTRY(_entry) bcl 20,31,over # branch after pointer table base: .align 3 -.LCgot: .quad _got-base+0x8000 +.LCgot: .quad _got-base .LCstack: .quad _stack+STACKSIZE-0x80-base over: mflr r8 # gpr 8 is the base diff --git a/qemu/roms/SLOF/clients/takeover/client.lds b/qemu/roms/SLOF/clients/takeover/client.lds index 2701d8e1e..0ab428a01 100644 --- a/qemu/roms/SLOF/clients/takeover/client.lds +++ b/qemu/roms/SLOF/clients/takeover/client.lds @@ -43,8 +43,8 @@ SECTIONS { .got : { - . = ALIGN(8); - _got = .; + . = ALIGN(256); + _got = DEFINED (.TOC.) ? .TOC. : ADDR (.got) + 0x8000; *(.got .toc) _got_end = .; } diff --git a/qemu/roms/SLOF/clients/takeover/entry.S b/qemu/roms/SLOF/clients/takeover/entry.S index a1030eb40..ff482732d 100644 --- a/qemu/roms/SLOF/clients/takeover/entry.S +++ b/qemu/roms/SLOF/clients/takeover/entry.S @@ -21,7 +21,7 @@ _wrapclient: bcl 20,31,over # branch after pointer table base: .align 3 -.LCgot: .quad _got-base+0x8000 +.LCgot: .quad _got-base over: mflr r8 # gpr 8 is the base ld r2, .LCgot-base(r8) # load got pointer diff --git a/qemu/roms/SLOF/clients/takeover/main.c b/qemu/roms/SLOF/clients/takeover/main.c index 360d8eaed..1e1b02614 100644 --- a/qemu/roms/SLOF/clients/takeover/main.c +++ b/qemu/roms/SLOF/clients/takeover/main.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include extern void call_client_interface(of_arg_t *); diff --git a/qemu/roms/SLOF/include/byteorder.h b/qemu/roms/SLOF/include/byteorder.h index 60ca67267..d4a2c8ca7 100644 --- a/qemu/roms/SLOF/include/byteorder.h +++ b/qemu/roms/SLOF/include/byteorder.h @@ -19,38 +19,36 @@ #include -static inline uint16_t -bswap_16 (uint16_t x) +typedef uint16_t le16; +typedef uint32_t le32; +typedef uint64_t le64; + +static inline uint16_t bswap_16 (uint16_t x) { return __builtin_bswap16(x); } -static inline uint32_t -bswap_32 (uint32_t x) +static inline uint32_t bswap_32 (uint32_t x) { return __builtin_bswap32(x); } -static inline uint64_t -bswap_64 (uint64_t x) +static inline uint64_t bswap_64 (uint64_t x) { return __builtin_bswap64(x); } -static inline void -bswap_16p (uint16_t *x) +static inline void bswap_16p (uint16_t *x) { *x = __builtin_bswap16(*x); } -static inline void -bswap_32p (uint32_t *x) +static inline void bswap_32p (uint32_t *x) { *x = __builtin_bswap32(*x); } -static inline void -bswap_64p (uint64_t *x) +static inline void bswap_64p (uint64_t *x) { *x = __builtin_bswap64(*x); } diff --git a/qemu/roms/SLOF/include/helpers.h b/qemu/roms/SLOF/include/helpers.h index fb105345e..5b3d711ac 100644 --- a/qemu/roms/SLOF/include/helpers.h +++ b/qemu/roms/SLOF/include/helpers.h @@ -30,8 +30,10 @@ extern long SLOF_dma_map_in(void *virt, long size, int cacheable); extern void SLOF_dma_map_out(long phys, void *virt, long size); extern long SLOF_pci_config_read32(long offset); extern long SLOF_pci_config_read16(long offset); +extern long SLOF_pci_config_read8(long offset); extern void SLOF_pci_config_write32(long offset, long value); extern void SLOF_pci_config_write16(long offset, long value); +extern void SLOF_pci_config_write8(long offset, long value); extern void *SLOF_translate_my_address(void *addr); #define offset_of(type, member) ((long) &((type *)0)->member) diff --git a/qemu/roms/SLOF/include/ppc970/cache.h b/qemu/roms/SLOF/include/ppc970/cache.h index b74868986..500182ea6 100644 --- a/qemu/roms/SLOF/include/ppc970/cache.h +++ b/qemu/roms/SLOF/include/ppc970/cache.h @@ -55,8 +55,8 @@ cache_inhibited_access(uint64_t, 64) #define _FASTMOVE(s, d, size) \ switch (((type_u)s | (type_u)d | size) & (sizeof(type_u)-1)) { \ case 0: _MOVE(s, d, size, type_u); break; \ - case sizeof(type_l): _MOVE(s, d, size, type_l); break; \ - case sizeof(type_w): _MOVE(s, d, size, type_w); break; \ + case 4: _MOVE(s, d, size, type_l); break; \ + case 2: case 6: _MOVE(s, d, size, type_w); break; \ default: _MOVE(s, d, size, type_c); break; \ } @@ -78,9 +78,51 @@ cache_inhibited_access(uint64_t, 64) #define _FASTRMOVE(s, d, size) \ switch (((type_u)s | (type_u)d | size) & (sizeof(type_u)-1)) { \ case 0: _RMOVE(s, d, size, type_u); break; \ - case sizeof(type_l): _RMOVE(s, d, size, type_l); break; \ - case sizeof(type_w): _RMOVE(s, d, size, type_w); break; \ + case 4: _RMOVE(s, d, size, type_l); break; \ + case 2: case 6: _RMOVE(s, d, size, type_w); break; \ default: _RMOVE(s, d, size, type_c); break; \ } +/* main RAM to IO memory move */ +#define FAST_MRMOVE_TYPED(s, d, size, t) \ +{ \ + t *s1 = (s), *d1 = (d); \ + register t tmp; \ + while (size > 0) { \ + tmp = *s1++; SET_CI; *d1++ = tmp; CLR_CI; size -= sizeof(t); \ + } \ +} + +#define FAST_MRMOVE(s, d, size) \ + switch (((type_u)(s) | (type_u)(d) | (size)) & (sizeof(type_u)-1)) { \ + case 0: FAST_MRMOVE_TYPED(s, d, size, type_u); break; \ + case 4: FAST_MRMOVE_TYPED(s, d, size, type_l); break; \ + case 2: case 6: FAST_MRMOVE_TYPED(s, d, size, type_w); break; \ + default: FAST_MRMOVE_TYPED(s, d, size, type_c); break; \ + } + +/* fill IO memory with pattern */ +#define FAST_RFILL_TYPED(dst, size, pat, t) \ +{ \ + t *d1 = (dst); \ + register t tmp = 0; \ + int i = sizeof(t); \ + while (i-- > 0) { \ + tmp <<= 8; tmp |= pat & 0xff; \ + } \ + SET_CI; \ + while (size > 0) { \ + *d1++ = tmp; size -= sizeof(t); \ + } \ + CLR_CI; \ +} + +#define FAST_RFILL(dst, size, pat) \ + switch (((type_u)dst | size) & (sizeof(type_u)-1)) { \ + case 0: FAST_RFILL_TYPED(dst, size, pat, type_u); break; \ + case 4: FAST_RFILL_TYPED(dst, size, pat, type_l); break; \ + case 2: case 6: FAST_RFILL_TYPED(dst, size, pat, type_w); break; \ + default: FAST_RFILL_TYPED(dst, size, pat, type_c); break; \ + } + #endif diff --git a/qemu/roms/SLOF/include/ppcp7/cache.h b/qemu/roms/SLOF/include/ppcp7/cache.h index dc6837196..3c02bb10d 100644 --- a/qemu/roms/SLOF/include/ppcp7/cache.h +++ b/qemu/roms/SLOF/include/ppcp7/cache.h @@ -81,8 +81,8 @@ cache_inhibited_access(uint64_t, 64) #define _FASTMOVE(s, d, size) \ switch (((type_u)s | (type_u)d | size) & (sizeof(type_u)-1)) { \ case 0: _MOVE(s, d, size, type_u); break; \ - case sizeof(type_l): _MOVE(s, d, size, type_l); break; \ - case sizeof(type_w): _MOVE(s, d, size, type_w); break; \ + case 4: _MOVE(s, d, size, type_l); break; \ + case 2: case 6: _MOVE(s, d, size, type_w); break; \ default: _MOVE(s, d, size, type_c); break; \ } @@ -116,12 +116,17 @@ static inline void ci_rmove(void *dst, void *src, unsigned long esize, #define _FASTRMOVE(s, d, size) do { \ switch (((type_u)s | (type_u)d | size) & (sizeof(type_u)-1)) {\ case 0: ci_rmove(d,s,3,size>>3); break; \ - case sizeof(type_l): ci_rmove(d,s,2,size>>2); break; \ - case sizeof(type_w): ci_rmove(d,s,1,size>>1); break; \ + case 4: ci_rmove(d,s,2,size>>2); break; \ + case 2: case 6: ci_rmove(d,s,1,size>>1); break; \ default: ci_rmove(d,s,0,size); break; \ } \ } while(0) +#define FAST_MRMOVE(s, d, size) _FASTRMOVE(s, d, size) + +extern void fast_rfill(char *dst, long size, char pat); +#define FAST_RFILL(dst, size, pat) fast_rfill(dst, size, pat) + static inline uint16_t bswap16_load(uint64_t addr) { unsigned int val; diff --git a/qemu/roms/SLOF/lib/libc/include/stdlib.h b/qemu/roms/SLOF/lib/libc/include/stdlib.h index dff57f577..5e0eda9ff 100644 --- a/qemu/roms/SLOF/lib/libc/include/stdlib.h +++ b/qemu/roms/SLOF/lib/libc/include/stdlib.h @@ -29,5 +29,6 @@ unsigned long int strtoul(const char *nptr, char **endptr, int base); long int strtol(const char *nptr, char **endptr, int base); int rand(void); +void srand(unsigned int seed); #endif diff --git a/qemu/roms/SLOF/lib/libc/stdio/vsnprintf.c b/qemu/roms/SLOF/lib/libc/stdio/vsnprintf.c index e78fb3d8e..21dd04dfe 100644 --- a/qemu/roms/SLOF/lib/libc/stdio/vsnprintf.c +++ b/qemu/roms/SLOF/lib/libc/stdio/vsnprintf.c @@ -10,72 +10,110 @@ * IBM Corporation - initial implementation *****************************************************************************/ +#include #include "stdio.h" #include "stdlib.h" #include "string.h" +#include "ctype.h" -const static unsigned long long convert[] = { +static const unsigned long long convert[] = { 0x0, 0xFF, 0xFFFF, 0xFFFFFF, 0xFFFFFFFF, 0xFFFFFFFFFFULL, 0xFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL }; - - static int -print_itoa(char **buffer,unsigned long value, unsigned short int base) +print_str_fill(char **buffer, size_t bufsize, char *sizec, + const char *str, char c) { - const char zeichen[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; - static char sign = 0; - - if(base <= 2 || base > 16) - return 0; - - if(value < 0) { - sign = 1; - value *= -1; - } + int i, sizei, len; + char *bstart = *buffer; - if(value < base) { - if(sign) { - **buffer = '-'; + sizei = strtoul(sizec, NULL, 10); + len = strlen(str); + if (sizei > len) { + for (i = 0; + (i < (sizei - len)) && ((*buffer - bstart) < bufsize); + i++) { + **buffer = c; *buffer += 1; - sign = 0; } - **buffer = zeichen[value]; - *buffer += 1; - } else { - print_itoa(buffer, value / base, base); - **buffer = zeichen[(value % base)]; - *buffer += 1; } - return 1; } +static int +print_str(char **buffer, size_t bufsize, const char *str) +{ + char *bstart = *buffer; + size_t i; + + for (i = 0; (i < strlen(str)) && ((*buffer - bstart) < bufsize); i++) { + **buffer = str[i]; + *buffer += 1; + } + return 1; +} static unsigned int print_intlen(unsigned long value, unsigned short int base) { int i = 0; - while(value > 0) { + while (value > 0) { value /= base; i++; } - if(i == 0) i = 1; + if (i == 0) + i = 1; return i; } +static int +print_itoa(char **buffer, size_t bufsize, unsigned long value, + unsigned short base, bool upper) +{ + const char zeichen[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + char c; + size_t i, len; + + if(base <= 2 || base > 16) + return 0; + + len = i = print_intlen(value, base); + + /* Don't print to buffer if bufsize is not enough. */ + if (len > bufsize) + return 0; + + do { + c = zeichen[value % base]; + if (upper) + c = toupper(c); + + (*buffer)[--i] = c; + value /= base; + } while(value); + + *buffer += len; + + return 1; +} + + static int -print_fill(char **buffer, char *sizec, unsigned long size, unsigned short int base, char c, int optlen) +print_fill(char **buffer, size_t bufsize, char *sizec, unsigned long size, + unsigned short int base, char c, int optlen) { int i, sizei, len; + char *bstart = *buffer; sizei = strtoul(sizec, NULL, 10); len = print_intlen(size, base) + optlen; - if(sizei > len) { - for(i = 0; i < (sizei - len); i++) { + if (sizei > len) { + for (i = 0; + (i < (sizei - len)) && ((*buffer - bstart) < bufsize); + i++) { **buffer = c; *buffer += 1; } @@ -86,17 +124,18 @@ print_fill(char **buffer, char *sizec, unsigned long size, unsigned short int ba static int -print_format(char **buffer, const char *format, void *var) +print_format(char **buffer, size_t bufsize, const char *format, void *var) { - unsigned long start; - unsigned int i = 0, sizei = 0, len = 0, length_mod = sizeof(int); + char *start; + unsigned int i = 0, length_mod = sizeof(int); unsigned long value = 0; unsigned long signBit; char *form, sizec[32]; char sign = ' '; + bool upper = false; form = (char *) format; - start = (unsigned long) *buffer; + start = *buffer; form++; if(*form == '0' || *form == '.') { @@ -104,7 +143,7 @@ print_format(char **buffer, const char *format, void *var) form++; } - while(*form != '\0') { + while ((*form != '\0') && ((*buffer - start) < bufsize)) { switch(*form) { case 'u': case 'd': @@ -112,57 +151,59 @@ print_format(char **buffer, const char *format, void *var) sizec[i] = '\0'; value = (unsigned long) var; signBit = 0x1ULL << (length_mod * 8 - 1); - if (signBit & value) { + if ((*form != 'u') && (signBit & value)) { **buffer = '-'; *buffer += 1; value = (-(unsigned long)value) & convert[length_mod]; } - print_fill(buffer, sizec, value, 10, sign, 0); - print_itoa(buffer, value, 10); + print_fill(buffer, bufsize - (*buffer - start), + sizec, value, 10, sign, 0); + print_itoa(buffer, bufsize - (*buffer - start), + value, 10, upper); break; case 'X': + upper = true; case 'x': sizec[i] = '\0'; value = (unsigned long) var & convert[length_mod]; - print_fill(buffer, sizec, value, 16, sign, 0); - print_itoa(buffer, value, 16); + print_fill(buffer, bufsize - (*buffer - start), + sizec, value, 16, sign, 0); + print_itoa(buffer, bufsize - (*buffer - start), + value, 16, upper); break; case 'O': case 'o': sizec[i] = '\0'; value = (long int) var & convert[length_mod]; - print_fill(buffer, sizec, value, 8, sign, 0); - print_itoa(buffer, value, 8); + print_fill(buffer, bufsize - (*buffer - start), + sizec, value, 8, sign, 0); + print_itoa(buffer, bufsize - (*buffer - start), + value, 8, upper); break; case 'p': sizec[i] = '\0'; - print_fill(buffer, sizec, (unsigned long) var, 16, ' ', 2); - **buffer = '0'; - *buffer += 1; - **buffer = 'x'; - *buffer += 1; - print_itoa(buffer,(unsigned long) var, 16); + print_fill(buffer, bufsize - (*buffer - start), + sizec, (unsigned long) var, 16, ' ', 2); + print_str(buffer, bufsize - (*buffer - start), + "0x"); + print_itoa(buffer, bufsize - (*buffer - start), + (unsigned long) var, 16, upper); break; case 'c': sizec[i] = '\0'; - print_fill(buffer, sizec, 1, 10, ' ', 0); + print_fill(buffer, bufsize - (*buffer - start), + sizec, 1, 10, ' ', 0); **buffer = (unsigned long) var; *buffer += 1; break; case 's': sizec[i] = '\0'; - sizei = strtoul(sizec, NULL, 10); - len = strlen((char *) var); - if(sizei > len) { - for(i = 0; i < (sizei - len); i++) { - **buffer = ' '; - *buffer += 1; - } - } - for(i = 0; i < strlen((char *) var); i++) { - **buffer = ((char *) var)[i]; - *buffer += 1; - } + print_str_fill(buffer, + bufsize - (*buffer - start), sizec, + (char *) var, ' '); + + print_str(buffer, bufsize - (*buffer - start), + (char *) var); break; case 'l': form++; @@ -182,6 +223,9 @@ print_format(char **buffer, const char *format, void *var) length_mod = sizeof(short int); } break; + case 'z': + length_mod = sizeof(size_t); + break; default: if(*form >= '0' && *form <= '9') sizec[i++] = *form; @@ -206,6 +250,16 @@ vsnprintf(char *buffer, size_t bufsize, const char *format, va_list arg) bstart = buffer; ptr = (char *) format; + /* + * Return from here if size passed is zero, otherwise we would + * overrun buffer while setting NULL character at the end. + */ + if (!buffer || !bufsize) + return 0; + + /* Leave one space for NULL character */ + bufsize--; + while(*ptr != '\0' && (buffer - bstart) < bufsize) { if(*ptr == '%') { @@ -224,7 +278,9 @@ vsnprintf(char *buffer, size_t bufsize, const char *format, va_list arg) if(*ptr == '%') { *buffer++ = '%'; } else { - print_format(&buffer, formstr, va_arg(arg, void *)); + print_format(&buffer, + bufsize - (buffer - bstart), + formstr, va_arg(arg, void *)); } ptr++; } else { diff --git a/qemu/roms/SLOF/lib/libc/stdlib/rand.c b/qemu/roms/SLOF/lib/libc/stdlib/rand.c index 87e3efd29..39f5a9a2c 100644 --- a/qemu/roms/SLOF/lib/libc/stdlib/rand.c +++ b/qemu/roms/SLOF/lib/libc/stdlib/rand.c @@ -18,7 +18,12 @@ static unsigned long _rand = 1; int rand(void) { - _rand = _rand * 25364735 + 34563; + _rand = _rand * 1237732973 + 34563; - return ((unsigned int) (_rand << 16) & RAND_MAX); + return ((unsigned int) (_rand >> 16) & RAND_MAX); +} + +void srand(unsigned int seed) +{ + _rand = seed; } diff --git a/qemu/roms/SLOF/lib/libhvcall/Makefile b/qemu/roms/SLOF/lib/libhvcall/Makefile index 2a9b2d7d1..def532509 100644 --- a/qemu/roms/SLOF/lib/libhvcall/Makefile +++ b/qemu/roms/SLOF/lib/libhvcall/Makefile @@ -24,7 +24,7 @@ TARGET = ../libhvcall.a all: $(TARGET) -SRCS = brokensc1.c +SRCS = brokensc1.c rfill.c SRCSS = hvcall.S diff --git a/qemu/roms/SLOF/lib/libhvcall/rfill.c b/qemu/roms/SLOF/lib/libhvcall/rfill.c new file mode 100644 index 000000000..5407cd2a6 --- /dev/null +++ b/qemu/roms/SLOF/lib/libhvcall/rfill.c @@ -0,0 +1,38 @@ +/***************************************************************************** + * Fast function for filling cache-inhibited memory regions via h-call. + * + * Copyright 2015 Red Hat, Inc. + * + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * Thomas Huth, Red Hat Inc. - initial implementation + *****************************************************************************/ + +#include +#include + +typedef unsigned long type_u; + +/** + * fast_rfill is the implementation of the FAST_RFILL macro with h-calls. + * This is defined here instead of cache.h since we need a temporary + * local buffer - and that caused stack size problems in engine() when + * we used it directly in the FAST_RFILL macro. + */ +void fast_rfill(char *dst, long size, char pat) +{ + type_u buf[64]; + + memset(buf, pat, size < sizeof(buf) ? size : sizeof(buf)); + + while (size > sizeof(buf)) { + FAST_MRMOVE(buf, dst, sizeof(buf)); + dst += sizeof(buf); + size -= sizeof(buf); + } + FAST_MRMOVE(buf, dst, size); +} diff --git a/qemu/roms/SLOF/lib/libnvram/envvar.c b/qemu/roms/SLOF/lib/libnvram/envvar.c index 87aaf27a0..ee943fce5 100644 --- a/qemu/roms/SLOF/lib/libnvram/envvar.c +++ b/qemu/roms/SLOF/lib/libnvram/envvar.c @@ -17,7 +17,7 @@ #include "nvram.h" /* returns the offset of the first byte after the searched envvar */ -static int get_past_env_pos(partition_t part, char *envvar) +static int get_past_env_pos(partition_t part, char *envvar, int evlen) { int offset, len; static char temp[256]; @@ -32,7 +32,7 @@ static int get_past_env_pos(partition_t part, char *envvar) while((data=nvram_read_byte(offset++)) && len < 256) { temp[len++]=data; } - if (!strncmp(envvar, temp, strlen(envvar))) { + if (!strncmp(envvar, temp, evlen)) { return offset; } } while (len); @@ -43,16 +43,16 @@ static int get_past_env_pos(partition_t part, char *envvar) /** * @param partition name of the envvar partition * @param envvar name of the environment variable + * @param evlen string length of the envvar parameter * @return pointer to temporary string containing the value of envvar */ - -char *get_env(partition_t part, char *envvar) +char *nvram_get_env(partition_t part, char *envvar, int evlen) { static char temp[256+1]; int len, offset; uint8_t data; - DEBUG("get_env %s... ", envvar); + DEBUG("nvram_get_env %p... ", envvar); if(!part.addr) { /* ERROR: No environment variable partition */ DEBUG("invalid partition.\n"); @@ -68,7 +68,7 @@ char *get_env(partition_t part, char *envvar) } temp[len]=0; - if (!strncmp(envvar, temp, strlen(envvar))) { + if (!strncmp(envvar, temp, evlen)) { int pos=0; while (temp[pos]!='=' && pos < len) pos++; // DEBUG("value='%s'\n", temp+pos+1); @@ -100,7 +100,7 @@ static int find_last_envvar(partition_t part) return -1; } -int add_env(partition_t part, char *envvar, char *value) +int nvram_add_env(partition_t part, char *envvar, int evlen, char *value, int vallen) { int freespace, last, len, offset; unsigned int i; @@ -112,7 +112,7 @@ int add_env(partition_t part, char *envvar, char *value) freespace = part.addr+part.len-last; /* how long is the entry we want to write? */ - len = strlen(envvar) + strlen(value) + 2; + len = evlen + vallen + 2; if(freespaceenq; val = val + XHCI_TRB_SIZE; - size = seg->size * XHCI_TRB_SIZE; - /* TRBs being a cyclic buffer, here we cycle back to beginning. */ - if ((val % size) == 0) { + index = (enq - (uint64_t)seg->trbs) / XHCI_TRB_SIZE + 1; + dprintf("%s: enq %llx, val %llx %x\n", __func__, enq, val, index); + /* TRBs being a cyclic buffer, here we cycle back to beginning. */ + if (index == (seg->size - 1)) { + dprintf("%s: rounding \n", __func__); seg->enq = (uint64_t)seg->trbs; - enq = seg->enq; - seg->enq = seg->enq + XHCI_TRB_SIZE; - val = 0; seg->cycle_state ^= seg->cycle_state; link = (struct xhci_link_trb *) (seg->trbs + seg->size - 1); link->addr = cpu_to_le64(seg->trbs_dma); @@ -1105,6 +1147,12 @@ static inline void *xhci_get_trb(struct xhci_seg *seg) return (void *)enq; } +static uint64_t xhci_get_trb_phys(struct xhci_seg *seg, uint64_t trb) +{ + return seg->trbs_dma + (trb - (uint64_t)seg->trbs); +} + +static int usb_kb = false; static int xhci_transfer_bulk(struct usb_pipe *pipe, void *td, void *td_phys, void *data, int datalen) { @@ -1114,7 +1162,8 @@ static int xhci_transfer_bulk(struct usb_pipe *pipe, void *td, void *td_phys, struct xhci_transfer_trb *trb; struct xhci_db_regs *dbr; int ret = true; - uint32_t slot_id, epno; + uint32_t slot_id, epno, time; + uint64_t trb_phys, event_phys; if (!pipe->dev || !pipe->dev->hcidev) { dprintf(" NULL pointer\n"); @@ -1139,13 +1188,26 @@ static int xhci_transfer_bulk(struct usb_pipe *pipe, void *td, void *td_phys, } trb = xhci_get_trb(seg); + trb_phys = xhci_get_trb_phys(seg, (uint64_t)trb); fill_normal_trb(trb, (void *)data, datalen); epno = xhci_get_epno(pipe); write_reg32(&dbr->db[slot_id], epno); - if (!xhci_poll_event(xhcd, 0)) { - dprintf("Bulk failed\n"); - ret = false; + + time = SLOF_GetTimer() + USB_TIMEOUT; + while (1) { + event_phys = xhci_poll_event(xhcd, 0); + if (event_phys == trb_phys) { + break; + } else if (event_phys == 0) { /* polling timed out */ + ret = false; + break; + } else + usb_kb = true; + + /* transfer timed out */ + if (time < SLOF_GetTimer()) + return false; } trb->addr = 0; trb->len = 0; @@ -1214,7 +1276,8 @@ static void xhci_init_bulk_ep(struct usb_dev *dev, struct usb_pipe *pipe) if (!seg->trbs) { if (!xhci_alloc_seg(seg, XHCI_DATA_TRBS_SIZE, TYPE_BULK)) { - dprintf("Failed allocating seg\n"); + printf("usb-xhci: allocation failed for bulk endpoint\n"); + return; } } else { xhci_init_seg(seg, XHCI_DATA_TRBS_SIZE, TYPE_BULK); @@ -1235,6 +1298,61 @@ static void xhci_init_bulk_ep(struct usb_dev *dev, struct usb_pipe *pipe) xpipe->seg = seg; } +static int xhci_get_pipe_intr(struct usb_pipe *pipe, + struct xhci_hcd *xhcd, + char *buf, size_t len) +{ + struct xhci_dev *xdev; + struct xhci_seg *seg; + struct xhci_pipe *xpipe; + struct xhci_control_ctx *ctrl; + struct xhci_ep_ctx *ep; + uint32_t x_epno, val, type; + struct usb_dev *dev; + struct xhci_transfer_trb *trb; + + dev = pipe->dev; + if (dev->class != DEV_HID_KEYB) + return false; + + xdev = dev->priv; + pipe->mps = 8; + seg = xhci_pipe_get_seg(pipe); + xpipe = xhci_pipe_get_xpipe(pipe); + type = EP_INT_IN; + seg = &xdev->intr; + + if (!seg->trbs) { + if (!xhci_alloc_seg(seg, XHCI_INTR_TRBS_SIZE, TYPE_BULK)) { + printf("usb-xhci: allocation failed for interrupt endpoint\n"); + return false; + } + } else { + xhci_init_seg(seg, XHCI_EVENT_TRBS_SIZE, TYPE_BULK); + } + + xpipe->buf = buf; + xpipe->buf_phys = SLOF_dma_map_in(buf, len, false); + xpipe->buflen = len; + + ctrl = xhci_get_control_ctx(&xdev->in_ctx); + x_epno = xhci_get_epno(pipe); + ep = xhci_get_ep_ctx(&xdev->in_ctx, xdev->ctx_size, x_epno); + val = EP_TYPE(type) | MAX_BURST(0) | ERROR_COUNT(3) | + MAX_PACKET_SIZE(pipe->mps); + ep->field2 = cpu_to_le32(val); + ep->deq_addr = cpu_to_le64(seg->trbs_dma | seg->cycle_state); + ep->field4 = cpu_to_le32(8); + ctrl->a_flags = cpu_to_le32(BIT(x_epno) | 0x1); + ctrl->d_flags = 0; + xhci_configure_ep(xhcd, xdev->slot_id, xdev->in_ctx.dma_addr); + xpipe->seg = seg; + + trb = xhci_get_trb(seg); + fill_normal_trb(trb, (void *)xpipe->buf_phys, pipe->mps); + return true; +} + static struct usb_pipe* xhci_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep, char *buf, size_t len) { struct xhci_hcd *xhcd; @@ -1264,6 +1382,12 @@ static struct usb_pipe* xhci_get_pipe(struct usb_dev *dev, struct usb_ep_descr * new->dir = (ep->bEndpointAddress & 0x80) >> 7; new->epno = ep->bEndpointAddress & 0x0f; + if (new->type == USB_EP_TYPE_INTR) { + if (!xhci_get_pipe_intr(new, xhcd, buf, len)) { + printf("usb-xhci: %s alloc_intr failed %p\n", + __func__, new); + } + } if (new->type == USB_EP_TYPE_BULK) xhci_init_bulk_ep(dev, new); @@ -1284,6 +1408,10 @@ static void xhci_put_pipe(struct usb_pipe *pipe) if (pipe->type == USB_EP_TYPE_BULK) { xpipe = xhci_pipe_get_xpipe(pipe); xpipe->seg = NULL; + } else if (pipe->type == USB_EP_TYPE_INTR) { + xpipe = xhci_pipe_get_xpipe(pipe); + SLOF_dma_map_out(xpipe->buf_phys, xpipe->buf, xpipe->buflen); + xpipe->seg = NULL; } if (xhcd->end) xhcd->end->next = pipe; @@ -1298,6 +1426,51 @@ static void xhci_put_pipe(struct usb_pipe *pipe) dprintf("usb-xhci: %s exit\n", __func__); } +static int xhci_poll_intr(struct usb_pipe *pipe, uint8_t *data) +{ + struct xhci_transfer_trb *trb; + struct xhci_seg *seg; + struct xhci_pipe *xpipe; + struct xhci_dev *xdev; + struct xhci_hcd *xhcd; + struct xhci_db_regs *dbr; + uint32_t x_epno; + uint8_t *buf, ret = 1; + + if (!pipe || !pipe->dev || !pipe->dev->hcidev) + return 0; + xdev = pipe->dev->priv; + xhcd = (struct xhci_hcd *)pipe->dev->hcidev->priv; + x_epno = xhci_get_epno(pipe); + seg = xhci_pipe_get_seg(pipe); + xpipe = xhci_pipe_get_xpipe(pipe); + + if (usb_kb == true) { + /* This event was consumed by bulk transfer */ + usb_kb = false; + goto skip_poll; + } + buf = xpipe->buf; + memset(buf, 0, 8); + + mb(); + /* Ring the doorbell - x_epno */ + dbr = xhcd->db_regs; + write_reg32(&dbr->db[xdev->slot_id], x_epno); + if (!xhci_poll_event(xhcd, 0)) { + printf("poll intr failed\n"); + return 0; + } + mb(); + memcpy(data, buf, 8); + +skip_poll: + trb = xhci_get_trb(seg); + fill_normal_trb(trb, (void *)xpipe->buf_phys, pipe->mps); + mb(); + return ret; +} + struct usb_hcd_ops xhci_ops = { .name = "xhci-hcd", .init = xhci_init, @@ -1305,6 +1478,7 @@ struct usb_hcd_ops xhci_ops = { .usb_type = USB_XHCI, .get_pipe = xhci_get_pipe, .put_pipe = xhci_put_pipe, + .poll_intr = xhci_poll_intr, .send_ctrl = xhci_send_ctrl, .transfer_bulk = xhci_transfer_bulk, .next = NULL, diff --git a/qemu/roms/SLOF/lib/libusb/usb-xhci.h b/qemu/roms/SLOF/lib/libusb/usb-xhci.h index faeb07ead..3fc7e7889 100644 --- a/qemu/roms/SLOF/lib/libusb/usb-xhci.h +++ b/qemu/roms/SLOF/lib/libusb/usb-xhci.h @@ -266,6 +266,7 @@ struct xhci_seg { #define XHCI_EVENT_TRBS_SIZE 4096 #define XHCI_CONTROL_TRBS_SIZE 4096 #define XHCI_DATA_TRBS_SIZE 4096 +#define XHCI_INTR_TRBS_SIZE 4096 #define XHCI_ERST_NUM_SEGS 1 #define XHCI_MAX_BULK_SIZE 0xF000 @@ -349,6 +350,7 @@ struct xhci_dev { struct xhci_ctx in_ctx; struct xhci_ctx out_ctx; struct xhci_seg control; + struct xhci_seg intr; struct xhci_seg bulk_in; struct xhci_seg bulk_out; uint32_t ctx_size; @@ -381,6 +383,9 @@ struct xhci_hcd { struct xhci_pipe { struct usb_pipe pipe; struct xhci_seg *seg; + void *buf; + long buf_phys; + uint32_t buflen; }; #endif /* USB_XHCI_H */ diff --git a/qemu/roms/SLOF/lib/libvirtio/p9.c b/qemu/roms/SLOF/lib/libvirtio/p9.c index a55662994..0e5953031 100644 --- a/qemu/roms/SLOF/lib/libvirtio/p9.c +++ b/qemu/roms/SLOF/lib/libvirtio/p9.c @@ -143,7 +143,7 @@ int p9_transaction(p9_connection_t *connection) { int rc; int tx_size = GET_SIZE; - int rx_size = connection->message_size; + uint32_t rx_size = connection->message_size; if (transact == NULL) { return P9_NO_TRANSPORT; diff --git a/qemu/roms/SLOF/lib/libvirtio/p9.h b/qemu/roms/SLOF/lib/libvirtio/p9.h index 7df9ef441..3a35e80ed 100644 --- a/qemu/roms/SLOF/lib/libvirtio/p9.h +++ b/qemu/roms/SLOF/lib/libvirtio/p9.h @@ -33,7 +33,7 @@ #define P9_PARTIAL_WALK 1 typedef int (*p9_transact_t)(void *opaque, uint8_t *tx, int tx_size, - uint8_t *rx, int *rx_size); + uint8_t *rx, uint32_t *rx_size); typedef struct { uint32_t message_size; diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-9p.c b/qemu/roms/SLOF/lib/libvirtio/virtio-9p.c index 5a5fd01da..fc5db9154 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio-9p.c +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-9p.c @@ -19,6 +19,7 @@ #include "virtio-9p.h" #include "p9.h" +static struct vqs vq; /** * Notes for 9P Server config: @@ -86,7 +87,7 @@ static void dprint_buffer(const char *name, uint8_t *buffer, int length) * @return 0 = success, -ve = error. */ static int virtio_9p_transact(void *opaque, uint8_t *tx, int tx_size, uint8_t *rx, - int *rx_size) + uint32_t *rx_size) { struct virtio_device *dev = opaque; struct vring_desc *desc; @@ -165,6 +166,7 @@ int virtio_9p_init(struct virtio_device *dev, void *tx_buf, void *rx_buf, int buf_size) { struct vring_avail *vq_avail; + int status = VIRTIO_STAT_ACKNOWLEDGE; /* Check for double open */ if (__buf_size) @@ -174,28 +176,31 @@ int virtio_9p_init(struct virtio_device *dev, void *tx_buf, void *rx_buf, dprintf("%s : device at %p\n", __func__, dev->base); dprintf("%s : type is %04x\n", __func__, dev->type); - /* Reset device */ - // XXX That will clear the virtq base. We need to move - // initializing it to here anyway - // - // virtio_reset_device(dev); + /* Keep it disabled until the driver is 1.0 capable */ + dev->is_modern = false; + + virtio_reset_device(dev); /* Acknowledge device. */ - virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE); + virtio_set_status(dev, status); /* Tell HV that we know how to drive the device. */ - virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE | VIRTIO_STAT_DRIVER); + status |= VIRTIO_STAT_DRIVER; + virtio_set_status(dev, status); /* Device specific setup - we do not support special features */ virtio_set_guest_features(dev, 0); + if (virtio_queue_init_vq(dev, &vq, 0)) + goto dev_error; + vq_avail = virtio_get_vring_avail(dev, 0); vq_avail->flags = VRING_AVAIL_F_NO_INTERRUPT; vq_avail->idx = 0; /* Tell HV that setup succeeded */ - virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE | VIRTIO_STAT_DRIVER - |VIRTIO_STAT_DRIVER_OK); + status |= VIRTIO_STAT_DRIVER_OK; + virtio_set_status(dev, status); /* Setup 9P library. */ p9_reg_transport(virtio_9p_transact, dev,(uint8_t *)tx_buf, @@ -203,6 +208,12 @@ int virtio_9p_init(struct virtio_device *dev, void *tx_buf, void *rx_buf, dprintf("%s : complete\n", __func__); return 0; + +dev_error: + printf("%s: failed\n", __func__); + status |= VIRTIO_STAT_FAILED; + virtio_set_status(dev, status); + return -1; } /** @@ -228,7 +239,7 @@ void virtio_9p_shutdown(struct virtio_device *dev) * @param buffer[out] Where to read the file to. * @return +ve = amount of data read, -ve = error. */ -int virtio_9p_load(struct virtio_device *dev, const char *file_name, uint8_t *buffer) +long virtio_9p_load(struct virtio_device *dev, const char *file_name, uint8_t *buffer) { int rc; uint16_t tag_len; @@ -332,5 +343,5 @@ cleanup_connection: dprintf("%s : complete, read %llu bytes\n", __func__, offset); - return rc == 0 ? offset : rc; + return rc == 0 ? (long)offset : rc; } diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-9p.h b/qemu/roms/SLOF/lib/libvirtio/virtio-9p.h index 4bf47d078..db2cf6f11 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio-9p.h +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-9p.h @@ -26,7 +26,7 @@ typedef struct { int virtio_9p_init(struct virtio_device *dev, void *tx_buf, void *rx_buf, int buf_size); void virtio_9p_shutdown(struct virtio_device *dev); -int virtio_9p_load(struct virtio_device *dev, const char *file_name, uint8_t *buffer); +long virtio_9p_load(struct virtio_device *dev, const char *file_name, uint8_t *buffer); #endif /* VIRTIO_9P_H_ */ diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-blk.c b/qemu/roms/SLOF/lib/libvirtio/virtio-blk.c index 826f2ea0e..07ec1048f 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio-blk.c +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-blk.c @@ -13,10 +13,15 @@ #include #include #include +#include #include "virtio.h" #include "virtio-blk.h" +#include "virtio-internal.h" #define DEFAULT_SECTOR_SIZE 512 +#define DRIVER_FEATURE_SUPPORT (VIRTIO_BLK_F_BLK_SIZE | VIRTIO_F_VERSION_1) + +static struct vqs vq; /** * Initialize virtio-block device. @@ -27,39 +32,54 @@ virtioblk_init(struct virtio_device *dev) { struct vring_avail *vq_avail; int blk_size = DEFAULT_SECTOR_SIZE; - int features; + uint64_t features; + int status = VIRTIO_STAT_ACKNOWLEDGE; /* Reset device */ - // XXX That will clear the virtq base. We need to move - // initializing it to here anyway - // - // virtio_reset_device(dev); + virtio_reset_device(dev); /* Acknowledge device. */ - virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE); + virtio_set_status(dev, status); /* Tell HV that we know how to drive the device. */ - virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER); + status |= VIRTIO_STAT_DRIVER; + virtio_set_status(dev, status); + + if (dev->is_modern) { + /* Negotiate features and sets FEATURES_OK if successful */ + if (virtio_negotiate_guest_features(dev, DRIVER_FEATURE_SUPPORT)) + goto dev_error; + + virtio_get_status(dev, &status); + } else { + /* Device specific setup - we support F_BLK_SIZE */ + virtio_set_guest_features(dev, VIRTIO_BLK_F_BLK_SIZE); + } - /* Device specific setup - we support F_BLK_SIZE */ - virtio_set_guest_features(dev, VIRTIO_BLK_F_BLK_SIZE); + if (virtio_queue_init_vq(dev, &vq, 0)) + goto dev_error; vq_avail = virtio_get_vring_avail(dev, 0); - vq_avail->flags = VRING_AVAIL_F_NO_INTERRUPT; + vq_avail->flags = virtio_cpu_to_modern16(dev, VRING_AVAIL_F_NO_INTERRUPT); vq_avail->idx = 0; /* Tell HV that setup succeeded */ - virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER - |VIRTIO_STAT_DRIVER_OK); + status |= VIRTIO_STAT_DRIVER_OK; + virtio_set_status(dev, status); - virtio_get_host_features(dev, &features); + features = virtio_get_host_features(dev); if (features & VIRTIO_BLK_F_BLK_SIZE) { blk_size = virtio_get_config(dev, - offset_of(struct virtio_blk_cfg, blk_size), - sizeof(blk_size)); + offset_of(struct virtio_blk_cfg, blk_size), + sizeof(blk_size)); } return blk_size; +dev_error: + printf("%s: failed\n", __func__); + status |= VIRTIO_STAT_FAILED; + virtio_set_status(dev, status); + return 0; } @@ -77,6 +97,19 @@ virtioblk_shutdown(struct virtio_device *dev) virtio_reset_device(dev); } +static void fill_blk_hdr(struct virtio_blk_req *blkhdr, bool is_modern, + uint32_t type, uint32_t ioprio, uint32_t sector) +{ + if (is_modern) { + blkhdr->type = cpu_to_le32(type); + blkhdr->ioprio = cpu_to_le32(ioprio); + blkhdr->sector = cpu_to_le64(sector); + } else { + blkhdr->type = type; + blkhdr->ioprio = ioprio; + blkhdr->sector = sector; + } +} /** * Read blocks @@ -87,7 +120,7 @@ virtioblk_shutdown(struct virtio_device *dev) * @return number of blocks that have been read successfully */ int -virtioblk_read(struct virtio_device *dev, char *buf, long blocknum, long cnt) +virtioblk_read(struct virtio_device *dev, char *buf, uint64_t blocknum, long cnt) { struct vring_desc *desc; int id; @@ -100,7 +133,7 @@ virtioblk_read(struct virtio_device *dev, char *buf, long blocknum, long cnt) struct vring_used *vq_used; /* "Used" vring */ volatile uint8_t status = -1; volatile uint16_t *current_used_idx; - uint16_t last_used_idx; + uint16_t last_used_idx, avail_idx; int blk_size = DEFAULT_SECTOR_SIZE; //printf("virtioblk_read: dev=%p buf=%p blocknum=%li count=%li\n", @@ -128,41 +161,38 @@ virtioblk_read(struct virtio_device *dev, char *buf, long blocknum, long cnt) vq_avail = virtio_get_vring_avail(dev, 0); vq_used = virtio_get_vring_used(dev, 0); + avail_idx = virtio_modern16_to_cpu(dev, vq_avail->idx); + last_used_idx = vq_used->idx; current_used_idx = &vq_used->idx; /* Set up header */ - blkhdr.type = VIRTIO_BLK_T_IN | VIRTIO_BLK_T_BARRIER; - blkhdr.ioprio = 1; - blkhdr.sector = blocknum * blk_size / DEFAULT_SECTOR_SIZE; + fill_blk_hdr(&blkhdr, dev->is_modern, VIRTIO_BLK_T_IN | VIRTIO_BLK_T_BARRIER, + 1, blocknum * blk_size / DEFAULT_SECTOR_SIZE); /* Determine descriptor index */ - id = (vq_avail->idx * 3) % vq_size; + id = (avail_idx * 3) % vq_size; /* Set up virtqueue descriptor for header */ desc = &vq_desc[id]; - desc->addr = (uint64_t)&blkhdr; - desc->len = sizeof(struct virtio_blk_req); - desc->flags = VRING_DESC_F_NEXT; - desc->next = (id + 1) % vq_size; + virtio_fill_desc(desc, dev->is_modern, (uint64_t)&blkhdr, + sizeof(struct virtio_blk_req), + VRING_DESC_F_NEXT, (id + 1) % vq_size); /* Set up virtqueue descriptor for data */ desc = &vq_desc[(id + 1) % vq_size]; - desc->addr = (uint64_t)buf; - desc->len = cnt * blk_size; - desc->flags = VRING_DESC_F_NEXT | VRING_DESC_F_WRITE; - desc->next = (id + 2) % vq_size; + virtio_fill_desc(desc, dev->is_modern, (uint64_t)buf, cnt * blk_size, + VRING_DESC_F_NEXT | VRING_DESC_F_WRITE, + (id + 2) % vq_size); /* Set up virtqueue descriptor for status */ desc = &vq_desc[(id + 2) % vq_size]; - desc->addr = (uint64_t)&status; - desc->len = 1; - desc->flags = VRING_DESC_F_WRITE; - desc->next = 0; + virtio_fill_desc(desc, dev->is_modern, (uint64_t)&status, 1, + VRING_DESC_F_WRITE, 0); - vq_avail->ring[vq_avail->idx % vq_size] = id; + vq_avail->ring[avail_idx % vq_size] = virtio_cpu_to_modern16 (dev, id); mb(); - vq_avail->idx += 1; + vq_avail->idx = virtio_cpu_to_modern16(dev, avail_idx + 1); /* Tell HV that the queue is ready */ virtio_queue_notify(dev, 0); diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-blk.h b/qemu/roms/SLOF/lib/libvirtio/virtio-blk.h index ac8bf2896..2e7b5926b 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio-blk.h +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-blk.h @@ -55,6 +55,6 @@ struct virtio_blk_req { extern int virtioblk_init(struct virtio_device *dev); extern void virtioblk_shutdown(struct virtio_device *dev); -extern int virtioblk_read(struct virtio_device *dev, char *buf, long blocknum, long cnt); +extern int virtioblk_read(struct virtio_device *dev, char *buf, uint64_t blocknum, long cnt); #endif /* _VIRTIO_BLK_H */ diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-internal.h b/qemu/roms/SLOF/lib/libvirtio/virtio-internal.h new file mode 100644 index 000000000..08662eab7 --- /dev/null +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-internal.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * Copyright (c) 2016 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _LIBVIRTIO_INTERNAL_H +#define _LIBVIRTIO_INTERNAL_H + +#include + +static inline uint16_t virtio_cpu_to_modern16(struct virtio_device *dev, uint16_t val) +{ + return dev->is_modern ? cpu_to_le16(val) : val; +} + +static inline uint32_t virtio_cpu_to_modern32(struct virtio_device *dev, uint32_t val) +{ + return dev->is_modern ? cpu_to_le32(val) : val; +} + +static inline uint64_t virtio_cpu_to_modern64(struct virtio_device *dev, uint64_t val) +{ + return dev->is_modern ? cpu_to_le64(val) : val; +} + +static inline uint16_t virtio_modern16_to_cpu(struct virtio_device *dev, uint16_t val) +{ + return dev->is_modern ? le16_to_cpu(val) : val; +} + +static inline uint32_t virtio_modern32_to_cpu(struct virtio_device *dev, uint32_t val) +{ + return dev->is_modern ? le32_to_cpu(val) : val; +} + +static inline uint64_t virtio_modern64_to_cpu(struct virtio_device *dev, uint64_t val) +{ + return dev->is_modern ? le64_to_cpu(val) : val; +} + +#endif /* _LIBVIRTIO_INTERNAL_H */ diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-net.c b/qemu/roms/SLOF/lib/libvirtio/virtio-net.c index 99c19d952..fc620a201 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio-net.c +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-net.c @@ -26,6 +26,7 @@ #include #include "virtio.h" #include "virtio-net.h" +#include "virtio-internal.h" #undef DEBUG //#define DEBUG @@ -37,22 +38,13 @@ #define sync() asm volatile (" sync \n" ::: "memory") -/* PCI virtio header offsets */ -#define VIRTIOHDR_DEVICE_FEATURES 0 -#define VIRTIOHDR_GUEST_FEATURES 4 -#define VIRTIOHDR_QUEUE_ADDRESS 8 -#define VIRTIOHDR_QUEUE_SIZE 12 -#define VIRTIOHDR_QUEUE_SELECT 14 -#define VIRTIOHDR_QUEUE_NOTIFY 16 -#define VIRTIOHDR_DEVICE_STATUS 18 -#define VIRTIOHDR_ISR_STATUS 19 -#define VIRTIOHDR_DEVICE_CONFIG 20 -#define VIRTIOHDR_MAC_ADDRESS 20 +#define DRIVER_FEATURE_SUPPORT (VIRTIO_NET_F_MAC | VIRTIO_F_VERSION_1) struct virtio_device virtiodev; -struct vqs vq[2]; /* Information about virtqueues */ +static struct vqs vq_rx; /* Information about receive virtqueues */ +static struct vqs vq_tx; /* Information about transmit virtqueues */ -/* See Virtio Spec, appendix C, "Device Operation" */ +/* See Virtio Spec, appendix C, "Device Operation" */ struct virtio_net_hdr { uint8_t flags; uint8_t gso_type; @@ -63,6 +55,18 @@ struct virtio_net_hdr { // uint16_t num_buffers; /* Only if VIRTIO_NET_F_MRG_RXBUF */ }; +static unsigned int net_hdr_size; + +struct virtio_net_hdr_v1 { + uint8_t flags; + uint8_t gso_type; + le16 hdr_len; + le16 gso_size; + le16 csum_start; + le16 csum_offset; + le16 num_buffers; +}; + static uint16_t last_rx_idx; /* Last index in RX "used" ring */ /** @@ -72,15 +76,13 @@ static uint16_t last_rx_idx; /* Last index in RX "used" ring */ */ static int virtionet_init_pci(struct virtio_device *dev) { - int i; - dprintf("virtionet: doing virtionet_init_pci!\n"); if (!dev) return -1; - virtiodev.base = dev->base; - virtiodev.type = dev->type; + /* make a copy of the device structure */ + memcpy(&virtiodev, dev, sizeof(struct virtio_device)); /* Reset device */ virtio_reset_device(&virtiodev); @@ -90,29 +92,11 @@ static int virtionet_init_pci(struct virtio_device *dev) * second the transmit queue, and the forth is the control queue for * networking options. * We are only interested in the receive and transmit queue here. */ - - for (i=VQ_RX; i<=VQ_TX; i++) { - /* Select ring (0=RX, 1=TX): */ - vq[i].id = i-VQ_RX; - ci_write_16(virtiodev.base+VIRTIOHDR_QUEUE_SELECT, - cpu_to_le16(vq[i].id)); - - vq[i].size = le16_to_cpu(ci_read_16(virtiodev.base+VIRTIOHDR_QUEUE_SIZE)); - vq[i].desc = SLOF_alloc_mem_aligned(virtio_vring_size(vq[i].size), 4096); - if (!vq[i].desc) { - printf("memory allocation failed!\n"); - return -1; - } - memset(vq[i].desc, 0, virtio_vring_size(vq[i].size)); - ci_write_32(virtiodev.base+VIRTIOHDR_QUEUE_ADDRESS, - cpu_to_le32((long)vq[i].desc / 4096)); - vq[i].avail = (void*)vq[i].desc - + vq[i].size * sizeof(struct vring_desc); - vq[i].used = (void*)VQ_ALIGN((long)vq[i].avail - + vq[i].size * sizeof(struct vring_avail)); - - dprintf("%i: vq.id = %llx\nvq.size =%x\n vq.avail =%p\nvq.used=%p\n", - i, vq[i].id, vq[i].size, vq[i].avail, vq[i].used); + if (virtio_queue_init_vq(dev, &vq_rx, VQ_RX) || + virtio_queue_init_vq(dev, &vq_tx, VQ_TX)) { + virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER + |VIRTIO_STAT_FAILED); + return -1; } /* Acknowledge device. */ @@ -129,6 +113,7 @@ static int virtionet_init_pci(struct virtio_device *dev) static int virtionet_init(net_driver_t *driver) { int i; + int status = VIRTIO_STAT_ACKNOWLEDGE | VIRTIO_STAT_DRIVER; dprintf("virtionet_init(%02x:%02x:%02x:%02x:%02x:%02x)\n", driver->mac_addr[0], driver->mac_addr[1], @@ -139,60 +124,69 @@ static int virtionet_init(net_driver_t *driver) return 0; /* Tell HV that we know how to drive the device. */ - virtio_set_status(&virtiodev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER); - - /* Device specific setup - we do not support special features right now */ - virtio_set_guest_features(&virtiodev, 0); + virtio_set_status(&virtiodev, status); + + /* Device specific setup */ + if (virtiodev.is_modern) { + if (virtio_negotiate_guest_features(&virtiodev, DRIVER_FEATURE_SUPPORT)) + goto dev_error; + net_hdr_size = sizeof(struct virtio_net_hdr_v1); + virtio_get_status(&virtiodev, &status); + } else { + net_hdr_size = sizeof(struct virtio_net_hdr); + virtio_set_guest_features(&virtiodev, 0); + } /* Allocate memory for one transmit an multiple receive buffers */ - vq[VQ_RX].buf_mem = SLOF_alloc_mem((BUFFER_ENTRY_SIZE+sizeof(struct virtio_net_hdr)) + vq_rx.buf_mem = SLOF_alloc_mem((BUFFER_ENTRY_SIZE+net_hdr_size) * RX_QUEUE_SIZE); - if (!vq[VQ_RX].buf_mem) { + if (!vq_rx.buf_mem) { printf("virtionet: Failed to allocate buffers!\n"); - virtio_set_status(&virtiodev, VIRTIO_STAT_FAILED); - return -1; + goto dev_error; } /* Prepare receive buffer queue */ for (i = 0; i < RX_QUEUE_SIZE; i++) { - struct vring_desc *desc; + uint64_t addr = (uint64_t)vq_rx.buf_mem + + i * (BUFFER_ENTRY_SIZE+net_hdr_size); + uint32_t id = i*2; /* Descriptor for net_hdr: */ - desc = &vq[VQ_RX].desc[i*2]; - desc->addr = (uint64_t)vq[VQ_RX].buf_mem - + i * (BUFFER_ENTRY_SIZE+sizeof(struct virtio_net_hdr)); - desc->len = sizeof(struct virtio_net_hdr); - desc->flags = VRING_DESC_F_NEXT | VRING_DESC_F_WRITE; - desc->next = i*2+1; + virtio_fill_desc(&vq_rx.desc[id], virtiodev.is_modern, addr, net_hdr_size, + VRING_DESC_F_NEXT | VRING_DESC_F_WRITE, id + 1); /* Descriptor for data: */ - desc = &vq[VQ_RX].desc[i*2+1]; - desc->addr = vq[VQ_RX].desc[i*2].addr + sizeof(struct virtio_net_hdr); - desc->len = BUFFER_ENTRY_SIZE; - desc->flags = VRING_DESC_F_WRITE; - desc->next = 0; + virtio_fill_desc(&vq_rx.desc[id+1], virtiodev.is_modern, addr + net_hdr_size, + BUFFER_ENTRY_SIZE, VRING_DESC_F_WRITE, 0); - vq[VQ_RX].avail->ring[i] = i*2; + vq_rx.avail->ring[i] = virtio_cpu_to_modern16(&virtiodev, id); } sync(); - vq[VQ_RX].avail->flags = VRING_AVAIL_F_NO_INTERRUPT; - vq[VQ_RX].avail->idx = RX_QUEUE_SIZE; - last_rx_idx = vq[VQ_RX].used->idx; + vq_rx.avail->flags = virtio_cpu_to_modern16(&virtiodev, VRING_AVAIL_F_NO_INTERRUPT); + vq_rx.avail->idx = virtio_cpu_to_modern16(&virtiodev, RX_QUEUE_SIZE); + + last_rx_idx = virtio_modern16_to_cpu(&virtiodev, vq_rx.used->idx); - vq[VQ_TX].avail->flags = VRING_AVAIL_F_NO_INTERRUPT; - vq[VQ_TX].avail->idx = 0; + vq_tx.avail->flags = virtio_cpu_to_modern16(&virtiodev, VRING_AVAIL_F_NO_INTERRUPT); + vq_tx.avail->idx = 0; /* Tell HV that setup succeeded */ - virtio_set_status(&virtiodev, VIRTIO_STAT_ACKNOWLEDGE - |VIRTIO_STAT_DRIVER - |VIRTIO_STAT_DRIVER_OK); + status |= VIRTIO_STAT_DRIVER_OK; + virtio_set_status(&virtiodev, status); /* Tell HV that RX queues are ready */ virtio_queue_notify(&virtiodev, VQ_RX); driver->running = 1; - + for(i = 0; i < (int)sizeof(driver->mac_addr); i++) { + driver->mac_addr[i] = virtio_get_config(&virtiodev, i, 1); + } return 0; + +dev_error: + status |= VIRTIO_STAT_FAILED; + virtio_set_status(&virtiodev, status); + return -1; } @@ -225,9 +219,10 @@ static int virtionet_term(net_driver_t *driver) */ static int virtionet_xmit(char *buf, int len) { - struct vring_desc *desc; - int id; - static struct virtio_net_hdr nethdr; + int id, idx; + static struct virtio_net_hdr_v1 nethdr_v1; + static struct virtio_net_hdr nethdr_legacy; + void *nethdr = &nethdr_legacy; if (len > BUFFER_ENTRY_SIZE) { printf("virtionet: Packet too big!\n"); @@ -236,28 +231,25 @@ static int virtionet_xmit(char *buf, int len) dprintf("\nvirtionet_xmit(packet at %p, %d bytes)\n", buf, len); - memset(&nethdr, 0, sizeof(nethdr)); + if (virtiodev.is_modern) + nethdr = &nethdr_v1; + + memset(nethdr, 0, net_hdr_size); /* Determine descriptor index */ - id = (vq[VQ_TX].avail->idx * 2) % vq[VQ_TX].size; + idx = virtio_modern16_to_cpu(&virtiodev, vq_tx.avail->idx); + id = (idx * 2) % vq_tx.size; /* Set up virtqueue descriptor for header */ - desc = &vq[VQ_TX].desc[id]; - desc->addr = (uint64_t)&nethdr; - desc->len = sizeof(struct virtio_net_hdr); - desc->flags = VRING_DESC_F_NEXT; - desc->next = id + 1; + virtio_fill_desc(&vq_tx.desc[id], virtiodev.is_modern, (uint64_t)nethdr, + net_hdr_size, VRING_DESC_F_NEXT, id + 1); /* Set up virtqueue descriptor for data */ - desc = &vq[VQ_TX].desc[id+1]; - desc->addr = (uint64_t)buf; - desc->len = len; - desc->flags = 0; - desc->next = 0; + virtio_fill_desc(&vq_tx.desc[id+1], virtiodev.is_modern, (uint64_t)buf, len, 0, 0); - vq[VQ_TX].avail->ring[vq[VQ_TX].avail->idx % vq[VQ_TX].size] = id; + vq_tx.avail->ring[idx % vq_tx.size] = virtio_cpu_to_modern16(&virtiodev, id); sync(); - vq[VQ_TX].avail->idx += 1; + vq_tx.avail->idx = virtio_cpu_to_modern16(&virtiodev, idx + 1); sync(); /* Tell HV that TX queue is ready */ @@ -272,23 +264,24 @@ static int virtionet_xmit(char *buf, int len) */ static int virtionet_receive(char *buf, int maxlen) { - int len = 0; - int id; + uint32_t len = 0; + uint32_t id, idx; - if (last_rx_idx == vq[VQ_RX].used->idx) { + idx = virtio_modern16_to_cpu(&virtiodev, vq_rx.used->idx); + + if (last_rx_idx == idx) { /* Nothing received yet */ return 0; } - id = (vq[VQ_RX].used->ring[last_rx_idx % vq[VQ_RX].size].id + 1) - % vq[VQ_RX].size; - len = vq[VQ_RX].used->ring[last_rx_idx % vq[VQ_RX].size].len - - sizeof(struct virtio_net_hdr); - - dprintf("virtionet_receive() last_rx_idx=%i, vq[VQ_RX].used->idx=%i," - " id=%i len=%i\n", last_rx_idx, vq[VQ_RX].used->idx, id, len); + id = (virtio_modern32_to_cpu(&virtiodev, vq_rx.used->ring[last_rx_idx % vq_rx.size].id) + 1) + % vq_rx.size; + len = virtio_modern32_to_cpu(&virtiodev, vq_rx.used->ring[last_rx_idx % vq_rx.size].len) + - net_hdr_size; + dprintf("virtionet_receive() last_rx_idx=%i, vq_rx.used->idx=%i," + " id=%i len=%i\n", last_rx_idx, vq_rx.used->idx, id, len); - if (len > maxlen) { + if (len > (uint32_t)maxlen) { printf("virtio-net: Receive buffer not big enough!\n"); len = maxlen; } @@ -298,7 +291,7 @@ static int virtionet_receive(char *buf, int maxlen) printf("\n"); int i; for (i=0; i<64; i++) { - printf(" %02x", *(uint8_t*)(vq[VQ_RX].desc[id].addr+i)); + printf(" %02x", *(uint8_t*)(vq_rx.desc[id].addr+i)); if ((i%16)==15) printf("\n"); } @@ -306,14 +299,14 @@ static int virtionet_receive(char *buf, int maxlen) #endif /* Copy data to destination buffer */ - memcpy(buf, (void*)vq[VQ_RX].desc[id].addr, len); + memcpy(buf, (void *)virtio_modern64_to_cpu(&virtiodev, vq_rx.desc[id].addr), len); /* Move indices to next entries */ last_rx_idx = last_rx_idx + 1; - vq[VQ_RX].avail->ring[vq[VQ_RX].avail->idx % vq[VQ_RX].size] = id - 1; + vq_rx.avail->ring[idx % vq_rx.size] = virtio_cpu_to_modern16(&virtiodev, id - 1); sync(); - vq[VQ_RX].avail->idx += 1; + vq_rx.avail->idx = virtio_cpu_to_modern16(&virtiodev, idx + 1); /* Tell HV that RX queue entry is ready */ virtio_queue_notify(&virtiodev, VQ_RX); @@ -321,7 +314,7 @@ static int virtionet_receive(char *buf, int maxlen) return len; } -net_driver_t *virtionet_open(char *mac_addr, int len, struct virtio_device *dev) +net_driver_t *virtionet_open(struct virtio_device *dev) { net_driver_t *driver; @@ -331,7 +324,6 @@ net_driver_t *virtionet_open(char *mac_addr, int len, struct virtio_device *dev) return NULL; } - memcpy(driver->mac_addr, mac_addr, 6); driver->running = 0; if (virtionet_init_pci(dev)) diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-net.h b/qemu/roms/SLOF/lib/libvirtio/virtio-net.h index bc7a189f7..c2d8ee336 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio-net.h +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-net.h @@ -23,19 +23,10 @@ enum { VQ_TX = 1, /* Transmit Queue */ }; -struct vqs { - uint64_t id; /* Queue ID */ - uint32_t size; - void *buf_mem; - struct vring_desc *desc; - struct vring_avail *avail; - struct vring_used *used; -}; - -/* Device is identified by RX queue ID: */ -#define DEVICE_ID vq[0].id +/* VIRTIO_NET Feature bits */ +#define VIRTIO_NET_F_MAC (1 << 5) -extern net_driver_t *virtionet_open(char *mac_addr, int len, struct virtio_device *dev); +extern net_driver_t *virtionet_open(struct virtio_device *dev); extern void virtionet_close(net_driver_t *driver); extern int virtionet_read(char *buf, int len); extern int virtionet_write(char *buf, int len); diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-scsi.c b/qemu/roms/SLOF/lib/libvirtio/virtio-scsi.c index 48289289a..04181b06c 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio-scsi.c +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-scsi.c @@ -15,6 +15,7 @@ #include #include #include "virtio.h" +#include "virtio-internal.h" #include "virtio-scsi.h" int virtioscsi_send(struct virtio_device *dev, @@ -22,58 +23,54 @@ int virtioscsi_send(struct virtio_device *dev, struct virtio_scsi_resp_cmd *resp, int is_read, void *buf, uint64_t buf_len) { - struct vring_desc *desc; - struct vring_desc *vq_desc; /* Descriptor vring */ - struct vring_avail *vq_avail; /* "Available" vring */ - struct vring_used *vq_used; /* "Used" vring */ - - volatile uint16_t *current_used_idx; - uint16_t last_used_idx; - int id; - uint32_t vq_size, time; - - int vq = VIRTIO_SCSI_REQUEST_VQ; - - vq_size = virtio_get_qsize(dev, vq); - vq_desc = virtio_get_vring_desc(dev, vq); - vq_avail = virtio_get_vring_avail(dev, vq); - vq_used = virtio_get_vring_used(dev, vq); - - last_used_idx = vq_used->idx; - current_used_idx = &vq_used->idx; - - /* Determine descriptor index */ - id = (vq_avail->idx * 3) % vq_size; - - desc = &vq_desc[id]; - desc->addr = (uint64_t)req; - desc->len = sizeof(*req); - desc->flags = VRING_DESC_F_NEXT; - desc->next = (id + 1) % vq_size; - - /* Set up virtqueue descriptor for data */ - desc = &vq_desc[(id + 1) % vq_size]; - desc->addr = (uint64_t)resp; - desc->len = sizeof(*resp); - desc->flags = VRING_DESC_F_NEXT | VRING_DESC_F_WRITE; - desc->next = (id + 2) % vq_size; - - if (buf && buf_len) { - /* Set up virtqueue descriptor for status */ - desc = &vq_desc[(id + 2) % vq_size]; - desc->addr = (uint64_t)buf; - desc->len = buf_len; - desc->flags = is_read ? VRING_DESC_F_WRITE : 0; - desc->next = 0; - } else - desc->flags &= ~VRING_DESC_F_NEXT; - - vq_avail->ring[vq_avail->idx % vq_size] = id; - mb(); - vq_avail->idx += 1; - - /* Tell HV that the vq is ready */ - virtio_queue_notify(dev, vq); + struct vring_desc *vq_desc; /* Descriptor vring */ + struct vring_avail *vq_avail; /* "Available" vring */ + struct vring_used *vq_used; /* "Used" vring */ + + volatile uint16_t *current_used_idx; + uint16_t last_used_idx, avail_idx; + int id; + uint32_t vq_size, time; + + int vq = VIRTIO_SCSI_REQUEST_VQ; + + vq_size = virtio_get_qsize(dev, vq); + vq_desc = virtio_get_vring_desc(dev, vq); + vq_avail = virtio_get_vring_avail(dev, vq); + vq_used = virtio_get_vring_used(dev, vq); + + avail_idx = virtio_modern16_to_cpu(dev, vq_avail->idx); + + last_used_idx = vq_used->idx; + current_used_idx = &vq_used->idx; + + /* Determine descriptor index */ + id = (avail_idx * 3) % vq_size; + virtio_fill_desc(&vq_desc[id], dev->is_modern, (uint64_t)req, sizeof(*req), VRING_DESC_F_NEXT, + (id + 1) % vq_size); + + /* Set up virtqueue descriptor for data */ + if (buf && buf_len) { + virtio_fill_desc(&vq_desc[(id + 1) % vq_size], dev->is_modern, + (uint64_t)resp, sizeof(*resp), + VRING_DESC_F_NEXT | VRING_DESC_F_WRITE, + (id + 2) % vq_size); + /* Set up virtqueue descriptor for status */ + virtio_fill_desc(&vq_desc[(id + 2) % vq_size], dev->is_modern, + (uint64_t)buf, buf_len, + (is_read ? VRING_DESC_F_WRITE : 0), 0); + } else { + virtio_fill_desc(&vq_desc[(id + 1) % vq_size], dev->is_modern, + (uint64_t)resp, sizeof(*resp), + VRING_DESC_F_WRITE, 0); + } + + vq_avail->ring[avail_idx % vq_size] = virtio_cpu_to_modern16(dev, id); + mb(); + vq_avail->idx = virtio_cpu_to_modern16(dev, avail_idx + 1); + + /* Tell HV that the vq is ready */ + virtio_queue_notify(dev, vq); /* Wait for host to consume the descriptor */ time = SLOF_GetTimer() + VIRTIO_TIMEOUT; @@ -84,7 +81,7 @@ int virtioscsi_send(struct virtio_device *dev, break; } - return 0; + return 0; } /** @@ -93,42 +90,55 @@ int virtioscsi_send(struct virtio_device *dev, */ int virtioscsi_init(struct virtio_device *dev) { - struct vring_avail *vq_avail; - unsigned int idx = 0; - int qsize = 0; - - /* Reset device */ - // XXX That will clear the virtq base. We need to move - // initializing it to here anyway - // - // virtio_reset_device(dev); - - /* Acknowledge device. */ - virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE); + struct vring_avail *vq_avail; + unsigned int idx = 0; + int qsize = 0; + int status = VIRTIO_STAT_ACKNOWLEDGE; - /* Tell HV that we know how to drive the device. */ - virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER); - - /* Device specific setup - we do not support special features right now */ - virtio_set_guest_features(dev, 0); + /* Reset device */ + // XXX That will clear the virtq base. We need to move + // initializing it to here anyway + // + // virtio_reset_device(dev); + + /* Acknowledge device. */ + virtio_set_status(dev, status); + + /* Tell HV that we know how to drive the device. */ + status |= VIRTIO_STAT_DRIVER; + virtio_set_status(dev, status); + + /* Device specific setup - we do not support special features right now */ + if (dev->is_modern) { + if (virtio_negotiate_guest_features(dev, VIRTIO_F_VERSION_1)) + goto dev_error; + virtio_get_status(dev, &status); + } else { + virtio_set_guest_features(dev, 0); + } - while(1) { - qsize = virtio_get_qsize(dev, idx); - if (!qsize) - break; - virtio_vring_size(qsize); + while(1) { + qsize = virtio_get_qsize(dev, idx); + if (!qsize) + break; + virtio_vring_size(qsize); - vq_avail = virtio_get_vring_avail(dev, 0); - vq_avail->flags = VRING_AVAIL_F_NO_INTERRUPT; - vq_avail->idx = 0; - idx++; - } + vq_avail = virtio_get_vring_avail(dev, idx); + vq_avail->flags = virtio_cpu_to_modern16(dev, VRING_AVAIL_F_NO_INTERRUPT); + vq_avail->idx = 0; + idx++; + } /* Tell HV that setup succeeded */ - virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER - |VIRTIO_STAT_DRIVER_OK); + status |= VIRTIO_STAT_DRIVER_OK; + virtio_set_status(dev, status); return 0; +dev_error: + printf("%s: failed\n", __func__); + status |= VIRTIO_STAT_FAILED; + virtio_set_status(dev, status); + return -1; } /** diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio.c b/qemu/roms/SLOF/lib/libvirtio/virtio.c index f9c00a67a..f189941c7 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio.c +++ b/qemu/roms/SLOF/lib/libvirtio/virtio.c @@ -10,10 +10,16 @@ * IBM Corporation - initial implementation *****************************************************************************/ +#include +#include +#include +#include +#include #include #include #include #include "virtio.h" +#include "helpers.h" /* PCI virtio header offsets */ #define VIRTIOHDR_DEVICE_FEATURES 0 @@ -26,6 +32,166 @@ #define VIRTIOHDR_ISR_STATUS 19 #define VIRTIOHDR_DEVICE_CONFIG 20 +/* PCI defines */ +#define PCI_BASE_ADDR_SPACE_IO 0x01 +#define PCI_BASE_ADDR_SPACE_64BIT 0x04 +#define PCI_BASE_ADDR_MEM_MASK (~0x0fUL) +#define PCI_BASE_ADDR_IO_MASK (~0x03UL) + +#define PCI_BASE_ADDR_REG_0 0x10 +#define PCI_CONFIG_CAP_REG 0x34 + +#define PCI_CAP_ID_VNDR 0x9 + +/* Common configuration */ +#define VIRTIO_PCI_CAP_COMMON_CFG 1 +/* Notifications */ +#define VIRTIO_PCI_CAP_NOTIFY_CFG 2 +/* ISR access */ +#define VIRTIO_PCI_CAP_ISR_CFG 3 +/* Device specific configuration */ +#define VIRTIO_PCI_CAP_DEVICE_CFG 4 +/* PCI configuration access */ +#define VIRTIO_PCI_CAP_PCI_CFG 5 + +#define VIRTIO_PCI_CAP_VNDR 0 /* Generic PCI field: PCI_CAP_ID_VNDR */ +#define VIRTIO_PCI_CAP_NEXT 1 /* Generic PCI field: next ptr. */ +#define VIRTIO_PCI_CAP_LEN 2 /* Generic PCI field: capability length */ +#define VIRTIO_PCI_CAP_CFG_TYPE 3 /* Identifies the structure. */ +#define VIRTIO_PCI_CAP_BAR 4 /* Where to find it. */ +#define VIRTIO_PCI_CAP_OFFSET 8 /* Offset within bar. */ +#define VIRTIO_PCI_CAP_LENGTH 12 /* Length of the structure, in bytes. */ + +struct virtio_dev_common { + le32 dev_features_sel; + le32 dev_features; + le32 drv_features_sel; + le32 drv_features; + le16 msix_config; + le16 num_queues; + uint8_t dev_status; + uint8_t cfg_generation; + + le16 q_select; + le16 q_size; + le16 q_msix_vec; + le16 q_enable; + le16 q_notify_off; + le64 q_desc; + le64 q_avail; + le64 q_used; +} __attribute__ ((packed)); + +/* virtio 1.0 Spec: 4.1.3 PCI Device Layout + * + * Fields of different sizes are present in the device configuration regions. + * All 64-bit, 32-bit and 16-bit fields are little-endian. 64-bit fields are to + * be treated as two 32-bit fields, with low 32 bit part followed by the high 32 + * bit part. + */ +static void virtio_pci_write64(void *addr, uint64_t val) +{ + uint32_t hi = (val >> 32) & 0xFFFFFFFF; + uint32_t lo = val & 0xFFFFFFFF; + + ci_write_32(addr, cpu_to_le32(lo)); + ci_write_32(addr + 4, cpu_to_le32(hi)); +} + +static uint64_t virtio_pci_read64(void *addr) +{ + uint64_t hi, lo; + + lo = le32_to_cpu(ci_read_32(addr)); + hi = le32_to_cpu(ci_read_32(addr + 4)); + return (hi << 32) | lo; +} + +static void virtio_cap_set_base_addr(struct virtio_cap *cap, uint32_t offset) +{ + uint64_t addr; + + addr = SLOF_pci_config_read32(PCI_BASE_ADDR_REG_0 + 4 * cap->bar); + if (addr & PCI_BASE_ADDR_SPACE_IO) { + addr = addr & PCI_BASE_ADDR_IO_MASK; + cap->is_io = 1; + } else { + if (addr & PCI_BASE_ADDR_SPACE_64BIT) + addr |= SLOF_pci_config_read32(PCI_BASE_ADDR_REG_0 + 4 * (cap->bar + 1)) << 32; + addr = addr & PCI_BASE_ADDR_MEM_MASK; + cap->is_io = 0; + } + addr = (uint64_t)SLOF_translate_my_address((void *)addr); + cap->addr = (void *)addr + offset; +} + +static void virtio_process_cap(struct virtio_device *dev, uint8_t cap_ptr) +{ + struct virtio_cap *cap; + uint8_t cfg_type, bar; + uint32_t offset; + + cfg_type = SLOF_pci_config_read8(cap_ptr + VIRTIO_PCI_CAP_CFG_TYPE); + bar = SLOF_pci_config_read8(cap_ptr + VIRTIO_PCI_CAP_BAR); + offset = SLOF_pci_config_read32(cap_ptr + VIRTIO_PCI_CAP_OFFSET); + + switch(cfg_type) { + case VIRTIO_PCI_CAP_COMMON_CFG: + cap = &dev->common; + break; + case VIRTIO_PCI_CAP_NOTIFY_CFG: + cap = &dev->notify; + dev->notify_off_mul = SLOF_pci_config_read32(cap_ptr + sizeof(struct virtio_cap)); + break; + case VIRTIO_PCI_CAP_ISR_CFG: + cap = &dev->isr; + break; + case VIRTIO_PCI_CAP_DEVICE_CFG: + cap = &dev->device; + break; + default: + return; + } + + cap->bar = bar; + virtio_cap_set_base_addr(cap, offset); + cap->cap_id = cfg_type; +} + +/** + * Reads the virtio device capabilities, gets called from SLOF routines The + * function determines legacy or modern device and sets up driver registers + */ +struct virtio_device *virtio_setup_vd(void) +{ + uint8_t cap_ptr, cap_vndr; + struct virtio_device *dev; + + dev = SLOF_alloc_mem(sizeof(struct virtio_device)); + if (!dev) { + printf("Failed to allocate memory"); + return NULL; + } + + cap_ptr = SLOF_pci_config_read8(PCI_CONFIG_CAP_REG); + while (cap_ptr != 0) { + cap_vndr = SLOF_pci_config_read8(cap_ptr + VIRTIO_PCI_CAP_VNDR); + if (cap_vndr == PCI_CAP_ID_VNDR) + virtio_process_cap(dev, cap_ptr); + cap_ptr = SLOF_pci_config_read8(cap_ptr+VIRTIO_PCI_CAP_NEXT); + } + + if (dev->common.cap_id && dev->notify.cap_id && + dev->isr.cap_id && dev->device.cap_id) { + dev->is_modern = 1; + } else { + dev->is_modern = 0; + dev->legacy.cap_id = 0; + dev->legacy.bar = 0; + virtio_cap_set_base_addr(&dev->legacy, 0); + } + return dev; +} /** * Calculate ring size according to queue size number @@ -33,9 +199,9 @@ unsigned long virtio_vring_size(unsigned int qsize) { return VQ_ALIGN(sizeof(struct vring_desc) * qsize + - sizeof(struct vring_avail) + sizeof(uint16_t) * qsize) + - VQ_ALIGN(sizeof(struct vring_used) + - sizeof(struct vring_used_elem) * qsize); + sizeof(struct vring_avail) + sizeof(uint16_t) * qsize) + + VQ_ALIGN(sizeof(struct vring_used) + + sizeof(struct vring_used_elem) * qsize); } @@ -45,15 +211,22 @@ unsigned long virtio_vring_size(unsigned int qsize) * @param queue virtio queue number * @return number of elements */ -int virtio_get_qsize(struct virtio_device *dev, int queue) +unsigned int virtio_get_qsize(struct virtio_device *dev, int queue) { - int size = 0; + unsigned int size = 0; - if (dev->type == VIRTIO_TYPE_PCI) { - ci_write_16(dev->base+VIRTIOHDR_QUEUE_SELECT, + if (dev->is_modern) { + void *addr = dev->common.addr + offset_of(struct virtio_dev_common, q_select); + ci_write_16(addr, cpu_to_le16(queue)); + eieio(); + addr = dev->common.addr + offset_of(struct virtio_dev_common, q_size); + size = le16_to_cpu(ci_read_16(addr)); + } + else { + ci_write_16(dev->legacy.addr+VIRTIOHDR_QUEUE_SELECT, cpu_to_le16(queue)); eieio(); - size = le16_to_cpu(ci_read_16(dev->base+VIRTIOHDR_QUEUE_SIZE)); + size = le16_to_cpu(ci_read_16(dev->legacy.addr+VIRTIOHDR_QUEUE_SIZE)); } return size; @@ -70,12 +243,19 @@ struct vring_desc *virtio_get_vring_desc(struct virtio_device *dev, int queue) { struct vring_desc *desc = 0; - if (dev->type == VIRTIO_TYPE_PCI) { - ci_write_16(dev->base+VIRTIOHDR_QUEUE_SELECT, + if (dev->is_modern) { + void *q_sel = dev->common.addr + offset_of(struct virtio_dev_common, q_select); + void *q_desc = dev->common.addr + offset_of(struct virtio_dev_common, q_desc); + + ci_write_16(q_sel, cpu_to_le16(queue)); + eieio(); + desc = (void *)(virtio_pci_read64(q_desc)); + } else { + ci_write_16(dev->legacy.addr+VIRTIOHDR_QUEUE_SELECT, cpu_to_le16(queue)); eieio(); desc = (void*)(4096L * - le32_to_cpu(ci_read_32(dev->base+VIRTIOHDR_QUEUE_ADDRESS))); + le32_to_cpu(ci_read_32(dev->legacy.addr+VIRTIOHDR_QUEUE_ADDRESS))); } return desc; @@ -90,8 +270,18 @@ struct vring_desc *virtio_get_vring_desc(struct virtio_device *dev, int queue) */ struct vring_avail *virtio_get_vring_avail(struct virtio_device *dev, int queue) { - return (void*)((uint64_t)virtio_get_vring_desc(dev, queue) - + virtio_get_qsize(dev, queue) * sizeof(struct vring_desc)); + if (dev->is_modern) { + void *q_sel = dev->common.addr + offset_of(struct virtio_dev_common, q_select); + void *q_avail = dev->common.addr + offset_of(struct virtio_dev_common, q_avail); + + ci_write_16(q_sel, cpu_to_le16(queue)); + eieio(); + return (void *)(virtio_pci_read64(q_avail)); + } + else { + return (void*)((uint64_t)virtio_get_vring_desc(dev, queue) + + virtio_get_qsize(dev, queue) * sizeof(struct vring_desc)); + } } @@ -103,20 +293,46 @@ struct vring_avail *virtio_get_vring_avail(struct virtio_device *dev, int queue) */ struct vring_used *virtio_get_vring_used(struct virtio_device *dev, int queue) { - return (void*)VQ_ALIGN((uint64_t)virtio_get_vring_avail(dev, queue) - + virtio_get_qsize(dev, queue) - * sizeof(struct vring_avail)); + if (dev->is_modern) { + void *q_sel = dev->common.addr + offset_of(struct virtio_dev_common, q_select); + void *q_used = dev->common.addr + offset_of(struct virtio_dev_common, q_used); + + ci_write_16(q_sel, cpu_to_le16(queue)); + eieio(); + return (void *)(virtio_pci_read64(q_used)); + } else { + return (void*)VQ_ALIGN((uint64_t)virtio_get_vring_avail(dev, queue) + + virtio_get_qsize(dev, queue) + * sizeof(struct vring_avail)); + } } +/** + * Fill the virtio ring descriptor depending on the legacy mode or virtio 1.0 + */ +void virtio_fill_desc(struct vring_desc *desc, bool is_modern, + uint64_t addr, uint32_t len, + uint16_t flags, uint16_t next) +{ + if (is_modern) { + desc->addr = cpu_to_le64(addr); + desc->len = cpu_to_le32(len); + desc->flags = cpu_to_le16(flags); + desc->next = cpu_to_le16(next); + } else { + desc->addr = addr; + desc->len = len; + desc->flags = flags; + desc->next = next; + } +} /** * Reset virtio device */ void virtio_reset_device(struct virtio_device *dev) { - if (dev->type == VIRTIO_TYPE_PCI) { - ci_write_8(dev->base+VIRTIOHDR_DEVICE_STATUS, 0); - } + virtio_set_status(dev, 0); } @@ -125,25 +341,64 @@ void virtio_reset_device(struct virtio_device *dev) */ void virtio_queue_notify(struct virtio_device *dev, int queue) { - if (dev->type == VIRTIO_TYPE_PCI) { - ci_write_16(dev->base+VIRTIOHDR_QUEUE_NOTIFY, cpu_to_le16(queue)); + if (dev->is_modern) { + void *q_sel = dev->common.addr + offset_of(struct virtio_dev_common, q_select); + void *q_ntfy = dev->common.addr + offset_of(struct virtio_dev_common, q_notify_off); + void *addr; + uint16_t q_notify_off; + + ci_write_16(q_sel, cpu_to_le16(queue)); + eieio(); + q_notify_off = le16_to_cpu(ci_read_16(q_ntfy)); + addr = dev->notify.addr + q_notify_off * dev->notify_off_mul; + ci_write_16(addr, cpu_to_le16(queue)); + } else { + ci_write_16(dev->legacy.addr+VIRTIOHDR_QUEUE_NOTIFY, cpu_to_le16(queue)); } } /** * Set queue address */ -void virtio_set_qaddr(struct virtio_device *dev, int queue, unsigned int qaddr) +void virtio_set_qaddr(struct virtio_device *dev, int queue, unsigned long qaddr) +{ + if (dev->is_modern) { + uint64_t q_desc = qaddr; + uint64_t q_avail; + uint64_t q_used; + uint32_t q_size = virtio_get_qsize(dev, queue); + + virtio_pci_write64(dev->common.addr + offset_of(struct virtio_dev_common, q_desc), q_desc); + q_avail = q_desc + q_size * sizeof(struct vring_desc); + virtio_pci_write64(dev->common.addr + offset_of(struct virtio_dev_common, q_avail), q_avail); + q_used = VQ_ALIGN(q_avail + sizeof(struct vring_avail) + sizeof(uint16_t) * q_size); + virtio_pci_write64(dev->common.addr + offset_of(struct virtio_dev_common, q_used), q_used); + ci_write_16(dev->common.addr + offset_of(struct virtio_dev_common, q_enable), cpu_to_le16(1)); + } else { + uint32_t val = qaddr; + val = val >> 12; + ci_write_16(dev->legacy.addr+VIRTIOHDR_QUEUE_SELECT, + cpu_to_le16(queue)); + eieio(); + ci_write_32(dev->legacy.addr+VIRTIOHDR_QUEUE_ADDRESS, + cpu_to_le32(val)); + } +} + +int virtio_queue_init_vq(struct virtio_device *dev, struct vqs *vq, unsigned int id) { - if (dev->type == VIRTIO_TYPE_PCI) { - uint32_t val = qaddr; - val = val >> 12; - ci_write_16(dev->base+VIRTIOHDR_QUEUE_SELECT, - cpu_to_le16(queue)); - eieio(); - ci_write_32(dev->base+VIRTIOHDR_QUEUE_ADDRESS, - cpu_to_le32(val)); - } + vq->size = virtio_get_qsize(dev, id); + vq->desc = SLOF_alloc_mem_aligned(virtio_vring_size(vq->size), 4096); + if (!vq->desc) { + printf("memory allocation failed!\n"); + return -1; + } + memset(vq->desc, 0, virtio_vring_size(vq->size)); + virtio_set_qaddr(dev, id, (unsigned long)vq->desc); + vq->avail = virtio_get_vring_avail(dev, id); + vq->used = virtio_get_vring_used(dev, id); + vq->id = id; + return 0; } /** @@ -151,34 +406,109 @@ void virtio_set_qaddr(struct virtio_device *dev, int queue, unsigned int qaddr) */ void virtio_set_status(struct virtio_device *dev, int status) { - if (dev->type == VIRTIO_TYPE_PCI) { - ci_write_8(dev->base+VIRTIOHDR_DEVICE_STATUS, status); + if (dev->is_modern) { + ci_write_8(dev->common.addr + + offset_of(struct virtio_dev_common, dev_status), status); + } else { + ci_write_8(dev->legacy.addr+VIRTIOHDR_DEVICE_STATUS, status); } } +/** + * Get device status bits + */ +void virtio_get_status(struct virtio_device *dev, int *status) +{ + if (dev->is_modern) { + *status = ci_read_8(dev->common.addr + + offset_of(struct virtio_dev_common, dev_status)); + } else { + *status = ci_read_8(dev->legacy.addr+VIRTIOHDR_DEVICE_STATUS); + } +} /** * Set guest feature bits */ -void virtio_set_guest_features(struct virtio_device *dev, int features) +void virtio_set_guest_features(struct virtio_device *dev, uint64_t features) { - if (dev->type == VIRTIO_TYPE_PCI) { - ci_write_32(dev->base+VIRTIOHDR_GUEST_FEATURES, bswap_32(features)); + if (dev->is_modern) { + uint32_t f1 = (features >> 32) & 0xFFFFFFFF; + uint32_t f0 = features & 0xFFFFFFFF; + void *addr = dev->common.addr; + + ci_write_32(addr + offset_of(struct virtio_dev_common, drv_features_sel), + cpu_to_le32(1)); + ci_write_32(addr + offset_of(struct virtio_dev_common, drv_features), + cpu_to_le32(f1)); + + ci_write_32(addr + offset_of(struct virtio_dev_common, drv_features_sel), + cpu_to_le32(0)); + ci_write_32(addr + offset_of(struct virtio_dev_common, drv_features), + cpu_to_le32(f0)); + } else { + ci_write_32(dev->legacy.addr+VIRTIOHDR_GUEST_FEATURES, cpu_to_le32(features)); } } /** * Get host feature bits */ -void virtio_get_host_features(struct virtio_device *dev, int *features) +uint64_t virtio_get_host_features(struct virtio_device *dev) { - if (dev->type == VIRTIO_TYPE_PCI && features) { - *features = bswap_32(ci_read_32(dev->base+VIRTIOHDR_DEVICE_FEATURES)); + uint64_t features = 0; + if (dev->is_modern) { + uint32_t f0 = 0, f1 = 0; + void *addr = dev->common.addr; + + ci_write_32(addr + offset_of(struct virtio_dev_common, dev_features_sel), + cpu_to_le32(1)); + f1 = ci_read_32(addr + + offset_of(struct virtio_dev_common, dev_features)); + ci_write_32(addr + offset_of(struct virtio_dev_common, dev_features_sel), + cpu_to_le32(0)); + f0 = ci_read_32(addr + + offset_of(struct virtio_dev_common, dev_features)); + + features = ((uint64_t)le32_to_cpu(f1) << 32) | le32_to_cpu(f0); + } else { + features = le32_to_cpu(ci_read_32(dev->legacy.addr+VIRTIOHDR_DEVICE_FEATURES)); } + return features; } +int virtio_negotiate_guest_features(struct virtio_device *dev, uint64_t features) +{ + uint64_t host_features = 0; + int status; + + /* Negotiate features */ + host_features = virtio_get_host_features(dev); + if (!(host_features & VIRTIO_F_VERSION_1)) { + fprintf(stderr, "Device does not support virtio 1.0 %llx\n", host_features); + return -1; + } + + virtio_set_guest_features(dev, features); + host_features = virtio_get_host_features(dev); + if ((host_features & features) != features) { + fprintf(stderr, "Features error %llx\n", features); + return -1; + } + + virtio_get_status(dev, &status); + status |= VIRTIO_STAT_FEATURES_OK; + virtio_set_status(dev, status); + + /* Read back to verify the FEATURES_OK bit */ + virtio_get_status(dev, &status); + if ((status & VIRTIO_STAT_FEATURES_OK) != VIRTIO_STAT_FEATURES_OK) + return -1; + + return 0; +} /** * Get additional config values @@ -186,32 +516,38 @@ void virtio_get_host_features(struct virtio_device *dev, int *features) uint64_t virtio_get_config(struct virtio_device *dev, int offset, int size) { uint64_t val = ~0ULL; + uint32_t hi, lo; void *confbase; - switch (dev->type) { - case VIRTIO_TYPE_PCI: - confbase = dev->base+VIRTIOHDR_DEVICE_CONFIG; - break; - default: - return ~0ULL; - } + if (dev->is_modern) + confbase = dev->device.addr; + else + confbase = dev->legacy.addr+VIRTIOHDR_DEVICE_CONFIG; + switch (size) { - case 1: + case 1: val = ci_read_8(confbase+offset); break; - case 2: + case 2: val = ci_read_16(confbase+offset); + if (dev->is_modern) + val = le16_to_cpu(val); break; - case 4: + case 4: val = ci_read_32(confbase+offset); + if (dev->is_modern) + val = le32_to_cpu(val); break; - case 8: + case 8: /* We don't support 8 bytes PIO accesses * in qemu and this is all PIO */ - val = ci_read_32(confbase+offset); - val <<= 32; - val |= ci_read_32(confbase+offset+4); + lo = ci_read_32(confbase+offset); + hi = ci_read_32(confbase+offset+4); + if (dev->is_modern) + val = (uint64_t)le32_to_cpu(hi) << 32 | le32_to_cpu(lo); + else + val = (uint64_t)hi << 32 | lo; break; } @@ -222,20 +558,19 @@ uint64_t virtio_get_config(struct virtio_device *dev, int offset, int size) * Get config blob */ int __virtio_read_config(struct virtio_device *dev, void *dst, - int offset, int len) + int offset, int len) { void *confbase; unsigned char *buf = dst; int i; - switch (dev->type) { - case VIRTIO_TYPE_PCI: - confbase = dev->base+VIRTIOHDR_DEVICE_CONFIG; - break; - default: - return 0; - } + if (dev->is_modern) + confbase = dev->device.addr; + else + confbase = dev->legacy.addr+VIRTIOHDR_DEVICE_CONFIG; + for (i = 0; i < len; i++) buf[i] = ci_read_8(confbase + offset + i); + return len; } diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio.code b/qemu/roms/SLOF/lib/libvirtio/virtio.code index 258b9bbda..8eec8f055 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio.code +++ b/qemu/roms/SLOF/lib/libvirtio/virtio.code @@ -18,6 +18,11 @@ /******** core virtio ********/ +// : virtio-setup-vd ( -- dev ) +PRIM(virtio_X2d_setup_X2d_vd) + PUSH; TOS.a = virtio_setup_vd(); +MIRP + // : virtio-vring-size ( queuesize -- ringsize ) PRIM(virtio_X2d_vring_X2d_size) TOS.u = virtio_vring_size(TOS.u); @@ -122,20 +127,18 @@ MIRP /******** virtio-net ********/ -// : virtio-net-open ( mac-addr-str len dev -- false | [ driver true ] ) +// : virtio-net-open ( dev -- false | [ driver true ] ) PRIM(virtio_X2d_net_X2d_open) { - void *dev = TOS.a; POP; - int len = TOS.u; POP; - char *mac_addr = TOS.a; + void *dev = TOS.a; - net_driver_t *net_driver = virtionet_open(mac_addr, len, dev); + net_driver_t *net_driver = virtionet_open(dev); - if (net_driver) { - TOS.u = (unsigned long)net_driver; PUSH; - TOS.n = -1; - } else - TOS.n = 0; + if (net_driver) { + TOS.u = (unsigned long)net_driver; PUSH; + TOS.n = -1; + } else + TOS.n = 0; } MIRP diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio.h b/qemu/roms/SLOF/lib/libvirtio/virtio.h index d5759b45a..0fee4baec 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio.h +++ b/qemu/roms/SLOF/lib/libvirtio/virtio.h @@ -14,13 +14,23 @@ #define _LIBVIRTIO_H #include +#include /* Device status bits */ #define VIRTIO_STAT_ACKNOWLEDGE 1 #define VIRTIO_STAT_DRIVER 2 #define VIRTIO_STAT_DRIVER_OK 4 +#define VIRTIO_STAT_FEATURES_OK 8 +#define VIRTIO_STAT_NEEDS_RESET 64 #define VIRTIO_STAT_FAILED 128 +#define BIT(x) (1UL << (x)) + +/* VIRTIO 1.0 Device independent feature bits */ +#define VIRTIO_F_RING_INDIRECT_DESC BIT(28) +#define VIRTIO_F_RING_EVENT_IDX BIT(29) +#define VIRTIO_F_VERSION_1 BIT(32) + #define VIRTIO_TIMEOUT 5000 /* 5 sec timeout */ /* Definitions for vring_desc.flags */ @@ -34,7 +44,7 @@ struct vring_desc { uint32_t len; /* Length */ uint16_t flags; /* The flags as indicated above */ uint16_t next; /* Next field if flags & NEXT */ -}; +}; /* Definitions for vring_avail.flags */ #define VRING_AVAIL_F_NO_INTERRUPT 1 @@ -44,8 +54,7 @@ struct vring_avail { uint16_t flags; uint16_t idx; uint16_t ring[]; -}; - +}; /* Definitions for vring_used.flags */ #define VRING_USED_F_NO_NOTIFY 1 @@ -61,27 +70,56 @@ struct vring_used { struct vring_used_elem ring[]; }; -#define VIRTIO_TYPE_PCI 0 /* For virtio-pci interface */ +/* Structure shared with SLOF and is 16bytes */ +struct virtio_cap { + void *addr; + uint8_t bar; + uint8_t is_io; + uint8_t cap_id; +}; + struct virtio_device { - void *base; /* base address */ - int type; /* VIRTIO_TYPE_PCI or VIRTIO_TYPE_VIO */ + uint32_t is_modern; /* Indicates whether to use virtio 1.0 */ + struct virtio_cap legacy; + struct virtio_cap common; + struct virtio_cap notify; + struct virtio_cap isr; + struct virtio_cap device; + struct virtio_cap pci; + uint32_t notify_off_mul; +}; + +struct vqs { + uint64_t id; /* Queue ID */ + uint32_t size; + void *buf_mem; + struct vring_desc *desc; + struct vring_avail *avail; + struct vring_used *used; }; /* Parts of the virtqueue are aligned on a 4096 byte page boundary */ #define VQ_ALIGN(addr) (((addr) + 0xfff) & ~0xfff) extern unsigned long virtio_vring_size(unsigned int qsize); -extern int virtio_get_qsize(struct virtio_device *dev, int queue); +extern unsigned int virtio_get_qsize(struct virtio_device *dev, int queue); extern struct vring_desc *virtio_get_vring_desc(struct virtio_device *dev, int queue); extern struct vring_avail *virtio_get_vring_avail(struct virtio_device *dev, int queue); extern struct vring_used *virtio_get_vring_used(struct virtio_device *dev, int queue); +extern void virtio_fill_desc(struct vring_desc *desc, bool is_modern, + uint64_t addr, uint32_t len, + uint16_t flags, uint16_t next); +extern int virtio_queue_init_vq(struct virtio_device *dev, struct vqs *vq, unsigned int id); +extern struct virtio_device *virtio_setup_vd(void); extern void virtio_reset_device(struct virtio_device *dev); extern void virtio_queue_notify(struct virtio_device *dev, int queue); extern void virtio_set_status(struct virtio_device *dev, int status); -extern void virtio_set_qaddr(struct virtio_device *dev, int queue, unsigned int qaddr); -extern void virtio_set_guest_features(struct virtio_device *dev, int features); -extern void virtio_get_host_features(struct virtio_device *dev, int *features); +extern void virtio_get_status(struct virtio_device *dev, int *status); +extern void virtio_set_qaddr(struct virtio_device *dev, int queue, unsigned long qaddr); +extern void virtio_set_guest_features(struct virtio_device *dev, uint64_t features); +extern uint64_t virtio_get_host_features(struct virtio_device *dev); +extern int virtio_negotiate_guest_features(struct virtio_device *dev, uint64_t features); extern uint64_t virtio_get_config(struct virtio_device *dev, int offset, int size); extern int __virtio_read_config(struct virtio_device *dev, void *dst, int offset, int len); diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio.in b/qemu/roms/SLOF/lib/libvirtio/virtio.in index c36d127c7..195840e0f 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio.in +++ b/qemu/roms/SLOF/lib/libvirtio/virtio.in @@ -10,6 +10,8 @@ * IBM Corporation - initial implementation *****************************************************************************/ +cod(virtio-setup-vd) + cod(virtio-vring-size) cod(virtio-get-qsize) cod(virtio-get-config) diff --git a/qemu/roms/SLOF/make.rules b/qemu/roms/SLOF/make.rules index aebc4e360..cbc63530a 100644 --- a/qemu/roms/SLOF/make.rules +++ b/qemu/roms/SLOF/make.rules @@ -19,8 +19,12 @@ ARCH := $(shell uname -p) # Auto-detect ppc64 -ifeq ($(ARCH), ppc64) -CROSS = "" +ifeq (ppc64,$(findstring ppc64,$(ARCH))) + ifeq ($(ARCH), ppc64le) + EXTRA_CC = -mbig -mabi=elfv1 + EXTRA_LD = -mbig + endif +CROSS ?= else CROSS ?= powerpc64-linux- endif @@ -31,8 +35,8 @@ HOSTCC ?= gcc HOSTCFLAGS = -g -Wall -W -O2 -I. -I../include DD = dd -ONLY_CC = $(CROSS)gcc -m$(CELLSIZE) -ONLY_AS = $(CROSS)as -m$(CELLSIZE) +ONLY_CC = $(CROSS)gcc -m$(CELLSIZE) $(EXTRA_CC) +ONLY_AS = $(CROSS)as -m$(CELLSIZE) $(EXTRA_LD) ONLY_LD = $(CROSS)ld -melf$(CELLSIZE)ppc # Verbose level: diff --git a/qemu/roms/SLOF/rtas/reloc.S b/qemu/roms/SLOF/rtas/reloc.S index e24d293d4..1b5b59a68 100644 --- a/qemu/roms/SLOF/rtas/reloc.S +++ b/qemu/roms/SLOF/rtas/reloc.S @@ -61,7 +61,7 @@ _rtas_start: ._rtas_entry_offset: .quad rtas_entry-_rtas_start ._rtas_config_offset: .quad rtas_config-_rtas_start ._rtas_stack: .quad .stack-_rtas_start+RTAS_STACKSIZE-0x60 -._rtas_toc: .quad _got-_rtas_start+0x8000 +._rtas_toc: .quad _got-_rtas_start .over: mflr r8 # gpr 8 is the base diff --git a/qemu/roms/SLOF/rtas/rtas.lds b/qemu/roms/SLOF/rtas/rtas.lds index a5ba1daaf..30b18dd26 100644 --- a/qemu/roms/SLOF/rtas/rtas.lds +++ b/qemu/roms/SLOF/rtas/rtas.lds @@ -28,7 +28,8 @@ SECTIONS { } .got : { - _got = .; + . = ALIGN(256); + _got = DEFINED (.TOC.) ? .TOC. : ADDR (.got) + 0x8000; *(.got .toc) } .reloc : diff --git a/qemu/roms/SLOF/rtas/rtas_entry.S b/qemu/roms/SLOF/rtas/rtas_entry.S index 74693aa48..424137bf5 100644 --- a/qemu/roms/SLOF/rtas/rtas_entry.S +++ b/qemu/roms/SLOF/rtas/rtas_entry.S @@ -39,7 +39,7 @@ rtas_entry: bcl 20,31,.over # branch to over .base: .align 3 -..got: .quad _got-.base+0x8000 +..got: .quad _got-.base ..stack: .quad .stack+RTAS_STACKSIZE-0x60-.base .over: mflr r8 # gpr 8 is the base diff --git a/qemu/roms/SLOF/slof/entry.S b/qemu/roms/SLOF/slof/entry.S index dcff57ba0..5372de357 100644 --- a/qemu/roms/SLOF/slof/entry.S +++ b/qemu/roms/SLOF/slof/entry.S @@ -207,4 +207,13 @@ call_client: li 3, -1 # client app return blr + + # Call another function via pointer in r6 + # (arguments can be provided in r3 to r5) + # Destination function should jump back to lr +C_ENTRY(call_c) + mtctr r6 + bctr + +.global the_system_stack .lcomm the_system_stack, STACKSIZE, 16 diff --git a/qemu/roms/SLOF/slof/fs/archsupport.fs b/qemu/roms/SLOF/slof/fs/archsupport.fs deleted file mode 100644 index cc4668769..000000000 --- a/qemu/roms/SLOF/slof/fs/archsupport.fs +++ /dev/null @@ -1,38 +0,0 @@ -\ ***************************************************************************** -\ * Copyright (c) 2011 IBM Corporation -\ * All rights reserved. -\ * This program and the accompanying materials -\ * are made available under the terms of the BSD License -\ * which accompanies this distribution, and is available at -\ * http://www.opensource.org/licenses/bsd-license.php -\ * -\ * Contributors: -\ * IBM Corporation - initial implementation -\ ****************************************************************************/ - -\ Qemu supports max 256cpus, 32K will be able to accomodate the fdt changes if -\ needed. -8000 VALUE size -: ibm,client-architecture-support ( vec -- err? ) - \ Store require parameters in nvram - \ to come back to right boot device - \ Allocate memory for H_CALL - size alloc-mem ( vec memaddr ) - swap over size ( memaddr vec memaddr size ) - \ make h_call to hypervisor - hv-cas 0= IF ( memaddr ) - dup l@ 1 >= IF \ Version number >= 1 - \ Make required changes - " /" find-node set-node - dup 4 + fdt-init - fdt-check-header - fdt-struct fdt-fix-cas-node - fdt-fix-cas-success NOT - ELSE - FALSE - THEN - ELSE - TRUE - THEN - >r size free-mem r> -; diff --git a/qemu/roms/SLOF/slof/fs/base.fs b/qemu/roms/SLOF/slof/fs/base.fs index e71e087eb..03e77e54f 100644 --- a/qemu/roms/SLOF/slof/fs/base.fs +++ b/qemu/roms/SLOF/slof/fs/base.fs @@ -579,8 +579,6 @@ defer cursor-off ( -- ) #include "debug.fs" \ provide 7.5.3.1 Dictionary search #include "dictionary.fs" -\ block data access for IO devices - ought to be implemented in engine -#include "rmove.fs" \ provide a simple run time preprocessor #include diff --git a/qemu/roms/SLOF/slof/fs/boot.fs b/qemu/roms/SLOF/slof/fs/boot.fs index 9a0ded0c2..e0b628140 100644 --- a/qemu/roms/SLOF/slof/fs/boot.fs +++ b/qemu/roms/SLOF/slof/fs/boot.fs @@ -181,17 +181,13 @@ defer go ( -- ) \ with watchdog timeout. 4ec set-watchdog THEN + 2dup " HALT" str= IF 2drop 0 EXIT THEN my-self >r current-node @ >r \ Save my-self ." Trying to load: " $bootargs type ." from: " 2dup type ." ... " 2dup open-dev dup IF dup to my-self dup ihandle>phandle set-node -rot ( ihandle devstr len ) - my-args nip 0= IF - 2dup 1- + c@ [char] : <> IF \ Add : to device path if missing - 1+ strdup 2dup 1- + [char] : swap c! - THEN - THEN encode-string s" bootpath" set-chosen $bootargs encode-string s" bootargs" set-chosen get-load-base s" load" 3 pick ['] $call-method CATCH IF @@ -211,7 +207,7 @@ defer go ( -- ) : parse-load ( "{devlist}" -- success ) \ Parse-execute boot-device list cr BEGIN parse-word dup WHILE - ( de-alias ) do-load dup 0< IF drop 0 THEN IF + de-alias do-load dup 0< IF drop 0 THEN IF state-valid @ IF ." Successfully loaded" cr THEN true 0d parse strdup load-list 2! EXIT THEN diff --git a/qemu/roms/SLOF/slof/fs/client.fs b/qemu/roms/SLOF/slof/fs/client.fs index 1b2bb0326..7d537a668 100644 --- a/qemu/roms/SLOF/slof/fs/client.fs +++ b/qemu/roms/SLOF/slof/fs/client.fs @@ -281,6 +281,18 @@ ALSO client-voc DEFINITIONS quiesce ; +\ +\ Standard for Boot, defined in 6.3.2.5: +\ +: boot ( zstr -- ) + zcount + debug-client-interface? IF + ." ci: boot " 2dup type cr + THEN + " boot " 2swap $cat " boot-command" $setenv (nvupdate) + reset-all +; + \ \ User Interface, defined in 6.3.2.6 \ diff --git a/qemu/roms/SLOF/slof/fs/fbuffer.fs b/qemu/roms/SLOF/slof/fs/fbuffer.fs index 756f05a95..47046087d 100644 --- a/qemu/roms/SLOF/slof/fs/fbuffer.fs +++ b/qemu/roms/SLOF/slof/fs/fbuffer.fs @@ -19,6 +19,7 @@ 0 VALUE screen-height 0 VALUE screen-width 0 VALUE screen-depth +0 VALUE screen-line-bytes 0 VALUE window-top 0 VALUE window-left @@ -54,10 +55,10 @@ : fb8-background inverse? ; : fb8-foreground inverse? invert ; -: fb8-lines2bytes ( #lines -- #bytes ) char-height * screen-width * screen-depth * ; +: fb8-lines2bytes ( #lines -- #bytes ) char-height * screen-line-bytes * ; : fb8-columns2bytes ( #columns -- #bytes ) char-width * screen-depth * ; : fb8-line2addr ( line# -- addr ) - char-height * window-top + screen-width * screen-depth * + char-height * window-top + screen-line-bytes * frame-buffer-adr + window-left screen-depth * + ; @@ -98,9 +99,10 @@ CREATE bitmap-buffer 400 4 * allot : fb8-toggle-cursor ( -- ) line# fb8-line2addr column# fb8-columns2bytes + - char-height 0 ?DO - char-width screen-depth * 0 ?DO dup dup rb@ -1 xor swap rb! 1+ LOOP - screen-width screen-depth * + char-width screen-depth * - + char-height 2 - screen-line-bytes * + + 2 0 ?DO + dup char-width screen-depth * invert-region + screen-line-bytes + LOOP drop ; @@ -110,7 +112,7 @@ CREATE bitmap-buffer 400 4 * allot line# fb8-line2addr column# fb8-columns2bytes + ( bitmap-buf fb-addr ) char-height 0 ?DO 2dup char-width screen-depth * mrmove - screen-width screen-depth * + >r char-width screen-depth * + r> + screen-line-bytes + >r char-width screen-depth * + r> LOOP 2drop ELSE 2drop r> 3drop THEN ; @@ -135,12 +137,12 @@ CREATE bitmap-buffer 400 4 * allot fb8-columns2bytes swap fb8-columns2bytes tuck - over r@ tuck + rot char-height 0 ?DO 3dup rmove - -rot screen-width screen-depth * tuck + -rot + swap rot + -rot screen-line-bytes tuck + -rot + swap rot LOOP 3drop r> THEN char-height 0 ?DO - dup 2 pick fb8-erase-block screen-width screen-depth * + + dup 2 pick fb8-erase-block screen-line-bytes + LOOP 2drop ; @@ -153,12 +155,12 @@ CREATE bitmap-buffer 400 4 * allot fb8-columns2bytes swap fb8-columns2bytes tuck - over r@ + 2dup + r> swap >r rot char-height 0 ?DO 3dup rmove - -rot screen-width screen-depth * tuck + -rot + swap rot + -rot screen-line-bytes tuck + -rot + swap rot LOOP 3drop r> over - THEN char-height 0 ?DO - dup 2 pick fb8-erase-block screen-width screen-depth * + + dup 2 pick fb8-erase-block screen-line-bytes + LOOP 2drop ; @@ -166,13 +168,11 @@ CREATE bitmap-buffer 400 4 * allot : fb8-reset-screen ( -- ) ( Left as no-op by design ) ; : fb8-erase-screen ( -- ) - frame-buffer-adr screen-height screen-width * screen-depth * fb8-erase-block + frame-buffer-adr screen-height screen-line-bytes * fb8-erase-block ; : fb8-invert-screen ( -- ) - frame-buffer-adr screen-height screen-width * screen-depth * 2dup /x / 0 ?DO - dup rx@ -1 xor over rx! xa1+ - LOOP 3drop + frame-buffer-adr screen-height screen-line-bytes * invert-region ; : fb8-blink-screen ( -- ) fb8-invert-screen fb8-invert-screen ; @@ -180,6 +180,7 @@ CREATE bitmap-buffer 400 4 * allot : fb8-install ( width height #columns #lines -- ) 1 to screen-depth 2swap to screen-height to screen-width + screen-width to screen-line-bytes screen-#rows min to #lines screen-#columns min to #columns screen-height char-height #lines * - 2/ to window-top @@ -201,6 +202,7 @@ CREATE bitmap-buffer 400 4 * allot >r fb8-install r> to screen-depth + screen-width screen-depth * to screen-line-bytes ; diff --git a/qemu/roms/SLOF/slof/fs/little-endian.fs b/qemu/roms/SLOF/slof/fs/little-endian.fs index f2e4e8d42..6b4779ee0 100644 --- a/qemu/roms/SLOF/slof/fs/little-endian.fs +++ b/qemu/roms/SLOF/slof/fs/little-endian.fs @@ -17,6 +17,9 @@ here c@ ef = CONSTANT ?littleendian ?bigendian [IF] +: x!-le >r xbflip r> x! ; +: x@-le x@ xbflip ; + : l!-le >r lbflip r> l! ; : l@-le l@ lbflip ; @@ -47,6 +50,9 @@ here c@ ef = CONSTANT ?littleendian [ELSE] +: x!-le x! ; +: x@-le x@ ; + : l!-le l! ; : l@-le l@ ; diff --git a/qemu/roms/SLOF/slof/fs/packages/disk-label.fs b/qemu/roms/SLOF/slof/fs/packages/disk-label.fs index fe1c25e7a..e034d6408 100644 --- a/qemu/roms/SLOF/slof/fs/packages/disk-label.fs +++ b/qemu/roms/SLOF/slof/fs/packages/disk-label.fs @@ -20,6 +20,7 @@ false VALUE debug-disk-label? \ If we ever want to put a large kernel with initramfs from a PREP partition \ we might need to increase this value. The default value is 65536 blocks (32MB) d# 65536 value max-prep-partition-blocks +d# 4096 CONSTANT block-array-size s" disk-label" device-name @@ -152,8 +153,8 @@ CONSTANT /gpt-part-entry : init-block ( -- ) s" block-size" ['] $call-parent CATCH IF ABORT" parent has no block-size." THEN to block-size - d# 4096 alloc-mem - dup d# 4096 erase + block-array-size alloc-mem + dup block-array-size erase to block debug-disk-label? IF ." init-block: block-size=" block-size .d ." block=0x" block u. cr @@ -178,7 +179,8 @@ CONSTANT /gpt-part-entry \ This word returns true if the currently loaded block has _NO_ GPT partition id : no-gpt? ( -- true|false ) 0 read-sector - 1 partition>part-entry part-entry>id c@ ee <> + 1 partition>part-entry part-entry>id c@ ee <> IF true EXIT THEN + block mbr>magic w@-le aa55 <> ; : pc-extended-partition? ( part-entry-addr -- true|false ) @@ -266,7 +268,10 @@ CONSTANT /gpt-part-entry : try-dos-partition ( -- okay? ) \ Read partition table and check magic. - no-mbr? IF cr ." No DOS disk-label found." cr false EXIT THEN + no-mbr? IF + debug-disk-label? IF cr ." No DOS disk-label found." cr THEN + false EXIT + THEN count-dos-logical-partitions TO dos-logical-partitions @@ -320,6 +325,14 @@ CONSTANT /gpt-part-entry \ Load from first active DOS boot partition. +: fat-bootblock? ( addr -- flag ) + \ byte 0-2 of the bootblock is a jump instruction in + \ all FAT filesystems. + \ e9 and eb are jump instructions in x86 assembler. + dup c@ e9 = IF drop true EXIT THEN + dup c@ eb = swap 2+ c@ 90 = and +; + \ NOTE: block-size is always 512 bytes for DOS partition tables. : load-from-dos-boot-partition ( addr -- size ) @@ -352,60 +365,103 @@ CONSTANT /gpt-part-entry drop 0 ; -\ Check for GPT PReP partition GUID -9E1A2D38 CONSTANT GPT-PREP-PARTITION-1 -C612 CONSTANT GPT-PREP-PARTITION-2 -4316 CONSTANT GPT-PREP-PARTITION-3 -AA26 CONSTANT GPT-PREP-PARTITION-4 -8B49521E5A8B CONSTANT GPT-PREP-PARTITION-5 +\ Check for GPT PReP partition GUID. Only first 3 blocks are +\ byte-swapped treating last two blocks as contigous for simplifying +\ comparison +9E1A2D38 CONSTANT GPT-PREP-PARTITION-1 +C612 CONSTANT GPT-PREP-PARTITION-2 +4316 CONSTANT GPT-PREP-PARTITION-3 +AA268B49521E5A8B CONSTANT GPT-PREP-PARTITION-4 : gpt-prep-partition? ( -- true|false ) - block gpt-part-entry>part-type-guid l@-le GPT-PREP-PARTITION-1 = IF - block gpt-part-entry>part-type-guid 4 + w@-le - GPT-PREP-PARTITION-2 = IF - block gpt-part-entry>part-type-guid 6 + w@-le - GPT-PREP-PARTITION-3 = IF - block gpt-part-entry>part-type-guid 8 + w@ - GPT-PREP-PARTITION-4 = IF - block gpt-part-entry>part-type-guid a + w@ - block gpt-part-entry>part-type-guid c + l@ swap lxjoin - GPT-PREP-PARTITION-5 = IF - TRUE EXIT - THEN - THEN - THEN - THEN + block gpt-part-entry>part-type-guid + dup l@-le GPT-PREP-PARTITION-1 <> IF drop false EXIT THEN + dup 4 + w@-le GPT-PREP-PARTITION-2 <> IF drop false EXIT THEN + dup 6 + w@-le GPT-PREP-PARTITION-3 <> IF drop false EXIT THEN + 8 + x@ GPT-PREP-PARTITION-4 = +; + +\ Check for GPT MSFT BASIC DATA GUID - fat based +EBD0A0A2 CONSTANT GPT-BASIC-DATA-PARTITION-1 +B9E5 CONSTANT GPT-BASIC-DATA-PARTITION-2 +4433 CONSTANT GPT-BASIC-DATA-PARTITION-3 +87C068B6B72699C7 CONSTANT GPT-BASIC-DATA-PARTITION-4 + +: gpt-basic-data-partition? ( -- true|false ) + block gpt-part-entry>part-type-guid + dup l@-le GPT-BASIC-DATA-PARTITION-1 <> IF drop false EXIT THEN + dup 4 + w@-le GPT-BASIC-DATA-PARTITION-2 <> IF drop false EXIT THEN + dup 6 + w@-le GPT-BASIC-DATA-PARTITION-3 <> IF drop false EXIT THEN + 8 + x@ GPT-BASIC-DATA-PARTITION-4 = +; + +\ +\ GPT Signature +\ ("EFI PART", 45h 46h 49h 20h 50h 41h 52h 54h) +\ +4546492050415254 CONSTANT GPT-SIGNATURE + +\ The routine checks whether the protective MBR has GPT ID and then +\ reads the gpt data from the sector. Also set the seek position and +\ the partition size used in caller routines. + +: get-gpt-partition ( -- true|false ) + no-gpt? IF false EXIT THEN + debug-disk-label? IF cr ." GPT partition found " cr THEN + 1 read-sector + block gpt>part-entry-lba x@-le + block-size * to seek-pos + block gpt>part-entry-size l@-le to gpt-part-size + gpt-part-size block-array-size > IF + cr ." GPT part size exceeds buffer allocated " cr + false exit THEN - FALSE + block gpt>signature x@ GPT-SIGNATURE = ; : load-from-gpt-prep-partition ( addr -- size ) - no-gpt? IF drop FALSE EXIT THEN - debug-disk-label? IF - cr ." GPT partition found " cr - THEN - 1 read-sector block gpt>part-entry-lba l@-le - block-size * to seek-pos - block gpt>part-entry-size l@-le to gpt-part-size - block gpt>num-part-entry l@-le dup 0= IF FALSE EXIT THEN + get-gpt-partition 0= IF false EXIT THEN + block gpt>num-part-entry l@-le dup 0= IF false exit THEN 1+ 1 ?DO seek-pos 0 seek drop block gpt-part-size read drop gpt-prep-partition? IF - debug-disk-label? IF - ." GPT PReP partition found " cr - THEN - block gpt-part-entry>first-lba x@ xbflip - block gpt-part-entry>last-lba x@ xbflip - over - 1+ ( addr offset len ) - swap ( addr len offset ) - block-size * to part-offset - 0 0 seek drop ( addr len ) - block-size * read ( size ) + debug-disk-label? IF ." GPT PReP partition found " cr THEN + block gpt-part-entry>first-lba x@-le ( addr first-lba ) + block gpt-part-entry>last-lba x@-le ( addr first-lba last-lba) + over - 1+ ( addr first-lba blocks ) + swap ( addr blocks first-lba ) + block-size * to part-offset ( addr blocks ) + 0 0 seek drop ( addr blocks ) + block-size * read ( size ) + UNLOOP EXIT + THEN + seek-pos gpt-part-size + to seek-pos + LOOP + false +; + +: try-gpt-dos-partition ( -- true|false ) + get-gpt-partition 0= IF false EXIT THEN + block gpt>num-part-entry l@-le dup 0= IF false EXIT THEN + 1+ 1 ?DO + seek-pos 0 seek drop + block gpt-part-size read drop + gpt-basic-data-partition? IF + debug-disk-label? IF ." GPT BASIC DATA partition found " cr THEN + block gpt-part-entry>first-lba x@-le ( first-lba ) + dup to part-start ( first-lba ) + block gpt-part-entry>last-lba x@-le ( first-lba last-lba ) + over - 1+ ( first-lba s1 ) + block-size * to part-size ( first-lba ) + block-size * to part-offset ( ) + 0 0 seek drop + block block-size read drop + block fat-bootblock? ( true|false ) UNLOOP EXIT THEN - seek-pos gpt-part-size i * + to seek-pos + seek-pos gpt-part-size + to seek-pos LOOP - FALSE + false ; \ Extract the boot loader path from a bootinfo.txt file @@ -493,7 +549,7 @@ AA26 CONSTANT GPT-PREP-PARTITION-4 debug-disk-label? IF ." Trying CHRP boot " .s cr THEN 1 disk-chrp-boot ! - dup load-chrp-boot-file ?dup 0 <> IF .s cr nip EXIT THEN + dup load-chrp-boot-file ?dup 0 <> IF nip EXIT THEN 0 disk-chrp-boot ! debug-disk-label? IF ." Trying GPT boot " .s cr THEN @@ -558,14 +614,7 @@ AA26 CONSTANT GPT-PREP-PARTITION-4 : try-dos-files ( -- found? ) no-mbr? IF false EXIT THEN - \ block 0 byte 0-2 is a jump instruction in all FAT - \ filesystems. - \ e9 and eb are jump instructions in x86 assembler. - block c@ e9 <> IF - block c@ eb <> - block 2+ c@ 90 <> or - IF false EXIT THEN - THEN + block fat-bootblock? 0= IF false EXIT THEN s" fat-files" (interpose-filesystem) true ; @@ -600,6 +649,7 @@ AA26 CONSTANT GPT-PREP-PARTITION-4 : try-partitions ( -- found? ) try-dos-partition IF try-files EXIT THEN + try-gpt-dos-partition IF try-files EXIT THEN \ try-iso9660-partition IF try-files EXIT THEN \ ... more partition types here... false @@ -610,7 +660,7 @@ AA26 CONSTANT GPT-PREP-PARTITION-4 : close ( -- ) debug-disk-label? IF ." Closing disk-label: block=0x" block u. ." block-size=" block-size .d cr THEN - block d# 4096 free-mem + block block-array-size free-mem ; diff --git a/qemu/roms/SLOF/slof/fs/packages/fat-files.fs b/qemu/roms/SLOF/slof/fs/packages/fat-files.fs index 0cec3664e..d9194527e 100644 --- a/qemu/roms/SLOF/slof/fs/packages/fat-files.fs +++ b/qemu/roms/SLOF/slof/fs/packages/fat-files.fs @@ -18,6 +18,7 @@ INSTANCE VARIABLE sectors/cluster INSTANCE VARIABLE #reserved-sectors INSTANCE VARIABLE #fats INSTANCE VARIABLE #root-entries +INSTANCE VARIABLE fat32-root-cluster INSTANCE VARIABLE total-#sectors INSTANCE VARIABLE media-descriptor INSTANCE VARIABLE sectors/fat @@ -59,9 +60,18 @@ INSTANCE VARIABLE next-cluster : read-cluster ( cluster# -- ) dup bytes/cluster @ * cluster-offset @ + bytes/cluster @ read-data read-fat dup #clusters @ >= IF drop 0 THEN next-cluster ! ; + : read-dir ( cluster# -- ) - ?dup 0= IF root-offset @ #root-entries @ 20 * read-data 0 next-cluster ! - ELSE read-cluster THEN ; + ?dup 0= IF + #root-entries @ 0= IF + fat32-root-cluster @ read-cluster + ELSE + root-offset @ #root-entries @ 20 * read-data 0 next-cluster ! + THEN + ELSE + read-cluster + THEN +; : .time ( x -- ) base @ >r decimal @@ -137,6 +147,7 @@ CREATE dos-name b allot \ For FAT32: sectors/fat @ 0= IF data @ 24 + 4c@ bljoin sectors/fat ! THEN + #root-entries @ 0= IF data @ 2c + 4c@ bljoin ELSE 0 THEN fat32-root-cluster ! \ XXX add other FAT32 stuff (offsets 28, 2c, 30) diff --git a/qemu/roms/SLOF/slof/fs/pci-config-bridge.fs b/qemu/roms/SLOF/slof/fs/pci-config-bridge.fs index 689325318..1efbcd804 100644 --- a/qemu/roms/SLOF/slof/fs/pci-config-bridge.fs +++ b/qemu/roms/SLOF/slof/fs/pci-config-bridge.fs @@ -81,26 +81,6 @@ 2drop ; -: dma-alloc ( ... size -- virt ) - \ ." dma-alloc called: " .s cr - alloc-mem -; - -: dma-free ( virt size -- ) - \ ." dma-free called: " .s cr - free-mem -; - -: dma-map-in ( ... virt size cacheable? -- devaddr ) - \ ." dma-map-in called: " .s cr - 2drop -; - -: dma-map-out ( virt devaddr size -- ) - \ ." dma-map-out called: " .s cr - 2drop drop -; - : dma-sync ( virt devaddr size -- ) \ XXX should we add at least a memory barrier here? \ ." dma-sync called: " .s cr diff --git a/qemu/roms/SLOF/slof/fs/pci-scan.fs b/qemu/roms/SLOF/slof/fs/pci-scan.fs index b8b9fe61f..2fdf0e8f5 100644 --- a/qemu/roms/SLOF/slof/fs/pci-scan.fs +++ b/qemu/roms/SLOF/slof/fs/pci-scan.fs @@ -110,10 +110,13 @@ here 100 allot CONSTANT pci-device-vec dup 100000 + pci-next-mem ! \ and write back with 1MB for bridge over 24 + rtas-config-w@ \ check if 64bit support 1 and IF \ IF 64 bit support - 2dup 20 rshift \ | keep upper 32 bits - swap 28 + rtas-config-l! \ | and write it into the Base-Upper32-bits - pci-max-mem @ 20 rshift \ | fetch max Limit address and keep upper 32 bits - 2 pick 2C + rtas-config-l! \ | and set the Limit + pci-next-mem64 @ 100000000 #aligned \ | read the current Value of 64-bit and align to 4GB boundary + dup 100000000 + pci-next-mem64 x! \ | and write back with 1GB for bridge + 2 pick swap \ | + 20 rshift \ | keep upper 32 bits + swap 28 + rtas-config-l! \ | and write it into the Base-Upper32-bits + pci-max-mem64 @ 20 rshift \ | fetch max Limit address and keep upper 32 bits + 2 pick 2C + rtas-config-l! \ | and set the Limit THEN \ FI 10 rshift \ keep upper 16 bits pci-max-mem @ 1- FFFF0000 and or \ and Insert mmem Limit (set it to max) @@ -129,8 +132,12 @@ here 100 allot CONSTANT pci-device-vec 1- \ make limit one less than boundary over 24 + rtas-config-w@ \ check if 64bit support 1 and IF \ IF 64 bit support - 2dup 20 rshift \ | keep upper 32 bits - swap 2C + rtas-config-l! \ | and write it into the Limit-Upper32-bits + pci-next-mem64 @ 100000000 #aligned \ | Reat current value of 64-bar and align at 4GB + dup pci-next-mem64 x! \ | and write it back + 1- \ | make limite one less than boundary + 2 pick swap \ | + 20 rshift \ | keep upper 32 bits + swap 2C + rtas-config-l! \ | and write it into the Limit-Upper32-bits THEN \ FI FFFF0000 and \ keep upper 16 bits over 24 + rtas-config-l@ 0000FFFF and \ fetch original Value diff --git a/qemu/roms/SLOF/slof/fs/rmove.fs b/qemu/roms/SLOF/slof/fs/rmove.fs deleted file mode 100644 index c28dba9c4..000000000 --- a/qemu/roms/SLOF/slof/fs/rmove.fs +++ /dev/null @@ -1,53 +0,0 @@ -\ ***************************************************************************** -\ * Copyright (c) 2004, 2008 IBM Corporation -\ * All rights reserved. -\ * This program and the accompanying materials -\ * are made available under the terms of the BSD License -\ * which accompanies this distribution, and is available at -\ * http://www.opensource.org/licenses/bsd-license.php -\ * -\ * Contributors: -\ * IBM Corporation - initial implementation -\ ****************************************************************************/ - -defer '(r@) -defer '(r!) -1 VALUE /(r) - - -\ The rest of the code already implemented in prim.in -\ In the end all of this should be moved over there and this file terminated - -: (rfill) ( addr size pattern 'r! /r -- ) - to /(r) to '(r!) ff and - dup 8 lshift or dup 10 lshift or dup 20 lshift or - -rot bounds ?do dup i '(r!) /(r) +loop drop -; - -: (fwrmove) ( src dest size -- ) - >r 0 -rot r> bounds ?do + dup '(r@) i '(r!) /(r) dup +loop 2drop -; - -\ Move from main to device memory -: mrmove ( src dest size -- ) - 3dup or or 7 AND CASE - 0 OF ['] x@ ['] rx! /x ENDOF - 4 OF ['] l@ ['] rl! /l ENDOF - 2 OF ['] w@ ['] rw! /w ENDOF - dup OF ['] c@ ['] rb! /c ENDOF - ENDCASE - ( We already know that source and destination do not overlap ) - to /(r) to '(r!) to '(r@) (fwrmove) -; - -: rfill ( addr size pattern -- ) - 3dup drop or 7 AND CASE - 0 OF ['] rx! /x ENDOF - 4 OF ['] rl! /l ENDOF - 2 OF ['] rw! /w ENDOF - dup OF ['] rb! /c ENDOF - ENDCASE (rfill) -; - - - diff --git a/qemu/roms/SLOF/slof/fs/root.fs b/qemu/roms/SLOF/slof/fs/root.fs index 21c710951..952b00e75 100644 --- a/qemu/roms/SLOF/slof/fs/root.fs +++ b/qemu/roms/SLOF/slof/fs/root.fs @@ -77,7 +77,6 @@ finish-device : open true ; : close ; -#include \ Finish root finish-device diff --git a/qemu/roms/SLOF/slof/fs/terminal.fs b/qemu/roms/SLOF/slof/fs/terminal.fs index 582bedeb3..dc82e7bf4 100644 --- a/qemu/roms/SLOF/slof/fs/terminal.fs +++ b/qemu/roms/SLOF/slof/fs/terminal.fs @@ -167,6 +167,7 @@ false VALUE stopcsi CREATE twtracebuf 4000 allot twtracebuf 4000 erase twtracebuf VALUE twbp 0 VALUE twbc +0 VALUE twtrace-enabled? : twtrace twbc 4000 = IF 0 to twbc twtracebuf to twbp THEN @@ -176,7 +177,7 @@ twtracebuf VALUE twbp : terminal-write ( addr len -- actual-len ) cursor-off tuck bounds ?DO i c@ - twtrace + twtrace-enabled? IF twtrace THEN esc-on IF esc-process ELSE CASE 1B OF true to esc-on ENDOF diff --git a/qemu/roms/SLOF/slof/helpers.c b/qemu/roms/SLOF/slof/helpers.c index d7c1888b4..48c34a65c 100644 --- a/qemu/roms/SLOF/slof/helpers.c +++ b/qemu/roms/SLOF/slof/helpers.c @@ -114,6 +114,13 @@ long SLOF_pci_config_read16(long offset) return forth_pop(); } +long SLOF_pci_config_read8(long offset) +{ + forth_push(offset); + forth_eval("config-b@"); + return forth_pop(); +} + void SLOF_pci_config_write32(long offset, long value) { forth_push(value); @@ -128,6 +135,13 @@ void SLOF_pci_config_write16(long offset, long value) forth_eval("config-w!"); } +void SLOF_pci_config_write8(long offset, long value) +{ + forth_push(value); + forth_push(offset); + forth_eval("config-b!"); +} + void *SLOF_translate_my_address(void *addr) { forth_push((long)addr); diff --git a/qemu/roms/SLOF/slof/paflof.c b/qemu/roms/SLOF/slof/paflof.c index 624955fba..2fc25c81e 100644 --- a/qemu/roms/SLOF/slof/paflof.c +++ b/qemu/roms/SLOF/slof/paflof.c @@ -19,6 +19,7 @@ #undef unix #include "paflof.h" +#include #include #include #include @@ -43,6 +44,9 @@ unsigned char hash_table[HASHSIZE*CELLSIZE]; #include ISTR(TARG,c) +static int did_stackwarning; +extern char the_system_stack[]; + // the actual engine long engine(int mode, long param_1, long param_2) { @@ -84,6 +88,11 @@ long engine(int mode, long param_1, long param_2) c_return[1].a = &dummy; } + if ((char *)&ip < the_system_stack && !did_stackwarning) { + puts("ERROR: stack overflow in engine()!"); + did_stackwarning = 1; + } + if (mode & ENGINE_MODE_PARAM_2) { (++dp)->n = param_2; } diff --git a/qemu/roms/SLOF/slof/ppc64.c b/qemu/roms/SLOF/slof/ppc64.c index 20d927069..619d95ec7 100644 --- a/qemu/roms/SLOF/slof/ppc64.c +++ b/qemu/roms/SLOF/slof/ppc64.c @@ -42,24 +42,7 @@ cell *the_heap_start = &the_heap[0]; cell *the_heap_end = &the_heap[HEAP_SIZE / CELLSIZE]; extern void io_putchar(unsigned char); - - -static unsigned long __attribute__((noinline)) -call_c(cell arg0, cell arg1, cell arg2, cell entry) -{ - register unsigned long r3 asm("r3") = arg0.u; - register unsigned long r4 asm("r4") = arg1.u; - register unsigned long r5 asm("r5") = arg2.u; - register unsigned long r6 = entry.u ; - - asm volatile("mflr 31 ; mtctr %4 ; bctrl ; mtlr 31" - : "=r" (r3) - : "r" (r3), "r" (r4), "r" (r5), "r" (r6) - : "ctr", "r6", "r7", "r8", "r9", "r10", "r11", - "r12", "r13", "r31", "lr", "cc"); - - return r3; -} +extern unsigned long call_c(cell arg0, cell arg1, cell arg2, cell entry); long diff --git a/qemu/roms/SLOF/slof/prim.code b/qemu/roms/SLOF/slof/prim.code index 9fbed7168..bb9e036a9 100644 --- a/qemu/roms/SLOF/slof/prim.code +++ b/qemu/roms/SLOF/slof/prim.code @@ -520,6 +520,19 @@ PRIM(RMOVE) MIRP +PRIM(MRMOVE) + type_u size = TOS.u; POP; + void *d = TOS.a; POP; + void *s = TOS.a; POP; + FAST_MRMOVE(s, d, size); + MIRP + +PRIM(RFILL) + type_u pat = TOS.u; POP; + type_u size = TOS.u; POP; + void *dst = TOS.a; POP; + FAST_RFILL(dst, size, pat); + MIRP // String compare, case insensitive: // : string=ci ( str1 len1 str2 len2 -- equal? ) diff --git a/qemu/roms/SLOF/slof/prim.in b/qemu/roms/SLOF/slof/prim.in index 7a0d6a2ed..855f59262 100644 --- a/qemu/roms/SLOF/slof/prim.in +++ b/qemu/roms/SLOF/slof/prim.in @@ -104,8 +104,9 @@ cod(SEMICOLON) cod(EXECUTE) cod(MOVE) -// cod(RMOVE64) cod(RMOVE) +cod(MRMOVE) +cod(RFILL) cod(ZCOUNT) con(HASH-SIZE HASHSIZE) cod(HASH) diff --git a/qemu/roms/config.ipxe.general.h b/qemu/roms/config.ipxe.general.h deleted file mode 100644 index 619ee4c15..000000000 --- a/qemu/roms/config.ipxe.general.h +++ /dev/null @@ -1,4 +0,0 @@ -#undef BANNER_TIMEOUT -#define BANNER_TIMEOUT 30 -#undef ROM_BANNER_TIMEOUT -#define ROM_BANNER_TIMEOUT 0 diff --git a/qemu/roms/config.seabios-128k b/qemu/roms/config.seabios-128k index c719ba689..0a9da77a5 100644 --- a/qemu/roms/config.seabios-128k +++ b/qemu/roms/config.seabios-128k @@ -3,6 +3,8 @@ CONFIG_QEMU=y CONFIG_ROM_SIZE=128 CONFIG_XEN=n +CONFIG_USB_OHCI=n CONFIG_USB_XHCI=n CONFIG_USB_UAS=n CONFIG_SDCARD=n +CONFIG_TCGBIOS=n diff --git a/qemu/roms/ipxe/COPYING b/qemu/roms/ipxe/COPYING index a43ea2126..342330bb9 100644 --- a/qemu/roms/ipxe/COPYING +++ b/qemu/roms/ipxe/COPYING @@ -1,339 +1,12 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 +In general iPXE files are licensed under the GPL. For historical +reasons, individual files may contain their own licence declarations. +Most builds of iPXE do not contain all iPXE code (in particular, most +builds will include only one driver), and so the overall licence can +vary depending on what target you are building. - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 675 Mass Ave, Cambridge, MA 02139, USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. +The resultant applicable licence(s) for any particular build can be +determined by using "make bin/xxxxxxx.yyy.licence"; for example: - Preamble + make bin/rtl8139.rom.licence - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - Appendix: How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) 19yy - - 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, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) 19yy name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. +to determine the resultant licence(s) for the build bin/rtl8139.rom diff --git a/qemu/roms/ipxe/COPYING.GPLv2 b/qemu/roms/ipxe/COPYING.GPLv2 new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/qemu/roms/ipxe/COPYING.GPLv2 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/qemu/roms/ipxe/COPYING.UBDL b/qemu/roms/ipxe/COPYING.UBDL new file mode 100644 index 000000000..780ddcd77 --- /dev/null +++ b/qemu/roms/ipxe/COPYING.UBDL @@ -0,0 +1,59 @@ +UNMODIFIED BINARY DISTRIBUTION LICENCE + + +PREAMBLE + +The GNU General Public License provides a legal guarantee that +software covered by it remains free (in the sense of freedom, not +price). It achieves this guarantee by imposing obligations on anyone +who chooses to distribute the software. + +Some of these obligations may be seen as unnecessarily burdensome. In +particular, when the source code for the software is already publicly +and freely available, there is minimal value in imposing upon each +distributor the obligation to provide the complete source code (or an +equivalent written offer to provide the complete source code). + +This Licence allows for the distribution of unmodified binaries built +from publicly available source code, without imposing the obligations +of the GNU General Public License upon anyone who chooses to +distribute only the unmodified binaries built from that source code. + +The extra permissions granted by this Licence apply only to unmodified +binaries built from source code which has already been made available +to the public in accordance with the terms of the GNU General Public +Licence. Nothing in this Licence allows for the creation of +closed-source modified versions of the Program. Any modified versions +of the Program are subject to the usual terms and conditions of the +GNU General Public License. + + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +This Licence applies to any Program or other work which contains a +notice placed by the copyright holder saying it may be distributed +under the terms of this Unmodified Binary Distribution Licence. All +terms used in the text of this Licence are to be interpreted as they +are used in version 2 of the GNU General Public License as published +by the Free Software Foundation. + +If you have made this Program available to the public in both source +code and executable form in accordance with 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, then you are hereby granted an additional permission to use, +copy, and distribute the unmodified executable form of this Program +(the "Unmodified Binary") without restriction, including the right to +permit persons to whom the Unmodified Binary is furnished to do +likewise, subject to the following conditions: + +- when started running, the Program must display an announcement which + includes the details of your existing publication of the Program + made in accordance with the terms of the GNU General Public License. + For example, the Program could display the URL of the publicly + available source code from which the Unmodified Binary was built. + +- when exercising your right to grant permissions under this Licence, + you do not need to refer directly to the text of this Licence, but + you may not grant permissions beyond those granted to you by this + Licence. diff --git a/qemu/roms/ipxe/COPYRIGHTS b/qemu/roms/ipxe/COPYRIGHTS deleted file mode 100644 index 342330bb9..000000000 --- a/qemu/roms/ipxe/COPYRIGHTS +++ /dev/null @@ -1,12 +0,0 @@ -In general iPXE files are licensed under the GPL. For historical -reasons, individual files may contain their own licence declarations. -Most builds of iPXE do not contain all iPXE code (in particular, most -builds will include only one driver), and so the overall licence can -vary depending on what target you are building. - -The resultant applicable licence(s) for any particular build can be -determined by using "make bin/xxxxxxx.yyy.licence"; for example: - - make bin/rtl8139.rom.licence - -to determine the resultant licence(s) for the build bin/rtl8139.rom diff --git a/qemu/roms/ipxe/src/Makefile b/qemu/roms/ipxe/src/Makefile index b742d1283..2a9cc9e8f 100644 --- a/qemu/roms/ipxe/src/Makefile +++ b/qemu/roms/ipxe/src/Makefile @@ -83,11 +83,13 @@ SRCDIRS += drivers/block SRCDIRS += drivers/nvs SRCDIRS += drivers/bitbash SRCDIRS += drivers/infiniband +SRCDIRS += drivers/usb SRCDIRS += interface/pxe interface/efi interface/smbios SRCDIRS += interface/bofm SRCDIRS += interface/xen +SRCDIRS += interface/hyperv SRCDIRS += tests -SRCDIRS += crypto crypto/axtls crypto/matrixssl +SRCDIRS += crypto crypto/mishmash SRCDIRS += hci hci/commands hci/tui SRCDIRS += hci/mucurses hci/mucurses/widgets SRCDIRS += hci/keymap diff --git a/qemu/roms/ipxe/src/Makefile.housekeeping b/qemu/roms/ipxe/src/Makefile.housekeeping index 1a75d3939..03800c8ef 100644 --- a/qemu/roms/ipxe/src/Makefile.housekeeping +++ b/qemu/roms/ipxe/src/Makefile.housekeeping @@ -157,17 +157,6 @@ SP_FLAGS := $(shell $(SP_TEST) && $(ECHO) '-fno-stack-protector') WORKAROUND_CFLAGS += $(SP_FLAGS) endif -# Some widespread patched versions of gcc include -fPIE -Wl,-pie by -# default. Note that gcc will exit *successfully* if it fails to -# recognise an option that starts with "no", so we have to test for -# output on stderr instead of checking the exit status. -# -ifeq ($(CCTYPE),gcc) -PIE_TEST = [ -z "`$(CC) -fno-PIE -nopie -x c -c /dev/null -o /dev/null 2>&1`" ] -PIE_FLAGS := $(shell $(PIE_TEST) && $(ECHO) '-fno-PIE -nopie') -WORKAROUND_CFLAGS += $(PIE_FLAGS) -endif - # gcc 4.4 generates .eh_frame sections by default, which distort the # output of "size". Inhibit this. # @@ -533,6 +522,7 @@ endif # COMPILE_c = $(CC) $(CFLAGS) $(CFLAGS_c) $(OBJ_CFLAGS) RULE_c = $(Q)$(COMPILE_c) -c $< -o $@ $(POST_O) +RULE_c_to_ids.o = $(Q)$(ECHO_E) '$(OBJ_IDS_ASM_NL)' | $(ASSEMBLE_S) -o $@ RULE_c_to_dbg%.o = $(Q)$(COMPILE_c) -DDBGLVL_MAX=$* -c $< -o $@ $(POST_O) RULE_c_to_c = $(Q)$(COMPILE_c) -E -c $< > $@ RULE_c_to_s = $(Q)$(COMPILE_c) -S -g0 -c $< -o $@ @@ -543,7 +533,7 @@ RULE_S = $(Q)$(PREPROCESS_S) $< | $(ASSEMBLE_S) -o $@ RULE_S_to_dbg%.o = $(Q)$(PREPROCESS_S) -DDBGLVL_MAX=$* $< | $(ASSEMBLE_S) -o $@ RULE_S_to_s = $(Q)$(PREPROCESS_S) $< > $@ -DEBUG_TARGETS += dbg%.o c s +GENERIC_TARGETS += ids.o dbg%.o c s # List of embedded images included in the last build of embedded.o. # This is needed in order to correctly rebuild embedded.o whenever the @@ -775,8 +765,6 @@ define deps_template_parts $(Q)$(CPP) $(CFLAGS) $(CFLAGS_$(2)) $(CFLAGS_$(3)) -DOBJECT=$(3) \ -Wno-error -M $(1) -MG -MP | \ sed 's/\.o\s*:/_DEPS +=/' > $(BIN)/deps/$(1).d - $(Q)$(if $(findstring drivers/,$(1)),\ - $(PERL) $(PARSEROM) $(1) >> $(BIN)/deps/$(1).d) endef # rules_template : generate rules for a given source file @@ -796,7 +784,7 @@ $$(BIN)/$(3).o : $(1) $$(MAKEDEPS) $$(POST_O_DEPS) $$($(3)_DEPS) $$(QM)$(ECHO) " [BUILD] $$@" $$(RULE_$(2)) BOBJS += $$(BIN)/$(3).o -$(foreach TGT,$(DEBUG_TARGETS),$(if $(RULE_$(2)_to_$(TGT)),$(NEWLINE)$(call rules_template_target,$(1),$(2),$(3),$(TGT)))) +$(foreach TGT,$(GENERIC_TARGETS),$(if $(RULE_$(2)_to_$(TGT)),$(NEWLINE)$(call rules_template_target,$(1),$(2),$(3),$(TGT)))) $$(BIN)/deps/$(1).d : $$($(3)_DEPS) TAGS : $$($(3)_DEPS) endef @@ -824,7 +812,7 @@ endef # Generate the dependency files # -$(BIN)/deps/%.d : % $(MAKEDEPS) $(PARSEROM) +$(BIN)/deps/%.d : % $(MAKEDEPS) $(call deps_template_file,$<) # Calculate list of dependency files @@ -866,10 +854,69 @@ endif endif endif -# The following variables are created by the rules files +# Files to be parsed using parserom.pl +# +ROM_SRCS = $(foreach SRC,$(AUTO_SRCS),\ + $(if $(findstring drivers/,$(SRC)),$(SRC))) +romsrcs : + @$(ECHO) $(ROM_SRCS) + +# List of files to be parsed using parserom.pl +# +ROM_SRCS_LIST := $(BIN)/.rom.list +ifeq ($(wildcard $(ROM_SRCS_LIST)),) +ROM_SRCS_OLD := +else +ROM_SRCS_OLD := $(shell cat $(ROM_SRCS_LIST)) +endif +ifneq ($(ROM_SRCS_OLD),$(ROM_SRCS)) +$(shell $(ECHO) "$(ROM_SRCS)" > $(ROM_SRCS_LIST)) +endif + +$(ROM_SRCS_LIST) : $(MAKEDEPS) + +VERYCLEANUP += $(ROM_SRCS_LIST) + +# ROM definition file +# +ROMDEFS = $(BIN)/.rom.defs +$(ROMDEFS) : $(ROM_SRCS) $(ROM_SRCS_LIST) $(PARSEROM) $(MAKEDEPS) + $(QM)$(ECHO) " [PARSEROM]" + $(Q)$(PERL) $(PARSEROM) $(ROM_SRCS) > $@ + +VERYCLEANUP += $(ROMDEFS) + +# Evaluate ROM definition file +ifdef NEED_DEPS +ifneq ($(ROM_SRCS),) +-include $(ROMDEFS) +endif +endif + +# Device ID tables (using IDs from ROM definition file) +# +define obj_pci_id_asm + .section ".pci_devlist.$(1)", "a", @progbits + .globl pci_devlist_$(1) +pci_devlist_$(1): + .short ( 0x$(1) & 0xffff ) + +endef +define obj_isa_id_asm +endef +OBJ_IDS_ASM = $(foreach ROM,$(ROMS_$(OBJECT)),$(call obj_$(ROM_TYPE_$(ROM))_id_asm,$(ROM))) +OBJ_IDS_ASM_NL = $(subst $(NEWLINE),\n,$(OBJ_IDS_ASM)) +$(BIN)/%.ids : + @$(ECHO_E) '$(OBJ_IDS_ASM_NL)' + +BOBJS += $(patsubst %,$(BIN)/%.ids.o,$(DRIVERS)) + +# The following variables are created by the autogenerated rules # bobjs : @$(ECHO) $(BOBJS) +drivers_% : + @$(ECHO) $(DRIVERS_$*) drivers : @$(ECHO) $(DRIVERS) .PHONY : drivers @@ -900,6 +947,11 @@ $(BIN)/NIC : $(AUTO_DEPS) @perl -ne 'chomp; print "$$1\n" if /\# NIC\t(.*)$$/' $^ >> $@ CLEANUP += $(BIN)/NIC # Doesn't match the $(BIN)/*.* pattern +# Select drivers to be included in the all-drivers build +# +DRIVERS_ipxe = $(DRIVERS_net) $(DRIVERS_infiniband) \ + $(DRIVERS_xen) $(DRIVERS_hyperv) + # Analyse a target name (e.g. "bin/dfe538--prism2_pci.rom.tmp") and # derive the variables: # @@ -908,7 +960,6 @@ CLEANUP += $(BIN)/NIC # Doesn't match the $(BIN)/*.* pattern # TGT_DRIVERS : the driver for each element (e.g. "rtl8139 prism2_pci") # TGT_ROM_NAME : the ROM name (e.g. "dfe538") # -DRIVERS_ipxe = $(DRIVERS) CARD_DRIVER = $(firstword $(DRIVER_$(1)) $(1)) TGT_ELEMENTS = $(subst --, ,$(firstword $(subst ., ,$(notdir $@)))) TGT_ROM_NAME = $(firstword $(TGT_ELEMENTS)) @@ -941,6 +992,8 @@ TGT_PCI_DEVICE = $(PCI_DEVICE_$(TGT_ROM_NAME)) TGT_LD_DRIVERS = $(subst -,_,$(patsubst %,obj_%,$(TGT_DRIVERS))) TGT_LD_IDS = pci_vendor_id=$(firstword $(TGT_PCI_VENDOR) 0) \ pci_device_id=$(firstword $(TGT_PCI_DEVICE) 0) +TGT_LD_DEVLIST = $(foreach ELEM,$(TGT_ELEMENTS),$(if $(PCI_VENDOR_$(ELEM)),\ + pci_devlist_$(patsubst 0x%,%,$(PCI_VENDOR_$(ELEM)))$(patsubst 0x%,%,$(PCI_DEVICE_$(ELEM))))) TGT_LD_ENTRY = _$(TGT_PREFIX)_start # Calculate linker flags based on link-time options for the current @@ -951,7 +1004,8 @@ TGT_LD_ENTRY = _$(TGT_PREFIX)_start # "-u obj_zpciprefix -u obj_rtl8139 -u obj_prism2_pci # --defsym pci_vendor=0x1186 --defsym pci_device=0x1300") # -TGT_LD_FLAGS = $(foreach SYM,$(TGT_LD_ENTRY) $(TGT_LD_DRIVERS) obj_config,\ +TGT_LD_FLAGS = $(foreach SYM,$(TGT_LD_ENTRY) $(TGT_LD_DRIVERS) \ + $(TGT_LD_DEVLIST) obj_config,\ -u $(SYM) --defsym check_$(SYM)=$(SYM) ) \ $(patsubst %,--defsym %,$(TGT_LD_IDS)) \ -e $(TGT_LD_ENTRY) @@ -981,6 +1035,7 @@ $(BIN)/%.info : @$(ECHO) @$(ECHO) 'LD driver symbols : $(TGT_LD_DRIVERS)' @$(ECHO) 'LD ID symbols : $(TGT_LD_IDS)' + @$(ECHO) 'LD devlist symbols : $(TGT_LD_DEVLIST)' @$(ECHO) 'LD entry point : $(TGT_LD_ENTRY)' @$(ECHO) @$(ECHO) 'LD target flags : $(TGT_LD_FLAGS)' @@ -1012,7 +1067,7 @@ BLIB = $(BIN)/blib.a $(BLIB) : $(BLIB_OBJS) $(BLIB_LIST) $(MAKEDEPS) $(Q)$(RM) $(BLIB) $(QM)$(ECHO) " [AR] $@" - $(Q)$(AR) r $@ $(BLIB_OBJS) + $(Q)$(AR) r $@ $(sort $(BLIB_OBJS)) $(Q)$(RANLIB) $@ blib : $(BLIB) @@ -1231,15 +1286,12 @@ endif # defined(BIN) # # The compression utilities # -$(NRV2B) : util/nrv2b.c $(MAKEDEPS) - $(QM)$(ECHO) " [HOSTCC] $@" - $(Q)$(HOST_CC) $(HOST_CFLAGS) -DENCODE -DDECODE -DMAIN -DVERBOSE \ - -DNDEBUG -DBITSIZE=32 -DENDIAN=0 -o $@ $< -CLEANUP += $(NRV2B) -$(ZBIN) : util/zbin.c util/nrv2b.c $(MAKEDEPS) +ZBIN_LDFLAGS := -llzma + +$(ZBIN) : util/zbin.c $(MAKEDEPS) $(QM)$(ECHO) " [HOSTCC] $@" - $(Q)$(HOST_CC) $(HOST_CFLAGS) -o $@ $< + $(Q)$(HOST_CC) $(HOST_CFLAGS) $< $(ZBIN_LDFLAGS) -o $@ CLEANUP += $(ZBIN) ############################################################################### @@ -1317,31 +1369,6 @@ $(CONFIG_LOCAL_NAMED_HEADERS) : endif -############################################################################### -# -# Auto-incrementing build serial number. Append "bs" to your list of -# build targets to get a serial number printed at the end of the -# build. Enable -DBUILD_SERIAL in order to see it when the code runs. -# -BUILDSERIAL_H = config/.buildserial.h -BUILDSERIAL_NOW = config/.buildserial.now -BUILDSERIAL_NEXT = config/.buildserial.next - -$(BUILDSERIAL_NOW) $(BUILDSERIAL_NEXT) : - $(ECHO) 1 > $@ - -$(BUILDSERIAL_H) : $(BUILDSERIAL_NOW) $(BUILDSERIAL_NEXT) - $(ECHO) '#define BUILD_SERIAL_NUM $(shell cat $<)' > $@ - -ifeq ($(filter bs,$(MAKECMDGOALS)),bs) -$(shell diff -q $(BUILDSERIAL_NOW) $(BUILDSERIAL_NEXT) > /dev/null || \ - cp -f $(BUILDSERIAL_NEXT) $(BUILDSERIAL_NOW)) -endif - -bs : $(BUILDSERIAL_NOW) - @$(ECHO) $$(( $(shell cat $<) + 1 )) > $(BUILDSERIAL_NEXT) - @$(ECHO) "Build serial number is $(shell cat $<)" - ############################################################################### # # Build the TAGS file(s) for emacs diff --git a/qemu/roms/ipxe/src/arch/i386/Makefile b/qemu/roms/ipxe/src/arch/i386/Makefile index 4925cc4e6..99f875314 100644 --- a/qemu/roms/ipxe/src/arch/i386/Makefile +++ b/qemu/roms/ipxe/src/arch/i386/Makefile @@ -69,6 +69,17 @@ CFLAGS += -fshort-wchar # CFLAGS += -Ui386 +# Some widespread patched versions of gcc include -fPIE -Wl,-pie by +# default. Note that gcc will exit *successfully* if it fails to +# recognise an option that starts with "no", so we have to test for +# output on stderr instead of checking the exit status. +# +ifeq ($(CCTYPE),gcc) +PIE_TEST = [ -z "`$(CC) -fno-PIE -nopie -x c -c /dev/null -o /dev/null 2>&1`" ] +PIE_FLAGS := $(shell $(PIE_TEST) && $(ECHO) '-fno-PIE -nopie') +WORKAROUND_CFLAGS += $(PIE_FLAGS) +endif + # Define version string for lkrnprefix.S # CFLAGS_lkrnprefix += -DVERSION="\"$(VERSION)\"" diff --git a/qemu/roms/ipxe/src/arch/i386/core/basemem_packet.c b/qemu/roms/ipxe/src/arch/i386/core/basemem_packet.c index 06ffa3bbd..9f5fbf330 100644 --- a/qemu/roms/ipxe/src/arch/i386/core/basemem_packet.c +++ b/qemu/roms/ipxe/src/arch/i386/core/basemem_packet.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/arch/i386/core/cachedhcp.c b/qemu/roms/ipxe/src/arch/i386/core/cachedhcp.c index 3cac28e7d..a5c624035 100644 --- a/qemu/roms/ipxe/src/arch/i386/core/cachedhcp.c +++ b/qemu/roms/ipxe/src/arch/i386/core/cachedhcp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/core/gdbmach.c b/qemu/roms/ipxe/src/arch/i386/core/gdbmach.c index 4d6897f7d..d92a4ac08 100644 --- a/qemu/roms/ipxe/src/arch/i386/core/gdbmach.c +++ b/qemu/roms/ipxe/src/arch/i386/core/gdbmach.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/core/patch_cf.S b/qemu/roms/ipxe/src/arch/i386/core/patch_cf.S index 97a62f494..4365563fe 100644 --- a/qemu/roms/ipxe/src/arch/i386/core/patch_cf.S +++ b/qemu/roms/ipxe/src/arch/i386/core/patch_cf.S @@ -14,9 +14,13 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .text .arch i386 diff --git a/qemu/roms/ipxe/src/arch/i386/core/pci_autoboot.c b/qemu/roms/ipxe/src/arch/i386/core/pci_autoboot.c index a3eb1f97d..337598091 100644 --- a/qemu/roms/ipxe/src/arch/i386/core/pci_autoboot.c +++ b/qemu/roms/ipxe/src/arch/i386/core/pci_autoboot.c @@ -16,9 +16,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/core/rdtsc_timer.c b/qemu/roms/ipxe/src/arch/i386/core/rdtsc_timer.c index 2f31afc66..e720a239c 100644 --- a/qemu/roms/ipxe/src/arch/i386/core/rdtsc_timer.c +++ b/qemu/roms/ipxe/src/arch/i386/core/rdtsc_timer.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -27,7 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include -#include +#include /** * Number of TSC ticks per microsecond @@ -56,10 +60,10 @@ static void rdtsc_udelay ( unsigned long usecs ) { elapsed = ( currticks() - start ); } while ( elapsed < ( usecs * rdtsc_ticks_per_usec ) ); } else { - /* Not yet calibrated; use timer2 and calibrate + /* Not yet calibrated; use 8254 PIT and calibrate * based on result. */ - timer2_udelay ( usecs ); + pit8254_udelay ( usecs ); elapsed = ( currticks() - start ); rdtsc_ticks_per_usec = ( elapsed / usecs ); DBG ( "RDTSC timer calibrated: %ld ticks in %ld usecs " diff --git a/qemu/roms/ipxe/src/arch/i386/core/relocate.c b/qemu/roms/ipxe/src/arch/i386/core/relocate.c index 5fbf2d2c2..54ad387e4 100644 --- a/qemu/roms/ipxe/src/arch/i386/core/relocate.c +++ b/qemu/roms/ipxe/src/arch/i386/core/relocate.c @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* * The linker passes in the symbol _max_align, which is the alignment diff --git a/qemu/roms/ipxe/src/arch/i386/core/runtime.c b/qemu/roms/ipxe/src/arch/i386/core/runtime.c index 18ca7936e..d160fee04 100644 --- a/qemu/roms/ipxe/src/arch/i386/core/runtime.c +++ b/qemu/roms/ipxe/src/arch/i386/core/runtime.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/arch/i386/core/setjmp.S b/qemu/roms/ipxe/src/arch/i386/core/setjmp.S index 03727148c..81d3b4911 100644 --- a/qemu/roms/ipxe/src/arch/i386/core/setjmp.S +++ b/qemu/roms/ipxe/src/arch/i386/core/setjmp.S @@ -1,42 +1,64 @@ -/* setjmp and longjmp. Use of these functions is deprecated. */ - -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .text .arch i386 .code32 - -/************************************************************************** -SETJMP - Save stack context for non-local goto -**************************************************************************/ + + /* Must match jmp_buf structure layout */ + .struct 0 +env_retaddr: .long 0 +env_stack: .long 0 +env_ebx: .long 0 +env_esi: .long 0 +env_edi: .long 0 +env_ebp: .long 0 + .previous + +/* + * Save stack context for non-local goto + */ .globl setjmp setjmp: - movl 4(%esp),%ecx /* jmpbuf */ - movl 0(%esp),%edx /* return address */ - movl %edx,0(%ecx) - movl %ebx,4(%ecx) - movl %esp,8(%ecx) - movl %ebp,12(%ecx) - movl %esi,16(%ecx) - movl %edi,20(%ecx) - movl $0,%eax + /* Get jmp_buf pointer in %edx */ + movl 4(%esp),%edx + /* Save return address */ + movl 0(%esp),%eax + movl %eax, env_retaddr(%edx) + /* Save stack pointer */ + movl %esp, env_stack(%edx) + /* Save other registers */ + movl %ebx, env_ebx(%edx) + movl %esi, env_esi(%edx) + movl %edi, env_edi(%edx) + movl %ebp, env_ebp(%edx) + /* Return 0 when returning as setjmp() */ + xorl %eax, %eax ret + .size setjmp, . - setjmp -/************************************************************************** -LONGJMP - Non-local jump to a saved stack context -**************************************************************************/ +/* + * Non-local jump to a saved stack context + */ .globl longjmp longjmp: - movl 4(%esp),%edx /* jumpbuf */ - movl 8(%esp),%eax /* result */ - movl 0(%edx),%ecx - movl 4(%edx),%ebx - movl 8(%edx),%esp - movl 12(%edx),%ebp - movl 16(%edx),%esi - movl 20(%edx),%edi - cmpl $0,%eax - jne 1f - movl $1,%eax -1: movl %ecx,0(%esp) + /* Get jmp_buf pointer in %edx */ + movl 4(%esp),%edx + /* Get result in %eax */ + movl 8(%esp),%eax + /* Force result to non-zero */ + testl %eax, %eax + jnz 1f + incl %eax +1: /* Restore stack pointer */ + movl env_stack(%edx), %esp + /* Restore other registers */ + movl env_ebx(%edx), %ebx + movl env_esi(%edx), %esi + movl env_edi(%edx), %edi + movl env_ebp(%edx), %ebp + /* Replace return address on the new stack */ + popl %ecx /* discard */ + pushl env_retaddr(%edx) + /* Return to setjmp() caller */ ret + .size longjmp, . - longjmp diff --git a/qemu/roms/ipxe/src/arch/i386/core/stack.S b/qemu/roms/ipxe/src/arch/i386/core/stack.S index 737ec0eed..98f1cd9b9 100644 --- a/qemu/roms/ipxe/src/arch/i386/core/stack.S +++ b/qemu/roms/ipxe/src/arch/i386/core/stack.S @@ -1,4 +1,4 @@ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .arch i386 diff --git a/qemu/roms/ipxe/src/arch/i386/core/stack16.S b/qemu/roms/ipxe/src/arch/i386/core/stack16.S index 523f0288b..4bc6f081a 100644 --- a/qemu/roms/ipxe/src/arch/i386/core/stack16.S +++ b/qemu/roms/ipxe/src/arch/i386/core/stack16.S @@ -1,4 +1,4 @@ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .arch i386 diff --git a/qemu/roms/ipxe/src/arch/i386/core/timer2.c b/qemu/roms/ipxe/src/arch/i386/core/timer2.c deleted file mode 100644 index 077866562..000000000 --- a/qemu/roms/ipxe/src/arch/i386/core/timer2.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * arch/i386/core/i386_timer.c - * - * Use the "System Timer 2" to implement the udelay callback in - * the BIOS timer driver. Also used to calibrate the clock rate - * in the RTDSC timer driver. - * - * 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) any later version. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include -#include -#include - -/* Timers tick over at this rate */ -#define TIMER2_TICKS_PER_SEC 1193180U - -/* Parallel Peripheral Controller Port B */ -#define PPC_PORTB 0x61 - -/* Meaning of the port bits */ -#define PPCB_T2OUT 0x20 /* Bit 5 */ -#define PPCB_SPKR 0x02 /* Bit 1 */ -#define PPCB_T2GATE 0x01 /* Bit 0 */ - -/* Ports for the 8254 timer chip */ -#define TIMER2_PORT 0x42 -#define TIMER_MODE_PORT 0x43 - -/* Meaning of the mode bits */ -#define TIMER0_SEL 0x00 -#define TIMER1_SEL 0x40 -#define TIMER2_SEL 0x80 -#define READBACK_SEL 0xC0 - -#define LATCH_COUNT 0x00 -#define LOBYTE_ACCESS 0x10 -#define HIBYTE_ACCESS 0x20 -#define WORD_ACCESS 0x30 - -#define MODE0 0x00 -#define MODE1 0x02 -#define MODE2 0x04 -#define MODE3 0x06 -#define MODE4 0x08 -#define MODE5 0x0A - -#define BINARY_COUNT 0x00 -#define BCD_COUNT 0x01 - -static void load_timer2 ( unsigned int ticks ) { - /* - * Now let's take care of PPC channel 2 - * - * Set the Gate high, program PPC channel 2 for mode 0, - * (interrupt on terminal count mode), binary count, - * load 5 * LATCH count, (LSB and MSB) to begin countdown. - * - * Note some implementations have a bug where the high bits byte - * of channel 2 is ignored. - */ - /* Set up the timer gate, turn off the speaker */ - /* Set the Gate high, disable speaker */ - outb((inb(PPC_PORTB) & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB); - /* binary, mode 0, LSB/MSB, Ch 2 */ - outb(TIMER2_SEL|WORD_ACCESS|MODE0|BINARY_COUNT, TIMER_MODE_PORT); - /* LSB of ticks */ - outb(ticks & 0xFF, TIMER2_PORT); - /* MSB of ticks */ - outb(ticks >> 8, TIMER2_PORT); -} - -static int timer2_running ( void ) { - return ((inb(PPC_PORTB) & PPCB_T2OUT) == 0); -} - -void timer2_udelay ( unsigned long usecs ) { - load_timer2 ( ( usecs * TIMER2_TICKS_PER_SEC ) / ( 1000 * 1000 ) ); - while (timer2_running()) { - /* Do nothing */ - } -} diff --git a/qemu/roms/ipxe/src/arch/i386/core/virtaddr.S b/qemu/roms/ipxe/src/arch/i386/core/virtaddr.S index 5e5d77352..425591570 100644 --- a/qemu/roms/ipxe/src/arch/i386/core/virtaddr.S +++ b/qemu/roms/ipxe/src/arch/i386/core/virtaddr.S @@ -4,7 +4,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #include "librm.h" diff --git a/qemu/roms/ipxe/src/arch/i386/drivers/net/undi.c b/qemu/roms/ipxe/src/arch/i386/drivers/net/undi.c index 2bc54824c..9820cf629 100644 --- a/qemu/roms/ipxe/src/arch/i386/drivers/net/undi.c +++ b/qemu/roms/ipxe/src/arch/i386/drivers/net/undi.c @@ -68,10 +68,6 @@ static int undipci_probe ( struct pci_device *pci ) { struct undi_rom *undirom; int rc; - /* Ignore non-network devices */ - if ( PCI_BASE_CLASS ( pci->class ) != PCI_BASE_CLASS_NETWORK ) - return -ENOTTY; - /* Allocate UNDI device structure */ undi = zalloc ( sizeof ( *undi ) ); if ( ! undi ) @@ -138,12 +134,13 @@ static void undipci_remove ( struct pci_device *pci ) { } static struct pci_device_id undipci_nics[] = { -PCI_ROM ( 0xffff, 0xffff, "undipci", "UNDI (PCI)", 0 ), + PCI_ROM ( 0xffff, 0xffff, "undipci", "UNDI (PCI)", 0 ), }; struct pci_driver undipci_driver __pci_driver_fallback = { .ids = undipci_nics, .id_count = ( sizeof ( undipci_nics ) / sizeof ( undipci_nics[0] ) ), + .class = PCI_CLASS_ID ( PCI_CLASS_NETWORK, PCI_ANY_ID, PCI_ANY_ID ), .probe = undipci_probe, .remove = undipci_remove, }; diff --git a/qemu/roms/ipxe/src/arch/i386/drivers/net/undiload.c b/qemu/roms/ipxe/src/arch/i386/drivers/net/undiload.c index 77134dcb8..7160ee384 100644 --- a/qemu/roms/ipxe/src/arch/i386/drivers/net/undiload.c +++ b/qemu/roms/ipxe/src/arch/i386/drivers/net/undiload.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/drivers/net/undionly.c b/qemu/roms/ipxe/src/arch/i386/drivers/net/undionly.c index 028fac5d9..70dbe4bfd 100644 --- a/qemu/roms/ipxe/src/arch/i386/drivers/net/undionly.c +++ b/qemu/roms/ipxe/src/arch/i386/drivers/net/undionly.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/drivers/net/undipreload.c b/qemu/roms/ipxe/src/arch/i386/drivers/net/undipreload.c index 81d7a80eb..fca771843 100644 --- a/qemu/roms/ipxe/src/arch/i386/drivers/net/undipreload.c +++ b/qemu/roms/ipxe/src/arch/i386/drivers/net/undipreload.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/basemem.c b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/basemem.c index b23f2c356..6a46081aa 100644 --- a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/basemem.c +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/basemem.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c index bd73838b5..63413cdc1 100644 --- a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -39,6 +43,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ATTR_FCOL_YELLOW 0x06 #define ATTR_FCOL_WHITE 0x07 +#define ATTR_BLINK 0x80 + #define ATTR_BCOL_MASK 0x70 #define ATTR_BCOL_BLACK 0x00 #define ATTR_BCOL_BLUE 0x10 @@ -137,8 +143,12 @@ static void bios_handle_sgr ( struct ansiesc_context *ctx __unused, bios_attr = ATTR_DEFAULT; } else if ( aspect == 1 ) { bios_attr |= ATTR_BOLD; + } else if ( aspect == 5 ) { + bios_attr |= ATTR_BLINK; } else if ( aspect == 22 ) { bios_attr &= ~ATTR_BOLD; + } else if ( aspect == 25 ) { + bios_attr &= ~ATTR_BLINK; } else if ( ( aspect >= 30 ) && ( aspect <= 39 ) ) { bios_attr &= ~ATTR_FCOL_MASK; bios_attr |= bios_attr_fcols[ aspect - 30 ]; diff --git a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S index cea17ef8e..d5d97b482 100644 --- a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .text .arch i386 diff --git a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/fakee820.c b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/fakee820.c index e5f713728..15f4d772f 100644 --- a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/fakee820.c +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/fakee820.c @@ -14,9 +14,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c index 8f3069e18..253c601ff 100644 --- a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c @@ -14,9 +14,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/memmap.c b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/memmap.c index 0937a7ce2..bcacecd6a 100644 --- a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/memmap.c +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/memmap.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/pnpbios.c b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/pnpbios.c index 5c74b0431..20ec35d75 100644 --- a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/pnpbios.c +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/pnpbios.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/hci/commands/pxe_cmd.c b/qemu/roms/ipxe/src/arch/i386/hci/commands/pxe_cmd.c index 523724ab0..473b97f97 100644 --- a/qemu/roms/ipxe/src/arch/i386/hci/commands/pxe_cmd.c +++ b/qemu/roms/ipxe/src/arch/i386/hci/commands/pxe_cmd.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #include @@ -23,7 +27,7 @@ #include #include -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/arch/i386/image/bootsector.c b/qemu/roms/ipxe/src/arch/i386/image/bootsector.c index 9a089e6bb..dba87613c 100644 --- a/qemu/roms/ipxe/src/arch/i386/image/bootsector.c +++ b/qemu/roms/ipxe/src/arch/i386/image/bootsector.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/arch/i386/image/bzimage.c b/qemu/roms/ipxe/src/arch/i386/image/bzimage.c index 4865c394c..a64206cd3 100644 --- a/qemu/roms/ipxe/src/arch/i386/image/bzimage.c +++ b/qemu/roms/ipxe/src/arch/i386/image/bzimage.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/arch/i386/image/elfboot.c b/qemu/roms/ipxe/src/arch/i386/image/elfboot.c index 0f6957f02..dc3568929 100644 --- a/qemu/roms/ipxe/src/arch/i386/image/elfboot.c +++ b/qemu/roms/ipxe/src/arch/i386/image/elfboot.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -74,6 +78,27 @@ static int elfboot_exec ( struct image *image ) { return -ECANCELED; /* -EIMPOSSIBLE, anyone? */ } +/** + * Check that ELF segment uses flat physical addressing + * + * @v image ELF file + * @v phdr ELF program header + * @v dest Destination address + * @ret rc Return status code + */ +static int elfboot_check_segment ( struct image *image, Elf_Phdr *phdr, + physaddr_t dest ) { + + /* Check that ELF segment uses flat physical addressing */ + if ( phdr->p_vaddr != dest ) { + DBGC ( image, "ELF %p uses virtual addressing (phys %x, " + "virt %x)\n", image, phdr->p_paddr, phdr->p_vaddr ); + return -ENOEXEC; + } + + return 0; +} + /** * Probe ELF image * @@ -91,14 +116,24 @@ static int elfboot_probe ( struct image *image ) { [EI_DATA] = ELFDATA2LSB, [EI_VERSION] = EV_CURRENT, }; + physaddr_t entry; + physaddr_t max; + int rc; /* Read ELF header */ copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) ); if ( memcmp ( ehdr.e_ident, e_ident, sizeof ( e_ident ) ) != 0 ) { - DBG ( "Invalid ELF identifier\n" ); + DBGC ( image, "Invalid ELF identifier\n" ); return -ENOEXEC; } + /* Check that this image uses flat physical addressing */ + if ( ( rc = elf_segments ( image, &ehdr, elfboot_check_segment, + &entry, &max ) ) != 0 ) { + DBGC ( image, "Unloadable ELF image\n" ); + return rc; + } + return 0; } diff --git a/qemu/roms/ipxe/src/arch/i386/image/initrd.c b/qemu/roms/ipxe/src/arch/i386/image/initrd.c index eaba3a645..80c197417 100644 --- a/qemu/roms/ipxe/src/arch/i386/image/initrd.c +++ b/qemu/roms/ipxe/src/arch/i386/image/initrd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/image/multiboot.c b/qemu/roms/ipxe/src/arch/i386/image/multiboot.c index 86b0bc12d..0c85df708 100644 --- a/qemu/roms/ipxe/src/arch/i386/image/multiboot.c +++ b/qemu/roms/ipxe/src/arch/i386/image/multiboot.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/arch/i386/image/pxe_image.c b/qemu/roms/ipxe/src/arch/i386/image/pxe_image.c index dc28f6082..5b0f6eb89 100644 --- a/qemu/roms/ipxe/src/arch/i386/image/pxe_image.c +++ b/qemu/roms/ipxe/src/arch/i386/image/pxe_image.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file @@ -34,6 +38,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include +#include FEATURE ( FEATURE_IMAGE, "PXE", DHCP_EB_FEATURE_PXE, 1 ); @@ -121,9 +127,45 @@ int pxe_probe ( struct image *image ) { return 0; } +/** + * Probe PXE image (with rejection of potential EFI images) + * + * @v image PXE file + * @ret rc Return status code + */ +int pxe_probe_no_mz ( struct image *image ) { + uint16_t magic; + int rc; + + /* Probe PXE image */ + if ( ( rc = pxe_probe ( image ) ) != 0 ) + return rc; + + /* Reject image with an "MZ" signature which may indicate an + * EFI image incorrectly handed out to a BIOS system. + */ + if ( image->len >= sizeof ( magic ) ) { + copy_from_user ( &magic, image->data, 0, sizeof ( magic ) ); + if ( magic == cpu_to_le16 ( EFI_IMAGE_DOS_SIGNATURE ) ) { + DBGC ( image, "IMAGE %p may be an EFI image\n", + image ); + return -ENOTTY; + } + } + + return 0; +} + /** PXE image type */ -struct image_type pxe_image_type __image_type ( PROBE_PXE ) = { - .name = "PXE", - .probe = pxe_probe, - .exec = pxe_exec, +struct image_type pxe_image_type[] __image_type ( PROBE_PXE ) = { + { + .name = "PXE-NBP", + .probe = pxe_probe_no_mz, + .exec = pxe_exec, + }, + { + .name = "PXE-NBP (may be EFI?)", + .probe = pxe_probe, + .exec = pxe_exec, + }, }; diff --git a/qemu/roms/ipxe/src/arch/i386/image/sdi.c b/qemu/roms/ipxe/src/arch/i386/image/sdi.c index df1c3a868..fa2d0b73f 100644 --- a/qemu/roms/ipxe/src/arch/i386/image/sdi.c +++ b/qemu/roms/ipxe/src/arch/i386/image/sdi.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/basemem.h b/qemu/roms/ipxe/src/arch/i386/include/basemem.h index c477c7fe2..01c2ea917 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/basemem.h +++ b/qemu/roms/ipxe/src/arch/i386/include/basemem.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/basemem_packet.h b/qemu/roms/ipxe/src/arch/i386/include/basemem_packet.h index 3cb477671..def6dee31 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/basemem_packet.h +++ b/qemu/roms/ipxe/src/arch/i386/include/basemem_packet.h @@ -1,7 +1,7 @@ #ifndef BASEMEM_PACKET_H #define BASEMEM_PACKET_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/bios.h b/qemu/roms/ipxe/src/arch/i386/include/bios.h index 0754b1168..988bbc62b 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/bios.h +++ b/qemu/roms/ipxe/src/arch/i386/include/bios.h @@ -1,7 +1,7 @@ #ifndef BIOS_H #define BIOS_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define BDA_SEG 0x0040 #define BDA_EQUIPMENT_WORD 0x0010 diff --git a/qemu/roms/ipxe/src/arch/i386/include/biosint.h b/qemu/roms/ipxe/src/arch/i386/include/biosint.h index ab466af3c..67d6a3811 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/biosint.h +++ b/qemu/roms/ipxe/src/arch/i386/include/biosint.h @@ -6,7 +6,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/byteswap.h b/qemu/roms/ipxe/src/arch/i386/include/bits/byteswap.h index 0d9cb967c..53b6a454d 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/bits/byteswap.h +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/byteswap.h @@ -9,7 +9,7 @@ #include -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); static inline __attribute__ (( always_inline, const )) uint16_t __bswap_variable_16 ( uint16_t x ) { diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/compiler.h b/qemu/roms/ipxe/src/arch/i386/include/bits/compiler.h index d48b4b385..87201135f 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/bits/compiler.h +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/compiler.h @@ -1,7 +1,10 @@ #ifndef _BITS_COMPILER_H #define _BITS_COMPILER_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** Dummy relocation type */ +#define RELOC_TYPE_NONE R_386_NONE #ifndef ASSEMBLY diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/endian.h b/qemu/roms/ipxe/src/arch/i386/include/bits/endian.h deleted file mode 100644 index 841885424..000000000 --- a/qemu/roms/ipxe/src/arch/i386/include/bits/endian.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef ETHERBOOT_BITS_ENDIAN_H -#define ETHERBOOT_BITS_ENDIAN_H - -FILE_LICENCE ( GPL2_OR_LATER ); - -#define __BYTE_ORDER __LITTLE_ENDIAN - -#endif /* ETHERBOOT_BITS_ENDIAN_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/entropy.h b/qemu/roms/ipxe/src/arch/i386/include/bits/entropy.h index 6dcceec6d..bfeb5e3b5 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/bits/entropy.h +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/entropy.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/hyperv.h b/qemu/roms/ipxe/src/arch/i386/include/bits/hyperv.h new file mode 100644 index 000000000..3565c8a83 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/hyperv.h @@ -0,0 +1,72 @@ +#ifndef _BITS_HYPERV_H +#define _BITS_HYPERV_H + +/** @file + * + * Hyper-V interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** + * Issue hypercall + * + * @v hv Hyper-V hypervisor + * @v code Call code + * @v in Input parameters + * @v out Output parameters + * @ret status Status code + */ +static inline __attribute__ (( always_inline )) int +hv_call ( struct hv_hypervisor *hv, unsigned int code, const void *in, + void *out ) { + void *hypercall = hv->hypercall; + uint32_t in_phys; + uint32_t out_phys; + uint32_t discard_ecx; + uint32_t discard_edx; + uint16_t result; + + in_phys = ( ( __builtin_constant_p ( in ) && ( in == NULL ) ) + ? 0 : virt_to_phys ( in ) ); + out_phys = ( ( __builtin_constant_p ( out ) && ( out == NULL ) ) + ? 0 : virt_to_phys ( out ) ); + __asm__ __volatile__ ( "call *%9" + : "=a" ( result ), "=c" ( discard_ecx ), + "=d" ( discard_edx ) + : "d" ( 0 ), "a" ( code ), + "b" ( 0 ), "c" ( in_phys ), + "D" ( 0 ), "S" ( out_phys ), + "m" ( hypercall ) ); + return result; +} + +/** + * Set bit atomically + * + * @v bits Bit field + * @v bit Bit to set + */ +static inline __attribute__ (( always_inline )) void +hv_set_bit ( void *bits, unsigned int bit ) { + struct { + uint32_t dword[ ( bit / 32 ) + 1 ]; + } *dwords = bits; + + /* Set bit using "lock bts". Inform compiler that any memory + * from the start of the bit field up to and including the + * dword containing this bit may be modified. (This is + * overkill but shouldn't matter in practice since we're + * unlikely to subsequently read other bits from the same bit + * field.) + */ + __asm__ __volatile__ ( "lock bts %1, %0" + : "+m" ( *dwords ) : "Ir" ( bit ) ); +} + +#endif /* _BITS_HYPERV_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/nap.h b/qemu/roms/ipxe/src/arch/i386/include/bits/nap.h index 64066e6ab..e8bcfd13b 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/bits/nap.h +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/nap.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/profile.h b/qemu/roms/ipxe/src/arch/i386/include/bits/profile.h index f3ee54ae9..e184d7b51 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/bits/profile.h +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/profile.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/reboot.h b/qemu/roms/ipxe/src/arch/i386/include/bits/reboot.h index 5b09e95f7..803dacfe4 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/bits/reboot.h +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/reboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/sanboot.h b/qemu/roms/ipxe/src/arch/i386/include/bits/sanboot.h index 9c77a4d42..f02d2e649 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/bits/sanboot.h +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/sanboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/smbios.h b/qemu/roms/ipxe/src/arch/i386/include/bits/smbios.h index cc79eec51..2ab31e74b 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/bits/smbios.h +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/smbios.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/stdint.h b/qemu/roms/ipxe/src/arch/i386/include/bits/stdint.h index 8edf13192..fe1f9946a 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/bits/stdint.h +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/stdint.h @@ -1,7 +1,7 @@ #ifndef _BITS_STDINT_H #define _BITS_STDINT_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); typedef __SIZE_TYPE__ size_t; typedef signed long ssize_t; diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/strings.h b/qemu/roms/ipxe/src/arch/i386/include/bits/strings.h index 092bcb593..453545f00 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/bits/strings.h +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/strings.h @@ -1,7 +1,51 @@ #ifndef _BITS_STRINGS_H #define _BITS_STRINGS_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * Find first (i.e. least significant) set bit + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __ffsl ( long value ) { + long lsb_minus_one; + + /* If the input value is zero, the BSF instruction returns + * ZF=0 and leaves an undefined value in the output register. + * Perform this check in C rather than asm so that it can be + * omitted in cases where the compiler is able to prove that + * the input is non-zero. + */ + if ( value ) { + __asm__ ( "bsfl %1, %0" + : "=r" ( lsb_minus_one ) + : "rm" ( value ) ); + return ( lsb_minus_one + 1 ); + } else { + return 0; + } +} + +/** + * Find first (i.e. least significant) set bit + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __ffsll ( long long value ){ + unsigned long high = ( value >> 32 ); + unsigned long low = ( value >> 0 ); + + if ( low ) { + return ( __ffsl ( low ) ); + } else if ( high ) { + return ( 32 + __ffsl ( high ) ); + } else { + return 0; + } +} /** * Find last (i.e. most significant) set bit @@ -13,7 +57,7 @@ static inline __attribute__ (( always_inline )) int __flsl ( long value ) { long msb_minus_one; /* If the input value is zero, the BSR instruction returns - * ZF=1 and leaves an undefined value in the output register. + * ZF=0 and leaves an undefined value in the output register. * Perform this check in C rather than asm so that it can be * omitted in cases where the compiler is able to prove that * the input is non-zero. diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/time.h b/qemu/roms/ipxe/src/arch/i386/include/bits/time.h index 24dd020e9..6a5d63d32 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/bits/time.h +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/time.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/timer.h b/qemu/roms/ipxe/src/arch/i386/include/bits/timer.h index 50b676b77..f7d86d78c 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/bits/timer.h +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/timer.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/uaccess.h b/qemu/roms/ipxe/src/arch/i386/include/bits/uaccess.h index 2bb52e021..aac09ba95 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/bits/uaccess.h +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/uaccess.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/umalloc.h b/qemu/roms/ipxe/src/arch/i386/include/bits/umalloc.h index 54fb006f0..113f16fd1 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/bits/umalloc.h +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/umalloc.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/bootsector.h b/qemu/roms/ipxe/src/arch/i386/include/bootsector.h index 8730fbfcc..c5d35aae3 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/bootsector.h +++ b/qemu/roms/ipxe/src/arch/i386/include/bootsector.h @@ -6,7 +6,7 @@ * x86 bootsector image format */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern int call_bootsector ( unsigned int segment, unsigned int offset, unsigned int drive ); diff --git a/qemu/roms/ipxe/src/arch/i386/include/bzimage.h b/qemu/roms/ipxe/src/arch/i386/include/bzimage.h index 7e42e3188..4933ce5b1 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/bzimage.h +++ b/qemu/roms/ipxe/src/arch/i386/include/bzimage.h @@ -1,7 +1,7 @@ #ifndef _BZIMAGE_H #define _BZIMAGE_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/efi/ipxe/dhcp_arch.h b/qemu/roms/ipxe/src/arch/i386/include/efi/ipxe/dhcp_arch.h index 184177219..c17c1ea5e 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/efi/ipxe/dhcp_arch.h +++ b/qemu/roms/ipxe/src/arch/i386/include/efi/ipxe/dhcp_arch.h @@ -4,7 +4,7 @@ * 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 any later version. + * 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 @@ -13,7 +13,12 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #ifndef _DHCP_ARCH_H @@ -24,7 +29,7 @@ * Architecture-specific DHCP options */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/fakee820.h b/qemu/roms/ipxe/src/arch/i386/include/fakee820.h index 9d00fb670..552b1e48d 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/fakee820.h +++ b/qemu/roms/ipxe/src/arch/i386/include/fakee820.h @@ -1,7 +1,7 @@ #ifndef _FAKEE820_H #define _FAKEE820_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern void fake_e820 ( void ); extern void unfake_e820 ( void ); diff --git a/qemu/roms/ipxe/src/arch/i386/include/initrd.h b/qemu/roms/ipxe/src/arch/i386/include/initrd.h index a5659f43c..ddb3e5a45 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/initrd.h +++ b/qemu/roms/ipxe/src/arch/i386/include/initrd.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/int13.h b/qemu/roms/ipxe/src/arch/i386/include/int13.h index e337ca1d1..f82a583c6 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/int13.h +++ b/qemu/roms/ipxe/src/arch/i386/include/int13.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_nap.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_nap.h index 5b684c041..c9b82c1e5 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_nap.h +++ b/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_nap.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef NAP_PCBIOS #define NAP_PREFIX_pcbios diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_reboot.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_reboot.h index a0845328d..3f6df9073 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_reboot.h +++ b/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_reboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef REBOOT_PCBIOS #define REBOOT_PREFIX_pcbios diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_sanboot.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_sanboot.h index 689227b70..1a86b7d57 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_sanboot.h +++ b/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_sanboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef SANBOOT_PCBIOS #define SANBOOT_PREFIX_pcbios diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_smbios.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_smbios.h index d8c7f648a..9f7f9c8ff 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_smbios.h +++ b/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_smbios.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef SMBIOS_PCBIOS #define SMBIOS_PREFIX_pcbios diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_timer.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_timer.h index f9fc80412..6b88a623c 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_timer.h +++ b/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_timer.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef TIMER_PCBIOS #define TIMER_PREFIX_pcbios @@ -15,7 +15,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define TIMER_PREFIX_pcbios __pcbios_ #endif -#include +#include /** * Delay for a fixed number of microseconds @@ -25,9 +25,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); static inline __always_inline void TIMER_INLINE ( pcbios, udelay ) ( unsigned long usecs ) { /* BIOS timer is not high-resolution enough for udelay(), so - * we use timer2 + * we use the 8254 Programmable Interval Timer. */ - timer2_udelay ( usecs ); + pit8254_udelay ( usecs ); } /** diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/errno/pcbios.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/errno/pcbios.h index 3a9eb2495..6312adaa4 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/ipxe/errno/pcbios.h +++ b/qemu/roms/ipxe/src/arch/i386/include/ipxe/errno/pcbios.h @@ -10,7 +10,7 @@ * for the PC-BIOS platform. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/guestrpc.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/guestrpc.h index 72a0f714f..bc3d85506 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/ipxe/guestrpc.h +++ b/qemu/roms/ipxe/src/arch/i386/include/ipxe/guestrpc.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/memtop_umalloc.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/memtop_umalloc.h index 001648fe5..dee055d16 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/ipxe/memtop_umalloc.h +++ b/qemu/roms/ipxe/src/arch/i386/include/ipxe/memtop_umalloc.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef UMALLOC_MEMTOP #define UMALLOC_PREFIX_memtop diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/msr.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/msr.h index c88e26a39..5705318fd 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/ipxe/msr.h +++ b/qemu/roms/ipxe/src/arch/i386/include/ipxe/msr.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Read model-specific register diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/rdtsc_timer.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/rdtsc_timer.h index 472e14007..598f4bb08 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/ipxe/rdtsc_timer.h +++ b/qemu/roms/ipxe/src/arch/i386/include/ipxe/rdtsc_timer.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef TIMER_RDTSC #define TIMER_PREFIX_rdtsc diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/rtc_entropy.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/rtc_entropy.h index 6c3cf2104..e214745d0 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/ipxe/rtc_entropy.h +++ b/qemu/roms/ipxe/src/arch/i386/include/ipxe/rtc_entropy.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/rtc_time.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/rtc_time.h index c0dfe3f88..cb8c7f49e 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/ipxe/rtc_time.h +++ b/qemu/roms/ipxe/src/arch/i386/include/ipxe/rtc_time.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef TIME_RTC #define TIME_PREFIX_rtc diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/timer2.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/timer2.h deleted file mode 100644 index 322a3ed59..000000000 --- a/qemu/roms/ipxe/src/arch/i386/include/ipxe/timer2.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _IPXE_TIMER2_H -#define _IPXE_TIMER2_H - -/** @file - * - * Timer chip control - * - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -extern void timer2_udelay ( unsigned long usecs ); - -#endif /* _IPXE_TIMER2_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/vesafb.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/vesafb.h index 48cd6a7b7..efc8f2cb8 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/ipxe/vesafb.h +++ b/qemu/roms/ipxe/src/arch/i386/include/ipxe/vesafb.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/vmware.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/vmware.h index 2ac65f436..24f60a03a 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/ipxe/vmware.h +++ b/qemu/roms/ipxe/src/arch/i386/include/ipxe/vmware.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/librm.h b/qemu/roms/ipxe/src/arch/i386/include/librm.h index c8ba72b53..a8a578a39 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/librm.h +++ b/qemu/roms/ipxe/src/arch/i386/include/librm.h @@ -1,7 +1,7 @@ #ifndef LIBRM_H #define LIBRM_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Segment selectors as used in our protected-mode GDTs. * @@ -170,18 +170,6 @@ extern uint16_t __text16 ( rm_cs ); extern uint16_t __text16 ( rm_ds ); #define rm_ds __use_text16 ( rm_ds ) -/** - * Convert segment:offset address to user buffer - * - * @v segment Real-mode segment - * @v offset Real-mode offset - * @ret buffer User buffer - */ -static inline __always_inline userptr_t -real_to_user ( unsigned int segment, unsigned int offset ) { - return ( phys_to_user ( ( segment << 4 ) + offset ) ); -} - extern uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ); extern void remove_user_from_rm_stack ( userptr_t data, size_t size ); diff --git a/qemu/roms/ipxe/src/arch/i386/include/limits.h b/qemu/roms/ipxe/src/arch/i386/include/limits.h index 031b6c57a..bb48b75ab 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/limits.h +++ b/qemu/roms/ipxe/src/arch/i386/include/limits.h @@ -1,7 +1,7 @@ #ifndef LIMITS_H #define LIMITS_H 1 -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Number of bits in a `char' */ #define CHAR_BIT 8 diff --git a/qemu/roms/ipxe/src/arch/i386/include/memsizes.h b/qemu/roms/ipxe/src/arch/i386/include/memsizes.h index 7b217494a..f115f7574 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/memsizes.h +++ b/qemu/roms/ipxe/src/arch/i386/include/memsizes.h @@ -1,7 +1,7 @@ #ifndef _MEMSIZES_H #define _MEMSIZES_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/multiboot.h b/qemu/roms/ipxe/src/arch/i386/include/multiboot.h index 44614c73a..ae09df6c7 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/multiboot.h +++ b/qemu/roms/ipxe/src/arch/i386/include/multiboot.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h b/qemu/roms/ipxe/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h index a36d9cfa1..e07e4c192 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h +++ b/qemu/roms/ipxe/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h @@ -4,7 +4,7 @@ * 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 any later version. + * 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 @@ -13,7 +13,12 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #ifndef _DHCP_ARCH_H @@ -24,7 +29,7 @@ * Architecture-specific DHCP options */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/pic8259.h b/qemu/roms/ipxe/src/arch/i386/include/pic8259.h deleted file mode 100644 index a07e97d30..000000000 --- a/qemu/roms/ipxe/src/arch/i386/include/pic8259.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Basic support for controlling the 8259 Programmable Interrupt Controllers. - * - * Initially written by Michael Brown (mcb30). - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#ifndef PIC8259_H -#define PIC8259_H - -#include - -/* For segoff_t */ -#include "realmode.h" - -#define IRQ_PIC_CUTOFF 8 - -/* 8259 register locations */ -#define PIC1_ICW1 0x20 -#define PIC1_OCW2 0x20 -#define PIC1_OCW3 0x20 -#define PIC1_ICR 0x20 -#define PIC1_IRR 0x20 -#define PIC1_ISR 0x20 -#define PIC1_ICW2 0x21 -#define PIC1_ICW3 0x21 -#define PIC1_ICW4 0x21 -#define PIC1_IMR 0x21 -#define PIC2_ICW1 0xa0 -#define PIC2_OCW2 0xa0 -#define PIC2_OCW3 0xa0 -#define PIC2_ICR 0xa0 -#define PIC2_IRR 0xa0 -#define PIC2_ISR 0xa0 -#define PIC2_ICW2 0xa1 -#define PIC2_ICW3 0xa1 -#define PIC2_ICW4 0xa1 -#define PIC2_IMR 0xa1 - -/* Register command values */ -#define OCW3_ID 0x08 -#define OCW3_READ_IRR 0x03 -#define OCW3_READ_ISR 0x02 -#define ICR_EOI_NON_SPECIFIC 0x20 -#define ICR_EOI_NOP 0x40 -#define ICR_EOI_SPECIFIC 0x60 -#define ICR_EOI_SET_PRIORITY 0xc0 - -/* Macros to enable/disable IRQs */ -#define IMR_REG(x) ( (x) < IRQ_PIC_CUTOFF ? PIC1_IMR : PIC2_IMR ) -#define IMR_BIT(x) ( 1 << ( (x) % IRQ_PIC_CUTOFF ) ) -#define irq_enabled(x) ( ( inb ( IMR_REG(x) ) & IMR_BIT(x) ) == 0 ) -#define enable_irq(x) outb ( inb( IMR_REG(x) ) & ~IMR_BIT(x), IMR_REG(x) ) -#define disable_irq(x) outb ( inb( IMR_REG(x) ) | IMR_BIT(x), IMR_REG(x) ) - -/* Macros for acknowledging IRQs */ -#define ICR_REG( irq ) ( (irq) < IRQ_PIC_CUTOFF ? PIC1_ICR : PIC2_ICR ) -#define ICR_VALUE( irq ) ( (irq) % IRQ_PIC_CUTOFF ) -#define CHAINED_IRQ 2 - -/* Utility macros to convert IRQ numbers to INT numbers and INT vectors */ -#define IRQ_INT( irq ) ( ( ( (irq) - IRQ_PIC_CUTOFF ) ^ 0x70 ) & 0x7f ) - -/* Other constants */ -#define IRQ_MAX 15 -#define IRQ_NONE -1U - -/* Function prototypes - */ -void send_eoi ( unsigned int irq ); - -#endif /* PIC8259_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/pnpbios.h b/qemu/roms/ipxe/src/arch/i386/include/pnpbios.h index 4c20e73ed..d14873700 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/pnpbios.h +++ b/qemu/roms/ipxe/src/arch/i386/include/pnpbios.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* BIOS segment address */ #define BIOS_SEG 0xf000 diff --git a/qemu/roms/ipxe/src/arch/i386/include/pxe.h b/qemu/roms/ipxe/src/arch/i386/include/pxe.h index b95b0cce5..66d752683 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/pxe.h +++ b/qemu/roms/ipxe/src/arch/i386/include/pxe.h @@ -1,7 +1,7 @@ #ifndef PXE_H #define PXE_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include "pxe_types.h" #include "pxe_error.h" diff --git a/qemu/roms/ipxe/src/arch/i386/include/pxe_api.h b/qemu/roms/ipxe/src/arch/i386/include/pxe_api.h index e4396efb2..3110d26da 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/pxe_api.h +++ b/qemu/roms/ipxe/src/arch/i386/include/pxe_api.h @@ -17,6 +17,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * * As an alternative, at your option, you may use this file under the * following terms, known as the "MIT license": * @@ -49,7 +53,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include "pxe_types.h" diff --git a/qemu/roms/ipxe/src/arch/i386/include/pxe_call.h b/qemu/roms/ipxe/src/arch/i386/include/pxe_call.h index 45af46549..cbd548318 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/pxe_call.h +++ b/qemu/roms/ipxe/src/arch/i386/include/pxe_call.h @@ -6,7 +6,7 @@ * PXE API entry point */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/pxe_error.h b/qemu/roms/ipxe/src/arch/i386/include/pxe_error.h index a1398cbd4..51298e665 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/pxe_error.h +++ b/qemu/roms/ipxe/src/arch/i386/include/pxe_error.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @defgroup pxeerrors PXE error codes diff --git a/qemu/roms/ipxe/src/arch/i386/include/pxe_types.h b/qemu/roms/ipxe/src/arch/i386/include/pxe_types.h index db8214591..483666e33 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/pxe_types.h +++ b/qemu/roms/ipxe/src/arch/i386/include/pxe_types.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include /* PXE status codes */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/realmode.h b/qemu/roms/ipxe/src/arch/i386/include/realmode.h index dafc5a32a..4defd3b97 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/realmode.h +++ b/qemu/roms/ipxe/src/arch/i386/include/realmode.h @@ -10,7 +10,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* * Declaration of variables in .data16 @@ -64,6 +64,18 @@ FILE_LICENCE ( GPL2_OR_LATER ); * assembler output to make sure that it's doing the right thing. */ +/** + * Convert segment:offset address to user buffer + * + * @v segment Real-mode segment + * @v offset Real-mode offset + * @ret buffer User buffer + */ +static inline __always_inline userptr_t +real_to_user ( unsigned int segment, unsigned int offset ) { + return ( phys_to_user ( ( segment << 4 ) + offset ) ); +} + /** * Copy data to base memory * diff --git a/qemu/roms/ipxe/src/arch/i386/include/registers.h b/qemu/roms/ipxe/src/arch/i386/include/registers.h index 06d236524..d9aa3c376 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/registers.h +++ b/qemu/roms/ipxe/src/arch/i386/include/registers.h @@ -10,7 +10,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/rtc.h b/qemu/roms/ipxe/src/arch/i386/include/rtc.h index 2a6abbae5..6294b63e3 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/rtc.h +++ b/qemu/roms/ipxe/src/arch/i386/include/rtc.h @@ -13,7 +13,7 @@ * http://wiki.osdev.org/CMOS */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/sdi.h b/qemu/roms/ipxe/src/arch/i386/include/sdi.h index fc486402d..806c3f194 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/sdi.h +++ b/qemu/roms/ipxe/src/arch/i386/include/sdi.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** SDI image header */ struct sdi_header { diff --git a/qemu/roms/ipxe/src/arch/i386/include/setjmp.h b/qemu/roms/ipxe/src/arch/i386/include/setjmp.h index 5d3c11b69..fe1a9ef4d 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/setjmp.h +++ b/qemu/roms/ipxe/src/arch/i386/include/setjmp.h @@ -1,40 +1,50 @@ -#ifndef ETHERBOOT_SETJMP_H -#define ETHERBOOT_SETJMP_H +#ifndef _SETJMP_H +#define _SETJMP_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include /** A jump buffer */ typedef struct { + /** Saved return address */ uint32_t retaddr; + /** Saved stack pointer */ + uint32_t stack; + /** Saved %ebx */ uint32_t ebx; - uint32_t esp; - uint32_t ebp; + /** Saved %esi */ uint32_t esi; + /** Saved %edi */ uint32_t edi; + /** Saved %ebp */ + uint32_t ebp; } jmp_buf[1]; /** A real-mode-extended jump buffer */ typedef struct { + /** Jump buffer */ jmp_buf env; - uint16_t rm_ss; - uint16_t rm_sp; + /** Real-mode stack pointer */ + segoff_t rm_stack; } rmjmp_buf[1]; -extern int __asmcall setjmp ( jmp_buf env ); -extern void __asmcall longjmp ( jmp_buf env, int val ); +extern int __asmcall __attribute__ (( returns_twice )) +setjmp ( jmp_buf env ); + +extern void __asmcall __attribute__ (( noreturn )) +longjmp ( jmp_buf env, int val ); -#define rmsetjmp( _env ) ( { \ - (_env)->rm_ss = rm_ss; \ - (_env)->rm_sp = rm_sp; \ - setjmp ( (_env)->env ); } ) \ +#define rmsetjmp( _env ) ( { \ + (_env)->rm_stack.segment = rm_ss; \ + (_env)->rm_stack.offset = rm_sp; \ + setjmp ( (_env)->env ); } ) \ -#define rmlongjmp( _env, _val ) do { \ - rm_ss = (_env)->rm_ss; \ - rm_sp = (_env)->rm_sp; \ - longjmp ( (_env)->env, (_val) ); \ +#define rmlongjmp( _env, _val ) do { \ + rm_ss = (_env)->rm_stack.segment; \ + rm_sp = (_env)->rm_stack.offset; \ + longjmp ( (_env)->env, (_val) ); \ } while ( 0 ) -#endif /* ETHERBOOT_SETJMP_H */ +#endif /* _SETJMP_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/undi.h b/qemu/roms/ipxe/src/arch/i386/include/undi.h index 325fcbbf9..7a5624f93 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/undi.h +++ b/qemu/roms/ipxe/src/arch/i386/include/undi.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifndef ASSEMBLY diff --git a/qemu/roms/ipxe/src/arch/i386/include/undiload.h b/qemu/roms/ipxe/src/arch/i386/include/undiload.h index 426830e8d..235e7a79e 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/undiload.h +++ b/qemu/roms/ipxe/src/arch/i386/include/undiload.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct undi_device; struct undi_rom; diff --git a/qemu/roms/ipxe/src/arch/i386/include/undinet.h b/qemu/roms/ipxe/src/arch/i386/include/undinet.h index c3c17c11a..2798c4466 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/undinet.h +++ b/qemu/roms/ipxe/src/arch/i386/include/undinet.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct undi_device; diff --git a/qemu/roms/ipxe/src/arch/i386/include/undipreload.h b/qemu/roms/ipxe/src/arch/i386/include/undipreload.h index de9b8fb52..57f493cec 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/undipreload.h +++ b/qemu/roms/ipxe/src/arch/i386/include/undipreload.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/include/undirom.h b/qemu/roms/ipxe/src/arch/i386/include/undirom.h index 86d7077b5..1c530118d 100644 --- a/qemu/roms/ipxe/src/arch/i386/include/undirom.h +++ b/qemu/roms/ipxe/src/arch/i386/include/undirom.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/apm.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/apm.c index 3b13e1cd0..50b19cb81 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/apm.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/apm.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_nap.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_nap.c index 1e7de756b..f1ba8297b 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_nap.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_nap.c @@ -1,7 +1,7 @@ #include #include -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Save power by halting the CPU until the next interrupt diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_reboot.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_reboot.c index 68546b2e5..10a1ecb89 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_reboot.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_reboot.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_smbios.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_smbios.c index dd7897e29..a8c0fc325 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_smbios.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_smbios.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_timer.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_timer.c index 65bbf9e01..3299c9aae 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_timer.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_timer.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/biosint.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/biosint.c index a193defa3..3b8e80438 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/biosint.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/biosint.c @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Hook INT vector diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/int13.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/int13.c index 1c7a8128f..f0450da90 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/int13.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/int13.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/int13con.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/int13con.c new file mode 100644 index 000000000..2414c6909 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/int13con.c @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * INT13 disk log console + * + */ + +/* Set default console usage if applicable */ +#if ! ( defined ( CONSOLE_INT13 ) && CONSOLE_EXPLICIT ( CONSOLE_INT13 ) ) +#undef CONSOLE_INT13 +#define CONSOLE_INT13 ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG ) +#endif + +/** Disk drive number */ +#define INT13CON_DRIVE 0x80 + +/** Log partition type */ +#define INT13CON_PARTITION_TYPE 0xe0 + +/** Maximum number of outstanding unwritten characters */ +#define INT13CON_MAX_UNWRITTEN 64 + +/** Log partition header */ +struct int13con_header { + /** Magic signature */ + char magic[10]; +} __attribute__ (( packed )); + +/** Log partition magic signature */ +#define INT13CON_MAGIC "iPXE LOG\n\n" + +/** Sector buffer */ +static uint8_t __bss16_array ( int13con_buffer, [INT13_BLKSIZE] ); +#define int13con_buffer __use_data16 ( int13con_buffer ) + +/** Disk address packet */ +static struct int13_disk_address __bss16 ( int13con_address ); +#define int13con_address __use_data16 ( int13con_address ) + +/** Current LBA */ +static uint64_t int13con_lba; + +/** Maximum LBA */ +static uint64_t int13con_max_lba; + +/** Current offset within sector */ +static size_t int13con_offset; + +/** Number of unwritten characters */ +static size_t int13con_unwritten; + +struct console_driver int13con __console_driver; + +/** + * Read/write disk sector + * + * @v op Operation + * @v lba Logical block address + * @ret rc Return status code + */ +static int int13con_rw ( unsigned int op, uint64_t lba ) { + uint8_t error; + + /* Construct disk address packet */ + int13con_address.bufsize = sizeof ( int13con_address ); + int13con_address.count = 1; + int13con_address.buffer.segment = rm_ds; + int13con_address.buffer.offset = __from_data16 ( int13con_buffer ); + int13con_address.lba = lba; + + /* Issue INT13 */ + __asm__ ( REAL_CODE ( "int $0x13\n\t" ) + : "=a" ( error ) + : "0" ( op << 8 ), "d" ( INT13CON_DRIVE ), + "S" ( __from_data16 ( &int13con_address ) ) ); + if ( error ) { + DBG ( "INT13CON operation %04x failed: %02x\n", + op, error ); + return -EIO; + } + + return 0; +} + +/** + * Write character to console + * + * @v character Character + */ +static void int13con_putchar ( int character ) { + static int busy; + int rc; + + /* Ignore if we are already mid-logging */ + if ( busy ) + return; + busy = 1; + + /* Write character to buffer */ + int13con_buffer[int13con_offset++] = character; + int13con_unwritten++; + + /* Write sector to disk, if applicable */ + if ( ( int13con_offset == INT13_BLKSIZE ) || + ( int13con_unwritten == INT13CON_MAX_UNWRITTEN ) || + ( character == '\n' ) ) { + + /* Write sector to disk */ + if ( ( rc = int13con_rw ( INT13_EXTENDED_WRITE, + int13con_lba ) ) != 0 ) { + DBG ( "INT13CON could not write log\n" ); + /* Ignore and continue; there's nothing we can do */ + } + + /* Reset count of unwritten characters */ + int13con_unwritten = 0; + } + + /* Move to next sector, if applicable */ + if ( int13con_offset == INT13_BLKSIZE ) { + + /* Disable console if we have run out of space */ + if ( int13con_lba >= int13con_max_lba ) + int13con.disabled = 1; + + /* Clear log buffer */ + memset ( int13con_buffer, 0, sizeof ( int13con_buffer ) ); + int13con_offset = 0; + + /* Move to next sector */ + int13con_lba++; + } + + /* Clear busy flag */ + busy = 0; +} + +/** + * Find log partition + * + * @ret rc Return status code + */ +static int int13con_find ( void ) { + struct master_boot_record *mbr = + ( ( struct master_boot_record * ) int13con_buffer ); + struct int13con_header *hdr = + ( ( struct int13con_header * ) int13con_buffer ); + struct partition_table_entry part[4]; + unsigned int i; + int rc; + + /* Read MBR */ + if ( ( rc = int13con_rw ( INT13_EXTENDED_READ, 0 ) ) != 0 ) { + DBG ( "INT13CON could not read MBR: %s\n", strerror ( rc ) ); + return rc; + } + + /* Check MBR magic */ + if ( mbr->magic != INT13_MBR_MAGIC ) { + DBG ( "INT13CON incorrect MBR magic\n" ); + DBG2_HDA ( 0, mbr, sizeof ( *mbr ) ); + return -EINVAL; + } + + /* Look for magic partition */ + memcpy ( part, mbr->partitions, sizeof ( part ) ); + for ( i = 0 ; i < ( sizeof ( part ) / sizeof ( part[0] ) ) ; i++ ) { + + /* Skip partitions of the wrong type */ + if ( part[i].type != INT13CON_PARTITION_TYPE ) + continue; + + /* Read partition header */ + if ( ( rc = int13con_rw ( INT13_EXTENDED_READ, + part[i].start ) ) != 0 ) { + DBG ( "INT13CON partition %d could not read header: " + "%s\n", ( i + 1 ), strerror ( rc ) ); + continue; + } + + /* Check partition header */ + if ( memcmp ( hdr->magic, INT13CON_MAGIC, + sizeof ( hdr->magic ) ) != 0 ) { + DBG ( "INT13CON partition %d bad magic\n", ( i + 1 ) ); + DBG2_HDA ( 0, hdr, sizeof ( *hdr ) ); + continue; + } + + /* Found log partition */ + DBG ( "INT13CON partition %d at [%08x,%08x)\n", ( i + 1 ), + part[i].start, ( part[i].start + part[i].length ) ); + int13con_lba = part[i].start; + int13con_max_lba = ( part[i].start + part[i].length - 1 ); + + /* Initialise log buffer */ + memset ( &int13con_buffer[ sizeof ( *hdr ) ], 0, + ( sizeof ( int13con_buffer ) - sizeof ( *hdr ) ) ); + int13con_offset = sizeof ( hdr->magic ); + + return 0; + } + + DBG ( "INT13CON found no log partition\n" ); + return -ENOENT; +} + +/** + * Initialise INT13 console + * + */ +static void int13con_init ( void ) { + uint8_t error; + uint16_t check; + unsigned int discard_c; + unsigned int discard_d; + int rc; + + /* Check for INT13 extensions */ + __asm__ __volatile__ ( REAL_CODE ( "int $0x13\n\t" + "setc %%al\n\t" ) + : "=a" ( error ), "=b" ( check ), + "=c" ( discard_c ), "=d" ( discard_d ) + : "0" ( INT13_EXTENSION_CHECK << 8 ), + "1" ( 0x55aa ), "3" ( INT13CON_DRIVE ) ); + if ( error || ( check != 0xaa55 ) ) { + DBG ( "INT13CON missing extensions (%02x,%04x)\n", + error, check ); + return; + } + + /* Locate log partition */ + if ( ( rc = int13con_find() ) != 0) + return; + + /* Enable console */ + int13con.disabled = 0; +} + +/** + * INT13 console initialisation function + */ +struct init_fn int13con_init_fn __init_fn ( INIT_CONSOLE ) = { + .initialise = int13con_init, +}; + +/** INT13 console driver */ +struct console_driver int13con __console_driver = { + .putchar = int13con_putchar, + .disabled = CONSOLE_DISABLED, + .usage = CONSOLE_INT13, +}; diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/memtop_umalloc.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/memtop_umalloc.c index c382e3c36..957f8e324 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/memtop_umalloc.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/memtop_umalloc.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/pcibios.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/pcibios.c index 61873039f..34efa0b39 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/pcibios.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/pcibios.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -38,7 +42,11 @@ static int pcibios_num_bus ( void ) { int discard_a, discard_D; uint8_t max_bus; - __asm__ __volatile__ ( REAL_CODE ( "stc\n\t" + /* We issue this call using flat real mode, to work around a + * bug in some HP BIOSes. + */ + __asm__ __volatile__ ( REAL_CODE ( "call flatten_real_mode\n\t" + "stc\n\t" "int $0x1a\n\t" "jnc 1f\n\t" "xorw %%cx, %%cx\n\t" diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/rtc_entropy.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/rtc_entropy.c index fad421c2a..9aab03c03 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/rtc_entropy.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/rtc_entropy.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/rtc_time.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/rtc_time.c index 67041d4ca..cdbeac8d5 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/rtc_time.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/rtc_time.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/vesafb.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/vesafb.c index 2adc7b040..9cf2bf29e 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pcbios/vesafb.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/vesafb.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_call.c b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_call.c index 657d47b6c..104313666 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_call.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_call.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -342,6 +346,7 @@ int pxe_start_nbp ( void ) { return 0; } +REQUIRING_SYMBOL ( pxe_api_call ); REQUIRE_OBJECT ( pxe_preboot ); REQUIRE_OBJECT ( pxe_undi ); REQUIRE_OBJECT ( pxe_udp ); diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_entry.S b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_entry.S index 6274264ff..07852cd50 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_entry.S +++ b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_entry.S @@ -16,9 +16,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .arch i386 diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_exit_hook.c b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_exit_hook.c index 9d1896507..f92dae0d1 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_exit_hook.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_exit_hook.c @@ -21,9 +21,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_file.c b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_file.c index 6e9610294..456ffb5fd 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_file.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_file.c @@ -31,9 +31,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); FEATURE ( FEATURE_MISC, "PXEXT", DHCP_EB_FEATURE_PXE_EXT, 2 ); diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_loader.c b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_loader.c index 695af3b93..e6a2e072a 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_loader.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_loader.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include "pxe.h" diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_preboot.c b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_preboot.c index 534352b2b..6e09080bc 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_preboot.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_preboot.c @@ -22,9 +22,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -174,18 +178,16 @@ pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO *get_cached_info ) { } info = &cached_info[idx]; - /* Construct cached version of packet, if not already constructed. */ - if ( ! info->dhcphdr.op ) { - /* Construct DHCP packet */ - creator = &pxe_dhcp_packet_creators[idx]; - if ( ( rc = creator->create ( pxe_netdev, info, - sizeof ( *info ) ) ) != 0 ) { - DBGC ( &pxe_netdev, " failed to build packet: %s\n", - strerror ( rc ) ); - goto err; - } + /* Construct DHCP packet */ + creator = &pxe_dhcp_packet_creators[idx]; + if ( ( rc = creator->create ( pxe_netdev, info, + sizeof ( *info ) ) ) != 0 ) { + DBGC ( &pxe_netdev, " failed to build packet: %s\n", + strerror ( rc ) ); + goto err; } + /* Copy packet (if applicable) */ len = get_cached_info->BufferSize; if ( len == 0 ) { /* Point client at our cached buffer. diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_tftp.c b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_tftp.c index f4801bad0..068d8a7b2 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_tftp.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_tftp.c @@ -21,9 +21,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -36,6 +40,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include +#include #include /** A PXE TFTP connection */ @@ -170,11 +176,10 @@ static struct pxe_tftp_connection pxe_tftp = { * @v blksize Requested block size * @ret rc Return status code */ -static int pxe_tftp_open ( uint32_t ipaddress, unsigned int port, - const unsigned char *filename, size_t blksize, - int sizeonly ) { - char uri_string[PXE_TFTP_URI_LEN]; +static int pxe_tftp_open ( IP4_t ipaddress, UDP_PORT_t port, + UINT8_t *filename, UINT16_t blksize ) { struct in_addr address; + struct uri *uri; int rc; /* Reset PXE TFTP connection structure */ @@ -185,19 +190,20 @@ static int pxe_tftp_open ( uint32_t ipaddress, unsigned int port, pxe_tftp.blksize = blksize; pxe_tftp.rc = -EINPROGRESS; - /* Construct URI string */ + /* Construct URI */ address.s_addr = ipaddress; - if ( ! port ) - port = htons ( TFTP_PORT ); - snprintf ( uri_string, sizeof ( uri_string ), "tftp%s://%s:%d%s%s", - sizeonly ? "size" : "", inet_ntoa ( address ), - ntohs ( port ), ( ( filename[0] == '/' ) ? "" : "/" ), - filename ); - DBG ( " %s", uri_string ); + DBG ( " %s", inet_ntoa ( address ) ); + if ( port ) + DBG ( ":%d", ntohs ( port ) ); + DBG ( ":%s", filename ); + uri = tftp_uri ( address, ntohs ( port ), ( ( char * ) filename ) ); + if ( ! uri ) { + DBG ( " could not create URI\n" ); + return -ENOMEM; + } /* Open PXE TFTP connection */ - if ( ( rc = xfer_open_uri_string ( &pxe_tftp.xfer, - uri_string ) ) != 0 ) { + if ( ( rc = xfer_open_uri ( &pxe_tftp.xfer, uri ) ) != 0 ) { DBG ( " could not open (%s)\n", strerror ( rc ) ); return rc; } @@ -259,8 +265,7 @@ static PXENV_EXIT_t pxenv_tftp_open ( struct s_PXENV_TFTP_OPEN *tftp_open ) { if ( ( rc = pxe_tftp_open ( tftp_open->ServerIPAddress, tftp_open->TFTPPort, tftp_open->FileName, - tftp_open->PacketSize, - 0) ) != 0 ) { + tftp_open->PacketSize ) ) != 0 ) { tftp_open->Status = PXENV_STATUS ( rc ); return PXENV_EXIT_FAILURE; } @@ -483,7 +488,7 @@ PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE /* Open TFTP file */ if ( ( rc = pxe_tftp_open ( tftp_read_file->ServerIPAddress, 0, - tftp_read_file->FileName, 0, 0 ) ) != 0 ) { + tftp_read_file->FileName, 0 ) ) != 0 ) { tftp_read_file->Status = PXENV_STATUS ( rc ); return PXENV_EXIT_FAILURE; } @@ -553,7 +558,7 @@ static PXENV_EXIT_t pxenv_tftp_get_fsize ( struct s_PXENV_TFTP_GET_FSIZE /* Open TFTP file */ if ( ( rc = pxe_tftp_open ( tftp_get_fsize->ServerIPAddress, 0, - tftp_get_fsize->FileName, 0, 1 ) ) != 0 ) { + tftp_get_fsize->FileName, 0 ) ) != 0 ) { tftp_get_fsize->Status = PXENV_STATUS ( rc ); return PXENV_EXIT_FAILURE; } diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_udp.c b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_udp.c index 32bc39c8e..071cb59db 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_udp.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_udp.c @@ -11,6 +11,7 @@ #include #include #include +#include #include /* @@ -30,9 +31,25 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** A PXE UDP pseudo-header */ +struct pxe_udp_pseudo_header { + /** Source IP address */ + IP4_t src_ip; + /** Source port */ + UDP_PORT_t s_port; + /** Destination IP address */ + IP4_t dest_ip; + /** Destination port */ + UDP_PORT_t d_port; +} __attribute__ (( packed )); /** A PXE UDP connection */ struct pxe_udp_connection { @@ -40,8 +57,8 @@ struct pxe_udp_connection { struct interface xfer; /** Local address */ struct sockaddr_in local; - /** Current PXENV_UDP_READ parameter block */ - struct s_PXENV_UDP_READ *pxenv_udp_read; + /** List of received packets */ + struct list_head list; }; /** @@ -58,45 +75,38 @@ struct pxe_udp_connection { static int pxe_udp_deliver ( struct pxe_udp_connection *pxe_udp, struct io_buffer *iobuf, struct xfer_metadata *meta ) { - struct s_PXENV_UDP_READ *pxenv_udp_read = pxe_udp->pxenv_udp_read; + struct pxe_udp_pseudo_header *pshdr; struct sockaddr_in *sin_src; struct sockaddr_in *sin_dest; - userptr_t buffer; - size_t len; - int rc = 0; - - if ( ! pxenv_udp_read ) { - DBG ( "PXE discarded UDP packet\n" ); - rc = -ENOBUFS; - goto done; - } - - /* Copy packet to buffer and record length */ - buffer = real_to_user ( pxenv_udp_read->buffer.segment, - pxenv_udp_read->buffer.offset ); - len = iob_len ( iobuf ); - if ( len > pxenv_udp_read->buffer_size ) - len = pxenv_udp_read->buffer_size; - copy_to_user ( buffer, 0, iobuf->data, len ); - pxenv_udp_read->buffer_size = len; + int rc; - /* Fill in source/dest information */ + /* Extract metadata */ assert ( meta ); sin_src = ( struct sockaddr_in * ) meta->src; assert ( sin_src ); assert ( sin_src->sin_family == AF_INET ); - pxenv_udp_read->src_ip = sin_src->sin_addr.s_addr; - pxenv_udp_read->s_port = sin_src->sin_port; sin_dest = ( struct sockaddr_in * ) meta->dest; assert ( sin_dest ); assert ( sin_dest->sin_family == AF_INET ); - pxenv_udp_read->dest_ip = sin_dest->sin_addr.s_addr; - pxenv_udp_read->d_port = sin_dest->sin_port; - /* Mark as received */ - pxe_udp->pxenv_udp_read = NULL; + /* Construct pseudo-header */ + if ( ( rc = iob_ensure_headroom ( iobuf, sizeof ( *pshdr ) ) ) != 0 ) { + DBG ( "PXE could not prepend pseudo-header\n" ); + rc = -ENOMEM; + goto drop; + } + pshdr = iob_push ( iobuf, sizeof ( *pshdr ) ); + pshdr->src_ip = sin_src->sin_addr.s_addr; + pshdr->s_port = sin_src->sin_port; + pshdr->dest_ip = sin_dest->sin_addr.s_addr; + pshdr->d_port = sin_dest->sin_port; - done: + /* Add to queue */ + list_add_tail ( &iobuf->list, &pxe_udp->list ); + + return 0; + + drop: free_iob ( iobuf ); return rc; } @@ -116,6 +126,7 @@ static struct pxe_udp_connection pxe_udp = { .local = { .sin_family = AF_INET, }, + .list = LIST_HEAD_INIT ( pxe_udp.list ), }; /** @@ -205,11 +216,20 @@ static PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *pxenv_udp_open ) { */ static PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *pxenv_udp_close ) { + struct io_buffer *iobuf; + struct io_buffer *tmp; + DBG ( "PXENV_UDP_CLOSE\n" ); /* Close UDP connection */ intf_restart ( &pxe_udp.xfer, 0 ); + /* Discard any received packets */ + list_for_each_entry_safe ( iobuf, tmp, &pxe_udp.list, list ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); + } + pxenv_udp_close->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; } @@ -365,20 +385,32 @@ pxenv_udp_write ( struct s_PXENV_UDP_WRITE *pxenv_udp_write ) { static PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) { struct in_addr dest_ip_wanted = { .s_addr = pxenv_udp_read->dest_ip }; struct in_addr dest_ip; + struct io_buffer *iobuf; + struct pxe_udp_pseudo_header *pshdr; uint16_t d_port_wanted = pxenv_udp_read->d_port; uint16_t d_port; + userptr_t buffer; + size_t len; + + /* Try receiving a packet, if the queue is empty */ + if ( list_empty ( &pxe_udp.list ) ) + step(); - /* Try receiving a packet */ - pxe_udp.pxenv_udp_read = pxenv_udp_read; - step(); - if ( pxe_udp.pxenv_udp_read ) { + /* Remove first packet from the queue */ + iobuf = list_first_entry ( &pxe_udp.list, struct io_buffer, list ); + if ( ! iobuf ) { /* No packet received */ DBG2 ( "PXENV_UDP_READ\n" ); - pxe_udp.pxenv_udp_read = NULL; goto no_packet; } - dest_ip.s_addr = pxenv_udp_read->dest_ip; - d_port = pxenv_udp_read->d_port; + list_del ( &iobuf->list ); + + /* Strip pseudo-header */ + assert ( iob_len ( iobuf ) >= sizeof ( *pshdr ) ); + pshdr = iobuf->data; + iob_pull ( iobuf, sizeof ( *pshdr ) ); + dest_ip.s_addr = pshdr->dest_ip; + d_port = pshdr->d_port; DBG ( "PXENV_UDP_READ" ); /* Filter on destination address and/or port */ @@ -386,14 +418,29 @@ static PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) { ( dest_ip_wanted.s_addr != dest_ip.s_addr ) ) { DBG ( " wrong IP %s", inet_ntoa ( dest_ip ) ); DBG ( " (wanted %s)\n", inet_ntoa ( dest_ip_wanted ) ); - goto no_packet; + goto drop; } if ( d_port_wanted && ( d_port_wanted != d_port ) ) { DBG ( " wrong port %d", htons ( d_port ) ); DBG ( " (wanted %d)\n", htons ( d_port_wanted ) ); - goto no_packet; + goto drop; } + /* Copy packet to buffer and record length */ + buffer = real_to_user ( pxenv_udp_read->buffer.segment, + pxenv_udp_read->buffer.offset ); + len = iob_len ( iobuf ); + if ( len > pxenv_udp_read->buffer_size ) + len = pxenv_udp_read->buffer_size; + copy_to_user ( buffer, 0, iobuf->data, len ); + pxenv_udp_read->buffer_size = len; + + /* Fill in source/dest information */ + pxenv_udp_read->src_ip = pshdr->src_ip; + pxenv_udp_read->s_port = pshdr->s_port; + pxenv_udp_read->dest_ip = pshdr->dest_ip; + pxenv_udp_read->d_port = pshdr->d_port; + DBG ( " %04x:%04x+%x %s:", pxenv_udp_read->buffer.segment, pxenv_udp_read->buffer.offset, pxenv_udp_read->buffer_size, inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->src_ip ) )); @@ -401,9 +448,14 @@ static PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) { inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->dest_ip ) ), ntohs ( pxenv_udp_read->d_port ) ); + /* Free I/O buffer */ + free_iob ( iobuf ); + pxenv_udp_read->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; + drop: + free_iob ( iobuf ); no_packet: pxenv_udp_read->Status = PXENV_STATUS_FAILURE; return PXENV_EXIT_FAILURE; diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_undi.c b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_undi.c index 29e586ed2..2eb68178a 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_undi.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_undi.c @@ -21,9 +21,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/interface/syslinux/comboot_call.c b/qemu/roms/ipxe/src/arch/i386/interface/syslinux/comboot_call.c index 1854501de..69d94c407 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/syslinux/comboot_call.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/syslinux/comboot_call.c @@ -41,8 +41,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include -#include "config/console.h" -#include "config/serial.h" /** The "SYSLINUX" version string */ static char __bss16_array ( syslinux_version, [32] ); @@ -86,7 +84,6 @@ rmjmp_buf comboot_return; /* Mode flags set by INT 22h AX=0017h */ static uint16_t comboot_graphics_mode = 0; - /** * Print a string with a particular terminator */ @@ -261,8 +258,10 @@ static __asmcall void int21 ( struct i386_all_regs *ix86 ) { break; case 0x04: /* Write Character to Serial Port */ - serial_putc ( ix86->regs.dl ); - ix86->flags &= ~CF; + if ( serial_console.base ) { + uart_transmit ( &serial_console, ix86->regs.dl ); + ix86->flags &= ~CF; + } break; case 0x09: /* Write DOS String to Console */ @@ -455,15 +454,16 @@ static __asmcall void int22 ( struct i386_all_regs *ix86 ) { break; case 0x000B: /* Get Serial Console Configuration */ -#if defined(CONSOLE_SERIAL) && !defined(COMPRESERVE) - ix86->regs.dx = COMCONSOLE; - ix86->regs.cx = 115200 / COMSPEED; - ix86->regs.bx = 0; -#else - ix86->regs.dx = 0; -#endif + if ( serial_console.base ) { + ix86->regs.dx = ( ( intptr_t ) serial_console.base ); + ix86->regs.cx = serial_console.divisor; + ix86->regs.bx = 0; + ix86->flags &= ~CF; + } + break; - ix86->flags &= ~CF; + case 0x000C: /* Perform final cleanup */ + shutdown_boot(); break; case 0x000E: /* Get configuration file name */ @@ -712,3 +712,6 @@ void unhook_comboot_interrupts ( ) { unhook_bios_interrupt ( 0x22, ( unsigned int ) int22_wrapper, &int22_vector ); } + +/* Avoid dragging in serial console support unconditionally */ +struct uart serial_console __attribute__ (( weak )); diff --git a/qemu/roms/ipxe/src/arch/i386/interface/vmware/guestrpc.c b/qemu/roms/ipxe/src/arch/i386/interface/vmware/guestrpc.c index 390fc5545..ef7ee8151 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/vmware/guestrpc.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/vmware/guestrpc.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/arch/i386/interface/vmware/vmconsole.c b/qemu/roms/ipxe/src/arch/i386/interface/vmware/vmconsole.c index c6b9fff12..f7df4f75b 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/vmware/vmconsole.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/vmware/vmconsole.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/arch/i386/interface/vmware/vmware.c b/qemu/roms/ipxe/src/arch/i386/interface/vmware/vmware.c index 8074e6118..a415465fb 100644 --- a/qemu/roms/ipxe/src/arch/i386/interface/vmware/vmware.c +++ b/qemu/roms/ipxe/src/arch/i386/interface/vmware/vmware.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/bootpart.S b/qemu/roms/ipxe/src/arch/i386/prefix/bootpart.S index 968da1a38..6d0c6034a 100644 --- a/qemu/roms/ipxe/src/arch/i386/prefix/bootpart.S +++ b/qemu/roms/ipxe/src/arch/i386/prefix/bootpart.S @@ -1,4 +1,4 @@ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define BOOT_SEG 0x07c0 #define EXEC_SEG 0x0100 diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/exeprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/exeprefix.S index cb61287d3..5c648d51d 100644 --- a/qemu/roms/ipxe/src/arch/i386/prefix/exeprefix.S +++ b/qemu/roms/ipxe/src/arch/i386/prefix/exeprefix.S @@ -16,9 +16,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) /* Initial temporary stack size */ #define EXE_STACK_SIZE 0x400 diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/hdprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/hdprefix.S index 876bfe1be..1d012d80b 100644 --- a/qemu/roms/ipxe/src/arch/i386/prefix/hdprefix.S +++ b/qemu/roms/ipxe/src/arch/i386/prefix/hdprefix.S @@ -1,4 +1,4 @@ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .text .arch i386 diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/isaromprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/isaromprefix.S index e28208089..fb49819ee 100644 --- a/qemu/roms/ipxe/src/arch/i386/prefix/isaromprefix.S +++ b/qemu/roms/ipxe/src/arch/i386/prefix/isaromprefix.S @@ -16,9 +16,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define BUSTYPE "ISAR" #define _rom_start _isarom_start diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/kkkpxeprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/kkkpxeprefix.S index 27ed231e7..6e43cd26a 100644 --- a/qemu/roms/ipxe/src/arch/i386/prefix/kkkpxeprefix.S +++ b/qemu/roms/ipxe/src/arch/i386/prefix/kkkpxeprefix.S @@ -5,12 +5,10 @@ ***************************************************************************** */ -FILE_LICENCE ( GPL2_OR_LATER ) - -/* Since we have the whole stack, we can use cached DHCP information */ -REQUIRE_OBJECT ( pxeparent_dhcp ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) /* Provide the PXENV_FILE_EXIT_HOOK API call */ +REQUIRING_SYMBOL ( _kkkpxe_start ) REQUIRE_OBJECT ( pxe_exit_hook ) #define PXELOADER_KEEP_UNDI diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/kkpxeprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/kkpxeprefix.S index d177d7d62..3c17dbdb1 100644 --- a/qemu/roms/ipxe/src/arch/i386/prefix/kkpxeprefix.S +++ b/qemu/roms/ipxe/src/arch/i386/prefix/kkpxeprefix.S @@ -3,10 +3,7 @@ ***************************************************************************** */ -FILE_LICENCE ( GPL2_OR_LATER ) - -/* Since we have the whole stack, we can use cached DHCP information */ -REQUEST_OBJECT ( pxeparent_dhcp ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define PXELOADER_KEEP_UNDI #define PXELOADER_KEEP_PXE diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/kpxeprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/kpxeprefix.S index c75608172..200006d83 100644 --- a/qemu/roms/ipxe/src/arch/i386/prefix/kpxeprefix.S +++ b/qemu/roms/ipxe/src/arch/i386/prefix/kpxeprefix.S @@ -3,7 +3,7 @@ ***************************************************************************** */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define PXELOADER_KEEP_UNDI #define _pxe_start _kpxe_start diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/libprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/libprefix.S index 7c1ece791..7d5c1ed53 100644 --- a/qemu/roms/ipxe/src/arch/i386/prefix/libprefix.S +++ b/qemu/roms/ipxe/src/arch/i386/prefix/libprefix.S @@ -16,9 +16,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .arch i386 @@ -296,11 +300,9 @@ copy_bytes: * Zero bytes * * Parameters: - * %ds:esi : source address * %es:edi : destination address * %ecx : length * Returns: - * %ds:esi : next source address * %es:edi : next destination address * Corrupts: * None @@ -396,8 +398,10 @@ process_bytes: movw %ax, %fs movw %ax, %gs +#ifdef NDEBUG /* Call memcpy()-like function */ call *%bx +#endif /* Return to (flat) real mode */ movl %cr0, %eax @@ -411,6 +415,20 @@ process_bytes: popw %fs popw %gs +#ifndef NDEBUG + /* Call memcpy()-like function in flat real mode (to allow for + * debug output via INT 10). + */ + pushw %ds + pushw %es + xorw %ax, %ax + movw %ax, %ds + movw %ax, %es + call *%bx + popw %es + popw %ds +#endif + /* Restore GDT */ data32 lgdt -8(%bp) addw $( 8 /* saved GDT */ + ( PM_DS + 8 ) /* GDT on stack */ ), %sp @@ -442,11 +460,11 @@ process_bytes: /* Convert %ds:esi and %es:edi back to physical addresses */ xorl %eax, %eax - movw %ds, %cx + movw %ds, %ax shll $4, %eax addl %eax, %esi xorl %eax, %eax - movw %es, %cx + movw %es, %ax shll $4, %eax addl %eax, %edi @@ -678,12 +696,21 @@ install: .globl install_prealloc install_prealloc: progress "install_prealloc:\n" - /* Save registers */ + /* Save registers on external stack */ pushal pushw %ds pushw %es cld /* Sanity: clear the direction flag asap */ + /* Switch to temporary stack in .bss16 */ + pushw %ss + popw %ds + movl %esp, %ecx + movw %bx, %ss + movl $_data16_memsz, %esp + pushw %ds + pushl %ecx + /* Set up %ds for (read-only) access to .prefix */ pushw %cs popw %ds @@ -710,6 +737,7 @@ install_prealloc: popl %esi #ifndef KEEP_IT_REAL + /* Access high memory by enabling the A20 gate. (We will * already have 4GB segment limits as a result of calling * install_block.) @@ -778,7 +806,7 @@ payload_death_message: movzwl %bx, %edi shll $4, %edi movl $_data16_filesz, %ecx - movl $_data16_memsz, %edx + movl $_data16_filesz, %edx /* do not zero our temporary stack */ call install_block /* .data16 */ /* Set up %ds for access to .data16 */ @@ -787,11 +815,8 @@ payload_death_message: /* Restore decompression temporary area physical address */ popl %edi -#ifdef KEEP_IT_REAL - /* Initialise libkir */ - movw %ax, (init_libkir_vector+2) - lcall *init_libkir_vector -#else +#ifndef KEEP_IT_REAL + /* Find a suitable decompression temporary area, if none specified */ pushl %eax testl %edi, %edi @@ -823,6 +848,22 @@ payload_death_message: call install_block popl %edi +#endif /* KEEP_IT_REAL */ + + /* Switch back to original stack and zero .bss16 */ + addr32 lss %ss:(%esp), %esp + pushl %edi + pushw %es + movw %bx, %es + movl $_data16_filesz, %edi + movl $_data16_memsz, %ecx + subl %edi, %ecx + call zero_bytes + popw %es + popl %edi + +#ifndef KEEP_IT_REAL + /* Initialise librm at current location */ progress " init_librm\n" movw %ax, (init_librm_vector+2) @@ -834,7 +875,6 @@ payload_death_message: incb memmap_post decl %ebp 1: - /* Call relocate() to determine target address for relocation. * relocate() will return with %esi, %edi and %ecx set up * ready for the copy to the new location. @@ -857,7 +897,14 @@ payload_death_message: /* Initialise librm at new location */ progress " init_librm\n" lcall *init_librm_vector -#endif + +#else /* KEEP_IT_REAL */ + + /* Initialise libkir */ + movw %ax, (init_libkir_vector+2) + lcall *init_libkir_vector + +#endif /* KEEP_IT_REAL */ /* Close access to payload */ progress " close_payload\n" diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/lkrnprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/lkrnprefix.S index 259bc6ba5..64135e14b 100644 --- a/qemu/roms/ipxe/src/arch/i386/prefix/lkrnprefix.S +++ b/qemu/roms/ipxe/src/arch/i386/prefix/lkrnprefix.S @@ -1,4 +1,4 @@ -FILE_LICENCE ( GPL_ANY ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define BZI_LOAD_HIGH_ADDR 0x100000 diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/mbr.S b/qemu/roms/ipxe/src/arch/i386/prefix/mbr.S index adfe20410..a1e237de8 100644 --- a/qemu/roms/ipxe/src/arch/i386/prefix/mbr.S +++ b/qemu/roms/ipxe/src/arch/i386/prefix/mbr.S @@ -1,3 +1,5 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + .text .arch i386 .section ".prefix", "awx", @progbits diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/mromprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/mromprefix.S index 4c94457c2..b636b92af 100644 --- a/qemu/roms/ipxe/src/arch/i386/prefix/mromprefix.S +++ b/qemu/roms/ipxe/src/arch/i386/prefix/mromprefix.S @@ -16,9 +16,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define PCIBIOS_READ_CONFIG_WORD 0xb109 #define PCIBIOS_READ_CONFIG_DWORD 0xb10a @@ -463,6 +467,7 @@ pci_set_mem_access: .org 0x00 mromheader: .word 0xaa55 /* BIOS extension signature */ + .byte 0x01 /* Dummy size (BIOS bug workaround) */ .org 0x18 .word mpciheader .org 0x1a diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/nbiprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/nbiprefix.S index 06e7df5b7..16c79566c 100644 --- a/qemu/roms/ipxe/src/arch/i386/prefix/nbiprefix.S +++ b/qemu/roms/ipxe/src/arch/i386/prefix/nbiprefix.S @@ -1,3 +1,5 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + .text .arch i386 .code16 diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/nullprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/nullprefix.S index 032d41e0f..bd0ff339e 100644 --- a/qemu/roms/ipxe/src/arch/i386/prefix/nullprefix.S +++ b/qemu/roms/ipxe/src/arch/i386/prefix/nullprefix.S @@ -1,3 +1,5 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + .org 0 .text .arch i386 diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/pciromprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/pciromprefix.S index 45ba31f50..5a5a49647 100644 --- a/qemu/roms/ipxe/src/arch/i386/prefix/pciromprefix.S +++ b/qemu/roms/ipxe/src/arch/i386/prefix/pciromprefix.S @@ -16,9 +16,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define BUSTYPE "PCIR" #define _rom_start _pcirom_start diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/pxeprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/pxeprefix.S index 6e29c7949..465ce4345 100644 --- a/qemu/roms/ipxe/src/arch/i386/prefix/pxeprefix.S +++ b/qemu/roms/ipxe/src/arch/i386/prefix/pxeprefix.S @@ -1,4 +1,4 @@ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define PXENV_UNDI_SHUTDOWN 0x0005 #define PXENV_UNDI_GET_NIC_TYPE 0x0012 diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/romprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/romprefix.S index 7bc4fe8cd..18dda2b37 100644 --- a/qemu/roms/ipxe/src/arch/i386/prefix/romprefix.S +++ b/qemu/roms/ipxe/src/arch/i386/prefix/romprefix.S @@ -6,9 +6,10 @@ * table so using a noticeable amount of stack space is a no-no. */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #include +#include #define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) ) #define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) ) @@ -90,7 +91,7 @@ pciheader: .ascii "PCIR" /* Signature */ .word pci_vendor_id /* Vendor identification */ .word pci_device_id /* Device identification */ - .word 0x0000 /* Device list pointer */ + .word ( pci_devlist - pciheader ) /* Device list pointer */ .word pciheader_len /* PCI data structure length */ .byte 0x03 /* PCI data structure revision */ .byte 0x02, 0x00, 0x00 /* Class code */ @@ -106,6 +107,17 @@ pciheader_runtime_length: .equ pciheader_len, . - pciheader .size pciheader, . - pciheader + /* PCI additional device list (filled in by linker) */ + .section ".pci_devlist.00000000", "a", @progbits +pci_devlist: + .previous + .section ".pci_devlist.ffffffff", "a", @progbits +pci_devlist_end: + .short 0x0000 /* List terminator */ + .previous + /* Ensure that terminator is always present */ + .reloc pciheader, RELOC_TYPE_NONE, pci_devlist_end + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ .ascii ZINFO_TYPE_ADxW .long pciheader_image_length @@ -573,7 +585,7 @@ get_pmm_decompress_to: * Note to hardware vendors: * * If you wish to brand this boot ROM, please do so by defining the - * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/general.h. + * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/branding.h. * * While nothing in the GPL prevents you from removing all references * to iPXE or http://ipxe.org, we prefer you not to do so. @@ -589,7 +601,10 @@ init_message: .ascii "\n" .ascii PRODUCT_NAME .ascii "\n" - .asciz "iPXE (http://ipxe.org)" + .ascii PRODUCT_SHORT_NAME + .ascii " (" + .ascii PRODUCT_URI + .asciz ")" .size init_message, . - init_message .ifeqs BUSTYPE, "PCIR" init_message_pci: @@ -771,7 +786,9 @@ exec: /* Set %ds = %cs */ /* Store PCI bus:dev.fn, if applicable */ .ifeqs BUSTYPE, "PCIR" +#ifdef AUTOBOOT_ROM_FILTER movw %ax, autoboot_busdevfn +#endif /* AUTOBOOT_ROM_FILTER */ .endif /* Call main() */ @@ -870,3 +887,9 @@ wait_for_tick: popl %eax ret .size wait_for_tick, . - wait_for_tick + +/* Drag in objects via _rom_start */ +REQUIRING_SYMBOL ( _rom_start ) + +/* Drag in ROM configuration */ +REQUIRE_OBJECT ( config_romprefix ) diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/undiloader.S b/qemu/roms/ipxe/src/arch/i386/prefix/undiloader.S index 74bb59041..5cace44b7 100644 --- a/qemu/roms/ipxe/src/arch/i386/prefix/undiloader.S +++ b/qemu/roms/ipxe/src/arch/i386/prefix/undiloader.S @@ -1,4 +1,4 @@ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .text .code16 diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/unlzma.S b/qemu/roms/ipxe/src/arch/i386/prefix/unlzma.S new file mode 100644 index 000000000..8d4b3c1a8 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/unlzma.S @@ -0,0 +1,942 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/**************************************************************************** + * + * This file provides the decompress() and decompress16() functions + * which can be called in order to decompress an LZMA-compressed + * image. The code is modelled on the public-domain "XZ Embedded" + * implementation as used by the Linux kernel. Symbol names are + * chosen to match the XZ Embedded implementation where possible, for + * ease of reference. + * + * This code is optimised for size rather than speed, since the amount + * of data to be decompressed is trivially small by modern standards. + * + * The same basic assembly code is used to compile both decompress() + * and decompress16(). + * + * Note that these functions require large amounts of stack space. + * + **************************************************************************** + */ + + .text + .arch i586 + .section ".prefix.lib", "ax", @progbits + +#ifdef CODE16 +#define ADDR16 +#define ADDR32 addr32 +#define decompress decompress16 + .code16 +#else /* CODE16 */ +#define ADDR16 addr16 +#define ADDR32 + .code32 +#endif /* CODE16 */ + +/**************************************************************************** + * Debugging + **************************************************************************** + * + * This code will usually run in 16-bit protected mode, in which case + * only the 0xe9 debug port (present on some virtual machines) can be + * used. + * + * To debug on real hardware, build with DEBUG=libprefix. This will + * cause this code to be called in flat real mode, and so DEBUG_INT10 + * may be used. + */ + +/* Enable debugging via 0xe9 debug port */ +#define DEBUG_E9 0 + +/* Enable debugging via BIOS INT 10 (works only when in flat real mode) */ +#define DEBUG_INT10 0 + +#if ( DEBUG_E9 || DEBUG_INT10 ) + .macro print_character, reg + pushfl + pushw %ax + pushw %bx + pushw %bp + movb \reg, %al + movw $0x0007, %bx + movb $0x0e, %ah +#if DEBUG_E9 + outb %al, $0xe9 +#endif +#if DEBUG_INT10 + cmpb $('\n'), %al + jne L\@ + int $0x10 + movb $('\r'), %al +L\@: int $0x10 +#endif + popw %bp + popw %bx + popw %ax + popfl + .endm + + .macro print_hex_nibble + pushfl + pushw %ax + cmpb $10, %al + sbb $0x69, %al + das + print_character %al + popw %ax + popfl + .endm + + .macro print_hex_byte, reg + pushfl + pushw %ax + movb \reg, %al + pushw %ax + shrb $4, %al + print_hex_nibble + popw %ax + andb $0x0f, %al + print_hex_nibble + popw %ax + popfl + .endm + + .macro print_hex_word, reg + pushw %ax + movw \reg, %ax + print_hex_byte %ah + print_hex_byte %al + popw %ax + .endm + + .macro print_hex_dword, reg + pushl %eax + movl \reg, %eax + rorl $16, %eax + print_hex_word %ax + rorl $16, %eax + print_hex_word %ax + popl %eax + .endm +#else + .macro print_character, char + .endm + .macro print_hex_byte, reg + .endm + .macro print_hex_word, reg + .endm + .macro print_hex_dword, reg + .endm +#endif + +/**************************************************************************** + * LZMA parameters and data structures + **************************************************************************** + */ + +/* LZMA decompressor states (as used in XZ Embedded) */ +#define STATE_LIT_LIT 0x00 +#define STATE_MATCH_LIT_LIT 0x01 +#define STATE_REP_LIT_LIT 0x02 +#define STATE_SHORTREP_LIT_LIT 0x03 +#define STATE_MATCH_LIT 0x04 +#define STATE_REP_LIT 0x05 +#define STATE_SHORTREP_LIT 0x06 +#define STATE_LIT_MATCH 0x07 +#define STATE_LIT_LONGREP 0x08 +#define STATE_LIT_SHORTREP 0x09 +#define STATE_NONLIT_MATCH 0x0a +#define STATE_NONLIT_REP 0x0b + +/* LZMA maximum decompressor state in which most recent symbol was a literal */ +#define STATE_LIT_MAX 0x06 + +/* LZMA number of literal context bits ("lc=" parameter) */ +#define LZMA_LC 2 + + .struct 0 +lzma_len_dec: +choice: .word 0 +choice2: .word 0 +low: .rept ( 1 << 3 ) + .word 0 + .endr +mid: .rept ( 1 << 3 ) + .word 0 + .endr +high: .rept ( 1 << 8 ) + .word 0 + .endr + .equ sizeof__lzma_len_dec, . - lzma_len_dec + .previous + + .struct 0 +lzma_dec: +out_start: .long 0 +rc_code: .long 0 +rc_range: .long 0 +len: .word 0 +reps: +rep0: .long 0 +rep1: .long 0 +rep2: .long 0 +rep3: .long 0 +probs: +is_match: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +is_rep: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +is_rep0: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +is_rep1: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +is_rep2: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +is_rep0_long: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +dist_slot: .rept ( 4 * ( 1 << 6 ) ) + .word 0 + .endr +dist_special: .rept ( ( 1 << ( 14 / 2 ) ) - 14 ) + .word 0 + .endr +dist_align: .rept ( 1 << 4 ) + .word 0 + .endr +match_len_dec: .space sizeof__lzma_len_dec +rep_len_dec: .space sizeof__lzma_len_dec +literal: .rept ( ( 1 << LZMA_LC ) * 0x300 ) + .word 0 + .endr + .align 4 + .equ sizeof__lzma_dec, . - lzma_dec + .previous + + /* Some binutils versions seem not to handle .struct/.previous */ + .section ".prefix.lib", "ax", @progbits + +/***************************************************************************** + * Normalise range encoder + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %eax : current range + ***************************************************************************** + */ +rc_normalise: + /* Check if rc_range is less than 1<<24 */ + testb $0xff, (rc_range+3)(%ebp) + jnz 1f + /* If it is, shift in a new byte from the compressed input data */ + shll $8, rc_range(%ebp) + shll $8, rc_code(%ebp) + ADDR32 lodsb + movb %al, (rc_code+0)(%ebp) +1: /* Return current range */ + movl rc_range(%ebp), %eax + ret + .size rc_normalise, . - rc_normalise + +/***************************************************************************** + * Decode single range-encoded bit using a probability estimate + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %ebx : probability estimate pointer (offset from %ebp) + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * CF : decoded bit + * ZF : inverse of decoded bit + * Corrupts: + * none + ***************************************************************************** + */ +rc_bit: + /* Preserve registers */ + pushl %eax + pushl %edx + /* Perform normalisation */ + call rc_normalise + /* Calculate bound in %eax and probability estimate in %dx */ + shrl $11, %eax + movzwl (%ebp,%ebx), %edx + mul %edx /* will zero %edx */ + movw (%ebp,%ebx), %dx + /* Compare code against bound */ + cmpl %eax, rc_code(%ebp) + jae 2f +1: /* Code is less than bound */ + movl %eax, rc_range(%ebp) + negw %dx + addw $(1<<11), %dx + shrw $5, %dx + addw %dx, (%ebp,%ebx) + xorw %ax, %ax /* Clear CF, set ZF */ + jmp 99f +2: /* Code is greater than or equal to bound */ + subl %eax, rc_range(%ebp) + subl %eax, rc_code(%ebp) + shrw $5, %dx + subw %dx, (%ebp,%ebx) + incw %dx /* Clear ZF (%dx is 11-bit; can never wrap) */ + stc /* Set CF */ +99: /* Restore registers and return */ + popl %edx + popl %eax + ret + .size rc_bit, . - rc_bit + +/***************************************************************************** + * Decode MSB-first bittree + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %ebx : probability estimate set pointer (offset from %ebp) + * %cx : number of bits to decode + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %eax : decoded bittree + * Corrupts: + * none + ***************************************************************************** + */ +rc_bittree: + /* Preserve registers */ + pushl %edi + pushw %cx + movl %ebx, %edi + /* Initialise registers */ + movl $1, %eax +1: /* Decode bit */ + leaw (%edi,%eax,2), %bx /* high word always zero anyway */ + call rc_bit + rclw %ax + ADDR16 loop 1b + /* Restore registers, clear unwanted high bit of result, and return */ + movl %edi, %ebx + popw %cx + popl %edi + btrw %cx, %ax + ret + .size rc_bittree, . - rc_bittree + +/***************************************************************************** + * Decode LSB-first bittree + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %ebx : probability estimate set pointer (offset from %ebp) + * %cx : number of bits to decode + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %eax : decoded bittree + * Corrupts: + * none + ***************************************************************************** + */ +rc_bittree_reverse: + /* Preserve registers */ + pushw %cx + /* Decode bittree */ + call rc_bittree +1: /* Reverse result */ + rcrb %al + rclb %ah + ADDR16 loop 1b + shrw $8, %ax + /* Restore registers and return */ + popw %cx + ret + .size rc_bittree_reverse, . - rc_bittree_reverse + +/***************************************************************************** + * Decode MSB-first bittree with optional match byte + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %ebx : probability estimate set pointer (offset from %ebp) + * %cl : match byte + * %ch : 1 to use match byte, 0 to ignore match byte + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %eax : decoded bittree + * Corrupts: + * none + ***************************************************************************** + */ +rc_bittree_match: + /* Preserve registers */ + pushl %edi + pushw %cx + pushw %dx + movl %ebx, %edi + /* Initialise registers */ + movl $1, %eax +1: /* Decode bit */ + rolb $1, %cl + movw %cx, %dx + andb %dh, %dl /* match_bit in %dl */ + movw %dx, %bx + addb %bl, %bh + xorb %bl, %bl + addw %ax, %bx /* offset + match_bit + symbol */ + leaw (%edi,%ebx,2), %bx /* high word always zero anyway */ + call rc_bit + rclw %ax + movb %al, %dh + notb %dh + xorb %dh, %dl + andb %dl, %ch /* offset &= ( match_bit ^ bit ) */ + testb %ah, %ah + jz 1b + /* Restore registers, clear unwanted high bit of result, and return */ + movl %edi, %ebx + popw %dx + popw %cx + popl %edi + xorb %ah, %ah + ret + .size rc_bittree_match, . - rc_bittree_match + +/***************************************************************************** + * Decode direct bits (no probability estimates) + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %cx : number of bits to decode + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %eax : decoded bits + * Corrupts: + * none + ***************************************************************************** + */ +rc_direct: + /* Preserve registers */ + pushl %ebx + pushw %cx + pushl %edx + /* Initialise registers */ + xorl %edx, %edx +1: /* Perform normalisation */ + call rc_normalise + /* Decode bit */ + shrl $1, %eax + movl %eax, rc_range(%ebp) + movl rc_code(%ebp), %ebx + subl %eax, %ebx + js 2f + movl %ebx, rc_code(%ebp) +2: rcll %ebx + rcll %edx + xorb $1, %dl + ADDR16 loop 1b + /* Restore registers and return */ + movl %edx, %eax + popl %edx + popw %cx + popl %ebx + ret + .size rc_direct, . - rc_direct + +/***************************************************************************** + * Decode an LZMA literal + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %es:%edi : uncompressed output data pointer + * %edx : LZMA state + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %es:%edi : uncompressed output data pointer (updated) + * %edx : LZMA state + * CF : end of payload marker found (always zero) + * Corrupts: + * %eax + * %ebx + * %ecx + ***************************************************************************** + * + * Literals are coded as an eight-bit tree, using a match byte if the + * previous symbol was not a literal. + * + */ +lzma_literal: + /* Get most recent output byte, if available */ + xorl %ebx, %ebx + cmpl %edi, out_start(%ebp) + je 1f + movb %es:-1(%edi), %bh +1: /* Locate probability estimate set */ + shrb $( 8 - LZMA_LC ), %bh + shlb $1, %bh + leaw literal(%ebx,%ebx,2), %bx + /* Get match byte, if applicable */ + xorw %cx, %cx + cmpb $STATE_LIT_MAX, %dl + jbe 1f + movl rep0(%ebp), %eax + notl %eax + movb %es:(%edi,%eax), %cl + movb $1, %ch +1: /* Decode bittree */ + call rc_bittree_match + /* Store output byte */ + ADDR32 stosb + print_hex_byte %al + print_character $(' ') + /* Update LZMA state */ + subb $3, %dl + jns 1f + xorb %dl, %dl +1: cmpb $7, %dl + jb 1f + subb $3, %dl +1: /* Clear CF and return */ + clc + ret + .size lzma_literal, . - lzma_literal + +/***************************************************************************** + * Decode an LZMA length + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %ebx : length parameter pointer (offset from %ebp) + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * Corrupts: + * %ebx + ***************************************************************************** + * + * Lengths are encoded as: + * + * "0" + 3 bits : lengths 2-9 ("low") + * "10" + 3 bits : lengths 10-17 ("mid") + * "11" + 8 bits : lengths 18-273 ("high") + */ +lzma_len: + /* Preserve registers */ + pushl %eax + pushl %ecx + pushl %edi + movl %ebx, %edi + /* Start by assuming three bits and a base length of 2 */ + movw $3, %cx + movw $2, len(%ebp) + /* Check low-length choice bit */ + leal choice(%edi), %ebx + call rc_bit + leal low(%edi), %ebx + jz 1f + /* Check high-length choice bit */ + leal choice2(%edi), %ebx + call rc_bit + leal mid(%edi), %ebx + movb $10, len(%ebp) + jz 1f + leal high(%edi), %ebx + movb $8, %cl + movb $18, len(%ebp) +1: /* Get encoded length */ + call rc_bittree + addw %ax, len(%ebp) + /* Restore registers and return */ + movl %edi, %ebx + popl %edi + popl %ecx + popl %eax + ret + .size lzma_len, . - lzma_len + +/***************************************************************************** + * Copy (possibly repeated) matched data + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %es:%edi : uncompressed output data pointer + * %cl : repeated match distance index (for repeated matches) + * %eax : match distance (for non-repeated matches) + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %es:%edi : uncompressed output data pointer + * CF : match distance is out of range + * Corrupts: + * %eax + * %ebx + * %ecx + ***************************************************************************** + */ +match: /* Update repeated match list */ + print_character $('[') + movl $3, %ecx + jmp 1f +match_rep: + print_character $('[') + print_character $('R') + print_hex_byte %cl + print_character $('=') + movzbl %cl, %ecx + movl reps(%ebp,%ecx,4), %eax + jcxz 2f +1: movl (reps-4)(%ebp,%ecx,4), %ebx + movl %ebx, reps(%ebp,%ecx,4) + loop 1b + movl %eax, rep0(%ebp) +2: /* Preserve registers */ + pushl %esi + /* Get stored match length */ + movzwl len(%ebp), %ecx + print_hex_dword %eax + print_character $('+') + print_hex_word %cx + print_character $(']') + print_character $(' ') + /* Abort with CF set if match distance is out of range */ + movl out_start(%ebp), %esi + negl %esi + leal -1(%edi,%esi), %esi + cmpl %eax, %esi + jc 99f + /* Perform copy */ + notl %eax + leal (%edi,%eax), %esi + ADDR32 es rep movsb +99: /* Restore registers and return */ + popl %esi + ret + .size match, . - match + +/***************************************************************************** + * Decode an LZMA match + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %es:%edi : uncompressed output data pointer + * %edx : LZMA state + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %es:%edi : uncompressed output data pointer + * %edx : LZMA state + * CF : end of payload marker found + * Corrupts: + * %eax + * %ebx + * %ecx + ***************************************************************************** + * + * Matches are encoded as an LZMA length followed by a 6-bit "distance + * slot" code, 0-26 fixed-probability bits, and 0-5 context encoded + * bits. + */ +lzma_match: + /* Preserve registers */ + pushl %edi + /* Update LZMA state */ + cmpb $STATE_LIT_MAX, %dl + movb $STATE_LIT_MATCH, %dl + jbe 1f + movb $STATE_NONLIT_MATCH, %dl +1: /* Decode length */ + movl $match_len_dec, %ebx + call lzma_len + /* Decode distance slot */ + movw len(%ebp), %bx + subw $2, %bx + cmpw $4, %bx + jb 1f + movw $3, %bx +1: shlw $7, %bx + addw $dist_slot, %bx + movw $6, %cx + call rc_bittree + /* Distance slots 0-3 are literal distances */ + cmpb $4, %al + jb 99f + /* Determine initial bits: 10/11 for even/odd distance codes */ + movl %eax, %edi + andw $1, %di + orw $2, %di + /* Determine number of context-encoded bits */ + movw %ax, %cx + shrb $1, %cl + decb %cl + /* Select context to be used in absence of fixed-probability bits */ + movl %edi, %ebx + shlw %cl, %bx + subw %ax, %bx + leaw (dist_special-2)(%ebx,%ebx), %bx + /* Decode fixed-probability bits, if any */ + cmpb $6, %cl + jb 1f + subb $4, %cl + shll %cl, %edi + call rc_direct + orl %eax, %edi + /* Select context to be used in presence of fixed-probability bits */ + movb $4, %cl + movl $dist_align, %ebx +1: /* Decode context-encoded bits */ + shll %cl, %edi + call rc_bittree_reverse + orl %edi, %eax +99: /* Restore registers and tail-call */ + popl %edi + jmp match + .size lzma_match, . - lzma_match + +/***************************************************************************** + * Decode an LZMA repeated match + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %es:%edi : uncompressed output data pointer + * %edx : LZMA state + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %es:%edi : uncompressed output data pointer + * %edx : LZMA state + * CF : end of payload marker found + * Corrupts: + * %eax + * %ebx + * %ecx + ***************************************************************************** + * + * Repeated matches are encoded as: + * + * "00" : shortrep0 (implicit length 1) + * "01" + len : longrep0 + * "10" + len : longrep1 + * "110" + len : longrep2 + * "111" + len : longrep3 + */ +lzma_rep_match: + /* Initially assume longrep0 */ + movw $(STATE_LIT_LONGREP << 8), %cx + /* Get is_rep0 bit */ + leal is_rep0(,%edx,2), %ebx + call rc_bit + jnz 1f + /* Get is_rep0_long bit */ + leal is_rep0_long(,%edx,2), %ebx + call rc_bit + jnz 98f + movw $1, len(%ebp) + movb $STATE_LIT_SHORTREP, %ch + jmp 99f +1: /* Get is_rep1 bit */ + incb %cl + leal is_rep1(,%edx,2), %ebx + call rc_bit + jz 98f + /* Get is_rep2 bit */ + incb %cl + leal is_rep2(,%edx,2), %ebx + call rc_bit + adcb $0, %cl +98: /* Decode length */ + movl $rep_len_dec, %ebx + call lzma_len +99: /* Update LZMA state */ + cmpb $STATE_LIT_MAX, %dl + movb %ch, %dl + jbe 1f + movb $STATE_NONLIT_REP, %dl +1: /* Tail call */ + jmp match_rep + .size lzma_match, . - lzma_match + +/***************************************************************************** + * Decode one LZMA symbol + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %es:%edi : uncompressed output data pointer + * %edx : LZMA state + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %es:%edi : uncompressed output data pointer (updated) + * %edx : LZMA state + * CF : end of payload marker found + * Corrupts: + * %eax + * %ebx + * %ecx + ***************************************************************************** + */ +lzma_decode: + /* Get is_match bit */ + leal is_match(,%edx,2), %ebx + call rc_bit + jz lzma_literal + /* Get is_rep bit */ + leal is_rep(,%edx,2), %ebx + call rc_bit + jz lzma_match + jmp lzma_rep_match + .size lzma_decode, . - lzma_decode + +/**************************************************************************** + * Undo effect of branch-call-jump (BCJ) filter + * + * Parameters: + * %es:%esi : start of uncompressed output data (note %es) + * %es:%edi : end of uncompressed output data + * Returns: + * Corrupts: + * %eax + * %ebx + * %ecx + * %edx + * %esi + ***************************************************************************** + */ +bcj_filter: + /* Store (negative) start of data in %edx */ + movl %esi, %edx + negl %edx + /* Calculate limit in %ecx */ + leal -5(%edi,%edx), %ecx +1: /* Calculate offset in %ebx */ + leal (%esi,%edx), %ebx + /* Check for end of data */ + cmpl %ecx, %ebx + ja 99f + /* Check for an opcode which would be followed by a rel32 address */ + ADDR32 es lodsb + andb $0xfe, %al + cmpb $0xe8, %al + jne 1b + /* Get current jump target value in %eax */ + ADDR32 es lodsl + /* Convert absolute addresses in the range [0,limit) back to + * relative addresses in the range [-offset,limit-offset). + */ + cmpl %ecx, %eax + jae 2f + subl %ebx,%es:-4(%esi) +2: /* Convert negative numbers in the range [-offset,0) back to + * positive numbers in the range [limit-offset,limit). + */ + notl %eax /* Range is now [0,offset) */ + cmpl %ebx, %eax + jae 1b + addl %ecx,%es:-4(%esi) + jmp 1b +99: /* Return */ + ret + .size bcj_filter, . - bcj_filter + +/**************************************************************************** + * decompress (real-mode or 16/32-bit protected-mode near call) + * + * Decompress data + * + * Parameters (passed via registers): + * %ds:%esi : Start of compressed input data + * %es:%edi : Start of output buffer + * Returns: + * %ds:%esi - End of compressed input data + * %es:%edi - End of decompressed output data + * All other registers are preserved + * + * NOTE: It would be possible to build a smaller version of the + * decompression code for -DKEEP_IT_REAL by using 16-bit registers + * where possible. + **************************************************************************** + */ + .globl decompress +decompress: + /* Preserve registers */ + pushl %eax + pushl %ebx + pushl %ecx + pushl %edx + pushl %ebp + /* Allocate parameter block */ + subl $sizeof__lzma_dec, %esp + movl %esp, %ebp + /* Zero parameter block and set all probabilities to 0.5 */ + pushl %edi + pushw %es + pushw %ss + popw %es + movl %ebp, %edi + xorl %eax, %eax + movl $( sizeof__lzma_dec / 4 ), %ecx + ADDR32 rep stosl + leal probs(%ebp), %edi + movw $( ( 1 << 11 ) / 2 ), %ax + movl $( ( sizeof__lzma_dec - probs ) / 2 ), %ecx + ADDR32 rep stosw + popw %es + popl %edi + /* Initialise remaining parameters */ + movl %edi, out_start(%ebp) + print_character $('\n') + ADDR32 lodsb /* discard initial byte */ + print_hex_byte %al + ADDR32 lodsl + bswapl %eax + print_hex_dword %eax + print_character $('\n') + movl %eax, rc_code(%ebp) + decl rc_range(%ebp) + movl $STATE_LIT_LIT, %edx +1: /* Decompress until we reach end of buffer */ + call lzma_decode + jnc 1b + call rc_normalise + print_character $('\n') + /* Undo BCJ filter */ + pushl %esi + movl out_start(%ebp), %esi + call bcj_filter + popl %esi + /* Restore registers and return */ + addl $sizeof__lzma_dec, %esp + popl %ebp + popl %edx + popl %ecx + popl %ebx + popl %eax + ret + + /* Specify minimum amount of stack space required */ + .globl _min_decompress_stack + .equ _min_decompress_stack, ( sizeof__lzma_dec + 512 /* margin */ ) diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/unlzma16.S b/qemu/roms/ipxe/src/arch/i386/prefix/unlzma16.S new file mode 100644 index 000000000..32b43f0dc --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/unlzma16.S @@ -0,0 +1,9 @@ +/* + * 16-bit version of the decompressor + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + +#define CODE16 +#include "unlzma.S" diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/unnrv2b.S b/qemu/roms/ipxe/src/arch/i386/prefix/unnrv2b.S deleted file mode 100644 index f5724c134..000000000 --- a/qemu/roms/ipxe/src/arch/i386/prefix/unnrv2b.S +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer - * - * This file 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. - * - * Originally this code was part of ucl the data compression library - * for upx the ``Ultimate Packer of eXecutables''. - * - * - Converted to gas assembly, and refitted to work with etherboot. - * Eric Biederman 20 Aug 2002 - * - * - Structure modified to be a subroutine call rather than an - * executable prefix. - * Michael Brown 30 Mar 2004 - * - * - Modified to be compilable as either 16-bit or 32-bit code. - * Michael Brown 9 Mar 2005 - */ - -FILE_LICENCE ( GPL2_OR_LATER ) - -/**************************************************************************** - * This file provides the decompress() and decompress16() functions - * which can be called in order to decompress an image compressed with - * the nrv2b utility in src/util. - * - * These functions are designed to be called by the prefix. They are - * position-independent code. - * - * The same basic assembly code is used to compile both - * decompress() and decompress16(). - **************************************************************************** - */ - - .text - .arch i386 - .section ".prefix.lib", "ax", @progbits - -#ifdef CODE16 -/**************************************************************************** - * decompress16 (real-mode near call, position independent) - * - * Decompress data in 16-bit mode - * - * Parameters (passed via registers): - * %ds:%esi - Start of compressed input data - * %es:%edi - Start of output buffer - * Returns: - * %ds:%esi - End of compressed input data - * %es:%edi - End of decompressed output data - * All other registers are preserved - * - * NOTE: It would be possible to build a smaller version of the - * decompression code for -DKEEP_IT_REAL by using - * #define REG(x) x - * to use 16-bit registers where possible. This would impose limits - * that the compressed data size must be in the range [1,65533-%si] - * and the uncompressed data size must be in the range [1,65536-%di] - * (where %si and %di are the input values for those registers). Note - * particularly that the lower limit is 1, not 0, and that the upper - * limit on the input (compressed) data really is 65533, since the - * algorithm may read up to three bytes beyond the end of the input - * data, since it reads dwords. - **************************************************************************** - */ - -#define REG(x) e ## x -#define ADDR32 addr32 - - .code16 - .globl decompress16 -decompress16: - -#else /* CODE16 */ - -/**************************************************************************** - * decompress (32-bit protected-mode near call, position independent) - * - * Parameters (passed via registers): - * %ds:%esi - Start of compressed input data - * %es:%edi - Start of output buffer - * Returns: - * %ds:%esi - End of compressed input data - * %es:%edi - End of decompressed output data - * All other registers are preserved - **************************************************************************** - */ - -#define REG(x) e ## x -#define ADDR32 - - .code32 - .globl decompress -decompress: - -#endif /* CODE16 */ - -#define xAX REG(ax) -#define xCX REG(cx) -#define xBP REG(bp) -#define xSI REG(si) -#define xDI REG(di) - - /* Save registers */ - push %xAX - pushl %ebx - push %xCX - push %xBP - /* Do the decompression */ - cld - xor %xBP, %xBP - dec %xBP /* last_m_off = -1 */ - jmp dcl1_n2b - -decompr_literals_n2b: - ADDR32 movsb -decompr_loop_n2b: - addl %ebx, %ebx - jnz dcl2_n2b -dcl1_n2b: - call getbit32 -dcl2_n2b: - jc decompr_literals_n2b - xor %xAX, %xAX - inc %xAX /* m_off = 1 */ -loop1_n2b: - call getbit1 - adc %xAX, %xAX /* m_off = m_off*2 + getbit() */ - call getbit1 - jnc loop1_n2b /* while(!getbit()) */ - sub $3, %xAX - jb decompr_ebpeax_n2b /* if (m_off == 2) goto decompr_ebpeax_n2b ? */ - shl $8, %xAX - ADDR32 movb (%xSI), %al /* m_off = (m_off - 3)*256 + src[ilen++] */ - inc %xSI - xor $-1, %xAX - jz decompr_end_n2b /* if (m_off == 0xffffffff) goto decomp_end_n2b */ - mov %xAX, %xBP /* last_m_off = m_off ?*/ -decompr_ebpeax_n2b: - xor %xCX, %xCX - call getbit1 - adc %xCX, %xCX /* m_len = getbit() */ - call getbit1 - adc %xCX, %xCX /* m_len = m_len*2 + getbit()) */ - jnz decompr_got_mlen_n2b /* if (m_len == 0) goto decompr_got_mlen_n2b */ - inc %xCX /* m_len++ */ -loop2_n2b: - call getbit1 - adc %xCX, %xCX /* m_len = m_len*2 + getbit() */ - call getbit1 - jnc loop2_n2b /* while(!getbit()) */ - inc %xCX - inc %xCX /* m_len += 2 */ -decompr_got_mlen_n2b: - cmp $-0xd00, %xBP - adc $1, %xCX /* m_len = m_len + 1 + (last_m_off > 0xd00) */ - push %xSI - ADDR32 lea (%xBP,%xDI), %xSI /* m_pos = dst + olen + -m_off */ - rep - es ADDR32 movsb /* dst[olen++] = *m_pos++ while(m_len > 0) */ - pop %xSI - jmp decompr_loop_n2b - - -getbit1: - addl %ebx, %ebx - jnz 1f -getbit32: - ADDR32 movl (%xSI), %ebx - sub $-4, %xSI /* sets carry flag */ - adcl %ebx, %ebx -1: - ret - -decompr_end_n2b: - /* Restore registers and return */ - pop %xBP - pop %xCX - popl %ebx - pop %xAX - ret diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/unnrv2b16.S b/qemu/roms/ipxe/src/arch/i386/prefix/unnrv2b16.S deleted file mode 100644 index b24c2846f..000000000 --- a/qemu/roms/ipxe/src/arch/i386/prefix/unnrv2b16.S +++ /dev/null @@ -1,9 +0,0 @@ -/* - * 16-bit version of the decompressor - * - */ - -FILE_LICENCE ( GPL2_OR_LATER ) - -#define CODE16 -#include "unnrv2b.S" diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/usbdisk.S b/qemu/roms/ipxe/src/arch/i386/prefix/usbdisk.S index fa7d1956e..9676406e2 100644 --- a/qemu/roms/ipxe/src/arch/i386/prefix/usbdisk.S +++ b/qemu/roms/ipxe/src/arch/i386/prefix/usbdisk.S @@ -1,3 +1,5 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + .text .arch i386 .section ".prefix", "awx", @progbits @@ -6,18 +8,27 @@ #include "mbr.S" -/* Partition table: ZIP-compatible partition 4, 64 heads, 32 sectors/track */ +/* Partition table: 64 heads, 32 sectors/track (ZIP-drive compatible) */ .org 446 .space 16 .space 16 - .space 16 - .byte 0x80, 0x01, 0x01, 0x00 - .byte 0xeb, 0x3f, 0x20, 0x01 + /* Partition 3: log partition (for CONSOLE_INT13) */ + .byte 0x00, 0x01, 0x01, 0x00 + .byte 0xe0, 0x3f, 0x20, 0x00 .long 0x00000020 - .long 0x00000fe0 + .long 0x000007e0 + /* Partition 4: boot partition */ + .byte 0x80, 0x00, 0x01, 0x01 + .byte 0xeb, 0x3f, 0x20, 0x02 + .long 0x00000800 + .long 0x00001000 .org 510 .byte 0x55, 0xaa -/* Skip to start of partition */ +/* Skip to start of log partition */ .org 32 * 512 + .ascii "iPXE LOG\n\n" + +/* Skip to start of boot partition */ + .org 2048 * 512 diff --git a/qemu/roms/ipxe/src/arch/i386/scripts/i386.lds b/qemu/roms/ipxe/src/arch/i386/scripts/i386.lds index 98f95cb23..38c89e14b 100644 --- a/qemu/roms/ipxe/src/arch/i386/scripts/i386.lds +++ b/qemu/roms/ipxe/src/arch/i386/scripts/i386.lds @@ -26,6 +26,13 @@ SECTIONS { PROVIDE ( _max_align = 16 ); + /* + * Allow decompressor to require a minimum amount of temporary stack + * space. + * + */ + PROVIDE ( _min_decompress_stack = 0 ); + /* * The prefix * @@ -34,6 +41,7 @@ SECTIONS { .prefix 0x0 : AT ( _prefix_lma ) { _prefix = .; *(.prefix) + *(SORT(.pci_devlist.*)) *(.prefix.*) _mprefix = .; } .bss.prefix (NOLOAD) : AT ( _end_lma ) { @@ -87,6 +95,7 @@ SECTIONS { *(.bss16.*) *(.stack16) *(.stack16.*) + . = MAX ( ., _mdata16 + _min_decompress_stack ); _edata16 = .; } _data16_filesz = ABSOLUTE ( _mdata16 ) - ABSOLUTE ( _data16 ); diff --git a/qemu/roms/ipxe/src/arch/i386/transitions/liba20.S b/qemu/roms/ipxe/src/arch/i386/transitions/liba20.S index 684697525..6c1e1f62f 100644 --- a/qemu/roms/ipxe/src/arch/i386/transitions/liba20.S +++ b/qemu/roms/ipxe/src/arch/i386/transitions/liba20.S @@ -16,9 +16,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .arch i386 diff --git a/qemu/roms/ipxe/src/arch/i386/transitions/libkir.S b/qemu/roms/ipxe/src/arch/i386/transitions/libkir.S index 1176fcced..fa9459d52 100644 --- a/qemu/roms/ipxe/src/arch/i386/transitions/libkir.S +++ b/qemu/roms/ipxe/src/arch/i386/transitions/libkir.S @@ -5,7 +5,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) /**************************************************************************** * This file defines libkir: an interface between external and diff --git a/qemu/roms/ipxe/src/arch/i386/transitions/librm.S b/qemu/roms/ipxe/src/arch/i386/transitions/librm.S index 2e447b030..863e22415 100644 --- a/qemu/roms/ipxe/src/arch/i386/transitions/librm.S +++ b/qemu/roms/ipxe/src/arch/i386/transitions/librm.S @@ -5,7 +5,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) /* Drag in local definitions */ #include "librm.h" diff --git a/qemu/roms/ipxe/src/arch/i386/transitions/librm_mgmt.c b/qemu/roms/ipxe/src/arch/i386/transitions/librm_mgmt.c index cc4765de2..becb02677 100644 --- a/qemu/roms/ipxe/src/arch/i386/transitions/librm_mgmt.c +++ b/qemu/roms/ipxe/src/arch/i386/transitions/librm_mgmt.c @@ -5,7 +5,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -103,7 +103,7 @@ void init_idt ( void ) { ( uint32_t ) vec->next ); set_interrupt_vector ( intr, vec ); } - DBGC ( &intr_vec[0], "INTn vector at %p+%xn (phys %#lx+%xn)\n", + DBGC ( &intr_vec[0], "INTn vector at %p+%zxn (phys %#lx+%zxn)\n", intr_vec, sizeof ( intr_vec[0] ), virt_to_phys ( intr_vec ), sizeof ( intr_vec[0] ) ); diff --git a/qemu/roms/ipxe/src/arch/i386/transitions/librm_test.c b/qemu/roms/ipxe/src/arch/i386/transitions/librm_test.c index e07cfccdd..f1a517eda 100644 --- a/qemu/roms/ipxe/src/arch/i386/transitions/librm_test.c +++ b/qemu/roms/ipxe/src/arch/i386/transitions/librm_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -114,4 +118,5 @@ struct self_test librm_test __self_test = { .exec = librm_test_exec, }; +REQUIRING_SYMBOL ( librm_test ); REQUIRE_OBJECT ( test ); diff --git a/qemu/roms/ipxe/src/arch/x86/Makefile b/qemu/roms/ipxe/src/arch/x86/Makefile index e555587df..98c49b98d 100644 --- a/qemu/roms/ipxe/src/arch/x86/Makefile +++ b/qemu/roms/ipxe/src/arch/x86/Makefile @@ -9,9 +9,14 @@ SRCDIRS += arch/x86/interface/efi SRCDIRS += arch/x86/prefix SRCDIRS += arch/x86/hci/commands SRCDIRS += arch/x86/drivers/xen +SRCDIRS += arch/x86/drivers/hyperv # breaks building some of the linux-related objects CFLAGS += -Ulinux # disable valgrind CFLAGS += -DNVALGRIND + +# Include Hyper-V driver in the all-drivers build +# +DRIVERS_hyperv += hyperv diff --git a/qemu/roms/ipxe/src/arch/x86/Makefile.efi b/qemu/roms/ipxe/src/arch/x86/Makefile.efi index 13a69d9f7..f73bc7d5d 100644 --- a/qemu/roms/ipxe/src/arch/x86/Makefile.efi +++ b/qemu/roms/ipxe/src/arch/x86/Makefile.efi @@ -17,7 +17,7 @@ NON_AUTO_MEDIA += efirom # Include SNP driver in the all-drivers build # -DRIVERS += snp +DRIVERS_net += snp # Rules for building EFI files # diff --git a/qemu/roms/ipxe/src/arch/x86/core/cpuid.c b/qemu/roms/ipxe/src/arch/x86/core/cpuid.c index 5908f4419..bc5a6c68c 100644 --- a/qemu/roms/ipxe/src/arch/x86/core/cpuid.c +++ b/qemu/roms/ipxe/src/arch/x86/core/cpuid.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/x86/core/cpuid_settings.c b/qemu/roms/ipxe/src/arch/x86/core/cpuid_settings.c index 42dea9336..08bd3918a 100644 --- a/qemu/roms/ipxe/src/arch/x86/core/cpuid_settings.c +++ b/qemu/roms/ipxe/src/arch/x86/core/cpuid_settings.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/x86/core/debugcon.c b/qemu/roms/ipxe/src/arch/x86/core/debugcon.c index 263cb4af1..60de61f55 100644 --- a/qemu/roms/ipxe/src/arch/x86/core/debugcon.c +++ b/qemu/roms/ipxe/src/arch/x86/core/debugcon.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/arch/x86/core/pcidirect.c b/qemu/roms/ipxe/src/arch/x86/core/pcidirect.c index dbc8317b8..9b8e6b1d9 100644 --- a/qemu/roms/ipxe/src/arch/x86/core/pcidirect.c +++ b/qemu/roms/ipxe/src/arch/x86/core/pcidirect.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/i386/core/pic8259.c b/qemu/roms/ipxe/src/arch/x86/core/pic8259.c similarity index 100% rename from qemu/roms/ipxe/src/arch/i386/core/pic8259.c rename to qemu/roms/ipxe/src/arch/x86/core/pic8259.c diff --git a/qemu/roms/ipxe/src/arch/x86/core/pit8254.c b/qemu/roms/ipxe/src/arch/x86/core/pit8254.c new file mode 100644 index 000000000..da2099263 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/x86/core/pit8254.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** @file + * + * 8254 Programmable Interval Timer + * + */ + +/** + * Delay for a fixed number of timer ticks using the speaker channel + * + * @v ticks Number of timer ticks for which to delay + */ +void pit8254_speaker_delay ( unsigned int ticks ) { + uint8_t spkr; + uint8_t cmd; + uint8_t low; + uint8_t high; + + /* Sanity check */ + assert ( ticks <= 0xffff ); + + /* Disable speaker, set speaker channel gate input high */ + spkr = inb ( PIT8254_SPKR ); + spkr &= ~PIT8254_SPKR_ENABLE; + spkr |= PIT8254_SPKR_GATE; + outb ( spkr, PIT8254_SPKR ); + + /* Program speaker channel to "interrupt" on terminal count */ + cmd = ( PIT8254_CMD_CHANNEL ( PIT8254_CH_SPKR ) | + PIT8254_CMD_ACCESS_LOHI | PIT8254_CMD_OP_TERMINAL | + PIT8254_CMD_BINARY ); + low = ( ( ticks >> 0 ) & 0xff ); + high = ( ( ticks >> 8 ) & 0xff ); + outb ( cmd, PIT8254_CMD ); + outb ( low, PIT8254_DATA ( PIT8254_CH_SPKR ) ); + outb ( high, PIT8254_DATA ( PIT8254_CH_SPKR ) ); + + /* Wait for channel to "interrupt" */ + do { + spkr = inb ( PIT8254_SPKR ); + } while ( ! ( spkr & PIT8254_SPKR_OUT ) ); +} diff --git a/qemu/roms/ipxe/src/arch/x86/core/vram_settings.c b/qemu/roms/ipxe/src/arch/x86/core/vram_settings.c new file mode 100644 index 000000000..9c169b40c --- /dev/null +++ b/qemu/roms/ipxe/src/arch/x86/core/vram_settings.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** @file + * + * Video RAM dump + * + */ + +/** Video RAM base address */ +#define VRAM_BASE 0xb8000 + +/** Video RAM length */ +#define VRAM_LEN \ + ( 80 /* columns */ * 25 /* rows */ * 2 /* bytes per character */ ) + +/** + * Fetch video RAM setting + * + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int vram_fetch ( void *data, size_t len ) { + userptr_t vram = phys_to_user ( VRAM_BASE ); + + /* Copy video RAM */ + if ( len > VRAM_LEN ) + len = VRAM_LEN; + copy_from_user ( data, vram, 0, len ); + + return VRAM_LEN; +} + +/** Video RAM setting */ +const struct setting vram_setting __setting ( SETTING_MISC, vram ) = { + .name = "vram", + .description = "Video RAM", + .type = &setting_type_base64, + .scope = &builtin_scope, +}; + +/** Video RAM built-in setting */ +struct builtin_setting vram_builtin_setting __builtin_setting = { + .setting = &vram_setting, + .fetch = vram_fetch, +}; diff --git a/qemu/roms/ipxe/src/arch/x86/core/x86_bigint.c b/qemu/roms/ipxe/src/arch/x86/core/x86_bigint.c index 418ac2309..6413b2fa8 100644 --- a/qemu/roms/ipxe/src/arch/x86/core/x86_bigint.c +++ b/qemu/roms/ipxe/src/arch/x86/core/x86_bigint.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/x86/core/x86_io.c b/qemu/roms/ipxe/src/arch/x86/core/x86_io.c index 9b2d2d935..3081fa8b9 100644 --- a/qemu/roms/ipxe/src/arch/x86/core/x86_io.c +++ b/qemu/roms/ipxe/src/arch/x86/core/x86_io.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/x86/core/x86_string.c b/qemu/roms/ipxe/src/arch/x86/core/x86_string.c index d48347c96..7d5e4a5f1 100644 --- a/qemu/roms/ipxe/src/arch/x86/core/x86_string.c +++ b/qemu/roms/ipxe/src/arch/x86/core/x86_string.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ /** @file @@ -23,7 +27,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include @@ -104,87 +108,3 @@ void * __memmove ( void *dest, const void *src, size_t len ) { return __memcpy_reverse ( dest, src, len ); } } - -/** - * Swap memory areas - * - * @v dest Destination address - * @v src Source address - * @v len Length - * @ret dest Destination address - */ -void * memswap ( void *dest, void *src, size_t len ) { - size_t discard_c; - int discard; - - __asm__ __volatile__ ( "\n1:\n\t" - "dec %2\n\t" - "js 2f\n\t" - "movb (%0,%2), %b3\n\t" - "xchgb (%1,%2), %b3\n\t" - "movb %b3, (%0,%2)\n\t" - "jmp 1b\n\t" - "2:\n\t" - : "=r" ( src ), "=r" ( dest ), - "=&c" ( discard_c ), "=&q" ( discard ) - : "0" ( src ), "1" ( dest ), "2" ( len ) - : "memory" ); - - return dest; -} - -/** - * Calculate length of string - * - * @v string String - * @ret len Length (excluding NUL) - */ -size_t strlen ( const char *string ) { - const char *discard_D; - size_t len_plus_one; - - __asm__ __volatile__ ( "repne scasb\n\t" - "not %1\n\t" - : "=&D" ( discard_D ), "=&c" ( len_plus_one ) - : "0" ( string ), "1" ( -1UL ), "a" ( 0 ) ); - - return ( len_plus_one - 1 ); -} - -/** - * Compare strings (up to a specified length) - * - * @v str1 First string - * @v str2 Second string - * @v len Maximum length - * @ret diff Difference - */ -int strncmp ( const char *str1, const char *str2, size_t len ) { - const void *discard_S; - const void *discard_D; - size_t discard_c; - int diff; - - __asm__ __volatile__ ( "\n1:\n\t" - "dec %2\n\t" - "js 2f\n\t" - "lodsb\n\t" - "scasb\n\t" - "jne 3f\n\t" - "testb %b3, %b3\n\t" - "jnz 1b\n\t" - /* Equal */ - "\n2:\n\t" - "xor %3, %3\n\t" - "jmp 4f\n\t" - /* Not equal; CF indicates difference */ - "\n3:\n\t" - "sbb %3, %3\n\t" - "orb $1, %b3\n\t" - "\n4:\n\t" - : "=&S" ( discard_S ), "=&D" ( discard_D ), - "=&c" ( discard_c ), "=&a" ( diff ) - : "0" ( str1 ), "1" ( str2 ), "2" ( len ) ); - - return diff; -} diff --git a/qemu/roms/ipxe/src/arch/x86/core/x86_tcpip.c b/qemu/roms/ipxe/src/arch/x86/core/x86_tcpip.c index 8a4ce5152..88042f5f7 100644 --- a/qemu/roms/ipxe/src/arch/x86/core/x86_tcpip.c +++ b/qemu/roms/ipxe/src/arch/x86/core/x86_tcpip.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/arch/x86/core/x86_uart.c b/qemu/roms/ipxe/src/arch/x86/core/x86_uart.c new file mode 100644 index 000000000..e455775bf --- /dev/null +++ b/qemu/roms/ipxe/src/arch/x86/core/x86_uart.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * 16550-compatible UART + * + */ + +#include +#include + +/** UART port bases */ +static uint16_t uart_base[] = { + [COM1] = 0x3f8, + [COM2] = 0x2f8, + [COM3] = 0x3e8, + [COM4] = 0x2e8, +}; + +/** + * Select UART port + * + * @v uart UART + * @v port Port number, or 0 to disable + * @ret rc Return status code + */ +int uart_select ( struct uart *uart, unsigned int port ) { + int rc; + + /* Set new UART base */ + if ( port >= ( sizeof ( uart_base ) / sizeof ( uart_base[0] ) ) ) { + rc = -ENODEV; + goto err; + } + uart->base = ( ( void * ) ( intptr_t ) uart_base[port] ); + + /* Check that UART exists */ + if ( ( rc = uart_exists ( uart ) ) != 0 ) + goto err; + + return 0; + + err: + uart->base = NULL; + return rc; +} diff --git a/qemu/roms/ipxe/src/arch/x86/drivers/hyperv/hyperv.c b/qemu/roms/ipxe/src/arch/x86/drivers/hyperv/hyperv.c new file mode 100644 index 000000000..f73829bd5 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/x86/drivers/hyperv/hyperv.c @@ -0,0 +1,597 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Hyper-V driver + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hyperv.h" + +/** Maximum time to wait for a message response + * + * This is a policy decision. + */ +#define HV_MESSAGE_MAX_WAIT_MS 1000 + +/** + * Convert a Hyper-V status code to an iPXE status code + * + * @v status Hyper-V status code + * @ret rc iPXE status code (before negation) + */ +#define EHV( status ) EPLATFORM ( EINFO_EPLATFORM, (status) ) + +/** + * Allocate zeroed pages + * + * @v hv Hyper-V hypervisor + * @v ... Page addresses to fill in, terminated by NULL + * @ret rc Return status code + */ +__attribute__ (( sentinel )) int +hv_alloc_pages ( struct hv_hypervisor *hv, ... ) { + va_list args; + void **page; + int i; + + /* Allocate and zero pages */ + va_start ( args, hv ); + for ( i = 0 ; ( ( page = va_arg ( args, void ** ) ) != NULL ); i++ ) { + *page = malloc_dma ( PAGE_SIZE, PAGE_SIZE ); + if ( ! *page ) + goto err_alloc; + memset ( *page, 0, PAGE_SIZE ); + } + va_end ( args ); + + return 0; + + err_alloc: + va_end ( args ); + va_start ( args, hv ); + for ( ; i >= 0 ; i-- ) { + page = va_arg ( args, void ** ); + free_dma ( *page, PAGE_SIZE ); + } + va_end ( args ); + return -ENOMEM; +} + +/** + * Free pages + * + * @v hv Hyper-V hypervisor + * @v ... Page addresses, terminated by NULL + */ +__attribute__ (( sentinel )) void +hv_free_pages ( struct hv_hypervisor *hv, ... ) { + va_list args; + void *page; + + va_start ( args, hv ); + while ( ( page = va_arg ( args, void * ) ) != NULL ) + free_dma ( page, PAGE_SIZE ); + va_end ( args ); +} + +/** + * Allocate message buffer + * + * @v hv Hyper-V hypervisor + * @ret rc Return status code + */ +static int hv_alloc_message ( struct hv_hypervisor *hv ) { + + /* Allocate buffer. Must be aligned to at least 8 bytes and + * must not cross a page boundary, so align on its own size. + */ + hv->message = malloc_dma ( sizeof ( *hv->message ), + sizeof ( *hv->message ) ); + if ( ! hv->message ) + return -ENOMEM; + + return 0; +} + +/** + * Free message buffer + * + * @v hv Hyper-V hypervisor + */ +static void hv_free_message ( struct hv_hypervisor *hv ) { + + /* Free buffer */ + free_dma ( hv->message, sizeof ( *hv->message ) ); +} + +/** + * Check whether or not we are running in Hyper-V + * + * @v hv Hyper-V hypervisor + * @ret rc Return status code + */ +static int hv_check_hv ( struct hv_hypervisor *hv ) { + struct x86_features features; + uint32_t interface_id; + uint32_t discard_ebx; + uint32_t discard_ecx; + uint32_t discard_edx; + uint32_t available; + uint32_t permissions; + + /* Check for presence of a hypervisor (not necessarily Hyper-V) */ + x86_features ( &features ); + if ( ! ( features.intel.ecx & CPUID_FEATURES_INTEL_ECX_HYPERVISOR ) ) { + DBGC ( hv, "HV %p not running in a hypervisor\n", hv ); + return -ENODEV; + } + + /* Check that hypervisor is Hyper-V */ + cpuid ( HV_CPUID_INTERFACE_ID, &interface_id, &discard_ebx, + &discard_ecx, &discard_edx ); + if ( interface_id != HV_INTERFACE_ID ) { + DBGC ( hv, "HV %p not running in Hyper-V (interface ID " + "%#08x)\n", hv, interface_id ); + return -ENODEV; + } + + /* Check that required features and privileges are available */ + cpuid ( HV_CPUID_FEATURES, &available, &permissions, &discard_ecx, + &discard_edx ); + if ( ! ( available & HV_FEATURES_AVAIL_HYPERCALL_MSR ) ) { + DBGC ( hv, "HV %p has no hypercall MSRs (features %08x:%08x)\n", + hv, available, permissions ); + return -ENODEV; + } + if ( ! ( available & HV_FEATURES_AVAIL_SYNIC_MSR ) ) { + DBGC ( hv, "HV %p has no SynIC MSRs (features %08x:%08x)\n", + hv, available, permissions ); + return -ENODEV; + } + if ( ! ( permissions & HV_FEATURES_PERM_POST_MESSAGES ) ) { + DBGC ( hv, "HV %p cannot post messages (features %08x:%08x)\n", + hv, available, permissions ); + return -EACCES; + } + if ( ! ( permissions & HV_FEATURES_PERM_SIGNAL_EVENTS ) ) { + DBGC ( hv, "HV %p cannot signal events (features %08x:%08x)", + hv, available, permissions ); + return -EACCES; + } + + return 0; +} + +/** + * Map hypercall page + * + * @v hv Hyper-V hypervisor + * @ret rc Return status code + */ +static int hv_map_hypercall ( struct hv_hypervisor *hv ) { + union { + struct { + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + } __attribute__ (( packed )); + char text[ 13 /* "bbbbccccdddd" + NUL */ ]; + } vendor_id; + uint32_t build; + uint32_t version; + uint32_t discard_eax; + uint32_t discard_ecx; + uint32_t discard_edx; + uint64_t guest_os_id; + uint64_t hypercall; + + /* Report guest OS identity */ + guest_os_id = rdmsr ( HV_X64_MSR_GUEST_OS_ID ); + if ( guest_os_id != 0 ) { + DBGC ( hv, "HV %p guest OS ID MSR already set to %#08llx\n", + hv, guest_os_id ); + return -EBUSY; + } + guest_os_id = HV_GUEST_OS_ID_IPXE; + DBGC2 ( hv, "HV %p guest OS ID MSR is %#08llx\n", hv, guest_os_id ); + wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id ); + + /* Get hypervisor system identity (for debugging) */ + cpuid ( HV_CPUID_VENDOR_ID, &discard_eax, &vendor_id.ebx, + &vendor_id.ecx, &vendor_id.edx ); + vendor_id.text[ sizeof ( vendor_id.text ) - 1 ] = '\0'; + cpuid ( HV_CPUID_HYPERVISOR_ID, &build, &version, &discard_ecx, + &discard_edx ); + DBGC ( hv, "HV %p detected \"%s\" version %d.%d build %d\n", hv, + vendor_id.text, ( version >> 16 ), ( version & 0xffff ), build ); + + /* Map hypercall page */ + hypercall = rdmsr ( HV_X64_MSR_HYPERCALL ); + hypercall &= ( PAGE_SIZE - 1 ); + hypercall |= ( virt_to_phys ( hv->hypercall ) | HV_HYPERCALL_ENABLE ); + DBGC2 ( hv, "HV %p hypercall MSR is %#08llx\n", hv, hypercall ); + wrmsr ( HV_X64_MSR_HYPERCALL, hypercall ); + + return 0; +} + +/** + * Unmap hypercall page + * + * @v hv Hyper-V hypervisor + */ +static void hv_unmap_hypercall ( struct hv_hypervisor *hv ) { + uint64_t hypercall; + uint64_t guest_os_id; + + /* Unmap the hypercall page */ + hypercall = rdmsr ( HV_X64_MSR_HYPERCALL ); + hypercall &= ( ( PAGE_SIZE - 1 ) & ~HV_HYPERCALL_ENABLE ); + DBGC2 ( hv, "HV %p hypercall MSR is %#08llx\n", hv, hypercall ); + wrmsr ( HV_X64_MSR_HYPERCALL, hypercall ); + + /* Reset the guest OS identity */ + guest_os_id = 0; + DBGC2 ( hv, "HV %p guest OS ID MSR is %#08llx\n", hv, guest_os_id ); + wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id ); +} + +/** + * Map synthetic interrupt controller + * + * @v hv Hyper-V hypervisor + * @ret rc Return status code + */ +static int hv_map_synic ( struct hv_hypervisor *hv ) { + uint64_t simp; + uint64_t siefp; + uint64_t scontrol; + + /* Map SynIC message page */ + simp = rdmsr ( HV_X64_MSR_SIMP ); + simp &= ( PAGE_SIZE - 1 ); + simp |= ( virt_to_phys ( hv->synic.message ) | HV_SIMP_ENABLE ); + DBGC2 ( hv, "HV %p SIMP MSR is %#08llx\n", hv, simp ); + wrmsr ( HV_X64_MSR_SIMP, simp ); + + /* Map SynIC event page */ + siefp = rdmsr ( HV_X64_MSR_SIEFP ); + siefp &= ( PAGE_SIZE - 1 ); + siefp |= ( virt_to_phys ( hv->synic.event ) | HV_SIEFP_ENABLE ); + DBGC2 ( hv, "HV %p SIEFP MSR is %#08llx\n", hv, siefp ); + wrmsr ( HV_X64_MSR_SIEFP, siefp ); + + /* Enable SynIC */ + scontrol = rdmsr ( HV_X64_MSR_SCONTROL ); + scontrol |= HV_SCONTROL_ENABLE; + DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol ); + wrmsr ( HV_X64_MSR_SCONTROL, scontrol ); + + return 0; +} + +/** + * Unmap synthetic interrupt controller + * + * @v hv Hyper-V hypervisor + */ +static void hv_unmap_synic ( struct hv_hypervisor *hv ) { + uint64_t scontrol; + uint64_t siefp; + uint64_t simp; + + /* Disable SynIC */ + scontrol = rdmsr ( HV_X64_MSR_SCONTROL ); + scontrol &= ~HV_SCONTROL_ENABLE; + DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol ); + wrmsr ( HV_X64_MSR_SCONTROL, scontrol ); + + /* Unmap SynIC event page */ + siefp = rdmsr ( HV_X64_MSR_SIEFP ); + siefp &= ( ( PAGE_SIZE - 1 ) & ~HV_SIEFP_ENABLE ); + DBGC2 ( hv, "HV %p SIEFP MSR is %#08llx\n", hv, siefp ); + wrmsr ( HV_X64_MSR_SIEFP, siefp ); + + /* Unmap SynIC message page */ + simp = rdmsr ( HV_X64_MSR_SIMP ); + simp &= ( ( PAGE_SIZE - 1 ) & ~HV_SIMP_ENABLE ); + DBGC2 ( hv, "HV %p SIMP MSR is %#08llx\n", hv, simp ); + wrmsr ( HV_X64_MSR_SIMP, simp ); +} + +/** + * Enable synthetic interrupt + * + * @v hv Hyper-V hypervisor + * @v sintx Synthetic interrupt number + */ +void hv_enable_sint ( struct hv_hypervisor *hv, unsigned int sintx ) { + unsigned long msr = HV_X64_MSR_SINT ( sintx ); + uint64_t sint; + + /* Enable synthetic interrupt + * + * We have to enable the interrupt, otherwise messages will + * not be delivered (even though the documentation implies + * that polling for messages is possible). We enable AutoEOI + * and hook the interrupt to the obsolete IRQ13 (FPU + * exception) vector, which will be implemented as a no-op. + */ + sint = rdmsr ( msr ); + sint &= ~( HV_SINT_MASKED | HV_SINT_VECTOR_MASK ); + sint |= ( HV_SINT_AUTO_EOI | + HV_SINT_VECTOR ( IRQ_INT ( 13 /* See comment above */ ) ) ); + DBGC2 ( hv, "HV %p SINT%d MSR is %#08llx\n", hv, sintx, sint ); + wrmsr ( msr, sint ); +} + +/** + * Disable synthetic interrupt + * + * @v hv Hyper-V hypervisor + * @v sintx Synthetic interrupt number + */ +void hv_disable_sint ( struct hv_hypervisor *hv, unsigned int sintx ) { + unsigned long msr = HV_X64_MSR_SINT ( sintx ); + uint64_t sint; + + /* Disable synthetic interrupt */ + sint = rdmsr ( msr ); + sint &= ~HV_SINT_AUTO_EOI; + sint |= HV_SINT_MASKED; + DBGC2 ( hv, "HV %p SINT%d MSR is %#08llx\n", hv, sintx, sint ); + wrmsr ( msr, sint ); +} + +/** + * Post message + * + * @v hv Hyper-V hypervisor + * @v id Connection ID + * @v type Message type + * @v data Message + * @v len Length of message + * @ret rc Return status code + */ +int hv_post_message ( struct hv_hypervisor *hv, unsigned int id, + unsigned int type, const void *data, size_t len ) { + struct hv_post_message *msg = &hv->message->posted; + int status; + int rc; + + /* Sanity check */ + assert ( len <= sizeof ( msg->data ) ); + + /* Construct message */ + memset ( msg, 0, sizeof ( *msg ) ); + msg->id = cpu_to_le32 ( id ); + msg->type = cpu_to_le32 ( type ); + msg->len = cpu_to_le32 ( len ); + memcpy ( msg->data, data, len ); + DBGC2 ( hv, "HV %p connection %d posting message type %#08x:\n", + hv, id, type ); + DBGC2_HDA ( hv, 0, msg->data, len ); + + /* Post message */ + if ( ( status = hv_call ( hv, HV_POST_MESSAGE, msg, NULL ) ) != 0 ) { + rc = -EHV ( status ); + DBGC ( hv, "HV %p could not post message to %#08x: %s\n", + hv, id, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Wait for received message + * + * @v hv Hyper-V hypervisor + * @v sintx Synthetic interrupt number + * @ret rc Return status code + */ +int hv_wait_for_message ( struct hv_hypervisor *hv, unsigned int sintx ) { + struct hv_message *msg = &hv->message->received; + struct hv_message *src = &hv->synic.message[sintx]; + unsigned int retries; + size_t len; + + /* Wait for message to arrive */ + for ( retries = 0 ; retries < HV_MESSAGE_MAX_WAIT_MS ; retries++ ) { + + /* Check for message */ + if ( src->type ) { + + /* Copy message */ + memset ( msg, 0, sizeof ( *msg ) ); + len = src->len; + assert ( len <= sizeof ( *msg ) ); + memcpy ( msg, src, + ( offsetof ( typeof ( *msg ), data ) + len ) ); + DBGC2 ( hv, "HV %p SINT%d received message type " + "%#08x:\n", hv, sintx, + le32_to_cpu ( msg->type ) ); + DBGC2_HDA ( hv, 0, msg->data, len ); + + /* Consume message */ + src->type = 0; + + return 0; + } + + /* Trigger message delivery */ + wrmsr ( HV_X64_MSR_EOM, 0 ); + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( hv, "HV %p SINT%d timed out waiting for message\n", + hv, sintx ); + return -ETIMEDOUT; +} + +/** + * Signal event + * + * @v hv Hyper-V hypervisor + * @v id Connection ID + * @v flag Flag number + * @ret rc Return status code + */ +int hv_signal_event ( struct hv_hypervisor *hv, unsigned int id, + unsigned int flag ) { + struct hv_signal_event *event = &hv->message->signalled; + int status; + int rc; + + /* Construct event */ + memset ( event, 0, sizeof ( *event ) ); + event->id = cpu_to_le32 ( id ); + event->flag = cpu_to_le16 ( flag ); + + /* Signal event */ + if ( ( status = hv_call ( hv, HV_SIGNAL_EVENT, event, NULL ) ) != 0 ) { + rc = -EHV ( status ); + DBGC ( hv, "HV %p could not signal event to %#08x: %s\n", + hv, id, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Probe root device + * + * @v rootdev Root device + * @ret rc Return status code + */ +static int hv_probe ( struct root_device *rootdev ) { + struct hv_hypervisor *hv; + int rc; + + /* Allocate and initialise structure */ + hv = zalloc ( sizeof ( *hv ) ); + if ( ! hv ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Check we are running in Hyper-V */ + if ( ( rc = hv_check_hv ( hv ) ) != 0 ) + goto err_check_hv; + + /* Allocate pages */ + if ( ( rc = hv_alloc_pages ( hv, &hv->hypercall, &hv->synic.message, + &hv->synic.event, NULL ) ) != 0 ) + goto err_alloc_pages; + + /* Allocate message buffer */ + if ( ( rc = hv_alloc_message ( hv ) ) != 0 ) + goto err_alloc_message; + + /* Map hypercall page */ + if ( ( rc = hv_map_hypercall ( hv ) ) != 0 ) + goto err_map_hypercall; + + /* Map synthetic interrupt controller */ + if ( ( rc = hv_map_synic ( hv ) ) != 0 ) + goto err_map_synic; + + /* Probe Hyper-V devices */ + if ( ( rc = vmbus_probe ( hv, &rootdev->dev ) ) != 0 ) + goto err_vmbus_probe; + + rootdev_set_drvdata ( rootdev, hv ); + return 0; + + vmbus_remove ( hv, &rootdev->dev ); + err_vmbus_probe: + hv_unmap_synic ( hv ); + err_map_synic: + hv_unmap_hypercall ( hv ); + err_map_hypercall: + hv_free_message ( hv ); + err_alloc_message: + hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event, + NULL ); + err_alloc_pages: + err_check_hv: + free ( hv ); + err_alloc: + return rc; +} + +/** + * Remove root device + * + * @v rootdev Root device + */ +static void hv_remove ( struct root_device *rootdev ) { + struct hv_hypervisor *hv = rootdev_get_drvdata ( rootdev ); + + vmbus_remove ( hv, &rootdev->dev ); + hv_unmap_synic ( hv ); + hv_unmap_hypercall ( hv ); + hv_free_message ( hv ); + hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event, + NULL ); + free ( hv ); +} + +/** Hyper-V root device driver */ +static struct root_driver hv_root_driver = { + .probe = hv_probe, + .remove = hv_remove, +}; + +/** Hyper-V root device */ +struct root_device hv_root_device __root_device = { + .dev = { .name = "Hyper-V" }, + .driver = &hv_root_driver, +}; + +/* Drag in objects via hv_root_device */ +REQUIRING_SYMBOL ( hv_root_device ); + +/* Drag in netvsc driver */ +REQUIRE_OBJECT ( netvsc ); diff --git a/qemu/roms/ipxe/src/arch/x86/drivers/hyperv/hyperv.h b/qemu/roms/ipxe/src/arch/x86/drivers/hyperv/hyperv.h new file mode 100644 index 000000000..0d09beb44 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/x86/drivers/hyperv/hyperv.h @@ -0,0 +1,57 @@ +#ifndef _HYPERV_H +#define _HYPERV_H + +/** @file + * + * Hyper-V driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** Get vendor identification */ +#define HV_CPUID_VENDOR_ID 0x40000000UL + +/** Get interface identification */ +#define HV_CPUID_INTERFACE_ID 0x40000001UL + +/** Get hypervisor identification */ +#define HV_CPUID_HYPERVISOR_ID 0x40000002UL + +/** Get hypervisor features */ +#define HV_CPUID_FEATURES 0x40000003UL + +/** SynIC MSRs are available */ +#define HV_FEATURES_AVAIL_SYNIC_MSR 0x00000004UL + +/** Hypercall MSRs are available */ +#define HV_FEATURES_AVAIL_HYPERCALL_MSR 0x00000020UL + +/** Guest may post messages */ +#define HV_FEATURES_PERM_POST_MESSAGES 0x00000010UL + +/** Guest may signal events */ +#define HV_FEATURES_PERM_SIGNAL_EVENTS 0x00000020UL + +/** Guest OS identity MSR */ +#define HV_X64_MSR_GUEST_OS_ID 0x40000000UL + +/** Hypercall page MSR */ +#define HV_X64_MSR_HYPERCALL 0x40000001UL + +/** SynIC control MSR */ +#define HV_X64_MSR_SCONTROL 0x40000080UL + +/** SynIC event flags page MSR */ +#define HV_X64_MSR_SIEFP 0x40000082UL + +/** SynIC message page MSR */ +#define HV_X64_MSR_SIMP 0x40000083UL + +/** SynIC end of message MSR */ +#define HV_X64_MSR_EOM 0x40000084UL + +/** SynIC interrupt source MSRs */ +#define HV_X64_MSR_SINT(x) ( 0x40000090UL + (x) ) + +#endif /* _HYPERV_H */ diff --git a/qemu/roms/ipxe/src/arch/x86/drivers/xen/hvm.c b/qemu/roms/ipxe/src/arch/x86/drivers/xen/hvm.c index 7406ca68d..7ac32d54c 100644 --- a/qemu/roms/ipxe/src/arch/x86/drivers/xen/hvm.c +++ b/qemu/roms/ipxe/src/arch/x86/drivers/xen/hvm.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -492,5 +496,8 @@ struct pci_driver hvm_driver __pci_driver = { .remove = hvm_remove, }; +/* Drag in objects via hvm_driver */ +REQUIRING_SYMBOL ( hvm_driver ); + /* Drag in netfront driver */ REQUIRE_OBJECT ( netfront ); diff --git a/qemu/roms/ipxe/src/arch/x86/drivers/xen/hvm.h b/qemu/roms/ipxe/src/arch/x86/drivers/xen/hvm.h index 325d20d66..72ed94f6d 100644 --- a/qemu/roms/ipxe/src/arch/x86/drivers/xen/hvm.h +++ b/qemu/roms/ipxe/src/arch/x86/drivers/xen/hvm.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/x86/hci/commands/cpuid_cmd.c b/qemu/roms/ipxe/src/arch/x86/hci/commands/cpuid_cmd.c index c4e35d179..d73ce2a3e 100644 --- a/qemu/roms/ipxe/src/arch/x86/hci/commands/cpuid_cmd.c +++ b/qemu/roms/ipxe/src/arch/x86/hci/commands/cpuid_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/x86/include/bits/bigint.h b/qemu/roms/ipxe/src/arch/x86/include/bits/bigint.h index d3449af5a..c9bb6ea45 100644 --- a/qemu/roms/ipxe/src/arch/x86/include/bits/bigint.h +++ b/qemu/roms/ipxe/src/arch/x86/include/bits/bigint.h @@ -6,7 +6,7 @@ * Big integer support */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/x86/include/bits/endian.h b/qemu/roms/ipxe/src/arch/x86/include/bits/endian.h new file mode 100644 index 000000000..85718cfdd --- /dev/null +++ b/qemu/roms/ipxe/src/arch/x86/include/bits/endian.h @@ -0,0 +1,8 @@ +#ifndef _BITS_ENDIAN_H +#define _BITS_ENDIAN_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#define __BYTE_ORDER __LITTLE_ENDIAN + +#endif /* _BITS_ENDIAN_H */ diff --git a/qemu/roms/ipxe/src/arch/x86/include/bits/errfile.h b/qemu/roms/ipxe/src/arch/x86/include/bits/errfile.h index 624575621..0d1617d20 100644 --- a/qemu/roms/ipxe/src/arch/x86/include/bits/errfile.h +++ b/qemu/roms/ipxe/src/arch/x86/include/bits/errfile.h @@ -1,7 +1,7 @@ #ifndef _BITS_ERRFILE_H #define _BITS_ERRFILE_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @addtogroup errfile Error file identifiers @@ -21,6 +21,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_guestinfo ( ERRFILE_ARCH | ERRFILE_CORE | 0x000a0000 ) #define ERRFILE_apm ( ERRFILE_ARCH | ERRFILE_CORE | 0x000b0000 ) #define ERRFILE_vesafb ( ERRFILE_ARCH | ERRFILE_CORE | 0x000c0000 ) +#define ERRFILE_int13con ( ERRFILE_ARCH | ERRFILE_CORE | 0x000d0000 ) #define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 ) @@ -46,9 +47,12 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_timer_rdtsc ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00000000 ) #define ERRFILE_timer_bios ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00010000 ) #define ERRFILE_hvm ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00020000 ) +#define ERRFILE_hyperv ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00030000 ) +#define ERRFILE_x86_uart ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00040000 ) #define ERRFILE_cpuid_cmd ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00000000 ) #define ERRFILE_cpuid_settings ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00010000 ) +#define ERRFILE_efi_entropy ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00020000 ) /** @} */ diff --git a/qemu/roms/ipxe/src/arch/x86/include/bits/io.h b/qemu/roms/ipxe/src/arch/x86/include/bits/io.h index cb1b67a6f..60c2e3edf 100644 --- a/qemu/roms/ipxe/src/arch/x86/include/bits/io.h +++ b/qemu/roms/ipxe/src/arch/x86/include/bits/io.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/x86/include/bits/pci_io.h b/qemu/roms/ipxe/src/arch/x86/include/bits/pci_io.h index 01b12326e..b41e562ee 100644 --- a/qemu/roms/ipxe/src/arch/x86/include/bits/pci_io.h +++ b/qemu/roms/ipxe/src/arch/x86/include/bits/pci_io.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/x86/include/bits/string.h b/qemu/roms/ipxe/src/arch/x86/include/bits/string.h index dce994983..c26fe30d5 100644 --- a/qemu/roms/ipxe/src/arch/x86/include/bits/string.h +++ b/qemu/roms/ipxe/src/arch/x86/include/bits/string.h @@ -18,9 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -28,8 +32,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -#define __HAVE_ARCH_MEMCPY - extern void * __memcpy ( void *dest, const void *src, size_t len ); extern void * __memcpy_reverse ( void *dest, const void *src, size_t len ); @@ -169,8 +171,6 @@ memcpy ( void *dest, const void *src, size_t len ) { } } -#define __HAVE_ARCH_MEMMOVE - extern void * __memmove ( void *dest, const void *src, size_t len ); /** @@ -196,8 +196,6 @@ memmove ( void *dest, const void *src, size_t len ) { } } -#define __HAVE_ARCH_MEMSET - /** * Fill memory region * @@ -206,7 +204,8 @@ memmove ( void *dest, const void *src, size_t len ) { * @v len Length * @ret dest Destination address */ -static inline void * memset ( void *dest, int fill, size_t len ) { +static inline __attribute__ (( always_inline )) void * +__memset ( void *dest, int fill, size_t len ) { void *discard_D; size_t discard_c; @@ -217,16 +216,129 @@ static inline void * memset ( void *dest, int fill, size_t len ) { return dest; } -#define __HAVE_ARCH_MEMSWAP +/** + * Fill memory region with zero (where length is a compile-time constant) + * + * @v dest Destination address + * @v len Length + * @ret dest Destination address + */ +static inline __attribute__ (( always_inline )) void * +__constant_memset_zero ( void *dest, size_t len ) { + union { + uint32_t u32[2]; + uint16_t u16[4]; + uint8_t u8[8]; + } __attribute__ (( __may_alias__ )) *dest_u = dest; + void *edi; + uint32_t eax; + + switch ( len ) { + case 0 : /* 0 bytes */ + return dest; + + /* Single-register moves. Almost certainly better than a + * string operation. We can avoid clobbering any registers, + * we can reuse a zero that happens to already be in a + * register, and we can optimise away the code entirely if the + * memset() is used to clear a region which then gets + * immediately overwritten. + */ + case 1 : /* 3 bytes */ + dest_u->u8[0] = 0; + return dest; + case 2: /* 5 bytes */ + dest_u->u16[0] = 0; + return dest; + case 4: /* 6 bytes */ + dest_u->u32[0] = 0; + return dest; + + /* Double-register moves. Very probably better than a string + * operation. + */ + case 3 : /* 9 bytes */ + dest_u->u16[0] = 0; + dest_u->u8[2] = 0; + return dest; + case 5 : /* 10 bytes */ + dest_u->u32[0] = 0; + dest_u->u8[4] = 0; + return dest; + case 6 : /* 12 bytes */ + dest_u->u32[0] = 0; + dest_u->u16[2] = 0; + return dest; + case 8 : /* 13 bytes */ + dest_u->u32[0] = 0; + dest_u->u32[1] = 0; + return dest; + } + + /* As with memcpy(), we can potentially save space by using + * multiple single-byte "stos" instructions instead of loading + * up ecx and using "rep stosb". + * + * "load ecx, rep movsb" is 7 bytes, plus an average of 1 byte + * to allow for saving/restoring ecx 50% of the time. + * + * "stosl" and "stosb" are 1 byte each, "stosw" is two bytes. + * + * The calculations are therefore the same as for memcpy(), + * giving a cutoff point of around 26 bytes. + */ -extern void * memswap ( void *dest, void *src, size_t len ); + edi = dest; + eax = 0; + + if ( len >= 26 ) + return __memset ( dest, 0, len ); -#define __HAVE_ARCH_STRNCMP + if ( len >= 6*4 ) + __asm__ __volatile__ ( "stosl" : "=&D" ( edi ), "=&a" ( eax ) + : "0" ( edi ), "1" ( eax ) : "memory" ); + if ( len >= 5*4 ) + __asm__ __volatile__ ( "stosl" : "=&D" ( edi ), "=&a" ( eax ) + : "0" ( edi ), "1" ( eax ) : "memory" ); + if ( len >= 4*4 ) + __asm__ __volatile__ ( "stosl" : "=&D" ( edi ), "=&a" ( eax ) + : "0" ( edi ), "1" ( eax ) : "memory" ); + if ( len >= 3*4 ) + __asm__ __volatile__ ( "stosl" : "=&D" ( edi ), "=&a" ( eax ) + : "0" ( edi ), "1" ( eax ) : "memory" ); + if ( len >= 2*4 ) + __asm__ __volatile__ ( "stosl" : "=&D" ( edi ), "=&a" ( eax ) + : "0" ( edi ), "1" ( eax ) : "memory" ); + if ( len >= 1*4 ) + __asm__ __volatile__ ( "stosl" : "=&D" ( edi ), "=&a" ( eax ) + : "0" ( edi ), "1" ( eax ) : "memory" ); + if ( ( len % 4 ) >= 2 ) + __asm__ __volatile__ ( "stosw" : "=&D" ( edi ), "=&a" ( eax ) + : "0" ( edi ), "1" ( eax ) : "memory" ); + if ( ( len % 2 ) >= 1 ) + __asm__ __volatile__ ( "stosb" : "=&D" ( edi ), "=&a" ( eax ) + : "0" ( edi ), "1" ( eax ) : "memory" ); -extern int strncmp ( const char *str1, const char *str2, size_t len ); + return dest; +} -#define __HAVE_ARCH_STRLEN +/** + * Fill memory region + * + * @v dest Destination address + * @v fill Fill pattern + * @v len Length + * @ret dest Destination address + */ +static inline __attribute__ (( always_inline )) void * +memset ( void *dest, int fill, size_t len ) { -extern size_t strlen ( const char *string ); + if ( __builtin_constant_p ( fill ) && ( fill == 0 ) && + __builtin_constant_p ( len ) ) { + return __constant_memset_zero ( dest, len ); + } else { + return __memset ( dest, fill, len ); + } +} #endif /* X86_BITS_STRING_H */ diff --git a/qemu/roms/ipxe/src/arch/x86/include/bits/tcpip.h b/qemu/roms/ipxe/src/arch/x86/include/bits/tcpip.h index a4b335eb1..5c2baffcf 100644 --- a/qemu/roms/ipxe/src/arch/x86/include/bits/tcpip.h +++ b/qemu/roms/ipxe/src/arch/x86/include/bits/tcpip.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern uint16_t x86_tcpip_continue_chksum ( uint16_t partial, const void *data, size_t len ); diff --git a/qemu/roms/ipxe/src/arch/x86/include/bits/uart.h b/qemu/roms/ipxe/src/arch/x86/include/bits/uart.h new file mode 100644 index 000000000..e09cd3f4c --- /dev/null +++ b/qemu/roms/ipxe/src/arch/x86/include/bits/uart.h @@ -0,0 +1,41 @@ +#ifndef _BITS_UART_H +#define _BITS_UART_H + +/** @file + * + * 16550-compatible UART + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** + * Write to UART register + * + * @v uart UART + * @v addr Register address + * @v data Data + */ +static inline __attribute__ (( always_inline )) void +uart_write ( struct uart *uart, unsigned int addr, uint8_t data ) { + outb ( data, ( uart->base + addr ) ); +} + +/** + * Read from UART register + * + * @v uart UART + * @v addr Register address + * @ret data Data + */ +static inline __attribute__ (( always_inline )) uint8_t +uart_read ( struct uart *uart, unsigned int addr ) { + return inb ( uart->base + addr ); +} + +extern int uart_select ( struct uart *uart, unsigned int port ); + +#endif /* _BITS_UART_H */ diff --git a/qemu/roms/ipxe/src/arch/x86/include/bits/xen.h b/qemu/roms/ipxe/src/arch/x86/include/bits/xen.h index dbccf1b77..fc065ea38 100644 --- a/qemu/roms/ipxe/src/arch/x86/include/bits/xen.h +++ b/qemu/roms/ipxe/src/arch/x86/include/bits/xen.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Hypercall registers */ #ifdef __x86_64__ @@ -161,4 +161,23 @@ xen_hypercall_5 ( struct xen_hypervisor *xen, unsigned int hypercall, return retval; } +/** + * Test and clear pending event + * + * @v xen Xen hypervisor + * @v port Event channel port + * @ret pending Event was pending + */ +static inline __attribute__ (( always_inline )) uint8_t +xenevent_pending ( struct xen_hypervisor *xen, evtchn_port_t port ) { + uint8_t pending; + + __asm__ __volatile__ ( "lock btr %2, %0\n\t" + "setc %1\n\t" + : "+m" ( xen->shared->evtchn_pending ), + "=a" ( pending ) + : "Ir" ( port ) ); + return pending; +} + #endif /* _BITS_XEN_H */ diff --git a/qemu/roms/ipxe/src/arch/x86/include/ipxe/cpuid.h b/qemu/roms/ipxe/src/arch/x86/include/ipxe/cpuid.h index 2f78dfca1..da85d0b88 100644 --- a/qemu/roms/ipxe/src/arch/x86/include/ipxe/cpuid.h +++ b/qemu/roms/ipxe/src/arch/x86/include/ipxe/cpuid.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include @@ -39,6 +39,9 @@ struct x86_features { /** Get standard features */ #define CPUID_FEATURES 0x00000001UL +/** Hypervisor is present */ +#define CPUID_FEATURES_INTEL_ECX_HYPERVISOR 0x80000000UL + /** Get largest extended function */ #define CPUID_AMD_MAX_FN 0x80000000UL diff --git a/qemu/roms/ipxe/src/arch/x86/include/ipxe/efi/efix86_nap.h b/qemu/roms/ipxe/src/arch/x86/include/ipxe/efi/efix86_nap.h index e85a272b3..1a391c9b6 100644 --- a/qemu/roms/ipxe/src/arch/x86/include/ipxe/efi/efix86_nap.h +++ b/qemu/roms/ipxe/src/arch/x86/include/ipxe/efi/efix86_nap.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef NAP_EFIX86 #define NAP_PREFIX_efix86 diff --git a/qemu/roms/ipxe/src/arch/x86/include/ipxe/pcibios.h b/qemu/roms/ipxe/src/arch/x86/include/ipxe/pcibios.h index 36af7fcde..7e1bcd814 100644 --- a/qemu/roms/ipxe/src/arch/x86/include/ipxe/pcibios.h +++ b/qemu/roms/ipxe/src/arch/x86/include/ipxe/pcibios.h @@ -9,7 +9,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef PCIAPI_PCBIOS #define PCIAPI_PREFIX_pcbios diff --git a/qemu/roms/ipxe/src/arch/x86/include/ipxe/pcidirect.h b/qemu/roms/ipxe/src/arch/x86/include/ipxe/pcidirect.h index 7fa7c4fa7..d924f2f20 100644 --- a/qemu/roms/ipxe/src/arch/x86/include/ipxe/pcidirect.h +++ b/qemu/roms/ipxe/src/arch/x86/include/ipxe/pcidirect.h @@ -1,7 +1,7 @@ #ifndef _PCIDIRECT_H #define _PCIDIRECT_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/x86/include/ipxe/pit8254.h b/qemu/roms/ipxe/src/arch/x86/include/ipxe/pit8254.h new file mode 100644 index 000000000..00b0ab164 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/x86/include/ipxe/pit8254.h @@ -0,0 +1,81 @@ +#ifndef _IPXE_PIT8254_H +#define _IPXE_PIT8254_H + +/** @file + * + * 8254 Programmable Interval Timer + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** IRQ0 channel */ +#define PIT8254_CH_IRQ0 0 + +/** PC speaker channel */ +#define PIT8254_CH_SPKR 2 + +/** Timer frequency (1.193182MHz) */ +#define PIT8254_HZ 1193182UL + +/** Data port */ +#define PIT8254_DATA(channel) ( 0x40 + (channel) ) + +/** Mode/command register */ +#define PIT8254_CMD 0x43 + +/** Select channel */ +#define PIT8254_CMD_CHANNEL(channel) ( (channel) << 6 ) + +/** Access modes */ +#define PIT8254_CMD_ACCESS_LATCH 0x00 /**< Latch count value command */ +#define PIT8254_CMD_ACCESS_LO 0x10 /**< Low byte only */ +#define PIT8254_CMD_ACCESS_HI 0x20 /**< High byte only */ +#define PIT8254_CMD_ACCESS_LOHI 0x30 /**< Low-byte, high-byte pair */ + +/* Operating modes */ +#define PIT8254_CMD_OP_TERMINAL 0x00 /**< Interrupt on terminal count */ +#define PIT8254_CMD_OP_ONESHOT 0x02 /**< Hardware re-triggerable one-shot */ +#define PIT8254_CMD_OP_RATE 0x04 /**< Rate generator */ +#define PIT8254_CMD_OP_SQUARE 0x06 /**< Square wave generator */ +#define PIT8254_CMD_OP_SWSTROBE 0x08 /**< Software triggered strobe */ +#define PIT8254_CMD_OP_HWSTROBE 0x0a /**< Hardware triggered strobe */ +#define PIT8254_CMD_OP_RATE2 0x0c /**< Rate generator (duplicate) */ +#define PIT8254_CMD_OP_SQUARE2 0x0e /**< Square wave generator (duplicate)*/ + +/** Binary mode */ +#define PIT8254_CMD_BINARY 0x00 + +/** BCD mode */ +#define PIT8254_CMD_BCD 0x01 + +/** PC speaker control register */ +#define PIT8254_SPKR 0x61 + +/** PC speaker channel gate */ +#define PIT8254_SPKR_GATE 0x01 + +/** PC speaker enabled */ +#define PIT8254_SPKR_ENABLE 0x02 + +/** PC speaker channel output */ +#define PIT8254_SPKR_OUT 0x20 + +extern void pit8254_speaker_delay ( unsigned int ticks ); + +/** + * Delay for a fixed number of microseconds + * + * @v usecs Number of microseconds for which to delay + */ +static inline __attribute__ (( always_inline )) void +pit8254_udelay ( unsigned long usecs ) { + + /* Delays are invariably compile-time constants; force the + * multiplication and division to take place at compilation + * time rather than runtime. + */ + pit8254_speaker_delay ( ( usecs * PIT8254_HZ ) / 1000000 ); +} + +#endif /* _IPXE_PIT8254_H */ diff --git a/qemu/roms/ipxe/src/arch/x86/include/ipxe/x86_io.h b/qemu/roms/ipxe/src/arch/x86/include/ipxe/x86_io.h index 9e68f4e78..5214e9fbb 100644 --- a/qemu/roms/ipxe/src/arch/x86/include/ipxe/x86_io.h +++ b/qemu/roms/ipxe/src/arch/x86/include/ipxe/x86_io.h @@ -15,7 +15,7 @@ * physically fit into a machine with such an old CPU anyway. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef IOAPI_X86 #define IOAPI_PREFIX_x86 diff --git a/qemu/roms/ipxe/src/arch/x86/include/linux/ipxe/dhcp_arch.h b/qemu/roms/ipxe/src/arch/x86/include/linux/ipxe/dhcp_arch.h index e83fd9d87..d60905f22 100644 --- a/qemu/roms/ipxe/src/arch/x86/include/linux/ipxe/dhcp_arch.h +++ b/qemu/roms/ipxe/src/arch/x86/include/linux/ipxe/dhcp_arch.h @@ -4,7 +4,7 @@ * 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 any later version. + * 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 @@ -13,7 +13,12 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #ifndef _LINUX_DHCP_ARCH_H @@ -24,7 +29,7 @@ * Architecture-specific DHCP options */ -FILE_LICENCE(GPL2_OR_LATER); +FILE_LICENCE(GPL2_OR_LATER_OR_UBDL); #include diff --git a/qemu/roms/ipxe/src/arch/x86/include/pic8259.h b/qemu/roms/ipxe/src/arch/x86/include/pic8259.h new file mode 100644 index 000000000..f02e62909 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/x86/include/pic8259.h @@ -0,0 +1,70 @@ +/* + * Basic support for controlling the 8259 Programmable Interrupt Controllers. + * + * Initially written by Michael Brown (mcb30). + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifndef PIC8259_H +#define PIC8259_H + +#include + +#define IRQ_PIC_CUTOFF 8 + +/* 8259 register locations */ +#define PIC1_ICW1 0x20 +#define PIC1_OCW2 0x20 +#define PIC1_OCW3 0x20 +#define PIC1_ICR 0x20 +#define PIC1_IRR 0x20 +#define PIC1_ISR 0x20 +#define PIC1_ICW2 0x21 +#define PIC1_ICW3 0x21 +#define PIC1_ICW4 0x21 +#define PIC1_IMR 0x21 +#define PIC2_ICW1 0xa0 +#define PIC2_OCW2 0xa0 +#define PIC2_OCW3 0xa0 +#define PIC2_ICR 0xa0 +#define PIC2_IRR 0xa0 +#define PIC2_ISR 0xa0 +#define PIC2_ICW2 0xa1 +#define PIC2_ICW3 0xa1 +#define PIC2_ICW4 0xa1 +#define PIC2_IMR 0xa1 + +/* Register command values */ +#define OCW3_ID 0x08 +#define OCW3_READ_IRR 0x03 +#define OCW3_READ_ISR 0x02 +#define ICR_EOI_NON_SPECIFIC 0x20 +#define ICR_EOI_NOP 0x40 +#define ICR_EOI_SPECIFIC 0x60 +#define ICR_EOI_SET_PRIORITY 0xc0 + +/* Macros to enable/disable IRQs */ +#define IMR_REG(x) ( (x) < IRQ_PIC_CUTOFF ? PIC1_IMR : PIC2_IMR ) +#define IMR_BIT(x) ( 1 << ( (x) % IRQ_PIC_CUTOFF ) ) +#define irq_enabled(x) ( ( inb ( IMR_REG(x) ) & IMR_BIT(x) ) == 0 ) +#define enable_irq(x) outb ( inb( IMR_REG(x) ) & ~IMR_BIT(x), IMR_REG(x) ) +#define disable_irq(x) outb ( inb( IMR_REG(x) ) | IMR_BIT(x), IMR_REG(x) ) + +/* Macros for acknowledging IRQs */ +#define ICR_REG( irq ) ( (irq) < IRQ_PIC_CUTOFF ? PIC1_ICR : PIC2_ICR ) +#define ICR_VALUE( irq ) ( (irq) % IRQ_PIC_CUTOFF ) +#define CHAINED_IRQ 2 + +/* Utility macros to convert IRQ numbers to INT numbers and INT vectors */ +#define IRQ_INT( irq ) ( ( ( (irq) - IRQ_PIC_CUTOFF ) ^ 0x70 ) & 0x7f ) + +/* Other constants */ +#define IRQ_MAX 15 +#define IRQ_NONE -1U + +/* Function prototypes + */ +void send_eoi ( unsigned int irq ); + +#endif /* PIC8259_H */ diff --git a/qemu/roms/ipxe/src/arch/x86/interface/efi/efi_entropy.c b/qemu/roms/ipxe/src/arch/x86/interface/efi/efi_entropy.c new file mode 100644 index 000000000..a54bd12e6 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/x86/interface/efi/efi_entropy.c @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include + +/** @file + * + * EFI entropy source + * + */ + +/** Random number generator protocol */ +static EFI_RNG_PROTOCOL *efirng; +EFI_REQUEST_PROTOCOL ( EFI_RNG_PROTOCOL, &efirng ); + +/** Minimum number of bytes to request from RNG + * + * The UEFI spec states (for no apparently good reason) that "When a + * Deterministic Random Bit Generator (DRBG) is used on the output of + * a (raw) entropy source, its security level must be at least 256 + * bits." The EDK2 codebase (mis)interprets this to mean that the + * call to GetRNG() should fail if given a buffer less than 32 bytes. + * + * Incidentally, nothing in the EFI RNG protocol provides any way to + * report the actual amount of entropy returned by GetRNG(). + */ +#define EFI_ENTROPY_RNG_LEN 32 + +/** Time (in 100ns units) to delay waiting for timer tick + * + * In theory, UEFI allows us to specify a trigger time of zero to + * simply wait for the next timer tick. In practice, specifying zero + * seems to often return immediately, which produces almost no + * entropy. Specify a delay of 1000ns to try to force an existent + * delay. + */ +#define EFI_ENTROPY_TRIGGER_TIME 10 + +/** Event used to wait for timer tick */ +static EFI_EVENT tick; + +/** + * Enable entropy gathering + * + * @ret rc Return status code + */ +static int efi_entropy_enable ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + int rc; + + DBGC ( &tick, "ENTROPY %s RNG protocol\n", + ( efirng ? "has" : "has no" ) ); + + /* Create timer tick event */ + if ( ( efirc = bs->CreateEvent ( EVT_TIMER, TPL_NOTIFY, NULL, NULL, + &tick ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &tick, "ENTROPY could not create event: %s\n", + strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Disable entropy gathering + * + */ +static void efi_entropy_disable ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + + /* Close timer tick event */ + bs->CloseEvent ( tick ); +} + +/** + * Wait for a timer tick + * + * @ret low TSC low-order bits, or negative error + */ +static int efi_entropy_tick ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + UINTN index; + uint16_t low; + uint32_t discard_d; + EFI_STATUS efirc; + int rc; + + /* Wait for next timer tick */ + if ( ( efirc = bs->SetTimer ( tick, TimerRelative, + EFI_ENTROPY_TRIGGER_TIME ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &tick, "ENTROPY could not set timer: %s\n", + strerror ( rc ) ); + return rc; + } + if ( ( efirc = bs->WaitForEvent ( 1, &tick, &index ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &tick, "ENTROPY could not wait for timer tick: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Get current TSC low-order bits */ + __asm__ __volatile__ ( "rdtsc" : "=a" ( low ), "=d" ( discard_d ) ); + + return low; +} + +/** + * Get noise sample from timer ticks + * + * @ret noise Noise sample + * @ret rc Return status code + */ +static int efi_get_noise_ticks ( noise_sample_t *noise ) { + int before; + int after; + int rc; + + /* Wait for a timer tick */ + before = efi_entropy_tick(); + if ( before < 0 ) { + rc = before; + return rc; + } + + /* Wait for another timer tick */ + after = efi_entropy_tick(); + if ( after < 0 ) { + rc = after; + return rc; + } + + /* Use TSC delta as noise sample */ + *noise = ( after - before ); + + return 0; +} + +/** + * Get noise sample from RNG protocol + * + * @ret noise Noise sample + * @ret rc Return status code + */ +static int efi_get_noise_rng ( noise_sample_t *noise ) { + uint8_t buf[EFI_ENTROPY_RNG_LEN]; + EFI_STATUS efirc; + int rc; + + /* Fail if we have no EFI RNG protocol */ + if ( ! efirng ) + return -ENOTSUP; + + /* Get the minimum allowed number of random bytes */ + if ( ( efirc = efirng->GetRNG ( efirng, NULL, EFI_ENTROPY_RNG_LEN, + buf ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &tick, "ENTROPY could not read from RNG: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Reduce random bytes to a single noise sample. This seems + * like overkill, but we have no way of knowing how much + * entropy is actually present in the bytes returned by the + * RNG protocol. + */ + *noise = crc32_le ( 0, buf, sizeof ( buf ) ); + + return 0; +} + +/** + * Get noise sample + * + * @ret noise Noise sample + * @ret rc Return status code + */ +static int efi_get_noise ( noise_sample_t *noise ) { + int rc; + + /* Try RNG first, falling back to timer ticks */ + if ( ( ( rc = efi_get_noise_rng ( noise ) ) != 0 ) && + ( ( rc = efi_get_noise_ticks ( noise ) ) != 0 ) ) + return rc; + + return 0; +} + +PROVIDE_ENTROPY_INLINE ( efi, min_entropy_per_sample ); +PROVIDE_ENTROPY ( efi, entropy_enable, efi_entropy_enable ); +PROVIDE_ENTROPY ( efi, entropy_disable, efi_entropy_disable ); +PROVIDE_ENTROPY ( efi, get_noise, efi_get_noise ); diff --git a/qemu/roms/ipxe/src/arch/x86/interface/efi/efix86_nap.c b/qemu/roms/ipxe/src/arch/x86/interface/efi/efix86_nap.c index b05421fab..3ebf0bd68 100644 --- a/qemu/roms/ipxe/src/arch/x86/interface/efi/efix86_nap.c +++ b/qemu/roms/ipxe/src/arch/x86/interface/efi/efix86_nap.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/arch/x86/prefix/efidrvprefix.c b/qemu/roms/ipxe/src/arch/x86/prefix/efidrvprefix.c index 3daefd00a..4fbb19ff7 100644 --- a/qemu/roms/ipxe/src/arch/x86/prefix/efidrvprefix.c +++ b/qemu/roms/ipxe/src/arch/x86/prefix/efidrvprefix.c @@ -21,7 +21,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include +#include #include +#include /** * EFI entry point @@ -44,3 +46,36 @@ EFI_STATUS EFIAPI _efidrv_start ( EFI_HANDLE image_handle, return 0; } + +/** + * Probe EFI root bus + * + * @v rootdev EFI root device + */ +static int efi_probe ( struct root_device *rootdev __unused ) { + + /* Do nothing */ + return 0; +} + +/** + * Remove EFI root bus + * + * @v rootdev EFI root device + */ +static void efi_remove ( struct root_device *rootdev __unused ) { + + efi_driver_disconnect_all(); +} + +/** EFI root device driver */ +static struct root_driver efi_root_driver = { + .probe = efi_probe, + .remove = efi_remove, +}; + +/** EFI root device */ +struct root_device efi_root_device __root_device = { + .dev = { .name = "EFI" }, + .driver = &efi_root_driver, +}; diff --git a/qemu/roms/ipxe/src/arch/x86/prefix/efiprefix.c b/qemu/roms/ipxe/src/arch/x86/prefix/efiprefix.c index b0bf99c65..18b931e68 100644 --- a/qemu/roms/ipxe/src/arch/x86/prefix/efiprefix.c +++ b/qemu/roms/ipxe/src/arch/x86/prefix/efiprefix.c @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include /** * EFI entry point @@ -49,6 +50,9 @@ EFI_STATUS EFIAPI _efi_start ( EFI_HANDLE image_handle, /* Claim SNP devices for use by iPXE */ efi_snp_claim(); + /* Start watchdog holdoff timer */ + efi_watchdog_start(); + /* Call to main() */ if ( ( rc = main() ) != 0 ) { efirc = EFIRC ( rc ); @@ -56,6 +60,7 @@ EFI_STATUS EFIAPI _efi_start ( EFI_HANDLE image_handle, } err_main: + efi_watchdog_stop(); efi_snp_release(); efi_loaded_image->Unload ( image_handle ); efi_driver_reconnect_all(); diff --git a/qemu/roms/ipxe/src/arch/x86_64/Makefile b/qemu/roms/ipxe/src/arch/x86_64/Makefile index b687f3407..48c0aa1af 100644 --- a/qemu/roms/ipxe/src/arch/x86_64/Makefile +++ b/qemu/roms/ipxe/src/arch/x86_64/Makefile @@ -40,6 +40,7 @@ endif # x86_64-specific directories containing source files # +SRCDIRS += arch/x86_64/core SRCDIRS += arch/x86_64/prefix # Include common x86 Makefile diff --git a/qemu/roms/ipxe/src/arch/x86_64/core/setjmp.S b/qemu/roms/ipxe/src/arch/x86_64/core/setjmp.S new file mode 100644 index 000000000..e43200d7b --- /dev/null +++ b/qemu/roms/ipxe/src/arch/x86_64/core/setjmp.S @@ -0,0 +1,65 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text + .code64 + + /* Must match jmp_buf structure layout */ + .struct 0 +env_retaddr: .quad 0 +env_stack: .quad 0 +env_rbx: .quad 0 +env_rbp: .quad 0 +env_r12: .quad 0 +env_r13: .quad 0 +env_r14: .quad 0 +env_r15: .quad 0 + .previous + +/* + * Save stack context for non-local goto + */ + .globl setjmp +setjmp: + /* Save return address */ + movq 0(%rsp), %rax + movq %rax, env_retaddr(%rdi) + /* Save stack pointer */ + movq %rsp, env_stack(%rdi) + /* Save other registers */ + movq %rbx, env_rbx(%rdi) + movq %rbp, env_rbp(%rdi) + movq %r12, env_r12(%rdi) + movq %r13, env_r13(%rdi) + movq %r14, env_r14(%rdi) + movq %r15, env_r15(%rdi) + /* Return 0 when returning as setjmp() */ + xorq %rax, %rax + ret + .size setjmp, . - setjmp + +/* + * Non-local jump to a saved stack context + */ + .globl longjmp +longjmp: + /* Get result in %rax */ + movq %rsi, %rax + /* Force result to non-zero */ + testq %rax, %rax + jnz 1f + incq %rax +1: /* Restore stack pointer */ + movq env_stack(%rdi), %rsp + /* Restore other registers */ + movq env_rbx(%rdi), %rbx + movq env_rbp(%rdi), %rbp + movq env_r12(%rdi), %r12 + movq env_r13(%rdi), %r13 + movq env_r14(%rdi), %r14 + movq env_r15(%rdi), %r15 + /* Replace return address on the new stack */ + popq %rcx /* discard */ + pushq env_retaddr(%rdi) + /* Return to setjmp() caller */ + ret + .size longjmp, . - longjmp diff --git a/qemu/roms/ipxe/src/arch/x86_64/include/bits/byteswap.h b/qemu/roms/ipxe/src/arch/x86_64/include/bits/byteswap.h index 2e472d98a..d8c5098ef 100644 --- a/qemu/roms/ipxe/src/arch/x86_64/include/bits/byteswap.h +++ b/qemu/roms/ipxe/src/arch/x86_64/include/bits/byteswap.h @@ -9,7 +9,7 @@ #include -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); static inline __attribute__ (( always_inline, const )) uint16_t __bswap_variable_16 ( uint16_t x ) { diff --git a/qemu/roms/ipxe/src/arch/x86_64/include/bits/compiler.h b/qemu/roms/ipxe/src/arch/x86_64/include/bits/compiler.h index 51a7eaae2..f70b2e517 100644 --- a/qemu/roms/ipxe/src/arch/x86_64/include/bits/compiler.h +++ b/qemu/roms/ipxe/src/arch/x86_64/include/bits/compiler.h @@ -1,6 +1,9 @@ #ifndef _BITS_COMPILER_H #define _BITS_COMPILER_H +/** Dummy relocation type */ +#define RELOC_TYPE_NONE R_X86_64_NONE + #ifndef ASSEMBLY /** Declare a function with standard calling conventions */ diff --git a/qemu/roms/ipxe/src/arch/x86_64/include/bits/endian.h b/qemu/roms/ipxe/src/arch/x86_64/include/bits/endian.h deleted file mode 100644 index 413e702db..000000000 --- a/qemu/roms/ipxe/src/arch/x86_64/include/bits/endian.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef ETHERBOOT_BITS_ENDIAN_H -#define ETHERBOOT_BITS_ENDIAN_H - -#define __BYTE_ORDER __LITTLE_ENDIAN - -#endif /* ETHERBOOT_BITS_ENDIAN_H */ diff --git a/qemu/roms/ipxe/src/arch/x86_64/include/bits/entropy.h b/qemu/roms/ipxe/src/arch/x86_64/include/bits/entropy.h index 9c64c833b..a9b3bc10e 100644 --- a/qemu/roms/ipxe/src/arch/x86_64/include/bits/entropy.h +++ b/qemu/roms/ipxe/src/arch/x86_64/include/bits/entropy.h @@ -7,6 +7,6 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #endif /* _BITS_ENTROPY_H */ diff --git a/qemu/roms/ipxe/src/arch/x86_64/include/bits/hyperv.h b/qemu/roms/ipxe/src/arch/x86_64/include/bits/hyperv.h new file mode 100644 index 000000000..845c182f7 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/x86_64/include/bits/hyperv.h @@ -0,0 +1,75 @@ +#ifndef _BITS_HYPERV_H +#define _BITS_HYPERV_H + +/** @file + * + * Hyper-V interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** + * Issue hypercall + * + * @v hv Hyper-V hypervisor + * @v code Call code + * @v in Input parameters + * @v out Output parameters + * @ret status Status code + */ +static inline __attribute__ (( always_inline )) int +hv_call ( struct hv_hypervisor *hv, unsigned int code, const void *in, + void *out ) { + void *hypercall = hv->hypercall; + register uint64_t rcx asm ( "rcx" ); + register uint64_t rdx asm ( "rdx" ); + register uint64_t r8 asm ( "r8" ); + uint64_t in_phys; + uint64_t out_phys; + uint16_t result; + + in_phys = ( ( __builtin_constant_p ( in ) && ( in == NULL ) ) + ? 0 : virt_to_phys ( in ) ); + out_phys = ( ( __builtin_constant_p ( out ) && ( out == NULL ) ) + ? 0 : virt_to_phys ( out ) ); + rcx = code; + rdx = in_phys; + r8 = out_phys; + __asm__ __volatile__ ( "call *%4" + : "=a" ( result ), "+r" ( rcx ), "+r" ( rdx ), + "+r" ( r8 ) + : "m" ( hypercall ) + : "r9", "r10", "r11", "xmm0", "xmm1", "xmm2", + "xmm3", "xmm4", "xmm5" ); + return result; +} + +/** + * Set bit atomically + * + * @v bits Bit field + * @v bit Bit to set + */ +static inline __attribute__ (( always_inline )) void +hv_set_bit ( void *bits, unsigned int bit ) { + struct { + uint64_t qword[ ( bit / 64 ) + 1 ]; + } *qwords = bits; + + /* Set bit using "lock bts". Inform compiler that any memory + * from the start of the bit field up to and including the + * qword containing this bit may be modified. (This is + * overkill but shouldn't matter in practice since we're + * unlikely to subsequently read other bits from the same bit + * field.) + */ + __asm__ __volatile__ ( "lock bts %1, %0" + : "+m" ( *qwords ) : "Ir" ( bit ) ); +} + +#endif /* _BITS_HYPERV_H */ diff --git a/qemu/roms/ipxe/src/arch/x86_64/include/bits/profile.h b/qemu/roms/ipxe/src/arch/x86_64/include/bits/profile.h index 6fc16d84b..b7c74fbe7 100644 --- a/qemu/roms/ipxe/src/arch/x86_64/include/bits/profile.h +++ b/qemu/roms/ipxe/src/arch/x86_64/include/bits/profile.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/x86_64/include/bits/reboot.h b/qemu/roms/ipxe/src/arch/x86_64/include/bits/reboot.h index f1bce0540..f9bcd6a7b 100644 --- a/qemu/roms/ipxe/src/arch/x86_64/include/bits/reboot.h +++ b/qemu/roms/ipxe/src/arch/x86_64/include/bits/reboot.h @@ -7,6 +7,6 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #endif /* _BITS_REBOOT_H */ diff --git a/qemu/roms/ipxe/src/arch/x86_64/include/bits/sanboot.h b/qemu/roms/ipxe/src/arch/x86_64/include/bits/sanboot.h index d33d03cbe..dcab830f6 100644 --- a/qemu/roms/ipxe/src/arch/x86_64/include/bits/sanboot.h +++ b/qemu/roms/ipxe/src/arch/x86_64/include/bits/sanboot.h @@ -7,6 +7,6 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #endif /* _BITS_SANBOOT_H */ diff --git a/qemu/roms/ipxe/src/arch/x86_64/include/bits/strings.h b/qemu/roms/ipxe/src/arch/x86_64/include/bits/strings.h index 6ee99a500..3b7911f3b 100644 --- a/qemu/roms/ipxe/src/arch/x86_64/include/bits/strings.h +++ b/qemu/roms/ipxe/src/arch/x86_64/include/bits/strings.h @@ -1,7 +1,43 @@ #ifndef _BITS_STRINGS_H #define _BITS_STRINGS_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * Find first (i.e. least significant) set bit + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __ffsll ( long long value ){ + long long lsb_minus_one; + + /* If the input value is zero, the BSF instruction returns + * ZF=0 and leaves an undefined value in the output register. + * Perform this check in C rather than asm so that it can be + * omitted in cases where the compiler is able to prove that + * the input is non-zero. + */ + if ( value ) { + __asm__ ( "bsfq %1, %0" + : "=r" ( lsb_minus_one ) + : "rm" ( value ) ); + return ( lsb_minus_one + 1 ); + } else { + return 0; + } +} + +/** + * Find first (i.e. least significant) set bit + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __ffsl ( long value ) { + + return __ffsll ( value ); +} /** * Find last (i.e. most significant) set bit @@ -13,7 +49,7 @@ static inline __attribute__ (( always_inline )) int __flsll ( long long value ){ long long msb_minus_one; /* If the input value is zero, the BSR instruction returns - * ZF=1 and leaves an undefined value in the output register. + * ZF=0 and leaves an undefined value in the output register. * Perform this check in C rather than asm so that it can be * omitted in cases where the compiler is able to prove that * the input is non-zero. diff --git a/qemu/roms/ipxe/src/arch/x86_64/include/bits/time.h b/qemu/roms/ipxe/src/arch/x86_64/include/bits/time.h index 59b355359..aa74fac8c 100644 --- a/qemu/roms/ipxe/src/arch/x86_64/include/bits/time.h +++ b/qemu/roms/ipxe/src/arch/x86_64/include/bits/time.h @@ -7,6 +7,6 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #endif /* _BITS_TIME_H */ diff --git a/qemu/roms/ipxe/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h b/qemu/roms/ipxe/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h index 9a4790fdc..6511c1ad3 100644 --- a/qemu/roms/ipxe/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h +++ b/qemu/roms/ipxe/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h @@ -4,7 +4,7 @@ * 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 any later version. + * 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 @@ -13,7 +13,12 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #ifndef _DHCP_ARCH_H @@ -24,7 +29,7 @@ * Architecture-specific DHCP options */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/arch/x86_64/include/ipxe/msr.h b/qemu/roms/ipxe/src/arch/x86_64/include/ipxe/msr.h index a5816ac35..316243b69 100644 --- a/qemu/roms/ipxe/src/arch/x86_64/include/ipxe/msr.h +++ b/qemu/roms/ipxe/src/arch/x86_64/include/ipxe/msr.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Read model-specific register diff --git a/qemu/roms/ipxe/src/arch/x86_64/include/setjmp.h b/qemu/roms/ipxe/src/arch/x86_64/include/setjmp.h new file mode 100644 index 000000000..69835d9fa --- /dev/null +++ b/qemu/roms/ipxe/src/arch/x86_64/include/setjmp.h @@ -0,0 +1,34 @@ +#ifndef _SETJMP_H +#define _SETJMP_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** A jump buffer */ +typedef struct { + /** Saved return address */ + uint64_t retaddr; + /** Saved stack pointer */ + uint64_t stack; + /** Saved %rbx */ + uint64_t rbx; + /** Saved %rbp */ + uint64_t rbp; + /** Saved %r12 */ + uint64_t r12; + /** Saved %r13 */ + uint64_t r13; + /** Saved %r14 */ + uint64_t r14; + /** Saved %r15 */ + uint64_t r15; +} jmp_buf[1]; + +extern int __asmcall __attribute__ (( returns_twice )) +setjmp ( jmp_buf env ); + +extern void __asmcall __attribute__ (( noreturn )) +longjmp ( jmp_buf env, int val ); + +#endif /* _SETJMP_H */ diff --git a/qemu/roms/ipxe/src/config/.gitignore b/qemu/roms/ipxe/src/config/.gitignore deleted file mode 100644 index 8e94f32fe..000000000 --- a/qemu/roms/ipxe/src/config/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.buildserial.* diff --git a/qemu/roms/ipxe/src/config/branding.h b/qemu/roms/ipxe/src/config/branding.h new file mode 100644 index 000000000..73f00af95 --- /dev/null +++ b/qemu/roms/ipxe/src/config/branding.h @@ -0,0 +1,174 @@ +#ifndef CONFIG_BRANDING_H +#define CONFIG_BRANDING_H + +/** @file + * + * Branding configuration + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/* + * Branding + * + * Vendors may use these strings to add their own branding to iPXE. + * PRODUCT_NAME is displayed prior to any iPXE branding in startup + * messages, and PRODUCT_SHORT_NAME is used where a brief product + * label is required (e.g. in BIOS boot selection menus). + * + * To minimise end-user confusion, it's probably a good idea to either + * make PRODUCT_SHORT_NAME a substring of PRODUCT_NAME or leave it as + * "iPXE". + * + */ +#define PRODUCT_NAME "" +#define PRODUCT_SHORT_NAME "iPXE" +#define PRODUCT_URI "http://ipxe.org" + +/* + * Tag line + * + * If your PRODUCT_SHORT_NAME is longer than the four characters used + * by "iPXE", then the standard tag line "Open Source Network Boot + * Firmware" is unlikely to fit neatly onto the screen. + */ +#define PRODUCT_TAG_LINE "Open Source Network Boot Firmware" + +/* + * Error messages + * + * iPXE error messages comprise a summary error message + * (e.g. "Permission denied") and a 32-bit error number. This number + * is incorporated into an error URI such as + * + * "No such file or directory (http://ipxe.org/2d0c613b)" + * + * or + * + * "Operation not supported (http://ipxe.org/3c092003)" + * + * Users may browse to the URI within the error message, which is + * provided by a database running on the iPXE web site + * (http://ipxe.org). This database provides details for all possible + * errors generated by iPXE, including: + * + * - the detailed error message (e.g. "Not an OCSP signing + * certificate") to complement the summary message (e.g. "Permission + * denied") which is compiled into the iPXE binary. + * + * - an instruction to the user to upgrade, if the error cannot be + * generated by the latest version of iPXE. + * + * - hints on how to fix the error (e.g. "This error indicates that + * the file was not found on the TFTP server. Check that you can + * retrieve the file using an alternative TFTP client, such as + * tftp-hpa on Linux.") + * + * - details of which source file within the iPXE codebase generated + * the error. + * + * - a direct link to the line(s) of code which generated the error. + * + * If you have a customer support team and would like your customers + * to contact your support team for all problems, instead of using the + * existing support infrastructure provided by http://ipxe.org, then + * you may define a custom URI to be included within error messages. + * + * Note that the custom URI is a printf() format string which must + * include a format specifier for the 32-bit error number. + */ +#define PRODUCT_ERROR_URI "http://ipxe.org/%08x" + +/* + * Command help messages + * + * iPXE command help messages include a URI constructed from the + * command name, such as + * + * "See http://ipxe.org/cmd/vcreate for further information" + * + * The iPXE web site includes documentation for the commands provided + * by the iPXE shell, including: + * + * - details of the command syntax (e.g. "vcreate --tag + * [--priority ] "). + * + * - example usages of the command (e.g. "vcreate --tag 123 net0") + * + * - a formal description of the command (e.g. "Create a VLAN network + * interface on an existing trunk network interface. The new network + * interface will be named by appending a hyphen and the VLAN tag + * value to the trunk network interface name.") + * + * - details of the possible exit statuses from the command. + * + * - links to documentation for related commands (e.g. "vdestroy") + * + * - links to documentation for relevant build options (e.g. "VLAN_CMD"). + * + * - general hints and tips on using the command. + * + * If you want to provide your own documentation for all of the + * commands provided by the iPXE shell, rather than using the existing + * support infrastructure provided by http://ipxe.org, then you may + * define a custom URI to be included within command help messages. + * + * Note that the custom URI is a printf() format string which must + * include a format specifier for the command name. + * + * [ Please also note that the existing documentation is licensed + * under Creative Commons terms which require attribution to the + * iPXE project and prohibit the alteration or removal of any + * references to "iPXE". ] + */ +#define PRODUCT_COMMAND_URI "http://ipxe.org/cmd/%s" + +/* + * Setting help messages + * + * iPXE setting help messages include a URI constructed from the + * setting name, such as + * + * "http://ipxe.org/cfg/initiator-iqn" + * + * The iPXE web site includes documentation for the settings used by + * iPXE, including: + * + * - details of the corresponding DHCP option number. + * + * - details of the corresponding ISC dhcpd option name. + * + * - examples of using the setting from the iPXE command line, or in + * iPXE scripts. + * + * - examples of configuring the setting via a DHCP server. + * + * - a formal description of the setting. + * + * - links to documentation for related settings. + * + * - links to documentation for relevant build options. + * + * - general notes about the setting. + * + * If you want to provide your own documentation for all of the + * settings used by iPXE, rather than using the existing support + * infrastructure provided by http://ipxe.org, then you may define a + * custom URI to be included within setting help messages. + * + * Note that the custom URI is a printf() format string which must + * include a format specifier for the setting name. + * + * [ Please also note that the existing documentation is licensed + * under Creative Commons terms which require attribution to the + * iPXE project and prohibit the alteration or removal of any + * references to "iPXE". ] + */ +#define PRODUCT_SETTING_URI "http://ipxe.org/cfg/%s" + +#include + +#endif /* CONFIG_BRANDING_H */ diff --git a/qemu/roms/ipxe/src/config/colour.h b/qemu/roms/ipxe/src/config/colour.h index 57d20c1db..98198f12f 100644 --- a/qemu/roms/ipxe/src/config/colour.h +++ b/qemu/roms/ipxe/src/config/colour.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define COLOR_NORMAL_FG COLOR_WHITE #define COLOR_NORMAL_BG COLOR_BLUE diff --git a/qemu/roms/ipxe/src/config/config.c b/qemu/roms/ipxe/src/config/config.c index 6c8b9551a..1dd912c1d 100644 --- a/qemu/roms/ipxe/src/config/config.c +++ b/qemu/roms/ipxe/src/config/config.c @@ -1,11 +1,25 @@ /* * 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) any later version. + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -30,33 +44,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * in the final iPXE executable built. */ -/* - * Build ID string calculations - * - */ -#undef XSTR -#undef STR -#define XSTR(s) STR(s) -#define STR(s) #s - -#ifdef BUILD_SERIAL -#include "config/.buildserial.h" -#define BUILD_SERIAL_STR " #" XSTR(BUILD_SERIAL_NUM) -#else -#define BUILD_SERIAL_STR "" -#endif - -#ifdef BUILD_ID -#define BUILD_ID_STR " " BUILD_ID -#else -#define BUILD_ID_STR "" -#endif - -#if defined(BUILD_ID) || defined(BUILD_SERIAL) -#define BUILD_STRING " [build" BUILD_ID_STR BUILD_SERIAL_STR "]" -#else -#define BUILD_STRING "" -#endif +PROVIDE_REQUIRING_SYMBOL(); /* * Drag in all requested console types @@ -67,7 +55,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); REQUIRE_OBJECT ( bios_console ); #endif #ifdef CONSOLE_SERIAL -REQUIRE_OBJECT ( serial_console ); +REQUIRE_OBJECT ( serial ); #endif #ifdef CONSOLE_DIRECT_VGA REQUIRE_OBJECT ( video_subr ); @@ -96,6 +84,9 @@ REQUIRE_OBJECT ( debugcon ); #ifdef CONSOLE_VESAFB REQUIRE_OBJECT ( vesafb ); #endif +#ifdef CONSOLE_INT13 +REQUIRE_OBJECT ( int13con ); +#endif /* * Drag in all requested network protocols @@ -149,6 +140,9 @@ REQUIRE_OBJECT ( slam ); #ifdef SANBOOT_PROTO_ISCSI REQUIRE_OBJECT ( iscsi ); #endif +#ifdef SANBOOT_PROTO_HTTP +REQUIRE_OBJECT ( httpblock ); +#endif /* * Drag in all requested resolvers @@ -349,6 +343,9 @@ REQUIRE_OBJECT ( cpuid_settings ); #ifdef MEMMAP_SETTINGS REQUIRE_OBJECT ( memmap_settings ); #endif +#ifdef VRAM_SETTINGS +REQUIRE_OBJECT ( vram_settings ); +#endif /* * Drag in selected keyboard map diff --git a/qemu/roms/ipxe/src/config/config_crypto.c b/qemu/roms/ipxe/src/config/config_crypto.c new file mode 100644 index 000000000..1e125d8ab --- /dev/null +++ b/qemu/roms/ipxe/src/config/config_crypto.c @@ -0,0 +1,76 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** @file + * + * Cryptographic configuration + * + * Cryptographic configuration is slightly messy since we need to drag + * in objects based on combinations of build options. + */ + +PROVIDE_REQUIRING_SYMBOL(); + +/* RSA and MD5 */ +#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_DIGEST_MD5 ) +REQUIRE_OBJECT ( rsa_md5 ); +#endif + +/* RSA and SHA-1 */ +#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_DIGEST_SHA1 ) +REQUIRE_OBJECT ( rsa_sha1 ); +#endif + +/* RSA and SHA-224 */ +#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_DIGEST_SHA224 ) +REQUIRE_OBJECT ( rsa_sha224 ); +#endif + +/* RSA and SHA-256 */ +#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_DIGEST_SHA256 ) +REQUIRE_OBJECT ( rsa_sha256 ); +#endif + +/* RSA and SHA-384 */ +#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_DIGEST_SHA384 ) +REQUIRE_OBJECT ( rsa_sha384 ); +#endif + +/* RSA and SHA-512 */ +#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_DIGEST_SHA512 ) +REQUIRE_OBJECT ( rsa_sha512 ); +#endif + +/* RSA, AES-CBC, and SHA-1 */ +#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_CIPHER_AES_CBC ) && \ + defined ( CRYPTO_DIGEST_SHA1 ) +REQUIRE_OBJECT ( rsa_aes_cbc_sha1 ); +#endif + +/* RSA, AES-CBC, and SHA-256 */ +#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_CIPHER_AES_CBC ) && \ + defined ( CRYPTO_DIGEST_SHA256 ) +REQUIRE_OBJECT ( rsa_aes_cbc_sha256 ); +#endif diff --git a/qemu/roms/ipxe/src/config/config_ethernet.c b/qemu/roms/ipxe/src/config/config_ethernet.c index d13bd6144..de7a07c57 100644 --- a/qemu/roms/ipxe/src/config/config_ethernet.c +++ b/qemu/roms/ipxe/src/config/config_ethernet.c @@ -1,11 +1,25 @@ /* * 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) any later version. + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include @@ -15,6 +29,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Drag in Ethernet-specific protocols */ @@ -24,3 +40,6 @@ REQUIRE_OBJECT ( aoe ); #ifdef NET_PROTO_FCOE REQUIRE_OBJECT ( fcoe ); #endif +#ifdef NET_PROTO_STP +REQUIRE_OBJECT ( stp ); +#endif diff --git a/qemu/roms/ipxe/src/config/config_fc.c b/qemu/roms/ipxe/src/config/config_fc.c index 414646994..33fc9462a 100644 --- a/qemu/roms/ipxe/src/config/config_fc.c +++ b/qemu/roms/ipxe/src/config/config_fc.c @@ -1,11 +1,25 @@ /* * 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) any later version. + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include @@ -15,6 +29,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Drag in Fibre Channel-specific commands * diff --git a/qemu/roms/ipxe/src/config/config_http.c b/qemu/roms/ipxe/src/config/config_http.c new file mode 100644 index 000000000..3f198d228 --- /dev/null +++ b/qemu/roms/ipxe/src/config/config_http.c @@ -0,0 +1,45 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** @file + * + * HTTP extensions + * + */ + +PROVIDE_REQUIRING_SYMBOL(); + +/* + * Drag in HTTP extensions + */ +#ifdef HTTP_AUTH_BASIC +REQUIRE_OBJECT ( httpbasic ); +#endif +#ifdef HTTP_AUTH_DIGEST +REQUIRE_OBJECT ( httpdigest ); +#endif +#ifdef HTTP_ENC_PEERDIST +REQUIRE_OBJECT ( peerdist ); +#endif diff --git a/qemu/roms/ipxe/src/config/config_infiniband.c b/qemu/roms/ipxe/src/config/config_infiniband.c index 432e621d0..a742e7559 100644 --- a/qemu/roms/ipxe/src/config/config_infiniband.c +++ b/qemu/roms/ipxe/src/config/config_infiniband.c @@ -1,11 +1,25 @@ /* * 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) any later version. + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include @@ -15,6 +29,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Drag in Infiniband-specific protocols */ diff --git a/qemu/roms/ipxe/src/config/config_net80211.c b/qemu/roms/ipxe/src/config/config_net80211.c index b33c363b1..343617548 100644 --- a/qemu/roms/ipxe/src/config/config_net80211.c +++ b/qemu/roms/ipxe/src/config/config_net80211.c @@ -1,8 +1,18 @@ /* * 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) any later version. + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. */ FILE_LICENCE ( GPL2_OR_LATER ); @@ -15,6 +25,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Drag in 802.11-specific commands * diff --git a/qemu/roms/ipxe/src/config/config_romprefix.c b/qemu/roms/ipxe/src/config/config_romprefix.c index 85f1e78ab..21921b867 100644 --- a/qemu/roms/ipxe/src/config/config_romprefix.c +++ b/qemu/roms/ipxe/src/config/config_romprefix.c @@ -1,11 +1,25 @@ /* * 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) any later version. + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include @@ -15,6 +29,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Provide UNDI loader if PXE stack is requested * diff --git a/qemu/roms/ipxe/src/config/config_route.c b/qemu/roms/ipxe/src/config/config_route.c index 33e18cdd3..c0b4ee91d 100644 --- a/qemu/roms/ipxe/src/config/config_route.c +++ b/qemu/roms/ipxe/src/config/config_route.c @@ -1,11 +1,25 @@ /* * 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) any later version. + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include @@ -15,6 +29,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Drag in routing management for relevant protocols * diff --git a/qemu/roms/ipxe/src/config/config_usb.c b/qemu/roms/ipxe/src/config/config_usb.c new file mode 100644 index 000000000..dc0e6e6af --- /dev/null +++ b/qemu/roms/ipxe/src/config/config_usb.c @@ -0,0 +1,52 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** @file + * + * USB configuration options + * + */ + +PROVIDE_REQUIRING_SYMBOL(); + +/* + * Drag in USB controllers + */ +#ifdef USB_HCD_XHCI +REQUIRE_OBJECT ( xhci ); +#endif +#ifdef USB_HCD_EHCI +REQUIRE_OBJECT ( ehci ); +#endif +#ifdef USB_HCD_UHCI +REQUIRE_OBJECT ( uhci ); +#endif + +/* + * Drag in USB peripherals + */ +#ifdef USB_KEYBOARD +REQUIRE_OBJECT ( usbkbd ); +#endif diff --git a/qemu/roms/ipxe/src/config/console.h b/qemu/roms/ipxe/src/config/console.h index 908ec5a0b..ffa5cf50d 100644 --- a/qemu/roms/ipxe/src/config/console.h +++ b/qemu/roms/ipxe/src/config/console.h @@ -10,7 +10,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include @@ -23,6 +23,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); //#define CONSOLE_VMWARE /* VMware logfile console */ //#define CONSOLE_DEBUGCON /* Debug port console */ //#define CONSOLE_VESAFB /* VESA framebuffer console */ +//#define CONSOLE_INT13 /* INT13 disk log console */ #define KEYBOARD_MAP us diff --git a/qemu/roms/ipxe/src/config/crypto.h b/qemu/roms/ipxe/src/config/crypto.h index 1e021b0fb..bccfc04b8 100644 --- a/qemu/roms/ipxe/src/config/crypto.h +++ b/qemu/roms/ipxe/src/config/crypto.h @@ -7,7 +7,40 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** RSA public-key algorithm */ +#define CRYPTO_PUBKEY_RSA + +/** AES-CBC block cipher */ +#define CRYPTO_CIPHER_AES_CBC + +/** MD5 digest algorithm + * + * Note that use of MD5 is implicit when using TLSv1.1 or earlier. + */ +#define CRYPTO_DIGEST_MD5 + +/** SHA-1 digest algorithm + * + * Note that use of SHA-1 is implicit when using TLSv1.1 or earlier. + */ +#define CRYPTO_DIGEST_SHA1 + +/** SHA-224 digest algorithm */ +#define CRYPTO_DIGEST_SHA224 + +/** SHA-256 digest algorithm + * + * Note that use of SHA-256 is implicit when using TLSv1.2. + */ +#define CRYPTO_DIGEST_SHA256 + +/** SHA-384 digest algorithm */ +#define CRYPTO_DIGEST_SHA384 + +/** SHA-512 digest algorithm */ +#define CRYPTO_DIGEST_SHA512 /** Margin of error (in seconds) allowed in signed timestamps * diff --git a/qemu/roms/ipxe/src/config/defaults.h b/qemu/roms/ipxe/src/config/defaults.h index 389c0b07b..32d6dbcce 100644 --- a/qemu/roms/ipxe/src/config/defaults.h +++ b/qemu/roms/ipxe/src/config/defaults.h @@ -1,7 +1,7 @@ #ifndef CONFIG_DEFAULTS_H #define CONFIG_DEFAULTS_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define CONFIG_DEFAULTS(_platform) diff --git a/qemu/roms/ipxe/src/config/defaults/efi.h b/qemu/roms/ipxe/src/config/defaults/efi.h index 4276d9366..cdf41c54d 100644 --- a/qemu/roms/ipxe/src/config/defaults/efi.h +++ b/qemu/roms/ipxe/src/config/defaults/efi.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define UACCESS_EFI #define IOAPI_X86 @@ -19,8 +19,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define SMBIOS_EFI #define SANBOOT_NULL #define BOFM_EFI -#define ENTROPY_NULL -#define TIME_NULL +#define ENTROPY_EFI +#define TIME_EFI #define REBOOT_EFI #define IMAGE_EFI /* EFI image support */ diff --git a/qemu/roms/ipxe/src/config/defaults/pcbios.h b/qemu/roms/ipxe/src/config/defaults/pcbios.h index 7debc8d2f..3ed8343ce 100644 --- a/qemu/roms/ipxe/src/config/defaults/pcbios.h +++ b/qemu/roms/ipxe/src/config/defaults/pcbios.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define UACCESS_LIBRM #define IOAPI_X86 @@ -35,6 +35,12 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define SANBOOT_PROTO_AOE /* AoE protocol */ #define SANBOOT_PROTO_IB_SRP /* Infiniband SCSI RDMA protocol */ #define SANBOOT_PROTO_FCP /* Fibre Channel protocol */ +#define SANBOOT_PROTO_HTTP /* HTTP SAN protocol */ + +#define USB_HCD_XHCI /* xHCI USB host controller */ +#define USB_HCD_EHCI /* EHCI USB host controller */ +#define USB_HCD_UHCI /* UHCI USB host controller */ +#define USB_KEYBOARD /* USB keyboards */ #define REBOOT_CMD /* Reboot command */ #define CPUID_CMD /* x86 CPU feature detection command */ diff --git a/qemu/roms/ipxe/src/config/dhcp.h b/qemu/roms/ipxe/src/config/dhcp.h new file mode 100644 index 000000000..49fe16b92 --- /dev/null +++ b/qemu/roms/ipxe/src/config/dhcp.h @@ -0,0 +1,87 @@ +#ifndef CONFIG_DHCP_H +#define CONFIG_DHCP_H + +/** @file + * + * DHCP configuration + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/* + * DHCP and PXE Boot Server timeout parameters + * + * Initial and final timeout for DHCP discovery + * + * The PXE spec indicates discover request are sent 4 times, with + * timeouts of 4, 8, 16, 32 seconds. iPXE by default uses 1, 2, 4, 8. + */ +#define DHCP_DISC_START_TIMEOUT_SEC 1 +#define DHCP_DISC_END_TIMEOUT_SEC 10 +//#define DHCP_DISC_START_TIMEOUT_SEC 4 /* as per PXE spec */ +//#define DHCP_DISC_END_TIMEOUT_SEC 32 /* as per PXE spec */ + +/* + * ProxyDHCP offers are given precedence by continue to wait for them + * after a valid DHCPOFFER is received. We'll wait through this + * timeout for it. The PXE spec indicates waiting through the 4 & 8 + * second timeouts, iPXE by default stops after 2. + */ +#define DHCP_DISC_PROXY_TIMEOUT_SEC 2 +//#define DHCP_DISC_PROXY_TIMEOUT_SEC 11 /* as per PXE spec */ + +/* + * Per the PXE spec, requests are also tried 4 times, but at timeout + * intervals of 1, 2, 3, 4 seconds. To adapt this to an exponential + * backoff timer, we can either do 1, 2, 4, 8, ie. 4 retires with a + * longer interval or start at 0 (0.25s) for 0.25, 0.5, 1, 2, 4, + * ie. one extra try and shorter initial timeouts. iPXE by default + * does a combination of both, starting at 0 and going through the 8 + * second timeout. + */ +#define DHCP_REQ_START_TIMEOUT_SEC 0 +#define DHCP_REQ_END_TIMEOUT_SEC 10 +//#define DHCP_REQ_END_TIMEOUT_SEC 4 /* as per PXE spec */ + +/* + * A ProxyDHCP offer without PXE options also goes through a request + * phase using these same parameters, but note the early break below. + */ +#define DHCP_PROXY_START_TIMEOUT_SEC 0 +#define DHCP_PROXY_END_TIMEOUT_SEC 10 +//#define DHCP_PROXY_END_TIMEOUT_SEC 8 /* as per PXE spec */ + +/* + * A ProxyDHCP request timeout should not induce a failure condition, + * so we always want to break before the above set of timers expire. + * The iPXE default value of 2 breaks at the first timeout after 2 + * seconds, which will be after the 2 second timeout. + */ +#define DHCP_REQ_PROXY_TIMEOUT_SEC 2 +//#define DHCP_REQ_PROXY_TIMEOUT_SEC 7 /* as per PXE spec */ + +/* + * Per the PXE spec, a PXE boot server request is also be retried 4 + * times at timeouts of 1, 2, 3, 4. iPXE uses the same timeouts as + * discovery, 1, 2, 4, 8, but will move on to the next server if + * available after an elapsed time greater than 3 seconds, therefore + * effectively only sending 3 tries at timeouts of 1, 2, 4. + */ +#define PXEBS_START_TIMEOUT_SEC 1 +#define PXEBS_END_TIMEOUT_SEC 10 +//#define PXEBS_START_TIMEOUT_SEC 0 /* as per PXE spec */ +//#define PXEBS_END_TIMEOUT_SEC 8 /* as per PXE spec */ + +/* + * Increment to the next PXE Boot server, if available, after this + * this much time has elapsed. + */ +#define PXEBS_MAX_TIMEOUT_SEC 3 +//#define PXEBS_MAX_TIMEOUT_SEC 7 /* as per PXE spec */ + +#include + +#endif /* CONFIG_DHCP_H */ diff --git a/qemu/roms/ipxe/src/config/entropy.h b/qemu/roms/ipxe/src/config/entropy.h index 7de2f6737..c79060fd5 100644 --- a/qemu/roms/ipxe/src/config/entropy.h +++ b/qemu/roms/ipxe/src/config/entropy.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/config/fault.h b/qemu/roms/ipxe/src/config/fault.h new file mode 100644 index 000000000..5024a8ff3 --- /dev/null +++ b/qemu/roms/ipxe/src/config/fault.h @@ -0,0 +1,34 @@ +#ifndef CONFIG_FAULT_H +#define CONFIG_FAULT_H + +/** @file + * + * Fault injection + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/* Drop every N transmitted or received network packets */ +#define NETDEV_DISCARD_RATE 0 + +/* Drop every N transmitted or received PeerDist discovery packets */ +#define PEERDISC_DISCARD_RATE 0 + +/* Annul every N PeerDist download attempts */ +#define PEERBLK_ANNUL_RATE 0 + +/* Stall every N PeerDist download attempts */ +#define PEERBLK_STALL_RATE 0 + +/* Abort every N PeerDist download attempts */ +#define PEERBLK_ABORT_RATE 0 + +/* Corrupt every N received PeerDist packets */ +#define PEERBLK_CORRUPT_RATE 0 + +#include + +#endif /* CONFIG_FAULT_H */ diff --git a/qemu/roms/ipxe/src/config/general.h b/qemu/roms/ipxe/src/config/general.h index 539203457..ee15f6bf1 100644 --- a/qemu/roms/ipxe/src/config/general.h +++ b/qemu/roms/ipxe/src/config/general.h @@ -7,26 +7,10 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -/* - * Branding - * - * Vendors may use these strings to add their own branding to iPXE. - * PRODUCT_NAME is displayed prior to any iPXE branding in startup - * messages, and PRODUCT_SHORT_NAME is used where a brief product - * label is required (e.g. in BIOS boot selection menus). - * - * To minimise end-user confusion, it's probably a good idea to either - * make PRODUCT_SHORT_NAME a substring of PRODUCT_NAME or leave it as - * "iPXE". - * - */ -#define PRODUCT_NAME "" -#define PRODUCT_SHORT_NAME "iPXE" - /* * Banner timeout configuration * @@ -53,6 +37,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define NET_PROTO_IPV4 /* IPv4 protocol */ #undef NET_PROTO_IPV6 /* IPv6 protocol */ #undef NET_PROTO_FCOE /* Fibre Channel over Ethernet protocol */ +#define NET_PROTO_STP /* Spanning Tree protocol */ /* * PXE support @@ -82,6 +67,15 @@ FILE_LICENCE ( GPL2_OR_LATER ); //#undef SANBOOT_PROTO_AOE /* AoE protocol */ //#undef SANBOOT_PROTO_IB_SRP /* Infiniband SCSI RDMA protocol */ //#undef SANBOOT_PROTO_FCP /* Fibre Channel protocol */ +//#undef SANBOOT_PROTO_HTTP /* HTTP SAN protocol */ + +/* + * HTTP extensions + * + */ +#define HTTP_AUTH_BASIC /* Basic authentication */ +#define HTTP_AUTH_DIGEST /* Digest authentication */ +//#define HTTP_ENC_PEERDIST /* PeerDist content encoding */ /* * 802.11 cryptosystems and handshaking protocols @@ -156,6 +150,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ #undef NONPNP_HOOK_INT19 /* Hook INT19 on non-PnP BIOSes */ +#define AUTOBOOT_ROM_FILTER /* Autoboot only devices matching our ROM */ /* * Error message tables to include @@ -170,7 +165,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -#define NETDEV_DISCARD_RATE 0 /* Drop every N packets (0=>no drop) */ #undef BUILD_SERIAL /* Include an automatic build serial * number. Add "bs" to the list of * make targets. For example: @@ -181,6 +175,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #undef GDBSERIAL /* Remote GDB debugging over serial */ #undef GDBUDP /* Remote GDB debugging over UDP * (both may be set) */ +//#define EFI_DOWNGRADE_UX /* Downgrade UEFI user experience */ #include #include NAMED_CONFIG(general.h) diff --git a/qemu/roms/ipxe/src/config/ioapi.h b/qemu/roms/ipxe/src/config/ioapi.h index ce19c6d71..abe5a50ce 100644 --- a/qemu/roms/ipxe/src/config/ioapi.h +++ b/qemu/roms/ipxe/src/config/ioapi.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/config/named.h b/qemu/roms/ipxe/src/config/named.h index 36efdabdd..ddde6f0a6 100644 --- a/qemu/roms/ipxe/src/config/named.h +++ b/qemu/roms/ipxe/src/config/named.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* config//
      .h */ #ifdef CONFIG diff --git a/qemu/roms/ipxe/src/config/nap.h b/qemu/roms/ipxe/src/config/nap.h index 187af4289..e4fe97964 100644 --- a/qemu/roms/ipxe/src/config/nap.h +++ b/qemu/roms/ipxe/src/config/nap.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/tests/qapi-schema/alternate-good.err b/qemu/roms/ipxe/src/config/qemu/colour.h similarity index 100% rename from qemu/tests/qapi-schema/alternate-good.err rename to qemu/roms/ipxe/src/config/qemu/colour.h diff --git a/qemu/tests/qapi-schema/data-array-empty.out b/qemu/roms/ipxe/src/config/qemu/console.h similarity index 100% rename from qemu/tests/qapi-schema/data-array-empty.out rename to qemu/roms/ipxe/src/config/qemu/console.h diff --git a/qemu/tests/qapi-schema/data-array-unknown.out b/qemu/roms/ipxe/src/config/qemu/crypto.h similarity index 100% rename from qemu/tests/qapi-schema/data-array-unknown.out rename to qemu/roms/ipxe/src/config/qemu/crypto.h diff --git a/qemu/roms/ipxe/src/config/qemu/general.h b/qemu/roms/ipxe/src/config/qemu/general.h new file mode 100644 index 000000000..30f60d3f7 --- /dev/null +++ b/qemu/roms/ipxe/src/config/qemu/general.h @@ -0,0 +1,10 @@ +/* Disable entry during POST */ +#undef ROM_BANNER_TIMEOUT +#define ROM_BANNER_TIMEOUT 0 + +/* Extend banner timeout */ +#undef BANNER_TIMEOUT +#define BANNER_TIMEOUT 30 + +/* Work around missing EFI_PXE_BASE_CODE_PROTOCOL */ +#define EFI_DOWNGRADE_UX diff --git a/qemu/tests/qapi-schema/data-int.out b/qemu/roms/ipxe/src/config/qemu/serial.h similarity index 100% rename from qemu/tests/qapi-schema/data-int.out rename to qemu/roms/ipxe/src/config/qemu/serial.h diff --git a/qemu/tests/qapi-schema/data-member-array-bad.out b/qemu/roms/ipxe/src/config/qemu/settings.h similarity index 100% rename from qemu/tests/qapi-schema/data-member-array-bad.out rename to qemu/roms/ipxe/src/config/qemu/settings.h diff --git a/qemu/tests/qapi-schema/data-member-array.err b/qemu/roms/ipxe/src/config/qemu/sideband.h similarity index 100% rename from qemu/tests/qapi-schema/data-member-array.err rename to qemu/roms/ipxe/src/config/qemu/sideband.h diff --git a/qemu/tests/qapi-schema/data-member-unknown.out b/qemu/roms/ipxe/src/config/qemu/usb.h similarity index 100% rename from qemu/tests/qapi-schema/data-member-unknown.out rename to qemu/roms/ipxe/src/config/qemu/usb.h diff --git a/qemu/roms/ipxe/src/config/reboot.h b/qemu/roms/ipxe/src/config/reboot.h index 240ef87be..2d1648e7b 100644 --- a/qemu/roms/ipxe/src/config/reboot.h +++ b/qemu/roms/ipxe/src/config/reboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/config/sanboot.h b/qemu/roms/ipxe/src/config/sanboot.h index 1d7f5f177..ccc4bda1f 100644 --- a/qemu/roms/ipxe/src/config/sanboot.h +++ b/qemu/roms/ipxe/src/config/sanboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/config/serial.h b/qemu/roms/ipxe/src/config/serial.h index 08368efdb..27040dc54 100644 --- a/qemu/roms/ipxe/src/config/serial.h +++ b/qemu/roms/ipxe/src/config/serial.h @@ -13,11 +13,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); -#define COM1 0x3f8 -#define COM2 0x2f8 -#define COM3 0x3e8 -#define COM4 0x2e8 - #define COMCONSOLE COM1 /* I/O port address */ /* Keep settings from a previous user of the serial port (e.g. lilo or diff --git a/qemu/roms/ipxe/src/config/settings.h b/qemu/roms/ipxe/src/config/settings.h index 42fe9cc81..01feaaa87 100644 --- a/qemu/roms/ipxe/src/config/settings.h +++ b/qemu/roms/ipxe/src/config/settings.h @@ -7,12 +7,13 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define PCI_SETTINGS /* PCI device settings */ //#define CPUID_SETTINGS /* CPUID settings */ //#define MEMMAP_SETTINGS /* Memory map settings */ //#define VMWARE_SETTINGS /* VMware GuestInfo settings */ +//#define VRAM_SETTINGS /* Video RAM dump settings */ #include #include NAMED_CONFIG(settings.h) diff --git a/qemu/roms/ipxe/src/config/sideband.h b/qemu/roms/ipxe/src/config/sideband.h index 039bb5d09..dd704f9bb 100644 --- a/qemu/roms/ipxe/src/config/sideband.h +++ b/qemu/roms/ipxe/src/config/sideband.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); //#define CONFIG_BOFM /* IBM's BladeCenter Open Fabric Manager */ diff --git a/qemu/roms/ipxe/src/config/time.h b/qemu/roms/ipxe/src/config/time.h index 0576211fd..678f6f864 100644 --- a/qemu/roms/ipxe/src/config/time.h +++ b/qemu/roms/ipxe/src/config/time.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/config/timer.h b/qemu/roms/ipxe/src/config/timer.h index abd669851..5a54d398c 100644 --- a/qemu/roms/ipxe/src/config/timer.h +++ b/qemu/roms/ipxe/src/config/timer.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/config/umalloc.h b/qemu/roms/ipxe/src/config/umalloc.h index 245c6b4aa..832dd21d1 100644 --- a/qemu/roms/ipxe/src/config/umalloc.h +++ b/qemu/roms/ipxe/src/config/umalloc.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/config/usb.h b/qemu/roms/ipxe/src/config/usb.h new file mode 100644 index 000000000..52e82eaad --- /dev/null +++ b/qemu/roms/ipxe/src/config/usb.h @@ -0,0 +1,33 @@ +#ifndef CONFIG_USB_H +#define CONFIG_USB_H + +/** @file + * + * USB configuration + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/* + * USB host controllers (all enabled by default) + * + */ +//#undef USB_HCD_XHCI /* xHCI USB host controller */ +//#undef USB_HCD_EHCI /* EHCI USB host controller */ +//#undef USB_HCD_UHCI /* UHCI USB host controller */ + +/* + * USB peripherals + * + */ +//#undef USB_KEYBOARD /* USB keyboards */ + +#include +#include NAMED_CONFIG(usb.h) +#include +#include LOCAL_NAMED_CONFIG(usb.h) + +#endif /* CONFIG_USB_H */ diff --git a/qemu/roms/ipxe/src/config/vbox/general.h b/qemu/roms/ipxe/src/config/vbox/general.h index 27d15daf2..06b45f1a8 100644 --- a/qemu/roms/ipxe/src/config/vbox/general.h +++ b/qemu/roms/ipxe/src/config/vbox/general.h @@ -1,25 +1,17 @@ /* Disabled from config/defaults/pcbios.h */ -#undef IMAGE_ELF #undef SANBOOT_PROTO_ISCSI #undef SANBOOT_PROTO_AOE #undef SANBOOT_PROTO_IB_SRP #undef SANBOOT_PROTO_FCP -#undef REBOOT_CMD -#undef CPUID_CMD /* Disabled from config/general.h */ -#undef DOWNLOAD_PROTO_HTTP #undef CRYPTO_80211_WEP #undef CRYPTO_80211_WPA #undef CRYPTO_80211_WPA2 #undef IWMGMT_CMD -#undef FCMGMT_CMD -#undef SANBOOT_CMD #undef MENU_CMD -#undef LOGIN_CMD -#undef SYNC_CMD /* Ensure ROM banner is not displayed */ diff --git a/qemu/tests/qapi-schema/data-unknown.out b/qemu/roms/ipxe/src/config/vbox/usb.h similarity index 100% rename from qemu/tests/qapi-schema/data-unknown.out rename to qemu/roms/ipxe/src/config/vbox/usb.h diff --git a/qemu/roms/ipxe/src/core/acpi.c b/qemu/roms/ipxe/src/core/acpi.c index 330f50631..b0ccfa78d 100644 --- a/qemu/roms/ipxe/src/core/acpi.c +++ b/qemu/roms/ipxe/src/core/acpi.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/ansicol.c b/qemu/roms/ipxe/src/core/ansicol.c index 142a00f8d..ddf9ba77c 100644 --- a/qemu/roms/ipxe/src/core/ansicol.c +++ b/qemu/roms/ipxe/src/core/ansicol.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/ansicoldef.c b/qemu/roms/ipxe/src/core/ansicoldef.c index dd89f3b70..6d8598e11 100644 --- a/qemu/roms/ipxe/src/core/ansicoldef.c +++ b/qemu/roms/ipxe/src/core/ansicoldef.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/ansiesc.c b/qemu/roms/ipxe/src/core/ansiesc.c index ca9a73ce0..7f545db0e 100644 --- a/qemu/roms/ipxe/src/core/ansiesc.c +++ b/qemu/roms/ipxe/src/core/ansiesc.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/asprintf.c b/qemu/roms/ipxe/src/core/asprintf.c index 03cf45cfc..00edf8e11 100644 --- a/qemu/roms/ipxe/src/core/asprintf.c +++ b/qemu/roms/ipxe/src/core/asprintf.c @@ -4,7 +4,7 @@ #include #include -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Write a formatted string to newly allocated memory. diff --git a/qemu/roms/ipxe/src/core/assert.c b/qemu/roms/ipxe/src/core/assert.c index 0791ea7b9..294e766be 100644 --- a/qemu/roms/ipxe/src/core/assert.c +++ b/qemu/roms/ipxe/src/core/assert.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/core/base16.c b/qemu/roms/ipxe/src/core/base16.c index bf9cc21bb..f9e0f3364 100644 --- a/qemu/roms/ipxe/src/core/base16.c +++ b/qemu/roms/ipxe/src/core/base16.c @@ -15,14 +15,20 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#include #include #include +#include +#include +#include #include /** @file @@ -32,48 +38,42 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ /** - * Base16-encode data + * Encode hexadecimal string (with optional byte separator character) * + * @v separator Byte separator character, or 0 for no separator * @v raw Raw data - * @v len Length of raw data - * @v encoded Buffer for encoded string - * - * The buffer must be the correct length for the encoded string. Use - * something like - * - * char buf[ base16_encoded_len ( len ) + 1 ]; - * - * (the +1 is for the terminating NUL) to provide a buffer of the - * correct size. + * @v raw_len Length of raw data + * @v data Buffer + * @v len Length of buffer + * @ret len Encoded length */ -void base16_encode ( const uint8_t *raw, size_t len, char *encoded ) { - const uint8_t *raw_bytes = raw; - char *encoded_bytes = encoded; - size_t remaining = len; - - /* Encode each byte */ - for ( ; remaining-- ; encoded_bytes += 2 ) { - sprintf ( encoded_bytes, "%02x", *(raw_bytes++) ); +size_t hex_encode ( char separator, const void *raw, size_t raw_len, + char *data, size_t len ) { + const uint8_t *bytes = raw; + const char delimiter[2] = { separator, '\0' }; + size_t used = 0; + unsigned int i; + + if ( len ) + data[0] = 0; /* Ensure that a terminating NUL exists */ + for ( i = 0 ; i < raw_len ; i++ ) { + used += ssnprintf ( ( data + used ), ( len - used ), + "%s%02x", ( used ? delimiter : "" ), + bytes[i] ); } - - /* Ensure terminating NUL exists even if length was zero */ - *encoded_bytes = '\0'; - - DBG ( "Base16-encoded to \"%s\":\n", encoded ); - DBG_HDA ( 0, raw, len ); - assert ( strlen ( encoded ) == base16_encoded_len ( len ) ); + return used; } /** - * Decode hexadecimal string + * Decode hexadecimal string (with optional byte separator character) * - * @v encoded Encoded string * @v separator Byte separator character, or 0 for no separator + * @v encoded Encoded string * @v data Buffer * @v len Length of buffer * @ret len Length of data, or negative error */ -int hex_decode ( const char *encoded, char separator, void *data, size_t len ) { +int hex_decode ( char separator, const char *encoded, void *data, size_t len ) { uint8_t *out = data; unsigned int count = 0; unsigned int sixteens; @@ -87,13 +87,13 @@ int hex_decode ( const char *encoded, char separator, void *data, size_t len ) { /* Extract digits. Note that either digit may be NUL, * which would be interpreted as an invalid value by - * strtoul_charval(); there is therefore no need for an + * digit_value(); there is therefore no need for an * explicit end-of-string check. */ - sixteens = strtoul_charval ( *(encoded++) ); + sixteens = digit_value ( *(encoded++) ); if ( sixteens >= 16 ) return -EINVAL; - units = strtoul_charval ( *(encoded++) ); + units = digit_value ( *(encoded++) ); if ( units >= 16 ) return -EINVAL; @@ -105,31 +105,3 @@ int hex_decode ( const char *encoded, char separator, void *data, size_t len ) { } return count; } - -/** - * Base16-decode data - * - * @v encoded Encoded string - * @v raw Raw data - * @ret len Length of raw data, or negative error - * - * The buffer must be large enough to contain the decoded data. Use - * something like - * - * char buf[ base16_decoded_max_len ( encoded ) ]; - * - * to provide a buffer of the correct size. - */ -int base16_decode ( const char *encoded, uint8_t *raw ) { - int len; - - len = hex_decode ( encoded, 0, raw, -1UL ); - if ( len < 0 ) - return len; - - DBG ( "Base16-decoded \"%s\" to:\n", encoded ); - DBG_HDA ( 0, raw, len ); - assert ( len <= ( int ) base16_decoded_max_len ( encoded ) ); - - return len; -} diff --git a/qemu/roms/ipxe/src/core/base64.c b/qemu/roms/ipxe/src/core/base64.c index bdaf70957..e452f7d41 100644 --- a/qemu/roms/ipxe/src/core/base64.c +++ b/qemu/roms/ipxe/src/core/base64.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -39,80 +43,73 @@ static const char base64[64] = * Base64-encode data * * @v raw Raw data - * @v len Length of raw data - * @v encoded Buffer for encoded string - * - * The buffer must be the correct length for the encoded string. Use - * something like - * - * char buf[ base64_encoded_len ( len ) + 1 ]; - * - * (the +1 is for the terminating NUL) to provide a buffer of the - * correct size. + * @v raw_len Length of raw data + * @v data Buffer + * @v len Length of buffer + * @ret len Encoded length */ -void base64_encode ( const uint8_t *raw, size_t len, char *encoded ) { +size_t base64_encode ( const void *raw, size_t raw_len, char *data, + size_t len ) { const uint8_t *raw_bytes = ( ( const uint8_t * ) raw ); - uint8_t *encoded_bytes = ( ( uint8_t * ) encoded ); - size_t raw_bit_len = ( 8 * len ); + size_t raw_bit_len = ( 8 * raw_len ); + size_t used = 0; unsigned int bit; unsigned int byte; unsigned int shift; unsigned int tmp; - for ( bit = 0 ; bit < raw_bit_len ; bit += 6 ) { + for ( bit = 0 ; bit < raw_bit_len ; bit += 6, used++ ) { byte = ( bit / 8 ); shift = ( bit % 8 ); tmp = ( raw_bytes[byte] << shift ); - if ( ( byte + 1 ) < len ) + if ( ( byte + 1 ) < raw_len ) tmp |= ( raw_bytes[ byte + 1 ] >> ( 8 - shift ) ); tmp = ( ( tmp >> 2 ) & 0x3f ); - *(encoded_bytes++) = base64[tmp]; + if ( used < len ) + data[used] = base64[tmp]; + } + for ( ; ( bit % 8 ) != 0 ; bit += 6, used++ ) { + if ( used < len ) + data[used] = '='; } - for ( ; ( bit % 8 ) != 0 ; bit += 6 ) - *(encoded_bytes++) = '='; - *(encoded_bytes++) = '\0'; + if ( used < len ) + data[used] = '\0'; + if ( len ) + data[ len - 1 ] = '\0'; /* Ensure terminator exists */ - DBG ( "Base64-encoded to \"%s\":\n", encoded ); - DBG_HDA ( 0, raw, len ); - assert ( strlen ( encoded ) == base64_encoded_len ( len ) ); + return used; } /** * Base64-decode string * * @v encoded Encoded string - * @v raw Raw data - * @ret len Length of raw data, or negative error - * - * The buffer must be large enough to contain the decoded data. Use - * something like - * - * char buf[ base64_decoded_max_len ( encoded ) ]; - * - * to provide a buffer of the correct size. + * @v data Buffer + * @v len Length of buffer + * @ret len Length of data, or negative error */ -int base64_decode ( const char *encoded, uint8_t *raw ) { - const uint8_t *encoded_bytes = ( ( const uint8_t * ) encoded ); - uint8_t *raw_bytes = ( ( uint8_t * ) raw ); - uint8_t encoded_byte; +int base64_decode ( const char *encoded, void *data, size_t len ) { + const char *in = encoded; + uint8_t *out = data; + uint8_t in_char; char *match; - int decoded; + int in_bits; unsigned int bit = 0; unsigned int pad_count = 0; - size_t len; + size_t offset; - /* Zero the raw data */ - memset ( raw, 0, base64_decoded_max_len ( encoded ) ); + /* Zero the output buffer */ + memset ( data, 0, len ); /* Decode string */ - while ( ( encoded_byte = *(encoded_bytes++) ) ) { + while ( ( in_char = *(in++) ) ) { /* Ignore whitespace characters */ - if ( isspace ( encoded_byte ) ) + if ( isspace ( in_char ) ) continue; /* Process pad characters */ - if ( encoded_byte == '=' ) { + if ( in_char == '=' ) { if ( pad_count >= 2 ) { DBG ( "Base64-encoded string \"%s\" has too " "many pad characters\n", encoded ); @@ -129,18 +126,22 @@ int base64_decode ( const char *encoded, uint8_t *raw ) { } /* Process normal characters */ - match = strchr ( base64, encoded_byte ); + match = strchr ( base64, in_char ); if ( ! match ) { DBG ( "Base64-encoded string \"%s\" contains invalid " - "character '%c'\n", encoded, encoded_byte ); + "character '%c'\n", encoded, in_char ); return -EINVAL; } - decoded = ( match - base64 ); + in_bits = ( match - base64 ); /* Add to raw data */ - decoded <<= 2; - raw_bytes[ bit / 8 ] |= ( decoded >> ( bit % 8 ) ); - raw_bytes[ bit / 8 + 1 ] |= ( decoded << ( 8 - ( bit % 8 ) ) ); + in_bits <<= 2; + offset = ( bit / 8 ); + if ( offset < len ) + out[offset] |= ( in_bits >> ( bit % 8 ) ); + offset++; + if ( offset < len ) + out[offset] |= ( in_bits << ( 8 - ( bit % 8 ) ) ); bit += 6; } @@ -150,12 +151,7 @@ int base64_decode ( const char *encoded, uint8_t *raw ) { "%d\n", encoded, bit ); return -EINVAL; } - len = ( bit / 8 ); - - DBG ( "Base64-decoded \"%s\" to:\n", encoded ); - DBG_HDA ( 0, raw, len ); - assert ( len <= base64_decoded_max_len ( encoded ) ); /* Return length in bytes */ - return ( len ); + return ( bit / 8 ); } diff --git a/qemu/roms/ipxe/src/core/basename.c b/qemu/roms/ipxe/src/core/basename.c index b534a7886..f4f929517 100644 --- a/qemu/roms/ipxe/src/core/basename.c +++ b/qemu/roms/ipxe/src/core/basename.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/core/bitmap.c b/qemu/roms/ipxe/src/core/bitmap.c index 0d1152327..2aac33870 100644 --- a/qemu/roms/ipxe/src/core/bitmap.c +++ b/qemu/roms/ipxe/src/core/bitmap.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/blockdev.c b/qemu/roms/ipxe/src/core/blockdev.c index 9d118cb2f..c219d9673 100644 --- a/qemu/roms/ipxe/src/core/blockdev.c +++ b/qemu/roms/ipxe/src/core/blockdev.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/blocktrans.c b/qemu/roms/ipxe/src/core/blocktrans.c new file mode 100644 index 000000000..3f32f9cf8 --- /dev/null +++ b/qemu/roms/ipxe/src/core/blocktrans.c @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * Block device translator + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * Reallocate block device translator data buffer + * + * @v xferbuf Data transfer buffer + * @v len New length (or zero to free buffer) + * @ret rc Return status code + */ +static int blktrans_xferbuf_realloc ( struct xfer_buffer *xferbuf, + size_t len ) { + struct block_translator *blktrans = + container_of ( xferbuf, struct block_translator, xferbuf ); + + /* Record length, if applicable */ + if ( blktrans->buffer ) { + + /* We have a (non-reallocatable) data buffer */ + return -ENOTSUP; + + } else { + + /* Record length (for block device capacity) */ + xferbuf->len = len; + return 0; + } +} + +/** + * Write data to block device translator data buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to copy + * @v len Length of data + */ +static void blktrans_xferbuf_write ( struct xfer_buffer *xferbuf, size_t offset, + const void *data, size_t len ) { + struct block_translator *blktrans = + container_of ( xferbuf, struct block_translator, xferbuf ); + + /* Write data to buffer, if applicable */ + if ( blktrans->buffer ) { + + /* Write data to buffer */ + copy_to_user ( blktrans->buffer, offset, data, len ); + + } else { + + /* Sanity check */ + assert ( len == 0 ); + } +} + +/** + * Read data from block device translator data buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to read + * @v len Length of data + */ +static void blktrans_xferbuf_read ( struct xfer_buffer *xferbuf, size_t offset, + void *data, size_t len ) { + struct block_translator *blktrans = + container_of ( xferbuf, struct block_translator, xferbuf ); + + /* Read data from buffer, if applicable */ + if ( blktrans->buffer ) { + + /* Read data from buffer */ + copy_from_user ( data, blktrans->buffer, offset, len ); + + } else { + + /* Sanity check */ + assert ( len == 0 ); + } +} + +/** Block device translator data transfer buffer operations */ +static struct xfer_buffer_operations blktrans_xferbuf_operations = { + .realloc = blktrans_xferbuf_realloc, + .write = blktrans_xferbuf_write, + .read = blktrans_xferbuf_read, +}; + +/** + * Close block device translator + * + * @v blktrans Block device translator + * @v rc Reason for close + */ +static void blktrans_close ( struct block_translator *blktrans, int rc ) { + struct block_device_capacity capacity; + + /* Report block device capacity, if applicable */ + if ( ( rc == 0 ) && ( blktrans->blksize ) ) { + + /* Construct block device capacity */ + capacity.blocks = + ( blktrans->xferbuf.len / blktrans->blksize ); + capacity.blksize = blktrans->blksize; + capacity.max_count = -1U; + + /* Report block device capacity */ + block_capacity ( &blktrans->block, &capacity ); + } + + /* Shut down interfaces */ + intf_shutdown ( &blktrans->xfer, rc ); + intf_shutdown ( &blktrans->block, rc ); +} + +/** + * Deliver data + * + * @v blktrans Block device translator + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int blktrans_deliver ( struct block_translator *blktrans, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + int rc; + + /* Deliver to buffer */ + if ( ( rc = xferbuf_deliver ( &blktrans->xferbuf, iob_disown ( iobuf ), + meta ) ) != 0 ) { + DBGC ( blktrans, "BLKTRANS %p could not deliver: %s\n", + blktrans, strerror ( rc ) ); + goto err; + } + + return 0; + + err: + blktrans_close ( blktrans, rc ); + return rc; +} + +/** + * Get underlying data transfer buffer + * + * @v blktrans Block device translator + * @ret xferbuf Data transfer buffer + */ +static struct xfer_buffer * +blktrans_buffer ( struct block_translator *blktrans ) { + + return &blktrans->xferbuf; +} + +/** Block device translator block device interface operations */ +static struct interface_operation blktrans_block_operations[] = { + INTF_OP ( intf_close, struct block_translator *, blktrans_close ), +}; + +/** Block device translator block device interface descriptor */ +static struct interface_descriptor blktrans_block_desc = + INTF_DESC_PASSTHRU ( struct block_translator, block, + blktrans_block_operations, xfer ); + +/** Block device translator data transfer interface operations */ +static struct interface_operation blktrans_xfer_operations[] = { + INTF_OP ( xfer_deliver, struct block_translator *, blktrans_deliver ), + INTF_OP ( xfer_buffer, struct block_translator *, blktrans_buffer ), + INTF_OP ( intf_close, struct block_translator *, blktrans_close ), +}; + +/** Block device translator data transfer interface descriptor */ +static struct interface_descriptor blktrans_xfer_desc = + INTF_DESC_PASSTHRU ( struct block_translator, xfer, + blktrans_xfer_operations, block ); + +/** + * Insert block device translator + * + * @v block Block device interface + * @v buffer Data buffer (or UNULL) + * @v size Length of data buffer, or block size + * @ret rc Return status code + */ +int block_translate ( struct interface *block, userptr_t buffer, size_t size ) { + struct block_translator *blktrans; + int rc; + + /* Allocate and initialise structure */ + blktrans = zalloc ( sizeof ( *blktrans ) ); + if ( ! blktrans ) { + rc = -ENOMEM; + goto err_alloc; + } + ref_init ( &blktrans->refcnt, NULL ); + intf_init ( &blktrans->block, &blktrans_block_desc, &blktrans->refcnt ); + intf_init ( &blktrans->xfer, &blktrans_xfer_desc, &blktrans->refcnt ); + blktrans->xferbuf.op = &blktrans_xferbuf_operations; + blktrans->buffer = buffer; + if ( buffer ) { + blktrans->xferbuf.len = size; + } else { + blktrans->blksize = size; + } + + /* Attach to interfaces, mortalise self, and return */ + assert ( block->dest != &null_intf ); + intf_plug_plug ( &blktrans->xfer, block->dest ); + intf_plug_plug ( &blktrans->block, block ); + ref_put ( &blktrans->refcnt ); + + DBGC2 ( blktrans, "BLKTRANS %p created", blktrans ); + if ( buffer ) { + DBGC2 ( blktrans, " for %#lx+%#zx", + user_to_phys ( buffer, 0 ), size ); + } + DBGC2 ( blktrans, "\n" ); + return 0; + + ref_put ( &blktrans->refcnt ); + err_alloc: + return rc; +} diff --git a/qemu/roms/ipxe/src/core/console.c b/qemu/roms/ipxe/src/core/console.c index 141d8f0f0..7fd00036f 100644 --- a/qemu/roms/ipxe/src/core/console.c +++ b/qemu/roms/ipxe/src/core/console.c @@ -5,7 +5,7 @@ /** @file */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Current console usage */ int console_usage = CONSOLE_USAGE_STDOUT; diff --git a/qemu/roms/ipxe/src/core/cpio.c b/qemu/roms/ipxe/src/core/cpio.c index 3a5f4d2b6..080c72daf 100644 --- a/qemu/roms/ipxe/src/core/cpio.c +++ b/qemu/roms/ipxe/src/core/cpio.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/core/ctype.c b/qemu/roms/ipxe/src/core/ctype.c index c812346a0..891af71ea 100644 --- a/qemu/roms/ipxe/src/core/ctype.c +++ b/qemu/roms/ipxe/src/core/ctype.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file @@ -31,11 +35,12 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** * Check to see if character is a space * - * @v c Character + * @v character Character * @ret isspace Character is a space */ -int isspace ( int c ) { - switch ( c ) { +int isspace ( int character ) { + + switch ( character ) { case ' ' : case '\f' : case '\n' : diff --git a/qemu/roms/ipxe/src/core/cwuri.c b/qemu/roms/ipxe/src/core/cwuri.c index 5865552a0..612f0b179 100644 --- a/qemu/roms/ipxe/src/core/cwuri.c +++ b/qemu/roms/ipxe/src/core/cwuri.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/debug.c b/qemu/roms/ipxe/src/core/debug.c index 7ded47089..def5d8b09 100644 --- a/qemu/roms/ipxe/src/core/debug.c +++ b/qemu/roms/ipxe/src/core/debug.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/debug_md5.c b/qemu/roms/ipxe/src/core/debug_md5.c index f049ac757..d0dbad9ed 100644 --- a/qemu/roms/ipxe/src/core/debug_md5.c +++ b/qemu/roms/ipxe/src/core/debug_md5.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/device.c b/qemu/roms/ipxe/src/core/device.c index 330f95c5a..77d7b719b 100644 --- a/qemu/roms/ipxe/src/core/device.c +++ b/qemu/roms/ipxe/src/core/device.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/downloader.c b/qemu/roms/ipxe/src/core/downloader.c index ec69db6b1..d745f3617 100644 --- a/qemu/roms/ipxe/src/core/downloader.c +++ b/qemu/roms/ipxe/src/core/downloader.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -29,7 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include -#include +#include #include /** @file @@ -38,14 +42,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -/** Receive profiler */ -static struct profiler downloader_rx_profiler __profiler = - { .name = "downloader.rx" }; - -/** Data copy profiler */ -static struct profiler downloader_copy_profiler __profiler = - { .name = "downloader.copy" }; - /** A downloader */ struct downloader { /** Reference count for this object */ @@ -58,8 +54,8 @@ struct downloader { /** Image to contain downloaded file */ struct image *image; - /** Current position within image buffer */ - size_t pos; + /** Data transfer buffer */ + struct xfer_buffer buffer; }; /** @@ -92,42 +88,14 @@ static void downloader_finished ( struct downloader *downloader, int rc ) { downloader->image->name, strerror ( rc ) ); } + /* Update image length */ + downloader->image->len = downloader->buffer.len; + /* Shut down interfaces */ intf_shutdown ( &downloader->xfer, rc ); intf_shutdown ( &downloader->job, rc ); } -/** - * Ensure that download buffer is large enough for the specified size - * - * @v downloader Downloader - * @v len Required minimum size - * @ret rc Return status code - */ -static int downloader_ensure_size ( struct downloader *downloader, - size_t len ) { - userptr_t new_buffer; - - /* If buffer is already large enough, do nothing */ - if ( len <= downloader->image->len ) - return 0; - - DBGC ( downloader, "Downloader %p extending to %zd bytes\n", - downloader, len ); - - /* Extend buffer */ - new_buffer = urealloc ( downloader->image->data, len ); - if ( ! new_buffer ) { - DBGC ( downloader, "Downloader %p could not extend buffer to " - "%zd bytes\n", downloader, len ); - return -ENOSPC; - } - downloader->image->data = new_buffer; - downloader->image->len = len; - - return 0; -} - /**************************************************************************** * * Job control interface @@ -148,8 +116,8 @@ static int downloader_progress ( struct downloader *downloader, * arrive out of order (e.g. with multicast protocols), but * it's a reasonable first approximation. */ - progress->completed = downloader->pos; - progress->total = downloader->image->len; + progress->completed = downloader->buffer.pos; + progress->total = downloader->buffer.len; return 0; } @@ -171,44 +139,37 @@ static int downloader_progress ( struct downloader *downloader, static int downloader_xfer_deliver ( struct downloader *downloader, struct io_buffer *iobuf, struct xfer_metadata *meta ) { - size_t len; - size_t max; int rc; - /* Start profiling */ - profile_start ( &downloader_rx_profiler ); - - /* Calculate new buffer position */ - if ( meta->flags & XFER_FL_ABS_OFFSET ) - downloader->pos = 0; - downloader->pos += meta->offset; - - /* Ensure that we have enough buffer space for this data */ - len = iob_len ( iobuf ); - max = ( downloader->pos + len ); - if ( ( rc = downloader_ensure_size ( downloader, max ) ) != 0 ) - goto done; - - /* Copy data to buffer */ - profile_start ( &downloader_copy_profiler ); - copy_to_user ( downloader->image->data, downloader->pos, - iobuf->data, len ); - profile_stop ( &downloader_copy_profiler ); - - /* Update current buffer position */ - downloader->pos += len; - - done: - free_iob ( iobuf ); - if ( rc != 0 ) - downloader_finished ( downloader, rc ); - profile_stop ( &downloader_rx_profiler ); + /* Add data to buffer */ + if ( ( rc = xferbuf_deliver ( &downloader->buffer, iob_disown ( iobuf ), + meta ) ) != 0 ) + goto err_deliver; + + return 0; + + err_deliver: + downloader_finished ( downloader, rc ); return rc; } +/** + * Get underlying data transfer buffer + * + * @v downloader Downloader + * @ret xferbuf Data transfer buffer, or NULL on error + */ +static struct xfer_buffer * +downloader_xfer_buffer ( struct downloader *downloader ) { + + /* Provide direct access to underlying data transfer buffer */ + return &downloader->buffer; +} + /** Downloader data transfer interface operations */ static struct interface_operation downloader_xfer_operations[] = { INTF_OP ( xfer_deliver, struct downloader *, downloader_xfer_deliver ), + INTF_OP ( xfer_buffer, struct downloader *, downloader_xfer_buffer ), INTF_OP ( intf_close, struct downloader *, downloader_finished ), }; @@ -262,6 +223,7 @@ int create_downloader ( struct interface *job, struct image *image ) { intf_init ( &downloader->xfer, &downloader_xfer_desc, &downloader->refcnt ); downloader->image = image_get ( image ); + xferbuf_umalloc_init ( &downloader->buffer, &image->data ); /* Instantiate child objects and attach to our interfaces */ if ( ( rc = xfer_open_uri ( &downloader->xfer, image->uri ) ) != 0 ) diff --git a/qemu/roms/ipxe/src/core/edd.c b/qemu/roms/ipxe/src/core/edd.c index d574ea6c0..a50b74ab1 100644 --- a/qemu/roms/ipxe/src/core/edd.c +++ b/qemu/roms/ipxe/src/core/edd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/errno.c b/qemu/roms/ipxe/src/core/errno.c index 06905561f..5de15bb92 100644 --- a/qemu/roms/ipxe/src/core/errno.c +++ b/qemu/roms/ipxe/src/core/errno.c @@ -1,6 +1,6 @@ #include -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/core/exec.c b/qemu/roms/ipxe/src/core/exec.c index 1c85705ae..2c2ade0a5 100644 --- a/qemu/roms/ipxe/src/core/exec.c +++ b/qemu/roms/ipxe/src/core/exec.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/fault.c b/qemu/roms/ipxe/src/core/fault.c new file mode 100644 index 000000000..63d3ccacf --- /dev/null +++ b/qemu/roms/ipxe/src/core/fault.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** @file + * + * Fault injection + * + */ + +/** + * Inject fault with a specified probability + * + * @v rate Reciprocal of fault probability (must be non-zero) + * @ret rc Return status code + */ +int inject_fault_nonzero ( unsigned int rate ) { + + /* Do nothing unless we want to inject a fault now */ + if ( ( random() % rate ) != 0 ) + return 0; + + /* Generate error number here so that faults can be injected + * into files that don't themselves have error file + * identifiers (via errfile.h). + */ + return -EFAULT; +} + +/** + * Corrupt data with a specified probability + * + * @v rate Reciprocal of fault probability (must be non-zero) + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ +void inject_corruption_nonzero ( unsigned int rate, const void *data, + size_t len ) { + uint8_t *writable; + size_t offset; + + /* Do nothing if we have no data to corrupt */ + if ( ! len ) + return; + + /* Do nothing unless we want to inject a fault now */ + if ( ! inject_fault_nonzero ( rate ) ) + return; + + /* Get a writable pointer to the nominally read-only data */ + writable = ( ( uint8_t * ) data ); + + /* Pick a random victim byte and zap it */ + offset = ( random() % len ); + writable[offset] ^= random(); +} diff --git a/qemu/roms/ipxe/src/core/fbcon.c b/qemu/roms/ipxe/src/core/fbcon.c index 72d6a6789..6d8b0086d 100644 --- a/qemu/roms/ipxe/src/core/fbcon.c +++ b/qemu/roms/ipxe/src/core/fbcon.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/core/fnrec.c b/qemu/roms/ipxe/src/core/fnrec.c index 3453c8b6a..0430817f8 100644 --- a/qemu/roms/ipxe/src/core/fnrec.c +++ b/qemu/roms/ipxe/src/core/fnrec.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/gdbserial.c b/qemu/roms/ipxe/src/core/gdbserial.c index 6f78c88bf..0983f2557 100644 --- a/qemu/roms/ipxe/src/core/gdbserial.c +++ b/qemu/roms/ipxe/src/core/gdbserial.c @@ -15,35 +15,105 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include +#include +#include #include -#include +#include #include #include +#include + +/* UART port number */ +#ifdef COMCONSOLE +#define GDBSERIAL_PORT COMCONSOLE +#else +#define GDBSERIAL_PORT 0 +#endif + +/* UART baud rate */ +#ifdef COMPRESERVE +#define GDBSERIAL_BAUD 0 +#else +#define GDBSERIAL_BAUD COMSPEED +#endif + +/* UART line control register value */ +#ifdef COMPRESERVE +#define GDBSERIAL_LCR 0 +#else +#define GDBSERIAL_LCR UART_LCR_WPS ( COMDATA, COMPARITY, COMSTOP ) +#endif + +/** GDB serial UART */ +static struct uart gdbserial_uart; struct gdb_transport serial_gdb_transport __gdb_transport; static size_t gdbserial_recv ( char *buf, size_t len ) { + assert ( len > 0 ); - buf [ 0 ] = serial_getc(); + while ( ! uart_data_ready ( &gdbserial_uart ) ) {} + buf[0] = uart_receive ( &gdbserial_uart ); return 1; } static void gdbserial_send ( const char *buf, size_t len ) { + while ( len-- > 0 ) { - serial_putc ( *buf++ ); + uart_transmit ( &gdbserial_uart, *buf++ ); } } +static int gdbserial_init ( int argc, char **argv ) { + unsigned int port; + char *endp; + + if ( argc == 0 ) { + port = GDBSERIAL_PORT; + } else if ( argc == 1 ) { + port = strtoul ( argv[0], &endp, 10 ); + if ( *endp ) { + printf ( "serial: invalid port\n" ); + return 1; + } + } else { + printf ( "serial: syntax \n" ); + return 1; + } + + if ( ! gdbserial_configure ( port, GDBSERIAL_BAUD, GDBSERIAL_LCR ) ) { + printf ( "serial: unable to configure\n" ); + return 1; + } + + return 0; +} + struct gdb_transport serial_gdb_transport __gdb_transport = { .name = "serial", + .init = gdbserial_init, .recv = gdbserial_recv, .send = gdbserial_send, }; -struct gdb_transport *gdbserial_configure ( void ) { +struct gdb_transport * gdbserial_configure ( unsigned int port, + unsigned int baud, uint8_t lcr ) { + int rc; + + if ( ( rc = uart_select ( &gdbserial_uart, port ) ) != 0 ) + return NULL; + + if ( ( rc = uart_init ( &gdbserial_uart, baud, lcr ) ) != 0 ) + return NULL; + return &serial_gdb_transport; } diff --git a/qemu/roms/ipxe/src/core/gdbstub.c b/qemu/roms/ipxe/src/core/gdbstub.c index af06118b2..6ad52d1a6 100644 --- a/qemu/roms/ipxe/src/core/gdbstub.c +++ b/qemu/roms/ipxe/src/core/gdbstub.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/core/gdbudp.c b/qemu/roms/ipxe/src/core/gdbudp.c index 5977547c8..e4613d137 100644 --- a/qemu/roms/ipxe/src/core/gdbudp.c +++ b/qemu/roms/ipxe/src/core/gdbudp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/getkey.c b/qemu/roms/ipxe/src/core/getkey.c index d69cfb44b..0f0f8b7c3 100644 --- a/qemu/roms/ipxe/src/core/getkey.c +++ b/qemu/roms/ipxe/src/core/getkey.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/getopt.c b/qemu/roms/ipxe/src/core/getopt.c index abc1edd6c..e6c3948d1 100644 --- a/qemu/roms/ipxe/src/core/getopt.c +++ b/qemu/roms/ipxe/src/core/getopt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/image.c b/qemu/roms/ipxe/src/core/image.c index ec4480238..529e3d72c 100644 --- a/qemu/roms/ipxe/src/core/image.c +++ b/qemu/roms/ipxe/src/core/image.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -153,6 +157,32 @@ int image_set_cmdline ( struct image *image, const char *cmdline ) { return 0; } +/** + * Determine image type + * + * @v image Executable image + * @ret rc Return status code + */ +static int image_probe ( struct image *image ) { + struct image_type *type; + int rc; + + /* Try each type in turn */ + for_each_table_entry ( type, IMAGE_TYPES ) { + if ( ( rc = type->probe ( image ) ) == 0 ) { + image->type = type; + DBGC ( image, "IMAGE %s is %s\n", + image->name, type->name ); + break; + } + DBGC ( image, "IMAGE %s is not %s: %s\n", image->name, + type->name, strerror ( rc ) ); + } + + DBGC ( image, "IMAGE %s format not recognised\n", image->name ); + return -ENOTSUP; +} + /** * Register executable image * @@ -185,6 +215,14 @@ int register_image ( struct image *image ) { image->name, user_to_phys ( image->data, 0 ), user_to_phys ( image->data, image->len ) ); + /* Try to detect image type, if applicable. Ignore failures, + * since we expect to handle some unrecognised images + * (e.g. kernel initrds, multiboot modules, random files + * provided via our EFI virtual filesystem, etc). + */ + if ( ! image->type ) + image_probe ( image ); + return 0; } @@ -222,36 +260,6 @@ struct image * find_image ( const char *name ) { return NULL; } -/** - * Determine image type - * - * @v image Executable image - * @ret rc Return status code - */ -int image_probe ( struct image *image ) { - struct image_type *type; - int rc; - - /* Succeed if we already have a type */ - if ( image->type ) - return 0; - - /* Try each type in turn */ - for_each_table_entry ( type, IMAGE_TYPES ) { - if ( ( rc = type->probe ( image ) ) == 0 ) { - image->type = type; - DBGC ( image, "IMAGE %s is %s\n", - image->name, type->name ); - return 0; - } - DBGC ( image, "IMAGE %s is not %s: %s\n", image->name, - type->name, strerror ( rc ) ); - } - - DBGC ( image, "IMAGE %s format not recognised\n", image->name ); - return -ENOEXEC; -} - /** * Execute image * @@ -284,9 +292,11 @@ int image_exec ( struct image *image ) { */ current_image = image_get ( image ); - /* Check that this image can be selected for execution */ - if ( ( rc = image_select ( image ) ) != 0 ) + /* Check that this image can be executed */ + if ( ! ( image->type && image->type->exec ) ) { + rc = -ENOEXEC; goto err; + } /* Check that image is trusted (if applicable) */ if ( require_trusted_images && ! ( image->flags & IMAGE_TRUSTED ) ) { @@ -378,8 +388,8 @@ int image_replace ( struct image *replacement ) { } /* Check that the replacement image can be executed */ - if ( ( rc = image_probe ( replacement ) ) != 0 ) - return rc; + if ( ! ( replacement->type && replacement->type->exec ) ) + return -ENOEXEC; /* Clear any existing replacement */ image_put ( image->replacement ); @@ -400,16 +410,13 @@ int image_replace ( struct image *replacement ) { */ int image_select ( struct image *image ) { struct image *tmp; - int rc; /* Unselect all other images */ for_each_image ( tmp ) tmp->flags &= ~IMAGE_SELECTED; /* Check that this image can be executed */ - if ( ( rc = image_probe ( image ) ) != 0 ) - return rc; - if ( ! image->type->exec ) + if ( ! ( image->type && image->type->exec ) ) return -ENOEXEC; /* Mark image as selected */ @@ -468,9 +475,7 @@ int image_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) { int rc; /* Check that this image can be used to create a pixel buffer */ - if ( ( rc = image_probe ( image ) ) != 0 ) - return rc; - if ( ! image->type->pixbuf ) + if ( ! ( image->type && image->type->pixbuf ) ) return -ENOTSUP; /* Try creating pixel buffer */ diff --git a/qemu/roms/ipxe/src/core/init.c b/qemu/roms/ipxe/src/core/init.c index 7ea0730fa..d91e44669 100644 --- a/qemu/roms/ipxe/src/core/init.c +++ b/qemu/roms/ipxe/src/core/init.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/interface.c b/qemu/roms/ipxe/src/core/interface.c index 62f4621db..ba148c13d 100644 --- a/qemu/roms/ipxe/src/core/interface.c +++ b/qemu/roms/ipxe/src/core/interface.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -307,3 +311,28 @@ void intf_restart ( struct interface *intf, int rc ) { */ intf->desc = desc; } + +/** + * Poke an object interface + * + * @v intf Object interface + * @v type Operation type + * + * This is a helper function to implement methods which take no + * parameters and return nothing. + */ +void intf_poke ( struct interface *intf, + void ( type ) ( struct interface *intf ) ) { + struct interface *dest; + intf_poke_TYPE ( void * ) *op = + intf_get_dest_op_untyped ( intf, type, &dest ); + void *object = intf_object ( dest ); + + if ( op ) { + op ( object ); + } else { + /* Default is to do nothing */ + } + + intf_put ( dest ); +} diff --git a/qemu/roms/ipxe/src/core/iobuf.c b/qemu/roms/ipxe/src/core/iobuf.c index afc91d150..3e52ada4f 100644 --- a/qemu/roms/ipxe/src/core/iobuf.c +++ b/qemu/roms/ipxe/src/core/iobuf.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -200,3 +204,33 @@ struct io_buffer * iob_concatenate ( struct list_head *list ) { return concatenated; } + +/** + * Split I/O buffer + * + * @v iobuf I/O buffer + * @v len Length to split into a new I/O buffer + * @ret split New I/O buffer, or NULL on allocation failure + * + * Split the first @c len bytes of the existing I/O buffer into a + * separate I/O buffer. The resulting buffers are likely to have no + * headroom or tailroom. + * + * If this call fails, then the original buffer will be unmodified. + */ +struct io_buffer * iob_split ( struct io_buffer *iobuf, size_t len ) { + struct io_buffer *split; + + /* Sanity checks */ + assert ( len <= iob_len ( iobuf ) ); + + /* Allocate new I/O buffer */ + split = alloc_iob ( len ); + if ( ! split ) + return NULL; + + /* Copy in data */ + memcpy ( iob_put ( split, len ), iobuf->data, len ); + iob_pull ( iobuf, len ); + return split; +} diff --git a/qemu/roms/ipxe/src/core/isqrt.c b/qemu/roms/ipxe/src/core/isqrt.c index 35c918d19..c4d0571e7 100644 --- a/qemu/roms/ipxe/src/core/isqrt.c +++ b/qemu/roms/ipxe/src/core/isqrt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/core/job.c b/qemu/roms/ipxe/src/core/job.c index 674bec8b5..65df80056 100644 --- a/qemu/roms/ipxe/src/core/job.c +++ b/qemu/roms/ipxe/src/core/job.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/linebuf.c b/qemu/roms/ipxe/src/core/linebuf.c index 8fb2f86a7..c197e383c 100644 --- a/qemu/roms/ipxe/src/core/linebuf.c +++ b/qemu/roms/ipxe/src/core/linebuf.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file @@ -39,7 +43,18 @@ FILE_LICENCE ( GPL2_OR_LATER ); * @ret line Buffered line, or NULL if no line ready to read */ char * buffered_line ( struct line_buffer *linebuf ) { - return ( linebuf->ready ? linebuf->data : NULL ); + char *line = &linebuf->data[ linebuf->len ]; + + /* Fail unless we have a newly completed line to retrieve */ + if ( ( linebuf->len == 0 ) || ( linebuf->consumed == 0 ) || + ( *(--line) != '\0' ) ) + return NULL; + + /* Identify start of line */ + while ( ( line > linebuf->data ) && ( line[-1] != '\0' ) ) + line--; + + return line; } /** @@ -48,10 +63,11 @@ char * buffered_line ( struct line_buffer *linebuf ) { * @v linebuf Line buffer */ void empty_line_buffer ( struct line_buffer *linebuf ) { + free ( linebuf->data ); linebuf->data = NULL; linebuf->len = 0; - linebuf->ready = 0; + linebuf->consumed = 0; } /** @@ -72,16 +88,13 @@ void empty_line_buffer ( struct line_buffer *linebuf ) { * should call empty_line_buffer() before freeing a @c struct @c * line_buffer. */ -ssize_t line_buffer ( struct line_buffer *linebuf, - const char *data, size_t len ) { +int line_buffer ( struct line_buffer *linebuf, const char *data, size_t len ) { const char *eol; size_t consume; size_t new_len; char *new_data; - - /* Free any completed line from previous iteration */ - if ( linebuf->ready ) - empty_line_buffer ( linebuf ); + char *lf; + char *cr; /* Search for line terminator */ if ( ( eol = memchr ( data, '\n', len ) ) ) { @@ -90,6 +103,10 @@ ssize_t line_buffer ( struct line_buffer *linebuf, consume = len; } + /* Reject any embedded NULs within the data to be consumed */ + if ( memchr ( data, '\0', consume ) ) + return -EINVAL; + /* Reallocate data buffer and copy in new data */ new_len = ( linebuf->len + consume ); new_data = realloc ( linebuf->data, ( new_len + 1 ) ); @@ -100,13 +117,27 @@ ssize_t line_buffer ( struct line_buffer *linebuf, linebuf->data = new_data; linebuf->len = new_len; - /* If we have reached end of line, trim the line and mark as ready */ + /* If we have reached end of line, terminate the line */ if ( eol ) { - linebuf->data[--linebuf->len] = '\0'; /* trim NL */ - if ( linebuf->data[linebuf->len - 1] == '\r' ) - linebuf->data[--linebuf->len] = '\0'; /* trim CR */ - linebuf->ready = 1; + + /* Overwrite trailing LF (which must exist at this point) */ + assert ( linebuf->len > 0 ); + lf = &linebuf->data[ linebuf->len - 1 ]; + assert ( *lf == '\n' ); + *lf = '\0'; + + /* Trim (and overwrite) trailing CR, if present */ + if ( linebuf->len > 1 ) { + cr = ( lf - 1 ); + if ( *cr == '\r' ) { + linebuf->len--; + *cr = '\0'; + } + } } + /* Record consumed length */ + linebuf->consumed = consume; + return consume; } diff --git a/qemu/roms/ipxe/src/core/lineconsole.c b/qemu/roms/ipxe/src/core/lineconsole.c index 1b6791cf3..bb3bfafc9 100644 --- a/qemu/roms/ipxe/src/core/lineconsole.c +++ b/qemu/roms/ipxe/src/core/lineconsole.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/core/list.c b/qemu/roms/ipxe/src/core/list.c index 77579d69a..5175c84ec 100644 --- a/qemu/roms/ipxe/src/core/list.c +++ b/qemu/roms/ipxe/src/core/list.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/core/log.c b/qemu/roms/ipxe/src/core/log.c index f160b4fc8..c08e4bb9b 100644 --- a/qemu/roms/ipxe/src/core/log.c +++ b/qemu/roms/ipxe/src/core/log.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/core/main.c b/qemu/roms/ipxe/src/core/main.c index db09e4c39..638dea9cf 100644 --- a/qemu/roms/ipxe/src/core/main.c +++ b/qemu/roms/ipxe/src/core/main.c @@ -12,7 +12,7 @@ Literature dealing with the network protocols: **************************************************************************/ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * @ret rc Return status code */ __asmcall int main ( void ) { + int rc; /* Perform one-time-only initialisation (e.g. heap) */ initialise(); @@ -35,9 +36,11 @@ __asmcall int main ( void ) { startup(); printf ( "ok\n" ); - ipxe ( NULL ); + /* Attempt to boot */ + if ( ( rc = ipxe ( NULL ) ) != 0 ) + goto err_ipxe; + err_ipxe: shutdown_exit(); - - return 0; + return rc; } diff --git a/qemu/roms/ipxe/src/core/malloc.c b/qemu/roms/ipxe/src/core/malloc.c index d9c07495d..b120c0325 100644 --- a/qemu/roms/ipxe/src/core/malloc.c +++ b/qemu/roms/ipxe/src/core/malloc.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -106,6 +110,7 @@ static char heap[HEAP_SIZE] __attribute__ (( aligned ( __alignof__(void *) ))); static inline void valgrind_make_blocks_defined ( void ) { struct memory_block *block; + /* Do nothing unless running under Valgrind */ if ( RUNNING_ON_VALGRIND <= 0 ) return; @@ -147,6 +152,7 @@ static inline void valgrind_make_blocks_noaccess ( void ) { struct memory_block *block; struct memory_block *prev = NULL; + /* Do nothing unless running under Valgrind */ if ( RUNNING_ON_VALGRIND <= 0 ) return; @@ -267,24 +273,25 @@ static void discard_all_cache ( void ) { void * alloc_memblock ( size_t size, size_t align, size_t offset ) { struct memory_block *block; size_t align_mask; + size_t actual_size; size_t pre_size; ssize_t post_size; struct memory_block *pre; struct memory_block *post; - struct memory_block *ptr; + void *ptr; /* Sanity checks */ assert ( size != 0 ); assert ( ( align == 0 ) || ( ( align & ( align - 1 ) ) == 0 ) ); - valgrind_make_blocks_defined(); check_blocks(); /* Round up size to multiple of MIN_MEMBLOCK_SIZE and * calculate alignment mask. */ - size = ( size + MIN_MEMBLOCK_SIZE - 1 ) & ~( MIN_MEMBLOCK_SIZE - 1 ); - align_mask = ( align - 1 ) | ( MIN_MEMBLOCK_SIZE - 1 ); + actual_size = ( ( size + MIN_MEMBLOCK_SIZE - 1 ) & + ~( MIN_MEMBLOCK_SIZE - 1 ) ); + align_mask = ( ( align - 1 ) | ( MIN_MEMBLOCK_SIZE - 1 ) ); DBGC2 ( &heap, "Allocating %#zx (aligned %#zx+%zx)\n", size, align, offset ); @@ -293,7 +300,7 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { list_for_each_entry ( block, &free_blocks, list ) { pre_size = ( ( offset - virt_to_phys ( block ) ) & align_mask ); - post_size = ( block->size - pre_size - size ); + post_size = ( block->size - pre_size - actual_size ); if ( post_size >= 0 ) { /* Split block into pre-block, block, and * post-block. After this split, the "pre" @@ -302,7 +309,7 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { */ pre = block; block = ( ( ( void * ) pre ) + pre_size ); - post = ( ( ( void * ) block ) + size ); + post = ( ( ( void * ) block ) + actual_size ); DBGC2 ( &heap, "[%p,%p) -> [%p,%p) + [%p,%p)\n", pre, ( ( ( void * ) pre ) + pre->size ), pre, block, post, @@ -313,8 +320,8 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { * the heap). */ if ( (size_t) post_size >= MIN_MEMBLOCK_SIZE ) { - VALGRIND_MAKE_MEM_DEFINED ( post, - sizeof ( *post ) ); + VALGRIND_MAKE_MEM_UNDEFINED + ( post, sizeof ( *post ) ); post->size = post_size; list_add ( &post->list, &pre->list ); } @@ -328,14 +335,18 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { * it is too small, which can happen only at * the very start of the heap. */ - if ( pre_size < MIN_MEMBLOCK_SIZE ) + if ( pre_size < MIN_MEMBLOCK_SIZE ) { list_del ( &pre->list ); + VALGRIND_MAKE_MEM_NOACCESS + ( pre, sizeof ( *pre ) ); + } /* Update total free memory */ - freemem -= size; + freemem -= actual_size; /* Return allocated block */ DBGC2 ( &heap, "Allocated [%p,%p)\n", block, ( ( ( void * ) block ) + size ) ); ptr = block; + VALGRIND_MAKE_MEM_UNDEFINED ( ptr, size ); goto done; } } @@ -368,13 +379,16 @@ void free_memblock ( void *ptr, size_t size ) { struct memory_block *freeing; struct memory_block *block; struct memory_block *tmp; + size_t actual_size; ssize_t gap_before; ssize_t gap_after = -1; /* Allow for ptr==NULL */ if ( ! ptr ) return; + VALGRIND_MAKE_MEM_NOACCESS ( ptr, size ); + /* Sanity checks */ valgrind_make_blocks_defined(); check_blocks(); @@ -382,9 +396,10 @@ void free_memblock ( void *ptr, size_t size ) { * would have used. */ assert ( size != 0 ); - size = ( size + MIN_MEMBLOCK_SIZE - 1 ) & ~( MIN_MEMBLOCK_SIZE - 1 ); + actual_size = ( ( size + MIN_MEMBLOCK_SIZE - 1 ) & + ~( MIN_MEMBLOCK_SIZE - 1 ) ); freeing = ptr; - VALGRIND_MAKE_MEM_DEFINED ( freeing, sizeof ( *freeing ) ); + VALGRIND_MAKE_MEM_UNDEFINED ( freeing, sizeof ( *freeing ) ); DBGC2 ( &heap, "Freeing [%p,%p)\n", freeing, ( ( ( void * ) freeing ) + size ) ); @@ -392,7 +407,7 @@ void free_memblock ( void *ptr, size_t size ) { if ( ASSERTING ) { list_for_each_entry ( block, &free_blocks, list ) { if ( ( ( ( void * ) block ) < - ( ( void * ) freeing + size ) ) && + ( ( void * ) freeing + actual_size ) ) && ( ( void * ) freeing < ( ( void * ) block + block->size ) ) ) { assert ( 0 ); @@ -407,7 +422,7 @@ void free_memblock ( void *ptr, size_t size ) { } /* Insert/merge into free list */ - freeing->size = size; + freeing->size = actual_size; list_for_each_entry_safe ( block, tmp, &free_blocks, list ) { /* Calculate gaps before and after the "freeing" block */ gap_before = ( ( ( void * ) freeing ) - @@ -421,8 +436,10 @@ void free_memblock ( void *ptr, size_t size ) { ( ( ( void * ) freeing ) + freeing->size ), block, ( ( ( void * ) freeing ) + freeing->size ) ); - block->size += size; + block->size += actual_size; list_del ( &block->list ); + VALGRIND_MAKE_MEM_NOACCESS ( freeing, + sizeof ( *freeing ) ); freeing = block; } /* Stop processing as soon as we reach a following block */ @@ -444,10 +461,11 @@ void free_memblock ( void *ptr, size_t size ) { ( ( ( void * ) block ) + block->size ) ); freeing->size += block->size; list_del ( &block->list ); + VALGRIND_MAKE_MEM_NOACCESS ( block, sizeof ( *block ) ); } /* Update free memory counter */ - freemem += size; + freemem += actual_size; check_blocks(); valgrind_make_blocks_noaccess(); @@ -490,9 +508,9 @@ void * realloc ( void *old_ptr, size_t new_size ) { new_block = alloc_memblock ( new_total_size, 1, 0 ); if ( ! new_block ) return NULL; - VALGRIND_MAKE_MEM_UNDEFINED ( new_block, offsetof ( struct autosized_block, data ) ); new_block->size = new_total_size; - VALGRIND_MAKE_MEM_NOACCESS ( new_block, offsetof ( struct autosized_block, data ) ); + VALGRIND_MAKE_MEM_NOACCESS ( &new_block->size, + sizeof ( new_block->size ) ); new_ptr = &new_block->data; VALGRIND_MALLOCLIKE_BLOCK ( new_ptr, new_size, 0, 0 ); } @@ -505,16 +523,16 @@ void * realloc ( void *old_ptr, size_t new_size ) { if ( old_ptr && ( old_ptr != NOWHERE ) ) { old_block = container_of ( old_ptr, struct autosized_block, data ); - VALGRIND_MAKE_MEM_DEFINED ( old_block, offsetof ( struct autosized_block, data ) ); + VALGRIND_MAKE_MEM_DEFINED ( &old_block->size, + sizeof ( old_block->size ) ); old_total_size = old_block->size; assert ( old_total_size != 0 ); old_size = ( old_total_size - offsetof ( struct autosized_block, data ) ); memcpy ( new_ptr, old_ptr, ( ( old_size < new_size ) ? old_size : new_size ) ); - free_memblock ( old_block, old_total_size ); - VALGRIND_MAKE_MEM_NOACCESS ( old_block, offsetof ( struct autosized_block, data ) ); VALGRIND_FREELIKE_BLOCK ( old_ptr, 0 ); + free_memblock ( old_block, old_total_size ); } if ( ASSERTED ) { @@ -611,6 +629,7 @@ void mpopulate ( void *start, size_t len ) { */ static void init_heap ( void ) { VALGRIND_MAKE_MEM_NOACCESS ( heap, sizeof ( heap ) ); + VALGRIND_MAKE_MEM_NOACCESS ( &free_blocks, sizeof ( free_blocks ) ); mpopulate ( heap, sizeof ( heap ) ); } diff --git a/qemu/roms/ipxe/src/core/memblock.c b/qemu/roms/ipxe/src/core/memblock.c index 1fd89b871..aecddc22c 100644 --- a/qemu/roms/ipxe/src/core/memblock.c +++ b/qemu/roms/ipxe/src/core/memblock.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/core/memmap_settings.c b/qemu/roms/ipxe/src/core/memmap_settings.c index 0f6d0abf5..fab3e5f3a 100644 --- a/qemu/roms/ipxe/src/core/memmap_settings.c +++ b/qemu/roms/ipxe/src/core/memmap_settings.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/menu.c b/qemu/roms/ipxe/src/core/menu.c index 8d42e1f83..ab5b0c7f5 100644 --- a/qemu/roms/ipxe/src/core/menu.c +++ b/qemu/roms/ipxe/src/core/menu.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/core/misc.c b/qemu/roms/ipxe/src/core/misc.c deleted file mode 100644 index eaceddfea..000000000 --- a/qemu/roms/ipxe/src/core/misc.c +++ /dev/null @@ -1,85 +0,0 @@ -/************************************************************************** -MISC Support Routines -**************************************************************************/ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include -#include -#include -#include -#include - -/************************************************************************** -INET_ATON - Convert an ascii x.x.x.x to binary form -**************************************************************************/ -int inet_aton ( const char *cp, struct in_addr *inp ) { - const char *p = cp; - const char *digits_start; - unsigned long ip = 0; - unsigned long val; - int j; - for(j = 0; j <= 3; j++) { - digits_start = p; - val = strtoul(p, ( char ** ) &p, 10); - if ((p == digits_start) || (val > 255)) return 0; - if ( ( j < 3 ) && ( *(p++) != '.' ) ) return 0; - ip = (ip << 8) | val; - } - if ( *p == '\0' ) { - inp->s_addr = htonl(ip); - return 1; - } - return 0; -} - -unsigned int strtoul_charval ( unsigned int charval ) { - - if ( charval >= 'a' ) { - charval = ( charval - 'a' + 10 ); - } else if ( charval >= 'A' ) { - charval = ( charval - 'A' + 10 ); - } else if ( charval <= '9' ) { - charval = ( charval - '0' ); - } - - return charval; -} - -unsigned long strtoul ( const char *p, char **endp, int base ) { - unsigned long ret = 0; - int negative = 0; - unsigned int charval; - - while ( isspace ( *p ) ) - p++; - - if ( *p == '-' ) { - negative = 1; - p++; - } - - base = strtoul_base ( &p, base ); - - while ( 1 ) { - charval = strtoul_charval ( *p ); - if ( charval >= ( unsigned int ) base ) - break; - ret = ( ( ret * base ) + charval ); - p++; - } - - if ( negative ) - ret = -ret; - - if ( endp ) - *endp = ( char * ) p; - - return ( ret ); -} - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/qemu/roms/ipxe/src/core/monojob.c b/qemu/roms/ipxe/src/core/monojob.c index 820fa31dc..817f21b2c 100644 --- a/qemu/roms/ipxe/src/core/monojob.c +++ b/qemu/roms/ipxe/src/core/monojob.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/null_reboot.c b/qemu/roms/ipxe/src/core/null_reboot.c index a3d5b2ef8..7be5612a3 100644 --- a/qemu/roms/ipxe/src/core/null_reboot.c +++ b/qemu/roms/ipxe/src/core/null_reboot.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/core/null_sanboot.c b/qemu/roms/ipxe/src/core/null_sanboot.c index 18c0dea84..2f7522c6c 100644 --- a/qemu/roms/ipxe/src/core/null_sanboot.c +++ b/qemu/roms/ipxe/src/core/null_sanboot.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/null_time.c b/qemu/roms/ipxe/src/core/null_time.c index 506c70b52..90041a456 100644 --- a/qemu/roms/ipxe/src/core/null_time.c +++ b/qemu/roms/ipxe/src/core/null_time.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/core/nvo.c b/qemu/roms/ipxe/src/core/nvo.c index e135d2b41..d2c9b5e73 100644 --- a/qemu/roms/ipxe/src/core/nvo.c +++ b/qemu/roms/ipxe/src/core/nvo.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/open.c b/qemu/roms/ipxe/src/core/open.c index b479c2975..9d665ffda 100644 --- a/qemu/roms/ipxe/src/core/open.c +++ b/qemu/roms/ipxe/src/core/open.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/params.c b/qemu/roms/ipxe/src/core/params.c index 93b834419..e1f66acca 100644 --- a/qemu/roms/ipxe/src/core/params.c +++ b/qemu/roms/ipxe/src/core/params.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/core/parseopt.c b/qemu/roms/ipxe/src/core/parseopt.c index d268c0594..66f60158c 100644 --- a/qemu/roms/ipxe/src/core/parseopt.c +++ b/qemu/roms/ipxe/src/core/parseopt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -32,6 +36,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include /** @file * @@ -343,7 +348,7 @@ void print_usage ( struct command_descriptor *cmd, char **argv ) { } if ( cmd->usage ) printf ( " %s", cmd->usage ); - printf ( "\n\nSee http://ipxe.org/cmd/%s for further information\n", + printf ( "\n\nSee " PRODUCT_COMMAND_URI " for further information\n", argv[0] ); } diff --git a/qemu/roms/ipxe/src/core/pending.c b/qemu/roms/ipxe/src/core/pending.c index 7bb0c2e00..96d0cf197 100644 --- a/qemu/roms/ipxe/src/core/pending.c +++ b/qemu/roms/ipxe/src/core/pending.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/pinger.c b/qemu/roms/ipxe/src/core/pinger.c index 31ea2ce1c..0ff7bb9f2 100644 --- a/qemu/roms/ipxe/src/core/pinger.c +++ b/qemu/roms/ipxe/src/core/pinger.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/pixbuf.c b/qemu/roms/ipxe/src/core/pixbuf.c index 48f8e9f9a..41e18f8dc 100644 --- a/qemu/roms/ipxe/src/core/pixbuf.c +++ b/qemu/roms/ipxe/src/core/pixbuf.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/core/pool.c b/qemu/roms/ipxe/src/core/pool.c new file mode 100644 index 000000000..0163405f7 --- /dev/null +++ b/qemu/roms/ipxe/src/core/pool.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * Pooled connections + * + */ + +#include +#include + +/** + * Recycle this connection after closing + * + * @v intf Data transfer interface + */ +void pool_recycle ( struct interface *intf ) { + + intf_poke ( intf, pool_recycle ); +} + +/** + * Reopen a defunct connection + * + * @v intf Data transfer interface + */ +void pool_reopen ( struct interface *intf ) { + + intf_poke ( intf, pool_reopen ); +} + +/** + * Add connection to pool + * + * @v pool Pooled connection + * @v list List of pooled connections + * @v expiry Expiry time + */ +void pool_add ( struct pooled_connection *pool, struct list_head *list, + unsigned long expiry ) { + + /* Sanity check */ + assert ( list_empty ( &pool->list ) ); + assert ( ! timer_running ( &pool->timer ) ); + + /* Add to list of pooled connections */ + list_add_tail ( &pool->list, list ); + + /* Start expiry timer */ + start_timer_fixed ( &pool->timer, expiry ); +} + +/** + * Remove connection from pool + * + * @v pool Pooled connection + */ +void pool_del ( struct pooled_connection *pool ) { + + /* Remove from list of pooled connections */ + list_del ( &pool->list ); + INIT_LIST_HEAD ( &pool->list ); + + /* Stop expiry timer */ + stop_timer ( &pool->timer ); + + /* Mark as a freshly recycled connection */ + pool->flags = POOL_RECYCLED; +} + +/** + * Close expired pooled connection + * + * @v timer Expiry timer + * @v over Failure indicator + */ +void pool_expired ( struct retry_timer *timer, int over __unused ) { + struct pooled_connection *pool = + container_of ( timer, struct pooled_connection, timer ); + + /* Sanity check */ + assert ( ! list_empty ( &pool->list ) ); + + /* Remove from connection pool */ + list_del ( &pool->list ); + INIT_LIST_HEAD ( &pool->list ); + + /* Close expired connection */ + pool->expired ( pool ); +} diff --git a/qemu/roms/ipxe/src/core/posix_io.c b/qemu/roms/ipxe/src/core/posix_io.c index 8460d0f51..35b52beeb 100644 --- a/qemu/roms/ipxe/src/core/posix_io.c +++ b/qemu/roms/ipxe/src/core/posix_io.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/process.c b/qemu/roms/ipxe/src/core/process.c index d341a2c37..69852c416 100644 --- a/qemu/roms/ipxe/src/core/process.c +++ b/qemu/roms/ipxe/src/core/process.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/profile.c b/qemu/roms/ipxe/src/core/profile.c index 150e6b273..1075047b9 100644 --- a/qemu/roms/ipxe/src/core/profile.c +++ b/qemu/roms/ipxe/src/core/profile.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/random.c b/qemu/roms/ipxe/src/core/random.c index 8824dca3a..a74175a79 100644 --- a/qemu/roms/ipxe/src/core/random.c +++ b/qemu/roms/ipxe/src/core/random.c @@ -4,7 +4,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/refcnt.c b/qemu/roms/ipxe/src/core/refcnt.c index 68a86120e..47c975a0b 100644 --- a/qemu/roms/ipxe/src/core/refcnt.c +++ b/qemu/roms/ipxe/src/core/refcnt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/resolv.c b/qemu/roms/ipxe/src/core/resolv.c index d59a8c0ad..1e3182b0b 100644 --- a/qemu/roms/ipxe/src/core/resolv.c +++ b/qemu/roms/ipxe/src/core/resolv.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/serial.c b/qemu/roms/ipxe/src/core/serial.c index 7e4460ab9..4ce025519 100644 --- a/qemu/roms/ipxe/src/core/serial.c +++ b/qemu/roms/ipxe/src/core/serial.c @@ -1,259 +1,184 @@ /* - * The serial port interface routines implement a simple polled i/o - * interface to a standard serial port. Due to the space restrictions - * for the boot blocks, no BIOS support is used (since BIOS requires - * expensive real/protected mode switches), instead the rudimentary - * BIOS support is duplicated here. + * Copyright (C) 2014 Michael Brown . * - * The base address and speed for the i/o port are passed from the - * Makefile in the COMCONSOLE and CONSPEED preprocessor macros. The - * line control parameters are currently hard-coded to 8 bits, no - * parity, 1 stop bit (8N1). This can be changed in init_serial(). + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Serial console + * + */ -#include "stddef.h" +#include #include -#include -#include +#include +#include #include -#include "config/serial.h" - -/* Set default values if none specified */ +#include +#include -#ifndef COMCONSOLE -#define COMCONSOLE 0x3f8 +/* Set default console usage if applicable */ +#if ! ( defined ( CONSOLE_SERIAL ) && CONSOLE_EXPLICIT ( CONSOLE_SERIAL ) ) +#undef CONSOLE_SERIAL +#define CONSOLE_SERIAL ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG ) #endif -#ifndef COMSPEED -#define COMSPEED 9600 -#endif - -#ifndef COMDATA -#define COMDATA 8 +/* UART port number */ +#ifdef COMCONSOLE +#define CONSOLE_PORT COMCONSOLE +#else +#define CONSOLE_PORT 0 #endif -#ifndef COMPARITY -#define COMPARITY 0 +/* UART baud rate */ +#ifdef COMPRESERVE +#define CONSOLE_BAUD 0 +#else +#define CONSOLE_BAUD COMSPEED #endif -#ifndef COMSTOP -#define COMSTOP 1 +/* UART line control register value */ +#ifdef COMPRESERVE +#define CONSOLE_LCR 0 +#else +#define CONSOLE_LCR UART_LCR_WPS ( COMDATA, COMPARITY, COMSTOP ) #endif -#undef UART_BASE -#define UART_BASE ( COMCONSOLE ) - -#undef UART_BAUD -#define UART_BAUD ( COMSPEED ) +/** Serial console UART */ +struct uart serial_console; -#if ((115200%UART_BAUD) != 0) -#error Bad ttys0 baud rate -#endif - -#define COMBRD (115200/UART_BAUD) +/** + * Print a character to serial console + * + * @v character Character to be printed + */ +static void serial_putchar ( int character ) { -/* Line Control Settings */ -#define UART_LCS ( ( ( (COMDATA) - 5 ) << 0 ) | \ - ( ( (COMPARITY) ) << 3 ) | \ - ( ( (COMSTOP) - 1 ) << 2 ) ) + /* Do nothing if we have no UART */ + if ( ! serial_console.base ) + return; -/* Data */ -#define UART_RBR 0x00 -#define UART_TBR 0x00 + /* Transmit character */ + uart_transmit ( &serial_console, character ); +} -/* Control */ -#define UART_IER 0x01 -#define UART_IIR 0x02 -#define UART_FCR 0x02 -#define UART_LCR 0x03 -#define UART_MCR 0x04 -#define UART_DLL 0x00 -#define UART_DLM 0x01 +/** + * Get character from serial console + * + * @ret character Character read from console + */ +static int serial_getchar ( void ) { + uint8_t data; -/* Status */ -#define UART_LSR 0x05 -#define UART_LSR_TEMPT 0x40 /* Transmitter empty */ -#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ -#define UART_LSR_BI 0x10 /* Break interrupt indicator */ -#define UART_LSR_FE 0x08 /* Frame error indicator */ -#define UART_LSR_PE 0x04 /* Parity error indicator */ -#define UART_LSR_OE 0x02 /* Overrun error indicator */ -#define UART_LSR_DR 0x01 /* Receiver data ready */ + /* Do nothing if we have no UART */ + if ( ! serial_console.base ) + return 0; -#define UART_MSR 0x06 -#define UART_SCR 0x07 + /* Wait for data to be ready */ + while ( ! uart_data_ready ( &serial_console ) ) {} -#if defined(UART_MEM) -#define uart_readb(addr) readb((addr)) -#define uart_writeb(val,addr) writeb((val),(addr)) -#else -#define uart_readb(addr) inb((addr)) -#define uart_writeb(val,addr) outb((val),(addr)) -#endif + /* Receive data */ + data = uart_receive ( &serial_console ); -/* Boolean for the state of serial driver initialization */ -int serial_initialized = 0; + /* Strip any high bit and convert DEL to backspace */ + data &= 0x7f; + if ( data == 0x7f ) + data = 0x08; -/* - * void serial_putc(int ch); - * Write character `ch' to port UART_BASE. - */ -void serial_putc ( int ch ) { - int i; - int status; - i = 1000; /* timeout */ - while(--i > 0) { - status = uart_readb(UART_BASE + UART_LSR); - if (status & UART_LSR_THRE) { - /* TX buffer emtpy */ - uart_writeb(ch, UART_BASE + UART_TBR); - break; - } - mdelay(2); - } + return data; } -/* - * int serial_getc(void); - * Read a character from port UART_BASE. - */ -int serial_getc ( void ) { - int status; - int ch; - do { - status = uart_readb(UART_BASE + UART_LSR); - } while((status & 1) == 0); - ch = uart_readb(UART_BASE + UART_RBR); /* fetch (first) character */ - ch &= 0x7f; /* remove any parity bits we get */ - if (ch == 0x7f) { /* Make DEL... look like BS */ - ch = 0x08; - } - return ch; -} - -/* - * int serial_ischar(void); - * If there is a character in the input buffer of port UART_BASE, - * return nonzero; otherwise return 0. +/** + * Check for character ready to read from serial console + * + * @ret True Character available to read + * @ret False No character available to read */ -int serial_ischar ( void ) { - int status; - status = uart_readb(UART_BASE + UART_LSR); /* line status reg; */ - return status & 1; /* rx char available */ -} +static int serial_iskey ( void ) { -/* - * int serial_init(void); - * Initialize port UART_BASE to speed COMSPEED, line settings 8N1. - */ -static void serial_init ( void ) { - int status; - int divisor, lcs; + /* Do nothing if we have no UART */ + if ( ! serial_console.base ) + return 0; - DBG ( "Serial port %#x initialising\n", UART_BASE ); + /* Check UART */ + return uart_data_ready ( &serial_console ); +} - divisor = COMBRD; - lcs = UART_LCS; +/** Serial console */ +struct console_driver serial_console_driver __console_driver = { + .putchar = serial_putchar, + .getchar = serial_getchar, + .iskey = serial_iskey, + .usage = CONSOLE_SERIAL, +}; +/** Initialise serial console */ +static void serial_init ( void ) { + int rc; -#ifdef COMPRESERVE - lcs = uart_readb(UART_BASE + UART_LCR) & 0x7f; - uart_writeb(0x80 | lcs, UART_BASE + UART_LCR); - divisor = (uart_readb(UART_BASE + UART_DLM) << 8) | uart_readb(UART_BASE + UART_DLL); - uart_writeb(lcs, UART_BASE + UART_LCR); -#endif + /* Do nothing if we have no default port */ + if ( ! CONSOLE_PORT ) + return; - /* Set Baud Rate Divisor to COMSPEED, and test to see if the - * serial port appears to be present. - */ - uart_writeb(0x80 | lcs, UART_BASE + UART_LCR); - uart_writeb(0xaa, UART_BASE + UART_DLL); - if (uart_readb(UART_BASE + UART_DLL) != 0xaa) { - DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE ); - goto out; - } - uart_writeb(0x55, UART_BASE + UART_DLL); - if (uart_readb(UART_BASE + UART_DLL) != 0x55) { - DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE ); - goto out; + /* Select UART */ + if ( ( rc = uart_select ( &serial_console, CONSOLE_PORT ) ) != 0 ) { + DBG ( "Could not select UART %d: %s\n", + CONSOLE_PORT, strerror ( rc ) ); + return; } - uart_writeb(divisor & 0xff, UART_BASE + UART_DLL); - if (uart_readb(UART_BASE + UART_DLL) != (divisor & 0xff)) { - DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE ); - goto out; - } - uart_writeb(0xaa, UART_BASE + UART_DLM); - if (uart_readb(UART_BASE + UART_DLM) != 0xaa) { - DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE ); - goto out; - } - uart_writeb(0x55, UART_BASE + UART_DLM); - if (uart_readb(UART_BASE + UART_DLM) != 0x55) { - DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE ); - goto out; - } - uart_writeb((divisor >> 8) & 0xff, UART_BASE + UART_DLM); - if (uart_readb(UART_BASE + UART_DLM) != ((divisor >> 8) & 0xff)) { - DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE ); - goto out; - } - uart_writeb(lcs, UART_BASE + UART_LCR); - - /* disable interrupts */ - uart_writeb(0x0, UART_BASE + UART_IER); - /* enable fifos */ - uart_writeb(0x01, UART_BASE + UART_FCR); + /* Initialise UART */ + if ( ( rc = uart_init ( &serial_console, CONSOLE_BAUD, + CONSOLE_LCR ) ) != 0 ) { + DBG ( "Could not initialise UART %d baud %d LCR %#02x: %s\n", + CONSOLE_PORT, CONSOLE_BAUD, CONSOLE_LCR, strerror ( rc )); + return; + } +} - /* Set clear to send, so flow control works... */ - uart_writeb((1<<1), UART_BASE + UART_MCR); +/** + * Shut down serial console + * + * @v flags Shutdown flags + */ +static void serial_shutdown ( int flags __unused ) { - /* Flush the input buffer. */ - do { - /* rx buffer reg - * throw away (unconditionally the first time) - */ - (void) uart_readb(UART_BASE + UART_RBR); - /* line status reg */ - status = uart_readb(UART_BASE + UART_LSR); - } while(status & UART_LSR_DR); + /* Do nothing if we have no UART */ + if ( ! serial_console.base ) + return; - /* Note that serial support has been initialized */ - serial_initialized = 1; - out: - return; -} + /* Flush any pending output */ + uart_flush ( &serial_console ); -/* - * void serial_fini(void); - * Cleanup our use of the serial port, in particular flush the - * output buffer so we don't accidentially lose characters. - */ -static void serial_fini ( int flags __unused ) { - int i, status; - /* Flush the output buffer to avoid dropping characters, - * if we are reinitializing the serial port. - */ - i = 10000; /* timeout */ - do { - status = uart_readb(UART_BASE + UART_LSR); - } while((--i > 0) && !(status & UART_LSR_TEMPT)); - /* Don't mark it as disabled; it's still usable */ + /* Leave console enabled; it's still usable */ } -/** - * Serial driver initialisation function - * - * Initialise serial port early on so that it is available to capture - * early debug messages. - */ -struct init_fn serial_init_fn __init_fn ( INIT_SERIAL ) = { +/** Serial console initialisation function */ +struct init_fn serial_console_init_fn __init_fn ( INIT_CONSOLE ) = { .initialise = serial_init, }; -/** Serial driver startup function */ +/** Serial console startup function */ struct startup_fn serial_startup_fn __startup_fn ( STARTUP_EARLY ) = { - .shutdown = serial_fini, + .shutdown = serial_shutdown, }; diff --git a/qemu/roms/ipxe/src/core/serial_console.c b/qemu/roms/ipxe/src/core/serial_console.c deleted file mode 100644 index de9b84ca7..000000000 --- a/qemu/roms/ipxe/src/core/serial_console.c +++ /dev/null @@ -1,42 +0,0 @@ -#include -#include -#include -#include - -/** @file - * - * Serial console - * - */ - -/* Set default console usage if applicable */ -#if ! ( defined ( CONSOLE_SERIAL ) && CONSOLE_EXPLICIT ( CONSOLE_SERIAL ) ) -#undef CONSOLE_SERIAL -#define CONSOLE_SERIAL ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG ) -#endif - -struct console_driver serial_console __console_driver; - -static void serial_console_init ( void ) { - /* - * Check if serial driver initialization is done. - * If so, it's time to enable the serial console. - */ - if ( serial_initialized ) - serial_console.disabled = 0; -} - -struct console_driver serial_console __console_driver = { - .putchar = serial_putc, - .getchar = serial_getc, - .iskey = serial_ischar, - .disabled = CONSOLE_DISABLED, - .usage = CONSOLE_SERIAL, -}; - -/** - * Serial console initialisation function - */ -struct init_fn serial_console_init_fn __init_fn ( INIT_CONSOLE ) = { - .initialise = serial_console_init, -}; diff --git a/qemu/roms/ipxe/src/core/settings.c b/qemu/roms/ipxe/src/core/settings.c index 5e16b27d0..12e6c7d61 100644 --- a/qemu/roms/ipxe/src/core/settings.c +++ b/qemu/roms/ipxe/src/core/settings.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -35,6 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include @@ -337,17 +342,20 @@ struct settings * autovivify_child_settings ( struct settings *parent, */ const char * settings_name ( struct settings *settings ) { static char buf[16]; - char tmp[ sizeof ( buf ) ]; + char tmp[ 1 /* '.' */ + sizeof ( buf ) ]; /* Find target settings block */ settings = settings_target ( settings ); /* Construct name */ - for ( buf[2] = buf[0] = 0 ; settings ; settings = settings->parent ) { - memcpy ( tmp, buf, sizeof ( tmp ) ); - snprintf ( buf, sizeof ( buf ), ".%s%s", settings->name, tmp ); + buf[0] = '\0'; + tmp[0] = '\0'; + for ( ; settings->parent ; settings = settings->parent ) { + memcpy ( ( tmp + 1 ), buf, ( sizeof ( tmp ) - 1 ) ); + snprintf ( buf, sizeof ( buf ), "%s%s", settings->name, tmp ); + tmp[0] = '.'; } - return ( buf + 2 ); + return buf; } /** @@ -499,10 +507,10 @@ int register_settings ( struct settings *settings, struct settings *parent, */ void unregister_settings ( struct settings *settings ) { struct settings *child; - struct settings *tmp; /* Unregister child settings */ - list_for_each_entry_safe ( child, tmp, &settings->children, siblings ) { + while ( ( child = list_first_entry ( &settings->children, + struct settings, siblings ) ) ) { unregister_settings ( child ); } @@ -1998,32 +2006,6 @@ const struct setting_type setting_type_uint16 __setting_type = const struct setting_type setting_type_uint32 __setting_type = SETTING_TYPE_UINT ( SETTING_TYPE_INT32 ); -/** - * Format hex string setting value - * - * @v delimiter Byte delimiter - * @v raw Raw setting value - * @v raw_len Length of raw setting value - * @v buf Buffer to contain formatted value - * @v len Length of buffer - * @ret len Length of formatted value, or negative error - */ -static int format_hex_setting ( const char *delimiter, const void *raw, - size_t raw_len, char *buf, size_t len ) { - const uint8_t *bytes = raw; - int used = 0; - unsigned int i; - - if ( len ) - buf[0] = 0; /* Ensure that a terminating NUL exists */ - for ( i = 0 ; i < raw_len ; i++ ) { - used += ssnprintf ( ( buf + used ), ( len - used ), - "%s%02x", ( used ? delimiter : "" ), - bytes[i] ); - } - return used; -} - /** * Parse hex string setting value (using colon delimiter) * @@ -2036,7 +2018,7 @@ static int format_hex_setting ( const char *delimiter, const void *raw, */ static int parse_hex_setting ( const struct setting_type *type __unused, const char *value, void *buf, size_t len ) { - return hex_decode ( value, ':', buf, len ); + return hex_decode ( ':', value, buf, len ); } /** @@ -2052,7 +2034,7 @@ static int parse_hex_setting ( const struct setting_type *type __unused, static int format_hex_colon_setting ( const struct setting_type *type __unused, const void *raw, size_t raw_len, char *buf, size_t len ) { - return format_hex_setting ( ":", raw, raw_len, buf, len ); + return hex_encode ( ':', raw, raw_len, buf, len ); } /** @@ -2068,7 +2050,7 @@ static int format_hex_colon_setting ( const struct setting_type *type __unused, static int parse_hex_hyphen_setting ( const struct setting_type *type __unused, const char *value, void *buf, size_t len ) { - return hex_decode ( value, '-', buf, len ); + return hex_decode ( '-', value, buf, len ); } /** @@ -2084,7 +2066,7 @@ static int parse_hex_hyphen_setting ( const struct setting_type *type __unused, static int format_hex_hyphen_setting ( const struct setting_type *type __unused, const void *raw, size_t raw_len, char *buf, size_t len ) { - return format_hex_setting ( "-", raw, raw_len, buf, len ); + return hex_encode ( '-', raw, raw_len, buf, len ); } /** @@ -2099,7 +2081,7 @@ static int format_hex_hyphen_setting ( const struct setting_type *type __unused, */ static int parse_hex_raw_setting ( const struct setting_type *type __unused, const char *value, void *buf, size_t len ) { - return hex_decode ( value, 0, buf, len ); + return hex_decode ( 0, value, buf, len ); } /** @@ -2115,7 +2097,7 @@ static int parse_hex_raw_setting ( const struct setting_type *type __unused, static int format_hex_raw_setting ( const struct setting_type *type __unused, const void *raw, size_t raw_len, char *buf, size_t len ) { - return format_hex_setting ( "", raw, raw_len, buf, len ); + return hex_encode ( 0, raw, raw_len, buf, len ); } /** A hex-string setting (colon-delimited) */ @@ -2139,6 +2121,46 @@ const struct setting_type setting_type_hexraw __setting_type = { .format = format_hex_raw_setting, }; +/** + * Parse Base64-encoded setting value + * + * @v type Setting type + * @v value Formatted setting value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @v size Integer size, in bytes + * @ret len Length of raw value, or negative error + */ +static int parse_base64_setting ( const struct setting_type *type __unused, + const char *value, void *buf, size_t len ) { + + return base64_decode ( value, buf, len ); +} + +/** + * Format Base64-encoded setting value + * + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int format_base64_setting ( const struct setting_type *type __unused, + const void *raw, size_t raw_len, + char *buf, size_t len ) { + + return base64_encode ( raw, raw_len, buf, len ); +} + +/** A Base64-encoded setting */ +const struct setting_type setting_type_base64 __setting_type = { + .name = "base64", + .parse = parse_base64_setting, + .format = format_base64_setting, +}; + /** * Format UUID setting value * diff --git a/qemu/roms/ipxe/src/core/string.c b/qemu/roms/ipxe/src/core/string.c index e53c283c2..3e658e54e 100644 --- a/qemu/roms/ipxe/src/core/string.c +++ b/qemu/roms/ipxe/src/core/string.c @@ -1,353 +1,501 @@ /* - * Copyright (C) 1991, 1992 Linus Torvalds - * Copyright (C) 2004 Tobias Lorenz + * Copyright (C) 2015 Michael Brown . * - * string handling functions - * based on linux/lib/string.c + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * - * This program is 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. - */ - -FILE_LICENCE ( GPL2_ONLY ); - -/* - * stupid library routines.. The optimized versions should generally be found - * as inline code in + * 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. * - * These are buggy as well.. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. * - * * Fri Jun 25 1999, Ingo Oeser - * - Added strsep() which will replace strtok() soon (because strsep() is - * reentrant and should be faster). Use only strsep() in new code, please. + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ - + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include #include #include #include #include -/* *** FROM string.c *** */ +/** @file + * + * String functions + * + */ -#ifndef __HAVE_ARCH_STRCPY /** - * strcpy - Copy a %NUL terminated string - * @dest: Where to copy the string to - * @src: Where to copy the string from + * Fill memory region + * + * @v dest Destination region + * @v character Fill character + * @v len Length + * @ret dest Destination region */ -char * strcpy(char * dest,const char *src) -{ - char *tmp = dest; +void * generic_memset ( void *dest, int character, size_t len ) { + uint8_t *dest_bytes = dest; - while ((*dest++ = *src++) != '\0') - /* nothing */; - return tmp; + while ( len-- ) + *(dest_bytes++) = character; + return dest; } -#endif -#ifndef __HAVE_ARCH_STRNCPY /** - * strncpy - Copy a length-limited, %NUL-terminated string - * @dest: Where to copy the string to - * @src: Where to copy the string from - * @count: The maximum number of bytes to copy + * Copy memory region * - * Note that unlike userspace strncpy, this does not %NUL-pad the buffer. - * However, the result is not %NUL-terminated if the source exceeds - * @count bytes. + * @v dest Destination region + * @v src Source region + * @v len Length + * @ret dest Destination region */ -char * strncpy(char * dest,const char *src,size_t count) -{ - char *tmp = dest; - - while (count-- && (*dest++ = *src++) != '\0') - /* nothing */; +void * generic_memcpy ( void *dest, const void *src, size_t len ) { + const uint8_t *src_bytes = src; + uint8_t *dest_bytes = dest; - return tmp; + while ( len-- ) + *(dest_bytes++) = *(src_bytes++); + return dest; } -#endif -#ifndef __HAVE_ARCH_STRCAT /** - * strcat - Append one %NUL-terminated string to another - * @dest: The string to be appended to - * @src: The string to append to it + * Copy (possibly overlapping) memory region + * + * @v dest Destination region + * @v src Source region + * @v len Length + * @ret dest Destination region */ -char * strcat(char * dest, const char * src) -{ - char *tmp = dest; - - while (*dest) - dest++; - while ((*dest++ = *src++) != '\0') - ; +void * generic_memmove ( void *dest, const void *src, size_t len ) { + const uint8_t *src_bytes = ( src + len ); + uint8_t *dest_bytes = ( dest + len ); + + if ( dest < src ) + return memcpy ( dest, src, len ); + while ( len-- ) + *(--dest_bytes) = *(--src_bytes); + return dest; +} - return tmp; +/** + * Compare memory regions + * + * @v first First region + * @v second Second region + * @v len Length + * @ret diff Difference + */ +int memcmp ( const void *first, const void *second, size_t len ) { + const uint8_t *first_bytes = first; + const uint8_t *second_bytes = second; + int diff; + + while ( len-- ) { + diff = ( *(second_bytes++) - *(first_bytes++) ); + if ( diff ) + return diff; + } + return 0; } -#endif -#ifndef __HAVE_ARCH_STRCMP /** - * strcmp - Compare two strings - * @cs: One string - * @ct: Another string + * Find character within a memory region + * + * @v src Source region + * @v character Character to find + * @v len Length + * @ret found Found character, or NULL if not found */ -int strcmp(const char * cs,const char * ct) -{ - register signed char __res; +void * memchr ( const void *src, int character, size_t len ) { + const uint8_t *src_bytes = src; - while (1) { - if ((__res = *cs - *ct++) != 0 || !*cs++) - break; + for ( ; len-- ; src_bytes++ ) { + if ( *src_bytes == character ) + return ( ( void * ) src_bytes ); } - - return __res; + return NULL; } -#endif -#ifndef __HAVE_ARCH_STRNCMP /** - * strncmp - Compare two length-limited strings - * @cs: One string - * @ct: Another string - * @count: The maximum number of bytes to compare + * Swap memory regions + * + * @v first First region + * @v second Second region + * @v len Length + * @ret first First region */ -int strncmp(const char * cs,const char * ct,size_t count) -{ - register signed char __res = 0; - - while (count) { - if ((__res = *cs - *ct++) != 0 || !*cs++) - break; - count--; +void * memswap ( void *first, void *second, size_t len ) { + uint8_t *first_bytes = first; + uint8_t *second_bytes = second; + uint8_t temp; + + for ( ; len-- ; first_bytes++, second_bytes++ ) { + temp = *first_bytes; + *first_bytes = *second_bytes; + *second_bytes = temp; } + return first; +} + +/** + * Compare strings + * + * @v first First string + * @v second Second string + * @ret diff Difference + */ +int strcmp ( const char *first, const char *second ) { - return __res; + return strncmp ( first, second, ~( ( size_t ) 0 ) ); } -#endif -#ifndef __HAVE_ARCH_STRCASECMP -int strcasecmp(const char *a, const char *b) -{ - while (*a && *b && (*a & ~0x20) == (*b & ~0x20)) {a++; b++; } - return((*a & ~0x20) - (*b & ~0x20)); +/** + * Compare strings + * + * @v first First string + * @v second Second string + * @v max Maximum length to compare + * @ret diff Difference + */ +int strncmp ( const char *first, const char *second, size_t max ) { + const uint8_t *first_bytes = ( ( const uint8_t * ) first ); + const uint8_t *second_bytes = ( ( const uint8_t * ) second ); + int diff; + + for ( ; max-- ; first_bytes++, second_bytes++ ) { + diff = ( *second_bytes - *first_bytes ); + if ( diff ) + return diff; + if ( ! *first_bytes ) + return 0; + } + return 0; } -#endif -#ifndef __HAVE_ARCH_STRCHR /** - * strchr - Find the first occurrence of a character in a string - * @s: The string to be searched - * @c: The character to search for + * Compare case-insensitive strings + * + * @v first First string + * @v second Second string + * @ret diff Difference */ -char * strchr(const char * s, int c) -{ - for(; *s != (char) c; ++s) - if (*s == '\0') - return NULL; - return (char *) s; +int strcasecmp ( const char *first, const char *second ) { + const uint8_t *first_bytes = ( ( const uint8_t * ) first ); + const uint8_t *second_bytes = ( ( const uint8_t * ) second ); + int diff; + + for ( ; ; first_bytes++, second_bytes++ ) { + diff = ( toupper ( *second_bytes ) - + toupper ( *first_bytes ) ); + if ( diff ) + return diff; + if ( ! *first_bytes ) + return 0; + } } -#endif -#ifndef __HAVE_ARCH_STRRCHR /** - * strrchr - Find the last occurrence of a character in a string - * @s: The string to be searched - * @c: The character to search for + * Get length of string + * + * @v src String + * @ret len Length */ -char * strrchr(const char * s, int c) -{ - const char *p = s + strlen(s); - do { - if (*p == (char)c) - return (char *)p; - } while (--p >= s); - return NULL; +size_t strlen ( const char *src ) { + + return strnlen ( src, ~( ( size_t ) 0 ) ); } -#endif -#ifndef __HAVE_ARCH_STRLEN /** - * strlen - Find the length of a string - * @s: The string to be sized + * Get length of string + * + * @v src String + * @v max Maximum length + * @ret len Length */ -size_t strlen(const char * s) -{ - const char *sc; +size_t strnlen ( const char *src, size_t max ) { + const uint8_t *src_bytes = ( ( const uint8_t * ) src ); + size_t len = 0; - for (sc = s; *sc != '\0'; ++sc) - /* nothing */; - return sc - s; + while ( max-- && *(src_bytes++) ) + len++; + return len; } -#endif -#ifndef __HAVE_ARCH_STRNLEN /** - * strnlen - Find the length of a length-limited string - * @s: The string to be sized - * @count: The maximum number of bytes to search + * Find character within a string + * + * @v src String + * @v character Character to find + * @ret found Found character, or NULL if not found */ -size_t strnlen(const char * s, size_t count) -{ - const char *sc; +char * strchr ( const char *src, int character ) { + const uint8_t *src_bytes = ( ( const uint8_t * ) src ); - for (sc = s; count-- && *sc != '\0'; ++sc) - /* nothing */; - return sc - s; + for ( ; ; src_bytes++ ) { + if ( *src_bytes == character ) + return ( ( char * ) src_bytes ); + if ( ! *src_bytes ) + return NULL; + } } -#endif -#ifndef __HAVE_ARCH_MEMSET /** - * memset - Fill a region of memory with the given value - * @s: Pointer to the start of the area. - * @c: The byte to fill the area with - * @count: The size of the area. + * Find rightmost character within a string * - * Do not use memset() to access IO space, use memset_io() instead. + * @v src String + * @v character Character to find + * @ret found Found character, or NULL if not found */ -void * memset(void * s,int c,size_t count) -{ - char *xs = (char *) s; +char * strrchr ( const char *src, int character ) { + const uint8_t *src_bytes = ( ( const uint8_t * ) src ); + const uint8_t *start = src_bytes; + + while ( *src_bytes ) + src_bytes++; + for ( src_bytes-- ; src_bytes >= start ; src_bytes-- ) { + if ( *src_bytes == character ) + return ( ( char * ) src_bytes ); + } + return NULL; +} - while (count--) - *xs++ = c; +/** + * Find substring + * + * @v haystack String + * @v needle Substring + * @ret found Found substring, or NULL if not found + */ +char * strstr ( const char *haystack, const char *needle ) { + size_t len = strlen ( needle ); - return s; + for ( ; *haystack ; haystack++ ) { + if ( memcmp ( haystack, needle, len ) == 0 ) + return ( ( char * ) haystack ); + } + return NULL; } -#endif -#ifndef __HAVE_ARCH_MEMCPY /** - * memcpy - Copy one area of memory to another - * @dest: Where to copy to - * @src: Where to copy from - * @count: The size of the area. + * Copy string * - * You should not use this function to access IO space, use memcpy_toio() - * or memcpy_fromio() instead. + * @v dest Destination string + * @v src Source string + * @ret dest Destination string */ -void * memcpy(void * dest,const void *src,size_t count) -{ - char *tmp = (char *) dest, *s = (char *) src; +char * strcpy ( char *dest, const char *src ) { + const uint8_t *src_bytes = ( ( const uint8_t * ) src ); + uint8_t *dest_bytes = ( ( uint8_t * ) dest ); + + /* We cannot use strncpy(), since that would pad the destination */ + for ( ; ; src_bytes++, dest_bytes++ ) { + *dest_bytes = *src_bytes; + if ( ! *dest_bytes ) + break; + } + return dest; +} - while (count--) - *tmp++ = *s++; +/** + * Copy string + * + * @v dest Destination string + * @v src Source string + * @v max Maximum length + * @ret dest Destination string + */ +char * strncpy ( char *dest, const char *src, size_t max ) { + const uint8_t *src_bytes = ( ( const uint8_t * ) src ); + uint8_t *dest_bytes = ( ( uint8_t * ) dest ); + for ( ; max ; max--, src_bytes++, dest_bytes++ ) { + *dest_bytes = *src_bytes; + if ( ! *dest_bytes ) + break; + } + while ( max-- ) + *(dest_bytes++) = '\0'; return dest; } -#endif -#ifndef __HAVE_ARCH_MEMMOVE /** - * memmove - Copy one area of memory to another - * @dest: Where to copy to - * @src: Where to copy from - * @count: The size of the area. + * Concatenate string * - * Unlike memcpy(), memmove() copes with overlapping areas. + * @v dest Destination string + * @v src Source string + * @ret dest Destination string */ -void * memmove(void * dest,const void *src,size_t count) -{ - char *tmp, *s; - - if (dest <= src) { - tmp = (char *) dest; - s = (char *) src; - while (count--) - *tmp++ = *s++; - } - else { - tmp = (char *) dest + count; - s = (char *) src + count; - while (count--) - *--tmp = *--s; - } +char * strcat ( char *dest, const char *src ) { + strcpy ( ( dest + strlen ( dest ) ), src ); return dest; } -#endif -#ifndef __HAVE_ARCH_MEMCMP /** - * memcmp - Compare two areas of memory - * @cs: One area of memory - * @ct: Another area of memory - * @count: The size of the area. + * Duplicate string + * + * @v src Source string + * @ret dup Duplicated string, or NULL if allocation failed */ -int memcmp(const void * cs,const void * ct,size_t count) -{ - const unsigned char *su1, *su2; - int res = 0; +char * strdup ( const char *src ) { - for( su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) - if ((res = *su1 - *su2) != 0) - break; - return res; + return strndup ( src, ~( ( size_t ) 0 ) ); } -#endif -#ifndef __HAVE_ARCH_STRSTR /** - * strstr - Find the first substring in a %NUL terminated string - * @s1: The string to be searched - * @s2: The string to search for + * Duplicate string + * + * @v src Source string + * @v max Maximum length + * @ret dup Duplicated string, or NULL if allocation failed */ -char * strstr(const char * s1,const char * s2) -{ - int l1, l2; - - l2 = strlen(s2); - if (!l2) - return (char *) s1; - l1 = strlen(s1); - while (l1 >= l2) { - l1--; - if (!memcmp(s1,s2,l2)) - return (char *) s1; - s1++; - } - return NULL; +char * strndup ( const char *src, size_t max ) { + size_t len = strnlen ( src, max ); + char *dup; + + dup = malloc ( len + 1 /* NUL */ ); + if ( dup ) { + memcpy ( dup, src, len ); + dup[len] = '\0'; + } + return dup; +} + +/** + * Calculate digit value + * + * @v character Digit character + * @ret digit Digit value + * + * Invalid digits will be returned as a value greater than or equal to + * the numeric base. + */ +unsigned int digit_value ( unsigned int character ) { + + if ( character >= 'a' ) + return ( character - ( 'a' - 10 ) ); + if ( character >= 'A' ) + return ( character - ( 'A' - 10 ) ); + if ( character <= '9' ) + return ( character - '0' ); + return character; } -#endif -#ifndef __HAVE_ARCH_MEMCHR /** - * memchr - Find a character in an area of memory. - * @s: The memory area - * @c: The byte to search for - * @n: The size of the area. + * Preprocess string for strtoul() or strtoull() * - * returns the address of the first occurrence of @c, or %NULL - * if @c is not found + * @v string String + * @v negate Final value should be negated + * @v base Numeric base + * @ret string Remaining string */ -void * memchr(const void *s, int c, size_t n) -{ - const unsigned char *p = s; - while (n-- != 0) { - if ((unsigned char)c == *p++) { - return (void *)(p-1); +static const char * strtoul_pre ( const char *string, int *negate, int *base ) { + + /* Skip any leading whitespace */ + while ( isspace ( *string ) ) + string++; + + /* Process arithmetic sign, if present */ + *negate = 0; + if ( *string == '-' ) { + string++; + *negate = 1; + } else if ( *string == '+' ) { + string++; + } + + /* Process base, if present */ + if ( *base == 0 ) { + *base = 10; + if ( *string == '0' ) { + string++; + *base = 8; + if ( ( *string & ~0x20 ) == 'X' ) { + string++; + *base = 16; + } } } - return NULL; + + return string; } -#endif +/** + * Convert string to numeric value + * + * @v string String + * @v endp End pointer (or NULL) + * @v base Numeric base (or zero to autodetect) + * @ret value Numeric value + */ +unsigned long strtoul ( const char *string, char **endp, int base ) { + unsigned long value = 0; + unsigned int digit; + int negate; + + /* Preprocess string */ + string = strtoul_pre ( string, &negate, &base ); + + /* Process digits */ + for ( ; ; string++ ) { + digit = digit_value ( *string ); + if ( digit >= ( unsigned int ) base ) + break; + value = ( ( value * base ) + digit ); + } + + /* Negate value if, applicable */ + if ( negate ) + value = -value; -char * strndup(const char *s, size_t n) -{ - size_t len = strnlen(s,n); - char *new; + /* Fill in end pointer, if applicable */ + if ( endp ) + *endp = ( ( char * ) string ); - new = malloc(len+1); - if (new) { - new[len] = '\0'; - memcpy(new,s,len); - } - return new; + return value; } -char * strdup(const char *s) { - return strndup(s, ~((size_t)0)); +/** + * Convert string to numeric value + * + * @v string String + * @v endp End pointer (or NULL) + * @v base Numeric base (or zero to autodetect) + * @ret value Numeric value + */ +unsigned long long strtoull ( const char *string, char **endp, int base ) { + unsigned long long value = 0; + unsigned int digit; + int negate; + + /* Preprocess string */ + string = strtoul_pre ( string, &negate, &base ); + + /* Process digits */ + for ( ; ; string++ ) { + digit = digit_value ( *string ); + if ( digit >= ( unsigned int ) base ) + break; + value = ( ( value * base ) + digit ); + } + + /* Negate value if, applicable */ + if ( negate ) + value = -value; + + /* Fill in end pointer, if applicable */ + if ( endp ) + *endp = ( ( char * ) string ); + + return value; } diff --git a/qemu/roms/ipxe/src/core/stringextra.c b/qemu/roms/ipxe/src/core/stringextra.c index 0a509852e..18ffc6301 100644 --- a/qemu/roms/ipxe/src/core/stringextra.c +++ b/qemu/roms/ipxe/src/core/stringextra.c @@ -38,122 +38,6 @@ FILE_LICENCE ( GPL2_ONLY ); /* *** FROM string.c *** */ -#ifndef __HAVE_ARCH_STRNICMP -/** - * strnicmp - Case insensitive, length-limited string comparison - * @s1: One string - * @s2: The other string - * @len: the maximum number of characters to compare - */ -int strnicmp(const char *s1, const char *s2, size_t len) -{ - /* Yes, Virginia, it had better be unsigned */ - unsigned char c1, c2; - - c1 = 0; c2 = 0; - if (len) { - do { - c1 = *s1; c2 = *s2; - s1++; s2++; - if (!c1) - break; - if (!c2) - break; - if (c1 == c2) - continue; - c1 = tolower(c1); - c2 = tolower(c2); - if (c1 != c2) - break; - } while (--len); - } - return (int)c1 - (int)c2; -} -#endif - -char * ___strtok; - -#ifndef __HAVE_ARCH_STRNCAT -/** - * strncat - Append a length-limited, %NUL-terminated string to another - * @dest: The string to be appended to - * @src: The string to append to it - * @count: The maximum numbers of bytes to copy - * - * Note that in contrast to strncpy, strncat ensures the result is - * terminated. - */ -char * strncat(char *dest, const char *src, size_t count) -{ - char *tmp = dest; - - if (count) { - while (*dest) - dest++; - while ((*dest++ = *src++)) { - if (--count == 0) { - *dest = '\0'; - break; - } - } - } - - return tmp; -} -#endif - -#ifndef __HAVE_ARCH_STRSPN -/** - * strspn - Calculate the length of the initial substring of @s which only - * contain letters in @accept - * @s: The string to be searched - * @accept: The string to search for - */ -size_t strspn(const char *s, const char *accept) -{ - const char *p; - const char *a; - size_t count = 0; - - for (p = s; *p != '\0'; ++p) { - for (a = accept; *a != '\0'; ++a) { - if (*p == *a) - break; - } - if (*a == '\0') - return count; - ++count; - } - - return count; -} -#endif - -#ifndef __HAVE_ARCH_STRCSPN -/** - * strcspn - Calculate the length of the initial substring of @s which only - * contain letters not in @reject - * @s: The string to be searched - * @accept: The string to search for - */ -size_t strcspn(const char *s, const char *reject) -{ - const char *p; - const char *r; - size_t count = 0; - - for (p = s; *p != '\0'; ++p) { - for (r = reject; *r != '\0'; ++r) { - if (*p == *r) - return count; - } - ++count; - } - - return count; -} -#endif - #ifndef __HAVE_ARCH_STRPBRK /** * strpbrk - Find the first occurrence of a set of characters @@ -174,35 +58,6 @@ char * strpbrk(const char * cs,const char * ct) } #endif -#ifndef __HAVE_ARCH_STRTOK -/** - * strtok - Split a string into tokens - * @s: The string to be searched - * @ct: The characters to search for - * - * WARNING: strtok is deprecated, use strsep instead. - */ -char * strtok(char * s,const char * ct) -{ - char *sbegin, *send; - - sbegin = s ? s : ___strtok; - if (!sbegin) { - return NULL; - } - sbegin += strspn(sbegin,ct); - if (*sbegin == '\0') { - ___strtok = NULL; - return( NULL ); - } - send = strpbrk( sbegin, ct); - if (send && *send != '\0') - *send++ = '\0'; - ___strtok = send; - return (sbegin); -} -#endif - #ifndef __HAVE_ARCH_STRSEP /** * strsep - Split a string into tokens @@ -230,46 +85,3 @@ char * strsep(char **s, const char *ct) return sbegin; } #endif - -#ifndef __HAVE_ARCH_BCOPY -/** - * bcopy - Copy one area of memory to another - * @src: Where to copy from - * @dest: Where to copy to - * @count: The size of the area. - * - * Note that this is the same as memcpy(), with the arguments reversed. - * memcpy() is the standard, bcopy() is a legacy BSD function. - * - * You should not use this function to access IO space, use memcpy_toio() - * or memcpy_fromio() instead. - */ -char * bcopy(const char * src, char * dest, int count) -{ - return memmove(dest,src,count); -} -#endif - -#ifndef __HAVE_ARCH_MEMSCAN -/** - * memscan - Find a character in an area of memory. - * @addr: The memory area - * @c: The byte to search for - * @size: The size of the area. - * - * returns the address of the first occurrence of @c, or 1 byte past - * the area if @c is not found - */ -void * memscan(const void * addr, int c, size_t size) -{ - unsigned char * p = (unsigned char *) addr; - - while (size) { - if (*p == c) - return (void *) p; - p++; - size--; - } - return (void *) p; -} -#endif diff --git a/qemu/roms/ipxe/src/core/strtoull.c b/qemu/roms/ipxe/src/core/strtoull.c deleted file mode 100644 index 00986eef0..000000000 --- a/qemu/roms/ipxe/src/core/strtoull.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2006 Michael Brown - * Copyright (C) 2010 Piotr Jaroszyński - * - * 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 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include -#include - -/* - * Despite being exactly the same as strtoul() except the long long instead of - * long it ends up being much bigger so provide a separate implementation in a - * separate object so that it won't be linked in if not used. - */ -unsigned long long strtoull ( const char *p, char **endp, int base ) { - unsigned long long ret = 0; - int negative = 0; - unsigned int charval; - - while ( isspace ( *p ) ) - p++; - - if ( *p == '-' ) { - negative = 1; - p++; - } - - base = strtoul_base ( &p, base ); - - while ( 1 ) { - charval = strtoul_charval ( *p ); - if ( charval >= ( unsigned int ) base ) - break; - ret = ( ( ret * base ) + charval ); - p++; - } - - if ( negative ) - ret = -ret; - - if ( endp ) - *endp = ( char * ) p; - - return ( ret ); -} diff --git a/qemu/roms/ipxe/src/core/time.c b/qemu/roms/ipxe/src/core/time.c index f70e1981d..29a924ebe 100644 --- a/qemu/roms/ipxe/src/core/time.c +++ b/qemu/roms/ipxe/src/core/time.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/core/timer.c b/qemu/roms/ipxe/src/core/timer.c index 18c2b2849..dbd89f12b 100644 --- a/qemu/roms/ipxe/src/core/timer.c +++ b/qemu/roms/ipxe/src/core/timer.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/core/uart.c b/qemu/roms/ipxe/src/core/uart.c new file mode 100644 index 000000000..b85fe0767 --- /dev/null +++ b/qemu/roms/ipxe/src/core/uart.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * 16550-compatible UART + * + */ + +#include +#include +#include + +/** Timeout for transmit holding register to become empty */ +#define UART_THRE_TIMEOUT_MS 100 + +/** Timeout for transmitter to become empty */ +#define UART_TEMT_TIMEOUT_MS 1000 + +/** + * Transmit data + * + * @v uart UART + * @v data Data + */ +void uart_transmit ( struct uart *uart, uint8_t data ) { + unsigned int i; + uint8_t lsr; + + /* Wait for transmitter holding register to become empty */ + for ( i = 0 ; i < UART_THRE_TIMEOUT_MS ; i++ ) { + lsr = uart_read ( uart, UART_LSR ); + if ( lsr & UART_LSR_THRE ) + break; + mdelay ( 1 ); + } + + /* Transmit data (even if we timed out) */ + uart_write ( uart, UART_THR, data ); +} + +/** + * Flush data + * + * @v uart UART + */ +void uart_flush ( struct uart *uart ) { + unsigned int i; + uint8_t lsr; + + /* Wait for transmitter and receiver to become empty */ + for ( i = 0 ; i < UART_TEMT_TIMEOUT_MS ; i++ ) { + uart_read ( uart, UART_RBR ); + lsr = uart_read ( uart, UART_LSR ); + if ( ( lsr & UART_LSR_TEMT ) && ! ( lsr & UART_LSR_DR ) ) + break; + } +} + +/** + * Check for existence of UART + * + * @v uart UART + * @ret rc Return status code + */ +int uart_exists ( struct uart *uart ) { + + /* Fail if no UART port is defined */ + if ( ! uart->base ) + return -ENODEV; + + /* Fail if UART scratch register seems not to be present */ + uart_write ( uart, UART_SCR, 0x18 ); + if ( uart_read ( uart, UART_SCR ) != 0x18 ) + return -ENODEV; + uart_write ( uart, UART_SCR, 0xae ); + if ( uart_read ( uart, UART_SCR ) != 0xae ) + return -ENODEV; + + return 0; +} + +/** + * Initialise UART + * + * @v uart UART + * @v baud Baud rate, or zero to leave unchanged + * @v lcr Line control register value, or zero to leave unchanged + * @ret rc Return status code + */ +int uart_init ( struct uart *uart, unsigned int baud, uint8_t lcr ) { + uint8_t dlm; + uint8_t dll; + int rc; + + /* Check for existence of UART */ + if ( ( rc = uart_exists ( uart ) ) != 0 ) + return rc; + + /* Configure divisor and line control register, if applicable */ + if ( ! lcr ) + lcr = uart_read ( uart, UART_LCR ); + uart->lcr = lcr; + uart_write ( uart, UART_LCR, ( lcr | UART_LCR_DLAB ) ); + if ( baud ) { + uart->divisor = ( UART_MAX_BAUD / baud ); + dlm = ( ( uart->divisor >> 8 ) & 0xff ); + dll = ( ( uart->divisor >> 0 ) & 0xff ); + uart_write ( uart, UART_DLM, dlm ); + uart_write ( uart, UART_DLL, dll ); + } else { + dlm = uart_read ( uart, UART_DLM ); + dll = uart_read ( uart, UART_DLL ); + uart->divisor = ( ( dlm << 8 ) | dll ); + } + uart_write ( uart, UART_LCR, ( lcr & ~UART_LCR_DLAB ) ); + + /* Disable interrupts */ + uart_write ( uart, UART_IER, 0 ); + + /* Enable FIFOs */ + uart_write ( uart, UART_FCR, UART_FCR_FE ); + + /* Assert DTR and RTS */ + uart_write ( uart, UART_MCR, ( UART_MCR_DTR | UART_MCR_RTS ) ); + + /* Flush any stale data */ + uart_flush ( uart ); + + return 0; +} diff --git a/qemu/roms/ipxe/src/core/uri.c b/qemu/roms/ipxe/src/core/uri.c index 9ec21cee4..3b5f270fe 100644 --- a/qemu/roms/ipxe/src/core/uri.c +++ b/qemu/roms/ipxe/src/core/uri.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -661,6 +665,7 @@ struct uri * resolve_uri ( const struct uri *base_uri, * Construct TFTP URI from next-server and filename * * @v next_server Next-server address + * @v port Port number, or zero to use the default port * @v filename Filename * @ret uri URI, or NULL on failure * @@ -669,12 +674,18 @@ struct uri * resolve_uri ( const struct uri *base_uri, * generic URI parser. We provide a mechanism for directly * constructing a TFTP URI from the next-server and filename. */ -struct uri * tftp_uri ( struct in_addr next_server, const char *filename ) { +struct uri * tftp_uri ( struct in_addr next_server, unsigned int port, + const char *filename ) { + char buf[ 6 /* "65535" + NUL */ ]; struct uri uri; memset ( &uri, 0, sizeof ( uri ) ); uri.scheme = "tftp"; uri.host = inet_ntoa ( next_server ); + if ( port ) { + snprintf ( buf, sizeof ( buf ), "%d", port ); + uri.port = buf; + } uri.path = filename; return uri_dup ( &uri ); } diff --git a/qemu/roms/ipxe/src/core/uuid.c b/qemu/roms/ipxe/src/core/uuid.c index 27a249da8..b8d21de17 100644 --- a/qemu/roms/ipxe/src/core/uuid.c +++ b/qemu/roms/ipxe/src/core/uuid.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/version.c b/qemu/roms/ipxe/src/core/version.c index 1e1e9daca..c984335c2 100644 --- a/qemu/roms/ipxe/src/core/version.c +++ b/qemu/roms/ipxe/src/core/version.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -29,6 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include /** * Create wide-character version of string diff --git a/qemu/roms/ipxe/src/core/vsprintf.c b/qemu/roms/ipxe/src/core/vsprintf.c index 54811b11b..cb3bec5dd 100644 --- a/qemu/roms/ipxe/src/core/vsprintf.c +++ b/qemu/roms/ipxe/src/core/vsprintf.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/core/wchar.c b/qemu/roms/ipxe/src/core/wchar.c index 7fabca470..860322820 100644 --- a/qemu/roms/ipxe/src/core/wchar.c +++ b/qemu/roms/ipxe/src/core/wchar.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/core/xfer.c b/qemu/roms/ipxe/src/core/xfer.c index 8d4bc9f53..112fee1bf 100644 --- a/qemu/roms/ipxe/src/core/xfer.c +++ b/qemu/roms/ipxe/src/core/xfer.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -134,18 +138,8 @@ size_t xfer_window ( struct interface *intf ) { * generating an xfer_window_changed() message. */ void xfer_window_changed ( struct interface *intf ) { - struct interface *dest; - xfer_window_changed_TYPE ( void * ) *op = - intf_get_dest_op ( intf, xfer_window_changed, &dest ); - void *object = intf_object ( dest ); - if ( op ) { - op ( object ); - } else { - /* Default is to do nothing */ - } - - intf_put ( dest ); + intf_poke ( intf, xfer_window_changed ); } /** @@ -365,3 +359,34 @@ int xfer_seek ( struct interface *intf, off_t offset ) { return xfer_deliver ( intf, iobuf, &meta ); } + +/** + * Check that data is delivered strictly in order + * + * @v meta Data transfer metadata + * @v pos Current position + * @v len Length of data + * @ret rc Return status code + */ +int xfer_check_order ( struct xfer_metadata *meta, size_t *pos, size_t len ) { + size_t new_pos; + + /* Allow out-of-order zero-length packets (as used by xfer_seek()) */ + if ( len == 0 ) + return 0; + + /* Calculate position of this delivery */ + new_pos = *pos; + if ( meta->flags & XFER_FL_ABS_OFFSET ) + new_pos = 0; + new_pos += meta->offset; + + /* Fail if delivery position is not equal to current position */ + if ( new_pos != *pos ) + return -EPROTO; + + /* Update current position */ + *pos += len; + + return 0; +} diff --git a/qemu/roms/ipxe/src/core/xferbuf.c b/qemu/roms/ipxe/src/core/xferbuf.c index a0457feee..240118557 100644 --- a/qemu/roms/ipxe/src/core/xferbuf.c +++ b/qemu/roms/ipxe/src/core/xferbuf.c @@ -15,15 +15,21 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include #include #include +#include +#include #include /** @file @@ -32,14 +38,26 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +/** Data delivery profiler */ +static struct profiler xferbuf_deliver_profiler __profiler = + { .name = "xferbuf.deliver" }; + +/** Data write profiler */ +static struct profiler xferbuf_write_profiler __profiler = + { .name = "xferbuf.write" }; + +/** Data read profiler */ +static struct profiler xferbuf_read_profiler __profiler = + { .name = "xferbuf.read" }; + /** - * Finish using data transfer buffer + * Free data transfer buffer * * @v xferbuf Data transfer buffer */ -void xferbuf_done ( struct xfer_buffer *xferbuf ) { - free ( xferbuf->data ); - xferbuf->data = NULL; +void xferbuf_free ( struct xfer_buffer *xferbuf ) { + + xferbuf->op->realloc ( xferbuf, 0 ); xferbuf->len = 0; xferbuf->pos = 0; } @@ -52,25 +70,77 @@ void xferbuf_done ( struct xfer_buffer *xferbuf ) { * @ret rc Return status code */ static int xferbuf_ensure_size ( struct xfer_buffer *xferbuf, size_t len ) { - void *new_data; + int rc; /* If buffer is already large enough, do nothing */ if ( len <= xferbuf->len ) return 0; /* Extend buffer */ - new_data = realloc ( xferbuf->data, len ); - if ( ! new_data ) { + if ( ( rc = xferbuf->op->realloc ( xferbuf, len ) ) != 0 ) { DBGC ( xferbuf, "XFERBUF %p could not extend buffer to " - "%zd bytes\n", xferbuf, len ); - return -ENOSPC; + "%zd bytes: %s\n", xferbuf, len, strerror ( rc ) ); + return rc; } - xferbuf->data = new_data; xferbuf->len = len; return 0; } +/** + * Write to data transfer buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to write + * @v len Length of data + */ +int xferbuf_write ( struct xfer_buffer *xferbuf, size_t offset, + const void *data, size_t len ) { + size_t max_len; + int rc; + + /* Check for overflow */ + max_len = ( offset + len ); + if ( max_len < offset ) + return -EOVERFLOW; + + /* Ensure buffer is large enough to contain this write */ + if ( ( rc = xferbuf_ensure_size ( xferbuf, max_len ) ) != 0 ) + return rc; + + /* Copy data to buffer */ + profile_start ( &xferbuf_write_profiler ); + xferbuf->op->write ( xferbuf, offset, data, len ); + profile_stop ( &xferbuf_write_profiler ); + + return 0; +} + +/** + * Read from data transfer buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to write + * @v len Length of data + */ +int xferbuf_read ( struct xfer_buffer *xferbuf, size_t offset, + void *data, size_t len ) { + + /* Check that read is within buffer range */ + if ( ( offset > xferbuf->len ) || + ( len > ( xferbuf->len - offset ) ) ) + return -ENOENT; + + /* Copy data from buffer */ + profile_start ( &xferbuf_read_profiler ); + xferbuf->op->read ( xferbuf, offset, data, len ); + profile_stop ( &xferbuf_read_profiler ); + + return 0; +} + /** * Add received data to data transfer buffer * @@ -81,28 +151,174 @@ static int xferbuf_ensure_size ( struct xfer_buffer *xferbuf, size_t len ) { */ int xferbuf_deliver ( struct xfer_buffer *xferbuf, struct io_buffer *iobuf, struct xfer_metadata *meta ) { - size_t len; - size_t max; + size_t len = iob_len ( iobuf ); + size_t pos; int rc; + /* Start profiling */ + profile_start ( &xferbuf_deliver_profiler ); + /* Calculate new buffer position */ + pos = xferbuf->pos; if ( meta->flags & XFER_FL_ABS_OFFSET ) - xferbuf->pos = 0; - xferbuf->pos += meta->offset; + pos = 0; + pos += meta->offset; - /* Ensure that we have enough buffer space for this data */ - len = iob_len ( iobuf ); - max = ( xferbuf->pos + len ); - if ( ( rc = xferbuf_ensure_size ( xferbuf, max ) ) != 0 ) + /* Write data to buffer */ + if ( ( rc = xferbuf_write ( xferbuf, pos, iobuf->data, len ) ) != 0 ) goto done; - /* Copy data to buffer */ - memcpy ( ( xferbuf->data + xferbuf->pos ), iobuf->data, len ); - /* Update current buffer position */ - xferbuf->pos += len; + xferbuf->pos = ( pos + len ); done: free_iob ( iobuf ); + profile_stop ( &xferbuf_deliver_profiler ); return rc; } + +/** + * Reallocate malloc()-based data buffer + * + * @v xferbuf Data transfer buffer + * @v len New length (or zero to free buffer) + * @ret rc Return status code + */ +static int xferbuf_malloc_realloc ( struct xfer_buffer *xferbuf, size_t len ) { + void *new_data; + + new_data = realloc ( xferbuf->data, len ); + if ( ! new_data ) + return -ENOSPC; + xferbuf->data = new_data; + return 0; +} + +/** + * Write data to malloc()-based data buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to copy + * @v len Length of data + */ +static void xferbuf_malloc_write ( struct xfer_buffer *xferbuf, size_t offset, + const void *data, size_t len ) { + + memcpy ( ( xferbuf->data + offset ), data, len ); +} + +/** + * Read data from malloc()-based data buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to read + * @v len Length of data + */ +static void xferbuf_malloc_read ( struct xfer_buffer *xferbuf, size_t offset, + void *data, size_t len ) { + + memcpy ( data, ( xferbuf->data + offset ), len ); +} + +/** malloc()-based data buffer operations */ +struct xfer_buffer_operations xferbuf_malloc_operations = { + .realloc = xferbuf_malloc_realloc, + .write = xferbuf_malloc_write, + .read = xferbuf_malloc_read, +}; + +/** + * Reallocate umalloc()-based data buffer + * + * @v xferbuf Data transfer buffer + * @v len New length (or zero to free buffer) + * @ret rc Return status code + */ +static int xferbuf_umalloc_realloc ( struct xfer_buffer *xferbuf, size_t len ) { + userptr_t *udata = xferbuf->data; + userptr_t new_udata; + + new_udata = urealloc ( *udata, len ); + if ( ! new_udata ) + return -ENOSPC; + *udata = new_udata; + return 0; +} + +/** + * Write data to umalloc()-based data buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to copy + * @v len Length of data + */ +static void xferbuf_umalloc_write ( struct xfer_buffer *xferbuf, size_t offset, + const void *data, size_t len ) { + userptr_t *udata = xferbuf->data; + + copy_to_user ( *udata, offset, data, len ); +} + +/** + * Read data from umalloc()-based data buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to read + * @v len Length of data + */ +static void xferbuf_umalloc_read ( struct xfer_buffer *xferbuf, size_t offset, + void *data, size_t len ) { + userptr_t *udata = xferbuf->data; + + copy_from_user ( data, *udata, offset, len ); +} + +/** umalloc()-based data buffer operations */ +struct xfer_buffer_operations xferbuf_umalloc_operations = { + .realloc = xferbuf_umalloc_realloc, + .write = xferbuf_umalloc_write, + .read = xferbuf_umalloc_read, +}; + +/** + * Get underlying data transfer buffer + * + * @v interface Data transfer interface + * @ret xferbuf Data transfer buffer, or NULL on error + * + * This call will check that the xfer_buffer() handler belongs to the + * destination interface which also provides xfer_deliver() for this + * interface. + * + * This is done to prevent accidental accesses to a data transfer + * buffer which may be located behind a non-transparent datapath via a + * series of pass-through interfaces. + */ +struct xfer_buffer * xfer_buffer ( struct interface *intf ) { + struct interface *dest; + xfer_buffer_TYPE ( void * ) *op = + intf_get_dest_op ( intf, xfer_buffer, &dest ); + void *object = intf_object ( dest ); + struct interface *xfer_deliver_dest; + struct xfer_buffer *xferbuf; + + /* Check that this operation is provided by the same interface + * which handles xfer_deliver(). + */ + ( void ) intf_get_dest_op ( intf, xfer_deliver, &xfer_deliver_dest ); + + if ( op && ( dest == xfer_deliver_dest ) ) { + xferbuf = op ( object ); + } else { + /* Default is to not have a data transfer buffer */ + xferbuf = NULL; + } + + intf_put ( xfer_deliver_dest ); + intf_put ( dest ); + return xferbuf; +} diff --git a/qemu/roms/ipxe/src/crypto/aes.c b/qemu/roms/ipxe/src/crypto/aes.c new file mode 100644 index 000000000..b9e206bfb --- /dev/null +++ b/qemu/roms/ipxe/src/crypto/aes.c @@ -0,0 +1,808 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * AES algorithm + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** AES strides + * + * These are the strides (modulo 16) used to walk through the AES + * input state bytes in order of byte position after [Inv]ShiftRows. + */ +enum aes_stride { + /** Input stride for ShiftRows + * + * 0 4 8 c + * \ \ \ + * 1 5 9 d + * \ \ \ + * 2 6 a e + * \ \ \ + * 3 7 b f + */ + AES_STRIDE_SHIFTROWS = +5, + /** Input stride for InvShiftRows + * + * 0 4 8 c + * / / / + * 1 5 9 d + * / / / + * 2 6 a e + * / / / + * 3 7 b f + */ + AES_STRIDE_INVSHIFTROWS = -3, +}; + +/** A single AES lookup table entry + * + * This represents the product (in the Galois field GF(2^8)) of an + * eight-byte vector multiplier with a single scalar multiplicand. + * + * The vector multipliers used for AES will be {1,1,1,3,2,1,1,3} for + * MixColumns and {1,9,13,11,14,9,13,11} for InvMixColumns. This + * allows for the result of multiplying any single column of the + * [Inv]MixColumns matrix by a scalar value to be obtained simply by + * extracting the relevant four-byte subset from the lookup table + * entry. + * + * For example, to find the result of multiplying the second column of + * the MixColumns matrix by the scalar value 0x80: + * + * MixColumns column[0]: { 2, 1, 1, 3 } + * MixColumns column[1]: { 3, 2, 1, 1 } + * MixColumns column[2]: { 1, 3, 2, 1 } + * MixColumns column[3]: { 1, 1, 3, 2 } + * Vector multiplier: { 1, 1, 1, 3, 2, 1, 1, 3 } + * Scalar multiplicand: 0x80 + * Lookup table entry: { 0x80, 0x80, 0x80, 0x9b, 0x1b, 0x80, 0x80, 0x9b } + * + * The second column of the MixColumns matrix is {3,2,1,1}. The + * product of this column with the scalar value 0x80 can be obtained + * by extracting the relevant four-byte subset of the lookup table + * entry: + * + * MixColumns column[1]: { 3, 2, 1, 1 } + * Vector multiplier: { 1, 1, 1, 3, 2, 1, 1, 3 } + * Lookup table entry: { 0x80, 0x80, 0x80, 0x9b, 0x1b, 0x80, 0x80, 0x9b } + * Product: { 0x9b, 0x1b, 0x80, 0x80 } + * + * The column lookups require only seven bytes of the eight-byte + * entry: the remaining (first) byte is used to hold the scalar + * multiplicand itself (i.e. the first byte of the vector multiplier + * is always chosen to be 1). + */ +union aes_table_entry { + /** Viewed as an array of bytes */ + uint8_t byte[8]; +} __attribute__ (( packed )); + +/** An AES lookup table + * + * This represents the products (in the Galois field GF(2^8)) of a + * constant eight-byte vector multiplier with all possible 256 scalar + * multiplicands. + * + * The entries are indexed by the AES [Inv]SubBytes S-box output + * values (denoted S(N)). This allows for the result of multiplying + * any single column of the [Inv]MixColumns matrix by S(N) to be + * obtained simply by extracting the relevant four-byte subset from + * the Nth table entry. For example: + * + * Input byte (N): 0x3a + * SubBytes output S(N): 0x80 + * MixColumns column[1]: { 3, 2, 1, 1 } + * Vector multiplier: { 1, 1, 1, 3, 2, 1, 1, 3 } + * Table entry[0x3a]: { 0x80, 0x80, 0x80, 0x9b, 0x1b, 0x80, 0x80, 0x9b } + * Product: { 0x9b, 0x1b, 0x80, 0x80 } + * + * Since the first byte of the eight-byte vector multiplier is always + * chosen to be 1, the value of S(N) may be lookup up by extracting + * the first byte of the Nth table entry. + */ +struct aes_table { + /** Table entries, indexed by S(N) */ + union aes_table_entry entry[256]; +} __attribute__ (( aligned ( 8 ) )); + +/** AES MixColumns lookup table */ +static struct aes_table aes_mixcolumns; + +/** AES InvMixColumns lookup table */ +static struct aes_table aes_invmixcolumns; + +/** + * Multiply [Inv]MixColumns matrix column by scalar multiplicand + * + * @v entry AES lookup table entry for scalar multiplicand + * @v column [Inv]MixColumns matrix column index + * @ret product Product of matrix column with scalar multiplicand + */ +static inline __attribute__ (( always_inline )) uint32_t +aes_entry_column ( const union aes_table_entry *entry, unsigned int column ) { + const union { + uint8_t byte; + uint32_t column; + } __attribute__ (( may_alias )) *product; + + /* Locate relevant four-byte subset */ + product = container_of ( &entry->byte[ 4 - column ], + typeof ( *product ), byte ); + + /* Extract this four-byte subset */ + return product->column; +} + +/** + * Multiply [Inv]MixColumns matrix column by S-boxed input byte + * + * @v table AES lookup table + * @v stride AES row shift stride + * @v in AES input state + * @v offset Output byte offset (after [Inv]ShiftRows) + * @ret product Product of matrix column with S(input byte) + * + * Note that the specified offset is not the offset of the input byte; + * it is the offset of the output byte which corresponds to the input + * byte. This output byte offset is used to calculate both the input + * byte offset and to select the appropriate matric column. + * + * With a compile-time constant offset, this function will optimise + * down to a single "movzbl" (to extract the input byte) and will + * generate a single x86 memory reference expression which can then be + * used directly within a single "xorl" instruction. + */ +static inline __attribute__ (( always_inline )) uint32_t +aes_column ( const struct aes_table *table, size_t stride, + const union aes_matrix *in, size_t offset ) { + const union aes_table_entry *entry; + unsigned int byte; + + /* Extract input byte corresponding to this output byte offset + * (i.e. perform [Inv]ShiftRows). + */ + byte = in->byte[ ( stride * offset ) & 0xf ]; + + /* Locate lookup table entry for this input byte (i.e. perform + * [Inv]SubBytes). + */ + entry = &table->entry[byte]; + + /* Multiply appropriate matrix column by this input byte + * (i.e. perform [Inv]MixColumns). + */ + return aes_entry_column ( entry, ( offset & 0x3 ) ); +} + +/** + * Calculate intermediate round output column + * + * @v table AES lookup table + * @v stride AES row shift stride + * @v in AES input state + * @v key AES round key + * @v column Column index + * @ret output Output column value + */ +static inline __attribute__ (( always_inline )) uint32_t +aes_output ( const struct aes_table *table, size_t stride, + const union aes_matrix *in, const union aes_matrix *key, + unsigned int column ) { + size_t offset = ( column * 4 ); + + /* Perform [Inv]ShiftRows, [Inv]SubBytes, [Inv]MixColumns, and + * AddRoundKey for this column. The loop is unrolled to allow + * for the required compile-time constant optimisations. + */ + return ( aes_column ( table, stride, in, ( offset + 0 ) ) ^ + aes_column ( table, stride, in, ( offset + 1 ) ) ^ + aes_column ( table, stride, in, ( offset + 2 ) ) ^ + aes_column ( table, stride, in, ( offset + 3 ) ) ^ + key->column[column] ); +} + +/** + * Perform a single intermediate round + * + * @v table AES lookup table + * @v stride AES row shift stride + * @v in AES input state + * @v out AES output state + * @v key AES round key + */ +static inline __attribute__ (( always_inline )) void +aes_round ( const struct aes_table *table, size_t stride, + const union aes_matrix *in, union aes_matrix *out, + const union aes_matrix *key ) { + + /* Perform [Inv]ShiftRows, [Inv]SubBytes, [Inv]MixColumns, and + * AddRoundKey for all columns. The loop is unrolled to allow + * for the required compile-time constant optimisations. + */ + out->column[0] = aes_output ( table, stride, in, key, 0 ); + out->column[1] = aes_output ( table, stride, in, key, 1 ); + out->column[2] = aes_output ( table, stride, in, key, 2 ); + out->column[3] = aes_output ( table, stride, in, key, 3 ); +} + +/** + * Perform encryption intermediate rounds + * + * @v in AES input state + * @v out AES output state + * @v key Round keys + * @v rounds Number of rounds (must be odd) + * + * This function is deliberately marked as non-inlinable to ensure + * maximal availability of registers for GCC's register allocator, + * which has a tendency to otherwise spill performance-critical + * registers to the stack. + */ +static __attribute__ (( noinline )) void +aes_encrypt_rounds ( union aes_matrix *in, union aes_matrix *out, + const union aes_matrix *key, unsigned int rounds ) { + union aes_matrix *tmp; + + /* Perform intermediate rounds */ + do { + /* Perform one intermediate round */ + aes_round ( &aes_mixcolumns, AES_STRIDE_SHIFTROWS, + in, out, key++ ); + + /* Swap input and output states for next round */ + tmp = in; + in = out; + out = tmp; + + } while ( --rounds ); +} + +/** + * Perform decryption intermediate rounds + * + * @v in AES input state + * @v out AES output state + * @v key Round keys + * @v rounds Number of rounds (must be odd) + * + * As with aes_encrypt_rounds(), this function is deliberately marked + * as non-inlinable. + * + * This function could potentially use the same binary code as is used + * for encryption. To compensate for the difference between ShiftRows + * and InvShiftRows, half of the input byte offsets would have to be + * modifiable at runtime (half by an offset of +4/-4, half by an + * offset of -4/+4 for ShiftRows/InvShiftRows). This can be + * accomplished in x86 assembly within the number of available + * registers, but GCC's register allocator struggles to do so, + * resulting in a significant performance decrease due to registers + * being spilled to the stack. We therefore use two separate but very + * similar binary functions based on the same C source. + */ +static __attribute__ (( noinline )) void +aes_decrypt_rounds ( union aes_matrix *in, union aes_matrix *out, + const union aes_matrix *key, unsigned int rounds ) { + union aes_matrix *tmp; + + /* Perform intermediate rounds */ + do { + /* Perform one intermediate round */ + aes_round ( &aes_invmixcolumns, AES_STRIDE_INVSHIFTROWS, + in, out, key++ ); + + /* Swap input and output states for next round */ + tmp = in; + in = out; + out = tmp; + + } while ( --rounds ); +} + +/** + * Perform standalone AddRoundKey + * + * @v state AES state + * @v key AES round key + */ +static inline __attribute__ (( always_inline )) void +aes_addroundkey ( union aes_matrix *state, const union aes_matrix *key ) { + + state->column[0] ^= key->column[0]; + state->column[1] ^= key->column[1]; + state->column[2] ^= key->column[2]; + state->column[3] ^= key->column[3]; +} + +/** + * Perform final round + * + * @v table AES lookup table + * @v stride AES row shift stride + * @v in AES input state + * @v out AES output state + * @v key AES round key + */ +static void aes_final ( const struct aes_table *table, size_t stride, + const union aes_matrix *in, union aes_matrix *out, + const union aes_matrix *key ) { + const union aes_table_entry *entry; + unsigned int byte; + size_t out_offset; + size_t in_offset; + + /* Perform [Inv]ShiftRows and [Inv]SubBytes */ + for ( out_offset = 0, in_offset = 0 ; out_offset < 16 ; + out_offset++, in_offset = ( ( in_offset + stride ) & 0xf ) ) { + + /* Extract input byte (i.e. perform [Inv]ShiftRows) */ + byte = in->byte[in_offset]; + + /* Locate lookup table entry for this input byte + * (i.e. perform [Inv]SubBytes). + */ + entry = &table->entry[byte]; + + /* Store output byte */ + out->byte[out_offset] = entry->byte[0]; + } + + /* Perform AddRoundKey */ + aes_addroundkey ( out, key ); +} + +/** + * Encrypt data + * + * @v ctx Context + * @v src Data to encrypt + * @v dst Buffer for encrypted data + * @v len Length of data + */ +static void aes_encrypt ( void *ctx, const void *src, void *dst, size_t len ) { + struct aes_context *aes = ctx; + union aes_matrix buffer[2]; + union aes_matrix *in = &buffer[0]; + union aes_matrix *out = &buffer[1]; + unsigned int rounds = aes->rounds; + + /* Sanity check */ + assert ( len == sizeof ( *in ) ); + + /* Initialise input state */ + memcpy ( in, src, sizeof ( *in ) ); + + /* Perform initial round (AddRoundKey) */ + aes_addroundkey ( in, &aes->encrypt.key[0] ); + + /* Perform intermediate rounds (ShiftRows, SubBytes, + * MixColumns, AddRoundKey). + */ + aes_encrypt_rounds ( in, out, &aes->encrypt.key[1], ( rounds - 2 ) ); + in = out; + + /* Perform final round (ShiftRows, SubBytes, AddRoundKey) */ + out = dst; + aes_final ( &aes_mixcolumns, AES_STRIDE_SHIFTROWS, in, out, + &aes->encrypt.key[ rounds - 1 ] ); +} + +/** + * Decrypt data + * + * @v ctx Context + * @v src Data to decrypt + * @v dst Buffer for decrypted data + * @v len Length of data + */ +static void aes_decrypt ( void *ctx, const void *src, void *dst, size_t len ) { + struct aes_context *aes = ctx; + union aes_matrix buffer[2]; + union aes_matrix *in = &buffer[0]; + union aes_matrix *out = &buffer[1]; + unsigned int rounds = aes->rounds; + + /* Sanity check */ + assert ( len == sizeof ( *in ) ); + + /* Initialise input state */ + memcpy ( in, src, sizeof ( *in ) ); + + /* Perform initial round (AddRoundKey) */ + aes_addroundkey ( in, &aes->decrypt.key[0] ); + + /* Perform intermediate rounds (InvShiftRows, InvSubBytes, + * InvMixColumns, AddRoundKey). + */ + aes_decrypt_rounds ( in, out, &aes->decrypt.key[1], ( rounds - 2 ) ); + in = out; + + /* Perform final round (InvShiftRows, InvSubBytes, AddRoundKey) */ + out = dst; + aes_final ( &aes_invmixcolumns, AES_STRIDE_INVSHIFTROWS, in, out, + &aes->decrypt.key[ rounds - 1 ] ); +} + +/** + * Multiply a polynomial by (x) modulo (x^8 + x^4 + x^3 + x^2 + 1) in GF(2^8) + * + * @v poly Polynomial to be multiplied + * @ret result Result + */ +static __attribute__ (( const )) unsigned int aes_double ( unsigned int poly ) { + + /* Multiply polynomial by (x), placing the resulting x^8 + * coefficient in the LSB (i.e. rotate byte left by one). + */ + poly = rol8 ( poly, 1 ); + + /* If coefficient of x^8 (in LSB) is non-zero, then reduce by + * subtracting (x^8 + x^4 + x^3 + x^2 + 1) in GF(2^8). + */ + if ( poly & 0x01 ) { + poly ^= 0x01; /* Subtract x^8 (currently in LSB) */ + poly ^= 0x1b; /* Subtract (x^4 + x^3 + x^2 + 1) */ + } + + return poly; +} + +/** + * Fill in MixColumns lookup table entry + * + * @v entry AES lookup table entry for scalar multiplicand + * + * The MixColumns lookup table vector multiplier is {1,1,1,3,2,1,1,3}. + */ +static void aes_mixcolumns_entry ( union aes_table_entry *entry ) { + unsigned int scalar_x_1; + unsigned int scalar_x; + unsigned int scalar; + + /* Retrieve scalar multiplicand */ + scalar = entry->byte[0]; + entry->byte[1] = scalar; + entry->byte[2] = scalar; + entry->byte[5] = scalar; + entry->byte[6] = scalar; + + /* Calculate scalar multiplied by (x) */ + scalar_x = aes_double ( scalar ); + entry->byte[4] = scalar_x; + + /* Calculate scalar multiplied by (x + 1) */ + scalar_x_1 = ( scalar_x ^ scalar ); + entry->byte[3] = scalar_x_1; + entry->byte[7] = scalar_x_1; +} + +/** + * Fill in InvMixColumns lookup table entry + * + * @v entry AES lookup table entry for scalar multiplicand + * + * The InvMixColumns lookup table vector multiplier is {1,9,13,11,14,9,13,11}. + */ +static void aes_invmixcolumns_entry ( union aes_table_entry *entry ) { + unsigned int scalar_x3_x2_x; + unsigned int scalar_x3_x2_1; + unsigned int scalar_x3_x2; + unsigned int scalar_x3_x_1; + unsigned int scalar_x3_1; + unsigned int scalar_x3; + unsigned int scalar_x2; + unsigned int scalar_x; + unsigned int scalar; + + /* Retrieve scalar multiplicand */ + scalar = entry->byte[0]; + + /* Calculate scalar multiplied by (x) */ + scalar_x = aes_double ( scalar ); + + /* Calculate scalar multiplied by (x^2) */ + scalar_x2 = aes_double ( scalar_x ); + + /* Calculate scalar multiplied by (x^3) */ + scalar_x3 = aes_double ( scalar_x2 ); + + /* Calculate scalar multiplied by (x^3 + 1) */ + scalar_x3_1 = ( scalar_x3 ^ scalar ); + entry->byte[1] = scalar_x3_1; + entry->byte[5] = scalar_x3_1; + + /* Calculate scalar multiplied by (x^3 + x + 1) */ + scalar_x3_x_1 = ( scalar_x3_1 ^ scalar_x ); + entry->byte[3] = scalar_x3_x_1; + entry->byte[7] = scalar_x3_x_1; + + /* Calculate scalar multiplied by (x^3 + x^2) */ + scalar_x3_x2 = ( scalar_x3 ^ scalar_x2 ); + + /* Calculate scalar multiplied by (x^3 + x^2 + 1) */ + scalar_x3_x2_1 = ( scalar_x3_x2 ^ scalar ); + entry->byte[2] = scalar_x3_x2_1; + entry->byte[6] = scalar_x3_x2_1; + + /* Calculate scalar multiplied by (x^3 + x^2 + x) */ + scalar_x3_x2_x = ( scalar_x3_x2 ^ scalar_x ); + entry->byte[4] = scalar_x3_x2_x; +} + +/** + * Generate AES lookup tables + * + */ +static void aes_generate ( void ) { + union aes_table_entry *entry; + union aes_table_entry *inventry; + unsigned int poly = 0x01; + unsigned int invpoly = 0x01; + unsigned int transformed; + unsigned int i; + + /* Iterate over non-zero values of GF(2^8) using generator (x + 1) */ + do { + + /* Multiply polynomial by (x + 1) */ + poly ^= aes_double ( poly ); + + /* Divide inverse polynomial by (x + 1). This code + * fragment is taken directly from the Wikipedia page + * on the Rijndael S-box. An explanation of why it + * works would be greatly appreciated. + */ + invpoly ^= ( invpoly << 1 ); + invpoly ^= ( invpoly << 2 ); + invpoly ^= ( invpoly << 4 ); + if ( invpoly & 0x80 ) + invpoly ^= 0x09; + invpoly &= 0xff; + + /* Apply affine transformation */ + transformed = ( 0x63 ^ invpoly ^ rol8 ( invpoly, 1 ) ^ + rol8 ( invpoly, 2 ) ^ rol8 ( invpoly, 3 ) ^ + rol8 ( invpoly, 4 ) ); + + /* Populate S-box (within MixColumns lookup table) */ + aes_mixcolumns.entry[poly].byte[0] = transformed; + + } while ( poly != 0x01 ); + + /* Populate zeroth S-box entry (which has no inverse) */ + aes_mixcolumns.entry[0].byte[0] = 0x63; + + /* Fill in MixColumns and InvMixColumns lookup tables */ + for ( i = 0 ; i < 256 ; i++ ) { + + /* Fill in MixColumns lookup table entry */ + entry = &aes_mixcolumns.entry[i]; + aes_mixcolumns_entry ( entry ); + + /* Populate inverse S-box (within InvMixColumns lookup table) */ + inventry = &aes_invmixcolumns.entry[ entry->byte[0] ]; + inventry->byte[0] = i; + + /* Fill in InvMixColumns lookup table entry */ + aes_invmixcolumns_entry ( inventry ); + } +} + +/** + * Rotate key column + * + * @v column Key column + * @ret column Updated key column + */ +static inline __attribute__ (( always_inline )) uint32_t +aes_key_rotate ( uint32_t column ) { + + return ( ( __BYTE_ORDER == __LITTLE_ENDIAN ) ? + ror32 ( column, 8 ) : rol32 ( column, 8 ) ); +} + +/** + * Apply S-box to key column + * + * @v column Key column + * @ret column Updated key column + */ +static uint32_t aes_key_sbox ( uint32_t column ) { + unsigned int i; + uint8_t byte; + + for ( i = 0 ; i < 4 ; i++ ) { + byte = ( column & 0xff ); + byte = aes_mixcolumns.entry[byte].byte[0]; + column = ( ( column & ~0xff ) | byte ); + column = rol32 ( column, 8 ); + } + return column; +} + +/** + * Apply schedule round constant to key column + * + * @v column Key column + * @v rcon Round constant + * @ret column Updated key column + */ +static inline __attribute__ (( always_inline )) uint32_t +aes_key_rcon ( uint32_t column, unsigned int rcon ) { + + return ( ( __BYTE_ORDER == __LITTLE_ENDIAN ) ? + ( column ^ rcon ) : ( column ^ ( rcon << 24 ) ) ); +} + +/** + * Set key + * + * @v ctx Context + * @v key Key + * @v keylen Key length + * @ret rc Return status code + */ +static int aes_setkey ( void *ctx, const void *key, size_t keylen ) { + struct aes_context *aes = ctx; + union aes_matrix *enc; + union aes_matrix *dec; + union aes_matrix temp; + union aes_matrix zero; + unsigned int rcon = 0x01; + unsigned int rounds; + size_t offset = 0; + uint32_t *prev; + uint32_t *next; + uint32_t *end; + uint32_t tmp; + + /* Generate lookup tables, if not already done */ + if ( ! aes_mixcolumns.entry[0].byte[0] ) + aes_generate(); + + /* Validate key length and calculate number of intermediate rounds */ + switch ( keylen ) { + case ( 128 / 8 ) : + rounds = 11; + break; + case ( 192 / 8 ) : + rounds = 13; + break; + case ( 256 / 8 ) : + rounds = 15; + break; + default: + DBGC ( aes, "AES %p unsupported key length (%zd bits)\n", + aes, ( keylen * 8 ) ); + return -EINVAL; + } + aes->rounds = rounds; + enc = aes->encrypt.key; + end = enc[rounds].column; + + /* Copy raw key */ + memcpy ( enc, key, keylen ); + prev = enc->column; + next = ( ( ( void * ) prev ) + keylen ); + tmp = next[-1]; + + /* Construct expanded key */ + while ( next < end ) { + + /* If this is the first column of an expanded key + * block, or the middle column of an AES-256 key + * block, then apply the S-box. + */ + if ( ( offset == 0 ) || ( ( offset | keylen ) == 48 ) ) + tmp = aes_key_sbox ( tmp ); + + /* If this is the first column of an expanded key + * block then rotate and apply the round constant. + */ + if ( offset == 0 ) { + tmp = aes_key_rotate ( tmp ); + tmp = aes_key_rcon ( tmp, rcon ); + rcon = aes_double ( rcon ); + } + + /* XOR with previous key column */ + tmp ^= *prev; + + /* Store column */ + *next = tmp; + + /* Move to next column */ + offset += sizeof ( *next ); + if ( offset == keylen ) + offset = 0; + next++; + prev++; + } + DBGC2 ( aes, "AES %p expanded %zd-bit key:\n", aes, ( keylen * 8 ) ); + DBGC2_HDA ( aes, 0, &aes->encrypt, ( rounds * sizeof ( *enc ) ) ); + + /* Convert to decryption key */ + memset ( &zero, 0, sizeof ( zero ) ); + dec = &aes->decrypt.key[ rounds - 1 ]; + memcpy ( dec--, enc++, sizeof ( *dec ) ); + while ( dec > aes->decrypt.key ) { + /* Perform InvMixColumns (by reusing the encryption + * final-round code to perform ShiftRows+SubBytes and + * reusing the decryption intermediate-round code to + * perform InvShiftRows+InvSubBytes+InvMixColumns, all + * with a zero encryption key). + */ + aes_final ( &aes_mixcolumns, AES_STRIDE_SHIFTROWS, + enc++, &temp, &zero ); + aes_decrypt_rounds ( &temp, dec--, &zero, 1 ); + } + memcpy ( dec--, enc++, sizeof ( *dec ) ); + DBGC2 ( aes, "AES %p inverted %zd-bit key:\n", aes, ( keylen * 8 ) ); + DBGC2_HDA ( aes, 0, &aes->decrypt, ( rounds * sizeof ( *dec ) ) ); + + return 0; +} + +/** + * Set initialisation vector + * + * @v ctx Context + * @v iv Initialisation vector + */ +static void aes_setiv ( void *ctx __unused, const void *iv __unused ) { + /* Nothing to do */ +} + +/** Basic AES algorithm */ +struct cipher_algorithm aes_algorithm = { + .name = "aes", + .ctxsize = sizeof ( struct aes_context ), + .blocksize = AES_BLOCKSIZE, + .setkey = aes_setkey, + .setiv = aes_setiv, + .encrypt = aes_encrypt, + .decrypt = aes_decrypt, +}; + +/* AES in Electronic Codebook mode */ +ECB_CIPHER ( aes_ecb, aes_ecb_algorithm, + aes_algorithm, struct aes_context, AES_BLOCKSIZE ); + +/* AES in Cipher Block Chaining mode */ +CBC_CIPHER ( aes_cbc, aes_cbc_algorithm, + aes_algorithm, struct aes_context, AES_BLOCKSIZE ); diff --git a/qemu/roms/ipxe/src/crypto/asn1.c b/qemu/roms/ipxe/src/crypto/asn1.c index 6d880704f..aca12bf30 100644 --- a/qemu/roms/ipxe/src/crypto/asn1.c +++ b/qemu/roms/ipxe/src/crypto/asn1.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/crypto/axtls/aes.c b/qemu/roms/ipxe/src/crypto/axtls/aes.c deleted file mode 100644 index bd99a7097..000000000 --- a/qemu/roms/ipxe/src/crypto/axtls/aes.c +++ /dev/null @@ -1,457 +0,0 @@ -/* - * Copyright (c) 2007, Cameron Rich - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of the axTLS project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * AES implementation - this is a small code version. There are much faster - * versions around but they are much larger in size (i.e. they use large - * submix tables). - */ - -#include -#include "os_port.h" -#include "crypto.h" - -/* all commented out in skeleton mode */ -#ifndef CONFIG_SSL_SKELETON_MODE - -#define rot1(x) (((x) << 24) | ((x) >> 8)) -#define rot2(x) (((x) << 16) | ((x) >> 16)) -#define rot3(x) (((x) << 8) | ((x) >> 24)) - -/* - * This cute trick does 4 'mul by two' at once. Stolen from - * Dr B. R. Gladman but I'm sure the u-(u>>7) is - * a standard graphics trick - * The key to this is that we need to xor with 0x1b if the top bit is set. - * a 1xxx xxxx 0xxx 0xxx First we mask the 7bit, - * b 1000 0000 0000 0000 then we shift right by 7 putting the 7bit in 0bit, - * c 0000 0001 0000 0000 we then subtract (c) from (b) - * d 0111 1111 0000 0000 and now we and with our mask - * e 0001 1011 0000 0000 - */ -#define mt 0x80808080 -#define ml 0x7f7f7f7f -#define mh 0xfefefefe -#define mm 0x1b1b1b1b -#define mul2(x,t) ((t)=((x)&mt), \ - ((((x)+(x))&mh)^(((t)-((t)>>7))&mm))) - -#define inv_mix_col(x,f2,f4,f8,f9) (\ - (f2)=mul2(x,f2), \ - (f4)=mul2(f2,f4), \ - (f8)=mul2(f4,f8), \ - (f9)=(x)^(f8), \ - (f8)=((f2)^(f4)^(f8)), \ - (f2)^=(f9), \ - (f4)^=(f9), \ - (f8)^=rot3(f2), \ - (f8)^=rot2(f4), \ - (f8)^rot1(f9)) - -/* - * AES S-box - */ -static const uint8_t aes_sbox[256] = -{ - 0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5, - 0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76, - 0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0, - 0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0, - 0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC, - 0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15, - 0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A, - 0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75, - 0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0, - 0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84, - 0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B, - 0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF, - 0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85, - 0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8, - 0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5, - 0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2, - 0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17, - 0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73, - 0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88, - 0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB, - 0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C, - 0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79, - 0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9, - 0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08, - 0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6, - 0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A, - 0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E, - 0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E, - 0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94, - 0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF, - 0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68, - 0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16, -}; - -/* - * AES is-box - */ -static const uint8_t aes_isbox[256] = -{ - 0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38, - 0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb, - 0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87, - 0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb, - 0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d, - 0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e, - 0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2, - 0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25, - 0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16, - 0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92, - 0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda, - 0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84, - 0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a, - 0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06, - 0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02, - 0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b, - 0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea, - 0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73, - 0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85, - 0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e, - 0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89, - 0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b, - 0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20, - 0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4, - 0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31, - 0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f, - 0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d, - 0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef, - 0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0, - 0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61, - 0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26, - 0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d -}; - -static const unsigned char Rcon[30]= -{ - 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80, - 0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f, - 0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4, - 0xb3,0x7d,0xfa,0xef,0xc5,0x91, -}; - -/* ----- static functions ----- */ -static void AES_encrypt(const AES_CTX *ctx, uint32_t *data); -static void AES_decrypt(const AES_CTX *ctx, uint32_t *data); - -/* Perform doubling in Galois Field GF(2^8) using the irreducible polynomial - x^8+x^4+x^3+x+1 */ -static unsigned char AES_xtime(uint32_t x) -{ - return (x&0x80) ? (x<<1)^0x1b : x<<1; -} - -/** - * Set up AES with the key/iv and cipher size. - */ -void AES_set_key(AES_CTX *ctx, const uint8_t *key, - const uint8_t *iv, AES_MODE mode) -{ - int i, ii; - uint32_t *W, tmp, tmp2; - const unsigned char *ip; - int words; - - switch (mode) - { - case AES_MODE_128: - i = 10; - words = 4; - break; - - case AES_MODE_256: - i = 14; - words = 8; - break; - - default: /* fail silently */ - return; - } - - ctx->rounds = i; - ctx->key_size = words; - W = ctx->ks; - for (i = 0; i < words; i+=2) - { - W[i+0]= ((uint32_t)key[ 0]<<24)| - ((uint32_t)key[ 1]<<16)| - ((uint32_t)key[ 2]<< 8)| - ((uint32_t)key[ 3] ); - W[i+1]= ((uint32_t)key[ 4]<<24)| - ((uint32_t)key[ 5]<<16)| - ((uint32_t)key[ 6]<< 8)| - ((uint32_t)key[ 7] ); - key += 8; - } - - ip = Rcon; - ii = 4 * (ctx->rounds+1); - for (i = words; i> 8)&0xff]<<16; - tmp2|=(uint32_t)aes_sbox[(tmp>>16)&0xff]<<24; - tmp2|=(uint32_t)aes_sbox[(tmp>>24) ]; - tmp=tmp2^(((unsigned int)*ip)<<24); - ip++; - } - - if ((words == 8) && ((i % words) == 4)) - { - tmp2 =(uint32_t)aes_sbox[(tmp )&0xff] ; - tmp2|=(uint32_t)aes_sbox[(tmp>> 8)&0xff]<< 8; - tmp2|=(uint32_t)aes_sbox[(tmp>>16)&0xff]<<16; - tmp2|=(uint32_t)aes_sbox[(tmp>>24) ]<<24; - tmp=tmp2; - } - - W[i]=W[i-words]^tmp; - } - - /* copy the iv across */ - memcpy(ctx->iv, iv, 16); -} - -/** - * Change a key for decryption. - */ -void AES_convert_key(AES_CTX *ctx) -{ - int i; - uint32_t *k,w,t1,t2,t3,t4; - - k = ctx->ks; - k += 4; - - for (i= ctx->rounds*4; i > 4; i--) - { - w= *k; - w = inv_mix_col(w,t1,t2,t3,t4); - *k++ =w; - } -} - -/** - * Encrypt a byte sequence (with a block size 16) using the AES cipher. - */ -void AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length) -{ - int i; - uint32_t tin[4], tout[4], iv[4]; - - memcpy(iv, ctx->iv, AES_IV_SIZE); - for (i = 0; i < 4; i++) - tout[i] = ntohl(iv[i]); - - for (length -= AES_BLOCKSIZE; length >= 0; length -= AES_BLOCKSIZE) - { - uint32_t msg_32[4]; - uint32_t out_32[4]; - memcpy(msg_32, msg, AES_BLOCKSIZE); - msg += AES_BLOCKSIZE; - - for (i = 0; i < 4; i++) - tin[i] = ntohl(msg_32[i])^tout[i]; - - AES_encrypt(ctx, tin); - - for (i = 0; i < 4; i++) - { - tout[i] = tin[i]; - out_32[i] = htonl(tout[i]); - } - - memcpy(out, out_32, AES_BLOCKSIZE); - out += AES_BLOCKSIZE; - } - - for (i = 0; i < 4; i++) - iv[i] = htonl(tout[i]); - memcpy(ctx->iv, iv, AES_IV_SIZE); -} - -/** - * Decrypt a byte sequence (with a block size 16) using the AES cipher. - */ -void AES_cbc_decrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length) -{ - int i; - uint32_t tin[4], xor[4], tout[4], data[4], iv[4]; - - memcpy(iv, ctx->iv, AES_IV_SIZE); - for (i = 0; i < 4; i++) - xor[i] = ntohl(iv[i]); - - for (length -= 16; length >= 0; length -= 16) - { - uint32_t msg_32[4]; - uint32_t out_32[4]; - memcpy(msg_32, msg, AES_BLOCKSIZE); - msg += AES_BLOCKSIZE; - - for (i = 0; i < 4; i++) - { - tin[i] = ntohl(msg_32[i]); - data[i] = tin[i]; - } - - AES_decrypt(ctx, data); - - for (i = 0; i < 4; i++) - { - tout[i] = data[i]^xor[i]; - xor[i] = tin[i]; - out_32[i] = htonl(tout[i]); - } - - memcpy(out, out_32, AES_BLOCKSIZE); - out += AES_BLOCKSIZE; - } - - for (i = 0; i < 4; i++) - iv[i] = htonl(xor[i]); - memcpy(ctx->iv, iv, AES_IV_SIZE); -} - -/** - * Encrypt a single block (16 bytes) of data - */ -static void AES_encrypt(const AES_CTX *ctx, uint32_t *data) -{ - /* To make this code smaller, generate the sbox entries on the fly. - * This will have a really heavy effect upon performance. - */ - uint32_t tmp[4]; - uint32_t tmp1, old_a0, a0, a1, a2, a3, row; - int curr_rnd; - int rounds = ctx->rounds; - const uint32_t *k = ctx->ks; - - /* Pre-round key addition */ - for (row = 0; row < 4; row++) - data[row] ^= *(k++); - - /* Encrypt one block. */ - for (curr_rnd = 0; curr_rnd < rounds; curr_rnd++) - { - /* Perform ByteSub and ShiftRow operations together */ - for (row = 0; row < 4; row++) - { - a0 = (uint32_t)aes_sbox[(data[row%4]>>24)&0xFF]; - a1 = (uint32_t)aes_sbox[(data[(row+1)%4]>>16)&0xFF]; - a2 = (uint32_t)aes_sbox[(data[(row+2)%4]>>8)&0xFF]; - a3 = (uint32_t)aes_sbox[(data[(row+3)%4])&0xFF]; - - /* Perform MixColumn iff not last round */ - if (curr_rnd < (rounds - 1)) - { - tmp1 = a0 ^ a1 ^ a2 ^ a3; - old_a0 = a0; - a0 ^= tmp1 ^ AES_xtime(a0 ^ a1); - a1 ^= tmp1 ^ AES_xtime(a1 ^ a2); - a2 ^= tmp1 ^ AES_xtime(a2 ^ a3); - a3 ^= tmp1 ^ AES_xtime(a3 ^ old_a0); - } - - tmp[row] = ((a0 << 24) | (a1 << 16) | (a2 << 8) | a3); - } - - /* KeyAddition - note that it is vital that this loop is separate from - the MixColumn operation, which must be atomic...*/ - for (row = 0; row < 4; row++) - data[row] = tmp[row] ^ *(k++); - } -} - -/** - * Decrypt a single block (16 bytes) of data - */ -static void AES_decrypt(const AES_CTX *ctx, uint32_t *data) -{ - uint32_t tmp[4]; - uint32_t xt0,xt1,xt2,xt3,xt4,xt5,xt6; - uint32_t a0, a1, a2, a3, row; - int curr_rnd; - int rounds = ctx->rounds; - const uint32_t *k = ctx->ks + ((rounds+1)*4); - - /* pre-round key addition */ - for (row=4; row > 0;row--) - data[row-1] ^= *(--k); - - /* Decrypt one block */ - for (curr_rnd = 0; curr_rnd < rounds; curr_rnd++) - { - /* Perform ByteSub and ShiftRow operations together */ - for (row = 4; row > 0; row--) - { - a0 = aes_isbox[(data[(row+3)%4]>>24)&0xFF]; - a1 = aes_isbox[(data[(row+2)%4]>>16)&0xFF]; - a2 = aes_isbox[(data[(row+1)%4]>>8)&0xFF]; - a3 = aes_isbox[(data[row%4])&0xFF]; - - /* Perform MixColumn iff not last round */ - if (curr_rnd<(rounds-1)) - { - /* The MDS cofefficients (0x09, 0x0B, 0x0D, 0x0E) - are quite large compared to encryption; this - operation slows decryption down noticeably. */ - xt0 = AES_xtime(a0^a1); - xt1 = AES_xtime(a1^a2); - xt2 = AES_xtime(a2^a3); - xt3 = AES_xtime(a3^a0); - xt4 = AES_xtime(xt0^xt1); - xt5 = AES_xtime(xt1^xt2); - xt6 = AES_xtime(xt4^xt5); - - xt0 ^= a1^a2^a3^xt4^xt6; - xt1 ^= a0^a2^a3^xt5^xt6; - xt2 ^= a0^a1^a3^xt4^xt6; - xt3 ^= a0^a1^a2^xt5^xt6; - tmp[row-1] = ((xt0<<24)|(xt1<<16)|(xt2<<8)|xt3); - } - else - tmp[row-1] = ((a0<<24)|(a1<<16)|(a2<<8)|a3); - } - - for (row = 4; row > 0; row--) - data[row-1] = tmp[row-1] ^ *(--k); - } -} - -#endif diff --git a/qemu/roms/ipxe/src/crypto/axtls/bigint.h b/qemu/roms/ipxe/src/crypto/axtls/bigint.h deleted file mode 100644 index 1f38c53d6..000000000 --- a/qemu/roms/ipxe/src/crypto/axtls/bigint.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2007, Cameron Rich - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of the axTLS project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef BIGINT_HEADER -#define BIGINT_HEADER - -#include "crypto.h" - -BI_CTX *bi_initialize(void); -void bi_terminate(BI_CTX *ctx); -void bi_permanent(bigint *bi); -void bi_depermanent(bigint *bi); -void bi_clear_cache(BI_CTX *ctx); -void bi_free(BI_CTX *ctx, bigint *bi); -bigint *bi_copy(bigint *bi); -bigint *bi_clone(BI_CTX *ctx, const bigint *bi); -void bi_export(BI_CTX *ctx, bigint *bi, uint8_t *data, int size); -bigint *bi_import(BI_CTX *ctx, const uint8_t *data, int len); -bigint *int_to_bi(BI_CTX *ctx, comp i); - -/* the functions that actually do something interesting */ -bigint *bi_add(BI_CTX *ctx, bigint *bia, bigint *bib); -bigint *bi_subtract(BI_CTX *ctx, bigint *bia, - bigint *bib, int *is_negative); -bigint *bi_divide(BI_CTX *ctx, bigint *bia, bigint *bim, int is_mod); -bigint *bi_multiply(BI_CTX *ctx, bigint *bia, bigint *bib); -bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp); -bigint *bi_mod_power2(BI_CTX *ctx, bigint *bi, bigint *bim, bigint *biexp); -int bi_compare(bigint *bia, bigint *bib); -void bi_set_mod(BI_CTX *ctx, bigint *bim, int mod_offset); -void bi_free_mod(BI_CTX *ctx, int mod_offset); - -#ifdef CONFIG_SSL_FULL_MODE -void bi_print(const char *label, bigint *bi); -bigint *bi_str_import(BI_CTX *ctx, const char *data); -#endif - -/** - * @def bi_mod - * Find the residue of B. bi_set_mod() must be called before hand. - */ -#define bi_mod(A, B) bi_divide(A, B, ctx->bi_mod[ctx->mod_offset], 1) - -/** - * bi_residue() is technically the same as bi_mod(), but it uses the - * appropriate reduction technique (which is bi_mod() when doing classical - * reduction). - */ -#if defined(CONFIG_BIGINT_MONTGOMERY) -#define bi_residue(A, B) bi_mont(A, B) -bigint *bi_mont(BI_CTX *ctx, bigint *bixy); -#elif defined(CONFIG_BIGINT_BARRETT) -#define bi_residue(A, B) bi_barrett(A, B) -bigint *bi_barrett(BI_CTX *ctx, bigint *bi); -#else /* if defined(CONFIG_BIGINT_CLASSICAL) */ -#define bi_residue(A, B) bi_mod(A, B) -#endif - -#ifdef CONFIG_BIGINT_SQUARE -bigint *bi_square(BI_CTX *ctx, bigint *bi); -#else -#define bi_square(A, B) bi_multiply(A, bi_copy(B), B) -#endif - -#ifdef CONFIG_BIGINT_CRT -bigint *bi_crt(BI_CTX *ctx, bigint *bi, - bigint *dP, bigint *dQ, - bigint *p, bigint *q, - bigint *qInv); -#endif - -#endif diff --git a/qemu/roms/ipxe/src/crypto/axtls/bigint_impl.h b/qemu/roms/ipxe/src/crypto/axtls/bigint_impl.h deleted file mode 100644 index 09d8550ea..000000000 --- a/qemu/roms/ipxe/src/crypto/axtls/bigint_impl.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2007, Cameron Rich - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of the axTLS project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef BIGINT_IMPL_HEADER -#define BIGINT_IMPL_HEADER - -/* Maintain a number of precomputed variables when doing reduction */ -#define BIGINT_M_OFFSET 0 /**< Normal modulo offset. */ -#ifdef CONFIG_BIGINT_CRT -#define BIGINT_P_OFFSET 1 /**< p modulo offset. */ -#define BIGINT_Q_OFFSET 2 /**< q module offset. */ -#define BIGINT_NUM_MODS 3 /**< The number of modulus constants used. */ -#else -#define BIGINT_NUM_MODS 1 -#endif - -/* Architecture specific functions for big ints */ -#if defined(CONFIG_INTEGER_8BIT) -#define COMP_RADIX 256U /**< Max component + 1 */ -#define COMP_MAX 0xFFFFU/**< (Max dbl comp -1) */ -#define COMP_BIT_SIZE 8 /**< Number of bits in a component. */ -#define COMP_BYTE_SIZE 1 /**< Number of bytes in a component. */ -#define COMP_NUM_NIBBLES 2 /**< Used For diagnostics only. */ -typedef uint8_t comp; /**< A single precision component. */ -typedef uint16_t long_comp; /**< A double precision component. */ -typedef int16_t slong_comp; /**< A signed double precision component. */ -#elif defined(CONFIG_INTEGER_16BIT) -#define COMP_RADIX 65536U /**< Max component + 1 */ -#define COMP_MAX 0xFFFFFFFFU/**< (Max dbl comp -1) */ -#define COMP_BIT_SIZE 16 /**< Number of bits in a component. */ -#define COMP_BYTE_SIZE 2 /**< Number of bytes in a component. */ -#define COMP_NUM_NIBBLES 4 /**< Used For diagnostics only. */ -typedef uint16_t comp; /**< A single precision component. */ -typedef uint32_t long_comp; /**< A double precision component. */ -typedef int32_t slong_comp; /**< A signed double precision component. */ -#else /* regular 32 bit */ -#ifdef WIN32 -#define COMP_RADIX 4294967296i64 -#define COMP_MAX 0xFFFFFFFFFFFFFFFFui64 -#else -#define COMP_RADIX 4294967296ULL /**< Max component + 1 */ -#define COMP_MAX 0xFFFFFFFFFFFFFFFFULL/**< (Max dbl comp -1) */ -#endif -#define COMP_BIT_SIZE 32 /**< Number of bits in a component. */ -#define COMP_BYTE_SIZE 4 /**< Number of bytes in a component. */ -#define COMP_NUM_NIBBLES 8 /**< Used For diagnostics only. */ -typedef uint32_t comp; /**< A single precision component. */ -typedef uint64_t long_comp; /**< A double precision component. */ -typedef int64_t slong_comp; /**< A signed double precision component. */ -#endif - -/** - * @struct _bigint - * @brief A big integer basic object - */ -struct _bigint -{ - struct _bigint* next; /**< The next bigint in the cache. */ - short size; /**< The number of components in this bigint. */ - short max_comps; /**< The heapsize allocated for this bigint */ - int refs; /**< An internal reference count. */ - comp* comps; /**< A ptr to the actual component data */ -}; - -typedef struct _bigint bigint; /**< An alias for _bigint */ - -/** - * Maintains the state of the cache, and a number of variables used in - * reduction. - */ -typedef struct /**< A big integer "session" context. */ -{ - bigint *active_list; /**< Bigints currently used. */ - bigint *free_list; /**< Bigints not used. */ - bigint *bi_radix; /**< The radix used. */ - bigint *bi_mod[BIGINT_NUM_MODS]; /**< modulus */ - -#if defined(CONFIG_BIGINT_MONTGOMERY) - bigint *bi_RR_mod_m[BIGINT_NUM_MODS]; /**< R^2 mod m */ - bigint *bi_R_mod_m[BIGINT_NUM_MODS]; /**< R mod m */ - comp N0_dash[BIGINT_NUM_MODS]; -#elif defined(CONFIG_BIGINT_BARRETT) - bigint *bi_mu[BIGINT_NUM_MODS]; /**< Storage for mu */ -#endif - bigint *bi_normalised_mod[BIGINT_NUM_MODS]; /**< Normalised mod storage. */ - bigint **g; /**< Used by sliding-window. */ - int window; /**< The size of the sliding window */ - int active_count; /**< Number of active bigints. */ - int free_count; /**< Number of free bigints. */ - -#ifdef CONFIG_BIGINT_MONTGOMERY - uint8_t use_classical; /**< Use classical reduction. */ -#endif - uint8_t mod_offset; /**< The mod offset we are using */ -} BI_CTX; - -#ifndef WIN32 -#define max(a,b) ((a)>(b)?(a):(b)) /**< Find the maximum of 2 numbers. */ -#define min(a,b) ((a)<(b)?(a):(b)) /**< Find the minimum of 2 numbers. */ -#endif - -#define PERMANENT 0x7FFF55AA /**< A magic number for permanents. */ - -#endif diff --git a/qemu/roms/ipxe/src/crypto/axtls/config.h b/qemu/roms/ipxe/src/crypto/axtls/config.h deleted file mode 100644 index 32fa3bf03..000000000 --- a/qemu/roms/ipxe/src/crypto/axtls/config.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef AXTLS_CONFIG_H -#define AXTLS_CONFIG_H - -/** - * @file config.h - * - * Trick the axtls code into building within our build environment. - */ - -#define CONFIG_SSL_ENABLE_CLIENT 1 -#define CONFIG_BIGINT_CLASSICAL 1 - -#endif diff --git a/qemu/roms/ipxe/src/crypto/axtls/crypto.h b/qemu/roms/ipxe/src/crypto/axtls/crypto.h deleted file mode 100644 index 2c4cda4de..000000000 --- a/qemu/roms/ipxe/src/crypto/axtls/crypto.h +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (c) 2007, Cameron Rich - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of the axTLS project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file crypto.h - */ - -#ifndef HEADER_CRYPTO_H -#define HEADER_CRYPTO_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "config.h" -#include "bigint_impl.h" -#include "bigint.h" - -#ifndef STDCALL -#define STDCALL -#endif -#ifndef EXP_FUNC -#define EXP_FUNC -#endif - - -/* enable features based on a 'super-set' capbaility. */ -#if defined(CONFIG_SSL_FULL_MODE) -#define CONFIG_SSL_ENABLE_CLIENT -#define CONFIG_SSL_CERT_VERIFICATION -#elif defined(CONFIG_SSL_ENABLE_CLIENT) -#define CONFIG_SSL_CERT_VERIFICATION -#endif - -/************************************************************************** - * AES declarations - **************************************************************************/ - -#define AES_MAXROUNDS 14 -#define AES_BLOCKSIZE 16 -#define AES_IV_SIZE 16 - -typedef struct aes_key_st -{ - uint16_t rounds; - uint16_t key_size; - uint32_t ks[(AES_MAXROUNDS+1)*8]; - uint8_t iv[AES_IV_SIZE]; -} AES_CTX; - -typedef enum -{ - AES_MODE_128, - AES_MODE_256 -} AES_MODE; - -void AES_set_key(AES_CTX *ctx, const uint8_t *key, - const uint8_t *iv, AES_MODE mode); -void AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg, - uint8_t *out, int length); -void AES_cbc_decrypt(AES_CTX *ks, const uint8_t *in, uint8_t *out, int length); -void AES_convert_key(AES_CTX *ctx); - -/************************************************************************** - * RC4 declarations - **************************************************************************/ - -typedef struct -{ - uint8_t x, y, m[256]; -} RC4_CTX; - -void RC4_setup(RC4_CTX *s, const uint8_t *key, int length); -void RC4_crypt(RC4_CTX *s, const uint8_t *msg, uint8_t *data, int length); - -/************************************************************************** - * SHA1 declarations - **************************************************************************/ - -#define SHA1_SIZE 20 - -/* - * This structure will hold context information for the SHA-1 - * hashing operation - */ -typedef struct -{ - uint32_t Intermediate_Hash[SHA1_SIZE/4]; /* Message Digest */ - uint32_t Length_Low; /* Message length in bits */ - uint32_t Length_High; /* Message length in bits */ - uint16_t Message_Block_Index; /* Index into message block array */ - uint8_t Message_Block[64]; /* 512-bit message blocks */ -} SHA1_CTX; - -void SHA1_Init(SHA1_CTX *); -void SHA1_Update(SHA1_CTX *, const uint8_t * msg, int len); -void SHA1_Final(uint8_t *digest, SHA1_CTX *); - -/************************************************************************** - * MD2 declarations - **************************************************************************/ - -#define MD2_SIZE 16 - -typedef struct -{ - unsigned char cksum[16]; /* checksum of the data block */ - unsigned char state[48]; /* intermediate digest state */ - unsigned char buffer[16]; /* data block being processed */ - int left; /* amount of data in buffer */ -} MD2_CTX; - -EXP_FUNC void STDCALL MD2_Init(MD2_CTX *ctx); -EXP_FUNC void STDCALL MD2_Update(MD2_CTX *ctx, const uint8_t *input, int ilen); -EXP_FUNC void STDCALL MD2_Final(uint8_t *digest, MD2_CTX *ctx); - -/************************************************************************** - * MD5 declarations - **************************************************************************/ - -#define MD5_SIZE 16 - -typedef struct -{ - uint32_t state[4]; /* state (ABCD) */ - uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ - uint8_t buffer[64]; /* input buffer */ -} MD5_CTX; - -EXP_FUNC void STDCALL MD5_Init(MD5_CTX *); -EXP_FUNC void STDCALL MD5_Update(MD5_CTX *, const uint8_t *msg, int len); -EXP_FUNC void STDCALL MD5_Final(uint8_t *digest, MD5_CTX *); - -/************************************************************************** - * HMAC declarations - **************************************************************************/ -void hmac_md5(const uint8_t *msg, int length, const uint8_t *key, - int key_len, uint8_t *digest); -void hmac_sha1(const uint8_t *msg, int length, const uint8_t *key, - int key_len, uint8_t *digest); - -/************************************************************************** - * RSA declarations - **************************************************************************/ - -typedef struct -{ - bigint *m; /* modulus */ - bigint *e; /* public exponent */ - bigint *d; /* private exponent */ -#ifdef CONFIG_BIGINT_CRT - bigint *p; /* p as in m = pq */ - bigint *q; /* q as in m = pq */ - bigint *dP; /* d mod (p-1) */ - bigint *dQ; /* d mod (q-1) */ - bigint *qInv; /* q^-1 mod p */ -#endif - int num_octets; - BI_CTX *bi_ctx; -} RSA_CTX; - -void RSA_priv_key_new(RSA_CTX **rsa_ctx, - const uint8_t *modulus, int mod_len, - const uint8_t *pub_exp, int pub_len, - const uint8_t *priv_exp, int priv_len -#ifdef CONFIG_BIGINT_CRT - , const uint8_t *p, int p_len, - const uint8_t *q, int q_len, - const uint8_t *dP, int dP_len, - const uint8_t *dQ, int dQ_len, - const uint8_t *qInv, int qInv_len -#endif - ); -void RSA_pub_key_new(RSA_CTX **rsa_ctx, - const uint8_t *modulus, int mod_len, - const uint8_t *pub_exp, int pub_len); -void RSA_free(RSA_CTX *ctx); -int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data, - int is_decryption); -bigint *RSA_private(const RSA_CTX *c, bigint *bi_msg); -#if defined(CONFIG_SSL_CERT_VERIFICATION) || defined(CONFIG_SSL_GENERATE_X509_CERT) -bigint *RSA_sign_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len, - bigint *modulus, bigint *pub_exp); -bigint *RSA_public(const RSA_CTX * c, bigint *bi_msg); -int RSA_encrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len, - uint8_t *out_data, int is_signing); -void RSA_print(const RSA_CTX *ctx); -#endif - -/************************************************************************** - * RNG declarations - **************************************************************************/ -EXP_FUNC void STDCALL RNG_initialize(const uint8_t *seed_buf, int size); -EXP_FUNC void STDCALL RNG_terminate(void); -EXP_FUNC void STDCALL get_random(int num_rand_bytes, uint8_t *rand_data); -void get_random_NZ(int num_rand_bytes, uint8_t *rand_data); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/qemu/roms/ipxe/src/crypto/axtls/os_port.h b/qemu/roms/ipxe/src/crypto/axtls/os_port.h deleted file mode 100644 index 76313e204..000000000 --- a/qemu/roms/ipxe/src/crypto/axtls/os_port.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef AXTLS_OS_PORT_H -#define AXTLS_OS_PORT_H - -/** - * @file os_port.h - * - * Trick the axtls code into building within our build environment. - */ - -#include -#include - -/** All imported axTLS files are licensed using the three-clause BSD licence */ -FILE_LICENCE ( BSD3 ); - -/** We can't actually abort, since we are effectively a kernel... */ -#define abort() assert ( 0 ) - -/** rsa.c uses alloca() */ -#define alloca( size ) __builtin_alloca ( size ) - -#include -static inline void get_random_NZ ( int num_rand_bytes, uint8_t *rand_data ) { - /* AXTLS does not check for failures when generating random - * data. Rely on the fact that get_random_nz() does not - * request prediction resistance (and so cannot introduce new - * failures) and therefore any potential failure must already - * have been encountered by e.g. tls_generate_random(), which - * does check for failures. - */ - get_random_nz ( rand_data, num_rand_bytes ); -} - -/* Expose AES_encrypt() and AES_decrypt() in aes.o */ -#define aes 1 -#if OBJECT - -struct aes_key_st; - -static void AES_encrypt ( const struct aes_key_st *ctx, uint32_t *data ); -static void AES_decrypt ( const struct aes_key_st *ctx, uint32_t *data ); - -void axtls_aes_encrypt ( void *ctx, uint32_t *data ) { - AES_encrypt ( ctx, data ); -} - -void axtls_aes_decrypt ( void *ctx, uint32_t *data ) { - AES_decrypt ( ctx, data ); -} - -#endif -#undef aes - -#endif diff --git a/qemu/roms/ipxe/src/crypto/axtls_aes.c b/qemu/roms/ipxe/src/crypto/axtls_aes.c deleted file mode 100644 index 7f93c0ed7..000000000 --- a/qemu/roms/ipxe/src/crypto/axtls_aes.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2007 Michael Brown . - * - * 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 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include -#include -#include -#include -#include -#include -#include -#include "crypto/axtls/crypto.h" - -/** @file - * - * AES algorithm - * - */ - -/** - * Set key - * - * @v ctx Context - * @v key Key - * @v keylen Key length - * @ret rc Return status code - */ -static int aes_setkey ( void *ctx, const void *key, size_t keylen ) { - struct aes_context *aes_ctx = ctx; - AES_MODE mode; - void *iv; - - switch ( keylen ) { - case ( 128 / 8 ): - mode = AES_MODE_128; - break; - case ( 256 / 8 ): - mode = AES_MODE_256; - break; - default: - return -EINVAL; - } - - /* IV is not a relevant concept at this stage; use a dummy - * value that will have no side-effects. - */ - iv = &aes_ctx->axtls_ctx.iv; - - AES_set_key ( &aes_ctx->axtls_ctx, key, iv, mode ); - - aes_ctx->decrypting = 0; - - return 0; -} - -/** - * Set initialisation vector - * - * @v ctx Context - * @v iv Initialisation vector - */ -static void aes_setiv ( void *ctx __unused, const void *iv __unused ) { - /* Nothing to do */ -} - -/** - * Call AXTLS' AES_encrypt() or AES_decrypt() functions - * - * @v axtls_ctx AXTLS AES context - * @v src Data to process - * @v dst Buffer for output - * @v func AXTLS AES function to call - */ -static void aes_call_axtls ( AES_CTX *axtls_ctx, const void *src, void *dst, - void ( * func ) ( const AES_CTX *axtls_ctx, - uint32_t *data ) ){ - const uint32_t *srcl = src; - uint32_t *dstl = dst; - unsigned int i; - - /* AXTLS' AES_encrypt() and AES_decrypt() functions both - * expect to deal with an array of four dwords in host-endian - * order. - */ - for ( i = 0 ; i < 4 ; i++ ) - dstl[i] = ntohl ( srcl[i] ); - func ( axtls_ctx, dstl ); - for ( i = 0 ; i < 4 ; i++ ) - dstl[i] = htonl ( dstl[i] ); -} - -/** - * Encrypt data - * - * @v ctx Context - * @v src Data to encrypt - * @v dst Buffer for encrypted data - * @v len Length of data - */ -static void aes_encrypt ( void *ctx, const void *src, void *dst, - size_t len ) { - struct aes_context *aes_ctx = ctx; - - assert ( len == AES_BLOCKSIZE ); - if ( aes_ctx->decrypting ) - assert ( 0 ); - aes_call_axtls ( &aes_ctx->axtls_ctx, src, dst, axtls_aes_encrypt ); -} - -/** - * Decrypt data - * - * @v ctx Context - * @v src Data to decrypt - * @v dst Buffer for decrypted data - * @v len Length of data - */ -static void aes_decrypt ( void *ctx, const void *src, void *dst, - size_t len ) { - struct aes_context *aes_ctx = ctx; - - assert ( len == AES_BLOCKSIZE ); - if ( ! aes_ctx->decrypting ) { - AES_convert_key ( &aes_ctx->axtls_ctx ); - aes_ctx->decrypting = 1; - } - aes_call_axtls ( &aes_ctx->axtls_ctx, src, dst, axtls_aes_decrypt ); -} - -/** Basic AES algorithm */ -struct cipher_algorithm aes_algorithm = { - .name = "aes", - .ctxsize = sizeof ( struct aes_context ), - .blocksize = AES_BLOCKSIZE, - .setkey = aes_setkey, - .setiv = aes_setiv, - .encrypt = aes_encrypt, - .decrypt = aes_decrypt, -}; - -/* AES with cipher-block chaining */ -CBC_CIPHER ( aes_cbc, aes_cbc_algorithm, - aes_algorithm, struct aes_context, AES_BLOCKSIZE ); diff --git a/qemu/roms/ipxe/src/crypto/bigint.c b/qemu/roms/ipxe/src/crypto/bigint.c index 340128e2f..50f320302 100644 --- a/qemu/roms/ipxe/src/crypto/bigint.c +++ b/qemu/roms/ipxe/src/crypto/bigint.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/crypto/cbc.c b/qemu/roms/ipxe/src/crypto/cbc.c index 9bf0e8b49..0ba17ee48 100644 --- a/qemu/roms/ipxe/src/crypto/cbc.c +++ b/qemu/roms/ipxe/src/crypto/cbc.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/crypto/certstore.c b/qemu/roms/ipxe/src/crypto/certstore.c index 77cf6ebb6..503ce499e 100644 --- a/qemu/roms/ipxe/src/crypto/certstore.c +++ b/qemu/roms/ipxe/src/crypto/certstore.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/crypto/chap.c b/qemu/roms/ipxe/src/crypto/chap.c index db64371c7..c90c16def 100644 --- a/qemu/roms/ipxe/src/crypto/chap.c +++ b/qemu/roms/ipxe/src/crypto/chap.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/crypto/cms.c b/qemu/roms/ipxe/src/crypto/cms.c index b4a41de6c..bc2148e8a 100644 --- a/qemu/roms/ipxe/src/crypto/cms.c +++ b/qemu/roms/ipxe/src/crypto/cms.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/crypto/crypto_null.c b/qemu/roms/ipxe/src/crypto/crypto_null.c index ba05f7269..15a1c538b 100644 --- a/qemu/roms/ipxe/src/crypto/crypto_null.c +++ b/qemu/roms/ipxe/src/crypto/crypto_null.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/crypto/deflate.c b/qemu/roms/ipxe/src/crypto/deflate.c index 91a489961..e1c87d5fe 100644 --- a/qemu/roms/ipxe/src/crypto/deflate.c +++ b/qemu/roms/ipxe/src/crypto/deflate.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/crypto/drbg.c b/qemu/roms/ipxe/src/crypto/drbg.c index 9e0175d25..5c8b5e612 100644 --- a/qemu/roms/ipxe/src/crypto/drbg.c +++ b/qemu/roms/ipxe/src/crypto/drbg.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/crypto/ecb.c b/qemu/roms/ipxe/src/crypto/ecb.c new file mode 100644 index 000000000..3c9cf340c --- /dev/null +++ b/qemu/roms/ipxe/src/crypto/ecb.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2009 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** @file + * + * Electronic codebook (ECB) + * + */ + +/** + * Encrypt data + * + * @v ctx Context + * @v src Data to encrypt + * @v dst Buffer for encrypted data + * @v len Length of data + * @v raw_cipher Underlying cipher algorithm + */ +void ecb_encrypt ( void *ctx, const void *src, void *dst, size_t len, + struct cipher_algorithm *raw_cipher ) { + size_t blocksize = raw_cipher->blocksize; + + assert ( ( len % blocksize ) == 0 ); + + while ( len ) { + cipher_encrypt ( raw_cipher, ctx, src, dst, blocksize ); + dst += blocksize; + src += blocksize; + len -= blocksize; + } +} + +/** + * Decrypt data + * + * @v ctx Context + * @v src Data to decrypt + * @v dst Buffer for decrypted data + * @v len Length of data + * @v raw_cipher Underlying cipher algorithm + */ +void ecb_decrypt ( void *ctx, const void *src, void *dst, size_t len, + struct cipher_algorithm *raw_cipher ) { + size_t blocksize = raw_cipher->blocksize; + + assert ( ( len % blocksize ) == 0 ); + + while ( len ) { + cipher_decrypt ( raw_cipher, ctx, src, dst, blocksize ); + dst += blocksize; + src += blocksize; + len -= blocksize; + } +} diff --git a/qemu/roms/ipxe/src/crypto/entropy.c b/qemu/roms/ipxe/src/crypto/entropy.c index c7045840e..5acbc0258 100644 --- a/qemu/roms/ipxe/src/crypto/entropy.c +++ b/qemu/roms/ipxe/src/crypto/entropy.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/crypto/hash_df.c b/qemu/roms/ipxe/src/crypto/hash_df.c index adf1d87e4..c1417e683 100644 --- a/qemu/roms/ipxe/src/crypto/hash_df.c +++ b/qemu/roms/ipxe/src/crypto/hash_df.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/crypto/hmac.c b/qemu/roms/ipxe/src/crypto/hmac.c index e9459198c..95a46195c 100644 --- a/qemu/roms/ipxe/src/crypto/hmac.c +++ b/qemu/roms/ipxe/src/crypto/hmac.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/crypto/hmac_drbg.c b/qemu/roms/ipxe/src/crypto/hmac_drbg.c index 1e5f732e2..6c1d5deb2 100644 --- a/qemu/roms/ipxe/src/crypto/hmac_drbg.c +++ b/qemu/roms/ipxe/src/crypto/hmac_drbg.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/crypto/md5.c b/qemu/roms/ipxe/src/crypto/md5.c index 122c7d59e..f9738b0ac 100644 --- a/qemu/roms/ipxe/src/crypto/md5.c +++ b/qemu/roms/ipxe/src/crypto/md5.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/crypto/mishmash/rsa_aes_cbc_sha1.c b/qemu/roms/ipxe/src/crypto/mishmash/rsa_aes_cbc_sha1.c new file mode 100644 index 000000000..06722c0e1 --- /dev/null +++ b/qemu/roms/ipxe/src/crypto/mishmash/rsa_aes_cbc_sha1.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include + +/** TLS_RSA_WITH_AES_128_CBC_SHA cipher suite */ +struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite (03) = { + .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA ), + .key_len = ( 128 / 8 ), + .pubkey = &rsa_algorithm, + .cipher = &aes_cbc_algorithm, + .digest = &sha1_algorithm, +}; + +/** TLS_RSA_WITH_AES_256_CBC_SHA cipher suite */ +struct tls_cipher_suite tls_rsa_with_aes_256_cbc_sha __tls_cipher_suite (04) = { + .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA ), + .key_len = ( 256 / 8 ), + .pubkey = &rsa_algorithm, + .cipher = &aes_cbc_algorithm, + .digest = &sha1_algorithm, +}; diff --git a/qemu/roms/ipxe/src/crypto/mishmash/rsa_aes_cbc_sha256.c b/qemu/roms/ipxe/src/crypto/mishmash/rsa_aes_cbc_sha256.c new file mode 100644 index 000000000..c609eacea --- /dev/null +++ b/qemu/roms/ipxe/src/crypto/mishmash/rsa_aes_cbc_sha256.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include + +/** TLS_RSA_WITH_AES_128_CBC_SHA256 cipher suite */ +struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite(01)={ + .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA256 ), + .key_len = ( 128 / 8 ), + .pubkey = &rsa_algorithm, + .cipher = &aes_cbc_algorithm, + .digest = &sha256_algorithm, +}; + +/** TLS_RSA_WITH_AES_256_CBC_SHA256 cipher suite */ +struct tls_cipher_suite tls_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite(02)={ + .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA256 ), + .key_len = ( 256 / 8 ), + .pubkey = &rsa_algorithm, + .cipher = &aes_cbc_algorithm, + .digest = &sha256_algorithm, +}; diff --git a/qemu/roms/ipxe/src/crypto/mishmash/rsa_md5.c b/qemu/roms/ipxe/src/crypto/mishmash/rsa_md5.c new file mode 100644 index 000000000..ac828ac11 --- /dev/null +++ b/qemu/roms/ipxe/src/crypto/mishmash/rsa_md5.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** "md5WithRSAEncryption" object identifier */ +static uint8_t oid_md5_with_rsa_encryption[] = + { ASN1_OID_MD5WITHRSAENCRYPTION }; + +/** "md5WithRSAEncryption" OID-identified algorithm */ +struct asn1_algorithm md5_with_rsa_encryption_algorithm __asn1_algorithm = { + .name = "md5WithRSAEncryption", + .pubkey = &rsa_algorithm, + .digest = &md5_algorithm, + .oid = ASN1_OID_CURSOR ( oid_md5_with_rsa_encryption ), +}; + +/** MD5 digestInfo prefix */ +static const uint8_t rsa_md5_prefix_data[] = + { RSA_DIGESTINFO_PREFIX ( MD5_DIGEST_SIZE, ASN1_OID_MD5 ) }; + +/** MD5 digestInfo prefix */ +struct rsa_digestinfo_prefix rsa_md5_prefix __rsa_digestinfo_prefix = { + .digest = &md5_algorithm, + .data = rsa_md5_prefix_data, + .len = sizeof ( rsa_md5_prefix_data ), +}; diff --git a/qemu/roms/ipxe/src/crypto/mishmash/rsa_sha1.c b/qemu/roms/ipxe/src/crypto/mishmash/rsa_sha1.c new file mode 100644 index 000000000..39424bf2d --- /dev/null +++ b/qemu/roms/ipxe/src/crypto/mishmash/rsa_sha1.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** "sha1WithRSAEncryption" object identifier */ +static uint8_t oid_sha1_with_rsa_encryption[] = + { ASN1_OID_SHA1WITHRSAENCRYPTION }; + +/** "sha1WithRSAEncryption" OID-identified algorithm */ +struct asn1_algorithm sha1_with_rsa_encryption_algorithm __asn1_algorithm = { + .name = "sha1WithRSAEncryption", + .pubkey = &rsa_algorithm, + .digest = &sha1_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha1_with_rsa_encryption ), +}; + +/** SHA-1 digestInfo prefix */ +static const uint8_t rsa_sha1_prefix_data[] = + { RSA_DIGESTINFO_PREFIX ( SHA1_DIGEST_SIZE, ASN1_OID_SHA1 ) }; + +/** SHA-1 digestInfo prefix */ +struct rsa_digestinfo_prefix rsa_sha1_prefix __rsa_digestinfo_prefix = { + .digest = &sha1_algorithm, + .data = rsa_sha1_prefix_data, + .len = sizeof ( rsa_sha1_prefix_data ), +}; + +/** RSA with SHA-1 signature hash algorithm */ +struct tls_signature_hash_algorithm tls_rsa_sha1 __tls_sig_hash_algorithm = { + .code = { + .signature = TLS_RSA_ALGORITHM, + .hash = TLS_SHA1_ALGORITHM, + }, + .pubkey = &rsa_algorithm, + .digest = &sha1_algorithm, +}; diff --git a/qemu/roms/ipxe/src/crypto/mishmash/rsa_sha224.c b/qemu/roms/ipxe/src/crypto/mishmash/rsa_sha224.c new file mode 100644 index 000000000..5e8755aab --- /dev/null +++ b/qemu/roms/ipxe/src/crypto/mishmash/rsa_sha224.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** "sha224WithRSAEncryption" object identifier */ +static uint8_t oid_sha224_with_rsa_encryption[] = + { ASN1_OID_SHA224WITHRSAENCRYPTION }; + +/** "sha224WithRSAEncryption" OID-identified algorithm */ +struct asn1_algorithm sha224_with_rsa_encryption_algorithm __asn1_algorithm = { + .name = "sha224WithRSAEncryption", + .pubkey = &rsa_algorithm, + .digest = &sha224_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha224_with_rsa_encryption ), +}; + +/** SHA-224 digestInfo prefix */ +static const uint8_t rsa_sha224_prefix_data[] = + { RSA_DIGESTINFO_PREFIX ( SHA224_DIGEST_SIZE, ASN1_OID_SHA224 ) }; + +/** SHA-224 digestInfo prefix */ +struct rsa_digestinfo_prefix rsa_sha224_prefix __rsa_digestinfo_prefix = { + .digest = &sha224_algorithm, + .data = rsa_sha224_prefix_data, + .len = sizeof ( rsa_sha224_prefix_data ), +}; + +/** RSA with SHA-224 signature hash algorithm */ +struct tls_signature_hash_algorithm tls_rsa_sha224 __tls_sig_hash_algorithm = { + .code = { + .signature = TLS_RSA_ALGORITHM, + .hash = TLS_SHA224_ALGORITHM, + }, + .pubkey = &rsa_algorithm, + .digest = &sha224_algorithm, +}; diff --git a/qemu/roms/ipxe/src/crypto/mishmash/rsa_sha256.c b/qemu/roms/ipxe/src/crypto/mishmash/rsa_sha256.c new file mode 100644 index 000000000..b44af5f19 --- /dev/null +++ b/qemu/roms/ipxe/src/crypto/mishmash/rsa_sha256.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** "sha256WithRSAEncryption" object identifier */ +static uint8_t oid_sha256_with_rsa_encryption[] = + { ASN1_OID_SHA256WITHRSAENCRYPTION }; + +/** "sha256WithRSAEncryption" OID-identified algorithm */ +struct asn1_algorithm sha256_with_rsa_encryption_algorithm __asn1_algorithm = { + .name = "sha256WithRSAEncryption", + .pubkey = &rsa_algorithm, + .digest = &sha256_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha256_with_rsa_encryption ), +}; + +/** SHA-256 digestInfo prefix */ +static const uint8_t rsa_sha256_prefix_data[] = + { RSA_DIGESTINFO_PREFIX ( SHA256_DIGEST_SIZE, ASN1_OID_SHA256 ) }; + +/** SHA-256 digestInfo prefix */ +struct rsa_digestinfo_prefix rsa_sha256_prefix __rsa_digestinfo_prefix = { + .digest = &sha256_algorithm, + .data = rsa_sha256_prefix_data, + .len = sizeof ( rsa_sha256_prefix_data ), +}; + +/** RSA with SHA-256 signature hash algorithm */ +struct tls_signature_hash_algorithm tls_rsa_sha256 __tls_sig_hash_algorithm = { + .code = { + .signature = TLS_RSA_ALGORITHM, + .hash = TLS_SHA256_ALGORITHM, + }, + .pubkey = &rsa_algorithm, + .digest = &sha256_algorithm, +}; diff --git a/qemu/roms/ipxe/src/crypto/mishmash/rsa_sha384.c b/qemu/roms/ipxe/src/crypto/mishmash/rsa_sha384.c new file mode 100644 index 000000000..af22a2bf0 --- /dev/null +++ b/qemu/roms/ipxe/src/crypto/mishmash/rsa_sha384.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** "sha384WithRSAEncryption" object identifier */ +static uint8_t oid_sha384_with_rsa_encryption[] = + { ASN1_OID_SHA384WITHRSAENCRYPTION }; + +/** "sha384WithRSAEncryption" OID-identified algorithm */ +struct asn1_algorithm sha384_with_rsa_encryption_algorithm __asn1_algorithm = { + .name = "sha384WithRSAEncryption", + .pubkey = &rsa_algorithm, + .digest = &sha384_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha384_with_rsa_encryption ), +}; + +/** SHA-384 digestInfo prefix */ +static const uint8_t rsa_sha384_prefix_data[] = + { RSA_DIGESTINFO_PREFIX ( SHA384_DIGEST_SIZE, ASN1_OID_SHA384 ) }; + +/** SHA-384 digestInfo prefix */ +struct rsa_digestinfo_prefix rsa_sha384_prefix __rsa_digestinfo_prefix = { + .digest = &sha384_algorithm, + .data = rsa_sha384_prefix_data, + .len = sizeof ( rsa_sha384_prefix_data ), +}; + +/** RSA with SHA-384 signature hash algorithm */ +struct tls_signature_hash_algorithm tls_rsa_sha384 __tls_sig_hash_algorithm = { + .code = { + .signature = TLS_RSA_ALGORITHM, + .hash = TLS_SHA384_ALGORITHM, + }, + .pubkey = &rsa_algorithm, + .digest = &sha384_algorithm, +}; diff --git a/qemu/roms/ipxe/src/crypto/mishmash/rsa_sha512.c b/qemu/roms/ipxe/src/crypto/mishmash/rsa_sha512.c new file mode 100644 index 000000000..29ee15493 --- /dev/null +++ b/qemu/roms/ipxe/src/crypto/mishmash/rsa_sha512.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** "sha512WithRSAEncryption" object identifier */ +static uint8_t oid_sha512_with_rsa_encryption[] = + { ASN1_OID_SHA512WITHRSAENCRYPTION }; + +/** "sha512WithRSAEncryption" OID-identified algorithm */ +struct asn1_algorithm sha512_with_rsa_encryption_algorithm __asn1_algorithm = { + .name = "sha512WithRSAEncryption", + .pubkey = &rsa_algorithm, + .digest = &sha512_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha512_with_rsa_encryption ), +}; + +/** SHA-512 digestInfo prefix */ +static const uint8_t rsa_sha512_prefix_data[] = + { RSA_DIGESTINFO_PREFIX ( SHA512_DIGEST_SIZE, ASN1_OID_SHA512 ) }; + +/** SHA-512 digestInfo prefix */ +struct rsa_digestinfo_prefix rsa_sha512_prefix __rsa_digestinfo_prefix = { + .digest = &sha512_algorithm, + .data = rsa_sha512_prefix_data, + .len = sizeof ( rsa_sha512_prefix_data ), +}; + +/** RSA with SHA-512 signature hash algorithm */ +struct tls_signature_hash_algorithm tls_rsa_sha512 __tls_sig_hash_algorithm = { + .code = { + .signature = TLS_RSA_ALGORITHM, + .hash = TLS_SHA512_ALGORITHM, + }, + .pubkey = &rsa_algorithm, + .digest = &sha512_algorithm, +}; diff --git a/qemu/roms/ipxe/src/crypto/null_entropy.c b/qemu/roms/ipxe/src/crypto/null_entropy.c index c56d5e76f..d1e1a6f73 100644 --- a/qemu/roms/ipxe/src/crypto/null_entropy.c +++ b/qemu/roms/ipxe/src/crypto/null_entropy.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/crypto/ocsp.c b/qemu/roms/ipxe/src/crypto/ocsp.c index 66e47c57e..5df55bc96 100644 --- a/qemu/roms/ipxe/src/crypto/ocsp.c +++ b/qemu/roms/ipxe/src/crypto/ocsp.c @@ -233,7 +233,7 @@ static int ocsp_uri_string ( struct ocsp_check *ocsp ) { goto err_path_base64; } base64_encode ( ocsp->request.builder.data, ocsp->request.builder.len, - path_base64_string ); + path_base64_string, path_len ); /* URI-encode the Base64-encoded request */ memset ( &path_uri, 0, sizeof ( path_uri ) ); diff --git a/qemu/roms/ipxe/src/crypto/privkey.c b/qemu/roms/ipxe/src/crypto/privkey.c index e010649c0..a6043bd1e 100644 --- a/qemu/roms/ipxe/src/crypto/privkey.c +++ b/qemu/roms/ipxe/src/crypto/privkey.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/crypto/random_nz.c b/qemu/roms/ipxe/src/crypto/random_nz.c index f1d2e187d..5fe576e05 100644 --- a/qemu/roms/ipxe/src/crypto/random_nz.c +++ b/qemu/roms/ipxe/src/crypto/random_nz.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/crypto/rbg.c b/qemu/roms/ipxe/src/crypto/rbg.c index e2d06978c..943b288c3 100644 --- a/qemu/roms/ipxe/src/crypto/rbg.c +++ b/qemu/roms/ipxe/src/crypto/rbg.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/crypto/rootcert.c b/qemu/roms/ipxe/src/crypto/rootcert.c index ae28905ac..00ea1647e 100644 --- a/qemu/roms/ipxe/src/crypto/rootcert.c +++ b/qemu/roms/ipxe/src/crypto/rootcert.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/crypto/rsa.c b/qemu/roms/ipxe/src/crypto/rsa.c index 0ab7b2ad3..36109280d 100644 --- a/qemu/roms/ipxe/src/crypto/rsa.c +++ b/qemu/roms/ipxe/src/crypto/rsa.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -28,9 +32,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include -#include -#include -#include #include /** @file @@ -49,18 +50,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** "rsaEncryption" object identifier */ static uint8_t oid_rsa_encryption[] = { ASN1_OID_RSAENCRYPTION }; -/** "md5WithRSAEncryption" object identifier */ -static uint8_t oid_md5_with_rsa_encryption[] = - { ASN1_OID_MD5WITHRSAENCRYPTION }; - -/** "sha1WithRSAEncryption" object identifier */ -static uint8_t oid_sha1_with_rsa_encryption[] = - { ASN1_OID_SHA1WITHRSAENCRYPTION }; - -/** "sha256WithRSAEncryption" object identifier */ -static uint8_t oid_sha256_with_rsa_encryption[] = - { ASN1_OID_SHA256WITHRSAENCRYPTION }; - /** "rsaEncryption" OID-identified algorithm */ struct asn1_algorithm rsa_encryption_algorithm __asn1_algorithm = { .name = "rsaEncryption", @@ -69,63 +58,6 @@ struct asn1_algorithm rsa_encryption_algorithm __asn1_algorithm = { .oid = ASN1_OID_CURSOR ( oid_rsa_encryption ), }; -/** "md5WithRSAEncryption" OID-identified algorithm */ -struct asn1_algorithm md5_with_rsa_encryption_algorithm __asn1_algorithm = { - .name = "md5WithRSAEncryption", - .pubkey = &rsa_algorithm, - .digest = &md5_algorithm, - .oid = ASN1_OID_CURSOR ( oid_md5_with_rsa_encryption ), -}; - -/** "sha1WithRSAEncryption" OID-identified algorithm */ -struct asn1_algorithm sha1_with_rsa_encryption_algorithm __asn1_algorithm = { - .name = "sha1WithRSAEncryption", - .pubkey = &rsa_algorithm, - .digest = &sha1_algorithm, - .oid = ASN1_OID_CURSOR ( oid_sha1_with_rsa_encryption ), -}; - -/** "sha256WithRSAEncryption" OID-identified algorithm */ -struct asn1_algorithm sha256_with_rsa_encryption_algorithm __asn1_algorithm = { - .name = "sha256WithRSAEncryption", - .pubkey = &rsa_algorithm, - .digest = &sha256_algorithm, - .oid = ASN1_OID_CURSOR ( oid_sha256_with_rsa_encryption ), -}; - -/** MD5 digestInfo prefix */ -static const uint8_t rsa_md5_prefix_data[] = - { RSA_DIGESTINFO_PREFIX ( MD5_DIGEST_SIZE, ASN1_OID_MD5 ) }; - -/** SHA-1 digestInfo prefix */ -static const uint8_t rsa_sha1_prefix_data[] = - { RSA_DIGESTINFO_PREFIX ( SHA1_DIGEST_SIZE, ASN1_OID_SHA1 ) }; - -/** SHA-256 digestInfo prefix */ -static const uint8_t rsa_sha256_prefix_data[] = - { RSA_DIGESTINFO_PREFIX ( SHA256_DIGEST_SIZE, ASN1_OID_SHA256 ) }; - -/** MD5 digestInfo prefix */ -struct rsa_digestinfo_prefix rsa_md5_prefix __rsa_digestinfo_prefix = { - .digest = &md5_algorithm, - .data = rsa_md5_prefix_data, - .len = sizeof ( rsa_md5_prefix_data ), -}; - -/** SHA-1 digestInfo prefix */ -struct rsa_digestinfo_prefix rsa_sha1_prefix __rsa_digestinfo_prefix = { - .digest = &sha1_algorithm, - .data = rsa_sha1_prefix_data, - .len = sizeof ( rsa_sha1_prefix_data ), -}; - -/** SHA-256 digestInfo prefix */ -struct rsa_digestinfo_prefix rsa_sha256_prefix __rsa_digestinfo_prefix = { - .digest = &sha256_algorithm, - .data = rsa_sha256_prefix_data, - .len = sizeof ( rsa_sha256_prefix_data ), -}; - /** * Identify RSA prefix * diff --git a/qemu/roms/ipxe/src/crypto/sha1.c b/qemu/roms/ipxe/src/crypto/sha1.c index e1bef669e..51866f4b7 100644 --- a/qemu/roms/ipxe/src/crypto/sha1.c +++ b/qemu/roms/ipxe/src/crypto/sha1.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/crypto/sha224.c b/qemu/roms/ipxe/src/crypto/sha224.c new file mode 100644 index 000000000..be25f24e9 --- /dev/null +++ b/qemu/roms/ipxe/src/crypto/sha224.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * SHA-224 algorithm + * + */ + +#include +#include +#include +#include +#include + +/** SHA-224 initial digest values */ +static const struct sha256_digest sha224_init_digest = { + .h = { + cpu_to_be32 ( 0xc1059ed8 ), + cpu_to_be32 ( 0x367cd507 ), + cpu_to_be32 ( 0x3070dd17 ), + cpu_to_be32 ( 0xf70e5939 ), + cpu_to_be32 ( 0xffc00b31 ), + cpu_to_be32 ( 0x68581511 ), + cpu_to_be32 ( 0x64f98fa7 ), + cpu_to_be32 ( 0xbefa4fa4 ), + }, +}; + +/** + * Initialise SHA-224 algorithm + * + * @v ctx SHA-224 context + */ +static void sha224_init ( void *ctx ) { + struct sha256_context *context = ctx; + + sha256_family_init ( context, &sha224_init_digest, SHA224_DIGEST_SIZE ); +} + +/** SHA-224 algorithm */ +struct digest_algorithm sha224_algorithm = { + .name = "sha224", + .ctxsize = sizeof ( struct sha256_context ), + .blocksize = sizeof ( union sha256_block ), + .digestsize = SHA224_DIGEST_SIZE, + .init = sha224_init, + .update = sha256_update, + .final = sha256_final, +}; + +/** "sha224" object identifier */ +static uint8_t oid_sha224[] = { ASN1_OID_SHA224 }; + +/** "sha224" OID-identified algorithm */ +struct asn1_algorithm oid_sha224_algorithm __asn1_algorithm = { + .name = "sha224", + .digest = &sha224_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha224 ), +}; diff --git a/qemu/roms/ipxe/src/crypto/sha256.c b/qemu/roms/ipxe/src/crypto/sha256.c index 36e02b3c2..0360d8d16 100644 --- a/qemu/roms/ipxe/src/crypto/sha256.c +++ b/qemu/roms/ipxe/src/crypto/sha256.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -47,11 +51,11 @@ struct sha256_variables { uint32_t f; uint32_t g; uint32_t h; - uint32_t w[64]; + uint32_t w[SHA256_ROUNDS]; } __attribute__ (( packed )); /** SHA-256 constants */ -static const uint32_t k[64] = { +static const uint32_t k[SHA256_ROUNDS] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, @@ -65,6 +69,37 @@ static const uint32_t k[64] = { 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; +/** SHA-256 initial digest values */ +static const struct sha256_digest sha256_init_digest = { + .h = { + cpu_to_be32 ( 0x6a09e667 ), + cpu_to_be32 ( 0xbb67ae85 ), + cpu_to_be32 ( 0x3c6ef372 ), + cpu_to_be32 ( 0xa54ff53a ), + cpu_to_be32 ( 0x510e527f ), + cpu_to_be32 ( 0x9b05688c ), + cpu_to_be32 ( 0x1f83d9ab ), + cpu_to_be32 ( 0x5be0cd19 ), + }, +}; + +/** + * Initialise SHA-256 family algorithm + * + * @v context SHA-256 context + * @v init Initial digest values + * @v digestsize Digest size + */ +void sha256_family_init ( struct sha256_context *context, + const struct sha256_digest *init, + size_t digestsize ) { + + context->len = 0; + context->digestsize = digestsize; + memcpy ( &context->ddd.dd.digest, init, + sizeof ( context->ddd.dd.digest ) ); +} + /** * Initialise SHA-256 algorithm * @@ -73,15 +108,8 @@ static const uint32_t k[64] = { static void sha256_init ( void *ctx ) { struct sha256_context *context = ctx; - context->ddd.dd.digest.h[0] = cpu_to_be32 ( 0x6a09e667 ); - context->ddd.dd.digest.h[1] = cpu_to_be32 ( 0xbb67ae85 ); - context->ddd.dd.digest.h[2] = cpu_to_be32 ( 0x3c6ef372 ); - context->ddd.dd.digest.h[3] = cpu_to_be32 ( 0xa54ff53a ); - context->ddd.dd.digest.h[4] = cpu_to_be32 ( 0x510e527f ); - context->ddd.dd.digest.h[5] = cpu_to_be32 ( 0x9b05688c ); - context->ddd.dd.digest.h[6] = cpu_to_be32 ( 0x1f83d9ab ); - context->ddd.dd.digest.h[7] = cpu_to_be32 ( 0x5be0cd19 ); - context->len = 0; + sha256_family_init ( context, &sha256_init_digest, + sizeof ( struct sha256_digest ) ); } /** @@ -139,7 +167,7 @@ static void sha256_digest ( struct sha256_context *context ) { } /* Initialise w[16..63] */ - for ( i = 16 ; i < 64 ; i++ ) { + for ( i = 16 ; i < SHA256_ROUNDS ; i++ ) { s0 = ( ror32 ( w[i-15], 7 ) ^ ror32 ( w[i-15], 18 ) ^ ( w[i-15] >> 3 ) ); s1 = ( ror32 ( w[i-2], 17 ) ^ ror32 ( w[i-2], 19 ) ^ @@ -148,7 +176,7 @@ static void sha256_digest ( struct sha256_context *context ) { } /* Main loop */ - for ( i = 0 ; i < 64 ; i++ ) { + for ( i = 0 ; i < SHA256_ROUNDS ; i++ ) { s0 = ( ror32 ( *a, 2 ) ^ ror32 ( *a, 13 ) ^ ror32 ( *a, 22 ) ); maj = ( ( *a & *b ) ^ ( *a & *c ) ^ ( *b & *c ) ); t2 = ( s0 + maj ); @@ -186,7 +214,7 @@ static void sha256_digest ( struct sha256_context *context ) { * @v data Data * @v len Length of data */ -static void sha256_update ( void *ctx, const void *data, size_t len ) { +void sha256_update ( void *ctx, const void *data, size_t len ) { struct sha256_context *context = ctx; const uint8_t *byte = data; size_t offset; @@ -209,7 +237,7 @@ static void sha256_update ( void *ctx, const void *data, size_t len ) { * @v ctx SHA-256 context * @v out Output buffer */ -static void sha256_final ( void *ctx, void *out ) { +void sha256_final ( void *ctx, void *out ) { struct sha256_context *context = ctx; uint64_t len_bits; uint8_t pad; @@ -230,8 +258,7 @@ static void sha256_final ( void *ctx, void *out ) { assert ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 ); /* Copy out final digest */ - memcpy ( out, &context->ddd.dd.digest, - sizeof ( context->ddd.dd.digest ) ); + memcpy ( out, &context->ddd.dd.digest, context->digestsize ); } /** SHA-256 algorithm */ diff --git a/qemu/roms/ipxe/src/crypto/sha384.c b/qemu/roms/ipxe/src/crypto/sha384.c new file mode 100644 index 000000000..017751826 --- /dev/null +++ b/qemu/roms/ipxe/src/crypto/sha384.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * SHA-384 algorithm + * + */ + +#include +#include +#include +#include +#include + +/** SHA-384 initial digest values */ +static const struct sha512_digest sha384_init_digest = { + .h = { + cpu_to_be64 ( 0xcbbb9d5dc1059ed8ULL ), + cpu_to_be64 ( 0x629a292a367cd507ULL ), + cpu_to_be64 ( 0x9159015a3070dd17ULL ), + cpu_to_be64 ( 0x152fecd8f70e5939ULL ), + cpu_to_be64 ( 0x67332667ffc00b31ULL ), + cpu_to_be64 ( 0x8eb44a8768581511ULL ), + cpu_to_be64 ( 0xdb0c2e0d64f98fa7ULL ), + cpu_to_be64 ( 0x47b5481dbefa4fa4ULL ), + }, +}; + +/** + * Initialise SHA-384 algorithm + * + * @v ctx SHA-384 context + */ +static void sha384_init ( void *ctx ) { + struct sha512_context *context = ctx; + + sha512_family_init ( context, &sha384_init_digest, SHA384_DIGEST_SIZE ); +} + +/** SHA-384 algorithm */ +struct digest_algorithm sha384_algorithm = { + .name = "sha384", + .ctxsize = sizeof ( struct sha512_context ), + .blocksize = sizeof ( union sha512_block ), + .digestsize = SHA384_DIGEST_SIZE, + .init = sha384_init, + .update = sha512_update, + .final = sha512_final, +}; + +/** "sha384" object identifier */ +static uint8_t oid_sha384[] = { ASN1_OID_SHA384 }; + +/** "sha384" OID-identified algorithm */ +struct asn1_algorithm oid_sha384_algorithm __asn1_algorithm = { + .name = "sha384", + .digest = &sha384_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha384 ), +}; diff --git a/qemu/roms/ipxe/src/crypto/sha512.c b/qemu/roms/ipxe/src/crypto/sha512.c new file mode 100644 index 000000000..814f44563 --- /dev/null +++ b/qemu/roms/ipxe/src/crypto/sha512.c @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * SHA-512 algorithm + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** SHA-512 variables */ +struct sha512_variables { + /* This layout matches that of struct sha512_digest_data, + * allowing for efficient endianness-conversion, + */ + uint64_t a; + uint64_t b; + uint64_t c; + uint64_t d; + uint64_t e; + uint64_t f; + uint64_t g; + uint64_t h; + uint64_t w[SHA512_ROUNDS]; +} __attribute__ (( packed )); + +/** SHA-512 constants */ +static const uint64_t k[SHA512_ROUNDS] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, + 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL, + 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, + 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, + 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, + 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL, + 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, + 0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, + 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, + 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL, + 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, + 0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, + 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, + 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL +}; + +/** SHA-512 initial digest values */ +static const struct sha512_digest sha512_init_digest = { + .h = { + cpu_to_be64 ( 0x6a09e667f3bcc908ULL ), + cpu_to_be64 ( 0xbb67ae8584caa73bULL ), + cpu_to_be64 ( 0x3c6ef372fe94f82bULL ), + cpu_to_be64 ( 0xa54ff53a5f1d36f1ULL ), + cpu_to_be64 ( 0x510e527fade682d1ULL ), + cpu_to_be64 ( 0x9b05688c2b3e6c1fULL ), + cpu_to_be64 ( 0x1f83d9abfb41bd6bULL ), + cpu_to_be64 ( 0x5be0cd19137e2179ULL ), + }, +}; + +/** + * Initialise SHA-512 family algorithm + * + * @v context SHA-512 context + * @v init Initial digest values + * @v digestsize Digest size + */ +void sha512_family_init ( struct sha512_context *context, + const struct sha512_digest *init, + size_t digestsize ) { + + context->len = 0; + context->digestsize = digestsize; + memcpy ( &context->ddq.dd.digest, init, + sizeof ( context->ddq.dd.digest ) ); +} + +/** + * Initialise SHA-512 algorithm + * + * @v ctx SHA-512 context + */ +static void sha512_init ( void *ctx ) { + struct sha512_context *context = ctx; + + sha512_family_init ( context, &sha512_init_digest, + sizeof ( struct sha512_digest ) ); +} + +/** + * Calculate SHA-512 digest of accumulated data + * + * @v context SHA-512 context + */ +static void sha512_digest ( struct sha512_context *context ) { + union { + union sha512_digest_data_qwords ddq; + struct sha512_variables v; + } u; + uint64_t *a = &u.v.a; + uint64_t *b = &u.v.b; + uint64_t *c = &u.v.c; + uint64_t *d = &u.v.d; + uint64_t *e = &u.v.e; + uint64_t *f = &u.v.f; + uint64_t *g = &u.v.g; + uint64_t *h = &u.v.h; + uint64_t *w = u.v.w; + uint64_t s0; + uint64_t s1; + uint64_t maj; + uint64_t t1; + uint64_t t2; + uint64_t ch; + unsigned int i; + + /* Sanity checks */ + assert ( ( context->len % sizeof ( context->ddq.dd.data ) ) == 0 ); + linker_assert ( &u.ddq.dd.digest.h[0] == a, sha512_bad_layout ); + linker_assert ( &u.ddq.dd.digest.h[1] == b, sha512_bad_layout ); + linker_assert ( &u.ddq.dd.digest.h[2] == c, sha512_bad_layout ); + linker_assert ( &u.ddq.dd.digest.h[3] == d, sha512_bad_layout ); + linker_assert ( &u.ddq.dd.digest.h[4] == e, sha512_bad_layout ); + linker_assert ( &u.ddq.dd.digest.h[5] == f, sha512_bad_layout ); + linker_assert ( &u.ddq.dd.digest.h[6] == g, sha512_bad_layout ); + linker_assert ( &u.ddq.dd.digest.h[7] == h, sha512_bad_layout ); + linker_assert ( &u.ddq.dd.data.qword[0] == w, sha512_bad_layout ); + + DBGC ( context, "SHA512 digesting:\n" ); + DBGC_HDA ( context, 0, &context->ddq.dd.digest, + sizeof ( context->ddq.dd.digest ) ); + DBGC_HDA ( context, context->len, &context->ddq.dd.data, + sizeof ( context->ddq.dd.data ) ); + + /* Convert h[0..7] to host-endian, and initialise a, b, c, d, + * e, f, g, h, and w[0..15] + */ + for ( i = 0 ; i < ( sizeof ( u.ddq.qword ) / + sizeof ( u.ddq.qword[0] ) ) ; i++ ) { + be64_to_cpus ( &context->ddq.qword[i] ); + u.ddq.qword[i] = context->ddq.qword[i]; + } + + /* Initialise w[16..79] */ + for ( i = 16 ; i < SHA512_ROUNDS ; i++ ) { + s0 = ( ror64 ( w[i-15], 1 ) ^ ror64 ( w[i-15], 8 ) ^ + ( w[i-15] >> 7 ) ); + s1 = ( ror64 ( w[i-2], 19 ) ^ ror64 ( w[i-2], 61 ) ^ + ( w[i-2] >> 6 ) ); + w[i] = ( w[i-16] + s0 + w[i-7] + s1 ); + } + + /* Main loop */ + for ( i = 0 ; i < SHA512_ROUNDS ; i++ ) { + s0 = ( ror64 ( *a, 28 ) ^ ror64 ( *a, 34 ) ^ ror64 ( *a, 39 ) ); + maj = ( ( *a & *b ) ^ ( *a & *c ) ^ ( *b & *c ) ); + t2 = ( s0 + maj ); + s1 = ( ror64 ( *e, 14 ) ^ ror64 ( *e, 18 ) ^ ror64 ( *e, 41 ) ); + ch = ( ( *e & *f ) ^ ( (~*e) & *g ) ); + t1 = ( *h + s1 + ch + k[i] + w[i] ); + *h = *g; + *g = *f; + *f = *e; + *e = ( *d + t1 ); + *d = *c; + *c = *b; + *b = *a; + *a = ( t1 + t2 ); + DBGC2 ( context, "%2d : %016llx %016llx %016llx %016llx " + "%016llx %016llx %016llx %016llx\n", + i, *a, *b, *c, *d, *e, *f, *g, *h ); + } + + /* Add chunk to hash and convert back to big-endian */ + for ( i = 0 ; i < 8 ; i++ ) { + context->ddq.dd.digest.h[i] = + cpu_to_be64 ( context->ddq.dd.digest.h[i] + + u.ddq.dd.digest.h[i] ); + } + + DBGC ( context, "SHA512 digested:\n" ); + DBGC_HDA ( context, 0, &context->ddq.dd.digest, + sizeof ( context->ddq.dd.digest ) ); +} + +/** + * Accumulate data with SHA-512 algorithm + * + * @v ctx SHA-512 context + * @v data Data + * @v len Length of data + */ +void sha512_update ( void *ctx, const void *data, size_t len ) { + struct sha512_context *context = ctx; + const uint8_t *byte = data; + size_t offset; + + /* Accumulate data a byte at a time, performing the digest + * whenever we fill the data buffer + */ + while ( len-- ) { + offset = ( context->len % sizeof ( context->ddq.dd.data ) ); + context->ddq.dd.data.byte[offset] = *(byte++); + context->len++; + if ( ( context->len % sizeof ( context->ddq.dd.data ) ) == 0 ) + sha512_digest ( context ); + } +} + +/** + * Generate SHA-512 digest + * + * @v ctx SHA-512 context + * @v out Output buffer + */ +void sha512_final ( void *ctx, void *out ) { + struct sha512_context *context = ctx; + uint64_t len_bits_hi; + uint64_t len_bits_lo; + uint8_t pad; + + /* Record length before pre-processing */ + len_bits_hi = 0; + len_bits_lo = cpu_to_be64 ( ( ( uint64_t ) context->len ) * 8 ); + + /* Pad with a single "1" bit followed by as many "0" bits as required */ + pad = 0x80; + do { + sha512_update ( ctx, &pad, sizeof ( pad ) ); + pad = 0x00; + } while ( ( context->len % sizeof ( context->ddq.dd.data ) ) != + offsetof ( typeof ( context->ddq.dd.data ), final.len_hi ) ); + + /* Append length (in bits) */ + sha512_update ( ctx, &len_bits_hi, sizeof ( len_bits_hi ) ); + sha512_update ( ctx, &len_bits_lo, sizeof ( len_bits_lo ) ); + assert ( ( context->len % sizeof ( context->ddq.dd.data ) ) == 0 ); + + /* Copy out final digest */ + memcpy ( out, &context->ddq.dd.digest, context->digestsize ); +} + +/** SHA-512 algorithm */ +struct digest_algorithm sha512_algorithm = { + .name = "sha512", + .ctxsize = sizeof ( struct sha512_context ), + .blocksize = sizeof ( union sha512_block ), + .digestsize = sizeof ( struct sha512_digest ), + .init = sha512_init, + .update = sha512_update, + .final = sha512_final, +}; + +/** "sha512" object identifier */ +static uint8_t oid_sha512[] = { ASN1_OID_SHA512 }; + +/** "sha512" OID-identified algorithm */ +struct asn1_algorithm oid_sha512_algorithm __asn1_algorithm = { + .name = "sha512", + .digest = &sha512_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha512 ), +}; diff --git a/qemu/roms/ipxe/src/crypto/sha512_224.c b/qemu/roms/ipxe/src/crypto/sha512_224.c new file mode 100644 index 000000000..8c37b566b --- /dev/null +++ b/qemu/roms/ipxe/src/crypto/sha512_224.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * SHA-512/224 algorithm + * + */ + +#include +#include +#include +#include +#include + +/** SHA-512/224 initial digest values */ +static const struct sha512_digest sha512_224_init_digest = { + .h = { + cpu_to_be64 ( 0x8c3d37c819544da2ULL ), + cpu_to_be64 ( 0x73e1996689dcd4d6ULL ), + cpu_to_be64 ( 0x1dfab7ae32ff9c82ULL ), + cpu_to_be64 ( 0x679dd514582f9fcfULL ), + cpu_to_be64 ( 0x0f6d2b697bd44da8ULL ), + cpu_to_be64 ( 0x77e36f7304c48942ULL ), + cpu_to_be64 ( 0x3f9d85a86a1d36c8ULL ), + cpu_to_be64 ( 0x1112e6ad91d692a1ULL ), + }, +}; + +/** + * Initialise SHA-512/224 algorithm + * + * @v ctx SHA-512/224 context + */ +static void sha512_224_init ( void *ctx ) { + struct sha512_context *context = ctx; + + sha512_family_init ( context, &sha512_224_init_digest, + SHA512_224_DIGEST_SIZE ); +} + +/** SHA-512/224 algorithm */ +struct digest_algorithm sha512_224_algorithm = { + .name = "sha512/224", + .ctxsize = sizeof ( struct sha512_context ), + .blocksize = sizeof ( union sha512_block ), + .digestsize = SHA512_224_DIGEST_SIZE, + .init = sha512_224_init, + .update = sha512_update, + .final = sha512_final, +}; + +/** "sha512_224" object identifier */ +static uint8_t oid_sha512_224[] = { ASN1_OID_SHA512_224 }; + +/** "sha512_224" OID-identified algorithm */ +struct asn1_algorithm oid_sha512_224_algorithm __asn1_algorithm = { + .name = "sha512/224", + .digest = &sha512_224_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha512_224 ), +}; diff --git a/qemu/roms/ipxe/src/crypto/sha512_256.c b/qemu/roms/ipxe/src/crypto/sha512_256.c new file mode 100644 index 000000000..f8afaf3e3 --- /dev/null +++ b/qemu/roms/ipxe/src/crypto/sha512_256.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * SHA-512/256 algorithm + * + */ + +#include +#include +#include +#include +#include + +/** SHA-512/256 initial digest values */ +static const struct sha512_digest sha512_256_init_digest = { + .h = { + cpu_to_be64 ( 0x22312194fc2bf72cULL ), + cpu_to_be64 ( 0x9f555fa3c84c64c2ULL ), + cpu_to_be64 ( 0x2393b86b6f53b151ULL ), + cpu_to_be64 ( 0x963877195940eabdULL ), + cpu_to_be64 ( 0x96283ee2a88effe3ULL ), + cpu_to_be64 ( 0xbe5e1e2553863992ULL ), + cpu_to_be64 ( 0x2b0199fc2c85b8aaULL ), + cpu_to_be64 ( 0x0eb72ddc81c52ca2ULL ), + }, +}; + +/** + * Initialise SHA-512/256 algorithm + * + * @v ctx SHA-512/256 context + */ +static void sha512_256_init ( void *ctx ) { + struct sha512_context *context = ctx; + + sha512_family_init ( context, &sha512_256_init_digest, + SHA512_256_DIGEST_SIZE ); +} + +/** SHA-512/256 algorithm */ +struct digest_algorithm sha512_256_algorithm = { + .name = "sha512/256", + .ctxsize = sizeof ( struct sha512_context ), + .blocksize = sizeof ( union sha512_block ), + .digestsize = SHA512_256_DIGEST_SIZE, + .init = sha512_256_init, + .update = sha512_update, + .final = sha512_final, +}; + +/** "sha512_256" object identifier */ +static uint8_t oid_sha512_256[] = { ASN1_OID_SHA512_256 }; + +/** "sha512_256" OID-identified algorithm */ +struct asn1_algorithm oid_sha512_256_algorithm __asn1_algorithm = { + .name = "sha512/256", + .digest = &sha512_256_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha512_256 ), +}; diff --git a/qemu/roms/ipxe/src/crypto/x509.c b/qemu/roms/ipxe/src/crypto/x509.c index 4a02dad14..43a4ca17a 100644 --- a/qemu/roms/ipxe/src/crypto/x509.c +++ b/qemu/roms/ipxe/src/crypto/x509.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -139,7 +143,8 @@ const char * x509_name ( struct x509_certificate *cert ) { } else { /* Certificate has no commonName: use SHA-1 fingerprint */ x509_fingerprint ( cert, digest, fingerprint ); - base16_encode ( fingerprint, sizeof ( fingerprint ), buf ); + base16_encode ( fingerprint, sizeof ( fingerprint ), + buf, sizeof ( buf ) ); } return buf; } @@ -1761,5 +1766,11 @@ int x509_validate_chain ( struct x509_chain *chain, time_t time, return -EACCES_USELESS; } +/* Drag in objects via x509_validate() */ +REQUIRING_SYMBOL ( x509_validate ); + /* Drag in certificate store */ REQUIRE_OBJECT ( certstore ); + +/* Drag in crypto configuration */ +REQUIRE_OBJECT ( config_crypto ); diff --git a/qemu/roms/ipxe/src/drivers/bitbash/bitbash.c b/qemu/roms/ipxe/src/drivers/bitbash/bitbash.c index 23ca30356..9b24f716c 100644 --- a/qemu/roms/ipxe/src/drivers/bitbash/bitbash.c +++ b/qemu/roms/ipxe/src/drivers/bitbash/bitbash.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/drivers/bitbash/i2c_bit.c b/qemu/roms/ipxe/src/drivers/bitbash/i2c_bit.c index decc8d80e..707d9447d 100644 --- a/qemu/roms/ipxe/src/drivers/bitbash/i2c_bit.c +++ b/qemu/roms/ipxe/src/drivers/bitbash/i2c_bit.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/bitbash/spi_bit.c b/qemu/roms/ipxe/src/drivers/bitbash/spi_bit.c index 1b39d72fa..04fddc20b 100644 --- a/qemu/roms/ipxe/src/drivers/bitbash/spi_bit.c +++ b/qemu/roms/ipxe/src/drivers/bitbash/spi_bit.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/block/ata.c b/qemu/roms/ipxe/src/drivers/block/ata.c index c9b87c20c..b1c6855a0 100644 --- a/qemu/roms/ipxe/src/drivers/block/ata.c +++ b/qemu/roms/ipxe/src/drivers/block/ata.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/block/scsi.c b/qemu/roms/ipxe/src/drivers/block/scsi.c index 64d692986..fd5f82b9f 100644 --- a/qemu/roms/ipxe/src/drivers/block/scsi.c +++ b/qemu/roms/ipxe/src/drivers/block/scsi.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/bus/cdc.c b/qemu/roms/ipxe/src/drivers/bus/cdc.c new file mode 100644 index 000000000..373a03072 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/bus/cdc.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** @file + * + * USB Communications Device Class (CDC) + * + */ + +/** + * Locate CDC union functional descriptor + * + * @v config Configuration descriptor + * @v interface Interface descriptor + * @ret desc Union functional descriptor, or NULL if not found + */ +struct cdc_union_descriptor * +cdc_union_descriptor ( struct usb_configuration_descriptor *config, + struct usb_interface_descriptor *interface ) { + struct cdc_union_descriptor *desc; + + for_each_interface_descriptor ( desc, config, interface ) { + if ( ( desc->header.type == USB_CS_INTERFACE_DESCRIPTOR ) && + ( desc->subtype == CDC_SUBTYPE_UNION ) ) + return desc; + } + return NULL; +} diff --git a/qemu/roms/ipxe/src/drivers/bus/pci.c b/qemu/roms/ipxe/src/drivers/bus/pci.c index 4a8d00b54..6fbedd940 100644 --- a/qemu/roms/ipxe/src/drivers/bus/pci.c +++ b/qemu/roms/ipxe/src/drivers/bus/pci.c @@ -18,9 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -58,8 +62,8 @@ static unsigned long pci_bar ( struct pci_device *pci, unsigned int reg ) { uint32_t high; pci_read_config_dword ( pci, reg, &low ); - if ( ( low & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK) ) - == (PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64) ){ + if ( ( low & (PCI_BASE_ADDRESS_SPACE_IO|PCI_BASE_ADDRESS_MEM_TYPE_MASK)) + == PCI_BASE_ADDRESS_MEM_TYPE_64 ) { pci_read_config_dword ( pci, reg + 4, &high ); if ( high ) { if ( sizeof ( unsigned long ) > sizeof ( uint32_t ) ) { @@ -93,10 +97,10 @@ unsigned long pci_bar_start ( struct pci_device *pci, unsigned int reg ) { unsigned long bar; bar = pci_bar ( pci, reg ); - if ( (bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY ){ - return ( bar & PCI_BASE_ADDRESS_MEM_MASK ); + if ( bar & PCI_BASE_ADDRESS_SPACE_IO ) { + return ( bar & ~PCI_BASE_ADDRESS_IO_MASK ); } else { - return ( bar & PCI_BASE_ADDRESS_IO_MASK ); + return ( bar & ~PCI_BASE_ADDRESS_MEM_MASK ); } } @@ -122,11 +126,11 @@ static void pci_read_bases ( struct pci_device *pci ) { if ( bar & PCI_BASE_ADDRESS_SPACE_IO ) { if ( ! pci->ioaddr ) pci->ioaddr = - ( bar & PCI_BASE_ADDRESS_IO_MASK ); + ( bar & ~PCI_BASE_ADDRESS_IO_MASK ); } else { if ( ! pci->membase ) pci->membase = - ( bar & PCI_BASE_ADDRESS_MEM_MASK ); + ( bar & ~PCI_BASE_ADDRESS_MEM_MASK ); /* Skip next BAR if 64-bit */ if ( bar & PCI_BASE_ADDRESS_MEM_TYPE_64 ) reg += 4; @@ -181,7 +185,7 @@ int pci_read_config ( struct pci_device *pci ) { pci->busdevfn = PCI_FIRST_FUNC ( pci->busdevfn ); pci_read_config_byte ( pci, PCI_HEADER_TYPE, &hdrtype ); pci->busdevfn = busdevfn; - if ( ! ( hdrtype & 0x80 ) ) + if ( ! ( hdrtype & PCI_HEADER_TYPE_MULTI ) ) return -ENODEV; } @@ -253,6 +257,8 @@ int pci_find_driver ( struct pci_device *pci ) { unsigned int i; for_each_table_entry ( driver, PCI_DRIVERS ) { + if ( ( driver->class.class ^ pci->class ) & driver->class.mask ) + continue; for ( i = 0 ; i < driver->id_count ; i++ ) { id = &driver->ids[i]; if ( ( id->vendor != PCI_ANY_ID ) && @@ -334,14 +340,15 @@ static int pcibus_probe ( struct root_device *rootdev ) { /* Look for a driver */ if ( ( rc = pci_find_driver ( pci ) ) != 0 ) { - DBGC ( pci, PCI_FMT " (%04x:%04x) has no driver\n", - PCI_ARGS ( pci ), pci->vendor, pci->device ); + DBGC ( pci, PCI_FMT " (%04x:%04x class %06x) has no " + "driver\n", PCI_ARGS ( pci ), pci->vendor, + pci->device, pci->class ); continue; } /* Add to device hierarchy */ pci->dev.parent = &rootdev->dev; - list_add ( &pci->dev.siblings, &rootdev->dev.children); + list_add ( &pci->dev.siblings, &rootdev->dev.children ); /* Look for a driver */ if ( ( rc = pci_probe ( pci ) ) == 0 ) { diff --git a/qemu/roms/ipxe/src/drivers/bus/pci_settings.c b/qemu/roms/ipxe/src/drivers/bus/pci_settings.c index db20452e0..1cb9fa5a3 100644 --- a/qemu/roms/ipxe/src/drivers/bus/pci_settings.c +++ b/qemu/roms/ipxe/src/drivers/bus/pci_settings.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/bus/pcibackup.c b/qemu/roms/ipxe/src/drivers/bus/pcibackup.c index 6b592e893..fecad8192 100644 --- a/qemu/roms/ipxe/src/drivers/bus/pcibackup.c +++ b/qemu/roms/ipxe/src/drivers/bus/pcibackup.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/bus/pciextra.c b/qemu/roms/ipxe/src/drivers/bus/pciextra.c index c4417e0cb..82287fb86 100644 --- a/qemu/roms/ipxe/src/drivers/bus/pciextra.c +++ b/qemu/roms/ipxe/src/drivers/bus/pciextra.c @@ -1,4 +1,4 @@ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -26,7 +26,7 @@ int pci_find_capability ( struct pci_device *pci, int cap ) { return 0; pci_read_config_byte ( pci, PCI_HEADER_TYPE, &hdr_type ); - switch ( hdr_type & 0x7F ) { + switch ( hdr_type & PCI_HEADER_TYPE_MASK ) { case PCI_HEADER_TYPE_NORMAL: case PCI_HEADER_TYPE_BRIDGE: default: @@ -38,13 +38,13 @@ int pci_find_capability ( struct pci_device *pci, int cap ) { } while ( ttl-- && pos >= 0x40 ) { pos &= ~3; - pci_read_config_byte ( pci, pos + PCI_CAP_LIST_ID, &id ); + pci_read_config_byte ( pci, pos + PCI_CAP_ID, &id ); DBG ( "PCI Capability: %d\n", id ); if ( id == 0xff ) break; if ( id == cap ) return pos; - pci_read_config_byte ( pci, pos + PCI_CAP_LIST_NEXT, &pos ); + pci_read_config_byte ( pci, pos + PCI_CAP_NEXT, &pos ); } return 0; } @@ -76,9 +76,9 @@ unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ) { /* Restore the original command register. This reenables decoding. */ pci_write_config_word ( pci, PCI_COMMAND, cmd ); if ( start & PCI_BASE_ADDRESS_SPACE_IO ) { - size &= PCI_BASE_ADDRESS_IO_MASK; + size &= ~PCI_BASE_ADDRESS_IO_MASK; } else { - size &= PCI_BASE_ADDRESS_MEM_MASK; + size &= ~PCI_BASE_ADDRESS_MEM_MASK; } /* Find the lowest bit set */ size = size & ~( size - 1 ); diff --git a/qemu/roms/ipxe/src/drivers/bus/pcivpd.c b/qemu/roms/ipxe/src/drivers/bus/pcivpd.c index 0b7a879fe..243b1f779 100644 --- a/qemu/roms/ipxe/src/drivers/bus/pcivpd.c +++ b/qemu/roms/ipxe/src/drivers/bus/pcivpd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/bus/usb.c b/qemu/roms/ipxe/src/drivers/bus/usb.c new file mode 100644 index 000000000..2019e3341 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/bus/usb.c @@ -0,0 +1,2128 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Universal Serial Bus (USB) + * + */ + +/** List of USB buses */ +struct list_head usb_buses = LIST_HEAD_INIT ( usb_buses ); + +/** List of changed ports */ +static struct list_head usb_changed = LIST_HEAD_INIT ( usb_changed ); + +/** List of halted endpoints */ +static struct list_head usb_halted = LIST_HEAD_INIT ( usb_halted ); + +/****************************************************************************** + * + * Utility functions + * + ****************************************************************************** + */ + +/** + * Get USB speed name (for debugging) + * + * @v speed Speed + * @ret name Speed name + */ +static inline const char * usb_speed_name ( unsigned int speed ) { + static const char *exponents[4] = { "", "k", "M", "G" }; + static char buf[ 10 /* "xxxxxXbps" + NUL */ ]; + unsigned int mantissa; + unsigned int exponent; + + /* Extract mantissa and exponent */ + mantissa = USB_SPEED_MANTISSA ( speed ); + exponent = USB_SPEED_EXPONENT ( speed ); + + /* Name speed */ + switch ( speed ) { + case USB_SPEED_NONE: return "DETACHED"; + case USB_SPEED_LOW: return "low"; + case USB_SPEED_FULL: return "full"; + case USB_SPEED_HIGH: return "high"; + case USB_SPEED_SUPER: return "super"; + default: + snprintf ( buf, sizeof ( buf ), "%d%sbps", + mantissa, exponents[exponent] ); + return buf; + } +} + +/** + * Transcribe USB BCD-coded value (for debugging) + * + * @v bcd BCD-coded value + * @ret string Transcribed value + */ +static inline const char * usb_bcd ( uint16_t bcd ) { + static char buf[ 6 /* "xx.xx" + NUL */ ]; + uint8_t high = ( bcd >> 8 ); + uint8_t low = ( bcd >> 0 ); + + snprintf ( buf, sizeof ( buf ), "%x.%02x", high, low ); + return buf; +} + +/****************************************************************************** + * + * USB descriptors + * + ****************************************************************************** + */ + +/** + * Locate USB interface association descriptor + * + * @v config Configuraton descriptor + * @v first First interface number + * @ret desc Interface association descriptor, or NULL if not found + */ +static struct usb_interface_association_descriptor * +usb_interface_association_descriptor ( struct usb_configuration_descriptor + *config, + unsigned int first ) { + struct usb_interface_association_descriptor *desc; + + /* Find a matching interface association descriptor */ + for_each_config_descriptor ( desc, config ) { + if ( ( desc->header.type == + USB_INTERFACE_ASSOCIATION_DESCRIPTOR ) && + ( desc->first == first ) ) + return desc; + } + return NULL; +} + +/** + * Locate USB interface descriptor + * + * @v config Configuraton descriptor + * @v interface Interface number + * @v alternate Alternate setting + * @ret desc Interface descriptor, or NULL if not found + */ +struct usb_interface_descriptor * +usb_interface_descriptor ( struct usb_configuration_descriptor *config, + unsigned int interface, unsigned int alternate ) { + struct usb_interface_descriptor *desc; + + /* Find a matching interface descriptor */ + for_each_config_descriptor ( desc, config ) { + if ( ( desc->header.type == USB_INTERFACE_DESCRIPTOR ) && + ( desc->interface == interface ) && + ( desc->alternate == alternate ) ) + return desc; + } + return NULL; +} + +/** + * Locate USB endpoint descriptor + * + * @v config Configuration descriptor + * @v interface Interface descriptor + * @v type Endpoint (internal) type + * @v index Endpoint index + * @ret desc Descriptor, or NULL if not found + */ +struct usb_endpoint_descriptor * +usb_endpoint_descriptor ( struct usb_configuration_descriptor *config, + struct usb_interface_descriptor *interface, + unsigned int type, unsigned int index ) { + struct usb_endpoint_descriptor *desc; + unsigned int attributes = ( type & USB_ENDPOINT_ATTR_TYPE_MASK ); + unsigned int direction = ( type & USB_DIR_IN ); + + /* Find a matching endpoint descriptor */ + for_each_interface_descriptor ( desc, config, interface ) { + if ( ( desc->header.type == USB_ENDPOINT_DESCRIPTOR ) && + ( ( desc->attributes & + USB_ENDPOINT_ATTR_TYPE_MASK ) == attributes ) && + ( ( desc->endpoint & USB_DIR_IN ) == direction ) && + ( index-- == 0 ) ) + return desc; + } + return NULL; +} + +/** + * Locate USB endpoint companion descriptor + * + * @v config Configuration descriptor + * @v desc Endpoint descriptor + * @ret descx Companion descriptor, or NULL if not found + */ +struct usb_endpoint_companion_descriptor * +usb_endpoint_companion_descriptor ( struct usb_configuration_descriptor *config, + struct usb_endpoint_descriptor *desc ) { + struct usb_endpoint_companion_descriptor *descx; + + /* Get companion descriptor, if present */ + descx = container_of ( usb_next_descriptor ( &desc->header ), + struct usb_endpoint_companion_descriptor, + header ); + return ( ( usb_is_within_config ( config, &descx->header ) && + descx->header.type == USB_ENDPOINT_COMPANION_DESCRIPTOR ) + ? descx : NULL ); +} + +/****************************************************************************** + * + * USB endpoint + * + ****************************************************************************** + */ + +/** + * Get USB endpoint name (for debugging) + * + * @v ep USB endpoint + * @ret name Endpoint name + */ +const char * usb_endpoint_name ( struct usb_endpoint *ep ) { + static char buf[ 9 /* "EPxx OUT" + NUL */ ]; + unsigned int address = ep->address; + + snprintf ( buf, sizeof ( buf ), "EP%d%s", + ( address & USB_ENDPOINT_MAX ), + ( address ? + ( ( address & USB_ENDPOINT_IN ) ? " IN" : " OUT" ) : "" )); + return buf; +} + +/** + * Describe USB endpoint from device configuration + * + * @v ep USB endpoint + * @v config Configuration descriptor + * @v interface Interface descriptor + * @v type Endpoint (internal) type + * @v index Endpoint index + * @ret rc Return status code + */ +int usb_endpoint_described ( struct usb_endpoint *ep, + struct usb_configuration_descriptor *config, + struct usb_interface_descriptor *interface, + unsigned int type, unsigned int index ) { + struct usb_device *usb = ep->usb; + struct usb_port *port = usb->port; + struct usb_endpoint_descriptor *desc; + struct usb_endpoint_companion_descriptor *descx; + unsigned int sizes; + unsigned int burst; + unsigned int interval; + size_t mtu; + + /* Locate endpoint descriptor */ + desc = usb_endpoint_descriptor ( config, interface, type, index ); + if ( ! desc ) + return -ENOENT; + + /* Locate companion descriptor, if any */ + descx = usb_endpoint_companion_descriptor ( config, desc ); + + /* Calculate MTU and burst size */ + sizes = le16_to_cpu ( desc->sizes ); + mtu = USB_ENDPOINT_MTU ( sizes ); + burst = ( descx ? descx->burst : USB_ENDPOINT_BURST ( sizes ) ); + + /* Calculate interval */ + if ( ( type & USB_ENDPOINT_ATTR_TYPE_MASK ) == + USB_ENDPOINT_ATTR_INTERRUPT ) { + if ( port->speed >= USB_SPEED_HIGH ) { + /* 2^(desc->interval-1) is a microframe count */ + interval = ( 1 << ( desc->interval - 1 ) ); + } else { + /* desc->interval is a (whole) frame count */ + interval = ( desc->interval << 3 ); + } + } else { + /* desc->interval is a microframe count */ + interval = desc->interval; + } + + /* Describe endpoint */ + usb_endpoint_describe ( ep, desc->endpoint, desc->attributes, + mtu, burst, interval ); + return 0; +} + +/** + * Open USB endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ +int usb_endpoint_open ( struct usb_endpoint *ep ) { + struct usb_device *usb = ep->usb; + unsigned int idx = USB_ENDPOINT_IDX ( ep->address ); + int rc; + + /* Populate host controller operations */ + ep->host = &usb->port->hub->bus->op->endpoint; + + /* Add to endpoint list */ + if ( usb->ep[idx] != NULL ) { + DBGC ( usb, "USB %s %s is already open\n", + usb->name, usb_endpoint_name ( ep ) ); + rc = -EALREADY; + goto err_already; + } + usb->ep[idx] = ep; + INIT_LIST_HEAD ( &ep->halted ); + + /* Open endpoint */ + if ( ( rc = ep->host->open ( ep ) ) != 0 ) { + DBGC ( usb, "USB %s %s could not open: %s\n", usb->name, + usb_endpoint_name ( ep ), strerror ( rc ) ); + goto err_open; + } + ep->open = 1; + + DBGC2 ( usb, "USB %s %s opened with MTU %zd, burst %d, interval %d\n", + usb->name, usb_endpoint_name ( ep ), ep->mtu, ep->burst, + ep->interval ); + return 0; + + ep->open = 0; + ep->host->close ( ep ); + err_open: + usb->ep[idx] = NULL; + err_already: + if ( ep->max ) + usb_flush ( ep ); + return rc; +} + +/** + * Clear transaction translator (if applicable) + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int usb_endpoint_clear_tt ( struct usb_endpoint *ep ) { + struct usb_device *usb = ep->usb; + struct usb_port *tt; + int rc; + + /* Do nothing if this is a periodic endpoint */ + if ( ep->attributes & USB_ENDPOINT_ATTR_PERIODIC ) + return 0; + + /* Do nothing if this endpoint is not behind a transaction translator */ + tt = usb_transaction_translator ( usb ); + if ( ! tt ) + return 0; + + /* Clear transaction translator buffer */ + if ( ( rc = tt->hub->driver->clear_tt ( tt->hub, tt, ep ) ) != 0 ) { + DBGC ( usb, "USB %s %s could not clear transaction translator: " + "%s\n", usb->name, usb_endpoint_name ( ep ), + strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Close USB endpoint + * + * @v ep USB endpoint + */ +void usb_endpoint_close ( struct usb_endpoint *ep ) { + struct usb_device *usb = ep->usb; + unsigned int idx = USB_ENDPOINT_IDX ( ep->address ); + + /* Sanity checks */ + assert ( usb->ep[idx] == ep ); + + /* Close endpoint */ + ep->open = 0; + ep->host->close ( ep ); + assert ( ep->fill == 0 ); + + /* Remove from endpoint list */ + usb->ep[idx] = NULL; + list_del ( &ep->halted ); + + /* Discard any recycled buffers, if applicable */ + if ( ep->max ) + usb_flush ( ep ); + + /* Clear transaction translator, if applicable */ + usb_endpoint_clear_tt ( ep ); +} + +/** + * Reset USB endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int usb_endpoint_reset ( struct usb_endpoint *ep ) { + struct usb_device *usb = ep->usb; + unsigned int type; + int rc; + + /* Sanity check */ + assert ( ! list_empty ( &ep->halted ) ); + + /* Reset endpoint */ + if ( ( rc = ep->host->reset ( ep ) ) != 0 ) { + DBGC ( usb, "USB %s %s could not reset: %s\n", + usb->name, usb_endpoint_name ( ep ), strerror ( rc ) ); + return rc; + } + + /* Clear transaction translator, if applicable */ + if ( ( rc = usb_endpoint_clear_tt ( ep ) ) != 0 ) + return rc; + + /* Clear endpoint halt, if applicable */ + type = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); + if ( ( type != USB_ENDPOINT_ATTR_CONTROL ) && + ( ( rc = usb_clear_feature ( usb, USB_RECIP_ENDPOINT, + USB_ENDPOINT_HALT, + ep->address ) ) != 0 ) ) { + DBGC ( usb, "USB %s %s could not clear endpoint halt: %s\n", + usb->name, usb_endpoint_name ( ep ), strerror ( rc ) ); + return rc; + } + + /* Remove from list of halted endpoints */ + list_del ( &ep->halted ); + INIT_LIST_HEAD ( &ep->halted ); + + DBGC ( usb, "USB %s %s reset\n", + usb->name, usb_endpoint_name ( ep ) ); + return 0; +} + +/** + * Update endpoint MTU + * + * @v ep USB endpoint + * @v mtu New MTU + * @ret rc Return status code + */ +static int usb_endpoint_mtu ( struct usb_endpoint *ep, size_t mtu ) { + struct usb_device *usb = ep->usb; + int rc; + + /* Update MTU */ + ep->mtu = mtu; + if ( ( rc = ep->host->mtu ( ep ) ) != 0 ) { + DBGC ( usb, "USB %s %s could not update MTU: %s\n", + usb->name, usb_endpoint_name ( ep ), strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Enqueue USB message transfer + * + * @v ep USB endpoint + * @v request Request + * @v value Value parameter + * @v index Index parameter + * @v iobuf I/O buffer + * @ret rc Return status code + * + * The I/O buffer must have sufficient headroom to contain a setup + * packet. + */ +int usb_message ( struct usb_endpoint *ep, unsigned int request, + unsigned int value, unsigned int index, + struct io_buffer *iobuf ) { + struct usb_device *usb = ep->usb; + struct usb_port *port = usb->port; + struct usb_setup_packet *packet; + size_t len = iob_len ( iobuf ); + int rc; + + /* Sanity check */ + assert ( iob_headroom ( iobuf ) >= sizeof ( *packet ) ); + + /* Fail immediately if device has been unplugged */ + if ( port->speed == USB_SPEED_NONE ) + return -ENODEV; + + /* Reset endpoint if required */ + if ( ( ! list_empty ( &ep->halted ) ) && + ( ( rc = usb_endpoint_reset ( ep ) ) != 0 ) ) + return rc; + + /* Zero input data buffer (if applicable) */ + if ( request & USB_DIR_IN ) + memset ( iobuf->data, 0, len ); + + /* Construct setup packet */ + packet = iob_push ( iobuf, sizeof ( *packet ) ); + packet->request = cpu_to_le16 ( request ); + packet->value = cpu_to_le16 ( value ); + packet->index = cpu_to_le16 ( index ); + packet->len = cpu_to_le16 ( len ); + + /* Enqueue message transfer */ + if ( ( rc = ep->host->message ( ep, iobuf ) ) != 0 ) { + DBGC ( usb, "USB %s %s could not enqueue message transfer: " + "%s\n", usb->name, usb_endpoint_name ( ep ), + strerror ( rc ) ); + return rc; + } + + /* Increment fill level */ + ep->fill++; + + return 0; +} + +/** + * Enqueue USB stream transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v terminate Terminate using a short packet + * @ret rc Return status code + */ +int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int terminate ) { + struct usb_device *usb = ep->usb; + struct usb_port *port = usb->port; + int rc; + + /* Fail immediately if device has been unplugged */ + if ( port->speed == USB_SPEED_NONE ) + return -ENODEV; + + /* Reset endpoint if required */ + if ( ( ! list_empty ( &ep->halted ) ) && + ( ( rc = usb_endpoint_reset ( ep ) ) != 0 ) ) + return rc; + + /* Enqueue stream transfer */ + if ( ( rc = ep->host->stream ( ep, iobuf, terminate ) ) != 0 ) { + DBGC ( usb, "USB %s %s could not enqueue stream transfer: %s\n", + usb->name, usb_endpoint_name ( ep ), strerror ( rc ) ); + return rc; + } + + /* Increment fill level */ + ep->fill++; + + return 0; +} + +/** + * Complete transfer (possibly with error) + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +void usb_complete_err ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int rc ) { + struct usb_device *usb = ep->usb; + + /* Decrement fill level */ + assert ( ep->fill > 0 ); + ep->fill--; + + /* Schedule reset, if applicable */ + if ( ( rc != 0 ) && ep->open ) { + DBGC ( usb, "USB %s %s completion failed: %s\n", + usb->name, usb_endpoint_name ( ep ), strerror ( rc ) ); + list_del ( &ep->halted ); + list_add_tail ( &ep->halted, &usb_halted ); + } + + /* Report completion */ + ep->driver->complete ( ep, iobuf, rc ); +} + +/****************************************************************************** + * + * Endpoint refilling + * + ****************************************************************************** + */ + +/** + * Prefill endpoint recycled buffer list + * + * @v ep USB endpoint + * @ret rc Return status code + */ +int usb_prefill ( struct usb_endpoint *ep ) { + struct io_buffer *iobuf; + size_t len = ( ep->len ? ep->len : ep->mtu ); + unsigned int fill; + int rc; + + /* Sanity checks */ + assert ( ep->fill == 0 ); + assert ( ep->max > 0 ); + assert ( list_empty ( &ep->recycled ) ); + + /* Fill recycled buffer list */ + for ( fill = 0 ; fill < ep->max ; fill++ ) { + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( len ); + if ( ! iobuf ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Add to recycled buffer list */ + list_add_tail ( &iobuf->list, &ep->recycled ); + } + + return 0; + + err_alloc: + usb_flush ( ep ); + return rc; +} + +/** + * Refill endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ +int usb_refill ( struct usb_endpoint *ep ) { + struct io_buffer *iobuf; + size_t len = ( ep->len ? ep->len : ep->mtu ); + int rc; + + /* Sanity checks */ + assert ( ep->open ); + assert ( ep->max > 0 ); + + /* Refill endpoint */ + while ( ep->fill < ep->max ) { + + /* Get or allocate buffer */ + if ( list_empty ( &ep->recycled ) ) { + /* Recycled buffer list is empty; allocate new buffer */ + iobuf = alloc_iob ( len ); + if ( ! iobuf ) + return -ENOMEM; + } else { + /* Get buffer from recycled buffer list */ + iobuf = list_first_entry ( &ep->recycled, + struct io_buffer, list ); + assert ( iobuf != NULL ); + list_del ( &iobuf->list ); + } + + /* Reset buffer to maximum size */ + assert ( iob_len ( iobuf ) <= len ); + iob_put ( iobuf, ( len - iob_len ( iobuf ) ) ); + + /* Enqueue buffer */ + if ( ( rc = usb_stream ( ep, iobuf, 0 ) ) != 0 ) { + list_add ( &iobuf->list, &ep->recycled ); + return rc; + } + } + + return 0; +} + +/** + * Discard endpoint recycled buffer list + * + * @v ep USB endpoint + */ +void usb_flush ( struct usb_endpoint *ep ) { + struct io_buffer *iobuf; + struct io_buffer *tmp; + + /* Sanity checks */ + assert ( ! ep->open ); + assert ( ep->max > 0 ); + + /* Free all I/O buffers */ + list_for_each_entry_safe ( iobuf, tmp, &ep->recycled, list ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); + } +} + +/****************************************************************************** + * + * Control endpoint + * + ****************************************************************************** + */ + +/** USB control transfer pseudo-header */ +struct usb_control_pseudo_header { + /** Completion status */ + int rc; +}; + +/** + * Complete USB control transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void usb_control_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct usb_device *usb = ep->usb; + struct usb_control_pseudo_header *pshdr; + + /* Record completion status in buffer */ + pshdr = iob_push ( iobuf, sizeof ( *pshdr ) ); + pshdr->rc = rc; + + /* Add to list of completed I/O buffers */ + list_add_tail ( &iobuf->list, &usb->complete ); +} + +/** USB control endpoint driver operations */ +static struct usb_endpoint_driver_operations usb_control_operations = { + .complete = usb_control_complete, +}; + +/** + * Issue USB control transaction + * + * @v usb USB device + * @v request Request + * @v value Value parameter + * @v index Index parameter + * @v data Data buffer (if any) + * @v len Length of data + * @ret rc Return status code + */ +int usb_control ( struct usb_device *usb, unsigned int request, + unsigned int value, unsigned int index, void *data, + size_t len ) { + struct usb_bus *bus = usb->port->hub->bus; + struct usb_endpoint *ep = &usb->control; + struct io_buffer *iobuf; + struct io_buffer *cmplt; + union { + struct usb_setup_packet setup; + struct usb_control_pseudo_header pshdr; + } *headroom; + struct usb_control_pseudo_header *pshdr; + unsigned int i; + int rc; + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( sizeof ( *headroom ) + len ); + if ( ! iobuf ) { + rc = -ENOMEM; + goto err_alloc; + } + iob_reserve ( iobuf, sizeof ( *headroom ) ); + iob_put ( iobuf, len ); + if ( request & USB_DIR_IN ) { + memset ( data, 0, len ); + } else { + memcpy ( iobuf->data, data, len ); + } + + /* Enqueue message */ + if ( ( rc = usb_message ( ep, request, value, index, iobuf ) ) != 0 ) + goto err_message; + + /* Wait for completion */ + for ( i = 0 ; i < USB_CONTROL_MAX_WAIT_MS ; i++ ) { + + /* Poll bus */ + usb_poll ( bus ); + + /* Check for completion */ + while ( ( cmplt = list_first_entry ( &usb->complete, + struct io_buffer, + list ) ) ) { + + /* Remove from completion list */ + list_del ( &cmplt->list ); + + /* Extract and strip completion status */ + pshdr = cmplt->data; + iob_pull ( cmplt, sizeof ( *pshdr ) ); + rc = pshdr->rc; + + /* Discard stale completions */ + if ( cmplt != iobuf ) { + DBGC ( usb, "USB %s stale control completion: " + "%s\n", usb->name, strerror ( rc ) ); + DBGC_HDA ( usb, 0, cmplt->data, + iob_len ( cmplt ) ); + free_iob ( cmplt ); + continue; + } + + /* Fail immediately if completion was in error */ + if ( rc != 0 ) { + DBGC ( usb, "USB %s control %04x:%04x:%04x " + "failed: %s\n", usb->name, request, + value, index, strerror ( rc ) ); + free_iob ( cmplt ); + return rc; + } + + /* Copy completion to data buffer, if applicable */ + assert ( iob_len ( cmplt ) <= len ); + if ( request & USB_DIR_IN ) + memcpy ( data, cmplt->data, iob_len ( cmplt ) ); + free_iob ( cmplt ); + return 0; + } + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( usb, "USB %s timed out waiting for control %04x:%04x:%04x\n", + usb->name, request, value, index ); + return -ETIMEDOUT; + + err_message: + free_iob ( iobuf ); + err_alloc: + return rc; +} + +/** + * Get USB string descriptor + * + * @v usb USB device + * @v index String index + * @v language Language ID + * @v buf Data buffer + * @v len Length of buffer + * @ret len String length (excluding NUL), or negative error + */ +int usb_get_string_descriptor ( struct usb_device *usb, unsigned int index, + unsigned int language, char *buf, size_t len ) { + size_t max = ( len ? ( len - 1 /* NUL */ ) : 0 ); + struct { + struct usb_descriptor_header header; + uint16_t character[max]; + } __attribute__ (( packed )) *desc; + unsigned int actual; + unsigned int i; + int rc; + + /* Allocate buffer for string */ + desc = malloc ( sizeof ( *desc ) ); + if ( ! desc ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Get descriptor */ + if ( ( rc = usb_get_descriptor ( usb, 0, USB_STRING_DESCRIPTOR, index, + language, &desc->header, + sizeof ( *desc ) ) ) != 0 ) + goto err_get_descriptor; + + /* Copy to buffer */ + actual = ( ( desc->header.len - sizeof ( desc->header ) ) / + sizeof ( desc->character[0] ) ); + for ( i = 0 ; ( ( i < actual ) && ( i < max ) ) ; i++ ) + buf[i] = le16_to_cpu ( desc->character[i] ); + if ( len ) + buf[i] = '\0'; + + /* Free buffer */ + free ( desc ); + + return actual; + + err_get_descriptor: + free ( desc ); + err_alloc: + return rc; +} + +/****************************************************************************** + * + * USB device driver + * + ****************************************************************************** + */ + +/** + * Describe USB function + * + * @v func USB function + * @v config Configuration descriptor + * @v first First interface number + * @ret rc Return status code + */ +static int usb_function ( struct usb_function *func, + struct usb_configuration_descriptor *config, + unsigned int first ) { + struct usb_device *usb = func->usb; + struct usb_interface_association_descriptor *association; + struct usb_interface_descriptor *interface; + struct cdc_union_descriptor *cdc_union; + unsigned int i; + + /* First, look for an interface association descriptor */ + association = usb_interface_association_descriptor ( config, first ); + if ( association ) { + + /* Sanity check */ + if ( association->count > config->interfaces ) { + DBGC ( usb, "USB %s has invalid association [%d-%d)\n", + func->name, association->first, + ( association->first + association->count ) ); + return -ERANGE; + } + + /* Describe function */ + memcpy ( &func->class, &association->class, + sizeof ( func->class ) ); + func->count = association->count; + for ( i = 0 ; i < association->count ; i++ ) + func->interface[i] = ( association->first + i ); + return 0; + } + + /* Next, look for an interface descriptor */ + interface = usb_interface_descriptor ( config, first, 0 ); + if ( ! interface ) { + DBGC ( usb, "USB %s has no interface descriptor\n", + func->name ); + return -ENOENT; + } + + /* Describe function */ + memcpy ( &func->class, &interface->class, sizeof ( func->class ) ); + func->count = 1; + func->interface[0] = first; + + /* Look for a CDC union descriptor, if applicable */ + if ( ( func->class.class == USB_CLASS_CDC ) && + ( cdc_union = cdc_union_descriptor ( config, interface ) ) ) { + + /* Determine interface count */ + func->count = ( ( cdc_union->header.len - + offsetof ( typeof ( *cdc_union ), + interface[0] ) ) / + sizeof ( cdc_union->interface[0] ) ); + if ( func->count > config->interfaces ) { + DBGC ( usb, "USB %s has invalid union functional " + "descriptor with %d interfaces\n", + func->name, func->count ); + return -ERANGE; + } + + /* Describe function */ + for ( i = 0 ; i < func->count ; i++ ) + func->interface[i] = cdc_union->interface[i]; + + return 0; + } + + return 0; +} + +/** + * Check for a USB device ID match + * + * @v func USB function + * @v id Device ID + * @ret matches Device ID matches + */ +static int +usb_device_id_matches ( struct usb_function *func, struct usb_device_id *id ) { + + return ( ( ( id->vendor == func->dev.desc.vendor ) || + ( id->vendor == USB_ANY_ID ) ) && + ( ( id->product == func->dev.desc.device ) || + ( id->product == USB_ANY_ID ) ) && + ( id->class.class == func->class.class ) && + ( id->class.subclass == func->class.subclass ) && + ( id->class.protocol == func->class.protocol ) ); +} + +/** + * Probe USB device driver + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int usb_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct usb_driver *driver; + struct usb_device_id *id; + unsigned int i; + int rc; + + /* Look for a matching driver */ + for_each_table_entry ( driver, USB_DRIVERS ) { + for ( i = 0 ; i < driver->id_count ; i++ ) { + + /* Check for a matching ID */ + id = &driver->ids[i]; + if ( ! usb_device_id_matches ( func, id ) ) + continue; + + /* Probe driver */ + if ( ( rc = driver->probe ( func, config ) ) != 0 ) { + DBGC ( usb, "USB %s failed to probe driver %s: " + "%s\n", func->name, id->name, + strerror ( rc ) ); + /* Continue trying other drivers */ + continue; + } + + /* Record driver */ + func->driver = driver; + func->dev.driver_name = id->name; + return 0; + } + } + + /* No driver found */ + DBGC ( usb, "USB %s %04x:%04x class %d:%d:%d has no driver\n", + func->name, func->dev.desc.vendor, func->dev.desc.device, + func->class.class, func->class.subclass, func->class.protocol ); + return -ENOENT; +} + +/** + * Remove USB device driver + * + * @v func USB function + */ +static void usb_remove ( struct usb_function *func ) { + + /* Remove driver */ + func->driver->remove ( func ); +} + +/** + * Probe all USB device drivers + * + * @v usb USB device + * @v config Configuration descriptor + */ +static void +usb_probe_all ( struct usb_device *usb, + struct usb_configuration_descriptor *config ) { + struct usb_bus *bus = usb->port->hub->bus; + struct usb_function *func; + uint8_t used[config->interfaces]; + unsigned int first; + unsigned int i; + int rc; + + /* Identify each function in turn */ + memset ( used, 0, sizeof ( used ) ); + for ( first = 0 ; first < config->interfaces ; first++ ) { + + /* Skip interfaces already used */ + if ( used[first] ) + continue; + + /* Allocate and initialise structure */ + func = zalloc ( sizeof ( *func ) + + ( config->interfaces * + sizeof ( func->interface[0] ) ) ); + if ( ! func ) + goto err_alloc; + func->name = func->dev.name; + func->usb = usb; + func->dev.desc.bus_type = BUS_TYPE_USB; + func->dev.desc.location = usb->address; + func->dev.desc.vendor = le16_to_cpu ( usb->device.vendor ); + func->dev.desc.device = le16_to_cpu ( usb->device.product ); + snprintf ( func->dev.name, sizeof ( func->dev.name ), + "%s-%d.%d", usb->name, config->config, first ); + INIT_LIST_HEAD ( &func->dev.children ); + func->dev.parent = bus->dev; + + /* Identify function */ + if ( ( rc = usb_function ( func, config, first ) ) != 0 ) + goto err_function; + assert ( func->count <= config->interfaces ); + + /* Mark interfaces as used */ + for ( i = 0 ; i < func->count ; i++ ) { + if ( func->interface[i] >= config->interfaces ) { + DBGC ( usb, "USB %s has invalid interface %d\n", + func->name, func->interface[i] ); + goto err_interface; + } + used[ func->interface[i] ] = 1; + } + + /* Probe device driver */ + if ( ( rc = usb_probe ( func, config ) ) != 0 ) + goto err_probe; + DBGC ( usb, "USB %s %04x:%04x class %d:%d:%d interfaces ", + func->name, func->dev.desc.vendor, func->dev.desc.device, + func->class.class, func->class.subclass, + func->class.protocol ); + for ( i = 0 ; i < func->count ; i++ ) + DBGC ( usb, "%s%d", ( i ? "," : "" ), + func->interface[i] ); + DBGC ( usb, " using driver %s\n", func->dev.driver_name ); + + /* Add to list of functions */ + list_add ( &func->list, &usb->functions ); + + /* Add to device hierarchy */ + list_add_tail ( &func->dev.siblings, &bus->dev->children ); + + continue; + + list_del ( &func->dev.siblings ); + list_del ( &func->list ); + usb_remove ( func ); + err_probe: + free ( func ); + err_alloc: + err_interface: + err_function: + /* Continue registering other functions */ + continue; + } +} + +/** + * Remove all device drivers + * + * @v usb USB device + */ +static void usb_remove_all ( struct usb_device *usb ) { + struct usb_function *func; + struct usb_function *tmp; + + /* Remove all functions */ + list_for_each_entry_safe ( func, tmp, &usb->functions, list ) { + + /* Remove device driver */ + usb_remove ( func ); + + /* Remove from device hierarchy */ + assert ( list_empty ( &func->dev.children ) ); + list_del ( &func->dev.siblings ); + + /* Remove from list of functions */ + list_del ( &func->list ); + + /* Free function */ + free ( func ); + } +} + +/** + * Select USB device configuration + * + * @v usb USB device + * @v index Configuration index + * @ret rc Return status code + */ +static int usb_configure ( struct usb_device *usb, unsigned int index ) { + struct usb_configuration_descriptor partial; + struct usb_configuration_descriptor *config; + size_t len; + int rc; + + /* Read first part of configuration descriptor to get size */ + if ( ( rc = usb_get_config_descriptor ( usb, index, &partial, + sizeof ( partial ) ) ) != 0 ) { + DBGC ( usb, "USB %s could not get configuration descriptor %d: " + "%s\n", usb->name, index, strerror ( rc ) ); + goto err_get_partial; + } + len = le16_to_cpu ( partial.len ); + if ( len < sizeof ( partial ) ) { + DBGC ( usb, "USB %s underlength configuraton descriptor %d\n", + usb->name, index ); + rc = -EINVAL; + goto err_partial_len; + } + + /* Allocate buffer for whole configuration descriptor */ + config = malloc ( len ); + if ( ! config ) { + rc = -ENOMEM; + goto err_alloc_config; + } + + /* Read whole configuration descriptor */ + if ( ( rc = usb_get_config_descriptor ( usb, index, config, + len ) ) != 0 ) { + DBGC ( usb, "USB %s could not get configuration descriptor %d: " + "%s\n", usb->name, index, strerror ( rc ) ); + goto err_get_config_descriptor; + } + if ( config->len != partial.len ) { + DBGC ( usb, "USB %s bad configuration descriptor %d length\n", + usb->name, index ); + rc = -EINVAL; + goto err_config_len; + } + + /* Set configuration */ + if ( ( rc = usb_set_configuration ( usb, config->config ) ) != 0){ + DBGC ( usb, "USB %s could not set configuration %d: %s\n", + usb->name, config->config, strerror ( rc ) ); + goto err_set_configuration; + } + + /* Probe USB device drivers */ + usb_probe_all ( usb, config ); + + /* Free configuration descriptor */ + free ( config ); + + return 0; + + usb_remove_all ( usb ); + usb_set_configuration ( usb, 0 ); + err_set_configuration: + err_config_len: + err_get_config_descriptor: + free ( config ); + err_alloc_config: + err_partial_len: + err_get_partial: + return rc; +} + +/** + * Clear USB device configuration + * + * @v usb USB device + */ +static void usb_deconfigure ( struct usb_device *usb ) { + unsigned int i; + + /* Remove device drivers */ + usb_remove_all ( usb ); + + /* Sanity checks */ + for ( i = 0 ; i < ( sizeof ( usb->ep ) / sizeof ( usb->ep[0] ) ) ; i++){ + if ( i != USB_ENDPOINT_IDX ( USB_EP0_ADDRESS ) ) + assert ( usb->ep[i] == NULL ); + } + + /* Clear device configuration */ + usb_set_configuration ( usb, 0 ); +} + +/** + * Find and select a supported USB device configuration + * + * @v usb USB device + * @ret rc Return status code + */ +static int usb_configure_any ( struct usb_device *usb ) { + unsigned int index; + int rc = -ENOENT; + + /* Attempt all configuration indexes */ + for ( index = 0 ; index < usb->device.configurations ; index++ ) { + + /* Attempt this configuration index */ + if ( ( rc = usb_configure ( usb, index ) ) != 0 ) + continue; + + /* If we have no drivers, then try the next configuration */ + if ( list_empty ( &usb->functions ) ) { + rc = -ENOTSUP; + usb_deconfigure ( usb ); + continue; + } + + return 0; + } + + return rc; +} + +/****************************************************************************** + * + * USB device + * + ****************************************************************************** + */ + +/** + * Allocate USB device + * + * @v port USB port + * @ret usb USB device, or NULL on allocation failure + */ +static struct usb_device * alloc_usb ( struct usb_port *port ) { + struct usb_hub *hub = port->hub; + struct usb_bus *bus = hub->bus; + struct usb_device *usb; + + /* Allocate and initialise structure */ + usb = zalloc ( sizeof ( *usb ) ); + if ( ! usb ) + return NULL; + snprintf ( usb->name, sizeof ( usb->name ), "%s%c%d", hub->name, + ( hub->usb ? '.' : '-' ), port->address ); + usb->port = port; + INIT_LIST_HEAD ( &usb->functions ); + usb->host = &bus->op->device; + usb_endpoint_init ( &usb->control, usb, &usb_control_operations ); + INIT_LIST_HEAD ( &usb->complete ); + + return usb; +} + +/** + * Register USB device + * + * @v usb USB device + * @ret rc Return status code + */ +static int register_usb ( struct usb_device *usb ) { + struct usb_port *port = usb->port; + struct usb_hub *hub = port->hub; + struct usb_bus *bus = hub->bus; + unsigned int protocol; + size_t mtu; + int rc; + + /* Add to port */ + if ( port->usb != NULL ) { + DBGC ( hub, "USB hub %s port %d is already registered to %s\n", + hub->name, port->address, port->usb->name ); + rc = -EALREADY; + goto err_already; + } + port->usb = usb; + + /* Add to bus device list */ + list_add_tail ( &usb->list, &bus->devices ); + + /* Enable device */ + if ( ( rc = hub->driver->enable ( hub, port ) ) != 0 ) { + DBGC ( hub, "USB hub %s port %d could not enable: %s\n", + hub->name, port->address, strerror ( rc ) ); + goto err_enable; + } + + /* Allow recovery interval since port may have been reset */ + mdelay ( USB_RESET_RECOVER_DELAY_MS ); + + /* Get device speed */ + if ( ( rc = hub->driver->speed ( hub, port ) ) != 0 ) { + DBGC ( hub, "USB hub %s port %d could not get speed: %s\n", + hub->name, port->address, strerror ( rc ) ); + goto err_speed; + } + DBGC2 ( usb, "USB %s attached as %s-speed device\n", + usb->name, usb_speed_name ( port->speed ) ); + + /* Open device */ + if ( ( rc = usb->host->open ( usb ) ) != 0 ) { + DBGC ( usb, "USB %s could not open: %s\n", + usb->name, strerror ( rc ) ); + goto err_open; + } + + /* Describe control endpoint */ + mtu = USB_EP0_DEFAULT_MTU ( port->speed ); + usb_endpoint_describe ( &usb->control, USB_EP0_ADDRESS, + USB_EP0_ATTRIBUTES, mtu, USB_EP0_BURST, + USB_EP0_INTERVAL ); + + /* Open control endpoint */ + if ( ( rc = usb_endpoint_open ( &usb->control ) ) != 0 ) + goto err_open_control; + assert ( usb_endpoint ( usb, USB_EP0_ADDRESS ) == &usb->control ); + + /* Assign device address */ + if ( ( rc = usb->host->address ( usb ) ) != 0 ) { + DBGC ( usb, "USB %s could not set address: %s\n", + usb->name, strerror ( rc ) ); + goto err_address; + } + DBGC2 ( usb, "USB %s assigned address %d\n", usb->name, usb->address ); + + /* Allow recovery interval after Set Address command */ + mdelay ( USB_SET_ADDRESS_RECOVER_DELAY_MS ); + + /* Read first part of device descriptor to get EP0 MTU */ + if ( ( rc = usb_get_mtu ( usb, &usb->device ) ) != 0 ) { + DBGC ( usb, "USB %s could not get MTU: %s\n", + usb->name, strerror ( rc ) ); + goto err_get_mtu; + } + + /* Calculate EP0 MTU */ + protocol = le16_to_cpu ( usb->device.protocol ); + mtu = ( ( protocol < USB_PROTO_3_0 ) ? + usb->device.mtu : ( 1 << usb->device.mtu ) ); + DBGC2 ( usb, "USB %s has control MTU %zd (guessed %zd)\n", + usb->name, mtu, usb->control.mtu ); + + /* Update MTU */ + if ( ( rc = usb_endpoint_mtu ( &usb->control, mtu ) ) != 0 ) + goto err_mtu; + + /* Read whole device descriptor */ + if ( ( rc = usb_get_device_descriptor ( usb, &usb->device ) ) != 0 ) { + DBGC ( usb, "USB %s could not get device descriptor: %s\n", + usb->name, strerror ( rc ) ); + goto err_get_device_descriptor; + } + DBGC ( usb, "USB %s addr %d %04x:%04x class %d:%d:%d (v%s, %s-speed, " + "MTU %zd)\n", usb->name, usb->address, + le16_to_cpu ( usb->device.vendor ), + le16_to_cpu ( usb->device.product ), usb->device.class.class, + usb->device.class.subclass, usb->device.class.protocol, + usb_bcd ( le16_to_cpu ( usb->device.protocol ) ), + usb_speed_name ( port->speed ), usb->control.mtu ); + + /* Configure device */ + if ( ( rc = usb_configure_any ( usb ) ) != 0 ) + goto err_configure_any; + + return 0; + + usb_deconfigure ( usb ); + err_configure_any: + err_get_device_descriptor: + err_mtu: + err_get_mtu: + err_address: + usb_endpoint_close ( &usb->control ); + err_open_control: + usb->host->close ( usb ); + err_open: + err_speed: + hub->driver->disable ( hub, port ); + err_enable: + list_del ( &usb->list ); + port->usb = NULL; + err_already: + return rc; +} + +/** + * Unregister USB device + * + * @v usb USB device + */ +static void unregister_usb ( struct usb_device *usb ) { + struct usb_port *port = usb->port; + struct usb_hub *hub = port->hub; + struct io_buffer *iobuf; + struct io_buffer *tmp; + + /* Sanity checks */ + assert ( port->usb == usb ); + + /* Clear device configuration */ + usb_deconfigure ( usb ); + + /* Close control endpoint */ + usb_endpoint_close ( &usb->control ); + + /* Discard any stale control completions */ + list_for_each_entry_safe ( iobuf, tmp, &usb->complete, list ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); + } + + /* Close device */ + usb->host->close ( usb ); + + /* Disable port */ + hub->driver->disable ( hub, port ); + + /* Remove from bus device list */ + list_del ( &usb->list ); + + /* Remove from port */ + port->usb = NULL; +} + +/** + * Free USB device + * + * @v usb USB device + */ +static void free_usb ( struct usb_device *usb ) { + unsigned int i; + + /* Sanity checks */ + for ( i = 0 ; i < ( sizeof ( usb->ep ) / sizeof ( usb->ep[0] ) ) ; i++ ) + assert ( usb->ep[i] == NULL ); + assert ( list_empty ( &usb->functions ) ); + assert ( list_empty ( &usb->complete ) ); + + /* Free device */ + free ( usb ); +} + +/****************************************************************************** + * + * USB device hotplug event handling + * + ****************************************************************************** + */ + +/** + * Handle newly attached USB device + * + * @v port USB port + * @ret rc Return status code + */ +static int usb_attached ( struct usb_port *port ) { + struct usb_device *usb; + int rc; + + /* Mark port as attached */ + port->attached = 1; + + /* Sanity checks */ + assert ( port->usb == NULL ); + + /* Allocate USB device */ + usb = alloc_usb ( port ); + if ( ! usb ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Register USB device */ + if ( ( rc = register_usb ( usb ) ) != 0 ) + goto err_register; + + return 0; + + unregister_usb ( usb ); + err_register: + free_usb ( usb ); + err_alloc: + return rc; +} + +/** + * Handle newly detached USB device + * + * @v port USB port + */ +static void usb_detached ( struct usb_port *port ) { + struct usb_device *usb = port->usb; + + /* Mark port as detached */ + port->attached = 0; + + /* Do nothing if we have no USB device */ + if ( ! usb ) + return; + + /* Unregister USB device */ + unregister_usb ( usb ); + + /* Free USB device */ + free_usb ( usb ); +} + +/** + * Handle newly attached or detached USB device + * + * @v port USB port + * @ret rc Return status code + */ +static int usb_hotplugged ( struct usb_port *port ) { + struct usb_hub *hub = port->hub; + int rc; + + /* Get current port speed */ + if ( ( rc = hub->driver->speed ( hub, port ) ) != 0 ) { + DBGC ( hub, "USB hub %s port %d could not get speed: %s\n", + hub->name, port->address, strerror ( rc ) ); + goto err_speed; + } + + /* Detach device, if applicable */ + if ( port->attached && ( port->disconnected || ! port->speed ) ) + usb_detached ( port ); + + /* Attach device, if applicable */ + if ( port->speed && ( ! port->attached ) && + ( ( rc = usb_attached ( port ) ) != 0 ) ) + goto err_attached; + + err_attached: + err_speed: + /* Clear any recorded disconnections */ + port->disconnected = 0; + return rc; +} + +/****************************************************************************** + * + * USB process + * + ****************************************************************************** + */ + +/** + * Report port status change + * + * @v port USB port + */ +void usb_port_changed ( struct usb_port *port ) { + + /* Record hub port status change */ + list_del ( &port->changed ); + list_add_tail ( &port->changed, &usb_changed ); +} + +/** + * Handle newly attached or detached USB device + * + */ +static void usb_hotplug ( void ) { + struct usb_port *port; + + /* Handle any changed ports, allowing for the fact that the + * port list may change as we perform hotplug actions. + */ + while ( ! list_empty ( &usb_changed ) ) { + + /* Get first changed port */ + port = list_first_entry ( &usb_changed, struct usb_port, + changed ); + assert ( port != NULL ); + + /* Remove from list of changed ports */ + list_del ( &port->changed ); + INIT_LIST_HEAD ( &port->changed ); + + /* Perform appropriate hotplug action */ + usb_hotplugged ( port ); + } +} + +/** + * USB process + * + * @v process USB process + */ +static void usb_step ( struct process *process __unused ) { + struct usb_bus *bus; + struct usb_endpoint *ep; + + /* Poll all buses */ + for_each_usb_bus ( bus ) + usb_poll ( bus ); + + /* Attempt to reset first halted endpoint in list, if any. We + * do not attempt to process the complete list, since this + * would require extra code to allow for the facts that the + * halted endpoint list may change as we do so, and that + * resetting an endpoint may fail. + */ + if ( ( ep = list_first_entry ( &usb_halted, struct usb_endpoint, + halted ) ) != NULL ) + usb_endpoint_reset ( ep ); + + /* Handle any changed ports */ + usb_hotplug(); +} + +/** USB process */ +PERMANENT_PROCESS ( usb_process, usb_step ); + +/****************************************************************************** + * + * USB hub + * + ****************************************************************************** + */ + +/** + * Allocate USB hub + * + * @v bus USB bus + * @v usb Underlying USB device, if any + * @v ports Number of ports + * @v driver Hub driver operations + * @ret hub USB hub, or NULL on allocation failure + */ +struct usb_hub * alloc_usb_hub ( struct usb_bus *bus, struct usb_device *usb, + unsigned int ports, + struct usb_hub_driver_operations *driver ) { + struct usb_hub *hub; + struct usb_port *port; + unsigned int i; + + /* Allocate and initialise structure */ + hub = zalloc ( sizeof ( *hub ) + ( ports * sizeof ( hub->port[0] ) ) ); + if ( ! hub ) + return NULL; + hub->name = ( usb ? usb->name : bus->name ); + hub->bus = bus; + hub->usb = usb; + if ( usb ) + hub->protocol = usb->port->protocol; + hub->ports = ports; + hub->driver = driver; + hub->host = &bus->op->hub; + + /* Initialise port list */ + for ( i = 1 ; i <= hub->ports ; i++ ) { + port = usb_port ( hub, i ); + port->hub = hub; + port->address = i; + if ( usb ) + port->protocol = usb->port->protocol; + INIT_LIST_HEAD ( &port->changed ); + } + + return hub; +} + +/** + * Register USB hub + * + * @v hub USB hub + * @ret rc Return status code + */ +int register_usb_hub ( struct usb_hub *hub ) { + struct usb_bus *bus = hub->bus; + struct usb_port *port; + unsigned int i; + int rc; + + /* Add to hub list */ + list_add_tail ( &hub->list, &bus->hubs ); + + /* Open hub (host controller) */ + if ( ( rc = hub->host->open ( hub ) ) != 0 ) { + DBGC ( hub, "USB hub %s could not open: %s\n", + hub->name, strerror ( rc ) ); + goto err_host_open; + } + + /* Open hub (driver) */ + if ( ( rc = hub->driver->open ( hub ) ) != 0 ) { + DBGC ( hub, "USB hub %s could not open: %s\n", + hub->name, strerror ( rc ) ); + goto err_driver_open; + } + + /* Delay to allow ports to stabilise */ + mdelay ( USB_PORT_DELAY_MS ); + + /* Mark all ports as changed */ + for ( i = 1 ; i <= hub->ports ; i++ ) { + port = usb_port ( hub, i ); + usb_port_changed ( port ); + } + + /* Some hubs seem to defer reporting device connections until + * their interrupt endpoint is polled for the first time. + * Poll the bus once now in order to pick up any such + * connections. + */ + usb_poll ( bus ); + + return 0; + + hub->driver->close ( hub ); + err_driver_open: + hub->host->close ( hub ); + err_host_open: + list_del ( &hub->list ); + return rc; +} + +/** + * Unregister USB hub + * + * @v hub USB hub + */ +void unregister_usb_hub ( struct usb_hub *hub ) { + struct usb_port *port; + unsigned int i; + + /* Detach all devices */ + for ( i = 1 ; i <= hub->ports ; i++ ) { + port = usb_port ( hub, i ); + if ( port->attached ) + usb_detached ( port ); + } + + /* Close hub (driver) */ + hub->driver->close ( hub ); + + /* Close hub (host controller) */ + hub->host->close ( hub ); + + /* Cancel any pending port status changes */ + for ( i = 1 ; i <= hub->ports ; i++ ) { + port = usb_port ( hub, i ); + list_del ( &port->changed ); + INIT_LIST_HEAD ( &port->changed ); + } + + /* Remove from hub list */ + list_del ( &hub->list ); +} + +/** + * Free USB hub + * + * @v hub USB hub + */ +void free_usb_hub ( struct usb_hub *hub ) { + struct usb_port *port; + unsigned int i; + + /* Sanity checks */ + for ( i = 1 ; i <= hub->ports ; i++ ) { + port = usb_port ( hub, i ); + assert ( ! port->attached ); + assert ( port->usb == NULL ); + assert ( list_empty ( &port->changed ) ); + } + + /* Free hub */ + free ( hub ); +} + +/****************************************************************************** + * + * USB bus + * + ****************************************************************************** + */ + +/** + * Allocate USB bus + * + * @v dev Underlying hardware device + * @v ports Number of root hub ports + * @v mtu Largest transfer allowed on the bus + * @v op Host controller operations + * @ret bus USB bus, or NULL on allocation failure + */ +struct usb_bus * alloc_usb_bus ( struct device *dev, unsigned int ports, + size_t mtu, struct usb_host_operations *op ) { + struct usb_bus *bus; + + /* Allocate and initialise structure */ + bus = zalloc ( sizeof ( *bus ) ); + if ( ! bus ) + goto err_alloc_bus; + bus->name = dev->name; + bus->dev = dev; + bus->mtu = mtu; + bus->op = op; + INIT_LIST_HEAD ( &bus->devices ); + INIT_LIST_HEAD ( &bus->hubs ); + bus->host = &bus->op->bus; + + /* Allocate root hub */ + bus->hub = alloc_usb_hub ( bus, NULL, ports, &op->root ); + if ( ! bus->hub ) + goto err_alloc_hub; + + return bus; + + free_usb_hub ( bus->hub ); + err_alloc_hub: + free ( bus ); + err_alloc_bus: + return NULL; +} + +/** + * Register USB bus + * + * @v bus USB bus + * @ret rc Return status code + */ +int register_usb_bus ( struct usb_bus *bus ) { + int rc; + + /* Sanity checks */ + assert ( bus->hub != NULL ); + + /* Open bus */ + if ( ( rc = bus->host->open ( bus ) ) != 0 ) + goto err_open; + + /* Add to list of USB buses */ + list_add_tail ( &bus->list, &usb_buses ); + + /* Register root hub */ + if ( ( rc = register_usb_hub ( bus->hub ) ) != 0 ) + goto err_register_hub; + + /* Attach any devices already present */ + usb_hotplug(); + + return 0; + + unregister_usb_hub ( bus->hub ); + err_register_hub: + list_del ( &bus->list ); + bus->host->close ( bus ); + err_open: + return rc; +} + +/** + * Unregister USB bus + * + * @v bus USB bus + */ +void unregister_usb_bus ( struct usb_bus *bus ) { + + /* Sanity checks */ + assert ( bus->hub != NULL ); + + /* Unregister root hub */ + unregister_usb_hub ( bus->hub ); + + /* Remove from list of USB buses */ + list_del ( &bus->list ); + + /* Close bus */ + bus->host->close ( bus ); + + /* Sanity checks */ + assert ( list_empty ( &bus->devices ) ); + assert ( list_empty ( &bus->hubs ) ); +} + +/** + * Free USB bus + * + * @v bus USB bus + */ +void free_usb_bus ( struct usb_bus *bus ) { + struct usb_endpoint *ep; + struct usb_port *port; + + /* Sanity checks */ + assert ( list_empty ( &bus->devices ) ); + assert ( list_empty ( &bus->hubs ) ); + list_for_each_entry ( ep, &usb_halted, halted ) + assert ( ep->usb->port->hub->bus != bus ); + list_for_each_entry ( port, &usb_changed, changed ) + assert ( port->hub->bus != bus ); + + /* Free root hub */ + free_usb_hub ( bus->hub ); + + /* Free bus */ + free ( bus ); +} + +/** + * Find USB bus by device location + * + * @v bus_type Bus type + * @v location Bus location + * @ret bus USB bus, or NULL + */ +struct usb_bus * find_usb_bus_by_location ( unsigned int bus_type, + unsigned int location ) { + struct usb_bus *bus; + + for_each_usb_bus ( bus ) { + if ( ( bus->dev->desc.bus_type == bus_type ) && + ( bus->dev->desc.location == location ) ) + return bus; + } + + return NULL; +} + +/****************************************************************************** + * + * USB address assignment + * + ****************************************************************************** + */ + +/** + * Allocate device address + * + * @v bus USB bus + * @ret address Device address, or negative error + */ +int usb_alloc_address ( struct usb_bus *bus ) { + unsigned int address; + + /* Find first free device address */ + address = ffsll ( ~bus->addresses ); + if ( ! address ) + return -ENOENT; + + /* Mark address as used */ + bus->addresses |= ( 1ULL << ( address - 1 ) ); + + return address; +} + +/** + * Free device address + * + * @v bus USB bus + * @v address Device address + */ +void usb_free_address ( struct usb_bus *bus, unsigned int address ) { + + /* Sanity check */ + assert ( address > 0 ); + assert ( bus->addresses & ( 1ULL << ( address - 1 ) ) ); + + /* Mark address as free */ + bus->addresses &= ~( 1ULL << ( address - 1 ) ); +} + +/****************************************************************************** + * + * USB bus topology + * + ****************************************************************************** + */ + +/** + * Get USB route string + * + * @v usb USB device + * @ret route USB route string + */ +unsigned int usb_route_string ( struct usb_device *usb ) { + struct usb_device *parent; + unsigned int route; + + /* Navigate up to root hub, constructing route string as we go */ + for ( route = 0 ; ( parent = usb->port->hub->usb ) ; usb = parent ) { + route <<= 4; + route |= ( ( usb->port->address > 0xf ) ? + 0xf : usb->port->address ); + } + + return route; +} + +/** + * Get USB depth + * + * @v usb USB device + * @ret depth Hub depth + */ +unsigned int usb_depth ( struct usb_device *usb ) { + struct usb_device *parent; + unsigned int depth; + + /* Navigate up to root hub, constructing depth as we go */ + for ( depth = 0 ; ( parent = usb->port->hub->usb ) ; usb = parent ) + depth++; + + return depth; +} + +/** + * Get USB root hub port + * + * @v usb USB device + * @ret port Root hub port + */ +struct usb_port * usb_root_hub_port ( struct usb_device *usb ) { + struct usb_device *parent; + + /* Navigate up to root hub */ + while ( ( parent = usb->port->hub->usb ) ) + usb = parent; + + return usb->port; +} + +/** + * Get USB transaction translator + * + * @v usb USB device + * @ret port Transaction translator port, or NULL + */ +struct usb_port * usb_transaction_translator ( struct usb_device *usb ) { + struct usb_device *parent; + + /* Navigate up to root hub. If we find a low-speed or + * full-speed port with a higher-speed parent device, then + * that port is the transaction translator. + */ + for ( ; ( parent = usb->port->hub->usb ) ; usb = parent ) { + if ( ( usb->port->speed <= USB_SPEED_FULL ) && + ( parent->port->speed > USB_SPEED_FULL ) ) + return usb->port; + } + + return NULL; +} + +/* Drag in objects via register_usb_bus() */ +REQUIRING_SYMBOL ( register_usb_bus ); + +/* Drag in USB configuration */ +REQUIRE_OBJECT ( config_usb ); + +/* Drag in hub driver */ +REQUIRE_OBJECT ( usbhub ); diff --git a/qemu/roms/ipxe/src/drivers/infiniband/arbel.c b/qemu/roms/ipxe/src/drivers/infiniband/arbel.c index 1a56ff9af..2a6c32dec 100644 --- a/qemu/roms/ipxe/src/drivers/infiniband/arbel.c +++ b/qemu/roms/ipxe/src/drivers/infiniband/arbel.c @@ -18,9 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/infiniband/arbel.h b/qemu/roms/ipxe/src/drivers/infiniband/arbel.h index c0303f1bc..73394cd9a 100644 --- a/qemu/roms/ipxe/src/drivers/infiniband/arbel.h +++ b/qemu/roms/ipxe/src/drivers/infiniband/arbel.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/infiniband/linda.c b/qemu/roms/ipxe/src/drivers/infiniband/linda.c index 4afda1208..a6ae9f529 100644 --- a/qemu/roms/ipxe/src/drivers/infiniband/linda.c +++ b/qemu/roms/ipxe/src/drivers/infiniband/linda.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/infiniband/linda.h b/qemu/roms/ipxe/src/drivers/infiniband/linda.h index 72ce70868..46a920a17 100644 --- a/qemu/roms/ipxe/src/drivers/infiniband/linda.h +++ b/qemu/roms/ipxe/src/drivers/infiniband/linda.h @@ -18,9 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/drivers/infiniband/qib7322.c b/qemu/roms/ipxe/src/drivers/infiniband/qib7322.c index 9979b346e..e22f2349a 100644 --- a/qemu/roms/ipxe/src/drivers/infiniband/qib7322.c +++ b/qemu/roms/ipxe/src/drivers/infiniband/qib7322.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/infiniband/qib7322.h b/qemu/roms/ipxe/src/drivers/infiniband/qib7322.h index 63abe221b..72797b240 100644 --- a/qemu/roms/ipxe/src/drivers/infiniband/qib7322.h +++ b/qemu/roms/ipxe/src/drivers/infiniband/qib7322.h @@ -18,9 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/drivers/net/amd8111e.h b/qemu/roms/ipxe/src/drivers/net/amd8111e.h index 2000df158..8ecd159af 100644 --- a/qemu/roms/ipxe/src/drivers/net/amd8111e.h +++ b/qemu/roms/ipxe/src/drivers/net/amd8111e.h @@ -16,6 +16,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. * USA Module Name: @@ -36,7 +40,7 @@ Revision History: 3.0.1 */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifndef _AMD811E_H #define _AMD811E_H diff --git a/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ani.h b/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ani.h index dbd4d4d5b..ba87ba0fd 100644 --- a/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ani.h +++ b/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ani.h @@ -125,7 +125,7 @@ struct ar5416AniState { u8 mrcCCKOff; u8 spurImmunityLevel; u8 firstepLevel; - u8 ofdmWeakSigDetectOff; + u8 ofdmWeakSigDetect; u8 cckWeakSigThreshold; u32 listenTime; int32_t rssiThrLow; diff --git a/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ani.c b/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ani.c index ff7df497f..76ca79cba 100644 --- a/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ani.c +++ b/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ani.c @@ -177,7 +177,7 @@ static void ath9k_hw_ani_ofdm_err_trigger_old(struct ath_hw *ah) rssi = BEACON_RSSI(ah); if (rssi > aniState->rssiThrHigh) { - if (!aniState->ofdmWeakSigDetectOff) { + if (aniState->ofdmWeakSigDetect) { if (ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, 0)) { @@ -192,7 +192,7 @@ static void ath9k_hw_ani_ofdm_err_trigger_old(struct ath_hw *ah) return; } } else if (rssi > aniState->rssiThrLow) { - if (aniState->ofdmWeakSigDetectOff) + if (!aniState->ofdmWeakSigDetect) ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, 1); @@ -202,7 +202,7 @@ static void ath9k_hw_ani_ofdm_err_trigger_old(struct ath_hw *ah) return; } else { if ((ah->dev->channels + ah->dev->channel)->band == NET80211_BAND_2GHZ) { - if (!aniState->ofdmWeakSigDetectOff) + if (aniState->ofdmWeakSigDetect) ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, 0); @@ -360,7 +360,7 @@ static void ath9k_hw_ani_lower_immunity_old(struct ath_hw *ah) if (rssi > aniState->rssiThrHigh) { /* XXX: Handle me */ } else if (rssi > aniState->rssiThrLow) { - if (aniState->ofdmWeakSigDetectOff) { + if (!aniState->ofdmWeakSigDetect) { if (ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, 1) == 1) @@ -436,9 +436,9 @@ static void ath9k_ani_reset_old(struct ath_hw *ah) if (aniState->spurImmunityLevel != 0) ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, aniState->spurImmunityLevel); - if (aniState->ofdmWeakSigDetectOff) + if (!aniState->ofdmWeakSigDetect) ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, - !aniState->ofdmWeakSigDetectOff); + aniState->ofdmWeakSigDetect); if (aniState->cckWeakSigThreshold) ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR, aniState->cckWeakSigThreshold); @@ -709,8 +709,8 @@ void ath9k_hw_ani_init(struct ath_hw *ah) ani->rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH; ani->rssiThrLow = ATH9K_ANI_RSSI_THR_LOW; - ani->ofdmWeakSigDetectOff = - !ATH9K_ANI_USE_OFDM_WEAK_SIG; + ani->ofdmWeakSigDetect = + ATH9K_ANI_USE_OFDM_WEAK_SIG; ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL; } diff --git a/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c b/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c index 60e87e9e2..2b6c133cb 100644 --- a/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c +++ b/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c @@ -1141,12 +1141,12 @@ static int ar5008_hw_ani_control_old(struct ath_hw *ah, REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); - if (!on != aniState->ofdmWeakSigDetectOff) { + if (on != aniState->ofdmWeakSigDetect) { if (on) ah->stats.ast_ani_ofdmon++; else ah->stats.ast_ani_ofdmoff++; - aniState->ofdmWeakSigDetectOff = !on; + aniState->ofdmWeakSigDetect = on; } break; } @@ -1215,10 +1215,10 @@ static int ar5008_hw_ani_control_old(struct ath_hw *ah, DBG2("ath9k: ANI parameters:\n"); DBG2( - "noiseImmunityLevel=%d, spurImmunityLevel=%d, ofdmWeakSigDetectOff=%d\n", + "noiseImmunityLevel=%d, spurImmunityLevel=%d, ofdmWeakSigDetect=%d\n", aniState->noiseImmunityLevel, aniState->spurImmunityLevel, - !aniState->ofdmWeakSigDetectOff); + aniState->ofdmWeakSigDetect); DBG2( "cckWeakSigThreshold=%d, firstepLevel=%d, listenTime=%d\n", aniState->cckWeakSigThreshold, @@ -1307,18 +1307,18 @@ static int ar5008_hw_ani_control_new(struct ath_hw *ah, REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); - if (!on != aniState->ofdmWeakSigDetectOff) { + if (on != aniState->ofdmWeakSigDetect) { DBG2("ath9k: " "** ch %d: ofdm weak signal: %s=>%s\n", chan->channel, - !aniState->ofdmWeakSigDetectOff ? + aniState->ofdmWeakSigDetect ? "on" : "off", on ? "on" : "off"); if (on) ah->stats.ast_ani_ofdmon++; else ah->stats.ast_ani_ofdmoff++; - aniState->ofdmWeakSigDetectOff = !on; + aniState->ofdmWeakSigDetect = on; } break; } @@ -1467,7 +1467,7 @@ static int ar5008_hw_ani_control_new(struct ath_hw *ah, DBG2("ath9k: " "ANI parameters: SI=%d, ofdmWS=%s FS=%d MRCcck=%s listenTime=%d ofdmErrs=%d cckErrs=%d\n", aniState->spurImmunityLevel, - !aniState->ofdmWeakSigDetectOff ? "on" : "off", + aniState->ofdmWeakSigDetect ? "on" : "off", aniState->firstepLevel, !aniState->mrcCCKOff ? "on" : "off", aniState->listenTime, @@ -1554,7 +1554,7 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah) /* these levels just got reset to defaults by the INI */ aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL_NEW; aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW; - aniState->ofdmWeakSigDetectOff = !ATH9K_ANI_USE_OFDM_WEAK_SIG; + aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; aniState->mrcCCKOff = 1; /* not available on pre AR9003 */ } diff --git a/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c b/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c index 6103040ab..2244b775a 100644 --- a/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c +++ b/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c @@ -859,18 +859,18 @@ static int ar9003_hw_ani_control(struct ath_hw *ah, REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); - if (!on != aniState->ofdmWeakSigDetectOff) { + if (on != aniState->ofdmWeakSigDetect) { DBG2("ath9k: " "** ch %d: ofdm weak signal: %s=>%s\n", chan->channel, - !aniState->ofdmWeakSigDetectOff ? + aniState->ofdmWeakSigDetect ? "on" : "off", on ? "on" : "off"); if (on) ah->stats.ast_ani_ofdmon++; else ah->stats.ast_ani_ofdmoff++; - aniState->ofdmWeakSigDetectOff = !on; + aniState->ofdmWeakSigDetect = on; } break; } @@ -1013,7 +1013,7 @@ static int ar9003_hw_ani_control(struct ath_hw *ah, AR_PHY_MRC_CCK_ENABLE, is_on); REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL, AR_PHY_MRC_CCK_MUX_REG, is_on); - if (!is_on != aniState->mrcCCKOff) { + if (!(is_on != aniState->mrcCCKOff)) { DBG2("ath9k: " "** ch %d: MRC CCK: %s=>%s\n", chan->channel, @@ -1037,7 +1037,7 @@ static int ar9003_hw_ani_control(struct ath_hw *ah, DBG2("ath9k: " "ANI parameters: SI=%d, ofdmWS=%s FS=%d MRCcck=%s listenTime=%d ofdmErrs=%d cckErrs=%d\n", aniState->spurImmunityLevel, - !aniState->ofdmWeakSigDetectOff ? "on" : "off", + aniState->ofdmWeakSigDetect ? "on" : "off", aniState->firstepLevel, !aniState->mrcCCKOff ? "on" : "off", aniState->listenTime, @@ -1137,7 +1137,7 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah) /* these levels just got reset to defaults by the INI */ aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL_NEW; aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW; - aniState->ofdmWeakSigDetectOff = !ATH9K_ANI_USE_OFDM_WEAK_SIG; + aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; aniState->mrcCCKOff = !ATH9K_ANI_ENABLE_MRC_CCK; } diff --git a/qemu/roms/ipxe/src/drivers/net/atl1e.c b/qemu/roms/ipxe/src/drivers/net/atl1e.c index 1ff0f0d10..d010d8c4a 100644 --- a/qemu/roms/ipxe/src/drivers/net/atl1e.c +++ b/qemu/roms/ipxe/src/drivers/net/atl1e.c @@ -224,7 +224,7 @@ static int atl1e_sw_init(struct atl1e_adapter *adapter) adapter->link_duplex = FULL_DUPLEX; /* PCI config space info */ - pci_read_config_byte(pdev, PCI_REVISION_ID, &rev_id); + pci_read_config_byte(pdev, PCI_REVISION, &rev_id); phy_status_data = AT_READ_REG(hw, REG_PHY_STATUS); /* nic type */ diff --git a/qemu/roms/ipxe/src/drivers/net/davicom.c b/qemu/roms/ipxe/src/drivers/net/davicom.c index a4870a729..9d3d8b915 100644 --- a/qemu/roms/ipxe/src/drivers/net/davicom.c +++ b/qemu/roms/ipxe/src/drivers/net/davicom.c @@ -340,6 +340,7 @@ static void davicom_media_chk(struct nic * nic __unused) csr6 = 0x00200000; /* SF */ outl(csr6, ioaddr + CSR6); +#define PCI_VENDOR_ID_DAVICOM 0x1282 #define PCI_DEVICE_ID_DM9009 0x9009 if (vendor == PCI_VENDOR_ID_DAVICOM && dev_id == PCI_DEVICE_ID_DM9009) { /* Set to 10BaseT mode for DM9009 */ diff --git a/qemu/roms/ipxe/src/drivers/net/dm96xx.c b/qemu/roms/ipxe/src/drivers/net/dm96xx.c new file mode 100644 index 000000000..58d8dd964 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/dm96xx.c @@ -0,0 +1,671 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include "dm96xx.h" + +/** @file + * + * Davicom DM96xx USB Ethernet driver + * + */ + +/****************************************************************************** + * + * Register operations + * + ****************************************************************************** + */ + +/** + * Reset device + * + * @v dm96xx DM96xx device + * @ret rc Return status code + */ +static int dm96xx_reset ( struct dm96xx_device *dm96xx ) { + int ncr; + int rc; + + /* Reset device */ + if ( ( rc = dm96xx_write_register ( dm96xx, DM96XX_NCR, + DM96XX_NCR_RST ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not reset: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + /* Wait for reset to complete */ + udelay ( DM96XX_RESET_DELAY_US ); + + /* Check that reset has completed */ + ncr = dm96xx_read_register ( dm96xx, DM96XX_NCR ); + if ( ncr < 0 ) { + rc = ncr; + DBGC ( dm96xx, "DM96XX %p failed to reset: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + if ( ncr & DM96XX_NCR_RST ) { + DBGC ( dm96xx, "DM96XX %p failed to reset (NCR=%#02x)\n", + dm96xx, ncr ); + return -EIO; + } + + return 0; +} + +/** + * Read MAC address + * + * @v dm96xx DM96xx device + * @v mac MAC address to fill in + * @ret rc Return status code + */ +static int dm96xx_read_mac ( struct dm96xx_device *dm96xx, uint8_t *mac ) { + int rc; + + /* Read MAC address */ + if ( ( rc = dm96xx_read_registers ( dm96xx, DM96XX_PAR, mac, + ETH_ALEN ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not read MAC address: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Write MAC address + * + * @v dm96xx DM96xx device + * @v mac MAC address + * @ret rc Return status code + */ +static int dm96xx_write_mac ( struct dm96xx_device *dm96xx, uint8_t *mac ) { + int rc; + + /* Write MAC address */ + if ( ( rc = dm96xx_write_registers ( dm96xx, DM96XX_PAR, mac, + ETH_ALEN ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not write MAC address: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Update link status based on network status register + * + * @v dm96xx DM96xx device + * @v nsr Network status register + */ +static void dm96xx_link_nsr ( struct dm96xx_device *dm96xx, unsigned int nsr ) { + struct net_device *netdev = dm96xx->netdev; + + if ( nsr & DM96XX_NSR_LINKST ) { + if ( ! netdev_link_ok ( netdev ) ) + netdev_link_up ( netdev ); + } else { + if ( netdev_link_ok ( netdev ) ) + netdev_link_down ( netdev ); + } +} + +/** + * Get link status + * + * @v dm96xx DM96xx device + * @ret rc Return status code + */ +static int dm96xx_check_link ( struct dm96xx_device *dm96xx ) { + int nsr; + int rc; + + /* Read network status register */ + nsr = dm96xx_read_register ( dm96xx, DM96XX_NSR ); + if ( nsr < 0 ) { + rc = nsr; + DBGC ( dm96xx, "DM96XX %p could not read network status: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + /* Update link status */ + dm96xx_link_nsr ( dm96xx, nsr ); + + return 0; +} + +/** + * Set DM9601-compatible RX header mode + * + * @v dm96xx DM96xx device + * @ret rc Return status code + */ +static int dm96xx_rx_mode ( struct dm96xx_device *dm96xx ) { + int chipr; + int mode_ctl; + int rc; + + /* Get chip revision */ + chipr = dm96xx_read_register ( dm96xx, DM96XX_CHIPR ); + if ( chipr < 0 ) { + rc = chipr; + DBGC ( dm96xx, "DM96XX %p could not read chip revision: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + /* Do nothing if device is a DM9601 anyway */ + if ( chipr == DM96XX_CHIPR_9601 ) + return 0; + + /* Read current mode control */ + mode_ctl = dm96xx_read_register ( dm96xx, DM96XX_MODE_CTL ); + if ( mode_ctl < 0 ) { + rc = mode_ctl; + DBGC ( dm96xx, "DM96XX %p could not read mode control: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + /* Write mode control */ + mode_ctl &= ~DM96XX_MODE_CTL_MODE; + if ( ( rc = dm96xx_write_register ( dm96xx, DM96XX_MODE_CTL, + mode_ctl ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not write mode control: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/****************************************************************************** + * + * Endpoint operations + * + ****************************************************************************** + */ + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void dm96xx_intr_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct dm96xx_device *dm96xx = container_of ( ep, struct dm96xx_device, + usbnet.intr ); + struct net_device *netdev = dm96xx->netdev; + struct dm96xx_interrupt *intr; + size_t len = iob_len ( iobuf ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto done; + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( dm96xx, "DM96XX %p interrupt failed: %s\n", + dm96xx, strerror ( rc ) ); + DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, NULL, rc ); + goto done; + } + + /* Extract message header */ + if ( len < sizeof ( *intr ) ) { + DBGC ( dm96xx, "DM96XX %p underlength interrupt:\n", dm96xx ); + DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, NULL, -EINVAL ); + goto done; + } + intr = iobuf->data; + + /* Update link status */ + dm96xx_link_nsr ( dm96xx, intr->nsr ); + + done: + /* Free I/O buffer */ + free_iob ( iobuf ); +} + +/** Interrupt endpoint operations */ +static struct usb_endpoint_driver_operations dm96xx_intr_operations = { + .complete = dm96xx_intr_complete, +}; + +/** + * Complete bulk IN transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void dm96xx_in_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct dm96xx_device *dm96xx = container_of ( ep, struct dm96xx_device, + usbnet.in ); + struct net_device *netdev = dm96xx->netdev; + struct dm96xx_rx_header *header; + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) { + free_iob ( iobuf ); + return; + } + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( dm96xx, "DM96XX %p bulk IN failed: %s\n", + dm96xx, strerror ( rc ) ); + goto err; + } + + /* Sanity check */ + if ( iob_len ( iobuf ) < ( sizeof ( *header ) + 4 /* CRC */ ) ) { + DBGC ( dm96xx, "DM96XX %p underlength bulk IN\n", dm96xx ); + DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto err; + } + + /* Strip header and CRC */ + header = iobuf->data; + iob_pull ( iobuf, sizeof ( *header ) ); + iob_unput ( iobuf, 4 /* CRC */ ); + + /* Check status */ + if ( header->rsr & ~DM96XX_RSR_MF ) { + DBGC ( dm96xx, "DM96XX %p receive error %02x:\n", + dm96xx, header->rsr ); + DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EIO; + goto err; + } + + /* Hand off to network stack */ + netdev_rx ( netdev, iob_disown ( iobuf ) ); + return; + + err: + /* Hand off to network stack */ + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); +} + +/** Bulk IN endpoint operations */ +static struct usb_endpoint_driver_operations dm96xx_in_operations = { + .complete = dm96xx_in_complete, +}; + +/** + * Transmit packet + * + * @v dm96xx DM96xx device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int dm96xx_out_transmit ( struct dm96xx_device *dm96xx, + struct io_buffer *iobuf ) { + struct dm96xx_tx_header *header; + size_t len = iob_len ( iobuf ); + int rc; + + /* Prepend header */ + if ( ( rc = iob_ensure_headroom ( iobuf, sizeof ( *header ) ) ) != 0 ) + return rc; + header = iob_push ( iobuf, sizeof ( *header ) ); + header->len = cpu_to_le16 ( len ); + + /* Enqueue I/O buffer */ + if ( ( rc = usb_stream ( &dm96xx->usbnet.out, iobuf, 0 ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Complete bulk OUT transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void dm96xx_out_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct dm96xx_device *dm96xx = container_of ( ep, struct dm96xx_device, + usbnet.out ); + struct net_device *netdev = dm96xx->netdev; + + /* Report TX completion */ + netdev_tx_complete_err ( netdev, iobuf, rc ); +} + +/** Bulk OUT endpoint operations */ +static struct usb_endpoint_driver_operations dm96xx_out_operations = { + .complete = dm96xx_out_complete, +}; + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int dm96xx_open ( struct net_device *netdev ) { + struct dm96xx_device *dm96xx = netdev->priv; + unsigned int rcr; + int rc; + + /* Set DM9601-compatible RX header mode */ + if ( ( rc = dm96xx_rx_mode ( dm96xx ) ) != 0 ) + goto err_rx_mode; + + /* Write MAC address */ + if ( ( rc = dm96xx_write_mac ( dm96xx, netdev->ll_addr ) ) != 0 ) + goto err_write_mac; + + /* Open USB network device */ + if ( ( rc = usbnet_open ( &dm96xx->usbnet ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not open: %s\n", + dm96xx, strerror ( rc ) ); + goto err_open; + } + + /* Set receive filters */ + rcr = ( DM96XX_RCR_ALL | DM96XX_RCR_RUNT | DM96XX_RCR_PRMSC | + DM96XX_RCR_RXEN ); + if ( ( rc = dm96xx_write_register ( dm96xx, DM96XX_RCR, rcr ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not write receive filters: " + "%s\n", dm96xx, strerror ( rc ) ); + goto err_write_rcr; + } + + /* Update link status */ + if ( ( rc = dm96xx_check_link ( dm96xx ) ) != 0 ) + goto err_check_link; + + return 0; + + err_check_link: + err_write_rcr: + usbnet_close ( &dm96xx->usbnet ); + err_open: + err_write_mac: + err_rx_mode: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void dm96xx_close ( struct net_device *netdev ) { + struct dm96xx_device *dm96xx = netdev->priv; + + /* Close USB network device */ + usbnet_close ( &dm96xx->usbnet ); + + /* Reset device */ + dm96xx_reset ( dm96xx ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int dm96xx_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct dm96xx_device *dm96xx = netdev->priv; + int rc; + + /* Transmit packet */ + if ( ( rc = dm96xx_out_transmit ( dm96xx, iobuf ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void dm96xx_poll ( struct net_device *netdev ) { + struct dm96xx_device *dm96xx = netdev->priv; + int rc; + + /* Poll USB bus */ + usb_poll ( dm96xx->bus ); + + /* Refill endpoints */ + if ( ( rc = usbnet_refill ( &dm96xx->usbnet ) ) != 0 ) + netdev_rx_err ( netdev, NULL, rc ); +} + +/** DM96xx network device operations */ +static struct net_device_operations dm96xx_operations = { + .open = dm96xx_open, + .close = dm96xx_close, + .transmit = dm96xx_transmit, + .poll = dm96xx_poll, +}; + +/****************************************************************************** + * + * USB interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int dm96xx_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct net_device *netdev; + struct dm96xx_device *dm96xx; + int rc; + + /* Allocate and initialise structure */ + netdev = alloc_etherdev ( sizeof ( *dm96xx ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &dm96xx_operations ); + netdev->dev = &func->dev; + dm96xx = netdev->priv; + memset ( dm96xx, 0, sizeof ( *dm96xx ) ); + dm96xx->usb = usb; + dm96xx->bus = usb->port->hub->bus; + dm96xx->netdev = netdev; + usbnet_init ( &dm96xx->usbnet, func, &dm96xx_intr_operations, + &dm96xx_in_operations, &dm96xx_out_operations ); + usb_refill_init ( &dm96xx->usbnet.intr, 0, DM96XX_INTR_MAX_FILL ); + usb_refill_init ( &dm96xx->usbnet.in, DM96XX_IN_MTU, + DM96XX_IN_MAX_FILL ); + DBGC ( dm96xx, "DM96XX %p on %s\n", dm96xx, func->name ); + + /* Describe USB network device */ + if ( ( rc = usbnet_describe ( &dm96xx->usbnet, config ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not describe: %s\n", + dm96xx, strerror ( rc ) ); + goto err_describe; + } + + /* Reset device */ + if ( ( rc = dm96xx_reset ( dm96xx ) ) != 0 ) + goto err_reset; + + /* Read MAC address */ + if ( ( rc = dm96xx_read_mac ( dm96xx, netdev->hw_addr ) ) != 0 ) + goto err_read_mac; + + /* Get initial link status */ + if ( ( rc = dm96xx_check_link ( dm96xx ) ) != 0 ) + goto err_check_link; + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + usb_func_set_drvdata ( func, netdev ); + return 0; + + unregister_netdev ( netdev ); + err_register: + err_check_link: + err_read_mac: + err_reset: + err_describe: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v func USB function + */ +static void dm96xx_remove ( struct usb_function *func ) { + struct net_device *netdev = usb_func_get_drvdata ( func ); + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** DM96xx device IDs */ +static struct usb_device_id dm96xx_ids[] = { + { + .name = "dm9601-corega", + .vendor = 0x07aa, + .product = 0x9601, + }, + { + .name = "dm9601", + .vendor = 0x0a46, + .product = 0x9601, + }, + { + .name = "zt6688", + .vendor = 0x0a46, + .product = 0x6688, + }, + { + .name = "st268", + .vendor = 0x0a46, + .product = 0x0268, + }, + { + .name = "adm8515", + .vendor = 0x0a46, + .product = 0x8515, + }, + { + .name = "dm9601-hirose", + .vendor = 0x0a47, + .product = 0x9601, + }, + { + .name = "dm9601-8101", + .vendor = 0x0fe6, + .product = 0x8101, + }, + { + .name = "dm9601-9700", + .vendor = 0x0fe6, + .product = 0x9700, + }, + { + .name = "dm9000e", + .vendor = 0x0a46, + .product = 0x9000, + }, + { + .name = "dm9620", + .vendor = 0x0a46, + .product = 0x9620, + }, + { + .name = "dm9621A", + .vendor = 0x0a46, + .product = 0x9621, + }, + { + .name = "dm9622", + .vendor = 0x0a46, + .product = 0x9622, + }, + { + .name = "dm962Oa", + .vendor = 0x0a46, + .product = 0x0269, + }, + { + .name = "dm9621a", + .vendor = 0x0a46, + .product = 0x1269, + }, +}; + +/** Davicom DM96xx driver */ +struct usb_driver dm96xx_driver __usb_driver = { + .ids = dm96xx_ids, + .id_count = ( sizeof ( dm96xx_ids ) / sizeof ( dm96xx_ids[0] ) ), + .probe = dm96xx_probe, + .remove = dm96xx_remove, +}; diff --git a/qemu/roms/ipxe/src/drivers/net/dm96xx.h b/qemu/roms/ipxe/src/drivers/net/dm96xx.h new file mode 100644 index 000000000..43a1a4e30 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/dm96xx.h @@ -0,0 +1,194 @@ +#ifndef _DM96XX_H +#define _DM96XX_H + +/** @file + * + * Davicom DM96xx USB Ethernet driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** Read register(s) */ +#define DM96XX_READ_REGISTER \ + ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0x00 ) ) + +/** Write register(s) */ +#define DM96XX_WRITE_REGISTER \ + ( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0x01 ) ) + +/** Write single register */ +#define DM96XX_WRITE1_REGISTER \ + ( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0x03 ) ) + +/** Network control register */ +#define DM96XX_NCR 0x00 +#define DM96XX_NCR_RST 0x01 /**< Software reset */ + +/** Network status register */ +#define DM96XX_NSR 0x01 +#define DM96XX_NSR_LINKST 0x40 /**< Link status */ + +/** Receive control register */ +#define DM96XX_RCR 0x05 +#define DM96XX_RCR_ALL 0x08 /**< Pass all multicast */ +#define DM96XX_RCR_RUNT 0x04 /**< Pass runt packet */ +#define DM96XX_RCR_PRMSC 0x02 /**< Promiscuous mode */ +#define DM96XX_RCR_RXEN 0x01 /**< RX enable */ + +/** Receive status register */ +#define DM96XX_RSR 0x06 +#define DM96XX_RSR_MF 0x40 /**< Multicast frame */ + +/** PHY address registers */ +#define DM96XX_PAR 0x10 + +/** Chip revision register */ +#define DM96XX_CHIPR 0x2c +#define DM96XX_CHIPR_9601 0x00 /**< DM9601 */ +#define DM96XX_CHIPR_9620 0x01 /**< DM9620 */ + +/** RX header control/status register (DM9620+ only) */ +#define DM96XX_MODE_CTL 0x91 +#define DM96XX_MODE_CTL_MODE 0x80 /**< 4-byte header mode */ + +/** DM96xx interrupt data */ +struct dm96xx_interrupt { + /** Network status register */ + uint8_t nsr; + /** Transmit status registers */ + uint8_t tsr[2]; + /** Receive status register */ + uint8_t rsr; + /** Receive overflow counter register */ + uint8_t rocr; + /** Receive packet counter */ + uint8_t rxc; + /** Transmit packet counter */ + uint8_t txc; + /** General purpose register */ + uint8_t gpr; +} __attribute__ (( packed )); + +/** DM96xx receive header */ +struct dm96xx_rx_header { + /** Packet status */ + uint8_t rsr; + /** Packet length (excluding this header, including CRC) */ + uint16_t len; +} __attribute__ (( packed )); + +/** DM96xx transmit header */ +struct dm96xx_tx_header { + /** Packet length (excluding this header) */ + uint16_t len; +} __attribute__ (( packed )); + +/** A DM96xx network device */ +struct dm96xx_device { + /** USB device */ + struct usb_device *usb; + /** USB bus */ + struct usb_bus *bus; + /** Network device */ + struct net_device *netdev; + /** USB network device */ + struct usbnet_device usbnet; +}; + +/** + * Read registers + * + * @v dm96xx DM96xx device + * @v offset Register offset + * @v data Data buffer + * @v len Length of data + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +dm96xx_read_registers ( struct dm96xx_device *dm96xx, unsigned int offset, + void *data, size_t len ) { + + return usb_control ( dm96xx->usb, DM96XX_READ_REGISTER, 0, offset, + data, len ); +} + +/** + * Read register + * + * @v dm96xx DM96xx device + * @v offset Register offset + * @ret value Register value, or negative error + */ +static inline __attribute__ (( always_inline )) int +dm96xx_read_register ( struct dm96xx_device *dm96xx, unsigned int offset ) { + uint8_t value; + int rc; + + if ( ( rc = dm96xx_read_registers ( dm96xx, offset, &value, + sizeof ( value ) ) ) != 0 ) + return rc; + return value; +} + +/** + * Write registers + * + * @v dm96xx DM96xx device + * @v offset Register offset + * @v data Data buffer + * @v len Length of data + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +dm96xx_write_registers ( struct dm96xx_device *dm96xx, unsigned int offset, + void *data, size_t len ) { + + return usb_control ( dm96xx->usb, DM96XX_WRITE_REGISTER, 0, offset, + data, len ); +} + +/** + * Write register + * + * @v dm96xx DM96xx device + * @v offset Register offset + * @v value Register value + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +dm96xx_write_register ( struct dm96xx_device *dm96xx, unsigned int offset, + uint8_t value ) { + + return usb_control ( dm96xx->usb, DM96XX_WRITE1_REGISTER, value, + offset, NULL, 0 ); +} + +/** Reset delay (in microseconds) */ +#define DM96XX_RESET_DELAY_US 10 + +/** Interrupt maximum fill level + * + * This is a policy decision. + */ +#define DM96XX_INTR_MAX_FILL 2 + +/** Bulk IN maximum fill level + * + * This is a policy decision. + */ +#define DM96XX_IN_MAX_FILL 8 + +/** Bulk IN buffer size */ +#define DM96XX_IN_MTU \ + ( 4 /* DM96xx header */ + ETH_FRAME_LEN + \ + 4 /* possible VLAN header */ + 4 /* CRC */ ) + +#endif /* _DM96XX_H */ diff --git a/qemu/roms/ipxe/src/drivers/net/dmfe.c b/qemu/roms/ipxe/src/drivers/net/dmfe.c index aae40fce7..2ea0d2b2b 100644 --- a/qemu/roms/ipxe/src/drivers/net/dmfe.c +++ b/qemu/roms/ipxe/src/drivers/net/dmfe.c @@ -462,7 +462,7 @@ static int dmfe_probe ( struct nic *nic, struct pci_device *pci ) { pci->id->name, pci->vendor, pci->device); /* Read Chip revision */ - pci_read_config_dword(pci, PCI_REVISION_ID, &dev_rev); + pci_read_config_dword(pci, PCI_REVISION, &dev_rev); dprintf(("Revision %lX\n", dev_rev)); /* point to private storage */ diff --git a/qemu/roms/ipxe/src/drivers/net/ecm.c b/qemu/roms/ipxe/src/drivers/net/ecm.c new file mode 100644 index 000000000..8c84ea9e9 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/ecm.c @@ -0,0 +1,520 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ecm.h" + +/** @file + * + * CDC-ECM USB Ethernet driver + * + */ + +/** Interrupt completion profiler */ +static struct profiler ecm_intr_profiler __profiler = + { .name = "ecm.intr" }; + +/** Bulk IN completion profiler */ +static struct profiler ecm_in_profiler __profiler = + { .name = "ecm.in" }; + +/** Bulk OUT profiler */ +static struct profiler ecm_out_profiler __profiler = + { .name = "ecm.out" }; + +/****************************************************************************** + * + * Ethernet functional descriptor + * + ****************************************************************************** + */ + +/** + * Locate Ethernet functional descriptor + * + * @v config Configuration descriptor + * @v interface Interface descriptor + * @ret desc Descriptor, or NULL if not found + */ +struct ecm_ethernet_descriptor * +ecm_ethernet_descriptor ( struct usb_configuration_descriptor *config, + struct usb_interface_descriptor *interface ) { + struct ecm_ethernet_descriptor *desc; + + for_each_interface_descriptor ( desc, config, interface ) { + if ( ( desc->header.type == USB_CS_INTERFACE_DESCRIPTOR ) && + ( desc->subtype == CDC_SUBTYPE_ETHERNET ) ) + return desc; + } + return NULL; +} + +/** + * Get hardware MAC address + * + * @v usb USB device + * @v desc Ethernet functional descriptor + * @v hw_addr Hardware address to fill in + * @ret rc Return status code + */ +int ecm_fetch_mac ( struct usb_device *usb, + struct ecm_ethernet_descriptor *desc, uint8_t *hw_addr ) { + char buf[ base16_encoded_len ( ETH_ALEN ) + 1 /* NUL */ ]; + int len; + int rc; + + /* Fetch MAC address string */ + len = usb_get_string_descriptor ( usb, desc->mac, 0, buf, + sizeof ( buf ) ); + if ( len < 0 ) { + rc = len; + return rc; + } + + /* Sanity check */ + if ( len != ( ( int ) ( sizeof ( buf ) - 1 /* NUL */ ) ) ) + return -EINVAL; + + /* Decode MAC address */ + len = base16_decode ( buf, hw_addr, ETH_ALEN ); + if ( len < 0 ) { + rc = len; + return rc; + } + + return 0; +} + +/****************************************************************************** + * + * CDC-ECM communications interface + * + ****************************************************************************** + */ + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void ecm_intr_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct ecm_device *ecm = container_of ( ep, struct ecm_device, + usbnet.intr ); + struct net_device *netdev = ecm->netdev; + struct usb_setup_packet *message; + size_t len = iob_len ( iobuf ); + + /* Profile completions */ + profile_start ( &ecm_intr_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto ignore; + + /* Drop packets with errors */ + if ( rc != 0 ) { + DBGC ( ecm, "ECM %p interrupt failed: %s\n", + ecm, strerror ( rc ) ); + DBGC_HDA ( ecm, 0, iobuf->data, iob_len ( iobuf ) ); + goto error; + } + + /* Extract message header */ + if ( len < sizeof ( *message ) ) { + DBGC ( ecm, "ECM %p underlength interrupt:\n", ecm ); + DBGC_HDA ( ecm, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto error; + } + message = iobuf->data; + + /* Parse message header */ + switch ( message->request ) { + + case cpu_to_le16 ( CDC_NETWORK_CONNECTION ) : + if ( message->value && ! netdev_link_ok ( netdev ) ) { + DBGC ( ecm, "ECM %p link up\n", ecm ); + netdev_link_up ( netdev ); + } else if ( netdev_link_ok ( netdev ) && ! message->value ) { + DBGC ( ecm, "ECM %p link down\n", ecm ); + netdev_link_down ( netdev ); + } + break; + + case cpu_to_le16 ( CDC_CONNECTION_SPEED_CHANGE ) : + /* Ignore */ + break; + + default: + DBGC ( ecm, "ECM %p unrecognised interrupt:\n", ecm ); + DBGC_HDA ( ecm, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -ENOTSUP; + goto error; + } + + /* Free I/O buffer */ + free_iob ( iobuf ); + profile_stop ( &ecm_intr_profiler ); + + return; + + error: + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); + ignore: + free_iob ( iobuf ); + return; +} + +/** Interrupt endpoint operations */ +static struct usb_endpoint_driver_operations ecm_intr_operations = { + .complete = ecm_intr_complete, +}; + +/****************************************************************************** + * + * CDC-ECM data interface + * + ****************************************************************************** + */ + +/** + * Complete bulk IN transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void ecm_in_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int rc ) { + struct ecm_device *ecm = container_of ( ep, struct ecm_device, + usbnet.in ); + struct net_device *netdev = ecm->netdev; + + /* Profile receive completions */ + profile_start ( &ecm_in_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto ignore; + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( ecm, "ECM %p bulk IN failed: %s\n", + ecm, strerror ( rc ) ); + goto error; + } + + /* Hand off to network stack */ + netdev_rx ( netdev, iob_disown ( iobuf ) ); + + profile_stop ( &ecm_in_profiler ); + return; + + error: + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); + ignore: + free_iob ( iobuf ); +} + +/** Bulk IN endpoint operations */ +static struct usb_endpoint_driver_operations ecm_in_operations = { + .complete = ecm_in_complete, +}; + +/** + * Transmit packet + * + * @v ecm CDC-ECM device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int ecm_out_transmit ( struct ecm_device *ecm, + struct io_buffer *iobuf ) { + int rc; + + /* Profile transmissions */ + profile_start ( &ecm_out_profiler ); + + /* Enqueue I/O buffer */ + if ( ( rc = usb_stream ( &ecm->usbnet.out, iobuf, 1 ) ) != 0 ) + return rc; + + profile_stop ( &ecm_out_profiler ); + return 0; +} + +/** + * Complete bulk OUT transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void ecm_out_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int rc ) { + struct ecm_device *ecm = container_of ( ep, struct ecm_device, + usbnet.out ); + struct net_device *netdev = ecm->netdev; + + /* Report TX completion */ + netdev_tx_complete_err ( netdev, iobuf, rc ); +} + +/** Bulk OUT endpoint operations */ +static struct usb_endpoint_driver_operations ecm_out_operations = { + .complete = ecm_out_complete, +}; + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ecm_open ( struct net_device *netdev ) { + struct ecm_device *ecm = netdev->priv; + struct usb_device *usb = ecm->usb; + unsigned int filter; + int rc; + + /* Open USB network device */ + if ( ( rc = usbnet_open ( &ecm->usbnet ) ) != 0 ) { + DBGC ( ecm, "ECM %p could not open: %s\n", + ecm, strerror ( rc ) ); + goto err_open; + } + + /* Set packet filter */ + filter = ( ECM_PACKET_TYPE_PROMISCUOUS | + ECM_PACKET_TYPE_ALL_MULTICAST | + ECM_PACKET_TYPE_DIRECTED | + ECM_PACKET_TYPE_BROADCAST ); + if ( ( rc = usb_control ( usb, ECM_SET_ETHERNET_PACKET_FILTER, + filter, ecm->usbnet.comms, NULL, 0 ) ) != 0 ){ + DBGC ( ecm, "ECM %p could not set packet filter: %s\n", + ecm, strerror ( rc ) ); + goto err_set_filter; + } + + return 0; + + err_set_filter: + usbnet_close ( &ecm->usbnet ); + err_open: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void ecm_close ( struct net_device *netdev ) { + struct ecm_device *ecm = netdev->priv; + + /* Close USB network device */ + usbnet_close ( &ecm->usbnet ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int ecm_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct ecm_device *ecm = netdev->priv; + int rc; + + /* Transmit packet */ + if ( ( rc = ecm_out_transmit ( ecm, iobuf ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void ecm_poll ( struct net_device *netdev ) { + struct ecm_device *ecm = netdev->priv; + int rc; + + /* Poll USB bus */ + usb_poll ( ecm->bus ); + + /* Refill endpoints */ + if ( ( rc = usbnet_refill ( &ecm->usbnet ) ) != 0 ) + netdev_rx_err ( netdev, NULL, rc ); +} + +/** CDC-ECM network device operations */ +static struct net_device_operations ecm_operations = { + .open = ecm_open, + .close = ecm_close, + .transmit = ecm_transmit, + .poll = ecm_poll, +}; + +/****************************************************************************** + * + * USB interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int ecm_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct net_device *netdev; + struct ecm_device *ecm; + struct usb_interface_descriptor *comms; + struct ecm_ethernet_descriptor *ethernet; + int rc; + + /* Allocate and initialise structure */ + netdev = alloc_etherdev ( sizeof ( *ecm ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &ecm_operations ); + netdev->dev = &func->dev; + ecm = netdev->priv; + memset ( ecm, 0, sizeof ( *ecm ) ); + ecm->usb = usb; + ecm->bus = usb->port->hub->bus; + ecm->netdev = netdev; + usbnet_init ( &ecm->usbnet, func, &ecm_intr_operations, + &ecm_in_operations, &ecm_out_operations ); + usb_refill_init ( &ecm->usbnet.intr, 0, ECM_INTR_MAX_FILL ); + usb_refill_init ( &ecm->usbnet.in, ECM_IN_MTU, ECM_IN_MAX_FILL ); + DBGC ( ecm, "ECM %p on %s\n", ecm, func->name ); + + /* Describe USB network device */ + if ( ( rc = usbnet_describe ( &ecm->usbnet, config ) ) != 0 ) { + DBGC ( ecm, "ECM %p could not describe: %s\n", + ecm, strerror ( rc ) ); + goto err_describe; + } + + /* Locate Ethernet descriptor */ + comms = usb_interface_descriptor ( config, ecm->usbnet.comms, 0 ); + assert ( comms != NULL ); + ethernet = ecm_ethernet_descriptor ( config, comms ); + if ( ! ethernet ) { + DBGC ( ecm, "ECM %p has no Ethernet descriptor\n", ecm ); + rc = -EINVAL; + goto err_ethernet; + } + + /* Fetch MAC address */ + if ( ( rc = ecm_fetch_mac ( usb, ethernet, netdev->hw_addr ) ) != 0 ) { + DBGC ( ecm, "ECM %p could not fetch MAC address: %s\n", + ecm, strerror ( rc ) ); + goto err_fetch_mac; + } + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + usb_func_set_drvdata ( func, ecm ); + return 0; + + unregister_netdev ( netdev ); + err_register: + err_fetch_mac: + err_ethernet: + err_describe: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v func USB function + */ +static void ecm_remove ( struct usb_function *func ) { + struct ecm_device *ecm = usb_func_get_drvdata ( func ); + struct net_device *netdev = ecm->netdev; + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** CDC-ECM device IDs */ +static struct usb_device_id ecm_ids[] = { + { + .name = "cdc-ecm", + .vendor = USB_ANY_ID, + .product = USB_ANY_ID, + .class = { + .class = USB_CLASS_CDC, + .subclass = USB_SUBCLASS_CDC_ECM, + .protocol = 0, + }, + }, +}; + +/** CDC-ECM driver */ +struct usb_driver ecm_driver __usb_driver = { + .ids = ecm_ids, + .id_count = ( sizeof ( ecm_ids ) / sizeof ( ecm_ids[0] ) ), + .probe = ecm_probe, + .remove = ecm_remove, +}; diff --git a/qemu/roms/ipxe/src/drivers/net/ecm.h b/qemu/roms/ipxe/src/drivers/net/ecm.h new file mode 100644 index 000000000..83d324bdc --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/ecm.h @@ -0,0 +1,93 @@ +#ifndef _ECM_H +#define _ECM_H + +/** @file + * + * CDC-ECM USB Ethernet driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** CDC-ECM subclass */ +#define USB_SUBCLASS_CDC_ECM 0x06 + +/** Set Ethernet packet filter */ +#define ECM_SET_ETHERNET_PACKET_FILTER \ + ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x43 ) ) + +/** Ethernet packet types */ +enum ecm_ethernet_packet_filter { + /** Promiscuous mode */ + ECM_PACKET_TYPE_PROMISCUOUS = 0x0001, + /** All multicast packets */ + ECM_PACKET_TYPE_ALL_MULTICAST = 0x0002, + /** Unicast packets */ + ECM_PACKET_TYPE_DIRECTED = 0x0004, + /** Broadcast packets */ + ECM_PACKET_TYPE_BROADCAST = 0x0008, + /** Specified multicast packets */ + ECM_PACKET_TYPE_MULTICAST = 0x0010, +}; + +/** An Ethernet Functional Descriptor */ +struct ecm_ethernet_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** Descriptor subtype */ + uint8_t subtype; + /** MAC address string */ + uint8_t mac; + /** Ethernet statistics bitmap */ + uint32_t statistics; + /** Maximum segment size */ + uint16_t mtu; + /** Multicast filter configuration */ + uint16_t mcast; + /** Number of wake-on-LAN filters */ + uint8_t wol; +} __attribute__ (( packed )); + +/** A CDC-ECM network device */ +struct ecm_device { + /** USB device */ + struct usb_device *usb; + /** USB bus */ + struct usb_bus *bus; + /** Network device */ + struct net_device *netdev; + /** USB network device */ + struct usbnet_device usbnet; +}; + +/** Interrupt maximum fill level + * + * This is a policy decision. + */ +#define ECM_INTR_MAX_FILL 2 + +/** Bulk IN maximum fill level + * + * This is a policy decision. + */ +#define ECM_IN_MAX_FILL 8 + +/** Bulk IN buffer size + * + * This is a policy decision. + */ +#define ECM_IN_MTU ( ETH_FRAME_LEN + 4 /* possible VLAN header */ ) + +extern struct ecm_ethernet_descriptor * +ecm_ethernet_descriptor ( struct usb_configuration_descriptor *config, + struct usb_interface_descriptor *interface ); +extern int ecm_fetch_mac ( struct usb_device *usb, + struct ecm_ethernet_descriptor *desc, + uint8_t *hw_addr ); + +#endif /* _ECM_H */ diff --git a/qemu/roms/ipxe/src/drivers/net/eepro.c b/qemu/roms/ipxe/src/drivers/net/eepro.c index 909482bcc..97b4c4061 100644 --- a/qemu/roms/ipxe/src/drivers/net/eepro.c +++ b/qemu/roms/ipxe/src/drivers/net/eepro.c @@ -27,8 +27,18 @@ has 34 pins, the top row of 2 are not used. /* * 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) any later version. + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. */ FILE_LICENCE ( GPL2_OR_LATER ); @@ -591,9 +601,9 @@ static int eepro_probe ( struct nic *nic, struct isa_device *isa ) { l_eepro = 0; name = "Intel 82595-based LAN card"; } - station_addr.saddr[0] = swap16(station_addr.saddr[0]); - station_addr.saddr[1] = swap16(station_addr.saddr[1]); - station_addr.saddr[2] = swap16(station_addr.saddr[2]); + station_addr.saddr[0] = bswap_16(station_addr.saddr[0]); + station_addr.saddr[1] = bswap_16(station_addr.saddr[1]); + station_addr.saddr[2] = bswap_16(station_addr.saddr[2]); for (i = 0; i < ETH_ALEN; i++) { nic->node_addr[i] = station_addr.caddr[i]; } diff --git a/qemu/roms/ipxe/src/drivers/net/eepro100.c b/qemu/roms/ipxe/src/drivers/net/eepro100.c index ede0a1a4b..1046cda39 100644 --- a/qemu/roms/ipxe/src/drivers/net/eepro100.c +++ b/qemu/roms/ipxe/src/drivers/net/eepro100.c @@ -1136,7 +1136,6 @@ PCI_ROM(0x8086, 0x2449, "82562em", "Intel EtherExpressPro100 82562EM", 0), PCI_ROM(0x8086, 0x2459, "82562-1", "Intel 82562 based Fast Ethernet Connection", 0), PCI_ROM(0x8086, 0x245d, "82562-2", "Intel 82562 based Fast Ethernet Connection", 0), PCI_ROM(0x8086, 0x1050, "82562ez", "Intel 82562EZ Network Connection", 0), -PCI_ROM(0x8086, 0x1051, "eepro100-1051", "Intel 82801EB/ER (ICH5/ICH5R) Chipset Ethernet Controller", 0), PCI_ROM(0x8086, 0x1065, "82562-3", "Intel 82562 based Fast Ethernet Connection", 0), PCI_ROM(0x8086, 0x5200, "eepro100-5200", "Intel EtherExpress PRO/100 Intelligent Server", 0), PCI_ROM(0x8086, 0x5201, "eepro100-5201", "Intel EtherExpress PRO/100 Intelligent Server", 0), diff --git a/qemu/roms/ipxe/src/drivers/net/efi/nii.c b/qemu/roms/ipxe/src/drivers/net/efi/nii.c index d0d7da95a..b91848f5c 100644 --- a/qemu/roms/ipxe/src/drivers/net/efi/nii.c +++ b/qemu/roms/ipxe/src/drivers/net/efi/nii.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -168,6 +172,9 @@ struct nii_nic { /** Saved task priority level */ EFI_TPL saved_tpl; + /** Media status is supported */ + int media; + /** Current transmit buffer */ struct io_buffer *txbuf; /** Current receive buffer */ @@ -408,6 +415,13 @@ static int nii_issue_cpb_db ( struct nii_nic *nii, unsigned int op, void *cpb, cdb.IFnum = nii->nii->IfNum; /* Issue command */ + DBGC2 ( nii, "NII %s issuing %02x:%04x ifnum %d%s%s\n", + nii->dev.name, cdb.OpCode, cdb.OpFlags, cdb.IFnum, + ( cpb ? " cpb" : "" ), ( db ? " db" : "" ) ); + if ( cpb ) + DBGC2_HD ( nii, cpb, cpb_len ); + if ( db ) + DBGC2_HD ( nii, db, db_len ); nii->issue ( ( intptr_t ) &cdb ); /* Check completion status */ @@ -552,6 +566,7 @@ static int nii_get_init_info ( struct nii_nic *nii, nii->buffer_len = db.MemoryRequired; nii->mtu = ( db.FrameDataLen + db.MediaHeaderLen ); netdev->max_pkt_len = nii->mtu; + nii->media = ( stat & PXE_STATFLAGS_GET_STATUS_NO_MEDIA_SUPPORTED ); return 0; } @@ -560,10 +575,12 @@ static int nii_get_init_info ( struct nii_nic *nii, * Initialise UNDI * * @v nii NII NIC + * @v flags Flags * @ret rc Return status code */ -static int nii_initialise ( struct nii_nic *nii ) { +static int nii_initialise_flags ( struct nii_nic *nii, unsigned int flags ) { PXE_CPB_INITIALIZE cpb; + PXE_DB_INITIALIZE db; unsigned int op; int stat; int rc; @@ -580,10 +597,13 @@ static int nii_initialise ( struct nii_nic *nii ) { cpb.MemoryAddr = ( ( intptr_t ) nii->buffer ); cpb.MemoryLength = nii->buffer_len; + /* Construct data block */ + memset ( &db, 0, sizeof ( db ) ); + /* Issue command */ - op = NII_OP ( PXE_OPCODE_INITIALIZE, - PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE ); - if ( ( stat = nii_issue_cpb ( nii, op, &cpb, sizeof ( cpb ) ) ) < 0 ) { + op = NII_OP ( PXE_OPCODE_INITIALIZE, flags ); + if ( ( stat = nii_issue_cpb_db ( nii, op, &cpb, sizeof ( cpb ), + &db, sizeof ( db ) ) ) < 0 ) { rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not initialise: %s\n", nii->dev.name, strerror ( rc ) ); @@ -598,6 +618,36 @@ static int nii_initialise ( struct nii_nic *nii ) { return rc; } +/** + * Initialise UNDI + * + * @v nii NII NIC + * @ret rc Return status code + */ +static int nii_initialise ( struct nii_nic *nii ) { + unsigned int flags; + + /* Initialise UNDI */ + flags = PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE; + return nii_initialise_flags ( nii, flags ); +} + +/** + * Initialise UNDI and detect cable + * + * @v nii NII NIC + * @ret rc Return status code + */ +static int nii_initialise_and_detect ( struct nii_nic *nii ) { + unsigned int flags; + + /* Initialise UNDI and detect cable. This is required to work + * around bugs in some Emulex NII drivers. + */ + flags = PXE_OPFLAGS_INITIALIZE_DETECT_CABLE; + return nii_initialise_flags ( nii, flags ); +} + /** * Shut down UNDI * @@ -630,6 +680,7 @@ static void nii_shutdown ( struct nii_nic *nii ) { static int nii_get_station_address ( struct nii_nic *nii, struct net_device *netdev ) { PXE_DB_STATION_ADDRESS db; + unsigned int op; int stat; int rc; @@ -638,8 +689,9 @@ static int nii_get_station_address ( struct nii_nic *nii, goto err_initialise; /* Issue command */ - if ( ( stat = nii_issue_db ( nii, PXE_OPCODE_STATION_ADDRESS, &db, - sizeof ( db ) ) ) < 0 ) { + op = NII_OP ( PXE_OPCODE_STATION_ADDRESS, + PXE_OPFLAGS_STATION_ADDRESS_READ ); + if ( ( stat = nii_issue_db ( nii, op, &db, sizeof ( db ) ) ) < 0 ) { rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not get station address: %s\n", nii->dev.name, strerror ( rc ) ); @@ -669,18 +721,25 @@ static int nii_get_station_address ( struct nii_nic *nii, */ static int nii_set_station_address ( struct nii_nic *nii, struct net_device *netdev ) { + uint32_t implementation = nii->undi->Implementation; PXE_CPB_STATION_ADDRESS cpb; + unsigned int op; int stat; int rc; + /* Fail if setting station address is unsupported */ + if ( ! ( implementation & PXE_ROMID_IMP_STATION_ADDR_SETTABLE ) ) + return -ENOTSUP; + /* Construct parameter block */ memset ( &cpb, 0, sizeof ( cpb ) ); memcpy ( cpb.StationAddr, netdev->ll_addr, netdev->ll_protocol->ll_addr_len ); /* Issue command */ - if ( ( stat = nii_issue_cpb ( nii, PXE_OPCODE_STATION_ADDRESS, - &cpb, sizeof ( cpb ) ) ) < 0 ) { + op = NII_OP ( PXE_OPCODE_STATION_ADDRESS, + PXE_OPFLAGS_STATION_ADDRESS_WRITE ); + if ( ( stat = nii_issue_cpb ( nii, op, &cpb, sizeof ( cpb ) ) ) < 0 ) { rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not set station address: %s\n", nii->dev.name, strerror ( rc ) ); @@ -697,21 +756,28 @@ static int nii_set_station_address ( struct nii_nic *nii, * @ret rc Return status code */ static int nii_set_rx_filters ( struct nii_nic *nii ) { + uint32_t implementation = nii->undi->Implementation; + unsigned int flags; unsigned int op; int stat; int rc; + /* Construct receive filter set */ + flags = ( PXE_OPFLAGS_RECEIVE_FILTER_ENABLE | + PXE_OPFLAGS_RECEIVE_FILTER_UNICAST ); + if ( implementation & PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED ) + flags |= PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST; + if ( implementation & PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED ) + flags |= PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS; + if ( implementation & PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED ) + flags |= PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST; + /* Issue command */ - op = NII_OP ( PXE_OPCODE_RECEIVE_FILTERS, - ( PXE_OPFLAGS_RECEIVE_FILTER_ENABLE | - PXE_OPFLAGS_RECEIVE_FILTER_UNICAST | - PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST | - PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS | - PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST ) ); + op = NII_OP ( PXE_OPCODE_RECEIVE_FILTERS, flags ); if ( ( stat = nii_issue ( nii, op ) ) < 0 ) { rc = -EIO_STAT ( stat ); - DBGC ( nii, "NII %s could not set receive filters: %s\n", - nii->dev.name, strerror ( rc ) ); + DBGC ( nii, "NII %s could not set receive filters %#04x: %s\n", + nii->dev.name, flags, strerror ( rc ) ); return rc; } @@ -729,6 +795,7 @@ static int nii_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { struct nii_nic *nii = netdev->priv; PXE_CPB_TRANSMIT cpb; + unsigned int op; int stat; int rc; @@ -745,8 +812,10 @@ static int nii_transmit ( struct net_device *netdev, cpb.MediaheaderLen = netdev->ll_protocol->ll_header_len; /* Transmit packet */ - if ( ( stat = nii_issue_cpb ( nii, PXE_OPCODE_TRANSMIT, &cpb, - sizeof ( cpb ) ) ) < 0 ) { + op = NII_OP ( PXE_OPCODE_TRANSMIT, + ( PXE_OPFLAGS_TRANSMIT_WHOLE | + PXE_OPFLAGS_TRANSMIT_DONT_BLOCK ) ); + if ( ( stat = nii_issue_cpb ( nii, op, &cpb, sizeof ( cpb ) ) ) < 0 ) { rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not transmit: %s\n", nii->dev.name, strerror ( rc ) ); @@ -772,12 +841,7 @@ static void nii_poll_tx ( struct net_device *netdev, unsigned int stat ) { return; /* Sanity check */ - if ( ! nii->txbuf ) { - DBGC ( nii, "NII %s reported spurious TX completion\n", - nii->dev.name ); - netdev_tx_err ( netdev, NULL, -EPIPE ); - return; - } + assert ( nii->txbuf != NULL ); /* Complete transmission */ iobuf = nii->txbuf; @@ -869,11 +933,14 @@ static void nii_poll ( struct net_device *netdev ) { int stat; int rc; + /* Construct data block */ + memset ( &db, 0, sizeof ( db ) ); + /* Get status */ op = NII_OP ( PXE_OPCODE_GET_STATUS, ( PXE_OPFLAGS_GET_INTERRUPT_STATUS | - PXE_OPFLAGS_GET_TRANSMITTED_BUFFERS | - PXE_OPFLAGS_GET_MEDIA_STATUS ) ); + ( nii->txbuf ? PXE_OPFLAGS_GET_TRANSMITTED_BUFFERS : 0)| + ( nii->media ? PXE_OPFLAGS_GET_MEDIA_STATUS : 0 ) ) ); if ( ( stat = nii_issue_db ( nii, op, &db, sizeof ( db ) ) ) < 0 ) { rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not get status: %s\n", @@ -882,13 +949,15 @@ static void nii_poll ( struct net_device *netdev ) { } /* Process any TX completions */ - nii_poll_tx ( netdev, stat ); + if ( nii->txbuf ) + nii_poll_tx ( netdev, stat ); /* Process any RX completions */ nii_poll_rx ( netdev ); /* Check for link state changes */ - nii_poll_link ( netdev, stat ); + if ( nii->media ) + nii_poll_link ( netdev, stat ); } /** @@ -901,8 +970,18 @@ static int nii_open ( struct net_device *netdev ) { struct nii_nic *nii = netdev->priv; int rc; - /* Initialise NIC */ - if ( ( rc = nii_initialise ( nii ) ) != 0 ) + /* Initialise NIC + * + * Some Emulex NII drivers have a bug which prevents packets + * from being sent or received unless we specifically ask it + * to detect cable presence during initialisation. Work + * around these buggy drivers by requesting cable detection at + * this point, even though we don't care about link state here + * (and would prefer to have the NIC initialise even if no + * cable is present, to match the behaviour of all other iPXE + * drivers). + */ + if ( ( rc = nii_initialise_and_detect ( nii ) ) != 0 ) goto err_initialise; /* Attempt to set station address */ @@ -1023,8 +1102,9 @@ int nii_start ( struct efi_device *efidev ) { nii->issue = ( ( ( void * ) nii->undi ) + nii->undi->EntryPoint ); } - DBGC ( nii, "NII %s using UNDI v%x.%x at %p entry %p\n", nii->dev.name, - nii->nii->MajorVer, nii->nii->MinorVer, nii->undi, nii->issue ); + DBGC ( nii, "NII %s using UNDI v%x.%x at %p entry %p impl %#08x\n", + nii->dev.name, nii->nii->MajorVer, nii->nii->MinorVer, + nii->undi, nii->issue, nii->undi->Implementation ); /* Open PCI I/O protocols and locate BARs */ if ( ( rc = nii_pci_open ( nii ) ) != 0 ) @@ -1048,6 +1128,10 @@ int nii_start ( struct efi_device *efidev ) { DBGC ( nii, "NII %s registered as %s for %p %s\n", nii->dev.name, netdev->name, device, efi_handle_name ( device ) ); + /* Set initial link state (if media detection is not supported) */ + if ( ! nii->media ) + netdev_link_up ( netdev ); + return 0; unregister_netdev ( netdev ); diff --git a/qemu/roms/ipxe/src/drivers/net/efi/nii.h b/qemu/roms/ipxe/src/drivers/net/efi/nii.h index de0ac687b..c10be9db5 100644 --- a/qemu/roms/ipxe/src/drivers/net/efi/nii.h +++ b/qemu/roms/ipxe/src/drivers/net/efi/nii.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct efi_device; diff --git a/qemu/roms/ipxe/src/drivers/net/efi/snp.c b/qemu/roms/ipxe/src/drivers/net/efi/snp.c index 2b5fc8618..acfcfba9f 100644 --- a/qemu/roms/ipxe/src/drivers/net/efi/snp.c +++ b/qemu/roms/ipxe/src/drivers/net/efi/snp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/net/efi/snponly.c b/qemu/roms/ipxe/src/drivers/net/efi/snponly.c index 99f264bca..73abfdbf4 100644 --- a/qemu/roms/ipxe/src/drivers/net/efi/snponly.c +++ b/qemu/roms/ipxe/src/drivers/net/efi/snponly.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/net/etherfabric.c b/qemu/roms/ipxe/src/drivers/net/etherfabric.c index 5e0efb1e1..29d117443 100644 --- a/qemu/roms/ipxe/src/drivers/net/etherfabric.c +++ b/qemu/roms/ipxe/src/drivers/net/etherfabric.c @@ -3176,7 +3176,7 @@ falcon_probe_nic_variant ( struct efab_nic *efab, struct pci_device *pci ) uint8_t revision; /* PCI revision */ - pci_read_config_byte ( pci, PCI_CLASS_REVISION, &revision ); + pci_read_config_byte ( pci, PCI_REVISION, &revision ); efab->pci_revision = revision; /* Asic vs FPGA */ diff --git a/qemu/roms/ipxe/src/drivers/net/forcedeth.c b/qemu/roms/ipxe/src/drivers/net/forcedeth.c index d8ece9a7a..79938cbbb 100644 --- a/qemu/roms/ipxe/src/drivers/net/forcedeth.c +++ b/qemu/roms/ipxe/src/drivers/net/forcedeth.c @@ -1749,10 +1749,8 @@ forcedeth_map_regs ( struct forcedeth_private *priv ) for ( reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4 ) { pci_read_config_dword ( priv->pci_dev, reg, &bar ); - if ( ( ( bar & PCI_BASE_ADDRESS_SPACE ) == - PCI_BASE_ADDRESS_SPACE_MEMORY ) && - ( pci_bar_size ( priv->pci_dev, reg ) >= - register_size ) ) { + if ( ( ! ( bar & PCI_BASE_ADDRESS_SPACE_IO ) ) && + ( pci_bar_size ( priv->pci_dev, reg ) >= register_size ) ){ addr = pci_bar_start ( priv->pci_dev, reg ); break; } diff --git a/qemu/roms/ipxe/src/drivers/net/igbvf/igbvf_main.c b/qemu/roms/ipxe/src/drivers/net/igbvf/igbvf_main.c index aace5ad56..fc7021c38 100644 --- a/qemu/roms/ipxe/src/drivers/net/igbvf/igbvf_main.c +++ b/qemu/roms/ipxe/src/drivers/net/igbvf/igbvf_main.c @@ -461,7 +461,7 @@ static int __devinit igbvf_sw_init ( struct igbvf_adapter *adapter ) hw->vendor_id = pdev->vendor; hw->device_id = pdev->device; - pci_read_config_byte ( pdev, PCI_REVISION_ID, &hw->revision_id ); + pci_read_config_byte ( pdev, PCI_REVISION, &hw->revision_id ); pci_read_config_word ( pdev, PCI_COMMAND, &hw->bus.pci_cmd_word ); diff --git a/qemu/roms/ipxe/src/drivers/net/intel.c b/qemu/roms/ipxe/src/drivers/net/intel.c index a89f947b2..6309e9aa5 100644 --- a/qemu/roms/ipxe/src/drivers/net/intel.c +++ b/qemu/roms/ipxe/src/drivers/net/intel.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -246,32 +250,6 @@ static int intel_fetch_mac ( struct intel_nic *intel, uint8_t *hw_addr ) { return -ENOENT; } -/****************************************************************************** - * - * Diagnostics - * - ****************************************************************************** - */ - -/** - * Dump diagnostic information - * - * @v intel Intel device - */ -static void __attribute__ (( unused )) intel_diag ( struct intel_nic *intel ) { - - DBGC ( intel, "INTEL %p TX %04x(%02x)/%04x(%02x) " - "RX %04x(%02x)/%04x(%02x)\n", intel, - ( intel->tx.cons & 0xffff ), - readl ( intel->regs + intel->tx.reg + INTEL_xDH ), - ( intel->tx.prod & 0xffff ), - readl ( intel->regs + intel->tx.reg + INTEL_xDT ), - ( intel->rx.cons & 0xffff ), - readl ( intel->regs + intel->rx.reg + INTEL_xDH ), - ( intel->rx.prod & 0xffff ), - readl ( intel->regs + intel->rx.reg + INTEL_xDT ) ); -} - /****************************************************************************** * * Device reset @@ -369,6 +347,67 @@ static void intel_check_link ( struct net_device *netdev ) { } } +/****************************************************************************** + * + * Descriptors + * + ****************************************************************************** + */ + +/** + * Populate transmit descriptor + * + * @v tx Transmit descriptor + * @v addr Data buffer address + * @v len Length of data + */ +void intel_describe_tx ( struct intel_descriptor *tx, physaddr_t addr, + size_t len ) { + + /* Populate transmit descriptor */ + tx->address = cpu_to_le64 ( addr ); + tx->length = cpu_to_le16 ( len ); + tx->flags = 0; + tx->command = ( INTEL_DESC_CMD_RS | INTEL_DESC_CMD_IFCS | + INTEL_DESC_CMD_EOP ); + tx->status = 0; +} + +/** + * Populate advanced transmit descriptor + * + * @v tx Transmit descriptor + * @v addr Data buffer address + * @v len Length of data + */ +void intel_describe_tx_adv ( struct intel_descriptor *tx, physaddr_t addr, + size_t len ) { + + /* Populate advanced transmit descriptor */ + tx->address = cpu_to_le64 ( addr ); + tx->length = cpu_to_le16 ( len ); + tx->flags = INTEL_DESC_FL_DTYP_DATA; + tx->command = ( INTEL_DESC_CMD_DEXT | INTEL_DESC_CMD_RS | + INTEL_DESC_CMD_IFCS | INTEL_DESC_CMD_EOP ); + tx->status = cpu_to_le32 ( INTEL_DESC_STATUS_PAYLEN ( len ) ); +} + +/** + * Populate receive descriptor + * + * @v rx Receive descriptor + * @v addr Data buffer address + * @v len Length of data + */ +void intel_describe_rx ( struct intel_descriptor *rx, physaddr_t addr, + size_t len __unused ) { + + /* Populate transmit descriptor */ + rx->address = cpu_to_le64 ( addr ); + rx->length = 0; + rx->status = 0; +} + /****************************************************************************** * * Network device interface @@ -479,10 +518,7 @@ void intel_refill_rx ( struct intel_nic *intel ) { /* Populate receive descriptor */ address = virt_to_bus ( iobuf->data ); - rx->address = cpu_to_le64 ( address ); - rx->length = 0; - rx->status = 0; - rx->errors = 0; + intel->rx.describe ( rx, address, 0 ); /* Record I/O buffer */ assert ( intel->rx_iobuf[rx_idx] == NULL ); @@ -568,6 +604,13 @@ static int intel_open ( struct net_device *netdev ) { /* Update link state */ intel_check_link ( netdev ); + /* Apply required errata */ + if ( intel->flags & INTEL_VMWARE ) { + DBGC ( intel, "INTEL %p applying VMware errata workaround\n", + intel ); + intel->force_icr = INTEL_IRQ_RXT0; + } + return 0; intel_destroy_ring ( intel, &intel->rx ); @@ -617,6 +660,7 @@ int intel_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { unsigned int tx_idx; unsigned int tx_tail; physaddr_t address; + size_t len; /* Get next transmit descriptor */ if ( ( intel->tx.prod - intel->tx.cons ) >= INTEL_TX_FILL ) { @@ -629,11 +673,8 @@ int intel_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { /* Populate transmit descriptor */ address = virt_to_bus ( iobuf->data ); - tx->address = cpu_to_le64 ( address ); - tx->length = cpu_to_le16 ( iob_len ( iobuf ) ); - tx->command = ( INTEL_DESC_CMD_RS | INTEL_DESC_CMD_IFCS | - INTEL_DESC_CMD_EOP ); - tx->status = 0; + len = iob_len ( iobuf ); + intel->tx.describe ( tx, address, len ); wmb(); /* Notify card that there are packets ready to transmit */ @@ -644,7 +685,7 @@ int intel_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { DBGC2 ( intel, "INTEL %p TX %d is [%llx,%llx)\n", intel, tx_idx, ( ( unsigned long long ) address ), - ( ( unsigned long long ) address + iob_len ( iobuf ) ) ); + ( ( unsigned long long ) address + len ) ); return 0; } @@ -667,7 +708,7 @@ void intel_poll_tx ( struct net_device *netdev ) { tx = &intel->tx.desc[tx_idx]; /* Stop if descriptor is still in use */ - if ( ! ( tx->status & INTEL_DESC_STATUS_DD ) ) + if ( ! ( tx->status & cpu_to_le32 ( INTEL_DESC_STATUS_DD ) ) ) return; DBGC2 ( intel, "INTEL %p TX %d complete\n", intel, tx_idx ); @@ -698,7 +739,7 @@ void intel_poll_rx ( struct net_device *netdev ) { rx = &intel->rx.desc[rx_idx]; /* Stop if descriptor is still in use */ - if ( ! ( rx->status & INTEL_DESC_STATUS_DD ) ) + if ( ! ( rx->status & cpu_to_le32 ( INTEL_DESC_STATUS_DD ) ) ) return; /* Populate I/O buffer */ @@ -708,10 +749,10 @@ void intel_poll_rx ( struct net_device *netdev ) { iob_put ( iobuf, len ); /* Hand off to network stack */ - if ( rx->errors ) { + if ( rx->status & cpu_to_le32 ( INTEL_DESC_STATUS_RXE ) ) { DBGC ( intel, "INTEL %p RX %d error (length %zd, " - "errors %02x)\n", - intel, rx_idx, len, rx->errors ); + "status %08x)\n", intel, rx_idx, len, + le32_to_cpu ( rx->status ) ); netdev_rx_err ( netdev, iobuf, -EIO ); } else { DBGC2 ( intel, "INTEL %p RX %d complete (length %zd)\n", @@ -736,6 +777,7 @@ static void intel_poll ( struct net_device *netdev ) { icr = readl ( intel->regs + INTEL_ICR ); profile_stop ( &intel_vm_poll_profiler ); profile_exclude ( &intel_vm_poll_profiler ); + icr |= intel->force_icr; if ( ! icr ) return; @@ -755,6 +797,14 @@ static void intel_poll ( struct net_device *netdev ) { if ( icr & INTEL_IRQ_LSC ) intel_check_link ( netdev ); + /* Check for unexpected interrupts */ + if ( icr & ~( INTEL_IRQ_TXDW | INTEL_IRQ_TXQE | INTEL_IRQ_LSC | + INTEL_IRQ_RXDMT0 | INTEL_IRQ_RXT0 | INTEL_IRQ_RXO ) ) { + DBGC ( intel, "INTEL %p unexpected ICR %08x\n", intel, icr ); + /* Report as a TX error */ + netdev_tx_err ( netdev, NULL, -ENOTSUP ); + } + /* Refill RX ring */ intel_refill_rx ( intel ); } @@ -817,8 +867,10 @@ static int intel_probe ( struct pci_device *pci ) { memset ( intel, 0, sizeof ( *intel ) ); intel->port = PCI_FUNC ( pci->busdevfn ); intel->flags = pci->id->driver_data; - intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTEL_TD ); - intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTEL_RD ); + intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTEL_TD, + intel_describe_tx ); + intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTEL_RD, + intel_describe_rx ); /* Fix up PCI device */ adjust_pci_device ( pci ); @@ -895,7 +947,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x100c, "82544gc", "82544GC (Copper)", 0 ), PCI_ROM ( 0x8086, 0x100d, "82544gc-l", "82544GC (LOM)", 0 ), PCI_ROM ( 0x8086, 0x100e, "82540em", "82540EM", 0 ), - PCI_ROM ( 0x8086, 0x100f, "82545em", "82545EM (Copper)", 0 ), + PCI_ROM ( 0x8086, 0x100f, "82545em", "82545EM (Copper)", INTEL_VMWARE ), PCI_ROM ( 0x8086, 0x1010, "82546eb", "82546EB (Copper)", 0 ), PCI_ROM ( 0x8086, 0x1011, "82545em-f", "82545EM (Fiber)", 0 ), PCI_ROM ( 0x8086, 0x1012, "82546eb-f", "82546EB (Fiber)", 0 ), @@ -998,6 +1050,12 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x1533, "i210", "I210", 0 ), PCI_ROM ( 0x8086, 0x153a, "i217lm", "I217-LM", 0 ), PCI_ROM ( 0x8086, 0x153b, "i217v", "I217-V", 0 ), + PCI_ROM ( 0x8086, 0x1559, "i218v", "I218-V", 0), + PCI_ROM ( 0x8086, 0x155a, "i218lm", "I218-LM", 0), + PCI_ROM ( 0x8086, 0x15a0, "i218lm-2", "I218-LM", 0 ), + PCI_ROM ( 0x8086, 0x15a1, "i218v-2", "I218-V", 0 ), + PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", 0 ), + PCI_ROM ( 0x8086, 0x15a3, "i218v-3", "I218-V", 0 ), PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ), PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ), }; diff --git a/qemu/roms/ipxe/src/drivers/net/intel.h b/qemu/roms/ipxe/src/drivers/net/intel.h index 8c4479bb4..ce9e3f467 100644 --- a/qemu/roms/ipxe/src/drivers/net/intel.h +++ b/qemu/roms/ipxe/src/drivers/net/intel.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -22,33 +22,38 @@ struct intel_descriptor { uint64_t address; /** Length */ uint16_t length; - /** Reserved */ - uint8_t reserved_a; + /** Flags */ + uint8_t flags; /** Command */ uint8_t command; /** Status */ - uint8_t status; - /** Errors */ - uint8_t errors; - /** Reserved */ - uint16_t reserved_b; + uint32_t status; } __attribute__ (( packed )); -/** Packet descriptor command bits */ -enum intel_descriptor_command { - /** Report status */ - INTEL_DESC_CMD_RS = 0x08, - /** Insert frame checksum (CRC) */ - INTEL_DESC_CMD_IFCS = 0x02, - /** End of packet */ - INTEL_DESC_CMD_EOP = 0x01, -}; +/** Descriptor type */ +#define INTEL_DESC_FL_DTYP( dtyp ) ( (dtyp) << 4 ) +#define INTEL_DESC_FL_DTYP_DATA INTEL_DESC_FL_DTYP ( 0x03 ) -/** Packet descriptor status bits */ -enum intel_descriptor_status { - /** Descriptor done */ - INTEL_DESC_STATUS_DD = 0x01, -}; +/** Descriptor extension */ +#define INTEL_DESC_CMD_DEXT 0x20 + +/** Report status */ +#define INTEL_DESC_CMD_RS 0x08 + +/** Insert frame checksum (CRC) */ +#define INTEL_DESC_CMD_IFCS 0x02 + +/** End of packet */ +#define INTEL_DESC_CMD_EOP 0x01 + +/** Descriptor done */ +#define INTEL_DESC_STATUS_DD 0x00000001UL + +/** Receive error */ +#define INTEL_DESC_STATUS_RXE 0x00000100UL + +/** Payload length */ +#define INTEL_DESC_STATUS_PAYLEN( len ) ( (len) << 14 ) /** Device Control Register */ #define INTEL_CTRL 0x00000UL @@ -91,7 +96,9 @@ enum intel_descriptor_status { /** Interrupt Cause Read Register */ #define INTEL_ICR 0x000c0UL #define INTEL_IRQ_TXDW 0x00000001UL /**< Transmit descriptor done */ +#define INTEL_IRQ_TXQE 0x00000002UL /**< Transmit queue empty */ #define INTEL_IRQ_LSC 0x00000004UL /**< Link status change */ +#define INTEL_IRQ_RXDMT0 0x00000010UL /**< Receive queue low */ #define INTEL_IRQ_RXT0 0x00000080UL /**< Receive timer */ #define INTEL_IRQ_RXO 0x00000400UL /**< Receive overrun */ @@ -207,6 +214,15 @@ struct intel_ring { unsigned int reg; /** Length (in bytes) */ size_t len; + + /** Populate descriptor + * + * @v desc Descriptor + * @v addr Data buffer address + * @v len Length of data + */ + void ( * describe ) ( struct intel_descriptor *desc, physaddr_t addr, + size_t len ); }; /** @@ -215,12 +231,39 @@ struct intel_ring { * @v ring Descriptor ring * @v count Number of descriptors * @v reg Descriptor register block + * @v describe Method to populate descriptor */ static inline __attribute__ (( always_inline)) void -intel_init_ring ( struct intel_ring *ring, unsigned int count, - unsigned int reg ) { +intel_init_ring ( struct intel_ring *ring, unsigned int count, unsigned int reg, + void ( * describe ) ( struct intel_descriptor *desc, + physaddr_t addr, size_t len ) ) { + ring->len = ( count * sizeof ( ring->desc[0] ) ); ring->reg = reg; + ring->describe = describe; +} + +/** An Intel virtual function mailbox */ +struct intel_mailbox { + /** Mailbox control register */ + unsigned int ctrl; + /** Mailbox memory base */ + unsigned int mem; +}; + +/** + * Initialise mailbox + * + * @v mbox Mailbox + * @v ctrl Mailbox control register + * @v mem Mailbox memory register base + */ +static inline __attribute__ (( always_inline )) void +intel_init_mbox ( struct intel_mailbox *mbox, unsigned int ctrl, + unsigned int mem ) { + + mbox->ctrl = ctrl; + mbox->mem = mem; } /** An Intel network card */ @@ -231,6 +274,8 @@ struct intel_nic { unsigned int port; /** Flags */ unsigned int flags; + /** Forced interrupts */ + unsigned int force_icr; /** EEPROM */ struct nvs_device eeprom; @@ -239,6 +284,9 @@ struct intel_nic { /** EEPROM address shift */ unsigned int eerd_addr_shift; + /** Mailbox */ + struct intel_mailbox mbox; + /** Transmit descriptor ring */ struct intel_ring tx; /** Receive descriptor ring */ @@ -251,8 +299,35 @@ struct intel_nic { enum intel_flags { /** PBS/PBA errata workaround required */ INTEL_PBS_ERRATA = 0x0001, + /** VMware missing interrupt workaround required */ + INTEL_VMWARE = 0x0002, }; +/** + * Dump diagnostic information + * + * @v intel Intel device + */ +static inline void intel_diag ( struct intel_nic *intel ) { + + DBGC ( intel, "INTEL %p TX %04x(%02x)/%04x(%02x) " + "RX %04x(%02x)/%04x(%02x)\n", intel, + ( intel->tx.cons & 0xffff ), + readl ( intel->regs + intel->tx.reg + INTEL_xDH ), + ( intel->tx.prod & 0xffff ), + readl ( intel->regs + intel->tx.reg + INTEL_xDT ), + ( intel->rx.cons & 0xffff ), + readl ( intel->regs + intel->rx.reg + INTEL_xDH ), + ( intel->rx.prod & 0xffff ), + readl ( intel->regs + intel->rx.reg + INTEL_xDT ) ); +} + +extern void intel_describe_tx ( struct intel_descriptor *tx, + physaddr_t addr, size_t len ); +extern void intel_describe_tx_adv ( struct intel_descriptor *tx, + physaddr_t addr, size_t len ); +extern void intel_describe_rx ( struct intel_descriptor *rx, + physaddr_t addr, size_t len ); extern int intel_create_ring ( struct intel_nic *intel, struct intel_ring *ring ); extern void intel_destroy_ring ( struct intel_nic *intel, diff --git a/qemu/roms/ipxe/src/drivers/net/intelvf.c b/qemu/roms/ipxe/src/drivers/net/intelvf.c new file mode 100644 index 000000000..ac6fea745 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/intelvf.c @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include "intelvf.h" + +/** @file + * + * Intel 10/100/1000 virtual function network card driver + * + */ + +/****************************************************************************** + * + * Mailbox messages + * + ****************************************************************************** + */ + +/** + * Write message to mailbox + * + * @v intel Intel device + * @v msg Message + */ +static void intelvf_mbox_write ( struct intel_nic *intel, + const union intelvf_msg *msg ) { + unsigned int i; + + /* Write message */ + DBGC2 ( intel, "INTEL %p sending message", intel ); + for ( i = 0 ; i < ( sizeof ( *msg ) / sizeof ( msg->dword[0] ) ) ; i++){ + DBGC2 ( intel, "%c%08x", ( i ? ':' : ' ' ), msg->dword[i] ); + writel ( msg->dword[i], ( intel->regs + intel->mbox.mem + + ( i * sizeof ( msg->dword[0] ) ) ) ); + } + DBGC2 ( intel, "\n" ); +} + +/** + * Read message from mailbox + * + * @v intel Intel device + * @v msg Message + */ +static void intelvf_mbox_read ( struct intel_nic *intel, + union intelvf_msg *msg ) { + unsigned int i; + + /* Read message */ + DBGC2 ( intel, "INTEL %p received message", intel ); + for ( i = 0 ; i < ( sizeof ( *msg ) / sizeof ( msg->dword[0] ) ) ; i++){ + msg->dword[i] = readl ( intel->regs + intel->mbox.mem + + ( i * sizeof ( msg->dword[0] ) ) ); + DBGC2 ( intel, "%c%08x", ( i ? ':' : ' ' ), msg->dword[i] ); + } + DBGC2 ( intel, "\n" ); +} + +/** + * Poll mailbox + * + * @v intel Intel device + * @ret rc Return status code + * + * Note that polling the mailbox may fail if the underlying PF is + * reset. + */ +int intelvf_mbox_poll ( struct intel_nic *intel ) { + struct intel_mailbox *mbox = &intel->mbox; + union intelvf_msg msg; + uint32_t ctrl; + + /* Get mailbox status */ + ctrl = readl ( intel->regs + mbox->ctrl ); + + /* Fail if a reset is in progress */ + if ( ctrl & INTELVF_MBCTRL_RSTI ) + return -EPIPE; + + /* Acknowledge (and ignore) any received messages */ + if ( ctrl & INTELVF_MBCTRL_PFSTS ) { + intelvf_mbox_read ( intel, &msg ); + writel ( INTELVF_MBCTRL_ACK, intel->regs + mbox->ctrl ); + } + + return 0; +} + +/** + * Wait for PF reset to complete + * + * @v intel Intel device + * @ret rc Return status code + */ +int intelvf_mbox_wait ( struct intel_nic *intel ) { + unsigned int i; + int rc; + + /* Wait until a poll completes successfully */ + for ( i = 0 ; i < INTELVF_MBOX_MAX_WAIT_MS ; i++ ) { + + /* Check for successful poll */ + if ( ( rc = intelvf_mbox_poll ( intel ) ) == 0 ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( intel, "INTEL %p timed out waiting for reset\n", intel ); + return -ETIMEDOUT; +} + +/** + * Send/receive mailbox message + * + * @v intel Intel device + * @v msg Message buffer + * @ret rc Return status code + */ +int intelvf_mbox_msg ( struct intel_nic *intel, union intelvf_msg *msg ) { + struct intel_mailbox *mbox = &intel->mbox; + uint32_t ctrl; + uint32_t seen = 0; + unsigned int i; + + /* Sanity check */ + assert ( ! ( msg->hdr & INTELVF_MSG_RESPONSE ) ); + + /* Handle mailbox */ + for ( i = 0 ; i < INTELVF_MBOX_MAX_WAIT_MS ; i++ ) { + + /* Attempt to claim mailbox, if we have not yet sent + * our message. + */ + if ( ! ( seen & INTELVF_MBCTRL_VFU ) ) + writel ( INTELVF_MBCTRL_VFU, intel->regs + mbox->ctrl ); + + /* Get mailbox status and record observed flags */ + ctrl = readl ( intel->regs + mbox->ctrl ); + seen |= ctrl; + + /* If a reset is in progress, clear VFU and abort */ + if ( ctrl & INTELVF_MBCTRL_RSTI ) { + writel ( 0, intel->regs + mbox->ctrl ); + return -EPIPE; + } + + /* Write message to mailbox, if applicable. This + * potentially overwrites a message sent by the PF (if + * the PF has simultaneously released PFU (thus + * allowing our VFU) and asserted PFSTS), but that + * doesn't really matter since there are no + * unsolicited PF->VF messages that require the actual + * message content to be observed. + */ + if ( ctrl & INTELVF_MBCTRL_VFU ) + intelvf_mbox_write ( intel, msg ); + + /* Read message from mailbox, if applicable. */ + if ( ( seen & INTELVF_MBCTRL_VFU ) && + ( seen & INTELVF_MBCTRL_PFACK ) && + ( ctrl & INTELVF_MBCTRL_PFSTS ) ) + intelvf_mbox_read ( intel, msg ); + + /* Acknowledge received message (if applicable), + * release VFU lock, and send message (if applicable). + */ + ctrl = ( ( ( ctrl & INTELVF_MBCTRL_PFSTS ) ? + INTELVF_MBCTRL_ACK : 0 ) | + ( ( ctrl & INTELVF_MBCTRL_VFU ) ? + INTELVF_MBCTRL_REQ : 0 ) ); + writel ( ctrl, intel->regs + mbox->ctrl ); + + /* Exit successfully if we have received a response */ + if ( msg->hdr & INTELVF_MSG_RESPONSE ) { + + /* Sanity check */ + assert ( seen & INTELVF_MBCTRL_VFU ); + assert ( seen & INTELVF_MBCTRL_PFACK ); + assert ( seen & INTELVF_MBCTRL_PFSTS ); + + return 0; + } + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( intel, "INTEL %p timed out waiting for mailbox (seen %08x)\n", + intel, seen ); + return -ETIMEDOUT; +} + +/** + * Send reset message and get initial MAC address + * + * @v intel Intel device + * @v hw_addr Hardware address to fill in, or NULL + * @ret rc Return status code + */ +int intelvf_mbox_reset ( struct intel_nic *intel, uint8_t *hw_addr ) { + union intelvf_msg msg; + int rc; + + /* Send reset message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.hdr = INTELVF_MSG_TYPE_RESET; + if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) { + DBGC ( intel, "INTEL %p reset failed: %s\n", + intel, strerror ( rc ) ); + return rc; + } + + /* Check response */ + if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) != INTELVF_MSG_TYPE_RESET ) { + DBGC ( intel, "INTEL %p reset unexpected response:\n", intel ); + DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) ); + return -EPROTO; + } + + /* Fill in MAC address, if applicable */ + if ( hw_addr ) { + if ( msg.hdr & INTELVF_MSG_ACK ) { + memcpy ( hw_addr, msg.mac.mac, sizeof ( msg.mac.mac ) ); + DBGC ( intel, "INTEL %p reset assigned MAC address " + "%s\n", intel, eth_ntoa ( hw_addr ) ); + } else { + eth_random_addr ( hw_addr ); + DBGC ( intel, "INTEL %p reset generated MAC address " + "%s\n", intel, eth_ntoa ( hw_addr ) ); + } + } + + return 0; +} + +/** + * Send set MAC address message + * + * @v intel Intel device + * @v ll_addr Link-layer address + * @ret rc Return status code + */ +int intelvf_mbox_set_mac ( struct intel_nic *intel, const uint8_t *ll_addr ) { + union intelvf_msg msg; + int rc; + + /* Send set MAC address message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.hdr = INTELVF_MSG_TYPE_SET_MAC; + memcpy ( msg.mac.mac, ll_addr, sizeof ( msg.mac.mac ) ); + if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) { + DBGC ( intel, "INTEL %p set MAC address failed: %s\n", + intel, strerror ( rc ) ); + return rc; + } + + /* Check response */ + if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) != INTELVF_MSG_TYPE_SET_MAC ) { + DBGC ( intel, "INTEL %p set MAC address unexpected response:\n", + intel ); + DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) ); + return -EPROTO; + } + + /* Check that we were allowed to set the MAC address */ + if ( ! ( msg.hdr & INTELVF_MSG_ACK ) ) { + DBGC ( intel, "INTEL %p set MAC address refused\n", intel ); + return -EPERM; + } + + return 0; +} + +/** + * Send set MTU message + * + * @v intel Intel device + * @v mtu Maximum packet size + * @ret rc Return status code + */ +int intelvf_mbox_set_mtu ( struct intel_nic *intel, size_t mtu ) { + union intelvf_msg msg; + int rc; + + /* Send set MTU message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.hdr = INTELVF_MSG_TYPE_SET_MTU; + msg.mtu.mtu = mtu; + if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) { + DBGC ( intel, "INTEL %p set MTU failed: %s\n", + intel, strerror ( rc ) ); + return rc; + } + + /* Check response */ + if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) != INTELVF_MSG_TYPE_SET_MTU ) { + DBGC ( intel, "INTEL %p set MTU unexpected response:\n", + intel ); + DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) ); + return -EPROTO; + } + + /* Check that we were allowed to set the MTU */ + if ( ! ( msg.hdr & INTELVF_MSG_ACK ) ) { + DBGC ( intel, "INTEL %p set MTU refused\n", intel ); + return -EPERM; + } + + return 0; +} diff --git a/qemu/roms/ipxe/src/drivers/net/intelvf.h b/qemu/roms/ipxe/src/drivers/net/intelvf.h new file mode 100644 index 000000000..d2f98d874 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/intelvf.h @@ -0,0 +1,109 @@ +#ifndef _INTELVF_H +#define _INTELVF_H + +/** @file + * + * Intel 10/100/1000 virtual function network card driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include "intel.h" + +/** Intel VF BAR size */ +#define INTELVF_BAR_SIZE ( 16 * 1024 ) + +/** Mailbox Control Register */ +#define INTELVF_MBCTRL 0x0c40UL +#define INTELVF_MBCTRL_REQ 0x00000001UL /**< Request for PF ready */ +#define INTELVF_MBCTRL_ACK 0x00000002UL /**< PF message received */ +#define INTELVF_MBCTRL_VFU 0x00000004UL /**< Buffer taken by VF */ +#define INTELVF_MBCTRL_PFU 0x00000008UL /**< Buffer taken to PF */ +#define INTELVF_MBCTRL_PFSTS 0x00000010UL /**< PF wrote a message */ +#define INTELVF_MBCTRL_PFACK 0x00000020UL /**< PF acknowledged message */ +#define INTELVF_MBCTRL_RSTI 0x00000040UL /**< PF reset in progress */ +#define INTELVF_MBCTRL_RSTD 0x00000080UL /**< PF reset complete */ + +/** Mailbox Memory Register Base */ +#define INTELVF_MBMEM 0x0800UL + +/** Reset mailbox message */ +#define INTELVF_MSG_TYPE_RESET 0x00000001UL + +/** Set MAC address mailbox message */ +#define INTELVF_MSG_TYPE_SET_MAC 0x00000002UL + +/** Set MTU mailbox message */ +#define INTELVF_MSG_TYPE_SET_MTU 0x00000005UL + +/** Control ("ping") mailbox message */ +#define INTELVF_MSG_TYPE_CONTROL 0x00000100UL + +/** Message type mask */ +#define INTELVF_MSG_TYPE_MASK 0x0000ffffUL + +/** Message NACK flag */ +#define INTELVF_MSG_NACK 0x40000000UL + +/** Message ACK flag */ +#define INTELVF_MSG_ACK 0x80000000UL + +/** Message is a response */ +#define INTELVF_MSG_RESPONSE ( INTELVF_MSG_ACK | INTELVF_MSG_NACK ) + +/** MAC address mailbox message */ +struct intelvf_msg_mac { + /** Message header */ + uint32_t hdr; + /** MAC address */ + uint8_t mac[ETH_ALEN]; + /** Alignment padding */ + uint8_t reserved[ (-ETH_ALEN) & 0x3 ]; +} __attribute__ (( packed )); + +/** Version number mailbox message */ +struct intelvf_msg_version { + /** Message header */ + uint32_t hdr; + /** API version */ + uint32_t version; +} __attribute__ (( packed )); + +/** MTU mailbox message */ +struct intelvf_msg_mtu { + /** Message header */ + uint32_t hdr; + /** Maximum packet size */ + uint32_t mtu; +} __attribute__ (( packed )); + +/** Mailbox message */ +union intelvf_msg { + /** Message header */ + uint32_t hdr; + /** MAC address message */ + struct intelvf_msg_mac mac; + /** Version number message */ + struct intelvf_msg_version version; + /** MTU message */ + struct intelvf_msg_mtu mtu; + /** Raw dwords */ + uint32_t dword[0]; +}; + +/** Maximum time to wait for mailbox message + * + * This is a policy decision. + */ +#define INTELVF_MBOX_MAX_WAIT_MS 500 + +extern int intelvf_mbox_msg ( struct intel_nic *intel, union intelvf_msg *msg ); +extern int intelvf_mbox_poll ( struct intel_nic *intel ); +extern int intelvf_mbox_wait ( struct intel_nic *intel ); +extern int intelvf_mbox_reset ( struct intel_nic *intel, uint8_t *hw_addr ); +extern int intelvf_mbox_set_mac ( struct intel_nic *intel, + const uint8_t *ll_addr ); +extern int intelvf_mbox_set_mtu ( struct intel_nic *intel, size_t mtu ); + +#endif /* _INTELVF_H */ diff --git a/qemu/roms/ipxe/src/drivers/net/intelx.c b/qemu/roms/ipxe/src/drivers/net/intelx.c index d69900e41..982b74f12 100644 --- a/qemu/roms/ipxe/src/drivers/net/intelx.c +++ b/qemu/roms/ipxe/src/drivers/net/intelx.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -392,8 +396,10 @@ static int intelx_probe ( struct pci_device *pci ) { netdev->dev = &pci->dev; memset ( intel, 0, sizeof ( *intel ) ); intel->port = PCI_FUNC ( pci->busdevfn ); - intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTELX_TD ); - intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTELX_RD ); + intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTELX_TD, + intel_describe_tx ); + intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTELX_RD, + intel_describe_rx ); /* Fix up PCI device */ adjust_pci_device ( pci ); @@ -458,10 +464,15 @@ static void intelx_remove ( struct pci_device *pci ) { /** PCI device IDs */ static struct pci_device_id intelx_nics[] = { - PCI_ROM ( 0x8086, 0x10fb, "82599", "82599", 0 ), - PCI_ROM ( 0x8086, 0x1528, "x540at2", "X540-AT2", 0 ), - PCI_ROM ( 0x8086, 0x154d, "x520", "X520", 0 ), - PCI_ROM ( 0x8086, 0x1557, "82599", "82599", 0 ), + PCI_ROM ( 0x8086, 0x10f7, "82599-kx4", "82599 (KX/KX4)", 0 ), + PCI_ROM ( 0x8086, 0x10f8, "82599-combo-backplane", "82599 (combined backplane; KR/KX4/KX)", 0 ), + PCI_ROM ( 0x8086, 0x10f9, "82599-cx4", "82599 (CX4)", 0 ), + PCI_ROM ( 0x8086, 0x10fb, "82599-sfp", "82599 (SFI/SFP+)", 0 ), + PCI_ROM ( 0x8086, 0x10fc, "82599-xaui", "82599 (XAUI/BX4)", 0 ), + PCI_ROM ( 0x8086, 0x1528, "x540t", "X540-AT2/X540-BT2", 0 ), + PCI_ROM ( 0x8086, 0x154d, "82599-sfp-sf2", "82599 (SFI/SFP+)", 0 ), + PCI_ROM ( 0x8086, 0x1557, "82599en-sfp", "82599 (Single Port SFI Only)", 0 ), + PCI_ROM ( 0x8086, 0x1560, "x540t1", "X540-AT2/X540-BT2 (with single port NVM)", 0 ), }; /** PCI driver */ diff --git a/qemu/roms/ipxe/src/drivers/net/intelx.h b/qemu/roms/ipxe/src/drivers/net/intelx.h index 60bb294d5..6383dfcad 100644 --- a/qemu/roms/ipxe/src/drivers/net/intelx.h +++ b/qemu/roms/ipxe/src/drivers/net/intelx.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/net/intelxvf.c b/qemu/roms/ipxe/src/drivers/net/intelxvf.c new file mode 100644 index 000000000..05e34c127 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/intelxvf.c @@ -0,0 +1,466 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include "intelxvf.h" + +/** @file + * + * Intel 10 Gigabit Ethernet virtual function network card driver + * + */ + +/****************************************************************************** + * + * Diagnostics + * + ****************************************************************************** + */ + +/** + * Dump statistics + * + * @v intel Intel device + */ +static __attribute__ (( unused )) void +intelxvf_stats ( struct intel_nic *intel ) { + + DBGC ( intel, "INTEL %p TX %d (%#x%08x) RX %d (%#x%08x) multi %d\n", + intel, readl ( intel->regs + INTELXVF_GPTC ), + readl ( intel->regs + INTELXVF_GOTCH ), + readl ( intel->regs + INTELXVF_GOTCL ), + readl ( intel->regs + INTELXVF_GPRC ), + readl ( intel->regs + INTELXVF_GORCH ), + readl ( intel->regs + INTELXVF_GORCL ), + readl ( intel->regs + INTELXVF_MPRC ) ); +} + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset hardware + * + * @v intel Intel device + */ +static void intelxvf_reset ( struct intel_nic *intel ) { + + /* Perform a function-level reset */ + writel ( INTELXVF_CTRL_RST, intel->regs + INTELXVF_CTRL ); +} + +/****************************************************************************** + * + * Link state + * + ****************************************************************************** + */ + +/** + * Check link state + * + * @v netdev Network device + */ +static void intelxvf_check_link ( struct net_device *netdev ) { + struct intel_nic *intel = netdev->priv; + uint32_t links; + + /* Read link status */ + links = readl ( intel->regs + INTELXVF_LINKS ); + DBGC ( intel, "INTEL %p link status is %08x\n", intel, links ); + + /* Update network device */ + if ( links & INTELXVF_LINKS_UP ) { + netdev_link_up ( netdev ); + } else { + netdev_link_down ( netdev ); + } +} + +/****************************************************************************** + * + * Mailbox messages + * + ****************************************************************************** + */ + +/** + * Send negotiate API version message + * + * @v intel Intel device + * @v version Requested version + * @ret rc Return status code + */ +static int intelxvf_mbox_version ( struct intel_nic *intel, + unsigned int version ) { + union intelvf_msg msg; + int rc; + + /* Send set MTU message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.hdr = INTELXVF_MSG_TYPE_VERSION; + msg.version.version = version; + if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) { + DBGC ( intel, "INTEL %p negotiate API version failed: %s\n", + intel, strerror ( rc ) ); + return rc; + } + + /* Check response */ + if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) != INTELXVF_MSG_TYPE_VERSION ){ + DBGC ( intel, "INTEL %p negotiate API version unexpected " + "response:\n", intel ); + DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) ); + return -EPROTO; + } + + /* Check that this version is supported */ + if ( ! ( msg.hdr & INTELVF_MSG_ACK ) ) { + DBGC ( intel, "INTEL %p negotiate API version failed\n", + intel ); + return -EPERM; + } + + return 0; +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxvf_open ( struct net_device *netdev ) { + struct intel_nic *intel = netdev->priv; + uint32_t srrctl; + uint32_t dca_rxctrl; + int rc; + + /* Reset the function */ + intelxvf_reset ( intel ); + + /* Notify PF that reset is complete */ + if ( ( rc = intelvf_mbox_reset ( intel, NULL ) ) != 0 ) { + DBGC ( intel, "INTEL %p could not reset: %s\n", + intel, strerror ( rc ) ); + goto err_mbox_reset; + } + + /* Negotiate API version 1.1. If we do not negotiate at least + * this version, then the RX datapath will remain disabled if + * the PF has jumbo frames enabled. + * + * Ignore failures, since the host may not actually support + * v1.1. + */ + intelxvf_mbox_version ( intel, INTELXVF_MSG_VERSION_1_1 ); + + /* Set MAC address */ + if ( ( rc = intelvf_mbox_set_mac ( intel, netdev->ll_addr ) ) != 0 ) { + DBGC ( intel, "INTEL %p could not set MAC address: %s\n", + intel, strerror ( rc ) ); + goto err_mbox_set_mac; + } + + /* Set MTU */ + if ( ( rc = intelvf_mbox_set_mtu ( intel, netdev->max_pkt_len ) ) != 0){ + DBGC ( intel, "INTEL %p could not set MTU %zd: %s\n", + intel, netdev->max_pkt_len, strerror ( rc ) ); + goto err_mbox_set_mtu; + } + + /* Create transmit descriptor ring */ + if ( ( rc = intel_create_ring ( intel, &intel->tx ) ) != 0 ) + goto err_create_tx; + + /* Create receive descriptor ring */ + if ( ( rc = intel_create_ring ( intel, &intel->rx ) ) != 0 ) + goto err_create_rx; + + /* Allocate interrupt vectors */ + writel ( ( INTELXVF_IVAR_RX0_DEFAULT | INTELXVF_IVAR_RX0_VALID | + INTELXVF_IVAR_TX0_DEFAULT | INTELXVF_IVAR_TX0_VALID ), + intel->regs + INTELXVF_IVAR ); + writel ( ( INTELXVF_IVARM_MBOX_DEFAULT | INTELXVF_IVARM_MBOX_VALID ), + intel->regs + INTELXVF_IVARM ); + + /* Configure receive buffer sizes and set receive descriptor type */ + srrctl = readl ( intel->regs + INTELXVF_SRRCTL ); + srrctl &= ~( INTELXVF_SRRCTL_BSIZE_MASK | + INTELXVF_SRRCTL_DESCTYPE_MASK ); + srrctl |= ( INTELXVF_SRRCTL_BSIZE_DEFAULT | + INTELXVF_SRRCTL_DESCTYPE_DEFAULT ); + writel ( srrctl, intel->regs + INTELXVF_SRRCTL ); + + /* Clear "must-be-zero" bit for direct cache access (DCA). We + * leave DCA disabled anyway, but if we do not clear this bit + * then the received packets contain garbage data. + */ + dca_rxctrl = readl ( intel->regs + INTELXVF_DCA_RXCTRL ); + dca_rxctrl &= ~INTELXVF_DCA_RXCTRL_MUST_BE_ZERO; + writel ( dca_rxctrl, intel->regs + INTELXVF_DCA_RXCTRL ); + + /* Fill receive ring */ + intel_refill_rx ( intel ); + + /* Update link state */ + intelxvf_check_link ( netdev ); + + return 0; + + intel_destroy_ring ( intel, &intel->rx ); + err_create_rx: + intel_destroy_ring ( intel, &intel->tx ); + err_create_tx: + err_mbox_set_mtu: + err_mbox_set_mac: + err_mbox_reset: + intelxvf_reset ( intel ); + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void intelxvf_close ( struct net_device *netdev ) { + struct intel_nic *intel = netdev->priv; + + /* Destroy receive descriptor ring */ + intel_destroy_ring ( intel, &intel->rx ); + + /* Discard any unused receive buffers */ + intel_empty_rx ( intel ); + + /* Destroy transmit descriptor ring */ + intel_destroy_ring ( intel, &intel->tx ); + + /* Reset the function */ + intelxvf_reset ( intel ); +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void intelxvf_poll ( struct net_device *netdev ) { + struct intel_nic *intel = netdev->priv; + uint32_t eicr; + int rc; + + /* Check for and acknowledge interrupts */ + eicr = readl ( intel->regs + INTELXVF_EICR ); + if ( ! eicr ) + return; + + /* Poll for TX completions, if applicable */ + if ( eicr & INTELXVF_EIRQ_TX0 ) + intel_poll_tx ( netdev ); + + /* Poll for RX completions, if applicable */ + if ( eicr & INTELXVF_EIRQ_RX0 ) + intel_poll_rx ( netdev ); + + /* Poll for mailbox messages, if applicable */ + if ( eicr & INTELXVF_EIRQ_MBOX ) { + + /* Poll mailbox */ + if ( ( rc = intelvf_mbox_poll ( intel ) ) != 0 ) { + DBGC ( intel, "INTEL %p mailbox poll failed!\n", + intel ); + netdev_rx_err ( netdev, NULL, rc ); + } + + /* Update link state */ + intelxvf_check_link ( netdev ); + } + + /* Refill RX ring */ + intel_refill_rx ( intel ); +} + +/** + * Enable or disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void intelxvf_irq ( struct net_device *netdev, int enable ) { + struct intel_nic *intel = netdev->priv; + uint32_t mask; + + mask = ( INTELXVF_EIRQ_MBOX | INTELXVF_EIRQ_TX0 | INTELXVF_EIRQ_RX0 ); + if ( enable ) { + writel ( mask, intel->regs + INTELXVF_EIMS ); + } else { + writel ( mask, intel->regs + INTELXVF_EIMC ); + } +} + +/** Network device operations */ +static struct net_device_operations intelxvf_operations = { + .open = intelxvf_open, + .close = intelxvf_close, + .transmit = intel_transmit, + .poll = intelxvf_poll, + .irq = intelxvf_irq, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int intelxvf_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct intel_nic *intel; + int rc; + + /* Allocate and initialise net device */ + netdev = alloc_etherdev ( sizeof ( *intel ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &intelxvf_operations ); + intel = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( intel, 0, sizeof ( *intel ) ); + intel_init_mbox ( &intel->mbox, INTELXVF_MBCTRL, INTELXVF_MBMEM ); + intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTELXVF_TD, + intel_describe_tx_adv ); + intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTELXVF_RD, + intel_describe_rx ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + intel->regs = ioremap ( pci->membase, INTELVF_BAR_SIZE ); + if ( ! intel->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Reset the function */ + intelxvf_reset ( intel ); + + /* Send reset message and fetch MAC address */ + if ( ( rc = intelvf_mbox_reset ( intel, netdev->hw_addr ) ) != 0 ) { + DBGC ( intel, "INTEL %p could not reset and fetch MAC: %s\n", + intel, strerror ( rc ) ); + goto err_mbox_reset; + } + + /* Reset the function (since we will not respond to Control + * ("ping") mailbox messages until the network device is opened. + */ + intelxvf_reset ( intel ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + /* Set initial link state */ + intelxvf_check_link ( netdev ); + + return 0; + + unregister_netdev ( netdev ); + err_register_netdev: + err_mbox_reset: + intelxvf_reset ( intel ); + iounmap ( intel->regs ); + err_ioremap: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void intelxvf_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct intel_nic *intel = netdev->priv; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Reset the NIC */ + intelxvf_reset ( intel ); + + /* Free network device */ + iounmap ( intel->regs ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** PCI device IDs */ +static struct pci_device_id intelxvf_nics[] = { + PCI_ROM ( 0x8086, 0x10ed, "82599-vf", "82599 VF", 0 ), + PCI_ROM ( 0x8086, 0x1515, "x540-vf", "X540 VF", 0 ), + PCI_ROM ( 0x8086, 0x1565, "x550-vf", "X550 VF", 0 ), + PCI_ROM ( 0x8086, 0x15a8, "x552-vf", "X552 VF", 0 ), +}; + +/** PCI driver */ +struct pci_driver intelxvf_driver __pci_driver = { + .ids = intelxvf_nics, + .id_count = ( sizeof ( intelxvf_nics ) / sizeof ( intelxvf_nics[0] ) ), + .probe = intelxvf_probe, + .remove = intelxvf_remove, +}; diff --git a/qemu/roms/ipxe/src/drivers/net/intelxvf.h b/qemu/roms/ipxe/src/drivers/net/intelxvf.h new file mode 100644 index 000000000..ad046a65c --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/intelxvf.h @@ -0,0 +1,104 @@ +#ifndef _INTELXVF_H +#define _INTELXVF_H + +/** @file + * + * Intel 10 Gigabit Ethernet virtual function network card driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include "intelvf.h" + +/** Control Register */ +#define INTELXVF_CTRL 0x0000UL +#define INTELXVF_CTRL_RST 0x04000000UL /**< Function-level reset */ + +/** Link Status Register */ +#define INTELXVF_LINKS 0x0010UL +#define INTELXVF_LINKS_UP 0x40000000UL /**< Link up */ + +/** Extended Interrupt Cause Read Register */ +#define INTELXVF_EICR 0x0100UL +#define INTELXVF_EIRQ_RX0 0x00000001UL /**< RX queue 0 (via IVAR) */ +#define INTELXVF_EIRQ_TX0 0x00000002UL /**< TX queue 0 (via IVAR) */ +#define INTELXVF_EIRQ_MBOX 0x00000004UL /**< Mailbox (via IVARM) */ + +/** Extended Interrupt Mask Set/Read Register */ +#define INTELXVF_EIMS 0x0108UL + +/** Extended Interrupt Mask Clear Register */ +#define INTELXVF_EIMC 0x010cUL + +/** Interrupt Vector Allocation Register */ +#define INTELXVF_IVAR 0x0120UL +#define INTELXVF_IVAR_RX0(bit) ( (bit) << 0 ) /**< RX queue 0 allocation */ +#define INTELXVF_IVAR_RX0_DEFAULT INTELXVF_IVAR_RX0 ( 0x00 ) +#define INTELXVF_IVAR_RX0_MASK INTELXVF_IVAR_RX0 ( 0x01 ) +#define INTELXVF_IVAR_RX0_VALID 0x00000080UL /**< RX queue 0 valid */ +#define INTELXVF_IVAR_TX0(bit) ( (bit) << 8 ) /**< TX queue 0 allocation */ +#define INTELXVF_IVAR_TX0_DEFAULT INTELXVF_IVAR_TX0 ( 0x01 ) +#define INTELXVF_IVAR_TX0_MASK INTELXVF_IVAR_TX0 ( 0x01 ) +#define INTELXVF_IVAR_TX0_VALID 0x00008000UL /**< TX queue 0 valid */ + +/** Interrupt Vector Allocation Miscellaneous Register */ +#define INTELXVF_IVARM 0x0140UL +#define INTELXVF_IVARM_MBOX(bit) ( (bit) << 0 ) /**< Mailbox allocation */ +#define INTELXVF_IVARM_MBOX_DEFAULT INTELXVF_IVARM_MBOX ( 0x02 ) +#define INTELXVF_IVARM_MBOX_MASK INTELXVF_IVARM_MBOX ( 0x03 ) +#define INTELXVF_IVARM_MBOX_VALID 0x00000080UL /**< Mailbox valid */ + +/** Mailbox Memory Register Base */ +#define INTELXVF_MBMEM 0x0200UL + +/** Mailbox Control Register */ +#define INTELXVF_MBCTRL 0x02fcUL + +/** Receive Descriptor register block */ +#define INTELXVF_RD 0x1000UL + +/** RX DCA Control Register */ +#define INTELXVF_DCA_RXCTRL 0x100cUL +#define INTELXVF_DCA_RXCTRL_MUST_BE_ZERO 0x00001000UL /**< Must be zero */ + +/** Split Receive Control Register */ +#define INTELXVF_SRRCTL 0x1014UL +#define INTELXVF_SRRCTL_BSIZE(kb) ( (kb) << 0 ) /**< Receive buffer size */ +#define INTELXVF_SRRCTL_BSIZE_DEFAULT INTELXVF_SRRCTL_BSIZE ( 0x02 ) +#define INTELXVF_SRRCTL_BSIZE_MASK INTELXVF_SRRCTL_BSIZE ( 0x1f ) +#define INTELXVF_SRRCTL_DESCTYPE(typ) ( (typ) << 25 ) /**< Descriptor type */ +#define INTELXVF_SRRCTL_DESCTYPE_DEFAULT INTELXVF_SRRCTL_DESCTYPE ( 0x00 ) +#define INTELXVF_SRRCTL_DESCTYPE_MASK INTELXVF_SRRCTL_DESCTYPE ( 0x07 ) + +/** Good Packets Received Count */ +#define INTELXVF_GPRC 0x101c + +/** Good Packets Received Count Low */ +#define INTELXVF_GORCL 0x1020 + +/** Good Packets Received Count High */ +#define INTELXVF_GORCH 0x1024 + +/* Multicast Packets Received Count */ +#define INTELXVF_MPRC 0x1034 + +/** Transmit Descriptor register block */ +#define INTELXVF_TD 0x2000UL + +/** Good Packets Transmitted Count */ +#define INTELXVF_GPTC 0x201c + +/** Good Packets Transmitted Count Low */ +#define INTELXVF_GOTCL 0x2020 + +/** Good Packets Transmitted Count High */ +#define INTELXVF_GOTCH 0x2024 + +/** Negotiate API version mailbox message */ +#define INTELXVF_MSG_TYPE_VERSION 0x00000008UL + +/** API version 1.1 */ +#define INTELXVF_MSG_VERSION_1_1 0x00000002UL + +#endif /* _INTELXVF_H */ diff --git a/qemu/roms/ipxe/src/drivers/net/ipoib.c b/qemu/roms/ipxe/src/drivers/net/ipoib.c index 1b5391776..6552d764e 100644 --- a/qemu/roms/ipxe/src/drivers/net/ipoib.c +++ b/qemu/roms/ipxe/src/drivers/net/ipoib.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -29,8 +33,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include +#include #include #include #include @@ -44,6 +50,20 @@ FILE_LICENCE ( GPL2_OR_LATER ); * IP over Infiniband */ +/* Disambiguate the various error causes */ +#define ENXIO_ARP_REPLY __einfo_error ( EINFO_ENXIO_ARP_REPLY ) +#define EINFO_ENXIO_ARP_REPLY \ + __einfo_uniqify ( EINFO_ENXIO, 0x01, \ + "Missing REMAC for ARP reply target address" ) +#define ENXIO_NON_IPV4 __einfo_error ( EINFO_ENXIO_NON_IPV4 ) +#define EINFO_ENXIO_NON_IPV4 \ + __einfo_uniqify ( EINFO_ENXIO, 0x02, \ + "Missing REMAC for non-IPv4 packet" ) +#define ENXIO_ARP_SENT __einfo_error ( EINFO_ENXIO_ARP_SENT ) +#define EINFO_ENXIO_ARP_SENT \ + __einfo_uniqify ( EINFO_ENXIO, 0x03, \ + "Missing REMAC for IPv4 packet (ARP sent)" ) + /** Number of IPoIB send work queue entries */ #define IPOIB_NUM_SEND_WQES 2 @@ -96,6 +116,8 @@ struct errortab ipoib_errors[] __errortab = { __einfo_errortab ( EINFO_EINPROGRESS_JOINING ), }; +static struct net_device_operations ipoib_operations; + /**************************************************************************** * * IPoIB REMAC cache @@ -124,8 +146,10 @@ static struct ipoib_mac * ipoib_find_remac ( struct ipoib_device *ipoib, const struct ipoib_remac *remac ) { struct ipoib_peer *peer; - /* Check for broadcast REMAC */ - if ( is_broadcast_ether_addr ( remac ) ) + /* Check for broadcast or multicast REMAC. We transmit + * multicasts as broadcasts for simplicity. + */ + if ( is_multicast_ether_addr ( remac ) ) return &ipoib->broadcast; /* Try to find via REMAC cache */ @@ -202,14 +226,20 @@ static void ipoib_flush_remac ( struct ipoib_device *ipoib ) { * @ret discarded Number of cached items discarded */ static unsigned int ipoib_discard_remac ( void ) { - struct ib_device *ibdev; + struct net_device *netdev; struct ipoib_device *ipoib; struct ipoib_peer *peer; unsigned int discarded = 0; /* Try to discard one cache entry for each IPoIB device */ - for_each_ibdev ( ibdev ) { - ipoib = ib_get_ownerdata ( ibdev ); + for_each_netdev ( netdev ) { + + /* Skip non-IPoIB devices */ + if ( netdev->op != &ipoib_operations ) + continue; + ipoib = netdev->priv; + + /* Discard least recently used cache entry (if any) */ list_for_each_entry_reverse ( peer, &ipoib->peers, list ) { list_del ( &peer->list ); free ( peer ); @@ -222,7 +252,7 @@ static unsigned int ipoib_discard_remac ( void ) { } /** IPoIB cache discarder */ -struct cache_discarder ipoib_discarder __cache_discarder ( CACHE_NORMAL ) = { +struct cache_discarder ipoib_discarder __cache_discarder ( CACHE_EXPENSIVE ) = { .discard = ipoib_discard_remac, }; @@ -324,8 +354,11 @@ static int ipoib_translate_tx_arp ( struct net_device *netdev, /* Look up REMAC, if applicable */ if ( arphdr->ar_op == ARPOP_REPLY ) { target_ha = ipoib_find_remac ( ipoib, arp_target_pa ( arphdr )); - if ( ! target_ha ) - return -ENXIO; + if ( ! target_ha ) { + DBGC ( ipoib, "IPoIB %p no REMAC for %s ARP reply\n", + ipoib, eth_ntoa ( arp_target_pa ( arphdr ) ) ); + return -ENXIO_ARP_REPLY; + } } /* Construct new packet */ @@ -461,6 +494,7 @@ static int ipoib_transmit ( struct net_device *netdev, struct ipoib_device *ipoib = netdev->priv; struct ib_device *ibdev = ipoib->ibdev; struct ethhdr *ethhdr; + struct iphdr *iphdr; struct ipoib_hdr *ipoib_hdr; struct ipoib_mac *mac; struct ib_address_vector dest; @@ -485,9 +519,34 @@ static int ipoib_transmit ( struct net_device *netdev, iob_pull ( iobuf, sizeof ( *ethhdr ) ); /* Identify destination address */ - mac = ipoib_find_remac ( ipoib, ( ( void *) ethhdr->h_dest ) ); - if ( ! mac ) - return -ENXIO; + mac = ipoib_find_remac ( ipoib, ( ( void * ) ethhdr->h_dest ) ); + if ( ! mac ) { + /* Generate a new ARP request (if possible) to trigger + * population of the REMAC cache entry. + */ + if ( ( net_proto != htons ( ETH_P_IP ) ) || + ( iob_len ( iobuf ) < sizeof ( *iphdr ) ) ) { + DBGC ( ipoib, "IPoIB %p no REMAC for %s non-IPv4 " + "packet type %04x\n", ipoib, + eth_ntoa ( ethhdr->h_dest ), + ntohs ( net_proto ) ); + return -ENXIO_NON_IPV4; + } + iphdr = iobuf->data; + if ( ( rc = arp_tx_request ( netdev, &ipv4_protocol, + &iphdr->dest, &iphdr->src ) ) !=0){ + DBGC ( ipoib, "IPoIB %p could not ARP for %s/%s/", + ipoib, eth_ntoa ( ethhdr->h_dest ), + inet_ntoa ( iphdr->dest ) ); + DBGC ( ipoib, "%s: %s\n", inet_ntoa ( iphdr->src ), + strerror ( rc ) ); + return rc; + } + DBGC ( ipoib, "IPoIB %p no REMAC for %s/%s/", ipoib, + eth_ntoa ( ethhdr->h_dest ), inet_ntoa ( iphdr->dest ) ); + DBGC ( ipoib, "%s\n", inet_ntoa ( iphdr->src ) ); + return -ENXIO_ARP_SENT; + } /* Translate packet if applicable */ if ( ( rc = ipoib_translate_tx ( netdev, iobuf, net_proto ) ) != 0 ) @@ -732,7 +791,8 @@ static void ipoib_link_state_changed ( struct ib_device *ibdev ) { int rc; /* Leave existing broadcast group */ - ipoib_leave_broadcast_group ( ipoib ); + if ( ipoib->qp ) + ipoib_leave_broadcast_group ( ipoib ); /* Update MAC address based on potentially-new GID prefix */ memcpy ( &ipoib->mac.gid.s.prefix, &ibdev->gid.s.prefix, @@ -747,7 +807,7 @@ static void ipoib_link_state_changed ( struct ib_device *ibdev ) { netdev_link_err ( netdev, ( rc ? rc : -EINPROGRESS_JOINING ) ); /* Join new broadcast group */ - if ( ib_is_open ( ibdev ) && ib_link_ok ( ibdev ) && + if ( ib_is_open ( ibdev ) && ib_link_ok ( ibdev ) && ipoib->qp && ( ( rc = ipoib_join_broadcast_group ( ipoib ) ) != 0 ) ) { DBGC ( ipoib, "IPoIB %p could not rejoin broadcast group: " "%s\n", ipoib, strerror ( rc ) ); @@ -835,7 +895,9 @@ static void ipoib_close ( struct net_device *netdev ) { /* Tear down the queues */ ib_destroy_qp ( ibdev, ipoib->qp ); + ipoib->qp = NULL; ib_destroy_cq ( ibdev, ipoib->cq ); + ipoib->cq = NULL; /* Close IB device */ ib_close ( ibdev ); diff --git a/qemu/roms/ipxe/src/drivers/net/legacy.c b/qemu/roms/ipxe/src/drivers/net/legacy.c index 4edbef162..73a80194f 100644 --- a/qemu/roms/ipxe/src/drivers/net/legacy.c +++ b/qemu/roms/ipxe/src/drivers/net/legacy.c @@ -17,7 +17,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct nic nic; diff --git a/qemu/roms/ipxe/src/drivers/net/mii.c b/qemu/roms/ipxe/src/drivers/net/mii.c index c4d32514d..9b297029a 100644 --- a/qemu/roms/ipxe/src/drivers/net/mii.c +++ b/qemu/roms/ipxe/src/drivers/net/mii.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -111,3 +115,35 @@ int mii_reset ( struct mii_interface *mii ) { DBGC ( mii, "MII %p timed out waiting for reset\n", mii ); return -ETIMEDOUT; } + +/** + * Update link status via MII + * + * @v mii MII interface + * @v netdev Network device + * @ret rc Return status code + */ +int mii_check_link ( struct mii_interface *mii, struct net_device *netdev ) { + int bmsr; + int link; + int rc; + + /* Read BMSR */ + bmsr = mii_read ( mii, MII_BMSR ); + if ( bmsr < 0 ) { + rc = bmsr; + return rc; + } + + /* Report link status */ + link = ( bmsr & BMSR_LSTATUS ); + DBGC ( mii, "MII %p link %s (BMSR %#04x)\n", + mii, ( link ? "up" : "down" ), bmsr ); + if ( link ) { + netdev_link_up ( netdev ); + } else { + netdev_link_down ( netdev ); + } + + return 0; +} diff --git a/qemu/roms/ipxe/src/drivers/net/myson.c b/qemu/roms/ipxe/src/drivers/net/myson.c index 6abb55660..84a550596 100644 --- a/qemu/roms/ipxe/src/drivers/net/myson.c +++ b/qemu/roms/ipxe/src/drivers/net/myson.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/net/myson.h b/qemu/roms/ipxe/src/drivers/net/myson.h index 8d7cc5855..05a6b8a58 100644 --- a/qemu/roms/ipxe/src/drivers/net/myson.h +++ b/qemu/roms/ipxe/src/drivers/net/myson.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/net/ncm.c b/qemu/roms/ipxe/src/drivers/net/ncm.c new file mode 100644 index 000000000..10728d2a1 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/ncm.c @@ -0,0 +1,672 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ecm.h" +#include "ncm.h" + +/** @file + * + * CDC-NCM USB Ethernet driver + * + */ + +/** Interrupt completion profiler */ +static struct profiler ncm_intr_profiler __profiler = + { .name = "ncm.intr" }; + +/** Bulk IN completion profiler */ +static struct profiler ncm_in_profiler __profiler = + { .name = "ncm.in" }; + +/** Bulk IN per-datagram profiler */ +static struct profiler ncm_in_datagram_profiler __profiler = + { .name = "ncm.in_dgram" }; + +/** Bulk OUT profiler */ +static struct profiler ncm_out_profiler __profiler = + { .name = "ncm.out" }; + +/****************************************************************************** + * + * CDC-NCM communications interface + * + ****************************************************************************** + */ + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void ncm_intr_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct ncm_device *ncm = container_of ( ep, struct ncm_device, + usbnet.intr ); + struct net_device *netdev = ncm->netdev; + struct usb_setup_packet *message; + size_t len = iob_len ( iobuf ); + + /* Profile completions */ + profile_start ( &ncm_intr_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto ignore; + + /* Ignore packets with errors */ + if ( rc != 0 ) { + DBGC ( ncm, "NCM %p interrupt failed: %s\n", + ncm, strerror ( rc ) ); + DBGC_HDA ( ncm, 0, iobuf->data, iob_len ( iobuf ) ); + goto error; + } + + /* Extract message header */ + if ( len < sizeof ( *message ) ) { + DBGC ( ncm, "NCM %p underlength interrupt:\n", ncm ); + DBGC_HDA ( ncm, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto error; + } + message = iobuf->data; + + /* Parse message header */ + switch ( message->request ) { + + case cpu_to_le16 ( CDC_NETWORK_CONNECTION ) : + if ( message->value ) { + DBGC ( ncm, "NCM %p link up\n", ncm ); + netdev_link_up ( netdev ); + } else { + DBGC ( ncm, "NCM %p link down\n", ncm ); + netdev_link_down ( netdev ); + } + break; + + case cpu_to_le16 ( CDC_CONNECTION_SPEED_CHANGE ) : + /* Ignore */ + break; + + default: + DBGC ( ncm, "NCM %p unrecognised interrupt:\n", ncm ); + DBGC_HDA ( ncm, 0, iobuf->data, iob_len ( iobuf ) ); + goto error; + } + + /* Free I/O buffer */ + free_iob ( iobuf ); + profile_stop ( &ncm_intr_profiler ); + + return; + + error: + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); + ignore: + free_iob ( iobuf ); + return; +} + +/** Interrupt endpoint operations */ +static struct usb_endpoint_driver_operations ncm_intr_operations = { + .complete = ncm_intr_complete, +}; + +/****************************************************************************** + * + * CDC-NCM data interface + * + ****************************************************************************** + */ + +/** + * Prefill bulk IN endpoint + * + * @v ncm CDC-NCM device + * @ret rc Return status code + */ +static int ncm_in_prefill ( struct ncm_device *ncm ) { + struct usb_bus *bus = ncm->bus; + size_t mtu; + unsigned int count; + int rc; + + /* Some devices have a very small number of internal buffers, + * and rely on being able to pack multiple packets into each + * buffer. We therefore want to use large buffers if + * possible. However, large allocations have a reasonable + * chance of failure, especially if this is not the first or + * only device to be opened. + * + * We therefore attempt to find a usable buffer size, starting + * large and working downwards until allocation succeeds. + * Smaller buffers will still work, albeit with a higher + * chance of packet loss and so lower overall throughput. + */ + for ( mtu = ncm->mtu ; mtu >= NCM_MIN_NTB_INPUT_SIZE ; mtu >>= 1 ) { + + /* Attempt allocation at this MTU */ + if ( mtu > NCM_MAX_NTB_INPUT_SIZE ) + continue; + if ( mtu > bus->mtu ) + continue; + count = ( NCM_IN_MIN_SIZE / mtu ); + if ( count < NCM_IN_MIN_COUNT ) + count = NCM_IN_MIN_COUNT; + if ( ( count * mtu ) > NCM_IN_MAX_SIZE ) + continue; + usb_refill_init ( &ncm->usbnet.in, mtu, count ); + if ( ( rc = usb_prefill ( &ncm->usbnet.in ) ) != 0 ) { + DBGC ( ncm, "NCM %p could not prefill %dx %zd-byte " + "buffers for bulk IN\n", ncm, count, mtu ); + continue; + } + + DBGC ( ncm, "NCM %p using %dx %zd-byte buffers for bulk IN\n", + ncm, count, mtu ); + return 0; + } + + DBGC ( ncm, "NCM %p could not prefill bulk IN endpoint\n", ncm ); + return -ENOMEM; +} + +/** + * Complete bulk IN transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void ncm_in_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int rc ) { + struct ncm_device *ncm = container_of ( ep, struct ncm_device, + usbnet.in ); + struct net_device *netdev = ncm->netdev; + struct ncm_transfer_header *nth; + struct ncm_datagram_pointer *ndp; + struct ncm_datagram_descriptor *desc; + struct io_buffer *pkt; + unsigned int remaining; + size_t ndp_offset; + size_t ndp_len; + size_t pkt_offset; + size_t pkt_len; + size_t headroom; + size_t len; + + /* Profile overall bulk IN completion */ + profile_start ( &ncm_in_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto ignore; + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( ncm, "NCM %p bulk IN failed: %s\n", + ncm, strerror ( rc ) ); + goto error; + } + + /* Locate transfer header */ + len = iob_len ( iobuf ); + if ( sizeof ( *nth ) > len ) { + DBGC ( ncm, "NCM %p packet too short for NTH:\n", ncm ); + rc = -EINVAL; + goto error; + } + nth = iobuf->data; + + /* Locate datagram pointer */ + ndp_offset = le16_to_cpu ( nth->offset ); + if ( ( ndp_offset + sizeof ( *ndp ) ) > len ) { + DBGC ( ncm, "NCM %p packet too short for NDP:\n", ncm ); + rc = -EINVAL; + goto error; + } + ndp = ( iobuf->data + ndp_offset ); + ndp_len = le16_to_cpu ( ndp->header_len ); + if ( ndp_len < offsetof ( typeof ( *ndp ), desc ) ) { + DBGC ( ncm, "NCM %p NDP header length too short:\n", ncm ); + rc = -EINVAL; + goto error; + } + if ( ( ndp_offset + ndp_len ) > len ) { + DBGC ( ncm, "NCM %p packet too short for NDP:\n", ncm ); + rc = -EINVAL; + goto error; + } + + /* Process datagrams */ + remaining = ( ( ndp_len - offsetof ( typeof ( *ndp ), desc ) ) / + sizeof ( ndp->desc[0] ) ); + for ( desc = ndp->desc ; remaining && desc->offset ; remaining-- ) { + + /* Profile individual datagrams */ + profile_start ( &ncm_in_datagram_profiler ); + + /* Locate datagram */ + pkt_offset = le16_to_cpu ( desc->offset ); + pkt_len = le16_to_cpu ( desc->len ); + if ( pkt_len < ETH_HLEN ) { + DBGC ( ncm, "NCM %p underlength datagram:\n", ncm ); + rc = -EINVAL; + goto error; + } + if ( ( pkt_offset + pkt_len ) > len ) { + DBGC ( ncm, "NCM %p datagram exceeds packet:\n", ncm ); + rc = -EINVAL; + goto error; + } + + /* Move to next descriptor */ + desc++; + + /* Copy data to a new I/O buffer. Our USB buffers may + * be very large and so we choose to recycle the + * buffers directly rather than attempt reallocation + * while the device is running. We therefore copy the + * data to a new I/O buffer even if this is the only + * (or last) packet within the buffer. + * + * We reserve enough space at the start of each buffer + * to allow for our own transmission header, to + * support protocols such as ARP which may modify the + * received packet and reuse the same I/O buffer for + * transmission. + */ + headroom = ( sizeof ( struct ncm_ntb_header ) + ncm->padding ); + pkt = alloc_iob ( headroom + pkt_len ); + if ( ! pkt ) { + /* Record error and continue */ + netdev_rx_err ( netdev, NULL, -ENOMEM ); + continue; + } + iob_reserve ( pkt, headroom ); + memcpy ( iob_put ( pkt, pkt_len ), + ( iobuf->data + pkt_offset ), pkt_len ); + + /* Strip CRC, if present */ + if ( ndp->magic & cpu_to_le32 ( NCM_DATAGRAM_POINTER_MAGIC_CRC)) + iob_unput ( pkt, 4 /* CRC32 */ ); + + /* Hand off to network stack */ + netdev_rx ( netdev, pkt ); + profile_stop ( &ncm_in_datagram_profiler ); + } + + /* Recycle I/O buffer */ + usb_recycle ( &ncm->usbnet.in, iobuf ); + profile_stop ( &ncm_in_profiler ); + + return; + + error: + /* Record error against network device */ + DBGC_HDA ( ncm, 0, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, NULL, rc ); + ignore: + usb_recycle ( &ncm->usbnet.in, iobuf ); +} + +/** Bulk IN endpoint operations */ +static struct usb_endpoint_driver_operations ncm_in_operations = { + .complete = ncm_in_complete, +}; + +/** + * Transmit packet + * + * @v ncm CDC-NCM device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int ncm_out_transmit ( struct ncm_device *ncm, + struct io_buffer *iobuf ) { + struct ncm_ntb_header *header; + size_t len = iob_len ( iobuf ); + size_t header_len = ( sizeof ( *header ) + ncm->padding ); + int rc; + + /* Profile transmissions */ + profile_start ( &ncm_out_profiler ); + + /* Prepend header */ + if ( ( rc = iob_ensure_headroom ( iobuf, header_len ) ) != 0 ) + return rc; + header = iob_push ( iobuf, header_len ); + + /* Populate header */ + header->nth.magic = cpu_to_le32 ( NCM_TRANSFER_HEADER_MAGIC ); + header->nth.header_len = cpu_to_le16 ( sizeof ( header->nth ) ); + header->nth.sequence = cpu_to_le16 ( ncm->sequence ); + header->nth.len = cpu_to_le16 ( iob_len ( iobuf ) ); + header->nth.offset = + cpu_to_le16 ( offsetof ( typeof ( *header ), ndp ) ); + header->ndp.magic = cpu_to_le32 ( NCM_DATAGRAM_POINTER_MAGIC ); + header->ndp.header_len = cpu_to_le16 ( sizeof ( header->ndp ) + + sizeof ( header->desc ) ); + header->ndp.offset = cpu_to_le16 ( 0 ); + header->desc[0].offset = cpu_to_le16 ( header_len ); + header->desc[0].len = cpu_to_le16 ( len ); + memset ( &header->desc[1], 0, sizeof ( header->desc[1] ) ); + + /* Enqueue I/O buffer */ + if ( ( rc = usb_stream ( &ncm->usbnet.out, iobuf, 0 ) ) != 0 ) + return rc; + + /* Increment sequence number */ + ncm->sequence++; + + profile_stop ( &ncm_out_profiler ); + return 0; +} + +/** + * Complete bulk OUT transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void ncm_out_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int rc ) { + struct ncm_device *ncm = container_of ( ep, struct ncm_device, + usbnet.out ); + struct net_device *netdev = ncm->netdev; + + /* Report TX completion */ + netdev_tx_complete_err ( netdev, iobuf, rc ); +} + +/** Bulk OUT endpoint operations */ +static struct usb_endpoint_driver_operations ncm_out_operations = { + .complete = ncm_out_complete, +}; + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ncm_open ( struct net_device *netdev ) { + struct ncm_device *ncm = netdev->priv; + struct usb_device *usb = ncm->usb; + struct ncm_set_ntb_input_size size; + int rc; + + /* Reset sequence number */ + ncm->sequence = 0; + + /* Prefill I/O buffers */ + if ( ( rc = ncm_in_prefill ( ncm ) ) != 0 ) + goto err_prefill; + + /* Set maximum input size */ + memset ( &size, 0, sizeof ( size ) ); + size.mtu = cpu_to_le32 ( ncm->usbnet.in.len ); + if ( ( rc = usb_control ( usb, NCM_SET_NTB_INPUT_SIZE, 0, + ncm->usbnet.comms, &size, + sizeof ( size ) ) ) != 0 ) { + DBGC ( ncm, "NCM %p could not set input size to %zd: %s\n", + ncm, ncm->usbnet.in.len, strerror ( rc ) ); + goto err_set_ntb_input_size; + } + + /* Open USB network device */ + if ( ( rc = usbnet_open ( &ncm->usbnet ) ) != 0 ) { + DBGC ( ncm, "NCM %p could not open: %s\n", + ncm, strerror ( rc ) ); + goto err_open; + } + + return 0; + + usbnet_close ( &ncm->usbnet ); + err_open: + err_set_ntb_input_size: + usb_flush ( &ncm->usbnet.in ); + err_prefill: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void ncm_close ( struct net_device *netdev ) { + struct ncm_device *ncm = netdev->priv; + + /* Close USB network device */ + usbnet_close ( &ncm->usbnet ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int ncm_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct ncm_device *ncm = netdev->priv; + int rc; + + /* Transmit packet */ + if ( ( rc = ncm_out_transmit ( ncm, iobuf ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void ncm_poll ( struct net_device *netdev ) { + struct ncm_device *ncm = netdev->priv; + int rc; + + /* Poll USB bus */ + usb_poll ( ncm->bus ); + + /* Refill endpoints */ + if ( ( rc = usbnet_refill ( &ncm->usbnet ) ) != 0 ) + netdev_rx_err ( netdev, NULL, rc ); + +} + +/** CDC-NCM network device operations */ +static struct net_device_operations ncm_operations = { + .open = ncm_open, + .close = ncm_close, + .transmit = ncm_transmit, + .poll = ncm_poll, +}; + +/****************************************************************************** + * + * USB interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int ncm_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct net_device *netdev; + struct ncm_device *ncm; + struct usb_interface_descriptor *comms; + struct ecm_ethernet_descriptor *ethernet; + struct ncm_ntb_parameters params; + int rc; + + /* Allocate and initialise structure */ + netdev = alloc_etherdev ( sizeof ( *ncm ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &ncm_operations ); + netdev->dev = &func->dev; + ncm = netdev->priv; + memset ( ncm, 0, sizeof ( *ncm ) ); + ncm->usb = usb; + ncm->bus = usb->port->hub->bus; + ncm->netdev = netdev; + usbnet_init ( &ncm->usbnet, func, &ncm_intr_operations, + &ncm_in_operations, &ncm_out_operations ); + usb_refill_init ( &ncm->usbnet.intr, 0, NCM_INTR_COUNT ); + DBGC ( ncm, "NCM %p on %s\n", ncm, func->name ); + + /* Describe USB network device */ + if ( ( rc = usbnet_describe ( &ncm->usbnet, config ) ) != 0 ) { + DBGC ( ncm, "NCM %p could not describe: %s\n", + ncm, strerror ( rc ) ); + goto err_describe; + } + + /* Locate Ethernet descriptor */ + comms = usb_interface_descriptor ( config, ncm->usbnet.comms, 0 ); + assert ( comms != NULL ); + ethernet = ecm_ethernet_descriptor ( config, comms ); + if ( ! ethernet ) { + DBGC ( ncm, "NCM %p has no Ethernet descriptor\n", ncm ); + rc = -EINVAL; + goto err_ethernet; + } + + /* Fetch MAC address */ + if ( ( rc = ecm_fetch_mac ( usb, ethernet, netdev->hw_addr ) ) != 0 ) { + DBGC ( ncm, "NCM %p could not fetch MAC address: %s\n", + ncm, strerror ( rc ) ); + goto err_fetch_mac; + } + + /* Get NTB parameters */ + if ( ( rc = usb_control ( usb, NCM_GET_NTB_PARAMETERS, 0, + ncm->usbnet.comms, ¶ms, + sizeof ( params ) ) ) != 0 ) { + DBGC ( ncm, "NCM %p could not get NTB parameters: %s\n", + ncm, strerror ( rc ) ); + goto err_ntb_parameters; + } + + /* Get maximum supported input size */ + ncm->mtu = le32_to_cpu ( params.in.mtu ); + DBGC2 ( ncm, "NCM %p maximum IN size is %zd bytes\n", ncm, ncm->mtu ); + + /* Calculate transmit padding */ + ncm->padding = ( ( le16_to_cpu ( params.out.remainder ) - + sizeof ( struct ncm_ntb_header ) - ETH_HLEN ) & + ( le16_to_cpu ( params.out.divisor ) - 1 ) ); + DBGC2 ( ncm, "NCM %p using %zd-byte transmit padding\n", + ncm, ncm->padding ); + assert ( ( ( sizeof ( struct ncm_ntb_header ) + ncm->padding + + ETH_HLEN ) % le16_to_cpu ( params.out.divisor ) ) == + le16_to_cpu ( params.out.remainder ) ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + usb_func_set_drvdata ( func, ncm ); + return 0; + + unregister_netdev ( netdev ); + err_register: + err_ntb_parameters: + err_fetch_mac: + err_ethernet: + err_describe: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v func USB function + */ +static void ncm_remove ( struct usb_function *func ) { + struct ncm_device *ncm = usb_func_get_drvdata ( func ); + struct net_device *netdev = ncm->netdev; + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** CDC-NCM device IDs */ +static struct usb_device_id ncm_ids[] = { + { + .name = "cdc-ncm", + .vendor = USB_ANY_ID, + .product = USB_ANY_ID, + .class = { + .class = USB_CLASS_CDC, + .subclass = USB_SUBCLASS_CDC_NCM, + .protocol = 0, + }, + }, +}; + +/** CDC-NCM driver */ +struct usb_driver ncm_driver __usb_driver = { + .ids = ncm_ids, + .id_count = ( sizeof ( ncm_ids ) / sizeof ( ncm_ids[0] ) ), + .probe = ncm_probe, + .remove = ncm_remove, +}; diff --git a/qemu/roms/ipxe/src/drivers/net/ncm.h b/qemu/roms/ipxe/src/drivers/net/ncm.h new file mode 100644 index 000000000..a9565a56b --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/ncm.h @@ -0,0 +1,173 @@ +#ifndef _NCM_H +#define _NCM_H + +/** @file + * + * CDC-NCM USB Ethernet driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include "ecm.h" + +/** CDC-NCM subclass */ +#define USB_SUBCLASS_CDC_NCM 0x0d + +/** Get NTB parameters */ +#define NCM_GET_NTB_PARAMETERS \ + ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x80 ) ) + +/** NTB datagram parameters */ +struct ncm_ntb_datagram_parameters { + /** Maximum size */ + uint32_t mtu; + /** Alignment divisor */ + uint16_t divisor; + /** Alignment remainder */ + uint16_t remainder; + /** Alignment modulus */ + uint16_t modulus; +} __attribute__ (( packed )); + +/** NTB parameters */ +struct ncm_ntb_parameters { + /** Length */ + uint16_t len; + /** Supported formats */ + uint16_t formats; + /** IN datagram parameters */ + struct ncm_ntb_datagram_parameters in; + /** Reserved */ + uint16_t reserved; + /** OUT datagram parameters */ + struct ncm_ntb_datagram_parameters out; + /** Maximum number of datagrams per OUT NTB */ + uint16_t max; +} __attribute__ (( packed )); + +/** Set NTB input size */ +#define NCM_SET_NTB_INPUT_SIZE \ + ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x86 ) ) + +/** Set NTB input size */ +struct ncm_set_ntb_input_size { + /** Maximum size */ + uint32_t mtu; +} __attribute__ (( packed )); + +/** Minimum allowed NTB input size */ +#define NCM_MIN_NTB_INPUT_SIZE 2048 + +/** Maximum allowed NTB input size (16-bit) */ +#define NCM_MAX_NTB_INPUT_SIZE 65536 + +/** CDC-NCM transfer header (16-bit) */ +struct ncm_transfer_header { + /** Signature */ + uint32_t magic; + /** Header length */ + uint16_t header_len; + /** Sequence number */ + uint16_t sequence; + /** Total length */ + uint16_t len; + /** Offset of first datagram pointer */ + uint16_t offset; +} __attribute__ (( packed )); + +/** CDC-NCM transfer header magic */ +#define NCM_TRANSFER_HEADER_MAGIC 0x484d434eUL + +/** CDC-NCM datagram descriptor (16-bit) */ +struct ncm_datagram_descriptor { + /** Starting offset */ + uint16_t offset; + /** Length */ + uint16_t len; +} __attribute__ (( packed )); + +/** CDC-NCM datagram pointer (16-bit) */ +struct ncm_datagram_pointer { + /** Signature */ + uint32_t magic; + /** Header length */ + uint16_t header_len; + /** Offset of next datagram pointer */ + uint16_t offset; + /** Datagram descriptors + * + * Must be terminated by an empty descriptor. + */ + struct ncm_datagram_descriptor desc[0]; +} __attribute__ (( packed )); + +/** CDC-NCM datagram pointer magic */ +#define NCM_DATAGRAM_POINTER_MAGIC 0x304d434eUL + +/** CDC-NCM datagram pointer CRC present flag */ +#define NCM_DATAGRAM_POINTER_MAGIC_CRC 0x01000000UL + +/** NTB constructed for transmitted packets (excluding padding) + * + * This is a policy decision. + */ +struct ncm_ntb_header { + /** Transfer header */ + struct ncm_transfer_header nth; + /** Datagram pointer */ + struct ncm_datagram_pointer ndp; + /** Datagram descriptors */ + struct ncm_datagram_descriptor desc[2]; +} __attribute__ (( packed )); + +/** A CDC-NCM network device */ +struct ncm_device { + /** USB device */ + struct usb_device *usb; + /** USB bus */ + struct usb_bus *bus; + /** Network device */ + struct net_device *netdev; + /** USB network device */ + struct usbnet_device usbnet; + + /** Maximum supported NTB input size */ + size_t mtu; + /** Transmitted packet sequence number */ + uint16_t sequence; + /** Alignment padding required on transmitted packets */ + size_t padding; +}; + +/** Bulk IN ring minimum buffer count + * + * This is a policy decision. + */ +#define NCM_IN_MIN_COUNT 3 + +/** Bulk IN ring minimum total buffer size + * + * This is a policy decision. + */ +#define NCM_IN_MIN_SIZE 16384 + +/** Bulk IN ring maximum total buffer size + * + * This is a policy decision. + */ +#define NCM_IN_MAX_SIZE 131072 + +/** Interrupt ring buffer count + * + * This is a policy decision. + */ +#define NCM_INTR_COUNT 2 + +#endif /* _NCM_H */ diff --git a/qemu/roms/ipxe/src/drivers/net/netfront.c b/qemu/roms/ipxe/src/drivers/net/netfront.c index 4b816329e..2f4bbf2a0 100644 --- a/qemu/roms/ipxe/src/drivers/net/netfront.c +++ b/qemu/roms/ipxe/src/drivers/net/netfront.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -135,7 +139,7 @@ static int netfront_read_mac ( struct netfront_nic *netfront, void *hw_addr ) { xendev->key, mac ); /* Decode MAC address */ - len = hex_decode ( mac, ':', hw_addr, ETH_ALEN ); + len = hex_decode ( ':', mac, hw_addr, ETH_ALEN ); if ( len < 0 ) { rc = len; DBGC ( netfront, "NETFRONT %s could not decode MAC address " @@ -593,6 +597,11 @@ static int netfront_open ( struct net_device *netdev ) { "feature-no-csum-offload" ) ) != 0 ) goto err_feature_no_csum_offload; + /* Inform backend that we will send notifications for RX requests */ + if ( ( rc = netfront_write_flag ( netfront, + "feature-rx-notify" ) ) != 0 ) + goto err_feature_rx_notify; + /* Set state to Connected */ if ( ( rc = xenbus_set_state ( xendev, XenbusStateConnected ) ) != 0 ) { DBGC ( netfront, "NETFRONT %s could not set state=\"%d\": %s\n", @@ -618,6 +627,8 @@ static int netfront_open ( struct net_device *netdev ) { err_backend_wait: netfront_reset ( netfront ); err_set_state: + netfront_rm ( netfront, "feature-rx-notify" ); + err_feature_rx_notify: netfront_rm ( netfront, "feature-no-csum-offload" ); err_feature_no_csum_offload: netfront_rm ( netfront, "request-rx-copy" ); @@ -661,6 +672,7 @@ static void netfront_close ( struct net_device *netdev ) { } /* Delete flags */ + netfront_rm ( netfront, "feature-rx-notify" ); netfront_rm ( netfront, "feature-no-csum-offload" ); netfront_rm ( netfront, "request-rx-copy" ); diff --git a/qemu/roms/ipxe/src/drivers/net/netfront.h b/qemu/roms/ipxe/src/drivers/net/netfront.h index b3f899f3c..38fd0a77e 100644 --- a/qemu/roms/ipxe/src/drivers/net/netfront.h +++ b/qemu/roms/ipxe/src/drivers/net/netfront.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/net/netvsc.c b/qemu/roms/ipxe/src/drivers/net/netvsc.c new file mode 100644 index 000000000..d269cd63e --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/netvsc.c @@ -0,0 +1,848 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Hyper-V network virtual service client + * + * The network virtual service client (NetVSC) connects to the network + * virtual service provider (NetVSP) via the Hyper-V virtual machine + * bus (VMBus). It provides a transport layer for RNDIS packets. + */ + +#include +#include +#include +#include +#include +#include +#include "netvsc.h" + +/** + * Send control message and wait for completion + * + * @v netvsc NetVSC device + * @v xrid Relative transaction ID + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ +static int netvsc_control ( struct netvsc_device *netvsc, unsigned int xrid, + const void *data, size_t len ) { + uint64_t xid = ( NETVSC_BASE_XID + xrid ); + unsigned int i; + int rc; + + /* Send control message */ + if ( ( rc = vmbus_send_control ( netvsc->vmdev, xid, data, len ) ) !=0){ + DBGC ( netvsc, "NETVSC %s could not send control message: %s\n", + netvsc->name, strerror ( rc ) ); + return rc; + } + + /* Record transaction ID */ + netvsc->wait_xrid = xrid; + + /* Wait for operation to complete */ + for ( i = 0 ; i < NETVSC_MAX_WAIT_MS ; i++ ) { + + /* Check for completion */ + if ( ! netvsc->wait_xrid ) + return netvsc->wait_rc; + + /* Poll VMBus device */ + vmbus_poll ( netvsc->vmdev ); + + /* Delay for 1ms */ + mdelay ( 1 ); + } + + DBGC ( netvsc, "NETVSC %s timed out waiting for XRID %d\n", + netvsc->name, xrid ); + vmbus_dump_channel ( netvsc->vmdev ); + return -ETIMEDOUT; +} + +/** + * Handle generic completion + * + * @v netvsc NetVSC device + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ +static int netvsc_completed ( struct netvsc_device *netvsc __unused, + const void *data __unused, size_t len __unused ) { + return 0; +} + +/** + * Initialise communication + * + * @v netvsc NetVSC device + * @ret rc Return status code + */ +static int netvsc_initialise ( struct netvsc_device *netvsc ) { + struct netvsc_init_message msg; + int rc; + + /* Construct message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.header.type = cpu_to_le32 ( NETVSC_INIT_MSG ); + msg.min = cpu_to_le32 ( NETVSC_VERSION_1 ); + msg.max = cpu_to_le32 ( NETVSC_VERSION_1 ); + + /* Send message and wait for completion */ + if ( ( rc = netvsc_control ( netvsc, NETVSC_INIT_XRID, &msg, + sizeof ( msg ) ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not initialise: %s\n", + netvsc->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Handle initialisation completion + * + * @v netvsc NetVSC device + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ +static int +netvsc_initialised ( struct netvsc_device *netvsc, const void *data, + size_t len ) { + const struct netvsc_init_completion *cmplt = data; + + /* Check completion */ + if ( len < sizeof ( *cmplt ) ) { + DBGC ( netvsc, "NETVSC %s underlength initialisation " + "completion (%zd bytes)\n", netvsc->name, len ); + return -EINVAL; + } + if ( cmplt->header.type != cpu_to_le32 ( NETVSC_INIT_CMPLT ) ) { + DBGC ( netvsc, "NETVSC %s unexpected initialisation completion " + "type %d\n", netvsc->name, + le32_to_cpu ( cmplt->header.type ) ); + return -EPROTO; + } + if ( cmplt->status != cpu_to_le32 ( NETVSC_OK ) ) { + DBGC ( netvsc, "NETVSC %s initialisation failure status %d\n", + netvsc->name, le32_to_cpu ( cmplt->status ) ); + return -EPROTO; + } + + return 0; +} + +/** + * Set NDIS version + * + * @v netvsc NetVSC device + * @ret rc Return status code + */ +static int netvsc_ndis_version ( struct netvsc_device *netvsc ) { + struct netvsc_ndis_version_message msg; + int rc; + + /* Construct message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.header.type = cpu_to_le32 ( NETVSC_NDIS_VERSION_MSG ); + msg.major = cpu_to_le32 ( NETVSC_NDIS_MAJOR ); + msg.minor = cpu_to_le32 ( NETVSC_NDIS_MINOR ); + + /* Send message and wait for completion */ + if ( ( rc = netvsc_control ( netvsc, NETVSC_NDIS_VERSION_XRID, + &msg, sizeof ( msg ) ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not set NDIS version: %s\n", + netvsc->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Establish data buffer + * + * @v netvsc NetVSC device + * @v buffer Data buffer + * @ret rc Return status code + */ +static int netvsc_establish_buffer ( struct netvsc_device *netvsc, + struct netvsc_buffer *buffer ) { + struct netvsc_establish_buffer_message msg; + int rc; + + /* Construct message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.header.type = cpu_to_le32 ( buffer->establish_type ); + msg.gpadl = cpu_to_le32 ( buffer->gpadl ); + msg.pageset = buffer->pages.pageset; /* Already protocol-endian */ + + /* Send message and wait for completion */ + if ( ( rc = netvsc_control ( netvsc, buffer->establish_xrid, &msg, + sizeof ( msg ) ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not establish buffer: %s\n", + netvsc->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Handle establish receive data buffer completion + * + * @v netvsc NetVSC device + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ +static int netvsc_rx_established_buffer ( struct netvsc_device *netvsc, + const void *data, size_t len ) { + const struct netvsc_rx_establish_buffer_completion *cmplt = data; + + /* Check completion */ + if ( len < sizeof ( *cmplt ) ) { + DBGC ( netvsc, "NETVSC %s underlength buffer completion (%zd " + "bytes)\n", netvsc->name, len ); + return -EINVAL; + } + if ( cmplt->header.type != cpu_to_le32 ( NETVSC_RX_ESTABLISH_CMPLT ) ) { + DBGC ( netvsc, "NETVSC %s unexpected buffer completion type " + "%d\n", netvsc->name, le32_to_cpu ( cmplt->header.type)); + return -EPROTO; + } + if ( cmplt->status != cpu_to_le32 ( NETVSC_OK ) ) { + DBGC ( netvsc, "NETVSC %s buffer failure status %d\n", + netvsc->name, le32_to_cpu ( cmplt->status ) ); + return -EPROTO; + } + + return 0; +} + +/** + * Revoke data buffer + * + * @v netvsc NetVSC device + * @v buffer Data buffer + * @ret rc Return status code + */ +static int netvsc_revoke_buffer ( struct netvsc_device *netvsc, + struct netvsc_buffer *buffer ) { + struct netvsc_revoke_buffer_message msg; + int rc; + + /* Construct message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.header.type = cpu_to_le32 ( buffer->revoke_type ); + msg.pageset = buffer->pages.pageset; /* Already protocol-endian */ + + /* Send message and wait for completion */ + if ( ( rc = netvsc_control ( netvsc, buffer->revoke_xrid, + &msg, sizeof ( msg ) ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not revoke buffer: %s\n", + netvsc->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Handle received control packet + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ +static int netvsc_recv_control ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len ) { + struct rndis_device *rndis = vmbus_get_drvdata ( vmdev ); + struct netvsc_device *netvsc = rndis->priv; + + DBGC ( netvsc, "NETVSC %s received unsupported control packet " + "(%08llx):\n", netvsc->name, xid ); + DBGC_HDA ( netvsc, 0, data, len ); + return -ENOTSUP; +} + +/** + * Handle received data packet + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @v data Data + * @v len Length of data + * @v list List of I/O buffers + * @ret rc Return status code + */ +static int netvsc_recv_data ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len, + struct list_head *list ) { + struct rndis_device *rndis = vmbus_get_drvdata ( vmdev ); + struct netvsc_device *netvsc = rndis->priv; + const struct netvsc_rndis_message *msg = data; + struct io_buffer *iobuf; + struct io_buffer *tmp; + int rc; + + /* Sanity check */ + if ( len < sizeof ( *msg ) ) { + DBGC ( netvsc, "NETVSC %s received underlength RNDIS packet " + "(%zd bytes)\n", netvsc->name, len ); + rc = -EINVAL; + goto err_sanity; + } + if ( msg->header.type != cpu_to_le32 ( NETVSC_RNDIS_MSG ) ) { + DBGC ( netvsc, "NETVSC %s received unexpected RNDIS packet " + "type %d\n", netvsc->name, + le32_to_cpu ( msg->header.type ) ); + rc = -EINVAL; + goto err_sanity; + } + + /* Send completion back to host */ + if ( ( rc = vmbus_send_completion ( vmdev, xid, NULL, 0 ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not send completion: %s\n", + netvsc->name, strerror ( rc ) ); + goto err_completion; + } + + /* Hand off to RNDIS */ + list_for_each_entry_safe ( iobuf, tmp, list, list ) { + list_del ( &iobuf->list ); + rndis_rx ( rndis, iob_disown ( iobuf ) ); + } + + return 0; + + err_completion: + err_sanity: + list_for_each_entry_safe ( iobuf, tmp, list, list ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); + } + return rc; +} + +/** + * Handle received completion packet + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ +static int netvsc_recv_completion ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len ) { + struct rndis_device *rndis = vmbus_get_drvdata ( vmdev ); + struct netvsc_device *netvsc = rndis->priv; + struct io_buffer *iobuf; + int ( * completion ) ( struct netvsc_device *netvsc, + const void *data, size_t len ); + unsigned int xrid = ( xid - NETVSC_BASE_XID ); + unsigned int tx_id; + int rc; + + /* Handle transmit completion, if applicable */ + tx_id = ( xrid - NETVSC_TX_BASE_XRID ); + if ( ( tx_id < NETVSC_TX_NUM_DESC ) && + ( ( iobuf = netvsc->tx.iobufs[tx_id] ) != NULL ) ) { + + /* Free buffer ID */ + netvsc->tx.iobufs[tx_id] = NULL; + netvsc->tx.ids[ ( netvsc->tx.id_cons++ ) & + ( netvsc->tx.count - 1 ) ] = tx_id; + + /* Hand back to RNDIS */ + rndis_tx_complete ( rndis, iobuf ); + return 0; + } + + /* Otherwise determine completion handler */ + if ( xrid == NETVSC_INIT_XRID ) { + completion = netvsc_initialised; + } else if ( xrid == NETVSC_RX_ESTABLISH_XRID ) { + completion = netvsc_rx_established_buffer; + } else if ( ( netvsc->wait_xrid != 0 ) && + ( xrid == netvsc->wait_xrid ) ) { + completion = netvsc_completed; + } else { + DBGC ( netvsc, "NETVSC %s received unexpected completion " + "(%08llx)\n", netvsc->name, xid ); + return -EPIPE; + } + + /* Hand off to completion handler */ + rc = completion ( netvsc, data, len ); + + /* Record completion handler result if applicable */ + if ( xrid == netvsc->wait_xrid ) { + netvsc->wait_xrid = 0; + netvsc->wait_rc = rc; + } + + return rc; +} + +/** + * Handle received cancellation packet + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @ret rc Return status code + */ +static int netvsc_recv_cancellation ( struct vmbus_device *vmdev, + uint64_t xid ) { + struct rndis_device *rndis = vmbus_get_drvdata ( vmdev ); + struct netvsc_device *netvsc = rndis->priv; + + DBGC ( netvsc, "NETVSC %s received unsupported cancellation packet " + "(%08llx):\n", netvsc->name, xid ); + return -ENOTSUP; +} + +/** VMBus channel operations */ +static struct vmbus_channel_operations netvsc_channel_operations = { + .recv_control = netvsc_recv_control, + .recv_data = netvsc_recv_data, + .recv_completion = netvsc_recv_completion, + .recv_cancellation = netvsc_recv_cancellation, +}; + +/** + * Poll for completed and received packets + * + * @v rndis RNDIS device + */ +static void netvsc_poll ( struct rndis_device *rndis ) { + struct netvsc_device *netvsc = rndis->priv; + struct vmbus_device *vmdev = netvsc->vmdev; + + /* Poll VMBus device */ + while ( vmbus_has_data ( vmdev ) ) + vmbus_poll ( vmdev ); +} + +/** + * Transmit packet + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + * @ret rc Return status code + * + * If this method returns success then the RNDIS device must + * eventually report completion via rndis_tx_complete(). + */ +static int netvsc_transmit ( struct rndis_device *rndis, + struct io_buffer *iobuf ) { + struct netvsc_device *netvsc = rndis->priv; + struct rndis_header *header = iobuf->data; + struct netvsc_rndis_message msg; + unsigned int tx_id; + unsigned int xrid; + uint64_t xid; + int rc; + + /* Sanity check */ + assert ( iob_len ( iobuf ) >= sizeof ( *header ) ); + assert ( iob_len ( iobuf ) == le32_to_cpu ( header->len ) ); + + /* Check that we have space in the transmit ring */ + if ( netvsc_ring_is_full ( &netvsc->tx ) ) + return rndis_tx_defer ( rndis, iobuf ); + + /* Allocate buffer ID and calculate transaction ID */ + tx_id = netvsc->tx.ids[ netvsc->tx.id_prod & ( netvsc->tx.count - 1 ) ]; + assert ( netvsc->tx.iobufs[tx_id] == NULL ); + xrid = ( NETVSC_TX_BASE_XRID + tx_id ); + xid = ( NETVSC_BASE_XID + xrid ); + + /* Construct message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.header.type = cpu_to_le32 ( NETVSC_RNDIS_MSG ); + msg.channel = ( ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) ? + NETVSC_RNDIS_DATA : NETVSC_RNDIS_CONTROL ); + msg.buffer = cpu_to_le32 ( NETVSC_RNDIS_NO_BUFFER ); + + /* Send message */ + if ( ( rc = vmbus_send_data ( netvsc->vmdev, xid, &msg, sizeof ( msg ), + iobuf ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not send RNDIS message: %s\n", + netvsc->name, strerror ( rc ) ); + return rc; + } + + /* Store I/O buffer and consume buffer ID */ + netvsc->tx.iobufs[tx_id] = iobuf; + netvsc->tx.id_prod++; + + return 0; +} + +/** + * Cancel transmission + * + * @v netvsc NetVSC device + * @v iobuf I/O buffer + * @v tx_id Transmission ID + */ +static void netvsc_cancel_transmit ( struct netvsc_device *netvsc, + struct io_buffer *iobuf, + unsigned int tx_id ) { + unsigned int xrid; + uint64_t xid; + + /* Send cancellation */ + xrid = ( NETVSC_TX_BASE_XRID + tx_id ); + xid = ( NETVSC_BASE_XID + xrid ); + DBGC ( netvsc, "NETVSC %s cancelling transmission %#x\n", + netvsc->name, tx_id ); + vmbus_send_cancellation ( netvsc->vmdev, xid ); + + /* Report back to RNDIS */ + rndis_tx_complete_err ( netvsc->rndis, iobuf, -ECANCELED ); +} + +/** + * Create descriptor ring + * + * @v netvsc NetVSC device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int netvsc_create_ring ( struct netvsc_device *netvsc __unused, + struct netvsc_ring *ring ) { + unsigned int i; + + /* Initialise buffer ID ring */ + for ( i = 0 ; i < ring->count ; i++ ) { + ring->ids[i] = i; + assert ( ring->iobufs[i] == NULL ); + } + ring->id_prod = 0; + ring->id_cons = 0; + + return 0; +} + +/** + * Destroy descriptor ring + * + * @v netvsc NetVSC device + * @v ring Descriptor ring + * @v discard Method used to discard outstanding buffer, or NULL + */ +static void netvsc_destroy_ring ( struct netvsc_device *netvsc, + struct netvsc_ring *ring, + void ( * discard ) ( struct netvsc_device *, + struct io_buffer *, + unsigned int ) ) { + struct io_buffer *iobuf; + unsigned int i; + + /* Flush any outstanding buffers */ + for ( i = 0 ; i < ring->count ; i++ ) { + iobuf = ring->iobufs[i]; + if ( ! iobuf ) + continue; + ring->iobufs[i] = NULL; + ring->ids[ ( ring->id_cons++ ) & ( ring->count - 1 ) ] = i; + if ( discard ) + discard ( netvsc, iobuf, i ); + } + + /* Sanity check */ + assert ( netvsc_ring_is_empty ( ring ) ); +} + +/** + * Copy data from data buffer + * + * @v pages Transfer page set + * @v data Data buffer + * @v offset Offset within page set + * @v len Length within page set + * @ret rc Return status code + */ +static int netvsc_buffer_copy ( struct vmbus_xfer_pages *pages, void *data, + size_t offset, size_t len ) { + struct netvsc_buffer *buffer = + container_of ( pages, struct netvsc_buffer, pages ); + + /* Sanity check */ + if ( ( offset > buffer->len ) || ( len > ( buffer->len - offset ) ) ) + return -ERANGE; + + /* Copy data from buffer */ + copy_from_user ( data, buffer->data, offset, len ); + + return 0; +} + +/** Transfer page set operations */ +static struct vmbus_xfer_pages_operations netvsc_xfer_pages_operations = { + .copy = netvsc_buffer_copy, +}; + +/** + * Create data buffer + * + * @v netvsc NetVSC device + * @v buffer Data buffer + * @ret rc Return status code + */ +static int netvsc_create_buffer ( struct netvsc_device *netvsc, + struct netvsc_buffer *buffer ) { + struct vmbus_device *vmdev = netvsc->vmdev; + int gpadl; + int rc; + + /* Allocate receive buffer */ + buffer->data = umalloc ( buffer->len ); + if ( ! buffer->data ) { + DBGC ( netvsc, "NETVSC %s could not allocate %zd-byte buffer\n", + netvsc->name, buffer->len ); + rc = -ENOMEM; + goto err_alloc; + } + + /* Establish GPA descriptor list */ + gpadl = vmbus_establish_gpadl ( vmdev, buffer->data, buffer->len ); + if ( gpadl < 0 ) { + rc = gpadl; + DBGC ( netvsc, "NETVSC %s could not establish GPADL: %s\n", + netvsc->name, strerror ( rc ) ); + goto err_establish_gpadl; + } + buffer->gpadl = gpadl; + + /* Register transfer page set */ + if ( ( rc = vmbus_register_pages ( vmdev, &buffer->pages ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not register transfer pages: " + "%s\n", netvsc->name, strerror ( rc ) ); + goto err_register_pages; + } + + return 0; + + vmbus_unregister_pages ( vmdev, &buffer->pages ); + err_register_pages: + vmbus_gpadl_teardown ( vmdev, gpadl ); + err_establish_gpadl: + ufree ( buffer->data ); + err_alloc: + return rc; +} + +/** + * Destroy data buffer + * + * @v netvsc NetVSC device + * @v buffer Data buffer + */ +static void netvsc_destroy_buffer ( struct netvsc_device *netvsc, + struct netvsc_buffer *buffer ) { + struct vmbus_device *vmdev = netvsc->vmdev; + int rc; + + /* Unregister transfer pages */ + vmbus_unregister_pages ( vmdev, &buffer->pages ); + + /* Tear down GPA descriptor list */ + if ( ( rc = vmbus_gpadl_teardown ( vmdev, buffer->gpadl ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not tear down GPADL: %s\n", + netvsc->name, strerror ( rc ) ); + /* Death is imminent. The host may well continue to + * write to the data buffer. The best we can do is + * leak memory for now and hope that the host doesn't + * write to this region after we load an OS. + */ + return; + } + + /* Free buffer */ + ufree ( buffer->data ); +} + +/** + * Open device + * + * @v rndis RNDIS device + * @ret rc Return status code + */ +static int netvsc_open ( struct rndis_device *rndis ) { + struct netvsc_device *netvsc = rndis->priv; + int rc; + + /* Initialise receive buffer */ + if ( ( rc = netvsc_create_buffer ( netvsc, &netvsc->rx ) ) != 0 ) + goto err_create_rx; + + /* Open channel */ + if ( ( rc = vmbus_open ( netvsc->vmdev, &netvsc_channel_operations, + PAGE_SIZE, PAGE_SIZE, NETVSC_MTU ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not open VMBus: %s\n", + netvsc->name, strerror ( rc ) ); + goto err_vmbus_open; + } + + /* Initialise communication with NetVSP */ + if ( ( rc = netvsc_initialise ( netvsc ) ) != 0 ) + goto err_initialise; + if ( ( rc = netvsc_ndis_version ( netvsc ) ) != 0 ) + goto err_ndis_version; + + /* Initialise transmit ring */ + if ( ( rc = netvsc_create_ring ( netvsc, &netvsc->tx ) ) != 0 ) + goto err_create_tx; + + /* Establish receive buffer */ + if ( ( rc = netvsc_establish_buffer ( netvsc, &netvsc->rx ) ) != 0 ) + goto err_establish_rx; + + return 0; + + netvsc_revoke_buffer ( netvsc, &netvsc->rx ); + err_establish_rx: + netvsc_destroy_ring ( netvsc, &netvsc->tx, NULL ); + err_create_tx: + err_ndis_version: + err_initialise: + vmbus_close ( netvsc->vmdev ); + err_vmbus_open: + netvsc_destroy_buffer ( netvsc, &netvsc->rx ); + err_create_rx: + return rc; +} + +/** + * Close device + * + * @v rndis RNDIS device + */ +static void netvsc_close ( struct rndis_device *rndis ) { + struct netvsc_device *netvsc = rndis->priv; + + /* Revoke receive buffer */ + netvsc_revoke_buffer ( netvsc, &netvsc->rx ); + + /* Destroy transmit ring */ + netvsc_destroy_ring ( netvsc, &netvsc->tx, netvsc_cancel_transmit ); + + /* Close channel */ + vmbus_close ( netvsc->vmdev ); + + /* Destroy receive buffer */ + netvsc_destroy_buffer ( netvsc, &netvsc->rx ); +} + +/** RNDIS operations */ +static struct rndis_operations netvsc_operations = { + .open = netvsc_open, + .close = netvsc_close, + .transmit = netvsc_transmit, + .poll = netvsc_poll, +}; + +/** + * Probe device + * + * @v vmdev VMBus device + * @ret rc Return status code + */ +static int netvsc_probe ( struct vmbus_device *vmdev ) { + struct netvsc_device *netvsc; + struct rndis_device *rndis; + int rc; + + /* Allocate and initialise structure */ + rndis = alloc_rndis ( sizeof ( *netvsc ) ); + if ( ! rndis ) { + rc = -ENOMEM; + goto err_alloc; + } + rndis_init ( rndis, &netvsc_operations ); + rndis->netdev->dev = &vmdev->dev; + netvsc = rndis->priv; + netvsc->vmdev = vmdev; + netvsc->rndis = rndis; + netvsc->name = vmdev->dev.name; + netvsc_init_ring ( &netvsc->tx, NETVSC_TX_NUM_DESC, + netvsc->tx_iobufs, netvsc->tx_ids ); + netvsc_init_buffer ( &netvsc->rx, NETVSC_RX_BUF_PAGESET, + &netvsc_xfer_pages_operations, + NETVSC_RX_ESTABLISH_MSG, NETVSC_RX_ESTABLISH_XRID, + NETVSC_RX_REVOKE_MSG, NETVSC_RX_REVOKE_XRID, + NETVSC_RX_BUF_LEN ); + vmbus_set_drvdata ( vmdev, rndis ); + + /* Register RNDIS device */ + if ( ( rc = register_rndis ( rndis ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not register: %s\n", + netvsc->name, strerror ( rc ) ); + goto err_register; + } + + return 0; + + unregister_rndis ( rndis ); + err_register: + free_rndis ( rndis ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v vmdev VMBus device + */ +static void netvsc_remove ( struct vmbus_device *vmdev ) { + struct rndis_device *rndis = vmbus_get_drvdata ( vmdev ); + + /* Unregister RNDIS device */ + unregister_rndis ( rndis ); + + /* Free RNDIS device */ + free_rndis ( rndis ); +} + +/** NetVSC driver */ +struct vmbus_driver netvsc_driver __vmbus_driver = { + .name = "netvsc", + .type = VMBUS_TYPE ( 0xf8615163, 0xdf3e, 0x46c5, 0x913f, + 0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e ), + .probe = netvsc_probe, + .remove = netvsc_remove, +}; diff --git a/qemu/roms/ipxe/src/drivers/net/netvsc.h b/qemu/roms/ipxe/src/drivers/net/netvsc.h new file mode 100644 index 000000000..39eeb891c --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/netvsc.h @@ -0,0 +1,365 @@ +#ifndef _NETVSC_H +#define _NETVSC_H + +/** @file + * + * Hyper-V network virtual service client + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** Maximum supported NetVSC message length */ +#define NETVSC_MTU 512 + +/** Maximum time to wait for a transaction to complete + * + * This is a policy decision. + */ +#define NETVSC_MAX_WAIT_MS 1000 + +/** Number of transmit ring entries + * + * Must be a power of two. This is a policy decision. This value + * must be sufficiently small to guarantee that we never run out of + * space in the VMBus outbound ring buffer. + */ +#define NETVSC_TX_NUM_DESC 32 + +/** RX data buffer page set ID + * + * This is a policy decision. + */ +#define NETVSC_RX_BUF_PAGESET 0xbead + +/** RX data buffer length + * + * This is a policy decision. + */ +#define NETVSC_RX_BUF_LEN ( 16 * PAGE_SIZE ) + +/** Base transaction ID + * + * This is a policy decision. + */ +#define NETVSC_BASE_XID 0x18ae0000UL + +/** Relative transaction IDs */ +enum netvsc_xrid { + /** Transmit descriptors (one per transmit buffer ID) */ + NETVSC_TX_BASE_XRID = 0, + /** Initialisation */ + NETVSC_INIT_XRID = ( NETVSC_TX_BASE_XRID + NETVSC_TX_NUM_DESC ), + /** NDIS version */ + NETVSC_NDIS_VERSION_XRID, + /** Establish receive buffer */ + NETVSC_RX_ESTABLISH_XRID, + /** Revoke receive buffer */ + NETVSC_RX_REVOKE_XRID, +}; + +/** NetVSC status codes */ +enum netvsc_status { + NETVSC_NONE = 0, + NETVSC_OK = 1, + NETVSC_FAIL = 2, + NETVSC_TOO_NEW = 3, + NETVSC_TOO_OLD = 4, + NETVSC_BAD_PACKET = 5, + NETVSC_BUSY = 6, + NETVSC_UNSUPPORTED = 7, +}; + +/** NetVSC message header */ +struct netvsc_header { + /** Type */ + uint32_t type; +} __attribute__ (( packed )); + +/** NetVSC initialisation message */ +#define NETVSC_INIT_MSG 1 + +/** NetVSC initialisation message */ +struct netvsc_init_message { + /** Message header */ + struct netvsc_header header; + /** Minimum supported protocol version */ + uint32_t min; + /** Maximum supported protocol version */ + uint32_t max; + /** Reserved */ + uint8_t reserved[20]; +} __attribute__ (( packed )); + +/** Oldest known NetVSC protocol version */ +#define NETVSC_VERSION_1 2 /* sic */ + +/** NetVSC initialisation completion */ +#define NETVSC_INIT_CMPLT 2 + +/** NetVSC initialisation completion */ +struct netvsc_init_completion { + /** Message header */ + struct netvsc_header header; + /** Protocol version */ + uint32_t version; + /** Maximum memory descriptor list length */ + uint32_t max_mdl_len; + /** Status */ + uint32_t status; + /** Reserved */ + uint8_t reserved[16]; +} __attribute__ (( packed )); + +/** NetVSC NDIS version message */ +#define NETVSC_NDIS_VERSION_MSG 100 + +/** NetVSC NDIS version message */ +struct netvsc_ndis_version_message { + /** Message header */ + struct netvsc_header header; + /** Major version */ + uint32_t major; + /** Minor version */ + uint32_t minor; + /** Reserved */ + uint8_t reserved[20]; +} __attribute__ (( packed )); + +/** NetVSC NDIS major version */ +#define NETVSC_NDIS_MAJOR 6 + +/** NetVSC NDIS minor version */ +#define NETVSC_NDIS_MINOR 1 + +/** NetVSC establish receive data buffer message */ +#define NETVSC_RX_ESTABLISH_MSG 101 + +/** NetVSC establish receive data buffer completion */ +#define NETVSC_RX_ESTABLISH_CMPLT 102 + +/** NetVSC revoke receive data buffer message */ +#define NETVSC_RX_REVOKE_MSG 103 + +/** NetVSC establish transmit data buffer message */ +#define NETVSC_TX_ESTABLISH_MSG 104 + +/** NetVSC establish transmit data buffer completion */ +#define NETVSC_TX_ESTABLISH_CMPLT 105 + +/** NetVSC revoke transmit data buffer message */ +#define NETVSC_TX_REVOKE_MSG 106 + +/** NetVSC establish data buffer message */ +struct netvsc_establish_buffer_message { + /** Message header */ + struct netvsc_header header; + /** GPADL ID */ + uint32_t gpadl; + /** Page set ID */ + uint16_t pageset; + /** Reserved */ + uint8_t reserved[22]; +} __attribute__ (( packed )); + +/** NetVSC receive data buffer section */ +struct netvsc_rx_buffer_section { + /** Starting offset */ + uint32_t start; + /** Subsection length */ + uint32_t len; + /** Number of subsections */ + uint32_t count; + /** Ending offset */ + uint32_t end; +} __attribute__ (( packed )); + +/** NetVSC establish receive data buffer completion */ +struct netvsc_rx_establish_buffer_completion { + /** Message header */ + struct netvsc_header header; + /** Status */ + uint32_t status; + /** Number of sections (must be 1) */ + uint32_t count; + /** Section descriptors */ + struct netvsc_rx_buffer_section section[1]; +} __attribute__ (( packed )); + +/** NetVSC establish transmit data buffer completion */ +struct netvsc_tx_establish_buffer_completion { + /** Message header */ + struct netvsc_header header; + /** Status */ + uint32_t status; + /** Section length */ + uint32_t len; +} __attribute__ (( packed )); + +/** NetVSC revoke data buffer message */ +struct netvsc_revoke_buffer_message { + /** Message header */ + struct netvsc_header header; + /** Page set ID */ + uint16_t pageset; + /** Reserved */ + uint8_t reserved[26]; +} __attribute__ (( packed )); + +/** NetVSC RNDIS message */ +#define NETVSC_RNDIS_MSG 107 + +/** NetVSC RNDIS message */ +struct netvsc_rndis_message { + /** Message header */ + struct netvsc_header header; + /** RNDIS channel */ + uint32_t channel; + /** Buffer index (or NETVSC_RNDIS_NO_BUFFER) */ + uint32_t buffer; + /** Buffer length */ + uint32_t len; + /** Reserved */ + uint8_t reserved[16]; +} __attribute__ (( packed )); + +/** RNDIS data channel (for RNDIS_PACKET_MSG only) */ +#define NETVSC_RNDIS_DATA 0 + +/** RNDIS control channel (for all other RNDIS messages) */ +#define NETVSC_RNDIS_CONTROL 1 + +/** "No buffer used" index */ +#define NETVSC_RNDIS_NO_BUFFER 0xffffffffUL + +/** A NetVSC descriptor ring */ +struct netvsc_ring { + /** Number of descriptors */ + unsigned int count; + /** I/O buffers, indexed by buffer ID */ + struct io_buffer **iobufs; + /** Buffer ID ring */ + uint8_t *ids; + /** Buffer ID producer counter */ + unsigned int id_prod; + /** Buffer ID consumer counter */ + unsigned int id_cons; +}; + +/** + * Initialise descriptor ring + * + * @v ring Descriptor ring + * @v count Maximum number of used descriptors + * @v iobufs I/O buffers + * @v ids Buffer IDs + */ +static inline __attribute__ (( always_inline )) void +netvsc_init_ring ( struct netvsc_ring *ring, unsigned int count, + struct io_buffer **iobufs, uint8_t *ids ) { + + ring->count = count; + ring->iobufs = iobufs; + ring->ids = ids; +} + +/** + * Check whether or not descriptor ring is full + * + * @v ring Descriptor ring + * @v is_full Ring is full + */ +static inline __attribute__ (( always_inline )) int +netvsc_ring_is_full ( struct netvsc_ring *ring ) { + unsigned int fill_level; + + fill_level = ( ring->id_prod - ring->id_cons ); + assert ( fill_level <= ring->count ); + return ( fill_level >= ring->count ); +} + +/** + * Check whether or not descriptor ring is empty + * + * @v ring Descriptor ring + * @v is_empty Ring is empty + */ +static inline __attribute__ (( always_inline )) int +netvsc_ring_is_empty ( struct netvsc_ring *ring ) { + + return ( ring->id_prod == ring->id_cons ); +} + +/** A NetVSC data buffer */ +struct netvsc_buffer { + /** Transfer page set */ + struct vmbus_xfer_pages pages; + /** Establish data buffer message type */ + uint8_t establish_type; + /** Establish data buffer relative transaction ID */ + uint8_t establish_xrid; + /** Revoke data buffer message type */ + uint8_t revoke_type; + /** Revoke data buffer relative transaction ID */ + uint8_t revoke_xrid; + /** Buffer length */ + size_t len; + /** Buffer */ + userptr_t data; + /** GPADL ID */ + unsigned int gpadl; +}; + +/** + * Initialise data buffer + * + * @v buffer Data buffer + * @v pageset Page set ID + * @v op Page set operations + * @v establish_type Establish data buffer message type + * @v establish_xrid Establish data buffer relative transaction ID + * @v revoke_type Revoke data buffer message type + * @v revoke_type Revoke data buffer relative transaction ID + * @v len Required length + */ +static inline __attribute__ (( always_inline )) void +netvsc_init_buffer ( struct netvsc_buffer *buffer, uint16_t pageset, + struct vmbus_xfer_pages_operations *op, + uint8_t establish_type, uint8_t establish_xrid, + uint8_t revoke_type, uint8_t revoke_xrid, size_t len ) { + + buffer->pages.pageset = cpu_to_le16 ( pageset ); + buffer->pages.op = op; + buffer->establish_type = establish_type; + buffer->establish_xrid = establish_xrid; + buffer->revoke_type = revoke_type; + buffer->revoke_xrid = revoke_xrid; + buffer->len = len; +} + +/** A NetVSC device */ +struct netvsc_device { + /** VMBus device */ + struct vmbus_device *vmdev; + /** RNDIS device */ + struct rndis_device *rndis; + /** Name */ + const char *name; + + /** Transmit ring */ + struct netvsc_ring tx; + /** Transmit buffer IDs */ + uint8_t tx_ids[NETVSC_TX_NUM_DESC]; + /** Transmit I/O buffers */ + struct io_buffer *tx_iobufs[NETVSC_TX_NUM_DESC]; + + /** Receive buffer */ + struct netvsc_buffer rx; + + /** Relative transaction ID for current blocking transaction */ + unsigned int wait_xrid; + /** Return status code for current blocking transaction */ + int wait_rc; +}; + +#endif /* _NETVSC_H */ diff --git a/qemu/roms/ipxe/src/drivers/net/phantom/nx_bitops.h b/qemu/roms/ipxe/src/drivers/net/phantom/nx_bitops.h index 15f3d3767..1687b6952 100644 --- a/qemu/roms/ipxe/src/drivers/net/phantom/nx_bitops.h +++ b/qemu/roms/ipxe/src/drivers/net/phantom/nx_bitops.h @@ -18,9 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/drivers/net/phantom/phantom.c b/qemu/roms/ipxe/src/drivers/net/phantom/phantom.c index e70ded08c..38b66743c 100644 --- a/qemu/roms/ipxe/src/drivers/net/phantom/phantom.c +++ b/qemu/roms/ipxe/src/drivers/net/phantom/phantom.c @@ -16,9 +16,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/net/phantom/phantom.h b/qemu/roms/ipxe/src/drivers/net/phantom/phantom.h index 1647168ba..967603409 100644 --- a/qemu/roms/ipxe/src/drivers/net/phantom/phantom.h +++ b/qemu/roms/ipxe/src/drivers/net/phantom/phantom.h @@ -19,9 +19,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/drivers/net/phantom/phantom_hw.h b/qemu/roms/ipxe/src/drivers/net/phantom/phantom_hw.h index 7dfff52b2..016730de3 100644 --- a/qemu/roms/ipxe/src/drivers/net/phantom/phantom_hw.h +++ b/qemu/roms/ipxe/src/drivers/net/phantom/phantom_hw.h @@ -19,9 +19,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/drivers/net/pnic.c b/qemu/roms/ipxe/src/drivers/net/pnic.c index 4170cc640..ca64299ea 100644 --- a/qemu/roms/ipxe/src/drivers/net/pnic.c +++ b/qemu/roms/ipxe/src/drivers/net/pnic.c @@ -6,8 +6,18 @@ Bochs Pseudo NIC driver for Etherboot /* * 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) any later version. + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. * * See pnic_api.h for an explanation of the Bochs Pseudo NIC. */ diff --git a/qemu/roms/ipxe/src/drivers/net/prism2.c b/qemu/roms/ipxe/src/drivers/net/prism2.c index ab974264c..4331f2cd0 100644 --- a/qemu/roms/ipxe/src/drivers/net/prism2.c +++ b/qemu/roms/ipxe/src/drivers/net/prism2.c @@ -9,8 +9,18 @@ $Id$ /* * 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) any later version. + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. */ FILE_LICENCE ( GPL2_OR_LATER ); diff --git a/qemu/roms/ipxe/src/drivers/net/prism2_pci.c b/qemu/roms/ipxe/src/drivers/net/prism2_pci.c index 72549babf..69ddf0fb0 100644 --- a/qemu/roms/ipxe/src/drivers/net/prism2_pci.c +++ b/qemu/roms/ipxe/src/drivers/net/prism2_pci.c @@ -10,8 +10,18 @@ $Id$ /* * 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) any later version. + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. */ FILE_LICENCE ( GPL2_OR_LATER ); @@ -40,8 +50,6 @@ static void prism2_pci_disable ( struct nic *nic ) { static struct pci_device_id prism2_pci_nics[] = { PCI_ROM(0x1260, 0x3873, "prism2_pci", "Harris Semiconductor Prism2.5 clone", 0), -PCI_ROM(0x1260, 0x3873, "hwp01170", "ActionTec HWP01170", 0), -PCI_ROM(0x1260, 0x3873, "dwl520", "DLink DWL-520", 0), }; PCI_DRIVER ( prism2_pci_driver, prism2_pci_nics, PCI_NO_CLASS ); diff --git a/qemu/roms/ipxe/src/drivers/net/prism2_plx.c b/qemu/roms/ipxe/src/drivers/net/prism2_plx.c index 2098f7f09..a73b0e087 100644 --- a/qemu/roms/ipxe/src/drivers/net/prism2_plx.c +++ b/qemu/roms/ipxe/src/drivers/net/prism2_plx.c @@ -10,8 +10,18 @@ $Id$ /* * 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) any later version. + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. */ FILE_LICENCE ( GPL2_OR_LATER ); @@ -44,10 +54,10 @@ static int prism2_find_plx ( hfa384x_t *hw, struct pci_device *p ) /* Obtain all memory and IO base addresses */ pci_read_config_dword( p, PLX_LOCAL_CONFIG_REGISTER_BASE, &plx_lcr); - plx_lcr &= PCI_BASE_ADDRESS_IO_MASK; + plx_lcr &= ~PCI_BASE_ADDRESS_IO_MASK; pci_read_config_dword( p, PRISM2_PLX_ATTR_MEM_BASE, &attr_mem); pci_read_config_dword( p, PRISM2_PLX_IO_BASE, &iobase); - iobase &= PCI_BASE_ADDRESS_IO_MASK; + iobase &= ~PCI_BASE_ADDRESS_IO_MASK; /* Fill out hw structure */ hw->iobase = iobase; diff --git a/qemu/roms/ipxe/src/drivers/net/realtek.c b/qemu/roms/ipxe/src/drivers/net/realtek.c index 0aca8c77f..022b59324 100644 --- a/qemu/roms/ipxe/src/drivers/net/realtek.c +++ b/qemu/roms/ipxe/src/drivers/net/realtek.c @@ -17,9 +17,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -194,7 +198,6 @@ static int realtek_init_eeprom ( struct net_device *netdev ) { DBGC ( rtl, "REALTEK %p EEPROM is a 93C46\n", rtl ); init_at93c46 ( &rtl->eeprom, 16 ); } - rtl->eeprom.bus = &rtl->spibit.bus; /* Check for EEPROM presence. Some onboard NICs will have no * EEPROM connected, with the BIOS being responsible for @@ -1085,6 +1088,7 @@ static void realtek_detect ( struct realtek_nic *rtl ) { rtl ); rtl->legacy = 1; } + rtl->eeprom.bus = &rtl->spibit.bus; } } @@ -1132,7 +1136,8 @@ static int realtek_probe ( struct pci_device *pci ) { realtek_detect ( rtl ); /* Initialise EEPROM */ - if ( ( rc = realtek_init_eeprom ( netdev ) ) == 0 ) { + if ( rtl->eeprom.bus && + ( ( rc = realtek_init_eeprom ( netdev ) ) == 0 ) ) { /* Read MAC address from EEPROM */ if ( ( rc = nvs_read ( &rtl->eeprom.nvs, RTL_EEPROM_MAC, diff --git a/qemu/roms/ipxe/src/drivers/net/realtek.h b/qemu/roms/ipxe/src/drivers/net/realtek.h index ac33405e8..b1ce7f98f 100644 --- a/qemu/roms/ipxe/src/drivers/net/realtek.h +++ b/qemu/roms/ipxe/src/drivers/net/realtek.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8180.c b/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8180.c index 8851d1bfb..5f97480fa 100644 --- a/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8180.c +++ b/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8180.c @@ -3,15 +3,23 @@ FILE_LICENCE(GPL2_OR_LATER); #include +#include "rtl818x.h" -REQUIRE_OBJECT(rtl818x); -REQUIRE_OBJECT(rtl8180_grf5101); -REQUIRE_OBJECT(rtl8180_max2820); -REQUIRE_OBJECT(rtl8180_sa2400); - -static struct pci_device_id rtl8180_nics[] __unused = { +static struct pci_device_id rtl8180_nics[] = { PCI_ROM(0x10ec, 0x8180, "rtl8180", "Realtek 8180", 0), PCI_ROM(0x1799, 0x6001, "f5d6001", "Belkin F5D6001", 0), PCI_ROM(0x1799, 0x6020, "f5d6020", "Belkin F5D6020", 0), PCI_ROM(0x1186, 0x3300, "dwl510", "D-Link DWL-510", 0), }; + +struct pci_driver rtl8180_driver __pci_driver = { + .ids = rtl8180_nics, + .id_count = sizeof(rtl8180_nics) / sizeof(rtl8180_nics[0]), + .probe = rtl818x_probe, + .remove = rtl818x_remove, +}; + +REQUIRING_SYMBOL(rtl8180_driver); +REQUIRE_OBJECT(rtl8180_grf5101); +REQUIRE_OBJECT(rtl8180_max2820); +REQUIRE_OBJECT(rtl8180_sa2400); diff --git a/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8185.c b/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8185.c index fd27e5c8c..234978cea 100644 --- a/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8185.c +++ b/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8185.c @@ -3,12 +3,20 @@ FILE_LICENCE(GPL2_OR_LATER); #include - -REQUIRE_OBJECT(rtl818x); -REQUIRE_OBJECT(rtl8185_rtl8225); +#include "rtl818x.h" static struct pci_device_id rtl8185_nics[] __unused = { PCI_ROM(0x10ec, 0x8185, "rtl8185", "Realtek 8185", 0), PCI_ROM(0x1799, 0x700f, "f5d7000", "Belkin F5D7000", 0), PCI_ROM(0x1799, 0x701f, "f5d7010", "Belkin F5D7010", 0), }; + +struct pci_driver rtl8185_driver __pci_driver = { + .ids = rtl8185_nics, + .id_count = sizeof(rtl8185_nics) / sizeof(rtl8185_nics[0]), + .probe = rtl818x_probe, + .remove = rtl818x_remove, +}; + +REQUIRING_SYMBOL(rtl8185_driver); +REQUIRE_OBJECT(rtl8185_rtl8225); diff --git a/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.c b/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.c index cf4c7556f..8b3c206d4 100644 --- a/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.c +++ b/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.c @@ -649,7 +649,7 @@ struct net80211_device_operations rtl818x_operations = { .config = rtl818x_config, }; -static int rtl818x_probe(struct pci_device *pdev ) +int rtl818x_probe(struct pci_device *pdev ) { struct net80211_device *dev; struct rtl818x_priv *priv; @@ -820,7 +820,7 @@ static int rtl818x_probe(struct pci_device *pdev ) return err; } -static void rtl818x_remove(struct pci_device *pdev) +void rtl818x_remove(struct pci_device *pdev) { struct net80211_device *dev = pci_get_drvdata(pdev); @@ -830,25 +830,3 @@ static void rtl818x_remove(struct pci_device *pdev) net80211_unregister(dev); net80211_free(dev); } - -/* Hide PCI_ROM definitions in here from parserom.pl; the definitions - that should be used are in rtl8180.c and rtl8185.c. */ -#define RTL_ROM PCI_ROM - -static struct pci_device_id rtl818x_nics[] = { - RTL_ROM(0x10ec, 0x8185, "rtl8185", "Realtek 8185", 0), - RTL_ROM(0x1799, 0x700f, "f5d7000", "Belkin F5D7000", 0), - RTL_ROM(0x1799, 0x701f, "f5d7010", "Belkin F5D7010", 0), - - RTL_ROM(0x10ec, 0x8180, "rtl8180", "Realtek 8180", 0), - RTL_ROM(0x1799, 0x6001, "f5d6001", "Belkin F5D6001", 0), - RTL_ROM(0x1799, 0x6020, "f5d6020", "Belkin F5D6020", 0), - RTL_ROM(0x1186, 0x3300, "dwl510", "D-Link DWL-510", 0), -}; - -struct pci_driver rtl818x_driver __pci_driver = { - .ids = rtl818x_nics, - .id_count = sizeof(rtl818x_nics) / sizeof(rtl818x_nics[0]), - .probe = rtl818x_probe, - .remove = rtl818x_remove, -}; diff --git a/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.h b/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.h index 4e57d0bd3..ae4b8a96f 100644 --- a/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.h +++ b/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.h @@ -19,6 +19,7 @@ #include #include +#include FILE_LICENCE(GPL2_ONLY); @@ -356,4 +357,7 @@ struct rtl818x_rf_ops { void (*conf_erp)(struct net80211_device *dev); /* set based on dev->erp_flags */ }; +extern int rtl818x_probe(struct pci_device *pdev ); +extern void rtl818x_remove(struct pci_device *pdev); + #endif /* RTL818X_H */ diff --git a/qemu/roms/ipxe/src/drivers/net/skeleton.c b/qemu/roms/ipxe/src/drivers/net/skeleton.c index 365111b8d..0435b9d0e 100644 --- a/qemu/roms/ipxe/src/drivers/net/skeleton.c +++ b/qemu/roms/ipxe/src/drivers/net/skeleton.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/net/skeleton.h b/qemu/roms/ipxe/src/drivers/net/skeleton.h index 3de2afa5b..2ab01bd56 100644 --- a/qemu/roms/ipxe/src/drivers/net/skeleton.h +++ b/qemu/roms/ipxe/src/drivers/net/skeleton.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Skeleton BAR size */ #define SKELETON_BAR_SIZE 256 diff --git a/qemu/roms/ipxe/src/drivers/net/smsc75xx.c b/qemu/roms/ipxe/src/drivers/net/smsc75xx.c new file mode 100644 index 000000000..017e02a59 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/smsc75xx.c @@ -0,0 +1,1057 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include "smsc75xx.h" + +/** @file + * + * SMSC LAN75xx USB Ethernet driver + * + */ + +/** Interrupt completion profiler */ +static struct profiler smsc75xx_intr_profiler __profiler = + { .name = "smsc75xx.intr" }; + +/** Bulk IN completion profiler */ +static struct profiler smsc75xx_in_profiler __profiler = + { .name = "smsc75xx.in" }; + +/** Bulk OUT profiler */ +static struct profiler smsc75xx_out_profiler __profiler = + { .name = "smsc75xx.out" }; + +/****************************************************************************** + * + * Register access + * + ****************************************************************************** + */ + +/** + * Write register (without byte-swapping) + * + * @v smsc75xx SMSC75xx device + * @v address Register address + * @v value Register value + * @ret rc Return status code + */ +static int smsc75xx_raw_writel ( struct smsc75xx_device *smsc75xx, + unsigned int address, uint32_t value ) { + int rc; + + /* Write register */ + if ( ( rc = usb_control ( smsc75xx->usb, SMSC75XX_REGISTER_WRITE, 0, + address, &value, sizeof ( value ) ) ) != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p could not write %03x: %s\n", + smsc75xx, address, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Write register + * + * @v smsc75xx SMSC75xx device + * @v address Register address + * @v value Register value + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +smsc75xx_writel ( struct smsc75xx_device *smsc75xx, unsigned int address, + uint32_t value ) { + int rc; + + /* Write register */ + if ( ( rc = smsc75xx_raw_writel ( smsc75xx, address, + cpu_to_le32 ( value ) ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Read register (without byte-swapping) + * + * @v smsc75xx SMSC75xx device + * @v address Register address + * @ret value Register value + * @ret rc Return status code + */ +static int smsc75xx_raw_readl ( struct smsc75xx_device *smsc75xx, + unsigned int address, uint32_t *value ) { + int rc; + + /* Read register */ + if ( ( rc = usb_control ( smsc75xx->usb, SMSC75XX_REGISTER_READ, 0, + address, value, sizeof ( *value ) ) ) != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p could not read %03x: %s\n", + smsc75xx, address, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Read register + * + * @v smsc75xx SMSC75xx device + * @v address Register address + * @ret value Register value + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +smsc75xx_readl ( struct smsc75xx_device *smsc75xx, unsigned int address, + uint32_t *value ) { + int rc; + + /* Read register */ + if ( ( rc = smsc75xx_raw_readl ( smsc75xx, address, value ) ) != 0 ) + return rc; + le32_to_cpus ( value ); + + return 0; +} + +/****************************************************************************** + * + * EEPROM access + * + ****************************************************************************** + */ + +/** + * Wait for EEPROM to become idle + * + * @v smsc75xx SMSC75xx device + * @ret rc Return status code + */ +static int smsc75xx_eeprom_wait ( struct smsc75xx_device *smsc75xx ) { + uint32_t e2p_cmd; + unsigned int i; + int rc; + + /* Wait for EPC_BSY to become clear */ + for ( i = 0 ; i < SMSC75XX_EEPROM_MAX_WAIT_MS ; i++ ) { + + /* Read E2P_CMD and check EPC_BSY */ + if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_E2P_CMD, + &e2p_cmd ) ) != 0 ) + return rc; + if ( ! ( e2p_cmd & SMSC75XX_E2P_CMD_EPC_BSY ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( smsc75xx, "SMSC75XX %p timed out waiting for EEPROM\n", + smsc75xx ); + return -ETIMEDOUT; +} + +/** + * Read byte from EEPROM + * + * @v smsc75xx SMSC75xx device + * @v address EEPROM address + * @ret byte Byte read, or negative error + */ +static int smsc75xx_eeprom_read_byte ( struct smsc75xx_device *smsc75xx, + unsigned int address ) { + uint32_t e2p_cmd; + uint32_t e2p_data; + int rc; + + /* Wait for EEPROM to become idle */ + if ( ( rc = smsc75xx_eeprom_wait ( smsc75xx ) ) != 0 ) + return rc; + + /* Initiate read command */ + e2p_cmd = ( SMSC75XX_E2P_CMD_EPC_BSY | SMSC75XX_E2P_CMD_EPC_CMD_READ | + SMSC75XX_E2P_CMD_EPC_ADDR ( address ) ); + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_E2P_CMD, + e2p_cmd ) ) != 0 ) + return rc; + + /* Wait for command to complete */ + if ( ( rc = smsc75xx_eeprom_wait ( smsc75xx ) ) != 0 ) + return rc; + + /* Read EEPROM data */ + if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_E2P_DATA, + &e2p_data ) ) != 0 ) + return rc; + + return SMSC75XX_E2P_DATA_GET ( e2p_data ); +} + +/** + * Read data from EEPROM + * + * @v smsc75xx SMSC75xx device + * @v address EEPROM address + * @v data Data buffer + * @v len Length of data + * @ret rc Return status code + */ +static int smsc75xx_eeprom_read ( struct smsc75xx_device *smsc75xx, + unsigned int address, void *data, + size_t len ) { + uint8_t *bytes; + int byte; + + /* Read bytes */ + for ( bytes = data ; len-- ; address++, bytes++ ) { + byte = smsc75xx_eeprom_read_byte ( smsc75xx, address ); + if ( byte < 0 ) + return byte; + *bytes = byte; + } + + return 0; +} + +/****************************************************************************** + * + * MII access + * + ****************************************************************************** + */ + +/** + * Wait for MII to become idle + * + * @v smsc75xx SMSC75xx device + * @ret rc Return status code + */ +static int smsc75xx_mii_wait ( struct smsc75xx_device *smsc75xx ) { + uint32_t mii_access; + unsigned int i; + int rc; + + /* Wait for MIIBZY to become clear */ + for ( i = 0 ; i < SMSC75XX_MII_MAX_WAIT_MS ; i++ ) { + + /* Read MII_ACCESS and check MIIBZY */ + if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_MII_ACCESS, + &mii_access ) ) != 0 ) + return rc; + if ( ! ( mii_access & SMSC75XX_MII_ACCESS_MIIBZY ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( smsc75xx, "SMSC75XX %p timed out waiting for MII\n", + smsc75xx ); + return -ETIMEDOUT; +} + +/** + * Read from MII register + * + * @v mii MII interface + * @v reg Register address + * @ret value Data read, or negative error + */ +static int smsc75xx_mii_read ( struct mii_interface *mii, unsigned int reg ) { + struct smsc75xx_device *smsc75xx = + container_of ( mii, struct smsc75xx_device, mii ); + uint32_t mii_access; + uint32_t mii_data; + int rc; + + /* Wait for MII to become idle */ + if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 ) + return rc; + + /* Initiate read command */ + mii_access = ( SMSC75XX_MII_ACCESS_PHY_ADDRESS | + SMSC75XX_MII_ACCESS_MIIRINDA ( reg ) | + SMSC75XX_MII_ACCESS_MIIBZY ); + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MII_ACCESS, + mii_access ) ) != 0 ) + return rc; + + /* Wait for command to complete */ + if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 ) + return rc; + + /* Read MII data */ + if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_MII_DATA, + &mii_data ) ) != 0 ) + return rc; + + return SMSC75XX_MII_DATA_GET ( mii_data ); +} + +/** + * Write to MII register + * + * @v mii MII interface + * @v reg Register address + * @v data Data to write + * @ret rc Return status code + */ +static int smsc75xx_mii_write ( struct mii_interface *mii, unsigned int reg, + unsigned int data ) { + struct smsc75xx_device *smsc75xx = + container_of ( mii, struct smsc75xx_device, mii ); + uint32_t mii_access; + uint32_t mii_data; + int rc; + + /* Wait for MII to become idle */ + if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 ) + return rc; + + /* Write MII data */ + mii_data = SMSC75XX_MII_DATA_SET ( data ); + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MII_DATA, + mii_data ) ) != 0 ) + return rc; + + /* Initiate write command */ + mii_access = ( SMSC75XX_MII_ACCESS_PHY_ADDRESS | + SMSC75XX_MII_ACCESS_MIIRINDA ( reg ) | + SMSC75XX_MII_ACCESS_MIIWNR | + SMSC75XX_MII_ACCESS_MIIBZY ); + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MII_ACCESS, + mii_access ) ) != 0 ) + return rc; + + /* Wait for command to complete */ + if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 ) + return rc; + + return 0; +} + +/** MII operations */ +static struct mii_operations smsc75xx_mii_operations = { + .read = smsc75xx_mii_read, + .write = smsc75xx_mii_write, +}; + +/** + * Check link status + * + * @v smsc75xx SMSC75xx device + * @ret rc Return status code + */ +static int smsc75xx_check_link ( struct smsc75xx_device *smsc75xx ) { + struct net_device *netdev = smsc75xx->netdev; + int intr; + int rc; + + /* Read PHY interrupt source */ + intr = mii_read ( &smsc75xx->mii, SMSC75XX_MII_PHY_INTR_SOURCE ); + if ( intr < 0 ) { + rc = intr; + DBGC ( smsc75xx, "SMSC75XX %p could not get PHY interrupt " + "source: %s\n", smsc75xx, strerror ( rc ) ); + return rc; + } + + /* Acknowledge PHY interrupt */ + if ( ( rc = mii_write ( &smsc75xx->mii, SMSC75XX_MII_PHY_INTR_SOURCE, + intr ) ) != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p could not acknowledge PHY " + "interrupt: %s\n", smsc75xx, strerror ( rc ) ); + return rc; + } + + /* Check link status */ + if ( ( rc = mii_check_link ( &smsc75xx->mii, netdev ) ) != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p could not check link: %s\n", + smsc75xx, strerror ( rc ) ); + return rc; + } + + DBGC ( smsc75xx, "SMSC75XX %p link %s (intr %#04x)\n", + smsc75xx, ( netdev_link_ok ( netdev ) ? "up" : "down" ), intr ); + return 0; +} + +/****************************************************************************** + * + * Statistics (for debugging) + * + ****************************************************************************** + */ + +/** + * Get statistics + * + * @v smsc75xx SMSC75xx device + * @v stats Statistics to fill in + * @ret rc Return status code + */ +static int smsc75xx_get_statistics ( struct smsc75xx_device *smsc75xx, + struct smsc75xx_statistics *stats ) { + int rc; + + /* Get statistics */ + if ( ( rc = usb_control ( smsc75xx->usb, SMSC75XX_GET_STATISTICS, 0, 0, + stats, sizeof ( *stats ) ) ) != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p could not get statistics: %s\n", + smsc75xx, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Dump statistics (for debugging) + * + * @v smsc75xx SMSC75xx device + * @ret rc Return status code + */ +static int smsc75xx_dump_statistics ( struct smsc75xx_device *smsc75xx ) { + struct smsc75xx_statistics stats; + int rc; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return 0; + + /* Get statistics */ + if ( ( rc = smsc75xx_get_statistics ( smsc75xx, &stats ) ) != 0 ) + return rc; + + /* Dump statistics */ + DBGC ( smsc75xx, "SMSC75XX %p RXE fcs %d aln %d frg %d jab %d und %d " + "ovr %d drp %d\n", smsc75xx, le32_to_cpu ( stats.rx.err.fcs ), + le32_to_cpu ( stats.rx.err.alignment ), + le32_to_cpu ( stats.rx.err.fragment ), + le32_to_cpu ( stats.rx.err.jabber ), + le32_to_cpu ( stats.rx.err.undersize ), + le32_to_cpu ( stats.rx.err.oversize ), + le32_to_cpu ( stats.rx.err.dropped ) ); + DBGC ( smsc75xx, "SMSC75XX %p RXB ucast %d bcast %d mcast %d\n", + smsc75xx, le32_to_cpu ( stats.rx.byte.unicast ), + le32_to_cpu ( stats.rx.byte.broadcast ), + le32_to_cpu ( stats.rx.byte.multicast ) ); + DBGC ( smsc75xx, "SMSC75XX %p RXF ucast %d bcast %d mcast %d pause " + "%d\n", smsc75xx, le32_to_cpu ( stats.rx.frame.unicast ), + le32_to_cpu ( stats.rx.frame.broadcast ), + le32_to_cpu ( stats.rx.frame.multicast ), + le32_to_cpu ( stats.rx.frame.pause ) ); + DBGC ( smsc75xx, "SMSC75XX %p TXE fcs %d def %d car %d cnt %d sgl %d " + "mul %d exc %d lat %d\n", smsc75xx, + le32_to_cpu ( stats.tx.err.fcs ), + le32_to_cpu ( stats.tx.err.deferral ), + le32_to_cpu ( stats.tx.err.carrier ), + le32_to_cpu ( stats.tx.err.count ), + le32_to_cpu ( stats.tx.err.single ), + le32_to_cpu ( stats.tx.err.multiple ), + le32_to_cpu ( stats.tx.err.excessive ), + le32_to_cpu ( stats.tx.err.late ) ); + DBGC ( smsc75xx, "SMSC75XX %p TXB ucast %d bcast %d mcast %d\n", + smsc75xx, le32_to_cpu ( stats.tx.byte.unicast ), + le32_to_cpu ( stats.tx.byte.broadcast ), + le32_to_cpu ( stats.tx.byte.multicast ) ); + DBGC ( smsc75xx, "SMSC75XX %p TXF ucast %d bcast %d mcast %d pause " + "%d\n", smsc75xx, le32_to_cpu ( stats.tx.frame.unicast ), + le32_to_cpu ( stats.tx.frame.broadcast ), + le32_to_cpu ( stats.tx.frame.multicast ), + le32_to_cpu ( stats.tx.frame.pause ) ); + + return 0; +} + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset device + * + * @v smsc75xx SMSC75xx device + * @ret rc Return status code + */ +static int smsc75xx_reset ( struct smsc75xx_device *smsc75xx ) { + uint32_t hw_cfg; + int rc; + + /* Reset device */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_HW_CFG, + SMSC75XX_HW_CFG_LRST ) ) != 0 ) + return rc; + + /* Wait for reset to complete */ + udelay ( SMSC75XX_RESET_DELAY_US ); + + /* Check that reset has completed */ + if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_HW_CFG, + &hw_cfg ) ) != 0 ) + return rc; + if ( hw_cfg & SMSC75XX_HW_CFG_LRST ) { + DBGC ( smsc75xx, "SMSC75XX %p failed to reset\n", smsc75xx ); + return -ETIMEDOUT; + } + + return 0; +} + +/****************************************************************************** + * + * Endpoint operations + * + ****************************************************************************** + */ + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void smsc75xx_intr_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct smsc75xx_device *smsc75xx = + container_of ( ep, struct smsc75xx_device, usbnet.intr ); + struct net_device *netdev = smsc75xx->netdev; + struct smsc75xx_interrupt *intr; + + /* Profile completions */ + profile_start ( &smsc75xx_intr_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto done; + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p interrupt failed: %s\n", + smsc75xx, strerror ( rc ) ); + DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, NULL, rc ); + goto done; + } + + /* Extract interrupt data */ + if ( iob_len ( iobuf ) != sizeof ( *intr ) ) { + DBGC ( smsc75xx, "SMSC75XX %p malformed interrupt\n", + smsc75xx ); + DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, NULL, rc ); + goto done; + } + intr = iobuf->data; + + /* Record interrupt status */ + smsc75xx->int_sts = le32_to_cpu ( intr->int_sts ); + profile_stop ( &smsc75xx_intr_profiler ); + + done: + /* Free I/O buffer */ + free_iob ( iobuf ); +} + +/** Interrupt endpoint operations */ +static struct usb_endpoint_driver_operations smsc75xx_intr_operations = { + .complete = smsc75xx_intr_complete, +}; + +/** + * Complete bulk IN transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void smsc75xx_in_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct smsc75xx_device *smsc75xx = + container_of ( ep, struct smsc75xx_device, usbnet.in ); + struct net_device *netdev = smsc75xx->netdev; + struct smsc75xx_rx_header *header; + + /* Profile completions */ + profile_start ( &smsc75xx_in_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) { + free_iob ( iobuf ); + return; + } + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p bulk IN failed: %s\n", + smsc75xx, strerror ( rc ) ); + goto err; + } + + /* Sanity check */ + if ( iob_len ( iobuf ) < ( sizeof ( *header ) ) ) { + DBGC ( smsc75xx, "SMSC75XX %p underlength bulk IN\n", + smsc75xx ); + DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto err; + } + + /* Strip header */ + header = iobuf->data; + iob_pull ( iobuf, sizeof ( *header ) ); + + /* Check for errors */ + if ( header->command & cpu_to_le32 ( SMSC75XX_RX_RED ) ) { + DBGC ( smsc75xx, "SMSC75XX %p receive error (%08x):\n", + smsc75xx, le32_to_cpu ( header->command ) ); + DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EIO; + goto err; + } + + /* Hand off to network stack */ + netdev_rx ( netdev, iob_disown ( iobuf ) ); + + profile_stop ( &smsc75xx_in_profiler ); + return; + + err: + /* Hand off to network stack */ + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); +} + +/** Bulk IN endpoint operations */ +static struct usb_endpoint_driver_operations smsc75xx_in_operations = { + .complete = smsc75xx_in_complete, +}; + +/** + * Transmit packet + * + * @v smsc75xx SMSC75xx device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int smsc75xx_out_transmit ( struct smsc75xx_device *smsc75xx, + struct io_buffer *iobuf ) { + struct smsc75xx_tx_header *header; + size_t len = iob_len ( iobuf ); + int rc; + + /* Profile transmissions */ + profile_start ( &smsc75xx_out_profiler ); + + /* Prepend header */ + if ( ( rc = iob_ensure_headroom ( iobuf, sizeof ( *header ) ) ) != 0 ) + return rc; + header = iob_push ( iobuf, sizeof ( *header ) ); + header->command = cpu_to_le32 ( SMSC75XX_TX_FCS | len ); + header->tag = 0; + header->mss = 0; + + /* Enqueue I/O buffer */ + if ( ( rc = usb_stream ( &smsc75xx->usbnet.out, iobuf, 0 ) ) != 0 ) + return rc; + + profile_stop ( &smsc75xx_out_profiler ); + return 0; +} + +/** + * Complete bulk OUT transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void smsc75xx_out_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct smsc75xx_device *smsc75xx = + container_of ( ep, struct smsc75xx_device, usbnet.out ); + struct net_device *netdev = smsc75xx->netdev; + + /* Report TX completion */ + netdev_tx_complete_err ( netdev, iobuf, rc ); +} + +/** Bulk OUT endpoint operations */ +static struct usb_endpoint_driver_operations smsc75xx_out_operations = { + .complete = smsc75xx_out_complete, +}; + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int smsc75xx_open ( struct net_device *netdev ) { + struct smsc75xx_device *smsc75xx = netdev->priv; + union smsc75xx_mac mac; + int rc; + + /* Clear stored interrupt status */ + smsc75xx->int_sts = 0; + + /* Copy MAC address */ + memset ( &mac, 0, sizeof ( mac ) ); + memcpy ( mac.raw, netdev->ll_addr, ETH_ALEN ); + + /* Configure bulk IN empty response */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_HW_CFG, + SMSC75XX_HW_CFG_BIR ) ) != 0 ) + goto err_hw_cfg; + + /* Open USB network device */ + if ( ( rc = usbnet_open ( &smsc75xx->usbnet ) ) != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p could not open: %s\n", + smsc75xx, strerror ( rc ) ); + goto err_open; + } + + /* Configure interrupt endpoint */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_INT_EP_CTL, + ( SMSC75XX_INT_EP_CTL_RDFO_EN | + SMSC75XX_INT_EP_CTL_PHY_EN ) ) ) != 0 ) + goto err_int_ep_ctl; + + /* Configure bulk IN delay */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_BULK_IN_DLY, + SMSC75XX_BULK_IN_DLY_SET ( 0 ) ) ) != 0 ) + goto err_bulk_in_dly; + + /* Configure receive filters */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_RFE_CTL, + ( SMSC75XX_RFE_CTL_AB | + SMSC75XX_RFE_CTL_AM | + SMSC75XX_RFE_CTL_AU ) ) ) != 0 ) + goto err_rfe_ctl; + + /* Configure receive FIFO */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_FCT_RX_CTL, + ( SMSC75XX_FCT_RX_CTL_EN | + SMSC75XX_FCT_RX_CTL_BAD ) ) ) != 0 ) + goto err_fct_rx_ctl; + + /* Configure transmit FIFO */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_FCT_TX_CTL, + SMSC75XX_FCT_TX_CTL_EN ) ) != 0 ) + goto err_fct_tx_ctl; + + /* Configure receive datapath */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MAC_RX, + ( SMSC75XX_MAC_RX_MAX_SIZE_DEFAULT | + SMSC75XX_MAC_RX_FCS | + SMSC75XX_MAC_RX_EN ) ) ) != 0 ) + goto err_mac_rx; + + /* Configure transmit datapath */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MAC_TX, + SMSC75XX_MAC_TX_EN ) ) != 0 ) + goto err_mac_tx; + + /* Write MAC address high register */ + if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_RX_ADDRH, + mac.addr.h ) ) != 0 ) + goto err_rx_addrh; + + /* Write MAC address low register */ + if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_RX_ADDRL, + mac.addr.l ) ) != 0 ) + goto err_rx_addrl; + + /* Write MAC address perfect filter high register */ + mac.addr.h |= cpu_to_le32 ( SMSC75XX_ADDR_FILTH_VALID ); + if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_ADDR_FILTH ( 0 ), + mac.addr.h ) ) != 0 ) + goto err_addr_filth; + + /* Write MAC address perfect filter low register */ + if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_ADDR_FILTL ( 0 ), + mac.addr.l ) ) != 0 ) + goto err_addr_filtl; + + /* Enable PHY interrupts */ + if ( ( rc = mii_write ( &smsc75xx->mii, SMSC75XX_MII_PHY_INTR_MASK, + ( SMSC75XX_PHY_INTR_ANEG_DONE | + SMSC75XX_PHY_INTR_LINK_DOWN ) ) ) != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p could not set PHY interrupt " + "mask: %s\n", smsc75xx, strerror ( rc ) ); + goto err_phy_intr_mask; + } + + /* Update link status */ + smsc75xx_check_link ( smsc75xx ); + + return 0; + + err_phy_intr_mask: + err_addr_filtl: + err_addr_filth: + err_rx_addrl: + err_rx_addrh: + err_mac_tx: + err_mac_rx: + err_fct_tx_ctl: + err_fct_rx_ctl: + err_rfe_ctl: + err_bulk_in_dly: + err_int_ep_ctl: + usbnet_close ( &smsc75xx->usbnet ); + err_open: + err_hw_cfg: + smsc75xx_reset ( smsc75xx ); + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void smsc75xx_close ( struct net_device *netdev ) { + struct smsc75xx_device *smsc75xx = netdev->priv; + + /* Close USB network device */ + usbnet_close ( &smsc75xx->usbnet ); + + /* Dump statistics (for debugging) */ + smsc75xx_dump_statistics ( smsc75xx ); + + /* Reset device */ + smsc75xx_reset ( smsc75xx ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int smsc75xx_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct smsc75xx_device *smsc75xx = netdev->priv; + int rc; + + /* Transmit packet */ + if ( ( rc = smsc75xx_out_transmit ( smsc75xx, iobuf ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void smsc75xx_poll ( struct net_device *netdev ) { + struct smsc75xx_device *smsc75xx = netdev->priv; + uint32_t int_sts; + int rc; + + /* Poll USB bus */ + usb_poll ( smsc75xx->bus ); + + /* Refill endpoints */ + if ( ( rc = usbnet_refill ( &smsc75xx->usbnet ) ) != 0 ) + netdev_rx_err ( netdev, NULL, rc ); + + /* Do nothing more unless there are interrupts to handle */ + int_sts = smsc75xx->int_sts; + if ( ! int_sts ) + return; + + /* Check link status if applicable */ + if ( int_sts & SMSC75XX_INT_STS_PHY_INT ) { + smsc75xx_check_link ( smsc75xx ); + int_sts &= ~SMSC75XX_INT_STS_PHY_INT; + } + + /* Record RX FIFO overflow if applicable */ + if ( int_sts & SMSC75XX_INT_STS_RDFO_INT ) { + DBGC2 ( smsc75xx, "SMSC75XX %p RX FIFO overflowed\n", + smsc75xx ); + netdev_rx_err ( netdev, NULL, -ENOBUFS ); + int_sts &= ~SMSC75XX_INT_STS_RDFO_INT; + } + + /* Check for unexpected interrupts */ + if ( int_sts ) { + DBGC ( smsc75xx, "SMSC75XX %p unexpected interrupt %#08x\n", + smsc75xx, int_sts ); + netdev_rx_err ( netdev, NULL, -ENOTTY ); + } + + /* Clear interrupts */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_INT_STS, + smsc75xx->int_sts ) ) != 0 ) + netdev_rx_err ( netdev, NULL, rc ); + smsc75xx->int_sts = 0; +} + +/** SMSC75xx network device operations */ +static struct net_device_operations smsc75xx_operations = { + .open = smsc75xx_open, + .close = smsc75xx_close, + .transmit = smsc75xx_transmit, + .poll = smsc75xx_poll, +}; + +/****************************************************************************** + * + * USB interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int smsc75xx_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct net_device *netdev; + struct smsc75xx_device *smsc75xx; + int rc; + + /* Allocate and initialise structure */ + netdev = alloc_etherdev ( sizeof ( *smsc75xx ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &smsc75xx_operations ); + netdev->dev = &func->dev; + smsc75xx = netdev->priv; + memset ( smsc75xx, 0, sizeof ( *smsc75xx ) ); + smsc75xx->usb = usb; + smsc75xx->bus = usb->port->hub->bus; + smsc75xx->netdev = netdev; + usbnet_init ( &smsc75xx->usbnet, func, &smsc75xx_intr_operations, + &smsc75xx_in_operations, &smsc75xx_out_operations ); + usb_refill_init ( &smsc75xx->usbnet.intr, 0, SMSC75XX_INTR_MAX_FILL ); + usb_refill_init ( &smsc75xx->usbnet.in, SMSC75XX_IN_MTU, + SMSC75XX_IN_MAX_FILL ); + mii_init ( &smsc75xx->mii, &smsc75xx_mii_operations ); + DBGC ( smsc75xx, "SMSC75XX %p on %s\n", smsc75xx, func->name ); + + /* Describe USB network device */ + if ( ( rc = usbnet_describe ( &smsc75xx->usbnet, config ) ) != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p could not describe: %s\n", + smsc75xx, strerror ( rc ) ); + goto err_describe; + } + + /* Reset device */ + if ( ( rc = smsc75xx_reset ( smsc75xx ) ) != 0 ) + goto err_reset; + + /* Read MAC address */ + if ( ( rc = smsc75xx_eeprom_read ( smsc75xx, SMSC75XX_EEPROM_MAC, + netdev->hw_addr, ETH_ALEN ) ) != 0 ) + goto err_eeprom_read; + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + usb_func_set_drvdata ( func, netdev ); + return 0; + + unregister_netdev ( netdev ); + err_register: + err_eeprom_read: + err_reset: + err_describe: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v func USB function + */ +static void smsc75xx_remove ( struct usb_function *func ) { + struct net_device *netdev = usb_func_get_drvdata ( func ); + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** SMSC75xx device IDs */ +static struct usb_device_id smsc75xx_ids[] = { + { + .name = "smsc7500", + .vendor = 0x0424, + .product = 0x7500, + .class = { 0xff, 0x00, 0xff }, + }, + { + .name = "smsc7505", + .vendor = 0x0424, + .product = 0x7505, + .class = { 0xff, 0x00, 0xff }, + }, +}; + +/** SMSC LAN75xx driver */ +struct usb_driver smsc75xx_driver __usb_driver = { + .ids = smsc75xx_ids, + .id_count = ( sizeof ( smsc75xx_ids ) / sizeof ( smsc75xx_ids[0] ) ), + .probe = smsc75xx_probe, + .remove = smsc75xx_remove, +}; diff --git a/qemu/roms/ipxe/src/drivers/net/smsc75xx.h b/qemu/roms/ipxe/src/drivers/net/smsc75xx.h new file mode 100644 index 000000000..2463b72a1 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/smsc75xx.h @@ -0,0 +1,309 @@ +#ifndef _SMSC75XX_H +#define _SMSC75XX_H + +/** @file + * + * SMSC LAN75xx USB Ethernet driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** Register write command */ +#define SMSC75XX_REGISTER_WRITE \ + ( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0xa0 ) ) + +/** Register read command */ +#define SMSC75XX_REGISTER_READ \ + ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0xa1 ) ) + +/** Get statistics command */ +#define SMSC75XX_GET_STATISTICS \ + ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0xa2 ) ) + +/** Interrupt status register */ +#define SMSC75XX_INT_STS 0x00c +#define SMSC75XX_INT_STS_RDFO_INT 0x00400000UL /**< RX FIFO overflow */ +#define SMSC75XX_INT_STS_PHY_INT 0x00020000UL /**< PHY interrupt */ + +/** Hardware configuration register */ +#define SMSC75XX_HW_CFG 0x010 +#define SMSC75XX_HW_CFG_BIR 0x00000080UL /**< Bulk IN use NAK */ +#define SMSC75XX_HW_CFG_LRST 0x00000002UL /**< Soft lite reset */ + +/** Interrupt endpoint control register */ +#define SMSC75XX_INT_EP_CTL 0x038 +#define SMSC75XX_INT_EP_CTL_RDFO_EN 0x00400000UL /**< RX FIFO overflow */ +#define SMSC75XX_INT_EP_CTL_PHY_EN 0x00020000UL /**< PHY interrupt */ + +/** Bulk IN delay register */ +#define SMSC75XX_BULK_IN_DLY 0x03c +#define SMSC75XX_BULK_IN_DLY_SET(ticks) ( (ticks) << 0 ) /**< Delay / 16.7ns */ + +/** EEPROM command register */ +#define SMSC75XX_E2P_CMD 0x040 +#define SMSC75XX_E2P_CMD_EPC_BSY 0x80000000UL /**< EPC busy */ +#define SMSC75XX_E2P_CMD_EPC_CMD_READ 0x00000000UL /**< READ command */ +#define SMSC75XX_E2P_CMD_EPC_ADDR(addr) ( (addr) << 0 ) /**< EPC address */ + +/** EEPROM data register */ +#define SMSC75XX_E2P_DATA 0x044 +#define SMSC75XX_E2P_DATA_GET(e2p_data) \ + ( ( (e2p_data) >> 0 ) & 0xff ) /**< EEPROM data */ + +/** MAC address EEPROM address */ +#define SMSC75XX_EEPROM_MAC 0x01 + +/** Receive filtering engine control register */ +#define SMSC75XX_RFE_CTL 0x060 +#define SMSC75XX_RFE_CTL_AB 0x00000400UL /**< Accept broadcast */ +#define SMSC75XX_RFE_CTL_AM 0x00000200UL /**< Accept multicast */ +#define SMSC75XX_RFE_CTL_AU 0x00000100UL /**< Accept unicast */ + +/** FIFO controller RX FIFO control register */ +#define SMSC75XX_FCT_RX_CTL 0x090 +#define SMSC75XX_FCT_RX_CTL_EN 0x80000000UL /**< FCT RX enable */ +#define SMSC75XX_FCT_RX_CTL_BAD 0x02000000UL /**< Store bad frames */ + +/** FIFO controller TX FIFO control register */ +#define SMSC75XX_FCT_TX_CTL 0x094 +#define SMSC75XX_FCT_TX_CTL_EN 0x80000000UL /**< FCT TX enable */ + +/** MAC receive register */ +#define SMSC75XX_MAC_RX 0x104 +#define SMSC75XX_MAC_RX_MAX_SIZE(mtu) ( (mtu) << 16 ) /**< Max frame size */ +#define SMSC75XX_MAC_RX_MAX_SIZE_DEFAULT \ + SMSC75XX_MAC_RX_MAX_SIZE ( ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */ ) +#define SMSC75XX_MAC_RX_FCS 0x00000010UL /**< FCS stripping */ +#define SMSC75XX_MAC_RX_EN 0x00000001UL /**< RX enable */ + +/** MAC transmit register */ +#define SMSC75XX_MAC_TX 0x108 +#define SMSC75XX_MAC_TX_EN 0x00000001UL /**< TX enable */ + +/** MAC receive address high register */ +#define SMSC75XX_RX_ADDRH 0x118 + +/** MAC receive address low register */ +#define SMSC75XX_RX_ADDRL 0x11c + +/** MII access register */ +#define SMSC75XX_MII_ACCESS 0x120 +#define SMSC75XX_MII_ACCESS_PHY_ADDRESS 0x00000800UL /**< PHY address */ +#define SMSC75XX_MII_ACCESS_MIIRINDA(addr) ( (addr) << 6 ) /**< MII register */ +#define SMSC75XX_MII_ACCESS_MIIWNR 0x00000002UL /**< MII write */ +#define SMSC75XX_MII_ACCESS_MIIBZY 0x00000001UL /**< MII busy */ + +/** MII data register */ +#define SMSC75XX_MII_DATA 0x124 +#define SMSC75XX_MII_DATA_SET(data) ( (data) << 0 ) /**< Set data */ +#define SMSC75XX_MII_DATA_GET(mii_data) \ + ( ( (mii_data) >> 0 ) & 0xffff ) /**< Get data */ + +/** PHY interrupt source MII register */ +#define SMSC75XX_MII_PHY_INTR_SOURCE 29 + +/** PHY interrupt mask MII register */ +#define SMSC75XX_MII_PHY_INTR_MASK 30 + +/** PHY interrupt: auto-negotiation complete */ +#define SMSC75XX_PHY_INTR_ANEG_DONE 0x0040 + +/** PHY interrupt: link down */ +#define SMSC75XX_PHY_INTR_LINK_DOWN 0x0010 + +/** MAC address perfect filter N high register */ +#define SMSC75XX_ADDR_FILTH(n) ( 0x300 + ( 8 * (n) ) ) +#define SMSC75XX_ADDR_FILTH_VALID 0x80000000UL /**< Address valid */ + +/** MAC address perfect filter N low register */ +#define SMSC75XX_ADDR_FILTL(n) ( 0x304 + ( 8 * (n) ) ) + +/** MAC address */ +union smsc75xx_mac { + /** MAC receive address registers */ + struct { + /** MAC receive address low register */ + uint32_t l; + /** MAC receive address high register */ + uint32_t h; + } __attribute__ (( packed )) addr; + /** Raw MAC address */ + uint8_t raw[ETH_ALEN]; +}; + +/** Receive packet header */ +struct smsc75xx_rx_header { + /** RX command word */ + uint32_t command; + /** VLAN tag */ + uint16_t vtag; + /** Checksum */ + uint16_t csum; + /** Two-byte padding used to align Ethernet payload */ + uint16_t pad; +} __attribute__ (( packed )); + +/** Receive error detected */ +#define SMSC75XX_RX_RED 0x00400000UL + +/** Transmit packet header */ +struct smsc75xx_tx_header { + /** TX command word */ + uint32_t command; + /** VLAN tag */ + uint16_t tag; + /** Maximum segment size */ + uint16_t mss; +} __attribute__ (( packed )); + +/** Insert frame checksum and pad */ +#define SMSC75XX_TX_FCS 0x00400000UL + +/** Interrupt packet format */ +struct smsc75xx_interrupt { + /** Current value of INT_STS register */ + uint32_t int_sts; +} __attribute__ (( packed )); + +/** Byte count statistics */ +struct smsc75xx_byte_statistics { + /** Unicast byte count */ + uint32_t unicast; + /** Broadcast byte count */ + uint32_t broadcast; + /** Multicast byte count */ + uint32_t multicast; +} __attribute__ (( packed )); + +/** Frame count statistics */ +struct smsc75xx_frame_statistics { + /** Unicast frames */ + uint32_t unicast; + /** Broadcast frames */ + uint32_t broadcast; + /** Multicast frames */ + uint32_t multicast; + /** Pause frames */ + uint32_t pause; + /** Frames by length category */ + uint32_t len[7]; +} __attribute__ (( packed )); + +/** Receive error statistics */ +struct smsc75xx_rx_error_statistics { + /** FCS errors */ + uint32_t fcs; + /** Alignment errors */ + uint32_t alignment; + /** Fragment errors */ + uint32_t fragment; + /** Jabber errors */ + uint32_t jabber; + /** Undersize frame errors */ + uint32_t undersize; + /** Oversize frame errors */ + uint32_t oversize; + /** Dropped frame errors */ + uint32_t dropped; +} __attribute__ (( packed )); + +/** Receive statistics */ +struct smsc75xx_rx_statistics { + /** Error statistics */ + struct smsc75xx_rx_error_statistics err; + /** Byte count statistics */ + struct smsc75xx_byte_statistics byte; + /** Frame count statistics */ + struct smsc75xx_frame_statistics frame; +} __attribute__ (( packed )); + +/** Transmit error statistics */ +struct smsc75xx_tx_error_statistics { + /** FCS errors */ + uint32_t fcs; + /** Excess deferral errors */ + uint32_t deferral; + /** Carrier errors */ + uint32_t carrier; + /** Bad byte count */ + uint32_t count; + /** Single collisions */ + uint32_t single; + /** Multiple collisions */ + uint32_t multiple; + /** Excession collisions */ + uint32_t excessive; + /** Late collisions */ + uint32_t late; +} __attribute__ (( packed )); + +/** Transmit statistics */ +struct smsc75xx_tx_statistics { + /** Error statistics */ + struct smsc75xx_tx_error_statistics err; + /** Byte count statistics */ + struct smsc75xx_byte_statistics byte; + /** Frame count statistics */ + struct smsc75xx_frame_statistics frame; +} __attribute__ (( packed )); + +/** Statistics */ +struct smsc75xx_statistics { + /** Receive statistics */ + struct smsc75xx_rx_statistics rx; + /** Transmit statistics */ + struct smsc75xx_tx_statistics tx; +} __attribute__ (( packed )); + +/** A SMSC75xx network device */ +struct smsc75xx_device { + /** USB device */ + struct usb_device *usb; + /** USB bus */ + struct usb_bus *bus; + /** Network device */ + struct net_device *netdev; + /** USB network device */ + struct usbnet_device usbnet; + /** MII interface */ + struct mii_interface mii; + /** Interrupt status */ + uint32_t int_sts; +}; + +/** Reset delay (in microseconds) */ +#define SMSC75XX_RESET_DELAY_US 2 + +/** Maximum time to wait for EEPROM (in milliseconds) */ +#define SMSC75XX_EEPROM_MAX_WAIT_MS 100 + +/** Maximum time to wait for MII (in milliseconds) */ +#define SMSC75XX_MII_MAX_WAIT_MS 100 + +/** Interrupt maximum fill level + * + * This is a policy decision. + */ +#define SMSC75XX_INTR_MAX_FILL 2 + +/** Bulk IN maximum fill level + * + * This is a policy decision. + */ +#define SMSC75XX_IN_MAX_FILL 8 + +/** Bulk IN buffer size */ +#define SMSC75XX_IN_MTU \ + ( sizeof ( struct smsc75xx_rx_header ) + \ + ETH_FRAME_LEN + 4 /* possible VLAN header */ ) + +#endif /* _SMSC75XX_H */ diff --git a/qemu/roms/ipxe/src/drivers/net/sundance.c b/qemu/roms/ipxe/src/drivers/net/sundance.c index eef7c9c7c..9127fa2cd 100644 --- a/qemu/roms/ipxe/src/drivers/net/sundance.c +++ b/qemu/roms/ipxe/src/drivers/net/sundance.c @@ -601,7 +601,7 @@ static int sundance_probe ( struct nic *nic, struct pci_device *pci ) { sdc->nic_name = pci->id->name; sdc->mtu = mtu; - pci_read_config_byte(pci, PCI_REVISION_ID, &sdc->pci_rev_id); + pci_read_config_byte(pci, PCI_REVISION, &sdc->pci_rev_id); DBG ( "Device revision id: %hx\n", sdc->pci_rev_id ); diff --git a/qemu/roms/ipxe/src/drivers/net/tg3/tg3.c b/qemu/roms/ipxe/src/drivers/net/tg3/tg3.c index 32ca1609c..42bfa2d99 100644 --- a/qemu/roms/ipxe/src/drivers/net/tg3/tg3.c +++ b/qemu/roms/ipxe/src/drivers/net/tg3/tg3.c @@ -928,6 +928,7 @@ static struct pci_device_id tg3_nics[] = { PCI_ROM(0x14e4, 0x16b6, "14e4-16b6", "14e4-16b6", 0), PCI_ROM(0x14e4, 0x1657, "14e4-1657", "14e4-1657", 0), PCI_ROM(0x14e4, 0x165f, "14e4-165f", "14e4-165f", 0), + PCI_ROM(0x14e4, 0x1686, "14e4-1686", "14e4-1686", 0), PCI_ROM(0x1148, 0x4400, "1148-4400", "1148-4400", 0), PCI_ROM(0x1148, 0x4500, "1148-4500", "1148-4500", 0), PCI_ROM(0x173b, 0x03e8, "173b-03e8", "173b-03e8", 0), diff --git a/qemu/roms/ipxe/src/drivers/net/tg3/tg3.h b/qemu/roms/ipxe/src/drivers/net/tg3/tg3.h index 660368394..2b85b065b 100644 --- a/qemu/roms/ipxe/src/drivers/net/tg3/tg3.h +++ b/qemu/roms/ipxe/src/drivers/net/tg3/tg3.h @@ -131,6 +131,10 @@ #define PCI_DEVICE_ID_TIGON3_5901_2 0x170e #define PCI_DEVICE_ID_TIGON3_5906 0x1712 #define PCI_DEVICE_ID_TIGON3_5906M 0x1713 +#define PCI_VENDOR_ID_COMPAQ 0x0e11 +#define PCI_VENDOR_ID_IBM 0x1014 +#define PCI_VENDOR_ID_DELL 0x1028 +#define PCI_VENDOR_ID_3COM 0x10b7 /* */ #define SPEED_10 10 @@ -185,6 +189,7 @@ #define TG3PCI_DEVICE_TIGON3_57761 0x16b0 #define TG3PCI_DEVICE_TIGON3_57762 0x1682 #define TG3PCI_DEVICE_TIGON3_57765 0x16b4 +#define TG3PCI_DEVICE_TIGON3_57766 0x1686 #define TG3PCI_DEVICE_TIGON3_57791 0x16b2 #define TG3PCI_DEVICE_TIGON3_57795 0x16b6 #define TG3PCI_DEVICE_TIGON3_5719 0x1657 diff --git a/qemu/roms/ipxe/src/drivers/net/tg3/tg3_hw.c b/qemu/roms/ipxe/src/drivers/net/tg3/tg3_hw.c index 3a481aba3..50353cf36 100644 --- a/qemu/roms/ipxe/src/drivers/net/tg3/tg3_hw.c +++ b/qemu/roms/ipxe/src/drivers/net/tg3/tg3_hw.c @@ -436,6 +436,7 @@ int tg3_get_invariants(struct tg3 *tp) tp->pdev->device == TG3PCI_DEVICE_TIGON3_57761 || tp->pdev->device == TG3PCI_DEVICE_TIGON3_57762 || tp->pdev->device == TG3PCI_DEVICE_TIGON3_57765 || + tp->pdev->device == TG3PCI_DEVICE_TIGON3_57766 || tp->pdev->device == TG3PCI_DEVICE_TIGON3_57791 || tp->pdev->device == TG3PCI_DEVICE_TIGON3_57795) pci_read_config_dword(tp->pdev, diff --git a/qemu/roms/ipxe/src/drivers/net/virtio-net.c b/qemu/roms/ipxe/src/drivers/net/virtio-net.c index d5fd81979..533ccb0c6 100644 --- a/qemu/roms/ipxe/src/drivers/net/virtio-net.c +++ b/qemu/roms/ipxe/src/drivers/net/virtio-net.c @@ -20,7 +20,7 @@ * See the COPYING file in the top-level directory. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -131,8 +131,8 @@ static void virtnet_enqueue_iob ( struct net_device *netdev, }, }; - DBGC ( virtnet, "VIRTIO-NET %p enqueuing iobuf %p on vq %d\n", - virtnet, iobuf, vq_idx ); + DBGC2 ( virtnet, "VIRTIO-NET %p enqueuing iobuf %p on vq %d\n", + virtnet, iobuf, vq_idx ); vring_add_buf ( vq, list, out, in, iobuf, 0 ); vring_kick ( virtnet->ioaddr, vq, 1 ); @@ -256,8 +256,8 @@ static void virtnet_process_tx_packets ( struct net_device *netdev ) { while ( vring_more_used ( tx_vq ) ) { struct io_buffer *iobuf = vring_get_buf ( tx_vq, NULL ); - DBGC ( virtnet, "VIRTIO-NET %p tx complete iobuf %p\n", - virtnet, iobuf ); + DBGC2 ( virtnet, "VIRTIO-NET %p tx complete iobuf %p\n", + virtnet, iobuf ); netdev_tx_complete ( netdev, iobuf ); } @@ -283,8 +283,8 @@ static void virtnet_process_rx_packets ( struct net_device *netdev ) { iob_unput ( iobuf, RX_BUF_SIZE ); iob_put ( iobuf, len - sizeof ( struct virtio_net_hdr ) ); - DBGC ( virtnet, "VIRTIO-NET %p rx complete iobuf %p len %zd\n", - virtnet, iobuf, iob_len ( iobuf ) ); + DBGC2 ( virtnet, "VIRTIO-NET %p rx complete iobuf %p len %zd\n", + virtnet, iobuf, iob_len ( iobuf ) ); /* Pass completed packet to the network stack */ netdev_rx ( netdev, iobuf ); diff --git a/qemu/roms/ipxe/src/drivers/net/vmxnet3.c b/qemu/roms/ipxe/src/drivers/net/vmxnet3.c index 31082bf6f..8d4f4b843 100644 --- a/qemu/roms/ipxe/src/drivers/net/vmxnet3.c +++ b/qemu/roms/ipxe/src/drivers/net/vmxnet3.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/net/vmxnet3.h b/qemu/roms/ipxe/src/drivers/net/vmxnet3.h index db313d4b8..a1671d9dd 100644 --- a/qemu/roms/ipxe/src/drivers/net/vmxnet3.h +++ b/qemu/roms/ipxe/src/drivers/net/vmxnet3.h @@ -18,9 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/drivers/net/vxge/vxge.c b/qemu/roms/ipxe/src/drivers/net/vxge/vxge.c index bf20ec43c..d50ac05b5 100644 --- a/qemu/roms/ipxe/src/drivers/net/vxge/vxge.c +++ b/qemu/roms/ipxe/src/drivers/net/vxge/vxge.c @@ -5,10 +5,11 @@ * as "vxge" even though the code is in vxge_* named files. */ -FILE_LICENCE(GPL2_OR_LATER); +FILE_LICENCE(GPL2_OR_LATER_OR_UBDL); #include +PROVIDE_REQUIRING_SYMBOL(); REQUIRE_OBJECT(vxge_main); /** vxge PCI IDs for util/parserom.pl which are put into bin/NIC */ diff --git a/qemu/roms/ipxe/src/drivers/net/vxge/vxge_main.c b/qemu/roms/ipxe/src/drivers/net/vxge/vxge_main.c index 130eab617..8b099c0e2 100644 --- a/qemu/roms/ipxe/src/drivers/net/vxge/vxge_main.c +++ b/qemu/roms/ipxe/src/drivers/net/vxge/vxge_main.c @@ -509,7 +509,7 @@ vxge_probe(struct pci_device *pdev) vxge_debug(VXGE_INFO, "vxge_probe for device " PCI_FMT "\n", PCI_ARGS(pdev)); - pci_read_config_byte(pdev, PCI_REVISION_ID, &revision); + pci_read_config_byte(pdev, PCI_REVISION, &revision); titan1 = is_titan1(pdev->device, revision); mmio_start = pci_bar_start(pdev, PCI_BASE_ADDRESS_0); diff --git a/qemu/roms/ipxe/src/drivers/net/w89c840.c b/qemu/roms/ipxe/src/drivers/net/w89c840.c index ce638ab99..d8144a8ce 100644 --- a/qemu/roms/ipxe/src/drivers/net/w89c840.c +++ b/qemu/roms/ipxe/src/drivers/net/w89c840.c @@ -641,7 +641,9 @@ static int w89c840_probe ( struct nic *nic, struct pci_device *p ) { ioaddr = ioaddr & ~3; /* Mask the bit that says "this is an io addr" */ +#define PCI_VENDOR_ID_WINBOND2 0x1050 #define PCI_DEVICE_ID_WINBOND2_89C840 0x0840 +#define PCI_VENDOR_ID_COMPEX 0x11f6 #define PCI_DEVICE_ID_COMPEX_RL100ATX 0x2011 /* From Matt Hortman */ diff --git a/qemu/roms/ipxe/src/drivers/nvs/nvs.c b/qemu/roms/ipxe/src/drivers/nvs/nvs.c index ccb2145bd..af7c466c4 100644 --- a/qemu/roms/ipxe/src/drivers/nvs/nvs.c +++ b/qemu/roms/ipxe/src/drivers/nvs/nvs.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/nvs/nvsvpd.c b/qemu/roms/ipxe/src/drivers/nvs/nvsvpd.c index 33148d5b9..3e88531c7 100644 --- a/qemu/roms/ipxe/src/drivers/nvs/nvsvpd.c +++ b/qemu/roms/ipxe/src/drivers/nvs/nvsvpd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/nvs/spi.c b/qemu/roms/ipxe/src/drivers/nvs/spi.c index 84613b9dd..dcfe1af91 100644 --- a/qemu/roms/ipxe/src/drivers/nvs/spi.c +++ b/qemu/roms/ipxe/src/drivers/nvs/spi.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/nvs/threewire.c b/qemu/roms/ipxe/src/drivers/nvs/threewire.c index 53f1ad8de..547f35382 100644 --- a/qemu/roms/ipxe/src/drivers/nvs/threewire.c +++ b/qemu/roms/ipxe/src/drivers/nvs/threewire.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/drivers/usb/ehci.c b/qemu/roms/ipxe/src/drivers/usb/ehci.c new file mode 100644 index 000000000..4124692a6 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/usb/ehci.c @@ -0,0 +1,1994 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ehci.h" + +/** @file + * + * USB Enhanced Host Controller Interface (EHCI) driver + * + */ + +/** + * Construct error code from transfer descriptor status + * + * @v status Transfer descriptor status + * @ret rc Error code + * + * Bits 2-5 of the status code provide some indication as to the root + * cause of the error. We incorporate these into the error code as + * reported to usb_complete_err(). + */ +#define EIO_STATUS( status ) EUNIQ ( EINFO_EIO, ( ( (status) >> 2 ) & 0xf ) ) + +/****************************************************************************** + * + * Register access + * + ****************************************************************************** + */ + +/** + * Initialise device + * + * @v ehci EHCI device + * @v regs MMIO registers + */ +static void ehci_init ( struct ehci_device *ehci, void *regs ) { + uint32_t hcsparams; + uint32_t hccparams; + size_t caplength; + + /* Locate capability and operational registers */ + ehci->cap = regs; + caplength = readb ( ehci->cap + EHCI_CAP_CAPLENGTH ); + ehci->op = ( ehci->cap + caplength ); + DBGC2 ( ehci, "EHCI %s cap %08lx op %08lx\n", ehci->name, + virt_to_phys ( ehci->cap ), virt_to_phys ( ehci->op ) ); + + /* Read structural parameters */ + hcsparams = readl ( ehci->cap + EHCI_CAP_HCSPARAMS ); + ehci->ports = EHCI_HCSPARAMS_PORTS ( hcsparams ); + DBGC ( ehci, "EHCI %s has %d ports\n", ehci->name, ehci->ports ); + + /* Read capability parameters 1 */ + hccparams = readl ( ehci->cap + EHCI_CAP_HCCPARAMS ); + ehci->addr64 = EHCI_HCCPARAMS_ADDR64 ( hccparams ); + ehci->flsize = ( EHCI_HCCPARAMS_FLSIZE ( hccparams ) ? + EHCI_FLSIZE_SMALL : EHCI_FLSIZE_DEFAULT ); + ehci->eecp = EHCI_HCCPARAMS_EECP ( hccparams ); + DBGC2 ( ehci, "EHCI %s %d-bit flsize %d\n", ehci->name, + ( ehci->addr64 ? 64 : 32 ), ehci->flsize ); +} + +/** + * Find extended capability + * + * @v ehci EHCI device + * @v pci PCI device + * @v id Capability ID + * @v offset Offset to previous extended capability instance, or zero + * @ret offset Offset to extended capability, or zero if not found + */ +static unsigned int ehci_extended_capability ( struct ehci_device *ehci, + struct pci_device *pci, + unsigned int id, + unsigned int offset ) { + uint32_t eecp; + + /* Locate the extended capability */ + while ( 1 ) { + + /* Locate first or next capability as applicable */ + if ( offset ) { + pci_read_config_dword ( pci, offset, &eecp ); + offset = EHCI_EECP_NEXT ( eecp ); + } else { + offset = ehci->eecp; + } + if ( ! offset ) + return 0; + + /* Check if this is the requested capability */ + pci_read_config_dword ( pci, offset, &eecp ); + if ( EHCI_EECP_ID ( eecp ) == id ) + return offset; + } +} + +/** + * Calculate buffer alignment + * + * @v len Length + * @ret align Buffer alignment + * + * Determine alignment required for a buffer which must be aligned to + * at least EHCI_MIN_ALIGN and which must not cross a page boundary. + */ +static inline size_t ehci_align ( size_t len ) { + size_t align; + + /* Align to own length (rounded up to a power of two) */ + align = ( 1 << fls ( len - 1 ) ); + + /* Round up to EHCI_MIN_ALIGN if needed */ + if ( align < EHCI_MIN_ALIGN ) + align = EHCI_MIN_ALIGN; + + return align; +} + +/** + * Check control data structure reachability + * + * @v ehci EHCI device + * @v ptr Data structure pointer + * @ret rc Return status code + */ +static int ehci_ctrl_reachable ( struct ehci_device *ehci, void *ptr ) { + physaddr_t phys = virt_to_phys ( ptr ); + uint32_t segment; + + /* Always reachable in a 32-bit build */ + if ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) ) + return 0; + + /* Reachable only if control segment matches in a 64-bit build */ + segment = ( ( ( uint64_t ) phys ) >> 32 ); + if ( segment == ehci->ctrldssegment ) + return 0; + + return -ENOTSUP; +} + +/****************************************************************************** + * + * USB legacy support + * + ****************************************************************************** + */ + +/** Prevent the release of ownership back to BIOS */ +static int ehci_legacy_prevent_release; + +/** + * Initialise USB legacy support + * + * @v ehci EHCI device + * @v pci PCI device + */ +static void ehci_legacy_init ( struct ehci_device *ehci, + struct pci_device *pci ) { + unsigned int legacy; + uint8_t bios; + + /* Locate USB legacy support capability (if present) */ + legacy = ehci_extended_capability ( ehci, pci, EHCI_EECP_ID_LEGACY, 0 ); + if ( ! legacy ) { + /* Not an error; capability may not be present */ + DBGC ( ehci, "EHCI %s has no USB legacy support capability\n", + ehci->name ); + return; + } + + /* Check if legacy USB support is enabled */ + pci_read_config_byte ( pci, ( legacy + EHCI_USBLEGSUP_BIOS ), &bios ); + if ( ! ( bios & EHCI_USBLEGSUP_BIOS_OWNED ) ) { + /* Not an error; already owned by OS */ + DBGC ( ehci, "EHCI %s USB legacy support already disabled\n", + ehci->name ); + return; + } + + /* Record presence of USB legacy support capability */ + ehci->legacy = legacy; +} + +/** + * Claim ownership from BIOS + * + * @v ehci EHCI device + * @v pci PCI device + */ +static void ehci_legacy_claim ( struct ehci_device *ehci, + struct pci_device *pci ) { + unsigned int legacy = ehci->legacy; + uint32_t ctlsts; + uint8_t bios; + unsigned int i; + + /* Do nothing unless legacy support capability is present */ + if ( ! legacy ) + return; + + /* Claim ownership */ + pci_write_config_byte ( pci, ( legacy + EHCI_USBLEGSUP_OS ), + EHCI_USBLEGSUP_OS_OWNED ); + + /* Wait for BIOS to release ownership */ + for ( i = 0 ; i < EHCI_USBLEGSUP_MAX_WAIT_MS ; i++ ) { + + /* Check if BIOS has released ownership */ + pci_read_config_byte ( pci, ( legacy + EHCI_USBLEGSUP_BIOS ), + &bios ); + if ( ! ( bios & EHCI_USBLEGSUP_BIOS_OWNED ) ) { + DBGC ( ehci, "EHCI %s claimed ownership from BIOS\n", + ehci->name ); + pci_read_config_dword ( pci, ( legacy + + EHCI_USBLEGSUP_CTLSTS ), + &ctlsts ); + if ( ctlsts ) { + DBGC ( ehci, "EHCI %s warning: BIOS retained " + "SMIs: %08x\n", ehci->name, ctlsts ); + } + return; + } + + /* Delay */ + mdelay ( 1 ); + } + + /* BIOS did not release ownership. Claim it forcibly by + * disabling all SMIs. + */ + DBGC ( ehci, "EHCI %s could not claim ownership from BIOS: forcibly " + "disabling SMIs\n", ehci->name ); + pci_write_config_dword ( pci, ( legacy + EHCI_USBLEGSUP_CTLSTS ), 0 ); +} + +/** + * Release ownership back to BIOS + * + * @v ehci EHCI device + * @v pci PCI device + */ +static void ehci_legacy_release ( struct ehci_device *ehci, + struct pci_device *pci ) { + + /* Do nothing unless legacy support capability is present */ + if ( ! ehci->legacy ) + return; + + /* Do nothing if releasing ownership is prevented */ + if ( ehci_legacy_prevent_release ) { + DBGC ( ehci, "EHCI %s not releasing ownership to BIOS\n", + ehci->name ); + return; + } + + /* Release ownership */ + pci_write_config_byte ( pci, ( ehci->legacy + EHCI_USBLEGSUP_OS ), 0 ); + DBGC ( ehci, "EHCI %s released ownership to BIOS\n", ehci->name ); +} + +/****************************************************************************** + * + * Companion controllers + * + ****************************************************************************** + */ + +/** + * Poll child companion controllers + * + * @v ehci EHCI device + */ +static void ehci_poll_companions ( struct ehci_device *ehci ) { + struct usb_bus *bus; + struct device_description *desc; + + /* Poll any USB buses belonging to child companion controllers */ + for_each_usb_bus ( bus ) { + + /* Get underlying devices description */ + desc = &bus->dev->desc; + + /* Skip buses that are not PCI devices */ + if ( desc->bus_type != BUS_TYPE_PCI ) + continue; + + /* Skip buses that are not part of the same PCI device */ + if ( PCI_FIRST_FUNC ( desc->location ) != + PCI_FIRST_FUNC ( ehci->bus->dev->desc.location ) ) + continue; + + /* Skip buses that are not UHCI or OHCI PCI devices */ + if ( ( desc->class != PCI_CLASS ( PCI_CLASS_SERIAL, + PCI_CLASS_SERIAL_USB, + PCI_CLASS_SERIAL_USB_UHCI ))&& + ( desc->class != PCI_CLASS ( PCI_CLASS_SERIAL, + PCI_CLASS_SERIAL_USB, + PCI_CLASS_SERIAL_USB_OHCI ) )) + continue; + + /* Poll child companion controller bus */ + DBGC2 ( ehci, "EHCI %s polling companion %s\n", + ehci->name, bus->name ); + usb_poll ( bus ); + } +} + +/** + * Locate EHCI companion controller + * + * @v pci PCI device + * @ret busdevfn EHCI companion controller bus:dev.fn (if any) + */ +unsigned int ehci_companion ( struct pci_device *pci ) { + struct pci_device tmp; + unsigned int busdevfn; + int rc; + + /* Look for an EHCI function on the same PCI device */ + busdevfn = pci->busdevfn; + while ( ++busdevfn <= PCI_LAST_FUNC ( pci->busdevfn ) ) { + pci_init ( &tmp, busdevfn ); + if ( ( rc = pci_read_config ( &tmp ) ) != 0 ) + continue; + if ( tmp.class == PCI_CLASS ( PCI_CLASS_SERIAL, + PCI_CLASS_SERIAL_USB, + PCI_CLASS_SERIAL_USB_EHCI ) ) + return busdevfn; + } + + return 0; +} + +/****************************************************************************** + * + * Run / stop / reset + * + ****************************************************************************** + */ + +/** + * Start EHCI device + * + * @v ehci EHCI device + */ +static void ehci_run ( struct ehci_device *ehci ) { + uint32_t usbcmd; + + /* Set run/stop bit */ + usbcmd = readl ( ehci->op + EHCI_OP_USBCMD ); + usbcmd &= ~EHCI_USBCMD_FLSIZE_MASK; + usbcmd |= ( EHCI_USBCMD_RUN | EHCI_USBCMD_FLSIZE ( ehci->flsize ) | + EHCI_USBCMD_PERIODIC | EHCI_USBCMD_ASYNC ); + writel ( usbcmd, ehci->op + EHCI_OP_USBCMD ); +} + +/** + * Stop EHCI device + * + * @v ehci EHCI device + * @ret rc Return status code + */ +static int ehci_stop ( struct ehci_device *ehci ) { + uint32_t usbcmd; + uint32_t usbsts; + unsigned int i; + + /* Clear run/stop bit */ + usbcmd = readl ( ehci->op + EHCI_OP_USBCMD ); + usbcmd &= ~( EHCI_USBCMD_RUN | EHCI_USBCMD_PERIODIC | + EHCI_USBCMD_ASYNC ); + writel ( usbcmd, ehci->op + EHCI_OP_USBCMD ); + + /* Wait for device to stop */ + for ( i = 0 ; i < EHCI_STOP_MAX_WAIT_MS ; i++ ) { + + /* Check if device is stopped */ + usbsts = readl ( ehci->op + EHCI_OP_USBSTS ); + if ( usbsts & EHCI_USBSTS_HCH ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( ehci, "EHCI %s timed out waiting for stop\n", ehci->name ); + return -ETIMEDOUT; +} + +/** + * Reset EHCI device + * + * @v ehci EHCI device + * @ret rc Return status code + */ +static int ehci_reset ( struct ehci_device *ehci ) { + uint32_t usbcmd; + unsigned int i; + int rc; + + /* The EHCI specification states that resetting a running + * device may result in undefined behaviour, so try stopping + * it first. + */ + if ( ( rc = ehci_stop ( ehci ) ) != 0 ) { + /* Ignore errors and attempt to reset the device anyway */ + } + + /* Reset device */ + writel ( EHCI_USBCMD_HCRST, ehci->op + EHCI_OP_USBCMD ); + + /* Wait for reset to complete */ + for ( i = 0 ; i < EHCI_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check if reset is complete */ + usbcmd = readl ( ehci->op + EHCI_OP_USBCMD ); + if ( ! ( usbcmd & EHCI_USBCMD_HCRST ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( ehci, "EHCI %s timed out waiting for reset\n", ehci->name ); + return -ETIMEDOUT; +} + +/****************************************************************************** + * + * Transfer descriptor rings + * + ****************************************************************************** + */ + +/** + * Allocate transfer descriptor ring + * + * @v ehci EHCI device + * @v ring Transfer descriptor ring + * @ret rc Return status code + */ +static int ehci_ring_alloc ( struct ehci_device *ehci, + struct ehci_ring *ring ) { + struct ehci_transfer_descriptor *desc; + struct ehci_transfer_descriptor *next; + unsigned int i; + size_t len; + uint32_t link; + int rc; + + /* Initialise structure */ + memset ( ring, 0, sizeof ( *ring ) ); + + /* Allocate I/O buffers */ + ring->iobuf = zalloc ( EHCI_RING_COUNT * sizeof ( ring->iobuf[0] ) ); + if ( ! ring->iobuf ) { + rc = -ENOMEM; + goto err_alloc_iobuf; + } + + /* Allocate queue head */ + ring->head = malloc_dma ( sizeof ( *ring->head ), + ehci_align ( sizeof ( *ring->head ) ) ); + if ( ! ring->head ) { + rc = -ENOMEM; + goto err_alloc_queue; + } + if ( ( rc = ehci_ctrl_reachable ( ehci, ring->head ) ) != 0 ) { + DBGC ( ehci, "EHCI %s queue head unreachable\n", ehci->name ); + goto err_unreachable_queue; + } + memset ( ring->head, 0, sizeof ( *ring->head ) ); + + /* Allocate transfer descriptors */ + len = ( EHCI_RING_COUNT * sizeof ( ring->desc[0] ) ); + ring->desc = malloc_dma ( len, sizeof ( ring->desc[0] ) ); + if ( ! ring->desc ) { + rc = -ENOMEM; + goto err_alloc_desc; + } + memset ( ring->desc, 0, len ); + + /* Initialise transfer descriptors */ + for ( i = 0 ; i < EHCI_RING_COUNT ; i++ ) { + desc = &ring->desc[i]; + if ( ( rc = ehci_ctrl_reachable ( ehci, desc ) ) != 0 ) { + DBGC ( ehci, "EHCI %s descriptor unreachable\n", + ehci->name ); + goto err_unreachable_desc; + } + next = &ring->desc[ ( i + 1 ) % EHCI_RING_COUNT ]; + link = virt_to_phys ( next ); + desc->next = cpu_to_le32 ( link ); + desc->alt = cpu_to_le32 ( link ); + } + + /* Initialise queue head */ + link = virt_to_phys ( &ring->desc[0] ); + ring->head->cache.next = cpu_to_le32 ( link ); + + return 0; + + err_unreachable_desc: + free_dma ( ring->desc, len ); + err_alloc_desc: + err_unreachable_queue: + free_dma ( ring->head, sizeof ( *ring->head ) ); + err_alloc_queue: + free ( ring->iobuf ); + err_alloc_iobuf: + return rc; +} + +/** + * Free transfer descriptor ring + * + * @v ring Transfer descriptor ring + */ +static void ehci_ring_free ( struct ehci_ring *ring ) { + unsigned int i; + + /* Sanity checks */ + assert ( ehci_ring_fill ( ring ) == 0 ); + for ( i = 0 ; i < EHCI_RING_COUNT ; i++ ) + assert ( ring->iobuf[i] == NULL ); + + /* Free transfer descriptors */ + free_dma ( ring->desc, ( EHCI_RING_COUNT * sizeof ( ring->desc[0] ) ) ); + + /* Free queue head */ + free_dma ( ring->head, sizeof ( *ring->head ) ); + + /* Free I/O buffers */ + free ( ring->iobuf ); +} + +/** + * Enqueue transfer descriptors + * + * @v ehci EHCI device + * @v ring Transfer descriptor ring + * @v iobuf I/O buffer + * @v xfers Transfers + * @v count Number of transfers + * @ret rc Return status code + */ +static int ehci_enqueue ( struct ehci_device *ehci, struct ehci_ring *ring, + struct io_buffer *iobuf, + const struct ehci_transfer *xfer, + unsigned int count ) { + struct ehci_transfer_descriptor *desc; + physaddr_t phys; + void *data; + size_t len; + size_t offset; + size_t frag_len; + unsigned int toggle; + unsigned int index; + unsigned int i; + + /* Sanity check */ + assert ( iobuf != NULL ); + assert ( count > 0 ); + + /* Fail if ring does not have sufficient space */ + if ( ehci_ring_remaining ( ring ) < count ) + return -ENOBUFS; + + /* Fail if any portion is unreachable */ + for ( i = 0 ; i < count ; i++ ) { + phys = ( virt_to_phys ( xfer[i].data ) + xfer[i].len - 1 ); + if ( ( phys > 0xffffffffUL ) && ( ! ehci->addr64 ) ) + return -ENOTSUP; + } + + /* Enqueue each transfer, recording the I/O buffer with the last */ + for ( ; count ; ring->prod++, xfer++ ) { + + /* Populate descriptor header */ + index = ( ring->prod % EHCI_RING_COUNT ); + desc = &ring->desc[index]; + toggle = ( xfer->flags & EHCI_FL_TOGGLE ); + assert ( xfer->len <= EHCI_LEN_MASK ); + assert ( EHCI_FL_TOGGLE == EHCI_LEN_TOGGLE ); + desc->len = cpu_to_le16 ( xfer->len | toggle ); + desc->flags = ( xfer->flags | EHCI_FL_CERR_MAX ); + + /* Populate buffer pointers */ + data = xfer->data; + len = xfer->len; + for ( i = 0 ; len ; i++ ) { + + /* Calculate length of this fragment */ + phys = virt_to_phys ( data ); + offset = ( phys & ( EHCI_PAGE_ALIGN - 1 ) ); + frag_len = ( EHCI_PAGE_ALIGN - offset ); + if ( frag_len > len ) + frag_len = len; + + /* Sanity checks */ + assert ( ( i == 0 ) || ( offset == 0 ) ); + assert ( i < ( sizeof ( desc->low ) / + sizeof ( desc->low[0] ) ) ); + + /* Populate buffer pointer */ + desc->low[i] = cpu_to_le32 ( phys ); + if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) { + desc->high[i] = + cpu_to_le32 ( ((uint64_t) phys) >> 32 ); + } + + /* Move to next fragment */ + data += frag_len; + len -= frag_len; + } + + /* Ensure everything is valid before activating descriptor */ + wmb(); + desc->status = EHCI_STATUS_ACTIVE; + + /* Record I/O buffer against last ring index */ + if ( --count == 0 ) + ring->iobuf[index] = iobuf; + } + + return 0; +} + +/** + * Dequeue a transfer descriptor + * + * @v ring Transfer descriptor ring + * @ret iobuf I/O buffer (or NULL) + */ +static struct io_buffer * ehci_dequeue ( struct ehci_ring *ring ) { + struct ehci_transfer_descriptor *desc; + struct io_buffer *iobuf; + unsigned int index = ( ring->cons % EHCI_RING_COUNT ); + + /* Sanity check */ + assert ( ehci_ring_fill ( ring ) > 0 ); + + /* Mark descriptor as inactive (and not halted) */ + desc = &ring->desc[index]; + desc->status = 0; + + /* Retrieve I/O buffer */ + iobuf = ring->iobuf[index]; + ring->iobuf[index] = NULL; + + /* Update consumer counter */ + ring->cons++; + + return iobuf; +} + +/****************************************************************************** + * + * Schedule management + * + ****************************************************************************** + */ + +/** + * Get link value for a queue head + * + * @v queue Queue head + * @ret link Link value + */ +static inline uint32_t ehci_link_qh ( struct ehci_queue_head *queue ) { + + return ( virt_to_phys ( queue ) | EHCI_LINK_TYPE_QH ); +} + +/** + * (Re)build asynchronous schedule + * + * @v ehci EHCI device + */ +static void ehci_async_schedule ( struct ehci_device *ehci ) { + struct ehci_endpoint *endpoint; + struct ehci_queue_head *queue; + uint32_t link; + + /* Build schedule in reverse order of execution. Provided + * that we only ever add or remove single endpoints, this can + * safely run concurrently with hardware execution of the + * schedule. + */ + link = ehci_link_qh ( ehci->head ); + list_for_each_entry_reverse ( endpoint, &ehci->async, schedule ) { + queue = endpoint->ring.head; + queue->link = cpu_to_le32 ( link ); + wmb(); + link = ehci_link_qh ( queue ); + } + ehci->head->link = cpu_to_le32 ( link ); + wmb(); +} + +/** + * Add endpoint to asynchronous schedule + * + * @v endpoint Endpoint + */ +static void ehci_async_add ( struct ehci_endpoint *endpoint ) { + struct ehci_device *ehci = endpoint->ehci; + + /* Add to end of schedule */ + list_add_tail ( &endpoint->schedule, &ehci->async ); + + /* Rebuild schedule */ + ehci_async_schedule ( ehci ); +} + +/** + * Remove endpoint from asynchronous schedule + * + * @v endpoint Endpoint + * @ret rc Return status code + */ +static int ehci_async_del ( struct ehci_endpoint *endpoint ) { + struct ehci_device *ehci = endpoint->ehci; + uint32_t usbcmd; + uint32_t usbsts; + unsigned int i; + + /* Remove from schedule */ + list_check_contains_entry ( endpoint, &ehci->async, schedule ); + list_del ( &endpoint->schedule ); + + /* Rebuild schedule */ + ehci_async_schedule ( ehci ); + + /* Request notification when asynchronous schedule advances */ + usbcmd = readl ( ehci->op + EHCI_OP_USBCMD ); + usbcmd |= EHCI_USBCMD_ASYNC_ADVANCE; + writel ( usbcmd, ehci->op + EHCI_OP_USBCMD ); + + /* Wait for asynchronous schedule to advance */ + for ( i = 0 ; i < EHCI_ASYNC_ADVANCE_MAX_WAIT_MS ; i++ ) { + + /* Check for asynchronous schedule advancing */ + usbsts = readl ( ehci->op + EHCI_OP_USBSTS ); + if ( usbsts & EHCI_USBSTS_ASYNC_ADVANCE ) { + usbsts &= ~EHCI_USBSTS_CHANGE; + usbsts |= EHCI_USBSTS_ASYNC_ADVANCE; + writel ( usbsts, ehci->op + EHCI_OP_USBSTS ); + return 0; + } + + /* Delay */ + mdelay ( 1 ); + } + + /* Bad things will probably happen now */ + DBGC ( ehci, "EHCI %s timed out waiting for asynchronous schedule " + "to advance\n", ehci->name ); + return -ETIMEDOUT; +} + +/** + * (Re)build periodic schedule + * + * @v ehci EHCI device + */ +static void ehci_periodic_schedule ( struct ehci_device *ehci ) { + struct ehci_endpoint *endpoint; + struct ehci_queue_head *queue; + uint32_t link; + unsigned int frames; + unsigned int max_interval; + unsigned int i; + + /* Build schedule in reverse order of execution. Provided + * that we only ever add or remove single endpoints, this can + * safely run concurrently with hardware execution of the + * schedule. + */ + DBGCP ( ehci, "EHCI %s periodic schedule: ", ehci->name ); + link = EHCI_LINK_TERMINATE; + list_for_each_entry_reverse ( endpoint, &ehci->periodic, schedule ) { + queue = endpoint->ring.head; + queue->link = cpu_to_le32 ( link ); + wmb(); + DBGCP ( ehci, "%s%d", + ( ( link == EHCI_LINK_TERMINATE ) ? "" : "<-" ), + endpoint->ep->interval ); + link = ehci_link_qh ( queue ); + } + DBGCP ( ehci, "\n" ); + + /* Populate periodic frame list */ + DBGCP ( ehci, "EHCI %s periodic frame list:", ehci->name ); + frames = EHCI_PERIODIC_FRAMES ( ehci->flsize ); + for ( i = 0 ; i < frames ; i++ ) { + + /* Calculate maximum interval (in microframes) which + * may appear as part of this frame list. + */ + if ( i == 0 ) { + /* Start of list: include all endpoints */ + max_interval = -1U; + } else { + /* Calculate highest power-of-two frame interval */ + max_interval = ( 1 << ( ffs ( i ) - 1 ) ); + /* Convert to microframes */ + max_interval <<= 3; + /* Round up to nearest 2^n-1 */ + max_interval = ( ( max_interval << 1 ) - 1 ); + } + + /* Find first endpoint in schedule satisfying this + * maximum interval constraint. + */ + link = EHCI_LINK_TERMINATE; + list_for_each_entry ( endpoint, &ehci->periodic, schedule ) { + if ( endpoint->ep->interval <= max_interval ) { + queue = endpoint->ring.head; + link = ehci_link_qh ( queue ); + DBGCP ( ehci, " %d:%d", + i, endpoint->ep->interval ); + break; + } + } + ehci->frame[i].link = cpu_to_le32 ( link ); + } + wmb(); + DBGCP ( ehci, "\n" ); +} + +/** + * Add endpoint to periodic schedule + * + * @v endpoint Endpoint + */ +static void ehci_periodic_add ( struct ehci_endpoint *endpoint ) { + struct ehci_device *ehci = endpoint->ehci; + struct ehci_endpoint *before; + unsigned int interval = endpoint->ep->interval; + + /* Find first endpoint with a smaller interval */ + list_for_each_entry ( before, &ehci->periodic, schedule ) { + if ( before->ep->interval < interval ) + break; + } + list_add_tail ( &endpoint->schedule, &before->schedule ); + + /* Rebuild schedule */ + ehci_periodic_schedule ( ehci ); +} + +/** + * Remove endpoint from periodic schedule + * + * @v endpoint Endpoint + * @ret rc Return status code + */ +static int ehci_periodic_del ( struct ehci_endpoint *endpoint ) { + struct ehci_device *ehci = endpoint->ehci; + + /* Remove from schedule */ + list_check_contains_entry ( endpoint, &ehci->periodic, schedule ); + list_del ( &endpoint->schedule ); + + /* Rebuild schedule */ + ehci_periodic_schedule ( ehci ); + + /* Delay for a whole USB frame (with a 100% safety margin) */ + mdelay ( 2 ); + + return 0; +} + +/** + * Add endpoint to appropriate schedule + * + * @v endpoint Endpoint + */ +static void ehci_schedule_add ( struct ehci_endpoint *endpoint ) { + struct usb_endpoint *ep = endpoint->ep; + unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); + + if ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) { + ehci_periodic_add ( endpoint ); + } else { + ehci_async_add ( endpoint ); + } +} + +/** + * Remove endpoint from appropriate schedule + * + * @v endpoint Endpoint + * @ret rc Return status code + */ +static int ehci_schedule_del ( struct ehci_endpoint *endpoint ) { + struct usb_endpoint *ep = endpoint->ep; + unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); + + if ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) { + return ehci_periodic_del ( endpoint ); + } else { + return ehci_async_del ( endpoint ); + } +} + +/****************************************************************************** + * + * Endpoint operations + * + ****************************************************************************** + */ + +/** + * Determine endpoint characteristics + * + * @v ep USB endpoint + * @ret chr Endpoint characteristics + */ +static uint32_t ehci_endpoint_characteristics ( struct usb_endpoint *ep ) { + struct usb_device *usb = ep->usb; + unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); + uint32_t chr; + + /* Determine basic characteristics */ + chr = ( EHCI_CHR_ADDRESS ( usb->address ) | + EHCI_CHR_ENDPOINT ( ep->address ) | + EHCI_CHR_MAX_LEN ( ep->mtu ) ); + + /* Control endpoints require manual control of the data toggle */ + if ( attr == USB_ENDPOINT_ATTR_CONTROL ) + chr |= EHCI_CHR_TOGGLE; + + /* Determine endpoint speed */ + if ( usb->port->speed == USB_SPEED_HIGH ) { + chr |= EHCI_CHR_EPS_HIGH; + } else { + if ( usb->port->speed == USB_SPEED_FULL ) { + chr |= EHCI_CHR_EPS_FULL; + } else { + chr |= EHCI_CHR_EPS_LOW; + } + if ( attr == USB_ENDPOINT_ATTR_CONTROL ) + chr |= EHCI_CHR_CONTROL; + } + + return chr; +} + +/** + * Determine endpoint capabilities + * + * @v ep USB endpoint + * @ret cap Endpoint capabilities + */ +static uint32_t ehci_endpoint_capabilities ( struct usb_endpoint *ep ) { + struct usb_device *usb = ep->usb; + struct usb_port *tt = usb_transaction_translator ( usb ); + unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); + uint32_t cap; + unsigned int i; + + /* Determine basic capabilities */ + cap = EHCI_CAP_MULT ( ep->burst + 1 ); + + /* Determine interrupt schedule mask, if applicable */ + if ( ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) && + ( ( ep->interval != 0 ) /* avoid infinite loop */ ) ) { + for ( i = 0 ; i < 8 /* microframes per frame */ ; + i += ep->interval ) { + cap |= EHCI_CAP_INTR_SCHED ( i ); + } + } + + /* Set transaction translator hub address and port, if applicable */ + if ( tt ) { + assert ( tt->hub->usb ); + cap |= ( EHCI_CAP_TT_HUB ( tt->hub->usb->address ) | + EHCI_CAP_TT_PORT ( tt->address ) ); + if ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) + cap |= EHCI_CAP_SPLIT_SCHED_DEFAULT; + } + + return cap; +} + +/** + * Update endpoint characteristics and capabilities + * + * @v ep USB endpoint + */ +static void ehci_endpoint_update ( struct usb_endpoint *ep ) { + struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct ehci_queue_head *head; + + /* Update queue characteristics and capabilities */ + head = endpoint->ring.head; + head->chr = cpu_to_le32 ( ehci_endpoint_characteristics ( ep ) ); + head->cap = cpu_to_le32 ( ehci_endpoint_capabilities ( ep ) ); +} + +/** + * Open endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int ehci_endpoint_open ( struct usb_endpoint *ep ) { + struct usb_device *usb = ep->usb; + struct ehci_device *ehci = usb_get_hostdata ( usb ); + struct ehci_endpoint *endpoint; + int rc; + + /* Allocate and initialise structure */ + endpoint = zalloc ( sizeof ( *endpoint ) ); + if ( ! endpoint ) { + rc = -ENOMEM; + goto err_alloc; + } + endpoint->ehci = ehci; + endpoint->ep = ep; + usb_endpoint_set_hostdata ( ep, endpoint ); + + /* Initialise descriptor ring */ + if ( ( rc = ehci_ring_alloc ( ehci, &endpoint->ring ) ) != 0 ) + goto err_ring_alloc; + + /* Update queue characteristics and capabilities */ + ehci_endpoint_update ( ep ); + + /* Add to list of endpoints */ + list_add_tail ( &endpoint->list, &ehci->endpoints ); + + /* Add to schedule */ + ehci_schedule_add ( endpoint ); + + return 0; + + ehci_ring_free ( &endpoint->ring ); + err_ring_alloc: + free ( endpoint ); + err_alloc: + return rc; +} + +/** + * Close endpoint + * + * @v ep USB endpoint + */ +static void ehci_endpoint_close ( struct usb_endpoint *ep ) { + struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct ehci_device *ehci = endpoint->ehci; + struct usb_device *usb = ep->usb; + struct io_buffer *iobuf; + int rc; + + /* Remove from schedule */ + if ( ( rc = ehci_schedule_del ( endpoint ) ) != 0 ) { + /* No way to prevent hardware from continuing to + * access the memory, so leak it. + */ + DBGC ( ehci, "EHCI %s %s could not unschedule: %s\n", + usb->name, usb_endpoint_name ( ep ), strerror ( rc ) ); + return; + } + + /* Cancel any incomplete transfers */ + while ( ehci_ring_fill ( &endpoint->ring ) ) { + iobuf = ehci_dequeue ( &endpoint->ring ); + if ( iobuf ) + usb_complete_err ( ep, iobuf, -ECANCELED ); + } + + /* Remove from list of endpoints */ + list_del ( &endpoint->list ); + + /* Free descriptor ring */ + ehci_ring_free ( &endpoint->ring ); + + /* Free endpoint */ + free ( endpoint ); +} + +/** + * Reset endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int ehci_endpoint_reset ( struct usb_endpoint *ep ) { + struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct ehci_ring *ring = &endpoint->ring; + struct ehci_transfer_descriptor *cache = &ring->head->cache; + uint32_t link; + + /* Sanity checks */ + assert ( ! ( cache->status & EHCI_STATUS_ACTIVE ) ); + assert ( cache->status & EHCI_STATUS_HALTED ); + + /* Reset residual count */ + ring->residual = 0; + + /* Reset data toggle */ + cache->len = 0; + + /* Prepare to restart at next unconsumed descriptor */ + link = virt_to_phys ( &ring->desc[ ring->cons % EHCI_RING_COUNT ] ); + cache->next = cpu_to_le32 ( link ); + + /* Restart ring */ + wmb(); + cache->status = 0; + + return 0; +} + +/** + * Update MTU + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int ehci_endpoint_mtu ( struct usb_endpoint *ep ) { + + /* Update endpoint characteristics and capabilities */ + ehci_endpoint_update ( ep ); + + return 0; +} + +/** + * Enqueue message transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int ehci_endpoint_message ( struct usb_endpoint *ep, + struct io_buffer *iobuf ) { + struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct ehci_device *ehci = endpoint->ehci; + struct usb_setup_packet *packet; + unsigned int input; + struct ehci_transfer xfers[3]; + struct ehci_transfer *xfer = xfers; + size_t len; + int rc; + + /* Construct setup stage */ + assert ( iob_len ( iobuf ) >= sizeof ( *packet ) ); + packet = iobuf->data; + iob_pull ( iobuf, sizeof ( *packet ) ); + xfer->data = packet; + xfer->len = sizeof ( *packet ); + xfer->flags = EHCI_FL_PID_SETUP; + xfer++; + + /* Construct data stage, if applicable */ + len = iob_len ( iobuf ); + input = ( packet->request & cpu_to_le16 ( USB_DIR_IN ) ); + if ( len ) { + xfer->data = iobuf->data; + xfer->len = len; + xfer->flags = ( EHCI_FL_TOGGLE | + ( input ? EHCI_FL_PID_IN : EHCI_FL_PID_OUT ) ); + xfer++; + } + + /* Construct status stage */ + xfer->data = NULL; + xfer->len = 0; + xfer->flags = ( EHCI_FL_TOGGLE | EHCI_FL_IOC | + ( ( len && input ) ? EHCI_FL_PID_OUT : EHCI_FL_PID_IN)); + xfer++; + + /* Enqueue transfer */ + if ( ( rc = ehci_enqueue ( ehci, &endpoint->ring, iobuf, xfers, + ( xfer - xfers ) ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Enqueue stream transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v terminate Terminate using a short packet + * @ret rc Return status code + */ +static int ehci_endpoint_stream ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int terminate ) { + struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct ehci_device *ehci = endpoint->ehci; + unsigned int input = ( ep->address & USB_DIR_IN ); + struct ehci_transfer xfers[2]; + struct ehci_transfer *xfer = xfers; + size_t len = iob_len ( iobuf ); + int rc; + + /* Create transfer */ + xfer->data = iobuf->data; + xfer->len = len; + xfer->flags = ( EHCI_FL_IOC | + ( input ? EHCI_FL_PID_IN : EHCI_FL_PID_OUT ) ); + xfer++; + if ( terminate && ( ( len & ( ep->mtu - 1 ) ) == 0 ) ) { + xfer->data = NULL; + xfer->len = 0; + assert ( ! input ); + xfer->flags = ( EHCI_FL_IOC | EHCI_FL_PID_OUT ); + xfer++; + } + + /* Enqueue transfer */ + if ( ( rc = ehci_enqueue ( ehci, &endpoint->ring, iobuf, xfers, + ( xfer - xfers ) ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Poll for completions + * + * @v endpoint Endpoint + */ +static void ehci_endpoint_poll ( struct ehci_endpoint *endpoint ) { + struct ehci_device *ehci = endpoint->ehci; + struct ehci_ring *ring = &endpoint->ring; + struct ehci_transfer_descriptor *desc; + struct usb_endpoint *ep = endpoint->ep; + struct usb_device *usb = ep->usb; + struct io_buffer *iobuf; + unsigned int index; + unsigned int status; + int rc; + + /* Consume all completed descriptors */ + while ( ehci_ring_fill ( &endpoint->ring ) ) { + + /* Stop if we reach an uncompleted descriptor */ + rmb(); + index = ( ring->cons % EHCI_RING_COUNT ); + desc = &ring->desc[index]; + status = desc->status; + if ( status & EHCI_STATUS_ACTIVE ) + break; + + /* Consume this descriptor */ + iobuf = ehci_dequeue ( ring ); + + /* If we have encountered an error, then consume all + * remaining descriptors in this transaction, report + * the error to the USB core, and stop further + * processing. + */ + if ( status & EHCI_STATUS_HALTED ) { + rc = -EIO_STATUS ( status ); + DBGC ( ehci, "EHCI %s %s completion %d failed (status " + "%02x): %s\n", usb->name, + usb_endpoint_name ( ep ), index, status, + strerror ( rc ) ); + while ( ! iobuf ) + iobuf = ehci_dequeue ( ring ); + usb_complete_err ( endpoint->ep, iobuf, rc ); + return; + } + + /* Accumulate residual data count */ + ring->residual += ( le16_to_cpu ( desc->len ) & EHCI_LEN_MASK ); + + /* If this is not the end of a transaction (i.e. has + * no I/O buffer), then continue to next descriptor. + */ + if ( ! iobuf ) + continue; + + /* Update I/O buffer length */ + iob_unput ( iobuf, ring->residual ); + ring->residual = 0; + + /* Report completion to USB core */ + usb_complete ( endpoint->ep, iobuf ); + } +} + +/****************************************************************************** + * + * Device operations + * + ****************************************************************************** + */ + +/** + * Open device + * + * @v usb USB device + * @ret rc Return status code + */ +static int ehci_device_open ( struct usb_device *usb ) { + struct ehci_device *ehci = usb_bus_get_hostdata ( usb->port->hub->bus ); + + usb_set_hostdata ( usb, ehci ); + return 0; +} + +/** + * Close device + * + * @v usb USB device + */ +static void ehci_device_close ( struct usb_device *usb ) { + struct ehci_device *ehci = usb_get_hostdata ( usb ); + struct usb_bus *bus = ehci->bus; + + /* Free device address, if assigned */ + if ( usb->address ) + usb_free_address ( bus, usb->address ); +} + +/** + * Assign device address + * + * @v usb USB device + * @ret rc Return status code + */ +static int ehci_device_address ( struct usb_device *usb ) { + struct ehci_device *ehci = usb_get_hostdata ( usb ); + struct usb_bus *bus = ehci->bus; + struct usb_endpoint *ep0 = usb_endpoint ( usb, USB_EP0_ADDRESS ); + int address; + int rc; + + /* Sanity checks */ + assert ( usb->address == 0 ); + assert ( ep0 != NULL ); + + /* Allocate device address */ + address = usb_alloc_address ( bus ); + if ( address < 0 ) { + rc = address; + DBGC ( ehci, "EHCI %s could not allocate address: %s\n", + usb->name, strerror ( rc ) ); + goto err_alloc_address; + } + + /* Set address */ + if ( ( rc = usb_set_address ( usb, address ) ) != 0 ) + goto err_set_address; + + /* Update device address */ + usb->address = address; + + /* Update control endpoint characteristics and capabilities */ + ehci_endpoint_update ( ep0 ); + + return 0; + + err_set_address: + usb_free_address ( bus, address ); + err_alloc_address: + return rc; +} + +/****************************************************************************** + * + * Hub operations + * + ****************************************************************************** + */ + +/** + * Open hub + * + * @v hub USB hub + * @ret rc Return status code + */ +static int ehci_hub_open ( struct usb_hub *hub __unused ) { + + /* Nothing to do */ + return 0; +} + +/** + * Close hub + * + * @v hub USB hub + */ +static void ehci_hub_close ( struct usb_hub *hub __unused ) { + + /* Nothing to do */ +} + +/****************************************************************************** + * + * Root hub operations + * + ****************************************************************************** + */ + +/** + * Open root hub + * + * @v hub USB hub + * @ret rc Return status code + */ +static int ehci_root_open ( struct usb_hub *hub ) { + struct usb_bus *bus = hub->bus; + struct ehci_device *ehci = usb_bus_get_hostdata ( bus ); + uint32_t portsc; + unsigned int i; + + /* Route all ports to EHCI controller */ + writel ( EHCI_CONFIGFLAG_CF, ehci->op + EHCI_OP_CONFIGFLAG ); + + /* Enable power to all ports */ + for ( i = 1 ; i <= ehci->ports ; i++ ) { + portsc = readl ( ehci->op + EHCI_OP_PORTSC ( i ) ); + portsc &= ~EHCI_PORTSC_CHANGE; + portsc |= EHCI_PORTSC_PP; + writel ( portsc, ehci->op + EHCI_OP_PORTSC ( i ) ); + } + + /* Wait 20ms after potentially enabling power to a port */ + mdelay ( EHCI_PORT_POWER_DELAY_MS ); + + /* Record hub driver private data */ + usb_hub_set_drvdata ( hub, ehci ); + + return 0; +} + +/** + * Close root hub + * + * @v hub USB hub + */ +static void ehci_root_close ( struct usb_hub *hub ) { + struct ehci_device *ehci = usb_hub_get_drvdata ( hub ); + + /* Route all ports back to companion controllers */ + writel ( 0, ehci->op + EHCI_OP_CONFIGFLAG ); + + /* Clear hub driver private data */ + usb_hub_set_drvdata ( hub, NULL ); +} + +/** + * Enable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int ehci_root_enable ( struct usb_hub *hub, struct usb_port *port ) { + struct ehci_device *ehci = usb_hub_get_drvdata ( hub ); + uint32_t portsc; + unsigned int line; + unsigned int i; + + /* Check for a low-speed device */ + portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) ); + line = EHCI_PORTSC_LINE_STATUS ( portsc ); + if ( line == EHCI_PORTSC_LINE_STATUS_LOW ) { + DBGC ( ehci, "EHCI %s-%d detected low-speed device: " + "disowning\n", ehci->name, port->address ); + goto disown; + } + + /* Reset port */ + portsc &= ~( EHCI_PORTSC_PED | EHCI_PORTSC_CHANGE ); + portsc |= EHCI_PORTSC_PR; + writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) ); + mdelay ( USB_RESET_DELAY_MS ); + portsc &= ~EHCI_PORTSC_PR; + writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) ); + + /* Wait for reset to complete */ + for ( i = 0 ; i < EHCI_PORT_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check port status */ + portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) ); + if ( ! ( portsc & EHCI_PORTSC_PR ) ) { + if ( portsc & EHCI_PORTSC_PED ) + return 0; + DBGC ( ehci, "EHCI %s-%d not enabled after reset: " + "disowning\n", ehci->name, port->address ); + goto disown; + } + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( ehci, "EHCI %s-%d timed out waiting for port to reset\n", + ehci->name, port->address ); + return -ETIMEDOUT; + + disown: + /* Disown port */ + portsc &= ~EHCI_PORTSC_CHANGE; + portsc |= EHCI_PORTSC_OWNER; + writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) ); + + /* Delay to allow child companion controllers to settle */ + mdelay ( EHCI_DISOWN_DELAY_MS ); + + /* Poll child companion controllers */ + ehci_poll_companions ( ehci ); + + return -ENODEV; +} + +/** + * Disable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int ehci_root_disable ( struct usb_hub *hub, struct usb_port *port ) { + struct ehci_device *ehci = usb_hub_get_drvdata ( hub ); + uint32_t portsc; + + /* Disable port */ + portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) ); + portsc &= ~( EHCI_PORTSC_PED | EHCI_PORTSC_CHANGE ); + writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) ); + + return 0; +} + +/** + * Update root hub port speed + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int ehci_root_speed ( struct usb_hub *hub, struct usb_port *port ) { + struct ehci_device *ehci = usb_hub_get_drvdata ( hub ); + uint32_t portsc; + unsigned int speed; + unsigned int line; + int ccs; + int csc; + int ped; + + /* Read port status */ + portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) ); + DBGC2 ( ehci, "EHCI %s-%d status is %08x\n", + ehci->name, port->address, portsc ); + ccs = ( portsc & EHCI_PORTSC_CCS ); + csc = ( portsc & EHCI_PORTSC_CSC ); + ped = ( portsc & EHCI_PORTSC_PED ); + line = EHCI_PORTSC_LINE_STATUS ( portsc ); + + /* Record disconnections and clear changes */ + port->disconnected |= csc; + writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) ); + + /* Determine port speed */ + if ( ! ccs ) { + /* Port not connected */ + speed = USB_SPEED_NONE; + } else if ( line == EHCI_PORTSC_LINE_STATUS_LOW ) { + /* Detected as low-speed */ + speed = USB_SPEED_LOW; + } else if ( ped ) { + /* Port already enabled: must be high-speed */ + speed = USB_SPEED_HIGH; + } else { + /* Not low-speed and not yet enabled. Could be either + * full-speed or high-speed; we can't yet tell. + */ + speed = USB_SPEED_FULL; + } + port->speed = speed; + return 0; +} + +/** + * Clear transaction translator buffer + * + * @v hub USB hub + * @v port USB port + * @v ep USB endpoint + * @ret rc Return status code + */ +static int ehci_root_clear_tt ( struct usb_hub *hub, struct usb_port *port, + struct usb_endpoint *ep ) { + struct ehci_device *ehci = usb_hub_get_drvdata ( hub ); + + /* Should never be called; this is a root hub */ + DBGC ( ehci, "EHCI %s-%d nonsensical CLEAR_TT for %s %s\n", ehci->name, + port->address, ep->usb->name, usb_endpoint_name ( ep ) ); + + return -ENOTSUP; +} + +/** + * Poll for port status changes + * + * @v hub USB hub + * @v port USB port + */ +static void ehci_root_poll ( struct usb_hub *hub, struct usb_port *port ) { + struct ehci_device *ehci = usb_hub_get_drvdata ( hub ); + uint32_t portsc; + uint32_t change; + + /* Do nothing unless something has changed */ + portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) ); + change = ( portsc & EHCI_PORTSC_CHANGE ); + if ( ! change ) + return; + + /* Record disconnections and clear changes */ + port->disconnected |= ( portsc & EHCI_PORTSC_CSC ); + writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) ); + + /* Report port status change */ + usb_port_changed ( port ); +} + +/****************************************************************************** + * + * Bus operations + * + ****************************************************************************** + */ + +/** + * Open USB bus + * + * @v bus USB bus + * @ret rc Return status code + */ +static int ehci_bus_open ( struct usb_bus *bus ) { + struct ehci_device *ehci = usb_bus_get_hostdata ( bus ); + unsigned int frames; + size_t len; + int rc; + + /* Sanity checks */ + assert ( list_empty ( &ehci->async ) ); + assert ( list_empty ( &ehci->periodic ) ); + + /* Allocate and initialise asynchronous queue head */ + ehci->head = malloc_dma ( sizeof ( *ehci->head ), + ehci_align ( sizeof ( *ehci->head ) ) ); + if ( ! ehci->head ) { + rc = -ENOMEM; + goto err_alloc_head; + } + memset ( ehci->head, 0, sizeof ( *ehci->head ) ); + ehci->head->chr = cpu_to_le32 ( EHCI_CHR_HEAD ); + ehci->head->cache.next = cpu_to_le32 ( EHCI_LINK_TERMINATE ); + ehci->head->cache.status = EHCI_STATUS_HALTED; + ehci_async_schedule ( ehci ); + writel ( virt_to_phys ( ehci->head ), + ehci->op + EHCI_OP_ASYNCLISTADDR ); + + /* Use async queue head to determine control data structure segment */ + ehci->ctrldssegment = + ( ( ( uint64_t ) virt_to_phys ( ehci->head ) ) >> 32 ); + if ( ehci->addr64 ) { + writel ( ehci->ctrldssegment, ehci->op + EHCI_OP_CTRLDSSEGMENT); + } else if ( ehci->ctrldssegment ) { + DBGC ( ehci, "EHCI %s CTRLDSSEGMENT not supported\n", + ehci->name ); + rc = -ENOTSUP; + goto err_ctrldssegment; + } + + /* Allocate periodic frame list */ + frames = EHCI_PERIODIC_FRAMES ( ehci->flsize ); + len = ( frames * sizeof ( ehci->frame[0] ) ); + ehci->frame = malloc_dma ( len, EHCI_PAGE_ALIGN ); + if ( ! ehci->frame ) { + rc = -ENOMEM; + goto err_alloc_frame; + } + if ( ( rc = ehci_ctrl_reachable ( ehci, ehci->frame ) ) != 0 ) { + DBGC ( ehci, "EHCI %s frame list unreachable\n", ehci->name ); + goto err_unreachable_frame; + } + ehci_periodic_schedule ( ehci ); + writel ( virt_to_phys ( ehci->frame ), + ehci->op + EHCI_OP_PERIODICLISTBASE ); + + /* Start controller */ + ehci_run ( ehci ); + + return 0; + + ehci_stop ( ehci ); + err_unreachable_frame: + free_dma ( ehci->frame, len ); + err_alloc_frame: + err_ctrldssegment: + free_dma ( ehci->head, sizeof ( *ehci->head ) ); + err_alloc_head: + return rc; +} + +/** + * Close USB bus + * + * @v bus USB bus + */ +static void ehci_bus_close ( struct usb_bus *bus ) { + struct ehci_device *ehci = usb_bus_get_hostdata ( bus ); + unsigned int frames = EHCI_PERIODIC_FRAMES ( ehci->flsize ); + + /* Sanity checks */ + assert ( list_empty ( &ehci->async ) ); + assert ( list_empty ( &ehci->periodic ) ); + + /* Stop controller */ + ehci_stop ( ehci ); + + /* Free periodic frame list */ + free_dma ( ehci->frame, ( frames * sizeof ( ehci->frame[0] ) ) ); + + /* Free asynchronous schedule */ + free_dma ( ehci->head, sizeof ( *ehci->head ) ); +} + +/** + * Poll USB bus + * + * @v bus USB bus + */ +static void ehci_bus_poll ( struct usb_bus *bus ) { + struct ehci_device *ehci = usb_bus_get_hostdata ( bus ); + struct usb_hub *hub = bus->hub; + struct ehci_endpoint *endpoint; + unsigned int i; + uint32_t usbsts; + uint32_t change; + + /* Do nothing unless something has changed */ + usbsts = readl ( ehci->op + EHCI_OP_USBSTS ); + assert ( usbsts & EHCI_USBSTS_ASYNC ); + assert ( usbsts & EHCI_USBSTS_PERIODIC ); + assert ( ! ( usbsts & EHCI_USBSTS_HCH ) ); + change = ( usbsts & EHCI_USBSTS_CHANGE ); + if ( ! change ) + return; + + /* Acknowledge changes */ + writel ( usbsts, ehci->op + EHCI_OP_USBSTS ); + + /* Process completions, if applicable */ + if ( change & ( EHCI_USBSTS_USBINT | EHCI_USBSTS_USBERRINT ) ) { + + /* Iterate over all endpoints looking for completed + * descriptors. We trust that completion handlers are + * minimal and will not do anything that could + * plausibly affect the endpoint list itself. + */ + list_for_each_entry ( endpoint, &ehci->endpoints, list ) + ehci_endpoint_poll ( endpoint ); + } + + /* Process port status changes, if applicable */ + if ( change & EHCI_USBSTS_PORT ) { + + /* Iterate over all ports looking for status changes */ + for ( i = 1 ; i <= ehci->ports ; i++ ) + ehci_root_poll ( hub, usb_port ( hub, i ) ); + } + + /* Report fatal errors */ + if ( change & EHCI_USBSTS_SYSERR ) + DBGC ( ehci, "EHCI %s host system error\n", ehci->name ); +} + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** USB host controller operations */ +static struct usb_host_operations ehci_operations = { + .endpoint = { + .open = ehci_endpoint_open, + .close = ehci_endpoint_close, + .reset = ehci_endpoint_reset, + .mtu = ehci_endpoint_mtu, + .message = ehci_endpoint_message, + .stream = ehci_endpoint_stream, + }, + .device = { + .open = ehci_device_open, + .close = ehci_device_close, + .address = ehci_device_address, + }, + .bus = { + .open = ehci_bus_open, + .close = ehci_bus_close, + .poll = ehci_bus_poll, + }, + .hub = { + .open = ehci_hub_open, + .close = ehci_hub_close, + }, + .root = { + .open = ehci_root_open, + .close = ehci_root_close, + .enable = ehci_root_enable, + .disable = ehci_root_disable, + .speed = ehci_root_speed, + .clear_tt = ehci_root_clear_tt, + }, +}; + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int ehci_probe ( struct pci_device *pci ) { + struct ehci_device *ehci; + struct usb_port *port; + unsigned long bar_start; + size_t bar_size; + unsigned int i; + int rc; + + /* Allocate and initialise structure */ + ehci = zalloc ( sizeof ( *ehci ) ); + if ( ! ehci ) { + rc = -ENOMEM; + goto err_alloc; + } + ehci->name = pci->dev.name; + INIT_LIST_HEAD ( &ehci->endpoints ); + INIT_LIST_HEAD ( &ehci->async ); + INIT_LIST_HEAD ( &ehci->periodic ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + bar_start = pci_bar_start ( pci, EHCI_BAR ); + bar_size = pci_bar_size ( pci, EHCI_BAR ); + ehci->regs = ioremap ( bar_start, bar_size ); + if ( ! ehci->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Initialise EHCI device */ + ehci_init ( ehci, ehci->regs ); + + /* Initialise USB legacy support and claim ownership */ + ehci_legacy_init ( ehci, pci ); + ehci_legacy_claim ( ehci, pci ); + + /* Reset device */ + if ( ( rc = ehci_reset ( ehci ) ) != 0 ) + goto err_reset; + + /* Allocate USB bus */ + ehci->bus = alloc_usb_bus ( &pci->dev, ehci->ports, EHCI_MTU, + &ehci_operations ); + if ( ! ehci->bus ) { + rc = -ENOMEM; + goto err_alloc_bus; + } + usb_bus_set_hostdata ( ehci->bus, ehci ); + usb_hub_set_drvdata ( ehci->bus->hub, ehci ); + + /* Set port protocols */ + for ( i = 1 ; i <= ehci->ports ; i++ ) { + port = usb_port ( ehci->bus->hub, i ); + port->protocol = USB_PROTO_2_0; + } + + /* Register USB bus */ + if ( ( rc = register_usb_bus ( ehci->bus ) ) != 0 ) + goto err_register; + + pci_set_drvdata ( pci, ehci ); + return 0; + + unregister_usb_bus ( ehci->bus ); + err_register: + free_usb_bus ( ehci->bus ); + err_alloc_bus: + ehci_reset ( ehci ); + err_reset: + ehci_legacy_release ( ehci, pci ); + iounmap ( ehci->regs ); + err_ioremap: + free ( ehci ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void ehci_remove ( struct pci_device *pci ) { + struct ehci_device *ehci = pci_get_drvdata ( pci ); + struct usb_bus *bus = ehci->bus; + + unregister_usb_bus ( bus ); + assert ( list_empty ( &ehci->async ) ); + assert ( list_empty ( &ehci->periodic ) ); + free_usb_bus ( bus ); + ehci_reset ( ehci ); + ehci_legacy_release ( ehci, pci ); + iounmap ( ehci->regs ); + free ( ehci ); +} + +/** EHCI PCI device IDs */ +static struct pci_device_id ehci_ids[] = { + PCI_ROM ( 0xffff, 0xffff, "ehci", "EHCI", 0 ), +}; + +/** EHCI PCI driver */ +struct pci_driver ehci_driver __pci_driver = { + .ids = ehci_ids, + .id_count = ( sizeof ( ehci_ids ) / sizeof ( ehci_ids[0] ) ), + .class = PCI_CLASS_ID ( PCI_CLASS_SERIAL, PCI_CLASS_SERIAL_USB, + PCI_CLASS_SERIAL_USB_EHCI ), + .probe = ehci_probe, + .remove = ehci_remove, +}; + +/** + * Prepare for exit + * + * @v booting System is shutting down for OS boot + */ +static void ehci_shutdown ( int booting ) { + /* If we are shutting down to boot an OS, then prevent the + * release of ownership back to BIOS. + */ + ehci_legacy_prevent_release = booting; +} + +/** Startup/shutdown function */ +struct startup_fn ehci_startup __startup_fn ( STARTUP_LATE ) = { + .shutdown = ehci_shutdown, +}; diff --git a/qemu/roms/ipxe/src/drivers/usb/ehci.h b/qemu/roms/ipxe/src/drivers/usb/ehci.h new file mode 100644 index 000000000..42e282e92 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/usb/ehci.h @@ -0,0 +1,544 @@ +#ifndef _IPXE_EHCI_H +#define _IPXE_EHCI_H + +/** @file + * + * USB Enhanced Host Controller Interface (EHCI) driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** Minimum alignment required for data structures + * + * With the exception of the periodic frame list (which is + * page-aligned), data structures used by EHCI generally require + * 32-byte alignment and must not cross a 4kB page boundary. We + * simplify this requirement by aligning each structure on its own + * size, with a minimum of a 32 byte alignment. + */ +#define EHCI_MIN_ALIGN 32 + +/** Maximum transfer size + * + * EHCI allows for transfers of up to 20kB with page-alignment, or + * 16kB with arbitrary alignment. + */ +#define EHCI_MTU 16384 + +/** Page-alignment required for some data structures */ +#define EHCI_PAGE_ALIGN 4096 + +/** EHCI PCI BAR */ +#define EHCI_BAR PCI_BASE_ADDRESS_0 + +/** Capability register length */ +#define EHCI_CAP_CAPLENGTH 0x00 + +/** Host controller interface version number */ +#define EHCI_CAP_HCIVERSION 0x02 + +/** Structural parameters */ +#define EHCI_CAP_HCSPARAMS 0x04 + +/** Number of ports */ +#define EHCI_HCSPARAMS_PORTS(params) ( ( (params) >> 0 ) & 0x0f ) + +/** Capability parameters */ +#define EHCI_CAP_HCCPARAMS 0x08 + +/** 64-bit addressing capability */ +#define EHCI_HCCPARAMS_ADDR64(params) ( ( (params) >> 0 ) & 0x1 ) + +/** Programmable frame list flag */ +#define EHCI_HCCPARAMS_FLSIZE(params) ( ( (params) >> 1 ) & 0x1 ) + +/** EHCI extended capabilities pointer */ +#define EHCI_HCCPARAMS_EECP(params) ( ( ( (params) >> 8 ) & 0xff ) ) + +/** EHCI extended capability ID */ +#define EHCI_EECP_ID(eecp) ( ( (eecp) >> 0 ) & 0xff ) + +/** Next EHCI extended capability pointer */ +#define EHCI_EECP_NEXT(eecp) ( ( ( (eecp) >> 8 ) & 0xff ) ) + +/** USB legacy support extended capability */ +#define EHCI_EECP_ID_LEGACY 1 + +/** USB legacy support BIOS owned semaphore */ +#define EHCI_USBLEGSUP_BIOS 0x02 + +/** USB legacy support BIOS ownership flag */ +#define EHCI_USBLEGSUP_BIOS_OWNED 0x01 + +/** USB legacy support OS owned semaphore */ +#define EHCI_USBLEGSUP_OS 0x03 + +/** USB legacy support OS ownership flag */ +#define EHCI_USBLEGSUP_OS_OWNED 0x01 + +/** USB legacy support control/status */ +#define EHCI_USBLEGSUP_CTLSTS 0x04 + +/** USB command register */ +#define EHCI_OP_USBCMD 0x00 + +/** Run/stop */ +#define EHCI_USBCMD_RUN 0x00000001UL + +/** Host controller reset */ +#define EHCI_USBCMD_HCRST 0x00000002UL + +/** Frame list size */ +#define EHCI_USBCMD_FLSIZE(flsize) ( (flsize) << 2 ) + +/** Frame list size mask */ +#define EHCI_USBCMD_FLSIZE_MASK EHCI_USBCMD_FLSIZE ( 3 ) + +/** Default frame list size */ +#define EHCI_FLSIZE_DEFAULT 0 + +/** Smallest allowed frame list size */ +#define EHCI_FLSIZE_SMALL 2 + +/** Number of elements in frame list */ +#define EHCI_PERIODIC_FRAMES(flsize) ( 1024 >> (flsize) ) + +/** Periodic schedule enable */ +#define EHCI_USBCMD_PERIODIC 0x00000010UL + +/** Asynchronous schedule enable */ +#define EHCI_USBCMD_ASYNC 0x00000020UL + +/** Asyncchronous schedule advance doorbell */ +#define EHCI_USBCMD_ASYNC_ADVANCE 0x000040UL + +/** USB status register */ +#define EHCI_OP_USBSTS 0x04 + +/** USB interrupt */ +#define EHCI_USBSTS_USBINT 0x00000001UL + +/** USB error interrupt */ +#define EHCI_USBSTS_USBERRINT 0x00000002UL + +/** Port change detect */ +#define EHCI_USBSTS_PORT 0x00000004UL + +/** Frame list rollover */ +#define EHCI_USBSTS_ROLLOVER 0x00000008UL + +/** Host system error */ +#define EHCI_USBSTS_SYSERR 0x00000010UL + +/** Asynchronous schedule advanced */ +#define EHCI_USBSTS_ASYNC_ADVANCE 0x00000020UL + +/** Periodic schedule enabled */ +#define EHCI_USBSTS_PERIODIC 0x00004000UL + +/** Asynchronous schedule enabled */ +#define EHCI_USBSTS_ASYNC 0x00008000UL + +/** Host controller halted */ +#define EHCI_USBSTS_HCH 0x00001000UL + +/** USB status change mask */ +#define EHCI_USBSTS_CHANGE \ + ( EHCI_USBSTS_USBINT | EHCI_USBSTS_USBERRINT | \ + EHCI_USBSTS_PORT | EHCI_USBSTS_ROLLOVER | \ + EHCI_USBSTS_SYSERR | EHCI_USBSTS_ASYNC_ADVANCE ) + +/** USB interrupt enable register */ +#define EHCI_OP_USBINTR 0x08 + +/** Frame index register */ +#define EHCI_OP_FRINDEX 0x0c + +/** Control data structure segment register */ +#define EHCI_OP_CTRLDSSEGMENT 0x10 + +/** Periodic frame list base address register */ +#define EHCI_OP_PERIODICLISTBASE 0x14 + +/** Current asynchronous list address register */ +#define EHCI_OP_ASYNCLISTADDR 0x18 + +/** Configure flag register */ +#define EHCI_OP_CONFIGFLAG 0x40 + +/** Configure flag */ +#define EHCI_CONFIGFLAG_CF 0x00000001UL + +/** Port status and control register */ +#define EHCI_OP_PORTSC(port) ( 0x40 + ( (port) << 2 ) ) + +/** Current connect status */ +#define EHCI_PORTSC_CCS 0x00000001UL + +/** Connect status change */ +#define EHCI_PORTSC_CSC 0x00000002UL + +/** Port enabled */ +#define EHCI_PORTSC_PED 0x00000004UL + +/** Port enabled/disabled change */ +#define EHCI_PORTSC_PEC 0x00000008UL + +/** Over-current change */ +#define EHCI_PORTSC_OCC 0x00000020UL + +/** Port reset */ +#define EHCI_PORTSC_PR 0x00000100UL + +/** Line status */ +#define EHCI_PORTSC_LINE_STATUS(portsc) ( ( (portsc) >> 10 ) & 0x3 ) + +/** Line status: low-speed device */ +#define EHCI_PORTSC_LINE_STATUS_LOW 0x1 + +/** Port power */ +#define EHCI_PORTSC_PP 0x00001000UL + +/** Port owner */ +#define EHCI_PORTSC_OWNER 0x00002000UL + +/** Port status change mask */ +#define EHCI_PORTSC_CHANGE \ + ( EHCI_PORTSC_CSC | EHCI_PORTSC_PEC | EHCI_PORTSC_OCC ) + +/** List terminator */ +#define EHCI_LINK_TERMINATE 0x00000001UL + +/** Frame list type */ +#define EHCI_LINK_TYPE(type) ( (type) << 1 ) + +/** Queue head type */ +#define EHCI_LINK_TYPE_QH EHCI_LINK_TYPE ( 1 ) + +/** A periodic frame list entry */ +struct ehci_periodic_frame { + /** First queue head */ + uint32_t link; +} __attribute__ (( packed )); + +/** A transfer descriptor */ +struct ehci_transfer_descriptor { + /** Next transfer descriptor */ + uint32_t next; + /** Alternate next transfer descriptor */ + uint32_t alt; + /** Status */ + uint8_t status; + /** Flags */ + uint8_t flags; + /** Transfer length */ + uint16_t len; + /** Buffer pointers (low 32 bits) */ + uint32_t low[5]; + /** Extended buffer pointers (high 32 bits) */ + uint32_t high[5]; + /** Reserved */ + uint8_t reserved[12]; +} __attribute__ (( packed )); + +/** Transaction error */ +#define EHCI_STATUS_XACT_ERR 0x08 + +/** Babble detected */ +#define EHCI_STATUS_BABBLE 0x10 + +/** Data buffer error */ +#define EHCI_STATUS_BUFFER 0x20 + +/** Halted */ +#define EHCI_STATUS_HALTED 0x40 + +/** Active */ +#define EHCI_STATUS_ACTIVE 0x80 + +/** PID code */ +#define EHCI_FL_PID(code) ( (code) << 0 ) + +/** OUT token */ +#define EHCI_FL_PID_OUT EHCI_FL_PID ( 0 ) + +/** IN token */ +#define EHCI_FL_PID_IN EHCI_FL_PID ( 1 ) + +/** SETUP token */ +#define EHCI_FL_PID_SETUP EHCI_FL_PID ( 2 ) + +/** Error counter */ +#define EHCI_FL_CERR( count ) ( (count) << 2 ) + +/** Error counter maximum value */ +#define EHCI_FL_CERR_MAX EHCI_FL_CERR ( 3 ) + +/** Interrupt on completion */ +#define EHCI_FL_IOC 0x80 + +/** Length mask */ +#define EHCI_LEN_MASK 0x7fff + +/** Data toggle */ +#define EHCI_LEN_TOGGLE 0x8000 + +/** A queue head */ +struct ehci_queue_head { + /** Horizontal link pointer */ + uint32_t link; + /** Endpoint characteristics */ + uint32_t chr; + /** Endpoint capabilities */ + uint32_t cap; + /** Current transfer descriptor */ + uint32_t current; + /** Transfer descriptor cache */ + struct ehci_transfer_descriptor cache; +} __attribute__ (( packed )); + +/** Device address */ +#define EHCI_CHR_ADDRESS( address ) ( (address) << 0 ) + +/** Endpoint number */ +#define EHCI_CHR_ENDPOINT( address ) ( ( (address) & 0xf ) << 8 ) + +/** Endpoint speed */ +#define EHCI_CHR_EPS( eps ) ( (eps) << 12 ) + +/** Full-speed endpoint */ +#define EHCI_CHR_EPS_FULL EHCI_CHR_EPS ( 0 ) + +/** Low-speed endpoint */ +#define EHCI_CHR_EPS_LOW EHCI_CHR_EPS ( 1 ) + +/** High-speed endpoint */ +#define EHCI_CHR_EPS_HIGH EHCI_CHR_EPS ( 2 ) + +/** Explicit data toggles */ +#define EHCI_CHR_TOGGLE 0x00004000UL + +/** Head of reclamation list flag */ +#define EHCI_CHR_HEAD 0x00008000UL + +/** Maximum packet length */ +#define EHCI_CHR_MAX_LEN( len ) ( (len) << 16 ) + +/** Control endpoint flag */ +#define EHCI_CHR_CONTROL 0x08000000UL + +/** Interrupt schedule mask */ +#define EHCI_CAP_INTR_SCHED( uframe ) ( 1 << ( (uframe) + 0 ) ) + +/** Split completion schedule mask */ +#define EHCI_CAP_SPLIT_SCHED( uframe ) ( 1 << ( (uframe) + 8 ) ) + +/** Default split completion schedule mask + * + * We schedule all split starts in microframe 0, on the assumption + * that we will never have to deal with more than sixteen actively + * interrupting devices via the same transaction translator. We + * schedule split completions for all remaining microframes after + * microframe 1 (in which the low-speed or full-speed transaction is + * assumed to execute). This is a very crude approximation designed + * to avoid the need for calculating exactly when low-speed and + * full-speed transactions will execute. Since we only ever deal with + * interrupt endpoints (rather than isochronous endpoints), the volume + * of periodic traffic is extremely low, and this approximation should + * remain valid. + */ +#define EHCI_CAP_SPLIT_SCHED_DEFAULT \ + ( EHCI_CAP_SPLIT_SCHED ( 2 ) | EHCI_CAP_SPLIT_SCHED ( 3 ) | \ + EHCI_CAP_SPLIT_SCHED ( 4 ) | EHCI_CAP_SPLIT_SCHED ( 5 ) | \ + EHCI_CAP_SPLIT_SCHED ( 6 ) | EHCI_CAP_SPLIT_SCHED ( 7 ) ) + +/** Transaction translator hub address */ +#define EHCI_CAP_TT_HUB( address ) ( (address) << 16 ) + +/** Transaction translator port number */ +#define EHCI_CAP_TT_PORT( port ) ( (port) << 23 ) + +/** High-bandwidth pipe multiplier */ +#define EHCI_CAP_MULT( mult ) ( (mult) << 30 ) + +/** A transfer descriptor ring */ +struct ehci_ring { + /** Producer counter */ + unsigned int prod; + /** Consumer counter */ + unsigned int cons; + + /** Residual untransferred data */ + size_t residual; + + /** I/O buffers */ + struct io_buffer **iobuf; + + /** Queue head */ + struct ehci_queue_head *head; + /** Transfer descriptors */ + struct ehci_transfer_descriptor *desc; +}; + +/** Number of transfer descriptors in a ring + * + * This is a policy decision. + */ +#define EHCI_RING_COUNT 64 + +/** + * Calculate space used in transfer descriptor ring + * + * @v ring Transfer descriptor ring + * @ret fill Number of entries used + */ +static inline __attribute__ (( always_inline )) unsigned int +ehci_ring_fill ( struct ehci_ring *ring ) { + unsigned int fill; + + fill = ( ring->prod - ring->cons ); + assert ( fill <= EHCI_RING_COUNT ); + return fill; +} + +/** + * Calculate space remaining in transfer descriptor ring + * + * @v ring Transfer descriptor ring + * @ret remaining Number of entries remaining + */ +static inline __attribute__ (( always_inline )) unsigned int +ehci_ring_remaining ( struct ehci_ring *ring ) { + unsigned int fill = ehci_ring_fill ( ring ); + + return ( EHCI_RING_COUNT - fill ); +} + +/** Time to delay after enabling power to a port + * + * This is not mandated by EHCI; we use the value given for xHCI. + */ +#define EHCI_PORT_POWER_DELAY_MS 20 + +/** Time to delay after releasing ownership of a port + * + * This is a policy decision. + */ +#define EHCI_DISOWN_DELAY_MS 100 + +/** Maximum time to wait for BIOS to release ownership + * + * This is a policy decision. + */ +#define EHCI_USBLEGSUP_MAX_WAIT_MS 100 + +/** Maximum time to wait for asynchronous schedule to advance + * + * This is a policy decision. + */ +#define EHCI_ASYNC_ADVANCE_MAX_WAIT_MS 100 + +/** Maximum time to wait for host controller to stop + * + * This is a policy decision. + */ +#define EHCI_STOP_MAX_WAIT_MS 100 + +/** Maximum time to wait for reset to complete + * + * This is a policy decision. + */ +#define EHCI_RESET_MAX_WAIT_MS 500 + +/** Maximum time to wait for a port reset to complete + * + * This is a policy decision. + */ +#define EHCI_PORT_RESET_MAX_WAIT_MS 500 + +/** An EHCI transfer */ +struct ehci_transfer { + /** Data buffer */ + void *data; + /** Length */ + size_t len; + /** Flags + * + * This is the bitwise OR of zero or more EHCI_FL_XXX values. + * The low 8 bits are copied to the flags byte within the + * transfer descriptor; the remaining bits hold flags + * meaningful only to our driver code. + */ + unsigned int flags; +}; + +/** Set initial data toggle */ +#define EHCI_FL_TOGGLE 0x8000 + +/** An EHCI device */ +struct ehci_device { + /** Registers */ + void *regs; + /** Name */ + const char *name; + + /** Capability registers */ + void *cap; + /** Operational registers */ + void *op; + + /** Number of ports */ + unsigned int ports; + /** 64-bit addressing capability */ + int addr64; + /** Frame list size */ + unsigned int flsize; + /** EHCI extended capabilities offset */ + unsigned int eecp; + + /** USB legacy support capability (if present and enabled) */ + unsigned int legacy; + + /** Control data structure segment */ + uint32_t ctrldssegment; + /** Asynchronous queue head */ + struct ehci_queue_head *head; + /** Periodic frame list */ + struct ehci_periodic_frame *frame; + + /** List of all endpoints */ + struct list_head endpoints; + /** Asynchronous schedule */ + struct list_head async; + /** Periodic schedule + * + * Listed in decreasing order of endpoint interval. + */ + struct list_head periodic; + + /** USB bus */ + struct usb_bus *bus; +}; + +/** An EHCI endpoint */ +struct ehci_endpoint { + /** EHCI device */ + struct ehci_device *ehci; + /** USB endpoint */ + struct usb_endpoint *ep; + /** List of all endpoints */ + struct list_head list; + /** Endpoint schedule */ + struct list_head schedule; + + /** Transfer descriptor ring */ + struct ehci_ring ring; +}; + +extern unsigned int ehci_companion ( struct pci_device *pci ); + +#endif /* _IPXE_EHCI_H */ diff --git a/qemu/roms/ipxe/src/drivers/usb/uhci.c b/qemu/roms/ipxe/src/drivers/usb/uhci.c new file mode 100644 index 000000000..b6bb92560 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/usb/uhci.c @@ -0,0 +1,1577 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include "ehci.h" +#include "uhci.h" + +/** @file + * + * USB Universal Host Controller Interface (UHCI) driver + * + */ + +/****************************************************************************** + * + * Register access + * + ****************************************************************************** + */ + +/** + * Check that address is reachable + * + * @v addr Address + * @v len Length + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline)) int +uhci_reachable ( void *addr, size_t len ) { + physaddr_t phys = virt_to_phys ( addr ); + + /* Always reachable in a 32-bit build */ + if ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) ) + return 0; + + /* Reachable if below 4GB */ + if ( ( ( phys + len - 1 ) & ~0xffffffffULL ) == 0 ) + return 0; + + return -ENOTSUP; +} + +/****************************************************************************** + * + * Run / stop / reset + * + ****************************************************************************** + */ + +/** + * Start UHCI device + * + * @v uhci UHCI device + */ +static void uhci_run ( struct uhci_device *uhci ) { + uint16_t usbcmd; + + /* Set run/stop bit */ + usbcmd = inw ( uhci->regs + UHCI_USBCMD ); + usbcmd |= ( UHCI_USBCMD_RUN | UHCI_USBCMD_MAX64 ); + outw ( usbcmd, uhci->regs + UHCI_USBCMD ); +} + +/** + * Stop UHCI device + * + * @v uhci UHCI device + * @ret rc Return status code + */ +static int uhci_stop ( struct uhci_device *uhci ) { + uint16_t usbcmd; + uint16_t usbsts; + unsigned int i; + + /* Clear run/stop bit */ + usbcmd = inw ( uhci->regs + UHCI_USBCMD ); + usbcmd &= ~UHCI_USBCMD_RUN; + outw ( usbcmd, uhci->regs + UHCI_USBCMD ); + + /* Wait for device to stop */ + for ( i = 0 ; i < UHCI_STOP_MAX_WAIT_MS ; i++ ) { + + /* Check if device is stopped */ + usbsts = inw ( uhci->regs + UHCI_USBSTS ); + if ( usbsts & UHCI_USBSTS_HCHALTED ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( uhci, "UHCI %s timed out waiting for stop\n", uhci->name ); + return -ETIMEDOUT; +} + +/** + * Reset UHCI device + * + * @v uhci UHCI device + * @ret rc Return status code + */ +static int uhci_reset ( struct uhci_device *uhci ) { + uint16_t usbcmd; + unsigned int i; + int rc; + + /* The UHCI specification states that resetting a running + * device may result in undefined behaviour, so try stopping + * it first. + */ + if ( ( rc = uhci_stop ( uhci ) ) != 0 ) { + /* Ignore errors and attempt to reset the device anyway */ + } + + /* Reset device */ + outw ( UHCI_USBCMD_HCRESET, uhci->regs + UHCI_USBCMD ); + + /* Wait for reset to complete */ + for ( i = 0 ; i < UHCI_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check if reset is complete */ + usbcmd = inw ( uhci->regs + UHCI_USBCMD ); + if ( ! ( usbcmd & UHCI_USBCMD_HCRESET ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( uhci, "UHCI %s timed out waiting for reset\n", uhci->name ); + return -ETIMEDOUT; +} + +/****************************************************************************** + * + * Transfer descriptor rings + * + ****************************************************************************** + */ + +/** + * Allocate transfer ring + * + * @v ring Transfer ring + * @ret rc Return status code + */ +static int uhci_ring_alloc ( struct uhci_ring *ring ) { + int rc; + + /* Initialise structure */ + memset ( ring, 0, sizeof ( *ring ) ); + + /* Allocate queue head */ + ring->head = malloc_dma ( sizeof ( *ring->head ), UHCI_ALIGN ); + if ( ! ring->head ) { + rc = -ENOMEM; + goto err_alloc; + } + if ( ( rc = uhci_reachable ( ring->head, + sizeof ( *ring->head ) ) ) != 0 ) + goto err_unreachable; + + /* Initialise queue head */ + ring->head->current = cpu_to_le32 ( UHCI_LINK_TERMINATE ); + + return 0; + + err_unreachable: + free_dma ( ring->head, sizeof ( *ring->head ) ); + err_alloc: + return rc; +} + +/** + * Free transfer ring + * + * @v ring Transfer ring + */ +static void uhci_ring_free ( struct uhci_ring *ring ) { + unsigned int i; + + /* Sanity checks */ + assert ( uhci_ring_fill ( ring ) == 0 ); + for ( i = 0 ; i < UHCI_RING_COUNT ; i++ ) + assert ( ring->xfer[i] == NULL ); + + /* Free queue head */ + free_dma ( ring->head, sizeof ( *ring->head ) ); +} + +/** + * Enqueue new transfer + * + * @v ring Transfer ring + * @v iobuf I/O buffer + * @v count Number of descriptors + * @ret rc Return status code + */ +static int uhci_enqueue ( struct uhci_ring *ring, struct io_buffer *iobuf, + unsigned int count ) { + struct uhci_transfer *xfer; + struct uhci_transfer *end; + struct uhci_transfer_descriptor *desc; + unsigned int index = ( ring->prod % UHCI_RING_COUNT ); + uint32_t link; + size_t len; + int rc; + + /* Sanity check */ + assert ( count > 0 ); + assert ( iobuf != NULL ); + + /* Check for space in ring */ + if ( ! uhci_ring_remaining ( ring ) ) { + rc = -ENOBUFS; + goto err_ring_full; + } + + /* Check for reachability of I/O buffer */ + if ( ( rc = uhci_reachable ( iobuf->data, iob_len ( iobuf ) ) ) != 0 ) + goto err_unreachable_iobuf; + + /* Allocate transfer */ + xfer = malloc ( sizeof ( *xfer ) ); + if ( ! xfer ) { + rc = -ENOMEM; + goto err_alloc_xfer; + } + + /* Initialise transfer */ + xfer->prod = 0; + xfer->cons = 0; + xfer->len = 0; + xfer->iobuf = iobuf; + + /* Allocate transfer descriptors */ + len = ( count * sizeof ( xfer->desc[0] ) ); + xfer->desc = malloc_dma ( len, UHCI_ALIGN ); + if ( ! xfer->desc ) { + rc = -ENOMEM; + goto err_alloc_desc; + } + if ( ( rc = uhci_reachable ( xfer->desc, len ) ) != 0 ) + goto err_unreachable_desc; + + /* Initialise transfer descriptors */ + memset ( xfer->desc, 0, len ); + desc = xfer->desc; + for ( ; --count ; desc++ ) { + link = ( virt_to_phys ( desc + 1 ) | UHCI_LINK_DEPTH_FIRST ); + desc->link = cpu_to_le32 ( link ); + desc->flags = ring->flags; + } + desc->link = cpu_to_le32 ( UHCI_LINK_TERMINATE ); + desc->flags = ( ring->flags | UHCI_FL_IOC ); + + /* Add to ring */ + wmb(); + link = virt_to_phys ( xfer->desc ); + if ( uhci_ring_fill ( ring ) > 0 ) { + end = ring->end; + end->desc[ end->prod - 1 ].link = cpu_to_le32 ( link ); + } else { + ring->head->current = cpu_to_le32 ( link ); + } + assert ( ring->xfer[index] == NULL ); + ring->xfer[index] = xfer; + ring->end = xfer; + ring->prod++; + + return 0; + + err_unreachable_desc: + free_dma ( xfer->desc, len ); + err_alloc_desc: + free ( xfer ); + err_alloc_xfer: + err_unreachable_iobuf: + err_ring_full: + return rc; +} + +/** + * Describe transfer + * + * @v ring Transfer ring + * @v data Data + * @v len Length of data + * @v pid Packet ID + */ +static void uhci_describe ( struct uhci_ring *ring, void *data, + size_t len, uint8_t pid ) { + struct uhci_transfer *xfer = ring->end; + struct uhci_transfer_descriptor *desc; + size_t frag_len; + uint32_t control; + + do { + /* Calculate fragment length */ + frag_len = len; + if ( frag_len > ring->mtu ) + frag_len = ring->mtu; + + /* Populate descriptor */ + desc = &xfer->desc[xfer->prod++]; + if ( pid == USB_PID_IN ) + desc->flags |= UHCI_FL_SPD; + control = ( ring->control | UHCI_CONTROL_PID ( pid ) | + UHCI_CONTROL_LEN ( frag_len ) ); + desc->control = cpu_to_le32 ( control ); + if ( data ) + desc->data = virt_to_phys ( data ); + wmb(); + desc->status = UHCI_STATUS_ACTIVE; + + /* Update data toggle */ + ring->control ^= UHCI_CONTROL_TOGGLE; + + /* Move to next descriptor */ + data += frag_len; + len -= frag_len; + + } while ( len ); +} + +/** + * Dequeue transfer + * + * @v ring Transfer ring + * @ret iobuf I/O buffer + */ +static struct io_buffer * uhci_dequeue ( struct uhci_ring *ring ) { + unsigned int index = ( ring->cons % UHCI_RING_COUNT ); + struct io_buffer *iobuf; + struct uhci_transfer *xfer; + size_t len; + + /* Sanity checks */ + assert ( uhci_ring_fill ( ring ) > 0 ); + + /* Consume transfer */ + xfer = ring->xfer[index]; + assert ( xfer != NULL ); + assert ( xfer->desc != NULL ); + iobuf = xfer->iobuf; + assert ( iobuf != NULL ); + ring->xfer[index] = NULL; + ring->cons++; + + /* Free transfer descriptors */ + len = ( xfer->prod * sizeof ( xfer->desc[0] ) ); + free_dma ( xfer->desc, len ); + + /* Free transfer */ + free ( xfer ); + + return iobuf; +} + +/** + * Restart ring + * + * @v ring Transfer ring + * @v toggle Expected data toggle for next descriptor + */ +static void uhci_restart ( struct uhci_ring *ring, uint32_t toggle ) { + struct uhci_transfer *xfer; + struct uhci_transfer_descriptor *desc; + struct uhci_transfer_descriptor *first; + uint32_t link; + unsigned int i; + unsigned int j; + + /* Sanity check */ + assert ( ring->head->current == cpu_to_le32 ( UHCI_LINK_TERMINATE ) ); + + /* If ring is empty, then just update the data toggle for the + * next descriptor. + */ + if ( uhci_ring_fill ( ring ) == 0 ) { + ring->control &= ~UHCI_CONTROL_TOGGLE; + ring->control |= toggle; + return; + } + + /* If expected toggle does not match the toggle in the first + * unconsumed descriptor, then invert all toggles. + */ + xfer = ring->xfer[ ring->cons % UHCI_RING_COUNT ]; + assert ( xfer != NULL ); + assert ( xfer->cons == 0 ); + first = &xfer->desc[0]; + if ( ( le32_to_cpu ( first->control ) ^ toggle ) & UHCI_CONTROL_TOGGLE){ + + /* Invert toggle on all unconsumed transfer descriptors */ + for ( i = ring->cons ; i != ring->prod ; i++ ) { + xfer = ring->xfer[ i % UHCI_RING_COUNT ]; + assert ( xfer != NULL ); + assert ( xfer->cons == 0 ); + for ( j = 0 ; j < xfer->prod ; j++ ) { + desc = &xfer->desc[j]; + desc->control ^= + cpu_to_le32 ( UHCI_CONTROL_TOGGLE ); + } + } + + /* Invert toggle for next descriptor to be enqueued */ + ring->control ^= UHCI_CONTROL_TOGGLE; + } + + /* Restart ring at first unconsumed transfer */ + link = virt_to_phys ( first ); + wmb(); + ring->head->current = cpu_to_le32 ( link ); +} + +/****************************************************************************** + * + * Schedule management + * + ****************************************************************************** + */ + +/** + * Get link value for a queue head + * + * @v queue Queue head + * @ret link Link value + */ +static inline uint32_t uhci_link_qh ( struct uhci_queue_head *queue ) { + + return ( virt_to_phys ( queue ) | UHCI_LINK_TYPE_QH ); +} + +/** + * (Re)build asynchronous schedule + * + * @v uhci UHCI device + */ +static void uhci_async_schedule ( struct uhci_device *uhci ) { + struct uhci_endpoint *endpoint; + struct uhci_queue_head *queue; + uint32_t end; + uint32_t link; + + /* Build schedule in reverse order of execution. Provided + * that we only ever add or remove single endpoints, this can + * safely run concurrently with hardware execution of the + * schedule. + */ + link = end = uhci_link_qh ( uhci->head ); + list_for_each_entry_reverse ( endpoint, &uhci->async, schedule ) { + queue = endpoint->ring.head; + queue->link = cpu_to_le32 ( link ); + wmb(); + link = uhci_link_qh ( queue ); + } + if ( link == end ) + link = UHCI_LINK_TERMINATE; + uhci->head->link = cpu_to_le32 ( link ); + wmb(); +} + +/** + * Add endpoint to asynchronous schedule + * + * @v endpoint Endpoint + */ +static void uhci_async_add ( struct uhci_endpoint *endpoint ) { + struct uhci_device *uhci = endpoint->uhci; + + /* Add to end of schedule */ + list_add_tail ( &endpoint->schedule, &uhci->async ); + + /* Rebuild schedule */ + uhci_async_schedule ( uhci ); +} + +/** + * Remove endpoint from asynchronous schedule + * + * @v endpoint Endpoint + */ +static void uhci_async_del ( struct uhci_endpoint *endpoint ) { + struct uhci_device *uhci = endpoint->uhci; + + /* Remove from schedule */ + list_check_contains_entry ( endpoint, &uhci->async, schedule ); + list_del ( &endpoint->schedule ); + + /* Rebuild schedule */ + uhci_async_schedule ( uhci ); + + /* Delay for a whole USB frame (with a 100% safety margin) */ + mdelay ( 2 ); +} + +/** + * (Re)build periodic schedule + * + * @v uhci UHCI device + */ +static void uhci_periodic_schedule ( struct uhci_device *uhci ) { + struct uhci_endpoint *endpoint; + struct uhci_queue_head *queue; + uint32_t link; + uint32_t end; + unsigned int max_interval; + unsigned int i; + + /* Build schedule in reverse order of execution. Provided + * that we only ever add or remove single endpoints, this can + * safely run concurrently with hardware execution of the + * schedule. + */ + DBGCP ( uhci, "UHCI %s periodic schedule: ", uhci->name ); + link = end = uhci_link_qh ( uhci->head ); + list_for_each_entry_reverse ( endpoint, &uhci->periodic, schedule ) { + queue = endpoint->ring.head; + queue->link = cpu_to_le32 ( link ); + wmb(); + DBGCP ( uhci, "%s%d", ( ( link == end ) ? "" : "<-" ), + endpoint->ep->interval ); + link = uhci_link_qh ( queue ); + } + DBGCP ( uhci, "\n" ); + + /* Populate periodic frame list */ + DBGCP ( uhci, "UHCI %s periodic frame list:", uhci->name ); + for ( i = 0 ; i < UHCI_FRAMES ; i++ ) { + + /* Calculate maximum interval (in microframes) which + * may appear as part of this frame list. + */ + if ( i == 0 ) { + /* Start of list: include all endpoints */ + max_interval = -1U; + } else { + /* Calculate highest power-of-two frame interval */ + max_interval = ( 1 << ( ffs ( i ) - 1 ) ); + /* Convert to microframes */ + max_interval <<= 3; + /* Round up to nearest 2^n-1 */ + max_interval = ( ( max_interval << 1 ) - 1 ); + } + + /* Find first endpoint in schedule satisfying this + * maximum interval constraint. + */ + link = uhci_link_qh ( uhci->head ); + list_for_each_entry ( endpoint, &uhci->periodic, schedule ) { + if ( endpoint->ep->interval <= max_interval ) { + queue = endpoint->ring.head; + link = uhci_link_qh ( queue ); + DBGCP ( uhci, " %d:%d", + i, endpoint->ep->interval ); + break; + } + } + uhci->frame->link[i] = cpu_to_le32 ( link ); + } + wmb(); + DBGCP ( uhci, "\n" ); +} + +/** + * Add endpoint to periodic schedule + * + * @v endpoint Endpoint + */ +static void uhci_periodic_add ( struct uhci_endpoint *endpoint ) { + struct uhci_device *uhci = endpoint->uhci; + struct uhci_endpoint *before; + unsigned int interval = endpoint->ep->interval; + + /* Find first endpoint with a smaller interval */ + list_for_each_entry ( before, &uhci->periodic, schedule ) { + if ( before->ep->interval < interval ) + break; + } + list_add_tail ( &endpoint->schedule, &before->schedule ); + + /* Rebuild schedule */ + uhci_periodic_schedule ( uhci ); +} + +/** + * Remove endpoint from periodic schedule + * + * @v endpoint Endpoint + */ +static void uhci_periodic_del ( struct uhci_endpoint *endpoint ) { + struct uhci_device *uhci = endpoint->uhci; + + /* Remove from schedule */ + list_check_contains_entry ( endpoint, &uhci->periodic, schedule ); + list_del ( &endpoint->schedule ); + + /* Rebuild schedule */ + uhci_periodic_schedule ( uhci ); + + /* Delay for a whole USB frame (with a 100% safety margin) */ + mdelay ( 2 ); +} + +/** + * Add endpoint to appropriate schedule + * + * @v endpoint Endpoint + */ +static void uhci_schedule_add ( struct uhci_endpoint *endpoint ) { + struct usb_endpoint *ep = endpoint->ep; + unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); + + if ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) { + uhci_periodic_add ( endpoint ); + } else { + uhci_async_add ( endpoint ); + } +} + +/** + * Remove endpoint from appropriate schedule + * + * @v endpoint Endpoint + */ +static void uhci_schedule_del ( struct uhci_endpoint *endpoint ) { + struct usb_endpoint *ep = endpoint->ep; + unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); + + if ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) { + uhci_periodic_del ( endpoint ); + } else { + uhci_async_del ( endpoint ); + } +} + +/****************************************************************************** + * + * Endpoint operations + * + ****************************************************************************** + */ + +/** + * Open endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int uhci_endpoint_open ( struct usb_endpoint *ep ) { + struct usb_device *usb = ep->usb; + struct uhci_device *uhci = usb_get_hostdata ( usb ); + struct uhci_endpoint *endpoint; + int rc; + + /* Allocate and initialise structure */ + endpoint = zalloc ( sizeof ( *endpoint ) ); + if ( ! endpoint ) { + rc = -ENOMEM; + goto err_alloc; + } + endpoint->uhci = uhci; + endpoint->ep = ep; + usb_endpoint_set_hostdata ( ep, endpoint ); + + /* Initialise descriptor ring */ + if ( ( rc = uhci_ring_alloc ( &endpoint->ring ) ) != 0 ) + goto err_ring_alloc; + endpoint->ring.mtu = ep->mtu; + endpoint->ring.flags = UHCI_FL_CERR_MAX; + if ( usb->port->speed < USB_SPEED_FULL ) + endpoint->ring.flags |= UHCI_FL_LS; + endpoint->ring.control = ( UHCI_CONTROL_DEVICE ( usb->address ) | + UHCI_CONTROL_ENDPOINT ( ep->address ) ); + + /* Add to list of endpoints */ + list_add_tail ( &endpoint->list, &uhci->endpoints ); + + /* Add to schedule */ + uhci_schedule_add ( endpoint ); + + return 0; + + uhci_ring_free ( &endpoint->ring ); + err_ring_alloc: + free ( endpoint ); + err_alloc: + return rc; +} + +/** + * Close endpoint + * + * @v ep USB endpoint + */ +static void uhci_endpoint_close ( struct usb_endpoint *ep ) { + struct uhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct io_buffer *iobuf; + + /* Remove from schedule */ + uhci_schedule_del ( endpoint ); + + /* Cancel any incomplete transfers */ + while ( uhci_ring_fill ( &endpoint->ring ) ) { + iobuf = uhci_dequeue ( &endpoint->ring ); + if ( iobuf ) + usb_complete_err ( ep, iobuf, -ECANCELED ); + } + + /* Remove from list of endpoints */ + list_del ( &endpoint->list ); + + /* Free descriptor ring */ + uhci_ring_free ( &endpoint->ring ); + + /* Free endpoint */ + free ( endpoint ); +} + +/** + * Reset endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int uhci_endpoint_reset ( struct usb_endpoint *ep ) { + struct uhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct uhci_ring *ring = &endpoint->ring; + + /* Restart ring */ + uhci_restart ( ring, 0 ); + + return 0; +} + +/** + * Update MTU + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int uhci_endpoint_mtu ( struct usb_endpoint *ep ) { + struct uhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + + /* Update endpoint MTU */ + endpoint->ring.mtu = ep->mtu; + + return 0; +} + +/** + * Enqueue message transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int uhci_endpoint_message ( struct usb_endpoint *ep, + struct io_buffer *iobuf ) { + struct uhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct uhci_ring *ring = &endpoint->ring; + struct usb_setup_packet *packet; + unsigned int count; + size_t len; + int input; + int rc; + + /* Calculate number of descriptors */ + assert ( iob_len ( iobuf ) >= sizeof ( *packet ) ); + len = ( iob_len ( iobuf ) - sizeof ( *packet ) ); + count = ( 1 /* setup stage */ + + ( ( len + ring->mtu - 1 ) / ring->mtu ) /* data stage */ + + 1 /* status stage */ ); + + /* Enqueue transfer */ + if ( ( rc = uhci_enqueue ( ring, iobuf, count ) ) != 0 ) + return rc; + + /* Describe setup stage */ + packet = iobuf->data; + ring->control &= ~UHCI_CONTROL_TOGGLE; + uhci_describe ( ring, packet, sizeof ( *packet ), USB_PID_SETUP ); + iob_pull ( iobuf, sizeof ( *packet ) ); + + /* Describe data stage, if applicable */ + assert ( ring->control & UHCI_CONTROL_TOGGLE ); + input = ( packet->request & cpu_to_le16 ( USB_DIR_IN ) ); + if ( len ) { + uhci_describe ( ring, iobuf->data, len, + ( input ? USB_PID_IN : USB_PID_OUT ) ); + } + + /* Describe status stage */ + ring->control |= UHCI_CONTROL_TOGGLE; + uhci_describe ( ring, NULL, 0, + ( ( len && input ) ? USB_PID_OUT : USB_PID_IN ) ); + + /* Sanity check */ + assert ( ring->end->prod == count ); + + return 0; +} + +/** + * Enqueue stream transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v terminate Terminate using a short packet + * @ret rc Return status code + */ +static int uhci_endpoint_stream ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int terminate ) { + struct uhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct uhci_ring *ring = &endpoint->ring; + unsigned int count; + size_t len; + int input; + int zlp; + int rc; + + /* Calculate number of descriptors */ + len = iob_len ( iobuf ); + zlp = ( terminate && ( ( len & ( ring->mtu - 1 ) ) == 0 ) ); + count = ( ( ( len + ring->mtu - 1 ) / ring->mtu ) + ( zlp ? 1 : 0 ) ); + + /* Enqueue transfer */ + if ( ( rc = uhci_enqueue ( ring, iobuf, count ) ) != 0 ) + return rc; + + /* Describe data packet */ + input = ( ep->address & USB_DIR_IN ); + uhci_describe ( ring, iobuf->data, len, + ( input ? USB_PID_IN : USB_PID_OUT ) ); + + /* Describe zero-length packet, if applicable */ + if ( zlp ) + uhci_describe ( ring, NULL, 0, USB_PID_OUT ); + + /* Sanity check */ + assert ( ring->end->prod == count ); + + return 0; +} + +/** + * Check if transfer is a message transfer + * + * @v xfer UHCI transfer + * @ret is_message Transfer is a message transfer + */ +static inline int uhci_is_message ( struct uhci_transfer *xfer ) { + struct uhci_transfer_descriptor *desc = &xfer->desc[0]; + + return ( ( desc->control & cpu_to_le32 ( UHCI_CONTROL_PID_MASK ) ) == + cpu_to_le32 ( UHCI_CONTROL_PID ( USB_PID_SETUP ) ) ); +} + +/** + * Poll for completions + * + * @v endpoint Endpoint + */ +static void uhci_endpoint_poll ( struct uhci_endpoint *endpoint ) { + struct uhci_ring *ring = &endpoint->ring; + struct uhci_device *uhci = endpoint->uhci; + struct usb_endpoint *ep = endpoint->ep; + struct usb_device *usb = ep->usb; + struct uhci_transfer *xfer; + struct uhci_transfer_descriptor *desc; + struct io_buffer *iobuf; + unsigned int index; + uint32_t link; + uint32_t toggle; + uint32_t control; + uint16_t actual; + size_t len; + + /* Consume all completed descriptors */ + while ( uhci_ring_fill ( ring ) ) { + + /* Stop if we reach an uncompleted descriptor */ + index = ( ring->cons % UHCI_RING_COUNT ); + xfer = ring->xfer[index]; + assert ( xfer != NULL ); + assert ( xfer->cons < xfer->prod ); + desc = &xfer->desc[xfer->cons]; + rmb(); + if ( desc->status & UHCI_STATUS_ACTIVE ) + break; + control = le32_to_cpu ( desc->control ); + actual = le16_to_cpu ( desc->actual ); + + /* Update data length, if applicable */ + if ( UHCI_DATA_PACKET ( control ) ) + xfer->len += UHCI_ACTUAL_LEN ( actual ); + + /* If we have encountered an error, then deactivate + * the queue head (to prevent further hardware + * accesses to this transfer), consume the transfer, + * and report the error to the USB core. + */ + if ( desc->status & UHCI_STATUS_STALLED ) { + DBGC ( uhci, "UHCI %s %s completion %d.%d failed " + "(status %02x)\n", usb->name, + usb_endpoint_name ( ep ), index, + xfer->cons, desc->status ); + link = UHCI_LINK_TERMINATE; + ring->head->current = cpu_to_le32 ( link ); + wmb(); + iobuf = uhci_dequeue ( ring ); + usb_complete_err ( ep, iobuf, -EIO ); + break; + } + + /* Consume this descriptor */ + xfer->cons++; + + /* Check for short packets */ + if ( UHCI_SHORT_PACKET ( control, actual ) ) { + + /* Sanity checks */ + assert ( desc->flags & UHCI_FL_SPD ); + link = virt_to_phys ( desc ); + assert ( ( le32_to_cpu ( ring->head->current ) & + ~( UHCI_ALIGN - 1 ) ) == link ); + + /* If this is a message transfer, then restart + * at the status stage. + */ + if ( uhci_is_message ( xfer ) ) { + xfer->cons = ( xfer->prod - 1 ); + link = virt_to_phys ( &xfer->desc[xfer->cons] ); + ring->head->current = cpu_to_le32 ( link ); + break; + } + + /* Otherwise, this is a stream transfer. + * First, prevent further hardware access to + * this transfer. + */ + link = UHCI_LINK_TERMINATE; + ring->head->current = cpu_to_le32 ( link ); + wmb(); + + /* Determine expected data toggle for next descriptor */ + toggle = ( ( control ^ UHCI_CONTROL_TOGGLE ) & + UHCI_CONTROL_TOGGLE ); + + /* Consume this transfer */ + len = xfer->len; + iobuf = uhci_dequeue ( ring ); + + /* Update packet length */ + assert ( len <= iob_len ( iobuf ) ); + iob_unput ( iobuf, ( iob_len ( iobuf ) - len ) ); + + /* Restart ring */ + uhci_restart ( ring, toggle ); + + } else if ( xfer->cons == xfer->prod ) { + + /* Completed a transfer: consume it */ + len = xfer->len; + iobuf = uhci_dequeue ( ring ); + assert ( len == iob_len ( iobuf ) ); + + } else { + + /* Not a short packet and not yet complete: + * continue processing. + */ + continue; + } + + /* Report completion to USB core */ + usb_complete ( ep, iobuf ); + } +} + +/****************************************************************************** + * + * Device operations + * + ****************************************************************************** + */ + +/** + * Open device + * + * @v usb USB device + * @ret rc Return status code + */ +static int uhci_device_open ( struct usb_device *usb ) { + struct uhci_device *uhci = usb_bus_get_hostdata ( usb->port->hub->bus ); + + usb_set_hostdata ( usb, uhci ); + return 0; +} + +/** + * Close device + * + * @v usb USB device + */ +static void uhci_device_close ( struct usb_device *usb ) { + struct uhci_device *uhci = usb_get_hostdata ( usb ); + struct usb_bus *bus = uhci->bus; + + /* Free device address, if assigned */ + if ( usb->address ) + usb_free_address ( bus, usb->address ); +} + +/** + * Assign device address + * + * @v usb USB device + * @ret rc Return status code + */ +static int uhci_device_address ( struct usb_device *usb ) { + struct uhci_device *uhci = usb_get_hostdata ( usb ); + struct usb_bus *bus = uhci->bus; + struct usb_endpoint *ep0 = usb_endpoint ( usb, USB_EP0_ADDRESS ); + struct uhci_endpoint *endpoint0 = usb_endpoint_get_hostdata ( ep0 ); + int address; + int rc; + + /* Sanity checks */ + assert ( usb->address == 0 ); + assert ( ep0 != NULL ); + + /* Allocate device address */ + address = usb_alloc_address ( bus ); + if ( address < 0 ) { + rc = address; + DBGC ( uhci, "UHCI %s could not allocate address: %s\n", + usb->name, strerror ( rc ) ); + goto err_alloc_address; + } + + /* Set address */ + if ( ( rc = usb_set_address ( usb, address ) ) != 0 ) + goto err_set_address; + + /* Update device address */ + usb->address = address; + endpoint0->ring.control |= UHCI_CONTROL_DEVICE ( address ); + + return 0; + + err_set_address: + usb_free_address ( bus, address ); + err_alloc_address: + return rc; +} + +/****************************************************************************** + * + * Hub operations + * + ****************************************************************************** + */ + +/** + * Open hub + * + * @v hub USB hub + * @ret rc Return status code + */ +static int uhci_hub_open ( struct usb_hub *hub __unused ) { + + /* Nothing to do */ + return 0; +} + +/** + * Close hub + * + * @v hub USB hub + */ +static void uhci_hub_close ( struct usb_hub *hub __unused ) { + + /* Nothing to do */ +} + +/****************************************************************************** + * + * Root hub operations + * + ****************************************************************************** + */ + +/** + * Open root hub + * + * @v hub USB hub + * @ret rc Return status code + */ +static int uhci_root_open ( struct usb_hub *hub ) { + struct usb_bus *bus = hub->bus; + struct uhci_device *uhci = usb_bus_get_hostdata ( bus ); + + /* Record hub driver private data */ + usb_hub_set_drvdata ( hub, uhci ); + + return 0; +} + +/** + * Close root hub + * + * @v hub USB hub + */ +static void uhci_root_close ( struct usb_hub *hub ) { + + /* Clear hub driver private data */ + usb_hub_set_drvdata ( hub, NULL ); +} + +/** + * Enable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int uhci_root_enable ( struct usb_hub *hub, struct usb_port *port ) { + struct uhci_device *uhci = usb_hub_get_drvdata ( hub ); + uint16_t portsc; + unsigned int i; + + /* Reset port */ + portsc = inw ( uhci->regs + UHCI_PORTSC ( port->address ) ); + portsc |= UHCI_PORTSC_PR; + outw ( portsc, uhci->regs + UHCI_PORTSC ( port->address ) ); + mdelay ( USB_RESET_DELAY_MS ); + portsc &= ~UHCI_PORTSC_PR; + outw ( portsc, uhci->regs + UHCI_PORTSC ( port->address ) ); + mdelay ( USB_RESET_RECOVER_DELAY_MS ); + + /* Enable port */ + portsc |= UHCI_PORTSC_PED; + outw ( portsc, uhci->regs + UHCI_PORTSC ( port->address ) ); + mdelay ( USB_RESET_RECOVER_DELAY_MS ); + + /* Wait for port to become enabled */ + for ( i = 0 ; i < UHCI_PORT_ENABLE_MAX_WAIT_MS ; i++ ) { + + /* Check port status */ + portsc = inw ( uhci->regs + UHCI_PORTSC ( port->address ) ); + if ( portsc & UHCI_PORTSC_PED ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( uhci, "UHCI %s-%d timed out waiting for port to enable " + "(status %04x)\n", uhci->name, port->address, portsc ); + return -ETIMEDOUT; +} + +/** + * Disable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int uhci_root_disable ( struct usb_hub *hub, struct usb_port *port ) { + struct uhci_device *uhci = usb_hub_get_drvdata ( hub ); + uint16_t portsc; + + /* Disable port */ + portsc = inw ( uhci->regs + UHCI_PORTSC ( port->address ) ); + portsc &= ~UHCI_PORTSC_PED; + outw ( portsc, uhci->regs + UHCI_PORTSC ( port->address ) ); + + return 0; +} + +/** + * Update root hub port speed + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int uhci_root_speed ( struct usb_hub *hub, struct usb_port *port ) { + struct uhci_device *uhci = usb_hub_get_drvdata ( hub ); + struct pci_device pci; + uint16_t portsc; + unsigned int speed; + + /* Read port status */ + portsc = inw ( uhci->regs + UHCI_PORTSC ( port->address ) ); + if ( ! ( portsc & UHCI_PORTSC_CCS ) ) { + /* Port not connected */ + speed = USB_SPEED_NONE; + } else if ( uhci->companion && + ! find_usb_bus_by_location ( BUS_TYPE_PCI, + uhci->companion ) ) { + /* Defer connection detection until companion + * controller has been enumerated. + */ + pci_init ( &pci, uhci->companion ); + DBGC ( uhci, "UHCI %s-%d deferring for companion " PCI_FMT "\n", + uhci->name, port->address, PCI_ARGS ( &pci ) ); + speed = USB_SPEED_NONE; + } else if ( portsc & UHCI_PORTSC_LS ) { + /* Low-speed device */ + speed = USB_SPEED_LOW; + } else { + /* Full-speed device */ + speed = USB_SPEED_FULL; + } + port->speed = speed; + + /* Record disconnections and clear changes */ + port->disconnected |= ( portsc & UHCI_PORTSC_CSC ); + outw ( portsc, uhci->regs + UHCI_PORTSC ( port->address ) ); + + return 0; +} + +/** + * Clear transaction translator buffer + * + * @v hub USB hub + * @v port USB port + * @v ep USB endpoint + * @ret rc Return status code + */ +static int uhci_root_clear_tt ( struct usb_hub *hub, struct usb_port *port, + struct usb_endpoint *ep ) { + struct uhci_device *uhci = usb_hub_get_drvdata ( hub ); + + /* Should never be called; this is a root hub */ + DBGC ( uhci, "UHCI %s-%d nonsensical CLEAR_TT for %s %s\n", uhci->name, + port->address, ep->usb->name, usb_endpoint_name ( ep ) ); + + return -ENOTSUP; +} + +/** + * Poll for port status changes + * + * @v hub USB hub + * @v port USB port + */ +static void uhci_root_poll ( struct usb_hub *hub, struct usb_port *port ) { + struct uhci_device *uhci = usb_hub_get_drvdata ( hub ); + uint16_t portsc; + uint16_t change; + + /* Do nothing unless something has changed */ + portsc = inw ( uhci->regs + UHCI_PORTSC ( port->address ) ); + change = ( portsc & UHCI_PORTSC_CHANGE ); + if ( ! change ) + return; + + /* Record disconnections and clear changes */ + port->disconnected |= ( portsc & UHCI_PORTSC_CSC ); + outw ( portsc, uhci->regs + UHCI_PORTSC ( port->address ) ); + + /* Report port status change */ + usb_port_changed ( port ); +} + +/****************************************************************************** + * + * Bus operations + * + ****************************************************************************** + */ + +/** + * Open USB bus + * + * @v bus USB bus + * @ret rc Return status code + */ +static int uhci_bus_open ( struct usb_bus *bus ) { + struct uhci_device *uhci = usb_bus_get_hostdata ( bus ); + int rc; + + /* Sanity checks */ + assert ( list_empty ( &uhci->async ) ); + assert ( list_empty ( &uhci->periodic ) ); + + /* Allocate and initialise asynchronous queue head */ + uhci->head = malloc_dma ( sizeof ( *uhci->head ), UHCI_ALIGN ); + if ( ! uhci->head ) { + rc = -ENOMEM; + goto err_alloc_head; + } + if ( ( rc = uhci_reachable ( uhci->head, sizeof ( *uhci->head ) ) ) !=0) + goto err_unreachable_head; + memset ( uhci->head, 0, sizeof ( *uhci->head ) ); + uhci->head->current = cpu_to_le32 ( UHCI_LINK_TERMINATE ); + uhci_async_schedule ( uhci ); + + /* Allocate periodic frame list */ + uhci->frame = malloc_dma ( sizeof ( *uhci->frame ), + sizeof ( *uhci->frame ) ); + if ( ! uhci->frame ) { + rc = -ENOMEM; + goto err_alloc_frame; + } + if ( ( rc = uhci_reachable ( uhci->frame, + sizeof ( *uhci->frame ) ) ) != 0 ) + goto err_unreachable_frame; + uhci_periodic_schedule ( uhci ); + outl ( virt_to_phys ( uhci->frame ), uhci->regs + UHCI_FLBASEADD ); + + /* Start controller */ + uhci_run ( uhci ); + + return 0; + + uhci_stop ( uhci ); + err_unreachable_frame: + free_dma ( uhci->frame, sizeof ( *uhci->frame ) ); + err_alloc_frame: + err_unreachable_head: + free_dma ( uhci->head, sizeof ( *uhci->head ) ); + err_alloc_head: + return rc; +} + +/** + * Close USB bus + * + * @v bus USB bus + */ +static void uhci_bus_close ( struct usb_bus *bus ) { + struct uhci_device *uhci = usb_bus_get_hostdata ( bus ); + + /* Sanity checks */ + assert ( list_empty ( &uhci->async ) ); + assert ( list_empty ( &uhci->periodic ) ); + + /* Stop controller */ + uhci_stop ( uhci ); + + /* Free periodic frame list */ + free_dma ( uhci->frame, sizeof ( *uhci->frame ) ); + + /* Free asynchronous schedule */ + free_dma ( uhci->head, sizeof ( *uhci->head ) ); +} + +/** + * Poll USB bus + * + * @v bus USB bus + */ +static void uhci_bus_poll ( struct usb_bus *bus ) { + struct uhci_device *uhci = usb_bus_get_hostdata ( bus ); + struct usb_hub *hub = bus->hub; + struct uhci_endpoint *endpoint; + unsigned int i; + + /* UHCI defers interrupts (including short packet detection) + * until the end of the frame. This can result in bulk IN + * endpoints remaining halted for much of the time, waiting + * for software action to reset the data toggles. We + * therefore ignore USBSTS and unconditionally poll all + * endpoints for completed transfer descriptors. + * + * As with EHCI, we trust that completion handlers are minimal + * and will not do anything that could plausibly affect the + * endpoint list itself. + */ + list_for_each_entry ( endpoint, &uhci->endpoints, list ) + uhci_endpoint_poll ( endpoint ); + + /* UHCI provides no single bit to indicate that a port status + * change has occurred. We therefore unconditionally iterate + * over all ports looking for status changes. + */ + for ( i = 1 ; i <= UHCI_PORTS ; i++ ) + uhci_root_poll ( hub, usb_port ( hub, i ) ); +} + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** USB host controller operations */ +static struct usb_host_operations uhci_operations = { + .endpoint = { + .open = uhci_endpoint_open, + .close = uhci_endpoint_close, + .reset = uhci_endpoint_reset, + .mtu = uhci_endpoint_mtu, + .message = uhci_endpoint_message, + .stream = uhci_endpoint_stream, + }, + .device = { + .open = uhci_device_open, + .close = uhci_device_close, + .address = uhci_device_address, + }, + .bus = { + .open = uhci_bus_open, + .close = uhci_bus_close, + .poll = uhci_bus_poll, + }, + .hub = { + .open = uhci_hub_open, + .close = uhci_hub_close, + }, + .root = { + .open = uhci_root_open, + .close = uhci_root_close, + .enable = uhci_root_enable, + .disable = uhci_root_disable, + .speed = uhci_root_speed, + .clear_tt = uhci_root_clear_tt, + }, +}; + +/** + * Locate EHCI companion controller (when no EHCI support is present) + * + * @v pci PCI device + * @ret busdevfn EHCI companion controller bus:dev.fn (if any) + */ +__weak unsigned int ehci_companion ( struct pci_device *pci __unused ) { + return 0; +} + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int uhci_probe ( struct pci_device *pci ) { + struct uhci_device *uhci; + struct usb_port *port; + unsigned int i; + int rc; + + /* Allocate and initialise structure */ + uhci = zalloc ( sizeof ( *uhci ) ); + if ( ! uhci ) { + rc = -ENOMEM; + goto err_alloc; + } + uhci->name = pci->dev.name; + INIT_LIST_HEAD ( &uhci->endpoints ); + INIT_LIST_HEAD ( &uhci->async ); + INIT_LIST_HEAD ( &uhci->periodic ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Identify EHCI companion controller, if any */ + uhci->companion = ehci_companion ( pci ); + + /* Claim ownership from BIOS. (There is no release mechanism + * for UHCI.) + */ + pci_write_config_word ( pci, UHCI_USBLEGSUP, UHCI_USBLEGSUP_DEFAULT ); + + /* Map registers */ + uhci->regs = pci->ioaddr; + if ( ! uhci->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Reset device */ + if ( ( rc = uhci_reset ( uhci ) ) != 0 ) + goto err_reset; + + /* Allocate USB bus */ + uhci->bus = alloc_usb_bus ( &pci->dev, UHCI_PORTS, UHCI_MTU, + &uhci_operations ); + if ( ! uhci->bus ) { + rc = -ENOMEM; + goto err_alloc_bus; + } + usb_bus_set_hostdata ( uhci->bus, uhci ); + usb_hub_set_drvdata ( uhci->bus->hub, uhci ); + + /* Set port protocols */ + for ( i = 1 ; i <= UHCI_PORTS ; i++ ) { + port = usb_port ( uhci->bus->hub, i ); + port->protocol = USB_PROTO_2_0; + } + + /* Register USB bus */ + if ( ( rc = register_usb_bus ( uhci->bus ) ) != 0 ) + goto err_register; + + pci_set_drvdata ( pci, uhci ); + return 0; + + unregister_usb_bus ( uhci->bus ); + err_register: + free_usb_bus ( uhci->bus ); + err_alloc_bus: + uhci_reset ( uhci ); + err_reset: + err_ioremap: + free ( uhci ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void uhci_remove ( struct pci_device *pci ) { + struct uhci_device *uhci = pci_get_drvdata ( pci ); + struct usb_bus *bus = uhci->bus; + + unregister_usb_bus ( bus ); + assert ( list_empty ( &uhci->async ) ); + assert ( list_empty ( &uhci->periodic ) ); + free_usb_bus ( bus ); + uhci_reset ( uhci ); + free ( uhci ); +} + +/** UHCI PCI device IDs */ +static struct pci_device_id uhci_ids[] = { + PCI_ROM ( 0xffff, 0xffff, "uhci", "UHCI", 0 ), +}; + +/** UHCI PCI driver */ +struct pci_driver uhci_driver __pci_driver = { + .ids = uhci_ids, + .id_count = ( sizeof ( uhci_ids ) / sizeof ( uhci_ids[0] ) ), + .class = PCI_CLASS_ID ( PCI_CLASS_SERIAL, PCI_CLASS_SERIAL_USB, + PCI_CLASS_SERIAL_USB_UHCI ), + .probe = uhci_probe, + .remove = uhci_remove, +}; diff --git a/qemu/roms/ipxe/src/drivers/usb/uhci.h b/qemu/roms/ipxe/src/drivers/usb/uhci.h new file mode 100644 index 000000000..ba4c28f7e --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/usb/uhci.h @@ -0,0 +1,350 @@ +#ifndef _IPXE_UHCI_H +#define _IPXE_UHCI_H + +/** @file + * + * USB Universal Host Controller Interface (UHCI) driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** Minimum alignment required for data structures + * + * With the exception of the frame list (which is page-aligned), data + * structures used by UHCI generally require 16-byte alignment. + */ +#define UHCI_ALIGN 16 + +/** Number of ports */ +#define UHCI_PORTS 2 + +/** Maximum transfer size */ +#define UHCI_MTU 1280 + +/** I/O BAR size */ +#define UHCI_BAR_SIZE 0x14 + +/** USB command register */ +#define UHCI_USBCMD 0x00 + +/** Max packet is 64 bytes */ +#define UHCI_USBCMD_MAX64 0x0080 + +/** Host controller reset */ +#define UHCI_USBCMD_HCRESET 0x0002 + +/** Run/stop */ +#define UHCI_USBCMD_RUN 0x0001 + +/** USB status register */ +#define UHCI_USBSTS 0x02 + +/** Host controller halted */ +#define UHCI_USBSTS_HCHALTED 0x0020 + +/** USB interrupt */ +#define UHCI_USBSTS_USBINT 0x0001 + +/** Frame list base address register */ +#define UHCI_FLBASEADD 0x08 + +/** Port status and control register */ +#define UHCI_PORTSC(port) ( 0x0e + ( (port) << 1 ) ) + +/** Port reset */ +#define UHCI_PORTSC_PR 0x0200 + +/** Low-speed device attached */ +#define UHCI_PORTSC_LS 0x0100 + +/** Port enabled/disabled change */ +#define UHCI_PORTSC_PEC 0x0008 + +/** Port enabled */ +#define UHCI_PORTSC_PED 0x0004 + +/** Connect status change */ +#define UHCI_PORTSC_CSC 0x0002 + +/** Current connect status */ +#define UHCI_PORTSC_CCS 0x0001 + +/** Port status change mask */ +#define UHCI_PORTSC_CHANGE ( UHCI_PORTSC_CSC | UHCI_PORTSC_PEC ) + +/** Depth-first processing */ +#define UHCI_LINK_DEPTH_FIRST 0x00000004UL + +/** Queue head type */ +#define UHCI_LINK_TYPE_QH 0x00000002UL + +/** List terminator */ +#define UHCI_LINK_TERMINATE 0x00000001UL + +/** Number of frames in frame list */ +#define UHCI_FRAMES 1024 + +/** A frame list */ +struct uhci_frame_list { + /** Link pointer */ + uint32_t link[UHCI_FRAMES]; +} __attribute__ (( packed )); + +/** A transfer descriptor */ +struct uhci_transfer_descriptor { + /** Link pointer */ + uint32_t link; + /** Actual length */ + uint16_t actual; + /** Status */ + uint8_t status; + /** Flags */ + uint8_t flags; + /** Control */ + uint32_t control; + /** Buffer pointer */ + uint32_t data; +} __attribute__ (( packed )); + +/** Length mask */ +#define UHCI_LEN_MASK 0x7ff + +/** Actual length */ +#define UHCI_ACTUAL_LEN( actual ) ( ( (actual) + 1 ) & UHCI_LEN_MASK ) + +/** Active */ +#define UHCI_STATUS_ACTIVE 0x80 + +/** Stalled */ +#define UHCI_STATUS_STALLED 0x40 + +/** Data buffer error */ +#define UHCI_STATUS_BUFFER 0x20 + +/** Babble detected */ +#define UHCI_STATUS_BABBLE 0x10 + +/** NAK received */ +#define UHCI_STATUS_NAK 0x08 + +/** CRC/timeout error */ +#define UHCI_STATUS_CRC_TIMEOUT 0x04 + +/** Bitstuff error */ +#define UHCI_STATUS_BITSTUFF 0x02 + +/** Short packet detect */ +#define UHCI_FL_SPD 0x20 + +/** Error counter */ +#define UHCI_FL_CERR( count ) ( (count) << 3 ) + +/** Error counter maximum value */ +#define UHCI_FL_CERR_MAX UHCI_FL_CERR ( 3 ) + +/** Low speed device */ +#define UHCI_FL_LS 0x04 + +/** Interrupt on completion */ +#define UHCI_FL_IOC 0x01 + +/** Packet ID */ +#define UHCI_CONTROL_PID( pid ) ( (pid) << 0 ) + +/** Packet ID mask */ +#define UHCI_CONTROL_PID_MASK UHCI_CONTROL_PID ( 0xff ) + +/** Device address */ +#define UHCI_CONTROL_DEVICE( address ) ( (address) << 8 ) + +/** Endpoint address */ +#define UHCI_CONTROL_ENDPOINT( address ) ( (address) << 15 ) + +/** Data toggle */ +#define UHCI_CONTROL_TOGGLE ( 1 << 19 ) + +/** Data length */ +#define UHCI_CONTROL_LEN( len ) ( ( ( (len) - 1 ) & UHCI_LEN_MASK ) << 21 ) + +/** Check for data packet + * + * This check is based on the fact that only USB_PID_SETUP has bit 2 + * set. + */ +#define UHCI_DATA_PACKET( control ) ( ! ( control & 0x04 ) ) + +/** Check for short packet */ +#define UHCI_SHORT_PACKET( control, actual ) \ + ( ( ( (control) >> 21 ) ^ (actual) ) & UHCI_LEN_MASK ) + +/** USB legacy support register (in PCI configuration space) */ +#define UHCI_USBLEGSUP 0xc0 + +/** USB legacy support default value */ +#define UHCI_USBLEGSUP_DEFAULT 0x2000 + +/** A queue head */ +struct uhci_queue_head { + /** Horizontal link pointer */ + uint32_t link; + /** Current transfer descriptor */ + uint32_t current; +} __attribute__ (( packed )); + +/** A single UHCI transfer + * + * UHCI hardware is extremely simple, and requires software to build + * the entire packet schedule (including manually handling all of the + * data toggles). The hardware requires at least 16 bytes of transfer + * descriptors per 64 bytes of transmitted/received data. We allocate + * the transfer descriptors at the time that the transfer is enqueued, + * to avoid the need to allocate unreasonably large blocks when the + * endpoint is opened. + */ +struct uhci_transfer { + /** Producer counter */ + unsigned int prod; + /** Consumer counter */ + unsigned int cons; + /** Completed data length */ + size_t len; + + /** Transfer descriptors */ + struct uhci_transfer_descriptor *desc; + + /** I/O buffer */ + struct io_buffer *iobuf; +}; + +/** Number of transfer descriptors in a ring + * + * This is a policy decision. + */ +#define UHCI_RING_COUNT 16 + +/** A transfer ring */ +struct uhci_ring { + /** Producer counter */ + unsigned int prod; + /** Consumer counter */ + unsigned int cons; + + /** Maximum packet length */ + size_t mtu; + /** Base flags + * + * This incorporates the CERR and LS bits + */ + uint8_t flags; + /** Base control word + * + * This incorporates the device address, the endpoint address, + * and the data toggle for the next descriptor to be enqueued. + */ + uint32_t control; + + /** Transfers */ + struct uhci_transfer *xfer[UHCI_RING_COUNT]; + /** End of transfer ring (if non-empty) */ + struct uhci_transfer *end; + + /** Queue head */ + struct uhci_queue_head *head; +}; + +/** + * Calculate space used in transfer ring + * + * @v ring Transfer ring + * @ret fill Number of entries used + */ +static inline __attribute__ (( always_inline )) unsigned int +uhci_ring_fill ( struct uhci_ring *ring ) { + unsigned int fill; + + fill = ( ring->prod - ring->cons ); + assert ( fill <= UHCI_RING_COUNT ); + return fill; +} + +/** + * Calculate space remaining in transfer ring + * + * @v ring Transfer ring + * @ret remaining Number of entries remaining + */ +static inline __attribute__ (( always_inline )) unsigned int +uhci_ring_remaining ( struct uhci_ring *ring ) { + unsigned int fill = uhci_ring_fill ( ring ); + + return ( UHCI_RING_COUNT - fill ); +} + +/** Maximum time to wait for host controller to stop + * + * This is a policy decision. + */ +#define UHCI_STOP_MAX_WAIT_MS 100 + +/** Maximum time to wait for reset to complete + * + * This is a policy decision. + */ +#define UHCI_RESET_MAX_WAIT_MS 500 + +/** Maximum time to wait for a port to be enabled + * + * This is a policy decision. + */ +#define UHCI_PORT_ENABLE_MAX_WAIT_MS 500 + +/** A UHCI device */ +struct uhci_device { + /** Registers */ + unsigned long regs; + /** Name */ + const char *name; + + /** EHCI companion controller bus:dev.fn address (if any) */ + unsigned int companion; + + /** Asynchronous queue head */ + struct uhci_queue_head *head; + /** Frame list */ + struct uhci_frame_list *frame; + + /** List of all endpoints */ + struct list_head endpoints; + /** Asynchronous schedule */ + struct list_head async; + /** Periodic schedule + * + * Listed in decreasing order of endpoint interval. + */ + struct list_head periodic; + + /** USB bus */ + struct usb_bus *bus; +}; + +/** A UHCI endpoint */ +struct uhci_endpoint { + /** UHCI device */ + struct uhci_device *uhci; + /** USB endpoint */ + struct usb_endpoint *ep; + /** List of all endpoints */ + struct list_head list; + /** Endpoint schedule */ + struct list_head schedule; + + /** Transfer ring */ + struct uhci_ring ring; +}; + +#endif /* _IPXE_UHCI_H */ diff --git a/qemu/roms/ipxe/src/drivers/usb/usbhid.c b/qemu/roms/ipxe/src/drivers/usb/usbhid.c new file mode 100644 index 000000000..c74535a05 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/usb/usbhid.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** @file + * + * USB human interface devices (HID) + * + */ + +/** + * Open USB human interface device + * + * @v hid USB human interface device + * @ret rc Return status code + */ +int usbhid_open ( struct usb_hid *hid ) { + int rc; + + /* Open interrupt IN endpoint */ + if ( ( rc = usb_endpoint_open ( &hid->in ) ) != 0 ) { + DBGC ( hid, "HID %s could not open interrupt IN: %s\n", + hid->func->name, strerror ( rc ) ); + goto err_open_in; + } + + /* Refill interrupt IN endpoint */ + if ( ( rc = usb_refill ( &hid->in ) ) != 0 ) { + DBGC ( hid, "HID %s could not refill interrupt IN: %s\n", + hid->func->name, strerror ( rc ) ); + goto err_refill_in; + } + + /* Open interrupt OUT endpoint, if applicable */ + if ( hid->out.usb && + ( ( rc = usb_endpoint_open ( &hid->out ) ) != 0 ) ) { + DBGC ( hid, "HID %s could not open interrupt OUT: %s\n", + hid->func->name, strerror ( rc ) ); + goto err_open_out; + } + + return 0; + + usb_endpoint_close ( &hid->out ); + err_open_out: + err_refill_in: + usb_endpoint_close ( &hid->in ); + err_open_in: + return rc; +} + +/** + * Close USB human interface device + * + * @v hid USB human interface device + */ +void usbhid_close ( struct usb_hid *hid ) { + + /* Close interrupt OUT endpoint, if applicable */ + if ( hid->out.usb ) + usb_endpoint_close ( &hid->out ); + + /* Close interrupt IN endpoint */ + usb_endpoint_close ( &hid->in ); +} + +/** + * Refill USB human interface device endpoints + * + * @v hid USB human interface device + * @ret rc Return status code + */ +int usbhid_refill ( struct usb_hid *hid ) { + int rc; + + /* Refill interrupt IN endpoint */ + if ( ( rc = usb_refill ( &hid->in ) ) != 0 ) + return rc; + + /* Refill interrupt OUT endpoint, if applicable */ + if ( hid->out.usb && ( ( rc = usb_refill ( &hid->out ) ) != 0 ) ) + return rc; + + return 0; +} + +/** + * Describe USB human interface device + * + * @v hid USB human interface device + * @v config Configuration descriptor + * @ret rc Return status code + */ +int usbhid_describe ( struct usb_hid *hid, + struct usb_configuration_descriptor *config ) { + struct usb_interface_descriptor *desc; + int rc; + + /* Locate interface descriptor */ + desc = usb_interface_descriptor ( config, hid->func->interface[0], 0 ); + if ( ! desc ) { + DBGC ( hid, "HID %s has no interface descriptor\n", + hid->func->name ); + return -EINVAL; + } + + /* Describe interrupt IN endpoint */ + if ( ( rc = usb_endpoint_described ( &hid->in, config, desc, + USB_INTERRUPT_IN, 0 ) ) != 0 ) { + DBGC ( hid, "HID %s could not describe interrupt IN: %s\n", + hid->func->name, strerror ( rc ) ); + return rc; + } + + /* Describe interrupt OUT endpoint, if applicable */ + if ( hid->out.usb && + ( ( rc = usb_endpoint_described ( &hid->out, config, desc, + USB_INTERRUPT_OUT, 0 ) ) != 0 )){ + DBGC ( hid, "HID %s could not describe interrupt OUT: %s\n", + hid->func->name, strerror ( rc ) ); + return rc; + } + + return 0; +} diff --git a/qemu/roms/ipxe/src/drivers/usb/usbhub.c b/qemu/roms/ipxe/src/drivers/usb/usbhub.c new file mode 100644 index 000000000..bf2a20005 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/usb/usbhub.c @@ -0,0 +1,547 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include "usbhub.h" + +/** @file + * + * USB hub driver + * + */ + +/** + * Refill interrupt ring + * + * @v hubdev Hub device + */ +static void hub_refill ( struct usb_hub_device *hubdev ) { + int rc; + + /* Refill interrupt endpoint */ + if ( ( rc = usb_refill ( &hubdev->intr ) ) != 0 ) { + DBGC ( hubdev, "HUB %s could not refill interrupt: %s\n", + hubdev->name, strerror ( rc ) ); + /* Continue attempting to refill */ + return; + } + + /* Stop refill process */ + process_del ( &hubdev->refill ); +} + +/** Refill process descriptor */ +static struct process_descriptor hub_refill_desc = + PROC_DESC ( struct usb_hub_device, refill, hub_refill ); + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void hub_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct usb_hub_device *hubdev = + container_of ( ep, struct usb_hub_device, intr ); + struct usb_hub *hub = hubdev->hub; + uint8_t *data = iobuf->data; + unsigned int bits = ( 8 * iob_len ( iobuf ) ); + unsigned int i; + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto done; + + /* Ignore packets with errors */ + if ( rc != 0 ) { + DBGC ( hubdev, "HUB %s interrupt failed: %s\n", + hubdev->name, strerror ( rc ) ); + DBGC_HDA ( hubdev, 0, iobuf->data, iob_len ( iobuf ) ); + goto done; + } + + /* Report any port status changes */ + for ( i = 1 ; i <= hub->ports ; i++ ) { + + /* Sanity check */ + if ( i > bits ) { + DBGC ( hubdev, "HUB %s underlength interrupt:\n", + hubdev->name ); + DBGC_HDA ( hubdev, 0, iobuf->data, iob_len ( iobuf ) ); + goto done; + } + + /* Report port status change if applicable */ + if ( data[ i / 8 ] & ( 1 << ( i % 8 ) ) ) { + DBGC2 ( hubdev, "HUB %s port %d status changed\n", + hubdev->name, i ); + usb_port_changed ( usb_port ( hub, i ) ); + } + } + + done: + /* Start refill process */ + process_add ( &hubdev->refill ); +} + +/** Interrupt endpoint operations */ +static struct usb_endpoint_driver_operations usb_hub_intr_operations = { + .complete = hub_complete, +}; + +/** + * Open hub + * + * @v hub USB hub + * @ret rc Return status code + */ +static int hub_open ( struct usb_hub *hub ) { + struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub ); + struct usb_device *usb = hubdev->usb; + unsigned int i; + int rc; + + /* Ensure ports are powered */ + for ( i = 1 ; i <= hub->ports ; i++ ) { + if ( ( rc = usb_hub_set_port_feature ( usb, i, + USB_HUB_PORT_POWER, + 0 ) ) != 0 ) { + DBGC ( hubdev, "HUB %s port %d could not apply power: " + "%s\n", hubdev->name, i, strerror ( rc ) ); + goto err_power; + } + } + + /* Open interrupt endpoint */ + if ( ( rc = usb_endpoint_open ( &hubdev->intr ) ) != 0 ) { + DBGC ( hubdev, "HUB %s could not register interrupt: %s\n", + hubdev->name, strerror ( rc ) ); + goto err_open; + } + + /* Start refill process */ + process_add ( &hubdev->refill ); + + /* Refill interrupt ring */ + hub_refill ( hubdev ); + + return 0; + + usb_endpoint_close ( &hubdev->intr ); + err_open: + err_power: + return rc; +} + +/** + * Close hub + * + * @v hub USB hub + */ +static void hub_close ( struct usb_hub *hub ) { + struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub ); + + /* Close interrupt endpoint */ + usb_endpoint_close ( &hubdev->intr ); + + /* Stop refill process */ + process_del ( &hubdev->refill ); +} + +/** + * Enable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int hub_enable ( struct usb_hub *hub, struct usb_port *port ) { + struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub ); + struct usb_device *usb = hubdev->usb; + struct usb_hub_port_status status; + unsigned int current; + unsigned int i; + int rc; + + /* Initiate reset if applicable */ + if ( ( hub->protocol < USB_PROTO_3_0 ) && + ( ( rc = usb_hub_set_port_feature ( usb, port->address, + USB_HUB_PORT_RESET, 0 ) )!=0)){ + DBGC ( hubdev, "HUB %s port %d could not initiate reset: %s\n", + hubdev->name, port->address, strerror ( rc ) ); + return rc; + } + + /* Wait for port to become enabled */ + for ( i = 0 ; i < USB_HUB_ENABLE_MAX_WAIT_MS ; i++ ) { + + /* Check for port being enabled */ + if ( ( rc = usb_hub_get_port_status ( usb, port->address, + &status ) ) != 0 ) { + DBGC ( hubdev, "HUB %s port %d could not get status: " + "%s\n", hubdev->name, port->address, + strerror ( rc ) ); + return rc; + } + current = le16_to_cpu ( status.current ); + if ( current & ( 1 << USB_HUB_PORT_ENABLE ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( hubdev, "HUB %s port %d timed out waiting for enable\n", + hubdev->name, port->address ); + return -ETIMEDOUT; +} + +/** + * Disable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int hub_disable ( struct usb_hub *hub, struct usb_port *port ) { + struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub ); + struct usb_device *usb = hubdev->usb; + int rc; + + /* Disable port */ + if ( ( rc = usb_hub_clear_port_feature ( usb, port->address, + USB_HUB_PORT_ENABLE, 0 ) )!=0){ + DBGC ( hubdev, "HUB %s port %d could not disable: %s\n", + hubdev->name, port->address, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Clear port status change bits + * + * @v hubdev USB hub device + * @v port Port number + * @v changed Port status change bits + * @ret rc Return status code + */ +static int hub_clear_changes ( struct usb_hub_device *hubdev, + unsigned int port, uint16_t changed ) { + struct usb_device *usb = hubdev->usb; + unsigned int bit; + unsigned int feature; + int rc; + + /* Clear each set bit */ + for ( bit = 0 ; bit < 16 ; bit++ ) { + + /* Skip unset bits */ + if ( ! ( changed & ( 1 << bit ) ) ) + continue; + + /* Skip unused features */ + feature = USB_HUB_C_FEATURE ( bit ); + if ( ! ( hubdev->features & ( 1 << feature ) ) ) + continue; + + /* Clear bit */ + if ( ( rc = usb_hub_clear_port_feature ( usb, port, + feature, 0 ) ) != 0 ) { + DBGC ( hubdev, "HUB %s port %d could not clear feature " + "%d: %s\n", hubdev->name, port, feature, + strerror ( rc ) ); + return rc; + } + } + + return 0; +} + +/** + * Update port speed + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int hub_speed ( struct usb_hub *hub, struct usb_port *port ) { + struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub ); + struct usb_device *usb = hubdev->usb; + struct usb_hub_port_status status; + unsigned int current; + unsigned int changed; + int rc; + + /* Get port status */ + if ( ( rc = usb_hub_get_port_status ( usb, port->address, + &status ) ) != 0 ) { + DBGC ( hubdev, "HUB %s port %d could not get status: %s\n", + hubdev->name, port->address, strerror ( rc ) ); + return rc; + } + current = le16_to_cpu ( status.current ); + changed = le16_to_cpu ( status.changed ); + DBGC2 ( hubdev, "HUB %s port %d status is %04x:%04x\n", + hubdev->name, port->address, changed, current ); + + /* Update port speed */ + if ( current & ( 1 << USB_HUB_PORT_CONNECTION ) ) { + if ( hub->protocol >= USB_PROTO_3_0 ) { + port->speed = USB_SPEED_SUPER; + } else if ( current & ( 1 << USB_HUB_PORT_LOW_SPEED ) ) { + port->speed = USB_SPEED_LOW; + } else if ( current & ( 1 << USB_HUB_PORT_HIGH_SPEED ) ) { + port->speed = USB_SPEED_HIGH; + } else { + port->speed = USB_SPEED_FULL; + } + } else { + port->speed = USB_SPEED_NONE; + } + + /* Record disconnections */ + port->disconnected |= ( changed & ( 1 << USB_HUB_PORT_CONNECTION ) ); + + /* Clear port status change bits */ + if ( ( rc = hub_clear_changes ( hubdev, port->address, changed ) ) != 0) + return rc; + + return 0; +} + +/** + * Clear transaction translator buffer + * + * @v hub USB hub + * @v port USB port + * @v ep USB endpoint + * @ret rc Return status code + */ +static int hub_clear_tt ( struct usb_hub *hub, struct usb_port *port, + struct usb_endpoint *ep ) { + struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub ); + struct usb_device *usb = hubdev->usb; + int rc; + + /* Clear transaction translator buffer. All hubs must support + * single-TT operation; we simplify our code by supporting + * only this configuration. + */ + if ( ( rc = usb_hub_clear_tt_buffer ( usb, ep->usb->address, + ep->address, ep->attributes, + USB_HUB_TT_SINGLE ) ) != 0 ) { + DBGC ( hubdev, "HUB %s port %d could not clear TT buffer: %s\n", + hubdev->name, port->address, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** USB hub operations */ +static struct usb_hub_driver_operations hub_operations = { + .open = hub_open, + .close = hub_close, + .enable = hub_enable, + .disable = hub_disable, + .speed = hub_speed, + .clear_tt = hub_clear_tt, +}; + +/** + * Probe USB hub + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int hub_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct usb_bus *bus = usb->port->hub->bus; + struct usb_hub_device *hubdev; + struct usb_interface_descriptor *interface; + union usb_hub_descriptor desc; + unsigned int depth; + unsigned int ports; + int enhanced; + int rc; + + /* Allocate and initialise structure */ + hubdev = zalloc ( sizeof ( *hubdev ) ); + if ( ! hubdev ) { + rc = -ENOMEM; + goto err_alloc; + } + enhanced = ( usb->port->protocol >= USB_PROTO_3_0 ); + hubdev->name = func->name; + hubdev->usb = usb; + hubdev->features = + ( enhanced ? USB_HUB_FEATURES_ENHANCED : USB_HUB_FEATURES ); + usb_endpoint_init ( &hubdev->intr, usb, &usb_hub_intr_operations ); + usb_refill_init ( &hubdev->intr, 0, USB_HUB_INTR_FILL ); + process_init_stopped ( &hubdev->refill, &hub_refill_desc, NULL ); + + /* Locate hub interface descriptor */ + interface = usb_interface_descriptor ( config, func->interface[0], 0 ); + if ( ! interface ) { + DBGC ( hubdev, "HUB %s has no interface descriptor\n", + hubdev->name ); + rc = -EINVAL; + goto err_interface; + } + + /* Locate interrupt endpoint descriptor */ + if ( ( rc = usb_endpoint_described ( &hubdev->intr, config, interface, + USB_INTERRUPT_IN, 0 ) ) != 0 ) { + DBGC ( hubdev, "HUB %s could not describe interrupt endpoint: " + "%s\n", hubdev->name, strerror ( rc ) ); + goto err_endpoint; + } + + /* Set hub depth */ + depth = usb_depth ( usb ); + if ( enhanced ) { + if ( ( rc = usb_hub_set_hub_depth ( usb, depth ) ) != 0 ) { + DBGC ( hubdev, "HUB %s could not set hub depth to %d: " + "%s\n", hubdev->name, depth, strerror ( rc ) ); + goto err_set_hub_depth; + } + } + + /* Get hub descriptor */ + if ( ( rc = usb_hub_get_descriptor ( usb, enhanced, &desc ) ) != 0 ) { + DBGC ( hubdev, "HUB %s could not get hub descriptor: %s\n", + hubdev->name, strerror ( rc ) ); + goto err_hub_descriptor; + } + ports = desc.basic.ports; + DBGC ( hubdev, "HUB %s has %d ports at depth %d%s\n", hubdev->name, + ports, depth, ( enhanced ? " (enhanced)" : "" ) ); + + /* Allocate hub */ + hubdev->hub = alloc_usb_hub ( bus, usb, ports, &hub_operations ); + if ( ! hubdev->hub ) { + rc = -ENOMEM; + goto err_alloc_hub; + } + usb_hub_set_drvdata ( hubdev->hub, hubdev ); + + /* Register hub */ + if ( ( rc = register_usb_hub ( hubdev->hub ) ) != 0 ) { + DBGC ( hubdev, "HUB %s could not register: %s\n", + hubdev->name, strerror ( rc ) ); + goto err_register_hub; + } + + usb_func_set_drvdata ( func, hubdev ); + return 0; + + unregister_usb_hub ( hubdev->hub ); + err_register_hub: + free_usb_hub ( hubdev->hub ); + err_alloc_hub: + err_hub_descriptor: + err_set_hub_depth: + err_endpoint: + err_interface: + free ( hubdev ); + err_alloc: + return rc; +} + +/** + * Remove USB hub + * + * @v func USB function + * @ret rc Return status code + */ +static void hub_remove ( struct usb_function *func ) { + struct usb_hub_device *hubdev = usb_func_get_drvdata ( func ); + struct usb_hub *hub = hubdev->hub; + struct usb_device *usb = hubdev->usb; + struct usb_port *port; + unsigned int i; + + /* If hub has been unplugged, mark all ports as unplugged */ + if ( usb->port->speed == USB_SPEED_NONE ) { + for ( i = 1 ; i <= hub->ports ; i++ ) { + port = usb_port ( hub, i ); + port->speed = USB_SPEED_NONE; + } + } + + /* Unregister hub */ + unregister_usb_hub ( hubdev->hub ); + assert ( ! process_running ( &hubdev->refill ) ); + + /* Free hub */ + free_usb_hub ( hubdev->hub ); + + /* Free hub device */ + free ( hubdev ); +} + +/** USB hub device IDs */ +static struct usb_device_id hub_ids[] = { + { + .name = "hub-1", + .vendor = USB_ANY_ID, + .product = USB_ANY_ID, + .class = { + .class = USB_CLASS_HUB, + .subclass = 0, + .protocol = 0, + }, + }, + { + .name = "hub-2", + .vendor = USB_ANY_ID, + .product = USB_ANY_ID, + .class = { + .class = USB_CLASS_HUB, + .subclass = 0, + .protocol = 1, + }, + }, +}; + +/** USB hub driver */ +struct usb_driver usb_hub_driver __usb_driver = { + .ids = hub_ids, + .id_count = ( sizeof ( hub_ids ) / sizeof ( hub_ids[0] ) ), + .probe = hub_probe, + .remove = hub_remove, +}; diff --git a/qemu/roms/ipxe/src/drivers/usb/usbhub.h b/qemu/roms/ipxe/src/drivers/usb/usbhub.h new file mode 100644 index 000000000..d7d8f9610 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/usb/usbhub.h @@ -0,0 +1,279 @@ +#ifndef _USBHUB_H +#define _USBHUB_H + +/** @file + * + * USB hubs + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** Request recipient is a port */ +#define USB_HUB_RECIP_PORT ( 3 << 0 ) + +/** A basic USB hub descriptor */ +struct usb_hub_descriptor_basic { + /** Descriptor header */ + struct usb_descriptor_header header; + /** Number of ports */ + uint8_t ports; + /** Characteristics */ + uint16_t characteristics; + /** Power-on delay (in 2ms intervals */ + uint8_t delay; + /** Controller current (in mA) */ + uint8_t current; +} __attribute__ (( packed )); + +/** A basic USB hub descriptor */ +#define USB_HUB_DESCRIPTOR 41 + +/** An enhanced USB hub descriptor */ +struct usb_hub_descriptor_enhanced { + /** Basic USB hub descriptor */ + struct usb_hub_descriptor_basic basic; + /** Header decode latency */ + uint8_t latency; + /** Maximum delay */ + uint16_t delay; + /** Removable device bitmask */ + uint16_t removable; +} __attribute__ (( packed )); + +/** An enhanced USB hub descriptor */ +#define USB_HUB_DESCRIPTOR_ENHANCED 42 + +/** A USB hub descriptor */ +union usb_hub_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** Basic hub descriptor */ + struct usb_hub_descriptor_basic basic; + /** Enhanced hub descriptor */ + struct usb_hub_descriptor_enhanced enhanced; +} __attribute__ (( packed )); + +/** Port status */ +struct usb_hub_port_status { + /** Current status */ + uint16_t current; + /** Changed status */ + uint16_t changed; +} __attribute__ (( packed )); + +/** Current connect status feature */ +#define USB_HUB_PORT_CONNECTION 0 + +/** Port enabled/disabled feature */ +#define USB_HUB_PORT_ENABLE 1 + +/** Port reset feature */ +#define USB_HUB_PORT_RESET 4 + +/** Port power feature */ +#define USB_HUB_PORT_POWER 8 + +/** Low-speed device attached */ +#define USB_HUB_PORT_LOW_SPEED 9 + +/** High-speed device attached */ +#define USB_HUB_PORT_HIGH_SPEED 10 + +/** Connect status changed */ +#define USB_HUB_C_PORT_CONNECTION 16 + +/** Port enable/disable changed */ +#define USB_HUB_C_PORT_ENABLE 17 + +/** Suspend changed */ +#define USB_HUB_C_PORT_SUSPEND 18 + +/** Over-current indicator changed */ +#define USB_HUB_C_PORT_OVER_CURRENT 19 + +/** Reset changed */ +#define USB_HUB_C_PORT_RESET 20 + +/** Link state changed */ +#define USB_HUB_C_PORT_LINK_STATE 25 + +/** Configuration error */ +#define USB_HUB_C_PORT_CONFIG_ERROR 26 + +/** Calculate feature from change bit number */ +#define USB_HUB_C_FEATURE( bit ) ( 16 + (bit) ) + +/** USB features */ +#define USB_HUB_FEATURES \ + ( ( 1 << USB_HUB_C_PORT_CONNECTION ) | \ + ( 1 << USB_HUB_C_PORT_ENABLE ) | \ + ( 1 << USB_HUB_C_PORT_SUSPEND ) | \ + ( 1 << USB_HUB_C_PORT_OVER_CURRENT ) | \ + ( 1 << USB_HUB_C_PORT_RESET ) ) + +/** USB features for enhanced hubs */ +#define USB_HUB_FEATURES_ENHANCED \ + ( ( 1 << USB_HUB_C_PORT_CONNECTION ) | \ + ( 1 << USB_HUB_C_PORT_OVER_CURRENT ) | \ + ( 1 << USB_HUB_C_PORT_RESET ) | \ + ( 1 << USB_HUB_C_PORT_LINK_STATE ) | \ + ( 1 << USB_HUB_C_PORT_CONFIG_ERROR ) ) + +/** Set hub depth */ +#define USB_HUB_SET_HUB_DEPTH \ + ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 12 ) ) + +/** Clear transaction translator buffer */ +#define USB_HUB_CLEAR_TT_BUFFER \ + ( USB_DIR_OUT | USB_TYPE_CLASS | USB_HUB_RECIP_PORT | \ + USB_REQUEST_TYPE ( 8 ) ) + +/** + * Get hub descriptor + * + * @v usb USB device + * @v enhanced Hub is an enhanced hub + * @v data Hub descriptor to fill in + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_hub_get_descriptor ( struct usb_device *usb, int enhanced, + union usb_hub_descriptor *data ) { + unsigned int desc; + size_t len; + + /* Determine descriptor type and length */ + desc = ( enhanced ? USB_HUB_DESCRIPTOR_ENHANCED : USB_HUB_DESCRIPTOR ); + len = ( enhanced ? sizeof ( data->enhanced ) : sizeof ( data->basic ) ); + + return usb_get_descriptor ( usb, USB_TYPE_CLASS, desc, 0, 0, + &data->header, len ); +} + +/** + * Get port status + * + * @v usb USB device + * @v port Port address + * @v status Port status descriptor to fill in + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_hub_get_port_status ( struct usb_device *usb, unsigned int port, + struct usb_hub_port_status *status ) { + + return usb_get_status ( usb, ( USB_TYPE_CLASS | USB_HUB_RECIP_PORT ), + port, status, sizeof ( *status ) ); +} + +/** + * Clear port feature + * + * @v usb USB device + * @v port Port address + * @v feature Feature to clear + * @v index Index (when clearing a port indicator) + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_hub_clear_port_feature ( struct usb_device *usb, unsigned int port, + unsigned int feature, unsigned int index ) { + + return usb_clear_feature ( usb, ( USB_TYPE_CLASS | USB_HUB_RECIP_PORT ), + feature, ( ( index << 8 ) | port ) ); +} + +/** + * Set port feature + * + * @v usb USB device + * @v port Port address + * @v feature Feature to clear + * @v index Index (when clearing a port indicator) + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_hub_set_port_feature ( struct usb_device *usb, unsigned int port, + unsigned int feature, unsigned int index ) { + + return usb_set_feature ( usb, ( USB_TYPE_CLASS | USB_HUB_RECIP_PORT ), + feature, ( ( index << 8 ) | port ) ); +} + +/** + * Set hub depth + * + * @v usb USB device + * @v depth Hub depth + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_hub_set_hub_depth ( struct usb_device *usb, unsigned int depth ) { + + return usb_control ( usb, USB_HUB_SET_HUB_DEPTH, depth, 0, NULL, 0 ); +} + +/** + * Clear transaction translator buffer + * + * @v usb USB device + * @v device Device address + * @v endpoint Endpoint address + * @v attributes Endpoint attributes + * @v tt_port Transaction translator port (or 1 for single-TT hubs) + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_hub_clear_tt_buffer ( struct usb_device *usb, unsigned int device, + unsigned int endpoint, unsigned int attributes, + unsigned int tt_port ) { + unsigned int value; + + /* Calculate value */ + value = ( ( ( endpoint & USB_ENDPOINT_MAX ) << 0 ) | ( device << 4 ) | + ( ( attributes & USB_ENDPOINT_ATTR_TYPE_MASK ) << 11 ) | + ( ( endpoint & USB_ENDPOINT_IN ) << 8 ) ); + + return usb_control ( usb, USB_HUB_CLEAR_TT_BUFFER, value, + tt_port, NULL, 0 ); +} + +/** Transaction translator port value for single-TT hubs */ +#define USB_HUB_TT_SINGLE 1 + +/** A USB hub device */ +struct usb_hub_device { + /** Name */ + const char *name; + /** USB device */ + struct usb_device *usb; + /** USB hub */ + struct usb_hub *hub; + /** Features */ + unsigned int features; + + /** Interrupt endpoint */ + struct usb_endpoint intr; + /** Interrupt endpoint refill process */ + struct process refill; +}; + +/** Interrupt ring fill level + * + * This is a policy decision. + */ +#define USB_HUB_INTR_FILL 4 + +/** Maximum time to wait for port to become enabled + * + * This is a policy decision. + */ +#define USB_HUB_ENABLE_MAX_WAIT_MS 100 + +#endif /* _USBHUB_H */ diff --git a/qemu/roms/ipxe/src/drivers/usb/usbkbd.c b/qemu/roms/ipxe/src/drivers/usb/usbkbd.c new file mode 100644 index 000000000..ea94f2e63 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/usb/usbkbd.c @@ -0,0 +1,509 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include "usbkbd.h" + +/** @file + * + * USB keyboard driver + * + */ + +/** List of USB keyboards */ +static LIST_HEAD ( usb_keyboards ); + +/****************************************************************************** + * + * Keyboard map + * + ****************************************************************************** + */ + +/** + * Map USB keycode to iPXE key + * + * @v keycode Keycode + * @v modifiers Modifiers + * @ret key iPXE key + * + * Key codes are defined in the USB HID Usage Tables Keyboard/Keypad + * page. + */ +static unsigned int usbkbd_map ( unsigned int keycode, + unsigned int modifiers ) { + unsigned int key; + + if ( keycode < USBKBD_KEY_A ) { + /* Not keys */ + key = 0; + } else if ( keycode <= USBKBD_KEY_Z ) { + /* Alphabetic keys */ + key = ( keycode - USBKBD_KEY_A + 'a' ); + if ( modifiers & USBKBD_CTRL ) { + key -= ( 'a' - CTRL_A ); + } else if ( modifiers & USBKBD_SHIFT ) { + key -= ( 'a' - 'A' ); + } + } else if ( keycode <= USBKBD_KEY_0 ) { + /* Numeric key row */ + if ( modifiers & USBKBD_SHIFT ) { + key = "!@#$%^&*()" [ keycode - USBKBD_KEY_1 ]; + } else { + key = ( ( ( keycode - USBKBD_KEY_1 + 1 ) % 10 ) + '0' ); + } + } else if ( keycode <= USBKBD_KEY_SPACE ) { + /* Unmodifiable keys */ + static const uint8_t unmodifable[] = + { LF, ESC, BACKSPACE, TAB, ' ' }; + key = unmodifable[ keycode - USBKBD_KEY_ENTER ]; + } else if ( keycode <= USBKBD_KEY_SLASH ) { + /* Punctuation keys */ + if ( modifiers & USBKBD_SHIFT ) { + key = "_+{}|~:\"~<>?" [ keycode - USBKBD_KEY_MINUS ]; + } else { + key = "-=[]\\#;'`,./" [ keycode - USBKBD_KEY_MINUS ]; + } + } else if ( keycode <= USBKBD_KEY_UP ) { + /* Special keys */ + static const uint16_t special[] = { + 0, 0, 0, 0, 0, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, + KEY_F10, KEY_F11, KEY_F12, 0, 0, 0, KEY_IC, KEY_HOME, + KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT, + KEY_LEFT, KEY_DOWN, KEY_UP + }; + key = special[ keycode - USBKBD_KEY_CAPSLOCK ]; + } else { + key = 0; + } + + return key; +} + +/****************************************************************************** + * + * Keyboard buffer + * + ****************************************************************************** + */ + +/** + * Insert keypress into keyboard buffer + * + * @v kbd USB keyboard + * @v keycode Keycode + * @v modifiers Modifiers + */ +static void usbkbd_produce ( struct usb_keyboard *kbd, unsigned int keycode, + unsigned int modifiers ) { + unsigned int key; + + /* Map to iPXE key */ + key = usbkbd_map ( keycode, modifiers ); + + /* Do nothing if this keycode has no corresponding iPXE key */ + if ( ! key ) { + DBGC ( kbd, "KBD %s has no key for keycode %#02x:%#02x\n", + kbd->name, modifiers, keycode ); + return; + } + + /* Check for buffer overrun */ + if ( usbkbd_fill ( kbd ) >= USBKBD_BUFSIZE ) { + DBGC ( kbd, "KBD %s buffer overrun (key %#02x)\n", + kbd->name, key ); + return; + } + + /* Insert into buffer */ + kbd->key[ ( kbd->prod++ ) % USBKBD_BUFSIZE ] = key; + DBGC2 ( kbd, "KBD %s key %#02x produced\n", kbd->name, key ); +} + +/** + * Consume character from keyboard buffer + * + * @v kbd USB keyboard + * @ret character Character + */ +static unsigned int usbkbd_consume ( struct usb_keyboard *kbd ) { + static char buf[] = "\x1b[xx~"; + char *tmp = &buf[2]; + unsigned int key; + unsigned int character; + unsigned int ansi_n; + unsigned int len; + + /* Sanity check */ + assert ( usbkbd_fill ( kbd ) > 0 ); + + /* Get current keypress */ + key = kbd->key[ kbd->cons % USBKBD_BUFSIZE ]; + + /* If this is a straightforward key, just consume and return it */ + if ( key < KEY_MIN ) { + kbd->cons++; + DBGC2 ( kbd, "KBD %s key %#02x consumed\n", kbd->name, key ); + return key; + } + + /* Construct ANSI sequence */ + ansi_n = KEY_ANSI_N ( key ); + if ( ansi_n ) + tmp += sprintf ( tmp, "%d", ansi_n ); + *(tmp++) = KEY_ANSI_TERMINATOR ( key ); + *tmp = '\0'; + len = ( tmp - buf ); + assert ( len < sizeof ( buf ) ); + if ( kbd->subcons == 0 ) { + DBGC2 ( kbd, "KBD %s key %#02x consumed as ^[%s\n", + kbd->name, key, &buf[1] ); + } + + /* Extract character from ANSI sequence */ + assert ( kbd->subcons < len ); + character = buf[ kbd->subcons++ ]; + + /* Consume key if applicable */ + if ( kbd->subcons == len ) { + kbd->cons++; + kbd->subcons = 0; + } + + return character; +} + +/****************************************************************************** + * + * Keyboard report + * + ****************************************************************************** + */ + +/** + * Check for presence of keycode in report + * + * @v report Keyboard report + * @v keycode Keycode (must be non-zero) + * @ret has_keycode Keycode is present in report + */ +static int usbkbd_has_keycode ( struct usb_keyboard_report *report, + unsigned int keycode ) { + unsigned int i; + + /* Check for keycode */ + for ( i = 0 ; i < ( sizeof ( report->keycode ) / + sizeof ( report->keycode[0] ) ) ; i++ ) { + if ( report->keycode[i] == keycode ) + return keycode; + } + + return 0; +} + +/** + * Handle keyboard report + * + * @v kbd USB keyboard + * @v new New keyboard report + */ +static void usbkbd_report ( struct usb_keyboard *kbd, + struct usb_keyboard_report *new ) { + struct usb_keyboard_report *old = &kbd->report; + unsigned int keycode; + unsigned int i; + + /* Check if current key has been released */ + if ( kbd->keycode && ! usbkbd_has_keycode ( new, kbd->keycode ) ) { + DBGC2 ( kbd, "KBD %s keycode %#02x released\n", + kbd->name, kbd->keycode ); + kbd->keycode = 0; + } + + /* Decrement auto-repeat hold-off timer, if applicable */ + if ( kbd->holdoff ) + kbd->holdoff--; + + /* Check if a new key has been pressed */ + for ( i = 0 ; i < ( sizeof ( new->keycode ) / + sizeof ( new->keycode[0] ) ) ; i++ ) { + + /* Ignore keys present in the previous report */ + keycode = new->keycode[i]; + if ( ( keycode == 0 ) || usbkbd_has_keycode ( old, keycode ) ) + continue; + DBGC2 ( kbd, "KBD %s keycode %#02x pressed\n", + kbd->name, keycode ); + + /* Insert keypress into keyboard buffer */ + usbkbd_produce ( kbd, keycode, new->modifiers ); + + /* Record as most recent keycode */ + kbd->keycode = keycode; + + /* Start auto-repeat hold-off timer */ + kbd->holdoff = USBKBD_HOLDOFF; + } + + /* Insert auto-repeated keypress into keyboard buffer, if applicable */ + if ( kbd->keycode && ! kbd->holdoff ) + usbkbd_produce ( kbd, kbd->keycode, new->modifiers ); + + /* Record report */ + memcpy ( old, new, sizeof ( *old ) ); +} + +/****************************************************************************** + * + * Interrupt endpoint + * + ****************************************************************************** + */ + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void usbkbd_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct usb_keyboard *kbd = container_of ( ep, struct usb_keyboard, + hid.in ); + struct usb_keyboard_report *report; + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto drop; + + /* Ignore packets with errors */ + if ( rc != 0 ) { + DBGC ( kbd, "KBD %s interrupt IN failed: %s\n", + kbd->name, strerror ( rc ) ); + goto drop; + } + + /* Ignore underlength packets */ + if ( iob_len ( iobuf ) < sizeof ( *report ) ) { + DBGC ( kbd, "KBD %s underlength report:\n", kbd->name ); + DBGC_HDA ( kbd, 0, iobuf->data, iob_len ( iobuf ) ); + goto drop; + } + report = iobuf->data; + + /* Handle keyboard report */ + usbkbd_report ( kbd, report ); + + drop: + /* Recycle I/O buffer */ + usb_recycle ( &kbd->hid.in, iobuf ); +} + +/** Interrupt endpoint operations */ +static struct usb_endpoint_driver_operations usbkbd_operations = { + .complete = usbkbd_complete, +}; + +/****************************************************************************** + * + * USB interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int usbkbd_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct usb_keyboard *kbd; + int rc; + + /* Allocate and initialise structure */ + kbd = zalloc ( sizeof ( *kbd ) ); + if ( ! kbd ) { + rc = -ENOMEM; + goto err_alloc; + } + kbd->name = func->name; + kbd->bus = usb->port->hub->bus; + usbhid_init ( &kbd->hid, func, &usbkbd_operations, NULL ); + usb_refill_init ( &kbd->hid.in, sizeof ( kbd->report ), + USBKBD_INTR_MAX_FILL ); + + /* Describe USB human interface device */ + if ( ( rc = usbhid_describe ( &kbd->hid, config ) ) != 0 ) { + DBGC ( kbd, "KBD %s could not describe: %s\n", + kbd->name, strerror ( rc ) ); + goto err_describe; + } + DBGC ( kbd, "KBD %s using %s (len %zd)\n", + kbd->name, usb_endpoint_name ( &kbd->hid.in ), kbd->hid.in.mtu ); + + /* Set boot protocol */ + if ( ( rc = usbhid_set_protocol ( usb, func->interface[0], + USBHID_PROTOCOL_BOOT ) ) != 0 ) { + DBGC ( kbd, "KBD %s could not set boot protocol: %s\n", + kbd->name, strerror ( rc ) ); + goto err_set_protocol; + } + + /* Set idle time */ + if ( ( rc = usbhid_set_idle ( usb, func->interface[0], 0, + USBKBD_IDLE_DURATION ) ) != 0 ) { + DBGC ( kbd, "KBD %s could not set idle time: %s\n", + kbd->name, strerror ( rc ) ); + goto err_set_idle; + } + + /* Open USB human interface device */ + if ( ( rc = usbhid_open ( &kbd->hid ) ) != 0 ) { + DBGC ( kbd, "KBD %s could not open: %s\n", + kbd->name, strerror ( rc ) ); + goto err_open; + } + + /* Add to list of USB keyboards */ + list_add_tail ( &kbd->list, &usb_keyboards ); + + usb_func_set_drvdata ( func, kbd ); + return 0; + + usbhid_close ( &kbd->hid ); + err_open: + err_set_idle: + err_set_protocol: + err_describe: + free ( kbd ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v func USB function + */ +static void usbkbd_remove ( struct usb_function *func ) { + struct usb_keyboard *kbd = usb_func_get_drvdata ( func ); + + /* Remove from list of USB keyboards */ + list_del ( &kbd->list ); + + /* Close USB human interface device */ + usbhid_close ( &kbd->hid ); + + /* Free device */ + free ( kbd ); +} + +/** USB keyboard device IDs */ +static struct usb_device_id usbkbd_ids[] = { + { + .name = "kbd", + .vendor = USB_ANY_ID, + .product = USB_ANY_ID, + .class = { + .class = USB_CLASS_HID, + .subclass = USB_SUBCLASS_HID_BOOT, + .protocol = USBKBD_PROTOCOL, + }, + }, +}; + +/** USB keyboard driver */ +struct usb_driver usbkbd_driver __usb_driver = { + .ids = usbkbd_ids, + .id_count = ( sizeof ( usbkbd_ids ) / sizeof ( usbkbd_ids[0] ) ), + .probe = usbkbd_probe, + .remove = usbkbd_remove, +}; + +/****************************************************************************** + * + * Console interface + * + ****************************************************************************** + */ + +/** + * Read a character from the console + * + * @ret character Character read + */ +static int usbkbd_getchar ( void ) { + struct usb_keyboard *kbd; + + /* Consume first available key */ + list_for_each_entry ( kbd, &usb_keyboards, list ) { + if ( usbkbd_fill ( kbd ) ) + return usbkbd_consume ( kbd ); + } + + return 0; +} + +/** + * Check for available input + * + * @ret is_available Input is available + */ +static int usbkbd_iskey ( void ) { + struct usb_keyboard *kbd; + unsigned int fill; + + /* Poll all USB keyboards and refill endpoints */ + list_for_each_entry ( kbd, &usb_keyboards, list ) { + usb_poll ( kbd->bus ); + usb_refill ( &kbd->hid.in ); + } + + /* Check for a non-empty keyboard buffer */ + list_for_each_entry ( kbd, &usb_keyboards, list ) { + fill = usbkbd_fill ( kbd ); + if ( fill ) + return fill; + } + + return 0; +} + +/** USB keyboard console */ +struct console_driver usbkbd_console __console_driver = { + .getchar = usbkbd_getchar, + .iskey = usbkbd_iskey, +}; diff --git a/qemu/roms/ipxe/src/drivers/usb/usbkbd.h b/qemu/roms/ipxe/src/drivers/usb/usbkbd.h new file mode 100644 index 000000000..7eab24e46 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/usb/usbkbd.h @@ -0,0 +1,154 @@ +#ifndef _USBKBD_H +#define _USBKBD_H + +/** @file + * + * USB keyboard driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** Keyboard protocol */ +#define USBKBD_PROTOCOL 1 + +/** A USB keyboard report */ +struct usb_keyboard_report { + /** Modifier keys */ + uint8_t modifiers; + /** Reserved */ + uint8_t reserved; + /** Keycodes */ + uint8_t keycode[6]; +} __attribute__ (( packed )); + +/** USB modifier keys */ +enum usb_keyboard_modifier { + /** Left Ctrl key */ + USBKBD_CTRL_LEFT = 0x01, + /** Left Shift key */ + USBKBD_SHIFT_LEFT = 0x02, + /** Left Alt key */ + USBKBD_ALT_LEFT = 0x04, + /** Left GUI key */ + USBKBD_GUI_LEFT = 0x08, + /** Right Ctrl key */ + USBKBD_CTRL_RIGHT = 0x10, + /** Right Shift key */ + USBKBD_SHIFT_RIGHT = 0x20, + /** Right Alt key */ + USBKBD_ALT_RIGHT = 0x40, + /** Right GUI key */ + USBKBD_GUI_RIGHT = 0x80, +}; + +/** Either Ctrl key */ +#define USBKBD_CTRL ( USBKBD_CTRL_LEFT | USBKBD_CTRL_RIGHT ) + +/** Either Shift key */ +#define USBKBD_SHIFT ( USBKBD_SHIFT_LEFT | USBKBD_SHIFT_RIGHT ) + +/** Either Alt key */ +#define USBKBD_ALT ( USBKBD_ALT_LEFT | USBKBD_ALT_RIGHT ) + +/** Either GUI key */ +#define USBKBD_GUI ( USBKBD_GUI_LEFT | USBKBD_GUI_RIGHT ) + +/** USB keycodes */ +enum usb_keycode { + USBKBD_KEY_A = 0x04, + USBKBD_KEY_Z = 0x1d, + USBKBD_KEY_1 = 0x1e, + USBKBD_KEY_0 = 0x27, + USBKBD_KEY_ENTER = 0x28, + USBKBD_KEY_SPACE = 0x2c, + USBKBD_KEY_MINUS = 0x2d, + USBKBD_KEY_SLASH = 0x38, + USBKBD_KEY_CAPSLOCK = 0x39, + USBKBD_KEY_UP = 0x52, +}; + +/** Keyboard idle duration (in 4ms units) + * + * This is a policy decision. We choose to use an autorepeat rate of + * approximately 40ms. + */ +#define USBKBD_IDLE_DURATION 10 /* 10 x 4ms = 40ms */ + +/** Keyboard auto-repeat hold-off (in units of USBKBD_IDLE_DURATION) + * + * This is a policy decision. We choose to use an autorepeat delay of + * approximately 500ms. + */ +#define USBKBD_HOLDOFF 12 /* 12 x 40ms = 480ms */ + +/** Interrupt endpoint maximum fill level + * + * When idling, we are likely to poll the USB endpoint at only the + * 18.2Hz system timer tick rate. With a typical observed bInterval + * of 10ms (which will be rounded down to 8ms by the HCI drivers), + * this gives approximately 7 completions per poll. + */ +#define USBKBD_INTR_MAX_FILL 8 + +/** Keyboard buffer size + * + * Must be a power of two. + */ +#define USBKBD_BUFSIZE 8 + +/** A USB keyboard device */ +struct usb_keyboard { + /** Name */ + const char *name; + /** List of all USB keyboards */ + struct list_head list; + + /** USB bus */ + struct usb_bus *bus; + /** USB human interface device */ + struct usb_hid hid; + + /** Most recent keyboard report */ + struct usb_keyboard_report report; + /** Most recently pressed non-modifier key (if any) */ + unsigned int keycode; + /** Autorepeat hold-off time (in number of completions reported) */ + unsigned int holdoff; + + /** Keyboard buffer + * + * This stores iPXE key values. + */ + unsigned int key[USBKBD_BUFSIZE]; + /** Keyboard buffer producer counter */ + unsigned int prod; + /** Keyboard buffer consumer counter */ + unsigned int cons; + /** Keyboard buffer sub-consumer counter + * + * This represents the index within the ANSI escape sequence + * corresponding to an iPXE key value. + */ + unsigned int subcons; +}; + +/** + * Calculate keyboard buffer fill level + * + * @v kbd USB keyboard + * @ret fill Keyboard buffer fill level + */ +static inline __attribute__ (( always_inline )) unsigned int +usbkbd_fill ( struct usb_keyboard *kbd ) { + unsigned int fill = ( kbd->prod - kbd->cons ); + + assert ( fill <= USBKBD_BUFSIZE ); + return fill; +} + +#endif /* _USBKBD_H */ diff --git a/qemu/roms/ipxe/src/drivers/usb/usbnet.c b/qemu/roms/ipxe/src/drivers/usb/usbnet.c new file mode 100644 index 000000000..b92336d05 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/usb/usbnet.c @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** @file + * + * USB network devices + * + * USB network devices use a variety of packet formats and interface + * descriptors, but tend to have several features in common: + * + * - a single interrupt endpoint using the generic refill mechanism + * + * - a single bulk IN endpoint using the generic refill mechanism + * + * - a single bulk OUT endpoint + * + * - optional use of an alternate setting to enable the data interface + * + */ + +/** + * Open USB network device + * + * @v usbnet USB network device + * @ret rc Return status code + */ +int usbnet_open ( struct usbnet_device *usbnet ) { + struct usb_device *usb = usbnet->func->usb; + int rc; + + /* Open interrupt endpoint */ + if ( ( rc = usb_endpoint_open ( &usbnet->intr ) ) != 0 ) { + DBGC ( usbnet, "USBNET %s could not open interrupt: %s\n", + usbnet->func->name, strerror ( rc ) ); + goto err_open_intr; + } + + /* Refill interrupt endpoint */ + if ( ( rc = usb_refill ( &usbnet->intr ) ) != 0 ) { + DBGC ( usbnet, "USBNET %s could not refill interrupt: %s\n", + usbnet->func->name, strerror ( rc ) ); + goto err_refill_intr; + } + + /* Select alternate setting for data interface, if applicable */ + if ( usbnet->alternate && + ( ( rc = usb_set_interface ( usb, usbnet->data, + usbnet->alternate ) ) != 0 ) ) { + DBGC ( usbnet, "USBNET %s could not set alternate interface " + "%d: %s\n", usbnet->func->name, usbnet->alternate, + strerror ( rc ) ); + goto err_set_interface; + } + + /* Open bulk IN endpoint */ + if ( ( rc = usb_endpoint_open ( &usbnet->in ) ) != 0 ) { + DBGC ( usbnet, "USBNET %s could not open bulk IN: %s\n", + usbnet->func->name, strerror ( rc ) ); + goto err_open_in; + } + + /* Open bulk OUT endpoint */ + if ( ( rc = usb_endpoint_open ( &usbnet->out ) ) != 0 ) { + DBGC ( usbnet, "USBNET %s could not open bulk OUT: %s\n", + usbnet->func->name, strerror ( rc ) ); + goto err_open_out; + } + + /* Refill bulk IN endpoint */ + if ( ( rc = usb_refill ( &usbnet->in ) ) != 0 ) { + DBGC ( usbnet, "USBNET %s could not refill bulk IN: %s\n", + usbnet->func->name, strerror ( rc ) ); + goto err_refill_in; + } + + return 0; + + err_refill_in: + usb_endpoint_close ( &usbnet->out ); + err_open_out: + usb_endpoint_close ( &usbnet->in ); + err_open_in: + if ( usbnet->alternate ) + usb_set_interface ( usb, usbnet->data, 0 ); + err_set_interface: + err_refill_intr: + usb_endpoint_close ( &usbnet->intr ); + err_open_intr: + return rc; +} + +/** + * Close USB network device + * + * @v usbnet USB network device + */ +void usbnet_close ( struct usbnet_device *usbnet ) { + struct usb_device *usb = usbnet->func->usb; + + /* Close bulk OUT endpoint */ + usb_endpoint_close ( &usbnet->out ); + + /* Close bulk IN endpoint */ + usb_endpoint_close ( &usbnet->in ); + + /* Reset alternate setting for data interface, if applicable */ + if ( usbnet->alternate ) + usb_set_interface ( usb, usbnet->data, 0 ); + + /* Close interrupt endpoint */ + usb_endpoint_close ( &usbnet->intr ); +} + +/** + * Refill USB network device bulk IN and interrupt endpoints + * + * @v usbnet USB network device + * @ret rc Return status code + */ +int usbnet_refill ( struct usbnet_device *usbnet ) { + int rc; + + /* Refill bulk IN endpoint */ + if ( ( rc = usb_refill ( &usbnet->in ) ) != 0 ) + return rc; + + /* Refill interrupt endpoint */ + if ( ( rc = usb_refill ( &usbnet->intr ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Describe communications interface and interrupt endpoint + * + * @v usbnet USB network device + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int usbnet_comms_describe ( struct usbnet_device *usbnet, + struct usb_configuration_descriptor *config){ + struct usb_interface_descriptor *desc; + unsigned int comms; + unsigned int i; + int rc; + + /* Iterate over all available interfaces */ + for ( i = 0 ; i < usbnet->func->count ; i++ ) { + + /* Get interface number */ + comms = usbnet->func->interface[i]; + + /* Locate interface descriptor */ + desc = usb_interface_descriptor ( config, comms, 0 ); + if ( ! desc ) + continue; + + /* Describe interrupt endpoint */ + if ( ( rc = usb_endpoint_described ( &usbnet->intr, config, + desc, USB_INTERRUPT_IN, + 0 ) ) != 0 ) + continue; + + /* Record communications interface */ + usbnet->comms = comms; + DBGC ( usbnet, "USBNET %s found communications interface %d\n", + usbnet->func->name, comms ); + return 0; + } + + DBGC ( usbnet, "USBNET %s found no communications interface\n", + usbnet->func->name ); + return -ENOENT; +} + +/** + * Describe data interface and bulk endpoints + * + * @v usbnet USB network device + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int usbnet_data_describe ( struct usbnet_device *usbnet, + struct usb_configuration_descriptor *config ){ + struct usb_interface_descriptor *desc; + unsigned int data; + unsigned int alt; + unsigned int i; + int rc; + + /* Iterate over all available interfaces */ + for ( i = 0 ; i < usbnet->func->count ; i++ ) { + + /* Get interface number */ + data = usbnet->func->interface[i]; + + /* Iterate over all existent alternate settings */ + for ( alt = 0 ; ; alt++ ) { + + /* Locate interface descriptor */ + desc = usb_interface_descriptor ( config, data, alt ); + if ( ! desc ) + break; + + /* Describe bulk IN endpoint */ + if ( ( rc = usb_endpoint_described ( &usbnet->in, + config, desc, + USB_BULK_IN, + 0 ) ) != 0 ) + continue; + + /* Describe bulk OUT endpoint */ + if ( ( rc = usb_endpoint_described ( &usbnet->out, + config, desc, + USB_BULK_OUT, + 0 ) ) != 0 ) + continue; + + /* Record data interface and alternate setting */ + usbnet->data = data; + usbnet->alternate = alt; + DBGC ( usbnet, "USBNET %s found data interface %d", + usbnet->func->name, data ); + if ( alt ) + DBGC ( usbnet, " using alternate %d", alt ); + DBGC ( usbnet, "\n" ); + return 0; + } + } + + DBGC ( usbnet, "USBNET %s found no data interface\n", + usbnet->func->name ); + return -ENOENT; +} + +/** + * Describe USB network device interfaces + * + * @v usbnet USB network device + * @v config Configuration descriptor + * @ret rc Return status code + */ +int usbnet_describe ( struct usbnet_device *usbnet, + struct usb_configuration_descriptor *config ) { + int rc; + + /* Describe communications interface */ + if ( ( rc = usbnet_comms_describe ( usbnet, config ) ) != 0 ) + return rc; + + /* Describe data interface */ + if ( ( rc = usbnet_data_describe ( usbnet, config ) ) != 0 ) + return rc; + + return 0; +} diff --git a/qemu/roms/ipxe/src/drivers/usb/xhci.c b/qemu/roms/ipxe/src/drivers/usb/xhci.c new file mode 100644 index 000000000..49e67316b --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/usb/xhci.c @@ -0,0 +1,3321 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xhci.h" + +/** @file + * + * USB eXtensible Host Controller Interface (xHCI) driver + * + */ + +/** Message transfer profiler */ +static struct profiler xhci_message_profiler __profiler = + { .name = "xhci.message" }; + +/** Stream transfer profiler */ +static struct profiler xhci_stream_profiler __profiler = + { .name = "xhci.stream" }; + +/** Event ring profiler */ +static struct profiler xhci_event_profiler __profiler = + { .name = "xhci.event" }; + +/** Transfer event profiler */ +static struct profiler xhci_transfer_profiler __profiler = + { .name = "xhci.transfer" }; + +/* Disambiguate the various error causes */ +#define EIO_DATA \ + __einfo_error ( EINFO_EIO_DATA ) +#define EINFO_EIO_DATA \ + __einfo_uniqify ( EINFO_EIO, ( 2 - 0 ), \ + "Data buffer error" ) +#define EIO_BABBLE \ + __einfo_error ( EINFO_EIO_BABBLE ) +#define EINFO_EIO_BABBLE \ + __einfo_uniqify ( EINFO_EIO, ( 3 - 0 ), \ + "Babble detected" ) +#define EIO_USB \ + __einfo_error ( EINFO_EIO_USB ) +#define EINFO_EIO_USB \ + __einfo_uniqify ( EINFO_EIO, ( 4 - 0 ), \ + "USB transaction error" ) +#define EIO_TRB \ + __einfo_error ( EINFO_EIO_TRB ) +#define EINFO_EIO_TRB \ + __einfo_uniqify ( EINFO_EIO, ( 5 - 0 ), \ + "TRB error" ) +#define EIO_STALL \ + __einfo_error ( EINFO_EIO_STALL ) +#define EINFO_EIO_STALL \ + __einfo_uniqify ( EINFO_EIO, ( 6 - 0 ), \ + "Stall error" ) +#define EIO_RESOURCE \ + __einfo_error ( EINFO_EIO_RESOURCE ) +#define EINFO_EIO_RESOURCE \ + __einfo_uniqify ( EINFO_EIO, ( 7 - 0 ), \ + "Resource error" ) +#define EIO_BANDWIDTH \ + __einfo_error ( EINFO_EIO_BANDWIDTH ) +#define EINFO_EIO_BANDWIDTH \ + __einfo_uniqify ( EINFO_EIO, ( 8 - 0 ), \ + "Bandwidth error" ) +#define EIO_NO_SLOTS \ + __einfo_error ( EINFO_EIO_NO_SLOTS ) +#define EINFO_EIO_NO_SLOTS \ + __einfo_uniqify ( EINFO_EIO, ( 9 - 0 ), \ + "No slots available" ) +#define EIO_STREAM_TYPE \ + __einfo_error ( EINFO_EIO_STREAM_TYPE ) +#define EINFO_EIO_STREAM_TYPE \ + __einfo_uniqify ( EINFO_EIO, ( 10 - 0 ), \ + "Invalid stream type" ) +#define EIO_SLOT \ + __einfo_error ( EINFO_EIO_SLOT ) +#define EINFO_EIO_SLOT \ + __einfo_uniqify ( EINFO_EIO, ( 11 - 0 ), \ + "Slot not enabled" ) +#define EIO_ENDPOINT \ + __einfo_error ( EINFO_EIO_ENDPOINT ) +#define EINFO_EIO_ENDPOINT \ + __einfo_uniqify ( EINFO_EIO, ( 12 - 0 ), \ + "Endpoint not enabled" ) +#define EIO_SHORT \ + __einfo_error ( EINFO_EIO_SHORT ) +#define EINFO_EIO_SHORT \ + __einfo_uniqify ( EINFO_EIO, ( 13 - 0 ), \ + "Short packet" ) +#define EIO_UNDERRUN \ + __einfo_error ( EINFO_EIO_UNDERRUN ) +#define EINFO_EIO_UNDERRUN \ + __einfo_uniqify ( EINFO_EIO, ( 14 - 0 ), \ + "Ring underrun" ) +#define EIO_OVERRUN \ + __einfo_error ( EINFO_EIO_OVERRUN ) +#define EINFO_EIO_OVERRUN \ + __einfo_uniqify ( EINFO_EIO, ( 15 - 0 ), \ + "Ring overrun" ) +#define EIO_VF_RING_FULL \ + __einfo_error ( EINFO_EIO_VF_RING_FULL ) +#define EINFO_EIO_VF_RING_FULL \ + __einfo_uniqify ( EINFO_EIO, ( 16 - 0 ), \ + "Virtual function event ring full" ) +#define EIO_PARAMETER \ + __einfo_error ( EINFO_EIO_PARAMETER ) +#define EINFO_EIO_PARAMETER \ + __einfo_uniqify ( EINFO_EIO, ( 17 - 0 ), \ + "Parameter error" ) +#define EIO_BANDWIDTH_OVERRUN \ + __einfo_error ( EINFO_EIO_BANDWIDTH_OVERRUN ) +#define EINFO_EIO_BANDWIDTH_OVERRUN \ + __einfo_uniqify ( EINFO_EIO, ( 18 - 0 ), \ + "Bandwidth overrun" ) +#define EIO_CONTEXT \ + __einfo_error ( EINFO_EIO_CONTEXT ) +#define EINFO_EIO_CONTEXT \ + __einfo_uniqify ( EINFO_EIO, ( 19 - 0 ), \ + "Context state error" ) +#define EIO_NO_PING \ + __einfo_error ( EINFO_EIO_NO_PING ) +#define EINFO_EIO_NO_PING \ + __einfo_uniqify ( EINFO_EIO, ( 20 - 0 ), \ + "No ping response" ) +#define EIO_RING_FULL \ + __einfo_error ( EINFO_EIO_RING_FULL ) +#define EINFO_EIO_RING_FULL \ + __einfo_uniqify ( EINFO_EIO, ( 21 - 0 ), \ + "Event ring full" ) +#define EIO_INCOMPATIBLE \ + __einfo_error ( EINFO_EIO_INCOMPATIBLE ) +#define EINFO_EIO_INCOMPATIBLE \ + __einfo_uniqify ( EINFO_EIO, ( 22 - 0 ), \ + "Incompatible device" ) +#define EIO_MISSED \ + __einfo_error ( EINFO_EIO_MISSED ) +#define EINFO_EIO_MISSED \ + __einfo_uniqify ( EINFO_EIO, ( 23 - 0 ), \ + "Missed service error" ) +#define EIO_CMD_STOPPED \ + __einfo_error ( EINFO_EIO_CMD_STOPPED ) +#define EINFO_EIO_CMD_STOPPED \ + __einfo_uniqify ( EINFO_EIO, ( 24 - 0 ), \ + "Command ring stopped" ) +#define EIO_CMD_ABORTED \ + __einfo_error ( EINFO_EIO_CMD_ABORTED ) +#define EINFO_EIO_CMD_ABORTED \ + __einfo_uniqify ( EINFO_EIO, ( 25 - 0 ), \ + "Command aborted" ) +#define EIO_STOP \ + __einfo_error ( EINFO_EIO_STOP ) +#define EINFO_EIO_STOP \ + __einfo_uniqify ( EINFO_EIO, ( 26 - 0 ), \ + "Stopped" ) +#define EIO_STOP_LEN \ + __einfo_error ( EINFO_EIO_STOP_LEN ) +#define EINFO_EIO_STOP_LEN \ + __einfo_uniqify ( EINFO_EIO, ( 27 - 0 ), \ + "Stopped - length invalid" ) +#define EIO_STOP_SHORT \ + __einfo_error ( EINFO_EIO_STOP_SHORT ) +#define EINFO_EIO_STOP_SHORT \ + __einfo_uniqify ( EINFO_EIO, ( 28 - 0 ), \ + "Stopped - short packet" ) +#define EIO_LATENCY \ + __einfo_error ( EINFO_EIO_LATENCY ) +#define EINFO_EIO_LATENCY \ + __einfo_uniqify ( EINFO_EIO, ( 29 - 0 ), \ + "Maximum exit latency too large" ) +#define EIO_ISOCH \ + __einfo_error ( EINFO_EIO_ISOCH ) +#define EINFO_EIO_ISOCH \ + __einfo_uniqify ( EINFO_EIO, ( 31 - 0 ), \ + "Isochronous buffer overrun" ) +#define EPROTO_LOST \ + __einfo_error ( EINFO_EPROTO_LOST ) +#define EINFO_EPROTO_LOST \ + __einfo_uniqify ( EINFO_EPROTO, ( 32 - 32 ), \ + "Event lost" ) +#define EPROTO_UNDEFINED \ + __einfo_error ( EINFO_EPROTO_UNDEFINED ) +#define EINFO_EPROTO_UNDEFINED \ + __einfo_uniqify ( EINFO_EPROTO, ( 33 - 32 ), \ + "Undefined error" ) +#define EPROTO_STREAM_ID \ + __einfo_error ( EINFO_EPROTO_STREAM_ID ) +#define EINFO_EPROTO_STREAM_ID \ + __einfo_uniqify ( EINFO_EPROTO, ( 34 - 32 ), \ + "Invalid stream ID" ) +#define EPROTO_SECONDARY \ + __einfo_error ( EINFO_EPROTO_SECONDARY ) +#define EINFO_EPROTO_SECONDARY \ + __einfo_uniqify ( EINFO_EPROTO, ( 35 - 32 ), \ + "Secondary bandwidth error" ) +#define EPROTO_SPLIT \ + __einfo_error ( EINFO_EPROTO_SPLIT ) +#define EINFO_EPROTO_SPLIT \ + __einfo_uniqify ( EINFO_EPROTO, ( 36 - 32 ), \ + "Split transaction error" ) +#define ECODE(code) \ + ( ( (code) < 32 ) ? \ + EUNIQ ( EINFO_EIO, ( (code) & 31 ), EIO_DATA, EIO_BABBLE, \ + EIO_USB, EIO_TRB, EIO_STALL, EIO_RESOURCE, \ + EIO_BANDWIDTH, EIO_NO_SLOTS, EIO_STREAM_TYPE, \ + EIO_SLOT, EIO_ENDPOINT, EIO_SHORT, EIO_UNDERRUN, \ + EIO_OVERRUN, EIO_VF_RING_FULL, EIO_PARAMETER, \ + EIO_BANDWIDTH_OVERRUN, EIO_CONTEXT, EIO_NO_PING, \ + EIO_RING_FULL, EIO_INCOMPATIBLE, EIO_MISSED, \ + EIO_CMD_STOPPED, EIO_CMD_ABORTED, EIO_STOP, \ + EIO_STOP_LEN, EIO_STOP_SHORT, EIO_LATENCY, \ + EIO_ISOCH ) : \ + ( (code) < 64 ) ? \ + EUNIQ ( EINFO_EPROTO, ( (code) & 31 ), EPROTO_LOST, \ + EPROTO_UNDEFINED, EPROTO_STREAM_ID, \ + EPROTO_SECONDARY, EPROTO_SPLIT ) : \ + EFAULT ) + +/****************************************************************************** + * + * Register access + * + ****************************************************************************** + */ + +/** + * Initialise device + * + * @v xhci xHCI device + * @v regs MMIO registers + */ +static void xhci_init ( struct xhci_device *xhci, void *regs ) { + uint32_t hcsparams1; + uint32_t hcsparams2; + uint32_t hccparams1; + uint32_t pagesize; + size_t caplength; + size_t rtsoff; + size_t dboff; + + /* Locate capability, operational, runtime, and doorbell registers */ + xhci->cap = regs; + caplength = readb ( xhci->cap + XHCI_CAP_CAPLENGTH ); + rtsoff = readl ( xhci->cap + XHCI_CAP_RTSOFF ); + dboff = readl ( xhci->cap + XHCI_CAP_DBOFF ); + xhci->op = ( xhci->cap + caplength ); + xhci->run = ( xhci->cap + rtsoff ); + xhci->db = ( xhci->cap + dboff ); + DBGC2 ( xhci, "XHCI %s cap %08lx op %08lx run %08lx db %08lx\n", + xhci->name, virt_to_phys ( xhci->cap ), + virt_to_phys ( xhci->op ), virt_to_phys ( xhci->run ), + virt_to_phys ( xhci->db ) ); + + /* Read structural parameters 1 */ + hcsparams1 = readl ( xhci->cap + XHCI_CAP_HCSPARAMS1 ); + xhci->slots = XHCI_HCSPARAMS1_SLOTS ( hcsparams1 ); + xhci->intrs = XHCI_HCSPARAMS1_INTRS ( hcsparams1 ); + xhci->ports = XHCI_HCSPARAMS1_PORTS ( hcsparams1 ); + DBGC ( xhci, "XHCI %s has %d slots %d intrs %d ports\n", + xhci->name, xhci->slots, xhci->intrs, xhci->ports ); + + /* Read structural parameters 2 */ + hcsparams2 = readl ( xhci->cap + XHCI_CAP_HCSPARAMS2 ); + xhci->scratchpads = XHCI_HCSPARAMS2_SCRATCHPADS ( hcsparams2 ); + DBGC2 ( xhci, "XHCI %s needs %d scratchpads\n", + xhci->name, xhci->scratchpads ); + + /* Read capability parameters 1 */ + hccparams1 = readl ( xhci->cap + XHCI_CAP_HCCPARAMS1 ); + xhci->addr64 = XHCI_HCCPARAMS1_ADDR64 ( hccparams1 ); + xhci->csz_shift = XHCI_HCCPARAMS1_CSZ_SHIFT ( hccparams1 ); + xhci->xecp = XHCI_HCCPARAMS1_XECP ( hccparams1 ); + + /* Read page size */ + pagesize = readl ( xhci->op + XHCI_OP_PAGESIZE ); + xhci->pagesize = XHCI_PAGESIZE ( pagesize ); + assert ( xhci->pagesize != 0 ); + assert ( ( ( xhci->pagesize ) & ( xhci->pagesize - 1 ) ) == 0 ); + DBGC2 ( xhci, "XHCI %s page size %zd bytes\n", + xhci->name, xhci->pagesize ); +} + +/** + * Find extended capability + * + * @v xhci xHCI device + * @v id Capability ID + * @v offset Offset to previous extended capability instance, or zero + * @ret offset Offset to extended capability, or zero if not found + */ +static unsigned int xhci_extended_capability ( struct xhci_device *xhci, + unsigned int id, + unsigned int offset ) { + uint32_t xecp; + unsigned int next; + + /* Locate the extended capability */ + while ( 1 ) { + + /* Locate first or next capability as applicable */ + if ( offset ) { + xecp = readl ( xhci->cap + offset ); + next = XHCI_XECP_NEXT ( xecp ); + } else { + next = xhci->xecp; + } + if ( ! next ) + return 0; + offset += next; + + /* Check if this is the requested capability */ + xecp = readl ( xhci->cap + offset ); + if ( XHCI_XECP_ID ( xecp ) == id ) + return offset; + } +} + +/** + * Write potentially 64-bit register + * + * @v xhci xHCI device + * @v value Value + * @v reg Register address + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +xhci_writeq ( struct xhci_device *xhci, physaddr_t value, void *reg ) { + + /* If this is a 32-bit build, then this can never fail + * (allowing the compiler to optimise out the error path). + */ + if ( sizeof ( value ) <= sizeof ( uint32_t ) ) { + writel ( value, reg ); + writel ( 0, ( reg + sizeof ( uint32_t ) ) ); + return 0; + } + + /* If the device does not support 64-bit addresses and this + * address is outside the 32-bit address space, then fail. + */ + if ( ( value & ~0xffffffffULL ) && ! xhci->addr64 ) { + DBGC ( xhci, "XHCI %s cannot access address %lx\n", + xhci->name, value ); + return -ENOTSUP; + } + + /* If this is a 64-bit build, then writeq() is available */ + writeq ( value, reg ); + return 0; +} + +/** + * Calculate buffer alignment + * + * @v len Length + * @ret align Buffer alignment + * + * Determine alignment required for a buffer which must be aligned to + * at least XHCI_MIN_ALIGN and which must not cross a page boundary. + */ +static inline size_t xhci_align ( size_t len ) { + size_t align; + + /* Align to own length (rounded up to a power of two) */ + align = ( 1 << fls ( len - 1 ) ); + + /* Round up to XHCI_MIN_ALIGN if needed */ + if ( align < XHCI_MIN_ALIGN ) + align = XHCI_MIN_ALIGN; + + return align; +} + +/** + * Calculate device context offset + * + * @v xhci xHCI device + * @v ctx Context index + */ +static inline size_t xhci_device_context_offset ( struct xhci_device *xhci, + unsigned int ctx ) { + + return ( XHCI_DCI ( ctx ) << xhci->csz_shift ); +} + +/** + * Calculate input context offset + * + * @v xhci xHCI device + * @v ctx Context index + */ +static inline size_t xhci_input_context_offset ( struct xhci_device *xhci, + unsigned int ctx ) { + + return ( XHCI_ICI ( ctx ) << xhci->csz_shift ); +} + +/****************************************************************************** + * + * Diagnostics + * + ****************************************************************************** + */ + +/** + * Dump host controller registers + * + * @v xhci xHCI device + */ +static inline void xhci_dump ( struct xhci_device *xhci ) { + uint32_t usbcmd; + uint32_t usbsts; + uint32_t pagesize; + uint32_t dnctrl; + uint32_t config; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; + + /* Dump USBCMD */ + usbcmd = readl ( xhci->op + XHCI_OP_USBCMD ); + DBGC ( xhci, "XHCI %s USBCMD %08x%s%s\n", xhci->name, usbcmd, + ( ( usbcmd & XHCI_USBCMD_RUN ) ? " run" : "" ), + ( ( usbcmd & XHCI_USBCMD_HCRST ) ? " hcrst" : "" ) ); + + /* Dump USBSTS */ + usbsts = readl ( xhci->op + XHCI_OP_USBSTS ); + DBGC ( xhci, "XHCI %s USBSTS %08x%s\n", xhci->name, usbsts, + ( ( usbsts & XHCI_USBSTS_HCH ) ? " hch" : "" ) ); + + /* Dump PAGESIZE */ + pagesize = readl ( xhci->op + XHCI_OP_PAGESIZE ); + DBGC ( xhci, "XHCI %s PAGESIZE %08x\n", xhci->name, pagesize ); + + /* Dump DNCTRL */ + dnctrl = readl ( xhci->op + XHCI_OP_DNCTRL ); + DBGC ( xhci, "XHCI %s DNCTRL %08x\n", xhci->name, dnctrl ); + + /* Dump CONFIG */ + config = readl ( xhci->op + XHCI_OP_CONFIG ); + DBGC ( xhci, "XHCI %s CONFIG %08x\n", xhci->name, config ); +} + +/** + * Dump port registers + * + * @v xhci xHCI device + * @v port Port number + */ +static inline void xhci_dump_port ( struct xhci_device *xhci, + unsigned int port ) { + uint32_t portsc; + uint32_t portpmsc; + uint32_t portli; + uint32_t porthlpmc; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; + + /* Dump PORTSC */ + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port ) ); + DBGC ( xhci, "XHCI %s-%d PORTSC %08x%s%s%s%s psiv=%d\n", + xhci->name, port, portsc, + ( ( portsc & XHCI_PORTSC_CCS ) ? " ccs" : "" ), + ( ( portsc & XHCI_PORTSC_PED ) ? " ped" : "" ), + ( ( portsc & XHCI_PORTSC_PR ) ? " pr" : "" ), + ( ( portsc & XHCI_PORTSC_PP ) ? " pp" : "" ), + XHCI_PORTSC_PSIV ( portsc ) ); + + /* Dump PORTPMSC */ + portpmsc = readl ( xhci->op + XHCI_OP_PORTPMSC ( port ) ); + DBGC ( xhci, "XHCI %s-%d PORTPMSC %08x\n", xhci->name, port, portpmsc ); + + /* Dump PORTLI */ + portli = readl ( xhci->op + XHCI_OP_PORTLI ( port ) ); + DBGC ( xhci, "XHCI %s-%d PORTLI %08x\n", xhci->name, port, portli ); + + /* Dump PORTHLPMC */ + porthlpmc = readl ( xhci->op + XHCI_OP_PORTHLPMC ( port ) ); + DBGC ( xhci, "XHCI %s-%d PORTHLPMC %08x\n", + xhci->name, port, porthlpmc ); +} + +/****************************************************************************** + * + * USB legacy support + * + ****************************************************************************** + */ + +/** Prevent the release of ownership back to BIOS */ +static int xhci_legacy_prevent_release; + +/** + * Initialise USB legacy support + * + * @v xhci xHCI device + */ +static void xhci_legacy_init ( struct xhci_device *xhci ) { + unsigned int legacy; + uint8_t bios; + + /* Locate USB legacy support capability (if present) */ + legacy = xhci_extended_capability ( xhci, XHCI_XECP_ID_LEGACY, 0 ); + if ( ! legacy ) { + /* Not an error; capability may not be present */ + DBGC ( xhci, "XHCI %s has no USB legacy support capability\n", + xhci->name ); + return; + } + + /* Check if legacy USB support is enabled */ + bios = readb ( xhci->cap + legacy + XHCI_USBLEGSUP_BIOS ); + if ( ! ( bios & XHCI_USBLEGSUP_BIOS_OWNED ) ) { + /* Not an error; already owned by OS */ + DBGC ( xhci, "XHCI %s USB legacy support already disabled\n", + xhci->name ); + return; + } + + /* Record presence of USB legacy support capability */ + xhci->legacy = legacy; +} + +/** + * Claim ownership from BIOS + * + * @v xhci xHCI device + */ +static void xhci_legacy_claim ( struct xhci_device *xhci ) { + uint32_t ctlsts; + uint8_t bios; + unsigned int i; + + /* Do nothing unless legacy support capability is present */ + if ( ! xhci->legacy ) + return; + + /* Claim ownership */ + writeb ( XHCI_USBLEGSUP_OS_OWNED, + xhci->cap + xhci->legacy + XHCI_USBLEGSUP_OS ); + + /* Wait for BIOS to release ownership */ + for ( i = 0 ; i < XHCI_USBLEGSUP_MAX_WAIT_MS ; i++ ) { + + /* Check if BIOS has released ownership */ + bios = readb ( xhci->cap + xhci->legacy + XHCI_USBLEGSUP_BIOS ); + if ( ! ( bios & XHCI_USBLEGSUP_BIOS_OWNED ) ) { + DBGC ( xhci, "XHCI %s claimed ownership from BIOS\n", + xhci->name ); + ctlsts = readl ( xhci->cap + xhci->legacy + + XHCI_USBLEGSUP_CTLSTS ); + if ( ctlsts ) { + DBGC ( xhci, "XHCI %s warning: BIOS retained " + "SMIs: %08x\n", xhci->name, ctlsts ); + } + return; + } + + /* Delay */ + mdelay ( 1 ); + } + + /* BIOS did not release ownership. Claim it forcibly by + * disabling all SMIs. + */ + DBGC ( xhci, "XHCI %s could not claim ownership from BIOS: forcibly " + "disabling SMIs\n", xhci->name ); + writel ( 0, xhci->cap + xhci->legacy + XHCI_USBLEGSUP_CTLSTS ); +} + +/** + * Release ownership back to BIOS + * + * @v xhci xHCI device + */ +static void xhci_legacy_release ( struct xhci_device *xhci ) { + + /* Do nothing unless legacy support capability is present */ + if ( ! xhci->legacy ) + return; + + /* Do nothing if releasing ownership is prevented */ + if ( xhci_legacy_prevent_release ) { + DBGC ( xhci, "XHCI %s not releasing ownership to BIOS\n", + xhci->name ); + return; + } + + /* Release ownership */ + writeb ( 0, xhci->cap + xhci->legacy + XHCI_USBLEGSUP_OS ); + DBGC ( xhci, "XHCI %s released ownership to BIOS\n", xhci->name ); +} + +/****************************************************************************** + * + * Supported protocols + * + ****************************************************************************** + */ + +/** + * Transcribe port speed (for debugging) + * + * @v psi Protocol speed ID + * @ret speed Transcribed speed + */ +static inline const char * xhci_speed_name ( uint32_t psi ) { + static const char *exponents[4] = { "", "k", "M", "G" }; + static char buf[ 10 /* "xxxxxXbps" + NUL */ ]; + unsigned int mantissa; + unsigned int exponent; + + /* Extract mantissa and exponent */ + mantissa = XHCI_SUPPORTED_PSI_MANTISSA ( psi ); + exponent = XHCI_SUPPORTED_PSI_EXPONENT ( psi ); + + /* Transcribe speed */ + snprintf ( buf, sizeof ( buf ), "%d%sbps", + mantissa, exponents[exponent] ); + return buf; +} + +/** + * Find supported protocol extended capability for a port + * + * @v xhci xHCI device + * @v port Port number + * @ret supported Offset to extended capability, or zero if not found + */ +static unsigned int xhci_supported_protocol ( struct xhci_device *xhci, + unsigned int port ) { + unsigned int supported = 0; + unsigned int offset; + unsigned int count; + uint32_t ports; + + /* Iterate over all supported protocol structures */ + while ( ( supported = xhci_extended_capability ( xhci, + XHCI_XECP_ID_SUPPORTED, + supported ) ) ) { + + /* Determine port range */ + ports = readl ( xhci->cap + supported + XHCI_SUPPORTED_PORTS ); + offset = XHCI_SUPPORTED_PORTS_OFFSET ( ports ); + count = XHCI_SUPPORTED_PORTS_COUNT ( ports ); + + /* Check if port lies within this range */ + if ( ( port - offset ) < count ) + return supported; + } + + DBGC ( xhci, "XHCI %s-%d has no supported protocol\n", + xhci->name, port ); + return 0; +} + +/** + * Find port protocol + * + * @v xhci xHCI device + * @v port Port number + * @ret protocol USB protocol, or zero if not found + */ +static unsigned int xhci_port_protocol ( struct xhci_device *xhci, + unsigned int port ) { + unsigned int supported = xhci_supported_protocol ( xhci, port ); + union { + uint32_t raw; + char text[5]; + } name; + unsigned int protocol; + unsigned int type; + unsigned int psic; + unsigned int psiv; + unsigned int i; + uint32_t revision; + uint32_t ports; + uint32_t slot; + uint32_t psi; + + /* Fail if there is no supported protocol */ + if ( ! supported ) + return 0; + + /* Determine protocol version */ + revision = readl ( xhci->cap + supported + XHCI_SUPPORTED_REVISION ); + protocol = XHCI_SUPPORTED_REVISION_VER ( revision ); + + /* Describe port protocol */ + if ( DBG_EXTRA ) { + name.raw = cpu_to_le32 ( readl ( xhci->cap + supported + + XHCI_SUPPORTED_NAME ) ); + name.text[4] = '\0'; + slot = readl ( xhci->cap + supported + XHCI_SUPPORTED_SLOT ); + type = XHCI_SUPPORTED_SLOT_TYPE ( slot ); + DBGC2 ( xhci, "XHCI %s-%d %sv%04x type %d", + xhci->name, port, name.text, protocol, type ); + ports = readl ( xhci->cap + supported + XHCI_SUPPORTED_PORTS ); + psic = XHCI_SUPPORTED_PORTS_PSIC ( ports ); + if ( psic ) { + DBGC2 ( xhci, " speeds" ); + for ( i = 0 ; i < psic ; i++ ) { + psi = readl ( xhci->cap + supported + + XHCI_SUPPORTED_PSI ( i ) ); + psiv = XHCI_SUPPORTED_PSI_VALUE ( psi ); + DBGC2 ( xhci, " %d:%s", psiv, + xhci_speed_name ( psi ) ); + } + } + if ( xhci->quirks & XHCI_BAD_PSIV ) + DBGC2 ( xhci, " (ignored)" ); + DBGC2 ( xhci, "\n" ); + } + + return protocol; +} + +/** + * Find port slot type + * + * @v xhci xHCI device + * @v port Port number + * @ret type Slot type, or negative error + */ +static int xhci_port_slot_type ( struct xhci_device *xhci, unsigned int port ) { + unsigned int supported = xhci_supported_protocol ( xhci, port ); + unsigned int type; + uint32_t slot; + + /* Fail if there is no supported protocol */ + if ( ! supported ) + return -ENOTSUP; + + /* Get slot type */ + slot = readl ( xhci->cap + supported + XHCI_SUPPORTED_SLOT ); + type = XHCI_SUPPORTED_SLOT_TYPE ( slot ); + + return type; +} + +/** + * Find port speed + * + * @v xhci xHCI device + * @v port Port number + * @v psiv Protocol speed ID value + * @ret speed Port speed, or negative error + */ +static int xhci_port_speed ( struct xhci_device *xhci, unsigned int port, + unsigned int psiv ) { + unsigned int supported = xhci_supported_protocol ( xhci, port ); + unsigned int psic; + unsigned int mantissa; + unsigned int exponent; + unsigned int speed; + unsigned int i; + uint32_t ports; + uint32_t psi; + + /* Fail if there is no supported protocol */ + if ( ! supported ) + return -ENOTSUP; + + /* Get protocol speed ID count */ + ports = readl ( xhci->cap + supported + XHCI_SUPPORTED_PORTS ); + psic = XHCI_SUPPORTED_PORTS_PSIC ( ports ); + + /* Use the default mappings if applicable */ + if ( ( psic == 0 ) || ( xhci->quirks & XHCI_BAD_PSIV ) ) { + switch ( psiv ) { + case XHCI_SPEED_LOW : return USB_SPEED_LOW; + case XHCI_SPEED_FULL : return USB_SPEED_FULL; + case XHCI_SPEED_HIGH : return USB_SPEED_HIGH; + case XHCI_SPEED_SUPER : return USB_SPEED_SUPER; + default: + DBGC ( xhci, "XHCI %s-%d non-standard PSI value %d\n", + xhci->name, port, psiv ); + return -ENOTSUP; + } + } + + /* Iterate over PSI dwords looking for a match */ + for ( i = 0 ; i < psic ; i++ ) { + psi = readl ( xhci->cap + supported + XHCI_SUPPORTED_PSI ( i )); + if ( psiv == XHCI_SUPPORTED_PSI_VALUE ( psi ) ) { + mantissa = XHCI_SUPPORTED_PSI_MANTISSA ( psi ); + exponent = XHCI_SUPPORTED_PSI_EXPONENT ( psi ); + speed = USB_SPEED ( mantissa, exponent ); + return speed; + } + } + + DBGC ( xhci, "XHCI %s-%d spurious PSI value %d\n", + xhci->name, port, psiv ); + return -ENOENT; +} + +/** + * Find protocol speed ID value + * + * @v xhci xHCI device + * @v port Port number + * @v speed USB speed + * @ret psiv Protocol speed ID value, or negative error + */ +static int xhci_port_psiv ( struct xhci_device *xhci, unsigned int port, + unsigned int speed ) { + unsigned int supported = xhci_supported_protocol ( xhci, port ); + unsigned int psic; + unsigned int mantissa; + unsigned int exponent; + unsigned int psiv; + unsigned int i; + uint32_t ports; + uint32_t psi; + + /* Fail if there is no supported protocol */ + if ( ! supported ) + return -ENOTSUP; + + /* Get protocol speed ID count */ + ports = readl ( xhci->cap + supported + XHCI_SUPPORTED_PORTS ); + psic = XHCI_SUPPORTED_PORTS_PSIC ( ports ); + + /* Use the default mappings if applicable */ + if ( ( psic == 0 ) || ( xhci->quirks & XHCI_BAD_PSIV ) ) { + switch ( speed ) { + case USB_SPEED_LOW : return XHCI_SPEED_LOW; + case USB_SPEED_FULL : return XHCI_SPEED_FULL; + case USB_SPEED_HIGH : return XHCI_SPEED_HIGH; + case USB_SPEED_SUPER : return XHCI_SPEED_SUPER; + default: + DBGC ( xhci, "XHCI %s-%d non-standard speed %d\n", + xhci->name, port, speed ); + return -ENOTSUP; + } + } + + /* Iterate over PSI dwords looking for a match */ + for ( i = 0 ; i < psic ; i++ ) { + psi = readl ( xhci->cap + supported + XHCI_SUPPORTED_PSI ( i )); + mantissa = XHCI_SUPPORTED_PSI_MANTISSA ( psi ); + exponent = XHCI_SUPPORTED_PSI_EXPONENT ( psi ); + if ( speed == USB_SPEED ( mantissa, exponent ) ) { + psiv = XHCI_SUPPORTED_PSI_VALUE ( psi ); + return psiv; + } + } + + DBGC ( xhci, "XHCI %s-%d unrepresentable speed %#x\n", + xhci->name, port, speed ); + return -ENOENT; +} + +/****************************************************************************** + * + * Device context base address array + * + ****************************************************************************** + */ + +/** + * Allocate device context base address array + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static int xhci_dcbaa_alloc ( struct xhci_device *xhci ) { + size_t len; + physaddr_t dcbaap; + int rc; + + /* Allocate and initialise structure. Must be at least + * 64-byte aligned and must not cross a page boundary, so + * align on its own size (rounded up to a power of two and + * with a minimum of 64 bytes). + */ + len = ( ( xhci->slots + 1 ) * sizeof ( xhci->dcbaa[0] ) ); + xhci->dcbaa = malloc_dma ( len, xhci_align ( len ) ); + if ( ! xhci->dcbaa ) { + DBGC ( xhci, "XHCI %s could not allocate DCBAA\n", xhci->name ); + rc = -ENOMEM; + goto err_alloc; + } + memset ( xhci->dcbaa, 0, len ); + + /* Program DCBAA pointer */ + dcbaap = virt_to_phys ( xhci->dcbaa ); + if ( ( rc = xhci_writeq ( xhci, dcbaap, + xhci->op + XHCI_OP_DCBAAP ) ) != 0 ) + goto err_writeq; + + DBGC2 ( xhci, "XHCI %s DCBAA at [%08lx,%08lx)\n", + xhci->name, dcbaap, ( dcbaap + len ) ); + return 0; + + err_writeq: + free_dma ( xhci->dcbaa, len ); + err_alloc: + return rc; +} + +/** + * Free device context base address array + * + * @v xhci xHCI device + */ +static void xhci_dcbaa_free ( struct xhci_device *xhci ) { + size_t len; + unsigned int i; + + /* Sanity check */ + for ( i = 0 ; i <= xhci->slots ; i++ ) + assert ( xhci->dcbaa[i] == 0 ); + + /* Clear DCBAA pointer */ + xhci_writeq ( xhci, 0, xhci->op + XHCI_OP_DCBAAP ); + + /* Free DCBAA */ + len = ( ( xhci->slots + 1 ) * sizeof ( xhci->dcbaa[0] ) ); + free_dma ( xhci->dcbaa, len ); +} + +/****************************************************************************** + * + * Scratchpad buffers + * + ****************************************************************************** + */ + +/** + * Allocate scratchpad buffers + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static int xhci_scratchpad_alloc ( struct xhci_device *xhci ) { + size_t array_len; + size_t len; + physaddr_t phys; + unsigned int i; + int rc; + + /* Do nothing if no scratchpad buffers are used */ + if ( ! xhci->scratchpads ) + return 0; + + /* Allocate scratchpads */ + len = ( xhci->scratchpads * xhci->pagesize ); + xhci->scratchpad = umalloc ( len ); + if ( ! xhci->scratchpad ) { + DBGC ( xhci, "XHCI %s could not allocate scratchpad buffers\n", + xhci->name ); + rc = -ENOMEM; + goto err_alloc; + } + memset_user ( xhci->scratchpad, 0, 0, len ); + + /* Allocate scratchpad array */ + array_len = ( xhci->scratchpads * sizeof ( xhci->scratchpad_array[0] )); + xhci->scratchpad_array = + malloc_dma ( array_len, xhci_align ( array_len ) ); + if ( ! xhci->scratchpad_array ) { + DBGC ( xhci, "XHCI %s could not allocate scratchpad buffer " + "array\n", xhci->name ); + rc = -ENOMEM; + goto err_alloc_array; + } + + /* Populate scratchpad array */ + for ( i = 0 ; i < xhci->scratchpads ; i++ ) { + phys = user_to_phys ( xhci->scratchpad, ( i * xhci->pagesize )); + xhci->scratchpad_array[i] = phys; + } + + /* Set scratchpad array pointer */ + assert ( xhci->dcbaa != NULL ); + xhci->dcbaa[0] = cpu_to_le64 ( virt_to_phys ( xhci->scratchpad_array )); + + DBGC2 ( xhci, "XHCI %s scratchpad [%08lx,%08lx) array [%08lx,%08lx)\n", + xhci->name, user_to_phys ( xhci->scratchpad, 0 ), + user_to_phys ( xhci->scratchpad, len ), + virt_to_phys ( xhci->scratchpad_array ), + ( virt_to_phys ( xhci->scratchpad_array ) + array_len ) ); + return 0; + + free_dma ( xhci->scratchpad_array, array_len ); + err_alloc_array: + ufree ( xhci->scratchpad ); + err_alloc: + return rc; +} + +/** + * Free scratchpad buffers + * + * @v xhci xHCI device + */ +static void xhci_scratchpad_free ( struct xhci_device *xhci ) { + size_t array_len; + + /* Do nothing if no scratchpad buffers are used */ + if ( ! xhci->scratchpads ) + return; + + /* Clear scratchpad array pointer */ + assert ( xhci->dcbaa != NULL ); + xhci->dcbaa[0] = 0; + + /* Free scratchpad array */ + array_len = ( xhci->scratchpads * sizeof ( xhci->scratchpad_array[0] )); + free_dma ( xhci->scratchpad_array, array_len ); + + /* Free scratchpads */ + ufree ( xhci->scratchpad ); +} + +/****************************************************************************** + * + * Run / stop / reset + * + ****************************************************************************** + */ + +/** + * Start xHCI device + * + * @v xhci xHCI device + */ +static void xhci_run ( struct xhci_device *xhci ) { + uint32_t config; + uint32_t usbcmd; + + /* Configure number of device slots */ + config = readl ( xhci->op + XHCI_OP_CONFIG ); + config &= ~XHCI_CONFIG_MAX_SLOTS_EN_MASK; + config |= XHCI_CONFIG_MAX_SLOTS_EN ( xhci->slots ); + writel ( config, xhci->op + XHCI_OP_CONFIG ); + + /* Set run/stop bit */ + usbcmd = readl ( xhci->op + XHCI_OP_USBCMD ); + usbcmd |= XHCI_USBCMD_RUN; + writel ( usbcmd, xhci->op + XHCI_OP_USBCMD ); +} + +/** + * Stop xHCI device + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static int xhci_stop ( struct xhci_device *xhci ) { + uint32_t usbcmd; + uint32_t usbsts; + unsigned int i; + + /* Clear run/stop bit */ + usbcmd = readl ( xhci->op + XHCI_OP_USBCMD ); + usbcmd &= ~XHCI_USBCMD_RUN; + writel ( usbcmd, xhci->op + XHCI_OP_USBCMD ); + + /* Wait for device to stop */ + for ( i = 0 ; i < XHCI_STOP_MAX_WAIT_MS ; i++ ) { + + /* Check if device is stopped */ + usbsts = readl ( xhci->op + XHCI_OP_USBSTS ); + if ( usbsts & XHCI_USBSTS_HCH ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( xhci, "XHCI %s timed out waiting for stop\n", xhci->name ); + return -ETIMEDOUT; +} + +/** + * Reset xHCI device + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static int xhci_reset ( struct xhci_device *xhci ) { + uint32_t usbcmd; + unsigned int i; + int rc; + + /* The xHCI specification states that resetting a running + * device may result in undefined behaviour, so try stopping + * it first. + */ + if ( ( rc = xhci_stop ( xhci ) ) != 0 ) { + /* Ignore errors and attempt to reset the device anyway */ + } + + /* Reset device */ + writel ( XHCI_USBCMD_HCRST, xhci->op + XHCI_OP_USBCMD ); + + /* Wait for reset to complete */ + for ( i = 0 ; i < XHCI_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check if reset is complete */ + usbcmd = readl ( xhci->op + XHCI_OP_USBCMD ); + if ( ! ( usbcmd & XHCI_USBCMD_HCRST ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( xhci, "XHCI %s timed out waiting for reset\n", xhci->name ); + return -ETIMEDOUT; +} + +/****************************************************************************** + * + * Transfer request blocks + * + ****************************************************************************** + */ + +/** + * Allocate transfer request block ring + * + * @v xhci xHCI device + * @v ring TRB ring + * @v shift Ring size (log2) + * @v slot Device slot + * @v target Doorbell target + * @v stream Doorbell stream ID + * @ret rc Return status code + */ +static int xhci_ring_alloc ( struct xhci_device *xhci, + struct xhci_trb_ring *ring, + unsigned int shift, unsigned int slot, + unsigned int target, unsigned int stream ) { + struct xhci_trb_link *link; + unsigned int count; + int rc; + + /* Sanity check */ + assert ( shift > 0 ); + + /* Initialise structure */ + memset ( ring, 0, sizeof ( *ring ) ); + ring->shift = shift; + count = ( 1U << shift ); + ring->mask = ( count - 1 ); + ring->len = ( ( count + 1 /* Link TRB */ ) * sizeof ( ring->trb[0] ) ); + ring->db = ( xhci->db + ( slot * sizeof ( ring->dbval ) ) ); + ring->dbval = XHCI_DBVAL ( target, stream ); + + /* Allocate I/O buffers */ + ring->iobuf = zalloc ( count * sizeof ( ring->iobuf[0] ) ); + if ( ! ring->iobuf ) { + rc = -ENOMEM; + goto err_alloc_iobuf; + } + + /* Allocate TRBs */ + ring->trb = malloc_dma ( ring->len, xhci_align ( ring->len ) ); + if ( ! ring->trb ) { + rc = -ENOMEM; + goto err_alloc_trb; + } + memset ( ring->trb, 0, ring->len ); + + /* Initialise Link TRB */ + link = &ring->trb[count].link; + link->next = cpu_to_le64 ( virt_to_phys ( ring->trb ) ); + link->flags = XHCI_TRB_TC; + link->type = XHCI_TRB_LINK; + ring->link = link; + + return 0; + + free_dma ( ring->trb, ring->len ); + err_alloc_trb: + free ( ring->iobuf ); + err_alloc_iobuf: + return rc; +} + +/** + * Reset transfer request block ring + * + * @v ring TRB ring + */ +static void xhci_ring_reset ( struct xhci_trb_ring *ring ) { + unsigned int count = ( 1U << ring->shift ); + + /* Reset producer and consumer counters */ + ring->prod = 0; + ring->cons = 0; + + /* Reset TRBs (except Link TRB) */ + memset ( ring->trb, 0, ( count * sizeof ( ring->trb[0] ) ) ); +} + +/** + * Free transfer request block ring + * + * @v ring TRB ring + */ +static void xhci_ring_free ( struct xhci_trb_ring *ring ) { + unsigned int count = ( 1U << ring->shift ); + unsigned int i; + + /* Sanity checks */ + assert ( ring->cons == ring->prod ); + for ( i = 0 ; i < count ; i++ ) + assert ( ring->iobuf[i] == NULL ); + + /* Free TRBs */ + free_dma ( ring->trb, ring->len ); + + /* Free I/O buffers */ + free ( ring->iobuf ); +} + +/** + * Enqueue a transfer request block + * + * @v ring TRB ring + * @v iobuf I/O buffer (if any) + * @v trb Transfer request block (with empty Cycle flag) + * @ret rc Return status code + * + * This operation does not implicitly ring the doorbell register. + */ +static int xhci_enqueue ( struct xhci_trb_ring *ring, struct io_buffer *iobuf, + const union xhci_trb *trb ) { + union xhci_trb *dest; + unsigned int prod; + unsigned int mask; + unsigned int index; + unsigned int cycle; + + /* Sanity check */ + assert ( ! ( trb->common.flags & XHCI_TRB_C ) ); + + /* Fail if ring is full */ + if ( ! xhci_ring_remaining ( ring ) ) + return -ENOBUFS; + + /* Update producer counter (and link TRB, if applicable) */ + prod = ring->prod++; + mask = ring->mask; + cycle = ( ( ~( prod >> ring->shift ) ) & XHCI_TRB_C ); + index = ( prod & mask ); + if ( index == 0 ) + ring->link->flags = ( XHCI_TRB_TC | ( cycle ^ XHCI_TRB_C ) ); + + /* Record I/O buffer */ + ring->iobuf[index] = iobuf; + + /* Enqueue TRB */ + dest = &ring->trb[index]; + dest->template.parameter = trb->template.parameter; + dest->template.status = trb->template.status; + wmb(); + dest->template.control = ( trb->template.control | + cpu_to_le32 ( cycle ) ); + + return 0; +} + +/** + * Dequeue a transfer request block + * + * @v ring TRB ring + * @ret iobuf I/O buffer + */ +static struct io_buffer * xhci_dequeue ( struct xhci_trb_ring *ring ) { + struct io_buffer *iobuf; + unsigned int cons; + unsigned int mask; + unsigned int index; + + /* Sanity check */ + assert ( xhci_ring_fill ( ring ) != 0 ); + + /* Update consumer counter */ + cons = ring->cons++; + mask = ring->mask; + index = ( cons & mask ); + + /* Retrieve I/O buffer */ + iobuf = ring->iobuf[index]; + ring->iobuf[index] = NULL; + + return iobuf; +} + +/** + * Enqueue multiple transfer request blocks + * + * @v ring TRB ring + * @v iobuf I/O buffer + * @v trbs Transfer request blocks (with empty Cycle flag) + * @v count Number of transfer request blocks + * @ret rc Return status code + * + * This operation does not implicitly ring the doorbell register. + */ +static int xhci_enqueue_multi ( struct xhci_trb_ring *ring, + struct io_buffer *iobuf, + const union xhci_trb *trbs, + unsigned int count ) { + const union xhci_trb *trb = trbs; + int rc; + + /* Sanity check */ + assert ( iobuf != NULL ); + + /* Fail if ring does not have sufficient space */ + if ( xhci_ring_remaining ( ring ) < count ) + return -ENOBUFS; + + /* Enqueue each TRB, recording the I/O buffer with the final TRB */ + while ( count-- ) { + rc = xhci_enqueue ( ring, ( count ? NULL : iobuf ), trb++ ); + assert ( rc == 0 ); /* Should never be able to fail */ + } + + return 0; +} + +/** + * Dequeue multiple transfer request blocks + * + * @v ring TRB ring + * @ret iobuf I/O buffer + */ +static struct io_buffer * xhci_dequeue_multi ( struct xhci_trb_ring *ring ) { + struct io_buffer *iobuf; + + /* Dequeue TRBs until we reach the final TRB for an I/O buffer */ + do { + iobuf = xhci_dequeue ( ring ); + } while ( iobuf == NULL ); + + return iobuf; +} + +/** + * Ring doorbell register + * + * @v ring TRB ring + */ +static inline __attribute__ (( always_inline )) void +xhci_doorbell ( struct xhci_trb_ring *ring ) { + + wmb(); + writel ( ring->dbval, ring->db ); +} + +/****************************************************************************** + * + * Command and event rings + * + ****************************************************************************** + */ + +/** + * Allocate command ring + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static int xhci_command_alloc ( struct xhci_device *xhci ) { + physaddr_t crp; + int rc; + + /* Allocate TRB ring */ + if ( ( rc = xhci_ring_alloc ( xhci, &xhci->command, XHCI_CMD_TRBS_LOG2, + 0, 0, 0 ) ) != 0 ) + goto err_ring_alloc; + + /* Program command ring control register */ + crp = virt_to_phys ( xhci->command.trb ); + if ( ( rc = xhci_writeq ( xhci, ( crp | XHCI_CRCR_RCS ), + xhci->op + XHCI_OP_CRCR ) ) != 0 ) + goto err_writeq; + + DBGC2 ( xhci, "XHCI %s CRCR at [%08lx,%08lx)\n", + xhci->name, crp, ( crp + xhci->command.len ) ); + return 0; + + err_writeq: + xhci_ring_free ( &xhci->command ); + err_ring_alloc: + return rc; +} + +/** + * Free command ring + * + * @v xhci xHCI device + */ +static void xhci_command_free ( struct xhci_device *xhci ) { + + /* Sanity check */ + assert ( ( readl ( xhci->op + XHCI_OP_CRCR ) & XHCI_CRCR_CRR ) == 0 ); + + /* Clear command ring control register */ + xhci_writeq ( xhci, 0, xhci->op + XHCI_OP_CRCR ); + + /* Free TRB ring */ + xhci_ring_free ( &xhci->command ); +} + +/** + * Allocate event ring + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static int xhci_event_alloc ( struct xhci_device *xhci ) { + struct xhci_event_ring *event = &xhci->event; + unsigned int count; + size_t len; + int rc; + + /* Allocate event ring */ + count = ( 1 << XHCI_EVENT_TRBS_LOG2 ); + len = ( count * sizeof ( event->trb[0] ) ); + event->trb = malloc_dma ( len, xhci_align ( len ) ); + if ( ! event->trb ) { + rc = -ENOMEM; + goto err_alloc_trb; + } + memset ( event->trb, 0, len ); + + /* Allocate event ring segment table */ + event->segment = malloc_dma ( sizeof ( event->segment[0] ), + xhci_align ( sizeof (event->segment[0]))); + if ( ! event->segment ) { + rc = -ENOMEM; + goto err_alloc_segment; + } + memset ( event->segment, 0, sizeof ( event->segment[0] ) ); + event->segment[0].base = cpu_to_le64 ( virt_to_phys ( event->trb ) ); + event->segment[0].count = cpu_to_le32 ( count ); + + /* Program event ring registers */ + writel ( 1, xhci->run + XHCI_RUN_ERSTSZ ( 0 ) ); + if ( ( rc = xhci_writeq ( xhci, virt_to_phys ( event->trb ), + xhci->run + XHCI_RUN_ERDP ( 0 ) ) ) != 0 ) + goto err_writeq_erdp; + if ( ( rc = xhci_writeq ( xhci, virt_to_phys ( event->segment ), + xhci->run + XHCI_RUN_ERSTBA ( 0 ) ) ) != 0 ) + goto err_writeq_erstba; + + DBGC2 ( xhci, "XHCI %s event ring [%08lx,%08lx) table [%08lx,%08lx)\n", + xhci->name, virt_to_phys ( event->trb ), + ( virt_to_phys ( event->trb ) + len ), + virt_to_phys ( event->segment ), + ( virt_to_phys ( event->segment ) + + sizeof (event->segment[0] ) ) ); + return 0; + + xhci_writeq ( xhci, 0, xhci->run + XHCI_RUN_ERSTBA ( 0 ) ); + err_writeq_erstba: + xhci_writeq ( xhci, 0, xhci->run + XHCI_RUN_ERDP ( 0 ) ); + err_writeq_erdp: + free_dma ( event->trb, len ); + err_alloc_segment: + free_dma ( event->segment, sizeof ( event->segment[0] ) ); + err_alloc_trb: + return rc; +} + +/** + * Free event ring + * + * @v xhci xHCI device + */ +static void xhci_event_free ( struct xhci_device *xhci ) { + struct xhci_event_ring *event = &xhci->event; + unsigned int count; + size_t len; + + /* Clear event ring registers */ + writel ( 0, xhci->run + XHCI_RUN_ERSTSZ ( 0 ) ); + xhci_writeq ( xhci, 0, xhci->run + XHCI_RUN_ERSTBA ( 0 ) ); + xhci_writeq ( xhci, 0, xhci->run + XHCI_RUN_ERDP ( 0 ) ); + + /* Free event ring segment table */ + free_dma ( event->segment, sizeof ( event->segment[0] ) ); + + /* Free event ring */ + count = ( 1 << XHCI_EVENT_TRBS_LOG2 ); + len = ( count * sizeof ( event->trb[0] ) ); + free_dma ( event->trb, len ); +} + +/** + * Handle transfer event + * + * @v xhci xHCI device + * @v trb Transfer event TRB + */ +static void xhci_transfer ( struct xhci_device *xhci, + struct xhci_trb_transfer *trb ) { + struct xhci_slot *slot; + struct xhci_endpoint *endpoint; + struct io_buffer *iobuf; + int rc; + + /* Profile transfer events */ + profile_start ( &xhci_transfer_profiler ); + + /* Identify slot */ + if ( ( trb->slot > xhci->slots ) || + ( ( slot = xhci->slot[trb->slot] ) == NULL ) ) { + DBGC ( xhci, "XHCI %s transfer event invalid slot %d:\n", + xhci->name, trb->slot ); + DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) ); + return; + } + + /* Identify endpoint */ + if ( ( trb->endpoint > XHCI_CTX_END ) || + ( ( endpoint = slot->endpoint[trb->endpoint] ) == NULL ) ) { + DBGC ( xhci, "XHCI %s slot %d transfer event invalid epid " + "%d:\n", xhci->name, slot->id, trb->endpoint ); + DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) ); + return; + } + + /* Dequeue TRB(s) */ + iobuf = xhci_dequeue_multi ( &endpoint->ring ); + assert ( iobuf != NULL ); + + /* Check for errors */ + if ( ! ( ( trb->code == XHCI_CMPLT_SUCCESS ) || + ( trb->code == XHCI_CMPLT_SHORT ) ) ) { + + /* Construct error */ + rc = -ECODE ( trb->code ); + DBGC ( xhci, "XHCI %s slot %d ctx %d failed (code %d): %s\n", + xhci->name, slot->id, endpoint->ctx, trb->code, + strerror ( rc ) ); + DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) ); + + /* Sanity check */ + assert ( ( endpoint->context->state & XHCI_ENDPOINT_STATE_MASK ) + != XHCI_ENDPOINT_RUNNING ); + + /* Report failure to USB core */ + usb_complete_err ( endpoint->ep, iobuf, rc ); + return; + } + + /* Record actual transfer size */ + iob_unput ( iobuf, le16_to_cpu ( trb->residual ) ); + + /* Sanity check (for successful completions only) */ + assert ( xhci_ring_consumed ( &endpoint->ring ) == + le64_to_cpu ( trb->transfer ) ); + + /* Report completion to USB core */ + usb_complete ( endpoint->ep, iobuf ); + profile_stop ( &xhci_transfer_profiler ); +} + +/** + * Handle command completion event + * + * @v xhci xHCI device + * @v trb Command completion event + */ +static void xhci_complete ( struct xhci_device *xhci, + struct xhci_trb_complete *trb ) { + int rc; + + /* Ignore "command ring stopped" notifications */ + if ( trb->code == XHCI_CMPLT_CMD_STOPPED ) { + DBGC2 ( xhci, "XHCI %s command ring stopped\n", xhci->name ); + return; + } + + /* Ignore unexpected completions */ + if ( ! xhci->pending ) { + rc = -ECODE ( trb->code ); + DBGC ( xhci, "XHCI %s unexpected completion (code %d): %s\n", + xhci->name, trb->code, strerror ( rc ) ); + DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) ); + return; + } + + /* Dequeue command TRB */ + xhci_dequeue ( &xhci->command ); + + /* Sanity check */ + assert ( xhci_ring_consumed ( &xhci->command ) == + le64_to_cpu ( trb->command ) ); + + /* Record completion */ + memcpy ( xhci->pending, trb, sizeof ( *xhci->pending ) ); + xhci->pending = NULL; +} + +/** + * Handle port status event + * + * @v xhci xHCI device + * @v trb Port status event + */ +static void xhci_port_status ( struct xhci_device *xhci, + struct xhci_trb_port_status *trb ) { + struct usb_port *port = usb_port ( xhci->bus->hub, trb->port ); + uint32_t portsc; + + /* Sanity check */ + assert ( ( trb->port > 0 ) && ( trb->port <= xhci->ports ) ); + + /* Record disconnections and clear changes */ + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( trb->port ) ); + port->disconnected |= ( portsc & XHCI_PORTSC_CSC ); + portsc &= ( XHCI_PORTSC_PRESERVE | XHCI_PORTSC_CHANGE ); + writel ( portsc, xhci->op + XHCI_OP_PORTSC ( trb->port ) ); + + /* Report port status change */ + usb_port_changed ( port ); +} + +/** + * Handle host controller event + * + * @v xhci xHCI device + * @v trb Host controller event + */ +static void xhci_host_controller ( struct xhci_device *xhci, + struct xhci_trb_host_controller *trb ) { + int rc; + + /* Construct error */ + rc = -ECODE ( trb->code ); + DBGC ( xhci, "XHCI %s host controller event (code %d): %s\n", + xhci->name, trb->code, strerror ( rc ) ); +} + +/** + * Poll event ring + * + * @v xhci xHCI device + */ +static void xhci_event_poll ( struct xhci_device *xhci ) { + struct xhci_event_ring *event = &xhci->event; + union xhci_trb *trb; + unsigned int shift = XHCI_EVENT_TRBS_LOG2; + unsigned int count = ( 1 << shift ); + unsigned int mask = ( count - 1 ); + unsigned int consumed; + unsigned int type; + + /* Poll for events */ + profile_start ( &xhci_event_profiler ); + for ( consumed = 0 ; ; consumed++ ) { + + /* Stop if we reach an empty TRB */ + rmb(); + trb = &event->trb[ event->cons & mask ]; + if ( ! ( ( trb->common.flags ^ + ( event->cons >> shift ) ) & XHCI_TRB_C ) ) + break; + + /* Handle TRB */ + type = ( trb->common.type & XHCI_TRB_TYPE_MASK ); + switch ( type ) { + + case XHCI_TRB_TRANSFER : + xhci_transfer ( xhci, &trb->transfer ); + break; + + case XHCI_TRB_COMPLETE : + xhci_complete ( xhci, &trb->complete ); + break; + + case XHCI_TRB_PORT_STATUS: + xhci_port_status ( xhci, &trb->port ); + break; + + case XHCI_TRB_HOST_CONTROLLER: + xhci_host_controller ( xhci, &trb->host ); + break; + + default: + DBGC ( xhci, "XHCI %s unrecognised event %#x\n:", + xhci->name, event->cons ); + DBGC_HDA ( xhci, virt_to_phys ( trb ), + trb, sizeof ( *trb ) ); + break; + } + + /* Consume this TRB */ + event->cons++; + } + + /* Update dequeue pointer if applicable */ + if ( consumed ) { + xhci_writeq ( xhci, virt_to_phys ( trb ), + xhci->run + XHCI_RUN_ERDP ( 0 ) ); + profile_stop ( &xhci_event_profiler ); + } +} + +/** + * Abort command + * + * @v xhci xHCI device + */ +static void xhci_abort ( struct xhci_device *xhci ) { + physaddr_t crp; + + /* Abort the command */ + DBGC2 ( xhci, "XHCI %s aborting command\n", xhci->name ); + xhci_writeq ( xhci, XHCI_CRCR_CA, xhci->op + XHCI_OP_CRCR ); + + /* Allow time for command to abort */ + mdelay ( XHCI_COMMAND_ABORT_DELAY_MS ); + + /* Sanity check */ + assert ( ( readl ( xhci->op + XHCI_OP_CRCR ) & XHCI_CRCR_CRR ) == 0 ); + + /* Consume (and ignore) any final command status */ + xhci_event_poll ( xhci ); + + /* Reset the command ring control register */ + xhci_ring_reset ( &xhci->command ); + crp = virt_to_phys ( xhci->command.trb ); + xhci_writeq ( xhci, ( crp | XHCI_CRCR_RCS ), xhci->op + XHCI_OP_CRCR ); +} + +/** + * Issue command and wait for completion + * + * @v xhci xHCI device + * @v trb Transfer request block (with empty Cycle flag) + * @ret rc Return status code + * + * On a successful completion, the TRB will be overwritten with the + * completion. + */ +static int xhci_command ( struct xhci_device *xhci, union xhci_trb *trb ) { + struct xhci_trb_complete *complete = &trb->complete; + unsigned int i; + int rc; + + /* Record the pending command */ + xhci->pending = trb; + + /* Enqueue the command */ + if ( ( rc = xhci_enqueue ( &xhci->command, NULL, trb ) ) != 0 ) + goto err_enqueue; + + /* Ring the command doorbell */ + xhci_doorbell ( &xhci->command ); + + /* Wait for the command to complete */ + for ( i = 0 ; i < XHCI_COMMAND_MAX_WAIT_MS ; i++ ) { + + /* Poll event ring */ + xhci_event_poll ( xhci ); + + /* Check for completion */ + if ( ! xhci->pending ) { + if ( complete->code != XHCI_CMPLT_SUCCESS ) { + rc = -ECODE ( complete->code ); + DBGC ( xhci, "XHCI %s command failed (code " + "%d): %s\n", xhci->name, complete->code, + strerror ( rc ) ); + DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) ); + return rc; + } + return 0; + } + + /* Delay */ + mdelay ( 1 ); + } + + /* Timeout */ + DBGC ( xhci, "XHCI %s timed out waiting for completion\n", xhci->name ); + rc = -ETIMEDOUT; + + /* Abort command */ + xhci_abort ( xhci ); + + err_enqueue: + xhci->pending = NULL; + return rc; +} + +/** + * Issue NOP and wait for completion + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static inline int xhci_nop ( struct xhci_device *xhci ) { + union xhci_trb trb; + struct xhci_trb_common *nop = &trb.common; + int rc; + + /* Construct command */ + memset ( nop, 0, sizeof ( *nop ) ); + nop->flags = XHCI_TRB_IOC; + nop->type = XHCI_TRB_NOP_CMD; + + /* Issue command and wait for completion */ + if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Enable slot + * + * @v xhci xHCI device + * @v type Slot type + * @ret slot Device slot ID, or negative error + */ +static inline int xhci_enable_slot ( struct xhci_device *xhci, + unsigned int type ) { + union xhci_trb trb; + struct xhci_trb_enable_slot *enable = &trb.enable; + struct xhci_trb_complete *enabled = &trb.complete; + unsigned int slot; + int rc; + + /* Construct command */ + memset ( enable, 0, sizeof ( *enable ) ); + enable->slot = type; + enable->type = XHCI_TRB_ENABLE_SLOT; + + /* Issue command and wait for completion */ + if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) { + DBGC ( xhci, "XHCI %s could not enable new slot: %s\n", + xhci->name, strerror ( rc ) ); + return rc; + } + + /* Extract slot number */ + slot = enabled->slot; + + DBGC2 ( xhci, "XHCI %s slot %d enabled\n", xhci->name, slot ); + return slot; +} + +/** + * Disable slot + * + * @v xhci xHCI device + * @v slot Device slot + * @ret rc Return status code + */ +static inline int xhci_disable_slot ( struct xhci_device *xhci, + unsigned int slot ) { + union xhci_trb trb; + struct xhci_trb_disable_slot *disable = &trb.disable; + int rc; + + /* Construct command */ + memset ( disable, 0, sizeof ( *disable ) ); + disable->type = XHCI_TRB_DISABLE_SLOT; + disable->slot = slot; + + /* Issue command and wait for completion */ + if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) { + DBGC ( xhci, "XHCI %s could not disable slot %d: %s\n", + xhci->name, slot, strerror ( rc ) ); + return rc; + } + + DBGC2 ( xhci, "XHCI %s slot %d disabled\n", xhci->name, slot ); + return 0; +} + +/** + * Issue context-based command and wait for completion + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @v type TRB type + * @v populate Input context populater + * @ret rc Return status code + */ +static int xhci_context ( struct xhci_device *xhci, struct xhci_slot *slot, + struct xhci_endpoint *endpoint, unsigned int type, + void ( * populate ) ( struct xhci_device *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint, + void *input ) ) { + union xhci_trb trb; + struct xhci_trb_context *context = &trb.context; + size_t len; + void *input; + int rc; + + /* Allocate an input context */ + len = xhci_input_context_offset ( xhci, XHCI_CTX_END ); + input = malloc_dma ( len, xhci_align ( len ) ); + if ( ! input ) { + rc = -ENOMEM; + goto err_alloc; + } + memset ( input, 0, len ); + + /* Populate input context */ + populate ( xhci, slot, endpoint, input ); + + /* Construct command */ + memset ( context, 0, sizeof ( *context ) ); + context->type = type; + context->input = cpu_to_le64 ( virt_to_phys ( input ) ); + context->slot = slot->id; + + /* Issue command and wait for completion */ + if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) + goto err_command; + + err_command: + free_dma ( input, len ); + err_alloc: + return rc; +} + +/** + * Populate address device input context + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @v input Input context + */ +static void xhci_address_device_input ( struct xhci_device *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint, + void *input ) { + struct xhci_control_context *control_ctx; + struct xhci_slot_context *slot_ctx; + struct xhci_endpoint_context *ep_ctx; + + /* Sanity checks */ + assert ( endpoint->ctx == XHCI_CTX_EP0 ); + + /* Populate control context */ + control_ctx = input; + control_ctx->add = cpu_to_le32 ( ( 1 << XHCI_CTX_SLOT ) | + ( 1 << XHCI_CTX_EP0 ) ); + + /* Populate slot context */ + slot_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_SLOT )); + slot_ctx->info = cpu_to_le32 ( XHCI_SLOT_INFO ( 1, 0, slot->psiv, + slot->route ) ); + slot_ctx->port = slot->port; + slot_ctx->tt_id = slot->tt_id; + slot_ctx->tt_port = slot->tt_port; + + /* Populate control endpoint context */ + ep_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_EP0 ) ); + ep_ctx->type = XHCI_EP_TYPE_CONTROL; + ep_ctx->burst = endpoint->ep->burst; + ep_ctx->mtu = cpu_to_le16 ( endpoint->ep->mtu ); + ep_ctx->dequeue = cpu_to_le64 ( virt_to_phys ( endpoint->ring.trb ) | + XHCI_EP_DCS ); + ep_ctx->trb_len = cpu_to_le16 ( XHCI_EP0_TRB_LEN ); +} + +/** + * Address device + * + * @v xhci xHCI device + * @v slot Device slot + * @ret rc Return status code + */ +static inline int xhci_address_device ( struct xhci_device *xhci, + struct xhci_slot *slot ) { + struct usb_device *usb = slot->usb; + struct xhci_slot_context *slot_ctx; + int rc; + + /* Assign device address */ + if ( ( rc = xhci_context ( xhci, slot, slot->endpoint[XHCI_CTX_EP0], + XHCI_TRB_ADDRESS_DEVICE, + xhci_address_device_input ) ) != 0 ) + return rc; + + /* Get assigned address */ + slot_ctx = ( slot->context + + xhci_device_context_offset ( xhci, XHCI_CTX_SLOT ) ); + usb->address = slot_ctx->address; + DBGC2 ( xhci, "XHCI %s assigned address %d to %s\n", + xhci->name, usb->address, usb->name ); + + return 0; +} + +/** + * Populate configure endpoint input context + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @v input Input context + */ +static void xhci_configure_endpoint_input ( struct xhci_device *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint, + void *input ) { + struct xhci_control_context *control_ctx; + struct xhci_slot_context *slot_ctx; + struct xhci_endpoint_context *ep_ctx; + + /* Populate control context */ + control_ctx = input; + control_ctx->add = cpu_to_le32 ( ( 1 << XHCI_CTX_SLOT ) | + ( 1 << endpoint->ctx ) ); + + /* Populate slot context */ + slot_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_SLOT )); + slot_ctx->info = cpu_to_le32 ( XHCI_SLOT_INFO ( ( XHCI_CTX_END - 1 ), + ( slot->ports ? 1 : 0 ), + slot->psiv, 0 ) ); + slot_ctx->ports = slot->ports; + + /* Populate endpoint context */ + ep_ctx = ( input + xhci_input_context_offset ( xhci, endpoint->ctx ) ); + ep_ctx->interval = endpoint->interval; + ep_ctx->type = endpoint->type; + ep_ctx->burst = endpoint->ep->burst; + ep_ctx->mtu = cpu_to_le16 ( endpoint->ep->mtu ); + ep_ctx->dequeue = cpu_to_le64 ( virt_to_phys ( endpoint->ring.trb ) | + XHCI_EP_DCS ); + ep_ctx->trb_len = cpu_to_le16 ( endpoint->ep->mtu ); /* best guess */ +} + +/** + * Configure endpoint + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @ret rc Return status code + */ +static inline int xhci_configure_endpoint ( struct xhci_device *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint ) { + int rc; + + /* Configure endpoint */ + if ( ( rc = xhci_context ( xhci, slot, endpoint, + XHCI_TRB_CONFIGURE_ENDPOINT, + xhci_configure_endpoint_input ) ) != 0 ) + return rc; + + DBGC2 ( xhci, "XHCI %s slot %d ctx %d configured\n", + xhci->name, slot->id, endpoint->ctx ); + return 0; +} + +/** + * Populate deconfigure endpoint input context + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @v input Input context + */ +static void +xhci_deconfigure_endpoint_input ( struct xhci_device *xhci __unused, + struct xhci_slot *slot __unused, + struct xhci_endpoint *endpoint, + void *input ) { + struct xhci_control_context *control_ctx; + struct xhci_slot_context *slot_ctx; + + /* Populate control context */ + control_ctx = input; + control_ctx->add = cpu_to_le32 ( 1 << XHCI_CTX_SLOT ); + control_ctx->drop = cpu_to_le32 ( 1 << endpoint->ctx ); + + /* Populate slot context */ + slot_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_SLOT )); + slot_ctx->info = cpu_to_le32 ( XHCI_SLOT_INFO ( ( XHCI_CTX_END - 1 ), + 0, 0, 0 ) ); +} + +/** + * Deconfigure endpoint + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @ret rc Return status code + */ +static inline int xhci_deconfigure_endpoint ( struct xhci_device *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint ) { + int rc; + + /* Deconfigure endpoint */ + if ( ( rc = xhci_context ( xhci, slot, endpoint, + XHCI_TRB_CONFIGURE_ENDPOINT, + xhci_deconfigure_endpoint_input ) ) != 0 ) + return rc; + + DBGC2 ( xhci, "XHCI %s slot %d ctx %d deconfigured\n", + xhci->name, slot->id, endpoint->ctx ); + return 0; +} + +/** + * Populate evaluate context input context + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @v input Input context + */ +static void xhci_evaluate_context_input ( struct xhci_device *xhci, + struct xhci_slot *slot __unused, + struct xhci_endpoint *endpoint, + void *input ) { + struct xhci_control_context *control_ctx; + struct xhci_slot_context *slot_ctx; + struct xhci_endpoint_context *ep_ctx; + + /* Populate control context */ + control_ctx = input; + control_ctx->add = cpu_to_le32 ( ( 1 << XHCI_CTX_SLOT ) | + ( 1 << endpoint->ctx ) ); + + /* Populate slot context */ + slot_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_SLOT )); + slot_ctx->info = cpu_to_le32 ( XHCI_SLOT_INFO ( ( XHCI_CTX_END - 1 ), + 0, 0, 0 ) ); + + /* Populate endpoint context */ + ep_ctx = ( input + xhci_input_context_offset ( xhci, endpoint->ctx ) ); + ep_ctx->mtu = cpu_to_le16 ( endpoint->ep->mtu ); +} + +/** + * Evaluate context + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @ret rc Return status code + */ +static inline int xhci_evaluate_context ( struct xhci_device *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint ) { + int rc; + + /* Configure endpoint */ + if ( ( rc = xhci_context ( xhci, slot, endpoint, + XHCI_TRB_EVALUATE_CONTEXT, + xhci_evaluate_context_input ) ) != 0 ) + return rc; + + DBGC2 ( xhci, "XHCI %s slot %d ctx %d (re-)evaluated\n", + xhci->name, slot->id, endpoint->ctx ); + return 0; +} + +/** + * Reset endpoint + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @ret rc Return status code + */ +static inline int xhci_reset_endpoint ( struct xhci_device *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint ) { + union xhci_trb trb; + struct xhci_trb_reset_endpoint *reset = &trb.reset; + int rc; + + /* Construct command */ + memset ( reset, 0, sizeof ( *reset ) ); + reset->slot = slot->id; + reset->endpoint = endpoint->ctx; + reset->type = XHCI_TRB_RESET_ENDPOINT; + + /* Issue command and wait for completion */ + if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) { + DBGC ( xhci, "XHCI %s slot %d ctx %d could not reset endpoint " + "in state %d: %s\n", xhci->name, slot->id, endpoint->ctx, + endpoint->context->state, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Stop endpoint + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @ret rc Return status code + */ +static inline int xhci_stop_endpoint ( struct xhci_device *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint ) { + union xhci_trb trb; + struct xhci_trb_stop_endpoint *stop = &trb.stop; + int rc; + + /* Construct command */ + memset ( stop, 0, sizeof ( *stop ) ); + stop->slot = slot->id; + stop->endpoint = endpoint->ctx; + stop->type = XHCI_TRB_STOP_ENDPOINT; + + /* Issue command and wait for completion */ + if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) { + DBGC ( xhci, "XHCI %s slot %d ctx %d could not stop endpoint " + "in state %d: %s\n", xhci->name, slot->id, endpoint->ctx, + endpoint->context->state, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Set transfer ring dequeue pointer + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @ret rc Return status code + */ +static inline int +xhci_set_tr_dequeue_pointer ( struct xhci_device *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint ) { + union xhci_trb trb; + struct xhci_trb_set_tr_dequeue_pointer *dequeue = &trb.dequeue; + struct xhci_trb_ring *ring = &endpoint->ring; + unsigned int cons; + unsigned int mask; + unsigned int index; + unsigned int dcs; + int rc; + + /* Construct command */ + memset ( dequeue, 0, sizeof ( *dequeue ) ); + cons = ring->cons; + mask = ring->mask; + dcs = ( ( ~( cons >> ring->shift ) ) & XHCI_EP_DCS ); + index = ( cons & mask ); + dequeue->dequeue = + cpu_to_le64 ( virt_to_phys ( &ring->trb[index] ) | dcs ); + dequeue->slot = slot->id; + dequeue->endpoint = endpoint->ctx; + dequeue->type = XHCI_TRB_SET_TR_DEQUEUE_POINTER; + + /* Issue command and wait for completion */ + if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) { + DBGC ( xhci, "XHCI %s slot %d ctx %d could not set TR dequeue " + "pointer in state %d: %s\n", xhci->name, slot->id, + endpoint->ctx, endpoint->context->state, strerror ( rc)); + return rc; + } + + return 0; +} + +/****************************************************************************** + * + * Endpoint operations + * + ****************************************************************************** + */ + +/** + * Open endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int xhci_endpoint_open ( struct usb_endpoint *ep ) { + struct usb_device *usb = ep->usb; + struct xhci_slot *slot = usb_get_hostdata ( usb ); + struct xhci_device *xhci = slot->xhci; + struct xhci_endpoint *endpoint; + unsigned int ctx; + unsigned int type; + unsigned int interval; + int rc; + + /* Calculate context index */ + ctx = XHCI_CTX ( ep->address ); + assert ( slot->endpoint[ctx] == NULL ); + + /* Calculate endpoint type */ + type = XHCI_EP_TYPE ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); + if ( type == XHCI_EP_TYPE ( USB_ENDPOINT_ATTR_CONTROL ) ) + type = XHCI_EP_TYPE_CONTROL; + if ( ep->address & USB_DIR_IN ) + type |= XHCI_EP_TYPE_IN; + + /* Calculate interval */ + if ( type & XHCI_EP_TYPE_PERIODIC ) { + interval = ( fls ( ep->interval ) - 1 ); + } else { + interval = ep->interval; + } + + /* Allocate and initialise structure */ + endpoint = zalloc ( sizeof ( *endpoint ) ); + if ( ! endpoint ) { + rc = -ENOMEM; + goto err_alloc; + } + usb_endpoint_set_hostdata ( ep, endpoint ); + slot->endpoint[ctx] = endpoint; + endpoint->xhci = xhci; + endpoint->slot = slot; + endpoint->ep = ep; + endpoint->ctx = ctx; + endpoint->type = type; + endpoint->interval = interval; + endpoint->context = ( ( ( void * ) slot->context ) + + xhci_device_context_offset ( xhci, ctx ) ); + + /* Allocate transfer ring */ + if ( ( rc = xhci_ring_alloc ( xhci, &endpoint->ring, + XHCI_TRANSFER_TRBS_LOG2, + slot->id, ctx, 0 ) ) != 0 ) + goto err_ring_alloc; + + /* Configure endpoint, if applicable */ + if ( ( ctx != XHCI_CTX_EP0 ) && + ( ( rc = xhci_configure_endpoint ( xhci, slot, endpoint ) ) != 0 )) + goto err_configure_endpoint; + + DBGC2 ( xhci, "XHCI %s slot %d ctx %d ring [%08lx,%08lx)\n", + xhci->name, slot->id, ctx, virt_to_phys ( endpoint->ring.trb ), + ( virt_to_phys ( endpoint->ring.trb ) + endpoint->ring.len ) ); + return 0; + + xhci_deconfigure_endpoint ( xhci, slot, endpoint ); + err_configure_endpoint: + xhci_ring_free ( &endpoint->ring ); + err_ring_alloc: + slot->endpoint[ctx] = NULL; + free ( endpoint ); + err_alloc: + return rc; +} + +/** + * Close endpoint + * + * @v ep USB endpoint + */ +static void xhci_endpoint_close ( struct usb_endpoint *ep ) { + struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct xhci_slot *slot = endpoint->slot; + struct xhci_device *xhci = slot->xhci; + struct io_buffer *iobuf; + unsigned int ctx = endpoint->ctx; + + /* Deconfigure endpoint, if applicable */ + if ( ctx != XHCI_CTX_EP0 ) + xhci_deconfigure_endpoint ( xhci, slot, endpoint ); + + /* Cancel any incomplete transfers */ + while ( xhci_ring_fill ( &endpoint->ring ) ) { + iobuf = xhci_dequeue_multi ( &endpoint->ring ); + usb_complete_err ( ep, iobuf, -ECANCELED ); + } + + /* Free endpoint */ + xhci_ring_free ( &endpoint->ring ); + slot->endpoint[ctx] = NULL; + free ( endpoint ); +} + +/** + * Reset endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int xhci_endpoint_reset ( struct usb_endpoint *ep ) { + struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct xhci_slot *slot = endpoint->slot; + struct xhci_device *xhci = slot->xhci; + int rc; + + /* Reset endpoint context */ + if ( ( rc = xhci_reset_endpoint ( xhci, slot, endpoint ) ) != 0 ) + return rc; + + /* Set transfer ring dequeue pointer */ + if ( ( rc = xhci_set_tr_dequeue_pointer ( xhci, slot, endpoint ) ) != 0) + return rc; + + /* Ring doorbell to resume processing */ + xhci_doorbell ( &endpoint->ring ); + + DBGC ( xhci, "XHCI %s slot %d ctx %d reset\n", + xhci->name, slot->id, endpoint->ctx ); + return 0; +} + +/** + * Update MTU + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int xhci_endpoint_mtu ( struct usb_endpoint *ep ) { + struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct xhci_slot *slot = endpoint->slot; + struct xhci_device *xhci = slot->xhci; + int rc; + + /* Evalulate context */ + if ( ( rc = xhci_evaluate_context ( xhci, slot, endpoint ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Enqueue message transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int xhci_endpoint_message ( struct usb_endpoint *ep, + struct io_buffer *iobuf ) { + struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct usb_setup_packet *packet; + unsigned int input; + size_t len; + union xhci_trb trbs[ 1 /* setup */ + 1 /* possible data */ + + 1 /* status */ ]; + union xhci_trb *trb = trbs; + struct xhci_trb_setup *setup; + struct xhci_trb_data *data; + struct xhci_trb_status *status; + int rc; + + /* Profile message transfers */ + profile_start ( &xhci_message_profiler ); + + /* Construct setup stage TRB */ + memset ( trbs, 0, sizeof ( trbs ) ); + assert ( iob_len ( iobuf ) >= sizeof ( *packet ) ); + packet = iobuf->data; + iob_pull ( iobuf, sizeof ( *packet ) ); + setup = &(trb++)->setup; + memcpy ( &setup->packet, packet, sizeof ( setup->packet ) ); + setup->len = cpu_to_le32 ( sizeof ( *packet ) ); + setup->flags = XHCI_TRB_IDT; + setup->type = XHCI_TRB_SETUP; + len = iob_len ( iobuf ); + input = ( packet->request & cpu_to_le16 ( USB_DIR_IN ) ); + if ( len ) + setup->direction = ( input ? XHCI_SETUP_IN : XHCI_SETUP_OUT ); + + /* Construct data stage TRB, if applicable */ + if ( len ) { + data = &(trb++)->data; + data->data = cpu_to_le64 ( virt_to_phys ( iobuf->data ) ); + data->len = cpu_to_le32 ( len ); + data->type = XHCI_TRB_DATA; + data->direction = ( input ? XHCI_DATA_IN : XHCI_DATA_OUT ); + } + + /* Construct status stage TRB */ + status = &(trb++)->status; + status->flags = XHCI_TRB_IOC; + status->type = XHCI_TRB_STATUS; + status->direction = + ( ( len && input ) ? XHCI_STATUS_OUT : XHCI_STATUS_IN ); + + /* Enqueue TRBs */ + if ( ( rc = xhci_enqueue_multi ( &endpoint->ring, iobuf, trbs, + ( trb - trbs ) ) ) != 0 ) + return rc; + + /* Ring the doorbell */ + xhci_doorbell ( &endpoint->ring ); + + profile_stop ( &xhci_message_profiler ); + return 0; +} + +/** + * Enqueue stream transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v terminate Terminate using a short packet + * @ret rc Return status code + */ +static int xhci_endpoint_stream ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int terminate ) { + struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + union xhci_trb trbs[ 1 /* Normal */ + 1 /* Possible zero-length */ ]; + union xhci_trb *trb = trbs; + struct xhci_trb_normal *normal; + size_t len = iob_len ( iobuf ); + int rc; + + /* Profile stream transfers */ + profile_start ( &xhci_stream_profiler ); + + /* Construct normal TRBs */ + memset ( &trbs, 0, sizeof ( trbs ) ); + normal = &(trb++)->normal; + normal->data = cpu_to_le64 ( virt_to_phys ( iobuf->data ) ); + normal->len = cpu_to_le32 ( len ); + normal->type = XHCI_TRB_NORMAL; + if ( terminate && ( ( len & ( ep->mtu - 1 ) ) == 0 ) ) { + normal->flags = XHCI_TRB_CH; + normal = &(trb++)->normal; + normal->type = XHCI_TRB_NORMAL; + } + normal->flags = XHCI_TRB_IOC; + + /* Enqueue TRBs */ + if ( ( rc = xhci_enqueue_multi ( &endpoint->ring, iobuf, trbs, + ( trb - trbs ) ) ) != 0 ) + return rc; + + /* Ring the doorbell */ + xhci_doorbell ( &endpoint->ring ); + + profile_stop ( &xhci_stream_profiler ); + return 0; +} + +/****************************************************************************** + * + * Device operations + * + ****************************************************************************** + */ + +/** + * Open device + * + * @v usb USB device + * @ret rc Return status code + */ +static int xhci_device_open ( struct usb_device *usb ) { + struct xhci_device *xhci = usb_bus_get_hostdata ( usb->port->hub->bus ); + struct usb_port *tt = usb_transaction_translator ( usb ); + struct xhci_slot *slot; + struct xhci_slot *tt_slot; + size_t len; + int type; + int id; + int rc; + + /* Determine applicable slot type */ + type = xhci_port_slot_type ( xhci, usb->port->address ); + if ( type < 0 ) { + rc = type; + DBGC ( xhci, "XHCI %s-%d has no slot type\n", + xhci->name, usb->port->address ); + goto err_type; + } + + /* Allocate a device slot number */ + id = xhci_enable_slot ( xhci, type ); + if ( id < 0 ) { + rc = id; + goto err_enable_slot; + } + assert ( ( id > 0 ) && ( ( unsigned int ) id <= xhci->slots ) ); + assert ( xhci->slot[id] == NULL ); + + /* Allocate and initialise structure */ + slot = zalloc ( sizeof ( *slot ) ); + if ( ! slot ) { + rc = -ENOMEM; + goto err_alloc; + } + usb_set_hostdata ( usb, slot ); + xhci->slot[id] = slot; + slot->xhci = xhci; + slot->usb = usb; + slot->id = id; + if ( tt ) { + tt_slot = usb_get_hostdata ( tt->hub->usb ); + slot->tt_id = tt_slot->id; + slot->tt_port = tt->address; + } + + /* Allocate a device context */ + len = xhci_device_context_offset ( xhci, XHCI_CTX_END ); + slot->context = malloc_dma ( len, xhci_align ( len ) ); + if ( ! slot->context ) { + rc = -ENOMEM; + goto err_alloc_context; + } + memset ( slot->context, 0, len ); + + /* Set device context base address */ + assert ( xhci->dcbaa[id] == 0 ); + xhci->dcbaa[id] = cpu_to_le64 ( virt_to_phys ( slot->context ) ); + + DBGC2 ( xhci, "XHCI %s slot %d device context [%08lx,%08lx) for %s\n", + xhci->name, slot->id, virt_to_phys ( slot->context ), + ( virt_to_phys ( slot->context ) + len ), usb->name ); + return 0; + + xhci->dcbaa[id] = 0; + free_dma ( slot->context, len ); + err_alloc_context: + xhci->slot[id] = NULL; + free ( slot ); + err_alloc: + xhci_disable_slot ( xhci, id ); + err_enable_slot: + err_type: + return rc; +} + +/** + * Close device + * + * @v usb USB device + */ +static void xhci_device_close ( struct usb_device *usb ) { + struct xhci_slot *slot = usb_get_hostdata ( usb ); + struct xhci_device *xhci = slot->xhci; + size_t len = xhci_device_context_offset ( xhci, XHCI_CTX_END ); + unsigned int id = slot->id; + int rc; + + /* Disable slot */ + if ( ( rc = xhci_disable_slot ( xhci, id ) ) != 0 ) { + /* Slot is still enabled. Leak the slot context, + * since the controller may still write to this + * memory, and leave the DCBAA entry intact. + * + * If the controller later reports that this same slot + * has been re-enabled, then some assertions will be + * triggered. + */ + DBGC ( xhci, "XHCI %s slot %d leaking context memory\n", + xhci->name, slot->id ); + slot->context = NULL; + } + + /* Free slot */ + if ( slot->context ) { + free_dma ( slot->context, len ); + xhci->dcbaa[id] = 0; + } + xhci->slot[id] = NULL; + free ( slot ); +} + +/** + * Assign device address + * + * @v usb USB device + * @ret rc Return status code + */ +static int xhci_device_address ( struct usb_device *usb ) { + struct xhci_slot *slot = usb_get_hostdata ( usb ); + struct xhci_device *xhci = slot->xhci; + struct usb_port *port = usb->port; + struct usb_port *root_port; + int psiv; + int rc; + + /* Calculate route string */ + slot->route = usb_route_string ( usb ); + + /* Calculate root hub port number */ + root_port = usb_root_hub_port ( usb ); + slot->port = root_port->address; + + /* Calculate protocol speed ID */ + psiv = xhci_port_psiv ( xhci, slot->port, port->speed ); + if ( psiv < 0 ) { + rc = psiv; + return rc; + } + slot->psiv = psiv; + + /* Address device */ + if ( ( rc = xhci_address_device ( xhci, slot ) ) != 0 ) + return rc; + + return 0; +} + +/****************************************************************************** + * + * Bus operations + * + ****************************************************************************** + */ + +/** + * Open USB bus + * + * @v bus USB bus + * @ret rc Return status code + */ +static int xhci_bus_open ( struct usb_bus *bus ) { + struct xhci_device *xhci = usb_bus_get_hostdata ( bus ); + int rc; + + /* Allocate device slot array */ + xhci->slot = zalloc ( ( xhci->slots + 1 ) * sizeof ( xhci->slot[0] ) ); + if ( ! xhci->slot ) { + rc = -ENOMEM; + goto err_slot_alloc; + } + + /* Allocate device context base address array */ + if ( ( rc = xhci_dcbaa_alloc ( xhci ) ) != 0 ) + goto err_dcbaa_alloc; + + /* Allocate scratchpad buffers */ + if ( ( rc = xhci_scratchpad_alloc ( xhci ) ) != 0 ) + goto err_scratchpad_alloc; + + /* Allocate command ring */ + if ( ( rc = xhci_command_alloc ( xhci ) ) != 0 ) + goto err_command_alloc; + + /* Allocate event ring */ + if ( ( rc = xhci_event_alloc ( xhci ) ) != 0 ) + goto err_event_alloc; + + /* Start controller */ + xhci_run ( xhci ); + + return 0; + + xhci_stop ( xhci ); + xhci_event_free ( xhci ); + err_event_alloc: + xhci_command_free ( xhci ); + err_command_alloc: + xhci_scratchpad_free ( xhci ); + err_scratchpad_alloc: + xhci_dcbaa_free ( xhci ); + err_dcbaa_alloc: + free ( xhci->slot ); + err_slot_alloc: + return rc; +} + +/** + * Close USB bus + * + * @v bus USB bus + */ +static void xhci_bus_close ( struct usb_bus *bus ) { + struct xhci_device *xhci = usb_bus_get_hostdata ( bus ); + unsigned int i; + + /* Sanity checks */ + assert ( xhci->slot != NULL ); + for ( i = 0 ; i <= xhci->slots ; i++ ) + assert ( xhci->slot[i] == NULL ); + + xhci_stop ( xhci ); + xhci_event_free ( xhci ); + xhci_command_free ( xhci ); + xhci_scratchpad_free ( xhci ); + xhci_dcbaa_free ( xhci ); + free ( xhci->slot ); +} + +/** + * Poll USB bus + * + * @v bus USB bus + */ +static void xhci_bus_poll ( struct usb_bus *bus ) { + struct xhci_device *xhci = usb_bus_get_hostdata ( bus ); + + /* Poll event ring */ + xhci_event_poll ( xhci ); +} + +/****************************************************************************** + * + * Hub operations + * + ****************************************************************************** + */ + +/** + * Open hub + * + * @v hub USB hub + * @ret rc Return status code + */ +static int xhci_hub_open ( struct usb_hub *hub ) { + struct xhci_slot *slot; + + /* Do nothing if this is the root hub */ + if ( ! hub->usb ) + return 0; + + /* Get device slot */ + slot = usb_get_hostdata ( hub->usb ); + + /* Update device slot hub parameters. We don't inform the + * hardware of this information until the hub's interrupt + * endpoint is opened, since the only mechanism for so doing + * provided by the xHCI specification is a Configure Endpoint + * command, and we can't issue that command until we have a + * non-EP0 endpoint to configure. + */ + slot->ports = hub->ports; + + return 0; +} + +/** + * Close hub + * + * @v hub USB hub + */ +static void xhci_hub_close ( struct usb_hub *hub __unused ) { + + /* Nothing to do */ +} + +/****************************************************************************** + * + * Root hub operations + * + ****************************************************************************** + */ + +/** + * Open root hub + * + * @v hub USB hub + * @ret rc Return status code + */ +static int xhci_root_open ( struct usb_hub *hub ) { + struct usb_bus *bus = hub->bus; + struct xhci_device *xhci = usb_bus_get_hostdata ( bus ); + struct usb_port *port; + uint32_t portsc; + unsigned int i; + + /* Enable power to all ports */ + for ( i = 1 ; i <= xhci->ports ; i++ ) { + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( i ) ); + portsc &= XHCI_PORTSC_PRESERVE; + portsc |= XHCI_PORTSC_PP; + writel ( portsc, xhci->op + XHCI_OP_PORTSC ( i ) ); + } + + /* xHCI spec requires us to potentially wait 20ms after + * enabling power to a port. + */ + mdelay ( XHCI_PORT_POWER_DELAY_MS ); + + /* USB3 ports may power up as Disabled */ + for ( i = 1 ; i <= xhci->ports ; i++ ) { + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( i ) ); + port = usb_port ( hub, i ); + if ( ( port->protocol >= USB_PROTO_3_0 ) && + ( ( portsc & XHCI_PORTSC_PLS_MASK ) == + XHCI_PORTSC_PLS_DISABLED ) ) { + /* Force link state to RxDetect */ + portsc &= XHCI_PORTSC_PRESERVE; + portsc |= ( XHCI_PORTSC_PLS_RXDETECT | XHCI_PORTSC_LWS); + writel ( portsc, xhci->op + XHCI_OP_PORTSC ( i ) ); + } + } + + /* Some xHCI cards seem to require an additional delay after + * setting the link state to RxDetect. + */ + mdelay ( XHCI_LINK_STATE_DELAY_MS ); + + /* Record hub driver private data */ + usb_hub_set_drvdata ( hub, xhci ); + + return 0; +} + +/** + * Close root hub + * + * @v hub USB hub + */ +static void xhci_root_close ( struct usb_hub *hub ) { + + /* Clear hub driver private data */ + usb_hub_set_drvdata ( hub, NULL ); +} + +/** + * Enable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int xhci_root_enable ( struct usb_hub *hub, struct usb_port *port ) { + struct xhci_device *xhci = usb_hub_get_drvdata ( hub ); + uint32_t portsc; + unsigned int i; + + /* Reset port */ + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port->address ) ); + portsc &= XHCI_PORTSC_PRESERVE; + portsc |= XHCI_PORTSC_PR; + writel ( portsc, xhci->op + XHCI_OP_PORTSC ( port->address ) ); + + /* Wait for port to become enabled */ + for ( i = 0 ; i < XHCI_PORT_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check port status */ + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port->address ) ); + if ( portsc & XHCI_PORTSC_PED ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( xhci, "XHCI %s-%d timed out waiting for port to enable\n", + xhci->name, port->address ); + return -ETIMEDOUT; +} + +/** + * Disable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int xhci_root_disable ( struct usb_hub *hub, struct usb_port *port ) { + struct xhci_device *xhci = usb_hub_get_drvdata ( hub ); + uint32_t portsc; + + /* Disable port */ + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port->address ) ); + portsc &= XHCI_PORTSC_PRESERVE; + portsc |= XHCI_PORTSC_PED; + writel ( portsc, xhci->op + XHCI_OP_PORTSC ( port->address ) ); + + return 0; +} + +/** + * Update root hub port speed + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int xhci_root_speed ( struct usb_hub *hub, struct usb_port *port ) { + struct xhci_device *xhci = usb_hub_get_drvdata ( hub ); + uint32_t portsc; + unsigned int psiv; + int ccs; + int ped; + int csc; + int speed; + int rc; + + /* Read port status */ + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port->address ) ); + DBGC2 ( xhci, "XHCI %s-%d status is %08x\n", + xhci->name, port->address, portsc ); + ccs = ( portsc & XHCI_PORTSC_CCS ); + ped = ( portsc & XHCI_PORTSC_PED ); + csc = ( portsc & XHCI_PORTSC_CSC ); + psiv = XHCI_PORTSC_PSIV ( portsc ); + + /* Record disconnections and clear changes */ + port->disconnected |= csc; + portsc &= ( XHCI_PORTSC_PRESERVE | XHCI_PORTSC_CHANGE ); + writel ( portsc, xhci->op + XHCI_OP_PORTSC ( port->address ) ); + + /* Port speed is not valid unless port is connected */ + if ( ! ccs ) { + port->speed = USB_SPEED_NONE; + return 0; + } + + /* For USB2 ports, the PSIV field is not valid until the port + * completes reset and becomes enabled. + */ + if ( ( port->protocol < USB_PROTO_3_0 ) && ! ped ) { + port->speed = USB_SPEED_FULL; + return 0; + } + + /* Get port speed and map to generic USB speed */ + speed = xhci_port_speed ( xhci, port->address, psiv ); + if ( speed < 0 ) { + rc = speed; + return rc; + } + + port->speed = speed; + return 0; +} + +/** + * Clear transaction translator buffer + * + * @v hub USB hub + * @v port USB port + * @v ep USB endpoint + * @ret rc Return status code + */ +static int xhci_root_clear_tt ( struct usb_hub *hub, struct usb_port *port, + struct usb_endpoint *ep ) { + struct xhci_device *xhci = usb_hub_get_drvdata ( hub ); + + /* Should never be called; this is a root hub */ + DBGC ( xhci, "XHCI %s-%d nonsensical CLEAR_TT for %s %s\n", xhci->name, + port->address, ep->usb->name, usb_endpoint_name ( ep ) ); + + return -ENOTSUP; +} + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** USB host controller operations */ +static struct usb_host_operations xhci_operations = { + .endpoint = { + .open = xhci_endpoint_open, + .close = xhci_endpoint_close, + .reset = xhci_endpoint_reset, + .mtu = xhci_endpoint_mtu, + .message = xhci_endpoint_message, + .stream = xhci_endpoint_stream, + }, + .device = { + .open = xhci_device_open, + .close = xhci_device_close, + .address = xhci_device_address, + }, + .bus = { + .open = xhci_bus_open, + .close = xhci_bus_close, + .poll = xhci_bus_poll, + }, + .hub = { + .open = xhci_hub_open, + .close = xhci_hub_close, + }, + .root = { + .open = xhci_root_open, + .close = xhci_root_close, + .enable = xhci_root_enable, + .disable = xhci_root_disable, + .speed = xhci_root_speed, + .clear_tt = xhci_root_clear_tt, + }, +}; + +/** + * Fix Intel PCH-specific quirks + * + * @v xhci xHCI device + * @v pci PCI device + */ +static void xhci_pch_fix ( struct xhci_device *xhci, struct pci_device *pci ) { + struct xhci_pch *pch = &xhci->pch; + uint32_t xusb2pr; + uint32_t xusb2prm; + uint32_t usb3pssen; + uint32_t usb3prm; + + /* Enable SuperSpeed capability. Do this before rerouting + * USB2 ports, so that USB3 devices connect at SuperSpeed. + */ + pci_read_config_dword ( pci, XHCI_PCH_USB3PSSEN, &usb3pssen ); + pci_read_config_dword ( pci, XHCI_PCH_USB3PRM, &usb3prm ); + if ( usb3prm & ~usb3pssen ) { + DBGC ( xhci, "XHCI %s enabling SuperSpeed on ports %08x\n", + xhci->name, ( usb3prm & ~usb3pssen ) ); + } + pch->usb3pssen = usb3pssen; + usb3pssen |= usb3prm; + pci_write_config_dword ( pci, XHCI_PCH_USB3PSSEN, usb3pssen ); + + /* Route USB2 ports from EHCI to xHCI */ + pci_read_config_dword ( pci, XHCI_PCH_XUSB2PR, &xusb2pr ); + pci_read_config_dword ( pci, XHCI_PCH_XUSB2PRM, &xusb2prm ); + if ( xusb2prm & ~xusb2pr ) { + DBGC ( xhci, "XHCI %s routing ports %08x from EHCI to xHCI\n", + xhci->name, ( xusb2prm & ~xusb2pr ) ); + } + pch->xusb2pr = xusb2pr; + xusb2pr |= xusb2prm; + pci_write_config_dword ( pci, XHCI_PCH_XUSB2PR, xusb2pr ); +} + +/** + * Undo Intel PCH-specific quirk fixes + * + * @v xhci xHCI device + * @v pci PCI device + */ +static void xhci_pch_undo ( struct xhci_device *xhci, struct pci_device *pci ) { + struct xhci_pch *pch = &xhci->pch; + + /* Restore USB2 port routing to original state */ + pci_write_config_dword ( pci, XHCI_PCH_XUSB2PR, pch->xusb2pr ); + + /* Restore SuperSpeed capability to original state */ + pci_write_config_dword ( pci, XHCI_PCH_USB3PSSEN, pch->usb3pssen ); +} + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int xhci_probe ( struct pci_device *pci ) { + struct xhci_device *xhci; + struct usb_port *port; + unsigned long bar_start; + size_t bar_size; + unsigned int i; + int rc; + + /* Allocate and initialise structure */ + xhci = zalloc ( sizeof ( *xhci ) ); + if ( ! xhci ) { + rc = -ENOMEM; + goto err_alloc; + } + xhci->name = pci->dev.name; + xhci->quirks = pci->id->driver_data; + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + bar_start = pci_bar_start ( pci, XHCI_BAR ); + bar_size = pci_bar_size ( pci, XHCI_BAR ); + xhci->regs = ioremap ( bar_start, bar_size ); + if ( ! xhci->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Initialise xHCI device */ + xhci_init ( xhci, xhci->regs ); + + /* Initialise USB legacy support and claim ownership */ + xhci_legacy_init ( xhci ); + xhci_legacy_claim ( xhci ); + + /* Fix Intel PCH-specific quirks, if applicable */ + if ( xhci->quirks & XHCI_PCH ) + xhci_pch_fix ( xhci, pci ); + + /* Reset device */ + if ( ( rc = xhci_reset ( xhci ) ) != 0 ) + goto err_reset; + + /* Allocate USB bus */ + xhci->bus = alloc_usb_bus ( &pci->dev, xhci->ports, XHCI_MTU, + &xhci_operations ); + if ( ! xhci->bus ) { + rc = -ENOMEM; + goto err_alloc_bus; + } + usb_bus_set_hostdata ( xhci->bus, xhci ); + usb_hub_set_drvdata ( xhci->bus->hub, xhci ); + + /* Set port protocols */ + for ( i = 1 ; i <= xhci->ports ; i++ ) { + port = usb_port ( xhci->bus->hub, i ); + port->protocol = xhci_port_protocol ( xhci, i ); + } + + /* Register USB bus */ + if ( ( rc = register_usb_bus ( xhci->bus ) ) != 0 ) + goto err_register; + + pci_set_drvdata ( pci, xhci ); + return 0; + + unregister_usb_bus ( xhci->bus ); + err_register: + free_usb_bus ( xhci->bus ); + err_alloc_bus: + xhci_reset ( xhci ); + err_reset: + if ( xhci->quirks & XHCI_PCH ) + xhci_pch_undo ( xhci, pci ); + xhci_legacy_release ( xhci ); + iounmap ( xhci->regs ); + err_ioremap: + free ( xhci ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void xhci_remove ( struct pci_device *pci ) { + struct xhci_device *xhci = pci_get_drvdata ( pci ); + struct usb_bus *bus = xhci->bus; + + unregister_usb_bus ( bus ); + free_usb_bus ( bus ); + xhci_reset ( xhci ); + if ( xhci->quirks & XHCI_PCH ) + xhci_pch_undo ( xhci, pci ); + xhci_legacy_release ( xhci ); + iounmap ( xhci->regs ); + free ( xhci ); +} + +/** XHCI PCI device IDs */ +static struct pci_device_id xhci_ids[] = { + PCI_ROM ( 0x8086, 0x9d2f, "xhci-skylake", "xHCI (Skylake)", ( XHCI_PCH | XHCI_BAD_PSIV ) ), + PCI_ROM ( 0x8086, 0xffff, "xhci-pch", "xHCI (Intel PCH)", XHCI_PCH ), + PCI_ROM ( 0xffff, 0xffff, "xhci", "xHCI", 0 ), +}; + +/** XHCI PCI driver */ +struct pci_driver xhci_driver __pci_driver = { + .ids = xhci_ids, + .id_count = ( sizeof ( xhci_ids ) / sizeof ( xhci_ids[0] ) ), + .class = PCI_CLASS_ID ( PCI_CLASS_SERIAL, PCI_CLASS_SERIAL_USB, + PCI_CLASS_SERIAL_USB_XHCI ), + .probe = xhci_probe, + .remove = xhci_remove, +}; + +/** + * Prepare for exit + * + * @v booting System is shutting down for OS boot + */ +static void xhci_shutdown ( int booting ) { + /* If we are shutting down to boot an OS, then prevent the + * release of ownership back to BIOS. + */ + xhci_legacy_prevent_release = booting; +} + +/** Startup/shutdown function */ +struct startup_fn xhci_startup __startup_fn ( STARTUP_LATE ) = { + .shutdown = xhci_shutdown, +}; diff --git a/qemu/roms/ipxe/src/drivers/usb/xhci.h b/qemu/roms/ipxe/src/drivers/usb/xhci.h new file mode 100644 index 000000000..83bf71e7e --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/usb/xhci.h @@ -0,0 +1,1150 @@ +#ifndef _IPXE_XHCI_H +#define _IPXE_XHCI_H + +/** @file + * + * USB eXtensible Host Controller Interface (xHCI) driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** Minimum alignment required for data structures + * + * With the exception of the scratchpad buffer pages (which are + * page-aligned), data structures used by xHCI generally require from + * 16 to 64 byte alignment and must not cross an (xHCI) page boundary. + * We simplify this requirement by aligning each structure on its own + * size, with a minimum of a 64 byte alignment. + */ +#define XHCI_MIN_ALIGN 64 + +/** Maximum transfer size */ +#define XHCI_MTU 65536 + +/** xHCI PCI BAR */ +#define XHCI_BAR PCI_BASE_ADDRESS_0 + +/** Capability register length */ +#define XHCI_CAP_CAPLENGTH 0x00 + +/** Host controller interface version number */ +#define XHCI_CAP_HCIVERSION 0x02 + +/** Structural parameters 1 */ +#define XHCI_CAP_HCSPARAMS1 0x04 + +/** Number of device slots */ +#define XHCI_HCSPARAMS1_SLOTS(params) ( ( (params) >> 0 ) & 0xff ) + +/** Number of interrupters */ +#define XHCI_HCSPARAMS1_INTRS(params) ( ( (params) >> 8 ) & 0x3ff ) + +/** Number of ports */ +#define XHCI_HCSPARAMS1_PORTS(params) ( ( (params) >> 24 ) & 0xff ) + +/** Structural parameters 2 */ +#define XHCI_CAP_HCSPARAMS2 0x08 + +/** Number of page-sized scratchpad buffers */ +#define XHCI_HCSPARAMS2_SCRATCHPADS(params) \ + ( ( ( (params) >> 16 ) & 0x3e0 ) | ( ( (params) >> 27 ) & 0x1f ) ) + +/** Capability parameters */ +#define XHCI_CAP_HCCPARAMS1 0x10 + +/** 64-bit addressing capability */ +#define XHCI_HCCPARAMS1_ADDR64(params) ( ( (params) >> 0 ) & 0x1 ) + +/** Context size shift */ +#define XHCI_HCCPARAMS1_CSZ_SHIFT(params) ( 5 + ( ( (params) >> 2 ) & 0x1 ) ) + +/** xHCI extended capabilities pointer */ +#define XHCI_HCCPARAMS1_XECP(params) ( ( ( (params) >> 16 ) & 0xffff ) << 2 ) + +/** Doorbell offset */ +#define XHCI_CAP_DBOFF 0x14 + +/** Runtime register space offset */ +#define XHCI_CAP_RTSOFF 0x18 + +/** xHCI extended capability ID */ +#define XHCI_XECP_ID(xecp) ( ( (xecp) >> 0 ) & 0xff ) + +/** Next xHCI extended capability pointer */ +#define XHCI_XECP_NEXT(xecp) ( ( ( (xecp) >> 8 ) & 0xff ) << 2 ) + +/** USB legacy support extended capability */ +#define XHCI_XECP_ID_LEGACY 1 + +/** USB legacy support BIOS owned semaphore */ +#define XHCI_USBLEGSUP_BIOS 0x02 + +/** USB legacy support BIOS ownership flag */ +#define XHCI_USBLEGSUP_BIOS_OWNED 0x01 + +/** USB legacy support OS owned semaphore */ +#define XHCI_USBLEGSUP_OS 0x03 + +/** USB legacy support OS ownership flag */ +#define XHCI_USBLEGSUP_OS_OWNED 0x01 + +/** USB legacy support control/status */ +#define XHCI_USBLEGSUP_CTLSTS 0x04 + +/** Supported protocol extended capability */ +#define XHCI_XECP_ID_SUPPORTED 2 + +/** Supported protocol revision */ +#define XHCI_SUPPORTED_REVISION 0x00 + +/** Supported protocol minor revision */ +#define XHCI_SUPPORTED_REVISION_VER(revision) ( ( (revision) >> 16 ) & 0xffff ) + +/** Supported protocol name */ +#define XHCI_SUPPORTED_NAME 0x04 + +/** Supported protocol ports */ +#define XHCI_SUPPORTED_PORTS 0x08 + +/** Supported protocol port offset */ +#define XHCI_SUPPORTED_PORTS_OFFSET(ports) ( ( (ports) >> 0 ) & 0xff ) + +/** Supported protocol port count */ +#define XHCI_SUPPORTED_PORTS_COUNT(ports) ( ( (ports) >> 8 ) & 0xff ) + +/** Supported protocol PSI count */ +#define XHCI_SUPPORTED_PORTS_PSIC(ports) ( ( (ports) >> 28 ) & 0x0f ) + +/** Supported protocol slot */ +#define XHCI_SUPPORTED_SLOT 0x0c + +/** Supported protocol slot type */ +#define XHCI_SUPPORTED_SLOT_TYPE(slot) ( ( (slot) >> 0 ) & 0x1f ) + +/** Supported protocol PSI */ +#define XHCI_SUPPORTED_PSI(index) ( 0x10 + ( (index) * 4 ) ) + +/** Supported protocol PSI value */ +#define XHCI_SUPPORTED_PSI_VALUE(psi) ( ( (psi) >> 0 ) & 0x0f ) + +/** Supported protocol PSI mantissa */ +#define XHCI_SUPPORTED_PSI_MANTISSA(psi) ( ( (psi) >> 16 ) & 0xffff ) + +/** Supported protocol PSI exponent */ +#define XHCI_SUPPORTED_PSI_EXPONENT(psi) ( ( (psi) >> 4 ) & 0x03 ) + +/** Default PSI values */ +enum xhci_default_psi_value { + /** Full speed (12Mbps) */ + XHCI_SPEED_FULL = 1, + /** Low speed (1.5Mbps) */ + XHCI_SPEED_LOW = 2, + /** High speed (480Mbps) */ + XHCI_SPEED_HIGH = 3, + /** Super speed */ + XHCI_SPEED_SUPER = 4, +}; + +/** USB command register */ +#define XHCI_OP_USBCMD 0x00 + +/** Run/stop */ +#define XHCI_USBCMD_RUN 0x00000001UL + +/** Host controller reset */ +#define XHCI_USBCMD_HCRST 0x00000002UL + +/** USB status register */ +#define XHCI_OP_USBSTS 0x04 + +/** Host controller halted */ +#define XHCI_USBSTS_HCH 0x00000001UL + +/** Page size register */ +#define XHCI_OP_PAGESIZE 0x08 + +/** Page size */ +#define XHCI_PAGESIZE(pagesize) ( (pagesize) << 12 ) + +/** Device notifcation control register */ +#define XHCI_OP_DNCTRL 0x14 + +/** Command ring control register */ +#define XHCI_OP_CRCR 0x18 + +/** Command ring cycle state */ +#define XHCI_CRCR_RCS 0x00000001UL + +/** Command abort */ +#define XHCI_CRCR_CA 0x00000004UL + +/** Command ring running */ +#define XHCI_CRCR_CRR 0x00000008UL + +/** Device context base address array pointer */ +#define XHCI_OP_DCBAAP 0x30 + +/** Configure register */ +#define XHCI_OP_CONFIG 0x38 + +/** Maximum device slots enabled */ +#define XHCI_CONFIG_MAX_SLOTS_EN(slots) ( (slots) << 0 ) + +/** Maximum device slots enabled mask */ +#define XHCI_CONFIG_MAX_SLOTS_EN_MASK \ + XHCI_CONFIG_MAX_SLOTS_EN ( 0xff ) + +/** Port status and control register */ +#define XHCI_OP_PORTSC(port) ( 0x400 - 0x10 + ( (port) << 4 ) ) + +/** Current connect status */ +#define XHCI_PORTSC_CCS 0x00000001UL + +/** Port enabled */ +#define XHCI_PORTSC_PED 0x00000002UL + +/** Port reset */ +#define XHCI_PORTSC_PR 0x00000010UL + +/** Port link state */ +#define XHCI_PORTSC_PLS(pls) ( (pls) << 5 ) + +/** Disabled port link state */ +#define XHCI_PORTSC_PLS_DISABLED XHCI_PORTSC_PLS ( 4 ) + +/** RxDetect port link state */ +#define XHCI_PORTSC_PLS_RXDETECT XHCI_PORTSC_PLS ( 5 ) + +/** Port link state mask */ +#define XHCI_PORTSC_PLS_MASK XHCI_PORTSC_PLS ( 0xf ) + +/** Port power */ +#define XHCI_PORTSC_PP 0x00000200UL + +/** Time to delay after enabling power to a port */ +#define XHCI_PORT_POWER_DELAY_MS 20 + +/** Port speed ID value */ +#define XHCI_PORTSC_PSIV(portsc) ( ( (portsc) >> 10 ) & 0xf ) + +/** Port indicator control */ +#define XHCI_PORTSC_PIC(indicators) ( (indicators) << 14 ) + +/** Port indicator control mask */ +#define XHCI_PORTSC_PIC_MASK XHCI_PORTSC_PIC ( 3 ) + +/** Port link state write strobe */ +#define XHCI_PORTSC_LWS 0x00010000UL + +/** Time to delay after writing the port link state */ +#define XHCI_LINK_STATE_DELAY_MS 20 + +/** Connect status change */ +#define XHCI_PORTSC_CSC 0x00020000UL + +/** Port enabled/disabled change */ +#define XHCI_PORTSC_PEC 0x00040000UL + +/** Warm port reset change */ +#define XHCI_PORTSC_WRC 0x00080000UL + +/** Over-current change */ +#define XHCI_PORTSC_OCC 0x00100000UL + +/** Port reset change */ +#define XHCI_PORTSC_PRC 0x00200000UL + +/** Port link state change */ +#define XHCI_PORTSC_PLC 0x00400000UL + +/** Port config error change */ +#define XHCI_PORTSC_CEC 0x00800000UL + +/** Port status change mask */ +#define XHCI_PORTSC_CHANGE \ + ( XHCI_PORTSC_CSC | XHCI_PORTSC_PEC | XHCI_PORTSC_WRC | \ + XHCI_PORTSC_OCC | XHCI_PORTSC_PRC | XHCI_PORTSC_PLC | \ + XHCI_PORTSC_CEC ) + +/** Port status and control bits which should be preserved + * + * The port status and control register is a horrendous mix of + * differing semantics. Some bits are written to only when a separate + * write strobe bit is set. Some bits should be preserved when + * modifying other bits. Some bits will be cleared if written back as + * a one. Most excitingly, the "port enabled" bit has the semantics + * that 1=enabled, 0=disabled, yet writing a 1 will disable the port. + */ +#define XHCI_PORTSC_PRESERVE ( XHCI_PORTSC_PP | XHCI_PORTSC_PIC_MASK ) + +/** Port power management status and control register */ +#define XHCI_OP_PORTPMSC(port) ( 0x404 - 0x10 + ( (port) << 4 ) ) + +/** Port link info register */ +#define XHCI_OP_PORTLI(port) ( 0x408 - 0x10 + ( (port) << 4 ) ) + +/** Port hardware link power management control register */ +#define XHCI_OP_PORTHLPMC(port) ( 0x40c - 0x10 + ( (port) << 4 ) ) + +/** Event ring segment table size register */ +#define XHCI_RUN_ERSTSZ(intr) ( 0x28 + ( (intr) << 5 ) ) + +/** Event ring segment table base address register */ +#define XHCI_RUN_ERSTBA(intr) ( 0x30 + ( (intr) << 5 ) ) + +/** Event ring dequeue pointer register */ +#define XHCI_RUN_ERDP(intr) ( 0x38 + ( (intr) << 5 ) ) + +/** A transfer request block template */ +struct xhci_trb_template { + /** Parameter */ + uint64_t parameter; + /** Status */ + uint32_t status; + /** Control */ + uint32_t control; +}; + +/** A transfer request block */ +struct xhci_trb_common { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint32_t reserved_b; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint16_t reserved_c; +} __attribute__ (( packed )); + +/** Transfer request block cycle bit flag */ +#define XHCI_TRB_C 0x01 + +/** Transfer request block toggle cycle bit flag */ +#define XHCI_TRB_TC 0x02 + +/** Transfer request block chain flag */ +#define XHCI_TRB_CH 0x10 + +/** Transfer request block interrupt on completion flag */ +#define XHCI_TRB_IOC 0x20 + +/** Transfer request block immediate data flag */ +#define XHCI_TRB_IDT 0x40 + +/** Transfer request block type */ +#define XHCI_TRB_TYPE(type) ( (type) << 2 ) + +/** Transfer request block type mask */ +#define XHCI_TRB_TYPE_MASK XHCI_TRB_TYPE ( 0x3f ) + +/** A normal transfer request block */ +struct xhci_trb_normal { + /** Data buffer */ + uint64_t data; + /** Length */ + uint32_t len; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint16_t reserved; +} __attribute__ (( packed )); + +/** A normal transfer request block */ +#define XHCI_TRB_NORMAL XHCI_TRB_TYPE ( 1 ) + +/** Construct TD size field */ +#define XHCI_TD_SIZE(remaining) \ + ( ( ( (remaining) <= 0xf ) ? remaining : 0xf ) << 17 ) + +/** A setup stage transfer request block */ +struct xhci_trb_setup { + /** Setup packet */ + struct usb_setup_packet packet; + /** Length */ + uint32_t len; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Transfer direction */ + uint8_t direction; + /** Reserved */ + uint8_t reserved; +} __attribute__ (( packed )); + +/** A setup stage transfer request block */ +#define XHCI_TRB_SETUP XHCI_TRB_TYPE ( 2 ) + +/** Setup stage input data direction */ +#define XHCI_SETUP_IN 3 + +/** Setup stage output data direction */ +#define XHCI_SETUP_OUT 2 + +/** A data stage transfer request block */ +struct xhci_trb_data { + /** Data buffer */ + uint64_t data; + /** Length */ + uint32_t len; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Transfer direction */ + uint8_t direction; + /** Reserved */ + uint8_t reserved; +} __attribute__ (( packed )); + +/** A data stage transfer request block */ +#define XHCI_TRB_DATA XHCI_TRB_TYPE ( 3 ) + +/** Input data direction */ +#define XHCI_DATA_IN 0x01 + +/** Output data direction */ +#define XHCI_DATA_OUT 0x00 + +/** A status stage transfer request block */ +struct xhci_trb_status { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint32_t reserved_b; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Direction */ + uint8_t direction; + /** Reserved */ + uint8_t reserved_c; +} __attribute__ (( packed )); + +/** A status stage transfer request block */ +#define XHCI_TRB_STATUS XHCI_TRB_TYPE ( 4 ) + +/** Input status direction */ +#define XHCI_STATUS_IN 0x01 + +/** Output status direction */ +#define XHCI_STATUS_OUT 0x00 + +/** A link transfer request block */ +struct xhci_trb_link { + /** Next ring segment */ + uint64_t next; + /** Reserved */ + uint32_t reserved_a; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint16_t reserved_c; +} __attribute__ (( packed )); + +/** A link transfer request block */ +#define XHCI_TRB_LINK XHCI_TRB_TYPE ( 6 ) + +/** A no-op transfer request block */ +#define XHCI_TRB_NOP XHCI_TRB_TYPE ( 8 ) + +/** An enable slot transfer request block */ +struct xhci_trb_enable_slot { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint32_t reserved_b; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Slot type */ + uint8_t slot; + /** Reserved */ + uint8_t reserved_c; +} __attribute__ (( packed )); + +/** An enable slot transfer request block */ +#define XHCI_TRB_ENABLE_SLOT XHCI_TRB_TYPE ( 9 ) + +/** A disable slot transfer request block */ +struct xhci_trb_disable_slot { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint32_t reserved_b; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint8_t reserved_c; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** A disable slot transfer request block */ +#define XHCI_TRB_DISABLE_SLOT XHCI_TRB_TYPE ( 10 ) + +/** A context transfer request block */ +struct xhci_trb_context { + /** Input context */ + uint64_t input; + /** Reserved */ + uint32_t reserved_a; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint8_t reserved_b; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** An address device transfer request block */ +#define XHCI_TRB_ADDRESS_DEVICE XHCI_TRB_TYPE ( 11 ) + +/** A configure endpoint transfer request block */ +#define XHCI_TRB_CONFIGURE_ENDPOINT XHCI_TRB_TYPE ( 12 ) + +/** An evaluate context transfer request block */ +#define XHCI_TRB_EVALUATE_CONTEXT XHCI_TRB_TYPE ( 13 ) + +/** A reset endpoint transfer request block */ +struct xhci_trb_reset_endpoint { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint32_t reserved_b; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Endpoint ID */ + uint8_t endpoint; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** A reset endpoint transfer request block */ +#define XHCI_TRB_RESET_ENDPOINT XHCI_TRB_TYPE ( 14 ) + +/** A stop endpoint transfer request block */ +struct xhci_trb_stop_endpoint { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint32_t reserved_b; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Endpoint ID */ + uint8_t endpoint; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** A stop endpoint transfer request block */ +#define XHCI_TRB_STOP_ENDPOINT XHCI_TRB_TYPE ( 15 ) + +/** A set transfer ring dequeue pointer transfer request block */ +struct xhci_trb_set_tr_dequeue_pointer { + /** Dequeue pointer */ + uint64_t dequeue; + /** Reserved */ + uint32_t reserved; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Endpoint ID */ + uint8_t endpoint; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** A set transfer ring dequeue pointer transfer request block */ +#define XHCI_TRB_SET_TR_DEQUEUE_POINTER XHCI_TRB_TYPE ( 16 ) + +/** A no-op command transfer request block */ +#define XHCI_TRB_NOP_CMD XHCI_TRB_TYPE ( 23 ) + +/** A transfer event transfer request block */ +struct xhci_trb_transfer { + /** Transfer TRB pointer */ + uint64_t transfer; + /** Residual transfer length */ + uint16_t residual; + /** Reserved */ + uint8_t reserved; + /** Completion code */ + uint8_t code; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Endpoint ID */ + uint8_t endpoint; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** A transfer event transfer request block */ +#define XHCI_TRB_TRANSFER XHCI_TRB_TYPE ( 32 ) + +/** A command completion event transfer request block */ +struct xhci_trb_complete { + /** Command TRB pointer */ + uint64_t command; + /** Parameter */ + uint8_t parameter[3]; + /** Completion code */ + uint8_t code; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Virtual function ID */ + uint8_t vf; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** A command completion event transfer request block */ +#define XHCI_TRB_COMPLETE XHCI_TRB_TYPE ( 33 ) + +/** xHCI completion codes */ +enum xhci_completion_code { + /** Success */ + XHCI_CMPLT_SUCCESS = 1, + /** Short packet */ + XHCI_CMPLT_SHORT = 13, + /** Command ring stopped */ + XHCI_CMPLT_CMD_STOPPED = 24, +}; + +/** A port status change transfer request block */ +struct xhci_trb_port_status { + /** Reserved */ + uint8_t reserved_a[3]; + /** Port ID */ + uint8_t port; + /** Reserved */ + uint8_t reserved_b[7]; + /** Completion code */ + uint8_t code; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint16_t reserved_c; +} __attribute__ (( packed )); + +/** A port status change transfer request block */ +#define XHCI_TRB_PORT_STATUS XHCI_TRB_TYPE ( 34 ) + +/** A port status change transfer request block */ +struct xhci_trb_host_controller { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint8_t reserved_b[3]; + /** Completion code */ + uint8_t code; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint16_t reserved_c; +} __attribute__ (( packed )); + +/** A port status change transfer request block */ +#define XHCI_TRB_HOST_CONTROLLER XHCI_TRB_TYPE ( 37 ) + +/** A transfer request block */ +union xhci_trb { + /** Template */ + struct xhci_trb_template template; + /** Common fields */ + struct xhci_trb_common common; + /** Normal TRB */ + struct xhci_trb_normal normal; + /** Setup stage TRB */ + struct xhci_trb_setup setup; + /** Data stage TRB */ + struct xhci_trb_data data; + /** Status stage TRB */ + struct xhci_trb_status status; + /** Link TRB */ + struct xhci_trb_link link; + /** Enable slot TRB */ + struct xhci_trb_enable_slot enable; + /** Disable slot TRB */ + struct xhci_trb_disable_slot disable; + /** Input context TRB */ + struct xhci_trb_context context; + /** Reset endpoint TRB */ + struct xhci_trb_reset_endpoint reset; + /** Stop endpoint TRB */ + struct xhci_trb_stop_endpoint stop; + /** Set transfer ring dequeue pointer TRB */ + struct xhci_trb_set_tr_dequeue_pointer dequeue; + /** Transfer event */ + struct xhci_trb_transfer transfer; + /** Command completion event */ + struct xhci_trb_complete complete; + /** Port status changed event */ + struct xhci_trb_port_status port; + /** Host controller event */ + struct xhci_trb_host_controller host; +} __attribute__ (( packed )); + +/** An input control context */ +struct xhci_control_context { + /** Drop context flags */ + uint32_t drop; + /** Add context flags */ + uint32_t add; + /** Reserved */ + uint32_t reserved_a[5]; + /** Configuration value */ + uint8_t config; + /** Interface number */ + uint8_t intf; + /** Alternate setting */ + uint8_t alt; + /** Reserved */ + uint8_t reserved_b; +} __attribute__ (( packed )); + +/** A slot context */ +struct xhci_slot_context { + /** Device info */ + uint32_t info; + /** Maximum exit latency */ + uint16_t latency; + /** Root hub port number */ + uint8_t port; + /** Number of downstream ports */ + uint8_t ports; + /** TT hub slot ID */ + uint8_t tt_id; + /** TT port number */ + uint8_t tt_port; + /** Interrupter target */ + uint16_t intr; + /** USB address */ + uint8_t address; + /** Reserved */ + uint16_t reserved_a; + /** Slot state */ + uint8_t state; + /** Reserved */ + uint32_t reserved_b[4]; +} __attribute__ (( packed )); + +/** Construct slot context device info */ +#define XHCI_SLOT_INFO( entries, hub, speed, route ) \ + ( ( (entries) << 27 ) | ( (hub) << 26 ) | ( (speed) << 20 ) | (route) ) + +/** An endpoint context */ +struct xhci_endpoint_context { + /** Endpoint state */ + uint8_t state; + /** Stream configuration */ + uint8_t stream; + /** Polling interval */ + uint8_t interval; + /** Max ESIT payload high */ + uint8_t esit_high; + /** Endpoint type */ + uint8_t type; + /** Maximum burst size */ + uint8_t burst; + /** Maximum packet size */ + uint16_t mtu; + /** Transfer ring dequeue pointer */ + uint64_t dequeue; + /** Average TRB length */ + uint16_t trb_len; + /** Max ESIT payload low */ + uint16_t esit_low; + /** Reserved */ + uint32_t reserved[3]; +} __attribute__ (( packed )); + +/** Endpoint states */ +enum xhci_endpoint_state { + /** Endpoint is disabled */ + XHCI_ENDPOINT_DISABLED = 0, + /** Endpoint is running */ + XHCI_ENDPOINT_RUNNING = 1, + /** Endpoint is halted due to a USB Halt condition */ + XHCI_ENDPOINT_HALTED = 2, + /** Endpoint is stopped */ + XHCI_ENDPOINT_STOPPED = 3, + /** Endpoint is halted due to a TRB error */ + XHCI_ENDPOINT_ERROR = 4, +}; + +/** Endpoint state mask */ +#define XHCI_ENDPOINT_STATE_MASK 0x07 + +/** Endpoint type */ +#define XHCI_EP_TYPE(type) ( (type) << 3 ) + +/** Control endpoint type */ +#define XHCI_EP_TYPE_CONTROL XHCI_EP_TYPE ( 4 ) + +/** Input endpoint type */ +#define XHCI_EP_TYPE_IN XHCI_EP_TYPE ( 4 ) + +/** Periodic endpoint type */ +#define XHCI_EP_TYPE_PERIODIC XHCI_EP_TYPE ( 1 ) + +/** Endpoint dequeue cycle state */ +#define XHCI_EP_DCS 0x00000001UL + +/** Control endpoint average TRB length */ +#define XHCI_EP0_TRB_LEN 8 + +/** An event ring segment */ +struct xhci_event_ring_segment { + /** Base address */ + uint64_t base; + /** Number of TRBs */ + uint32_t count; + /** Reserved */ + uint32_t reserved; +} __attribute__ (( packed )); + +/** A transfer request block command/transfer ring */ +struct xhci_trb_ring { + /** Producer counter */ + unsigned int prod; + /** Consumer counter */ + unsigned int cons; + /** Ring size (log2) */ + unsigned int shift; + /** Ring counter mask */ + unsigned int mask; + + /** I/O buffers */ + struct io_buffer **iobuf; + + /** Transfer request blocks */ + union xhci_trb *trb; + /** Length of transfer request blocks */ + size_t len; + /** Link TRB (if applicable) */ + struct xhci_trb_link *link; + + /** Doorbell register */ + void *db; + /** Doorbell register value */ + uint32_t dbval; +}; + +/** An event ring */ +struct xhci_event_ring { + /** Consumer counter */ + unsigned int cons; + /** Event ring segment table */ + struct xhci_event_ring_segment *segment; + /** Transfer request blocks */ + union xhci_trb *trb; +}; + +/** + * Calculate doorbell register value + * + * @v target Doorbell target + * @v stream Doorbell stream ID + * @ret dbval Doorbell register value + */ +#define XHCI_DBVAL( target, stream ) ( (target) | ( (stream) << 16 ) ) + +/** + * Calculate space used in TRB ring + * + * @v ring TRB ring + * @ret fill Number of entries used + */ +static inline __attribute__ (( always_inline )) unsigned int +xhci_ring_fill ( struct xhci_trb_ring *ring ) { + + return ( ring->prod - ring->cons ); +} + +/** + * Calculate space remaining in TRB ring + * + * @v ring TRB ring + * @ret remaining Number of entries remaining + * + * xHCI does not allow us to completely fill a ring; there must be at + * least one free entry (excluding the Link TRB). + */ +static inline __attribute__ (( always_inline )) unsigned int +xhci_ring_remaining ( struct xhci_trb_ring *ring ) { + unsigned int fill = xhci_ring_fill ( ring ); + + /* We choose to utilise rings with ( 2^n + 1 ) entries, with + * the final entry being a Link TRB. The maximum fill level + * is therefore + * + * ( ( 2^n + 1 ) - 1 (Link TRB) - 1 (one slot always empty) + * == ( 2^n - 1 ) + * + * which is therefore equal to the ring mask. + */ + assert ( fill <= ring->mask ); + return ( ring->mask - fill ); +} + +/** + * Calculate physical address of most recently consumed TRB + * + * @v ring TRB ring + * @ret trb TRB physical address + */ +static inline __attribute__ (( always_inline )) physaddr_t +xhci_ring_consumed ( struct xhci_trb_ring *ring ) { + unsigned int index = ( ( ring->cons - 1 ) & ring->mask ); + + return virt_to_phys ( &ring->trb[index] ); +} + +/** Slot context index */ +#define XHCI_CTX_SLOT 0 + +/** Calculate context index from USB endpoint address */ +#define XHCI_CTX(address) \ + ( (address) ? ( ( ( (address) & 0x0f ) << 1 ) | \ + ( ( (address) & 0x80 ) >> 7 ) ) : 1 ) + +/** Endpoint zero context index */ +#define XHCI_CTX_EP0 XHCI_CTX ( 0x00 ) + +/** End of contexts */ +#define XHCI_CTX_END 32 + +/** Device context index */ +#define XHCI_DCI(ctx) ( (ctx) + 0 ) + +/** Input context index */ +#define XHCI_ICI(ctx) ( (ctx) + 1 ) + +/** Number of TRBs (excluding Link TRB) in the command ring + * + * This is a policy decision. + */ +#define XHCI_CMD_TRBS_LOG2 2 + +/** Number of TRBs in the event ring + * + * This is a policy decision. + */ +#define XHCI_EVENT_TRBS_LOG2 6 + +/** Number of TRBs in a transfer ring + * + * This is a policy decision. + */ +#define XHCI_TRANSFER_TRBS_LOG2 6 + +/** Maximum time to wait for BIOS to release ownership + * + * This is a policy decision. + */ +#define XHCI_USBLEGSUP_MAX_WAIT_MS 100 + +/** Maximum time to wait for host controller to stop + * + * This is a policy decision. + */ +#define XHCI_STOP_MAX_WAIT_MS 100 + +/** Maximum time to wait for reset to complete + * + * This is a policy decision. + */ +#define XHCI_RESET_MAX_WAIT_MS 500 + +/** Maximum time to wait for a command to complete + * + * The "address device" command involves waiting for a response to a + * USB control transaction, and so we must wait for up to the 5000ms + * that USB allows for devices to respond to control transactions. + */ +#define XHCI_COMMAND_MAX_WAIT_MS USB_CONTROL_MAX_WAIT_MS + +/** Time to delay after aborting a command + * + * This is a policy decision + */ +#define XHCI_COMMAND_ABORT_DELAY_MS 500 + +/** Maximum time to wait for a port reset to complete + * + * This is a policy decision. + */ +#define XHCI_PORT_RESET_MAX_WAIT_MS 500 + +/** Intel PCH quirk */ +struct xhci_pch { + /** USB2 port routing register original value */ + uint32_t xusb2pr; + /** USB3 port SuperSpeed enable register original value */ + uint32_t usb3pssen; +}; + +/** Intel PCH quirk flag */ +#define XHCI_PCH 0x0001 + +/** Intel PCH USB2 port routing register */ +#define XHCI_PCH_XUSB2PR 0xd0 + +/** Intel PCH USB2 port routing mask register */ +#define XHCI_PCH_XUSB2PRM 0xd4 + +/** Intel PCH SuperSpeed enable register */ +#define XHCI_PCH_USB3PSSEN 0xd8 + +/** Intel PCH USB3 port routing mask register */ +#define XHCI_PCH_USB3PRM 0xdc + +/** Invalid protocol speed ID values quirk */ +#define XHCI_BAD_PSIV 0x0002 + +/** An xHCI device */ +struct xhci_device { + /** Registers */ + void *regs; + /** Name */ + const char *name; + /** Quirks */ + unsigned int quirks; + + /** Capability registers */ + void *cap; + /** Operational registers */ + void *op; + /** Runtime registers */ + void *run; + /** Doorbell registers */ + void *db; + + /** Number of device slots */ + unsigned int slots; + /** Number of interrupters */ + unsigned int intrs; + /** Number of ports */ + unsigned int ports; + + /** Number of page-sized scratchpad buffers */ + unsigned int scratchpads; + + /** 64-bit addressing capability */ + int addr64; + /** Context size shift */ + unsigned int csz_shift; + /** xHCI extended capabilities offset */ + unsigned int xecp; + + /** Page size */ + size_t pagesize; + + /** USB legacy support capability (if present and enabled) */ + unsigned int legacy; + + /** Device context base address array */ + uint64_t *dcbaa; + + /** Scratchpad buffer area */ + userptr_t scratchpad; + /** Scratchpad buffer array */ + uint64_t *scratchpad_array; + + /** Command ring */ + struct xhci_trb_ring command; + /** Event ring */ + struct xhci_event_ring event; + /** Current command (if any) */ + union xhci_trb *pending; + + /** Device slots, indexed by slot ID */ + struct xhci_slot **slot; + + /** USB bus */ + struct usb_bus *bus; + + /** Intel PCH quirk */ + struct xhci_pch pch; +}; + +/** An xHCI device slot */ +struct xhci_slot { + /** xHCI device */ + struct xhci_device *xhci; + /** USB device */ + struct usb_device *usb; + /** Slot ID */ + unsigned int id; + /** Slot context */ + struct xhci_slot_context *context; + /** Route string */ + unsigned int route; + /** Root hub port number */ + unsigned int port; + /** Protocol speed ID */ + unsigned int psiv; + /** Number of ports (if this device is a hub) */ + unsigned int ports; + /** Transaction translator slot ID */ + unsigned int tt_id; + /** Transaction translator port */ + unsigned int tt_port; + /** Endpoints, indexed by context ID */ + struct xhci_endpoint *endpoint[XHCI_CTX_END]; +}; + +/** An xHCI endpoint */ +struct xhci_endpoint { + /** xHCI device */ + struct xhci_device *xhci; + /** xHCI slot */ + struct xhci_slot *slot; + /** USB endpoint */ + struct usb_endpoint *ep; + /** Context index */ + unsigned int ctx; + /** Endpoint type */ + unsigned int type; + /** Endpoint interval */ + unsigned int interval; + /** Endpoint context */ + struct xhci_endpoint_context *context; + /** Transfer ring */ + struct xhci_trb_ring ring; +}; + +#endif /* _IPXE_XHCI_H */ diff --git a/qemu/roms/ipxe/src/hci/commands/autoboot_cmd.c b/qemu/roms/ipxe/src/hci/commands/autoboot_cmd.c index 62235a278..56f39a1ce 100644 --- a/qemu/roms/ipxe/src/hci/commands/autoboot_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/autoboot_cmd.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #include @@ -25,7 +29,7 @@ #include #include -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/hci/commands/config_cmd.c b/qemu/roms/ipxe/src/hci/commands/config_cmd.c index b81c866ff..ad415e045 100644 --- a/qemu/roms/ipxe/src/hci/commands/config_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/config_cmd.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #include @@ -26,7 +30,7 @@ #include #include -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/hci/commands/console_cmd.c b/qemu/roms/ipxe/src/hci/commands/console_cmd.c index d2eae59f0..ba472b9f6 100644 --- a/qemu/roms/ipxe/src/hci/commands/console_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/console_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/hci/commands/dhcp_cmd.c b/qemu/roms/ipxe/src/hci/commands/dhcp_cmd.c index feeb55ee5..45a922b51 100644 --- a/qemu/roms/ipxe/src/hci/commands/dhcp_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/dhcp_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/hci/commands/fcmgmt_cmd.c b/qemu/roms/ipxe/src/hci/commands/fcmgmt_cmd.c index 1c199b5dc..97f10f4dd 100644 --- a/qemu/roms/ipxe/src/hci/commands/fcmgmt_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/fcmgmt_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/hci/commands/gdbstub_cmd.c b/qemu/roms/ipxe/src/hci/commands/gdbstub_cmd.c index 33890aebc..c4a831e7a 100644 --- a/qemu/roms/ipxe/src/hci/commands/gdbstub_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/gdbstub_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/hci/commands/ifmgmt_cmd.c b/qemu/roms/ipxe/src/hci/commands/ifmgmt_cmd.c index 5307c9423..c89af2e81 100644 --- a/qemu/roms/ipxe/src/hci/commands/ifmgmt_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/ifmgmt_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/hci/commands/image_cmd.c b/qemu/roms/ipxe/src/hci/commands/image_cmd.c index a9e831bf5..4a7c500a4 100644 --- a/qemu/roms/ipxe/src/hci/commands/image_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/image_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/hci/commands/image_trust_cmd.c b/qemu/roms/ipxe/src/hci/commands/image_trust_cmd.c index ca59a858a..f9d6b5b3e 100644 --- a/qemu/roms/ipxe/src/hci/commands/image_trust_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/image_trust_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -169,6 +173,9 @@ struct command image_trust_commands[] __command = { }, }; +/* Drag in objects via command list */ +REQUIRING_SYMBOL ( image_trust_commands ); + /* Drag in objects typically required for signature verification */ REQUIRE_OBJECT ( rsa ); REQUIRE_OBJECT ( md5 ); diff --git a/qemu/roms/ipxe/src/hci/commands/ipstat_cmd.c b/qemu/roms/ipxe/src/hci/commands/ipstat_cmd.c index d565dc0ae..763e4dfd6 100644 --- a/qemu/roms/ipxe/src/hci/commands/ipstat_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/ipstat_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/hci/commands/login_cmd.c b/qemu/roms/ipxe/src/hci/commands/login_cmd.c index f5db427d5..c9e196437 100644 --- a/qemu/roms/ipxe/src/hci/commands/login_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/login_cmd.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #include @@ -23,7 +27,7 @@ #include #include -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/hci/commands/lotest_cmd.c b/qemu/roms/ipxe/src/hci/commands/lotest_cmd.c index 0fa031bcb..a989932d4 100644 --- a/qemu/roms/ipxe/src/hci/commands/lotest_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/lotest_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/hci/commands/menu_cmd.c b/qemu/roms/ipxe/src/hci/commands/menu_cmd.c index 66a6262e6..76bce8695 100644 --- a/qemu/roms/ipxe/src/hci/commands/menu_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/menu_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/hci/commands/neighbour_cmd.c b/qemu/roms/ipxe/src/hci/commands/neighbour_cmd.c index a1e052439..816e87357 100644 --- a/qemu/roms/ipxe/src/hci/commands/neighbour_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/neighbour_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/hci/commands/nvo_cmd.c b/qemu/roms/ipxe/src/hci/commands/nvo_cmd.c index e63dab08e..ac0d60651 100644 --- a/qemu/roms/ipxe/src/hci/commands/nvo_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/nvo_cmd.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #include @@ -29,7 +33,7 @@ #include #include -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/hci/commands/param_cmd.c b/qemu/roms/ipxe/src/hci/commands/param_cmd.c index 6cf096d00..bff04f2ff 100644 --- a/qemu/roms/ipxe/src/hci/commands/param_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/param_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/hci/commands/pci_cmd.c b/qemu/roms/ipxe/src/hci/commands/pci_cmd.c index f5145fb35..a2a811aa0 100644 --- a/qemu/roms/ipxe/src/hci/commands/pci_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/pci_cmd.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #include @@ -23,7 +27,7 @@ #include #include -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/hci/commands/ping_cmd.c b/qemu/roms/ipxe/src/hci/commands/ping_cmd.c index 34807696f..ab271e75a 100644 --- a/qemu/roms/ipxe/src/hci/commands/ping_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/ping_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/hci/commands/poweroff_cmd.c b/qemu/roms/ipxe/src/hci/commands/poweroff_cmd.c index 9d487d330..afdf12dde 100644 --- a/qemu/roms/ipxe/src/hci/commands/poweroff_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/poweroff_cmd.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #include @@ -24,7 +28,7 @@ #include #include -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/hci/commands/profstat_cmd.c b/qemu/roms/ipxe/src/hci/commands/profstat_cmd.c index e4c9e5a24..dc6f649e3 100644 --- a/qemu/roms/ipxe/src/hci/commands/profstat_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/profstat_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/hci/commands/reboot_cmd.c b/qemu/roms/ipxe/src/hci/commands/reboot_cmd.c index 485939e42..45d54cc2c 100644 --- a/qemu/roms/ipxe/src/hci/commands/reboot_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/reboot_cmd.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #include @@ -22,7 +26,7 @@ #include #include -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/hci/commands/route_cmd.c b/qemu/roms/ipxe/src/hci/commands/route_cmd.c index cc5ffc2f2..8aa535363 100644 --- a/qemu/roms/ipxe/src/hci/commands/route_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/route_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/hci/commands/sanboot_cmd.c b/qemu/roms/ipxe/src/hci/commands/sanboot_cmd.c index 5954b6326..24ec8bc4e 100644 --- a/qemu/roms/ipxe/src/hci/commands/sanboot_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/sanboot_cmd.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #include @@ -27,7 +31,7 @@ #include #include -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/hci/commands/sync_cmd.c b/qemu/roms/ipxe/src/hci/commands/sync_cmd.c index adf7e3cc6..54799d422 100644 --- a/qemu/roms/ipxe/src/hci/commands/sync_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/sync_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/hci/commands/vlan_cmd.c b/qemu/roms/ipxe/src/hci/commands/vlan_cmd.c index 5d7298220..8a2f0c749 100644 --- a/qemu/roms/ipxe/src/hci/commands/vlan_cmd.c +++ b/qemu/roms/ipxe/src/hci/commands/vlan_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/hci/editstring.c b/qemu/roms/ipxe/src/hci/editstring.c index 5f6f04d51..8cbce0767 100644 --- a/qemu/roms/ipxe/src/hci/editstring.c +++ b/qemu/roms/ipxe/src/hci/editstring.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/hci/jumpscroll.c b/qemu/roms/ipxe/src/hci/jumpscroll.c new file mode 100644 index 000000000..dd6bcac2b --- /dev/null +++ b/qemu/roms/ipxe/src/hci/jumpscroll.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * Jump scrolling + * + */ + +#include +#include +#include + +/** + * Handle keypress + * + * @v scroll Jump scroller + * @v key Key pressed by user + * @ret move Scroller movement, or zero + */ +int jump_scroll_key ( struct jump_scroller *scroll, int key ) { + + /* Sanity checks */ + assert ( scroll->rows != 0 ); + assert ( scroll->count != 0 ); + assert ( scroll->current < scroll->count ); + assert ( scroll->first < scroll->count ); + assert ( scroll->first <= scroll->current ); + assert ( scroll->current < ( scroll->first + scroll->rows ) ); + + /* Handle key, if applicable */ + switch ( key ) { + case KEY_UP: + return -1; + case KEY_DOWN: + return +1; + case KEY_PPAGE: + return ( scroll->first - scroll->current - 1 ); + case KEY_NPAGE: + return ( scroll->first - scroll->current + scroll->rows ); + case KEY_HOME: + return -( scroll->count ); + case KEY_END: + return +( scroll->count ); + default: + return 0; + } +} + +/** + * Move scroller + * + * @v scroll Jump scroller + * @v move Scroller movement + * @ret move Continuing scroller movement (if applicable) + */ +int jump_scroll_move ( struct jump_scroller *scroll, int move ) { + int current = scroll->current; + int last = ( scroll->count - 1 ); + + /* Sanity checks */ + assert ( move != 0 ); + assert ( scroll->count != 0 ); + + /* Move to the new current item */ + current += move; + + /* Check for start/end of list */ + if ( current < 0 ) { + /* We have attempted to move before the start of the + * list. Move to the start of the list and continue + * moving forwards (if applicable). + */ + scroll->current = 0; + return +1; + } else if ( current > last ) { + /* We have attempted to move after the end of the + * list. Move to the end of the list and continue + * moving backwards (if applicable). + */ + scroll->current = last; + return -1; + } else { + /* Update the current item and continue moving in the + * same direction (if applicable). + */ + scroll->current = current; + return ( ( move > 0 ) ? +1 : -1 ); + } +} + +/** + * Jump scroll to new page (if applicable) + * + * @v scroll Jump scroller + * @ret jumped Jumped to a new page + */ +int jump_scroll ( struct jump_scroller *scroll ) { + unsigned int index; + + /* Sanity checks */ + assert ( scroll->rows != 0 ); + assert ( scroll->count != 0 ); + assert ( scroll->current < scroll->count ); + assert ( scroll->first < scroll->count ); + + /* Do nothing if we are already on the correct page */ + index = ( scroll->current - scroll->first ); + if ( index < scroll->rows ) + return 0; + + /* Move to required page */ + while ( scroll->first < scroll->current ) + scroll->first += scroll->rows; + while ( scroll->first > scroll->current ) + scroll->first -= scroll->rows; + + return 1; +} diff --git a/qemu/roms/ipxe/src/hci/mucurses/alert.c b/qemu/roms/ipxe/src/hci/mucurses/alert.c index 00e959a89..7dc61c222 100644 --- a/qemu/roms/ipxe/src/hci/mucurses/alert.c +++ b/qemu/roms/ipxe/src/hci/mucurses/alert.c @@ -7,6 +7,8 @@ * */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + /** * Audible signal * diff --git a/qemu/roms/ipxe/src/hci/mucurses/ansi_screen.c b/qemu/roms/ipxe/src/hci/mucurses/ansi_screen.c index 1d3143f89..1cf3309dd 100644 --- a/qemu/roms/ipxe/src/hci/mucurses/ansi_screen.c +++ b/qemu/roms/ipxe/src/hci/mucurses/ansi_screen.c @@ -3,7 +3,7 @@ #include #include -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); static void ansiscr_reset(struct _curses_screen *scr) __nonnull; static void ansiscr_movetoyx(struct _curses_screen *scr, diff --git a/qemu/roms/ipxe/src/hci/mucurses/clear.c b/qemu/roms/ipxe/src/hci/mucurses/clear.c index f5e52ca20..2054f72cc 100644 --- a/qemu/roms/ipxe/src/hci/mucurses/clear.c +++ b/qemu/roms/ipxe/src/hci/mucurses/clear.c @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Clear a window to the bottom from current cursor position diff --git a/qemu/roms/ipxe/src/hci/mucurses/colour.c b/qemu/roms/ipxe/src/hci/mucurses/colour.c index c1359c868..b0c480b1f 100644 --- a/qemu/roms/ipxe/src/hci/mucurses/colour.c +++ b/qemu/roms/ipxe/src/hci/mucurses/colour.c @@ -1,6 +1,6 @@ #include -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct colour_pair { short fcol; diff --git a/qemu/roms/ipxe/src/hci/mucurses/cursor.h b/qemu/roms/ipxe/src/hci/mucurses/cursor.h index 16b7d27c2..2e0c896a6 100644 --- a/qemu/roms/ipxe/src/hci/mucurses/cursor.h +++ b/qemu/roms/ipxe/src/hci/mucurses/cursor.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct cursor_pos { unsigned int y, x; diff --git a/qemu/roms/ipxe/src/hci/mucurses/edging.c b/qemu/roms/ipxe/src/hci/mucurses/edging.c index eccd32422..e938d338b 100644 --- a/qemu/roms/ipxe/src/hci/mucurses/edging.c +++ b/qemu/roms/ipxe/src/hci/mucurses/edging.c @@ -8,6 +8,8 @@ * */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + /** * Draw borders from single-byte characters and renditions around a * window diff --git a/qemu/roms/ipxe/src/hci/mucurses/kb.c b/qemu/roms/ipxe/src/hci/mucurses/kb.c index b38c8c146..8face14d8 100644 --- a/qemu/roms/ipxe/src/hci/mucurses/kb.c +++ b/qemu/roms/ipxe/src/hci/mucurses/kb.c @@ -8,6 +8,8 @@ * MuCurses keyboard input handling functions */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + #define INPUT_DELAY 200 // half-blocking delay timer resolution (ms) #define INPUT_DELAY_TIMEOUT 1000 // half-blocking delay timeout diff --git a/qemu/roms/ipxe/src/hci/mucurses/mucurses.c b/qemu/roms/ipxe/src/hci/mucurses/mucurses.c index b67445baf..98a8a2c59 100644 --- a/qemu/roms/ipxe/src/hci/mucurses/mucurses.c +++ b/qemu/roms/ipxe/src/hci/mucurses/mucurses.c @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); static void _wupdcurs ( WINDOW *win ) __nonnull; void _wputch ( WINDOW *win, chtype ch, int wrap ) __nonnull; diff --git a/qemu/roms/ipxe/src/hci/mucurses/mucurses.h b/qemu/roms/ipxe/src/hci/mucurses/mucurses.h index 7ac1086ac..270394787 100644 --- a/qemu/roms/ipxe/src/hci/mucurses/mucurses.h +++ b/qemu/roms/ipxe/src/hci/mucurses/mucurses.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define WRAP 0 #define NOWRAP 1 diff --git a/qemu/roms/ipxe/src/hci/mucurses/print.c b/qemu/roms/ipxe/src/hci/mucurses/print.c index 9c682588b..e8831c58f 100644 --- a/qemu/roms/ipxe/src/hci/mucurses/print.c +++ b/qemu/roms/ipxe/src/hci/mucurses/print.c @@ -10,7 +10,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Add a single-byte character and rendition to a window and advance diff --git a/qemu/roms/ipxe/src/hci/mucurses/print_nadv.c b/qemu/roms/ipxe/src/hci/mucurses/print_nadv.c index ee472e685..3a44e5bd2 100644 --- a/qemu/roms/ipxe/src/hci/mucurses/print_nadv.c +++ b/qemu/roms/ipxe/src/hci/mucurses/print_nadv.c @@ -8,6 +8,8 @@ * */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + /** * Add string of single-byte characters and renditions to a window * diff --git a/qemu/roms/ipxe/src/hci/mucurses/slk.c b/qemu/roms/ipxe/src/hci/mucurses/slk.c index 600658e75..660eb65c0 100644 --- a/qemu/roms/ipxe/src/hci/mucurses/slk.c +++ b/qemu/roms/ipxe/src/hci/mucurses/slk.c @@ -11,6 +11,8 @@ * Soft label key functions */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + #define MIN_SPACE_SIZE 2 #define SLK_MAX_LABEL_LEN 8 diff --git a/qemu/roms/ipxe/src/hci/mucurses/widgets/editbox.c b/qemu/roms/ipxe/src/hci/mucurses/widgets/editbox.c index 630a66e0b..210de4481 100644 --- a/qemu/roms/ipxe/src/hci/mucurses/widgets/editbox.c +++ b/qemu/roms/ipxe/src/hci/mucurses/widgets/editbox.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/hci/mucurses/winattrs.c b/qemu/roms/ipxe/src/hci/mucurses/winattrs.c index f549d7519..97a5a18b3 100644 --- a/qemu/roms/ipxe/src/hci/mucurses/winattrs.c +++ b/qemu/roms/ipxe/src/hci/mucurses/winattrs.c @@ -6,7 +6,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Get the background rendition attributes for a window diff --git a/qemu/roms/ipxe/src/hci/mucurses/windows.c b/qemu/roms/ipxe/src/hci/mucurses/windows.c index 63d0af08c..7f39bdea2 100644 --- a/qemu/roms/ipxe/src/hci/mucurses/windows.c +++ b/qemu/roms/ipxe/src/hci/mucurses/windows.c @@ -9,6 +9,8 @@ * */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + /** * Delete a window * diff --git a/qemu/roms/ipxe/src/hci/mucurses/wininit.c b/qemu/roms/ipxe/src/hci/mucurses/wininit.c index b75abba44..dd84d2f1d 100644 --- a/qemu/roms/ipxe/src/hci/mucurses/wininit.c +++ b/qemu/roms/ipxe/src/hci/mucurses/wininit.c @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Initialise console environment diff --git a/qemu/roms/ipxe/src/hci/readline.c b/qemu/roms/ipxe/src/hci/readline.c index 40aa59787..83a2e0b90 100644 --- a/qemu/roms/ipxe/src/hci/readline.c +++ b/qemu/roms/ipxe/src/hci/readline.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/hci/shell.c b/qemu/roms/ipxe/src/hci/shell.c index c1a543849..276eb3527 100644 --- a/qemu/roms/ipxe/src/hci/shell.c +++ b/qemu/roms/ipxe/src/hci/shell.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -28,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include /** @file * @@ -36,7 +41,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ /** The shell prompt string */ -static const char shell_prompt[] = "iPXE> "; +static const char shell_prompt[] = PRODUCT_SHORT_NAME "> "; /** * "help" command diff --git a/qemu/roms/ipxe/src/hci/strerror.c b/qemu/roms/ipxe/src/hci/strerror.c index 9356e9e0a..1bba8c620 100644 --- a/qemu/roms/ipxe/src/hci/strerror.c +++ b/qemu/roms/ipxe/src/hci/strerror.c @@ -2,6 +2,7 @@ #include #include #include +#include /** @file * @@ -18,7 +19,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Find error description @@ -74,7 +75,7 @@ static struct errortab * find_closest_error ( int errno ) { * call to strerror(). * */ -const char * strerror ( int errno ) { +char * strerror ( int errno ) { static char errbuf[64]; struct errortab *errortab; @@ -88,11 +89,11 @@ const char * strerror ( int errno ) { /* Construct the error message */ if ( errortab ) { snprintf ( errbuf, sizeof ( errbuf ), - "%s (http://ipxe.org/%08x)", + "%s (" PRODUCT_ERROR_URI ")", errortab->text, errno ); } else { snprintf ( errbuf, sizeof ( errbuf ), - "Error %#08x (http://ipxe.org/%08x)", + "Error %#08x (" PRODUCT_ERROR_URI ")", errno, errno ); } diff --git a/qemu/roms/ipxe/src/hci/tui/login_ui.c b/qemu/roms/ipxe/src/hci/tui/login_ui.c index 996b68a0a..3c55325d5 100644 --- a/qemu/roms/ipxe/src/hci/tui/login_ui.c +++ b/qemu/roms/ipxe/src/hci/tui/login_ui.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/hci/tui/menu_ui.c b/qemu/roms/ipxe/src/hci/tui/menu_ui.c index 0a9566def..f9dd9d100 100644 --- a/qemu/roms/ipxe/src/hci/tui/menu_ui.c +++ b/qemu/roms/ipxe/src/hci/tui/menu_ui.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -32,6 +36,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include /* Screen layout */ @@ -46,12 +51,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); struct menu_ui { /** Menu */ struct menu *menu; - /** Number of menu items */ - unsigned int count; - /** Currently selected item */ - int selected; - /** First visible item */ - int first_visible; + /** Jump scroller */ + struct jump_scroller scroll; /** Timeout (0=indefinite) */ unsigned long timeout; }; @@ -80,7 +81,7 @@ static struct menu_item * menu_item ( struct menu *menu, unsigned int index ) { * @v ui Menu user interface * @v index Index */ -static void draw_menu_item ( struct menu_ui *ui, int index ) { +static void draw_menu_item ( struct menu_ui *ui, unsigned int index ) { struct menu_item *item; unsigned int row_offset; char buf[ MENU_COLS + 1 /* NUL */ ]; @@ -90,7 +91,7 @@ static void draw_menu_item ( struct menu_ui *ui, int index ) { size_t len; /* Move to start of row */ - row_offset = ( index - ui->first_visible ); + row_offset = ( index - ui->scroll.first ); move ( ( MENU_ROW + row_offset ), MENU_COL ); /* Get menu item */ @@ -102,7 +103,7 @@ static void draw_menu_item ( struct menu_ui *ui, int index ) { color_set ( CPAIR_SEPARATOR, NULL ); /* Highlight if this is the selected item */ - if ( index == ui->selected ) { + if ( index == ui->scroll.current ) { color_set ( CPAIR_SELECT, NULL ); attron ( A_BOLD ); } @@ -121,7 +122,7 @@ static void draw_menu_item ( struct menu_ui *ui, int index ) { snprintf ( timeout_buf, sizeof ( timeout_buf ), "(%ld)", ( ( ui->timeout + TICKS_PER_SEC - 1 ) / TICKS_PER_SEC ) ); - if ( ( index == ui->selected ) && ( ui->timeout != 0 ) ) { + if ( ( index == ui->scroll.current ) && ( ui->timeout != 0 ) ) { memcpy ( ( buf + MENU_COLS - MENU_PAD - timeout_len ), timeout_buf, timeout_len ); } @@ -150,24 +151,17 @@ static void draw_menu_item ( struct menu_ui *ui, int index ) { static void draw_menu_items ( struct menu_ui *ui ) { unsigned int i; - /* Jump scroll to correct point in list */ - while ( ui->first_visible < ui->selected ) - ui->first_visible += MENU_ROWS; - while ( ui->first_visible > ui->selected ) - ui->first_visible -= MENU_ROWS; - /* Draw ellipses before and/or after the list as necessary */ color_set ( CPAIR_SEPARATOR, NULL ); mvaddstr ( ( MENU_ROW - 1 ), ( MENU_COL + MENU_PAD ), - ( ( ui->first_visible > 0 ) ? "..." : " " ) ); + ( jump_scroll_is_first ( &ui->scroll ) ? " " : "..." ) ); mvaddstr ( ( MENU_ROW + MENU_ROWS ), ( MENU_COL + MENU_PAD ), - ( ( ( ui->first_visible + MENU_ROWS ) < ui->count ) ? - "..." : " " ) ); + ( jump_scroll_is_last ( &ui->scroll ) ? " " : "..." ) ); color_set ( CPAIR_NORMAL, NULL ); /* Draw visible items */ for ( i = 0 ; i < MENU_ROWS ; i++ ) - draw_menu_item ( ui, ( ui->first_visible + i ) ); + draw_menu_item ( ui, ( ui->scroll.first + i ) ); } /** @@ -180,8 +174,7 @@ static void draw_menu_items ( struct menu_ui *ui ) { static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { struct menu_item *item; unsigned long timeout; - unsigned int delta; - int current; + unsigned int previous; int key; int i; int move; @@ -190,7 +183,7 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { do { /* Record current selection */ - current = ui->selected; + previous = ui->scroll.current; /* Calculate timeout as remainder of current second */ timeout = ( ui->timeout % TICKS_PER_SEC ); @@ -209,27 +202,11 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { /* Cancel any timeout */ ui->timeout = 0; - /* Handle key */ + /* Handle scroll keys */ + move = jump_scroll_key ( &ui->scroll, key ); + + /* Handle other keys */ switch ( key ) { - case KEY_UP: - move = -1; - break; - case KEY_DOWN: - move = +1; - break; - case KEY_PPAGE: - move = ( ui->first_visible - ui->selected - 1 ); - break; - case KEY_NPAGE: - move = ( ui->first_visible - ui->selected - + MENU_ROWS ); - break; - case KEY_HOME: - move = -ui->count; - break; - case KEY_END: - move = +ui->count; - break; case ESC: case CTRL_C: rc = -ECANCELED; @@ -247,7 +224,7 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { i++; continue; } - ui->selected = i; + ui->scroll.current = i; if ( item->label ) { chosen = 1; } else { @@ -260,31 +237,22 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { /* Move selection, if applicable */ while ( move ) { - ui->selected += move; - if ( ui->selected < 0 ) { - ui->selected = 0; - move = +1; - } else if ( ui->selected >= ( int ) ui->count ) { - ui->selected = ( ui->count - 1 ); - move = -1; - } - item = menu_item ( ui->menu, ui->selected ); + move = jump_scroll_move ( &ui->scroll, move ); + item = menu_item ( ui->menu, ui->scroll.current ); if ( item->label ) break; - move = ( ( move > 0 ) ? +1 : -1 ); } /* Redraw selection if necessary */ - if ( ( ui->selected != current ) || ( timeout != 0 ) ) { - draw_menu_item ( ui, current ); - delta = ( ui->selected - ui->first_visible ); - if ( delta >= MENU_ROWS ) + if ( ( ui->scroll.current != previous ) || ( timeout != 0 ) ) { + draw_menu_item ( ui, previous ); + if ( jump_scroll ( &ui->scroll ) ) draw_menu_items ( ui ); - draw_menu_item ( ui, ui->selected ); + draw_menu_item ( ui, ui->scroll.current ); } /* Record selection */ - item = menu_item ( ui->menu, ui->selected ); + item = menu_item ( ui->menu, ui->scroll.current ); assert ( item != NULL ); assert ( item->label != NULL ); *selected = item; @@ -313,21 +281,22 @@ int show_menu ( struct menu *menu, unsigned long timeout, /* Initialise UI */ memset ( &ui, 0, sizeof ( ui ) ); ui.menu = menu; + ui.scroll.rows = MENU_ROWS; ui.timeout = timeout; list_for_each_entry ( item, &menu->items, list ) { if ( item->label ) { if ( ! labelled_count ) - ui.selected = ui.count; + ui.scroll.current = ui.scroll.count; labelled_count++; if ( select ) { if ( strcmp ( select, item->label ) == 0 ) - ui.selected = ui.count; + ui.scroll.current = ui.scroll.count; } else { if ( item->is_default ) - ui.selected = ui.count; + ui.scroll.current = ui.scroll.count; } } - ui.count++; + ui.scroll.count++; } if ( ! labelled_count ) { /* Menus with no labelled items cannot be selected @@ -349,8 +318,9 @@ int show_menu ( struct menu *menu, unsigned long timeout, snprintf ( buf, sizeof ( buf ), "%s", ui.menu->title ); mvprintw ( TITLE_ROW, ( ( COLS - strlen ( buf ) ) / 2 ), "%s", buf ); attroff ( A_BOLD ); + jump_scroll ( &ui.scroll ); draw_menu_items ( &ui ); - draw_menu_item ( &ui, ui.selected ); + draw_menu_item ( &ui, ui.scroll.current ); /* Enter main loop */ rc = menu_loop ( &ui, selected ); diff --git a/qemu/roms/ipxe/src/hci/tui/settings_ui.c b/qemu/roms/ipxe/src/hci/tui/settings_ui.c index 221839730..be421cc0a 100644 --- a/qemu/roms/ipxe/src/hci/tui/settings_ui.c +++ b/qemu/roms/ipxe/src/hci/tui/settings_ui.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -29,7 +33,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include +#include /** @file * @@ -47,7 +53,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define INSTRUCTION_ROW ( LINES - 2U ) #define INSTRUCTION_PAD " " -/** Layout of text within a setting widget */ +/** Layout of text within a setting row */ #define SETTING_ROW_TEXT( cols ) struct { \ char start[0]; \ char pad1[1]; \ @@ -63,8 +69,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); char nul; \ } __attribute__ (( packed )) -/** A setting row widget */ -struct setting_row_widget { +/** A settings user interface row */ +struct settings_ui_row { /** Target configuration settings block * * Valid only for rows that lead to new settings blocks. @@ -82,8 +88,6 @@ struct setting_row_widget { struct setting setting; /** Screen row */ unsigned int row; - /** Screen column */ - unsigned int col; /** Edit box widget used for editing setting */ struct edit_box editbox; /** Editing in progress flag */ @@ -92,28 +96,24 @@ struct setting_row_widget { char value[256]; /* enough size for a DHCP string */ }; -/** A settings widget */ -struct setting_widget { +/** A settings user interface */ +struct settings_ui { /** Settings block */ struct settings *settings; - /** Number of rows */ - unsigned int num_rows; - /** Current row index */ - unsigned int current; - /** Index of the first visible row, for scrolling. */ - unsigned int first_visible; - /** Active row */ - struct setting_row_widget row; + /** Jump scroller */ + struct jump_scroller scroll; + /** Current row */ + struct settings_ui_row row; }; /** - * Select a setting row + * Select a setting * - * @v widget Setting widget + * @v ui Settings user interface * @v index Index of setting row - * @ret count Number of settings rows + * @ret count Number of setting rows */ -static unsigned int select_setting_row ( struct setting_widget *widget, +static unsigned int select_setting_row ( struct settings_ui *ui, unsigned int index ) { SETTING_ROW_TEXT ( COLS ) *text; struct settings *settings; @@ -122,25 +122,22 @@ static unsigned int select_setting_row ( struct setting_widget *widget, unsigned int count = 0; /* Initialise structure */ - memset ( &widget->row, 0, sizeof ( widget->row ) ); - widget->current = index; - widget->row.row = ( SETTINGS_LIST_ROW + index - widget->first_visible ); - widget->row.col = SETTINGS_LIST_COL; + memset ( &ui->row, 0, sizeof ( ui->row ) ); + ui->row.row = ( SETTINGS_LIST_ROW + index - ui->scroll.first ); /* Include parent settings block, if applicable */ - if ( widget->settings->parent && ( count++ == index ) ) { - widget->row.settings = widget->settings->parent; - snprintf ( widget->row.value, sizeof ( widget->row.value ), + if ( ui->settings->parent && ( count++ == index ) ) { + ui->row.settings = ui->settings->parent; + snprintf ( ui->row.value, sizeof ( ui->row.value ), "../" ); } /* Include any child settings blocks, if applicable */ - list_for_each_entry ( settings, &widget->settings->children, siblings ){ + list_for_each_entry ( settings, &ui->settings->children, siblings ) { if ( count++ == index ) { - widget->row.settings = settings; - snprintf ( widget->row.value, - sizeof ( widget->row.value ), "%s/", - settings->name ); + ui->row.settings = settings; + snprintf ( ui->row.value, sizeof ( ui->row.value ), + "%s/", settings->name ); } } @@ -148,7 +145,7 @@ static unsigned int select_setting_row ( struct setting_widget *widget, for_each_table_entry ( setting, SETTINGS ) { /* Skip inapplicable settings */ - if ( ! setting_applies ( widget->settings, setting ) ) + if ( ! setting_applies ( ui->settings, setting ) ) continue; /* Skip duplicate settings */ @@ -158,18 +155,16 @@ static unsigned int select_setting_row ( struct setting_widget *widget, /* Read current setting value and origin */ if ( count++ == index ) { - fetchf_setting ( widget->settings, setting, - &widget->row.origin, - &widget->row.setting, - widget->row.value, - sizeof ( widget->row.value ) ); + fetchf_setting ( ui->settings, setting, &ui->row.origin, + &ui->row.setting, ui->row.value, + sizeof ( ui->row.value ) ); } } /* Initialise edit box */ - init_editbox ( &widget->row.editbox, widget->row.value, - sizeof ( widget->row.value ), NULL, widget->row.row, - ( widget->row.col + + init_editbox ( &ui->row.editbox, ui->row.value, + sizeof ( ui->row.value ), NULL, ui->row.row, + ( SETTINGS_LIST_COL + offsetof ( typeof ( *text ), u.setting.value ) ), sizeof ( text->u.setting.value ), 0 ); @@ -197,9 +192,9 @@ static size_t string_copy ( char *dest, const char *src, size_t len ) { /** * Draw setting row * - * @v widget Setting widget + * @v ui Settings UI */ -static void draw_setting_row ( struct setting_widget *widget ) { +static void draw_setting_row ( struct settings_ui *ui ) { SETTING_ROW_TEXT ( COLS ) text; unsigned int curs_offset; char *value; @@ -209,12 +204,12 @@ static void draw_setting_row ( struct setting_widget *widget ) { text.nul = '\0'; /* Construct row content */ - if ( widget->row.settings ) { + if ( ui->row.settings ) { /* Construct space-padded name */ curs_offset = ( offsetof ( typeof ( text ), u.settings ) + string_copy ( text.u.settings, - widget->row.value, + ui->row.value, sizeof ( text.u.settings ) ) ); } else { @@ -222,11 +217,11 @@ static void draw_setting_row ( struct setting_widget *widget ) { /* Construct dot-padded name */ memset ( text.u.setting.name, '.', sizeof ( text.u.setting.name ) ); - string_copy ( text.u.setting.name, widget->row.setting.name, + string_copy ( text.u.setting.name, ui->row.setting.name, sizeof ( text.u.setting.name ) ); /* Construct space-padded value */ - value = widget->row.value; + value = ui->row.value; if ( ! *value ) value = ""; curs_offset = ( offsetof ( typeof ( text ), u.setting.value ) + @@ -235,37 +230,34 @@ static void draw_setting_row ( struct setting_widget *widget ) { } /* Print row */ - if ( ( widget->row.origin == widget->settings ) || - ( widget->row.settings != NULL ) ) { + if ( ( ui->row.origin == ui->settings ) || ( ui->row.settings != NULL )) attron ( A_BOLD ); - } - mvprintw ( widget->row.row, widget->row.col, "%s", text.start ); + mvprintw ( ui->row.row, SETTINGS_LIST_COL, "%s", text.start ); attroff ( A_BOLD ); - move ( widget->row.row, widget->row.col + curs_offset ); + move ( ui->row.row, ( SETTINGS_LIST_COL + curs_offset ) ); } /** - * Edit setting widget + * Edit setting ui * - * @v widget Setting widget + * @v ui Settings UI * @v key Key pressed by user * @ret key Key returned to application, or zero */ -static int edit_setting ( struct setting_widget *widget, int key ) { - assert ( widget->row.setting.name != NULL ); - widget->row.editing = 1; - return edit_editbox ( &widget->row.editbox, key ); +static int edit_setting ( struct settings_ui *ui, int key ) { + assert ( ui->row.setting.name != NULL ); + ui->row.editing = 1; + return edit_editbox ( &ui->row.editbox, key ); } /** - * Save setting widget value back to configuration settings + * Save setting ui value back to configuration settings * - * @v widget Setting widget + * @v ui Settings UI */ -static int save_setting ( struct setting_widget *widget ) { - assert ( widget->row.setting.name != NULL ); - return storef_setting ( widget->settings, &widget->row.setting, - widget->row.value ); +static int save_setting ( struct settings_ui *ui ) { + assert ( ui->row.setting.name != NULL ); + return storef_setting ( ui->settings, &ui->row.setting, ui->row.value ); } /** @@ -340,15 +332,15 @@ static void alert ( const char *fmt, ... ) { /** * Draw title row * - * @v widget Setting widget + * @v ui Settings UI */ -static void draw_title_row ( struct setting_widget *widget ) { +static void draw_title_row ( struct settings_ui *ui ) { const char *name; clearmsg ( TITLE_ROW ); - name = settings_name ( widget->settings ); + name = settings_name ( ui->settings ); attron ( A_BOLD ); - msg ( TITLE_ROW, "iPXE configuration settings%s%s", + msg ( TITLE_ROW, PRODUCT_SHORT_NAME " configuration settings%s%s", ( name[0] ? " - " : "" ), name ); attroff ( A_BOLD ); } @@ -356,89 +348,73 @@ static void draw_title_row ( struct setting_widget *widget ) { /** * Draw information row * - * @v widget Setting widget + * @v ui Settings UI */ -static void draw_info_row ( struct setting_widget *widget ) { +static void draw_info_row ( struct settings_ui *ui ) { char buf[32]; /* Draw nothing unless this row represents a setting */ clearmsg ( INFO_ROW ); clearmsg ( INFO_ROW + 1 ); - if ( ! widget->row.setting.name ) + if ( ! ui->row.setting.name ) return; /* Determine a suitable setting name */ - setting_name ( ( widget->row.origin ? - widget->row.origin : widget->settings ), - &widget->row.setting, buf, sizeof ( buf ) ); + setting_name ( ( ui->row.origin ? + ui->row.origin : ui->settings ), + &ui->row.setting, buf, sizeof ( buf ) ); /* Draw row */ attron ( A_BOLD ); - msg ( INFO_ROW, "%s - %s", buf, widget->row.setting.description ); + msg ( INFO_ROW, "%s - %s", buf, ui->row.setting.description ); attroff ( A_BOLD ); color_set ( CPAIR_URL, NULL ); - msg ( ( INFO_ROW + 1 ), "http://ipxe.org/cfg/%s", - widget->row.setting.name ); + msg ( ( INFO_ROW + 1 ), PRODUCT_SETTING_URI, ui->row.setting.name ); color_set ( CPAIR_NORMAL, NULL ); } /** * Draw instruction row * - * @v widget Setting widget + * @v ui Settings UI */ -static void draw_instruction_row ( struct setting_widget *widget ) { +static void draw_instruction_row ( struct settings_ui *ui ) { clearmsg ( INSTRUCTION_ROW ); - if ( widget->row.editing ) { + if ( ui->row.editing ) { msg ( INSTRUCTION_ROW, "Enter - accept changes" INSTRUCTION_PAD "Ctrl-C - discard changes" ); } else { msg ( INSTRUCTION_ROW, "%sCtrl-X - exit configuration utility", - ( ( widget->row.origin == widget->settings ) ? + ( ( ui->row.origin == ui->settings ) ? "Ctrl-D - delete setting" INSTRUCTION_PAD : "" ) ); } } /** - * Reveal setting row + * Draw the current block of setting rows * - * @v widget Setting widget - * @v index Index of setting row + * @v ui Settings UI */ -static void reveal_setting_row ( struct setting_widget *widget, - unsigned int index ) { +static void draw_setting_rows ( struct settings_ui *ui ) { unsigned int i; - /* Simply return if setting N is already on-screen. */ - if ( index - widget->first_visible < SETTINGS_LIST_ROWS ) - return; - - /* Jump scroll to make the specified setting row visible. */ - while ( widget->first_visible < index ) - widget->first_visible += SETTINGS_LIST_ROWS; - while ( widget->first_visible > index ) - widget->first_visible -= SETTINGS_LIST_ROWS; - - /* Draw ellipses before and/or after the settings list to - * represent any invisible settings. - */ - mvaddstr ( SETTINGS_LIST_ROW - 1, - SETTINGS_LIST_COL + 1, - widget->first_visible > 0 ? "..." : " " ); - mvaddstr ( SETTINGS_LIST_ROW + SETTINGS_LIST_ROWS, - SETTINGS_LIST_COL + 1, - ( ( widget->first_visible + SETTINGS_LIST_ROWS ) - < widget->num_rows ? "..." : " " ) ); + /* Draw ellipses before and/or after the list as necessary */ + color_set ( CPAIR_SEPARATOR, NULL ); + mvaddstr ( ( SETTINGS_LIST_ROW - 1 ), ( SETTINGS_LIST_COL + 1 ), + jump_scroll_is_first ( &ui->scroll ) ? " " : "..." ); + mvaddstr ( ( SETTINGS_LIST_ROW + SETTINGS_LIST_ROWS ), + ( SETTINGS_LIST_COL + 1 ), + jump_scroll_is_last ( &ui->scroll ) ? " " : "..." ); + color_set ( CPAIR_NORMAL, NULL ); /* Draw visible settings. */ - for ( i = 0; i < SETTINGS_LIST_ROWS; i++ ) { - if ( ( widget->first_visible + i ) < widget->num_rows ) { - select_setting_row ( widget, - widget->first_visible + i ); - draw_setting_row ( widget ); + for ( i = 0 ; i < SETTINGS_LIST_ROWS ; i++ ) { + if ( ( ui->scroll.first + i ) < ui->scroll.count ) { + select_setting_row ( ui, ( ui->scroll.first + i ) ); + draw_setting_row ( ui ); } else { clearmsg ( SETTINGS_LIST_ROW + i ); } @@ -446,69 +422,72 @@ static void reveal_setting_row ( struct setting_widget *widget, } /** - * Reveal setting row + * Select settings block * - * @v widget Setting widget + * @v ui Settings UI * @v settings Settings block */ -static void init_widget ( struct setting_widget *widget, - struct settings *settings ) { - - widget->settings = settings_target ( settings ); - widget->num_rows = select_setting_row ( widget, 0 ); - widget->first_visible = SETTINGS_LIST_ROWS; - draw_title_row ( widget ); - reveal_setting_row ( widget, 0 ); - select_setting_row ( widget, 0 ); +static void select_settings ( struct settings_ui *ui, + struct settings *settings ) { + + ui->settings = settings_target ( settings ); + ui->scroll.count = select_setting_row ( ui, 0 ); + ui->scroll.rows = SETTINGS_LIST_ROWS; + ui->scroll.current = 0; + ui->scroll.first = 0; + draw_title_row ( ui ); + draw_setting_rows ( ui ); + select_setting_row ( ui, 0 ); } static int main_loop ( struct settings *settings ) { - struct setting_widget widget; + struct settings_ui ui; + unsigned int previous; int redraw = 1; int move; - unsigned int next; int key; int rc; /* Print initial screen content */ color_set ( CPAIR_NORMAL, NULL ); - memset ( &widget, 0, sizeof ( widget ) ); - init_widget ( &widget, settings ); + memset ( &ui, 0, sizeof ( ui ) ); + select_settings ( &ui, settings ); while ( 1 ) { /* Redraw rows if necessary */ if ( redraw ) { - draw_info_row ( &widget ); - draw_instruction_row ( &widget ); - color_set ( ( widget.row.editing ? + draw_info_row ( &ui ); + draw_instruction_row ( &ui ); + color_set ( ( ui.row.editing ? CPAIR_EDIT : CPAIR_SELECT ), NULL ); - draw_setting_row ( &widget ); + draw_setting_row ( &ui ); color_set ( CPAIR_NORMAL, NULL ); - curs_set ( widget.row.editing ); + curs_set ( ui.row.editing ); redraw = 0; } - if ( widget.row.editing ) { + /* Edit setting, if we are currently editing */ + if ( ui.row.editing ) { /* Sanity check */ - assert ( widget.row.setting.name != NULL ); + assert ( ui.row.setting.name != NULL ); /* Redraw edit box */ color_set ( CPAIR_EDIT, NULL ); - draw_editbox ( &widget.row.editbox ); + draw_editbox ( &ui.row.editbox ); color_set ( CPAIR_NORMAL, NULL ); /* Process keypress */ - key = edit_setting ( &widget, getkey ( 0 ) ); + key = edit_setting ( &ui, getkey ( 0 ) ); switch ( key ) { case CR: case LF: - if ( ( rc = save_setting ( &widget ) ) != 0 ) + if ( ( rc = save_setting ( &ui ) ) != 0 ) alert ( " %s ", strerror ( rc ) ); /* Fall through */ case CTRL_C: - select_setting_row ( &widget, widget.current ); + select_setting_row ( &ui, ui.scroll.current ); redraw = 1; break; default: @@ -516,72 +495,52 @@ static int main_loop ( struct settings *settings ) { break; } - } else { + continue; + } - /* Process keypress */ - key = getkey ( 0 ); - move = 0; - switch ( key ) { - case KEY_UP: - move = -1; - break; - case KEY_DOWN: - move = +1; - break; - case KEY_PPAGE: - move = ( widget.first_visible - - widget.current - 1 ); - break; - case KEY_NPAGE: - move = ( widget.first_visible - widget.current - + SETTINGS_LIST_ROWS ); - break; - case KEY_HOME: - move = -widget.num_rows; - break; - case KEY_END: - move = +widget.num_rows; - break; - case CTRL_D: - if ( ! widget.row.setting.name ) - break; - if ( ( rc = delete_setting ( widget.settings, - &widget.row.setting ) ) != 0 ) { - alert ( " %s ", strerror ( rc ) ); - } - select_setting_row ( &widget, widget.current ); + /* Otherwise, navigate through settings */ + key = getkey ( 0 ); + move = jump_scroll_key ( &ui.scroll, key ); + if ( move ) { + previous = ui.scroll.current; + jump_scroll_move ( &ui.scroll, move ); + if ( ui.scroll.current != previous ) { + draw_setting_row ( &ui ); redraw = 1; + if ( jump_scroll ( &ui.scroll ) ) + draw_setting_rows ( &ui ); + select_setting_row ( &ui, ui.scroll.current ); + } + continue; + } + + /* Handle non-navigation keys */ + switch ( key ) { + case CTRL_D: + if ( ! ui.row.setting.name ) break; - case CTRL_X: - return 0; - case CR: - case LF: - if ( widget.row.settings ) { - init_widget ( &widget, - widget.row.settings ); - redraw = 1; - } - /* Fall through */ - default: - if ( widget.row.setting.name ) { - edit_setting ( &widget, key ); - redraw = 1; - } - break; + if ( ( rc = delete_setting ( ui.settings, + &ui.row.setting ) ) != 0 ){ + alert ( " %s ", strerror ( rc ) ); + } + select_setting_row ( &ui, ui.scroll.current ); + redraw = 1; + break; + case CTRL_X: + return 0; + case CR: + case LF: + if ( ui.row.settings ) { + select_settings ( &ui, ui.row.settings ); + redraw = 1; } - if ( move ) { - next = ( widget.current + move ); - if ( ( int ) next < 0 ) - next = 0; - if ( next >= widget.num_rows ) - next = ( widget.num_rows - 1 ); - if ( next != widget.current ) { - draw_setting_row ( &widget ); - redraw = 1; - reveal_setting_row ( &widget, next ); - select_setting_row ( &widget, next ); - } + /* Fall through */ + default: + if ( ui.row.setting.name ) { + edit_setting ( &ui, key ); + redraw = 1; } + break; } } } diff --git a/qemu/roms/ipxe/src/image/elf.c b/qemu/roms/ipxe/src/image/elf.c index 51636a8e9..5c2f9db25 100644 --- a/qemu/roms/ipxe/src/image/elf.c +++ b/qemu/roms/ipxe/src/image/elf.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file @@ -36,27 +40,54 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include -typedef Elf32_Ehdr Elf_Ehdr; -typedef Elf32_Phdr Elf_Phdr; -typedef Elf32_Off Elf_Off; -#define ELFCLASS ELFCLASS32 - /** * Load ELF segment into memory * * @v image ELF file * @v phdr ELF program header + * @v dest Destination address + * @ret rc Return status code + */ +static int elf_load_segment ( struct image *image, Elf_Phdr *phdr, + physaddr_t dest ) { + userptr_t buffer = phys_to_user ( dest ); + int rc; + + DBGC ( image, "ELF %p loading segment [%x,%x) to [%lx,%lx,%lx)\n", + image, phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ), + dest, ( dest + phdr->p_filesz ), ( dest + phdr->p_memsz ) ); + + /* Verify and prepare segment */ + if ( ( rc = prep_segment ( buffer, phdr->p_filesz, + phdr->p_memsz ) ) != 0 ) { + DBGC ( image, "ELF %p could not prepare segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* Copy image to segment */ + memcpy_user ( buffer, 0, image->data, phdr->p_offset, phdr->p_filesz ); + + return 0; +} + +/** + * Process ELF segment + * + * @v image ELF file * @v ehdr ELF executable header + * @v phdr ELF program header + * @v process Segment processor * @ret entry Entry point, if found * @ret max Maximum used address * @ret rc Return status code */ -static int elf_load_segment ( struct image *image, Elf_Phdr *phdr, - Elf_Ehdr *ehdr, physaddr_t *entry, - physaddr_t *max ) { +static int elf_segment ( struct image *image, Elf_Ehdr *ehdr, Elf_Phdr *phdr, + int ( * process ) ( struct image *image, + Elf_Phdr *phdr, physaddr_t dest ), + physaddr_t *entry, physaddr_t *max ) { physaddr_t dest; physaddr_t end; - userptr_t buffer; unsigned long e_offset; int rc; @@ -82,28 +113,15 @@ static int elf_load_segment ( struct image *image, Elf_Phdr *phdr, image ); return -ENOEXEC; } - buffer = phys_to_user ( dest ); end = ( dest + phdr->p_memsz ); - DBGC ( image, "ELF %p loading segment [%x,%x) to [%x,%x,%x)\n", image, - phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ), - phdr->p_paddr, ( phdr->p_paddr + phdr->p_filesz ), - ( phdr->p_paddr + phdr->p_memsz ) ); - - /* Verify and prepare segment */ - if ( ( rc = prep_segment ( buffer, phdr->p_filesz, - phdr->p_memsz ) ) != 0 ) { - DBGC ( image, "ELF %p could not prepare segment: %s\n", - image, strerror ( rc ) ); - return rc; - } - /* Update maximum used address, if applicable */ if ( end > *max ) *max = end; - /* Copy image to segment */ - memcpy_user ( buffer, 0, image->data, phdr->p_offset, phdr->p_filesz ); + /* Process segment */ + if ( ( rc = process ( image, phdr, dest ) ) != 0 ) + return rc; /* Set execution address, if it lies within this segment */ if ( ( e_offset = ( ehdr->e_entry - dest ) ) < phdr->p_filesz ) { @@ -124,62 +142,85 @@ static int elf_load_segment ( struct image *image, Elf_Phdr *phdr, } /** - * Load ELF image into memory + * Process ELF segments * * @v image ELF file - * @ret entry Entry point + * @v ehdr ELF executable header + * @v process Segment processor + * @ret entry Entry point, if found * @ret max Maximum used address * @ret rc Return status code */ -int elf_load ( struct image *image, physaddr_t *entry, physaddr_t *max ) { - static const uint8_t e_ident[] = { - [EI_MAG0] = ELFMAG0, - [EI_MAG1] = ELFMAG1, - [EI_MAG2] = ELFMAG2, - [EI_MAG3] = ELFMAG3, - [EI_CLASS] = ELFCLASS, - }; - Elf_Ehdr ehdr; +int elf_segments ( struct image *image, Elf_Ehdr *ehdr, + int ( * process ) ( struct image *image, Elf_Phdr *phdr, + physaddr_t dest ), + physaddr_t *entry, physaddr_t *max ) { Elf_Phdr phdr; Elf_Off phoff; unsigned int phnum; int rc; - /* Read ELF header */ - copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) ); - if ( memcmp ( &ehdr.e_ident[EI_MAG0], e_ident, - sizeof ( e_ident ) ) != 0 ) { - DBGC ( image, "ELF %p has invalid signature\n", image ); - return -ENOEXEC; - } - /* Initialise maximum used address */ *max = 0; /* Invalidate entry point */ *entry = 0; - /* Read ELF program headers */ - for ( phoff = ehdr.e_phoff , phnum = ehdr.e_phnum ; phnum ; - phoff += ehdr.e_phentsize, phnum-- ) { + /* Read and process ELF program headers */ + for ( phoff = ehdr->e_phoff , phnum = ehdr->e_phnum ; phnum ; + phoff += ehdr->e_phentsize, phnum-- ) { if ( phoff > image->len ) { DBGC ( image, "ELF %p program header %d outside " "image\n", image, phnum ); return -ENOEXEC; } copy_from_user ( &phdr, image->data, phoff, sizeof ( phdr ) ); - if ( ( rc = elf_load_segment ( image, &phdr, &ehdr, - entry, max ) ) != 0 ) { + if ( ( rc = elf_segment ( image, ehdr, &phdr, process, + entry, max ) ) != 0 ) return rc; - } } /* Check for a valid execution address */ if ( ! *entry ) { DBGC ( image, "ELF %p entry point %lx outside image\n", - image, ( ( unsigned long ) ehdr.e_entry ) ); + image, ( ( unsigned long ) ehdr->e_entry ) ); + return -ENOEXEC; + } + + return 0; +} + +/** + * Load ELF image into memory + * + * @v image ELF file + * @ret entry Entry point + * @ret max Maximum used address + * @ret rc Return status code + */ +int elf_load ( struct image *image, physaddr_t *entry, physaddr_t *max ) { + static const uint8_t e_ident[] = { + [EI_MAG0] = ELFMAG0, + [EI_MAG1] = ELFMAG1, + [EI_MAG2] = ELFMAG2, + [EI_MAG3] = ELFMAG3, + [EI_CLASS] = ELFCLASS, + }; + Elf_Ehdr ehdr; + int rc; + + /* Read ELF header */ + copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) ); + if ( memcmp ( &ehdr.e_ident[EI_MAG0], e_ident, + sizeof ( e_ident ) ) != 0 ) { + DBGC ( image, "ELF %p has invalid signature\n", image ); return -ENOEXEC; } + /* Load ELF segments into memory */ + if ( ( rc = elf_segments ( image, &ehdr, elf_load_segment, + entry, max ) ) != 0 ) + return rc; + return 0; } diff --git a/qemu/roms/ipxe/src/image/embedded.c b/qemu/roms/ipxe/src/image/embedded.c index 6358378fb..48dd86851 100644 --- a/qemu/roms/ipxe/src/image/embedded.c +++ b/qemu/roms/ipxe/src/image/embedded.c @@ -6,7 +6,7 @@ * fetching over the network. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/image/png.c b/qemu/roms/ipxe/src/image/png.c index c14608553..5c4bcb3a0 100644 --- a/qemu/roms/ipxe/src/image/png.c +++ b/qemu/roms/ipxe/src/image/png.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/image/pnm.c b/qemu/roms/ipxe/src/image/pnm.c index af9e571a2..f24b28841 100644 --- a/qemu/roms/ipxe/src/image/pnm.c +++ b/qemu/roms/ipxe/src/image/pnm.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/image/script.c b/qemu/roms/ipxe/src/image/script.c index 5328da8b4..28050868a 100644 --- a/qemu/roms/ipxe/src/image/script.c +++ b/qemu/roms/ipxe/src/image/script.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/image/segment.c b/qemu/roms/ipxe/src/image/segment.c index 86fe42662..2d0f2f0fc 100644 --- a/qemu/roms/ipxe/src/image/segment.c +++ b/qemu/roms/ipxe/src/image/segment.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/include/.gitignore b/qemu/roms/ipxe/src/include/.gitignore deleted file mode 100644 index de1598ef3..000000000 --- a/qemu/roms/ipxe/src/include/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.buildserial.h diff --git a/qemu/roms/ipxe/src/include/assert.h b/qemu/roms/ipxe/src/include/assert.h index a33f6017c..07f3ecb8c 100644 --- a/qemu/roms/ipxe/src/include/assert.h +++ b/qemu/roms/ipxe/src/include/assert.h @@ -10,7 +10,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef NDEBUG #define ASSERTING 0 diff --git a/qemu/roms/ipxe/src/include/big_bswap.h b/qemu/roms/ipxe/src/include/big_bswap.h deleted file mode 100644 index 6c375a573..000000000 --- a/qemu/roms/ipxe/src/include/big_bswap.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef ETHERBOOT_BIG_BSWAP_H -#define ETHERBOOT_BIG_BSWAP_H - -#define htonll(x) (x) -#define ntohll(x) (x) -#define ntohl(x) (x) -#define htonl(x) (x) -#define ntohs(x) (x) -#define htons(x) (x) -#define cpu_to_le64(x) __bswap_64(x) -#define cpu_to_le32(x) __bswap_32(x) -#define cpu_to_le16(x) __bswap_16(x) -#define cpu_to_be64(x) (x) -#define cpu_to_be32(x) (x) -#define cpu_to_be16(x) (x) -#define le64_to_cpu(x) __bswap_64(x) -#define le32_to_cpu(x) __bswap_32(x) -#define le16_to_cpu(x) __bswap_16(x) -#define be64_to_cpu(x) (x) -#define be32_to_cpu(x) (x) -#define be16_to_cpu(x) (x) -#define cpu_to_le64s(x) __bswap_64s(x) -#define cpu_to_le32s(x) __bswap_32s(x) -#define cpu_to_le16s(x) __bswap_16s(x) -#define cpu_to_be64s(x) do {} while (0) -#define cpu_to_be32s(x) do {} while (0) -#define cpu_to_be16s(x) do {} while (0) -#define le64_to_cpus(x) __bswap_64s(x) -#define le32_to_cpus(x) __bswap_32s(x) -#define le16_to_cpus(x) __bswap_16s(x) -#define be64_to_cpus(x) do {} while (0) -#define be32_to_cpus(x) do {} while (0) -#define be16_to_cpus(x) do {} while (0) - -#endif /* ETHERBOOT_BIG_BSWAP_H */ diff --git a/qemu/roms/ipxe/src/include/byteswap.h b/qemu/roms/ipxe/src/include/byteswap.h index 466759cf8..d1028c579 100644 --- a/qemu/roms/ipxe/src/include/byteswap.h +++ b/qemu/roms/ipxe/src/include/byteswap.h @@ -1,59 +1,138 @@ -#ifndef ETHERBOOT_BYTESWAP_H -#define ETHERBOOT_BYTESWAP_H - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include "endian.h" -#include "bits/byteswap.h" - -#define __bswap_constant_16(x) \ - ((uint16_t)((((uint16_t)(x) & 0x00ff) << 8) | \ - (((uint16_t)(x) & 0xff00) >> 8))) - -#define __bswap_constant_32(x) \ - ((uint32_t)((((uint32_t)(x) & 0x000000ffU) << 24) | \ - (((uint32_t)(x) & 0x0000ff00U) << 8) | \ - (((uint32_t)(x) & 0x00ff0000U) >> 8) | \ - (((uint32_t)(x) & 0xff000000U) >> 24))) - -#define __bswap_constant_64(x) \ - ((uint64_t)((((uint64_t)(x) & 0x00000000000000ffULL) << 56) | \ - (((uint64_t)(x) & 0x000000000000ff00ULL) << 40) | \ - (((uint64_t)(x) & 0x0000000000ff0000ULL) << 24) | \ - (((uint64_t)(x) & 0x00000000ff000000ULL) << 8) | \ - (((uint64_t)(x) & 0x000000ff00000000ULL) >> 8) | \ - (((uint64_t)(x) & 0x0000ff0000000000ULL) >> 24) | \ - (((uint64_t)(x) & 0x00ff000000000000ULL) >> 40) | \ - (((uint64_t)(x) & 0xff00000000000000ULL) >> 56))) - -#define __bswap_16(x) \ - ((uint16_t)(__builtin_constant_p(x) ? \ - __bswap_constant_16(x) : \ - __bswap_variable_16(x))) - -#define __bswap_32(x) \ - ((uint32_t)(__builtin_constant_p(x) ? \ - __bswap_constant_32(x) : \ - __bswap_variable_32(x))) - -#define __bswap_64(x) \ - ((uint64_t)(__builtin_constant_p(x) ? \ - __bswap_constant_64(x) : \ - __bswap_variable_64(x))) +#ifndef BYTESWAP_H +#define BYTESWAP_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** + * Byte-swap a 16-bit constant + * + * @v value Constant value + * @ret swapped Byte-swapped value + */ +#define __bswap_constant_16( value ) \ + ( ( ( (value) & 0x00ff ) << 8 ) | \ + ( ( (value) & 0xff00 ) >> 8 ) ) + +/** + * Byte-swap a 32-bit constant + * + * @v value Constant value + * @ret swapped Byte-swapped value + */ +#define __bswap_constant_32( value ) \ + ( ( ( (value) & 0x000000ffUL ) << 24 ) | \ + ( ( (value) & 0x0000ff00UL ) << 8 ) | \ + ( ( (value) & 0x00ff0000UL ) >> 8 ) | \ + ( ( (value) & 0xff000000UL ) >> 24 ) ) + +/** + * Byte-swap a 64-bit constant + * + * @v value Constant value + * @ret swapped Byte-swapped value + */ +#define __bswap_constant_64( value ) \ + ( ( ( (value) & 0x00000000000000ffULL ) << 56 ) | \ + ( ( (value) & 0x000000000000ff00ULL ) << 40 ) | \ + ( ( (value) & 0x0000000000ff0000ULL ) << 24 ) | \ + ( ( (value) & 0x00000000ff000000ULL ) << 8 ) | \ + ( ( (value) & 0x000000ff00000000ULL ) >> 8 ) | \ + ( ( (value) & 0x0000ff0000000000ULL ) >> 24 ) | \ + ( ( (value) & 0x00ff000000000000ULL ) >> 40 ) | \ + ( ( (value) & 0xff00000000000000ULL ) >> 56 ) ) + +/** + * Byte-swap a 16-bit value + * + * @v value Value + * @ret swapped Byte-swapped value + */ +#define __bswap_16( value ) \ + ( __builtin_constant_p (value) ? \ + ( ( uint16_t ) __bswap_constant_16 ( ( uint16_t ) (value) ) ) \ + : __bswap_variable_16 (value) ) +#define bswap_16( value ) __bswap_16 (value) + +/** + * Byte-swap a 32-bit value + * + * @v value Value + * @ret swapped Byte-swapped value + */ +#define __bswap_32( value ) \ + ( __builtin_constant_p (value) ? \ + ( ( uint32_t ) __bswap_constant_32 ( ( uint32_t ) (value) ) ) \ + : __bswap_variable_32 (value) ) +#define bswap_32( value ) __bswap_32 (value) + +/** + * Byte-swap a 64-bit value + * + * @v value Value + * @ret swapped Byte-swapped value + */ +#define __bswap_64( value ) \ + ( __builtin_constant_p (value) ? \ + ( ( uint64_t ) __bswap_constant_64 ( ( uint64_t ) (value) ) ) \ + : __bswap_variable_64 (value) ) +#define bswap_64( value ) __bswap_64 (value) #if __BYTE_ORDER == __LITTLE_ENDIAN -#include "little_bswap.h" +#define __cpu_to_leNN( bits, value ) (value) +#define __cpu_to_beNN( bits, value ) __bswap_ ## bits (value) +#define __leNN_to_cpu( bits, value ) (value) +#define __beNN_to_cpu( bits, value ) __bswap_ ## bits (value) +#define __cpu_to_leNNs( bits, ptr ) do { } while ( 0 ) +#define __cpu_to_beNNs( bits, ptr ) __bswap_ ## bits ## s (ptr) +#define __leNN_to_cpus( bits, ptr ) do { } while ( 0 ) +#define __beNN_to_cpus( bits, ptr ) __bswap_ ## bits ## s (ptr) #endif + #if __BYTE_ORDER == __BIG_ENDIAN -#include "big_bswap.h" +#define __cpu_to_leNN( bits, value ) __bswap_ ## bits (value) +#define __cpu_to_beNN( bits, value ) (value) +#define __leNN_to_cpu( bits, value ) __bswap_ ## bits (value) +#define __beNN_to_cpu( bits, value ) (value) +#define __cpu_to_leNNs( bits, ptr ) __bswap_ ## bits ## s (ptr) +#define __cpu_to_beNNs( bits, ptr ) do { } while ( 0 ) +#define __leNN_to_cpus( bits, ptr ) __bswap_ ## bits ## s (ptr) +#define __beNN_to_cpus( bits, ptr ) do { } while ( 0 ) #endif -/* Make routines available to all */ -#define swap64(x) __bswap_64(x) -#define swap32(x) __bswap_32(x) -#define swap16(x) __bswap_16(x) -#define bswap_64(x) __bswap_64(x) -#define bswap_32(x) __bswap_32(x) -#define bswap_16(x) __bswap_16(x) - -#endif /* ETHERBOOT_BYTESWAP_H */ +#define cpu_to_le16( value ) __cpu_to_leNN ( 16, value ) +#define cpu_to_le32( value ) __cpu_to_leNN ( 32, value ) +#define cpu_to_le64( value ) __cpu_to_leNN ( 64, value ) +#define cpu_to_be16( value ) __cpu_to_beNN ( 16, value ) +#define cpu_to_be32( value ) __cpu_to_beNN ( 32, value ) +#define cpu_to_be64( value ) __cpu_to_beNN ( 64, value ) +#define le16_to_cpu( value ) __leNN_to_cpu ( 16, value ) +#define le32_to_cpu( value ) __leNN_to_cpu ( 32, value ) +#define le64_to_cpu( value ) __leNN_to_cpu ( 64, value ) +#define be16_to_cpu( value ) __beNN_to_cpu ( 16, value ) +#define be32_to_cpu( value ) __beNN_to_cpu ( 32, value ) +#define be64_to_cpu( value ) __beNN_to_cpu ( 64, value ) +#define cpu_to_le16s( ptr ) __cpu_to_leNNs ( 16, ptr ) +#define cpu_to_le32s( ptr ) __cpu_to_leNNs ( 32, ptr ) +#define cpu_to_le64s( ptr ) __cpu_to_leNNs ( 64, ptr ) +#define cpu_to_be16s( ptr ) __cpu_to_beNNs ( 16, ptr ) +#define cpu_to_be32s( ptr ) __cpu_to_beNNs ( 32, ptr ) +#define cpu_to_be64s( ptr ) __cpu_to_beNNs ( 64, ptr ) +#define le16_to_cpus( ptr ) __leNN_to_cpus ( 16, ptr ) +#define le32_to_cpus( ptr ) __leNN_to_cpus ( 32, ptr ) +#define le64_to_cpus( ptr ) __leNN_to_cpus ( 64, ptr ) +#define be16_to_cpus( ptr ) __beNN_to_cpus ( 16, ptr ) +#define be32_to_cpus( ptr ) __beNN_to_cpus ( 32, ptr ) +#define be64_to_cpus( ptr ) __beNN_to_cpus ( 64, ptr ) + +#define htonll( value ) cpu_to_be64 (value) +#define ntohll( value ) be64_to_cpu (value) +#define htonl( value ) cpu_to_be32 (value) +#define ntohl( value ) be32_to_cpu (value) +#define htons( value ) cpu_to_be16 (value) +#define ntohs( value ) be16_to_cpu (value) + +#endif /* BYTESWAP_H */ diff --git a/qemu/roms/ipxe/src/include/compiler.h b/qemu/roms/ipxe/src/include/compiler.h index 3f5c913a0..ca82f9523 100644 --- a/qemu/roms/ipxe/src/include/compiler.h +++ b/qemu/roms/ipxe/src/include/compiler.h @@ -57,101 +57,100 @@ * @{ */ -/** Provide a symbol within this object file */ +/** + * Provide a symbol within this object file + * + * @v symbol Symbol name + */ #ifdef ASSEMBLY -#define PROVIDE_SYMBOL( _sym ) \ - .section ".provided", "a", @nobits ; \ - .hidden _sym ; \ - .globl _sym ; \ - _sym: ; \ +#define PROVIDE_SYMBOL( symbol ) \ + .section ".provided", "a", @nobits ; \ + .hidden symbol ; \ + .globl symbol ; \ + symbol: ; \ .previous -#else /* ASSEMBLY */ -#define PROVIDE_SYMBOL( _sym ) \ - char _sym[0] \ +#else +#define PROVIDE_SYMBOL( symbol ) \ + char symbol[0] \ __attribute__ (( section ( ".provided" ) )) -#endif /* ASSEMBLY */ +#endif -/** Require a symbol within this object file +/** + * Request a symbol + * + * @v symbol Symbol name * - * The symbol is referenced by a relocation in a discarded section, so - * if it is not available at link time the link will fail. + * Request a symbol to be included within the link. If the symbol + * cannot be found, the link will succeed anyway. */ #ifdef ASSEMBLY -#define REQUIRE_SYMBOL( _sym ) \ - .section ".discard", "a", @progbits ; \ - .extern _sym ; \ - .long _sym ; \ - .previous -#else /* ASSEMBLY */ -#define REQUIRE_SYMBOL( _sym ) \ - extern char _sym; \ - static char * _C2 ( _C2 ( __require_, _sym ), _C2 ( _, __LINE__ ) ) \ - __attribute__ (( section ( ".discard" ), used )) \ - = &_sym +#define REQUEST_SYMBOL( symbol ) \ + .equ __request_ ## symbol, symbol +#else +#define REQUEST_SYMBOL( symbol ) \ + __asm__ ( ".equ __request_" #symbol ", " #symbol ) #endif -/** Request that a symbol be available at runtime +/** + * Require a symbol + * + * @v symbol Symbol name * - * The requested symbol is entered as undefined into the symbol table - * for this object, so the linker will pull in other object files as - * necessary to satisfy the reference. However, the undefined symbol - * is not referenced in any relocations, so the link can still succeed - * if no file contains it. + * Require a symbol to be included within the link. If the symbol + * cannot be found, the link will fail. * - * A symbol passed to this macro may not be referenced anywhere - * else in the file. If you want to do that, see IMPORT_SYMBOL(). + * To use this macro within a file, you must also specify the file's + * "requiring symbol" using the REQUIRING_SYMBOL() or + * PROVIDE_REQUIRING_SYMBOL() macros. */ #ifdef ASSEMBLY -#define REQUEST_SYMBOL( _sym ) \ - .equ __need_ ## _sym, _sym -#else /* ASSEMBLY */ -#define REQUEST_SYMBOL( _sym ) \ - __asm__ ( ".equ\t__need_" #_sym ", " #_sym ) -#endif /* ASSEMBLY */ +#define REQUIRE_SYMBOL( symbol ) \ + .reloc __requiring_symbol__, RELOC_TYPE_NONE, symbol +#else +#define REQUIRE_SYMBOL( symbol ) \ + __asm__ ( ".reloc __requiring_symbol__, " \ + _S2 ( RELOC_TYPE_NONE ) ", " #symbol ) +#endif -/** Set up a symbol to be usable in another file by IMPORT_SYMBOL() +/** + * Specify the file's requiring symbol + * + * @v symbol Symbol name * - * The symbol must already be marked as global. + * REQUIRE_SYMBOL() works by defining a dummy relocation record + * against a nominated "requiring symbol". The presence of the + * nominated requiring symbol will drag in all of the symbols + * specified using REQUIRE_SYMBOL(). */ -#define EXPORT_SYMBOL( _sym ) PROVIDE_SYMBOL ( __export_ ## _sym ) +#ifdef ASSEMBLY +#define REQUIRING_SYMBOL( symbol ) \ + .equ __requiring_symbol__, symbol +#else +#define REQUIRING_SYMBOL( symbol ) \ + __asm__ ( ".equ __requiring_symbol__, " #symbol ) +#endif -/** Make a symbol usable to this file if available at link time - * - * If no file passed to the linker contains the symbol, it will have - * @c NULL value to future uses. Keep in mind that the symbol value is - * really the @e address of a variable or function; see the code - * snippet below. - * - * In C using IMPORT_SYMBOL, you must specify the declaration as the - * second argument, for instance - * - * @code - * IMPORT_SYMBOL ( my_func, int my_func ( int arg ) ); - * IMPORT_SYMBOL ( my_var, int my_var ); - * - * void use_imports ( void ) { - * if ( my_func && &my_var ) - * my_var = my_func ( my_var ); - * } - * @endcode - * - * GCC considers a weak declaration to override a strong one no matter - * which comes first, so it is safe to include a header file declaring - * the imported symbol normally, but providing the declaration to - * IMPORT_SYMBOL is still required. +/** + * Provide a file's requiring symbol * - * If no EXPORT_SYMBOL declaration exists for the imported symbol in - * another file, the behavior will be most likely be identical to that - * for an unavailable symbol. + * If the file contains no symbols that can be used as the requiring + * symbol, you can provide a dummy one-byte-long symbol using + * PROVIDE_REQUIRING_SYMBOL(). */ #ifdef ASSEMBLY -#define IMPORT_SYMBOL( _sym ) \ - REQUEST_SYMBOL ( __export_ ## _sym ) ; \ - .weak _sym -#else /* ASSEMBLY */ -#define IMPORT_SYMBOL( _sym, _decl ) \ - REQUEST_SYMBOL ( __export_ ## _sym ) ; \ - extern _decl __attribute__ (( weak )) +#define PROVIDE_REQUIRING_SYMBOL() \ + .section ".tbl.requiring_symbols", "a", @progbits ; \ + __requiring_symbol__: .byte 0 ; \ + .size __requiring_symbol__, . - __requiring_symbol__ ; \ + .previous +#else +#define PROVIDE_REQUIRING_SYMBOL() \ + __asm__ ( ".section \".tbl.requiring_symbols\", " \ + " \"a\", @progbits\n" \ + "__requiring_symbol__:\t.byte 0\n" \ + ".size __requiring_symbol__, " \ + " . - __requiring_symbol__\n" \ + ".previous" ) #endif /** @} */ @@ -163,20 +162,33 @@ #define PREFIX_OBJECT( _prefix ) _C2 ( _prefix, OBJECT ) #define OBJECT_SYMBOL PREFIX_OBJECT ( obj_ ) -#define REQUEST_EXPANDED( _sym ) REQUEST_SYMBOL ( _sym ) -#define CONFIG_SYMBOL PREFIX_OBJECT ( obj_config_ ) /** Always provide the symbol for the current object (defined by -DOBJECT) */ PROVIDE_SYMBOL ( OBJECT_SYMBOL ); -/** Pull in an object-specific configuration file if available */ -REQUEST_EXPANDED ( CONFIG_SYMBOL ); - -/** Explicitly require another object */ -#define REQUIRE_OBJECT( _obj ) REQUIRE_SYMBOL ( obj_ ## _obj ) +/** + * Request an object + * + * @v object Object name + * + * Request an object to be included within the link. If the object + * cannot be found, the link will succeed anyway. + */ +#define REQUEST_OBJECT( object ) REQUEST_SYMBOL ( obj_ ## object ) -/** Pull in another object if it exists */ -#define REQUEST_OBJECT( _obj ) REQUEST_SYMBOL ( obj_ ## _obj ) +/** + * Require an object + * + * @v object Object name + * + * Require an object to be included within the link. If the object + * cannot be found, the link will fail. + * + * To use this macro within a file, you must also specify the file's + * "requiring symbol" using the REQUIRING_SYMBOL() or + * PROVIDE_REQUIRING_SYMBOL() macros. + */ +#define REQUIRE_OBJECT( object ) REQUIRE_SYMBOL ( obj_ ## object ) /** @} */ @@ -195,14 +207,6 @@ REQUEST_EXPANDED ( CONFIG_SYMBOL ); */ #define __weak __attribute__ (( weak, noinline )) -/** Prevent a function from being optimized away without inlining - * - * Calls to functions with void return type that contain no code in their body - * may be removed by gcc's optimizer even when inlining is inhibited. Placing - * this macro in the body of the function prevents that from occurring. - */ -#define __keepme asm(""); - #endif /** @defgroup dbg Debugging infrastructure @@ -730,13 +734,24 @@ int __debug_disable; #define FILE_LICENCE_MIT \ PROVIDE_SYMBOL ( PREFIX_OBJECT ( __licence__mit__ ) ) +/** Declare a file as being under GPLv2+ or UBDL + * + * This licence declaration is applicable when a file states itself to + * be licensed under the GNU GPL; "either version 2 of the License, or + * (at your option) any later version" and also states that it may be + * distributed under the terms of the Unmodified Binary Distribution + * Licence (as given in the file COPYING.UBDL). + */ +#define FILE_LICENCE_GPL2_OR_LATER_OR_UBDL \ + PROVIDE_SYMBOL ( PREFIX_OBJECT ( __licence__gpl2_or_later_or_ubdl__ ) ) + /** Declare a particular licence as applying to a file */ #define FILE_LICENCE( _licence ) FILE_LICENCE_ ## _licence /** @} */ -/* This file itself is under GPLv2-or-later */ -FILE_LICENCE ( GPL2_OR_LATER ); +/* This file itself is under GPLv2+/UBDL */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ctype.h b/qemu/roms/ipxe/src/include/ctype.h index e92ecb1c0..0d79ecd19 100644 --- a/qemu/roms/ipxe/src/include/ctype.h +++ b/qemu/roms/ipxe/src/include/ctype.h @@ -4,30 +4,114 @@ /** @file * * Character types + * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -#define isdigit(c) ((c) >= '0' && (c) <= '9') -#define islower(c) ((c) >= 'a' && (c) <= 'z') -#define isupper(c) ((c) >= 'A' && (c) <= 'Z') -#define isxdigit(c) (isdigit(c) || ((c) >= 'A' && (c) <= 'F') || ((c) >= 'a' && (c) <= 'f')) -#define isprint(c) ((c) >= ' ' && (c) <= '~' ) +/** + * Check if character is a decimal digit + * + * @v character ASCII character + * @ret is_digit Character is a decimal digit + */ +static inline int isdigit ( int character ) { -static inline unsigned char tolower(unsigned char c) -{ - if (isupper(c)) - c -= 'A'-'a'; - return c; + return ( ( character >= '0' ) && ( character <= '9' ) ); } -static inline unsigned char toupper(unsigned char c) -{ - if (islower(c)) - c -= 'a'-'A'; - return c; +/** + * Check if character is a hexadecimal digit + * + * @v character ASCII character + * @ret is_xdigit Character is a hexadecimal digit + */ +static inline int isxdigit ( int character ) { + + return ( ( ( character >= '0' ) && ( character <= '9' ) ) || + ( ( character >= 'A' ) && ( character <= 'F' ) ) || + ( ( character >= 'a' ) && ( character <= 'f' ) ) ); +} + +/** + * Check if character is an upper-case letter + * + * @v character ASCII character + * @ret is_upper Character is an upper-case letter + */ +static inline int isupper ( int character ) { + + return ( ( character >= 'A' ) && ( character <= 'Z' ) ); +} + +/** + * Check if character is a lower-case letter + * + * @v character ASCII character + * @ret is_lower Character is a lower-case letter + */ +static inline int islower ( int character ) { + + return ( ( character >= 'a' ) && ( character <= 'z' ) ); +} + +/** + * Check if character is alphabetic + * + * @v character ASCII character + * @ret is_alpha Character is alphabetic + */ +static inline int isalpha ( int character ) { + + return ( isupper ( character ) || islower ( character ) ); +} + +/** + * Check if character is alphanumeric + * + * @v character ASCII character + * @ret is_alnum Character is alphanumeric + */ +static inline int isalnum ( int character ) { + + return ( isalpha ( character ) || isdigit ( character ) ); +} + +/** + * Check if character is printable + * + * @v character ASCII character + * @ret is_print Character is printable + */ +static inline int isprint ( int character ) { + + return ( ( character >= ' ' ) && ( character <= '~' ) ); +} + +/** + * Convert character to lower case + * + * @v character Character + * @v character Lower-case character + */ +static inline int tolower ( int character ) { + + return ( isupper ( character ) ? + ( character - 'A' + 'a' ) : character ); +} + +/** + * Convert character to upper case + * + * @v character Character + * @v character Upper-case character + */ +static inline int toupper ( int character ) { + + return ( islower ( character ) ? + ( character - 'a' + 'A' ) : character ); } -extern int isspace ( int c ); +extern int isspace ( int character ); #endif /* _CTYPE_H */ diff --git a/qemu/roms/ipxe/src/include/curses.h b/qemu/roms/ipxe/src/include/curses.h index f16f9d7d0..04060fe27 100644 --- a/qemu/roms/ipxe/src/include/curses.h +++ b/qemu/roms/ipxe/src/include/curses.h @@ -11,7 +11,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #undef ERR #define ERR (-1) diff --git a/qemu/roms/ipxe/src/include/elf.h b/qemu/roms/ipxe/src/include/elf.h index 04022b687..18f755a21 100644 --- a/qemu/roms/ipxe/src/include/elf.h +++ b/qemu/roms/ipxe/src/include/elf.h @@ -1,234 +1,81 @@ #ifndef ELF_H #define ELF_H -FILE_LICENCE ( GPL2_OR_LATER ); - -#define EI_NIDENT 16 /* Size of e_ident array. */ - -/* Values for e_type. */ -#define ET_NONE 0 /* No file type */ -#define ET_REL 1 /* Relocatable file */ -#define ET_EXEC 2 /* Executable file */ -#define ET_DYN 3 /* Shared object file */ -#define ET_CORE 4 /* Core file */ - -/* Values for e_machine (architecute). */ -#define EM_NONE 0 /* No machine */ -#define EM_M32 1 /* AT&T WE 32100 */ -#define EM_SPARC 2 /* SUN SPARC */ -#define EM_386 3 /* Intel 80386+ */ -#define EM_68K 4 /* Motorola m68k family */ -#define EM_88K 5 /* Motorola m88k family */ -#define EM_486 6 /* Perhaps disused */ -#define EM_860 7 /* Intel 80860 */ -#define EM_MIPS 8 /* MIPS R3000 big-endian */ -#define EM_S370 9 /* IBM System/370 */ -#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ - -#define EM_PARISC 15 /* HPPA */ -#define EM_VPP500 17 /* Fujitsu VPP500 */ -#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ -#define EM_960 19 /* Intel 80960 */ -#define EM_PPC 20 /* PowerPC */ -#define EM_PPC64 21 /* PowerPC 64-bit */ -#define EM_S390 22 /* IBM S390 */ - -#define EM_V800 36 /* NEC V800 series */ -#define EM_FR20 37 /* Fujitsu FR20 */ -#define EM_RH32 38 /* TRW RH-32 */ -#define EM_RCE 39 /* Motorola RCE */ -#define EM_ARM 40 /* ARM */ -#define EM_FAKE_ALPHA 41 /* Digital Alpha */ -#define EM_SH 42 /* Hitachi SH */ -#define EM_SPARCV9 43 /* SPARC v9 64-bit */ -#define EM_TRICORE 44 /* Siemens Tricore */ -#define EM_ARC 45 /* Argonaut RISC Core */ -#define EM_H8_300 46 /* Hitachi H8/300 */ -#define EM_H8_300H 47 /* Hitachi H8/300H */ -#define EM_H8S 48 /* Hitachi H8S */ -#define EM_H8_500 49 /* Hitachi H8/500 */ -#define EM_IA_64 50 /* Intel Merced */ -#define EM_MIPS_X 51 /* Stanford MIPS-X */ -#define EM_COLDFIRE 52 /* Motorola Coldfire */ -#define EM_68HC12 53 /* Motorola M68HC12 */ -#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ -#define EM_PCP 55 /* Siemens PCP */ -#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ -#define EM_NDR1 57 /* Denso NDR1 microprocessor */ -#define EM_STARCORE 58 /* Motorola Start*Core processor */ -#define EM_ME16 59 /* Toyota ME16 processor */ -#define EM_ST100 60 /* STMicroelectronic ST100 processor */ -#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ -#define EM_X86_64 62 /* AMD x86-64 architecture */ -#define EM_PDSP 63 /* Sony DSP Processor */ - -#define EM_FX66 66 /* Siemens FX66 microcontroller */ -#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ -#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ -#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ -#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ -#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ -#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ -#define EM_SVX 73 /* Silicon Graphics SVx */ -#define EM_AT19 74 /* STMicroelectronics ST19 8 bit mc */ -#define EM_VAX 75 /* Digital VAX */ -#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ -#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ -#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ -#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ -#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ -#define EM_HUANY 81 /* Harvard University machine-independent object files */ -#define EM_PRISM 82 /* SiTera Prism */ -#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ -#define EM_FR30 84 /* Fujitsu FR30 */ -#define EM_D10V 85 /* Mitsubishi D10V */ -#define EM_D30V 86 /* Mitsubishi D30V */ -#define EM_V850 87 /* NEC v850 */ -#define EM_M32R 88 /* Mitsubishi M32R */ -#define EM_MN10300 89 /* Matsushita MN10300 */ -#define EM_MN10200 90 /* Matsushita MN10200 */ -#define EM_PJ 91 /* picoJava */ -#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ -#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ -#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ -#define EM_NUM 95 - -/* Values for p_type. */ -#define PT_NULL 0 /* Unused entry. */ -#define PT_LOAD 1 /* Loadable segment. */ -#define PT_DYNAMIC 2 /* Dynamic linking information segment. */ -#define PT_INTERP 3 /* Pathname of interpreter. */ -#define PT_NOTE 4 /* Auxiliary information. */ -#define PT_SHLIB 5 /* Reserved (not used). */ -#define PT_PHDR 6 /* Location of program header itself. */ - -/* Values for p_flags. */ -#define PF_X 0x1 /* Executable. */ -#define PF_W 0x2 /* Writable. */ -#define PF_R 0x4 /* Readable. */ - - -#define ELF_PROGRAM_RETURNS_BIT 0x8000000 /* e_flags bit 31 */ - -#define EI_MAG0 0 -#define ELFMAG0 0x7f - -#define EI_MAG1 1 -#define ELFMAG1 'E' - -#define EI_MAG2 2 -#define ELFMAG2 'L' - -#define EI_MAG3 3 -#define ELFMAG3 'F' - -#define ELFMAG "\177ELF" -#define SELFMAG 4 - -#define EI_CLASS 4 /* File class byte index */ -#define ELFCLASSNONE 0 /* Invalid class */ -#define ELFCLASS32 1 /* 32-bit objects */ -#define ELFCLASS64 2 /* 64-bit objects */ +/** + * @file + * + * ELF headers + * + */ -#define EI_DATA 5 /* Data encodeing byte index */ -#define ELFDATANONE 0 /* Invalid data encoding */ -#define ELFDATA2LSB 1 /* 2's complement little endian */ -#define ELFDATA2MSB 2 /* 2's complement big endian */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -#define EI_VERSION 6 /* File version byte index */ - /* Value must be EV_CURRENT */ +#include -#define EV_NONE 0 /* Invalid ELF Version */ -#define EV_CURRENT 1 /* Current version */ +typedef uint32_t Elf32_Addr; +typedef uint16_t Elf32_Half; +typedef uint32_t Elf32_Off; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf32_Word; -#define ELF32_PHDR_SIZE (8*4) /* Size of an elf program header */ +/** Length of ELF identifier */ +#define EI_NIDENT 16 -#ifndef ASSEMBLY +/** ELF header */ +typedef struct { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; -#include +/* ELF identifier indexes */ +#define EI_MAG0 0 +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 -/* - * ELF definitions common to all 32-bit architectures. - */ +/* ELF magic signature bytes */ +#define ELFMAG0 0x7f +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' -typedef uint32_t Elf32_Addr; -typedef uint16_t Elf32_Half; -typedef uint32_t Elf32_Off; -typedef int32_t Elf32_Sword; -typedef uint32_t Elf32_Word; -typedef uint32_t Elf32_Size; +/* ELF classes */ +#define ELFCLASS32 1 -typedef uint64_t Elf64_Addr; -typedef uint16_t Elf64_Half; -typedef uint64_t Elf64_Off; -typedef int32_t Elf64_Sword; -typedef uint32_t Elf64_Word; -typedef uint64_t Elf64_Size; +/* ELF data encodings */ +#define ELFDATA2LSB 1 -/* - * ELF header. - */ -typedef struct { - unsigned char e_ident[EI_NIDENT]; /* File identification. */ - Elf32_Half e_type; /* File type. */ - Elf32_Half e_machine; /* Machine architecture. */ - Elf32_Word e_version; /* ELF format version. */ - Elf32_Addr e_entry; /* Entry point. */ - Elf32_Off e_phoff; /* Program header file offset. */ - Elf32_Off e_shoff; /* Section header file offset. */ - Elf32_Word e_flags; /* Architecture-specific flags. */ - Elf32_Half e_ehsize; /* Size of ELF header in bytes. */ - Elf32_Half e_phentsize; /* Size of program header entry. */ - Elf32_Half e_phnum; /* Number of program header entries. */ - Elf32_Half e_shentsize; /* Size of section header entry. */ - Elf32_Half e_shnum; /* Number of section header entries. */ - Elf32_Half e_shstrndx; /* Section name strings section. */ -} Elf32_Ehdr; - -typedef struct { - unsigned char e_ident[EI_NIDENT]; /* File identification. */ - Elf64_Half e_type; /* File type. */ - Elf64_Half e_machine; /* Machine architecture. */ - Elf64_Word e_version; /* ELF format version. */ - Elf64_Addr e_entry; /* Entry point. */ - Elf64_Off e_phoff; /* Program header file offset. */ - Elf64_Off e_shoff; /* Section header file offset. */ - Elf64_Word e_flags; /* Architecture-specific flags. */ - Elf64_Half e_ehsize; /* Size of ELF header in bytes. */ - Elf64_Half e_phentsize; /* Size of program header entry. */ - Elf64_Half e_phnum; /* Number of program header entries. */ - Elf64_Half e_shentsize; /* Size of section header entry. */ - Elf64_Half e_shnum; /* Number of section header entries. */ - Elf64_Half e_shstrndx; /* Section name strings section. */ -} Elf64_Ehdr; +/* ELF versions */ +#define EV_CURRENT 1 -/* - * Program header. - */ +/** ELF program header */ typedef struct { - Elf32_Word p_type; /* Entry type. */ - Elf32_Off p_offset; /* File offset of contents. */ - Elf32_Addr p_vaddr; /* Virtual address (not used). */ - Elf32_Addr p_paddr; /* Physical address. */ - Elf32_Size p_filesz; /* Size of contents in file. */ - Elf32_Size p_memsz; /* Size of contents in memory. */ - Elf32_Word p_flags; /* Access permission flags. */ - Elf32_Size p_align; /* Alignment in memory and file. */ + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; } Elf32_Phdr; -typedef struct { - Elf64_Word p_type; /* Entry type. */ - Elf64_Word p_flags; /* Access permission flags. */ - Elf64_Off p_offset; /* File offset of contents. */ - Elf64_Addr p_vaddr; /* Virtual address (not used). */ - Elf64_Addr p_paddr; /* Physical address. */ - Elf64_Size p_filesz; /* Size of contents in file. */ - Elf64_Size p_memsz; /* Size of contents in memory. */ - Elf64_Size p_align; /* Alignment in memory and file. */ -} Elf64_Phdr; - -/* Standardized Elf image notes for booting... The name for all of these is ELFBoot */ - -#endif /* ASSEMBLY */ +/* ELF segment types */ +#define PT_LOAD 1 #endif /* ELF_H */ diff --git a/qemu/roms/ipxe/src/include/endian.h b/qemu/roms/ipxe/src/include/endian.h index 9682cf9b4..79c3163ee 100644 --- a/qemu/roms/ipxe/src/include/endian.h +++ b/qemu/roms/ipxe/src/include/endian.h @@ -1,21 +1,22 @@ -#ifndef ETHERBOOT_ENDIAN_H -#define ETHERBOOT_ENDIAN_H +#ifndef _ENDIAN_H +#define _ENDIAN_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -/* Definitions for byte order, according to significance of bytes, - from low addresses to high addresses. The value is what you get by - putting '4' in the most significant byte, '3' in the second most - significant byte, '2' in the second least significant byte, and '1' - in the least significant byte, and then writing down one digit for - each byte, starting with the byte at the lowest address at the left, - and proceeding to the byte with the highest address at the right. */ +/** Constant representing little-endian byte order + * + * Little-endian systems should define BYTE_ORDER as LITTLE_ENDIAN. + * This constant is intended to be used only at compile time. + */ +#define __LITTLE_ENDIAN 0x44332211UL -#define __LITTLE_ENDIAN 1234 -#define __BIG_ENDIAN 4321 -#define __PDP_ENDIAN 3412 +/** Constant representing big-endian byte order + * + * Big-endian systems should define BYTE_ORDER as BIG_ENDIAN. + * This constant is intended to be used only at compile time. + */ +#define __BIG_ENDIAN 0x11223344UL #include "bits/endian.h" - -#endif /* ETHERBOOT_ENDIAN_H */ +#endif /* _ENDIAN_H */ diff --git a/qemu/roms/ipxe/src/include/errno.h b/qemu/roms/ipxe/src/include/errno.h index bcc4a8816..036479aff 100644 --- a/qemu/roms/ipxe/src/include/errno.h +++ b/qemu/roms/ipxe/src/include/errno.h @@ -15,12 +15,16 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #ifndef ERRNO_H #define ERRNO_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/include/getopt.h b/qemu/roms/ipxe/src/include/getopt.h index 0fe43567e..db3de1786 100644 --- a/qemu/roms/ipxe/src/include/getopt.h +++ b/qemu/roms/ipxe/src/include/getopt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/hci/ifmgmt_cmd.h b/qemu/roms/ipxe/src/include/hci/ifmgmt_cmd.h index 913b911d8..5debf85c2 100644 --- a/qemu/roms/ipxe/src/include/hci/ifmgmt_cmd.h +++ b/qemu/roms/ipxe/src/include/hci/ifmgmt_cmd.h @@ -15,12 +15,16 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #ifndef _IFMGMT_CMD_H #define _IFMGMT_CMD_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/acpi.h b/qemu/roms/ipxe/src/include/ipxe/acpi.h index 282b6d92d..2ccd691ed 100644 --- a/qemu/roms/ipxe/src/include/ipxe/acpi.h +++ b/qemu/roms/ipxe/src/include/ipxe/acpi.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/aes.h b/qemu/roms/ipxe/src/include/ipxe/aes.h index 4e44f9853..0432e43ee 100644 --- a/qemu/roms/ipxe/src/include/ipxe/aes.h +++ b/qemu/roms/ipxe/src/include/ipxe/aes.h @@ -1,31 +1,51 @@ #ifndef _IPXE_AES_H #define _IPXE_AES_H -FILE_LICENCE ( GPL2_OR_LATER ); +/** @file + * + * AES algorithm + * + */ -struct cipher_algorithm; +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -/** Basic AES blocksize */ +#include + +/** AES blocksize */ #define AES_BLOCKSIZE 16 -#include "crypto/axtls/crypto.h" +/** Maximum number of AES rounds */ +#define AES_MAX_ROUNDS 15 + +/** AES matrix */ +union aes_matrix { + /** Viewed as an array of bytes */ + uint8_t byte[16]; + /** Viewed as an array of four-byte columns */ + uint32_t column[4]; +} __attribute__ (( packed )); + +/** AES round keys */ +struct aes_round_keys { + /** Round keys */ + union aes_matrix key[AES_MAX_ROUNDS]; +}; /** AES context */ struct aes_context { - /** AES context for AXTLS */ - AES_CTX axtls_ctx; - /** Cipher is being used for decrypting */ - int decrypting; + /** Encryption keys */ + struct aes_round_keys encrypt; + /** Decryption keys */ + struct aes_round_keys decrypt; + /** Number of rounds */ + unsigned int rounds; }; /** AES context size */ #define AES_CTX_SIZE sizeof ( struct aes_context ) -/* AXTLS functions */ -extern void axtls_aes_encrypt ( const AES_CTX *ctx, uint32_t *data ); -extern void axtls_aes_decrypt ( const AES_CTX *ctx, uint32_t *data ); - extern struct cipher_algorithm aes_algorithm; +extern struct cipher_algorithm aes_ecb_algorithm; extern struct cipher_algorithm aes_cbc_algorithm; int aes_wrap ( const void *kek, const void *src, void *dest, int nblk ); diff --git a/qemu/roms/ipxe/src/include/ipxe/ansicol.h b/qemu/roms/ipxe/src/include/ipxe/ansicol.h index 707d1599d..2b54ecaca 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ansicol.h +++ b/qemu/roms/ipxe/src/include/ipxe/ansicol.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include /* For COLOR_RED etc. */ diff --git a/qemu/roms/ipxe/src/include/ipxe/ansiesc.h b/qemu/roms/ipxe/src/include/ipxe/ansiesc.h index c1c74481d..80bc83308 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ansiesc.h +++ b/qemu/roms/ipxe/src/include/ipxe/ansiesc.h @@ -26,7 +26,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct ansiesc_context; diff --git a/qemu/roms/ipxe/src/include/ipxe/aoe.h b/qemu/roms/ipxe/src/include/ipxe/aoe.h index 60f3bd959..0c656e7c2 100644 --- a/qemu/roms/ipxe/src/include/ipxe/aoe.h +++ b/qemu/roms/ipxe/src/include/ipxe/aoe.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/api.h b/qemu/roms/ipxe/src/include/ipxe/api.h index 838b8936e..d05d3b07a 100644 --- a/qemu/roms/ipxe/src/include/ipxe/api.h +++ b/qemu/roms/ipxe/src/include/ipxe/api.h @@ -11,7 +11,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @defgroup Single-implementation APIs * diff --git a/qemu/roms/ipxe/src/include/ipxe/arp.h b/qemu/roms/ipxe/src/include/ipxe/arp.h index e30ae6b76..5822fa095 100644 --- a/qemu/roms/ipxe/src/include/ipxe/arp.h +++ b/qemu/roms/ipxe/src/include/ipxe/arp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -57,4 +57,8 @@ static inline int arp_tx ( struct io_buffer *iobuf, struct net_device *netdev, &arp_discovery, net_source, ll_source ); } +extern int arp_tx_request ( struct net_device *netdev, + struct net_protocol *net_protocol, + const void *net_dest, const void *net_source ); + #endif /* _IPXE_ARP_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/asn1.h b/qemu/roms/ipxe/src/include/ipxe/asn1.h index d12524ddb..5fbd58281 100644 --- a/qemu/roms/ipxe/src/include/ipxe/asn1.h +++ b/qemu/roms/ipxe/src/include/ipxe/asn1.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -141,6 +141,24 @@ struct asn1_builder_header { ASN1_OID_TRIPLE ( 113549 ), ASN1_OID_SINGLE ( 1 ), \ ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 11 ) +/** ASN.1 OID for sha384WithRSAEncryption (1.2.840.113549.1.1.12) */ +#define ASN1_OID_SHA384WITHRSAENCRYPTION \ + ASN1_OID_INITIAL ( 1, 2 ), ASN1_OID_DOUBLE ( 840 ), \ + ASN1_OID_TRIPLE ( 113549 ), ASN1_OID_SINGLE ( 1 ), \ + ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 12 ) + +/** ASN.1 OID for sha512WithRSAEncryption (1.2.840.113549.1.1.13) */ +#define ASN1_OID_SHA512WITHRSAENCRYPTION \ + ASN1_OID_INITIAL ( 1, 2 ), ASN1_OID_DOUBLE ( 840 ), \ + ASN1_OID_TRIPLE ( 113549 ), ASN1_OID_SINGLE ( 1 ), \ + ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 13 ) + +/** ASN.1 OID for sha224WithRSAEncryption (1.2.840.113549.1.1.14) */ +#define ASN1_OID_SHA224WITHRSAENCRYPTION \ + ASN1_OID_INITIAL ( 1, 2 ), ASN1_OID_DOUBLE ( 840 ), \ + ASN1_OID_TRIPLE ( 113549 ), ASN1_OID_SINGLE ( 1 ), \ + ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 14 ) + /** ASN.1 OID for id-md5 (1.2.840.113549.2.5) */ #define ASN1_OID_MD5 \ ASN1_OID_INITIAL ( 1, 2 ), ASN1_OID_DOUBLE ( 840 ), \ @@ -160,6 +178,41 @@ struct asn1_builder_header { ASN1_OID_SINGLE ( 3 ), ASN1_OID_SINGLE ( 4 ), \ ASN1_OID_SINGLE ( 2 ), ASN1_OID_SINGLE ( 1 ) +/** ASN.1 OID for id-sha384 (2.16.840.1.101.3.4.2.2) */ +#define ASN1_OID_SHA384 \ + ASN1_OID_INITIAL ( 2, 16 ), ASN1_OID_DOUBLE ( 840 ), \ + ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 101 ), \ + ASN1_OID_SINGLE ( 3 ), ASN1_OID_SINGLE ( 4 ), \ + ASN1_OID_SINGLE ( 2 ), ASN1_OID_SINGLE ( 2 ) + +/** ASN.1 OID for id-sha512 (2.16.840.1.101.3.4.2.3) */ +#define ASN1_OID_SHA512 \ + ASN1_OID_INITIAL ( 2, 16 ), ASN1_OID_DOUBLE ( 840 ), \ + ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 101 ), \ + ASN1_OID_SINGLE ( 3 ), ASN1_OID_SINGLE ( 4 ), \ + ASN1_OID_SINGLE ( 2 ), ASN1_OID_SINGLE ( 3 ) + +/** ASN.1 OID for id-sha224 (2.16.840.1.101.3.4.2.4) */ +#define ASN1_OID_SHA224 \ + ASN1_OID_INITIAL ( 2, 16 ), ASN1_OID_DOUBLE ( 840 ), \ + ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 101 ), \ + ASN1_OID_SINGLE ( 3 ), ASN1_OID_SINGLE ( 4 ), \ + ASN1_OID_SINGLE ( 2 ), ASN1_OID_SINGLE ( 4 ) + +/** ASN.1 OID for id-sha512-224 (2.16.840.1.101.3.4.2.5) */ +#define ASN1_OID_SHA512_224 \ + ASN1_OID_INITIAL ( 2, 16 ), ASN1_OID_DOUBLE ( 840 ), \ + ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 101 ), \ + ASN1_OID_SINGLE ( 3 ), ASN1_OID_SINGLE ( 4 ), \ + ASN1_OID_SINGLE ( 2 ), ASN1_OID_SINGLE ( 5 ) + +/** ASN.1 OID for id-sha512-256 (2.16.840.1.101.3.4.2.6) */ +#define ASN1_OID_SHA512_256 \ + ASN1_OID_INITIAL ( 2, 16 ), ASN1_OID_DOUBLE ( 840 ), \ + ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 101 ), \ + ASN1_OID_SINGLE ( 3 ), ASN1_OID_SINGLE ( 4 ), \ + ASN1_OID_SINGLE ( 2 ), ASN1_OID_SINGLE ( 6 ) + /** ASN.1 OID for commonName (2.5.4.3) */ #define ASN1_OID_COMMON_NAME \ ASN1_OID_INITIAL ( 2, 5 ), ASN1_OID_SINGLE ( 4 ), \ diff --git a/qemu/roms/ipxe/src/include/ipxe/ata.h b/qemu/roms/ipxe/src/include/ipxe/ata.h index b7f02d655..a10cfafcc 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ata.h +++ b/qemu/roms/ipxe/src/include/ipxe/ata.h @@ -11,7 +11,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * An ATA Logical Block Address diff --git a/qemu/roms/ipxe/src/include/ipxe/base16.h b/qemu/roms/ipxe/src/include/ipxe/base16.h index 60e3f2315..8c44da17e 100644 --- a/qemu/roms/ipxe/src/include/ipxe/base16.h +++ b/qemu/roms/ipxe/src/include/ipxe/base16.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -32,9 +32,36 @@ static inline size_t base16_decoded_max_len ( const char *encoded ) { return ( ( strlen ( encoded ) + 1 ) / 2 ); } -extern void base16_encode ( const uint8_t *raw, size_t len, char *encoded ); -extern int hex_decode ( const char *string, char separator, void *data, +extern size_t hex_encode ( char separator, const void *raw, size_t raw_len, + char *data, size_t len ); +extern int hex_decode ( char separator, const char *encoded, void *data, size_t len ); -extern int base16_decode ( const char *encoded, uint8_t *raw ); + +/** + * Base16-encode data + * + * @v raw Raw data + * @v raw_len Length of raw data + * @v data Buffer + * @v len Length of buffer + * @ret len Encoded length + */ +static inline __attribute__ (( always_inline )) size_t +base16_encode ( const void *raw, size_t raw_len, char *data, size_t len ) { + return hex_encode ( 0, raw, raw_len, data, len ); +} + +/** + * Base16-decode data + * + * @v encoded Encoded string + * @v data Buffer + * @v len Length of buffer + * @ret len Length of data, or negative error + */ +static inline __attribute__ (( always_inline )) int +base16_decode ( const char *encoded, void *data, size_t len ) { + return hex_decode ( 0, encoded, data, len ); +} #endif /* _IPXE_BASE16_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/base64.h b/qemu/roms/ipxe/src/include/ipxe/base64.h index 5fe134dc8..0c70d8382 100644 --- a/qemu/roms/ipxe/src/include/ipxe/base64.h +++ b/qemu/roms/ipxe/src/include/ipxe/base64.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -35,7 +35,8 @@ static inline size_t base64_decoded_max_len ( const char *encoded ) { return ( ( ( strlen ( encoded ) + 4 - 1 ) / 4 ) * 3 ); } -extern void base64_encode ( const uint8_t *raw, size_t len, char *encoded ); -extern int base64_decode ( const char *encoded, uint8_t *raw ); +extern size_t base64_encode ( const void *raw, size_t raw_len, char *data, + size_t len ); +extern int base64_decode ( const char *encoded, void *data, size_t len ); #endif /* _IPXE_BASE64_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/bigint.h b/qemu/roms/ipxe/src/include/ipxe/bigint.h index 97fbce245..2f99f8445 100644 --- a/qemu/roms/ipxe/src/include/ipxe/bigint.h +++ b/qemu/roms/ipxe/src/include/ipxe/bigint.h @@ -6,7 +6,7 @@ * Big integer support */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Define a big-integer type diff --git a/qemu/roms/ipxe/src/include/ipxe/bitbash.h b/qemu/roms/ipxe/src/include/ipxe/bitbash.h index 69d5d9e3e..2a2e475d0 100644 --- a/qemu/roms/ipxe/src/include/ipxe/bitbash.h +++ b/qemu/roms/ipxe/src/include/ipxe/bitbash.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct bit_basher; diff --git a/qemu/roms/ipxe/src/include/ipxe/bitmap.h b/qemu/roms/ipxe/src/include/ipxe/bitmap.h index b18584c1f..38aca694b 100644 --- a/qemu/roms/ipxe/src/include/ipxe/bitmap.h +++ b/qemu/roms/ipxe/src/include/ipxe/bitmap.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/bitops.h b/qemu/roms/ipxe/src/include/ipxe/bitops.h index 73e859f41..220ab0fe7 100644 --- a/qemu/roms/ipxe/src/include/ipxe/bitops.h +++ b/qemu/roms/ipxe/src/include/ipxe/bitops.h @@ -18,9 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/include/ipxe/blockdev.h b/qemu/roms/ipxe/src/include/ipxe/blockdev.h index 9f0a9f787..418c43004 100644 --- a/qemu/roms/ipxe/src/include/ipxe/blockdev.h +++ b/qemu/roms/ipxe/src/include/ipxe/blockdev.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/blocktrans.h b/qemu/roms/ipxe/src/include/ipxe/blocktrans.h new file mode 100644 index 000000000..fee71b96c --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/blocktrans.h @@ -0,0 +1,38 @@ +#ifndef _IPXE_BLOCKTRANS_H +#define _IPXE_BLOCKTRANS_H + +/** @file + * + * Block device translator + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include + +/** A block device translator */ +struct block_translator { + /** Reference count */ + struct refcnt refcnt; + /** Block device interface */ + struct interface block; + /** Data transfer interface */ + struct interface xfer; + + /** Data transfer buffer */ + struct xfer_buffer xferbuf; + /** Data buffer */ + userptr_t buffer; + /** Block size */ + size_t blksize; +}; + +extern int block_translate ( struct interface *block, + userptr_t buffer, size_t size ); + +#endif /* _IPXE_BLOCKTRANS_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/bofm.h b/qemu/roms/ipxe/src/include/ipxe/bofm.h index 1da47f651..bc994ea8b 100644 --- a/qemu/roms/ipxe/src/include/ipxe/bofm.h +++ b/qemu/roms/ipxe/src/include/ipxe/bofm.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/cbc.h b/qemu/roms/ipxe/src/include/ipxe/cbc.h index fae376577..18a94e144 100644 --- a/qemu/roms/ipxe/src/include/ipxe/cbc.h +++ b/qemu/roms/ipxe/src/include/ipxe/cbc.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/cdc.h b/qemu/roms/ipxe/src/include/ipxe/cdc.h new file mode 100644 index 000000000..f1799cd9a --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/cdc.h @@ -0,0 +1,55 @@ +#ifndef _IPXE_CDC_H +#define _IPXE_CDC_H + +/** @file + * + * USB Communications Device Class (CDC) + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** Class code for communications devices */ +#define USB_CLASS_CDC 2 + +/** Union functional descriptor */ +struct cdc_union_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** Descriptor subtype */ + uint8_t subtype; + /** Interfaces (variable-length) */ + uint8_t interface[1]; +} __attribute__ (( packed )); + +/** Union functional descriptor subtype */ +#define CDC_SUBTYPE_UNION 6 + +/** Ethernet descriptor subtype */ +#define CDC_SUBTYPE_ETHERNET 15 + +/** Network connection notification */ +#define CDC_NETWORK_CONNECTION \ + ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x00 ) ) + +/** Connection speed change notification */ +#define CDC_CONNECTION_SPEED_CHANGE \ + ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x2a ) ) + +/** Connection speed change notification */ +struct cdc_connection_speed_change { + /** Downlink bit rate, in bits per second */ + uint32_t down; + /** Uplink bit rate, in bits per second */ + uint32_t up; +} __attribute__ (( packed )); + +extern struct cdc_union_descriptor * +cdc_union_descriptor ( struct usb_configuration_descriptor *config, + struct usb_interface_descriptor *interface ); + +#endif /* _IPXE_CDC_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/certstore.h b/qemu/roms/ipxe/src/include/ipxe/certstore.h index 7456db621..49b3b512c 100644 --- a/qemu/roms/ipxe/src/include/ipxe/certstore.h +++ b/qemu/roms/ipxe/src/include/ipxe/certstore.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/chap.h b/qemu/roms/ipxe/src/include/ipxe/chap.h index fce48f3ea..7c693e29d 100644 --- a/qemu/roms/ipxe/src/include/ipxe/chap.h +++ b/qemu/roms/ipxe/src/include/ipxe/chap.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/cms.h b/qemu/roms/ipxe/src/include/ipxe/cms.h index e026ebd2f..7adf724b2 100644 --- a/qemu/roms/ipxe/src/include/ipxe/cms.h +++ b/qemu/roms/ipxe/src/include/ipxe/cms.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/command.h b/qemu/roms/ipxe/src/include/ipxe/command.h index 432da1abb..a208e7d8f 100644 --- a/qemu/roms/ipxe/src/include/ipxe/command.h +++ b/qemu/roms/ipxe/src/include/ipxe/command.h @@ -1,7 +1,7 @@ #ifndef _IPXE_COMMAND_H #define _IPXE_COMMAND_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/console.h b/qemu/roms/ipxe/src/include/ipxe/console.h index 4b90c9cec..1b764aaca 100644 --- a/qemu/roms/ipxe/src/include/ipxe/console.h +++ b/qemu/roms/ipxe/src/include/ipxe/console.h @@ -16,7 +16,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct pixel_buffer; diff --git a/qemu/roms/ipxe/src/include/ipxe/cpio.h b/qemu/roms/ipxe/src/include/ipxe/cpio.h index 277232808..0637c531d 100644 --- a/qemu/roms/ipxe/src/include/ipxe/cpio.h +++ b/qemu/roms/ipxe/src/include/ipxe/cpio.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** A CPIO archive header * diff --git a/qemu/roms/ipxe/src/include/ipxe/crc32.h b/qemu/roms/ipxe/src/include/ipxe/crc32.h index 38ac1b31f..30d2fe66c 100644 --- a/qemu/roms/ipxe/src/include/ipxe/crc32.h +++ b/qemu/roms/ipxe/src/include/ipxe/crc32.h @@ -1,7 +1,7 @@ #ifndef _IPXE_CRC32_H #define _IPXE_CRC32_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/crypto.h b/qemu/roms/ipxe/src/include/ipxe/crypto.h index 3eda5ec6e..fc0d8b22b 100644 --- a/qemu/roms/ipxe/src/include/ipxe/crypto.h +++ b/qemu/roms/ipxe/src/include/ipxe/crypto.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/deflate.h b/qemu/roms/ipxe/src/include/ipxe/deflate.h index 19c5125eb..b751aa9a3 100644 --- a/qemu/roms/ipxe/src/include/ipxe/deflate.h +++ b/qemu/roms/ipxe/src/include/ipxe/deflate.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/device.h b/qemu/roms/ipxe/src/include/ipxe/device.h index 7202a6966..d81417e8e 100644 --- a/qemu/roms/ipxe/src/include/ipxe/device.h +++ b/qemu/roms/ipxe/src/include/ipxe/device.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -63,10 +63,16 @@ struct device_description { /** Xen bus type */ #define BUS_TYPE_XEN 8 +/** Hyper-V bus type */ +#define BUS_TYPE_HV 9 + +/** USB bus type */ +#define BUS_TYPE_USB 10 + /** A hardware device */ struct device { /** Name */ - char name[16]; + char name[32]; /** Driver name */ const char *driver_name; /** Device description */ @@ -93,6 +99,8 @@ struct root_device { struct device dev; /** Root device driver */ struct root_driver *driver; + /** Driver-private data */ + void *priv; }; /** A root device driver */ @@ -123,6 +131,27 @@ struct root_driver { /** Declare a root device */ #define __root_device __table_entry ( ROOT_DEVICES, 01 ) +/** + * Set root device driver-private data + * + * @v rootdev Root device + * @v priv Private data + */ +static inline void rootdev_set_drvdata ( struct root_device *rootdev, + void *priv ){ + rootdev->priv = priv; +} + +/** + * Get root device driver-private data + * + * @v rootdev Root device + * @ret priv Private data + */ +static inline void * rootdev_get_drvdata ( struct root_device *rootdev ) { + return rootdev->priv; +} + extern int device_keep_count; /** diff --git a/qemu/roms/ipxe/src/include/ipxe/dhcp.h b/qemu/roms/ipxe/src/include/ipxe/dhcp.h index bcfb85cc1..a11db3497 100644 --- a/qemu/roms/ipxe/src/include/ipxe/dhcp.h +++ b/qemu/roms/ipxe/src/include/ipxe/dhcp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -639,16 +639,6 @@ struct dhcphdr { */ #define DHCP_MIN_LEN 552 -/** Timeouts for sending DHCP packets */ -#define DHCP_MIN_TIMEOUT ( 1 * TICKS_PER_SEC ) -#define DHCP_MAX_TIMEOUT ( 10 * TICKS_PER_SEC ) - -/** Maximum time that we will wait for ProxyDHCP responses */ -#define PROXYDHCP_MAX_TIMEOUT ( 2 * TICKS_PER_SEC ) - -/** Maximum time that we will wait for Boot Server responses */ -#define PXEBS_MAX_TIMEOUT ( 3 * TICKS_PER_SEC ) - /** Settings block name used for DHCP responses */ #define DHCP_SETTINGS_NAME "dhcp" diff --git a/qemu/roms/ipxe/src/include/ipxe/dhcpopts.h b/qemu/roms/ipxe/src/include/ipxe/dhcpopts.h index c5af5d749..707fda4a8 100644 --- a/qemu/roms/ipxe/src/include/ipxe/dhcpopts.h +++ b/qemu/roms/ipxe/src/include/ipxe/dhcpopts.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/dhcppkt.h b/qemu/roms/ipxe/src/include/ipxe/dhcppkt.h index 3179a6bb0..f13dfc93d 100644 --- a/qemu/roms/ipxe/src/include/ipxe/dhcppkt.h +++ b/qemu/roms/ipxe/src/include/ipxe/dhcppkt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/dhcpv6.h b/qemu/roms/ipxe/src/include/ipxe/dhcpv6.h index 2636b8ab2..9307b6cae 100644 --- a/qemu/roms/ipxe/src/include/ipxe/dhcpv6.h +++ b/qemu/roms/ipxe/src/include/ipxe/dhcpv6.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/dns.h b/qemu/roms/ipxe/src/include/ipxe/dns.h index 4f6cab3a4..738dea6e4 100644 --- a/qemu/roms/ipxe/src/include/ipxe/dns.h +++ b/qemu/roms/ipxe/src/include/ipxe/dns.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/downloader.h b/qemu/roms/ipxe/src/include/ipxe/downloader.h index de1a2e75e..ccb1abfef 100644 --- a/qemu/roms/ipxe/src/include/ipxe/downloader.h +++ b/qemu/roms/ipxe/src/include/ipxe/downloader.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct interface; struct image; diff --git a/qemu/roms/ipxe/src/include/ipxe/drbg.h b/qemu/roms/ipxe/src/include/ipxe/drbg.h index 6374e7787..ed2b3757a 100644 --- a/qemu/roms/ipxe/src/include/ipxe/drbg.h +++ b/qemu/roms/ipxe/src/include/ipxe/drbg.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/ecb.h b/qemu/roms/ipxe/src/include/ipxe/ecb.h new file mode 100644 index 000000000..4e6aa3c81 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/ecb.h @@ -0,0 +1,55 @@ +#ifndef _IPXE_ECB_H +#define _IPXE_ECB_H + +/** @file + * + * Electronic codebook (ECB) + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +extern void ecb_encrypt ( void *ctx, const void *src, void *dst, + size_t len, struct cipher_algorithm *raw_cipher ); +extern void ecb_decrypt ( void *ctx, const void *src, void *dst, + size_t len, struct cipher_algorithm *raw_cipher ); + +/** + * Create a cipher-block chaining mode of behaviour of an existing cipher + * + * @v _ecb_name Name for the new ECB cipher + * @v _ecb_cipher New cipher algorithm + * @v _raw_cipher Underlying cipher algorithm + * @v _raw_context Context structure for the underlying cipher + * @v _blocksize Cipher block size + */ +#define ECB_CIPHER( _ecb_name, _ecb_cipher, _raw_cipher, _raw_context, \ + _blocksize ) \ +static int _ecb_name ## _setkey ( void *ctx, const void *key, \ + size_t keylen ) { \ + return cipher_setkey ( &_raw_cipher, ctx, key, keylen ); \ +} \ +static void _ecb_name ## _setiv ( void *ctx, const void *iv ) { \ + cipher_setiv ( &_raw_cipher, ctx, iv ); \ +} \ +static void _ecb_name ## _encrypt ( void *ctx, const void *src, \ + void *dst, size_t len ) { \ + ecb_encrypt ( ctx, src, dst, len, &_raw_cipher ); \ +} \ +static void _ecb_name ## _decrypt ( void *ctx, const void *src, \ + void *dst, size_t len ) { \ + ecb_decrypt ( ctx, src, dst, len, &_raw_cipher ); \ +} \ +struct cipher_algorithm _ecb_cipher = { \ + .name = #_ecb_name, \ + .ctxsize = sizeof ( _raw_context ), \ + .blocksize = _blocksize, \ + .setkey = _ecb_name ## _setkey, \ + .setiv = _ecb_name ## _setiv, \ + .encrypt = _ecb_name ## _encrypt, \ + .decrypt = _ecb_name ## _decrypt, \ +}; + +#endif /* _IPXE_ECB_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/edd.h b/qemu/roms/ipxe/src/include/ipxe/edd.h index 0c25593d5..1914fd0b0 100644 --- a/qemu/roms/ipxe/src/include/ipxe/edd.h +++ b/qemu/roms/ipxe/src/include/ipxe/edd.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/editbox.h b/qemu/roms/ipxe/src/include/ipxe/editbox.h index 9122dbbf3..2c70e0b6b 100644 --- a/qemu/roms/ipxe/src/include/ipxe/editbox.h +++ b/qemu/roms/ipxe/src/include/ipxe/editbox.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/editstring.h b/qemu/roms/ipxe/src/include/ipxe/editstring.h index 2ef546a63..a00a8adaa 100644 --- a/qemu/roms/ipxe/src/include/ipxe/editstring.h +++ b/qemu/roms/ipxe/src/include/ipxe/editstring.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** An editable string */ struct edit_string { diff --git a/qemu/roms/ipxe/src/include/ipxe/efi/ProcessorBind.h b/qemu/roms/ipxe/src/include/ipxe/efi/ProcessorBind.h index 1294459f9..7466814fa 100644 --- a/qemu/roms/ipxe/src/include/ipxe/efi/ProcessorBind.h +++ b/qemu/roms/ipxe/src/include/ipxe/efi/ProcessorBind.h @@ -1,7 +1,7 @@ #ifndef _IPXE_EFI_PROCESSOR_BIND_H #define _IPXE_EFI_PROCESSOR_BIND_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* * EFI header files rely on having the CPU architecture directory diff --git a/qemu/roms/ipxe/src/include/ipxe/efi/Protocol/Rng.h b/qemu/roms/ipxe/src/include/ipxe/efi/Protocol/Rng.h new file mode 100644 index 000000000..f04efbb03 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/efi/Protocol/Rng.h @@ -0,0 +1,158 @@ +/** @file + EFI_RNG_PROTOCOL as defined in UEFI 2.4. + The UEFI Random Number Generator Protocol is used to provide random bits for use + in applications, or entropy for seeding other random number generators. + +Copyright (c) 2013, Intel Corporation. All rights reserved.
      +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_RNG_PROTOCOL_H__ +#define __EFI_RNG_PROTOCOL_H__ + +FILE_LICENCE ( BSD3 ); + +/// +/// Global ID for the Random Number Generator Protocol +/// +#define EFI_RNG_PROTOCOL_GUID \ + { \ + 0x3152bca5, 0xeade, 0x433d, {0x86, 0x2e, 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44 } \ + } + +typedef struct _EFI_RNG_PROTOCOL EFI_RNG_PROTOCOL; + +/// +/// A selection of EFI_RNG_PROTOCOL algorithms. +/// The algorithms listed are optional, not meant to be exhaustive and be argmented by +/// vendors or other industry standards. +/// + +typedef EFI_GUID EFI_RNG_ALGORITHM; + +/// +/// The algorithms corresponds to SP800-90 as defined in +/// NIST SP 800-90, "Recommendation for Random Number Generation Using Deterministic Random +/// Bit Generators", March 2007. +/// +#define EFI_RNG_ALGORITHM_SP800_90_HASH_256_GUID \ + { \ + 0xa7af67cb, 0x603b, 0x4d42, {0xba, 0x21, 0x70, 0xbf, 0xb6, 0x29, 0x3f, 0x96 } \ + } +#define EFI_RNG_ALGORITHM_SP800_90_HMAC_256_GUID \ + { \ + 0xc5149b43, 0xae85, 0x4f53, {0x99, 0x82, 0xb9, 0x43, 0x35, 0xd3, 0xa9, 0xe7 } \ + } +#define EFI_RNG_ALGORITHM_SP800_90_CTR_256_GUID \ + { \ + 0x44f0de6e, 0x4d8c, 0x4045, {0xa8, 0xc7, 0x4d, 0xd1, 0x68, 0x85, 0x6b, 0x9e } \ + } +/// +/// The algorithms correspond to X9.31 as defined in +/// NIST, "Recommended Random Number Generator Based on ANSI X9.31 Appendix A.2.4 Using +/// the 3-Key Triple DES and AES Algorithm", January 2005. +/// +#define EFI_RNG_ALGORITHM_X9_31_3DES_GUID \ + { \ + 0x63c4785a, 0xca34, 0x4012, {0xa3, 0xc8, 0x0b, 0x6a, 0x32, 0x4f, 0x55, 0x46 } \ + } +#define EFI_RNG_ALGORITHM_X9_31_AES_GUID \ + { \ + 0xacd03321, 0x777e, 0x4d3d, {0xb1, 0xc8, 0x20, 0xcf, 0xd8, 0x88, 0x20, 0xc9 } \ + } +/// +/// The "raw" algorithm, when supported, is intended to provide entropy directly from +/// the source, without it going through some deterministic random bit generator. +/// +#define EFI_RNG_ALGORITHM_RAW \ + { \ + 0xe43176d7, 0xb6e8, 0x4827, {0xb7, 0x84, 0x7f, 0xfd, 0xc4, 0xb6, 0x85, 0x61 } \ + } + +/** + Returns information about the random number generation implementation. + + @param[in] This A pointer to the EFI_RNG_PROTOCOL instance. + @param[in,out] RNGAlgorithmListSize On input, the size in bytes of RNGAlgorithmList. + On output with a return code of EFI_SUCCESS, the size + in bytes of the data returned in RNGAlgorithmList. On output + with a return code of EFI_BUFFER_TOO_SMALL, + the size of RNGAlgorithmList required to obtain the list. + @param[out] RNGAlgorithmList A caller-allocated memory buffer filled by the driver + with one EFI_RNG_ALGORITHM element for each supported + RNG algorithm. The list must not change across multiple + calls to the same driver. The first algorithm in the list + is the default algorithm for the driver. + + @retval EFI_SUCCESS The RNG algorithm list was returned successfully. + @retval EFI_UNSUPPORTED The services is not supported by this driver. + @retval EFI_DEVICE_ERROR The list of algorithms could not be retrieved due to a + hardware or firmware error. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. + @retval EFI_BUFFER_TOO_SMALL The buffer RNGAlgorithmList is too small to hold the result. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_RNG_GET_INFO) ( + IN EFI_RNG_PROTOCOL *This, + IN OUT UINTN *RNGAlgorithmListSize, + OUT EFI_RNG_ALGORITHM *RNGAlgorithmList + ); + +/** + Produces and returns an RNG value using either the default or specified RNG algorithm. + + @param[in] This A pointer to the EFI_RNG_PROTOCOL instance. + @param[in] RNGAlgorithm A pointer to the EFI_RNG_ALGORITHM that identifies the RNG + algorithm to use. May be NULL in which case the function will + use its default RNG algorithm. + @param[in] RNGValueLength The length in bytes of the memory buffer pointed to by + RNGValue. The driver shall return exactly this numbers of bytes. + @param[out] RNGValue A caller-allocated memory buffer filled by the driver with the + resulting RNG value. + + @retval EFI_SUCCESS The RNG value was returned successfully. + @retval EFI_UNSUPPORTED The algorithm specified by RNGAlgorithm is not supported by + this driver. + @retval EFI_DEVICE_ERROR An RNG value could not be retrieved due to a hardware or + firmware error. + @retval EFI_NOT_READY There is not enough random data available to satisfy the length + requested by RNGValueLength. + @retval EFI_INVALID_PARAMETER RNGValue is NULL or RNGValueLength is zero. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_RNG_GET_RNG) ( + IN EFI_RNG_PROTOCOL *This, + IN EFI_RNG_ALGORITHM *RNGAlgorithm, OPTIONAL + IN UINTN RNGValueLength, + OUT UINT8 *RNGValue + ); + +/// +/// The Random Number Generator (RNG) protocol provides random bits for use in +/// applications, or entropy for seeding other random number generators. +/// +struct _EFI_RNG_PROTOCOL { + EFI_RNG_GET_INFO GetInfo; + EFI_RNG_GET_RNG GetRNG; +}; + +extern EFI_GUID gEfiRngProtocolGuid; +extern EFI_GUID gEfiRngAlgorithmSp80090Hash256Guid; +extern EFI_GUID gEfiRngAlgorithmSp80090Hmac256Guid; +extern EFI_GUID gEfiRngAlgorithmSp80090Ctr256Guid; +extern EFI_GUID gEfiRngAlgorithmX9313DesGuid; +extern EFI_GUID gEfiRngAlgorithmX931AesGuid; +extern EFI_GUID gEfiRngAlgorithmRaw; + +#endif diff --git a/qemu/roms/ipxe/src/include/ipxe/efi/efi_autoboot.h b/qemu/roms/ipxe/src/include/ipxe/efi/efi_autoboot.h index d4a26850c..1d5ddc8c3 100644 --- a/qemu/roms/ipxe/src/include/ipxe/efi/efi_autoboot.h +++ b/qemu/roms/ipxe/src/include/ipxe/efi/efi_autoboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern void efi_set_autoboot ( void ); diff --git a/qemu/roms/ipxe/src/include/ipxe/efi/efi_driver.h b/qemu/roms/ipxe/src/include/ipxe/efi/efi_driver.h index e16a24daa..f497df3e3 100644 --- a/qemu/roms/ipxe/src/include/ipxe/efi/efi_driver.h +++ b/qemu/roms/ipxe/src/include/ipxe/efi/efi_driver.h @@ -6,7 +6,7 @@ * EFI driver interface */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/efi/efi_entropy.h b/qemu/roms/ipxe/src/include/ipxe/efi/efi_entropy.h new file mode 100644 index 000000000..39a667355 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/efi/efi_entropy.h @@ -0,0 +1,35 @@ +#ifndef _IPXE_EFI_ENTROPY_H +#define _IPXE_EFI_ENTROPY_H + +/** @file + * + * EFI entropy source + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +#ifdef ENTROPY_EFI +#define ENTROPY_PREFIX_efi +#else +#define ENTROPY_PREFIX_efi __efi_ +#endif + +/** + * min-entropy per sample + * + * @ret min_entropy min-entropy of each sample + */ +static inline __always_inline double +ENTROPY_INLINE ( efi, min_entropy_per_sample ) ( void ) { + + /* We use essentially the same mechanism as for the BIOS + * RTC-based entropy source, and so assume the same + * min-entropy per sample. + */ + return 1.3; +} + +#endif /* _IPXE_EFI_ENTROPY_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/efi/efi_hii.h b/qemu/roms/ipxe/src/include/ipxe/efi/efi_hii.h index 8e94bbe7e..bbec31194 100644 --- a/qemu/roms/ipxe/src/include/ipxe/efi/efi_hii.h +++ b/qemu/roms/ipxe/src/include/ipxe/efi/efi_hii.h @@ -6,7 +6,7 @@ * EFI human interface infrastructure */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/efi/efi_pci.h b/qemu/roms/ipxe/src/include/ipxe/efi/efi_pci.h index af36613d9..6dd945f05 100644 --- a/qemu/roms/ipxe/src/include/ipxe/efi/efi_pci.h +++ b/qemu/roms/ipxe/src/include/ipxe/efi/efi_pci.h @@ -6,7 +6,7 @@ * EFI driver interface */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/efi/efi_pci_api.h b/qemu/roms/ipxe/src/include/ipxe/efi/efi_pci_api.h index 498a0388b..887d5ee14 100644 --- a/qemu/roms/ipxe/src/include/ipxe/efi/efi_pci_api.h +++ b/qemu/roms/ipxe/src/include/ipxe/efi/efi_pci_api.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef PCIAPI_EFI #define PCIAPI_PREFIX_efi diff --git a/qemu/roms/ipxe/src/include/ipxe/efi/efi_reboot.h b/qemu/roms/ipxe/src/include/ipxe/efi/efi_reboot.h index 33921b913..249cae8c5 100644 --- a/qemu/roms/ipxe/src/include/ipxe/efi/efi_reboot.h +++ b/qemu/roms/ipxe/src/include/ipxe/efi/efi_reboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef REBOOT_EFI #define REBOOT_PREFIX_efi diff --git a/qemu/roms/ipxe/src/include/ipxe/efi/efi_smbios.h b/qemu/roms/ipxe/src/include/ipxe/efi/efi_smbios.h index 7642e5bc5..d890d5460 100644 --- a/qemu/roms/ipxe/src/include/ipxe/efi/efi_smbios.h +++ b/qemu/roms/ipxe/src/include/ipxe/efi/efi_smbios.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef SMBIOS_EFI #define SMBIOS_PREFIX_efi diff --git a/qemu/roms/ipxe/src/include/ipxe/efi/efi_snp.h b/qemu/roms/ipxe/src/include/ipxe/efi/efi_snp.h index a18bced5f..1e5c66626 100644 --- a/qemu/roms/ipxe/src/include/ipxe/efi/efi_snp.h +++ b/qemu/roms/ipxe/src/include/ipxe/efi/efi_snp.h @@ -18,6 +18,9 @@ #include #include +/** SNP transmit completion ring size */ +#define EFI_SNP_NUM_TX 32 + /** An SNP device */ struct efi_snp_device { /** List of SNP devices */ @@ -34,20 +37,16 @@ struct efi_snp_device { EFI_SIMPLE_NETWORK_MODE mode; /** Started flag */ int started; - /** Outstanding TX packet count (via "interrupt status") - * - * Used in order to generate TX completions. - */ - unsigned int tx_count_interrupts; - /** Outstanding TX packet count (via "recycled tx buffers") - * - * Used in order to generate TX completions. - */ - unsigned int tx_count_txbufs; - /** Outstanding RX packet count (via "interrupt status") */ - unsigned int rx_count_interrupts; - /** Outstanding RX packet count (via WaitForPacket event) */ - unsigned int rx_count_events; + /** Pending interrupt status */ + unsigned int interrupts; + /** Transmit completion ring */ + VOID *tx[EFI_SNP_NUM_TX]; + /** Transmit completion ring producer counter */ + unsigned int tx_prod; + /** Transmit completion ring consumer counter */ + unsigned int tx_cons; + /** Receive queue */ + struct list_head rx; /** The network interface identifier */ EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL nii; /** Component name protocol */ diff --git a/qemu/roms/ipxe/src/include/ipxe/efi/efi_strings.h b/qemu/roms/ipxe/src/include/ipxe/efi/efi_strings.h index 023ccda07..2f241537e 100644 --- a/qemu/roms/ipxe/src/include/ipxe/efi/efi_strings.h +++ b/qemu/roms/ipxe/src/include/ipxe/efi/efi_strings.h @@ -6,7 +6,7 @@ * EFI strings */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/efi/efi_time.h b/qemu/roms/ipxe/src/include/ipxe/efi/efi_time.h new file mode 100644 index 000000000..099994b57 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/efi/efi_time.h @@ -0,0 +1,20 @@ +#ifndef _IPXE_EFI_TIME_H +#define _IPXE_EFI_TIME_H + +/** @file + * + * EFI time source + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +#ifdef TIME_EFI +#define TIME_PREFIX_efi +#else +#define TIME_PREFIX_efi __efi_ +#endif + +#endif /* _IPXE_EFI_TIME_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/efi/efi_timer.h b/qemu/roms/ipxe/src/include/ipxe/efi/efi_timer.h index b10543d6c..c03765393 100644 --- a/qemu/roms/ipxe/src/include/ipxe/efi/efi_timer.h +++ b/qemu/roms/ipxe/src/include/ipxe/efi/efi_timer.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef TIMER_EFI #define TIMER_PREFIX_efi diff --git a/qemu/roms/ipxe/src/include/ipxe/efi/efi_uaccess.h b/qemu/roms/ipxe/src/include/ipxe/efi/efi_uaccess.h index 870a089b2..3cc750405 100644 --- a/qemu/roms/ipxe/src/include/ipxe/efi/efi_uaccess.h +++ b/qemu/roms/ipxe/src/include/ipxe/efi/efi_uaccess.h @@ -10,7 +10,7 @@ * no-ops. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef UACCESS_EFI #define UACCESS_PREFIX_efi diff --git a/qemu/roms/ipxe/src/include/ipxe/efi/efi_umalloc.h b/qemu/roms/ipxe/src/include/ipxe/efi/efi_umalloc.h index 911e69a96..4eb2a5f9b 100644 --- a/qemu/roms/ipxe/src/include/ipxe/efi/efi_umalloc.h +++ b/qemu/roms/ipxe/src/include/ipxe/efi/efi_umalloc.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef UMALLOC_EFI #define UMALLOC_PREFIX_efi diff --git a/qemu/roms/ipxe/src/include/ipxe/efi/efi_utils.h b/qemu/roms/ipxe/src/include/ipxe/efi/efi_utils.h index 9164be190..57268daf7 100644 --- a/qemu/roms/ipxe/src/include/ipxe/efi/efi_utils.h +++ b/qemu/roms/ipxe/src/include/ipxe/efi/efi_utils.h @@ -6,7 +6,7 @@ * EFI utilities */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/efi/efi_watchdog.h b/qemu/roms/ipxe/src/include/ipxe/efi/efi_watchdog.h new file mode 100644 index 000000000..4a56b9a29 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/efi/efi_watchdog.h @@ -0,0 +1,31 @@ +#ifndef _IPXE_EFI_WATCHDOG_H +#define _IPXE_EFI_WATCHDOG_H + +/** @file + * + * EFI watchdog holdoff timer + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +extern struct retry_timer efi_watchdog; + +/** + * Start EFI watchdog holdoff timer + * + */ +static inline void efi_watchdog_start ( void ) { + + start_timer_nodelay ( &efi_watchdog ); +} + +/** + * Stop EFI watchdog holdoff timer + * + */ +static inline void efi_watchdog_stop ( void ) { + + stop_timer ( &efi_watchdog ); +} + +#endif /* _IPXE_EFI_WATCHDOG_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/efi/efi_wrap.h b/qemu/roms/ipxe/src/include/ipxe/efi/efi_wrap.h index 7579e0fe9..d8ed1a5cc 100644 --- a/qemu/roms/ipxe/src/include/ipxe/efi/efi_wrap.h +++ b/qemu/roms/ipxe/src/include/ipxe/efi/efi_wrap.h @@ -6,7 +6,7 @@ * EFI driver interface */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/eisa.h b/qemu/roms/ipxe/src/include/ipxe/eisa.h index 22a1ed94e..e7dac1f39 100644 --- a/qemu/roms/ipxe/src/include/ipxe/eisa.h +++ b/qemu/roms/ipxe/src/include/ipxe/eisa.h @@ -1,7 +1,7 @@ #ifndef EISA_H #define EISA_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/elf.h b/qemu/roms/ipxe/src/include/ipxe/elf.h index ec675c047..033c3f7a8 100644 --- a/qemu/roms/ipxe/src/include/ipxe/elf.h +++ b/qemu/roms/ipxe/src/include/ipxe/elf.h @@ -8,10 +8,21 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include +#include #include +typedef Elf32_Ehdr Elf_Ehdr; +typedef Elf32_Phdr Elf_Phdr; +typedef Elf32_Off Elf_Off; +#define ELFCLASS ELFCLASS32 + +extern int elf_segments ( struct image *image, Elf_Ehdr *ehdr, + int ( * process ) ( struct image *image, + Elf_Phdr *phdr, physaddr_t dest ), + physaddr_t *entry, physaddr_t *max ); extern int elf_load ( struct image *image, physaddr_t *entry, physaddr_t *max ); #endif /* _IPXE_ELF_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/eltorito.h b/qemu/roms/ipxe/src/include/ipxe/eltorito.h index 3302b38b6..27e361b16 100644 --- a/qemu/roms/ipxe/src/include/ipxe/eltorito.h +++ b/qemu/roms/ipxe/src/include/ipxe/eltorito.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/entropy.h b/qemu/roms/ipxe/src/include/ipxe/entropy.h index adf325e79..beeb3abfa 100644 --- a/qemu/roms/ipxe/src/include/ipxe/entropy.h +++ b/qemu/roms/ipxe/src/include/ipxe/entropy.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -54,6 +54,7 @@ typedef uint8_t entropy_sample_t; /* Include all architecture-independent entropy API headers */ #include +#include #include /* Include all architecture-dependent entropy API headers */ diff --git a/qemu/roms/ipxe/src/include/ipxe/errfile.h b/qemu/roms/ipxe/src/include/ipxe/errfile.h index f809337ff..e21c95938 100644 --- a/qemu/roms/ipxe/src/include/ipxe/errfile.h +++ b/qemu/roms/ipxe/src/include/ipxe/errfile.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include @@ -68,6 +68,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_fbcon ( ERRFILE_CORE | 0x001c0000 ) #define ERRFILE_ansicol ( ERRFILE_CORE | 0x001d0000 ) #define ERRFILE_ansicoldef ( ERRFILE_CORE | 0x001e0000 ) +#define ERRFILE_fault ( ERRFILE_CORE | 0x001f0000 ) +#define ERRFILE_blocktrans ( ERRFILE_CORE | 0x00200000 ) #define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 ) #define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 ) @@ -76,12 +78,20 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_pci ( ERRFILE_DRIVER | 0x00040000 ) #define ERRFILE_linux ( ERRFILE_DRIVER | 0x00050000 ) #define ERRFILE_pcivpd ( ERRFILE_DRIVER | 0x00060000 ) +#define ERRFILE_usb ( ERRFILE_DRIVER | 0x00070000 ) +#define ERRFILE_usbhub ( ERRFILE_DRIVER | 0x00080000 ) +#define ERRFILE_xhci ( ERRFILE_DRIVER | 0x00090000 ) +#define ERRFILE_ehci ( ERRFILE_DRIVER | 0x000a0000 ) +#define ERRFILE_uhci ( ERRFILE_DRIVER | 0x000b0000 ) +#define ERRFILE_usbhid ( ERRFILE_DRIVER | 0x000c0000 ) +#define ERRFILE_usbkbd ( ERRFILE_DRIVER | 0x000d0000 ) #define ERRFILE_nvs ( ERRFILE_DRIVER | 0x00100000 ) #define ERRFILE_spi ( ERRFILE_DRIVER | 0x00110000 ) #define ERRFILE_i2c_bit ( ERRFILE_DRIVER | 0x00120000 ) #define ERRFILE_spi_bit ( ERRFILE_DRIVER | 0x00130000 ) #define ERRFILE_nvsvpd ( ERRFILE_DRIVER | 0x00140000 ) +#define ERRFILE_uart ( ERRFILE_DRIVER | 0x00150000 ) #define ERRFILE_3c509 ( ERRFILE_DRIVER | 0x00200000 ) #define ERRFILE_bnx2 ( ERRFILE_DRIVER | 0x00210000 ) @@ -157,7 +167,11 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_snp ( ERRFILE_DRIVER | 0x00680000 ) #define ERRFILE_netfront ( ERRFILE_DRIVER | 0x00690000 ) #define ERRFILE_nii ( ERRFILE_DRIVER | 0x006a0000 ) - +#define ERRFILE_netvsc ( ERRFILE_DRIVER | 0x006b0000 ) +#define ERRFILE_ecm ( ERRFILE_DRIVER | 0x006c0000 ) +#define ERRFILE_ncm ( ERRFILE_DRIVER | 0x006d0000 ) +#define ERRFILE_usbnet ( ERRFILE_DRIVER | 0x006e0000 ) +#define ERRFILE_dm96xx ( ERRFILE_DRIVER | 0x006f0000 ) #define ERRFILE_scsi ( ERRFILE_DRIVER | 0x00700000 ) #define ERRFILE_arbel ( ERRFILE_DRIVER | 0x00710000 ) #define ERRFILE_hermon ( ERRFILE_DRIVER | 0x00720000 ) @@ -165,6 +179,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_ata ( ERRFILE_DRIVER | 0x00740000 ) #define ERRFILE_srp ( ERRFILE_DRIVER | 0x00750000 ) #define ERRFILE_qib7322 ( ERRFILE_DRIVER | 0x00760000 ) +#define ERRFILE_smsc75xx ( ERRFILE_DRIVER | 0x00770000 ) +#define ERRFILE_intelvf ( ERRFILE_DRIVER | 0x00780000 ) +#define ERRFILE_intelxvf ( ERRFILE_DRIVER | 0x00790000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) @@ -227,6 +244,17 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_ping ( ERRFILE_NET | 0x003a0000 ) #define ERRFILE_dhcpv6 ( ERRFILE_NET | 0x003b0000 ) #define ERRFILE_nfs_uri ( ERRFILE_NET | 0x003c0000 ) +#define ERRFILE_rndis ( ERRFILE_NET | 0x003d0000 ) +#define ERRFILE_pccrc ( ERRFILE_NET | 0x003e0000 ) +#define ERRFILE_stp ( ERRFILE_NET | 0x003f0000 ) +#define ERRFILE_pccrd ( ERRFILE_NET | 0x00400000 ) +#define ERRFILE_httpconn ( ERRFILE_NET | 0x00410000 ) +#define ERRFILE_httpauth ( ERRFILE_NET | 0x00420000 ) +#define ERRFILE_httpbasic ( ERRFILE_NET | 0x00430000 ) +#define ERRFILE_httpdigest ( ERRFILE_NET | 0x00440000 ) +#define ERRFILE_peerdisc ( ERRFILE_NET | 0x00450000 ) +#define ERRFILE_peerblk ( ERRFILE_NET | 0x00460000 ) +#define ERRFILE_peermux ( ERRFILE_NET | 0x00470000 ) #define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) @@ -245,7 +273,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_imgmgmt ( ERRFILE_OTHER | 0x00050000 ) #define ERRFILE_pxe_tftp ( ERRFILE_OTHER | 0x00060000 ) #define ERRFILE_pxe_udp ( ERRFILE_OTHER | 0x00070000 ) -#define ERRFILE_axtls_aes ( ERRFILE_OTHER | 0x00080000 ) +#define ERRFILE_aes ( ERRFILE_OTHER | 0x00080000 ) #define ERRFILE_cipher ( ERRFILE_OTHER | 0x00090000 ) #define ERRFILE_image_cmd ( ERRFILE_OTHER | 0x000a0000 ) #define ERRFILE_uri_test ( ERRFILE_OTHER | 0x000b0000 ) @@ -308,6 +336,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_xengrant ( ERRFILE_OTHER | 0x00440000 ) #define ERRFILE_efi_utils ( ERRFILE_OTHER | 0x00450000 ) #define ERRFILE_efi_wrap ( ERRFILE_OTHER | 0x00460000 ) +#define ERRFILE_vmbus ( ERRFILE_OTHER | 0x00470000 ) +#define ERRFILE_efi_time ( ERRFILE_OTHER | 0x00480000 ) +#define ERRFILE_efi_watchdog ( ERRFILE_OTHER | 0x00490000 ) /** @} */ diff --git a/qemu/roms/ipxe/src/include/ipxe/errno/efi.h b/qemu/roms/ipxe/src/include/ipxe/errno/efi.h index 2d2c50176..9f010f5fb 100644 --- a/qemu/roms/ipxe/src/include/ipxe/errno/efi.h +++ b/qemu/roms/ipxe/src/include/ipxe/errno/efi.h @@ -21,7 +21,7 @@ * as-is. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/errno/linux.h b/qemu/roms/ipxe/src/include/ipxe/errno/linux.h index 11309b4ad..99133c816 100644 --- a/qemu/roms/ipxe/src/include/ipxe/errno/linux.h +++ b/qemu/roms/ipxe/src/include/ipxe/errno/linux.h @@ -10,7 +10,7 @@ * directly as our platform error codes. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Convert platform error code to platform component of iPXE error code diff --git a/qemu/roms/ipxe/src/include/ipxe/errortab.h b/qemu/roms/ipxe/src/include/ipxe/errortab.h index a2f6a70f5..4fe81a6be 100644 --- a/qemu/roms/ipxe/src/include/ipxe/errortab.h +++ b/qemu/roms/ipxe/src/include/ipxe/errortab.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/eth_slow.h b/qemu/roms/ipxe/src/include/ipxe/eth_slow.h index 00509197d..f6d731b3b 100644 --- a/qemu/roms/ipxe/src/include/ipxe/eth_slow.h +++ b/qemu/roms/ipxe/src/include/ipxe/eth_slow.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Slow protocols header */ struct eth_slow_header { diff --git a/qemu/roms/ipxe/src/include/ipxe/ethernet.h b/qemu/roms/ipxe/src/include/ipxe/ethernet.h index d1263d7c3..dd04e00ce 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ethernet.h +++ b/qemu/roms/ipxe/src/include/ipxe/ethernet.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/fakedhcp.h b/qemu/roms/ipxe/src/include/ipxe/fakedhcp.h index ea06b06dc..d016b5237 100644 --- a/qemu/roms/ipxe/src/include/ipxe/fakedhcp.h +++ b/qemu/roms/ipxe/src/include/ipxe/fakedhcp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/fault.h b/qemu/roms/ipxe/src/include/ipxe/fault.h new file mode 100644 index 000000000..356296c35 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/fault.h @@ -0,0 +1,53 @@ +#ifndef _IPXE_FAULT_H +#define _IPXE_FAULT_H + +/** @file + * + * Fault injection + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +extern int inject_fault_nonzero ( unsigned int rate ); +extern void inject_corruption_nonzero ( unsigned int rate, const void *data, + size_t len ); + +/** + * Inject fault with a specified probability + * + * @v rate Reciprocal of fault probability (zero for no faults) + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +inject_fault ( unsigned int rate ) { + + /* Force dead code elimination in non-fault-injecting builds */ + if ( rate == 0 ) + return 0; + + return inject_fault_nonzero ( rate ); +} + +/** + * Corrupt data with a specified probability + * + * @v rate Reciprocal of fault probability (zero for no faults) + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) void +inject_corruption ( unsigned int rate, const void *data, size_t len ) { + + /* Force dead code elimination in non-fault-injecting builds */ + if ( rate == 0 ) + return; + + return inject_corruption_nonzero ( rate, data, len ); +} + +#endif /* _IPXE_FAULT_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/fbcon.h b/qemu/roms/ipxe/src/include/ipxe/fbcon.h index 0538449ac..d442bb918 100644 --- a/qemu/roms/ipxe/src/include/ipxe/fbcon.h +++ b/qemu/roms/ipxe/src/include/ipxe/fbcon.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/fc.h b/qemu/roms/ipxe/src/include/ipxe/fc.h index 6fdef092d..840d11f62 100644 --- a/qemu/roms/ipxe/src/include/ipxe/fc.h +++ b/qemu/roms/ipxe/src/include/ipxe/fc.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/fcels.h b/qemu/roms/ipxe/src/include/ipxe/fcels.h index 45fa69a4a..02f755115 100644 --- a/qemu/roms/ipxe/src/include/ipxe/fcels.h +++ b/qemu/roms/ipxe/src/include/ipxe/fcels.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/fcns.h b/qemu/roms/ipxe/src/include/ipxe/fcns.h index e25d9b9d5..9011a7be7 100644 --- a/qemu/roms/ipxe/src/include/ipxe/fcns.h +++ b/qemu/roms/ipxe/src/include/ipxe/fcns.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/fcoe.h b/qemu/roms/ipxe/src/include/ipxe/fcoe.h index 6ba5b406a..b61e82fea 100644 --- a/qemu/roms/ipxe/src/include/ipxe/fcoe.h +++ b/qemu/roms/ipxe/src/include/ipxe/fcoe.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/fcp.h b/qemu/roms/ipxe/src/include/ipxe/fcp.h index f6922bc7c..853ca13f6 100644 --- a/qemu/roms/ipxe/src/include/ipxe/fcp.h +++ b/qemu/roms/ipxe/src/include/ipxe/fcp.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/features.h b/qemu/roms/ipxe/src/include/ipxe/features.h index d8b8b2184..e86a2d226 100644 --- a/qemu/roms/ipxe/src/include/ipxe/features.h +++ b/qemu/roms/ipxe/src/include/ipxe/features.h @@ -11,7 +11,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @defgroup featurecat Feature categories diff --git a/qemu/roms/ipxe/src/include/ipxe/fragment.h b/qemu/roms/ipxe/src/include/ipxe/fragment.h index e311ad1e4..0069e5e08 100644 --- a/qemu/roms/ipxe/src/include/ipxe/fragment.h +++ b/qemu/roms/ipxe/src/include/ipxe/fragment.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/ftp.h b/qemu/roms/ipxe/src/include/ipxe/ftp.h index cbab12d2c..3180f1631 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ftp.h +++ b/qemu/roms/ipxe/src/include/ipxe/ftp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** FTP default port */ #define FTP_PORT 21 diff --git a/qemu/roms/ipxe/src/include/ipxe/gdbserial.h b/qemu/roms/ipxe/src/include/ipxe/gdbserial.h index a3b56173c..e1040c94e 100644 --- a/qemu/roms/ipxe/src/include/ipxe/gdbserial.h +++ b/qemu/roms/ipxe/src/include/ipxe/gdbserial.h @@ -7,15 +7,14 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include struct gdb_transport; -/** - * Set up the serial transport - * - * @ret transport suitable for starting the GDB stub or NULL on error - */ -struct gdb_transport *gdbserial_configure ( void ); +extern struct gdb_transport * gdbserial_configure ( unsigned int port, + unsigned int baud, + uint8_t lcr ); #endif /* _IPXE_GDBSERIAL_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/gdbstub.h b/qemu/roms/ipxe/src/include/ipxe/gdbstub.h index 319606747..13ca33ddb 100644 --- a/qemu/roms/ipxe/src/include/ipxe/gdbstub.h +++ b/qemu/roms/ipxe/src/include/ipxe/gdbstub.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/gdbudp.h b/qemu/roms/ipxe/src/include/ipxe/gdbudp.h index db7a451c9..a1c091522 100644 --- a/qemu/roms/ipxe/src/include/ipxe/gdbudp.h +++ b/qemu/roms/ipxe/src/include/ipxe/gdbudp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct sockaddr_in; struct gdb_transport; diff --git a/qemu/roms/ipxe/src/include/ipxe/hash_df.h b/qemu/roms/ipxe/src/include/ipxe/hash_df.h index 607a4a610..e57682446 100644 --- a/qemu/roms/ipxe/src/include/ipxe/hash_df.h +++ b/qemu/roms/ipxe/src/include/ipxe/hash_df.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/hidemem.h b/qemu/roms/ipxe/src/include/ipxe/hidemem.h index ddc9cd8b3..cc8d5ee37 100644 --- a/qemu/roms/ipxe/src/include/ipxe/hidemem.h +++ b/qemu/roms/ipxe/src/include/ipxe/hidemem.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/hmac.h b/qemu/roms/ipxe/src/include/ipxe/hmac.h index d5ec0868d..09d3e273d 100644 --- a/qemu/roms/ipxe/src/include/ipxe/hmac.h +++ b/qemu/roms/ipxe/src/include/ipxe/hmac.h @@ -6,7 +6,7 @@ * Keyed-Hashing for Message Authentication */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/hmac_drbg.h b/qemu/roms/ipxe/src/include/ipxe/hmac_drbg.h index 8dfd2924f..a0f22da75 100644 --- a/qemu/roms/ipxe/src/include/ipxe/hmac_drbg.h +++ b/qemu/roms/ipxe/src/include/ipxe/hmac_drbg.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/http.h b/qemu/roms/ipxe/src/include/ipxe/http.h index cf8c0c7fa..a0dff7d00 100644 --- a/qemu/roms/ipxe/src/include/ipxe/http.h +++ b/qemu/roms/ipxe/src/include/ipxe/http.h @@ -7,7 +7,26 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct http_transaction; + +/****************************************************************************** + * + * HTTP URI schemes + * + ****************************************************************************** + */ /** HTTP default port */ #define HTTP_PORT 80 @@ -15,10 +34,469 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** HTTPS default port */ #define HTTPS_PORT 443 -extern int http_open_filter ( struct interface *xfer, struct uri *uri, - unsigned int default_port, - int ( * filter ) ( struct interface *, - const char *, - struct interface ** ) ); +/** An HTTP URI scheme */ +struct http_scheme { + /** Scheme name (e.g. "http" or "https") */ + const char *name; + /** Default port */ + unsigned int port; + /** Transport-layer filter (if any) + * + * @v xfer Data transfer interface + * @v name Host name + * @v next Next interface + * @ret rc Return status code + */ + int ( * filter ) ( struct interface *xfer, const char *name, + struct interface **next ); +}; + +/** HTTP scheme table */ +#define HTTP_SCHEMES __table ( struct http_scheme, "http_schemes" ) + +/** Declare an HTTP scheme */ +#define __http_scheme __table_entry ( HTTP_SCHEMES, 01 ) + +/****************************************************************************** + * + * Connections + * + ****************************************************************************** + */ + +/** An HTTP connection + * + * This represents a potentially reusable connection to an HTTP + * server. + */ +struct http_connection { + /** Reference count */ + struct refcnt refcnt; + /** Connection URI + * + * This encapsulates the server (and protocol) used for the + * connection. This may be the origin server or a proxy + * server. + */ + struct uri *uri; + /** HTTP scheme */ + struct http_scheme *scheme; + /** Transport layer interface */ + struct interface socket; + /** Data transfer interface */ + struct interface xfer; + /** Pooled connection */ + struct pooled_connection pool; +}; + +/****************************************************************************** + * + * HTTP methods + * + ****************************************************************************** + */ + +/** An HTTP method */ +struct http_method { + /** Method name (e.g. "GET" or "POST") */ + const char *name; +}; + +extern struct http_method http_head; +extern struct http_method http_get; +extern struct http_method http_post; + +/****************************************************************************** + * + * Requests + * + ****************************************************************************** + */ + +/** HTTP Digest authentication client nonce count + * + * We choose to generate a new client nonce each time. + */ +#define HTTP_DIGEST_NC "00000001" + +/** HTTP Digest authentication client nonce length + * + * We choose to use a 32-bit hex client nonce. + */ +#define HTTP_DIGEST_CNONCE_LEN 8 + +/** HTTP Digest authentication response length + * + * The Digest authentication response is a Base16-encoded 16-byte MD5 + * checksum. + */ +#define HTTP_DIGEST_RESPONSE_LEN 32 + +/** HTTP request range descriptor */ +struct http_request_range { + /** Range start */ + size_t start; + /** Range length, or zero for no range request */ + size_t len; +}; + +/** HTTP request content descriptor */ +struct http_request_content { + /** Content type (if any) */ + const char *type; + /** Content data (if any) */ + const void *data; + /** Content length */ + size_t len; +}; + +/** HTTP request authentication descriptor */ +struct http_request_auth { + /** Authentication scheme (if any) */ + struct http_authentication *auth; + /** Username */ + const char *username; + /** Password */ + const char *password; + /** Quality of protection */ + const char *qop; + /** Algorithm */ + const char *algorithm; + /** Client nonce */ + char cnonce[ HTTP_DIGEST_CNONCE_LEN + 1 /* NUL */ ]; + /** Response */ + char response[ HTTP_DIGEST_RESPONSE_LEN + 1 /* NUL */ ]; +}; + +/** An HTTP request + * + * This represents a single request to be sent to a server, including + * the values required to construct all headers. + * + * Pointers within this structure must point to storage which is + * guaranteed to remain valid for the lifetime of the containing HTTP + * transaction. + */ +struct http_request { + /** Method */ + struct http_method *method; + /** Request URI string */ + const char *uri; + /** Server host name */ + const char *host; + /** Range descriptor */ + struct http_request_range range; + /** Content descriptor */ + struct http_request_content content; + /** Authentication descriptor */ + struct http_request_auth auth; +}; + +/** An HTTP request header */ +struct http_request_header { + /** Header name (e.g. "User-Agent") */ + const char *name; + /** Construct remaining header line + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Header length if present, or negative error + */ + int ( * format ) ( struct http_transaction *http, char *buf, + size_t len ); +}; + +/** HTTP request header table */ +#define HTTP_REQUEST_HEADERS \ + __table ( struct http_request_header, "http_request_headers" ) + +/** Declare an HTTP request header */ +#define __http_request_header __table_entry ( HTTP_REQUEST_HEADERS, 01 ) + +/****************************************************************************** + * + * Responses + * + ****************************************************************************** + */ + +/** HTTP response transfer descriptor */ +struct http_response_transfer { + /** Transfer encoding */ + struct http_transfer_encoding *encoding; +}; + +/** HTTP response content descriptor */ +struct http_response_content { + /** Content length (may be zero) */ + size_t len; + /** Content encoding */ + struct http_content_encoding *encoding; +}; + +/** HTTP response authorization descriptor */ +struct http_response_auth { + /** Authentication scheme (if any) */ + struct http_authentication *auth; + /** Realm */ + const char *realm; + /** Quality of protection */ + const char *qop; + /** Algorithm */ + const char *algorithm; + /** Nonce */ + const char *nonce; + /** Opaque */ + const char *opaque; +}; + +/** An HTTP response + * + * This represents a single response received from the server, + * including all values parsed from headers. + * + * Pointers within this structure may point into the raw response + * buffer, and so should be invalidated when the response buffer is + * modified or discarded. + */ +struct http_response { + /** Raw response header lines + * + * This is the raw response data received from the server, up + * to and including the terminating empty line. String + * pointers within the response may point into this data + * buffer; NUL terminators will be added (overwriting the + * original terminating characters) as needed. + */ + struct line_buffer headers; + /** Status code + * + * This is the raw HTTP numeric status code (e.g. 404). + */ + unsigned int status; + /** Return status code + * + * This is the iPXE return status code corresponding to the + * HTTP status code (e.g. -ENOENT). + */ + int rc; + /** Redirection location */ + const char *location; + /** Transfer descriptor */ + struct http_response_transfer transfer; + /** Content descriptor */ + struct http_response_content content; + /** Authorization descriptor */ + struct http_response_auth auth; + /** Retry delay (in seconds) */ + unsigned int retry_after; + /** Flags */ + unsigned int flags; +}; + +/** HTTP response flags */ +enum http_response_flags { + /** Keep connection alive after close */ + HTTP_RESPONSE_KEEPALIVE = 0x0001, + /** Content length specified */ + HTTP_RESPONSE_CONTENT_LEN = 0x0002, + /** Transaction may be retried on failure */ + HTTP_RESPONSE_RETRY = 0x0004, +}; + +/** An HTTP response header */ +struct http_response_header { + /** Header name (e.g. "Transfer-Encoding") */ + const char *name; + /** Parse header line + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ + int ( * parse ) ( struct http_transaction *http, char *line ); +}; + +/** HTTP response header table */ +#define HTTP_RESPONSE_HEADERS \ + __table ( struct http_response_header, "http_response_headers" ) + +/** Declare an HTTP response header */ +#define __http_response_header __table_entry ( HTTP_RESPONSE_HEADERS, 01 ) + +/****************************************************************************** + * + * Transactions + * + ****************************************************************************** + */ + +/** HTTP transaction state */ +struct http_state { + /** Transmit data + * + * @v http HTTP transaction + * @ret rc Return status code + */ + int ( * tx ) ( struct http_transaction *http ); + /** Receive data + * + * @v http HTTP transaction + * @v iobuf I/O buffer (may be claimed) + * @ret rc Return status code + */ + int ( * rx ) ( struct http_transaction *http, + struct io_buffer **iobuf ); + /** Server connection closed + * + * @v http HTTP transaction + * @v rc Reason for close + */ + void ( * close ) ( struct http_transaction *http, int rc ); +}; + +/** An HTTP transaction */ +struct http_transaction { + /** Reference count */ + struct refcnt refcnt; + /** Data transfer interface */ + struct interface xfer; + /** Content-decoded interface */ + struct interface content; + /** Transfer-decoded interface */ + struct interface transfer; + /** Server connection */ + struct interface conn; + /** Transmit process */ + struct process process; + /** Reconnection timer */ + struct retry_timer timer; + + /** Request URI */ + struct uri *uri; + /** Request */ + struct http_request request; + /** Response */ + struct http_response response; + /** Temporary line buffer */ + struct line_buffer linebuf; + + /** Transaction state */ + struct http_state *state; + /** Accumulated transfer-decoded length */ + size_t len; + /** Chunk length remaining */ + size_t remaining; +}; + +/****************************************************************************** + * + * Transfer encoding + * + ****************************************************************************** + */ + +/** An HTTP transfer encoding */ +struct http_transfer_encoding { + /** Name */ + const char *name; + /** Initialise transfer encoding + * + * @v http HTTP transaction + * @ret rc Return status code + */ + int ( * init ) ( struct http_transaction *http ); + /** Receive data state */ + struct http_state state; +}; + +/** HTTP transfer encoding table */ +#define HTTP_TRANSFER_ENCODINGS \ + __table ( struct http_transfer_encoding, "http_transfer_encodings" ) + +/** Declare an HTTP transfer encoding */ +#define __http_transfer_encoding __table_entry ( HTTP_TRANSFER_ENCODINGS, 01 ) + +/****************************************************************************** + * + * Content encoding + * + ****************************************************************************** + */ + +/** An HTTP content encoding */ +struct http_content_encoding { + /** Name */ + const char *name; + /** Check if content encoding is supported for this request + * + * @v http HTTP transaction + * @ret supported Content encoding is supported for this request + */ + int ( * supported ) ( struct http_transaction *http ); + /** Initialise content encoding + * + * @v http HTTP transaction + * @ret rc Return status code + */ + int ( * init ) ( struct http_transaction *http ); +}; + +/** HTTP content encoding table */ +#define HTTP_CONTENT_ENCODINGS \ + __table ( struct http_content_encoding, "http_content_encodings" ) + +/** Declare an HTTP content encoding */ +#define __http_content_encoding __table_entry ( HTTP_CONTENT_ENCODINGS, 01 ) + +/****************************************************************************** + * + * Authentication + * + ****************************************************************************** + */ + +/** An HTTP authentication scheme */ +struct http_authentication { + /** Name (e.g. "Digest") */ + const char *name; + /** Perform authentication + * + * @v http HTTP transaction + * @ret rc Return status code + */ + int ( * authenticate ) ( struct http_transaction *http ); + /** Construct remaining "Authorization" header line + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Header length if present, or negative error + */ + int ( * format ) ( struct http_transaction *http, char *buf, + size_t len ); +}; + +/** HTTP authentication scheme table */ +#define HTTP_AUTHENTICATIONS \ + __table ( struct http_authentication, "http_authentications" ) + +/** Declare an HTTP authentication scheme */ +#define __http_authentication __table_entry ( HTTP_AUTHENTICATIONS, 01 ) + +/****************************************************************************** + * + * General + * + ****************************************************************************** + */ + +extern char * http_token ( char **line, char **value ); +extern int http_connect ( struct interface *xfer, struct uri *uri ); +extern int http_open ( struct interface *xfer, struct http_method *method, + struct uri *uri, struct http_request_range *range, + struct http_request_content *content ); +extern int http_open_uri ( struct interface *xfer, struct uri *uri ); #endif /* _IPXE_HTTP_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/hyperv.h b/qemu/roms/ipxe/src/include/ipxe/hyperv.h new file mode 100644 index 000000000..c61e2a083 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/hyperv.h @@ -0,0 +1,232 @@ +#ifndef _IPXE_HYPERV_H +#define _IPXE_HYPERV_H + +/** @file + * + * Hyper-V interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** Hyper-V interface identification */ +#define HV_INTERFACE_ID 0x31237648 /* "Hv#1" */ + +/** Guest OS identity for iPXE + * + * This field comprises: + * + * Bit 63 : set to 1 to indicate an open source OS + * Bits 62:56 : OS Type + * Bits 55:48 : OS ID + * Bits 47:16 : Version + * Bits 15:0 : Build number + * + * There appears to be no central registry for the "OS Type". The + * specification states that "Linux is 0x100", and the FreeBSD source + * states that "FreeBSD is 0x200". Both of these statements are + * actually referring to the combined "OS Type" and "OS ID" field. + * + * We choose to use 0x98ae: this is generated by setting bit 63 (to + * indicate an open source OS) and setting the OS Type+ID equal to the + * PnP vendor ID used in romprefix.S. No version information or build + * number is included. + */ +#define HV_GUEST_OS_ID_IPXE ( ( 1ULL << 63 ) | ( 0x18aeULL << 48 ) ) + +/** Enable hypercall page */ +#define HV_HYPERCALL_ENABLE 0x00000001UL + +/** Enable SynIC */ +#define HV_SCONTROL_ENABLE 0x00000001UL + +/** Enable SynIC event flags */ +#define HV_SIEFP_ENABLE 0x00000001UL + +/** Enable SynIC messages */ +#define HV_SIMP_ENABLE 0x00000001UL + +/** Perform implicit EOI upon synthetic interrupt delivery */ +#define HV_SINT_AUTO_EOI 0x00020000UL + +/** Mask synthetic interrupt */ +#define HV_SINT_MASKED 0x00010000UL + +/** Synthetic interrupt vector */ +#define HV_SINT_VECTOR(x) ( (x) << 0 ) + +/** Synthetic interrupt vector mask */ +#define HV_SINT_VECTOR_MASK HV_SINT_VECTOR ( 0xff ) + +/** Post message */ +#define HV_POST_MESSAGE 0x005c + +/** A posted message + * + * This is the input parameter list for the HvPostMessage hypercall. + */ +struct hv_post_message { + /** Connection ID */ + uint32_t id; + /** Padding */ + uint32_t reserved; + /** Type */ + uint32_t type; + /** Length of message */ + uint32_t len; + /** Message */ + uint8_t data[240]; +} __attribute__ (( packed )); + +/** A received message + * + * This is the HV_MESSAGE structure from the Hypervisor Top-Level + * Functional Specification. The field order given in the + * documentation is incorrect. + */ +struct hv_message { + /** Type */ + uint32_t type; + /** Length of message */ + uint8_t len; + /** Flags */ + uint8_t flags; + /** Padding */ + uint16_t reserved; + /** Origin */ + uint64_t origin; + /** Message */ + uint8_t data[240]; +} __attribute__ (( packed )); + +/** Signal event */ +#define HV_SIGNAL_EVENT 0x005d + +/** A signalled event */ +struct hv_signal_event { + /** Connection ID */ + uint32_t id; + /** Flag number */ + uint16_t flag; + /** Reserved */ + uint16_t reserved; +} __attribute__ (( packed )); + +/** A received event */ +struct hv_event { + /** Event flags */ + uint8_t flags[256]; +} __attribute__ (( packed )); + +/** A monitor trigger group + * + * This is the HV_MONITOR_TRIGGER_GROUP structure from the Hypervisor + * Top-Level Functional Specification. + */ +struct hv_monitor_trigger { + /** Pending events */ + uint32_t pending; + /** Armed events */ + uint32_t armed; +} __attribute__ (( packed )); + +/** A monitor parameter set + * + * This is the HV_MONITOR_PARAMETER structure from the Hypervisor + * Top-Level Functional Specification. + */ +struct hv_monitor_parameter { + /** Connection ID */ + uint32_t id; + /** Flag number */ + uint16_t flag; + /** Reserved */ + uint16_t reserved; +} __attribute__ (( packed )); + +/** A monitor page + * + * This is the HV_MONITOR_PAGE structure from the Hypervisor Top-Level + * Functional Specification. + */ +struct hv_monitor { + /** Flags */ + uint32_t flags; + /** Reserved */ + uint8_t reserved_a[4]; + /** Trigger groups */ + struct hv_monitor_trigger trigger[4]; + /** Reserved */ + uint8_t reserved_b[536]; + /** Latencies */ + uint16 latency[4][32]; + /** Reserved */ + uint8_t reserved_c[256]; + /** Parameters */ + struct hv_monitor_parameter param[4][32]; + /** Reserved */ + uint8_t reserved_d[1984]; +} __attribute__ (( packed )); + +/** A synthetic interrupt controller */ +struct hv_synic { + /** Message page */ + struct hv_message *message; + /** Event flag page */ + struct hv_event *event; +}; + +/** A message buffer */ +union hv_message_buffer { + /** Posted message */ + struct hv_post_message posted; + /** Received message */ + struct hv_message received; + /** Signalled event */ + struct hv_signal_event signalled; +}; + +/** A Hyper-V hypervisor */ +struct hv_hypervisor { + /** Hypercall page */ + void *hypercall; + /** Synthetic interrupt controller (SynIC) */ + struct hv_synic synic; + /** Message buffer */ + union hv_message_buffer *message; + /** Virtual machine bus */ + struct vmbus *vmbus; +}; + +#include + +/** + * Calculate the number of pages covering an address range + * + * @v data Start of data + * @v len Length of data (must be non-zero) + * @ret pfn_count Number of pages covered + */ +static inline unsigned int hv_pfn_count ( physaddr_t data, size_t len ) { + unsigned int first_pfn = ( data / PAGE_SIZE ); + unsigned int last_pfn = ( ( data + len - 1 ) / PAGE_SIZE ); + + return ( last_pfn - first_pfn + 1 ); +} + +extern __attribute__ (( sentinel )) int +hv_alloc_pages ( struct hv_hypervisor *hv, ... ); +extern __attribute__ (( sentinel )) void +hv_free_pages ( struct hv_hypervisor *hv, ... ); +extern void hv_enable_sint ( struct hv_hypervisor *hv, unsigned int sintx ); +extern void hv_disable_sint ( struct hv_hypervisor *hv, unsigned int sintx ); +extern int hv_post_message ( struct hv_hypervisor *hv, unsigned int id, + unsigned int type, const void *data, size_t len ); +extern int hv_wait_for_message ( struct hv_hypervisor *hv, unsigned int sintx ); +extern int hv_signal_event ( struct hv_hypervisor *hv, unsigned int id, + unsigned int flag ); + +#endif /* _IPXE_HYPERV_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/i2c.h b/qemu/roms/ipxe/src/include/ipxe/i2c.h index c1f5a9bbd..46970515c 100644 --- a/qemu/roms/ipxe/src/include/ipxe/i2c.h +++ b/qemu/roms/ipxe/src/include/ipxe/i2c.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/ib_cm.h b/qemu/roms/ipxe/src/include/ipxe/ib_cm.h index 7d08cd9b1..4913eebae 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ib_cm.h +++ b/qemu/roms/ipxe/src/include/ipxe/ib_cm.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/ib_mad.h b/qemu/roms/ipxe/src/include/ipxe/ib_mad.h index b8694833e..ae1eea7e4 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ib_mad.h +++ b/qemu/roms/ipxe/src/include/ipxe/ib_mad.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/ib_mcast.h b/qemu/roms/ipxe/src/include/ipxe/ib_mcast.h index a5c22a03e..564066975 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ib_mcast.h +++ b/qemu/roms/ipxe/src/include/ipxe/ib_mcast.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/ib_mi.h b/qemu/roms/ipxe/src/include/ipxe/ib_mi.h index 5c5415b71..c7c8143ba 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ib_mi.h +++ b/qemu/roms/ipxe/src/include/ipxe/ib_mi.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/ib_packet.h b/qemu/roms/ipxe/src/include/ipxe/ib_packet.h index a959967cb..f275fcb09 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ib_packet.h +++ b/qemu/roms/ipxe/src/include/ipxe/ib_packet.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct ib_device; struct ib_queue_pair; diff --git a/qemu/roms/ipxe/src/include/ipxe/ib_pathrec.h b/qemu/roms/ipxe/src/include/ipxe/ib_pathrec.h index 1fe67f87d..a4e11ebe3 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ib_pathrec.h +++ b/qemu/roms/ipxe/src/include/ipxe/ib_pathrec.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/ib_sma.h b/qemu/roms/ipxe/src/include/ipxe/ib_sma.h index fa355c652..74003d045 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ib_sma.h +++ b/qemu/roms/ipxe/src/include/ipxe/ib_sma.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct ib_device; struct ib_mad_interface; diff --git a/qemu/roms/ipxe/src/include/ipxe/ib_smc.h b/qemu/roms/ipxe/src/include/ipxe/ib_smc.h index 259d2cde1..f9b96b1bd 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ib_smc.h +++ b/qemu/roms/ipxe/src/include/ipxe/ib_smc.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/icmp.h b/qemu/roms/ipxe/src/include/ipxe/icmp.h index 0480ddfaf..803f8e019 100644 --- a/qemu/roms/ipxe/src/include/ipxe/icmp.h +++ b/qemu/roms/ipxe/src/include/ipxe/icmp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/icmpv6.h b/qemu/roms/ipxe/src/include/ipxe/icmpv6.h index b5ea54eab..0474ddca8 100644 --- a/qemu/roms/ipxe/src/include/ipxe/icmpv6.h +++ b/qemu/roms/ipxe/src/include/ipxe/icmpv6.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -40,6 +40,18 @@ struct icmpv6_handler { /** Declare an ICMPv6 handler */ #define __icmpv6_handler __table_entry ( ICMPV6_HANDLERS, 01 ) +/** ICMPv6 destination unreachable */ +#define ICMPV6_DESTINATION_UNREACHABLE 1 + +/** ICMPv6 packet too big */ +#define ICMPV6_PACKET_TOO_BIG 2 + +/** ICMPv6 time exceeded */ +#define ICMPV6_TIME_EXCEEDED 3 + +/** ICMPv6 parameter problem */ +#define ICMPV6_PARAMETER_PROBLEM 4 + /** ICMPv6 echo request */ #define ICMPV6_ECHO_REQUEST 128 diff --git a/qemu/roms/ipxe/src/include/ipxe/if_arp.h b/qemu/roms/ipxe/src/include/ipxe/if_arp.h index fd36e9c67..4eb1f80b7 100644 --- a/qemu/roms/ipxe/src/include/ipxe/if_arp.h +++ b/qemu/roms/ipxe/src/include/ipxe/if_arp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/if_ether.h b/qemu/roms/ipxe/src/include/ipxe/if_ether.h index a7e237349..58d91b976 100644 --- a/qemu/roms/ipxe/src/include/ipxe/if_ether.h +++ b/qemu/roms/ipxe/src/include/ipxe/if_ether.h @@ -1,7 +1,7 @@ #ifndef _IPXE_IF_ETHER_H #define _IPXE_IF_ETHER_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/image.h b/qemu/roms/ipxe/src/include/ipxe/image.h index 5d7080a75..6abd7a2d2 100644 --- a/qemu/roms/ipxe/src/include/ipxe/image.h +++ b/qemu/roms/ipxe/src/include/ipxe/image.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -163,7 +163,6 @@ extern int image_set_cmdline ( struct image *image, const char *cmdline ); extern int register_image ( struct image *image ); extern void unregister_image ( struct image *image ); struct image * find_image ( const char *name ); -extern int image_probe ( struct image *image ); extern int image_exec ( struct image *image ); extern int image_replace ( struct image *replacement ); extern int image_select ( struct image *image ); diff --git a/qemu/roms/ipxe/src/include/ipxe/in.h b/qemu/roms/ipxe/src/include/ipxe/in.h index de96ca22a..0ebf441c2 100644 --- a/qemu/roms/ipxe/src/include/ipxe/in.h +++ b/qemu/roms/ipxe/src/include/ipxe/in.h @@ -1,9 +1,10 @@ #ifndef _IPXE_IN_H #define _IPXE_IN_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include #include /* Protocol numbers */ @@ -15,17 +16,22 @@ FILE_LICENCE ( GPL2_OR_LATER ); /* IP address constants */ -#define INADDR_NONE 0xffffffff +#define INADDR_NONE htonl ( 0xffffffff ) -#define INADDR_BROADCAST 0xffffffff +#define INADDR_BROADCAST htonl ( 0xffffffff ) -#define IN_CLASSA(addr) ( ( (addr) & 0x80000000 ) == 0x00000000 ) -#define IN_CLASSA_NET 0xff000000 -#define IN_CLASSB(addr) ( ( (addr) & 0xc0000000 ) == 0x80000000 ) -#define IN_CLASSB_NET 0xffff0000 -#define IN_CLASSC(addr) ( ( (addr) & 0xe0000000 ) == 0xc0000000 ) -#define IN_CLASSC_NET 0xffffff00 -#define IN_MULTICAST(addr) ( ( (addr) & 0xf0000000 ) == 0xe0000000 ) +#define INADDR_NET_CLASSA htonl ( 0xff000000 ) +#define INADDR_NET_CLASSB htonl ( 0xffff0000 ) +#define INADDR_NET_CLASSC htonl ( 0xffffff00 ) + +#define IN_IS_CLASSA( addr ) \ + ( ( (addr) & htonl ( 0x80000000 ) ) == htonl ( 0x00000000 ) ) +#define IN_IS_CLASSB( addr ) \ + ( ( (addr) & htonl ( 0xc0000000 ) ) == htonl ( 0x80000000 ) ) +#define IN_IS_CLASSC( addr ) \ + ( ( (addr) & htonl ( 0xe0000000 ) ) == htonl ( 0xc0000000 ) ) +#define IN_IS_MULTICAST( addr ) \ + ( ( (addr) & htonl ( 0xf0000000 ) ) == htonl ( 0xe0000000 ) ) /** * IP address structure @@ -63,6 +69,9 @@ struct in6_addr { ( ( *( ( const uint16_t * ) (addr) ) & htons ( 0xffc0 ) ) == \ htons ( 0xfe80 ) ) +#define IN6_IS_ADDR_NONGLOBAL( addr ) \ + ( IN6_IS_ADDR_LINKLOCAL (addr) || IN6_IS_ADDR_MULTICAST (addr) ) + /** * IPv4 socket address */ @@ -76,6 +85,11 @@ struct sockaddr_in { uint16_t sin_flags; /** TCP/IP port (part of struct @c sockaddr_tcpip) */ uint16_t sin_port; + /** Scope ID (part of struct @c sockaddr_tcpip) + * + * For multicast addresses, this is the network device index. + */ + uint16_t sin_scope_id; /** IPv4 address */ struct in_addr sin_addr; /** Padding @@ -87,6 +101,7 @@ struct sockaddr_in { ( sizeof ( sa_family_t ) /* sin_family */ + sizeof ( uint16_t ) /* sin_flags */ + sizeof ( uint16_t ) /* sin_port */ + + sizeof ( uint16_t ) /* sin_scope_id */ + sizeof ( struct in_addr ) /* sin_addr */ ) ]; } __attribute__ (( packed, may_alias )); @@ -103,9 +118,10 @@ struct sockaddr_in6 { uint16_t sin6_flags; /** TCP/IP port (part of struct @c sockaddr_tcpip) */ uint16_t sin6_port; - /** Scope ID + /** Scope ID (part of struct @c sockaddr_tcpip) * - * For link-local addresses, this is the network device index. + * For link-local or multicast addresses, this is the network + * device index. */ uint16_t sin6_scope_id; /** IPv6 address */ diff --git a/qemu/roms/ipxe/src/include/ipxe/infiniband.h b/qemu/roms/ipxe/src/include/ipxe/infiniband.h index f546ea61b..87cfe5082 100644 --- a/qemu/roms/ipxe/src/include/ipxe/infiniband.h +++ b/qemu/roms/ipxe/src/include/ipxe/infiniband.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/init.h b/qemu/roms/ipxe/src/include/ipxe/init.h index 19c5925bf..025cfaf37 100644 --- a/qemu/roms/ipxe/src/include/ipxe/init.h +++ b/qemu/roms/ipxe/src/include/ipxe/init.h @@ -1,7 +1,7 @@ #ifndef _IPXE_INIT_H #define _IPXE_INIT_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include @@ -26,10 +26,9 @@ struct init_fn { */ #define INIT_EARLY 01 /**< Early initialisation */ -#define INIT_SERIAL 02 /**< Serial driver initialisation */ -#define INIT_CONSOLE 03 /**< Console initialisation */ -#define INIT_NORMAL 04 /**< Normal initialisation */ -#define INIT_LATE 05 /**< Late initialisation */ +#define INIT_CONSOLE 02 /**< Console initialisation */ +#define INIT_NORMAL 03 /**< Normal initialisation */ +#define INIT_LATE 04 /**< Late initialisation */ /** @} */ diff --git a/qemu/roms/ipxe/src/include/ipxe/interface.h b/qemu/roms/ipxe/src/include/ipxe/interface.h index a474aaad0..a8d823775 100644 --- a/qemu/roms/ipxe/src/include/ipxe/interface.h +++ b/qemu/roms/ipxe/src/include/ipxe/interface.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -145,6 +145,11 @@ extern void intf_close ( struct interface *intf, int rc ); extern void intf_shutdown ( struct interface *intf, int rc ); extern void intf_restart ( struct interface *intf, int rc ); +extern void intf_poke ( struct interface *intf, + void ( type ) ( struct interface *intf ) ); +#define intf_poke_TYPE( object_type ) \ + typeof ( void ( object_type ) ) + extern struct interface_descriptor null_intf_desc; extern struct interface null_intf; diff --git a/qemu/roms/ipxe/src/include/ipxe/io.h b/qemu/roms/ipxe/src/include/ipxe/io.h index 29ccfd1fa..af767915d 100644 --- a/qemu/roms/ipxe/src/include/ipxe/io.h +++ b/qemu/roms/ipxe/src/include/ipxe/io.h @@ -16,7 +16,7 @@ * the address parameter. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/iobuf.h b/qemu/roms/ipxe/src/include/ipxe/iobuf.h index b2b0cb440..27d285d44 100644 --- a/qemu/roms/ipxe/src/include/ipxe/iobuf.h +++ b/qemu/roms/ipxe/src/include/ipxe/iobuf.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -217,5 +217,6 @@ extern void free_iob ( struct io_buffer *iobuf ); extern void iob_pad ( struct io_buffer *iobuf, size_t min_len ); extern int iob_ensure_headroom ( struct io_buffer *iobuf, size_t len ); extern struct io_buffer * iob_concatenate ( struct list_head *list ); +extern struct io_buffer * iob_split ( struct io_buffer *iobuf, size_t len ); #endif /* _IPXE_IOBUF_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/ip.h b/qemu/roms/ipxe/src/include/ipxe/ip.h index 1a93a552e..285be6dcd 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ip.h +++ b/qemu/roms/ipxe/src/include/ipxe/ip.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/ipoib.h b/qemu/roms/ipxe/src/include/ipxe/ipoib.h index 68ff8df49..b34dd32d0 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ipoib.h +++ b/qemu/roms/ipxe/src/include/ipxe/ipoib.h @@ -6,7 +6,7 @@ * IP over Infiniband */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/ipstat.h b/qemu/roms/ipxe/src/include/ipxe/ipstat.h index c554c1859..b34ed5fcf 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ipstat.h +++ b/qemu/roms/ipxe/src/include/ipxe/ipstat.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/ipv6.h b/qemu/roms/ipxe/src/include/ipxe/ipv6.h index 48aaf677e..b500382c1 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ipv6.h +++ b/qemu/roms/ipxe/src/include/ipxe/ipv6.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/isa_ids.h b/qemu/roms/ipxe/src/include/ipxe/isa_ids.h index 1faf1148d..d815bda34 100644 --- a/qemu/roms/ipxe/src/include/ipxe/isa_ids.h +++ b/qemu/roms/ipxe/src/include/ipxe/isa_ids.h @@ -19,7 +19,7 @@ * the underlying "meaning" is big-endian. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/isapnp.h b/qemu/roms/ipxe/src/include/ipxe/isapnp.h index b0b0e98d6..59beac986 100644 --- a/qemu/roms/ipxe/src/include/ipxe/isapnp.h +++ b/qemu/roms/ipxe/src/include/ipxe/isapnp.h @@ -17,6 +17,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. * * Portions of this code: * Copyright (C) 2001 P.J.H.Fox (fox@roestock.demon.co.uk) @@ -34,7 +38,7 @@ * ***************************************************************************/ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifndef ISAPNP_H #define ISAPNP_H diff --git a/qemu/roms/ipxe/src/include/ipxe/iscsi.h b/qemu/roms/ipxe/src/include/ipxe/iscsi.h index be71360a0..c75ff4188 100644 --- a/qemu/roms/ipxe/src/include/ipxe/iscsi.h +++ b/qemu/roms/ipxe/src/include/ipxe/iscsi.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/iso9660.h b/qemu/roms/ipxe/src/include/ipxe/iso9660.h index 02c2ae377..34cb8f0a1 100644 --- a/qemu/roms/ipxe/src/include/ipxe/iso9660.h +++ b/qemu/roms/ipxe/src/include/ipxe/iso9660.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/isqrt.h b/qemu/roms/ipxe/src/include/ipxe/isqrt.h index 58ed42f0c..68255d1bc 100644 --- a/qemu/roms/ipxe/src/include/ipxe/isqrt.h +++ b/qemu/roms/ipxe/src/include/ipxe/isqrt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern unsigned long isqrt ( unsigned long value ); diff --git a/qemu/roms/ipxe/src/include/ipxe/job.h b/qemu/roms/ipxe/src/include/ipxe/job.h index a2369f7c2..7e1bd8109 100644 --- a/qemu/roms/ipxe/src/include/ipxe/job.h +++ b/qemu/roms/ipxe/src/include/ipxe/job.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/jumpscroll.h b/qemu/roms/ipxe/src/include/ipxe/jumpscroll.h new file mode 100644 index 000000000..7a5b111c1 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/jumpscroll.h @@ -0,0 +1,50 @@ +#ifndef _IPXE_JUMPSCROLL_H +#define _IPXE_JUMPSCROLL_H + +/** @file + * + * Jump scrolling + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** A jump scroller */ +struct jump_scroller { + /** Maximum number of visible rows */ + unsigned int rows; + /** Total number of items */ + unsigned int count; + /** Currently selected item */ + unsigned int current; + /** First visible item */ + unsigned int first; +}; + +/** + * Check if jump scroller is currently on first page + * + * @v scroll Jump scroller + * @ret is_first Scroller is currently on first page + */ +static inline int jump_scroll_is_first ( struct jump_scroller *scroll ) { + + return ( scroll->first == 0 ); +} + +/** + * Check if jump scroller is currently on last page + * + * @v scroll Jump scroller + * @ret is_last Scroller is currently on last page + */ +static inline int jump_scroll_is_last ( struct jump_scroller *scroll ) { + + return ( ( scroll->first + scroll->rows ) >= scroll->count ); +} + +extern int jump_scroll_key ( struct jump_scroller *scroll, int key ); +extern int jump_scroll_move ( struct jump_scroller *scroll, int move ); +extern int jump_scroll ( struct jump_scroller *scroll ); + +#endif /* _IPXE_JUMPSCROLL_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/keymap.h b/qemu/roms/ipxe/src/include/ipxe/keymap.h index 9ac42a6b1..0f1b0c656 100644 --- a/qemu/roms/ipxe/src/include/ipxe/keymap.h +++ b/qemu/roms/ipxe/src/include/ipxe/keymap.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/keys.h b/qemu/roms/ipxe/src/include/ipxe/keys.h index 8b13550b9..d15267a1f 100644 --- a/qemu/roms/ipxe/src/include/ipxe/keys.h +++ b/qemu/roms/ipxe/src/include/ipxe/keys.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* * Symbolic names for some standard ASCII characters @@ -58,6 +58,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ #define KEY_ANSI( n, terminator ) ( 0x100 * ( (n) + 1 ) + (terminator) ) +#define KEY_ANSI_N( key ) ( ( (key) / 0x100 ) - 1 ) +#define KEY_ANSI_TERMINATOR( key ) ( (key) & 0xff ) #define KEY_MIN 0x101 #define KEY_UP KEY_ANSI ( 0, 'A' ) /**< Up arrow */ diff --git a/qemu/roms/ipxe/src/include/ipxe/linebuf.h b/qemu/roms/ipxe/src/include/ipxe/linebuf.h index 706ef2554..630278a04 100644 --- a/qemu/roms/ipxe/src/include/ipxe/linebuf.h +++ b/qemu/roms/ipxe/src/include/ipxe/linebuf.h @@ -7,24 +7,24 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include /** A line buffer */ struct line_buffer { - /** Current string in the buffer */ + /** Data buffer */ char *data; - /** Length of current string, excluding the terminating NUL */ + /** Length of buffered data */ size_t len; - /** String is ready to read */ - int ready; + /** Most recently consumed length */ + size_t consumed; }; extern char * buffered_line ( struct line_buffer *linebuf ); -extern ssize_t line_buffer ( struct line_buffer *linebuf, - const char *data, size_t len ); +extern int line_buffer ( struct line_buffer *linebuf, + const char *data, size_t len ); extern void empty_line_buffer ( struct line_buffer *linebuf ); #endif /* _IPXE_LINEBUF_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/lineconsole.h b/qemu/roms/ipxe/src/include/ipxe/lineconsole.h index 925c0accc..31117e73c 100644 --- a/qemu/roms/ipxe/src/include/ipxe/lineconsole.h +++ b/qemu/roms/ipxe/src/include/ipxe/lineconsole.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/linux/linux_entropy.h b/qemu/roms/ipxe/src/include/ipxe/linux/linux_entropy.h index bd89bd52f..afef6fe19 100644 --- a/qemu/roms/ipxe/src/include/ipxe/linux/linux_entropy.h +++ b/qemu/roms/ipxe/src/include/ipxe/linux/linux_entropy.h @@ -3,11 +3,11 @@ /** @file * - * iPXE entropy API for linux + * /dev/random-based entropy source * */ -FILE_LICENCE(GPL2_OR_LATER); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef ENTROPY_LINUX #define ENTROPY_PREFIX_linux @@ -23,10 +23,12 @@ FILE_LICENCE(GPL2_OR_LATER); static inline __always_inline double ENTROPY_INLINE ( linux, min_entropy_per_sample ) ( void ) { - /* We read single bytes from /dev/random and assume that each - * contains full entropy. + /* linux_get_noise() reads a single byte from /dev/random, + * which is supposed to block until a sufficient amount of + * entropy is available. We therefore assume that each sample + * contains exactly 8 bits of entropy. */ - return 8; + return 8.0; } #endif /* _IPXE_LINUX_ENTROPY_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/linux/linux_nap.h b/qemu/roms/ipxe/src/include/ipxe/linux/linux_nap.h index 5bac7242f..d072886c7 100644 --- a/qemu/roms/ipxe/src/include/ipxe/linux/linux_nap.h +++ b/qemu/roms/ipxe/src/include/ipxe/linux/linux_nap.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE(GPL2_OR_LATER); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef NAP_LINUX #define NAP_PREFIX_linux diff --git a/qemu/roms/ipxe/src/include/ipxe/linux/linux_pci.h b/qemu/roms/ipxe/src/include/ipxe/linux/linux_pci.h index 439166733..22ae7f1bc 100644 --- a/qemu/roms/ipxe/src/include/ipxe/linux/linux_pci.h +++ b/qemu/roms/ipxe/src/include/ipxe/linux/linux_pci.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef PCIAPI_LINUX #define PCIAPI_PREFIX_linux diff --git a/qemu/roms/ipxe/src/include/ipxe/linux/linux_smbios.h b/qemu/roms/ipxe/src/include/ipxe/linux/linux_smbios.h index 6d51e13ba..16c6d8acd 100644 --- a/qemu/roms/ipxe/src/include/ipxe/linux/linux_smbios.h +++ b/qemu/roms/ipxe/src/include/ipxe/linux/linux_smbios.h @@ -3,11 +3,11 @@ /** @file * - * iPXE SMBIOS API for linux + * iPXE SMBIOS API for Linux * */ -FILE_LICENCE(GPL2_OR_LATER); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef SMBIOS_LINUX #define SMBIOS_PREFIX_linux diff --git a/qemu/roms/ipxe/src/include/ipxe/linux/linux_time.h b/qemu/roms/ipxe/src/include/ipxe/linux/linux_time.h index 93a257730..872ef5ade 100644 --- a/qemu/roms/ipxe/src/include/ipxe/linux/linux_time.h +++ b/qemu/roms/ipxe/src/include/ipxe/linux/linux_time.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef TIME_LINUX #define TIME_PREFIX_linux diff --git a/qemu/roms/ipxe/src/include/ipxe/linux/linux_timer.h b/qemu/roms/ipxe/src/include/ipxe/linux/linux_timer.h index 379507417..7f46e36b2 100644 --- a/qemu/roms/ipxe/src/include/ipxe/linux/linux_timer.h +++ b/qemu/roms/ipxe/src/include/ipxe/linux/linux_timer.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef TIMER_LINUX #define TIMER_PREFIX_linux diff --git a/qemu/roms/ipxe/src/include/ipxe/linux/linux_uaccess.h b/qemu/roms/ipxe/src/include/ipxe/linux/linux_uaccess.h index e4d16d9e0..acd919a85 100644 --- a/qemu/roms/ipxe/src/include/ipxe/linux/linux_uaccess.h +++ b/qemu/roms/ipxe/src/include/ipxe/linux/linux_uaccess.h @@ -1,116 +1,108 @@ -/* - * Copyright (C) 2010 Piotr Jaroszyński - * - * 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 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - */ - #ifndef _IPXE_LINUX_UACCESS_H #define _IPXE_LINUX_UACCESS_H -FILE_LICENCE(GPL2_OR_LATER); - /** @file * - * iPXE user access API for linux + * iPXE user access API for Linux + * + * We run with no distinction between internal and external addresses, + * so can use trivial_virt_to_user() et al. * - * In linux userspace virtual == user == phys addresses. - * Physical addresses also being the same is wrong, but there is no general way - * of converting userspace addresses to physical as what appears to be - * contiguous in userspace is physically fragmented. - * Currently only the DMA memory is special-cased, but its conversion to bus - * addresses is done in phys_to_bus. - * This is known to break virtio as it is passing phys addresses to the virtual - * device. + * We have no concept of the underlying physical addresses, since + * these are not exposed to userspace. We provide a stub + * implementation of user_to_phys() since this is required by + * alloc_memblock(). We provide no implementation of phys_to_user(); + * any code attempting to access physical addresses will therefore + * (correctly) fail to link. */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + #ifdef UACCESS_LINUX #define UACCESS_PREFIX_linux #else #define UACCESS_PREFIX_linux __linux_ #endif -static inline __always_inline userptr_t -UACCESS_INLINE(linux, phys_to_user)(unsigned long phys_addr) -{ - return phys_addr; -} - +/** + * Convert user buffer to physical address + * + * @v userptr User pointer + * @v offset Offset from user pointer + * @ret phys_addr Physical address + */ static inline __always_inline unsigned long -UACCESS_INLINE(linux, user_to_phys)(userptr_t userptr, off_t offset) -{ - return userptr + offset; +UACCESS_INLINE ( linux, user_to_phys ) ( userptr_t userptr, off_t offset ) { + + /* We do not know the real underlying physical address. We + * provide this stub implementation only because it is + * required by alloc_memblock() (which allocates memory with + * specified physical address alignment). We assume that the + * low-order bits of virtual addresses match the low-order + * bits of physical addresses, and so simply returning the + * virtual address will suffice for the purpose of determining + * alignment. + */ + return ( userptr + offset ); } static inline __always_inline userptr_t -UACCESS_INLINE(linux, virt_to_user)(volatile const void *addr) -{ - return trivial_virt_to_user(addr); +UACCESS_INLINE ( linux, virt_to_user ) ( volatile const void *addr ) { + return trivial_virt_to_user ( addr ); } static inline __always_inline void * -UACCESS_INLINE(linux, user_to_virt)(userptr_t userptr, off_t offset) -{ - return trivial_user_to_virt(userptr, offset); +UACCESS_INLINE ( linux, user_to_virt ) ( userptr_t userptr, off_t offset ) { + return trivial_user_to_virt ( userptr, offset ); } static inline __always_inline userptr_t -UACCESS_INLINE(linux, userptr_add)(userptr_t userptr, off_t offset) -{ - return trivial_userptr_add(userptr, offset); +UACCESS_INLINE ( linux, userptr_add ) ( userptr_t userptr, off_t offset ) { + return trivial_userptr_add ( userptr, offset ); } static inline __always_inline off_t -UACCESS_INLINE(linux, userptr_sub)(userptr_t userptr, userptr_t subtrahend) -{ +UACCESS_INLINE ( linux, userptr_sub ) ( userptr_t userptr, + userptr_t subtrahend ) { return trivial_userptr_sub ( userptr, subtrahend ); } static inline __always_inline void -UACCESS_INLINE(linux, memcpy_user)(userptr_t dest, off_t dest_off, userptr_t src, off_t src_off, size_t len) -{ - trivial_memcpy_user(dest, dest_off, src, src_off, len); +UACCESS_INLINE ( linux, memcpy_user ) ( userptr_t dest, off_t dest_off, + userptr_t src, off_t src_off, + size_t len ) { + trivial_memcpy_user ( dest, dest_off, src, src_off, len ); } static inline __always_inline void -UACCESS_INLINE(linux, memmove_user)(userptr_t dest, off_t dest_off, userptr_t src, off_t src_off, size_t len) -{ - trivial_memmove_user(dest, dest_off, src, src_off, len); +UACCESS_INLINE ( linux, memmove_user ) ( userptr_t dest, off_t dest_off, + userptr_t src, off_t src_off, + size_t len ) { + trivial_memmove_user ( dest, dest_off, src, src_off, len ); } static inline __always_inline int -UACCESS_INLINE(linux, memcmp_user)(userptr_t first, off_t first_off, userptr_t second, off_t second_off, size_t len) -{ - return trivial_memcmp_user(first, first_off, second, second_off, len); +UACCESS_INLINE ( linux, memcmp_user ) ( userptr_t first, off_t first_off, + userptr_t second, off_t second_off, + size_t len ) { + return trivial_memcmp_user ( first, first_off, second, second_off, len); } static inline __always_inline void -UACCESS_INLINE(linux, memset_user)(userptr_t buffer, off_t offset, int c, size_t len) -{ - trivial_memset_user(buffer, offset, c, len); +UACCESS_INLINE ( linux, memset_user ) ( userptr_t buffer, off_t offset, + int c, size_t len ) { + trivial_memset_user ( buffer, offset, c, len ); } static inline __always_inline size_t -UACCESS_INLINE(linux, strlen_user)(userptr_t buffer, off_t offset) -{ - return trivial_strlen_user(buffer, offset); +UACCESS_INLINE ( linux, strlen_user ) ( userptr_t buffer, off_t offset ) { + return trivial_strlen_user ( buffer, offset ); } static inline __always_inline off_t -UACCESS_INLINE(linux, memchr_user)(userptr_t buffer, off_t offset, int c, size_t len) -{ - return trivial_memchr_user(buffer, offset, c, len); +UACCESS_INLINE ( linux, memchr_user ) ( userptr_t buffer, off_t offset, + int c, size_t len ) { + return trivial_memchr_user ( buffer, offset, c, len ); } #endif /* _IPXE_LINUX_UACCESS_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/linux/linux_umalloc.h b/qemu/roms/ipxe/src/include/ipxe/linux/linux_umalloc.h index 4de55ecf3..1811d0bc6 100644 --- a/qemu/roms/ipxe/src/include/ipxe/linux/linux_umalloc.h +++ b/qemu/roms/ipxe/src/include/ipxe/linux/linux_umalloc.h @@ -1,14 +1,14 @@ #ifndef _IPXE_LINUX_UMALLOC_H #define _IPXE_LINUX_UMALLOC_H -FILE_LICENCE(GPL2_OR_LATER); - /** @file * - * iPXE user memory allocation API for linux + * iPXE user memory allocation API for Linux * */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + #ifdef UMALLOC_LINUX #define UMALLOC_PREFIX_linux #else diff --git a/qemu/roms/ipxe/src/include/ipxe/linux_compat.h b/qemu/roms/ipxe/src/include/ipxe/linux_compat.h index 6f6ed97d7..4704c4817 100644 --- a/qemu/roms/ipxe/src/include/ipxe/linux_compat.h +++ b/qemu/roms/ipxe/src/include/ipxe/linux_compat.h @@ -10,7 +10,7 @@ * intended to be a substitute for proper porting. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/list.h b/qemu/roms/ipxe/src/include/ipxe/list.h index 581ec9806..6a9b76f91 100644 --- a/qemu/roms/ipxe/src/include/ipxe/list.h +++ b/qemu/roms/ipxe/src/include/ipxe/list.h @@ -9,7 +9,7 @@ * list.h. */ -FILE_LICENCE ( GPL2_ONLY ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/login_ui.h b/qemu/roms/ipxe/src/include/ipxe/login_ui.h index 01e5479f7..313e07349 100644 --- a/qemu/roms/ipxe/src/include/ipxe/login_ui.h +++ b/qemu/roms/ipxe/src/include/ipxe/login_ui.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern int login_ui ( void ); diff --git a/qemu/roms/ipxe/src/include/ipxe/malloc.h b/qemu/roms/ipxe/src/include/ipxe/malloc.h index bbd6cb898..dd158b8e6 100644 --- a/qemu/roms/ipxe/src/include/ipxe/malloc.h +++ b/qemu/roms/ipxe/src/include/ipxe/malloc.h @@ -9,7 +9,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* * Prototypes for the standard functions (malloc() et al) are in @@ -77,8 +77,8 @@ static inline void * __malloc malloc_dma ( size_t size, size_t phys_align ) { * If @c ptr is NULL, no action is taken. */ static inline void free_dma ( void *ptr, size_t size ) { - free_memblock ( ptr, size ); VALGRIND_FREELIKE_BLOCK ( ptr, 0 ); + free_memblock ( ptr, size ); } /** A cache discarder */ diff --git a/qemu/roms/ipxe/src/include/ipxe/mca.h b/qemu/roms/ipxe/src/include/ipxe/mca.h index d86dab195..11470ec93 100644 --- a/qemu/roms/ipxe/src/include/ipxe/mca.h +++ b/qemu/roms/ipxe/src/include/ipxe/mca.h @@ -5,7 +5,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifndef MCA_H #define MCA_H diff --git a/qemu/roms/ipxe/src/include/ipxe/md5.h b/qemu/roms/ipxe/src/include/ipxe/md5.h index 860bc4769..05c3974c8 100644 --- a/qemu/roms/ipxe/src/include/ipxe/md5.h +++ b/qemu/roms/ipxe/src/include/ipxe/md5.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/memblock.h b/qemu/roms/ipxe/src/include/ipxe/memblock.h index 13af3e433..2bb38c460 100644 --- a/qemu/roms/ipxe/src/include/ipxe/memblock.h +++ b/qemu/roms/ipxe/src/include/ipxe/memblock.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/menu.h b/qemu/roms/ipxe/src/include/ipxe/menu.h index f2b3caccc..3cc99be48 100644 --- a/qemu/roms/ipxe/src/include/ipxe/menu.h +++ b/qemu/roms/ipxe/src/include/ipxe/menu.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/mii.h b/qemu/roms/ipxe/src/include/ipxe/mii.h index f53ad4a62..c2245b49e 100644 --- a/qemu/roms/ipxe/src/include/ipxe/mii.h +++ b/qemu/roms/ipxe/src/include/ipxe/mii.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -114,5 +114,7 @@ mii_dump ( struct mii_interface *mii ) { extern int mii_restart ( struct mii_interface *mii ); extern int mii_reset ( struct mii_interface *mii ); +extern int mii_check_link ( struct mii_interface *mii, + struct net_device *netdev ); #endif /* _IPXE_MII_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/monojob.h b/qemu/roms/ipxe/src/include/ipxe/monojob.h index aedc37eca..1661d91c2 100644 --- a/qemu/roms/ipxe/src/include/ipxe/monojob.h +++ b/qemu/roms/ipxe/src/include/ipxe/monojob.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct interface; diff --git a/qemu/roms/ipxe/src/include/ipxe/mount.h b/qemu/roms/ipxe/src/include/ipxe/mount.h index ca958117a..2d42ba080 100644 --- a/qemu/roms/ipxe/src/include/ipxe/mount.h +++ b/qemu/roms/ipxe/src/include/ipxe/mount.h @@ -9,7 +9,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** NFS MOUNT protocol number */ #define ONCRPC_MOUNT 100005 diff --git a/qemu/roms/ipxe/src/include/ipxe/nap.h b/qemu/roms/ipxe/src/include/ipxe/nap.h index afc887910..f4de778c4 100644 --- a/qemu/roms/ipxe/src/include/ipxe/nap.h +++ b/qemu/roms/ipxe/src/include/ipxe/nap.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/ndp.h b/qemu/roms/ipxe/src/include/ipxe/ndp.h index 7388f938e..1815236f5 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ndp.h +++ b/qemu/roms/ipxe/src/include/ipxe/ndp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/neighbour.h b/qemu/roms/ipxe/src/include/ipxe/neighbour.h index f2a3946f1..1c1d1b6ca 100644 --- a/qemu/roms/ipxe/src/include/ipxe/neighbour.h +++ b/qemu/roms/ipxe/src/include/ipxe/neighbour.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/net80211_err.h b/qemu/roms/ipxe/src/include/ipxe/net80211_err.h index 7df3d0d85..32ccc257f 100644 --- a/qemu/roms/ipxe/src/include/ipxe/net80211_err.h +++ b/qemu/roms/ipxe/src/include/ipxe/net80211_err.h @@ -10,7 +10,7 @@ * Copyright (c) 2009 Joshua Oreman . */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/include/ipxe/netdevice.h b/qemu/roms/ipxe/src/include/ipxe/netdevice.h index 95ad1cf1b..a1d207ffc 100644 --- a/qemu/roms/ipxe/src/include/ipxe/netdevice.h +++ b/qemu/roms/ipxe/src/include/ipxe/netdevice.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -15,6 +15,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include struct io_buffer; struct net_device; @@ -36,13 +37,12 @@ struct device; /** Maximum length of a link-layer header * - * The longest currently-supported link-layer header is for 802.11: a - * 24-byte frame header plus an 8-byte 802.3 LLC/SNAP header, plus a - * possible 4-byte VLAN header. (The IPoIB link-layer pseudo-header - * doesn't actually include link-layer addresses; see ipoib.c for - * details.) + * The longest currently-supported link-layer header is for RNDIS: an + * 8-byte RNDIS header, a 32-byte RNDIS packet message header, a + * 14-byte Ethernet header and a possible 4-byte VLAN header. Round + * up to 64 bytes. */ -#define MAX_LL_HEADER_LEN 36 +#define MAX_LL_HEADER_LEN 64 /** Maximum length of a network-layer address */ #define MAX_NET_ADDR_LEN 16 @@ -393,6 +393,8 @@ struct net_device { * indicates the error preventing link-up. */ int link_rc; + /** Link block timer */ + struct retry_timer link_block; /** Maximum packet length * * This length includes any link-layer headers. @@ -428,6 +430,14 @@ struct net_device { /** Network device receive queue processing is frozen */ #define NETDEV_RX_FROZEN 0x0004 +/** Network device interrupts are unsupported + * + * This flag can be used by a network device to indicate that + * interrupts are not supported despite the presence of an irq() + * method. + */ +#define NETDEV_IRQ_UNSUPPORTED 0x0008 + /** Link-layer protocol table */ #define LL_PROTOCOLS __table ( struct ll_protocol, "ll_protocols" ) @@ -614,6 +624,17 @@ netdev_link_ok ( struct net_device *netdev ) { return ( netdev->link_rc == 0 ); } +/** + * Check link block state of network device + * + * @v netdev Network device + * @ret link_blocked Link is blocked + */ +static inline __attribute__ (( always_inline )) int +netdev_link_blocked ( struct net_device *netdev ) { + return ( timer_running ( &netdev->link_block ) ); +} + /** * Check whether or not network device is open * @@ -633,7 +654,8 @@ netdev_is_open ( struct net_device *netdev ) { */ static inline __attribute__ (( always_inline )) int netdev_irq_supported ( struct net_device *netdev ) { - return ( netdev->op->irq != NULL ); + return ( ( netdev->op->irq != NULL ) && + ! ( netdev->state & NETDEV_IRQ_UNSUPPORTED ) ); } /** @@ -662,6 +684,9 @@ extern void netdev_rx_freeze ( struct net_device *netdev ); extern void netdev_rx_unfreeze ( struct net_device *netdev ); extern void netdev_link_err ( struct net_device *netdev, int rc ); extern void netdev_link_down ( struct net_device *netdev ); +extern void netdev_link_block ( struct net_device *netdev, + unsigned long timeout ); +extern void netdev_link_unblock ( struct net_device *netdev ); extern int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf ); extern void netdev_tx_defer ( struct net_device *netdev, struct io_buffer *iobuf ); diff --git a/qemu/roms/ipxe/src/include/ipxe/nfs.h b/qemu/roms/ipxe/src/include/ipxe/nfs.h index 498ed5a27..69b8b5381 100644 --- a/qemu/roms/ipxe/src/include/ipxe/nfs.h +++ b/qemu/roms/ipxe/src/include/ipxe/nfs.h @@ -10,7 +10,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** NFS protocol number */ #define ONCRPC_NFS 100003 diff --git a/qemu/roms/ipxe/src/include/ipxe/nfs_open.h b/qemu/roms/ipxe/src/include/ipxe/nfs_open.h index caba977f7..8572c41b3 100644 --- a/qemu/roms/ipxe/src/include/ipxe/nfs_open.h +++ b/qemu/roms/ipxe/src/include/ipxe/nfs_open.h @@ -7,6 +7,6 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #endif /* _IPXE_NFS_OPEN_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/nfs_uri.h b/qemu/roms/ipxe/src/include/ipxe/nfs_uri.h index d88bd6f65..aaa6d3749 100644 --- a/qemu/roms/ipxe/src/include/ipxe/nfs_uri.h +++ b/qemu/roms/ipxe/src/include/ipxe/nfs_uri.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/null_entropy.h b/qemu/roms/ipxe/src/include/ipxe/null_entropy.h index 646d1a17e..91adefa69 100644 --- a/qemu/roms/ipxe/src/include/ipxe/null_entropy.h +++ b/qemu/roms/ipxe/src/include/ipxe/null_entropy.h @@ -9,7 +9,7 @@ * security-sensitive environment. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/null_nap.h b/qemu/roms/ipxe/src/include/ipxe/null_nap.h index 0c0704bc7..17145b48b 100644 --- a/qemu/roms/ipxe/src/include/ipxe/null_nap.h +++ b/qemu/roms/ipxe/src/include/ipxe/null_nap.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef NAP_NULL #define NAP_PREFIX_null diff --git a/qemu/roms/ipxe/src/include/ipxe/null_reboot.h b/qemu/roms/ipxe/src/include/ipxe/null_reboot.h index 3de36c5b3..5de38afc0 100644 --- a/qemu/roms/ipxe/src/include/ipxe/null_reboot.h +++ b/qemu/roms/ipxe/src/include/ipxe/null_reboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef REBOOT_NULL #define REBOOT_PREFIX_null diff --git a/qemu/roms/ipxe/src/include/ipxe/null_sanboot.h b/qemu/roms/ipxe/src/include/ipxe/null_sanboot.h index 2b3a2c74d..58f03339f 100644 --- a/qemu/roms/ipxe/src/include/ipxe/null_sanboot.h +++ b/qemu/roms/ipxe/src/include/ipxe/null_sanboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef SANBOOT_NULL #define SANBOOT_PREFIX_null diff --git a/qemu/roms/ipxe/src/include/ipxe/null_time.h b/qemu/roms/ipxe/src/include/ipxe/null_time.h index 2b72cdf50..d2b15194b 100644 --- a/qemu/roms/ipxe/src/include/ipxe/null_time.h +++ b/qemu/roms/ipxe/src/include/ipxe/null_time.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef TIME_NULL #define TIME_PREFIX_null diff --git a/qemu/roms/ipxe/src/include/ipxe/nvo.h b/qemu/roms/ipxe/src/include/ipxe/nvo.h index 1a629da78..7a3c7a3db 100644 --- a/qemu/roms/ipxe/src/include/ipxe/nvo.h +++ b/qemu/roms/ipxe/src/include/ipxe/nvo.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/nvs.h b/qemu/roms/ipxe/src/include/ipxe/nvs.h index 4733123cf..5789f4c0d 100644 --- a/qemu/roms/ipxe/src/include/ipxe/nvs.h +++ b/qemu/roms/ipxe/src/include/ipxe/nvs.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/nvsvpd.h b/qemu/roms/ipxe/src/include/ipxe/nvsvpd.h index 3450e5c71..4c50daf85 100644 --- a/qemu/roms/ipxe/src/include/ipxe/nvsvpd.h +++ b/qemu/roms/ipxe/src/include/ipxe/nvsvpd.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/ocsp.h b/qemu/roms/ipxe/src/include/ipxe/ocsp.h index 387e28f81..71fa41dc9 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ocsp.h +++ b/qemu/roms/ipxe/src/include/ipxe/ocsp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/oncrpc.h b/qemu/roms/ipxe/src/include/ipxe/oncrpc.h index 76c1260f2..071468711 100644 --- a/qemu/roms/ipxe/src/include/ipxe/oncrpc.h +++ b/qemu/roms/ipxe/src/include/ipxe/oncrpc.h @@ -11,7 +11,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** ONC RCP Version */ #define ONCRPC_VERS 2 diff --git a/qemu/roms/ipxe/src/include/ipxe/oncrpc_iob.h b/qemu/roms/ipxe/src/include/ipxe/oncrpc_iob.h index 4858d96b5..b55043770 100644 --- a/qemu/roms/ipxe/src/include/ipxe/oncrpc_iob.h +++ b/qemu/roms/ipxe/src/include/ipxe/oncrpc_iob.h @@ -13,7 +13,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Add a string to the end of an I/O buffer diff --git a/qemu/roms/ipxe/src/include/ipxe/open.h b/qemu/roms/ipxe/src/include/ipxe/open.h index a522f0cd1..43d4cdc66 100644 --- a/qemu/roms/ipxe/src/include/ipxe/open.h +++ b/qemu/roms/ipxe/src/include/ipxe/open.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/params.h b/qemu/roms/ipxe/src/include/ipxe/params.h index c2d82d9cf..dd3292efc 100644 --- a/qemu/roms/ipxe/src/include/ipxe/params.h +++ b/qemu/roms/ipxe/src/include/ipxe/params.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/parseopt.h b/qemu/roms/ipxe/src/include/ipxe/parseopt.h index 840de7497..829b3431c 100644 --- a/qemu/roms/ipxe/src/include/ipxe/parseopt.h +++ b/qemu/roms/ipxe/src/include/ipxe/parseopt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/pccrc.h b/qemu/roms/ipxe/src/include/ipxe/pccrc.h new file mode 100644 index 000000000..7f0963428 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/pccrc.h @@ -0,0 +1,447 @@ +#ifndef _IPXE_PCCRC_H +#define _IPXE_PCCRC_H + +/** @file + * + * Peer Content Caching and Retrieval: Content Identification [MS-PCCRC] + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/****************************************************************************** + * + * Content Information versioning + * + ****************************************************************************** + * + * Note that version 1 data structures are little-endian, but version + * 2 data structures are big-endian. + */ + +/** Content Information version number */ +union peerdist_info_version { + /** Raw version number + * + * Always little-endian, regardless of whether the + * encompassing structure is version 1 (little-endian) or + * version 2 (big-endian). + */ + uint16_t raw; + /** Major:minor version number */ + struct { + /** Minor version number */ + uint8_t minor; + /** Major version number */ + uint8_t major; + } __attribute__ (( packed )); +} __attribute__ (( packed )); + +/** Content Information version 1 */ +#define PEERDIST_INFO_V1 0x0100 + +/** Content Information version 2 */ +#define PEERDIST_INFO_V2 0x0200 + +/****************************************************************************** + * + * Content Information version 1 + * + ****************************************************************************** + */ + +/** Content Information version 1 data structure header + * + * All fields are little-endian. + */ +struct peerdist_info_v1 { + /** Version number */ + union peerdist_info_version version; + /** Hash algorithm + * + * This is a @c PEERDIST_INFO_V1_HASH_XXX constant. + */ + uint32_t hash; + /** Length to skip in first segment + * + * Length at the start of the first segment which is not + * included within the content range. + */ + uint32_t first; + /** Length to read in last segment, or zero + * + * Length within the last segment which is included within the + * content range. A zero value indicates that the whole of + * the last segment is included within the content range. + */ + uint32_t last; + /** Number of segments within the content information */ + uint32_t segments; + /* Followed by a variable-length array of segment descriptions + * and a list of variable-length block descriptions: + * + * peerdist_info_v1_segment_t(digestsize) segment[segments]; + * peerdist_info_v1_block_t(digestsize, block0.blocks) block0; + * peerdist_info_v1_block_t(digestsize, block1.blocks) block1; + * ... + * peerdist_info_v1_block_t(digestsize, blockN.blocks) blockN; + */ +} __attribute__ (( packed )); + +/** SHA-256 hash algorithm */ +#define PEERDIST_INFO_V1_HASH_SHA256 0x0000800cUL + +/** SHA-384 hash algorithm */ +#define PEERDIST_INFO_V1_HASH_SHA384 0x0000800dUL + +/** SHA-512 hash algorithm */ +#define PEERDIST_INFO_V1_HASH_SHA512 0x0000800eUL + +/** Content Information version 1 segment description header + * + * All fields are little-endian. + */ +struct peerdist_info_v1_segment { + /** Offset of this segment within the content */ + uint64_t offset; + /** Length of this segment + * + * Should always be 32MB, except for the last segment within + * the content. + */ + uint32_t len; + /** Block size for this segment + * + * Should always be 64kB. Note that the last block within the + * last segment may actually be less than 64kB. + */ + uint32_t blksize; + /* Followed by two variable-length hashes: + * + * uint8_t hash[digestsize]; + * uint8_t secret[digestsize]; + * + * where digestsize is the digest size for the selected hash + * algorithm. + * + * Note that the hash is taken over (the hashes of all blocks + * within) the entire segment, even if the blocks do not + * intersect the content range (and so do not appear within + * the block list). It therefore functions only as a segment + * identifier; it cannot be used to verify the content of the + * segment (since we may not download all blocks within the + * segment). + */ +} __attribute__ (( packed )); + +/** Content Information version 1 segment description + * + * @v digestsize Digest size + */ +#define peerdist_info_v1_segment_t( digestsize ) \ + struct { \ + struct peerdist_info_v1_segment segment; \ + uint8_t hash[digestsize]; \ + uint8_t secret[digestsize]; \ + } __attribute__ (( packed )) + +/** Content Information version 1 block description header + * + * All fields are little-endian. + */ +struct peerdist_info_v1_block { + /** Number of blocks within the block description + * + * This is the number of blocks within the segment which + * overlap the content range. It may therefore be less than + * the number of blocks within the segment. + */ + uint32_t blocks; + /* Followed by an array of variable-length hashes: + * + * uint8_t hash[blocks][digestsize]; + * + * where digestsize is the digest size for the selected hash + * algorithm. + */ + } __attribute__ (( packed )); + +/** Content Information version 1 block description + * + * @v digestsize Digest size + * @v blocks Number of blocks + */ +#define peerdist_info_v1_block_t( digestsize, blocks ) \ + struct { \ + struct peerdist_info_v1_block block; \ + uint8_t hash[blocks][digestsize]; \ + } __attribute__ (( packed )) + +/****************************************************************************** + * + * Content Information version 2 + * + ****************************************************************************** + */ + +/** Content Information version 2 data structure header + * + * All fields are big-endian. + */ +struct peerdist_info_v2 { + /** Version number */ + union peerdist_info_version version; + /** Hash algorithm + * + * This is a @c PEERDIST_INFO_V2_HASH_XXX constant. + */ + uint8_t hash; + /** Offset of the first segment within the content */ + uint64_t offset; + /** Index of the first segment within the content */ + uint64_t index; + /** Length to skip in first segment + * + * Length at the start of the first segment which is not + * included within the content range. + */ + uint32_t first; + /** Length of content range, or zero + * + * Length of the content range. A zero indicates that + * everything up to the end of the last segment is included in + * the content range. + */ + uint64_t len; + /* Followed by a list of chunk descriptions */ +} __attribute__ (( packed )); + +/** SHA-512 hash algorithm with output truncated to first 256 bits */ +#define PEERDIST_INFO_V2_HASH_SHA512_TRUNC 0x04 + +/** Content Information version 2 chunk description header + * + * All fields are big-endian. + */ +struct peerdist_info_v2_chunk { + /** Chunk type */ + uint8_t type; + /** Chunk data length */ + uint32_t len; + /* Followed by an array of segment descriptions: + * + * peerdist_info_v2_segment_t(digestsize) segment[segments] + * + * where digestsize is the digest size for the selected hash + * algorithm, and segments is equal to @c len divided by the + * size of each segment array entry. + */ +} __attribute__ (( packed )); + +/** Content Information version 2 chunk description + * + * @v digestsize Digest size + */ +#define peerdist_info_v2_chunk_t( digestsize ) \ + struct { \ + struct peerdist_info_v2_chunk chunk; \ + peerdist_info_v2_segment_t ( digestsize ) segment[0]; \ + } __attribute__ (( packed )) + +/** Chunk type */ +#define PEERDIST_INFO_V2_CHUNK_TYPE 0x00 + +/** Content Information version 2 segment description header + * + * All fields are big-endian. + */ +struct peerdist_info_v2_segment { + /** Segment length */ + uint32_t len; + /* Followed by two variable-length hashes: + * + * uint8_t hash[digestsize]; + * uint8_t secret[digestsize]; + * + * where digestsize is the digest size for the selected hash + * algorithm. + */ +} __attribute__ (( packed )); + +/** Content Information version 2 segment description + * + * @v digestsize Digest size + */ +#define peerdist_info_v2_segment_t( digestsize ) \ + struct { \ + struct peerdist_info_v2_segment segment; \ + uint8_t hash[digestsize]; \ + uint8_t secret[digestsize]; \ + } __attribute__ (( packed )) + +/****************************************************************************** + * + * Content Information + * + ****************************************************************************** + */ + +/** Maximum digest size for any supported algorithm + * + * The largest digest size that we support is for SHA-512 at 64 bytes + */ +#define PEERDIST_DIGEST_MAX_SIZE 64 + +/** Raw content information */ +struct peerdist_raw { + /** Data buffer */ + userptr_t data; + /** Length of data buffer */ + size_t len; +}; + +/** A content range */ +struct peerdist_range { + /** Start offset */ + size_t start; + /** End offset */ + size_t end; +}; + +/** Content information */ +struct peerdist_info { + /** Raw content information */ + struct peerdist_raw raw; + + /** Content information operations */ + struct peerdist_info_operations *op; + /** Digest algorithm */ + struct digest_algorithm *digest; + /** Digest size + * + * Note that this may be shorter than the digest size of the + * digest algorithm. The truncation does not always take + * place as soon as a digest is calculated. For example, + * version 2 content information uses SHA-512 with a truncated + * digest size of 32 (256 bits), but the segment identifier + * ("HoHoDk") is calculated by using HMAC with the full + * SHA-512 digest and then truncating the HMAC output, rather + * than by simply using HMAC with the truncated SHA-512 + * digest. This is, of course, totally undocumented. + */ + size_t digestsize; + /** Content range */ + struct peerdist_range range; + /** Trimmed content range */ + struct peerdist_range trim; + /** Number of segments within the content information */ + unsigned int segments; +}; + +/** A content information segment */ +struct peerdist_info_segment { + /** Content information */ + const struct peerdist_info *info; + /** Segment index */ + unsigned int index; + + /** Content range + * + * Note that this range may exceed the overall content range. + */ + struct peerdist_range range; + /** Number of blocks within this segment */ + unsigned int blocks; + /** Block size */ + size_t blksize; + /** Segment hash of data + * + * This is MS-PCCRC's "HoD". + */ + uint8_t hash[PEERDIST_DIGEST_MAX_SIZE]; + /** Segment secret + * + * This is MS-PCCRC's "Ke = Kp". + */ + uint8_t secret[PEERDIST_DIGEST_MAX_SIZE]; + /** Segment identifier + * + * This is MS-PCCRC's "HoHoDk". + */ + uint8_t id[PEERDIST_DIGEST_MAX_SIZE]; +}; + +/** Magic string constant used to calculate segment identifier + * + * Note that the MS-PCCRC specification states that this constant is + * + * "the null-terminated ASCII string constant "MS_P2P_CACHING"; + * string literals are all ASCII strings with NULL terminators + * unless otherwise noted." + * + * The specification lies. This constant is a UTF-16LE string, not an + * ASCII string. The terminating wNUL *is* included within the + * constant. + */ +#define PEERDIST_SEGMENT_ID_MAGIC L"MS_P2P_CACHING" + +/** A content information block */ +struct peerdist_info_block { + /** Content information segment */ + const struct peerdist_info_segment *segment; + /** Block index */ + unsigned int index; + + /** Content range + * + * Note that this range may exceed the overall content range. + */ + struct peerdist_range range; + /** Trimmed content range */ + struct peerdist_range trim; + /** Block hash */ + uint8_t hash[PEERDIST_DIGEST_MAX_SIZE]; +}; + +/** Content information operations */ +struct peerdist_info_operations { + /** + * Populate content information + * + * @v info Content information to fill in + * @ret rc Return status code + */ + int ( * info ) ( struct peerdist_info *info ); + /** + * Populate content information segment + * + * @v segment Content information segment to fill in + * @ret rc Return status code + */ + int ( * segment ) ( struct peerdist_info_segment *segment ); + /** + * Populate content information block + * + * @v block Content information block to fill in + * @ret rc Return status code + */ + int ( * block ) ( struct peerdist_info_block *block ); +}; + +extern struct digest_algorithm sha512_trunc_algorithm; + +extern int peerdist_info ( userptr_t data, size_t len, + struct peerdist_info *info ); +extern int peerdist_info_segment ( const struct peerdist_info *info, + struct peerdist_info_segment *segment, + unsigned int index ); +extern int peerdist_info_block ( const struct peerdist_info_segment *segment, + struct peerdist_info_block *block, + unsigned int index ); + +#endif /* _IPXE_PCCRC_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/pccrd.h b/qemu/roms/ipxe/src/include/ipxe/pccrd.h new file mode 100644 index 000000000..3daa92f29 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/pccrd.h @@ -0,0 +1,47 @@ +#ifndef _IPXE_PCCRD_H +#define _IPXE_PCCRD_H + +/** @file + * + * Peer Content Caching and Retrieval: Discovery Protocol [MS-PCCRD] + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** PeerDist discovery port */ +#define PEERDIST_DISCOVERY_PORT 3702 + +/** PeerDist discovery IPv4 address (239.255.255.250) */ +#define PEERDIST_DISCOVERY_IPV4 \ + ( ( 239 << 24 ) | ( 255 << 16 ) | ( 255 << 8 ) | ( 250 << 0 ) ) + +/** PeerDist discovery IPv6 address (ff02::c) */ +#define PEERDIST_DISCOVERY_IPV6 \ + { 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc } + +/** A PeerDist discovery reply block count */ +struct peerdist_discovery_block_count { + /** Count (as an eight-digit hex value) */ + char hex[8]; +} __attribute__ (( packed )); + +/** A PeerDist discovery reply */ +struct peerdist_discovery_reply { + /** List of segment ID strings + * + * The list is terminated with a zero-length string. + */ + char *ids; + /** List of peer locations + * + * The list is terminated with a zero-length string. + */ + char *locations; +}; + +extern char * peerdist_discovery_request ( const char *uuid, const char *id ); +extern int peerdist_discovery_reply ( char *data, size_t len, + struct peerdist_discovery_reply *reply ); + +#endif /* _IPXE_PCCRD_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/pccrr.h b/qemu/roms/ipxe/src/include/ipxe/pccrr.h new file mode 100644 index 000000000..1ea86c40d --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/pccrr.h @@ -0,0 +1,376 @@ +#ifndef _IPXE_PCCRR_H +#define _IPXE_PCCRR_H + +/** @file + * + * Peer Content Caching and Retrieval: Retrieval Protocol [MS-PCCRR] + * + * All fields are in network byte order. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** Magic retrieval URI path */ +#define PEERDIST_MAGIC_PATH "/116B50EB-ECE2-41ac-8429-9F9E963361B7/" + +/** Retrieval protocol version */ +union peerdist_msg_version { + /** Raw version number */ + uint32_t raw; + /** Major:minor version number */ + struct { + /** Minor version number */ + uint16_t minor; + /** Major version number */ + uint16_t major; + } __attribute__ (( packed )); +} __attribute__ (( packed )); + +/** Retrieval protocol version 1.0 */ +#define PEERDIST_MSG_VERSION_1_0 0x00000001UL + +/** Retrieval protocol version 2.0 */ +#define PEERDIST_MSG_VERSION_2_0 0x00000002UL + +/** Retrieval protocol supported versions */ +struct peerdist_msg_versions { + /** Minimum supported protocol version */ + union peerdist_msg_version min; + /** Maximum supported protocol version */ + union peerdist_msg_version max; +} __attribute__ (( packed )); + +/** Retrieval protocol block range */ +struct peerdist_msg_range { + /** First block in range */ + uint32_t first; + /** Number of blocks in range */ + uint32_t count; +} __attribute__ (( packed )); + +/** Retrieval protocol segment ID header */ +struct peerdist_msg_segment { + /** Digest size (i.e. length of segment ID) */ + uint32_t digestsize; + /* Followed by a single variable-length ID and padding: + * + * uint8_t id[digestsize]; + * uint8_t pad[ (-digestsize) & 0x3 ]; + */ +} __attribute__ (( packed )); + +/** Retrieval protocol segment ID + * + * @v digestsize Digest size + */ +#define peerdist_msg_segment_t( digestsize ) \ + struct { \ + struct peerdist_msg_segment segment; \ + uint8_t id[digestsize]; \ + uint8_t pad[ ( -(digestsize) ) & 0x3 ]; \ + } __attribute__ (( packed )) + +/** Retrieval protocol block range list header */ +struct peerdist_msg_ranges { + /** Number of ranges */ + uint32_t count; + /* Followed by an array of block ranges: + * + * struct peerdist_msg_range range[count]; + */ +} __attribute__ (( packed )); + +/** Retrieval protocol block range list + * + * @v count Number of ranges + */ +#define peerdist_msg_ranges_t( count ) \ + struct { \ + struct peerdist_msg_ranges ranges; \ + struct peerdist_msg_range range[count]; \ + } __attribute__ (( packed )) + +/** Retrieval protocol data block header */ +struct peerdist_msg_block { + /** Length of data block */ + uint32_t len; + /* Followed by the (encrypted) data block: + * + * uint8_t data[len]; + */ +} __attribute__ (( packed )); + +/** Retrieval protocol data block */ +#define peerdist_msg_block_t( len ) \ + struct { \ + struct peerdist_msg_block block; \ + uint8_t data[len]; \ + } __attribute__ (( packed )) + +/** Retrieval protocol initialisation vector header */ +struct peerdist_msg_iv { + /** Cipher block size */ + uint32_t blksize; + /* Followed by the initialisation vector: + * + * uint8_t data[blksize]; + */ +} __attribute__ (( packed )); + +/** Retrieval protocol initialisation vector */ +#define peerdist_msg_iv_t( blksize ) \ + struct { \ + struct peerdist_msg_iv iv; \ + uint8_t data[blksize]; \ + } __attribute__ (( packed )) + +/** Retrieval protocol useless VRF data header */ +struct peerdist_msg_useless_vrf { + /** Length of useless VRF data */ + uint32_t len; + /* Followed by a variable-length useless VRF data block and + * padding: + * + * uint8_t data[len]; + * uint8_t pad[ (-len) & 0x3 ]; + */ +} __attribute__ (( packed )); + +/** Retrieval protocol useless VRF data */ +#define peerdist_msg_useless_vrf_t( vrf_len ) \ + struct { \ + struct peerdist_msg_useless_vrf vrf; \ + uint8_t data[vrf_len]; \ + uint8_t pad[ ( -(vrf_len) ) & 0x3 ]; \ + } __attribute__ (( packed )) + +/** Retrieval protocol message header */ +struct peerdist_msg_header { + /** Protocol version + * + * This is the protocol version in which the message type was + * first defined. + */ + union peerdist_msg_version version; + /** Message type */ + uint32_t type; + /** Message size (including this header) */ + uint32_t len; + /** Cryptographic algorithm ID */ + uint32_t algorithm; +} __attribute__ (( packed )); + +/** Retrieval protocol cryptographic algorithm IDs */ +enum peerdist_msg_algorithm { + /** No encryption */ + PEERDIST_MSG_PLAINTEXT = 0x00000000UL, + /** AES-128 in CBC mode */ + PEERDIST_MSG_AES_128_CBC = 0x00000001UL, + /** AES-192 in CBC mode */ + PEERDIST_MSG_AES_192_CBC = 0x00000002UL, + /** AES-256 in CBC mode */ + PEERDIST_MSG_AES_256_CBC = 0x00000003UL, +}; + +/** Retrieval protocol transport response header */ +struct peerdist_msg_transport_header { + /** Length (excluding this header) + * + * This seems to be identical in both purpose and value to the + * length found within the message header, and therefore + * serves no useful purpose. + */ + uint32_t len; +} __attribute__ (( packed )); + +/** Retrieval protocol negotiation request */ +struct peerdist_msg_nego_req { + /** Message header */ + struct peerdist_msg_header hdr; + /** Supported versions */ + struct peerdist_msg_versions versions; +} __attribute__ (( packed )); + +/** Retrieval protocol negotiation request version */ +#define PEERDIST_MSG_NEGO_REQ_VERSION PEERDIST_MSG_VERSION_1_0 + +/** Retrieval protocol negotiation request type */ +#define PEERDIST_MSG_NEGO_REQ_TYPE 0x00000000UL + +/** Retrieval protocol negotiation response */ +struct peerdist_msg_nego_resp { + /** Message header */ + struct peerdist_msg_header hdr; + /** Supported versions */ + struct peerdist_msg_versions versions; +} __attribute__ (( packed )); + +/** Retrieval protocol negotiation response version */ +#define PEERDIST_MSG_NEGO_RESP_VERSION PEERDIST_MSG_VERSION_1_0 + +/** Retrieval protocol negotiation response type */ +#define PEERDIST_MSG_NEGO_RESP_TYPE 0x00000001UL + +/** Retrieval protocol block list request header */ +struct peerdist_msg_getblklist { + /** Message header */ + struct peerdist_msg_header hdr; + /* Followed by a segment ID and a block range list: + * + * peerdist_msg_segment_t(digestsize) segment; + * peerdist_msg_ranges_t(count) ranges; + */ +} __attribute__ (( packed )); + +/** Retrieval protocol block list request + * + * @v digestsize Digest size + * @v count Block range count + */ +#define peerdist_msg_getblklist_t( digestsize, count ) \ + struct { \ + struct peerdist_msg_getblklist getblklist; \ + peerdist_msg_segment_t ( digestsize ) segment; \ + peerdist_msg_ranges_t ( count ) ranges; \ + } __attribute__ (( packed )) + +/** Retrieval protocol block list request version */ +#define PEERDIST_MSG_GETBLKLIST_VERSION PEERDIST_MSG_VERSION_1_0 + +/** Retrieval protocol block list request type */ +#define PEERDIST_MSG_GETBLKLIST_TYPE 0x00000002UL + +/** Retrieval protocol block fetch request header */ +struct peerdist_msg_getblks { + /** Message header */ + struct peerdist_msg_header hdr; + /* Followed by a segment ID, a block range list, and a useless + * VRF block: + * + * peerdist_msg_segment_t(digestsize) segment; + * peerdist_msg_ranges_t(count) ranges; + * peerdist_msg_vrf_t(vrf_len) vrf; + */ +} __attribute__ (( packed )); + +/** Retrieval protocol block fetch request + * + * @v digestsize Digest size + * @v count Block range count + * @v vrf_len Length of uselessness + */ +#define peerdist_msg_getblks_t( digestsize, count, vrf_len ) \ + struct { \ + struct peerdist_msg_getblks getblks; \ + peerdist_msg_segment_t ( digestsize ) segment; \ + peerdist_msg_ranges_t ( count ) ranges; \ + peerdist_msg_useless_vrf_t ( vrf_len ); \ + } __attribute__ (( packed )) + +/** Retrieval protocol block fetch request version */ +#define PEERDIST_MSG_GETBLKS_VERSION PEERDIST_MSG_VERSION_1_0 + +/** Retrieval protocol block fetch request type */ +#define PEERDIST_MSG_GETBLKS_TYPE 0x00000003UL + +/** Retrieval protocol block list response header */ +struct peerdist_msg_blklist { + /** Message header */ + struct peerdist_msg_header hdr; + /* Followed by a segment ID, a block range list, and a next + * block index: + * + * peerdist_msg_segment_t(digestsize) segment; + * peerdist_msg_ranges_t(count) ranges; + * uint32_t next; + */ +} __attribute__ (( packed )); + +/** Retrieval protocol block list response + * + * @v digestsize Digest size + * @v count Block range count + */ +#define peerdist_msg_blklist_t( digestsize, count ) \ + struct { \ + struct peerdist_msg_blklist blklist; \ + peerdist_msg_segment_t ( digestsize ) segment; \ + peerdist_msg_ranges_t ( count ) ranges; \ + uint32_t next; \ + } __attribute__ (( packed )) + +/** Retrieval protocol block list response version */ +#define PEERDIST_MSG_BLKLIST_VERSION PEERDIST_MSG_VERSION_1_0 + +/** Retrieval protocol block list response type */ +#define PEERDIST_MSG_BLKLIST_TYPE 0x00000004UL + +/** Retrieval protocol block fetch response header */ +struct peerdist_msg_blk { + /** Message header */ + struct peerdist_msg_header hdr; + /* Followed by a segment ID, a block index, a next block + * index, a data block, a useless VRF block, and an + * initialisation vector: + * + * peerdist_msg_segment_t(digestsize) segment; + * uint32_t index; + * uint32_t next; + * peerdist_msg_block_t(len) data; + * peerdist_msg_useless_vrf_t(vrf_len) vrf; + * peerdist_msg_iv_t(blksize) iv; + */ +} __attribute__ (( packed )); + +/** Retrieval protocol block fetch response + * + * @v digestsize Digest size + * @v len Data block length + * @v vrf_len Length of uselessness + * @v blksize Cipher block size + */ +#define peerdist_msg_blk_t( digestsize, len, vrf_len, blksize ) \ + struct { \ + struct peerdist_msg_blk blk; \ + peerdist_msg_segment_t ( digestsize ) segment; \ + uint32_t index; \ + uint32_t next; \ + peerdist_msg_block_t ( len ) block; \ + peerdist_msg_useless_vrf_t ( vrf_len ) vrf; \ + peerdist_msg_iv_t ( blksize ) iv; \ + } __attribute__ (( packed )) + +/** Retrieval protocol block fetch response version */ +#define PEERDIST_MSG_BLK_VERSION PEERDIST_MSG_VERSION_1_0 + +/** Retrieval protocol block fetch response type */ +#define PEERDIST_MSG_BLK_TYPE 0x00000005UL + +/** + * Parse retrieval protocol block fetch response + * + * @v raw Raw data + * @v raw_len Length of raw data + * @v digestsize Digest size + * @v blksize Cipher block size + * @v blk Structure to fill in + * @ret rc Return status code + */ +#define peerdist_msg_blk( raw, raw_len, digestsize, blksize, blk ) ( { \ + assert ( sizeof ( (blk)->segment.id ) == (digestsize) ); \ + assert ( sizeof ( (blk)->block.data ) == 0 ); \ + assert ( sizeof ( (blk)->vrf.data ) == 0 ); \ + assert ( sizeof ( (blk)->iv.data ) == blksize ); \ + peerdist_msg_blk_untyped ( (raw), (raw_len), (digestsize), \ + (blksize), blk ); \ + } ) + +extern int peerdist_msg_blk_untyped ( userptr_t raw, size_t raw_len, + size_t digestsize, size_t blksize, + void *out ); + +#endif /* _IPXE_PCCRR_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/pci.h b/qemu/roms/ipxe/src/include/ipxe/pci.h index 692771ebe..a841e00ff 100644 --- a/qemu/roms/ipxe/src/include/ipxe/pci.h +++ b/qemu/roms/ipxe/src/include/ipxe/pci.h @@ -1,268 +1,132 @@ #ifndef _IPXE_PCI_H #define _IPXE_PCI_H -/* - * Support for NE2000 PCI clones added David Monro June 1997 - * Generalised for other PCI NICs by Ken Yap July 1997 - * PCI support rewritten by Michael Brown 2006 +/** @file + * + * PCI bus * - * Most of this is taken from /usr/src/linux/include/linux/pci.h. - */ - -/* - * 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) any later version. */ -FILE_LICENCE ( GPL2_ONLY ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include #include -#include "pci_ids.h" -/* - * PCI constants - * - */ - -#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ -#define PCI_COMMAND_MEM 0x2 /* Enable response in mem space */ -#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ - -#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ -#define PCI_LATENCY_TIMER 0x0d /* 8 bits */ - -#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */ -#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ -#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ -#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */ -#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ -#define PCI_COMMAND_SERR 0x100 /* Enable SERR */ -#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ -#define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */ - - -#define PCI_VENDOR_ID 0x00 /* 16 bits */ -#define PCI_DEVICE_ID 0x02 /* 16 bits */ -#define PCI_COMMAND 0x04 /* 16 bits */ - -#define PCI_STATUS 0x06 /* 16 bits */ -#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ -#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ -#define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */ -#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ -#define PCI_STATUS_PARITY 0x100 /* Detected parity error */ -#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */ -#define PCI_STATUS_DEVSEL_FAST 0x000 -#define PCI_STATUS_DEVSEL_MEDIUM 0x200 -#define PCI_STATUS_DEVSEL_SLOW 0x400 -#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */ -#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */ -#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */ -#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */ -#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */ - -#define PCI_REVISION 0x08 /* 8 bits */ -#define PCI_REVISION_ID 0x08 /* 8 bits */ -#define PCI_CLASS_REVISION 0x08 /* 32 bits */ -#define PCI_CLASS_CODE 0x0b /* 8 bits */ -#define PCI_SUBCLASS_CODE 0x0a /* 8 bits */ -#define PCI_HEADER_TYPE 0x0e /* 8 bits */ -#define PCI_HEADER_TYPE_NORMAL 0 -#define PCI_HEADER_TYPE_BRIDGE 1 -#define PCI_HEADER_TYPE_CARDBUS 2 - - -/* Header type 0 (normal devices) */ -#define PCI_CARDBUS_CIS 0x28 +/** PCI vendor ID */ +#define PCI_VENDOR_ID 0x00 + +/** PCI device ID */ +#define PCI_DEVICE_ID 0x02 + +/** PCI command */ +#define PCI_COMMAND 0x04 +#define PCI_COMMAND_IO 0x0001 /**< I/O space */ +#define PCI_COMMAND_MEM 0x0002 /**< Memory space */ +#define PCI_COMMAND_MASTER 0x0004 /**< Bus master */ +#define PCI_COMMAND_INVALIDATE 0x0010 /**< Mem. write & invalidate */ +#define PCI_COMMAND_PARITY 0x0040 /**< Parity error response */ +#define PCI_COMMAND_SERR 0x0100 /**< SERR# enable */ +#define PCI_COMMAND_INTX_DISABLE 0x0400 /**< Interrupt disable */ + +/** PCI status */ +#define PCI_STATUS 0x06 +#define PCI_STATUS_CAP_LIST 0x0010 /**< Capabilities list */ +#define PCI_STATUS_PARITY 0x0100 /**< Master data parity error */ +#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /**< Received target abort */ +#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /**< Received master abort */ +#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /**< Signalled system error */ +#define PCI_STATUS_DETECTED_PARITY 0x8000 /**< Detected parity error */ + +/** PCI revision */ +#define PCI_REVISION 0x08 + +/** PCI cache line size */ +#define PCI_CACHE_LINE_SIZE 0x0c + +/** PCI latency timer */ +#define PCI_LATENCY_TIMER 0x0d + +/** PCI header type */ +#define PCI_HEADER_TYPE 0x0e +#define PCI_HEADER_TYPE_NORMAL 0x00 /**< Normal header */ +#define PCI_HEADER_TYPE_BRIDGE 0x01 /**< PCI-to-PCI bridge header */ +#define PCI_HEADER_TYPE_CARDBUS 0x02 /**< CardBus header */ +#define PCI_HEADER_TYPE_MASK 0x7f /**< Header type mask */ +#define PCI_HEADER_TYPE_MULTI 0x80 /**< Multi-function device */ + +/** PCI base address registers */ +#define PCI_BASE_ADDRESS(n) ( 0x10 + ( 4 * (n) ) ) +#define PCI_BASE_ADDRESS_0 PCI_BASE_ADDRESS ( 0 ) +#define PCI_BASE_ADDRESS_1 PCI_BASE_ADDRESS ( 1 ) +#define PCI_BASE_ADDRESS_2 PCI_BASE_ADDRESS ( 2 ) +#define PCI_BASE_ADDRESS_3 PCI_BASE_ADDRESS ( 3 ) +#define PCI_BASE_ADDRESS_4 PCI_BASE_ADDRESS ( 4 ) +#define PCI_BASE_ADDRESS_5 PCI_BASE_ADDRESS ( 5 ) +#define PCI_BASE_ADDRESS_SPACE_IO 0x00000001UL /**< I/O BAR */ +#define PCI_BASE_ADDRESS_IO_MASK 0x00000003UL /**< I/O BAR mask */ +#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x00000004UL /**< 64-bit memory */ +#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x00000006UL /**< Memory type mask */ +#define PCI_BASE_ADDRESS_MEM_MASK 0x0000000fUL /**< Memory BAR mask */ + +/** PCI subsystem vendor ID */ #define PCI_SUBSYSTEM_VENDOR_ID 0x2c + +/** PCI subsystem ID */ #define PCI_SUBSYSTEM_ID 0x2e -#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ -#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits */ -#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits */ -#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */ -#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */ -#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */ - -#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ -#define PCI_BASE_ADDRESS_SPACE_IO 0x01 -#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 - -#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 -#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ -#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ -#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ -#define PCI_BASE_ADDRESS_MEM_MASK (~0x0f) -#define PCI_BASE_ADDRESS_IO_MASK (~0x03) -#define PCI_ROM_ADDRESS 0x30 /* 32 bits */ -#define PCI_ROM_ADDRESS_ENABLE 0x01 /* Write 1 to enable ROM, - bits 31..11 are address, - 10..2 are reserved */ - -#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */ - -#define PCI_INTERRUPT_LINE 0x3c /* IRQ number (0-15) */ -#define PCI_INTERRUPT_PIN 0x3d /* IRQ pin on PCI bus (A-D) */ - -/* Header type 1 (PCI-to-PCI bridges) */ -#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */ -#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */ -#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */ -#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */ -#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ -#define PCI_IO_LIMIT 0x1d -#define PCI_IO_RANGE_TYPE_MASK 0x0f /* I/O bridging type */ -#define PCI_IO_RANGE_TYPE_16 0x00 -#define PCI_IO_RANGE_TYPE_32 0x01 -#define PCI_IO_RANGE_MASK ~0x0f -#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ -#define PCI_MEMORY_BASE 0x20 /* Memory range behind */ -#define PCI_MEMORY_LIMIT 0x22 -#define PCI_MEMORY_RANGE_TYPE_MASK 0x0f -#define PCI_MEMORY_RANGE_MASK ~0x0f -#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */ -#define PCI_PREF_MEMORY_LIMIT 0x26 -#define PCI_PREF_RANGE_TYPE_MASK 0x0f -#define PCI_PREF_RANGE_TYPE_32 0x00 -#define PCI_PREF_RANGE_TYPE_64 0x01 -#define PCI_PREF_RANGE_MASK ~0x0f -#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */ -#define PCI_PREF_LIMIT_UPPER32 0x2c -#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */ -#define PCI_IO_LIMIT_UPPER16 0x32 -/* 0x34 same as for htype 0 */ -/* 0x35-0x3b is reserved */ -#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */ -/* 0x3c-0x3d are same as for htype 0 */ -#define PCI_BRIDGE_CONTROL 0x3e -#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */ -#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */ -#define PCI_BRIDGE_CTL_NO_ISA 0x04 /* Disable bridging of ISA ports */ -#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */ -#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */ -#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */ -#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */ +/** PCI expansion ROM base address */ +#define PCI_ROM_ADDRESS 0x30 +/** PCI capabilities pointer */ +#define PCI_CAPABILITY_LIST 0x34 + +/** CardBus capabilities pointer */ #define PCI_CB_CAPABILITY_LIST 0x14 -/* Capability lists */ - -#define PCI_CAP_LIST_ID 0 /* Capability ID */ -#define PCI_CAP_ID_PM 0x01 /* Power Management */ -#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */ -#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */ -#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */ -#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */ -#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */ -#define PCI_CAP_ID_VNDR 0x09 /* Vendor specific */ -#define PCI_CAP_ID_EXP 0x10 /* PCI Express */ -#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ -#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ -#define PCI_CAP_SIZEOF 4 - -/* Power Management Registers */ - -#define PCI_PM_PMC 2 /* PM Capabilities Register */ -#define PCI_PM_CAP_VER_MASK 0x0007 /* Version */ -#define PCI_PM_CAP_PME_CLOCK 0x0008 /* PME clock required */ -#define PCI_PM_CAP_RESERVED 0x0010 /* Reserved field */ -#define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization */ -#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxiliary power support mask */ -#define PCI_PM_CAP_D1 0x0200 /* D1 power state support */ -#define PCI_PM_CAP_D2 0x0400 /* D2 power state support */ -#define PCI_PM_CAP_PME 0x0800 /* PME pin supported */ -#define PCI_PM_CAP_PME_MASK 0xF800 /* PME Mask of all supported states */ -#define PCI_PM_CAP_PME_D0 0x0800 /* PME# from D0 */ -#define PCI_PM_CAP_PME_D1 0x1000 /* PME# from D1 */ -#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */ -#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */ -#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */ -#define PCI_PM_CTRL 4 /* PM control and status register */ -#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */ -#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */ -#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */ -#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */ -#define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */ -#define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions (??) */ -#define PCI_PM_PPB_B2_B3 0x40 /* Stop clock when in D3hot (??) */ -#define PCI_PM_BPCC_ENABLE 0x80 /* Bus power/clock control enable (??) */ -#define PCI_PM_DATA_REGISTER 7 /* (??) */ -#define PCI_PM_SIZEOF 8 - -/* AGP registers */ - -#define PCI_AGP_VERSION 2 /* BCD version number */ -#define PCI_AGP_RFU 3 /* Rest of capability flags */ -#define PCI_AGP_STATUS 4 /* Status register */ -#define PCI_AGP_STATUS_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */ -#define PCI_AGP_STATUS_SBA 0x0200 /* Sideband addressing supported */ -#define PCI_AGP_STATUS_64BIT 0x0020 /* 64-bit addressing supported */ -#define PCI_AGP_STATUS_FW 0x0010 /* FW transfers supported */ -#define PCI_AGP_STATUS_RATE4 0x0004 /* 4x transfer rate supported */ -#define PCI_AGP_STATUS_RATE2 0x0002 /* 2x transfer rate supported */ -#define PCI_AGP_STATUS_RATE1 0x0001 /* 1x transfer rate supported */ -#define PCI_AGP_COMMAND 8 /* Control register */ -#define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */ -#define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */ -#define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */ -#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow processing of 64-bit addresses */ -#define PCI_AGP_COMMAND_FW 0x0010 /* Force FW transfers */ -#define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate */ -#define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate */ -#define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate */ -#define PCI_AGP_SIZEOF 12 - -/* Slot Identification */ - -#define PCI_SID_ESR 2 /* Expansion Slot Register */ -#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */ -#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */ -#define PCI_SID_CHASSIS_NR 3 /* Chassis Number */ - -/* Message Signalled Interrupts registers */ - -#define PCI_MSI_FLAGS 2 /* Various flags */ -#define PCI_MSI_FLAGS_64BIT 0x80 /* 64-bit addresses allowed */ -#define PCI_MSI_FLAGS_QSIZE 0x70 /* Message queue size configured */ -#define PCI_MSI_FLAGS_QMASK 0x0e /* Maximum queue size available */ -#define PCI_MSI_FLAGS_ENABLE 0x01 /* MSI feature enabled */ -#define PCI_MSI_RFU 3 /* Rest of capability flags */ -#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */ -#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ -#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ -#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ - -/* Advanced Error Reporting */ - -#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ -#define PCI_ERR_UNC_TRAIN 0x00000001 /* Training */ -#define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */ -#define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */ -#define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */ -#define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */ -#define PCI_ERR_UNC_COMP_ABORT 0x00008000 /* Completer Abort */ -#define PCI_ERR_UNC_UNX_COMP 0x00010000 /* Unexpected Completion */ -#define PCI_ERR_UNC_RX_OVER 0x00020000 /* Receiver Overflow */ -#define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */ -#define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */ -#define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */ -#define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */ - /* Same bits as above */ -#define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */ - /* Same bits as above */ -#define PCI_ERR_COR_STATUS 16 /* Correctable Error Status */ -#define PCI_ERR_COR_RCVR 0x00000001 /* Receiver Error Status */ -#define PCI_ERR_COR_BAD_TLP 0x00000040 /* Bad TLP Status */ -#define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */ -#define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */ -#define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */ -#define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */ - /* Same bits as above */ +/** PCI interrupt line */ +#define PCI_INTERRUPT_LINE 0x3c + +/** Capability ID */ +#define PCI_CAP_ID 0x00 +#define PCI_CAP_ID_PM 0x01 /**< Power management */ +#define PCI_CAP_ID_VPD 0x03 /**< Vital product data */ +#define PCI_CAP_ID_VNDR 0x09 /**< Vendor-specific */ +#define PCI_CAP_ID_EXP 0x10 /**< PCI Express */ + +/** Next capability */ +#define PCI_CAP_NEXT 0x01 + +/** Power management control and status */ +#define PCI_PM_CTRL 0x04 +#define PCI_PM_CTRL_STATE_MASK 0x0003 /**< Current power state */ +#define PCI_PM_CTRL_PME_ENABLE 0x0100 /**< PME pin enable */ +#define PCI_PM_CTRL_PME_STATUS 0x8000 /**< PME pin status */ + +/** Uncorrectable error status */ +#define PCI_ERR_UNCOR_STATUS 0x04 + +/** Network controller */ +#define PCI_CLASS_NETWORK 0x02 + +/** Serial bus controller */ +#define PCI_CLASS_SERIAL 0x0c +#define PCI_CLASS_SERIAL_USB 0x03 /**< USB controller */ +#define PCI_CLASS_SERIAL_USB_UHCI 0x00 /**< UHCI USB controller */ +#define PCI_CLASS_SERIAL_USB_OHCI 0x10 /**< OHCI USB controller */ +#define PCI_CLASS_SERIAL_USB_EHCI 0x20 /**< ECHI USB controller */ +#define PCI_CLASS_SERIAL_USB_XHCI 0x30 /**< xHCI USB controller */ + +/** Construct PCI class + * + * @v base Base class (or PCI_ANY_ID) + * @v sub Subclass (or PCI_ANY_ID) + * @v progif Programming interface (or PCI_ANY_ID) + */ +#define PCI_CLASS( base, sub, progif ) \ + ( ( ( (base) & 0xff ) << 16 ) | ( ( (sub) & 0xff ) << 8 ) | \ + ( ( (progif) & 0xff) << 0 ) ) /** A PCI device ID list entry */ struct pci_device_id { @@ -279,6 +143,27 @@ struct pci_device_id { /** Match-anything ID */ #define PCI_ANY_ID 0xffff +/** A PCI class ID */ +struct pci_class_id { + /** Class */ + uint32_t class; + /** Class mask */ + uint32_t mask; +}; + +/** Construct PCI class ID + * + * @v base Base class (or PCI_ANY_ID) + * @v sub Subclass (or PCI_ANY_ID) + * @v progif Programming interface (or PCI_ANY_ID) + */ +#define PCI_CLASS_ID( base, sub, progif ) { \ + .class = PCI_CLASS ( base, sub, progif ), \ + .mask = ( ( ( ( (base) == PCI_ANY_ID ) ? 0x00 : 0xff ) << 16 ) | \ + ( ( ( (sub) == PCI_ANY_ID ) ? 0x00 : 0xff ) << 8 ) | \ + ( ( ( (progif) == PCI_ANY_ID ) ? 0x00 : 0xff ) << 0 ) ), \ + } + /** A PCI device */ struct pci_device { /** Generic device */ @@ -322,6 +207,8 @@ struct pci_driver { struct pci_device_id *ids; /** Number of entries in PCI ID table */ unsigned int id_count; + /** PCI class ID */ + struct pci_class_id class; /** * Probe device * @@ -352,6 +239,7 @@ struct pci_driver { #define PCI_BUSDEVFN( bus, slot, func ) \ ( ( (bus) << 8 ) | ( (slot) << 3 ) | ( (func) << 0 ) ) #define PCI_FIRST_FUNC( busdevfn ) ( (busdevfn) & ~0x07 ) +#define PCI_LAST_FUNC( busdevfn ) ( (busdevfn) | 0x07 ) #define PCI_BASE_CLASS( class ) ( (class) >> 16 ) #define PCI_SUB_CLASS( class ) ( ( (class) >> 8 ) & 0xff ) diff --git a/qemu/roms/ipxe/src/include/ipxe/pci_ids.h b/qemu/roms/ipxe/src/include/ipxe/pci_ids.h deleted file mode 100644 index 25c7782bc..000000000 --- a/qemu/roms/ipxe/src/include/ipxe/pci_ids.h +++ /dev/null @@ -1,351 +0,0 @@ -#ifndef _IPXE_PCI_IDS_H -#define _IPXE_PCI_IDS_H - -/* - * PCI Class, Vendor and Device IDs - * - * Please keep sorted. - */ - -FILE_LICENCE ( GPL2_ONLY ); - -/* Device classes and subclasses */ - -#define PCI_CLASS_NOT_DEFINED 0x0000 -#define PCI_CLASS_NOT_DEFINED_VGA 0x0001 - -#define PCI_BASE_CLASS_STORAGE 0x01 -#define PCI_CLASS_STORAGE_SCSI 0x0100 -#define PCI_CLASS_STORAGE_IDE 0x0101 -#define PCI_CLASS_STORAGE_FLOPPY 0x0102 -#define PCI_CLASS_STORAGE_IPI 0x0103 -#define PCI_CLASS_STORAGE_RAID 0x0104 -#define PCI_CLASS_STORAGE_OTHER 0x0180 - -#define PCI_BASE_CLASS_NETWORK 0x02 -#define PCI_CLASS_NETWORK_ETHERNET 0x0200 -#define PCI_CLASS_NETWORK_TOKEN_RING 0x0201 -#define PCI_CLASS_NETWORK_FDDI 0x0202 -#define PCI_CLASS_NETWORK_ATM 0x0203 -#define PCI_CLASS_NETWORK_OTHER 0x0280 - -#define PCI_BASE_CLASS_DISPLAY 0x03 -#define PCI_CLASS_DISPLAY_VGA 0x0300 -#define PCI_CLASS_DISPLAY_XGA 0x0301 -#define PCI_CLASS_DISPLAY_3D 0x0302 -#define PCI_CLASS_DISPLAY_OTHER 0x0380 - -#define PCI_BASE_CLASS_MULTIMEDIA 0x04 -#define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400 -#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401 -#define PCI_CLASS_MULTIMEDIA_PHONE 0x0402 -#define PCI_CLASS_MULTIMEDIA_OTHER 0x0480 - -#define PCI_BASE_CLASS_MEMORY 0x05 -#define PCI_CLASS_MEMORY_RAM 0x0500 -#define PCI_CLASS_MEMORY_FLASH 0x0501 -#define PCI_CLASS_MEMORY_OTHER 0x0580 - -#define PCI_BASE_CLASS_BRIDGE 0x06 -#define PCI_CLASS_BRIDGE_HOST 0x0600 -#define PCI_CLASS_BRIDGE_ISA 0x0601 -#define PCI_CLASS_BRIDGE_EISA 0x0602 -#define PCI_CLASS_BRIDGE_MC 0x0603 -#define PCI_CLASS_BRIDGE_PCI 0x0604 -#define PCI_CLASS_BRIDGE_PCMCIA 0x0605 -#define PCI_CLASS_BRIDGE_NUBUS 0x0606 -#define PCI_CLASS_BRIDGE_CARDBUS 0x0607 -#define PCI_CLASS_BRIDGE_RACEWAY 0x0608 -#define PCI_CLASS_BRIDGE_OTHER 0x0680 - -#define PCI_BASE_CLASS_COMMUNICATION 0x07 -#define PCI_CLASS_COMMUNICATION_SERIAL 0x0700 -#define PCI_CLASS_COMMUNICATION_PARALLEL 0x0701 -#define PCI_CLASS_COMMUNICATION_MULTISERIAL 0x0702 -#define PCI_CLASS_COMMUNICATION_MODEM 0x0703 -#define PCI_CLASS_COMMUNICATION_OTHER 0x0780 - -#define PCI_BASE_CLASS_SYSTEM 0x08 -#define PCI_CLASS_SYSTEM_PIC 0x0800 -#define PCI_CLASS_SYSTEM_DMA 0x0801 -#define PCI_CLASS_SYSTEM_TIMER 0x0802 -#define PCI_CLASS_SYSTEM_RTC 0x0803 -#define PCI_CLASS_SYSTEM_PCI_HOTPLUG 0x0804 -#define PCI_CLASS_SYSTEM_OTHER 0x0880 - -#define PCI_BASE_CLASS_INPUT 0x09 -#define PCI_CLASS_INPUT_KEYBOARD 0x0900 -#define PCI_CLASS_INPUT_PEN 0x0901 -#define PCI_CLASS_INPUT_MOUSE 0x0902 -#define PCI_CLASS_INPUT_SCANNER 0x0903 -#define PCI_CLASS_INPUT_GAMEPORT 0x0904 -#define PCI_CLASS_INPUT_OTHER 0x0980 - -#define PCI_BASE_CLASS_DOCKING 0x0a -#define PCI_CLASS_DOCKING_GENERIC 0x0a00 -#define PCI_CLASS_DOCKING_OTHER 0x0a80 - -#define PCI_BASE_CLASS_PROCESSOR 0x0b -#define PCI_CLASS_PROCESSOR_386 0x0b00 -#define PCI_CLASS_PROCESSOR_486 0x0b01 -#define PCI_CLASS_PROCESSOR_PENTIUM 0x0b02 -#define PCI_CLASS_PROCESSOR_ALPHA 0x0b10 -#define PCI_CLASS_PROCESSOR_POWERPC 0x0b20 -#define PCI_CLASS_PROCESSOR_MIPS 0x0b30 -#define PCI_CLASS_PROCESSOR_CO 0x0b40 - -#define PCI_BASE_CLASS_SERIAL 0x0c -#define PCI_CLASS_SERIAL_FIREWIRE 0x0c00 -#define PCI_CLASS_SERIAL_ACCESS 0x0c01 -#define PCI_CLASS_SERIAL_SSA 0x0c02 -#define PCI_CLASS_SERIAL_USB 0x0c03 -#define PCI_CLASS_SERIAL_FIBER 0x0c04 -#define PCI_CLASS_SERIAL_SMBUS 0x0c05 - -#define PCI_BASE_CLASS_INTELLIGENT 0x0e -#define PCI_CLASS_INTELLIGENT_I2O 0x0e00 - -#define PCI_BASE_CLASS_SATELLITE 0x0f -#define PCI_CLASS_SATELLITE_TV 0x0f00 -#define PCI_CLASS_SATELLITE_AUDIO 0x0f01 -#define PCI_CLASS_SATELLITE_VOICE 0x0f03 -#define PCI_CLASS_SATELLITE_DATA 0x0f04 - -#define PCI_BASE_CLASS_CRYPT 0x10 -#define PCI_CLASS_CRYPT_NETWORK 0x1000 -#define PCI_CLASS_CRYPT_ENTERTAINMENT 0x1001 -#define PCI_CLASS_CRYPT_OTHER 0x1080 - -#define PCI_BASE_CLASS_SIGNAL_PROCESSING 0x11 -#define PCI_CLASS_SP_DPIO 0x1100 -#define PCI_CLASS_SP_OTHER 0x1180 - -#define PCI_CLASS_OTHERS 0xff - -/* Vendors */ - -#define PCI_VENDOR_ID_DYNALINK 0x0675 -#define PCI_VENDOR_ID_BERKOM 0x0871 -#define PCI_VENDOR_ID_COMPAQ 0x0e11 -#define PCI_VENDOR_ID_NCR 0x1000 -#define PCI_VENDOR_ID_LSI_LOGIC 0x1000 -#define PCI_VENDOR_ID_ATI 0x1002 -#define PCI_VENDOR_ID_VLSI 0x1004 -#define PCI_VENDOR_ID_ADL 0x1005 -#define PCI_VENDOR_ID_NS 0x100b -#define PCI_VENDOR_ID_TSENG 0x100c -#define PCI_VENDOR_ID_WEITEK 0x100e -#define PCI_VENDOR_ID_DEC 0x1011 -#define PCI_VENDOR_ID_CIRRUS 0x1013 -#define PCI_VENDOR_ID_IBM 0x1014 -#define PCI_VENDOR_ID_COMPEX2 0x101a -/* pci.ids says "AT&T GIS (NCR)" */ -#define PCI_VENDOR_ID_WD 0x101c -#define PCI_VENDOR_ID_AMI 0x101e -#define PCI_VENDOR_ID_AMD 0x1022 -#define PCI_VENDOR_ID_TRIDENT 0x1023 -#define PCI_VENDOR_ID_AI 0x1025 -#define PCI_VENDOR_ID_DELL 0x1028 -#define PCI_VENDOR_ID_MATROX 0x102B -#define PCI_VENDOR_ID_CT 0x102c -#define PCI_VENDOR_ID_MIRO 0x1031 -#define PCI_VENDOR_ID_NEC 0x1033 -#define PCI_VENDOR_ID_FD 0x1036 -#define PCI_VENDOR_ID_SIS 0x1039 -#define PCI_VENDOR_ID_SI 0x1039 -#define PCI_VENDOR_ID_HP 0x103c -#define PCI_VENDOR_ID_PCTECH 0x1042 -#define PCI_VENDOR_ID_ASUSTEK 0x1043 -#define PCI_VENDOR_ID_DPT 0x1044 -#define PCI_VENDOR_ID_OPTI 0x1045 -#define PCI_VENDOR_ID_ELSA 0x1048 -#define PCI_VENDOR_ID_ELSA 0x1048 -#define PCI_VENDOR_ID_SGS 0x104a -#define PCI_VENDOR_ID_BUSLOGIC 0x104B -#define PCI_VENDOR_ID_TI 0x104c -#define PCI_VENDOR_ID_SONY 0x104d -#define PCI_VENDOR_ID_OAK 0x104e -/* Winbond have two vendor IDs! See 0x10ad as well */ -#define PCI_VENDOR_ID_WINBOND2 0x1050 -#define PCI_VENDOR_ID_ANIGMA 0x1051 -#define PCI_VENDOR_ID_EFAR 0x1055 -#define PCI_VENDOR_ID_MOTOROLA 0x1057 -#define PCI_VENDOR_ID_MOTOROLA_OOPS 0x1507 -#define PCI_VENDOR_ID_PROMISE 0x105a -#define PCI_VENDOR_ID_N9 0x105d -#define PCI_VENDOR_ID_UMC 0x1060 -#define PCI_VENDOR_ID_X 0x1061 -#define PCI_VENDOR_ID_MYLEX 0x1069 -#define PCI_VENDOR_ID_PICOP 0x1066 -#define PCI_VENDOR_ID_APPLE 0x106b -#define PCI_VENDOR_ID_YAMAHA 0x1073 -#define PCI_VENDOR_ID_NEXGEN 0x1074 -#define PCI_VENDOR_ID_QLOGIC 0x1077 -#define PCI_VENDOR_ID_CYRIX 0x1078 -#define PCI_VENDOR_ID_LEADTEK 0x107d -#define PCI_VENDOR_ID_INTERPHASE 0x107e -#define PCI_VENDOR_ID_CONTAQ 0x1080 -#define PCI_VENDOR_ID_FOREX 0x1083 -#define PCI_VENDOR_ID_OLICOM 0x108d -#define PCI_VENDOR_ID_SUN 0x108e -#define PCI_VENDOR_ID_CMD 0x1095 -#define PCI_VENDOR_ID_VISION 0x1098 -#define PCI_VENDOR_ID_BROOKTREE 0x109e -#define PCI_VENDOR_ID_SIERRA 0x10a8 -#define PCI_VENDOR_ID_SGI 0x10a9 -#define PCI_VENDOR_ID_ACC 0x10aa -#define PCI_VENDOR_ID_WINBOND 0x10ad -#define PCI_VENDOR_ID_DATABOOK 0x10b3 -#define PCI_VENDOR_ID_PLX 0x10b5 -#define PCI_VENDOR_ID_MADGE 0x10b6 -#define PCI_VENDOR_ID_3COM 0x10b7 -#define PCI_VENDOR_ID_SMC 0x10b8 -#define PCI_VENDOR_ID_SUNDANCE 0x13F0 -#define PCI_VENDOR_ID_AL 0x10b9 -#define PCI_VENDOR_ID_MITSUBISHI 0x10ba -#define PCI_VENDOR_ID_SURECOM 0x10bd -#define PCI_VENDOR_ID_NEOMAGIC 0x10c8 -#define PCI_VENDOR_ID_ASP 0x10cd -#define PCI_VENDOR_ID_MACRONIX 0x10d9 -#define PCI_VENDOR_ID_TCONRAD 0x10da -#define PCI_VENDOR_ID_CERN 0x10dc -#define PCI_VENDOR_ID_NVIDIA 0x10de -#define PCI_VENDOR_ID_IMS 0x10e0 -#define PCI_VENDOR_ID_TEKRAM2 0x10e1 -#define PCI_VENDOR_ID_TUNDRA 0x10e3 -#define PCI_VENDOR_ID_AMCC 0x10e8 -#define PCI_VENDOR_ID_INTERG 0x10ea -#define PCI_VENDOR_ID_REALTEK 0x10ec -#define PCI_VENDOR_ID_XILINX 0x10ee -#define PCI_VENDOR_ID_TRUEVISION 0x10fa -#define PCI_VENDOR_ID_INIT 0x1101 -#define PCI_VENDOR_ID_CREATIVE 0x1102 -/* duplicate: ECTIVA */ -#define PCI_VENDOR_ID_ECTIVA 0x1102 -/* duplicate: CREATIVE */ -#define PCI_VENDOR_ID_TTI 0x1103 -#define PCI_VENDOR_ID_VIA 0x1106 -#define PCI_VENDOR_ID_VIATEC 0x1106 -#define PCI_VENDOR_ID_SIEMENS 0x110A -#define PCI_VENDOR_ID_SMC2 0x1113 -#define PCI_VENDOR_ID_VORTEX 0x1119 -#define PCI_VENDOR_ID_EF 0x111a -#define PCI_VENDOR_ID_IDT 0x111d -#define PCI_VENDOR_ID_FORE 0x1127 -#define PCI_VENDOR_ID_IMAGINGTECH 0x112f -#define PCI_VENDOR_ID_PHILIPS 0x1131 -#define PCI_VENDOR_ID_EICON 0x1133 -#define PCI_VENDOR_ID_CYCLONE 0x113c -#define PCI_VENDOR_ID_ALLIANCE 0x1142 -#define PCI_VENDOR_ID_SYSKONNECT 0x1148 -#define PCI_VENDOR_ID_VMIC 0x114a -#define PCI_VENDOR_ID_DIGI 0x114f -#define PCI_VENDOR_ID_MUTECH 0x1159 -#define PCI_VENDOR_ID_XIRCOM 0x115d -#define PCI_VENDOR_ID_RENDITION 0x1163 -#define PCI_VENDOR_ID_SERVERWORKS 0x1166 -#define PCI_VENDOR_ID_SBE 0x1176 -#define PCI_VENDOR_ID_TOSHIBA 0x1179 -#define PCI_VENDOR_ID_RICOH 0x1180 -#define PCI_VENDOR_ID_DLINK 0x1186 -#define PCI_VENDOR_ID_ARTOP 0x1191 -#define PCI_VENDOR_ID_ZEITNET 0x1193 -#define PCI_VENDOR_ID_OMEGA 0x119b -#define PCI_VENDOR_ID_FUJITSU_ME 0x119e -#define PCI_SUBVENDOR_ID_KEYSPAN 0x11a9 -#define PCI_VENDOR_ID_GALILEO 0x11ab -#define PCI_VENDOR_ID_LINKSYS 0x11ad -#define PCI_VENDOR_ID_LITEON 0x11ad -#define PCI_VENDOR_ID_V3 0x11b0 -#define PCI_VENDOR_ID_NP 0x11bc -#define PCI_VENDOR_ID_ATT 0x11c1 -#define PCI_VENDOR_ID_SPECIALIX 0x11cb -#define PCI_VENDOR_ID_AURAVISION 0x11d1 -#define PCI_VENDOR_ID_ANALOG_DEVICES 0x11d4 -#define PCI_VENDOR_ID_IKON 0x11d5 -#define PCI_VENDOR_ID_ZORAN 0x11de -#define PCI_VENDOR_ID_KINETIC 0x11f4 -#define PCI_VENDOR_ID_COMPEX 0x11f6 -#define PCI_VENDOR_ID_RP 0x11fe -#define PCI_VENDOR_ID_CYCLADES 0x120e -#define PCI_VENDOR_ID_ESSENTIAL 0x120f -#define PCI_VENDOR_ID_O2 0x1217 -#define PCI_VENDOR_ID_3DFX 0x121a -#define PCI_VENDOR_ID_SIGMADES 0x1236 -#define PCI_VENDOR_ID_CCUBE 0x123f -#define PCI_VENDOR_ID_AVM 0x1244 -#define PCI_VENDOR_ID_DIPIX 0x1246 -#define PCI_VENDOR_ID_STALLION 0x124d -#define PCI_VENDOR_ID_OPTIBASE 0x1255 -#define PCI_VENDOR_ID_ESS 0x125d -#define PCI_VENDOR_ID_HARRIS 0x1260 -#define PCI_VENDOR_ID_SATSAGEM 0x1267 -#define PCI_VENDOR_ID_HUGHES 0x1273 -#define PCI_VENDOR_ID_ENSONIQ 0x1274 -#define PCI_VENDOR_ID_ROCKWELL 0x127A -#define PCI_VENDOR_ID_DAVICOM 0x1282 -#define PCI_VENDOR_ID_ITE 0x1283 -/* formerly Platform Tech */ -#define PCI_VENDOR_ID_ESS_OLD 0x1285 -#define PCI_VENDOR_ID_ALTEON 0x12ae -#define PCI_VENDOR_ID_USR 0x12B9 -#define PCI_VENDOR_ID_HOLTEK 0x12c3 -#define PCI_SUBVENDOR_ID_CONNECT_TECH 0x12c4 -#define PCI_VENDOR_ID_PICTUREL 0x12c5 -#define PCI_VENDOR_ID_NVIDIA_SGS 0x12d2 -#define PCI_SUBVENDOR_ID_CHASE_PCIFAST 0x12E0 -#define PCI_SUBVENDOR_ID_CHASE_PCIRAS 0x124D -#define PCI_VENDOR_ID_AUREAL 0x12eb -#define PCI_VENDOR_ID_CBOARDS 0x1307 -#define PCI_VENDOR_ID_SIIG 0x131f -#define PCI_VENDOR_ID_ADMTEK 0x1317 -#define PCI_VENDOR_ID_DOMEX 0x134a -#define PCI_VENDOR_ID_QUATECH 0x135C -#define PCI_VENDOR_ID_SEALEVEL 0x135e -#define PCI_VENDOR_ID_HYPERCOPE 0x1365 -#define PCI_VENDOR_ID_KAWASAKI 0x136b -#define PCI_VENDOR_ID_LMC 0x1376 -#define PCI_VENDOR_ID_NETGEAR 0x1385 -#define PCI_VENDOR_ID_APPLICOM 0x1389 -#define PCI_VENDOR_ID_MOXA 0x1393 -#define PCI_VENDOR_ID_CCD 0x1397 -#define PCI_VENDOR_ID_MICROGATE 0x13c0 -#define PCI_VENDOR_ID_3WARE 0x13C1 -#define PCI_VENDOR_ID_ABOCOM 0x13D1 -#define PCI_VENDOR_ID_CMEDIA 0x13f6 -#define PCI_VENDOR_ID_LAVA 0x1407 -#define PCI_VENDOR_ID_TIMEDIA 0x1409 -#define PCI_VENDOR_ID_OXSEMI 0x1415 -#define PCI_VENDOR_ID_AIRONET 0x14b9 -#define PCI_VENDOR_ID_MYRICOM 0x14c1 -#define PCI_VENDOR_ID_TITAN 0x14D2 -#define PCI_VENDOR_ID_PANACOM 0x14d4 -#define PCI_VENDOR_ID_BROADCOM 0x14e4 -#define PCI_VENDOR_ID_SYBA 0x1592 -#define PCI_VENDOR_ID_MORETON 0x15aa -#define PCI_VENDOR_ID_ZOLTRIX 0x15b0 -#define PCI_VENDOR_ID_PDC 0x15e9 -#define PCI_VENDOR_ID_FSC 0x1734 -#define PCI_VENDOR_ID_SYMPHONY 0x1c1c -#define PCI_VENDOR_ID_TEKRAM 0x1de1 -#define PCI_VENDOR_ID_3DLABS 0x3d3d -#define PCI_VENDOR_ID_AVANCE 0x4005 -#define PCI_VENDOR_ID_AKS 0x416c -#define PCI_VENDOR_ID_NETVIN 0x4a14 -#define PCI_VENDOR_ID_S3 0x5333 -#define PCI_VENDOR_ID_DCI 0x6666 -#define PCI_VENDOR_ID_GENROCO 0x5555 -#define PCI_VENDOR_ID_INTEL 0x8086 -#define PCI_VENDOR_ID_COMPUTONE 0x8e0e -#define PCI_SUBVENDOR_ID_COMPUTONE 0x8e0e -#define PCI_VENDOR_ID_KTI 0x8e2e -#define PCI_VENDOR_ID_ADAPTEC 0x9004 -#define PCI_VENDOR_ID_ADAPTEC2 0x9005 -#define PCI_VENDOR_ID_ATRONICS 0x907f -#define PCI_VENDOR_ID_HOLTEK2 0x9412 -#define PCI_VENDOR_ID_NETMOS 0x9710 -#define PCI_SUBVENDOR_ID_EXSYS 0xd84d -#define PCI_VENDOR_ID_TIGERJET 0xe159 -#define PCI_VENDOR_ID_ARK 0xedd8 - -#endif /* _IPXE_PCI_IDS_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/pci_io.h b/qemu/roms/ipxe/src/include/ipxe/pci_io.h index 781b77fe1..10e69763e 100644 --- a/qemu/roms/ipxe/src/include/ipxe/pci_io.h +++ b/qemu/roms/ipxe/src/include/ipxe/pci_io.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/pcibackup.h b/qemu/roms/ipxe/src/include/ipxe/pcibackup.h index b9f55cf71..159d25392 100644 --- a/qemu/roms/ipxe/src/include/ipxe/pcibackup.h +++ b/qemu/roms/ipxe/src/include/ipxe/pcibackup.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/pcivpd.h b/qemu/roms/ipxe/src/include/ipxe/pcivpd.h index 0abf8a956..fefb69740 100644 --- a/qemu/roms/ipxe/src/include/ipxe/pcivpd.h +++ b/qemu/roms/ipxe/src/include/ipxe/pcivpd.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/peerblk.h b/qemu/roms/ipxe/src/include/ipxe/peerblk.h new file mode 100644 index 000000000..6fc9172f6 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/peerblk.h @@ -0,0 +1,144 @@ +#ifndef _IPXE_PEERBLK_H +#define _IPXE_PEERBLK_H + +/** @file + * + * Peer Content Caching and Retrieval (PeerDist) protocol block downloads + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** A PeerDist retrieval protocol decryption buffer descriptor */ +struct peerdist_block_decrypt { + /** Data transfer buffer */ + struct xfer_buffer *xferbuf; + /** Offset within data transfer buffer */ + size_t offset; + /** Length to use from data transfer buffer */ + size_t len; +}; + +/** PeerDist retrieval protocol decryption data transfer buffer indices */ +enum peerdist_block_decrypt_index { + /** Data before the trimmed content */ + PEERBLK_BEFORE = 0, + /** Data within the trimmed content */ + PEERBLK_DURING, + /** Data after the trimmed content */ + PEERBLK_AFTER, + /** Number of decryption buffers */ + PEERBLK_NUM_BUFFERS +}; + +/** A PeerDist block download */ +struct peerdist_block { + /** Reference count */ + struct refcnt refcnt; + /** Data transfer interface */ + struct interface xfer; + /** Raw data interface */ + struct interface raw; + /** Retrieval protocol interface */ + struct interface retrieval; + + /** Original URI */ + struct uri *uri; + /** Content range of this block */ + struct peerdist_range range; + /** Trimmed range of this block */ + struct peerdist_range trim; + /** Offset of first byte in trimmed range within overall download */ + size_t offset; + + /** Digest algorithm */ + struct digest_algorithm *digest; + /** Digest size + * + * Note that this may be shorter than the digest size of the + * digest algorithm. + */ + size_t digestsize; + /** Digest context (statically allocated at instantiation time) */ + void *digestctx; + + /** Cipher algorithm */ + struct cipher_algorithm *cipher; + /** Cipher context (dynamically allocated as needed) */ + void *cipherctx; + + /** Segment index */ + unsigned int segment; + /** Segment identifier */ + uint8_t id[PEERDIST_DIGEST_MAX_SIZE]; + /** Segment secret */ + uint8_t secret[PEERDIST_DIGEST_MAX_SIZE]; + /** Block index */ + unsigned int block; + /** Block hash */ + uint8_t hash[PEERDIST_DIGEST_MAX_SIZE]; + + /** Current position (relative to incoming data stream) */ + size_t pos; + /** Start of trimmed content (relative to incoming data stream) */ + size_t start; + /** End of trimmed content (relative to incoming data stream) */ + size_t end; + /** Data buffer */ + struct xfer_buffer buffer; + + /** Decryption process */ + struct process process; + /** Decryption data buffer descriptors */ + struct peerdist_block_decrypt decrypt[PEERBLK_NUM_BUFFERS]; + /** Remaining decryption length */ + size_t cipher_remaining; + /** Remaining digest length (excluding AES padding bytes) */ + size_t digest_remaining; + + /** Discovery client */ + struct peerdisc_client discovery; + /** Current position in discovered peer list */ + struct peerdisc_peer *peer; + /** Retry timer */ + struct retry_timer timer; + /** Number of full attempt cycles completed */ + unsigned int cycles; + /** Most recent attempt failure */ + int rc; + + /** Time at which block download was started */ + unsigned long started; + /** Time at which most recent attempt was started */ + unsigned long attempted; +}; + +/** Retrieval protocol block fetch response (including transport header) + * + * @v digestsize Digest size + * @v len Data block length + * @v vrf_len Length of uselessness + * @v blksize Cipher block size + */ +#define peerblk_msg_blk_t( digestsize, len, vrf_len, blksize ) \ + struct { \ + struct peerdist_msg_transport_header hdr; \ + peerdist_msg_blk_t ( digestsize, len, vrf_len, \ + blksize ) msg; \ + } __attribute__ (( packed )) + +extern int peerblk_open ( struct interface *xfer, struct uri *uri, + struct peerdist_info_block *block ); + +#endif /* _IPXE_PEERBLK_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/peerdisc.h b/qemu/roms/ipxe/src/include/ipxe/peerdisc.h new file mode 100644 index 000000000..f08ccaae2 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/peerdisc.h @@ -0,0 +1,116 @@ +#ifndef _IPXE_PEERDISC_H +#define _IPXE_PEERDISC_H + +/** @file + * + * Peer Content Caching and Retrieval (PeerDist) protocol peer discovery + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include + +/** A PeerDist discovery socket */ +struct peerdisc_socket { + /** Name */ + const char *name; + /** Data transfer interface */ + struct interface xfer; + /** Socket address */ + union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } address; +}; + +/** PeerDist discovery socket table */ +#define PEERDISC_SOCKETS __table ( struct peerdisc_socket, "peerdisc_sockets" ) + +/** Declare a PeerDist discovery socket */ +#define __peerdisc_socket __table_entry ( PEERDISC_SOCKETS, 01 ) + +/** A PeerDist discovery segment */ +struct peerdisc_segment { + /** Reference count */ + struct refcnt refcnt; + /** List of segments */ + struct list_head list; + /** Segment identifier string + * + * This is MS-PCCRC's "HoHoDk", transcribed as an upper-case + * Base16-encoded string. + */ + const char *id; + /** Message UUID string */ + const char *uuid; + /** List of discovered peers + * + * The list of peers may be appended to during the lifetime of + * the discovery segment. Discovered peers will not be + * removed from the list until the last discovery has been + * closed; this allows users to safely maintain a pointer to a + * current position within the list. + */ + struct list_head peers; + /** List of active clients */ + struct list_head clients; + /** Transmission timer */ + struct retry_timer timer; +}; + +/** A PeerDist discovery peer */ +struct peerdisc_peer { + /** List of peers */ + struct list_head list; + /** Peer location */ + char location[0]; +}; + +/** A PeerDist discovery client */ +struct peerdisc_client { + /** Discovery segment */ + struct peerdisc_segment *segment; + /** List of clients */ + struct list_head list; + /** Operations */ + struct peerdisc_client_operations *op; +}; + +/** PeerDist discovery client operations */ +struct peerdisc_client_operations { + /** New peers have been discovered + * + * @v peerdisc PeerDist discovery client + */ + void ( * discovered ) ( struct peerdisc_client *peerdisc ); +}; + +/** + * Initialise PeerDist discovery + * + * @v peerdisc PeerDist discovery client + * @v op Discovery operations + */ +static inline __attribute__ (( always_inline )) void +peerdisc_init ( struct peerdisc_client *peerdisc, + struct peerdisc_client_operations *op ) { + + peerdisc->op = op; +} + +extern unsigned int peerdisc_timeout_secs; + +extern int peerdisc_open ( struct peerdisc_client *peerdisc, const void *id, + size_t len ); +extern void peerdisc_close ( struct peerdisc_client *peerdisc ); + +#endif /* _IPXE_PEERDISC_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/peermux.h b/qemu/roms/ipxe/src/include/ipxe/peermux.h new file mode 100644 index 000000000..44cbdb9d6 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/peermux.h @@ -0,0 +1,73 @@ +#ifndef _IPXE_PEERMUX_H +#define _IPXE_PEERMUX_H + +/** @file + * + * Peer Content Caching and Retrieval (PeerDist) protocol multiplexer + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include + +/** Maximum number of concurrent block downloads */ +#define PEERMUX_MAX_BLOCKS 32 + +/** PeerDist download content information cache */ +struct peerdist_info_cache { + /** Content information */ + struct peerdist_info info; + /** Content information segment */ + struct peerdist_info_segment segment; + /** Content information block */ + struct peerdist_info_block block; +}; + +/** A PeerDist multiplexed block download */ +struct peerdist_multiplexed_block { + /** PeerDist download multiplexer */ + struct peerdist_multiplexer *peermux; + /** List of multiplexed blocks */ + struct list_head list; + /** Data transfer interface */ + struct interface xfer; +}; + +/** A PeerDist download multiplexer */ +struct peerdist_multiplexer { + /** Reference count */ + struct refcnt refcnt; + /** Data transfer interface */ + struct interface xfer; + /** Content information interface */ + struct interface info; + /** Original URI */ + struct uri *uri; + + /** Content information data transfer buffer */ + struct xfer_buffer buffer; + /** Content information cache */ + struct peerdist_info_cache cache; + + /** Block download initiation process */ + struct process process; + /** List of busy block downloads */ + struct list_head busy; + /** List of idle block downloads */ + struct list_head idle; + /** Block downloads */ + struct peerdist_multiplexed_block block[PEERMUX_MAX_BLOCKS]; +}; + +extern int peermux_filter ( struct interface *xfer, struct interface *info, + struct uri *uri ); + +#endif /* _IPXE_PEERMUX_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/pending.h b/qemu/roms/ipxe/src/include/ipxe/pending.h index e6a369813..be6ed05a1 100644 --- a/qemu/roms/ipxe/src/include/ipxe/pending.h +++ b/qemu/roms/ipxe/src/include/ipxe/pending.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** A pending operation */ struct pending_operation { diff --git a/qemu/roms/ipxe/src/include/ipxe/ping.h b/qemu/roms/ipxe/src/include/ipxe/ping.h index 6cd376b6f..c55bd1ab2 100644 --- a/qemu/roms/ipxe/src/include/ipxe/ping.h +++ b/qemu/roms/ipxe/src/include/ipxe/ping.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/pinger.h b/qemu/roms/ipxe/src/include/ipxe/pinger.h index 9932df6b0..227f002dc 100644 --- a/qemu/roms/ipxe/src/include/ipxe/pinger.h +++ b/qemu/roms/ipxe/src/include/ipxe/pinger.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/pixbuf.h b/qemu/roms/ipxe/src/include/ipxe/pixbuf.h index 106b666e6..615744812 100644 --- a/qemu/roms/ipxe/src/include/ipxe/pixbuf.h +++ b/qemu/roms/ipxe/src/include/ipxe/pixbuf.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/png.h b/qemu/roms/ipxe/src/include/ipxe/png.h index f51d1e6fe..3505eefc8 100644 --- a/qemu/roms/ipxe/src/include/ipxe/png.h +++ b/qemu/roms/ipxe/src/include/ipxe/png.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/pnm.h b/qemu/roms/ipxe/src/include/ipxe/pnm.h index 536c14d5f..860968cbc 100644 --- a/qemu/roms/ipxe/src/include/ipxe/pnm.h +++ b/qemu/roms/ipxe/src/include/ipxe/pnm.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/pool.h b/qemu/roms/ipxe/src/include/ipxe/pool.h new file mode 100644 index 000000000..27066e9b3 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/pool.h @@ -0,0 +1,127 @@ +#ifndef _IPXE_POOL_H +#define _IPXE_POOL_H + +/** @file + * + * Pooled connections + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** A pooled connection */ +struct pooled_connection { + /** List of pooled connections + * + * Note that each connecton in the pool has a running expiry + * timer which holds a reference to the connection. We + * therefore do not require the connection pool list to hold a + * reference for each pooled connection. + */ + struct list_head list; + /** Expiry timer */ + struct retry_timer timer; + /** Close expired pooled connection + * + * @v pool Pooled connection + */ + void ( * expired ) ( struct pooled_connection *pool ); + /** Flags */ + unsigned int flags; +}; + +/** Pooled connection flags */ +enum pooled_connection_flags { + /** Connection should be recycled after closing */ + POOL_RECYCLABLE = 0x0001, + /** Connection has been recycled */ + POOL_RECYCLED = 0x0002, + /** Connection is known to be alive */ + POOL_ALIVE = 0x0004, +}; + +extern void pool_add ( struct pooled_connection *pool, struct list_head *list, + unsigned long expiry ); +extern void pool_del ( struct pooled_connection *pool ); +extern void pool_expired ( struct retry_timer *timer, int over ); + +/** + * Initialise a pooled connection + * + * @v pool Pooled connection + * @v expired Close expired pooled connection method + * @v refcnt Containing object reference counter + */ +static inline __attribute__ (( always_inline )) void +pool_init ( struct pooled_connection *pool, + void ( * expired ) ( struct pooled_connection *pool ), + struct refcnt *refcnt ) { + + INIT_LIST_HEAD ( &pool->list ); + timer_init ( &pool->timer, pool_expired, refcnt ); + pool->expired = expired; +} + +/** + * Mark pooled connection as recyclable + * + * @v pool Pooled connection + */ +static inline __attribute__ (( always_inline )) void +pool_recyclable ( struct pooled_connection *pool ) { + + pool->flags |= POOL_RECYCLABLE; +} + +/** + * Mark pooled connection as alive + * + * @v pool Pooled connection + */ +static inline __attribute__ (( always_inline )) void +pool_alive ( struct pooled_connection *pool ) { + + pool->flags |= POOL_ALIVE; +} + +/** + * Check if pooled connection is recyclable + * + * @v pool Pooled connection + * @ret recyclable Pooled connection is recyclable + */ +static inline __attribute__ (( always_inline )) int +pool_is_recyclable ( struct pooled_connection *pool ) { + + return ( pool->flags & POOL_RECYCLABLE ); +} + +/** + * Check if pooled connection is reopenable + * + * @v pool Pooled connection + * @ret reopenable Pooled connection is reopenable + */ +static inline __attribute__ (( always_inline )) int +pool_is_reopenable ( struct pooled_connection *pool ) { + + /* A connection is reopenable if it has been recycled but is + * not yet known to be alive. + */ + return ( ( pool->flags & POOL_RECYCLED ) & + ( ! ( pool->flags & POOL_ALIVE ) ) ); +} + +extern void pool_recycle ( struct interface *intf ); +#define pool_recycle_TYPE( object_type ) \ + typeof ( void ( object_type ) ) + +extern void pool_reopen ( struct interface *intf ); +#define pool_reopen_TYPE( object_type ) \ + typeof ( void ( object_type ) ) + +#endif /* _IPXE_POOL_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/portmap.h b/qemu/roms/ipxe/src/include/ipxe/portmap.h index 9b735bbca..681ca2ec2 100644 --- a/qemu/roms/ipxe/src/include/ipxe/portmap.h +++ b/qemu/roms/ipxe/src/include/ipxe/portmap.h @@ -10,7 +10,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** PORTMAP default port */ #define PORTMAP_PORT 111 diff --git a/qemu/roms/ipxe/src/include/ipxe/posix_io.h b/qemu/roms/ipxe/src/include/ipxe/posix_io.h index 11f3bb5c9..1a73b5e86 100644 --- a/qemu/roms/ipxe/src/include/ipxe/posix_io.h +++ b/qemu/roms/ipxe/src/include/ipxe/posix_io.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/privkey.h b/qemu/roms/ipxe/src/include/ipxe/privkey.h index 39049ac9f..81108b6bf 100644 --- a/qemu/roms/ipxe/src/include/ipxe/privkey.h +++ b/qemu/roms/ipxe/src/include/ipxe/privkey.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/process.h b/qemu/roms/ipxe/src/include/ipxe/process.h index 2c76ff260..d600508e7 100644 --- a/qemu/roms/ipxe/src/include/ipxe/process.h +++ b/qemu/roms/ipxe/src/include/ipxe/process.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/profile.h b/qemu/roms/ipxe/src/include/ipxe/profile.h index 3a745fcfa..b6d2b19e0 100644 --- a/qemu/roms/ipxe/src/include/ipxe/profile.h +++ b/qemu/roms/ipxe/src/include/ipxe/profile.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -186,4 +186,18 @@ profile_exclude ( struct profiler *profiler ) { profile_excluded += profile_elapsed ( profiler ); } +/** + * Record profiling sample in custom units + * + * @v profiler Profiler + * @v sample Profiling sample + */ +static inline __attribute__ (( always_inline )) void +profile_custom ( struct profiler *profiler, unsigned long sample ) { + + /* If profiling is active then update stats */ + if ( PROFILING ) + profile_update ( profiler, sample ); +} + #endif /* _IPXE_PROFILE_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/random_nz.h b/qemu/roms/ipxe/src/include/ipxe/random_nz.h index 6bb80d2ab..4c433fa38 100644 --- a/qemu/roms/ipxe/src/include/ipxe/random_nz.h +++ b/qemu/roms/ipxe/src/include/ipxe/random_nz.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/rarp.h b/qemu/roms/ipxe/src/include/ipxe/rarp.h index f84301a43..9054db21a 100644 --- a/qemu/roms/ipxe/src/include/ipxe/rarp.h +++ b/qemu/roms/ipxe/src/include/ipxe/rarp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/rbg.h b/qemu/roms/ipxe/src/include/ipxe/rbg.h index 9689142f8..758238a65 100644 --- a/qemu/roms/ipxe/src/include/ipxe/rbg.h +++ b/qemu/roms/ipxe/src/include/ipxe/rbg.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/reboot.h b/qemu/roms/ipxe/src/include/ipxe/reboot.h index 97e0d5fb6..33606d9d5 100644 --- a/qemu/roms/ipxe/src/include/ipxe/reboot.h +++ b/qemu/roms/ipxe/src/include/ipxe/reboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/refcnt.h b/qemu/roms/ipxe/src/include/ipxe/refcnt.h index 0e8b8658c..7f489abc9 100644 --- a/qemu/roms/ipxe/src/include/ipxe/refcnt.h +++ b/qemu/roms/ipxe/src/include/ipxe/refcnt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/resolv.h b/qemu/roms/ipxe/src/include/ipxe/resolv.h index d9868a5d7..ff48d35ca 100644 --- a/qemu/roms/ipxe/src/include/ipxe/resolv.h +++ b/qemu/roms/ipxe/src/include/ipxe/resolv.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/retry.h b/qemu/roms/ipxe/src/include/ipxe/retry.h index c514822b2..76d45fbd0 100644 --- a/qemu/roms/ipxe/src/include/ipxe/retry.h +++ b/qemu/roms/ipxe/src/include/ipxe/retry.h @@ -7,14 +7,14 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -/** Default timeout value */ +/** Default minimum timeout value (in ticks) */ #define DEFAULT_MIN_TIMEOUT ( TICKS_PER_SEC / 4 ) -/** Limit after which the timeout will be deemed permanent */ +/** Default maximum timeout value (in ticks) */ #define DEFAULT_MAX_TIMEOUT ( 10 * TICKS_PER_SEC ) /** A retry timer */ @@ -25,16 +25,18 @@ struct retry_timer { unsigned int running; /** Timeout value (in ticks) */ unsigned long timeout; - /** Minimum timeout value (in ticks) + /** Minimum timeout value (in ticks), or zero to use default * - * A value of zero means "use default timeout." + * The timeout will never be reduced below this value. */ - unsigned long min_timeout; - /** Maximum timeout value before failure (in ticks) + unsigned long min; + /** Maximum timeout value (in ticks), or zero to use default * - * A value of zero means "use default timeout." + * The timeout will be deemed permanent (according to the + * failure indicator passed to expired()) when it exceeds this + * value. */ - unsigned long max_timeout; + unsigned long max; /** Start time (in ticks) */ unsigned long start; /** Retry count */ @@ -46,7 +48,7 @@ struct retry_timer { * * The timer will already be stopped when this method is * called. The failure indicator will be True if the retry - * timeout has already exceeded @c MAX_TIMEOUT. + * timeout has already exceeded @c max_timeout. */ void ( * expired ) ( struct retry_timer *timer, int over ); /** Reference counter @@ -109,4 +111,18 @@ timer_running ( struct retry_timer *timer ) { return ( timer->running ); } +/** + * Set minimum and maximum timeouts + * + * @v timer Retry timer + * @v min Minimum timeout (in ticks), or zero to use default + * @v max Maximum timeout (in ticks), or zero to use default + */ +static inline __attribute__ (( always_inline )) void +set_timer_limits ( struct retry_timer *timer, unsigned long min, + unsigned long max ) { + timer->min = min; + timer->max = max; +} + #endif /* _IPXE_RETRY_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/rndis.h b/qemu/roms/ipxe/src/include/ipxe/rndis.h new file mode 100644 index 000000000..bcb6d8e6a --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/rndis.h @@ -0,0 +1,370 @@ +#ifndef _IPXE_RNDIS_H +#define _IPXE_RNDIS_H + +/** @file + * + * Remote Network Driver Interface Specification + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** Maximum time to wait for a transaction to complete + * + * This is a policy decision. + */ +#define RNDIS_MAX_WAIT_MS 1000 + +/** RNDIS message header */ +struct rndis_header { + /** Message type */ + uint32_t type; + /** Message length */ + uint32_t len; +} __attribute__ (( packed )); + +/** RNDIS initialise message */ +#define RNDIS_INITIALISE_MSG 0x00000002UL + +/** RNDIS initialise message */ +struct rndis_initialise_message { + /** Request ID */ + uint32_t id; + /** Major version */ + uint32_t major; + /** Minor version */ + uint32_t minor; + /** Maximum transfer size */ + uint32_t mtu; +} __attribute__ (( packed )); + +/** Request ID used for initialisation + * + * This is a policy decision. + */ +#define RNDIS_INIT_ID 0xe110e110UL + +/** RNDIS major version */ +#define RNDIS_VERSION_MAJOR 1 + +/** RNDIS minor version */ +#define RNDIS_VERSION_MINOR 0 + +/** RNDIS maximum transfer size + * + * This is a policy decision. + */ +#define RNDIS_MTU 2048 + +/** RNDIS initialise completion */ +#define RNDIS_INITIALISE_CMPLT 0x80000002UL + +/** RNDIS initialise completion */ +struct rndis_initialise_completion { + /** Request ID */ + uint32_t id; + /** Status */ + uint32_t status; + /** Major version */ + uint32_t major; + /** Minor version */ + uint32_t minor; + /** Device flags */ + uint32_t flags; + /** Medium */ + uint32_t medium; + /** Maximum packets per transfer */ + uint32_t max_pkts; + /** Maximum transfer size */ + uint32_t mtu; + /** Packet alignment factor */ + uint32_t align; + /** Reserved */ + uint32_t reserved; +} __attribute__ (( packed )); + +/** RNDIS halt message */ +#define RNDIS_HALT_MSG 0x00000003UL + +/** RNDIS halt message */ +struct rndis_halt_message { + /** Request ID */ + uint32_t id; +} __attribute__ (( packed )); + +/** RNDIS query OID message */ +#define RNDIS_QUERY_MSG 0x00000004UL + +/** RNDIS set OID message */ +#define RNDIS_SET_MSG 0x00000005UL + +/** RNDIS query or set OID message */ +struct rndis_oid_message { + /** Request ID */ + uint32_t id; + /** Object ID */ + uint32_t oid; + /** Information buffer length */ + uint32_t len; + /** Information buffer offset */ + uint32_t offset; + /** Reserved */ + uint32_t reserved; +} __attribute__ (( packed )); + +/** RNDIS query OID completion */ +#define RNDIS_QUERY_CMPLT 0x80000004UL + +/** RNDIS query OID completion */ +struct rndis_query_completion { + /** Request ID */ + uint32_t id; + /** Status */ + uint32_t status; + /** Information buffer length */ + uint32_t len; + /** Information buffer offset */ + uint32_t offset; +} __attribute__ (( packed )); + +/** RNDIS set OID completion */ +#define RNDIS_SET_CMPLT 0x80000005UL + +/** RNDIS set OID completion */ +struct rndis_set_completion { + /** Request ID */ + uint32_t id; + /** Status */ + uint32_t status; +} __attribute__ (( packed )); + +/** RNDIS reset message */ +#define RNDIS_RESET_MSG 0x00000006UL + +/** RNDIS reset message */ +struct rndis_reset_message { + /** Reserved */ + uint32_t reserved; +} __attribute__ (( packed )); + +/** RNDIS reset completion */ +#define RNDIS_RESET_CMPLT 0x80000006UL + +/** RNDIS reset completion */ +struct rndis_reset_completion { + /** Status */ + uint32_t status; + /** Addressing reset */ + uint32_t addr; +} __attribute__ (( packed )); + +/** RNDIS indicate status message */ +#define RNDIS_INDICATE_STATUS_MSG 0x00000007UL + +/** RNDIS diagnostic information */ +struct rndis_diagnostic_info { + /** Status */ + uint32_t status; + /** Error offset */ + uint32_t offset; +} __attribute__ (( packed )); + +/** RNDIS indicate status message */ +struct rndis_indicate_status_message { + /** Status */ + uint32_t status; + /** Status buffer length */ + uint32_t len; + /** Status buffer offset */ + uint32_t offset; + /** Diagnostic information (optional) */ + struct rndis_diagnostic_info diag[0]; +} __attribute__ (( packed )); + +/** RNDIS status codes */ +enum rndis_status { + /** Device is connected to a network medium */ + RNDIS_STATUS_MEDIA_CONNECT = 0x4001000bUL, + /** Device is disconnected from the medium */ + RNDIS_STATUS_MEDIA_DISCONNECT = 0x4001000cUL, + /** Unknown start-of-day status code */ + RNDIS_STATUS_WTF_WORLD = 0x40020006UL, +}; + +/** RNDIS keepalive message */ +#define RNDIS_KEEPALIVE_MSG 0x00000008UL + +/** RNDIS keepalive message */ +struct rndis_keepalive_message { + /** Request ID */ + uint32_t id; +} __attribute__ (( packed )); + +/** RNDIS keepalive completion */ +#define RNDIS_KEEPALIVE_CMPLT 0x80000008UL + +/** RNDIS keepalive completion */ +struct rndis_keepalive_completion { + /** Request ID */ + uint32_t id; + /** Status */ + uint32_t status; +} __attribute__ (( packed )); + +/** RNDIS packet message */ +#define RNDIS_PACKET_MSG 0x00000001UL + +/** RNDIS packet field */ +struct rndis_packet_field { + /** Offset */ + uint32_t offset; + /** Length */ + uint32_t len; +} __attribute__ (( packed )); + +/** RNDIS packet message */ +struct rndis_packet_message { + /** Data */ + struct rndis_packet_field data; + /** Out-of-band data records */ + struct rndis_packet_field oob; + /** Number of out-of-band data records */ + uint32_t oob_count; + /** Per-packet information record */ + struct rndis_packet_field ppi; + /** Reserved */ + uint32_t reserved; +} __attribute__ (( packed )); + +/** RNDIS packet record */ +struct rndis_packet_record { + /** Length */ + uint32_t len; + /** Type */ + uint32_t type; + /** Offset */ + uint32_t offset; +} __attribute__ (( packed )); + +/** OID for packet filter */ +#define RNDIS_OID_GEN_CURRENT_PACKET_FILTER 0x0001010eUL + +/** Packet filter bits */ +enum rndis_packet_filter { + /** Unicast packets */ + RNDIS_FILTER_UNICAST = 0x00000001UL, + /** Multicast packets */ + RNDIS_FILTER_MULTICAST = 0x00000002UL, + /** All multicast packets */ + RNDIS_FILTER_ALL_MULTICAST = 0x00000004UL, + /** Broadcast packets */ + RNDIS_FILTER_BROADCAST = 0x00000008UL, + /** All packets */ + RNDIS_FILTER_PROMISCUOUS = 0x00000020UL +}; + +/** OID for media status */ +#define RNDIS_OID_GEN_MEDIA_CONNECT_STATUS 0x00010114UL + +/** OID for permanent MAC address */ +#define RNDIS_OID_802_3_PERMANENT_ADDRESS 0x01010101UL + +/** OID for current MAC address */ +#define RNDIS_OID_802_3_CURRENT_ADDRESS 0x01010102UL + +struct rndis_device; + +/** RNDIS device operations */ +struct rndis_operations { + /** + * Open RNDIS device + * + * @v rndis RNDIS device + * @ret rc Return status code + */ + int ( * open ) ( struct rndis_device *rndis ); + /** + * Close RNDIS device + * + * @v rndis RNDIS device + */ + void ( * close ) ( struct rndis_device *rndis ); + /** + * Transmit packet + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + * @ret rc Return status code + * + * If this method returns success then the RNDIS device must + * eventually report completion via rndis_tx_complete(). + */ + int ( * transmit ) ( struct rndis_device *rndis, + struct io_buffer *iobuf ); + /** + * Poll for completed and received packets + * + * @v rndis RNDIS device + */ + void ( * poll ) ( struct rndis_device *rndis ); +}; + +/** An RNDIS device */ +struct rndis_device { + /** Network device */ + struct net_device *netdev; + /** Device name */ + const char *name; + /** RNDIS operations */ + struct rndis_operations *op; + /** Driver private data */ + void *priv; + + /** Request ID for current blocking request */ + unsigned int wait_id; + /** Return status code for current blocking request */ + int wait_rc; +}; + +/** + * Initialise an RNDIS device + * + * @v rndis RNDIS device + * @v op RNDIS device operations + */ +static inline void rndis_init ( struct rndis_device *rndis, + struct rndis_operations *op ) { + + rndis->op = op; +} + +extern void rndis_tx_complete_err ( struct rndis_device *rndis, + struct io_buffer *iobuf, int rc ); +extern int rndis_tx_defer ( struct rndis_device *rndis, + struct io_buffer *iobuf ); +extern void rndis_rx ( struct rndis_device *rndis, struct io_buffer *iobuf ); +extern void rndis_rx_err ( struct rndis_device *rndis, struct io_buffer *iobuf, + int rc ); + +extern struct rndis_device * alloc_rndis ( size_t priv_len ); +extern int register_rndis ( struct rndis_device *rndis ); +extern void unregister_rndis ( struct rndis_device *rndis ); +extern void free_rndis ( struct rndis_device *rndis ); + +/** + * Complete message transmission + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + */ +static inline void rndis_tx_complete ( struct rndis_device *rndis, + struct io_buffer *iobuf ) { + + rndis_tx_complete_err ( rndis, iobuf, 0 ); +} + +#endif /* _IPXE_RNDIS_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/rootcert.h b/qemu/roms/ipxe/src/include/ipxe/rootcert.h index 6525df87a..d4be2e1bc 100644 --- a/qemu/roms/ipxe/src/include/ipxe/rootcert.h +++ b/qemu/roms/ipxe/src/include/ipxe/rootcert.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/rotate.h b/qemu/roms/ipxe/src/include/ipxe/rotate.h index ba271ca74..b5693e3ca 100644 --- a/qemu/roms/ipxe/src/include/ipxe/rotate.h +++ b/qemu/roms/ipxe/src/include/ipxe/rotate.h @@ -6,10 +6,30 @@ * Bit operations */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +static inline __attribute__ (( always_inline )) uint8_t +rol8 ( uint8_t data, unsigned int rotation ) { + return ( ( data << rotation ) | ( data >> ( 8 - rotation ) ) ); +} + +static inline __attribute__ (( always_inline )) uint8_t +ror8 ( uint8_t data, unsigned int rotation ) { + return ( ( data >> rotation ) | ( data << ( 8 - rotation ) ) ); +} + +static inline __attribute__ (( always_inline )) uint16_t +rol16 ( uint16_t data, unsigned int rotation ) { + return ( ( data << rotation ) | ( data >> ( 16 - rotation ) ) ); +} + +static inline __attribute__ (( always_inline )) uint16_t +ror16 ( uint16_t data, unsigned int rotation ) { + return ( ( data >> rotation ) | ( data << ( 16 - rotation ) ) ); +} + static inline __attribute__ (( always_inline )) uint32_t rol32 ( uint32_t data, unsigned int rotation ) { return ( ( data << rotation ) | ( data >> ( 32 - rotation ) ) ); diff --git a/qemu/roms/ipxe/src/include/ipxe/rsa.h b/qemu/roms/ipxe/src/include/ipxe/rsa.h index 1a5ad8bab..d947eec73 100644 --- a/qemu/roms/ipxe/src/include/ipxe/rsa.h +++ b/qemu/roms/ipxe/src/include/ipxe/rsa.h @@ -6,8 +6,9 @@ * RSA public-key cryptography */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include #include #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/sanboot.h b/qemu/roms/ipxe/src/include/ipxe/sanboot.h index 14c8a5da4..57025f2c6 100644 --- a/qemu/roms/ipxe/src/include/ipxe/sanboot.h +++ b/qemu/roms/ipxe/src/include/ipxe/sanboot.h @@ -16,7 +16,7 @@ * the address parameter. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/script.h b/qemu/roms/ipxe/src/include/ipxe/script.h index 33420dae4..7e7a9a3a4 100644 --- a/qemu/roms/ipxe/src/include/ipxe/script.h +++ b/qemu/roms/ipxe/src/include/ipxe/script.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/scsi.h b/qemu/roms/ipxe/src/include/ipxe/scsi.h index 4428daac3..28b55b2d5 100644 --- a/qemu/roms/ipxe/src/include/ipxe/scsi.h +++ b/qemu/roms/ipxe/src/include/ipxe/scsi.h @@ -11,7 +11,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Maximum block for READ/WRITE (10) commands */ #define SCSI_MAX_BLOCK_10 0xffffffffULL diff --git a/qemu/roms/ipxe/src/include/ipxe/segment.h b/qemu/roms/ipxe/src/include/ipxe/segment.h index 37bed0e19..9d5ecbd9b 100644 --- a/qemu/roms/ipxe/src/include/ipxe/segment.h +++ b/qemu/roms/ipxe/src/include/ipxe/segment.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/serial.h b/qemu/roms/ipxe/src/include/ipxe/serial.h index b47b1d125..83be59c31 100644 --- a/qemu/roms/ipxe/src/include/ipxe/serial.h +++ b/qemu/roms/ipxe/src/include/ipxe/serial.h @@ -3,15 +3,14 @@ /** @file * - * Serial driver functions + * Serial console * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -extern void serial_putc ( int ch ); -extern int serial_getc ( void ); -extern int serial_ischar ( void ); -extern int serial_initialized; +#include + +extern struct uart serial_console; #endif /* _IPXE_SERIAL_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/settings.h b/qemu/roms/ipxe/src/include/ipxe/settings.h index d6929ecd0..95a553cc8 100644 --- a/qemu/roms/ipxe/src/include/ipxe/settings.h +++ b/qemu/roms/ipxe/src/include/ipxe/settings.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -415,6 +415,7 @@ extern const struct setting_type setting_type_uint32 __setting_type; extern const struct setting_type setting_type_hex __setting_type; extern const struct setting_type setting_type_hexhyp __setting_type; extern const struct setting_type setting_type_hexraw __setting_type; +extern const struct setting_type setting_type_base64 __setting_type; extern const struct setting_type setting_type_uuid __setting_type; extern const struct setting_type setting_type_busdevfn __setting_type; extern const struct setting_type setting_type_dnssl __setting_type; diff --git a/qemu/roms/ipxe/src/include/ipxe/settings_ui.h b/qemu/roms/ipxe/src/include/ipxe/settings_ui.h index 5f7be30cc..0bf21935d 100644 --- a/qemu/roms/ipxe/src/include/ipxe/settings_ui.h +++ b/qemu/roms/ipxe/src/include/ipxe/settings_ui.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct settings; diff --git a/qemu/roms/ipxe/src/include/ipxe/sha256.h b/qemu/roms/ipxe/src/include/ipxe/sha256.h index 9aa9f3e57..e234cce33 100644 --- a/qemu/roms/ipxe/src/include/ipxe/sha256.h +++ b/qemu/roms/ipxe/src/include/ipxe/sha256.h @@ -7,11 +7,14 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +/** SHA-256 number of rounds */ +#define SHA256_ROUNDS 64 + /** An SHA-256 digest */ struct sha256_digest { /** Hash output */ @@ -58,6 +61,8 @@ union sha256_digest_data_dwords { struct sha256_context { /** Amount of accumulated data */ size_t len; + /** Digest size */ + size_t digestsize; /** Digest and accumulated data */ union sha256_digest_data_dwords ddd; } __attribute__ (( packed )); @@ -68,6 +73,16 @@ struct sha256_context { /** SHA-256 digest size */ #define SHA256_DIGEST_SIZE sizeof ( struct sha256_digest ) +/** SHA-224 digest size */ +#define SHA224_DIGEST_SIZE ( SHA256_DIGEST_SIZE * 224 / 256 ) + +extern void sha256_family_init ( struct sha256_context *context, + const struct sha256_digest *init, + size_t digestsize ); +extern void sha256_update ( void *ctx, const void *data, size_t len ); +extern void sha256_final ( void *ctx, void *out ); + extern struct digest_algorithm sha256_algorithm; +extern struct digest_algorithm sha224_algorithm; #endif /* _IPXE_SHA256_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/sha512.h b/qemu/roms/ipxe/src/include/ipxe/sha512.h new file mode 100644 index 000000000..8e22d8357 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/sha512.h @@ -0,0 +1,98 @@ +#ifndef _IPXE_SHA512_H +#define _IPXE_SHA512_H + +/** @file + * + * SHA-512 algorithm + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** SHA-512 number of rounds */ +#define SHA512_ROUNDS 80 + +/** An SHA-512 digest */ +struct sha512_digest { + /** Hash output */ + uint64_t h[8]; +}; + +/** An SHA-512 data block */ +union sha512_block { + /** Raw bytes */ + uint8_t byte[128]; + /** Raw qwords */ + uint64_t qword[16]; + /** Final block structure */ + struct { + /** Padding */ + uint8_t pad[112]; + /** High 64 bits of length in bits */ + uint64_t len_hi; + /** Low 64 bits of length in bits */ + uint64_t len_lo; + } final; +}; + +/** SHA-512 digest and data block + * + * The order of fields within this structure is designed to minimise + * code size. + */ +struct sha512_digest_data { + /** Digest of data already processed */ + struct sha512_digest digest; + /** Accumulated data */ + union sha512_block data; +} __attribute__ (( packed )); + +/** SHA-512 digest and data block */ +union sha512_digest_data_qwords { + /** Digest and data block */ + struct sha512_digest_data dd; + /** Raw qwords */ + uint64_t qword[ sizeof ( struct sha512_digest_data ) / + sizeof ( uint64_t ) ]; +}; + +/** An SHA-512 context */ +struct sha512_context { + /** Amount of accumulated data */ + size_t len; + /** Digest size */ + size_t digestsize; + /** Digest and accumulated data */ + union sha512_digest_data_qwords ddq; +} __attribute__ (( packed )); + +/** SHA-512 context size */ +#define SHA512_CTX_SIZE sizeof ( struct sha512_context ) + +/** SHA-512 digest size */ +#define SHA512_DIGEST_SIZE sizeof ( struct sha512_digest ) + +/** SHA-384 digest size */ +#define SHA384_DIGEST_SIZE ( SHA512_DIGEST_SIZE * 384 / 512 ) + +/** SHA-512/256 digest size */ +#define SHA512_256_DIGEST_SIZE ( SHA512_DIGEST_SIZE * 256 / 512 ) + +/** SHA-512/224 digest size */ +#define SHA512_224_DIGEST_SIZE ( SHA512_DIGEST_SIZE * 224 / 512 ) + +extern void sha512_family_init ( struct sha512_context *context, + const struct sha512_digest *init, + size_t digestsize ); +extern void sha512_update ( void *ctx, const void *data, size_t len ); +extern void sha512_final ( void *ctx, void *out ); + +extern struct digest_algorithm sha512_algorithm; +extern struct digest_algorithm sha384_algorithm; +extern struct digest_algorithm sha512_256_algorithm; +extern struct digest_algorithm sha512_224_algorithm; + +#endif /* IPXE_SHA512_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/shell.h b/qemu/roms/ipxe/src/include/ipxe/shell.h index faa32f422..0d574e028 100644 --- a/qemu/roms/ipxe/src/include/ipxe/shell.h +++ b/qemu/roms/ipxe/src/include/ipxe/shell.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Shell stop states */ enum shell_stop_state { diff --git a/qemu/roms/ipxe/src/include/ipxe/smbios.h b/qemu/roms/ipxe/src/include/ipxe/smbios.h index ef5892a21..24b05ed62 100644 --- a/qemu/roms/ipxe/src/include/ipxe/smbios.h +++ b/qemu/roms/ipxe/src/include/ipxe/smbios.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/socket.h b/qemu/roms/ipxe/src/include/ipxe/socket.h index 7cb3912f4..8c70ea4c0 100644 --- a/qemu/roms/ipxe/src/include/ipxe/socket.h +++ b/qemu/roms/ipxe/src/include/ipxe/socket.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/spi.h b/qemu/roms/ipxe/src/include/ipxe/spi.h index d92d1aec9..83b53bce3 100644 --- a/qemu/roms/ipxe/src/include/ipxe/spi.h +++ b/qemu/roms/ipxe/src/include/ipxe/spi.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/spi_bit.h b/qemu/roms/ipxe/src/include/ipxe/spi_bit.h index 9cfa7b825..049d30a22 100644 --- a/qemu/roms/ipxe/src/include/ipxe/spi_bit.h +++ b/qemu/roms/ipxe/src/include/ipxe/spi_bit.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/stp.h b/qemu/roms/ipxe/src/include/ipxe/stp.h new file mode 100644 index 000000000..3d85e5ba4 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/stp.h @@ -0,0 +1,76 @@ +#ifndef _IPXE_STP_H +#define _IPXE_STP_H + +/** @file + * + * Spanning Tree Protocol (STP) + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** "Protocol" value for STP + * + * This is the concatenated {DSAP,SSAP} value used internally by iPXE + * as the network-layer protocol for LLC frames. + */ +#define ETH_P_STP 0x4242 + +/** A switch identifier */ +struct stp_switch { + /** Priotity */ + uint16_t priority; + /** MAC address */ + uint8_t mac[ETH_ALEN]; +} __attribute__ (( packed )); + +/** A Spanning Tree bridge protocol data unit */ +struct stp_bpdu { + /** LLC DSAP */ + uint8_t dsap; + /** LLC SSAP */ + uint8_t ssap; + /** LLC control field */ + uint8_t control; + /** Protocol ID */ + uint16_t protocol; + /** Protocol version */ + uint8_t version; + /** Message type */ + uint8_t type; + /** Flags */ + uint8_t flags; + /** Root switch */ + struct stp_switch root; + /** Root path cost */ + uint32_t cost; + /** Sender switch */ + struct stp_switch sender; + /** Port */ + uint16_t port; + /** Message age */ + uint16_t age; + /** Maximum age */ + uint16_t max; + /** Hello time */ + uint16_t hello; + /** Forward delay */ + uint16_t delay; +} __attribute__ (( packed )); + +/** Spanning Tree protocol ID */ +#define STP_PROTOCOL 0x0000 + +/** Rapid Spanning Tree protocol version */ +#define STP_VERSION_RSTP 0x02 + +/** Rapid Spanning Tree bridge PDU type */ +#define STP_TYPE_RSTP 0x02 + +/** Port is forwarding */ +#define STP_FL_FORWARDING 0x20 + +#endif /* _IPXE_STP_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/string.h b/qemu/roms/ipxe/src/include/ipxe/string.h new file mode 100644 index 000000000..a8cbe8faa --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/string.h @@ -0,0 +1,14 @@ +#ifndef _IPXE_STRING_H +#define _IPXE_STRING_H + +/** @file + * + * String functions + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +extern unsigned int digit_value ( unsigned int digit ); + +#endif /* _IPXE_STRING_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/syslog.h b/qemu/roms/ipxe/src/include/ipxe/syslog.h index 131692654..138440d66 100644 --- a/qemu/roms/ipxe/src/include/ipxe/syslog.h +++ b/qemu/roms/ipxe/src/include/ipxe/syslog.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/tables.h b/qemu/roms/ipxe/src/include/ipxe/tables.h index e35ce8220..60f8efdea 100644 --- a/qemu/roms/ipxe/src/include/ipxe/tables.h +++ b/qemu/roms/ipxe/src/include/ipxe/tables.h @@ -1,7 +1,7 @@ #ifndef _IPXE_TABLES_H #define _IPXE_TABLES_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @page ifdef_harmful #ifdef considered harmful * diff --git a/qemu/roms/ipxe/src/include/ipxe/tcp.h b/qemu/roms/ipxe/src/include/ipxe/tcp.h index 9baa6391c..063ebaa4b 100644 --- a/qemu/roms/ipxe/src/include/ipxe/tcp.h +++ b/qemu/roms/ipxe/src/include/ipxe/tcp.h @@ -9,7 +9,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include @@ -79,6 +79,48 @@ struct tcp_window_scale_padded_option { */ #define TCP_RX_WINDOW_SCALE 9 +/** TCP selective acknowledgement permitted option */ +struct tcp_sack_permitted_option { + uint8_t kind; + uint8_t length; +} __attribute__ (( packed )); + +/** Padded TCP selective acknowledgement permitted option (used for sending) */ +struct tcp_sack_permitted_padded_option { + uint8_t nop[2]; + struct tcp_sack_permitted_option spopt; +} __attribute__ (( packed )); + +/** Code for the TCP selective acknowledgement permitted option */ +#define TCP_OPTION_SACK_PERMITTED 4 + +/** TCP selective acknowledgement option */ +struct tcp_sack_option { + uint8_t kind; + uint8_t length; +} __attribute__ (( packed )); + +/** TCP selective acknowledgement block */ +struct tcp_sack_block { + uint32_t left; + uint32_t right; +} __attribute__ (( packed )); + +/** Maximum number of selective acknowledgement blocks + * + * This allows for the presence of the TCP timestamp option. + */ +#define TCP_SACK_MAX 3 + +/** Padded TCP selective acknowledgement option (used for sending) */ +struct tcp_sack_padded_option { + uint8_t nop[2]; + struct tcp_sack_option sackopt; +} __attribute__ (( packed )); + +/** Code for the TCP selective acknowledgement option */ +#define TCP_OPTION_SACK 5 + /** TCP timestamp option */ struct tcp_timestamp_option { uint8_t kind; @@ -102,6 +144,8 @@ struct tcp_options { const struct tcp_mss_option *mssopt; /** Window scale option, if present */ const struct tcp_window_scale_option *wsopt; + /** SACK permitted option, if present */ + const struct tcp_sack_permitted_option *spopt; /** Timestamp option, if present */ const struct tcp_timestamp_option *tsopt; }; @@ -376,6 +420,13 @@ static inline int tcp_in_window ( uint32_t seq, uint32_t start, return ( ( seq - start ) < len ); } +/** TCP finish wait time + * + * Currently set to one second, since we should not allow a slowly + * responding server to substantially delay a call to shutdown(). + */ +#define TCP_FINISH_TIMEOUT ( 1 * TICKS_PER_SEC ) + extern struct tcpip_protocol tcp_protocol __tcpip_protocol; #endif /* _IPXE_TCP_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/tcpip.h b/qemu/roms/ipxe/src/include/ipxe/tcpip.h index 200630d6b..3cfc8e3ac 100644 --- a/qemu/roms/ipxe/src/include/ipxe/tcpip.h +++ b/qemu/roms/ipxe/src/include/ipxe/tcpip.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -48,6 +48,12 @@ struct sockaddr_tcpip { uint16_t st_flags; /** TCP/IP port */ uint16_t st_port; + /** Scope ID + * + * For link-local or multicast addresses, this is the network + * device index. + */ + uint16_t st_scope_id; /** Padding * * This ensures that a struct @c sockaddr_tcpip is large @@ -57,7 +63,8 @@ struct sockaddr_tcpip { char pad[ sizeof ( struct sockaddr ) - ( sizeof ( sa_family_t ) /* st_family */ + sizeof ( uint16_t ) /* st_flags */ + - sizeof ( uint16_t ) /* st_port */ ) ]; + sizeof ( uint16_t ) /* st_port */ + + sizeof ( uint16_t ) /* st_scope_id */ ) ]; } __attribute__ (( packed, may_alias )); /** diff --git a/qemu/roms/ipxe/src/include/ipxe/test.h b/qemu/roms/ipxe/src/include/ipxe/test.h index 028ee29fb..0b65c299c 100644 --- a/qemu/roms/ipxe/src/include/ipxe/test.h +++ b/qemu/roms/ipxe/src/include/ipxe/test.h @@ -1,7 +1,7 @@ #ifndef _IPXE_TEST_H #define _IPXE_TEST_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/include/ipxe/tftp.h b/qemu/roms/ipxe/src/include/ipxe/tftp.h index aecafa2ae..e3661e1ac 100644 --- a/qemu/roms/ipxe/src/include/ipxe/tftp.h +++ b/qemu/roms/ipxe/src/include/ipxe/tftp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/time.h b/qemu/roms/ipxe/src/include/ipxe/time.h index 673fe098a..4c5bb2a00 100644 --- a/qemu/roms/ipxe/src/include/ipxe/time.h +++ b/qemu/roms/ipxe/src/include/ipxe/time.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -44,6 +44,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); /* Include all architecture-independent time API headers */ #include +#include #include /* Include all architecture-dependent time API headers */ diff --git a/qemu/roms/ipxe/src/include/ipxe/timer.h b/qemu/roms/ipxe/src/include/ipxe/timer.h index d0309655d..82fbb6764 100644 --- a/qemu/roms/ipxe/src/include/ipxe/timer.h +++ b/qemu/roms/ipxe/src/include/ipxe/timer.h @@ -9,7 +9,7 @@ * for a monotonically increasing tick counter. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/tls.h b/qemu/roms/ipxe/src/include/ipxe/tls.h index 586da26ec..7d982c326 100644 --- a/qemu/roms/ipxe/src/include/ipxe/tls.h +++ b/qemu/roms/ipxe/src/include/ipxe/tls.h @@ -7,7 +7,7 @@ * Transport Layer Security Protocol */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -20,6 +20,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include /** A TLS header */ struct tls_header { @@ -85,7 +86,10 @@ struct tls_header { /* TLS hash algorithm identifiers */ #define TLS_MD5_ALGORITHM 1 #define TLS_SHA1_ALGORITHM 2 +#define TLS_SHA224_ALGORITHM 3 #define TLS_SHA256_ALGORITHM 4 +#define TLS_SHA384_ALGORITHM 5 +#define TLS_SHA512_ALGORITHM 6 /* TLS signature algorithm identifiers */ #define TLS_RSA_ALGORITHM 1 @@ -101,6 +105,9 @@ struct tls_header { #define TLS_MAX_FRAGMENT_LENGTH_2048 3 #define TLS_MAX_FRAGMENT_LENGTH_4096 4 +/* TLS signature algorithms extension */ +#define TLS_SIGNATURE_ALGORITHMS 13 + /** TLS RX state machine state */ enum tls_rx_state { TLS_RX_HEADER = 0, @@ -131,6 +138,14 @@ struct tls_cipher_suite { uint16_t code; }; +/** TLS cipher suite table */ +#define TLS_CIPHER_SUITES \ + __table ( struct tls_cipher_suite, "tls_cipher_suites" ) + +/** Declare a TLS cipher suite */ +#define __tls_cipher_suite( pref ) \ + __table_entry ( TLS_CIPHER_SUITES, pref ) + /** A TLS cipher specification */ struct tls_cipherspec { /** Cipher suite */ @@ -165,6 +180,19 @@ struct tls_signature_hash_algorithm { struct tls_signature_hash_id code; }; +/** TLS signature hash algorithm table + * + * Note that the default (TLSv1.1 and earlier) algorithm using + * MD5+SHA1 is never explicitly specified. + */ +#define TLS_SIG_HASH_ALGORITHMS \ + __table ( struct tls_signature_hash_algorithm, \ + "tls_sig_hash_algorithms" ) + +/** Declare a TLS signature hash algorithm */ +#define __tls_sig_hash_algorithm \ + __table_entry ( TLS_SIG_HASH_ALGORITHMS, 01 ) + /** TLS pre-master secret */ struct tls_pre_master_secret { /** TLS version */ diff --git a/qemu/roms/ipxe/src/include/ipxe/uaccess.h b/qemu/roms/ipxe/src/include/ipxe/uaccess.h index 055bb2ca7..a3f78566a 100644 --- a/qemu/roms/ipxe/src/include/ipxe/uaccess.h +++ b/qemu/roms/ipxe/src/include/ipxe/uaccess.h @@ -19,7 +19,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/uart.h b/qemu/roms/ipxe/src/include/ipxe/uart.h new file mode 100644 index 000000000..c63eae615 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/uart.h @@ -0,0 +1,132 @@ +#ifndef _IPXE_UART_H +#define _IPXE_UART_H + +/** @file + * + * 16550-compatible UART + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** Transmitter holding register */ +#define UART_THR 0x00 + +/** Receiver buffer register */ +#define UART_RBR 0x00 + +/** Interrupt enable register */ +#define UART_IER 0x01 + +/** FIFO control register */ +#define UART_FCR 0x02 +#define UART_FCR_FE 0x01 /**< FIFO enable */ + +/** Line control register */ +#define UART_LCR 0x03 +#define UART_LCR_WLS0 0x01 /**< Word length select bit 0 */ +#define UART_LCR_WLS1 0x02 /**< Word length select bit 1 */ +#define UART_LCR_STB 0x04 /**< Number of stop bits */ +#define UART_LCR_PEN 0x08 /**< Parity enable */ +#define UART_LCR_EPS 0x10 /**< Even parity select */ +#define UART_LCR_DLAB 0x80 /**< Divisor latch access bit */ + +#define UART_LCR_WORD_LEN(x) ( ( (x) - 5 ) << 0 ) /**< Word length */ +#define UART_LCR_STOP_BITS(x) ( ( (x) - 1 ) << 2 ) /**< Stop bits */ +#define UART_LCR_PARITY(x) ( ( (x) - 0 ) << 3 ) /**< Parity */ + +/** + * Calculate line control register value + * + * @v word_len Word length (5-8) + * @v parity Parity (0=none, 1=odd, 3=even) + * @v stop_bits Stop bits (1-2) + * @ret lcr Line control register value + */ +#define UART_LCR_WPS( word_len, parity, stop_bits ) \ + ( UART_LCR_WORD_LEN ( (word_len) ) | \ + UART_LCR_PARITY ( (parity) ) | \ + UART_LCR_STOP_BITS ( (stop_bits) ) ) + +/** Default LCR value: 8 data bits, no parity, one stop bit */ +#define UART_LCR_8N1 UART_LCR_WPS ( 8, 0, 1 ) + +/** Modem control register */ +#define UART_MCR 0x04 +#define UART_MCR_DTR 0x01 /**< Data terminal ready */ +#define UART_MCR_RTS 0x02 /**< Request to send */ + +/** Line status register */ +#define UART_LSR 0x05 +#define UART_LSR_DR 0x01 /**< Data ready */ +#define UART_LSR_THRE 0x20 /**< Transmitter holding register empty */ +#define UART_LSR_TEMT 0x40 /**< Transmitter empty */ + +/** Scratch register */ +#define UART_SCR 0x07 + +/** Divisor latch (least significant byte) */ +#define UART_DLL 0x00 + +/** Divisor latch (most significant byte) */ +#define UART_DLM 0x01 + +/** Maximum baud rate */ +#define UART_MAX_BAUD 115200 + +/** A 16550-compatible UART */ +struct uart { + /** I/O port base address */ + void *base; + /** Baud rate divisor */ + uint16_t divisor; + /** Line control register */ + uint8_t lcr; +}; + +/** Symbolic names for port indexes */ +enum uart_port { + COM1 = 1, + COM2 = 2, + COM3 = 3, + COM4 = 4, +}; + +#include + +void uart_write ( struct uart *uart, unsigned int addr, uint8_t data ); +uint8_t uart_read ( struct uart *uart, unsigned int addr ); +int uart_select ( struct uart *uart, unsigned int port ); + +/** + * Check if received data is ready + * + * @v uart UART + * @ret ready Data is ready + */ +static inline int uart_data_ready ( struct uart *uart ) { + uint8_t lsr; + + lsr = uart_read ( uart, UART_LSR ); + return ( lsr & UART_LSR_DR ); +} + +/** + * Receive data + * + * @v uart UART + * @ret data Data + */ +static inline uint8_t uart_receive ( struct uart *uart ) { + + return uart_read ( uart, UART_RBR ); +} + +extern void uart_transmit ( struct uart *uart, uint8_t data ); +extern void uart_flush ( struct uart *uart ); +extern int uart_exists ( struct uart *uart ); +extern int uart_init ( struct uart *uart, unsigned int baud, uint8_t lcr ); + +#endif /* _IPXE_UART_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/udp.h b/qemu/roms/ipxe/src/include/ipxe/udp.h index 5717ef213..7b0de4dc0 100644 --- a/qemu/roms/ipxe/src/include/ipxe/udp.h +++ b/qemu/roms/ipxe/src/include/ipxe/udp.h @@ -9,7 +9,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/umalloc.h b/qemu/roms/ipxe/src/include/ipxe/umalloc.h index 4b25e182a..3892ef53b 100644 --- a/qemu/roms/ipxe/src/include/ipxe/umalloc.h +++ b/qemu/roms/ipxe/src/include/ipxe/umalloc.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/uri.h b/qemu/roms/ipxe/src/include/ipxe/uri.h index 7613d578d..00e5a24c4 100644 --- a/qemu/roms/ipxe/src/include/ipxe/uri.h +++ b/qemu/roms/ipxe/src/include/ipxe/uri.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -203,7 +203,7 @@ extern char * resolve_path ( const char *base_path, const char *relative_path ); extern struct uri * resolve_uri ( const struct uri *base_uri, struct uri *relative_uri ); -extern struct uri * tftp_uri ( struct in_addr next_server, +extern struct uri * tftp_uri ( struct in_addr next_server, unsigned int port, const char *filename ); extern void churi ( struct uri *uri ); diff --git a/qemu/roms/ipxe/src/include/ipxe/usb.h b/qemu/roms/ipxe/src/include/ipxe/usb.h new file mode 100644 index 000000000..ab060b8f4 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/usb.h @@ -0,0 +1,1319 @@ +#ifndef _IPXE_USB_H +#define _IPXE_USB_H + +/** @file + * + * Universal Serial Bus (USB) + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include + +/** USB protocols */ +enum usb_protocol { + /** USB 2.0 */ + USB_PROTO_2_0 = 0x0200, + /** USB 3.0 */ + USB_PROTO_3_0 = 0x0300, + /** USB 3.1 */ + USB_PROTO_3_1 = 0x0301, +}; + +/** Define a USB speed + * + * @v mantissa Mantissa + * @v exponent Exponent (in engineering terms: 1=k, 2=M, 3=G) + * @ret speed USB speed + */ +#define USB_SPEED( mantissa, exponent ) ( (exponent << 16) | (mantissa) ) + +/** Extract USB speed mantissa */ +#define USB_SPEED_MANTISSA(speed) ( (speed) & 0xffff ) + +/** Extract USB speed exponent */ +#define USB_SPEED_EXPONENT(speed) ( ( (speed) >> 16 ) & 0x3 ) + +/** USB device speeds */ +enum usb_speed { + /** Not connected */ + USB_SPEED_NONE = 0, + /** Low speed (1.5Mbps) */ + USB_SPEED_LOW = USB_SPEED ( 1500, 1 ), + /** Full speed (12Mbps) */ + USB_SPEED_FULL = USB_SPEED ( 12, 2 ), + /** High speed (480Mbps) */ + USB_SPEED_HIGH = USB_SPEED ( 480, 2 ), + /** Super speed (5Gbps) */ + USB_SPEED_SUPER = USB_SPEED ( 5, 3 ), +}; + +/** USB packet IDs */ +enum usb_pid { + /** IN PID */ + USB_PID_IN = 0x69, + /** OUT PID */ + USB_PID_OUT = 0xe1, + /** SETUP PID */ + USB_PID_SETUP = 0x2d, +}; + +/** A USB setup data packet */ +struct usb_setup_packet { + /** Request */ + uint16_t request; + /** Value paramer */ + uint16_t value; + /** Index parameter */ + uint16_t index; + /** Length of data stage */ + uint16_t len; +} __attribute__ (( packed )); + +/** Data transfer is from host to device */ +#define USB_DIR_OUT ( 0 << 7 ) + +/** Data transfer is from device to host */ +#define USB_DIR_IN ( 1 << 7 ) + +/** Standard request type */ +#define USB_TYPE_STANDARD ( 0 << 5 ) + +/** Class-specific request type */ +#define USB_TYPE_CLASS ( 1 << 5 ) + +/** Vendor-specific request type */ +#define USB_TYPE_VENDOR ( 2 << 5 ) + +/** Request recipient is the device */ +#define USB_RECIP_DEVICE ( 0 << 0 ) + +/** Request recipient is an interface */ +#define USB_RECIP_INTERFACE ( 1 << 0 ) + +/** Request recipient is an endpoint */ +#define USB_RECIP_ENDPOINT ( 2 << 0 ) + +/** Construct USB request type */ +#define USB_REQUEST_TYPE(type) ( (type) << 8 ) + +/** Get status */ +#define USB_GET_STATUS ( USB_DIR_IN | USB_REQUEST_TYPE ( 0 ) ) + +/** Clear feature */ +#define USB_CLEAR_FEATURE ( USB_DIR_OUT | USB_REQUEST_TYPE ( 1 ) ) + +/** Set feature */ +#define USB_SET_FEATURE ( USB_DIR_OUT | USB_REQUEST_TYPE ( 3 ) ) + +/** Set address */ +#define USB_SET_ADDRESS ( USB_DIR_OUT | USB_REQUEST_TYPE ( 5 ) ) + +/** Get descriptor */ +#define USB_GET_DESCRIPTOR ( USB_DIR_IN | USB_REQUEST_TYPE ( 6 ) ) + +/** Set descriptor */ +#define USB_SET_DESCRIPTOR ( USB_DIR_OUT | USB_REQUEST_TYPE ( 7 ) ) + +/** Get configuration */ +#define USB_GET_CONFIGURATION ( USB_DIR_IN | USB_REQUEST_TYPE ( 8 ) ) + +/** Set configuration */ +#define USB_SET_CONFIGURATION ( USB_DIR_OUT | USB_REQUEST_TYPE ( 9 ) ) + +/** Get interface */ +#define USB_GET_INTERFACE \ + ( USB_DIR_IN | USB_RECIP_INTERFACE | USB_REQUEST_TYPE ( 10 ) ) + +/** Set interface */ +#define USB_SET_INTERFACE \ + ( USB_DIR_OUT | USB_RECIP_INTERFACE | USB_REQUEST_TYPE ( 11 ) ) + +/** Endpoint halt feature */ +#define USB_ENDPOINT_HALT 0 + +/** A USB class code tuple */ +struct usb_class { + /** Class code */ + uint8_t class; + /** Subclass code */ + uint8_t subclass; + /** Protocol code */ + uint8_t protocol; +} __attribute__ (( packed )); + +/** Class code for USB hubs */ +#define USB_CLASS_HUB 9 + +/** A USB descriptor header */ +struct usb_descriptor_header { + /** Length of descriptor */ + uint8_t len; + /** Descriptor type */ + uint8_t type; +} __attribute__ (( packed )); + +/** A USB device descriptor */ +struct usb_device_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** USB specification release number in BCD */ + uint16_t protocol; + /** Device class */ + struct usb_class class; + /** Maximum packet size for endpoint zero */ + uint8_t mtu; + /** Vendor ID */ + uint16_t vendor; + /** Product ID */ + uint16_t product; + /** Device release number in BCD */ + uint16_t release; + /** Manufacturer string */ + uint8_t manufacturer; + /** Product string */ + uint8_t name; + /** Serial number string */ + uint8_t serial; + /** Number of possible configurations */ + uint8_t configurations; +} __attribute__ (( packed )); + +/** A USB device descriptor */ +#define USB_DEVICE_DESCRIPTOR 1 + +/** A USB configuration descriptor */ +struct usb_configuration_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** Total length */ + uint16_t len; + /** Number of interfaces */ + uint8_t interfaces; + /** Configuration value */ + uint8_t config; + /** Configuration string */ + uint8_t name; + /** Attributes */ + uint8_t attributes; + /** Maximum power consumption */ + uint8_t power; +} __attribute__ (( packed )); + +/** A USB configuration descriptor */ +#define USB_CONFIGURATION_DESCRIPTOR 2 + +/** A USB string descriptor */ +struct usb_string_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** String */ + char string[0]; +} __attribute__ (( packed )); + +/** A USB string descriptor */ +#define USB_STRING_DESCRIPTOR 3 + +/** A USB interface descriptor */ +struct usb_interface_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** Interface number */ + uint8_t interface; + /** Alternate setting */ + uint8_t alternate; + /** Number of endpoints */ + uint8_t endpoints; + /** Interface class */ + struct usb_class class; + /** Interface name */ + uint8_t name; +} __attribute__ (( packed )); + +/** A USB interface descriptor */ +#define USB_INTERFACE_DESCRIPTOR 4 + +/** A USB endpoint descriptor */ +struct usb_endpoint_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** Endpoint address */ + uint8_t endpoint; + /** Attributes */ + uint8_t attributes; + /** Maximum packet size and burst size */ + uint16_t sizes; + /** Polling interval */ + uint8_t interval; +} __attribute__ (( packed )); + +/** A USB endpoint descriptor */ +#define USB_ENDPOINT_DESCRIPTOR 5 + +/** Endpoint attribute transfer type mask */ +#define USB_ENDPOINT_ATTR_TYPE_MASK 0x03 + +/** Endpoint periodic type */ +#define USB_ENDPOINT_ATTR_PERIODIC 0x01 + +/** Control endpoint transfer type */ +#define USB_ENDPOINT_ATTR_CONTROL 0x00 + +/** Bulk endpoint transfer type */ +#define USB_ENDPOINT_ATTR_BULK 0x02 + +/** Interrupt endpoint transfer type */ +#define USB_ENDPOINT_ATTR_INTERRUPT 0x03 + +/** Bulk OUT endpoint (internal) type */ +#define USB_BULK_OUT ( USB_ENDPOINT_ATTR_BULK | USB_DIR_OUT ) + +/** Bulk IN endpoint (internal) type */ +#define USB_BULK_IN ( USB_ENDPOINT_ATTR_BULK | USB_DIR_IN ) + +/** Interrupt IN endpoint (internal) type */ +#define USB_INTERRUPT_IN ( USB_ENDPOINT_ATTR_INTERRUPT | USB_DIR_IN ) + +/** Interrupt OUT endpoint (internal) type */ +#define USB_INTERRUPT_OUT ( USB_ENDPOINT_ATTR_INTERRUPT | USB_DIR_OUT ) + +/** USB endpoint MTU */ +#define USB_ENDPOINT_MTU(sizes) ( ( (sizes) >> 0 ) & 0x07ff ) + +/** USB endpoint maximum burst size */ +#define USB_ENDPOINT_BURST(sizes) ( ( (sizes) >> 11 ) & 0x0003 ) + +/** A USB endpoint companion descriptor */ +struct usb_endpoint_companion_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** Maximum burst size */ + uint8_t burst; + /** Extended attributes */ + uint8_t extended; + /** Number of bytes per service interval */ + uint16_t periodic; +} __attribute__ (( packed )); + +/** A USB endpoint companion descriptor */ +#define USB_ENDPOINT_COMPANION_DESCRIPTOR 48 + +/** A USB interface association descriptor */ +struct usb_interface_association_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** First interface number */ + uint8_t first; + /** Interface count */ + uint8_t count; + /** Association class */ + struct usb_class class; + /** Association name */ + uint8_t name; +} __attribute__ (( packed )); + +/** A USB interface association descriptor */ +#define USB_INTERFACE_ASSOCIATION_DESCRIPTOR 11 + +/** A class-specific interface descriptor */ +#define USB_CS_INTERFACE_DESCRIPTOR 36 + +/** A class-specific endpoint descriptor */ +#define USB_CS_ENDPOINT_DESCRIPTOR 37 + +/** + * Get next USB descriptor + * + * @v desc USB descriptor header + * @ret next Next USB descriptor header + */ +static inline __attribute__ (( always_inline )) struct usb_descriptor_header * +usb_next_descriptor ( struct usb_descriptor_header *desc ) { + + return ( ( ( void * ) desc ) + desc->len ); +} + +/** + * Check that descriptor lies within a configuration descriptor + * + * @v config Configuration descriptor + * @v desc Descriptor header + * @v is_within Descriptor is within the configuration descriptor + */ +static inline __attribute__ (( always_inline )) int +usb_is_within_config ( struct usb_configuration_descriptor *config, + struct usb_descriptor_header *desc ) { + struct usb_descriptor_header *end = + ( ( ( void * ) config ) + le16_to_cpu ( config->len ) ); + + /* Check that descriptor starts within the configuration + * descriptor, and that the length does not exceed the + * configuration descriptor. This relies on the fact that + * usb_next_descriptor() needs to access only the first byte + * of the descriptor in order to determine the length. + */ + return ( ( desc < end ) && ( usb_next_descriptor ( desc ) <= end ) ); +} + +/** Iterate over all configuration descriptors */ +#define for_each_config_descriptor( desc, config ) \ + for ( desc = container_of ( &(config)->header, \ + typeof ( *desc ), header ) ; \ + usb_is_within_config ( (config), &desc->header ) ; \ + desc = container_of ( usb_next_descriptor ( &desc->header ), \ + typeof ( *desc ), header ) ) + +/** Iterate over all configuration descriptors within an interface descriptor */ +#define for_each_interface_descriptor( desc, config, interface ) \ + for ( desc = container_of ( usb_next_descriptor ( &(interface)-> \ + header ), \ + typeof ( *desc ), header ) ; \ + ( usb_is_within_config ( (config), &desc->header ) && \ + ( desc->header.type != USB_INTERFACE_DESCRIPTOR ) ) ; \ + desc = container_of ( usb_next_descriptor ( &desc->header ), \ + typeof ( *desc ), header ) ) + +/** A USB endpoint */ +struct usb_endpoint { + /** USB device */ + struct usb_device *usb; + /** Endpoint address */ + unsigned int address; + /** Attributes */ + unsigned int attributes; + /** Maximum transfer size */ + size_t mtu; + /** Maximum burst size */ + unsigned int burst; + /** Interval (in microframes) */ + unsigned int interval; + + /** Endpoint is open */ + int open; + /** Buffer fill level */ + unsigned int fill; + + /** List of halted endpoints */ + struct list_head halted; + + /** Host controller operations */ + struct usb_endpoint_host_operations *host; + /** Host controller private data */ + void *priv; + /** Driver operations */ + struct usb_endpoint_driver_operations *driver; + + /** Recycled I/O buffer list */ + struct list_head recycled; + /** Refill buffer length */ + size_t len; + /** Maximum fill level */ + unsigned int max; +}; + +/** USB endpoint host controller operations */ +struct usb_endpoint_host_operations { + /** Open endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ + int ( * open ) ( struct usb_endpoint *ep ); + /** Close endpoint + * + * @v ep USB endpoint + */ + void ( * close ) ( struct usb_endpoint *ep ); + /** + * Reset endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ + int ( * reset ) ( struct usb_endpoint *ep ); + /** Update MTU + * + * @v ep USB endpoint + * @ret rc Return status code + */ + int ( * mtu ) ( struct usb_endpoint *ep ); + /** Enqueue message transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @ret rc Return status code + */ + int ( * message ) ( struct usb_endpoint *ep, + struct io_buffer *iobuf ); + /** Enqueue stream transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v terminate Terminate using a short packet + * @ret rc Return status code + */ + int ( * stream ) ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int terminate ); +}; + +/** USB endpoint driver operations */ +struct usb_endpoint_driver_operations { + /** Complete transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ + void ( * complete ) ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ); +}; + +/** Control endpoint address */ +#define USB_EP0_ADDRESS 0x00 + +/** Control endpoint attributes */ +#define USB_EP0_ATTRIBUTES 0x00 + +/** Calculate default MTU based on device speed + * + * @v speed Device speed + * @ret mtu Default MTU + */ +#define USB_EP0_DEFAULT_MTU(speed) \ + ( ( (speed) >= USB_SPEED_SUPER ) ? 512 : \ + ( ( (speed) >= USB_SPEED_FULL ) ? 64 : 8 ) ) + +/** Control endpoint maximum burst size */ +#define USB_EP0_BURST 0 + +/** Control endpoint interval */ +#define USB_EP0_INTERVAL 0 + +/** Maximum endpoint number */ +#define USB_ENDPOINT_MAX 0x0f + +/** Endpoint direction is in */ +#define USB_ENDPOINT_IN 0x80 + +/** Construct endpoint index from endpoint address */ +#define USB_ENDPOINT_IDX(address) \ + ( ( (address) & USB_ENDPOINT_MAX ) | \ + ( ( (address) & USB_ENDPOINT_IN ) >> 3 ) ) + +/** + * Initialise USB endpoint + * + * @v ep USB endpoint + * @v usb USB device + * @v driver Driver operations + */ +static inline __attribute__ (( always_inline )) void +usb_endpoint_init ( struct usb_endpoint *ep, struct usb_device *usb, + struct usb_endpoint_driver_operations *driver ) { + + ep->usb = usb; + ep->driver = driver; +} + +/** + * Describe USB endpoint + * + * @v ep USB endpoint + * @v address Endpoint address + * @v attributes Attributes + * @v mtu Maximum packet size + * @v burst Maximum burst size + * @v interval Interval (in microframes) + */ +static inline __attribute__ (( always_inline )) void +usb_endpoint_describe ( struct usb_endpoint *ep, unsigned int address, + unsigned int attributes, size_t mtu, + unsigned int burst, unsigned int interval ) { + + ep->address = address; + ep->attributes = attributes; + ep->mtu = mtu; + ep->burst = burst; + ep->interval = interval; +} + +/** + * Set USB endpoint host controller private data + * + * @v ep USB endpoint + * @v priv Host controller private data + */ +static inline __attribute__ (( always_inline )) void +usb_endpoint_set_hostdata ( struct usb_endpoint *ep, void *priv ) { + ep->priv = priv; +} + +/** + * Get USB endpoint host controller private data + * + * @v ep USB endpoint + * @ret priv Host controller private data + */ +static inline __attribute__ (( always_inline )) void * +usb_endpoint_get_hostdata ( struct usb_endpoint *ep ) { + return ep->priv; +} + +extern const char * usb_endpoint_name ( struct usb_endpoint *ep ); +extern int +usb_endpoint_described ( struct usb_endpoint *ep, + struct usb_configuration_descriptor *config, + struct usb_interface_descriptor *interface, + unsigned int type, unsigned int index ); +extern int usb_endpoint_open ( struct usb_endpoint *ep ); +extern void usb_endpoint_close ( struct usb_endpoint *ep ); +extern int usb_message ( struct usb_endpoint *ep, unsigned int request, + unsigned int value, unsigned int index, + struct io_buffer *iobuf ); +extern int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int terminate ); +extern void usb_complete_err ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ); + +/** + * Initialise USB endpoint refill + * + * @v ep USB endpoint + * @v len Refill buffer length (or zero to use endpoint's MTU) + * @v max Maximum fill level + */ +static inline __attribute__ (( always_inline )) void +usb_refill_init ( struct usb_endpoint *ep, size_t len, unsigned int max ) { + + INIT_LIST_HEAD ( &ep->recycled ); + ep->len = len; + ep->max = max; +} + +/** + * Recycle I/O buffer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + */ +static inline __attribute__ (( always_inline )) void +usb_recycle ( struct usb_endpoint *ep, struct io_buffer *iobuf ) { + + list_add_tail ( &iobuf->list, &ep->recycled ); +} + +extern int usb_prefill ( struct usb_endpoint *ep ); +extern int usb_refill ( struct usb_endpoint *ep ); +extern void usb_flush ( struct usb_endpoint *ep ); + +/** + * A USB function + * + * A USB function represents an association of interfaces within a USB + * device. + */ +struct usb_function { + /** Name */ + const char *name; + /** USB device */ + struct usb_device *usb; + /** Class */ + struct usb_class class; + /** Number of interfaces */ + unsigned int count; + /** Generic device */ + struct device dev; + /** List of functions within this USB device */ + struct list_head list; + + /** Driver */ + struct usb_driver *driver; + /** Driver private data */ + void *priv; + + /** List of interface numbers + * + * This must be the last field within the structure. + */ + uint8_t interface[0]; +}; + +/** + * Set USB function driver private data + * + * @v func USB function + * @v priv Driver private data + */ +static inline __attribute__ (( always_inline )) void +usb_func_set_drvdata ( struct usb_function *func, void *priv ) { + func->priv = priv; +} + +/** + * Get USB function driver private data + * + * @v function USB function + * @ret priv Driver private data + */ +static inline __attribute__ (( always_inline )) void * +usb_func_get_drvdata ( struct usb_function *func ) { + return func->priv; +} + +/** A USB device */ +struct usb_device { + /** Name */ + char name[32]; + /** USB port */ + struct usb_port *port; + /** List of devices on this bus */ + struct list_head list; + /** Device address, if assigned */ + unsigned int address; + /** Device descriptor */ + struct usb_device_descriptor device; + /** List of functions */ + struct list_head functions; + + /** Host controller operations */ + struct usb_device_host_operations *host; + /** Host controller private data */ + void *priv; + + /** Endpoint list */ + struct usb_endpoint *ep[32]; + + /** Control endpoint */ + struct usb_endpoint control; + /** Completed control transfers */ + struct list_head complete; +}; + +/** USB device host controller operations */ +struct usb_device_host_operations { + /** Open device + * + * @v usb USB device + * @ret rc Return status code + */ + int ( * open ) ( struct usb_device *usb ); + /** Close device + * + * @v usb USB device + */ + void ( * close ) ( struct usb_device *usb ); + /** Assign device address + * + * @v usb USB device + * @ret rc Return status code + */ + int ( * address ) ( struct usb_device *usb ); +}; + +/** + * Set USB device host controller private data + * + * @v usb USB device + * @v priv Host controller private data + */ +static inline __attribute__ (( always_inline )) void +usb_set_hostdata ( struct usb_device *usb, void *priv ) { + usb->priv = priv; +} + +/** + * Get USB device host controller private data + * + * @v usb USB device + * @ret priv Host controller private data + */ +static inline __attribute__ (( always_inline )) void * +usb_get_hostdata ( struct usb_device *usb ) { + return usb->priv; +} + +/** + * Get USB endpoint + * + * @v usb USB device + * @v address Endpoint address + * @ret ep USB endpoint, or NULL if not opened + */ +static inline struct usb_endpoint * usb_endpoint ( struct usb_device *usb, + unsigned int address ) { + + return usb->ep[ USB_ENDPOINT_IDX ( address ) ]; +} + +/** A USB port */ +struct usb_port { + /** USB hub */ + struct usb_hub *hub; + /** Port address */ + unsigned int address; + /** Port protocol */ + unsigned int protocol; + /** Port speed */ + unsigned int speed; + /** Port disconnection has been detected + * + * This should be set whenever the underlying hardware reports + * a connection status change. + */ + int disconnected; + /** Port has an attached device */ + int attached; + /** Currently attached device (if in use) + * + * Note that this field will be NULL if the attached device + * has been freed (e.g. because there were no drivers found). + */ + struct usb_device *usb; + /** List of changed ports */ + struct list_head changed; +}; + +/** A USB hub */ +struct usb_hub { + /** Name */ + const char *name; + /** USB bus */ + struct usb_bus *bus; + /** Underlying USB device, if any */ + struct usb_device *usb; + /** Hub protocol */ + unsigned int protocol; + /** Number of ports */ + unsigned int ports; + + /** List of hubs */ + struct list_head list; + + /** Host controller operations */ + struct usb_hub_host_operations *host; + /** Driver operations */ + struct usb_hub_driver_operations *driver; + /** Driver private data */ + void *priv; + + /** Port list + * + * This must be the last field within the structure. + */ + struct usb_port port[0]; +}; + +/** USB hub host controller operations */ +struct usb_hub_host_operations { + /** Open hub + * + * @v hub USB hub + * @ret rc Return status code + */ + int ( * open ) ( struct usb_hub *hub ); + /** Close hub + * + * @v hub USB hub + */ + void ( * close ) ( struct usb_hub *hub ); +}; + +/** USB hub driver operations */ +struct usb_hub_driver_operations { + /** Open hub + * + * @v hub USB hub + * @ret rc Return status code + */ + int ( * open ) ( struct usb_hub *hub ); + /** Close hub + * + * @v hub USB hub + */ + void ( * close ) ( struct usb_hub *hub ); + /** Enable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ + int ( * enable ) ( struct usb_hub *hub, struct usb_port *port ); + /** Disable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ + int ( * disable ) ( struct usb_hub *hub, struct usb_port *port ); + /** Update port speed + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ + int ( * speed ) ( struct usb_hub *hub, struct usb_port *port ); + /** Clear transaction translator buffer + * + * @v hub USB hub + * @v port USB port + * @v ep USB endpoint + * @ret rc Return status code + */ + int ( * clear_tt ) ( struct usb_hub *hub, struct usb_port *port, + struct usb_endpoint *ep ); +}; + +/** + * Set USB hub driver private data + * + * @v hub USB hub + * @v priv Driver private data + */ +static inline __attribute__ (( always_inline )) void +usb_hub_set_drvdata ( struct usb_hub *hub, void *priv ) { + hub->priv = priv; +} + +/** + * Get USB hub driver private data + * + * @v hub USB hub + * @ret priv Driver private data + */ +static inline __attribute__ (( always_inline )) void * +usb_hub_get_drvdata ( struct usb_hub *hub ) { + return hub->priv; +} + +/** + * Get USB port + * + * @v hub USB hub + * @v address Port address + * @ret port USB port + */ +static inline __attribute__ (( always_inline )) struct usb_port * +usb_port ( struct usb_hub *hub, unsigned int address ) { + + return &hub->port[ address - 1 ]; +} + +/** A USB bus */ +struct usb_bus { + /** Name */ + const char *name; + /** Underlying hardware device */ + struct device *dev; + /** Host controller operations set */ + struct usb_host_operations *op; + + /** Largest transfer allowed on the bus */ + size_t mtu; + /** Address in-use mask + * + * This is used only by buses which perform manual address + * assignment. USB allows for addresses in the range [1,127]. + * We use a simple bitmask which restricts us to the range + * [1,64]; this is unlikely to be a problem in practice. For + * comparison: controllers which perform autonomous address + * assignment (such as xHCI) typically allow for only 32 + * devices per bus anyway. + */ + unsigned long long addresses; + + /** Root hub */ + struct usb_hub *hub; + + /** List of USB buses */ + struct list_head list; + /** List of devices */ + struct list_head devices; + /** List of hubs */ + struct list_head hubs; + + /** Host controller operations */ + struct usb_bus_host_operations *host; + /** Host controller private data */ + void *priv; +}; + +/** USB bus host controller operations */ +struct usb_bus_host_operations { + /** Open bus + * + * @v bus USB bus + * @ret rc Return status code + */ + int ( * open ) ( struct usb_bus *bus ); + /** Close bus + * + * @v bus USB bus + */ + void ( * close ) ( struct usb_bus *bus ); + /** Poll bus + * + * @v bus USB bus + */ + void ( * poll ) ( struct usb_bus *bus ); +}; + +/** USB host controller operations */ +struct usb_host_operations { + /** Endpoint operations */ + struct usb_endpoint_host_operations endpoint; + /** Device operations */ + struct usb_device_host_operations device; + /** Bus operations */ + struct usb_bus_host_operations bus; + /** Hub operations */ + struct usb_hub_host_operations hub; + /** Root hub operations */ + struct usb_hub_driver_operations root; +}; + +/** + * Set USB bus host controller private data + * + * @v bus USB bus + * @v priv Host controller private data + */ +static inline __attribute__ (( always_inline )) void +usb_bus_set_hostdata ( struct usb_bus *bus, void *priv ) { + bus->priv = priv; +} + +/** + * Get USB bus host controller private data + * + * @v bus USB bus + * @ret priv Host controller private data + */ +static inline __attribute__ (( always_inline )) void * +usb_bus_get_hostdata ( struct usb_bus *bus ) { + return bus->priv; +} + +/** + * Poll USB bus + * + * @v bus USB bus + */ +static inline __attribute__ (( always_inline )) void +usb_poll ( struct usb_bus *bus ) { + bus->host->poll ( bus ); +} + +/** Iterate over all USB buses */ +#define for_each_usb_bus( bus ) \ + list_for_each_entry ( (bus), &usb_buses, list ) + +/** + * Complete transfer (without error) + * + * @v ep USB endpoint + * @v iobuf I/O buffer + */ +static inline __attribute__ (( always_inline )) void +usb_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf ) { + usb_complete_err ( ep, iobuf, 0 ); +} + +extern int usb_control ( struct usb_device *usb, unsigned int request, + unsigned int value, unsigned int index, void *data, + size_t len ); +extern int usb_get_string_descriptor ( struct usb_device *usb, + unsigned int index, + unsigned int language, + char *buf, size_t len ); + +/** + * Get status + * + * @v usb USB device + * @v type Request type + * @v index Target index + * @v data Status to fill in + * @v len Length of status descriptor + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_get_status ( struct usb_device *usb, unsigned int type, unsigned int index, + void *data, size_t len ) { + + return usb_control ( usb, ( USB_GET_STATUS | type ), 0, index, + data, len ); +} + +/** + * Clear feature + * + * @v usb USB device + * @v type Request type + * @v feature Feature selector + * @v index Target index + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_clear_feature ( struct usb_device *usb, unsigned int type, + unsigned int feature, unsigned int index ) { + + return usb_control ( usb, ( USB_CLEAR_FEATURE | type ), + feature, index, NULL, 0 ); +} + +/** + * Set feature + * + * @v usb USB device + * @v type Request type + * @v feature Feature selector + * @v index Target index + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_set_feature ( struct usb_device *usb, unsigned int type, + unsigned int feature, unsigned int index ) { + + return usb_control ( usb, ( USB_SET_FEATURE | type ), + feature, index, NULL, 0 ); +} + +/** + * Set address + * + * @v usb USB device + * @v address Device address + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_set_address ( struct usb_device *usb, unsigned int address ) { + + return usb_control ( usb, USB_SET_ADDRESS, address, 0, NULL, 0 ); +} + +/** + * Get USB descriptor + * + * @v usb USB device + * @v type Request type + * @v desc Descriptor type + * @v index Descriptor index + * @v language Language ID (for string descriptors) + * @v data Descriptor to fill in + * @v len Maximum length of descriptor + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_get_descriptor ( struct usb_device *usb, unsigned int type, + unsigned int desc, unsigned int index, + unsigned int language, struct usb_descriptor_header *data, + size_t len ) { + + return usb_control ( usb, ( USB_GET_DESCRIPTOR | type ), + ( ( desc << 8 ) | index ), language, data, len ); +} + +/** + * Get first part of USB device descriptor (up to and including MTU) + * + * @v usb USB device + * @v data Device descriptor to (partially) fill in + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_get_mtu ( struct usb_device *usb, struct usb_device_descriptor *data ) { + + return usb_get_descriptor ( usb, 0, USB_DEVICE_DESCRIPTOR, 0, 0, + &data->header, + ( offsetof ( typeof ( *data ), mtu ) + + sizeof ( data->mtu ) ) ); +} + +/** + * Get USB device descriptor + * + * @v usb USB device + * @v data Device descriptor to fill in + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_get_device_descriptor ( struct usb_device *usb, + struct usb_device_descriptor *data ) { + + return usb_get_descriptor ( usb, 0, USB_DEVICE_DESCRIPTOR, 0, 0, + &data->header, sizeof ( *data ) ); +} + +/** + * Get USB configuration descriptor + * + * @v usb USB device + * @v index Configuration index + * @v data Configuration descriptor to fill in + * @ret rc Return status code + */ +static inline __attribute (( always_inline )) int +usb_get_config_descriptor ( struct usb_device *usb, unsigned int index, + struct usb_configuration_descriptor *data, + size_t len ) { + + return usb_get_descriptor ( usb, 0, USB_CONFIGURATION_DESCRIPTOR, index, + 0, &data->header, len ); +} + +/** + * Set USB configuration + * + * @v usb USB device + * @v index Configuration index + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_set_configuration ( struct usb_device *usb, unsigned int index ) { + + return usb_control ( usb, USB_SET_CONFIGURATION, index, 0, NULL, 0 ); +} + +/** + * Set USB interface alternate setting + * + * @v usb USB device + * @v interface Interface number + * @v alternate Alternate setting + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_set_interface ( struct usb_device *usb, unsigned int interface, + unsigned int alternate ) { + + return usb_control ( usb, USB_SET_INTERFACE, alternate, interface, + NULL, 0 ); +} + +extern struct list_head usb_buses; + +extern struct usb_interface_descriptor * +usb_interface_descriptor ( struct usb_configuration_descriptor *config, + unsigned int interface, unsigned int alternate ); +extern struct usb_endpoint_descriptor * +usb_endpoint_descriptor ( struct usb_configuration_descriptor *config, + struct usb_interface_descriptor *interface, + unsigned int type, unsigned int index ); +extern struct usb_endpoint_companion_descriptor * +usb_endpoint_companion_descriptor ( struct usb_configuration_descriptor *config, + struct usb_endpoint_descriptor *desc ); + +extern struct usb_hub * alloc_usb_hub ( struct usb_bus *bus, + struct usb_device *usb, + unsigned int ports, + struct usb_hub_driver_operations *op ); +extern int register_usb_hub ( struct usb_hub *hub ); +extern void unregister_usb_hub ( struct usb_hub *hub ); +extern void free_usb_hub ( struct usb_hub *hub ); + +extern void usb_port_changed ( struct usb_port *port ); + +extern struct usb_bus * alloc_usb_bus ( struct device *dev, + unsigned int ports, size_t mtu, + struct usb_host_operations *op ); +extern int register_usb_bus ( struct usb_bus *bus ); +extern void unregister_usb_bus ( struct usb_bus *bus ); +extern void free_usb_bus ( struct usb_bus *bus ); +extern struct usb_bus * find_usb_bus_by_location ( unsigned int bus_type, + unsigned int location ); + +extern int usb_alloc_address ( struct usb_bus *bus ); +extern void usb_free_address ( struct usb_bus *bus, unsigned int address ); +extern unsigned int usb_route_string ( struct usb_device *usb ); +extern unsigned int usb_depth ( struct usb_device *usb ); +extern struct usb_port * usb_root_hub_port ( struct usb_device *usb ); +extern struct usb_port * usb_transaction_translator ( struct usb_device *usb ); + +/** Minimum reset time + * + * Section 7.1.7.5 of the USB2 specification states that root hub + * ports should assert reset signalling for at least 50ms. + */ +#define USB_RESET_DELAY_MS 50 + +/** Reset recovery time + * + * Section 9.2.6.2 of the USB2 specification states that the + * "recovery" interval after a port reset is 10ms. + */ +#define USB_RESET_RECOVER_DELAY_MS 10 + +/** Maximum time to wait for a control transaction to complete + * + * Section 9.2.6.1 of the USB2 specification states that the upper + * limit for commands to be processed is 5 seconds. + */ +#define USB_CONTROL_MAX_WAIT_MS 5000 + +/** Set address recovery time + * + * Section 9.2.6.3 of the USB2 specification states that devices are + * allowed a 2ms recovery interval after receiving a new address. + */ +#define USB_SET_ADDRESS_RECOVER_DELAY_MS 2 + +/** Time to wait for ports to stabilise + * + * Section 7.1.7.3 of the USB specification states that we must allow + * 100ms for devices to signal attachment, and an additional 100ms for + * connection debouncing. (This delay is parallelised across all + * ports on a hub; we do not delay separately for each port.) + */ +#define USB_PORT_DELAY_MS 200 + +/** A USB device ID */ +struct usb_device_id { + /** Name */ + const char *name; + /** Vendor ID */ + uint16_t vendor; + /** Product ID */ + uint16_t product; + /** Class */ + struct usb_class class; +}; + +/** Match-anything ID */ +#define USB_ANY_ID 0xffff + +/** A USB driver */ +struct usb_driver { + /** USB ID table */ + struct usb_device_id *ids; + /** Number of entries in ID table */ + unsigned int id_count; + /** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ + int ( * probe ) ( struct usb_function *func, + struct usb_configuration_descriptor *config ); + /** + * Remove device + * + * @v func USB function + */ + void ( * remove ) ( struct usb_function *func ); +}; + +/** USB driver table */ +#define USB_DRIVERS __table ( struct usb_driver, "usb_drivers" ) + +/** Declare a USB driver */ +#define __usb_driver __table_entry ( USB_DRIVERS, 01 ) + +#endif /* _IPXE_USB_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/usbhid.h b/qemu/roms/ipxe/src/include/ipxe/usbhid.h new file mode 100644 index 000000000..fe9d84455 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/usbhid.h @@ -0,0 +1,106 @@ +#ifndef _IPXE_USBHID_H +#define _IPXE_USBHID_H + +/** @file + * + * USB human interface devices (HID) + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** Class code for human interface devices */ +#define USB_CLASS_HID 3 + +/** Subclass code for boot devices */ +#define USB_SUBCLASS_HID_BOOT 1 + +/** Set protocol */ +#define USBHID_SET_PROTOCOL \ + ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x0b ) ) + +/** Boot protocol */ +#define USBHID_PROTOCOL_BOOT 0 + +/** Report protocol */ +#define USBHID_PROTOCOL_REPORT 1 + +/** Set idle time */ +#define USBHID_SET_IDLE \ + ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x0a ) ) + +/** A USB human interface device */ +struct usb_hid { + /** USB function */ + struct usb_function *func; + /** Interrupt IN endpoint */ + struct usb_endpoint in; + /** Interrupt OUT endpoint (optional) */ + struct usb_endpoint out; +}; + +/** + * Initialise USB human interface device + * + * @v hid USB human interface device + * @v func USB function + * @v in Interrupt IN endpoint operations + * @v out Interrupt OUT endpoint operations (or NULL) + */ +static inline __attribute__ (( always_inline )) void +usbhid_init ( struct usb_hid *hid, struct usb_function *func, + struct usb_endpoint_driver_operations *in, + struct usb_endpoint_driver_operations *out ) { + struct usb_device *usb = func->usb; + + hid->func = func; + usb_endpoint_init ( &hid->in, usb, in ); + if ( out ) + usb_endpoint_init ( &hid->out, usb, out ); +} + +/** + * Set protocol + * + * @v usb USB device + * @v interface Interface number + * @v protocol HID protocol + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usbhid_set_protocol ( struct usb_device *usb, unsigned int interface, + unsigned int protocol ) { + + return usb_control ( usb, USBHID_SET_PROTOCOL, protocol, interface, + NULL, 0 ); +} + +/** + * Set idle time + * + * @v usb USB device + * @v interface Interface number + * @v report Report ID + * @v duration Duration (in 4ms units) + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usbhid_set_idle ( struct usb_device *usb, unsigned int interface, + unsigned int report, unsigned int duration ) { + + return usb_control ( usb, USBHID_SET_IDLE, + ( ( duration << 8 ) | report ), + interface, NULL, 0 ); +} + +extern int usbhid_open ( struct usb_hid *hid ); +extern void usbhid_close ( struct usb_hid *hid ); +extern int usbhid_refill ( struct usb_hid *hid ); +extern int usbhid_describe ( struct usb_hid *hid, + struct usb_configuration_descriptor *config ); + +#endif /* _IPXE_USBHID_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/usbnet.h b/qemu/roms/ipxe/src/include/ipxe/usbnet.h new file mode 100644 index 000000000..33a8f3f58 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/usbnet.h @@ -0,0 +1,62 @@ +#ifndef _IPXE_USBNET_H +#define _IPXE_USBNET_H + +/** @file + * + * USB network devices + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** A USB network device */ +struct usbnet_device { + /** USB function */ + struct usb_function *func; + + /** Communications interface */ + unsigned int comms; + /** Data interface */ + unsigned int data; + /** Alternate setting for data interface */ + unsigned int alternate; + + /** Interrupt endpoint */ + struct usb_endpoint intr; + /** Bulk IN endpoint */ + struct usb_endpoint in; + /** Bulk OUT endpoint */ + struct usb_endpoint out; +}; + +/** + * Initialise USB network device + * + * @v usbnet USB network device + * @v func USB function + * @v intr Interrupt endpoint operations + * @v in Bulk IN endpoint operations + * @v out Bulk OUT endpoint operations + */ +static inline __attribute__ (( always_inline )) void +usbnet_init ( struct usbnet_device *usbnet, struct usb_function *func, + struct usb_endpoint_driver_operations *intr, + struct usb_endpoint_driver_operations *in, + struct usb_endpoint_driver_operations *out ) { + struct usb_device *usb = func->usb; + + usbnet->func = func; + usb_endpoint_init ( &usbnet->intr, usb, intr ); + usb_endpoint_init ( &usbnet->in, usb, in ); + usb_endpoint_init ( &usbnet->out, usb, out ); +} + +extern int usbnet_open ( struct usbnet_device *usbnet ); +extern void usbnet_close ( struct usbnet_device *usbnet ); +extern int usbnet_refill ( struct usbnet_device *usbnet ); +extern int usbnet_describe ( struct usbnet_device *usbnet, + struct usb_configuration_descriptor *config ); + +#endif /* _IPXE_USBNET_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/uuid.h b/qemu/roms/ipxe/src/include/ipxe/uuid.h index ad515d0cb..6c45eb9aa 100644 --- a/qemu/roms/ipxe/src/include/ipxe/uuid.h +++ b/qemu/roms/ipxe/src/include/ipxe/uuid.h @@ -6,7 +6,7 @@ * Universally unique IDs */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/validator.h b/qemu/roms/ipxe/src/include/ipxe/validator.h index 23bdab423..0aee56eb0 100644 --- a/qemu/roms/ipxe/src/include/ipxe/validator.h +++ b/qemu/roms/ipxe/src/include/ipxe/validator.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/version.h b/qemu/roms/ipxe/src/include/ipxe/version.h index ae4275db1..a43a33425 100644 --- a/qemu/roms/ipxe/src/include/ipxe/version.h +++ b/qemu/roms/ipxe/src/include/ipxe/version.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/vlan.h b/qemu/roms/ipxe/src/include/ipxe/vlan.h index 083c21916..439e0c16d 100644 --- a/qemu/roms/ipxe/src/include/ipxe/vlan.h +++ b/qemu/roms/ipxe/src/include/ipxe/vlan.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** A VLAN header */ struct vlan_header { diff --git a/qemu/roms/ipxe/src/include/ipxe/vmbus.h b/qemu/roms/ipxe/src/include/ipxe/vmbus.h new file mode 100644 index 000000000..26fc578c6 --- /dev/null +++ b/qemu/roms/ipxe/src/include/ipxe/vmbus.h @@ -0,0 +1,634 @@ +#ifndef _IPXE_VMBUS_H +#define _IPXE_VMBUS_H + +/** @file + * + * Hyper-V virtual machine bus + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include + +/** VMBus message connection ID */ +#define VMBUS_MESSAGE_ID 1 + +/** VMBus event connection ID */ +#define VMBUS_EVENT_ID 2 + +/** VMBus message type */ +#define VMBUS_MESSAGE_TYPE 1 + +/** VMBus message synthetic interrupt */ +#define VMBUS_MESSAGE_SINT 2 + +/** VMBus version number */ +union vmbus_version { + /** Raw version */ + uint32_t raw; + /** Major/minor version */ + struct { + /** Minor version */ + uint16_t minor; + /** Major version */ + uint16_t major; + }; +} __attribute__ (( packed )); + +/** Known VMBus protocol versions */ +enum vmbus_raw_version { + /** Windows Server 2008 */ + VMBUS_VERSION_WS2008 = ( ( 0 << 16 ) | ( 13 << 0 ) ), + /** Windows 7 */ + VMBUS_VERSION_WIN7 = ( ( 1 << 16 ) | ( 1 << 0 ) ), + /** Windows 8 */ + VMBUS_VERSION_WIN8 = ( ( 2 << 16 ) | ( 4 << 0 ) ), + /** Windows 8.1 */ + VMBUS_VERSION_WIN8_1 = ( ( 3 << 16 ) | ( 0 << 0 ) ), +}; + +/** Guest physical address range descriptor */ +struct vmbus_gpa_range { + /** Byte count */ + uint32_t len; + /** Starting byte offset */ + uint32_t offset; + /** Page frame numbers + * + * The length of this array is implied by the byte count and + * starting offset. + */ + uint64_t pfn[0]; +} __attribute__ (( packed )); + +/** VMBus message header */ +struct vmbus_message_header { + /** Message type */ + uint32_t type; + /** Reserved */ + uint32_t reserved; +} __attribute__ (( packed )); + +/** VMBus message types */ +enum vmbus_message_type { + VMBUS_OFFER_CHANNEL = 1, + VMBUS_REQUEST_OFFERS = 3, + VMBUS_ALL_OFFERS_DELIVERED = 4, + VMBUS_OPEN_CHANNEL = 5, + VMBUS_OPEN_CHANNEL_RESULT = 6, + VMBUS_CLOSE_CHANNEL = 7, + VMBUS_GPADL_HEADER = 8, + VMBUS_GPADL_CREATED = 10, + VMBUS_GPADL_TEARDOWN = 11, + VMBUS_GPADL_TORNDOWN = 12, + VMBUS_INITIATE_CONTACT = 14, + VMBUS_VERSION_RESPONSE = 15, + VMBUS_UNLOAD = 16, + VMBUS_UNLOAD_RESPONSE = 17, +}; + +/** VMBus "offer channel" message */ +struct vmbus_offer_channel { + /** Message header */ + struct vmbus_message_header header; + /** Channel type */ + union uuid type; + /** Channel instance */ + union uuid instance; + /** Reserved */ + uint8_t reserved_a[16]; + /** Flags */ + uint16_t flags; + /** Reserved */ + uint8_t reserved_b[2]; + /** User data */ + uint8_t data[120]; + /** Reserved */ + uint8_t reserved_c[4]; + /** Channel ID */ + uint32_t channel; + /** Monitor ID */ + uint8_t monitor; + /** Monitor exists */ + uint8_t monitored; + /** Reserved */ + uint8_t reserved[2]; + /** Connection ID */ + uint32_t connection; +} __attribute__ (( packed )); + +/** VMBus "open channel" message */ +struct vmbus_open_channel { + /** Message header */ + struct vmbus_message_header header; + /** Channel ID */ + uint32_t channel; + /** Open ID */ + uint32_t id; + /** Ring buffer GPADL ID */ + uint32_t gpadl; + /** Reserved */ + uint32_t reserved; + /** Outbound ring buffer size (in pages) */ + uint32_t out_pages; + /** User-specific data */ + uint8_t data[120]; +} __attribute__ (( packed )); + +/** VMBus "open channel result" message */ +struct vmbus_open_channel_result { + /** Message header */ + struct vmbus_message_header header; + /** Channel ID */ + uint32_t channel; + /** Open ID */ + uint32_t id; + /** Status */ + uint32_t status; +} __attribute__ (( packed )); + +/** VMBus "close channel" message */ +struct vmbus_close_channel { + /** Message header */ + struct vmbus_message_header header; + /** Channel ID */ + uint32_t channel; +} __attribute__ (( packed )); + +/** VMBus "GPADL header" message */ +struct vmbus_gpadl_header { + /** Message header */ + struct vmbus_message_header header; + /** Channel ID */ + uint32_t channel; + /** GPADL ID */ + uint32_t gpadl; + /** Length of range descriptors */ + uint16_t range_len; + /** Number of range descriptors */ + uint16_t range_count; + /** Range descriptors */ + struct vmbus_gpa_range range[0]; +} __attribute__ (( packed )); + +/** VMBus "GPADL created" message */ +struct vmbus_gpadl_created { + /** Message header */ + struct vmbus_message_header header; + /** Channel ID */ + uint32_t channel; + /** GPADL ID */ + uint32_t gpadl; + /** Creation status */ + uint32_t status; +} __attribute__ (( packed )); + +/** VMBus "GPADL teardown" message */ +struct vmbus_gpadl_teardown { + /** Message header */ + struct vmbus_message_header header; + /** Channel ID */ + uint32_t channel; + /** GPADL ID */ + uint32_t gpadl; +} __attribute__ (( packed )); + +/** VMBus "GPADL torndown" message */ +struct vmbus_gpadl_torndown { + /** Message header */ + struct vmbus_message_header header; + /** GPADL ID */ + uint32_t gpadl; +} __attribute__ (( packed )); + +/** VMBus "initiate contact" message */ +struct vmbus_initiate_contact { + /** Message header */ + struct vmbus_message_header header; + /** Requested version */ + union vmbus_version version; + /** Target virtual CPU */ + uint32_t vcpu; + /** Interrupt page base address */ + uint64_t intr; + /** Parent to child monitor page base address */ + uint64_t monitor_in; + /** Child to parent monitor page base address */ + uint64_t monitor_out; +} __attribute__ (( packed )); + +/** VMBus "version response" message */ +struct vmbus_version_response { + /** Message header */ + struct vmbus_message_header header; + /** Version is supported */ + uint8_t supported; + /** Reserved */ + uint8_t reserved[3]; + /** Version */ + union vmbus_version version; +} __attribute__ (( packed )); + +/** VMBus message */ +union vmbus_message { + /** Common message header */ + struct vmbus_message_header header; + /** "Offer channel" message */ + struct vmbus_offer_channel offer; + /** "Open channel" message */ + struct vmbus_open_channel open; + /** "Open channel result" message */ + struct vmbus_open_channel_result opened; + /** "Close channel" message */ + struct vmbus_close_channel close; + /** "GPADL header" message */ + struct vmbus_gpadl_header gpadlhdr; + /** "GPADL created" message */ + struct vmbus_gpadl_created created; + /** "GPADL teardown" message */ + struct vmbus_gpadl_teardown teardown; + /** "GPADL torndown" message */ + struct vmbus_gpadl_torndown torndown; + /** "Initiate contact" message */ + struct vmbus_initiate_contact initiate; + /** "Version response" message */ + struct vmbus_version_response version; +}; + +/** VMBus packet header */ +struct vmbus_packet_header { + /** Type */ + uint16_t type; + /** Length of packet header (in quadwords) */ + uint16_t hdr_qlen; + /** Length of packet (in quadwords) */ + uint16_t qlen; + /** Flags */ + uint16_t flags; + /** Transaction ID + * + * This is an opaque token: we therefore treat it as + * native-endian and don't worry about byte-swapping. + */ + uint64_t xid; +} __attribute__ (( packed )); + +/** VMBus packet types */ +enum vmbus_packet_type { + VMBUS_DATA_INBAND = 6, + VMBUS_DATA_XFER_PAGES = 7, + VMBUS_DATA_GPA_DIRECT = 9, + VMBUS_CANCELLATION = 10, + VMBUS_COMPLETION = 11, +}; + +/** VMBus packet flags */ +enum vmbus_packet_flags { + VMBUS_COMPLETION_REQUESTED = 0x0001, +}; + +/** VMBus GPA direct header */ +struct vmbus_gpa_direct_header { + /** Packet header */ + struct vmbus_packet_header header; + /** Reserved */ + uint32_t reserved; + /** Number of range descriptors */ + uint32_t range_count; + /** Range descriptors */ + struct vmbus_gpa_range range[0]; +} __attribute__ (( packed )); + +/** VMBus transfer page range */ +struct vmbus_xfer_page_range { + /** Length */ + uint32_t len; + /** Offset */ + uint32_t offset; +} __attribute__ (( packed )); + +/** VMBus transfer page header */ +struct vmbus_xfer_page_header { + /** Packet header */ + struct vmbus_packet_header header; + /** Page set ID */ + uint16_t pageset; + /** Sender owns page set */ + uint8_t owner; + /** Reserved */ + uint8_t reserved; + /** Number of range descriptors */ + uint32_t range_count; + /** Range descriptors */ + struct vmbus_xfer_page_range range[0]; +} __attribute__ (( packed )); + +/** Maximum expected size of VMBus packet header */ +#define VMBUS_PACKET_MAX_HEADER_LEN 64 + +/** VMBus maximum-sized packet header */ +union vmbus_packet_header_max { + /** Common header */ + struct vmbus_packet_header header; + /** GPA direct header */ + struct vmbus_gpa_direct_header gpa; + /** Transfer page header */ + struct vmbus_xfer_page_header xfer; + /** Padding to maximum supported size */ + uint8_t padding[VMBUS_PACKET_MAX_HEADER_LEN]; +} __attribute__ (( packed )); + +/** VMBus packet footer */ +struct vmbus_packet_footer { + /** Reserved */ + uint32_t reserved; + /** Producer index of the first byte of the packet */ + uint32_t prod; +} __attribute__ (( packed )); + +/** VMBus ring buffer + * + * This is the structure of the each of the ring buffers created when + * a VMBus channel is opened. + */ +struct vmbus_ring { + /** Producer index (modulo ring length) */ + uint32_t prod; + /** Consumer index (modulo ring length) */ + uint32_t cons; + /** Interrupt mask */ + uint32_t intr_mask; + /** Reserved */ + uint8_t reserved[4084]; + /** Ring buffer contents */ + uint8_t data[0]; +} __attribute__ (( packed )); + +/** VMBus interrupt page */ +struct vmbus_interrupt { + /** Inbound interrupts */ + uint8_t in[ PAGE_SIZE / 2 ]; + /** Outbound interrupts */ + uint8_t out[ PAGE_SIZE / 2 ]; +} __attribute__ (( packed )); + +/** A virtual machine bus */ +struct vmbus { + /** Interrupt page */ + struct vmbus_interrupt *intr; + /** Inbound notifications */ + struct hv_monitor *monitor_in; + /** Outbound notifications */ + struct hv_monitor *monitor_out; + /** Received message buffer */ + const union vmbus_message *message; +}; + +struct vmbus_device; + +/** VMBus channel operations */ +struct vmbus_channel_operations { + /** + * Handle received control packet + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ + int ( * recv_control ) ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len ); + /** + * Handle received data packet + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @v data Data + * @v len Length of data + * @v list List of I/O buffers + * @ret rc Return status code + * + * This function takes ownership of the I/O buffer. It should + * eventually call vmbus_send_completion() to indicate to the + * host that the buffer can be reused. + */ + int ( * recv_data ) ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len, + struct list_head *list ); + /** + * Handle received completion packet + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ + int ( * recv_completion ) ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len ); + /** + * Handle received cancellation packet + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @ret rc Return status code + */ + int ( * recv_cancellation ) ( struct vmbus_device *vmdev, + uint64_t xid ); +}; + +struct vmbus_xfer_pages; + +/** VMBus transfer page set operations */ +struct vmbus_xfer_pages_operations { + /** + * Copy data from transfer page + * + * @v pages Transfer page set + * @v data Data buffer + * @v offset Offset within page set + * @v len Length within page set + * @ret rc Return status code + */ + int ( * copy ) ( struct vmbus_xfer_pages *pages, void *data, + size_t offset, size_t len ); +}; + +/** VMBus transfer page set */ +struct vmbus_xfer_pages { + /** List of all transfer page sets */ + struct list_head list; + /** Page set ID (in protocol byte order) */ + uint16_t pageset; + /** Page set operations */ + struct vmbus_xfer_pages_operations *op; +}; + +/** A VMBus device */ +struct vmbus_device { + /** Generic iPXE device */ + struct device dev; + /** Hyper-V hypervisor */ + struct hv_hypervisor *hv; + + /** Channel ID */ + unsigned int channel; + /** Monitor ID */ + unsigned int monitor; + /** Signal channel + * + * @v vmdev VMBus device + */ + void ( * signal ) ( struct vmbus_device *vmdev ); + + /** Outbound ring buffer length */ + uint32_t out_len; + /** Inbound ring buffer length */ + uint32_t in_len; + /** Outbound ring buffer */ + struct vmbus_ring *out; + /** Inbound ring buffer */ + struct vmbus_ring *in; + /** Ring buffer GPADL ID */ + unsigned int gpadl; + + /** Channel operations */ + struct vmbus_channel_operations *op; + /** Maximum expected data packet length */ + size_t mtu; + /** Packet buffer */ + void *packet; + /** List of transfer page sets */ + struct list_head pages; + + /** Driver */ + struct vmbus_driver *driver; + /** Driver-private data */ + void *priv; +}; + +/** A VMBus device driver */ +struct vmbus_driver { + /** Name */ + const char *name; + /** Device type */ + union uuid type; + /** Probe device + * + * @v vmdev VMBus device + * @ret rc Return status code + */ + int ( * probe ) ( struct vmbus_device *vmdev ); + /** Remove device + * + * @v vmdev VMBus device + */ + void ( * remove ) ( struct vmbus_device *vmdev ); +}; + +/** VMBus device driver table */ +#define VMBUS_DRIVERS __table ( struct vmbus_driver, "vmbus_drivers" ) + +/** Declare a VMBus device driver */ +#define __vmbus_driver __table_entry ( VMBUS_DRIVERS, 01 ) + +/** + * Set VMBus device driver-private data + * + * @v vmdev VMBus device + * @v priv Private data + */ +static inline void vmbus_set_drvdata ( struct vmbus_device *vmdev, void *priv ){ + vmdev->priv = priv; +} + +/** + * Get VMBus device driver-private data + * + * @v vmdev VMBus device + * @ret priv Private data + */ +static inline void * vmbus_get_drvdata ( struct vmbus_device *vmdev ) { + return vmdev->priv; +} + +/** Construct VMBus type */ +#define VMBUS_TYPE( a, b, c, d, e0, e1, e2, e3, e4, e5 ) { \ + .canonical = { \ + cpu_to_le32 ( a ), cpu_to_le16 ( b ), \ + cpu_to_le16 ( c ), cpu_to_be16 ( d ), \ + { e0, e1, e2, e3, e4, e5 } \ + } } + +/** + * Check if data is present in ring buffer + * + * @v vmdev VMBus device + * @v has_data Data is present + */ +static inline __attribute__ (( always_inline )) int +vmbus_has_data ( struct vmbus_device *vmdev ) { + + return ( vmdev->in->prod != vmdev->in->cons ); +} + +/** + * Register transfer page set + * + * @v vmdev VMBus device + * @v pages Transfer page set + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +vmbus_register_pages ( struct vmbus_device *vmdev, + struct vmbus_xfer_pages *pages ) { + + list_add ( &pages->list, &vmdev->pages ); + return 0; +} + +/** + * Unregister transfer page set + * + * @v vmdev VMBus device + * @v pages Transfer page set + */ +static inline __attribute__ (( always_inline )) void +vmbus_unregister_pages ( struct vmbus_device *vmdev, + struct vmbus_xfer_pages *pages ) { + + list_check_contains_entry ( pages, &vmdev->pages, list ); + list_del ( &pages->list ); +} + +extern int vmbus_establish_gpadl ( struct vmbus_device *vmdev, userptr_t data, + size_t len ); +extern int vmbus_gpadl_teardown ( struct vmbus_device *vmdev, + unsigned int gpadl ); +extern int vmbus_open ( struct vmbus_device *vmdev, + struct vmbus_channel_operations *op, + size_t out_len, size_t in_len, size_t mtu ); +extern void vmbus_close ( struct vmbus_device *vmdev ); +extern int vmbus_send_control ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len ); +extern int vmbus_send_data ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len, + struct io_buffer *iobuf ); +extern int vmbus_send_completion ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len ); +extern int vmbus_send_cancellation ( struct vmbus_device *vmdev, uint64_t xid ); +extern int vmbus_poll ( struct vmbus_device *vmdev ); +extern void vmbus_dump_channel ( struct vmbus_device *vmdev ); + +extern int vmbus_probe ( struct hv_hypervisor *hv, struct device *parent ); +extern void vmbus_remove ( struct hv_hypervisor *hv, struct device *parent ); + +#endif /* _IPXE_VMBUS_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/vsprintf.h b/qemu/roms/ipxe/src/include/ipxe/vsprintf.h index c48c97a87..9e6297715 100644 --- a/qemu/roms/ipxe/src/include/ipxe/vsprintf.h +++ b/qemu/roms/ipxe/src/include/ipxe/vsprintf.h @@ -31,7 +31,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/x509.h b/qemu/roms/ipxe/src/include/ipxe/x509.h index 802480e54..0daaf5e59 100644 --- a/qemu/roms/ipxe/src/include/ipxe/x509.h +++ b/qemu/roms/ipxe/src/include/ipxe/x509.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/xen.h b/qemu/roms/ipxe/src/include/ipxe/xen.h index 60aabe03e..eac1145ad 100644 --- a/qemu/roms/ipxe/src/include/ipxe/xen.h +++ b/qemu/roms/ipxe/src/include/ipxe/xen.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Define Xen interface version before including any Xen header files */ #define __XEN_INTERFACE_VERSION__ 0x00040400 diff --git a/qemu/roms/ipxe/src/include/ipxe/xenbus.h b/qemu/roms/ipxe/src/include/ipxe/xenbus.h index ef2b5496f..ec5782eed 100644 --- a/qemu/roms/ipxe/src/include/ipxe/xenbus.h +++ b/qemu/roms/ipxe/src/include/ipxe/xenbus.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/xenevent.h b/qemu/roms/ipxe/src/include/ipxe/xenevent.h index 1dd6a0c0b..f0bd3465e 100644 --- a/qemu/roms/ipxe/src/include/ipxe/xenevent.h +++ b/qemu/roms/ipxe/src/include/ipxe/xenevent.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/xengrant.h b/qemu/roms/ipxe/src/include/ipxe/xengrant.h index f9b3beb21..451a3ceee 100644 --- a/qemu/roms/ipxe/src/include/ipxe/xengrant.h +++ b/qemu/roms/ipxe/src/include/ipxe/xengrant.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/xenmem.h b/qemu/roms/ipxe/src/include/ipxe/xenmem.h index 9b9aeda9c..dcc38d460 100644 --- a/qemu/roms/ipxe/src/include/ipxe/xenmem.h +++ b/qemu/roms/ipxe/src/include/ipxe/xenmem.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/xenstore.h b/qemu/roms/ipxe/src/include/ipxe/xenstore.h index f25f15704..892640755 100644 --- a/qemu/roms/ipxe/src/include/ipxe/xenstore.h +++ b/qemu/roms/ipxe/src/include/ipxe/xenstore.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/ipxe/xenver.h b/qemu/roms/ipxe/src/include/ipxe/xenver.h index 5d678c5a3..b29dfb321 100644 --- a/qemu/roms/ipxe/src/include/ipxe/xenver.h +++ b/qemu/roms/ipxe/src/include/ipxe/xenver.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/ipxe/xfer.h b/qemu/roms/ipxe/src/include/ipxe/xfer.h index 1167e5cba..3a35fa924 100644 --- a/qemu/roms/ipxe/src/include/ipxe/xfer.h +++ b/qemu/roms/ipxe/src/include/ipxe/xfer.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -103,5 +103,7 @@ extern int xfer_vprintf ( struct interface *intf, extern int __attribute__ (( format ( printf, 2, 3 ) )) xfer_printf ( struct interface *intf, const char *format, ... ); extern int xfer_seek ( struct interface *intf, off_t offset ); +extern int xfer_check_order ( struct xfer_metadata *meta, size_t *pos, + size_t len ); #endif /* _IPXE_XFER_H */ diff --git a/qemu/roms/ipxe/src/include/ipxe/xferbuf.h b/qemu/roms/ipxe/src/include/ipxe/xferbuf.h index 2ca871e59..cb0b1a0e8 100644 --- a/qemu/roms/ipxe/src/include/ipxe/xferbuf.h +++ b/qemu/roms/ipxe/src/include/ipxe/xferbuf.h @@ -7,10 +7,12 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include +#include #include /** A data transfer buffer */ @@ -21,11 +23,83 @@ struct xfer_buffer { size_t len; /** Current offset within data */ size_t pos; + /** Data transfer buffer operations */ + struct xfer_buffer_operations *op; }; -extern void xferbuf_done ( struct xfer_buffer *xferbuf ); +/** Data transfer buffer operations */ +struct xfer_buffer_operations { + /** Reallocate data buffer + * + * @v xferbuf Data transfer buffer + * @v len New length (or zero to free buffer) + * @ret rc Return status code + */ + int ( * realloc ) ( struct xfer_buffer *xferbuf, size_t len ); + /** Write data to buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to write + * @v len Length of data + * + * This call is simply a wrapper for the appropriate + * memcpy()-like operation: the caller is responsible for + * ensuring that the write does not exceed the buffer length. + */ + void ( * write ) ( struct xfer_buffer *xferbuf, size_t offset, + const void *data, size_t len ); + /** Read data from buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to read + * @v len Length of data + * + * This call is simply a wrapper for the appropriate + * memcpy()-like operation: the caller is responsible for + * ensuring that the read does not exceed the buffer length. + */ + void ( * read ) ( struct xfer_buffer *xferbuf, size_t offset, + void *data, size_t len ); +}; + +extern struct xfer_buffer_operations xferbuf_malloc_operations; +extern struct xfer_buffer_operations xferbuf_umalloc_operations; + +/** + * Initialise malloc()-based data transfer buffer + * + * @v xferbuf Data transfer buffer + */ +static inline __attribute__ (( always_inline )) void +xferbuf_malloc_init ( struct xfer_buffer *xferbuf ) { + xferbuf->op = &xferbuf_malloc_operations; +} + +/** + * Initialise umalloc()-based data transfer buffer + * + * @v xferbuf Data transfer buffer + * @v data User pointer + */ +static inline __attribute__ (( always_inline )) void +xferbuf_umalloc_init ( struct xfer_buffer *xferbuf, userptr_t *data ) { + xferbuf->data = data; + xferbuf->op = &xferbuf_umalloc_operations; +} + +extern void xferbuf_free ( struct xfer_buffer *xferbuf ); +extern int xferbuf_write ( struct xfer_buffer *xferbuf, size_t offset, + const void *data, size_t len ); +extern int xferbuf_read ( struct xfer_buffer *xferbuf, size_t offset, + void *data, size_t len ); extern int xferbuf_deliver ( struct xfer_buffer *xferbuf, struct io_buffer *iobuf, struct xfer_metadata *meta ); +extern struct xfer_buffer * xfer_buffer ( struct interface *intf ); +#define xfer_buffer_TYPE( object_type ) \ + typeof ( struct xfer_buffer * ( object_type ) ) + #endif /* _IPXE_XFERBUF_H */ diff --git a/qemu/roms/ipxe/src/include/libgen.h b/qemu/roms/ipxe/src/include/libgen.h index 7e94881a9..ae0861270 100644 --- a/qemu/roms/ipxe/src/include/libgen.h +++ b/qemu/roms/ipxe/src/include/libgen.h @@ -1,7 +1,7 @@ #ifndef _LIBGEN_H #define _LIBGEN_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern char * basename ( char *path ); extern char * dirname ( char *path ); diff --git a/qemu/roms/ipxe/src/include/little_bswap.h b/qemu/roms/ipxe/src/include/little_bswap.h deleted file mode 100644 index 92dd26ba1..000000000 --- a/qemu/roms/ipxe/src/include/little_bswap.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef ETHERBOOT_LITTLE_BSWAP_H -#define ETHERBOOT_LITTLE_BSWAP_H - -FILE_LICENCE ( GPL2_OR_LATER ); - -#define htonll(x) __bswap_64(x) -#define ntohll(x) __bswap_64(x) -#define ntohl(x) __bswap_32(x) -#define htonl(x) __bswap_32(x) -#define ntohs(x) __bswap_16(x) -#define htons(x) __bswap_16(x) -#define cpu_to_le64(x) (x) -#define cpu_to_le32(x) (x) -#define cpu_to_le16(x) (x) -#define cpu_to_be64(x) __bswap_64(x) -#define cpu_to_be32(x) __bswap_32(x) -#define cpu_to_be16(x) __bswap_16(x) -#define le64_to_cpu(x) (x) -#define le32_to_cpu(x) (x) -#define le16_to_cpu(x) (x) -#define be64_to_cpu(x) __bswap_64(x) -#define be32_to_cpu(x) __bswap_32(x) -#define be16_to_cpu(x) __bswap_16(x) -#define cpu_to_le64s(x) do {} while (0) -#define cpu_to_le32s(x) do {} while (0) -#define cpu_to_le16s(x) do {} while (0) -#define cpu_to_be64s(x) __bswap_64s(x) -#define cpu_to_be32s(x) __bswap_32s(x) -#define cpu_to_be16s(x) __bswap_16s(x) -#define le64_to_cpus(x) do {} while (0) -#define le32_to_cpus(x) do {} while (0) -#define le16_to_cpus(x) do {} while (0) -#define be64_to_cpus(x) __bswap_64s(x) -#define be32_to_cpus(x) __bswap_32s(x) -#define be16_to_cpus(x) __bswap_16s(x) - -#endif /* ETHERBOOT_LITTLE_BSWAP_H */ diff --git a/qemu/roms/ipxe/src/include/nic.h b/qemu/roms/ipxe/src/include/nic.h index 9aaede8a7..4c91f57a6 100644 --- a/qemu/roms/ipxe/src/include/nic.h +++ b/qemu/roms/ipxe/src/include/nic.h @@ -1,8 +1,18 @@ - /* +/* * 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) any later version. + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. */ FILE_LICENCE ( GPL2_OR_LATER ); @@ -266,6 +276,7 @@ static inline void * legacy_isa_get_drvdata ( void *hwdev ) { _name ## _isa_legacy_remove ( struct isa_device *isa ) { \ return legacy_remove ( isa, legacy_isa_get_drvdata, \ _name ## _disable ); \ - } + } \ + PROVIDE_REQUIRING_SYMBOL() #endif /* NIC_H */ diff --git a/qemu/roms/ipxe/src/include/readline/readline.h b/qemu/roms/ipxe/src/include/readline/readline.h index 0449a3f98..afafbbdf5 100644 --- a/qemu/roms/ipxe/src/include/readline/readline.h +++ b/qemu/roms/ipxe/src/include/readline/readline.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** A readline history entry */ struct readline_history_entry { diff --git a/qemu/roms/ipxe/src/include/stdarg.h b/qemu/roms/ipxe/src/include/stdarg.h index f317238a9..89e94ce22 100644 --- a/qemu/roms/ipxe/src/include/stdarg.h +++ b/qemu/roms/ipxe/src/include/stdarg.h @@ -1,7 +1,7 @@ #ifndef _STDARG_H #define _STDARG_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); typedef __builtin_va_list va_list; #define va_start( ap, last ) __builtin_va_start ( ap, last ) diff --git a/qemu/roms/ipxe/src/include/stddef.h b/qemu/roms/ipxe/src/include/stddef.h index bf792771f..3c056294f 100644 --- a/qemu/roms/ipxe/src/include/stddef.h +++ b/qemu/roms/ipxe/src/include/stddef.h @@ -1,25 +1,43 @@ #ifndef STDDEF_H #define STDDEF_H -FILE_LICENCE ( GPL2_ONLY ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -/* for size_t */ #include +/** EFI headers also define NULL */ #undef NULL -#define NULL ((void *)0) -#undef offsetof -#if ( defined ( __GNUC__ ) && ( __GNUC__ > 3 ) ) -#define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE, MEMBER) +/** Null pointer */ +#define NULL ( ( void * ) 0 ) + +/** + * Get offset of a field within a structure + * + * @v type Structure type + * @v field Field within structure + * @ret offset Offset within structure + */ +#if defined ( __GNUC__ ) && ( __GNUC__ > 3 ) +#define offsetof( type, field ) __builtin_offsetof ( type, field ) #else -#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#define offsetof( type, field ) ( ( size_t ) &( ( ( type * ) NULL )->field ) ) #endif -#undef container_of -#define container_of(ptr, type, member) ({ \ - const typeof( ((type *)0)->member ) *__mptr = (ptr); \ - (type *)( (char *)__mptr - offsetof(type,member) );}) +/** + * Get containing structure + * + * @v ptr Pointer to contained field + * @v type Containing structure type + * @v field Field within containing structure + * @ret container Pointer to containing structure + */ +#define container_of( ptr, type, field ) ( { \ + type *__container; \ + const typeof ( __container->field ) *__field = (ptr); \ + __container = ( ( ( void * ) __field ) - \ + offsetof ( type, field ) ); \ + __container; } ) /* __WCHAR_TYPE__ is defined by gcc and will change if -fshort-wchar is used */ #ifndef __WCHAR_TYPE__ diff --git a/qemu/roms/ipxe/src/include/stdint.h b/qemu/roms/ipxe/src/include/stdint.h index 8cc9b84a5..0a239a517 100644 --- a/qemu/roms/ipxe/src/include/stdint.h +++ b/qemu/roms/ipxe/src/include/stdint.h @@ -1,7 +1,7 @@ #ifndef _STDINT_H #define _STDINT_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* * This is a standard predefined macro on all gcc's I've seen. It's diff --git a/qemu/roms/ipxe/src/include/stdio.h b/qemu/roms/ipxe/src/include/stdio.h index 91840af5b..a618482ce 100644 --- a/qemu/roms/ipxe/src/include/stdio.h +++ b/qemu/roms/ipxe/src/include/stdio.h @@ -1,7 +1,7 @@ #ifndef _STDIO_H #define _STDIO_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/stdlib.h b/qemu/roms/ipxe/src/include/stdlib.h index 2951522b8..d7748a07e 100644 --- a/qemu/roms/ipxe/src/include/stdlib.h +++ b/qemu/roms/ipxe/src/include/stdlib.h @@ -1,7 +1,7 @@ #ifndef STDLIB_H #define STDLIB_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -13,31 +13,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); **************************************************************************** */ -static inline int strtoul_base ( const char **pp, int base ) -{ - const char *p = *pp; - - if ( base == 0 ) { - base = 10; - if ( *p == '0' ) { - p++; - base = 8; - if ( ( *p | 0x20 ) == 'x' ) { - p++; - base = 16; - } - } - } - - *pp = p; - - return base; -} - -extern unsigned int strtoul_charval ( unsigned int charval ); -extern unsigned long strtoul ( const char *p, char **endp, int base ); -extern unsigned long long strtoull ( const char *p, char **endp, int base ); - +extern unsigned long strtoul ( const char *string, char **endp, int base ); +extern unsigned long long strtoull ( const char *string, char **endp, + int base ); /***************************************************************************** * diff --git a/qemu/roms/ipxe/src/include/string.h b/qemu/roms/ipxe/src/include/string.h index 3482e1b22..0fab6c74b 100644 --- a/qemu/roms/ipxe/src/include/string.h +++ b/qemu/roms/ipxe/src/include/string.h @@ -1,52 +1,53 @@ -/* - * Copyright (C) 1991, 1992 Linus Torvalds - * Copyright (C) 2004 Tobias Lorenz +#ifndef _STRING_H +#define _STRING_H + +/** @file * - * string handling functions - * based on linux/include/linux/ctype.h - * and linux/include/linux/string.h + * String functions * - * 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. */ -FILE_LICENCE ( GPL2_ONLY ); - -#ifndef ETHERBOOT_STRING_H -#define ETHERBOOT_STRING_H +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -int __pure strnicmp(const char *s1, const char *s2, size_t len) __nonnull; -char * strcpy(char * dest,const char *src) __nonnull; -char * strncpy(char * dest,const char *src,size_t count) __nonnull; -char * strcat(char * dest, const char * src) __nonnull; -char * strncat(char *dest, const char *src, size_t count) __nonnull; -int __pure strcmp(const char * cs,const char * ct) __nonnull; -int __pure strncmp(const char * cs,const char * ct, - size_t count) __nonnull; -char * __pure strchr(const char * s, int c) __nonnull; -char * __pure strrchr(const char * s, int c) __nonnull; -size_t __pure strlen(const char * s) __nonnull; -size_t __pure strnlen(const char * s, size_t count) __nonnull; -size_t __pure strspn(const char *s, const char *accept) __nonnull; -size_t __pure strcspn(const char *s, const char *reject) __nonnull; -char * __pure strpbrk(const char * cs,const char * ct) __nonnull; -char * strtok(char * s,const char * ct) __nonnull; -char * strsep(char **s, const char *ct) __nonnull; -void * memset(void * s,int c,size_t count) __nonnull; +/* Architecture-specific code is expected to provide these functions, + * but may instead explicitly choose to use the generic versions. + */ +void * memset ( void *dest, int character, size_t len ) __nonnull; void * memcpy ( void *dest, const void *src, size_t len ) __nonnull; -void * memmove(void * dest,const void *src,size_t count) __nonnull; -int __pure memcmp(const void * cs,const void * ct, - size_t count) __nonnull; -void * __pure memscan(const void * addr, int c, size_t size) __nonnull; -char * __pure strstr(const char * s1,const char * s2) __nonnull; -void * __pure memchr(const void *s, int c, size_t n) __nonnull; -char * __malloc strdup(const char *s) __nonnull; -char * __malloc strndup(const char *s, size_t n) __nonnull; +void * memmove ( void *dest, const void *src, size_t len ) __nonnull; +extern void * generic_memset ( void *dest, int character, + size_t len ) __nonnull; +extern void * generic_memcpy ( void *dest, const void *src, + size_t len ) __nonnull; +extern void * generic_memmove ( void *dest, const void *src, + size_t len ) __nonnull; + +extern int __pure memcmp ( const void *first, const void *second, + size_t len ) __nonnull; +extern void * __pure memchr ( const void *src, int character, + size_t len ) __nonnull; +extern void * memswap ( void *dest, void *src, size_t len ) __nonnull; +extern int __pure strcmp ( const char *first, const char *second ) __nonnull; +extern int __pure strncmp ( const char *first, const char *second, + size_t max ) __nonnull; +extern size_t __pure strlen ( const char *src ) __nonnull; +extern size_t __pure strnlen ( const char *src, size_t max ) __nonnull; +extern char * __pure strchr ( const char *src, int character ) __nonnull; +extern char * __pure strrchr ( const char *src, int character ) __nonnull; +extern char * __pure strstr ( const char *haystack, + const char *needle ) __nonnull; +extern char * strcpy ( char *dest, const char *src ) __nonnull; +extern char * strncpy ( char *dest, const char *src, size_t max ) __nonnull; +extern char * strcat ( char *dest, const char *src ) __nonnull; +extern char * __malloc strdup ( const char *src ) __nonnull; +extern char * __malloc strndup ( const char *src, size_t max ) __nonnull; +extern char * __pure strpbrk ( const char *string, + const char *delim ) __nonnull; +extern char * strsep ( char **string, const char *delim ) __nonnull; -extern const char * __pure strerror ( int errno ); +extern char * __pure strerror ( int errno ); -#endif /* ETHERBOOT_STRING */ +#endif /* _STRING_H */ diff --git a/qemu/roms/ipxe/src/include/strings.h b/qemu/roms/ipxe/src/include/strings.h index 6912a1e45..fab26dc28 100644 --- a/qemu/roms/ipxe/src/include/strings.h +++ b/qemu/roms/ipxe/src/include/strings.h @@ -1,12 +1,71 @@ #ifndef _STRINGS_H #define _STRINGS_H -FILE_LICENCE ( GPL2_OR_LATER ); +/** @file + * + * String functions + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -#include #include #include +/** + * Find first (i.e. least significant) set bit + * + * @v x Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int +__constant_ffsll ( unsigned long long x ) { + int r = 0; + + if ( ! ( x & 0x00000000ffffffffULL ) ) { + x >>= 32; + r += 32; + } + if ( ! ( x & 0x0000ffffUL ) ) { + x >>= 16; + r += 16; + } + if ( ! ( x & 0x00ff ) ) { + x >>= 8; + r += 8; + } + if ( ! ( x & 0x0f ) ) { + x >>= 4; + r += 4; + } + if ( ! ( x & 0x3 ) ) { + x >>= 2; + r += 2; + } + if ( ! ( x & 0x1 ) ) { + x >>= 1; + r += 1; + } + return ( x ? ( r + 1 ) : 0 ); +} + +/** + * Find first (i.e. least significant) set bit + * + * @v x Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int +__constant_ffsl ( unsigned long x ) { + return __constant_ffsll ( x ); +} + +/** + * Find last (i.e. most significant) set bit + * + * @v x Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ static inline __attribute__ (( always_inline )) int __constant_flsll ( unsigned long long x ) { int r = 0; @@ -35,38 +94,100 @@ __constant_flsll ( unsigned long long x ) { x >>= 1; r += 1; } - if ( x & 0x1 ) { - r += 1; - } - return r; + return ( x ? ( r + 1 ) : 0 ); } +/** + * Find last (i.e. most significant) set bit + * + * @v x Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ static inline __attribute__ (( always_inline )) int __constant_flsl ( unsigned long x ) { return __constant_flsll ( x ); } +int __ffsll ( long long x ); +int __ffsl ( long x ); int __flsll ( long long x ); int __flsl ( long x ); +/** + * Find first (i.e. least significant) set bit + * + * @v x Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +#define ffsll( x ) \ + ( __builtin_constant_p ( x ) ? __constant_ffsll ( x ) : __ffsll ( x ) ) + +/** + * Find first (i.e. least significant) set bit + * + * @v x Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +#define ffsl( x ) \ + ( __builtin_constant_p ( x ) ? __constant_ffsl ( x ) : __ffsl ( x ) ) + +/** + * Find first (i.e. least significant) set bit + * + * @v x Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +#define ffs( x ) ffsl ( x ) + +/** + * Find last (i.e. most significant) set bit + * + * @v x Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ #define flsll( x ) \ ( __builtin_constant_p ( x ) ? __constant_flsll ( x ) : __flsll ( x ) ) +/** + * Find last (i.e. most significant) set bit + * + * @v x Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ #define flsl( x ) \ ( __builtin_constant_p ( x ) ? __constant_flsl ( x ) : __flsl ( x ) ) +/** + * Find last (i.e. most significant) set bit + * + * @v x Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ #define fls( x ) flsl ( x ) -extern int strcasecmp ( const char *s1, const char *s2 ); - +/** + * Copy memory + * + * @v src Source + * @v dest Destination + * @v len Length + */ static inline __attribute__ (( always_inline )) void -bcopy ( const void *src, void *dest, size_t n ) { - memmove ( dest, src, n ); +bcopy ( const void *src, void *dest, size_t len ) { + memmove ( dest, src, len ); } +/** + * Zero memory + * + * @v dest Destination + * @v len Length + */ static inline __attribute__ (( always_inline )) void -bzero ( void *s, size_t n ) { - memset ( s, 0, n ); +bzero ( void *dest, size_t len ) { + memset ( dest, 0, len ); } +int __pure strcasecmp ( const char *first, const char *second ) __nonnull; + #endif /* _STRINGS_H */ diff --git a/qemu/roms/ipxe/src/include/sys/time.h b/qemu/roms/ipxe/src/include/sys/time.h index 2647d3588..6e2a24447 100644 --- a/qemu/roms/ipxe/src/include/sys/time.h +++ b/qemu/roms/ipxe/src/include/sys/time.h @@ -6,7 +6,7 @@ * Date and time */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/syslog.h b/qemu/roms/ipxe/src/include/syslog.h index 93f32f867..748a4faec 100644 --- a/qemu/roms/ipxe/src/include/syslog.h +++ b/qemu/roms/ipxe/src/include/syslog.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/time.h b/qemu/roms/ipxe/src/include/time.h index 452a544bb..462ac6999 100644 --- a/qemu/roms/ipxe/src/include/time.h +++ b/qemu/roms/ipxe/src/include/time.h @@ -6,7 +6,7 @@ * Date and time */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/unistd.h b/qemu/roms/ipxe/src/include/unistd.h index 3a50a2521..d09e1ae30 100644 --- a/qemu/roms/ipxe/src/include/unistd.h +++ b/qemu/roms/ipxe/src/include/unistd.h @@ -1,7 +1,7 @@ #ifndef _UNISTD_H #define _UNISTD_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/include/usr/autoboot.h b/qemu/roms/ipxe/src/include/usr/autoboot.h index bc51aae79..4db226b9c 100644 --- a/qemu/roms/ipxe/src/include/usr/autoboot.h +++ b/qemu/roms/ipxe/src/include/usr/autoboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include @@ -35,7 +35,7 @@ extern int uriboot ( struct uri *filename, struct uri *root_path, int drive, extern struct uri * fetch_next_server_and_filename ( struct settings *settings ); extern int netboot ( struct net_device *netdev ); -extern void ipxe ( struct net_device *netdev ); +extern int ipxe ( struct net_device *netdev ); extern int pxe_menu_boot ( struct net_device *netdev ); diff --git a/qemu/roms/ipxe/src/include/usr/dhcpmgmt.h b/qemu/roms/ipxe/src/include/usr/dhcpmgmt.h index af1eceb17..ed669eb9d 100644 --- a/qemu/roms/ipxe/src/include/usr/dhcpmgmt.h +++ b/qemu/roms/ipxe/src/include/usr/dhcpmgmt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct net_device; diff --git a/qemu/roms/ipxe/src/include/usr/fcmgmt.h b/qemu/roms/ipxe/src/include/usr/fcmgmt.h index 9441cefb4..eb568fd20 100644 --- a/qemu/roms/ipxe/src/include/usr/fcmgmt.h +++ b/qemu/roms/ipxe/src/include/usr/fcmgmt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct fc_port; struct fc_peer; diff --git a/qemu/roms/ipxe/src/include/usr/ifmgmt.h b/qemu/roms/ipxe/src/include/usr/ifmgmt.h index db77f1f1b..5c386327b 100644 --- a/qemu/roms/ipxe/src/include/usr/ifmgmt.h +++ b/qemu/roms/ipxe/src/include/usr/ifmgmt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct net_device; struct net_device_configurator; diff --git a/qemu/roms/ipxe/src/include/usr/imgmgmt.h b/qemu/roms/ipxe/src/include/usr/imgmgmt.h index 5e25c562b..806df0bfb 100644 --- a/qemu/roms/ipxe/src/include/usr/imgmgmt.h +++ b/qemu/roms/ipxe/src/include/usr/imgmgmt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/usr/imgtrust.h b/qemu/roms/ipxe/src/include/usr/imgtrust.h index f47105af0..414e07a80 100644 --- a/qemu/roms/ipxe/src/include/usr/imgtrust.h +++ b/qemu/roms/ipxe/src/include/usr/imgtrust.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/usr/ipstat.h b/qemu/roms/ipxe/src/include/usr/ipstat.h index 5ff8b40c3..803254bcb 100644 --- a/qemu/roms/ipxe/src/include/usr/ipstat.h +++ b/qemu/roms/ipxe/src/include/usr/ipstat.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern void ipstat ( void ); diff --git a/qemu/roms/ipxe/src/include/usr/lotest.h b/qemu/roms/ipxe/src/include/usr/lotest.h index aa4bbac4d..ce0fe5eda 100644 --- a/qemu/roms/ipxe/src/include/usr/lotest.h +++ b/qemu/roms/ipxe/src/include/usr/lotest.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern int loopback_test ( struct net_device *sender, struct net_device *receiver, size_t mtu ); diff --git a/qemu/roms/ipxe/src/include/usr/neighmgmt.h b/qemu/roms/ipxe/src/include/usr/neighmgmt.h index 3c2b704af..06f03716e 100644 --- a/qemu/roms/ipxe/src/include/usr/neighmgmt.h +++ b/qemu/roms/ipxe/src/include/usr/neighmgmt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern void nstat ( void ); diff --git a/qemu/roms/ipxe/src/include/usr/pingmgmt.h b/qemu/roms/ipxe/src/include/usr/pingmgmt.h index d4c2d6cd5..c7a8434be 100644 --- a/qemu/roms/ipxe/src/include/usr/pingmgmt.h +++ b/qemu/roms/ipxe/src/include/usr/pingmgmt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/usr/profstat.h b/qemu/roms/ipxe/src/include/usr/profstat.h index 06ea251a0..b7812ca7f 100644 --- a/qemu/roms/ipxe/src/include/usr/profstat.h +++ b/qemu/roms/ipxe/src/include/usr/profstat.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern void profstat ( void ); diff --git a/qemu/roms/ipxe/src/include/usr/prompt.h b/qemu/roms/ipxe/src/include/usr/prompt.h index 57e43d2dc..8d3eeee3c 100644 --- a/qemu/roms/ipxe/src/include/usr/prompt.h +++ b/qemu/roms/ipxe/src/include/usr/prompt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern int prompt ( const char *text, unsigned long timeout, int key ); diff --git a/qemu/roms/ipxe/src/include/usr/route.h b/qemu/roms/ipxe/src/include/usr/route.h index b914f4b84..7ec4a3509 100644 --- a/qemu/roms/ipxe/src/include/usr/route.h +++ b/qemu/roms/ipxe/src/include/usr/route.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/include/usr/sync.h b/qemu/roms/ipxe/src/include/usr/sync.h index 0047d4ed9..b6f12ad6e 100644 --- a/qemu/roms/ipxe/src/include/usr/sync.h +++ b/qemu/roms/ipxe/src/include/usr/sync.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern int sync ( unsigned long timeout ); diff --git a/qemu/roms/ipxe/src/arch/x86/include/valgrind/memcheck.h b/qemu/roms/ipxe/src/include/valgrind/memcheck.h similarity index 100% rename from qemu/roms/ipxe/src/arch/x86/include/valgrind/memcheck.h rename to qemu/roms/ipxe/src/include/valgrind/memcheck.h diff --git a/qemu/roms/ipxe/src/arch/x86/include/valgrind/valgrind.h b/qemu/roms/ipxe/src/include/valgrind/valgrind.h similarity index 100% rename from qemu/roms/ipxe/src/arch/x86/include/valgrind/valgrind.h rename to qemu/roms/ipxe/src/include/valgrind/valgrind.h diff --git a/qemu/roms/ipxe/src/include/wchar.h b/qemu/roms/ipxe/src/include/wchar.h index ba349aae8..d054b8d5b 100644 --- a/qemu/roms/ipxe/src/include/wchar.h +++ b/qemu/roms/ipxe/src/include/wchar.h @@ -1,7 +1,7 @@ #ifndef WCHAR_H #define WCHAR_H -FILE_LICENCE ( GPL2_ONLY ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/qemu/roms/ipxe/src/interface/bofm/bofm.c b/qemu/roms/ipxe/src/interface/bofm/bofm.c index b0e92b27c..545088dc6 100644 --- a/qemu/roms/ipxe/src/interface/bofm/bofm.c +++ b/qemu/roms/ipxe/src/interface/bofm/bofm.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/interface/efi/efi_autoboot.c b/qemu/roms/ipxe/src/interface/efi/efi_autoboot.c index ab0f36541..a9e807e23 100644 --- a/qemu/roms/ipxe/src/interface/efi/efi_autoboot.c +++ b/qemu/roms/ipxe/src/interface/efi/efi_autoboot.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/interface/efi/efi_bofm.c b/qemu/roms/ipxe/src/interface/efi/efi_bofm.c index bdb705196..ea0e15f7f 100644 --- a/qemu/roms/ipxe/src/interface/efi/efi_bofm.c +++ b/qemu/roms/ipxe/src/interface/efi/efi_bofm.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/interface/efi/efi_debug.c b/qemu/roms/ipxe/src/interface/efi/efi_debug.c index d23960140..473803951 100644 --- a/qemu/roms/ipxe/src/interface/efi/efi_debug.c +++ b/qemu/roms/ipxe/src/interface/efi/efi_debug.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file @@ -326,7 +330,7 @@ const char * efi_devpath_text ( EFI_DEVICE_PATH_PROTOCOL *path ) { max_len = ( ( sizeof ( text ) - 1 /* NUL */ ) / 2 /* "xx" */ ); if ( len > max_len ) len = max_len; - base16_encode ( start, len, text ); + base16_encode ( start, len, text, sizeof ( text ) ); return text; } diff --git a/qemu/roms/ipxe/src/interface/efi/efi_file.c b/qemu/roms/ipxe/src/interface/efi/efi_file.c index 2ef3c5734..3715b70bf 100644 --- a/qemu/roms/ipxe/src/interface/efi/efi_file.c +++ b/qemu/roms/ipxe/src/interface/efi/efi_file.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/interface/efi/efi_guid.c b/qemu/roms/ipxe/src/interface/efi/efi_guid.c index 52ba58ae4..ab1c91e9f 100644 --- a/qemu/roms/ipxe/src/interface/efi/efi_guid.c +++ b/qemu/roms/ipxe/src/interface/efi/efi_guid.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/interface/efi/efi_hii.c b/qemu/roms/ipxe/src/interface/efi/efi_hii.c index 834060b54..0ea970e67 100644 --- a/qemu/roms/ipxe/src/interface/efi/efi_hii.c +++ b/qemu/roms/ipxe/src/interface/efi/efi_hii.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/interface/efi/efi_pci.c b/qemu/roms/ipxe/src/interface/efi/efi_pci.c index 86c781d82..97ea72bb9 100644 --- a/qemu/roms/ipxe/src/interface/efi/efi_pci.c +++ b/qemu/roms/ipxe/src/interface/efi/efi_pci.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/interface/efi/efi_reboot.c b/qemu/roms/ipxe/src/interface/efi/efi_reboot.c index 96638c48e..35919221e 100644 --- a/qemu/roms/ipxe/src/interface/efi/efi_reboot.c +++ b/qemu/roms/ipxe/src/interface/efi/efi_reboot.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/interface/efi/efi_snp.c b/qemu/roms/ipxe/src/interface/efi/efi_snp.c index 67fba342e..3dfcc5e16 100644 --- a/qemu/roms/ipxe/src/interface/efi/efi_snp.c +++ b/qemu/roms/ipxe/src/interface/efi/efi_snp.c @@ -32,8 +32,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include +#include /** List of SNP devices */ static LIST_HEAD ( efi_snp_devices ); @@ -41,6 +43,39 @@ static LIST_HEAD ( efi_snp_devices ); /** Network devices are currently claimed for use by iPXE */ static int efi_snp_claimed; +/* Downgrade user experience if configured to do so + * + * The default UEFI user experience for network boot is somewhat + * excremental: only TFTP is available as a download protocol, and if + * anything goes wrong the user will be shown just a dot on an + * otherwise blank screen. (Some programmer was clearly determined to + * win a bet that they could outshine Apple at producing uninformative + * error messages.) + * + * For comparison, the default iPXE user experience provides the + * option to use protocols designed more recently than 1980 (such as + * HTTP and iSCSI), and if anything goes wrong the the user will be + * shown one of over 1200 different error messages, complete with a + * link to a wiki page describing that specific error. + * + * We default to upgrading the user experience to match that available + * in a "legacy" BIOS environment, by installing our own instance of + * EFI_LOAD_FILE_PROTOCOL. + * + * Note that unfortunately we can't sensibly provide the choice of + * both options to the user in the same build, because the UEFI boot + * menu ignores the multitude of ways in which a network device handle + * can be described and opaquely labels both menu entries as just "EFI + * Network". + */ +#ifdef EFI_DOWNGRADE_UX +static EFI_GUID dummy_load_file_protocol_guid = { + 0x6f6c7323, 0x2077, 0x7523, + { 0x6e, 0x68, 0x65, 0x6c, 0x70, 0x66, 0x75, 0x6c } +}; +#define efi_load_file_protocol_guid dummy_load_file_protocol_guid +#endif + /** * Set EFI SNP mode state * @@ -97,29 +132,44 @@ static void efi_snp_set_mode ( struct efi_snp_device *snpdev ) { mode->MediaPresent = ( netdev_link_ok ( netdev ) ? TRUE : FALSE ); } +/** + * Flush transmit ring and receive queue + * + * @v snpdev SNP device + */ +static void efi_snp_flush ( struct efi_snp_device *snpdev ) { + struct io_buffer *iobuf; + struct io_buffer *tmp; + + /* Reset transmit completion ring */ + snpdev->tx_prod = 0; + snpdev->tx_cons = 0; + + /* Discard any queued receive buffers */ + list_for_each_entry_safe ( iobuf, tmp, &snpdev->rx, list ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); + } +} + /** * Poll net device and count received packets * * @v snpdev SNP device */ static void efi_snp_poll ( struct efi_snp_device *snpdev ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct io_buffer *iobuf; - unsigned int before = 0; - unsigned int after = 0; - unsigned int arrived; - /* We have to report packet arrivals, and this is the easiest - * way to fake it. - */ - list_for_each_entry ( iobuf, &snpdev->netdev->rx_queue, list ) - before++; + /* Poll network device */ netdev_poll ( snpdev->netdev ); - list_for_each_entry ( iobuf, &snpdev->netdev->rx_queue, list ) - after++; - arrived = ( after - before ); - snpdev->rx_count_interrupts += arrived; - snpdev->rx_count_events += arrived; + /* Retrieve any received packets */ + while ( ( iobuf = netdev_rx_dequeue ( snpdev->netdev ) ) ) { + list_add_tail ( &iobuf->list, &snpdev->rx ); + snpdev->interrupts |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; + bs->SignalEvent ( &snpdev->snp.WaitForPacket ); + } } /** @@ -221,6 +271,7 @@ efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) { netdev_close ( snpdev->netdev ); efi_snp_set_state ( snpdev ); + efi_snp_flush ( snpdev ); if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) { DBGC ( snpdev, "SNPDEV %p could not reopen %s: %s\n", @@ -251,6 +302,7 @@ efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { netdev_close ( snpdev->netdev ); efi_snp_set_state ( snpdev ); + efi_snp_flush ( snpdev ); return 0; } @@ -446,20 +498,22 @@ efi_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN read, * * @v snp SNP interface * @v interrupts Interrupt status, or NULL - * @v txbufs Recycled transmit buffer address, or NULL + * @v txbuf Recycled transmit buffer address, or NULL * @ret efirc EFI status code */ static EFI_STATUS EFIAPI efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, - UINT32 *interrupts, VOID **txbufs ) { + UINT32 *interrupts, VOID **txbuf ) { struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); DBGC2 ( snpdev, "SNPDEV %p GET_STATUS", snpdev ); /* Fail if net device is currently claimed for use by iPXE */ - if ( efi_snp_claimed ) + if ( efi_snp_claimed ) { + DBGC2 ( snpdev, "\n" ); return EFI_NOT_READY; + } /* Poll the network device */ efi_snp_poll ( snpdev ); @@ -468,47 +522,19 @@ efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, * to detect TX completions. */ if ( interrupts ) { - *interrupts = 0; - /* Report TX completions once queue is empty; this - * avoids having to add hooks in the net device layer. - */ - if ( snpdev->tx_count_interrupts && - list_empty ( &snpdev->netdev->tx_queue ) ) { - *interrupts |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; - snpdev->tx_count_interrupts--; - } - /* Report RX */ - if ( snpdev->rx_count_interrupts ) { - *interrupts |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; - snpdev->rx_count_interrupts--; - } + *interrupts = snpdev->interrupts; DBGC2 ( snpdev, " INTS:%02x", *interrupts ); + snpdev->interrupts = 0; } - /* TX completions. It would be possible to design a more - * idiotic scheme for this, but it would be a challenge. - * According to the UEFI header file, txbufs will be filled in - * with a list of "recycled transmit buffers" (i.e. completed - * TX buffers). Observant readers may care to note that - * *txbufs is a void pointer. Precisely how a list of - * completed transmit buffers is meant to be represented as an - * array of voids is left as an exercise for the reader. - * - * The only users of this interface (MnpDxe/MnpIo.c and - * PxeBcDxe/Bc.c within the EFI dev kit) both just poll until - * seeing a non-NULL result return in txbufs. This is valid - * provided that they do not ever attempt to transmit more - * than one packet concurrently (and that TX never times out). - */ - if ( txbufs ) { - if ( snpdev->tx_count_txbufs && - list_empty ( &snpdev->netdev->tx_queue ) ) { - *txbufs = "Which idiot designed this API?"; - snpdev->tx_count_txbufs--; + /* TX completions */ + if ( txbuf ) { + if ( snpdev->tx_prod != snpdev->tx_cons ) { + *txbuf = snpdev->tx[snpdev->tx_cons++ % EFI_SNP_NUM_TX]; } else { - *txbufs = NULL; + *txbuf = NULL; } - DBGC2 ( snpdev, " TX:%s", ( *txbufs ? "some" : "none" ) ); + DBGC2 ( snpdev, " TX:%p", *txbuf ); } DBGC2 ( snpdev, "\n" ); @@ -537,6 +563,7 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; struct io_buffer *iobuf; size_t payload_len; + unsigned int tx_fill; int rc; DBGC2 ( snpdev, "SNPDEV %p TRANSMIT %p+%lx", snpdev, data, @@ -624,12 +651,27 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, goto err_tx; } - /* Record transmission as outstanding */ - snpdev->tx_count_interrupts++; - snpdev->tx_count_txbufs++; + /* Record in transmit completion ring. If we run out of + * space, report the failure even though we have already + * transmitted the packet. + * + * This allows us to report completions only for packets for + * which we had reported successfully initiating transmission, + * while continuing to support clients that never poll for + * transmit completions. + */ + tx_fill = ( snpdev->tx_prod - snpdev->tx_cons ); + if ( tx_fill >= EFI_SNP_NUM_TX ) { + DBGC ( snpdev, "SNPDEV %p TX completion ring full\n", snpdev ); + rc = -ENOBUFS; + goto err_ring_full; + } + snpdev->tx[ snpdev->tx_prod++ % EFI_SNP_NUM_TX ] = data; + snpdev->interrupts |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; return 0; + err_ring_full: err_tx: err_ll_push: free_iob ( iobuf ); @@ -676,12 +718,13 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, efi_snp_poll ( snpdev ); /* Dequeue a packet, if one is available */ - iobuf = netdev_rx_dequeue ( snpdev->netdev ); + iobuf = list_first_entry ( &snpdev->rx, struct io_buffer, list ); if ( ! iobuf ) { DBGC2 ( snpdev, "\n" ); rc = -EAGAIN; goto out_no_packet; } + list_del ( &iobuf->list ); DBGC2 ( snpdev, "+%zx\n", iob_len ( iobuf ) ); /* Return packet to caller */ @@ -721,9 +764,8 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, * @v event Event * @v context Event context */ -static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event, +static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event __unused, VOID *context ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = context; DBGCP ( snpdev, "SNPDEV %p WAIT_FOR_PACKET\n", snpdev ); @@ -738,14 +780,6 @@ static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event, /* Poll the network device */ efi_snp_poll ( snpdev ); - - /* Fire event if packets have been received */ - if ( snpdev->rx_count_events != 0 ) { - DBGC2 ( snpdev, "SNPDEV %p firing WaitForPacket event\n", - snpdev ); - bs->SignalEvent ( event ); - snpdev->rx_count_events--; - } } /** SNP interface */ @@ -837,6 +871,7 @@ efi_snp_load_file ( EFI_LOAD_FILE_PROTOCOL *load_file, struct efi_snp_device *snpdev = container_of ( load_file, struct efi_snp_device, load_file ); struct net_device *netdev = snpdev->netdev; + int rc; /* Fail unless this is a boot attempt */ if ( ! booting ) { @@ -848,14 +883,17 @@ efi_snp_load_file ( EFI_LOAD_FILE_PROTOCOL *load_file, /* Claim network devices for use by iPXE */ efi_snp_claim(); + /* Start watchdog holdoff timer */ + efi_watchdog_start(); + /* Boot from network device */ - ipxe ( netdev ); + if ( ( rc = ipxe ( netdev ) ) != 0 ) + goto err_ipxe; - /* Release network devices for use via SNP */ + err_ipxe: + efi_watchdog_stop(); efi_snp_release(); - - /* Assume boot process was aborted */ - return EFI_ABORTED; + return EFIRC ( rc ); } /** Load file protocol */ @@ -922,6 +960,7 @@ static int efi_snp_probe ( struct net_device *netdev ) { } snpdev->netdev = netdev_get ( netdev ); snpdev->efidev = efidev; + INIT_LIST_HEAD ( &snpdev->rx ); /* Sanity check */ if ( netdev->ll_protocol->ll_addr_len > sizeof ( EFI_MAC_ADDRESS ) ) { diff --git a/qemu/roms/ipxe/src/interface/efi/efi_snp_hii.c b/qemu/roms/ipxe/src/interface/efi/efi_snp_hii.c index c49c76a32..720402bdb 100644 --- a/qemu/roms/ipxe/src/interface/efi/efi_snp_hii.c +++ b/qemu/roms/ipxe/src/interface/efi/efi_snp_hii.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file @@ -59,6 +63,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include /** EFI platform setup formset GUID */ static EFI_GUID efi_hii_platform_setup_formset_guid @@ -136,7 +141,7 @@ static void efi_snp_hii_questions ( struct efi_snp_device *snpdev, previous = setting; name_id = efi_ifr_string ( ifr, "%s", setting->name ); prompt_id = efi_ifr_string ( ifr, "%s", setting->description ); - help_id = efi_ifr_string ( ifr, "http://ipxe.org/cfg/%s", + help_id = efi_ifr_string ( ifr, PRODUCT_SETTING_URI, setting->name ); question_id = setting->tag; efi_ifr_string_op ( ifr, prompt_id, help_id, diff --git a/qemu/roms/ipxe/src/interface/efi/efi_strings.c b/qemu/roms/ipxe/src/interface/efi/efi_strings.c index 751589b46..aa3afc64f 100644 --- a/qemu/roms/ipxe/src/interface/efi/efi_strings.c +++ b/qemu/roms/ipxe/src/interface/efi/efi_strings.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/interface/efi/efi_time.c b/qemu/roms/ipxe/src/interface/efi/efi_time.c new file mode 100644 index 000000000..983a0ef5c --- /dev/null +++ b/qemu/roms/ipxe/src/interface/efi/efi_time.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include + +/** @file + * + * EFI time source + * + */ + +/** + * Get current time in seconds + * + * @ret time Time, in seconds + */ +static time_t efi_get_time ( void ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + EFI_TIME time; + struct tm tm; + EFI_STATUS efirc; + int rc; + + /* Get current time and date */ + if ( ( efirc = rs->GetTime ( &time, NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( rs, "EFITIME could not get system time: %s\n", + strerror ( rc ) ); + /* Nothing meaningful we can return */ + return 0; + } + + /* Construct broken-down time */ + memset ( &tm, 0, sizeof ( tm ) ); + tm.tm_sec = time.Second; + tm.tm_min = time.Minute; + tm.tm_hour = time.Hour; + tm.tm_mday = time.Day; + tm.tm_mon = ( time.Month - 1 ); + tm.tm_year = ( time.Year - 1900 ); + DBGC ( rs, "EFITIME is %04d-%02d-%02d %02d:%02d:%02d\n", + ( tm.tm_year + 1900 ), ( tm.tm_mon + 1 ), + tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec ); + + /* Convert to seconds since the Epoch */ + return mktime ( &tm ); +} + +PROVIDE_TIME ( efi, time_now, efi_get_time ); diff --git a/qemu/roms/ipxe/src/interface/efi/efi_timer.c b/qemu/roms/ipxe/src/interface/efi/efi_timer.c index 7a1ff7869..81620c92c 100644 --- a/qemu/roms/ipxe/src/interface/efi/efi_timer.c +++ b/qemu/roms/ipxe/src/interface/efi/efi_timer.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/interface/efi/efi_uaccess.c b/qemu/roms/ipxe/src/interface/efi/efi_uaccess.c index 8b429b9ee..e058be66b 100644 --- a/qemu/roms/ipxe/src/interface/efi/efi_uaccess.c +++ b/qemu/roms/ipxe/src/interface/efi/efi_uaccess.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/interface/efi/efi_umalloc.c b/qemu/roms/ipxe/src/interface/efi/efi_umalloc.c index 356efaa6f..e3f1dacc2 100644 --- a/qemu/roms/ipxe/src/interface/efi/efi_umalloc.c +++ b/qemu/roms/ipxe/src/interface/efi/efi_umalloc.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/interface/efi/efi_watchdog.c b/qemu/roms/ipxe/src/interface/efi/efi_watchdog.c new file mode 100644 index 000000000..7061f81d8 --- /dev/null +++ b/qemu/roms/ipxe/src/interface/efi/efi_watchdog.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * EFI watchdog holdoff timer + * + */ + +#include +#include +#include +#include +#include +#include + +/** Watchdog holdoff interval (in seconds) */ +#define WATCHDOG_HOLDOFF_SECS 10 + +/** Watchdog timeout (in seconds) */ +#define WATCHDOG_TIMEOUT_SECS ( 5 * 60 ) + +/** Watchdog code (to be logged on watchdog timeout) */ +#define WATCHDOG_CODE 0x6950584544454144ULL + +/** Watchdog data (to be logged on watchdog timeout) */ +#define WATCHDOG_DATA L"iPXE"; + +/** + * Hold off watchdog timer + * + * @v retry Retry timer + * @v over Failure indicator + */ +static void efi_watchdog_expired ( struct retry_timer *timer, + int over __unused ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + static CHAR16 data[] = WATCHDOG_DATA; + EFI_STATUS efirc; + int rc; + + DBGC2 ( timer, "EFI holding off watchdog timer\n" ); + + /* Restart this holdoff timer */ + start_timer_fixed ( timer, ( WATCHDOG_HOLDOFF_SECS * TICKS_PER_SEC ) ); + + /* Reset watchdog timer */ + if ( ( efirc = bs->SetWatchdogTimer ( WATCHDOG_TIMEOUT_SECS, + WATCHDOG_CODE, sizeof ( data ), + data ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( timer, "EFI could not set watchdog timer: %s\n", + strerror ( rc ) ); + return; + } +} + +/** Watchdog holdoff timer */ +struct retry_timer efi_watchdog = TIMER_INIT ( efi_watchdog_expired ); diff --git a/qemu/roms/ipxe/src/interface/efi/efi_wrap.c b/qemu/roms/ipxe/src/interface/efi/efi_wrap.c index ff46b76ed..2ea184e97 100644 --- a/qemu/roms/ipxe/src/interface/efi/efi_wrap.c +++ b/qemu/roms/ipxe/src/interface/efi/efi_wrap.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/interface/hyperv/vmbus.c b/qemu/roms/ipxe/src/interface/hyperv/vmbus.c new file mode 100644 index 000000000..795929eae --- /dev/null +++ b/qemu/roms/ipxe/src/interface/hyperv/vmbus.c @@ -0,0 +1,1333 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Hyper-V virtual machine bus + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** VMBus initial GPADL ID + * + * This is an opaque value with no meaning. The Linux kernel uses + * 0xe1e10. + */ +#define VMBUS_GPADL_MAGIC 0x18ae0000 + +/** + * Post message + * + * @v hv Hyper-V hypervisor + * @v header Message header + * @v len Length of message (including header) + * @ret rc Return status code + */ +static int vmbus_post_message ( struct hv_hypervisor *hv, + const struct vmbus_message_header *header, + size_t len ) { + struct vmbus *vmbus = hv->vmbus; + int rc; + + /* Post message */ + if ( ( rc = hv_post_message ( hv, VMBUS_MESSAGE_ID, VMBUS_MESSAGE_TYPE, + header, len ) ) != 0 ) { + DBGC ( vmbus, "VMBUS %p could not post message: %s\n", + vmbus, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Post empty message + * + * @v hv Hyper-V hypervisor + * @v type Message type + * @ret rc Return status code + */ +static int vmbus_post_empty_message ( struct hv_hypervisor *hv, + unsigned int type ) { + struct vmbus_message_header header = { .type = cpu_to_le32 ( type ) }; + + return vmbus_post_message ( hv, &header, sizeof ( header ) ); +} + +/** + * Wait for received message + * + * @v hv Hyper-V hypervisor + * @ret rc Return status code + */ +static int vmbus_wait_for_message ( struct hv_hypervisor *hv ) { + struct vmbus *vmbus = hv->vmbus; + int rc; + + /* Wait for message */ + if ( ( rc = hv_wait_for_message ( hv, VMBUS_MESSAGE_SINT ) ) != 0 ) { + DBGC ( vmbus, "VMBUS %p failed waiting for message: %s\n", + vmbus, strerror ( rc ) ); + return rc; + } + + /* Sanity check */ + if ( hv->message->received.type != cpu_to_le32 ( VMBUS_MESSAGE_TYPE ) ){ + DBGC ( vmbus, "VMBUS %p invalid message type %d\n", + vmbus, le32_to_cpu ( hv->message->received.type ) ); + return -EINVAL; + } + + return 0; +} + +/** + * Initiate contact + * + * @v hv Hyper-V hypervisor + * @v raw VMBus protocol (raw) version + * @ret rc Return status code + */ +static int vmbus_initiate_contact ( struct hv_hypervisor *hv, + unsigned int raw ) { + struct vmbus *vmbus = hv->vmbus; + const struct vmbus_version_response *version = &vmbus->message->version; + struct vmbus_initiate_contact initiate; + int rc; + + /* Construct message */ + memset ( &initiate, 0, sizeof ( initiate ) ); + initiate.header.type = cpu_to_le32 ( VMBUS_INITIATE_CONTACT ); + initiate.version.raw = cpu_to_le32 ( raw ); + initiate.intr = virt_to_phys ( vmbus->intr ); + initiate.monitor_in = virt_to_phys ( vmbus->monitor_in ); + initiate.monitor_out = virt_to_phys ( vmbus->monitor_out ); + + /* Post message */ + if ( ( rc = vmbus_post_message ( hv, &initiate.header, + sizeof ( initiate ) ) ) != 0 ) + return rc; + + /* Wait for response */ + if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + return rc; + + /* Check response */ + if ( version->header.type != cpu_to_le32 ( VMBUS_VERSION_RESPONSE ) ) { + DBGC ( vmbus, "VMBUS %p unexpected version response type %d\n", + vmbus, le32_to_cpu ( version->header.type ) ); + return -EPROTO; + } + if ( ! version->supported ) { + DBGC ( vmbus, "VMBUS %p requested version not supported\n", + vmbus ); + return -ENOTSUP; + } + if ( version->version.raw != cpu_to_le32 ( raw ) ) { + DBGC ( vmbus, "VMBUS %p unexpected version %d.%d\n", + vmbus, le16_to_cpu ( version->version.major ), + le16_to_cpu ( version->version.minor ) ); + return -EPROTO; + } + + DBGC ( vmbus, "VMBUS %p initiated contact using version %d.%d\n", + vmbus, le16_to_cpu ( version->version.major ), + le16_to_cpu ( version->version.minor ) ); + return 0; +} + +/** + * Terminate contact + * + * @v hv Hyper-V hypervisor + * @ret rc Return status code + */ +static int vmbus_unload ( struct hv_hypervisor *hv ) { + struct vmbus *vmbus = hv->vmbus; + const struct vmbus_message_header *header = &vmbus->message->header; + int rc; + + /* Post message */ + if ( ( rc = vmbus_post_empty_message ( hv, VMBUS_UNLOAD ) ) != 0 ) + return rc; + + /* Wait for response */ + if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + return rc; + + /* Check response */ + if ( header->type != cpu_to_le32 ( VMBUS_UNLOAD_RESPONSE ) ) { + DBGC ( vmbus, "VMBUS %p unexpected unload response type %d\n", + vmbus, le32_to_cpu ( header->type ) ); + return -EPROTO; + } + + return 0; +} + +/** + * Negotiate protocol version + * + * @v hv Hyper-V hypervisor + * @ret rc Return status code + */ +static int vmbus_negotiate_version ( struct hv_hypervisor *hv ) { + int rc; + + /* We require the ability to disconnect from and reconnect to + * VMBus; if we don't have this then there is no (viable) way + * for a loaded operating system to continue to use any VMBus + * devices. (There is also a small but non-zero risk that the + * host will continue to write to our interrupt and monitor + * pages, since the VMBUS_UNLOAD message in earlier versions + * is essentially a no-op.) + * + * This requires us to ensure that the host supports protocol + * version 3.0 (VMBUS_VERSION_WIN8_1). However, we can't + * actually _use_ protocol version 3.0, since doing so causes + * an iSCSI-booted Windows Server 2012 R2 VM to crash due to a + * NULL pointer dereference in vmbus.sys. + * + * To work around this problem, we first ensure that we can + * connect using protocol v3.0, then disconnect and reconnect + * using the oldest known protocol. + */ + + /* Initiate contact to check for required protocol support */ + if ( ( rc = vmbus_initiate_contact ( hv, VMBUS_VERSION_WIN8_1 ) ) != 0 ) + return rc; + + /* Terminate contact */ + if ( ( rc = vmbus_unload ( hv ) ) != 0 ) + return rc; + + /* Reinitiate contact using the oldest known protocol version */ + if ( ( rc = vmbus_initiate_contact ( hv, VMBUS_VERSION_WS2008 ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Establish GPA descriptor list + * + * @v vmdev VMBus device + * @v data Data buffer + * @v len Length of data buffer + * @ret gpadl GPADL ID, or negative error + */ +int vmbus_establish_gpadl ( struct vmbus_device *vmdev, userptr_t data, + size_t len ) { + struct hv_hypervisor *hv = vmdev->hv; + struct vmbus *vmbus = hv->vmbus; + physaddr_t addr = user_to_phys ( data, 0 ); + unsigned int pfn_count = hv_pfn_count ( addr, len ); + struct { + struct vmbus_gpadl_header gpadlhdr; + struct vmbus_gpa_range range; + uint64_t pfn[pfn_count]; + } __attribute__ (( packed )) gpadlhdr; + const struct vmbus_gpadl_created *created = &vmbus->message->created; + static unsigned int gpadl = VMBUS_GPADL_MAGIC; + unsigned int i; + int rc; + + /* Allocate GPADL ID */ + gpadl++; + + /* Construct message */ + memset ( &gpadlhdr, 0, sizeof ( gpadlhdr ) ); + gpadlhdr.gpadlhdr.header.type = cpu_to_le32 ( VMBUS_GPADL_HEADER ); + gpadlhdr.gpadlhdr.channel = cpu_to_le32 ( vmdev->channel ); + gpadlhdr.gpadlhdr.gpadl = cpu_to_le32 ( gpadl ); + gpadlhdr.gpadlhdr.range_len = + cpu_to_le16 ( ( sizeof ( gpadlhdr.range ) + + sizeof ( gpadlhdr.pfn ) ) ); + gpadlhdr.gpadlhdr.range_count = cpu_to_le16 ( 1 ); + gpadlhdr.range.len = cpu_to_le32 ( len ); + gpadlhdr.range.offset = cpu_to_le32 ( addr & ( PAGE_SIZE - 1 ) ); + for ( i = 0 ; i < pfn_count ; i++ ) + gpadlhdr.pfn[i] = ( ( addr / PAGE_SIZE ) + i ); + + /* Post message */ + if ( ( rc = vmbus_post_message ( hv, &gpadlhdr.gpadlhdr.header, + sizeof ( gpadlhdr ) ) ) != 0 ) + return rc; + + /* Wait for response */ + if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + return rc; + + /* Check response */ + if ( created->header.type != cpu_to_le32 ( VMBUS_GPADL_CREATED ) ) { + DBGC ( vmdev, "VMBUS %s unexpected GPADL response type %d\n", + vmdev->dev.name, le32_to_cpu ( created->header.type ) ); + return -EPROTO; + } + if ( created->channel != cpu_to_le32 ( vmdev->channel ) ) { + DBGC ( vmdev, "VMBUS %s unexpected GPADL channel %d\n", + vmdev->dev.name, le32_to_cpu ( created->channel ) ); + return -EPROTO; + } + if ( created->gpadl != cpu_to_le32 ( gpadl ) ) { + DBGC ( vmdev, "VMBUS %s unexpected GPADL ID %#08x\n", + vmdev->dev.name, le32_to_cpu ( created->gpadl ) ); + return -EPROTO; + } + if ( created->status != 0 ) { + DBGC ( vmdev, "VMBUS %s GPADL creation failed: %#08x\n", + vmdev->dev.name, le32_to_cpu ( created->status ) ); + return -EPROTO; + } + + DBGC ( vmdev, "VMBUS %s GPADL %#08x is [%08lx,%08lx)\n", + vmdev->dev.name, gpadl, addr, ( addr + len ) ); + return gpadl; +} + +/** + * Tear down GPA descriptor list + * + * @v vmdev VMBus device + * @v gpadl GPADL ID + * @ret rc Return status code + */ +int vmbus_gpadl_teardown ( struct vmbus_device *vmdev, unsigned int gpadl ) { + struct hv_hypervisor *hv = vmdev->hv; + struct vmbus *vmbus = hv->vmbus; + struct vmbus_gpadl_teardown teardown; + const struct vmbus_gpadl_torndown *torndown = &vmbus->message->torndown; + int rc; + + /* Construct message */ + memset ( &teardown, 0, sizeof ( teardown ) ); + teardown.header.type = cpu_to_le32 ( VMBUS_GPADL_TEARDOWN ); + teardown.channel = cpu_to_le32 ( vmdev->channel ); + teardown.gpadl = cpu_to_le32 ( gpadl ); + + /* Post message */ + if ( ( rc = vmbus_post_message ( hv, &teardown.header, + sizeof ( teardown ) ) ) != 0 ) + return rc; + + /* Wait for response */ + if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + return rc; + + /* Check response */ + if ( torndown->header.type != cpu_to_le32 ( VMBUS_GPADL_TORNDOWN ) ) { + DBGC ( vmdev, "VMBUS %s unexpected GPADL response type %d\n", + vmdev->dev.name, le32_to_cpu ( torndown->header.type ) ); + return -EPROTO; + } + if ( torndown->gpadl != cpu_to_le32 ( gpadl ) ) { + DBGC ( vmdev, "VMBUS %s unexpected GPADL ID %#08x\n", + vmdev->dev.name, le32_to_cpu ( torndown->gpadl ) ); + return -EPROTO; + } + + return 0; +} + +/** + * Open VMBus channel + * + * @v vmdev VMBus device + * @v op Channel operations + * @v out_len Outbound ring buffer length + * @v in_len Inbound ring buffer length + * @v mtu Maximum expected data packet length (including headers) + * @ret rc Return status code + * + * Both outbound and inbound ring buffer lengths must be a power of + * two and a multiple of PAGE_SIZE. The requirement to be a power of + * two is a policy decision taken to simplify the ring buffer indexing + * logic. + */ +int vmbus_open ( struct vmbus_device *vmdev, + struct vmbus_channel_operations *op, + size_t out_len, size_t in_len, size_t mtu ) { + struct hv_hypervisor *hv = vmdev->hv; + struct vmbus *vmbus = hv->vmbus; + struct vmbus_open_channel open; + const struct vmbus_open_channel_result *opened = + &vmbus->message->opened; + size_t len; + void *ring; + void *packet; + int gpadl; + uint32_t open_id; + int rc; + + /* Sanity checks */ + assert ( ( out_len % PAGE_SIZE ) == 0 ); + assert ( ( out_len & ( out_len - 1 ) ) == 0 ); + assert ( ( in_len % PAGE_SIZE ) == 0 ); + assert ( ( in_len & ( in_len - 1 ) ) == 0 ); + assert ( mtu >= ( sizeof ( struct vmbus_packet_header ) + + sizeof ( struct vmbus_packet_footer ) ) ); + + /* Allocate packet buffer */ + packet = malloc ( mtu ); + if ( ! packet ) { + rc = -ENOMEM; + goto err_alloc_packet; + } + + /* Allocate ring buffer */ + len = ( sizeof ( *vmdev->out ) + out_len + + sizeof ( *vmdev->in ) + in_len ); + assert ( ( len % PAGE_SIZE ) == 0 ); + ring = malloc_dma ( len, PAGE_SIZE ); + if ( ! ring ) { + rc = -ENOMEM; + goto err_alloc_ring; + } + memset ( ring, 0, len ); + + /* Establish GPADL for ring buffer */ + gpadl = vmbus_establish_gpadl ( vmdev, virt_to_user ( ring ), len ); + if ( gpadl < 0 ) { + rc = gpadl; + goto err_establish; + } + + /* Construct message */ + memset ( &open, 0, sizeof ( open ) ); + open.header.type = cpu_to_le32 ( VMBUS_OPEN_CHANNEL ); + open.channel = cpu_to_le32 ( vmdev->channel ); + open_id = random(); + open.id = open_id; /* Opaque random value: endianness irrelevant */ + open.gpadl = cpu_to_le32 ( gpadl ); + open.out_pages = ( ( sizeof ( *vmdev->out ) / PAGE_SIZE ) + + ( out_len / PAGE_SIZE ) ); + + /* Post message */ + if ( ( rc = vmbus_post_message ( hv, &open.header, + sizeof ( open ) ) ) != 0 ) + return rc; + + /* Wait for response */ + if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + return rc; + + /* Check response */ + if ( opened->header.type != cpu_to_le32 ( VMBUS_OPEN_CHANNEL_RESULT ) ){ + DBGC ( vmdev, "VMBUS %s unexpected open response type %d\n", + vmdev->dev.name, le32_to_cpu ( opened->header.type ) ); + return -EPROTO; + } + if ( opened->channel != cpu_to_le32 ( vmdev->channel ) ) { + DBGC ( vmdev, "VMBUS %s unexpected opened channel %#08x\n", + vmdev->dev.name, le32_to_cpu ( opened->channel ) ); + return -EPROTO; + } + if ( opened->id != open_id /* Non-endian */ ) { + DBGC ( vmdev, "VMBUS %s unexpected open ID %#08x\n", + vmdev->dev.name, le32_to_cpu ( opened->id ) ); + return -EPROTO; + } + if ( opened->status != 0 ) { + DBGC ( vmdev, "VMBUS %s open failed: %#08x\n", + vmdev->dev.name, le32_to_cpu ( opened->status ) ); + return -EPROTO; + } + + /* Store channel parameters */ + vmdev->out_len = out_len; + vmdev->in_len = in_len; + vmdev->out = ring; + vmdev->in = ( ring + sizeof ( *vmdev->out ) + out_len ); + vmdev->gpadl = gpadl; + vmdev->op = op; + vmdev->mtu = mtu; + vmdev->packet = packet; + + DBGC ( vmdev, "VMBUS %s channel GPADL %#08x ring " + "[%#08lx,%#08lx,%#08lx)\n", vmdev->dev.name, vmdev->gpadl, + virt_to_phys ( vmdev->out ), virt_to_phys ( vmdev->in ), + ( virt_to_phys ( vmdev->out ) + len ) ); + return 0; + + vmbus_gpadl_teardown ( vmdev, vmdev->gpadl ); + err_establish: + free_dma ( ring, len ); + err_alloc_ring: + free ( packet ); + err_alloc_packet: + return rc; +} + +/** + * Close VMBus channel + * + * @v vmdev VMBus device + */ +void vmbus_close ( struct vmbus_device *vmdev ) { + struct hv_hypervisor *hv = vmdev->hv; + struct vmbus_close_channel close; + size_t len; + int rc; + + /* Construct message */ + memset ( &close, 0, sizeof ( close ) ); + close.header.type = cpu_to_le32 ( VMBUS_CLOSE_CHANNEL ); + close.channel = cpu_to_le32 ( vmdev->channel ); + + /* Post message */ + if ( ( rc = vmbus_post_message ( hv, &close.header, + sizeof ( close ) ) ) != 0 ) { + DBGC ( vmdev, "VMBUS %s failed to close: %s\n", + vmdev->dev.name, strerror ( rc ) ); + /* Continue to attempt to tear down GPADL, so that our + * memory is no longer accessible by the remote VM. + */ + } + + /* Tear down GPADL */ + if ( ( rc = vmbus_gpadl_teardown ( vmdev, + vmdev->gpadl ) ) != 0 ) { + DBGC ( vmdev, "VMBUS %s failed to tear down channel GPADL: " + "%s\n", vmdev->dev.name, strerror ( rc ) ); + /* We can't prevent the remote VM from continuing to + * access this memory, so leak it. + */ + return; + } + + /* Free ring buffer */ + len = ( sizeof ( *vmdev->out ) + vmdev->out_len + + sizeof ( *vmdev->in ) + vmdev->in_len ); + free_dma ( vmdev->out, len ); + vmdev->out = NULL; + vmdev->in = NULL; + + /* Free packet buffer */ + free ( vmdev->packet ); + vmdev->packet = NULL; + + DBGC ( vmdev, "VMBUS %s closed\n", vmdev->dev.name ); +} + +/** + * Signal channel via monitor page + * + * @v vmdev VMBus device + */ +static void vmbus_signal_monitor ( struct vmbus_device *vmdev ) { + struct hv_hypervisor *hv = vmdev->hv; + struct vmbus *vmbus = hv->vmbus; + struct hv_monitor_trigger *trigger; + unsigned int group; + unsigned int bit; + + /* Set bit in monitor trigger group */ + group = ( vmdev->monitor / ( 8 * sizeof ( trigger->pending ) )); + bit = ( vmdev->monitor % ( 8 * sizeof ( trigger->pending ) ) ); + trigger = &vmbus->monitor_out->trigger[group]; + hv_set_bit ( trigger, bit ); +} + +/** + * Signal channel via hypervisor event + * + * @v vmdev VMBus device + */ +static void vmbus_signal_event ( struct vmbus_device *vmdev ) { + struct hv_hypervisor *hv = vmdev->hv; + int rc; + + /* Signal hypervisor event */ + if ( ( rc = hv_signal_event ( hv, VMBUS_EVENT_ID, 0 ) ) != 0 ) { + DBGC ( vmdev, "VMBUS %s could not signal event: %s\n", + vmdev->dev.name, strerror ( rc ) ); + return; + } +} + +/** + * Fill outbound ring buffer + * + * @v vmdev VMBus device + * @v prod Producer index + * @v data Data + * @v len Length + * @ret prod New producer index + * + * The caller must ensure that there is sufficient space in the ring + * buffer. + */ +static size_t vmbus_produce ( struct vmbus_device *vmdev, size_t prod, + const void *data, size_t len ) { + size_t first; + size_t second; + + /* Determine fragment lengths */ + first = ( vmdev->out_len - prod ); + if ( first > len ) + first = len; + second = ( len - first ); + + /* Copy fragment(s) */ + memcpy ( &vmdev->out->data[prod], data, first ); + if ( second ) + memcpy ( &vmdev->out->data[0], ( data + first ), second ); + + return ( ( prod + len ) & ( vmdev->out_len - 1 ) ); +} + +/** + * Consume inbound ring buffer + * + * @v vmdev VMBus device + * @v cons Consumer index + * @v data Data buffer, or NULL + * @v len Length to consume + * @ret cons New consumer index + */ +static size_t vmbus_consume ( struct vmbus_device *vmdev, size_t cons, + void *data, size_t len ) { + size_t first; + size_t second; + + /* Determine fragment lengths */ + first = ( vmdev->in_len - cons ); + if ( first > len ) + first = len; + second = ( len - first ); + + /* Copy fragment(s) */ + memcpy ( data, &vmdev->in->data[cons], first ); + if ( second ) + memcpy ( ( data + first ), &vmdev->in->data[0], second ); + + return ( ( cons + len ) & ( vmdev->in_len - 1 ) ); +} + +/** + * Send packet via ring buffer + * + * @v vmdev VMBus device + * @v header Packet header + * @v data Data + * @v len Length of data + * @ret rc Return status code + * + * Send a packet via the outbound ring buffer. All fields in the + * packet header must be filled in, with the exception of the total + * packet length. + */ +static int vmbus_send ( struct vmbus_device *vmdev, + struct vmbus_packet_header *header, + const void *data, size_t len ) { + struct hv_hypervisor *hv = vmdev->hv; + struct vmbus *vmbus = hv->vmbus; + static uint8_t padding[ 8 - 1 ]; + struct vmbus_packet_footer footer; + size_t header_len; + size_t pad_len; + size_t footer_len; + size_t ring_len; + size_t cons; + size_t prod; + size_t old_prod; + size_t fill; + + /* Sanity check */ + assert ( vmdev->out != NULL ); + + /* Calculate lengths */ + header_len = ( le16_to_cpu ( header->hdr_qlen ) * 8 ); + pad_len = ( ( -len ) & ( 8 - 1 ) ); + footer_len = sizeof ( footer ); + ring_len = ( header_len + len + pad_len + footer_len ); + + /* Check that we have enough room in the outbound ring buffer */ + cons = le32_to_cpu ( vmdev->out->cons ); + prod = le32_to_cpu ( vmdev->out->prod ); + old_prod = prod; + fill = ( ( prod - cons ) & ( vmdev->out_len - 1 ) ); + if ( ( fill + ring_len ) >= vmdev->out_len ) { + DBGC ( vmdev, "VMBUS %s ring buffer full\n", vmdev->dev.name ); + return -ENOBUFS; + } + + /* Complete header */ + header->qlen = cpu_to_le16 ( ( ring_len - footer_len ) / 8 ); + + /* Construct footer */ + footer.reserved = 0; + footer.prod = vmdev->out->prod; + + /* Copy packet to buffer */ + DBGC2 ( vmdev, "VMBUS %s sending:\n", vmdev->dev.name ); + DBGC2_HDA ( vmdev, prod, header, header_len ); + prod = vmbus_produce ( vmdev, prod, header, header_len ); + DBGC2_HDA ( vmdev, prod, data, len ); + prod = vmbus_produce ( vmdev, prod, data, len ); + prod = vmbus_produce ( vmdev, prod, padding, pad_len ); + DBGC2_HDA ( vmdev, prod, &footer, sizeof ( footer ) ); + prod = vmbus_produce ( vmdev, prod, &footer, sizeof ( footer ) ); + assert ( ( ( prod - old_prod ) & ( vmdev->out_len - 1 ) ) == ring_len ); + + /* Update producer index */ + wmb(); + vmdev->out->prod = cpu_to_le32 ( prod ); + + /* Return if we do not need to signal the host. This follows + * the logic of hv_need_to_signal() in the Linux driver. + */ + mb(); + if ( vmdev->out->intr_mask ) + return 0; + rmb(); + cons = le32_to_cpu ( vmdev->out->cons ); + if ( cons != old_prod ) + return 0; + + /* Set channel bit in interrupt page */ + hv_set_bit ( vmbus->intr->out, vmdev->channel ); + + /* Signal the host */ + vmdev->signal ( vmdev ); + + return 0; +} + +/** + * Send control packet via ring buffer + * + * @v vmdev VMBus device + * @v xid Transaction ID (or zero to not request completion) + * @v data Data + * @v len Length of data + * @ret rc Return status code + * + * Send data using a VMBUS_DATA_INBAND packet. + */ +int vmbus_send_control ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len ) { + struct vmbus_packet_header *header = vmdev->packet; + + /* Construct header in packet buffer */ + assert ( header != NULL ); + header->type = cpu_to_le16 ( VMBUS_DATA_INBAND ); + header->hdr_qlen = cpu_to_le16 ( sizeof ( *header ) / 8 ); + header->flags = ( xid ? + cpu_to_le16 ( VMBUS_COMPLETION_REQUESTED ) : 0 ); + header->xid = xid; /* Non-endian */ + + return vmbus_send ( vmdev, header, data, len ); +} + +/** + * Send data packet via ring buffer + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @v data Data + * @v len Length of data + * @v iobuf I/O buffer + * @ret rc Return status code + * + * Send data using a VMBUS_DATA_GPA_DIRECT packet. The caller is + * responsible for ensuring that the I/O buffer remains untouched + * until the corresponding completion has been received. + */ +int vmbus_send_data ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len, struct io_buffer *iobuf ) { + physaddr_t addr = virt_to_phys ( iobuf->data ); + unsigned int pfn_count = hv_pfn_count ( addr, iob_len ( iobuf ) ); + struct { + struct vmbus_gpa_direct_header gpa; + struct vmbus_gpa_range range; + uint64_t pfn[pfn_count]; + } __attribute__ (( packed )) *header = vmdev->packet; + unsigned int i; + + /* Sanity check */ + assert ( header != NULL ); + assert ( sizeof ( *header ) <= vmdev->mtu ); + + /* Construct header in packet buffer */ + header->gpa.header.type = cpu_to_le16 ( VMBUS_DATA_GPA_DIRECT ); + header->gpa.header.hdr_qlen = cpu_to_le16 ( sizeof ( *header ) / 8 ); + header->gpa.header.flags = cpu_to_le16 ( VMBUS_COMPLETION_REQUESTED ); + header->gpa.header.xid = xid; /* Non-endian */ + header->gpa.range_count = 1; + header->range.len = cpu_to_le32 ( iob_len ( iobuf ) ); + header->range.offset = cpu_to_le32 ( addr & ( PAGE_SIZE - 1 ) ); + for ( i = 0 ; i < pfn_count ; i++ ) + header->pfn[i] = ( ( addr / PAGE_SIZE ) + i ); + + return vmbus_send ( vmdev, &header->gpa.header, data, len ); +} + +/** + * Send completion packet via ring buffer + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @v data Data + * @v len Length of data + * @ret rc Return status code + * + * Send data using a VMBUS_COMPLETION packet. + */ +int vmbus_send_completion ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len ) { + struct vmbus_packet_header *header = vmdev->packet; + + /* Construct header in packet buffer */ + assert ( header != NULL ); + header->type = cpu_to_le16 ( VMBUS_COMPLETION ); + header->hdr_qlen = cpu_to_le16 ( sizeof ( *header ) / 8 ); + header->flags = 0; + header->xid = xid; /* Non-endian */ + + return vmbus_send ( vmdev, header, data, len ); +} + +/** + * Send cancellation packet via ring buffer + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @ret rc Return status code + * + * Send data using a VMBUS_CANCELLATION packet. + */ +int vmbus_send_cancellation ( struct vmbus_device *vmdev, uint64_t xid ) { + struct vmbus_packet_header *header = vmdev->packet; + + /* Construct header in packet buffer */ + assert ( header != NULL ); + header->type = cpu_to_le16 ( VMBUS_CANCELLATION ); + header->hdr_qlen = cpu_to_le16 ( sizeof ( *header ) / 8 ); + header->flags = 0; + header->xid = xid; /* Non-endian */ + + return vmbus_send ( vmdev, header, NULL, 0 ); +} + +/** + * Get transfer page set from pageset ID + * + * @v vmdev VMBus device + * @v pageset Page set ID (in protocol byte order) + * @ret pages Page set, or NULL if not found + */ +static struct vmbus_xfer_pages * vmbus_xfer_pages ( struct vmbus_device *vmdev, + uint16_t pageset ) { + struct vmbus_xfer_pages *pages; + + /* Locate page set */ + list_for_each_entry ( pages, &vmdev->pages, list ) { + if ( pages->pageset == pageset ) + return pages; + } + + DBGC ( vmdev, "VMBUS %s unrecognised page set ID %#04x\n", + vmdev->dev.name, le16_to_cpu ( pageset ) ); + return NULL; +} + +/** + * Construct I/O buffer list from transfer pages + * + * @v vmdev VMBus device + * @v header Transfer page header + * @v list I/O buffer list to populate + * @ret rc Return status code + */ +static int vmbus_xfer_page_iobufs ( struct vmbus_device *vmdev, + struct vmbus_packet_header *header, + struct list_head *list ) { + struct vmbus_xfer_page_header *page_header = + container_of ( header, struct vmbus_xfer_page_header, header ); + struct vmbus_xfer_pages *pages; + struct io_buffer *iobuf; + struct io_buffer *tmp; + size_t len; + size_t offset; + unsigned int range_count; + unsigned int i; + int rc; + + /* Sanity check */ + assert ( header->type == cpu_to_le16 ( VMBUS_DATA_XFER_PAGES ) ); + + /* Locate page set */ + pages = vmbus_xfer_pages ( vmdev, page_header->pageset ); + if ( ! pages ) { + rc = -ENOENT; + goto err_pages; + } + + /* Allocate and populate I/O buffers */ + range_count = le32_to_cpu ( page_header->range_count ); + for ( i = 0 ; i < range_count ; i++ ) { + + /* Parse header */ + len = le32_to_cpu ( page_header->range[i].len ); + offset = le32_to_cpu ( page_header->range[i].offset ); + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( len ); + if ( ! iobuf ) { + DBGC ( vmdev, "VMBUS %s could not allocate %zd-byte " + "I/O buffer\n", vmdev->dev.name, len ); + rc = -ENOMEM; + goto err_alloc; + } + + /* Add I/O buffer to list */ + list_add ( &iobuf->list, list ); + + /* Populate I/O buffer */ + if ( ( rc = pages->op->copy ( pages, iob_put ( iobuf, len ), + offset, len ) ) != 0 ) { + DBGC ( vmdev, "VMBUS %s could not populate I/O buffer " + "range [%zd,%zd): %s\n", + vmdev->dev.name, offset, len, strerror ( rc ) ); + goto err_copy; + } + } + + return 0; + + err_copy: + err_alloc: + list_for_each_entry_safe ( iobuf, tmp, list, list ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); + } + err_pages: + return rc; +} + +/** + * Poll ring buffer + * + * @v vmdev VMBus device + * @ret rc Return status code + */ +int vmbus_poll ( struct vmbus_device *vmdev ) { + struct vmbus_packet_header *header = vmdev->packet; + struct list_head list; + void *data; + size_t header_len; + size_t len; + size_t footer_len; + size_t ring_len; + size_t cons; + size_t old_cons; + uint64_t xid; + int rc; + + /* Sanity checks */ + assert ( vmdev->packet != NULL ); + assert ( vmdev->in != NULL ); + + /* Return immediately if buffer is empty */ + if ( ! vmbus_has_data ( vmdev ) ) + return 0; + cons = le32_to_cpu ( vmdev->in->cons ); + old_cons = cons; + + /* Consume (start of) header */ + cons = vmbus_consume ( vmdev, cons, header, sizeof ( *header ) ); + + /* Parse and sanity check header */ + header_len = ( le16_to_cpu ( header->hdr_qlen ) * 8 ); + if ( header_len < sizeof ( *header ) ) { + DBGC ( vmdev, "VMBUS %s received underlength header (%zd " + "bytes)\n", vmdev->dev.name, header_len ); + return -EINVAL; + } + len = ( ( le16_to_cpu ( header->qlen ) * 8 ) - header_len ); + footer_len = sizeof ( struct vmbus_packet_footer ); + ring_len = ( header_len + len + footer_len ); + if ( ring_len > vmdev->mtu ) { + DBGC ( vmdev, "VMBUS %s received overlength packet (%zd " + "bytes)\n", vmdev->dev.name, ring_len ); + return -ERANGE; + } + xid = le64_to_cpu ( header->xid ); + + /* Consume remainder of packet */ + cons = vmbus_consume ( vmdev, cons, + ( ( ( void * ) header ) + sizeof ( *header ) ), + ( ring_len - sizeof ( *header ) ) ); + DBGC2 ( vmdev, "VMBUS %s received:\n", vmdev->dev.name ); + DBGC2_HDA ( vmdev, old_cons, header, ring_len ); + assert ( ( ( cons - old_cons ) & ( vmdev->in_len - 1 ) ) == ring_len ); + + /* Allocate I/O buffers, if applicable */ + INIT_LIST_HEAD ( &list ); + if ( header->type == cpu_to_le16 ( VMBUS_DATA_XFER_PAGES ) ) { + if ( ( rc = vmbus_xfer_page_iobufs ( vmdev, header, + &list ) ) != 0 ) + return rc; + } + + /* Update producer index */ + rmb(); + vmdev->in->cons = cpu_to_le32 ( cons ); + + /* Handle packet */ + data = ( ( ( void * ) header ) + header_len ); + switch ( header->type ) { + + case cpu_to_le16 ( VMBUS_DATA_INBAND ) : + if ( ( rc = vmdev->op->recv_control ( vmdev, xid, data, + len ) ) != 0 ) { + DBGC ( vmdev, "VMBUS %s could not handle control " + "packet: %s\n", + vmdev->dev.name, strerror ( rc ) ); + return rc; + } + break; + + case cpu_to_le16 ( VMBUS_DATA_XFER_PAGES ) : + if ( ( rc = vmdev->op->recv_data ( vmdev, xid, data, len, + &list ) ) != 0 ) { + DBGC ( vmdev, "VMBUS %s could not handle data packet: " + "%s\n", vmdev->dev.name, strerror ( rc ) ); + return rc; + } + break; + + case cpu_to_le16 ( VMBUS_COMPLETION ) : + if ( ( rc = vmdev->op->recv_completion ( vmdev, xid, data, + len ) ) != 0 ) { + DBGC ( vmdev, "VMBUS %s could not handle completion: " + "%s\n", vmdev->dev.name, strerror ( rc ) ); + return rc; + } + break; + + case cpu_to_le16 ( VMBUS_CANCELLATION ) : + if ( ( rc = vmdev->op->recv_cancellation ( vmdev, xid ) ) != 0){ + DBGC ( vmdev, "VMBUS %s could not handle cancellation: " + "%s\n", vmdev->dev.name, strerror ( rc ) ); + return rc; + } + break; + + default: + DBGC ( vmdev, "VMBUS %s unknown packet type %d\n", + vmdev->dev.name, le16_to_cpu ( header->type ) ); + return -ENOTSUP; + } + + return 0; +} + +/** + * Dump channel status (for debugging) + * + * @v vmdev VMBus device + */ +void vmbus_dump_channel ( struct vmbus_device *vmdev ) { + size_t out_prod = le32_to_cpu ( vmdev->out->prod ); + size_t out_cons = le32_to_cpu ( vmdev->out->cons ); + size_t in_prod = le32_to_cpu ( vmdev->in->prod ); + size_t in_cons = le32_to_cpu ( vmdev->in->cons ); + size_t in_len; + size_t first; + size_t second; + + /* Dump ring status */ + DBGC ( vmdev, "VMBUS %s out %03zx:%03zx%s in %03zx:%03zx%s\n", + vmdev->dev.name, out_prod, out_cons, + ( vmdev->out->intr_mask ? "(m)" : "" ), in_prod, in_cons, + ( vmdev->in->intr_mask ? "(m)" : "" ) ); + + /* Dump inbound ring contents, if any */ + if ( in_prod != in_cons ) { + in_len = ( ( in_prod - in_cons ) & + ( vmdev->in_len - 1 ) ); + first = ( vmdev->in_len - in_cons ); + if ( first > in_len ) + first = in_len; + second = ( in_len - first ); + DBGC_HDA ( vmdev, in_cons, &vmdev->in->data[in_cons], first ); + DBGC_HDA ( vmdev, 0, &vmdev->in->data[0], second ); + } +} + +/** + * Find driver for VMBus device + * + * @v vmdev VMBus device + * @ret driver Driver, or NULL + */ +static struct vmbus_driver * vmbus_find_driver ( const union uuid *type ) { + struct vmbus_driver *vmdrv; + + for_each_table_entry ( vmdrv, VMBUS_DRIVERS ) { + if ( memcmp ( &vmdrv->type, type, sizeof ( *type ) ) == 0 ) + return vmdrv; + } + return NULL; +} + +/** + * Probe channels + * + * @v hv Hyper-V hypervisor + * @v parent Parent device + * @ret rc Return status code + */ +static int vmbus_probe_channels ( struct hv_hypervisor *hv, + struct device *parent ) { + struct vmbus *vmbus = hv->vmbus; + const struct vmbus_message_header *header = &vmbus->message->header; + const struct vmbus_offer_channel *offer = &vmbus->message->offer; + const union uuid *type; + struct vmbus_driver *driver; + struct vmbus_device *vmdev; + struct vmbus_device *tmp; + unsigned int channel; + int rc; + + /* Post message */ + if ( ( rc = vmbus_post_empty_message ( hv, VMBUS_REQUEST_OFFERS ) ) !=0) + goto err_post_message; + + /* Collect responses */ + while ( 1 ) { + + /* Wait for response */ + if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + goto err_wait_for_message; + + /* Handle response */ + if ( header->type == cpu_to_le32 ( VMBUS_OFFER_CHANNEL ) ) { + + /* Parse offer */ + type = &offer->type; + channel = le32_to_cpu ( offer->channel ); + DBGC2 ( vmbus, "VMBUS %p offer %d type %s", + vmbus, channel, uuid_ntoa ( type ) ); + if ( offer->monitored ) + DBGC2 ( vmbus, " monitor %d", offer->monitor ); + DBGC2 ( vmbus, "\n" ); + + /* Look for a driver */ + driver = vmbus_find_driver ( type ); + if ( ! driver ) { + DBGC2 ( vmbus, "VMBUS %p has no driver for " + "type %s\n", vmbus, uuid_ntoa ( type )); + /* Not a fatal error */ + continue; + } + + /* Allocate and initialise device */ + vmdev = zalloc ( sizeof ( *vmdev ) ); + if ( ! vmdev ) { + rc = -ENOMEM; + goto err_alloc_vmdev; + } + snprintf ( vmdev->dev.name, sizeof ( vmdev->dev.name ), + "vmbus:%02x", channel ); + vmdev->dev.desc.bus_type = BUS_TYPE_HV; + INIT_LIST_HEAD ( &vmdev->dev.children ); + list_add_tail ( &vmdev->dev.siblings, + &parent->children ); + vmdev->dev.parent = parent; + vmdev->hv = hv; + vmdev->channel = channel; + vmdev->monitor = offer->monitor; + vmdev->signal = ( offer->monitored ? + vmbus_signal_monitor : + vmbus_signal_event ); + INIT_LIST_HEAD ( &vmdev->pages ); + vmdev->driver = driver; + vmdev->dev.driver_name = driver->name; + DBGC ( vmdev, "VMBUS %s has driver \"%s\"\n", + vmdev->dev.name, vmdev->driver->name ); + + } else if ( header->type == + cpu_to_le32 ( VMBUS_ALL_OFFERS_DELIVERED ) ) { + + break; + + } else { + DBGC ( vmbus, "VMBUS %p unexpected offer response type " + "%d\n", vmbus, le32_to_cpu ( header->type ) ); + rc = -EPROTO; + goto err_unexpected_offer; + } + } + + /* Probe all devices. We do this only after completing + * enumeration since devices will need to send and receive + * VMBus messages. + */ + list_for_each_entry ( vmdev, &parent->children, dev.siblings ) { + if ( ( rc = vmdev->driver->probe ( vmdev ) ) != 0 ) { + DBGC ( vmdev, "VMBUS %s could not probe: %s\n", + vmdev->dev.name, strerror ( rc ) ); + goto err_probe; + } + } + + return 0; + + err_probe: + /* Remove driver from each device that was already probed */ + list_for_each_entry_continue_reverse ( vmdev, &parent->children, + dev.siblings ) { + vmdev->driver->remove ( vmdev ); + } + err_unexpected_offer: + err_alloc_vmdev: + err_wait_for_message: + /* Free any devices allocated (but potentially not yet probed) */ + list_for_each_entry_safe ( vmdev, tmp, &parent->children, + dev.siblings ) { + list_del ( &vmdev->dev.siblings ); + free ( vmdev ); + } + err_post_message: + return rc; +} + +/** + * Remove channels + * + * @v hv Hyper-V hypervisor + * @v parent Parent device + */ +static void vmbus_remove_channels ( struct hv_hypervisor *hv __unused, + struct device *parent ) { + struct vmbus_device *vmdev; + struct vmbus_device *tmp; + + /* Remove devices */ + list_for_each_entry_safe ( vmdev, tmp, &parent->children, + dev.siblings ) { + vmdev->driver->remove ( vmdev ); + assert ( list_empty ( &vmdev->dev.children ) ); + assert ( vmdev->out == NULL ); + assert ( vmdev->in == NULL ); + assert ( vmdev->packet == NULL ); + assert ( list_empty ( &vmdev->pages ) ); + list_del ( &vmdev->dev.siblings ); + free ( vmdev ); + } +} + +/** + * Probe Hyper-V virtual machine bus + * + * @v hv Hyper-V hypervisor + * @v parent Parent device + * @ret rc Return status code + */ +int vmbus_probe ( struct hv_hypervisor *hv, struct device *parent ) { + struct vmbus *vmbus; + int rc; + + /* Allocate and initialise structure */ + vmbus = zalloc ( sizeof ( *vmbus ) ); + if ( ! vmbus ) { + rc = -ENOMEM; + goto err_alloc; + } + hv->vmbus = vmbus; + + /* Initialise message buffer pointer + * + * We use a pointer to the fixed-size Hyper-V received message + * buffer. This allows us to access fields within received + * messages without first checking the message size: any + * fields beyond the end of the message will read as zero. + */ + vmbus->message = ( ( void * ) hv->message->received.data ); + assert ( sizeof ( *vmbus->message ) <= + sizeof ( hv->message->received.data ) ); + + /* Allocate interrupt and monitor pages */ + if ( ( rc = hv_alloc_pages ( hv, &vmbus->intr, &vmbus->monitor_in, + &vmbus->monitor_out, NULL ) ) != 0 ) + goto err_alloc_pages; + + /* Enable message interrupt */ + hv_enable_sint ( hv, VMBUS_MESSAGE_SINT ); + + /* Negotiate protocol version */ + if ( ( rc = vmbus_negotiate_version ( hv ) ) != 0 ) + goto err_negotiate_version; + + /* Enumerate channels */ + if ( ( rc = vmbus_probe_channels ( hv, parent ) ) != 0 ) + goto err_probe_channels; + + return 0; + + vmbus_remove_channels ( hv, parent ); + err_probe_channels: + vmbus_unload ( hv ); + err_negotiate_version: + hv_disable_sint ( hv, VMBUS_MESSAGE_SINT ); + hv_free_pages ( hv, vmbus->intr, vmbus->monitor_in, vmbus->monitor_out, + NULL ); + err_alloc_pages: + free ( vmbus ); + err_alloc: + return rc; +} + +/** + * Remove Hyper-V virtual machine bus + * + * @v hv Hyper-V hypervisor + * @v parent Parent device + */ +void vmbus_remove ( struct hv_hypervisor *hv, struct device *parent ) { + struct vmbus *vmbus = hv->vmbus; + + vmbus_remove_channels ( hv, parent ); + vmbus_unload ( hv ); + hv_disable_sint ( hv, VMBUS_MESSAGE_SINT ); + hv_free_pages ( hv, vmbus->intr, vmbus->monitor_in, vmbus->monitor_out, + NULL ); + free ( vmbus ); +} diff --git a/qemu/roms/ipxe/src/interface/linux/linux_entropy.c b/qemu/roms/ipxe/src/interface/linux/linux_entropy.c index 4671a48da..0f8e45d36 100644 --- a/qemu/roms/ipxe/src/interface/linux/linux_entropy.c +++ b/qemu/roms/ipxe/src/interface/linux/linux_entropy.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/interface/linux/linux_pci.c b/qemu/roms/ipxe/src/interface/linux/linux_pci.c index cbd825c18..0c140cb89 100644 --- a/qemu/roms/ipxe/src/interface/linux/linux_pci.c +++ b/qemu/roms/ipxe/src/interface/linux/linux_pci.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/interface/linux/linux_time.c b/qemu/roms/ipxe/src/interface/linux/linux_time.c index e3cbafec6..9e99fe9cd 100644 --- a/qemu/roms/ipxe/src/interface/linux/linux_time.c +++ b/qemu/roms/ipxe/src/interface/linux/linux_time.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/interface/linux/linux_uaccess.c b/qemu/roms/ipxe/src/interface/linux/linux_uaccess.c index 5ab0b6b65..ea2d8057c 100644 --- a/qemu/roms/ipxe/src/interface/linux/linux_uaccess.c +++ b/qemu/roms/ipxe/src/interface/linux/linux_uaccess.c @@ -27,7 +27,6 @@ FILE_LICENCE(GPL2_OR_LATER); * */ -PROVIDE_UACCESS_INLINE(linux, phys_to_user); PROVIDE_UACCESS_INLINE(linux, user_to_phys); PROVIDE_UACCESS_INLINE(linux, virt_to_user); PROVIDE_UACCESS_INLINE(linux, user_to_virt); diff --git a/qemu/roms/ipxe/src/interface/smbios/smbios.c b/qemu/roms/ipxe/src/interface/smbios/smbios.c index 856943428..1dcf819c2 100644 --- a/qemu/roms/ipxe/src/interface/smbios/smbios.c +++ b/qemu/roms/ipxe/src/interface/smbios/smbios.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/interface/smbios/smbios_settings.c b/qemu/roms/ipxe/src/interface/smbios/smbios_settings.c index 83e4320e9..5eadfa081 100644 --- a/qemu/roms/ipxe/src/interface/smbios/smbios_settings.c +++ b/qemu/roms/ipxe/src/interface/smbios/smbios_settings.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/interface/xen/xenbus.c b/qemu/roms/ipxe/src/interface/xen/xenbus.c index ffc8aba3e..c328af443 100644 --- a/qemu/roms/ipxe/src/interface/xen/xenbus.c +++ b/qemu/roms/ipxe/src/interface/xen/xenbus.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/interface/xen/xengrant.c b/qemu/roms/ipxe/src/interface/xen/xengrant.c index be12b23dc..269cd5836 100644 --- a/qemu/roms/ipxe/src/interface/xen/xengrant.c +++ b/qemu/roms/ipxe/src/interface/xen/xengrant.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/interface/xen/xenstore.c b/qemu/roms/ipxe/src/interface/xen/xenstore.c index b96982927..23424a926 100644 --- a/qemu/roms/ipxe/src/interface/xen/xenstore.c +++ b/qemu/roms/ipxe/src/interface/xen/xenstore.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -238,6 +242,10 @@ static int xenstore_response ( struct xen_hypervisor *xen, uint32_t req_id, char *string; int rc; + /* Wait for response to become available */ + while ( ! xenevent_pending ( xen, xen->store.port ) ) + cpu_nap(); + /* Receive message header */ xenstore_recv ( xen, &msg, sizeof ( msg ) ); *len = msg.len; diff --git a/qemu/roms/ipxe/src/net/80211/net80211.c b/qemu/roms/ipxe/src/net/80211/net80211.c index 434944523..d4970ad5c 100644 --- a/qemu/roms/ipxe/src/net/80211/net80211.c +++ b/qemu/roms/ipxe/src/net/80211/net80211.c @@ -805,6 +805,10 @@ int net80211_register ( struct net80211_device *dev, NET80211_MAX_CHANNELS * sizeof ( dev->channels[0] ) ); dev->channel = 0; + /* Mark device as not supporting interrupts, if applicable */ + if ( ! ops->irq ) + dev->netdev->state |= NETDEV_IRQ_UNSUPPORTED; + list_add_tail ( &dev->list, &net80211_devices ); return register_netdev ( dev->netdev ); } @@ -2826,3 +2830,9 @@ struct errortab common_wireless_errors[] __errortab = { __einfo_errortab ( EINFO_ECONNREFUSED_ASSOC_DENIED ), __einfo_errortab ( EINFO_ECONNREFUSED_AUTH_ALGO_UNSUPP ), }; + +/* Drag in objects via net80211_ll_protocol */ +REQUIRING_SYMBOL ( net80211_ll_protocol ); + +/* Drag in 802.11 configuration */ +REQUIRE_OBJECT ( config_net80211 ); diff --git a/qemu/roms/ipxe/src/net/80211/wpa.c b/qemu/roms/ipxe/src/net/80211/wpa.c index e2c4945f9..77f66d825 100644 --- a/qemu/roms/ipxe/src/net/80211/wpa.c +++ b/qemu/roms/ipxe/src/net/80211/wpa.c @@ -912,4 +912,5 @@ struct eapol_handler eapol_key_handler __eapol_handler = { }; /* WPA always needs EAPOL in order to be useful */ +REQUIRING_SYMBOL ( eapol_key_handler ); REQUIRE_OBJECT ( eapol ); diff --git a/qemu/roms/ipxe/src/net/80211/wpa_ccmp.c b/qemu/roms/ipxe/src/net/80211/wpa_ccmp.c index f98ebea26..a073c6a3c 100644 --- a/qemu/roms/ipxe/src/net/80211/wpa_ccmp.c +++ b/qemu/roms/ipxe/src/net/80211/wpa_ccmp.c @@ -480,7 +480,7 @@ static void ccmp_kie_mic ( const void *kck, const void *msg, size_t len, { u8 sha1_ctx[SHA1_CTX_SIZE]; u8 kckb[16]; - u8 hash[SHA1_SIZE]; + u8 hash[SHA1_DIGEST_SIZE]; size_t kck_len = 16; memcpy ( kckb, kck, kck_len ); diff --git a/qemu/roms/ipxe/src/net/80211/wpa_tkip.c b/qemu/roms/ipxe/src/net/80211/wpa_tkip.c index fa3e0763b..3b1934b59 100644 --- a/qemu/roms/ipxe/src/net/80211/wpa_tkip.c +++ b/qemu/roms/ipxe/src/net/80211/wpa_tkip.c @@ -136,7 +136,7 @@ static const u16 Sbox[256] = { */ static inline u16 S ( u16 v ) { - return Sbox[v & 0xFF] ^ swap16 ( Sbox[v >> 8] ); + return Sbox[v & 0xFF] ^ bswap_16 ( Sbox[v >> 8] ); } /** diff --git a/qemu/roms/ipxe/src/net/aoe.c b/qemu/roms/ipxe/src/net/aoe.c index a6d7b3e7b..2da8655b4 100644 --- a/qemu/roms/ipxe/src/net/aoe.c +++ b/qemu/roms/ipxe/src/net/aoe.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/arp.c b/qemu/roms/ipxe/src/net/arp.c index 261e681e1..1e27c44e7 100644 --- a/qemu/roms/ipxe/src/net/arp.c +++ b/qemu/roms/ipxe/src/net/arp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -52,9 +56,9 @@ struct net_protocol arp_protocol __net_protocol; * @v net_source Source network-layer address * @ret rc Return status code */ -static int arp_tx_request ( struct net_device *netdev, - struct net_protocol *net_protocol, - const void *net_dest, const void *net_source ) { +int arp_tx_request ( struct net_device *netdev, + struct net_protocol *net_protocol, + const void *net_dest, const void *net_source ) { struct ll_protocol *ll_protocol = netdev->ll_protocol; struct io_buffer *iobuf; struct arphdr *arphdr; diff --git a/qemu/roms/ipxe/src/net/dhcpopts.c b/qemu/roms/ipxe/src/net/dhcpopts.c index 8cd19cf80..cdb632b46 100644 --- a/qemu/roms/ipxe/src/net/dhcpopts.c +++ b/qemu/roms/ipxe/src/net/dhcpopts.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/dhcppkt.c b/qemu/roms/ipxe/src/net/dhcppkt.c index a9a6d3a94..4e64f85e4 100644 --- a/qemu/roms/ipxe/src/net/dhcppkt.c +++ b/qemu/roms/ipxe/src/net/dhcppkt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/eth_slow.c b/qemu/roms/ipxe/src/net/eth_slow.c index db54b55a4..049c26cb3 100644 --- a/qemu/roms/ipxe/src/net/eth_slow.c +++ b/qemu/roms/ipxe/src/net/eth_slow.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/ethernet.c b/qemu/roms/ipxe/src/net/ethernet.c index 03978c2a8..6ddf05344 100644 --- a/qemu/roms/ipxe/src/net/ethernet.c +++ b/qemu/roms/ipxe/src/net/ethernet.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -42,6 +46,24 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** Ethernet broadcast MAC address */ uint8_t eth_broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +/** + * Check if Ethernet packet has an 802.3 LLC header + * + * @v ethhdr Ethernet header + * @ret is_llc Packet has 802.3 LLC header + */ +static inline int eth_is_llc_packet ( struct ethhdr *ethhdr ) { + uint8_t len_msb; + + /* Check if the protocol field contains a value short enough + * to be a frame length. The slightly convoluted form of the + * comparison is designed to reduce to a single x86 + * instruction. + */ + len_msb = *( ( uint8_t * ) ðhdr->h_protocol ); + return ( len_msb < 0x06 ); +} + /** * Add Ethernet link-layer header * @@ -80,9 +102,14 @@ int eth_pull ( struct net_device *netdev __unused, struct io_buffer *iobuf, const void **ll_dest, const void **ll_source, uint16_t *net_proto, unsigned int *flags ) { struct ethhdr *ethhdr = iobuf->data; + uint16_t *llc_proto; - /* Sanity check */ - if ( iob_len ( iobuf ) < sizeof ( *ethhdr ) ) { + /* Sanity check. While in theory we could receive a one-byte + * packet, this will never happen in practice and performing + * the combined length check here avoids the need for an + * additional comparison if we detect an LLC frame. + */ + if ( iob_len ( iobuf ) < ( sizeof ( *ethhdr ) + sizeof ( *llc_proto ))){ DBG ( "Ethernet packet too short (%zd bytes)\n", iob_len ( iobuf ) ); return -EINVAL; @@ -100,6 +127,17 @@ int eth_pull ( struct net_device *netdev __unused, struct io_buffer *iobuf, ( is_broadcast_ether_addr ( ethhdr->h_dest ) ? LL_BROADCAST : 0 ) ); + /* If this is an LLC frame (with a length in place of the + * protocol field), then use the next two bytes (which happen + * to be the LLC DSAP and SSAP) as the protocol. This allows + * for minimal-overhead support for receiving (rare) LLC + * frames, without requiring a full LLC protocol layer. + */ + if ( eth_is_llc_packet ( ethhdr ) ) { + llc_proto = ( ðhdr->h_protocol + 1 ); + *net_proto = *llc_proto; + } + return 0; } @@ -235,5 +273,11 @@ struct net_device * alloc_etherdev ( size_t priv_size ) { return netdev; } +/* Drag in objects via ethernet_protocol */ +REQUIRING_SYMBOL ( ethernet_protocol ); + +/* Drag in Ethernet configuration */ +REQUIRE_OBJECT ( config_ethernet ); + /* Drag in Ethernet slow protocols */ REQUIRE_OBJECT ( eth_slow ); diff --git a/qemu/roms/ipxe/src/net/fakedhcp.c b/qemu/roms/ipxe/src/net/fakedhcp.c index 3dec88b11..b6c456a59 100644 --- a/qemu/roms/ipxe/src/net/fakedhcp.c +++ b/qemu/roms/ipxe/src/net/fakedhcp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/fc.c b/qemu/roms/ipxe/src/net/fc.c index 58008995c..2e8070272 100644 --- a/qemu/roms/ipxe/src/net/fc.c +++ b/qemu/roms/ipxe/src/net/fc.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -1935,3 +1939,9 @@ struct fc_ulp * fc_ulp_get_port_id_type ( struct fc_port *port, err_peer_get_wwn: return NULL; } + +/* Drag in objects via fc_ports */ +REQUIRING_SYMBOL ( fc_ports ); + +/* Drag in Fibre Channel configuration */ +REQUIRE_OBJECT ( config_fc ); diff --git a/qemu/roms/ipxe/src/net/fcels.c b/qemu/roms/ipxe/src/net/fcels.c index 1cfe90727..5fc27cef4 100644 --- a/qemu/roms/ipxe/src/net/fcels.c +++ b/qemu/roms/ipxe/src/net/fcels.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/fcns.c b/qemu/roms/ipxe/src/net/fcns.c index 3ca4ad557..be4dfea24 100644 --- a/qemu/roms/ipxe/src/net/fcns.c +++ b/qemu/roms/ipxe/src/net/fcns.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/fcoe.c b/qemu/roms/ipxe/src/net/fcoe.c index e9e404ec3..c3258f15e 100644 --- a/qemu/roms/ipxe/src/net/fcoe.c +++ b/qemu/roms/ipxe/src/net/fcoe.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/fcp.c b/qemu/roms/ipxe/src/net/fcp.c index 9c36a4c72..930bf7dd4 100644 --- a/qemu/roms/ipxe/src/net/fcp.c +++ b/qemu/roms/ipxe/src/net/fcp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/fragment.c b/qemu/roms/ipxe/src/net/fragment.c index 410915b3b..781b9bc60 100644 --- a/qemu/roms/ipxe/src/net/fragment.c +++ b/qemu/roms/ipxe/src/net/fragment.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/icmp.c b/qemu/roms/ipxe/src/net/icmp.c index 1bbf8bd30..5371277e4 100644 --- a/qemu/roms/ipxe/src/net/icmp.c +++ b/qemu/roms/ipxe/src/net/icmp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/icmpv4.c b/qemu/roms/ipxe/src/net/icmpv4.c index 996ba1490..0858ff37f 100644 --- a/qemu/roms/ipxe/src/net/icmpv4.c +++ b/qemu/roms/ipxe/src/net/icmpv4.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/icmpv6.c b/qemu/roms/ipxe/src/net/icmpv6.c index 479800e7d..8555aaf0b 100644 --- a/qemu/roms/ipxe/src/net/icmpv6.c +++ b/qemu/roms/ipxe/src/net/icmpv6.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -34,6 +38,65 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +/* Disambiguate the various error causes */ +#define EHOSTUNREACH_ROUTE \ + __einfo_error ( EINFO_EHOSTUNREACH_ROUTE ) +#define EINFO_EHOSTUNREACH_ROUTE \ + __einfo_uniqify ( EINFO_EHOSTUNREACH, 0, \ + "No route to destination" ) +#define EHOSTUNREACH_PROHIBITED \ + __einfo_error ( EINFO_EHOSTUNREACH_PROHIBITED ) +#define EINFO_EHOSTUNREACH_PROHIBITED \ + __einfo_uniqify ( EINFO_EHOSTUNREACH, 1, \ + "Communication administratively prohibited" ) +#define EHOSTUNREACH_ADDRESS \ + __einfo_error ( EINFO_EHOSTUNREACH_ADDRESS ) +#define EINFO_EHOSTUNREACH_ADDRESS \ + __einfo_uniqify ( EINFO_EHOSTUNREACH, 3, \ + "Address unreachable" ) +#define EHOSTUNREACH_PORT \ + __einfo_error ( EINFO_EHOSTUNREACH_PORT ) +#define EINFO_EHOSTUNREACH_PORT \ + __einfo_uniqify ( EINFO_EHOSTUNREACH, 4, \ + "Port unreachable" ) +#define EHOSTUNREACH_CODE( code ) \ + EUNIQ ( EINFO_EHOSTUNREACH, ( (code) & 0x1f ), \ + EHOSTUNREACH_ROUTE, EHOSTUNREACH_PROHIBITED, \ + EHOSTUNREACH_ADDRESS, EHOSTUNREACH_PORT ) + +#define ETIMEDOUT_HOP \ + __einfo_error ( EINFO_ETIMEDOUT_HOP ) +#define EINFO_ETIMEDOUT_HOP \ + __einfo_uniqify ( EINFO_ETIMEDOUT, 0, \ + "Hop limit exceeded in transit" ) +#define ETIMEDOUT_REASSEMBLY \ + __einfo_error ( EINFO_ETIMEDOUT_REASSEMBLY ) +#define EINFO_ETIMEDOUT_REASSEMBLY \ + __einfo_uniqify ( EINFO_ETIMEDOUT, 1, \ + "Fragment reassembly time exceeded" ) +#define ETIMEDOUT_CODE( code ) \ + EUNIQ ( EINFO_ETIMEDOUT, ( (code) & 0x1f ), \ + ETIMEDOUT_HOP, ETIMEDOUT_REASSEMBLY ) + +#define EPROTO_BAD_HEADER \ + __einfo_error ( EINFO_EPROTO_BAD_HEADER ) +#define EINFO_EPROTO_BAD_HEADER \ + __einfo_uniqify ( EINFO_EPROTO, 0, \ + "Erroneous header field" ) +#define EPROTO_NEXT_HEADER \ + __einfo_error ( EINFO_EPROTO_NEXT_HEADER ) +#define EINFO_EPROTO_NEXT_HEADER \ + __einfo_uniqify ( EINFO_EPROTO, 1, \ + "Unrecognised next header type" ) +#define EPROTO_OPTION \ + __einfo_error ( EINFO_EPROTO_OPTION ) +#define EINFO_EPROTO_OPTION \ + __einfo_uniqify ( EINFO_EPROTO, 2, \ + "Unrecognised IPv6 option" ) +#define EPROTO_CODE( code ) \ + EUNIQ ( EINFO_EPROTO, ( (code) & 0x1f ), \ + EPROTO_BAD_HEADER, EPROTO_NEXT_HEADER, EPROTO_OPTION ) + struct icmp_echo_protocol icmpv6_echo_protocol __icmp_echo_protocol; /** @@ -144,8 +207,25 @@ static int icmpv6_rx ( struct io_buffer *iobuf, struct net_device *netdev, /* Identify handler */ handler = icmpv6_handler ( icmp->type ); if ( ! handler ) { - DBGC ( netdev, "ICMPv6 unrecognised type %d\n", icmp->type ); - rc = -ENOTSUP; + switch ( icmp->type ) { + case ICMPV6_DESTINATION_UNREACHABLE: + rc = -EHOSTUNREACH_CODE ( icmp->code ); + break; + case ICMPV6_PACKET_TOO_BIG: + rc = -ERANGE; + break; + case ICMPV6_TIME_EXCEEDED: + rc = -ETIMEDOUT_CODE ( icmp->code ); + break; + case ICMPV6_PARAMETER_PROBLEM: + rc = -EPROTO_CODE ( icmp->code ); + break; + default: + DBGC ( netdev, "ICMPv6 unrecognised type %d code %d\n", + icmp->type, icmp->code ); + rc = -ENOTSUP; + break; + }; goto done; } diff --git a/qemu/roms/ipxe/src/net/infiniband.c b/qemu/roms/ipxe/src/net/infiniband.c index 12d1d83ce..2e3d76d54 100644 --- a/qemu/roms/ipxe/src/net/infiniband.c +++ b/qemu/roms/ipxe/src/net/infiniband.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -714,6 +718,9 @@ int ib_mcast_attach ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct ib_multicast_gid *mgid; int rc; + /* Sanity check */ + assert ( qp != NULL ); + /* Add to software multicast GID list */ mgid = zalloc ( sizeof ( *mgid ) ); if ( ! mgid ) { @@ -747,6 +754,9 @@ void ib_mcast_detach ( struct ib_device *ibdev, struct ib_queue_pair *qp, union ib_gid *gid ) { struct ib_multicast_gid *mgid; + /* Sanity check */ + assert ( qp != NULL ); + /* Remove from hardware multicast GID list */ ibdev->op->mcast_detach ( ibdev, qp, gid ); @@ -995,5 +1005,11 @@ struct ib_device * last_opened_ibdev ( void ) { return ibdev; } +/* Drag in objects via register_ibdev() */ +REQUIRING_SYMBOL ( register_ibdev ); + +/* Drag in Infiniband configuration */ +REQUIRE_OBJECT ( config_infiniband ); + /* Drag in IPoIB */ REQUIRE_OBJECT ( ipoib ); diff --git a/qemu/roms/ipxe/src/net/infiniband/ib_cm.c b/qemu/roms/ipxe/src/net/infiniband/ib_cm.c index 797639bc8..85982f09d 100644 --- a/qemu/roms/ipxe/src/net/infiniband/ib_cm.c +++ b/qemu/roms/ipxe/src/net/infiniband/ib_cm.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/infiniband/ib_mcast.c b/qemu/roms/ipxe/src/net/infiniband/ib_mcast.c index 0a5e72a37..fc4ff7f0a 100644 --- a/qemu/roms/ipxe/src/net/infiniband/ib_mcast.c +++ b/qemu/roms/ipxe/src/net/infiniband/ib_mcast.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -146,6 +150,9 @@ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, DBGC ( ibdev, "IBDEV %p QPN %lx joining " IB_GID_FMT "\n", ibdev, qp->qpn, IB_GID_ARGS ( gid ) ); + /* Sanity check */ + assert ( qp != NULL ); + /* Initialise structure */ membership->qp = qp; memcpy ( &membership->gid, gid, sizeof ( membership->gid ) ); @@ -195,6 +202,9 @@ void ib_mcast_leave ( struct ib_device *ibdev, struct ib_queue_pair *qp, DBGC ( ibdev, "IBDEV %p QPN %lx leaving " IB_GID_FMT "\n", ibdev, qp->qpn, IB_GID_ARGS ( gid ) ); + /* Sanity check */ + assert ( qp != NULL ); + /* Detach from multicast GID */ ib_mcast_detach ( ibdev, qp, &membership->gid ); diff --git a/qemu/roms/ipxe/src/net/infiniband/ib_mi.c b/qemu/roms/ipxe/src/net/infiniband/ib_mi.c index ef6d539f1..b43212974 100644 --- a/qemu/roms/ipxe/src/net/infiniband/ib_mi.c +++ b/qemu/roms/ipxe/src/net/infiniband/ib_mi.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/infiniband/ib_packet.c b/qemu/roms/ipxe/src/net/infiniband/ib_packet.c index 6c850e39b..d3a22d309 100644 --- a/qemu/roms/ipxe/src/net/infiniband/ib_packet.c +++ b/qemu/roms/ipxe/src/net/infiniband/ib_packet.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/infiniband/ib_pathrec.c b/qemu/roms/ipxe/src/net/infiniband/ib_pathrec.c index 1b95cbfa8..f9cbab87f 100644 --- a/qemu/roms/ipxe/src/net/infiniband/ib_pathrec.c +++ b/qemu/roms/ipxe/src/net/infiniband/ib_pathrec.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/infiniband/ib_sma.c b/qemu/roms/ipxe/src/net/infiniband/ib_sma.c index 86553732a..a05d7c924 100644 --- a/qemu/roms/ipxe/src/net/infiniband/ib_sma.c +++ b/qemu/roms/ipxe/src/net/infiniband/ib_sma.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/infiniband/ib_smc.c b/qemu/roms/ipxe/src/net/infiniband/ib_smc.c index 4d947d568..c1741b26c 100644 --- a/qemu/roms/ipxe/src/net/infiniband/ib_smc.c +++ b/qemu/roms/ipxe/src/net/infiniband/ib_smc.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/infiniband/ib_srp.c b/qemu/roms/ipxe/src/net/infiniband/ib_srp.c index 7b2b2b4ea..3700184c0 100644 --- a/qemu/roms/ipxe/src/net/infiniband/ib_srp.c +++ b/qemu/roms/ipxe/src/net/infiniband/ib_srp.c @@ -291,7 +291,7 @@ static int ib_srp_parse_byte_string ( const char *rp_comp, uint8_t *bytes, return -EINVAL_BYTE_STRING_LEN; /* Parse byte string */ - decoded_size = base16_decode ( rp_comp, bytes ); + decoded_size = base16_decode ( rp_comp, bytes, size ); if ( decoded_size < 0 ) return decoded_size; diff --git a/qemu/roms/ipxe/src/net/iobpad.c b/qemu/roms/ipxe/src/net/iobpad.c index 9cc8328e9..936b4bde4 100644 --- a/qemu/roms/ipxe/src/net/iobpad.c +++ b/qemu/roms/ipxe/src/net/iobpad.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/qemu/roms/ipxe/src/net/ipv4.c b/qemu/roms/ipxe/src/net/ipv4.c index 9c5cf2eb4..a54784049 100644 --- a/qemu/roms/ipxe/src/net/ipv4.c +++ b/qemu/roms/ipxe/src/net/ipv4.c @@ -1,3 +1,27 @@ +/* + * Copyright (C) 2006 Michael Brown . + * Copyright (C) 2006 Nikhil Chandru Rao + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + #include #include #include @@ -24,7 +48,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Unique IP datagram identification number (high byte) */ static uint8_t next_ident_high = 0; @@ -115,6 +139,7 @@ static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) { /** * Perform IPv4 routing * + * @v scope_id Destination address scope ID * @v dest Final destination address * @ret dest Next hop destination address * @ret miniroute Routing table entry to use, or NULL if no route @@ -122,22 +147,42 @@ static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) { * If the route requires use of a gateway, the next hop destination * address will be overwritten with the gateway address. */ -static struct ipv4_miniroute * ipv4_route ( struct in_addr *dest ) { +static struct ipv4_miniroute * ipv4_route ( unsigned int scope_id, + struct in_addr *dest ) { struct ipv4_miniroute *miniroute; - int local; - int has_gw; /* Find first usable route in routing table */ list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) { + + /* Skip closed network devices */ if ( ! netdev_is_open ( miniroute->netdev ) ) continue; - local = ( ( ( dest->s_addr ^ miniroute->address.s_addr ) - & miniroute->netmask.s_addr ) == 0 ); - has_gw = ( miniroute->gateway.s_addr ); - if ( local || has_gw ) { - if ( ! local ) + + if ( IN_IS_MULTICAST ( dest->s_addr ) ) { + + /* If destination is non-global, and the scope ID + * matches this network device, then use this route. + */ + if ( miniroute->netdev->index == scope_id ) + return miniroute; + + } else { + + /* If destination is an on-link global + * address, then use this route. + */ + if ( ( ( dest->s_addr ^ miniroute->address.s_addr ) + & miniroute->netmask.s_addr ) == 0 ) + return miniroute; + + /* If destination is an off-link global + * address, and we have a default gateway, + * then use this route. + */ + if ( miniroute->gateway.s_addr ) { *dest = miniroute->gateway; - return miniroute; + return miniroute; + } } } @@ -156,7 +201,7 @@ static struct net_device * ipv4_netdev ( struct sockaddr_tcpip *st_dest ) { struct ipv4_miniroute *miniroute; /* Find routing table entry */ - miniroute = ipv4_route ( &dest ); + miniroute = ipv4_route ( sin_dest->sin_scope_id, &dest ); if ( ! miniroute ) return NULL; @@ -290,8 +335,8 @@ static int ipv4_tx ( struct io_buffer *iobuf, if ( sin_src ) iphdr->src = sin_src->sin_addr; if ( ( next_hop.s_addr != INADDR_BROADCAST ) && - ( ! IN_MULTICAST ( ntohl ( next_hop.s_addr ) ) ) && - ( ( miniroute = ipv4_route ( &next_hop ) ) != NULL ) ) { + ( ( miniroute = ipv4_route ( sin_dest->sin_scope_id, + &next_hop ) ) != NULL ) ) { iphdr->src = miniroute->address; netmask = miniroute->netmask; netdev = miniroute->netdev; @@ -329,7 +374,7 @@ static int ipv4_tx ( struct io_buffer *iobuf, /* Broadcast address */ ipv4_stats.out_bcast_pkts++; ll_dest = netdev->ll_broadcast; - } else if ( IN_MULTICAST ( ntohl ( next_hop.s_addr ) ) ) { + } else if ( IN_IS_MULTICAST ( next_hop.s_addr ) ) { /* Multicast address */ ipv4_stats.out_mcast_pkts++; if ( ( rc = netdev->ll_protocol->mc_hash ( AF_INET, &next_hop, @@ -568,11 +613,43 @@ static int ipv4_arp_check ( struct net_device *netdev, const void *net_addr ) { return -ENOENT; } +/** + * Parse IPv4 address + * + * @v string IPv4 address string + * @ret in IPv4 address to fill in + * @ret ok IPv4 address is valid + * + * Note that this function returns nonzero iff the address is valid, + * to match the standard BSD API function of the same name. Unlike + * most other iPXE functions, a zero therefore indicates failure. + */ +int inet_aton ( const char *string, struct in_addr *in ) { + const char *separator = "..."; + uint8_t *byte = ( ( uint8_t * ) in ); + char *endp; + unsigned long value; + + while ( 1 ) { + value = strtoul ( string, &endp, 0 ); + if ( string == endp ) + return 0; + if ( value > 0xff ) + return 0; + *(byte++) = value; + if ( *endp != *separator ) + return 0; + if ( ! *(separator++) ) + return 1; + string = ( endp + 1 ); + } +} + /** * Convert IPv4 address to dotted-quad notation * - * @v in IP address - * @ret string IP address in dotted-quad notation + * @v in IPv4 address + * @ret string IPv4 address in dotted-quad notation */ char * inet_ntoa ( struct in_addr in ) { static char buf[16]; /* "xxx.xxx.xxx.xxx" */ @@ -583,10 +660,10 @@ char * inet_ntoa ( struct in_addr in ) { } /** - * Transcribe IP address + * Transcribe IPv4 address * - * @v net_addr IP address - * @ret string IP address in dotted-quad notation + * @v net_addr IPv4 address + * @ret string IPv4 address in dotted-quad notation * */ static const char * ipv4_ntoa ( const void *net_addr ) { @@ -760,12 +837,12 @@ static int ipv4_create_routes ( void ) { fetch_ipv4_setting ( settings, &netmask_setting, &netmask ); /* Calculate default netmask, if necessary */ if ( ! netmask.s_addr ) { - if ( IN_CLASSA ( ntohl ( address.s_addr ) ) ) { - netmask.s_addr = htonl ( IN_CLASSA_NET ); - } else if ( IN_CLASSB ( ntohl ( address.s_addr ) ) ) { - netmask.s_addr = htonl ( IN_CLASSB_NET ); - } else if ( IN_CLASSC ( ntohl ( address.s_addr ) ) ) { - netmask.s_addr = htonl ( IN_CLASSC_NET ); + if ( IN_IS_CLASSA ( address.s_addr ) ) { + netmask.s_addr = INADDR_NET_CLASSA; + } else if ( IN_IS_CLASSB ( address.s_addr ) ) { + netmask.s_addr = INADDR_NET_CLASSB; + } else if ( IN_IS_CLASSC ( address.s_addr ) ) { + netmask.s_addr = INADDR_NET_CLASSC; } } /* Get default gateway, if present */ @@ -785,5 +862,8 @@ struct settings_applicator ipv4_settings_applicator __settings_applicator = { .apply = ipv4_create_routes, }; +/* Drag in objects via ipv4_protocol */ +REQUIRING_SYMBOL ( ipv4_protocol ); + /* Drag in ICMPv4 */ REQUIRE_OBJECT ( icmpv4 ); diff --git a/qemu/roms/ipxe/src/net/ipv6.c b/qemu/roms/ipxe/src/net/ipv6.c index 3c374168c..a75e72ddb 100644 --- a/qemu/roms/ipxe/src/net/ipv6.c +++ b/qemu/roms/ipxe/src/net/ipv6.c @@ -290,8 +290,7 @@ static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id, if ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) ) continue; - if ( IN6_IS_ADDR_LINKLOCAL ( *dest ) || - IN6_IS_ADDR_MULTICAST ( *dest ) ) { + if ( IN6_IS_ADDR_NONGLOBAL ( *dest ) ) { /* If destination is non-global, and the scope ID * matches this network device, then use this route. @@ -901,7 +900,7 @@ static const char * ipv6_sock_ntoa ( struct sockaddr *sa ) { const char *netdev_name; /* Identify network device, if applicable */ - if ( IN6_IS_ADDR_LINKLOCAL ( in ) || IN6_IS_ADDR_MULTICAST ( in ) ) { + if ( IN6_IS_ADDR_NONGLOBAL ( in ) ) { netdev = find_netdev_by_index ( sin6->sin6_scope_id ); netdev_name = ( netdev ? netdev->name : "UNKNOWN" ); } else { @@ -956,14 +955,26 @@ static int ipv6_sock_aton ( const char *string, struct sockaddr *sa ) { if ( ( rc = inet6_aton ( in_string, &in ) ) != 0 ) goto err_inet6_aton; - /* Parse network device name, if present */ + /* Parse scope ID, if applicable */ if ( netdev_string ) { + + /* Parse explicit network device name, if present */ netdev = find_netdev ( netdev_string ); if ( ! netdev ) { rc = -ENODEV; goto err_find_netdev; } sin6->sin6_scope_id = netdev->index; + + } else if ( IN6_IS_ADDR_NONGLOBAL ( &in ) ) { + + /* If no network device is explicitly specified for a + * link-local or multicast address, default to using + * "netX" (if existent). + */ + netdev = last_opened_netdev(); + if ( netdev ) + sin6->sin6_scope_id = netdev->index; } /* Copy IPv6 address portion to socket address */ @@ -1104,6 +1115,9 @@ struct net_driver ipv6_driver __net_driver = { .remove = ipv6_remove, }; +/* Drag in objects via ipv6_protocol */ +REQUIRING_SYMBOL ( ipv6_protocol ); + /* Drag in ICMPv6 */ REQUIRE_OBJECT ( icmpv6 ); diff --git a/qemu/roms/ipxe/src/net/neighbour.c b/qemu/roms/ipxe/src/net/neighbour.c index e3026ce46..7f66d9992 100644 --- a/qemu/roms/ipxe/src/net/neighbour.c +++ b/qemu/roms/ipxe/src/net/neighbour.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -91,8 +95,8 @@ static struct neighbour * neighbour_create ( struct net_device *netdev, memcpy ( neighbour->net_dest, net_dest, net_protocol->net_addr_len ); timer_init ( &neighbour->timer, neighbour_expired, &neighbour->refcnt ); - neighbour->timer.min_timeout = NEIGHBOUR_MIN_TIMEOUT; - neighbour->timer.max_timeout = NEIGHBOUR_MAX_TIMEOUT; + set_timer_limits ( &neighbour->timer, NEIGHBOUR_MIN_TIMEOUT, + NEIGHBOUR_MAX_TIMEOUT ); INIT_LIST_HEAD ( &neighbour->tx_queue ); /* Transfer ownership to cache */ @@ -318,7 +322,7 @@ int neighbour_tx ( struct io_buffer *iobuf, struct net_device *netdev, netdev->name, net_protocol->name, net_protocol->ntoa ( net_dest ) ); list_add_tail ( &iobuf->list, &neighbour->tx_queue ); - return -EAGAIN; + return 0; } } diff --git a/qemu/roms/ipxe/src/net/netdev_settings.c b/qemu/roms/ipxe/src/net/netdev_settings.c index b3b2e68d8..edd4c4b9f 100644 --- a/qemu/roms/ipxe/src/net/netdev_settings.c +++ b/qemu/roms/ipxe/src/net/netdev_settings.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -121,6 +125,10 @@ static int netdev_fetch_bustype ( struct net_device *netdev, void *data, [BUS_TYPE_MCA] = "MCA", [BUS_TYPE_ISA] = "ISA", [BUS_TYPE_TAP] = "TAP", + [BUS_TYPE_EFI] = "EFI", + [BUS_TYPE_XEN] = "XEN", + [BUS_TYPE_HV] = "HV", + [BUS_TYPE_USB] = "USB", }; struct device_description *desc = &netdev->dev->desc; const char *bustype; diff --git a/qemu/roms/ipxe/src/net/netdevice.c b/qemu/roms/ipxe/src/net/netdevice.c index a55e6b7d7..7c40a2ac8 100644 --- a/qemu/roms/ipxe/src/net/netdevice.c +++ b/qemu/roms/ipxe/src/net/netdevice.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -35,6 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include @@ -157,6 +162,9 @@ void netdev_rx_unfreeze ( struct net_device *netdev ) { */ void netdev_link_err ( struct net_device *netdev, int rc ) { + /* Stop link block timer */ + stop_timer ( &netdev->link_block ); + /* Record link state */ netdev->link_rc = rc; if ( netdev->link_rc == 0 ) { @@ -186,6 +194,50 @@ void netdev_link_down ( struct net_device *netdev ) { } } +/** + * Mark network device link as being blocked + * + * @v netdev Network device + * @v timeout Timeout (in ticks) + */ +void netdev_link_block ( struct net_device *netdev, unsigned long timeout ) { + + /* Start link block timer */ + if ( ! netdev_link_blocked ( netdev ) ) { + DBGC ( netdev, "NETDEV %s link blocked for %ld ticks\n", + netdev->name, timeout ); + } + start_timer_fixed ( &netdev->link_block, timeout ); +} + +/** + * Mark network device link as being unblocked + * + * @v netdev Network device + */ +void netdev_link_unblock ( struct net_device *netdev ) { + + /* Stop link block timer */ + if ( netdev_link_blocked ( netdev ) ) + DBGC ( netdev, "NETDEV %s link unblocked\n", netdev->name ); + stop_timer ( &netdev->link_block ); +} + +/** + * Handle network device link block timer expiry + * + * @v timer Link block timer + * @v fail Failure indicator + */ +static void netdev_link_block_expired ( struct retry_timer *timer, + int fail __unused ) { + struct net_device *netdev = + container_of ( timer, struct net_device, link_block ); + + /* Assume link is no longer blocked */ + DBGC ( netdev, "NETDEV %s link block expired\n", netdev->name ); +} + /** * Record network device statistic * @@ -252,11 +304,8 @@ int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf ) { } /* Discard packet (for test purposes) if applicable */ - if ( ( NETDEV_DISCARD_RATE > 0 ) && - ( ( random() % NETDEV_DISCARD_RATE ) == 0 ) ) { - rc = -EAGAIN; + if ( ( rc = inject_fault ( NETDEV_DISCARD_RATE ) ) != 0 ) goto err; - } /* Transmit packet */ if ( ( rc = netdev->op->transmit ( netdev, iobuf ) ) != 0 ) @@ -406,14 +455,14 @@ static void netdev_tx_flush ( struct net_device *netdev ) { * function takes ownership of the I/O buffer. */ void netdev_rx ( struct net_device *netdev, struct io_buffer *iobuf ) { + int rc; DBGC2 ( netdev, "NETDEV %s received %p (%p+%zx)\n", netdev->name, iobuf, iobuf->data, iob_len ( iobuf ) ); /* Discard packet (for test purposes) if applicable */ - if ( ( NETDEV_DISCARD_RATE > 0 ) && - ( ( random() % NETDEV_DISCARD_RATE ) == 0 ) ) { - netdev_rx_err ( netdev, iobuf, -EAGAIN ); + if ( ( rc = inject_fault ( NETDEV_DISCARD_RATE ) ) != 0 ) { + netdev_rx_err ( netdev, iobuf, rc ); return; } @@ -541,7 +590,8 @@ static struct interface_descriptor netdev_config_desc = static void free_netdev ( struct refcnt *refcnt ) { struct net_device *netdev = container_of ( refcnt, struct net_device, refcnt ); - + + stop_timer ( &netdev->link_block ); netdev_tx_flush ( netdev ); netdev_rx_flush ( netdev ); clear_settings ( netdev_settings ( netdev ) ); @@ -571,6 +621,8 @@ struct net_device * alloc_netdev ( size_t priv_len ) { if ( netdev ) { ref_init ( &netdev->refcnt, free_netdev ); netdev->link_rc = -EUNKNOWN_LINK_STATUS; + timer_init ( &netdev->link_block, netdev_link_block_expired, + &netdev->refcnt ); INIT_LIST_HEAD ( &netdev->tx_queue ); INIT_LIST_HEAD ( &netdev->tx_deferred ); INIT_LIST_HEAD ( &netdev->rx_queue ); @@ -624,11 +676,11 @@ int register_netdev ( struct net_device *netdev ) { } /* Record device index and create device name */ - netdev->index = netdev_index++; if ( netdev->name[0] == '\0' ) { snprintf ( netdev->name, sizeof ( netdev->name ), "net%d", - netdev->index ); + netdev_index ); } + netdev->index = ++netdev_index; /* Use least significant bits of the link-layer address to * improve the randomness of the (non-cryptographic) random diff --git a/qemu/roms/ipxe/src/net/nullnet.c b/qemu/roms/ipxe/src/net/nullnet.c index 4ac50f64b..2948b38c0 100644 --- a/qemu/roms/ipxe/src/net/nullnet.c +++ b/qemu/roms/ipxe/src/net/nullnet.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/pccrc.c b/qemu/roms/ipxe/src/net/pccrc.c new file mode 100644 index 000000000..4cd82cd1c --- /dev/null +++ b/qemu/roms/ipxe/src/net/pccrc.c @@ -0,0 +1,818 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Peer Content Caching and Retrieval: Content Identification [MS-PCCRC] + * + */ + +/****************************************************************************** + * + * Utility functions + * + ****************************************************************************** + */ + +/** + * Transcribe hash value (for debugging) + * + * @v info Content information + * @v hash Hash value + * @ret string Hash value string + */ +static inline const char * +peerdist_info_hash_ntoa ( const struct peerdist_info *info, const void *hash ) { + static char buf[ ( 2 * PEERDIST_DIGEST_MAX_SIZE ) + 1 /* NUL */ ]; + size_t digestsize = info->digestsize; + + /* Sanity check */ + assert ( info != NULL ); + assert ( digestsize != 0 ); + assert ( base16_encoded_len ( digestsize ) < sizeof ( buf ) ); + + /* Transcribe hash value */ + base16_encode ( hash, digestsize, buf, sizeof ( buf ) ); + return buf; +} + +/** + * Get raw data + * + * @v info Content information + * @v data Data buffer + * @v offset Starting offset + * @v len Length + * @ret rc Return status code + */ +static int peerdist_info_get ( const struct peerdist_info *info, void *data, + size_t offset, size_t len ) { + + /* Sanity check */ + if ( ( offset > info->raw.len ) || + ( len > ( info->raw.len - offset ) ) ) { + DBGC ( info, "PCCRC %p data underrun at [%zx,%zx) of %zx\n", + info, offset, ( offset + len ), info->raw.len ); + return -ERANGE; + } + + /* Copy data */ + copy_from_user ( data, info->raw.data, offset, len ); + + return 0; +} + +/** + * Populate segment hashes + * + * @v segment Content information segment to fill in + * @v hash Segment hash of data + * @v secret Segment secret + */ +static void peerdist_info_segment_hash ( struct peerdist_info_segment *segment, + const void *hash, const void *secret ){ + const struct peerdist_info *info = segment->info; + struct digest_algorithm *digest = info->digest; + uint8_t ctx[digest->ctxsize]; + size_t digestsize = info->digestsize; + size_t secretsize = digestsize; + static const uint16_t magic[] = PEERDIST_SEGMENT_ID_MAGIC; + + /* Sanity check */ + assert ( digestsize <= sizeof ( segment->hash ) ); + assert ( digestsize <= sizeof ( segment->secret ) ); + assert ( digestsize <= sizeof ( segment->id ) ); + + /* Get segment hash of data */ + memcpy ( segment->hash, hash, digestsize ); + + /* Get segment secret */ + memcpy ( segment->secret, secret, digestsize ); + + /* Calculate segment identifier */ + hmac_init ( digest, ctx, segment->secret, &secretsize ); + assert ( secretsize == digestsize ); + hmac_update ( digest, ctx, segment->hash, digestsize ); + hmac_update ( digest, ctx, magic, sizeof ( magic ) ); + hmac_final ( digest, ctx, segment->secret, &secretsize, segment->id ); + assert ( secretsize == digestsize ); +} + +/****************************************************************************** + * + * Content Information version 1 + * + ****************************************************************************** + */ + +/** + * Get number of blocks within a block description + * + * @v info Content information + * @v offset Block description offset + * @ret blocks Number of blocks, or negative error + */ +static int peerdist_info_v1_blocks ( const struct peerdist_info *info, + size_t offset ) { + struct peerdist_info_v1_block raw; + unsigned int blocks; + int rc; + + /* Get block description header */ + if ( ( rc = peerdist_info_get ( info, &raw, offset, + sizeof ( raw ) ) ) != 0 ) + return rc; + + /* Calculate number of blocks */ + blocks = le32_to_cpu ( raw.blocks ); + + return blocks; +} + +/** + * Locate block description + * + * @v info Content information + * @v index Segment index + * @ret offset Block description offset, or negative error + */ +static ssize_t peerdist_info_v1_block_offset ( const struct peerdist_info *info, + unsigned int index ) { + size_t digestsize = info->digestsize; + unsigned int i; + size_t offset; + int blocks; + int rc; + + /* Sanity check */ + assert ( index < info->segments ); + + /* Calculate offset of first block description */ + offset = ( sizeof ( struct peerdist_info_v1 ) + + ( info->segments * + sizeof ( peerdist_info_v1_segment_t ( digestsize ) ) ) ); + + /* Iterate over block descriptions until we find this segment */ + for ( i = 0 ; i < index ; i++ ) { + + /* Get number of blocks */ + blocks = peerdist_info_v1_blocks ( info, offset ); + if ( blocks < 0 ) { + rc = blocks; + DBGC ( info, "PCCRC %p segment %d could not get number " + "of blocks: %s\n", info, i, strerror ( rc ) ); + return rc; + } + + /* Move to next block description */ + offset += sizeof ( peerdist_info_v1_block_t ( digestsize, + blocks ) ); + } + + return offset; +} + +/** + * Populate content information + * + * @v info Content information to fill in + * @ret rc Return status code + */ +static int peerdist_info_v1 ( struct peerdist_info *info ) { + struct peerdist_info_v1 raw; + struct peerdist_info_segment first; + struct peerdist_info_segment last; + size_t first_skip; + size_t last_skip; + size_t last_read; + int rc; + + /* Get raw header */ + if ( ( rc = peerdist_info_get ( info, &raw, 0, sizeof ( raw ) ) ) != 0){ + DBGC ( info, "PCCRC %p could not get V1 content information: " + "%s\n", info, strerror ( rc ) ); + return rc; + } + assert ( raw.version.raw == cpu_to_le16 ( PEERDIST_INFO_V1 ) ); + + /* Determine hash algorithm */ + switch ( raw.hash ) { + case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA256 ) : + info->digest = &sha256_algorithm; + break; + case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA384 ) : + info->digest = &sha384_algorithm; + break; + case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA512 ) : + info->digest = &sha512_algorithm; + break; + default: + DBGC ( info, "PCCRC %p unsupported hash algorithm %#08x\n", + info, le32_to_cpu ( raw.hash ) ); + return -ENOTSUP; + } + info->digestsize = info->digest->digestsize; + assert ( info->digest != NULL ); + DBGC2 ( info, "PCCRC %p using %s[%zd]\n", + info, info->digest->name, ( info->digestsize * 8 ) ); + + /* Calculate number of segments */ + info->segments = le32_to_cpu ( raw.segments ); + + /* Get first segment */ + if ( ( rc = peerdist_info_segment ( info, &first, 0 ) ) != 0 ) + return rc; + + /* Calculate range start offset */ + info->range.start = first.range.start; + + /* Calculate trimmed range start offset */ + first_skip = le32_to_cpu ( raw.first ); + info->trim.start = ( first.range.start + first_skip ); + + /* Get last segment */ + if ( ( rc = peerdist_info_segment ( info, &last, + ( info->segments - 1 ) ) ) != 0 ) + return rc; + + /* Calculate range end offset */ + info->range.end = last.range.end; + + /* Calculate trimmed range end offset */ + if ( raw.last ) { + /* Explicit length to include from last segment is given */ + last_read = le32_to_cpu ( raw.last ); + last_skip = ( last.index ? 0 : first_skip ); + info->trim.end = ( last.range.start + last_skip + last_read ); + } else { + /* No explicit length given: range extends to end of segment */ + info->trim.end = last.range.end; + } + + return 0; +} + +/** + * Populate content information segment + * + * @v segment Content information segment to fill in + * @ret rc Return status code + */ +static int peerdist_info_v1_segment ( struct peerdist_info_segment *segment ) { + const struct peerdist_info *info = segment->info; + size_t digestsize = info->digestsize; + peerdist_info_v1_segment_t ( digestsize ) raw; + ssize_t raw_offset; + int blocks; + int rc; + + /* Sanity checks */ + assert ( segment->index < info->segments ); + + /* Get raw description */ + raw_offset = ( sizeof ( struct peerdist_info_v1 ) + + ( segment->index * sizeof ( raw ) ) ); + if ( ( rc = peerdist_info_get ( info, &raw, raw_offset, + sizeof ( raw ) ) ) != 0 ) { + DBGC ( info, "PCCRC %p segment %d could not get segment " + "description: %s\n", info, segment->index, + strerror ( rc ) ); + return rc; + } + + /* Calculate start offset of this segment */ + segment->range.start = le64_to_cpu ( raw.segment.offset ); + + /* Calculate end offset of this segment */ + segment->range.end = ( segment->range.start + + le32_to_cpu ( raw.segment.len ) ); + + /* Calculate block size of this segment */ + segment->blksize = le32_to_cpu ( raw.segment.blksize ); + + /* Locate block description for this segment */ + raw_offset = peerdist_info_v1_block_offset ( info, segment->index ); + if ( raw_offset < 0 ) { + rc = raw_offset; + return rc; + } + + /* Get number of blocks */ + blocks = peerdist_info_v1_blocks ( info, raw_offset ); + if ( blocks < 0 ) { + rc = blocks; + DBGC ( info, "PCCRC %p segment %d could not get number of " + "blocks: %s\n", info, segment->index, strerror ( rc ) ); + return rc; + } + segment->blocks = blocks; + + /* Calculate segment hashes */ + peerdist_info_segment_hash ( segment, raw.hash, raw.secret ); + + return 0; +} + +/** + * Populate content information block + * + * @v block Content information block to fill in + * @ret rc Return status code + */ +static int peerdist_info_v1_block ( struct peerdist_info_block *block ) { + const struct peerdist_info_segment *segment = block->segment; + const struct peerdist_info *info = segment->info; + size_t digestsize = info->digestsize; + peerdist_info_v1_block_t ( digestsize, segment->blocks ) raw; + ssize_t raw_offset; + int rc; + + /* Sanity checks */ + assert ( block->index < segment->blocks ); + + /* Calculate start offset of this block */ + block->range.start = ( segment->range.start + + ( block->index * segment->blksize ) ); + + /* Calculate end offset of this block */ + block->range.end = ( block->range.start + segment->blksize ); + if ( block->range.end > segment->range.end ) + block->range.end = segment->range.end; + + /* Locate block description */ + raw_offset = peerdist_info_v1_block_offset ( info, segment->index ); + if ( raw_offset < 0 ) { + rc = raw_offset; + return rc; + } + + /* Get block hash */ + raw_offset += offsetof ( typeof ( raw ), hash[block->index] ); + if ( ( rc = peerdist_info_get ( info, block->hash, raw_offset, + digestsize ) ) != 0 ) { + DBGC ( info, "PCCRC %p segment %d block %d could not get " + "hash: %s\n", info, segment->index, block->index, + strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** Content information version 1 operations */ +static struct peerdist_info_operations peerdist_info_v1_operations = { + .info = peerdist_info_v1, + .segment = peerdist_info_v1_segment, + .block = peerdist_info_v1_block, +}; + +/****************************************************************************** + * + * Content Information version 2 + * + ****************************************************************************** + */ + +/** A segment cursor */ +struct peerdist_info_v2_cursor { + /** Raw data offset */ + size_t offset; + /** Number of segments remaining within this chunk */ + unsigned int remaining; + /** Accumulated segment length */ + size_t len; +}; + +/** + * Initialise segment cursor + * + * @v cursor Segment cursor + */ +static inline void +peerdist_info_v2_cursor_init ( struct peerdist_info_v2_cursor *cursor ) { + + /* Initialise cursor */ + cursor->offset = ( sizeof ( struct peerdist_info_v2 ) + + sizeof ( struct peerdist_info_v2_chunk ) ); + cursor->remaining = 0; + cursor->len = 0; +} + +/** + * Update segment cursor to next segment description + * + * @v info Content information + * @v offset Current offset + * @v remaining Number of segments remaining within this chunk + * @ret rc Return status code + */ +static int +peerdist_info_v2_cursor_next ( const struct peerdist_info *info, + struct peerdist_info_v2_cursor *cursor ) { + size_t digestsize = info->digestsize; + peerdist_info_v2_segment_t ( digestsize ) raw; + struct peerdist_info_v2_chunk chunk; + int rc; + + /* Get chunk description if applicable */ + if ( ! cursor->remaining ) { + + /* Get chunk description */ + if ( ( rc = peerdist_info_get ( info, &chunk, + ( cursor->offset - + sizeof ( chunk ) ), + sizeof ( chunk ) ) ) != 0 ) + return rc; + + /* Update number of segments remaining */ + cursor->remaining = ( be32_to_cpu ( chunk.len ) / + sizeof ( raw ) ); + } + + /* Get segment description header */ + if ( ( rc = peerdist_info_get ( info, &raw.segment, cursor->offset, + sizeof ( raw.segment ) ) ) != 0 ) + return rc; + + /* Update cursor */ + cursor->offset += sizeof ( raw ); + cursor->remaining--; + if ( ! cursor->remaining ) + cursor->offset += sizeof ( chunk ); + cursor->len += be32_to_cpu ( raw.segment.len ); + + return 0; +} + +/** + * Get number of segments and total length + * + * @v info Content information + * @v len Length to fill in + * @ret rc Number of segments, or negative error + */ +static int peerdist_info_v2_segments ( const struct peerdist_info *info, + size_t *len ) { + struct peerdist_info_v2_cursor cursor; + unsigned int segments; + int rc; + + /* Iterate over all segments */ + for ( peerdist_info_v2_cursor_init ( &cursor ), segments = 0 ; + cursor.offset < info->raw.len ; segments++ ) { + + /* Update segment cursor */ + if ( ( rc = peerdist_info_v2_cursor_next ( info, + &cursor ) ) != 0 ) { + DBGC ( info, "PCCRC %p segment %d could not update " + "segment cursor: %s\n", + info, segments, strerror ( rc ) ); + return rc; + } + } + + /* Record accumulated length */ + *len = cursor.len; + + return segments; +} + +/** + * Populate content information + * + * @v info Content information to fill in + * @ret rc Return status code + */ +static int peerdist_info_v2 ( struct peerdist_info *info ) { + struct peerdist_info_v2 raw; + size_t len = 0; + int segments; + int rc; + + /* Get raw header */ + if ( ( rc = peerdist_info_get ( info, &raw, 0, sizeof ( raw ) ) ) != 0){ + DBGC ( info, "PCCRC %p could not get V2 content information: " + "%s\n", info, strerror ( rc ) ); + return rc; + } + assert ( raw.version.raw == cpu_to_le16 ( PEERDIST_INFO_V2 ) ); + + /* Determine hash algorithm */ + switch ( raw.hash ) { + case PEERDIST_INFO_V2_HASH_SHA512_TRUNC : + info->digest = &sha512_algorithm; + info->digestsize = ( 256 / 8 ); + break; + default: + DBGC ( info, "PCCRC %p unsupported hash algorithm %#02x\n", + info, raw.hash ); + return -ENOTSUP; + } + assert ( info->digest != NULL ); + DBGC2 ( info, "PCCRC %p using %s[%zd]\n", + info, info->digest->name, ( info->digestsize * 8 ) ); + + /* Calculate number of segments and total length */ + segments = peerdist_info_v2_segments ( info, &len ); + if ( segments < 0 ) { + rc = segments; + DBGC ( info, "PCCRC %p could not get segment count and length: " + "%s\n", info, strerror ( rc ) ); + return rc; + } + info->segments = segments; + + /* Calculate range start offset */ + info->range.start = be64_to_cpu ( raw.offset ); + + /* Calculate trimmed range start offset */ + info->trim.start = ( info->range.start + be32_to_cpu ( raw.first ) ); + + /* Calculate range end offset */ + info->range.end = ( info->range.start + len ); + + /* Calculate trimmed range end offset */ + info->trim.end = ( raw.len ? be64_to_cpu ( raw.len ) : + info->range.end ); + + return 0; +} + +/** + * Populate content information segment + * + * @v segment Content information segment to fill in + * @ret rc Return status code + */ +static int peerdist_info_v2_segment ( struct peerdist_info_segment *segment ) { + const struct peerdist_info *info = segment->info; + size_t digestsize = info->digestsize; + peerdist_info_v2_segment_t ( digestsize ) raw; + struct peerdist_info_v2_cursor cursor; + unsigned int index; + size_t len; + int rc; + + /* Sanity checks */ + assert ( segment->index < info->segments ); + + /* Iterate over all segments before the target segment */ + for ( peerdist_info_v2_cursor_init ( &cursor ), index = 0 ; + index < segment->index ; index++ ) { + + /* Update segment cursor */ + if ( ( rc = peerdist_info_v2_cursor_next ( info, + &cursor ) ) != 0 ) { + DBGC ( info, "PCCRC %p segment %d could not update " + "segment cursor: %s\n", + info, index, strerror ( rc ) ); + return rc; + } + } + + /* Get raw description */ + if ( ( rc = peerdist_info_get ( info, &raw, cursor.offset, + sizeof ( raw ) ) ) != 0 ) { + DBGC ( info, "PCCRC %p segment %d could not get segment " + "description: %s\n", + info, segment->index, strerror ( rc ) ); + return rc; + } + + /* Calculate start offset of this segment */ + segment->range.start = ( info->range.start + cursor.len ); + + /* Calculate end offset of this segment */ + len = be32_to_cpu ( raw.segment.len ); + segment->range.end = ( segment->range.start + len ); + + /* Model as a segment containing a single block */ + segment->blocks = 1; + segment->blksize = len; + + /* Calculate segment hashes */ + peerdist_info_segment_hash ( segment, raw.hash, raw.secret ); + + return 0; +} + +/** + * Populate content information block + * + * @v block Content information block to fill in + * @ret rc Return status code + */ +static int peerdist_info_v2_block ( struct peerdist_info_block *block ) { + const struct peerdist_info_segment *segment = block->segment; + const struct peerdist_info *info = segment->info; + size_t digestsize = info->digestsize; + + /* Sanity checks */ + assert ( block->index < segment->blocks ); + + /* Model as a block covering the whole segment */ + memcpy ( &block->range, &segment->range, sizeof ( block->range ) ); + memcpy ( block->hash, segment->hash, digestsize ); + + return 0; +} + +/** Content information version 2 operations */ +static struct peerdist_info_operations peerdist_info_v2_operations = { + .block = peerdist_info_v2_block, + .segment = peerdist_info_v2_segment, + .info = peerdist_info_v2, +}; + +/****************************************************************************** + * + * Content Information + * + ****************************************************************************** + */ + +/** + * Populate content information + * + * @v data Raw data + * @v len Length of raw data + * @v info Content information to fill in + * @ret rc Return status code + */ +int peerdist_info ( userptr_t data, size_t len, struct peerdist_info *info ) { + union peerdist_info_version version; + int rc; + + /* Initialise structure */ + memset ( info, 0, sizeof ( *info ) ); + info->raw.data = data; + info->raw.len = len; + + /* Get version */ + if ( ( rc = peerdist_info_get ( info, &version, 0, + sizeof ( version ) ) ) != 0 ) { + DBGC ( info, "PCCRC %p could not get version: %s\n", + info, strerror ( rc ) ); + return rc; + } + DBGC2 ( info, "PCCRC %p version %d.%d\n", + info, version.major, version.minor ); + + /* Determine version */ + switch ( version.raw ) { + case cpu_to_le16 ( PEERDIST_INFO_V1 ) : + info->op = &peerdist_info_v1_operations; + break; + case cpu_to_le16 ( PEERDIST_INFO_V2 ) : + info->op = &peerdist_info_v2_operations; + break; + default: + DBGC ( info, "PCCRC %p unsupported version %d.%d\n", + info, version.major, version.minor ); + return -ENOTSUP; + } + assert ( info->op != NULL ); + assert ( info->op->info != NULL ); + + /* Populate content information */ + if ( ( rc = info->op->info ( info ) ) != 0 ) + return rc; + + DBGC2 ( info, "PCCRC %p range [%08zx,%08zx) covers [%08zx,%08zx) with " + "%d segments\n", info, info->range.start, info->range.end, + info->trim.start, info->trim.end, info->segments ); + return 0; +} + +/** + * Populate content information segment + * + * @v info Content information + * @v segment Content information segment to fill in + * @v index Segment index + * @ret rc Return status code + */ +int peerdist_info_segment ( const struct peerdist_info *info, + struct peerdist_info_segment *segment, + unsigned int index ) { + int rc; + + /* Sanity checks */ + assert ( info != NULL ); + assert ( info->op != NULL ); + assert ( info->op->segment != NULL ); + if ( index >= info->segments ) { + DBGC ( info, "PCCRC %p segment %d of [0,%d) out of range\n", + info, index, info->segments ); + return -ERANGE; + } + + /* Initialise structure */ + memset ( segment, 0, sizeof ( *segment ) ); + segment->info = info; + segment->index = index; + + /* Populate content information segment */ + if ( ( rc = info->op->segment ( segment ) ) != 0 ) + return rc; + + DBGC2 ( info, "PCCRC %p segment %d range [%08zx,%08zx) with %d " + "blocks\n", info, segment->index, segment->range.start, + segment->range.end, segment->blocks ); + DBGC2 ( info, "PCCRC %p segment %d digest %s\n", info, segment->index, + peerdist_info_hash_ntoa ( info, segment->hash ) ); + DBGC2 ( info, "PCCRC %p segment %d secret %s\n", info, segment->index, + peerdist_info_hash_ntoa ( info, segment->secret ) ); + DBGC2 ( info, "PCCRC %p segment %d identf %s\n", info, segment->index, + peerdist_info_hash_ntoa ( info, segment->id ) ); + return 0; +} + +/** + * Populate content information block + * + * @v segment Content information segment + * @v block Content information block to fill in + * @v index Block index + * @ret rc Return status code + */ +int peerdist_info_block ( const struct peerdist_info_segment *segment, + struct peerdist_info_block *block, + unsigned int index ) { + const struct peerdist_info *info = segment->info; + size_t start; + size_t end; + int rc; + + /* Sanity checks */ + assert ( segment != NULL ); + assert ( info != NULL ); + assert ( info->op != NULL ); + assert ( info->op->block != NULL ); + if ( index >= segment->blocks ) { + DBGC ( info, "PCCRC %p segment %d block %d of [0,%d) out of " + "range\n", info, segment->index, index, segment->blocks); + return -ERANGE; + } + + /* Initialise structure */ + memset ( block, 0, sizeof ( *block ) ); + block->segment = segment; + block->index = index; + + /* Populate content information block */ + if ( ( rc = info->op->block ( block ) ) != 0 ) + return rc; + + /* Calculate trimmed range */ + start = block->range.start; + if ( start < info->trim.start ) + start = info->trim.start; + end = block->range.end; + if ( end > info->trim.end ) + end = info->trim.end; + if ( end < start ) + end = start; + block->trim.start = start; + block->trim.end = end; + + DBGC2 ( info, "PCCRC %p segment %d block %d hash %s\n", + info, segment->index, block->index, + peerdist_info_hash_ntoa ( info, block->hash ) ); + DBGC2 ( info, "PCCRC %p segment %d block %d range [%08zx,%08zx) covers " + "[%08zx,%08zx)\n", info, segment->index, block->index, + block->range.start, block->range.end, block->trim.start, + block->trim.end ); + return 0; +} diff --git a/qemu/roms/ipxe/src/net/pccrd.c b/qemu/roms/ipxe/src/net/pccrd.c new file mode 100644 index 000000000..04b5dd86c --- /dev/null +++ b/qemu/roms/ipxe/src/net/pccrd.c @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Peer Content Caching and Retrieval: Discovery Protocol [MS-PCCRD] + * + * This protocol manages to ingeniously combine the excessive + * verbosity of XML with a paucity of actual information. For + * example: even in version 2.0 of the protocol it is still not + * possible to discover which peers hold a specific block within a + * given segment. + * + * For added bonus points, version 1.0 of the protocol is specified to + * use a case-sensitive string comparison (for SHA2 digest values) but + * nothing specifies whether the strings in question should be in + * upper or lower case. There are example strings given in the + * specification, but the author skilfully manages to leave the issue + * unresolved by using the somewhat implausible digest value of + * "0200000000000000000000000000000000000000000000000000000000000000". + * + * Just in case you were thinking that the silver lining of the choice + * to use an XML-based protocol would be the ability to generate and + * process messages with standard tools, version 2.0 of the protocol + * places most of the critical information inside a Base64-encoded + * custom binary data structure. Within an XML element, naturally. + * + * I hereby announce this specification to be the 2015 winner of the + * prestigious "UEFI HII API" award for incompetent design. + */ + +/** Discovery request format */ +#define PEERDIST_DISCOVERY_REQUEST \ + "" \ + "" \ + "" \ + "" \ + "urn:schemas-xmlsoap-org:ws:2005:04:discovery" \ + "" \ + "" \ + "http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe" \ + "" \ + "" \ + "urn:uuid:%s" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "PeerDist:PeerDistData" \ + "" \ + "" \ + "%s" \ + "" \ + "" \ + "" \ + "" + +/** + * Construct discovery request + * + * @v uuid Message UUID string + * @v id Segment identifier string + * @ret request Discovery request, or NULL on failure + * + * The request is dynamically allocated; the caller must eventually + * free() the request. + */ +char * peerdist_discovery_request ( const char *uuid, const char *id ) { + char *request; + int len; + + /* Construct request */ + len = asprintf ( &request, PEERDIST_DISCOVERY_REQUEST, uuid, id ); + if ( len < 0 ) + return NULL; + + return request; +} + +/** + * Locate discovery reply tag + * + * @v data Reply data (not NUL-terminated) + * @v len Length of reply data + * @v tag XML tag + * @ret found Found tag (or NULL if not found) + */ +static char * peerdist_discovery_reply_tag ( char *data, size_t len, + const char *tag ) { + size_t tag_len = strlen ( tag ); + + /* Search, allowing for the fact that the reply data is not + * cleanly NUL-terminated and may contain embedded NULs due to + * earlier parsing. + */ + for ( ; len >= tag_len ; data++, len-- ) { + if ( strncmp ( data, tag, tag_len ) == 0 ) + return data; + } + return NULL; +} + +/** + * Locate discovery reply values + * + * @v data Reply data (not NUL-terminated, will be modified) + * @v len Length of reply data + * @v name XML tag name + * @ret values Tag values (or NULL if not found) + * + * The reply data is modified by adding NULs and moving characters as + * needed to produce a NUL-separated list of values, terminated with a + * zero-length string. + * + * This is not supposed to be a full XML parser; it's supposed to + * include just enough functionality to allow PeerDist discovery to + * work with existing implementations. + */ +static char * peerdist_discovery_reply_values ( char *data, size_t len, + const char *name ) { + char buf[ 2 /* "" */ + 1 /* NUL */ ]; + char *open; + char *close; + char *start; + char *end; + char *in; + char *out; + char c; + + /* Locate opening tag */ + snprintf ( buf, sizeof ( buf ), "<%s>", name ); + open = peerdist_discovery_reply_tag ( data, len, buf ); + if ( ! open ) + return NULL; + start = ( open + strlen ( buf ) ); + len -= ( start - data ); + data = start; + + /* Locate closing tag */ + snprintf ( buf, sizeof ( buf ), "", name ); + close = peerdist_discovery_reply_tag ( data, len, buf ); + if ( ! close ) + return NULL; + assert ( close >= open ); + end = close; + + /* Strip initial whitespace, convert other whitespace + * sequences to single NULs, add terminating pair of NULs. + * This will probably overwrite part of the closing tag. + */ + for ( in = start, out = start ; in < end ; in++ ) { + c = *in; + if ( isspace ( c ) ) { + if ( ( out > start ) && ( out[-1] != '\0' ) ) + *(out++) = '\0'; + } else { + *(out++) = c; + } + } + *(out++) = '\0'; + *(out++) = '\0'; + assert ( out < ( close + strlen ( buf ) ) ); + + return start; +} + +/** + * Parse discovery reply + * + * @v data Reply data (not NUL-terminated, will be modified) + * @v len Length of reply data + * @v reply Discovery reply to fill in + * @ret rc Return status code + * + * The discovery reply includes pointers to strings within the + * modified reply data. + */ +int peerdist_discovery_reply ( char *data, size_t len, + struct peerdist_discovery_reply *reply ) { + static const struct peerdist_discovery_block_count zcount = { + .hex = "00000000", + }; + struct peerdist_discovery_block_count *count; + unsigned int max; + unsigned int i; + char *scopes; + char *xaddrs; + char *blockcount; + char *in; + char *out; + size_t skip; + + /* Find tag */ + scopes = peerdist_discovery_reply_values ( data, len, "wsd:Scopes" ); + if ( ! scopes ) { + DBGC ( reply, "PCCRD %p missing tag\n", reply ); + return -ENOENT; + } + + /* Find tag */ + xaddrs = peerdist_discovery_reply_values ( data, len, "wsd:XAddrs" ); + if ( ! xaddrs ) { + DBGC ( reply, "PCCRD %p missing tag\n", reply ); + return -ENOENT; + } + + /* Find tag */ + blockcount = peerdist_discovery_reply_values ( data, len, + "PeerDist:BlockCount" ); + if ( ! blockcount ) { + DBGC ( reply, "PCCRD %p missing tag\n", + reply ); + return -ENOENT; + } + + /* Determine maximum number of segments (according to number + * of entries in the block count list). + */ + max = ( strlen ( blockcount ) / sizeof ( *count ) ); + count = container_of ( blockcount, + struct peerdist_discovery_block_count, hex[0] ); + + /* Eliminate any segments with a zero block count */ + for ( i = 0, in = scopes, out = scopes ; *in ; i++, in += skip ) { + + /* Fail if we have overrun the maximum number of segments */ + if ( i >= max ) { + DBGC ( reply, "PCCRD %p too many segment IDs\n", + reply ); + return -EPROTO; + } + + /* Delete segment if block count is zero */ + skip = ( strlen ( in ) + 1 /* NUL */ ); + if ( memcmp ( count[i].hex, zcount.hex, + sizeof ( zcount.hex ) ) == 0 ) + continue; + strcpy ( out, in ); + out += skip; + } + out[0] = '\0'; /* Ensure list is terminated with a zero-length string */ + + /* Fill in discovery reply */ + reply->ids = scopes; + reply->locations = xaddrs; + + return 0; +} diff --git a/qemu/roms/ipxe/src/net/peerblk.c b/qemu/roms/ipxe/src/net/peerblk.c new file mode 100644 index 000000000..fd7ea0893 --- /dev/null +++ b/qemu/roms/ipxe/src/net/peerblk.c @@ -0,0 +1,1366 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Peer Content Caching and Retrieval (PeerDist) protocol block downloads + * + */ + +/** PeerDist decryption chunksize + * + * This is a policy decision. + */ +#define PEERBLK_DECRYPT_CHUNKSIZE 2048 + +/** PeerDist raw block download attempt initial progress timeout + * + * This is a policy decision. + */ +#define PEERBLK_RAW_OPEN_TIMEOUT ( 10 * TICKS_PER_SEC ) + +/** PeerDist raw block download attempt ongoing progress timeout + * + * This is a policy decision. + */ +#define PEERBLK_RAW_RX_TIMEOUT ( 15 * TICKS_PER_SEC ) + +/** PeerDist retrieval protocol block download attempt initial progress timeout + * + * This is a policy decision. + */ +#define PEERBLK_RETRIEVAL_OPEN_TIMEOUT ( 3 * TICKS_PER_SEC ) + +/** PeerDist retrieval protocol block download attempt ongoing progress timeout + * + * This is a policy decision. + */ +#define PEERBLK_RETRIEVAL_RX_TIMEOUT ( 5 * TICKS_PER_SEC ) + +/** PeerDist maximum number of full download attempt cycles + * + * This is the maximum number of times that we will try a full cycle + * of download attempts (i.e. a retrieval protocol download attempt + * from each discovered peer plus a raw download attempt from the + * origin server). + * + * This is a policy decision. + */ +#define PEERBLK_MAX_ATTEMPT_CYCLES 4 + +/** PeerDist block download profiler */ +static struct profiler peerblk_download_profiler __profiler = + { .name = "peerblk.download" }; + +/** PeerDist block download attempt success profiler */ +static struct profiler peerblk_attempt_success_profiler __profiler = + { .name = "peerblk.attempt.success" }; + +/** PeerDist block download attempt failure profiler */ +static struct profiler peerblk_attempt_failure_profiler __profiler = + { .name = "peerblk.attempt.failure" }; + +/** PeerDist block download attempt timeout profiler */ +static struct profiler peerblk_attempt_timeout_profiler __profiler = + { .name = "peerblk.attempt.timeout" }; + +/** PeerDist block download discovery success profiler */ +static struct profiler peerblk_discovery_success_profiler __profiler = + { .name = "peerblk.discovery.success" }; + +/** PeerDist block download discovery timeout profiler */ +static struct profiler peerblk_discovery_timeout_profiler __profiler = + { .name = "peerblk.discovery.timeout" }; + +/** + * Get profiling timestamp + * + * @ret timestamp Timestamp + */ +static inline __attribute__ (( always_inline )) unsigned long +peerblk_timestamp ( void ) { + + if ( PROFILING ) { + return currticks(); + } else { + return 0; + } +} + +/** + * Free PeerDist block download + * + * @v refcnt Reference count + */ +static void peerblk_free ( struct refcnt *refcnt ) { + struct peerdist_block *peerblk = + container_of ( refcnt, struct peerdist_block, refcnt ); + + uri_put ( peerblk->uri ); + free ( peerblk->cipherctx ); + free ( peerblk ); +} + +/** + * Reset PeerDist block download attempt + * + * @v peerblk PeerDist block download + * @v rc Reason for reset + */ +static void peerblk_reset ( struct peerdist_block *peerblk, int rc ) { + + /* Stop decryption process */ + process_del ( &peerblk->process ); + + /* Stop timer */ + stop_timer ( &peerblk->timer ); + + /* Abort any current download attempt */ + intf_restart ( &peerblk->raw, rc ); + intf_restart ( &peerblk->retrieval, rc ); + + /* Empty received data buffer */ + xferbuf_free ( &peerblk->buffer ); + peerblk->pos = 0; + + /* Reset digest and free cipher context */ + digest_init ( peerblk->digest, peerblk->digestctx ); + free ( peerblk->cipherctx ); + peerblk->cipherctx = NULL; + peerblk->cipher = NULL; + + /* Reset trim thresholds */ + peerblk->start = ( peerblk->trim.start - peerblk->range.start ); + peerblk->end = ( peerblk->trim.end - peerblk->range.start ); + assert ( peerblk->start <= peerblk->end ); +} + +/** + * Close PeerDist block download + * + * @v peerblk PeerDist block download + * @v rc Reason for close + */ +static void peerblk_close ( struct peerdist_block *peerblk, int rc ) { + unsigned long now = peerblk_timestamp(); + + /* Profile overall block download */ + profile_custom ( &peerblk_download_profiler, + ( now - peerblk->started ) ); + + /* Reset download attempt */ + peerblk_reset ( peerblk, rc ); + + /* Close discovery */ + peerdisc_close ( &peerblk->discovery ); + + /* Shut down all interfaces */ + intf_shutdown ( &peerblk->retrieval, rc ); + intf_shutdown ( &peerblk->raw, rc ); + intf_shutdown ( &peerblk->xfer, rc ); +} + +/** + * Calculate offset within overall download + * + * @v peerblk PeerDist block download + * @v pos Position within incoming data stream + * @ret offset Offset within overall download + */ +static inline __attribute__ (( always_inline )) size_t +peerblk_offset ( struct peerdist_block *peerblk, size_t pos ) { + + return ( ( pos - peerblk->start ) + peerblk->offset ); +} + +/** + * Deliver download attempt data block + * + * @v peerblk PeerDist block download + * @v iobuf I/O buffer + * @v meta Original data transfer metadata + * @v pos Position within incoming data stream + * @ret rc Return status code + */ +static int peerblk_deliver ( struct peerdist_block *peerblk, + struct io_buffer *iobuf, + struct xfer_metadata *meta, size_t pos ) { + struct xfer_metadata xfer_meta; + size_t len = iob_len ( iobuf ); + size_t start = pos; + size_t end = ( pos + len ); + int rc; + + /* Discard zero-length packets and packets which lie entirely + * outside the trimmed range. + */ + if ( ( start >= peerblk->end ) || ( end <= peerblk->start ) || + ( len == 0 ) ) { + free_iob ( iobuf ); + return 0; + } + + /* Truncate data to within trimmed range */ + if ( start < peerblk->start ) { + iob_pull ( iobuf, ( peerblk->start - start ) ); + start = peerblk->start; + } + if ( end > peerblk->end ) { + iob_unput ( iobuf, ( end - peerblk->end ) ); + end = peerblk->end; + } + + /* Construct metadata */ + memcpy ( &xfer_meta, meta, sizeof ( xfer_meta ) ); + xfer_meta.flags |= XFER_FL_ABS_OFFSET; + xfer_meta.offset = peerblk_offset ( peerblk, start ); + + /* Deliver data */ + if ( ( rc = xfer_deliver ( &peerblk->xfer, iob_disown ( iobuf ), + &xfer_meta ) ) != 0 ) { + DBGC ( peerblk, "PEERBLK %p %d.%d could not deliver data: %s\n", + peerblk, peerblk->segment, peerblk->block, + strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Finish PeerDist block download attempt + * + * @v peerblk PeerDist block download + * @v rc Reason for close + */ +static void peerblk_done ( struct peerdist_block *peerblk, int rc ) { + struct digest_algorithm *digest = peerblk->digest; + uint8_t hash[digest->digestsize]; + unsigned long now = peerblk_timestamp(); + + /* Check for errors on completion */ + if ( rc != 0 ) { + DBGC ( peerblk, "PEERBLK %p %d.%d attempt failed: %s\n", + peerblk, peerblk->segment, peerblk->block, + strerror ( rc ) ); + goto err; + } + + /* Check digest */ + digest_final ( digest, peerblk->digestctx, hash ); + if ( memcmp ( hash, peerblk->hash, peerblk->digestsize ) != 0 ) { + DBGC ( peerblk, "PEERBLK %p %d.%d digest mismatch:\n", + peerblk, peerblk->segment, peerblk->block ); + DBGC_HDA ( peerblk, 0, hash, peerblk->digestsize ); + DBGC_HDA ( peerblk, 0, peerblk->hash, peerblk->digestsize ); + rc = -EIO; + goto err; + } + + /* Profile successful attempt */ + profile_custom ( &peerblk_attempt_success_profiler, + ( now - peerblk->attempted ) ); + + /* Close download */ + peerblk_close ( peerblk, 0 ); + return; + + err: + /* Record failure reason and schedule a retry attempt */ + profile_custom ( &peerblk_attempt_failure_profiler, + ( now - peerblk->attempted ) ); + peerblk_reset ( peerblk, rc ); + peerblk->rc = rc; + start_timer_nodelay ( &peerblk->timer ); +} + +/****************************************************************************** + * + * Raw block download attempts (using an HTTP range request) + * + ****************************************************************************** + */ + +/** + * Open PeerDist raw block download attempt + * + * @v peerblk PeerDist block download + * @ret rc Return status code + */ +static int peerblk_raw_open ( struct peerdist_block *peerblk ) { + struct http_request_range range; + int rc; + + DBGC2 ( peerblk, "PEERBLK %p %d.%d attempting raw range request\n", + peerblk, peerblk->segment, peerblk->block ); + + /* Construct HTTP range */ + memset ( &range, 0, sizeof ( range ) ); + range.start = peerblk->range.start; + range.len = ( peerblk->range.end - peerblk->range.start ); + + /* Initiate range request to retrieve block */ + if ( ( rc = http_open ( &peerblk->raw, &http_get, peerblk->uri, + &range, NULL ) ) != 0 ) { + DBGC ( peerblk, "PEERBLK %p %d.%d could not create range " + "request: %s\n", peerblk, peerblk->segment, + peerblk->block, strerror ( rc ) ); + return rc; + } + + /* Annul HTTP connection (for testing) if applicable. Do not + * report as an immediate error, in order to test our ability + * to recover from a totally unresponsive HTTP server. + */ + if ( inject_fault ( PEERBLK_ANNUL_RATE ) ) + intf_restart ( &peerblk->raw, 0 ); + + return 0; +} + +/** + * Receive PeerDist raw data + * + * @v peerblk PeerDist block download + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int peerblk_raw_rx ( struct peerdist_block *peerblk, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + size_t len = iob_len ( iobuf ); + size_t pos = peerblk->pos; + size_t mid = ( ( peerblk->range.end - peerblk->range.start ) / 2 ); + int rc; + + /* Corrupt received data (for testing) if applicable */ + inject_corruption ( PEERBLK_CORRUPT_RATE, iobuf->data, len ); + + /* Fail if data is delivered out of order, since the streaming + * digest requires strict ordering. + */ + if ( ( rc = xfer_check_order ( meta, &peerblk->pos, len ) ) != 0 ) + goto err; + + /* Add data to digest */ + digest_update ( peerblk->digest, peerblk->digestctx, iobuf->data, len ); + + /* Deliver data */ + if ( ( rc = peerblk_deliver ( peerblk, iob_disown ( iobuf ), meta, + pos ) ) != 0 ) + goto err; + + /* Extend download attempt timer */ + start_timer_fixed ( &peerblk->timer, PEERBLK_RAW_RX_TIMEOUT ); + + /* Stall download attempt (for testing) if applicable */ + if ( ( pos < mid ) && ( ( pos + len ) >= mid ) && + ( ( rc = inject_fault ( PEERBLK_STALL_RATE ) ) != 0 ) ) { + intf_restart ( &peerblk->raw, rc ); + } + + return 0; + + err: + free_iob ( iobuf ); + peerblk_done ( peerblk, rc ); + return rc; +} + +/** + * Close PeerDist raw block download attempt + * + * @v peerblk PeerDist block download + * @v rc Reason for close + */ +static void peerblk_raw_close ( struct peerdist_block *peerblk, int rc ) { + + /* Restart interface */ + intf_restart ( &peerblk->raw, rc ); + + /* Fail immediately if we have an error */ + if ( rc != 0 ) + goto done; + + /* Abort download attempt (for testing) if applicable */ + if ( ( rc = inject_fault ( PEERBLK_ABORT_RATE ) ) != 0 ) + goto done; + + done: + /* Complete download attempt */ + peerblk_done ( peerblk, rc ); +} + +/****************************************************************************** + * + * Retrieval protocol block download attempts (using HTTP POST) + * + ****************************************************************************** + */ + +/** + * Construct PeerDist retrieval protocol URI + * + * @v location Peer location + * @ret uri Retrieval URI, or NULL on error + */ +static struct uri * peerblk_retrieval_uri ( const char *location ) { + char uri_string[ 7 /* "http://" */ + strlen ( location ) + + sizeof ( PEERDIST_MAGIC_PATH /* includes NUL */ ) ]; + + /* Construct URI string */ + snprintf ( uri_string, sizeof ( uri_string ), + ( "http://%s" PEERDIST_MAGIC_PATH ), location ); + + /* Parse URI string */ + return parse_uri ( uri_string ); +} + +/** + * Open PeerDist retrieval protocol block download attempt + * + * @v peerblk PeerDist block download + * @v location Peer location + * @ret rc Return status code + */ +static int peerblk_retrieval_open ( struct peerdist_block *peerblk, + const char *location ) { + size_t digestsize = peerblk->digestsize; + peerdist_msg_getblks_t ( digestsize, 1, 0 ) req; + peerblk_msg_blk_t ( digestsize, 0, 0, 0 ) *rsp; + struct http_request_content content; + struct uri *uri; + int rc; + + DBGC2 ( peerblk, "PEERBLK %p %d.%d attempting retrieval from %s\n", + peerblk, peerblk->segment, peerblk->block, location ); + + /* Construct block fetch request */ + memset ( &req, 0, sizeof ( req ) ); + req.getblks.hdr.version.raw = htonl ( PEERDIST_MSG_GETBLKS_VERSION ); + req.getblks.hdr.type = htonl ( PEERDIST_MSG_GETBLKS_TYPE ); + req.getblks.hdr.len = htonl ( sizeof ( req ) ); + req.getblks.hdr.algorithm = htonl ( PEERDIST_MSG_AES_128_CBC ); + req.segment.segment.digestsize = htonl ( digestsize ); + memcpy ( req.segment.id, peerblk->id, digestsize ); + req.ranges.ranges.count = htonl ( 1 ); + req.ranges.range[0].first = htonl ( peerblk->block ); + req.ranges.range[0].count = htonl ( 1 ); + + /* Construct POST request content */ + memset ( &content, 0, sizeof ( content ) ); + content.data = &req; + content.len = sizeof ( req ); + + /* Construct URI */ + if ( ( uri = peerblk_retrieval_uri ( location ) ) == NULL ) { + rc = -ENOMEM; + goto err_uri; + } + + /* Update trim thresholds */ + peerblk->start += offsetof ( typeof ( *rsp ), msg.vrf ); + peerblk->end += offsetof ( typeof ( *rsp ), msg.vrf ); + + /* Initiate HTTP POST to retrieve block */ + if ( ( rc = http_open ( &peerblk->retrieval, &http_post, uri, + NULL, &content ) ) != 0 ) { + DBGC ( peerblk, "PEERBLK %p %d.%d could not create retrieval " + "request: %s\n", peerblk, peerblk->segment, + peerblk->block, strerror ( rc ) ); + goto err_open; + } + + /* Annul HTTP connection (for testing) if applicable. Do not + * report as an immediate error, in order to test our ability + * to recover from a totally unresponsive HTTP server. + */ + if ( inject_fault ( PEERBLK_ANNUL_RATE ) ) + intf_restart ( &peerblk->retrieval, 0 ); + + err_open: + uri_put ( uri ); + err_uri: + return rc; +} + +/** + * Receive PeerDist retrieval protocol data + * + * @v peerblk PeerDist block download + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int peerblk_retrieval_rx ( struct peerdist_block *peerblk, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + size_t len = iob_len ( iobuf ); + size_t start; + size_t end; + size_t before; + size_t after; + size_t cut; + int rc; + + /* Some genius at Microsoft thought it would be a great idea + * to place the AES-CBC initialisation vector *after* the + * encrypted data, thereby making it logically impossible to + * decrypt each packet as it arrives. + * + * To work around this mindless stupidity, we deliver the + * ciphertext as-is and later use xfer_buffer() to obtain + * access to the underlying data transfer buffer in order to + * perform the decryption. + * + * There will be some data both before and after the bytes + * corresponding to the trimmed plaintext: a MSG_BLK + * header/footer, some block padding for the AES-CBC cipher, + * and a possibly large quantity of unwanted ciphertext which + * is excluded from the trimmed content range. We store this + * data in a local data transfer buffer. If the amount of + * data to be stored is too large, we will fail allocation and + * so eventually fall back to using a range request (which + * does not require this kind of temporary storage + * allocation). + */ + + /* Corrupt received data (for testing) if applicable */ + inject_corruption ( PEERBLK_CORRUPT_RATE, iobuf->data, len ); + + /* Calculate start and end positions of this buffer */ + start = peerblk->pos; + if ( meta->flags & XFER_FL_ABS_OFFSET ) + start = 0; + start += meta->offset; + end = ( start + len ); + + /* Buffer any data before the trimmed content */ + if ( ( start < peerblk->start ) && ( len > 0 ) ) { + + /* Calculate length of data before the trimmed content */ + before = ( peerblk->start - start ); + if ( before > len ) + before = len; + + /* Buffer data before the trimmed content */ + if ( ( rc = xferbuf_write ( &peerblk->buffer, start, + iobuf->data, before ) ) != 0 ) { + DBGC ( peerblk, "PEERBLK %p %d.%d could not buffer " + "data: %s\n", peerblk, peerblk->segment, + peerblk->block, strerror ( rc ) ); + goto err; + } + } + + /* Buffer any data after the trimmed content */ + if ( ( end > peerblk->end ) && ( len > 0 ) ) { + + /* Calculate length of data after the trimmed content */ + after = ( end - peerblk->end ); + if ( after > len ) + after = len; + + /* Buffer data after the trimmed content */ + cut = ( peerblk->end - peerblk->start ); + if ( ( rc = xferbuf_write ( &peerblk->buffer, + ( end - after - cut ), + ( iobuf->data + len - after ), + after ) ) != 0 ) { + DBGC ( peerblk, "PEERBLK %p %d.%d could not buffer " + "data: %s\n", peerblk, peerblk->segment, + peerblk->block, strerror ( rc ) ); + goto err; + } + } + + /* Deliver any remaining data */ + if ( ( rc = peerblk_deliver ( peerblk, iob_disown ( iobuf ), meta, + start ) ) != 0 ) + goto err; + + /* Update position */ + peerblk->pos = end; + + /* Extend download attempt timer */ + start_timer_fixed ( &peerblk->timer, PEERBLK_RETRIEVAL_RX_TIMEOUT ); + + /* Stall download attempt (for testing) if applicable */ + if ( ( start < peerblk->end ) && ( end >= peerblk->end ) && + ( ( rc = inject_fault ( PEERBLK_STALL_RATE ) ) != 0 ) ) { + intf_restart ( &peerblk->retrieval, rc ); + } + + return 0; + + err: + free_iob ( iobuf ); + peerblk_done ( peerblk, rc ); + return rc; +} + +/** + * Parse retrieval protocol message header + * + * @v peerblk PeerDist block download + * @ret rc Return status code + */ +static int peerblk_parse_header ( struct peerdist_block *peerblk ) { + struct { + struct peerdist_msg_transport_header hdr; + struct peerdist_msg_header msg; + } __attribute__ (( packed )) *msg = peerblk->buffer.data; + struct cipher_algorithm *cipher; + size_t len = peerblk->buffer.len; + size_t keylen = 0; + int rc; + + /* Check message length */ + if ( len < sizeof ( *msg ) ) { + DBGC ( peerblk, "PEERBLK %p %d.%d message too short for header " + "(%zd bytes)\n", peerblk, peerblk->segment, + peerblk->block, len ); + return -ERANGE; + } + + /* Check message type */ + if ( msg->msg.type != htonl ( PEERDIST_MSG_BLK_TYPE ) ) { + DBGC ( peerblk, "PEERBLK %p %d.%d unexpected message type " + "%#08x\n", peerblk, peerblk->segment, peerblk->block, + ntohl ( msg->msg.type ) ); + return -EPROTO; + } + + /* Determine cipher algorithm and key length */ + cipher = &aes_cbc_algorithm; + switch ( msg->msg.algorithm ) { + case htonl ( PEERDIST_MSG_PLAINTEXT ) : + cipher = NULL; + break; + case htonl ( PEERDIST_MSG_AES_128_CBC ) : + keylen = ( 128 / 8 ); + break; + case htonl ( PEERDIST_MSG_AES_192_CBC ) : + keylen = ( 192 / 8 ); + break; + case htonl ( PEERDIST_MSG_AES_256_CBC ) : + keylen = ( 256 / 8 ); + break; + default: + DBGC ( peerblk, "PEERBLK %p %d.%d unrecognised algorithm " + "%#08x\n", peerblk, peerblk->segment, peerblk->block, + ntohl ( msg->msg.algorithm ) ); + return -ENOTSUP; + } + DBGC2 ( peerblk, "PEERBLK %p %d.%d using %s with %zd-bit key\n", + peerblk, peerblk->segment, peerblk->block, + ( cipher ? cipher->name : "plaintext" ), ( 8 * keylen ) ); + + /* Sanity check key length against maximum secret length */ + if ( keylen > peerblk->digestsize ) { + DBGC ( peerblk, "PEERBLK %p %d.%d %zd-byte secret too short " + "for %zd-bit key\n", peerblk, peerblk->segment, + peerblk->block, peerblk->digestsize, ( 8 * keylen ) ); + return -EPROTO; + } + + /* Allocate cipher context. Freeing the cipher context (on + * error or otherwise) is handled by peerblk_reset(). + */ + peerblk->cipher = cipher; + assert ( peerblk->cipherctx == NULL ); + peerblk->cipherctx = malloc ( cipher->ctxsize ); + if ( ! peerblk->cipherctx ) + return -ENOMEM; + + /* Initialise cipher */ + if ( ( rc = cipher_setkey ( cipher, peerblk->cipherctx, peerblk->secret, + keylen ) ) != 0 ) { + DBGC ( peerblk, "PEERBLK %p %d.%d could not set key: %s\n", + peerblk, peerblk->segment, peerblk->block, + strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Parse retrieval protocol message segment and block details + * + * @v peerblk PeerDist block download + * @v buf_len Length of buffered data to fill in + * @ret rc Return status code + */ +static int peerblk_parse_block ( struct peerdist_block *peerblk, + size_t *buf_len ) { + size_t digestsize = peerblk->digestsize; + peerblk_msg_blk_t ( digestsize, 0, 0, 0 ) *msg = peerblk->buffer.data; + size_t len = peerblk->buffer.len; + size_t data_len; + size_t total; + + /* Check message length */ + if ( len < offsetof ( typeof ( *msg ), msg.block.data ) ) { + DBGC ( peerblk, "PEERBLK %p %d.%d message too short for " + "zero-length data (%zd bytes)\n", peerblk, + peerblk->segment, peerblk->block, len ); + return -ERANGE; + } + + /* Check digest size */ + if ( ntohl ( msg->msg.segment.segment.digestsize ) != digestsize ) { + DBGC ( peerblk, "PEERBLK %p %d.%d incorrect digest size %d\n", + peerblk, peerblk->segment, peerblk->block, + ntohl ( msg->msg.segment.segment.digestsize ) ); + return -EPROTO; + } + + /* Check segment ID */ + if ( memcmp ( msg->msg.segment.id, peerblk->id, digestsize ) != 0 ) { + DBGC ( peerblk, "PEERBLK %p %d.%d segment ID mismatch\n", + peerblk, peerblk->segment, peerblk->block ); + return -EPROTO; + } + + /* Check block ID */ + if ( ntohl ( msg->msg.index ) != peerblk->block ) { + DBGC ( peerblk, "PEERBLK %p %d.%d block ID mismatch (got %d)\n", + peerblk, peerblk->segment, peerblk->block, + ntohl ( msg->msg.index ) ); + return -EPROTO; + } + + /* Check for missing blocks */ + data_len = be32_to_cpu ( msg->msg.block.block.len ); + if ( ! data_len ) { + DBGC ( peerblk, "PEERBLK %p %d.%d block not found\n", + peerblk, peerblk->segment, peerblk->block ); + return -ENOENT; + } + + /* Check for underlength blocks */ + if ( data_len < ( peerblk->range.end - peerblk->range.start ) ) { + DBGC ( peerblk, "PEERBLK %p %d.%d underlength block (%zd " + "bytes)\n", peerblk, peerblk->segment, peerblk->block, + data_len ); + return -ERANGE; + } + + /* Calculate buffered data length (i.e. excluding data which + * was delivered to the final data transfer buffer). + */ + *buf_len = ( data_len - ( peerblk->end - peerblk->start ) ); + + /* Describe data before the trimmed content */ + peerblk->decrypt[PEERBLK_BEFORE].xferbuf = &peerblk->buffer; + peerblk->decrypt[PEERBLK_BEFORE].offset = + offsetof ( typeof ( *msg ), msg.block.data ); + peerblk->decrypt[PEERBLK_BEFORE].len = + ( peerblk->start - + offsetof ( typeof ( *msg ), msg.block.data ) ); + total = peerblk->decrypt[PEERBLK_BEFORE].len; + + /* Describe data within the trimmed content */ + peerblk->decrypt[PEERBLK_DURING].offset = + peerblk_offset ( peerblk, peerblk->start ); + peerblk->decrypt[PEERBLK_DURING].len = + ( peerblk->end - peerblk->start ); + total += peerblk->decrypt[PEERBLK_DURING].len; + + /* Describe data after the trimmed content */ + peerblk->decrypt[PEERBLK_AFTER].xferbuf = &peerblk->buffer; + peerblk->decrypt[PEERBLK_AFTER].offset = peerblk->start; + peerblk->decrypt[PEERBLK_AFTER].len = + ( offsetof ( typeof ( *msg ), msg.block.data ) + + *buf_len - peerblk->start ); + total += peerblk->decrypt[PEERBLK_AFTER].len; + + /* Sanity check */ + assert ( total == be32_to_cpu ( msg->msg.block.block.len ) ); + + /* Initialise cipher and digest lengths */ + peerblk->cipher_remaining = total; + peerblk->digest_remaining = + ( peerblk->range.end - peerblk->range.start ); + assert ( peerblk->cipher_remaining >= peerblk->digest_remaining ); + + return 0; +} + +/** + * Parse retrieval protocol message useless details + * + * @v peerblk PeerDist block download + * @v buf_len Length of buffered data + * @v vrf_len Length of uselessness to fill in + * @ret rc Return status code + */ +static int peerblk_parse_useless ( struct peerdist_block *peerblk, + size_t buf_len, size_t *vrf_len ) { + size_t digestsize = peerblk->digestsize; + peerblk_msg_blk_t ( digestsize, buf_len, 0, 0 ) *msg = + peerblk->buffer.data; + size_t len = peerblk->buffer.len; + + /* Check message length */ + if ( len < offsetof ( typeof ( *msg ), msg.vrf.data ) ) { + DBGC ( peerblk, "PEERBLK %p %d.%d message too short for " + "zero-length uselessness (%zd bytes)\n", peerblk, + peerblk->segment, peerblk->block, len ); + return -ERANGE; + } + + /* Extract length of uselessness */ + *vrf_len = be32_to_cpu ( msg->msg.vrf.vrf.len ); + + return 0; +} + +/** + * Parse retrieval protocol message initialisation vector details + * + * @v peerblk PeerDist block download + * @v buf_len Length of buffered data + * @v vrf_len Length of uselessness + * @ret rc Return status code + */ +static int peerblk_parse_iv ( struct peerdist_block *peerblk, size_t buf_len, + size_t vrf_len ) { + size_t digestsize = peerblk->digestsize; + size_t blksize = peerblk->cipher->blocksize; + peerblk_msg_blk_t ( digestsize, buf_len, vrf_len, blksize ) *msg = + peerblk->buffer.data; + size_t len = peerblk->buffer.len; + + /* Check message length */ + if ( len < sizeof ( *msg ) ) { + DBGC ( peerblk, "PEERBLK %p %d.%d message too short for " + "initialisation vector (%zd bytes)\n", peerblk, + peerblk->segment, peerblk->block, len ); + return -ERANGE; + } + + /* Check initialisation vector size */ + if ( ntohl ( msg->msg.iv.iv.blksize ) != blksize ) { + DBGC ( peerblk, "PEERBLK %p %d.%d incorrect IV size %d\n", + peerblk, peerblk->segment, peerblk->block, + ntohl ( msg->msg.iv.iv.blksize ) ); + return -EPROTO; + } + + /* Set initialisation vector */ + cipher_setiv ( peerblk->cipher, peerblk->cipherctx, msg->msg.iv.data ); + + return 0; +} + +/** + * Read from decryption buffers + * + * @v peerblk PeerDist block download + * @v data Data buffer + * @v len Length to read + * @ret rc Return status code + */ +static int peerblk_decrypt_read ( struct peerdist_block *peerblk, + void *data, size_t len ) { + struct peerdist_block_decrypt *decrypt = peerblk->decrypt; + size_t frag_len; + int rc; + + /* Read from each decryption buffer in turn */ + for ( ; len ; decrypt++, data += frag_len, len -= frag_len ) { + + /* Calculate length to use from this buffer */ + frag_len = decrypt->len; + if ( frag_len > len ) + frag_len = len; + if ( ! frag_len ) + continue; + + /* Read from this buffer */ + if ( ( rc = xferbuf_read ( decrypt->xferbuf, decrypt->offset, + data, frag_len ) ) != 0 ) + return rc; + } + + return 0; +} + +/** + * Write to decryption buffers and update offsets and lengths + * + * @v peerblk PeerDist block download + * @v data Data buffer + * @v len Length to read + * @ret rc Return status code + */ +static int peerblk_decrypt_write ( struct peerdist_block *peerblk, + const void *data, size_t len ) { + struct peerdist_block_decrypt *decrypt = peerblk->decrypt; + size_t frag_len; + int rc; + + /* Write to each decryption buffer in turn */ + for ( ; len ; decrypt++, data += frag_len, len -= frag_len ) { + + /* Calculate length to use from this buffer */ + frag_len = decrypt->len; + if ( frag_len > len ) + frag_len = len; + if ( ! frag_len ) + continue; + + /* Write to this buffer */ + if ( ( rc = xferbuf_write ( decrypt->xferbuf, decrypt->offset, + data, frag_len ) ) != 0 ) + return rc; + + /* Update offset and length */ + decrypt->offset += frag_len; + decrypt->len -= frag_len; + } + + return 0; +} + +/** + * Decrypt one chunk of PeerDist retrieval protocol data + * + * @v peerblk PeerDist block download + */ +static void peerblk_decrypt ( struct peerdist_block *peerblk ) { + struct cipher_algorithm *cipher = peerblk->cipher; + struct digest_algorithm *digest = peerblk->digest; + struct xfer_buffer *xferbuf; + size_t cipher_len; + size_t digest_len; + void *data; + int rc; + + /* Sanity check */ + assert ( ( PEERBLK_DECRYPT_CHUNKSIZE % cipher->blocksize ) == 0 ); + + /* Get the underlying data transfer buffer */ + xferbuf = xfer_buffer ( &peerblk->xfer ); + if ( ! xferbuf ) { + DBGC ( peerblk, "PEERBLK %p %d.%d has no underlying data " + "transfer buffer\n", peerblk, peerblk->segment, + peerblk->block ); + rc = -ENOTSUP; + goto err_xfer_buffer; + } + peerblk->decrypt[PEERBLK_DURING].xferbuf = xferbuf; + + /* Calculate cipher and digest lengths */ + cipher_len = PEERBLK_DECRYPT_CHUNKSIZE; + if ( cipher_len > peerblk->cipher_remaining ) + cipher_len = peerblk->cipher_remaining; + digest_len = cipher_len; + if ( digest_len > peerblk->digest_remaining ) + digest_len = peerblk->digest_remaining; + assert ( ( cipher_len & ( cipher->blocksize - 1 ) ) == 0 ); + + /* Allocate temporary data buffer */ + data = malloc ( cipher_len ); + if ( ! data ) { + rc = -ENOMEM; + goto err_alloc_data; + } + + /* Read ciphertext */ + if ( ( rc = peerblk_decrypt_read ( peerblk, data, cipher_len ) ) != 0 ){ + DBGC ( peerblk, "PEERBLK %p %d.%d could not read ciphertext: " + "%s\n", peerblk, peerblk->segment, peerblk->block, + strerror ( rc ) ); + goto err_read; + } + + /* Decrypt data */ + cipher_decrypt ( cipher, peerblk->cipherctx, data, data, cipher_len ); + + /* Add data to digest */ + digest_update ( digest, peerblk->digestctx, data, digest_len ); + + /* Write plaintext */ + if ( ( rc = peerblk_decrypt_write ( peerblk, data, cipher_len ) ) != 0){ + DBGC ( peerblk, "PEERBLK %p %d.%d could not write plaintext: " + "%s\n", peerblk, peerblk->segment, peerblk->block, + strerror ( rc ) ); + goto err_write; + } + + /* Consume input */ + peerblk->cipher_remaining -= cipher_len; + peerblk->digest_remaining -= digest_len; + + /* Free temporary data buffer */ + free ( data ); + + /* Continue processing until all input is consumed */ + if ( peerblk->cipher_remaining ) + return; + + /* Complete download attempt */ + peerblk_done ( peerblk, 0 ); + return; + + err_write: + err_read: + free ( data ); + err_alloc_data: + err_xfer_buffer: + peerblk_done ( peerblk, rc ); +} + +/** + * Close PeerDist retrieval protocol block download attempt + * + * @v peerblk PeerDist block download + * @v rc Reason for close + */ +static void peerblk_retrieval_close ( struct peerdist_block *peerblk, int rc ) { + size_t buf_len; + size_t vrf_len; + + /* Restart interface */ + intf_restart ( &peerblk->retrieval, rc ); + + /* Fail immediately if we have an error */ + if ( rc != 0 ) + goto done; + + /* Abort download attempt (for testing) if applicable */ + if ( ( rc = inject_fault ( PEERBLK_ABORT_RATE ) ) != 0 ) + goto done; + + /* Parse message header */ + if ( ( rc = peerblk_parse_header ( peerblk ) ) != 0 ) + goto done; + + /* Parse message segment and block details */ + if ( ( rc = peerblk_parse_block ( peerblk, &buf_len ) ) != 0 ) + goto done; + + /* If the block was plaintext, then there is nothing more to do */ + if ( ! peerblk->cipher ) + goto done; + + /* Parse message useless details */ + if ( ( rc = peerblk_parse_useless ( peerblk, buf_len, &vrf_len ) ) != 0) + goto done; + + /* Parse message initialisation vector details */ + if ( ( rc = peerblk_parse_iv ( peerblk, buf_len, vrf_len ) ) != 0 ) + goto done; + + /* Fail if decryption length is not aligned to the cipher block size */ + if ( peerblk->cipher_remaining & ( peerblk->cipher->blocksize - 1 ) ) { + DBGC ( peerblk, "PEERBLK %p %d.%d unaligned data length %zd\n", + peerblk, peerblk->segment, peerblk->block, + peerblk->cipher_remaining ); + rc = -EPROTO; + goto done; + } + + /* Stop the download attempt timer: there is no point in + * timing out while decrypting. + */ + stop_timer ( &peerblk->timer ); + + /* Start decryption process */ + process_add ( &peerblk->process ); + return; + + done: + /* Complete download attempt */ + peerblk_done ( peerblk, rc ); +} + +/****************************************************************************** + * + * Retry policy + * + ****************************************************************************** + */ + +/** + * Handle PeerDist retry timer expiry + * + * @v timer Retry timer + * @v over Failure indicator + */ +static void peerblk_expired ( struct retry_timer *timer, int over __unused ) { + struct peerdist_block *peerblk = + container_of ( timer, struct peerdist_block, timer ); + struct peerdisc_segment *segment = peerblk->discovery.segment; + struct peerdisc_peer *head; + unsigned long now = peerblk_timestamp(); + const char *location; + int rc; + + /* Profile discovery timeout, if applicable */ + if ( ( peerblk->peer == NULL ) && ( timer->timeout != 0 ) ) { + profile_custom ( &peerblk_discovery_timeout_profiler, + ( now - peerblk->started ) ); + DBGC ( peerblk, "PEERBLK %p %d.%d discovery timed out after " + "%ld ticks\n", peerblk, peerblk->segment, + peerblk->block, timer->timeout ); + } + + /* Profile download timeout, if applicable */ + if ( ( peerblk->peer != NULL ) && ( timer->timeout != 0 ) ) { + profile_custom ( &peerblk_attempt_timeout_profiler, + ( now - peerblk->attempted ) ); + DBGC ( peerblk, "PEERBLK %p %d.%d timed out after %ld ticks\n", + peerblk, peerblk->segment, peerblk->block, + timer->timeout ); + } + + /* Abort any current download attempt */ + peerblk_reset ( peerblk, -ETIMEDOUT ); + + /* Record attempt start time */ + peerblk->attempted = now; + + /* If we have exceeded our maximum number of attempt cycles + * (each cycle comprising a retrieval protocol download from + * each peer in the list followed by a raw download from the + * origin server), then abort the overall download. + */ + head = list_entry ( &segment->peers, struct peerdisc_peer, list ); + if ( ( peerblk->peer == head ) && + ( ++peerblk->cycles >= PEERBLK_MAX_ATTEMPT_CYCLES ) ) { + rc = peerblk->rc; + assert ( rc != 0 ); + goto err; + } + + /* If we have not yet made any download attempts, then move to + * the start of the peer list. + */ + if ( peerblk->peer == NULL ) + peerblk->peer = head; + + /* Attempt retrieval protocol download from next usable peer */ + list_for_each_entry_continue ( peerblk->peer, &segment->peers, list ) { + + /* Attempt retrieval protocol download from this peer */ + location = peerblk->peer->location; + if ( ( rc = peerblk_retrieval_open ( peerblk, + location ) ) != 0 ) { + /* Non-fatal: continue to try next peer */ + continue; + } + + /* Start download attempt timer */ + peerblk->rc = -ETIMEDOUT; + start_timer_fixed ( &peerblk->timer, + PEERBLK_RETRIEVAL_OPEN_TIMEOUT ); + return; + } + + /* Attempt raw download */ + if ( ( rc = peerblk_raw_open ( peerblk ) ) != 0 ) + goto err; + + /* Start download attempt timer */ + peerblk->rc = -ETIMEDOUT; + start_timer_fixed ( &peerblk->timer, PEERBLK_RAW_OPEN_TIMEOUT ); + return; + + err: + peerblk_close ( peerblk, rc ); +} + +/** + * Handle PeerDist peer discovery + * + * @v discovery PeerDist discovery client + */ +static void peerblk_discovered ( struct peerdisc_client *discovery ) { + struct peerdist_block *peerblk = + container_of ( discovery, struct peerdist_block, discovery ); + unsigned long now = peerblk_timestamp(); + + /* Do nothing unless we are still waiting for the initial + * discovery timeout. + */ + if ( ( peerblk->peer != NULL ) || ( peerblk->timer.timeout == 0 ) ) + return; + + /* Schedule an immediate retry */ + start_timer_nodelay ( &peerblk->timer ); + + /* Profile discovery success */ + profile_custom ( &peerblk_discovery_success_profiler, + ( now - peerblk->started ) ); +} + +/****************************************************************************** + * + * Opener + * + ****************************************************************************** + */ + +/** PeerDist block download data transfer interface operations */ +static struct interface_operation peerblk_xfer_operations[] = { + INTF_OP ( intf_close, struct peerdist_block *, peerblk_close ), +}; + +/** PeerDist block download data transfer interface descriptor */ +static struct interface_descriptor peerblk_xfer_desc = + INTF_DESC ( struct peerdist_block, xfer, peerblk_xfer_operations ); + +/** PeerDist block download raw data interface operations */ +static struct interface_operation peerblk_raw_operations[] = { + INTF_OP ( xfer_deliver, struct peerdist_block *, peerblk_raw_rx ), + INTF_OP ( intf_close, struct peerdist_block *, peerblk_raw_close ), +}; + +/** PeerDist block download raw data interface descriptor */ +static struct interface_descriptor peerblk_raw_desc = + INTF_DESC ( struct peerdist_block, raw, peerblk_raw_operations ); + +/** PeerDist block download retrieval protocol interface operations */ +static struct interface_operation peerblk_retrieval_operations[] = { + INTF_OP ( xfer_deliver, struct peerdist_block *, peerblk_retrieval_rx ), + INTF_OP ( intf_close, struct peerdist_block *, peerblk_retrieval_close), +}; + +/** PeerDist block download retrieval protocol interface descriptor */ +static struct interface_descriptor peerblk_retrieval_desc = + INTF_DESC ( struct peerdist_block, retrieval, + peerblk_retrieval_operations ); + +/** PeerDist block download decryption process descriptor */ +static struct process_descriptor peerblk_process_desc = + PROC_DESC ( struct peerdist_block, process, peerblk_decrypt ); + +/** PeerDist block download discovery operations */ +static struct peerdisc_client_operations peerblk_discovery_operations = { + .discovered = peerblk_discovered, +}; + +/** + * Open PeerDist block download + * + * @v xfer Data transfer interface + * @v uri Original URI + * @v info Content information block + * @ret rc Return status code + */ +int peerblk_open ( struct interface *xfer, struct uri *uri, + struct peerdist_info_block *block ) { + const struct peerdist_info_segment *segment = block->segment; + const struct peerdist_info *info = segment->info; + struct digest_algorithm *digest = info->digest; + struct peerdist_block *peerblk; + unsigned long timeout; + size_t digestsize; + int rc; + + /* Allocate and initialise structure */ + peerblk = zalloc ( sizeof ( *peerblk ) + digest->ctxsize ); + if ( ! peerblk ) { + rc = -ENOMEM; + goto err_alloc; + } + ref_init ( &peerblk->refcnt, peerblk_free ); + intf_init ( &peerblk->xfer, &peerblk_xfer_desc, &peerblk->refcnt ); + intf_init ( &peerblk->raw, &peerblk_raw_desc, &peerblk->refcnt ); + intf_init ( &peerblk->retrieval, &peerblk_retrieval_desc, + &peerblk->refcnt ); + peerblk->uri = uri_get ( uri ); + memcpy ( &peerblk->range, &block->range, sizeof ( peerblk->range ) ); + memcpy ( &peerblk->trim, &block->trim, sizeof ( peerblk->trim ) ); + peerblk->offset = ( block->trim.start - info->trim.start ); + peerblk->digest = info->digest; + peerblk->digestsize = digestsize = info->digestsize; + peerblk->digestctx = ( ( ( void * ) peerblk ) + sizeof ( *peerblk ) ); + peerblk->segment = segment->index; + memcpy ( peerblk->id, segment->id, sizeof ( peerblk->id ) ); + memcpy ( peerblk->secret, segment->secret, sizeof ( peerblk->secret ) ); + peerblk->block = block->index; + memcpy ( peerblk->hash, block->hash, sizeof ( peerblk->hash ) ); + xferbuf_malloc_init ( &peerblk->buffer ); + process_init_stopped ( &peerblk->process, &peerblk_process_desc, + &peerblk->refcnt ); + peerdisc_init ( &peerblk->discovery, &peerblk_discovery_operations ); + timer_init ( &peerblk->timer, peerblk_expired, &peerblk->refcnt ); + DBGC2 ( peerblk, "PEERBLK %p %d.%d id %02x%02x%02x%02x%02x..." + "%02x%02x%02x [%08zx,%08zx)", peerblk, peerblk->segment, + peerblk->block, peerblk->id[0], peerblk->id[1], peerblk->id[2], + peerblk->id[3], peerblk->id[4], peerblk->id[ digestsize - 3 ], + peerblk->id[ digestsize - 2 ], peerblk->id[ digestsize - 1 ], + peerblk->range.start, peerblk->range.end ); + if ( ( peerblk->trim.start != peerblk->range.start ) || + ( peerblk->trim.end != peerblk->range.end ) ) { + DBGC2 ( peerblk, " covers [%08zx,%08zx)", + peerblk->trim.start, peerblk->trim.end ); + } + DBGC2 ( peerblk, "\n" ); + + /* Open discovery */ + if ( ( rc = peerdisc_open ( &peerblk->discovery, peerblk->id, + peerblk->digestsize ) ) != 0 ) + goto err_open_discovery; + + /* Schedule a retry attempt either immediately (if we already + * have some peers) or after the discovery timeout. + */ + timeout = ( list_empty ( &peerblk->discovery.segment->peers ) ? + ( peerdisc_timeout_secs * TICKS_PER_SEC ) : 0 ); + start_timer_fixed ( &peerblk->timer, timeout ); + + /* Record start time */ + peerblk->started = peerblk_timestamp(); + + /* Attach to parent interface, mortalise self, and return */ + intf_plug_plug ( xfer, &peerblk->xfer ); + ref_put ( &peerblk->refcnt ); + return 0; + + err_open_discovery: + peerblk_close ( peerblk, rc ); + err_alloc: + return rc; +} diff --git a/qemu/roms/ipxe/src/net/peerdisc.c b/qemu/roms/ipxe/src/net/peerdisc.c new file mode 100644 index 000000000..5b0e98911 --- /dev/null +++ b/qemu/roms/ipxe/src/net/peerdisc.c @@ -0,0 +1,551 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Peer Content Caching and Retrieval (PeerDist) protocol peer discovery + * + */ + +/** List of discovery segments */ +static LIST_HEAD ( peerdisc_segments ); + +/** Number of repeated discovery attempts */ +#define PEERDISC_REPEAT_COUNT 2 + +/** Time between repeated discovery attempts */ +#define PEERDISC_REPEAT_TIMEOUT ( 1 * TICKS_PER_SEC ) + +/** Default discovery timeout (in seconds) */ +#define PEERDISC_DEFAULT_TIMEOUT_SECS 2 + +/** Recommended discovery timeout (in seconds) + * + * We reduce the recommended discovery timeout whenever a segment + * fails to discover any peers, and restore the default value whenever + * a valid discovery reply is received. We continue to send discovery + * requests even if the recommended timeout is reduced to zero. + * + * This strategy is intended to minimise discovery delays when no + * peers are available on the network, while allowing downloads to + * quickly switch back to using PeerDist acceleration if new peers + * become available. + */ +unsigned int peerdisc_timeout_secs = PEERDISC_DEFAULT_TIMEOUT_SECS; + +static struct peerdisc_segment * peerdisc_find ( const char *id ); +static int peerdisc_discovered ( struct peerdisc_segment *segment, + const char *location ); + +/****************************************************************************** + * + * Discovery sockets + * + ****************************************************************************** + */ + +/** + * Open all PeerDist discovery sockets + * + * @ret rc Return status code + */ +static int peerdisc_socket_open ( void ) { + struct peerdisc_socket *socket; + int rc; + + /* Open each socket */ + for_each_table_entry ( socket, PEERDISC_SOCKETS ) { + if ( ( rc = xfer_open_socket ( &socket->xfer, SOCK_DGRAM, + &socket->address.sa, + NULL ) ) != 0 ) { + DBGC ( socket, "PEERDISC %s could not open socket: " + "%s\n", socket->name, strerror ( rc ) ); + goto err; + } + } + + return 0; + + err: + for_each_table_entry_continue_reverse ( socket, PEERDISC_SOCKETS ) + intf_restart ( &socket->xfer, rc ); + return rc; +} + +/** + * Attempt to transmit PeerDist discovery requests on all sockets + * + * @v uuid Message UUID string + * @v id Segment identifier string + */ +static void peerdisc_socket_tx ( const char *uuid, const char *id ) { + struct peerdisc_socket *socket; + struct net_device *netdev; + struct xfer_metadata meta; + union { + struct sockaddr sa; + struct sockaddr_tcpip st; + } address; + char *request; + size_t len; + int rc; + + /* Construct discovery request */ + request = peerdist_discovery_request ( uuid, id ); + if ( ! request ) + goto err_request; + len = strlen ( request ); + + /* Initialise data transfer metadata */ + memset ( &meta, 0, sizeof ( meta ) ); + meta.dest = &address.sa; + + /* Send message on each socket */ + for_each_table_entry ( socket, PEERDISC_SOCKETS ) { + + /* Initialise socket address */ + memcpy ( &address.sa, &socket->address.sa, + sizeof ( address.sa ) ); + + /* Send message on each open network device */ + for_each_netdev ( netdev ) { + + /* Skip unopened network devices */ + if ( ! netdev_is_open ( netdev ) ) + continue; + address.st.st_scope_id = netdev->index; + + /* Discard request (for test purposes) if applicable */ + if ( inject_fault ( PEERDISC_DISCARD_RATE ) ) + continue; + + /* Transmit request */ + if ( ( rc = xfer_deliver_raw_meta ( &socket->xfer, + request, len, + &meta ) ) != 0 ) { + DBGC ( socket, "PEERDISC %s could not transmit " + "via %s: %s\n", socket->name, + netdev->name, strerror ( rc ) ); + /* Contine to try other net devices/sockets */ + continue; + } + } + } + + free ( request ); + err_request: + return; +} + +/** + * Handle received PeerDist discovery reply + * + * @v socket PeerDist discovery socket + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int peerdisc_socket_rx ( struct peerdisc_socket *socket, + struct io_buffer *iobuf, + struct xfer_metadata *meta __unused ) { + struct peerdist_discovery_reply reply; + struct peerdisc_segment *segment; + char *id; + char *location; + int rc; + + /* Discard reply (for test purposes) if applicable */ + if ( ( rc = inject_fault ( PEERDISC_DISCARD_RATE ) ) != 0 ) + goto err; + + /* Parse reply */ + if ( ( rc = peerdist_discovery_reply ( iobuf->data, iob_len ( iobuf ), + &reply ) ) != 0 ) { + DBGC ( socket, "PEERDISC %s could not parse reply: %s\n", + socket->name, strerror ( rc ) ); + DBGC_HDA ( socket, 0, iobuf->data, iob_len ( iobuf ) ); + goto err; + } + + /* Any kind of discovery reply indicates that there are active + * peers on a local network, so restore the recommended + * discovery timeout to its default value for future requests. + */ + if ( peerdisc_timeout_secs != PEERDISC_DEFAULT_TIMEOUT_SECS ) { + DBGC ( socket, "PEERDISC %s restoring timeout to %d seconds\n", + socket->name, PEERDISC_DEFAULT_TIMEOUT_SECS ); + } + peerdisc_timeout_secs = PEERDISC_DEFAULT_TIMEOUT_SECS; + + /* Iterate over segment IDs */ + for ( id = reply.ids ; *id ; id += ( strlen ( id ) + 1 /* NUL */ ) ) { + + /* Find corresponding segment */ + segment = peerdisc_find ( id ); + if ( ! segment ) { + DBGC ( socket, "PEERDISC %s ignoring reply for %s\n", + socket->name, id ); + continue; + } + + /* Report all discovered peer locations */ + for ( location = reply.locations ; *location ; + location += ( strlen ( location ) + 1 /* NUL */ ) ) { + + /* Report discovered peer location */ + if ( ( rc = peerdisc_discovered ( segment, + location ) ) != 0 ) + goto err; + } + } + + err: + free_iob ( iobuf ); + return rc; +} + +/** + * Close all PeerDist discovery sockets + * + * @v rc Reason for close + */ +static void peerdisc_socket_close ( int rc ) { + struct peerdisc_socket *socket; + + /* Close all sockets */ + for_each_table_entry ( socket, PEERDISC_SOCKETS ) + intf_restart ( &socket->xfer, rc ); +} + +/** PeerDist discovery socket interface operations */ +static struct interface_operation peerdisc_socket_operations[] = { + INTF_OP ( xfer_deliver, struct peerdisc_socket *, peerdisc_socket_rx ), +}; + +/** PeerDist discovery socket interface descriptor */ +static struct interface_descriptor peerdisc_socket_desc = + INTF_DESC ( struct peerdisc_socket, xfer, peerdisc_socket_operations ); + +/** PeerDist discovery IPv4 socket */ +struct peerdisc_socket peerdisc_socket_ipv4 __peerdisc_socket = { + .name = "IPv4", + .address = { + .sin = { + .sin_family = AF_INET, + .sin_port = htons ( PEERDIST_DISCOVERY_PORT ), + .sin_addr.s_addr = htonl ( PEERDIST_DISCOVERY_IPV4 ), + }, + }, + .xfer = INTF_INIT ( peerdisc_socket_desc ), +}; + +/** PeerDist discovery IPv6 socket */ +struct peerdisc_socket peerdisc_socket_ipv6 __peerdisc_socket = { + .name = "IPv6", + .address = { + .sin6 = { + .sin6_family = AF_INET6, + .sin6_port = htons ( PEERDIST_DISCOVERY_PORT ), + .sin6_addr.s6_addr = PEERDIST_DISCOVERY_IPV6, + }, + }, + .xfer = INTF_INIT ( peerdisc_socket_desc ), +}; + +/****************************************************************************** + * + * Discovery segments + * + ****************************************************************************** + */ + +/** + * Free PeerDist discovery segment + * + * @v refcnt Reference count + */ +static void peerdisc_free ( struct refcnt *refcnt ) { + struct peerdisc_segment *segment = + container_of ( refcnt, struct peerdisc_segment, refcnt ); + struct peerdisc_peer *peer; + struct peerdisc_peer *tmp; + + /* Free all discovered peers */ + list_for_each_entry_safe ( peer, tmp, &segment->peers, list ) { + list_del ( &peer->list ); + free ( peer ); + } + + /* Free segment */ + free ( segment ); +} + +/** + * Find PeerDist discovery segment + * + * @v id Segment ID + * @ret segment PeerDist discovery segment, or NULL if not found + */ +static struct peerdisc_segment * peerdisc_find ( const char *id ) { + struct peerdisc_segment *segment; + + /* Look for a matching segment */ + list_for_each_entry ( segment, &peerdisc_segments, list ) { + if ( strcmp ( id, segment->id ) == 0 ) + return segment; + } + + return NULL; +} + +/** + * Add discovered PeerDist peer + * + * @v segment PeerDist discovery segment + * @v location Peer location + * @ret rc Return status code + */ +static int peerdisc_discovered ( struct peerdisc_segment *segment, + const char *location ) { + struct peerdisc_peer *peer; + struct peerdisc_client *peerdisc; + struct peerdisc_client *tmp; + + /* Ignore duplicate peers */ + list_for_each_entry ( peer, &segment->peers, list ) { + if ( strcmp ( peer->location, location ) == 0 ) { + DBGC2 ( segment, "PEERDISC %p duplicate %s\n", + segment, location ); + return 0; + } + } + DBGC2 ( segment, "PEERDISC %p discovered %s\n", segment, location ); + + /* Allocate and initialise structure */ + peer = zalloc ( sizeof ( *peer ) + strlen ( location ) + 1 /* NUL */ ); + if ( ! peer ) + return -ENOMEM; + strcpy ( peer->location, location ); + + /* Add to end of list of peers */ + list_add_tail ( &peer->list, &segment->peers ); + + /* Notify all clients */ + list_for_each_entry_safe ( peerdisc, tmp, &segment->clients, list ) + peerdisc->op->discovered ( peerdisc ); + + return 0; +} + +/** + * Handle discovery timer expiry + * + * @v timer Discovery timer + * @v over Failure indicator + */ +static void peerdisc_expired ( struct retry_timer *timer, int over __unused ) { + struct peerdisc_segment *segment = + container_of ( timer, struct peerdisc_segment, timer ); + + /* Attempt to transmit discovery requests */ + peerdisc_socket_tx ( segment->uuid, segment->id ); + + /* Schedule next transmission, if applicable */ + if ( timer->count < PEERDISC_REPEAT_COUNT ) + start_timer_fixed ( &segment->timer, PEERDISC_REPEAT_TIMEOUT ); +} + +/** + * Create PeerDist discovery segment + * + * @v id Segment ID + * @ret segment PeerDist discovery segment, or NULL on error + */ +static struct peerdisc_segment * peerdisc_create ( const char *id ) { + struct peerdisc_segment *segment; + union { + union uuid uuid; + uint32_t dword[ sizeof ( union uuid ) / sizeof ( uint32_t ) ]; + } random_uuid; + size_t uuid_len; + size_t id_len; + char *uuid; + char *uuid_copy; + char *id_copy; + unsigned int i; + + /* Generate a random message UUID. This does not require high + * quality randomness. + */ + for ( i = 0 ; i < ( sizeof ( random_uuid.dword ) / + sizeof ( random_uuid.dword[0] ) ) ; i++ ) + random_uuid.dword[i] = random(); + uuid = uuid_ntoa ( &random_uuid.uuid ); + + /* Calculate string lengths */ + id_len = ( strlen ( id ) + 1 /* NUL */ ); + uuid_len = ( strlen ( uuid ) + 1 /* NUL */ ); + + /* Allocate and initialise structure */ + segment = zalloc ( sizeof ( *segment ) + id_len + uuid_len ); + if ( ! segment ) + return NULL; + id_copy = ( ( ( void * ) segment ) + sizeof ( *segment ) ); + memcpy ( id_copy, id, id_len ); + uuid_copy = ( ( ( void * ) id_copy ) + id_len ); + memcpy ( uuid_copy, uuid, uuid_len ); + ref_init ( &segment->refcnt, peerdisc_free ); + segment->id = id_copy; + segment->uuid = uuid_copy; + INIT_LIST_HEAD ( &segment->peers ); + INIT_LIST_HEAD ( &segment->clients ); + timer_init ( &segment->timer, peerdisc_expired, &segment->refcnt ); + DBGC2 ( segment, "PEERDISC %p discovering %s\n", segment, segment->id ); + + /* Start discovery timer */ + start_timer_nodelay ( &segment->timer ); + + /* Add to list of segments, transfer reference to list, and return */ + list_add_tail ( &segment->list, &peerdisc_segments ); + return segment; +} + +/** + * Destroy PeerDist discovery segment + * + * @v segment PeerDist discovery segment + */ +static void peerdisc_destroy ( struct peerdisc_segment *segment ) { + + /* Sanity check */ + assert ( list_empty ( &segment->clients ) ); + + /* Stop timer */ + stop_timer ( &segment->timer ); + + /* Remove from list of segments and drop list's reference */ + list_del ( &segment->list ); + ref_put ( &segment->refcnt ); +} + +/****************************************************************************** + * + * Discovery clients + * + ****************************************************************************** + */ + +/** + * Open PeerDist discovery client + * + * @v peerdisc PeerDist discovery client + * @v id Segment ID + * @v len Length of segment ID + * @ret rc Return status code + */ +int peerdisc_open ( struct peerdisc_client *peerdisc, const void *id, + size_t len ) { + struct peerdisc_segment *segment; + char id_string[ base16_encoded_len ( len ) + 1 /* NUL */ ]; + char *id_chr; + int rc; + + /* Construct ID string */ + base16_encode ( id, len, id_string, sizeof ( id_string ) ); + for ( id_chr = id_string ; *id_chr ; id_chr++ ) + *id_chr = toupper ( *id_chr ); + + /* Sanity check */ + assert ( peerdisc->segment == NULL ); + + /* Open socket if this is the first segment */ + if ( list_empty ( &peerdisc_segments ) && + ( ( rc = peerdisc_socket_open() ) != 0 ) ) + return rc; + + /* Find or create segment */ + if ( ! ( ( segment = peerdisc_find ( id_string ) ) || + ( segment = peerdisc_create ( id_string ) ) ) ) + return -ENOMEM; + + /* Add to list of clients */ + ref_get ( &segment->refcnt ); + peerdisc->segment = segment; + list_add_tail ( &peerdisc->list, &segment->clients ); + + return 0; +} + +/** + * Close PeerDist discovery client + * + * @v peerdisc PeerDist discovery client + */ +void peerdisc_close ( struct peerdisc_client *peerdisc ) { + struct peerdisc_segment *segment = peerdisc->segment; + + /* Ignore if discovery is already closed */ + if ( ! segment ) + return; + + /* If no peers were discovered, reduce the recommended + * discovery timeout to minimise delays on future requests. + */ + if ( list_empty ( &segment->peers ) && peerdisc_timeout_secs ) { + peerdisc_timeout_secs--; + DBGC ( segment, "PEERDISC %p reducing timeout to %d " + "seconds\n", peerdisc, peerdisc_timeout_secs ); + } + + /* Remove from list of clients */ + peerdisc->segment = NULL; + list_del ( &peerdisc->list ); + ref_put ( &segment->refcnt ); + + /* If this was the last clients, destroy the segment */ + if ( list_empty ( &segment->clients ) ) + peerdisc_destroy ( segment ); + + /* If there are no more segments, close the socket */ + if ( list_empty ( &peerdisc_segments ) ) + peerdisc_socket_close ( 0 ); +} diff --git a/qemu/roms/ipxe/src/net/peerdist.c b/qemu/roms/ipxe/src/net/peerdist.c new file mode 100644 index 000000000..48933f951 --- /dev/null +++ b/qemu/roms/ipxe/src/net/peerdist.c @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** @file + * + * Peer Content Caching and Retrieval (PeerDist) protocol + * + * This is quite possibly the ugliest protocol I have ever had the + * misfortune to encounter, and I've encountered multicast TFTP. + */ + +/** + * Check whether or not to support PeerDist encoding for this request + * + * @v http HTTP transaction + * @ret supported PeerDist encoding is supported for this request + */ +static int http_peerdist_supported ( struct http_transaction *http ) { + + /* Support PeerDist encoding only if we can directly access an + * underlying data transfer buffer. Direct access is required + * in order to support decryption of data received via the + * retrieval protocol (which provides the AES initialisation + * vector only after all of the encrypted data has been + * received). + * + * This test simultaneously ensures that we do not attempt to + * use PeerDist encoding on a request which is itself a + * PeerDist individual block download, since the individual + * block downloads do not themselves provide direct access to + * an underlying data transfer buffer. + */ + return ( xfer_buffer ( &http->xfer ) != NULL ); +} + +/** + * Format HTTP "X-P2P-PeerDist" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_p2p_peerdist ( struct http_transaction *http, + char *buf, size_t len ) { + int supported = http_peerdist_supported ( http ); + int missing; + + /* PeerDist wants us to inform the server whenever we make a + * request for data that was missing from local peers + * (presumably for statistical purposes only). We use the + * heuristic of assuming that the combination of "this request + * may not itself use PeerDist content encoding" and "this is + * a range request" probably indicates that we are making a + * PeerDist block raw range request for missing data. + */ + missing = ( http->request.range.len && ( ! supported ) ); + + /* Omit header if PeerDist encoding is not supported and we + * are not reporting a missing data request. + */ + if ( ! ( supported || missing ) ) + return 0; + + /* Construct header */ + return snprintf ( buf, len, "Version=1.1%s", + ( missing ? ", MissingDataRequest=true" : "" ) ); +} + +/** HTTP "X-P2P-PeerDist" header */ +struct http_request_header http_request_p2p_peerdist __http_request_header = { + .name = "X-P2P-PeerDist", + .format = http_format_p2p_peerdist, +}; + +/** + * Format HTTP "X-P2P-PeerDistEx" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_p2p_peerdistex ( struct http_transaction *http, + char *buf, size_t len ) { + int supported = http_peerdist_supported ( http ); + + /* Omit header if PeerDist encoding is not supported */ + if ( ! supported ) + return 0; + + /* Construct header */ + return snprintf ( buf, len, ( "MinContentInformation=1.0, " + "MaxContentInformation=2.0" ) ); +} + +/** HTTP "X-P2P-PeerDist" header */ +struct http_request_header http_request_p2p_peerdistex __http_request_header = { + .name = "X-P2P-PeerDistEx", + .format = http_format_p2p_peerdistex, +}; + +/** + * Initialise PeerDist content encoding + * + * @v http HTTP transaction + * @ret rc Return status code + */ +static int http_peerdist_init ( struct http_transaction *http ) { + + return peermux_filter ( &http->content, &http->transfer, http->uri ); +} + +/** PeerDist HTTP content encoding */ +struct http_content_encoding peerdist_encoding __http_content_encoding = { + .name = "peerdist", + .supported = http_peerdist_supported, + .init = http_peerdist_init, +}; diff --git a/qemu/roms/ipxe/src/net/peermux.c b/qemu/roms/ipxe/src/net/peermux.c new file mode 100644 index 000000000..634c69992 --- /dev/null +++ b/qemu/roms/ipxe/src/net/peermux.c @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * Peer Content Caching and Retrieval (PeerDist) protocol multiplexer + * + */ + +/** + * Free PeerDist download multiplexer + * + * @v refcnt Reference count + */ +static void peermux_free ( struct refcnt *refcnt ) { + struct peerdist_multiplexer *peermux = + container_of ( refcnt, struct peerdist_multiplexer, refcnt ); + + uri_put ( peermux->uri ); + xferbuf_free ( &peermux->buffer ); + free ( peermux ); +} + +/** + * Close PeerDist download multiplexer + * + * @v peermux PeerDist download multiplexer + * @v rc Reason for close + */ +static void peermux_close ( struct peerdist_multiplexer *peermux, int rc ) { + unsigned int i; + + /* Stop block download initiation process */ + process_del ( &peermux->process ); + + /* Shut down all block downloads */ + for ( i = 0 ; i < PEERMUX_MAX_BLOCKS ; i++ ) + intf_shutdown ( &peermux->block[i].xfer, rc ); + + /* Shut down all other interfaces (which may be connected to + * the same object). + */ + intf_nullify ( &peermux->info ); /* avoid potential loops */ + intf_shutdown ( &peermux->xfer, rc ); + intf_shutdown ( &peermux->info, rc ); +} + +/** + * Receive content information + * + * @v peermux PeerDist download multiplexer + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int peermux_info_deliver ( struct peerdist_multiplexer *peermux, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + int rc; + + /* Add data to buffer */ + if ( ( rc = xferbuf_deliver ( &peermux->buffer, iobuf, meta ) ) != 0 ) + goto err; + + return 0; + + err: + peermux_close ( peermux, rc ); + return rc; +} + +/** + * Close content information interface + * + * @v peermux PeerDist download multiplexer + * @v rc Reason for close + */ +static void peermux_info_close ( struct peerdist_multiplexer *peermux, int rc ){ + struct peerdist_info *info = &peermux->cache.info; + size_t len; + + /* Terminate download on error */ + if ( rc != 0 ) + goto err; + + /* Successfully closing the content information interface + * indicates that the content information has been fully + * received, and initiates the actual PeerDist download. + */ + + /* Shut down content information interface */ + intf_shutdown ( &peermux->info, rc ); + + /* Parse content information */ + if ( ( rc = peerdist_info ( info->raw.data, peermux->buffer.len, + info ) ) != 0 ) { + DBGC ( peermux, "PEERMUX %p could not parse content info: %s\n", + peermux, strerror ( rc ) ); + goto err; + } + + /* Notify recipient of total download size */ + len = ( info->trim.end - info->trim.start ); + if ( ( rc = xfer_seek ( &peermux->xfer, len ) ) != 0 ) { + DBGC ( peermux, "PEERMUX %p could not presize buffer: %s\n", + peermux, strerror ( rc ) ); + goto err; + } + xfer_seek ( &peermux->xfer, 0 ); + + /* Start block download process */ + process_add ( &peermux->process ); + + return; + + err: + peermux_close ( peermux, rc ); +} + +/** + * Initiate multiplexed block download + * + * @v peermux PeerDist download multiplexer + */ +static void peermux_step ( struct peerdist_multiplexer *peermux ) { + struct peerdist_info *info = &peermux->cache.info; + struct peerdist_info_segment *segment = &peermux->cache.segment; + struct peerdist_info_block *block = &peermux->cache.block; + struct peerdist_multiplexed_block *peermblk; + unsigned int next_segment; + unsigned int next_block; + int rc; + + /* Stop initiation process if all block downloads are busy */ + peermblk = list_first_entry ( &peermux->idle, + struct peerdist_multiplexed_block, list ); + if ( ! peermblk ) { + process_del ( &peermux->process ); + return; + } + + /* Increment block index */ + next_block = ( block->index + 1 ); + + /* Move to first/next segment, if applicable */ + if ( next_block >= segment->blocks ) { + + /* Reset block index */ + next_block = 0; + + /* Calculate segment index */ + next_segment = ( segment->info ? ( segment->index + 1 ) : 0 ); + + /* If we have finished all segments and have no + * remaining block downloads, then we are finished. + */ + if ( next_segment >= info->segments ) { + process_del ( &peermux->process ); + if ( list_empty ( &peermux->busy ) ) + peermux_close ( peermux, 0 ); + return; + } + + /* Get content information segment */ + if ( ( rc = peerdist_info_segment ( info, segment, + next_segment ) ) != 0 ) { + DBGC ( peermux, "PEERMUX %p could not get segment %d " + "information: %s\n", peermux, next_segment, + strerror ( rc ) ); + goto err; + } + } + + /* Get content information block */ + if ( ( rc = peerdist_info_block ( segment, block, next_block ) ) != 0 ){ + DBGC ( peermux, "PEERMUX %p could not get segment %d block " + "%d information: %s\n", peermux, segment->index, + next_block, strerror ( rc ) ); + goto err; + } + + /* Ignore block if it lies entirely outside the trimmed range */ + if ( block->trim.start == block->trim.end ) { + DBGC ( peermux, "PEERMUX %p skipping segment %d block %d\n", + peermux, segment->index, block->index ); + return; + } + + /* Start downloading this block */ + if ( ( rc = peerblk_open ( &peermblk->xfer, peermux->uri, + block ) ) != 0 ) { + DBGC ( peermux, "PEERMUX %p could not start download for " + "segment %d block %d: %s\n", peermux, segment->index, + block->index, strerror ( rc ) ); + goto err; + } + + /* Move to list of busy block downloads */ + list_del ( &peermblk->list ); + list_add_tail ( &peermblk->list, &peermux->busy ); + + return; + + err: + peermux_close ( peermux, rc ); +} + +/** + * Receive data from multiplexed block download + * + * @v peermblk PeerDist multiplexed block download + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int peermux_block_deliver ( struct peerdist_multiplexed_block *peermblk, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct peerdist_multiplexer *peermux = peermblk->peermux; + + /* Sanity check: all block downloads must use absolute + * positions for all deliveries, since they run concurrently. + */ + assert ( meta->flags & XFER_FL_ABS_OFFSET ); + + /* We can't use a simple passthrough interface descriptor, + * since there are multiple block download interfaces. + */ + return xfer_deliver ( &peermux->xfer, iob_disown ( iobuf ), meta ); +} + +/** + * Get multiplexed block download underlying data transfer buffer + * + * @v peermblk PeerDist multiplexed download block + * @ret xferbuf Data transfer buffer, or NULL on error + */ +static struct xfer_buffer * +peermux_block_buffer ( struct peerdist_multiplexed_block *peermblk ) { + struct peerdist_multiplexer *peermux = peermblk->peermux; + + /* We can't use a simple passthrough interface descriptor, + * since there are multiple block download interfaces. + */ + return xfer_buffer ( &peermux->xfer ); +} + +/** + * Close multiplexed block download + * + * @v peermblk PeerDist multiplexed block download + * @v rc Reason for close + */ +static void peermux_block_close ( struct peerdist_multiplexed_block *peermblk, + int rc ) { + struct peerdist_multiplexer *peermux = peermblk->peermux; + + /* Move to list of idle downloads */ + list_del ( &peermblk->list ); + list_add_tail ( &peermblk->list, &peermux->idle ); + + /* If any error occurred, terminate the whole multiplexer */ + if ( rc != 0 ) { + peermux_close ( peermux, rc ); + return; + } + + /* Restart data transfer interface */ + intf_restart ( &peermblk->xfer, rc ); + + /* Restart block download initiation process */ + process_add ( &peermux->process ); +} + +/** Data transfer interface operations */ +static struct interface_operation peermux_xfer_operations[] = { + INTF_OP ( intf_close, struct peerdist_multiplexer *, peermux_close ), +}; + +/** Data transfer interface descriptor */ +static struct interface_descriptor peermux_xfer_desc = + INTF_DESC_PASSTHRU ( struct peerdist_multiplexer, xfer, + peermux_xfer_operations, info ); + +/** Content information interface operations */ +static struct interface_operation peermux_info_operations[] = { + INTF_OP ( xfer_deliver, struct peerdist_multiplexer *, + peermux_info_deliver ), + INTF_OP ( intf_close, struct peerdist_multiplexer *, + peermux_info_close ), +}; + +/** Content information interface descriptor */ +static struct interface_descriptor peermux_info_desc = + INTF_DESC_PASSTHRU ( struct peerdist_multiplexer, info, + peermux_info_operations, xfer ); + +/** Block download data transfer interface operations */ +static struct interface_operation peermux_block_operations[] = { + INTF_OP ( xfer_deliver, struct peerdist_multiplexed_block *, + peermux_block_deliver ), + INTF_OP ( xfer_buffer, struct peerdist_multiplexed_block *, + peermux_block_buffer ), + INTF_OP ( intf_close, struct peerdist_multiplexed_block *, + peermux_block_close ), +}; + +/** Block download data transfer interface descriptor */ +static struct interface_descriptor peermux_block_desc = + INTF_DESC ( struct peerdist_multiplexed_block, xfer, + peermux_block_operations ); + +/** Block download initiation process descriptor */ +static struct process_descriptor peermux_process_desc = + PROC_DESC ( struct peerdist_multiplexer, process, peermux_step ); + +/** + * Add PeerDist content-encoding filter + * + * @v xfer Data transfer interface + * @v info Content information interface + * @v uri Original URI + * @ret rc Return status code + */ +int peermux_filter ( struct interface *xfer, struct interface *info, + struct uri *uri ) { + struct peerdist_multiplexer *peermux; + struct peerdist_multiplexed_block *peermblk; + unsigned int i; + + /* Allocate and initialise structure */ + peermux = zalloc ( sizeof ( *peermux ) ); + if ( ! peermux ) + return -ENOMEM; + ref_init ( &peermux->refcnt, peermux_free ); + intf_init ( &peermux->xfer, &peermux_xfer_desc, &peermux->refcnt ); + intf_init ( &peermux->info, &peermux_info_desc, &peermux->refcnt ); + peermux->uri = uri_get ( uri ); + xferbuf_umalloc_init ( &peermux->buffer, + &peermux->cache.info.raw.data ); + process_init_stopped ( &peermux->process, &peermux_process_desc, + &peermux->refcnt ); + INIT_LIST_HEAD ( &peermux->busy ); + INIT_LIST_HEAD ( &peermux->idle ); + for ( i = 0 ; i < PEERMUX_MAX_BLOCKS ; i++ ) { + peermblk = &peermux->block[i]; + peermblk->peermux = peermux; + list_add_tail ( &peermblk->list, &peermux->idle ); + intf_init ( &peermblk->xfer, &peermux_block_desc, + &peermux->refcnt ); + } + + /* Attach to parent interfaces, mortalise self, and return */ + intf_plug_plug ( &peermux->xfer, xfer ); + intf_plug_plug ( &peermux->info, info ); + ref_put ( &peermux->refcnt ); + return 0; +} diff --git a/qemu/roms/ipxe/src/net/ping.c b/qemu/roms/ipxe/src/net/ping.c index d9da87ade..3f4fa5c11 100644 --- a/qemu/roms/ipxe/src/net/ping.c +++ b/qemu/roms/ipxe/src/net/ping.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/rarp.c b/qemu/roms/ipxe/src/net/rarp.c index 371145015..c194a404f 100644 --- a/qemu/roms/ipxe/src/net/rarp.c +++ b/qemu/roms/ipxe/src/net/rarp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/retry.c b/qemu/roms/ipxe/src/net/retry.c index 8f210bdcc..734567be5 100644 --- a/qemu/roms/ipxe/src/net/retry.c +++ b/qemu/roms/ipxe/src/net/retry.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -35,7 +39,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); * * This implementation of the timer is designed to satisfy RFC 2988 * and therefore be usable as a TCP retransmission timer. - * * */ @@ -49,47 +52,59 @@ FILE_LICENCE ( GPL2_OR_LATER ); static LIST_HEAD ( timers ); /** - * Start timer + * Start timer with a specified timeout * * @v timer Retry timer + * @v timeout Timeout, in ticks * - * This starts the timer running with the current timeout value. If + * This starts the timer running with the specified timeout value. If * stop_timer() is not called before the timer expires, the timer will * be stopped and the timer's callback function will be called. */ -void start_timer ( struct retry_timer *timer ) { +void start_timer_fixed ( struct retry_timer *timer, unsigned long timeout ) { + + /* Add to list of running timers (if applicable) */ if ( ! timer->running ) { list_add ( &timer->list, &timers ); ref_get ( timer->refcnt ); + timer->running = 1; } + + /* Record start time */ timer->start = currticks(); - timer->running = 1; - - /* 0 means "use default timeout" */ - if ( timer->min_timeout == 0 ) - timer->min_timeout = DEFAULT_MIN_TIMEOUT; - /* We must never be less than MIN_TIMEOUT under any circumstances */ - if ( timer->min_timeout < MIN_TIMEOUT ) - timer->min_timeout = MIN_TIMEOUT; - /* Honor user-specified minimum timeout */ - if ( timer->timeout < timer->min_timeout ) - timer->timeout = timer->min_timeout; - - DBG2 ( "Timer %p started at time %ld (expires at %ld)\n", - timer, timer->start, ( timer->start + timer->timeout ) ); + + /* Record timeout */ + timer->timeout = timeout; + + DBGC2 ( timer, "Timer %p started at time %ld (expires at %ld)\n", + timer, timer->start, ( timer->start + timer->timeout ) ); } /** - * Start timer with a specified fixed timeout + * Start timer * * @v timer Retry timer - * @v timeout Timeout, in ticks + * + * This starts the timer running with the current timeout value + * (rounded up to the minimum timeout value). If stop_timer() is not + * called before the timer expires, the timer will be stopped and the + * timer's callback function will be called. */ -void start_timer_fixed ( struct retry_timer *timer, unsigned long timeout ) { - start_timer ( timer ); - timer->timeout = timeout; - DBG2 ( "Timer %p expiry time changed to %ld\n", - timer, ( timer->start + timer->timeout ) ); +void start_timer ( struct retry_timer *timer ) { + unsigned long timeout = timer->timeout; + unsigned long min; + + /* Calculate minimum timeout */ + min = ( timer->min ? timer->min : DEFAULT_MIN_TIMEOUT ); + if ( min < MIN_TIMEOUT ) + min = MIN_TIMEOUT; + + /* Ensure timeout is at least the minimum */ + if ( timeout < min ) + timeout = min; + + /* Start timer with this timeout */ + start_timer_fixed ( timer, timeout ); } /** @@ -111,8 +126,8 @@ void stop_timer ( struct retry_timer *timer ) { list_del ( &timer->list ); runtime = ( now - timer->start ); timer->running = 0; - DBG2 ( "Timer %p stopped at time %ld (ran for %ld)\n", - timer, now, runtime ); + DBGC2 ( timer, "Timer %p stopped at time %ld (ran for %ld)\n", + timer, now, runtime ); /* Update timer. Variables are: * @@ -135,8 +150,8 @@ void stop_timer ( struct retry_timer *timer ) { timer->timeout -= ( timer->timeout >> 3 ); timer->timeout += ( runtime >> 1 ); if ( timer->timeout != old_timeout ) { - DBG ( "Timer %p timeout updated to %ld\n", - timer, timer->timeout ); + DBGC ( timer, "Timer %p timeout updated to %ld\n", + timer, timer->timeout ); } } @@ -150,11 +165,12 @@ void stop_timer ( struct retry_timer *timer ) { */ static void timer_expired ( struct retry_timer *timer ) { struct refcnt *refcnt = timer->refcnt; + unsigned long max = ( timer->max ? timer->max : DEFAULT_MAX_TIMEOUT ); int fail; /* Stop timer without performing RTT calculations */ - DBG2 ( "Timer %p stopped at time %ld on expiry\n", - timer, currticks() ); + DBGC2 ( timer, "Timer %p stopped at time %ld on expiry\n", + timer, currticks() ); assert ( timer->running ); list_del ( &timer->list ); timer->running = 0; @@ -162,12 +178,10 @@ static void timer_expired ( struct retry_timer *timer ) { /* Back off the timeout value */ timer->timeout <<= 1; - if ( timer->max_timeout == 0 ) /* 0 means "use default timeout" */ - timer->max_timeout = DEFAULT_MAX_TIMEOUT; - if ( ( fail = ( timer->timeout > timer->max_timeout ) ) ) - timer->timeout = timer->max_timeout; - DBG ( "Timer %p timeout backed off to %ld\n", - timer, timer->timeout ); + if ( ( fail = ( timer->timeout > max ) ) ) + timer->timeout = max; + DBGC ( timer, "Timer %p timeout backed off to %ld\n", + timer, timer->timeout ); /* Call expiry callback */ timer->expired ( timer, fail ); diff --git a/qemu/roms/ipxe/src/net/rndis.c b/qemu/roms/ipxe/src/net/rndis.c new file mode 100644 index 000000000..8c4fe8b30 --- /dev/null +++ b/qemu/roms/ipxe/src/net/rndis.c @@ -0,0 +1,1052 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Remote Network Driver Interface Specification + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Allocate I/O buffer + * + * @v len Length + * @ret iobuf I/O buffer, or NULL + */ +static struct io_buffer * rndis_alloc_iob ( size_t len ) { + struct rndis_header *header; + struct io_buffer *iobuf; + + /* Allocate I/O buffer and reserve space */ + iobuf = alloc_iob ( sizeof ( *header ) + len ); + if ( iobuf ) + iob_reserve ( iobuf, sizeof ( *header ) ); + + return iobuf; +} + +/** + * Wait for completion + * + * @v rndis RNDIS device + * @v wait_id Request ID + * @ret rc Return status code + */ +static int rndis_wait ( struct rndis_device *rndis, unsigned int wait_id ) { + unsigned int i; + + /* Record query ID */ + rndis->wait_id = wait_id; + + /* Wait for operation to complete */ + for ( i = 0 ; i < RNDIS_MAX_WAIT_MS ; i++ ) { + + /* Check for completion */ + if ( ! rndis->wait_id ) + return rndis->wait_rc; + + /* Poll RNDIS device */ + rndis->op->poll ( rndis ); + + /* Delay for 1ms */ + mdelay ( 1 ); + } + + DBGC ( rndis, "RNDIS %s timed out waiting for ID %#08x\n", + rndis->name, wait_id ); + return -ETIMEDOUT; +} + +/** + * Transmit message + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + * @v type Message type + * @ret rc Return status code + */ +static int rndis_tx_message ( struct rndis_device *rndis, + struct io_buffer *iobuf, unsigned int type ) { + struct rndis_header *header; + int rc; + + /* Prepend RNDIS header */ + header = iob_push ( iobuf, sizeof ( *header ) ); + header->type = cpu_to_le32 ( type ); + header->len = cpu_to_le32 ( iob_len ( iobuf ) ); + + /* Transmit message */ + if ( ( rc = rndis->op->transmit ( rndis, iobuf ) ) != 0 ) { + DBGC ( rndis, "RNDIS %s could not transmit: %s\n", + rndis->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Complete message transmission + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + * @v rc Packet status code + */ +void rndis_tx_complete_err ( struct rndis_device *rndis, + struct io_buffer *iobuf, int rc ) { + struct net_device *netdev = rndis->netdev; + struct rndis_header *header; + size_t len = iob_len ( iobuf ); + + /* Sanity check */ + if ( len < sizeof ( *header ) ) { + DBGC ( rndis, "RNDIS %s completed underlength transmission:\n", + rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + netdev_tx_err ( netdev, NULL, -EINVAL ); + return; + } + header = iobuf->data; + + /* Complete buffer */ + if ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) { + netdev_tx_complete_err ( netdev, iobuf, rc ); + } else { + free_iob ( iobuf ); + } +} + +/** + * Transmit data packet + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int rndis_tx_data ( struct rndis_device *rndis, + struct io_buffer *iobuf ) { + struct rndis_packet_message *msg; + size_t len = iob_len ( iobuf ); + int rc; + + /* Prepend packet message header */ + msg = iob_push ( iobuf, sizeof ( *msg ) ); + memset ( msg, 0, sizeof ( *msg ) ); + msg->data.offset = cpu_to_le32 ( sizeof ( *msg ) ); + msg->data.len = cpu_to_le32 ( len ); + + /* Transmit message */ + if ( ( rc = rndis_tx_message ( rndis, iobuf, RNDIS_PACKET_MSG ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Defer transmitted packet + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + * @ret rc Return status code + * + * As with netdev_tx_defer(), the caller must ensure that space in the + * transmit descriptor ring is freed up before calling + * rndis_tx_complete(). + * + * Unlike netdev_tx_defer(), this call may fail. + */ +int rndis_tx_defer ( struct rndis_device *rndis, struct io_buffer *iobuf ) { + struct net_device *netdev = rndis->netdev; + struct rndis_header *header; + struct rndis_packet_message *msg; + + /* Fail unless this was a packet message. Only packet + * messages correspond to I/O buffers in the network device's + * TX queue; other messages cannot be deferred in this way. + */ + assert ( iob_len ( iobuf ) >= sizeof ( *header ) ); + header = iobuf->data; + if ( header->type != cpu_to_le32 ( RNDIS_PACKET_MSG ) ) + return -ENOTSUP; + + /* Strip RNDIS header and packet message header, to return + * this packet to the state in which we received it. + */ + iob_pull ( iobuf, ( sizeof ( *header ) + sizeof ( *msg ) ) ); + + /* Defer packet */ + netdev_tx_defer ( netdev, iobuf ); + + return 0; +} + +/** + * Receive data packet + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + */ +static void rndis_rx_data ( struct rndis_device *rndis, + struct io_buffer *iobuf ) { + struct net_device *netdev = rndis->netdev; + struct rndis_packet_message *msg; + size_t len = iob_len ( iobuf ); + size_t data_offset; + size_t data_len; + int rc; + + /* Sanity check */ + if ( len < sizeof ( *msg ) ) { + DBGC ( rndis, "RNDIS %s received underlength data packet:\n", + rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EINVAL; + goto err_len; + } + msg = iobuf->data; + + /* Locate and sanity check data buffer */ + data_offset = le32_to_cpu ( msg->data.offset ); + data_len = le32_to_cpu ( msg->data.len ); + if ( ( data_offset > len ) || ( data_len > ( len - data_offset ) ) ) { + DBGC ( rndis, "RNDIS %s data packet data exceeds packet:\n", + rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EINVAL; + goto err_data; + } + + /* Strip non-data portions */ + iob_pull ( iobuf, data_offset ); + iob_unput ( iobuf, ( iob_len ( iobuf ) - data_len ) ); + + /* Hand off to network stack */ + netdev_rx ( netdev, iob_disown ( iobuf ) ); + + return; + + err_data: + err_len: + /* Report error to network stack */ + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); +} + +/** + * Transmit initialisation message + * + * @v rndis RNDIS device + * @v id Request ID + * @ret rc Return status code + */ +static int rndis_tx_initialise ( struct rndis_device *rndis, unsigned int id ) { + struct io_buffer *iobuf; + struct rndis_initialise_message *msg; + int rc; + + /* Allocate I/O buffer */ + iobuf = rndis_alloc_iob ( sizeof ( *msg ) ); + if ( ! iobuf ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Construct message */ + msg = iob_put ( iobuf, sizeof ( *msg ) ); + memset ( msg, 0, sizeof ( *msg ) ); + msg->id = id; /* Non-endian */ + msg->major = cpu_to_le32 ( RNDIS_VERSION_MAJOR ); + msg->minor = cpu_to_le32 ( RNDIS_VERSION_MINOR ); + msg->mtu = cpu_to_le32 ( RNDIS_MTU ); + + /* Transmit message */ + if ( ( rc = rndis_tx_message ( rndis, iobuf, + RNDIS_INITIALISE_MSG ) ) != 0 ) + goto err_tx; + + return 0; + + err_tx: + free_iob ( iobuf ); + err_alloc: + return rc; +} + +/** + * Receive initialisation completion + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + */ +static void rndis_rx_initialise ( struct rndis_device *rndis, + struct io_buffer *iobuf ) { + struct rndis_initialise_completion *cmplt; + size_t len = iob_len ( iobuf ); + unsigned int id; + int rc; + + /* Sanity check */ + if ( len < sizeof ( *cmplt ) ) { + DBGC ( rndis, "RNDIS %s received underlength initialisation " + "completion:\n", rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EINVAL; + goto err_len; + } + cmplt = iobuf->data; + + /* Extract request ID */ + id = cmplt->id; /* Non-endian */ + + /* Check status */ + if ( cmplt->status ) { + DBGC ( rndis, "RNDIS %s received initialisation completion " + "failure %#08x\n", rndis->name, + le32_to_cpu ( cmplt->status ) ); + rc = -EIO; + goto err_status; + } + + /* Success */ + rc = 0; + + err_status: + /* Record completion result if applicable */ + if ( id == rndis->wait_id ) { + rndis->wait_id = 0; + rndis->wait_rc = rc; + } + err_len: + free_iob ( iobuf ); +} + +/** + * Initialise RNDIS + * + * @v rndis RNDIS device + * @ret rc Return status code + */ +static int rndis_initialise ( struct rndis_device *rndis ) { + int rc; + + /* Transmit initialisation message */ + if ( ( rc = rndis_tx_initialise ( rndis, RNDIS_INIT_ID ) ) != 0 ) + return rc; + + /* Wait for response */ + if ( ( rc = rndis_wait ( rndis, RNDIS_INIT_ID ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Transmit halt message + * + * @v rndis RNDIS device + * @ret rc Return status code + */ +static int rndis_tx_halt ( struct rndis_device *rndis ) { + struct io_buffer *iobuf; + struct rndis_halt_message *msg; + int rc; + + /* Allocate I/O buffer */ + iobuf = rndis_alloc_iob ( sizeof ( *msg ) ); + if ( ! iobuf ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Construct message */ + msg = iob_put ( iobuf, sizeof ( *msg ) ); + memset ( msg, 0, sizeof ( *msg ) ); + + /* Transmit message */ + if ( ( rc = rndis_tx_message ( rndis, iobuf, RNDIS_HALT_MSG ) ) != 0 ) + goto err_tx; + + return 0; + + err_tx: + free_iob ( iobuf ); + err_alloc: + return rc; +} + +/** + * Halt RNDIS + * + * @v rndis RNDIS device + * @ret rc Return status code + */ +static int rndis_halt ( struct rndis_device *rndis ) { + int rc; + + /* Transmit halt message */ + if ( ( rc = rndis_tx_halt ( rndis ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Transmit OID message + * + * @v rndis RNDIS device + * @v oid Object ID + * @v data New OID value (or NULL to query current value) + * @v len Length of new OID value + * @ret rc Return status code + */ +static int rndis_tx_oid ( struct rndis_device *rndis, unsigned int oid, + const void *data, size_t len ) { + struct io_buffer *iobuf; + struct rndis_oid_message *msg; + unsigned int type; + int rc; + + /* Allocate I/O buffer */ + iobuf = rndis_alloc_iob ( sizeof ( *msg ) + len ); + if ( ! iobuf ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Construct message. We use the OID as the request ID. */ + msg = iob_put ( iobuf, sizeof ( *msg ) ); + memset ( msg, 0, sizeof ( *msg ) ); + msg->id = oid; /* Non-endian */ + msg->oid = cpu_to_le32 ( oid ); + msg->offset = cpu_to_le32 ( sizeof ( *msg ) ); + msg->len = cpu_to_le32 ( len ); + memcpy ( iob_put ( iobuf, len ), data, len ); + + /* Transmit message */ + type = ( data ? RNDIS_SET_MSG : RNDIS_QUERY_MSG ); + if ( ( rc = rndis_tx_message ( rndis, iobuf, type ) ) != 0 ) + goto err_tx; + + return 0; + + err_tx: + free_iob ( iobuf ); + err_alloc: + return rc; +} + +/** + * Receive query OID completion + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + */ +static void rndis_rx_query_oid ( struct rndis_device *rndis, + struct io_buffer *iobuf ) { + struct net_device *netdev = rndis->netdev; + struct rndis_query_completion *cmplt; + size_t len = iob_len ( iobuf ); + size_t info_offset; + size_t info_len; + unsigned int id; + void *info; + uint32_t *link_status; + int rc; + + /* Sanity check */ + if ( len < sizeof ( *cmplt ) ) { + DBGC ( rndis, "RNDIS %s received underlength query " + "completion:\n", rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EINVAL; + goto err_len; + } + cmplt = iobuf->data; + + /* Extract request ID */ + id = cmplt->id; /* Non-endian */ + + /* Check status */ + if ( cmplt->status ) { + DBGC ( rndis, "RNDIS %s received query completion failure " + "%#08x\n", rndis->name, le32_to_cpu ( cmplt->status ) ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EIO; + goto err_status; + } + + /* Locate and sanity check information buffer */ + info_offset = le32_to_cpu ( cmplt->offset ); + info_len = le32_to_cpu ( cmplt->len ); + if ( ( info_offset > len ) || ( info_len > ( len - info_offset ) ) ) { + DBGC ( rndis, "RNDIS %s query completion information exceeds " + "packet:\n", rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EINVAL; + goto err_info; + } + info = ( ( ( void * ) cmplt ) + info_offset ); + + /* Handle OID */ + switch ( id ) { + + case RNDIS_OID_802_3_PERMANENT_ADDRESS: + if ( info_len > sizeof ( netdev->hw_addr ) ) + info_len = sizeof ( netdev->hw_addr ); + memcpy ( netdev->hw_addr, info, info_len ); + break; + + case RNDIS_OID_802_3_CURRENT_ADDRESS: + if ( info_len > sizeof ( netdev->ll_addr ) ) + info_len = sizeof ( netdev->ll_addr ); + memcpy ( netdev->ll_addr, info, info_len ); + break; + + case RNDIS_OID_GEN_MEDIA_CONNECT_STATUS: + if ( info_len != sizeof ( *link_status ) ) { + DBGC ( rndis, "RNDIS %s invalid link status:\n", + rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EPROTO; + goto err_link_status; + } + link_status = info; + if ( *link_status == 0 ) { + DBGC ( rndis, "RNDIS %s link is up\n", rndis->name ); + netdev_link_up ( netdev ); + } else { + DBGC ( rndis, "RNDIS %s link is down: %#08x\n", + rndis->name, le32_to_cpu ( *link_status ) ); + netdev_link_down ( netdev ); + } + break; + + default: + DBGC ( rndis, "RNDIS %s unexpected query completion ID %#08x\n", + rndis->name, id ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EPROTO; + goto err_id; + } + + /* Success */ + rc = 0; + + err_id: + err_link_status: + err_info: + err_status: + /* Record completion result if applicable */ + if ( id == rndis->wait_id ) { + rndis->wait_id = 0; + rndis->wait_rc = rc; + } + err_len: + /* Free I/O buffer */ + free_iob ( iobuf ); +} + +/** + * Receive set OID completion + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + */ +static void rndis_rx_set_oid ( struct rndis_device *rndis, + struct io_buffer *iobuf ) { + struct rndis_set_completion *cmplt; + size_t len = iob_len ( iobuf ); + unsigned int id; + int rc; + + /* Sanity check */ + if ( len < sizeof ( *cmplt ) ) { + DBGC ( rndis, "RNDIS %s received underlength set completion:\n", + rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EINVAL; + goto err_len; + } + cmplt = iobuf->data; + + /* Extract request ID */ + id = cmplt->id; /* Non-endian */ + + /* Check status */ + if ( cmplt->status ) { + DBGC ( rndis, "RNDIS %s received set completion failure " + "%#08x\n", rndis->name, le32_to_cpu ( cmplt->status ) ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EIO; + goto err_status; + } + + /* Success */ + rc = 0; + + err_status: + /* Record completion result if applicable */ + if ( id == rndis->wait_id ) { + rndis->wait_id = 0; + rndis->wait_rc = rc; + } + err_len: + /* Free I/O buffer */ + free_iob ( iobuf ); +} + +/** + * Query or set OID + * + * @v rndis RNDIS device + * @v oid Object ID + * @v data New OID value (or NULL to query current value) + * @v len Length of new OID value + * @ret rc Return status code + */ +static int rndis_oid ( struct rndis_device *rndis, unsigned int oid, + const void *data, size_t len ) { + int rc; + + /* Transmit query */ + if ( ( rc = rndis_tx_oid ( rndis, oid, data, len ) ) != 0 ) + return rc; + + /* Wait for response */ + if ( ( rc = rndis_wait ( rndis, oid ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Receive indicate status message + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + */ +static void rndis_rx_status ( struct rndis_device *rndis, + struct io_buffer *iobuf ) { + struct net_device *netdev = rndis->netdev; + struct rndis_indicate_status_message *msg; + size_t len = iob_len ( iobuf ); + unsigned int status; + int rc; + + /* Sanity check */ + if ( len < sizeof ( *msg ) ) { + DBGC ( rndis, "RNDIS %s received underlength status message:\n", + rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EINVAL; + goto err_len; + } + msg = iobuf->data; + + /* Extract status */ + status = le32_to_cpu ( msg->status ); + + /* Handle status */ + switch ( msg->status ) { + + case RNDIS_STATUS_MEDIA_CONNECT: + DBGC ( rndis, "RNDIS %s link is up\n", rndis->name ); + netdev_link_up ( netdev ); + break; + + case RNDIS_STATUS_MEDIA_DISCONNECT: + DBGC ( rndis, "RNDIS %s link is down\n", rndis->name ); + netdev_link_down ( netdev ); + break; + + case RNDIS_STATUS_WTF_WORLD: + /* Ignore */ + break; + + default: + DBGC ( rndis, "RNDIS %s unexpected status %#08x:\n", + rndis->name, status ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -ENOTSUP; + goto err_status; + } + + /* Free I/O buffer */ + free_iob ( iobuf ); + + return; + + err_status: + err_len: + /* Report error via network device statistics */ + netdev_rx_err ( netdev, iobuf, rc ); +} + +/** + * Receive RNDIS message + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + * @v type Message type + */ +static void rndis_rx_message ( struct rndis_device *rndis, + struct io_buffer *iobuf, unsigned int type ) { + struct net_device *netdev = rndis->netdev; + int rc; + + /* Handle packet */ + switch ( type ) { + + case RNDIS_PACKET_MSG: + rndis_rx_data ( rndis, iob_disown ( iobuf ) ); + break; + + case RNDIS_INITIALISE_CMPLT: + rndis_rx_initialise ( rndis, iob_disown ( iobuf ) ); + break; + + case RNDIS_QUERY_CMPLT: + rndis_rx_query_oid ( rndis, iob_disown ( iobuf ) ); + break; + + case RNDIS_SET_CMPLT: + rndis_rx_set_oid ( rndis, iob_disown ( iobuf ) ); + break; + + case RNDIS_INDICATE_STATUS_MSG: + rndis_rx_status ( rndis, iob_disown ( iobuf ) ); + break; + + default: + DBGC ( rndis, "RNDIS %s received unexpected type %#08x\n", + rndis->name, type ); + DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EPROTO; + goto err_type; + } + + return; + + err_type: + /* Report error via network device statistics */ + netdev_rx_err ( netdev, iobuf, rc ); +} + +/** + * Receive packet from underlying transport layer + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + */ +void rndis_rx ( struct rndis_device *rndis, struct io_buffer *iobuf ) { + struct net_device *netdev = rndis->netdev; + struct rndis_header *header; + unsigned int type; + int rc; + + /* Sanity check */ + if ( iob_len ( iobuf ) < sizeof ( *header ) ) { + DBGC ( rndis, "RNDIS %s received underlength packet:\n", + rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto drop; + } + header = iobuf->data; + + /* Parse and strip header */ + type = le32_to_cpu ( header->type ); + iob_pull ( iobuf, sizeof ( *header ) ); + + /* Handle message */ + rndis_rx_message ( rndis, iob_disown ( iobuf ), type ); + + return; + + drop: + /* Record error */ + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); +} + +/** + * Discard packet from underlying transport layer + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + * @v rc Packet status code + */ +void rndis_rx_err ( struct rndis_device *rndis, struct io_buffer *iobuf, + int rc ) { + struct net_device *netdev = rndis->netdev; + + /* Record error */ + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); +} + +/** + * Set receive filter + * + * @v rndis RNDIS device + * @v filter Receive filter + * @ret rc Return status code + */ +static int rndis_filter ( struct rndis_device *rndis, unsigned int filter ) { + uint32_t value = cpu_to_le32 ( filter ); + int rc; + + /* Set receive filter */ + if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_CURRENT_PACKET_FILTER, + &value, sizeof ( value ) ) ) != 0 ) { + DBGC ( rndis, "RNDIS %s could not set receive filter to %#08x: " + "%s\n", rndis->name, filter, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int rndis_open ( struct net_device *netdev ) { + struct rndis_device *rndis = netdev->priv; + int rc; + + /* Open RNDIS device */ + if ( ( rc = rndis->op->open ( rndis ) ) != 0 ) { + DBGC ( rndis, "RNDIS %s could not open: %s\n", + rndis->name, strerror ( rc ) ); + goto err_open; + } + + /* Initialise RNDIS */ + if ( ( rc = rndis_initialise ( rndis ) ) != 0 ) + goto err_initialise; + + /* Set receive filter */ + if ( ( rc = rndis_filter ( rndis, ( RNDIS_FILTER_UNICAST | + RNDIS_FILTER_MULTICAST | + RNDIS_FILTER_ALL_MULTICAST | + RNDIS_FILTER_BROADCAST | + RNDIS_FILTER_PROMISCUOUS ) ) ) != 0) + goto err_set_filter; + + /* Update link status */ + if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, + NULL, 0 ) ) != 0 ) + goto err_query_link; + + return 0; + + err_query_link: + err_set_filter: + rndis_halt ( rndis ); + err_initialise: + rndis->op->close ( rndis ); + err_open: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void rndis_close ( struct net_device *netdev ) { + struct rndis_device *rndis = netdev->priv; + + /* Clear receive filter */ + rndis_filter ( rndis, 0 ); + + /* Halt RNDIS device */ + rndis_halt ( rndis ); + + /* Close RNDIS device */ + rndis->op->close ( rndis ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int rndis_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct rndis_device *rndis = netdev->priv; + + /* Transmit data packet */ + return rndis_tx_data ( rndis, iobuf ); +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void rndis_poll ( struct net_device *netdev ) { + struct rndis_device *rndis = netdev->priv; + + /* Poll RNDIS device */ + rndis->op->poll ( rndis ); +} + +/** Network device operations */ +static struct net_device_operations rndis_operations = { + .open = rndis_open, + .close = rndis_close, + .transmit = rndis_transmit, + .poll = rndis_poll, +}; + +/** + * Allocate RNDIS device + * + * @v priv_len Length of private data + * @ret rndis RNDIS device, or NULL on allocation failure + */ +struct rndis_device * alloc_rndis ( size_t priv_len ) { + struct net_device *netdev; + struct rndis_device *rndis; + + /* Allocate and initialise structure */ + netdev = alloc_etherdev ( sizeof ( *rndis ) + priv_len ); + if ( ! netdev ) + return NULL; + netdev_init ( netdev, &rndis_operations ); + rndis = netdev->priv; + rndis->netdev = netdev; + rndis->priv = ( ( ( void * ) rndis ) + sizeof ( *rndis ) ); + + return rndis; +} + +/** + * Register RNDIS device + * + * @v rndis RNDIS device + * @ret rc Return status code + * + * Note that this routine will open and use the RNDIS device in order + * to query the MAC address. The device must be immediately ready for + * use prior to registration. + */ +int register_rndis ( struct rndis_device *rndis ) { + struct net_device *netdev = rndis->netdev; + int rc; + + /* Assign device name (for debugging) */ + rndis->name = netdev->dev->name; + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) { + DBGC ( rndis, "RNDIS %s could not register: %s\n", + rndis->name, strerror ( rc ) ); + goto err_register; + } + + /* Open RNDIS device to read MAC addresses */ + if ( ( rc = rndis->op->open ( rndis ) ) != 0 ) { + DBGC ( rndis, "RNDIS %s could not open: %s\n", + rndis->name, strerror ( rc ) ); + goto err_open; + } + + /* Initialise RNDIS */ + if ( ( rc = rndis_initialise ( rndis ) ) != 0 ) + goto err_initialise; + + /* Query permanent MAC address */ + if ( ( rc = rndis_oid ( rndis, RNDIS_OID_802_3_PERMANENT_ADDRESS, + NULL, 0 ) ) != 0 ) + goto err_query_permanent; + + /* Query current MAC address */ + if ( ( rc = rndis_oid ( rndis, RNDIS_OID_802_3_CURRENT_ADDRESS, + NULL, 0 ) ) != 0 ) + goto err_query_current; + + /* Get link status */ + if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, + NULL, 0 ) ) != 0 ) + goto err_query_link; + + /* Halt RNDIS device */ + rndis_halt ( rndis ); + + /* Close RNDIS device */ + rndis->op->close ( rndis ); + + return 0; + + err_query_link: + err_query_current: + err_query_permanent: + rndis_halt ( rndis ); + err_initialise: + rndis->op->close ( rndis ); + err_open: + unregister_netdev ( netdev ); + err_register: + return rc; +} + +/** + * Unregister RNDIS device + * + * @v rndis RNDIS device + */ +void unregister_rndis ( struct rndis_device *rndis ) { + struct net_device *netdev = rndis->netdev; + + /* Unregister network device */ + unregister_netdev ( netdev ); +} + +/** + * Free RNDIS device + * + * @v rndis RNDIS device + */ +void free_rndis ( struct rndis_device *rndis ) { + struct net_device *netdev = rndis->netdev; + + /* Free network device */ + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} diff --git a/qemu/roms/ipxe/src/net/socket.c b/qemu/roms/ipxe/src/net/socket.c index 24f6a0892..2009ab237 100644 --- a/qemu/roms/ipxe/src/net/socket.c +++ b/qemu/roms/ipxe/src/net/socket.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/stp.c b/qemu/roms/ipxe/src/net/stp.c new file mode 100644 index 000000000..d4e65a1a2 --- /dev/null +++ b/qemu/roms/ipxe/src/net/stp.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Spanning Tree Protocol (STP) + * + */ + +/* Disambiguate the various error causes */ +#define ENOTSUP_PROTOCOL __einfo_error ( EINFO_ENOTSUP_PROTOCOL ) +#define EINFO_ENOTSUP_PROTOCOL \ + __einfo_uniqify ( EINFO_ENOTSUP, 0x01, \ + "Non-STP packet received" ) +#define ENOTSUP_VERSION __einfo_error ( EINFO_ENOTSUP_VERSION ) +#define EINFO_ENOTSUP_VERSION \ + __einfo_uniqify ( EINFO_ENOTSUP, 0x01, \ + "Legacy STP packet received" ) +#define ENOTSUP_TYPE __einfo_error ( EINFO_ENOTSUP_TYPE ) +#define EINFO_ENOTSUP_TYPE \ + __einfo_uniqify ( EINFO_ENOTSUP, 0x01, \ + "Non-RSTP packet received" ) + +/** + * Process incoming STP packets + * + * @v iobuf I/O buffer + * @v netdev Network device + * @v ll_source Link-layer source address + * @v flags Packet flags + * @ret rc Return status code + */ +static int stp_rx ( struct io_buffer *iobuf, struct net_device *netdev, + const void *ll_dest __unused, + const void *ll_source __unused, + unsigned int flags __unused ) { + struct stp_bpdu *stp; + unsigned int hello; + int rc; + + /* Sanity check */ + if ( iob_len ( iobuf ) < sizeof ( *stp ) ) { + DBGC ( netdev, "STP %s received underlength packet (%zd " + "bytes):\n", netdev->name, iob_len ( iobuf ) ); + DBGC_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto done; + } + stp = iobuf->data; + + /* Ignore non-RSTP packets */ + if ( stp->protocol != htons ( STP_PROTOCOL ) ) { + DBGC ( netdev, "STP %s ignoring non-STP packet (protocol " + "%#04x)\n", netdev->name, ntohs ( stp->protocol ) ); + rc = -ENOTSUP_PROTOCOL; + goto done; + } + if ( stp->version < STP_VERSION_RSTP ) { + DBGC ( netdev, "STP %s received legacy STP packet (version " + "%#02x)\n", netdev->name, stp->version ); + rc = -ENOTSUP_VERSION; + goto done; + } + if ( stp->type != STP_TYPE_RSTP ) { + DBGC ( netdev, "STP %s received non-RSTP packet (type %#02x)\n", + netdev->name, stp->type ); + rc = -ENOTSUP_TYPE; + goto done; + } + + /* Dump information */ + DBGC2 ( netdev, "STP %s %s port %#04x flags %#02x hello %d delay %d\n", + netdev->name, eth_ntoa ( stp->sender.mac ), ntohs ( stp->port ), + stp->flags, ntohs ( stp->hello ), ntohs ( stp->delay ) ); + + /* Check if port is forwarding */ + if ( ! ( stp->flags & STP_FL_FORWARDING ) ) { + /* Port is not forwarding: block link for two hello times */ + DBGC ( netdev, "STP %s %s port %#04x flags %#02x is not " + "forwarding\n", + netdev->name, eth_ntoa ( stp->sender.mac ), + ntohs ( stp->port ), stp->flags ); + hello = ( ( ntohs ( stp->hello ) * TICKS_PER_SEC ) / 256 ); + netdev_link_block ( netdev, ( hello * 2 ) ); + rc = -ENETUNREACH; + goto done; + } + + /* Success */ + if ( netdev_link_blocked ( netdev ) ) { + DBGC ( netdev, "STP %s %s port %#04x flags %#02x is " + "forwarding\n", + netdev->name, eth_ntoa ( stp->sender.mac ), + ntohs ( stp->port ), stp->flags ); + } + netdev_link_unblock ( netdev ); + rc = 0; + + done: + free_iob ( iobuf ); + return rc; +} + +/** + * Transcribe STP address + * + * @v net_addr STP address + * @ret string "" + * + * This operation is meaningless for the STP protocol. + */ +static const char * stp_ntoa ( const void *net_addr __unused ) { + return ""; +} + +/** STP network protocol */ +struct net_protocol stp_protocol __net_protocol = { + .name = "STP", + .net_proto = htons ( ETH_P_STP ), + .rx = stp_rx, + .ntoa = stp_ntoa, +}; diff --git a/qemu/roms/ipxe/src/net/tcp.c b/qemu/roms/ipxe/src/net/tcp.c index 987cb63e1..c69c83b85 100644 --- a/qemu/roms/ipxe/src/net/tcp.c +++ b/qemu/roms/ipxe/src/net/tcp.c @@ -26,7 +26,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** A TCP connection */ struct tcp_connection { @@ -101,8 +101,9 @@ struct tcp_connection { * Equivalent to Rcv.Wind.Scale in RFC 1323 terminology */ uint8_t rcv_win_scale; - /** Maximum receive window */ - uint32_t max_rcv_win; + + /** Selective acknowledgement list (in host-endian order) */ + struct tcp_sack_block sack[TCP_SACK_MAX]; /** Transmit queue */ struct list_head tx_queue; @@ -129,6 +130,8 @@ enum tcp_flags { TCP_TS_ENABLED = 0x0002, /** TCP acknowledgement is pending */ TCP_ACK_PENDING = 0x0004, + /** TCP selective acknowledgement is enabled */ + TCP_SACK_ENABLED = 0x0008, }; /** TCP internal header @@ -143,6 +146,8 @@ struct tcp_rx_queued_header { * enqueued, and so excludes the SYN, if present. */ uint32_t seq; + /** Next SEQ value, in host-endian order */ + uint32_t nxt; /** Flags * * Only FIN is valid within this flags byte; all other flags @@ -284,7 +289,6 @@ static int tcp_open ( struct interface *xfer, struct sockaddr *peer, tcp->tcp_state = TCP_STATE_SENT ( TCP_SYN ); tcp_dump_state ( tcp ); tcp->snd_seq = random(); - tcp->max_rcv_win = TCP_MAX_WINDOW_SIZE; INIT_LIST_HEAD ( &tcp->tx_queue ); INIT_LIST_HEAD ( &tcp->rx_queue ); memcpy ( &tcp->peer, st_peer, sizeof ( tcp->peer ) ); @@ -396,6 +400,7 @@ static void tcp_close ( struct tcp_connection *tcp, int rc ) { tcp->tcp_state |= TCP_STATE_SENT ( TCP_FIN ); tcp_dump_state ( tcp ); + process_add ( &tcp->process ); /* Add a pending operation for the FIN */ pending_get ( &tcp->pending_flags ); @@ -449,6 +454,94 @@ static size_t tcp_xfer_window ( struct tcp_connection *tcp ) { return tcp_xmit_win ( tcp ); } +/** + * Find selective acknowledgement block + * + * @v tcp TCP connection + * @v seq SEQ value in SACK block (in host-endian order) + * @v sack SACK block to fill in (in host-endian order) + * @ret len Length of SACK block + */ +static uint32_t tcp_sack_block ( struct tcp_connection *tcp, uint32_t seq, + struct tcp_sack_block *sack ) { + struct io_buffer *iobuf; + struct tcp_rx_queued_header *tcpqhdr; + uint32_t left = tcp->rcv_ack; + uint32_t right = left; + + /* Find highest block which does not start after SEQ */ + list_for_each_entry ( iobuf, &tcp->rx_queue, list ) { + tcpqhdr = iobuf->data; + if ( tcp_cmp ( tcpqhdr->seq, right ) > 0 ) { + if ( tcp_cmp ( tcpqhdr->seq, seq ) > 0 ) + break; + left = tcpqhdr->seq; + } + if ( tcp_cmp ( tcpqhdr->nxt, right ) > 0 ) + right = tcpqhdr->nxt; + } + + /* Fail if this block does not contain SEQ */ + if ( tcp_cmp ( right, seq ) < 0 ) + return 0; + + /* Populate SACK block */ + sack->left = left; + sack->right = right; + return ( right - left ); +} + +/** + * Update TCP selective acknowledgement list + * + * @v tcp TCP connection + * @v seq SEQ value in first SACK block (in host-endian order) + * @ret count Number of SACK blocks + */ +static unsigned int tcp_sack ( struct tcp_connection *tcp, uint32_t seq ) { + struct tcp_sack_block sack[TCP_SACK_MAX]; + unsigned int old = 0; + unsigned int new = 0; + unsigned int i; + uint32_t len; + + /* Populate first new SACK block */ + len = tcp_sack_block ( tcp, seq, &sack[0] ); + if ( len ) + new++; + + /* Populate remaining new SACK blocks based on old SACK blocks */ + for ( old = 0 ; old < TCP_SACK_MAX ; old++ ) { + + /* Stop if we run out of space in the new list */ + if ( new == TCP_SACK_MAX ) + break; + + /* Skip empty old SACK blocks */ + if ( tcp->sack[old].left == tcp->sack[old].right ) + continue; + + /* Populate new SACK block */ + len = tcp_sack_block ( tcp, tcp->sack[old].left, &sack[new] ); + if ( len == 0 ) + continue; + + /* Eliminate duplicates */ + for ( i = 0 ; i < new ; i++ ) { + if ( sack[i].left == sack[new].left ) { + new--; + break; + } + } + new++; + } + + /* Update SACK list */ + memset ( tcp->sack, 0, sizeof ( tcp->sack ) ); + memcpy ( tcp->sack, sack, ( new * sizeof ( tcp->sack[0] ) ) ); + return new; +} + /** * Process TCP transmit queue * @@ -493,9 +586,10 @@ static size_t tcp_process_tx_queue ( struct tcp_connection *tcp, size_t max_len, } /** - * Transmit any outstanding data + * Transmit any outstanding data (with selective acknowledgement) * * @v tcp TCP connection + * @v sack_seq SEQ for first selective acknowledgement (if any) * * Transmits any outstanding data on the connection. * @@ -503,17 +597,22 @@ static size_t tcp_process_tx_queue ( struct tcp_connection *tcp, size_t max_len, * will have been started if necessary, and so the stack will * eventually attempt to retransmit the failed packet. */ -static void tcp_xmit ( struct tcp_connection *tcp ) { +static void tcp_xmit_sack ( struct tcp_connection *tcp, uint32_t sack_seq ) { struct io_buffer *iobuf; struct tcp_header *tcphdr; struct tcp_mss_option *mssopt; struct tcp_window_scale_padded_option *wsopt; struct tcp_timestamp_padded_option *tsopt; + struct tcp_sack_permitted_padded_option *spopt; + struct tcp_sack_padded_option *sackopt; + struct tcp_sack_block *sack; void *payload; unsigned int flags; + unsigned int sack_count; + unsigned int i; size_t len = 0; + size_t sack_len; uint32_t seq_len; - uint32_t app_win; uint32_t max_rcv_win; uint32_t max_representable_win; int rc; @@ -567,10 +666,9 @@ static void tcp_xmit ( struct tcp_connection *tcp ) { tcp_process_tx_queue ( tcp, len, iobuf, 0 ); /* Expand receive window if possible */ - max_rcv_win = tcp->max_rcv_win; - app_win = xfer_window ( &tcp->xfer ); - if ( max_rcv_win > app_win ) - max_rcv_win = app_win; + max_rcv_win = xfer_window ( &tcp->xfer ); + if ( max_rcv_win > TCP_MAX_WINDOW_SIZE ) + max_rcv_win = TCP_MAX_WINDOW_SIZE; max_representable_win = ( 0xffff << tcp->rcv_win_scale ); if ( max_rcv_win > max_representable_win ) max_rcv_win = max_representable_win; @@ -590,6 +688,10 @@ static void tcp_xmit ( struct tcp_connection *tcp ) { wsopt->wsopt.kind = TCP_OPTION_WS; wsopt->wsopt.length = sizeof ( wsopt->wsopt ); wsopt->wsopt.scale = TCP_RX_WINDOW_SCALE; + spopt = iob_push ( iobuf, sizeof ( *spopt ) ); + memset ( spopt->nop, TCP_OPTION_NOP, sizeof ( spopt ) ); + spopt->spopt.kind = TCP_OPTION_SACK_PERMITTED; + spopt->spopt.length = sizeof ( spopt->spopt ); } if ( ( flags & TCP_SYN ) || ( tcp->flags & TCP_TS_ENABLED ) ) { tsopt = iob_push ( iobuf, sizeof ( *tsopt ) ); @@ -599,6 +701,21 @@ static void tcp_xmit ( struct tcp_connection *tcp ) { tsopt->tsopt.tsval = htonl ( currticks() ); tsopt->tsopt.tsecr = htonl ( tcp->ts_recent ); } + if ( ( tcp->flags & TCP_SACK_ENABLED ) && + ( ! list_empty ( &tcp->rx_queue ) ) && + ( ( sack_count = tcp_sack ( tcp, sack_seq ) ) != 0 ) ) { + sack_len = ( sack_count * sizeof ( *sack ) ); + sackopt = iob_push ( iobuf, ( sizeof ( *sackopt ) + sack_len )); + memset ( sackopt->nop, TCP_OPTION_NOP, sizeof ( sackopt->nop )); + sackopt->sackopt.kind = TCP_OPTION_SACK; + sackopt->sackopt.length = + ( sizeof ( sackopt->sackopt ) + sack_len ); + sack = ( ( ( void * ) sackopt ) + sizeof ( *sackopt ) ); + for ( i = 0 ; i < sack_count ; i++, sack++ ) { + sack->left = htonl ( tcp->sack[i].left ); + sack->right = htonl ( tcp->sack[i].right ); + } + } if ( len != 0 ) flags |= TCP_PSH; tcphdr = iob_push ( iobuf, sizeof ( *tcphdr ) ); @@ -635,6 +752,17 @@ static void tcp_xmit ( struct tcp_connection *tcp ) { profile_stop ( &tcp_tx_profiler ); } +/** + * Transmit any outstanding data + * + * @v tcp TCP connection + */ +static void tcp_xmit ( struct tcp_connection *tcp ) { + + /* Transmit without an explicit first SACK */ + tcp_xmit_sack ( tcp, tcp->rcv_ack ); +} + /** TCP process descriptor */ static struct process_descriptor tcp_process_desc = PROC_DESC_ONCE ( struct tcp_connection, process, tcp_xmit ); @@ -804,6 +932,12 @@ static void tcp_rx_opts ( struct tcp_connection *tcp, const void *data, case TCP_OPTION_WS: options->wsopt = data; break; + case TCP_OPTION_SACK_PERMITTED: + options->spopt = data; + break; + case TCP_OPTION_SACK: + /* Ignore received SACKs */ + break; case TCP_OPTION_TS: options->tsopt = data; break; @@ -823,6 +957,7 @@ static void tcp_rx_opts ( struct tcp_connection *tcp, const void *data, * @v seq_len Sequence space length to consume */ static void tcp_rx_seq ( struct tcp_connection *tcp, uint32_t seq_len ) { + unsigned int sack; /* Sanity check */ assert ( seq_len > 0 ); @@ -840,6 +975,16 @@ static void tcp_rx_seq ( struct tcp_connection *tcp, uint32_t seq_len ) { /* Update timestamp */ tcp->ts_recent = tcp->ts_val; + /* Update SACK list */ + for ( sack = 0 ; sack < TCP_SACK_MAX ; sack++ ) { + if ( tcp->sack[sack].left == tcp->sack[sack].right ) + continue; + if ( tcp_cmp ( tcp->sack[sack].left, tcp->rcv_ack ) < 0 ) + tcp->sack[sack].left = tcp->rcv_ack; + if ( tcp_cmp ( tcp->sack[sack].right, tcp->rcv_ack ) < 0 ) + tcp->sack[sack].right = tcp->rcv_ack; + } + /* Mark ACK as pending */ tcp->flags |= TCP_ACK_PENDING; } @@ -860,6 +1005,8 @@ static int tcp_rx_syn ( struct tcp_connection *tcp, uint32_t seq, tcp->rcv_ack = seq; if ( options->tsopt ) tcp->flags |= TCP_TS_ENABLED; + if ( options->spopt ) + tcp->flags |= TCP_SACK_ENABLED; if ( options->wsopt ) { tcp->snd_win_scale = options->wsopt->scale; tcp->rcv_win_scale = TCP_RX_WINDOW_SCALE; @@ -1070,6 +1217,7 @@ static void tcp_rx_enqueue ( struct tcp_connection *tcp, uint32_t seq, struct io_buffer *queued; size_t len; uint32_t seq_len; + uint32_t nxt; /* Calculate remaining flags and sequence length. Note that * SYN, if present, has already been processed by this point. @@ -1077,6 +1225,7 @@ static void tcp_rx_enqueue ( struct tcp_connection *tcp, uint32_t seq, flags &= TCP_FIN; len = iob_len ( iobuf ); seq_len = ( len + ( flags ? 1 : 0 ) ); + nxt = ( seq + seq_len ); /* Discard immediately (to save memory) if: * @@ -1087,7 +1236,7 @@ static void tcp_rx_enqueue ( struct tcp_connection *tcp, uint32_t seq, */ if ( ( ! ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) ) || ( tcp_cmp ( seq, tcp->rcv_ack + tcp->rcv_win ) >= 0 ) || - ( tcp_cmp ( seq + seq_len, tcp->rcv_ack ) < 0 ) || + ( tcp_cmp ( nxt, tcp->rcv_ack ) < 0 ) || ( seq_len == 0 ) ) { free_iob ( iobuf ); return; @@ -1096,6 +1245,7 @@ static void tcp_rx_enqueue ( struct tcp_connection *tcp, uint32_t seq, /* Add internal header */ tcpqhdr = iob_push ( iobuf, sizeof ( *tcpqhdr ) ); tcpqhdr->seq = seq; + tcpqhdr->nxt = nxt; tcpqhdr->flags = flags; /* Add to RX queue */ @@ -1289,7 +1439,7 @@ static int tcp_rx ( struct io_buffer *iobuf, if ( list_empty ( &tcp->rx_queue ) ) { process_add ( &tcp->process ); } else { - tcp_xmit ( tcp ); + tcp_xmit_sack ( tcp, seq ); } /* If this packet was the last we expect to receive, set up @@ -1328,24 +1478,12 @@ struct tcpip_protocol tcp_protocol __tcpip_protocol = { static unsigned int tcp_discard ( void ) { struct tcp_connection *tcp; struct io_buffer *iobuf; - struct tcp_rx_queued_header *tcpqhdr; - uint32_t max_win; unsigned int discarded = 0; /* Try to drop one queued RX packet from each connection */ list_for_each_entry ( tcp, &tcp_conns, list ) { list_for_each_entry_reverse ( iobuf, &tcp->rx_queue, list ) { - /* Limit window to prevent future discards */ - tcpqhdr = iobuf->data; - max_win = ( tcpqhdr->seq - tcp->rcv_ack ); - if ( max_win < tcp->max_rcv_win ) { - DBGC ( tcp, "TCP %p reducing maximum window " - "from %d to %d\n", - tcp, tcp->max_rcv_win, max_win ); - tcp->max_rcv_win = max_win; - } - /* Remove packet from queue */ list_del ( &iobuf->list ); free_iob ( iobuf ); @@ -1364,13 +1502,68 @@ struct cache_discarder tcp_discarder __cache_discarder ( CACHE_NORMAL ) = { .discard = tcp_discard, }; +/** + * Find first TCP connection that has not yet been closed + * + * @ret tcp First unclosed connection, or NULL + */ +static struct tcp_connection * tcp_first_unclosed ( void ) { + struct tcp_connection *tcp; + + /* Find first connection which has not yet been closed */ + list_for_each_entry ( tcp, &tcp_conns, list ) { + if ( ! ( tcp->flags & TCP_XFER_CLOSED ) ) + return tcp; + } + return NULL; +} + +/** + * Find first TCP connection that has not yet finished all operations + * + * @ret tcp First unfinished connection, or NULL + */ +static struct tcp_connection * tcp_first_unfinished ( void ) { + struct tcp_connection *tcp; + + /* Find first connection which has not yet closed gracefully, + * or which still has a pending transmission (e.g. to ACK the + * received FIN). + */ + list_for_each_entry ( tcp, &tcp_conns, list ) { + if ( ( ! TCP_CLOSED_GRACEFULLY ( tcp->tcp_state ) ) || + process_running ( &tcp->process ) ) { + return tcp; + } + } + return NULL; +} + /** * Shut down all TCP connections * */ static void tcp_shutdown ( int booting __unused ) { struct tcp_connection *tcp; + unsigned long start; + unsigned long elapsed; + + /* Initiate a graceful close of all connections, allowing for + * the fact that the connection list may change as we do so. + */ + while ( ( tcp = tcp_first_unclosed() ) ) { + DBGC ( tcp, "TCP %p closing for shutdown\n", tcp ); + tcp_close ( tcp, -ECANCELED ); + } + + /* Wait for all connections to finish closing gracefully */ + start = currticks(); + while ( ( tcp = tcp_first_unfinished() ) && + ( ( elapsed = ( currticks() - start ) ) < TCP_FINISH_TIMEOUT )){ + step(); + } + /* Forcibly close any remaining connections */ while ( ( tcp = list_first_entry ( &tcp_conns, struct tcp_connection, list ) ) != NULL ) { tcp->tcp_state = TCP_CLOSED; @@ -1380,7 +1573,7 @@ static void tcp_shutdown ( int booting __unused ) { } /** TCP shutdown function */ -struct startup_fn tcp_startup_fn __startup_fn ( STARTUP_EARLY ) = { +struct startup_fn tcp_startup_fn __startup_fn ( STARTUP_LATE ) = { .shutdown = tcp_shutdown, }; diff --git a/qemu/roms/ipxe/src/net/tcp/http.c b/qemu/roms/ipxe/src/net/tcp/http.c index 90bae9d7a..b000ed80f 100644 --- a/qemu/roms/ipxe/src/net/tcp/http.c +++ b/qemu/roms/ipxe/src/net/tcp/http.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file @@ -26,26 +30,20 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -#include #include #include #include FEATURE ( FEATURE_PROTOCOL, "HTTP", DHCP_EB_FEATURE_HTTP, 1 ); -/** - * Initiate an HTTP connection - * - * @v xfer Data transfer interface - * @v uri Uniform Resource Identifier - * @ret rc Return status code - */ -static int http_open ( struct interface *xfer, struct uri *uri ) { - return http_open_filter ( xfer, uri, HTTP_PORT, NULL ); -} - /** HTTP URI opener */ struct uri_opener http_uri_opener __uri_opener = { .scheme = "http", - .open = http_open, + .open = http_open_uri, +}; + +/** HTTP URI scheme */ +struct http_scheme http_scheme __http_scheme = { + .name = "http", + .port = HTTP_PORT, }; diff --git a/qemu/roms/ipxe/src/net/tcp/httpauth.c b/qemu/roms/ipxe/src/net/tcp/httpauth.c new file mode 100644 index 000000000..fb6dcd035 --- /dev/null +++ b/qemu/roms/ipxe/src/net/tcp/httpauth.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * Hyper Text Transfer Protocol (HTTP) authentication + * + */ + +#include +#include +#include +#include + +/** + * Identify authentication scheme + * + * @v http HTTP transaction + * @v name Scheme name + * @ret auth Authentication scheme, or NULL + */ +static struct http_authentication * http_authentication ( const char *name ) { + struct http_authentication *auth; + + /* Identify authentication scheme */ + for_each_table_entry ( auth, HTTP_AUTHENTICATIONS ) { + if ( strcasecmp ( name, auth->name ) == 0 ) + return auth; + } + + return NULL; +} + +/** An HTTP "WWW-Authenticate" response field */ +struct http_www_authenticate_field { + /** Name */ + const char *name; + /** Offset */ + size_t offset; +}; + +/** Define an HTTP "WWW-Authenticate" response field */ +#define HTTP_WWW_AUTHENTICATE_FIELD( _name ) { \ + .name = #_name, \ + .offset = offsetof ( struct http_transaction, \ + response.auth._name ), \ + } + +/** + * Set HTTP "WWW-Authenticate" response field value + * + * @v http HTTP transaction + * @v field Response field + * @v value Field value + */ +static inline void +http_www_auth_field ( struct http_transaction *http, + struct http_www_authenticate_field *field, char *value ) { + char **ptr; + + ptr = ( ( ( void * ) http ) + field->offset ); + *ptr = value; +} + +/** HTTP "WWW-Authenticate" fields */ +static struct http_www_authenticate_field http_www_auth_fields[] = { + HTTP_WWW_AUTHENTICATE_FIELD ( realm ), + HTTP_WWW_AUTHENTICATE_FIELD ( qop ), + HTTP_WWW_AUTHENTICATE_FIELD ( algorithm ), + HTTP_WWW_AUTHENTICATE_FIELD ( nonce ), + HTTP_WWW_AUTHENTICATE_FIELD ( opaque ), +}; + +/** + * Parse HTTP "WWW-Authenticate" header + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ +static int http_parse_www_authenticate ( struct http_transaction *http, + char *line ) { + struct http_www_authenticate_field *field; + char *name; + char *key; + char *value; + unsigned int i; + + /* Get scheme name */ + name = http_token ( &line, NULL ); + if ( ! name ) { + DBGC ( http, "HTTP %p malformed WWW-Authenticate \"%s\"\n", + http, value ); + return -EPROTO; + } + + /* Identify scheme */ + http->response.auth.auth = http_authentication ( name ); + if ( ! http->response.auth.auth ) { + DBGC ( http, "HTTP %p unrecognised authentication scheme " + "\"%s\"\n", http, name ); + return -ENOTSUP; + } + + /* Process fields */ + while ( ( key = http_token ( &line, &value ) ) ) { + for ( i = 0 ; i < ( sizeof ( http_www_auth_fields ) / + sizeof ( http_www_auth_fields[0] ) ) ; i++){ + field = &http_www_auth_fields[i]; + if ( strcasecmp ( key, field->name ) == 0 ) + http_www_auth_field ( http, field, value ); + } + } + + /* Allow HTTP request to be retried if the request had not + * already tried authentication. + */ + if ( ! http->request.auth.auth ) + http->response.flags |= HTTP_RESPONSE_RETRY; + + return 0; +} + +/** HTTP "WWW-Authenticate" header */ +struct http_response_header +http_response_www_authenticate __http_response_header = { + .name = "WWW-Authenticate", + .parse = http_parse_www_authenticate, +}; + +/** + * Construct HTTP "Authorization" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_authorization ( struct http_transaction *http, + char *buf, size_t len ) { + struct http_authentication *auth = http->request.auth.auth; + size_t used; + int auth_len; + int rc; + + /* Do nothing unless we have an authentication scheme */ + if ( ! auth ) + return 0; + + /* Construct header */ + used = snprintf ( buf, len, "%s ", auth->name ); + auth_len = auth->format ( http, ( buf + used ), + ( ( used < len ) ? ( len - used ) : 0 ) ); + if ( auth_len < 0 ) { + rc = auth_len; + return rc; + } + used += auth_len; + + return used; +} + +/** HTTP "Authorization" header */ +struct http_request_header http_request_authorization __http_request_header = { + .name = "Authorization", + .format = http_format_authorization, +}; diff --git a/qemu/roms/ipxe/src/net/tcp/httpbasic.c b/qemu/roms/ipxe/src/net/tcp/httpbasic.c new file mode 100644 index 000000000..7ed7de9e7 --- /dev/null +++ b/qemu/roms/ipxe/src/net/tcp/httpbasic.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * Hyper Text Transfer Protocol (HTTP) Basic authentication + * + */ + +#include +#include +#include +#include +#include + +/* Disambiguate the various error causes */ +#define EACCES_USERNAME __einfo_error ( EINFO_EACCES_USERNAME ) +#define EINFO_EACCES_USERNAME \ + __einfo_uniqify ( EINFO_EACCES, 0x01, \ + "No username available for Basic authentication" ) + +/** + * Perform HTTP Basic authentication + * + * @v http HTTP transaction + * @ret rc Return status code + */ +static int http_basic_authenticate ( struct http_transaction *http ) { + struct http_request_auth *req = &http->request.auth; + + /* Record username and password */ + if ( ! http->uri->user ) { + DBGC ( http, "HTTP %p has no username for Basic " + "authentication\n", http ); + return -EACCES_USERNAME; + } + req->username = http->uri->user; + req->password = ( http->uri->password ? http->uri->password : "" ); + + return 0; +} + +/** + * Construct HTTP "Authorization" header for Basic authentication + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_basic_auth ( struct http_transaction *http, + char *buf, size_t len ) { + struct http_request_auth *req = &http->request.auth; + size_t user_pw_len = ( strlen ( req->username ) + 1 /* ":" */ + + strlen ( req->password ) ); + char user_pw[ user_pw_len + 1 /* NUL */ ]; + + /* Sanity checks */ + assert ( req->username != NULL ); + assert ( req->password != NULL ); + + /* Construct "user:password" string */ + snprintf ( user_pw, sizeof ( user_pw ), "%s:%s", + req->username, req->password ); + + /* Construct response */ + return base64_encode ( user_pw, user_pw_len, buf, len ); +} + +/** HTTP Basic authentication scheme */ +struct http_authentication http_basic_auth __http_authentication = { + .name = "Basic", + .authenticate = http_basic_authenticate, + .format = http_format_basic_auth, +}; + +/* Drag in HTTP authentication support */ +REQUIRING_SYMBOL ( http_basic_auth ); +REQUIRE_OBJECT ( httpauth ); diff --git a/qemu/roms/ipxe/src/net/tcp/httpblock.c b/qemu/roms/ipxe/src/net/tcp/httpblock.c new file mode 100644 index 000000000..e124ad2d6 --- /dev/null +++ b/qemu/roms/ipxe/src/net/tcp/httpblock.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * Hyper Text Transfer Protocol (HTTP) block device + * + */ + +#include +#include +#include +#include +#include +#include + +/** Block size used for HTTP block device requests */ +#define HTTP_BLKSIZE 512 + +/** + * Read from block device + * + * @v http HTTP transaction + * @v data Data interface + * @v lba Starting logical block address + * @v count Number of logical blocks + * @v buffer Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +int http_block_read ( struct http_transaction *http, struct interface *data, + uint64_t lba, unsigned int count, userptr_t buffer, + size_t len ) { + struct http_request_range range; + int rc; + + /* Sanity check */ + assert ( len == ( count * HTTP_BLKSIZE ) ); + + /* Construct request range descriptor */ + range.start = ( lba * HTTP_BLKSIZE ); + range.len = len; + + /* Start a range request to retrieve the block(s) */ + if ( ( rc = http_open ( data, &http_get, http->uri, &range, + NULL ) ) != 0 ) + goto err_open; + + /* Insert block device translator */ + if ( ( rc = block_translate ( data, buffer, len ) ) != 0 ) { + DBGC ( http, "HTTP %p could not insert block translator: %s\n", + http, strerror ( rc ) ); + goto err_translate; + } + + return 0; + + err_translate: + intf_restart ( data, rc ); + err_open: + return rc; +} + +/** + * Read block device capacity + * + * @v control Control interface + * @v data Data interface + * @ret rc Return status code + */ +int http_block_read_capacity ( struct http_transaction *http, + struct interface *data ) { + int rc; + + /* Start a HEAD request to retrieve the capacity */ + if ( ( rc = http_open ( data, &http_head, http->uri, NULL, + NULL ) ) != 0 ) + goto err_open; + + /* Insert block device translator */ + if ( ( rc = block_translate ( data, UNULL, HTTP_BLKSIZE ) ) != 0 ) { + DBGC ( http, "HTTP %p could not insert block translator: %s\n", + http, strerror ( rc ) ); + goto err_translate; + } + + return 0; + + err_translate: + intf_restart ( data, rc ); + err_open: + return rc; +} + +/** + * Describe device in ACPI table + * + * @v http HTTP transaction + * @v acpi ACPI table + * @v len Length of ACPI table + * @ret rc Return status code + */ +int http_acpi_describe ( struct http_transaction *http, + struct acpi_description_header *acpi, size_t len ) { + + DBGC ( http, "HTTP %p cannot yet describe device in an ACPI table\n", + http ); + ( void ) acpi; + ( void ) len; + return 0; +} diff --git a/qemu/roms/ipxe/src/net/tcp/httpconn.c b/qemu/roms/ipxe/src/net/tcp/httpconn.c new file mode 100644 index 000000000..7e4877b7b --- /dev/null +++ b/qemu/roms/ipxe/src/net/tcp/httpconn.c @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * Hyper Text Transfer Protocol (HTTP) connection management + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** HTTP pooled connection expiry time */ +#define HTTP_CONN_EXPIRY ( 10 * TICKS_PER_SEC ) + +/** HTTP connection pool */ +static LIST_HEAD ( http_connection_pool ); + +/** + * Identify HTTP scheme + * + * @v uri URI + * @ret scheme HTTP scheme, or NULL + */ +static struct http_scheme * http_scheme ( struct uri *uri ) { + struct http_scheme *scheme; + + /* Sanity check */ + if ( ! uri->scheme ) + return NULL; + + /* Identify scheme */ + for_each_table_entry ( scheme, HTTP_SCHEMES ) { + if ( strcmp ( uri->scheme, scheme->name ) == 0 ) + return scheme; + } + + return NULL; +} + +/** + * Free HTTP connection + * + * @v refcnt Reference count + */ +static void http_conn_free ( struct refcnt *refcnt ) { + struct http_connection *conn = + container_of ( refcnt, struct http_connection, refcnt ); + + /* Free connection */ + uri_put ( conn->uri ); + free ( conn ); +} + +/** + * Close HTTP connection + * + * @v conn HTTP connection + * @v rc Reason for close + */ +static void http_conn_close ( struct http_connection *conn, int rc ) { + + /* Remove from connection pool, if applicable */ + pool_del ( &conn->pool ); + + /* Shut down interfaces */ + intf_shutdown ( &conn->socket, rc ); + intf_shutdown ( &conn->xfer, rc ); + if ( rc == 0 ) { + DBGC2 ( conn, "HTTPCONN %p closed %s://%s\n", + conn, conn->scheme->name, conn->uri->host ); + } else { + DBGC ( conn, "HTTPCONN %p closed %s://%s: %s\n", + conn, conn->scheme->name, conn->uri->host, + strerror ( rc ) ); + } +} + +/** + * Disconnect idle HTTP connection + * + * @v pool Pooled connection + */ +static void http_conn_expired ( struct pooled_connection *pool ) { + struct http_connection *conn = + container_of ( pool, struct http_connection, pool ); + + /* Close connection */ + http_conn_close ( conn, 0 /* Not an error to close idle connection */ ); +} + +/** + * Receive data from transport layer interface + * + * @v http HTTP connection + * @v iobuf I/O buffer + * @v meta Transfer metadata + * @ret rc Return status code + */ +static int http_conn_socket_deliver ( struct http_connection *conn, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + + /* Mark connection as alive */ + pool_alive ( &conn->pool ); + + /* Pass on to data transfer interface */ + return xfer_deliver ( &conn->xfer, iobuf, meta ); +} + +/** + * Close HTTP connection transport layer interface + * + * @v http HTTP connection + * @v rc Reason for close + */ +static void http_conn_socket_close ( struct http_connection *conn, int rc ) { + + /* If we are reopenable (i.e. we are a recycled connection + * from the connection pool, and we have received no data from + * the underlying socket since we were pooled), then suggest + * that the client should reopen the connection. + */ + if ( pool_is_reopenable ( &conn->pool ) ) + pool_reopen ( &conn->xfer ); + + /* Close the connection */ + http_conn_close ( conn, rc ); +} + +/** + * Recycle this connection after closing + * + * @v http HTTP connection + */ +static void http_conn_xfer_recycle ( struct http_connection *conn ) { + + /* Mark connection as recyclable */ + pool_recyclable ( &conn->pool ); + DBGC2 ( conn, "HTTPCONN %p keepalive enabled\n", conn ); +} + +/** + * Close HTTP connection data transfer interface + * + * @v conn HTTP connection + * @v rc Reason for close + */ +static void http_conn_xfer_close ( struct http_connection *conn, int rc ) { + + /* Add to the connection pool if keepalive is enabled and no + * error occurred. + */ + if ( ( rc == 0 ) && pool_is_recyclable ( &conn->pool ) ) { + intf_restart ( &conn->xfer, rc ); + pool_add ( &conn->pool, &http_connection_pool, + HTTP_CONN_EXPIRY ); + DBGC2 ( conn, "HTTPCONN %p pooled %s://%s\n", + conn, conn->scheme->name, conn->uri->host ); + return; + } + + /* Otherwise, close the connection */ + http_conn_close ( conn, rc ); +} + +/** HTTP connection socket interface operations */ +static struct interface_operation http_conn_socket_operations[] = { + INTF_OP ( xfer_deliver, struct http_connection *, + http_conn_socket_deliver ), + INTF_OP ( intf_close, struct http_connection *, + http_conn_socket_close ), +}; + +/** HTTP connection socket interface descriptor */ +static struct interface_descriptor http_conn_socket_desc = + INTF_DESC_PASSTHRU ( struct http_connection, socket, + http_conn_socket_operations, xfer ); + +/** HTTP connection data transfer interface operations */ +static struct interface_operation http_conn_xfer_operations[] = { + INTF_OP ( pool_recycle, struct http_connection *, + http_conn_xfer_recycle ), + INTF_OP ( intf_close, struct http_connection *, + http_conn_xfer_close ), +}; + +/** HTTP connection data transfer interface descriptor */ +static struct interface_descriptor http_conn_xfer_desc = + INTF_DESC_PASSTHRU ( struct http_connection, xfer, + http_conn_xfer_operations, socket ); + +/** + * Connect to an HTTP server + * + * @v xfer Data transfer interface + * @v uri Connection URI + * @ret rc Return status code + * + * HTTP connections are pooled. The caller should be prepared to + * receive a pool_reopen() message. + */ +int http_connect ( struct interface *xfer, struct uri *uri ) { + struct http_connection *conn; + struct http_scheme *scheme; + struct sockaddr_tcpip server; + struct interface *socket; + int rc; + + /* Identify scheme */ + scheme = http_scheme ( uri ); + if ( ! scheme ) + return -ENOTSUP; + + /* Sanity check */ + if ( ! uri->host ) + return -EINVAL; + + /* Look for a reusable connection in the pool */ + list_for_each_entry ( conn, &http_connection_pool, pool.list ) { + + /* Sanity checks */ + assert ( conn->uri != NULL ); + assert ( conn->uri->host != NULL ); + + /* Reuse connection, if possible */ + if ( ( scheme == conn->scheme ) && + ( strcmp ( uri->host, conn->uri->host ) == 0 ) ) { + + /* Remove from connection pool, stop timer, + * attach to parent interface, and return. + */ + pool_del ( &conn->pool ); + intf_plug_plug ( &conn->xfer, xfer ); + DBGC2 ( conn, "HTTPCONN %p reused %s://%s\n", + conn, conn->scheme->name, conn->uri->host ); + return 0; + } + } + + /* Allocate and initialise structure */ + conn = zalloc ( sizeof ( *conn ) ); + ref_init ( &conn->refcnt, http_conn_free ); + conn->uri = uri_get ( uri ); + conn->scheme = scheme; + intf_init ( &conn->socket, &http_conn_socket_desc, &conn->refcnt ); + intf_init ( &conn->xfer, &http_conn_xfer_desc, &conn->refcnt ); + pool_init ( &conn->pool, http_conn_expired, &conn->refcnt ); + + /* Open socket */ + memset ( &server, 0, sizeof ( server ) ); + server.st_port = htons ( uri_port ( uri, scheme->port ) ); + socket = &conn->socket; + if ( scheme->filter && + ( ( rc = scheme->filter ( socket, uri->host, &socket ) ) != 0 ) ) + goto err_filter; + if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM, + ( struct sockaddr * ) &server, + uri->host, NULL ) ) != 0 ) + goto err_open; + + /* Attach to parent interface, mortalise self, and return */ + intf_plug_plug ( &conn->xfer, xfer ); + ref_put ( &conn->refcnt ); + + DBGC2 ( conn, "HTTPCONN %p created %s://%s:%d\n", conn, + conn->scheme->name, conn->uri->host, ntohs ( server.st_port ) ); + return 0; + + err_open: + err_filter: + DBGC2 ( conn, "HTTPCONN %p could not create %s://%s: %s\n", + conn, conn->scheme->name, conn->uri->host, strerror ( rc ) ); + http_conn_close ( conn, rc ); + ref_put ( &conn->refcnt ); + return rc; +} diff --git a/qemu/roms/ipxe/src/net/tcp/httpcore.c b/qemu/roms/ipxe/src/net/tcp/httpcore.c index 1d1953e61..af3ca9780 100644 --- a/qemu/roms/ipxe/src/net/tcp/httpcore.c +++ b/qemu/roms/ipxe/src/net/tcp/httpcore.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Michael Brown . + * Copyright (C) 2015 Michael Brown . * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file @@ -40,35 +44,26 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include -#include -#include #include #include #include #include -#include -#include -#include +#include #include #include #include #include #include +#include #include /* Disambiguate the various error causes */ #define EACCES_401 __einfo_error ( EINFO_EACCES_401 ) #define EINFO_EACCES_401 \ __einfo_uniqify ( EINFO_EACCES, 0x01, "HTTP 401 Unauthorized" ) -#define EIO_OTHER __einfo_error ( EINFO_EIO_OTHER ) -#define EINFO_EIO_OTHER \ - __einfo_uniqify ( EINFO_EIO, 0x01, "Unrecognised HTTP response code" ) -#define EIO_CONTENT_LENGTH __einfo_error ( EINFO_EIO_CONTENT_LENGTH ) -#define EINFO_EIO_CONTENT_LENGTH \ - __einfo_uniqify ( EINFO_EIO, 0x02, "Content length mismatch" ) -#define EINVAL_RESPONSE __einfo_error ( EINFO_EINVAL_RESPONSE ) -#define EINFO_EINVAL_RESPONSE \ - __einfo_uniqify ( EINFO_EINVAL, 0x01, "Invalid content length" ) +#define EINVAL_STATUS __einfo_error ( EINFO_EINVAL_STATUS ) +#define EINFO_EINVAL_STATUS \ + __einfo_uniqify ( EINFO_EINVAL, 0x01, "Invalid status line" ) #define EINVAL_HEADER __einfo_error ( EINFO_EINVAL_HEADER ) #define EINFO_EINVAL_HEADER \ __einfo_uniqify ( EINFO_EINVAL, 0x02, "Invalid header" ) @@ -78,9 +73,27 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define EINVAL_CHUNK_LENGTH __einfo_error ( EINFO_EINVAL_CHUNK_LENGTH ) #define EINFO_EINVAL_CHUNK_LENGTH \ __einfo_uniqify ( EINFO_EINVAL, 0x04, "Invalid chunk length" ) +#define EIO_OTHER __einfo_error ( EINFO_EIO_OTHER ) +#define EINFO_EIO_OTHER \ + __einfo_uniqify ( EINFO_EIO, 0x01, "Unrecognised HTTP response code" ) +#define EIO_CONTENT_LENGTH __einfo_error ( EINFO_EIO_CONTENT_LENGTH ) +#define EINFO_EIO_CONTENT_LENGTH \ + __einfo_uniqify ( EINFO_EIO, 0x02, "Content length mismatch" ) +#define EIO_4XX __einfo_error ( EINFO_EIO_4XX ) +#define EINFO_EIO_4XX \ + __einfo_uniqify ( EINFO_EIO, 0x04, "HTTP 4xx Client Error" ) +#define EIO_5XX __einfo_error ( EINFO_EIO_5XX ) +#define EINFO_EIO_5XX \ + __einfo_uniqify ( EINFO_EIO, 0x05, "HTTP 5xx Server Error" ) #define ENOENT_404 __einfo_error ( EINFO_ENOENT_404 ) #define EINFO_ENOENT_404 \ __einfo_uniqify ( EINFO_ENOENT, 0x01, "HTTP 404 Not Found" ) +#define ENOTSUP_CONNECTION __einfo_error ( EINFO_ENOTSUP_CONNECTION ) +#define EINFO_ENOTSUP_CONNECTION \ + __einfo_uniqify ( EINFO_ENOTSUP, 0x01, "Unsupported connection header" ) +#define ENOTSUP_TRANSFER __einfo_error ( EINFO_ENOTSUP_TRANSFER ) +#define EINFO_ENOTSUP_TRANSFER \ + __einfo_uniqify ( EINFO_ENOTSUP, 0x02, "Unsupported transfer encoding" ) #define EPERM_403 __einfo_error ( EINFO_EPERM_403 ) #define EINFO_EPERM_403 \ __einfo_uniqify ( EINFO_EPERM, 0x01, "HTTP 403 Forbidden" ) @@ -88,9 +101,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define EINFO_EPROTO_UNSOLICITED \ __einfo_uniqify ( EINFO_EPROTO, 0x01, "Unsolicited data" ) -/** Block size used for HTTP block device request */ -#define HTTP_BLKSIZE 512 - /** Retry delay used when we cannot understand the Retry-After header */ #define HTTP_RETRY_SECONDS 5 @@ -100,1475 +110,1821 @@ static struct profiler http_rx_profiler __profiler = { .name = "http.rx" }; /** Data transfer profiler */ static struct profiler http_xfer_profiler __profiler = { .name = "http.xfer" }; -/** HTTP flags */ -enum http_flags { - /** Request is waiting to be transmitted */ - HTTP_TX_PENDING = 0x0001, - /** Fetch header only */ - HTTP_HEAD_ONLY = 0x0002, - /** Client would like to keep connection alive */ - HTTP_CLIENT_KEEPALIVE = 0x0004, - /** Server will keep connection alive */ - HTTP_SERVER_KEEPALIVE = 0x0008, - /** Discard the current request and try again */ - HTTP_TRY_AGAIN = 0x0010, - /** Provide Basic authentication details */ - HTTP_BASIC_AUTH = 0x0020, - /** Provide Digest authentication details */ - HTTP_DIGEST_AUTH = 0x0040, - /** Socket must be reopened */ - HTTP_REOPEN_SOCKET = 0x0080, -}; - -/** HTTP receive state */ -enum http_rx_state { - HTTP_RX_RESPONSE = 0, - HTTP_RX_HEADER, - HTTP_RX_CHUNK_LEN, - /* In HTTP_RX_DATA, it is acceptable for the server to close - * the connection (unless we are in the middle of a chunked - * transfer). - */ - HTTP_RX_DATA, - /* In the following states, it is acceptable for the server to - * close the connection. - */ - HTTP_RX_TRAILER, - HTTP_RX_IDLE, - HTTP_RX_DEAD, -}; +static struct http_state http_request; +static struct http_state http_headers; +static struct http_state http_trailers; +static struct http_transfer_encoding http_transfer_identity; -/** - * An HTTP request +/****************************************************************************** * + * Methods + * + ****************************************************************************** */ -struct http_request { - /** Reference count */ - struct refcnt refcnt; - /** Data transfer interface */ - struct interface xfer; - /** Partial transfer interface */ - struct interface partial; - - /** URI being fetched */ - struct uri *uri; - /** Default port */ - unsigned int default_port; - /** Filter (if any) */ - int ( * filter ) ( struct interface *xfer, - const char *name, - struct interface **next ); - /** Transport layer interface */ - struct interface socket; - - /** Flags */ - unsigned int flags; - /** Starting offset of partial transfer (if applicable) */ - size_t partial_start; - /** Length of partial transfer (if applicable) */ - size_t partial_len; - - /** TX process */ - struct process process; - - /** RX state */ - enum http_rx_state rx_state; - /** Response code */ - unsigned int code; - /** Received length */ - size_t rx_len; - /** Length remaining (or 0 if unknown) */ - size_t remaining; - /** HTTP is using Transfer-Encoding: chunked */ - int chunked; - /** Current chunk length remaining (if applicable) */ - size_t chunk_remaining; - /** Line buffer for received header lines */ - struct line_buffer linebuf; - /** Receive data buffer (if applicable) */ - userptr_t rx_buffer; - - /** Authentication realm (if any) */ - char *auth_realm; - /** Authentication nonce (if any) */ - char *auth_nonce; - /** Authentication opaque string (if any) */ - char *auth_opaque; - - /** Request retry timer */ - struct retry_timer timer; - /** Retry delay (in timer ticks) */ - unsigned long retry_delay; + +/** HTTP HEAD method */ +struct http_method http_head = { + .name = "HEAD", }; -/** - * Free HTTP request - * - * @v refcnt Reference counter - */ -static void http_free ( struct refcnt *refcnt ) { - struct http_request *http = - container_of ( refcnt, struct http_request, refcnt ); +/** HTTP GET method */ +struct http_method http_get = { + .name = "GET", +}; - uri_put ( http->uri ); - empty_line_buffer ( &http->linebuf ); - free ( http->auth_realm ); - free ( http->auth_nonce ); - free ( http->auth_opaque ); - free ( http ); +/** HTTP POST method */ +struct http_method http_post = { + .name = "POST", }; -/** - * Close HTTP request +/****************************************************************************** * - * @v http HTTP request - * @v rc Return status code + * Utility functions + * + ****************************************************************************** */ -static void http_close ( struct http_request *http, int rc ) { - - /* Prevent further processing of any current packet */ - http->rx_state = HTTP_RX_DEAD; - - /* Prevent reconnection */ - http->flags &= ~HTTP_CLIENT_KEEPALIVE; - - /* Remove process */ - process_del ( &http->process ); - - /* Close all data transfer interfaces */ - intf_shutdown ( &http->socket, rc ); - intf_shutdown ( &http->partial, rc ); - intf_shutdown ( &http->xfer, rc ); -} /** - * Open HTTP socket + * Handle received HTTP line-buffered data * - * @v http HTTP request + * @v http HTTP transaction + * @v iobuf I/O buffer + * @v linebuf Line buffer * @ret rc Return status code */ -static int http_socket_open ( struct http_request *http ) { - struct uri *uri = http->uri; - struct sockaddr_tcpip server; - struct interface *socket; +static int http_rx_linebuf ( struct http_transaction *http, + struct io_buffer *iobuf, + struct line_buffer *linebuf ) { + int consumed; int rc; - /* Open socket */ - memset ( &server, 0, sizeof ( server ) ); - server.st_port = htons ( uri_port ( uri, http->default_port ) ); - socket = &http->socket; - if ( http->filter ) { - if ( ( rc = http->filter ( socket, uri->host, &socket ) ) != 0 ) - return rc; - } - if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM, - ( struct sockaddr * ) &server, - uri->host, NULL ) ) != 0 ) + /* Buffer received line */ + consumed = line_buffer ( linebuf, iobuf->data, iob_len ( iobuf ) ); + if ( consumed < 0 ) { + rc = consumed; + DBGC ( http, "HTTP %p could not buffer line: %s\n", + http, strerror ( rc ) ); return rc; + } + + /* Consume line */ + iob_pull ( iobuf, consumed ); return 0; } /** - * Retry HTTP request + * Get HTTP response token * - * @v timer Retry timer - * @v fail Failure indicator + * @v line Line position + * @v value Token value to fill in (if any) + * @ret token Token, or NULL */ -static void http_retry ( struct retry_timer *timer, int fail __unused ) { - struct http_request *http = - container_of ( timer, struct http_request, timer ); - int rc; +char * http_token ( char **line, char **value ) { + char *token; + char quote = '\0'; + char c; - /* Reopen socket if required */ - if ( http->flags & HTTP_REOPEN_SOCKET ) { - http->flags &= ~HTTP_REOPEN_SOCKET; - DBGC ( http, "HTTP %p reopening connection\n", http ); - if ( ( rc = http_socket_open ( http ) ) != 0 ) { - http_close ( http, rc ); - return; - } - } + /* Avoid returning uninitialised data */ + if ( value ) + *value = NULL; - /* Retry the request if applicable */ - if ( http->flags & HTTP_TRY_AGAIN ) { - http->flags &= ~HTTP_TRY_AGAIN; - DBGC ( http, "HTTP %p retrying request\n", http ); - http->flags |= HTTP_TX_PENDING; - http->rx_state = HTTP_RX_RESPONSE; - process_add ( &http->process ); - } -} + /* Skip any initial whitespace */ + while ( isspace ( **line ) ) + (*line)++; -/** - * Mark HTTP request as completed successfully - * - * @v http HTTP request - */ -static void http_done ( struct http_request *http ) { + /* Check for end of line and record token position */ + if ( ! **line ) + return NULL; + token = *line; - /* If we are not at an appropriate stage of the protocol - * (including being in the middle of a chunked transfer), - * force an error. - */ - if ( ( http->rx_state < HTTP_RX_DATA ) || ( http->chunked != 0 ) ) { - DBGC ( http, "HTTP %p connection closed unexpectedly in state " - "%d\n", http, http->rx_state ); - http_close ( http, -ECONNRESET ); - return; - } + /* Scan for end of token */ + while ( ( c = **line ) ) { - /* If we had a Content-Length, and the received content length - * isn't correct, force an error - */ - if ( http->remaining != 0 ) { - DBGC ( http, "HTTP %p incorrect length %zd, should be %zd\n", - http, http->rx_len, ( http->rx_len + http->remaining ) ); - http_close ( http, -EIO_CONTENT_LENGTH ); - return; - } + /* Terminate if we hit an unquoted whitespace */ + if ( isspace ( c ) && ! quote ) + break; - /* Enter idle state */ - http->rx_state = HTTP_RX_IDLE; - http->rx_len = 0; - assert ( http->remaining == 0 ); - assert ( http->chunked == 0 ); - assert ( http->chunk_remaining == 0 ); + /* Terminate if we hit a closing quote */ + if ( c == quote ) + break; - /* Close partial transfer interface */ - if ( ! ( http->flags & HTTP_TRY_AGAIN ) ) - intf_restart ( &http->partial, 0 ); + /* Check for value separator */ + if ( value && ( ! *value ) && ( c == '=' ) ) { - /* Close everything unless we want to keep the connection alive */ - if ( ! ( http->flags & ( HTTP_CLIENT_KEEPALIVE | HTTP_TRY_AGAIN ) ) ) { - http_close ( http, 0 ); - return; - } + /* Terminate key portion of token */ + *((*line)++) = '\0'; - /* If the server is not intending to keep the connection - * alive, then close the socket and mark it as requiring - * reopening. - */ - if ( ! ( http->flags & HTTP_SERVER_KEEPALIVE ) ) { - intf_restart ( &http->socket, 0 ); - http->flags &= ~HTTP_SERVER_KEEPALIVE; - http->flags |= HTTP_REOPEN_SOCKET; + /* Check for quote character */ + c = **line; + if ( ( c == '"' ) || ( c == '\'' ) ) { + quote = c; + (*line)++; + } + + /* Record value portion of token */ + *value = *line; + + } else { + + /* Move to next character */ + (*line)++; + } } - /* Start request retry timer */ - start_timer_fixed ( &http->timer, http->retry_delay ); - http->retry_delay = 0; + /* Terminate token, if applicable */ + if ( c ) + *((*line)++) = '\0'; + + return token; } +/****************************************************************************** + * + * Transactions + * + ****************************************************************************** + */ + /** - * Convert HTTP response code to return status code + * Free HTTP transaction * - * @v response HTTP response code - * @ret rc Return status code + * @v refcnt Reference count */ -static int http_response_to_rc ( unsigned int response ) { - switch ( response ) { - case 200: - case 206: - case 301: - case 302: - case 303: - return 0; - case 404: - return -ENOENT_404; - case 403: - return -EPERM_403; - case 401: - return -EACCES_401; - default: - return -EIO_OTHER; - } +static void http_free ( struct refcnt *refcnt ) { + struct http_transaction *http = + container_of ( refcnt, struct http_transaction, refcnt ); + + empty_line_buffer ( &http->response.headers ); + empty_line_buffer ( &http->linebuf ); + uri_put ( http->uri ); + free ( http ); } /** - * Handle HTTP response + * Close HTTP transaction * - * @v http HTTP request - * @v response HTTP response - * @ret rc Return status code + * @v http HTTP transaction + * @v rc Reason for close */ -static int http_rx_response ( struct http_request *http, char *response ) { - char *spc; +static void http_close ( struct http_transaction *http, int rc ) { - DBGC ( http, "HTTP %p response \"%s\"\n", http, response ); + /* Stop process */ + process_del ( &http->process ); - /* Check response starts with "HTTP/" */ - if ( strncmp ( response, "HTTP/", 5 ) != 0 ) - return -EINVAL_RESPONSE; + /* Stop timer */ + stop_timer ( &http->timer ); - /* Locate and store response code */ - spc = strchr ( response, ' ' ); - if ( ! spc ) - return -EINVAL_RESPONSE; - http->code = strtoul ( spc, NULL, 10 ); + /* Close all interfaces, allowing for the fact that the + * content-decoded and transfer-decoded interfaces may be + * connected to the same object. + */ + intf_shutdown ( &http->conn, rc ); + intf_nullify ( &http->transfer ); + intf_shutdown ( &http->content, rc ); + intf_shutdown ( &http->transfer, rc ); + intf_shutdown ( &http->xfer, rc ); +} - /* Move to receive headers */ - http->rx_state = ( ( http->flags & HTTP_HEAD_ONLY ) ? - HTTP_RX_TRAILER : HTTP_RX_HEADER ); - return 0; +/** + * Close HTTP transaction with error (even if none specified) + * + * @v http HTTP transaction + * @v rc Reason for close + */ +static void http_close_error ( struct http_transaction *http, int rc ) { + + /* Treat any close as an error */ + http_close ( http, ( rc ? rc : -EPIPE ) ); } /** - * Handle HTTP Location header + * Reopen stale HTTP connection * - * @v http HTTP request - * @v value HTTP header value - * @ret rc Return status code + * @v http HTTP transaction */ -static int http_rx_location ( struct http_request *http, char *value ) { +static void http_reopen ( struct http_transaction *http ) { int rc; - /* Redirect to new location */ - DBGC ( http, "HTTP %p redirecting to %s\n", http, value ); - if ( ( rc = xfer_redirect ( &http->xfer, LOCATION_URI_STRING, - value ) ) != 0 ) { - DBGC ( http, "HTTP %p could not redirect: %s\n", + /* Close existing connection */ + intf_restart ( &http->conn, -ECANCELED ); + + /* Reopen connection */ + if ( ( rc = http_connect ( &http->conn, http->uri ) ) != 0 ) { + DBGC ( http, "HTTP %p could not reconnect: %s\n", http, strerror ( rc ) ); - return rc; + goto err_connect; } - return 0; + /* Reset state */ + http->state = &http_request; + + /* Reschedule transmission process */ + process_add ( &http->process ); + + return; + + err_connect: + http_close ( http, rc ); } /** - * Handle HTTP Content-Length header + * Handle retry timer expiry * - * @v http HTTP request - * @v value HTTP header value - * @ret rc Return status code + * @v timer Retry timer + * @v over Failure indicator */ -static int http_rx_content_length ( struct http_request *http, char *value ) { - struct block_device_capacity capacity; - size_t content_len; - char *endp; +static void http_expired ( struct retry_timer *timer, int over __unused ) { + struct http_transaction *http = + container_of ( timer, struct http_transaction, timer ); - /* Parse content length */ - content_len = strtoul ( value, &endp, 10 ); - if ( ! ( ( *endp == '\0' ) || isspace ( *endp ) ) ) { - DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n", - http, value ); - return -EINVAL_CONTENT_LENGTH; - } + /* Reopen connection */ + http_reopen ( http ); +} - /* If we already have an expected content length, and this - * isn't it, then complain - */ - if ( http->remaining && ( http->remaining != content_len ) ) { - DBGC ( http, "HTTP %p incorrect Content-Length %zd (expected " - "%zd)\n", http, content_len, http->remaining ); - return -EIO_CONTENT_LENGTH; - } - if ( ! ( http->flags & HTTP_HEAD_ONLY ) ) - http->remaining = content_len; +/** + * HTTP transmit process + * + * @v http HTTP transaction + */ +static void http_step ( struct http_transaction *http ) { + int rc; - /* Do nothing more if we are retrying the request */ - if ( http->flags & HTTP_TRY_AGAIN ) - return 0; + /* Do nothing if we have nothing to transmit */ + if ( ! http->state->tx ) + return; - /* Use seek() to notify recipient of filesize */ - xfer_seek ( &http->xfer, http->remaining ); - xfer_seek ( &http->xfer, 0 ); + /* Do nothing until connection is ready */ + if ( ! xfer_window ( &http->conn ) ) + return; - /* Report block device capacity if applicable */ - if ( http->flags & HTTP_HEAD_ONLY ) { - capacity.blocks = ( content_len / HTTP_BLKSIZE ); - capacity.blksize = HTTP_BLKSIZE; - capacity.max_count = -1U; - block_capacity ( &http->partial, &capacity ); - } - return 0; + /* Do nothing until data transfer interface is ready */ + if ( ! xfer_window ( &http->xfer ) ) + return; + + /* Transmit data */ + if ( ( rc = http->state->tx ( http ) ) != 0 ) + goto err; + + return; + + err: + http_close ( http, rc ); } /** - * Handle HTTP Transfer-Encoding header + * Handle received HTTP data * - * @v http HTTP request - * @v value HTTP header value + * @v http HTTP transaction + * @v iobuf I/O buffer + * @v meta Transfer metadata * @ret rc Return status code + * + * This function takes ownership of the I/O buffer. */ -static int http_rx_transfer_encoding ( struct http_request *http, char *value ){ +static int http_conn_deliver ( struct http_transaction *http, + struct io_buffer *iobuf, + struct xfer_metadata *meta __unused ) { + int rc; + + /* Handle received data */ + profile_start ( &http_rx_profiler ); + while ( iobuf && iob_len ( iobuf ) ) { + + /* Sanity check */ + if ( ( ! http->state ) || ( ! http->state->rx ) ) { + DBGC ( http, "HTTP %p unexpected data\n", http ); + rc = -EPROTO_UNSOLICITED; + goto err; + } - if ( strcasecmp ( value, "chunked" ) == 0 ) { - /* Mark connection as using chunked transfer encoding */ - http->chunked = 1; + /* Receive (some) data */ + if ( ( rc = http->state->rx ( http, &iobuf ) ) != 0 ) + goto err; } + /* Free I/O buffer, if applicable */ + free_iob ( iobuf ); + + profile_stop ( &http_rx_profiler ); return 0; + + err: + free_iob ( iobuf ); + http_close ( http, rc ); + return rc; } /** - * Handle HTTP Connection header + * Handle server connection close * - * @v http HTTP request - * @v value HTTP header value - * @ret rc Return status code + * @v http HTTP transaction + * @v rc Reason for close */ -static int http_rx_connection ( struct http_request *http, char *value ) { +static void http_conn_close ( struct http_transaction *http, int rc ) { - if ( strcasecmp ( value, "keep-alive" ) == 0 ) { - /* Mark connection as being kept alive by the server */ - http->flags |= HTTP_SERVER_KEEPALIVE; - } + /* Sanity checks */ + assert ( http->state != NULL ); + assert ( http->state->close != NULL ); - return 0; + /* Restart server connection interface */ + intf_restart ( &http->conn, rc ); + + /* Hand off to state-specific method */ + http->state->close ( http, rc ); } /** - * Handle WWW-Authenticate Basic header + * Handle received content-decoded data * - * @v http HTTP request - * @v params Parameters - * @ret rc Return status code + * @v http HTTP transaction + * @v iobuf I/O buffer + * @v meta Data transfer metadata */ -static int http_rx_basic_auth ( struct http_request *http, char *params ) { - - DBGC ( http, "HTTP %p Basic authentication required (%s)\n", - http, params ); +static int http_content_deliver ( struct http_transaction *http, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + int rc; - /* If we received a 401 Unauthorized response, then retry - * using Basic authentication + /* Ignore content if this is anything other than a successful + * transfer. */ - if ( ( http->code == 401 ) && - ( ! ( http->flags & HTTP_BASIC_AUTH ) ) && - ( http->uri->user != NULL ) ) { - http->flags |= ( HTTP_TRY_AGAIN | HTTP_BASIC_AUTH ); + if ( http->response.rc != 0 ) { + free_iob ( iobuf ); + return 0; } + /* Deliver to data transfer interface */ + profile_start ( &http_xfer_profiler ); + if ( ( rc = xfer_deliver ( &http->xfer, iob_disown ( iobuf ), + meta ) ) != 0 ) + return rc; + profile_stop ( &http_xfer_profiler ); + return 0; } /** - * Parse Digest authentication parameter + * Get underlying data transfer buffer * - * @v params Parameters - * @v name Parameter name (including trailing "=\"") - * @ret value Parameter value, or NULL + * @v http HTTP transaction + * @ret xferbuf Data transfer buffer, or NULL on error */ -static char * http_digest_param ( char *params, const char *name ) { - char *key; - char *value; - char *terminator; - - /* Locate parameter */ - key = strstr ( params, name ); - if ( ! key ) - return NULL; +static struct xfer_buffer * +http_content_buffer ( struct http_transaction *http ) { - /* Extract value */ - value = ( key + strlen ( name ) ); - terminator = strchr ( value, '"' ); - if ( ! terminator ) + /* Deny access to the data transfer buffer if this is anything + * other than a successful transfer. + */ + if ( http->response.rc != 0 ) return NULL; - return strndup ( value, ( terminator - value ) ); + + /* Hand off to data transfer interface */ + return xfer_buffer ( &http->xfer ); } /** - * Handle WWW-Authenticate Digest header + * Read from block device (when HTTP block device support is not present) * - * @v http HTTP request - * @v params Parameters + * @v http HTTP transaction + * @v data Data interface + * @v lba Starting logical block address + * @v count Number of logical blocks + * @v buffer Data buffer + * @v len Length of data buffer * @ret rc Return status code */ -static int http_rx_digest_auth ( struct http_request *http, char *params ) { - - DBGC ( http, "HTTP %p Digest authentication required (%s)\n", - http, params ); +__weak int http_block_read ( struct http_transaction *http __unused, + struct interface *data __unused, + uint64_t lba __unused, unsigned int count __unused, + userptr_t buffer __unused, size_t len __unused ) { - /* If we received a 401 Unauthorized response, then retry - * using Digest authentication - */ - if ( ( http->code == 401 ) && - ( ! ( http->flags & HTTP_DIGEST_AUTH ) ) && - ( http->uri->user != NULL ) ) { - - /* Extract realm */ - free ( http->auth_realm ); - http->auth_realm = http_digest_param ( params, "realm=\"" ); - if ( ! http->auth_realm ) { - DBGC ( http, "HTTP %p Digest prompt missing realm\n", - http ); - return -EINVAL_HEADER; - } + return -ENOTSUP; +} - /* Extract nonce */ - free ( http->auth_nonce ); - http->auth_nonce = http_digest_param ( params, "nonce=\"" ); - if ( ! http->auth_nonce ) { - DBGC ( http, "HTTP %p Digest prompt missing nonce\n", - http ); - return -EINVAL_HEADER; - } +/** + * Read block device capacity (when HTTP block device support is not present) + * + * @v control Control interface + * @v data Data interface + * @ret rc Return status code + */ +__weak int http_block_read_capacity ( struct http_transaction *http __unused, + struct interface *data __unused ) { - /* Extract opaque */ - free ( http->auth_opaque ); - http->auth_opaque = http_digest_param ( params, "opaque=\"" ); - if ( ! http->auth_opaque ) { - /* Not an error; "opaque" is optional */ - } + return -ENOTSUP; +} - http->flags |= ( HTTP_TRY_AGAIN | HTTP_DIGEST_AUTH ); - } +/** + * Describe device in ACPI table (when HTTP block device support is not present) + * + * @v http HTTP transaction + * @v acpi ACPI table + * @v len Length of ACPI table + * @ret rc Return status code + */ +__weak int http_acpi_describe ( struct http_transaction *http __unused, + struct acpi_description_header *acpi __unused, + size_t len __unused ) { - return 0; + return -ENOTSUP; } -/** An HTTP WWW-Authenticate header handler */ -struct http_auth_header_handler { - /** Scheme (e.g. "Basic") */ - const char *scheme; - /** Handle received parameters - * - * @v http HTTP request - * @v params Parameters - * @ret rc Return status code - */ - int ( * rx ) ( struct http_request *http, char *params ); +/** HTTP data transfer interface operations */ +static struct interface_operation http_xfer_operations[] = { + INTF_OP ( block_read, struct http_transaction *, http_block_read ), + INTF_OP ( block_read_capacity, struct http_transaction *, + http_block_read_capacity ), + INTF_OP ( acpi_describe, struct http_transaction *, + http_acpi_describe ), + INTF_OP ( xfer_window_changed, struct http_transaction *, http_step ), + INTF_OP ( intf_close, struct http_transaction *, http_close ), }; -/** List of HTTP WWW-Authenticate header handlers */ -static struct http_auth_header_handler http_auth_header_handlers[] = { - { - .scheme = "Basic", - .rx = http_rx_basic_auth, - }, - { - .scheme = "Digest", - .rx = http_rx_digest_auth, - }, - { NULL, NULL }, +/** HTTP data transfer interface descriptor */ +static struct interface_descriptor http_xfer_desc = + INTF_DESC_PASSTHRU ( struct http_transaction, xfer, + http_xfer_operations, content ); + +/** HTTP content-decoded interface operations */ +static struct interface_operation http_content_operations[] = { + INTF_OP ( xfer_deliver, struct http_transaction *, + http_content_deliver ), + INTF_OP ( xfer_buffer, struct http_transaction *, http_content_buffer ), + INTF_OP ( intf_close, struct http_transaction *, http_close ), }; +/** HTTP content-decoded interface descriptor */ +static struct interface_descriptor http_content_desc = + INTF_DESC_PASSTHRU ( struct http_transaction, content, + http_content_operations, xfer ); + +/** HTTP transfer-decoded interface operations */ +static struct interface_operation http_transfer_operations[] = { + INTF_OP ( intf_close, struct http_transaction *, http_close ), +}; + +/** HTTP transfer-decoded interface descriptor */ +static struct interface_descriptor http_transfer_desc = + INTF_DESC_PASSTHRU ( struct http_transaction, transfer, + http_transfer_operations, conn ); + +/** HTTP server connection interface operations */ +static struct interface_operation http_conn_operations[] = { + INTF_OP ( xfer_deliver, struct http_transaction *, http_conn_deliver ), + INTF_OP ( xfer_window_changed, struct http_transaction *, http_step ), + INTF_OP ( pool_reopen, struct http_transaction *, http_reopen ), + INTF_OP ( intf_close, struct http_transaction *, http_conn_close ), +}; + +/** HTTP server connection interface descriptor */ +static struct interface_descriptor http_conn_desc = + INTF_DESC_PASSTHRU ( struct http_transaction, conn, + http_conn_operations, transfer ); + +/** HTTP process descriptor */ +static struct process_descriptor http_process_desc = + PROC_DESC_ONCE ( struct http_transaction, process, http_step ); + /** - * Handle HTTP WWW-Authenticate header + * Open HTTP transaction * - * @v http HTTP request - * @v value HTTP header value + * @v xfer Data transfer interface + * @v method Request method + * @v uri Request URI + * @v range Content range (if any) + * @v content Request content (if any) * @ret rc Return status code */ -static int http_rx_www_authenticate ( struct http_request *http, char *value ) { - struct http_auth_header_handler *handler; - char *separator; - char *scheme; - char *params; +int http_open ( struct interface *xfer, struct http_method *method, + struct uri *uri, struct http_request_range *range, + struct http_request_content *content ) { + struct http_transaction *http; + struct uri request_uri; + struct uri request_host; + size_t request_uri_len; + size_t request_host_len; + size_t content_len; + char *request_uri_string; + char *request_host_string; + void *content_data; int rc; - /* Extract scheme */ - separator = strchr ( value, ' ' ); - if ( ! separator ) { - DBGC ( http, "HTTP %p malformed WWW-Authenticate header\n", - http ); - return -EINVAL_HEADER; + /* Calculate request URI length */ + memset ( &request_uri, 0, sizeof ( request_uri ) ); + request_uri.path = ( uri->path ? uri->path : "/" ); + request_uri.query = uri->query; + request_uri_len = + ( format_uri ( &request_uri, NULL, 0 ) + 1 /* NUL */); + + /* Calculate host name length */ + memset ( &request_host, 0, sizeof ( request_host ) ); + request_host.host = uri->host; + request_host.port = uri->port; + request_host_len = + ( format_uri ( &request_host, NULL, 0 ) + 1 /* NUL */ ); + + /* Calculate request content length */ + content_len = ( content ? content->len : 0 ); + + /* Allocate and initialise structure */ + http = zalloc ( sizeof ( *http ) + request_uri_len + request_host_len + + content_len ); + if ( ! http ) { + rc = -ENOMEM; + goto err_alloc; } - *separator = '\0'; - scheme = value; - params = ( separator + 1 ); - - /* Hand off to header handler, if one exists */ - for ( handler = http_auth_header_handlers; handler->scheme; handler++ ){ - if ( strcasecmp ( scheme, handler->scheme ) == 0 ) { - if ( ( rc = handler->rx ( http, params ) ) != 0 ) - return rc; - break; - } + request_uri_string = ( ( ( void * ) http ) + sizeof ( *http ) ); + request_host_string = ( request_uri_string + request_uri_len ); + content_data = ( request_host_string + request_host_len ); + format_uri ( &request_uri, request_uri_string, request_uri_len ); + format_uri ( &request_host, request_host_string, request_host_len ); + ref_init ( &http->refcnt, http_free ); + intf_init ( &http->xfer, &http_xfer_desc, &http->refcnt ); + intf_init ( &http->content, &http_content_desc, &http->refcnt ); + intf_init ( &http->transfer, &http_transfer_desc, &http->refcnt ); + intf_init ( &http->conn, &http_conn_desc, &http->refcnt ); + intf_plug_plug ( &http->transfer, &http->content ); + process_init ( &http->process, &http_process_desc, &http->refcnt ); + timer_init ( &http->timer, http_expired, &http->refcnt ); + http->uri = uri_get ( uri ); + http->request.method = method; + http->request.uri = request_uri_string; + http->request.host = request_host_string; + if ( range ) { + memcpy ( &http->request.range, range, + sizeof ( http->request.range ) ); + } + if ( content ) { + http->request.content.type = content->type; + http->request.content.data = content_data; + http->request.content.len = content_len; + memcpy ( content_data, content->data, content_len ); } + http->state = &http_request; + DBGC2 ( http, "HTTP %p %s://%s%s\n", http, http->uri->scheme, + http->request.host, http->request.uri ); + + /* Open connection */ + if ( ( rc = http_connect ( &http->conn, uri ) ) != 0 ) { + DBGC ( http, "HTTP %p could not connect: %s\n", + http, strerror ( rc ) ); + goto err_connect; + } + + /* Attach to parent interface, mortalise self, and return */ + intf_plug_plug ( &http->xfer, xfer ); + ref_put ( &http->refcnt ); return 0; + + err_connect: + http_close ( http, rc ); + ref_put ( &http->refcnt ); + err_alloc: + return rc; } /** - * Handle HTTP Retry-After header + * Handle successful transfer completion * - * @v http HTTP request - * @v value HTTP header value + * @v http HTTP transaction * @ret rc Return status code */ -static int http_rx_retry_after ( struct http_request *http, char *value ) { - unsigned long seconds; - char *endp; +static int http_transfer_complete ( struct http_transaction *http ) { + struct http_authentication *auth; + const char *location; + int rc; + + /* Keep connection alive if applicable */ + if ( http->response.flags & HTTP_RESPONSE_KEEPALIVE ) + pool_recycle ( &http->conn ); - DBGC ( http, "HTTP %p retry requested (%s)\n", http, value ); + /* Restart server connection interface */ + intf_restart ( &http->conn, 0 ); - /* If we received a 503 Service Unavailable response, then - * retry after the specified number of seconds. If the value - * is not a simple number of seconds (e.g. a full HTTP date), - * then retry after a fixed delay, since we don't have code - * able to parse full HTTP dates. + /* No more data is expected */ + http->state = NULL; + + /* If transaction is successful, then close the + * transfer-decoded interface. The content encoding may + * choose whether or not to immediately terminate the + * transaction. */ - if ( http->code == 503 ) { - seconds = strtoul ( value, &endp, 10 ); - if ( *endp != '\0' ) { - seconds = HTTP_RETRY_SECONDS; - DBGC ( http, "HTTP %p cannot understand \"%s\"; " - "using %ld seconds\n", http, value, seconds ); + if ( http->response.rc == 0 ) { + intf_shutdown ( &http->transfer, 0 ); + return 0; + } + + /* Perform redirection, if applicable */ + if ( ( location = http->response.location ) ) { + DBGC2 ( http, "HTTP %p redirecting to \"%s\"\n", + http, location ); + if ( ( rc = xfer_redirect ( &http->xfer, LOCATION_URI_STRING, + location ) ) != 0 ) { + DBGC ( http, "HTTP %p could not redirect: %s\n", + http, strerror ( rc ) ); + return rc; + } + http_close ( http, 0 ); + return 0; + } + + /* Fail unless a retry is permitted */ + if ( ! ( http->response.flags & HTTP_RESPONSE_RETRY ) ) + return http->response.rc; + + /* Perform authentication, if applicable */ + if ( ( auth = http->response.auth.auth ) ) { + http->request.auth.auth = auth; + DBGC2 ( http, "HTTP %p performing %s authentication\n", + http, auth->name ); + if ( ( rc = auth->authenticate ( http ) ) != 0 ) { + DBGC ( http, "HTTP %p could not authenticate: %s\n", + http, strerror ( rc ) ); + return rc; } - http->flags |= HTTP_TRY_AGAIN; - http->retry_delay = ( seconds * TICKS_PER_SEC ); } + /* Restart content decoding interfaces (which may be attached + * to the same object). + */ + intf_nullify ( &http->content ); + intf_nullify ( &http->transfer ); + intf_restart ( &http->content, http->response.rc ); + intf_restart ( &http->transfer, http->response.rc ); + http->content.desc = &http_content_desc; + http->transfer.desc = &http_transfer_desc; + intf_plug_plug ( &http->transfer, &http->content ); + http->len = 0; + assert ( http->remaining == 0 ); + + /* Start timer to initiate retry */ + DBGC2 ( http, "HTTP %p retrying after %d seconds\n", + http, http->response.retry_after ); + start_timer_fixed ( &http->timer, + ( http->response.retry_after * TICKS_PER_SEC ) ); return 0; } -/** An HTTP header handler */ -struct http_header_handler { - /** Name (e.g. "Content-Length") */ - const char *header; - /** Handle received header - * - * @v http HTTP request - * @v value HTTP header value - * @ret rc Return status code - * - * If an error is returned, the download will be aborted. - */ - int ( * rx ) ( struct http_request *http, char *value ); -}; - -/** List of HTTP header handlers */ -static struct http_header_handler http_header_handlers[] = { - { - .header = "Location", - .rx = http_rx_location, - }, - { - .header = "Content-Length", - .rx = http_rx_content_length, - }, - { - .header = "Transfer-Encoding", - .rx = http_rx_transfer_encoding, - }, - { - .header = "Connection", - .rx = http_rx_connection, - }, - { - .header = "WWW-Authenticate", - .rx = http_rx_www_authenticate, - }, - { - .header = "Retry-After", - .rx = http_rx_retry_after, - }, - { NULL, NULL } -}; +/****************************************************************************** + * + * Requests + * + ****************************************************************************** + */ /** - * Handle HTTP header + * Construct HTTP request headers * - * @v http HTTP request - * @v header HTTP header - * @ret rc Return status code + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length, or negative error */ -static int http_rx_header ( struct http_request *http, char *header ) { - struct http_header_handler *handler; - char *separator; - char *value; +static int http_format_headers ( struct http_transaction *http, char *buf, + size_t len ) { + struct http_request_header *header; + size_t used; + size_t remaining; + char *line; + int value_len; int rc; - /* An empty header line marks the end of this phase */ - if ( ! header[0] ) { - empty_line_buffer ( &http->linebuf ); + /* Construct request line */ + used = ssnprintf ( buf, len, "%s %s HTTP/1.1", + http->request.method->name, http->request.uri ); + if ( used < len ) + DBGC2 ( http, "HTTP %p TX %s\n", http, buf ); + used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" ); - /* Handle response code */ - if ( ! ( http->flags & HTTP_TRY_AGAIN ) ) { - if ( ( rc = http_response_to_rc ( http->code ) ) != 0 ) - return rc; - } + /* Construct all headers */ + for_each_table_entry ( header, HTTP_REQUEST_HEADERS ) { - /* Move to next state */ - if ( http->rx_state == HTTP_RX_HEADER ) { - DBGC ( http, "HTTP %p start of data\n", http ); - http->rx_state = ( http->chunked ? - HTTP_RX_CHUNK_LEN : HTTP_RX_DATA ); - if ( ( http->partial_len != 0 ) && - ( ! ( http->flags & HTTP_TRY_AGAIN ) ) ) { - http->remaining = http->partial_len; - } - return 0; - } else { - DBGC ( http, "HTTP %p end of trailer\n", http ); - http_done ( http ); - return 0; + /* Determine header value length */ + value_len = header->format ( http, NULL, 0 ); + if ( value_len < 0 ) { + rc = value_len; + return rc; } + + /* Skip zero-length headers */ + if ( ! value_len ) + continue; + + /* Construct header */ + line = ( buf + used ); + used += ssnprintf ( ( buf + used ), ( len - used ), "%s: ", + header->name ); + remaining = ( ( used < len ) ? ( len - used ) : 0 ); + used += header->format ( http, ( buf + used ), remaining ); + if ( used < len ) + DBGC2 ( http, "HTTP %p TX %s\n", http, line ); + used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" ); } - DBGC ( http, "HTTP %p header \"%s\"\n", http, header ); + /* Construct terminating newline */ + used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" ); - /* Split header at the ": " */ - separator = strstr ( header, ": " ); - if ( ! separator ) { - DBGC ( http, "HTTP %p malformed header\n", http ); - return -EINVAL_HEADER; - } - *separator = '\0'; - value = ( separator + 2 ); - - /* Hand off to header handler, if one exists */ - for ( handler = http_header_handlers ; handler->header ; handler++ ) { - if ( strcasecmp ( header, handler->header ) == 0 ) { - if ( ( rc = handler->rx ( http, value ) ) != 0 ) - return rc; - break; - } - } - return 0; + return used; } /** - * Handle HTTP chunk length + * Construct HTTP "Host" header * - * @v http HTTP request - * @v length HTTP chunk length - * @ret rc Return status code + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error */ -static int http_rx_chunk_len ( struct http_request *http, char *length ) { - char *endp; +static int http_format_host ( struct http_transaction *http, char *buf, + size_t len ) { - /* Skip blank lines between chunks */ - if ( length[0] == '\0' ) - return 0; + /* Construct host URI */ + return snprintf ( buf, len, "%s", http->request.host ); +} - /* Parse chunk length */ - http->chunk_remaining = strtoul ( length, &endp, 16 ); - if ( *endp != '\0' ) { - DBGC ( http, "HTTP %p invalid chunk length \"%s\"\n", - http, length ); - return -EINVAL_CHUNK_LENGTH; - } +/** HTTP "Host" header "*/ +struct http_request_header http_request_host __http_request_header = { + .name = "Host", + .format = http_format_host, +}; + +/** + * Construct HTTP "User-Agent" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_user_agent ( struct http_transaction *http __unused, + char *buf, size_t len ) { + + /* Construct user agent */ + return snprintf ( buf, len, "iPXE/%s", product_version ); +} + +/** HTTP "User-Agent" header */ +struct http_request_header http_request_user_agent __http_request_header = { + .name = "User-Agent", + .format = http_format_user_agent, +}; + +/** + * Construct HTTP "Connection" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_connection ( struct http_transaction *http __unused, + char *buf, size_t len ) { - /* Terminate chunked encoding if applicable */ - if ( http->chunk_remaining == 0 ) { - DBGC ( http, "HTTP %p end of chunks\n", http ); - http->chunked = 0; - http->rx_state = HTTP_RX_TRAILER; + /* Always request keep-alive */ + return snprintf ( buf, len, "keep-alive" ); +} + +/** HTTP "Connection" header */ +struct http_request_header http_request_connection __http_request_header = { + .name = "Connection", + .format = http_format_connection, +}; + +/** + * Construct HTTP "Range" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_range ( struct http_transaction *http, + char *buf, size_t len ) { + + /* Construct range, if applicable */ + if ( http->request.range.len ) { + return snprintf ( buf, len, "bytes=%zd-%zd", + http->request.range.start, + ( http->request.range.start + + http->request.range.len - 1 ) ); + } else { return 0; } +} + +/** HTTP "Range" header */ +struct http_request_header http_request_range __http_request_header = { + .name = "Range", + .format = http_format_range, +}; + +/** + * Construct HTTP "Content-Type" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_content_type ( struct http_transaction *http, + char *buf, size_t len ) { - /* Use seek() to notify recipient of new filesize */ - DBGC ( http, "HTTP %p start of chunk of length %zd\n", - http, http->chunk_remaining ); - if ( ! ( http->flags & HTTP_TRY_AGAIN ) ) { - xfer_seek ( &http->xfer, - ( http->rx_len + http->chunk_remaining ) ); - xfer_seek ( &http->xfer, http->rx_len ); + /* Construct content type, if applicable */ + if ( http->request.content.type ) { + return snprintf ( buf, len, "%s", http->request.content.type ); + } else { + return 0; } +} - /* Start receiving data */ - http->rx_state = HTTP_RX_DATA; +/** HTTP "Content-Type" header */ +struct http_request_header http_request_content_type __http_request_header = { + .name = "Content-Type", + .format = http_format_content_type, +}; - return 0; +/** + * Construct HTTP "Content-Length" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_content_length ( struct http_transaction *http, + char *buf, size_t len ) { + + /* Construct content length, if applicable */ + if ( http->request.content.len ) { + return snprintf ( buf, len, "%zd", http->request.content.len ); + } else { + return 0; + } } -/** An HTTP line-based data handler */ -struct http_line_handler { - /** Handle line - * - * @v http HTTP request - * @v line Line to handle - * @ret rc Return status code - */ - int ( * rx ) ( struct http_request *http, char *line ); +/** HTTP "Content-Length" header */ +struct http_request_header http_request_content_length __http_request_header = { + .name = "Content-Length", + .format = http_format_content_length, }; -/** List of HTTP line-based data handlers */ -static struct http_line_handler http_line_handlers[] = { - [HTTP_RX_RESPONSE] = { .rx = http_rx_response }, - [HTTP_RX_HEADER] = { .rx = http_rx_header }, - [HTTP_RX_CHUNK_LEN] = { .rx = http_rx_chunk_len }, - [HTTP_RX_TRAILER] = { .rx = http_rx_header }, +/** + * Construct HTTP "Accept-Encoding" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_accept_encoding ( struct http_transaction *http, + char *buf, size_t len ) { + struct http_content_encoding *encoding; + const char *sep = ""; + size_t used = 0; + + /* Construct list of content encodings */ + for_each_table_entry ( encoding, HTTP_CONTENT_ENCODINGS ) { + if ( encoding->supported && ( ! encoding->supported ( http ) ) ) + continue; + used += ssnprintf ( ( buf + used ), ( len - used ), + "%s%s", sep, encoding->name ); + sep = ", "; + } + + return used; +} + +/** HTTP "Accept-Encoding" header */ +struct http_request_header http_request_accept_encoding __http_request_header ={ + .name = "Accept-Encoding", + .format = http_format_accept_encoding, }; /** - * Handle new data arriving via HTTP connection + * Transmit request * - * @v http HTTP request - * @v iobuf I/O buffer - * @v meta Data transfer metadata + * @v http HTTP transaction * @ret rc Return status code */ -static int http_socket_deliver ( struct http_request *http, - struct io_buffer *iobuf, - struct xfer_metadata *meta __unused ) { - struct http_line_handler *lh; - char *line; - size_t data_len; - ssize_t line_len; - int rc = 0; +static int http_tx_request ( struct http_transaction *http ) { + struct io_buffer *iobuf; + int len; + int check_len; + int rc; - profile_start ( &http_rx_profiler ); - while ( iobuf && iob_len ( iobuf ) ) { + /* Calculate request length */ + len = http_format_headers ( http, NULL, 0 ); + if ( len < 0 ) { + rc = len; + DBGC ( http, "HTTP %p could not construct request: %s\n", + http, strerror ( rc ) ); + goto err_len; + } - switch ( http->rx_state ) { - case HTTP_RX_IDLE: - /* Receiving any data in this state is an error */ - DBGC ( http, "HTTP %p received %zd bytes while %s\n", - http, iob_len ( iobuf ), - ( ( http->rx_state == HTTP_RX_IDLE ) ? - "idle" : "dead" ) ); - rc = -EPROTO_UNSOLICITED; - goto done; - case HTTP_RX_DEAD: - /* Do no further processing */ - goto done; - case HTTP_RX_DATA: - /* Pass received data to caller */ - data_len = iob_len ( iobuf ); - if ( http->chunk_remaining && - ( http->chunk_remaining < data_len ) ) { - data_len = http->chunk_remaining; - } - if ( http->remaining && - ( http->remaining < data_len ) ) { - data_len = http->remaining; - } - if ( http->flags & HTTP_TRY_AGAIN ) { - /* Discard all received data */ - iob_pull ( iobuf, data_len ); - } else if ( http->rx_buffer != UNULL ) { - /* Copy to partial transfer buffer */ - copy_to_user ( http->rx_buffer, http->rx_len, - iobuf->data, data_len ); - iob_pull ( iobuf, data_len ); - } else if ( data_len < iob_len ( iobuf ) ) { - /* Deliver partial buffer as raw data */ - profile_start ( &http_xfer_profiler ); - rc = xfer_deliver_raw ( &http->xfer, - iobuf->data, data_len ); - iob_pull ( iobuf, data_len ); - if ( rc != 0 ) - goto done; - profile_stop ( &http_xfer_profiler ); - } else { - /* Deliver whole I/O buffer */ - profile_start ( &http_xfer_profiler ); - if ( ( rc = xfer_deliver_iob ( &http->xfer, - iob_disown ( iobuf ) ) ) != 0 ) - goto done; - profile_stop ( &http_xfer_profiler ); - } - http->rx_len += data_len; - if ( http->chunk_remaining ) { - http->chunk_remaining -= data_len; - if ( http->chunk_remaining == 0 ) - http->rx_state = HTTP_RX_CHUNK_LEN; - } - if ( http->remaining ) { - http->remaining -= data_len; - if ( ( http->remaining == 0 ) && - ( http->rx_state == HTTP_RX_DATA ) ) { - http_done ( http ); - } - } - break; - case HTTP_RX_RESPONSE: - case HTTP_RX_HEADER: - case HTTP_RX_CHUNK_LEN: - case HTTP_RX_TRAILER: - /* In the other phases, buffer and process a - * line at a time - */ - line_len = line_buffer ( &http->linebuf, iobuf->data, - iob_len ( iobuf ) ); - if ( line_len < 0 ) { - rc = line_len; - DBGC ( http, "HTTP %p could not buffer line: " - "%s\n", http, strerror ( rc ) ); - goto done; - } - iob_pull ( iobuf, line_len ); - line = buffered_line ( &http->linebuf ); - if ( line ) { - lh = &http_line_handlers[http->rx_state]; - if ( ( rc = lh->rx ( http, line ) ) != 0 ) - goto done; - } - break; - default: - assert ( 0 ); - break; - } + /* Allocate I/O buffer */ + iobuf = alloc_iob ( len + 1 /* NUL */ + http->request.content.len ); + if ( ! iobuf ) { + rc = -ENOMEM; + goto err_alloc; } - done: - if ( rc ) - http_close ( http, rc ); + /* Construct request */ + check_len = http_format_headers ( http, iob_put ( iobuf, len ), + ( len + 1 /* NUL */ ) ); + assert ( check_len == len ); + memcpy ( iob_put ( iobuf, http->request.content.len ), + http->request.content.data, http->request.content.len ); + + /* Deliver request */ + if ( ( rc = xfer_deliver_iob ( &http->conn, + iob_disown ( iobuf ) ) ) != 0 ) { + DBGC ( http, "HTTP %p could not deliver request: %s\n", + http, strerror ( rc ) ); + goto err_deliver; + } + + /* Clear any previous response */ + empty_line_buffer ( &http->response.headers ); + memset ( &http->response, 0, sizeof ( http->response ) ); + + /* Move to response headers state */ + http->state = &http_headers; + + return 0; + + err_deliver: free_iob ( iobuf ); - profile_stop ( &http_rx_profiler ); + err_alloc: + err_len: return rc; } +/** HTTP request state */ +static struct http_state http_request = { + .tx = http_tx_request, + .close = http_close_error, +}; + +/****************************************************************************** + * + * Response headers + * + ****************************************************************************** + */ + /** - * Check HTTP socket flow control window + * Parse HTTP status line * - * @v http HTTP request - * @ret len Length of window + * @v http HTTP transaction + * @v line Status line + * @ret rc Return status code */ -static size_t http_socket_window ( struct http_request *http __unused ) { +static int http_parse_status ( struct http_transaction *http, char *line ) { + char *endp; + char *version; + char *vernum; + char *status; + int response_rc; + + DBGC2 ( http, "HTTP %p RX %s\n", http, line ); + + /* Parse HTTP version */ + version = http_token ( &line, NULL ); + if ( ( ! version ) || ( strncmp ( version, "HTTP/", 5 ) != 0 ) ) { + DBGC ( http, "HTTP %p malformed version \"%s\"\n", http, line ); + return -EINVAL_STATUS; + } - /* Window is always open. This is to prevent TCP from - * stalling if our parent window is not currently open. - */ - return ( ~( ( size_t ) 0 ) ); + /* Keepalive is enabled by default for anything newer than HTTP/1.0 */ + vernum = ( version + 5 /* "HTTP/" (presence already checked) */ ); + if ( vernum[0] == '0' ) { + /* HTTP/0.x : keepalive not enabled by default */ + } else if ( strncmp ( vernum, "1.0", 3 ) == 0 ) { + /* HTTP/1.0 : keepalive not enabled by default */ + } else { + /* HTTP/1.1 or newer: keepalive enabled by default */ + http->response.flags |= HTTP_RESPONSE_KEEPALIVE; + } + + /* Parse status code */ + status = line; + http->response.status = strtoul ( status, &endp, 10 ); + if ( *endp != ' ' ) { + DBGC ( http, "HTTP %p malformed status code \"%s\"\n", + http, status ); + return -EINVAL_STATUS; + } + + /* Convert HTTP status code to iPXE return status code */ + if ( status[0] == '2' ) { + /* 2xx Success */ + response_rc = 0; + } else if ( status[0] == '3' ) { + /* 3xx Redirection */ + response_rc = -EXDEV; + } else if ( http->response.status == 401 ) { + /* 401 Unauthorized */ + response_rc = -EACCES_401; + } else if ( http->response.status == 403 ) { + /* 403 Forbidden */ + response_rc = -EPERM_403; + } else if ( http->response.status == 404 ) { + /* 404 Not Found */ + response_rc = -ENOENT_404; + } else if ( status[0] == '4' ) { + /* 4xx Client Error (not already specified) */ + response_rc = -EIO_4XX; + } else if ( status[0] == '5' ) { + /* 5xx Server Error */ + response_rc = -EIO_5XX; + } else { + /* Unrecognised */ + response_rc = -EIO_OTHER; + } + http->response.rc = response_rc; + + return 0; } /** - * Close HTTP socket + * Parse HTTP header * - * @v http HTTP request - * @v rc Reason for close + * @v http HTTP transaction + * @v line Header line + * @ret rc Return status code */ -static void http_socket_close ( struct http_request *http, int rc ) { +static int http_parse_header ( struct http_transaction *http, char *line ) { + struct http_response_header *header; + char *name = line; + char *sep; - /* If we have an error, terminate */ - if ( rc != 0 ) { - http_close ( http, rc ); - return; + DBGC2 ( http, "HTTP %p RX %s\n", http, line ); + + /* Extract header name */ + sep = strstr ( line, ": " ); + if ( ! sep ) { + DBGC ( http, "HTTP %p malformed header \"%s\"\n", http, line ); + return -EINVAL_HEADER; + } + *sep = '\0'; + line = ( sep + 2 /* ": " */ ); + + /* Process header, if recognised */ + for_each_table_entry ( header, HTTP_RESPONSE_HEADERS ) { + if ( strcasecmp ( name, header->name ) == 0 ) + return header->parse ( http, line ); } - /* Mark HTTP request as complete */ - http_done ( http ); + /* Unrecognised headers should be ignored */ + return 0; } /** - * Generate HTTP Basic authorisation string + * Parse HTTP response headers * - * @v http HTTP request - * @ret auth Authorisation string, or NULL on error - * - * The authorisation string is dynamically allocated, and must be - * freed by the caller. + * @v http HTTP transaction + * @ret rc Return status code */ -static char * http_basic_auth ( struct http_request *http ) { - const char *user = http->uri->user; - const char *password = ( http->uri->password ? - http->uri->password : "" ); - size_t user_pw_len = - ( strlen ( user ) + 1 /* ":" */ + strlen ( password ) ); - char user_pw[ user_pw_len + 1 /* NUL */ ]; - size_t user_pw_base64_len = base64_encoded_len ( user_pw_len ); - char user_pw_base64[ user_pw_base64_len + 1 /* NUL */ ]; - char *auth; - int len; +static int http_parse_headers ( struct http_transaction *http ) { + char *line; + char *next; + int rc; + + /* Get status line */ + line = http->response.headers.data; + assert ( line != NULL ); + next = ( line + strlen ( line ) + 1 /* NUL */ ); - /* Sanity check */ - assert ( user != NULL ); + /* Parse status line */ + if ( ( rc = http_parse_status ( http, line ) ) != 0 ) + return rc; - /* Make "user:password" string from decoded fields */ - snprintf ( user_pw, sizeof ( user_pw ), "%s:%s", user, password ); + /* Process header lines */ + while ( 1 ) { - /* Base64-encode the "user:password" string */ - base64_encode ( ( void * ) user_pw, user_pw_len, user_pw_base64 ); + /* Move to next line */ + line = next; + next = ( line + strlen ( line ) + 1 /* NUL */ ); - /* Generate the authorisation string */ - len = asprintf ( &auth, "Authorization: Basic %s\r\n", - user_pw_base64 ); - if ( len < 0 ) - return NULL; + /* Stop on terminating blank line */ + if ( ! line[0] ) + return 0; - return auth; + /* Process header line */ + if ( ( rc = http_parse_header ( http, line ) ) != 0 ) + return rc; + } } /** - * Generate HTTP Digest authorisation string - * - * @v http HTTP request - * @v method HTTP method (e.g. "GET") - * @v uri HTTP request URI (e.g. "/index.html") - * @ret auth Authorisation string, or NULL on error + * Parse HTTP "Location" header * - * The authorisation string is dynamically allocated, and must be - * freed by the caller. + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code */ -static char * http_digest_auth ( struct http_request *http, - const char *method, const char *uri ) { - const char *user = http->uri->user; - const char *password = ( http->uri->password ? - http->uri->password : "" ); - const char *realm = http->auth_realm; - const char *nonce = http->auth_nonce; - const char *opaque = http->auth_opaque; - static const char colon = ':'; - uint8_t ctx[MD5_CTX_SIZE]; - uint8_t digest[MD5_DIGEST_SIZE]; - char ha1[ base16_encoded_len ( sizeof ( digest ) ) + 1 /* NUL */ ]; - char ha2[ base16_encoded_len ( sizeof ( digest ) ) + 1 /* NUL */ ]; - char response[ base16_encoded_len ( sizeof ( digest ) ) + 1 /* NUL */ ]; - char *auth; - int len; +static int http_parse_location ( struct http_transaction *http, char *line ) { - /* Sanity checks */ - assert ( user != NULL ); - assert ( realm != NULL ); - assert ( nonce != NULL ); - - /* Generate HA1 */ - digest_init ( &md5_algorithm, ctx ); - digest_update ( &md5_algorithm, ctx, user, strlen ( user ) ); - digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) ); - digest_update ( &md5_algorithm, ctx, realm, strlen ( realm ) ); - digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) ); - digest_update ( &md5_algorithm, ctx, password, strlen ( password ) ); - digest_final ( &md5_algorithm, ctx, digest ); - base16_encode ( digest, sizeof ( digest ), ha1 ); - - /* Generate HA2 */ - digest_init ( &md5_algorithm, ctx ); - digest_update ( &md5_algorithm, ctx, method, strlen ( method ) ); - digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) ); - digest_update ( &md5_algorithm, ctx, uri, strlen ( uri ) ); - digest_final ( &md5_algorithm, ctx, digest ); - base16_encode ( digest, sizeof ( digest ), ha2 ); - - /* Generate response */ - digest_init ( &md5_algorithm, ctx ); - digest_update ( &md5_algorithm, ctx, ha1, strlen ( ha1 ) ); - digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) ); - digest_update ( &md5_algorithm, ctx, nonce, strlen ( nonce ) ); - digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) ); - digest_update ( &md5_algorithm, ctx, ha2, strlen ( ha2 ) ); - digest_final ( &md5_algorithm, ctx, digest ); - base16_encode ( digest, sizeof ( digest ), response ); - - /* Generate the authorisation string */ - len = asprintf ( &auth, "Authorization: Digest username=\"%s\", " - "realm=\"%s\", nonce=\"%s\", uri=\"%s\", " - "%s%s%sresponse=\"%s\"\r\n", user, realm, nonce, uri, - ( opaque ? "opaque=\"" : "" ), - ( opaque ? opaque : "" ), - ( opaque ? "\", " : "" ), response ); - if ( len < 0 ) - return NULL; + /* Store location */ + http->response.location = line; + return 0; +} + +/** HTTP "Location" header */ +struct http_response_header http_response_location __http_response_header = { + .name = "Location", + .parse = http_parse_location, +}; + +/** + * Parse HTTP "Transfer-Encoding" header + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ +static int http_parse_transfer_encoding ( struct http_transaction *http, + char *line ) { + struct http_transfer_encoding *encoding; + + /* Check for known transfer encodings */ + for_each_table_entry ( encoding, HTTP_TRANSFER_ENCODINGS ) { + if ( strcasecmp ( line, encoding->name ) == 0 ) { + http->response.transfer.encoding = encoding; + return 0; + } + } - return auth; + DBGC ( http, "HTTP %p unrecognised Transfer-Encoding \"%s\"\n", + http, line ); + return -ENOTSUP_TRANSFER; } +/** HTTP "Transfer-Encoding" header */ +struct http_response_header +http_response_transfer_encoding __http_response_header = { + .name = "Transfer-Encoding", + .parse = http_parse_transfer_encoding, +}; + /** - * Generate HTTP POST parameter list + * Parse HTTP "Connection" header * - * @v http HTTP request - * @v buf Buffer to contain HTTP POST parameters - * @v len Length of buffer - * @ret len Length of parameter list (excluding terminating NUL) + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code */ -static size_t http_post_params ( struct http_request *http, - char *buf, size_t len ) { - struct parameter *param; - ssize_t remaining = len; - size_t frag_len; +static int http_parse_connection ( struct http_transaction *http, char *line ) { - /* Add each parameter in the form "key=value", joined with "&" */ - len = 0; - for_each_param ( param, http->uri->params ) { + /* Check for known connection intentions */ + if ( strcasecmp ( line, "keep-alive" ) == 0 ) { + http->response.flags |= HTTP_RESPONSE_KEEPALIVE; + return 0; + } + if ( strcasecmp ( line, "close" ) == 0 ) { + http->response.flags &= ~HTTP_RESPONSE_KEEPALIVE; + return 0; + } - /* Add the "&", if applicable */ - if ( len ) { - if ( remaining > 0 ) - *buf = '&'; - buf++; - len++; - remaining--; - } + DBGC ( http, "HTTP %p unrecognised Connection \"%s\"\n", http, line ); + return -ENOTSUP_CONNECTION; +} - /* URI-encode the key */ - frag_len = uri_encode ( param->key, 0, buf, remaining ); - buf += frag_len; - len += frag_len; - remaining -= frag_len; +/** HTTP "Connection" header */ +struct http_response_header http_response_connection __http_response_header = { + .name = "Connection", + .parse = http_parse_connection, +}; - /* Add the "=" */ - if ( remaining > 0 ) - *buf = '='; - buf++; - len++; - remaining--; +/** + * Parse HTTP "Content-Length" header + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ +static int http_parse_content_length ( struct http_transaction *http, + char *line ) { + char *endp; - /* URI-encode the value */ - frag_len = uri_encode ( param->value, 0, buf, remaining ); - buf += frag_len; - len += frag_len; - remaining -= frag_len; + /* Parse length */ + http->response.content.len = strtoul ( line, &endp, 10 ); + if ( *endp != '\0' ) { + DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n", + http, line ); + return -EINVAL_CONTENT_LENGTH; } - /* Ensure string is NUL-terminated even if no parameters are present */ - if ( remaining > 0 ) - *buf = '\0'; + /* Record that we have a content length (since it may be zero) */ + http->response.flags |= HTTP_RESPONSE_CONTENT_LEN; - return len; + return 0; } +/** HTTP "Content-Length" header */ +struct http_response_header +http_response_content_length __http_response_header = { + .name = "Content-Length", + .parse = http_parse_content_length, +}; + /** - * Generate HTTP POST body + * Parse HTTP "Content-Encoding" header * - * @v http HTTP request - * @ret post I/O buffer containing POST body, or NULL on error + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code */ -static struct io_buffer * http_post ( struct http_request *http ) { - struct io_buffer *post; - size_t len; - size_t check_len; +static int http_parse_content_encoding ( struct http_transaction *http, + char *line ) { + struct http_content_encoding *encoding; + + /* Check for known content encodings */ + for_each_table_entry ( encoding, HTTP_CONTENT_ENCODINGS ) { + if ( encoding->supported && ( ! encoding->supported ( http ) ) ) + continue; + if ( strcasecmp ( line, encoding->name ) == 0 ) { + http->response.content.encoding = encoding; + return 0; + } + } - /* Calculate length of parameter list */ - len = http_post_params ( http, NULL, 0 ); + /* Some servers (e.g. Apache) have a habit of specifying + * unwarranted content encodings. For example, if Apache + * detects (via /etc/httpd/conf/magic) that a file's contents + * are gzip-compressed, it will set "Content-Encoding: x-gzip" + * regardless of the client's Accept-Encoding header. The + * only viable way to handle such servers is to treat unknown + * content encodings as equivalent to "identity". + */ + DBGC ( http, "HTTP %p unrecognised Content-Encoding \"%s\"\n", + http, line ); + return 0; +} - /* Allocate parameter list */ - post = alloc_iob ( len + 1 /* NUL */ ); - if ( ! post ) - return NULL; +/** HTTP "Content-Encoding" header */ +struct http_response_header +http_response_content_encoding __http_response_header = { + .name = "Content-Encoding", + .parse = http_parse_content_encoding, +}; - /* Fill parameter list */ - check_len = http_post_params ( http, iob_put ( post, len ), - ( len + 1 /* NUL */ ) ); - assert ( len == check_len ); - DBGC ( http, "HTTP %p POST %s\n", http, ( ( char * ) post->data ) ); +/** + * Parse HTTP "Retry-After" header + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ +static int http_parse_retry_after ( struct http_transaction *http, + char *line ) { + char *endp; - return post; + /* Try to parse value as a simple number of seconds */ + http->response.retry_after = strtoul ( line, &endp, 10 ); + if ( *endp != '\0' ) { + /* For any value which is not a simple number of + * seconds (e.g. a full HTTP date), just retry after a + * fixed delay, since we don't have code able to parse + * full HTTP dates. + */ + http->response.retry_after = HTTP_RETRY_SECONDS; + DBGC ( http, "HTTP %p cannot understand Retry-After \"%s\"; " + "using %d seconds\n", http, line, HTTP_RETRY_SECONDS ); + } + + /* Allow HTTP request to be retried after specified delay */ + http->response.flags |= HTTP_RESPONSE_RETRY; + + return 0; } +/** HTTP "Retry-After" header */ +struct http_response_header http_response_retry_after __http_response_header = { + .name = "Retry-After", + .parse = http_parse_retry_after, +}; + /** - * HTTP process + * Handle received HTTP headers * - * @v http HTTP request + * @v http HTTP transaction + * @v iobuf I/O buffer (may be claimed) + * @ret rc Return status code */ -static void http_step ( struct http_request *http ) { - struct io_buffer *post; - struct uri host_uri; - struct uri path_uri; - char *host_uri_string; - char *path_uri_string; - char *method; - char *range; - char *auth; - char *content; - int len; +static int http_rx_headers ( struct http_transaction *http, + struct io_buffer **iobuf ) { + struct http_transfer_encoding *transfer; + struct http_content_encoding *content; + char *line; int rc; - /* Do nothing if we have already transmitted the request */ - if ( ! ( http->flags & HTTP_TX_PENDING ) ) - return; - - /* Do nothing until socket is ready */ - if ( ! xfer_window ( &http->socket ) ) - return; + /* Buffer header line */ + if ( ( rc = http_rx_linebuf ( http, *iobuf, + &http->response.headers ) ) != 0 ) + return rc; - /* Force a HEAD request if we have nowhere to send any received data */ - if ( ( xfer_window ( &http->xfer ) == 0 ) && - ( http->rx_buffer == UNULL ) ) { - http->flags |= ( HTTP_HEAD_ONLY | HTTP_CLIENT_KEEPALIVE ); - } + /* Wait until we see the empty line marking end of headers */ + line = buffered_line ( &http->response.headers ); + if ( ( line == NULL ) || ( line[0] != '\0' ) ) + return 0; - /* Determine method */ - method = ( ( http->flags & HTTP_HEAD_ONLY ) ? "HEAD" : - ( http->uri->params ? "POST" : "GET" ) ); + /* Process headers */ + if ( ( rc = http_parse_headers ( http ) ) != 0 ) + return rc; - /* Construct host URI */ - memset ( &host_uri, 0, sizeof ( host_uri ) ); - host_uri.host = http->uri->host; - host_uri.port = http->uri->port; - host_uri_string = format_uri_alloc ( &host_uri ); - if ( ! host_uri_string ) { - rc = -ENOMEM; - goto err_host_uri; + /* Initialise content encoding, if applicable */ + if ( ( content = http->response.content.encoding ) && + ( ( rc = content->init ( http ) ) != 0 ) ) { + DBGC ( http, "HTTP %p could not initialise %s content " + "encoding: %s\n", http, content->name, strerror ( rc ) ); + return rc; } - /* Construct path URI */ - memset ( &path_uri, 0, sizeof ( path_uri ) ); - path_uri.path = ( http->uri->path ? http->uri->path : "/" ); - path_uri.query = http->uri->query; - path_uri_string = format_uri_alloc ( &path_uri ); - if ( ! path_uri_string ) { - rc = -ENOMEM; - goto err_path_uri; + /* Presize receive buffer, if we have a content length */ + if ( http->response.content.len ) { + xfer_seek ( &http->transfer, http->response.content.len ); + xfer_seek ( &http->transfer, 0 ); } - /* Calculate range request parameters if applicable */ - if ( http->partial_len ) { - len = asprintf ( &range, "Range: bytes=%zd-%zd\r\n", - http->partial_start, - ( http->partial_start + http->partial_len - - 1 ) ); - if ( len < 0 ) { - rc = len; - goto err_range; - } - } else { - range = NULL; + /* Complete transfer if this is a HEAD request */ + if ( http->request.method == &http_head ) { + if ( ( rc = http_transfer_complete ( http ) ) != 0 ) + return rc; + return 0; } - /* Construct authorisation, if applicable */ - if ( http->flags & HTTP_BASIC_AUTH ) { - auth = http_basic_auth ( http ); - if ( ! auth ) { - rc = -ENOMEM; - goto err_auth; - } - } else if ( http->flags & HTTP_DIGEST_AUTH ) { - auth = http_digest_auth ( http, method, path_uri_string ); - if ( ! auth ) { - rc = -ENOMEM; - goto err_auth; - } - } else { - auth = NULL; - } + /* Default to identity transfer encoding, if none specified */ + if ( ! http->response.transfer.encoding ) + http->response.transfer.encoding = &http_transfer_identity; - /* Construct POST content, if applicable */ - if ( http->uri->params ) { - post = http_post ( http ); - if ( ! post ) { - rc = -ENOMEM; - goto err_post; - } - len = asprintf ( &content, "Content-Type: " - "application/x-www-form-urlencoded\r\n" - "Content-Length: %zd\r\n", iob_len ( post ) ); - if ( len < 0 ) { - rc = len; - goto err_content; - } - } else { - post = NULL; - content = NULL; - } + /* Move to transfer encoding-specific data state */ + transfer = http->response.transfer.encoding; + http->state = &transfer->state; - /* Mark request as transmitted */ - http->flags &= ~HTTP_TX_PENDING; - - /* Send request */ - if ( ( rc = xfer_printf ( &http->socket, - "%s %s HTTP/1.1\r\n" - "User-Agent: iPXE/%s\r\n" - "Host: %s\r\n" - "%s%s%s%s" - "\r\n", - method, path_uri_string, product_version, - host_uri_string, - ( ( http->flags & HTTP_CLIENT_KEEPALIVE ) ? - "Connection: keep-alive\r\n" : "" ), - ( range ? range : "" ), - ( auth ? auth : "" ), - ( content ? content : "" ) ) ) != 0 ) { - goto err_xfer; + /* Initialise transfer encoding */ + if ( ( rc = transfer->init ( http ) ) != 0 ) { + DBGC ( http, "HTTP %p could not initialise %s transfer " + "encoding: %s\n", http, transfer->name, strerror ( rc )); + return rc; } - /* Send POST content, if applicable */ - if ( post ) { - if ( ( rc = xfer_deliver_iob ( &http->socket, - iob_disown ( post ) ) ) != 0 ) - goto err_xfer_post; + return 0; +} + +/** HTTP response headers state */ +static struct http_state http_headers = { + .rx = http_rx_headers, + .close = http_close_error, +}; + +/****************************************************************************** + * + * Identity transfer encoding + * + ****************************************************************************** + */ + +/** + * Initialise transfer encoding + * + * @v http HTTP transaction + * @ret rc Return status code + */ +static int http_init_transfer_identity ( struct http_transaction *http ) { + int rc; + + /* Complete transfer immediately if we have a zero content length */ + if ( ( http->response.flags & HTTP_RESPONSE_CONTENT_LEN ) && + ( http->response.content.len == 0 ) && + ( ( rc = http_transfer_complete ( http ) ) != 0 ) ) + return rc; + + return 0; +} + +/** + * Handle received data + * + * @v http HTTP transaction + * @v iobuf I/O buffer (may be claimed) + * @ret rc Return status code + */ +static int http_rx_transfer_identity ( struct http_transaction *http, + struct io_buffer **iobuf ) { + size_t len = iob_len ( *iobuf ); + int rc; + + /* Update lengths */ + http->len += len; + + /* Fail if this transfer would overrun the expected content + * length (if any). + */ + if ( ( http->response.flags & HTTP_RESPONSE_CONTENT_LEN ) && + ( http->len > http->response.content.len ) ) { + DBGC ( http, "HTTP %p content length overrun\n", http ); + return -EIO_CONTENT_LENGTH; } - err_xfer_post: - err_xfer: - free ( content ); - err_content: - free ( post ); - err_post: - free ( auth ); - err_auth: - free ( range ); - err_range: - free ( path_uri_string ); - err_path_uri: - free ( host_uri_string ); - err_host_uri: + /* Hand off to content encoding */ + if ( ( rc = xfer_deliver_iob ( &http->transfer, + iob_disown ( *iobuf ) ) ) != 0 ) + return rc; + + /* Complete transfer if we have received the expected content + * length (if any). + */ + if ( ( http->response.flags & HTTP_RESPONSE_CONTENT_LEN ) && + ( http->len == http->response.content.len ) && + ( ( rc = http_transfer_complete ( http ) ) != 0 ) ) + return rc; + + return 0; +} + +/** + * Handle server connection close + * + * @v http HTTP transaction + * @v rc Reason for close + */ +static void http_close_transfer_identity ( struct http_transaction *http, + int rc ) { + + /* Fail if any error occurred */ if ( rc != 0 ) - http_close ( http, rc ); + goto err; + + /* Fail if we have a content length (since we would have + * already closed the connection if we had received the + * correct content length). + */ + if ( http->response.flags & HTTP_RESPONSE_CONTENT_LEN ) { + DBGC ( http, "HTTP %p content length underrun\n", http ); + rc = EIO_CONTENT_LENGTH; + goto err; + } + + /* Indicate that transfer is complete */ + if ( ( rc = http_transfer_complete ( http ) ) != 0 ) + goto err; + + return; + + err: + http_close ( http, rc ); } +/** Identity transfer encoding */ +static struct http_transfer_encoding http_transfer_identity = { + .name = "identity", + .init = http_init_transfer_identity, + .state = { + .rx = http_rx_transfer_identity, + .close = http_close_transfer_identity, + }, +}; + +/****************************************************************************** + * + * Chunked transfer encoding + * + ****************************************************************************** + */ + /** - * Check HTTP data transfer flow control window + * Initialise transfer encoding * - * @v http HTTP request - * @ret len Length of window + * @v http HTTP transaction + * @ret rc Return status code */ -static size_t http_xfer_window ( struct http_request *http ) { +static int http_init_transfer_chunked ( struct http_transaction *http ) { - /* New block commands may be issued only when we are idle */ - return ( ( http->rx_state == HTTP_RX_IDLE ) ? 1 : 0 ); + /* Sanity checks */ + assert ( http->remaining == 0 ); + assert ( http->linebuf.len == 0 ); + + return 0; } /** - * Initiate HTTP partial read + * Handle received chunk length * - * @v http HTTP request - * @v partial Partial transfer interface - * @v offset Starting offset - * @v buffer Data buffer - * @v len Length + * @v http HTTP transaction + * @v iobuf I/O buffer (may be claimed) * @ret rc Return status code */ -static int http_partial_read ( struct http_request *http, - struct interface *partial, - size_t offset, userptr_t buffer, size_t len ) { - - /* Sanity check */ - if ( http_xfer_window ( http ) == 0 ) - return -EBUSY; - - /* Initialise partial transfer parameters */ - http->rx_buffer = buffer; - http->partial_start = offset; - http->partial_len = len; - - /* Schedule request */ - http->rx_state = HTTP_RX_RESPONSE; - http->flags = ( HTTP_TX_PENDING | HTTP_CLIENT_KEEPALIVE ); - if ( ! len ) - http->flags |= HTTP_HEAD_ONLY; - process_add ( &http->process ); +static int http_rx_chunk_len ( struct http_transaction *http, + struct io_buffer **iobuf ) { + char *line; + char *endp; + size_t len; + int rc; + + /* Receive into temporary line buffer */ + if ( ( rc = http_rx_linebuf ( http, *iobuf, &http->linebuf ) ) != 0 ) + return rc; - /* Attach to parent interface and return */ - intf_plug_plug ( &http->partial, partial ); + /* Wait until we receive a non-empty line */ + line = buffered_line ( &http->linebuf ); + if ( ( line == NULL ) || ( line[0] == '\0' ) ) + return 0; + + /* Parse chunk length */ + http->remaining = strtoul ( line, &endp, 16 ); + if ( *endp != '\0' ) { + DBGC ( http, "HTTP %p invalid chunk length \"%s\"\n", + http, line ); + return -EINVAL_CHUNK_LENGTH; + } + + /* Empty line buffer */ + empty_line_buffer ( &http->linebuf ); + + /* Update expected length */ + len = ( http->len + http->remaining ); + xfer_seek ( &http->transfer, len ); + xfer_seek ( &http->transfer, http->len ); + + /* If chunk length is zero, then move to response trailers state */ + if ( ! http->remaining ) + http->state = &http_trailers; return 0; } /** - * Issue HTTP block device read + * Handle received chunk data * - * @v http HTTP request - * @v block Block data interface - * @v lba Starting logical block address - * @v count Number of blocks to transfer - * @v buffer Data buffer - * @v len Length of data buffer + * @v http HTTP transaction + * @v iobuf I/O buffer (may be claimed) * @ret rc Return status code */ -static int http_block_read ( struct http_request *http, - struct interface *block, - uint64_t lba, unsigned int count, - userptr_t buffer, size_t len __unused ) { +static int http_rx_chunk_data ( struct http_transaction *http, + struct io_buffer **iobuf ) { + struct io_buffer *payload; + uint8_t *crlf; + size_t len; + int rc; + + /* In the common case of a final chunk in a packet which also + * includes the terminating CRLF, strip the terminating CRLF + * (which we would ignore anyway) and hence avoid + * unnecessarily copying the data. + */ + if ( iob_len ( *iobuf ) == ( http->remaining + 2 /* CRLF */ ) ) { + crlf = ( (*iobuf)->data + http->remaining ); + if ( ( crlf[0] == '\r' ) && ( crlf[1] == '\n' ) ) + iob_unput ( (*iobuf), 2 /* CRLF */ ); + } + len = iob_len ( *iobuf ); + + /* Use whole/partial buffer as applicable */ + if ( len <= http->remaining ) { + + /* Whole buffer is to be consumed: decrease remaining + * length and use original I/O buffer as payload. + */ + payload = iob_disown ( *iobuf ); + http->len += len; + http->remaining -= len; + + } else { - return http_partial_read ( http, block, ( lba * HTTP_BLKSIZE ), - buffer, ( count * HTTP_BLKSIZE ) ); + /* Partial buffer is to be consumed: copy data to a + * temporary I/O buffer. + */ + payload = alloc_iob ( http->remaining ); + if ( ! payload ) { + rc = -ENOMEM; + goto err; + } + memcpy ( iob_put ( payload, http->remaining ), (*iobuf)->data, + http->remaining ); + iob_pull ( *iobuf, http->remaining ); + http->len += http->remaining; + http->remaining = 0; + } + + /* Hand off to content encoding */ + if ( ( rc = xfer_deliver_iob ( &http->transfer, + iob_disown ( payload ) ) ) != 0 ) + goto err; + + return 0; + + err: + assert ( payload == NULL ); + return rc; } /** - * Read HTTP block device capacity + * Handle received chunked data * - * @v http HTTP request - * @v block Block data interface + * @v http HTTP transaction + * @v iobuf I/O buffer (may be claimed) * @ret rc Return status code */ -static int http_block_read_capacity ( struct http_request *http, - struct interface *block ) { +static int http_rx_transfer_chunked ( struct http_transaction *http, + struct io_buffer **iobuf ) { - return http_partial_read ( http, block, 0, 0, 0 ); + /* Handle as chunk length or chunk data as appropriate */ + if ( http->remaining ) { + return http_rx_chunk_data ( http, iobuf ); + } else { + return http_rx_chunk_len ( http, iobuf ); + } } +/** Chunked transfer encoding */ +struct http_transfer_encoding http_transfer_chunked __http_transfer_encoding = { + .name = "chunked", + .init = http_init_transfer_chunked, + .state = { + .rx = http_rx_transfer_chunked, + .close = http_close_error, + }, +}; + +/****************************************************************************** + * + * Response trailers + * + ****************************************************************************** + */ + /** - * Describe HTTP device in an ACPI table + * Handle received HTTP trailer * - * @v http HTTP request - * @v acpi ACPI table - * @v len Length of ACPI table + * @v http HTTP transaction + * @v iobuf I/O buffer (may be claimed) * @ret rc Return status code */ -static int http_acpi_describe ( struct http_request *http, - struct acpi_description_header *acpi, - size_t len ) { - - DBGC ( http, "HTTP %p cannot yet describe device in an ACPI table\n", - http ); - ( void ) acpi; - ( void ) len; +static int http_rx_trailers ( struct http_transaction *http, + struct io_buffer **iobuf ) { + char *line; + int rc; + + /* Buffer trailer line */ + if ( ( rc = http_rx_linebuf ( http, *iobuf, &http->linebuf ) ) != 0 ) + return rc; + + /* Wait until we see the empty line marking end of trailers */ + line = buffered_line ( &http->linebuf ); + if ( ( line == NULL ) || ( line[0] != '\0' ) ) + return 0; + + /* Empty line buffer */ + empty_line_buffer ( &http->linebuf ); + + /* Transfer is complete */ + if ( ( rc = http_transfer_complete ( http ) ) != 0 ) + return rc; + return 0; } -/** HTTP socket interface operations */ -static struct interface_operation http_socket_operations[] = { - INTF_OP ( xfer_window, struct http_request *, http_socket_window ), - INTF_OP ( xfer_deliver, struct http_request *, http_socket_deliver ), - INTF_OP ( xfer_window_changed, struct http_request *, http_step ), - INTF_OP ( intf_close, struct http_request *, http_socket_close ), +/** HTTP response trailers state */ +static struct http_state http_trailers = { + .rx = http_rx_trailers, + .close = http_close_error, }; -/** HTTP socket interface descriptor */ -static struct interface_descriptor http_socket_desc = - INTF_DESC_PASSTHRU ( struct http_request, socket, - http_socket_operations, xfer ); +/****************************************************************************** + * + * Simple URI openers + * + ****************************************************************************** + */ -/** HTTP partial transfer interface operations */ -static struct interface_operation http_partial_operations[] = { - INTF_OP ( intf_close, struct http_request *, http_close ), -}; +/** + * Construct HTTP parameter list + * + * @v params Parameter list + * @v buf Buffer to contain HTTP POST parameters + * @v len Length of buffer + * @ret len Length of parameter list (excluding terminating NUL) + */ +static size_t http_params ( struct parameters *params, char *buf, size_t len ) { + struct parameter *param; + ssize_t remaining = len; + size_t frag_len; -/** HTTP partial transfer interface descriptor */ -static struct interface_descriptor http_partial_desc = - INTF_DESC ( struct http_request, partial, http_partial_operations ); + /* Add each parameter in the form "key=value", joined with "&" */ + len = 0; + for_each_param ( param, params ) { -/** HTTP data transfer interface operations */ -static struct interface_operation http_xfer_operations[] = { - INTF_OP ( xfer_window, struct http_request *, http_xfer_window ), - INTF_OP ( block_read, struct http_request *, http_block_read ), - INTF_OP ( block_read_capacity, struct http_request *, - http_block_read_capacity ), - INTF_OP ( intf_close, struct http_request *, http_close ), - INTF_OP ( acpi_describe, struct http_request *, http_acpi_describe ), -}; + /* Add the "&", if applicable */ + if ( len ) { + if ( remaining > 0 ) + *buf = '&'; + buf++; + len++; + remaining--; + } -/** HTTP data transfer interface descriptor */ -static struct interface_descriptor http_xfer_desc = - INTF_DESC_PASSTHRU ( struct http_request, xfer, - http_xfer_operations, socket ); + /* URI-encode the key */ + frag_len = uri_encode ( param->key, 0, buf, remaining ); + buf += frag_len; + len += frag_len; + remaining -= frag_len; -/** HTTP process descriptor */ -static struct process_descriptor http_process_desc = - PROC_DESC_ONCE ( struct http_request, process, http_step ); + /* Add the "=" */ + if ( remaining > 0 ) + *buf = '='; + buf++; + len++; + remaining--; + + /* URI-encode the value */ + frag_len = uri_encode ( param->value, 0, buf, remaining ); + buf += frag_len; + len += frag_len; + remaining -= frag_len; + } + + /* Ensure string is NUL-terminated even if no parameters are present */ + if ( remaining > 0 ) + *buf = '\0'; + + return len; +} /** - * Initiate an HTTP connection, with optional filter + * Open HTTP transaction for simple GET URI * * @v xfer Data transfer interface - * @v uri Uniform Resource Identifier - * @v default_port Default port number - * @v filter Filter to apply to socket, or NULL + * @v uri Request URI * @ret rc Return status code */ -int http_open_filter ( struct interface *xfer, struct uri *uri, - unsigned int default_port, - int ( * filter ) ( struct interface *xfer, - const char *name, - struct interface **next ) ) { - struct http_request *http; +static int http_open_get_uri ( struct interface *xfer, struct uri *uri ) { + + return http_open ( xfer, &http_get, uri, NULL, NULL ); +} + +/** + * Open HTTP transaction for simple POST URI + * + * @v xfer Data transfer interface + * @v uri Request URI + * @ret rc Return status code + */ +static int http_open_post_uri ( struct interface *xfer, struct uri *uri ) { + struct parameters *params = uri->params; + struct http_request_content content; + void *data; + size_t len; + size_t check_len; int rc; - /* Sanity checks */ - if ( ! uri->host ) - return -EINVAL; + /* Calculate length of parameter list */ + len = http_params ( params, NULL, 0 ); - /* Allocate and populate HTTP structure */ - http = zalloc ( sizeof ( *http ) ); - if ( ! http ) - return -ENOMEM; - ref_init ( &http->refcnt, http_free ); - intf_init ( &http->xfer, &http_xfer_desc, &http->refcnt ); - intf_init ( &http->partial, &http_partial_desc, &http->refcnt ); - http->uri = uri_get ( uri ); - http->default_port = default_port; - http->filter = filter; - intf_init ( &http->socket, &http_socket_desc, &http->refcnt ); - process_init ( &http->process, &http_process_desc, &http->refcnt ); - timer_init ( &http->timer, http_retry, &http->refcnt ); - http->flags = HTTP_TX_PENDING; + /* Allocate temporary parameter list */ + data = zalloc ( len + 1 /* NUL */ ); + if ( ! data ) { + rc = -ENOMEM; + goto err_alloc; + } - /* Open socket */ - if ( ( rc = http_socket_open ( http ) ) != 0 ) - goto err; + /* Construct temporary parameter list */ + check_len = http_params ( params, data, ( len + 1 /* NUL */ ) ); + assert ( check_len == len ); - /* Attach to parent interface, mortalise self, and return */ - intf_plug_plug ( &http->xfer, xfer ); - ref_put ( &http->refcnt ); - return 0; + /* Construct request content */ + content.type = "application/x-www-form-urlencoded"; + content.data = data; + content.len = len; - err: - DBGC ( http, "HTTP %p could not create request: %s\n", - http, strerror ( rc ) ); - http_close ( http, rc ); - ref_put ( &http->refcnt ); + /* Open HTTP transaction */ + if ( ( rc = http_open ( xfer, &http_post, uri, NULL, &content ) ) != 0 ) + goto err_open; + + err_open: + free ( data ); + err_alloc: return rc; } + +/** + * Open HTTP transaction for simple URI + * + * @v xfer Data transfer interface + * @v uri Request URI + * @ret rc Return status code + */ +int http_open_uri ( struct interface *xfer, struct uri *uri ) { + + /* Open GET/POST URI as applicable */ + if ( uri->params ) { + return http_open_post_uri ( xfer, uri ); + } else { + return http_open_get_uri ( xfer, uri ); + } +} + +/* Drag in HTTP extensions */ +REQUIRING_SYMBOL ( http_open ); +REQUIRE_OBJECT ( config_http ); diff --git a/qemu/roms/ipxe/src/net/tcp/httpdigest.c b/qemu/roms/ipxe/src/net/tcp/httpdigest.c new file mode 100644 index 000000000..626dd7e9d --- /dev/null +++ b/qemu/roms/ipxe/src/net/tcp/httpdigest.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * Hyper Text Transfer Protocol (HTTP) Digest authentication + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Disambiguate the various error causes */ +#define EACCES_USERNAME __einfo_error ( EINFO_EACCES_USERNAME ) +#define EINFO_EACCES_USERNAME \ + __einfo_uniqify ( EINFO_EACCES, 0x01, \ + "No username available for Digest authentication" ) + +/** + * Initialise HTTP Digest + * + * @v ctx Digest context + * @v string Initial string + */ +static void http_digest_init ( struct md5_context *ctx ) { + + /* Initialise MD5 digest */ + digest_init ( &md5_algorithm, ctx ); +} + +/** + * Update HTTP Digest with new data + * + * @v ctx Digest context + * @v string String to append + */ +static void http_digest_update ( struct md5_context *ctx, const char *string ) { + static const char colon = ':'; + + /* Add (possibly colon-separated) field to MD5 digest */ + if ( ctx->len ) + digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) ); + digest_update ( &md5_algorithm, ctx, string, strlen ( string ) ); +} + +/** + * Finalise HTTP Digest + * + * @v ctx Digest context + * @v out Buffer for digest output + * @v len Buffer length + */ +static void http_digest_final ( struct md5_context *ctx, char *out, + size_t len ) { + uint8_t digest[MD5_DIGEST_SIZE]; + + /* Finalise and base16-encode MD5 digest */ + digest_final ( &md5_algorithm, ctx, digest ); + base16_encode ( digest, sizeof ( digest ), out, len ); +} + +/** + * Perform HTTP Digest authentication + * + * @v http HTTP transaction + * @ret rc Return status code + */ +static int http_digest_authenticate ( struct http_transaction *http ) { + struct http_request_auth *req = &http->request.auth; + struct http_response_auth *rsp = &http->response.auth; + char ha1[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ]; + char ha2[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ]; + static const char md5sess[] = "MD5-sess"; + static const char md5[] = "MD5"; + struct md5_context ctx; + + /* Check for required response parameters */ + if ( ! rsp->realm ) { + DBGC ( http, "HTTP %p has no realm for Digest authentication\n", + http ); + return -EINVAL; + } + if ( ! rsp->nonce ) { + DBGC ( http, "HTTP %p has no nonce for Digest authentication\n", + http ); + return -EINVAL; + } + + /* Record username and password */ + if ( ! http->uri->user ) { + DBGC ( http, "HTTP %p has no username for Digest " + "authentication\n", http ); + return -EACCES_USERNAME; + } + req->username = http->uri->user; + req->password = ( http->uri->password ? http->uri->password : "" ); + + /* Handle quality of protection */ + if ( rsp->qop ) { + + /* Use "auth" in subsequent request */ + req->qop = "auth"; + + /* Generate a client nonce */ + snprintf ( req->cnonce, sizeof ( req->cnonce ), + "%08lx", random() ); + + /* Determine algorithm */ + req->algorithm = md5; + if ( rsp->algorithm && + ( strcasecmp ( rsp->algorithm, md5sess ) == 0 ) ) { + req->algorithm = md5sess; + } + } + + /* Generate HA1 */ + http_digest_init ( &ctx ); + http_digest_update ( &ctx, req->username ); + http_digest_update ( &ctx, rsp->realm ); + http_digest_update ( &ctx, req->password ); + http_digest_final ( &ctx, ha1, sizeof ( ha1 ) ); + if ( req->algorithm == md5sess ) { + http_digest_init ( &ctx ); + http_digest_update ( &ctx, ha1 ); + http_digest_update ( &ctx, rsp->nonce ); + http_digest_update ( &ctx, req->cnonce ); + http_digest_final ( &ctx, ha1, sizeof ( ha1 ) ); + } + + /* Generate HA2 */ + http_digest_init ( &ctx ); + http_digest_update ( &ctx, http->request.method->name ); + http_digest_update ( &ctx, http->request.uri ); + http_digest_final ( &ctx, ha2, sizeof ( ha2 ) ); + + /* Generate response */ + http_digest_init ( &ctx ); + http_digest_update ( &ctx, ha1 ); + http_digest_update ( &ctx, rsp->nonce ); + if ( req->qop ) { + http_digest_update ( &ctx, HTTP_DIGEST_NC ); + http_digest_update ( &ctx, req->cnonce ); + http_digest_update ( &ctx, req->qop ); + } + http_digest_update ( &ctx, ha2 ); + http_digest_final ( &ctx, req->response, sizeof ( req->response ) ); + + return 0; +} + +/** + * Construct HTTP "Authorization" header for Digest authentication + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_digest_auth ( struct http_transaction *http, + char *buf, size_t len ) { + struct http_request_auth *req = &http->request.auth; + struct http_response_auth *rsp = &http->response.auth; + size_t used = 0; + + /* Sanity checks */ + assert ( rsp->realm != NULL ); + assert ( rsp->nonce != NULL ); + assert ( req->username != NULL ); + if ( req->qop ) { + assert ( req->algorithm != NULL ); + assert ( req->cnonce[0] != '\0' ); + } + assert ( req->response[0] != '\0' ); + + /* Construct response */ + used += ssnprintf ( ( buf + used ), ( len - used ), + "realm=\"%s\", nonce=\"%s\", uri=\"%s\", " + "username=\"%s\"", rsp->realm, rsp->nonce, + http->request.uri, req->username ); + if ( rsp->opaque ) { + used += ssnprintf ( ( buf + used ), ( len - used ), + ", opaque=\"%s\"", rsp->opaque ); + } + if ( req->qop ) { + used += ssnprintf ( ( buf + used ), ( len - used ), + ", qop=%s, algorithm=%s, cnonce=\"%s\", " + "nc=" HTTP_DIGEST_NC, req->qop, + req->algorithm, req->cnonce ); + } + used += ssnprintf ( ( buf + used ), ( len - used ), + ", response=\"%s\"", req->response ); + + return used; +} + +/** HTTP Digest authentication scheme */ +struct http_authentication http_digest_auth __http_authentication = { + .name = "Digest", + .authenticate = http_digest_authenticate, + .format = http_format_digest_auth, +}; + +/* Drag in HTTP authentication support */ +REQUIRING_SYMBOL ( http_digest_auth ); +REQUIRE_OBJECT ( httpauth ); diff --git a/qemu/roms/ipxe/src/net/tcp/https.c b/qemu/roms/ipxe/src/net/tcp/https.c index 6112acdae..e91000322 100644 --- a/qemu/roms/ipxe/src/net/tcp/https.c +++ b/qemu/roms/ipxe/src/net/tcp/https.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file @@ -26,7 +30,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -#include #include #include #include @@ -34,19 +37,15 @@ FILE_LICENCE ( GPL2_OR_LATER ); FEATURE ( FEATURE_PROTOCOL, "HTTPS", DHCP_EB_FEATURE_HTTPS, 1 ); -/** - * Initiate an HTTPS connection - * - * @v xfer Data transfer interface - * @v uri Uniform Resource Identifier - * @ret rc Return status code - */ -static int https_open ( struct interface *xfer, struct uri *uri ) { - return http_open_filter ( xfer, uri, HTTPS_PORT, add_tls ); -} - /** HTTPS URI opener */ struct uri_opener https_uri_opener __uri_opener = { .scheme = "https", - .open = https_open, + .open = http_open_uri, +}; + +/** HTTP URI scheme */ +struct http_scheme https_scheme __http_scheme = { + .name = "https", + .port = HTTPS_PORT, + .filter = add_tls, }; diff --git a/qemu/roms/ipxe/src/net/tcp/iscsi.c b/qemu/roms/ipxe/src/net/tcp/iscsi.c index 03c6d0f23..019a4c14e 100644 --- a/qemu/roms/ipxe/src/net/tcp/iscsi.c +++ b/qemu/roms/ipxe/src/net/tcp/iscsi.c @@ -15,14 +15,19 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include #include +#include #include #include #include @@ -127,7 +132,7 @@ FEATURE ( FEATURE_PROTOCOL, "iSCSI", DHCP_EB_FEATURE_ISCSI, 1 ); #define EPROTO_INVALID_LARGE_BINARY \ __einfo_error ( EINFO_EPROTO_INVALID_LARGE_BINARY ) #define EINFO_EPROTO_INVALID_LARGE_BINARY \ - __einfo_uniqify ( EINFO_EPROTO, 0x03, "Invalid large binary" ) + __einfo_uniqify ( EINFO_EPROTO, 0x03, "Invalid large binary value" ) #define EPROTO_INVALID_CHAP_RESPONSE \ __einfo_error ( EINFO_EPROTO_INVALID_CHAP_RESPONSE ) #define EINFO_EPROTO_INVALID_CHAP_RESPONSE \ @@ -704,7 +709,7 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi, char buf[ base16_encoded_len ( iscsi->chap.response_len ) + 1 ]; assert ( iscsi->initiator_username != NULL ); base16_encode ( iscsi->chap.response, iscsi->chap.response_len, - buf ); + buf, sizeof ( buf ) ); used += ssnprintf ( data + used, len - used, "CHAP_N=%s%cCHAP_R=0x%s%c", iscsi->initiator_username, 0, buf, 0 ); @@ -714,7 +719,7 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi, size_t challenge_len = ( sizeof ( iscsi->chap_challenge ) - 1 ); char buf[ base16_encoded_len ( challenge_len ) + 1 ]; base16_encode ( ( iscsi->chap_challenge + 1 ), challenge_len, - buf ); + buf, sizeof ( buf ) ); used += ssnprintf ( data + used, len - used, "CHAP_I=%d%cCHAP_C=0x%s%c", iscsi->chap_challenge[0], 0, buf, 0 ); @@ -823,39 +828,28 @@ static int iscsi_tx_login_request ( struct iscsi_session *iscsi ) { return xfer_deliver_iob ( &iscsi->socket, iobuf ); } -/** - * Calculate maximum length of decoded large binary value - * - * @v encoded Encoded large binary value - * @v max_raw_len Maximum length of raw data - */ -static inline size_t -iscsi_large_binary_decoded_max_len ( const char *encoded ) { - return ( strlen ( encoded ) ); /* Decoding never expands data */ -} - /** * Decode large binary value * * @v encoded Encoded large binary value * @v raw Raw data + * @v len Length of data buffer * @ret len Length of raw data, or negative error */ -static int iscsi_large_binary_decode ( const char *encoded, uint8_t *raw ) { - - if ( encoded[0] != '0' ) - return -EPROTO_INVALID_LARGE_BINARY; - - switch ( encoded[1] ) { - case 'x' : - case 'X' : - return base16_decode ( ( encoded + 2 ), raw ); - case 'b' : - case 'B' : - return base64_decode ( ( encoded + 2 ), raw ); - default: - return -EPROTO_INVALID_LARGE_BINARY; +static int iscsi_large_binary_decode ( const char *encoded, uint8_t *raw, + size_t len ) { + + /* Check for initial '0x' or '0b' and decode as appropriate */ + if ( *(encoded++) == '0' ) { + switch ( tolower ( *(encoded++) ) ) { + case 'x' : + return base16_decode ( encoded, raw, len ); + case 'b' : + return base64_decode ( encoded, raw, len ); + } } + + return -EPROTO_INVALID_LARGE_BINARY; } /** @@ -982,19 +976,19 @@ static int iscsi_handle_chap_i_value ( struct iscsi_session *iscsi, */ static int iscsi_handle_chap_c_value ( struct iscsi_session *iscsi, const char *value ) { - uint8_t buf[ iscsi_large_binary_decoded_max_len ( value ) ]; + uint8_t buf[ strlen ( value ) ]; /* Decoding never expands data */ unsigned int i; - size_t len; + int len; int rc; /* Process challenge */ - rc = iscsi_large_binary_decode ( value, buf ); - if ( rc < 0 ) { + len = iscsi_large_binary_decode ( value, buf, sizeof ( buf ) ); + if ( len < 0 ) { + rc = len; DBGC ( iscsi, "iSCSI %p invalid CHAP challenge \"%s\": %s\n", iscsi, value, strerror ( rc ) ); return rc; } - len = rc; chap_update ( &iscsi->chap, buf, len ); /* Build CHAP response */ @@ -1052,8 +1046,8 @@ static int iscsi_handle_chap_n_value ( struct iscsi_session *iscsi, */ static int iscsi_handle_chap_r_value ( struct iscsi_session *iscsi, const char *value ) { - uint8_t buf[ iscsi_large_binary_decoded_max_len ( value ) ]; - size_t len; + uint8_t buf[ strlen ( value ) ]; /* Decoding never expands data */ + int len; int rc; /* Generate CHAP response for verification */ @@ -1073,16 +1067,16 @@ static int iscsi_handle_chap_r_value ( struct iscsi_session *iscsi, chap_respond ( &iscsi->chap ); /* Process response */ - rc = iscsi_large_binary_decode ( value, buf ); - if ( rc < 0 ) { + len = iscsi_large_binary_decode ( value, buf, sizeof ( buf ) ); + if ( len < 0 ) { + rc = len; DBGC ( iscsi, "iSCSI %p invalid CHAP response \"%s\": %s\n", iscsi, value, strerror ( rc ) ); return rc; } - len = rc; /* Check CHAP response */ - if ( len != iscsi->chap.response_len ) { + if ( len != ( int ) iscsi->chap.response_len ) { DBGC ( iscsi, "iSCSI %p invalid CHAP response length\n", iscsi ); return -EPROTO_INVALID_CHAP_RESPONSE; @@ -1445,8 +1439,10 @@ static void iscsi_tx_done ( struct iscsi_session *iscsi ) { switch ( common->opcode & ISCSI_OPCODE_MASK ) { case ISCSI_OPCODE_DATA_OUT: iscsi_data_out_done ( iscsi ); + break; case ISCSI_OPCODE_LOGIN_REQUEST: iscsi_login_request_done ( iscsi ); + break; default: /* No action */ break; diff --git a/qemu/roms/ipxe/src/net/tcp/syslogs.c b/qemu/roms/ipxe/src/net/tcp/syslogs.c index 095afc543..0c07f86d5 100644 --- a/qemu/roms/ipxe/src/net/tcp/syslogs.c +++ b/qemu/roms/ipxe/src/net/tcp/syslogs.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/net/tcpip.c b/qemu/roms/ipxe/src/net/tcpip.c index 4bcbe64bb..5ad982fd1 100644 --- a/qemu/roms/ipxe/src/net/tcpip.c +++ b/qemu/roms/ipxe/src/net/tcpip.c @@ -17,7 +17,7 @@ * TCP/IP transport-network layer interface */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Process a received TCP/IP packet @@ -235,7 +235,7 @@ int tcpip_bind ( struct sockaddr_tcpip *st_local, /* Otherwise, find an available port in the range [1,1023] or * [1025,65535] as appropriate. */ - min_port = ( ( ( ! flags ) & TCPIP_BIND_PRIVILEGED ) + 1 ); + min_port = ( ( ( ~flags ) & TCPIP_BIND_PRIVILEGED ) + 1 ); max_port = ( ( flags & TCPIP_BIND_PRIVILEGED ) - 1 ); offset = random(); for ( i = 0 ; i <= max_port ; i++ ) { diff --git a/qemu/roms/ipxe/src/net/tls.c b/qemu/roms/ipxe/src/net/tls.c index 30ccc932e..db01fb291 100644 --- a/qemu/roms/ipxe/src/net/tls.c +++ b/qemu/roms/ipxe/src/net/tls.c @@ -179,20 +179,29 @@ static void tls_clear_cipher ( struct tls_session *tls, ****************************************************************************** */ +/** A TLS 24-bit integer + * + * TLS uses 24-bit integers in several places, which are awkward to + * parse in C. + */ +typedef struct { + /** High byte */ + uint8_t high; + /** Low word */ + uint16_t low; +} __attribute__ (( packed )) tls24_t; + /** * Extract 24-bit field value * * @v field24 24-bit field * @ret value Field value * - * TLS uses 24-bit integers in several places, which are awkward to - * parse in C. */ static inline __attribute__ (( always_inline )) unsigned long -tls_uint24 ( const uint8_t field24[3] ) { - const uint32_t *field32 __attribute__ (( may_alias )) = - ( ( const void * ) field24 ); - return ( be32_to_cpu ( *field32 ) >> 8 ); +tls_uint24 ( const tls24_t *field24 ) { + + return ( ( field24->high << 16 ) | be16_to_cpu ( field24->low ) ); } /** @@ -200,13 +209,11 @@ tls_uint24 ( const uint8_t field24[3] ) { * * @v field24 24-bit field * @v value Field value - * - * The field must be pre-zeroed. */ -static void tls_set_uint24 ( uint8_t field24[3], unsigned long value ) { - uint32_t *field32 __attribute__ (( may_alias )) = - ( ( void * ) field24 ); - *field32 |= cpu_to_be32 ( value << 8 ); +static void tls_set_uint24 ( tls24_t *field24, unsigned long value ) { + + field24->high = ( value >> 16 ); + field24->low = cpu_to_be16 ( value ); } /** @@ -659,41 +666,8 @@ struct tls_cipher_suite tls_cipher_suite_null = { .digest = &digest_null, }; -/** Supported cipher suites, in order of preference */ -struct tls_cipher_suite tls_cipher_suites[] = { - { - .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA256 ), - .key_len = ( 256 / 8 ), - .pubkey = &rsa_algorithm, - .cipher = &aes_cbc_algorithm, - .digest = &sha256_algorithm, - }, - { - .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA256 ), - .key_len = ( 128 / 8 ), - .pubkey = &rsa_algorithm, - .cipher = &aes_cbc_algorithm, - .digest = &sha256_algorithm, - }, - { - .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA ), - .key_len = ( 256 / 8 ), - .pubkey = &rsa_algorithm, - .cipher = &aes_cbc_algorithm, - .digest = &sha1_algorithm, - }, - { - .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA ), - .key_len = ( 128 / 8 ), - .pubkey = &rsa_algorithm, - .cipher = &aes_cbc_algorithm, - .digest = &sha1_algorithm, - }, -}; - /** Number of supported cipher suites */ -#define TLS_NUM_CIPHER_SUITES \ - ( sizeof ( tls_cipher_suites ) / sizeof ( tls_cipher_suites[0] ) ) +#define TLS_NUM_CIPHER_SUITES table_num_entries ( TLS_CIPHER_SUITES ) /** * Identify cipher suite @@ -704,11 +678,9 @@ struct tls_cipher_suite tls_cipher_suites[] = { static struct tls_cipher_suite * tls_find_cipher_suite ( unsigned int cipher_suite ) { struct tls_cipher_suite *suite; - unsigned int i; /* Identify cipher suite */ - for ( i = 0 ; i < TLS_NUM_CIPHER_SUITES ; i++ ) { - suite = &tls_cipher_suites[i]; + for_each_table_entry ( suite, TLS_CIPHER_SUITES ) { if ( suite->code == cipher_suite ) return suite; } @@ -841,26 +813,9 @@ static int tls_change_cipher ( struct tls_session *tls, ****************************************************************************** */ -/** Supported signature and hash algorithms - * - * Note that the default (TLSv1.1 and earlier) algorithm using - * MD5+SHA1 is never explicitly specified. - */ -struct tls_signature_hash_algorithm tls_signature_hash_algorithms[] = { - { - .code = { - .signature = TLS_RSA_ALGORITHM, - .hash = TLS_SHA256_ALGORITHM, - }, - .pubkey = &rsa_algorithm, - .digest = &sha256_algorithm, - }, -}; - /** Number of supported signature and hash algorithms */ -#define TLS_NUM_SIG_HASH_ALGORITHMS \ - ( sizeof ( tls_signature_hash_algorithms ) / \ - sizeof ( tls_signature_hash_algorithms[0] ) ) +#define TLS_NUM_SIG_HASH_ALGORITHMS \ + table_num_entries ( TLS_SIG_HASH_ALGORITHMS ) /** * Find TLS signature and hash algorithm @@ -873,11 +828,9 @@ static struct tls_signature_hash_algorithm * tls_signature_hash_algorithm ( struct pubkey_algorithm *pubkey, struct digest_algorithm *digest ) { struct tls_signature_hash_algorithm *sig_hash; - unsigned int i; /* Identify signature and hash algorithm */ - for ( i = 0 ; i < TLS_NUM_SIG_HASH_ALGORITHMS ; i++ ) { - sig_hash = &tls_signature_hash_algorithms[i]; + for_each_table_entry ( sig_hash, TLS_SIG_HASH_ALGORITHMS ) { if ( ( sig_hash->pubkey == pubkey ) && ( sig_hash->digest == digest ) ) { return sig_hash; @@ -994,8 +947,17 @@ static int tls_send_client_hello ( struct tls_session *tls ) { struct { uint8_t max; } __attribute__ (( packed )) max_fragment_length; + uint16_t signature_algorithms_type; + uint16_t signature_algorithms_len; + struct { + uint16_t len; + struct tls_signature_hash_id + code[TLS_NUM_SIG_HASH_ALGORITHMS]; + } __attribute__ (( packed )) signature_algorithms; } __attribute__ (( packed )) extensions; } __attribute__ (( packed )) hello; + struct tls_cipher_suite *suite; + struct tls_signature_hash_algorithm *sighash; unsigned int i; memset ( &hello, 0, sizeof ( hello ) ); @@ -1005,8 +967,8 @@ static int tls_send_client_hello ( struct tls_session *tls ) { hello.version = htons ( tls->version ); memcpy ( &hello.random, &tls->client_random, sizeof ( hello.random ) ); hello.cipher_suite_len = htons ( sizeof ( hello.cipher_suites ) ); - for ( i = 0 ; i < TLS_NUM_CIPHER_SUITES ; i++ ) - hello.cipher_suites[i] = tls_cipher_suites[i].code; + i = 0 ; for_each_table_entry ( suite, TLS_CIPHER_SUITES ) + hello.cipher_suites[i++] = suite->code; hello.compression_methods_len = sizeof ( hello.compression_methods ); hello.extensions_len = htons ( sizeof ( hello.extensions ) ); hello.extensions.server_name_type = htons ( TLS_SERVER_NAME ); @@ -1025,6 +987,14 @@ static int tls_send_client_hello ( struct tls_session *tls ) { = htons ( sizeof ( hello.extensions.max_fragment_length ) ); hello.extensions.max_fragment_length.max = TLS_MAX_FRAGMENT_LENGTH_4096; + hello.extensions.signature_algorithms_type + = htons ( TLS_SIGNATURE_ALGORITHMS ); + hello.extensions.signature_algorithms_len + = htons ( sizeof ( hello.extensions.signature_algorithms ) ); + hello.extensions.signature_algorithms.len + = htons ( sizeof ( hello.extensions.signature_algorithms.code)); + i = 0 ; for_each_table_entry ( sighash, TLS_SIG_HASH_ALGORITHMS ) + hello.extensions.signature_algorithms.code[i++] = sighash->code; return tls_send_handshake ( tls, &hello, sizeof ( hello ) ); } @@ -1038,9 +1008,9 @@ static int tls_send_client_hello ( struct tls_session *tls ) { static int tls_send_certificate ( struct tls_session *tls ) { struct { uint32_t type_length; - uint8_t length[3]; + tls24_t length; struct { - uint8_t length[3]; + tls24_t length; uint8_t data[ tls->cert->raw.len ]; } __attribute__ (( packed )) certificates[1]; } __attribute__ (( packed )) *certificate; @@ -1058,9 +1028,9 @@ static int tls_send_certificate ( struct tls_session *tls ) { ( cpu_to_le32 ( TLS_CERTIFICATE ) | htonl ( sizeof ( *certificate ) - sizeof ( certificate->type_length ) ) ); - tls_set_uint24 ( certificate->length, + tls_set_uint24 ( &certificate->length, sizeof ( certificate->certificates ) ); - tls_set_uint24 ( certificate->certificates[0].length, + tls_set_uint24 ( &certificate->certificates[0].length, sizeof ( certificate->certificates[0].data ) ); memcpy ( certificate->certificates[0].data, tls->cert->raw.data, @@ -1412,7 +1382,7 @@ static int tls_parse_chain ( struct tls_session *tls, const void *data, size_t len ) { const void *end = ( data + len ); const struct { - uint8_t length[3]; + tls24_t length; uint8_t data[0]; } __attribute__ (( packed )) *certificate; size_t certificate_len; @@ -1436,7 +1406,7 @@ static int tls_parse_chain ( struct tls_session *tls, /* Extract raw certificate data */ certificate = data; - certificate_len = tls_uint24 ( certificate->length ); + certificate_len = tls_uint24 ( &certificate->length ); next = ( certificate->data + certificate_len ); if ( next > end ) { DBGC ( tls, "TLS %p overlength certificate:\n", tls ); @@ -1482,10 +1452,10 @@ static int tls_parse_chain ( struct tls_session *tls, static int tls_new_certificate ( struct tls_session *tls, const void *data, size_t len ) { const struct { - uint8_t length[3]; + tls24_t length; uint8_t certificates[0]; } __attribute__ (( packed )) *certificate = data; - size_t certificates_len = tls_uint24 ( certificate->length ); + size_t certificates_len = tls_uint24 ( &certificate->length ); const void *end = ( certificate->certificates + certificates_len ); int rc; @@ -1634,12 +1604,12 @@ static int tls_new_handshake ( struct tls_session *tls, while ( data != end ) { const struct { uint8_t type; - uint8_t length[3]; + tls24_t length; uint8_t payload[0]; } __attribute__ (( packed )) *handshake = data; - void *payload = &handshake->payload; - size_t payload_len = tls_uint24 ( handshake->length ); - void *next = ( payload + payload_len ); + const void *payload = &handshake->payload; + size_t payload_len = tls_uint24 ( &handshake->length ); + const void *next = ( payload + payload_len ); /* Sanity check */ if ( next > end ) { @@ -2637,3 +2607,9 @@ int add_tls ( struct interface *xfer, const char *name, err_alloc: return rc; } + +/* Drag in objects via add_tls() */ +REQUIRING_SYMBOL ( add_tls ); + +/* Drag in crypto configuration */ +REQUIRE_OBJECT ( config_crypto ); diff --git a/qemu/roms/ipxe/src/net/udp.c b/qemu/roms/ipxe/src/net/udp.c index 76da67ecf..0f7dfb24a 100644 --- a/qemu/roms/ipxe/src/net/udp.c +++ b/qemu/roms/ipxe/src/net/udp.c @@ -17,7 +17,7 @@ * UDP protocol */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * A UDP connection diff --git a/qemu/roms/ipxe/src/net/udp/dhcp.c b/qemu/roms/ipxe/src/net/udp/dhcp.c index 04fad04c2..aed5ee360 100644 --- a/qemu/roms/ipxe/src/net/udp/dhcp.c +++ b/qemu/roms/ipxe/src/net/udp/dhcp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -44,6 +48,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include /** @file * @@ -149,30 +154,32 @@ struct dhcp_session_state { * @v dhcppkt DHCP packet * @v peer Destination address */ - int ( * tx ) ( struct dhcp_session *dhcp, - struct dhcp_packet *dhcppkt, + int ( * tx ) ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, struct sockaddr_in *peer ); - /** Handle received packet + /** + * Handle received packet * * @v dhcp DHCP session * @v dhcppkt DHCP packet * @v peer DHCP server address * @v msgtype DHCP message type * @v server_id DHCP server ID + * @v pseudo_id DHCP server pseudo-ID */ - void ( * rx ) ( struct dhcp_session *dhcp, - struct dhcp_packet *dhcppkt, - struct sockaddr_in *peer, - uint8_t msgtype, struct in_addr server_id ); - /** Handle timer expiry + void ( * rx ) ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, + struct sockaddr_in *peer, uint8_t msgtype, + struct in_addr server_id, struct in_addr pseudo_id ); + /** + * Handle timer expiry * * @v dhcp DHCP session */ void ( * expired ) ( struct dhcp_session *dhcp ); /** Transmitted message type */ uint8_t tx_msgtype; - /** Apply minimum timeout */ - uint8_t apply_min_timeout; + /** Timeout parameters */ + uint8_t min_timeout_sec; + uint8_t max_timeout_sec; }; static struct dhcp_session_state dhcp_state_discover; @@ -272,9 +279,9 @@ static void dhcp_set_state ( struct dhcp_session *dhcp, dhcp->state = state; dhcp->start = currticks(); stop_timer ( &dhcp->timer ); - dhcp->timer.min_timeout = - ( state->apply_min_timeout ? DHCP_MIN_TIMEOUT : 0 ); - dhcp->timer.max_timeout = DHCP_MAX_TIMEOUT; + set_timer_limits ( &dhcp->timer, + ( state->min_timeout_sec * TICKS_PER_SEC ), + ( state->max_timeout_sec * TICKS_PER_SEC ) ); start_timer_nodelay ( &dhcp->timer ); } @@ -334,11 +341,13 @@ static int dhcp_discovery_tx ( struct dhcp_session *dhcp, * @v peer DHCP server address * @v msgtype DHCP message type * @v server_id DHCP server ID + * @v pseudo_id DHCP server pseudo-ID */ static void dhcp_discovery_rx ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, struct sockaddr_in *peer, uint8_t msgtype, - struct in_addr server_id ) { + struct in_addr server_id, + struct in_addr pseudo_id ) { struct in_addr ip; char vci[9]; /* "PXEClient" */ int vci_len; @@ -350,8 +359,11 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp, DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ) ); - if ( server_id.s_addr != peer->sin_addr.s_addr ) - DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) ); + if ( ( server_id.s_addr != peer->sin_addr.s_addr ) || + ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) { + DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) ); + DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) ); + } /* Identify offered IP address */ ip = dhcppkt->dhcphdr->yiaddr; @@ -392,10 +404,10 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp, } /* Select as ProxyDHCP offer, if applicable */ - if ( server_id.s_addr && has_pxeclient && + if ( pseudo_id.s_addr && has_pxeclient && ( priority >= dhcp->proxy_priority ) ) { dhcppkt_put ( dhcp->proxy_offer ); - dhcp->proxy_server = server_id; + dhcp->proxy_server = pseudo_id; dhcp->proxy_offer = dhcppkt_get ( dhcppkt ); dhcp->proxy_priority = priority; } @@ -415,7 +427,7 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp, /* If we can't yet transition to DHCPREQUEST, do nothing */ elapsed = ( currticks() - dhcp->start ); if ( ! ( dhcp->no_pxedhcp || dhcp->proxy_offer || - ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) ) + ( elapsed > DHCP_DISC_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) ) ) return; /* Transition to DHCPREQUEST */ @@ -430,8 +442,18 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp, static void dhcp_discovery_expired ( struct dhcp_session *dhcp ) { unsigned long elapsed = ( currticks() - dhcp->start ); + /* If link is blocked, defer DHCP discovery (and reset timeout) */ + if ( netdev_link_blocked ( dhcp->netdev ) ) { + DBGC ( dhcp, "DHCP %p deferring discovery\n", dhcp ); + start_timer_fixed ( &dhcp->timer, + ( DHCP_DISC_START_TIMEOUT_SEC * + TICKS_PER_SEC ) ); + return; + } + /* Give up waiting for ProxyDHCP before we reach the failure point */ - if ( dhcp->offer.s_addr && ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) { + if ( dhcp->offer.s_addr && + ( elapsed > DHCP_DISC_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) ) { dhcp_set_state ( dhcp, &dhcp_state_request ); return; } @@ -447,7 +469,8 @@ static struct dhcp_session_state dhcp_state_discover = { .rx = dhcp_discovery_rx, .expired = dhcp_discovery_expired, .tx_msgtype = DHCPDISCOVER, - .apply_min_timeout = 1, + .min_timeout_sec = DHCP_DISC_START_TIMEOUT_SEC, + .max_timeout_sec = DHCP_DISC_END_TIMEOUT_SEC, }; /** @@ -493,11 +516,13 @@ static int dhcp_request_tx ( struct dhcp_session *dhcp, * @v peer DHCP server address * @v msgtype DHCP message type * @v server_id DHCP server ID + * @v pseudo_id DHCP server pseudo-ID */ static void dhcp_request_rx ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, struct sockaddr_in *peer, uint8_t msgtype, - struct in_addr server_id ) { + struct in_addr server_id, + struct in_addr pseudo_id ) { struct in_addr ip; struct settings *parent; struct settings *settings; @@ -506,8 +531,11 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp, DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ) ); - if ( server_id.s_addr != peer->sin_addr.s_addr ) - DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) ); + if ( ( server_id.s_addr != peer->sin_addr.s_addr ) || + ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) { + DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) ); + DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) ); + } /* Identify leased IP address */ ip = dhcppkt->dhcphdr->yiaddr; @@ -584,7 +612,8 @@ static struct dhcp_session_state dhcp_state_request = { .rx = dhcp_request_rx, .expired = dhcp_request_expired, .tx_msgtype = DHCPREQUEST, - .apply_min_timeout = 0, + .min_timeout_sec = DHCP_REQ_START_TIMEOUT_SEC, + .max_timeout_sec = DHCP_REQ_END_TIMEOUT_SEC, }; /** @@ -623,19 +652,26 @@ static int dhcp_proxy_tx ( struct dhcp_session *dhcp, * @v peer DHCP server address * @v msgtype DHCP message type * @v server_id DHCP server ID + * @v pseudo_id DHCP server pseudo-ID */ static void dhcp_proxy_rx ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, struct sockaddr_in *peer, uint8_t msgtype, - struct in_addr server_id ) { + struct in_addr server_id, + struct in_addr pseudo_id ) { struct settings *settings = &dhcppkt->settings; int rc; DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ) ); - if ( server_id.s_addr != peer->sin_addr.s_addr ) - DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) ); + if ( ( server_id.s_addr != peer->sin_addr.s_addr ) || + ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) { + DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) ); + DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) ); + } + if ( dhcp_has_pxeopts ( dhcppkt ) ) + DBGC ( dhcp, " pxe" ); DBGC ( dhcp, "\n" ); /* Filter out unacceptable responses */ @@ -643,8 +679,9 @@ static void dhcp_proxy_rx ( struct dhcp_session *dhcp, return; if ( ( msgtype != DHCPOFFER ) && ( msgtype != DHCPACK ) ) return; - if ( server_id.s_addr /* Linux PXE server omits server ID */ && - ( server_id.s_addr != dhcp->proxy_server.s_addr ) ) + if ( ( pseudo_id.s_addr != dhcp->proxy_server.s_addr ) ) + return; + if ( ! dhcp_has_pxeopts ( dhcppkt ) ) return; /* Register settings */ @@ -669,7 +706,7 @@ static void dhcp_proxy_expired ( struct dhcp_session *dhcp ) { unsigned long elapsed = ( currticks() - dhcp->start ); /* Give up waiting for ProxyDHCP before we reach the failure point */ - if ( elapsed > PROXYDHCP_MAX_TIMEOUT ) { + if ( elapsed > DHCP_REQ_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) { dhcp_finished ( dhcp, 0 ); return; } @@ -685,7 +722,8 @@ static struct dhcp_session_state dhcp_state_proxy = { .rx = dhcp_proxy_rx, .expired = dhcp_proxy_expired, .tx_msgtype = DHCPREQUEST, - .apply_min_timeout = 0, + .min_timeout_sec = DHCP_PROXY_START_TIMEOUT_SEC, + .max_timeout_sec = DHCP_PROXY_END_TIMEOUT_SEC, }; /** @@ -753,19 +791,24 @@ static int dhcp_pxebs_accept ( struct dhcp_session *dhcp, * @v peer DHCP server address * @v msgtype DHCP message type * @v server_id DHCP server ID + * @v pseudo_id DHCP server pseudo-ID */ static void dhcp_pxebs_rx ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, struct sockaddr_in *peer, uint8_t msgtype, - struct in_addr server_id ) { + struct in_addr server_id, + struct in_addr pseudo_id ) { struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 }; int rc; DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ) ); - if ( server_id.s_addr != peer->sin_addr.s_addr ) - DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) ); + if ( ( server_id.s_addr != peer->sin_addr.s_addr ) || + ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) { + DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) ); + DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) ); + } /* Identify boot menu item */ dhcppkt_fetch ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM, @@ -782,8 +825,7 @@ static void dhcp_pxebs_rx ( struct dhcp_session *dhcp, return; if ( menu_item.type != dhcp->pxe_type ) return; - if ( ! dhcp_pxebs_accept ( dhcp, ( server_id.s_addr ? - server_id : peer->sin_addr ) ) ) + if ( ! dhcp_pxebs_accept ( dhcp, pseudo_id ) ) return; /* Register settings */ @@ -810,7 +852,7 @@ static void dhcp_pxebs_expired ( struct dhcp_session *dhcp ) { /* Give up waiting before we reach the failure point, and fail * over to the next server in the attempt list */ - if ( elapsed > PXEBS_MAX_TIMEOUT ) { + if ( elapsed > PXEBS_MAX_TIMEOUT_SEC * TICKS_PER_SEC ) { dhcp->pxe_attempt++; if ( dhcp->pxe_attempt->s_addr ) { dhcp_set_state ( dhcp, &dhcp_state_pxebs ); @@ -832,7 +874,8 @@ static struct dhcp_session_state dhcp_state_pxebs = { .rx = dhcp_pxebs_rx, .expired = dhcp_pxebs_expired, .tx_msgtype = DHCPREQUEST, - .apply_min_timeout = 1, + .min_timeout_sec = PXEBS_START_TIMEOUT_SEC, + .max_timeout_sec = PXEBS_END_TIMEOUT_SEC, }; /**************************************************************************** @@ -1114,6 +1157,7 @@ static int dhcp_deliver ( struct dhcp_session *dhcp, struct dhcphdr *dhcphdr; uint8_t msgtype = 0; struct in_addr server_id = { 0 }; + struct in_addr pseudo_id; int rc = 0; /* Sanity checks */ @@ -1148,6 +1192,13 @@ static int dhcp_deliver ( struct dhcp_session *dhcp, dhcppkt_fetch ( dhcppkt, DHCP_SERVER_IDENTIFIER, &server_id, sizeof ( server_id ) ); + /* Identify server pseudo-ID */ + pseudo_id = server_id; + if ( ! pseudo_id.s_addr ) + pseudo_id = dhcppkt->dhcphdr->siaddr; + if ( ! pseudo_id.s_addr ) + pseudo_id = peer->sin_addr; + /* Check for matching transaction ID */ if ( dhcphdr->xid != dhcp->xid ) { DBGC ( dhcp, "DHCP %p %s from %s:%d has bad transaction " @@ -1170,7 +1221,7 @@ static int dhcp_deliver ( struct dhcp_session *dhcp, } /* Handle packet based on current state */ - dhcp->state->rx ( dhcp, dhcppkt, peer, msgtype, server_id ); + dhcp->state->rx ( dhcp, dhcppkt, peer, msgtype, server_id, pseudo_id ); err_chaddr: err_xid: diff --git a/qemu/roms/ipxe/src/net/udp/dhcpv6.c b/qemu/roms/ipxe/src/net/udp/dhcpv6.c index f7736d08e..a63543775 100644 --- a/qemu/roms/ipxe/src/net/udp/dhcpv6.c +++ b/qemu/roms/ipxe/src/net/udp/dhcpv6.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/udp/dns.c b/qemu/roms/ipxe/src/net/udp/dns.c index fffe6e697..2d77477f6 100644 --- a/qemu/roms/ipxe/src/net/udp/dns.c +++ b/qemu/roms/ipxe/src/net/udp/dns.c @@ -18,9 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/udp/slam.c b/qemu/roms/ipxe/src/net/udp/slam.c index 3cb492d73..8b26bfb3c 100644 --- a/qemu/roms/ipxe/src/net/udp/slam.c +++ b/qemu/roms/ipxe/src/net/udp/slam.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/net/udp/syslog.c b/qemu/roms/ipxe/src/net/udp/syslog.c index d65d19ab8..b6eee6036 100644 --- a/qemu/roms/ipxe/src/net/udp/syslog.c +++ b/qemu/roms/ipxe/src/net/udp/syslog.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/net/udp/tftp.c b/qemu/roms/ipxe/src/net/udp/tftp.c index ee827ae3d..953bcb46a 100644 --- a/qemu/roms/ipxe/src/net/udp/tftp.c +++ b/qemu/roms/ipxe/src/net/udp/tftp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -149,8 +153,6 @@ enum { TFTP_FL_RRQ_MULTICAST = 0x0004, /** Perform MTFTP recovery on timeout */ TFTP_FL_MTFTP_RECOVERY = 0x0008, - /** Only get filesize and then abort the transfer */ - TFTP_FL_SIZEONLY = 0x0010, }; /** Maximum number of MTFTP open requests before falling back to TFTP */ @@ -759,14 +761,6 @@ static int tftp_rx_oack ( struct tftp_request *tftp, void *buf, size_t len ) { goto done; } - /* Abort request if only trying to determine file size */ - if ( tftp->flags & TFTP_FL_SIZEONLY ) { - rc = 0; - tftp_send_error ( tftp, 0, "TFTP Aborted" ); - tftp_done ( tftp, rc ); - return rc; - } - /* Request next data block */ tftp_send_packet ( tftp ); @@ -794,13 +788,6 @@ static int tftp_rx_data ( struct tftp_request *tftp, size_t data_len; int rc; - if ( tftp->flags & TFTP_FL_SIZEONLY ) { - /* If we get here then server doesn't support SIZE option */ - rc = -ENOTSUP; - tftp_send_error ( tftp, 0, "TFTP Aborted" ); - goto done; - } - /* Sanity check */ if ( iob_len ( iobuf ) < sizeof ( *data ) ) { DBGC ( tftp, "TFTP %p received underlength DATA packet " @@ -1036,10 +1023,25 @@ static size_t tftp_xfer_window ( struct tftp_request *tftp ) { return tftp->blksize; } +/** + * Terminate download + * + * @v tftp TFTP connection + * @v rc Reason for close + */ +static void tftp_close ( struct tftp_request *tftp, int rc ) { + + /* Abort download */ + tftp_send_error ( tftp, 0, "TFTP Aborted" ); + + /* Close TFTP request */ + tftp_done ( tftp, rc ); +} + /** TFTP data transfer interface operations */ static struct interface_operation tftp_xfer_operations[] = { INTF_OP ( xfer_window, struct tftp_request *, tftp_xfer_window ), - INTF_OP ( intf_close, struct tftp_request *, tftp_done ), + INTF_OP ( intf_close, struct tftp_request *, tftp_close ), }; /** TFTP data transfer interface descriptor */ @@ -1125,26 +1127,6 @@ struct uri_opener tftp_uri_opener __uri_opener = { .open = tftp_open, }; -/** - * Initiate TFTP-size request - * - * @v xfer Data transfer interface - * @v uri Uniform Resource Identifier - * @ret rc Return status code - */ -static int tftpsize_open ( struct interface *xfer, struct uri *uri ) { - return tftp_core_open ( xfer, uri, TFTP_PORT, NULL, - ( TFTP_FL_RRQ_SIZES | - TFTP_FL_SIZEONLY ) ); - -} - -/** TFTP URI opener */ -struct uri_opener tftpsize_uri_opener __uri_opener = { - .scheme = "tftpsize", - .open = tftpsize_open, -}; - /** * Initiate TFTM download * diff --git a/qemu/roms/ipxe/src/net/validator.c b/qemu/roms/ipxe/src/net/validator.c index 74d70e312..db968398a 100644 --- a/qemu/roms/ipxe/src/net/validator.c +++ b/qemu/roms/ipxe/src/net/validator.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -79,7 +83,7 @@ static void validator_free ( struct refcnt *refcnt ) { DBGC2 ( validator, "VALIDATOR %p freed\n", validator ); x509_chain_put ( validator->chain ); ocsp_put ( validator->ocsp ); - xferbuf_done ( &validator->buffer ); + xferbuf_free ( &validator->buffer ); free ( validator ); } @@ -250,7 +254,8 @@ static int validator_start_download ( struct validator *validator, /* Generate URI string */ len = snprintf ( uri_string, uri_string_len, "%s/%08x.der?subject=", crosscert, crc ); - base64_encode ( issuer->data, issuer->len, ( uri_string + len ) ); + base64_encode ( issuer->data, issuer->len, ( uri_string + len ), + ( uri_string_len - len ) ); DBGC ( validator, "VALIDATOR %p downloading cross-signed certificate " "from %s\n", validator, uri_string ); @@ -387,7 +392,7 @@ static void validator_xfer_close ( struct validator *validator, int rc ) { goto err_append; /* Free downloaded data */ - xferbuf_done ( &validator->buffer ); + xferbuf_free ( &validator->buffer ); /* Resume validation process */ process_add ( &validator->process ); @@ -552,6 +557,7 @@ int create_validator ( struct interface *job, struct x509_chain *chain ) { process_init ( &validator->process, &validator_process_desc, &validator->refcnt ); validator->chain = x509_chain_get ( chain ); + xferbuf_malloc_init ( &validator->buffer ); /* Attach parent interface, mortalise self, and return */ intf_plug_plug ( &validator->job, job ); diff --git a/qemu/roms/ipxe/src/net/vlan.c b/qemu/roms/ipxe/src/net/vlan.c index b4ddde42d..f515c2dc9 100644 --- a/qemu/roms/ipxe/src/net/vlan.c +++ b/qemu/roms/ipxe/src/net/vlan.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -385,6 +389,10 @@ int vlan_create ( struct net_device *trunk, unsigned int tag, snprintf ( netdev->name, sizeof ( netdev->name ), "%s-%d", trunk->name, vlan->tag ); + /* Mark device as not supporting interrupts, if applicable */ + if ( ! netdev_irq_supported ( trunk ) ) + netdev->state |= NETDEV_IRQ_UNSUPPORTED; + /* Register VLAN device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) { DBGC ( netdev, "VLAN %s could not register: %s\n", diff --git a/qemu/roms/ipxe/src/tests/aes_cbc_test.c b/qemu/roms/ipxe/src/tests/aes_cbc_test.c deleted file mode 100644 index 4ae3a92e5..000000000 --- a/qemu/roms/ipxe/src/tests/aes_cbc_test.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2012 Michael Brown . - * - * 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 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -/** @file - * - * AES-in-CBC-mode tests - * - * These test vectors are provided by NIST as part of the - * Cryptographic Toolkit Examples, downloadable from: - * - * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/AES_CBC.pdf - * - */ - -/* Forcibly enable assertions */ -#undef NDEBUG - -#include -#include -#include -#include -#include "cbc_test.h" - -/** Define inline key */ -#define KEY(...) { __VA_ARGS__ } - -/** Define inline initialisation vector */ -#define IV(...) { __VA_ARGS__ } - -/** Define inline plaintext data */ -#define PLAINTEXT(...) { __VA_ARGS__ } - -/** Define inline ciphertext data */ -#define CIPHERTEXT(...) { __VA_ARGS__ } - -/** An AES-in-CBC-mode test */ -struct aes_cbc_test { - /** Key */ - const void *key; - /** Length of key */ - size_t key_len; - /** Initialisation vector */ - const void *iv; - /** Length of initialisation vector */ - size_t iv_len; - /** Plaintext */ - const void *plaintext; - /** Length of plaintext */ - size_t plaintext_len; - /** Ciphertext */ - const void *ciphertext; - /** Length of ciphertext */ - size_t ciphertext_len; -}; - -/** - * Define an AES-in-CBC-mode test - * - * @v name Test name - * @v key_array Key - * @v iv_array Initialisation vector - * @v plaintext_array Plaintext - * @v ciphertext_array Ciphertext - * @ret test AES-in-CBC-mode test - */ -#define AES_CBC_TEST( name, key_array, iv_array, plaintext_array, \ - ciphertext_array ) \ - static const uint8_t name ## _key [] = key_array; \ - static const uint8_t name ## _iv [] = iv_array; \ - static const uint8_t name ## _plaintext [] = plaintext_array; \ - static const uint8_t name ## _ciphertext [] = ciphertext_array; \ - static struct aes_cbc_test name = { \ - .key = name ## _key, \ - .key_len = sizeof ( name ## _key ), \ - .iv = name ## _iv, \ - .iv_len = sizeof ( name ## _iv ), \ - .plaintext = name ## _plaintext, \ - .plaintext_len = sizeof ( name ## _plaintext ), \ - .ciphertext = name ## _ciphertext, \ - .ciphertext_len = sizeof ( name ## _ciphertext ), \ - } - -/** - * Report AES-in-CBC-mode - * - * @v state HMAC_DRBG internal state - * @v test Instantiation test - */ -#define aes_cbc_ok( test ) do { \ - struct cipher_algorithm *cipher = &aes_cbc_algorithm; \ - \ - assert ( (test)->iv_len == cipher->blocksize ); \ - assert ( (test)->plaintext_len == (test)->ciphertext_len ); \ - cbc_encrypt_ok ( cipher, (test)->key, (test)->key_len, \ - (test)->iv, (test)->plaintext, \ - (test)->ciphertext, (test)->plaintext_len ); \ - cbc_decrypt_ok ( cipher, (test)->key, (test)->key_len, \ - (test)->iv, (test)->ciphertext, \ - (test)->plaintext, (test)->ciphertext_len ); \ - } while ( 0 ) - -/** CBC_AES128 */ -AES_CBC_TEST ( test_128, - KEY ( 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, - 0x88, 0x09, 0xcf, 0x4f, 0x3c ), - IV ( 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, - 0x0b, 0x0c, 0x0d, 0x0e, 0x0f ), - PLAINTEXT ( 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, - 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, - 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, - 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, - 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, - 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, - 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, - 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 ), - CIPHERTEXT ( 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, - 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d, - 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, - 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2, - 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, - 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16, - 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, - 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7 ) ); - -/** CBC_AES256 */ -AES_CBC_TEST ( test_256, - KEY ( 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, - 0xf0, 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, - 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 ), - IV ( 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, - 0x0b, 0x0c, 0x0d, 0x0e, 0x0f ), - PLAINTEXT ( 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, - 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, - 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, - 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, - 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, - 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, - 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, - 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 ), - CIPHERTEXT ( 0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, - 0x77, 0x9e, 0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6, - 0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, - 0x67, 0x9f, 0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d, - 0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, - 0xa5, 0x30, 0xe2, 0x63, 0x04, 0x23, 0x14, 0x61, - 0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, - 0xda, 0x6c, 0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b ) ); - -/** - * Perform AES-in-CBC-mode self-test - * - */ -static void aes_cbc_test_exec ( void ) { - struct cipher_algorithm *cipher = &aes_cbc_algorithm; - - /* Correctness tests */ - aes_cbc_ok ( &test_128 ); - aes_cbc_ok ( &test_256 ); - - /* Speed tests */ - DBG ( "AES128 encryption required %ld cycles per byte\n", - cbc_cost_encrypt ( cipher, test_128.key_len ) ); - DBG ( "AES128 decryption required %ld cycles per byte\n", - cbc_cost_decrypt ( cipher, test_128.key_len ) ); - DBG ( "AES256 encryption required %ld cycles per byte\n", - cbc_cost_encrypt ( cipher, test_256.key_len ) ); - DBG ( "AES256 decryption required %ld cycles per byte\n", - cbc_cost_decrypt ( cipher, test_256.key_len ) ); -} - -/** AES-in-CBC-mode self-test */ -struct self_test aes_cbc_test __self_test = { - .name = "aes_cbc", - .exec = aes_cbc_test_exec, -}; diff --git a/qemu/roms/ipxe/src/tests/aes_test.c b/qemu/roms/ipxe/src/tests/aes_test.c new file mode 100644 index 000000000..ad66c734c --- /dev/null +++ b/qemu/roms/ipxe/src/tests/aes_test.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2012 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * AES tests + * + * These test vectors are provided by NIST as part of the + * Cryptographic Toolkit Examples, downloadable from: + * + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/AES_Core_All.pdf + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/AES_ECB.pdf + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/AES_CBC.pdf + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include "cipher_test.h" + +/** Key used for NIST 128-bit test vectors */ +#define AES_KEY_NIST_128 \ + KEY ( 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, \ + 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c ) + +/** Key used for NIST 192-bit test vectors */ +#define AES_KEY_NIST_192 \ + KEY ( 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, \ + 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5, 0x62, 0xf8, \ + 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b ) + +/** Key used for NIST 256-bit test vectors */ +#define AES_KEY_NIST_256 \ + KEY ( 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, \ + 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35, \ + 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, \ + 0xa3, 0x09, 0x14, 0xdf, 0xf4 ) + +/** Dummy initialisation vector used for NIST ECB-mode test vectors */ +#define AES_IV_NIST_DUMMY \ + IV ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ) + +/** Initialisation vector used for NIST CBC-mode test vectors */ +#define AES_IV_NIST_CBC \ + IV ( 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \ + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f ) + +/** Plaintext used for NIST test vectors */ +#define AES_PLAINTEXT_NIST \ + PLAINTEXT ( 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, \ + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, \ + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, \ + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, \ + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, \ + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, \ + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, \ + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 ) + +/** AES-128-ECB (same test as AES-128-Core) */ +CIPHER_TEST ( aes_128_ecb, &aes_ecb_algorithm, + AES_KEY_NIST_128, AES_IV_NIST_DUMMY, AES_PLAINTEXT_NIST, + CIPHERTEXT ( 0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, + 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97, + 0xf5, 0xd3, 0xd5, 0x85, 0x03, 0xb9, 0x69, 0x9d, + 0xe7, 0x85, 0x89, 0x5a, 0x96, 0xfd, 0xba, 0xaf, + 0x43, 0xb1, 0xcd, 0x7f, 0x59, 0x8e, 0xce, 0x23, + 0x88, 0x1b, 0x00, 0xe3, 0xed, 0x03, 0x06, 0x88, + 0x7b, 0x0c, 0x78, 0x5e, 0x27, 0xe8, 0xad, 0x3f, + 0x82, 0x23, 0x20, 0x71, 0x04, 0x72, 0x5d, 0xd4 ) ); + +/** AES-128-CBC */ +CIPHER_TEST ( aes_128_cbc, &aes_cbc_algorithm, + AES_KEY_NIST_128, AES_IV_NIST_CBC, AES_PLAINTEXT_NIST, + CIPHERTEXT ( 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, + 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d, + 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, + 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2, + 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, + 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16, + 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, + 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7 ) ); + +/** AES-192-ECB (same test as AES-192-Core) */ +CIPHER_TEST ( aes_192_ecb, &aes_ecb_algorithm, + AES_KEY_NIST_192, AES_IV_NIST_DUMMY, AES_PLAINTEXT_NIST, + CIPHERTEXT ( 0xbd, 0x33, 0x4f, 0x1d, 0x6e, 0x45, 0xf2, 0x5f, + 0xf7, 0x12, 0xa2, 0x14, 0x57, 0x1f, 0xa5, 0xcc, + 0x97, 0x41, 0x04, 0x84, 0x6d, 0x0a, 0xd3, 0xad, + 0x77, 0x34, 0xec, 0xb3, 0xec, 0xee, 0x4e, 0xef, + 0xef, 0x7a, 0xfd, 0x22, 0x70, 0xe2, 0xe6, 0x0a, + 0xdc, 0xe0, 0xba, 0x2f, 0xac, 0xe6, 0x44, 0x4e, + 0x9a, 0x4b, 0x41, 0xba, 0x73, 0x8d, 0x6c, 0x72, + 0xfb, 0x16, 0x69, 0x16, 0x03, 0xc1, 0x8e, 0x0e ) ); + +/** AES-192-CBC */ +CIPHER_TEST ( aes_192_cbc, &aes_cbc_algorithm, + AES_KEY_NIST_192, AES_IV_NIST_CBC, AES_PLAINTEXT_NIST, + CIPHERTEXT ( 0x4f, 0x02, 0x1d, 0xb2, 0x43, 0xbc, 0x63, 0x3d, + 0x71, 0x78, 0x18, 0x3a, 0x9f, 0xa0, 0x71, 0xe8, + 0xb4, 0xd9, 0xad, 0xa9, 0xad, 0x7d, 0xed, 0xf4, + 0xe5, 0xe7, 0x38, 0x76, 0x3f, 0x69, 0x14, 0x5a, + 0x57, 0x1b, 0x24, 0x20, 0x12, 0xfb, 0x7a, 0xe0, + 0x7f, 0xa9, 0xba, 0xac, 0x3d, 0xf1, 0x02, 0xe0, + 0x08, 0xb0, 0xe2, 0x79, 0x88, 0x59, 0x88, 0x81, + 0xd9, 0x20, 0xa9, 0xe6, 0x4f, 0x56, 0x15, 0xcd ) ); + +/** AES-256-ECB (same test as AES-256-Core) */ +CIPHER_TEST ( aes_256_ecb, &aes_ecb_algorithm, + AES_KEY_NIST_256, AES_IV_NIST_DUMMY, AES_PLAINTEXT_NIST, + CIPHERTEXT ( 0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, + 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8, + 0x59, 0x1c, 0xcb, 0x10, 0xd4, 0x10, 0xed, 0x26, + 0xdc, 0x5b, 0xa7, 0x4a, 0x31, 0x36, 0x28, 0x70, + 0xb6, 0xed, 0x21, 0xb9, 0x9c, 0xa6, 0xf4, 0xf9, + 0xf1, 0x53, 0xe7, 0xb1, 0xbe, 0xaf, 0xed, 0x1d, + 0x23, 0x30, 0x4b, 0x7a, 0x39, 0xf9, 0xf3, 0xff, + 0x06, 0x7d, 0x8d, 0x8f, 0x9e, 0x24, 0xec, 0xc7 ) ); + +/** AES-256-CBC */ +CIPHER_TEST ( aes_256_cbc, &aes_cbc_algorithm, + AES_KEY_NIST_256, AES_IV_NIST_CBC, AES_PLAINTEXT_NIST, + CIPHERTEXT ( 0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, + 0x77, 0x9e, 0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6, + 0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, + 0x67, 0x9f, 0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d, + 0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, + 0xa5, 0x30, 0xe2, 0x63, 0x04, 0x23, 0x14, 0x61, + 0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, + 0xda, 0x6c, 0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b ) ); + +/** + * Perform AES self-test + * + */ +static void aes_test_exec ( void ) { + struct cipher_algorithm *ecb = &aes_ecb_algorithm; + struct cipher_algorithm *cbc = &aes_cbc_algorithm; + unsigned int keylen; + + /* Correctness tests */ + cipher_ok ( &aes_128_ecb ); + cipher_ok ( &aes_128_cbc ); + cipher_ok ( &aes_192_ecb ); + cipher_ok ( &aes_192_cbc ); + cipher_ok ( &aes_256_ecb ); + cipher_ok ( &aes_256_cbc ); + + /* Speed tests */ + for ( keylen = 128 ; keylen <= 256 ; keylen += 64 ) { + DBG ( "AES-%d-ECB encryption required %ld cycles per byte\n", + keylen, cipher_cost_encrypt ( ecb, ( keylen / 8 ) ) ); + DBG ( "AES-%d-ECB decryption required %ld cycles per byte\n", + keylen, cipher_cost_decrypt ( ecb, ( keylen / 8 ) ) ); + DBG ( "AES-%d-CBC encryption required %ld cycles per byte\n", + keylen, cipher_cost_encrypt ( cbc, ( keylen / 8 ) ) ); + DBG ( "AES-%d-CBC decryption required %ld cycles per byte\n", + keylen, cipher_cost_decrypt ( cbc, ( keylen / 8 ) ) ); + } +} + +/** AES self-test */ +struct self_test aes_test __self_test = { + .name = "aes", + .exec = aes_test_exec, +}; diff --git a/qemu/roms/ipxe/src/tests/base16_test.c b/qemu/roms/ipxe/src/tests/base16_test.c index 9b047b74c..46884aef7 100644 --- a/qemu/roms/ipxe/src/tests/base16_test.c +++ b/qemu/roms/ipxe/src/tests/base16_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -73,30 +77,42 @@ BASE16 ( random_test, * Report a base16 encoding test result * * @v test Base16 test + * @v file Test code file + * @v line Test code line */ -#define base16_encode_ok( test ) do { \ - size_t len = base16_encoded_len ( (test)->len ); \ - char buf[ len + 1 /* NUL */ ]; \ - ok ( len == strlen ( (test)->encoded ) ); \ - base16_encode ( (test)->data, (test)->len, buf ); \ - ok ( strcmp ( (test)->encoded, buf ) == 0 ); \ - } while ( 0 ) +static void base16_encode_okx ( struct base16_test *test, const char *file, + unsigned int line ) { + size_t len = base16_encoded_len ( test->len ); + char buf[ len + 1 /* NUL */ ]; + size_t check_len; + + okx ( len == strlen ( test->encoded ), file, line ); + check_len = base16_encode ( test->data, test->len, buf, sizeof ( buf )); + okx ( check_len == len, file, line ); + okx ( strcmp ( test->encoded, buf ) == 0, file, line ); +} +#define base16_encode_ok( test ) base16_encode_okx ( test, __FILE__, __LINE__ ) /** * Report a base16 decoding test result * * @v test Base16 test + * @v file Test code file + * @v line Test code line */ -#define base16_decode_ok( test ) do { \ - size_t max_len = base16_decoded_max_len ( (test)->encoded ); \ - uint8_t buf[max_len]; \ - int len; \ - len = base16_decode ( (test)->encoded, buf ); \ - ok ( len >= 0 ); \ - ok ( ( size_t ) len <= max_len ); \ - ok ( ( size_t ) len == (test)->len ); \ - ok ( memcmp ( (test)->data, buf, len ) == 0 ); \ - } while ( 0 ) +static void base16_decode_okx ( struct base16_test *test, const char *file, + unsigned int line ) { + size_t max_len = base16_decoded_max_len ( test->encoded ); + uint8_t buf[max_len]; + int len; + + len = base16_decode ( test->encoded, buf, sizeof ( buf ) ); + okx ( len >= 0, file, line ); + okx ( ( size_t ) len <= max_len, file, line ); + okx ( ( size_t ) len == test->len, file, line ); + okx ( memcmp ( test->data, buf, len ) == 0, file, line ); +} +#define base16_decode_ok( test ) base16_decode_okx ( test, __FILE__, __LINE__ ) /** * Perform Base16 self-tests diff --git a/qemu/roms/ipxe/src/tests/base64_test.c b/qemu/roms/ipxe/src/tests/base64_test.c index c088298ca..0fc595d90 100644 --- a/qemu/roms/ipxe/src/tests/base64_test.c +++ b/qemu/roms/ipxe/src/tests/base64_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -76,30 +80,42 @@ BASE64 ( random_test, * Report a base64 encoding test result * * @v test Base64 test + * @v file Test code file + * @v line Test code line */ -#define base64_encode_ok( test ) do { \ - size_t len = base64_encoded_len ( (test)->len ); \ - char buf[ len + 1 /* NUL */ ]; \ - ok ( len == strlen ( (test)->encoded ) ); \ - base64_encode ( (test)->data, (test)->len, buf ); \ - ok ( strcmp ( (test)->encoded, buf ) == 0 ); \ - } while ( 0 ) +static void base64_encode_okx ( struct base64_test *test, const char *file, + unsigned int line ) { + size_t len = base64_encoded_len ( test->len ); + char buf[ len + 1 /* NUL */ ]; + size_t check_len; + + okx ( len == strlen ( test->encoded ), file, line ); + check_len = base64_encode ( test->data, test->len, buf, sizeof ( buf )); + okx ( check_len == len, file, line ); + okx ( strcmp ( test->encoded, buf ) == 0, file, line ); +} +#define base64_encode_ok( test ) base64_encode_okx ( test, __FILE__, __LINE__ ) /** * Report a base64 decoding test result * * @v test Base64 test + * @v file Test code file + * @v line Test code line */ -#define base64_decode_ok( test ) do { \ - size_t max_len = base64_decoded_max_len ( (test)->encoded ); \ - uint8_t buf[max_len]; \ - int len; \ - len = base64_decode ( (test)->encoded, buf ); \ - ok ( len >= 0 ); \ - ok ( ( size_t ) len <= max_len ); \ - ok ( ( size_t ) len == (test)->len ); \ - ok ( memcmp ( (test)->data, buf, len ) == 0 ); \ - } while ( 0 ) +static void base64_decode_okx ( struct base64_test *test, const char *file, + unsigned int line ) { + size_t max_len = base64_decoded_max_len ( test->encoded ); + uint8_t buf[max_len]; + int len; + + len = base64_decode ( test->encoded, buf, sizeof ( buf ) ); + okx ( len >= 0, file, line ); + okx ( ( size_t ) len <= max_len, file, line ); + okx ( ( size_t ) len == test->len, file, line ); + okx ( memcmp ( test->data, buf, len ) == 0, file, line ); +} +#define base64_decode_ok( test ) base64_decode_okx ( test, __FILE__, __LINE__ ) /** * Perform Base64 self-tests diff --git a/qemu/roms/ipxe/src/tests/bigint_test.c b/qemu/roms/ipxe/src/tests/bigint_test.c index 75a80622f..8d40c3188 100644 --- a/qemu/roms/ipxe/src/tests/bigint_test.c +++ b/qemu/roms/ipxe/src/tests/bigint_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/tests/bofm_test.c b/qemu/roms/ipxe/src/tests/bofm_test.c index e430d12d4..829924887 100644 --- a/qemu/roms/ipxe/src/tests/bofm_test.c +++ b/qemu/roms/ipxe/src/tests/bofm_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/tests/byteswap_test.c b/qemu/roms/ipxe/src/tests/byteswap_test.c index a500218be..92bdb1d59 100644 --- a/qemu/roms/ipxe/src/tests/byteswap_test.c +++ b/qemu/roms/ipxe/src/tests/byteswap_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/tests/cbc_test.c b/qemu/roms/ipxe/src/tests/cbc_test.c deleted file mode 100644 index cb0f7bdea..000000000 --- a/qemu/roms/ipxe/src/tests/cbc_test.c +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2012 Michael Brown . - * - * 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 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -/** @file - * - * CBC self-tests - * - */ - -/* Forcibly enable assertions */ -#undef NDEBUG - -#include -#include -#include -#include -#include -#include -#include "cbc_test.h" - -/** Number of sample iterations for profiling */ -#define PROFILE_COUNT 16 - -/** - * Test CBC encryption - * - * @v cipher Cipher algorithm - * @v key Key - * @v key_len Length of key - * @v iv Initialisation vector - * @v plaintext Plaintext data - * @v expected_ciphertext Expected ciphertext data - * @v len Length of data - * @ret ok Ciphertext is as expected - */ -int cbc_test_encrypt ( struct cipher_algorithm *cipher, const void *key, - size_t key_len, const void *iv, const void *plaintext, - const void *expected_ciphertext, size_t len ) { - uint8_t ctx[cipher->ctxsize]; - uint8_t ciphertext[len]; - int rc; - - /* Initialise cipher */ - rc = cipher_setkey ( cipher, ctx, key, key_len ); - assert ( rc == 0 ); - cipher_setiv ( cipher, ctx, iv ); - - /* Perform encryption */ - cipher_encrypt ( cipher, ctx, plaintext, ciphertext, len ); - - /* Verify result */ - return ( memcmp ( ciphertext, expected_ciphertext, len ) == 0 ); -} - -/** - * Test CBC decryption - * - * @v cipher Cipher algorithm - * @v key Key - * @v key_len Length of key - * @v iv Initialisation vector - * @v ciphertext Ciphertext data - * @v expected_plaintext Expected plaintext data - * @v len Length of data - * @ret ok Plaintext is as expected - */ -int cbc_test_decrypt ( struct cipher_algorithm *cipher, const void *key, - size_t key_len, const void *iv, const void *ciphertext, - const void *expected_plaintext, size_t len ) { - uint8_t ctx[cipher->ctxsize]; - uint8_t plaintext[len]; - int rc; - - /* Initialise cipher */ - rc = cipher_setkey ( cipher, ctx, key, key_len ); - assert ( rc == 0 ); - cipher_setiv ( cipher, ctx, iv ); - - /* Perform encryption */ - cipher_decrypt ( cipher, ctx, ciphertext, plaintext, len ); - - /* Verify result */ - return ( memcmp ( plaintext, expected_plaintext, len ) == 0 ); -} - -/** - * Calculate CBC encryption or decryption cost - * - * @v cipher Cipher algorithm - * @v key_len Length of key - * @v op Encryption or decryption operation - * @ret cost Cost (in cycles per byte) - */ -static unsigned long cbc_cost ( struct cipher_algorithm *cipher, - size_t key_len, - void ( * op ) ( struct cipher_algorithm *cipher, - void *ctx, const void *src, - void *dst, size_t len ) ) { - static uint8_t random[8192]; /* Too large for stack */ - uint8_t key[key_len]; - uint8_t iv[cipher->blocksize]; - uint8_t ctx[cipher->ctxsize]; - struct profiler profiler; - unsigned long cost; - unsigned int i; - int rc; - - /* Fill buffer with pseudo-random data */ - srand ( 0x1234568 ); - for ( i = 0 ; i < sizeof ( random ) ; i++ ) - random[i] = rand(); - for ( i = 0 ; i < sizeof ( key ) ; i++ ) - key[i] = rand(); - for ( i = 0 ; i < sizeof ( iv ) ; i++ ) - iv[i] = rand(); - - /* Initialise cipher */ - rc = cipher_setkey ( cipher, ctx, key, key_len ); - assert ( rc == 0 ); - cipher_setiv ( cipher, ctx, iv ); - - /* Profile cipher operation */ - memset ( &profiler, 0, sizeof ( profiler ) ); - for ( i = 0 ; i < PROFILE_COUNT ; i++ ) { - profile_start ( &profiler ); - op ( cipher, ctx, random, random, sizeof ( random ) ); - profile_stop ( &profiler ); - } - - /* Round to nearest whole number of cycles per byte */ - cost = ( ( profile_mean ( &profiler ) + ( sizeof ( random ) / 2 ) ) / - sizeof ( random ) ); - - return cost; -} - -/** - * Calculate CBC encryption cost - * - * @v cipher Cipher algorithm - * @v key_len Length of key - * @ret cost Cost (in cycles per byte) - */ -unsigned long cbc_cost_encrypt ( struct cipher_algorithm *cipher, - size_t key_len ) { - return cbc_cost ( cipher, key_len, cipher_encrypt ); -} - -/** - * Calculate CBC decryption cost - * - * @v cipher Cipher algorithm - * @v key_len Length of key - * @ret cost Cost (in cycles per byte) - */ -unsigned long cbc_cost_decrypt ( struct cipher_algorithm *cipher, - size_t key_len ) { - return cbc_cost ( cipher, key_len, cipher_decrypt ); -} diff --git a/qemu/roms/ipxe/src/tests/cbc_test.h b/qemu/roms/ipxe/src/tests/cbc_test.h deleted file mode 100644 index ad9e6f341..000000000 --- a/qemu/roms/ipxe/src/tests/cbc_test.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef _CBC_TEST_H -#define _CBC_TEST_H - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include -#include -#include - -extern int cbc_test_encrypt ( struct cipher_algorithm *cipher, const void *key, - size_t key_len, const void *iv, - const void *plaintext, - const void *expected_ciphertext, size_t len ); -extern int cbc_test_decrypt ( struct cipher_algorithm *cipher, const void *key, - size_t key_len, const void *iv, - const void *ciphertext, - const void *expected_plaintext, size_t len ); -extern unsigned long cbc_cost_encrypt ( struct cipher_algorithm *cipher, - size_t key_len ); -extern unsigned long cbc_cost_decrypt ( struct cipher_algorithm *cipher, - size_t key_len ); - -/** - * Report CBC encryption test result - * - * @v cipher Cipher algorithm - * @v key Key - * @v key_len Length of key - * @v iv Initialisation vector - * @v plaintext Plaintext data - * @v expected_ciphertext Expected ciphertext data - * @v len Length of data - */ -#define cbc_encrypt_ok( cipher, key, key_len, iv, plaintext, \ - expected_ciphertext, len ) do { \ - ok ( cbc_test_encrypt ( cipher, key, key_len, iv, plaintext, \ - expected_ciphertext, len ) ); \ - } while ( 0 ) - -/** - * Report CBC decryption test result - * - * @v cipher Cipher algorithm - * @v key Key - * @v key_len Length of key - * @v iv Initialisation vector - * @v ciphertext Ciphertext data - * @v expected_plaintext Expected plaintext data - * @v len Length of data - */ -#define cbc_decrypt_ok( cipher, key, key_len, iv, ciphertext, \ - expected_plaintext, len ) do { \ - ok ( cbc_test_decrypt ( cipher, key, key_len, iv, ciphertext, \ - expected_plaintext, len ) ); \ - } while ( 0 ) - -#endif /* _CBC_TEST_H */ diff --git a/qemu/roms/ipxe/src/tests/cipher_test.c b/qemu/roms/ipxe/src/tests/cipher_test.c new file mode 100644 index 000000000..800d6c138 --- /dev/null +++ b/qemu/roms/ipxe/src/tests/cipher_test.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2012 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Cipher self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include +#include +#include +#include "cipher_test.h" + +/** Number of sample iterations for profiling */ +#define PROFILE_COUNT 16 + +/** + * Report a cipher encryption test result + * + * @v test Cipher test + * @v file Test code file + * @v line Test code line + */ +void cipher_encrypt_okx ( struct cipher_test *test, const char *file, + unsigned int line ) { + struct cipher_algorithm *cipher = test->cipher; + size_t len = test->len; + uint8_t ctx[cipher->ctxsize]; + uint8_t ciphertext[len]; + + /* Initialise cipher */ + okx ( cipher_setkey ( cipher, ctx, test->key, test->key_len ) == 0, + file, line ); + cipher_setiv ( cipher, ctx, test->iv ); + + /* Perform encryption */ + cipher_encrypt ( cipher, ctx, test->plaintext, ciphertext, len ); + + /* Compare against expected ciphertext */ + okx ( memcmp ( ciphertext, test->ciphertext, len ) == 0, file, line ); +} + +/** + * Report a cipher decryption test result + * + * @v test Cipher test + * @v file Test code file + * @v line Test code line + */ +void cipher_decrypt_okx ( struct cipher_test *test, const char *file, + unsigned int line ) { + struct cipher_algorithm *cipher = test->cipher; + size_t len = test->len; + uint8_t ctx[cipher->ctxsize]; + uint8_t plaintext[len]; + + /* Initialise cipher */ + okx ( cipher_setkey ( cipher, ctx, test->key, test->key_len ) == 0, + file, line ); + cipher_setiv ( cipher, ctx, test->iv ); + + /* Perform encryption */ + cipher_decrypt ( cipher, ctx, test->ciphertext, plaintext, len ); + + /* Compare against expected plaintext */ + okx ( memcmp ( plaintext, test->plaintext, len ) == 0, file, line ); +} + +/** + * Report a cipher encryption and decryption test result + * + * @v test Cipher test + * @v file Test code file + * @v line Test code line + */ +void cipher_okx ( struct cipher_test *test, const char *file, + unsigned int line ) { + + cipher_encrypt_okx ( test, file, line ); + cipher_decrypt_okx ( test, file, line ); +} + +/** + * Calculate cipher encryption or decryption cost + * + * @v cipher Cipher algorithm + * @v key_len Length of key + * @v op Encryption or decryption operation + * @ret cost Cost (in cycles per byte) + */ +static unsigned long +cipher_cost ( struct cipher_algorithm *cipher, size_t key_len, + void ( * op ) ( struct cipher_algorithm *cipher, void *ctx, + const void *src, void *dst, size_t len ) ) { + static uint8_t random[8192]; /* Too large for stack */ + uint8_t key[key_len]; + uint8_t iv[cipher->blocksize]; + uint8_t ctx[cipher->ctxsize]; + struct profiler profiler; + unsigned long cost; + unsigned int i; + int rc; + + /* Fill buffer with pseudo-random data */ + srand ( 0x1234568 ); + for ( i = 0 ; i < sizeof ( random ) ; i++ ) + random[i] = rand(); + for ( i = 0 ; i < sizeof ( key ) ; i++ ) + key[i] = rand(); + for ( i = 0 ; i < sizeof ( iv ) ; i++ ) + iv[i] = rand(); + + /* Initialise cipher */ + rc = cipher_setkey ( cipher, ctx, key, key_len ); + assert ( rc == 0 ); + cipher_setiv ( cipher, ctx, iv ); + + /* Profile cipher operation */ + memset ( &profiler, 0, sizeof ( profiler ) ); + for ( i = 0 ; i < PROFILE_COUNT ; i++ ) { + profile_start ( &profiler ); + op ( cipher, ctx, random, random, sizeof ( random ) ); + profile_stop ( &profiler ); + } + + /* Round to nearest whole number of cycles per byte */ + cost = ( ( profile_mean ( &profiler ) + ( sizeof ( random ) / 2 ) ) / + sizeof ( random ) ); + + return cost; +} + +/** + * Calculate cipher encryption cost + * + * @v cipher Cipher algorithm + * @v key_len Length of key + * @ret cost Cost (in cycles per byte) + */ +unsigned long cipher_cost_encrypt ( struct cipher_algorithm *cipher, + size_t key_len ) { + return cipher_cost ( cipher, key_len, cipher_encrypt ); +} + +/** + * Calculate cipher decryption cost + * + * @v cipher Cipher algorithm + * @v key_len Length of key + * @ret cost Cost (in cycles per byte) + */ +unsigned long cipher_cost_decrypt ( struct cipher_algorithm *cipher, + size_t key_len ) { + return cipher_cost ( cipher, key_len, cipher_decrypt ); +} diff --git a/qemu/roms/ipxe/src/tests/cipher_test.h b/qemu/roms/ipxe/src/tests/cipher_test.h new file mode 100644 index 000000000..d7c5aef8f --- /dev/null +++ b/qemu/roms/ipxe/src/tests/cipher_test.h @@ -0,0 +1,111 @@ +#ifndef _CIPHER_TEST_H +#define _CIPHER_TEST_H + +/** @file + * + * Cipher self-tests + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** A cipher test */ +struct cipher_test { + /** Cipher algorithm */ + struct cipher_algorithm *cipher; + /** Key */ + const void *key; + /** Length of key */ + size_t key_len; + /** Initialisation vector */ + const void *iv; + /** Length of initialisation vector */ + size_t iv_len; + /** Plaintext */ + const void *plaintext; + /** Ciphertext */ + const void *ciphertext; + /** Length of text */ + size_t len; +}; + +/** Define inline key */ +#define KEY(...) { __VA_ARGS__ } + +/** Define inline initialisation vector */ +#define IV(...) { __VA_ARGS__ } + +/** Define inline plaintext data */ +#define PLAINTEXT(...) { __VA_ARGS__ } + +/** Define inline ciphertext data */ +#define CIPHERTEXT(...) { __VA_ARGS__ } + +/** + * Define a cipher test + * + * @v name Test name + * @v CIPHER Cipher algorithm + * @v KEY Key + * @v IV Initialisation vector + * @v PLAINTEXT Plaintext + * @v CIPHERTEXT Ciphertext + * @ret test Cipher test + */ +#define CIPHER_TEST( name, CIPHER, KEY, IV, PLAINTEXT, CIPHERTEXT ) \ + static const uint8_t name ## _key [] = KEY; \ + static const uint8_t name ## _iv [] = IV; \ + static const uint8_t name ## _plaintext [] = PLAINTEXT; \ + static const uint8_t name ## _ciphertext \ + [ sizeof ( name ## _plaintext ) ] = CIPHERTEXT; \ + static struct cipher_test name = { \ + .cipher = CIPHER, \ + .key = name ## _key, \ + .key_len = sizeof ( name ## _key ), \ + .iv = name ## _iv, \ + .iv_len = sizeof ( name ## _iv ), \ + .plaintext = name ## _plaintext, \ + .ciphertext = name ## _ciphertext, \ + .len = sizeof ( name ## _plaintext ), \ + } + +extern void cipher_encrypt_okx ( struct cipher_test *test, const char *file, + unsigned int line ); +extern void cipher_decrypt_okx ( struct cipher_test *test, const char *file, + unsigned int line ); +extern void cipher_okx ( struct cipher_test *test, const char *file, + unsigned int line ); +extern unsigned long cipher_cost_encrypt ( struct cipher_algorithm *cipher, + size_t key_len ); +extern unsigned long cipher_cost_decrypt ( struct cipher_algorithm *cipher, + size_t key_len ); + +/** + * Report a cipher encryption test result + * + * @v test Cipher test + */ +#define cipher_encrypt_ok( test ) \ + cipher_encrypt_okx ( test, __FILE__, __LINE__ ) + +/** + * Report a cipher decryption test result + * + * @v test Cipher test + */ +#define cipher_decrypt_ok( test ) \ + cipher_decrypt_okx ( test, __FILE__, __LINE__ ) + +/** + * Report a cipher encryption and decryption test result + * + * @v test Cipher test + */ +#define cipher_ok( test ) \ + cipher_okx ( test, __FILE__, __LINE__ ) + +#endif /* _CIPHER_TEST_H */ diff --git a/qemu/roms/ipxe/src/tests/cms_test.c b/qemu/roms/ipxe/src/tests/cms_test.c index 8767504c0..b805a9974 100644 --- a/qemu/roms/ipxe/src/tests/cms_test.c +++ b/qemu/roms/ipxe/src/tests/cms_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -1470,6 +1474,7 @@ struct self_test cms_test __self_test = { }; /* Drag in algorithms required for tests */ +REQUIRING_SYMBOL ( cms_test ); REQUIRE_OBJECT ( rsa ); REQUIRE_OBJECT ( md5 ); REQUIRE_OBJECT ( sha1 ); diff --git a/qemu/roms/ipxe/src/tests/crc32_test.c b/qemu/roms/ipxe/src/tests/crc32_test.c index 873f633a5..46cde0f7b 100644 --- a/qemu/roms/ipxe/src/tests/crc32_test.c +++ b/qemu/roms/ipxe/src/tests/crc32_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/tests/deflate_test.c b/qemu/roms/ipxe/src/tests/deflate_test.c index 68c1aad96..20ff5b9a2 100644 --- a/qemu/roms/ipxe/src/tests/deflate_test.c +++ b/qemu/roms/ipxe/src/tests/deflate_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/tests/digest_test.c b/qemu/roms/ipxe/src/tests/digest_test.c index 4df26c099..c3a128853 100644 --- a/qemu/roms/ipxe/src/tests/digest_test.c +++ b/qemu/roms/ipxe/src/tests/digest_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -34,27 +38,47 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include "digest_test.h" +/** Maximum number of digest test fragments */ +#define NUM_DIGEST_TEST_FRAG 8 + +/** A digest test fragment list */ +struct digest_test_fragments { + /** Fragment lengths */ + size_t len[NUM_DIGEST_TEST_FRAG]; +}; + +/** Digest test fragment lists */ +static struct digest_test_fragments digest_test_fragments[] = { + { { 0, -1UL, } }, + { { 1, 1, 1, 1, 1, 1, 1, 1 } }, + { { 2, 0, 23, 4, 6, 1, 0 } }, +}; + /** Number of sample iterations for profiling */ #define PROFILE_COUNT 16 /** - * Test digest algorithm + * Report a digest fragmented test result * - * @v digest Digest algorithm - * @v fragments Digest test fragment list, or NULL - * @v data Test data - * @v len Length of test data - * @v expected Expected digest value - * @ret ok Digest value is as expected + * @v test Digest test + * @v fragments Fragment list + * @v file Test code file + * @v line Test code line */ -int digest_test ( struct digest_algorithm *digest, - struct digest_test_fragments *fragments, - void *data, size_t len, void *expected ) { +void digest_frag_okx ( struct digest_test *test, + struct digest_test_fragments *fragments, + const char *file, unsigned int line ) { + struct digest_algorithm *digest = test->digest; uint8_t ctx[digest->ctxsize]; uint8_t out[digest->digestsize]; + const void *data = test->data; + size_t len = test->len; size_t frag_len = 0; unsigned int i; + /* Sanity check */ + okx ( test->expected_len == sizeof ( out ), file, line ); + /* Initialise digest */ digest_init ( digest, ctx ); @@ -74,7 +98,28 @@ int digest_test ( struct digest_algorithm *digest, digest_final ( digest, ctx, out ); /* Compare against expected output */ - return ( memcmp ( expected, out, sizeof ( out ) ) == 0 ); + okx ( memcmp ( test->expected, out, sizeof ( out ) ) == 0, file, line ); +} + +/** + * Report a digest test result + * + * @v test Digest test + * @v file Test code file + * @v line Test code line + */ +void digest_okx ( struct digest_test *test, const char *file, + unsigned int line ) { + unsigned int i; + + /* Test with a single pass */ + digest_frag_okx ( test, NULL, file, line ); + + /* Test with fragment lists */ + for ( i = 0 ; i < ( sizeof ( digest_test_fragments ) / + sizeof ( digest_test_fragments[0] ) ) ; i++ ) { + digest_frag_okx ( test, &digest_test_fragments[i], file, line ); + } } /** diff --git a/qemu/roms/ipxe/src/tests/digest_test.h b/qemu/roms/ipxe/src/tests/digest_test.h index 49e06d1cb..abf1b834f 100644 --- a/qemu/roms/ipxe/src/tests/digest_test.h +++ b/qemu/roms/ipxe/src/tests/digest_test.h @@ -1,37 +1,115 @@ #ifndef _DIGEST_TEST_H #define _DIGEST_TEST_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -/** Maximum number of digest test fragments */ -#define NUM_DIGEST_TEST_FRAG 8 - -/** A digest test fragment list */ -struct digest_test_fragments { - /** Fragment lengths */ - size_t len[NUM_DIGEST_TEST_FRAG]; +/** A digest test */ +struct digest_test { + /** Digest algorithm */ + struct digest_algorithm *digest; + /** Test data */ + const void *data; + /** Length of test data */ + size_t len; + /** Expected digest value */ + const void *expected; + /** Expected digest length */ + size_t expected_len; }; -extern int digest_test ( struct digest_algorithm *digest, - struct digest_test_fragments *fragments, - void *data, size_t len, void *expected ); -extern unsigned long digest_cost ( struct digest_algorithm *digest ); +/** Define inline test data */ +#define DATA(...) { __VA_ARGS__ } + +/** Define inline expected digest value */ +#define DIGEST(...) { __VA_ARGS__ } + +/** + * Define a digest test + * + * @v name Test name + * @v DIGEST Digest algorithm + * @v DATA Test data + * @v EXPECTED Expected digest value + * @ret test Digest test + */ +#define DIGEST_TEST( name, DIGEST, DATA, EXPECTED ) \ + static const uint8_t name ## _data[] = DATA; \ + static const uint8_t name ## _expected[] = EXPECTED; \ + static struct digest_test name = { \ + .digest = DIGEST, \ + .data = name ## _data, \ + .len = sizeof ( name ## _data ), \ + .expected = name ## _expected, \ + .expected_len = sizeof ( name ## _expected ), \ + }; + +/** Standard test vector: empty data */ +#define DIGEST_EMPTY DATA() + +/** Standard test vector: NIST string "abc" + * + * The NIST Cryptographic Toolkit examples for all digest algorithms + * include a test vector which is the unterminated string + * + * "abc" + */ +#define DIGEST_NIST_ABC \ + DATA ( 0x61, 0x62, 0x63 ) + +/** Standard test vector: NIST string "abc...opq" + * + * The NIST Cryptographic Toolkit examples for all 32-bit digest + * algorithms (SHA-1 and the SHA-256 family) include a test vector + * which is the unterminated string + * + * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + */ +#define DIGEST_NIST_ABC_OPQ \ + DATA ( 0x61, 0x62, 0x63, 0x64, 0x62, 0x63, 0x64, 0x65, 0x63, \ + 0x64, 0x65, 0x66, 0x64, 0x65, 0x66, 0x67, 0x65, 0x66, \ + 0x67, 0x68, 0x66, 0x67, 0x68, 0x69, 0x67, 0x68, 0x69, \ + 0x6a, 0x68, 0x69, 0x6a, 0x6b, 0x69, 0x6a, 0x6b, 0x6c, \ + 0x6a, 0x6b, 0x6c, 0x6d, 0x6b, 0x6c, 0x6d, 0x6e, 0x6c, \ + 0x6d, 0x6e, 0x6f, 0x6d, 0x6e, 0x6f, 0x70, 0x6e, 0x6f, \ + 0x70, 0x71 ) + +/** Standard test vector: NIST string "abc...stu" + * + * The NIST Cryptographic Toolkit examples for all 64-bit digest + * algorithms (SHA-512 family) include a test vector which is the + * unterminated string + * + * "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" + * "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" + */ +#define DIGEST_NIST_ABC_STU \ + DATA ( 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x62, \ + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x63, 0x64, \ + 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x64, 0x65, 0x66, \ + 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x65, 0x66, 0x67, 0x68, \ + 0x69, 0x6a, 0x6b, 0x6c, 0x66, 0x67, 0x68, 0x69, 0x6a, \ + 0x6b, 0x6c, 0x6d, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, \ + 0x6d, 0x6e, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, \ + 0x6f, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, \ + 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x6b, \ + 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x6c, 0x6d, \ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x6d, 0x6e, 0x6f, \ + 0x70, 0x71, 0x72, 0x73, 0x74, 0x6e, 0x6f, 0x70, 0x71, \ + 0x72, 0x73, 0x74, 0x75 ) /** - * Report digest test result + * Report a digest test result * - * @v digest Digest algorithm - * @v fragments Digest test fragment list, or NULL - * @v data Test data - * @v len Length of test data - * @v expected Expected digest value + * @v test Digest test */ -#define digest_ok( digest, fragments, data, len, expected ) do { \ - ok ( digest_test ( digest, fragments, data, len, expected ) ); \ - } while ( 0 ) +#define digest_ok(test) digest_okx ( test, __FILE__, __LINE__ ) + +extern void digest_okx ( struct digest_test *test, const char *file, + unsigned int line ); +extern unsigned long digest_cost ( struct digest_algorithm *digest ); #endif /* _DIGEST_TEST_H */ diff --git a/qemu/roms/ipxe/src/tests/dns_test.c b/qemu/roms/ipxe/src/tests/dns_test.c index 52f5f19f2..f08e7810f 100644 --- a/qemu/roms/ipxe/src/tests/dns_test.c +++ b/qemu/roms/ipxe/src/tests/dns_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/tests/entropy_sample.c b/qemu/roms/ipxe/src/tests/entropy_sample.c index 95a662e3e..b45648c11 100644 --- a/qemu/roms/ipxe/src/tests/entropy_sample.c +++ b/qemu/roms/ipxe/src/tests/entropy_sample.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/tests/hash_df_test.c b/qemu/roms/ipxe/src/tests/hash_df_test.c index 74c8d0f4d..0b7d56ad7 100644 --- a/qemu/roms/ipxe/src/tests/hash_df_test.c +++ b/qemu/roms/ipxe/src/tests/hash_df_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/tests/hmac_drbg_test.c b/qemu/roms/ipxe/src/tests/hmac_drbg_test.c index 8cbf1cc8b..ddf9db2c5 100644 --- a/qemu/roms/ipxe/src/tests/hmac_drbg_test.c +++ b/qemu/roms/ipxe/src/tests/hmac_drbg_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/tests/ipv4_test.c b/qemu/roms/ipxe/src/tests/ipv4_test.c new file mode 100644 index 000000000..f84a8b81f --- /dev/null +++ b/qemu/roms/ipxe/src/tests/ipv4_test.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * IPv4 tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include + +/** Define inline IPv4 address */ +#define IPV4(a,b,c,d) \ + htonl ( ( (a) << 24 ) | ( (b) << 16 ) | ( (c) << 8 ) | (d) ) + +/** + * Report an inet_ntoa() test result + * + * @v addr IPv4 address + * @v text Expected textual representation + * @v file Test code file + * @v line Test code line + */ +static void inet_ntoa_okx ( uint32_t addr, const char *text, const char *file, + unsigned int line ) { + struct in_addr in = { .s_addr = addr }; + char *actual; + + /* Format address */ + actual = inet_ntoa ( in ); + DBG ( "inet_ntoa ( %d.%d.%d.%d ) = %s\n", + ( ( ntohl ( addr ) >> 24 ) & 0xff ), + ( ( ntohl ( addr ) >> 16 ) & 0xff ), + ( ( ntohl ( addr ) >> 8 ) & 0xff ), + ( ( ntohl ( addr ) >> 0 ) & 0xff ), actual ); + okx ( strcmp ( actual, text ) == 0, file, line ); +} +#define inet_ntoa_ok( addr, text ) \ + inet_ntoa_okx ( addr, text, __FILE__, __LINE__ ) + +/** + * Report an inet_aton() test result + * + * @v text Textual representation + * @v addr Expected IPv4 address + * @v file Test code file + * @v line Test code line + */ +static void inet_aton_okx ( const char *text, uint32_t addr, const char *file, + unsigned int line ) { + struct in_addr actual; + + /* Parse address */ + okx ( inet_aton ( text, &actual ) != 0, file, line ); + DBG ( "inet_aton ( \"%s\" ) = %s\n", text, inet_ntoa ( actual ) ); + okx ( actual.s_addr == addr, file, line ); +}; +#define inet_aton_ok( text, addr ) \ + inet_aton_okx ( text, addr, __FILE__, __LINE__ ) + +/** + * Report an inet_aton() failure test result + * + * @v text Textual representation + * @v file Test code file + * @v line Test code line + */ +static void inet_aton_fail_okx ( const char *text, const char *file, + unsigned int line ) { + struct in_addr actual; + + /* Attempt to parse address */ + okx ( inet_aton ( text, &actual ) == 0, file, line ); +} +#define inet_aton_fail_ok( text ) \ + inet_aton_fail_okx ( text, __FILE__, __LINE__ ) + +/** + * Perform IPv4 self-tests + * + */ +static void ipv4_test_exec ( void ) { + + /* Address testing macros */ + ok ( IN_IS_CLASSA ( IPV4 ( 10, 0, 0, 1 ) ) ); + ok ( ! IN_IS_CLASSB ( IPV4 ( 10, 0, 0, 1 ) ) ); + ok ( ! IN_IS_CLASSC ( IPV4 ( 10, 0, 0, 1 ) ) ); + ok ( ! IN_IS_CLASSA ( IPV4 ( 172, 16, 0, 1 ) ) ); + ok ( IN_IS_CLASSB ( IPV4 ( 172, 16, 0, 1 ) ) ); + ok ( ! IN_IS_CLASSC ( IPV4 ( 172, 16, 0, 1 ) ) ); + ok ( ! IN_IS_CLASSA ( IPV4 ( 192, 168, 0, 1 ) ) ); + ok ( ! IN_IS_CLASSB ( IPV4 ( 192, 168, 0, 1 ) ) ); + ok ( IN_IS_CLASSC ( IPV4 ( 192, 168, 0, 1 ) ) ); + ok ( ! IN_IS_MULTICAST ( IPV4 ( 127, 0, 0, 1 ) ) ); + ok ( ! IN_IS_MULTICAST ( IPV4 ( 8, 8, 8, 8 ) ) ); + ok ( ! IN_IS_MULTICAST ( IPV4 ( 0, 0, 0, 0 ) ) ); + ok ( ! IN_IS_MULTICAST ( IPV4 ( 223, 0, 0, 1 ) ) ); + ok ( ! IN_IS_MULTICAST ( IPV4 ( 240, 0, 0, 1 ) ) ); + ok ( IN_IS_MULTICAST ( IPV4 ( 224, 0, 0, 1 ) ) ); + ok ( IN_IS_MULTICAST ( IPV4 ( 231, 89, 0, 2 ) ) ); + ok ( IN_IS_MULTICAST ( IPV4 ( 239, 6, 1, 17 ) ) ); + + /* inet_ntoa() tests */ + inet_ntoa_ok ( IPV4 ( 127, 0, 0, 1 ), "127.0.0.1" ); + inet_ntoa_ok ( IPV4 ( 0, 0, 0, 0 ), "0.0.0.0" ); + inet_ntoa_ok ( IPV4 ( 255, 255, 255, 255 ), "255.255.255.255" ); + inet_ntoa_ok ( IPV4 ( 212, 13, 204, 60 ), "212.13.204.60" ); + + /* inet_aton() tests */ + inet_aton_ok ( "212.13.204.60", IPV4 ( 212, 13, 204, 60 ) ); + inet_aton_ok ( "127.0.0.1", IPV4 ( 127, 0, 0, 1 ) ); + + /* inet_aton() failure tests */ + inet_aton_fail_ok ( "256.0.0.1" ); /* Byte out of range */ + inet_aton_fail_ok ( "212.13.204.60.1" ); /* Too long */ + inet_aton_fail_ok ( "127.0.0" ); /* Too short */ + inet_aton_fail_ok ( "1.2.3.a" ); /* Invalid characters */ + inet_aton_fail_ok ( "127.0..1" ); /* Missing bytes */ +} + +/** IPv4 self-test */ +struct self_test ipv4_test __self_test = { + .name = "ipv4", + .exec = ipv4_test_exec, +}; diff --git a/qemu/roms/ipxe/src/tests/ipv6_test.c b/qemu/roms/ipxe/src/tests/ipv6_test.c index e16fc7c3d..772eb1b82 100644 --- a/qemu/roms/ipxe/src/tests/ipv6_test.c +++ b/qemu/roms/ipxe/src/tests/ipv6_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/tests/linebuf_test.c b/qemu/roms/ipxe/src/tests/linebuf_test.c index e06ac7d86..0dd486e9d 100644 --- a/qemu/roms/ipxe/src/tests/linebuf_test.c +++ b/qemu/roms/ipxe/src/tests/linebuf_test.c @@ -1,35 +1,320 @@ -#include +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Line buffer self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + #include -#include +#include #include +#include -static const char data1[] = -"Hello world\r\n" -"This is a reasonably nice set of lines\n" -"with not many different terminators\r\n\r\n" -"There should be exactly one blank line above\n" -"and this line should never appear at all since it has no terminator"; +/** Define inline raw data */ +#define DATA(...) { __VA_ARGS__ } -void linebuf_test ( void ) { - struct line_buffer linebuf; - const char *data = data1; - size_t len = ( sizeof ( data1 ) - 1 /* be mean; strip the NUL */ ); - ssize_t frag_len; - char *line; - - memset ( &linebuf, 0, sizeof ( linebuf ) ); - while ( len ) { - frag_len = line_buffer ( &linebuf, data, len ); - if ( frag_len < 0 ) { - printf ( "line_buffer() failed: %s\n", - strerror ( frag_len ) ); +/** Define inline lines */ +#define LINES(...) { __VA_ARGS__ } + +/** A line buffer test */ +struct linebuf_test { + /** Raw data */ + const void *data; + /** Length of raw data */ + size_t len; + /** Expected sequence of lines */ + const char **lines; + /** Number of expected lines */ + unsigned int count; +}; + +/** Line buffer test expected failure indicator */ +static const char linebuf_failure[1]; + +/** + * Define a line buffer test + * + * @v name Test name + * @v DATA Raw data + * @v LINES Expected sequence of lines + * @ret test Line buffer test + */ +#define LINEBUF_TEST( name, DATA, LINES ) \ + static const char name ## _data[] = DATA; \ + static const char * name ## _lines[] = LINES; \ + static struct linebuf_test name = { \ + .data = name ## _data, \ + .len = ( sizeof ( name ## _data ) - 1 /* NUL */ ), \ + .lines = name ## _lines, \ + .count = ( sizeof ( name ## _lines ) / \ + sizeof ( name ## _lines[0] ) ), \ + } + +/** Simple line buffer test */ +LINEBUF_TEST ( simple, + ( "HTTP/1.1 200 OK\r\n" + "Content-Length: 123\r\n" + "Content-Type: text/plain\r\n" + "\r\n" ), + LINES ( "HTTP/1.1 200 OK", + "Content-Length: 123", + "Content-Type: text/plain", + "" ) ); + +/** Mixed line terminators */ +LINEBUF_TEST ( mixed, + ( "LF only\n" "CRLF\r\n" "\n" "\n" "\r\n" "\r\n" "CR only\r" ), + LINES ( "LF only", "CRLF", "", "", "", "", + NULL /* \r should not be treated as a terminator */ ) ); + +/** Split consumption: part 1 */ +LINEBUF_TEST ( split_1, + ( "This line was" ), + LINES ( NULL ) ); + +/** Split consumption: part 2 */ +LINEBUF_TEST ( split_2, + ( " split across" ), + LINES ( NULL ) ); + +/** Split consumption: part 3 */ +LINEBUF_TEST ( split_3, + ( " multiple calls\r\nand so was this one\r" ), + LINES ( "This line was split across multiple calls", NULL ) ); + +/** Split consumption: part 4 */ +LINEBUF_TEST ( split_4, + ( "\nbut not this one\r\n" ), + LINES ( "and so was this one", "but not this one" ) ); + +/** Split consumption: part 5 */ +LINEBUF_TEST ( split_5, + ( "" ), + LINES ( NULL ) ); + +/** Split consumption: part 6 */ +LINEBUF_TEST ( split_6, + ( "This line came after a zero-length call\r\n" ), + LINES ( "This line came after a zero-length call" ) ); + +/** Embedded NULs */ +LINEBUF_TEST ( embedded_nuls, + ( "This\r\ntest\r\nincludes\r\n\r\nsome\0binary\0data\r\n" ), + LINES ( "This", "test", "includes", "", linebuf_failure ) ); + +/** + * Report line buffer initialisation test result + * + * @v linebuf Line buffer + * @v file Test code file + * @v line Test code line + */ +static void linebuf_init_okx ( struct line_buffer *linebuf, + const char *file, unsigned int line ) { + + /* Initialise line buffer */ + memset ( linebuf, 0, sizeof ( *linebuf ) ); + okx ( buffered_line ( linebuf ) == NULL, file, line ); +} +#define linebuf_init_ok( linebuf ) \ + linebuf_init_okx ( linebuf, __FILE__, __LINE__ ) + +/** + * Report line buffer consumption test result + * + * @v test Line buffer test + * @v linebuf Line buffer + * @v file Test code file + * @v line Test code line + */ +static void linebuf_consume_okx ( struct linebuf_test *test, + struct line_buffer *linebuf, + const char *file, unsigned int line ) { + const char *data = test->data; + size_t remaining = test->len; + int len; + unsigned int i; + const char *expected; + char *actual; + int rc; + + DBGC ( test, "LINEBUF %p:\n", test ); + DBGC_HDA ( test, 0, data, remaining ); + + /* Consume data one line at a time */ + for ( i = 0 ; i < test->count ; i++ ) { + + /* Add data to line buffer */ + len = line_buffer ( linebuf, data, remaining ); + + /* Get buffered line, if any */ + actual = buffered_line ( linebuf ); + if ( len < 0 ) { + rc = len; + DBGC ( test, "LINEBUF %p %s\n", test, strerror ( rc ) ); + } else if ( actual != NULL ) { + DBGC ( test, "LINEBUF %p \"%s\" (consumed %d)\n", + test, actual, len ); + } else { + DBGC ( test, "LINEBUF %p unterminated (consumed %d)\n", + test, len ); + } + + /* Check for success/failure */ + expected = test->lines[i]; + if ( expected == linebuf_failure ) { + rc = len; + okx ( rc < 0, file, line ); + okx ( remaining > 0, file, line ); return; } - data += frag_len; - len -= frag_len; - if ( ( line = buffered_line ( &linebuf ) ) ) - printf ( "\"%s\"\n", line ); + okx ( len >= 0, file, line ); + okx ( ( ( size_t ) len ) <= remaining, file, line ); + + /* Check expected result */ + if ( expected == NULL ) { + okx ( actual == NULL, file, line ); + } else { + okx ( actual != NULL, file, line ); + okx ( strcmp ( actual, expected ) == 0, file, line ); + } + + /* Consume data */ + data += len; + remaining -= len; + } + + /* Check that all data was consumed */ + okx ( remaining == 0, file, line ); +} +#define linebuf_consume_ok( test, linebuf ) \ + linebuf_consume_okx ( test, linebuf, __FILE__, __LINE__ ) + +/** + * Report line buffer accumulation test result + * + * @v test Line buffer test + * @v linebuf Line buffer + * @v file Test code file + * @v line Test code line + */ +static void linebuf_accumulated_okx ( struct linebuf_test *test, + struct line_buffer *linebuf, + const char *file, unsigned int line ) { + const char *actual; + const char *expected; + unsigned int i; + + /* Check each accumulated line */ + actual = linebuf->data; + for ( i = 0 ; i < test->count ; i++ ) { + + /* Check accumulated line */ + okx ( actual != NULL, file, line ); + okx ( actual >= linebuf->data, file, line ); + expected = test->lines[i]; + if ( ( expected == NULL ) || ( expected == linebuf_failure ) ) + return; + okx ( strcmp ( actual, expected ) == 0, file, line ); + + /* Move to next line */ + actual += ( strlen ( actual ) + 1 /* NUL */ ); + okx ( actual <= ( linebuf->data + linebuf->len ), file, line ); } +} +#define linebuf_accumulated_ok( test, linebuf ) \ + linebuf_accumulated_okx ( test, linebuf, __FILE__, __LINE__ ) + +/** + * Report line buffer emptying test result + * + * @v linebuf Line buffer + * @v file Test code file + * @v line Test code line + */ +static void linebuf_empty_okx ( struct line_buffer *linebuf, + const char *file, unsigned int line ) { - empty_line_buffer ( &linebuf ); + /* Empty line buffer */ + empty_line_buffer ( linebuf ); + okx ( buffered_line ( linebuf ) == NULL, file, line ); } +#define linebuf_empty_ok( linebuf ) \ + linebuf_empty_okx ( linebuf, __FILE__, __LINE__ ) + +/** + * Report line buffer combined test result + * + * @v test Line buffer test + * @v file Test code file + * @v line Test code line + */ +static void linebuf_okx ( struct linebuf_test *test, const char *file, + unsigned int line ) { + struct line_buffer linebuf; + + linebuf_init_okx ( &linebuf, file, line ); + linebuf_consume_okx ( test, &linebuf, file, line ); + linebuf_accumulated_okx ( test, &linebuf, file, line ); + linebuf_empty_okx ( &linebuf, file, line ); +} +#define linebuf_ok( test ) \ + linebuf_okx ( test, __FILE__, __LINE__ ) + +/** + * Perform line buffer self-tests + * + */ +static void linebuf_test_exec ( void ) { + struct line_buffer linebuf; + + /* Basic tests */ + linebuf_ok ( &simple ); + linebuf_ok ( &mixed ); + + /* Split consumption test */ + linebuf_init_ok ( &linebuf ); + linebuf_consume_ok ( &split_1, &linebuf ); + linebuf_consume_ok ( &split_2, &linebuf ); + linebuf_consume_ok ( &split_3, &linebuf ); + linebuf_consume_ok ( &split_4, &linebuf ); + linebuf_consume_ok ( &split_5, &linebuf ); + linebuf_consume_ok ( &split_6, &linebuf ); + linebuf_empty_ok ( &linebuf ); + + /* Embedded NULs */ + linebuf_ok ( &embedded_nuls ); +} + +/** Line buffer self-test */ +struct self_test linebuf_test __self_test = { + .name = "linebuf", + .exec = linebuf_test_exec, +}; diff --git a/qemu/roms/ipxe/src/tests/list_test.c b/qemu/roms/ipxe/src/tests/list_test.c index 35cbd5e5f..352c87da0 100644 --- a/qemu/roms/ipxe/src/tests/list_test.c +++ b/qemu/roms/ipxe/src/tests/list_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/tests/math_test.c b/qemu/roms/ipxe/src/tests/math_test.c index e12b7939d..1a244f1eb 100644 --- a/qemu/roms/ipxe/src/tests/math_test.c +++ b/qemu/roms/ipxe/src/tests/math_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -34,6 +38,26 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include +/** + * Force a call to the non-constant implementation of ffsl() + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +__attribute__ (( noinline )) int ffsl_var ( long value ) { + return ffsl ( value ); +} + +/** + * Force a call to the non-constant implementation of ffsll() + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +__attribute__ (( noinline )) int ffsll_var ( long long value ) { + return ffsll ( value ); +} + /** * Force a call to the non-constant implementation of flsl() * @@ -172,6 +196,44 @@ __attribute__ (( noinline )) int64_t s64mod_var ( int64_t dividend, return check_divmod ( dividend, divisor, % ); } +/** + * Report a ffsl() test result + * + * @v value Value + * @v lsb Expected LSB + * @v file Test code file + * @v line Test code line + */ +static inline __attribute__ (( always_inline )) void +ffsl_okx ( long value, int lsb, const char *file, unsigned int line ) { + + /* Verify as a constant (requires to be inlined) */ + okx ( ffsl ( value ) == lsb, file, line ); + + /* Verify as a non-constant */ + okx ( ffsl_var ( value ) == lsb, file, line ); +} +#define ffsl_ok( value, lsb ) ffsl_okx ( value, lsb, __FILE__, __LINE__ ) + +/** + * Report a ffsll() test result + * + * @v value Value + * @v lsb Expected LSB + * @v file Test code file + * @v line Test code line + */ +static inline __attribute__ (( always_inline )) void +ffsll_okx ( long long value, int lsb, const char *file, unsigned int line ) { + + /* Verify as a constant (requires to be inlined) */ + okx ( ffsll ( value ) == lsb, file, line ); + + /* Verify as a non-constant */ + okx ( ffsll_var ( value ) == lsb, file, line ); +} +#define ffsll_ok( value, lsb ) ffsll_okx ( value, lsb, __FILE__, __LINE__ ) + /** * Report a flsl() test result * @@ -270,6 +332,22 @@ static void s64divmod_okx ( int64_t dividend, int64_t divisor, */ static void math_test_exec ( void ) { + /* Test ffsl() */ + ffsl_ok ( 0, 0 ); + ffsl_ok ( 1, 1 ); + ffsl_ok ( 255, 1 ); + ffsl_ok ( 256, 9 ); + ffsl_ok ( 257, 1 ); + ffsl_ok ( 0x54850596, 2 ); + ffsl_ok ( 0x80000000, 32 ); + + /* Test ffsll() */ + ffsll_ok ( 0, 0 ); + ffsll_ok ( 1, 1 ); + ffsll_ok ( 0x6d63623330ULL, 5 ); + ffsll_ok ( 0x80000000UL, 32 ); + ffsll_ok ( 0x8000000000000000ULL, 64 ); + /* Test flsl() */ flsl_ok ( 0, 0 ); flsl_ok ( 1, 1 ); diff --git a/qemu/roms/ipxe/src/tests/md5_test.c b/qemu/roms/ipxe/src/tests/md5_test.c index ba5f24c3e..e9ed2716a 100644 --- a/qemu/roms/ipxe/src/tests/md5_test.c +++ b/qemu/roms/ipxe/src/tests/md5_test.c @@ -15,82 +15,58 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * * MD5 tests * + * Test inputs borrowed from NIST SHA-1 tests, with results calculated + * using md5sum. */ -#include +/* Forcibly enable assertions */ +#undef NDEBUG + #include #include #include "digest_test.h" -/** An MD5 test vector */ -struct md5_test_vector { - /** Test data */ - void *data; - /** Test data length */ - size_t len; - /** Expected digest */ - uint8_t digest[MD5_DIGEST_SIZE]; -}; +/* Empty test vector (digest obtained from "md5sum /dev/null") */ +DIGEST_TEST ( md5_empty, &md5_algorithm, DIGEST_EMPTY, + DIGEST ( 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, 0xe9, + 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e ) ); -/** MD5 test vectors */ -static struct md5_test_vector md5_test_vectors[] = { - /* Test inputs borrowed from SHA-1 tests, with results - * calculated using md5sum. - */ - { NULL, 0, - { 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, - 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e } }, - { "abc", 3, - { 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, - 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 } }, - { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56, - { 0x82, 0x15, 0xef, 0x07, 0x96, 0xa2, 0x0b, 0xca, - 0xaa, 0xe1, 0x16, 0xd3, 0x87, 0x6c, 0x66, 0x4a } }, -}; +/* NIST test vector "abc" (digest obtained from "md5sum ") */ +DIGEST_TEST ( md5_nist_abc, &md5_algorithm, DIGEST_NIST_ABC, + DIGEST ( 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, + 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 ) ); -/** MD5 test fragment lists */ -static struct digest_test_fragments md5_test_fragments[] = { - { { 0, -1UL, } }, - { { 1, 1, 1, 1, 1, 1, 1, 1 } }, - { { 2, 0, 23, 4, 6, 1, 0 } }, -}; +/* NIST test vector "abc...opq" (digest obtained from "md5sum ") */ +DIGEST_TEST ( md5_nist_abc_opq, &md5_algorithm, DIGEST_NIST_ABC_OPQ, + DIGEST ( 0x82, 0x15, 0xef, 0x07, 0x96, 0xa2, 0x0b, 0xca, 0xaa, + 0xe1, 0x16, 0xd3, 0x87, 0x6c, 0x66, 0x4a ) ); /** * Perform MD5 self-test * */ static void md5_test_exec ( void ) { - struct digest_algorithm *digest = &md5_algorithm; - struct md5_test_vector *test; - unsigned long cost; - unsigned int i; - unsigned int j; - /* Correctness test */ - for ( i = 0 ; i < ( sizeof ( md5_test_vectors ) / - sizeof ( md5_test_vectors[0] ) ) ; i++ ) { - test = &md5_test_vectors[i]; - /* Test with a single pass */ - digest_ok ( digest, NULL, test->data, test->len, test->digest ); - /* Test with fragment lists */ - for ( j = 0 ; j < ( sizeof ( md5_test_fragments ) / - sizeof ( md5_test_fragments[0] ) ) ; j++ ){ - digest_ok ( digest, &md5_test_fragments[j], - test->data, test->len, test->digest ); - } - } + /* Correctness tests */ + digest_ok ( &md5_empty ); + digest_ok ( &md5_nist_abc ); + digest_ok ( &md5_nist_abc_opq ); - /* Speed test */ - cost = digest_cost ( digest ); - DBG ( "MD5 required %ld cycles per byte\n", cost ); + /* Speed tests */ + DBG ( "MD5 required %ld cycles per byte\n", + digest_cost ( &md5_algorithm ) ); } /** MD5 self-test */ diff --git a/qemu/roms/ipxe/src/tests/memcpy_test.c b/qemu/roms/ipxe/src/tests/memcpy_test.c index f1e5503a6..0247c71d4 100644 --- a/qemu/roms/ipxe/src/tests/memcpy_test.c +++ b/qemu/roms/ipxe/src/tests/memcpy_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/tests/memset_test.c b/qemu/roms/ipxe/src/tests/memset_test.c new file mode 100644 index 000000000..d96f83fa6 --- /dev/null +++ b/qemu/roms/ipxe/src/tests/memset_test.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * memset() self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include + +/* Provide global functions to allow inspection of generated code */ + +void memset_zero_0 ( void *dest ) { memset ( dest, 0, 0 ); } +void memset_zero_1 ( void *dest ) { memset ( dest, 0, 1 ); } +void memset_zero_2 ( void *dest ) { memset ( dest, 0, 2 ); } +void memset_zero_3 ( void *dest ) { memset ( dest, 0, 3 ); } +void memset_zero_4 ( void *dest ) { memset ( dest, 0, 4 ); } +void memset_zero_5 ( void *dest ) { memset ( dest, 0, 5 ); } +void memset_zero_6 ( void *dest ) { memset ( dest, 0, 6 ); } +void memset_zero_7 ( void *dest ) { memset ( dest, 0, 7 ); } +void memset_zero_8 ( void *dest ) { memset ( dest, 0, 8 ); } +void memset_zero_9 ( void *dest ) { memset ( dest, 0, 9 ); } +void memset_zero_10 ( void *dest ) { memset ( dest, 0, 10 ); } +void memset_zero_11 ( void *dest ) { memset ( dest, 0, 11 ); } +void memset_zero_12 ( void *dest ) { memset ( dest, 0, 12 ); } +void memset_zero_13 ( void *dest ) { memset ( dest, 0, 13 ); } +void memset_zero_14 ( void *dest ) { memset ( dest, 0, 14 ); } +void memset_zero_15 ( void *dest ) { memset ( dest, 0, 15 ); } +void memset_zero_16 ( void *dest ) { memset ( dest, 0, 16 ); } +void memset_zero_17 ( void *dest ) { memset ( dest, 0, 17 ); } +void memset_zero_18 ( void *dest ) { memset ( dest, 0, 18 ); } +void memset_zero_19 ( void *dest ) { memset ( dest, 0, 19 ); } +void memset_zero_20 ( void *dest ) { memset ( dest, 0, 20 ); } +void memset_zero_21 ( void *dest ) { memset ( dest, 0, 21 ); } +void memset_zero_22 ( void *dest ) { memset ( dest, 0, 22 ); } +void memset_zero_23 ( void *dest ) { memset ( dest, 0, 23 ); } +void memset_zero_24 ( void *dest ) { memset ( dest, 0, 24 ); } +void memset_zero_25 ( void *dest ) { memset ( dest, 0, 25 ); } +void memset_zero_26 ( void *dest ) { memset ( dest, 0, 26 ); } +void memset_zero_27 ( void *dest ) { memset ( dest, 0, 27 ); } +void memset_zero_28 ( void *dest ) { memset ( dest, 0, 28 ); } +void memset_zero_29 ( void *dest ) { memset ( dest, 0, 29 ); } +void memset_zero_30 ( void *dest ) { memset ( dest, 0, 30 ); } +void memset_zero_31 ( void *dest ) { memset ( dest, 0, 31 ); } + +/** + * Force a call to the variable-length implementation of memset() + * + * @v dest Destination address + * @v fill Fill pattern + * @v len Length of data + * @ret dest Destination address + */ +__attribute__ (( noinline )) void * memset_var ( void *dest, unsigned int fill, + size_t len ) { + return memset ( dest, fill, len ); +} + +/** + * Perform a constant-length memset() test + * + * @v len Length of data + */ +#define MEMSET_TEST_CONSTANT( len ) do { \ + uint8_t dest_const[ 1 + len + 1 ]; \ + uint8_t dest_var[ 1 + len + 1 ]; \ + static uint8_t zero[len]; \ + unsigned int i; \ + \ + for ( i = 0 ; i < sizeof ( dest_const ) ; i++ ) \ + dest_const[i] = 0xaa; \ + memset ( ( dest_const + 1 ), 0, len ); \ + ok ( dest_const[0] == 0xaa ); \ + ok ( dest_const[ sizeof ( dest_const ) - 1 ] == 0xaa ); \ + ok ( memcmp ( ( dest_const + 1 ), zero, len ) == 0 ); \ + \ + for ( i = 0 ; i < sizeof ( dest_var ) ; i++ ) \ + dest_var[i] = 0xbb; \ + memset_var ( ( dest_var + 1 ), 0, len ); \ + ok ( dest_var[0] == 0xbb ); \ + ok ( dest_var[ sizeof ( dest_var ) - 1 ] == 0xbb ); \ + ok ( memcmp ( ( dest_var + 1 ), zero, len ) == 0 ); \ + } while ( 0 ) + +/** + * Perform memset() self-tests + * + */ +static void memset_test_exec ( void ) { + + /* Constant-length tests */ + MEMSET_TEST_CONSTANT ( 0 ); + MEMSET_TEST_CONSTANT ( 1 ); + MEMSET_TEST_CONSTANT ( 2 ); + MEMSET_TEST_CONSTANT ( 3 ); + MEMSET_TEST_CONSTANT ( 4 ); + MEMSET_TEST_CONSTANT ( 5 ); + MEMSET_TEST_CONSTANT ( 6 ); + MEMSET_TEST_CONSTANT ( 7 ); + MEMSET_TEST_CONSTANT ( 8 ); + MEMSET_TEST_CONSTANT ( 9 ); + MEMSET_TEST_CONSTANT ( 10 ); + MEMSET_TEST_CONSTANT ( 11 ); + MEMSET_TEST_CONSTANT ( 12 ); + MEMSET_TEST_CONSTANT ( 13 ); + MEMSET_TEST_CONSTANT ( 14 ); + MEMSET_TEST_CONSTANT ( 15 ); + MEMSET_TEST_CONSTANT ( 16 ); + MEMSET_TEST_CONSTANT ( 17 ); + MEMSET_TEST_CONSTANT ( 18 ); + MEMSET_TEST_CONSTANT ( 19 ); + MEMSET_TEST_CONSTANT ( 20 ); + MEMSET_TEST_CONSTANT ( 21 ); + MEMSET_TEST_CONSTANT ( 22 ); + MEMSET_TEST_CONSTANT ( 23 ); + MEMSET_TEST_CONSTANT ( 24 ); + MEMSET_TEST_CONSTANT ( 25 ); + MEMSET_TEST_CONSTANT ( 26 ); + MEMSET_TEST_CONSTANT ( 27 ); + MEMSET_TEST_CONSTANT ( 28 ); + MEMSET_TEST_CONSTANT ( 29 ); + MEMSET_TEST_CONSTANT ( 30 ); + MEMSET_TEST_CONSTANT ( 31 ); +} + +/** memset() self-test */ +struct self_test memset_test __self_test = { + .name = "memset", + .exec = memset_test_exec, +}; diff --git a/qemu/roms/ipxe/src/tests/ocsp_test.c b/qemu/roms/ipxe/src/tests/ocsp_test.c index a318c185a..c6d458596 100644 --- a/qemu/roms/ipxe/src/tests/ocsp_test.c +++ b/qemu/roms/ipxe/src/tests/ocsp_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -1857,5 +1861,6 @@ struct self_test ocsp_test __self_test = { }; /* Drag in algorithms required for tests */ +REQUIRING_SYMBOL ( ocsp_test ); REQUIRE_OBJECT ( rsa ); REQUIRE_OBJECT ( sha1 ); diff --git a/qemu/roms/ipxe/src/tests/pccrc_test.c b/qemu/roms/ipxe/src/tests/pccrc_test.c new file mode 100644 index 000000000..f4ab573ac --- /dev/null +++ b/qemu/roms/ipxe/src/tests/pccrc_test.c @@ -0,0 +1,529 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Peer Content Caching and Retrieval: Content Identification [MS-PCCRC] tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** Define inline raw data */ +#define DATA(...) { __VA_ARGS__ } + +/** + * Define an inline content range + * + * @v START Start offset + * @v END End offset + * @ret range Content range + */ +#define RANGE( START, END ) { .start = START, .end = END } + +/** + * Define an inline trimmed content range + * + * @v START Start offset + * @v END End offset + * @ret trim Trimmed content range + */ +#define TRIM( START, END ) { .start = START, .end = END } + +/** A content information test */ +struct peerdist_info_test { + /** Raw content information */ + const void *data; + /** Length of raw content information */ + size_t len; + /** Expected digest algorithm */ + struct digest_algorithm *expected_digest; + /** Expected digest size */ + size_t expected_digestsize; + /** Expected content range */ + struct peerdist_range expected_range; + /** Expected trimmed content range */ + struct peerdist_range expected_trim; + /** Expected number of segments */ + unsigned int expected_segments; +}; + +/** + * Define a content information test + * + * @v name Test name + * @v DATA Raw content information + * @v DIGEST Expected digest algorithm + * @v DIGESTSIZE Expected digest size + * @v RANGE Expected content range + * @v TRIM Expected trimmer content range + * @v SEGMENTS Expected number of segments + * @ret test Content information test + * + * Raw content information can be obtained from PeerDist-capable web + * servers using wget's "--header" option to inject the relevant + * PeerDist headers. For example: + * + * wget --header "Accept-Encoding: peerdist" \ + * --header "X-P2P-PeerDist: Version=1.0" \ + * http://peerdist.server.address/test.url -O - | xxd -i -c 11 + * + * Version 1 content information can be retrieved using the headers: + * + * Accept-Encoding: peerdist + * X-P2P-PeerDist: Version=1.0 + * + * Version 2 content information can be retrieved (from compatible + * servers) using the headers: + * + * Accept-Encoding: peerdist + * X-P2P-PeerDist: Version=1.1 + * X-P2P-PeerDistEx: MinContentInformation=2.0, MaxContentInformation=2.0 + */ +#define PEERDIST_INFO_TEST( name, DATA, DIGEST, DIGESTSIZE, RANGE, \ + TRIM, SEGMENTS ) \ + static const uint8_t name ## _data[] = DATA; \ + static struct peerdist_info_test name = { \ + .data = name ## _data, \ + .len = sizeof ( name ## _data ), \ + .expected_digest = DIGEST, \ + .expected_digestsize = DIGESTSIZE, \ + .expected_range = RANGE, \ + .expected_trim = TRIM, \ + .expected_segments = SEGMENTS, \ + } + +/** A content information segment test */ +struct peerdist_info_segment_test { + /** Segment index */ + unsigned int index; + /** Expected content range */ + struct peerdist_range expected_range; + /** Expected number of blocks */ + unsigned int expected_blocks; + /** Expected block size */ + size_t expected_blksize; + /** Expected segment hash of data */ + uint8_t expected_hash[PEERDIST_DIGEST_MAX_SIZE]; + /** Expected segment secret */ + uint8_t expected_secret[PEERDIST_DIGEST_MAX_SIZE]; + /** Expected segment identifier */ + uint8_t expected_id[PEERDIST_DIGEST_MAX_SIZE]; +}; + +/** + * Define a content information segment test + * + * @v name Test name + * @v INDEX Segment index + * @v RANGE Expected content range + * @v BLOCKS Expected number of blocks + * @v BLKSIZE Expected block size + * @v HASH Expected segment hash of data + * @v SECRET Expected segment secret + * @v ID Expected segment identifier + * @ret test Content information segment test + */ +#define PEERDIST_INFO_SEGMENT_TEST( name, INDEX, RANGE, BLOCKS, \ + BLKSIZE, HASH, SECRET, ID ) \ + static struct peerdist_info_segment_test name = { \ + .index = INDEX, \ + .expected_range = RANGE, \ + .expected_blocks = BLOCKS, \ + .expected_blksize = BLKSIZE, \ + .expected_hash = HASH, \ + .expected_secret = SECRET, \ + .expected_id = ID, \ + } + +/** A content information block test */ +struct peerdist_info_block_test { + /** Block index */ + unsigned int index; + /** Expected content range */ + struct peerdist_range expected_range; + /** Expected trimmed content range */ + struct peerdist_range expected_trim; + /** Expected hash of data */ + uint8_t expected_hash[PEERDIST_DIGEST_MAX_SIZE]; +}; + +/** + * Define a content information block test + * + * @v name Test name + * @v INDEX Block index + * @v RANGE Expected content range + * @v TRIM Expected trimmed content range + * @v HASH Expected hash of data + * @ret test Content information block test + */ +#define PEERDIST_INFO_BLOCK_TEST( name, INDEX, RANGE, TRIM, HASH ) \ + static struct peerdist_info_block_test name = { \ + .index = INDEX, \ + .expected_range = RANGE, \ + .expected_trim = TRIM, \ + .expected_hash = HASH, \ + } + +/** + * Define a server passphrase + * + * @v name Server passphrase name + * @v DATA Raw server passphrase + * + * The server passphrase can be exported from a Windows BranchCache + * server using the command: + * + * netsh branchcache exportkey exported.key somepassword + * + * and this encrypted exported key can be decrypted using the + * oSSL_key_dx or mcrypt_key_dx utilities found in the (prototype) + * Prequel project at https://fedorahosted.org/prequel/ : + * + * oSSL_key_dx exported.key somepassword + * or + * mcrypt_key_dx exported.key somepassword + * + * Either command will display both the server passphrase and the + * "Server Secret". Note that this latter is the version 1 server + * secret (i.e. the SHA-256 of the server passphrase); the + * corresponding version 2 server secret can be obtained by + * calculating the truncated SHA-512 of the server passphrase. + * + * We do not know the server passphrase during normal operation. We + * use it in the self-tests only to check for typos and other errors + * in the test vectors, by checking that the segment secret defined in + * a content information segment test is as expected. + */ +#define SERVER_PASSPHRASE( name, DATA ) \ + static uint8_t name[] = DATA + +/** Server passphrase used for these test vectors */ +SERVER_PASSPHRASE ( passphrase, + DATA ( 0x2a, 0x3d, 0x73, 0xeb, 0x43, 0x5e, 0x9f, 0x2b, 0x8a, 0x34, 0x42, + 0x67, 0xe7, 0x46, 0x7a, 0x3c, 0x73, 0x85, 0xc6, 0xe0, 0x55, 0xe2, + 0xb4, 0xd3, 0x0d, 0xfe, 0xc7, 0xc3, 0x8b, 0x0e, 0xd7, 0x2c ) ); + +/** IIS logo (iis-85.png) content information version 1 */ +PEERDIST_INFO_TEST ( iis_85_png_v1, + DATA ( 0x00, 0x01, 0x0c, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x85, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0xd8, 0xd9, 0x76, 0x35, 0x4a, 0x48, 0x72, 0xe9, 0x25, 0x76, + 0x18, 0x03, 0xf4, 0x58, 0xd9, 0xda, 0xaa, 0x67, 0xf8, 0xe3, 0x1c, + 0x63, 0x0f, 0xb7, 0x4e, 0x6a, 0x31, 0x2e, 0xf8, 0xa2, 0x5a, 0xba, + 0x11, 0xaf, 0xc0, 0xd7, 0x94, 0x92, 0x43, 0xf9, 0x4f, 0x9c, 0x1f, + 0xab, 0x35, 0xd9, 0xfd, 0x1e, 0x33, 0x1f, 0xcf, 0x78, 0x11, 0xa2, + 0xe0, 0x1d, 0x35, 0x87, 0xb3, 0x8d, 0x77, 0x0a, 0x29, 0xe2, 0x02, + 0x00, 0x00, 0x00, 0x73, 0xc1, 0x8a, 0xb8, 0x54, 0x91, 0x10, 0xf8, + 0xe9, 0x0e, 0x71, 0xbb, 0xc3, 0xab, 0x2a, 0xa8, 0xc4, 0x4d, 0x13, + 0xf4, 0x92, 0x94, 0x99, 0x25, 0x5b, 0x66, 0x0f, 0x24, 0xec, 0x77, + 0x80, 0x0b, 0x97, 0x4b, 0xdd, 0x65, 0x56, 0x7f, 0xde, 0xec, 0xcd, + 0xaf, 0xe4, 0x57, 0xa9, 0x50, 0x3b, 0x45, 0x48, 0xf6, 0x6e, 0xd3, + 0xb1, 0x88, 0xdc, 0xfd, 0xa0, 0xac, 0x38, 0x2b, 0x09, 0x71, 0x1a, + 0xcc ), + &sha256_algorithm, 32, RANGE ( 0, 99710 ), TRIM ( 0, 99710 ), 1 ); + +/** IIS logo (iis-85.png) content information version 1 segment 0 */ +PEERDIST_INFO_SEGMENT_TEST ( iis_85_png_v1_s0, 0, + RANGE ( 0, 99710 ), 2, 65536, + DATA ( 0xd8, 0xd9, 0x76, 0x35, 0x4a, 0x48, 0x72, 0xe9, 0x25, 0x76, 0x18, + 0x03, 0xf4, 0x58, 0xd9, 0xda, 0xaa, 0x67, 0xf8, 0xe3, 0x1c, 0x63, + 0x0f, 0xb7, 0x4e, 0x6a, 0x31, 0x2e, 0xf8, 0xa2, 0x5a, 0xba ), + DATA ( 0x11, 0xaf, 0xc0, 0xd7, 0x94, 0x92, 0x43, 0xf9, 0x4f, 0x9c, 0x1f, + 0xab, 0x35, 0xd9, 0xfd, 0x1e, 0x33, 0x1f, 0xcf, 0x78, 0x11, 0xa2, + 0xe0, 0x1d, 0x35, 0x87, 0xb3, 0x8d, 0x77, 0x0a, 0x29, 0xe2 ), + DATA ( 0x49, 0x1b, 0x21, 0x7d, 0xbe, 0xe2, 0xb5, 0xf1, 0x2c, 0xa7, 0x9b, + 0x01, 0x5e, 0x06, 0xf4, 0xbb, 0xe6, 0x4f, 0x97, 0x45, 0xba, 0xd7, + 0x86, 0x7a, 0xef, 0x17, 0xde, 0x59, 0x92, 0x7e, 0xdc, 0xe9 ) ); + +/** IIS logo (iis-85.png) content information version 1 segment 0 block 0 */ +PEERDIST_INFO_BLOCK_TEST ( iis_85_png_v1_s0_b0, 0, + RANGE ( 0, 65536 ), + TRIM ( 0, 65536 ), + DATA ( 0x73, 0xc1, 0x8a, 0xb8, 0x54, 0x91, 0x10, 0xf8, 0xe9, 0x0e, 0x71, + 0xbb, 0xc3, 0xab, 0x2a, 0xa8, 0xc4, 0x4d, 0x13, 0xf4, 0x92, 0x94, + 0x99, 0x25, 0x5b, 0x66, 0x0f, 0x24, 0xec, 0x77, 0x80, 0x0b ) ); + +/** IIS logo (iis-85.png) content information version 1 segment 0 block 1 */ +PEERDIST_INFO_BLOCK_TEST ( iis_85_png_v1_s0_b1, 1, + RANGE ( 65536, 99710 ), + TRIM ( 65536, 99710 ), + DATA ( 0x97, 0x4b, 0xdd, 0x65, 0x56, 0x7f, 0xde, 0xec, 0xcd, 0xaf, 0xe4, + 0x57, 0xa9, 0x50, 0x3b, 0x45, 0x48, 0xf6, 0x6e, 0xd3, 0xb1, 0x88, + 0xdc, 0xfd, 0xa0, 0xac, 0x38, 0x2b, 0x09, 0x71, 0x1a, 0xcc ) ); + +/** IIS logo (iis-85.png) content information version 2 */ +PEERDIST_INFO_TEST ( iis_85_png_v2, + DATA ( 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x88, 0x00, 0x00, 0x99, 0xde, 0xe0, 0xd0, 0xc3, 0x58, + 0xe2, 0x68, 0x4b, 0x62, 0x33, 0x0d, 0x32, 0xb5, 0xf1, 0x97, 0x87, + 0x24, 0xa0, 0xd0, 0xa5, 0x2b, 0xdc, 0x5e, 0x78, 0x1f, 0xae, 0x71, + 0xff, 0x57, 0xa8, 0xbe, 0x3d, 0xd4, 0x58, 0x03, 0x7e, 0xd4, 0x04, + 0x11, 0x6b, 0xb6, 0x16, 0xd9, 0xb1, 0x41, 0x16, 0x08, 0x85, 0x20, + 0xc4, 0x7c, 0xdc, 0x50, 0xab, 0xce, 0xa3, 0xfa, 0xe1, 0x88, 0xa9, + 0x8e, 0xa2, 0x2d, 0xf3, 0xc0, 0x00, 0x00, 0xeb, 0xa0, 0x33, 0x81, + 0xd0, 0xd0, 0xcb, 0x74, 0xf4, 0xb6, 0x13, 0xd8, 0x21, 0x0f, 0x37, + 0xf0, 0x02, 0xa0, 0x6f, 0x39, 0x10, 0x58, 0x60, 0x96, 0xa1, 0x30, + 0xd3, 0x43, 0x98, 0xc0, 0x8e, 0x66, 0xd7, 0xbc, 0xb8, 0xb6, 0xeb, + 0x77, 0x83, 0xe4, 0xf8, 0x07, 0x64, 0x7b, 0x63, 0xf1, 0x46, 0xb5, + 0x2f, 0x4a, 0xc8, 0x9c, 0xcc, 0x7a, 0xbf, 0x5f, 0xa1, 0x1a, 0xca, + 0xfc, 0x2a, 0xcf, 0x50, 0x28, 0x58, 0x6c ), + &sha512_algorithm, 32, RANGE ( 0, 99710 ), TRIM ( 0, 99710 ), 2 ); + +/** IIS logo (iis-85.png) content information version 2 segment 0 */ +PEERDIST_INFO_SEGMENT_TEST ( iis_85_png_v2_s0, 0, + RANGE ( 0, 39390 ), 1, 39390, + DATA ( 0xe0, 0xd0, 0xc3, 0x58, 0xe2, 0x68, 0x4b, 0x62, 0x33, 0x0d, 0x32, + 0xb5, 0xf1, 0x97, 0x87, 0x24, 0xa0, 0xd0, 0xa5, 0x2b, 0xdc, 0x5e, + 0x78, 0x1f, 0xae, 0x71, 0xff, 0x57, 0xa8, 0xbe, 0x3d, 0xd4 ), + DATA ( 0x58, 0x03, 0x7e, 0xd4, 0x04, 0x11, 0x6b, 0xb6, 0x16, 0xd9, 0xb1, + 0x41, 0x16, 0x08, 0x85, 0x20, 0xc4, 0x7c, 0xdc, 0x50, 0xab, 0xce, + 0xa3, 0xfa, 0xe1, 0x88, 0xa9, 0x8e, 0xa2, 0x2d, 0xf3, 0xc0 ), + DATA ( 0x33, 0x71, 0xbb, 0xea, 0xdd, 0xb6, 0x23, 0x53, 0xad, 0xce, 0xf9, + 0x70, 0xa0, 0x6f, 0xdf, 0x65, 0x00, 0x1e, 0x04, 0x21, 0xf4, 0xc7, + 0x10, 0x82, 0x76, 0xb0, 0xc3, 0x7a, 0x9f, 0x9e, 0xc1, 0x0f ) ); + +/** IIS logo (iis-85.png) content information version 2 segment 0 block 0 */ +PEERDIST_INFO_BLOCK_TEST ( iis_85_png_v2_s0_b0, 0, + RANGE ( 0, 39390 ), + TRIM ( 0, 39390 ), + DATA ( 0xe0, 0xd0, 0xc3, 0x58, 0xe2, 0x68, 0x4b, 0x62, 0x33, 0x0d, 0x32, + 0xb5, 0xf1, 0x97, 0x87, 0x24, 0xa0, 0xd0, 0xa5, 0x2b, 0xdc, 0x5e, + 0x78, 0x1f, 0xae, 0x71, 0xff, 0x57, 0xa8, 0xbe, 0x3d, 0xd4 ) ); + +/** IIS logo (iis-85.png) content information version 2 segment 1 */ +PEERDIST_INFO_SEGMENT_TEST ( iis_85_png_v2_s1, 1, + RANGE ( 39390, 99710 ), 1, 60320, + DATA ( 0x33, 0x81, 0xd0, 0xd0, 0xcb, 0x74, 0xf4, 0xb6, 0x13, 0xd8, 0x21, + 0x0f, 0x37, 0xf0, 0x02, 0xa0, 0x6f, 0x39, 0x10, 0x58, 0x60, 0x96, + 0xa1, 0x30, 0xd3, 0x43, 0x98, 0xc0, 0x8e, 0x66, 0xd7, 0xbc ), + DATA ( 0xb8, 0xb6, 0xeb, 0x77, 0x83, 0xe4, 0xf8, 0x07, 0x64, 0x7b, 0x63, + 0xf1, 0x46, 0xb5, 0x2f, 0x4a, 0xc8, 0x9c, 0xcc, 0x7a, 0xbf, 0x5f, + 0xa1, 0x1a, 0xca, 0xfc, 0x2a, 0xcf, 0x50, 0x28, 0x58, 0x6c ), + DATA ( 0xd7, 0xe9, 0x24, 0x42, 0x5e, 0x8f, 0x4f, 0x88, 0xf0, 0x1d, 0xc6, + 0xa9, 0xbb, 0x1b, 0xc3, 0x7b, 0xe1, 0x13, 0xec, 0x79, 0x17, 0xc7, + 0x45, 0xd4, 0x96, 0x5c, 0x2b, 0x55, 0xfa, 0x16, 0x3a, 0x6e ) ); + +/** IIS logo (iis-85.png) content information version 2 segment 1 block 0 */ +PEERDIST_INFO_BLOCK_TEST ( iis_85_png_v2_s1_b0, 0, + RANGE ( 39390, 99710 ), + TRIM ( 39390, 99710 ), + DATA ( 0x33, 0x81, 0xd0, 0xd0, 0xcb, 0x74, 0xf4, 0xb6, 0x13, 0xd8, 0x21, + 0x0f, 0x37, 0xf0, 0x02, 0xa0, 0x6f, 0x39, 0x10, 0x58, 0x60, 0x96, + 0xa1, 0x30, 0xd3, 0x43, 0x98, 0xc0, 0x8e, 0x66, 0xd7, 0xbc ) ); + +/** + * Report content information test result + * + * @v test Content information test + * @v info Content information to fill in + * @v file Test code file + * @v line Test code line + */ +static void peerdist_info_okx ( struct peerdist_info_test *test, + struct peerdist_info *info, + const char *file, unsigned int line ) { + + /* Parse content information */ + okx ( peerdist_info ( virt_to_user ( test->data ), test->len, + info ) == 0, file, line ); + + /* Verify content information */ + okx ( info->raw.data == virt_to_user ( test->data ), file, line ); + okx ( info->raw.len == test->len, file, line ); + okx ( info->digest == test->expected_digest, file, line ); + okx ( info->digestsize == test->expected_digestsize, file, line ); + okx ( info->range.start == test->expected_range.start, file, line ); + okx ( info->range.end == test->expected_range.end, file, line ); + okx ( info->trim.start == test->expected_trim.start, file, line ); + okx ( info->trim.end == test->expected_trim.end, file, line ); + okx ( info->trim.start >= info->range.start, file, line ); + okx ( info->trim.end <= info->range.end, file, line ); + okx ( info->segments == test->expected_segments, file, line ); +} +#define peerdist_info_ok( test, info ) \ + peerdist_info_okx ( test, info, __FILE__, __LINE__ ) + +/** + * Report content information segment test result + * + * @v test Content information segment test + * @v info Content information + * @v segment Segment information to fill in + * @v file Test code file + * @v line Test code line + */ +static void peerdist_info_segment_okx ( struct peerdist_info_segment_test *test, + const struct peerdist_info *info, + struct peerdist_info_segment *segment, + const char *file, unsigned int line ) { + size_t digestsize = info->digestsize; + + /* Parse content information segment */ + okx ( peerdist_info_segment ( info, segment, test->index ) == 0, + file, line ); + + /* Verify content information segment */ + okx ( segment->info == info, file, line ); + okx ( segment->index == test->index, file, line ); + okx ( segment->range.start == test->expected_range.start, file, line ); + okx ( segment->range.end == test->expected_range.end, file, line ); + okx ( segment->blocks == test->expected_blocks, file, line ); + okx ( segment->blksize == test->expected_blksize, file, line ); + okx ( memcmp ( segment->hash, test->expected_hash, + digestsize ) == 0, file, line ); + okx ( memcmp ( segment->secret, test->expected_secret, + digestsize ) == 0, file, line ); + okx ( memcmp ( segment->id, test->expected_id, + digestsize ) == 0, file, line ); +} +#define peerdist_info_segment_ok( test, info, segment ) \ + peerdist_info_segment_okx ( test, info, segment, __FILE__, __LINE__ ) + +/** + * Report content information block test result + * + * @v test Content information block test + * @v segment Segment information + * @v block Block information to fill in + * @v file Test code file + * @v line Test code line + */ +static void +peerdist_info_block_okx ( struct peerdist_info_block_test *test, + const struct peerdist_info_segment *segment, + struct peerdist_info_block *block, + const char *file, unsigned int line ) { + const struct peerdist_info *info = segment->info; + size_t digestsize = info->digestsize; + + /* Parse content information block */ + okx ( peerdist_info_block ( segment, block, test->index ) == 0, + file, line ); + + /* Verify content information block */ + okx ( block->segment == segment, file, line ); + okx ( block->index == test->index, file, line ); + okx ( block->range.start == test->expected_range.start, file, line ); + okx ( block->range.end == test->expected_range.end, file, line ); + okx ( block->trim.start == test->expected_trim.start, file, line ); + okx ( block->trim.end == test->expected_trim.end, file, line ); + okx ( memcmp ( block->hash, test->expected_hash, + digestsize ) == 0, file, line ); +} +#define peerdist_info_block_ok( test, segment, block ) \ + peerdist_info_block_okx ( test, segment, block, __FILE__, __LINE__ ) + +/** + * Report server passphrase test result + * + * @v test Content information segment test + * @v info Content information + * @v pass Server passphrase + * @v pass_len Length of server passphrase + * @v file Test code file + * @v line Test code line + */ +static void +peerdist_info_passphrase_okx ( struct peerdist_info_segment_test *test, + const struct peerdist_info *info, + uint8_t *pass, size_t pass_len, + const char *file, unsigned int line ) { + struct digest_algorithm *digest = info->digest; + uint8_t ctx[digest->ctxsize]; + uint8_t secret[digest->digestsize]; + uint8_t expected[digest->digestsize]; + size_t digestsize = info->digestsize; + size_t secretsize = digestsize; + + /* Calculate server secret */ + digest_init ( digest, ctx ); + digest_update ( digest, ctx, pass, pass_len ); + digest_final ( digest, ctx, secret ); + + /* Calculate expected segment secret */ + hmac_init ( digest, ctx, secret, &secretsize ); + assert ( secretsize == digestsize ); + hmac_update ( digest, ctx, test->expected_hash, digestsize ); + hmac_final ( digest, ctx, secret, &secretsize, expected ); + assert ( secretsize == digestsize ); + + /* Verify segment secret */ + okx ( memcmp ( test->expected_secret, expected, digestsize ) == 0, + file, line ); +} +#define peerdist_info_passphrase_ok( test, info, pass, pass_len ) \ + peerdist_info_passphrase_okx ( test, info, pass, pass_len, \ + __FILE__, __LINE__ ) + +/** + * Perform content information self-tests + * + */ +static void peerdist_info_test_exec ( void ) { + struct peerdist_info info; + struct peerdist_info_segment segment; + struct peerdist_info_block block; + + /* IIS logo (iis-85.png) content information version 1 */ + peerdist_info_ok ( &iis_85_png_v1, &info ); + peerdist_info_passphrase_ok ( &iis_85_png_v1_s0, &info, + passphrase, sizeof ( passphrase ) ); + peerdist_info_segment_ok ( &iis_85_png_v1_s0, &info, &segment ); + peerdist_info_block_ok ( &iis_85_png_v1_s0_b0, &segment, &block ); + peerdist_info_block_ok ( &iis_85_png_v1_s0_b1, &segment, &block ); + + /* IIS logo (iis-85.png) content information version 2 */ + peerdist_info_ok ( &iis_85_png_v2, &info ); + peerdist_info_passphrase_ok ( &iis_85_png_v2_s0, &info, + passphrase, sizeof ( passphrase ) ); + peerdist_info_segment_ok ( &iis_85_png_v2_s0, &info, &segment ); + peerdist_info_block_ok ( &iis_85_png_v2_s0_b0, &segment, &block ); + peerdist_info_passphrase_ok ( &iis_85_png_v2_s1, &info, + passphrase, sizeof ( passphrase ) ); + peerdist_info_segment_ok ( &iis_85_png_v2_s1, &info, &segment ); + peerdist_info_block_ok ( &iis_85_png_v2_s1_b0, &segment, &block ); +} + +/** Content information self-test */ +struct self_test peerdist_info_test __self_test = { + .name = "pccrc", + .exec = peerdist_info_test_exec, +}; diff --git a/qemu/roms/ipxe/src/tests/pixbuf_test.c b/qemu/roms/ipxe/src/tests/pixbuf_test.c index 15cd33dfd..aaa516bb2 100644 --- a/qemu/roms/ipxe/src/tests/pixbuf_test.c +++ b/qemu/roms/ipxe/src/tests/pixbuf_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -53,8 +57,8 @@ void pixbuf_okx ( struct pixel_buffer_test *test, const char *file, /* Correct image data pointer */ test->image->data = virt_to_user ( ( void * ) test->image->data ); - /* Check that image is detected as PNM */ - okx ( image_probe ( test->image ) == 0, file, line ); + /* Check that image is detected as correct type */ + okx ( register_image ( test->image ) == 0, file, line ); okx ( test->image->type == test->type, file, line ); /* Check that a pixel buffer can be created from the image */ @@ -73,4 +77,7 @@ void pixbuf_okx ( struct pixel_buffer_test *test, const char *file, pixbuf_put ( pixbuf ); } + + /* Unregister image */ + unregister_image ( test->image ); } diff --git a/qemu/roms/ipxe/src/tests/pixbuf_test.h b/qemu/roms/ipxe/src/tests/pixbuf_test.h index 394f7f5fa..d12829d89 100644 --- a/qemu/roms/ipxe/src/tests/pixbuf_test.h +++ b/qemu/roms/ipxe/src/tests/pixbuf_test.h @@ -1,7 +1,7 @@ #ifndef _PIXBUF_TEST_H #define _PIXBUF_TEST_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/tests/png_test.c b/qemu/roms/ipxe/src/tests/png_test.c index cf32f2034..e921aa2a6 100644 --- a/qemu/roms/ipxe/src/tests/png_test.c +++ b/qemu/roms/ipxe/src/tests/png_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/tests/pnm_test.c b/qemu/roms/ipxe/src/tests/pnm_test.c index 26b0c0726..d57fdaaef 100644 --- a/qemu/roms/ipxe/src/tests/pnm_test.c +++ b/qemu/roms/ipxe/src/tests/pnm_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/tests/profile_test.c b/qemu/roms/ipxe/src/tests/profile_test.c index 9d682bf2b..d2f8df211 100644 --- a/qemu/roms/ipxe/src/tests/profile_test.c +++ b/qemu/roms/ipxe/src/tests/profile_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/tests/pubkey_test.h b/qemu/roms/ipxe/src/tests/pubkey_test.h index 7678453a9..cd65b8703 100644 --- a/qemu/roms/ipxe/src/tests/pubkey_test.h +++ b/qemu/roms/ipxe/src/tests/pubkey_test.h @@ -1,7 +1,7 @@ #ifndef _PUBKEY_TEST_H #define _PUBKEY_TEST_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/tests/rsa_test.c b/qemu/roms/ipxe/src/tests/rsa_test.c index 3b32c74bc..c0d05d263 100644 --- a/qemu/roms/ipxe/src/tests/rsa_test.c +++ b/qemu/roms/ipxe/src/tests/rsa_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/tests/setjmp_test.c b/qemu/roms/ipxe/src/tests/setjmp_test.c new file mode 100644 index 000000000..50ad13f3c --- /dev/null +++ b/qemu/roms/ipxe/src/tests/setjmp_test.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * setjmp()/longjmp() tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include + +/** A setjmp()/longjmp() test */ +struct setjmp_test { + /** Jump buffer */ + jmp_buf env; + /** Expected value */ + int expected; + /** Test code file */ + const char *file; + /** Test code line */ + unsigned int line; +}; + +/** Expected jump */ +static struct setjmp_test *jumped; + +/** + * Report a setjmp() test result + * + * @v test setjmp()/longjmp() test + * + * This has to be implemented as a macro since if it were a function + * then the context saved by setjmp() would be invalidated when the + * function returned. + */ +#define setjmp_ok( test ) do { \ + int value; \ + /* Sanity check */ \ + assert ( jumped == NULL ); \ + /* Initialise test */ \ + (test)->expected = 0; \ + (test)->file = __FILE__; \ + (test)->line = __LINE__; \ + /* Perform setjmp() */ \ + value = setjmp ( (test)->env ); \ + /* Report setjmp()/longjmp() result */ \ + setjmp_return_ok ( (test), value ); \ + } while ( 0 ) + +/** + * Report a setjmp()/longjmp() test result + * + * @v test setjmp()/longjmp() test + * @v value Value returned from setjmp() + * + * This function ends up reporting results from either setjmp() or + * longjmp() tests (since calls to longjmp() will return via the + * corresponding setjmp()). It therefore uses the test code file and + * line stored in the test structure, which will represent the line + * from which either setjmp() or longjmp() was called. + */ +static void setjmp_return_ok ( struct setjmp_test *test, int value ) { + + /* Determine whether this was reached via setjmp() or longjmp() */ + if ( value == 0 ) { + /* This is the initial call to setjmp() */ + okx ( test->expected == 0, test->file, test->line ); + okx ( jumped == NULL, test->file, test->line ); + } else { + /* This is reached via a call to longjmp() */ + okx ( value == test->expected, test->file, test->line ); + okx ( jumped == test, test->file, test->line ); + } + + /* Clear expected jump */ + jumped = NULL; +} + +/** + * Report a longjmp() test result + * + * @v test setjmp()/longjmp() test + * @v file Test code file + * @v line Test code line + */ +static void longjmp_okx ( struct setjmp_test *test, int value, + const char *file, unsigned int line ) { + + /* Record expected value. A zero passed to longjmp() should + * result in setjmp() returning a value of one. + */ + test->expected = ( value ? value : 1 ); + + /* Record test code file and line */ + test->file = file; + test->line = line; + + /* Record expected jump */ + jumped = test; + + /* Perform longjmp(). Should return via setjmp_okx() */ + longjmp ( test->env, value ); + + /* longjmp() should never return */ + assert ( 0 ); +} +#define longjmp_ok( test, value ) \ + longjmp_okx ( test, value, __FILE__, __LINE__ ) + +/** + * Perform setjmp()/longjmp() self-tests + * + */ +static void setjmp_test_exec ( void ) { + static struct setjmp_test alpha; + static struct setjmp_test beta; + static int iteration; + + /* This is one of the very few situations in which the + * "for-case" pattern is justified. + */ + for ( iteration = 0 ; iteration < 10 ; iteration++ ) { + DBGC ( jumped, "SETJMP test iteration %d\n", iteration ); + switch ( iteration ) { + case 0: setjmp_ok ( &alpha ); break; + case 1: setjmp_ok ( &beta ); break; + case 2: longjmp_ok ( &alpha, 0 ); + case 3: longjmp_ok ( &alpha, 1 ); + case 4: longjmp_ok ( &alpha, 2 ); + case 5: longjmp_ok ( &beta, 17 ); + case 6: longjmp_ok ( &beta, 29 ); + case 7: longjmp_ok ( &alpha, -1 ); + case 8: longjmp_ok ( &beta, 0 ); + case 9: longjmp_ok ( &beta, 42 ); + } + } +} + +/** setjmp()/longjmp() self-test */ +struct self_test setjmp_test __self_test = { + .name = "setjmp", + .exec = setjmp_test_exec, +}; diff --git a/qemu/roms/ipxe/src/tests/settings_test.c b/qemu/roms/ipxe/src/tests/settings_test.c index 4ee6a10fa..f7fb35d0d 100644 --- a/qemu/roms/ipxe/src/tests/settings_test.c +++ b/qemu/roms/ipxe/src/tests/settings_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -228,6 +232,12 @@ static struct setting test_hexraw_setting = { .type = &setting_type_hexraw, }; +/** Test Base64 setting type */ +static struct setting test_base64_setting = { + .name = "test_base64", + .type = &setting_type_base64, +}; + /** Test UUID setting type */ static struct setting test_uuid_setting = { .name = "test_uuid", @@ -379,6 +389,15 @@ static void settings_test_exec ( void ) { 0x17, 0x06, 0x39, 0x6b, 0xf4, 0x48, 0x4e ), "9e4b6eef36b646fe8f1706396bf4484e" ); + /* "base64" setting type */ + storef_ok ( &test_settings, &test_base64_setting, + "cGFzc6\nNwaHJhc2U= ", + RAW ( 0x70, 0x61, 0x73, 0x73, 0xa3, 0x70, 0x68, 0x72, 0x61, + 0x73, 0x65 ) ); + fetchf_ok ( &test_settings, &test_base64_setting, + RAW ( 0x80, 0x81, 0x82, 0x83, 0x84, 0x00, 0xff ), + "gIGCg4QA/w==" ); + /* "uuid" setting type (no store capability) */ fetchf_ok ( &test_settings, &test_uuid_setting, RAW ( 0x1a, 0x6a, 0x74, 0x9d, 0x0e, 0xda, 0x46, 0x1a,0xa8, @@ -399,3 +418,7 @@ struct self_test settings_test __self_test = { .name = "settings", .exec = settings_test_exec, }; + +/* Include real IPv6 setting type */ +REQUIRING_SYMBOL ( settings_test ); +REQUIRE_OBJECT ( ipv6 ); diff --git a/qemu/roms/ipxe/src/tests/sha1_test.c b/qemu/roms/ipxe/src/tests/sha1_test.c index bcf761bdd..9f1d75686 100644 --- a/qemu/roms/ipxe/src/tests/sha1_test.c +++ b/qemu/roms/ipxe/src/tests/sha1_test.c @@ -15,87 +15,63 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * * SHA-1 tests * + * NIST test vectors are taken from + * + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA1.pdf + * */ -#include +/* Forcibly enable assertions */ +#undef NDEBUG + #include #include #include "digest_test.h" -/** An SHA-1 test vector */ -struct sha1_test_vector { - /** Test data */ - void *data; - /** Test data length */ - size_t len; - /** Expected digest */ - uint8_t digest[SHA1_DIGEST_SIZE]; -}; +/* Empty test vector (digest obtained from "sha1sum /dev/null") */ +DIGEST_TEST ( sha1_empty, &sha1_algorithm, DIGEST_EMPTY, + DIGEST ( 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, + 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, + 0x07, 0x09 ) ); -/** SHA-1 test vectors */ -static struct sha1_test_vector sha1_test_vectors[] = { - /* Empty test data - * - * Expected digest value obtained from "sha1sum /dev/null" - */ - { NULL, 0, - { 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, - 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 } }, - /* Test data and expected digests taken from the NIST - * Cryptographic Toolkit Algorithm Examples at - * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA1.pdf - */ - { "abc", 3, - { 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e, - 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d } }, - { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56, - { 0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2, 0x6e, 0xba, 0xae, - 0x4a, 0xa1, 0xf9, 0x51, 0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1 } }, -}; +/* NIST test vector "abc" */ +DIGEST_TEST ( sha1_nist_abc, &sha1_algorithm, DIGEST_NIST_ABC, + DIGEST ( 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, + 0x3e, 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, + 0xd8, 0x9d ) ); -/** SHA-1 test fragment lists */ -static struct digest_test_fragments sha1_test_fragments[] = { - { { 0, -1UL, } }, - { { 1, 1, 1, 1, 1, 1, 1, 1 } }, - { { 2, 0, 23, 4, 6, 1, 0 } }, -}; +/* NIST test vector "abc...opq" */ +DIGEST_TEST ( sha1_nist_abc_opq, &sha1_algorithm, DIGEST_NIST_ABC_OPQ, + DIGEST ( 0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2, 0x6e, 0xba, + 0xae, 0x4a, 0xa1, 0xf9, 0x51, 0x29, 0xe5, 0xe5, 0x46, + 0x70, 0xf1 ) ); /** * Perform SHA-1 self-test * */ static void sha1_test_exec ( void ) { - struct digest_algorithm *digest = &sha1_algorithm; - struct sha1_test_vector *test; - unsigned long cost; - unsigned int i; - unsigned int j; - /* Correctness test */ - for ( i = 0 ; i < ( sizeof ( sha1_test_vectors ) / - sizeof ( sha1_test_vectors[0] ) ) ; i++ ) { - test = &sha1_test_vectors[i]; - /* Test with a single pass */ - digest_ok ( digest, NULL, test->data, test->len, test->digest ); - /* Test with fragment lists */ - for ( j = 0 ; j < ( sizeof ( sha1_test_fragments ) / - sizeof ( sha1_test_fragments[0] ) ) ; j++ ){ - digest_ok ( digest, &sha1_test_fragments[j], - test->data, test->len, test->digest ); - } - } + /* Correctness tests */ + digest_ok ( &sha1_empty ); + digest_ok ( &sha1_nist_abc ); + digest_ok ( &sha1_nist_abc_opq ); - /* Speed test */ - cost = digest_cost ( digest ); - DBG ( "SHA1 required %ld cycles per byte\n", cost ); + /* Speed tests */ + DBG ( "SHA1 required %ld cycles per byte\n", + digest_cost ( &sha1_algorithm ) ); } /** SHA-1 self-test */ diff --git a/qemu/roms/ipxe/src/tests/sha256_test.c b/qemu/roms/ipxe/src/tests/sha256_test.c index 06a8cae25..3b4c423fd 100644 --- a/qemu/roms/ipxe/src/tests/sha256_test.c +++ b/qemu/roms/ipxe/src/tests/sha256_test.c @@ -15,93 +15,96 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * - * SHA-256 tests + * SHA-256 family tests + * + * NIST test vectors are taken from + * + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA256.pdf + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA224.pdf * */ -#include +/* Forcibly enable assertions */ +#undef NDEBUG + #include #include #include "digest_test.h" -/** An SHA-256 test vector */ -struct sha256_test_vector { - /** Test data */ - void *data; - /** Test data length */ - size_t len; - /** Expected digest */ - uint8_t digest[SHA256_DIGEST_SIZE]; -}; +/* Empty test vector (digest obtained from "sha256sum /dev/null") */ +DIGEST_TEST ( sha256_empty, &sha256_algorithm, DIGEST_EMPTY, + DIGEST ( 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, + 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, + 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, + 0x1b, 0x78, 0x52, 0xb8, 0x55 ) ); -/** SHA-256 test vectors */ -static struct sha256_test_vector sha256_test_vectors[] = { - /* Empty test data - * - * Expected digest value obtained from "sha256sum /dev/null" - */ - { NULL, 0, - { 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, - 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, - 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 } }, - /* Test data and expected digests taken from the NIST - * Cryptographic Toolkit Algorithm Examples at - * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA256.pdf - */ - { "abc", 3, - { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, - 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, - 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad } }, - { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56, - { 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26, - 0x93, 0x0c, 0x3e, 0x60, 0x39, 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, - 0x21, 0x67, 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1 } }, -}; +/* NIST test vector "abc" */ +DIGEST_TEST ( sha256_nist_abc, &sha256_algorithm, DIGEST_NIST_ABC, + DIGEST ( 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, + 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, + 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, + 0x61, 0xf2, 0x00, 0x15, 0xad ) ); -/** SHA-256 test fragment lists */ -static struct digest_test_fragments sha256_test_fragments[] = { - { { 0, -1UL, } }, - { { 1, 1, 1, 1, 1, 1, 1, 1 } }, - { { 2, 0, 23, 4, 6, 1, 0 } }, -}; +/* NIST test vector "abc...opq" */ +DIGEST_TEST ( sha256_nist_abc_opq, &sha256_algorithm, DIGEST_NIST_ABC_OPQ, + DIGEST ( 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, + 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39, 0xa3, 0x3c, + 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, 0xf6, 0xec, 0xed, + 0xd4, 0x19, 0xdb, 0x06, 0xc1 ) ); + +/* Empty test vector (digest obtained from "sha224sum /dev/null") */ +DIGEST_TEST ( sha224_empty, &sha224_algorithm, DIGEST_EMPTY, + DIGEST ( 0xd1, 0x4a, 0x02, 0x8c, 0x2a, 0x3a, 0x2b, 0xc9, 0x47, + 0x61, 0x02, 0xbb, 0x28, 0x82, 0x34, 0xc4, 0x15, 0xa2, + 0xb0, 0x1f, 0x82, 0x8e, 0xa6, 0x2a, 0xc5, 0xb3, 0xe4, + 0x2f ) ); + +/* NIST test vector "abc" */ +DIGEST_TEST ( sha224_nist_abc, &sha224_algorithm, DIGEST_NIST_ABC, + DIGEST ( 0x23, 0x09, 0x7d, 0x22, 0x34, 0x05, 0xd8, 0x22, 0x86, + 0x42, 0xa4, 0x77, 0xbd, 0xa2, 0x55, 0xb3, 0x2a, 0xad, + 0xbc, 0xe4, 0xbd, 0xa0, 0xb3, 0xf7, 0xe3, 0x6c, 0x9d, + 0xa7 ) ); + +/* NIST test vector "abc...opq" */ +DIGEST_TEST ( sha224_nist_abc_opq, &sha224_algorithm, DIGEST_NIST_ABC_OPQ, + DIGEST ( 0x75, 0x38, 0x8b, 0x16, 0x51, 0x27, 0x76, 0xcc, 0x5d, + 0xba, 0x5d, 0xa1, 0xfd, 0x89, 0x01, 0x50, 0xb0, 0xc6, + 0x45, 0x5c, 0xb4, 0xf5, 0x8b, 0x19, 0x52, 0x52, 0x25, + 0x25 ) ); /** - * Perform SHA-256 self-test + * Perform SHA-256 family self-test * */ static void sha256_test_exec ( void ) { - struct digest_algorithm *digest = &sha256_algorithm; - struct sha256_test_vector *test; - unsigned long cost; - unsigned int i; - unsigned int j; - /* Correctness test */ - for ( i = 0 ; i < ( sizeof ( sha256_test_vectors ) / - sizeof ( sha256_test_vectors[0] ) ) ; i++ ) { - test = &sha256_test_vectors[i]; - /* Test with a single pass */ - digest_ok ( digest, NULL, test->data, test->len, test->digest ); - /* Test with fragment lists */ - for ( j = 0 ; j < ( sizeof ( sha256_test_fragments ) / - sizeof ( sha256_test_fragments[0] )); j++ ){ - digest_ok ( digest, &sha256_test_fragments[j], - test->data, test->len, test->digest ); - } - } + /* Correctness tests */ + digest_ok ( &sha256_empty ); + digest_ok ( &sha256_nist_abc ); + digest_ok ( &sha256_nist_abc_opq ); + digest_ok ( &sha224_empty ); + digest_ok ( &sha224_nist_abc ); + digest_ok ( &sha224_nist_abc_opq ); - /* Speed test */ - cost = digest_cost ( digest ); - DBG ( "SHA256 required %ld cycles per byte\n", cost ); + /* Speed tests */ + DBG ( "SHA256 required %ld cycles per byte\n", + digest_cost ( &sha256_algorithm ) ); + DBG ( "SHA224 required %ld cycles per byte\n", + digest_cost ( &sha224_algorithm ) ); } -/** SHA-256 self-test */ +/** SHA-256 family self-test */ struct self_test sha256_test __self_test = { .name = "sha256", .exec = sha256_test_exec, diff --git a/qemu/roms/ipxe/src/tests/sha512_test.c b/qemu/roms/ipxe/src/tests/sha512_test.c new file mode 100644 index 000000000..be530ebad --- /dev/null +++ b/qemu/roms/ipxe/src/tests/sha512_test.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * SHA-512 family tests + * + * NIST test vectors are taken from + * + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA512.pdf + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA384.pdf + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA512_256.pdf + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA512_224.pdf + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include "digest_test.h" + +/* Empty test vector (digest obtained from "sha512sum /dev/null") */ +DIGEST_TEST ( sha512_empty, &sha512_algorithm, DIGEST_EMPTY, + DIGEST ( 0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd, 0xf1, + 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07, 0xd6, 0x20, + 0xe4, 0x05, 0x0b, 0x57, 0x15, 0xdc, 0x83, 0xf4, 0xa9, + 0x21, 0xd3, 0x6c, 0xe9, 0xce, 0x47, 0xd0, 0xd1, 0x3c, + 0x5d, 0x85, 0xf2, 0xb0, 0xff, 0x83, 0x18, 0xd2, 0x87, + 0x7e, 0xec, 0x2f, 0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, + 0x7a, 0x81, 0xa5, 0x38, 0x32, 0x7a, 0xf9, 0x27, 0xda, + 0x3e ) ); + +/* NIST test vector "abc" */ +DIGEST_TEST ( sha512_nist_abc, &sha512_algorithm, DIGEST_NIST_ABC, + DIGEST ( 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, + 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, + 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, 0x0a, 0x9e, 0xee, + 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, + 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, + 0xfe, 0xeb, 0xbd, 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, + 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, + 0x9f ) ); + +/* NIST test vector "abc...stu" */ +DIGEST_TEST ( sha512_nist_abc_stu, &sha512_algorithm, DIGEST_NIST_ABC_STU, + DIGEST ( 0x8e, 0x95, 0x9b, 0x75, 0xda, 0xe3, 0x13, 0xda, 0x8c, + 0xf4, 0xf7, 0x28, 0x14, 0xfc, 0x14, 0x3f, 0x8f, 0x77, + 0x79, 0xc6, 0xeb, 0x9f, 0x7f, 0xa1, 0x72, 0x99, 0xae, + 0xad, 0xb6, 0x88, 0x90, 0x18, 0x50, 0x1d, 0x28, 0x9e, + 0x49, 0x00, 0xf7, 0xe4, 0x33, 0x1b, 0x99, 0xde, 0xc4, + 0xb5, 0x43, 0x3a, 0xc7, 0xd3, 0x29, 0xee, 0xb6, 0xdd, + 0x26, 0x54, 0x5e, 0x96, 0xe5, 0x5b, 0x87, 0x4b, 0xe9, + 0x09 ) ); + +/* Empty test vector (digest obtained from "sha384sum /dev/null") */ +DIGEST_TEST ( sha384_empty, &sha384_algorithm, DIGEST_EMPTY, + DIGEST ( 0x38, 0xb0, 0x60, 0xa7, 0x51, 0xac, 0x96, 0x38, 0x4c, + 0xd9, 0x32, 0x7e, 0xb1, 0xb1, 0xe3, 0x6a, 0x21, 0xfd, + 0xb7, 0x11, 0x14, 0xbe, 0x07, 0x43, 0x4c, 0x0c, 0xc7, + 0xbf, 0x63, 0xf6, 0xe1, 0xda, 0x27, 0x4e, 0xde, 0xbf, + 0xe7, 0x6f, 0x65, 0xfb, 0xd5, 0x1a, 0xd2, 0xf1, 0x48, + 0x98, 0xb9, 0x5b ) ); + +/* NIST test vector "abc" */ +DIGEST_TEST ( sha384_nist_abc, &sha384_algorithm, DIGEST_NIST_ABC, + DIGEST ( 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b, 0xb5, + 0xa0, 0x3d, 0x69, 0x9a, 0xc6, 0x50, 0x07, 0x27, 0x2c, + 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63, 0x1a, 0x8b, 0x60, + 0x5a, 0x43, 0xff, 0x5b, 0xed, 0x80, 0x86, 0x07, 0x2b, + 0xa1, 0xe7, 0xcc, 0x23, 0x58, 0xba, 0xec, 0xa1, 0x34, + 0xc8, 0x25, 0xa7 ) ); + +/* NIST test vector "abc...stu" */ +DIGEST_TEST ( sha384_nist_abc_stu, &sha384_algorithm, DIGEST_NIST_ABC_STU, + DIGEST ( 0x09, 0x33, 0x0c, 0x33, 0xf7, 0x11, 0x47, 0xe8, 0x3d, + 0x19, 0x2f, 0xc7, 0x82, 0xcd, 0x1b, 0x47, 0x53, 0x11, + 0x1b, 0x17, 0x3b, 0x3b, 0x05, 0xd2, 0x2f, 0xa0, 0x80, + 0x86, 0xe3, 0xb0, 0xf7, 0x12, 0xfc, 0xc7, 0xc7, 0x1a, + 0x55, 0x7e, 0x2d, 0xb9, 0x66, 0xc3, 0xe9, 0xfa, 0x91, + 0x74, 0x60, 0x39 ) ); + +/* Empty test vector (digest obtained from "shasum -a 512256 /dev/null") */ +DIGEST_TEST ( sha512_256_empty, &sha512_256_algorithm, DIGEST_EMPTY, + DIGEST ( 0xc6, 0x72, 0xb8, 0xd1, 0xef, 0x56, 0xed, 0x28, 0xab, + 0x87, 0xc3, 0x62, 0x2c, 0x51, 0x14, 0x06, 0x9b, 0xdd, + 0x3a, 0xd7, 0xb8, 0xf9, 0x73, 0x74, 0x98, 0xd0, 0xc0, + 0x1e, 0xce, 0xf0, 0x96, 0x7a ) ); + +/* NIST test vector "abc" */ +DIGEST_TEST ( sha512_256_nist_abc, &sha512_256_algorithm, DIGEST_NIST_ABC, + DIGEST ( 0x53, 0x04, 0x8e, 0x26, 0x81, 0x94, 0x1e, 0xf9, 0x9b, + 0x2e, 0x29, 0xb7, 0x6b, 0x4c, 0x7d, 0xab, 0xe4, 0xc2, + 0xd0, 0xc6, 0x34, 0xfc, 0x6d, 0x46, 0xe0, 0xe2, 0xf1, + 0x31, 0x07, 0xe7, 0xaf, 0x23 ) ); + +/* NIST test vector "abc...stu" */ +DIGEST_TEST ( sha512_256_nist_abc_stu, &sha512_256_algorithm, + DIGEST_NIST_ABC_STU, + DIGEST ( 0x39, 0x28, 0xe1, 0x84, 0xfb, 0x86, 0x90, 0xf8, 0x40, + 0xda, 0x39, 0x88, 0x12, 0x1d, 0x31, 0xbe, 0x65, 0xcb, + 0x9d, 0x3e, 0xf8, 0x3e, 0xe6, 0x14, 0x6f, 0xea, 0xc8, + 0x61, 0xe1, 0x9b, 0x56, 0x3a ) ); + +/* Empty test vector (digest obtained from "shasum -a 512224 /dev/null") */ +DIGEST_TEST ( sha512_224_empty, &sha512_224_algorithm, DIGEST_EMPTY, + DIGEST ( 0x6e, 0xd0, 0xdd, 0x02, 0x80, 0x6f, 0xa8, 0x9e, 0x25, + 0xde, 0x06, 0x0c, 0x19, 0xd3, 0xac, 0x86, 0xca, 0xbb, + 0x87, 0xd6, 0xa0, 0xdd, 0xd0, 0x5c, 0x33, 0x3b, 0x84, + 0xf4 ) ); + +/* NIST test vector "abc" */ +DIGEST_TEST ( sha512_224_nist_abc, &sha512_224_algorithm, DIGEST_NIST_ABC, + DIGEST ( 0x46, 0x34, 0x27, 0x0f, 0x70, 0x7b, 0x6a, 0x54, 0xda, + 0xae, 0x75, 0x30, 0x46, 0x08, 0x42, 0xe2, 0x0e, 0x37, + 0xed, 0x26, 0x5c, 0xee, 0xe9, 0xa4, 0x3e, 0x89, 0x24, + 0xaa ) ); + +/* NIST test vector "abc...stu" */ +DIGEST_TEST ( sha512_224_nist_abc_stu, &sha512_224_algorithm, + DIGEST_NIST_ABC_STU, + DIGEST ( 0x23, 0xfe, 0xc5, 0xbb, 0x94, 0xd6, 0x0b, 0x23, 0x30, + 0x81, 0x92, 0x64, 0x0b, 0x0c, 0x45, 0x33, 0x35, 0xd6, + 0x64, 0x73, 0x4f, 0xe4, 0x0e, 0x72, 0x68, 0x67, 0x4a, + 0xf9 ) ); + +/** + * Perform SHA-512 family self-test + * + */ +static void sha512_test_exec ( void ) { + + /* Correctness tests */ + digest_ok ( &sha512_empty ); + digest_ok ( &sha512_nist_abc ); + digest_ok ( &sha512_nist_abc_stu ); + digest_ok ( &sha384_empty ); + digest_ok ( &sha384_nist_abc ); + digest_ok ( &sha384_nist_abc_stu ); + digest_ok ( &sha512_256_empty ); + digest_ok ( &sha512_256_nist_abc ); + digest_ok ( &sha512_256_nist_abc_stu ); + digest_ok ( &sha512_224_empty ); + digest_ok ( &sha512_224_nist_abc ); + digest_ok ( &sha512_224_nist_abc_stu ); + + /* Speed tests */ + DBG ( "SHA512 required %ld cycles per byte\n", + digest_cost ( &sha512_algorithm ) ); + DBG ( "SHA384 required %ld cycles per byte\n", + digest_cost ( &sha384_algorithm ) ); + DBG ( "SHA512/256 required %ld cycles per byte\n", + digest_cost ( &sha512_256_algorithm ) ); + DBG ( "SHA512/224 required %ld cycles per byte\n", + digest_cost ( &sha512_224_algorithm ) ); +} + +/** SHA-512 family self-test */ +struct self_test sha512_test __self_test = { + .name = "sha512", + .exec = sha512_test_exec, +}; diff --git a/qemu/roms/ipxe/src/tests/string_test.c b/qemu/roms/ipxe/src/tests/string_test.c index 3b48d9f3d..4693b5f65 100644 --- a/qemu/roms/ipxe/src/tests/string_test.c +++ b/qemu/roms/ipxe/src/tests/string_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -31,7 +35,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include +#include #include +#include +#include #include /** @@ -63,6 +70,18 @@ static void string_test_exec ( void ) { ok ( *(strchr ( "Testing", 'g' )) == 'g' ); ok ( strchr ( "Testing", 'x' ) == NULL ); + /* Test strrchr() */ + ok ( strrchr ( "", 'a' ) == NULL ); + ok ( *(strrchr ( "Haystack", 'a' )) == 'a' ); + ok ( *(strrchr ( "Haystack", 'k' )) == 'k' ); + ok ( strrchr ( "Haystack", 'x' ) == NULL ); + + /* Test memchr() */ + ok ( memchr ( "", '\0', 0 ) == NULL ); + ok ( *((uint8_t *)memchr ( "post\0null", 'l', 9 )) == 'l' ); + ok ( *((uint8_t *)memchr ( "post\0null", '\0', 9 )) == '\0' ); + ok ( memchr ( "thingy", 'z', 6 ) == NULL ); + /* Test strcmp() */ ok ( strcmp ( "", "" ) == 0 ); ok ( strcmp ( "Hello", "Hello" ) == 0 ); @@ -78,11 +97,31 @@ static void string_test_exec ( void ) { ok ( strncmp ( "Goodbye", "Goodbye world", 32 ) != 0 ); ok ( strncmp ( "Goodbye", "Goodbye world", 7 ) == 0 ); + /* Test strcasecmp() */ + ok ( strcasecmp ( "", "" ) == 0 ); + ok ( strcasecmp ( "Uncle Jack", "Uncle jack" ) == 0 ); + ok ( strcasecmp ( "Uncle Jack", "Uncle" ) != 0 ); + ok ( strcasecmp ( "Uncle", "Uncle Jack" ) != 0 ); + ok ( strcasecmp ( "not", "equal" ) != 0 ); + /* Test memcmp() */ ok ( memcmp ( "", "", 0 ) == 0 ); ok ( memcmp ( "Foo", "Foo", 3 ) == 0 ); ok ( memcmp ( "Foo", "Bar", 3 ) != 0 ); + /* Test strstr() */ + { + const char haystack[] = "find me!"; + char *found; + + found = strstr ( haystack, "find" ); + ok ( found == &haystack[0] ); + found = strstr ( haystack, "me" ); + ok ( found == &haystack[5] ); + found = strstr ( haystack, "me." ); + ok ( found == NULL ); + } + /* Test memset() */ { static uint8_t test[7] = { '>', 1, 1, 1, 1, 1, '<' }; @@ -154,6 +193,107 @@ static void string_test_exec ( void ) { ok ( strcmp ( dup, "hello" ) == 0 ); free ( dup ); } + + /* Test strcpy() */ + { + const char longer[7] = "copyme"; + const char shorter[3] = "hi"; + char dest[7]; + char *copy; + + copy = strcpy ( dest, longer ); + ok ( copy == dest ); + ok ( memcmp ( dest, longer, 7 ) == 0 ); + copy = strcpy ( dest, shorter ); + ok ( copy == dest ); + ok ( memcmp ( dest, shorter, 3 ) == 0 ); + ok ( memcmp ( ( dest + 3 ), ( longer + 3 ), 4 ) == 0 ); + } + + /* Test strncpy() */ + { + const char src[5] = "copy"; + const char orig[8] = { 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x' }; + const char zero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + char dest[8]; + char *copy; + + memcpy ( dest, orig, sizeof ( dest ) ); + copy = strncpy ( dest, src, 5 ); + ok ( copy == dest ); + ok ( memcmp ( dest, src, 5 ) == 0 ); + ok ( memcmp ( dest + 5, orig + 5, 3 ) == 0 ); + memcpy ( dest, orig, sizeof ( dest ) ); + copy = strncpy ( dest, src, 4 ); + ok ( copy == dest ); + ok ( memcmp ( dest, src, 4 ) == 0 ); + ok ( memcmp ( dest + 4, orig + 4, 4 ) == 0 ); + memcpy ( dest, orig, sizeof ( dest ) ); + copy = strncpy ( dest, src, 8 ); + ok ( copy == dest ); + ok ( memcmp ( dest, src, 5 ) == 0 ); + ok ( memcmp ( dest + 5, zero + 5, 3 ) == 0 ); + memcpy ( dest, orig, sizeof ( dest ) ); + copy = strncpy ( dest, "", 8 ); + ok ( copy == dest ); + ok ( memcmp ( dest, zero, 8 ) == 0 ); + } + + /* Test strcat() */ + { + char buf[16] = "append"; + char *dest; + + dest = strcat ( buf, " this" ); + ok ( dest == buf ); + ok ( strcmp ( buf, "append this" ) == 0 ); + } + + /* Test digit_value() */ + { + unsigned int i; + char buf[2]; + for ( i = 0 ; i < 16 ; i++ ) { + snprintf ( buf, sizeof ( buf ), "%x", i ); + ok ( digit_value ( buf[0] ) == i ); + snprintf ( buf, sizeof ( buf ), "%X", i ); + ok ( digit_value ( buf[0] ) == i ); + } + ok ( digit_value ( 0 ) >= 16 ); + ok ( digit_value ( 9 ) >= 16 ); + ok ( digit_value ( '0' - 1 ) >= 16 ); + ok ( digit_value ( '9' + 1 ) >= 16 ); + ok ( digit_value ( 'A' - 1 ) >= 16 ); + ok ( digit_value ( 'F' + 1 ) >= 16 ); + ok ( digit_value ( 'a' - 1 ) >= 16 ); + ok ( digit_value ( 'f' + 1 ) >= 16 ); + } + + /* Test strtoul() */ + ok ( strtoul ( "12345", NULL, 0 ) == 12345UL ); + ok ( strtoul ( " 741", NULL, 10 ) == 741UL ); + ok ( strtoul ( " 555a", NULL, 0 ) == 555UL ); + ok ( strtoul ( " 555a", NULL, 16 ) == 0x555aUL ); + ok ( strtoul ( "-12", NULL, 0 ) == -12UL ); + ok ( strtoul ( "+3", NULL, 0 ) == 3UL ); + ok ( strtoul ( "721", NULL, 0 ) == 721UL ); + ok ( strtoul ( "721", NULL, 8 ) == 0721UL ); + ok ( strtoul ( "0721", NULL, 0 ) == 0721UL ); + ok ( strtoul ( "", NULL, 0 ) == 0UL ); + ok ( strtoul ( "\t0xcAfe", NULL, 0 ) == 0xcafeUL ); + ok ( strtoul ( "0xffffffff", NULL, 0 ) == 0xffffffffUL ); + { + static const char string[] = "123aHa.world"; + char *endp; + ok ( strtoul ( string, &endp, 0 ) == 123UL ); + ok ( endp == &string[3] ); + ok ( strtoul ( string, &endp, 16 ) == 0x123aUL ); + ok ( endp == &string[4] ); + ok ( strtoul ( string, &endp, 26 ) == + ( ( ( ( ( 1 * 26 + 2 ) * 26 + 3 ) * 26 + 10 ) * 26 + + 17 ) * 26 + 10 ) ); + ok ( endp == &string[6] ); + } } /** String self-test */ diff --git a/qemu/roms/ipxe/src/tests/tcpip_test.c b/qemu/roms/ipxe/src/tests/tcpip_test.c index 00c88ae32..759f886bc 100644 --- a/qemu/roms/ipxe/src/tests/tcpip_test.c +++ b/qemu/roms/ipxe/src/tests/tcpip_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/tests/test.c b/qemu/roms/ipxe/src/tests/test.c index c05e72a76..67bd4cf89 100644 --- a/qemu/roms/ipxe/src/tests/test.c +++ b/qemu/roms/ipxe/src/tests/test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/tests/tests.c b/qemu/roms/ipxe/src/tests/tests.c index 2b4b78c7c..54ce86677 100644 --- a/qemu/roms/ipxe/src/tests/tests.c +++ b/qemu/roms/ipxe/src/tests/tests.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -26,6 +30,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ /* Drag in all applicable self-tests */ +PROVIDE_REQUIRING_SYMBOL(); +REQUIRE_OBJECT ( memset_test ); REQUIRE_OBJECT ( memcpy_test ); REQUIRE_OBJECT ( string_test ); REQUIRE_OBJECT ( math_test ); @@ -37,12 +43,14 @@ REQUIRE_OBJECT ( base16_test ); REQUIRE_OBJECT ( settings_test ); REQUIRE_OBJECT ( time_test ); REQUIRE_OBJECT ( tcpip_test ); +REQUIRE_OBJECT ( ipv4_test ); REQUIRE_OBJECT ( ipv6_test ); REQUIRE_OBJECT ( crc32_test ); REQUIRE_OBJECT ( md5_test ); REQUIRE_OBJECT ( sha1_test ); REQUIRE_OBJECT ( sha256_test ); -REQUIRE_OBJECT ( aes_cbc_test ); +REQUIRE_OBJECT ( sha512_test ); +REQUIRE_OBJECT ( aes_test ); REQUIRE_OBJECT ( hmac_drbg_test ); REQUIRE_OBJECT ( hash_df_test ); REQUIRE_OBJECT ( bigint_test ); @@ -56,3 +64,6 @@ REQUIRE_OBJECT ( png_test ); REQUIRE_OBJECT ( dns_test ); REQUIRE_OBJECT ( uri_test ); REQUIRE_OBJECT ( profile_test ); +REQUIRE_OBJECT ( setjmp_test ); +REQUIRE_OBJECT ( pccrc_test ); +REQUIRE_OBJECT ( linebuf_test ); diff --git a/qemu/roms/ipxe/src/tests/time_test.c b/qemu/roms/ipxe/src/tests/time_test.c index 28acebee6..3bf01dd1d 100644 --- a/qemu/roms/ipxe/src/tests/time_test.c +++ b/qemu/roms/ipxe/src/tests/time_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/tests/uri_test.c b/qemu/roms/ipxe/src/tests/uri_test.c index 14f1b4ad0..da7fb8abe 100644 --- a/qemu/roms/ipxe/src/tests/uri_test.c +++ b/qemu/roms/ipxe/src/tests/uri_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -66,6 +70,8 @@ struct uri_resolve_test { struct uri_tftp_test { /** Next-server address */ struct in_addr next_server; + /** Port number */ + unsigned int port; /** Filename */ const char *filename; /** URI */ @@ -330,7 +336,7 @@ static void uri_tftp_okx ( struct uri_tftp_test *test, const char *file, size_t len; /* Construct URI */ - uri = tftp_uri ( test->next_server, test->filename ); + uri = tftp_uri ( test->next_server, test->port, test->filename ); okx ( uri != NULL, file, line ); if ( uri ) { uri_okx ( uri, &test->uri, file, line ); @@ -674,7 +680,7 @@ static struct uri_resolve_test uri_fragment = { /** TFTP URI with absolute path */ static struct uri_tftp_test uri_tftp_absolute = { - { .s_addr = htonl ( 0xc0a80002 ) /* 192.168.0.2 */ }, + { .s_addr = htonl ( 0xc0a80002 ) /* 192.168.0.2 */ }, 0, "/absolute/path", { .scheme = "tftp", @@ -686,7 +692,7 @@ static struct uri_tftp_test uri_tftp_absolute = { /** TFTP URI with relative path */ static struct uri_tftp_test uri_tftp_relative = { - { .s_addr = htonl ( 0xc0a80003 ) /* 192.168.0.3 */ }, + { .s_addr = htonl ( 0xc0a80003 ) /* 192.168.0.3 */ }, 0, "relative/path", { .scheme = "tftp", @@ -698,7 +704,7 @@ static struct uri_tftp_test uri_tftp_relative = { /** TFTP URI with path containing special characters */ static struct uri_tftp_test uri_tftp_icky = { - { .s_addr = htonl ( 0x0a000006 ) /* 10.0.0.6 */ }, + { .s_addr = htonl ( 0x0a000006 ) /* 10.0.0.6 */ }, 0, "C:\\tftpboot\\icky#path", { .scheme = "tftp", @@ -708,6 +714,19 @@ static struct uri_tftp_test uri_tftp_icky = { "tftp://10.0.0.6/C%3A\\tftpboot\\icky%23path", }; +/** TFTP URI with custom port */ +static struct uri_tftp_test uri_tftp_port = { + { .s_addr = htonl ( 0xc0a80001 ) /* 192.168.0.1 */ }, 4069, + "/another/path", + { + .scheme = "tftp", + .host = "192.168.0.1", + .port = "4069", + .path = "/another/path", + }, + "tftp://192.168.0.1:4069/another/path", +}; + /** Current working URI test */ static struct uri_churi_test uri_churi[] = { { @@ -842,6 +861,7 @@ static void uri_test_exec ( void ) { uri_tftp_ok ( &uri_tftp_absolute ); uri_tftp_ok ( &uri_tftp_relative ); uri_tftp_ok ( &uri_tftp_icky ); + uri_tftp_ok ( &uri_tftp_port ); /* Current working URI tests */ uri_churi_ok ( uri_churi ); diff --git a/qemu/roms/ipxe/src/tests/vsprintf_test.c b/qemu/roms/ipxe/src/tests/vsprintf_test.c index 11512ec8e..0ad4f1c56 100644 --- a/qemu/roms/ipxe/src/tests/vsprintf_test.c +++ b/qemu/roms/ipxe/src/tests/vsprintf_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/tests/x509_test.c b/qemu/roms/ipxe/src/tests/x509_test.c index fd39e12d2..658d5247c 100644 --- a/qemu/roms/ipxe/src/tests/x509_test.c +++ b/qemu/roms/ipxe/src/tests/x509_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -1105,6 +1109,7 @@ struct self_test x509_test __self_test = { }; /* Drag in algorithms required for tests */ +REQUIRING_SYMBOL ( x509_test ); REQUIRE_OBJECT ( rsa ); REQUIRE_OBJECT ( sha1 ); REQUIRE_OBJECT ( sha256 ); diff --git a/qemu/roms/ipxe/src/usr/autoboot.c b/qemu/roms/ipxe/src/usr/autoboot.c index 47476ae40..912543828 100644 --- a/qemu/roms/ipxe/src/usr/autoboot.c +++ b/qemu/roms/ipxe/src/usr/autoboot.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -42,6 +46,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include /** @file * @@ -101,7 +106,7 @@ static struct uri * parse_next_server_and_filename ( struct in_addr next_server, /* Construct a TFTP URI for the filename, if applicable */ if ( next_server.s_addr && filename[0] && ! uri_is_absolute ( uri ) ) { uri_put ( uri ); - uri = tftp_uri ( next_server, filename ); + uri = tftp_uri ( next_server, 0, filename ); if ( ! uri ) return NULL; } @@ -173,6 +178,7 @@ int uriboot ( struct uri *filename, struct uri *root_path, int drive, if ( filename ) { if ( ( rc = imgdownload ( filename, 0, &image ) ) != 0 ) goto err_download; + imgstat ( image ); image->flags |= IMAGE_AUTO_UNREGISTER; if ( ( rc = image_exec ( image ) ) != 0 ) { printf ( "Could not boot image: %s\n", @@ -434,9 +440,14 @@ int netboot ( struct net_device *netdev ) { * @ret is_autoboot Network device matches the autoboot device */ static int is_autoboot_busloc ( struct net_device *netdev ) { + struct device *dev; - return ( ( netdev->dev->desc.bus_type == autoboot_desc.bus_type ) && - ( netdev->dev->desc.location == autoboot_desc.location ) ); + for ( dev = netdev->dev ; dev ; dev = dev->parent ) { + if ( ( dev->desc.bus_type == autoboot_desc.bus_type ) && + ( dev->desc.location == autoboot_desc.location ) ) + return 1; + } + return 0; } /** @@ -522,7 +533,8 @@ static int shell_banner ( void ) { /* Prompt user */ printf ( "\n" ); - return ( prompt ( "Press Ctrl-B for the iPXE command line...", + return ( prompt ( "Press Ctrl-B for the " PRODUCT_SHORT_NAME + " command line...", ( ( BANNER_TIMEOUT * TICKS_PER_SEC ) / 10 ), CTRL_B ) == 0 ); } @@ -531,28 +543,29 @@ static int shell_banner ( void ) { * Main iPXE flow of execution * * @v netdev Network device, or NULL + * @ret rc Return status code */ -void ipxe ( struct net_device *netdev ) { +int ipxe ( struct net_device *netdev ) { struct feature *feature; struct image *image; char *scriptlet; + int rc; /* * Print welcome banner * * * If you wish to brand this build of iPXE, please do so by - * defining the string PRODUCT_NAME in config/general.h. + * defining the string PRODUCT_NAME in config/branding.h. * * While nothing in the GPL prevents you from removing all * references to iPXE or http://ipxe.org, we prefer you not to * do so. * */ - printf ( NORMAL "\n\n%s\n" BOLD "iPXE %s" - NORMAL " -- Open Source Network Boot Firmware -- " - CYAN "http://ipxe.org" NORMAL "\n" - "Features:", product_name, product_version ); + printf ( NORMAL "\n\n" PRODUCT_NAME "\n" BOLD PRODUCT_SHORT_NAME " %s" + NORMAL " -- " PRODUCT_TAG_LINE " -- " + CYAN PRODUCT_URI NORMAL "\nFeatures:", product_version ); for_each_table_entry ( feature, FEATURES ) printf ( " %s", feature->name ); printf ( "\n" ); @@ -560,28 +573,30 @@ void ipxe ( struct net_device *netdev ) { /* Boot system */ if ( ( image = first_image() ) != NULL ) { /* We have an embedded image; execute it */ - image_exec ( image ); + return image_exec ( image ); } else if ( shell_banner() ) { /* User wants shell; just give them a shell */ - shell(); + return shell(); } else { fetch_string_setting_copy ( NULL, &scriptlet_setting, &scriptlet ); if ( scriptlet ) { /* User has defined a scriptlet; execute it */ - system ( scriptlet ); + rc = system ( scriptlet ); free ( scriptlet ); + return rc; } else { /* Try booting. If booting fails, offer the * user another chance to enter the shell. */ if ( netdev ) { - netboot ( netdev ); + rc = netboot ( netdev ); } else { - autoboot(); + rc = autoboot(); } if ( shell_banner() ) - shell(); + rc = shell(); + return rc; } } } diff --git a/qemu/roms/ipxe/src/usr/dhcpmgmt.c b/qemu/roms/ipxe/src/usr/dhcpmgmt.c index 23982b19c..dcb360b23 100644 --- a/qemu/roms/ipxe/src/usr/dhcpmgmt.c +++ b/qemu/roms/ipxe/src/usr/dhcpmgmt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/usr/fcmgmt.c b/qemu/roms/ipxe/src/usr/fcmgmt.c index a30f37a71..6f626143f 100644 --- a/qemu/roms/ipxe/src/usr/fcmgmt.c +++ b/qemu/roms/ipxe/src/usr/fcmgmt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/usr/ifmgmt.c b/qemu/roms/ipxe/src/usr/ifmgmt.c index 3d05895c2..aefdaa45d 100644 --- a/qemu/roms/ipxe/src/usr/ifmgmt.c +++ b/qemu/roms/ipxe/src/usr/ifmgmt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -99,11 +103,12 @@ static void ifstat_errors ( struct net_device_stats *stats, */ void ifstat ( struct net_device *netdev ) { printf ( "%s: %s using %s on %s (%s)\n" - " [Link:%s, TX:%d TXE:%d RX:%d RXE:%d]\n", + " [Link:%s%s, TX:%d TXE:%d RX:%d RXE:%d]\n", netdev->name, netdev_addr ( netdev ), netdev->dev->driver_name, netdev->dev->name, ( netdev_is_open ( netdev ) ? "open" : "closed" ), ( netdev_link_ok ( netdev ) ? "up" : "down" ), + ( netdev_link_blocked ( netdev ) ? " (blocked)" : "" ), netdev->tx_stats.good, netdev->tx_stats.bad, netdev->rx_stats.good, netdev->rx_stats.bad ); if ( ! netdev_link_ok ( netdev ) ) { diff --git a/qemu/roms/ipxe/src/usr/imgmgmt.c b/qemu/roms/ipxe/src/usr/imgmgmt.c index c9c571640..352dd0242 100644 --- a/qemu/roms/ipxe/src/usr/imgmgmt.c +++ b/qemu/roms/ipxe/src/usr/imgmgmt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/usr/imgtrust.c b/qemu/roms/ipxe/src/usr/imgtrust.c index da7ff2ef0..a269833a6 100644 --- a/qemu/roms/ipxe/src/usr/imgtrust.c +++ b/qemu/roms/ipxe/src/usr/imgtrust.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/usr/ipstat.c b/qemu/roms/ipxe/src/usr/ipstat.c index 95ad799dc..0f09cc2ff 100644 --- a/qemu/roms/ipxe/src/usr/ipstat.c +++ b/qemu/roms/ipxe/src/usr/ipstat.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/usr/lotest.c b/qemu/roms/ipxe/src/usr/lotest.c index ad7a2fad7..6b328713c 100644 --- a/qemu/roms/ipxe/src/usr/lotest.c +++ b/qemu/roms/ipxe/src/usr/lotest.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/usr/neighmgmt.c b/qemu/roms/ipxe/src/usr/neighmgmt.c index e4d21a208..9fd88f82b 100644 --- a/qemu/roms/ipxe/src/usr/neighmgmt.c +++ b/qemu/roms/ipxe/src/usr/neighmgmt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/usr/pingmgmt.c b/qemu/roms/ipxe/src/usr/pingmgmt.c index 16b3ec994..bb33c5d47 100644 --- a/qemu/roms/ipxe/src/usr/pingmgmt.c +++ b/qemu/roms/ipxe/src/usr/pingmgmt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/usr/profstat.c b/qemu/roms/ipxe/src/usr/profstat.c index 991427473..d80fa26b2 100644 --- a/qemu/roms/ipxe/src/usr/profstat.c +++ b/qemu/roms/ipxe/src/usr/profstat.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/usr/prompt.c b/qemu/roms/ipxe/src/usr/prompt.c index 957b4ab3d..fca0a157c 100644 --- a/qemu/roms/ipxe/src/usr/prompt.c +++ b/qemu/roms/ipxe/src/usr/prompt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/usr/pxemenu.c b/qemu/roms/ipxe/src/usr/pxemenu.c index b69905df1..2d05d3f51 100644 --- a/qemu/roms/ipxe/src/usr/pxemenu.c +++ b/qemu/roms/ipxe/src/usr/pxemenu.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/usr/route.c b/qemu/roms/ipxe/src/usr/route.c index ba4cc3221..690ba3b6b 100644 --- a/qemu/roms/ipxe/src/usr/route.c +++ b/qemu/roms/ipxe/src/usr/route.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include @@ -42,3 +46,7 @@ void route ( void ) { } } } + +/* Drag in routing management configuration */ +REQUIRING_SYMBOL ( route ); +REQUIRE_OBJECT ( config_route ); diff --git a/qemu/roms/ipxe/src/usr/route_ipv4.c b/qemu/roms/ipxe/src/usr/route_ipv4.c index b4d1b7bf3..6260335ac 100644 --- a/qemu/roms/ipxe/src/usr/route_ipv4.c +++ b/qemu/roms/ipxe/src/usr/route_ipv4.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/usr/route_ipv6.c b/qemu/roms/ipxe/src/usr/route_ipv6.c index 6045f85bb..9e94b4a15 100644 --- a/qemu/roms/ipxe/src/usr/route_ipv6.c +++ b/qemu/roms/ipxe/src/usr/route_ipv6.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/usr/sync.c b/qemu/roms/ipxe/src/usr/sync.c index f7a04c44c..f599588ae 100644 --- a/qemu/roms/ipxe/src/usr/sync.c +++ b/qemu/roms/ipxe/src/usr/sync.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include diff --git a/qemu/roms/ipxe/src/util/Option/ROM.pm b/qemu/roms/ipxe/src/util/Option/ROM.pm index 6c396730e..232cf16b8 100644 --- a/qemu/roms/ipxe/src/util/Option/ROM.pm +++ b/qemu/roms/ipxe/src/util/Option/ROM.pm @@ -529,6 +529,26 @@ sub new { return $hash; } +sub device_list { + my $hash = shift; + my $self = tied(%$hash); + + my $device_list = $hash->{device_list}; + return undef unless $device_list; + + my @ids; + my $offset = ( $self->{offset} + $device_list ); + while ( 1 ) { + my $raw = substr ( ${$self->{data}}, $offset, 2 ); + my $id = unpack ( "S", $raw ); + last unless $id; + push @ids, $id; + $offset += 2; + } + + return @ids; +} + ############################################################################## # # Option::ROM::PnP diff --git a/qemu/roms/ipxe/src/util/disrom.pl b/qemu/roms/ipxe/src/util/disrom.pl index 574957acd..920a86b24 100755 --- a/qemu/roms/ipxe/src/util/disrom.pl +++ b/qemu/roms/ipxe/src/util/disrom.pl @@ -55,6 +55,10 @@ do { printf " %-16s %s\n", "Signature:", $pci->{signature}; printf " %-16s 0x%04x\n", "Vendor ID:", $pci->{vendor_id}; printf " %-16s 0x%04x\n", "Device ID:", $pci->{device_id}; + if ( $pci->{device_list} ) { + printf " %-16s %s\n", "Device list:", + ( join ( ", ", map { sprintf "0x%04x", $_ } $pci->device_list ) ); + } printf " %-16s 0x%02x%02x%02x\n", "Device class:", $pci->{base_class}, $pci->{sub_class}, $pci->{prog_intf}; printf " %-16s 0x%04x (%d)\n", "Image length:", diff --git a/qemu/roms/ipxe/src/util/elf2efi.c b/qemu/roms/ipxe/src/util/elf2efi.c index 45d539574..e68fa5d14 100644 --- a/qemu/roms/ipxe/src/util/elf2efi.c +++ b/qemu/roms/ipxe/src/util/elf2efi.c @@ -478,11 +478,13 @@ static void process_reloc ( bfd *bfd __attribute__ (( unused )), /* Skip absolute symbols; the symbol value won't * change when the object is loaded. */ + } else if ( ( strcmp ( howto->name, "R_386_NONE" ) == 0 ) || + ( strcmp ( howto->name, "R_X86_64_NONE" ) == 0 ) ) { + /* Ignore dummy relocations used by REQUIRE_SYMBOL() */ } else if ( strcmp ( howto->name, "R_X86_64_64" ) == 0 ) { /* Generate an 8-byte PE relocation */ generate_pe_reloc ( pe_reltab, offset, 8 ); - } else if ( ( strcmp ( howto->name, "R_386_32" ) == 0 ) || - ( strcmp ( howto->name, "R_X86_64_32" ) == 0 ) ) { + } else if ( strcmp ( howto->name, "R_386_32" ) == 0 ) { /* Generate a 4-byte PE relocation */ generate_pe_reloc ( pe_reltab, offset, 4 ); } else if ( strcmp ( howto->name, "R_386_16" ) == 0 ) { diff --git a/qemu/roms/ipxe/src/util/licence.pl b/qemu/roms/ipxe/src/util/licence.pl index 0e43c7b4c..79e70fd65 100755 --- a/qemu/roms/ipxe/src/util/licence.pl +++ b/qemu/roms/ipxe/src/util/licence.pl @@ -37,6 +37,7 @@ my $known_licences = { desc => "GPL version 2 (or, at your option, any later version)", can_subsume => { gpl_any => 1, + gpl2_or_later_or_ubdl => 1, public_domain => 1, bsd3 => 1, bsd2 => 1, @@ -49,6 +50,7 @@ my $known_licences = { can_subsume => { gpl_any => 1, gpl2_or_later => 1, + gpl2_or_later_or_ubdl => 1, public_domain => 1, bsd3 => 1, bsd2 => 1, @@ -56,6 +58,17 @@ my $known_licences = { isc => 1, }, }, + gpl2_or_later_or_ubdl => { + desc => ( "GPL version 2 (or, at your option, any later version) or ". + "Unmodified Binary Distribution Licence" ), + can_subsume => { + public_domain => 1, + bsd3 => 1, + bsd2 => 1, + mit => 1, + isc => 1, + }, + }, public_domain => { desc => "Public Domain", can_subsume => {}, diff --git a/qemu/roms/ipxe/src/util/parserom.pl b/qemu/roms/ipxe/src/util/parserom.pl index e278e6336..28df60652 100755 --- a/qemu/roms/ipxe/src/util/parserom.pl +++ b/qemu/roms/ipxe/src/util/parserom.pl @@ -1,66 +1,260 @@ #!/usr/bin/env perl # -# Parse PCI_ROM and ISA_ROM entries from a source file on stdin and -# output the relevant Makefile variable definitions to stdout +# Parse PCI_ROM and ISA_ROM entries from source file(s) specified as +# arguments and output the relevant Makefile rules to STDOUT. # -# Based upon portions of Ken Yap's genrules.pl +# Originally based on portions of Ken Yap's genrules.pl. Completely +# rewritten by Robin Smidsrød to be more maintainable. use strict; use warnings; +use Getopt::Long; -die "Syntax: $0 driver_source.c" unless @ARGV == 1; -my $source = shift; -open DRV, "<$source" or die "Could not open $source: $!\n"; +# Parse command-line options +my @exclude_driver_classes = (); +my @exclude_drivers = (); +my $debug = 0; +my $help = 0; +GetOptions( + "exclude-driver-class=s" => \@exclude_driver_classes, + "exclude-driver=s" => \@exclude_drivers, + "debug" => \$debug, + "help" => \$help, +); -( my $family, my $driver_name ) = ( $source =~ /^(.*?([^\/]+))\..$/ ) - or die "Could not parse source file name \"$source\"\n"; +# Convert exclution arrays to lookup tables +my $exclude_driver_class_map = { map { $_ => 1 } @exclude_driver_classes }; +my $exclude_driver_map = { map { $_ => 1 } @exclude_drivers }; -my $printed_family; +# Ensure STDOUT and STDERR are synchronized if debugging +if ( $debug ) { + STDOUT->autoflush(1); + STDERR->autoflush(1); +} + +# Compile regular expressions here for slight performance boost +my %RE = ( + 'parse_driver_class' => qr{ drivers/ (\w+?) / }x, + 'parse_family' => qr{^ (?:\./)? (.*) \..+? $}x, + 'find_rom_line' => qr/^ \s* ( (PCI|ISA)_ROM \s* \( \s* (.*?) ) $/x, + 'extract_pci_id' => qr/^ \s* 0x([0-9A-Fa-f]{4}) \s* ,? \s* (.*) $/x, + 'extract_quoted_string' => qr/^ \s* \" ([^\"]*?) \" \s* ,? \s* (.*) $/x, +); + +# Show help if required arguments are missing or help was requested +show_usage_and_exit() if $help or @ARGV < 1; + +# Process each source file specified +process_source_file($_) for @ARGV; + +exit; + +sub show_usage_and_exit { + print STDERR <<"EOM"; +Syntax: $0 [] [] +Options: + --exclude-driver-class Exclude specified driver classes + --exclude-driver Exclude specified drivers + --debug Output debug information on STDERR + --help This help information +EOM + exit 1; +} + +# Figure out if source file is a driver and look for ROM declarations +sub process_source_file { + my ($source_file) = @_; + return unless defined $source_file; + return unless length $source_file; + my $state = { 'source_file' => $source_file }; + log_debug("SOURCE_FILE", $state->{source_file}); + # Skip source files that aren't drivers + parse_driver_class( $state ); + unless ( $state->{'driver_class'} ) { + log_debug("SKIP_NOT_DRIVER", $state->{source_file} ); + return; + } + # Skip source files with driver classes that are explicitly excluded + if ( $exclude_driver_class_map->{ $state->{'driver_class'} } ) { + log_debug("SKIP_EXCL_CLASS", $state->{'driver_class'} ); + return; + } + # Skip source files without driver information + parse_family( $state ); + parse_driver_name( $state ); + unless ( $state->{'family'} and $state->{'driver_name'} ) { + log_debug("SKIP_NO_DRV_INFO", $state->{source_file} ); + return; + } + # Skip source files with drivers that are explicitly excluded + if ( $exclude_driver_map->{ $state->{'driver_name'} } ) { + log_debug("SKIP_EXCL_DRV", $state->{'driver_name'} ); + return; + } + # Iterate through lines in source files looking for ROM declarations + # and # output Makefile rules + open( my $fh, "<", $state->{'source_file'} ) + or die "Couldn't open $state->{source_file}: $!\n"; + while (<$fh>) { + process_rom_decl($state, $1, $2, $3) if m/$RE{find_rom_line}/; + } + close($fh) or die "Couldn't close $source_file: $!\n"; + return 1; +} + +# Verify that the found ROM declaration is sane and dispatch to the right +# handler depending on type +sub process_rom_decl { + my ($state, $rom_line, $rom_type, $rom_decl) = @_; + return unless defined $rom_line; + return unless length $rom_line; + log_debug("ROM_LINE", $rom_line); + return unless defined $rom_type; + return unless length $rom_type; + log_debug("ROM_TYPE", $rom_type); + $state->{'type'} = lc $rom_type; + return process_pci_rom($state, $rom_decl) if $rom_type eq "PCI"; + return process_isa_rom($state, $rom_decl) if $rom_type eq "ISA"; + return; +} + +# Extract values from PCI_ROM declaration lines and dispatch to +# Makefile rule generator +sub process_pci_rom { + my ($state, $decl) = @_; + return unless defined $decl; + return unless length $decl; + (my $vendor, $decl) = extract_pci_id($decl, 'PCI_VENDOR'); + (my $device, $decl) = extract_pci_id($decl, 'PCI_DEVICE'); + (my $image, $decl) = extract_quoted_string($decl, 'IMAGE'); + (my $desc, $decl) = extract_quoted_string($decl, 'DESCRIPTION'); + if ( $vendor and $device and $image and $desc ) { + print_make_rules( $state, "${vendor}${device}", $desc, $vendor, $device ); + print_make_rules( $state, $image, $desc, $vendor, $device, 1 ); + } + else { + log_debug("WARNING", "Malformed PCI_ROM macro on line $. of $state->{source_file}"); + } + return 1; +} + +# Extract values from ISA_ROM declaration lines and dispatch to +# Makefile rule generator +sub process_isa_rom { + my ($state, $decl) = @_; + return unless defined $decl; + return unless length $decl; + (my $image, $decl) = extract_quoted_string($decl, 'IMAGE'); + (my $desc, $decl) = extract_quoted_string($decl, 'DESCRIPTION'); + if ( $image and $desc ) { + print_make_rules( $state, $image, $desc ); + } + else { + log_debug("WARNING", "Malformed ISA_ROM macro on line $. of $state->{source_file}"); + } + return 1; +} -sub rom { - ( my $type, my $image, my $desc, my $vendor, my $device, my $dup ) = @_; - my $ids = $vendor ? "$vendor,$device" : "-"; - unless ( $printed_family ) { +# Output Makefile rules for the specified ROM declarations +sub print_make_rules { + my ( $state, my $image, my $desc, my $vendor, my $device, my $dup ) = @_; + unless ( $state->{'is_header_printed'} ) { + print "# NIC\t\n"; + print "# NIC\tfamily\t$state->{family}\n"; + print "DRIVERS_$state->{driver_class} += $state->{driver_name}\n"; + print "DRIVERS += $state->{driver_name}\n"; + print "\n"; + $state->{'is_header_printed'} = 1; + } + return if $vendor and ( $vendor eq "ffff" or $device eq "ffff" ); + my $ids = $vendor ? "$vendor,$device" : "-"; + print "# NIC\t$image\t$ids\t$desc\n"; + print "DRIVER_$image = $state->{driver_name}\n"; + print "ROM_TYPE_$image = $state->{type}\n"; + print "ROM_DESCRIPTION_$image = \"$desc\"\n"; + print "PCI_VENDOR_$image = 0x$vendor\n" if $vendor; + print "PCI_DEVICE_$image = 0x$device\n" if $device; + print "ROMS += $image\n" unless $dup; + print "ROMS_$state->{driver_name} += $image\n" unless $dup; print "\n"; - print "# NIC\t\n"; - print "# NIC\tfamily\t$family\n"; - print "DRIVERS += $driver_name\n"; - $printed_family = 1; - } - print "\n"; - return if ( $vendor && ( ( $vendor eq "ffff" ) || ( $device eq "ffff" ) ) ); - print "# NIC\t$image\t$ids\t$desc\n"; - print "DRIVER_$image = $driver_name\n"; - print "ROM_TYPE_$image = $type\n"; - print "ROM_DESCRIPTION_$image = \"$desc\"\n"; - print "PCI_VENDOR_$image = 0x$vendor\n" if $vendor; - print "PCI_DEVICE_$image = 0x$device\n" if $device; - print "ROMS += $image\n" unless $dup; - print "ROMS_$driver_name += $image\n" unless $dup; + return 1; +} + +# Driver class is whatever comes after the "drivers" part of the filename (relative path) +sub parse_driver_class { + my ($state) = @_; + my $filename = $state->{'source_file'}; + return unless defined $filename; + return unless length $filename; + if ( $filename =~ m/$RE{parse_driver_class}/ ) { + log_debug("DRIVER_CLASS", $1); + $state->{'driver_class'} = $1; + } + return; +} + +# Family name is filename (relative path) without extension +sub parse_family { + my ($state) = @_; + my $filename = $state->{'source_file'}; + return unless defined $filename; + return unless length $filename; + if ( $filename =~ m/$RE{parse_family}/ ) { + log_debug("FAMILY", $1); + $state->{'family'} = $1; + } + return; +} + +# Driver name is last part of family name +sub parse_driver_name { + my ($state) = @_; + my $family = $state->{'family'}; + return unless defined $family; + return unless length $family; + my @parts = split "/", $family; + $state->{'driver_name'} = $parts[-1]; + log_debug("DRIVER", $state->{'driver_name'}); + return; } -while ( ) { - next unless /(PCI|ISA)_ROM\s*\(/; - - if ( /^\s*PCI_ROM\s*\( - \s*0x([0-9A-Fa-f]{4})\s*, # PCI vendor - \s*0x([0-9A-Fa-f]{4})\s*, # PCI device - \s*\"([^\"]*)\"\s*, # Image - \s*\"([^\"]*)\"\s*, # Description - \s*.*\s* # Driver data - \)/x ) { - ( my $vendor, my $device, my $image, my $desc ) = ( lc $1, lc $2, $3, $4 ); - rom ( "pci", lc "${vendor}${device}", $desc, $vendor, $device ); - rom ( "pci", $image, $desc, $vendor, $device, 1 ); - } elsif ( /^\s*ISA_ROM\s*\( - \s*\"([^\"]*)\"\s*, # Image - \s*\"([^\"]*)\"\s* # Description - \)/x ) { - ( my $image, my $desc ) = ( $1, $2 ); - rom ( "isa", $image, $desc ); - } else { - warn "Malformed PCI_ROM or ISA_ROM macro on line $. of $source\n"; - } +# Extract a PCI vendor/device ID e.g. 0x8086, possibly followed by a comma +# Should always be 4-digit lower-case hex number +sub extract_pci_id { + my ($str, $label) = @_; + return "", $str unless defined $str; + return "", $str unless length $str; + if ( $str =~ m/$RE{extract_pci_id}/ ) { + my $id = lc $1; + log_debug($label, $id); + return $id, $2; + } + return "", $str; } -close DRV; +# Extract a double-quoted string, possibly followed by a comma +sub extract_quoted_string { + my ($str, $label) = @_; + return "", $str unless defined $str; + return "", $str unless length $str; + if ( $str =~ m/$RE{extract_quoted_string}/ ) { + log_debug($label, $1); + return $1, $2; + } + return "", $str; +} + +# Output debug info to STDERR (off by default) +sub log_debug { + my ($label, $str) = @_; + return unless $debug; + return unless defined $str; + print STDERR "\n" if $label eq 'SOURCE_FILE'; + print STDERR "="; + if ( defined $label ) { + my $pad_count = 16 - length $label; + print STDERR $label . ":" . ( " " x $pad_count ); + } + print STDERR $str . "\n"; + return; +} diff --git a/qemu/roms/ipxe/src/util/relicense.pl b/qemu/roms/ipxe/src/util/relicense.pl new file mode 100755 index 000000000..41954c1b3 --- /dev/null +++ b/qemu/roms/ipxe/src/util/relicense.pl @@ -0,0 +1,169 @@ +#!/usr/bin/perl -w + +=head1 NAME + +relicense.pl + +=head1 SYNOPSIS + +relicense.pl [options] -p [...] + +Option: + + -p,--permitted=FILE Specify file of emails with relicensing permission + -f,--force Manually force relicensing + -h,--help Display brief help message + -v,--verbose Increase verbosity + -q,--quiet Decrease verbosity + +=cut + +use File::Slurp; +use IPC::Run qw ( run ); +use Getopt::Long; +use Pod::Usage; +use strict; +use warnings; + +# Parse command-line options +my $verbosity = 0; +my $permfile; +my $force; +Getopt::Long::Configure ( "bundling", "auto_abbrev" ); +GetOptions ( + 'permitted|p=s' => \$permfile, + 'force|f' => \$force, + 'verbose|v+' => sub { $verbosity++; }, + 'quiet|q+' => sub { $verbosity--; }, + 'help|h' => sub { pod2usage ( 1 ); }, +) or die "Could not parse command-line options"; +pod2usage ( 1 ) unless @ARGV; + +# Read permitted emails file +my @emails = ( $permfile ? read_file ( $permfile ) : () ); +chomp @emails; +my $permitted = { map { /^.*<(\S+)>$/; ( $1 || $_ ) => 1 } @emails }; + +# Define list of relicensable licences +my $relicensable = { + GPL2_OR_LATER => 1, +}; + +# Define blurb to be added to copyright notice +my $blurb = ' + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements.'; + +# Process files +my @succeeded; +my @failed; +while ( my $filename = shift @ARGV ) { + + # Read file to determine existing licence + my $file = read_file ( $filename ); + my @licences = ( $file =~ /^\s*FILE_LICENCE\s*\(\s*(\S+)\s*\)\s*;?$/mg ); + die "No licence declaration in $filename\n" unless @licences; + die "Multiple licence declarations in $filename\n" if @licences > 1; + my $licence = $licences[0]; + + # Skip if file is already UBDL-licensed + next if $licence =~ /_OR_UBDL$/; + + # Fail immediately if file is not a candidate for relicensing + if ( ! exists $relicensable->{$licence} ) { + print "Non-relicensable licence $licence in $filename\n"; + push @failed, $filename; + next; + } + + # Run git-blame + my $stdout; + my $stderr; + run [ "git", "blame", "-M", "-C", "-p", "-w", $filename ], + \undef, \$stdout, \$stderr + or die "git-blame $filename: $?"; + die $stderr if $stderr; + + # Process output + my @stdout = split ( /\n/, $stdout ); + chomp @stdout; + my $details = {}; + my $failures = 0; + while ( @stdout ) { + + # Parse output + my $commit_line = shift @stdout; + ( my $commit, undef, my $lineno, undef, my $count ) = + ( $commit_line =~ + /^([0-9a-f]{40})\s+([0-9]+)\s+([0-9]+)(\s+([0-9]+))?$/ ) + or die "Malformed commit line \"$commit_line\"\n"; + if ( $count ) { + $details->{$commit} ||= {}; + while ( ! ( $stdout[0] =~ /^\t/ ) ) { + my $detail_line = shift @stdout; + ( my $key, undef, my $value ) = + ( $detail_line =~ /^([a-z-]+)(\s+(.+))?$/ ) + or die "Malformed detail line \"$detail_line\" for $commit_line\n"; + $details->{$commit}->{$key} = $value; + } + } + die "Missing commit details for $commit_line\n" + unless %{$details->{$commit}}; + my $code_line = shift @stdout; + ( my $line ) = ( $code_line =~ /^\t(.*)$/ ) + or die "Malformed code line \"$code_line\" for $commit_line\n"; + + # Skip trivial lines and lines so common that they are likely to + # be misattributed by git-blame + next if $line =~ /^\s*$/; # Empty lines + next if $line =~ /^\s*\/\*/; # Start of comments + next if $line =~ /^\s*\*/; # Middle (or end) of comments + next if $line =~ /^\s*\{\s*$/; # Standalone opening braces + next if $line =~ /^\s*\};?\s*$/; # Standalone closing braces + next if $line =~ /^\#include/; # Header inclusions + next if $line =~ /^\s*return\s+0;/; # return 0; + next if $line =~ /^\s*return\s+rc;/; # return rc; + next if $line =~ /^\s*PCI_ROM\s*\(.*\)\s*,\s*$/; # PCI IDs + next if $line =~ /^\s*FILE_LICENCE\s*\(.*\)\s*;$/; # Licence declarations + + # Identify author + my $author_mail = $details->{$commit}->{"author-mail"} + or die "Missing author email for $commit_line\n"; + ( my $email ) = ( $author_mail =~ /^<(\S+)>$/ ) + or die "Malformed author email \"$author_mail\" for $commit_line\n"; + undef $email if exists $details->{$commit}->{boundary}; + + # Check for relicensing permission + next if defined $email && exists $permitted->{$email}; + + # Print out lines lacking permission + printf $filename."\n" unless $failures; + printf "%4d %-30s %s\n", $lineno, ( $email || "" ), $line; + $failures++; + } + + # Fail if there are any non-trivial lines lacking relicensing permission + if ( $failures && ! $force ) { + push @failed, $filename; + next; + } + + # Modify FILE_LICENCE() line + $file =~ s/(^\s*FILE_LICENCE\s*\(\s*${licence})(\s*\)\s*;?$)/$1_OR_UBDL$2/m + or die "Could not modify FILE_LICENCE() in $filename\n"; + + # Modify copyright notice, if present + if ( $file =~ /GNU General Public License/i ) { + $file =~ s/(02110-1301, USA.$)/$1${blurb}/m + or die "Could not modify copyright notice in $filename\n"; + } + + # Write out modified file + write_file ( $filename, { atomic => 1 }, $file ); + push @succeeded, $filename; +} + +print "Relicensed: ".join ( " ", @succeeded )."\n" if @succeeded; +die "Cannot relicense: ".join ( " ", @failed )."\n" if @failed; diff --git a/qemu/roms/ipxe/src/util/zbin.c b/qemu/roms/ipxe/src/util/zbin.c index 3b7cf95b3..1862a3827 100644 --- a/qemu/roms/ipxe/src/util/zbin.c +++ b/qemu/roms/ipxe/src/util/zbin.c @@ -1,13 +1,21 @@ +#include #include +#include +#include +#include #include - -#define ENCODE -#define VERBOSE -#include "nrv2b.c" -FILE *infile, *outfile; +#include #define DEBUG 0 +/* LZMA filter choices. Must match those used by unlzma.S */ +#define LZMA_LC 2 +#define LZMA_LP 0 +#define LZMA_PB 0 + +/* LZMA preset choice. This is a policy decision */ +#define LZMA_PRESET ( LZMA_PRESET_DEFAULT | LZMA_PRESET_EXTREME ) + struct input_file { void *buf; size_t len; @@ -177,13 +185,75 @@ static int process_zinfo_copy ( struct input_file *input, return 0; } +#define OPCODE_CALL 0xe8 +#define OPCODE_JMP 0xe9 + +static void bcj_filter ( void *data, size_t len ) { + struct { + uint8_t opcode; + int32_t target; + } __attribute__ (( packed )) *jump; + ssize_t limit = ( len - sizeof ( *jump ) ); + ssize_t offset; + + /* liblzma does include an x86 BCJ filter, but it's hideously + * convoluted and undocumented. This BCJ filter is + * substantially simpler and achieves the same compression (at + * the cost of requiring the decompressor to know the size of + * the decompressed data, which we already have in iPXE). + */ + for ( offset = 0 ; offset <= limit ; offset++ ) { + jump = ( data + offset ); + + /* Skip instructions that are not followed by a rel32 address */ + if ( ( jump->opcode != OPCODE_CALL ) && + ( jump->opcode != OPCODE_JMP ) ) + continue; + + /* Convert rel32 address to an absolute address. To + * avoid false positives (which damage the compression + * ratio), we should check that the jump target is + * within the range [0,limit). + * + * Some output values would then end up being mapped + * from two distinct input values, making the + * transformation irreversible. To solve this, we + * transform such values back into the part of the + * range which would otherwise correspond to no input + * values. + */ + if ( ( jump->target >= -offset ) && + ( jump->target < ( limit - offset ) ) ) { + /* Convert relative addresses in the range + * [-offset,limit-offset) to absolute + * addresses in the range [0,limit). + */ + jump->target += offset; + } else if ( ( jump->target >= ( limit - offset ) ) && + ( jump->target < limit ) ) { + /* Convert positive numbers in the range + * [limit-offset,limit) to negative numbers in + * the range [-offset,0). + */ + jump->target -= limit; + } + offset += sizeof ( jump->target ); + }; +} + static int process_zinfo_pack ( struct input_file *input, struct output_file *output, union zinfo_record *zinfo ) { struct zinfo_pack *pack = &zinfo->pack; size_t offset = pack->offset; size_t len = pack->len; - unsigned long packed_len; + size_t packed_len = 0; + size_t remaining = ( output->max_len - output->len ); + lzma_options_lzma options; + const lzma_filter filters[] = { + { .id = LZMA_FILTER_LZMA1, .options = &options }, + { .id = LZMA_VLI_UNKNOWN } + }; if ( ( offset + len ) > input->len ) { fprintf ( stderr, "Input buffer overrun on pack\n" ); @@ -196,9 +266,15 @@ static int process_zinfo_pack ( struct input_file *input, return -1; } - if ( ucl_nrv2b_99_compress ( ( input->buf + offset ), len, - ( output->buf + output->len ), - &packed_len, 0 ) != UCL_E_OK ) { + bcj_filter ( ( input->buf + offset ), len ); + + lzma_lzma_preset ( &options, LZMA_PRESET ); + options.lc = LZMA_LC; + options.lp = LZMA_LP; + options.pb = LZMA_PB; + if ( lzma_raw_buffer_encode ( filters, NULL, ( input->buf + offset ), + len, ( output->buf + output->len ), + &packed_len, remaining ) != LZMA_OK ) { fprintf ( stderr, "Compression failure\n" ); return -1; } @@ -206,7 +282,7 @@ static int process_zinfo_pack ( struct input_file *input, if ( DEBUG ) { fprintf ( stderr, "PACK [%#zx,%#zx) to [%#zx,%#zx)\n", offset, ( offset + len ), output->len, - ( size_t )( output->len + packed_len ) ); + ( output->len + packed_len ) ); } output->len += packed_len; diff --git a/qemu/roms/openbios/Makefile.target b/qemu/roms/openbios/Makefile.target index a7363e667..4c54105a7 100644 --- a/qemu/roms/openbios/Makefile.target +++ b/qemu/roms/openbios/Makefile.target @@ -15,7 +15,7 @@ HOSTCFLAGS+= -Wstrict-aliasing -Wwrite-strings -Wmissing-prototypes -Wnested-ext HOSTCFLAGS+= -W # Flags for dependency generation HOSTCFLAGS+= -MMD -MP -MT $@ -MF '$(*D)/$(*F).d' -HOSTINCLUDES := -I$(SRCDIR)/include -I$(SRCDIR)/kernel -I$(SRCDIR)/kernel/include -I$(ODIR)/target/include +HOSTINCLUDES := -I$(SRCDIR)/include -I$(SRCDIR)/kernel -I$(SRCDIR)/kernel/include -iquote $(ODIR)/target/include CC := $(TARGET)gcc AS := $(TARGET)as diff --git a/qemu/roms/openbios/arch/ppc/qemu/init.c b/qemu/roms/openbios/arch/ppc/qemu/init.c index 4fe8b7220..b76c5706f 100644 --- a/qemu/roms/openbios/arch/ppc/qemu/init.c +++ b/qemu/roms/openbios/arch/ppc/qemu/init.c @@ -302,6 +302,11 @@ cpu_generic_init(const struct cpudef *cpu) fword("encode-string"); push_str("state"); fword("property"); + + PUSH(0x20); + fword("encode-int"); + push_str("reservation-granule-size"); + fword("property"); } static void @@ -596,6 +601,11 @@ go(void) { ucell addr; + /* Insert copyright property for MacOS 9 and below */ + if (find_dev("/rom/macos")) { + fword("insert-copyright-property"); + } + feval("saved-program-state >sps.entry @"); addr = POP(); @@ -680,6 +690,60 @@ static void ffilll(void) } } +/* + * adler32 ( adler buf len -- checksum ) + * + * Adapted from Mark Adler's original implementation (zlib license) + * + * Both OS 9 and BootX require this word for payload validation. + */ + +#define DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +static void adler32(void) +{ + uint32_t len = (uint32_t)POP(); + char *buf = (char *)POP(); + uint32_t adler = (uint32_t)POP(); + + if (buf == NULL) { + RET(-1); + } + + uint32_t base = 65521; + uint32_t nmax = 5552; + + uint32_t s1 = adler & 0xffff; + uint32_t s2 = (adler >> 16) & 0xffff; + + uint32_t k; + while (len > 0) { + k = (len < nmax ? len : nmax); + len -= k; + + while (k >= 16) { + DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) { + do { + s1 += *buf++; + s2 += s1; + } while (--k); + } + + s1 %= base; + s2 %= base; + } + + RET(s2 << 16 | s1); +} + void arch_of_init(void) { @@ -945,6 +1009,9 @@ arch_of_init(void) /* Implementation of filll word (required by BootX) */ bind_func("filll", ffilll); + + /* Implementation of adler32 word (required by OS 9, BootX) */ + bind_func("(adler32)", adler32); bind_func("platform-boot", boot); bind_func("(go)", go); diff --git a/qemu/roms/openbios/arch/ppc/qemu/methods.c b/qemu/roms/openbios/arch/ppc/qemu/methods.c index fd993daa9..930b47c4e 100644 --- a/qemu/roms/openbios/arch/ppc/qemu/methods.c +++ b/qemu/roms/openbios/arch/ppc/qemu/methods.c @@ -114,6 +114,8 @@ static void ciface_quiesce( unsigned long args[], unsigned long ret[] ) { usb_exit(); + + ob_ide_quiesce(); #if 0 unsigned long msr; /* This seems to be the correct thing to do - but I'm not sure */ @@ -164,21 +166,21 @@ DECLARE_UNNAMED_NODE( mmu, INSTALL_OPEN, 0 ); DECLARE_NODE( mmu_ciface, 0, 0, "+/openprom/client-services" ); -/* ( phys size align --- base ) */ +/* ( [phys] size align --- base ) */ static void mem_claim( void ) { ucell align = POP(); ucell size = POP(); - ucell phys = POP(); - ucell ret = ofmem_claim_phys( phys, size, align ); + phys_addr_t phys = -1; - if( ret == -1 ) { - printk("MEM: claim failure\n"); - throw( -13 ); - return; + if (!align) { + phys = POP(); } - PUSH( ret ); + + phys = ofmem_claim_phys(phys, size, align); + + PUSH(phys); } /* ( phys size --- ) */ @@ -188,24 +190,24 @@ mem_release( void ) POP(); POP(); } -/* ( phys size align --- base ) */ +/* ( [virt] size align --- base ) */ static void mmu_claim( void ) { ucell align = POP(); ucell size = POP(); - ucell phys = POP(); - ucell ret = ofmem_claim_virt( phys, size, align ); + ucell virt = -1; - if( ret == -1 ) { - printk("MMU: CLAIM failure\n"); - throw( -13 ); - return; + if (!align) { + virt = POP(); } - PUSH( ret ); + + virt = ofmem_claim_virt(virt, size, align); + + PUSH(virt); } -/* ( phys size --- ) */ +/* ( virt size --- ) */ static void mmu_release( void ) { diff --git a/qemu/roms/openbios/arch/ppc/qemu/qemu.fs b/qemu/roms/openbios/arch/ppc/qemu/qemu.fs index 458af1bc7..3d99a34a1 100644 --- a/qemu/roms/openbios/arch/ppc/qemu/qemu.fs +++ b/qemu/roms/openbios/arch/ppc/qemu/qemu.fs @@ -93,3 +93,48 @@ variable keyboard-phandle 0 keyboard-phandle ! :noname set-defaults ; PREPOST-initializer + +\ ------------------------------------------------------------------------- +\ copyright property handling +\ ------------------------------------------------------------------------- + +: insert-copyright-property + \ As required for MacOS 9 and below + " Pbclevtug 1983-2001 Nccyr Pbzchgre, Vap. GUVF ZRFFNTR SBE PBZCNGVOVYVGL BAYL" + rot13-str encode-string " copyright" + " /" find-package if + " set-property" $find if + execute + else + 3drop drop + then + then +; + +: delete-copyright-property + \ Remove copyright property created above + active-package + " /" find-package if + active-package! + " copyright" delete-property + then + active-package! +; + +: (exit) + \ Clean up before returning to the interpreter + delete-copyright-property +; + +\ ------------------------------------------------------------------------- +\ Adler-32 wrapper +\ ------------------------------------------------------------------------- + +: adler32 ( adler buf len -- checksum ) + " (adler32)" $find if + execute + else + ." Can't find " ( adler32-name ) type cr + 3drop 0 + then +; diff --git a/qemu/roms/openbios/arch/ppc/qemu/tree.fs b/qemu/roms/openbios/arch/ppc/qemu/tree.fs index 1ed838397..5b6bbc6f7 100644 --- a/qemu/roms/openbios/arch/ppc/qemu/tree.fs +++ b/qemu/roms/openbios/arch/ppc/qemu/tree.fs @@ -42,6 +42,14 @@ new-device : close ; finish-device +new-device + " rom" device-name + h# ff800000 encode-int 0 encode-int encode+ " reg" property + 1 encode-int " #address-cells" property + h# ff800000 encode-int h# 800000 encode-int encode+ + h# ff800000 encode-int encode+ " ranges" property +finish-device + \ ------------------------------------------------------------- \ /packages \ ------------------------------------------------------------- diff --git a/qemu/roms/openbios/arch/sparc64/call-client.S b/qemu/roms/openbios/arch/sparc64/call-client.S index f365e3cb1..a8c0348e4 100644 --- a/qemu/roms/openbios/arch/sparc64/call-client.S +++ b/qemu/roms/openbios/arch/sparc64/call-client.S @@ -1,3 +1,5 @@ +#include "cpustate.h" + .globl sparc64_of_client_interface, client_tba @@ -9,151 +11,9 @@ * behaviour of OBP. */ -#define SAVE_WINDOW_STATE(type) \ - setx client_window, %g6, %g1; \ - rdpr %cwp, %g7; \ - stx %g7, [%g1]; \ - rdpr %cansave, %g7; \ - stx %g7, [%g1 + 0x8]; \ - rdpr %canrestore, %g7; \ - stx %g7, [%g1 + 0x10]; \ - rdpr %otherwin, %g7; \ - stx %g7, [%g1 + 0x18]; \ - rdpr %wstate, %g7; \ - stx %g7, [%g1 + 0x20]; \ - rdpr %cleanwin, %g7; \ - stx %g7, [%g1 + 0x28]; \ - \ - stx %o0, [%g1 + 0x30]; \ - stx %o1, [%g1 + 0x38]; \ - stx %o2, [%g1 + 0x40]; \ - stx %o3, [%g1 + 0x48]; \ - stx %o4, [%g1 + 0x50]; \ - stx %o5, [%g1 + 0x58]; \ - stx %o6, [%g1 + 0x60]; \ - stx %o7, [%g1 + 0x68]; \ - \ - rdpr %pstate, %g7; \ - stx %g7, [%g1 + 0x70]; \ - rd %y, %g7; \ - stx %g7, [%g1 + 0x78]; \ - rd %fprs, %g7; \ - stx %g7, [%g1 + 0x80]; \ - \ - /* Now iterate through all of the windows saving all l and i registers */ \ - add %g1, 0x90, %g5; \ - \ - /* Get the number of windows in %g6 */ \ - rdpr %ver, %g6; \ - and %g6, 0xf, %g6; \ - inc %g6; \ - \ -save_cpu_window_##type: \ - deccc %g6; \ - wrpr %g6, %cwp; \ - stx %l0, [%g5]; \ - stx %l1, [%g5 + 0x8]; \ - stx %l2, [%g5 + 0x10]; \ - stx %l3, [%g5 + 0x18]; \ - stx %l4, [%g5 + 0x20]; \ - stx %l5, [%g5 + 0x28]; \ - stx %l6, [%g5 + 0x30]; \ - stx %l7, [%g5 + 0x38]; \ - stx %i0, [%g5 + 0x40]; \ - stx %i1, [%g5 + 0x48]; \ - stx %i2, [%g5 + 0x50]; \ - stx %i3, [%g5 + 0x58]; \ - stx %i4, [%g5 + 0x60]; \ - stx %i5, [%g5 + 0x68]; \ - stx %i6, [%g5 + 0x70]; \ - stx %i7, [%g5 + 0x78]; \ - bne save_cpu_window_##type; \ - add %g5, 0x80, %g5; \ - \ - /* For 8 windows with 16 registers to save in the window, memory required \ - is 16*8*8 = 0x400 bytes */ \ - \ - /* Now we should be in window 0 so update the other window registers */ \ - rdpr %ver, %g6; \ - and %g6, 0xf, %g6; \ - dec %g6; \ - wrpr %g6, %cansave; \ - \ - wrpr %g0, %cleanwin; \ - wrpr %g0, %canrestore; \ - wrpr %g0, %otherwin; - - -#define RESTORE_WINDOW_STATE(type) \ - setx client_window, %g6, %g1; \ - \ - /* Get the number of windows in %g6 */ \ - rdpr %ver, %g6; \ - and %g6, 0xf, %g6; \ - inc %g6; \ - \ - /* Now iterate through all of the windows restoring all l and i registers */ \ - add %g1, 0x90, %g5; \ - \ -restore_cpu_window_##type: \ - deccc %g6; \ - wrpr %g6, %cwp; \ - ldx [%g5], %l0; \ - ldx [%g5 + 0x8], %l1; \ - ldx [%g5 + 0x10], %l2; \ - ldx [%g5 + 0x18], %l3; \ - ldx [%g5 + 0x20], %l4; \ - ldx [%g5 + 0x28], %l5; \ - ldx [%g5 + 0x30], %l6; \ - ldx [%g5 + 0x38], %l7; \ - ldx [%g5 + 0x40], %i0; \ - ldx [%g5 + 0x48], %i1; \ - ldx [%g5 + 0x50], %i2; \ - ldx [%g5 + 0x58], %i3; \ - ldx [%g5 + 0x60], %i4; \ - ldx [%g5 + 0x68], %i5; \ - ldx [%g5 + 0x70], %i6; \ - ldx [%g5 + 0x78], %i7; \ - bne restore_cpu_window_##type; \ - add %g5, 0x80, %g5; \ - \ - /* Restore the window registers to their original value */ \ - ldx [%g1], %g7; \ - wrpr %g7, %cwp; \ - ldx [%g1 + 0x8], %g7; \ - wrpr %g7, %cansave; \ - ldx [%g1 + 0x10], %g7; \ - wrpr %g7, %canrestore; \ - ldx [%g1 + 0x18], %g7; \ - wrpr %g7, %otherwin; \ - ldx [%g1 + 0x20], %g7; \ - wrpr %g7, %wstate; \ - ldx [%g1 + 0x28], %g7; \ - wrpr %g7, %cleanwin; \ - \ - ldx [%g1 + 0x30], %o0; \ - ldx [%g1 + 0x38], %o1; \ - ldx [%g1 + 0x40], %o2; \ - ldx [%g1 + 0x48], %o3; \ - ldx [%g1 + 0x50], %o4; \ - ldx [%g1 + 0x58], %o5; \ - ldx [%g1 + 0x60], %o6; \ - ldx [%g1 + 0x68], %o7; \ - \ - ldx [%g1 + 0x70], %g7; \ - wrpr %g7, %pstate; \ - ldx [%g1 + 0x78], %g7; \ - wr %g7, 0, %y; \ - ldx [%g1 + 0x80], %g7; \ - wr %g7, 0, %fprs - - .data .align 8 - .skip 16384 -openbios_stack: - client_stack: .xword 0 client_tba: @@ -176,15 +36,15 @@ client_window: sparc64_of_client_interface: /* Save globals on callers stack */ - add %sp, -56, %sp + add %sp, -248, %sp - stx %g1, [%sp + 2047 + 0] - stx %g2, [%sp + 2047 + 8] - stx %g3, [%sp + 2047 + 16] - stx %g4, [%sp + 2047 + 24] - stx %g5, [%sp + 2047 + 32] - stx %g6, [%sp + 2047 + 40] - stx %g7, [%sp + 2047 + 48] + stx %g1, [%sp + 2047 + 192] + stx %g2, [%sp + 2047 + 200] + stx %g3, [%sp + 2047 + 208] + stx %g4, [%sp + 2047 + 216] + stx %g5, [%sp + 2047 + 224] + stx %g6, [%sp + 2047 + 232] + stx %g7, [%sp + 2047 + 240] /* Save client trap table */ setx client_tba, %g6, %g7 @@ -196,22 +56,44 @@ sparc64_of_client_interface: stx %sp, [%g7] /* Save windows */ - SAVE_WINDOW_STATE(cif) - - /* Move to OpenBIOS stack */ - setx openbios_stack - 2047 - 192, %g6, %g7 + setx _fcstack_ptr, %g6, %g7 + ldx [%g7], %g1 + add %g1, -CONTEXT_STATE_SIZE, %g1 + stx %g1, [%g7] + + SAVE_CPU_WINDOW_STATE(cif) + + /* Move to OpenBIOS context stack */ + setx _fcstack_ptr, %g6, %g7 + ldx [%g7], %g6 + setx CONTEXT_STACK_SIZE, %g4, %g5 + sub %g6, %g5, %g6 + stx %g6, [%g7] + + setx - 2047 - 192, %g6, %g7 + add %g1, %g7, %g7 mov %g7, %sp /* Call client inteface */ call of_client_interface ldx [%g1 + 0x30], %o0 - setx client_window, %g6, %g1 - stx %o0, [%g1 + 0x30] - /* Restore windows */ - RESTORE_WINDOW_STATE(cif) - + setx _fcstack_ptr, %g6, %g7 + ldx [%g7], %g1 + setx CONTEXT_STACK_SIZE, %g4, %g5 + add %g1, %g5, %g1 + stx %g1, [%g7] + + /* Return value */ + stx %o0, [%g1 + 0x30] + + RESTORE_CPU_WINDOW_STATE(cif) + + add %g1, CONTEXT_STATE_SIZE, %g1 + setx _fcstack_ptr, %g6, %g7 + stx %g1, [%g7] + /* Restore stack */ setx client_stack, %g6, %g7 ldx [%g7], %sp @@ -222,15 +104,15 @@ sparc64_of_client_interface: wrpr %g6, %tba /* Restore globals */ - ldx [%sp + 2047 + 0], %g1 - ldx [%sp + 2047 + 8], %g2 - ldx [%sp + 2047 + 16], %g3 - ldx [%sp + 2047 + 24], %g4 - ldx [%sp + 2047 + 32], %g5 - ldx [%sp + 2047 + 40], %g6 - ldx [%sp + 2047 + 48], %g7 - - add %sp, 56, %sp + ldx [%sp + 2047 + 192], %g1 + ldx [%sp + 2047 + 200], %g2 + ldx [%sp + 2047 + 208], %g3 + ldx [%sp + 2047 + 216], %g4 + ldx [%sp + 2047 + 224], %g5 + ldx [%sp + 2047 + 232], %g6 + ldx [%sp + 2047 + 240], %g7 + + add %sp, 248, %sp jmp %o7+8 nop diff --git a/qemu/roms/openbios/arch/sparc64/context.c b/qemu/roms/openbios/arch/sparc64/context.c index 2e7668958..98932ee9c 100644 --- a/qemu/roms/openbios/arch/sparc64/context.c +++ b/qemu/roms/openbios/arch/sparc64/context.c @@ -40,6 +40,10 @@ static uint8_t image_stack[IMAGE_STACK_SIZE]; /* Pointer to startup context (physical address) */ unsigned long __boot_ctx; +/* Pointer to Forth context stack */ +void *_fcstack_ptr = &_efcstack; + + /* * Main starter * This is the C function that runs first. diff --git a/qemu/roms/openbios/arch/sparc64/cpustate.h b/qemu/roms/openbios/arch/sparc64/cpustate.h new file mode 100644 index 000000000..0c276bfd1 --- /dev/null +++ b/qemu/roms/openbios/arch/sparc64/cpustate.h @@ -0,0 +1,244 @@ +/* + * Save/restore CPU state macros + * + * Copyright (C) 2015 Mark Cave-Ayland (mark.cave-ayland@ilande.co.uk>) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 + * + */ + +/* State size for context (see below) */ +#define CONTEXT_STATE_SIZE 0x510 + +/* Stack size for context (allocated inline of the context stack) */ +#define CONTEXT_STACK_SIZE 0x2000 + +/* + * SAVE_CPU_STATE and RESTORE_CPU_STATE are macros used to enable a context switch + * to C to occur within the MMU I/D TLB miss handlers. + * + * Because these handlers are called on a TLB miss, we cannot use flushw to store + * processor window state on the stack, as the memory areas used by each window's + * stack pointer may not be in the TLB, causing recursive TLB miss traps. + * + * For this reason, we save window state by manually rotating the window registers + * and saving their contents (along with other vital registers) into a special + * tlb_handler_stack defined above which is guaranteed to be locked in the TLB, and + * so won't cause issues with trap recursion. + * + * Once this process is complete, we remain in a TL=0, CWP=0 state (with IE=1 to allow + * window fill/spill traps if required), switch to our safe tlb_handler_stack and + * invoke the miss handler. + */ + +#define SAVE_CPU_WINDOW_STATE(type) \ + /* Save window state into context at %g1 */ \ + rdpr %cwp, %g7; \ + stx %g7, [%g1]; \ + rdpr %cansave, %g7; \ + stx %g7, [%g1 + 0x8]; \ + rdpr %canrestore, %g7; \ + stx %g7, [%g1 + 0x10]; \ + rdpr %otherwin, %g7; \ + stx %g7, [%g1 + 0x18]; \ + rdpr %wstate, %g7; \ + stx %g7, [%g1 + 0x20]; \ + rdpr %cleanwin, %g7; \ + stx %g7, [%g1 + 0x28]; \ + \ + stx %o0, [%g1 + 0x30]; \ + stx %o1, [%g1 + 0x38]; \ + stx %o2, [%g1 + 0x40]; \ + stx %o3, [%g1 + 0x48]; \ + stx %o4, [%g1 + 0x50]; \ + stx %o5, [%g1 + 0x58]; \ + stx %o6, [%g1 + 0x60]; \ + stx %o7, [%g1 + 0x68]; \ + \ + rdpr %pstate, %g7; \ + stx %g7, [%g1 + 0x70]; \ + rd %y, %g7; \ + stx %g7, [%g1 + 0x78]; \ + rd %fprs, %g7; \ + stx %g7, [%g1 + 0x80]; \ + rdpr %tl, %g7; \ + stx %g7, [%g1 + 0x88]; \ + \ + /* Now iterate through all of the windows saving all l and i registers */ \ + add %g1, 0x90, %g5; \ + \ + /* Get the number of windows in %g6 */ \ + rdpr %ver, %g6; \ + and %g6, 0xf, %g6; \ + \ + mov %g6, %g4; \ + inc %g4; \ + \ + /* Starting cwp in g7 */ \ + rdpr %cwp, %g7; \ + \ +save_cpu_window_##type: \ + wrpr %g7, %cwp; \ + stx %l0, [%g5]; \ + stx %l1, [%g5 + 0x8]; \ + stx %l2, [%g5 + 0x10]; \ + stx %l3, [%g5 + 0x18]; \ + stx %l4, [%g5 + 0x20]; \ + stx %l5, [%g5 + 0x28]; \ + stx %l6, [%g5 + 0x30]; \ + stx %l7, [%g5 + 0x38]; \ + stx %i0, [%g5 + 0x40]; \ + stx %i1, [%g5 + 0x48]; \ + stx %i2, [%g5 + 0x50]; \ + stx %i3, [%g5 + 0x58]; \ + stx %i4, [%g5 + 0x60]; \ + stx %i5, [%g5 + 0x68]; \ + stx %i6, [%g5 + 0x70]; \ + stx %i7, [%g5 + 0x78]; \ + dec %g7; \ + and %g7, %g6, %g7; \ + subcc %g4, 1, %g4; \ + bne save_cpu_window_##type; \ + add %g5, 0x80, %g5; \ + \ + /* For 8 windows with 16 registers to save in the window, memory required \ + is 16*8*8 = 0x400 bytes */ \ + \ + /* Now we should be in window 0 so update the other window registers */ \ + rdpr %ver, %g6; \ + and %g6, 0xf, %g6; \ + dec %g6; \ + wrpr %g6, %cansave; \ + \ + wrpr %g0, %cleanwin; \ + wrpr %g0, %canrestore; \ + wrpr %g0, %otherwin; \ + + +#define SAVE_CPU_TRAP_STATE(type) \ + /* Save trap state into context at %g1 */ \ + add %g1, 0x490, %g5; \ + mov 4, %g6; \ + \ +save_trap_state_##type: \ + deccc %g6; \ + wrpr %g6, %tl; \ + rdpr %tpc, %g7; \ + stx %g7, [%g5]; \ + rdpr %tnpc, %g7; \ + stx %g7, [%g5 + 0x8]; \ + rdpr %tstate, %g7; \ + stx %g7, [%g5 + 0x10]; \ + rdpr %tt, %g7; \ + stx %g7, [%g5 + 0x18]; \ + bne save_trap_state_##type; \ + add %g5, 0x20, %g5; \ + \ + /* For 4 trap levels with 4 registers, memory required is \ + 4*8*4 = 0x80 bytes */ + +/* Save all state into context at %g1 */ +#define SAVE_CPU_STATE(type) \ + SAVE_CPU_WINDOW_STATE(type); \ + SAVE_CPU_TRAP_STATE(type); + + +#define RESTORE_CPU_WINDOW_STATE(type) \ + /* Restore window state from context at %g1 */ \ + \ + /* Get the number of windows in %g6 */ \ + rdpr %ver, %g6; \ + and %g6, 0xf, %g6; \ + \ + mov %g6, %g4; \ + inc %g4; \ + \ + /* Set starting window */ \ + ldx [%g1], %g7; \ + \ + /* Now iterate through all of the windows restoring all l and i registers */ \ + add %g1, 0x90, %g5; \ + \ +restore_cpu_window_##type: \ + wrpr %g7, %cwp; \ + ldx [%g5], %l0; \ + ldx [%g5 + 0x8], %l1; \ + ldx [%g5 + 0x10], %l2; \ + ldx [%g5 + 0x18], %l3; \ + ldx [%g5 + 0x20], %l4; \ + ldx [%g5 + 0x28], %l5; \ + ldx [%g5 + 0x30], %l6; \ + ldx [%g5 + 0x38], %l7; \ + ldx [%g5 + 0x40], %i0; \ + ldx [%g5 + 0x48], %i1; \ + ldx [%g5 + 0x50], %i2; \ + ldx [%g5 + 0x58], %i3; \ + ldx [%g5 + 0x60], %i4; \ + ldx [%g5 + 0x68], %i5; \ + ldx [%g5 + 0x70], %i6; \ + ldx [%g5 + 0x78], %i7; \ + dec %g7; \ + and %g7, %g6, %g7; \ + subcc %g4, 1, %g4; \ + bne restore_cpu_window_##type; \ + add %g5, 0x80, %g5; \ + \ + /* Restore the window registers to their original value */ \ + ldx [%g1], %g7; \ + wrpr %g7, %cwp; \ + ldx [%g1 + 0x8], %g7; \ + wrpr %g7, %cansave; \ + ldx [%g1 + 0x10], %g7; \ + wrpr %g7, %canrestore; \ + ldx [%g1 + 0x18], %g7; \ + wrpr %g7, %otherwin; \ + ldx [%g1 + 0x20], %g7; \ + wrpr %g7, %wstate; \ + ldx [%g1 + 0x28], %g7; \ + wrpr %g7, %cleanwin; \ + \ + ldx [%g1 + 0x30], %o0; \ + ldx [%g1 + 0x38], %o1; \ + ldx [%g1 + 0x40], %o2; \ + ldx [%g1 + 0x48], %o3; \ + ldx [%g1 + 0x50], %o4; \ + ldx [%g1 + 0x58], %o5; \ + ldx [%g1 + 0x60], %o6; \ + ldx [%g1 + 0x68], %o7; \ + \ + ldx [%g1 + 0x70], %g7; \ + wrpr %g7, %pstate; \ + ldx [%g1 + 0x78], %g7; \ + wr %g7, 0, %y; \ + ldx [%g1 + 0x80], %g7; \ + wr %g7, 0, %fprs; \ + + +#define RESTORE_CPU_TRAP_STATE(type) \ + /* Restore trap state from context at %g1 */ \ + add %g1, 0x490, %g5; \ + mov 4, %g6; \ + \ +restore_trap_state_##type: \ + deccc %g6; \ + wrpr %g6, %tl; \ + ldx [%g5], %g7; \ + wrpr %g7, %tpc; \ + ldx [%g5 + 0x8], %g7; \ + wrpr %g7, %tnpc; \ + ldx [%g5 + 0x10], %g7; \ + wrpr %g7, %tstate; \ + ldx [%g5 + 0x18], %g7; \ + wrpr %g7, %tt; \ + bne restore_trap_state_##type; \ + add %g5, 0x20, %g5; \ + \ + ldx [%g1 + 0x88], %g7; \ + wrpr %g7, %tl + +/* Restore all state from context at %g1 */ +#define RESTORE_CPU_STATE(type) \ + RESTORE_CPU_WINDOW_STATE(type); \ + RESTORE_CPU_TRAP_STATE(type); diff --git a/qemu/roms/openbios/arch/sparc64/ldscript b/qemu/roms/openbios/arch/sparc64/ldscript index 54288e825..c5cc6a5ce 100644 --- a/qemu/roms/openbios/arch/sparc64/ldscript +++ b/qemu/roms/openbios/arch/sparc64/ldscript @@ -50,6 +50,11 @@ SECTIONS *(.bss.*) *(COMMON) + _fcstack = .; + . += 32768; + . = ALIGN(16); + _efcstack = .; + _stack = .; . += STACK_SIZE; . = ALIGN(16); diff --git a/qemu/roms/openbios/arch/sparc64/lib.c b/qemu/roms/openbios/arch/sparc64/lib.c index e9101af52..4709ca8fe 100644 --- a/qemu/roms/openbios/arch/sparc64/lib.c +++ b/qemu/roms/openbios/arch/sparc64/lib.c @@ -458,10 +458,10 @@ NODE_METHODS(mmu) = { void ob_mmu_init(const char *cpuname, uint64_t ram_size) { /* memory node */ - REGISTER_NODE_METHODS(memory, "/memory"); + REGISTER_NODE(memory); /* MMU node */ - REGISTER_NODE_METHODS(mmu, "/virtual-memory"); + REGISTER_NODE(mmu); ofmem_register(find_dev("/memory"), find_dev("/virtual-memory")); diff --git a/qemu/roms/openbios/arch/sparc64/vectors.S b/qemu/roms/openbios/arch/sparc64/vectors.S index 927c1cdc7..9d86b6bd8 100644 --- a/qemu/roms/openbios/arch/sparc64/vectors.S +++ b/qemu/roms/openbios/arch/sparc64/vectors.S @@ -24,6 +24,7 @@ */ #define __ASSEMBLY__ +#include "cpustate.h" #include "pstate.h" #include #define ASI_BP ASI_PHYS_BYPASS_EC_E @@ -276,17 +277,8 @@ tl1_resv1f0: BTRAPS(0x1f0) BTRAPS(0x1f8) .section ".data" .align 8 - .globl tlb_handler_stack_top, tlb_handler_stack_pointer, obp_ticks_pointer + .globl obp_ticks_pointer - ! Stack for the tlb MMU trap handlers -tlb_handler_stack_bottom: - .skip 8192 -tlb_handler_stack_top: - .skip 8 - - ! MMU trap handler stack pointer -tlb_handler_stack_pointer: - .xword tlb_handler_stack_top ! Pointer to current tick value obp_ticks_pointer: @@ -336,234 +328,30 @@ fill_32bit: restored retry -/* - * SAVE_CPU_STATE and RESTORE_CPU_STATE are macros used to enable a context switch - * to C to occur within the MMU I/D TLB miss handlers. - * - * Because these handlers are called on a TLB miss, we cannot use flushw to store - * processor window state on the stack, as the memory areas used by each window's - * stack pointer may not be in the TLB, causing recursive TLB miss traps. - * - * For this reason, we save window state by manually rotating the window registers - * and saving their contents (along with other vital registers) into a special - * tlb_handler_stack defined above which is guaranteed to be locked in the TLB, and - * so won't cause issues with trap recursion. - * - * Once this process is complete, we remain in a TL=0, CWP=0 state (with IE=1 to allow - * window fill/spill traps if required), switch to our safe tlb_handler_stack and - * invoke the miss handler. - */ - -#define SAVE_CPU_STATE(type) \ - /* Set up our exception stack pointer in %g1 */ \ - setx tlb_handler_stack_pointer, %g7, %g6; \ - ldx [%g6], %g1; \ - add %g1, -0x510, %g1; \ - \ - /* First save the various state registers */ \ - rdpr %cwp, %g7; \ - stx %g7, [%g1]; \ - rdpr %cansave, %g7; \ - stx %g7, [%g1 + 0x8]; \ - rdpr %canrestore, %g7; \ - stx %g7, [%g1 + 0x10]; \ - rdpr %otherwin, %g7; \ - stx %g7, [%g1 + 0x18]; \ - rdpr %wstate, %g7; \ - stx %g7, [%g1 + 0x20]; \ - rdpr %cleanwin, %g7; \ - stx %g7, [%g1 + 0x28]; \ - rdpr %pstate, %g7; \ - stx %g7, [%g1 + 0x30]; \ - \ - rd %y, %g7; \ - stx %g7, [%g1 + 0x38]; \ - rd %fprs, %g7; \ - stx %g7, [%g1 + 0x40]; \ - \ - rdpr %tl, %g7; \ - stx %g7, [%g1 + 0x48]; \ - \ - /* Trap state */ \ - add %g1, 0x50, %g5; \ - mov 4, %g6; \ - \ -save_trap_state_##type: \ - deccc %g6; \ - wrpr %g6, %tl; \ - rdpr %tpc, %g7; \ - stx %g7, [%g5]; \ - rdpr %tnpc, %g7; \ - stx %g7, [%g5 + 0x8]; \ - rdpr %tstate, %g7; \ - stx %g7, [%g5 + 0x10]; \ - rdpr %tt, %g7; \ - stx %g7, [%g5 + 0x18]; \ - bne save_trap_state_##type; \ - add %g5, 0x20, %g5; \ - \ - /* For 4 trap levels with 4 registers, memory required is - 4*8*4 = 0x80 bytes */ \ - \ - /* Save the o registers */ \ - stx %o0, [%g1 + 0xd0]; \ - stx %o1, [%g1 + 0xd8]; \ - stx %o2, [%g1 + 0xe0]; \ - stx %o3, [%g1 + 0xe8]; \ - stx %o4, [%g1 + 0xf0]; \ - stx %o5, [%g1 + 0xf8]; \ - stx %o6, [%g1 + 0x100]; \ - stx %o7, [%g1 + 0x108]; \ - \ - /* Now iterate through all of the windows saving all l and i registers */ \ - add %g1, 0x110, %g5; \ - \ - /* Get the number of windows in %g6 */ \ - rdpr %ver, %g6; \ - and %g6, 0xf, %g6; \ - inc %g6; \ - \ -save_cpu_window_##type: \ - deccc %g6; \ - wrpr %g6, %cwp; \ - stx %l0, [%g5]; \ - stx %l1, [%g5 + 0x8]; \ - stx %l2, [%g5 + 0x10]; \ - stx %l3, [%g5 + 0x18]; \ - stx %l4, [%g5 + 0x20]; \ - stx %l5, [%g5 + 0x28]; \ - stx %l6, [%g5 + 0x30]; \ - stx %l7, [%g5 + 0x38]; \ - stx %i0, [%g5 + 0x40]; \ - stx %i1, [%g5 + 0x48]; \ - stx %i2, [%g5 + 0x50]; \ - stx %i3, [%g5 + 0x58]; \ - stx %i4, [%g5 + 0x60]; \ - stx %i5, [%g5 + 0x68]; \ - stx %i6, [%g5 + 0x70]; \ - stx %i7, [%g5 + 0x78]; \ - bne save_cpu_window_##type; \ - add %g5, 0x80, %g5; \ - \ - /* For 8 windows with 16 registers to save in the window, memory required - is 16*8*8 = 0x400 bytes */ \ - \ - /* Now we should be in window 0 so update the other window registers */ \ - rdpr %ver, %g6; \ - and %g6, 0xf, %g6; \ - dec %g6; \ - wrpr %g6, %cansave; \ - \ - wrpr %g0, %cleanwin; \ - wrpr %g0, %canrestore; \ - wrpr %g0, %otherwin; \ - \ - /* Update our exception stack pointer */ \ - setx tlb_handler_stack_pointer, %g7, %g6; \ - stx %g1, [%g6]; - - -#define RESTORE_CPU_STATE(type) \ - /* Set up our exception stack pointer in %g1 */ \ - setx tlb_handler_stack_pointer, %g7, %g6; \ - ldx [%g6], %g1; \ - \ - /* Get the number of windows in %g6 */ \ - rdpr %ver, %g6; \ - and %g6, 0xf, %g6; \ - inc %g6; \ - \ - /* Now iterate through all of the windows restoring all l and i registers */ \ - add %g1, 0x110, %g5; \ - \ -restore_cpu_window_##type: \ - deccc %g6; \ - wrpr %g6, %cwp; \ - ldx [%g5], %l0; \ - ldx [%g5 + 0x8], %l1; \ - ldx [%g5 + 0x10], %l2; \ - ldx [%g5 + 0x18], %l3; \ - ldx [%g5 + 0x20], %l4; \ - ldx [%g5 + 0x28], %l5; \ - ldx [%g5 + 0x30], %l6; \ - ldx [%g5 + 0x38], %l7; \ - ldx [%g5 + 0x40], %i0; \ - ldx [%g5 + 0x48], %i1; \ - ldx [%g5 + 0x50], %i2; \ - ldx [%g5 + 0x58], %i3; \ - ldx [%g5 + 0x60], %i4; \ - ldx [%g5 + 0x68], %i5; \ - ldx [%g5 + 0x70], %i6; \ - ldx [%g5 + 0x78], %i7; \ - bne restore_cpu_window_##type; \ - add %g5, 0x80, %g5; \ - \ - /* Restore the window registers to their original value */ \ - ldx [%g1], %g7; \ - wrpr %g7, %cwp; \ - ldx [%g1 + 0x8], %g7; \ - wrpr %g7, %cansave; \ - ldx [%g1 + 0x10], %g7; \ - wrpr %g7, %canrestore; \ - ldx [%g1 + 0x18], %g7; \ - wrpr %g7, %otherwin; \ - ldx [%g1 + 0x20], %g7; \ - wrpr %g7, %wstate; \ - ldx [%g1 + 0x28], %g7; \ - wrpr %g7, %cleanwin; \ - ldx [%g1 + 0x30], %g7; \ - wrpr %g7, %pstate; \ - \ - /* Restore the o registers */ \ - ldx [%g1 + 0xd0], %o0; \ - ldx [%g1 + 0xd8], %o1; \ - ldx [%g1 + 0xe0], %o2; \ - ldx [%g1 + 0xe8], %o3; \ - ldx [%g1 + 0xf0], %o4; \ - ldx [%g1 + 0xf8], %o5; \ - ldx [%g1 + 0x100], %o6; \ - ldx [%g1 + 0x108], %o7; \ - \ - /* Restore the trap state */ \ - add %g1, 0x50, %g5; \ - mov 4, %g6; \ - \ -restore_trap_state_##type: \ - deccc %g6; \ - wrpr %g6, %tl; \ - ldx [%g5], %g7; \ - wrpr %g7, %tpc; \ - ldx [%g5 + 0x8], %g7; \ - wrpr %g7, %tnpc; \ - ldx [%g5 + 0x10], %g7; \ - wrpr %g7, %tstate; \ - ldx [%g5 + 0x18], %g7; \ - wrpr %g7, %tt; \ - bne restore_trap_state_##type; \ - add %g5, 0x20, %g5; \ - \ - ldx [%g1 + 0x38], %g7; \ - wr %g7, 0, %y; \ - ldx [%g1 + 0x40], %g7; \ - wr %g7, 0, %fprs; \ - ldx [%g1 + 0x48], %g7; \ - wrpr %g7, %tl; \ - \ - /* Restore exception stack pointer to previous value */ \ - setx tlb_handler_stack_pointer, %g7, %g6; \ - add %g1, 0x510, %g1; \ - stx %g1, [%g6]; - .globl reload_DMMU_tlb, reload_IMMU_tlb, bug reload_DMMU_tlb: - + + /* Save CPU state to stack */ + setx _fcstack_ptr, %g6, %g7 + ldx [%g7], %g1 + add %g1, -CONTEXT_STATE_SIZE, %g1 + stx %g1, [%g7] + SAVE_CPU_STATE(dtlb) - /* Switch to TLB locked stack space (note we add an additional 192 bytes required for + /* Switch to 8K TLB locked OpenBIOS stack (note we add an additional 192 bytes required for gcc to save its arguments when building with -O0) */ - add %g1, -STACK_BIAS - 192, %sp + setx _fcstack_ptr, %g6, %g7 + ldx [%g7], %g6 + setx CONTEXT_STACK_SIZE, %g4, %g5 + sub %g6, %g5, %g6 + stx %g6, [%g7] + + setx - 2047 - 192, %g6, %g7 + add %g1, %g7, %g7 + mov %g7, %sp /* Enable interrupts for window spill/fill traps */ rdpr %pstate, %g7 @@ -577,18 +365,44 @@ reload_DMMU_tlb: rdpr %pstate, %g7 andn %g7, PSTATE_IE, %g7 wrpr %g7, %pstate - + + /* Restore CPU state from stack */ + setx _fcstack_ptr, %g6, %g7 + ldx [%g7], %g1 + setx CONTEXT_STACK_SIZE, %g4, %g5 + add %g1, %g5, %g1 + stx %g1, [%g7] + RESTORE_CPU_STATE(dtlb) - + + setx _fcstack_ptr, %g6, %g7 + ldx [%g7], %g1 + add %g1, CONTEXT_STATE_SIZE, %g1 + stx %g1, [%g7] + retry reload_IMMU_tlb: - + + /* Save CPU state to stack */ + setx _fcstack_ptr, %g6, %g7 + ldx [%g7], %g1 + add %g1, -CONTEXT_STATE_SIZE, %g1 + stx %g1, [%g7] + SAVE_CPU_STATE(itlb) - /* Switch to TLB locked stack space (note we add an additional 192 bytes required for + /* Switch to 8K TLB locked OpenBIOS stack (note we add an additional 192 bytes required for gcc to save its arguments when building with -O0) */ - add %g1, -STACK_BIAS - 192, %sp + setx _fcstack_ptr, %g6, %g7 + ldx [%g7], %g6 + setx CONTEXT_STACK_SIZE, %g4, %g5 + sub %g6, %g5, %g6 + stx %g6, [%g7] + + setx - 2047 - 192, %g6, %g7 + add %g1, %g7, %g7 + mov %g7, %sp /* Enable interrupts for window spill/fill traps */ rdpr %pstate, %g7 @@ -602,8 +416,20 @@ reload_IMMU_tlb: rdpr %pstate, %g7 andn %g7, PSTATE_IE, %g7 wrpr %g7, %pstate - + + /* Restore CPU state from stack */ + setx _fcstack_ptr, %g6, %g7 + ldx [%g7], %g1 + setx CONTEXT_STACK_SIZE, %g4, %g5 + add %g1, %g5, %g1 + stx %g1, [%g7] + RESTORE_CPU_STATE(itlb) + + setx _fcstack_ptr, %g6, %g7 + ldx [%g7], %g1 + add %g1, CONTEXT_STATE_SIZE, %g1 + stx %g1, [%g7] retry diff --git a/qemu/roms/openbios/config/scripts/switch-arch b/qemu/roms/openbios/config/scripts/switch-arch index d5e2f7710..ab3b4ce69 100755 --- a/qemu/roms/openbios/config/scripts/switch-arch +++ b/qemu/roms/openbios/config/scripts/switch-arch @@ -17,48 +17,57 @@ if [ x"$1" = x -o "$1" = "-help" ]; then exit 0 fi -crosscflags() +is_bigendian() { - host=$1 - target=$2 - - if test "$host" = "powerpc" -o "$host" = "ppc" \ - -o "$host" = "mips" -o "$host" = "s390" \ - -o "$host" = "sparc32" -o "$host" = "sparc64" \ - -o "$host" = "m68k" -o "$host" = "armv4b"; then - hostbigendian="yes" + cpu=$1 + + if test "$cpu" = "powerpc" -o "$cpu" = "ppc" \ + -o "$cpu" = "powerpc64" -o "$cpu" = "ppc64" \ + -o "$cpu" = "mips" -o "$cpu" = "s390" \ + -o "$cpu" = "sparc32" -o "$cpu" = "sparc64" \ + -o "$cpu" = "m68k" -o "$cpu" = "armv4b"; then + echo yes else - hostbigendian="no" + echo no fi +} -# host long bits test - if test "$host" = "sparc64" -o "$host" = "ia64" \ - -o "$host" = "amd64" -o "$host" = "x86_64" \ - -o "$host" = "alpha"; then - hostlongbits="64" +longbits() +{ + cpu=$1 + if test "$cpu" = "sparc64" -o "$cpu" = "ia64" \ + -o "$cpu" = "amd64" -o "$cpu" = "x86_64" \ + -o "$cpu" = "powerpc64" -o "$cpu" = "ppc64" \ + -o "$cpu" = "alpha"; then + echo 64 else - hostlongbits="32" + echo 32 fi +} - if test "$target" = "powerpc" -o "$target" = "ppc" \ - -o "$target" = "powerpc64" -o "$target" = "ppc64" \ - -o "$target" = "mips" -o "$target" = "s390" \ - -o "$target" = "sparc32" -o "$target" = "sparc64" \ - -o "$target" = "m68k" -o "$target" = "armv4b"; then - targetbigendian="yes" - else - targetbigendian="no" - fi +basearch() +{ + arch=$1 + case $arch in + powerpc|ppc64|powerpc64) + echo ppc + ;; + *) + echo $arch + ;; + esac +} -# target long bits test - if test "$target" = "sparc64" -o "$target" = "ia64" \ - -o "$target" = "amd64" -o "$target" = "x86_64" \ - -o "$target" = "powerpc64" -o "$target" = "ppc64" \ - -o "$target" = "alpha"; then - targetlongbits="64" - else - targetlongbits="32" - fi +crosscflags() +{ + host=$1 + target=$2 + + hostbigendian=$(is_bigendian $host) + hostlongbits=$(longbits $host) + + targetbigendian=$(is_bigendian $target) + targetlongbits=$(longbits $target) if test "$targetbigendian" = "$hostbigendian"; then cflags="-USWAP_ENDIANNESS" @@ -99,23 +108,27 @@ archname() select_prefix() { - TARGETS="${1}-unknown-linux-gnu- ${1}-linux-gnu- ${1}-linux- ${1}-elf- ${1}-eabi-" + BASEARCH=$(basearch $ARCH) + for target_arch ; do + TARGETS="${target_arch}-unknown-linux-gnu- ${target_arch}-linux-gnu- ${target_arch}-linux- ${target_arch}-elf- ${target_arch}-eabi-" - if [ x"$CROSS_COMPILE" != "x" ]; then - TARGETS=$CROSS_COMPILE - fi + if [ x"$CROSS_COMPILE" != "x" ]; then + TARGETS=$CROSS_COMPILE + fi - for TARGET in $TARGETS - do - if type ${TARGET}gcc > /dev/null 2>&1 - then + for TARGET in $TARGETS + do + if type ${TARGET}gcc > /dev/null 2>&1 + then + return + fi + done + if [ "$BASEARCH" = "$(basearch $HOSTARCH)" ]; then + TARGET="" return fi done - if [ "$ARCH" = "$HOSTARCH" ]; then - return - fi - echo "ERROR: no ${1} cross-compiler found !" 1>&2 + echo "ERROR: no $* cross-compiler found !" 1>&2 exit 1 } @@ -242,7 +255,6 @@ for ARCH in $arch_list; do esac done - BASEARCH=$ARCH case $ARCH in amd64) select_prefix x86_64 @@ -251,9 +263,10 @@ for ARCH in $arch_list; do ;; ppc) - select_prefix powerpc + select_prefix powerpc powerpc64 if [ "$unix" = "no" ]; then - CFLAGS="-m32 -msoft-float -fno-builtin-bcopy -fno-builtin-log2" + # 604 cpu includes support for PReP as well as Mac + CFLAGS="-m32 -mcpu=604 -msoft-float -fno-builtin-bcopy -fno-builtin-log2" AS_FLAGS="-m32" else CFLAGS="-fno-builtin" @@ -263,13 +276,14 @@ for ARCH in $arch_list; do ppc64) select_prefix powerpc64 - CFLAGS="-Wa,-a64 -m64 -msoft-float -fno-builtin" + + # 970 cpu is used in all 64-bit Macs but disable altivec + CFLAGS="-mcpu=970 -mno-altivec -Wa,-a64 -m64 -msoft-float -fno-builtin" AS_FLAGS="-Wa,-a64" - BASEARCH=ppc ;; sparc32) - select_prefix sparc + select_prefix sparc sparc64 CFLAGS="-Wa,-xarch=v8 -Wa,-32 -m32 -mcpu=supersparc -fno-builtin" AS_FLAGS="-Wa,-xarch=v8 -Wa,-32" ;; diff --git a/qemu/roms/openbios/drivers/cuda.c b/qemu/roms/openbios/drivers/cuda.c index 9555dea49..ff5d22de2 100644 --- a/qemu/roms/openbios/drivers/cuda.c +++ b/qemu/roms/openbios/drivers/cuda.c @@ -144,8 +144,22 @@ static int cuda_adb_req (void *host, const uint8_t *snd_buf, int len, // CUDA_DPRINTF("len: %d %02x\n", len, snd_buf[0]); len = cuda_request(host, ADB_PACKET, snd_buf, len, buffer); if (len > 1 && buffer[0] == ADB_PACKET) { - pos = buffer + 2; - len -= 2; + /* We handle 2 types of ADB packet here: + Normal: ... + Error : ( ...) + Ideally we should use buffer[1] (status) to determine whether this + is a normal or error packet but this requires a corresponding fix + in QEMU <= 2.4. Hence we temporarily handle it this way to ease + the transition. */ + if (len > 2 && buffer[2] == snd_buf[0]) { + /* Error */ + pos = buffer + 3; + len -= 3; + } else { + /* Normal */ + pos = buffer + 2; + len -= 2; + } } else { pos = buffer + 1; len = -1; @@ -380,7 +394,8 @@ powermgt_init(char *path) ph = find_dev(buf); set_property(ph, "device_type", "power-mgt", 10); - set_property(ph, "compatible", "power-mgt", 10); + set_property(ph, "mgt-kind", "min-consumption-pwm-led", strlen("min-consumption-pwm-led") + 1); + set_property(ph, "compatible", "cuda", strlen("cuda") + 1); } cuda_t *cuda_init (const char *path, phys_addr_t base) diff --git a/qemu/roms/openbios/drivers/escc.c b/qemu/roms/openbios/drivers/escc.c index 240043be3..1990e798d 100644 --- a/qemu/roms/openbios/drivers/escc.c +++ b/qemu/roms/openbios/drivers/escc.c @@ -380,12 +380,44 @@ ob_zs_init(phys_addr_t base, uint64_t offset, int intr, int slave, int keyboard) static void escc_add_channel(const char *path, const char *node, phys_addr_t addr, - uint32_t offset) + int esnum) { char buf[64], tty[32]; phandle_t dnode, aliases; - int len; - cell props[2]; + + cell props[10]; + ucell offset; + int index; + int legacy; + + int dbdma_offsets[2][2] = { + /* ch-b */ + { 0x6, 0x7 }, + /* ch-a */ + { 0x4, 0x5 } + }; + + int reg_offsets[2][2][3] = { + { + /* ch-b */ + { 0x00, 0x10, 0x40 }, + /* ch-a */ + { 0x20, 0x30, 0x50 } + },{ + /* legacy ch-b */ + { 0x0, 0x2, 0x8 }, + /* legacy ch-a */ + { 0x4, 0x6, 0xa } + } + }; + + switch (esnum) { + case 2: index = 1; legacy = 0; break; + case 3: index = 0; legacy = 0; break; + case 4: index = 1; legacy = 1; break; + case 5: index = 0; legacy = 1; break; + default: return; + } /* add device */ @@ -411,31 +443,49 @@ escc_add_channel(const char *path, const char *node, phys_addr_t addr, set_property(dnode, "device_type", "serial", strlen("serial") + 1); - snprintf(buf, sizeof(buf), "ch-%s", node); - len = strlen(buf) + 1; - snprintf(buf + len, sizeof(buf) - len, "CHRP,es2"); - set_property(dnode, "compatible", buf, len + 9); + snprintf(buf, sizeof(buf), "chrp,es%d", esnum); + set_property(dnode, "compatible", buf, 9); - props[0] = IO_ESCC_OFFSET + offset * 0x20; - props[1] = 0x00000020; - set_property(dnode, "reg", (char *)&props, 2 * sizeof(cell)); + if (legacy) { + offset = IO_ESCC_LEGACY_OFFSET; + } else { + offset = IO_ESCC_OFFSET; + } - props[0] = addr + IO_ESCC_OFFSET + offset * 0x20; + props[0] = offset + reg_offsets[legacy][index][0]; + props[1] = 0x1; + props[2] = offset + reg_offsets[legacy][index][1]; + props[3] = 0x1; + props[4] = offset + reg_offsets[legacy][index][2]; + props[5] = 0x1; + props[6] = 0x8000 + dbdma_offsets[index][0] * 0x100; + props[7] = 0x100; + props[8] = 0x8000 + dbdma_offsets[index][1] * 0x100; + props[9] = 0x100; + set_property(dnode, "reg", (char *)&props, 10 * sizeof(cell)); + + props[0] = addr + offset + reg_offsets[legacy][index][0]; OLDWORLD(set_property(dnode, "AAPL,address", (char *)&props, 1 * sizeof(cell))); - props[0] = 0x00000010 - offset; + props[0] = 0x10 - index; OLDWORLD(set_property(dnode, "AAPL,interrupts", (char *)&props, 1 * sizeof(cell))); - props[0] = (0x24) + offset; - props[1] = 0; + props[0] = (0x24) + index; + props[1] = 0x1; + props[2] = dbdma_offsets[index][0]; + props[3] = 0x0; + props[4] = dbdma_offsets[index][1]; + props[5] = 0x0; NEWWORLD(set_property(dnode, "interrupts", - (char *)&props, 2 * sizeof(cell))); + (char *)&props, 6 * sizeof(cell))); + + set_int_property(dnode, "slot-names", 0); device_end(); - uart_init_line((unsigned char*)addr + IO_ESCC_OFFSET + offset * 0x20, + uart_init_line((unsigned char*)addr + offset + reg_offsets[legacy][index][0], CONFIG_SERIAL_SPEED); } @@ -464,13 +514,39 @@ escc_init(const char *path, phys_addr_t addr) set_property(dnode, "device_type", "escc", strlen("escc") + 1); set_property(dnode, "compatible", "escc\0CHRP,es0", 14); + set_property(dnode, "ranges", "", 0); fword("finish-device"); - escc_add_channel(buf, "a", addr, 1); - escc_add_channel(buf, "b", addr, 0); + escc_add_channel(buf, "a", addr, 2); + escc_add_channel(buf, "b", addr, 3); escc_serial_dev = (unsigned char *)addr + IO_ESCC_OFFSET + (CONFIG_SERIAL_PORT ? 0 : 0x20); + + push_str(path); + fword("find-device"); + fword("new-device"); + + push_str("escc-legacy"); + fword("device-name"); + + snprintf(buf, sizeof(buf), "%s/escc-legacy", path); + + dnode = find_dev(buf); + + set_int_property(dnode, "#address-cells", 1); + props[0] = __cpu_to_be32(IO_ESCC_LEGACY_OFFSET); + props[1] = __cpu_to_be32(IO_ESCC_LEGACY_SIZE); + set_property(dnode, "reg", (char *)&props, sizeof(props)); + set_property(dnode, "device_type", "escc-legacy", + strlen("escc-legacy") + 1); + set_property(dnode, "compatible", "chrp,es1", 9); + set_property(dnode, "ranges", "", 0); + + fword("finish-device"); + + escc_add_channel(buf, "a", addr, 4); + escc_add_channel(buf, "b", addr, 5); } #endif diff --git a/qemu/roms/openbios/drivers/escc.h b/qemu/roms/openbios/drivers/escc.h index caaf00d40..e73f267b2 100644 --- a/qemu/roms/openbios/drivers/escc.h +++ b/qemu/roms/openbios/drivers/escc.h @@ -1,6 +1,8 @@ #define IO_ESCC_SIZE 0x00001000 #define IO_ESCC_OFFSET 0x00013000 +#define IO_ESCC_LEGACY_SIZE 0x00001000 +#define IO_ESCC_LEGACY_OFFSET 0x00012000 #define ZS_REGS 8 diff --git a/qemu/roms/openbios/drivers/ide.c b/qemu/roms/openbios/drivers/ide.c index 327c64a40..1da60c895 100644 --- a/qemu/roms/openbios/drivers/ide.c +++ b/qemu/roms/openbios/drivers/ide.c @@ -73,13 +73,13 @@ static inline void ide_add_channel(struct ide_channel *chan) channels = chan; } -static struct ide_channel *ide_seek_channel(const char *name) +static struct ide_channel *ide_seek_channel(phandle_t ph) { struct ide_channel *current; current = channels; while (current) { - if (!strcmp(current->name, name)) + if (current->ph == ph) return current; current = current->next; } @@ -1247,11 +1247,10 @@ ob_ide_initialize(int *idx) static void ob_ide_open(int *idx) { - int ret=1, len; + int ret=1; phandle_t ph; struct ide_drive *drive; struct ide_channel *chan; - char *idename; int unit; fword("my-unit"); @@ -1260,9 +1259,8 @@ ob_ide_open(int *idx) fword("my-parent"); fword("ihandle>phandle"); ph=(phandle_t)POP(); - idename=get_property(ph, "name", &len); - chan = ide_seek_channel(idename); + chan = ide_seek_channel(ph); drive = &chan->drives[unit]; *(struct ide_drive **)idx = drive; @@ -1380,9 +1378,6 @@ int ob_ide_init(const char *path, uint32_t io_port0, uint32_t ctl_port0, chan = malloc(sizeof(struct ide_channel)); - snprintf(chan->name, sizeof(chan->name), - DEV_NAME, current_channel); - chan->mmio = 0; for (j = 0; j < 8; j++) @@ -1424,9 +1419,9 @@ int ob_ide_init(const char *path, uint32_t io_port0, uint32_t ctl_port0, snprintf(nodebuff, sizeof(nodebuff), "%s/" DEV_NAME, path, current_channel); - REGISTER_NAMED_NODE(ob_ide_ctrl, nodebuff); + REGISTER_NAMED_NODE_PHANDLE(ob_ide_ctrl, nodebuff, dnode); - dnode = find_dev(nodebuff); + chan->ph = dnode; #if !defined(CONFIG_PPC) && !defined(CONFIG_SPARC64) props[0]=14; props[1]=0; @@ -1468,11 +1463,9 @@ int ob_ide_init(const char *path, uint32_t io_port0, uint32_t ctl_port0, break; } IDE_DPRINTF("%s]: %s\n", media, drive->model); - snprintf(nodebuff, sizeof(nodebuff), - "%s/" DEV_NAME "/%s", path, current_channel, - media); - REGISTER_NAMED_NODE(ob_ide, nodebuff); - dnode=find_dev(nodebuff); + snprintf(nodebuff, sizeof(nodebuff), "%s/%s", + get_path_from_ph(dnode), media); + REGISTER_NAMED_NODE_PHANDLE(ob_ide, nodebuff, dnode); set_int_property(dnode, "reg", j); /* create aliases */ @@ -1488,6 +1481,28 @@ int ob_ide_init(const char *path, uint32_t io_port0, uint32_t ctl_port0, return 0; } +void ob_ide_quiesce(void) +{ + struct ide_channel *channel; + int i; + + channel = channels; + while (channel) { + for (i = 0; i < 2; i++) { + struct ide_drive *drive = &channel->drives[i]; + + if (!drive->present) + continue; + + ob_ide_select_drive(drive); + ob_ide_software_reset(drive); + ob_ide_device_type_check(drive); + } + + channel = channel->next; + } +} + #if defined(CONFIG_DRIVER_MACIO) static unsigned char macio_ide_inb(struct ide_channel *chan, unsigned int port) @@ -1527,16 +1542,13 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels) struct ide_channel *chan; /* IDE ports on Macs are numbered from 3. - * Also see comments in macio.c:openpic_init() */ + * Also see comments in pci.c:ob_pci_host_set_interrupt_map() */ current_channel = 3; - for (i = 0; i < nb_channels; i++, current_channel++) { + for (i = 0; i < nb_channels; i++) { chan = malloc(sizeof(struct ide_channel)); - snprintf(chan->name, sizeof(chan->name), - DEV_NAME, current_channel); - chan->mmio = addr + MACIO_IDE_OFFSET + i * MACIO_IDE_SIZE; chan->obide_inb = macio_ide_inb; @@ -1574,13 +1586,19 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels) snprintf(nodebuff, sizeof(nodebuff), "%s/" DEV_NAME, path, current_channel); - REGISTER_NAMED_NODE(ob_ide_ctrl, nodebuff); + REGISTER_NAMED_NODE_PHANDLE(ob_ide_ctrl, nodebuff, dnode); - dnode = find_dev(nodebuff); + chan->ph = dnode; set_property(dnode, "compatible", (is_oldworld() ? "heathrow-ata" : "keylargo-ata"), 13); + set_property(dnode, "model", ((current_channel == 3) ? + "ata-3" : "ata-4"), strlen("ata-*") + 1); + + set_property(dnode, "AAPL,connector", "ata", + strlen("ata") + 1); + props[0] = 0x00000526; props[1] = 0x00000085; props[2] = 0x00000025; @@ -1589,8 +1607,8 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels) props[5] = 0x00000000; props[6] = 0x00000000; props[7] = 0x00000000; - OLDWORLD(set_property(dnode, "AAPL,pio-timing", - (char *)&props, 8*sizeof(props[0]))); + set_property(dnode, "AAPL,pio-timing", + (char *)&props, 8*sizeof(props[0])); /* The first interrupt entry is the ide interrupt, the second the dbdma interrupt */ @@ -1633,9 +1651,9 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels) OLDWORLD(set_property(dnode, "AAPL,address", (char *)&props, 2*sizeof(props[0]))); - props[0] = 0; - OLDWORLD(set_property(dnode, "AAPL,bus-id", (char*)props, - 1 * sizeof(props[0]))); + props[0] = i; + set_property(dnode, "AAPL,bus-id", (char*)props, + 1 * sizeof(props[0])); IDE_DPRINTF(DEV_NAME": [io ports 0x%lx]\n", current_channel, chan->mmio); @@ -1663,11 +1681,9 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels) break; } IDE_DPRINTF("%s]: %s\n", media, drive->model); - snprintf(nodebuff, sizeof(nodebuff), - "%s/" DEV_NAME "/%s", path, current_channel, - media); - REGISTER_NAMED_NODE(ob_ide, nodebuff); - dnode = find_dev(nodebuff); + snprintf(nodebuff, sizeof(nodebuff), "%s/%s", + get_path_from_ph(dnode), media); + REGISTER_NAMED_NODE_PHANDLE(ob_ide, nodebuff, dnode); set_int_property(dnode, "reg", j); /* create aliases */ diff --git a/qemu/roms/openbios/drivers/ide.h b/qemu/roms/openbios/drivers/ide.h index d6c4b9f5d..8983c8ecf 100644 --- a/qemu/roms/openbios/drivers/ide.h +++ b/qemu/roms/openbios/drivers/ide.h @@ -167,7 +167,7 @@ struct ide_drive { struct ide_channel { - char name[32]; + phandle_t ph; struct ide_channel *next; /* diff --git a/qemu/roms/openbios/drivers/obio.c b/qemu/roms/openbios/drivers/obio.c index 7c135a362..4ac063188 100644 --- a/qemu/roms/openbios/drivers/obio.c +++ b/qemu/roms/openbios/drivers/obio.c @@ -26,8 +26,6 @@ #define PROMDEV_SCREEN 0 /* output to screen */ #define PROMDEV_TTYA 1 /* in/out to ttya */ -/* DECLARE data structures for the nodes. */ -DECLARE_UNNAMED_NODE( ob_obio, INSTALL_OPEN, sizeof(int) ); void ob_new_obio_device(const char *name, const char *type) @@ -396,45 +394,6 @@ ob_smp_init(unsigned long mem_size) sizeof(struct smp_cfg)); } -static void -ob_obio_open(__attribute__((unused))int *idx) -{ - int ret=1; - RET ( -ret ); -} - -static void -ob_obio_close(__attribute__((unused))int *idx) -{ - selfword("close-deblocker"); -} - -static void -ob_obio_initialize(__attribute__((unused))int *idx) -{ - push_str("/"); - fword("find-device"); - fword("new-device"); - - push_str("obio"); - fword("device-name"); - - push_str("hierarchical"); - fword("device-type"); - - PUSH(2); - fword("encode-int"); - push_str("#address-cells"); - fword("property"); - - PUSH(1); - fword("encode-int"); - push_str("#size-cells"); - fword("property"); - - fword("finish-device"); -} - static void ob_set_obio_ranges(uint64_t base) { @@ -458,27 +417,6 @@ ob_set_obio_ranges(uint64_t base) fword("property"); } -static void -ob_obio_decodeunit(__attribute__((unused)) int *idx) -{ - fword("decode-unit-sbus"); -} - - -static void -ob_obio_encodeunit(__attribute__((unused)) int *idx) -{ - fword("encode-unit-sbus"); -} - -NODE_METHODS(ob_obio) = { - { NULL, ob_obio_initialize }, - { "open", ob_obio_open }, - { "close", ob_obio_close }, - { "encode-unit", ob_obio_encodeunit }, - { "decode-unit", ob_obio_decodeunit }, -}; - int ob_obio_init(uint64_t slavio_base, unsigned long fd_offset, @@ -491,10 +429,6 @@ ob_obio_init(uint64_t slavio_base, unsigned long fd_offset, // http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt //printk("Initializing OBIO devices...\n"); -#if 0 // XXX - REGISTER_NAMED_NODE(ob_obio, "/obio"); - device_end(); -#endif ob_set_obio_ranges(slavio_base); // Zilog Z8530 serial ports, see http://www.zilog.com diff --git a/qemu/roms/openbios/drivers/pci.c b/qemu/roms/openbios/drivers/pci.c index 366f4a17f..5062f302f 100644 --- a/qemu/roms/openbios/drivers/pci.c +++ b/qemu/roms/openbios/drivers/pci.c @@ -144,9 +144,16 @@ static void dump_reg_property(const char* description, int nreg, u32 *reg) } #endif -static unsigned long pci_bus_addr_to_host_addr(uint32_t ba) +static unsigned long pci_bus_addr_to_host_addr(int space, uint32_t ba) { - return arch->host_pci_base + (unsigned long)ba; + if (space == IO_SPACE) { + return arch->io_base + (unsigned long)ba; + } else if (space == MEMORY_SPACE_32) { + return arch->host_pci_base + (unsigned long)ba; + } else { + /* Return unaltered to aid debugging property values */ + return (unsigned long)ba; + } } static void @@ -340,22 +347,27 @@ ob_pci_encode_unit(int *idx) ss, dev, fn, buf); } -/* ( pci-addr.lo pci-addr.hi size -- virt ) */ +/* ( pci-addr.lo pci-addr.mid pci-addr.hi size -- virt ) */ static void ob_pci_map_in(int *idx) { phys_addr_t phys; uint32_t ba; - ucell size, virt; + ucell size, virt, tmp; + int space; PCI_DPRINTF("ob_pci_bar_map_in idx=%p\n", idx); size = POP(); + tmp = POP(); POP(); ba = POP(); - phys = pci_bus_addr_to_host_addr(ba); + /* Get the space from the pci-addr.hi */ + space = ((tmp & PCI_RANGE_TYPE_MASK) >> 24); + + phys = pci_bus_addr_to_host_addr(space, ba); #if defined(CONFIG_OFMEM) ofmem_claim_phys(phys, size, 0); @@ -448,13 +460,18 @@ static void pci_host_set_ranges(const pci_config_t *config) int ncells; ncells = 0; - /* first encode PCI configuration space */ - { - ncells += pci_encode_phys_addr(props + ncells, 0, CONFIGURATION_SPACE, + +#ifdef CONFIG_SPARC64 + /* While configuration space isn't mentioned in the IEEE-1275 PCI + bindings, it appears in the PCI host bridge ranges property in + real device trees. Hence we disable this range for all host + bridges except for SPARC, particularly as it causes Darwin/OS X + to incorrectly calculated PCI memory space ranges on PPC. */ + ncells += pci_encode_phys_addr(props + ncells, 0, CONFIGURATION_SPACE, config->dev, 0, 0); ncells += host_encode_phys_addr(props + ncells, arch->cfg_addr); ncells += pci_encode_size(props + ncells, arch->cfg_len); - } +#endif if (arch->io_base) { ncells += pci_encode_phys_addr(props + ncells, 0, IO_SPACE, @@ -585,13 +602,18 @@ static void pci_set_AAPL_address(const pci_config_t *config) { phandle_t dev = get_cur_dev(); cell props[7]; - int ncells, i; + uint32_t mask; + int ncells, i, flags, space_code; ncells = 0; for (i = 0; i < 6; i++) { if (!config->assigned[i] || !config->sizes[i]) continue; - props[ncells++] = config->assigned[i] & ~0x0000000F; + pci_decode_pci_addr(config->assigned[i], + &flags, &space_code, &mask); + + props[ncells++] = pci_bus_addr_to_host_addr(space_code, + config->assigned[i] & ~mask); } if (ncells) set_property(dev, "AAPL,address", (char *)props, @@ -752,13 +774,19 @@ int macio_keylargo_config_cb (const pci_config_t *config) int vga_config_cb (const pci_config_t *config) { unsigned long rom; - uint32_t rom_size, size; + uint32_t rom_size, size, mask; + int flags, space_code; phandle_t ph; if (config->assigned[0] != 0x00000000) { setup_video(); - rom = pci_bus_addr_to_host_addr(config->assigned[1] & ~0x0000000F); + pci_decode_pci_addr(config->assigned[1], + &flags, &space_code, &mask); + + rom = pci_bus_addr_to_host_addr(space_code, + config->assigned[1] & ~0x0000000F); + rom_size = config->sizes[1]; ph = get_cur_dev(); @@ -824,7 +852,7 @@ int ebus_config_cb(const pci_config_t *config) ncells += pci_encode_phys_addr(props + ncells, flags, space_code, config->dev, PCI_BASE_ADDR_0 + (i * sizeof(uint32_t)), - 0); + config->assigned[i] & ~mask); props[ncells++] = config->sizes[i]; } @@ -997,7 +1025,10 @@ static void ob_pci_add_properties(phandle_t phandle, } pci_set_assigned_addresses(phandle, config, num_bars); - OLDWORLD(pci_set_AAPL_address(config)); + + if (is_apple()) { + pci_set_AAPL_address(config); + } PCI_DPRINTF("\n"); } @@ -1397,9 +1428,11 @@ static void ob_pci_set_available(phandle_t host, unsigned long mem_base, unsigne static void ob_pci_host_set_interrupt_map(phandle_t host) { - phandle_t dnode = 0; - u32 props[128]; - int i; + phandle_t dnode = 0, pci_childnode = 0; + u32 props[128], intno; + int i, ncells, len; + u32 *val, addr; + char *reg; #if defined(CONFIG_PPC) phandle_t target_node; @@ -1420,16 +1453,22 @@ static void ob_pci_host_set_interrupt_map(phandle_t host) target_node = find_dev("/pci/mac-io/escc/ch-b"); set_int_property(target_node, "interrupt-parent", dnode); + target_node = find_dev("/pci/mac-io/escc-legacy/ch-a"); + set_int_property(target_node, "interrupt-parent", dnode); + + target_node = find_dev("/pci/mac-io/escc-legacy/ch-b"); + set_int_property(target_node, "interrupt-parent", dnode); + /* QEMU only emulates 2 of the 3 ata buses currently */ /* On a new world Mac these are not numbered but named by the * ATA version they support. Thus we have: ata-3, ata-3, ata-4 * On g3beige they all called just ide. - * We take ata-3 and ata-4 which seems to work for both - * at least for clients we care about */ - target_node = find_dev("/pci/mac-io/ata-3"); + * We take 2 x ata-3 buses which seems to work for + * at least the clients we care about */ + target_node = find_dev("/pci/mac-io/ata-3@20000"); set_int_property(target_node, "interrupt-parent", dnode); - target_node = find_dev("/pci/mac-io/ata-4"); + target_node = find_dev("/pci/mac-io/ata-3@21000"); set_int_property(target_node, "interrupt-parent", dnode); target_node = find_dev("/pci/mac-io/via-cuda"); @@ -1437,69 +1476,61 @@ static void ob_pci_host_set_interrupt_map(phandle_t host) target_node = find_dev("/pci"); set_int_property(target_node, "interrupt-parent", dnode); - - /* openpic interrupt mapping */ - for (i = 0; i < (7*8); i += 7) { - props[i + PCI_INT_MAP_PCI0] = 0; - props[i + PCI_INT_MAP_PCI1] = 0; - props[i + PCI_INT_MAP_PCI2] = 0; - props[i + PCI_INT_MAP_PCI_INT] = (i / 7) + 1; // starts at PINA=1 - props[i + PCI_INT_MAP_PIC_HANDLE] = dnode; - props[i + PCI_INT_MAP_PIC_INT] = arch->irqs[i / 7]; - props[i + PCI_INT_MAP_PIC_POL] = 3; - } - set_property(host, "interrupt-map", (char *)props, 7 * 8 * sizeof(props[0])); - - props[PCI_INT_MAP_PCI0] = 0; - props[PCI_INT_MAP_PCI1] = 0; - props[PCI_INT_MAP_PCI2] = 0; - props[PCI_INT_MAP_PCI_INT] = 0x7; - - set_property(host, "interrupt-map-mask", (char *)props, 4 * sizeof(props[0])); } -#elif defined(CONFIG_SPARC64) - int ncells, len; - u32 *val, addr; - char *reg; +#else + /* PCI host bridge is the default interrupt controller */ + dnode = host; +#endif /* Set interrupt-map for PCI devices with an interrupt pin present */ ncells = 0; PUSH(host); fword("child"); - dnode = POP(); - while (dnode) { - if (get_int_property(dnode, "interrupts", &len)) { - reg = get_property(dnode, "reg", &len); - if (reg) { + pci_childnode = POP(); + while (pci_childnode) { + intno = get_int_property(pci_childnode, "interrupts", &len); + if (len && intno) { + reg = get_property(pci_childnode, "reg", &len); + if (len && reg) { val = (u32 *)reg; for (i = 0; i < (len / sizeof(u32)); i += 5) { addr = val[i]; /* Device address is in 1st 32-bit word of encoded PCI address for config space */ - if (!(addr & 0x03000000)) { + if ((addr & PCI_RANGE_TYPE_MASK) == PCI_RANGE_CONFIG) { +#if defined(CONFIG_SPARC64) ncells += pci_encode_phys_addr(props + ncells, 0, 0, addr, 0, 0); - props[ncells++] = 1; /* always interrupt pin 1 for QEMU */ - props[ncells++] = host; - props[ncells++] = SUN4U_INTERRUPT(addr, 1); + props[ncells++] = intno; + props[ncells++] = dnode; + props[ncells++] = SUN4U_INTERRUPT(addr, intno); +#elif defined(CONFIG_PPC) + ncells += pci_encode_phys_addr(props + ncells, 0, 0, addr, 0, 0); + props[ncells++] = intno; + props[ncells++] = dnode; + props[ncells++] = arch->irqs[intno - 1]; + props[ncells++] = 3; +#else + /* Keep compiler quiet */ + dnode = dnode; +#endif } } } } - PUSH(dnode); + PUSH(pci_childnode); fword("peer"); - dnode = POP(); + pci_childnode = POP(); } set_property(host, "interrupt-map", (char *)props, ncells * sizeof(props[0])); props[0] = 0x0000f800; props[1] = 0x0; props[2] = 0x0; - props[3] = 7; + props[3] = 0x7; set_property(host, "interrupt-map-mask", (char *)props, 4 * sizeof(props[0])); -#endif } int ob_pci_init(void) diff --git a/qemu/roms/openbios/drivers/pci.fs b/qemu/roms/openbios/drivers/pci.fs index 563b652a4..a7b56e1f8 100644 --- a/qemu/roms/openbios/drivers/pci.fs +++ b/qemu/roms/openbios/drivers/pci.fs @@ -12,59 +12,19 @@ rot encode-int encode+ ; -\ Get region offset for BAR reg -: pci-bar-offset@ ( bar-reg -- off.lo off.hi -1 | 0 ) - " reg" active-package get-package-property 0= if - begin - decode-phys \ ( reg prop prop-len phys.lo phys.mid phys.hi ) - ff and 5 pick = if - >r >r 3drop r> r> - -1 exit - else - 2drop - then - \ Drop the size as we don't need it - decode-int drop decode-int drop - dup 0= - until - 3drop - 0 exit - else - 0 - then - ; - -\ Get region size for BAR reg -: pci-bar-size@ ( bar-reg -- size ) - " reg" active-package get-package-property 0= if - begin - decode-phys \ ( reg prop prop-len phys.lo phys.mid phys.hi ) - ff and 5 pick = if - 2drop decode-int drop - decode-int - >r 3drop r> - exit - else - 2drop decode-int drop - decode-int drop - then - dup 0= - until - 3drop - 0 \ default size of 0 if BAR not found - then - ; - -\ Get base address for configured BAR reg -: pci-bar-base@ ( bar-reg -- addr.lo addr.hi -1 | 0 ) +\ Get PCI physical address and size for configured BAR reg +: pci-bar>pci-addr ( bar-reg -- addr.lo addr.mid addr.hi size -1 | 0 ) " assigned-addresses" active-package get-package-property 0= if begin decode-phys \ ( reg prop prop-len phys.lo phys.mid phys.hi ) - ff and 5 pick = if - >r >r 3drop r> r> + dup ff and 6 pick = if + >r >r >r rot drop + decode-int drop decode-int + -rot 2drop + r> swap r> r> rot -1 exit else - 2drop + 3drop then \ Drop the size as we don't need it decode-int drop decode-int drop @@ -77,16 +37,4 @@ then ; -\ Get PCI bus address and size for configured BAR reg -: pci-bar>pci-region ( bar-reg -- addr.lo addr.hi size ) - dup - >r pci-bar-offset@ if - swap r@ pci-bar-base@ if - swap d+ - then - swap r@ pci-bar-size@ - then - r> drop - ; - [THEN] diff --git a/qemu/roms/openbios/drivers/pci.h b/qemu/roms/openbios/drivers/pci.h index 84a2b2cf6..d5aa5f84a 100644 --- a/qemu/roms/openbios/drivers/pci.h +++ b/qemu/roms/openbios/drivers/pci.h @@ -59,6 +59,15 @@ #define PCI_MIN_GNT 0x3e /* 8 bits */ #define PCI_MAX_LAT 0x3f /* 8 bits */ +#define PCI_RANGE_RELOCATABLE 0x80000000 +#define PCI_RANGE_PREFETCHABLE 0x40000000 +#define PCI_RANGE_ALIASED 0x20000000 +#define PCI_RANGE_TYPE_MASK 0x03000000 +#define PCI_RANGE_MMIO_64BIT 0x03000000 +#define PCI_RANGE_MMIO 0x02000000 +#define PCI_RANGE_IOPORT 0x01000000 +#define PCI_RANGE_CONFIG 0x00000000 + typedef struct { u16 signature; u8 reserved[0x16]; diff --git a/qemu/roms/openbios/drivers/sbus.c b/qemu/roms/openbios/drivers/sbus.c index a9b26c0a0..4caa59aaf 100644 --- a/qemu/roms/openbios/drivers/sbus.c +++ b/qemu/roms/openbios/drivers/sbus.c @@ -369,31 +369,6 @@ sbus_probe_slot_ss600mp(unsigned int slot, uint64_t base) } } -static void -ob_sbus_open(void) -{ - int ret=1; - RET ( -ret ); -} - -static void -ob_sbus_close(void) -{ - selfword("close-deblocker"); -} - -static void -ob_sbus_initialize(void) -{ -} - - -NODE_METHODS(ob_sbus_node) = { - { NULL, ob_sbus_initialize }, - { "open", ob_sbus_open }, - { "close", ob_sbus_close }, -}; - struct sbus_offset { int slot, type; uint64_t base; diff --git a/qemu/roms/openbios/drivers/usbohci_private.h b/qemu/roms/openbios/drivers/usbohci_private.h index b3a723e21..99c964100 100644 --- a/qemu/roms/openbios/drivers/usbohci_private.h +++ b/qemu/roms/openbios/drivers/usbohci_private.h @@ -43,7 +43,7 @@ // FIXME: fake typedef enum { CMD} reg; - enum { + extern enum { NumberDownstreamPorts = 1<<0, PowerSwitchingMode = 1<<8, NoPowerSwitching = 1<<9, @@ -53,17 +53,17 @@ PowerOnToPowerGoodTime = 1<<24 } HcRhDescriptorAReg; - enum { + extern enum { NumberDownstreamPortsMask = MASK(0, 8), PowerOnToPowerGoodTimeMask = MASK(24, 8) } HcRhDescriptorAMask; - enum { + extern enum { DeviceRemovable = 1<<0, PortPowerControlMask = 1<<16 } HcRhDescriptorBReg; - enum { + extern enum { CurrentConnectStatus = 1<<0, PortEnableStatus = 1<<1, PortSuspendStatus = 1<<2, @@ -77,7 +77,7 @@ PortOverCurrentIndicatorChange = 1<<19, PortResetStatusChange = 1<<20 } HcRhPortStatusRead; - enum { + extern enum { ClearPortEnable = 1<<0, SetPortEnable = 1<<1, SetPortSuspend = 1<<2, @@ -87,7 +87,7 @@ ClearPortPower = 1<<9, } HcRhPortStatusSet; - enum { + extern enum { LocalPowerStatus = 1<<0, OverCurrentIndicator = 1<<1, DeviceRemoteWakeupEnable = 1<<15, @@ -96,18 +96,18 @@ ClearRemoteWakeupEnable = 1<<31 } HcRhStatusReg; - enum { + extern enum { FrameInterval = 1<<0, FSLargestDataPacket = 1<<16, FrameIntervalToggle = 1<<31 } HcFmIntervalOffset; - enum { + extern enum { FrameIntervalMask = MASK(0, 14), FSLargestDataPacketMask = MASK(16, 15), FrameIntervalToggleMask = MASK(31, 1) } HcFmIntervalMask; - enum { + extern enum { ControlBulkServiceRatio = 1<<0, PeriodicListEnable = 1<<2, IsochronousEnable = 1<<3, @@ -119,7 +119,7 @@ RemoteWakeupEnable = 1<<10 } HcControlReg; - enum { + extern enum { ControlBulkServiceRatioMask = MASK(0, 2), HostControllerFunctionalStateMask = MASK(6, 2) } HcControlMask; @@ -131,7 +131,7 @@ USBSuspend = 3*HostControllerFunctionalState }; - enum { + extern enum { HostControllerReset = 1<<0, ControlListFilled = 1<<1, BulkListFilled = 1<<2, @@ -139,16 +139,16 @@ SchedulingOverrunCount = 1<<16 } HcCommandStatusReg; - enum { + extern enum { SchedulingOverrunCountMask = MASK(16, 2) } HcCommandStatusMask; - enum { + extern enum { FrameRemaining = 1<<0, FrameRemainingToggle = 1<<31 } HcFmRemainingReg; - enum { + extern enum { SchedulingOverrung = 1<<0, WritebackDoneHead = 1<<1, StartofFrame = 1<<2, diff --git a/qemu/roms/openbios/drivers/vga.fs b/qemu/roms/openbios/drivers/vga.fs index ec4c6c5f1..29a043a7a 100644 --- a/qemu/roms/openbios/drivers/vga.fs +++ b/qemu/roms/openbios/drivers/vga.fs @@ -109,16 +109,17 @@ h# 1 constant VBE_DISPI_ENABLED \ PCI \ -" pci-bar>pci-region" (find-xt) value pci-bar>pci-region-xt -: pci-bar>pci-region pci-bar>pci-region-xt execute ; +" pci-bar>pci-addr" (find-xt) value pci-bar>pci-addr-xt +: pci-bar>pci-addr pci-bar>pci-addr-xt execute ; h# 10 constant cfg-bar0 \ Framebuffer BAR -1 value fb-addr : map-fb ( -- ) - cfg-bar0 pci-bar>pci-region \ ( pci-addr.lo pci-addr.hi size ) - " pci-map-in" $call-parent - to fb-addr + cfg-bar0 pci-bar>pci-addr if \ ( pci-addr.lo pci-addr.mid pci-addr.hi size ) + " pci-map-in" $call-parent + to fb-addr + then ; \ diff --git a/qemu/roms/openbios/forth/lib/string.fs b/qemu/roms/openbios/forth/lib/string.fs index eb6474917..f97db232f 100644 --- a/qemu/roms/openbios/forth/lib/string.fs +++ b/qemu/roms/openbios/forth/lib/string.fs @@ -125,3 +125,17 @@ : parse-hex ( str len -- value ) base @ hex -rot $number if 0 then swap base ! ; + + +\ ----------------------------------------------------- +\ miscellaneous functions +\ ----------------------------------------------------- + +: rot13 ( c - c ) + dup upc [char] A [char] M between if d# 13 + exit then + dup upc [char] N [char] Z between if d# 13 - then +; + +: rot13-str ( str len -- newstr len ) + strdup 2dup bounds ?do i c@ rot13 i c! loop +; diff --git a/qemu/roms/openbios/forth/system/ciface.fs b/qemu/roms/openbios/forth/system/ciface.fs index fd6c54efd..85a607627 100644 --- a/qemu/roms/openbios/forth/system/ciface.fs +++ b/qemu/roms/openbios/forth/system/ciface.fs @@ -326,6 +326,14 @@ external : exit ( -- ) ." EXIT" + + \ Execute (exit) hook if one exists + s" (exit)" $find if + execute + else + 2drop + then + outer-interpreter ; diff --git a/qemu/roms/openbios/include/arch/ppc/types.h b/qemu/roms/openbios/include/arch/ppc/types.h index 69b3db405..b2246d060 100644 --- a/qemu/roms/openbios/include/arch/ppc/types.h +++ b/qemu/roms/openbios/include/arch/ppc/types.h @@ -84,21 +84,13 @@ typedef uint32_t prom_uarg_t; /* size named types */ typedef unsigned char u8; -typedef unsigned char __u8; typedef unsigned short u16; -typedef unsigned short __u16; typedef unsigned int u32; -typedef unsigned int __u32; typedef unsigned long long u64; -typedef unsigned long long __u64; typedef signed char s8; -typedef signed char __s8; typedef short s16; -typedef short __s16; typedef int s32; -typedef int __s32; typedef long long s64; -typedef long long __s64; #endif diff --git a/qemu/roms/openbios/include/arch/sparc64/io.h b/qemu/roms/openbios/include/arch/sparc64/io.h index 2e4dfa37f..0f1a73284 100644 --- a/qemu/roms/openbios/include/arch/sparc64/io.h +++ b/qemu/roms/openbios/include/arch/sparc64/io.h @@ -9,7 +9,7 @@ extern unsigned long va_shift; // Set in entry.S // Defined in ldscript -extern char _start, _data, _stack, _estack, _end, _iomem; +extern char _start, _data, _stack, _estack, _fcstack, _efcstack, _end, _iomem; // XXX check use and merge #define phys_to_virt(phys) ((void *) ((unsigned long) (phys))) diff --git a/qemu/roms/openbios/include/drivers/drivers.h b/qemu/roms/openbios/include/drivers/drivers.h index 3b83b12d1..48f81a870 100644 --- a/qemu/roms/openbios/include/drivers/drivers.h +++ b/qemu/roms/openbios/include/drivers/drivers.h @@ -52,6 +52,7 @@ void kbd_init(uint64_t base); /* drivers/ide.c */ int ob_ide_init(const char *path, uint32_t io_port0, uint32_t ctl_port0, uint32_t io_port1, uint32_t ctl_port1); +void ob_ide_quiesce(void); int macio_ide_init(const char *path, uint32_t addr, int nb_channels); #endif #ifdef CONFIG_DRIVER_ESP diff --git a/qemu/roms/openbios/include/libopenbios/bindings.h b/qemu/roms/openbios/include/libopenbios/bindings.h index de9c77520..4ec978912 100644 --- a/qemu/roms/openbios/include/libopenbios/bindings.h +++ b/qemu/roms/openbios/include/libopenbios/bindings.h @@ -56,6 +56,7 @@ extern ihandle_t open_package( const char *argstr, phandle_t ph ); extern ihandle_t open_dev( const char *spec ); extern void close_package( ihandle_t ih ); extern void close_dev( ihandle_t ih ); +extern char *get_path_from_ph( phandle_t ph ); /* property access */ extern void set_property( phandle_t ph, const char *name, diff --git a/qemu/roms/openbios/libopenbios/bindings.c b/qemu/roms/openbios/libopenbios/bindings.c index 5323421f5..4f7a99379 100644 --- a/qemu/roms/openbios/libopenbios/bindings.c +++ b/qemu/roms/openbios/libopenbios/bindings.c @@ -366,6 +366,14 @@ find_dev( const char *path ) return ret; } +char * +get_path_from_ph( phandle_t ph ) +{ + PUSH(ph); + fword("get-package-path"); + return pop_fstr_copy(); +} + phandle_t dt_iter_begin( void ) { diff --git a/qemu/roms/openbios/libopenbios/bootinfo_load.c b/qemu/roms/openbios/libopenbios/bootinfo_load.c index fa9e36bd4..f33678185 100644 --- a/qemu/roms/openbios/libopenbios/bootinfo_load.c +++ b/qemu/roms/openbios/libopenbios/bootinfo_load.c @@ -161,6 +161,12 @@ bootinfo_init_program(void) feval("load-size"); size = POP(); + /* Some bootinfo scripts contain a binary payload after the + NULL-terminated Forth string such as OS 9. Restrict our + size to just the Forth section, otherwise we end up trying + to allocate memory for the entire binary which might fail. */ + size = strnlen(base, size); + bootscript = malloc(size); if (bootscript == NULL) { DPRINTF("Can't malloc %d bytes\n", size); diff --git a/qemu/roms/seabios/.version b/qemu/roms/seabios/.version index a96337339..89c0c6acb 100644 --- a/qemu/roms/seabios/.version +++ b/qemu/roms/seabios/.version @@ -1 +1 @@ -rel-1.8.2-0-g33fbe13 +rel-1.9.1-0-gb3ef39f diff --git a/qemu/roms/seabios/Makefile b/qemu/roms/seabios/Makefile index 83cdff377..4e4092d07 100644 --- a/qemu/roms/seabios/Makefile +++ b/qemu/roms/seabios/Makefile @@ -34,15 +34,16 @@ SRCBOTH=misc.c stacks.c output.c string.c block.c cdrom.c disk.c mouse.c kbd.c \ hw/usb.c hw/usb-uhci.c hw/usb-ohci.c hw/usb-ehci.c \ hw/usb-hid.c hw/usb-msc.c hw/usb-uas.c \ hw/blockcmd.c hw/floppy.c hw/ata.c hw/ramdisk.c \ - hw/virtio-ring.c hw/virtio-pci.c hw/virtio-blk.c hw/virtio-scsi.c \ hw/lsi-scsi.c hw/esp-scsi.c hw/megasas.c SRC16=$(SRCBOTH) -SRC32FLAT=$(SRCBOTH) post.c memmap.c malloc.c romfile.c x86.c optionroms.c \ - pmm.c font.c boot.c bootsplash.c jpeg.c bmp.c \ +SRC32FLAT=$(SRCBOTH) post.c e820map.c malloc.c romfile.c x86.c optionroms.c \ + pmm.c font.c boot.c bootsplash.c jpeg.c bmp.c tcgbios.c sha1.c \ hw/ahci.c hw/pvscsi.c hw/usb-xhci.c hw/usb-hub.c hw/sdcard.c \ - fw/coreboot.c fw/lzmadecode.c fw/csm.c fw/biostables.c \ + fw/coreboot.c fw/lzmadecode.c fw/multiboot.c fw/csm.c fw/biostables.c \ fw/paravirt.c fw/shadow.c fw/pciinit.c fw/smm.c fw/smp.c fw/mtrr.c fw/xen.c \ - fw/acpi.c fw/mptable.c fw/pirtable.c fw/smbios.c fw/romfile_loader.c + fw/acpi.c fw/mptable.c fw/pirtable.c fw/smbios.c fw/romfile_loader.c \ + hw/virtio-ring.c hw/virtio-pci.c hw/virtio-blk.c hw/virtio-scsi.c \ + hw/tpm_drivers.c SRC32SEG=string.c output.c pcibios.c apm.c stacks.c hw/pci.c hw/serialio.c DIRS=src src/hw src/fw vgasrc @@ -50,6 +51,8 @@ DIRS=src src/hw src/fw vgasrc cc-option=$(shell if test -z "`$(1) $(2) -S -o /dev/null -xc /dev/null 2>&1`" \ ; then echo "$(2)"; else echo "$(3)"; fi ;) +EXTRAVERSION= + CPPFLAGS = -P -MD -MT $@ COMMONCFLAGS := -I$(OUT) -Isrc -Os -MD -g \ @@ -62,6 +65,7 @@ COMMONCFLAGS := -I$(OUT) -Isrc -Os -MD -g \ COMMONCFLAGS += $(call cc-option,$(CC),-nopie,) COMMONCFLAGS += $(call cc-option,$(CC),-fno-stack-protector,) COMMONCFLAGS += $(call cc-option,$(CC),-fno-stack-protector-all,) +COMMONCFLAGS += $(call cc-option,$(CC),-fstack-check=no,) COMMA := , CFLAGS32FLAT := $(COMMONCFLAGS) -DMODE16=0 -DMODESEGMENT=0 @@ -152,10 +156,10 @@ $(OUT)romlayout.o: src/romlayout.S $(OUT)autoconf.h $(OUT)asm-offsets.h @echo " Compiling (16bit) $@" $(Q)$(CC) $(CFLAGS16) -c -D__ASSEMBLY__ $< -o $@ -$(OUT)romlayout16.lds: $(OUT)ccode32flat.o $(OUT)code32seg.o $(OUT)ccode16.o $(OUT)romlayout.o scripts/layoutrom.py scripts/buildversion.sh +$(OUT)romlayout16.lds: $(OUT)ccode32flat.o $(OUT)code32seg.o $(OUT)ccode16.o $(OUT)romlayout.o src/version.c scripts/layoutrom.py scripts/buildversion.py @echo " Building ld scripts" - $(Q)BUILD_VERSION="$(VERSION)" ./scripts/buildversion.sh $(OUT)version.c - $(Q)$(CC) $(CFLAGS32FLAT) -c $(OUT)version.c -o $(OUT)version.o + $(Q)$(PYTHON) ./scripts/buildversion.py -e "$(EXTRAVERSION)" -t "$(CC);$(AS);$(LD);$(OBJCOPY);$(OBJDUMP);$(STRIP)" $(OUT)autoversion.h + $(Q)$(CC) $(CFLAGS32FLAT) -c src/version.c -o $(OUT)version.o $(Q)$(LD) $(LD32BIT_FLAG) -r $(OUT)ccode32flat.o $(OUT)version.o -o $(OUT)code32flat.o $(Q)$(LD) $(LD32BIT_FLAG) -r $(OUT)ccode16.o $(OUT)romlayout.o -o $(OUT)code16.o $(Q)$(OBJDUMP) -thr $(OUT)code32flat.o > $(OUT)code32flat.o.objdump @@ -177,7 +181,7 @@ $(OUT)rom32seg.o: $(OUT)code32seg.o $(OUT)romlayout32seg.lds $(OUT)rom.o: $(OUT)rom16.strip.o $(OUT)rom32seg.strip.o $(OUT)code32flat.o $(OUT)romlayout32flat.lds @echo " Linking $@" - $(Q)$(LD) -T $(OUT)romlayout32flat.lds $(OUT)rom16.strip.o $(OUT)rom32seg.strip.o $(OUT)code32flat.o -o $@ + $(Q)$(LD) -N -T $(OUT)romlayout32flat.lds $(OUT)rom16.strip.o $(OUT)rom32seg.strip.o $(OUT)code32flat.o -o $@ $(OUT)bios.bin.prep: $(OUT)rom.o scripts/checkrom.py @echo " Prepping $@" @@ -224,10 +228,10 @@ $(OUT)vgaentry.o: vgasrc/vgaentry.S $(OUT)autoconf.h $(OUT)asm-offsets.h @echo " Compiling (16bit) $@" $(Q)$(CC) $(CFLAGS16) -c -D__ASSEMBLY__ $< -o $@ -$(OUT)vgarom.o: $(OUT)vgaccode16.o $(OUT)vgaentry.o $(OUT)vgasrc/vgalayout.lds scripts/buildversion.sh +$(OUT)vgarom.o: $(OUT)vgaccode16.o $(OUT)vgaentry.o $(OUT)vgasrc/vgalayout.lds vgasrc/vgaversion.c scripts/buildversion.py @echo " Linking $@" - $(Q)BUILD_VERSION="$(VERSION)" ./scripts/buildversion.sh $(OUT)vgaversion.c VAR16 - $(Q)$(CC) $(CFLAGS16) -c $(OUT)vgaversion.c -o $(OUT)vgaversion.o + $(Q)$(PYTHON) ./scripts/buildversion.py -e "$(EXTRAVERSION)" -t "$(CC);$(AS);$(LD);$(OBJCOPY);$(OBJDUMP);$(STRIP)" $(OUT)autovgaversion.h + $(Q)$(CC) $(CFLAGS16) -c vgasrc/vgaversion.c -o $(OUT)vgaversion.o $(Q)$(LD) --gc-sections -T $(OUT)vgasrc/vgalayout.lds $(OUT)vgaccode16.o $(OUT)vgaentry.o $(OUT)vgaversion.o -o $@ $(OUT)vgabios.bin.raw: $(OUT)vgarom.o diff --git a/qemu/roms/seabios/docs/Build_overview.md b/qemu/roms/seabios/docs/Build_overview.md index 26db22691..8c6b2f458 100644 --- a/qemu/roms/seabios/docs/Build_overview.md +++ b/qemu/roms/seabios/docs/Build_overview.md @@ -52,6 +52,34 @@ CSM_ENABLE'. The SeaBIOS binary will be included as a discrete file within the 'Flash Volume' which is created, and there are tools which will extract it and allow it to be replaced. +Distribution builds +=================== + +If one is building a binary version of SeaBIOS as part of a package +(such as an rpm) or for wide distribution, please provide the +EXTRAVERSION field during the build. For example: + +`make EXTRAVERSION="-${RPM_PACKAGE_RELEASE}"` + +The EXTRAVERSION field should provide the package version (if +applicable) and the name of the distribution (if that's not already +obvious from the package version). This string will be appended to the +main SeaBIOS version. The above information helps SeaBIOS developers +correlate defect reports to the source code and build environment. + +If one is building a binary in a build environment that does not have +access to the git tool or does not have the full SeaBIOS git repo +available, then please use an official SeaBIOS release tar file as +source. If building from a snapshot (where there is no official +SeaBIOS tar) then one should generate a snapshot tar file on a machine +that does support git using the scripts/tarball.sh tool. For example: + +`scripts/tarball.sh` + +The tarball.sh script encodes version information in the resulting tar +file which the build can extract and include in the final binary. The +above EXTRAVERSION field should still be set when building from a tar. + Overview of files in the repository =================================== @@ -61,11 +89,7 @@ drivers. The **src/fw/** directory contains source code for platform firmware initialization. The **src/std/** directory contains header files describing standard bios, firmware, and hardware interfaces. -The **vgasrc/** directory contains code for VGA BIOS implementations. -This code is separate from the main BIOS code in the src/ directory. -When the build is configured to produce a VGA BIOS the resulting -binary is found in out/vgabios.bin. The VGA BIOS code is always -compiled in 16bit mode. +The **vgasrc/** directory contains code for [SeaVGABIOS](SeaVGABIOS). The **scripts/** directory contains helper utilities for manipulating and building the final roms. diff --git a/qemu/roms/seabios/docs/Contributing.md b/qemu/roms/seabios/docs/Contributing.md new file mode 100644 index 000000000..d0f2b5b5e --- /dev/null +++ b/qemu/roms/seabios/docs/Contributing.md @@ -0,0 +1,20 @@ +SeaBIOS welcomes contributions of code (either fixing bugs or adding +new functionality). At a high level, the process to contribute a +change is: + +1. [Obtain](Download) the current code and documentation +2. Enhance and test the code locally +3. Submit changes to the SeaBIOS [mailing list](Mailinglist) as a + patch +4. Receive feedback, answer questions, and possibly provide updated + patches +5. When accepted, a maintainer (Kevin O'Connor or Gerd Hoffman) will + commit the change to the master SeaBIOS repository + +The SeaBIOS patch submission process is similar to the +[QEMU process](http://wiki.qemu.org/Contribute/SubmitAPatch). Please +review the QEMU process for more details and tips on the best way to +submit patches. The SeaBIOS C code does follow a slightly different +coding style from QEMU (eg, mixed code and C99 style variable +declarations are encouraged, braces are not required around single +statement blocks), however patches in the QEMU style are acceptable. diff --git a/qemu/roms/seabios/docs/Debugging.md b/qemu/roms/seabios/docs/Debugging.md index 03567de4d..7ab5d02d8 100644 --- a/qemu/roms/seabios/docs/Debugging.md +++ b/qemu/roms/seabios/docs/Debugging.md @@ -55,8 +55,10 @@ rate. The tool can also timestamp the messages from the QEMU debug port. To use with QEMU run the following: -`mkfifo qemudebugpipe`\ -`qemu -chardev pipe,path=qemudebugpipe,id=seabios -device isa-debugcon,iobase=0x402,chardev=seabios ...` +``` +mkfifo qemudebugpipe +qemu -chardev pipe,path=qemudebugpipe,id=seabios -device isa-debugcon,iobase=0x402,chardev=seabios ... +``` and then in another session: @@ -84,20 +86,23 @@ bios 16bit code) or out/rom.o (to debug bios 32bit code). For example: `gdb out/rom16.o` -Once in gdb, use the command "target remote localhost:1234" to have +Once in gdb, use the command `target remote localhost:1234` to have gdb connect to QEMU. See the QEMU documentation for more information on using gdb and QEMU in this mode. -When debugging 16bit code, also run the following commands in gdb: +When debugging 16bit code it is necessary to load the 16bit symbols +twice in order for gdb to properly handle break points. To do this, +run the following command `objcopy --adjust-vma 0xf0000 out/rom16.o +rom16offset.o` and then run the following in gdb: -`set architecture i8086`\ -`add-symbol-file out/rom16.o 0xf0000` +``` +set architecture i8086 +add-symbol-file rom16offset.o 0 +``` -The second command loads the 16bit symbols a second time at an offset -of 0xf0000, which helps gdb set and catch breakpoints correctly. - -To debug a VGA BIOS image, run "gdb out/vgarom.o" add use the gdb -command "add-symbol-file out/vgarom.o 0xc0000" to load the 16bit VGA +To debug a VGA BIOS image, run `gdb out/vgarom.o`, create a +vgaromoffset.o file with offset 0xc0000, add use the gdb +command `add-symbol-file out/vgaromoffset.o 0` to load the 16bit VGA BIOS symbols twice. If debugging the 32bit SeaBIOS initialization code with gdb, note that diff --git a/qemu/roms/seabios/docs/Developer_Documentation.md b/qemu/roms/seabios/docs/Developer_Documentation.md index d50455d36..24bf48a3e 100644 --- a/qemu/roms/seabios/docs/Developer_Documentation.md +++ b/qemu/roms/seabios/docs/Developer_Documentation.md @@ -10,15 +10,16 @@ page. See details on [building SeaBIOS](Build overview). There is also information on the SeaBIOS [Memory Model](Memory Model). - Along with information on SeaBIOS [Execution and code flow](Execution -and code flow). +and code flow). A description of the process of linking the final +SeaBIOS binary is available at [Linking overview](Linking overview). -A description of the process of linking the final SeaBIOS binary is -available at [Linking overview](Linking overview). +The list of available runtime configuration items is at +[runtime config](Runtime_config). To debug SeaBIOS and report problems see SeaBIOS -[debugging](Debugging). +[debugging](Debugging). To contribute changes to SeaBIOS see +[contributing](Contributing). Useful links to specifications is available at [Developer links](Developer links). diff --git a/qemu/roms/seabios/docs/Download.md b/qemu/roms/seabios/docs/Download.md index a49c6fb74..9b1492ac5 100644 --- a/qemu/roms/seabios/docs/Download.md +++ b/qemu/roms/seabios/docs/Download.md @@ -9,8 +9,10 @@ The SeaBIOS project uses the [git](http://git-scm.com/) revision control system. To download the latest source from revision control, run: -`$ git clone git://git.seabios.org/seabios.git seabios`\ -`$ cd seabios` +``` +$ git clone git://git.seabios.org/seabios.git seabios +$ cd seabios +``` There's also a [website](http://git.seabios.org/) to browse the latest source code online. diff --git a/qemu/roms/seabios/docs/Execution_and_code_flow.md b/qemu/roms/seabios/docs/Execution_and_code_flow.md index 9396ecaa4..a54776eef 100644 --- a/qemu/roms/seabios/docs/Execution_and_code_flow.md +++ b/qemu/roms/seabios/docs/Execution_and_code_flow.md @@ -36,7 +36,7 @@ process. The POST phase itself has several sub-phases. -* The "preinit" sub-phase: code run prior to code relocation. +* The "preinit" sub-phase: code run prior to [code relocation](Linking overview#Code relocation). * The "init" sub-phase: code to initialize internal variables and interfaces. * The "setup" sub-phase: code to setup hardware and drivers. diff --git a/qemu/roms/seabios/docs/Linking_overview.md b/qemu/roms/seabios/docs/Linking_overview.md index fb938b632..bcb8298c3 100644 --- a/qemu/roms/seabios/docs/Linking_overview.md +++ b/qemu/roms/seabios/docs/Linking_overview.md @@ -92,15 +92,9 @@ those situations where an address of a C function in another mode is required the build supports symbols with a special "\_cfuncX_" prefix. The layoutrom.py script detects these references and will emit a corresponding symbol definitions in the linker script that points to -the C code of the specified mode. This is typically seen with code -like: - -`extern void _cfunc32flat_process_op(void);`\ -`return call32(_cfunc32flat_process_op, 0, 0);` - -In the above example, when the build finds the symbol -"\_cfunc32flat_process_op" it will emit that symbol with the physical -address of the 32bit "flat" version of the process_op() C function. +the C code of the specified mode. The call32() and stack_hop_back() +macros automatically add the required prefix for C code, but the +prefixes need to be explicitly added in assembler code. Build garbage collection ------------------------ diff --git a/qemu/roms/seabios/docs/Releases.md b/qemu/roms/seabios/docs/Releases.md index 6a1ecd564..c24d3c0b7 100644 --- a/qemu/roms/seabios/docs/Releases.md +++ b/qemu/roms/seabios/docs/Releases.md @@ -1,6 +1,25 @@ History of SeaBIOS releases. Please see [download](Download) for information on obtaining these releases. +SeaBIOS 1.9.0 +============= + +Available on 20151117. Major changes in this release: + +* The default boot menu key is now the ESC key (instead of F12) +* Initial support for Trusted Platform Module (TPM) hardware and BIOS calls +* Initial support for chain loading SeaBIOS from Grub (via multiboot + support) +* Initial support for booting from SD cards on real hardware +* virtio 1.0 device support +* The build will no longer include the build hostname or build time on + "clean" builds. This makes the build binaries more "reproducible". +* Basic support for running SeaBIOS on Baytrail Chromebooks +* SeaVGABIOS improvements: + * Improved support for old versions of x86emu (the "leal" + instruction is now emulated) +* Several bug fixes and code cleanups + SeaBIOS 1.8.0 ============= @@ -23,6 +42,16 @@ two release numbers (eg, 1.8) and stable releases will use three numbers (eg, 1.8.1). The prior behavior of using a forth number (eg, 1.7.5.1) for stable releases will no longer be used. +SeaBIOS 1.8.1 +------------- + +Available on 20150316. Stable release containing only bug fixes. + +SeaBIOS 1.8.2 +------------- + +Available on 20150617. Stable release containing only bug fixes. + SeaBIOS 1.7.5 ============= diff --git a/qemu/roms/seabios/docs/Runtime_config.md b/qemu/roms/seabios/docs/Runtime_config.md new file mode 100644 index 000000000..d6fea2827 --- /dev/null +++ b/qemu/roms/seabios/docs/Runtime_config.md @@ -0,0 +1,191 @@ +SeaBIOS can read several configuration items at runtime. On coreboot +the configuration comes from files located in CBFS. When SeaBIOS runs +natively on QEMU the files are passed from QEMU via the fw_cfg +interface. + +This page documents the user visible configuration and control +features that SeaBIOS supports. + +LZMA compression +================ + +On coreboot, when scanning files in CBFS, any filename that ends with +a ".lzma" suffix will be treated as a raw file that is compressed with +the lzma compression algorithm. This works for option ROMs, +configuration files, floppy images, etc. . (This feature should not be +used with embedded payloads - to compress payloads, use the standard +section based compression algorithm that is built into the payload +specification.) + +For example, the file **pci1106,3344.rom.lzma** would be treated the +same as **pci1106,3344.rom**, but will be automatically uncompressed +when accessed. + +A file is typically compressed with the lzma compression command line +tool. For example: + +`lzma -zc /path/to/somefile.bin > somefile.bin.lzma` + +However, some recent versions of lzma no longer supply an uncompressed +file size in the lzma header. (They instead populate the field with +zero.) Unfortunately, SeaBIOS requires the uncompressed file size, so +it may be necessary to use a different version of the lzma tool. + +File aliases +============ + +It is possible to create the equivalent of "symbolic links" so that +one file's content appears under another name. To do this, create a +**links** file with one line per link and each line having the format +of "linkname" and "destname" separated by a space character. For +example, the **links** file may look like: + +``` +pci1234,1000.rom somerom.rom +pci1234,1001.rom somerom.rom +pci1234,1002.rom somerom.rom +``` + +The above example would cause SeaBIOS to treat "pci1234,1000.rom" or +"pci1234,1001.rom" as files with the same content as the file +"somerom.rom". + +Option ROMs +=========== + +SeaBIOS will scan all of the PCI devices in the target machine for +option ROMs on PCI devices. It recognizes option ROMs in files that +have the form **pciVVVV,DDDD.rom**. The VVVV,DDDD should correspond to +the PCI vendor and device id of a device in the machine. If a given +file is found then SeaBIOS will deploy the file instead of attempting +to extract an option ROM from the device. In addition to supplying +option ROMs for on-board devices that do not store their own ROMs, +this mechanism may be used to prevent a ROM on a specific device from +running. + +SeaBIOS always deploys the VGA rom associated with the active VGA +device before any other ROMs. + +In addition, SeaBIOS will also run any file in the directory +**vgaroms/** as a VGA option ROM not specific to a device and files in +**genroms/** as a generic option ROM not specific to a device. The +ROMS in **vgaroms/** are run immediately after running the option ROM +associated with the primary VGA device (if any were found), and the +**genroms/** ROMs are run after all other PCI ROMs are run. + +Bootsplash images +================= + +SeaBIOS can show a custom [JPEG](http://en.wikipedia.org/wiki/JPEG) +image or [BMP](http://en.wikipedia.org/wiki/BMP_file_format) image +during bootup. To enable this, add the JPEG file to flash with the +name **bootsplash.jpg** or BMP file as **bootsplash.bmp**. + +The size of the image determines the video mode to use for showing the +image. Make sure the dimensions of the image exactly correspond to an +available video mode (eg, 640x480, or 1024x768), otherwise it will not +be displayed. + +SeaBIOS will show the image during the wait for the boot menu (if the +boot menu has been disabled, users will not see the image). The image +should probably have "Press ESC for boot menu" embedded in it so users +know they can enter the normal SeaBIOS boot menu. By default, the boot +menu prompt (and thus graphical image) is shown for 2.5 seconds. This +can be customized via a [configuration +parameter](#Other_Configuration_items). + +The JPEG viewer in SeaBIOS uses a simplified decoding algorithm. It +supports most common JPEGs, but does not support all possible formats. +Please see the [trouble reporting section](Debugging) if a valid image +isn't displayed properly. + +Payloads +======== + +On coreboot, SeaBIOS will treat all files found in the **img/** +directory as a coreboot payload. Each payload file will be available +for boot, and one can select from the available payloads in the +bootmenu. SeaBIOS supports both uncompressed and lzma compressed +payloads. + +Floppy images +============= + +It is possible to embed an image of a floppy into a file. SeaBIOS can +then boot from and redirect floppy BIOS calls to the image. This is +mainly useful for legacy software (such as DOS utilities). To use this +feature, place a floppy image into the directory **floppyimg/**. + +Using LZMA file compression with the [.lzma file +suffix](#LZMA_compression) is a useful way to reduce the file +size. Several floppy formats are available: 360K, 1.2MB, 720K, 1.44MB, +2.88MB, 160K, 180K, 320K. + +The floppy image will appear as writable to the system, however all +writes are discarded on reboot. + +When using this system, SeaBIOS reserves high-memory to store the +floppy. The reserved memory is then no longer available for OS use, so +this feature should only be used when needed. + +Configuring boot order +====================== + +The **bootorder** file may be used to configure the boot up order. The +file should be ASCII text and contain one line per boot method. The +description of each boot method follows an [Open +Firmware](https://secure.wikimedia.org/wikipedia/en/wiki/Open_firmware) +device path format. SeaBIOS will attempt to boot from each item in the +file - first line of the file first. + +The easiest way to find the available boot methods is to look for +"Searching bootorder for" in the SeaBIOS debug output. For example, +one may see lines similar to: + +``` +Searching bootorder for: /pci@i0cf8/*@f/drive@1/disk@0 +Searching bootorder for: /pci@i0cf8/*@f,1/drive@2/disk@1 +Searching bootorder for: /pci@i0cf8/usb@10,4/*@2 +``` + +The above represents the patterns SeaBIOS will search for in the +bootorder file. However, it's safe to just copy and paste the pattern +into bootorder. For example, the file: + +``` +/pci@i0cf8/usb@10,4/*@2 +/pci@i0cf8/*@f/drive@1/disk@0 +``` + +will instruct SeaBIOS to attempt to boot from the given USB drive +first and then attempt the given ATA harddrive second. + +SeaBIOS also supports a special "HALT" directive. If a line that +contains "HALT" is found in the bootorder file then SeaBIOS will (by +default) only attempt to boot from devices explicitly listed above +HALT in the file. + +Other Configuration items +========================= + +There are several additional configuration options available in the +**etc/** directory. + +| Filename | Description +|---------------------|--------------------------------------------------- +| show-boot-menu | Controls the display of the boot menu. Set to 0 to disable the boot menu. +| boot-menu-message | Customize the text boot menu message. Normally, when in text mode SeaBIOS will report the string "\\nPress ESC for boot menu.\\n\\n". This field allows the string to be changed. (This is a string field, and is added as a file containing the raw string.) +| boot-menu-key | Controls which key activates the boot menu. The value stored is the DOS scan code (eg, 0x86 for F12, 0x01 for Esc). If this field is set, be sure to also customize the **boot-menu-message** field above. +| boot-menu-wait | Amount of time (in milliseconds) to wait at the boot menu prompt before selecting the default boot. +| boot-fail-wait | If no boot devices are found SeaBIOS will reboot after 60 seconds. Set this to the amount of time (in milliseconds) to customize the reboot delay or set to -1 to disable rebooting when no boot devices are found +| extra-pci-roots | If the target machine has multiple independent root buses set this to a positive value. The SeaBIOS PCI probe will then search for the given number of extra root buses. +| ps2-keyboard-spinup | Some laptops that emulate PS2 keyboards don't respond to keyboard commands immediately after powering on. One may specify the amount of time (in milliseconds) here to allow as additional time for the keyboard to become responsive. When this field is set, SeaBIOS will repeatedly attempt to detect the keyboard until the keyboard is found or the specified timeout is reached. +| optionroms-checksum | Option ROMs are required to have correct checksums. However, some option ROMs in the wild don't correctly follow the specifications and have bad checksums. Set this to a zero value to allow SeaBIOS to execute them anyways. +| pci-optionrom-exec | Controls option ROM execution for roms found on PCI devices (as opposed to roms found in CBFS/fw_cfg). Valid values are 0: Execute no ROMs, 1: Execute only VGA ROMs, 2: Execute all ROMs. The default is 2 (execute all ROMs). +| s3-resume-vga-init | Set this to a non-zero value to instruct SeaBIOS to run the vga rom on an S3 resume. +| screen-and-debug | Set this to a zero value to instruct SeaBIOS to not write characters it sends to the screen to the debug ports. This can be useful when using sgabios. +| advertise-serial-debug-port | If using a serial debug port, one can set this file to a zero value to prevent SeaBIOS from listing that serial port as available for operating system use. This can be useful when running old DOS programs that are known to reset the baud rate of all advertised serial ports. +| floppy0 | Set this to the type of the first floppy drive in the system (only type 4 for 3.5 inch drives is supported). +| floppy1 | The type of the second floppy drive in the system. See the description of **floppy0** for more info. +| threads | By default, SeaBIOS will parallelize hardware initialization during bootup to reduce boot time. Multiple hardware devices can be initialized in parallel between vga initialization and option rom initialization. One can set this file to a value of zero to force hardware initialization to run serially. Alternatively, one can set this file to 2 to enable early hardware initialization that runs in parallel with vga, option rom initialization, and the boot menu. +| sdcard* | One may create one or more files with an "sdcard" prefix (eg, "etc/sdcard0") with the physical memory address of an SDHCI controller (one memory address per file). This may be useful for SDHCI controllers that do not appear as PCI devices, but are mapped to a consistent memory address. diff --git a/qemu/roms/seabios/docs/SeaBIOS.md b/qemu/roms/seabios/docs/SeaBIOS.md index 831bfced9..e24913a64 100644 --- a/qemu/roms/seabios/docs/SeaBIOS.md +++ b/qemu/roms/seabios/docs/SeaBIOS.md @@ -10,6 +10,8 @@ information on using SeaBIOS in coreboot. Please see the [releases](Releases) page for information on recent releases. See the [download](Download) page to obtain SeaBIOS. +[SeaVGABIOS](SeaVGABIOS) is a sub-project of SeaBIOS. + Please join the [mailing list](Mailinglist) to contribute to SeaBIOS. Information on the internals of SeaBIOS is available on the [Developer Documentation](Developer Documentation) page. diff --git a/qemu/roms/seabios/docs/SeaVGABIOS.md b/qemu/roms/seabios/docs/SeaVGABIOS.md new file mode 100644 index 000000000..7ec27804d --- /dev/null +++ b/qemu/roms/seabios/docs/SeaVGABIOS.md @@ -0,0 +1,39 @@ +SeaVGABIOS is a sub-project of the SeaBIOS project - it is an open +source implementation of a 16bit X86 +[VGA BIOS](http://en.wikipedia.org/wiki/Video_BIOS). SeaVGABIOS is the +default VGA BIOS on [QEMU](http://www.qemu.org/). SeaVGABIOS can also +run natively on some X86 VGA hardware with +[coreboot](http://www.coreboot.org/). + +Building SeaVGABIOS +=================== + +To build SeaVGABIOS, obtain the [code](Download), run `make +menuconfig` and select the type of VGA BIOS to build in the "VGA ROM" +menu. Once selected, run `make` and the final VGA BIOS binary will be +located in "out/vgabios.bin". + +The choice of available VGA BIOSes within "make menuconfig" is +dependent on whether CONFIG_QEMU, CONFIG_COREBOOT, or CONFIG_CSM is +selected. Also, the debug options under the "Debugging" menu apply to +SeaVGABIOS. All other options found in "make menuconfig" apply only to +SeaBIOS and will not impact the SeaVGABIOS build. + +If SeaVGABIOS is needed for multiple different devices (eg, QEMU's +cirrus emulation and QEMU's "dispi" emulation), then one must compile +SeaVGABIOS multiple times with the appropriate config for each build. + +SeaVGABIOS code +=============== + +The source code for SeaVGABIOS is located in the SeaBIOS +[git repository](Download). The main VGA BIOS code is located in the +"vgasrc/" directory. The VGA BIOS code is always compiled in 16bit +mode. + +The SeaVGABIOS builds to a separate binary from the main SeaBIOS +binary, and much of the VGA BIOS code is separate from the main BIOS +code. However, much of the SeaBIOS +[developer documentation](Developer_Documentation) applies to +SeaVGABIOS. To contribute, please join the +[SeaBIOS mailing list](Mailinglist). diff --git a/qemu/roms/seabios/scripts/buildversion.py b/qemu/roms/seabios/scripts/buildversion.py new file mode 100755 index 000000000..46928984e --- /dev/null +++ b/qemu/roms/seabios/scripts/buildversion.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python +# Generate version information for a program +# +# Copyright (C) 2015 Kevin O'Connor +# +# This file may be distributed under the terms of the GNU GPLv3 license. +import sys, os, subprocess, shlex, time, socket, optparse, logging, traceback + +VERSION_FORMAT = """ +/* DO NOT EDIT! This is an autogenerated file. See scripts/buildversion.py. */ +#define BUILD_VERSION "%s" +#define BUILD_TOOLS "%s" +""" + +# Run program and return the specified output +def check_output(prog): + logging.debug("Running %s" % (repr(prog),)) + try: + process = subprocess.Popen(shlex.split(prog), stdout=subprocess.PIPE) + output = process.communicate()[0] + retcode = process.poll() + except OSError: + logging.debug("Exception on run: %s" % (traceback.format_exc(),)) + return "" + logging.debug("Got (code=%s): %s" % (retcode, repr(output))) + if retcode: + return "" + try: + return output.decode() + except UnicodeError: + logging.debug("Exception on decode: %s" % (traceback.format_exc(),)) + return "" + +# Obtain version info from "git" program +def git_version(): + if not os.path.exists('.git'): + logging.debug("No '.git' file/directory found") + return "" + ver = check_output("git describe --tags --long --dirty").strip() + logging.debug("Got git version: %s" % (repr(ver),)) + return ver + +# Look for version in a ".version" file. Official release tarballs +# have this file (see scripts/tarball.sh). +def file_version(): + if not os.path.isfile('.version'): + logging.debug("No '.version' file found") + return "" + try: + f = open('.version', 'r') + ver = f.readline().strip() + f.close() + except OSError: + logging.debug("Exception on read: %s" % (traceback.format_exc(),)) + return "" + logging.debug("Got .version: %s" % (repr(ver),)) + return ver + +# Generate an output file with the version information +def write_version(outfile, version, toolstr): + logging.debug("Write file %s and %s" % (repr(version), repr(toolstr))) + sys.stdout.write("Version: %s\n" % (version,)) + f = open(outfile, 'w') + f.write(VERSION_FORMAT % (version, toolstr)) + f.close() + +# Run "tool --version" for each specified tool and extract versions +def tool_versions(tools): + tools = [t.strip() for t in tools.split(';')] + versions = ['', ''] + success = 0 + for tool in tools: + # Extract first line from "tool --version" output + verstr = check_output("%s --version" % (tool,)).split('\n')[0] + # Check if this tool looks like a binutils program + isbinutils = 0 + if verstr.startswith('GNU '): + isbinutils = 1 + verstr = verstr[4:] + # Extract version information and exclude program name + if ' ' not in verstr: + continue + prog, ver = verstr.split(' ', 1) + if not prog or not ver: + continue + # Check for any version conflicts + if versions[isbinutils] and versions[isbinutils] != ver: + logging.debug("Mixed version %s vs %s" % ( + repr(versions[isbinutils]), repr(ver))) + versions[isbinutils] = "mixed" + continue + versions[isbinutils] = ver + success += 1 + cleanbuild = versions[0] and versions[1] and success == len(tools) + return cleanbuild, "gcc: %s binutils: %s" % (versions[0], versions[1]) + +def main(): + usage = "%prog [options] " + opts = optparse.OptionParser(usage) + opts.add_option("-e", "--extra", dest="extra", default="", + help="extra version string to append to version") + opts.add_option("-t", "--tools", dest="tools", default="", + help="list of build programs to extract version from") + opts.add_option("-v", action="store_true", dest="verbose", + help="enable debug messages") + + options, args = opts.parse_args() + if len(args) != 1: + opts.error("Incorrect arguments") + outfile = args[0] + if options.verbose: + logging.basicConfig(level=logging.DEBUG) + + cleanbuild, toolstr = tool_versions(options.tools) + + ver = git_version() + cleanbuild = cleanbuild and 'dirty' not in ver + if not ver: + ver = file_version() + # We expect the "extra version" to contain information on the + # distributor and distribution package version (if + # applicable). It is a "clean" build if this is a build from + # an official release tarball and the above info is present. + cleanbuild = cleanbuild and ver and options.extra != "" + if not ver: + ver = "?" + if not cleanbuild: + btime = time.strftime("%Y%m%d_%H%M%S") + hostname = socket.gethostname() + ver = "%s-%s-%s" % (ver, btime, hostname) + write_version(outfile, ver + options.extra, toolstr) + +if __name__ == '__main__': + main() diff --git a/qemu/roms/seabios/scripts/buildversion.sh b/qemu/roms/seabios/scripts/buildversion.sh deleted file mode 100755 index 516aff5b2..000000000 --- a/qemu/roms/seabios/scripts/buildversion.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh -# Script to generate a C file with version information. -OUTFILE="$1" -VAR16MODE="$2" - -# Extract version info -if [ -z "$BUILD_VERSION" ]; then - if [ -d .git -o -f .git ]; then - VERSION="`git describe --tags --long --dirty`" - elif [ -f .version ]; then - VERSION="`cat .version`" - else - VERSION="?" - fi - VERSION="${VERSION}-`date +"%Y%m%d_%H%M%S"`-`hostname`" -else - VERSION="$BUILD_VERSION" -fi -echo "Version: ${VERSION}" - -# Build header file -if [ "$VAR16MODE" = "VAR16" ]; then - cat > ${OUTFILE} < ${OUTFILE} < finalsize: print("Error! ROM doesn't fit (%d > %d)" % (datasize, finalsize)) - print(" You have to either increate the size (CONFIG_ROM_SIZE)") + print(" You have to either increase the size (CONFIG_ROM_SIZE)") print(" or turn off some features (such as hardware support not") print(" needed) to make it fit. Trying a more recent gcc version") print(" might work too.") diff --git a/qemu/roms/seabios/scripts/checkstack.py b/qemu/roms/seabios/scripts/checkstack.py index b49b6c8cc..5d9b0bfaf 100755 --- a/qemu/roms/seabios/scripts/checkstack.py +++ b/qemu/roms/seabios/scripts/checkstack.py @@ -2,7 +2,7 @@ # Script that tries to find how much stack space each function in an # object is using. # -# Copyright (C) 2008 Kevin O'Connor +# Copyright (C) 2008-2015 Kevin O'Connor # # This file may be distributed under the terms of the GNU GPLv3 license. @@ -26,85 +26,84 @@ OUTPUTDESC = """ # insn_addr:called_function [u+c,t,usage_to_yield_point] """ +class function: + def __init__(self, funcaddr, funcname): + self.funcaddr = funcaddr + self.funcname = funcname + self.basic_stack_usage = 0 + self.max_stack_usage = None + self.yield_usage = -1 + self.max_yield_usage = None + self.total_calls = 0 + # called_funcs = [(insnaddr, calladdr, stackusage), ...] + self.called_funcs = [] + self.subfuncs = {} + # Update function info with a found "yield" point. + def noteYield(self, stackusage): + if self.yield_usage < stackusage: + self.yield_usage = stackusage + # Update function info with a found "call" point. + def noteCall(self, insnaddr, calladdr, stackusage): + if (calladdr, stackusage) in self.subfuncs: + # Already noted a nearly identical call - ignore this one. + return + self.called_funcs.append((insnaddr, calladdr, stackusage)) + self.subfuncs[(calladdr, stackusage)] = 1 + # Find out maximum stack usage for a function -def calcmaxstack(funcs, funcaddr): - info = funcs[funcaddr] - # Find max of all nested calls. - maxusage = info[1] - maxyieldusage = doesyield = 0 - if info[3] is not None: - maxyieldusage = info[3] - doesyield = 1 - info[2] = maxusage - info[4] = info[3] +def calcmaxstack(info, funcs): + if info.max_stack_usage is not None: + return + info.max_stack_usage = max_stack_usage = info.basic_stack_usage + info.max_yield_usage = max_yield_usage = info.yield_usage + total_calls = 0 seenbefore = {} - totcalls = 0 - for insnaddr, calladdr, usage in info[6]: + # Find max of all nested calls. + for insnaddr, calladdr, usage in info.called_funcs: callinfo = funcs.get(calladdr) if callinfo is None: continue - if callinfo[2] is None: - calcmaxstack(funcs, calladdr) - if callinfo[0] not in seenbefore: - seenbefore[callinfo[0]] = 1 - totcalls += 1 + callinfo[5] - funcnameroot = callinfo[0].split('.')[0] + calcmaxstack(callinfo, funcs) + if callinfo.funcname not in seenbefore: + seenbefore[callinfo.funcname] = 1 + total_calls += callinfo.total_calls + 1 + funcnameroot = callinfo.funcname.split('.')[0] if funcnameroot in IGNORE: # This called function is ignored - don't contribute it to # the max stack. continue + totusage = usage + callinfo.max_stack_usage + totyieldusage = usage + callinfo.max_yield_usage if funcnameroot in STACKHOP: - if usage > maxusage: - maxusage = usage - if callinfo[4] is not None: - doesyield = 1 - if usage > maxyieldusage: - maxyieldusage = usage - continue - totusage = usage + callinfo[2] - if totusage > maxusage: - maxusage = totusage - if callinfo[4] is not None: - doesyield = 1 - totyieldusage = usage + callinfo[4] - if totyieldusage > maxyieldusage: - maxyieldusage = totyieldusage - info[2] = maxusage - if doesyield: - info[4] = maxyieldusage - info[5] = totcalls + # Don't count children of this function + totusage = totyieldusage = usage + if totusage > max_stack_usage: + max_stack_usage = totusage + if callinfo.max_yield_usage >= 0 and totyieldusage > max_yield_usage: + max_yield_usage = totyieldusage + info.max_stack_usage = max_stack_usage + info.max_yield_usage = max_yield_usage + info.total_calls = total_calls # Try to arrange output so that functions that call each other are # near each other. def orderfuncs(funcaddrs, availfuncs): - l = [(availfuncs[funcaddr][5], availfuncs[funcaddr][0], funcaddr) + l = [(availfuncs[funcaddr].total_calls + , availfuncs[funcaddr].funcname, funcaddr) for funcaddr in funcaddrs if funcaddr in availfuncs] l.sort() l.reverse() out = [] while l: count, name, funcaddr = l.pop(0) - if funcaddr not in availfuncs: + info = availfuncs.get(funcaddr) + if info is None: continue - calladdrs = [calls[1] for calls in availfuncs[funcaddr][6]] + calladdrs = [calls[1] for calls in info.called_funcs] del availfuncs[funcaddr] - out = out + orderfuncs(calladdrs, availfuncs) + [funcaddr] + out = out + orderfuncs(calladdrs, availfuncs) + [info] return out -# Update function info with a found "yield" point. -def noteYield(info, stackusage): - prevyield = info[3] - if prevyield is None or prevyield < stackusage: - info[3] = stackusage - -# Update function info with a found "call" point. -def noteCall(info, subfuncs, insnaddr, calladdr, stackusage): - if (calladdr, stackusage) in subfuncs: - # Already noted a nearly identical call - ignore this one. - return - info[6].append((insnaddr, calladdr, stackusage)) - subfuncs[(calladdr, stackusage)] = 1 - hex_s = r'[0-9a-f]+' re_func = re.compile(r'^(?P' + hex_s + r') <(?P.*)>:$') re_asm = re.compile( @@ -114,11 +113,12 @@ re_asm = re.compile( re_usestack = re.compile( r'^(push[f]?[lw])|(sub.* [$](?P0x' + hex_s + r'),%esp)$') -def calc(): - # funcs[funcaddr] = [funcname, basicstackusage, maxstackusage - # , yieldusage, maxyieldusage, totalcalls - # , [(insnaddr, calladdr, stackusage), ...]] - funcs = {-1: ['', 0, 0, None, None, 0, []]} +def main(): + unknownfunc = function(None, "") + indirectfunc = function(-1, '') + unknownfunc.max_stack_usage = indirectfunc.max_stack_usage = 0 + unknownfunc.max_yield_usage = indirectfunc.max_yield_usage = -1 + funcs = {-1: indirectfunc} cur = None atstart = 0 stackusage = 0 @@ -129,99 +129,93 @@ def calc(): if m is not None: # Found function funcaddr = int(m.group('funcaddr'), 16) - funcs[funcaddr] = cur = [m.group('func'), 0, None, None, None, 0, []] + funcs[funcaddr] = cur = function(funcaddr, m.group('func')) stackusage = 0 atstart = 1 - subfuncs = {} continue m = re_asm.match(line) - if m is not None: - insn = m.group('insn') - - im = re_usestack.match(insn) - if im is not None: - if insn.startswith('pushl') or insn.startswith('pushfl'): - stackusage += 4 - continue - elif insn.startswith('pushw') or insn.startswith('pushfw'): - stackusage += 2 - continue - stackusage += int(im.group('num'), 16) - - if atstart: - if '%esp' in insn or insn.startswith('leal'): - # Still part of initial header - continue - cur[1] = stackusage - atstart = 0 - - insnaddr = m.group('insnaddr') - calladdr = m.group('calladdr') - if calladdr is None: - if insn.startswith('lcallw'): - noteCall(cur, subfuncs, insnaddr, -1, stackusage + 4) - noteYield(cur, stackusage + 4) - elif insn.startswith('int'): - noteCall(cur, subfuncs, insnaddr, -1, stackusage + 6) - noteYield(cur, stackusage + 6) - elif insn.startswith('sti'): - noteYield(cur, stackusage) - else: - # misc instruction - continue + if m is None: + #print("other", repr(line)) + continue + insn = m.group('insn') + + im = re_usestack.match(insn) + if im is not None: + if insn.startswith('pushl') or insn.startswith('pushfl'): + stackusage += 4 + continue + elif insn.startswith('pushw') or insn.startswith('pushfw'): + stackusage += 2 + continue + stackusage += int(im.group('num'), 16) + + if atstart: + if '%esp' in insn or insn.startswith('leal'): + # Still part of initial header + continue + cur.basic_stack_usage = stackusage + atstart = 0 + + insnaddr = m.group('insnaddr') + calladdr = m.group('calladdr') + if calladdr is None: + if insn.startswith('lcallw'): + cur.noteCall(insnaddr, -1, stackusage + 4) + cur.noteYield(stackusage + 4) + elif insn.startswith('int'): + cur.noteCall(insnaddr, -1, stackusage + 6) + cur.noteYield(stackusage + 6) + elif insn.startswith('sti'): + cur.noteYield(stackusage) + else: + # misc instruction + continue + else: + # Jump or call insn + calladdr = int(calladdr, 16) + ref = m.group('ref') + if '+' in ref: + # Inter-function jump. + pass + elif insn.startswith('j'): + # Tail call + cur.noteCall(insnaddr, calladdr, 0) + elif insn.startswith('calll'): + cur.noteCall(insnaddr, calladdr, stackusage + 4) + elif insn.startswith('callw'): + cur.noteCall(insnaddr, calladdr, stackusage + 2) else: - # Jump or call insn - calladdr = int(calladdr, 16) - ref = m.group('ref') - if '+' in ref: - # Inter-function jump. - pass - elif insn.startswith('j'): - # Tail call - noteCall(cur, subfuncs, insnaddr, calladdr, 0) - elif insn.startswith('calll'): - noteCall(cur, subfuncs, insnaddr, calladdr, stackusage + 4) - elif insn.startswith('callw'): - noteCall(cur, subfuncs, insnaddr, calladdr, stackusage + 2) - else: - print("unknown call", ref) - noteCall(cur, subfuncs, insnaddr, calladdr, stackusage) - # Reset stack usage to preamble usage - stackusage = cur[1] - - #print("other", repr(line)) + print("unknown call", ref) + cur.noteCall(insnaddr, calladdr, stackusage) + # Reset stack usage to preamble usage + stackusage = cur.basic_stack_usage # Calculate maxstackusage - for funcaddr, info in funcs.items(): - if info[2] is not None: - continue - calcmaxstack(funcs, funcaddr) + for info in funcs.values(): + calcmaxstack(info, funcs) # Sort functions for output - funcaddrs = orderfuncs(funcs.keys(), funcs.copy()) + funcinfos = orderfuncs(funcs.keys(), funcs.copy()) # Show all functions print(OUTPUTDESC) - for funcaddr in funcaddrs: - name, basicusage, maxusage, yieldusage, maxyieldusage, count, calls = \ - funcs[funcaddr] - if maxusage == 0 and maxyieldusage is None: + for info in funcinfos: + if info.max_stack_usage == 0 and info.max_yield_usage < 0: continue yieldstr = "" - if maxyieldusage is not None: - yieldstr = ",%d" % maxyieldusage - print("\n%s[%d,%d%s]:" % (name, basicusage, maxusage, yieldstr)) - for insnaddr, calladdr, stackusage in calls: - callinfo = funcs.get(calladdr, ("", 0, 0, 0, None)) + if info.max_yield_usage >= 0: + yieldstr = ",%d" % info.max_yield_usage + print("\n%s[%d,%d%s]:" % (info.funcname, info.basic_stack_usage + , info.max_stack_usage, yieldstr)) + for insnaddr, calladdr, stackusage in info.called_funcs: + callinfo = funcs.get(calladdr, unknownfunc) yieldstr = "" - if callinfo[4] is not None: - yieldstr = ",%d" % (stackusage + callinfo[4]) + if callinfo.max_yield_usage >= 0: + yieldstr = ",%d" % (stackusage + callinfo.max_yield_usage) print(" %04s:%-40s [%d+%d,%d%s]" % ( - insnaddr, callinfo[0], stackusage, callinfo[1] - , stackusage+callinfo[2], yieldstr)) - -def main(): - calc() + insnaddr, callinfo.funcname, stackusage + , callinfo.basic_stack_usage + , stackusage+callinfo.max_stack_usage, yieldstr)) if __name__ == '__main__': main() diff --git a/qemu/roms/seabios/scripts/kconfig/lxdialog/util.c b/qemu/roms/seabios/scripts/kconfig/lxdialog/util.c index f7abdeb92..2a0d182e8 100644 --- a/qemu/roms/seabios/scripts/kconfig/lxdialog/util.c +++ b/qemu/roms/seabios/scripts/kconfig/lxdialog/util.c @@ -376,7 +376,7 @@ void print_title(WINDOW *dialog, const char *title, int width) /* * Print a string of text in a window, automatically wrap around to the * next line if the string is too long to fit on one line. Newline - * characters '\n' are propperly processed. We start on a new line + * characters '\n' are properly processed. We start on a new line * if there is no room for at least 4 nonblanks following a double-space. */ void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x) diff --git a/qemu/roms/seabios/scripts/layoutrom.py b/qemu/roms/seabios/scripts/layoutrom.py index dd770fe49..b976fb056 100755 --- a/qemu/roms/seabios/scripts/layoutrom.py +++ b/qemu/roms/seabios/scripts/layoutrom.py @@ -161,6 +161,7 @@ def getSectionsPrefix(sections, prefix): # The sections (and associated information) to be placed in output rom class LayoutInfo: sections = None + config = None genreloc = None sec32init_start = sec32init_end = sec32init_align = None sec32low_start = sec32low_end = None @@ -172,6 +173,7 @@ class LayoutInfo: # Determine final memory addresses for sections def doLayout(sections, config, genreloc): li = LayoutInfo() + li.config = config li.sections = sections li.genreloc = genreloc # Determine 16bit positions @@ -399,6 +401,10 @@ def writeLinkerScripts(li, out16, out32seg, out32flat): filesections32flat = getSectionsFileid(li.sections, '32flat') out = outXRefs([], exportsyms=li.varlowsyms , forcedelta=li.final_sec32low_start-li.sec32low_start) + multiboot_header = "" + if li.config.get('CONFIG_MULTIBOOT'): + multiboot_header = "LONG(0x1BADB002) LONG(0) LONG(-0x1BADB002)" + sec32all_start -= 3 * 4 out += outXRefs(filesections32flat, exportsyms=[li.entrysym]) + """ _reloc_min_align = 0x%x ; zonefseg_start = 0x%x ; @@ -414,6 +420,7 @@ def writeLinkerScripts(li, out16, out32seg, out32flat): code32flat_start = 0x%x ; .text code32flat_start : { %s +%s %s code32flat_end = ABSOLUTE(.) ; } :text @@ -428,6 +435,7 @@ def writeLinkerScripts(li, out16, out32seg, out32flat): li.sec32init_start, li.sec32init_end, sec32all_start, + multiboot_header, relocstr, outRelSections(li.sections, 'code32flat_start')) out = COMMONHEADER + out + COMMONTRAILER + """ diff --git a/qemu/roms/seabios/src/Kconfig b/qemu/roms/seabios/src/Kconfig index 45ca59cf3..95bf087b8 100644 --- a/qemu/roms/seabios/src/Kconfig +++ b/qemu/roms/seabios/src/Kconfig @@ -20,7 +20,7 @@ choice Configure for an emulated machine (QEMU, Xen, KVM, or Bochs). config CSM - bool "Build as Compatibilty Support Module for EFI BIOS" + bool "Build as Compatibility Support Module for EFI BIOS" help Configure to be used by EFI firmware as Compatibility Support module (CSM) to provide legacy BIOS services. @@ -96,12 +96,13 @@ endchoice the CBFS filesystem is at a non-standard location (eg, 0xffe00000 if CBFS ends 2Meg below the end of flash). - config FLASH_FLOPPY - depends on COREBOOT_FLASH - bool "Floppy images in CBFS" + config MULTIBOOT + depends on COREBOOT + bool "multiboot support" default y help - Support floppy images in coreboot flash. + Add multiboot header in bios.bin.raw and accept files supplied + as multiboot modules. config ENTRY_EXTRASTACK bool "Use internal stack for 16bit interrupt entry points" default y @@ -160,7 +161,7 @@ menu "Hardware support" help Support for AHCI disk code. config SDCARD - depends on DRIVES && QEMU_HARDWARE + depends on DRIVES bool "SD controllers" default y help @@ -208,11 +209,18 @@ menu "Hardware support" help Support boot from LSI MegaRAID SAS scsi storage. config FLOPPY - depends on DRIVES + depends on DRIVES && HARDWARE_IRQ bool "Floppy controller" default y help Support floppy drive access. + config FLASH_FLOPPY + depends on DRIVES + bool "Floppy images from CBFS or fw_cfg" + default y + help + Support floppy images stored in coreboot flash or from + QEMU fw_cfg. config PS2PORT depends on KEYBOARD || MOUSE @@ -291,6 +299,26 @@ menu "Hardware support" default y help Support parallel ports. This also enables int 17 parallel port calls. + config RTC_TIMER + bool "Real Time Clock (RTC) scheduling" + depends on HARDWARE_IRQ + default y + help + Support MC146818 Real Time Clock chip timer + interrupts. This also enables int 1583 and int 1586 calls. + + Disabling this support does not disable access to the RTC + cmos registers. + + config HARDWARE_IRQ + bool "Hardware interrupts" + default y + help + Program and support hardware interrupts using the i8259 + programmable interrupt controller (PIC). This option must + be enabled in order to support most boot loaders. Only + disable this option if running on peculiar hardware known + not to support irq routing. config USE_SMM depends on QEMU @@ -309,10 +337,16 @@ menu "Hardware support" help Initialize the Memory Type Range Registers (on emulators). config PMTIMER - bool "Use ACPI timer" + bool "Support ACPI timer" default y help - Use the ACPI timer instead of the TSC for timekeeping (on qemu). + Detect and use the ACPI timer for timekeeping. + config TSC_TIMER + bool "Support CPU timestamp counter as timer" + default y + help + Support for using the CPU timestamp counter as an internal + timing source. endmenu menu "BIOS interfaces" @@ -421,6 +455,13 @@ menu "BIOS interfaces" modified by programs. However, some old DOS high memory managers may require the UMB region to be read-only. + config TCGBIOS + depends on S3_RESUME + bool "TPM support and TCG BIOS extensions" + default y + help + Provide TPM support along with TCG BIOS extensions + endmenu menu "BIOS Tables" diff --git a/qemu/roms/seabios/src/biosvar.h b/qemu/roms/seabios/src/biosvar.h index 58bcbcedb..f61fb6a50 100644 --- a/qemu/roms/seabios/src/biosvar.h +++ b/qemu/roms/seabios/src/biosvar.h @@ -8,11 +8,12 @@ #include "config.h" // SEG_BDA #include "farptr.h" // GET_FARVAR +#include "memmap.h" // SYMBOL #include "std/bda.h" // struct bios_data_area_s /**************************************************************** - * Interupt vector table + * Interrupt vector table ****************************************************************/ #define GET_IVT(vector) \ @@ -112,13 +113,12 @@ static inline u16 get_global_seg(void) { * "Low" memory variables ****************************************************************/ -extern u8 _zonelow_seg, zonelow_base[]; -#define SEG_LOW ((u32)&_zonelow_seg) +#define SEG_LOW SYMBOL(_zonelow_seg) #if MODESEGMENT #define GET_LOW(var) GET_FARVAR(SEG_LOW, (var)) #define SET_LOW(var, val) SET_FARVAR(SEG_LOW, (var), (val)) -#define LOWFLAT2LOW(var) ((typeof(var))((void*)(var) - (u32)zonelow_base)) +#define LOWFLAT2LOW(var) ((typeof(var))((void*)(var) - SYMBOL(zonelow_base))) #else #define GET_LOW(var) (var) #define SET_LOW(var, val) do { (var) = (val); } while (0) diff --git a/qemu/roms/seabios/src/block.c b/qemu/roms/seabios/src/block.c index 3f7ecb1d7..1762e2a33 100644 --- a/qemu/roms/seabios/src/block.c +++ b/qemu/roms/seabios/src/block.c @@ -10,9 +10,16 @@ #include "hw/ata.h" // process_ata_op #include "hw/ahci.h" // process_ahci_op #include "hw/blockcmd.h" // cdb_* +#include "hw/esp-scsi.h" // esp_scsi_process_op +#include "hw/lsi-scsi.h" // lsi_scsi_process_op +#include "hw/megasas.h" // megasas_process_op #include "hw/pci.h" // pci_bdf_to_bus +#include "hw/pvscsi.h" // pvscsi_process_op #include "hw/rtc.h" // rtc_read +#include "hw/usb-msc.h" // usb_process_op +#include "hw/usb-uas.h" // uas_process_op #include "hw/virtio-blk.h" // process_virtio_blk_op +#include "hw/virtio-scsi.h" // virtio_scsi_process_op #include "malloc.h" // malloc_low #include "output.h" // dprintf #include "stacks.h" // stack_hop @@ -67,10 +74,8 @@ get_translation(struct drive_s *drive) u8 type = drive->type; if (CONFIG_QEMU && type == DTYPE_ATA) { // Emulators pass in the translation info via nvram. - u8 ataid = drive->cntl_id; - u8 channel = ataid / 2; - u8 translation = rtc_read(CMOS_BIOS_DISKTRANSFLAG + channel/2); - translation >>= 2 * (ataid % 4); + u8 translation = rtc_read(CMOS_BIOS_DISKTRANSFLAG + drive->cntl_id/4); + translation >>= 2 * (drive->cntl_id % 4); translation &= 0x03; return translation; } @@ -282,11 +287,21 @@ map_floppy_drive(struct drive_s *drive) * Extended Disk Drive (EDD) get drive parameters ****************************************************************/ +// flags for bus_iface field in fill_generic_edd() +#define EDD_ISA 0x01 +#define EDD_PCI 0x02 +#define EDD_BUS_MASK 0x0f +#define EDD_ATA 0x10 +#define EDD_SCSI 0x20 +#define EDD_IFACE_MASK 0xf0 + +// Fill in EDD info static int -fill_generic_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf - , u32 dpte_so, char *iface_type - , int bdf, u8 channel, u16 iobase, u64 device_path) +fill_generic_edd(struct segoff_s edd, struct drive_s *drive_gf + , u32 dpte_so, u8 bus_iface, u32 iface_path, u32 device_path) { + u16 seg = edd.seg; + struct int13dpt_s *param_far = (void*)(edd.offset+0); u16 size = GET_FARVAR(seg, param_far->size); u16 t13 = size == 74; @@ -335,7 +350,7 @@ fill_generic_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf SET_FARVAR(seg, param_far->size, 30); SET_FARVAR(seg, param_far->dpte.segoff, dpte_so); - if (size < 66 || !iface_type) + if (size < 66 || !bus_iface) return DISK_RET_SUCCESS; // EDD 3.x @@ -344,32 +359,22 @@ fill_generic_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf SET_FARVAR(seg, param_far->reserved1, 0); SET_FARVAR(seg, param_far->reserved2, 0); - int i; - for (i=0; iiface_type); i++) - SET_FARVAR(seg, param_far->iface_type[i], GET_GLOBAL(iface_type[i])); - - if (bdf != -1) { - SET_FARVAR(seg, param_far->host_bus[0], 'P'); - SET_FARVAR(seg, param_far->host_bus[1], 'C'); - SET_FARVAR(seg, param_far->host_bus[2], 'I'); - SET_FARVAR(seg, param_far->host_bus[3], ' '); - - u32 path = (pci_bdf_to_bus(bdf) | (pci_bdf_to_dev(bdf) << 8) - | (pci_bdf_to_fn(bdf) << 16)); - if (t13) - path |= channel << 24; - - SET_FARVAR(seg, param_far->iface_path, path); - } else { - // ISA - SET_FARVAR(seg, param_far->host_bus[0], 'I'); - SET_FARVAR(seg, param_far->host_bus[1], 'S'); - SET_FARVAR(seg, param_far->host_bus[2], 'A'); - SET_FARVAR(seg, param_far->host_bus[3], ' '); - - SET_FARVAR(seg, param_far->iface_path, iobase); + const char *host_bus = "ISA "; + if ((bus_iface & EDD_BUS_MASK) == EDD_PCI) { + host_bus = "PCI "; + if (!t13) + // Phoenix v3 spec (pre t13) did not define the PCI channel field + iface_path &= 0x00ffffff; } - + memcpy_far(seg, param_far->host_bus, SEG_BIOS, host_bus + , sizeof(param_far->host_bus)); + SET_FARVAR(seg, param_far->iface_path, iface_path); + + const char *iface_type = "ATA "; + if ((bus_iface & EDD_IFACE_MASK) == EDD_SCSI) + iface_type = "SCSI "; + memcpy_far(seg, param_far->iface_type, SEG_BIOS, iface_type + , sizeof(param_far->iface_type)); if (t13) { SET_FARVAR(seg, param_far->t13.device_path[0], device_path); SET_FARVAR(seg, param_far->t13.device_path[1], 0); @@ -386,10 +391,19 @@ fill_generic_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf return DISK_RET_SUCCESS; } +// Build an EDD "iface_path" field for a PCI device +static u32 +edd_pci_path(u16 bdf, u8 channel) +{ + return (pci_bdf_to_bus(bdf) | (pci_bdf_to_dev(bdf) << 8) + | (pci_bdf_to_fn(bdf) << 16) | ((u32)channel << 24)); +} + struct dpte_s DefaultDPTE VARLOW; +// EDD info for ATA and ATAPI drives static int -fill_ata_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf) +fill_ata_edd(struct segoff_s edd, struct drive_s *drive_gf) { if (!CONFIG_ATA) return DISK_RET_EPARAM; @@ -440,109 +454,141 @@ fill_ata_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf) u8 sum = checksum_far(SEG_LOW, &DefaultDPTE, 15); SET_LOW(DefaultDPTE.checksum, -sum); + u32 bustype = EDD_ISA, ifpath = iobase1; + if (bdf >= 0) { + bustype = EDD_PCI; + ifpath = edd_pci_path(bdf, channel); + } return fill_generic_edd( - seg, param_far, drive_gf, SEGOFF(SEG_LOW, (u32)&DefaultDPTE).segoff - , "ATA ", bdf, channel, iobase1, slave); + edd, drive_gf, SEGOFF(SEG_LOW, (u32)&DefaultDPTE).segoff + , bustype | EDD_ATA, ifpath, slave); } +// Fill Extended Disk Drive (EDD) "Get drive parameters" info for a drive int noinline -fill_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf) +fill_edd(struct segoff_s edd, struct drive_s *drive_gf) { switch (GET_GLOBALFLAT(drive_gf->type)) { case DTYPE_ATA: case DTYPE_ATA_ATAPI: - return fill_ata_edd(seg, param_far, drive_gf); + return fill_ata_edd(edd, drive_gf); case DTYPE_VIRTIO_BLK: case DTYPE_VIRTIO_SCSI: return fill_generic_edd( - seg, param_far, drive_gf, 0xffffffff - , "SCSI ", GET_GLOBALFLAT(drive_gf->cntl_id), 0, 0, 0); + edd, drive_gf, 0xffffffff, EDD_PCI | EDD_SCSI + , edd_pci_path(GET_GLOBALFLAT(drive_gf->cntl_id), 0), 0); default: - return fill_generic_edd(seg, param_far, drive_gf, 0, NULL, 0, 0, 0, 0); + return fill_generic_edd(edd, drive_gf, 0, 0, 0, 0); } } /**************************************************************** - * 16bit calling interface + * Disk driver dispatch ****************************************************************/ -int VISIBLE32FLAT -process_atapi_op(struct disk_op_s *op) +// Fallback handler for command requests not implemented by drivers +int +default_process_op(struct disk_op_s *op) { switch (op->command) { - case CMD_WRITE: case CMD_FORMAT: - return DISK_RET_EWRITEPROTECT; + case CMD_RESET: + case CMD_ISREADY: + case CMD_VERIFY: + case CMD_SEEK: + // Return success if the driver doesn't implement these commands + return DISK_RET_SUCCESS; default: - return scsi_process_op(op); + return DISK_RET_EPARAM; } } -// Execute a disk_op request. -int -process_op(struct disk_op_s *op) +// Command dispatch for disk drivers that run in both 16bit and 32bit mode +static int +process_op_both(struct disk_op_s *op) { - ASSERT16(); - int ret, origcount = op->count; - if (origcount * GET_GLOBALFLAT(op->drive_gf->blksize) > 64*1024) { - op->count = 0; - return DISK_RET_EBOUNDARY; - } - u8 type = GET_GLOBALFLAT(op->drive_gf->type); - switch (type) { - case DTYPE_FLOPPY: - ret = process_floppy_op(op); - break; - case DTYPE_ATA: - ret = process_ata_op(op); - break; - case DTYPE_RAMDISK: - ret = process_ramdisk_op(op); - break; - case DTYPE_CDEMU: - ret = process_cdemu_op(op); - break; - case DTYPE_VIRTIO_BLK: - ret = process_virtio_blk_op(op); - break; - case DTYPE_AHCI: ; - extern void _cfunc32flat_process_ahci_op(void); - ret = call32(_cfunc32flat_process_ahci_op - , (u32)MAKE_FLATPTR(GET_SEG(SS), op), DISK_RET_EPARAM); - break; + switch (GET_GLOBALFLAT(op->drive_gf->type)) { case DTYPE_ATA_ATAPI: - ret = process_atapi_op(op); - break; - case DTYPE_AHCI_ATAPI: ; - extern void _cfunc32flat_process_atapi_op(void); - ret = call32(_cfunc32flat_process_atapi_op - , (u32)MAKE_FLATPTR(GET_SEG(SS), op), DISK_RET_EPARAM); - break; - case DTYPE_SDCARD: ; - extern void _cfunc32flat_process_sdcard_op(void); - ret = call32(_cfunc32flat_process_sdcard_op - , (u32)MAKE_FLATPTR(GET_SEG(SS), op), DISK_RET_EPARAM); - break; + return ata_atapi_process_op(op); case DTYPE_USB: + return usb_process_op(op); case DTYPE_UAS: - case DTYPE_VIRTIO_SCSI: + return uas_process_op(op); case DTYPE_LSI_SCSI: + return lsi_scsi_process_op(op); case DTYPE_ESP_SCSI: + return esp_scsi_process_op(op); case DTYPE_MEGASAS: - ret = scsi_process_op(op); - break; + return megasas_process_op(op); + default: + if (!MODESEGMENT) + return DISK_RET_EPARAM; + // In 16bit mode and driver not found - try in 32bit mode + return call32(process_op_32, MAKE_FLATPTR(GET_SEG(SS), op) + , DISK_RET_EPARAM); + } +} + +// Command dispatch for disk drivers that only run in 32bit mode +int VISIBLE32FLAT +process_op_32(struct disk_op_s *op) +{ + ASSERT32FLAT(); + switch (op->drive_gf->type) { + case DTYPE_VIRTIO_BLK: + return virtio_blk_process_op(op); + case DTYPE_AHCI: + return ahci_process_op(op); + case DTYPE_AHCI_ATAPI: + return ahci_atapi_process_op(op); + case DTYPE_SDCARD: + return sdcard_process_op(op); case DTYPE_USB_32: + return usb_process_op(op); case DTYPE_UAS_32: - case DTYPE_PVSCSI: ; - extern void _cfunc32flat_scsi_process_op(void); - ret = call32(_cfunc32flat_scsi_process_op - , (u32)MAKE_FLATPTR(GET_SEG(SS), op), DISK_RET_EPARAM); - break; + return uas_process_op(op); + case DTYPE_VIRTIO_SCSI: + return virtio_scsi_process_op(op); + case DTYPE_PVSCSI: + return pvscsi_process_op(op); default: - ret = DISK_RET_EPARAM; - break; + return process_op_both(op); } +} + +// Command dispatch for disk drivers that only run in 16bit mode +static int +process_op_16(struct disk_op_s *op) +{ + ASSERT16(); + switch (GET_GLOBALFLAT(op->drive_gf->type)) { + case DTYPE_FLOPPY: + return floppy_process_op(op); + case DTYPE_ATA: + return ata_process_op(op); + case DTYPE_RAMDISK: + return ramdisk_process_op(op); + case DTYPE_CDEMU: + return cdemu_process_op(op); + default: + return process_op_both(op); + } +} + +// Execute a disk_op_s request. +int +process_op(struct disk_op_s *op) +{ + int ret, origcount = op->count; + if (origcount * GET_GLOBALFLAT(op->drive_gf->blksize) > 64*1024) { + op->count = 0; + return DISK_RET_EBOUNDARY; + } + if (MODESEGMENT) + ret = process_op_16(op); + else + ret = process_op_32(op); if (ret && op->count == origcount) // If the count hasn't changed on error, assume no data transferred. op->count = 0; @@ -578,5 +624,5 @@ send_disk_op(struct disk_op_s *op) if (! CONFIG_DRIVES) return -1; - return stack_hop((u32)op, GET_SEG(SS), __send_disk_op); + return stack_hop(__send_disk_op, op, GET_SEG(SS)); } diff --git a/qemu/roms/seabios/src/block.h b/qemu/roms/seabios/src/block.h index 8182288d4..2ff359fb2 100644 --- a/qemu/roms/seabios/src/block.h +++ b/qemu/roms/seabios/src/block.h @@ -9,11 +9,19 @@ ****************************************************************/ struct disk_op_s { - u64 lba; void *buf_fl; struct drive_s *drive_gf; - u16 count; u8 command; + u16 count; + union { + // Commands: READ, WRITE, VERIFY, SEEK, FORMAT + u64 lba; + // Commands: SCSI + struct { + u16 blocksize; + void *cdbcmd; + }; + }; }; #define CMD_RESET 0x00 @@ -23,6 +31,7 @@ struct disk_op_s { #define CMD_FORMAT 0x05 #define CMD_SEEK 0x07 #define CMD_ISREADY 0x10 +#define CMD_SCSI 0x20 /**************************************************************** @@ -101,7 +110,8 @@ void map_floppy_drive(struct drive_s *drive); void map_hd_drive(struct drive_s *drive); void map_cd_drive(struct drive_s *drive); struct int13dpt_s; -int fill_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf); +int fill_edd(struct segoff_s edd, struct drive_s *drive_gf); +int default_process_op(struct disk_op_s *op); int process_op(struct disk_op_s *op); int send_disk_op(struct disk_op_s *op); int create_bounce_buf(void); diff --git a/qemu/roms/seabios/src/bmp.c b/qemu/roms/seabios/src/bmp.c index d8e76b789..96a2b3f22 100644 --- a/qemu/roms/seabios/src/bmp.c +++ b/qemu/roms/seabios/src/bmp.c @@ -1,6 +1,6 @@ /* * Basic BMP data process and Raw picture data handle functions. -* Could be used to adjust pixel data format, get infomation, etc. +* Could be used to adjust pixel data format, get information, etc. * * Copyright (C) 2011 Wayne Xia * diff --git a/qemu/roms/seabios/src/boot.c b/qemu/roms/seabios/src/boot.c index f23e9e154..e0f73a385 100644 --- a/qemu/roms/seabios/src/boot.c +++ b/qemu/roms/seabios/src/boot.c @@ -19,6 +19,7 @@ #include "std/disk.h" // struct mbr_s #include "string.h" // memset #include "util.h" // irqtimer_calc +#include "tcgbios.h" // tpm_* /**************************************************************** @@ -111,9 +112,9 @@ build_pci_path(char *buf, int max, const char *devname, struct pci_device *pci) if (pci->parent) { p = build_pci_path(p, max, "pci-bridge", pci->parent); } else { - if (pci->rootbus) - p += snprintf(p, max, "/pci-root@%x", pci->rootbus); p += snprintf(p, buf+max-p, "%s", FW_PCI_DOMAIN); + if (pci->rootbus) + p += snprintf(p, buf+max-p, ",%x", pci->rootbus); } int dev = pci_bdf_to_dev(pci->bdf), fn = pci_bdf_to_fn(pci->bdf); @@ -459,8 +460,8 @@ interactive_bootmenu(void) ; char *bootmsg = romfile_loadfile("etc/boot-menu-message", NULL); - int menukey = romfile_loadint("etc/boot-menu-key", 0x86); - printf("%s", bootmsg ?: "\nPress F12 for boot menu.\n\n"); + int menukey = romfile_loadint("etc/boot-menu-key", 1); + printf("%s", bootmsg ?: "\nPress ESC for boot menu.\n\n"); free(bootmsg); u32 menutime = romfile_loadint("etc/boot-menu-wait", DEFAULT_BOOTMENU_WAIT); @@ -486,9 +487,15 @@ interactive_bootmenu(void) , strtcpy(desc, pos->description, ARRAY_SIZE(desc))); } - // Get key press + // Get key press. If the menu key is ESC, do not restart boot unless + // 1.5 seconds have passed. Otherwise users (trained by years of + // repeatedly hitting keys to enter the BIOS) will end up hitting ESC + // multiple times and immediately booting the primary boot device. + int esc_accepted_time = irqtimer_calc(menukey == 1 ? 1500 : 0); for (;;) { scan_code = get_keystroke(1000); + if (scan_code == 1 && !irqtimer_check(esc_accepted_time)) + continue; if (scan_code >= 1 && scan_code <= maxmenu+1) break; } @@ -622,6 +629,8 @@ boot_disk(u8 bootdrv, int checksig) } } + tpm_add_bcv(bootdrv, MAKE_FLATPTR(bootseg, 0), 512); + /* Canonicalize bootseg:bootip */ u16 bootip = (bootseg & 0x0fff) << 4; bootseg &= 0xf000; @@ -645,6 +654,9 @@ boot_cdrom(struct drive_s *drive_g) u8 bootdrv = CDEmu.emulated_drive; u16 bootseg = CDEmu.load_segment; + + tpm_add_cdrom(bootdrv, MAKE_FLATPTR(bootseg, 0), 512); + /* Canonicalize bootseg:bootip */ u16 bootip = (bootseg & 0x0fff) << 4; bootseg &= 0xf000; diff --git a/qemu/roms/seabios/src/cdrom.c b/qemu/roms/seabios/src/cdrom.c index 92f34f42b..a4f31adde 100644 --- a/qemu/roms/seabios/src/cdrom.c +++ b/qemu/roms/seabios/src/cdrom.c @@ -15,9 +15,7 @@ #include "std/disk.h" // DISK_RET_SUCCESS #include "string.h" // memset #include "util.h" // cdrom_prepboot - -// Locks for removable devices -u8 CDRom_locks[BUILD_MAX_EXTDRIVE] VARLOW; +#include "tcgbios.h" // tpm_* /**************************************************************** @@ -88,7 +86,7 @@ cdemu_read(struct disk_op_s *op) } int -process_cdemu_op(struct disk_op_s *op) +cdemu_process_op(struct disk_op_s *op) { if (!CONFIG_CDROM_EMU) return 0; @@ -99,13 +97,8 @@ process_cdemu_op(struct disk_op_s *op) case CMD_WRITE: case CMD_FORMAT: return DISK_RET_EWRITEPROTECT; - case CMD_VERIFY: - case CMD_RESET: - case CMD_SEEK: - case CMD_ISREADY: - return DISK_RET_SUCCESS; default: - return DISK_RET_EPARAM; + return default_process_op(op); } } @@ -122,7 +115,6 @@ cdrom_prepboot(void) struct drive_s *drive = malloc_fseg(sizeof(*drive)); if (!drive) { warn_noalloc(); - free(drive); return; } cdemu_drive_gf = drive; @@ -158,7 +150,7 @@ cdrom_boot(struct drive_s *drive) dop.lba = 0x11; dop.count = 1; dop.buf_fl = buffer; - ret = scsi_process_op(&dop); + ret = process_op(&dop); if (ret) return 3; @@ -174,7 +166,7 @@ cdrom_boot(struct drive_s *drive) // And we read the Boot Catalog dop.lba = lba; dop.count = 1; - ret = scsi_process_op(&dop); + ret = process_op(&dop); if (ret) return 7; @@ -192,6 +184,9 @@ cdrom_boot(struct drive_s *drive) if (buffer[0x20] != 0x88) return 11; // Bootable + /* measure 2048 bytes (one sector) */ + tpm_add_cdrom_catalog(MAKE_FLATPTR(GET_SEG(SS), buffer), sizeof(buffer)); + // Fill in el-torito cdrom emulation fields. emulated_drive_gf = drive; u8 media = buffer[0x21]; @@ -220,7 +215,7 @@ cdrom_boot(struct drive_s *drive) if (count > 64*1024/CDROM_SECTOR_SIZE) count = 64*1024/CDROM_SECTOR_SIZE; dop.count = count; - ret = scsi_process_op(&dop); + ret = process_op(&dop); if (ret) return 12; nbsectors -= count; diff --git a/qemu/roms/seabios/src/clock.c b/qemu/roms/seabios/src/clock.c index 9ab0ac026..e83e0f338 100644 --- a/qemu/roms/seabios/src/clock.c +++ b/qemu/roms/seabios/src/clock.c @@ -8,6 +8,7 @@ #include "biosvar.h" // SET_BDA #include "bregs.h" // struct bregs #include "hw/pic.h" // pic_eoi1 +#include "hw/ps2port.h" // ps2_check_event #include "hw/rtc.h" // rtc_read #include "hw/usb-hid.h" // usb_check_event #include "output.h" // debug_enter @@ -55,7 +56,8 @@ clock_setup(void) } enable_hwirq(0, FUNC16(entry_08)); - enable_hwirq(8, FUNC16(entry_70)); + if (CONFIG_RTC_TIMER) + enable_hwirq(8, FUNC16(entry_70)); } @@ -239,6 +241,16 @@ handle_1a07(struct bregs *regs) set_success(regs); } +static void +handle_1abb(struct bregs *regs) +{ + if (!CONFIG_TCGBIOS) + return; + + dprintf(DEBUG_tcg, "16: Calling tpm_interrupt_handler\n"); + call32(tpm_interrupt_handler32, MAKE_FLATPTR(GET_SEG(SS), regs), 0); +} + // Unsupported static void handle_1aXX(struct bregs *regs) @@ -260,17 +272,15 @@ handle_1a(struct bregs *regs) case 0x05: handle_1a05(regs); break; case 0x06: handle_1a06(regs); break; case 0x07: handle_1a07(regs); break; + case 0xbb: handle_1abb(regs); break; default: handle_1aXX(regs); break; } } -// INT 08h System Timer ISR Entry Point -void VISIBLE16 -handle_08(void) +// Update main tick counter +static void +clock_update(void) { - debug_isr(DEBUG_ISR_08); - - // Update counter u32 counter = GET_BDA(timer_counter); counter++; // compare to one days worth of timer ticks at 18.2 hz @@ -284,6 +294,15 @@ handle_08(void) // Check for internal events. floppy_tick(); usb_check_event(); + ps2_check_event(); +} + +// INT 08h System Timer ISR Entry Point +void VISIBLE16 +handle_08(void) +{ + debug_isr(DEBUG_ISR_08); + clock_update(); // chain to user timer tick INT #0x1c struct bregs br; @@ -294,6 +313,20 @@ handle_08(void) pic_eoi1(); } +u32 last_timer_check VARLOW; + +// Simulate timer irq on machines without hardware irqs +void +clock_poll_irq(void) +{ + if (CONFIG_HARDWARE_IRQ) + return; + if (!timer_check(GET_LOW(last_timer_check))) + return; + SET_LOW(last_timer_check, timer_calc(ticks_to_ms(1))); + clock_update(); +} + /**************************************************************** * IRQ based timer @@ -359,6 +392,10 @@ clear_usertimer(void) void handle_1586(struct bregs *regs) { + if (!CONFIG_RTC_TIMER) { + set_code_unimplemented(regs, RET_EUNSUPPORTED); + return; + } // Use the rtc to wait for the specified time. u8 statusflag = 0; u32 count = (regs->cx << 16) | regs->dx; @@ -402,6 +439,10 @@ handle_1583XX(struct bregs *regs) void handle_1583(struct bregs *regs) { + if (!CONFIG_RTC_TIMER) { + handle_1583XX(regs); + return; + } switch (regs->al) { case 0x00: handle_158300(regs); break; case 0x01: handle_158301(regs); break; @@ -415,6 +456,8 @@ handle_1583(struct bregs *regs) void VISIBLE16 handle_70(void) { + if (!CONFIG_RTC_TIMER) + return; debug_isr(DEBUG_ISR_70); // Check which modes are enabled and have occurred. diff --git a/qemu/roms/seabios/src/config.h b/qemu/roms/seabios/src/config.h index 6da067d0b..6c47f161c 100644 --- a/qemu/roms/seabios/src/config.h +++ b/qemu/roms/seabios/src/config.h @@ -22,6 +22,8 @@ #define BUILD_MAX_EXTDRIVE 16 // Number of bytes the smbios may be and still live in the f-segment #define BUILD_MAX_SMBIOS_FSEG 600 +// Maximum number of bytes the mptable may be and still be copied to f-segment +#define BUILD_MAX_MPTABLE_FSEG 600 #define BUILD_MODEL_ID 0xFC #define BUILD_SUBMODEL_ID 0x00 @@ -104,5 +106,6 @@ #define DEBUG_unimplemented 2 #define DEBUG_invalid 3 #define DEBUG_thread 2 +#define DEBUG_tcg 20 #endif // config.h diff --git a/qemu/roms/seabios/src/disk.c b/qemu/roms/seabios/src/disk.c index 0e0af24b3..3854d0024 100644 --- a/qemu/roms/seabios/src/disk.c +++ b/qemu/roms/seabios/src/disk.c @@ -407,6 +407,9 @@ disk_1344(struct bregs *regs, struct drive_s *drive_gf) extended_access(regs, drive_gf, CMD_VERIFY); } +// Locks for removable devices +u8 CDRom_locks[BUILD_MAX_EXTDRIVE] VARLOW; + // lock static void disk_134500(struct bregs *regs, struct drive_s *drive_gf) @@ -519,7 +522,7 @@ disk_1347(struct bregs *regs, struct drive_s *drive_gf) static void disk_1348(struct bregs *regs, struct drive_s *drive_gf) { - int ret = fill_edd(regs->ds, (void*)(regs->si+0), drive_gf); + int ret = fill_edd(SEGOFF(regs->ds, regs->si), drive_gf); disk_ret(regs, ret); } diff --git a/qemu/roms/seabios/src/e820map.c b/qemu/roms/seabios/src/e820map.c new file mode 100644 index 000000000..39445cf63 --- /dev/null +++ b/qemu/roms/seabios/src/e820map.c @@ -0,0 +1,152 @@ +// Support for building memory maps suitable for int 15 e820 calls. +// +// Copyright (C) 2008,2009 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // BUILD_MAX_E820 +#include "e820map.h" // struct e820entry +#include "output.h" // dprintf +#include "string.h" // memmove + + +/**************************************************************** + * e820 memory map + ****************************************************************/ + +// Info on e820 map location and size. +struct e820entry e820_list[BUILD_MAX_E820] VARFSEG; +int e820_count VARFSEG; + +// Remove an entry from the e820_list. +static void +remove_e820(int i) +{ + e820_count--; + memmove(&e820_list[i], &e820_list[i+1] + , sizeof(e820_list[0]) * (e820_count - i)); +} + +// Insert an entry in the e820_list at the given position. +static void +insert_e820(int i, u64 start, u64 size, u32 type) +{ + if (e820_count >= BUILD_MAX_E820) { + warn_noalloc(); + return; + } + + memmove(&e820_list[i+1], &e820_list[i] + , sizeof(e820_list[0]) * (e820_count - i)); + e820_count++; + struct e820entry *e = &e820_list[i]; + e->start = start; + e->size = size; + e->type = type; +} + +static const char * +e820_type_name(u32 type) +{ + switch (type) { + case E820_RAM: return "RAM"; + case E820_RESERVED: return "RESERVED"; + case E820_ACPI: return "ACPI"; + case E820_NVS: return "NVS"; + case E820_UNUSABLE: return "UNUSABLE"; + default: return "UNKNOWN"; + } +} + +// Show the current e820_list. +static void +dump_map(void) +{ + dprintf(1, "e820 map has %d items:\n", e820_count); + int i; + for (i=0; istart + e->size; + dprintf(1, " %d: %016llx - %016llx = %d %s\n", i + , e->start, e_end, e->type, e820_type_name(e->type)); + } +} + +#define E820_HOLE ((u32)-1) // Used internally to remove entries + +// Add a new entry to the list. This scans for overlaps and keeps the +// list sorted. +void +e820_add(u64 start, u64 size, u32 type) +{ + dprintf(8, "Add to e820 map: %08llx %08llx %d\n", start, size, type); + + if (! size) + // Huh? Nothing to do. + return; + + // Find position of new item (splitting existing item if needed). + u64 end = start + size; + int i; + for (i=0; istart + e->size; + if (start > e_end) + continue; + // Found position - check if an existing item needs to be split. + if (start > e->start) { + if (type == e->type) { + // Same type - merge them. + size += start - e->start; + start = e->start; + } else { + // Split existing item. + e->size = start - e->start; + i++; + if (e_end > end) + insert_e820(i, end, e_end - end, e->type); + } + } + break; + } + // Remove/adjust existing items that are overlapping. + while (istart) + // No overlap - done. + break; + u64 e_end = e->start + e->size; + if (end >= e_end) { + // Existing item completely overlapped - remove it. + remove_e820(i); + continue; + } + // Not completely overlapped - adjust its start. + e->start = end; + e->size = e_end - end; + if (type == e->type) { + // Same type - merge them. + size += e->size; + remove_e820(i); + } + break; + } + // Insert new item. + if (type != E820_HOLE) + insert_e820(i, start, size, type); + //dump_map(); +} + +// Remove any definitions in a memory range (make a memory hole). +void +e820_remove(u64 start, u64 size) +{ + e820_add(start, size, E820_HOLE); +} + +// Report on final memory locations. +void +e820_prepboot(void) +{ + dump_map(); +} diff --git a/qemu/roms/seabios/src/e820map.h b/qemu/roms/seabios/src/e820map.h new file mode 100644 index 000000000..de8b52300 --- /dev/null +++ b/qemu/roms/seabios/src/e820map.h @@ -0,0 +1,26 @@ +#ifndef __E820MAP_H +#define __E820MAP_H + +#include "types.h" // u64 + +#define E820_RAM 1 +#define E820_RESERVED 2 +#define E820_ACPI 3 +#define E820_NVS 4 +#define E820_UNUSABLE 5 + +struct e820entry { + u64 start; + u64 size; + u32 type; +}; + +void e820_add(u64 start, u64 size, u32 type); +void e820_remove(u64 start, u64 size); +void e820_prepboot(void); + +// e820 map storage +extern struct e820entry e820_list[]; +extern int e820_count; + +#endif // e820map.h diff --git a/qemu/roms/seabios/src/fw/biostables.c b/qemu/roms/seabios/src/fw/biostables.c index 50a891be8..9fb9ff9df 100644 --- a/qemu/roms/seabios/src/fw/biostables.c +++ b/qemu/roms/seabios/src/fw/biostables.c @@ -6,14 +6,15 @@ #include "byteorder.h" // le32_to_cpu #include "config.h" // CONFIG_* +#include "hw/pci.h" // pci_config_writeb #include "malloc.h" // malloc_fseg +#include "memmap.h" // SYMBOL #include "output.h" // dprintf -#include "hw/pci.h" // pci_config_writeb +#include "romfile.h" // romfile_find #include "std/acpi.h" // struct rsdp_descriptor #include "std/mptable.h" // MPTABLE_SIGNATURE #include "std/pirtable.h" // struct pir_header #include "std/smbios.h" // struct smbios_entry_point -#include "romfile.h" #include "string.h" // memcpy #include "util.h" // copy_table #include "x86.h" // outb @@ -54,6 +55,11 @@ copy_mptable(void *pos) return; u32 length = p->length * 16; u16 mpclength = ((struct mptable_config_s *)p->physaddr)->length; + if (length + mpclength > BUILD_MAX_MPTABLE_FSEG) { + dprintf(1, "Skipping MPTABLE copy due to large size (%d bytes)\n" + , length + mpclength); + return; + } // Allocate final memory location. (In theory the config // structure can go in high memory, but Linux kernels before // v2.6.30 crash with that.) @@ -117,9 +123,8 @@ copy_acpi_rsdp(void *pos) void *find_acpi_rsdp(void) { - extern u8 zonefseg_start[], zonefseg_end[]; - unsigned long start = (unsigned long)zonefseg_start; - unsigned long end = (unsigned long)zonefseg_end; + unsigned long start = SYMBOL(zonefseg_start); + unsigned long end = SYMBOL(zonefseg_end); unsigned long pos; for (pos = ALIGN(start, 0x10); pos <= ALIGN_DOWN(end, 0x10); pos += 0x10) @@ -271,7 +276,7 @@ copy_smbios(void *pos) if (SMBiosAddr) return; struct smbios_entry_point *p = pos; - if (memcmp(p->anchor_string, "_SM_", 4)) + if (p->signature != SMBIOS_SIGNATURE) return; if (checksum(pos, 0x10) != 0) return; @@ -301,17 +306,42 @@ display_uuid(void) if (memcmp(uuid, empty_uuid, sizeof(empty_uuid)) == 0) return; - printf("Machine UUID" - " %02x%02x%02x%02x" - "-%02x%02x" - "-%02x%02x" - "-%02x%02x" - "-%02x%02x%02x%02x%02x%02x\n" - , uuid[ 0], uuid[ 1], uuid[ 2], uuid[ 3] - , uuid[ 4], uuid[ 5] - , uuid[ 6], uuid[ 7] - , uuid[ 8], uuid[ 9] - , uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); + /* + * According to SMBIOS v2.6 the first three fields are encoded in + * little-endian format. Versions prior to v2.6 did not specify + * the encoding, but we follow dmidecode and assume big-endian + * encoding. + */ + if (SMBiosAddr->smbios_major_version > 2 || + (SMBiosAddr->smbios_major_version == 2 && + SMBiosAddr->smbios_minor_version >= 6)) { + printf("Machine UUID" + " %02x%02x%02x%02x" + "-%02x%02x" + "-%02x%02x" + "-%02x%02x" + "-%02x%02x%02x%02x%02x%02x\n" + , uuid[ 3], uuid[ 2], uuid[ 1], uuid[ 0] + , uuid[ 5], uuid[ 4] + , uuid[ 7], uuid[ 6] + , uuid[ 8], uuid[ 9] + , uuid[10], uuid[11], uuid[12] + , uuid[13], uuid[14], uuid[15]); + } else { + printf("Machine UUID" + " %02x%02x%02x%02x" + "-%02x%02x" + "-%02x%02x" + "-%02x%02x" + "-%02x%02x%02x%02x%02x%02x\n" + , uuid[ 0], uuid[ 1], uuid[ 2], uuid[ 3] + , uuid[ 4], uuid[ 5] + , uuid[ 6], uuid[ 7] + , uuid[ 8], uuid[ 9] + , uuid[10], uuid[11], uuid[12] + , uuid[13], uuid[14], uuid[15]); + } + return; } } @@ -447,7 +477,7 @@ void smbios_setup(void) { if (smbios_romfile_setup()) - return; + return; smbios_legacy_setup(); } diff --git a/qemu/roms/seabios/src/fw/coreboot.c b/qemu/roms/seabios/src/fw/coreboot.c index 8fd84493b..4fe12928c 100644 --- a/qemu/roms/seabios/src/fw/coreboot.c +++ b/qemu/roms/seabios/src/fw/coreboot.c @@ -7,10 +7,10 @@ #include "block.h" // MAXDESCSIZE #include "byteorder.h" // be32_to_cpu #include "config.h" // CONFIG_* +#include "e820map.h" // e820_add #include "hw/pci.h" // pci_probe_devices #include "lzmadecode.h" // LzmaDecode #include "malloc.h" // free -#include "memmap.h" // add_e820 #include "output.h" // dprintf #include "paravirt.h" // PlatformRunningOn #include "romfile.h" // romfile_findprefix @@ -184,12 +184,12 @@ coreboot_preinit(void) u32 type = m->type; if (type == CB_MEM_TABLE) type = E820_RESERVED; - add_e820(m->start, m->size, type); + e820_add(m->start, m->size, type); } // Ughh - coreboot likes to set a map at 0x0000-0x1000, but this // confuses grub. So, override it. - add_e820(0, 16*1024, E820_RAM); + e820_add(0, 16*1024, E820_RAM); struct cb_cbmem_ref *cbref = find_cb_subtable(cbh, CB_TAG_CBMEM_CONSOLE); if (cbref) { @@ -210,7 +210,7 @@ coreboot_preinit(void) fail: // No table found.. Use 16Megs as a dummy value. dprintf(1, "Unable to find coreboot table!\n"); - add_e820(0, 16*1024*1024, E820_RAM); + e820_add(0, 16*1024*1024, E820_RAM); return; } @@ -421,6 +421,13 @@ coreboot_cbfs_init(void) return; struct cbfs_header *hdr = *(void **)(CONFIG_CBFS_LOCATION - 4); + if ((u32)hdr & 0x03) { + dprintf(1, "Invalid CBFS pointer %p\n", hdr); + return; + } + if (CONFIG_CBFS_LOCATION && (u32)hdr > CONFIG_CBFS_LOCATION) + // Looks like the pointer is relative to CONFIG_CBFS_LOCATION + hdr = (void*)hdr + CONFIG_CBFS_LOCATION; if (hdr->magic != cpu_to_be32(CBFS_HEADER_MAGIC)) { dprintf(1, "Unable to find CBFS (ptr=%p; got %x not %x)\n" , hdr, hdr->magic, cpu_to_be32(CBFS_HEADER_MAGIC)); @@ -503,7 +510,7 @@ cbfs_run_payload(struct cbfs_file *fhdr) break; case PAYLOAD_SEGMENT_ENTRY: { dprintf(1, "Calling addr %p\n", dest); - void (*func)() = dest; + void (*func)(void) = dest; func(); return; } diff --git a/qemu/roms/seabios/src/fw/csm.c b/qemu/roms/seabios/src/fw/csm.c index 7cdb398f2..7cadd12e5 100644 --- a/qemu/roms/seabios/src/fw/csm.c +++ b/qemu/roms/seabios/src/fw/csm.c @@ -4,20 +4,21 @@ // // This file may be distributed under the terms of the GNU LGPLv3 license. -#include "bregs.h" +#include "bregs.h" // struct bregs #include "config.h" // CONFIG_* +#include "e820map.h" // e820_add #include "farptr.h" // MAKE_FLATPTR -#include "hw/pci.h" -#include "hw/pic.h" -#include "malloc.h" // csm_malloc_preinit -#include "memmap.h" +#include "hw/pci.h" // pci_probe_devices +#include "hw/pic.h" // pic_irqmask_read +#include "malloc.h" // malloc_csm_preinit +#include "memmap.h" // SYMBOL #include "output.h" // dprintf +#include "paravirt.h" // qemu_preinit #include "stacks.h" // wait_threads #include "std/acpi.h" // RSDP_SIGNATURE #include "std/bda.h" // struct bios_data_area_s #include "std/optionrom.h" // struct rom_header #include "util.h" // copy_smbios -#include "paravirt.h" // qemu_preinit #define UINT8 u8 #define UINT16 u16 @@ -47,12 +48,11 @@ static void csm_return(struct bregs *regs) { u32 rommax = rom_get_max(); - extern u8 final_readonly_start[]; dprintf(3, "handle_csm returning AX=%04x\n", regs->ax); csm_compat_table.UmaAddress = rommax; - csm_compat_table.UmaSize = (u32)final_readonly_start - rommax; + csm_compat_table.UmaSize = SYMBOL(final_readonly_start) - rommax; PICMask = pic_irqmask_read(); __csm_return(regs); @@ -95,7 +95,7 @@ handle_csm_0000(struct bregs *regs) dprintf(3, "LoPmmMemory %08x\n", csm_init_table->LowPmmMemory); dprintf(3, "LoPmmMemorySize %08x\n", csm_init_table->LowPmmMemorySizeInBytes); - csm_malloc_preinit(csm_init_table->LowPmmMemory, + malloc_csm_preinit(csm_init_table->LowPmmMemory, csm_init_table->LowPmmMemorySizeInBytes, csm_init_table->HiPmmMemory, csm_init_table->HiPmmMemorySizeInBytes); @@ -147,11 +147,11 @@ handle_csm_0002(struct bregs *regs) struct e820entry *p = (void *)csm_compat_table.E820Pointer; int i; for (i=0; i < csm_compat_table.E820Length / sizeof(struct e820entry); i++) - add_e820(p[i].start, p[i].size, p[i].type); + e820_add(p[i].start, p[i].size, p[i].type); if (csm_init_table->HiPmmMemorySizeInBytes > BUILD_MAX_HIGHTABLE) { u32 hi_pmm_end = csm_init_table->HiPmmMemory + csm_init_table->HiPmmMemorySizeInBytes; - add_e820(hi_pmm_end - BUILD_MAX_HIGHTABLE, BUILD_MAX_HIGHTABLE, E820_RESERVED); + e820_add(hi_pmm_end - BUILD_MAX_HIGHTABLE, BUILD_MAX_HIGHTABLE, E820_RESERVED); } // For PCIBIOS 1ab10e @@ -183,6 +183,7 @@ handle_csm_0002(struct bregs *regs) struct bios_data_area_s *bda = MAKE_FLATPTR(SEG_BDA, 0); bda->hdcount = 0; + thread_setup(); mathcp_setup(); timer_setup(); clock_setup(); diff --git a/qemu/roms/seabios/src/fw/dev-q35.h b/qemu/roms/seabios/src/fw/dev-q35.h index c6f8bd9e7..201825deb 100644 --- a/qemu/roms/seabios/src/fw/dev-q35.h +++ b/qemu/roms/seabios/src/fw/dev-q35.h @@ -27,6 +27,9 @@ #define ICH9_LPC_GEN_PMCON_1_SMI_LOCK (1 << 4) #define ICH9_LPC_PORT_ELCR1 0x4d0 #define ICH9_LPC_PORT_ELCR2 0x4d1 +#define ICH9_LPC_RCBA 0xf0 +#define ICH9_LPC_RCBA_ADDR 0xfed1c000 +#define ICH9_LPC_RCBA_EN 0x1 #define PCI_DEVICE_ID_INTEL_ICH9_SMBUS 0x2930 #define ICH9_SMB_SMB_BASE 0x20 #define ICH9_SMB_HOSTC 0x40 diff --git a/qemu/roms/seabios/src/fw/multiboot.c b/qemu/roms/seabios/src/fw/multiboot.c new file mode 100644 index 000000000..d9df06764 --- /dev/null +++ b/qemu/roms/seabios/src/fw/multiboot.c @@ -0,0 +1,111 @@ +// Multiboot interface support. +// +// Copyright (C) 2015 Vladimir Serbinenko +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "romfile.h" // romfile_add +#include "std/multiboot.h" // MULTIBOOT_* +#include "string.h" // memset +#include "util.h" // multiboot_init + +struct mbfs_romfile_s { + struct romfile_s file; + void *data; +}; + +static int +extract_filename(char *dest, char *src, size_t lim) +{ + char *ptr; + for (ptr = src; *ptr; ptr++) { + if (!(ptr == src || ptr[-1] == ' ' || ptr[-1] == '\t')) + continue; + /* memcmp stops early if it encounters \0 as it doesn't match name=. */ + if (memcmp(ptr, "name=", 5) == 0) { + int i; + char *optr = dest; + for (i = 0, ptr += 5; *ptr && *ptr != ' ' && i < lim; i++) { + *optr++ = *ptr++; + } + *optr++ = '\0'; + return 1; + } + } + return 0; +} + +// Copy a file to memory +static int +mbfs_copyfile(struct romfile_s *file, void *dst, u32 maxlen) +{ + struct mbfs_romfile_s *cfile; + cfile = container_of(file, struct mbfs_romfile_s, file); + u32 size = cfile->file.size; + void *src = cfile->data; + + // Not compressed. + dprintf(3, "Copying data %d@%p to %d@%p\n", size, src, maxlen, dst); + if (size > maxlen) { + warn_noalloc(); + return -1; + } + iomemcpy(dst, src, size); + return size; +} + +u32 __VISIBLE entry_elf_eax, entry_elf_ebx; + +void +multiboot_init(void) +{ + struct multiboot_info *mbi; + if (!CONFIG_MULTIBOOT) + return; + dprintf(1, "multiboot: eax=%x, ebx=%x\n", entry_elf_eax, entry_elf_ebx); + if (entry_elf_eax != MULTIBOOT_BOOTLOADER_MAGIC) + return; + mbi = (void *)entry_elf_ebx; + dprintf(1, "mbptr=%p\n", mbi); + dprintf(1, "flags=0x%x, mods=0x%x, mods_c=%d\n", mbi->flags, mbi->mods_addr, + mbi->mods_count); + if (!(mbi->flags & MULTIBOOT_INFO_MODS)) + return; + int i; + struct multiboot_mod_list *mod = (void *)mbi->mods_addr; + for (i = 0; i < mbi->mods_count; i++) { + struct mbfs_romfile_s *cfile; + u8 *copy; + u32 len; + if (!mod[i].cmdline) + continue; + len = mod[i].mod_end - mod[i].mod_start; + cfile = malloc_tmp(sizeof(*cfile)); + if (!cfile) { + warn_noalloc(); + return; + } + memset(cfile, 0, sizeof(*cfile)); + dprintf(1, "module %s, size 0x%x\n", (char *)mod[i].cmdline, len); + if (!extract_filename(cfile->file.name, (char *)mod[i].cmdline, + sizeof(cfile->file.name))) { + free(cfile); + continue; + } + dprintf(1, "assigned file name <%s>\n", cfile->file.name); + cfile->file.size = len; + copy = malloc_tmp(len); + if (!copy) { + warn_noalloc(); + free(cfile); + return; + } + memcpy(copy, (void *)mod[i].mod_start, len); + cfile->file.copy = mbfs_copyfile; + cfile->data = copy; + romfile_add(&cfile->file); + } +} diff --git a/qemu/roms/seabios/src/fw/paravirt.c b/qemu/roms/seabios/src/fw/paravirt.c index db22ae8fc..3fae13a83 100644 --- a/qemu/roms/seabios/src/fw/paravirt.c +++ b/qemu/roms/seabios/src/fw/paravirt.c @@ -10,11 +10,11 @@ #include "byteorder.h" // be32_to_cpu #include "config.h" // CONFIG_QEMU +#include "e820map.h" // e820_add #include "hw/pci.h" // create_pirtable #include "hw/pci_regs.h" // PCI_DEVICE_ID #include "hw/rtc.h" // CMOS_* #include "malloc.h" // malloc_tmp -#include "memmap.h" // add_e820 #include "output.h" // dprintf #include "paravirt.h" // qemu_cfg_preinit #include "romfile.h" // romfile_loadint @@ -23,6 +23,7 @@ #include "util.h" // pci_setup #include "x86.h" // cpuid #include "xen.h" // xen_biostable_setup +#include "stacks.h" // yield // Amount of continuous ram under 4Gig u32 RamSize; @@ -30,6 +31,13 @@ u32 RamSize; u64 RamSizeOver4G; // Type of emulator platform. int PlatformRunningOn VARFSEG; +// cfg_dma enabled +int cfg_dma_enabled = 0; + +inline int qemu_cfg_dma_enabled(void) +{ + return cfg_dma_enabled; +} /* This CPUID returns the signature 'KVMKVMKVM' in ebx, ecx, and edx. It * should be used to determine that a VM is running under KVM. @@ -114,10 +122,10 @@ qemu_preinit(void) | (rtc_read(CMOS_MEM_EXTMEM_HIGH) << 18)) + 1 * 1024 * 1024); RamSize = rs; - add_e820(0, rs, E820_RAM); + e820_add(0, rs, E820_RAM); /* reserve 256KB BIOS area at the end of 4 GB */ - add_e820(0xfffc0000, 256*1024, E820_RESERVED); + e820_add(0xfffc0000, 256*1024, E820_RESERVED); dprintf(1, "RamSize: 0x%08x [cmos]\n", RamSize); } @@ -198,24 +206,64 @@ qemu_cfg_select(u16 f) outw(f, PORT_QEMU_CFG_CTL); } +static void +qemu_cfg_dma_transfer(void *address, u32 length, u32 control) +{ + QemuCfgDmaAccess access; + + access.address = cpu_to_be64((u64)(u32)address); + access.length = cpu_to_be32(length); + access.control = cpu_to_be32(control); + + barrier(); + + outl(cpu_to_be32((u32)&access), PORT_QEMU_CFG_DMA_ADDR_LOW); + + while(be32_to_cpu(access.control) & ~QEMU_CFG_DMA_CTL_ERROR) { + yield(); + } +} + static void qemu_cfg_read(void *buf, int len) { - insb(PORT_QEMU_CFG_DATA, buf, len); + if (len == 0) { + return; + } + + if (qemu_cfg_dma_enabled()) { + qemu_cfg_dma_transfer(buf, len, QEMU_CFG_DMA_CTL_READ); + } else { + insb(PORT_QEMU_CFG_DATA, buf, len); + } } static void qemu_cfg_skip(int len) { - while (len--) - inb(PORT_QEMU_CFG_DATA); + if (len == 0) { + return; + } + + if (qemu_cfg_dma_enabled()) { + qemu_cfg_dma_transfer(0, len, QEMU_CFG_DMA_CTL_SKIP); + } else { + while (len--) + inb(PORT_QEMU_CFG_DATA); + } } static void qemu_cfg_read_entry(void *buf, int e, int len) { - qemu_cfg_select(e); - qemu_cfg_read(buf, len); + if (qemu_cfg_dma_enabled()) { + u32 control = (e << 16) | QEMU_CFG_DMA_CTL_SELECT + | QEMU_CFG_DMA_CTL_READ; + qemu_cfg_dma_transfer(buf, len, control); + } else { + qemu_cfg_select(e); + qemu_cfg_read(buf, len); + } } struct qemu_romfile_s { @@ -230,9 +278,14 @@ qemu_cfg_read_file(struct romfile_s *file, void *dst, u32 maxlen) return -1; struct qemu_romfile_s *qfile; qfile = container_of(file, struct qemu_romfile_s, file); - qemu_cfg_select(qfile->select); - qemu_cfg_skip(qfile->skip); - qemu_cfg_read(dst, file->size); + if (qfile->skip == 0) { + /* Do it in one transfer */ + qemu_cfg_read_entry(dst, qfile->select, file->size); + } else { + qemu_cfg_select(qfile->select); + qemu_cfg_skip(qfile->skip); + qemu_cfg_read(dst, file->size); + } return file->size; } @@ -302,7 +355,7 @@ qemu_cfg_e820(void) } /* fall through */ case E820_RESERVED: - add_e820(table[i].address, table[i].length, table[i].type); + e820_add(table[i].address, table[i].length, table[i].type); break; default: /* @@ -324,13 +377,13 @@ qemu_cfg_e820(void) int i; for (i = 0; i < count32; i++) { qemu_cfg_read(&entry, sizeof(entry)); - add_e820(entry.address, entry.length, entry.type); + e820_add(entry.address, entry.length, entry.type); } } else if (runningOnKVM()) { // Backwards compatibility - provide hard coded range. // 4 pages before the bios, 3 pages for vmx tss pages, the // other page for EPT real mode pagetable - add_e820(0xfffbc000, 4*4096, E820_RESERVED); + e820_add(0xfffbc000, 4*4096, E820_RESERVED); } // Check for memory over 4Gig in cmos @@ -338,7 +391,7 @@ qemu_cfg_e820(void) | ((u32)rtc_read(CMOS_MEM_HIGHMEM_MID) << 24) | ((u64)rtc_read(CMOS_MEM_HIGHMEM_HIGH) << 32)); RamSizeOver4G = high; - add_e820(0x100000000ull, high, E820_RAM); + e820_add(0x100000000ull, high, E820_RAM); dprintf(1, "RamSizeOver4G: 0x%016llx [cmos]\n", RamSizeOver4G); } @@ -422,8 +475,18 @@ void qemu_cfg_init(void) for (i = 0; i < 4; i++) if (inb(PORT_QEMU_CFG_DATA) != sig[i]) return; + dprintf(1, "Found QEMU fw_cfg\n"); + // Detect DMA interface. + u32 id; + qemu_cfg_read_entry(&id, QEMU_CFG_ID, sizeof(id)); + + if (id & QEMU_CFG_VERSION_DMA) { + dprintf(1, "QEMU fw_cfg DMA interface supported\n"); + cfg_dma_enabled = 1; + } + // Populate romfiles for legacy fw_cfg entries qemu_cfg_legacy(); diff --git a/qemu/roms/seabios/src/fw/paravirt.h b/qemu/roms/seabios/src/fw/paravirt.h index 95ffb92ad..ed8e5f1f8 100644 --- a/qemu/roms/seabios/src/fw/paravirt.h +++ b/qemu/roms/seabios/src/fw/paravirt.h @@ -9,6 +9,12 @@ #define PF_XEN (1<<1) #define PF_KVM (1<<2) +typedef struct QemuCfgDmaAccess { + u32 control; + u32 length; + u64 address; +} PACKED QemuCfgDmaAccess; + extern u32 RamSize; extern u64 RamSizeOver4G; extern int PlatformRunningOn; @@ -25,11 +31,23 @@ static inline int runningOnKVM(void) { } // Common paravirt ports. -#define PORT_SMI_CMD 0x00b2 -#define PORT_SMI_STATUS 0x00b3 -#define PORT_QEMU_CFG_CTL 0x0510 -#define PORT_QEMU_CFG_DATA 0x0511 +#define PORT_SMI_CMD 0x00b2 +#define PORT_SMI_STATUS 0x00b3 +#define PORT_QEMU_CFG_CTL 0x0510 +#define PORT_QEMU_CFG_DATA 0x0511 +#define PORT_QEMU_CFG_DMA_ADDR_HIGH 0x0514 +#define PORT_QEMU_CFG_DMA_ADDR_LOW 0x0518 + +// QEMU_CFG_DMA_CONTROL bits +#define QEMU_CFG_DMA_CTL_ERROR 0x01 +#define QEMU_CFG_DMA_CTL_READ 0x02 +#define QEMU_CFG_DMA_CTL_SKIP 0x04 +#define QEMU_CFG_DMA_CTL_SELECT 0x08 + +// QEMU_CFG_DMA ID bit +#define QEMU_CFG_VERSION_DMA 2 +int qemu_cfg_dma_enabled(void); void qemu_preinit(void); void qemu_platform_setup(void); void qemu_cfg_init(void); diff --git a/qemu/roms/seabios/src/fw/pciinit.c b/qemu/roms/seabios/src/fw/pciinit.c index 46ae7090e..c31c2fa0c 100644 --- a/qemu/roms/seabios/src/fw/pciinit.c +++ b/qemu/roms/seabios/src/fw/pciinit.c @@ -9,13 +9,13 @@ #include "config.h" // CONFIG_* #include "dev-q35.h" // Q35_HOST_BRIDGE_PCIEXBAR_ADDR #include "dev-piix.h" // PIIX_* +#include "e820map.h" // e820_add #include "hw/ata.h" // PORT_ATA1_CMD_BASE #include "hw/pci.h" // pci_config_readl #include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL #include "hw/pci_regs.h" // PCI_COMMAND #include "list.h" // struct hlist_node #include "malloc.h" // free -#include "memmap.h" // add_e820 #include "output.h" // dprintf #include "paravirt.h" // RamSize #include "romfile.h" // romfile_loadint @@ -183,6 +183,11 @@ static void mch_isa_bridge_setup(struct pci_device *dev, void *arg) /* acpi enable, SCI: IRQ9 000b = irq9*/ pci_config_writeb(bdf, ICH9_LPC_ACPI_CTRL, ICH9_LPC_ACPI_CTRL_ACPI_EN); + /* set root complex register block BAR */ + pci_config_writel(bdf, ICH9_LPC_RCBA, + ICH9_LPC_RCBA_ADDR | ICH9_LPC_RCBA_EN); + e820_add(ICH9_LPC_RCBA_ADDR, 16*1024, E820_RESERVED); + acpi_pm1a_cnt = acpi_pm_base + 0x04; pmtimer_setup(acpi_pm_base + 0x08); } @@ -316,6 +321,10 @@ static void pci_bios_init_device(struct pci_device *pci) /* enable memory mappings */ pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_SERR); + /* enable SERR# for forwarding */ + if (pci->header_type & PCI_HEADER_TYPE_BRIDGE) + pci_config_maskw(bdf, PCI_BRIDGE_CONTROL, 0, + PCI_BRIDGE_CTL_SERR); } static void pci_bios_init_devices(void) @@ -391,7 +400,7 @@ static void mch_mem_addr_setup(struct pci_device *dev, void *arg) pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, 0); pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR + 4, upper); pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, lower); - add_e820(addr, size, E820_RESERVED); + e820_add(addr, size, E820_RESERVED); /* setup pci i/o window (above mmconfig) */ pcimem_start = addr + size; @@ -636,9 +645,8 @@ pci_region_create_entry(struct pci_bus *bus, struct pci_device *dev, return entry; } -static int pci_bus_hotplug_support(struct pci_bus *bus) +static int pci_bus_hotplug_support(struct pci_bus *bus, u8 pcie_cap) { - u8 pcie_cap = pci_find_capability(bus->bus_dev, PCI_CAP_ID_EXP); u8 shpc_cap; if (pcie_cap) { @@ -662,7 +670,7 @@ static int pci_bus_hotplug_support(struct pci_bus *bus) return downstream_port && slot_implemented; } - shpc_cap = pci_find_capability(bus->bus_dev, PCI_CAP_ID_SHPC); + shpc_cap = pci_find_capability(bus->bus_dev, PCI_CAP_ID_SHPC, 0); return !!shpc_cap; } @@ -718,7 +726,8 @@ static int pci_bios_check_devices(struct pci_bus *busses) */ parent = &busses[0]; int type; - int hotplug_support = pci_bus_hotplug_support(s); + u8 pcie_cap = pci_find_capability(s->bus_dev, PCI_CAP_ID_EXP, 0); + int hotplug_support = pci_bus_hotplug_support(s, pcie_cap); for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) { u64 align = (type == PCI_REGION_TYPE_IO) ? PCI_BRIDGE_IO_MIN : PCI_BRIDGE_MEM_MIN; @@ -727,7 +736,8 @@ static int pci_bios_check_devices(struct pci_bus *busses) if (pci_region_align(&s->r[type]) > align) align = pci_region_align(&s->r[type]); u64 sum = pci_region_sum(&s->r[type]); - if (!sum && hotplug_support) + int resource_optional = pcie_cap && (type == PCI_REGION_TYPE_IO); + if (!sum && hotplug_support && !resource_optional) sum = align; /* reserve min size for hot-plug */ u64 size = ALIGN(sum, align); int is64 = pci_bios_bridge_region_is64(&s->r[type], diff --git a/qemu/roms/seabios/src/fw/shadow.c b/qemu/roms/seabios/src/fw/shadow.c index 4f00006bf..ee87d36e0 100644 --- a/qemu/roms/seabios/src/fw/shadow.c +++ b/qemu/roms/seabios/src/fw/shadow.c @@ -53,9 +53,8 @@ __make_bios_writable_intel(u16 bdf, u32 pam0) return; // Copy bios. - extern u8 code32flat_start[], code32flat_end[]; - memcpy(code32flat_start, code32flat_start + BIOS_SRC_OFFSET - , code32flat_end - code32flat_start); + memcpy(VSYMBOL(code32flat_start), VSYMBOL(code32flat_start) + BIOS_SRC_OFFSET + , SYMBOL(code32flat_end) - SYMBOL(code32flat_start)); } static void @@ -65,7 +64,7 @@ make_bios_writable_intel(u16 bdf, u32 pam0) if (!(reg & 0x10)) { // QEMU doesn't fully implement the piix shadow capabilities - // if ram isn't backing the bios segment when shadowing is - // disabled, the code itself wont be in memory. So, run the + // disabled, the code itself won't be in memory. So, run the // code from the high-memory flash location. u32 pos = (u32)__make_bios_writable_intel + BIOS_SRC_OFFSET; void (*func)(u16 bdf, u32 pam0) = (void*)pos; @@ -165,7 +164,6 @@ qemu_prep_reset(void) // QEMU doesn't map 0xc0000-0xfffff back to the original rom on a // reset, so do that manually before invoking a hard reset. make_bios_writable(); - extern u8 code32flat_start[], code32flat_end[]; - memcpy(code32flat_start, code32flat_start + BIOS_SRC_OFFSET - , code32flat_end - code32flat_start); + memcpy(VSYMBOL(code32flat_start), VSYMBOL(code32flat_start) + BIOS_SRC_OFFSET + , SYMBOL(code32flat_end) - SYMBOL(code32flat_start)); } diff --git a/qemu/roms/seabios/src/fw/smbios.c b/qemu/roms/seabios/src/fw/smbios.c index dba054133..f3b5ad9dd 100644 --- a/qemu/roms/seabios/src/fw/smbios.c +++ b/qemu/roms/seabios/src/fw/smbios.c @@ -37,7 +37,7 @@ smbios_entry_point_setup(u16 max_structure_size, struct smbios_entry_point ep; memset(&ep, 0, sizeof(ep)); - memcpy(ep.anchor_string, "_SM_", 4); + ep.signature = SMBIOS_SIGNATURE; ep.length = 0x1f; ep.smbios_major_version = 2; ep.smbios_minor_version = 4; diff --git a/qemu/roms/seabios/src/fw/smm.c b/qemu/roms/seabios/src/fw/smm.c index 6cb484e7e..8f042ee4d 100644 --- a/qemu/roms/seabios/src/fw/smm.c +++ b/qemu/roms/seabios/src/fw/smm.c @@ -64,11 +64,11 @@ handle_smi(u16 cs) return; u8 cmd = inb(PORT_SMI_CMD); struct smm_layout *smm = MAKE_FLATPTR(cs, 0); + u32 rev = smm->cpu.i32.smm_rev & SMM_REV_MASK; dprintf(DEBUG_HDL_smi, "handle_smi cmd=%x smbase=%p\n", cmd, smm); if (smm == (void*)BUILD_SMM_INIT_ADDR) { // relocate SMBASE to 0xa0000 - u32 rev = smm->cpu.i32.smm_rev & SMM_REV_MASK; if (rev == SMM_REV_I32) { smm->cpu.i32.smm_base = BUILD_SMM_ADDR; } else if (rev == SMM_REV_I64) { @@ -92,7 +92,7 @@ handle_smi(u16 cs) } if (CONFIG_CALL32_SMM && cmd == CALL32SMM_CMDID) { - if (smm->cpu.i32.smm_rev == SMM_REV_I32) { + if (rev == SMM_REV_I32) { u32 regs[8]; memcpy(regs, &smm->cpu.i32.eax, sizeof(regs)); if (smm->cpu.i32.ecx == CALL32SMM_ENTERID) { @@ -107,7 +107,7 @@ handle_smi(u16 cs) memcpy(&smm->cpu.i32.eax, regs, sizeof(regs)); smm->cpu.i32.eip = regs[3]; } - } else if (smm->cpu.i64.smm_rev == SMM_REV_I64) { + } else if (rev == SMM_REV_I64) { u64 regs[8]; memcpy(regs, &smm->cpu.i64.rdi, sizeof(regs)); if ((u32)smm->cpu.i64.rcx == CALL32SMM_ENTERID) { @@ -184,7 +184,7 @@ static void piix4_apmc_smm_setup(int isabdf, int i440_bdf) /* enable SMI generation */ value = inl(acpi_pm_base + PIIX_PMIO_GLBCTL); - outl(acpi_pm_base + PIIX_PMIO_GLBCTL, value | PIIX_PMIO_GLBCTL_SMI_EN); + outl(value | PIIX_PMIO_GLBCTL_SMI_EN, acpi_pm_base + PIIX_PMIO_GLBCTL); smm_relocate_and_restore(); diff --git a/qemu/roms/seabios/src/fw/smp.c b/qemu/roms/seabios/src/fw/smp.c index a466ea6e9..579acdbd0 100644 --- a/qemu/roms/seabios/src/fw/smp.c +++ b/qemu/roms/seabios/src/fw/smp.c @@ -52,9 +52,6 @@ handle_smp(void) if (!CONFIG_QEMU) return; - // Enable CPU caching - setcr0(getcr0() & ~(CR0_CD|CR0_NW)); - // Detect apic_id u32 eax, ebx, ecx, cpuid_features; cpuid(1, &eax, &ebx, &ecx, &cpuid_features); diff --git a/qemu/roms/seabios/src/fw/xen.c b/qemu/roms/seabios/src/fw/xen.c index dd8e8afd4..3f19ef2dc 100644 --- a/qemu/roms/seabios/src/fw/xen.c +++ b/qemu/roms/seabios/src/fw/xen.c @@ -4,16 +4,17 @@ // // This file may be distributed under the terms of the GNU LGPLv3 license. -#include "config.h" +#include "config.h" // CONFIG_XEN +#include "e820map.h" // e820_add #include "hw/serialio.h" // DebugOutputPort #include "malloc.h" // memalign_high -#include "memmap.h" // add_e820 +#include "memmap.h" // PAGE_SIZE #include "output.h" // dprintf #include "paravirt.h" // PlatformRunningOn #include "string.h" // memcpy #include "util.h" // copy_acpi_rsdp #include "x86.h" // cpuid -#include "xen.h" +#include "xen.h" // xen_extraversion_t #define INFO_PHYSICAL_ADDRESS 0x00001000 @@ -142,6 +143,6 @@ void xen_ramsize_preinit(void) for (i = 0; i < info->e820_nr; i++) { struct e820entry *e = &e820[i]; - add_e820(e->start, e->size, e->type); + e820_add(e->start, e->size, e->type); } } diff --git a/qemu/roms/seabios/src/hw/ahci.c b/qemu/roms/seabios/src/hw/ahci.c index 3193d81a6..83b747cb2 100644 --- a/qemu/roms/seabios/src/hw/ahci.c +++ b/qemu/roms/seabios/src/hw/ahci.c @@ -213,7 +213,7 @@ static int ahci_command(struct ahci_port_s *port_gf, int iswrite, int isatapi, #define CDROM_CDB_SIZE 12 -int ahci_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +int ahci_atapi_process_op(struct disk_op_s *op) { if (! CONFIG_AHCI) return 0; @@ -221,15 +221,14 @@ int ahci_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) struct ahci_port_s *port_gf = container_of( op->drive_gf, struct ahci_port_s, drive); struct ahci_cmd_s *cmd = port_gf->cmd; - u8 *atapi = cdbcmd; - int i, rc; + if (op->command == CMD_WRITE || op->command == CMD_FORMAT) + return DISK_RET_EWRITEPROTECT; + int blocksize = scsi_fill_cmd(op, cmd->atapi, CDROM_CDB_SIZE); + if (blocksize < 0) + return default_process_op(op); sata_prep_atapi(&cmd->fis, blocksize); - for (i = 0; i < CDROM_CDB_SIZE; i++) { - cmd->atapi[i] = atapi[i]; - } - rc = ahci_command(port_gf, 0, 1, op->buf_fl, - op->count * blocksize); + int rc = ahci_command(port_gf, 0, 1, op->buf_fl, op->count * blocksize); if (rc < 0) return DISK_RET_EBADTRACK; return DISK_RET_SUCCESS; @@ -296,8 +295,8 @@ ahci_disk_readwrite(struct disk_op_s *op, int iswrite) } // command demuxer -int VISIBLE32FLAT -process_ahci_op(struct disk_op_s *op) +int +ahci_process_op(struct disk_op_s *op) { if (!CONFIG_AHCI) return 0; @@ -306,15 +305,8 @@ process_ahci_op(struct disk_op_s *op) return ahci_disk_readwrite(op, 0); case CMD_WRITE: return ahci_disk_readwrite(op, 1); - case CMD_FORMAT: - case CMD_RESET: - case CMD_ISREADY: - case CMD_VERIFY: - case CMD_SEEK: - return DISK_RET_SUCCESS; default: - dprintf(1, "AHCI: unknown disk command %d\n", op->command); - return DISK_RET_EPARAM; + return default_process_op(op); } } @@ -405,6 +397,14 @@ static struct ahci_port_s* ahci_port_realloc(struct ahci_port_s *port) port->list = memalign_high(1024, 1024); port->fis = memalign_high(256, 256); port->cmd = memalign_high(256, 256); + if (!port->list || !port->fis || !port->cmd) { + warn_noalloc(); + free(port->list); + free(port->fis); + free(port->cmd); + free(port); + return NULL; + } ahci_port_writel(port->ctrl, port->pnr, PORT_LST_ADDR, (u32)port->list); ahci_port_writel(port->ctrl, port->pnr, PORT_FIS_ADDR, (u32)port->fis); diff --git a/qemu/roms/seabios/src/hw/ahci.h b/qemu/roms/seabios/src/hw/ahci.h index c8c755a3f..fa11d6619 100644 --- a/qemu/roms/seabios/src/hw/ahci.h +++ b/qemu/roms/seabios/src/hw/ahci.h @@ -83,8 +83,8 @@ struct ahci_port_s { }; void ahci_setup(void); -int process_ahci_op(struct disk_op_s *op); -int ahci_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +int ahci_process_op(struct disk_op_s *op); +int ahci_atapi_process_op(struct disk_op_s *op); #define AHCI_IRQ_ON_SG (1 << 31) #define AHCI_CMD_ATAPI (1 << 5) diff --git a/qemu/roms/seabios/src/hw/ata.c b/qemu/roms/seabios/src/hw/ata.c index d805706dd..fbbbbc1bb 100644 --- a/qemu/roms/seabios/src/hw/ata.c +++ b/qemu/roms/seabios/src/hw/ata.c @@ -552,7 +552,7 @@ ata_readwrite(struct disk_op_s *op, int iswrite) // 16bit command demuxer for ATA harddrives. int -process_ata_op(struct disk_op_s *op) +ata_process_op(struct disk_op_s *op) { if (!CONFIG_ATA) return 0; @@ -569,12 +569,8 @@ process_ata_op(struct disk_op_s *op) return DISK_RET_SUCCESS; case CMD_ISREADY: return isready(adrive_gf); - case CMD_FORMAT: - case CMD_VERIFY: - case CMD_SEEK: - return DISK_RET_SUCCESS; default: - return DISK_RET_EPARAM; + return default_process_op(op); } } @@ -587,11 +583,18 @@ process_ata_op(struct disk_op_s *op) // Low-level atapi command transmit function. int -atapi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +ata_atapi_process_op(struct disk_op_s *op) { if (! CONFIG_ATA) return 0; + if (op->command == CMD_WRITE || op->command == CMD_FORMAT) + return DISK_RET_EWRITEPROTECT; + u8 cdbcmd[CDROM_CDB_SIZE]; + int blocksize = scsi_fill_cmd(op, cdbcmd, sizeof(cdbcmd)); + if (blocksize < 0) + return default_process_op(op); + struct atadrive_s *adrive_gf = container_of( op->drive_gf, struct atadrive_s, drive); struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); @@ -715,7 +718,7 @@ init_atadrive(struct atadrive_s *dummy, u16 *buffer) memset(adrive, 0, sizeof(*adrive)); adrive->chan_gf = dummy->chan_gf; adrive->slave = dummy->slave; - adrive->drive.cntl_id = adrive->chan_gf->chanid * 2 + dummy->slave; + adrive->drive.cntl_id = adrive->chan_gf->ataid * 2 + dummy->slave; adrive->drive.removable = (buffer[0] & 0x80) ? 1 : 0; return adrive; } @@ -740,7 +743,7 @@ init_drive_atapi(struct atadrive_s *dummy, u16 *buffer) char model[MAXMODEL+1]; char *desc = znprintf(MAXDESCSIZE , "DVD/CD [ata%d-%d: %s ATAPI-%d %s]" - , adrive->chan_gf->chanid, adrive->slave + , adrive->chan_gf->ataid, adrive->slave , ata_extract_model(model, MAXMODEL, buffer) , ata_extract_version(buffer) , (iscd ? "DVD/CD" : "Device")); @@ -792,7 +795,7 @@ init_drive_ata(struct atadrive_s *dummy, u16 *buffer) char model[MAXMODEL+1]; char *desc = znprintf(MAXDESCSIZE , "ata%d-%d: %s ATA-%d Hard-Disk (%u %ciBytes)" - , adrive->chan_gf->chanid, adrive->slave + , adrive->chan_gf->ataid, adrive->slave , ata_extract_model(model, MAXMODEL, buffer) , ata_extract_version(buffer) , (u32)adjsize, adjprefix); @@ -866,7 +869,7 @@ ata_detect(void *data) u8 sc = inb(iobase1+ATA_CB_SC); u8 sn = inb(iobase1+ATA_CB_SN); dprintf(6, "ata_detect ata%d-%d: sc=%x sn=%x dh=%x\n" - , chan_gf->chanid, slave, sc, sn, dh); + , chan_gf->ataid, slave, sc, sn, dh); if (sc != 0x55 || sn != 0xaa || dh != newdh) continue; @@ -913,16 +916,17 @@ ata_detect(void *data) // Initialize an ata controller and detect its drives. static void -init_controller(struct pci_device *pci, int irq +init_controller(struct pci_device *pci, int chanid, int irq , u32 port1, u32 port2, u32 master) { - static int chanid = 0; + static int ataid = 0; struct ata_channel_s *chan_gf = malloc_fseg(sizeof(*chan_gf)); if (!chan_gf) { warn_noalloc(); return; } - chan_gf->chanid = chanid++; + chan_gf->ataid = ataid++; + chan_gf->chanid = chanid; chan_gf->irq = irq; chan_gf->pci_bdf = pci ? pci->bdf : -1; chan_gf->pci_tmp = pci; @@ -930,7 +934,7 @@ init_controller(struct pci_device *pci, int irq chan_gf->iobase2 = port2; chan_gf->iomaster = master; dprintf(1, "ATA controller %d at %x/%x/%x (irq %d dev %x)\n" - , chanid, port1, port2, master, irq, chan_gf->pci_bdf); + , ataid, port1, port2, master, irq, chan_gf->pci_bdf); run_thread(ata_detect, chan_gf); } @@ -966,7 +970,7 @@ init_pciata(struct pci_device *pci, u8 prog_if) port2 = PORT_ATA1_CTRL_BASE; irq = IRQ_ATA1; } - init_controller(pci, irq, port1, port2, master); + init_controller(pci, 0, irq, port1, port2, master); if (prog_if & 4) { port1 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_2) @@ -979,7 +983,7 @@ init_pciata(struct pci_device *pci, u8 prog_if) port2 = PORT_ATA2_CTRL_BASE; irq = IRQ_ATA2; } - init_controller(pci, irq, port1, port2, master ? master + 8 : 0); + init_controller(pci, 1, irq, port1, port2, master ? master + 8 : 0); } static void @@ -1011,9 +1015,9 @@ ata_scan(void) if (CONFIG_QEMU && hlist_empty(&PCIDevices)) { // No PCI devices found - probably a QEMU "-M isapc" machine. // Try using ISA ports for ATA controllers. - init_controller(NULL, IRQ_ATA1 + init_controller(NULL, 0, IRQ_ATA1 , PORT_ATA1_CMD_BASE, PORT_ATA1_CTRL_BASE, 0); - init_controller(NULL, IRQ_ATA2 + init_controller(NULL, 1, IRQ_ATA2 , PORT_ATA2_CMD_BASE, PORT_ATA2_CTRL_BASE, 0); return; } diff --git a/qemu/roms/seabios/src/hw/ata.h b/qemu/roms/seabios/src/hw/ata.h index c73892bbe..cd14e59e9 100644 --- a/qemu/roms/seabios/src/hw/ata.h +++ b/qemu/roms/seabios/src/hw/ata.h @@ -11,6 +11,7 @@ struct ata_channel_s { u16 iomaster; u8 irq; u8 chanid; + u8 ataid; int pci_bdf; struct pci_device *pci_tmp; }; @@ -24,10 +25,9 @@ struct atadrive_s { // ata.c char *ata_extract_model(char *model, u32 size, u16 *buffer); int ata_extract_version(u16 *buffer); -int cdrom_read(struct disk_op_s *op); -int atapi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +int ata_process_op(struct disk_op_s *op); +int ata_atapi_process_op(struct disk_op_s *op); void ata_setup(void); -int process_ata_op(struct disk_op_s *op); #define PORT_ATA2_CMD_BASE 0x0170 #define PORT_ATA1_CMD_BASE 0x01f0 diff --git a/qemu/roms/seabios/src/hw/blockcmd.c b/qemu/roms/seabios/src/hw/blockcmd.c index 78c0e65f4..0725b46db 100644 --- a/qemu/roms/seabios/src/hw/blockcmd.c +++ b/qemu/roms/seabios/src/hw/blockcmd.c @@ -5,67 +5,14 @@ // // This file may be distributed under the terms of the GNU LGPLv3 license. -#include "ahci.h" // atapi_cmd_data -#include "ata.h" // atapi_cmd_data #include "biosvar.h" // GET_GLOBALFLAT #include "block.h" // struct disk_op_s #include "blockcmd.h" // struct cdb_request_sense #include "byteorder.h" // be32_to_cpu -#include "esp-scsi.h" // esp_scsi_cmd_data -#include "lsi-scsi.h" // lsi_scsi_cmd_data -#include "megasas.h" // megasas_cmd_data -#include "pvscsi.h" // pvscsi_cmd_data #include "output.h" // dprintf #include "std/disk.h" // DISK_RET_EPARAM #include "string.h" // memset -#include "usb-msc.h" // usb_cmd_data -#include "usb-uas.h" // usb_cmd_data #include "util.h" // timer_calc -#include "virtio-scsi.h" // virtio_scsi_cmd_data - -// Route command to low-level handler. -static int -cdb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) -{ - u8 type = GET_GLOBALFLAT(op->drive_gf->type); - switch (type) { - case DTYPE_ATA_ATAPI: - return atapi_cmd_data(op, cdbcmd, blocksize); - case DTYPE_USB: - return usb_cmd_data(op, cdbcmd, blocksize); - case DTYPE_UAS: - return uas_cmd_data(op, cdbcmd, blocksize); - case DTYPE_VIRTIO_SCSI: - return virtio_scsi_cmd_data(op, cdbcmd, blocksize); - case DTYPE_LSI_SCSI: - return lsi_scsi_cmd_data(op, cdbcmd, blocksize); - case DTYPE_ESP_SCSI: - return esp_scsi_cmd_data(op, cdbcmd, blocksize); - case DTYPE_MEGASAS: - return megasas_cmd_data(op, cdbcmd, blocksize); - case DTYPE_USB_32: - if (!MODESEGMENT) - return usb_cmd_data(op, cdbcmd, blocksize); - case DTYPE_UAS_32: - if (!MODESEGMENT) - return uas_cmd_data(op, cdbcmd, blocksize); - case DTYPE_PVSCSI: - if (!MODESEGMENT) - return pvscsi_cmd_data(op, cdbcmd, blocksize); - case DTYPE_AHCI_ATAPI: - if (!MODESEGMENT) - return ahci_cmd_data(op, cdbcmd, blocksize); - default: - return DISK_RET_EPARAM; - } -} - -// Determine if the command is a request to pull data from the device -int -cdb_is_read(u8 *cdbcmd, u16 blocksize) -{ - return blocksize && cdbcmd[0] != CDB_CMD_WRITE_10; -} /**************************************************************** @@ -79,9 +26,12 @@ cdb_get_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data) memset(&cmd, 0, sizeof(cmd)); cmd.command = CDB_CMD_INQUIRY; cmd.length = sizeof(*data); + op->command = CMD_SCSI; op->count = 1; op->buf_fl = data; - return cdb_cmd_data(op, &cmd, sizeof(*data)); + op->cdbcmd = &cmd; + op->blocksize = sizeof(*data); + return process_op(op); } // Request SENSE @@ -92,9 +42,12 @@ cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data) memset(&cmd, 0, sizeof(cmd)); cmd.command = CDB_CMD_REQUEST_SENSE; cmd.length = sizeof(*data); + op->command = CMD_SCSI; op->count = 1; op->buf_fl = data; - return cdb_cmd_data(op, &cmd, sizeof(*data)); + op->cdbcmd = &cmd; + op->blocksize = sizeof(*data); + return process_op(op); } // Test unit ready @@ -104,9 +57,12 @@ cdb_test_unit_ready(struct disk_op_s *op) struct cdb_request_sense cmd; memset(&cmd, 0, sizeof(cmd)); cmd.command = CDB_CMD_TEST_UNIT_READY; + op->command = CMD_SCSI; op->count = 0; op->buf_fl = NULL; - return cdb_cmd_data(op, &cmd, 0); + op->cdbcmd = &cmd; + op->blocksize = 0; + return process_op(op); } // Request capacity @@ -116,9 +72,12 @@ cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data) struct cdb_read_capacity cmd; memset(&cmd, 0, sizeof(cmd)); cmd.command = CDB_CMD_READ_CAPACITY; + op->command = CMD_SCSI; op->count = 1; op->buf_fl = data; - return cdb_cmd_data(op, &cmd, sizeof(*data)); + op->cdbcmd = &cmd; + op->blocksize = sizeof(*data); + return process_op(op); } // Mode sense, geometry page. @@ -131,33 +90,12 @@ cdb_mode_sense_geom(struct disk_op_s *op, struct cdbres_mode_sense_geom *data) cmd.flags = 8; /* DBD */ cmd.page = MODE_PAGE_HD_GEOMETRY; cmd.count = cpu_to_be16(sizeof(*data)); + op->command = CMD_SCSI; op->count = 1; op->buf_fl = data; - return cdb_cmd_data(op, &cmd, sizeof(*data)); -} - -// Read sectors. -static int -cdb_read(struct disk_op_s *op) -{ - struct cdb_rwdata_10 cmd; - memset(&cmd, 0, sizeof(cmd)); - cmd.command = CDB_CMD_READ_10; - cmd.lba = cpu_to_be32(op->lba); - cmd.count = cpu_to_be16(op->count); - return cdb_cmd_data(op, &cmd, GET_GLOBALFLAT(op->drive_gf->blksize)); -} - -// Write sectors. -static int -cdb_write(struct disk_op_s *op) -{ - struct cdb_rwdata_10 cmd; - memset(&cmd, 0, sizeof(cmd)); - cmd.command = CDB_CMD_WRITE_10; - cmd.lba = cpu_to_be32(op->lba); - cmd.count = cpu_to_be16(op->count); - return cdb_cmd_data(op, &cmd, GET_GLOBALFLAT(op->drive_gf->blksize)); + op->cdbcmd = &cmd; + op->blocksize = sizeof(*data); + return process_op(op); } @@ -165,25 +103,36 @@ cdb_write(struct disk_op_s *op) * Main SCSI commands ****************************************************************/ -int VISIBLE32FLAT -scsi_process_op(struct disk_op_s *op) +// Create a scsi command request from a disk_op_s request +int +scsi_fill_cmd(struct disk_op_s *op, void *cdbcmd, int maxcdb) { switch (op->command) { case CMD_READ: - return cdb_read(op); - case CMD_WRITE: - return cdb_write(op); - case CMD_FORMAT: - case CMD_RESET: - case CMD_ISREADY: - case CMD_VERIFY: - case CMD_SEEK: - return DISK_RET_SUCCESS; + case CMD_WRITE: ; + struct cdb_rwdata_10 *cmd = cdbcmd; + memset(cmd, 0, maxcdb); + cmd->command = (op->command == CMD_READ ? CDB_CMD_READ_10 + : CDB_CMD_WRITE_10); + cmd->lba = cpu_to_be32(op->lba); + cmd->count = cpu_to_be16(op->count); + return GET_GLOBALFLAT(op->drive_gf->blksize); + case CMD_SCSI: + memcpy(cdbcmd, op->cdbcmd, maxcdb); + return op->blocksize; default: - return DISK_RET_EPARAM; + return -1; } } +// Determine if the command is a request to pull data from the device +int +scsi_is_read(struct disk_op_s *op) +{ + return op->command == CMD_READ || (op->command == CMD_SCSI && op->blocksize); +} + +// Check if a SCSI device is ready to receive commands int scsi_is_ready(struct disk_op_s *op) { @@ -219,7 +168,7 @@ scsi_is_ready(struct disk_op_s *op) if (sense.asc == 0x04 && sense.ascq == 0x01 && !in_progress) { /* IN PROGRESS OF BECOMING READY */ - printf("Waiting for device to detect medium... "); + dprintf(1, "Waiting for device to detect medium... "); /* Allow 30 seconds more */ end = timer_calc(30000); in_progress = 1; diff --git a/qemu/roms/seabios/src/hw/blockcmd.h b/qemu/roms/seabios/src/hw/blockcmd.h index df12a6d42..b543f85eb 100644 --- a/qemu/roms/seabios/src/hw/blockcmd.h +++ b/qemu/roms/seabios/src/hw/blockcmd.h @@ -100,9 +100,9 @@ struct cdbres_mode_sense_geom { } PACKED; // blockcmd.c -int cdb_is_read(u8 *cdbcmd, u16 blocksize); struct disk_op_s; -int scsi_process_op(struct disk_op_s *op); +int scsi_fill_cmd(struct disk_op_s *op, void *cdbcmd, int maxcdb); +int scsi_is_read(struct disk_op_s *op); int scsi_is_ready(struct disk_op_s *op); struct drive_s; int scsi_drive_setup(struct drive_s *drive, const char *s, int prio); diff --git a/qemu/roms/seabios/src/hw/esp-scsi.c b/qemu/roms/seabios/src/hw/esp-scsi.c index 33cc44986..d4e47e3c5 100644 --- a/qemu/roms/seabios/src/hw/esp-scsi.c +++ b/qemu/roms/seabios/src/hw/esp-scsi.c @@ -76,10 +76,19 @@ esp_scsi_dma(u32 iobase, u32 buf, u32 len, int read) outb(read ? 0x83 : 0x03, iobase + ESP_DMA_CMD); } -static int -esp_scsi_cmd(struct esp_lun_s *llun_gf, struct disk_op_s *op, - u8 *cdbcmd, u16 target, u16 lun, u16 blocksize) +int +esp_scsi_process_op(struct disk_op_s *op) { + if (!CONFIG_ESP_SCSI) + return DISK_RET_EBADTRACK; + struct esp_lun_s *llun_gf = + container_of(op->drive_gf, struct esp_lun_s, drive); + u16 target = GET_GLOBALFLAT(llun_gf->target); + u16 lun = GET_GLOBALFLAT(llun_gf->lun); + u8 cdbcmd[16]; + int blocksize = scsi_fill_cmd(op, cdbcmd, sizeof(cdbcmd)); + if (blocksize < 0) + return default_process_op(op); u32 iobase = GET_GLOBALFLAT(llun_gf->iobase); int i, state; u8 status; @@ -113,8 +122,7 @@ esp_scsi_cmd(struct esp_lun_s *llun_gf, struct disk_op_s *op, if (op->count && blocksize) { /* Data phase. */ u32 count = (u32)op->count * blocksize; - esp_scsi_dma(iobase, (u32)op->buf_fl, count, - cdb_is_read(cdbcmd, blocksize)); + esp_scsi_dma(iobase, (u32)op->buf_fl, count, scsi_is_read(op)); outb(ESP_CMD_TI | ESP_CMD_DMA, iobase + ESP_CMD); continue; } @@ -144,21 +152,6 @@ esp_scsi_cmd(struct esp_lun_s *llun_gf, struct disk_op_s *op, return DISK_RET_EBADTRACK; } -int -esp_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) -{ - if (!CONFIG_ESP_SCSI) - return DISK_RET_EBADTRACK; - - struct esp_lun_s *llun_gf = - container_of(op->drive_gf, struct esp_lun_s, drive); - - return esp_scsi_cmd(llun_gf, op, cdbcmd, - GET_GLOBALFLAT(llun_gf->target), - GET_GLOBALFLAT(llun_gf->lun), - blocksize); -} - static int esp_scsi_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun) { diff --git a/qemu/roms/seabios/src/hw/esp-scsi.h b/qemu/roms/seabios/src/hw/esp-scsi.h index dc555f395..0616d14b1 100644 --- a/qemu/roms/seabios/src/hw/esp-scsi.h +++ b/qemu/roms/seabios/src/hw/esp-scsi.h @@ -2,7 +2,7 @@ #define __ESP_SCSI_H struct disk_op_s; -int esp_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +int esp_scsi_process_op(struct disk_op_s *op); void esp_scsi_setup(void); #endif /* __ESP_SCSI_H */ diff --git a/qemu/roms/seabios/src/hw/floppy.c b/qemu/roms/seabios/src/hw/floppy.c index d60362a34..a14f7e093 100644 --- a/qemu/roms/seabios/src/hw/floppy.c +++ b/qemu/roms/seabios/src/hw/floppy.c @@ -613,7 +613,7 @@ floppy_format(struct disk_op_s *op) } int -process_floppy_op(struct disk_op_s *op) +floppy_process_op(struct disk_op_s *op) { if (!CONFIG_FLOPPY) return 0; diff --git a/qemu/roms/seabios/src/hw/lsi-scsi.c b/qemu/roms/seabios/src/hw/lsi-scsi.c index b1d6bbf4b..ad3352886 100644 --- a/qemu/roms/seabios/src/hw/lsi-scsi.c +++ b/qemu/roms/seabios/src/hw/lsi-scsi.c @@ -50,12 +50,21 @@ struct lsi_lun_s { u8 lun; }; -static int -lsi_scsi_cmd(struct lsi_lun_s *llun_gf, struct disk_op_s *op, - void *cdbcmd, u16 target, u16 lun, u16 blocksize) +int +lsi_scsi_process_op(struct disk_op_s *op) { + if (!CONFIG_LSI_SCSI) + return DISK_RET_EBADTRACK; + struct lsi_lun_s *llun_gf = + container_of(op->drive_gf, struct lsi_lun_s, drive); + u16 target = GET_GLOBALFLAT(llun_gf->target); + u16 lun = GET_GLOBALFLAT(llun_gf->lun); + u8 cdbcmd[16]; + int blocksize = scsi_fill_cmd(op, cdbcmd, sizeof(cdbcmd)); + if (blocksize < 0) + return default_process_op(op); u32 iobase = GET_GLOBALFLAT(llun_gf->iobase); - u32 dma = ((cdb_is_read(cdbcmd, blocksize) ? 0x01000000 : 0x00000000) | + u32 dma = ((scsi_is_read(op) ? 0x01000000 : 0x00000000) | (op->count * blocksize)); u8 msgout[] = { 0x80 | lun, // select lun @@ -122,21 +131,6 @@ fail: return DISK_RET_EBADTRACK; } -int -lsi_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) -{ - if (!CONFIG_LSI_SCSI) - return DISK_RET_EBADTRACK; - - struct lsi_lun_s *llun_gf = - container_of(op->drive_gf, struct lsi_lun_s, drive); - - return lsi_scsi_cmd(llun_gf, op, cdbcmd, - GET_GLOBALFLAT(llun_gf->target), - GET_GLOBALFLAT(llun_gf->lun), - blocksize); -} - static int lsi_scsi_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun) { diff --git a/qemu/roms/seabios/src/hw/lsi-scsi.h b/qemu/roms/seabios/src/hw/lsi-scsi.h index 9c5a9b212..6baf4a162 100644 --- a/qemu/roms/seabios/src/hw/lsi-scsi.h +++ b/qemu/roms/seabios/src/hw/lsi-scsi.h @@ -2,7 +2,7 @@ #define __LSI_SCSI_H struct disk_op_s; -int lsi_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +int lsi_scsi_process_op(struct disk_op_s *op); void lsi_scsi_setup(void); #endif /* __LSI_SCSI_H */ diff --git a/qemu/roms/seabios/src/hw/megasas.c b/qemu/roms/seabios/src/hw/megasas.c index b2a65e48b..cb1a2a653 100644 --- a/qemu/roms/seabios/src/hw/megasas.c +++ b/qemu/roms/seabios/src/hw/megasas.c @@ -157,18 +157,20 @@ static int megasas_fire_cmd(u16 pci_id, u32 ioaddr, } int -megasas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +megasas_process_op(struct disk_op_s *op) { + if (!CONFIG_MEGASAS) + return DISK_RET_EBADTRACK; + u8 cdb[16]; + int blocksize = scsi_fill_cmd(op, cdb, sizeof(cdb)); + if (blocksize < 0) + return default_process_op(op); struct megasas_lun_s *mlun_gf = container_of(op->drive_gf, struct megasas_lun_s, drive); - u8 *cdb = cdbcmd; struct megasas_cmd_frame *frame = GET_GLOBALFLAT(mlun_gf->frame); u16 pci_id = GET_GLOBALFLAT(mlun_gf->pci_id); int i; - if (!CONFIG_MEGASAS) - return DISK_RET_EBADTRACK; - memset_fl(frame, 0, sizeof(*frame)); SET_LOWFLAT(frame->cmd, MFI_CMD_LD_SCSI_IO); SET_LOWFLAT(frame->cmd_status, 0xFF); @@ -241,7 +243,10 @@ static void megasas_scan_target(struct pci_device *pci, u32 iobase) { struct mfi_ld_list_s ld_list; struct megasas_cmd_frame *frame = memalign_tmp(256, sizeof(*frame)); - int i; + if (!frame) { + warn_noalloc(); + return; + } memset(&ld_list, 0, sizeof(ld_list)); memset_fl(frame, 0, sizeof(*frame)); @@ -258,6 +263,7 @@ static void megasas_scan_target(struct pci_device *pci, u32 iobase) if (megasas_fire_cmd(pci->device, iobase, frame) == 0) { dprintf(2, "%d LD found\n", ld_list.count); + int i; for (i = 0; i < ld_list.count; i++) { dprintf(2, "LD %d:%d state 0x%x\n", ld_list.lds[i].target, ld_list.lds[i].lun, @@ -295,9 +301,9 @@ static int megasas_transition_to_ready(struct pci_device *pci, u32 ioaddr) pci->device == PCI_DEVICE_ID_LSI_SAS2008 || pci->device == PCI_DEVICE_ID_LSI_SAS2208 || pci->device == PCI_DEVICE_ID_LSI_SAS3108) { - outl(ioaddr + MFI_DB, mfi_flags); + outl(mfi_flags, ioaddr + MFI_DB); } else { - outl(ioaddr + MFI_IDB, mfi_flags); + outl(mfi_flags, ioaddr + MFI_IDB); } break; case MFI_STATE_OPERATIONAL: @@ -306,7 +312,7 @@ static int megasas_transition_to_ready(struct pci_device *pci, u32 ioaddr) pci->device == PCI_DEVICE_ID_LSI_SAS2008 || pci->device == PCI_DEVICE_ID_LSI_SAS2208 || pci->device == PCI_DEVICE_ID_LSI_SAS3108) { - outl(ioaddr + MFI_DB, mfi_flags); + outl(mfi_flags, ioaddr + MFI_DB); if (pci->device == PCI_DEVICE_ID_LSI_SAS2208 || pci->device == PCI_DEVICE_ID_LSI_SAS3108) { int j = 0; @@ -321,7 +327,7 @@ static int megasas_transition_to_ready(struct pci_device *pci, u32 ioaddr) } } } else { - outw(ioaddr + MFI_IDB, mfi_flags); + outl(mfi_flags, ioaddr + MFI_IDB); } break; case MFI_STATE_READY: diff --git a/qemu/roms/seabios/src/hw/megasas.h b/qemu/roms/seabios/src/hw/megasas.h index 124042e1c..ed0e4f096 100644 --- a/qemu/roms/seabios/src/hw/megasas.h +++ b/qemu/roms/seabios/src/hw/megasas.h @@ -2,7 +2,7 @@ #define __MEGASAS_H struct disk_op_s; -int megasas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +int megasas_process_op(struct disk_op_s *op); void megasas_setup(void); #endif /* __MEGASAS_H */ diff --git a/qemu/roms/seabios/src/hw/pci.c b/qemu/roms/seabios/src/hw/pci.c index 0379b558e..a241d0675 100644 --- a/qemu/roms/seabios/src/hw/pci.c +++ b/qemu/roms/seabios/src/hw/pci.c @@ -221,16 +221,21 @@ pci_find_init_device(const struct pci_device_id *ids, void *arg) return NULL; } -u8 pci_find_capability(struct pci_device *pci, u8 cap_id) +u8 pci_find_capability(struct pci_device *pci, u8 cap_id, u8 cap) { int i; - u8 cap; u16 status = pci_config_readw(pci->bdf, PCI_STATUS); if (!(status & PCI_STATUS_CAP_LIST)) return 0; - cap = pci_config_readb(pci->bdf, PCI_CAPABILITY_LIST); + if (cap == 0) { + /* find first */ + cap = pci_config_readb(pci->bdf, PCI_CAPABILITY_LIST); + } else { + /* find next */ + cap = pci_config_readb(pci->bdf, cap + PCI_CAP_LIST_NEXT); + } for (i = 0; cap && i <= 0xff; i++) { if (pci_config_readb(pci->bdf, cap + PCI_CAP_LIST_ID) == cap_id) return cap; diff --git a/qemu/roms/seabios/src/hw/pci.h b/qemu/roms/seabios/src/hw/pci.h index 0aaa84c1a..fc5e7b9bf 100644 --- a/qemu/roms/seabios/src/hw/pci.h +++ b/qemu/roms/seabios/src/hw/pci.h @@ -123,7 +123,7 @@ int pci_init_device(const struct pci_device_id *ids , struct pci_device *pci, void *arg); struct pci_device *pci_find_init_device(const struct pci_device_id *ids , void *arg); -u8 pci_find_capability(struct pci_device *pci, u8 cap_id); +u8 pci_find_capability(struct pci_device *pci, u8 cap_id, u8 cap); int pci_bridge_has_region(struct pci_device *pci, enum pci_region_type region_type); void pci_reboot(void); diff --git a/qemu/roms/seabios/src/hw/pci_ids.h b/qemu/roms/seabios/src/hw/pci_ids.h index 1cd4f7269..cdf9b3cbc 100644 --- a/qemu/roms/seabios/src/hw/pci_ids.h +++ b/qemu/roms/seabios/src/hw/pci_ids.h @@ -2616,8 +2616,12 @@ #define PCI_DEVICE_ID_RME_DIGI32_8 0x9898 #define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 -#define PCI_DEVICE_ID_VIRTIO_BLK 0x1001 -#define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004 +/* virtio 0.9.5 ids (legacy/transitional devices) */ +#define PCI_DEVICE_ID_VIRTIO_BLK_09 0x1001 +#define PCI_DEVICE_ID_VIRTIO_SCSI_09 0x1004 +/* virtio 1.0 ids (modern devices) */ +#define PCI_DEVICE_ID_VIRTIO_BLK_10 0x1042 +#define PCI_DEVICE_ID_VIRTIO_SCSI_10 0x1048 #define PCI_VENDOR_ID_VMWARE 0x15ad #define PCI_DEVICE_ID_VMWARE_PVSCSI 0x07C0 diff --git a/qemu/roms/seabios/src/hw/pic.c b/qemu/roms/seabios/src/hw/pic.c index 6ff696765..d8b9764c7 100644 --- a/qemu/roms/seabios/src/hw/pic.c +++ b/qemu/roms/seabios/src/hw/pic.c @@ -13,12 +13,16 @@ u16 pic_irqmask_read(void) { + if (!CONFIG_HARDWARE_IRQ) + return 0; return inb(PORT_PIC1_DATA) | (inb(PORT_PIC2_DATA) << 8); } void pic_irqmask_write(u16 mask) { + if (!CONFIG_HARDWARE_IRQ) + return; outb(mask, PORT_PIC1_DATA); outb(mask >> 8, PORT_PIC2_DATA); } @@ -26,6 +30,8 @@ pic_irqmask_write(u16 mask) void pic_irqmask_mask(u16 off, u16 on) { + if (!CONFIG_HARDWARE_IRQ) + return; u8 pic1off = off, pic1on = on, pic2off = off>>8, pic2on = on>>8; outb((inb(PORT_PIC1_DATA) & ~pic1off) | pic1on, PORT_PIC1_DATA); outb((inb(PORT_PIC2_DATA) & ~pic2off) | pic2on, PORT_PIC2_DATA); @@ -34,6 +40,8 @@ pic_irqmask_mask(u16 off, u16 on) void pic_reset(u8 irq0, u8 irq8) { + if (!CONFIG_HARDWARE_IRQ) + return; // Send ICW1 (select OCW1 + will send ICW4) outb(0x11, PORT_PIC1_CMD); outb(0x11, PORT_PIC2_CMD); @@ -60,6 +68,8 @@ pic_setup(void) void enable_hwirq(int hwirq, struct segoff_s func) { + if (!CONFIG_HARDWARE_IRQ) + return; pic_irqmask_mask(1 << hwirq, 0); int vector; if (hwirq < 8) @@ -72,6 +82,8 @@ enable_hwirq(int hwirq, struct segoff_s func) static u8 pic_isr1_read(void) { + if (!CONFIG_HARDWARE_IRQ) + return 0; // 0x0b == select OCW1 + read ISR outb(0x0b, PORT_PIC1_CMD); return inb(PORT_PIC1_CMD); @@ -80,6 +92,8 @@ pic_isr1_read(void) static u8 pic_isr2_read(void) { + if (!CONFIG_HARDWARE_IRQ) + return 0; // 0x0b == select OCW1 + read ISR outb(0x0b, PORT_PIC2_CMD); return inb(PORT_PIC2_CMD); diff --git a/qemu/roms/seabios/src/hw/pic.h b/qemu/roms/seabios/src/hw/pic.h index 6947b6e81..f2d9f6130 100644 --- a/qemu/roms/seabios/src/hw/pic.h +++ b/qemu/roms/seabios/src/hw/pic.h @@ -34,6 +34,8 @@ static inline void pic_eoi1(void) { + if (!CONFIG_HARDWARE_IRQ) + return; // Send eoi (select OCW2 + eoi) outb(0x20, PORT_PIC1_CMD); } @@ -41,6 +43,8 @@ pic_eoi1(void) static inline void pic_eoi2(void) { + if (!CONFIG_HARDWARE_IRQ) + return; // Send eoi (select OCW2 + eoi) outb(0x20, PORT_PIC2_CMD); pic_eoi1(); diff --git a/qemu/roms/seabios/src/hw/ps2port.c b/qemu/roms/seabios/src/hw/ps2port.c index 04995c881..d5504f71e 100644 --- a/qemu/roms/seabios/src/hw/ps2port.c +++ b/qemu/roms/seabios/src/hw/ps2port.c @@ -210,7 +210,7 @@ ps2_sendbyte(int aux, u8 command, int timeout) return 0; } -u8 Ps2ctr VARLOW; +u8 Ps2ctr VARLOW = I8042_CTR_KBDDIS | I8042_CTR_AUXDIS; static int __ps2_command(int aux, int command, u8 *param) @@ -232,6 +232,7 @@ __ps2_command(int aux, int command, u8 *param) yield(); // Enable port command is being sent to. + SET_LOW(Ps2ctr, newctr); if (aux) newctr &= ~I8042_CTR_AUXDIS; else @@ -240,8 +241,8 @@ __ps2_command(int aux, int command, u8 *param) if (ret) goto fail; - if (command == ATKBD_CMD_RESET_BAT) { - // Reset is special wrt timeouts and bytes received. + if ((u8)command == (u8)ATKBD_CMD_RESET_BAT) { + // Reset is special wrt timeouts. // Send command. ret = ps2_sendbyte(aux, command, 1000); @@ -253,11 +254,12 @@ __ps2_command(int aux, int command, u8 *param) if (ret < 0) goto fail; param[0] = ret; - ret = ps2_recvbyte(aux, 0, 100); - if (ret < 0) - // Some devices only respond with one byte on reset. - ret = 0; - param[1] = ret; + if (receive > 1) { + ret = ps2_recvbyte(aux, 0, 500); + if (ret < 0) + goto fail; + param[1] = ret; + } } else if (command == ATKBD_CMD_GETID) { // Getid is special wrt bytes received. @@ -308,6 +310,7 @@ __ps2_command(int aux, int command, u8 *param) fail: // Restore interrupts and keyboard/mouse. + SET_LOW(Ps2ctr, ps2ctr); ret2 = i8042_command(I8042_CMD_CTL_WCTR, &ps2ctr); if (ret2) return ret2; @@ -343,7 +346,8 @@ ps2_mouse_command(int command, u8 *param) if (command == PSMOUSE_CMD_ENABLE || command == PSMOUSE_CMD_DISABLE) { u8 ps2ctr = GET_LOW(Ps2ctr); if (command == PSMOUSE_CMD_ENABLE) - ps2ctr = (ps2ctr | I8042_CTR_AUXINT) & ~I8042_CTR_AUXDIS; + ps2ctr = ((ps2ctr | (CONFIG_HARDWARE_IRQ ? I8042_CTR_AUXINT : 0)) + & ~I8042_CTR_AUXDIS); else ps2ctr = (ps2ctr | I8042_CTR_AUXDIS) & ~I8042_CTR_AUXINT; SET_LOW(Ps2ctr, ps2ctr); @@ -414,6 +418,31 @@ done: pic_eoi1(); } +// Check for ps2 activity on machines without hardware irqs +void +ps2_check_event(void) +{ + if (! CONFIG_PS2PORT || CONFIG_HARDWARE_IRQ) + return; + u8 ps2ctr = GET_LOW(Ps2ctr); + if ((ps2ctr & (I8042_CTR_KBDDIS|I8042_CTR_AUXDIS)) + == (I8042_CTR_KBDDIS|I8042_CTR_AUXDIS)) + return; + for (;;) { + u8 status = inb(PORT_PS2_STATUS); + if (!(status & I8042_STR_OBF)) + break; + u8 data = inb(PORT_PS2_DATA); + if (status & I8042_STR_AUXDATA) { + if (!(ps2ctr & I8042_CTR_AUXDIS)) + process_mouse(data); + } else { + if (!(ps2ctr & I8042_CTR_KBDDIS)) + process_key(data); + } + } +} + /**************************************************************** * Setup @@ -446,9 +475,6 @@ ps2_keyboard_setup(void *data) return; } - // Disable keyboard and mouse events. - SET_LOW(Ps2ctr, I8042_CTR_KBDDIS | I8042_CTR_AUXDIS); - /* ------------------- keyboard side ------------------------*/ /* reset keyboard and self test (keyboard side) */ @@ -482,7 +508,8 @@ ps2_keyboard_setup(void *data) return; // Keyboard Mode: disable mouse, scan code convert, enable kbd IRQ - SET_LOW(Ps2ctr, I8042_CTR_AUXDIS | I8042_CTR_XLATE | I8042_CTR_KBDINT); + Ps2ctr = (I8042_CTR_AUXDIS | I8042_CTR_XLATE + | (CONFIG_HARDWARE_IRQ ? I8042_CTR_KBDINT : 0)); /* Enable keyboard */ ret = ps2_kbd_command(ATKBD_CMD_ENABLE, NULL); diff --git a/qemu/roms/seabios/src/hw/ps2port.h b/qemu/roms/seabios/src/hw/ps2port.h index e5d9014b7..1338406ac 100644 --- a/qemu/roms/seabios/src/hw/ps2port.h +++ b/qemu/roms/seabios/src/hw/ps2port.h @@ -26,7 +26,7 @@ #define ATKBD_CMD_GETID 0x02f2 #define ATKBD_CMD_ENABLE 0x00f4 #define ATKBD_CMD_RESET_DIS 0x00f5 -#define ATKBD_CMD_RESET_BAT 0x02ff +#define ATKBD_CMD_RESET_BAT 0x01ff // Mouse commands #define PSMOUSE_CMD_SETSCALE11 0x00e6 @@ -61,6 +61,7 @@ void i8042_reboot(void); int ps2_kbd_command(int command, u8 *param); int ps2_mouse_command(int command, u8 *param); +void ps2_check_event(void); void ps2port_setup(void); #endif // ps2port.h diff --git a/qemu/roms/seabios/src/hw/pvscsi.c b/qemu/roms/seabios/src/hw/pvscsi.c index 601a551db..fa20efef7 100644 --- a/qemu/roms/seabios/src/hw/pvscsi.c +++ b/qemu/roms/seabios/src/hw/pvscsi.c @@ -11,6 +11,7 @@ #include "blockcmd.h" // scsi_drive_setup #include "config.h" // CONFIG_* #include "malloc.h" // free +#include "memmap.h" // PAGE_SHIFT, virt_to_phys #include "output.h" // dprintf #include "pci.h" // foreachpci #include "pci_ids.h" // PCI_DEVICE_ID_VMWARE_PVSCSI @@ -19,7 +20,6 @@ #include "std/disk.h" // DISK_RET_SUCCESS #include "string.h" // memset #include "util.h" // usleep -#include "virtio-ring.h" // PAGE_SHIFT, virt_to_phys #include "x86.h" // writel #define MASK(n) ((1 << (n)) - 1) @@ -197,29 +197,6 @@ pvscsi_init_rings(void *iobase, struct pvscsi_ring_dsc_s **ring_dsc) *ring_dsc = dsc; } -static void pvscsi_fill_req(struct PVSCSIRingsState *s, - struct PVSCSIRingReqDesc *req, - u16 target, u16 lun, void *cdbcmd, u16 blocksize, - struct disk_op_s *op) -{ - req->bus = 0; - req->target = target; - memset(req->lun, 0, sizeof(req->lun)); - req->lun[1] = lun; - req->senseLen = 0; - req->senseAddr = 0; - req->cdbLen = 16; - req->vcpuHint = 0; - memcpy(req->cdb, cdbcmd, 16); - req->tag = SIMPLE_QUEUE_TAG; - req->flags = cdb_is_read(cdbcmd, blocksize) ? - PVSCSI_FLAG_CMD_DIR_TOHOST : PVSCSI_FLAG_CMD_DIR_TODEVICE; - - req->dataLen = op->count * blocksize; - req->dataAddr = (u32)op->buf_fl; - s->reqProdIdx = s->reqProdIdx + 1; -} - static u32 pvscsi_get_rsp(struct PVSCSIRingsState *s, struct PVSCSIRingCmpDesc *rsp) @@ -229,10 +206,13 @@ pvscsi_get_rsp(struct PVSCSIRingsState *s, return status; } -static int -pvscsi_cmd(struct pvscsi_lun_s *plun, struct disk_op_s *op, - void *cdbcmd, u16 target, u16 lun, u16 blocksize) +int +pvscsi_process_op(struct disk_op_s *op) { + if (!CONFIG_PVSCSI) + return DISK_RET_EBADTRACK; + struct pvscsi_lun_s *plun = + container_of(op->drive_gf, struct pvscsi_lun_s, drive); struct pvscsi_ring_dsc_s *ring_dsc = plun->ring_dsc; struct PVSCSIRingsState *s = ring_dsc->ring_state; u32 req_entries = s->reqNumEntriesLog2; @@ -248,7 +228,23 @@ pvscsi_cmd(struct pvscsi_lun_s *plun, struct disk_op_s *op, } req = ring_dsc->ring_reqs + (s->reqProdIdx & MASK(req_entries)); - pvscsi_fill_req(s, req, target, lun, cdbcmd, blocksize, op); + int blocksize = scsi_fill_cmd(op, req->cdb, 16); + if (blocksize < 0) + return default_process_op(op); + req->bus = 0; + req->target = plun->target; + memset(req->lun, 0, sizeof(req->lun)); + req->lun[1] = plun->lun; + req->senseLen = 0; + req->senseAddr = 0; + req->cdbLen = 16; + req->vcpuHint = 0; + req->tag = SIMPLE_QUEUE_TAG; + req->flags = scsi_is_read(op) ? + PVSCSI_FLAG_CMD_DIR_TOHOST : PVSCSI_FLAG_CMD_DIR_TODEVICE; + req->dataLen = op->count * blocksize; + req->dataAddr = (u32)op->buf_fl; + s->reqProdIdx = s->reqProdIdx + 1; pvscsi_kick_rw_io(plun->iobase); pvscsi_wait_intr_cmpl(plun->iobase); @@ -259,18 +255,6 @@ pvscsi_cmd(struct pvscsi_lun_s *plun, struct disk_op_s *op, return status == 0 ? DISK_RET_SUCCESS : DISK_RET_EBADTRACK; } -int -pvscsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) -{ - if (!CONFIG_PVSCSI) - return DISK_RET_EBADTRACK; - - struct pvscsi_lun_s *plun = - container_of(op->drive_gf, struct pvscsi_lun_s, drive); - - return pvscsi_cmd(plun, op, cdbcmd, plun->target, plun->lun, blocksize); -} - static int pvscsi_add_lun(struct pci_device *pci, void *iobase, struct pvscsi_ring_dsc_s *ring_dsc, u8 target, u8 lun) diff --git a/qemu/roms/seabios/src/hw/pvscsi.h b/qemu/roms/seabios/src/hw/pvscsi.h index fde9f0b98..5af7dcb0e 100644 --- a/qemu/roms/seabios/src/hw/pvscsi.h +++ b/qemu/roms/seabios/src/hw/pvscsi.h @@ -2,7 +2,7 @@ #define _PVSCSI_H_ struct disk_op_s; -int pvscsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +int pvscsi_process_op(struct disk_op_s *op); void pvscsi_setup(void); #endif /* _PVSCSI_H_ */ diff --git a/qemu/roms/seabios/src/hw/ramdisk.c b/qemu/roms/seabios/src/hw/ramdisk.c index 1177bc00a..adec1d1b3 100644 --- a/qemu/roms/seabios/src/hw/ramdisk.c +++ b/qemu/roms/seabios/src/hw/ramdisk.c @@ -7,8 +7,9 @@ #include "biosvar.h" // GET_GLOBALFLAT #include "block.h" // struct drive_s #include "bregs.h" // struct bregs -#include "malloc.h" // malloc_fseg -#include "memmap.h" // add_e820 +#include "e820map.h" // e820_add +#include "malloc.h" // memalign_tmphigh +#include "memmap.h" // PAGE_SIZE #include "output.h" // dprintf #include "romfile.h" // romfile_findprefix #include "stacks.h" // call16_int @@ -41,7 +42,7 @@ ramdisk_setup(void) warn_noalloc(); return; } - add_e820((u32)pos, size, E820_RESERVED); + e820_add((u32)pos, size, E820_RESERVED); // Copy image into ram. int ret = file->copy(file, pos, size); @@ -53,7 +54,7 @@ ramdisk_setup(void) if (!drive) return; drive->type = DTYPE_RAMDISK; - dprintf(1, "Mapping CBFS floppy %s to addr %p\n", filename, pos); + dprintf(1, "Mapping floppy %s to addr %p\n", filename, pos); char *desc = znprintf(MAXDESCSIZE, "Ramdisk [%s]", &filename[10]); boot_add_floppy(drive, desc, bootprio_find_named_rom(filename, 0)); } @@ -91,7 +92,7 @@ ramdisk_copy(struct disk_op_s *op, int iswrite) } int -process_ramdisk_op(struct disk_op_s *op) +ramdisk_process_op(struct disk_op_s *op) { if (!CONFIG_FLASH_FLOPPY) return 0; @@ -101,11 +102,7 @@ process_ramdisk_op(struct disk_op_s *op) return ramdisk_copy(op, 0); case CMD_WRITE: return ramdisk_copy(op, 1); - case CMD_VERIFY: - case CMD_FORMAT: - case CMD_RESET: - return DISK_RET_SUCCESS; default: - return DISK_RET_EPARAM; + return default_process_op(op); } } diff --git a/qemu/roms/seabios/src/hw/rtc.c b/qemu/roms/seabios/src/hw/rtc.c index 628d5429f..9649a5a79 100644 --- a/qemu/roms/seabios/src/hw/rtc.c +++ b/qemu/roms/seabios/src/hw/rtc.c @@ -30,6 +30,7 @@ rtc_write(u8 index, u8 val) void rtc_mask(u8 index, u8 off, u8 on) { + index |= NMI_DISABLE_BIT; outb(index, PORT_CMOS_INDEX); u8 val = inb(PORT_CMOS_DATA); outb((val & ~off) | on, PORT_CMOS_DATA); @@ -62,6 +63,8 @@ rtc_updating(void) void rtc_setup(void) { + if (!CONFIG_RTC_TIMER) + return; rtc_write(CMOS_STATUS_A, 0x26); // 32,768Khz src, 976.5625us updates rtc_mask(CMOS_STATUS_B, ~RTC_B_DSE, RTC_B_24HR); rtc_read(CMOS_STATUS_C); @@ -73,6 +76,8 @@ int RTCusers VARLOW; void rtc_use(void) { + if (!CONFIG_RTC_TIMER) + return; int count = GET_LOW(RTCusers); SET_LOW(RTCusers, count+1); if (count) @@ -84,6 +89,8 @@ rtc_use(void) void rtc_release(void) { + if (!CONFIG_RTC_TIMER) + return; int count = GET_LOW(RTCusers); SET_LOW(RTCusers, count-1); if (count != 1) diff --git a/qemu/roms/seabios/src/hw/sdcard.c b/qemu/roms/seabios/src/hw/sdcard.c index 6ff93c856..e01e1bb02 100644 --- a/qemu/roms/seabios/src/hw/sdcard.c +++ b/qemu/roms/seabios/src/hw/sdcard.c @@ -5,12 +5,12 @@ // This file may be distributed under the terms of the GNU LGPLv3 license. #include "block.h" // struct drive_s -#include "fw/paravirt.h" // runningOnQEMU #include "malloc.h" // malloc_fseg #include "output.h" // znprintf #include "pci.h" // pci_config_readl #include "pci_ids.h" // PCI_CLASS_SYSTEM_SDHCI #include "pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "romfile.h" // romfile_findprefix #include "stacks.h" // wait_preempt #include "std/disk.h" // DISK_RET_SUCCESS #include "string.h" // memset @@ -42,8 +42,8 @@ struct sdhci_s { u16 irq_signal; u16 error_signal; u16 auto_cmd12; - u8 pad_3E[2]; - u64 cap; + u16 host_control2; + u32 cap_lo, cap_hi; u64 max_current; u16 force_auto_cmd12; u16 force_error; @@ -56,25 +56,38 @@ struct sdhci_s { } PACKED; // SDHCI commands -#define SC_ALL_SEND_CID ((2<<8) | 0x21) -#define SC_SEND_RELATIVE_ADDR ((3<<8) | 0x22) -#define SC_SELECT_DESELECT_CARD ((7<<8) | 0x23) -#define SC_READ_SINGLE ((17<<8) | 0x22) -#define SC_READ_MULTIPLE ((18<<8) | 0x22) -#define SC_WRITE_SINGLE ((24<<8) | 0x22) -#define SC_WRITE_MULTIPLE ((25<<8) | 0x22) -#define SC_APP_CMD ((55<<8) | 0x22) -#define SC_APP_SEND_OP_COND ((41<<8) | 0x22) +#define SCB_R0 0x00 // No response +#define SCB_R48 0x1a // Response R1 (no data), R5, R6, R7 +#define SCB_R48d 0x3a // Response R1 (with data) +#define SCB_R48b 0x1b // Response R1b, R5b +#define SCB_R48o 0x02 // Response R3, R4 +#define SCB_R136 0x09 // Response R2 +#define SC_GO_IDLE_STATE ((0<<8) | SCB_R0) +#define SC_SEND_OP_COND ((1<<8) | SCB_R48o) +#define SC_ALL_SEND_CID ((2<<8) | SCB_R136) +#define SC_SEND_RELATIVE_ADDR ((3<<8) | SCB_R48) +#define SC_SELECT_DESELECT_CARD ((7<<8) | SCB_R48b) +#define SC_SEND_IF_COND ((8<<8) | SCB_R48) +#define SC_SEND_EXT_CSD ((8<<8) | SCB_R48d) +#define SC_SEND_CSD ((9<<8) | SCB_R136) +#define SC_READ_SINGLE ((17<<8) | SCB_R48d) +#define SC_READ_MULTIPLE ((18<<8) | SCB_R48d) +#define SC_WRITE_SINGLE ((24<<8) | SCB_R48d) +#define SC_WRITE_MULTIPLE ((25<<8) | SCB_R48d) +#define SC_APP_CMD ((55<<8) | SCB_R48) +#define SC_APP_SEND_OP_COND ((41<<8) | SCB_R48o) // SDHCI irqs #define SI_CMD_COMPLETE (1<<0) #define SI_TRANS_DONE (1<<1) #define SI_WRITE_READY (1<<4) #define SI_READ_READY (1<<5) +#define SI_ERROR (1<<15) // SDHCI present_state flags -#define SP_CMD_INHIBIT (1<<0) -#define SP_DAT_INHIBIT (1<<1) +#define SP_CMD_INHIBIT (1<<0) +#define SP_DAT_INHIBIT (1<<1) +#define SP_CARD_INSERTED (1<<16) // SDHCI transfer_mode flags #define ST_BLOCKCOUNT (1<<1) @@ -82,12 +95,43 @@ struct sdhci_s { #define ST_READ (1<<4) #define ST_MULTIPLE (1<<5) +// SDHCI capabilities flags +#define SD_CAPLO_V33 (1<<24) +#define SD_CAPLO_V30 (1<<25) +#define SD_CAPLO_V18 (1<<26) +#define SD_CAPLO_BASECLOCK_SHIFT 8 +#define SD_CAPLO_BASECLOCK_MASK 0xff + +// SDHCI clock control flags +#define SCC_INTERNAL_ENABLE (1<<0) +#define SCC_STABLE (1<<1) +#define SCC_CLOCK_ENABLE (1<<2) +#define SCC_SDCLK_MASK 0xff +#define SCC_SDCLK_SHIFT 8 +#define SCC_SDCLK_HI_MASK 0x300 +#define SCC_SDCLK_HI_RSHIFT 2 + +// SDHCI power control flags +#define SPC_POWER_ON (1<<0) +#define SPC_V18 0x0a +#define SPC_V30 0x0c +#define SPC_V33 0x0e + +// SDHCI software reset flags +#define SRF_ALL 0x01 +#define SRF_CMD 0x02 +#define SRF_DATA 0x04 + // SDHCI result flags -#define SR_OCR_CCS (1<<30) +#define SR_OCR_CCS (1<<30) +#define SR_OCR_NOTBUSY (1<<31) // SDHCI timeouts -#define SDHCI_PIO_TIMEOUT 1000 // XXX - these are just made up -#define SDHCI_TRANSFER_TIMEOUT 10000 +#define SDHCI_POWER_OFF_TIME 1 +#define SDHCI_POWER_ON_TIME 1 +#define SDHCI_CLOCK_ON_TIME 1 // 74 clock cycles +#define SDHCI_POWERUP_TIMEOUT 1000 +#define SDHCI_PIO_TIMEOUT 1000 // XXX - this is just made up // Internal 'struct drive_s' storage for a detected card struct sddrive_s { @@ -97,18 +141,18 @@ struct sddrive_s { }; // SD card types -#define SF_MMC 0 -#define SF_SDSC 1 -#define SF_SDHC 2 +#define SF_MMC (1<<0) +#define SF_HIGHCAPACITY (1<<1) -// Repeatedly read a u16 register until the specific value is found +// Repeatedly read a u16 register until any bit in a given mask is set static int -waitw(u16 *reg, u16 mask, u16 value, u32 end) +sdcard_waitw(u16 *reg, u16 mask) { + u32 end = timer_calc(SDHCI_PIO_TIMEOUT); for (;;) { u16 v = readw(reg); - if ((v & mask) == value) - return 0; + if (v & mask) + return v; if (timer_check(end)) { warn_timeout(); return -1; @@ -117,24 +161,49 @@ waitw(u16 *reg, u16 mask, u16 value, u32 end) } } +// Send an sdhci reset +static int +sdcard_reset(struct sdhci_s *regs, int flags) +{ + writeb(®s->software_reset, flags); + u32 end = timer_calc(SDHCI_PIO_TIMEOUT); + while (readb(®s->software_reset)) + if (timer_check(end)) { + warn_timeout(); + return -1; + } + return 0; +} + // Send a command to the card. static int sdcard_pio(struct sdhci_s *regs, int cmd, u32 *param) { - u32 end = timer_calc(SDHCI_PIO_TIMEOUT); - u16 busyf = SP_CMD_INHIBIT | ((cmd & 0x03) == 0x03 ? SP_DAT_INHIBIT : 0); - int ret = waitw((u16*)®s->present_state, busyf, 0, end); - if (ret) - return ret; + u32 state = readl(®s->present_state); + dprintf(9, "sdcard_pio cmd %x %x %x\n", cmd, *param, state); + if ((state & SP_CMD_INHIBIT) + || ((cmd & 0x03) == 0x03 && state & SP_DAT_INHIBIT)) { + dprintf(1, "sdcard_pio not ready %x\n", state); + return -1; + } // Send command writel(®s->arg, *param); writew(®s->cmd, cmd); - ret = waitw(®s->irq_status, SI_CMD_COMPLETE, SI_CMD_COMPLETE, end); - if (ret) + int ret = sdcard_waitw(®s->irq_status, SI_ERROR|SI_CMD_COMPLETE); + if (ret < 0) return ret; + if (ret & SI_ERROR) { + u16 err = readw(®s->error_irq_status); + dprintf(3, "sdcard_pio command stop (code=%x)\n", err); + sdcard_reset(regs, SRF_CMD|SRF_DATA); + writew(®s->error_irq_status, err); + return -1; + } writew(®s->irq_status, SI_CMD_COMPLETE); // Read response memcpy(param, regs->response, sizeof(regs->response)); + dprintf(9, "sdcard cmd %x response %x %x %x %x\n" + , cmd, param[0], param[1], param[2], param[3]); return 0; } @@ -155,24 +224,23 @@ sdcard_pio_transfer(struct sddrive_s *drive, int cmd, u32 addr , void *data, int count) { // Send command - writel(&drive->regs->block_size, DISK_SECTOR_SIZE); - writew(&drive->regs->block_count, count); // XXX - SC_SET_BLOCK_COUNT? + writew(&drive->regs->block_size, DISK_SECTOR_SIZE); + writew(&drive->regs->block_count, count); int isread = cmd != SC_WRITE_SINGLE && cmd != SC_WRITE_MULTIPLE; u16 tmode = ((count > 1 ? ST_MULTIPLE|ST_AUTO_CMD12|ST_BLOCKCOUNT : 0) | (isread ? ST_READ : 0)); writew(&drive->regs->transfer_mode, tmode); - if (drive->card_type < SF_SDHC) + if (!(drive->card_type & SF_HIGHCAPACITY)) addr *= DISK_SECTOR_SIZE; u32 param[4] = { addr }; int ret = sdcard_pio(drive->regs, cmd, param); if (ret) return ret; // Read/write data - u32 end = timer_calc(SDHCI_TRANSFER_TIMEOUT); u16 cbit = isread ? SI_READ_READY : SI_WRITE_READY; while (count--) { - ret = waitw(&drive->regs->irq_status, cbit, cbit, end); - if (ret) + ret = sdcard_waitw(&drive->regs->irq_status, cbit); + if (ret < 0) return ret; writew(&drive->regs->irq_status, cbit); int i; @@ -185,9 +253,8 @@ sdcard_pio_transfer(struct sddrive_s *drive, int cmd, u32 addr } } // Complete command - // XXX - SC_STOP_TRANSMISSION? - ret = waitw(&drive->regs->irq_status, SI_TRANS_DONE, SI_TRANS_DONE, end); - if (ret) + ret = sdcard_waitw(&drive->regs->irq_status, SI_TRANS_DONE); + if (ret < 0) return ret; writew(&drive->regs->irq_status, SI_TRANS_DONE); return 0; @@ -208,8 +275,8 @@ sdcard_readwrite(struct disk_op_s *op, int iswrite) return DISK_RET_SUCCESS; } -int VISIBLE32FLAT -process_sdcard_op(struct disk_op_s *op) +int +sdcard_process_op(struct disk_op_s *op) { if (!CONFIG_SDCARD) return 0; @@ -218,14 +285,8 @@ process_sdcard_op(struct disk_op_s *op) return sdcard_readwrite(op, 0); case CMD_WRITE: return sdcard_readwrite(op, 1); - case CMD_FORMAT: - case CMD_RESET: - case CMD_ISREADY: - case CMD_VERIFY: - case CMD_SEEK: - return DISK_RET_SUCCESS; default: - return DISK_RET_EPARAM; + return default_process_op(op); } } @@ -234,75 +295,253 @@ process_sdcard_op(struct disk_op_s *op) * Setup ****************************************************************/ +static int +sdcard_set_power(struct sdhci_s *regs) +{ + u32 cap = readl(®s->cap_lo); + u32 volt, vbits; + if (cap & SD_CAPLO_V33) { + volt = 1<<20; + vbits = SPC_V33; + } else if (cap & SD_CAPLO_V30) { + volt = 1<<18; + vbits = SPC_V30; + } else if (cap & SD_CAPLO_V18) { + volt = 1<<7; + vbits = SPC_V18; + } else { + dprintf(1, "SD controller unsupported volt range (%x)\n", cap); + return -1; + } + writeb(®s->power_control, 0); + msleep(SDHCI_POWER_OFF_TIME); + writeb(®s->power_control, vbits | SPC_POWER_ON); + msleep(SDHCI_POWER_ON_TIME); + return volt; +} + +static int +sdcard_set_frequency(struct sdhci_s *regs, u32 khz) +{ + u16 ver = readw(®s->controller_version); + u32 cap = readl(®s->cap_lo); + u32 base_freq = (cap >> SD_CAPLO_BASECLOCK_SHIFT) & SD_CAPLO_BASECLOCK_MASK; + if (!base_freq) { + dprintf(1, "Unknown base frequency for SD controller\n"); + return -1; + } + // Set new frequency + u32 divisor = DIV_ROUND_UP(base_freq * 1000, khz); + u16 creg; + if ((ver & 0xff) <= 0x01) { + divisor = divisor > 1 ? 1 << __fls(divisor-1) : 0; + creg = (divisor & SCC_SDCLK_MASK) << SCC_SDCLK_SHIFT; + } else { + divisor = DIV_ROUND_UP(divisor, 2); + creg = (divisor & SCC_SDCLK_MASK) << SCC_SDCLK_SHIFT; + creg |= (divisor & SCC_SDCLK_HI_MASK) >> SCC_SDCLK_HI_RSHIFT; + } + dprintf(3, "sdcard_set_frequency %d %d %x\n", base_freq, khz, creg); + writew(®s->clock_control, 0); + writew(®s->clock_control, creg | SCC_INTERNAL_ENABLE); + // Wait for frequency to become active + int ret = sdcard_waitw(®s->clock_control, SCC_STABLE); + if (ret < 0) + return ret; + // Enable SD clock + writew(®s->clock_control, creg | SCC_INTERNAL_ENABLE | SCC_CLOCK_ENABLE); + return 0; +} + +// Obtain the disk size of an SD card +static int +sdcard_get_capacity(struct sddrive_s *drive, u8 *csd) +{ + // Original MMC/SD card capacity formula + u16 C_SIZE = (csd[6] >> 6) | (csd[7] << 2) | ((csd[8] & 0x03) << 10); + u8 C_SIZE_MULT = (csd[4] >> 7) | ((csd[5] & 0x03) << 1); + u8 READ_BL_LEN = csd[9] & 0x0f; + u32 count = (C_SIZE+1) << (C_SIZE_MULT + 2 + READ_BL_LEN - 9); + // Check for newer encoding formats. + u8 CSD_STRUCTURE = csd[14] >> 6; + if ((drive->card_type & SF_MMC) && CSD_STRUCTURE >= 2) { + // Get capacity from EXT_CSD register + u8 ext_csd[512]; + int ret = sdcard_pio_transfer(drive, SC_SEND_EXT_CSD, 0, ext_csd, 1); + if (ret) + return ret; + count = *(u32*)&ext_csd[212]; + } else if (!(drive->card_type & SF_MMC) && CSD_STRUCTURE >= 1) { + // High capacity SD card + u32 C_SIZE2 = csd[5] | (csd[6] << 8) | ((csd[7] & 0x3f) << 16); + count = (C_SIZE2+1) << (19-9); + } + // Fill drive struct and return + drive->drive.blksize = DISK_SECTOR_SIZE; + drive->drive.sectors = count; + return 0; +} + // Initialize an SD card static int -sdcard_card_setup(struct sdhci_s *regs) +sdcard_card_setup(struct sddrive_s *drive, int volt, int prio) { - // XXX - works on QEMU; probably wont on real hardware! - u32 param[4] = { 0x01 }; - int ret = sdcard_pio_app(regs, SC_APP_SEND_OP_COND, param); + struct sdhci_s *regs = drive->regs; + // Set controller to initialization clock rate + int ret = sdcard_set_frequency(regs, 400); + if (ret) + return ret; + msleep(SDHCI_CLOCK_ON_TIME); + // Reset card + u32 param[4] = { }; + ret = sdcard_pio(regs, SC_GO_IDLE_STATE, param); if (ret) return ret; - int card_type = (param[0] & SR_OCR_CCS) ? SF_SDHC : SF_SDSC; + // Let card know SDHC/SDXC is supported and confirm voltage + u32 hcs = 0, vrange = (volt >= (1<<15) ? 0x100 : 0x200) | 0xaa; + param[0] = vrange; + ret = sdcard_pio(regs, SC_SEND_IF_COND, param); + if (!ret && param[0] == vrange) + hcs = (1<<30); + // Verify SD card (instead of MMC or SDIO) + param[0] = 0x00; + ret = sdcard_pio_app(regs, SC_APP_SEND_OP_COND, param); + if (ret) { + // Check for MMC card + param[0] = 0x00; + ret = sdcard_pio(regs, SC_SEND_OP_COND, param); + if (ret) + return ret; + drive->card_type |= SF_MMC; + hcs = (1<<30); + } + // Init card + u32 end = timer_calc(SDHCI_POWERUP_TIMEOUT); + for (;;) { + param[0] = hcs | volt; // high-capacity support and voltage level + if (drive->card_type & SF_MMC) + ret = sdcard_pio(regs, SC_SEND_OP_COND, param); + else + ret = sdcard_pio_app(regs, SC_APP_SEND_OP_COND, param); + if (ret) + return ret; + if (param[0] & SR_OCR_NOTBUSY) + break; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + msleep(5); // Avoid flooding log when debugging + } + drive->card_type |= (param[0] & SR_OCR_CCS) ? SF_HIGHCAPACITY : 0; + // Select card (get cid, set rca, get csd, select card) param[0] = 0x00; ret = sdcard_pio(regs, SC_ALL_SEND_CID, param); if (ret) return ret; - param[0] = 0x01 << 16; + u8 cid[16]; + memcpy(cid, param, sizeof(cid)); + param[0] = drive->card_type & SF_MMC ? 0x0001 << 16 : 0x00; ret = sdcard_pio(regs, SC_SEND_RELATIVE_ADDR, param); if (ret) return ret; - u16 rca = param[0] >> 16; + u16 rca = drive->card_type & SF_MMC ? 0x0001 : param[0] >> 16; + param[0] = rca << 16; + ret = sdcard_pio(regs, SC_SEND_CSD, param); + if (ret) + return ret; + u8 csd[16]; + memcpy(csd, param, sizeof(csd)); param[0] = rca << 16; ret = sdcard_pio(regs, SC_SELECT_DESELECT_CARD, param); if (ret) return ret; - return card_type; + // Set controller to data transfer clock rate + ret = sdcard_set_frequency(regs, 25000); + if (ret) + return ret; + // Register drive + ret = sdcard_get_capacity(drive, csd); + if (ret) + return ret; + char pnm[7] = {}; + int i; + for (i=0; i < (drive->card_type & SF_MMC ? 6 : 5); i++) + pnm[i] = cid[11-i]; + char *desc = znprintf(MAXDESCSIZE, "%s %s %dMiB" + , drive->card_type & SF_MMC ? "MMC drive" : "SD card" + , pnm, (u32)(drive->drive.sectors >> 11)); + dprintf(1, "Found sdcard at %p: %s\n", regs, desc); + boot_add_hd(&drive->drive, desc, prio); + return 0; } // Setup and configure an SD card controller static void -sdcard_controller_setup(void *data) +sdcard_controller_setup(struct sdhci_s *regs, int prio) { - struct pci_device *pci = data; - u16 bdf = pci->bdf; - wait_preempt(); // Avoid pci_config_readl when preempting - struct sdhci_s *regs = (void*)pci_config_readl(bdf, PCI_BASE_ADDRESS_0); - pci_config_maskw(bdf, PCI_COMMAND, 0, - PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - // Initialize controller - if (!runningOnQEMU()) - // XXX - this init logic will probably only work on qemu! + u32 present_state = readl(®s->present_state); + if (!(present_state & SP_CARD_INSERTED)) + // No card present return; + dprintf(3, "sdhci@%p ver=%x cap=%x %x\n", regs + , readw(®s->controller_version) + , readl(®s->cap_lo), readl(®s->cap_hi)); + sdcard_reset(regs, SRF_ALL); writew(®s->irq_signal, 0); - writew(®s->irq_enable, 0xffff); + writew(®s->irq_enable, 0x01ff); + writew(®s->irq_status, readw(®s->irq_status)); writew(®s->error_signal, 0); - writeb(®s->power_control, 0x0f); - writew(®s->clock_control, 0x0005); - - // Initialize card - int card_type = sdcard_card_setup(regs); - if (card_type < 0) + writew(®s->error_irq_enable, 0x01ff); + writew(®s->error_irq_status, readw(®s->error_irq_status)); + writeb(®s->timeout_control, 0x0e); // Set to max timeout + int volt = sdcard_set_power(regs); + if (volt < 0) return; - // Register drive + // Initialize card struct sddrive_s *drive = malloc_fseg(sizeof(*drive)); if (!drive) { warn_noalloc(); - return; + goto fail; } memset(drive, 0, sizeof(*drive)); drive->drive.type = DTYPE_SDCARD; - drive->drive.blksize = DISK_SECTOR_SIZE; - drive->drive.sectors = (u64)-1; // XXX drive->regs = regs; - drive->card_type = card_type; + int ret = sdcard_card_setup(drive, volt, prio); + if (ret) { + free(drive); + goto fail; + } + return; +fail: + writeb(®s->power_control, 0); + writew(®s->clock_control, 0); +} - dprintf(1, "Found SD Card at %02x:%02x.%x\n" - , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf)); - char *desc = znprintf(MAXDESCSIZE, "SD Card"); // XXX - boot_add_hd(&drive->drive, desc, bootprio_find_pci_device(pci)); +static void +sdcard_pci_setup(void *data) +{ + struct pci_device *pci = data; + wait_preempt(); // Avoid pci_config_readl when preempting + // XXX - bars dependent on slot index register in pci config space + u32 regs = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0); + regs &= PCI_BASE_ADDRESS_MEM_MASK; + pci_config_maskw(pci->bdf, PCI_COMMAND, 0, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + int prio = bootprio_find_pci_device(pci); + sdcard_controller_setup((void*)regs, prio); +} + +static void +sdcard_romfile_setup(void *data) +{ + struct romfile_s *file = data; + int prio = bootprio_find_named_rom(file->name, 0); + u32 addr = romfile_loadint(file->name, 0); + dprintf(1, "Starting sdcard controller check at addr %x\n", addr); + sdcard_controller_setup((void*)addr, prio); } void @@ -311,11 +550,19 @@ sdcard_setup(void) if (!CONFIG_SDCARD) return; + struct romfile_s *file = NULL; + for (;;) { + file = romfile_findprefix("etc/sdcard", file); + if (!file) + break; + run_thread(sdcard_romfile_setup, file); + } + struct pci_device *pci; foreachpci(pci) { if (pci->class != PCI_CLASS_SYSTEM_SDHCI || pci->prog_if >= 2) // Not an SDHCI controller following SDHCI spec continue; - run_thread(sdcard_controller_setup, pci); + run_thread(sdcard_pci_setup, pci); } } diff --git a/qemu/roms/seabios/src/hw/timer.c b/qemu/roms/seabios/src/hw/timer.c index 5edc9fdbb..03d22b2f5 100644 --- a/qemu/roms/seabios/src/hw/timer.c +++ b/qemu/roms/seabios/src/hw/timer.c @@ -49,8 +49,8 @@ #define PMTIMER_HZ 3579545 // Underlying Hz of the PM Timer #define PMTIMER_TO_PIT 3 // Ratio of pmtimer rate to pit rate -u32 TimerKHz VARFSEG; -u16 TimerPort VARFSEG; +u32 TimerKHz VARFSEG = DIV_ROUND_UP(PMTIMER_HZ, 1000 * PMTIMER_TO_PIT); +u16 TimerPort VARFSEG = PORT_PIT_COUNTER0; u8 ShiftTSC VARFSEG; @@ -92,6 +92,7 @@ tsctimer_setup(void) t = (t + 1) >> 1; } TimerKHz = DIV_ROUND_UP((u32)t, 1000 * PMTIMER_TO_PIT); + TimerPort = 0; dprintf(1, "CPU Mhz=%u\n", (TimerKHz << ShiftTSC) / 1000); } @@ -100,24 +101,16 @@ tsctimer_setup(void) void timer_setup(void) { - if (CONFIG_PMTIMER && TimerPort) { - dprintf(3, "pmtimer already configured; will not calibrate TSC\n"); + if (!CONFIG_TSC_TIMER || (CONFIG_PMTIMER && TimerPort != PORT_PIT_COUNTER0)) return; - } + // Check if CPU has a timestamp counter u32 eax, ebx, ecx, edx, cpuid_features = 0; cpuid(0, &eax, &ebx, &ecx, &edx); if (eax > 0) cpuid(1, &eax, &ebx, &ecx, &cpuid_features); - - if (!(cpuid_features & CPUID_TSC)) { - TimerPort = PORT_PIT_COUNTER0; - TimerKHz = DIV_ROUND_UP(PMTIMER_HZ, 1000 * PMTIMER_TO_PIT); - dprintf(3, "386/486 class CPU. Using TSC emulation\n"); - return; - } - - tsctimer_setup(); + if (cpuid_features & CPUID_TSC) + tsctimer_setup(); } void @@ -154,7 +147,7 @@ static u32 timer_read(void) { u16 port = GET_GLOBAL(TimerPort); - if (!port) + if (CONFIG_TSC_TIMER && !port) // Read from CPU TSC return rdtscll() >> GET_GLOBAL(ShiftTSC); if (CONFIG_PMTIMER && port != PORT_PIT_COUNTER0) @@ -249,6 +242,8 @@ ticks_from_ms(u32 ms) void pit_setup(void) { + if (!CONFIG_HARDWARE_IRQ) + return; // timer0: binary count, 16bit count, mode 2 outb(PM_SEL_TIMER0|PM_ACCESS_WORD|PM_MODE2|PM_CNT_BINARY, PORT_PIT_MODE); // maximum count of 0000H = 18.2Hz diff --git a/qemu/roms/seabios/src/hw/tpm_drivers.c b/qemu/roms/seabios/src/hw/tpm_drivers.c new file mode 100644 index 000000000..444eac39b --- /dev/null +++ b/qemu/roms/seabios/src/hw/tpm_drivers.c @@ -0,0 +1,291 @@ +// Implementation of a TPM driver for the TPM TIS interface +// +// Copyright (C) 2006-2011 IBM Corporation +// +// Authors: +// Stefan Berger +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_TPM_TIS_SHA1THRESHOLD +#include "string.h" // memcpy +#include "util.h" // msleep +#include "x86.h" // readl +#include "hw/tpm_drivers.h" // struct tpm_driver +#include "tcgbios.h" // TCG_* + +static const u32 tis_default_timeouts[4] = { + TIS_DEFAULT_TIMEOUT_A, + TIS_DEFAULT_TIMEOUT_B, + TIS_DEFAULT_TIMEOUT_C, + TIS_DEFAULT_TIMEOUT_D, +}; + +static const u32 tpm_default_durations[3] = { + TPM_DEFAULT_DURATION_SHORT, + TPM_DEFAULT_DURATION_MEDIUM, + TPM_DEFAULT_DURATION_LONG, +}; + +/* determined values */ +static u32 tpm_default_dur[3]; +static u32 tpm_default_to[4]; + + +/* if device is not there, return '0', '1' otherwise */ +static u32 tis_probe(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + u32 rc = 0; + u32 didvid = readl(TIS_REG(0, TIS_REG_DID_VID)); + + if ((didvid != 0) && (didvid != 0xffffffff)) + rc = 1; + + return rc; +} + +static u32 tis_init(void) +{ + if (!CONFIG_TCGBIOS) + return 1; + + writeb(TIS_REG(0, TIS_REG_INT_ENABLE), 0); + + if (tpm_drivers[TIS_DRIVER_IDX].durations == NULL) { + u32 *durations = tpm_default_dur; + memcpy(durations, tpm_default_durations, + sizeof(tpm_default_durations)); + tpm_drivers[TIS_DRIVER_IDX].durations = durations; + } + + if (tpm_drivers[TIS_DRIVER_IDX].timeouts == NULL) { + u32 *timeouts = tpm_default_to; + memcpy(timeouts, tis_default_timeouts, + sizeof(tis_default_timeouts)); + tpm_drivers[TIS_DRIVER_IDX].timeouts = timeouts; + } + + return 1; +} + + +static void set_timeouts(u32 timeouts[4], u32 durations[3]) +{ + if (!CONFIG_TCGBIOS) + return; + + u32 *tos = tpm_drivers[TIS_DRIVER_IDX].timeouts; + u32 *dus = tpm_drivers[TIS_DRIVER_IDX].durations; + + if (tos && tos != tis_default_timeouts && timeouts) + memcpy(tos, timeouts, 4 * sizeof(u32)); + if (dus && dus != tpm_default_durations && durations) + memcpy(dus, durations, 3 * sizeof(u32)); +} + + +static u32 tis_wait_sts(u8 locty, u32 time, u8 mask, u8 expect) +{ + if (!CONFIG_TCGBIOS) + return 0; + + u32 rc = 1; + + while (time > 0) { + u8 sts = readb(TIS_REG(locty, TIS_REG_STS)); + if ((sts & mask) == expect) { + rc = 0; + break; + } + msleep(1); + time--; + } + return rc; +} + +static u32 tis_activate(u8 locty) +{ + if (!CONFIG_TCGBIOS) + return 0; + + u32 rc = 0; + u8 acc; + int l; + u32 timeout_a = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_A]; + + if (!(readb(TIS_REG(locty, TIS_REG_ACCESS)) & + TIS_ACCESS_ACTIVE_LOCALITY)) { + /* release locality in use top-downwards */ + for (l = 4; l >= 0; l--) + writeb(TIS_REG(l, TIS_REG_ACCESS), + TIS_ACCESS_ACTIVE_LOCALITY); + } + + /* request access to locality */ + writeb(TIS_REG(locty, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE); + + acc = readb(TIS_REG(locty, TIS_REG_ACCESS)); + if ((acc & TIS_ACCESS_ACTIVE_LOCALITY)) { + writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY); + rc = tis_wait_sts(locty, timeout_a, + TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY); + } + + return rc; +} + +static u32 tis_find_active_locality(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + u8 locty; + + for (locty = 0; locty <= 4; locty++) { + if ((readb(TIS_REG(locty, TIS_REG_ACCESS)) & + TIS_ACCESS_ACTIVE_LOCALITY)) + return locty; + } + + tis_activate(0); + + return 0; +} + +static u32 tis_ready(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + u32 rc = 0; + u8 locty = tis_find_active_locality(); + u32 timeout_b = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_B]; + + writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY); + rc = tis_wait_sts(locty, timeout_b, + TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY); + + return rc; +} + +static u32 tis_senddata(const u8 *const data, u32 len) +{ + if (!CONFIG_TCGBIOS) + return 0; + + u32 rc = 0; + u32 offset = 0; + u32 end = 0; + u16 burst = 0; + u32 ctr = 0; + u8 locty = tis_find_active_locality(); + u32 timeout_d = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_D]; + + do { + while (burst == 0 && ctr < timeout_d) { + burst = readl(TIS_REG(locty, TIS_REG_STS)) >> 8; + if (burst == 0) { + msleep(1); + ctr++; + } + } + + if (burst == 0) { + rc = TCG_RESPONSE_TIMEOUT; + break; + } + + while (1) { + writeb(TIS_REG(locty, TIS_REG_DATA_FIFO), data[offset++]); + burst--; + + if (burst == 0 || offset == len) + break; + } + + if (offset == len) + end = 1; + } while (end == 0); + + return rc; +} + +static u32 tis_readresp(u8 *buffer, u32 *len) +{ + if (!CONFIG_TCGBIOS) + return 0; + + u32 rc = 0; + u32 offset = 0; + u32 sts; + u8 locty = tis_find_active_locality(); + + while (offset < *len) { + buffer[offset] = readb(TIS_REG(locty, TIS_REG_DATA_FIFO)); + offset++; + sts = readb(TIS_REG(locty, TIS_REG_STS)); + /* data left ? */ + if ((sts & TIS_STS_DATA_AVAILABLE) == 0) + break; + } + + *len = offset; + + return rc; +} + + +static u32 tis_waitdatavalid(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + u32 rc = 0; + u8 locty = tis_find_active_locality(); + u32 timeout_c = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_C]; + + if (tis_wait_sts(locty, timeout_c, TIS_STS_VALID, TIS_STS_VALID) != 0) + rc = TCG_NO_RESPONSE; + + return rc; +} + +static u32 tis_waitrespready(enum tpmDurationType to_t) +{ + if (!CONFIG_TCGBIOS) + return 0; + + u32 rc = 0; + u8 locty = tis_find_active_locality(); + u32 timeout = tpm_drivers[TIS_DRIVER_IDX].durations[to_t]; + + writeb(TIS_REG(locty ,TIS_REG_STS), TIS_STS_TPM_GO); + + if (tis_wait_sts(locty, timeout, + TIS_STS_DATA_AVAILABLE, TIS_STS_DATA_AVAILABLE) != 0) + rc = TCG_NO_RESPONSE; + + return rc; +} + + +struct tpm_driver tpm_drivers[TPM_NUM_DRIVERS] = { + [TIS_DRIVER_IDX] = + { + .timeouts = NULL, + .durations = NULL, + .set_timeouts = set_timeouts, + .probe = tis_probe, + .init = tis_init, + .activate = tis_activate, + .ready = tis_ready, + .senddata = tis_senddata, + .readresp = tis_readresp, + .waitdatavalid = tis_waitdatavalid, + .waitrespready = tis_waitrespready, + .sha1threshold = 100 * 1024, + }, +}; diff --git a/qemu/roms/seabios/src/hw/tpm_drivers.h b/qemu/roms/seabios/src/hw/tpm_drivers.h new file mode 100644 index 000000000..34bb12d1c --- /dev/null +++ b/qemu/roms/seabios/src/hw/tpm_drivers.h @@ -0,0 +1,90 @@ +#ifndef TPM_DRIVERS_H +#define TPM_DRIVERS_H + +#include "types.h" // u32 + + +enum tpmDurationType { + TPM_DURATION_TYPE_SHORT = 0, + TPM_DURATION_TYPE_MEDIUM, + TPM_DURATION_TYPE_LONG, +}; + +/* low level driver implementation */ +struct tpm_driver { + u32 *timeouts; + u32 *durations; + void (*set_timeouts)(u32 timeouts[4], u32 durations[3]); + u32 (*probe)(void); + u32 (*init)(void); + u32 (*activate)(u8 locty); + u32 (*ready)(void); + u32 (*senddata)(const u8 *const data, u32 len); + u32 (*readresp)(u8 *buffer, u32 *len); + u32 (*waitdatavalid)(void); + u32 (*waitrespready)(enum tpmDurationType to_t); + /* the TPM will be used for buffers of sizes below the sha1threshold + for calculating the hash */ + u32 sha1threshold; +}; + +extern struct tpm_driver tpm_drivers[]; + + +#define TIS_DRIVER_IDX 0 +#define TPM_NUM_DRIVERS 1 + +#define TPM_INVALID_DRIVER -1 + +/* TIS driver */ +/* address of locality 0 (TIS) */ +#define TPM_TIS_BASE_ADDRESS 0xfed40000 + +#define TIS_REG(LOCTY, REG) \ + (void *)(TPM_TIS_BASE_ADDRESS + (LOCTY << 12) + REG) + +/* hardware registers */ +#define TIS_REG_ACCESS 0x0 +#define TIS_REG_INT_ENABLE 0x8 +#define TIS_REG_INT_VECTOR 0xc +#define TIS_REG_INT_STATUS 0x10 +#define TIS_REG_INTF_CAPABILITY 0x14 +#define TIS_REG_STS 0x18 +#define TIS_REG_DATA_FIFO 0x24 +#define TIS_REG_DID_VID 0xf00 +#define TIS_REG_RID 0xf04 + +#define TIS_STS_VALID (1 << 7) /* 0x80 */ +#define TIS_STS_COMMAND_READY (1 << 6) /* 0x40 */ +#define TIS_STS_TPM_GO (1 << 5) /* 0x20 */ +#define TIS_STS_DATA_AVAILABLE (1 << 4) /* 0x10 */ +#define TIS_STS_EXPECT (1 << 3) /* 0x08 */ +#define TIS_STS_RESPONSE_RETRY (1 << 1) /* 0x02 */ + +#define TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) /* 0x80 */ +#define TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) /* 0x20 */ +#define TIS_ACCESS_BEEN_SEIZED (1 << 4) /* 0x10 */ +#define TIS_ACCESS_SEIZE (1 << 3) /* 0x08 */ +#define TIS_ACCESS_PENDING_REQUEST (1 << 2) /* 0x04 */ +#define TIS_ACCESS_REQUEST_USE (1 << 1) /* 0x02 */ +#define TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) /* 0x01 */ + +#define SCALER 10 + +#define TIS_DEFAULT_TIMEOUT_A (750 * SCALER) +#define TIS_DEFAULT_TIMEOUT_B (2000 * SCALER) +#define TIS_DEFAULT_TIMEOUT_C (750 * SCALER) +#define TIS_DEFAULT_TIMEOUT_D (750 * SCALER) + +enum tisTimeoutType { + TIS_TIMEOUT_TYPE_A = 0, + TIS_TIMEOUT_TYPE_B, + TIS_TIMEOUT_TYPE_C, + TIS_TIMEOUT_TYPE_D, +}; + +#define TPM_DEFAULT_DURATION_SHORT (2000 * SCALER) +#define TPM_DEFAULT_DURATION_MEDIUM (20000 * SCALER) +#define TPM_DEFAULT_DURATION_LONG (60000 * SCALER) + +#endif /* TPM_DRIVERS_H */ diff --git a/qemu/roms/seabios/src/hw/usb-hid.h b/qemu/roms/seabios/src/hw/usb-hid.h index ef34e7963..fd7b8f8be 100644 --- a/qemu/roms/seabios/src/hw/usb-hid.h +++ b/qemu/roms/seabios/src/hw/usb-hid.h @@ -4,10 +4,10 @@ // usb-hid.c struct usbdevice_s; int usb_hid_setup(struct usbdevice_s *usbdev); -inline int usb_kbd_active(void); -inline int usb_kbd_command(int command, u8 *param); -inline int usb_mouse_active(void); -inline int usb_mouse_command(int command, u8 *param); +int usb_kbd_active(void); +int usb_kbd_command(int command, u8 *param); +int usb_mouse_active(void); +int usb_mouse_command(int command, u8 *param); void usb_check_event(void); diff --git a/qemu/roms/seabios/src/hw/usb-msc.c b/qemu/roms/seabios/src/hw/usb-msc.c index d90319f51..a234f13be 100644 --- a/qemu/roms/seabios/src/hw/usb-msc.c +++ b/qemu/roms/seabios/src/hw/usb-msc.c @@ -63,25 +63,27 @@ usb_msc_send(struct usbdrive_s *udrive_gf, int dir, void *buf, u32 bytes) // Low-level usb command transmit function. int -usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +usb_process_op(struct disk_op_s *op) { if (!CONFIG_USB_MSC) return 0; - dprintf(16, "usb_cmd_data id=%p write=%d count=%d bs=%d buf=%p\n" - , op->drive_gf, 0, op->count, blocksize, op->buf_fl); + dprintf(16, "usb_cmd_data id=%p write=%d count=%d buf=%p\n" + , op->drive_gf, 0, op->count, op->buf_fl); struct usbdrive_s *udrive_gf = container_of( op->drive_gf, struct usbdrive_s, drive); // Setup command block wrapper. - u32 bytes = blocksize * op->count; struct cbw_s cbw; memset(&cbw, 0, sizeof(cbw)); - memcpy(cbw.CBWCB, cdbcmd, USB_CDB_SIZE); + int blocksize = scsi_fill_cmd(op, cbw.CBWCB, USB_CDB_SIZE); + if (blocksize < 0) + return default_process_op(op); + u32 bytes = blocksize * op->count; cbw.dCBWSignature = CBW_SIGNATURE; cbw.dCBWTag = 999; // XXX cbw.dCBWDataTransferLength = bytes; - cbw.bmCBWFlags = cdb_is_read(cdbcmd, blocksize) ? USB_DIR_IN : USB_DIR_OUT; + cbw.bmCBWFlags = scsi_is_read(op) ? USB_DIR_IN : USB_DIR_OUT; cbw.bCBWLUN = GET_GLOBALFLAT(udrive_gf->lun); cbw.bCBWCBLength = USB_CDB_SIZE; diff --git a/qemu/roms/seabios/src/hw/usb-msc.h b/qemu/roms/seabios/src/hw/usb-msc.h index c40d75556..ff3c38038 100644 --- a/qemu/roms/seabios/src/hw/usb-msc.h +++ b/qemu/roms/seabios/src/hw/usb-msc.h @@ -3,7 +3,7 @@ // usb-msc.c struct disk_op_s; -int usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +int usb_process_op(struct disk_op_s *op); struct usbdevice_s; int usb_msc_setup(struct usbdevice_s *usbdev); diff --git a/qemu/roms/seabios/src/hw/usb-uas.c b/qemu/roms/seabios/src/hw/usb-uas.c index 6ef8d0912..10e38454a 100644 --- a/qemu/roms/seabios/src/hw/usb-uas.c +++ b/qemu/roms/seabios/src/hw/usb-uas.c @@ -91,7 +91,7 @@ struct uasdrive_s { }; int -uas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +uas_process_op(struct disk_op_s *op) { if (!CONFIG_USB_UAS) return DISK_RET_EBADTRACK; @@ -104,7 +104,9 @@ uas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) ui.hdr.id = UAS_UI_COMMAND; ui.hdr.tag = 0xdead; ui.command.lun[1] = GET_GLOBALFLAT(drive_gf->lun); - memcpy(ui.command.cdb, cdbcmd, sizeof(ui.command.cdb)); + int blocksize = scsi_fill_cmd(op, ui.command.cdb, sizeof(ui.command.cdb)); + if (blocksize < 0) + return default_process_op(op); int ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->command), USB_DIR_OUT, MAKE_FLATPTR(GET_SEG(SS), &ui), sizeof(ui.hdr) + sizeof(ui.command)); diff --git a/qemu/roms/seabios/src/hw/usb-uas.h b/qemu/roms/seabios/src/hw/usb-uas.h index ad91c5f60..8b2f810e9 100644 --- a/qemu/roms/seabios/src/hw/usb-uas.h +++ b/qemu/roms/seabios/src/hw/usb-uas.h @@ -2,7 +2,7 @@ #define __USB_UAS_H struct disk_op_s; -int uas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +int uas_process_op(struct disk_op_s *op); struct usbdevice_s; int usb_uas_setup(struct usbdevice_s *usbdev); diff --git a/qemu/roms/seabios/src/hw/usb-xhci.c b/qemu/roms/seabios/src/hw/usb-xhci.c index fd58334dc..654febaad 100644 --- a/qemu/roms/seabios/src/hw/usb-xhci.c +++ b/qemu/roms/seabios/src/hw/usb-xhci.c @@ -350,26 +350,41 @@ xhci_hub_reset(struct usbhub_s *hub, u32 port) { struct usb_xhci_s *xhci = container_of(hub->cntl, struct usb_xhci_s, usb); u32 portsc = readl(&xhci->pr[port].portsc); - int rc; + if (!(portsc & XHCI_PORTSC_CCS)) + // Device no longer connected?! + return -1; switch (xhci_get_field(portsc, XHCI_PORTSC_PLS)) { case PLS_U0: - rc = speed_from_xhci[xhci_get_field(portsc, XHCI_PORTSC_SPEED)]; + // A USB3 port - controller automatically performs reset break; case PLS_POLLING: + // A USB2 port - perform device reset xhci_print_port_state(3, __func__, port, portsc); - portsc |= XHCI_PORTSC_PR; - writel(&xhci->pr[port].portsc, portsc); - if (wait_bit(&xhci->pr[port].portsc, XHCI_PORTSC_PED, XHCI_PORTSC_PED, 100) != 0) - return -1; - portsc = readl(&xhci->pr[port].portsc); - rc = speed_from_xhci[xhci_get_field(portsc, XHCI_PORTSC_SPEED)]; + writel(&xhci->pr[port].portsc, portsc | XHCI_PORTSC_PR); break; default: - rc = -1; - break; + return -1; } + // Wait for device to complete reset and be enabled + u32 end = timer_calc(100); + for (;;) { + portsc = readl(&xhci->pr[port].portsc); + if (!(portsc & XHCI_PORTSC_CCS)) + // Device disconnected during reset + return -1; + if (portsc & XHCI_PORTSC_PED) + // Reset complete + break; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } + + int rc = speed_from_xhci[xhci_get_field(portsc, XHCI_PORTSC_SPEED)]; xhci_print_port_state(1, "XHCI", port, portsc); return rc; } @@ -465,7 +480,7 @@ configure_xhci(void *data) xhci->evts->cs = 1; reg = readl(&xhci->caps->hcsparams2); - u32 spb = reg >> 27; + u32 spb = (reg >> 21 & 0x1f) << 5 | reg >> 27; if (spb) { dprintf(3, "%s: setup %d scratch pad buffers\n", __func__, spb); u64 *spba = memalign_high(64, sizeof(*spba) * spb); @@ -921,8 +936,14 @@ xhci_alloc_pipe(struct usbdevice_s *usbdev usb_desc2pipe(&pipe->pipe, usbdev, epdesc); pipe->epid = epid; pipe->reqs.cs = 1; - if (eptype == USB_ENDPOINT_XFER_INT) + if (eptype == USB_ENDPOINT_XFER_INT) { pipe->buf = malloc_high(pipe->pipe.maxpacket); + if (!pipe->buf) { + warn_noalloc(); + free(pipe); + return NULL; + } + } // Allocate input context and initialize endpoint info. struct xhci_inctx *in = xhci_alloc_inctx(usbdev, epid); @@ -988,6 +1009,7 @@ xhci_alloc_pipe(struct usbdevice_s *usbdev return &pipe->pipe; fail: + free(pipe->buf); free(pipe); free(in); return NULL; diff --git a/qemu/roms/seabios/src/hw/usb.c b/qemu/roms/seabios/src/hw/usb.c index 1b4ea8bed..e46092c63 100644 --- a/qemu/roms/seabios/src/hw/usb.c +++ b/qemu/roms/seabios/src/hw/usb.c @@ -79,9 +79,8 @@ usb_poll_intr(struct usb_pipe *pipe_fl, void *data) case USB_TYPE_EHCI: return ehci_poll_intr(pipe_fl, data); case USB_TYPE_XHCI: ; - extern void _cfunc32flat_xhci_poll_intr(void); - return call32_params(_cfunc32flat_xhci_poll_intr, (u32)pipe_fl - , (u32)MAKE_FLATPTR(GET_SEG(SS), (u32)data), 0, -1); + return call32_params(xhci_poll_intr, pipe_fl + , MAKE_FLATPTR(GET_SEG(SS), data), 0, -1); } } @@ -249,8 +248,10 @@ get_device_config(struct usb_pipe *pipe) return NULL; void *config = malloc_tmphigh(cfg.wTotalLength); - if (!config) + if (!config) { + warn_noalloc(); return NULL; + } req.wLength = cfg.wTotalLength; ret = usb_send_default_control(pipe, &req, config); if (ret) { diff --git a/qemu/roms/seabios/src/hw/virtio-blk.c b/qemu/roms/seabios/src/hw/virtio-blk.c index e2dbd3c94..20a79ebba 100644 --- a/qemu/roms/seabios/src/hw/virtio-blk.c +++ b/qemu/roms/seabios/src/hw/virtio-blk.c @@ -25,7 +25,7 @@ struct virtiodrive_s { struct drive_s drive; struct vring_virtqueue *vq; - u16 ioaddr; + struct vp_device vp; }; static int @@ -33,7 +33,7 @@ virtio_blk_op(struct disk_op_s *op, int write) { struct virtiodrive_s *vdrive_gf = container_of(op->drive_gf, struct virtiodrive_s, drive); - struct vring_virtqueue *vq = GET_GLOBALFLAT(vdrive_gf->vq); + struct vring_virtqueue *vq = vdrive_gf->vq; struct virtio_blk_outhdr hdr = { .type = write ? VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN, .ioprio = 0, @@ -42,15 +42,15 @@ virtio_blk_op(struct disk_op_s *op, int write) u8 status = VIRTIO_BLK_S_UNSUPP; struct vring_list sg[] = { { - .addr = MAKE_FLATPTR(GET_SEG(SS), &hdr), + .addr = (void*)(&hdr), .length = sizeof(hdr), }, { .addr = op->buf_fl, - .length = GET_GLOBALFLAT(vdrive_gf->drive.blksize) * op->count, + .length = vdrive_gf->drive.blksize * op->count, }, { - .addr = MAKE_FLATPTR(GET_SEG(SS), &status), + .addr = (void*)(&status), .length = sizeof(status), }, }; @@ -60,7 +60,7 @@ virtio_blk_op(struct disk_op_s *op, int write) vring_add_buf(vq, sg, 2, 1, 0, 0); else vring_add_buf(vq, sg, 1, 2, 0, 0); - vring_kick(GET_GLOBALFLAT(vdrive_gf->ioaddr), vq, 1); + vring_kick(&vdrive_gf->vp, vq, 1); /* Wait for reply */ while (!vring_more_used(vq)) @@ -72,13 +72,13 @@ virtio_blk_op(struct disk_op_s *op, int write) /* Clear interrupt status register. Avoid leaving interrupts stuck if * VRING_AVAIL_F_NO_INTERRUPT was ignored and interrupts were raised. */ - vp_get_isr(GET_GLOBALFLAT(vdrive_gf->ioaddr)); + vp_get_isr(&vdrive_gf->vp); return status == VIRTIO_BLK_S_OK ? DISK_RET_SUCCESS : DISK_RET_EBADTRACK; } int -process_virtio_blk_op(struct disk_op_s *op) +virtio_blk_process_op(struct disk_op_s *op) { if (! CONFIG_VIRTIO_BLK) return 0; @@ -87,14 +87,8 @@ process_virtio_blk_op(struct disk_op_s *op) return virtio_blk_op(op, 0); case CMD_WRITE: return virtio_blk_op(op, 1); - case CMD_FORMAT: - case CMD_RESET: - case CMD_ISREADY: - case CMD_VERIFY: - case CMD_SEEK: - return DISK_RET_SUCCESS; default: - return DISK_RET_EPARAM; + return default_process_op(op); } } @@ -102,6 +96,7 @@ static void init_virtio_blk(struct pci_device *pci) { u16 bdf = pci->bdf; + u8 status = VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER; dprintf(1, "found virtio-blk at %x:%x\n", pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); struct virtiodrive_s *vdrive = malloc_fseg(sizeof(*vdrive)); @@ -113,47 +108,93 @@ init_virtio_blk(struct pci_device *pci) vdrive->drive.type = DTYPE_VIRTIO_BLK; vdrive->drive.cntl_id = bdf; - u16 ioaddr = vp_init_simple(bdf); - vdrive->ioaddr = ioaddr; - if (vp_find_vq(ioaddr, 0, &vdrive->vq) < 0 ) { + vp_init_simple(&vdrive->vp, pci); + if (vp_find_vq(&vdrive->vp, 0, &vdrive->vq) < 0 ) { dprintf(1, "fail to find vq for virtio-blk %x:%x\n", pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); goto fail; } - struct virtio_blk_config cfg; - vp_get(ioaddr, 0, &cfg, sizeof(cfg)); - - u32 f = vp_get_features(ioaddr); - vdrive->drive.blksize = (f & (1 << VIRTIO_BLK_F_BLK_SIZE)) ? - cfg.blk_size : DISK_SECTOR_SIZE; - - vdrive->drive.sectors = cfg.capacity; - dprintf(3, "virtio-blk %x:%x blksize=%d sectors=%u\n", - pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), - vdrive->drive.blksize, (u32)vdrive->drive.sectors); - - if (vdrive->drive.blksize != DISK_SECTOR_SIZE) { - dprintf(1, "virtio-blk %x:%x block size %d is unsupported\n", + if (vdrive->vp.use_modern) { + struct vp_device *vp = &vdrive->vp; + u64 features = vp_get_features(vp); + u64 version1 = 1ull << VIRTIO_F_VERSION_1; + u64 blk_size = 1ull << VIRTIO_BLK_F_BLK_SIZE; + if (!(features & version1)) { + dprintf(1, "modern device without virtio_1 feature bit: %x:%x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); + goto fail; + } + + features = features & (version1 | blk_size); + vp_set_features(vp, features); + status |= VIRTIO_CONFIG_S_FEATURES_OK; + vp_set_status(vp, status); + if (!(vp_get_status(vp) & VIRTIO_CONFIG_S_FEATURES_OK)) { + dprintf(1, "device didn't accept features: %x:%x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); + goto fail; + } + + vdrive->drive.sectors = + vp_read(&vp->device, struct virtio_blk_config, capacity); + if (features & blk_size) { + vdrive->drive.blksize = + vp_read(&vp->device, struct virtio_blk_config, blk_size); + } else { + vdrive->drive.blksize = DISK_SECTOR_SIZE; + } + if (vdrive->drive.blksize != DISK_SECTOR_SIZE) { + dprintf(1, "virtio-blk %x:%x block size %d is unsupported\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + vdrive->drive.blksize); + goto fail; + } + dprintf(3, "virtio-blk %x:%x blksize=%d sectors=%u\n", pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), - vdrive->drive.blksize); - goto fail; + vdrive->drive.blksize, (u32)vdrive->drive.sectors); + + vdrive->drive.pchs.cylinder = + vp_read(&vp->device, struct virtio_blk_config, cylinders); + vdrive->drive.pchs.head = + vp_read(&vp->device, struct virtio_blk_config, heads); + vdrive->drive.pchs.sector = + vp_read(&vp->device, struct virtio_blk_config, sectors); + } else { + struct virtio_blk_config cfg; + vp_get_legacy(&vdrive->vp, 0, &cfg, sizeof(cfg)); + + u64 f = vp_get_features(&vdrive->vp); + vdrive->drive.blksize = (f & (1 << VIRTIO_BLK_F_BLK_SIZE)) ? + cfg.blk_size : DISK_SECTOR_SIZE; + + vdrive->drive.sectors = cfg.capacity; + dprintf(3, "virtio-blk %x:%x blksize=%d sectors=%u\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + vdrive->drive.blksize, (u32)vdrive->drive.sectors); + + if (vdrive->drive.blksize != DISK_SECTOR_SIZE) { + dprintf(1, "virtio-blk %x:%x block size %d is unsupported\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + vdrive->drive.blksize); + goto fail; + } + vdrive->drive.pchs.cylinder = cfg.cylinders; + vdrive->drive.pchs.head = cfg.heads; + vdrive->drive.pchs.sector = cfg.sectors; } - vdrive->drive.pchs.cylinder = cfg.cylinders; - vdrive->drive.pchs.head = cfg.heads; - vdrive->drive.pchs.sector = cfg.sectors; char *desc = znprintf(MAXDESCSIZE, "Virtio disk PCI:%x:%x", pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); boot_add_hd(&vdrive->drive, desc, bootprio_find_pci_device(pci)); - vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE | - VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK); + status |= VIRTIO_CONFIG_S_DRIVER_OK; + vp_set_status(&vdrive->vp, status); return; fail: - vp_reset(ioaddr); + vp_reset(&vdrive->vp); free(vdrive->vq); free(vdrive); } @@ -169,8 +210,9 @@ virtio_blk_setup(void) struct pci_device *pci; foreachpci(pci) { - if (pci->vendor != PCI_VENDOR_ID_REDHAT_QUMRANET - || pci->device != PCI_DEVICE_ID_VIRTIO_BLK) + if (pci->vendor != PCI_VENDOR_ID_REDHAT_QUMRANET || + (pci->device != PCI_DEVICE_ID_VIRTIO_BLK_09 && + pci->device != PCI_DEVICE_ID_VIRTIO_BLK_10)) continue; init_virtio_blk(pci); } diff --git a/qemu/roms/seabios/src/hw/virtio-blk.h b/qemu/roms/seabios/src/hw/virtio-blk.h index b233c744b..157bed627 100644 --- a/qemu/roms/seabios/src/hw/virtio-blk.h +++ b/qemu/roms/seabios/src/hw/virtio-blk.h @@ -37,7 +37,7 @@ struct virtio_blk_outhdr { #define VIRTIO_BLK_S_UNSUPP 2 struct disk_op_s; -int process_virtio_blk_op(struct disk_op_s *op); +int virtio_blk_process_op(struct disk_op_s *op); void virtio_blk_setup(void); #endif /* _VIRTIO_BLK_H */ diff --git a/qemu/roms/seabios/src/hw/virtio-pci.c b/qemu/roms/seabios/src/hw/virtio-pci.c index b9b3ab1e3..6df519489 100644 --- a/qemu/roms/seabios/src/hw/virtio-pci.c +++ b/qemu/roms/seabios/src/hw/virtio-pci.c @@ -24,47 +24,153 @@ #include "virtio-pci.h" #include "virtio-ring.h" -int vp_find_vq(unsigned int ioaddr, int queue_index, +u64 vp_get_features(struct vp_device *vp) +{ + u32 f0, f1; + + if (vp->use_modern) { + vp_write(&vp->common, virtio_pci_common_cfg, device_feature_select, 0); + f0 = vp_read(&vp->common, virtio_pci_common_cfg, device_feature); + vp_write(&vp->common, virtio_pci_common_cfg, device_feature_select, 1); + f1 = vp_read(&vp->common, virtio_pci_common_cfg, device_feature); + } else { + f0 = vp_read(&vp->legacy, virtio_pci_legacy, host_features); + f1 = 0; + } + return ((u64)f1 << 32) | f0; +} + +void vp_set_features(struct vp_device *vp, u64 features) +{ + u32 f0, f1; + + f0 = features; + f1 = features >> 32; + + if (vp->use_modern) { + vp_write(&vp->common, virtio_pci_common_cfg, guest_feature_select, 0); + vp_write(&vp->common, virtio_pci_common_cfg, guest_feature, f0); + vp_write(&vp->common, virtio_pci_common_cfg, guest_feature_select, 1); + vp_write(&vp->common, virtio_pci_common_cfg, guest_feature, f1); + } else { + vp_write(&vp->legacy, virtio_pci_legacy, guest_features, f0); + } +} + +u8 vp_get_status(struct vp_device *vp) +{ + if (vp->use_modern) { + return vp_read(&vp->common, virtio_pci_common_cfg, device_status); + } else { + return vp_read(&vp->legacy, virtio_pci_legacy, status); + } +} + +void vp_set_status(struct vp_device *vp, u8 status) +{ + if (status == 0) /* reset */ + return; + if (vp->use_modern) { + vp_write(&vp->common, virtio_pci_common_cfg, device_status, status); + } else { + vp_write(&vp->legacy, virtio_pci_legacy, status, status); + } +} + +u8 vp_get_isr(struct vp_device *vp) +{ + if (vp->use_modern) { + return vp_read(&vp->isr, virtio_pci_isr, isr); + } else { + return vp_read(&vp->legacy, virtio_pci_legacy, isr); + } +} + +void vp_reset(struct vp_device *vp) +{ + if (vp->use_modern) { + vp_write(&vp->common, virtio_pci_common_cfg, device_status, 0); + vp_read(&vp->isr, virtio_pci_isr, isr); + } else { + vp_write(&vp->legacy, virtio_pci_legacy, status, 0); + vp_read(&vp->legacy, virtio_pci_legacy, isr); + } +} + +void vp_notify(struct vp_device *vp, struct vring_virtqueue *vq) +{ + if (vp->use_modern) { + u32 addr = vp->notify.addr + + vq->queue_notify_off * + vp->notify_off_multiplier; + if (vp->notify.is_io) { + outw(vq->queue_index, addr); + } else { + writew((void*)addr, vq->queue_index); + } + dprintf(9, "vp notify %x (%d) -- 0x%x\n", + addr, 2, vq->queue_index); + } else { + vp_write(&vp->legacy, virtio_pci_legacy, queue_notify, vq->queue_index); + } +} + +int vp_find_vq(struct vp_device *vp, int queue_index, struct vring_virtqueue **p_vq) { u16 num; ASSERT32FLAT(); - struct vring_virtqueue *vq = *p_vq = memalign_low(PAGE_SIZE, sizeof(*vq)); + struct vring_virtqueue *vq = *p_vq = memalign_high(PAGE_SIZE, sizeof(*vq)); if (!vq) { warn_noalloc(); goto fail; } memset(vq, 0, sizeof(*vq)); - /* select the queue */ - outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_SEL); + /* select the queue */ + if (vp->use_modern) { + vp_write(&vp->common, virtio_pci_common_cfg, queue_select, queue_index); + } else { + vp_write(&vp->legacy, virtio_pci_legacy, queue_sel, queue_index); + } /* check if the queue is available */ - - num = inw(ioaddr + VIRTIO_PCI_QUEUE_NUM); + if (vp->use_modern) { + num = vp_read(&vp->common, virtio_pci_common_cfg, queue_size); + if (num > MAX_QUEUE_NUM) { + vp_write(&vp->common, virtio_pci_common_cfg, queue_size, + MAX_QUEUE_NUM); + num = vp_read(&vp->common, virtio_pci_common_cfg, queue_size); + } + } else { + num = vp_read(&vp->legacy, virtio_pci_legacy, queue_num); + } if (!num) { dprintf(1, "ERROR: queue size is 0\n"); goto fail; } - if (num > MAX_QUEUE_NUM) { dprintf(1, "ERROR: queue size %d > %d\n", num, MAX_QUEUE_NUM); goto fail; } /* check if the queue is already active */ - - if (inl(ioaddr + VIRTIO_PCI_QUEUE_PFN)) { - dprintf(1, "ERROR: queue already active\n"); - goto fail; + if (vp->use_modern) { + if (vp_read(&vp->common, virtio_pci_common_cfg, queue_enable)) { + dprintf(1, "ERROR: queue already active\n"); + goto fail; + } + } else { + if (vp_read(&vp->legacy, virtio_pci_legacy, queue_pfn)) { + dprintf(1, "ERROR: queue already active\n"); + goto fail; + } } - vq->queue_index = queue_index; /* initialize the queue */ - struct vring * vr = &vq->vring; vring_init(vr, num, (unsigned char*)&vq->queue); @@ -73,9 +179,23 @@ int vp_find_vq(unsigned int ioaddr, int queue_index, * NOTE: vr->desc is initialized by vring_init() */ - outl((unsigned long)virt_to_phys(vr->desc) >> PAGE_SHIFT, - ioaddr + VIRTIO_PCI_QUEUE_PFN); - + if (vp->use_modern) { + vp_write(&vp->common, virtio_pci_common_cfg, queue_desc_lo, + (unsigned long)virt_to_phys(vr->desc)); + vp_write(&vp->common, virtio_pci_common_cfg, queue_desc_hi, 0); + vp_write(&vp->common, virtio_pci_common_cfg, queue_avail_lo, + (unsigned long)virt_to_phys(vr->avail)); + vp_write(&vp->common, virtio_pci_common_cfg, queue_avail_hi, 0); + vp_write(&vp->common, virtio_pci_common_cfg, queue_used_lo, + (unsigned long)virt_to_phys(vr->used)); + vp_write(&vp->common, virtio_pci_common_cfg, queue_used_hi, 0); + vp_write(&vp->common, virtio_pci_common_cfg, queue_enable, 1); + vq->queue_notify_off = vp_read(&vp->common, virtio_pci_common_cfg, + queue_notify_off); + } else { + vp_write(&vp->legacy, virtio_pci_legacy, queue_pfn, + (unsigned long)virt_to_phys(vr->desc) >> PAGE_SHIFT); + } return num; fail: @@ -84,14 +204,76 @@ fail: return -1; } -u16 vp_init_simple(u16 bdf) +void vp_init_simple(struct vp_device *vp, struct pci_device *pci) { - u16 ioaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0) & - PCI_BASE_ADDRESS_IO_MASK; + u8 cap = pci_find_capability(pci, PCI_CAP_ID_VNDR, 0); + struct vp_cap *vp_cap; + u32 addr, offset, mul; + u8 type; + + memset(vp, 0, sizeof(*vp)); + while (cap != 0) { + type = pci_config_readb(pci->bdf, cap + + offsetof(struct virtio_pci_cap, cfg_type)); + switch (type) { + case VIRTIO_PCI_CAP_COMMON_CFG: + vp_cap = &vp->common; + break; + case VIRTIO_PCI_CAP_NOTIFY_CFG: + vp_cap = &vp->notify; + mul = offsetof(struct virtio_pci_notify_cap, notify_off_multiplier); + vp->notify_off_multiplier = pci_config_readl(pci->bdf, cap + mul); + break; + case VIRTIO_PCI_CAP_ISR_CFG: + vp_cap = &vp->isr; + break; + case VIRTIO_PCI_CAP_DEVICE_CFG: + vp_cap = &vp->device; + break; + default: + vp_cap = NULL; + break; + } + if (vp_cap && !vp_cap->cap) { + vp_cap->cap = cap; + vp_cap->bar = pci_config_readb(pci->bdf, cap + + offsetof(struct virtio_pci_cap, bar)); + offset = pci_config_readl(pci->bdf, cap + + offsetof(struct virtio_pci_cap, offset)); + addr = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0 + 4 * vp_cap->bar); + if (addr & PCI_BASE_ADDRESS_SPACE_IO) { + vp_cap->is_io = 1; + addr &= PCI_BASE_ADDRESS_IO_MASK; + } else { + vp_cap->is_io = 0; + addr &= PCI_BASE_ADDRESS_MEM_MASK; + } + vp_cap->addr = addr + offset; + dprintf(3, "pci dev %x:%x virtio cap at 0x%x type %d " + "bar %d at 0x%08x off +0x%04x [%s]\n", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + vp_cap->cap, type, vp_cap->bar, addr, offset, + vp_cap->is_io ? "io" : "mmio"); + } + + cap = pci_find_capability(pci, PCI_CAP_ID_VNDR, cap); + } + + if (vp->common.cap && vp->notify.cap && vp->isr.cap && vp->device.cap) { + dprintf(1, "pci dev %x:%x using modern (1.0) virtio mode\n", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf)); + vp->use_modern = 1; + } else { + dprintf(1, "pci dev %x:%x using legacy (0.9.5) virtio mode\n", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf)); + vp->legacy.bar = 0; + vp->legacy.addr = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) & + PCI_BASE_ADDRESS_IO_MASK; + vp->legacy.is_io = 1; + } - vp_reset(ioaddr); - pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); - vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE | + vp_reset(vp); + pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + vp_set_status(vp, VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER ); - return ioaddr; } diff --git a/qemu/roms/seabios/src/hw/virtio-pci.h b/qemu/roms/seabios/src/hw/virtio-pci.h index bc04b039e..b11c3555e 100644 --- a/qemu/roms/seabios/src/hw/virtio-pci.h +++ b/qemu/roms/seabios/src/hw/virtio-pci.h @@ -2,104 +2,210 @@ #define _VIRTIO_PCI_H #include "x86.h" // inl - -/* A 32-bit r/o bitmask of the features supported by the host */ -#define VIRTIO_PCI_HOST_FEATURES 0 - -/* A 32-bit r/w bitmask of features activated by the guest */ -#define VIRTIO_PCI_GUEST_FEATURES 4 - -/* A 32-bit r/w PFN for the currently selected queue */ -#define VIRTIO_PCI_QUEUE_PFN 8 - -/* A 16-bit r/o queue size for the currently selected queue */ -#define VIRTIO_PCI_QUEUE_NUM 12 - -/* A 16-bit r/w queue selector */ -#define VIRTIO_PCI_QUEUE_SEL 14 - -/* A 16-bit r/w queue notifier */ -#define VIRTIO_PCI_QUEUE_NOTIFY 16 - -/* An 8-bit device status register. */ -#define VIRTIO_PCI_STATUS 18 - -/* An 8-bit r/o interrupt status register. Reading the value will return the - * current contents of the ISR and will also clear it. This is effectively - * a read-and-acknowledge. */ -#define VIRTIO_PCI_ISR 19 +#include "biosvar.h" // GET_LOWFLAT /* The bit of the ISR which indicates a device configuration change. */ #define VIRTIO_PCI_ISR_CONFIG 0x2 -/* The remaining space is defined by each driver as the per-driver - * configuration space */ -#define VIRTIO_PCI_CONFIG 20 - /* Virtio ABI version, this must match exactly */ #define VIRTIO_PCI_ABI_VERSION 0 -static inline u32 vp_get_features(unsigned int ioaddr) -{ - return inl(ioaddr + VIRTIO_PCI_HOST_FEATURES); -} - -static inline void vp_set_features(unsigned int ioaddr, u32 features) +/* --- virtio 0.9.5 (legacy) struct --------------------------------- */ + +typedef struct virtio_pci_legacy { + u32 host_features; + u32 guest_features; + u32 queue_pfn; + u16 queue_num; + u16 queue_sel; + u16 queue_notify; + u8 status; + u8 isr; + u8 device[]; +} virtio_pci_legacy; + +/* --- virtio 1.0 (modern) structs ---------------------------------- */ + +/* Common configuration */ +#define VIRTIO_PCI_CAP_COMMON_CFG 1 +/* Notifications */ +#define VIRTIO_PCI_CAP_NOTIFY_CFG 2 +/* ISR access */ +#define VIRTIO_PCI_CAP_ISR_CFG 3 +/* Device specific configuration */ +#define VIRTIO_PCI_CAP_DEVICE_CFG 4 +/* PCI configuration access */ +#define VIRTIO_PCI_CAP_PCI_CFG 5 + +/* This is the PCI capability header: */ +struct virtio_pci_cap { + u8 cap_vndr; /* Generic PCI field: PCI_CAP_ID_VNDR */ + u8 cap_next; /* Generic PCI field: next ptr. */ + u8 cap_len; /* Generic PCI field: capability length */ + u8 cfg_type; /* Identifies the structure. */ + u8 bar; /* Where to find it. */ + u8 padding[3]; /* Pad to full dword. */ + u32 offset; /* Offset within bar. */ + u32 length; /* Length of the structure, in bytes. */ +}; + +struct virtio_pci_notify_cap { + struct virtio_pci_cap cap; + u32 notify_off_multiplier; /* Multiplier for queue_notify_off. */ +}; + +typedef struct virtio_pci_common_cfg { + /* About the whole device. */ + u32 device_feature_select; /* read-write */ + u32 device_feature; /* read-only */ + u32 guest_feature_select; /* read-write */ + u32 guest_feature; /* read-write */ + u16 msix_config; /* read-write */ + u16 num_queues; /* read-only */ + u8 device_status; /* read-write */ + u8 config_generation; /* read-only */ + + /* About a specific virtqueue. */ + u16 queue_select; /* read-write */ + u16 queue_size; /* read-write, power of 2. */ + u16 queue_msix_vector; /* read-write */ + u16 queue_enable; /* read-write */ + u16 queue_notify_off; /* read-only */ + u32 queue_desc_lo; /* read-write */ + u32 queue_desc_hi; /* read-write */ + u32 queue_avail_lo; /* read-write */ + u32 queue_avail_hi; /* read-write */ + u32 queue_used_lo; /* read-write */ + u32 queue_used_hi; /* read-write */ +} virtio_pci_common_cfg; + +typedef struct virtio_pci_isr { + u8 isr; +} virtio_pci_isr; + +/* --- driver structs ----------------------------------------------- */ + +struct vp_cap { + u32 addr; + u8 cap; + u8 bar; + u8 is_io; +}; + +struct vp_device { + struct vp_cap common, notify, isr, device, legacy; + u32 notify_off_multiplier; + u8 use_modern; +}; + +static inline u64 _vp_read(struct vp_cap *cap, u32 offset, u8 size) { - outl(features, ioaddr + VIRTIO_PCI_GUEST_FEATURES); + u32 addr = cap->addr + offset; + u64 var; + + if (cap->is_io) { + switch (size) { + case 8: + var = inl(addr); + var |= (u64)inl(addr+4) << 32; + break; + case 4: + var = inl(addr); + break; + case 2: + var = inw(addr); + break; + case 1: + var = inb(addr); + break; + default: + var = 0; + } + } else { + switch (size) { + case 8: + var = readl((void*)addr); + var |= (u64)readl((void*)(addr+4)) << 32; + break; + case 4: + var = readl((void*)addr); + break; + case 2: + var = readw((void*)addr); + break; + case 1: + var = readb((void*)addr); + break; + default: + var = 0; + } + } + dprintf(9, "vp read %x (%d) -> 0x%llx\n", addr, size, var); + return var; } -static inline void vp_get(unsigned int ioaddr, unsigned offset, - void *buf, unsigned len) +static inline void _vp_write(struct vp_cap *cap, u32 offset, u8 size, u64 var) { - u8 *ptr = buf; - unsigned i; - - for (i = 0; i < len; i++) - ptr[i] = inb(ioaddr + VIRTIO_PCI_CONFIG + offset + i); + u32 addr = cap->addr + offset; + + dprintf(9, "vp write %x (%d) <- 0x%llx\n", addr, size, var); + if (cap->is_io) { + switch (size) { + case 4: + outl(var, addr); + break; + case 2: + outw(var, addr); + break; + case 1: + outb(var, addr); + break; + } + } else { + switch (size) { + case 4: + writel((void*)addr, var); + break; + case 2: + writew((void*)addr, var); + break; + case 1: + writeb((void*)addr, var); + break; + } + } } -static inline u8 vp_get_status(unsigned int ioaddr) -{ - return inb(ioaddr + VIRTIO_PCI_STATUS); -} +#define vp_read(_cap, _struct, _field) \ + _vp_read(_cap, offsetof(_struct, _field), \ + sizeof(((_struct *)0)->_field)) -static inline void vp_set_status(unsigned int ioaddr, u8 status) -{ - if (status == 0) /* reset */ - return; - outb(status, ioaddr + VIRTIO_PCI_STATUS); -} +#define vp_write(_cap, _struct, _field, _var) \ + _vp_write(_cap, offsetof(_struct, _field), \ + sizeof(((_struct *)0)->_field), _var) -static inline u8 vp_get_isr(unsigned int ioaddr) -{ - return inb(ioaddr + VIRTIO_PCI_ISR); -} +u64 vp_get_features(struct vp_device *vp); +void vp_set_features(struct vp_device *vp, u64 features); -static inline void vp_reset(unsigned int ioaddr) +static inline void vp_get_legacy(struct vp_device *vp, unsigned offset, + void *buf, unsigned len) { - outb(0, ioaddr + VIRTIO_PCI_STATUS); - (void)inb(ioaddr + VIRTIO_PCI_ISR); -} + u8 *ptr = buf; + unsigned i; -static inline void vp_notify(unsigned int ioaddr, int queue_index) -{ - outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_NOTIFY); + for (i = 0; i < len; i++) + ptr[i] = vp_read(&vp->legacy, virtio_pci_legacy, device[i]); } -static inline void vp_del_vq(unsigned int ioaddr, int queue_index) -{ - /* select the queue */ - - outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_SEL); - - /* deactivate the queue */ - - outl(0, ioaddr + VIRTIO_PCI_QUEUE_PFN); -} +u8 vp_get_status(struct vp_device *vp); +void vp_set_status(struct vp_device *vp, u8 status); +u8 vp_get_isr(struct vp_device *vp); +void vp_reset(struct vp_device *vp); +struct pci_device; struct vring_virtqueue; -u16 vp_init_simple(u16 bdf); -int vp_find_vq(unsigned int ioaddr, int queue_index, +void vp_init_simple(struct vp_device *vp, struct pci_device *pci); +void vp_notify(struct vp_device *vp, struct vring_virtqueue *vq); +int vp_find_vq(struct vp_device *vp, int queue_index, struct vring_virtqueue **p_vq); #endif /* _VIRTIO_PCI_H_ */ diff --git a/qemu/roms/seabios/src/hw/virtio-ring.c b/qemu/roms/seabios/src/hw/virtio-ring.c index 97e0b3487..7205a0acd 100644 --- a/qemu/roms/seabios/src/hw/virtio-ring.c +++ b/qemu/roms/seabios/src/hw/virtio-ring.c @@ -35,8 +35,8 @@ int vring_more_used(struct vring_virtqueue *vq) { - struct vring_used *used = GET_LOWFLAT(vq->vring.used); - int more = GET_LOWFLAT(vq->last_used_idx) != GET_LOWFLAT(used->idx); + struct vring_used *used = vq->vring.used; + int more = vq->last_used_idx != used->idx; /* Make sure ring reads are done after idx read above. */ smp_rmb(); return more; @@ -57,13 +57,13 @@ void vring_detach(struct vring_virtqueue *vq, unsigned int head) /* find end of given descriptor */ i = head; - while (GET_LOWFLAT(desc[i].flags) & VRING_DESC_F_NEXT) - i = GET_LOWFLAT(desc[i].next); + while (desc[i].flags & VRING_DESC_F_NEXT) + i = desc[i].next; /* link it with free list and point to it */ - SET_LOWFLAT(desc[i].next, GET_LOWFLAT(vq->free_head)); - SET_LOWFLAT(vq->free_head, head); + desc[i].next = vq->free_head; + vq->free_head = head; } /* @@ -77,22 +77,22 @@ int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len) { struct vring *vr = &vq->vring; struct vring_used_elem *elem; - struct vring_used *used = GET_LOWFLAT(vq->vring.used); + struct vring_used *used = vq->vring.used; u32 id; int ret; // BUG_ON(!vring_more_used(vq)); - elem = &used->ring[GET_LOWFLAT(vq->last_used_idx) % GET_LOWFLAT(vr->num)]; - id = GET_LOWFLAT(elem->id); + elem = &used->ring[vq->last_used_idx % vr->num]; + id = elem->id; if (len != NULL) - *len = GET_LOWFLAT(elem->len); + *len = elem->len; - ret = GET_LOWFLAT(vq->vdata[id]); + ret = vq->vdata[id]; vring_detach(vq, id); - SET_LOWFLAT(vq->last_used_idx, GET_LOWFLAT(vq->last_used_idx) + 1); + vq->last_used_idx = vq->last_used_idx + 1; return ret; } @@ -104,46 +104,45 @@ void vring_add_buf(struct vring_virtqueue *vq, { struct vring *vr = &vq->vring; int i, av, head, prev; - struct vring_desc *desc = GET_LOWFLAT(vr->desc); - struct vring_avail *avail = GET_LOWFLAT(vr->avail); + struct vring_desc *desc = vr->desc; + struct vring_avail *avail = vr->avail; BUG_ON(out + in == 0); prev = 0; - head = GET_LOWFLAT(vq->free_head); - for (i = head; out; i = GET_LOWFLAT(desc[i].next), out--) { - SET_LOWFLAT(desc[i].flags, VRING_DESC_F_NEXT); - SET_LOWFLAT(desc[i].addr, (u64)virt_to_phys(list->addr)); - SET_LOWFLAT(desc[i].len, list->length); + head = vq->free_head; + for (i = head; out; i = desc[i].next, out--) { + desc[i].flags = VRING_DESC_F_NEXT; + desc[i].addr = (u64)virt_to_phys(list->addr); + desc[i].len = list->length; prev = i; list++; } - for ( ; in; i = GET_LOWFLAT(desc[i].next), in--) { - SET_LOWFLAT(desc[i].flags, VRING_DESC_F_NEXT|VRING_DESC_F_WRITE); - SET_LOWFLAT(desc[i].addr, (u64)virt_to_phys(list->addr)); - SET_LOWFLAT(desc[i].len, list->length); + for ( ; in; i = desc[i].next, in--) { + desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE; + desc[i].addr = (u64)virt_to_phys(list->addr); + desc[i].len = list->length; prev = i; list++; } - SET_LOWFLAT(desc[prev].flags, - GET_LOWFLAT(desc[prev].flags) & ~VRING_DESC_F_NEXT); + desc[prev].flags = desc[prev].flags & ~VRING_DESC_F_NEXT; - SET_LOWFLAT(vq->free_head, i); + vq->free_head = i; - SET_LOWFLAT(vq->vdata[head], index); + vq->vdata[head] = index; - av = (GET_LOWFLAT(avail->idx) + num_added) % GET_LOWFLAT(vr->num); - SET_LOWFLAT(avail->ring[av], head); + av = (avail->idx + num_added) % vr->num; + avail->ring[av] = head; } -void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added) +void vring_kick(struct vp_device *vp, struct vring_virtqueue *vq, int num_added) { struct vring *vr = &vq->vring; - struct vring_avail *avail = GET_LOWFLAT(vr->avail); + struct vring_avail *avail = vr->avail; /* Make sure idx update is done after ring write. */ smp_wmb(); - SET_LOWFLAT(avail->idx, GET_LOWFLAT(avail->idx) + num_added); + avail->idx = avail->idx + num_added; - vp_notify(ioaddr, GET_LOWFLAT(vq->queue_index)); + vp_notify(vp, vq); } diff --git a/qemu/roms/seabios/src/hw/virtio-ring.h b/qemu/roms/seabios/src/hw/virtio-ring.h index b7a7aafb2..7665fd54b 100644 --- a/qemu/roms/seabios/src/hw/virtio-ring.h +++ b/qemu/roms/seabios/src/hw/virtio-ring.h @@ -4,15 +4,6 @@ #include "types.h" // u64 #include "memmap.h" // PAGE_SIZE -#define PAGE_SHIFT 12 -#define PAGE_MASK (PAGE_SIZE-1) - -#define virt_to_phys(v) (unsigned long)(v) -#define phys_to_virt(p) (void*)(p) -/* Compiler barrier is enough as an x86 CPU does not reorder reads or writes */ -#define smp_rmb() barrier() -#define smp_wmb() barrier() - /* Status byte for guest to report progress, and synchronize features. */ /* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */ #define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 @@ -20,9 +11,14 @@ #define VIRTIO_CONFIG_S_DRIVER 2 /* Driver has used its parts of the config, and is happy */ #define VIRTIO_CONFIG_S_DRIVER_OK 4 +/* Driver has finished configuring features */ +#define VIRTIO_CONFIG_S_FEATURES_OK 8 /* We've given up on this device. */ #define VIRTIO_CONFIG_S_FAILED 0x80 +/* v1.0 compliant. */ +#define VIRTIO_F_VERSION_1 32 + #define MAX_QUEUE_NUM (128) #define VRING_DESC_F_NEXT 1 @@ -68,10 +64,9 @@ struct vring { }; #define vring_size(num) \ - (((((sizeof(struct vring_desc) * num) + \ - (sizeof(struct vring_avail) + sizeof(u16) * num)) \ - + PAGE_MASK) & ~PAGE_MASK) + \ - (sizeof(struct vring_used) + sizeof(struct vring_used_elem) * num)) + (ALIGN(sizeof(struct vring_desc) * num + sizeof(struct vring_avail) \ + + sizeof(u16) * num, PAGE_SIZE) \ + + sizeof(struct vring_used) + sizeof(struct vring_used_elem) * num) typedef unsigned char virtio_queue_t[vring_size(MAX_QUEUE_NUM)]; @@ -83,6 +78,7 @@ struct vring_virtqueue { u16 vdata[MAX_QUEUE_NUM]; /* PCI */ int queue_index; + int queue_notify_off; }; struct vring_list { @@ -90,42 +86,35 @@ struct vring_list { unsigned int length; }; -static inline void vring_init(struct vring *vr, - unsigned int num, unsigned char *queue) +static inline void +vring_init(struct vring *vr, unsigned int num, unsigned char *queue) { - unsigned int i; - unsigned long pa; - ASSERT32FLAT(); vr->num = num; /* physical address of desc must be page aligned */ - - pa = virt_to_phys(queue); - pa = (pa + PAGE_MASK) & ~PAGE_MASK; - vr->desc = phys_to_virt(pa); + vr->desc = (void*)ALIGN((u32)queue, PAGE_SIZE); vr->avail = (struct vring_avail *)&vr->desc[num]; /* disable interrupts */ vr->avail->flags |= VRING_AVAIL_F_NO_INTERRUPT; /* physical address of used must be page aligned */ + vr->used = (void*)ALIGN((u32)&vr->avail->ring[num], PAGE_SIZE); - pa = virt_to_phys(&vr->avail->ring[num]); - pa = (pa + PAGE_MASK) & ~PAGE_MASK; - vr->used = phys_to_virt(pa); - + int i; for (i = 0; i < num - 1; i++) - vr->desc[i].next = i + 1; + vr->desc[i].next = i + 1; vr->desc[i].next = 0; } +struct vp_device; int vring_more_used(struct vring_virtqueue *vq); void vring_detach(struct vring_virtqueue *vq, unsigned int head); int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len); void vring_add_buf(struct vring_virtqueue *vq, struct vring_list list[], unsigned int out, unsigned int in, int index, int num_added); -void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added); +void vring_kick(struct vp_device *vp, struct vring_virtqueue *vq, int num_added); #endif /* _VIRTIO_RING_H_ */ diff --git a/qemu/roms/seabios/src/hw/virtio-scsi.c b/qemu/roms/seabios/src/hw/virtio-scsi.c index 8f966875b..80afd04ca 100644 --- a/qemu/roms/seabios/src/hw/virtio-scsi.c +++ b/qemu/roms/seabios/src/hw/virtio-scsi.c @@ -27,35 +27,42 @@ struct virtio_lun_s { struct drive_s drive; struct pci_device *pci; struct vring_virtqueue *vq; - u16 ioaddr; + struct vp_device *vp; u16 target; u16 lun; }; -static int -virtio_scsi_cmd(u16 ioaddr, struct vring_virtqueue *vq, struct disk_op_s *op, - void *cdbcmd, u16 target, u16 lun, u16 blocksize) +int +virtio_scsi_process_op(struct disk_op_s *op) { + if (! CONFIG_VIRTIO_SCSI) + return 0; + struct virtio_lun_s *vlun = + container_of(op->drive_gf, struct virtio_lun_s, drive); + struct vp_device *vp = vlun->vp; + struct vring_virtqueue *vq = vlun->vq; struct virtio_scsi_req_cmd req; struct virtio_scsi_resp_cmd resp; struct vring_list sg[3]; memset(&req, 0, sizeof(req)); + int blocksize = scsi_fill_cmd(op, req.cdb, 16); + if (blocksize < 0) + return default_process_op(op); req.lun[0] = 1; - req.lun[1] = target; - req.lun[2] = (lun >> 8) | 0x40; - req.lun[3] = (lun & 0xff); - memcpy(req.cdb, cdbcmd, 16); + req.lun[1] = vlun->target; + req.lun[2] = (vlun->lun >> 8) | 0x40; + req.lun[3] = (vlun->lun & 0xff); u32 len = op->count * blocksize; - int datain = cdb_is_read(cdbcmd, blocksize); + int datain = scsi_is_read(op); int in_num = (datain ? 2 : 1); int out_num = (len ? 3 : 2) - in_num; - sg[0].addr = MAKE_FLATPTR(GET_SEG(SS), &req); + sg[0].addr = (void*)(&req); sg[0].length = sizeof(req); - sg[out_num].addr = MAKE_FLATPTR(GET_SEG(SS), &resp); + sg[out_num].addr = (void*)(&resp); sg[out_num].length = sizeof(resp); if (len) { @@ -66,7 +73,7 @@ virtio_scsi_cmd(u16 ioaddr, struct vring_virtqueue *vq, struct disk_op_s *op, /* Add to virtqueue and kick host */ vring_add_buf(vq, sg, out_num, in_num, 0, 0); - vring_kick(ioaddr, vq, 1); + vring_kick(vp, vq, 1); /* Wait for reply */ while (!vring_more_used(vq)) @@ -78,7 +85,7 @@ virtio_scsi_cmd(u16 ioaddr, struct vring_virtqueue *vq, struct disk_op_s *op, /* Clear interrupt status register. Avoid leaving interrupts stuck if * VRING_AVAIL_F_NO_INTERRUPT was ignored and interrupts were raised. */ - vp_get_isr(ioaddr); + vp_get_isr(vp); if (resp.response == VIRTIO_SCSI_S_OK && resp.status == 0) { return DISK_RET_SUCCESS; @@ -86,21 +93,8 @@ virtio_scsi_cmd(u16 ioaddr, struct vring_virtqueue *vq, struct disk_op_s *op, return DISK_RET_EBADTRACK; } -int -virtio_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) -{ - struct virtio_lun_s *vlun_gf = - container_of(op->drive_gf, struct virtio_lun_s, drive); - - return virtio_scsi_cmd(GET_GLOBALFLAT(vlun_gf->ioaddr), - GET_GLOBALFLAT(vlun_gf->vq), op, cdbcmd, - GET_GLOBALFLAT(vlun_gf->target), - GET_GLOBALFLAT(vlun_gf->lun), - blocksize); -} - static int -virtio_scsi_add_lun(struct pci_device *pci, u16 ioaddr, +virtio_scsi_add_lun(struct pci_device *pci, struct vp_device *vp, struct vring_virtqueue *vq, u16 target, u16 lun) { struct virtio_lun_s *vlun = malloc_fseg(sizeof(*vlun)); @@ -112,7 +106,7 @@ virtio_scsi_add_lun(struct pci_device *pci, u16 ioaddr, vlun->drive.type = DTYPE_VIRTIO_SCSI; vlun->drive.cntl_id = pci->bdf; vlun->pci = pci; - vlun->ioaddr = ioaddr; + vlun->vp = vp; vlun->vq = vq; vlun->target = target; vlun->lun = lun; @@ -129,11 +123,11 @@ fail: } static int -virtio_scsi_scan_target(struct pci_device *pci, u16 ioaddr, +virtio_scsi_scan_target(struct pci_device *pci, struct vp_device *vp, struct vring_virtqueue *vq, u16 target) { /* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */ - int ret = virtio_scsi_add_lun(pci, ioaddr, vq, target, 0); + int ret = virtio_scsi_add_lun(pci, vp, vq, target, 0); return ret < 0 ? 0 : 1; } @@ -144,19 +138,45 @@ init_virtio_scsi(struct pci_device *pci) dprintf(1, "found virtio-scsi at %x:%x\n", pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); struct vring_virtqueue *vq = NULL; - u16 ioaddr = vp_init_simple(bdf); - if (vp_find_vq(ioaddr, 2, &vq) < 0 ) { + struct vp_device *vp = malloc_high(sizeof(*vp)); + if (!vp) { + warn_noalloc(); + return; + } + vp_init_simple(vp, pci); + u8 status = VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER; + + if (vp->use_modern) { + u64 features = vp_get_features(vp); + u64 version1 = 1ull << VIRTIO_F_VERSION_1; + if (!(features & version1)) { + dprintf(1, "modern device without virtio_1 feature bit: %x:%x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); + goto fail; + } + + vp_set_features(vp, version1); + status |= VIRTIO_CONFIG_S_FEATURES_OK; + vp_set_status(vp, status); + if (!(vp_get_status(vp) & VIRTIO_CONFIG_S_FEATURES_OK)) { + dprintf(1, "device didn't accept features: %x:%x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); + goto fail; + } + } + + if (vp_find_vq(vp, 2, &vq) < 0 ) { dprintf(1, "fail to find vq for virtio-scsi %x:%x\n", pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); goto fail; } - vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE | - VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK); + status |= VIRTIO_CONFIG_S_DRIVER_OK; + vp_set_status(vp, status); int i, tot; for (tot = 0, i = 0; i < 256; i++) - tot += virtio_scsi_scan_target(pci, ioaddr, vq, i); + tot += virtio_scsi_scan_target(pci, vp, vq, i); if (!tot) goto fail; @@ -164,7 +184,8 @@ init_virtio_scsi(struct pci_device *pci) return; fail: - vp_reset(ioaddr); + vp_reset(vp); + free(vp); free(vq); } @@ -179,8 +200,9 @@ virtio_scsi_setup(void) struct pci_device *pci; foreachpci(pci) { - if (pci->vendor != PCI_VENDOR_ID_REDHAT_QUMRANET - || pci->device != PCI_DEVICE_ID_VIRTIO_SCSI) + if (pci->vendor != PCI_VENDOR_ID_REDHAT_QUMRANET || + (pci->device != PCI_DEVICE_ID_VIRTIO_SCSI_09 && + pci->device != PCI_DEVICE_ID_VIRTIO_SCSI_10)) continue; init_virtio_scsi(pci); } diff --git a/qemu/roms/seabios/src/hw/virtio-scsi.h b/qemu/roms/seabios/src/hw/virtio-scsi.h index 96c3701d2..7532cc98e 100644 --- a/qemu/roms/seabios/src/hw/virtio-scsi.h +++ b/qemu/roms/seabios/src/hw/virtio-scsi.h @@ -41,7 +41,7 @@ struct virtio_scsi_resp_cmd { #define VIRTIO_SCSI_S_OK 0 struct disk_op_s; -int virtio_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +int virtio_scsi_process_op(struct disk_op_s *op); void virtio_scsi_setup(void); #endif /* _VIRTIO_SCSI_H */ diff --git a/qemu/roms/seabios/src/list.h b/qemu/roms/seabios/src/list.h index de656b9d6..94512e306 100644 --- a/qemu/roms/seabios/src/list.h +++ b/qemu/roms/seabios/src/list.h @@ -61,6 +61,16 @@ hlist_add_after(struct hlist_node *n, struct hlist_node *prev) hlist_add(n, &prev->next); } +static inline void +hlist_replace(struct hlist_node *old, struct hlist_node *new) +{ + new->next = old->next; + if (new->next) + new->next->pprev = &new->next; + new->pprev = old->pprev; + *new->pprev = new; +} + #define hlist_for_each_entry(pos, head, member) \ for (pos = container_of((head)->first, typeof(*pos), member) \ ; pos != container_of(NULL, typeof(*pos), member) \ diff --git a/qemu/roms/seabios/src/malloc.c b/qemu/roms/seabios/src/malloc.c index c4cb17149..3733855ca 100644 --- a/qemu/roms/seabios/src/malloc.c +++ b/qemu/roms/seabios/src/malloc.c @@ -6,9 +6,10 @@ #include "biosvar.h" // GET_BDA #include "config.h" // BUILD_BIOS_ADDR +#include "e820map.h" // struct e820entry #include "list.h" // hlist_node #include "malloc.h" // _malloc -#include "memmap.h" // struct e820entry +#include "memmap.h" // PAGE_SIZE #include "output.h" // dprintf #include "stacks.h" // wait_preempt #include "std/optionrom.h" // OPTION_ROM_ALIGN @@ -17,7 +18,7 @@ // Information on a reserved area. struct allocinfo_s { struct hlist_node node; - void *data, *dataend, *allocend; + u32 range_start, range_end, alloc_size; }; // Information on a tracked memory allocation. @@ -46,98 +47,106 @@ static struct zone_s *Zones[] VARVERIFY32INIT = { ****************************************************************/ // Find and reserve space from a given zone -static void * -allocSpace(struct zone_s *zone, u32 size, u32 align, struct allocinfo_s *fill) +static u32 +alloc_new(struct zone_s *zone, u32 size, u32 align, struct allocinfo_s *fill) { struct allocinfo_s *info; hlist_for_each_entry(info, &zone->head, node) { - void *dataend = info->dataend; - void *allocend = info->allocend; - void *newallocend = (void*)ALIGN_DOWN((u32)allocend - size, align); - if (newallocend >= dataend && newallocend <= allocend) { + u32 alloc_end = info->range_start + info->alloc_size; + u32 range_end = info->range_end; + u32 new_range_end = ALIGN_DOWN(range_end - size, align); + if (new_range_end >= alloc_end && new_range_end <= range_end) { // Found space - now reserve it. - if (!fill) - fill = newallocend; - fill->data = newallocend; - fill->dataend = newallocend + size; - fill->allocend = allocend; + fill->range_start = new_range_end; + fill->range_end = range_end; + fill->alloc_size = size; - info->allocend = newallocend; + info->range_end = new_range_end; hlist_add_before(&fill->node, &info->node); - return newallocend; + return new_range_end; } } - return NULL; + return 0; } -// Release space allocated with allocSpace() -static void -freeSpace(struct allocinfo_s *info) +// Reserve space for a 'struct allocdetail_s' and fill +static struct allocdetail_s * +alloc_new_detail(struct allocdetail_s *temp) { - struct allocinfo_s *next = container_of_or_null( - info->node.next, struct allocinfo_s, node); - if (next && next->allocend == info->data) - next->allocend = info->allocend; - hlist_del(&info->node); + u32 detail_addr = alloc_new(&ZoneTmpHigh, sizeof(struct allocdetail_s) + , MALLOC_MIN_ALIGN, &temp->detailinfo); + if (!detail_addr) { + detail_addr = alloc_new(&ZoneTmpLow, sizeof(struct allocdetail_s) + , MALLOC_MIN_ALIGN, &temp->detailinfo); + if (!detail_addr) { + warn_noalloc(); + return NULL; + } + } + struct allocdetail_s *detail = memremap(detail_addr, sizeof(*detail)); + + // Fill final 'detail' allocation from data in 'temp' + memcpy(detail, temp, sizeof(*detail)); + hlist_replace(&temp->detailinfo.node, &detail->detailinfo.node); + hlist_replace(&temp->datainfo.node, &detail->datainfo.node); + return detail; } // Add new memory to a zone static void -addSpace(struct zone_s *zone, void *start, void *end) +alloc_add(struct zone_s *zone, u32 start, u32 end) { // Find position to add space struct allocinfo_s *info; struct hlist_node **pprev; hlist_for_each_entry_pprev(info, pprev, &zone->head, node) { - if (info->data < start) + if (info->range_start < start) break; } // Add space using temporary allocation info. struct allocdetail_s tempdetail; - tempdetail.datainfo.data = tempdetail.datainfo.dataend = start; - tempdetail.datainfo.allocend = end; + tempdetail.handle = MALLOC_DEFAULT_HANDLE; + tempdetail.datainfo.range_start = start; + tempdetail.datainfo.range_end = end; + tempdetail.datainfo.alloc_size = 0; hlist_add(&tempdetail.datainfo.node, pprev); // Allocate final allocation info. - struct allocdetail_s *detail = allocSpace( - &ZoneTmpHigh, sizeof(*detail), MALLOC_MIN_ALIGN, NULL); - if (!detail) { - detail = allocSpace(&ZoneTmpLow, sizeof(*detail) - , MALLOC_MIN_ALIGN, NULL); - if (!detail) { - hlist_del(&tempdetail.datainfo.node); - warn_noalloc(); - return; - } - } + struct allocdetail_s *detail = alloc_new_detail(&tempdetail); + if (!detail) + hlist_del(&tempdetail.datainfo.node); +} - // Replace temp alloc space with final alloc space - pprev = tempdetail.datainfo.node.pprev; - hlist_del(&tempdetail.datainfo.node); - memcpy(&detail->datainfo, &tempdetail.datainfo, sizeof(detail->datainfo)); - detail->handle = MALLOC_DEFAULT_HANDLE; - hlist_add(&detail->datainfo.node, pprev); +// Release space allocated with alloc_new() +static void +alloc_free(struct allocinfo_s *info) +{ + struct allocinfo_s *next = container_of_or_null( + info->node.next, struct allocinfo_s, node); + if (next && next->range_end == info->range_start) + next->range_end = info->range_end; + hlist_del(&info->node); } -// Search all zones for an allocation obtained from allocSpace() +// Search all zones for an allocation obtained from alloc_new() static struct allocinfo_s * -findAlloc(void *data) +alloc_find(u32 data) { int i; for (i=0; ihead, node) { - if (info->data == data) + if (info->range_start == data) return info; } } return NULL; } -// Return the last sentinal node of a zone +// Find the lowest memory range added by alloc_add() static struct allocinfo_s * -findLast(struct zone_s *zone) +alloc_find_lowest(struct zone_s *zone) { struct allocinfo_s *info, *last = NULL; hlist_for_each_entry(info, &zone->head, node) { @@ -171,25 +180,25 @@ relocate_ebda(u32 newebda, u32 oldebda, u8 ebda_size) } // Support expanding the ZoneLow dynamically. -static void * +static u32 zonelow_expand(u32 size, u32 align, struct allocinfo_s *fill) { // Make sure to not move ebda while an optionrom is running. if (unlikely(wait_preempt())) { - void *data = allocSpace(&ZoneLow, size, align, fill); + u32 data = alloc_new(&ZoneLow, size, align, fill); if (data) return data; } - struct allocinfo_s *info = findLast(&ZoneLow); + struct allocinfo_s *info = alloc_find_lowest(&ZoneLow); if (!info) - return NULL; - u32 oldpos = (u32)info->allocend; + return 0; + u32 oldpos = info->range_end; u32 newpos = ALIGN_DOWN(oldpos - size, align); - u32 bottom = (u32)info->dataend; + u32 bottom = info->range_start + info->alloc_size; if (newpos >= bottom && newpos <= oldpos) // Space already present. - return allocSpace(&ZoneLow, size, align, fill); + return alloc_new(&ZoneLow, size, align, fill); u16 ebda_seg = get_ebda_seg(); u32 ebda_pos = (u32)MAKE_FLATPTR(ebda_seg, 0); u8 ebda_size = GET_EBDA(ebda_seg, size); @@ -201,21 +210,20 @@ zonelow_expand(u32 size, u32 align, struct allocinfo_s *fill) u32 newebda = ALIGN_DOWN(newbottom - ebda_size * 1024, 1024); if (newebda < BUILD_EBDA_MINIMUM) // Not enough space. - return NULL; + return 0; // Move ebda int ret = relocate_ebda(newebda, ebda_pos, ebda_size); if (ret) - return NULL; + return 0; // Update zone - if (ebda_end == bottom) { - info->data = (void*)newbottom; - info->dataend = (void*)newbottom; - } else - addSpace(&ZoneLow, (void*)newbottom, (void*)ebda_end); + if (ebda_end == bottom) + info->range_start = newbottom; + else + alloc_add(&ZoneLow, newbottom, ebda_end); - return allocSpace(&ZoneLow, size, align, fill); + return alloc_new(&ZoneLow, size, align, fill); } @@ -223,56 +231,69 @@ zonelow_expand(u32 size, u32 align, struct allocinfo_s *fill) * tracked memory allocations ****************************************************************/ -// Allocate memory from the given zone and track it as a PMM allocation -void * __malloc -_malloc(struct zone_s *zone, u32 size, u32 align) +// Allocate physical memory from the given zone and track it as a PMM allocation +u32 +malloc_palloc(struct zone_s *zone, u32 size, u32 align) { ASSERT32FLAT(); if (!size) - return NULL; - - // Find and reserve space for bookkeeping. - struct allocdetail_s *detail = allocSpace( - &ZoneTmpHigh, sizeof(*detail), MALLOC_MIN_ALIGN, NULL); - if (!detail) { - detail = allocSpace(&ZoneTmpLow, sizeof(*detail) - , MALLOC_MIN_ALIGN, NULL); - if (!detail) - return NULL; - } - detail->handle = MALLOC_DEFAULT_HANDLE; + return 0; // Find and reserve space for main allocation - void *data = allocSpace(zone, size, align, &detail->datainfo); + struct allocdetail_s tempdetail; + tempdetail.handle = MALLOC_DEFAULT_HANDLE; + u32 data = alloc_new(zone, size, align, &tempdetail.datainfo); if (!CONFIG_MALLOC_UPPERMEMORY && !data && zone == &ZoneLow) - data = zonelow_expand(size, align, &detail->datainfo); - if (!data) { - freeSpace(&detail->detailinfo); - return NULL; + data = zonelow_expand(size, align, &tempdetail.datainfo); + if (!data) + return 0; + + // Find and reserve space for bookkeeping. + struct allocdetail_s *detail = alloc_new_detail(&tempdetail); + if (!detail) { + alloc_free(&tempdetail.datainfo); + return 0; } - dprintf(8, "_malloc zone=%p size=%d align=%x ret=%p (detail=%p)\n" + dprintf(8, "phys_alloc zone=%p size=%d align=%x ret=%x (detail=%p)\n" , zone, size, align, data, detail); return data; } -// Free a data block allocated with _malloc +// Allocate virtual memory from the given zone +void * __malloc +_malloc(struct zone_s *zone, u32 size, u32 align) +{ + return memremap(malloc_palloc(zone, size, align), size); +} + +// Free a data block allocated with phys_alloc int -_free(void *data) +malloc_pfree(u32 data) { ASSERT32FLAT(); - struct allocinfo_s *info = findAlloc(data); - if (!info || data == (void*)info || data == info->dataend) + struct allocinfo_s *info = alloc_find(data); + if (!info || data == virt_to_phys(info) || !info->alloc_size) return -1; struct allocdetail_s *detail = container_of( info, struct allocdetail_s, datainfo); - dprintf(8, "_free %p (detail=%p)\n", data, detail); - freeSpace(info); - freeSpace(&detail->detailinfo); + dprintf(8, "phys_free %x (detail=%p)\n", data, detail); + alloc_free(info); + alloc_free(&detail->detailinfo); return 0; } +void +free(void *data) +{ + if (!data) + return; + int ret = malloc_pfree(virt_to_phys(data)); + if (ret) + warn_internalerror(); +} + // Find the amount of free space in a given zone. u32 malloc_getspace(struct zone_s *zone) @@ -282,7 +303,7 @@ malloc_getspace(struct zone_s *zone) u32 maxspace = 0; struct allocinfo_s *info; hlist_for_each_entry(info, &zone->head, node) { - u32 space = info->allocend - info->dataend; + u32 space = info->range_end - info->range_start - info->alloc_size; if (space > maxspace) maxspace = space; } @@ -298,34 +319,34 @@ malloc_getspace(struct zone_s *zone) // Set a handle associated with an allocation. void -malloc_sethandle(void *data, u32 handle) +malloc_sethandle(u32 data, u32 handle) { ASSERT32FLAT(); - struct allocinfo_s *info = findAlloc(data); - if (!info || data == (void*)info || data == info->dataend) + struct allocinfo_s *info = alloc_find(data); + if (!info || data == virt_to_phys(info) || !info->alloc_size) return; struct allocdetail_s *detail = container_of( info, struct allocdetail_s, datainfo); detail->handle = handle; } -// Find the data block allocated with _malloc with a given handle. -void * +// Find the data block allocated with phys_alloc with a given handle. +u32 malloc_findhandle(u32 handle) { int i; for (i=0; ihead, node) { - if (info->data != (void*)info) + if (info->range_start != virt_to_phys(info)) continue; struct allocdetail_s *detail = container_of( info, struct allocdetail_s, detailinfo); if (detail->handle == handle) - return detail->datainfo.data; + return detail->datainfo.range_start; } } - return NULL; + return 0; } @@ -343,10 +364,9 @@ u32 rom_get_max(void) { if (CONFIG_MALLOC_UPPERMEMORY) - return ALIGN_DOWN((u32)RomBase->allocend - OPROM_HEADER_RESERVE + return ALIGN_DOWN(RomBase->range_end - OPROM_HEADER_RESERVE , OPTION_ROM_ALIGN); - extern u8 final_readonly_start[]; - return (u32)final_readonly_start; + return SYMBOL(final_readonly_start); } // Return the end of the last deployed option rom. @@ -364,9 +384,9 @@ rom_reserve(u32 size) if (newend > rom_get_max()) return NULL; if (CONFIG_MALLOC_UPPERMEMORY) { - if (newend < (u32)zonelow_base) - newend = (u32)zonelow_base; - RomBase->data = RomBase->dataend = (void*)newend + OPROM_HEADER_RESERVE; + if (newend < SYMBOL(zonelow_base)) + newend = SYMBOL(zonelow_base); + RomBase->range_start = newend + OPROM_HEADER_RESERVE; } return (void*)RomEnd; } @@ -396,10 +416,10 @@ malloc_preinit(void) dprintf(3, "malloc preinit\n"); // Don't declare any memory between 0xa0000 and 0x100000 - add_e820(BUILD_LOWRAM_END, BUILD_BIOS_ADDR-BUILD_LOWRAM_END, E820_HOLE); + e820_remove(BUILD_LOWRAM_END, BUILD_BIOS_ADDR-BUILD_LOWRAM_END); // Mark known areas as reserved. - add_e820(BUILD_BIOS_ADDR, BUILD_BIOS_SIZE, E820_RESERVED); + e820_add(BUILD_BIOS_ADDR, BUILD_BIOS_SIZE, E820_RESERVED); // Populate temp high ram u32 highram = 0; @@ -419,31 +439,30 @@ malloc_preinit(void) e = newe; } } - addSpace(&ZoneTmpHigh, (void*)s, (void*)e); + alloc_add(&ZoneTmpHigh, s, e); } // Populate regions - addSpace(&ZoneTmpLow, (void*)BUILD_STACK_ADDR, (void*)BUILD_EBDA_MINIMUM); + alloc_add(&ZoneTmpLow, BUILD_STACK_ADDR, BUILD_EBDA_MINIMUM); if (highram) { - addSpace(&ZoneHigh, (void*)highram - , (void*)highram + BUILD_MAX_HIGHTABLE); - add_e820(highram, BUILD_MAX_HIGHTABLE, E820_RESERVED); + alloc_add(&ZoneHigh, highram, highram + BUILD_MAX_HIGHTABLE); + e820_add(highram, BUILD_MAX_HIGHTABLE, E820_RESERVED); } } void -csm_malloc_preinit(u32 low_pmm, u32 low_pmm_size, u32 hi_pmm, u32 hi_pmm_size) +malloc_csm_preinit(u32 low_pmm, u32 low_pmm_size, u32 hi_pmm, u32 hi_pmm_size) { ASSERT32FLAT(); if (hi_pmm_size > BUILD_MAX_HIGHTABLE) { - void *hi_pmm_end = (void *)hi_pmm + hi_pmm_size; - addSpace(&ZoneTmpHigh, (void *)hi_pmm, hi_pmm_end - BUILD_MAX_HIGHTABLE); - addSpace(&ZoneHigh, hi_pmm_end - BUILD_MAX_HIGHTABLE, hi_pmm_end); + u32 hi_pmm_end = hi_pmm + hi_pmm_size; + alloc_add(&ZoneTmpHigh, hi_pmm, hi_pmm_end - BUILD_MAX_HIGHTABLE); + alloc_add(&ZoneHigh, hi_pmm_end - BUILD_MAX_HIGHTABLE, hi_pmm_end); } else { - addSpace(&ZoneTmpHigh, (void *)hi_pmm, (void *)hi_pmm + hi_pmm_size); + alloc_add(&ZoneTmpHigh, hi_pmm, hi_pmm + hi_pmm_size); } - addSpace(&ZoneTmpLow, (void *)low_pmm, (void *)low_pmm + low_pmm_size); + alloc_add(&ZoneTmpLow, low_pmm, low_pmm + low_pmm_size); } u32 LegacyRamSize VARFSEG; @@ -484,21 +503,21 @@ malloc_init(void) } // Initialize low-memory region - extern u8 varlow_start[], varlow_end[], final_varlow_start[]; - memmove(final_varlow_start, varlow_start, varlow_end - varlow_start); + memmove(VSYMBOL(final_varlow_start), VSYMBOL(varlow_start) + , SYMBOL(varlow_end) - SYMBOL(varlow_start)); if (CONFIG_MALLOC_UPPERMEMORY) { - addSpace(&ZoneLow, zonelow_base + OPROM_HEADER_RESERVE - , final_varlow_start); - RomBase = findLast(&ZoneLow); + alloc_add(&ZoneLow, SYMBOL(zonelow_base) + OPROM_HEADER_RESERVE + , SYMBOL(final_varlow_start)); + RomBase = alloc_find_lowest(&ZoneLow); } else { - addSpace(&ZoneLow, (void*)ALIGN_DOWN((u32)final_varlow_start, 1024) - , final_varlow_start); + alloc_add(&ZoneLow, ALIGN_DOWN(SYMBOL(final_varlow_start), 1024) + , SYMBOL(final_varlow_start)); } // Add space available in f-segment to ZoneFSeg - extern u8 zonefseg_start[], zonefseg_end[]; - memset(zonefseg_start, 0, zonefseg_end - zonefseg_start); - addSpace(&ZoneFSeg, zonefseg_start, zonefseg_end); + memset(VSYMBOL(zonefseg_start), 0 + , SYMBOL(zonefseg_end) - SYMBOL(zonefseg_start)); + alloc_add(&ZoneFSeg, SYMBOL(zonefseg_start), SYMBOL(zonefseg_end)); calcRamSize(); } @@ -521,19 +540,20 @@ malloc_prepboot(void) // Reserve more low-mem if needed. u32 endlow = GET_BDA(mem_size_kb)*1024; - add_e820(endlow, BUILD_LOWRAM_END-endlow, E820_RESERVED); + e820_add(endlow, BUILD_LOWRAM_END-endlow, E820_RESERVED); // Clear unused f-seg ram. - struct allocinfo_s *info = findLast(&ZoneFSeg); - memset(info->dataend, 0, info->allocend - info->dataend); + struct allocinfo_s *info = alloc_find_lowest(&ZoneFSeg); + u32 size = info->range_end - info->range_start; + memset(memremap(info->range_start, size), 0, size); dprintf(1, "Space available for UMB: %x-%x, %x-%x\n" - , RomEnd, base, (u32)info->dataend, (u32)info->allocend); + , RomEnd, base, info->range_start, info->range_end); // Give back unused high ram. - info = findLast(&ZoneHigh); + info = alloc_find_lowest(&ZoneHigh); if (info) { - u32 giveback = ALIGN_DOWN(info->allocend - info->dataend, PAGE_SIZE); - add_e820((u32)info->dataend, giveback, E820_RAM); + u32 giveback = ALIGN_DOWN(info->range_end-info->range_start, PAGE_SIZE); + e820_add(info->range_start, giveback, E820_RAM); dprintf(1, "Returned %d bytes of ZoneHigh\n", giveback); } diff --git a/qemu/roms/seabios/src/malloc.h b/qemu/roms/seabios/src/malloc.h index 2bcb5bf6d..960a7f800 100644 --- a/qemu/roms/seabios/src/malloc.h +++ b/qemu/roms/seabios/src/malloc.h @@ -9,17 +9,19 @@ u32 rom_get_max(void); u32 rom_get_last(void); struct rom_header *rom_reserve(u32 size); int rom_confirm(u32 size); -void csm_malloc_preinit(u32 low_pmm, u32 low_pmm_size, u32 hi_pmm, +void malloc_csm_preinit(u32 low_pmm, u32 low_pmm_size, u32 hi_pmm, u32 hi_pmm_size); void malloc_preinit(void); extern u32 LegacyRamSize; void malloc_init(void); void malloc_prepboot(void); +u32 malloc_palloc(struct zone_s *zone, u32 size, u32 align); void *_malloc(struct zone_s *zone, u32 size, u32 align); -int _free(void *data); +int malloc_pfree(u32 data); +void free(void *data); u32 malloc_getspace(struct zone_s *zone); -void malloc_sethandle(void *data, u32 handle); -void *malloc_findhandle(u32 handle); +void malloc_sethandle(u32 data, u32 handle); +u32 malloc_findhandle(u32 handle); #define MALLOC_DEFAULT_HANDLE 0xFFFFFFFF // Minimum alignment of malloc'd memory @@ -64,8 +66,5 @@ static inline void *memalign_tmp(u32 align, u32 size) { return ret; return memalign_tmplow(align, size); } -static inline void free(void *data) { - _free(data); -} #endif // malloc.h diff --git a/qemu/roms/seabios/src/memmap.c b/qemu/roms/seabios/src/memmap.c deleted file mode 100644 index e03f8d0bf..000000000 --- a/qemu/roms/seabios/src/memmap.c +++ /dev/null @@ -1,144 +0,0 @@ -// Support for building memory maps suitable for int 15 e820 calls. -// -// Copyright (C) 2008,2009 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU LGPLv3 license. - -#include "config.h" // BUILD_MAX_E820 -#include "memmap.h" // struct e820entry -#include "output.h" // dprintf -#include "string.h" // memmove - - -/**************************************************************** - * e820 memory map - ****************************************************************/ - -// Info on e820 map location and size. -struct e820entry e820_list[BUILD_MAX_E820] VARFSEG; -int e820_count VARFSEG; - -// Remove an entry from the e820_list. -static void -remove_e820(int i) -{ - e820_count--; - memmove(&e820_list[i], &e820_list[i+1] - , sizeof(e820_list[0]) * (e820_count - i)); -} - -// Insert an entry in the e820_list at the given position. -static void -insert_e820(int i, u64 start, u64 size, u32 type) -{ - if (e820_count >= BUILD_MAX_E820) { - warn_noalloc(); - return; - } - - memmove(&e820_list[i+1], &e820_list[i] - , sizeof(e820_list[0]) * (e820_count - i)); - e820_count++; - struct e820entry *e = &e820_list[i]; - e->start = start; - e->size = size; - e->type = type; -} - -static const char * -e820_type_name(u32 type) -{ - switch (type) { - case E820_RAM: return "RAM"; - case E820_RESERVED: return "RESERVED"; - case E820_ACPI: return "ACPI"; - case E820_NVS: return "NVS"; - case E820_UNUSABLE: return "UNUSABLE"; - case E820_HOLE: return "HOLE"; - default: return "UNKNOWN"; - } -} - -// Show the current e820_list. -static void -dump_map(void) -{ - dprintf(1, "e820 map has %d items:\n", e820_count); - int i; - for (i=0; istart + e->size; - dprintf(1, " %d: %016llx - %016llx = %d %s\n", i - , e->start, e_end, e->type, e820_type_name(e->type)); - } -} - -// Add a new entry to the list. This scans for overlaps and keeps the -// list sorted. -void -add_e820(u64 start, u64 size, u32 type) -{ - dprintf(8, "Add to e820 map: %08x %08x %d\n", (u32)start, (u32)size, type); - - if (! size) - // Huh? Nothing to do. - return; - - // Find position of new item (splitting existing item if needed). - u64 end = start + size; - int i; - for (i=0; istart + e->size; - if (start > e_end) - continue; - // Found position - check if an existing item needs to be split. - if (start > e->start) { - if (type == e->type) { - // Same type - merge them. - size += start - e->start; - start = e->start; - } else { - // Split existing item. - e->size = start - e->start; - i++; - if (e_end > end) - insert_e820(i, end, e_end - end, e->type); - } - } - break; - } - // Remove/adjust existing items that are overlapping. - while (istart) - // No overlap - done. - break; - u64 e_end = e->start + e->size; - if (end >= e_end) { - // Existing item completely overlapped - remove it. - remove_e820(i); - continue; - } - // Not completely overlapped - adjust its start. - e->start = end; - e->size = e_end - end; - if (type == e->type) { - // Same type - merge them. - size += e->size; - remove_e820(i); - } - break; - } - // Insert new item. - if (type != E820_HOLE) - insert_e820(i, start, size, type); - //dump_map(); -} - -// Report on final memory locations. -void -memmap_prepboot(void) -{ - dump_map(); -} diff --git a/qemu/roms/seabios/src/memmap.h b/qemu/roms/seabios/src/memmap.h index 7bda56e2b..22bd4bcb8 100644 --- a/qemu/roms/seabios/src/memmap.h +++ b/qemu/roms/seabios/src/memmap.h @@ -1,29 +1,21 @@ -#ifndef __E820MAP_H -#define __E820MAP_H +#ifndef __MEMMAP_H +#define __MEMMAP_H -#include "types.h" // u64 - -#define E820_RAM 1 -#define E820_RESERVED 2 -#define E820_ACPI 3 -#define E820_NVS 4 -#define E820_UNUSABLE 5 -#define E820_HOLE ((u32)-1) // Useful for removing entries - -struct e820entry { - u64 start; - u64 size; - u32 type; -}; - -void add_e820(u64 start, u64 size, u32 type); -void memmap_prepboot(void); +#include "types.h" // u32 // A typical OS page size #define PAGE_SIZE 4096 +#define PAGE_SHIFT 12 + +static inline u32 virt_to_phys(void *v) { + return (u32)v; +} +static inline void *memremap(u32 addr, u32 len) { + return (void*)addr; +} -// e820 map storage -extern struct e820entry e820_list[]; -extern int e820_count; +// Return the value of a linker script symbol (see scripts/layoutrom.py) +#define SYMBOL(SYM) ({ extern char SYM; (u32)&SYM; }) +#define VSYMBOL(SYM) ((void*)SYMBOL(SYM)) -#endif // e820map.h +#endif // memmap.h diff --git a/qemu/roms/seabios/src/misc.c b/qemu/roms/seabios/src/misc.c index 8caaf31d8..f02237c36 100644 --- a/qemu/roms/seabios/src/misc.c +++ b/qemu/roms/seabios/src/misc.c @@ -56,7 +56,7 @@ void VISIBLE16 handle_10(struct bregs *regs) { debug_enter(regs, DEBUG_HDL_10); - // dont do anything, since the VGA BIOS handles int10h requests + // don't do anything, since the VGA BIOS handles int10h requests } // NMI handler diff --git a/qemu/roms/seabios/src/mouse.c b/qemu/roms/seabios/src/mouse.c index 6d1f5b77e..b7ad7c62a 100644 --- a/qemu/roms/seabios/src/mouse.c +++ b/qemu/roms/seabios/src/mouse.c @@ -280,8 +280,7 @@ invoke_mouse_handler(void) if (!CONFIG_MOUSE) return; if (need_hop_back()) { - extern void _cfunc16_invoke_mouse_handler(void); - stack_hop_back(0, 0, _cfunc16_invoke_mouse_handler); + stack_hop_back(invoke_mouse_handler, 0, 0); return; } ASSERT16(); diff --git a/qemu/roms/seabios/src/optionroms.c b/qemu/roms/seabios/src/optionroms.c index 93d9d2fe6..c81eff2d2 100644 --- a/qemu/roms/seabios/src/optionroms.c +++ b/qemu/roms/seabios/src/optionroms.c @@ -19,6 +19,9 @@ #include "std/pnpbios.h" // PNP_SIGNATURE #include "string.h" // memset #include "util.h" // get_pnp_offset +#include "tcgbios.h" // tpm_* + +static int EnforceChecksum, S3ResumeVga, RunPCIroms; /**************************************************************** @@ -60,8 +63,6 @@ call_bcv(u16 seg, u16 ip) __callrom(MAKE_FLATPTR(seg, 0), ip, 0); } -static int EnforceChecksum; - // Verify that an option rom looks valid static int is_valid_rom(struct rom_header *rom) @@ -132,6 +133,8 @@ init_optionrom(struct rom_header *rom, u16 bdf, int isvga) if (newrom != rom) memmove(newrom, rom, rom->size * 512); + tpm_option_rom(newrom, rom->size * 512); + if (isvga || get_pnp_rom(newrom)) // Only init vga and PnP roms here. callrom(newrom, bdf); @@ -180,19 +183,6 @@ deploy_romfile(struct romfile_s *file) return rom; } -// Check if an option rom is at a hardcoded location or in CBFS. -static struct rom_header * -lookup_hardcode(struct pci_device *pci) -{ - char fname[17]; - snprintf(fname, sizeof(fname), "pci%04x,%04x.rom" - , pci->vendor, pci->device); - struct romfile_s *file = romfile_find(fname); - if (file) - return deploy_romfile(file); - return NULL; -} - // Run all roms in a given CBFS directory. static void run_file_roms(const char *prefix, int isvga, u64 *sources) @@ -321,21 +311,28 @@ fail: } // Attempt to map and initialize the option rom on a given PCI device. -static int +static void init_pcirom(struct pci_device *pci, int isvga, u64 *sources) { u16 bdf = pci->bdf; dprintf(4, "Attempting to init PCI bdf %02x:%02x.%x (vd %04x:%04x)\n" , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf) , pci->vendor, pci->device); - struct rom_header *rom = lookup_hardcode(pci); - if (! rom) + + char fname[17]; + snprintf(fname, sizeof(fname), "pci%04x,%04x.rom" + , pci->vendor, pci->device); + struct romfile_s *file = romfile_find(fname); + struct rom_header *rom = NULL; + if (file) + rom = deploy_romfile(file); + else if (RunPCIroms > 1 || (RunPCIroms == 1 && isvga)) rom = map_pcirom(pci); if (! rom) // No ROM present. - return -1; + return; setRomSource(sources, rom, RS_PCIROM | (u32)pci); - return init_optionrom(rom, bdf, isvga); + init_optionrom(rom, bdf, isvga); } @@ -416,7 +413,6 @@ optionrom_setup(void) * VGA init ****************************************************************/ -static int S3ResumeVga; int ScreenAndDebug; struct rom_header *VgaROM; @@ -432,6 +428,7 @@ vgarom_setup(void) // Load some config settings that impact VGA. EnforceChecksum = romfile_loadint("etc/optionroms-checksum", 1); S3ResumeVga = romfile_loadint("etc/s3-resume-vga-init", CONFIG_QEMU); + RunPCIroms = romfile_loadint("etc/pci-optionrom-exec", 2); ScreenAndDebug = romfile_loadint("etc/screen-and-debug", 1); if (CONFIG_OPTIONROMS_DEPLOYED) { diff --git a/qemu/roms/seabios/src/output.c b/qemu/roms/seabios/src/output.c index 45397b3f6..8a883889c 100644 --- a/qemu/roms/seabios/src/output.c +++ b/qemu/roms/seabios/src/output.c @@ -30,6 +30,7 @@ void debug_banner(void) { dprintf(1, "SeaBIOS (version %s)\n", VERSION); + dprintf(1, "BUILD: %s\n", BUILDINFO); } // Write a character to debug port(s). diff --git a/qemu/roms/seabios/src/pmm.c b/qemu/roms/seabios/src/pmm.c index 304faab2c..640341472 100644 --- a/qemu/roms/seabios/src/pmm.c +++ b/qemu/roms/seabios/src/pmm.c @@ -65,26 +65,26 @@ handle_pmm00(u16 *args) if (align < MALLOC_MIN_ALIGN) align = MALLOC_MIN_ALIGN; } - void *data; + u32 data; switch (flags & 3) { default: case 0: return 0; case 1: - data = _malloc(lowzone, size, align); + data = malloc_palloc(lowzone, size, align); break; case 2: - data = _malloc(highzone, size, align); + data = malloc_palloc(highzone, size, align); break; case 3: { - data = _malloc(lowzone, size, align); + data = malloc_palloc(lowzone, size, align); if (!data) - data = _malloc(highzone, size, align); + data = malloc_palloc(highzone, size, align); } } if (data && handle != MALLOC_DEFAULT_HANDLE) malloc_sethandle(data, handle); - return (u32)data; + return data; } // PMM - find @@ -95,7 +95,7 @@ handle_pmm01(u16 *args) dprintf(3, "pmm01: handle=%x\n", handle); if (handle == MALLOC_DEFAULT_HANDLE) return 0; - return (u32)malloc_findhandle(handle); + return malloc_findhandle(handle); } // PMM - deallocate @@ -104,7 +104,7 @@ handle_pmm02(u16 *args) { u32 buffer = *(u32*)&args[1]; dprintf(3, "pmm02: buffer=%x\n", buffer); - int ret = _free((void*)buffer); + int ret = malloc_pfree(buffer); if (ret) // Error return 1; diff --git a/qemu/roms/seabios/src/post.c b/qemu/roms/seabios/src/post.c index 9ea5620c9..49c22b875 100644 --- a/qemu/roms/seabios/src/post.c +++ b/qemu/roms/seabios/src/post.c @@ -8,6 +8,7 @@ #include "biosvar.h" // SET_BDA #include "bregs.h" // struct bregs #include "config.h" // CONFIG_* +#include "e820map.h" // e820_add #include "fw/paravirt.h" // qemu_cfg_preinit #include "fw/xen.h" // xen_preinit #include "hw/ahci.h" // ahci_setup @@ -24,10 +25,11 @@ #include "hw/virtio-blk.h" // virtio_blk_setup #include "hw/virtio-scsi.h" // virtio_scsi_setup #include "malloc.h" // malloc_init -#include "memmap.h" // add_e820 +#include "memmap.h" // SYMBOL #include "output.h" // dprintf #include "string.h" // memset #include "util.h" // kbd_init +#include "tcgbios.h" // tpm_* /**************************************************************** @@ -88,9 +90,8 @@ bda_init(void) int esize = EBDA_SIZE_START; u16 ebda_seg = EBDA_SEGMENT_START; - extern u8 final_varlow_start[]; if (!CONFIG_MALLOC_UPPERMEMORY) - ebda_seg = FLATPTR_TO_SEG(ALIGN_DOWN((u32)final_varlow_start, 1024) + ebda_seg = FLATPTR_TO_SEG(ALIGN_DOWN(SYMBOL(final_varlow_start), 1024) - EBDA_SIZE_START*1024); SET_BDA(ebda_seg, ebda_seg); @@ -101,10 +102,10 @@ bda_init(void) memset(ebda, 0, sizeof(*ebda)); ebda->size = esize; - add_e820((u32)ebda, BUILD_LOWRAM_END-(u32)ebda, E820_RESERVED); + e820_add((u32)ebda, BUILD_LOWRAM_END-(u32)ebda, E820_RESERVED); // Init extra stack - StackPos = (void*)(&ExtraStack[BUILD_EXTRA_STACK_SIZE] - zonelow_base); + StackPos = &ExtraStack[BUILD_EXTRA_STACK_SIZE] - SYMBOL(zonelow_base); } void @@ -116,13 +117,13 @@ interface_init(void) // Setup romfile items. qemu_cfg_init(); coreboot_cbfs_init(); + multiboot_init(); // Setup ivt/bda/ebda ivt_init(); bda_init(); // Other interfaces - thread_init(); boot_init(); bios32_init(); pmm_init(); @@ -157,26 +158,32 @@ device_hardware_setup(void) static void platform_hardware_setup(void) { - // Enable CPU caching - setcr0(getcr0() & ~(CR0_CD|CR0_NW)); - // Make sure legacy DMA isn't running. dma_setup(); // Init base pc hardware. pic_setup(); + thread_setup(); mathcp_setup(); - timer_setup(); - clock_setup(); // Platform specific setup qemu_platform_setup(); coreboot_platform_setup(); + + // Setup timers and periodic clock interrupt + timer_setup(); + clock_setup(); + + // Initialize TPM + tpm_setup(); } void prepareboot(void) { + // Change TPM phys. presence state befor leaving BIOS + tpm_prepboot(); + // Run BCVs bcv_prepboot(); @@ -184,7 +191,7 @@ prepareboot(void) cdrom_prepboot(); pmm_prepboot(); malloc_prepboot(); - memmap_prepboot(); + e820_prepboot(); HaveRunPost = 2; @@ -269,30 +276,27 @@ reloc_preinit(void *f, void *arg) void (*func)(void *) __noreturn = f; if (!CONFIG_RELOCATE_INIT) func(arg); - // Symbols populated by the build. - extern u8 code32flat_start[]; - extern u8 _reloc_min_align; - extern u32 _reloc_abs_start[], _reloc_abs_end[]; - extern u32 _reloc_rel_start[], _reloc_rel_end[]; - extern u32 _reloc_init_start[], _reloc_init_end[]; - extern u8 code32init_start[], code32init_end[]; // Allocate space for init code. - u32 initsize = code32init_end - code32init_start; - u32 codealign = (u32)&_reloc_min_align; + u32 initsize = SYMBOL(code32init_end) - SYMBOL(code32init_start); + u32 codealign = SYMBOL(_reloc_min_align); void *codedest = memalign_tmp(codealign, initsize); + void *codesrc = VSYMBOL(code32init_start); if (!codedest) panic("No space for init relocation.\n"); // Copy code and update relocs (init absolute, init relative, and runtime) dprintf(1, "Relocating init from %p to %p (size %d)\n" - , code32init_start, codedest, initsize); - s32 delta = codedest - (void*)code32init_start; - memcpy(codedest, code32init_start, initsize); - updateRelocs(codedest, _reloc_abs_start, _reloc_abs_end, delta); - updateRelocs(codedest, _reloc_rel_start, _reloc_rel_end, -delta); - updateRelocs(code32flat_start, _reloc_init_start, _reloc_init_end, delta); - if (f >= (void*)code32init_start && f < (void*)code32init_end) + , codesrc, codedest, initsize); + s32 delta = codedest - codesrc; + memcpy(codedest, codesrc, initsize); + updateRelocs(codedest, VSYMBOL(_reloc_abs_start), VSYMBOL(_reloc_abs_end) + , delta); + updateRelocs(codedest, VSYMBOL(_reloc_rel_start), VSYMBOL(_reloc_rel_end) + , -delta); + updateRelocs(VSYMBOL(code32flat_start), VSYMBOL(_reloc_init_start) + , VSYMBOL(_reloc_init_end), delta); + if (f >= codesrc && f < VSYMBOL(code32init_end)) func = f + delta; // Call function in relocated code. diff --git a/qemu/roms/seabios/src/resume.c b/qemu/roms/seabios/src/resume.c index 19031747c..a5465d877 100644 --- a/qemu/roms/seabios/src/resume.c +++ b/qemu/roms/seabios/src/resume.c @@ -16,6 +16,7 @@ #include "std/bda.h" // struct bios_data_area_s #include "string.h" // memset #include "util.h" // dma_setup +#include "tcgbios.h" // tpm_s3_resume // Handler for post calls that look like a resume. void VISIBLE16 @@ -99,6 +100,8 @@ s3_resume(void) pci_resume(); + /* resume TPM before we may measure option roms */ + tpm_s3_resume(); s3_resume_vga(); make_bios_readonly(); diff --git a/qemu/roms/seabios/src/romlayout.S b/qemu/roms/seabios/src/romlayout.S index 93b6874e7..53cc0f5e3 100644 --- a/qemu/roms/seabios/src/romlayout.S +++ b/qemu/roms/seabios/src/romlayout.S @@ -22,18 +22,14 @@ // %edx = return location (in 32bit mode) // Clobbers: ecx, flags, segment registers, cr0, idt/gdt DECLFUNC transition32 -transition32_nmi_off: - // transition32 when NMI and A20 are already initialized - movl %eax, %ecx - jmp 1f + .global transition32_nmi_off transition32: - movl %eax, %ecx - // Disable irqs (and clear direction flag) cli cld // Disable nmi + movl %eax, %ecx movl $CMOS_RESET_CODE|NMI_DISABLE_BIT, %eax outb %al, $PORT_CMOS_INDEX inb $PORT_CMOS_DATA, %al @@ -42,29 +38,31 @@ transition32: inb $PORT_A20, %al orb $A20_ENABLE_BIT, %al outb %al, $PORT_A20 + movl %ecx, %eax +transition32_nmi_off: // Set segment descriptors -1: lidtw %cs:pmode_IDT_info + lidtw %cs:pmode_IDT_info lgdtw %cs:rombios32_gdt_48 // Enable protected mode - movl %cr0, %eax - orl $CR0_PE, %eax - movl %eax, %cr0 + movl %cr0, %ecx + andl $~(CR0_PG|CR0_CD|CR0_NW), %ecx + orl $CR0_PE, %ecx + movl %ecx, %cr0 // start 32bit protected mode code - ljmpl $SEG32_MODE32_CS, $(BUILD_BIOS_ADDR + 2f) + ljmpl $SEG32_MODE32_CS, $(BUILD_BIOS_ADDR + 1f) .code32 // init data segments -2: movl $SEG32_MODE32_DS, %eax - movw %ax, %ds - movw %ax, %es - movw %ax, %ss - movw %ax, %fs - movw %ax, %gs +1: movl $SEG32_MODE32_DS, %ecx + movw %cx, %ds + movw %cx, %es + movw %cx, %ss + movw %cx, %fs + movw %cx, %gs - movl %ecx, %eax jmpl *%edx .code16 @@ -75,61 +73,47 @@ transition32: .global transition16big .code32 transition16: - movl %eax, %ecx - - // restore data segment limits to 0xffff - movl $SEG32_MODE16_DS, %eax - movw %ax, %ds - movw %ax, %es - movw %ax, %ss - movw %ax, %fs - movw %ax, %gs - -#if CONFIG_DISABLE_A20 - // disable a20 - inb $PORT_A20, %al - andb $~A20_ENABLE_BIT, %al - outb %al, $PORT_A20 -#endif + // Reset data segment limits + movl $SEG32_MODE16_DS, %ecx + movw %cx, %ds + movw %cx, %es + movw %cx, %ss + movw %cx, %fs + movw %cx, %gs // Jump to 16bit mode ljmpw $SEG32_MODE16_CS, $1f transition16big: - movl %eax, %ecx - - movl $SEG32_MODE16BIG_DS, %eax - movw %ax, %ds - movw %ax, %es - movw %ax, %ss - movw %ax, %fs - movw %ax, %gs + movl $SEG32_MODE16BIG_DS, %ecx + movw %cx, %ds + movw %cx, %es + movw %cx, %ss + movw %cx, %fs + movw %cx, %gs ljmpw $SEG32_MODE16BIG_CS, $1f .code16 -1: // Disable protected mode - movl %cr0, %eax - andl $~CR0_PE, %eax - movl %eax, %cr0 +1: movl %cr0, %ecx + andl $~CR0_PE, %ecx + movl %ecx, %cr0 // far jump to flush CPU queue after transition to real mode ljmpw $SEG_BIOS, $2f -2: // restore IDT to normal real-mode defaults - lidtw %cs:rmode_IDT_info +2: lidtw %cs:rmode_IDT_info // Clear segment registers - xorw %ax, %ax - movw %ax, %fs - movw %ax, %gs - movw %ax, %es - movw %ax, %ds - movw %ax, %ss // Assume stack is in segment 0 + xorw %cx, %cx + movw %cx, %fs + movw %cx, %gs + movw %cx, %es + movw %cx, %ds + movw %cx, %ss // Assume stack is in segment 0 - movl %ecx, %eax jmpl *%edx @@ -264,7 +248,7 @@ entry_pmm: movl $_cfunc32flat_handle_pmm, %eax // Setup: call32(handle_pmm, args, -1) leal PUSHBREGS_size+12(%esp, %ecx), %edx // %edx points to start of args movl $-1, %ecx - calll call32 + calll __call32 movw %ax, BREGS_eax(%esp) // Modify %ax:%dx to return %eax shrl $16, %eax movw %ax, BREGS_edx(%esp) @@ -374,6 +358,8 @@ entry_bios32: entry_elf: cli cld + movl %eax, entry_elf_eax + movl %ebx, entry_elf_ebx lidtl (BUILD_BIOS_ADDR + pmode_IDT_info) lgdtl (BUILD_BIOS_ADDR + rombios32_gdt_48) movl $SEG32_MODE32_DS, %eax @@ -562,7 +548,10 @@ entry_post: ENTRY_INTO32 _cfunc32flat_handle_post // Normal entry point ORG 0xe2c3 - IRQ_ENTRY 02 + .global entry_02 +entry_02: + ENTRY handle_02 // NMI handler does not switch onto extra stack + iretw ORG 0xe3fe .global entry_13_official diff --git a/qemu/roms/seabios/src/sha1.c b/qemu/roms/seabios/src/sha1.c new file mode 100644 index 000000000..2ecb3cb89 --- /dev/null +++ b/qemu/roms/seabios/src/sha1.c @@ -0,0 +1,147 @@ +// Support for Calculation of SHA1 in SW +// +// Copyright (C) 2006-2011 IBM Corporation +// +// Authors: +// Stefan Berger +// +// This file may be distributed under the terms of the GNU LGPLv3 license. +// +// See: http://www.itl.nist.gov/fipspubs/fip180-1.htm +// RFC3174, Wikipedia's SHA1 alogrithm description +// + +#include "config.h" +#include "byteorder.h" // cpu_to_*, __swab64 +#include "sha1.h" // sha1 +#include "string.h" // memcpy +#include "x86.h" // rol + +typedef struct _sha1_ctx { + u32 h[5]; +} sha1_ctx; + + +static void +sha1_block(u32 *w, sha1_ctx *ctx) +{ + u32 i; + u32 a,b,c,d,e,f; + u32 tmp; + u32 idx; + + static const u32 sha_ko[4] = { + 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 }; + + /* change endianness of given data */ + for (i = 0; i < 16; i++) + w[i] = be32_to_cpu(w[i]); + + for (i = 16; i <= 79; i++) { + tmp = w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]; + w[i] = rol(tmp,1); + } + + a = ctx->h[0]; + b = ctx->h[1]; + c = ctx->h[2]; + d = ctx->h[3]; + e = ctx->h[4]; + + for (i = 0; i <= 79; i++) { + if (i <= 19) { + f = (b & c) | ((b ^ 0xffffffff) & d); + idx = 0; + } else if (i <= 39) { + f = b ^ c ^ d; + idx = 1; + } else if (i <= 59) { + f = (b & c) | (b & d) | (c & d); + idx = 2; + } else { + f = b ^ c ^ d; + idx = 3; + } + + tmp = rol(a, 5) + + f + + e + + sha_ko[idx] + + w[i]; + e = d; + d = c; + c = rol(b, 30); + b = a; + a = tmp; + } + + ctx->h[0] += a; + ctx->h[1] += b; + ctx->h[2] += c; + ctx->h[3] += d; + ctx->h[4] += e; +} + + +static void +sha1_do(sha1_ctx *ctx, const u8 *data32, u32 length) +{ + u32 offset; + u16 num; + u32 bits = 0; + u32 w[80]; + u64 tmp; + + /* treat data in 64-byte chunks */ + for (offset = 0; length - offset >= 64; offset += 64) { + memcpy(w, data32 + offset, 64); + sha1_block((u32 *)w, ctx); + bits += (64 * 8); + } + + /* last block with less than 64 bytes */ + num = length - offset; + bits += (num << 3); + + memcpy(w, data32 + offset, num); + ((u8 *)w)[num] = 0x80; + if (64 - (num + 1) > 0) + memset( &((u8 *)w)[num + 1], 0x0, 64 - (num + 1)); + + if (num >= 56) { + /* cannot append number of bits here */ + sha1_block((u32 *)w, ctx); + memset(w, 0x0, 60); + } + + /* write number of bits to end of block */ + tmp = __swab64(bits); + memcpy(&w[14], &tmp, 8); + + sha1_block(w, ctx); + + /* need to switch result's endianness */ + for (num = 0; num < 5; num++) + ctx->h[num] = cpu_to_be32(ctx->h[num]); +} + + +u32 +sha1(const u8 *data, u32 length, u8 *hash) +{ + if (!CONFIG_TCGBIOS) + return 0; + + sha1_ctx ctx = { + .h[0] = 0x67452301, + .h[1] = 0xefcdab89, + .h[2] = 0x98badcfe, + .h[3] = 0x10325476, + .h[4] = 0xc3d2e1f0, + }; + + sha1_do(&ctx, data, length); + memcpy(hash, &ctx.h[0], 20); + + return 0; +} diff --git a/qemu/roms/seabios/src/sha1.h b/qemu/roms/seabios/src/sha1.h new file mode 100644 index 000000000..07aabf34f --- /dev/null +++ b/qemu/roms/seabios/src/sha1.h @@ -0,0 +1,8 @@ +#ifndef __SHA1_H +#define __SHA1_H + +#include "types.h" // u32 + +u32 sha1(const u8 *data, u32 length, u8 *hash); + +#endif // sha1.h diff --git a/qemu/roms/seabios/src/stacks.c b/qemu/roms/seabios/src/stacks.c index 1dbdfe9bb..ef6a70775 100644 --- a/qemu/roms/seabios/src/stacks.c +++ b/qemu/roms/seabios/src/stacks.c @@ -1,6 +1,6 @@ // Code for manipulating stack locations. // -// Copyright (C) 2009-2014 Kevin O'Connor +// Copyright (C) 2009-2015 Kevin O'Connor // // This file may be distributed under the terms of the GNU LGPLv3 license. @@ -13,6 +13,7 @@ #include "output.h" // dprintf #include "romfile.h" // romfile_loadint #include "stacks.h" // struct mutex_s +#include "string.h" // memset #include "util.h" // useRTC #define MAIN_STACK_MAX (1024*1024) @@ -27,40 +28,108 @@ struct { u8 cmosindex; u8 a20; u16 ss, fs, gs; + u32 cr0; struct descloc_s gdt; -} Call32Data VARLOW; +} Call16Data VARLOW; -#define C32_SLOPPY 1 -#define C32_SMM 2 +#define C16_BIG 1 +#define C16_SMM 2 int HaveSmmCall32 VARFSEG; -// Backup state in preparation for call32_smm() -static void -call32_smm_prep(void) -{ +// Backup state in preparation for call32 +static int +call32_prep(u8 method) +{ + if (!CONFIG_CALL32_SMM || method != C16_SMM) { + // Backup cr0 + u32 cr0 = cr0_read(); + if (cr0 & CR0_PE) + // Called in 16bit protected mode?! + return -1; + SET_LOW(Call16Data.cr0, cr0); + + // Backup fs/gs and gdt + SET_LOW(Call16Data.fs, GET_SEG(FS)); + SET_LOW(Call16Data.gs, GET_SEG(GS)); + struct descloc_s gdt; + sgdt(&gdt); + SET_LOW(Call16Data.gdt.length, gdt.length); + SET_LOW(Call16Data.gdt.addr, gdt.addr); + + // Enable a20 and backup its previous state + SET_LOW(Call16Data.a20, set_a20(1)); + } + + // Backup ss + SET_LOW(Call16Data.ss, GET_SEG(SS)); + // Backup cmos index register and disable nmi u8 cmosindex = inb(PORT_CMOS_INDEX); outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX); inb(PORT_CMOS_DATA); - SET_LOW(Call32Data.cmosindex, cmosindex); - - // Backup ss - SET_LOW(Call32Data.ss, GET_SEG(SS)); + SET_LOW(Call16Data.cmosindex, cmosindex); - SET_LOW(Call32Data.method, C32_SMM); + SET_LOW(Call16Data.method, method); + return 0; } -// Restore state backed up during call32_smm() -static void -call32_smm_post(void) +// Restore state backed up during call32 +static u8 +call32_post(void) { - SET_LOW(Call32Data.method, 0); - SET_LOW(Call32Data.ss, 0); + u8 method = GET_LOW(Call16Data.method); + SET_LOW(Call16Data.method, 0); + SET_LOW(Call16Data.ss, 0); + + if (!CONFIG_CALL32_SMM || method != C16_SMM) { + // Restore a20 + set_a20(GET_LOW(Call16Data.a20)); + + // Restore gdt and fs/gs + struct descloc_s gdt; + gdt.length = GET_LOW(Call16Data.gdt.length); + gdt.addr = GET_LOW(Call16Data.gdt.addr); + lgdt(&gdt); + SET_SEG(FS, GET_LOW(Call16Data.fs)); + SET_SEG(GS, GET_LOW(Call16Data.gs)); + + // Restore cr0 + u32 cr0_caching = GET_LOW(Call16Data.cr0) & (CR0_CD|CR0_NW); + if (cr0_caching) + cr0_mask(CR0_CD|CR0_NW, cr0_caching); + } // Restore cmos index register - outb(GET_LOW(Call32Data.cmosindex), PORT_CMOS_INDEX); + outb(GET_LOW(Call16Data.cmosindex), PORT_CMOS_INDEX); inb(PORT_CMOS_DATA); + return method; +} + +// Force next call16() to restore to a pristine cpu environment state +static void +call16_override(int big) +{ + ASSERT32FLAT(); + if (getesp() > BUILD_STACK_ADDR) + panic("call16_override with invalid stack\n"); + memset(&Call16Data, 0, sizeof(Call16Data)); + if (big) { + Call16Data.method = C16_BIG; + Call16Data.a20 = 1; + } else { + Call16Data.a20 = !CONFIG_DISABLE_A20; + } +} + +// 16bit handler code called from call16() / call16_smm() +u32 VISIBLE16 +call16_helper(u32 eax, u32 edx, u32 (*func)(u32 eax, u32 edx)) +{ + u8 method = call32_post(); + u32 ret = func(eax, edx); + call32_prep(method); + return ret; } #define ASM32_SWITCH16 " .pushsection .text.32fseg." UNIQSEC "\n .code16\n" @@ -74,7 +143,7 @@ call32_smm(void *func, u32 eax) { ASSERT16(); dprintf(9, "call32_smm %p %x\n", func, eax); - call32_smm_prep(); + call32_prep(C16_SMM); u32 bkup_esp; asm volatile( // Backup esp / set esp to flat stack location @@ -109,24 +178,12 @@ call32_smm(void *func, u32 eax) : "=&r" (bkup_esp), "+r" (eax) : "r" (func) : "eax", "ecx", "edx", "ebx", "cc", "memory"); - call32_smm_post(); + call32_post(); dprintf(9, "call32_smm done %p %x\n", func, eax); return eax; } -// 16bit handler code called from call16_smm() -u32 VISIBLE16 -call16_smm_helper(u32 eax, u32 edx, u32 (*func)(u32 eax, u32 edx)) -{ - if (!CONFIG_CALL32_SMM) - return eax; - call32_smm_post(); - u32 ret = func(eax, edx); - call32_smm_prep(); - return ret; -} - static u32 call16_smm(u32 eax, u32 edx, void *func) { @@ -135,7 +192,7 @@ call16_smm(u32 eax, u32 edx, void *func) return eax; func -= BUILD_BIOS_ADDR; dprintf(9, "call16_smm %p %x %x\n", func, eax, edx); - u32 stackoffset = Call32Data.ss << 4; + u32 stackoffset = Call16Data.ss << 4; asm volatile( // Restore esp " subl %0, %%esp\n" @@ -151,7 +208,7 @@ call16_smm(u32 eax, u32 edx, void *func) ASM32_SWITCH16 "1:movl %1, %%eax\n" " movl %3, %%ecx\n" - " calll _cfunc16_call16_smm_helper\n" + " calll _cfunc16_call16_helper\n" " movl %%eax, %1\n" " movl $" __stringify(CALL32SMM_CMDID) ", %%eax\n" @@ -170,61 +227,18 @@ call16_smm(u32 eax, u32 edx, void *func) return eax; } -// Backup state in preparation for call32_sloppy() -static void -call32_sloppy_prep(void) -{ - // Backup cmos index register and disable nmi - u8 cmosindex = inb(PORT_CMOS_INDEX); - outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX); - inb(PORT_CMOS_DATA); - SET_LOW(Call32Data.cmosindex, cmosindex); - - // Enable a20 and backup it's previous state - SET_LOW(Call32Data.a20, set_a20(1)); - - // Backup ss/fs/gs and gdt - SET_LOW(Call32Data.ss, GET_SEG(SS)); - SET_LOW(Call32Data.fs, GET_SEG(FS)); - SET_LOW(Call32Data.gs, GET_SEG(GS)); - struct descloc_s gdt; - sgdt(&gdt); - SET_LOW(Call32Data.gdt.length, gdt.length); - SET_LOW(Call32Data.gdt.addr, gdt.addr); - - SET_LOW(Call32Data.method, C32_SLOPPY); -} - -// Restore state backed up during call32_sloppy() -static void -call32_sloppy_post(void) -{ - SET_LOW(Call32Data.method, 0); - SET_LOW(Call32Data.ss, 0); - - // Restore gdt and fs/gs - struct descloc_s gdt; - gdt.length = GET_LOW(Call32Data.gdt.length); - gdt.addr = GET_LOW(Call32Data.gdt.addr); - lgdt(&gdt); - SET_SEG(FS, GET_LOW(Call32Data.fs)); - SET_SEG(GS, GET_LOW(Call32Data.gs)); - - // Restore a20 - set_a20(GET_LOW(Call32Data.a20)); - - // Restore cmos index register - outb(GET_LOW(Call32Data.cmosindex), PORT_CMOS_INDEX); - inb(PORT_CMOS_DATA); -} - -// Call a C function in 32bit mode. This clobbers the 16bit segment -// selector registers. -static u32 -call32_sloppy(void *func, u32 eax) +// Call a 32bit SeaBIOS function from a 16bit SeaBIOS function. +u32 VISIBLE16 +__call32(void *func, u32 eax, u32 errret) { ASSERT16(); - call32_sloppy_prep(); + if (CONFIG_CALL32_SMM && GET_GLOBAL(HaveSmmCall32)) + return call32_smm(func, eax); + // Jump direclty to 32bit mode - this clobbers the 16bit segment + // selector registers. + int ret = call32_prep(C16_BIG); + if (ret) + return errret; u32 bkup_ss, bkup_esp; asm volatile( // Backup ss/esp / set esp to flat stack location @@ -236,7 +250,7 @@ call32_sloppy(void *func, u32 eax) // Transition to 32bit mode, call func, return to 16bit " movl $(" __stringify(BUILD_BIOS_ADDR) " + 1f), %%edx\n" - " jmp transition32\n" + " jmp transition32_nmi_off\n" ASM16_SWITCH32 "1:calll *%3\n" " movl $2f, %%edx\n" @@ -250,136 +264,52 @@ call32_sloppy(void *func, u32 eax) : "=&r" (bkup_ss), "=&r" (bkup_esp), "+a" (eax) : "r" (func) : "ecx", "edx", "cc", "memory"); - call32_sloppy_post(); + call32_post(); return eax; } -// 16bit handler code called from call16_sloppy() -u32 VISIBLE16 -call16_sloppy_helper(u32 eax, u32 edx, u32 (*func)(u32 eax, u32 edx)) -{ - call32_sloppy_post(); - u32 ret = func(eax, edx); - call32_sloppy_prep(); - return ret; -} - -// Jump back to 16bit mode while in 32bit mode from call32_sloppy() +// Call a 16bit SeaBIOS function, restoring the mode from last call32(). static u32 -call16_sloppy(u32 eax, u32 edx, void *func) +call16(u32 eax, u32 edx, void *func) { ASSERT32FLAT(); if (getesp() > MAIN_STACK_MAX) - panic("call16_sloppy with invalid stack\n"); + panic("call16 with invalid stack\n"); + if (CONFIG_CALL32_SMM && Call16Data.method == C16_SMM) + return call16_smm(eax, edx, func); + + extern void transition16big(void); + extern void transition16(void); + void *thunk = transition16; + if (Call16Data.method == C16_BIG || in_post()) + thunk = transition16big; func -= BUILD_BIOS_ADDR; - u32 stackseg = Call32Data.ss; + u32 stackseg = Call16Data.ss; asm volatile( // Transition to 16bit mode " movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%edx\n" - " jmp transition16big\n" + " jmp *%%ecx\n" // Setup ss/esp and call func ASM32_SWITCH16 - "1:movl %3, %%ecx\n" - " shll $4, %3\n" + "1:movl %2, %%ecx\n" + " shll $4, %2\n" " movw %%cx, %%ss\n" - " subl %3, %%esp\n" + " subl %2, %%esp\n" " movw %%cx, %%ds\n" - " movl %2, %%edx\n" - " movl %1, %%ecx\n" - " calll _cfunc16_call16_sloppy_helper\n" + " movl %4, %%edx\n" + " movl %3, %%ecx\n" + " calll _cfunc16_call16_helper\n" // Return to 32bit and restore esp " movl $2f, %%edx\n" - " jmp transition32\n" - ASM32_BACK32 - "2:addl %3, %%esp\n" - : "+a" (eax) - : "r" (func), "r" (edx), "r" (stackseg) - : "edx", "ecx", "cc", "memory"); - return eax; -} - -// Call a 32bit SeaBIOS function from a 16bit SeaBIOS function. -u32 VISIBLE16 -call32(void *func, u32 eax, u32 errret) -{ - ASSERT16(); - if (CONFIG_CALL32_SMM && GET_GLOBAL(HaveSmmCall32)) - return call32_smm(func, eax); - u32 cr0 = getcr0(); - if (cr0 & CR0_PE) - // Called in 16bit protected mode?! - return errret; - return call32_sloppy(func, eax); -} - -// Call a 16bit SeaBIOS function from a 32bit SeaBIOS function. -static u32 -call16(u32 eax, u32 edx, void *func) -{ - ASSERT32FLAT(); - if (getesp() > BUILD_STACK_ADDR) - panic("call16 with invalid stack\n"); - func -= BUILD_BIOS_ADDR; - asm volatile( - // Transition to 16bit mode - " movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%edx\n" - " jmp transition16\n" - // Call func - ASM32_SWITCH16 - "1:movl %2, %%edx\n" - " calll *%1\n" - // Return to 32bit - " movl $2f, %%edx\n" - " jmp transition32\n" - ASM32_BACK32 - "2:\n" - : "+a" (eax) - : "r" (func), "r" (edx) - : "edx", "ecx", "cc", "memory"); - return eax; -} - -// Call a 16bit SeaBIOS function in "big real" mode. -static u32 -call16big(u32 eax, u32 edx, void *func) -{ - ASSERT32FLAT(); - if (getesp() > BUILD_STACK_ADDR) - panic("call16big with invalid stack\n"); - func -= BUILD_BIOS_ADDR; - asm volatile( - // Transition to 16bit mode - " movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%edx\n" - " jmp transition16big\n" - // Call func - ASM32_SWITCH16 - "1:movl %2, %%edx\n" - " calll *%1\n" - // Return to 32bit - " movl $2f, %%edx\n" - " jmp transition32\n" + " jmp transition32_nmi_off\n" ASM32_BACK32 - "2:\n" - : "+a" (eax) + "2:addl %2, %%esp\n" + : "+a" (eax), "+c"(thunk), "+r"(stackseg) : "r" (func), "r" (edx) - : "edx", "ecx", "cc", "memory"); + : "edx", "cc", "memory"); return eax; } -// Call a 16bit SeaBIOS function, restoring the mode from last call32(). -static u32 -call16_back(u32 eax, u32 edx, void *func) -{ - ASSERT32FLAT(); - if (CONFIG_CALL32_SMM && Call32Data.method == C32_SMM) - return call16_smm(eax, edx, func); - if (Call32Data.method == C32_SLOPPY) - return call16_sloppy(eax, edx, func); - if (in_post()) - return call16big(eax, edx, func); - return call16(eax, edx, func); -} - /**************************************************************** * Extra 16bit stack @@ -398,7 +328,7 @@ on_extra_stack(void) // Switch to the extra stack and call a function. u32 -stack_hop(u32 eax, u32 edx, void *func) +__stack_hop(u32 eax, u32 edx, void *func) { if (on_extra_stack()) return ((u32 (*)(u32, u32))func)(eax, edx); @@ -431,10 +361,10 @@ stack_hop(u32 eax, u32 edx, void *func) // Switch back to original caller's stack and call a function. u32 -stack_hop_back(u32 eax, u32 edx, void *func) +__stack_hop_back(u32 eax, u32 edx, void *func) { if (!MODESEGMENT) - return call16_back(eax, edx, func); + return call16(eax, edx, func); if (!MODE16 || !on_extra_stack()) return ((u32 (*)(u32, u32))func)(eax, edx); ASSERT16(); @@ -474,8 +404,7 @@ void VISIBLE16 _farcall16(struct bregs *callregs, u16 callregseg) { if (need_hop_back()) { - extern void _cfunc16__farcall16(void); - stack_hop_back((u32)callregs, callregseg, _cfunc16__farcall16); + stack_hop_back(_farcall16, callregs, callregseg); return; } ASSERT16(); @@ -486,18 +415,20 @@ _farcall16(struct bregs *callregs, u16 callregseg) : "ebx", "ecx", "esi", "edi", "cc", "memory"); } +// Invoke external 16bit code. void farcall16(struct bregs *callregs) { - extern void _cfunc16__farcall16(void); - call16((u32)callregs, 0, _cfunc16__farcall16); + call16_override(0); + _farcall16(callregs, 0); } +// Invoke external 16bit code in "big real" mode. void farcall16big(struct bregs *callregs) { - extern void _cfunc16__farcall16(void); - call16big((u32)callregs, 0, _cfunc16__farcall16); + call16_override(1); + _farcall16(callregs, 0); } // Invoke a 16bit software interrupt. @@ -507,7 +438,7 @@ __call16_int(struct bregs *callregs, u16 offset) callregs->code.offset = offset; if (!MODESEGMENT) { callregs->code.seg = SEG_BIOS; - _farcall16((void*)callregs - Call32Data.ss * 16, Call32Data.ss); + _farcall16((void*)callregs - Call16Data.ss * 16, Call16Data.ss); return; } callregs->code.seg = GET_SEG(CS); @@ -520,7 +451,7 @@ reset(void) { extern void reset_vector(void) __noreturn; if (!MODE16) - call16_back(0, 0, reset_vector); + call16(0, 0, reset_vector); reset_vector(); } @@ -558,12 +489,13 @@ getCurThread(void) return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE); } -static int ThreadControl; +static u8 CanInterrupt, ThreadControl; // Initialize the support for internal threads. void -thread_init(void) +thread_setup(void) { + CanInterrupt = 1; if (! CONFIG_THREADS) return; ThreadControl = romfile_loadint("etc/threads", 1); @@ -573,7 +505,7 @@ thread_init(void) int threads_during_optionroms(void) { - return CONFIG_THREADS && ThreadControl == 2 && in_post(); + return CONFIG_THREADS && CONFIG_RTC_TIMER && ThreadControl == 2 && in_post(); } // Switch to next thread stack. @@ -660,11 +592,17 @@ fail: void VISIBLE16 check_irqs(void) { + if (!MODESEGMENT && !CanInterrupt) { + // Can't enable interrupts (PIC and/or IVT not yet setup) + cpu_relax(); + return; + } if (need_hop_back()) { - extern void _cfunc16_check_irqs(void); - stack_hop_back(0, 0, _cfunc16_check_irqs); + stack_hop_back(check_irqs, 0, 0); return; } + if (MODE16) + clock_poll_irq(); asm volatile("sti ; nop ; rep ; nop ; cli ; cld" : : :"memory"); } @@ -689,8 +627,7 @@ void VISIBLE16 wait_irq(void) { if (need_hop_back()) { - extern void _cfunc16_wait_irq(void); - stack_hop_back(0, 0, _cfunc16_wait_irq); + stack_hop_back(wait_irq, 0, 0); return; } asm volatile("sti ; hlt ; cli ; cld": : :"memory"); @@ -700,8 +637,9 @@ wait_irq(void) void yield_toirq(void) { - if (!MODESEGMENT && have_threads()) { - // Threads still active - do a yield instead. + if (!CONFIG_HARDWARE_IRQ + || (!MODESEGMENT && (have_threads() || !CanInterrupt))) { + // Threads still active or irqs not available - do a yield instead. yield(); return; } @@ -794,9 +732,8 @@ yield_preempt(void) void check_preempt(void) { - extern void _cfunc32flat_yield_preempt(void); if (CONFIG_THREADS && GET_GLOBAL(CanPreempt) && have_threads()) - call32(_cfunc32flat_yield_preempt, 0, 0); + call32(yield_preempt, 0, 0); } @@ -817,11 +754,10 @@ call32_params_helper(struct call32_params_s *params) } u32 -call32_params(void *func, u32 eax, u32 edx, u32 ecx, u32 errret) +__call32_params(void *func, u32 eax, u32 edx, u32 ecx, u32 errret) { ASSERT16(); struct call32_params_s params = {func, eax, edx, ecx}; - extern void _cfunc32flat_call32_params_helper(void); - return call32(_cfunc32flat_call32_params_helper - , (u32)MAKE_FLATPTR(GET_SEG(SS), ¶ms), errret); + return call32(call32_params_helper, MAKE_FLATPTR(GET_SEG(SS), ¶ms) + , errret); } diff --git a/qemu/roms/seabios/src/stacks.h b/qemu/roms/seabios/src/stacks.h index 82c4c3c85..c71bdc8e3 100644 --- a/qemu/roms/seabios/src/stacks.h +++ b/qemu/roms/seabios/src/stacks.h @@ -10,17 +10,27 @@ // stacks.c extern int HaveSmmCall32; -u32 call32(void *func, u32 eax, u32 errret); +u32 __call32(void *func, u32 eax, u32 errret); +#define call32(func, eax, errret) ({ \ + extern void _cfunc32flat_ ##func (void); \ + __call32( _cfunc32flat_ ##func , (u32)(eax), (errret)); \ + }) extern u8 ExtraStack[], *StackPos; -u32 stack_hop(u32 eax, u32 edx, void *func); -u32 stack_hop_back(u32 eax, u32 edx, void *func); +u32 __stack_hop(u32 eax, u32 edx, void *func); +#define stack_hop(func, eax, edx) \ + __stack_hop((u32)(eax), (u32)(edx), (func)) +u32 __stack_hop_back(u32 eax, u32 edx, void *func); +#define stack_hop_back(func, eax, edx) ({ \ + extern void _cfunc16_ ##func (void); \ + __stack_hop_back((u32)(eax), (u32)(edx), _cfunc16_ ##func ); \ + }) int on_extra_stack(void); struct bregs; void farcall16(struct bregs *callregs); void farcall16big(struct bregs *callregs); void __call16_int(struct bregs *callregs, u16 offset); #define call16_int(nr, callregs) do { \ - extern void irq_trampoline_ ##nr (); \ + extern void irq_trampoline_ ##nr (void); \ __call16_int((callregs), (u32)&irq_trampoline_ ##nr ); \ } while (0) void reset(void); @@ -28,7 +38,7 @@ extern struct thread_info MainThread; struct thread_info *getCurThread(void); void yield(void); void yield_toirq(void); -void thread_init(void); +void thread_setup(void); int threads_during_optionroms(void); void run_thread(void (*func)(void*), void *data); void wait_threads(void); @@ -39,7 +49,12 @@ void start_preempt(void); void finish_preempt(void); int wait_preempt(void); void check_preempt(void); -u32 call32_params(void *func, u32 eax, u32 edx, u32 ecx, u32 errret); +u32 __call32_params(void *func, u32 eax, u32 edx, u32 ecx, u32 errret); +#define call32_params(func, eax, edx, ecx, errret) ({ \ + extern void _cfunc32flat_ ##func (void); \ + __call32_params( _cfunc32flat_ ##func , (u32)(eax), (u32)(edx) \ + , (u32)(ecx), (errret)); \ + }) // Inline functions diff --git a/qemu/roms/seabios/src/std/acpi.h b/qemu/roms/seabios/src/std/acpi.h index e0d9516ba..b672bbee4 100644 --- a/qemu/roms/seabios/src/std/acpi.h +++ b/qemu/roms/seabios/src/std/acpi.h @@ -294,4 +294,24 @@ struct acpi_table_mcfg { struct acpi_mcfg_allocation allocation[0]; } PACKED; + +struct rsdt_descriptor { + ACPI_TABLE_HEADER_DEF + u32 entry[1]; +} PACKED; + +#define TCPA_SIGNATURE 0x41504354 +struct tcpa_descriptor_rev2 +{ + ACPI_TABLE_HEADER_DEF + u16 platform_class; + u32 log_area_minimum_length; + u64 log_area_start_address; +} PACKED; + +/* TCPA ACPI definitions */ +#define TCPA_ACPI_CLASS_CLIENT 0 +#define TCPA_ACPI_CLASS_SERVER 1 + + #endif // acpi.h diff --git a/qemu/roms/seabios/src/std/bda.h b/qemu/roms/seabios/src/std/bda.h index c321266e2..4ad6605d4 100644 --- a/qemu/roms/seabios/src/std/bda.h +++ b/qemu/roms/seabios/src/std/bda.h @@ -7,7 +7,7 @@ /**************************************************************** - * Interupt vector table + * Interrupt vector table ****************************************************************/ struct rmode_IVT { diff --git a/qemu/roms/seabios/src/std/multiboot.h b/qemu/roms/seabios/src/std/multiboot.h new file mode 100644 index 000000000..6c9512703 --- /dev/null +++ b/qemu/roms/seabios/src/std/multiboot.h @@ -0,0 +1,260 @@ +/* multiboot.h - Multiboot header file. */ +/* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY + * DEVELOPER OR DISTRIBUTOR 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 MULTIBOOT_HEADER +#define MULTIBOOT_HEADER 1 + +/* How many bytes from the start of the file we search for the header. */ +#define MULTIBOOT_SEARCH 8192 +#define MULTIBOOT_HEADER_ALIGN 4 + +/* The magic field should contain this. */ +#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 + +/* This should be in %eax. */ +#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 + +/* Alignment of multiboot modules. */ +#define MULTIBOOT_MOD_ALIGN 0x00001000 + +/* Alignment of the multiboot info structure. */ +#define MULTIBOOT_INFO_ALIGN 0x00000004 + +/* Flags set in the 'flags' member of the multiboot header. */ + +/* Align all boot modules on i386 page (4KB) boundaries. */ +#define MULTIBOOT_PAGE_ALIGN 0x00000001 + +/* Must pass memory information to OS. */ +#define MULTIBOOT_MEMORY_INFO 0x00000002 + +/* Must pass video information to OS. */ +#define MULTIBOOT_VIDEO_MODE 0x00000004 + +/* This flag indicates the use of the address fields in the header. */ +#define MULTIBOOT_AOUT_KLUDGE 0x00010000 + +/* Flags to be set in the 'flags' member of the multiboot info structure. */ + +/* is there basic lower/upper memory information? */ +#define MULTIBOOT_INFO_MEMORY 0x00000001 +/* is there a boot device set? */ +#define MULTIBOOT_INFO_BOOTDEV 0x00000002 +/* is the command-line defined? */ +#define MULTIBOOT_INFO_CMDLINE 0x00000004 +/* are there modules to do something with? */ +#define MULTIBOOT_INFO_MODS 0x00000008 + +/* These next two are mutually exclusive */ + +/* is there a symbol table loaded? */ +#define MULTIBOOT_INFO_AOUT_SYMS 0x00000010 +/* is there an ELF section header table? */ +#define MULTIBOOT_INFO_ELF_SHDR 0X00000020 + +/* is there a full memory map? */ +#define MULTIBOOT_INFO_MEM_MAP 0x00000040 + +/* Is there drive info? */ +#define MULTIBOOT_INFO_DRIVE_INFO 0x00000080 + +/* Is there a config table? */ +#define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100 + +/* Is there a boot loader name? */ +#define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200 + +/* Is there a APM table? */ +#define MULTIBOOT_INFO_APM_TABLE 0x00000400 + +/* Is there video information? */ +#define MULTIBOOT_INFO_VBE_INFO 0x00000800 +#define MULTIBOOT_INFO_FRAMEBUFFER_INFO 0x00001000 + +#ifndef ASM_FILE + +typedef unsigned char multiboot_uint8_t; +typedef unsigned short multiboot_uint16_t; +typedef unsigned int multiboot_uint32_t; +typedef unsigned long long multiboot_uint64_t; + +struct multiboot_header +{ + /* Must be MULTIBOOT_MAGIC - see above. */ + multiboot_uint32_t magic; + + /* Feature flags. */ + multiboot_uint32_t flags; + + /* The above fields plus this one must equal 0 mod 2^32. */ + multiboot_uint32_t checksum; + + /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */ + multiboot_uint32_t header_addr; + multiboot_uint32_t load_addr; + multiboot_uint32_t load_end_addr; + multiboot_uint32_t bss_end_addr; + multiboot_uint32_t entry_addr; + + /* These are only valid if MULTIBOOT_VIDEO_MODE is set. */ + multiboot_uint32_t mode_type; + multiboot_uint32_t width; + multiboot_uint32_t height; + multiboot_uint32_t depth; +}; + +/* The symbol table for a.out. */ +struct multiboot_aout_symbol_table +{ + multiboot_uint32_t tabsize; + multiboot_uint32_t strsize; + multiboot_uint32_t addr; + multiboot_uint32_t reserved; +}; +typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t; + +/* The section header table for ELF. */ +struct multiboot_elf_section_header_table +{ + multiboot_uint32_t num; + multiboot_uint32_t size; + multiboot_uint32_t addr; + multiboot_uint32_t shndx; +}; +typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t; + +struct multiboot_info +{ + /* Multiboot info version number */ + multiboot_uint32_t flags; + + /* Available memory from BIOS */ + multiboot_uint32_t mem_lower; + multiboot_uint32_t mem_upper; + + /* "root" partition */ + multiboot_uint32_t boot_device; + + /* Kernel command line */ + multiboot_uint32_t cmdline; + + /* Boot-Module list */ + multiboot_uint32_t mods_count; + multiboot_uint32_t mods_addr; + + union + { + multiboot_aout_symbol_table_t aout_sym; + multiboot_elf_section_header_table_t elf_sec; + } u; + + /* Memory Mapping buffer */ + multiboot_uint32_t mmap_length; + multiboot_uint32_t mmap_addr; + + /* Drive Info buffer */ + multiboot_uint32_t drives_length; + multiboot_uint32_t drives_addr; + + /* ROM configuration table */ + multiboot_uint32_t config_table; + + /* Boot Loader Name */ + multiboot_uint32_t boot_loader_name; + + /* APM table */ + multiboot_uint32_t apm_table; + + /* Video */ + multiboot_uint32_t vbe_control_info; + multiboot_uint32_t vbe_mode_info; + multiboot_uint16_t vbe_mode; + multiboot_uint16_t vbe_interface_seg; + multiboot_uint16_t vbe_interface_off; + multiboot_uint16_t vbe_interface_len; + + multiboot_uint64_t framebuffer_addr; + multiboot_uint32_t framebuffer_pitch; + multiboot_uint32_t framebuffer_width; + multiboot_uint32_t framebuffer_height; + multiboot_uint8_t framebuffer_bpp; +#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 +#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 +#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 + multiboot_uint8_t framebuffer_type; + union + { + struct + { + multiboot_uint32_t framebuffer_palette_addr; + multiboot_uint16_t framebuffer_palette_num_colors; + }; + struct + { + multiboot_uint8_t framebuffer_red_field_position; + multiboot_uint8_t framebuffer_red_mask_size; + multiboot_uint8_t framebuffer_green_field_position; + multiboot_uint8_t framebuffer_green_mask_size; + multiboot_uint8_t framebuffer_blue_field_position; + multiboot_uint8_t framebuffer_blue_mask_size; + }; + }; +}; +typedef struct multiboot_info multiboot_info_t; + +struct multiboot_color +{ + multiboot_uint8_t red; + multiboot_uint8_t green; + multiboot_uint8_t blue; +}; + +struct multiboot_mmap_entry +{ + multiboot_uint32_t size; + multiboot_uint64_t addr; + multiboot_uint64_t len; +#define MULTIBOOT_MEMORY_AVAILABLE 1 +#define MULTIBOOT_MEMORY_RESERVED 2 +#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 +#define MULTIBOOT_MEMORY_NVS 4 +#define MULTIBOOT_MEMORY_BADRAM 5 + multiboot_uint32_t type; +} __attribute__((packed)); +typedef struct multiboot_mmap_entry multiboot_memory_map_t; + +struct multiboot_mod_list +{ + /* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */ + multiboot_uint32_t mod_start; + multiboot_uint32_t mod_end; + + /* Module command line */ + multiboot_uint32_t cmdline; + + /* padding to take it to 16 bytes (must be zero) */ + multiboot_uint32_t pad; +}; +typedef struct multiboot_mod_list multiboot_module_t; + +#endif /* ! ASM_FILE */ + +#endif /* ! MULTIBOOT_HEADER */ diff --git a/qemu/roms/seabios/src/std/smbios.h b/qemu/roms/seabios/src/std/smbios.h index 05137167a..4ccf2ea34 100644 --- a/qemu/roms/seabios/src/std/smbios.h +++ b/qemu/roms/seabios/src/std/smbios.h @@ -3,11 +3,13 @@ #include "types.h" // u32 +#define SMBIOS_SIGNATURE 0x5f4d535f // "_SM_" + /* SMBIOS entry point -- must be written to a 16-bit aligned address between 0xf0000 and 0xfffff. */ struct smbios_entry_point { - char anchor_string[4]; + u32 signature; u8 checksum; u8 length; u8 smbios_major_version; diff --git a/qemu/roms/seabios/src/string.c b/qemu/roms/seabios/src/string.c index 2e4e43746..adb8198f8 100644 --- a/qemu/roms/seabios/src/string.c +++ b/qemu/roms/seabios/src/string.c @@ -227,7 +227,7 @@ strtcpy(char *dest, const char *src, size_t len) return dest; } -// locate first occurance of character c in the string s +// locate first occurrence of character c in the string s char * strchr(const char *s, int c) { diff --git a/qemu/roms/seabios/src/string.h b/qemu/roms/seabios/src/string.h index a557d6a44..d069989db 100644 --- a/qemu/roms/seabios/src/string.h +++ b/qemu/roms/seabios/src/string.h @@ -11,12 +11,12 @@ size_t strlen(const char *s); int memcmp_far(u16 s1seg, const void *s1, u16 s2seg, const void *s2, size_t n); int memcmp(const void *s1, const void *s2, size_t n); int strcmp(const char *s1, const char *s2); -inline void memset_far(u16 d_seg, void *d_far, u8 c, size_t len); -inline void memset16_far(u16 d_seg, void *d_far, u16 c, size_t len); +void memset_far(u16 d_seg, void *d_far, u8 c, size_t len); +void memset16_far(u16 d_seg, void *d_far, u16 c, size_t len); void *memset(void *s, int c, size_t n); void memset_fl(void *ptr, u8 val, size_t size); -inline void memcpy_far(u16 d_seg, void *d_far - , u16 s_seg, const void *s_far, size_t len); +void memcpy_far(u16 d_seg, void *d_far + , u16 s_seg, const void *s_far, size_t len); void memcpy_fl(void *d_fl, const void *s_fl, size_t len); void *memcpy(void *d1, const void *s1, size_t len); #if MODESEGMENT == 0 diff --git a/qemu/roms/seabios/src/system.c b/qemu/roms/seabios/src/system.c index 60a6fce58..438e60e2c 100644 --- a/qemu/roms/seabios/src/system.c +++ b/qemu/roms/seabios/src/system.c @@ -7,9 +7,9 @@ #include "biosvar.h" // GET_GLOBAL #include "bregs.h" // struct bregs +#include "e820map.h" // E820_RAM #include "hw/pic.h" // pic_reset #include "malloc.h" // LegacyRamSize -#include "memmap.h" // E820_RAM #include "output.h" // debug_enter #include "string.h" // memcpy_far #include "util.h" // handle_1553 diff --git a/qemu/roms/seabios/src/tcgbios.c b/qemu/roms/seabios/src/tcgbios.c new file mode 100644 index 000000000..09954825c --- /dev/null +++ b/qemu/roms/seabios/src/tcgbios.c @@ -0,0 +1,1480 @@ +// Implementation of the TCG BIOS extension according to the specification +// described in specs found at +// http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific_implementation_specification_for_conventional_bios +// +// Copyright (C) 2006-2011, 2014, 2015 IBM Corporation +// +// Authors: +// Stefan Berger +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + + +#include "config.h" + +#include "types.h" +#include "byteorder.h" // cpu_to_* +#include "hw/tpm_drivers.h" // tpm_drivers[] +#include "farptr.h" // MAKE_FLATPTR +#include "string.h" // checksum +#include "tcgbios.h"// tpm_*, prototypes +#include "util.h" // printf, get_keystroke +#include "output.h" // dprintf +#include "std/acpi.h" // RSDP_SIGNATURE, rsdt_descriptor +#include "bregs.h" // struct bregs +#include "sha1.h" // sha1 +#include "fw/paravirt.h" // runningOnXen +#include "std/smbios.h" + +static const u8 Startup_ST_CLEAR[] = { 0x00, TPM_ST_CLEAR }; +static const u8 Startup_ST_STATE[] = { 0x00, TPM_ST_STATE }; + +static const u8 PhysicalPresence_CMD_ENABLE[] = { 0x00, 0x20 }; +static const u8 PhysicalPresence_CMD_DISABLE[] = { 0x01, 0x00 }; +static const u8 PhysicalPresence_PRESENT[] = { 0x00, 0x08 }; +static const u8 PhysicalPresence_NOT_PRESENT_LOCK[] = { 0x00, 0x14 }; + +static const u8 CommandFlag_FALSE[1] = { 0x00 }; +static const u8 CommandFlag_TRUE[1] = { 0x01 }; + +static const u8 GetCapability_Permanent_Flags[] = { + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x08 +}; + +static const u8 GetCapability_OwnerAuth[] = { + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x11 +}; + +static const u8 GetCapability_Timeouts[] = { + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x15 +}; + +static const u8 GetCapability_Durations[] = { + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x20 +}; + +static u8 evt_separator[] = {0xff,0xff,0xff,0xff}; + + +#define RSDP_CAST(ptr) ((struct rsdp_descriptor *)ptr) + +/* local function prototypes */ + +static u32 tpm_calling_int19h(void); +static u32 tpm_add_event_separators(void); +static u32 tpm_start_option_rom_scan(void); +static u32 tpm_smbios_measure(void); + +/* helper functions */ + +static inline void *input_buf32(struct bregs *regs) +{ + return MAKE_FLATPTR(regs->es, regs->di); +} + +static inline void *output_buf32(struct bregs *regs) +{ + return MAKE_FLATPTR(regs->ds, regs->si); +} + + +typedef struct { + u8 tpm_probed:1; + u8 tpm_found:1; + u8 tpm_working:1; + u8 if_shutdown:1; + u8 tpm_driver_to_use:4; +} tpm_state_t; + + +static tpm_state_t tpm_state = { + .tpm_driver_to_use = TPM_INVALID_DRIVER, +}; + + +/******************************************************** + Extensions for TCG-enabled BIOS + *******************************************************/ + + +static u32 +is_tpm_present(void) +{ + u32 rc = 0; + unsigned int i; + + for (i = 0; i < TPM_NUM_DRIVERS; i++) { + struct tpm_driver *td = &tpm_drivers[i]; + if (td->probe() != 0) { + td->init(); + tpm_state.tpm_driver_to_use = i; + rc = 1; + break; + } + } + + return rc; +} + +static void +probe_tpm(void) +{ + if (!tpm_state.tpm_probed) { + tpm_state.tpm_probed = 1; + tpm_state.tpm_found = (is_tpm_present() != 0); + tpm_state.tpm_working = tpm_state.tpm_found; + } +} + +static int +has_working_tpm(void) +{ + probe_tpm(); + + return tpm_state.tpm_working; +} + +static struct tcpa_descriptor_rev2 * +find_tcpa_by_rsdp(struct rsdp_descriptor *rsdp) +{ + u32 ctr = 0; + struct tcpa_descriptor_rev2 *tcpa = NULL; + struct rsdt_descriptor *rsdt; + u32 length; + u16 off; + + rsdt = (struct rsdt_descriptor *)rsdp->rsdt_physical_address; + if (!rsdt) + return NULL; + + length = rsdt->length; + off = offsetof(struct rsdt_descriptor, entry); + + while ((off + sizeof(rsdt->entry[0])) <= length) { + /* try all pointers to structures */ + tcpa = (struct tcpa_descriptor_rev2 *)(int)rsdt->entry[ctr]; + + /* valid TCPA ACPI table ? */ + if (tcpa->signature == TCPA_SIGNATURE && + checksum((u8 *)tcpa, tcpa->length) == 0) + break; + + tcpa = NULL; + off += sizeof(rsdt->entry[0]); + ctr++; + } + + return tcpa; +} + + +static struct tcpa_descriptor_rev2 * +find_tcpa_table(void) +{ + struct tcpa_descriptor_rev2 *tcpa = NULL; + struct rsdp_descriptor *rsdp = RsdpAddr; + + if (rsdp) + tcpa = find_tcpa_by_rsdp(rsdp); + else + tpm_state.if_shutdown = 1; + + if (!rsdp) + dprintf(DEBUG_tcg, + "TCGBIOS: RSDP was NOT found! -- Disabling interface.\n"); + else if (!tcpa) + dprintf(DEBUG_tcg, "TCGBIOS: TCPA ACPI was NOT found!\n"); + + return tcpa; +} + + +static u8 * +get_lasa_base_ptr(u32 *log_area_minimum_length) +{ + u8 *log_area_start_address = 0; + struct tcpa_descriptor_rev2 *tcpa = find_tcpa_table(); + + if (tcpa) { + log_area_start_address = (u8 *)(long)tcpa->log_area_start_address; + if (log_area_minimum_length) + *log_area_minimum_length = tcpa->log_area_minimum_length; + } + + return log_area_start_address; +} + + +/* clear the ACPI log */ +static void +reset_acpi_log(void) +{ + u32 log_area_minimum_length; + u8 *log_area_start_address = get_lasa_base_ptr(&log_area_minimum_length); + + if (log_area_start_address) + memset(log_area_start_address, 0x0, log_area_minimum_length); +} + + +/* + initialize the TCPA ACPI subsystem; find the ACPI tables and determine + where the TCPA table is. + */ +static void +tpm_acpi_init(void) +{ + tpm_state.if_shutdown = 0; + tpm_state.tpm_probed = 0; + tpm_state.tpm_found = 0; + tpm_state.tpm_working = 0; + + if (!has_working_tpm()) { + tpm_state.if_shutdown = 1; + return; + } + + reset_acpi_log(); +} + + +static u32 +transmit(u8 locty, const struct iovec iovec[], + u8 *respbuffer, u32 *respbufferlen, + enum tpmDurationType to_t) +{ + u32 rc = 0; + u32 irc; + struct tpm_driver *td; + unsigned int i; + + if (tpm_state.tpm_driver_to_use == TPM_INVALID_DRIVER) + return TCG_FATAL_COM_ERROR; + + td = &tpm_drivers[tpm_state.tpm_driver_to_use]; + + irc = td->activate(locty); + if (irc != 0) { + /* tpm could not be activated */ + return TCG_FATAL_COM_ERROR; + } + + for (i = 0; iovec[i].length; i++) { + irc = td->senddata(iovec[i].data, + iovec[i].length); + if (irc != 0) + return TCG_FATAL_COM_ERROR; + } + + irc = td->waitdatavalid(); + if (irc != 0) + return TCG_FATAL_COM_ERROR; + + irc = td->waitrespready(to_t); + if (irc != 0) + return TCG_FATAL_COM_ERROR; + + irc = td->readresp(respbuffer, + respbufferlen); + if (irc != 0) + return TCG_FATAL_COM_ERROR; + + td->ready(); + + return rc; +} + + +/* + * Send a TPM command with the given ordinal. Append the given buffer + * containing all data in network byte order to the command (this is + * the custom part per command) and expect a response of the given size. + * If a buffer is provided, the response will be copied into it. + */ +static u32 +build_and_send_cmd_od(u8 locty, u32 ordinal, const u8 *append, u32 append_size, + u8 *resbuffer, u32 return_size, u32 *returnCode, + const u8 *otherdata, u32 otherdata_size, + enum tpmDurationType to_t) +{ +#define MAX_APPEND_SIZE sizeof(GetCapability_Timeouts) +#define MAX_RESPONSE_SIZE sizeof(struct tpm_res_getcap_perm_flags) + u32 rc; + u8 ibuffer[TPM_REQ_HEADER_SIZE + MAX_APPEND_SIZE]; + u8 obuffer[MAX_RESPONSE_SIZE]; + struct tpm_req_header *trqh = (struct tpm_req_header *)ibuffer; + struct tpm_rsp_header *trsh = (struct tpm_rsp_header *)obuffer; + struct iovec iovec[3]; + u32 obuffer_len = sizeof(obuffer); + u32 idx = 1; + + if (append_size > MAX_APPEND_SIZE || + return_size > MAX_RESPONSE_SIZE) { + dprintf(DEBUG_tcg, "TCGBIOS: size of requested buffers too big."); + return TCG_FIRMWARE_ERROR; + } + + iovec[0].data = trqh; + iovec[0].length = TPM_REQ_HEADER_SIZE + append_size; + + if (otherdata) { + iovec[1].data = (void *)otherdata; + iovec[1].length = otherdata_size; + idx = 2; + } + + iovec[idx].data = NULL; + iovec[idx].length = 0; + + memset(ibuffer, 0x0, sizeof(ibuffer)); + memset(obuffer, 0x0, sizeof(obuffer)); + + trqh->tag = cpu_to_be16(TPM_TAG_RQU_CMD); + trqh->totlen = cpu_to_be32(TPM_REQ_HEADER_SIZE + append_size + + otherdata_size); + trqh->ordinal = cpu_to_be32(ordinal); + + if (append_size) + memcpy((char *)trqh + sizeof(*trqh), + append, append_size); + + rc = transmit(locty, iovec, obuffer, &obuffer_len, to_t); + if (rc) + return rc; + + *returnCode = be32_to_cpu(trsh->errcode); + + if (resbuffer) + memcpy(resbuffer, trsh, return_size); + + return 0; +} + + +static u32 +build_and_send_cmd(u8 locty, u32 ordinal, const u8 *append, u32 append_size, + u8 *resbuffer, u32 return_size, u32 *returnCode, + enum tpmDurationType to_t) +{ + return build_and_send_cmd_od(locty, ordinal, append, append_size, + resbuffer, return_size, returnCode, + NULL, 0, to_t); +} + + +static u32 +determine_timeouts(void) +{ + u32 rc; + u32 returnCode; + struct tpm_res_getcap_timeouts timeouts; + struct tpm_res_getcap_durations durations; + struct tpm_driver *td = &tpm_drivers[tpm_state.tpm_driver_to_use]; + u32 i; + + rc = build_and_send_cmd(0, TPM_ORD_GetCapability, + GetCapability_Timeouts, + sizeof(GetCapability_Timeouts), + (u8 *)&timeouts, sizeof(timeouts), + &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "TCGBIOS: Return code from TPM_GetCapability(Timeouts)" + " = 0x%08x\n", returnCode); + + if (rc || returnCode) + goto err_exit; + + rc = build_and_send_cmd(0, TPM_ORD_GetCapability, + GetCapability_Durations, + sizeof(GetCapability_Durations), + (u8 *)&durations, sizeof(durations), + &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "TCGBIOS: Return code from TPM_GetCapability(Durations)" + " = 0x%08x\n", returnCode); + + if (rc || returnCode) + goto err_exit; + + for (i = 0; i < 3; i++) + durations.durations[i] = be32_to_cpu(durations.durations[i]); + + for (i = 0; i < 4; i++) + timeouts.timeouts[i] = be32_to_cpu(timeouts.timeouts[i]); + + dprintf(DEBUG_tcg, "TCGBIOS: timeouts: %u %u %u %u\n", + timeouts.timeouts[0], + timeouts.timeouts[1], + timeouts.timeouts[2], + timeouts.timeouts[3]); + + dprintf(DEBUG_tcg, "TCGBIOS: durations: %u %u %u\n", + durations.durations[0], + durations.durations[1], + durations.durations[2]); + + + td->set_timeouts(timeouts.timeouts, durations.durations); + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +tpm_startup(void) +{ + u32 rc; + u32 returnCode; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + dprintf(DEBUG_tcg, "TCGBIOS: Starting with TPM_Startup(ST_CLEAR)\n"); + rc = build_and_send_cmd(0, TPM_ORD_Startup, + Startup_ST_CLEAR, sizeof(Startup_ST_CLEAR), + NULL, 0, &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "Return code from TPM_Startup = 0x%08x\n", + returnCode); + + if (CONFIG_COREBOOT) { + /* with other firmware on the system the TPM may already have been + * initialized + */ + if (returnCode == TPM_INVALID_POSTINIT) + returnCode = 0; + } + + if (rc || returnCode) + goto err_exit; + + rc = build_and_send_cmd(0, TPM_ORD_SelfTestFull, NULL, 0, + NULL, 0, &returnCode, TPM_DURATION_TYPE_LONG); + + dprintf(DEBUG_tcg, "Return code from TPM_SelfTestFull = 0x%08x\n", + returnCode); + + if (rc || returnCode) + goto err_exit; + + rc = build_and_send_cmd(3, TSC_ORD_ResetEstablishmentBit, NULL, 0, + NULL, 0, &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "Return code from TSC_ResetEstablishmentBit = 0x%08x\n", + returnCode); + + if (rc || (returnCode != 0 && returnCode != TPM_BAD_LOCALITY)) + goto err_exit; + + rc = determine_timeouts(); + if (rc) + goto err_exit; + + rc = tpm_smbios_measure(); + if (rc) + goto err_exit; + + rc = tpm_start_option_rom_scan(); + if (rc) + goto err_exit; + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +void +tpm_setup(void) +{ + if (!CONFIG_TCGBIOS) + return; + + tpm_acpi_init(); + if (runningOnXen()) + return; + + tpm_startup(); +} + + +void +tpm_prepboot(void) +{ + u32 rc; + u32 returnCode; + + if (!CONFIG_TCGBIOS) + return; + + if (!has_working_tpm()) + return; + + rc = build_and_send_cmd(0, TPM_ORD_PhysicalPresence, + PhysicalPresence_CMD_ENABLE, + sizeof(PhysicalPresence_CMD_ENABLE), + NULL, 0, &returnCode, TPM_DURATION_TYPE_SHORT); + if (rc || returnCode) + goto err_exit; + + rc = build_and_send_cmd(0, TPM_ORD_PhysicalPresence, + PhysicalPresence_NOT_PRESENT_LOCK, + sizeof(PhysicalPresence_NOT_PRESENT_LOCK), + NULL, 0, &returnCode, TPM_DURATION_TYPE_SHORT); + if (rc || returnCode) + goto err_exit; + + rc = tpm_calling_int19h(); + if (rc) + goto err_exit; + + rc = tpm_add_event_separators(); + if (rc) + goto err_exit; + + return; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_state.tpm_working = 0; +} + +static int +is_valid_pcpes(struct pcpes *pcpes) +{ + return (pcpes->eventtype != 0); +} + + +static u8 * +get_lasa_last_ptr(u16 *entry_count, u8 **log_area_start_address_next) +{ + struct pcpes *pcpes; + u32 log_area_minimum_length = 0; + u8 *log_area_start_address_base = + get_lasa_base_ptr(&log_area_minimum_length); + u8 *log_area_start_address_last = NULL; + u8 *end = log_area_start_address_base + log_area_minimum_length; + u32 size; + + if (entry_count) + *entry_count = 0; + + if (!log_area_start_address_base) + return NULL; + + while (log_area_start_address_base < end) { + pcpes = (struct pcpes *)log_area_start_address_base; + if (!is_valid_pcpes(pcpes)) + break; + if (entry_count) + (*entry_count)++; + size = pcpes->eventdatasize + offsetof(struct pcpes, event); + log_area_start_address_last = log_area_start_address_base; + log_area_start_address_base += size; + } + + if (log_area_start_address_next) + *log_area_start_address_next = log_area_start_address_base; + + return log_area_start_address_last; +} + + +static u32 +tpm_sha1_calc(const u8 *data, u32 length, u8 *hash) +{ + u32 rc; + u32 returnCode; + struct tpm_res_sha1start start; + struct tpm_res_sha1complete complete; + u32 blocks = length / 64; + u32 rest = length & 0x3f; + u32 numbytes, numbytes_no; + u32 offset = 0; + + rc = build_and_send_cmd(0, TPM_ORD_SHA1Start, + NULL, 0, + (u8 *)&start, sizeof(start), + &returnCode, TPM_DURATION_TYPE_SHORT); + + if (rc || returnCode) + goto err_exit; + + while (blocks > 0) { + + numbytes = be32_to_cpu(start.max_num_bytes); + if (numbytes > blocks * 64) + numbytes = blocks * 64; + + numbytes_no = cpu_to_be32(numbytes); + + rc = build_and_send_cmd_od(0, TPM_ORD_SHA1Update, + (u8 *)&numbytes_no, sizeof(numbytes_no), + NULL, 0, &returnCode, + &data[offset], numbytes, + TPM_DURATION_TYPE_SHORT); + + if (rc || returnCode) + goto err_exit; + + offset += numbytes; + blocks -= (numbytes / 64); + } + + numbytes_no = cpu_to_be32(rest); + + rc = build_and_send_cmd_od(0, TPM_ORD_SHA1Complete, + (u8 *)&numbytes_no, sizeof(numbytes_no), + (u8 *)&complete, sizeof(complete), + &returnCode, + &data[offset], rest, TPM_DURATION_TYPE_SHORT); + + if (rc || returnCode) + goto err_exit; + + memcpy(hash, complete.hash, sizeof(complete.hash)); + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM SHA1 malfunctioning.\n"); + + tpm_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +sha1_calc(const u8 *data, u32 length, u8 *hash) +{ + if (length < tpm_drivers[tpm_state.tpm_driver_to_use].sha1threshold) + return tpm_sha1_calc(data, length, hash); + + return sha1(data, length, hash); +} + + +/* + * Extend the ACPI log with the given entry by copying the + * entry data into the log. + * Input + * Pointer to the structure to be copied into the log + * + * Output: + * lower 16 bits of return code contain entry number + * if entry number is '0', then upper 16 bits contain error code. + */ +static u32 +tpm_extend_acpi_log(void *entry_ptr, u16 *entry_count) +{ + u32 log_area_minimum_length, size; + u8 *log_area_start_address_base = + get_lasa_base_ptr(&log_area_minimum_length); + u8 *log_area_start_address_next = NULL; + struct pcpes *pcpes = (struct pcpes *)entry_ptr; + + get_lasa_last_ptr(entry_count, &log_area_start_address_next); + + dprintf(DEBUG_tcg, "TCGBIOS: LASA_BASE = %p, LASA_NEXT = %p\n", + log_area_start_address_base, log_area_start_address_next); + + if (log_area_start_address_next == NULL || log_area_minimum_length == 0) + return TCG_PC_LOGOVERFLOW; + + size = pcpes->eventdatasize + offsetof(struct pcpes, event); + + if ((log_area_start_address_next + size - log_area_start_address_base) > + log_area_minimum_length) { + dprintf(DEBUG_tcg, "TCGBIOS: LOG OVERFLOW: size = %d\n", size); + return TCG_PC_LOGOVERFLOW; + } + + memcpy(log_area_start_address_next, entry_ptr, size); + + (*entry_count)++; + + return 0; +} + + +static u32 +is_preboot_if_shutdown(void) +{ + return tpm_state.if_shutdown; +} + + +static u32 +shutdown_preboot_interface(void) +{ + u32 rc = 0; + + if (!is_preboot_if_shutdown()) { + tpm_state.if_shutdown = 1; + } else { + rc = TCG_INTERFACE_SHUTDOWN; + } + + return rc; +} + + +static void +tpm_shutdown(void) +{ + reset_acpi_log(); + shutdown_preboot_interface(); +} + + +static u32 +pass_through_to_tpm(struct pttti *pttti, struct pttto *pttto) +{ + u32 rc = 0; + u32 resbuflen = 0; + struct tpm_req_header *trh; + u8 locty = 0; + struct iovec iovec[2]; + const u32 *tmp; + + if (is_preboot_if_shutdown()) { + rc = TCG_INTERFACE_SHUTDOWN; + goto err_exit; + } + + trh = (struct tpm_req_header *)pttti->tpmopin; + + if (pttti->ipblength < sizeof(struct pttti) + TPM_REQ_HEADER_SIZE || + pttti->opblength < sizeof(struct pttto) || + be32_to_cpu(trh->totlen) + sizeof(struct pttti) > pttti->ipblength ) { + rc = TCG_INVALID_INPUT_PARA; + goto err_exit; + } + + resbuflen = pttti->opblength - offsetof(struct pttto, tpmopout); + + iovec[0].data = pttti->tpmopin; + tmp = (const u32 *)&((u8 *)iovec[0].data)[2]; + iovec[0].length = cpu_to_be32(*tmp); + + iovec[1].data = NULL; + iovec[1].length = 0; + + rc = transmit(locty, iovec, pttto->tpmopout, &resbuflen, + TPM_DURATION_TYPE_LONG /* worst case */); + if (rc) + goto err_exit; + + pttto->opblength = offsetof(struct pttto, tpmopout) + resbuflen; + pttto->reserved = 0; + +err_exit: + if (rc != 0) { + pttto->opblength = 4; + pttto->reserved = 0; + } + + return rc; +} + + +static u32 +tpm_extend(u8 *hash, u32 pcrindex) +{ + u32 rc; + struct pttto_extend pttto; + struct pttti_extend pttti = { + .pttti = { + .ipblength = sizeof(struct pttti_extend), + .opblength = sizeof(struct pttto_extend), + }, + .req = { + .tag = cpu_to_be16(0xc1), + .totlen = cpu_to_be32(sizeof(pttti.req)), + .ordinal = cpu_to_be32(TPM_ORD_Extend), + .pcrindex = cpu_to_be32(pcrindex), + }, + }; + + memcpy(pttti.req.digest, hash, sizeof(pttti.req.digest)); + + rc = pass_through_to_tpm(&pttti.pttti, &pttto.pttto); + + if (rc == 0) { + if (pttto.pttto.opblength < TPM_RSP_HEADER_SIZE || + pttto.pttto.opblength != + sizeof(struct pttto) + be32_to_cpu(pttto.rsp.totlen) || + be16_to_cpu(pttto.rsp.tag) != 0xc4) { + rc = TCG_FATAL_COM_ERROR; + } + } + + if (rc) + tpm_shutdown(); + + return rc; +} + + +static u32 +hash_all(const struct hai *hai, u8 *hash) +{ + if (is_preboot_if_shutdown() != 0) + return TCG_INTERFACE_SHUTDOWN; + + if (hai->ipblength != sizeof(struct hai) || + hai->hashdataptr == 0 || + hai->hashdatalen == 0 || + hai->algorithmid != TPM_ALG_SHA) + return TCG_INVALID_INPUT_PARA; + + return sha1_calc((const u8 *)hai->hashdataptr, hai->hashdatalen, hash); +} + + +static u32 +hash_log_event(const struct hlei *hlei, struct hleo *hleo) +{ + u32 rc = 0; + u16 size; + struct pcpes *pcpes; + u16 entry_count; + + if (is_preboot_if_shutdown() != 0) { + rc = TCG_INTERFACE_SHUTDOWN; + goto err_exit; + } + + size = hlei->ipblength; + if (size != sizeof(*hlei)) { + rc = TCG_INVALID_INPUT_PARA; + goto err_exit; + } + + pcpes = (struct pcpes *)hlei->logdataptr; + + if (pcpes->pcrindex >= 24 || + pcpes->pcrindex != hlei->pcrindex || + pcpes->eventtype != hlei->logeventtype) { + rc = TCG_INVALID_INPUT_PARA; + goto err_exit; + } + + if ((hlei->hashdataptr != 0) && (hlei->hashdatalen != 0)) { + rc = sha1_calc((const u8 *)hlei->hashdataptr, + hlei->hashdatalen, pcpes->digest); + if (rc) + return rc; + } + + rc = tpm_extend_acpi_log((void *)hlei->logdataptr, &entry_count); + if (rc) + goto err_exit; + + /* updating the log was fine */ + hleo->opblength = sizeof(struct hleo); + hleo->reserved = 0; + hleo->eventnumber = entry_count; + +err_exit: + if (rc != 0) { + hleo->opblength = 2; + hleo->reserved = 0; + } + + return rc; +} + + +static u32 +hash_log_extend_event(const struct hleei_short *hleei_s, struct hleeo *hleeo) +{ + u32 rc = 0; + struct hleo hleo; + struct hleei_long *hleei_l = (struct hleei_long *)hleei_s; + const void *logdataptr; + u32 logdatalen; + struct pcpes *pcpes; + + /* short or long version? */ + switch (hleei_s->ipblength) { + case sizeof(struct hleei_short): + /* short */ + logdataptr = hleei_s->logdataptr; + logdatalen = hleei_s->logdatalen; + break; + + case sizeof(struct hleei_long): + /* long */ + logdataptr = hleei_l->logdataptr; + logdatalen = hleei_l->logdatalen; + break; + + default: + /* bad input block */ + rc = TCG_INVALID_INPUT_PARA; + goto err_exit; + } + + pcpes = (struct pcpes *)logdataptr; + + struct hlei hlei = { + .ipblength = sizeof(hlei), + .hashdataptr = hleei_s->hashdataptr, + .hashdatalen = hleei_s->hashdatalen, + .pcrindex = hleei_s->pcrindex, + .logeventtype= pcpes->eventtype, + .logdataptr = logdataptr, + .logdatalen = logdatalen, + }; + + rc = hash_log_event(&hlei, &hleo); + if (rc) + goto err_exit; + + hleeo->opblength = sizeof(struct hleeo); + hleeo->reserved = 0; + hleeo->eventnumber = hleo.eventnumber; + + rc = tpm_extend(pcpes->digest, hleei_s->pcrindex); + +err_exit: + if (rc != 0) { + hleeo->opblength = 4; + hleeo->reserved = 0; + } + + return rc; + +} + + +static u32 +tss(struct ti *ti, struct to *to) +{ + u32 rc = 0; + + if (is_preboot_if_shutdown() == 0) { + rc = TCG_PC_UNSUPPORTED; + } else { + rc = TCG_INTERFACE_SHUTDOWN; + } + + to->opblength = sizeof(struct to); + to->reserved = 0; + + return rc; +} + + +static u32 +compact_hash_log_extend_event(u8 *buffer, + u32 info, + u32 length, + u32 pcrindex, + u32 *edx_ptr) +{ + u32 rc = 0; + struct hleeo hleeo; + struct pcpes pcpes = { + .pcrindex = pcrindex, + .eventtype = EV_COMPACT_HASH, + .eventdatasize = sizeof(info), + .event = info, + }; + struct hleei_short hleei = { + .ipblength = sizeof(hleei), + .hashdataptr = buffer, + .hashdatalen = length, + .pcrindex = pcrindex, + .logdataptr = &pcpes, + .logdatalen = sizeof(pcpes), + }; + + rc = hash_log_extend_event(&hleei, &hleeo); + if (rc == 0) + *edx_ptr = hleeo.eventnumber; + + return rc; +} + + +void VISIBLE32FLAT +tpm_interrupt_handler32(struct bregs *regs) +{ + if (!CONFIG_TCGBIOS) + return; + + set_cf(regs, 0); + + if (!has_working_tpm()) { + regs->eax = TCG_GENERAL_ERROR; + return; + } + + switch ((enum irq_ids)regs->al) { + case TCG_StatusCheck: + if (is_tpm_present() == 0) { + /* no TPM available */ + regs->eax = TCG_PC_TPM_NOT_PRESENT; + } else { + regs->eax = 0; + regs->ebx = TCG_MAGIC; + regs->ch = TCG_VERSION_MAJOR; + regs->cl = TCG_VERSION_MINOR; + regs->edx = 0x0; + regs->esi = (u32)get_lasa_base_ptr(NULL); + regs->edi = + (u32)get_lasa_last_ptr(NULL, NULL); + } + break; + + case TCG_HashLogExtendEvent: + regs->eax = + hash_log_extend_event( + (struct hleei_short *)input_buf32(regs), + (struct hleeo *)output_buf32(regs)); + break; + + case TCG_PassThroughToTPM: + regs->eax = + pass_through_to_tpm((struct pttti *)input_buf32(regs), + (struct pttto *)output_buf32(regs)); + break; + + case TCG_ShutdownPreBootInterface: + regs->eax = shutdown_preboot_interface(); + break; + + case TCG_HashLogEvent: + regs->eax = hash_log_event((struct hlei*)input_buf32(regs), + (struct hleo*)output_buf32(regs)); + break; + + case TCG_HashAll: + regs->eax = + hash_all((struct hai*)input_buf32(regs), + (u8 *)output_buf32(regs)); + break; + + case TCG_TSS: + regs->eax = tss((struct ti*)input_buf32(regs), + (struct to*)output_buf32(regs)); + break; + + case TCG_CompactHashLogExtendEvent: + regs->eax = + compact_hash_log_extend_event((u8 *)input_buf32(regs), + regs->esi, + regs->ecx, + regs->edx, + ®s->edx); + break; + + default: + set_cf(regs, 1); + } + + return; +} + +/* + * Add a measurement to the log; the data at data_seg:data/length are + * appended to the TCG_PCClientPCREventStruct + * + * Input parameters: + * pcrIndex : which PCR to extend + * event_type : type of event; specs section on 'Event Types' + * info : pointer to info (e.g., string) to be added to log as-is + * info_length: length of the info + * data : pointer to the data (i.e., string) to be added to the log + * data_length: length of the data + */ +static u32 +tpm_add_measurement_to_log(u32 pcrIndex, u32 event_type, + const char *info, u32 info_length, + const u8 *data, u32 data_length) +{ + u32 rc = 0; + struct hleeo hleeo; + u8 _pcpes[offsetof(struct pcpes, event) + 400]; + struct pcpes *pcpes = (struct pcpes *)_pcpes; + + if (info_length < sizeof(_pcpes) - offsetof(struct pcpes, event)) { + + pcpes->pcrindex = pcrIndex; + pcpes->eventtype = event_type; + memset(&pcpes->digest, 0x0, sizeof(pcpes->digest)); + pcpes->eventdatasize = info_length; + memcpy(&pcpes->event, info, info_length); + + struct hleei_short hleei = { + .ipblength = sizeof(hleei), + .hashdataptr = data, + .hashdatalen = data_length, + .pcrindex = pcrIndex, + .logdataptr = _pcpes, + .logdatalen = info_length + offsetof(struct pcpes, event), + }; + + rc = hash_log_extend_event(&hleei, &hleeo); + } else { + rc = TCG_GENERAL_ERROR; + } + + return rc; +} + + +/* + * Add a measurement to the list of measurements + * pcrIndex : PCR to be extended + * event_type : type of event; specs section on 'Event Types' + * data : additional parameter; used as parameter for + * 'action index' + */ +static u32 +tpm_add_measurement(u32 pcrIndex, + u16 event_type, + const char *string) +{ + u32 rc; + u32 len; + + switch (event_type) { + case EV_SEPARATOR: + len = sizeof(evt_separator); + rc = tpm_add_measurement_to_log(pcrIndex, event_type, + (char *)NULL, 0, + (u8 *)evt_separator, len); + break; + + case EV_ACTION: + rc = tpm_add_measurement_to_log(pcrIndex, event_type, + string, strlen(string), + (u8 *)string, strlen(string)); + break; + + default: + rc = TCG_INVALID_INPUT_PARA; + } + + return rc; +} + + +static u32 +tpm_calling_int19h(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + return tpm_add_measurement(4, EV_ACTION, + "Calling INT 19h"); +} + +/* + * Add event separators for PCRs 0 to 7; specs on 'Measuring Boot Events' + */ +u32 +tpm_add_event_separators(void) +{ + u32 rc; + u32 pcrIndex = 0; + + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + while (pcrIndex <= 7) { + rc = tpm_add_measurement(pcrIndex, EV_SEPARATOR, NULL); + if (rc) + break; + pcrIndex ++; + } + + return rc; +} + + +/* + * Add a measurement regarding the boot device (CDRom, Floppy, HDD) to + * the list of measurements. + */ +static u32 +tpm_add_bootdevice(u32 bootcd, u32 bootdrv) +{ + const char *string; + + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + switch (bootcd) { + case 0: + switch (bootdrv) { + case 0: + string = "Booting BCV device 00h (Floppy)"; + break; + + case 0x80: + string = "Booting BCV device 80h (HDD)"; + break; + + default: + string = "Booting unknown device"; + break; + } + + break; + + default: + string = "Booting from CD ROM device"; + } + + return tpm_add_measurement_to_log(4, EV_ACTION, + string, strlen(string), + (u8 *)string, strlen(string)); +} + + +/* + * Add measurement to the log about option rom scan + */ +u32 +tpm_start_option_rom_scan(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + return tpm_add_measurement(2, EV_ACTION, + "Start Option ROM Scan"); +} + + +/* + * Add measurement to the log about an option rom + */ +u32 +tpm_option_rom(const void *addr, u32 len) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + u32 rc; + struct pcctes_romex pcctes = { + .eventid = 7, + .eventdatasize = sizeof(u16) + sizeof(u16) + SHA1_BUFSIZE, + }; + + rc = sha1((const u8 *)addr, len, pcctes.digest); + if (rc) + return rc; + + return tpm_add_measurement_to_log(2, + EV_EVENT_TAG, + (const char *)&pcctes, sizeof(pcctes), + (u8 *)&pcctes, sizeof(pcctes)); +} + + +u32 +tpm_smbios_measure(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + u32 rc; + struct pcctes pcctes = { + .eventid = 1, + .eventdatasize = SHA1_BUFSIZE, + }; + struct smbios_entry_point *sep = SMBiosAddr; + + dprintf(DEBUG_tcg, "TCGBIOS: SMBIOS at %p\n", sep); + + if (!sep) + return 0; + + rc = sha1((const u8 *)sep->structure_table_address, + sep->structure_table_length, pcctes.digest); + if (rc) + return rc; + + return tpm_add_measurement_to_log(1, + EV_EVENT_TAG, + (const char *)&pcctes, sizeof(pcctes), + (u8 *)&pcctes, sizeof(pcctes)); +} + + +/* + * Add a measurement related to Initial Program Loader to the log. + * Creates two log entries. + * + * Input parameter: + * bootcd : 0: MBR of hdd, 1: boot image, 2: boot catalog of El Torito + * addr : address where the IP data are located + * length : IP data length in bytes + */ +static u32 +tpm_ipl(enum ipltype bootcd, const u8 *addr, u32 length) +{ + u32 rc; + const char *string; + + switch (bootcd) { + case IPL_EL_TORITO_1: + /* specs: see section 'El Torito' */ + string = "EL TORITO IPL"; + rc = tpm_add_measurement_to_log(4, EV_IPL, + string, strlen(string), + addr, length); + break; + + case IPL_EL_TORITO_2: + /* specs: see section 'El Torito' */ + string = "BOOT CATALOG"; + rc = tpm_add_measurement_to_log(5, EV_IPL_PARTITION_DATA, + string, strlen(string), + addr, length); + break; + + default: + /* specs: see section 'Hard Disk Device or Hard Disk-Like Devices' */ + /* equivalent to: dd if=/dev/hda ibs=1 count=440 | sha1sum */ + string = "MBR"; + rc = tpm_add_measurement_to_log(4, EV_IPL, + string, strlen(string), + addr, 0x1b8); + + if (rc) + break; + + /* equivalent to: dd if=/dev/hda ibs=1 count=72 skip=440 | sha1sum */ + string = "MBR PARTITION_TABLE"; + rc = tpm_add_measurement_to_log(5, EV_IPL_PARTITION_DATA, + string, strlen(string), + addr + 0x1b8, 0x48); + } + + return rc; +} + +u32 +tpm_add_bcv(u32 bootdrv, const u8 *addr, u32 length) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + u32 rc = tpm_add_bootdevice(0, bootdrv); + if (rc) + return rc; + + return tpm_ipl(IPL_BCV, addr, length); +} + +u32 +tpm_add_cdrom(u32 bootdrv, const u8 *addr, u32 length) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + u32 rc = tpm_add_bootdevice(1, bootdrv); + if (rc) + return rc; + + return tpm_ipl(IPL_EL_TORITO_1, addr, length); +} + +u32 +tpm_add_cdrom_catalog(const u8 *addr, u32 length) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + u32 rc = tpm_add_bootdevice(1, 0); + if (rc) + return rc; + + return tpm_ipl(IPL_EL_TORITO_2, addr, length); +} + +void +tpm_s3_resume(void) +{ + u32 rc; + u32 returnCode; + + if (!CONFIG_TCGBIOS) + return; + + if (!has_working_tpm()) + return; + + dprintf(DEBUG_tcg, "TCGBIOS: Resuming with TPM_Startup(ST_STATE)\n"); + + rc = build_and_send_cmd(0, TPM_ORD_Startup, + Startup_ST_STATE, sizeof(Startup_ST_STATE), + NULL, 0, &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "TCGBIOS: ReturnCode from TPM_Startup = 0x%08x\n", + returnCode); + + if (rc || returnCode) + goto err_exit; + + return; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_state.tpm_working = 0; +} diff --git a/qemu/roms/seabios/src/tcgbios.h b/qemu/roms/seabios/src/tcgbios.h new file mode 100644 index 000000000..4b7eaabef --- /dev/null +++ b/qemu/roms/seabios/src/tcgbios.h @@ -0,0 +1,375 @@ +#ifndef TCGBIOS_H +#define TCGBIOS_H + +#include "types.h" + +/* Define for section 12.3 */ +#define TCG_PC_OK 0x0 +#define TCG_PC_TPMERROR 0x1 +#define TCG_PC_LOGOVERFLOW 0x2 +#define TCG_PC_UNSUPPORTED 0x3 + +#define TPM_ALG_SHA 0x4 + +#define TCG_MAGIC 0x41504354L +#define TCG_VERSION_MAJOR 1 +#define TCG_VERSION_MINOR 2 + +#define TPM_OK 0x0 +#define TPM_RET_BASE 0x1 +#define TCG_GENERAL_ERROR (TPM_RET_BASE + 0x0) +#define TCG_TPM_IS_LOCKED (TPM_RET_BASE + 0x1) +#define TCG_NO_RESPONSE (TPM_RET_BASE + 0x2) +#define TCG_INVALID_RESPONSE (TPM_RET_BASE + 0x3) +#define TCG_INVALID_ACCESS_REQUEST (TPM_RET_BASE + 0x4) +#define TCG_FIRMWARE_ERROR (TPM_RET_BASE + 0x5) +#define TCG_INTEGRITY_CHECK_FAILED (TPM_RET_BASE + 0x6) +#define TCG_INVALID_DEVICE_ID (TPM_RET_BASE + 0x7) +#define TCG_INVALID_VENDOR_ID (TPM_RET_BASE + 0x8) +#define TCG_UNABLE_TO_OPEN (TPM_RET_BASE + 0x9) +#define TCG_UNABLE_TO_CLOSE (TPM_RET_BASE + 0xa) +#define TCG_RESPONSE_TIMEOUT (TPM_RET_BASE + 0xb) +#define TCG_INVALID_COM_REQUEST (TPM_RET_BASE + 0xc) +#define TCG_INVALID_ADR_REQUEST (TPM_RET_BASE + 0xd) +#define TCG_WRITE_BYTE_ERROR (TPM_RET_BASE + 0xe) +#define TCG_READ_BYTE_ERROR (TPM_RET_BASE + 0xf) +#define TCG_BLOCK_WRITE_TIMEOUT (TPM_RET_BASE + 0x10) +#define TCG_CHAR_WRITE_TIMEOUT (TPM_RET_BASE + 0x11) +#define TCG_CHAR_READ_TIMEOUT (TPM_RET_BASE + 0x12) +#define TCG_BLOCK_READ_TIMEOUT (TPM_RET_BASE + 0x13) +#define TCG_TRANSFER_ABORT (TPM_RET_BASE + 0x14) +#define TCG_INVALID_DRV_FUNCTION (TPM_RET_BASE + 0x15) +#define TCG_OUTPUT_BUFFER_TOO_SHORT (TPM_RET_BASE + 0x16) +#define TCG_FATAL_COM_ERROR (TPM_RET_BASE + 0x17) +#define TCG_INVALID_INPUT_PARA (TPM_RET_BASE + 0x18) +#define TCG_TCG_COMMAND_ERROR (TPM_RET_BASE + 0x19) +#define TCG_INTERFACE_SHUTDOWN (TPM_RET_BASE + 0x20) +//define TCG_PC_UNSUPPORTED (TPM_RET_BASE + 0x21) +#define TCG_PC_TPM_NOT_PRESENT (TPM_RET_BASE + 0x22) +#define TCG_PC_TPM_DEACTIVATED (TPM_RET_BASE + 0x23) + + +#define TPM_ORD_SelfTestFull 0x00000050 +#define TPM_ORD_ForceClear 0x0000005d +#define TPM_ORD_GetCapability 0x00000065 +#define TPM_ORD_PhysicalEnable 0x0000006f +#define TPM_ORD_PhysicalDisable 0x00000070 +#define TPM_ORD_SetOwnerInstall 0x00000071 +#define TPM_ORD_PhysicalSetDeactivated 0x00000072 +#define TPM_ORD_Startup 0x00000099 +#define TPM_ORD_PhysicalPresence 0x4000000a +#define TPM_ORD_Extend 0x00000014 +#define TPM_ORD_SHA1Start 0x000000a0 +#define TPM_ORD_SHA1Update 0x000000a1 +#define TPM_ORD_SHA1Complete 0x000000a2 +#define TSC_ORD_ResetEstablishmentBit 0x4000000b + + +#define TPM_ST_CLEAR 0x1 +#define TPM_ST_STATE 0x2 +#define TPM_ST_DEACTIVATED 0x3 + + +/* TPM command error codes */ +#define TPM_INVALID_POSTINIT 0x26 +#define TPM_BAD_LOCALITY 0x3d + +/* TPM command tags */ +#define TPM_TAG_RQU_CMD 0x00c1 + +/* interrupt identifiers (al register) */ +enum irq_ids { + TCG_StatusCheck = 0, + TCG_HashLogExtendEvent = 1, + TCG_PassThroughToTPM = 2, + TCG_ShutdownPreBootInterface = 3, + TCG_HashLogEvent = 4, + TCG_HashAll = 5, + TCG_TSS = 6, + TCG_CompactHashLogExtendEvent = 7, +}; + +/* event types: 10.4.1 / table 11 */ +#define EV_POST_CODE 1 +#define EV_SEPARATOR 4 +#define EV_ACTION 5 +#define EV_EVENT_TAG 6 +#define EV_COMPACT_HASH 12 +#define EV_IPL 13 +#define EV_IPL_PARTITION_DATA 14 + + +#define STATUS_FLAG_SHUTDOWN (1 << 0) + +#define SHA1_BUFSIZE 20 + + +struct iovec +{ + size_t length; + void *data; +}; + + +/* Input and Output blocks for the TCG BIOS commands */ + +struct hleei_short +{ + u16 ipblength; + u16 reserved; + const void *hashdataptr; + u32 hashdatalen; + u32 pcrindex; + const void *logdataptr; + u32 logdatalen; +} PACKED; + + +struct hleei_long +{ + u16 ipblength; + u16 reserved; + void *hashdataptr; + u32 hashdatalen; + u32 pcrindex; + u32 reserved2; + void *logdataptr; + u32 logdatalen; +} PACKED; + + +struct hleeo +{ + u16 opblength; + u16 reserved; + u32 eventnumber; + u8 digest[SHA1_BUFSIZE]; +} PACKED; + + +struct pttti +{ + u16 ipblength; + u16 reserved; + u16 opblength; + u16 reserved2; + u8 tpmopin[0]; +} PACKED; + + +struct pttto +{ + u16 opblength; + u16 reserved; + u8 tpmopout[0]; +}; + + +struct hlei +{ + u16 ipblength; + u16 reserved; + const void *hashdataptr; + u32 hashdatalen; + u32 pcrindex; + u32 logeventtype; + const void *logdataptr; + u32 logdatalen; +} PACKED; + + +struct hleo +{ + u16 opblength; + u16 reserved; + u32 eventnumber; +} PACKED; + + +struct hai +{ + u16 ipblength; + u16 reserved; + const void *hashdataptr; + u32 hashdatalen; + u32 algorithmid; +} PACKED; + + +struct ti +{ + u16 ipblength; + u16 reserved; + u16 opblength; + u16 reserved2; + u8 tssoperandin[0]; +} PACKED; + + +struct to +{ + u16 opblength; + u16 reserved; + u8 tssoperandout[0]; +} PACKED; + + +struct pcpes +{ + u32 pcrindex; + u32 eventtype; + u8 digest[SHA1_BUFSIZE]; + u32 eventdatasize; + u32 event; +} PACKED; + +struct pcctes +{ + u32 eventid; + u32 eventdatasize; + u8 digest[SHA1_BUFSIZE]; +} PACKED; + +struct pcctes_romex +{ + u32 eventid; + u32 eventdatasize; + u16 reserved; + u16 pfa; + u8 digest[SHA1_BUFSIZE]; +} PACKED; + + +#define TPM_REQ_HEADER \ + u16 tag; \ + u32 totlen; \ + u32 ordinal; + +#define TPM_REQ_HEADER_SIZE (sizeof(u16) + sizeof(u32) + sizeof(u32)) + +#define TPM_RSP_HEADER \ + u16 tag; \ + u32 totlen; \ + u32 errcode; + +#define TPM_RSP_HEADER_SIZE (sizeof(u16) + sizeof(u32) + sizeof(u32)) + +struct tpm_req_header { + TPM_REQ_HEADER; +} PACKED; + + +struct tpm_rsp_header { + TPM_RSP_HEADER; +} PACKED; + + +struct tpm_req_extend { + TPM_REQ_HEADER + u32 pcrindex; + u8 digest[SHA1_BUFSIZE]; +} PACKED; + + +struct tpm_rsp_extend { + TPM_RSP_HEADER + u8 digest[SHA1_BUFSIZE]; +} PACKED; + + +struct tpm_req_getcap_perm_flags { + TPM_REQ_HEADER + u32 capArea; + u32 subCapSize; + u32 subCap; +} PACKED; + + +struct tpm_permanent_flags { + u16 tag; + u8 flags[20]; +} PACKED; + + +enum permFlagsIndex { + PERM_FLAG_IDX_DISABLE = 0, + PERM_FLAG_IDX_OWNERSHIP, + PERM_FLAG_IDX_DEACTIVATED, + PERM_FLAG_IDX_READPUBEK, + PERM_FLAG_IDX_DISABLEOWNERCLEAR, + PERM_FLAG_IDX_ALLOW_MAINTENANCE, + PERM_FLAG_IDX_PHYSICAL_PRESENCE_LIFETIME_LOCK, + PERM_FLAG_IDX_PHYSICAL_PRESENCE_HW_ENABLE, +}; + + +struct tpm_res_getcap_perm_flags { + TPM_RSP_HEADER + u32 size; + struct tpm_permanent_flags perm_flags; +} PACKED; + + +struct tpm_res_getcap_ownerauth { + TPM_RSP_HEADER + u32 size; + u8 flag; +} PACKED; + + +struct tpm_res_getcap_timeouts { + TPM_RSP_HEADER + u32 size; + u32 timeouts[4]; +} PACKED; + + +struct tpm_res_getcap_durations { + TPM_RSP_HEADER + u32 size; + u32 durations[3]; +} PACKED; + + +struct tpm_res_sha1start { + TPM_RSP_HEADER + u32 max_num_bytes; +} PACKED; + + +struct tpm_res_sha1complete { + TPM_RSP_HEADER + u8 hash[20]; +} PACKED; + +struct pttti_extend { + struct pttti pttti; + struct tpm_req_extend req; +} PACKED; + + +struct pttto_extend { + struct pttto pttto; + struct tpm_rsp_extend rsp; +} PACKED; + + +enum ipltype { + IPL_BCV = 0, + IPL_EL_TORITO_1, + IPL_EL_TORITO_2 +}; + + +struct bregs; +void tpm_interrupt_handler32(struct bregs *regs); + +void tpm_setup(void); +void tpm_prepboot(void); +void tpm_s3_resume(void); +u32 tpm_add_bcv(u32 bootdrv, const u8 *addr, u32 length); +u32 tpm_add_cdrom(u32 bootdrv, const u8 *addr, u32 length); +u32 tpm_add_cdrom_catalog(const u8 *addr, u32 length); +u32 tpm_option_rom(const void *addr, u32 len); + +#endif /* TCGBIOS_H */ diff --git a/qemu/roms/seabios/src/types.h b/qemu/roms/seabios/src/types.h index 097372cdb..19d9f6c14 100644 --- a/qemu/roms/seabios/src/types.h +++ b/qemu/roms/seabios/src/types.h @@ -70,7 +70,7 @@ extern void __force_link_error__only_in_16bit(void) __noreturn; # define VARFSEG __section(".discard.varfseg." UNIQSEC) __VISIBLE __weak // Designate a variable at a specific address in the f-segment. # define VARFSEGFIXED(addr) __section(".discard.varfixed." UNIQSEC) __VISIBLE __weak -// Verify a variable is only accessable via 32bit "init" functions +// Verify a variable is only accessible via 32bit "init" functions # define VARVERIFY32INIT __section(".discard.varinit." UNIQSEC) // Designate top-level assembler as 16bit only. # define ASM16(code) __ASM(code) diff --git a/qemu/roms/seabios/src/util.h b/qemu/roms/seabios/src/util.h index 09bb8a9f3..cba3359d5 100644 --- a/qemu/roms/seabios/src/util.h +++ b/qemu/roms/seabios/src/util.h @@ -43,17 +43,17 @@ void enable_bootsplash(void); void disable_bootsplash(void); // cdrom.c -extern u8 CDRom_locks[]; extern struct eltorito_s CDEmu; extern struct drive_s *cdemu_drive_gf; struct disk_op_s; -int process_cdemu_op(struct disk_op_s *op); +int cdemu_process_op(struct disk_op_s *op); void cdrom_prepboot(void); int cdrom_boot(struct drive_s *drive_g); // clock.c void clock_setup(void); void handle_1583(struct bregs *regs); +void clock_poll_irq(void); u32 irqtimer_calc_ticks(u32 count); u32 irqtimer_calc(u32 msecs); int irqtimer_check(u32 end); @@ -75,6 +75,7 @@ u32 find_resume_vector(void); void acpi_reboot(void); void find_acpi_features(void); extern struct smbios_entry_point *SMBiosAddr; +struct smbios_entry_point *get_smbios_entry_point(); void copy_smbios(void *pos); void display_uuid(void); void copy_table(void *pos); @@ -104,6 +105,9 @@ void mptable_setup(void); // fw/mtrr.c void mtrr_setup(void); +// fw/multiboot.c +void multiboot_init(void); + // fw/pciinit.c extern const u8 pci_irqs[4]; void pci_setup(void); @@ -139,15 +143,15 @@ extern struct floppy_ext_dbt_s diskette_param_table2; void floppy_setup(void); struct drive_s *init_floppy(int floppyid, int ftype); int find_floppy_type(u32 size); -int process_floppy_op(struct disk_op_s *op); +int floppy_process_op(struct disk_op_s *op); void floppy_tick(void); // hw/ramdisk.c void ramdisk_setup(void); -int process_ramdisk_op(struct disk_op_s *op); +int ramdisk_process_op(struct disk_op_s *op); // hw/sdcard.c -int process_sdcard_op(struct disk_op_s *op); +int sdcard_process_op(struct disk_op_s *op); void sdcard_setup(void); // hw/timer.c @@ -232,6 +236,6 @@ void vgahook_setup(struct pci_device *pci); // version (auto generated file out/version.c) -extern const char VERSION[]; +extern const char VERSION[], BUILDINFO[]; #endif // util.h diff --git a/qemu/roms/seabios/src/version.c b/qemu/roms/seabios/src/version.c new file mode 100644 index 000000000..a8a58cf09 --- /dev/null +++ b/qemu/roms/seabios/src/version.c @@ -0,0 +1,5 @@ +// Place build generated version into a C variable +#include "autoversion.h" + +char VERSION[] = BUILD_VERSION; +char BUILDINFO[] = BUILD_TOOLS; diff --git a/qemu/roms/seabios/src/vgahooks.c b/qemu/roms/seabios/src/vgahooks.c index 6a4acfeaf..48efb086c 100644 --- a/qemu/roms/seabios/src/vgahooks.c +++ b/qemu/roms/seabios/src/vgahooks.c @@ -124,7 +124,7 @@ getAMDRamSpeed(void) /* int 0x15 - 5f18 - ECX = unknown/dont care + ECX = unknown/don't care EBX[3..0] Frame Buffer Size 2^N MiB EBX[7..4] Memory speed: 0: SDR 66Mhz diff --git a/qemu/roms/seabios/src/x86.h b/qemu/roms/seabios/src/x86.h index 7798b1c17..53378e9ed 100644 --- a/qemu/roms/seabios/src/x86.h +++ b/qemu/roms/seabios/src/x86.h @@ -75,14 +75,22 @@ static inline void __cpuid(u32 index, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx) : "0" (index)); } -static inline u32 getcr0(void) { +static inline u32 cr0_read(void) { u32 cr0; asm("movl %%cr0, %0" : "=r"(cr0)); return cr0; } -static inline void setcr0(u32 cr0) { +static inline void cr0_write(u32 cr0) { asm("movl %0, %%cr0" : : "r"(cr0)); } +static inline void cr0_mask(u32 off, u32 on) { + cr0_write((cr0_read() & ~off) | on); +} +static inline u16 cr0_vm86_read(void) { + u16 cr0; + asm("smsww %0" : "=r"(cr0)); + return cr0; +} static inline u64 rdmsr(u32 index) { @@ -124,6 +132,13 @@ static inline u32 getesp(void) { return esp; } +static inline u32 rol(u32 val, u16 rol) { + u32 res; + asm volatile("roll %%cl, %%eax" + : "=a" (res) : "a" (val), "c" (rol)); + return res; +} + static inline void outb(u8 value, u16 port) { __asm__ __volatile__("outb %b0, %w1" : : "a"(value), "Nd"(port)); } @@ -175,6 +190,14 @@ static inline void outsl(u16 port, u32 *data, u32 count) { : "+c"(count), "+S"(data) : "d"(port) : "memory"); } +/* Compiler barrier is enough as an x86 CPU does not reorder reads or writes */ +static inline void smp_rmb(void) { + barrier(); +} +static inline void smp_wmb(void) { + barrier(); +} + static inline void writel(void *addr, u32 val) { barrier(); *(volatile u32 *)addr = val; diff --git a/qemu/roms/seabios/vgasrc/Kconfig b/qemu/roms/seabios/vgasrc/Kconfig index 91d590ae2..f5098a4bd 100644 --- a/qemu/roms/seabios/vgasrc/Kconfig +++ b/qemu/roms/seabios/vgasrc/Kconfig @@ -57,6 +57,25 @@ menu "VGA ROM" endchoice + choice + depends on VGA_BOCHS + prompt "bochs vga variant" + default VGA_BOCHS_STDVGA + + config VGA_BOCHS_STDVGA + bool "qemu stdvga / bochs svga" + + config VGA_BOCHS_VMWARE + bool "qemu vmware svga" + + config VGA_BOCHS_QXL + bool "qemu qxl vga" + + config VGA_BOCHS_VIRTIO + bool "qemu virtio vga" + + endchoice + choice depends on VGA_GEODEGX2 || VGA_GEODELX prompt "Output Mode" @@ -141,7 +160,10 @@ menu "VGA ROM" hex prompt "PCI Vendor ID" if OVERRIDE_PCI_ID default 0x1013 if VGA_CIRRUS - default 0x1234 if VGA_BOCHS + default 0x1234 if VGA_BOCHS_STDVGA + default 0x15ad if VGA_BOCHS_VMWARE + default 0x1b36 if VGA_BOCHS_QXL + default 0x1af4 if VGA_BOCHS_VIRTIO default 0x100b if VGA_GEODEGX2 default 0x1022 if VGA_GEODELX default 0x0000 @@ -153,7 +175,10 @@ menu "VGA ROM" hex prompt "PCI Vendor ID" if OVERRIDE_PCI_ID default 0x00b8 if VGA_CIRRUS - default 0x1111 if VGA_BOCHS + default 0x1111 if VGA_BOCHS_STDVGA + default 0x0405 if VGA_BOCHS_VMWARE + default 0x0100 if VGA_BOCHS_QXL + default 0x1050 if VGA_BOCHS_VIRTIO default 0x0030 if VGA_GEODEGX2 default 0x2081 if VGA_GEODELX default 0x0000 diff --git a/qemu/roms/seabios/vgasrc/geodevga.h b/qemu/roms/seabios/vgasrc/geodevga.h index 61d78084d..c99f54bb1 100644 --- a/qemu/roms/seabios/vgasrc/geodevga.h +++ b/qemu/roms/seabios/vgasrc/geodevga.h @@ -2,7 +2,7 @@ // // Copyright (C) 2009 Chris Kindt // -// Writen for Google Summer of Code 2009 for the coreboot project +// Written for Google Summer of Code 2009 for the coreboot project // // This file may be distributed under the terms of the GNU LGPLv3 license. diff --git a/qemu/roms/seabios/vgasrc/vgabios.c b/qemu/roms/seabios/vgasrc/vgabios.c index 4aa50e1c1..f07e85bd3 100644 --- a/qemu/roms/seabios/vgasrc/vgabios.c +++ b/qemu/roms/seabios/vgasrc/vgabios.c @@ -304,6 +304,12 @@ vga_set_mode(int mode, int flags) SET_BDA(video_mode, 0xff); SET_BDA_EXT(vbe_mode, mode | (flags & MF_VBEFLAGS)); SET_BDA_EXT(vgamode_offset, (u32)vmode_g); + if (CONFIG_VGA_ALLOCATE_EXTRA_STACK) + // Disable extra stack if it appears a modern OS is in use. + // This works around bugs in some versions of Windows (Vista + // and possibly later) when the stack is in the e-segment. + MASK_BDA_EXT(flags, BF_EXTRA_STACK + , (flags & MF_LEGACY) ? BF_EXTRA_STACK : 0); if (memmodel == MM_TEXT) { SET_BDA(video_cols, width); SET_BDA(video_rows, height-1); diff --git a/qemu/roms/seabios/vgasrc/vgabios.h b/qemu/roms/seabios/vgasrc/vgabios.h index fd796f2e6..831f69407 100644 --- a/qemu/roms/seabios/vgasrc/vgabios.h +++ b/qemu/roms/seabios/vgasrc/vgabios.h @@ -62,7 +62,8 @@ struct gfx_op { #define GO_MEMSET 3 #define GO_MEMMOVE 4 -// Custom internal storage in BDA +// Custom internal storage in BDA (don't change here without also +// updating vgaentry.S) #define VGA_CUSTOM_BDA 0xb9 struct vga_bda_s { @@ -74,6 +75,7 @@ struct vga_bda_s { #define BF_PM_MASK 0x0f #define BF_EMULATE_TEXT 0x10 #define BF_SWCURSOR 0x20 +#define BF_EXTRA_STACK 0x40 #define GET_BDA_EXT(var) \ GET_FARVAR(SEG_BDA, ((struct vga_bda_s *)VGA_CUSTOM_BDA)->var) diff --git a/qemu/roms/seabios/vgasrc/vgaentry.S b/qemu/roms/seabios/vgasrc/vgaentry.S index d9ebdb93c..53be2b38c 100644 --- a/qemu/roms/seabios/vgasrc/vgaentry.S +++ b/qemu/roms/seabios/vgasrc/vgaentry.S @@ -86,14 +86,23 @@ entry_10: ENTRY_ARG_VGA handle_10 iretw +#define VGA_CUSTOM_BDA_FLAGS 0xb9 +#define BF_EXTRA_STACK 0x40 + // Entry point using extra stack DECLFUNC entry_10_extrastack entry_10_extrastack: cli cld - pushw %ds // Set %ds:%eax to space on ExtraStack + pushw %ds pushl %eax - movw %cs:ExtraStackSeg, %ds + + movw $SEG_BDA, %ax // Check if extra stack is enabled + movw %ax, %ds + testb $BF_EXTRA_STACK, VGA_CUSTOM_BDA_FLAGS + jz 1f + + movw %cs:ExtraStackSeg, %ds // Set %ds:%eax to space on ExtraStack movl $(CONFIG_VGA_EXTRA_STACK_SIZE-PUSHBREGS_size-16), %eax SAVEBREGS_POP_DSEAX // Save registers on extra stack movl %esp, PUSHBREGS_size+8(%eax) @@ -116,6 +125,11 @@ entry_10_extrastack: RESTOREBREGS_DSEAX iretw +1: // Use regular entry point if the extra stack is disabled + popl %eax + popw %ds + jmp entry_10 + // Timer irq handling DECLFUNC entry_timer_hook entry_timer_hook: diff --git a/qemu/roms/seabios/vgasrc/vgafb.c b/qemu/roms/seabios/vgasrc/vgafb.c index 1a94fcf70..5d1ecc936 100644 --- a/qemu/roms/seabios/vgasrc/vgafb.c +++ b/qemu/roms/seabios/vgasrc/vgafb.c @@ -273,28 +273,22 @@ gfx_direct(struct gfx_op *op) int bypp = DIV_ROUND_UP(depth, 8); void *dest_far = (fb + op->displaystart + op->y * op->linelength + op->x * bypp); + u8 data[64]; + int i; switch (op->op) { default: - case GO_READ8: { - u8 data[64]; + case GO_READ8: memcpy_high(MAKE_FLATPTR(GET_SEG(SS), data), dest_far, bypp * 8); - int i; for (i=0; i<8; i++) op->pixels[i] = reverse_color(depth, *(u32*)&data[i*bypp]); break; - } - case GO_WRITE8: { - u8 data[64]; - int i; + case GO_WRITE8: for (i=0; i<8; i++) *(u32*)&data[i*bypp] = get_color(depth, op->pixels[i]); memcpy_high(dest_far, MAKE_FLATPTR(GET_SEG(SS), data), bypp * 8); break; - } - case GO_MEMSET: { + case GO_MEMSET: ; u32 color = get_color(depth, op->pixels[0]); - u8 data[64]; - int i; for (i=0; i<8; i++) *(u32*)&data[i*bypp] = color; memcpy_high(dest_far, MAKE_FLATPTR(GET_SEG(SS), data), bypp * 8); @@ -303,7 +297,6 @@ gfx_direct(struct gfx_op *op) memcpy_high(dest_far + op->linelength * i , dest_far, op->xlen * bypp); break; - } case GO_MEMMOVE: ; void *src_far = (fb + op->displaystart + op->srcy * op->linelength + op->x * bypp); diff --git a/qemu/roms/seabios/vgasrc/vgainit.c b/qemu/roms/seabios/vgasrc/vgainit.c index 8d1226182..40997dbbd 100644 --- a/qemu/roms/seabios/vgasrc/vgainit.c +++ b/qemu/roms/seabios/vgasrc/vgainit.c @@ -150,6 +150,7 @@ vga_post(struct bregs *regs) { serial_debug_preinit(); dprintf(1, "Start SeaVGABIOS (version %s)\n", VERSION); + dprintf(1, "VGABUILD: %s\n", BUILDINFO); debug_enter(regs, DEBUG_VGA_POST); if (CONFIG_VGA_PCI && !GET_GLOBAL(HaveRunInit)) { diff --git a/qemu/roms/seabios/vgasrc/vgaversion.c b/qemu/roms/seabios/vgasrc/vgaversion.c new file mode 100644 index 000000000..1ef5ddb79 --- /dev/null +++ b/qemu/roms/seabios/vgasrc/vgaversion.c @@ -0,0 +1,6 @@ +// Place build generated version into a C variable +#include "autovgaversion.h" +#include "types.h" + +char VERSION[] VAR16 = BUILD_VERSION; +char BUILDINFO[] VAR16 = BUILD_TOOLS; diff --git a/qemu/rules.mak b/qemu/rules.mak index aec27f81d..d1ff31125 100644 --- a/qemu/rules.mak +++ b/qemu/rules.mak @@ -17,7 +17,7 @@ MAKEFLAGS += -rR QEMU_CXXFLAGS = -D__STDC_LIMIT_MACROS $(filter-out -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Wold-style-declaration -Wold-style-definition -Wredundant-decls, $(QEMU_CFLAGS)) # Flags for dependency generation -QEMU_DGFLAGS += -MMD -MP -MT $@ -MF $(*D)/$(*F).d +QEMU_DGFLAGS += -MMD -MP -MT $@ -MF $(@D)/$(*F).d # Same as -I$(SRC_PATH) -I., but for the nested source/object directories QEMU_INCLUDES += -I$( -# -# 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 . - -# Process mixed ASL/AML listing (.lst file) produced by iasl -l -# Locate and execute ACPI_EXTRACT directives, output offset info -# -# Documentation of ACPI_EXTRACT_* directive tags: -# -# These directive tags output offset information from AML for BIOS runtime -# table generation. -# Each directive is of the form: -# ACPI_EXTRACT_ (...) -# and causes the extractor to create an array -# named with offset, in the generated AML, -# of an object of a given type in the following . -# -# A directive must fit on a single code line. -# -# Object type in AML is verified, a mismatch causes a build failure. -# -# Directives and operators currently supported are: -# ACPI_EXTRACT_NAME_DWORD_CONST - extract a Dword Const object from Name() -# ACPI_EXTRACT_NAME_WORD_CONST - extract a Word Const object from Name() -# ACPI_EXTRACT_NAME_BYTE_CONST - extract a Byte Const object from Name() -# ACPI_EXTRACT_METHOD_STRING - extract a NameString from Method() -# ACPI_EXTRACT_NAME_STRING - extract a NameString from Name() -# ACPI_EXTRACT_PROCESSOR_START - start of Processor() block -# ACPI_EXTRACT_PROCESSOR_STRING - extract a NameString from Processor() -# ACPI_EXTRACT_PROCESSOR_END - offset at last byte of Processor() + 1 -# ACPI_EXTRACT_PKG_START - start of Package block -# -# ACPI_EXTRACT_ALL_CODE - create an array storing the generated AML bytecode -# -# ACPI_EXTRACT is not allowed anywhere else in code, except in comments. - -import re; -import sys; -import fileinput; - -aml = [] -asl = [] -output = {} -debug = "" - -class asl_line: - line = None - lineno = None - aml_offset = None - -def die(diag): - sys.stderr.write("Error: %s; %s\n" % (diag, debug)) - sys.exit(1) - -#Store an ASL command, matching AML offset, and input line (for debugging) -def add_asl(lineno, line): - l = asl_line() - l.line = line - l.lineno = lineno - l.aml_offset = len(aml) - asl.append(l) - -#Store an AML byte sequence -#Verify that offset output by iasl matches # of bytes so far -def add_aml(offset, line): - o = int(offset, 16); - # Sanity check: offset must match size of code so far - if (o != len(aml)): - die("Offset 0x%x != 0x%x" % (o, len(aml))) - # Strip any trailing dots and ASCII dump after " - line = re.sub(r'\s*\.*\s*".*$',"", line) - # Strip traling whitespace - line = re.sub(r'\s+$',"", line) - # Strip leading whitespace - line = re.sub(r'^\s+',"", line) - # Split on whitespace - code = re.split(r'\s+', line) - for c in code: - # Require a legal hex number, two digits - if (not(re.search(r'^[0-9A-Fa-f][0-9A-Fa-f]$', c))): - die("Unexpected octet %s" % c); - aml.append(int(c, 16)); - -# Process aml bytecode array, decoding AML -def aml_pkglen_bytes(offset): - # PkgLength can be multibyte. Bits 8-7 give the # of extra bytes. - pkglenbytes = aml[offset] >> 6; - return pkglenbytes + 1 - -def aml_pkglen(offset): - pkgstart = offset - pkglenbytes = aml_pkglen_bytes(offset) - pkglen = aml[offset] & 0x3F - # If multibyte, first nibble only uses bits 0-3 - if ((pkglenbytes > 1) and (pkglen & 0x30)): - die("PkgLen bytes 0x%x but first nibble 0x%x expected 0x0X" % - (pkglen, pkglen)) - offset += 1 - pkglenbytes -= 1 - for i in range(pkglenbytes): - pkglen |= aml[offset + i] << (i * 8 + 4) - if (len(aml) < pkgstart + pkglen): - die("PckgLen 0x%x at offset 0x%x exceeds AML size 0x%x" % - (pkglen, offset, len(aml))) - return pkglen - -# Given method offset, find its NameString offset -def aml_method_string(offset): - #0x14 MethodOp PkgLength NameString MethodFlags TermList - if (aml[offset] != 0x14): - die( "Method offset 0x%x: expected 0x14 actual 0x%x" % - (offset, aml[offset])); - offset += 1; - pkglenbytes = aml_pkglen_bytes(offset) - offset += pkglenbytes; - return offset; - -# Given name offset, find its NameString offset -def aml_name_string(offset): - #0x08 NameOp NameString DataRef - if (aml[offset] != 0x08): - die( "Name offset 0x%x: expected 0x08 actual 0x%x" % - (offset, aml[offset])); - offset += 1 - # Block Name Modifier. Skip it. - if (aml[offset] == 0x5c or aml[offset] == 0x5e): - offset += 1 - return offset; - -# Given data offset, find variable length byte buffer offset -def aml_data_buffer(offset, length): - #0x11 PkgLength BufferSize ByteList - if (length > 63): - die( "Name offset 0x%x: expected a one byte PkgLength (length<=63)" % - (offset)); - expect = [0x11, length+3, 0x0A, length] - if (aml[offset:offset+4] != expect): - die( "Name offset 0x%x: expected %s actual %s" % - (offset, expect, aml[offset:offset+4])) - return offset + len(expect) - -# Given data offset, find dword const offset -def aml_data_dword_const(offset): - #0x08 NameOp NameString DataRef - if (aml[offset] != 0x0C): - die( "Name offset 0x%x: expected 0x0C actual 0x%x" % - (offset, aml[offset])); - return offset + 1; - -# Given data offset, find word const offset -def aml_data_word_const(offset): - #0x08 NameOp NameString DataRef - if (aml[offset] != 0x0B): - die( "Name offset 0x%x: expected 0x0B actual 0x%x" % - (offset, aml[offset])); - return offset + 1; - -# Given data offset, find byte const offset -def aml_data_byte_const(offset): - #0x08 NameOp NameString DataRef - if (aml[offset] != 0x0A): - die( "Name offset 0x%x: expected 0x0A actual 0x%x" % - (offset, aml[offset])); - return offset + 1; - -# Find name'd buffer -def aml_name_buffer(offset, length): - return aml_data_buffer(aml_name_string(offset) + 4, length) - -# Given name offset, find dword const offset -def aml_name_dword_const(offset): - return aml_data_dword_const(aml_name_string(offset) + 4) - -# Given name offset, find word const offset -def aml_name_word_const(offset): - return aml_data_word_const(aml_name_string(offset) + 4) - -# Given name offset, find byte const offset -def aml_name_byte_const(offset): - return aml_data_byte_const(aml_name_string(offset) + 4) - -def aml_device_start(offset): - #0x5B 0x82 DeviceOp PkgLength NameString - if ((aml[offset] != 0x5B) or (aml[offset + 1] != 0x82)): - die( "Name offset 0x%x: expected 0x5B 0x82 actual 0x%x 0x%x" % - (offset, aml[offset], aml[offset + 1])); - return offset - -def aml_device_string(offset): - #0x5B 0x82 DeviceOp PkgLength NameString - start = aml_device_start(offset) - offset += 2 - pkglenbytes = aml_pkglen_bytes(offset) - offset += pkglenbytes - return offset - -def aml_device_end(offset): - start = aml_device_start(offset) - offset += 2 - pkglenbytes = aml_pkglen_bytes(offset) - pkglen = aml_pkglen(offset) - return offset + pkglen - -def aml_processor_start(offset): - #0x5B 0x83 ProcessorOp PkgLength NameString ProcID - if ((aml[offset] != 0x5B) or (aml[offset + 1] != 0x83)): - die( "Name offset 0x%x: expected 0x5B 0x83 actual 0x%x 0x%x" % - (offset, aml[offset], aml[offset + 1])); - return offset - -def aml_processor_string(offset): - #0x5B 0x83 ProcessorOp PkgLength NameString ProcID - start = aml_processor_start(offset) - offset += 2 - pkglenbytes = aml_pkglen_bytes(offset) - offset += pkglenbytes - return offset - -def aml_processor_end(offset): - start = aml_processor_start(offset) - offset += 2 - pkglenbytes = aml_pkglen_bytes(offset) - pkglen = aml_pkglen(offset) - return offset + pkglen - -def aml_package_start(offset): - offset = aml_name_string(offset) + 4 - # 0x12 PkgLength NumElements PackageElementList - if (aml[offset] != 0x12): - die( "Name offset 0x%x: expected 0x12 actual 0x%x" % - (offset, aml[offset])); - offset += 1 - return offset + aml_pkglen_bytes(offset) + 1 - -lineno = 0 -for line in fileinput.input(): - # Strip trailing newline - line = line.rstrip(); - # line number and debug string to output in case of errors - lineno = lineno + 1 - debug = "input line %d: %s" % (lineno, line) - #ASL listing: space, then line#, then ...., then code - pasl = re.compile('^\s+([0-9]+)(:\s\s|\.\.\.\.)\s*') - m = pasl.search(line) - if (m): - add_asl(lineno, pasl.sub("", line)); - # AML listing: offset in hex, then ...., then code - paml = re.compile('^([0-9A-Fa-f]+)(:\s\s|\.\.\.\.)\s*') - m = paml.search(line) - if (m): - add_aml(m.group(1), paml.sub("", line)) - -# Now go over code -# Track AML offset of a previous non-empty ASL command -prev_aml_offset = -1 -for i in range(len(asl)): - debug = "input line %d: %s" % (asl[i].lineno, asl[i].line) - - l = asl[i].line - - # skip if not an extract directive - a = len(re.findall(r'ACPI_EXTRACT', l)) - if (not a): - # If not empty, store AML offset. Will be used for sanity checks - # IASL seems to put {}. at random places in the listing. - # Ignore any non-words for the purpose of this test. - m = re.search(r'\w+', l) - if (m): - prev_aml_offset = asl[i].aml_offset - continue - - if (a > 1): - die("Expected at most one ACPI_EXTRACT per line, actual %d" % a) - - mext = re.search(r''' - ^\s* # leading whitespace - /\*\s* # start C comment - (ACPI_EXTRACT_\w+) # directive: group(1) - \s+ # whitspace separates directive from array name - (\w+) # array name: group(2) - \s*\*/ # end of C comment - \s*$ # trailing whitespace - ''', l, re.VERBOSE) - if (not mext): - die("Stray ACPI_EXTRACT in input") - - # previous command must have produced some AML, - # otherwise we are in a middle of a block - if (prev_aml_offset == asl[i].aml_offset): - die("ACPI_EXTRACT directive in the middle of a block") - - directive = mext.group(1) - array = mext.group(2) - offset = asl[i].aml_offset - - if (directive == "ACPI_EXTRACT_ALL_CODE"): - if array in output: - die("%s directive used more than once" % directive) - output[array] = aml - continue - if (directive == "ACPI_EXTRACT_NAME_BUFFER8"): - offset = aml_name_buffer(offset, 8) - elif (directive == "ACPI_EXTRACT_NAME_BUFFER16"): - offset = aml_name_buffer(offset, 16) - elif (directive == "ACPI_EXTRACT_NAME_DWORD_CONST"): - offset = aml_name_dword_const(offset) - elif (directive == "ACPI_EXTRACT_NAME_WORD_CONST"): - offset = aml_name_word_const(offset) - elif (directive == "ACPI_EXTRACT_NAME_BYTE_CONST"): - offset = aml_name_byte_const(offset) - elif (directive == "ACPI_EXTRACT_NAME_STRING"): - offset = aml_name_string(offset) - elif (directive == "ACPI_EXTRACT_METHOD_STRING"): - offset = aml_method_string(offset) - elif (directive == "ACPI_EXTRACT_DEVICE_START"): - offset = aml_device_start(offset) - elif (directive == "ACPI_EXTRACT_DEVICE_STRING"): - offset = aml_device_string(offset) - elif (directive == "ACPI_EXTRACT_DEVICE_END"): - offset = aml_device_end(offset) - elif (directive == "ACPI_EXTRACT_PROCESSOR_START"): - offset = aml_processor_start(offset) - elif (directive == "ACPI_EXTRACT_PROCESSOR_STRING"): - offset = aml_processor_string(offset) - elif (directive == "ACPI_EXTRACT_PROCESSOR_END"): - offset = aml_processor_end(offset) - elif (directive == "ACPI_EXTRACT_PKG_START"): - offset = aml_package_start(offset) - else: - die("Unsupported directive %s" % directive) - - if array not in output: - output[array] = [] - output[array].append(offset) - -debug = "at end of file" - -def get_value_type(maxvalue): - #Use type large enough to fit the table - if (maxvalue >= 0x10000): - return "int" - elif (maxvalue >= 0x100): - return "short" - else: - return "char" - -# Pretty print output -for array in output.keys(): - otype = get_value_type(max(output[array])) - odata = [] - for value in output[array]: - odata.append("0x%x" % value) - sys.stdout.write("static unsigned %s %s[] = {\n" % (otype, array)) - sys.stdout.write(",\n".join(odata)) - sys.stdout.write('\n};\n'); diff --git a/qemu/scripts/acpi_extract_preprocess.py b/qemu/scripts/acpi_extract_preprocess.py deleted file mode 100755 index 69d10d621..000000000 --- a/qemu/scripts/acpi_extract_preprocess.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/python -# Copyright (C) 2011 Red Hat, Inc., Michael S. Tsirkin -# -# 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 . - -# Read a preprocessed ASL listing and put each ACPI_EXTRACT -# directive in a comment, to make iasl skip it. -# We also put each directive on a new line, the machinery -# in tools/acpi_extract.py requires this. - -import re; -import sys; -import fileinput; - -def die(diag): - sys.stderr.write("Error: %s\n" % (diag)) - sys.exit(1) - -# Note: () around pattern make split return matched string as part of list -psplit = re.compile(r''' ( - \b # At word boundary - ACPI_EXTRACT_\w+ # directive - \s+ # some whitespace - \w+ # array name - )''', re.VERBOSE); - -lineno = 0 -for line in fileinput.input(): - # line number and debug string to output in case of errors - lineno = lineno + 1 - debug = "input line %d: %s" % (lineno, line.rstrip()) - - s = psplit.split(line); - # The way split works, each odd item is the matching ACPI_EXTRACT directive. - # Put each in a comment, and on a line by itself. - for i in range(len(s)): - if (i % 2): - sys.stdout.write("\n/* %s */\n" % s[i]) - else: - sys.stdout.write(s[i]) diff --git a/qemu/scripts/analyze-migration.py b/qemu/scripts/analyze-migration.py index f6894bece..14553876a 100755 --- a/qemu/scripts/analyze-migration.py +++ b/qemu/scripts/analyze-migration.py @@ -252,6 +252,15 @@ class HTABSection(object): def getDict(self): return "" + +class ConfigurationSection(object): + def __init__(self, file): + self.file = file + + def read(self): + name_len = self.file.read32() + name = self.file.readstr(len = name_len) + class VMSDFieldGeneric(object): def __init__(self, desc, file): self.file = file @@ -474,6 +483,7 @@ class MigrationDump(object): QEMU_VM_SECTION_FULL = 0x04 QEMU_VM_SUBSECTION = 0x05 QEMU_VM_VMDESCRIPTION = 0x06 + QEMU_VM_CONFIGURATION = 0x07 QEMU_VM_SECTION_FOOTER= 0x7e def __init__(self, filename): @@ -514,6 +524,9 @@ class MigrationDump(object): section_type = file.read8() if section_type == self.QEMU_VM_EOF: break + elif section_type == self.QEMU_VM_CONFIGURATION: + section = ConfigurationSection(file) + section.read() elif section_type == self.QEMU_VM_SECTION_START or section_type == self.QEMU_VM_SECTION_FULL: section_id = file.read32() name = file.readstr() diff --git a/qemu/scripts/checkpatch.pl b/qemu/scripts/checkpatch.pl index 7f0aae977..c9554ba64 100755 --- a/qemu/scripts/checkpatch.pl +++ b/qemu/scripts/checkpatch.pl @@ -141,44 +141,22 @@ our $Ident = qr{ }x; our $Storage = qr{extern|static|asmlinkage}; our $Sparse = qr{ - __user| - __kernel| - __force| - __iomem| - __must_check| - __init_refok| - __kprobes| - __ref + __force }x; # Notes to $Attribute: -# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check our $Attribute = qr{ const| - __percpu| - __nocast| - __safe| - __bitwise__| - __packed__| - __packed2__| - __naked| - __maybe_unused| - __always_unused| - __noreturn| - __used| - __cold| - __noclone| - __deprecated| - __read_mostly| - __kprobes| - __(?:mem|cpu|dev|)(?:initdata|initconst|init\b)| - ____cacheline_aligned| - ____cacheline_aligned_in_smp| - ____cacheline_internodealigned_in_smp| - __weak + volatile| + QEMU_NORETURN| + QEMU_WARN_UNUSED_RESULT| + QEMU_SENTINEL| + QEMU_ARTIFICIAL| + QEMU_PACKED| + GCC_FMT_ATTR }x; our $Modifier; -our $Inline = qr{inline|__always_inline|noinline}; +our $Inline = qr{inline}; our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; our $Lval = qr{$Ident(?:$Member)*}; @@ -215,14 +193,6 @@ our $typeTypedefs = qr{(?x: | QEMUBH # all uppercase )}; -our $logFunctions = qr{(?x: - printk| - pr_(debug|dbg|vdbg|devel|info|warning|err|notice|alert|crit|emerg|cont)| - (dev|netdev|netif)_(printk|dbg|vdbg|info|warn|err|notice|alert|crit|emerg|WARN)| - WARN| - panic -)}; - our @typeList = ( qr{void}, qr{(?:unsigned\s+)?char}, @@ -242,21 +212,22 @@ our @typeList = ( qr{${Ident}_t}, qr{${Ident}_handler}, qr{${Ident}_handler_fn}, + qr{target_(?:u)?long}, ); + +# This can be modified by sub possible. Since it can be empty, be careful +# about regexes that always match, because they can cause infinite loops. our @modifierList = ( - qr{fastcall}, ); -our $allowed_asm_includes = qr{(?x: - irq| - memory -)}; -# memory.h: ARM has a custom one - sub build_types { - my $mods = "(?x: \n" . join("|\n ", @modifierList) . "\n)"; my $all = "(?x: \n" . join("|\n ", @typeList) . "\n)"; - $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; + if (@modifierList > 0) { + my $mods = "(?x: \n" . join("|\n ", @modifierList) . "\n)"; + $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; + } else { + $Modifier = qr{(?:$Attribute|$Sparse)}; + } $NonptrType = qr{ (?:$Modifier\s+|const\s+)* (?: @@ -277,27 +248,6 @@ build_types(); $chk_signoff = 0 if ($file); -my @dep_includes = (); -my @dep_functions = (); -my $removal = "Documentation/feature-removal-schedule.txt"; -if ($tree && -f "$root/$removal") { - open(my $REMOVE, '<', "$root/$removal") || - die "$P: $removal: open failed - $!\n"; - while (<$REMOVE>) { - if (/^Check:\s+(.*\S)/) { - for my $entry (split(/[, ]+/, $1)) { - if ($entry =~ m@include/(.*)@) { - push(@dep_includes, $1); - - } elsif ($entry !~ m@/@) { - push(@dep_functions, $entry); - } - } - } - } - close($REMOVE); -} - my @rawlines = (); my @lines = (); my $vname; @@ -633,7 +583,7 @@ sub statement_block_size { my ($stmt) = @_; $stmt =~ s/(^|\n)./$1/g; - $stmt =~ s/^\s*{//; + $stmt =~ s/^\s*\{//; $stmt =~ s/}\s*$//; $stmt =~ s/^\s*//; $stmt =~ s/\s*$//; @@ -1061,7 +1011,9 @@ sub possible { case| else| asm|__asm__| - do + do| + \#| + \#\# )(?:\s|$)| ^(?:typedef|struct|enum)\b )}x; @@ -1127,33 +1079,6 @@ sub CHK { } } -sub check_absolute_file { - my ($absolute, $herecurr) = @_; - my $file = $absolute; - - ##print "absolute<$absolute>\n"; - - # See if any suffix of this path is a path within the tree. - while ($file =~ s@^[^/]*/@@) { - if (-f "$root/$file") { - ##print "file<$file>\n"; - last; - } - } - if (! -f _) { - return 0; - } - - # It is, so see if the prefix is acceptable. - my $prefix = $absolute; - substr($prefix, -length($file)) = ''; - - ##print "prefix<$prefix>\n"; - if ($prefix ne ".../") { - WARN("use relative pathname instead of absolute in changelog text\n" . $herecurr); - } -} - sub process { my $filename = shift; @@ -1196,10 +1121,6 @@ sub process { my %suppress_export; # Pre-scan the patch sanitizing the lines. - # Pre-scan the patch looking for any __setup documentation. - # - my @setup_docs = (); - my $setup_docs = 0; sanitise_line_reset(); my $line; @@ -1207,13 +1128,6 @@ sub process { $linenr++; $line = $rawline; - if ($rawline=~/^\+\+\+\s+(\S+)/) { - $setup_docs = 0; - if ($1 =~ m@Documentation/kernel-parameters.txt$@) { - $setup_docs = 1; - } - #next; - } if ($rawline=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { $realline=$1-1; if (defined $2) { @@ -1272,10 +1186,6 @@ sub process { #print "==>$rawline\n"; #print "-->$line\n"; - - if ($setup_docs && $line =~ /^\+/) { - push(@setup_docs, $line); - } } $prefix = ''; @@ -1350,9 +1260,6 @@ sub process { WARN("patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n"); } - if ($realfile =~ m@^include/asm/@) { - ERROR("do not modify files in include/asm, change architecture specific files in include/asm-\n" . "$here$rawline\n"); - } next; } @@ -1367,7 +1274,7 @@ sub process { # Check for incorrect file permissions if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { my $permhere = $here . "FILE: $realfile\n"; - if ($realfile =~ /(Makefile|Kconfig|\.c|\.cpp|\.h|\.S|\.tmpl)$/) { + if ($realfile =~ /(\bMakefile(?:\.objs)?|\.c|\.cc|\.cpp|\.h|\.mak|\.[sS])$/) { ERROR("do not set execute permissions for source files\n" . $permhere); } } @@ -1392,20 +1299,6 @@ sub process { $herecurr) if (!$emitted_corrupt++); } -# Check for absolute kernel paths. - if ($tree) { - while ($line =~ m{(?:^|\s)(/\S*)}g) { - my $file = $1; - - if ($file =~ m{^(.*?)(?::\d+)+:?$} && - check_absolute_file($1, $herecurr)) { - # - } else { - check_absolute_file($file, $herecurr); - } - } - } - # UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php if (($realfile =~ /^$/ || $line =~ /^\+/) && $rawline !~ m/^$UTF8*$/) { @@ -1432,45 +1325,12 @@ sub process { $rpt_cleaners = 1; } -# check for Kconfig help text having a real description -# Only applies when adding the entry originally, after that we do not have -# sufficient context to determine whether it is indeed long enough. - if ($realfile =~ /Kconfig/ && - $line =~ /\+\s*(?:---)?help(?:---)?$/) { - my $length = 0; - my $cnt = $realcnt; - my $ln = $linenr + 1; - my $f; - my $is_end = 0; - while ($cnt > 0 && defined $lines[$ln - 1]) { - $f = $lines[$ln - 1]; - $cnt-- if ($lines[$ln - 1] !~ /^-/); - $is_end = $lines[$ln - 1] =~ /^\+/; - $ln++; - - next if ($f =~ /^-/); - $f =~ s/^.//; - $f =~ s/#.*//; - $f =~ s/^\s+//; - next if ($f =~ /^$/); - if ($f =~ /^\s*config\s/) { - $is_end = 1; - last; - } - $length++; - } - WARN("please write a paragraph that describes the config symbol fully\n" . $herecurr) if ($is_end && $length < 4); - #print "is_end<$is_end> length<$length>\n"; - } - # check we are in a valid source file if not then ignore this hunk next if ($realfile !~ /\.(h|c|cpp|s|S|pl|sh)$/); #80 column limit - if ($line =~ /^\+/ && $prevrawline !~ /\/\*\*/ && - $rawline !~ /^.\s*\*\s*\@$Ident\s/ && - !($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(KERN_\S+\s*|[^"]*))?"[X\t]*"\s*(?:,|\)\s*;)\s*$/ || - $line =~ /^\+\s*"[^"]*"\s*(?:\s*|,|\)\s*;)\s*$/) && + if ($line =~ /^\+/ && + !($line =~ /^\+\s*"[^"]*"\s*(?:\s*|,|\)\s*;)\s*$/) && $length > 80) { WARN("line over 80 characters\n" . $herecurr); @@ -1486,18 +1346,6 @@ sub process { WARN("adding a line without newline at end of file\n" . $herecurr); } -# Blackfin: use hi/lo macros - if ($realfile =~ m@arch/blackfin/.*\.S$@) { - if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("use the LO() macro, not (... & 0xFFFF)\n" . $herevet); - } - if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("use the HI() macro, not (... >> 16)\n" . $herevet); - } - } - # check we are in a valid source file C or perl if not then ignore this hunk next if ($realfile !~ /\.(h|c|cpp|pl)$/); @@ -1516,16 +1364,6 @@ sub process { WARN("CVS style keyword markers, these will _not_ be updated\n". $herecurr); } -# Blackfin: don't use __builtin_bfin_[cs]sync - if ($line =~ /__builtin_bfin_csync/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("use the CSYNC() macro in asm/blackfin.h\n" . $herevet); - } - if ($line =~ /__builtin_bfin_ssync/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("use the SSYNC() macro in asm/blackfin.h\n" . $herevet); - } - # Check for potential 'bare' types my ($stat, $cond, $line_nr_next, $remain_next, $off_next, $realline_next); @@ -1644,7 +1482,7 @@ sub process { # 79 or 80 characters, it is no longer possible to add a space and an # opening brace there) if ($#ctx == 0 && $ctx !~ /{\s*/ && - defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/ && + defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^\+\s*\{/ && defined($lines[$ctx_ln - 2]) && length($lines[$ctx_ln - 2]) < 80) { ERROR("that open brace { should be on the previous line\n" . "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); @@ -1684,7 +1522,7 @@ sub process { my $continuation = 0; my $check = 0; $s =~ s/^.*\bdo\b//; - $s =~ s/^\s*{//; + $s =~ s/^\s*\{//; if ($s =~ s/^\s*\\//) { $continuation = 1; } @@ -1783,7 +1621,7 @@ sub process { } # check for initialisation to aggregates open brace on the next line - if ($line =~ /^.\s*{/ && + if ($line =~ /^.\s*\{/ && $prevline =~ /(?:^|[^=])=\s*$/) { ERROR("that open brace { should be on the previous line\n" . $hereprev); } @@ -1809,50 +1647,6 @@ sub process { $line =~ s@//.*@@; $opline =~ s@//.*@@; -# EXPORT_SYMBOL should immediately follow the thing it is exporting, consider -# the whole statement. -#print "APW <$lines[$realline_next - 1]>\n"; - if (defined $realline_next && - exists $lines[$realline_next - 1] && - !defined $suppress_export{$realline_next} && - ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ || - $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { - # Handle definitions which produce identifiers with - # a prefix: - # XXX(foo); - # EXPORT_SYMBOL(something_foo); - my $name = $1; - if ($stat =~ /^.([A-Z_]+)\s*\(\s*($Ident)/ && - $name =~ /^${Ident}_$2/) { -#print "FOO C name<$name>\n"; - $suppress_export{$realline_next} = 1; - - } elsif ($stat !~ /(?: - \n.}\s*$| - ^.DEFINE_$Ident\(\Q$name\E\)| - ^.DECLARE_$Ident\(\Q$name\E\)| - ^.LIST_HEAD\(\Q$name\E\)| - ^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(| - \b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\() - )/x) { -#print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n"; - $suppress_export{$realline_next} = 2; - } else { - $suppress_export{$realline_next} = 1; - } - } - if (!defined $suppress_export{$linenr} && - $prevline =~ /^.\s*$/ && - ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ || - $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { -#print "FOO B <$lines[$linenr - 1]>\n"; - $suppress_export{$linenr} = 2; - } - if (defined $suppress_export{$linenr} && - $suppress_export{$linenr} == 2) { - WARN("EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr); - } - # check for global initialisers. if ($line =~ /^.$Type\s*$Ident\s*(?:\s+$Modifier)*\s*=\s*(0|NULL|false)\s*;/) { ERROR("do not initialise globals to 0 or NULL\n" . @@ -1900,67 +1694,37 @@ sub process { } } -# # no BUG() or BUG_ON() -# if ($line =~ /\b(BUG|BUG_ON)\b/) { -# print "Try to use WARN_ON & Recovery code rather than BUG() or BUG_ON()\n"; -# print "$herecurr"; -# $clean = 0; -# } - - if ($line =~ /\bLINUX_VERSION_CODE\b/) { - WARN("LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr); - } - -# printk should use KERN_* levels. Note that follow on printk's on the -# same line do not need a level, so we use the current block context -# to try and find and validate the current printk. In summary the current -# printk includes all preceding printk's which have no newline on the end. -# we assume the first bad printk is the one to report. - if ($line =~ /\bprintk\((?!KERN_)\s*"/) { - my $ok = 0; - for (my $ln = $linenr - 1; $ln >= $first_line; $ln--) { - #print "CHECK<$lines[$ln - 1]\n"; - # we have a preceding printk if it ends - # with "\n" ignore it, else it is to blame - if ($lines[$ln - 1] =~ m{\bprintk\(}) { - if ($rawlines[$ln - 1] !~ m{\\n"}) { - $ok = 1; - } - last; - } - } - if ($ok == 0) { - WARN("printk() should include KERN_ facility level\n" . $herecurr); - } - } - # function brace can't be on same line, except for #defines of do while, # or if closed on same line - if (($line=~/$Type\s*$Ident\(.*\).*\s{/) and - !($line=~/\#\s*define.*do\s{/) and !($line=~/}/)) { + if (($line=~/$Type\s*$Ident\(.*\).*\s\{/) and + !($line=~/\#\s*define.*do\s\{/) and !($line=~/}/)) { ERROR("open brace '{' following function declarations go on the next line\n" . $herecurr); } # open braces for enum, union and struct go on the same line. - if ($line =~ /^.\s*{/ && + if ($line =~ /^.\s*\{/ && $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) { ERROR("open brace '{' following $1 go on the same line\n" . $hereprev); } # missing space after union, struct or enum definition if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?(?:\s+$Ident)?[=\{]/) { - WARN("missing space after $1 definition\n" . $herecurr); + ERROR("missing space after $1 definition\n" . $herecurr); } # check for spacing round square brackets; allowed: # 1. with a type on the left -- int [] a; # 2. at the beginning of a line for slice initialisers -- [0...10] = 5, # 3. inside a curly brace -- = { [0...10] = 5 } +# 4. after a comma -- [1] = 5, [2] = 6 +# 5. in a macro definition -- #define abc(x) [x] = y while ($line =~ /(.*?\s)\[/g) { my ($where, $prefix) = ($-[1], $1); if ($prefix !~ /$Type\s+$/ && ($where != 0 || $prefix !~ /^.\s+$/) && - $prefix !~ /{\s+$/) { + $prefix !~ /{\s+$/ && + $prefix !~ /\#\s*define[^(]*\([^)]*\)\s+$/ && + $prefix !~ /,\s+$/) { ERROR("space prohibited before open square bracket '['\n" . $herecurr); } } @@ -2091,7 +1855,7 @@ sub process { # not required when having a single },{ on one line } elsif ($op eq ',') { if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/ && - ($elements[$n] . $elements[$n + 2]) !~ " *}{") { + ($elements[$n] . $elements[$n + 2]) !~ " *}\\{") { ERROR("space required after that '$op' $at\n" . $hereptr); } @@ -2131,19 +1895,6 @@ sub process { ERROR("space prohibited after that '$op' $at\n" . $hereptr); } - - # << and >> may either have or not have spaces both sides - } elsif ($op eq '<<' or $op eq '>>' or - $op eq '&' or $op eq '^' or $op eq '|' or - $op eq '+' or $op eq '-' or - $op eq '*' or $op eq '/' or - $op eq '%') - { - if ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { - ERROR("need consistent spacing around '$op' $at\n" . - $hereptr); - } - # A colon needs no spaces before when it is # terminating a case value or a label. } elsif ($opv eq ':C' || $opv eq ':L') { @@ -2190,29 +1941,9 @@ sub process { } } -# check for multiple assignments - if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) { - CHK("multiple assignments should be avoided\n" . $herecurr); - } - -## # check for multiple declarations, allowing for a function declaration -## # continuation. -## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ && -## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { -## -## # Remove any bracketed sections to ensure we do not -## # falsly report the parameters of functions. -## my $ln = $line; -## while ($ln =~ s/\([^\(\)]*\)//g) { -## } -## if ($ln =~ /,/) { -## WARN("declaring multiple variables together should be avoided\n" . $herecurr); -## } -## } - #need space before brace following if, while, etc - if (($line =~ /\(.*\){/ && $line !~ /\($Type\){/) || - $line =~ /do{/) { + if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\)\{/) || + $line =~ /do\{/) { ERROR("space required before the open brace '{'\n" . $herecurr); } @@ -2267,7 +1998,7 @@ sub process { if ($line =~ /^.\s*return\s*(E[A-Z]*)\s*;/) { my $name = $1; if ($name ne 'EOF' && $name ne 'ERROR') { - CHK("return of an errno should typically be -ve (return -$1)\n" . $herecurr); + WARN("return of an errno should typically be -ve (return -$1)\n" . $herecurr); } } @@ -2398,22 +2129,6 @@ sub process { WARN("Whitepspace after \\ makes next lines useless\n" . $herecurr); } -#warn if is #included and is available (uses RAW line) - if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\}) { - my $file = "$1.h"; - my $checkfile = "include/linux/$file"; - if (-f "$root/$checkfile" && - $realfile ne $checkfile && - $1 !~ /$allowed_asm_includes/) - { - if ($realfile =~ m{^arch/}) { - CHK("Consider using #include instead of \n" . $herecurr); - } else { - WARN("Use #include instead of \n" . $herecurr); - } - } - } - # multi-statement macros should be enclosed in a do while loop, grab the # first statement and ensure its the whole macro if its not enclosed # in a known good container @@ -2508,15 +2223,6 @@ sub process { } } -# make sure symbols are always wrapped with VMLINUX_SYMBOL() ... -# all assignments may have only one of the following with an assignment: -# . -# ALIGN(...) -# VMLINUX_SYMBOL(...) - if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) { - WARN("vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr); - } - # check for missing bracing round if etc if ($line =~ /(^.*)\bif\b/ && $line !~ /\#\s*if/) { my ($level, $endln, @chunks) = @@ -2551,7 +2257,7 @@ sub process { my $spaced_block = $block; $spaced_block =~ s/\n\+/ /g; - $seen++ if ($spaced_block =~ /^\s*{/); + $seen++ if ($spaced_block =~ /^\s*\{/); print "APW: cond<$cond> block<$block> allowed<$allowed>\n" if $dbg_adv_apw; @@ -2644,64 +2350,23 @@ sub process { } } -# don't include deprecated include files (uses RAW line) - for my $inc (@dep_includes) { - if ($rawline =~ m@^.\s*\#\s*include\s*\<$inc>@) { - ERROR("Don't use <$inc>: see Documentation/feature-removal-schedule.txt\n" . $herecurr); - } - } - -# don't use deprecated functions - for my $func (@dep_functions) { - if ($line =~ /\b$func\b/) { - ERROR("Don't use $func(): see Documentation/feature-removal-schedule.txt\n" . $herecurr); - } - } - # no volatiles please my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b}; if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) { WARN("Use of volatile is usually wrong: see Documentation/volatile-considered-harmful.txt\n" . $herecurr); } -# SPIN_LOCK_UNLOCKED & RW_LOCK_UNLOCKED are deprecated - if ($line =~ /\b(SPIN_LOCK_UNLOCKED|RW_LOCK_UNLOCKED)/) { - ERROR("Use of $1 is deprecated: see Documentation/spinlocks.txt\n" . $herecurr); - } - # warn about #if 0 if ($line =~ /^.\s*\#\s*if\s+0\b/) { - CHK("if this code is redundant consider removing it\n" . + WARN("if this code is redundant consider removing it\n" . $herecurr); } -# check for needless kfree() checks +# check for needless g_free() checks if ($prevline =~ /\bif\s*\(([^\)]*)\)/) { my $expr = $1; - if ($line =~ /\bkfree\(\Q$expr\E\);/) { - WARN("kfree(NULL) is safe this check is probably not required\n" . $hereprev); - } - } -# check for needless usb_free_urb() checks - if ($prevline =~ /\bif\s*\(([^\)]*)\)/) { - my $expr = $1; - if ($line =~ /\busb_free_urb\(\Q$expr\E\);/) { - WARN("usb_free_urb(NULL) is safe this check is probably not required\n" . $hereprev); - } - } - -# prefer usleep_range over udelay - if ($line =~ /\budelay\s*\(\s*(\w+)\s*\)/) { - # ignore udelay's < 10, however - if (! (($1 =~ /(\d+)/) && ($1 < 10)) ) { - CHK("usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $line); - } - } - -# warn about unexpectedly long msleep's - if ($line =~ /\bmsleep\s*\((\d+)\);/) { - if ($1 < 20) { - WARN("msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $line); + if ($line =~ /\bg_free\(\Q$expr\E\);/) { + WARN("g_free(NULL) is safe this check is probably not required\n" . $hereprev); } } @@ -2716,24 +2381,17 @@ sub process { if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) { ERROR("exactly one space required after that #$1\n" . $herecurr); } - -# check for spinlock_t definitions without a comment. - if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ || - $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) { - my $which = $1; - if (!ctx_has_comment($first_line, $linenr)) { - CHK("$1 definition without comment\n" . $herecurr); - } - } # check for memory barriers without a comment. - if ($line =~ /\b(mb|rmb|wmb|read_barrier_depends|smp_mb|smp_rmb|smp_wmb|smp_read_barrier_depends)\(/) { + if ($line =~ /\b(smp_mb|smp_rmb|smp_wmb|smp_read_barrier_depends)\(/) { if (!ctx_has_comment($first_line, $linenr)) { - CHK("memory barrier without comment\n" . $herecurr); + WARN("memory barrier without comment\n" . $herecurr); } } # check of hardware specific defines - if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) { - CHK("architecture specific defines should be avoided\n" . $herecurr); +# we have e.g. CONFIG_LINUX and CONFIG_WIN32 for common cases +# where they might be necessary. + if ($line =~ m@^.\s*\#\s*if.*\b__@) { + WARN("architecture specific defines should be avoided\n" . $herecurr); } # Check that the storage class is at the beginning of a declaration @@ -2748,11 +2406,6 @@ sub process { ERROR("inline keyword should sit between storage class and type\n" . $herecurr); } -# Check for __inline__ and __inline, prefer inline - if ($line =~ /\b(__inline__|__inline)\b/) { - WARN("plain inline is preferred over $1\n" . $herecurr); - } - # check for sizeof(&) if ($line =~ /\bsizeof\s*\(\s*\&/) { WARN("sizeof(& should be avoided\n" . $herecurr); @@ -2785,98 +2438,55 @@ sub process { WARN("externs should be avoided in .c files\n" . $herecurr); } -# checks for new __setup's - if ($rawline =~ /\b__setup\("([^"]*)"/) { - my $name = $1; - - if (!grep(/$name/, @setup_docs)) { - CHK("__setup appears un-documented -- check Documentation/kernel-parameters.txt\n" . $herecurr); +# check for pointless casting of g_malloc return + if ($line =~ /\*\s*\)\s*g_(try)?(m|re)alloc(0?)(_n)?\b/) { + if ($2 == 'm') { + WARN("unnecessary cast may hide bugs, use g_$1new$3 instead\n" . $herecurr); + } else { + WARN("unnecessary cast may hide bugs, use g_$1renew$3 instead\n" . $herecurr); } } -# check for pointless casting of kmalloc return - if ($line =~ /\*\s*\)\s*k[czm]alloc\b/) { - WARN("unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); - } - # check for gcc specific __FUNCTION__ if ($line =~ /__FUNCTION__/) { WARN("__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr); } -# check for semaphores used as mutexes - if ($line =~ /^.\s*(DECLARE_MUTEX|init_MUTEX)\s*\(/) { - WARN("mutexes are preferred for single holder semaphores\n" . $herecurr); - } -# check for semaphores used as mutexes - if ($line =~ /^.\s*init_MUTEX_LOCKED\s*\(/) { - WARN("consider using a completion\n" . $herecurr); - - } -# recommend strict_strto* over simple_strto* - if ($line =~ /\bsimple_(strto.*?)\s*\(/) { - WARN("consider using strict_$1 in preference to simple_$1\n" . $herecurr); +# recommend qemu_strto* over strto* for numeric conversions + if ($line =~ /\b(strto[^k].*?)\s*\(/) { + WARN("consider using qemu_$1 in preference to $1\n" . $herecurr); } -# check for __initcall(), use device_initcall() explicitly please - if ($line =~ /^.\s*__initcall\s*\(/) { - WARN("please use device_initcall() instead of __initcall()\n" . $herecurr); +# check for module_init(), use category-specific init macros explicitly please + if ($line =~ /^module_init\s*\(/) { + WARN("please use block_init(), type_init() etc. instead of module_init()\n" . $herecurr); } # check for various ops structs, ensure they are const. - my $struct_ops = qr{acpi_dock_ops| - address_space_operations| - backlight_ops| - block_device_operations| - dentry_operations| - dev_pm_ops| - dma_map_ops| - extent_io_ops| - file_lock_operations| - file_operations| - hv_ops| - ide_dma_ops| - intel_dvo_dev_ops| - item_operations| - iwl_ops| - kgdb_arch| - kgdb_io| - kset_uevent_ops| - lock_manager_operations| - microcode_ops| - mtrr_ops| - neigh_ops| - nlmsvc_binding| - pci_raw_ops| - pipe_buf_operations| - platform_hibernation_ops| - platform_suspend_ops| - proto_ops| - rpc_pipe_ops| - seq_operations| - snd_ac97_build_ops| - soc_pcmcia_socket_ops| - stacktrace_ops| - sysfs_ops| - tty_operations| - usb_mon_operations| - wd_ops}x; + my $struct_ops = qr{AIOCBInfo| + BdrvActionOps| + BlockDevOps| + BlockJobDriver| + DisplayChangeListenerOps| + GraphicHwOps| + IDEDMAOps| + KVMCapabilityInfo| + MemoryRegionIOMMUOps| + MemoryRegionOps| + MemoryRegionPortio| + QEMUFileOps| + SCSIBusInfo| + SCSIReqOps| + Spice[A-Z][a-zA-Z0-9]*Interface| + TPMDriverOps| + USBDesc[A-Z][a-zA-Z0-9]*| + VhostOps| + VMStateDescription| + VMStateInfo}x; if ($line !~ /\bconst\b/ && - $line =~ /\bstruct\s+($struct_ops)\b/) { + $line =~ /\b($struct_ops)\b/) { WARN("struct $1 should normally be const\n" . $herecurr); } -# use of NR_CPUS is usually wrong -# ignore definitions of NR_CPUS and usage to define arrays as likely right - if ($line =~ /\bNR_CPUS\b/ && - $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ && - $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ && - $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ && - $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ && - $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/) - { - WARN("usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr); - } - # check for %L{u,d,i} in strings my $string; while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { @@ -2888,29 +2498,46 @@ sub process { } } -# whine mightly about in_atomic - if ($line =~ /\bin_atomic\s*\(/) { - if ($realfile =~ m@^drivers/@) { - ERROR("do not use in_atomic in drivers\n" . $herecurr); - } elsif ($realfile !~ m@^kernel/@) { - WARN("use of in_atomic() is incorrect outside core kernel code\n" . $herecurr); - } +# QEMU specific tests + if ($rawline =~ /\b(?:Qemu|QEmu)\b/) { + WARN("use QEMU instead of Qemu or QEmu\n" . $herecurr); } -# check for lockdep_set_novalidate_class - if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ || - $line =~ /__lockdep_no_validate__\s*\)/ ) { - if ($realfile !~ m@^kernel/lockdep@ && - $realfile !~ m@^include/linux/lockdep@ && - $realfile !~ m@^drivers/base/core@) { - ERROR("lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr); - } +# Qemu error function tests + + # Find newlines in error messages + my $qemu_error_funcs = qr{error_setg| + error_setg_errno| + error_setg_win32| + error_set| + error_vreport| + error_report}x; + + if ($rawline =~ /\b(?:$qemu_error_funcs)\s*\(\s*\".*\\n/) { + WARN("Error messages should not contain newlines\n" . $herecurr); + } + + # Continue checking for error messages that contains newlines. This + # check handles cases where string literals are spread over multiple lines. + # Example: + # error_report("Error msg line #1" + # "Error msg line #2\n"); + my $quoted_newline_regex = qr{\+\s*\".*\\n.*\"}; + my $continued_str_literal = qr{\+\s*\".*\"}; + + if ($rawline =~ /$quoted_newline_regex/) { + # Backtrack to first line that does not contain only a quoted literal + # and assume that it is the start of the statement. + my $i = $linenr - 2; + + while (($i >= 0) & $rawlines[$i] =~ /$continued_str_literal/) { + $i--; } -# QEMU specific tests - if ($rawline =~ /\b(?:Qemu|QEmu)\b/) { - WARN("use QEMU instead of Qemu or QEmu\n" . $herecurr); + if ($rawlines[$i] =~ /\b(?:$qemu_error_funcs)\s*\(/) { + WARN("Error messages should not contain newlines\n" . $herecurr); } + } # check for non-portable ffs() calls that have portable alternatives in QEMU if ($line =~ /\bffs\(/) { diff --git a/qemu/scripts/clean-includes b/qemu/scripts/clean-includes new file mode 100755 index 000000000..72b47f17f --- /dev/null +++ b/qemu/scripts/clean-includes @@ -0,0 +1,165 @@ +#!/bin/sh -e +# +# Clean up QEMU #include lines by ensuring that qemu/osdep.h +# is the first include listed in .c files, and no headers provided +# by osdep.h itself are redundantly included in either .c or .h files. +# +# Copyright (c) 2015 Linaro Limited +# +# Authors: +# Peter Maydell +# +# This work is licensed under the terms of the GNU GPL, version 2 +# or (at your option) any later version. See the COPYING file in +# the top-level directory. + +# Usage: +# clean-includes [--git subjectprefix] file ... +# or +# clean-includes [--git subjectprefix] --all +# +# If the --git subjectprefix option is given, then after making +# the changes to the files this script will create a git commit +# with the subject line "subjectprefix: Clean up includes" +# and a boilerplate commit message. +# +# Using --all will cause clean-includes to run on the whole source +# tree (excluding certain directories which are known not to need +# handling). + +# This script requires Coccinelle to be installed. + +# .c files will have the osdep.h included added, and redundant +# includes removed. +# .h files will have redundant includes (including includes of osdep.h) +# removed. +# Other files (including C++ and ObjectiveC) can't be handled by this script. + +# The following one-liner may be handy for finding files to run this on. +# However some caution is required regarding files that might be part +# of the guest agent or standalone tests. + +# for i in `git ls-tree --name-only HEAD` ; do test -f $i && \ +# grep -E '^# *include' $i | head -1 | grep 'osdep.h' ; test $? != 0 && \ +# echo $i ; done + + +GIT=no + +# Extended regular expression defining files to ignore when using --all +XDIRREGEX='^(tests/tcg|tests/multiboot|pc-bios|disas/libvixl)' + +if [ $# -ne 0 ] && [ "$1" = "--git" ]; then + if [ $# -eq 1 ]; then + echo "--git option requires an argument" + exit 1 + fi + GITSUBJ="$2" + GIT=yes + shift + shift +fi + +if [ $# -eq 0 ]; then + echo "Usage: clean-includes [--git subjectprefix] [--all | foo.c ...]" + echo "(modifies the files in place)" + exit 1 +fi + +if [ "$1" = "--all" ]; then + # We assume there are no files in the tree with spaces in their name + set -- $(git ls-files '*.[ch]' | grep -E -v "$XDIRREGEX") +fi + +# Annoyingly coccinelle won't read a scriptfile unless its +# name ends '.cocci', so write it out to a tempfile with the +# right kind of name. +COCCIFILE="$(mktemp --suffix=.cocci)" + +trap 'rm -f -- "$COCCIFILE"' INT TERM HUP EXIT + +cat >"$COCCIFILE" < +) +EOT + + +for f in "$@"; do + case "$f" in + *.inc.c) + # These aren't standalone C source files + echo "SKIPPING $f (not a standalone source file)" + continue + ;; + *.c) + MODE=c + ;; + *include/qemu/osdep.h | \ + *include/qemu/compiler.h | \ + *include/standard-headers/ ) + # Removing include lines from osdep.h itself would be counterproductive. + echo "SKIPPING $f (special case header)" + continue + ;; + *include/standard-headers/*) + echo "SKIPPING $f (autogenerated header)" + continue + ;; + *.h) + MODE=h + ;; + *) + echo "WARNING: ignoring $f (cannot handle non-C files)" + continue + ;; + esac + + if [ "$MODE" = "c" ]; then + # First, use Coccinelle to add qemu/osdep.h before the first existing include + # (this will add two lines if the file uses both "..." and <...> #includes, + # but we will remove the extras in the next step) + spatch --in-place --no-show-diff --cocci-file "$COCCIFILE" "$f" + + # Now remove any duplicate osdep.h includes + perl -n -i -e 'print if !/#include "qemu\/osdep.h"/ || !$n++;' "$f" + else + # Remove includes of osdep.h itself + perl -n -i -e 'print if !/\s*#\s*include\s*(["<][^>"]*[">])/ || + ! (grep { $_ eq $1 } qw ("qemu/osdep.h"))' "$f" + fi + + # Remove includes that osdep.h already provides + perl -n -i -e 'print if !/\s*#\s*include\s*(["<][^>"]*[">])/ || + ! (grep { $_ eq $1 } qw ( + "config-host.h" "config-target.h" "qemu/compiler.h" + + + + + "sysemu/os-posix.h, sysemu/os-win32.h "glib-compat.h" + "qemu/typedefs.h" + ))' "$f" + +done + +if [ "$GIT" = "yes" ]; then + git add -- "$@" + git commit --signoff -F - < + * + * This work is licensed under the terms of the GNU GPL, version 2 or, at your + * option, any later version. See the COPYING file in the top-level directory. + */ + +/* Coccinelle only does limited parsing of headers, and chokes on some idioms + * defined in compiler.h and queue.h. Macros that Coccinelle must know about + * in order to parse .c files must be in a separate macro file---which is + * exactly what you're staring at now. + * + * To use this file, add the "--macro-file scripts/cocci-macro-file.h" to the + * Coccinelle command line. + */ + +/* From qemu/compiler.h */ +#define QEMU_GNUC_PREREQ(maj, min) 1 +#define QEMU_NORETURN __attribute__ ((__noreturn__)) +#define QEMU_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#define QEMU_SENTINEL __attribute__((sentinel)) +#define QEMU_ARTIFICIAL __attribute__((always_inline, artificial)) +#define QEMU_PACKED __attribute__((gcc_struct, packed)) + +#define cat(x,y) x ## y +#define cat2(x,y) cat(x,y) +#define QEMU_BUILD_BUG_ON(x) \ + typedef char cat2(qemu_build_bug_on__,__LINE__)[(x)?-1:1] __attribute__((unused)); + +#define GCC_FMT_ATTR(n, m) __attribute__((format(gnu_printf, n, m))) + +#define xglue(x, y) x ## y +#define glue(x, y) xglue(x, y) +#define stringify(s) tostring(s) +#define tostring(s) #s + +#define typeof_field(type, field) typeof(((type *)0)->field) +#define type_check(t1,t2) ((t1*)0 - (t2*)0) + +/* From qemu/queue.h */ + +#define QLIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define QLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define QLIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * Singly-linked List definitions. + */ +#define QSLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define QSLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define QSLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Simple queue definitions. + */ +#define QSIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define QSIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define QSIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Tail queue definitions. + */ +#define Q_TAILQ_HEAD(name, type, qual) \ +struct name { \ + qual type *tqh_first; /* first element */ \ + qual type *qual *tqh_last; /* addr of last next element */ \ +} +#define QTAILQ_HEAD(name, type) \ +struct name { \ + type *tqh_first; /* first element */ \ + type **tqh_last; /* addr of last next element */ \ +} + +#define QTAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define Q_TAILQ_ENTRY(type, qual) \ +struct { \ + qual type *tqe_next; /* next element */ \ + qual type *qual *tqe_prev; /* address of previous next element */\ +} +#define QTAILQ_ENTRY(type) \ +struct { \ + type *tqe_next; /* next element */ \ + type **tqe_prev; /* address of previous next element */ \ +} diff --git a/qemu/scripts/coverity-model.c b/qemu/scripts/coverity-model.c index 617f67d71..ee5bf9d07 100644 --- a/qemu/scripts/coverity-model.c +++ b/qemu/scripts/coverity-model.c @@ -236,6 +236,23 @@ void *g_try_realloc(void *ptr, size_t size) return g_try_realloc_n(ptr, 1, size); } +/* Other memory allocation functions */ + +void *g_memdup(const void *ptr, unsigned size) +{ + unsigned char *dup; + unsigned i; + + if (!ptr) { + return NULL; + } + + dup = g_malloc(size); + for (i = 0; i < size; i++) + dup[i] = ((unsigned char *)ptr)[i]; + return dup; +} + /* * GLib string allocation functions */ @@ -325,6 +342,15 @@ char *g_strconcat(const char *s, ...) /* Other glib functions */ +typedef struct pollfd GPollFD; + +int poll(); + +int g_poll (GPollFD *fds, unsigned nfds, int timeout) +{ + return poll(fds, nfds, timeout); +} + typedef struct _GIOChannel GIOChannel; GIOChannel *g_io_channel_unix_new(int fd) { diff --git a/qemu/scripts/create_config b/qemu/scripts/create_config index 546f88914..9cb176f1b 100755 --- a/qemu/scripts/create_config +++ b/qemu/scripts/create_config @@ -61,6 +61,15 @@ case $line in value=${line#*=} echo "#define $name $value" ;; + HAVE_*=y) # configuration + name=${line%=*} + echo "#define $name 1" + ;; + HAVE_*=*) # configuration + name=${line%=*} + value=${line#*=} + echo "#define $name $value" + ;; ARCH=*) # configuration arch=${line#*=} arch_name=`echo $arch | LC_ALL=C tr '[a-z]' '[A-Z]'` diff --git a/qemu/scripts/dump-guest-memory.py b/qemu/scripts/dump-guest-memory.py index dc8e44acf..c0a2e99f4 100644 --- a/qemu/scripts/dump-guest-memory.py +++ b/qemu/scripts/dump-guest-memory.py @@ -1,39 +1,456 @@ -# This python script adds a new gdb command, "dump-guest-memory". It -# should be loaded with "source dump-guest-memory.py" at the (gdb) -# prompt. -# -# Copyright (C) 2013, Red Hat, Inc. -# -# Authors: -# Laszlo Ersek -# -# This work is licensed under the terms of the GNU GPL, version 2 or later. See -# the COPYING file in the top-level directory. -# +""" +This python script adds a new gdb command, "dump-guest-memory". It +should be loaded with "source dump-guest-memory.py" at the (gdb) +prompt. + +Copyright (C) 2013, Red Hat, Inc. + +Authors: + Laszlo Ersek + Janosch Frank + +This work is licensed under the terms of the GNU GPL, version 2 or later. See +the COPYING file in the top-level directory. +""" + +import ctypes + +UINTPTR_T = gdb.lookup_type("uintptr_t") + +TARGET_PAGE_SIZE = 0x1000 +TARGET_PAGE_MASK = 0xFFFFFFFFFFFFF000 + +# Special value for e_phnum. This indicates that the real number of +# program headers is too large to fit into e_phnum. Instead the real +# value is in the field sh_info of section 0. +PN_XNUM = 0xFFFF + +EV_CURRENT = 1 + +ELFCLASS32 = 1 +ELFCLASS64 = 2 + +ELFDATA2LSB = 1 +ELFDATA2MSB = 2 + +ET_CORE = 4 + +PT_LOAD = 1 +PT_NOTE = 4 + +EM_386 = 3 +EM_PPC = 20 +EM_PPC64 = 21 +EM_S390 = 22 +EM_AARCH = 183 +EM_X86_64 = 62 + +class ELF(object): + """Representation of a ELF file.""" + + def __init__(self, arch): + self.ehdr = None + self.notes = [] + self.segments = [] + self.notes_size = 0 + self.endianess = None + self.elfclass = ELFCLASS64 + + if arch == 'aarch64-le': + self.endianess = ELFDATA2LSB + self.elfclass = ELFCLASS64 + self.ehdr = get_arch_ehdr(self.endianess, self.elfclass) + self.ehdr.e_machine = EM_AARCH + + elif arch == 'aarch64-be': + self.endianess = ELFDATA2MSB + self.ehdr = get_arch_ehdr(self.endianess, self.elfclass) + self.ehdr.e_machine = EM_AARCH + + elif arch == 'X86_64': + self.endianess = ELFDATA2LSB + self.ehdr = get_arch_ehdr(self.endianess, self.elfclass) + self.ehdr.e_machine = EM_X86_64 + + elif arch == '386': + self.endianess = ELFDATA2LSB + self.elfclass = ELFCLASS32 + self.ehdr = get_arch_ehdr(self.endianess, self.elfclass) + self.ehdr.e_machine = EM_386 + + elif arch == 's390': + self.endianess = ELFDATA2MSB + self.ehdr = get_arch_ehdr(self.endianess, self.elfclass) + self.ehdr.e_machine = EM_S390 + + elif arch == 'ppc64-le': + self.endianess = ELFDATA2LSB + self.ehdr = get_arch_ehdr(self.endianess, self.elfclass) + self.ehdr.e_machine = EM_PPC64 + + elif arch == 'ppc64-be': + self.endianess = ELFDATA2MSB + self.ehdr = get_arch_ehdr(self.endianess, self.elfclass) + self.ehdr.e_machine = EM_PPC64 + + else: + raise gdb.GdbError("No valid arch type specified.\n" + "Currently supported types:\n" + "aarch64-be, aarch64-le, X86_64, 386, s390, " + "ppc64-be, ppc64-le") + + self.add_segment(PT_NOTE, 0, 0) + + def add_note(self, n_name, n_desc, n_type): + """Adds a note to the ELF.""" + + note = get_arch_note(self.endianess, len(n_name), len(n_desc)) + note.n_namesz = len(n_name) + 1 + note.n_descsz = len(n_desc) + note.n_name = n_name.encode() + note.n_type = n_type + + # Desc needs to be 4 byte aligned (although the 64bit spec + # specifies 8 byte). When defining n_desc as uint32 it will be + # automatically aligned but we need the memmove to copy the + # string into it. + ctypes.memmove(note.n_desc, n_desc.encode(), len(n_desc)) + + self.notes.append(note) + self.segments[0].p_filesz += ctypes.sizeof(note) + self.segments[0].p_memsz += ctypes.sizeof(note) + + def add_segment(self, p_type, p_paddr, p_size): + """Adds a segment to the elf.""" + + phdr = get_arch_phdr(self.endianess, self.elfclass) + phdr.p_type = p_type + phdr.p_paddr = p_paddr + phdr.p_filesz = p_size + phdr.p_memsz = p_size + self.segments.append(phdr) + self.ehdr.e_phnum += 1 + + def to_file(self, elf_file): + """Writes all ELF structures to the the passed file. + + Structure: + Ehdr + Segment 0:PT_NOTE + Segment 1:PT_LOAD + Segment N:PT_LOAD + Note 0..N + Dump contents + """ + elf_file.write(self.ehdr) + off = ctypes.sizeof(self.ehdr) + \ + len(self.segments) * ctypes.sizeof(self.segments[0]) + + for phdr in self.segments: + phdr.p_offset = off + elf_file.write(phdr) + off += phdr.p_filesz + + for note in self.notes: + elf_file.write(note) + + +def get_arch_note(endianess, len_name, len_desc): + """Returns a Note class with the specified endianess.""" + + if endianess == ELFDATA2LSB: + superclass = ctypes.LittleEndianStructure + else: + superclass = ctypes.BigEndianStructure + + len_name = len_name + 1 + + class Note(superclass): + """Represents an ELF note, includes the content.""" + + _fields_ = [("n_namesz", ctypes.c_uint32), + ("n_descsz", ctypes.c_uint32), + ("n_type", ctypes.c_uint32), + ("n_name", ctypes.c_char * len_name), + ("n_desc", ctypes.c_uint32 * ((len_desc + 3) // 4))] + return Note() + + +class Ident(ctypes.Structure): + """Represents the ELF ident array in the ehdr structure.""" + + _fields_ = [('ei_mag0', ctypes.c_ubyte), + ('ei_mag1', ctypes.c_ubyte), + ('ei_mag2', ctypes.c_ubyte), + ('ei_mag3', ctypes.c_ubyte), + ('ei_class', ctypes.c_ubyte), + ('ei_data', ctypes.c_ubyte), + ('ei_version', ctypes.c_ubyte), + ('ei_osabi', ctypes.c_ubyte), + ('ei_abiversion', ctypes.c_ubyte), + ('ei_pad', ctypes.c_ubyte * 7)] + + def __init__(self, endianess, elfclass): + self.ei_mag0 = 0x7F + self.ei_mag1 = ord('E') + self.ei_mag2 = ord('L') + self.ei_mag3 = ord('F') + self.ei_class = elfclass + self.ei_data = endianess + self.ei_version = EV_CURRENT + + +def get_arch_ehdr(endianess, elfclass): + """Returns a EHDR64 class with the specified endianess.""" + + if endianess == ELFDATA2LSB: + superclass = ctypes.LittleEndianStructure + else: + superclass = ctypes.BigEndianStructure + + class EHDR64(superclass): + """Represents the 64 bit ELF header struct.""" + + _fields_ = [('e_ident', Ident), + ('e_type', ctypes.c_uint16), + ('e_machine', ctypes.c_uint16), + ('e_version', ctypes.c_uint32), + ('e_entry', ctypes.c_uint64), + ('e_phoff', ctypes.c_uint64), + ('e_shoff', ctypes.c_uint64), + ('e_flags', ctypes.c_uint32), + ('e_ehsize', ctypes.c_uint16), + ('e_phentsize', ctypes.c_uint16), + ('e_phnum', ctypes.c_uint16), + ('e_shentsize', ctypes.c_uint16), + ('e_shnum', ctypes.c_uint16), + ('e_shstrndx', ctypes.c_uint16)] + + def __init__(self): + super(superclass, self).__init__() + self.e_ident = Ident(endianess, elfclass) + self.e_type = ET_CORE + self.e_version = EV_CURRENT + self.e_ehsize = ctypes.sizeof(self) + self.e_phoff = ctypes.sizeof(self) + self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianess, elfclass)) + self.e_phnum = 0 + + + class EHDR32(superclass): + """Represents the 32 bit ELF header struct.""" + + _fields_ = [('e_ident', Ident), + ('e_type', ctypes.c_uint16), + ('e_machine', ctypes.c_uint16), + ('e_version', ctypes.c_uint32), + ('e_entry', ctypes.c_uint32), + ('e_phoff', ctypes.c_uint32), + ('e_shoff', ctypes.c_uint32), + ('e_flags', ctypes.c_uint32), + ('e_ehsize', ctypes.c_uint16), + ('e_phentsize', ctypes.c_uint16), + ('e_phnum', ctypes.c_uint16), + ('e_shentsize', ctypes.c_uint16), + ('e_shnum', ctypes.c_uint16), + ('e_shstrndx', ctypes.c_uint16)] + + def __init__(self): + super(superclass, self).__init__() + self.e_ident = Ident(endianess, elfclass) + self.e_type = ET_CORE + self.e_version = EV_CURRENT + self.e_ehsize = ctypes.sizeof(self) + self.e_phoff = ctypes.sizeof(self) + self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianess, elfclass)) + self.e_phnum = 0 + + # End get_arch_ehdr + if elfclass == ELFCLASS64: + return EHDR64() + else: + return EHDR32() + + +def get_arch_phdr(endianess, elfclass): + """Returns a 32 or 64 bit PHDR class with the specified endianess.""" + + if endianess == ELFDATA2LSB: + superclass = ctypes.LittleEndianStructure + else: + superclass = ctypes.BigEndianStructure + + class PHDR64(superclass): + """Represents the 64 bit ELF program header struct.""" + + _fields_ = [('p_type', ctypes.c_uint32), + ('p_flags', ctypes.c_uint32), + ('p_offset', ctypes.c_uint64), + ('p_vaddr', ctypes.c_uint64), + ('p_paddr', ctypes.c_uint64), + ('p_filesz', ctypes.c_uint64), + ('p_memsz', ctypes.c_uint64), + ('p_align', ctypes.c_uint64)] + + class PHDR32(superclass): + """Represents the 32 bit ELF program header struct.""" + + _fields_ = [('p_type', ctypes.c_uint32), + ('p_offset', ctypes.c_uint32), + ('p_vaddr', ctypes.c_uint32), + ('p_paddr', ctypes.c_uint32), + ('p_filesz', ctypes.c_uint32), + ('p_memsz', ctypes.c_uint32), + ('p_flags', ctypes.c_uint32), + ('p_align', ctypes.c_uint32)] + + # End get_arch_phdr + if elfclass == ELFCLASS64: + return PHDR64() + else: + return PHDR32() + + +def int128_get64(val): + """Returns low 64bit part of Int128 struct.""" + + assert val["hi"] == 0 + return val["lo"] + + +def qlist_foreach(head, field_str): + """Generator for qlists.""" + + var_p = head["lh_first"] + while var_p != 0: + var = var_p.dereference() + var_p = var[field_str]["le_next"] + yield var + + +def qemu_get_ram_block(ram_addr): + """Returns the RAMBlock struct to which the given address belongs.""" + + ram_blocks = gdb.parse_and_eval("ram_list.blocks") + + for block in qlist_foreach(ram_blocks, "next"): + if (ram_addr - block["offset"]) < block["used_length"]: + return block + + raise gdb.GdbError("Bad ram offset %x" % ram_addr) + + +def qemu_get_ram_ptr(ram_addr): + """Returns qemu vaddr for given guest physical address.""" + + block = qemu_get_ram_block(ram_addr) + return block["host"] + (ram_addr - block["offset"]) + + +def memory_region_get_ram_ptr(memory_region): + if memory_region["alias"] != 0: + return (memory_region_get_ram_ptr(memory_region["alias"].dereference()) + + memory_region["alias_offset"]) + + return qemu_get_ram_ptr(memory_region["ram_block"]["offset"]) + + +def get_guest_phys_blocks(): + """Returns a list of ram blocks. + + Each block entry contains: + 'target_start': guest block phys start address + 'target_end': guest block phys end address + 'host_addr': qemu vaddr of the block's start + """ + + guest_phys_blocks = [] + + print("guest RAM blocks:") + print("target_start target_end host_addr message " + "count") + print("---------------- ---------------- ---------------- ------- " + "-----") + + current_map_p = gdb.parse_and_eval("address_space_memory.current_map") + current_map = current_map_p.dereference() + + # Conversion to int is needed for python 3 + # compatibility. Otherwise range doesn't cast the value itself and + # breaks. + for cur in range(int(current_map["nr"])): + flat_range = (current_map["ranges"] + cur).dereference() + memory_region = flat_range["mr"].dereference() + + # we only care about RAM + if not memory_region["ram"]: + continue + + section_size = int128_get64(flat_range["addr"]["size"]) + target_start = int128_get64(flat_range["addr"]["start"]) + target_end = target_start + section_size + host_addr = (memory_region_get_ram_ptr(memory_region) + + flat_range["offset_in_region"]) + predecessor = None + + # find continuity in guest physical address space + if len(guest_phys_blocks) > 0: + predecessor = guest_phys_blocks[-1] + predecessor_size = (predecessor["target_end"] - + predecessor["target_start"]) + + # the memory API guarantees monotonically increasing + # traversal + assert predecessor["target_end"] <= target_start + + # we want continuity in both guest-physical and + # host-virtual memory + if (predecessor["target_end"] < target_start or + predecessor["host_addr"] + predecessor_size != host_addr): + predecessor = None + + if predecessor is None: + # isolated mapping, add it to the list + guest_phys_blocks.append({"target_start": target_start, + "target_end": target_end, + "host_addr": host_addr}) + message = "added" + else: + # expand predecessor until @target_end; predecessor's + # start doesn't change + predecessor["target_end"] = target_end + message = "joined" + + print("%016x %016x %016x %-7s %5u" % + (target_start, target_end, host_addr.cast(UINTPTR_T), + message, len(guest_phys_blocks))) + + return guest_phys_blocks + + # The leading docstring doesn't have idiomatic Python formatting. It is # printed by gdb's "help" command (the first line is printed in the # "help data" summary), and it should match how other help texts look in # gdb. - -import struct - class DumpGuestMemory(gdb.Command): """Extract guest vmcore from qemu process coredump. -The sole argument is FILE, identifying the target file to write the -guest vmcore to. +The two required arguments are FILE and ARCH: +FILE identifies the target file to write the guest vmcore to. +ARCH specifies the architecture for which the core will be generated. This GDB command reimplements the dump-guest-memory QMP command in python, using the representation of guest memory as captured in the qemu coredump. The qemu process that has been dumped must have had the -command line option "-machine dump-guest-core=on". +command line option "-machine dump-guest-core=on" which is the default. For simplicity, the "paging", "begin" and "end" parameters of the QMP command are not supported -- no attempt is made to get the guest's internal paging structures (ie. paging=false is hard-wired), and guest memory is always fully dumped. -Only x86_64 guests are supported. +Currently aarch64-be, aarch64-le, X86_64, 386, s390, ppc64-be, +ppc64-le guests are supported. The CORE/NT_PRSTATUS and QEMU notes (that is, the VCPUs' statuses) are not written to the vmcore. Preparing these would require context that is @@ -47,293 +464,66 @@ deliberately called abort(), or it was dumped in response to a signal at a halfway fortunate point, then its coredump should be in reasonable shape and this command should mostly work.""" - TARGET_PAGE_SIZE = 0x1000 - TARGET_PAGE_MASK = 0xFFFFFFFFFFFFF000 - - # Various ELF constants - EM_X86_64 = 62 # AMD x86-64 target machine - ELFDATA2LSB = 1 # little endian - ELFCLASS64 = 2 - ELFMAG = "\x7FELF" - EV_CURRENT = 1 - ET_CORE = 4 - PT_LOAD = 1 - PT_NOTE = 4 - - # Special value for e_phnum. This indicates that the real number of - # program headers is too large to fit into e_phnum. Instead the real - # value is in the field sh_info of section 0. - PN_XNUM = 0xFFFF - - # Format strings for packing and header size calculation. - ELF64_EHDR = ("4s" # e_ident/magic - "B" # e_ident/class - "B" # e_ident/data - "B" # e_ident/version - "B" # e_ident/osabi - "8s" # e_ident/pad - "H" # e_type - "H" # e_machine - "I" # e_version - "Q" # e_entry - "Q" # e_phoff - "Q" # e_shoff - "I" # e_flags - "H" # e_ehsize - "H" # e_phentsize - "H" # e_phnum - "H" # e_shentsize - "H" # e_shnum - "H" # e_shstrndx - ) - ELF64_PHDR = ("I" # p_type - "I" # p_flags - "Q" # p_offset - "Q" # p_vaddr - "Q" # p_paddr - "Q" # p_filesz - "Q" # p_memsz - "Q" # p_align - ) - def __init__(self): super(DumpGuestMemory, self).__init__("dump-guest-memory", gdb.COMMAND_DATA, gdb.COMPLETE_FILENAME) - self.uintptr_t = gdb.lookup_type("uintptr_t") - self.elf64_ehdr_le = struct.Struct("<%s" % self.ELF64_EHDR) - self.elf64_phdr_le = struct.Struct("<%s" % self.ELF64_PHDR) - - def int128_get64(self, val): - assert (val["hi"] == 0) - return val["lo"] - - def qlist_foreach(self, head, field_str): - var_p = head["lh_first"] - while (var_p != 0): - var = var_p.dereference() - yield var - var_p = var[field_str]["le_next"] - - def qemu_get_ram_block(self, ram_addr): - ram_blocks = gdb.parse_and_eval("ram_list.blocks") - for block in self.qlist_foreach(ram_blocks, "next"): - if (ram_addr - block["offset"] < block["length"]): - return block - raise gdb.GdbError("Bad ram offset %x" % ram_addr) - - def qemu_get_ram_ptr(self, ram_addr): - block = self.qemu_get_ram_block(ram_addr) - return block["host"] + (ram_addr - block["offset"]) - - def memory_region_get_ram_ptr(self, mr): - if (mr["alias"] != 0): - return (self.memory_region_get_ram_ptr(mr["alias"].dereference()) + - mr["alias_offset"]) - return self.qemu_get_ram_ptr(mr["ram_addr"] & self.TARGET_PAGE_MASK) - - def guest_phys_blocks_init(self): - self.guest_phys_blocks = [] - - def guest_phys_blocks_append(self): - print "guest RAM blocks:" - print ("target_start target_end host_addr message " - "count") - print ("---------------- ---------------- ---------------- ------- " - "-----") - - current_map_p = gdb.parse_and_eval("address_space_memory.current_map") - current_map = current_map_p.dereference() - for cur in range(current_map["nr"]): - flat_range = (current_map["ranges"] + cur).dereference() - mr = flat_range["mr"].dereference() - - # we only care about RAM - if (not mr["ram"]): - continue - - section_size = self.int128_get64(flat_range["addr"]["size"]) - target_start = self.int128_get64(flat_range["addr"]["start"]) - target_end = target_start + section_size - host_addr = (self.memory_region_get_ram_ptr(mr) + - flat_range["offset_in_region"]) - predecessor = None - - # find continuity in guest physical address space - if (len(self.guest_phys_blocks) > 0): - predecessor = self.guest_phys_blocks[-1] - predecessor_size = (predecessor["target_end"] - - predecessor["target_start"]) - - # the memory API guarantees monotonically increasing - # traversal - assert (predecessor["target_end"] <= target_start) - - # we want continuity in both guest-physical and - # host-virtual memory - if (predecessor["target_end"] < target_start or - predecessor["host_addr"] + predecessor_size != host_addr): - predecessor = None - - if (predecessor is None): - # isolated mapping, add it to the list - self.guest_phys_blocks.append({"target_start": target_start, - "target_end" : target_end, - "host_addr" : host_addr}) - message = "added" - else: - # expand predecessor until @target_end; predecessor's - # start doesn't change - predecessor["target_end"] = target_end - message = "joined" - - print ("%016x %016x %016x %-7s %5u" % - (target_start, target_end, host_addr.cast(self.uintptr_t), - message, len(self.guest_phys_blocks))) - - def cpu_get_dump_info(self): - # We can't synchronize the registers with KVM post-mortem, and - # the bits in (first_x86_cpu->env.hflags) seem to be stale; they - # may not reflect long mode for example. Hence just assume the - # most common values. This also means that instruction pointer - # etc. will be bogus in the dump, but at least the RAM contents - # should be valid. - self.dump_info = {"d_machine": self.EM_X86_64, - "d_endian" : self.ELFDATA2LSB, - "d_class" : self.ELFCLASS64} - - def encode_elf64_ehdr_le(self): - return self.elf64_ehdr_le.pack( - self.ELFMAG, # e_ident/magic - self.dump_info["d_class"], # e_ident/class - self.dump_info["d_endian"], # e_ident/data - self.EV_CURRENT, # e_ident/version - 0, # e_ident/osabi - "", # e_ident/pad - self.ET_CORE, # e_type - self.dump_info["d_machine"], # e_machine - self.EV_CURRENT, # e_version - 0, # e_entry - self.elf64_ehdr_le.size, # e_phoff - 0, # e_shoff - 0, # e_flags - self.elf64_ehdr_le.size, # e_ehsize - self.elf64_phdr_le.size, # e_phentsize - self.phdr_num, # e_phnum - 0, # e_shentsize - 0, # e_shnum - 0 # e_shstrndx - ) - - def encode_elf64_note_le(self): - return self.elf64_phdr_le.pack(self.PT_NOTE, # p_type - 0, # p_flags - (self.memory_offset - - len(self.note)), # p_offset - 0, # p_vaddr - 0, # p_paddr - len(self.note), # p_filesz - len(self.note), # p_memsz - 0 # p_align - ) - - def encode_elf64_load_le(self, offset, start_hwaddr, range_size): - return self.elf64_phdr_le.pack(self.PT_LOAD, # p_type - 0, # p_flags - offset, # p_offset - 0, # p_vaddr - start_hwaddr, # p_paddr - range_size, # p_filesz - range_size, # p_memsz - 0 # p_align - ) - - def note_init(self, name, desc, type): - # name must include a trailing NUL - namesz = (len(name) + 1 + 3) / 4 * 4 - descsz = (len(desc) + 3) / 4 * 4 - fmt = ("<" # little endian - "I" # n_namesz - "I" # n_descsz - "I" # n_type - "%us" # name - "%us" # desc - % (namesz, descsz)) - self.note = struct.pack(fmt, - len(name) + 1, len(desc), type, name, desc) - - def dump_init(self): - self.guest_phys_blocks_init() - self.guest_phys_blocks_append() - self.cpu_get_dump_info() - # we have no way to retrieve the VCPU status from KVM - # post-mortem - self.note_init("NONE", "EMPTY", 0) - - # Account for PT_NOTE. - self.phdr_num = 1 - - # We should never reach PN_XNUM for paging=false dumps: there's - # just a handful of discontiguous ranges after merging. - self.phdr_num += len(self.guest_phys_blocks) - assert (self.phdr_num < self.PN_XNUM) - - # Calculate the ELF file offset where the memory dump commences: - # - # ELF header - # PT_NOTE - # PT_LOAD: 1 - # PT_LOAD: 2 - # ... - # PT_LOAD: len(self.guest_phys_blocks) - # ELF note - # memory dump - self.memory_offset = (self.elf64_ehdr_le.size + - self.elf64_phdr_le.size * self.phdr_num + - len(self.note)) - - def dump_begin(self, vmcore): - vmcore.write(self.encode_elf64_ehdr_le()) - vmcore.write(self.encode_elf64_note_le()) - running = self.memory_offset + self.elf = None + self.guest_phys_blocks = None + + def dump_init(self, vmcore): + """Prepares and writes ELF structures to core file.""" + + # Needed to make crash happy, data for more useful notes is + # not available in a qemu core. + self.elf.add_note("NONE", "EMPTY", 0) + + # We should never reach PN_XNUM for paging=false dumps, + # there's just a handful of discontiguous ranges after + # merging. + # The constant is needed to account for the PT_NOTE segment. + phdr_num = len(self.guest_phys_blocks) + 1 + assert phdr_num < PN_XNUM + for block in self.guest_phys_blocks: - range_size = block["target_end"] - block["target_start"] - vmcore.write(self.encode_elf64_load_le(running, - block["target_start"], - range_size)) - running += range_size - vmcore.write(self.note) + block_size = block["target_end"] - block["target_start"] + self.elf.add_segment(PT_LOAD, block["target_start"], block_size) + + self.elf.to_file(vmcore) def dump_iterate(self, vmcore): + """Writes guest core to file.""" + qemu_core = gdb.inferiors()[0] for block in self.guest_phys_blocks: - cur = block["host_addr"] + cur = block["host_addr"] left = block["target_end"] - block["target_start"] - print ("dumping range at %016x for length %016x" % - (cur.cast(self.uintptr_t), left)) - while (left > 0): - chunk_size = min(self.TARGET_PAGE_SIZE, left) + print("dumping range at %016x for length %016x" % + (cur.cast(UINTPTR_T), left)) + + while left > 0: + chunk_size = min(TARGET_PAGE_SIZE, left) chunk = qemu_core.read_memory(cur, chunk_size) vmcore.write(chunk) - cur += chunk_size + cur += chunk_size left -= chunk_size - def create_vmcore(self, filename): - vmcore = open(filename, "wb") - self.dump_begin(vmcore) - self.dump_iterate(vmcore) - vmcore.close() - def invoke(self, args, from_tty): + """Handles command invocation from gdb.""" + # Unwittingly pressing the Enter key after the command should # not dump the same multi-gig coredump to the same file. self.dont_repeat() argv = gdb.string_to_argv(args) - if (len(argv) != 1): - raise gdb.GdbError("usage: dump-guest-memory FILE") + if len(argv) != 2: + raise gdb.GdbError("usage: dump-guest-memory FILE ARCH") + + self.elf = ELF(argv[1]) + self.guest_phys_blocks = get_guest_phys_blocks() - self.dump_init() - self.create_vmcore(argv[0]) + with open(argv[0], "wb") as vmcore: + self.dump_init(vmcore) + self.dump_iterate(vmcore) DumpGuestMemory() diff --git a/qemu/scripts/feature_to_c.sh b/qemu/scripts/feature_to_c.sh index 888548e58..fb1f3363f 100644 --- a/qemu/scripts/feature_to_c.sh +++ b/qemu/scripts/feature_to_c.sh @@ -36,7 +36,7 @@ for input; do arrayname=xml_feature_`echo $input | sed 's,.*/,,; s/[-.]/_/g'` ${AWK:-awk} 'BEGIN { n = 0 - printf "#include \"config.h\"\n" + printf "#include \"qemu/osdep.h\"\n" printf "#include \"qemu-common.h\"\n" printf "#include \"exec/gdbstub.h\"\n" print "static const char '$arrayname'[] = {" diff --git a/qemu/scripts/get_maintainer.pl b/qemu/scripts/get_maintainer.pl index f39630eb3..8261bcb1a 100755 --- a/qemu/scripts/get_maintainer.pl +++ b/qemu/scripts/get_maintainer.pl @@ -258,7 +258,7 @@ open (my $maint, '<', "${lk_path}MAINTAINERS") while (<$maint>) { my $line = $_; - if ($line =~ m/^(\C):\s*(.*)/) { + if ($line =~ m/^(.):\s*(.*)/) { my $type = $1; my $value = $2; @@ -493,7 +493,7 @@ sub range_is_maintained { for (my $i = $start; $i < $end; $i++) { my $line = $typevalue[$i]; - if ($line =~ m/^(\C):\s*(.*)/) { + if ($line =~ m/^(.):\s*(.*)/) { my $type = $1; my $value = $2; if ($type eq 'S') { @@ -511,7 +511,7 @@ sub range_has_maintainer { for (my $i = $start; $i < $end; $i++) { my $line = $typevalue[$i]; - if ($line =~ m/^(\C):\s*(.*)/) { + if ($line =~ m/^(.):\s*(.*)/) { my $type = $1; my $value = $2; if ($type eq 'M') { @@ -560,7 +560,7 @@ sub get_maintainers { for ($i = $start; $i < $end; $i++) { my $line = $typevalue[$i]; - if ($line =~ m/^(\C):\s*(.*)/) { + if ($line =~ m/^(.):\s*(.*)/) { my $type = $1; my $value = $2; if ($type eq 'X') { @@ -575,7 +575,7 @@ sub get_maintainers { if (!$exclude) { for ($i = $start; $i < $end; $i++) { my $line = $typevalue[$i]; - if ($line =~ m/^(\C):\s*(.*)/) { + if ($line =~ m/^(.):\s*(.*)/) { my $type = $1; my $value = $2; if ($type eq 'F') { @@ -636,7 +636,7 @@ sub get_maintainers { if ($email) { if (! $interactive) { - $email_git_fallback = 0 if @email_to > 0 || @list_to > 0 || $email_git || $email_git_blame; + $email_git_fallback = 0 if @email_to > 0 || $email_git || $email_git_blame; if ($email_git_fallback) { print STDERR "get_maintainer.pl: No maintainers found, printing recent contributors.\n"; print STDERR "get_maintainer.pl: Do not blindly cc: them on patches! Use common sense.\n"; @@ -855,7 +855,7 @@ sub find_first_section { while ($index < @typevalue) { my $tv = $typevalue[$index]; - if (($tv =~ m/^(\C):\s*(.*)/)) { + if (($tv =~ m/^(.):\s*(.*)/)) { last; } $index++; @@ -869,7 +869,7 @@ sub find_starting_index { while ($index > 0) { my $tv = $typevalue[$index]; - if (!($tv =~ m/^(\C):\s*(.*)/)) { + if (!($tv =~ m/^(.):\s*(.*)/)) { last; } $index--; @@ -883,7 +883,7 @@ sub find_ending_index { while ($index < @typevalue) { my $tv = $typevalue[$index]; - if (!($tv =~ m/^(\C):\s*(.*)/)) { + if (!($tv =~ m/^(.):\s*(.*)/)) { last; } $index++; @@ -909,7 +909,7 @@ sub get_maintainer_role { for ($i = $start + 1; $i < $end; $i++) { my $tv = $typevalue[$i]; - if ($tv =~ m/^(\C):\s*(.*)/) { + if ($tv =~ m/^(.):\s*(.*)/) { my $ptype = $1; my $pvalue = $2; if ($ptype eq "S") { @@ -968,7 +968,7 @@ sub add_categories { for ($i = $start + 1; $i < $end; $i++) { my $tv = $typevalue[$i]; - if ($tv =~ m/^(\C):\s*(.*)/) { + if ($tv =~ m/^(.):\s*(.*)/) { my $ptype = $1; my $pvalue = $2; if ($ptype eq "L") { @@ -1010,7 +1010,7 @@ sub add_categories { if ($name eq "") { if ($i > 0) { my $tv = $typevalue[$i - 1]; - if ($tv =~ m/^(\C):\s*(.*)/) { + if ($tv =~ m/^(.):\s*(.*)/) { if ($1 eq "P") { $name = $2; $pvalue = format_email($name, $address, $email_usename); diff --git a/qemu/scripts/kvm/kvm_stat b/qemu/scripts/kvm/kvm_stat index 7e5d25612..769d884b6 100755 --- a/qemu/scripts/kvm/kvm_stat +++ b/qemu/scripts/kvm/kvm_stat @@ -12,285 +12,312 @@ # the COPYING file in the top-level directory. import curses -import sys, os, time, optparse, ctypes -from ctypes import * - -class DebugfsProvider(object): - def __init__(self): - self.base = '/sys/kernel/debug/kvm' - self._fields = os.listdir(self.base) - def fields(self): - return self._fields - def select(self, fields): - self._fields = fields - def read(self): - def val(key): - return int(file(self.base + '/' + key).read()) - return dict([(key, val(key)) for key in self._fields]) - -vmx_exit_reasons = { - 0: 'EXCEPTION_NMI', - 1: 'EXTERNAL_INTERRUPT', - 2: 'TRIPLE_FAULT', - 7: 'PENDING_INTERRUPT', - 8: 'NMI_WINDOW', - 9: 'TASK_SWITCH', - 10: 'CPUID', - 12: 'HLT', - 14: 'INVLPG', - 15: 'RDPMC', - 16: 'RDTSC', - 18: 'VMCALL', - 19: 'VMCLEAR', - 20: 'VMLAUNCH', - 21: 'VMPTRLD', - 22: 'VMPTRST', - 23: 'VMREAD', - 24: 'VMRESUME', - 25: 'VMWRITE', - 26: 'VMOFF', - 27: 'VMON', - 28: 'CR_ACCESS', - 29: 'DR_ACCESS', - 30: 'IO_INSTRUCTION', - 31: 'MSR_READ', - 32: 'MSR_WRITE', - 33: 'INVALID_STATE', - 36: 'MWAIT_INSTRUCTION', - 39: 'MONITOR_INSTRUCTION', - 40: 'PAUSE_INSTRUCTION', - 41: 'MCE_DURING_VMENTRY', - 43: 'TPR_BELOW_THRESHOLD', - 44: 'APIC_ACCESS', - 48: 'EPT_VIOLATION', - 49: 'EPT_MISCONFIG', - 54: 'WBINVD', - 55: 'XSETBV', - 56: 'APIC_WRITE', - 58: 'INVPCID', +import sys +import os +import time +import optparse +import ctypes +import fcntl +import resource +import struct +import re +from collections import defaultdict +from time import sleep + +VMX_EXIT_REASONS = { + 'EXCEPTION_NMI': 0, + 'EXTERNAL_INTERRUPT': 1, + 'TRIPLE_FAULT': 2, + 'PENDING_INTERRUPT': 7, + 'NMI_WINDOW': 8, + 'TASK_SWITCH': 9, + 'CPUID': 10, + 'HLT': 12, + 'INVLPG': 14, + 'RDPMC': 15, + 'RDTSC': 16, + 'VMCALL': 18, + 'VMCLEAR': 19, + 'VMLAUNCH': 20, + 'VMPTRLD': 21, + 'VMPTRST': 22, + 'VMREAD': 23, + 'VMRESUME': 24, + 'VMWRITE': 25, + 'VMOFF': 26, + 'VMON': 27, + 'CR_ACCESS': 28, + 'DR_ACCESS': 29, + 'IO_INSTRUCTION': 30, + 'MSR_READ': 31, + 'MSR_WRITE': 32, + 'INVALID_STATE': 33, + 'MWAIT_INSTRUCTION': 36, + 'MONITOR_INSTRUCTION': 39, + 'PAUSE_INSTRUCTION': 40, + 'MCE_DURING_VMENTRY': 41, + 'TPR_BELOW_THRESHOLD': 43, + 'APIC_ACCESS': 44, + 'EPT_VIOLATION': 48, + 'EPT_MISCONFIG': 49, + 'WBINVD': 54, + 'XSETBV': 55, + 'APIC_WRITE': 56, + 'INVPCID': 58, } -svm_exit_reasons = { - 0x000: 'READ_CR0', - 0x003: 'READ_CR3', - 0x004: 'READ_CR4', - 0x008: 'READ_CR8', - 0x010: 'WRITE_CR0', - 0x013: 'WRITE_CR3', - 0x014: 'WRITE_CR4', - 0x018: 'WRITE_CR8', - 0x020: 'READ_DR0', - 0x021: 'READ_DR1', - 0x022: 'READ_DR2', - 0x023: 'READ_DR3', - 0x024: 'READ_DR4', - 0x025: 'READ_DR5', - 0x026: 'READ_DR6', - 0x027: 'READ_DR7', - 0x030: 'WRITE_DR0', - 0x031: 'WRITE_DR1', - 0x032: 'WRITE_DR2', - 0x033: 'WRITE_DR3', - 0x034: 'WRITE_DR4', - 0x035: 'WRITE_DR5', - 0x036: 'WRITE_DR6', - 0x037: 'WRITE_DR7', - 0x040: 'EXCP_BASE', - 0x060: 'INTR', - 0x061: 'NMI', - 0x062: 'SMI', - 0x063: 'INIT', - 0x064: 'VINTR', - 0x065: 'CR0_SEL_WRITE', - 0x066: 'IDTR_READ', - 0x067: 'GDTR_READ', - 0x068: 'LDTR_READ', - 0x069: 'TR_READ', - 0x06a: 'IDTR_WRITE', - 0x06b: 'GDTR_WRITE', - 0x06c: 'LDTR_WRITE', - 0x06d: 'TR_WRITE', - 0x06e: 'RDTSC', - 0x06f: 'RDPMC', - 0x070: 'PUSHF', - 0x071: 'POPF', - 0x072: 'CPUID', - 0x073: 'RSM', - 0x074: 'IRET', - 0x075: 'SWINT', - 0x076: 'INVD', - 0x077: 'PAUSE', - 0x078: 'HLT', - 0x079: 'INVLPG', - 0x07a: 'INVLPGA', - 0x07b: 'IOIO', - 0x07c: 'MSR', - 0x07d: 'TASK_SWITCH', - 0x07e: 'FERR_FREEZE', - 0x07f: 'SHUTDOWN', - 0x080: 'VMRUN', - 0x081: 'VMMCALL', - 0x082: 'VMLOAD', - 0x083: 'VMSAVE', - 0x084: 'STGI', - 0x085: 'CLGI', - 0x086: 'SKINIT', - 0x087: 'RDTSCP', - 0x088: 'ICEBP', - 0x089: 'WBINVD', - 0x08a: 'MONITOR', - 0x08b: 'MWAIT', - 0x08c: 'MWAIT_COND', - 0x08d: 'XSETBV', - 0x400: 'NPF', +SVM_EXIT_REASONS = { + 'READ_CR0': 0x000, + 'READ_CR3': 0x003, + 'READ_CR4': 0x004, + 'READ_CR8': 0x008, + 'WRITE_CR0': 0x010, + 'WRITE_CR3': 0x013, + 'WRITE_CR4': 0x014, + 'WRITE_CR8': 0x018, + 'READ_DR0': 0x020, + 'READ_DR1': 0x021, + 'READ_DR2': 0x022, + 'READ_DR3': 0x023, + 'READ_DR4': 0x024, + 'READ_DR5': 0x025, + 'READ_DR6': 0x026, + 'READ_DR7': 0x027, + 'WRITE_DR0': 0x030, + 'WRITE_DR1': 0x031, + 'WRITE_DR2': 0x032, + 'WRITE_DR3': 0x033, + 'WRITE_DR4': 0x034, + 'WRITE_DR5': 0x035, + 'WRITE_DR6': 0x036, + 'WRITE_DR7': 0x037, + 'EXCP_BASE': 0x040, + 'INTR': 0x060, + 'NMI': 0x061, + 'SMI': 0x062, + 'INIT': 0x063, + 'VINTR': 0x064, + 'CR0_SEL_WRITE': 0x065, + 'IDTR_READ': 0x066, + 'GDTR_READ': 0x067, + 'LDTR_READ': 0x068, + 'TR_READ': 0x069, + 'IDTR_WRITE': 0x06a, + 'GDTR_WRITE': 0x06b, + 'LDTR_WRITE': 0x06c, + 'TR_WRITE': 0x06d, + 'RDTSC': 0x06e, + 'RDPMC': 0x06f, + 'PUSHF': 0x070, + 'POPF': 0x071, + 'CPUID': 0x072, + 'RSM': 0x073, + 'IRET': 0x074, + 'SWINT': 0x075, + 'INVD': 0x076, + 'PAUSE': 0x077, + 'HLT': 0x078, + 'INVLPG': 0x079, + 'INVLPGA': 0x07a, + 'IOIO': 0x07b, + 'MSR': 0x07c, + 'TASK_SWITCH': 0x07d, + 'FERR_FREEZE': 0x07e, + 'SHUTDOWN': 0x07f, + 'VMRUN': 0x080, + 'VMMCALL': 0x081, + 'VMLOAD': 0x082, + 'VMSAVE': 0x083, + 'STGI': 0x084, + 'CLGI': 0x085, + 'SKINIT': 0x086, + 'RDTSCP': 0x087, + 'ICEBP': 0x088, + 'WBINVD': 0x089, + 'MONITOR': 0x08a, + 'MWAIT': 0x08b, + 'MWAIT_COND': 0x08c, + 'XSETBV': 0x08d, + 'NPF': 0x400, } # EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h) -aarch64_exit_reasons = { - 0x00: 'UNKNOWN', - 0x01: 'WFI', - 0x03: 'CP15_32', - 0x04: 'CP15_64', - 0x05: 'CP14_MR', - 0x06: 'CP14_LS', - 0x07: 'FP_ASIMD', - 0x08: 'CP10_ID', - 0x0C: 'CP14_64', - 0x0E: 'ILL_ISS', - 0x11: 'SVC32', - 0x12: 'HVC32', - 0x13: 'SMC32', - 0x15: 'SVC64', - 0x16: 'HVC64', - 0x17: 'SMC64', - 0x18: 'SYS64', - 0x20: 'IABT', - 0x21: 'IABT_HYP', - 0x22: 'PC_ALIGN', - 0x24: 'DABT', - 0x25: 'DABT_HYP', - 0x26: 'SP_ALIGN', - 0x28: 'FP_EXC32', - 0x2C: 'FP_EXC64', - 0x2F: 'SERROR', - 0x30: 'BREAKPT', - 0x31: 'BREAKPT_HYP', - 0x32: 'SOFTSTP', - 0x33: 'SOFTSTP_HYP', - 0x34: 'WATCHPT', - 0x35: 'WATCHPT_HYP', - 0x38: 'BKPT32', - 0x3A: 'VECTOR32', - 0x3C: 'BRK64', +AARCH64_EXIT_REASONS = { + 'UNKNOWN': 0x00, + 'WFI': 0x01, + 'CP15_32': 0x03, + 'CP15_64': 0x04, + 'CP14_MR': 0x05, + 'CP14_LS': 0x06, + 'FP_ASIMD': 0x07, + 'CP10_ID': 0x08, + 'CP14_64': 0x0C, + 'ILL_ISS': 0x0E, + 'SVC32': 0x11, + 'HVC32': 0x12, + 'SMC32': 0x13, + 'SVC64': 0x15, + 'HVC64': 0x16, + 'SMC64': 0x17, + 'SYS64': 0x18, + 'IABT': 0x20, + 'IABT_HYP': 0x21, + 'PC_ALIGN': 0x22, + 'DABT': 0x24, + 'DABT_HYP': 0x25, + 'SP_ALIGN': 0x26, + 'FP_EXC32': 0x28, + 'FP_EXC64': 0x2C, + 'SERROR': 0x2F, + 'BREAKPT': 0x30, + 'BREAKPT_HYP': 0x31, + 'SOFTSTP': 0x32, + 'SOFTSTP_HYP': 0x33, + 'WATCHPT': 0x34, + 'WATCHPT_HYP': 0x35, + 'BKPT32': 0x38, + 'VECTOR32': 0x3A, + 'BRK64': 0x3C, } # From include/uapi/linux/kvm.h, KVM_EXIT_xxx -userspace_exit_reasons = { - 0: 'UNKNOWN', - 1: 'EXCEPTION', - 2: 'IO', - 3: 'HYPERCALL', - 4: 'DEBUG', - 5: 'HLT', - 6: 'MMIO', - 7: 'IRQ_WINDOW_OPEN', - 8: 'SHUTDOWN', - 9: 'FAIL_ENTRY', - 10: 'INTR', - 11: 'SET_TPR', - 12: 'TPR_ACCESS', - 13: 'S390_SIEIC', - 14: 'S390_RESET', - 15: 'DCR', - 16: 'NMI', - 17: 'INTERNAL_ERROR', - 18: 'OSI', - 19: 'PAPR_HCALL', - 20: 'S390_UCONTROL', - 21: 'WATCHDOG', - 22: 'S390_TSCH', - 23: 'EPR', - 24: 'SYSTEM_EVENT', +USERSPACE_EXIT_REASONS = { + 'UNKNOWN': 0, + 'EXCEPTION': 1, + 'IO': 2, + 'HYPERCALL': 3, + 'DEBUG': 4, + 'HLT': 5, + 'MMIO': 6, + 'IRQ_WINDOW_OPEN': 7, + 'SHUTDOWN': 8, + 'FAIL_ENTRY': 9, + 'INTR': 10, + 'SET_TPR': 11, + 'TPR_ACCESS': 12, + 'S390_SIEIC': 13, + 'S390_RESET': 14, + 'DCR': 15, + 'NMI': 16, + 'INTERNAL_ERROR': 17, + 'OSI': 18, + 'PAPR_HCALL': 19, + 'S390_UCONTROL': 20, + 'WATCHDOG': 21, + 'S390_TSCH': 22, + 'EPR': 23, + 'SYSTEM_EVENT': 24, } -x86_exit_reasons = { - 'vmx': vmx_exit_reasons, - 'svm': svm_exit_reasons, +IOCTL_NUMBERS = { + 'SET_FILTER': 0x40082406, + 'ENABLE': 0x00002400, + 'DISABLE': 0x00002401, + 'RESET': 0x00002403, } -sc_perf_evt_open = None -exit_reasons = None +class Arch(object): + """Class that encapsulates global architecture specific data like + syscall and ioctl numbers. + + """ + @staticmethod + def get_arch(): + machine = os.uname()[4] + + if machine.startswith('ppc'): + return ArchPPC() + elif machine.startswith('aarch64'): + return ArchA64() + elif machine.startswith('s390'): + return ArchS390() + else: + # X86_64 + for line in open('/proc/cpuinfo'): + if not line.startswith('flags'): + continue + + flags = line.split() + if 'vmx' in flags: + return ArchX86(VMX_EXIT_REASONS) + if 'svm' in flags: + return ArchX86(SVM_EXIT_REASONS) + return + +class ArchX86(Arch): + def __init__(self, exit_reasons): + self.sc_perf_evt_open = 298 + self.ioctl_numbers = IOCTL_NUMBERS + self.exit_reasons = exit_reasons + +class ArchPPC(Arch): + def __init__(self): + self.sc_perf_evt_open = 319 + self.ioctl_numbers = IOCTL_NUMBERS + self.ioctl_numbers['ENABLE'] = 0x20002400 + self.ioctl_numbers['DISABLE'] = 0x20002401 -ioctl_numbers = { - 'SET_FILTER' : 0x40082406, - 'ENABLE' : 0x00002400, - 'DISABLE' : 0x00002401, - 'RESET' : 0x00002403, -} + # PPC comes in 32 and 64 bit and some generated ioctl + # numbers depend on the wordsize. + char_ptr_size = ctypes.sizeof(ctypes.c_char_p) + self.ioctl_numbers['SET_FILTER'] = 0x80002406 | char_ptr_size << 16 + +class ArchA64(Arch): + def __init__(self): + self.sc_perf_evt_open = 241 + self.ioctl_numbers = IOCTL_NUMBERS + self.exit_reasons = AARCH64_EXIT_REASONS + +class ArchS390(Arch): + def __init__(self): + self.sc_perf_evt_open = 331 + self.ioctl_numbers = IOCTL_NUMBERS + self.exit_reasons = None + +ARCH = Arch.get_arch() + + +def walkdir(path): + """Returns os.walk() data for specified directory. + + As it is only a wrapper it returns the same 3-tuple of (dirpath, + dirnames, filenames). + """ + return next(os.walk(path)) + + +def parse_int_list(list_string): + """Returns an int list from a string of comma separated integers and + integer ranges.""" + integers = [] + members = list_string.split(',') -def x86_init(flag): - globals().update({ - 'sc_perf_evt_open' : 298, - 'exit_reasons' : x86_exit_reasons[flag], - }) - -def s390_init(): - globals().update({ - 'sc_perf_evt_open' : 331 - }) - -def ppc_init(): - globals().update({ - 'sc_perf_evt_open' : 319, - 'ioctl_numbers' : { - 'SET_FILTER' : 0x80002406 | (ctypes.sizeof(ctypes.c_char_p) << 16), - 'ENABLE' : 0x20002400, - 'DISABLE' : 0x20002401, - } - }) - -def aarch64_init(): - globals().update({ - 'sc_perf_evt_open' : 241, - 'exit_reasons' : aarch64_exit_reasons, - }) - -def detect_platform(): - if os.uname()[4].startswith('ppc'): - ppc_init() - return - elif os.uname()[4].startswith('aarch64'): - aarch64_init() - return - - for line in file('/proc/cpuinfo').readlines(): - if line.startswith('flags'): - for flag in line.split(): - if flag in x86_exit_reasons: - x86_init(flag) - return - elif line.startswith('vendor_id'): - for flag in line.split(): - if flag == 'IBM/S390': - s390_init() - return - -detect_platform() - -def invert(d): - return dict((x[1], x[0]) for x in d.iteritems()) - -filters = {} -filters['kvm_userspace_exit'] = ('reason', invert(userspace_exit_reasons)) -if exit_reasons: - filters['kvm_exit'] = ('exit_reason', invert(exit_reasons)) - -import struct, array - -libc = ctypes.CDLL('libc.so.6') + for member in members: + if '-' not in member: + integers.append(int(member)) + else: + int_range = member.split('-') + integers.extend(range(int(int_range[0]), + int(int_range[1]) + 1)) + + return integers + + +def get_online_cpus(): + with open('/sys/devices/system/cpu/online') as cpu_list: + cpu_string = cpu_list.readline() + return parse_int_list(cpu_string) + + +def get_filters(): + filters = {} + filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS) + if ARCH.exit_reasons: + filters['kvm_exit'] = ('exit_reason', ARCH.exit_reasons) + return filters + +libc = ctypes.CDLL('libc.so.6', use_errno=True) syscall = libc.syscall -get_errno = libc.__errno_location -get_errno.restype = POINTER(c_int) class perf_event_attr(ctypes.Structure): _fields_ = [('type', ctypes.c_uint32), @@ -305,262 +332,350 @@ class perf_event_attr(ctypes.Structure): ('bp_addr', ctypes.c_uint64), ('bp_len', ctypes.c_uint64), ] -def _perf_event_open(attr, pid, cpu, group_fd, flags): - return syscall(sc_perf_evt_open, ctypes.pointer(attr), ctypes.c_int(pid), - ctypes.c_int(cpu), ctypes.c_int(group_fd), - ctypes.c_long(flags)) - -PERF_TYPE_HARDWARE = 0 -PERF_TYPE_SOFTWARE = 1 -PERF_TYPE_TRACEPOINT = 2 -PERF_TYPE_HW_CACHE = 3 -PERF_TYPE_RAW = 4 -PERF_TYPE_BREAKPOINT = 5 - -PERF_SAMPLE_IP = 1 << 0 -PERF_SAMPLE_TID = 1 << 1 -PERF_SAMPLE_TIME = 1 << 2 -PERF_SAMPLE_ADDR = 1 << 3 -PERF_SAMPLE_READ = 1 << 4 -PERF_SAMPLE_CALLCHAIN = 1 << 5 -PERF_SAMPLE_ID = 1 << 6 -PERF_SAMPLE_CPU = 1 << 7 -PERF_SAMPLE_PERIOD = 1 << 8 -PERF_SAMPLE_STREAM_ID = 1 << 9 -PERF_SAMPLE_RAW = 1 << 10 - -PERF_FORMAT_TOTAL_TIME_ENABLED = 1 << 0 -PERF_FORMAT_TOTAL_TIME_RUNNING = 1 << 1 -PERF_FORMAT_ID = 1 << 2 -PERF_FORMAT_GROUP = 1 << 3 -import re + def __init__(self): + super(self.__class__, self).__init__() + self.type = PERF_TYPE_TRACEPOINT + self.size = ctypes.sizeof(self) + self.read_format = PERF_FORMAT_GROUP + +def perf_event_open(attr, pid, cpu, group_fd, flags): + return syscall(ARCH.sc_perf_evt_open, ctypes.pointer(attr), + ctypes.c_int(pid), ctypes.c_int(cpu), + ctypes.c_int(group_fd), ctypes.c_long(flags)) -sys_tracing = '/sys/kernel/debug/tracing' +PERF_TYPE_TRACEPOINT = 2 +PERF_FORMAT_GROUP = 1 << 3 + +PATH_DEBUGFS_TRACING = '/sys/kernel/debug/tracing' +PATH_DEBUGFS_KVM = '/sys/kernel/debug/kvm' class Group(object): - def __init__(self, cpu): + def __init__(self): self.events = [] - self.group_leader = None - self.cpu = cpu - def add_event(self, name, event_set, tracepoint, filter = None): - self.events.append(Event(group = self, - name = name, event_set = event_set, - tracepoint = tracepoint, filter = filter)) - if len(self.events) == 1: - self.file = os.fdopen(self.events[0].fd) + + def add_event(self, event): + self.events.append(event) + def read(self): - bytes = 8 * (1 + len(self.events)) - fmt = 'xxxxxxxx' + 'q' * len(self.events) + length = 8 * (1 + len(self.events)) + read_format = 'xxxxxxxx' + 'Q' * len(self.events) return dict(zip([event.name for event in self.events], - struct.unpack(fmt, self.file.read(bytes)))) + struct.unpack(read_format, + os.read(self.events[0].fd, length)))) class Event(object): - def __init__(self, group, name, event_set, tracepoint, filter = None): + def __init__(self, name, group, trace_cpu, trace_point, trace_filter, + trace_set='kvm'): self.name = name - attr = perf_event_attr() - attr.type = PERF_TYPE_TRACEPOINT - attr.size = ctypes.sizeof(attr) - id_path = os.path.join(sys_tracing, 'events', event_set, - tracepoint, 'id') - id = int(file(id_path).read()) - attr.config = id - attr.sample_type = (PERF_SAMPLE_RAW - | PERF_SAMPLE_TIME - | PERF_SAMPLE_CPU) - attr.sample_period = 1 - attr.read_format = PERF_FORMAT_GROUP + self.fd = None + self.setup_event(group, trace_cpu, trace_point, trace_filter, + trace_set) + + def setup_event_attribute(self, trace_set, trace_point): + id_path = os.path.join(PATH_DEBUGFS_TRACING, 'events', trace_set, + trace_point, 'id') + + event_attr = perf_event_attr() + event_attr.config = int(open(id_path).read()) + return event_attr + + def setup_event(self, group, trace_cpu, trace_point, trace_filter, + trace_set): + event_attr = self.setup_event_attribute(trace_set, trace_point) + group_leader = -1 if group.events: group_leader = group.events[0].fd - fd = _perf_event_open(attr, -1, group.cpu, group_leader, 0) + + fd = perf_event_open(event_attr, -1, trace_cpu, + group_leader, 0) if fd == -1: - err = get_errno()[0] - raise Exception('perf_event_open failed, errno = ' + err.__str__()) - if filter: - import fcntl - fcntl.ioctl(fd, ioctl_numbers['SET_FILTER'], filter) + err = ctypes.get_errno() + raise OSError(err, os.strerror(err), + 'while calling sys_perf_event_open().') + + if trace_filter: + fcntl.ioctl(fd, ARCH.ioctl_numbers['SET_FILTER'], + trace_filter) + self.fd = fd + def enable(self): - import fcntl - fcntl.ioctl(self.fd, ioctl_numbers['ENABLE'], 0) + fcntl.ioctl(self.fd, ARCH.ioctl_numbers['ENABLE'], 0) + def disable(self): - import fcntl - fcntl.ioctl(self.fd, ioctl_numbers['DISABLE'], 0) + fcntl.ioctl(self.fd, ARCH.ioctl_numbers['DISABLE'], 0) + def reset(self): - import fcntl - fcntl.ioctl(self.fd, ioctl_numbers['RESET'], 0) + fcntl.ioctl(self.fd, ARCH.ioctl_numbers['RESET'], 0) class TracepointProvider(object): def __init__(self): - path = os.path.join(sys_tracing, 'events', 'kvm') - fields = [f - for f in os.listdir(path) - if os.path.isdir(os.path.join(path, f))] + self.group_leaders = [] + self.filters = get_filters() + self._fields = self.get_available_fields() + self.setup_traces() + self.fields = self._fields + + def get_available_fields(self): + path = os.path.join(PATH_DEBUGFS_TRACING, 'events', 'kvm') + fields = walkdir(path)[1] extra = [] - for f in fields: - if f in filters: - subfield, values = filters[f] - for name, number in values.iteritems(): - extra.append(f + '(' + name + ')') + for field in fields: + if field in self.filters: + filter_name_, filter_dicts = self.filters[field] + for name in filter_dicts: + extra.append(field + '(' + name + ')') fields += extra - self._setup(fields) - self.select(fields) - def fields(self): - return self._fields + return fields + + def setup_traces(self): + cpus = get_online_cpus() + + # The constant is needed as a buffer for python libs, std + # streams and other files that the script opens. + newlim = len(cpus) * len(self._fields) + 50 + try: + softlim_, hardlim = resource.getrlimit(resource.RLIMIT_NOFILE) + + if hardlim < newlim: + # Now we need CAP_SYS_RESOURCE, to increase the hard limit. + resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, newlim)) + else: + # Raising the soft limit is sufficient. + resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, hardlim)) + + except ValueError: + sys.exit("NOFILE rlimit could not be raised to {0}".format(newlim)) - def _online_cpus(self): - l = [] - pattern = r'cpu([0-9]+)' - basedir = '/sys/devices/system/cpu' - for entry in os.listdir(basedir): - match = re.match(pattern, entry) - if not match: - continue - path = os.path.join(basedir, entry, 'online') - if os.path.exists(path) and open(path).read().strip() != '1': - continue - l.append(int(match.group(1))) - return l - - def _setup(self, _fields): - self._fields = _fields - cpus = self._online_cpus() - import resource - nfiles = len(cpus) * 1000 - resource.setrlimit(resource.RLIMIT_NOFILE, (nfiles, nfiles)) - events = [] - self.group_leaders = [] for cpu in cpus: - group = Group(cpu) - for name in _fields: + group = Group() + for name in self._fields: tracepoint = name - filter = None - m = re.match(r'(.*)\((.*)\)', name) - if m: - tracepoint, sub = m.groups() - filter = '%s==%d\0' % (filters[tracepoint][0], - filters[tracepoint][1][sub]) - event = group.add_event(name, event_set = 'kvm', - tracepoint = tracepoint, - filter = filter) + tracefilter = None + match = re.match(r'(.*)\((.*)\)', name) + if match: + tracepoint, sub = match.groups() + tracefilter = ('%s==%d\0' % + (self.filters[tracepoint][0], + self.filters[tracepoint][1][sub])) + + group.add_event(Event(name=name, + group=group, + trace_cpu=cpu, + trace_point=tracepoint, + trace_filter=tracefilter)) self.group_leaders.append(group) - def select(self, fields): + + def available_fields(self): + return self.get_available_fields() + + @property + def fields(self): + return self._fields + + @fields.setter + def fields(self, fields): + self._fields = fields for group in self.group_leaders: - for event in group.events: + for index, event in enumerate(group.events): if event.name in fields: event.reset() event.enable() else: - event.disable() + # Do not disable the group leader. + # It would disable all of its events. + if index != 0: + event.disable() + def read(self): - from collections import defaultdict ret = defaultdict(int) for group in self.group_leaders: for name, val in group.read().iteritems(): - ret[name] += val + if name in self._fields: + ret[name] += val return ret -class Stats: - def __init__(self, providers, fields = None): +class DebugfsProvider(object): + def __init__(self): + self._fields = self.get_available_fields() + + def get_available_fields(self): + return walkdir(PATH_DEBUGFS_KVM)[2] + + @property + def fields(self): + return self._fields + + @fields.setter + def fields(self, fields): + self._fields = fields + + def read(self): + def val(key): + return int(file(PATH_DEBUGFS_KVM + '/' + key).read()) + return dict([(key, val(key)) for key in self._fields]) + +class Stats(object): + def __init__(self, providers, fields=None): self.providers = providers - self.fields_filter = fields - self._update() - def _update(self): + self._fields_filter = fields + self.values = {} + self.update_provider_filters() + + def update_provider_filters(self): def wanted(key): - import re - if not self.fields_filter: + if not self._fields_filter: return True - return re.match(self.fields_filter, key) is not None - self.values = dict() - for d in providers: - provider_fields = [key for key in d.fields() if wanted(key)] - for key in provider_fields: - self.values[key] = None - d.select(provider_fields) - def set_fields_filter(self, fields_filter): - self.fields_filter = fields_filter - self._update() + return re.match(self._fields_filter, key) is not None + + # As we reset the counters when updating the fields we can + # also clear the cache of old values. + self.values = {} + for provider in self.providers: + provider_fields = [key for key in provider.get_available_fields() + if wanted(key)] + provider.fields = provider_fields + + @property + def fields_filter(self): + return self._fields_filter + + @fields_filter.setter + def fields_filter(self, fields_filter): + self._fields_filter = fields_filter + self.update_provider_filters() + def get(self): - for d in providers: - new = d.read() - for key in d.fields(): + for provider in self.providers: + new = provider.read() + for key in provider.fields: oldval = self.values.get(key, (0, 0)) - newval = new[key] + newval = new.get(key, 0) newdelta = None if oldval is not None: newdelta = newval - oldval[0] self.values[key] = (newval, newdelta) return self.values -if not os.access('/sys/kernel/debug', os.F_OK): - print 'Please enable CONFIG_DEBUG_FS in your kernel' - sys.exit(1) -if not os.access('/sys/kernel/debug/kvm', os.F_OK): - print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')" - print "and ensure the kvm modules are loaded" - sys.exit(1) - -label_width = 40 -number_width = 10 - -def tui(screen, stats): - curses.use_default_colors() - curses.noecho() - drilldown = False - fields_filter = stats.fields_filter - def update_drilldown(): - if not fields_filter: - if drilldown: - stats.set_fields_filter(None) - else: - stats.set_fields_filter(r'^[^\(]*$') - update_drilldown() - def refresh(sleeptime): - screen.erase() - screen.addstr(0, 0, 'kvm statistics') - screen.addstr(2, 1, 'Event') - screen.addstr(2, 1 + label_width + number_width - len('Total'), 'Total') - screen.addstr(2, 1 + label_width + number_width + 8 - len('Current'), 'Current') +LABEL_WIDTH = 40 +NUMBER_WIDTH = 10 + +class Tui(object): + def __init__(self, stats): + self.stats = stats + self.screen = None + self.drilldown = False + self.update_drilldown() + + def __enter__(self): + """Initialises curses for later use. Based on curses.wrapper + implementation from the Python standard library.""" + self.screen = curses.initscr() + curses.noecho() + curses.cbreak() + + # The try/catch works around a minor bit of + # over-conscientiousness in the curses module, the error + # return from C start_color() is ignorable. + try: + curses.start_color() + except: + pass + + curses.use_default_colors() + return self + + def __exit__(self, *exception): + """Resets the terminal to its normal state. Based on curses.wrappre + implementation from the Python standard library.""" + if self.screen: + self.screen.keypad(0) + curses.echo() + curses.nocbreak() + curses.endwin() + + def update_drilldown(self): + if not self.stats.fields_filter: + self.stats.fields_filter = r'^[^\(]*$' + + elif self.stats.fields_filter == r'^[^\(]*$': + self.stats.fields_filter = None + + def refresh(self, sleeptime): + self.screen.erase() + self.screen.addstr(0, 0, 'kvm statistics - summary', curses.A_BOLD) + self.screen.addstr(2, 1, 'Event') + self.screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH - + len('Total'), 'Total') + self.screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH + 8 - + len('Current'), 'Current') row = 3 - s = stats.get() + stats = self.stats.get() def sortkey(x): - if s[x][1]: - return (-s[x][1], -s[x][0]) + if stats[x][1]: + return (-stats[x][1], -stats[x][0]) else: - return (0, -s[x][0]) - for key in sorted(s.keys(), key = sortkey): - if row >= screen.getmaxyx()[0]: + return (0, -stats[x][0]) + for key in sorted(stats.keys(), key=sortkey): + + if row >= self.screen.getmaxyx()[0]: break - values = s[key] + values = stats[key] if not values[0] and not values[1]: break col = 1 - screen.addstr(row, col, key) - col += label_width - screen.addstr(row, col, '%10d' % (values[0],)) - col += number_width + self.screen.addstr(row, col, key) + col += LABEL_WIDTH + self.screen.addstr(row, col, '%10d' % (values[0],)) + col += NUMBER_WIDTH if values[1] is not None: - screen.addstr(row, col, '%8d' % (values[1] / sleeptime,)) + self.screen.addstr(row, col, '%8d' % (values[1] / sleeptime,)) row += 1 - screen.refresh() + self.screen.refresh() + + def show_filter_selection(self): + while True: + self.screen.erase() + self.screen.addstr(0, 0, + "Show statistics for events matching a regex.", + curses.A_BOLD) + self.screen.addstr(2, 0, + "Current regex: {0}" + .format(self.stats.fields_filter)) + self.screen.addstr(3, 0, "New regex: ") + curses.echo() + regex = self.screen.getstr() + curses.noecho() + if len(regex) == 0: + return + try: + re.compile(regex) + self.stats.fields_filter = regex + return + except re.error: + continue - sleeptime = 0.25 - while True: - refresh(sleeptime) - curses.halfdelay(int(sleeptime * 10)) - sleeptime = 3 - try: - c = screen.getkey() - if c == 'x': - drilldown = not drilldown - update_drilldown() - if c == 'q': + def show_stats(self): + sleeptime = 0.25 + while True: + self.refresh(sleeptime) + curses.halfdelay(int(sleeptime * 10)) + sleeptime = 3 + try: + char = self.screen.getkey() + if char == 'x': + self.drilldown = not self.drilldown + self.update_drilldown() + if char == 'q': + break + if char == 'f': + self.show_filter_selection() + except KeyboardInterrupt: break - except KeyboardInterrupt: - break - except curses.error: - continue + except curses.error: + continue def batch(stats): s = stats.get() @@ -568,13 +683,13 @@ def batch(stats): s = stats.get() for key in sorted(s.keys()): values = s[key] - print '%-22s%10d%10d' % (key, values[0], values[1]) + print '%-42s%10d%10d' % (key, values[0], values[1]) def log(stats): keys = sorted(stats.get().iterkeys()) def banner(): for k in keys: - print '%10s' % k[0:9], + print '%s' % k, print def statline(): s = stats.get() @@ -590,57 +705,121 @@ def log(stats): statline() line += 1 -options = optparse.OptionParser() -options.add_option('-1', '--once', '--batch', - action = 'store_true', - default = False, - dest = 'once', - help = 'run in batch mode for one second', - ) -options.add_option('-l', '--log', - action = 'store_true', - default = False, - dest = 'log', - help = 'run in logging mode (like vmstat)', - ) -options.add_option('-t', '--tracepoints', - action = 'store_true', - default = False, - dest = 'tracepoints', - help = 'retrieve statistics from tracepoints', - ) -options.add_option('-d', '--debugfs', - action = 'store_true', - default = False, - dest = 'debugfs', - help = 'retrieve statistics from debugfs', - ) -options.add_option('-f', '--fields', - action = 'store', - default = None, - dest = 'fields', - help = 'fields to display (regex)', - ) -(options, args) = options.parse_args(sys.argv) - -providers = [] -if options.tracepoints: - providers.append(TracepointProvider()) -if options.debugfs: - providers.append(DebugfsProvider()) - -if len(providers) == 0: - try: - providers = [TracepointProvider()] - except: - providers = [DebugfsProvider()] - -stats = Stats(providers, fields = options.fields) - -if options.log: - log(stats) -elif not options.once: - import curses.wrapper - curses.wrapper(tui, stats) -else: - batch(stats) +def get_options(): + description_text = """ +This script displays various statistics about VMs running under KVM. +The statistics are gathered from the KVM debugfs entries and / or the +currently available perf traces. + +The monitoring takes additional cpu cycles and might affect the VM's +performance. + +Requirements: +- Access to: + /sys/kernel/debug/kvm + /sys/kernel/debug/trace/events/* + /proc/pid/task +- /proc/sys/kernel/perf_event_paranoid < 1 if user has no + CAP_SYS_ADMIN and perf events are used. +- CAP_SYS_RESOURCE if the hard limit is not high enough to allow + the large number of files that are possibly opened. +""" + + class PlainHelpFormatter(optparse.IndentedHelpFormatter): + def format_description(self, description): + if description: + return description + "\n" + else: + return "" + + optparser = optparse.OptionParser(description=description_text, + formatter=PlainHelpFormatter()) + optparser.add_option('-1', '--once', '--batch', + action='store_true', + default=False, + dest='once', + help='run in batch mode for one second', + ) + optparser.add_option('-l', '--log', + action='store_true', + default=False, + dest='log', + help='run in logging mode (like vmstat)', + ) + optparser.add_option('-t', '--tracepoints', + action='store_true', + default=False, + dest='tracepoints', + help='retrieve statistics from tracepoints', + ) + optparser.add_option('-d', '--debugfs', + action='store_true', + default=False, + dest='debugfs', + help='retrieve statistics from debugfs', + ) + optparser.add_option('-f', '--fields', + action='store', + default=None, + dest='fields', + help='fields to display (regex)', + ) + (options, _) = optparser.parse_args(sys.argv) + return options + +def get_providers(options): + providers = [] + + if options.tracepoints: + providers.append(TracepointProvider()) + if options.debugfs: + providers.append(DebugfsProvider()) + if len(providers) == 0: + providers.append(TracepointProvider()) + + return providers + +def check_access(options): + if not os.path.exists('/sys/kernel/debug'): + sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.') + sys.exit(1) + + if not os.path.exists(PATH_DEBUGFS_KVM): + sys.stderr.write("Please make sure, that debugfs is mounted and " + "readable by the current user:\n" + "('mount -t debugfs debugfs /sys/kernel/debug')\n" + "Also ensure, that the kvm modules are loaded.\n") + sys.exit(1) + + if not os.path.exists(PATH_DEBUGFS_TRACING) and (options.tracepoints + or not options.debugfs): + sys.stderr.write("Please enable CONFIG_TRACING in your kernel " + "when using the option -t (default).\n" + "If it is enabled, make {0} readable by the " + "current user.\n" + .format(PATH_DEBUGFS_TRACING)) + if options.tracepoints: + sys.exit(1) + + sys.stderr.write("Falling back to debugfs statistics!\n") + options.debugfs = True + sleep(5) + + return options + +def main(): + options = get_options() + options = check_access(options) + providers = get_providers(options) + stats = Stats(providers, fields=options.fields) + + if options.log: + log(stats) + elif not options.once: + with Tui(stats) as tui: + tui.show_stats() + else: + batch(stats) + +if __name__ == "__main__": + main() diff --git a/qemu/scripts/ordereddict.py b/qemu/scripts/ordereddict.py index 7242b5060..2d1d81370 100644 --- a/qemu/scripts/ordereddict.py +++ b/qemu/scripts/ordereddict.py @@ -22,6 +22,7 @@ from UserDict import DictMixin + class OrderedDict(dict, DictMixin): def __init__(self, *args, **kwds): @@ -117,7 +118,7 @@ class OrderedDict(dict, DictMixin): if isinstance(other, OrderedDict): if len(self) != len(other): return False - for p, q in zip(self.items(), other.items()): + for p, q in zip(self.items(), other.items()): if p != q: return False return True diff --git a/qemu/scripts/qapi-commands.py b/qemu/scripts/qapi-commands.py index ca22acc1d..b570069fa 100644 --- a/qemu/scripts/qapi-commands.py +++ b/qemu/scripts/qapi-commands.py @@ -2,7 +2,7 @@ # QAPI command marshaller generator # # Copyright IBM, Corp. 2011 -# Copyright (C) 2014-2015 Red Hat, Inc. +# Copyright (C) 2014-2016 Red Hat, Inc. # # Authors: # Anthony Liguori @@ -12,274 +12,217 @@ # This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. -from ordereddict import OrderedDict from qapi import * import re -def generate_command_decl(name, args, ret_type): - arglist="" - for argname, argtype, optional in parse_args(args): - argtype = c_type(argtype, is_param=True) - if optional: - arglist += "bool has_%s, " % c_name(argname) - arglist += "%s %s, " % (argtype, c_name(argname)) + +def gen_command_decl(name, arg_type, ret_type): return mcgen(''' -%(ret_type)s qmp_%(name)s(%(args)sError **errp); +%(c_type)s qmp_%(c_name)s(%(params)s); ''', - ret_type=c_type(ret_type), name=c_name(name), - args=arglist).strip() - -def gen_err_check(errvar): - if errvar: - return mcgen(''' -if (local_err) { - goto out; -} -''') - return '' + c_type=(ret_type and ret_type.c_type()) or 'void', + c_name=c_name(name), + params=gen_params(arg_type, 'Error **errp')) -def gen_sync_call(name, args, ret_type, indent=0): - ret = "" - arglist="" - retval="" - if ret_type: - retval = "retval = " - for argname, argtype, optional in parse_args(args): - if optional: - arglist += "has_%s, " % c_name(argname) - arglist += "%s, " % (c_name(argname)) - push_indent(indent) - ret = mcgen(''' -%(retval)sqmp_%(name)s(%(args)s&local_err); -''', - name=c_name(name), args=arglist, retval=retval).rstrip() - if ret_type: - ret += "\n" + gen_err_check('local_err') - ret += "\n" + mcgen('''' -%(marshal_output_call)s -''', - marshal_output_call=gen_marshal_output_call(name, ret_type)).rstrip() - pop_indent(indent) - return ret.rstrip() +def gen_call(name, arg_type, ret_type): + ret = '' + argstr = '' + if arg_type: + assert not arg_type.variants + for memb in arg_type.members: + if memb.optional: + argstr += 'arg.has_%s, ' % c_name(memb.name) + argstr += 'arg.%s, ' % c_name(memb.name) -def gen_marshal_output_call(name, ret_type): - if not ret_type: - return "" - return "qmp_marshal_output_%s(retval, ret, &local_err);" % c_name(name) + lhs = '' + if ret_type: + lhs = 'retval = ' -def gen_visitor_input_containers_decl(args, obj): - ret = "" + ret = mcgen(''' - push_indent() - if len(args) > 0: - ret += mcgen(''' -QmpInputVisitor *mi = qmp_input_visitor_new_strict(%(obj)s); -QapiDeallocVisitor *md; -Visitor *v; -''', - obj=obj) - pop_indent() - - return ret.rstrip() - -def gen_visitor_input_vars_decl(args): - ret = "" - push_indent() - for argname, argtype, optional in parse_args(args): - if optional: - ret += mcgen(''' -bool has_%(argname)s = false; -''', - argname=c_name(argname)) - if is_c_ptr(argtype): - ret += mcgen(''' -%(argtype)s %(argname)s = NULL; + %(lhs)sqmp_%(c_name)s(%(args)s&err); ''', - argname=c_name(argname), argtype=c_type(argtype)) - else: - ret += mcgen(''' -%(argtype)s %(argname)s = {0}; -''', - argname=c_name(argname), argtype=c_type(argtype)) - - pop_indent() - return ret.rstrip() - -def gen_visitor_input_block(args, dealloc=False): - ret = "" - errparg = '&local_err' - errarg = 'local_err' - - if len(args) == 0: - return ret - - push_indent() - - if dealloc: - errparg = 'NULL' - errarg = None; - ret += mcgen(''' -qmp_input_visitor_cleanup(mi); -md = qapi_dealloc_visitor_new(); -v = qapi_dealloc_get_visitor(md); -''') - else: + c_name=c_name(name), args=argstr, lhs=lhs) + if ret_type: + ret += gen_err_check() ret += mcgen(''' -v = qmp_input_get_visitor(mi); -''') - for argname, argtype, optional in parse_args(args): - if optional: - ret += mcgen(''' -visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s); + qmp_marshal_output_%(c_name)s(retval, ret, &err); ''', - c_name=c_name(argname), name=argname, errp=errparg) - ret += gen_err_check(errarg) - ret += mcgen(''' -if (has_%(c_name)s) { -''', - c_name=c_name(argname)) - push_indent() - ret += mcgen(''' -visit_type_%(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s); -''', - c_name=c_name(argname), name=argname, argtype=argtype, - visitor=type_name(argtype), errp=errparg) - ret += gen_err_check(errarg) - if optional: - pop_indent() - ret += mcgen(''' -} -''') + c_name=ret_type.c_name()) + return ret - if dealloc: - ret += mcgen(''' -qapi_dealloc_visitor_cleanup(md); -''') - pop_indent() - return ret.rstrip() -def gen_marshal_output(name, args, ret_type, middle_mode): - if not ret_type: - return "" +def gen_marshal_output(ret_type): + return mcgen(''' - ret = mcgen(''' -static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp) +static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp) { - Error *local_err = NULL; - QmpOutputVisitor *mo = qmp_output_visitor_new(); - QapiDeallocVisitor *md; + Error *err = NULL; + QmpOutputVisitor *qov = qmp_output_visitor_new(); + QapiDeallocVisitor *qdv; Visitor *v; - v = qmp_output_get_visitor(mo); - visit_type_%(visitor)s(v, &ret_in, "unused", &local_err); - if (local_err) { + v = qmp_output_get_visitor(qov); + visit_type_%(c_name)s(v, "unused", &ret_in, &err); + if (err) { goto out; } - *ret_out = qmp_output_get_qobject(mo); + *ret_out = qmp_output_get_qobject(qov); out: - error_propagate(errp, local_err); - qmp_output_visitor_cleanup(mo); - md = qapi_dealloc_visitor_new(); - v = qapi_dealloc_get_visitor(md); - visit_type_%(visitor)s(v, &ret_in, "unused", NULL); - qapi_dealloc_visitor_cleanup(md); + error_propagate(errp, err); + qmp_output_visitor_cleanup(qov); + qdv = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(qdv); + visit_type_%(c_name)s(v, "unused", &ret_in, NULL); + qapi_dealloc_visitor_cleanup(qdv); } ''', - c_ret_type=c_type(ret_type), c_name=c_name(name), - visitor=type_name(ret_type)) + c_type=ret_type.c_type(), c_name=ret_type.c_name()) - return ret -def gen_marshal_input_decl(name, args, ret_type, middle_mode): - ret = 'void qmp_marshal_input_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name) +def gen_marshal_proto(name): + ret = 'void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name) if not middle_mode: - ret = "static " + ret + ret = 'static ' + ret return ret -def gen_marshal_input(name, args, ret_type, middle_mode): - hdr = gen_marshal_input_decl(name, args, ret_type, middle_mode) +def gen_marshal_decl(name): + return mcgen(''' +%(proto)s; +''', + proto=gen_marshal_proto(name)) + + +def gen_marshal(name, arg_type, ret_type): ret = mcgen(''' -%(header)s + +%(proto)s { - Error *local_err = NULL; + Error *err = NULL; ''', - header=hdr) + proto=gen_marshal_proto(name)) if ret_type: - if is_c_ptr(ret_type): - retval = " %s retval = NULL;" % c_type(ret_type) - else: - retval = " %s retval;" % c_type(ret_type) ret += mcgen(''' -%(retval)s + %(c_type)s retval; ''', - retval=retval) + c_type=ret_type.c_type()) - if len(args) > 0: + if arg_type and arg_type.members: ret += mcgen(''' -%(visitor_input_containers_decl)s -%(visitor_input_vars_decl)s - -%(visitor_input_block)s + QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args)); + QapiDeallocVisitor *qdv; + Visitor *v; + %(c_name)s arg = {0}; + v = qmp_input_get_visitor(qiv); + visit_type_%(c_name)s_members(v, &arg, &err); + if (err) { + goto out; + } ''', - visitor_input_containers_decl=gen_visitor_input_containers_decl(args, "QOBJECT(args)"), - visitor_input_vars_decl=gen_visitor_input_vars_decl(args), - visitor_input_block=gen_visitor_input_block(args)) + c_name=arg_type.c_name()) + else: ret += mcgen(''' (void)args; ''') - ret += mcgen(''' -%(sync_call)s -''', - sync_call=gen_sync_call(name, args, ret_type, indent=4)) - if re.search('^ *goto out\\;', ret, re.MULTILINE): + ret += gen_call(name, arg_type, ret_type) + + # 'goto out' produced above for arg_type, and by gen_call() for ret_type + if (arg_type and arg_type.members) or ret_type: ret += mcgen(''' out: ''') ret += mcgen(''' - error_propagate(errp, local_err); -%(visitor_input_block_cleanup)s -} + error_propagate(errp, err); +''') + if arg_type and arg_type.members: + ret += mcgen(''' + qmp_input_visitor_cleanup(qiv); + qdv = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(qdv); + visit_type_%(c_name)s_members(v, &arg, NULL); + qapi_dealloc_visitor_cleanup(qdv); ''', - visitor_input_block_cleanup=gen_visitor_input_block(args, - dealloc=True)) + c_name=arg_type.c_name()) + + ret += mcgen(''' +} +''') return ret -def gen_registry(commands): - registry="" - push_indent() - for cmd in commands: - options = 'QCO_NO_OPTIONS' - if not cmd.get('success-response', True): - options = 'QCO_NO_SUCCESS_RESP' - registry += mcgen(''' -qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s); +def gen_register_command(name, success_response): + options = 'QCO_NO_OPTIONS' + if not success_response: + options = 'QCO_NO_SUCCESS_RESP' + + ret = mcgen(''' + qmp_register_command("%(name)s", qmp_marshal_%(c_name)s, %(opts)s); ''', - name=cmd['command'], c_name=c_name(cmd['command']), - opts=options) - pop_indent() + name=name, c_name=c_name(name), + opts=options) + return ret + + +def gen_registry(registry): ret = mcgen(''' + static void qmp_init_marshal(void) { -%(registry)s +''') + ret += registry + ret += mcgen(''' } qapi_init(qmp_init_marshal); -''', - registry=registry.rstrip()) +''') return ret + +class QAPISchemaGenCommandVisitor(QAPISchemaVisitor): + def __init__(self): + self.decl = None + self.defn = None + self._regy = None + self._visited_ret_types = None + + def visit_begin(self, schema): + self.decl = '' + self.defn = '' + self._regy = '' + self._visited_ret_types = set() + + def visit_end(self): + if not middle_mode: + self.defn += gen_registry(self._regy) + self._regy = None + self._visited_ret_types = None + + def visit_command(self, name, info, arg_type, ret_type, + gen, success_response): + if not gen: + return + self.decl += gen_command_decl(name, arg_type, ret_type) + if ret_type and ret_type not in self._visited_ret_types: + self._visited_ret_types.add(ret_type) + self.defn += gen_marshal_output(ret_type) + if middle_mode: + self.decl += gen_marshal_decl(name) + self.defn += gen_marshal(name, arg_type, ret_type) + if not middle_mode: + self._regy += gen_register_command(name, success_response) + + middle_mode = False (input_file, output_dir, do_c, do_h, prefix, opts) = \ @@ -289,10 +232,6 @@ for o, a in opts: if o in ("-m", "--middle"): middle_mode = True -exprs = parse_schema(input_file) -commands = filter(lambda expr: expr.has_key('command'), exprs) -commands = filter(lambda expr: not expr.has_key('gen'), commands) - c_comment = ''' /* * schema-defined QMP->QAPI command dispatch @@ -327,6 +266,7 @@ h_comment = ''' c_comment, h_comment) fdef.write(mcgen(''' +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/module.h" #include "qapi/qmp/types.h" @@ -340,7 +280,7 @@ fdef.write(mcgen(''' #include "%(prefix)sqmp-commands.h" ''', - prefix=prefix)) + prefix=prefix)) fdecl.write(mcgen(''' #include "%(prefix)sqapi-types.h" @@ -348,29 +288,12 @@ fdecl.write(mcgen(''' #include "qapi/error.h" ''', - prefix=prefix)) - -for cmd in commands: - arglist = [] - ret_type = None - if cmd.has_key('data'): - arglist = cmd['data'] - if cmd.has_key('returns'): - ret_type = cmd['returns'] - ret = generate_command_decl(cmd['command'], arglist, ret_type) + "\n" - fdecl.write(ret) - if ret_type: - ret = gen_marshal_output(cmd['command'], arglist, ret_type, middle_mode) + "\n" - fdef.write(ret) - - if middle_mode: - fdecl.write('%s;\n' % gen_marshal_input_decl(cmd['command'], arglist, ret_type, middle_mode)) - - ret = gen_marshal_input(cmd['command'], arglist, ret_type, middle_mode) + "\n" - fdef.write(ret) + prefix=prefix)) -if not middle_mode: - ret = gen_registry(commands) - fdef.write(ret) +schema = QAPISchema(input_file) +gen = QAPISchemaGenCommandVisitor() +schema.visit(gen) +fdef.write(gen.defn) +fdecl.write(gen.decl) close_output(fdef, fdecl) diff --git a/qemu/scripts/qapi-event.py b/qemu/scripts/qapi-event.py index 56bc602a6..9b5c5b535 100644 --- a/qemu/scripts/qapi-event.py +++ b/qemu/scripts/qapi-event.py @@ -2,215 +2,151 @@ # QAPI event generator # # Copyright (c) 2014 Wenchao Xia +# Copyright (c) 2015-2016 Red Hat Inc. # # Authors: # Wenchao Xia +# Markus Armbruster # # This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. -from ordereddict import OrderedDict from qapi import * -def _generate_event_api_name(event_name, params): - api_name = "void qapi_event_send_%s(" % c_name(event_name).lower(); - l = len(api_name) - if params: - for argname, argentry, optional in parse_args(params): - if optional: - api_name += "bool has_%s,\n" % c_name(argname) - api_name += "".ljust(l) +def gen_event_send_proto(name, arg_type): + return 'void qapi_event_send_%(c_name)s(%(param)s)' % { + 'c_name': c_name(name.lower()), + 'param': gen_params(arg_type, 'Error **errp')} - api_name += "%s %s,\n" % (c_type(argentry, is_param=True), - c_name(argname)) - api_name += "".ljust(l) - - api_name += "Error **errp)" - return api_name; +def gen_event_send_decl(name, arg_type): + return mcgen(''' -# Following are the core functions that generate C APIs to emit event. +%(proto)s; +''', + proto=gen_event_send_proto(name, arg_type)) -def generate_event_declaration(api_name): - return mcgen(''' -%(api_name)s; +# Declare and initialize an object 'qapi' using parameters from gen_params() +def gen_param_var(typ): + assert not typ.variants + ret = mcgen(''' + %(c_name)s param = { ''', - api_name = api_name) + c_name=typ.c_name()) + sep = ' ' + for memb in typ.members: + ret += sep + sep = ', ' + if memb.optional: + ret += 'has_' + c_name(memb.name) + sep + if memb.type.name == 'str': + # Cast away const added in gen_params() + ret += '(char *)' + ret += c_name(memb.name) + ret += mcgen(''' + + }; +''') + return ret -def generate_event_implement(api_name, event_name, params): - # step 1: declare any variables - ret = mcgen(""" -%(api_name)s +def gen_event_send(name, arg_type): + # FIXME: Our declaration of local variables (and of 'errp' in the + # parameter list) can collide with exploded members of the event's + # data type passed in as parameters. If this collision ever hits in + # practice, we can rename our local variables with a leading _ prefix, + # or split the code into a wrapper function that creates a boxed + # 'param' object then calls another to do the real work. + ret = mcgen(''' + +%(proto)s { QDict *qmp; - Error *local_err = NULL; + Error *err = NULL; QMPEventFuncEmit emit; -""", - api_name = api_name) +''', + proto=gen_event_send_proto(name, arg_type)) - if params: - ret += mcgen(""" + if arg_type and arg_type.members: + ret += mcgen(''' QmpOutputVisitor *qov; Visitor *v; - QObject *obj; +''') + ret += gen_param_var(arg_type) -""") + ret += mcgen(''' - # step 2: check emit function, create a dict - ret += mcgen(""" emit = qmp_event_get_func_emit(); if (!emit) { return; } - qmp = qmp_event_build_dict("%(event_name)s"); + qmp = qmp_event_build_dict("%(name)s"); -""", - event_name = event_name) +''', + name=name) - # step 3: visit the params if params != None - if params: - ret += mcgen(""" + if arg_type and arg_type.members: + ret += mcgen(''' qov = qmp_output_visitor_new(); - g_assert(qov); - v = qmp_output_get_visitor(qov); - g_assert(v); - /* Fake visit, as if all members are under a structure */ - visit_start_struct(v, NULL, "", "%(event_name)s", 0, &local_err); - if (local_err) { - goto clean; - } - -""", - event_name = event_name) - - for argname, argentry, optional in parse_args(params): - if optional: - ret += mcgen(""" - if (has_%(var)s) { -""", - var = c_name(argname)) - push_indent() - - if argentry == "str": - var_type = "(char **)" - else: - var_type = "" - - ret += mcgen(""" - visit_type_%(type)s(v, %(var_type)s&%(var)s, "%(name)s", &local_err); - if (local_err) { - goto clean; - } -""", - var_type = var_type, - var = c_name(argname), - type = type_name(argentry), - name = argname) - - if optional: - pop_indent() - ret += mcgen(""" + visit_start_struct(v, "%(name)s", NULL, 0, &err); + if (err) { + goto out; } -""") - - ret += mcgen(""" - - visit_end_struct(v, &local_err); - if (local_err) { - goto clean; + visit_type_%(c_name)s_members(v, ¶m, &err); + visit_end_struct(v, err ? NULL : &err); + if (err) { + goto out; } - obj = qmp_output_get_qobject(qov); - g_assert(obj != NULL); - - qdict_put_obj(qmp, "data", obj); -""") + qdict_put_obj(qmp, "data", qmp_output_get_qobject(qov)); +''', + name=name, c_name=arg_type.c_name()) - # step 4: call qmp event api - ret += mcgen(""" - emit(%(event_enum_value)s, qmp, &local_err); + ret += mcgen(''' + emit(%(c_enum)s, qmp, &err); -""", - event_enum_value = event_enum_value) +''', + c_enum=c_enum_const(event_enum_name, name)) - # step 5: clean up - if params: - ret += mcgen(""" - clean: + if arg_type and arg_type.members: + ret += mcgen(''' +out: qmp_output_visitor_cleanup(qov); -""") - ret += mcgen(""" - error_propagate(errp, local_err); +''') + ret += mcgen(''' + error_propagate(errp, err); QDECREF(qmp); } -""") - +''') return ret -# Following are the functions that generate an enum type for all defined -# events, similar to qapi-types.py. Here we already have enum name and -# values which were generated before and recorded in event_enum_*. It also -# works around the issue that "import qapi-types" can't work. - -def generate_event_enum_decl(event_enum_name, event_enum_values): - lookup_decl = mcgen(''' - -extern const char *%(event_enum_name)s_lookup[]; -''', - event_enum_name = event_enum_name) - - enum_decl = mcgen(''' -typedef enum %(event_enum_name)s -{ -''', - event_enum_name = event_enum_name) - - # append automatically generated _MAX value - enum_max_value = c_enum_const(event_enum_name, "MAX") - enum_values = event_enum_values + [ enum_max_value ] - - i = 0 - for value in enum_values: - enum_decl += mcgen(''' - %(value)s = %(i)d, -''', - value = value, - i = i) - i += 1 - - enum_decl += mcgen(''' -} %(event_enum_name)s; -''', - event_enum_name = event_enum_name) - - return lookup_decl + enum_decl +class QAPISchemaGenEventVisitor(QAPISchemaVisitor): + def __init__(self): + self.decl = None + self.defn = None + self._event_names = None -def generate_event_enum_lookup(event_enum_name, event_enum_strings): - ret = mcgen(''' + def visit_begin(self, schema): + self.decl = '' + self.defn = '' + self._event_names = [] -const char *%(event_enum_name)s_lookup[] = { -''', - event_enum_name = event_enum_name) + def visit_end(self): + self.decl += gen_enum(event_enum_name, self._event_names) + self.defn += gen_enum_lookup(event_enum_name, self._event_names) + self._event_names = None - i = 0 - for string in event_enum_strings: - ret += mcgen(''' - "%(string)s", -''', - string = string) + def visit_event(self, name, info, arg_type): + self.decl += gen_event_send_decl(name, arg_type) + self.defn += gen_event_send(name, arg_type) + self._event_names.append(name) - ret += mcgen(''' - NULL, -}; -''') - return ret (input_file, output_dir, do_c, do_h, prefix, dummy) = parse_command_line() @@ -248,6 +184,7 @@ h_comment = ''' c_comment, h_comment) fdef.write(mcgen(''' +#include "qemu/osdep.h" #include "qemu-common.h" #include "%(prefix)sqapi-event.h" #include "%(prefix)sqapi-visit.h" @@ -265,35 +202,12 @@ fdecl.write(mcgen(''' ''', prefix=prefix)) -exprs = parse_schema(input_file) - -event_enum_name = prefix.upper().replace('-', '_') + "QAPIEvent" -event_enum_values = [] -event_enum_strings = [] - -for expr in exprs: - if expr.has_key('event'): - event_name = expr['event'] - params = expr.get('data') - if params and len(params) == 0: - params = None - - api_name = _generate_event_api_name(event_name, params) - ret = generate_event_declaration(api_name) - fdecl.write(ret) - - # We need an enum value per event - event_enum_value = c_enum_const(event_enum_name, event_name) - ret = generate_event_implement(api_name, event_name, params) - fdef.write(ret) - - # Record it, and generate enum later - event_enum_values.append(event_enum_value) - event_enum_strings.append(event_name) +event_enum_name = c_name(prefix + "QAPIEvent", protect=False) -ret = generate_event_enum_decl(event_enum_name, event_enum_values) -fdecl.write(ret) -ret = generate_event_enum_lookup(event_enum_name, event_enum_strings) -fdef.write(ret) +schema = QAPISchema(input_file) +gen = QAPISchemaGenEventVisitor() +schema.visit(gen) +fdef.write(gen.defn) +fdecl.write(gen.decl) close_output(fdef, fdecl) diff --git a/qemu/scripts/qapi-introspect.py b/qemu/scripts/qapi-introspect.py new file mode 100644 index 000000000..e0f926be0 --- /dev/null +++ b/qemu/scripts/qapi-introspect.py @@ -0,0 +1,219 @@ +# +# QAPI introspection generator +# +# Copyright (C) 2015-2016 Red Hat, Inc. +# +# Authors: +# Markus Armbruster +# +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. + +from qapi 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): + if obj is None: + ret = 'null' + elif isinstance(obj, str): + ret = '"' + obj.replace('"', r'\"') + '"' + elif isinstance(obj, list): + elts = [to_json(elt, level + 1) + for elt in obj] + ret = '[' + ', '.join(elts) + ']' + 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) + '}' + else: + assert False # not implemented + if level == 1: + ret = '\n' + ret + return ret + + +def to_c_string(string): + return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"' + + +class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor): + def __init__(self, unmask): + self._unmask = unmask + self.defn = None + self.decl = None + self._schema = None + self._jsons = None + self._used_types = None + self._name_map = None + + def visit_begin(self, schema): + self._schema = schema + self._jsons = [] + self._used_types = [] + self._name_map = {} + + def visit_end(self): + # visit the types that are actually used + jsons = self._jsons + self._jsons = [] + for typ in self._used_types: + typ.visit(self) + # generate C + # TODO can generate awfully long lines + jsons.extend(self._jsons) + name = prefix + 'qmp_schema_json' + self.decl = mcgen(''' +extern const char %(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.defn = mcgen(''' +const char %(c_name)s[] = %(c_string)s; +''', + c_name=c_name(name), + c_string=c_string) + self._schema = None + self._jsons = None + self._used_types = None + self._name_map = None + + def visit_needed(self, entity): + # Ignore types on first pass; visit_end() will pick up used types + return not isinstance(entity, QAPISchemaType) + + def _name(self, name): + if self._unmask: + return name + if name not in self._name_map: + self._name_map[name] = '%d' % len(self._name_map) + return self._name_map[name] + + def _use_type(self, typ): + # Map the various integer types to plain int + if typ.json_type() == 'int': + typ = self._schema.lookup_type('int') + elif (isinstance(typ, QAPISchemaArrayType) and + typ.element_type.json_type() == 'int'): + typ = self._schema.lookup_type('intList') + # Add type to work queue if new + if typ not in self._used_types: + self._used_types.append(typ) + # Clients should examine commands and events, not types. Hide + # type names to reduce the temptation. Also saves a few + # characters. + if isinstance(typ, QAPISchemaBuiltinType): + return typ.name + if isinstance(typ, QAPISchemaArrayType): + return '[' + self._use_type(typ.element_type) + ']' + return self._name(typ.name) + + def _gen_json(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) + + def _gen_member(self, member): + ret = {'name': member.name, 'type': self._use_type(member.type)} + if member.optional: + ret['default'] = None + return ret + + def _gen_variants(self, tag_name, variants): + return {'tag': tag_name, + 'variants': [self._gen_variant(v) for v in variants]} + + def _gen_variant(self, variant): + 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}) + + def visit_enum_type(self, name, info, values, prefix): + self._gen_json(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}) + + 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) + + def visit_alternate_type(self, name, info, variants): + self._gen_json(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): + 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', + {'arg-type': self._use_type(arg_type), + 'ret-type': self._use_type(ret_type)}) + + def visit_event(self, name, info, arg_type): + arg_type = arg_type or self._schema.the_empty_object_type + self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)}) + +# Debugging aid: unmask QAPI schema's type names +# We normally mask them, because they're not QMP wire ABI +opt_unmask = False + +(input_file, output_dir, do_c, do_h, prefix, opts) = \ + parse_command_line("u", ["unmask-non-abi-names"]) + +for o, a in opts: + if o in ("-u", "--unmask-non-abi-names"): + opt_unmask = True + +c_comment = ''' +/* + * QAPI/QMP schema introspection + * + * Copyright (C) 2015 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ +''' +h_comment = ''' +/* + * QAPI/QMP schema introspection + * + * Copyright (C) 2015 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ +''' + +(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix, + 'qmp-introspect.c', 'qmp-introspect.h', + c_comment, h_comment) + +fdef.write(mcgen(''' +#include "qemu/osdep.h" +#include "%(prefix)sqmp-introspect.h" + +''', + prefix=prefix)) + +schema = QAPISchema(input_file) +gen = QAPISchemaGenIntrospectVisitor(opt_unmask) +schema.visit(gen) +fdef.write(gen.defn) +fdecl.write(gen.decl) + +close_output(fdef, fdecl) diff --git a/qemu/scripts/qapi-types.py b/qemu/scripts/qapi-types.py index e6eb4b613..437cf6c8e 100644 --- a/qemu/scripts/qapi-types.py +++ b/qemu/scripts/qapi-types.py @@ -2,271 +2,247 @@ # QAPI types generator # # Copyright IBM, Corp. 2011 +# Copyright (c) 2013-2016 Red Hat Inc. # # Authors: # Anthony Liguori +# Markus Armbruster # # This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. -from ordereddict import OrderedDict from qapi import * -def generate_fwd_builtin(name): - return mcgen(''' -typedef struct %(name)sList -{ - union { - %(type)s value; - uint64_t padding; - }; - struct %(name)sList *next; -} %(name)sList; -''', - type=c_type(name), - name=name) +# variants must be emitted before their container; track what has already +# been output +objects_seen = set() -def generate_fwd_struct(name): - return mcgen(''' -typedef struct %(name)s %(name)s; +def gen_fwd_object_or_array(name): + return mcgen(''' -typedef struct %(name)sList -{ - union { - %(name)s *value; - uint64_t padding; - }; - struct %(name)sList *next; -} %(name)sList; +typedef struct %(c_name)s %(c_name)s; ''', - name=c_name(name)) + c_name=c_name(name)) + -def generate_fwd_enum_struct(name): +def gen_array(name, element_type): return mcgen(''' -typedef struct %(name)sList -{ - union { - %(name)s value; - uint64_t padding; - }; - struct %(name)sList *next; -} %(name)sList; + +struct %(c_name)s { + %(c_name)s *next; + %(c_type)s value; +}; ''', - name=c_name(name)) + c_name=c_name(name), c_type=element_type.c_type()) -def generate_struct_fields(members): - ret = '' - for argname, argentry, optional in parse_args(members): - if optional: +def gen_struct_members(members): + ret = '' + for memb in members: + if memb.optional: ret += mcgen(''' bool has_%(c_name)s; ''', - c_name=c_name(argname)) + c_name=c_name(memb.name)) ret += mcgen(''' %(c_type)s %(c_name)s; ''', - c_type=c_type(argentry), c_name=c_name(argname)) - + c_type=memb.type.c_type(), c_name=c_name(memb.name)) return ret -def generate_struct(expr): - structname = expr.get('struct', "") - members = expr['data'] - base = expr.get('base') +def gen_object(name, base, members, variants): + if name in objects_seen: + return '' + objects_seen.add(name) - ret = mcgen(''' -struct %(name)s -{ -''', - name=c_name(structname)) + ret = '' + if variants: + for v in variants.variants: + if isinstance(v.type, QAPISchemaObjectType): + ret += gen_object(v.type.name, v.type.base, + v.type.local_members, v.type.variants) - if base: - ret += generate_struct_fields({'base': base}) + ret += mcgen(''' - ret += generate_struct_fields(members) +struct %(c_name)s { +''', + c_name=c_name(name)) - # Make sure that all structs have at least one field; this avoids - # potential issues with attempting to malloc space for zero-length structs - # in C, and also incompatibility with C++ (where an empty struct is size 1). - if not base and not members: + if base: + if not base.is_implicit(): ret += mcgen(''' - char qapi_dummy_field_for_empty_struct; -''') - - ret += mcgen(''' -}; + /* Members inherited from %(c_name)s: */ +''', + c_name=base.c_name()) + ret += gen_struct_members(base.members) + if not base.is_implicit(): + ret += mcgen(''' + /* Own members: */ ''') + ret += gen_struct_members(members) - return ret + if variants: + ret += gen_variants(variants) -def generate_enum_lookup(name, values): - ret = mcgen(''' -const char * const %(name)s_lookup[] = { -''', - name=c_name(name)) - i = 0 - for value in values: - index = c_enum_const(name, value) + # Make sure that all structs have at least one member; this avoids + # potential issues with attempting to malloc space for zero-length + # structs in C, and also incompatibility with C++ (where an empty + # struct is size 1). + if not (base and base.members) and not members and not variants: ret += mcgen(''' - [%(index)s] = "%(value)s", -''', - index = index, value = value) + char qapi_dummy_for_empty_struct; +''') - max_index = c_enum_const(name, 'MAX') ret += mcgen(''' - [%(max_index)s] = NULL, }; +''') -''', - max_index=max_index) return ret -def generate_enum(name, values): - name = c_name(name) - lookup_decl = mcgen(''' -extern const char * const %(name)s_lookup[]; -''', - name=name) - - enum_decl = mcgen(''' -typedef enum %(name)s -{ -''', - name=name) - # append automatically generated _MAX value - enum_values = values + [ 'MAX' ] - - i = 0 - for value in enum_values: - enum_full_value = c_enum_const(name, value) - enum_decl += mcgen(''' - %(enum_full_value)s = %(i)d, -''', - enum_full_value = enum_full_value, - i=i) - i += 1 +def gen_upcast(name, base): + # C makes const-correctness ugly. We have to cast away const to let + # this function work for both const and non-const obj. + return mcgen(''' - enum_decl += mcgen(''' -} %(name)s; +static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj) +{ + return (%(base)s *)obj; +} ''', - name=name) + c_name=c_name(name), base=base.c_name()) - return lookup_decl + enum_decl - -def generate_alternate_qtypes(expr): - - name = expr['alternate'] - members = expr['data'] +def gen_variants(variants): ret = mcgen(''' -const int %(name)s_qtypes[QTYPE_MAX] = { + union { /* union tag is @%(c_name)s */ ''', - name=c_name(name)) - - for key in members: - qtype = find_alternate_member_qtype(members[key]) - assert qtype, "Invalid alternate member" + c_name=c_name(variants.tag_member.name)) + for var in variants.variants: ret += mcgen(''' - [%(qtype)s] = %(enum_const)s, + %(c_type)s %(c_name)s; ''', - qtype = qtype, - enum_const = c_enum_const(name + 'Kind', key)) + c_type=var.type.c_unboxed_type(), + c_name=c_name(var.name)) ret += mcgen(''' -}; + } u; ''') - return ret - - -def generate_union(expr, meta): - name = c_name(expr[meta]) - typeinfo = expr['data'] - - base = expr.get('base') - discriminator = expr.get('discriminator') + return ret - enum_define = discriminator_find_enum_define(expr) - if enum_define: - discriminator_type_name = enum_define['enum_name'] - else: - discriminator_type_name = '%sKind' % (name) +def gen_type_cleanup_decl(name): ret = mcgen(''' -struct %(name)s -{ - %(discriminator_type_name)s kind; - union { - void *data; -''', - name=name, - discriminator_type_name=c_name(discriminator_type_name)) - for key in typeinfo: - ret += mcgen(''' - %(c_type)s %(c_name)s; +void qapi_free_%(c_name)s(%(c_name)s *obj); ''', - c_type=c_type(typeinfo[key]), - c_name=c_name(key)) - - ret += mcgen(''' - }; -''') - - if base: - assert discriminator - base_fields = find_struct(base)['data'].copy() - del base_fields[discriminator] - ret += generate_struct_fields(base_fields) - else: - assert not discriminator - - ret += mcgen(''' -}; -''') - if meta == 'alternate': - ret += mcgen(''' -extern const int %(name)s_qtypes[]; -''', - name=name) - - + c_name=c_name(name)) return ret -def generate_type_cleanup_decl(name): - ret = mcgen(''' -void qapi_free_%(name)s(%(c_type)s obj); -''', - c_type=c_type(name), name=c_name(name)) - return ret -def generate_type_cleanup(name): +def gen_type_cleanup(name): ret = mcgen(''' -void qapi_free_%(name)s(%(c_type)s obj) +void qapi_free_%(c_name)s(%(c_name)s *obj) { - QapiDeallocVisitor *md; + QapiDeallocVisitor *qdv; Visitor *v; if (!obj) { return; } - md = qapi_dealloc_visitor_new(); - v = qapi_dealloc_get_visitor(md); - visit_type_%(name)s(v, &obj, NULL, NULL); - qapi_dealloc_visitor_cleanup(md); + qdv = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(qdv); + visit_type_%(c_name)s(v, NULL, &obj, NULL); + qapi_dealloc_visitor_cleanup(qdv); } ''', - c_type=c_type(name), name=c_name(name)) + c_name=c_name(name)) return ret + +class QAPISchemaGenTypeVisitor(QAPISchemaVisitor): + def __init__(self): + self.decl = None + self.defn = None + self._fwdecl = None + self._btin = None + + def visit_begin(self, schema): + # gen_object() is recursive, ensure it doesn't visit the empty type + objects_seen.add(schema.the_empty_object_type.name) + self.decl = '' + self.defn = '' + self._fwdecl = '' + self._btin = guardstart('QAPI_TYPES_BUILTIN') + + def visit_end(self): + self.decl = self._fwdecl + self.decl + self._fwdecl = None + # To avoid header dependency hell, we always generate + # declarations for built-in types in our header files and + # simply guard them. See also do_builtins (command line + # option -b). + self._btin += guardend('QAPI_TYPES_BUILTIN') + self.decl = self._btin + self.decl + self._btin = None + + def _gen_type_cleanup(self, name): + self.decl += gen_type_cleanup_decl(name) + self.defn += gen_type_cleanup(name) + + def visit_enum_type(self, name, info, values, prefix): + # Special case for our lone builtin enum type + # TODO use something cleaner than existence of info + if not info: + self._btin += gen_enum(name, values, prefix) + if do_builtins: + self.defn += gen_enum_lookup(name, values, prefix) + else: + self._fwdecl += gen_enum(name, values, prefix) + self.defn += gen_enum_lookup(name, values, prefix) + + def visit_array_type(self, name, info, element_type): + if isinstance(element_type, QAPISchemaBuiltinType): + self._btin += gen_fwd_object_or_array(name) + self._btin += gen_array(name, element_type) + self._btin += gen_type_cleanup_decl(name) + if do_builtins: + self.defn += gen_type_cleanup(name) + else: + self._fwdecl += gen_fwd_object_or_array(name) + self.decl += gen_array(name, element_type) + self._gen_type_cleanup(name) + + def visit_object_type(self, name, info, base, members, variants): + # Nothing to do for the special empty builtin + if name == 'q_empty': + return + self._fwdecl += gen_fwd_object_or_array(name) + self.decl += gen_object(name, base, members, variants) + if base and not base.is_implicit(): + self.decl += gen_upcast(name, base) + # TODO Worth changing the visitor signature, so we could + # directly use rather than repeat type.is_implicit()? + if not name.startswith('q_'): + # implicit types won't be directly allocated/freed + self._gen_type_cleanup(name) + + def visit_alternate_type(self, name, info, variants): + self._fwdecl += gen_fwd_object_or_array(name) + self.decl += gen_object(name, None, [variants.tag_member], variants) + self._gen_type_cleanup(name) + +# If you link code generated from multiple schemata, you want only one +# instance of the code for built-in types. Generate it only when +# do_builtins, enabled by command line option -b. See also +# QAPISchemaGenTypeVisitor.visit_end(). do_builtins = False (input_file, output_dir, do_c, do_h, prefix, opts) = \ @@ -311,92 +287,17 @@ h_comment = ''' c_comment, h_comment) fdef.write(mcgen(''' +#include "qemu/osdep.h" #include "qapi/dealloc-visitor.h" #include "%(prefix)sqapi-types.h" #include "%(prefix)sqapi-visit.h" - ''', prefix=prefix)) -fdecl.write(mcgen(''' -#include -#include - -''')) - -exprs = parse_schema(input_file) - -fdecl.write(guardstart("QAPI_TYPES_BUILTIN_STRUCT_DECL")) -for typename in builtin_types.keys(): - fdecl.write(generate_fwd_builtin(typename)) -fdecl.write(guardend("QAPI_TYPES_BUILTIN_STRUCT_DECL")) - -for expr in exprs: - ret = "\n" - if expr.has_key('struct'): - ret += generate_fwd_struct(expr['struct']) - elif expr.has_key('enum'): - ret += generate_enum(expr['enum'], expr['data']) + "\n" - ret += generate_fwd_enum_struct(expr['enum']) - fdef.write(generate_enum_lookup(expr['enum'], expr['data'])) - elif expr.has_key('union'): - ret += generate_fwd_struct(expr['union']) + "\n" - enum_define = discriminator_find_enum_define(expr) - if not enum_define: - ret += generate_enum('%sKind' % expr['union'], expr['data'].keys()) - fdef.write(generate_enum_lookup('%sKind' % expr['union'], - expr['data'].keys())) - elif expr.has_key('alternate'): - ret += generate_fwd_struct(expr['alternate']) + "\n" - ret += generate_enum('%sKind' % expr['alternate'], expr['data'].keys()) - fdef.write(generate_enum_lookup('%sKind' % expr['alternate'], - expr['data'].keys())) - fdef.write(generate_alternate_qtypes(expr)) - else: - continue - fdecl.write(ret) - -# to avoid header dependency hell, we always generate declarations -# for built-in types in our header files and simply guard them -fdecl.write(guardstart("QAPI_TYPES_BUILTIN_CLEANUP_DECL")) -for typename in builtin_types.keys(): - fdecl.write(generate_type_cleanup_decl(typename + "List")) -fdecl.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DECL")) - -# ...this doesn't work for cases where we link in multiple objects that -# have the functions defined, so we use -b option to provide control -# over these cases -if do_builtins: - fdef.write(guardstart("QAPI_TYPES_BUILTIN_CLEANUP_DEF")) - for typename in builtin_types.keys(): - fdef.write(generate_type_cleanup(typename + "List")) - fdef.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DEF")) - -for expr in exprs: - ret = "\n" - if expr.has_key('struct'): - ret += generate_struct(expr) + "\n" - ret += generate_type_cleanup_decl(expr['struct'] + "List") - fdef.write(generate_type_cleanup(expr['struct'] + "List") + "\n") - ret += generate_type_cleanup_decl(expr['struct']) - fdef.write(generate_type_cleanup(expr['struct']) + "\n") - elif expr.has_key('union'): - ret += generate_union(expr, 'union') - ret += generate_type_cleanup_decl(expr['union'] + "List") - fdef.write(generate_type_cleanup(expr['union'] + "List") + "\n") - ret += generate_type_cleanup_decl(expr['union']) - fdef.write(generate_type_cleanup(expr['union']) + "\n") - elif expr.has_key('alternate'): - ret += generate_union(expr, 'alternate') - ret += generate_type_cleanup_decl(expr['alternate'] + "List") - fdef.write(generate_type_cleanup(expr['alternate'] + "List") + "\n") - ret += generate_type_cleanup_decl(expr['alternate']) - fdef.write(generate_type_cleanup(expr['alternate']) + "\n") - elif expr.has_key('enum'): - ret += generate_type_cleanup_decl(expr['enum'] + "List") - fdef.write(generate_type_cleanup(expr['enum'] + "List") + "\n") - else: - continue - fdecl.write(ret) +schema = QAPISchema(input_file) +gen = QAPISchemaGenTypeVisitor() +schema.visit(gen) +fdef.write(gen.defn) +fdecl.write(gen.decl) close_output(fdef, fdecl) diff --git a/qemu/scripts/qapi-visit.py b/qemu/scripts/qapi-visit.py index 5b9933648..31d233035 100644 --- a/qemu/scripts/qapi-visit.py +++ b/qemu/scripts/qapi-visit.py @@ -2,7 +2,7 @@ # QAPI visitor generator # # Copyright IBM, Corp. 2011 -# Copyright (C) 2014-2015 Red Hat, Inc. +# Copyright (C) 2014-2016 Red Hat, Inc. # # Authors: # Anthony Liguori @@ -12,86 +12,90 @@ # This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. -from ordereddict import OrderedDict from qapi import * import re -implicit_structs = [] -def generate_visit_implicit_struct(type): - global implicit_structs - if type in implicit_structs: - return '' - implicit_structs.append(type) +def gen_visit_decl(name, scalar=False): + c_type = c_name(name) + ' *' + if not scalar: + c_type += '*' return mcgen(''' +void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_type)sobj, Error **errp); +''', + c_name=c_name(name), c_type=c_type) -static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error **errp) -{ - Error *err = NULL; - visit_start_implicit_struct(m, (void **)obj, sizeof(%(c_type)s), &err); - if (!err) { - visit_type_%(c_type)s_fields(m, obj, errp); - visit_end_implicit_struct(m, &err); - } - error_propagate(errp, err); -} +def gen_visit_members_decl(name): + return mcgen(''' + +void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp); ''', - c_type=type_name(type)) + c_name=c_name(name)) -def generate_visit_struct_fields(name, members, base = None): - substructs = [] - ret = '' - if base: - ret += generate_visit_implicit_struct(base) - - ret += mcgen(''' +def gen_visit_object_members(name, base, members, variants): + ret = mcgen(''' -static void visit_type_%(name)s_fields(Visitor *m, %(name)s **obj, Error **errp) +void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) { Error *err = NULL; + ''', - name=c_name(name)) - push_indent() + c_name=c_name(name)) if base: ret += mcgen(''' -visit_type_implicit_%(type)s(m, &(*obj)->%(c_name)s, &err); -if (err) { - goto out; -} + visit_type_%(c_type)s_members(v, (%(c_type)s *)obj, &err); ''', - type=type_name(base), c_name=c_name('base')) + c_type=base.c_name()) + ret += gen_err_check() - for argname, argentry, optional in parse_args(members): - if optional: + for memb in members: + if memb.optional: ret += mcgen(''' -visit_optional(m, &(*obj)->has_%(c_name)s, "%(name)s", &err); -if (!err && (*obj)->has_%(c_name)s) { + if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) { ''', - c_name=c_name(argname), name=argname) + name=memb.name, c_name=c_name(memb.name)) push_indent() - ret += mcgen(''' -visit_type_%(type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err); + visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, &err); ''', - type=type_name(argentry), c_name=c_name(argname), - name=argname) - - if optional: + c_type=memb.type.c_name(), name=memb.name, + c_name=c_name(memb.name)) + ret += gen_err_check() + if memb.optional: pop_indent() ret += mcgen(''' -} + } ''') + + if variants: ret += mcgen(''' -if (err) { - goto out; -} + switch (obj->%(c_name)s) { +''', + c_name=c_name(variants.tag_member.name)) + + for var in variants.variants: + ret += mcgen(''' + case %(case)s: + visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, &err); + break; +''', + case=c_enum_const(variants.tag_member.type.name, + var.name, + variants.tag_member.type.prefix), + c_type=var.type.c_name(), c_name=c_name(var.name)) + + ret += mcgen(''' + default: + abort(); + } ''') - pop_indent() - if re.search('^ *goto out\\;', ret, re.MULTILINE): + # 'goto out' produced for base, for each member, and if variants were + # present + if base or members or variants: ret += mcgen(''' out: @@ -103,275 +107,209 @@ out: return ret -def generate_visit_struct_body(name, members): - ret = mcgen(''' - Error *err = NULL; - - visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err); - if (!err) { - if (*obj) { - visit_type_%(c_name)s_fields(m, obj, errp); - } - visit_end_struct(m, &err); - } - error_propagate(errp, err); -''', - name=name, c_name=c_name(name)) - - return ret - -def generate_visit_struct(expr): - - name = expr['struct'] - members = expr['data'] - base = expr.get('base') - - ret = generate_visit_struct_fields(name, members, base) - - ret += mcgen(''' - -void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp) -{ -''', - name=c_name(name)) - - ret += generate_visit_struct_body(name, members) - - ret += mcgen(''' -} -''') - return ret - -def generate_visit_list(name, members): +def gen_visit_list(name, element_type): + # FIXME: if *obj is NULL on entry, and the first visit_next_list() + # assigns to *obj, while a later one fails, we should clean up *obj + # rather than leaving it non-NULL. As currently written, the caller must + # call qapi_free_FOOList() to avoid a memory leak of the partial FOOList. return mcgen(''' -void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp) +void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp) { Error *err = NULL; GenericList *i, **prev; - visit_start_list(m, name, &err); + visit_start_list(v, name, &err); if (err) { goto out; } for (prev = (GenericList **)obj; - !err && (i = visit_next_list(m, prev, &err)) != NULL; + !err && (i = visit_next_list(v, prev, sizeof(**obj))) != NULL; prev = &i) { - %(name)sList *native_i = (%(name)sList *)i; - visit_type_%(name)s(m, &native_i->value, NULL, &err); + %(c_name)s *native_i = (%(c_name)s *)i; + visit_type_%(c_elt_type)s(v, NULL, &native_i->value, &err); } - error_propagate(errp, err); - err = NULL; - visit_end_list(m, &err); + visit_end_list(v); out: error_propagate(errp, err); } ''', - name=type_name(name)) + c_name=c_name(name), c_elt_type=element_type.c_name()) -def generate_visit_enum(name, members): + +def gen_visit_enum(name): return mcgen(''' -void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp) +void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s *obj, Error **errp) { - visit_type_enum(m, (int *)obj, %(name)s_lookup, "%(name)s", name, errp); + int value = *obj; + visit_type_enum(v, name, &value, %(c_name)s_lookup, errp); + *obj = value; } ''', - name=c_name(name)) - -def generate_visit_alternate(name, members): - ret = mcgen(''' - -void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp) -{ - Error *err = NULL; - - visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err); - if (err) { - goto out; - } - visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err); - if (err) { - goto out_end; - } - switch ((*obj)->kind) { -''', - name=c_name(name)) - - # For alternate, always use the default enum type automatically generated - # as name + 'Kind' - disc_type = c_name(name) + 'Kind' - - for key in members: - assert (members[key] in builtin_types.keys() - or find_struct(members[key]) - or find_union(members[key]) - or find_enum(members[key])), "Invalid alternate member" - - enum_full_value = c_enum_const(disc_type, key) - ret += mcgen(''' - case %(enum_full_value)s: - visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err); - break; -''', - enum_full_value = enum_full_value, - c_type = type_name(members[key]), - c_name = c_name(key)) - - ret += mcgen(''' - default: - abort(); - } -out_end: - error_propagate(errp, err); - err = NULL; - visit_end_implicit_struct(m, &err); -out: - error_propagate(errp, err); -} -''') - - return ret - - -def generate_visit_union(expr): - - name = expr['union'] - members = expr['data'] - - base = expr.get('base') - discriminator = expr.get('discriminator') - - enum_define = discriminator_find_enum_define(expr) - if enum_define: - # Use the enum type as discriminator - ret = "" - disc_type = c_name(enum_define['enum_name']) - else: - # There will always be a discriminator in the C switch code, by default - # it is an enum type generated silently - ret = generate_visit_enum(name + 'Kind', members.keys()) - disc_type = c_name(name) + 'Kind' + c_name=c_name(name)) - if base: - assert discriminator - base_fields = find_struct(base)['data'].copy() - del base_fields[discriminator] - ret += generate_visit_struct_fields(name, base_fields) - if discriminator: - for key in members: - ret += generate_visit_implicit_struct(members[key]) +def gen_visit_alternate(name, variants): + promote_int = 'true' + ret = '' + for var in variants.variants: + if var.type.alternate_qtype() == 'QTYPE_QINT': + promote_int = 'false' ret += mcgen(''' -void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp) +void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp) { Error *err = NULL; - visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); + visit_start_alternate(v, name, (GenericAlternate **)obj, sizeof(**obj), + %(promote_int)s, &err); if (err) { goto out; } - if (*obj) { + switch ((*obj)->type) { ''', - name=c_name(name)) + c_name=c_name(name), promote_int=promote_int) - if base: + for var in variants.variants: ret += mcgen(''' - visit_type_%(name)s_fields(m, obj, &err); - if (err) { - goto out_obj; - } + case %(case)s: ''', - name=c_name(name)) - - if not discriminator: - disc_key = "type" - else: - disc_key = discriminator - ret += mcgen(''' - visit_type_%(disc_type)s(m, &(*obj)->kind, "%(disc_key)s", &err); + case=var.type.alternate_qtype()) + if isinstance(var.type, QAPISchemaObjectType): + ret += mcgen(''' + visit_start_struct(v, name, NULL, 0, &err); if (err) { - goto out_obj; - } - if (!visit_start_union(m, !!(*obj)->data, &err) || err) { - goto out_obj; + break; } - switch ((*obj)->kind) { + visit_type_%(c_type)s_members(v, &(*obj)->u.%(c_name)s, &err); + error_propagate(errp, err); + err = NULL; + visit_end_struct(v, &err); ''', - disc_type = disc_type, - disc_key = disc_key) - - for key in members: - if not discriminator: - fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);' + c_type=var.type.c_name(), + c_name=c_name(var.name)) else: - fmt = 'visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);' - - enum_full_value = c_enum_const(disc_type, key) - ret += mcgen(''' - case %(enum_full_value)s: - ''' + fmt + ''' - break; + ret += mcgen(''' + visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, &err); ''', - enum_full_value = enum_full_value, - c_type=type_name(members[key]), - c_name=c_name(key)) + c_type=var.type.c_name(), + c_name=c_name(var.name)) + ret += mcgen(''' + break; +''') ret += mcgen(''' - default: - abort(); - } -out_obj: - error_propagate(errp, err); - err = NULL; - visit_end_union(m, !!(*obj)->data, &err); - error_propagate(errp, err); - err = NULL; + default: + error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", + "%(name)s"); } - visit_end_struct(m, &err); + visit_end_alternate(v); out: error_propagate(errp, err); } -''') - - return ret - -def generate_declaration(name, members, builtin_type=False): - ret = "" - if not builtin_type: - name = c_name(name) - ret += mcgen(''' - -void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp); -''', - name=name) - - ret += mcgen(''' -void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp); ''', name=name) return ret -def generate_enum_declaration(name, members): - ret = mcgen(''' -void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp); -''', - name=c_name(name)) - return ret - -def generate_decl_enum(name, members): +def gen_visit_object(name, base, members, variants): + # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to + # *obj, but then visit_type_FOO_members() fails, we should clean up *obj + # rather than leaving it non-NULL. As currently written, the caller must + # call qapi_free_FOO() to avoid a memory leak of the partial FOO. return mcgen(''' -void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp); -''', - name=c_name(name)) +void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp) +{ + Error *err = NULL; + visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err); + if (err) { + goto out; + } + if (!*obj) { + goto out_obj; + } + visit_type_%(c_name)s_members(v, *obj, &err); + error_propagate(errp, err); + err = NULL; +out_obj: + visit_end_struct(v, &err); +out: + error_propagate(errp, err); +} +''', + c_name=c_name(name)) + + +class QAPISchemaGenVisitVisitor(QAPISchemaVisitor): + def __init__(self): + self.decl = None + self.defn = None + self._btin = None + + def visit_begin(self, schema): + self.decl = '' + self.defn = '' + self._btin = guardstart('QAPI_VISIT_BUILTIN') + + def visit_end(self): + # To avoid header dependency hell, we always generate + # declarations for built-in types in our header files and + # simply guard them. See also do_builtins (command line + # option -b). + self._btin += guardend('QAPI_VISIT_BUILTIN') + self.decl = self._btin + self.decl + self._btin = None + + def visit_enum_type(self, name, info, values, prefix): + # Special case for our lone builtin enum type + # TODO use something cleaner than existence of info + if not info: + self._btin += gen_visit_decl(name, scalar=True) + if do_builtins: + self.defn += gen_visit_enum(name) + else: + self.decl += gen_visit_decl(name, scalar=True) + self.defn += gen_visit_enum(name) + + def visit_array_type(self, name, info, element_type): + decl = gen_visit_decl(name) + defn = gen_visit_list(name, element_type) + if isinstance(element_type, QAPISchemaBuiltinType): + self._btin += decl + if do_builtins: + self.defn += defn + else: + self.decl += decl + self.defn += defn + + def visit_object_type(self, name, info, base, members, variants): + # Nothing to do for the special empty builtin + if name == 'q_empty': + return + self.decl += gen_visit_members_decl(name) + self.defn += gen_visit_object_members(name, base, members, variants) + # TODO Worth changing the visitor signature, so we could + # directly use rather than repeat type.is_implicit()? + if not name.startswith('q_'): + # only explicit types need an allocating visit + self.decl += gen_visit_decl(name) + self.defn += gen_visit_object(name, base, members, variants) + + def visit_alternate_type(self, name, info, variants): + self.decl += gen_visit_decl(name) + self.defn += gen_visit_alternate(name, variants) + +# If you link code generated from multiple schemata, you want only one +# instance of the code for built-in types. Generate it only when +# do_builtins, enabled by command line option -b. See also +# QAPISchemaGenVisitVisitor.visit_end(). do_builtins = False (input_file, output_dir, do_c, do_h, prefix, opts) = \ @@ -415,70 +353,25 @@ h_comment = ''' c_comment, h_comment) fdef.write(mcgen(''' +#include "qemu/osdep.h" #include "qemu-common.h" +#include "qapi/error.h" #include "%(prefix)sqapi-visit.h" ''', - prefix = prefix)) + prefix=prefix)) fdecl.write(mcgen(''' #include "qapi/visitor.h" +#include "qapi/qmp/qerror.h" #include "%(prefix)sqapi-types.h" ''', prefix=prefix)) -exprs = parse_schema(input_file) - -# to avoid header dependency hell, we always generate declarations -# for built-in types in our header files and simply guard them -fdecl.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DECL")) -for typename in builtin_types.keys(): - fdecl.write(generate_declaration(typename, None, builtin_type=True)) -fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL")) - -# ...this doesn't work for cases where we link in multiple objects that -# have the functions defined, so we use -b option to provide control -# over these cases -if do_builtins: - for typename in builtin_types.keys(): - fdef.write(generate_visit_list(typename, None)) - -for expr in exprs: - if expr.has_key('struct'): - ret = generate_visit_struct(expr) - ret += generate_visit_list(expr['struct'], expr['data']) - fdef.write(ret) - - ret = generate_declaration(expr['struct'], expr['data']) - fdecl.write(ret) - elif expr.has_key('union'): - ret = generate_visit_union(expr) - ret += generate_visit_list(expr['union'], expr['data']) - fdef.write(ret) - - enum_define = discriminator_find_enum_define(expr) - ret = "" - if not enum_define: - ret = generate_decl_enum('%sKind' % expr['union'], - expr['data'].keys()) - ret += generate_declaration(expr['union'], expr['data']) - fdecl.write(ret) - elif expr.has_key('alternate'): - ret = generate_visit_alternate(expr['alternate'], expr['data']) - ret += generate_visit_list(expr['alternate'], expr['data']) - fdef.write(ret) - - ret = generate_decl_enum('%sKind' % expr['alternate'], - expr['data'].keys()) - ret += generate_declaration(expr['alternate'], expr['data']) - fdecl.write(ret) - elif expr.has_key('enum'): - ret = generate_visit_list(expr['enum'], expr['data']) - ret += generate_visit_enum(expr['enum'], expr['data']) - fdef.write(ret) - - ret = generate_decl_enum(expr['enum'], expr['data']) - ret += generate_enum_declaration(expr['enum'], expr['data']) - fdecl.write(ret) +schema = QAPISchema(input_file) +gen = QAPISchemaGenVisitVisitor() +schema.visit(gen) +fdef.write(gen.defn) +fdecl.write(gen.decl) close_output(fdef, fdecl) diff --git a/qemu/scripts/qapi.py b/qemu/scripts/qapi.py index 06d7fc284..b13ae4789 100644 --- a/qemu/scripts/qapi.py +++ b/qemu/scripts/qapi.py @@ -2,7 +2,7 @@ # QAPI helper library # # Copyright IBM, Corp. 2011 -# Copyright (c) 2013-2015 Red Hat Inc. +# Copyright (c) 2013-2016 Red Hat Inc. # # Authors: # Anthony Liguori @@ -33,12 +33,15 @@ builtin_types = { 'uint32': 'QTYPE_QINT', 'uint64': 'QTYPE_QINT', 'size': 'QTYPE_QINT', + 'any': None, # any QType possible, actually + 'QType': 'QTYPE_QSTRING', } # Whitelist of commands allowed to return a non-dictionary returns_whitelist = [ # From QMP: 'human-monitor-command', + 'qom-get', 'query-migrate-cache-size', 'query-tpm-models', 'query-tpm-types', @@ -54,9 +57,18 @@ returns_whitelist = [ 'guest-set-vcpus', 'guest-sync', 'guest-sync-delimited', +] - # From qapi-schema-test: - 'user_def_cmd3', +# Whitelist of entities allowed to violate case conventions +case_whitelist = [ + # From QMP: + 'ACPISlotType', # DIMM, visible through query-acpi-ospm-status + 'CpuInfoMIPS', # PC, visible through query-cpu + 'CpuInfoTricore', # PC, visible through query-cpu + 'QapiErrorClass', # all members, visible through errors + 'UuidInfo', # UUID, visible through query-uuid + 'X86CPURegister32', # all members, visible indirectly through qom-get + 'q_obj_CpuInfo-base', # CPU, visible through query-cpu ] enum_types = [] @@ -69,6 +81,7 @@ all_names = {} # Parsing the schema into expressions # + def error_path(parent): res = "" while parent: @@ -77,8 +90,10 @@ def error_path(parent): parent = parent['parent'] return res + class QAPISchemaError(Exception): def __init__(self, schema, msg): + Exception.__init__(self) self.fname = schema.fname self.msg = msg self.col = 1 @@ -94,8 +109,11 @@ class QAPISchemaError(Exception): return error_path(self.info) + \ "%s:%d:%d: %s" % (self.fname, self.line, self.col, self.msg) + class QAPIExprError(Exception): def __init__(self, expr_info, msg): + Exception.__init__(self) + assert expr_info self.info = expr_info self.msg = msg @@ -103,9 +121,10 @@ class QAPIExprError(Exception): return error_path(self.info['parent']) + \ "%s:%d: %s" % (self.info['file'], self.info['line'], self.msg) -class QAPISchema: - def __init__(self, fp, previously_included = [], incl_info = None): +class QAPISchemaParser(object): + + def __init__(self, fp, previously_included=[], incl_info=None): abs_fname = os.path.abspath(fp.name) fname = fp.name self.fname = fname @@ -120,18 +139,18 @@ class QAPISchema: self.exprs = [] self.accept() - while self.tok != None: + while self.tok is not None: expr_info = {'file': fname, 'line': self.line, 'parent': self.incl_info} expr = self.get_expr(False) if isinstance(expr, dict) and "include" in expr: if len(expr) != 1: - raise QAPIExprError(expr_info, "Invalid 'include' directive") + raise QAPIExprError(expr_info, + "Invalid 'include' directive") include = expr["include"] if not isinstance(include, str): raise QAPIExprError(expr_info, - 'Expected a file name (string), got: %s' - % include) + "Value of 'include' must be a string") incl_abs_fname = os.path.join(os.path.dirname(abs_fname), include) # catch inclusion cycle @@ -146,11 +165,11 @@ class QAPISchema: continue try: fobj = open(incl_abs_fname, 'r') - except IOError, e: + except IOError as e: raise QAPIExprError(expr_info, '%s: %s' % (e.strerror, include)) - exprs_include = QAPISchema(fobj, previously_included, - expr_info) + exprs_include = QAPISchemaParser(fobj, previously_included, + expr_info) self.exprs.extend(exprs_include.exprs) else: expr_elem = {'expr': expr, @@ -166,7 +185,7 @@ class QAPISchema: if self.tok == '#': self.cursor = self.src.find('\n', self.cursor) - elif self.tok in ['{', '}', ':', ',', '[', ']']: + elif self.tok in "{}:,[]": return elif self.tok == "'": string = '' @@ -190,7 +209,7 @@ class QAPISchema: string += '\t' elif ch == 'u': value = 0 - for x in range(0, 4): + for _ in range(0, 4): ch = self.src[self.cursor] self.cursor += 1 if ch not in "0123456789abcdefABCDEF": @@ -212,7 +231,7 @@ class QAPISchema: string += ch else: raise QAPISchemaError(self, - "Unknown escape \\%s" %ch) + "Unknown escape \\%s" % ch) esc = False elif ch == "\\": esc = True @@ -272,7 +291,7 @@ class QAPISchema: if self.tok == ']': self.accept() return expr - if not self.tok in "{['tfn": + if self.tok not in "{['tfn": raise QAPISchemaError(self, 'Expected "{", "[", "]", string, ' 'boolean or "null"') while True: @@ -302,17 +321,23 @@ class QAPISchema: # # Semantic analysis of schema expressions +# TODO fold into QAPISchema +# TODO catching name collisions in generated code would be nice # -def find_base_fields(base): + +def find_base_members(base): + if isinstance(base, dict): + return base base_struct_define = find_struct(base) if not base_struct_define: return None return base_struct_define['data'] + # Return the qtype of an alternate branch, or None on error. def find_alternate_member_qtype(qapi_type): - if builtin_types.has_key(qapi_type): + if qapi_type in builtin_types: return builtin_types[qapi_type] elif find_struct(qapi_type): return "QTYPE_QDICT" @@ -322,6 +347,7 @@ def find_alternate_member_qtype(qapi_type): return "QTYPE_QDICT" return None + # Return the discriminator enum define if discriminator is specified as an # enum type, otherwise return None. def discriminator_find_enum_define(expr): @@ -331,19 +357,26 @@ def discriminator_find_enum_define(expr): if not (discriminator and base): return None - base_fields = find_base_fields(base) - if not base_fields: + base_members = find_base_members(base) + if not base_members: return None - discriminator_type = base_fields.get(discriminator) + discriminator_type = base_members.get(discriminator) if not discriminator_type: return None return find_enum(discriminator_type) -valid_name = re.compile('^[a-zA-Z_][a-zA-Z0-9_.-]*$') -def check_name(expr_info, source, name, allow_optional = False, - enum_member = False): + +# Names must be letters, numbers, -, and _. They must start with letter, +# except for downstream extensions which must start with __RFQDN_. +# Dots are only valid in the downstream extension prefix. +valid_name = re.compile('^(__[a-zA-Z0-9.-]+_)?' + '[a-zA-Z][a-zA-Z0-9_-]*$') + + +def check_name(expr_info, source, name, allow_optional=False, + enum_member=False): global valid_name membername = name @@ -358,31 +391,39 @@ def check_name(expr_info, source, name, allow_optional = False, % (source, name)) # Enum members can start with a digit, because the generated C # code always prefixes it with the enum name - if enum_member: - membername = '_' + membername - if not valid_name.match(membername): + if enum_member and membername[0].isdigit(): + membername = 'D' + membername + # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty' + # and 'q_obj_*' implicit type names. + if not valid_name.match(membername) or \ + c_name(membername, False).startswith('q_'): raise QAPIExprError(expr_info, "%s uses invalid name '%s'" % (source, name)) -def add_name(name, info, meta, implicit = False): + +def add_name(name, info, meta, implicit=False): global all_names check_name(info, "'%s'" % meta, name) + # FIXME should reject names that differ only in '_' vs. '.' + # vs. '-', because they're liable to clash in generated C. if name in all_names: raise QAPIExprError(info, "%s '%s' is already defined" % (all_names[name], name)) - if not implicit and name[-4:] == 'Kind': + if not implicit and (name.endswith('Kind') or name.endswith('List')): raise QAPIExprError(info, - "%s '%s' should not end in 'Kind'" - % (meta, name)) + "%s '%s' should not end in '%s'" + % (meta, name, name[-4:])) all_names[name] = meta + def add_struct(definition, info): global struct_types name = definition['struct'] add_name(name, info, 'struct') struct_types.append(definition) + def find_struct(name): global struct_types for struct in struct_types: @@ -390,12 +431,14 @@ def find_struct(name): return struct return None + def add_union(definition, info): global union_types name = definition['union'] add_name(name, info, 'union') union_types.append(definition) + def find_union(name): global union_types for union in union_types: @@ -403,11 +446,13 @@ def find_union(name): return union return None -def add_enum(name, info, enum_values = None, implicit = False): + +def add_enum(name, info, enum_values=None, implicit=False): global enum_types add_name(name, info, 'enum', implicit) enum_types.append({"enum_name": name, "enum_values": enum_values}) + def find_enum(name): global enum_types for enum in enum_types: @@ -415,21 +460,19 @@ def find_enum(name): return enum return None + def is_enum(name): - return find_enum(name) != None + return find_enum(name) is not None + -def check_type(expr_info, source, value, allow_array = False, - allow_dict = False, allow_optional = False, - allow_star = False, allow_metas = []): +def check_type(expr_info, source, value, allow_array=False, + allow_dict=False, allow_optional=False, + allow_metas=[]): global all_names - orig_value = value if value is None: return - if allow_star and value == '**': - return - # Check if array type for value is okay if isinstance(value, list): if not allow_array: @@ -440,103 +483,79 @@ def check_type(expr_info, source, value, allow_array = False, "%s: array type must contain single type name" % source) value = value[0] - orig_value = "array of %s" %value # Check if type name for value is okay if isinstance(value, str): - if value == '**': - raise QAPIExprError(expr_info, - "%s uses '**' but did not request 'gen':false" - % source) - if not value in all_names: + if value not in all_names: raise QAPIExprError(expr_info, "%s uses unknown type '%s'" - % (source, orig_value)) + % (source, value)) if not all_names[value] in allow_metas: raise QAPIExprError(expr_info, "%s cannot use %s type '%s'" - % (source, all_names[value], orig_value)) + % (source, all_names[value], value)) return - # value is a dictionary, check that each member is okay - if not isinstance(value, OrderedDict): - raise QAPIExprError(expr_info, - "%s should be a dictionary" % source) if not allow_dict: raise QAPIExprError(expr_info, "%s should be a type name" % source) + + if not isinstance(value, OrderedDict): + raise QAPIExprError(expr_info, + "%s should be a dictionary or type name" % source) + + # value is a dictionary, check that each member is okay for (key, arg) in value.items(): check_name(expr_info, "Member of %s" % source, key, allow_optional=allow_optional) + if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'): + raise QAPIExprError(expr_info, + "Member of %s uses reserved name '%s'" + % (source, key)) # Todo: allow dictionaries to represent default values of # an optional argument. check_type(expr_info, "Member '%s' of %s" % (key, source), arg, - allow_array=True, allow_star=allow_star, + allow_array=True, allow_metas=['built-in', 'union', 'alternate', 'struct', 'enum']) -def check_member_clash(expr_info, base_name, data, source = ""): - base = find_struct(base_name) - assert base - base_members = base['data'] - for key in data.keys(): - if key.startswith('*'): - key = key[1:] - if key in base_members or "*" + key in base_members: - raise QAPIExprError(expr_info, - "Member name '%s'%s clashes with base '%s'" - % (key, source, base_name)) - if base.get('base'): - check_member_clash(expr_info, base['base'], data, source) def check_command(expr, expr_info): name = expr['command'] - allow_star = expr.has_key('gen') check_type(expr_info, "'data' for command '%s'" % name, expr.get('data'), allow_dict=True, allow_optional=True, - allow_metas=['union', 'struct'], allow_star=allow_star) + allow_metas=['struct']) returns_meta = ['union', 'struct'] if name in returns_whitelist: returns_meta += ['built-in', 'alternate', 'enum'] check_type(expr_info, "'returns' for command '%s'" % name, - expr.get('returns'), allow_array=True, allow_dict=True, - allow_optional=True, allow_metas=returns_meta, - allow_star=allow_star) + expr.get('returns'), allow_array=True, + allow_optional=True, allow_metas=returns_meta) + def check_event(expr, expr_info): global events name = expr['event'] - params = expr.get('data') - if name.upper() == 'MAX': - raise QAPIExprError(expr_info, "Event name 'MAX' cannot be created") events.append(name) check_type(expr_info, "'data' for event '%s'" % name, expr.get('data'), allow_dict=True, allow_optional=True, - allow_metas=['union', 'struct']) + allow_metas=['struct']) + def check_union(expr, expr_info): name = expr['union'] base = expr.get('base') discriminator = expr.get('discriminator') members = expr['data'] - values = { 'MAX': '(automatic)' } - - # If the object has a member 'base', its value must name a struct, - # and there must be a discriminator. - if base is not None: - if discriminator is None: - raise QAPIExprError(expr_info, - "Union '%s' requires a discriminator to go " - "along with base" %name) # Two types of unions, determined by discriminator. # With no discriminator it is a simple union. if discriminator is None: enum_define = None - allow_metas=['built-in', 'union', 'alternate', 'struct', 'enum'] + allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum'] if base is not None: raise QAPIExprError(expr_info, "Simple union '%s' must not have a base" @@ -544,91 +563,78 @@ def check_union(expr, expr_info): # Else, it's a flat union. else: - # The object must have a string member 'base'. - if not isinstance(base, str): + # The object must have a string or dictionary 'base'. + check_type(expr_info, "'base' for union '%s'" % name, + base, allow_dict=True, allow_optional=True, + allow_metas=['struct']) + if not base: raise QAPIExprError(expr_info, - "Flat union '%s' must have a string base field" + "Flat union '%s' must have a base" % name) - base_fields = find_base_fields(base) - if not base_fields: - raise QAPIExprError(expr_info, - "Base '%s' is not a valid struct" - % base) + base_members = find_base_members(base) + assert base_members # The value of member 'discriminator' must name a non-optional # member of the base struct. check_name(expr_info, "Discriminator of flat union '%s'" % name, discriminator) - discriminator_type = base_fields.get(discriminator) + discriminator_type = base_members.get(discriminator) if not discriminator_type: raise QAPIExprError(expr_info, "Discriminator '%s' is not a member of base " "struct '%s'" % (discriminator, base)) enum_define = find_enum(discriminator_type) - allow_metas=['struct'] + allow_metas = ['struct'] # Do not allow string discriminator if not enum_define: raise QAPIExprError(expr_info, "Discriminator '%s' must be of enumeration " "type" % discriminator) - # Check every branch + # Check every branch; don't allow an empty union + if len(members) == 0: + raise QAPIExprError(expr_info, + "Union '%s' cannot have empty 'data'" % name) for (key, value) in members.items(): check_name(expr_info, "Member of union '%s'" % name, key) - # Each value must name a known type; furthermore, in flat unions, - # branches must be a struct with no overlapping member names + # Each value must name a known type check_type(expr_info, "Member '%s' of union '%s'" % (key, name), value, allow_array=not base, allow_metas=allow_metas) - if base: - branch_struct = find_struct(value) - assert branch_struct - check_member_clash(expr_info, base, branch_struct['data'], - " of branch '%s'" % key) # If the discriminator names an enum type, then all members # of 'data' must also be members of the enum type. if enum_define: - if not key in enum_define['enum_values']: + if key not in enum_define['enum_values']: raise QAPIExprError(expr_info, "Discriminator value '%s' is not found in " "enum '%s'" % (key, enum_define["enum_name"])) - # Otherwise, check for conflicts in the generated enum - else: - c_key = camel_to_upper(key) - if c_key in values: - raise QAPIExprError(expr_info, - "Union '%s' member '%s' clashes with '%s'" - % (name, key, values[c_key])) - values[c_key] = key def check_alternate(expr, expr_info): name = expr['alternate'] members = expr['data'] - values = { 'MAX': '(automatic)' } types_seen = {} - # Check every branch + # Check every branch; require at least two branches + if len(members) < 2: + raise QAPIExprError(expr_info, + "Alternate '%s' should have at least two branches " + "in 'data'" % name) for (key, value) in members.items(): check_name(expr_info, "Member of alternate '%s'" % name, key) - # Check for conflicts in the generated enum - c_key = camel_to_upper(key) - if c_key in values: - raise QAPIExprError(expr_info, - "Alternate '%s' member '%s' clashes with '%s'" - % (name, key, values[c_key])) - values[c_key] = key - # Ensure alternates have no type conflicts. check_type(expr_info, "Member '%s' of alternate '%s'" % (key, name), value, allow_metas=['built-in', 'union', 'struct', 'enum']) qtype = find_alternate_member_qtype(value) - assert qtype + if not qtype: + raise QAPIExprError(expr_info, + "Alternate '%s' member '%s' cannot use " + "type '%s'" % (name, key, value)) if qtype in types_seen: raise QAPIExprError(expr_info, "Alternate '%s' member '%s' can't " @@ -636,23 +642,22 @@ def check_alternate(expr, expr_info): % (name, key, types_seen[qtype])) types_seen[qtype] = key + def check_enum(expr, expr_info): name = expr['enum'] members = expr.get('data') - values = { 'MAX': '(automatic)' } + prefix = expr.get('prefix') if not isinstance(members, list): raise QAPIExprError(expr_info, "Enum '%s' requires an array for 'data'" % name) + if prefix is not None and not isinstance(prefix, str): + raise QAPIExprError(expr_info, + "Enum '%s' requires a string for 'prefix'" % name) for member in members: - check_name(expr_info, "Member of enum '%s'" %name, member, + check_name(expr_info, "Member of enum '%s'" % name, member, enum_member=True) - key = camel_to_upper(member) - if key in values: - raise QAPIExprError(expr_info, - "Enum '%s' member '%s' clashes with '%s'" - % (name, member, values[key])) - values[key] = member + def check_struct(expr, expr_info): name = expr['struct'] @@ -662,8 +667,7 @@ def check_struct(expr, expr_info): allow_dict=True, allow_optional=True) check_type(expr_info, "'base' for struct '%s'" % name, expr.get('base'), allow_metas=['struct']) - if expr.get('base'): - check_member_clash(expr_info, expr['base'], expr['data']) + def check_keys(expr_elem, meta, required, optional=[]): expr = expr_elem['expr'] @@ -672,22 +676,23 @@ def check_keys(expr_elem, meta, required, optional=[]): if not isinstance(name, str): raise QAPIExprError(info, "'%s' key must have a string value" % meta) - required = required + [ meta ] + required = required + [meta] for (key, value) in expr.items(): - if not key in required and not key in optional: + if key not in required and key not in optional: raise QAPIExprError(info, "Unknown key '%s' in %s '%s'" % (key, meta, name)) - if (key == 'gen' or key == 'success-response') and value != False: + if (key == 'gen' or key == 'success-response') and value is not False: raise QAPIExprError(info, "'%s' of %s '%s' should only use false value" % (key, meta, name)) for key in required: - if not expr.has_key(key): + if key not in expr: raise QAPIExprError(info, "Key '%s' is missing from %s '%s'" % (key, meta, name)) + def check_exprs(exprs): global all_names @@ -697,24 +702,24 @@ def check_exprs(exprs): for expr_elem in exprs: expr = expr_elem['expr'] info = expr_elem['info'] - if expr.has_key('enum'): - check_keys(expr_elem, 'enum', ['data']) + if 'enum' in expr: + check_keys(expr_elem, 'enum', ['data'], ['prefix']) add_enum(expr['enum'], info, expr['data']) - elif expr.has_key('union'): + elif 'union' in expr: check_keys(expr_elem, 'union', ['data'], ['base', 'discriminator']) add_union(expr, info) - elif expr.has_key('alternate'): + elif 'alternate' in expr: check_keys(expr_elem, 'alternate', ['data']) add_name(expr['alternate'], info, 'alternate') - elif expr.has_key('struct'): + elif 'struct' in expr: check_keys(expr_elem, 'struct', ['data'], ['base']) add_struct(expr, info) - elif expr.has_key('command'): + elif 'command' in expr: check_keys(expr_elem, 'command', [], ['data', 'returns', 'gen', 'success-response']) add_name(expr['command'], info, 'command') - elif expr.has_key('event'): + elif 'event' in expr: check_keys(expr_elem, 'event', [], ['data']) add_name(expr['event'], info, 'event') else: @@ -724,11 +729,11 @@ def check_exprs(exprs): # Try again for hidden UnionKind enum for expr_elem in exprs: expr = expr_elem['expr'] - if expr.has_key('union'): + if 'union' in expr: if not discriminator_find_enum_define(expr): add_enum('%sKind' % expr['union'], expr_elem['info'], implicit=True) - elif expr.has_key('alternate'): + elif 'alternate' in expr: add_enum('%sKind' % expr['alternate'], expr_elem['info'], implicit=True) @@ -737,51 +742,683 @@ def check_exprs(exprs): expr = expr_elem['expr'] info = expr_elem['info'] - if expr.has_key('enum'): + if 'enum' in expr: check_enum(expr, info) - elif expr.has_key('union'): + elif 'union' in expr: check_union(expr, info) - elif expr.has_key('alternate'): + elif 'alternate' in expr: check_alternate(expr, info) - elif expr.has_key('struct'): + elif 'struct' in expr: check_struct(expr, info) - elif expr.has_key('command'): + elif 'command' in expr: check_command(expr, info) - elif expr.has_key('event'): + elif 'event' in expr: check_event(expr, info) else: assert False, 'unexpected meta type' - return map(lambda expr_elem: expr_elem['expr'], exprs) + return exprs -def parse_schema(fname): - try: - schema = QAPISchema(open(fname, "r")) - return check_exprs(schema.exprs) - except (QAPISchemaError, QAPIExprError), e: - print >>sys.stderr, e - exit(1) # -# Code generation helpers +# Schema compiler frontend # -def parse_args(typeinfo): - if isinstance(typeinfo, str): - struct = find_struct(typeinfo) - assert struct != None - typeinfo = struct['data'] +class QAPISchemaEntity(object): + def __init__(self, name, info): + assert isinstance(name, str) + self.name = name + # For explicitly defined entities, info points to the (explicit) + # definition. For builtins (and their arrays), info is None. + # For implicitly defined entities, info points to a place that + # triggered the implicit definition (there may be more than one + # such place). + self.info = info + + def c_name(self): + return c_name(self.name) + + def check(self, schema): + pass + + def is_implicit(self): + return not self.info + + def visit(self, visitor): + pass + + +class QAPISchemaVisitor(object): + def visit_begin(self, schema): + pass + + def visit_end(self): + pass + + def visit_needed(self, entity): + # Default to visiting everything + return True + + def visit_builtin_type(self, name, info, json_type): + pass + + def visit_enum_type(self, name, info, values, prefix): + pass + + def visit_array_type(self, name, info, element_type): + pass + + def visit_object_type(self, name, info, base, members, variants): + pass + + def visit_object_type_flat(self, name, info, members, variants): + pass + + def visit_alternate_type(self, name, info, variants): + pass + + def visit_command(self, name, info, arg_type, ret_type, + gen, success_response): + pass - for member in typeinfo: - argname = member - argentry = typeinfo[member] + def visit_event(self, name, info, arg_type): + pass + + +class QAPISchemaType(QAPISchemaEntity): + # Return the C type for common use. + # For the types we commonly box, this is a pointer type. + def c_type(self): + pass + + # Return the C type to be used in a parameter list. + def c_param_type(self): + return self.c_type() + + # Return the C type to be used where we suppress boxing. + def c_unboxed_type(self): + return self.c_type() + + def json_type(self): + pass + + def alternate_qtype(self): + json2qtype = { + 'string': 'QTYPE_QSTRING', + 'number': 'QTYPE_QFLOAT', + 'int': 'QTYPE_QINT', + 'boolean': 'QTYPE_QBOOL', + 'object': 'QTYPE_QDICT' + } + return json2qtype.get(self.json_type()) + + +class QAPISchemaBuiltinType(QAPISchemaType): + def __init__(self, name, json_type, c_type): + QAPISchemaType.__init__(self, name, None) + assert not c_type or isinstance(c_type, str) + assert json_type in ('string', 'number', 'int', 'boolean', 'null', + 'value') + self._json_type_name = json_type + self._c_type_name = c_type + + def c_name(self): + return self.name + + def c_type(self): + return self._c_type_name + + def c_param_type(self): + if self.name == 'str': + return 'const ' + self._c_type_name + return self._c_type_name + + def json_type(self): + return self._json_type_name + + def visit(self, visitor): + visitor.visit_builtin_type(self.name, self.info, self.json_type()) + + +class QAPISchemaEnumType(QAPISchemaType): + def __init__(self, name, info, values, prefix): + QAPISchemaType.__init__(self, name, info) + for v in values: + assert isinstance(v, QAPISchemaMember) + v.set_owner(name) + assert prefix is None or isinstance(prefix, str) + self.values = values + self.prefix = prefix + + def check(self, schema): + seen = {} + for v in self.values: + v.check_clash(self.info, seen) + + def is_implicit(self): + # See QAPISchema._make_implicit_enum_type() + return self.name.endswith('Kind') + + def c_type(self): + return c_name(self.name) + + def member_names(self): + return [v.name for v in self.values] + + def json_type(self): + return 'string' + + def visit(self, visitor): + visitor.visit_enum_type(self.name, self.info, + self.member_names(), self.prefix) + + +class QAPISchemaArrayType(QAPISchemaType): + def __init__(self, name, info, element_type): + QAPISchemaType.__init__(self, name, info) + assert isinstance(element_type, str) + self._element_type_name = element_type + self.element_type = None + + def check(self, schema): + self.element_type = schema.lookup_type(self._element_type_name) + assert self.element_type + + def is_implicit(self): + return True + + def c_type(self): + return c_name(self.name) + pointer_suffix + + def json_type(self): + return 'array' + + def visit(self, visitor): + visitor.visit_array_type(self.name, self.info, self.element_type) + + +class QAPISchemaObjectType(QAPISchemaType): + def __init__(self, name, info, base, local_members, variants): + # struct has local_members, optional base, and no variants + # flat union has base, variants, and no local_members + # simple union has local_members, variants, and no base + QAPISchemaType.__init__(self, name, info) + assert base is None or isinstance(base, str) + for m in local_members: + assert isinstance(m, QAPISchemaObjectTypeMember) + m.set_owner(name) + if variants is not None: + assert isinstance(variants, QAPISchemaObjectTypeVariants) + variants.set_owner(name) + self._base_name = base + self.base = None + self.local_members = local_members + self.variants = variants + self.members = None + + def check(self, schema): + if self.members is False: # check for cycles + raise QAPIExprError(self.info, + "Object %s contains itself" % self.name) + if self.members: + return + self.members = False # mark as being checked + seen = OrderedDict() + if self._base_name: + self.base = schema.lookup_type(self._base_name) + assert isinstance(self.base, QAPISchemaObjectType) + self.base.check(schema) + self.base.check_clash(schema, self.info, seen) + for m in self.local_members: + m.check(schema) + m.check_clash(self.info, seen) + self.members = seen.values() + if self.variants: + self.variants.check(schema, seen) + assert self.variants.tag_member in self.members + self.variants.check_clash(schema, self.info, seen) + + # Check that the members of this type do not cause duplicate JSON members, + # and update seen to track the members seen so far. Report any errors + # on behalf of info, which is not necessarily self.info + def check_clash(self, schema, info, seen): + assert not self.variants # not implemented + for m in self.members: + m.check_clash(info, seen) + + def is_implicit(self): + # See QAPISchema._make_implicit_object_type(), as well as + # _def_predefineds() + return self.name.startswith('q_') + + def c_name(self): + return QAPISchemaType.c_name(self) + + def c_type(self): + assert not self.is_implicit() + return c_name(self.name) + pointer_suffix + + def c_unboxed_type(self): + return c_name(self.name) + + def json_type(self): + return 'object' + + def visit(self, visitor): + visitor.visit_object_type(self.name, self.info, + self.base, self.local_members, self.variants) + visitor.visit_object_type_flat(self.name, self.info, + self.members, self.variants) + + +class QAPISchemaMember(object): + role = 'member' + + def __init__(self, name): + assert isinstance(name, str) + self.name = name + self.owner = None + + def set_owner(self, name): + assert not self.owner + self.owner = name + + def check_clash(self, info, seen): + cname = c_name(self.name) + if cname.lower() != cname and self.owner not in case_whitelist: + raise QAPIExprError(info, + "%s should not use uppercase" % self.describe()) + if cname in seen: + raise QAPIExprError(info, + "%s collides with %s" + % (self.describe(), seen[cname].describe())) + seen[cname] = self + + def _pretty_owner(self): + owner = self.owner + if owner.startswith('q_obj_'): + # See QAPISchema._make_implicit_object_type() - reverse the + # mapping there to create a nice human-readable description + owner = owner[6:] + if owner.endswith('-arg'): + return '(parameter of %s)' % owner[:-4] + elif owner.endswith('-base'): + return '(base of %s)' % owner[:-5] + else: + assert owner.endswith('-wrapper') + # Unreachable and not implemented + assert False + if owner.endswith('Kind'): + # See QAPISchema._make_implicit_enum_type() + return '(branch of %s)' % owner[:-4] + return '(%s of %s)' % (self.role, owner) + + def describe(self): + return "'%s' %s" % (self.name, self._pretty_owner()) + + +class QAPISchemaObjectTypeMember(QAPISchemaMember): + def __init__(self, name, typ, optional): + QAPISchemaMember.__init__(self, name) + assert isinstance(typ, str) + assert isinstance(optional, bool) + self._type_name = typ + self.type = None + self.optional = optional + + def check(self, schema): + assert self.owner + self.type = schema.lookup_type(self._type_name) + assert self.type + + +class QAPISchemaObjectTypeVariants(object): + def __init__(self, tag_name, tag_member, variants): + # Flat unions pass tag_name but not tag_member. + # Simple unions and alternates pass tag_member but not tag_name. + # After check(), tag_member is always set, and tag_name remains + # a reliable witness of being used by a flat union. + assert bool(tag_member) != bool(tag_name) + assert (isinstance(tag_name, str) or + isinstance(tag_member, QAPISchemaObjectTypeMember)) + assert len(variants) > 0 + for v in variants: + assert isinstance(v, QAPISchemaObjectTypeVariant) + self.tag_name = tag_name + self.tag_member = tag_member + self.variants = variants + + def set_owner(self, name): + for v in self.variants: + v.set_owner(name) + + def check(self, schema, seen): + if not self.tag_member: # flat union + self.tag_member = seen[c_name(self.tag_name)] + assert self.tag_name == self.tag_member.name + assert isinstance(self.tag_member.type, QAPISchemaEnumType) + for v in self.variants: + v.check(schema) + # Union names must match enum values; alternate names are + # checked separately. Use 'seen' to tell the two apart. + if seen: + assert v.name in self.tag_member.type.member_names() + assert isinstance(v.type, QAPISchemaObjectType) + v.type.check(schema) + + def check_clash(self, schema, info, seen): + for v in self.variants: + # Reset seen map for each variant, since qapi names from one + # branch do not affect another branch + assert isinstance(v.type, QAPISchemaObjectType) + v.type.check_clash(schema, info, dict(seen)) + + +class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): + role = 'branch' + + def __init__(self, name, typ): + QAPISchemaObjectTypeMember.__init__(self, name, typ, False) + + +class QAPISchemaAlternateType(QAPISchemaType): + def __init__(self, name, info, variants): + QAPISchemaType.__init__(self, name, info) + assert isinstance(variants, QAPISchemaObjectTypeVariants) + assert not variants.tag_name + variants.set_owner(name) + variants.tag_member.set_owner(self.name) + self.variants = variants + + def check(self, schema): + self.variants.tag_member.check(schema) + # Not calling self.variants.check_clash(), because there's nothing + # to clash with + self.variants.check(schema, {}) + # Alternate branch names have no relation to the tag enum values; + # so we have to check for potential name collisions ourselves. + seen = {} + for v in self.variants.variants: + v.check_clash(self.info, seen) + + def c_type(self): + return c_name(self.name) + pointer_suffix + + def json_type(self): + return 'value' + + def visit(self, visitor): + visitor.visit_alternate_type(self.name, self.info, self.variants) + + +class QAPISchemaCommand(QAPISchemaEntity): + def __init__(self, name, info, arg_type, ret_type, gen, success_response): + QAPISchemaEntity.__init__(self, name, info) + assert not arg_type or isinstance(arg_type, str) + assert not ret_type or isinstance(ret_type, str) + self._arg_type_name = arg_type + self.arg_type = None + self._ret_type_name = ret_type + self.ret_type = None + self.gen = gen + self.success_response = success_response + + def check(self, schema): + if self._arg_type_name: + self.arg_type = schema.lookup_type(self._arg_type_name) + assert isinstance(self.arg_type, QAPISchemaObjectType) + assert not self.arg_type.variants # not implemented + if self._ret_type_name: + self.ret_type = schema.lookup_type(self._ret_type_name) + assert isinstance(self.ret_type, QAPISchemaType) + + def visit(self, visitor): + visitor.visit_command(self.name, self.info, + self.arg_type, self.ret_type, + self.gen, self.success_response) + + +class QAPISchemaEvent(QAPISchemaEntity): + def __init__(self, name, info, arg_type): + QAPISchemaEntity.__init__(self, name, info) + assert not arg_type or isinstance(arg_type, str) + self._arg_type_name = arg_type + self.arg_type = None + + def check(self, schema): + if self._arg_type_name: + self.arg_type = schema.lookup_type(self._arg_type_name) + assert isinstance(self.arg_type, QAPISchemaObjectType) + assert not self.arg_type.variants # not implemented + + def visit(self, visitor): + visitor.visit_event(self.name, self.info, self.arg_type) + + +class QAPISchema(object): + def __init__(self, fname): + try: + self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs) + self._entity_dict = {} + self._predefining = True + self._def_predefineds() + self._predefining = False + self._def_exprs() + self.check() + except (QAPISchemaError, QAPIExprError) as err: + print >>sys.stderr, err + exit(1) + + def _def_entity(self, ent): + # Only the predefined types are allowed to not have info + assert ent.info or self._predefining + assert ent.name not in self._entity_dict + self._entity_dict[ent.name] = ent + + def lookup_entity(self, name, typ=None): + ent = self._entity_dict.get(name) + if typ and not isinstance(ent, typ): + return None + return ent + + def lookup_type(self, name): + return self.lookup_entity(name, QAPISchemaType) + + def _def_builtin_type(self, name, json_type, c_type): + self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) + # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple + # qapi-types.h from a single .c, all arrays of builtins must be + # declared in the first file whether or not they are used. Nicer + # would be to use lazy instantiation, while figuring out how to + # avoid compilation issues with multiple qapi-types.h. + self._make_array_type(name, None) + + def _def_predefineds(self): + for t in [('str', 'string', 'char' + pointer_suffix), + ('number', 'number', 'double'), + ('int', 'int', 'int64_t'), + ('int8', 'int', 'int8_t'), + ('int16', 'int', 'int16_t'), + ('int32', 'int', 'int32_t'), + ('int64', 'int', 'int64_t'), + ('uint8', 'int', 'uint8_t'), + ('uint16', 'int', 'uint16_t'), + ('uint32', 'int', 'uint32_t'), + ('uint64', 'int', 'uint64_t'), + ('size', 'int', 'uint64_t'), + ('bool', 'boolean', 'bool'), + ('any', 'value', 'QObject' + pointer_suffix)]: + self._def_builtin_type(*t) + self.the_empty_object_type = QAPISchemaObjectType('q_empty', None, + None, [], None) + self._def_entity(self.the_empty_object_type) + qtype_values = self._make_enum_members(['none', 'qnull', 'qint', + 'qstring', 'qdict', 'qlist', + 'qfloat', 'qbool']) + self._def_entity(QAPISchemaEnumType('QType', None, qtype_values, + 'QTYPE')) + + def _make_enum_members(self, values): + return [QAPISchemaMember(v) for v in values] + + def _make_implicit_enum_type(self, name, info, values): + # See also QAPISchemaObjectTypeMember._pretty_owner() + name = name + 'Kind' # Use namespace reserved by add_name() + self._def_entity(QAPISchemaEnumType( + name, info, self._make_enum_members(values), None)) + return name + + def _make_array_type(self, element_type, info): + name = element_type + 'List' # Use namespace reserved by add_name() + if not self.lookup_type(name): + self._def_entity(QAPISchemaArrayType(name, info, element_type)) + return name + + def _make_implicit_object_type(self, name, info, role, members): + if not members: + return None + # See also QAPISchemaObjectTypeMember._pretty_owner() + name = 'q_obj_%s-%s' % (name, role) + if not self.lookup_entity(name, QAPISchemaObjectType): + self._def_entity(QAPISchemaObjectType(name, info, None, + members, None)) + return name + + def _def_enum_type(self, expr, info): + name = expr['enum'] + data = expr['data'] + prefix = expr.get('prefix') + self._def_entity(QAPISchemaEnumType( + name, info, self._make_enum_members(data), prefix)) + + def _make_member(self, name, typ, info): optional = False - if member.startswith('*'): - argname = member[1:] + if name.startswith('*'): + name = name[1:] optional = True - # Todo: allow argentry to be OrderedDict, for providing the - # value of an optional argument. - yield (argname, argentry, optional) + if isinstance(typ, list): + assert len(typ) == 1 + typ = self._make_array_type(typ[0], info) + return QAPISchemaObjectTypeMember(name, typ, optional) + + def _make_members(self, data, info): + return [self._make_member(key, value, info) + for (key, value) in data.iteritems()] + + def _def_struct_type(self, expr, info): + name = expr['struct'] + base = expr.get('base') + data = expr['data'] + self._def_entity(QAPISchemaObjectType(name, info, base, + self._make_members(data, info), + None)) + + def _make_variant(self, case, typ): + return QAPISchemaObjectTypeVariant(case, typ) + + def _make_simple_variant(self, case, typ, info): + if isinstance(typ, list): + assert len(typ) == 1 + typ = self._make_array_type(typ[0], info) + typ = self._make_implicit_object_type( + typ, info, 'wrapper', [self._make_member('data', typ, info)]) + return QAPISchemaObjectTypeVariant(case, typ) + + def _def_union_type(self, expr, info): + name = expr['union'] + data = expr['data'] + base = expr.get('base') + tag_name = expr.get('discriminator') + tag_member = None + if isinstance(base, dict): + base = (self._make_implicit_object_type( + name, info, 'base', self._make_members(base, info))) + if tag_name: + variants = [self._make_variant(key, value) + for (key, value) in data.iteritems()] + members = [] + else: + variants = [self._make_simple_variant(key, value, info) + for (key, value) in data.iteritems()] + typ = self._make_implicit_enum_type(name, info, + [v.name for v in variants]) + tag_member = QAPISchemaObjectTypeMember('type', typ, False) + members = [tag_member] + self._def_entity( + QAPISchemaObjectType(name, info, base, members, + QAPISchemaObjectTypeVariants(tag_name, + tag_member, + variants))) + + def _def_alternate_type(self, expr, info): + name = expr['alternate'] + data = expr['data'] + variants = [self._make_variant(key, value) + for (key, value) in data.iteritems()] + tag_member = QAPISchemaObjectTypeMember('type', 'QType', False) + self._def_entity( + QAPISchemaAlternateType(name, info, + QAPISchemaObjectTypeVariants(None, + tag_member, + variants))) + + def _def_command(self, expr, info): + name = expr['command'] + data = expr.get('data') + rets = expr.get('returns') + gen = expr.get('gen', True) + success_response = expr.get('success-response', True) + if isinstance(data, OrderedDict): + data = self._make_implicit_object_type( + name, info, 'arg', self._make_members(data, info)) + if isinstance(rets, list): + assert len(rets) == 1 + rets = self._make_array_type(rets[0], info) + self._def_entity(QAPISchemaCommand(name, info, data, rets, gen, + success_response)) + + def _def_event(self, expr, info): + name = expr['event'] + data = expr.get('data') + if isinstance(data, OrderedDict): + data = self._make_implicit_object_type( + name, info, 'arg', self._make_members(data, info)) + self._def_entity(QAPISchemaEvent(name, info, data)) + + def _def_exprs(self): + for expr_elem in self.exprs: + expr = expr_elem['expr'] + info = expr_elem['info'] + if 'enum' in expr: + self._def_enum_type(expr, info) + elif 'struct' in expr: + self._def_struct_type(expr, info) + elif 'union' in expr: + self._def_union_type(expr, info) + elif 'alternate' in expr: + self._def_alternate_type(expr, info) + elif 'command' in expr: + self._def_command(expr, info) + elif 'event' in expr: + self._def_event(expr, info) + else: + assert False + + def check(self): + for ent in self._entity_dict.values(): + ent.check(self) + + def visit(self, visitor): + visitor.visit_begin(self) + for (name, entity) in sorted(self._entity_dict.items()): + if visitor.visit_needed(entity): + entity.visit(visitor) + visitor.visit_end() + + +# +# Code generation helpers +# def camel_case(name): new_name = '' @@ -796,6 +1433,7 @@ def camel_case(name): new_name += ch.lower() return new_name + # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2 # ENUM24_Name -> ENUM24_NAME @@ -810,19 +1448,22 @@ def camel_to_upper(value): c = c_fun_str[i] # When c is upper and no "_" appears before, do more checks if c.isupper() and (i > 0) and c_fun_str[i - 1] != "_": - # Case 1: next string is lower - # Case 2: previous string is digit - if (i < (l - 1) and c_fun_str[i + 1].islower()) or \ - c_fun_str[i - 1].isdigit(): + if i < l - 1 and c_fun_str[i + 1].islower(): + new_name += '_' + elif c_fun_str[i - 1].isdigit(): new_name += '_' new_name += c return new_name.lstrip('_').upper() -def c_enum_const(type_name, const_name): - return camel_to_upper(type_name + '_' + const_name) + +def c_enum_const(type_name, const_name, prefix=None): + if prefix is not None: + type_name = prefix + return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper() c_name_trans = string.maketrans('.-', '__') + # Map @name to a valid C identifier. # If @protect, avoid returning certain ticklish identifiers (like # C keywords) by prepending "q_". @@ -835,15 +1476,16 @@ c_name_trans = string.maketrans('.-', '__') def c_name(name, protect=True): # ANSI X3J11/88-090, 3.1.1 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue', - 'default', 'do', 'double', 'else', 'enum', 'extern', 'float', - 'for', 'goto', 'if', 'int', 'long', 'register', 'return', - 'short', 'signed', 'sizeof', 'static', 'struct', 'switch', - 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while']) + 'default', 'do', 'double', 'else', 'enum', 'extern', + 'float', 'for', 'goto', 'if', 'int', 'long', 'register', + 'return', 'short', 'signed', 'sizeof', 'static', + 'struct', 'switch', 'typedef', 'union', 'unsigned', + 'void', 'volatile', 'while']) # ISO/IEC 9899:1999, 6.4.1 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary']) # ISO/IEC 9899:2011, 6.4.1 - c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', '_Noreturn', - '_Static_assert', '_Thread_local']) + c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', + '_Noreturn', '_Static_assert', '_Thread_local']) # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html # excluding _.* gcc_words = set(['asm', 'typeof']) @@ -858,109 +1500,58 @@ def c_name(name, protect=True): 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq']) # namespace pollution: - polluted_words = set(['unix', 'errno']) - if protect and (name in c89_words | c99_words | c11_words | gcc_words | cpp_words | polluted_words): + polluted_words = set(['unix', 'errno', 'mips', 'sparc']) + name = name.translate(c_name_trans) + if protect and (name in c89_words | c99_words | c11_words | gcc_words + | cpp_words | polluted_words): return "q_" + name - return name.translate(c_name_trans) - -# Map type @name to the C typedef name for the list form. -# -# ['Name'] -> 'NameList', ['x-Foo'] -> 'x_FooList', ['int'] -> 'intList' -def c_list_type(name): - return type_name(name) + 'List' - -# Map type @value to the C typedef form. -# -# Used for converting 'type' from a 'member':'type' qapi definition -# into the alphanumeric portion of the type for a generated C parameter, -# as well as generated C function names. See c_type() for the rest of -# the conversion such as adding '*' on pointer types. -# 'int' -> 'int', '[x-Foo]' -> 'x_FooList', '__a.b_c' -> '__a_b_c' -def type_name(value): - if type(value) == list: - return c_list_type(value[0]) - if value in builtin_types.keys(): - return value - return c_name(value) + return name eatspace = '\033EATSPACE.' pointer_suffix = ' *' + eatspace -# Map type @name to its C type expression. -# If @is_param, const-qualify the string type. -# -# This function is used for computing the full C type of 'member':'name'. -# A special suffix is added in c_type() for pointer types, and it's -# stripped in mcgen(). So please notice this when you check the return -# value of c_type() outside mcgen(). -def c_type(value, is_param=False): - if value == 'str': - if is_param: - return 'const char' + pointer_suffix - return 'char' + pointer_suffix - - elif value == 'int': - return 'int64_t' - elif (value == 'int8' or value == 'int16' or value == 'int32' or - value == 'int64' or value == 'uint8' or value == 'uint16' or - value == 'uint32' or value == 'uint64'): - return value + '_t' - elif value == 'size': - return 'uint64_t' - elif value == 'bool': - return 'bool' - elif value == 'number': - return 'double' - elif type(value) == list: - return c_list_type(value[0]) + pointer_suffix - elif is_enum(value): - return c_name(value) - elif value == None: - return 'void' - elif value in events: - return camel_case(value) + 'Event' + pointer_suffix - else: - # complex type name - assert isinstance(value, str) and value != "" - return c_name(value) + pointer_suffix - -def is_c_ptr(value): - return c_type(value).endswith(pointer_suffix) def genindent(count): ret = "" - for i in range(count): + for _ in range(count): ret += " " return ret indent_level = 0 + def push_indent(indent_amount=4): global indent_level indent_level += indent_amount + def pop_indent(indent_amount=4): global indent_level indent_level -= indent_amount + +# Generate @code with @kwds interpolated. +# Obey indent_level, and strip eatspace. def cgen(code, **kwds): - indent = genindent(indent_level) - lines = code.split('\n') - lines = map(lambda x: indent + x, lines) - return '\n'.join(lines) % kwds + '\n' + raw = code % kwds + if indent_level: + indent = genindent(indent_level) + # re.subn() lacks flags support before Python 2.7, use re.compile() + raw = re.subn(re.compile("^.", re.MULTILINE), + indent + r'\g<0>', raw) + raw = raw[0] + return re.sub(re.escape(eatspace) + ' *', '', raw) + def mcgen(code, **kwds): - raw = cgen('\n'.join(code.split('\n')[1:-1]), **kwds) - return re.sub(re.escape(eatspace) + ' *', '', raw) + if code[0] == '\n': + code = code[1:] + return cgen(code, **kwds) -def basename(filename): - return filename.split("/")[-1] def guardname(filename): - guard = basename(filename).rsplit(".", 1)[0] - for substr in [".", " ", "-"]: - guard = guard.replace(substr, "_") - return guard.upper() + '_H' + return c_name(filename, protect=False).upper() + def guardstart(name): return mcgen(''' @@ -971,6 +1562,7 @@ def guardstart(name): ''', name=guardname(name)) + def guardend(name): return mcgen(''' @@ -979,18 +1571,99 @@ def guardend(name): ''', name=guardname(name)) + +def gen_enum_lookup(name, values, prefix=None): + ret = mcgen(''' + +const char *const %(c_name)s_lookup[] = { +''', + c_name=c_name(name)) + for value in values: + index = c_enum_const(name, value, prefix) + ret += mcgen(''' + [%(index)s] = "%(value)s", +''', + index=index, value=value) + + max_index = c_enum_const(name, '_MAX', prefix) + ret += mcgen(''' + [%(max_index)s] = NULL, +}; +''', + max_index=max_index) + return ret + + +def gen_enum(name, values, prefix=None): + # append automatically generated _MAX value + enum_values = values + ['_MAX'] + + ret = mcgen(''' + +typedef enum %(c_name)s { +''', + c_name=c_name(name)) + + i = 0 + for value in enum_values: + ret += mcgen(''' + %(c_enum)s = %(i)d, +''', + c_enum=c_enum_const(name, value, prefix), + i=i) + i += 1 + + ret += mcgen(''' +} %(c_name)s; +''', + c_name=c_name(name)) + + ret += mcgen(''' + +extern const char *const %(c_name)s_lookup[]; +''', + c_name=c_name(name)) + return ret + + +def gen_params(arg_type, extra): + if not arg_type: + return extra + assert not arg_type.variants + ret = '' + sep = '' + for memb in arg_type.members: + ret += sep + sep = ', ' + if memb.optional: + ret += 'bool has_%s, ' % c_name(memb.name) + ret += '%s %s' % (memb.type.c_param_type(), c_name(memb.name)) + if extra: + ret += sep + extra + return ret + + +def gen_err_check(): + return mcgen(''' + if (err) { + goto out; + } +''') + + # # Common command line parsing # -def parse_command_line(extra_options = "", extra_long_options = []): + +def parse_command_line(extra_options="", extra_long_options=[]): try: opts, args = getopt.gnu_getopt(sys.argv[1:], "chp:o:" + extra_options, ["source", "header", "prefix=", "output-dir="] + extra_long_options) - except getopt.GetoptError, err: + except getopt.GetoptError as err: print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err)) sys.exit(1) @@ -1003,6 +1676,12 @@ def parse_command_line(extra_options = "", extra_long_options = []): for oa in opts: o, a = oa if o in ("-p", "--prefix"): + match = re.match('([A-Za-z_.-][A-Za-z0-9_.-]*)?', a) + if match.end() != len(a): + print >>sys.stderr, \ + "%s: 'funny character '%s' in argument of --prefix" \ + % (sys.argv[0], a[match.end()]) + sys.exit(1) prefix = a elif o in ("-o", "--output-dir"): output_dir = a + "/" @@ -1028,16 +1707,19 @@ def parse_command_line(extra_options = "", extra_long_options = []): # Generate output files with boilerplate # + def open_output(output_dir, do_c, do_h, prefix, c_file, h_file, c_comment, h_comment): + guard = guardname(prefix + h_file) c_file = output_dir + prefix + c_file h_file = output_dir + prefix + h_file - try: - os.makedirs(output_dir) - except os.error, e: - if e.errno != errno.EEXIST: - raise + if output_dir: + try: + os.makedirs(output_dir) + except os.error as e: + if e.errno != errno.EEXIST: + raise def maybe_open(really, name, opt): if really: @@ -1053,7 +1735,7 @@ def open_output(output_dir, do_c, do_h, prefix, c_file, h_file, /* AUTOMATICALLY GENERATED, DO NOT MODIFY */ %(comment)s ''', - comment = c_comment)) + comment=c_comment)) fdecl.write(mcgen(''' /* AUTOMATICALLY GENERATED, DO NOT MODIFY */ @@ -1062,10 +1744,11 @@ def open_output(output_dir, do_c, do_h, prefix, c_file, h_file, #define %(guard)s ''', - comment = h_comment, guard = guardname(h_file))) + comment=h_comment, guard=guard)) return (fdef, fdecl) + def close_output(fdef, fdecl): fdecl.write(''' #endif diff --git a/qemu/scripts/qemu-gdb.py b/qemu/scripts/qemu-gdb.py index 6c7f4fbe5..b3f8e04f7 100644 --- a/qemu/scripts/qemu-gdb.py +++ b/qemu/scripts/qemu-gdb.py @@ -13,73 +13,20 @@ # Contributions after 2012-01-13 are licensed under the terms of the # GNU GPL, version 2 or (at your option) any later version. +# Usage: +# At the (gdb) prompt, type "source scripts/qemu-gdb.py". +# "help qemu" should then list the supported QEMU debug support commands. import gdb -def isnull(ptr): - return ptr == gdb.Value(0).cast(ptr.type) +import os, sys -def int128(p): - return long(p['lo']) + (long(p['hi']) << 64) +# Annoyingly, gdb doesn't put the directory of scripts onto the +# module search path. Do it manually. -def get_fs_base(): - '''Fetch %fs base value using arch_prctl(ARCH_GET_FS)''' - # %rsp - 120 is scratch space according to the SystemV ABI - old = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)') - gdb.execute('call arch_prctl(0x1003, $rsp - 120)', False, True) - fs_base = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)') - gdb.execute('set *(uint64_t*)($rsp - 120) = %s' % old, False, True) - return fs_base +sys.path.append(os.path.dirname(__file__)) -def get_glibc_pointer_guard(): - '''Fetch glibc pointer guard value''' - fs_base = get_fs_base() - return gdb.parse_and_eval('*(uint64_t*)((uint64_t)%s + 0x30)' % fs_base) - -def glibc_ptr_demangle(val, pointer_guard): - '''Undo effect of glibc's PTR_MANGLE()''' - return gdb.parse_and_eval('(((uint64_t)%s >> 0x11) | ((uint64_t)%s << (64 - 0x11))) ^ (uint64_t)%s' % (val, val, pointer_guard)) - -def bt_jmpbuf(jmpbuf): - '''Backtrace a jmpbuf''' - JB_RBX = 0 - JB_RBP = 1 - JB_R12 = 2 - JB_R13 = 3 - JB_R14 = 4 - JB_R15 = 5 - JB_RSP = 6 - JB_PC = 7 - - old_rbx = gdb.parse_and_eval('(uint64_t)$rbx') - old_rbp = gdb.parse_and_eval('(uint64_t)$rbp') - old_rsp = gdb.parse_and_eval('(uint64_t)$rsp') - old_r12 = gdb.parse_and_eval('(uint64_t)$r12') - old_r13 = gdb.parse_and_eval('(uint64_t)$r13') - old_r14 = gdb.parse_and_eval('(uint64_t)$r14') - old_r15 = gdb.parse_and_eval('(uint64_t)$r15') - old_rip = gdb.parse_and_eval('(uint64_t)$rip') - - pointer_guard = get_glibc_pointer_guard() - gdb.execute('set $rbx = %s' % jmpbuf[JB_RBX]) - gdb.execute('set $rbp = %s' % glibc_ptr_demangle(jmpbuf[JB_RBP], pointer_guard)) - gdb.execute('set $rsp = %s' % glibc_ptr_demangle(jmpbuf[JB_RSP], pointer_guard)) - gdb.execute('set $r12 = %s' % jmpbuf[JB_R12]) - gdb.execute('set $r13 = %s' % jmpbuf[JB_R13]) - gdb.execute('set $r14 = %s' % jmpbuf[JB_R14]) - gdb.execute('set $r15 = %s' % jmpbuf[JB_R15]) - gdb.execute('set $rip = %s' % glibc_ptr_demangle(jmpbuf[JB_PC], pointer_guard)) - - gdb.execute('bt') - - gdb.execute('set $rbx = %s' % old_rbx) - gdb.execute('set $rbp = %s' % old_rbp) - gdb.execute('set $rsp = %s' % old_rsp) - gdb.execute('set $r12 = %s' % old_r12) - gdb.execute('set $r13 = %s' % old_r13) - gdb.execute('set $r14 = %s' % old_r14) - gdb.execute('set $r15 = %s' % old_r15) - gdb.execute('set $rip = %s' % old_rip) +from qemugdb import aio, mtree, coroutine class QemuCommand(gdb.Command): '''Prefix for QEMU debug support commands''' @@ -87,78 +34,14 @@ class QemuCommand(gdb.Command): gdb.Command.__init__(self, 'qemu', gdb.COMMAND_DATA, gdb.COMPLETE_NONE, True) -class CoroutineCommand(gdb.Command): - '''Display coroutine backtrace''' - def __init__(self): - gdb.Command.__init__(self, 'qemu coroutine', gdb.COMMAND_DATA, - gdb.COMPLETE_NONE) - - def invoke(self, arg, from_tty): - argv = gdb.string_to_argv(arg) - if len(argv) != 1: - gdb.write('usage: qemu coroutine \n') - return - - coroutine_pointer = gdb.parse_and_eval(argv[0]).cast(gdb.lookup_type('CoroutineUContext').pointer()) - bt_jmpbuf(coroutine_pointer['env']['__jmpbuf']) +QemuCommand() +coroutine.CoroutineCommand() +mtree.MtreeCommand() +aio.HandlersCommand() -class MtreeCommand(gdb.Command): - '''Display the memory tree hierarchy''' - def __init__(self): - gdb.Command.__init__(self, 'qemu mtree', gdb.COMMAND_DATA, - gdb.COMPLETE_NONE) - self.queue = [] - def invoke(self, arg, from_tty): - self.seen = set() - self.queue_root('address_space_memory') - self.queue_root('address_space_io') - self.process_queue() - def queue_root(self, varname): - ptr = gdb.parse_and_eval(varname)['root'] - self.queue.append(ptr) - def process_queue(self): - while self.queue: - ptr = self.queue.pop(0) - if long(ptr) in self.seen: - continue - self.print_item(ptr) - def print_item(self, ptr, offset = gdb.Value(0), level = 0): - self.seen.add(long(ptr)) - addr = ptr['addr'] - addr += offset - size = int128(ptr['size']) - alias = ptr['alias'] - klass = '' - if not isnull(alias): - klass = ' (alias)' - elif not isnull(ptr['ops']): - klass = ' (I/O)' - elif bool(ptr['ram']): - klass = ' (RAM)' - gdb.write('%s%016x-%016x %s%s (@ %s)\n' - % (' ' * level, - long(addr), - long(addr + (size - 1)), - ptr['name'].string(), - klass, - ptr, - ), - gdb.STDOUT) - if not isnull(alias): - gdb.write('%s alias: %s@%016x (@ %s)\n' % - (' ' * level, - alias['name'].string(), - ptr['alias_offset'], - alias, - ), - gdb.STDOUT) - self.queue.append(alias) - subregion = ptr['subregions']['tqh_first'] - level += 1 - while not isnull(subregion): - self.print_item(subregion, addr, level) - subregion = subregion['subregions_link']['tqe_next'] +coroutine.CoroutineSPFunction() +coroutine.CoroutinePCFunction() -QemuCommand() -CoroutineCommand() -MtreeCommand() +# Default to silently passing through SIGUSR1, because QEMU sends it +# to itself a lot. +gdb.execute('handle SIGUSR1 pass noprint nostop') diff --git a/qemu/scripts/qemugdb/__init__.py b/qemu/scripts/qemugdb/__init__.py new file mode 100644 index 000000000..969f552b2 --- /dev/null +++ b/qemu/scripts/qemugdb/__init__.py @@ -0,0 +1,28 @@ +#!/usr/bin/python + +# GDB debugging support +# +# Copyright (c) 2015 Linaro Ltd +# +# 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 +# +# + +# We don't need to do anything in our init file currently. + +""" +Support routines for debugging QEMU under GDB +""" + +__license__ = "GPL version 2 or (at your option) any later version" diff --git a/qemu/scripts/qemugdb/aio.py b/qemu/scripts/qemugdb/aio.py new file mode 100644 index 000000000..2ba00c444 --- /dev/null +++ b/qemu/scripts/qemugdb/aio.py @@ -0,0 +1,58 @@ +#!/usr/bin/python + +# GDB debugging support: aio/iohandler debug +# +# Copyright (c) 2015 Red Hat, Inc. +# +# Author: Dr. David Alan Gilbert +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. +# + +import gdb +from qemugdb import coroutine + +def isnull(ptr): + return ptr == gdb.Value(0).cast(ptr.type) + +def dump_aiocontext(context, verbose): + '''Display a dump and backtrace for an aiocontext''' + cur = context['aio_handlers']['lh_first'] + # Get pointers to functions we're going to process specially + sym_fd_coroutine_enter = gdb.parse_and_eval('fd_coroutine_enter') + + while not isnull(cur): + entry = cur.dereference() + gdb.write('----\n%s\n' % entry) + if verbose and cur['io_read'] == sym_fd_coroutine_enter: + coptr = (cur['opaque'].cast(gdb.lookup_type('FDYieldUntilData').pointer()))['co'] + coptr = coptr.cast(gdb.lookup_type('CoroutineUContext').pointer()) + coroutine.bt_jmpbuf(coptr['env']['__jmpbuf']) + cur = cur['node']['le_next']; + + gdb.write('----\n') + +class HandlersCommand(gdb.Command): + '''Display aio handlers''' + def __init__(self): + gdb.Command.__init__(self, 'qemu handlers', gdb.COMMAND_DATA, + gdb.COMPLETE_NONE) + + def invoke(self, arg, from_tty): + verbose = False + argv = gdb.string_to_argv(arg) + + if len(argv) > 0 and argv[0] == '--verbose': + verbose = True + argv.pop(0) + + if len(argv) > 1: + gdb.write('usage: qemu handlers [--verbose] [handler]\n') + return + + if len(argv) == 1: + handlers_name = argv[0] + else: + handlers_name = 'qemu_aio_context' + dump_aiocontext(gdb.parse_and_eval(handlers_name), verbose) diff --git a/qemu/scripts/qemugdb/coroutine.py b/qemu/scripts/qemugdb/coroutine.py new file mode 100644 index 000000000..ab699794a --- /dev/null +++ b/qemu/scripts/qemugdb/coroutine.py @@ -0,0 +1,119 @@ +#!/usr/bin/python + +# GDB debugging support +# +# Copyright 2012 Red Hat, Inc. and/or its affiliates +# +# Authors: +# Avi Kivity +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. +# +# Contributions after 2012-01-13 are licensed under the terms of the +# GNU GPL, version 2 or (at your option) any later version. + +import gdb + +VOID_PTR = gdb.lookup_type('void').pointer() + +def get_fs_base(): + '''Fetch %fs base value using arch_prctl(ARCH_GET_FS). This is + pthread_self().''' + # %rsp - 120 is scratch space according to the SystemV ABI + old = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)') + gdb.execute('call arch_prctl(0x1003, $rsp - 120)', False, True) + fs_base = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)') + gdb.execute('set *(uint64_t*)($rsp - 120) = %s' % old, False, True) + return fs_base + +def pthread_self(): + '''Fetch pthread_self() from the glibc start_thread function.''' + f = gdb.newest_frame() + while f.name() != 'start_thread': + f = f.older() + if f is None: + return get_fs_base() + + try: + return f.read_var("arg") + except ValueError: + return get_fs_base() + +def get_glibc_pointer_guard(): + '''Fetch glibc pointer guard value''' + fs_base = pthread_self() + return gdb.parse_and_eval('*(uint64_t*)((uint64_t)%s + 0x30)' % fs_base) + +def glibc_ptr_demangle(val, pointer_guard): + '''Undo effect of glibc's PTR_MANGLE()''' + return gdb.parse_and_eval('(((uint64_t)%s >> 0x11) | ((uint64_t)%s << (64 - 0x11))) ^ (uint64_t)%s' % (val, val, pointer_guard)) + +def get_jmpbuf_regs(jmpbuf): + JB_RBX = 0 + JB_RBP = 1 + JB_R12 = 2 + JB_R13 = 3 + JB_R14 = 4 + JB_R15 = 5 + JB_RSP = 6 + JB_PC = 7 + + pointer_guard = get_glibc_pointer_guard() + return {'rbx': jmpbuf[JB_RBX], + 'rbp': glibc_ptr_demangle(jmpbuf[JB_RBP], pointer_guard), + 'rsp': glibc_ptr_demangle(jmpbuf[JB_RSP], pointer_guard), + 'r12': jmpbuf[JB_R12], + 'r13': jmpbuf[JB_R13], + 'r14': jmpbuf[JB_R14], + 'r15': jmpbuf[JB_R15], + 'rip': glibc_ptr_demangle(jmpbuf[JB_PC], pointer_guard) } + +def bt_jmpbuf(jmpbuf): + '''Backtrace a jmpbuf''' + regs = get_jmpbuf_regs(jmpbuf) + old = dict() + + for i in regs: + old[i] = gdb.parse_and_eval('(uint64_t)$%s' % i) + + for i in regs: + gdb.execute('set $%s = %s' % (i, regs[i])) + + gdb.execute('bt') + + for i in regs: + gdb.execute('set $%s = %s' % (i, old[i])) + +def coroutine_to_jmpbuf(co): + coroutine_pointer = co.cast(gdb.lookup_type('CoroutineUContext').pointer()) + return coroutine_pointer['env']['__jmpbuf'] + + +class CoroutineCommand(gdb.Command): + '''Display coroutine backtrace''' + def __init__(self): + gdb.Command.__init__(self, 'qemu coroutine', gdb.COMMAND_DATA, + gdb.COMPLETE_NONE) + + def invoke(self, arg, from_tty): + argv = gdb.string_to_argv(arg) + if len(argv) != 1: + gdb.write('usage: qemu coroutine \n') + return + + bt_jmpbuf(coroutine_to_jmpbuf(gdb.parse_and_eval(argv[0]))) + +class CoroutineSPFunction(gdb.Function): + def __init__(self): + gdb.Function.__init__(self, 'qemu_coroutine_sp') + + def invoke(self, addr): + return get_jmpbuf_regs(coroutine_to_jmpbuf(addr))['rsp'].cast(VOID_PTR) + +class CoroutinePCFunction(gdb.Function): + def __init__(self): + gdb.Function.__init__(self, 'qemu_coroutine_pc') + + def invoke(self, addr): + return get_jmpbuf_regs(coroutine_to_jmpbuf(addr))['rip'].cast(VOID_PTR) diff --git a/qemu/scripts/qemugdb/mtree.py b/qemu/scripts/qemugdb/mtree.py new file mode 100644 index 000000000..cc8131c2e --- /dev/null +++ b/qemu/scripts/qemugdb/mtree.py @@ -0,0 +1,82 @@ +#!/usr/bin/python + +# GDB debugging support +# +# Copyright 2012 Red Hat, Inc. and/or its affiliates +# +# Authors: +# Avi Kivity +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. +# +# Contributions after 2012-01-13 are licensed under the terms of the +# GNU GPL, version 2 or (at your option) any later version. + +# 'qemu mtree' -- display the memory hierarchy + +import gdb + +def isnull(ptr): + return ptr == gdb.Value(0).cast(ptr.type) + +def int128(p): + return int(p['lo']) + (int(p['hi']) << 64) + +class MtreeCommand(gdb.Command): + '''Display the memory tree hierarchy''' + def __init__(self): + gdb.Command.__init__(self, 'qemu mtree', gdb.COMMAND_DATA, + gdb.COMPLETE_NONE) + self.queue = [] + def invoke(self, arg, from_tty): + self.seen = set() + self.queue_root('address_space_memory') + self.queue_root('address_space_io') + self.process_queue() + def queue_root(self, varname): + ptr = gdb.parse_and_eval(varname)['root'] + self.queue.append(ptr) + def process_queue(self): + while self.queue: + ptr = self.queue.pop(0) + if int(ptr) in self.seen: + continue + self.print_item(ptr) + def print_item(self, ptr, offset = gdb.Value(0), level = 0): + self.seen.add(int(ptr)) + addr = ptr['addr'] + addr += offset + size = int128(ptr['size']) + alias = ptr['alias'] + klass = '' + if not isnull(alias): + klass = ' (alias)' + elif not isnull(ptr['ops']): + klass = ' (I/O)' + elif bool(ptr['ram']): + klass = ' (RAM)' + gdb.write('%s%016x-%016x %s%s (@ %s)\n' + % (' ' * level, + int(addr), + int(addr + (size - 1)), + ptr['name'].string(), + klass, + ptr, + ), + gdb.STDOUT) + if not isnull(alias): + gdb.write('%s alias: %s@%016x (@ %s)\n' % + (' ' * level, + alias['name'].string(), + ptr['alias_offset'], + alias, + ), + gdb.STDOUT) + self.queue.append(alias) + subregion = ptr['subregions']['tqh_first'] + level += 1 + while not isnull(subregion): + self.print_item(subregion, addr, level) + subregion = subregion['subregions_link']['tqe_next'] + diff --git a/qemu/scripts/qmp/qemu-ga-client b/qemu/scripts/qmp/qemu-ga-client index 9908f2109..fd056056f 100755 --- a/qemu/scripts/qmp/qemu-ga-client +++ b/qemu/scripts/qmp/qemu-ga-client @@ -259,7 +259,7 @@ def main(address, cmd, args): try: client = QemuGuestAgentClient(address) - except QemuGuestAgent.error, e: + except QemuGuestAgent.error as e: import errno print(e) diff --git a/qemu/scripts/qmp/qmp b/qemu/scripts/qmp/qmp index 1db3c7ffe..514b539a6 100755 --- a/qemu/scripts/qmp/qmp +++ b/qemu/scripts/qmp/qmp @@ -91,8 +91,8 @@ def main(args): try: os.environ['QMP_PATH'] = path os.execvp(fullcmd, [fullcmd] + args) - except OSError, (errno, msg): - if errno == 2: + except OSError as exc: + if exc.errno == 2: print 'Command "%s" not found.' % (fullcmd) return 1 raise diff --git a/qemu/scripts/qmp/qmp-shell b/qemu/scripts/qmp/qmp-shell index 65280d29d..0373b24b2 100755 --- a/qemu/scripts/qmp/qmp-shell +++ b/qemu/scripts/qmp/qmp-shell @@ -29,13 +29,47 @@ # (QEMU) device_add driver=e1000 id=net1 # {u'return': {}} # (QEMU) +# +# key=value pairs also support Python or JSON object literal subset notations, +# without spaces. Dictionaries/objects {} are supported as are arrays []. +# +# example-command arg-name1={'key':'value','obj'={'prop':"value"}} +# +# Both JSON and Python formatting should work, including both styles of +# string literal quotes. Both paradigms of literal values should work, +# including null/true/false for JSON and None/True/False for Python. +# +# +# Transactions have the following multi-line format: +# +# transaction( +# action-name1 [ arg-name1=arg1 ] ... [arg-nameN=argN ] +# ... +# action-nameN [ arg-name1=arg1 ] ... [arg-nameN=argN ] +# ) +# +# One line transactions are also supported: +# +# transaction( action-name1 ... ) +# +# For example: +# +# (QEMU) transaction( +# TRANS> block-dirty-bitmap-add node=drive0 name=bitmap1 +# TRANS> block-dirty-bitmap-clear node=drive0 name=bitmap0 +# TRANS> ) +# {"return": {}} +# (QEMU) +# +# Use the -v and -p options to activate the verbose and pretty-print options, +# which will echo back the properly formatted JSON-compliant QMP that is being +# sent to QEMU, which is useful for debugging and documentation generation. import qmp import json import ast import readline import sys -import pprint class QMPCompleter(list): def complete(self, text, state): @@ -68,11 +102,11 @@ class FuzzyJSON(ast.NodeTransformer): # TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and # _execute_cmd()). Let's design a better one. class QMPShell(qmp.QEMUMonitorProtocol): - def __init__(self, address, pp=None): + def __init__(self, address, pretty=False): qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address)) self._greeting = None self._completer = None - self._pp = pp + self._pretty = pretty self._transmode = False self._actions = list() @@ -196,16 +230,16 @@ class QMPShell(qmp.QEMUMonitorProtocol): return qmpcmd def _print(self, qmp): - jsobj = json.dumps(qmp) - if self._pp is not None: - self._pp.pprint(jsobj) - else: - print str(jsobj) + indent = None + if self._pretty: + indent = 4 + jsobj = json.dumps(qmp, indent=indent) + print str(jsobj) def _execute_cmd(self, cmdline): try: qmpcmd = self.__build_cmd(cmdline) - except Exception, e: + except Exception as e: print 'Error while parsing command line: %s' % e print 'command format: ', print '[arg-name1=arg1] ... [arg-nameN=argN]' @@ -342,7 +376,7 @@ def main(): addr = '' qemu = None hmp = False - pp = None + pretty = False verbose = False try: @@ -352,9 +386,7 @@ def main(): fail_cmdline(arg) hmp = True elif arg == "-p": - if pp is not None: - fail_cmdline(arg) - pp = pprint.PrettyPrinter(indent=4) + pretty = True elif arg == "-v": verbose = True else: @@ -363,7 +395,7 @@ def main(): if hmp: qemu = HMPShell(arg) else: - qemu = QMPShell(arg, pp) + qemu = QMPShell(arg, pretty) addr = arg if qemu is None: diff --git a/qemu/scripts/qmp/qmp.py b/qemu/scripts/qmp/qmp.py index 1d38e3e9e..779332f32 100644 --- a/qemu/scripts/qmp/qmp.py +++ b/qemu/scripts/qmp/qmp.py @@ -92,7 +92,7 @@ class QEMUMonitorProtocol: self.__sock.setblocking(0) try: self.__json_read() - except socket.error, err: + except socket.error as err: if err[0] == errno.EAGAIN: # No data available pass @@ -150,7 +150,7 @@ class QEMUMonitorProtocol: """ try: self.__sock.sendall(json.dumps(qmp_cmd)) - except socket.error, err: + except socket.error as err: if err[0] == errno.EPIPE: return raise socket.error(err) diff --git a/qemu/scripts/texi2pod.pl b/qemu/scripts/texi2pod.pl index 94097fb06..8767662d3 100755 --- a/qemu/scripts/texi2pod.pl +++ b/qemu/scripts/texi2pod.pl @@ -317,7 +317,7 @@ while(<$inf>) { @columns = (); for $column (split (/\s*\@tab\s*/, $1)) { # @strong{...} is used a @headitem work-alike - $column =~ s/^\@strong{(.*)}$/$1/; + $column =~ s/^\@strong\{(.*)\}$/$1/; push @columns, $column; } $_ = "\n=item ".join (" : ", @columns)."\n"; diff --git a/qemu/scripts/tracetool.py b/qemu/scripts/tracetool.py index 83bde7bda..7b82959e8 100755 --- a/qemu/scripts/tracetool.py +++ b/qemu/scripts/tracetool.py @@ -71,7 +71,7 @@ def main(args): try: opts, args = getopt.getopt(args[1:], "", long_opts) - except getopt.GetoptError, err: + except getopt.GetoptError as err: error_opt(str(err)) check_backends = False @@ -132,7 +132,7 @@ def main(args): try: tracetool.generate(sys.stdin, arg_format, arg_backends, binary=binary, probe_prefix=probe_prefix) - except tracetool.TracetoolError, e: + except tracetool.TracetoolError as e: error_opt(str(e)) if __name__ == "__main__": diff --git a/qemu/scripts/tracetool/__init__.py b/qemu/scripts/tracetool/__init__.py index 181675f00..be24039c5 100644 --- a/qemu/scripts/tracetool/__init__.py +++ b/qemu/scripts/tracetool/__init__.py @@ -6,7 +6,7 @@ Machinery for generating tracing-related intermediate files. """ __author__ = "Lluís Vilanova " -__copyright__ = "Copyright 2012-2014, Lluís Vilanova " +__copyright__ = "Copyright 2012-2016, Lluís Vilanova " __license__ = "GPL version 2 or (at your option) any later version" __maintainer__ = "Stefan Hajnoczi" @@ -50,9 +50,14 @@ class Arguments: Parameters ---------- args : - List of (type, name) tuples. + List of (type, name) tuples or Arguments objects. """ - self._args = args + self._args = [] + for arg in args: + if isinstance(arg, Arguments): + self._args.extend(arg._args) + else: + self._args.append(arg) def copy(self): """Create a new copy.""" @@ -83,6 +88,12 @@ class Arguments: res.append((arg_type, identifier)) return Arguments(res) + def __getitem__(self, index): + if isinstance(index, slice): + return Arguments(self._args[index]) + else: + return self._args[index] + def __iter__(self): """Iterate over the (type, name) pairs.""" return iter(self._args) @@ -110,6 +121,10 @@ class Arguments: """List of argument types.""" return [ type_ for type_, _ in self._args ] + def casted(self): + """List of argument names casted to their type.""" + return ["(%s)%s" % (type_, name) for type_, name in self._args] + def transform(self, *trans): """Return a new Arguments instance with transformed types. @@ -146,9 +161,10 @@ class Event(object): "(?:(?:(?P\".+),)?\s*(?P\".+))?" "\s*") - _VALID_PROPS = set(["disable", "tcg", "tcg-trans", "tcg-exec"]) + _VALID_PROPS = set(["disable", "tcg", "tcg-trans", "tcg-exec", "vcpu"]) - def __init__(self, name, props, fmt, args, orig=None): + def __init__(self, name, props, fmt, args, orig=None, + event_trans=None, event_exec=None): """ Parameters ---------- @@ -161,13 +177,19 @@ class Event(object): args : Arguments Event arguments. orig : Event or None - Original Event before transformation. + Original Event before transformation/generation. + event_trans : Event or None + Generated translation-time event ("tcg" property). + event_exec : Event or None + Generated execution-time event ("tcg" property). """ self.name = name self.properties = props self.fmt = fmt self.args = args + self.event_trans = event_trans + self.event_exec = event_exec if orig is None: self.original = weakref.ref(self) @@ -183,7 +205,7 @@ class Event(object): def copy(self): """Create a new copy.""" return Event(self.name, list(self.properties), self.fmt, - self.args.copy(), self) + self.args.copy(), self, self.event_trans, self.event_exec) @staticmethod def build(line_str): @@ -215,7 +237,13 @@ class Event(object): if "tcg" in props and isinstance(fmt, str): raise ValueError("Events with 'tcg' property must have two formats") - return Event(name, props, fmt, args) + event = Event(name, props, fmt, args) + + # add implicit arguments when using the 'vcpu' property + import tracetool.vcpu + event = tracetool.vcpu.transform_event(event) + + return event def __repr__(self): """Evaluable string representation for this object.""" @@ -270,6 +298,7 @@ def _read_events(fobj): event_trans.name += "_trans" event_trans.properties += ["tcg-trans"] event_trans.fmt = event.fmt[0] + # ignore TCG arguments args_trans = [] for atrans, aorig in zip( event_trans.transform(tracetool.transform.TCG_2_HOST).args, @@ -277,13 +306,12 @@ def _read_events(fobj): if atrans == aorig: args_trans.append(atrans) event_trans.args = Arguments(args_trans) - event_trans = event_trans.copy() event_exec = event.copy() event_exec.name += "_exec" event_exec.properties += ["tcg-exec"] event_exec.fmt = event.fmt[1] - event_exec = event_exec.transform(tracetool.transform.TCG_2_HOST) + event_exec.args = event_exec.args.transform(tracetool.transform.TCG_2_HOST) new_event = [event_trans, event_exec] event.event_trans, event.event_exec = new_event diff --git a/qemu/scripts/tracetool/backend/log.py b/qemu/scripts/tracetool/backend/log.py new file mode 100644 index 000000000..e409b7326 --- /dev/null +++ b/qemu/scripts/tracetool/backend/log.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Stderr built-in backend. +""" + +__author__ = "Lluís Vilanova " +__copyright__ = "Copyright 2012-2014, Lluís Vilanova " +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +from tracetool import out + + +PUBLIC = True + + +def generate_h_begin(events): + out('#include "trace/control.h"', + '#include "qemu/log.h"', + '') + + +def generate_h(event): + argnames = ", ".join(event.args.names()) + if len(event.args) > 0: + argnames = ", " + argnames + + out(' if (trace_event_get_state(%(event_id)s)) {', + ' struct timeval _now;', + ' gettimeofday(&_now, NULL);', + ' qemu_log_mask(LOG_TRACE, "%%d@%%zd.%%06zd:%(name)s " %(fmt)s "\\n",', + ' getpid(),', + ' (size_t)_now.tv_sec, (size_t)_now.tv_usec', + ' %(argnames)s);', + ' }', + event_id="TRACE_" + event.name.upper(), + name=event.name, + fmt=event.fmt.rstrip("\n"), + argnames=argnames) diff --git a/qemu/scripts/tracetool/backend/simple.py b/qemu/scripts/tracetool/backend/simple.py index e8c2cd57e..3246c2001 100644 --- a/qemu/scripts/tracetool/backend/simple.py +++ b/qemu/scripts/tracetool/backend/simple.py @@ -42,7 +42,8 @@ def generate_h(event): def generate_c_begin(events): - out('#include "trace.h"', + out('#include "qemu/osdep.h"', + '#include "trace.h"', '#include "trace/control.h"', '#include "trace/simple.h"', '') diff --git a/qemu/scripts/tracetool/backend/stderr.py b/qemu/scripts/tracetool/backend/stderr.py deleted file mode 100644 index ca5805462..000000000 --- a/qemu/scripts/tracetool/backend/stderr.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -Stderr built-in backend. -""" - -__author__ = "Lluís Vilanova " -__copyright__ = "Copyright 2012-2014, Lluís Vilanova " -__license__ = "GPL version 2 or (at your option) any later version" - -__maintainer__ = "Stefan Hajnoczi" -__email__ = "stefanha@linux.vnet.ibm.com" - - -from tracetool import out - - -PUBLIC = True - - -def generate_h_begin(events): - out('#include ', - '#include ', - '#include ', - '#include ', - '#include "trace/control.h"', - '') - - -def generate_h(event): - argnames = ", ".join(event.args.names()) - if len(event.args) > 0: - argnames = ", " + argnames - - out(' if (trace_event_get_state(%(event_id)s)) {', - ' struct timeval _now;', - ' gettimeofday(&_now, NULL);', - ' fprintf(stderr, "%%d@%%zd.%%06zd:%(name)s " %(fmt)s "\\n",', - ' getpid(),', - ' (size_t)_now.tv_sec, (size_t)_now.tv_usec', - ' %(argnames)s);', - ' }', - event_id="TRACE_" + event.name.upper(), - name=event.name, - fmt=event.fmt.rstrip("\n"), - argnames=argnames) diff --git a/qemu/scripts/tracetool/format/events_c.py b/qemu/scripts/tracetool/format/events_c.py index 2d97fa310..1cc6a49a7 100644 --- a/qemu/scripts/tracetool/format/events_c.py +++ b/qemu/scripts/tracetool/format/events_c.py @@ -19,6 +19,7 @@ from tracetool import out def generate(events, backend): out('/* This file is autogenerated by tracetool, do not edit. */', '', + '#include "qemu/osdep.h"', '#include "trace.h"', '#include "trace/generated-events.h"', '#include "trace/control.h"', @@ -27,7 +28,7 @@ def generate(events, backend): out('TraceEvent trace_events[TRACE_EVENT_COUNT] = {') for e in events: - out(' { .id = %(id)s, .name = \"%(name)s\", .sstate = %(sstate)s, .dstate = 0 },', + out(' { .id = %(id)s, .name = \"%(name)s\", .sstate = %(sstate)s },', id = "TRACE_" + e.name.upper(), name = e.name, sstate = "TRACE_%s_ENABLED" % e.name.upper()) diff --git a/qemu/scripts/tracetool/format/events_h.py b/qemu/scripts/tracetool/format/events_h.py index 9f114a349..4529263e0 100644 --- a/qemu/scripts/tracetool/format/events_h.py +++ b/qemu/scripts/tracetool/format/events_h.py @@ -6,7 +6,7 @@ trace/generated-events.h """ __author__ = "Lluís Vilanova " -__copyright__ = "Copyright 2012-2014, Lluís Vilanova " +__copyright__ = "Copyright 2012-2016, Lluís Vilanova " __license__ = "GPL version 2 or (at your option) any later version" __maintainer__ = "Stefan Hajnoczi" @@ -21,8 +21,6 @@ def generate(events, backend): '', '#ifndef TRACE__GENERATED_EVENTS_H', '#define TRACE__GENERATED_EVENTS_H', - '', - '#include ', '') # event identifiers @@ -43,7 +41,7 @@ def generate(events, backend): if "tcg-trans" in e.properties: # a single define for the two "sub-events" out('#define TRACE_%(name)s_ENABLED %(enabled)d', - name=e.original.original.name.upper(), + name=e.original.name.upper(), enabled=enabled) out('#define TRACE_%s_ENABLED %d' % (e.name.upper(), enabled)) diff --git a/qemu/scripts/tracetool/format/h.py b/qemu/scripts/tracetool/format/h.py index 9b3943002..083540621 100644 --- a/qemu/scripts/tracetool/format/h.py +++ b/qemu/scripts/tracetool/format/h.py @@ -6,7 +6,7 @@ trace/generated-tracers.h """ __author__ = "Lluís Vilanova " -__copyright__ = "Copyright 2012-2014, Lluís Vilanova " +__copyright__ = "Copyright 2012-2016, Lluís Vilanova " __license__ = "GPL version 2 or (at your option) any later version" __maintainer__ = "Stefan Hajnoczi" diff --git a/qemu/scripts/tracetool/format/tcg_h.py b/qemu/scripts/tracetool/format/tcg_h.py index f676b6662..e2331f251 100644 --- a/qemu/scripts/tracetool/format/tcg_h.py +++ b/qemu/scripts/tracetool/format/tcg_h.py @@ -6,14 +6,25 @@ Generate .h file for TCG code generation. """ __author__ = "Lluís Vilanova " -__copyright__ = "Copyright 2012-2014, Lluís Vilanova " +__copyright__ = "Copyright 2012-2016, Lluís Vilanova " __license__ = "GPL version 2 or (at your option) any later version" __maintainer__ = "Stefan Hajnoczi" __email__ = "stefanha@linux.vnet.ibm.com" -from tracetool import out +from tracetool import out, Arguments +import tracetool.vcpu + + +def vcpu_transform_args(args): + assert len(args) == 1 + return Arguments([ + args, + # NOTE: this name must be kept in sync with the one in "tcg_h" + # NOTE: Current helper code uses TCGv_env (CPUArchState*) + ("TCGv_env", "__tcg_" + args.names()[0]), + ]) def generate(events, backend): @@ -23,8 +34,6 @@ def generate(events, backend): '#ifndef TRACE__GENERATED_TCG_TRACERS_H', '#define TRACE__GENERATED_TCG_TRACERS_H', '', - '#include ', - '', '#include "trace.h"', '#include "exec/helper-proto.h"', '', @@ -35,21 +44,21 @@ def generate(events, backend): if "tcg-trans" not in e.properties: continue - # get the original event definition - e = e.original.original - out('static inline void %(name_tcg)s(%(args)s)', '{', - name_tcg=e.api(e.QEMU_TRACE_TCG), - args=e.args) + name_tcg=e.original.api(e.QEMU_TRACE_TCG), + args=tracetool.vcpu.transform_args("tcg_h", e.original)) if "disable" not in e.properties: + args_trans = e.original.event_trans.args + args_exec = tracetool.vcpu.transform_args( + "tcg_helper_c", e.original.event_exec, "wrapper") out(' %(name_trans)s(%(argnames_trans)s);', ' gen_helper_%(name_exec)s(%(argnames_exec)s);', - name_trans=e.event_trans.api(e.QEMU_TRACE), - name_exec=e.event_exec.api(e.QEMU_TRACE), - argnames_trans=", ".join(e.event_trans.args.names()), - argnames_exec=", ".join(e.event_exec.args.names())) + name_trans=e.original.event_trans.api(e.QEMU_TRACE), + name_exec=e.original.event_exec.api(e.QEMU_TRACE), + argnames_trans=", ".join(args_trans.names()), + argnames_exec=", ".join(args_exec.names())) out('}') diff --git a/qemu/scripts/tracetool/format/tcg_helper_c.py b/qemu/scripts/tracetool/format/tcg_helper_c.py index 96655a059..a089b0bf0 100644 --- a/qemu/scripts/tracetool/format/tcg_helper_c.py +++ b/qemu/scripts/tracetool/format/tcg_helper_c.py @@ -6,15 +6,38 @@ Generate trace/generated-helpers.c. """ __author__ = "Lluís Vilanova " -__copyright__ = "Copyright 2012-2014, Lluís Vilanova " +__copyright__ = "Copyright 2012-2016, Lluís Vilanova " __license__ = "GPL version 2 or (at your option) any later version" __maintainer__ = "Stefan Hajnoczi" __email__ = "stefanha@linux.vnet.ibm.com" -from tracetool import out +from tracetool import Arguments, out from tracetool.transform import * +import tracetool.vcpu + + +def vcpu_transform_args(args, mode): + assert len(args) == 1 + # NOTE: this name must be kept in sync with the one in "tcg_h" + args = Arguments([(args.types()[0], "__tcg_" + args.names()[0])]) + if mode == "code": + return Arguments([ + # Does cast from helper requirements to tracing types + ("CPUState *", "ENV_GET_CPU(%s)" % args.names()[0]), + ]) + else: + args = Arguments([ + # NOTE: Current helper code uses TCGv_env (CPUArchState*) + ("CPUArchState *", args.names()[0]), + ]) + if mode == "header": + return args + elif mode == "wrapper": + return args.transform(HOST_2_TCG) + else: + assert False def generate(events, backend): @@ -23,6 +46,7 @@ def generate(events, backend): out('/* This file is autogenerated by tracetool, do not edit. */', '', + '#include "qemu/osdep.h"', '#include "qemu-common.h"', '#include "trace.h"', '#include "exec/helper-proto.h"', @@ -33,18 +57,18 @@ def generate(events, backend): if "tcg-exec" not in e.properties: continue - # tracetool.generate always transforms types to host - e_args = e.original.args - - values = ["(%s)%s" % (t, n) - for t, n in e.args.transform(TCG_2_TCG_HELPER_DEF)] + e_args_api = tracetool.vcpu.transform_args( + "tcg_helper_c", e.original, "header").transform( + HOST_2_TCG_COMPAT, TCG_2_TCG_HELPER_DEF) + e_args_call = tracetool.vcpu.transform_args( + "tcg_helper_c", e, "code") - out('void %(name_tcg)s(%(args)s)', + out('void %(name_tcg)s(%(args_api)s)', '{', - ' %(name)s(%(values)s);', + ' %(name)s(%(args_call)s);', '}', name_tcg="helper_%s_proxy" % e.api(), name=e.api(), - args=e_args.transform(HOST_2_TCG_COMPAT, TCG_2_TCG_HELPER_DEF), - values=", ".join(values), + args_api=e_args_api, + args_call=", ".join(e_args_call.casted()), ) diff --git a/qemu/scripts/tracetool/format/tcg_helper_h.py b/qemu/scripts/tracetool/format/tcg_helper_h.py index a8ba7ba8e..dc76c15eb 100644 --- a/qemu/scripts/tracetool/format/tcg_helper_h.py +++ b/qemu/scripts/tracetool/format/tcg_helper_h.py @@ -6,7 +6,7 @@ Generate trace/generated-helpers.h. """ __author__ = "Lluís Vilanova " -__copyright__ = "Copyright 2012-2014, Lluís Vilanova " +__copyright__ = "Copyright 2012-2016, Lluís Vilanova " __license__ = "GPL version 2 or (at your option) any later version" __maintainer__ = "Stefan Hajnoczi" @@ -15,6 +15,7 @@ __email__ = "stefanha@linux.vnet.ibm.com" from tracetool import out from tracetool.transform import * +import tracetool.vcpu def generate(events, backend): @@ -29,11 +30,9 @@ def generate(events, backend): if "tcg-exec" not in e.properties: continue - # tracetool.generate always transforms types to host - e_args = e.original.args - # TCG helper proxy declaration fmt = "DEF_HELPER_FLAGS_%(argc)d(%(name)s, %(flags)svoid%(types)s)" + e_args = tracetool.vcpu.transform_args("tcg_helper_c", e.original, "header") args = e_args.transform(HOST_2_TCG_COMPAT, HOST_2_TCG, TCG_2_TCG_HELPER_DECL) types = ", ".join(args.types()) diff --git a/qemu/scripts/tracetool/format/tcg_helper_wrapper_h.py b/qemu/scripts/tracetool/format/tcg_helper_wrapper_h.py index cac5a878f..020f4422a 100644 --- a/qemu/scripts/tracetool/format/tcg_helper_wrapper_h.py +++ b/qemu/scripts/tracetool/format/tcg_helper_wrapper_h.py @@ -6,7 +6,7 @@ Generate trace/generated-helpers-wrappers.h. """ __author__ = "Lluís Vilanova " -__copyright__ = "Copyright 2012-2014, Lluís Vilanova " +__copyright__ = "Copyright 2012-2016, Lluís Vilanova " __license__ = "GPL version 2 or (at your option) any later version" __maintainer__ = "Stefan Hajnoczi" @@ -15,6 +15,7 @@ __email__ = "stefanha@linux.vnet.ibm.com" from tracetool import out from tracetool.transform import * +import tracetool.vcpu def generate(events, backend): @@ -33,7 +34,7 @@ def generate(events, backend): continue # tracetool.generate always transforms types to host - e_args = e.original.args + e_args = tracetool.vcpu.transform_args("tcg_helper_c", e.original, "wrapper") # mixed-type to TCG helper bridge args_tcg_compat = e_args.transform(HOST_2_TCG_COMPAT) diff --git a/qemu/scripts/tracetool/format/ust_events_c.py b/qemu/scripts/tracetool/format/ust_events_c.py index bc970936b..9967c7a82 100644 --- a/qemu/scripts/tracetool/format/ust_events_c.py +++ b/qemu/scripts/tracetool/format/ust_events_c.py @@ -21,6 +21,8 @@ def generate(events, backend): if "disabled" not in e.properties] out('/* This file is autogenerated by tracetool, do not edit. */', + '', + '#include "qemu/osdep.h"', '', '#define TRACEPOINT_DEFINE', '#define TRACEPOINT_CREATE_PROBES', diff --git a/qemu/scripts/tracetool/transform.py b/qemu/scripts/tracetool/transform.py index fc5e679ed..e18b05315 100644 --- a/qemu/scripts/tracetool/transform.py +++ b/qemu/scripts/tracetool/transform.py @@ -6,7 +6,7 @@ Type-transformation rules. """ __author__ = "Lluís Vilanova " -__copyright__ = "Copyright 2012-2014, Lluís Vilanova " +__copyright__ = "Copyright 2012-2016, Lluís Vilanova " __license__ = "GPL version 2 or (at your option) any later version" __maintainer__ = "Stefan Hajnoczi" @@ -98,6 +98,7 @@ HOST_2_TCG = { "uint32_t": "TCGv_i32", "uint64_t": "TCGv_i64", "void *" : "TCGv_ptr", + "CPUArchState *": "TCGv_env", None: _host_2_tcg, } @@ -130,6 +131,7 @@ TCG_2_TCG_HELPER_DECL = { "TCGv_ptr": "ptr", "TCGv_i32": "i32", "TCGv_i64": "i64", + "TCGv_env": "env", None: _tcg_2_tcg_helper_decl_error, } diff --git a/qemu/scripts/tracetool/vcpu.py b/qemu/scripts/tracetool/vcpu.py new file mode 100644 index 000000000..452c7f589 --- /dev/null +++ b/qemu/scripts/tracetool/vcpu.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Generic management for the 'vcpu' property. + +""" + +__author__ = "Lluís Vilanova " +__copyright__ = "Copyright 2016, Lluís Vilanova " +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +from tracetool import Arguments, try_import + + +def transform_event(event): + """Transform event to comply with the 'vcpu' property (if present).""" + if "vcpu" in event.properties: + # events with 'tcg-trans' and 'tcg-exec' are auto-generated from + # already-patched events + assert "tcg-trans" not in event.properties + assert "tcg-exec" not in event.properties + + event.args = Arguments([("CPUState *", "__cpu"), event.args]) + if "tcg" in event.properties: + fmt = "\"cpu=%p \"" + event.fmt = [fmt + event.fmt[0], + fmt + event.fmt[1]] + else: + fmt = "\"cpu=%p \"" + event.fmt = fmt + event.fmt + return event + + +def transform_args(format, event, *args, **kwargs): + """Transforms the arguments to suit the specified format. + + The format module must implement function 'vcpu_args', which receives the + implicit arguments added by the 'vcpu' property, and must return suitable + arguments for the given format. + + The function is only called for events with the 'vcpu' property. + + Parameters + ========== + format : str + Format module name. + event : Event + args, kwargs + Passed to 'vcpu_transform_args'. + + Returns + ======= + Arguments + The transformed arguments, including the non-implicit ones. + + """ + if "vcpu" in event.properties: + ok, func = try_import("tracetool.format." + format, + "vcpu_transform_args") + assert ok + assert func + return Arguments([func(event.args[:1], *args, **kwargs), + event.args[1:]]) + else: + return event.args diff --git a/qemu/scripts/update-acpi.sh b/qemu/scripts/update-acpi.sh deleted file mode 100644 index b5f05ff3c..000000000 --- a/qemu/scripts/update-acpi.sh +++ /dev/null @@ -1,4 +0,0 @@ -cd x86_64-softmmu -for file in hw/i386/*.hex; do - cp -f $file ../$file.generated -done diff --git a/qemu/scripts/update-linux-headers.sh b/qemu/scripts/update-linux-headers.sh index f0e830c2d..f7d62d974 100755 --- a/qemu/scripts/update-linux-headers.sh +++ b/qemu/scripts/update-linux-headers.sh @@ -28,38 +28,35 @@ if [ -z "$output" ]; then output="$PWD" fi -cp_virtio() { - from=$1 +cp_portable() { + f=$1 to=$2 - virtio=$(find "$from" -name '*virtio*h' -o -name "input.h" -o -name "pci_regs.h") - if [ "$virtio" ]; then - rm -rf "$to" - mkdir -p "$to" - for f in $virtio; do - if - grep '#include' "$f" | grep -v -e 'linux/virtio' \ - -e 'linux/types' \ - -e 'linux/if_ether' \ - -e 'sys/' \ - > /dev/null - then - echo "Unexpected #include in input file $f". - exit 2 - fi - - header=$(basename "$f"); - sed -e 's/__u\([0-9][0-9]*\)/uint\1_t/g' \ - -e 's/__s\([0-9][0-9]*\)/int\1_t/g' \ - -e 's/__le\([0-9][0-9]*\)/uint\1_t/g' \ - -e 's/__be\([0-9][0-9]*\)/uint\1_t/g' \ - -e 's/]*\)>/"standard-headers\/linux\/\1"/' \ - -e 's/__bitwise__//' \ - -e 's/__attribute__((packed))/QEMU_PACKED/' \ - -e 's/__inline__/inline/' \ - -e '/sys\/ioctl.h/d' \ - "$f" > "$to/$header"; - done + if + grep '#include' "$f" | grep -v -e 'linux/virtio' \ + -e 'linux/types' \ + -e 'stdint' \ + -e 'linux/if_ether' \ + -e 'input-event-codes' \ + -e 'sys/' \ + > /dev/null + then + echo "Unexpected #include in input file $f". + exit 2 fi + + header=$(basename "$f"); + sed -e 's/__u\([0-9][0-9]*\)/uint\1_t/g' \ + -e 's/__s\([0-9][0-9]*\)/int\1_t/g' \ + -e 's/__le\([0-9][0-9]*\)/uint\1_t/g' \ + -e 's/__be\([0-9][0-9]*\)/uint\1_t/g' \ + -e 's/"\(input-event-codes\.h\)"/"standard-headers\/linux\/\1"/' \ + -e 's/]*\)>/"standard-headers\/linux\/\1"/' \ + -e 's/__bitwise__//' \ + -e 's/__attribute__((packed))/QEMU_PACKED/' \ + -e 's/__inline__/inline/' \ + -e '/sys\/ioctl.h/d' \ + -e 's/SW_MAX/SW_MAX_/' \ + "$f" > "$to/$header"; } # This will pick up non-directories too (eg "Kconfig") but we will @@ -74,7 +71,7 @@ for arch in $ARCHLIST; do fi # Blacklist architectures which have KVM headers but are actually dead - if [ "$arch" = "ia64" ]; then + if [ "$arch" = "ia64" -o "$arch" = "mips" ]; then continue fi @@ -82,23 +79,31 @@ for arch in $ARCHLIST; do rm -rf "$output/linux-headers/asm-$arch" mkdir -p "$output/linux-headers/asm-$arch" - for header in kvm.h kvm_para.h; do + for header in kvm.h kvm_para.h unistd.h; do cp "$tmpdir/include/asm/$header" "$output/linux-headers/asm-$arch" done - if [ $arch = x86 ]; then - cp "$tmpdir/include/asm/hyperv.h" "$output/linux-headers/asm-x86" - fi if [ $arch = powerpc ]; then cp "$tmpdir/include/asm/epapr_hcalls.h" "$output/linux-headers/asm-powerpc/" fi - cp_virtio "$tmpdir/include/asm" "$output/include/standard-headers/asm-$arch" + rm -rf "$output/include/standard-headers/asm-$arch" + mkdir -p "$output/include/standard-headers/asm-$arch" + if [ $arch = s390 ]; then + cp_portable "$tmpdir/include/asm/kvm_virtio.h" "$output/include/standard-headers/asm-s390/" + cp_portable "$tmpdir/include/asm/virtio-ccw.h" "$output/include/standard-headers/asm-s390/" + fi + if [ $arch = x86 ]; then + cp_portable "$tmpdir/include/asm/hyperv.h" "$output/include/standard-headers/asm-x86/" + cp "$tmpdir/include/asm/unistd_32.h" "$output/linux-headers/asm-x86/" + cp "$tmpdir/include/asm/unistd_x32.h" "$output/linux-headers/asm-x86/" + cp "$tmpdir/include/asm/unistd_64.h" "$output/linux-headers/asm-x86/" + fi done rm -rf "$output/linux-headers/linux" mkdir -p "$output/linux-headers/linux" for header in kvm.h kvm_para.h vfio.h vhost.h \ - psci.h; do + psci.h userfaultfd.h; do cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux" done rm -rf "$output/linux-headers/asm-generic" @@ -112,6 +117,9 @@ else cp "$linux/COPYING" "$output/linux-headers" fi +cat <$output/linux-headers/asm-x86/hyperv.h +#include "standard-headers/asm-x86/hyperv.h" +EOF cat <$output/linux-headers/linux/virtio_config.h #include "standard-headers/linux/virtio_config.h" EOF @@ -119,11 +127,18 @@ cat <$output/linux-headers/linux/virtio_ring.h #include "standard-headers/linux/virtio_ring.h" EOF -cp_virtio "$tmpdir/include/linux/" "$output/include/standard-headers/linux" +rm -rf "$output/include/standard-headers/linux" +mkdir -p "$output/include/standard-headers/linux" +for i in "$tmpdir"/include/linux/*virtio*.h "$tmpdir/include/linux/input.h" \ + "$tmpdir/include/linux/input-event-codes.h" \ + "$tmpdir/include/linux/pci_regs.h"; do + cp_portable "$i" "$output/include/standard-headers/linux" +done cat <$output/include/standard-headers/linux/types.h -#include -#include "qemu/compiler.h" +/* For QEMU all types are already defined via osdep.h, so this + * header does not need to do anything. + */ EOF cat <$output/include/standard-headers/linux/if_ether.h #define ETH_ALEN 6 diff --git a/qemu/scripts/vmstate-static-checker.py b/qemu/scripts/vmstate-static-checker.py index b6c0bbead..b5ecaf644 100755 --- a/qemu/scripts/vmstate-static-checker.py +++ b/qemu/scripts/vmstate-static-checker.py @@ -99,6 +99,7 @@ def get_changed_sec_name(sec): # Section names can change -- see commit 292b1634 for an example. changes = { "ICH9 LPC": "ICH9-LPC", + "e1000-82540em": "e1000", } for item in changes: diff --git a/qemu/slirp/Makefile.objs b/qemu/slirp/Makefile.objs index 2daa9dc58..6748e4f60 100644 --- a/qemu/slirp/Makefile.objs +++ b/qemu/slirp/Makefile.objs @@ -1,3 +1,5 @@ -common-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o dnssearch.o +common-obj-y = cksum.o if.o ip_icmp.o ip6_icmp.o ip6_input.o ip6_output.o \ + ip_input.o ip_output.o dnssearch.o common-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o -common-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o arp_table.o +common-obj-y += tcp_subr.o tcp_timer.o udp.o udp6.o bootp.o tftp.o arp_table.o \ + ndp_table.o diff --git a/qemu/slirp/arp_table.c b/qemu/slirp/arp_table.c index bcaeb4486..354704355 100644 --- a/qemu/slirp/arp_table.c +++ b/qemu/slirp/arp_table.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "slirp.h" void arp_table_add(Slirp *slirp, uint32_t ip_addr, uint8_t ethaddr[ETH_ALEN]) diff --git a/qemu/slirp/bootp.c b/qemu/slirp/bootp.c index b7db9fa33..7b3232bdc 100644 --- a/qemu/slirp/bootp.c +++ b/qemu/slirp/bootp.c @@ -21,8 +21,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include +#if defined(_WIN32) +/* Windows ntohl() returns an u_long value. + * Add a type cast to match the format strings. */ +# define ntohl(n) ((uint32_t)ntohl(n)) +#endif + /* XXX: only DHCP is supported */ #define LEASE_TIME (24 * 3600) @@ -155,7 +162,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) dhcp_decode(bp, &dhcp_msg_type, &preq_addr); DPRINTF("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type); if (preq_addr.s_addr != htonl(0L)) - DPRINTF(" req_addr=%08x\n", ntohl(preq_addr.s_addr)); + DPRINTF(" req_addr=%08" PRIx32 "\n", ntohl(preq_addr.s_addr)); else DPRINTF("\n"); @@ -234,7 +241,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) q += 4; if (bc) { - DPRINTF("%s addr=%08x\n", + DPRINTF("%s addr=%08" PRIx32 "\n", (dhcp_msg_type == DHCPDISCOVER) ? "offered" : "ack'ed", ntohl(daddr.sin_addr.s_addr)); @@ -302,7 +309,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) } else { static const char nak_msg[] = "requested address not available"; - DPRINTF("nak'ed addr=%08x\n", ntohl(preq_addr.s_addr)); + DPRINTF("nak'ed addr=%08" PRIx32 "\n", ntohl(preq_addr.s_addr)); *q++ = RFC2132_MSG_TYPE; *q++ = 1; @@ -319,7 +326,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) m->m_len = sizeof(struct bootp_t) - sizeof(struct ip) - sizeof(struct udphdr); - udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); + udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); } void bootp_input(struct mbuf *m) diff --git a/qemu/slirp/cksum.c b/qemu/slirp/cksum.c index 63286600e..2ad0e6540 100644 --- a/qemu/slirp/cksum.c +++ b/qemu/slirp/cksum.c @@ -30,6 +30,7 @@ * in_cksum.c,v 1.2 1994/08/02 07:48:16 davidg Exp */ +#include "qemu/osdep.h" #include /* @@ -137,3 +138,28 @@ cont: REDUCE; return (~sum & 0xffff); } + +int ip6_cksum(struct mbuf *m) +{ + /* TODO: Optimize this by being able to pass the ip6_pseudohdr to cksum + * separately from the mbuf */ + struct ip6 save_ip, *ip = mtod(m, struct ip6 *); + struct ip6_pseudohdr *ih = mtod(m, struct ip6_pseudohdr *); + int sum; + + save_ip = *ip; + + ih->ih_src = save_ip.ip_src; + ih->ih_dst = save_ip.ip_dst; + ih->ih_pl = htonl((uint32_t)ntohs(save_ip.ip_pl)); + ih->ih_zero_hi = 0; + ih->ih_zero_lo = 0; + ih->ih_nh = save_ip.ip_nh; + + sum = cksum(m, ((int)sizeof(struct ip6_pseudohdr)) + + ntohl(ih->ih_pl)); + + *ip = save_ip; + + return sum; +} diff --git a/qemu/slirp/dnssearch.c b/qemu/slirp/dnssearch.c index 4c9064ecb..aed2f13af 100644 --- a/qemu/slirp/dnssearch.c +++ b/qemu/slirp/dnssearch.c @@ -22,9 +22,7 @@ * THE SOFTWARE. */ -#include -#include -#include +#include "qemu/osdep.h" #include #include "slirp.h" diff --git a/qemu/slirp/if.c b/qemu/slirp/if.c index fb7acf87d..9b02180db 100644 --- a/qemu/slirp/if.c +++ b/qemu/slirp/if.c @@ -5,6 +5,7 @@ * terms and conditions of the copyright. */ +#include "qemu/osdep.h" #include #include "qemu/timer.h" @@ -27,9 +28,9 @@ ifs_remque(struct mbuf *ifm) void if_init(Slirp *slirp) { - slirp->if_fastq.ifq_next = slirp->if_fastq.ifq_prev = &slirp->if_fastq; - slirp->if_batchq.ifq_next = slirp->if_batchq.ifq_prev = &slirp->if_batchq; - slirp->next_m = &slirp->if_batchq; + slirp->if_fastq.qh_link = slirp->if_fastq.qh_rlink = &slirp->if_fastq; + slirp->if_batchq.qh_link = slirp->if_batchq.qh_rlink = &slirp->if_batchq; + slirp->next_m = (struct mbuf *) &slirp->if_batchq; } /* @@ -53,8 +54,8 @@ if_output(struct socket *so, struct mbuf *ifm) int on_fastq = 1; DEBUG_CALL("if_output"); - DEBUG_ARG("so = %lx", (long)so); - DEBUG_ARG("ifm = %lx", (long)ifm); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("ifm = %p", ifm); /* * First remove the mbuf from m_usedlist, @@ -73,7 +74,8 @@ if_output(struct socket *so, struct mbuf *ifm) * We mustn't put this packet back on the fastq (or we'll send it out of order) * XXX add cache here? */ - for (ifq = slirp->if_batchq.ifq_prev; ifq != &slirp->if_batchq; + for (ifq = (struct mbuf *) slirp->if_batchq.qh_rlink; + (struct quehead *) ifq != &slirp->if_batchq; ifq = ifq->ifq_prev) { if (so == ifq->ifq_so) { /* A match! */ @@ -85,7 +87,7 @@ if_output(struct socket *so, struct mbuf *ifm) /* No match, check which queue to put it on */ if (so && (so->so_iptos & IPTOS_LOWDELAY)) { - ifq = slirp->if_fastq.ifq_prev; + ifq = (struct mbuf *) slirp->if_fastq.qh_rlink; on_fastq = 1; /* * Check if this packet is a part of the last @@ -97,9 +99,9 @@ if_output(struct socket *so, struct mbuf *ifm) goto diddit; } } else { - ifq = slirp->if_batchq.ifq_prev; + ifq = (struct mbuf *) slirp->if_batchq.qh_rlink; /* Set next_m if the queue was empty so far */ - if (slirp->next_m == &slirp->if_batchq) { + if ((struct quehead *) slirp->next_m == &slirp->if_batchq) { slirp->next_m = ifm; } } @@ -165,10 +167,10 @@ void if_start(Slirp *slirp) } slirp->if_start_busy = true; - if (slirp->if_fastq.ifq_next != &slirp->if_fastq) { - ifm_next = slirp->if_fastq.ifq_next; + if (slirp->if_fastq.qh_link != &slirp->if_fastq) { + ifm_next = (struct mbuf *) slirp->if_fastq.qh_link; next_from_batchq = false; - } else if (slirp->next_m != &slirp->if_batchq) { + } else if ((struct quehead *) slirp->next_m != &slirp->if_batchq) { /* Nothing on fastq, pick up from batchq via next_m */ ifm_next = slirp->next_m; next_from_batchq = true; @@ -181,19 +183,19 @@ void if_start(Slirp *slirp) from_batchq = next_from_batchq; ifm_next = ifm->ifq_next; - if (ifm_next == &slirp->if_fastq) { + if ((struct quehead *) ifm_next == &slirp->if_fastq) { /* No more packets in fastq, switch to batchq */ ifm_next = slirp->next_m; next_from_batchq = true; } - if (ifm_next == &slirp->if_batchq) { + if ((struct quehead *) ifm_next == &slirp->if_batchq) { /* end of batchq */ ifm_next = NULL; } /* Try to send packet unless it already expired */ if (ifm->expiration_date >= now && !if_encap(slirp, ifm)) { - /* Packet is delayed due to pending ARP resolution */ + /* Packet is delayed due to pending ARP or NDP resolution */ continue; } @@ -217,7 +219,7 @@ void if_start(Slirp *slirp) /* Next packet in fastq is from the same session */ ifm_next = next; next_from_batchq = false; - } else if (slirp->next_m == &slirp->if_batchq) { + } else if ((struct quehead *) slirp->next_m == &slirp->if_batchq) { /* Set next_m and ifm_next if the session packet is now the * only one on batchq */ slirp->next_m = ifm_next = next; diff --git a/qemu/slirp/if.h b/qemu/slirp/if.h index 33270239f..c7a5c5724 100644 --- a/qemu/slirp/if.h +++ b/qemu/slirp/if.h @@ -17,7 +17,7 @@ #define IF_MRU 1500 #define IF_COMP IF_AUTOCOMP /* Flags for compression */ -/* 2 for alignment, 14 for ethernet, 40 for TCP/IP */ -#define IF_MAXLINKHDR (2 + 14 + 40) +/* 2 for alignment, 14 for ethernet */ +#define IF_MAXLINKHDR (2 + ETH_HLEN) #endif diff --git a/qemu/slirp/ip6.h b/qemu/slirp/ip6.h new file mode 100644 index 000000000..8ddfa242c --- /dev/null +++ b/qemu/slirp/ip6.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2013 + * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. + */ + +#ifndef SLIRP_IP6_H_ +#define SLIRP_IP6_H_ + +#include "net/eth.h" + +#define ALLNODES_MULTICAST { .s6_addr = \ + { 0xff, 0x02, 0x00, 0x00,\ + 0x00, 0x00, 0x00, 0x00,\ + 0x00, 0x00, 0x00, 0x00,\ + 0x00, 0x00, 0x00, 0x01 } } + +#define SOLICITED_NODE_PREFIX { .s6_addr = \ + { 0xff, 0x02, 0x00, 0x00,\ + 0x00, 0x00, 0x00, 0x00,\ + 0x00, 0x00, 0x00, 0x01,\ + 0xff, 0x00, 0x00, 0x00 } } + +#define LINKLOCAL_ADDR { .s6_addr = \ + { 0xfe, 0x80, 0x00, 0x00,\ + 0x00, 0x00, 0x00, 0x00,\ + 0x00, 0x00, 0x00, 0x00,\ + 0x00, 0x00, 0x00, 0x02 } } + +static inline bool in6_equal(const struct in6_addr *a, const struct in6_addr *b) +{ + return memcmp(a, b, sizeof(*a)) == 0; +} + +static inline bool in6_equal_net(const struct in6_addr *a, + const struct in6_addr *b, + int prefix_len) +{ + if (memcmp(a, b, prefix_len / 8) != 0) { + return 0; + } + + if (prefix_len % 8 == 0) { + return 1; + } + + return a->s6_addr[prefix_len / 8] >> (8 - (prefix_len % 8)) + == b->s6_addr[prefix_len / 8] >> (8 - (prefix_len % 8)); +} + +static inline bool in6_equal_mach(const struct in6_addr *a, + const struct in6_addr *b, + int prefix_len) +{ + if (memcmp(&(a->s6_addr[(prefix_len + 7) / 8]), + &(b->s6_addr[(prefix_len + 7) / 8]), + 16 - (prefix_len + 7) / 8) != 0) { + return 0; + } + + if (prefix_len % 8 == 0) { + return 1; + } + + return (a->s6_addr[prefix_len / 8] & ((1U << (8 - (prefix_len % 8))) - 1)) + == (b->s6_addr[prefix_len / 8] & ((1U << (8 - (prefix_len % 8))) - 1)); +} + + +#define in6_equal_router(a)\ + ((in6_equal_net(a, &slirp->vprefix_addr6, slirp->vprefix_len)\ + && in6_equal_mach(a, &slirp->vhost_addr6, slirp->vprefix_len))\ + || (in6_equal_net(a, &(struct in6_addr)LINKLOCAL_ADDR, 64)\ + && in6_equal_mach(a, &slirp->vhost_addr6, 64))) + +#define in6_equal_dns(a)\ + ((in6_equal_net(a, &slirp->vprefix_addr6, slirp->vprefix_len)\ + && in6_equal_mach(a, &slirp->vnameserver_addr6, slirp->vprefix_len))\ + || (in6_equal_net(a, &(struct in6_addr)LINKLOCAL_ADDR, 64)\ + && in6_equal_mach(a, &slirp->vnameserver_addr6, 64))) + +#define in6_equal_host(a)\ + (in6_equal_router(a) || in6_equal_dns(a)) + +#define in6_solicitednode_multicast(a)\ + (in6_equal_net(a, &(struct in6_addr)SOLICITED_NODE_PREFIX, 104)) + +/* Compute emulated host MAC address from its ipv6 address */ +static inline void in6_compute_ethaddr(struct in6_addr ip, + uint8_t eth[ETH_ALEN]) +{ + eth[0] = 0x52; + eth[1] = 0x56; + memcpy(ð[2], &ip.s6_addr[16 - (ETH_ALEN - 2)], ETH_ALEN - 2); +} + +/* + * Definitions for internet protocol version 6. + * Per RFC 2460, December 1998. + */ +#define IP6VERSION 6 +#define IP6_HOP_LIMIT 255 + +/* + * Structure of an internet header, naked of options. + */ +struct ip6 { +#ifdef HOST_WORDS_BIGENDIAN + uint32_t + ip_v:4, /* version */ + ip_tc_hi:4, /* traffic class */ + ip_tc_lo:4, + ip_fl_hi:4, /* flow label */ + ip_fl_lo:16; +#else + uint32_t + ip_tc_hi:4, + ip_v:4, + ip_fl_hi:4, + ip_tc_lo:4, + ip_fl_lo:16; +#endif + uint16_t ip_pl; /* payload length */ + uint8_t ip_nh; /* next header */ + uint8_t ip_hl; /* hop limit */ + struct in6_addr ip_src, ip_dst; /* source and dest address */ +} QEMU_PACKED; + +/* + * IPv6 pseudo-header used by upper-layer protocols + */ +struct ip6_pseudohdr { + struct in6_addr ih_src; /* source internet address */ + struct in6_addr ih_dst; /* destination internet address */ + uint32_t ih_pl; /* upper-layer packet length */ + uint16_t ih_zero_hi; /* zero */ + uint8_t ih_zero_lo; /* zero */ + uint8_t ih_nh; /* next header */ +} QEMU_PACKED; + + +#endif diff --git a/qemu/slirp/ip6_icmp.c b/qemu/slirp/ip6_icmp.c new file mode 100644 index 000000000..09571bcd6 --- /dev/null +++ b/qemu/slirp/ip6_icmp.c @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2013 + * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. + */ + +#include "qemu/osdep.h" +#include "slirp.h" +#include "ip6_icmp.h" +#include "qemu/timer.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include + +#define NDP_Interval g_rand_int_range(slirp->grand, \ + NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval) + +static void ra_timer_handler(void *opaque) +{ + Slirp *slirp = opaque; + timer_mod(slirp->ra_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval); + ndp_send_ra(slirp); +} + +void icmp6_init(Slirp *slirp) +{ + if (!slirp->in6_enabled) { + return; + } + + slirp->ra_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, ra_timer_handler, slirp); + timer_mod(slirp->ra_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval); +} + +void icmp6_cleanup(Slirp *slirp) +{ + if (!slirp->in6_enabled) { + return; + } + + timer_del(slirp->ra_timer); + timer_free(slirp->ra_timer); +} + +static void icmp6_send_echoreply(struct mbuf *m, Slirp *slirp, struct ip6 *ip, + struct icmp6 *icmp) +{ + struct mbuf *t = m_get(slirp); + t->m_len = sizeof(struct ip6) + ntohs(ip->ip_pl); + memcpy(t->m_data, m->m_data, t->m_len); + + /* IPv6 Packet */ + struct ip6 *rip = mtod(t, struct ip6 *); + rip->ip_dst = ip->ip_src; + rip->ip_src = ip->ip_dst; + + /* ICMPv6 packet */ + t->m_data += sizeof(struct ip6); + struct icmp6 *ricmp = mtod(t, struct icmp6 *); + ricmp->icmp6_type = ICMP6_ECHO_REPLY; + ricmp->icmp6_cksum = 0; + + /* Checksum */ + t->m_data -= sizeof(struct ip6); + ricmp->icmp6_cksum = ip6_cksum(t); + + ip6_output(NULL, t, 0); +} + +void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code) +{ + Slirp *slirp = m->slirp; + struct mbuf *t; + struct ip6 *ip = mtod(m, struct ip6 *); + + DEBUG_CALL("icmp6_send_error"); + DEBUG_ARGS((dfd, " type = %d, code = %d\n", type, code)); + + if (IN6_IS_ADDR_MULTICAST(&ip->ip_src) || + IN6_IS_ADDR_UNSPECIFIED(&ip->ip_src)) { + /* TODO icmp error? */ + return; + } + + t = m_get(slirp); + + /* IPv6 packet */ + struct ip6 *rip = mtod(t, struct ip6 *); + rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR; + rip->ip_dst = ip->ip_src; +#if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600) + char addrstr[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &rip->ip_dst, addrstr, INET6_ADDRSTRLEN); + DEBUG_ARG("target = %s", addrstr); +#endif + + rip->ip_nh = IPPROTO_ICMPV6; + const int error_data_len = min(m->m_len, + IF_MTU - (sizeof(struct ip6) + ICMP6_ERROR_MINLEN)); + rip->ip_pl = htons(ICMP6_ERROR_MINLEN + error_data_len); + t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl); + + /* ICMPv6 packet */ + t->m_data += sizeof(struct ip6); + struct icmp6 *ricmp = mtod(t, struct icmp6 *); + ricmp->icmp6_type = type; + ricmp->icmp6_code = code; + ricmp->icmp6_cksum = 0; + + switch (type) { + case ICMP6_UNREACH: + case ICMP6_TIMXCEED: + ricmp->icmp6_err.unused = 0; + break; + case ICMP6_TOOBIG: + ricmp->icmp6_err.mtu = htonl(IF_MTU); + break; + case ICMP6_PARAMPROB: + /* TODO: Handle this case */ + break; + default: + g_assert_not_reached(); + break; + } + t->m_data += ICMP6_ERROR_MINLEN; + memcpy(t->m_data, m->m_data, error_data_len); + + /* Checksum */ + t->m_data -= ICMP6_ERROR_MINLEN; + t->m_data -= sizeof(struct ip6); + ricmp->icmp6_cksum = ip6_cksum(t); + + ip6_output(NULL, t, 0); +} + +/* + * Send NDP Router Advertisement + */ +void ndp_send_ra(Slirp *slirp) +{ + DEBUG_CALL("ndp_send_ra"); + + /* Build IPv6 packet */ + struct mbuf *t = m_get(slirp); + struct ip6 *rip = mtod(t, struct ip6 *); + rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR; + rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST; + rip->ip_nh = IPPROTO_ICMPV6; + rip->ip_pl = htons(ICMP6_NDP_RA_MINLEN + + NDPOPT_LINKLAYER_LEN + + NDPOPT_PREFIXINFO_LEN); + t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl); + + /* Build ICMPv6 packet */ + t->m_data += sizeof(struct ip6); + struct icmp6 *ricmp = mtod(t, struct icmp6 *); + ricmp->icmp6_type = ICMP6_NDP_RA; + ricmp->icmp6_code = 0; + ricmp->icmp6_cksum = 0; + + /* NDP */ + ricmp->icmp6_nra.chl = NDP_AdvCurHopLimit; + ricmp->icmp6_nra.M = NDP_AdvManagedFlag; + ricmp->icmp6_nra.O = NDP_AdvOtherConfigFlag; + ricmp->icmp6_nra.reserved = 0; + ricmp->icmp6_nra.lifetime = htons(NDP_AdvDefaultLifetime); + ricmp->icmp6_nra.reach_time = htonl(NDP_AdvReachableTime); + ricmp->icmp6_nra.retrans_time = htonl(NDP_AdvRetransTime); + + /* Source link-layer address (NDP option) */ + t->m_data += ICMP6_NDP_RA_MINLEN; + struct ndpopt *opt = mtod(t, struct ndpopt *); + opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE; + opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8; + in6_compute_ethaddr(rip->ip_src, opt->ndpopt_linklayer); + + /* Prefix information (NDP option) */ + t->m_data += NDPOPT_LINKLAYER_LEN; + struct ndpopt *opt2 = mtod(t, struct ndpopt *); + opt2->ndpopt_type = NDPOPT_PREFIX_INFO; + opt2->ndpopt_len = NDPOPT_PREFIXINFO_LEN / 8; + opt2->ndpopt_prefixinfo.prefix_length = slirp->vprefix_len; + opt2->ndpopt_prefixinfo.L = 1; + opt2->ndpopt_prefixinfo.A = 1; + opt2->ndpopt_prefixinfo.reserved1 = 0; + opt2->ndpopt_prefixinfo.valid_lt = htonl(NDP_AdvValidLifetime); + opt2->ndpopt_prefixinfo.pref_lt = htonl(NDP_AdvPrefLifetime); + opt2->ndpopt_prefixinfo.reserved2 = 0; + opt2->ndpopt_prefixinfo.prefix = slirp->vprefix_addr6; + + /* ICMPv6 Checksum */ + t->m_data -= NDPOPT_LINKLAYER_LEN; + t->m_data -= ICMP6_NDP_RA_MINLEN; + t->m_data -= sizeof(struct ip6); + ricmp->icmp6_cksum = ip6_cksum(t); + + ip6_output(NULL, t, 0); +} + +/* + * Send NDP Neighbor Solitication + */ +void ndp_send_ns(Slirp *slirp, struct in6_addr addr) +{ + DEBUG_CALL("ndp_send_ns"); +#if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600) + char addrstr[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &addr, addrstr, INET6_ADDRSTRLEN); + DEBUG_ARG("target = %s", addrstr); +#endif + + /* Build IPv6 packet */ + struct mbuf *t = m_get(slirp); + struct ip6 *rip = mtod(t, struct ip6 *); + rip->ip_src = slirp->vhost_addr6; + rip->ip_dst = (struct in6_addr)SOLICITED_NODE_PREFIX; + memcpy(&rip->ip_dst.s6_addr[13], &addr.s6_addr[13], 3); + rip->ip_nh = IPPROTO_ICMPV6; + rip->ip_pl = htons(ICMP6_NDP_NS_MINLEN + NDPOPT_LINKLAYER_LEN); + t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl); + + /* Build ICMPv6 packet */ + t->m_data += sizeof(struct ip6); + struct icmp6 *ricmp = mtod(t, struct icmp6 *); + ricmp->icmp6_type = ICMP6_NDP_NS; + ricmp->icmp6_code = 0; + ricmp->icmp6_cksum = 0; + + /* NDP */ + ricmp->icmp6_nns.reserved = 0; + ricmp->icmp6_nns.target = addr; + + /* Build NDP option */ + t->m_data += ICMP6_NDP_NS_MINLEN; + struct ndpopt *opt = mtod(t, struct ndpopt *); + opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE; + opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8; + in6_compute_ethaddr(slirp->vhost_addr6, opt->ndpopt_linklayer); + + /* ICMPv6 Checksum */ + t->m_data -= ICMP6_NDP_NA_MINLEN; + t->m_data -= sizeof(struct ip6); + ricmp->icmp6_cksum = ip6_cksum(t); + + ip6_output(NULL, t, 1); +} + +/* + * Send NDP Neighbor Advertisement + */ +static void ndp_send_na(Slirp *slirp, struct ip6 *ip, struct icmp6 *icmp) +{ + /* Build IPv6 packet */ + struct mbuf *t = m_get(slirp); + struct ip6 *rip = mtod(t, struct ip6 *); + rip->ip_src = icmp->icmp6_nns.target; + if (IN6_IS_ADDR_UNSPECIFIED(&ip->ip_src)) { + rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST; + } else { + rip->ip_dst = ip->ip_src; + } + rip->ip_nh = IPPROTO_ICMPV6; + rip->ip_pl = htons(ICMP6_NDP_NA_MINLEN + + NDPOPT_LINKLAYER_LEN); + t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl); + + /* Build ICMPv6 packet */ + t->m_data += sizeof(struct ip6); + struct icmp6 *ricmp = mtod(t, struct icmp6 *); + ricmp->icmp6_type = ICMP6_NDP_NA; + ricmp->icmp6_code = 0; + ricmp->icmp6_cksum = 0; + + /* NDP */ + ricmp->icmp6_nna.R = NDP_IsRouter; + ricmp->icmp6_nna.S = !IN6_IS_ADDR_MULTICAST(&rip->ip_dst); + ricmp->icmp6_nna.O = 1; + ricmp->icmp6_nna.reserved_hi = 0; + ricmp->icmp6_nna.reserved_lo = 0; + ricmp->icmp6_nna.target = icmp->icmp6_nns.target; + + /* Build NDP option */ + t->m_data += ICMP6_NDP_NA_MINLEN; + struct ndpopt *opt = mtod(t, struct ndpopt *); + opt->ndpopt_type = NDPOPT_LINKLAYER_TARGET; + opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8; + in6_compute_ethaddr(ricmp->icmp6_nna.target, + opt->ndpopt_linklayer); + + /* ICMPv6 Checksum */ + t->m_data -= ICMP6_NDP_NA_MINLEN; + t->m_data -= sizeof(struct ip6); + ricmp->icmp6_cksum = ip6_cksum(t); + + ip6_output(NULL, t, 0); +} + +/* + * Process a NDP message + */ +static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip, + struct icmp6 *icmp) +{ + m->m_len += ETH_HLEN; + m->m_data -= ETH_HLEN; + struct ethhdr *eth = mtod(m, struct ethhdr *); + m->m_len -= ETH_HLEN; + m->m_data += ETH_HLEN; + + switch (icmp->icmp6_type) { + case ICMP6_NDP_RS: + DEBUG_CALL(" type = Router Solicitation"); + if (ip->ip_hl == 255 + && icmp->icmp6_code == 0 + && ntohs(ip->ip_pl) >= ICMP6_NDP_RS_MINLEN) { + /* Gratuitous NDP */ + ndp_table_add(slirp, ip->ip_src, eth->h_source); + + ndp_send_ra(slirp); + } + break; + + case ICMP6_NDP_RA: + DEBUG_CALL(" type = Router Advertisement"); + qemu_log_mask(LOG_GUEST_ERROR, + "Warning: guest sent NDP RA, but shouldn't"); + break; + + case ICMP6_NDP_NS: + DEBUG_CALL(" type = Neighbor Solicitation"); + if (ip->ip_hl == 255 + && icmp->icmp6_code == 0 + && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nns.target) + && ntohs(ip->ip_pl) >= ICMP6_NDP_NS_MINLEN + && (!IN6_IS_ADDR_UNSPECIFIED(&ip->ip_src) + || in6_solicitednode_multicast(&ip->ip_dst))) { + if (in6_equal_host(&icmp->icmp6_nns.target)) { + /* Gratuitous NDP */ + ndp_table_add(slirp, ip->ip_src, eth->h_source); + ndp_send_na(slirp, ip, icmp); + } + } + break; + + case ICMP6_NDP_NA: + DEBUG_CALL(" type = Neighbor Advertisement"); + if (ip->ip_hl == 255 + && icmp->icmp6_code == 0 + && ntohs(ip->ip_pl) >= ICMP6_NDP_NA_MINLEN + && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nna.target) + && (!IN6_IS_ADDR_MULTICAST(&ip->ip_dst) + || icmp->icmp6_nna.S == 0)) { + ndp_table_add(slirp, ip->ip_src, eth->h_source); + } + break; + + case ICMP6_NDP_REDIRECT: + DEBUG_CALL(" type = Redirect"); + qemu_log_mask(LOG_GUEST_ERROR, + "Warning: guest sent NDP REDIRECT, but shouldn't"); + break; + } +} + +/* + * Process a received ICMPv6 message. + */ +void icmp6_input(struct mbuf *m) +{ + struct icmp6 *icmp; + struct ip6 *ip = mtod(m, struct ip6 *); + Slirp *slirp = m->slirp; + int hlen = sizeof(struct ip6); + + DEBUG_CALL("icmp6_input"); + DEBUG_ARG("m = %lx", (long) m); + DEBUG_ARG("m_len = %d", m->m_len); + + if (ntohs(ip->ip_pl) < ICMP6_MINLEN) { + goto end; + } + + if (ip6_cksum(m)) { + goto end; + } + + m->m_len -= hlen; + m->m_data += hlen; + icmp = mtod(m, struct icmp6 *); + m->m_len += hlen; + m->m_data -= hlen; + + DEBUG_ARG("icmp6_type = %d", icmp->icmp6_type); + switch (icmp->icmp6_type) { + case ICMP6_ECHO_REQUEST: + if (in6_equal_host(&ip->ip_dst)) { + icmp6_send_echoreply(m, slirp, ip, icmp); + } else { + /* TODO */ + error_report("external icmpv6 not supported yet"); + } + break; + + case ICMP6_NDP_RS: + case ICMP6_NDP_RA: + case ICMP6_NDP_NS: + case ICMP6_NDP_NA: + case ICMP6_NDP_REDIRECT: + ndp_input(m, slirp, ip, icmp); + break; + + case ICMP6_UNREACH: + case ICMP6_TOOBIG: + case ICMP6_TIMXCEED: + case ICMP6_PARAMPROB: + /* XXX? report error? close socket? */ + default: + break; + } + +end: + m_free(m); +} diff --git a/qemu/slirp/ip6_icmp.h b/qemu/slirp/ip6_icmp.h new file mode 100644 index 000000000..9460bf837 --- /dev/null +++ b/qemu/slirp/ip6_icmp.h @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2013 + * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. + */ + +#ifndef SLIRP_NETINET_ICMP6_H_ +#define SLIRP_NETINET_ICMP6_H_ + +/* + * Interface Control Message Protocol version 6 Definitions. + * Per RFC 4443, March 2006. + * + * Network Discover Protocol Definitions. + * Per RFC 4861, September 2007. + */ + +struct icmp6_echo { /* Echo Messages */ + uint16_t id; + uint16_t seq_num; +}; + +union icmp6_error_body { + uint32_t unused; + uint32_t pointer; + uint32_t mtu; +}; + +/* + * NDP Messages + */ +struct ndp_rs { /* Router Solicitation Message */ + uint32_t reserved; +}; + +struct ndp_ra { /* Router Advertisement Message */ + uint8_t chl; /* Cur Hop Limit */ +#ifdef HOST_WORDS_BIGENDIAN + uint8_t + M:1, + O:1, + reserved:6; +#else + uint8_t + reserved:6, + O:1, + M:1; +#endif + uint16_t lifetime; /* Router Lifetime */ + uint32_t reach_time; /* Reachable Time */ + uint32_t retrans_time; /* Retrans Timer */ +} QEMU_PACKED; + +struct ndp_ns { /* Neighbor Solicitation Message */ + uint32_t reserved; + struct in6_addr target; /* Target Address */ +} QEMU_PACKED; + +struct ndp_na { /* Neighbor Advertisement Message */ +#ifdef HOST_WORDS_BIGENDIAN + uint32_t + R:1, /* Router Flag */ + S:1, /* Solicited Flag */ + O:1, /* Override Flag */ + reserved_hi:5, + reserved_lo:24; +#else + uint32_t + reserved_hi:5, + O:1, + S:1, + R:1, + reserved_lo:24; +#endif + struct in6_addr target; /* Target Address */ +} QEMU_PACKED; + +struct ndp_redirect { + uint32_t reserved; + struct in6_addr target; /* Target Address */ + struct in6_addr dest; /* Destination Address */ +} QEMU_PACKED; + +/* + * Structure of an icmpv6 header. + */ +struct icmp6 { + uint8_t icmp6_type; /* type of message, see below */ + uint8_t icmp6_code; /* type sub code */ + uint16_t icmp6_cksum; /* ones complement cksum of struct */ + union { + union icmp6_error_body error_body; + struct icmp6_echo echo; + struct ndp_rs ndp_rs; + struct ndp_ra ndp_ra; + struct ndp_ns ndp_ns; + struct ndp_na ndp_na; + struct ndp_redirect ndp_redirect; + } icmp6_body; +#define icmp6_err icmp6_body.error_body +#define icmp6_echo icmp6_body.echo +#define icmp6_nrs icmp6_body.ndp_rs +#define icmp6_nra icmp6_body.ndp_ra +#define icmp6_nns icmp6_body.ndp_ns +#define icmp6_nna icmp6_body.ndp_na +#define icmp6_redirect icmp6_body.ndp_redirect +} QEMU_PACKED; + +#define ICMP6_MINLEN 4 +#define ICMP6_ERROR_MINLEN 8 +#define ICMP6_ECHO_MINLEN 8 +#define ICMP6_NDP_RS_MINLEN 8 +#define ICMP6_NDP_RA_MINLEN 16 +#define ICMP6_NDP_NS_MINLEN 24 +#define ICMP6_NDP_NA_MINLEN 24 +#define ICMP6_NDP_REDIRECT_MINLEN 40 + +/* + * NDP Options + */ +struct ndpopt { + uint8_t ndpopt_type; /* Option type */ + uint8_t ndpopt_len; /* /!\ In units of 8 octets */ + union { + unsigned char linklayer_addr[6]; /* Source/Target Link-layer */ + struct prefixinfo { /* Prefix Information */ + uint8_t prefix_length; +#ifdef HOST_WORDS_BIGENDIAN + uint8_t L:1, A:1, reserved1:6; +#else + uint8_t reserved1:6, A:1, L:1; +#endif + uint32_t valid_lt; /* Valid Lifetime */ + uint32_t pref_lt; /* Preferred Lifetime */ + uint32_t reserved2; + struct in6_addr prefix; + } QEMU_PACKED prefixinfo; + } ndpopt_body; +#define ndpopt_linklayer ndpopt_body.linklayer_addr +#define ndpopt_prefixinfo ndpopt_body.prefixinfo +} QEMU_PACKED; + +/* NDP options type */ +#define NDPOPT_LINKLAYER_SOURCE 1 /* Source Link-Layer Address */ +#define NDPOPT_LINKLAYER_TARGET 2 /* Target Link-Layer Address */ +#define NDPOPT_PREFIX_INFO 3 /* Prefix Information */ + +/* NDP options size, in octets. */ +#define NDPOPT_LINKLAYER_LEN 8 +#define NDPOPT_PREFIXINFO_LEN 32 + +/* + * Definition of type and code field values. + * Per https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xml + * Last Updated 2012-11-12 + */ + +/* Errors */ +#define ICMP6_UNREACH 1 /* Destination Unreachable */ +#define ICMP6_UNREACH_NO_ROUTE 0 /* no route to dest */ +#define ICMP6_UNREACH_DEST_PROHIB 1 /* com with dest prohibited */ +#define ICMP6_UNREACH_SCOPE 2 /* beyond scope of src addr */ +#define ICMP6_UNREACH_ADDRESS 3 /* address unreachable */ +#define ICMP6_UNREACH_PORT 4 /* port unreachable */ +#define ICMP6_UNREACH_SRC_FAIL 5 /* src addr failed */ +#define ICMP6_UNREACH_REJECT_ROUTE 6 /* reject route to dest */ +#define ICMP6_UNREACH_SRC_HDR_ERROR 7 /* error in src routing header */ +#define ICMP6_TOOBIG 2 /* Packet Too Big */ +#define ICMP6_TIMXCEED 3 /* Time Exceeded */ +#define ICMP6_TIMXCEED_INTRANS 0 /* hop limit exceeded in transit */ +#define ICMP6_TIMXCEED_REASS 1 /* ttl=0 in reass */ +#define ICMP6_PARAMPROB 4 /* Parameter Problem */ +#define ICMP6_PARAMPROB_HDR_FIELD 0 /* err header field */ +#define ICMP6_PARAMPROB_NXTHDR_TYPE 1 /* unrecognized Next Header type */ +#define ICMP6_PARAMPROB_IPV6_OPT 2 /* unrecognized IPv6 option */ + +/* Informational Messages */ +#define ICMP6_ECHO_REQUEST 128 /* Echo Request */ +#define ICMP6_ECHO_REPLY 129 /* Echo Reply */ +#define ICMP6_NDP_RS 133 /* Router Solicitation (NDP) */ +#define ICMP6_NDP_RA 134 /* Router Advertisement (NDP) */ +#define ICMP6_NDP_NS 135 /* Neighbor Solicitation (NDP) */ +#define ICMP6_NDP_NA 136 /* Neighbor Advertisement (NDP) */ +#define ICMP6_NDP_REDIRECT 137 /* Redirect Message (NDP) */ + +/* + * Router Configuration Variables (rfc4861#section-6) + */ +#define NDP_IsRouter 1 +#define NDP_AdvSendAdvertisements 1 +#define NDP_MaxRtrAdvInterval 600000 +#define NDP_MinRtrAdvInterval ((NDP_MaxRtrAdvInterval >= 9) ? \ + NDP_MaxRtrAdvInterval / 3 : \ + NDP_MaxRtrAdvInterval) +#define NDP_AdvManagedFlag 0 +#define NDP_AdvOtherConfigFlag 0 +#define NDP_AdvLinkMTU 0 +#define NDP_AdvReachableTime 0 +#define NDP_AdvRetransTime 0 +#define NDP_AdvCurHopLimit 64 +#define NDP_AdvDefaultLifetime ((3 * NDP_MaxRtrAdvInterval) / 1000) +#define NDP_AdvValidLifetime 86400 +#define NDP_AdvOnLinkFlag 1 +#define NDP_AdvPrefLifetime 14400 +#define NDP_AdvAutonomousFlag 1 + +void icmp6_init(Slirp *slirp); +void icmp6_cleanup(Slirp *slirp); +void icmp6_input(struct mbuf *); +void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code); +void ndp_send_ra(Slirp *slirp); +void ndp_send_ns(Slirp *slirp, struct in6_addr addr); + +#endif diff --git a/qemu/slirp/ip6_input.c b/qemu/slirp/ip6_input.c new file mode 100644 index 000000000..ac2e3ea88 --- /dev/null +++ b/qemu/slirp/ip6_input.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2013 + * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. + */ + +#include "qemu/osdep.h" +#include "slirp.h" +#include "ip6_icmp.h" + +/* + * IP initialization: fill in IP protocol switch table. + * All protocols not implemented in kernel go to raw IP protocol handler. + */ +void ip6_init(Slirp *slirp) +{ + icmp6_init(slirp); +} + +void ip6_cleanup(Slirp *slirp) +{ + icmp6_cleanup(slirp); +} + +void ip6_input(struct mbuf *m) +{ + struct ip6 *ip6; + Slirp *slirp = m->slirp; + + if (!slirp->in6_enabled) { + goto bad; + } + + DEBUG_CALL("ip6_input"); + DEBUG_ARG("m = %lx", (long)m); + DEBUG_ARG("m_len = %d", m->m_len); + + if (m->m_len < sizeof(struct ip6)) { + goto bad; + } + + ip6 = mtod(m, struct ip6 *); + + if (ip6->ip_v != IP6VERSION) { + goto bad; + } + + if (ntohs(ip6->ip_pl) > IF_MTU) { + icmp6_send_error(m, ICMP6_TOOBIG, 0); + goto bad; + } + + /* check ip_ttl for a correct ICMP reply */ + if (ip6->ip_hl == 0) { + icmp6_send_error(m, ICMP6_TIMXCEED, ICMP6_TIMXCEED_INTRANS); + goto bad; + } + + /* + * Switch out to protocol's input routine. + */ + switch (ip6->ip_nh) { + case IPPROTO_TCP: + NTOHS(ip6->ip_pl); + tcp_input(m, sizeof(struct ip6), (struct socket *)NULL, AF_INET6); + break; + case IPPROTO_UDP: + udp6_input(m); + break; + case IPPROTO_ICMPV6: + icmp6_input(m); + break; + default: + m_free(m); + } + return; +bad: + m_free(m); +} diff --git a/qemu/slirp/ip6_output.c b/qemu/slirp/ip6_output.c new file mode 100644 index 000000000..762cbfe89 --- /dev/null +++ b/qemu/slirp/ip6_output.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013 + * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "slirp.h" + +/* Number of packets queued before we start sending + * (to prevent allocing too many mbufs) */ +#define IF6_THRESH 10 + +/* + * IPv6 output. The packet in mbuf chain m contains a IP header + */ +int ip6_output(struct socket *so, struct mbuf *m, int fast) +{ + struct ip6 *ip = mtod(m, struct ip6 *); + + DEBUG_CALL("ip6_output"); + DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("m = %lx", (long)m); + + /* Fill IPv6 header */ + ip->ip_v = IP6VERSION; + ip->ip_hl = IP6_HOP_LIMIT; + ip->ip_tc_hi = 0; + ip->ip_tc_lo = 0; + ip->ip_fl_hi = 0; + ip->ip_fl_lo = 0; + + if (fast) { + if_encap(m->slirp, m); + } else { + if_output(so, m); + } + + return 0; +} diff --git a/qemu/slirp/ip_icmp.c b/qemu/slirp/ip_icmp.c index 9f1cb08a1..5ffc7a683 100644 --- a/qemu/slirp/ip_icmp.c +++ b/qemu/slirp/ip_icmp.c @@ -30,6 +30,7 @@ * ip_icmp.c,v 1.7 1995/05/30 08:09:42 rgrimes Exp */ +#include "qemu/osdep.h" #include "slirp.h" #include "ip_icmp.h" @@ -37,7 +38,7 @@ /* Be nice and tell them it's just a pseudo-ping packet */ static const char icmp_ping_msg[] = "This is a pseudo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST packets.\n"; -/* list of actions for icmp_error() on RX of an icmp message */ +/* list of actions for icmp_send_error() on RX of an icmp message */ static const int icmp_flush[19] = { /* ECHO REPLY (0) */ 0, 1, @@ -100,7 +101,7 @@ static int icmp_send(struct socket *so, struct mbuf *m, int hlen) (struct sockaddr *)&addr, sizeof(addr)) == -1) { DEBUG_MISC((dfd, "icmp_input icmp sendto tx errno = %d-%s\n", errno, strerror(errno))); - icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno)); + icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno)); icmp_detach(so); } @@ -125,7 +126,7 @@ icmp_input(struct mbuf *m, int hlen) Slirp *slirp = m->slirp; DEBUG_CALL("icmp_input"); - DEBUG_ARG("m = %lx", (long )m); + DEBUG_ARG("m = %p", m); DEBUG_ARG("m_len = %d", m->m_len); /* @@ -157,12 +158,12 @@ icmp_input(struct mbuf *m, int hlen) goto freeit; } else { struct socket *so; - struct sockaddr_in addr; + struct sockaddr_storage addr; if ((so = socreate(slirp)) == NULL) goto freeit; if (icmp_send(so, m, hlen) == 0) { return; } - if(udp_attach(so) == -1) { + if (udp_attach(so, AF_INET) == -1) { DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n", errno,strerror(errno))); sofree(so); @@ -170,8 +171,10 @@ icmp_input(struct mbuf *m, int hlen) goto end_error; } so->so_m = m; + so->so_ffamily = AF_INET; so->so_faddr = ip->ip_dst; so->so_fport = htons(7); + so->so_lfamily = AF_INET; so->so_laddr = ip->ip_src; so->so_lport = htons(9); so->so_iptos = ip->ip_tos; @@ -179,25 +182,14 @@ icmp_input(struct mbuf *m, int hlen) so->so_state = SS_ISFCONNECTED; /* Send the packet */ - addr.sin_family = AF_INET; - if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == - slirp->vnetwork_addr.s_addr) { - /* It's an alias */ - if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) { - if (get_dns_addr(&addr.sin_addr) < 0) - addr.sin_addr = loopback_addr; - } else { - addr.sin_addr = loopback_addr; - } - } else { - addr.sin_addr = so->so_faddr; - } - addr.sin_port = so->so_fport; + addr = so->fhost.ss; + sotranslate_out(so, &addr); + if(sendto(so->s, icmp_ping_msg, strlen(icmp_ping_msg), 0, - (struct sockaddr *)&addr, sizeof(addr)) == -1) { + (struct sockaddr *)&addr, sockaddr_size(&addr)) == -1) { DEBUG_MISC((dfd,"icmp_input udp sendto tx errno = %d-%s\n", errno,strerror(errno))); - icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno)); + icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno)); udp_detach(so); } } /* if ip->ip_dst.s_addr == alias_addr.s_addr */ @@ -243,7 +235,7 @@ end_error: #define ICMP_MAXDATALEN (IP_MSS-28) void -icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize, +icmp_send_error(struct mbuf *msrc, u_char type, u_char code, int minsize, const char *message) { unsigned hlen, shlen, s_ip_len; @@ -251,8 +243,8 @@ icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize, register struct icmp *icp; register struct mbuf *m; - DEBUG_CALL("icmp_error"); - DEBUG_ARG("msrc = %lx", (long )msrc); + DEBUG_CALL("icmp_send_error"); + DEBUG_ARG("msrc = %p", msrc); DEBUG_ARG("msrc_len = %d", msrc->m_len); if(type!=ICMP_UNREACH && type!=ICMP_TIMXCEED) goto end_error; @@ -441,7 +433,7 @@ void icmp_receive(struct socket *so) } DEBUG_MISC((dfd, " udp icmp rx errno = %d-%s\n", errno, strerror(errno))); - icmp_error(so->so_m, ICMP_UNREACH, error_code, 0, strerror(errno)); + icmp_send_error(so->so_m, ICMP_UNREACH, error_code, 0, strerror(errno)); } else { icmp_reflect(so->so_m); so->so_m = NULL; /* Don't m_free() it again! */ diff --git a/qemu/slirp/ip_icmp.h b/qemu/slirp/ip_icmp.h index be4426b8e..846761d08 100644 --- a/qemu/slirp/ip_icmp.h +++ b/qemu/slirp/ip_icmp.h @@ -156,8 +156,8 @@ struct icmp { void icmp_init(Slirp *slirp); void icmp_cleanup(Slirp *slirp); void icmp_input(struct mbuf *, int); -void icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize, - const char *message); +void icmp_send_error(struct mbuf *msrc, u_char type, u_char code, int minsize, + const char *message); void icmp_reflect(struct mbuf *); void icmp_receive(struct socket *so); void icmp_detach(struct socket *so); diff --git a/qemu/slirp/ip_input.c b/qemu/slirp/ip_input.c index 880bdfd3c..cdd54833a 100644 --- a/qemu/slirp/ip_input.c +++ b/qemu/slirp/ip_input.c @@ -38,6 +38,7 @@ * terms and conditions of the copyright. */ +#include "qemu/osdep.h" #include #include #include "ip_icmp.h" @@ -79,12 +80,16 @@ ip_input(struct mbuf *m) register struct ip *ip; int hlen; + if (!slirp->in_enabled) { + goto bad; + } + DEBUG_CALL("ip_input"); - DEBUG_ARG("m = %lx", (long)m); + DEBUG_ARG("m = %p", m); DEBUG_ARG("m_len = %d", m->m_len); if (m->m_len < sizeof (struct ip)) { - return; + goto bad; } ip = mtod(m, struct ip *); @@ -131,9 +136,9 @@ ip_input(struct mbuf *m) m_adj(m, ip->ip_len - m->m_len); /* check ip_ttl for a correct ICMP reply */ - if(ip->ip_ttl==0) { - icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl"); - goto bad; + if (ip->ip_ttl == 0) { + icmp_send_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0, "ttl"); + goto bad; } /* @@ -199,7 +204,7 @@ ip_input(struct mbuf *m) */ switch (ip->ip_p) { case IPPROTO_TCP: - tcp_input(m, hlen, (struct socket *)NULL); + tcp_input(m, hlen, (struct socket *)NULL, AF_INET); break; case IPPROTO_UDP: udp_input(m, hlen); @@ -232,9 +237,9 @@ ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp) int i, next; DEBUG_CALL("ip_reass"); - DEBUG_ARG("ip = %lx", (long)ip); - DEBUG_ARG("fp = %lx", (long)fp); - DEBUG_ARG("m = %lx", (long)m); + DEBUG_ARG("ip = %p", ip); + DEBUG_ARG("fp = %p", fp); + DEBUG_ARG("m = %p", m); /* * Presence of header sizes in mbufs @@ -400,7 +405,7 @@ static void ip_enq(register struct ipasfrag *p, register struct ipasfrag *prev) { DEBUG_CALL("ip_enq"); - DEBUG_ARG("prev = %lx", (long)prev); + DEBUG_ARG("prev = %p", prev); p->ipf_prev = prev; p->ipf_next = prev->ipf_next; ((struct ipasfrag *)(prev->ipf_next))->ipf_prev = p; @@ -636,7 +641,7 @@ typedef uint32_t n_time; } return (0); bad: - icmp_error(m, type, code, 0, 0); + icmp_send_error(m, type, code, 0, 0); return (1); } diff --git a/qemu/slirp/ip_output.c b/qemu/slirp/ip_output.c index c82830fe7..0d6b3b831 100644 --- a/qemu/slirp/ip_output.c +++ b/qemu/slirp/ip_output.c @@ -38,6 +38,7 @@ * terms and conditions of the copyright. */ +#include "qemu/osdep.h" #include /* Number of packets queued before we start sending @@ -60,8 +61,8 @@ ip_output(struct socket *so, struct mbuf *m0) int len, off, error = 0; DEBUG_CALL("ip_output"); - DEBUG_ARG("so = %lx", (long)so); - DEBUG_ARG("m0 = %lx", (long)m0); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("m0 = %p", m0); ip = mtod(m, struct ip *); /* diff --git a/qemu/slirp/libslirp.h b/qemu/slirp/libslirp.h index 5bdcbd50f..127aa41d4 100644 --- a/qemu/slirp/libslirp.h +++ b/qemu/slirp/libslirp.h @@ -8,11 +8,14 @@ typedef struct Slirp Slirp; int get_dns_addr(struct in_addr *pdns_addr); -Slirp *slirp_init(int restricted, struct in_addr vnetwork, +Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, struct in_addr vnetmask, struct in_addr vhost, - const char *vhostname, const char *tftp_path, - const char *bootfile, struct in_addr vdhcp_start, - struct in_addr vnameserver, const char **vdnssearch, + bool in6_enabled, + struct in6_addr vprefix_addr6, uint8_t vprefix_len, + struct in6_addr vhost6, const char *vhostname, + const char *tftp_path, const char *bootfile, + struct in_addr vdhcp_start, struct in_addr vnameserver, + struct in6_addr vnameserver6, const char **vdnssearch, void *opaque); void slirp_cleanup(Slirp *slirp); diff --git a/qemu/slirp/mbuf.c b/qemu/slirp/mbuf.c index 4fefb043b..d13698839 100644 --- a/qemu/slirp/mbuf.c +++ b/qemu/slirp/mbuf.c @@ -15,29 +15,30 @@ * the flags */ +#include "qemu/osdep.h" #include #define MBUF_THRESH 30 /* * Find a nice value for msize - * XXX if_maxlinkhdr already in mtu */ -#define SLIRP_MSIZE (IF_MTU + IF_MAXLINKHDR + offsetof(struct mbuf, m_dat) + 6) +#define SLIRP_MSIZE\ + (offsetof(struct mbuf, m_dat) + IF_MAXLINKHDR + TCPIPHDR_DELTA + IF_MTU) void m_init(Slirp *slirp) { - slirp->m_freelist.m_next = slirp->m_freelist.m_prev = &slirp->m_freelist; - slirp->m_usedlist.m_next = slirp->m_usedlist.m_prev = &slirp->m_usedlist; + slirp->m_freelist.qh_link = slirp->m_freelist.qh_rlink = &slirp->m_freelist; + slirp->m_usedlist.qh_link = slirp->m_usedlist.qh_rlink = &slirp->m_usedlist; } void m_cleanup(Slirp *slirp) { struct mbuf *m, *next; - m = slirp->m_usedlist.m_next; - while (m != &slirp->m_usedlist) { + m = (struct mbuf *) slirp->m_usedlist.qh_link; + while ((struct quehead *) m != &slirp->m_usedlist) { next = m->m_next; if (m->m_flags & M_EXT) { free(m->m_ext); @@ -45,8 +46,8 @@ void m_cleanup(Slirp *slirp) free(m); m = next; } - m = slirp->m_freelist.m_next; - while (m != &slirp->m_freelist) { + m = (struct mbuf *) slirp->m_freelist.qh_link; + while ((struct quehead *) m != &slirp->m_freelist) { next = m->m_next; free(m); m = next; @@ -69,7 +70,7 @@ m_get(Slirp *slirp) DEBUG_CALL("m_get"); - if (slirp->m_freelist.m_next == &slirp->m_freelist) { + if (slirp->m_freelist.qh_link == &slirp->m_freelist) { m = (struct mbuf *)malloc(SLIRP_MSIZE); if (m == NULL) goto end_error; slirp->mbuf_alloced++; @@ -77,7 +78,7 @@ m_get(Slirp *slirp) flags = M_DOFREE; m->slirp = slirp; } else { - m = slirp->m_freelist.m_next; + m = (struct mbuf *) slirp->m_freelist.qh_link; remque(m); } @@ -91,10 +92,10 @@ m_get(Slirp *slirp) m->m_len = 0; m->m_nextpkt = NULL; m->m_prevpkt = NULL; - m->arp_requested = false; + m->resolution_requested = false; m->expiration_date = (uint64_t)-1; end_error: - DEBUG_ARG("m = %lx", (long )m); + DEBUG_ARG("m = %p", m); return m; } @@ -103,7 +104,7 @@ m_free(struct mbuf *m) { DEBUG_CALL("m_free"); - DEBUG_ARG("m = %lx", (long )m); + DEBUG_ARG("m = %p", m); if(m) { /* Remove from m_usedlist */ @@ -221,10 +222,11 @@ dtom(Slirp *slirp, void *dat) struct mbuf *m; DEBUG_CALL("dtom"); - DEBUG_ARG("dat = %lx", (long )dat); + DEBUG_ARG("dat = %p", dat); /* bug corrected for M_EXT buffers */ - for (m = slirp->m_usedlist.m_next; m != &slirp->m_usedlist; + for (m = (struct mbuf *) slirp->m_usedlist.qh_link; + (struct quehead *) m != &slirp->m_usedlist; m = m->m_next) { if (m->m_flags & M_EXT) { if( (char *)dat>=m->m_ext && (char *)dat<(m->m_ext + m->m_size) ) diff --git a/qemu/slirp/mbuf.h b/qemu/slirp/mbuf.h index b144f1ce3..36fb81409 100644 --- a/qemu/slirp/mbuf.h +++ b/qemu/slirp/mbuf.h @@ -79,13 +79,11 @@ struct mbuf { int m_len; /* Amount of data in this mbuf */ Slirp *slirp; - bool arp_requested; + bool resolution_requested; uint64_t expiration_date; + char *m_ext; /* start of dynamic buffer area, must be last element */ - union { - char m_dat[1]; /* ANSI don't like 0 sized arrays */ - char *m_ext; - }; + char m_dat[]; }; #define ifq_prev m_prev diff --git a/qemu/slirp/misc.c b/qemu/slirp/misc.c index 578e8b2c1..2fbd04856 100644 --- a/qemu/slirp/misc.c +++ b/qemu/slirp/misc.c @@ -5,6 +5,7 @@ * terms and conditions of the copyright. */ +#include "qemu/osdep.h" #include #include @@ -16,11 +17,6 @@ int slirp_debug = DBG_CALL|DBG_MISC|DBG_ERROR; #endif -struct quehead { - struct quehead *qh_link; - struct quehead *qh_rlink; -}; - inline void insque(void *a, void *b) { @@ -123,9 +119,9 @@ fork_exec(struct socket *so, const char *ex, int do_pty) pid_t pid; DEBUG_CALL("fork_exec"); - DEBUG_ARG("so = %lx", (long)so); - DEBUG_ARG("ex = %lx", (long)ex); - DEBUG_ARG("do_pty = %lx", (long)do_pty); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("ex = %p", ex); + DEBUG_ARG("do_pty = %x", do_pty); if (do_pty == 2) { return 0; diff --git a/qemu/slirp/misc.h b/qemu/slirp/misc.h index 41a32583d..0d0c059e6 100644 --- a/qemu/slirp/misc.h +++ b/qemu/slirp/misc.h @@ -45,6 +45,11 @@ struct emu_t { struct emu_t *next; }; +struct slirp_quehead { + struct slirp_quehead *qh_link; + struct slirp_quehead *qh_rlink; +}; + void slirp_insque(void *, void *); void slirp_remque(void *); int add_exec(struct ex_list **, int, char *, struct in_addr, int); diff --git a/qemu/slirp/ndp_table.c b/qemu/slirp/ndp_table.c new file mode 100644 index 000000000..9d4c39b45 --- /dev/null +++ b/qemu/slirp/ndp_table.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2013 + * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "slirp.h" + +void ndp_table_add(Slirp *slirp, struct in6_addr ip_addr, + uint8_t ethaddr[ETH_ALEN]) +{ + NdpTable *ndp_table = &slirp->ndp_table; + int i; + + DEBUG_CALL("ndp_table_add"); +#if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600) + char addrstr[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &(ip_addr), addrstr, INET6_ADDRSTRLEN); + DEBUG_ARG("ip = %s", addrstr); +#endif + DEBUG_ARGS((dfd, " hw addr = %02x:%02x:%02x:%02x:%02x:%02x\n", + ethaddr[0], ethaddr[1], ethaddr[2], + ethaddr[3], ethaddr[4], ethaddr[5])); + + if (IN6_IS_ADDR_MULTICAST(&ip_addr) || IN6_IS_ADDR_UNSPECIFIED(&ip_addr)) { + /* Do not register multicast or unspecified addresses */ + DEBUG_CALL(" abort: do not register multicast or unspecified address"); + return; + } + + /* Search for an entry */ + for (i = 0; i < NDP_TABLE_SIZE; i++) { + if (in6_equal(&ndp_table->table[i].ip_addr, &ip_addr)) { + DEBUG_CALL(" already in table: update the entry"); + /* Update the entry */ + memcpy(ndp_table->table[i].eth_addr, ethaddr, ETH_ALEN); + return; + } + } + + /* No entry found, create a new one */ + DEBUG_CALL(" create new entry"); + ndp_table->table[ndp_table->next_victim].ip_addr = ip_addr; + memcpy(ndp_table->table[ndp_table->next_victim].eth_addr, + ethaddr, ETH_ALEN); + ndp_table->next_victim = (ndp_table->next_victim + 1) % NDP_TABLE_SIZE; +} + +bool ndp_table_search(Slirp *slirp, struct in6_addr ip_addr, + uint8_t out_ethaddr[ETH_ALEN]) +{ + NdpTable *ndp_table = &slirp->ndp_table; + int i; + + DEBUG_CALL("ndp_table_search"); +#if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600) + char addrstr[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &(ip_addr), addrstr, INET6_ADDRSTRLEN); + DEBUG_ARG("ip = %s", addrstr); +#endif + + assert(!IN6_IS_ADDR_UNSPECIFIED(&ip_addr)); + + /* Multicast address: fec0::abcd:efgh/8 -> 33:33:ab:cd:ef:gh */ + if (IN6_IS_ADDR_MULTICAST(&ip_addr)) { + out_ethaddr[0] = 0x33; out_ethaddr[1] = 0x33; + out_ethaddr[2] = ip_addr.s6_addr[12]; + out_ethaddr[3] = ip_addr.s6_addr[13]; + out_ethaddr[4] = ip_addr.s6_addr[14]; + out_ethaddr[5] = ip_addr.s6_addr[15]; + DEBUG_ARGS((dfd, " multicast addr = %02x:%02x:%02x:%02x:%02x:%02x\n", + out_ethaddr[0], out_ethaddr[1], out_ethaddr[2], + out_ethaddr[3], out_ethaddr[4], out_ethaddr[5])); + return 1; + } + + for (i = 0; i < NDP_TABLE_SIZE; i++) { + if (in6_equal(&ndp_table->table[i].ip_addr, &ip_addr)) { + memcpy(out_ethaddr, ndp_table->table[i].eth_addr, ETH_ALEN); + DEBUG_ARGS((dfd, " found hw addr = %02x:%02x:%02x:%02x:%02x:%02x\n", + out_ethaddr[0], out_ethaddr[1], out_ethaddr[2], + out_ethaddr[3], out_ethaddr[4], out_ethaddr[5])); + return 1; + } + } + + DEBUG_CALL(" ip not found in table"); + return 0; +} diff --git a/qemu/slirp/sbuf.c b/qemu/slirp/sbuf.c index 08ec2b4f4..dd4cb8c13 100644 --- a/qemu/slirp/sbuf.c +++ b/qemu/slirp/sbuf.c @@ -5,6 +5,7 @@ * terms and conditions of the copyright. */ +#include "qemu/osdep.h" #include #include @@ -72,8 +73,8 @@ sbappend(struct socket *so, struct mbuf *m) int ret = 0; DEBUG_CALL("sbappend"); - DEBUG_ARG("so = %lx", (long)so); - DEBUG_ARG("m = %lx", (long)m); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("m = %p", m); DEBUG_ARG("m->m_len = %d", m->m_len); /* Shouldn't happen, but... e.g. foreign host closes connection */ diff --git a/qemu/slirp/slirp.c b/qemu/slirp/slirp.c index 35f819afb..9f4bea3d3 100644 --- a/qemu/slirp/slirp.c +++ b/qemu/slirp/slirp.c @@ -21,11 +21,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/timer.h" +#include "qemu/error-report.h" #include "sysemu/char.h" #include "slirp.h" #include "hw/hw.h" +#include "qemu/cutils.h" /* host loopback address */ struct in_addr loopback_addr; @@ -197,21 +200,29 @@ static void slirp_init_once(void) static void slirp_state_save(QEMUFile *f, void *opaque); static int slirp_state_load(QEMUFile *f, void *opaque, int version_id); -Slirp *slirp_init(int restricted, struct in_addr vnetwork, +Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, struct in_addr vnetmask, struct in_addr vhost, - const char *vhostname, const char *tftp_path, - const char *bootfile, struct in_addr vdhcp_start, - struct in_addr vnameserver, const char **vdnssearch, + bool in6_enabled, + struct in6_addr vprefix_addr6, uint8_t vprefix_len, + struct in6_addr vhost6, const char *vhostname, + const char *tftp_path, const char *bootfile, + struct in_addr vdhcp_start, struct in_addr vnameserver, + struct in6_addr vnameserver6, const char **vdnssearch, void *opaque) { Slirp *slirp = g_malloc0(sizeof(Slirp)); slirp_init_once(); + slirp->grand = g_rand_new(); slirp->restricted = restricted; + slirp->in_enabled = in_enabled; + slirp->in6_enabled = in6_enabled; + if_init(slirp); ip_init(slirp); + ip6_init(slirp); /* Initialise mbufs *after* setting the MTU */ m_init(slirp); @@ -219,6 +230,9 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork, slirp->vnetwork_addr = vnetwork; slirp->vnetwork_mask = vnetmask; slirp->vhost_addr = vhost; + slirp->vprefix_addr6 = vprefix_addr6; + slirp->vprefix_len = vprefix_len; + slirp->vhost_addr6 = vhost6; if (vhostname) { pstrcpy(slirp->client_hostname, sizeof(slirp->client_hostname), vhostname); @@ -227,6 +241,7 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork, slirp->bootp_filename = g_strdup(bootfile); slirp->vdhcp_startaddr = vdhcp_start; slirp->vnameserver_addr = vnameserver; + slirp->vnameserver_addr6 = vnameserver6; if (vdnssearch) { translate_dnssearch(slirp, vdnssearch); @@ -234,7 +249,7 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork, slirp->opaque = opaque; - register_savevm(NULL, "slirp", 0, 3, + register_savevm(NULL, "slirp", 0, 4, slirp_state_save, slirp_state_load, slirp); QTAILQ_INSERT_TAIL(&slirp_instances, slirp, entry); @@ -249,8 +264,11 @@ void slirp_cleanup(Slirp *slirp) unregister_savevm(NULL, "slirp", slirp); ip_cleanup(slirp); + ip6_cleanup(slirp); m_cleanup(slirp); + g_rand_free(slirp->grand); + g_free(slirp->vdnssearch); g_free(slirp->tftp_prefix); g_free(slirp->bootp_filename); @@ -516,7 +534,12 @@ void slirp_pollfds_poll(GArray *pollfds, int select_error) * test for G_IO_IN below if this succeeds */ if (revents & G_IO_PRI) { - sorecvoob(so); + ret = sorecvoob(so); + if (ret < 0) { + /* Socket error might have resulted in the socket being + * removed, do not try to do anything more with it. */ + continue; + } } /* * Check sockets for reading @@ -535,6 +558,11 @@ void slirp_pollfds_poll(GArray *pollfds, int select_error) if (ret > 0) { tcp_output(sototcpcb(so)); } + if (ret < 0) { + /* Socket error might have resulted in the socket being + * removed, do not try to do anything more with it. */ + continue; + } } /* @@ -566,7 +594,8 @@ void slirp_pollfds_poll(GArray *pollfds, int select_error) /* * Continue tcp_input */ - tcp_input((struct mbuf *)NULL, sizeof(struct ip), so); + tcp_input((struct mbuf *)NULL, sizeof(struct ip), so, + so->so_ffamily); /* continue; */ } else { ret = sowrite(so); @@ -615,7 +644,8 @@ void slirp_pollfds_poll(GArray *pollfds, int select_error) } } - tcp_input((struct mbuf *)NULL, sizeof(struct ip), so); + tcp_input((struct mbuf *)NULL, sizeof(struct ip), so, + so->so_ffamily); } /* SS_ISFCONNECTING */ #endif } @@ -678,6 +708,10 @@ static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) int ar_op; struct ex_list *ex_ptr; + if (!slirp->in_enabled) { + return; + } + ar_op = ntohs(ah->ar_op); switch(ar_op) { case ARPOP_REQUEST: @@ -742,40 +776,42 @@ void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) arp_input(slirp, pkt, pkt_len); break; case ETH_P_IP: + case ETH_P_IPV6: m = m_get(slirp); if (!m) return; - /* Note: we add to align the IP header */ - if (M_FREEROOM(m) < pkt_len + 2) { - m_inc(m, pkt_len + 2); + /* Note: we add 2 to align the IP header on 4 bytes, + * and add the margin for the tcpiphdr overhead */ + if (M_FREEROOM(m) < pkt_len + TCPIPHDR_DELTA + 2) { + m_inc(m, pkt_len + TCPIPHDR_DELTA + 2); } - m->m_len = pkt_len + 2; - memcpy(m->m_data + 2, pkt, pkt_len); + m->m_len = pkt_len + TCPIPHDR_DELTA + 2; + memcpy(m->m_data + TCPIPHDR_DELTA + 2, pkt, pkt_len); - m->m_data += 2 + ETH_HLEN; - m->m_len -= 2 + ETH_HLEN; + m->m_data += TCPIPHDR_DELTA + 2 + ETH_HLEN; + m->m_len -= TCPIPHDR_DELTA + 2 + ETH_HLEN; - ip_input(m); + if (proto == ETH_P_IP) { + ip_input(m); + } else if (proto == ETH_P_IPV6) { + ip6_input(m); + } break; + default: break; } } -/* Output the IP packet to the ethernet device. Returns 0 if the packet must be - * re-queued. +/* Prepare the IPv4 packet to be sent to the ethernet device. Returns 1 if no + * packet should be sent, 0 if the packet must be re-queued, 2 if the packet + * is ready to go. */ -int if_encap(Slirp *slirp, struct mbuf *ifm) +static int if_encap4(Slirp *slirp, struct mbuf *ifm, struct ethhdr *eh, + uint8_t ethaddr[ETH_ALEN]) { - uint8_t buf[1600]; - struct ethhdr *eh = (struct ethhdr *)buf; - uint8_t ethaddr[ETH_ALEN]; const struct ip *iph = (const struct ip *)ifm->m_data; - if (ifm->m_len + ETH_HLEN > sizeof(buf)) { - return 1; - } - if (iph->ip_dst.s_addr == 0) { /* 0.0.0.0 can not be a destination address, something went wrong, * avoid making it worse */ @@ -786,7 +822,7 @@ int if_encap(Slirp *slirp, struct mbuf *ifm) struct ethhdr *reh = (struct ethhdr *)arp_req; struct arphdr *rah = (struct arphdr *)(arp_req + ETH_HLEN); - if (!ifm->arp_requested) { + if (!ifm->resolution_requested) { /* If the client addr is not known, send an ARP request */ memset(reh->h_dest, 0xff, ETH_ALEN); memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4); @@ -812,22 +848,93 @@ int if_encap(Slirp *slirp, struct mbuf *ifm) rah->ar_tip = iph->ip_dst.s_addr; slirp->client_ipaddr = iph->ip_dst; slirp_output(slirp->opaque, arp_req, sizeof(arp_req)); - ifm->arp_requested = true; + ifm->resolution_requested = true; /* Expire request and drop outgoing packet after 1 second */ ifm->expiration_date = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + 1000000000ULL; } return 0; } else { - memcpy(eh->h_dest, ethaddr, ETH_ALEN); memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 4); /* XXX: not correct */ memcpy(&eh->h_source[2], &slirp->vhost_addr, 4); eh->h_proto = htons(ETH_P_IP); - memcpy(buf + sizeof(struct ethhdr), ifm->m_data, ifm->m_len); - slirp_output(slirp->opaque, buf, ifm->m_len + ETH_HLEN); + + /* Send this */ + return 2; + } +} + +/* Prepare the IPv6 packet to be sent to the ethernet device. Returns 1 if no + * packet should be sent, 0 if the packet must be re-queued, 2 if the packet + * is ready to go. + */ +static int if_encap6(Slirp *slirp, struct mbuf *ifm, struct ethhdr *eh, + uint8_t ethaddr[ETH_ALEN]) +{ + const struct ip6 *ip6h = mtod(ifm, const struct ip6 *); + if (!ndp_table_search(slirp, ip6h->ip_dst, ethaddr)) { + if (!ifm->resolution_requested) { + ndp_send_ns(slirp, ip6h->ip_dst); + ifm->resolution_requested = true; + ifm->expiration_date = + qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + 1000000000ULL; + } + return 0; + } else { + eh->h_proto = htons(ETH_P_IPV6); + in6_compute_ethaddr(ip6h->ip_src, eh->h_source); + + /* Send this */ + return 2; + } +} + +/* Output the IP packet to the ethernet device. Returns 0 if the packet must be + * re-queued. + */ +int if_encap(Slirp *slirp, struct mbuf *ifm) +{ + uint8_t buf[1600]; + struct ethhdr *eh = (struct ethhdr *)buf; + uint8_t ethaddr[ETH_ALEN]; + const struct ip *iph = (const struct ip *)ifm->m_data; + int ret; + + if (ifm->m_len + ETH_HLEN > sizeof(buf)) { return 1; } + + switch (iph->ip_v) { + case IPVERSION: + ret = if_encap4(slirp, ifm, eh, ethaddr); + if (ret < 2) { + return ret; + } + break; + + case IP6VERSION: + ret = if_encap6(slirp, ifm, eh, ethaddr); + if (ret < 2) { + return ret; + } + break; + + default: + g_assert_not_reached(); + break; + } + + memcpy(eh->h_dest, ethaddr, ETH_ALEN); + DEBUG_ARGS((dfd, " src = %02x:%02x:%02x:%02x:%02x:%02x\n", + eh->h_source[0], eh->h_source[1], eh->h_source[2], + eh->h_source[3], eh->h_source[4], eh->h_source[5])); + DEBUG_ARGS((dfd, " dst = %02x:%02x:%02x:%02x:%02x:%02x\n", + eh->h_dest[0], eh->h_dest[1], eh->h_dest[2], + eh->h_dest[3], eh->h_dest[4], eh->h_dest[5])); + memcpy(buf + sizeof(struct ethhdr), ifm->m_data, ifm->m_len); + slirp_output(slirp->opaque, buf, ifm->m_len + ETH_HLEN); + return 1; } /* Drop host forwarding rule, return 0 if found. */ @@ -1011,10 +1118,26 @@ static void slirp_sbuf_save(QEMUFile *f, struct sbuf *sbuf) static void slirp_socket_save(QEMUFile *f, struct socket *so) { qemu_put_be32(f, so->so_urgc); - qemu_put_be32(f, so->so_faddr.s_addr); - qemu_put_be32(f, so->so_laddr.s_addr); - qemu_put_be16(f, so->so_fport); - qemu_put_be16(f, so->so_lport); + qemu_put_be16(f, so->so_ffamily); + switch (so->so_ffamily) { + case AF_INET: + qemu_put_be32(f, so->so_faddr.s_addr); + qemu_put_be16(f, so->so_fport); + break; + default: + error_report( + "so_ffamily unknown, unable to save so_faddr and so_fport\n"); + } + qemu_put_be16(f, so->so_lfamily); + switch (so->so_lfamily) { + case AF_INET: + qemu_put_be32(f, so->so_laddr.s_addr); + qemu_put_be16(f, so->so_lport); + break; + default: + error_report( + "so_ffamily unknown, unable to save so_laddr and so_lport\n"); + } qemu_put_byte(f, so->so_iptos); qemu_put_byte(f, so->so_emu); qemu_put_byte(f, so->so_type); @@ -1128,16 +1251,40 @@ static int slirp_sbuf_load(QEMUFile *f, struct sbuf *sbuf) return 0; } -static int slirp_socket_load(QEMUFile *f, struct socket *so) +static int slirp_socket_load(QEMUFile *f, struct socket *so, int version_id) { if (tcp_attach(so) < 0) return -ENOMEM; so->so_urgc = qemu_get_be32(f); - so->so_faddr.s_addr = qemu_get_be32(f); - so->so_laddr.s_addr = qemu_get_be32(f); - so->so_fport = qemu_get_be16(f); - so->so_lport = qemu_get_be16(f); + if (version_id <= 3) { + so->so_ffamily = AF_INET; + so->so_faddr.s_addr = qemu_get_be32(f); + so->so_laddr.s_addr = qemu_get_be32(f); + so->so_fport = qemu_get_be16(f); + so->so_lport = qemu_get_be16(f); + } else { + so->so_ffamily = qemu_get_be16(f); + switch (so->so_ffamily) { + case AF_INET: + so->so_faddr.s_addr = qemu_get_be32(f); + so->so_fport = qemu_get_be16(f); + break; + default: + error_report( + "so_ffamily unknown, unable to restore so_faddr and so_lport"); + } + so->so_lfamily = qemu_get_be16(f); + switch (so->so_lfamily) { + case AF_INET: + so->so_laddr.s_addr = qemu_get_be32(f); + so->so_lport = qemu_get_be16(f); + break; + default: + error_report( + "so_ffamily unknown, unable to restore so_laddr and so_lport"); + } + } so->so_iptos = qemu_get_byte(f); so->so_emu = qemu_get_byte(f); so->so_type = qemu_get_byte(f); @@ -1173,7 +1320,7 @@ static int slirp_state_load(QEMUFile *f, void *opaque, int version_id) if (!so) return -ENOMEM; - ret = slirp_socket_load(f, so); + ret = slirp_socket_load(f, so, version_id); if (ret < 0) return ret; diff --git a/qemu/slirp/slirp.h b/qemu/slirp/slirp.h index 6589d7eef..203deec48 100644 --- a/qemu/slirp/slirp.h +++ b/qemu/slirp/slirp.h @@ -1,11 +1,9 @@ #ifndef __COMMON_H__ #define __COMMON_H__ -#include "config-host.h" #include "slirp_config.h" #ifdef _WIN32 -# include typedef char *caddr_t; @@ -16,52 +14,20 @@ typedef char *caddr_t; # include #else -# define ioctlsocket ioctl -# define closesocket(s) close(s) # if !defined(__HAIKU__) # define O_BINARY 0 # endif #endif -#include #ifdef HAVE_SYS_BITYPES_H # include #endif -#include - -#ifdef HAVE_UNISTD_H -# include -#endif - -#ifdef HAVE_STDLIB_H -# include -#endif - -#include -#include #ifndef HAVE_MEMMOVE #define memmove(x, y, z) bcopy(y, x, z) #endif -#if TIME_WITH_SYS_TIME -# include -# include -#else -# ifdef HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif - -#ifdef HAVE_STRING_H -# include -#else -# include -#endif - #ifndef _WIN32 #include #endif @@ -82,11 +48,9 @@ void *malloc(size_t arg); void free(void *ptr); #endif -#include #ifndef NO_UNIX_SOCKETS #include #endif -#include #ifdef HAVE_SYS_SIGNAL_H # include #endif @@ -114,36 +78,34 @@ void free(void *ptr); #include #endif -#ifdef __STDC__ -#include -#else -#include -#endif - -#include - /* Avoid conflicting with the libc insque() and remque(), which have different prototypes. */ #define insque slirp_insque #define remque slirp_remque +#define quehead slirp_quehead #ifdef HAVE_SYS_STROPTS_H #include #endif +#include + #include "debug.h" #include "qemu/queue.h" #include "qemu/sockets.h" +#include "net/eth.h" #include "libslirp.h" #include "ip.h" +#include "ip6.h" #include "tcp.h" #include "tcp_timer.h" #include "tcp_var.h" #include "tcpip.h" #include "udp.h" #include "ip_icmp.h" +#include "ip6_icmp.h" #include "mbuf.h" #include "sbuf.h" #include "socket.h" @@ -158,12 +120,6 @@ void free(void *ptr); #include "bootp.h" #include "tftp.h" -#define ETH_ALEN 6 -#define ETH_HLEN 14 - -#define ETH_P_IP 0x0800 /* Internet Protocol packet */ -#define ETH_P_ARP 0x0806 /* Address Resolution packet */ - #define ARPOP_REQUEST 1 /* ARP request */ #define ARPOP_REPLY 2 /* ARP reply */ @@ -201,18 +157,41 @@ void arp_table_add(Slirp *slirp, uint32_t ip_addr, uint8_t ethaddr[ETH_ALEN]); bool arp_table_search(Slirp *slirp, uint32_t ip_addr, uint8_t out_ethaddr[ETH_ALEN]); +struct ndpentry { + unsigned char eth_addr[ETH_ALEN]; /* sender hardware address */ + struct in6_addr ip_addr; /* sender IP address */ +} QEMU_PACKED; + +#define NDP_TABLE_SIZE 16 + +typedef struct NdpTable { + struct ndpentry table[NDP_TABLE_SIZE]; + int next_victim; +} NdpTable; + +void ndp_table_add(Slirp *slirp, struct in6_addr ip_addr, + uint8_t ethaddr[ETH_ALEN]); +bool ndp_table_search(Slirp *slirp, struct in6_addr ip_addr, + uint8_t out_ethaddr[ETH_ALEN]); + struct Slirp { QTAILQ_ENTRY(Slirp) entry; u_int time_fasttimo; u_int last_slowtimo; bool do_slowtimo; + bool in_enabled, in6_enabled; + /* virtual network configuration */ struct in_addr vnetwork_addr; struct in_addr vnetwork_mask; struct in_addr vhost_addr; + struct in6_addr vprefix_addr6; + uint8_t vprefix_len; + struct in6_addr vhost_addr6; struct in_addr vdhcp_startaddr; struct in_addr vnameserver_addr; + struct in6_addr vnameserver_addr6; struct in_addr client_ipaddr; char client_hostname[33]; @@ -221,12 +200,13 @@ struct Slirp { struct ex_list *exec_list; /* mbuf states */ - struct mbuf m_freelist, m_usedlist; + struct quehead m_freelist; + struct quehead m_usedlist; int mbuf_alloced; /* if states */ - struct mbuf if_fastq; /* fast queue (for interactive data) */ - struct mbuf if_batchq; /* queue for non-interactive data */ + struct quehead if_fastq; /* fast queue (for interactive data) */ + struct quehead if_batchq; /* queue for non-interactive data */ struct mbuf *next_m; /* pointer to next mbuf to output */ bool if_start_busy; /* avoid if_start recursion */ @@ -259,6 +239,10 @@ struct Slirp { struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX]; ArpTable arp_table; + NdpTable ndp_table; + + GRand *grand; + QEMUTimer *ra_timer; void *opaque; }; @@ -301,6 +285,7 @@ int translate_dnssearch(Slirp *s, const char ** names); /* cksum.c */ int cksum(struct mbuf *m, int len); +int ip6_cksum(struct mbuf *m); /* if.c */ void if_init(Slirp *); @@ -316,8 +301,16 @@ void ip_stripoptions(register struct mbuf *, struct mbuf *); /* ip_output.c */ int ip_output(struct socket *, struct mbuf *); +/* ip6_input.c */ +void ip6_init(Slirp *); +void ip6_cleanup(Slirp *); +void ip6_input(struct mbuf *); + +/* ip6_output */ +int ip6_output(struct socket *, struct mbuf *, int fast); + /* tcp_input.c */ -void tcp_input(register struct mbuf *, int, struct socket *); +void tcp_input(register struct mbuf *, int, struct socket *, unsigned short af); int tcp_mss(register struct tcpcb *, u_int); /* tcp_output.c */ @@ -328,11 +321,12 @@ void tcp_setpersist(register struct tcpcb *); void tcp_init(Slirp *); void tcp_cleanup(Slirp *); void tcp_template(struct tcpcb *); -void tcp_respond(struct tcpcb *, register struct tcpiphdr *, register struct mbuf *, tcp_seq, tcp_seq, int); +void tcp_respond(struct tcpcb *, register struct tcpiphdr *, + register struct mbuf *, tcp_seq, tcp_seq, int, unsigned short); struct tcpcb * tcp_newtcpcb(struct socket *); struct tcpcb * tcp_close(register struct tcpcb *); void tcp_sockclosed(struct tcpcb *); -int tcp_fconnect(struct socket *); +int tcp_fconnect(struct socket *, unsigned short af); void tcp_connect(struct socket *); int tcp_attach(struct socket *); uint8_t tcp_tos(struct socket *); @@ -353,9 +347,4 @@ struct tcpcb *tcp_drop(struct tcpcb *tp, int err); #define max(x,y) ((x) > (y) ? (x) : (y)) #endif -#ifdef _WIN32 -#undef errno -#define errno (WSAGetLastError()) -#endif - #endif diff --git a/qemu/slirp/socket.c b/qemu/slirp/socket.c index 37ac5cf2f..a10eff18c 100644 --- a/qemu/slirp/socket.c +++ b/qemu/slirp/socket.c @@ -5,6 +5,7 @@ * terms and conditions of the copyright. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include #include "ip_icmp.h" @@ -15,24 +16,26 @@ static void sofcantrcvmore(struct socket *so); static void sofcantsendmore(struct socket *so); -struct socket * -solookup(struct socket *head, struct in_addr laddr, u_int lport, - struct in_addr faddr, u_int fport) +struct socket *solookup(struct socket **last, struct socket *head, + struct sockaddr_storage *lhost, struct sockaddr_storage *fhost) { - struct socket *so; - - for (so = head->so_next; so != head; so = so->so_next) { - if (so->so_lport == lport && - so->so_laddr.s_addr == laddr.s_addr && - so->so_faddr.s_addr == faddr.s_addr && - so->so_fport == fport) - break; - } - - if (so == head) - return (struct socket *)NULL; - return so; + struct socket *so = *last; + + /* Optimisation */ + if (so != head && sockaddr_equal(&(so->lhost.ss), lhost) + && (!fhost || sockaddr_equal(&so->fhost.ss, fhost))) { + return so; + } + + for (so = head->so_next; so != head; so = so->so_next) { + if (sockaddr_equal(&(so->lhost.ss), lhost) + && (!fhost || sockaddr_equal(&so->fhost.ss, fhost))) { + *last = so; + return so; + } + } + return (struct socket *)NULL; } /* @@ -91,7 +94,7 @@ size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np) int mss = so->so_tcpcb->t_maxseg; DEBUG_CALL("sopreprbuf"); - DEBUG_ARG("so = %lx", (long )so); + DEBUG_ARG("so = %p", so); if (len <= 0) return 0; @@ -155,7 +158,7 @@ soread(struct socket *so) struct iovec iov[2]; DEBUG_CALL("soread"); - DEBUG_ARG("so = %lx", (long )so); + DEBUG_ARG("so = %p", so); /* * No need to check if there's enough room to read. @@ -173,9 +176,24 @@ soread(struct socket *so) if (nn < 0 && (errno == EINTR || errno == EAGAIN)) return 0; else { + int err; + socklen_t slen = sizeof err; + + err = errno; + if (nn == 0) { + getsockopt(so->s, SOL_SOCKET, SO_ERROR, + &err, &slen); + } + DEBUG_MISC((dfd, " --- soread() disconnected, nn = %d, errno = %d-%s\n", nn, errno,strerror(errno))); sofcantrcvmore(so); - tcp_sockclosed(sototcpcb(so)); + + if (err == ECONNRESET || err == ECONNREFUSED + || err == ENOTCONN || err == EPIPE) { + tcp_drop(sototcpcb(so), err); + } else { + tcp_sockclosed(sototcpcb(so)); + } return -1; } } @@ -215,7 +233,7 @@ int soreadbuf(struct socket *so, const char *buf, int size) struct iovec iov[2]; DEBUG_CALL("soreadbuf"); - DEBUG_ARG("so = %lx", (long )so); + DEBUG_ARG("so = %p", so); /* * No need to check if there's enough room to read. @@ -257,13 +275,14 @@ err: * so when OOB data arrives, we soread() it and everything * in the send buffer is sent as urgent data */ -void +int sorecvoob(struct socket *so) { struct tcpcb *tp = sototcpcb(so); + int ret; DEBUG_CALL("sorecvoob"); - DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("so = %p", so); /* * We take a guess at how much urgent data has arrived. @@ -273,11 +292,15 @@ sorecvoob(struct socket *so) * urgent data, or the read() doesn't return all the * urgent data. */ - soread(so); - tp->snd_up = tp->snd_una + so->so_snd.sb_cc; - tp->t_force = 1; - tcp_output(tp); - tp->t_force = 0; + ret = soread(so); + if (ret > 0) { + tp->snd_up = tp->snd_una + so->so_snd.sb_cc; + tp->t_force = 1; + tcp_output(tp); + tp->t_force = 0; + } + + return ret; } /* @@ -293,7 +316,7 @@ sosendoob(struct socket *so) int n, len; DEBUG_CALL("sosendoob"); - DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("so = %p", so); DEBUG_ARG("sb->sb_cc = %d", sb->sb_cc); if (so->so_urgc > 2048) @@ -351,7 +374,7 @@ sowrite(struct socket *so) struct iovec iov[2]; DEBUG_CALL("sowrite"); - DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("so = %p", so); if (so->so_urgc) { sosendoob(so); @@ -437,11 +460,12 @@ sowrite(struct socket *so) void sorecvfrom(struct socket *so) { - struct sockaddr_in addr; - socklen_t addrlen = sizeof(struct sockaddr_in); + struct sockaddr_storage addr; + struct sockaddr_storage saddr, daddr; + socklen_t addrlen = sizeof(struct sockaddr_storage); DEBUG_CALL("sorecvfrom"); - DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("so = %p", so); if (so->so_type == IPPROTO_ICMP) { /* This is a "ping" reply */ char buff[256]; @@ -459,7 +483,7 @@ sorecvfrom(struct socket *so) DEBUG_MISC((dfd," udp icmp rx errno = %d-%s\n", errno,strerror(errno))); - icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno)); + icmp_send_error(so->so_m, ICMP_UNREACH, code, 0, strerror(errno)); } else { icmp_reflect(so->so_m); so->so_m = NULL; /* Don't m_free() it again! */ @@ -479,7 +503,18 @@ sorecvfrom(struct socket *so) if (!m) { return; } - m->m_data += IF_MAXLINKHDR; + switch (so->so_ffamily) { + case AF_INET: + m->m_data += IF_MAXLINKHDR + sizeof(struct udpiphdr); + break; + case AF_INET6: + m->m_data += IF_MAXLINKHDR + sizeof(struct ip6) + + sizeof(struct udphdr); + break; + default: + g_assert_not_reached(); + break; + } /* * XXX Shouldn't FIONREAD packets destined for port 53, @@ -501,13 +536,37 @@ sorecvfrom(struct socket *so) DEBUG_MISC((dfd, " did recvfrom %d, errno = %d-%s\n", m->m_len, errno,strerror(errno))); if(m->m_len<0) { - u_char code=ICMP_UNREACH_PORT; - - if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST; - else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET; - - DEBUG_MISC((dfd," rx error, tx icmp ICMP_UNREACH:%i\n", code)); - icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno)); + /* Report error as ICMP */ + switch (so->so_lfamily) { + uint8_t code; + case AF_INET: + code = ICMP_UNREACH_PORT; + + if (errno == EHOSTUNREACH) { + code = ICMP_UNREACH_HOST; + } else if (errno == ENETUNREACH) { + code = ICMP_UNREACH_NET; + } + + DEBUG_MISC((dfd, " rx error, tx icmp ICMP_UNREACH:%i\n", code)); + icmp_send_error(so->so_m, ICMP_UNREACH, code, 0, strerror(errno)); + break; + case AF_INET6: + code = ICMP6_UNREACH_PORT; + + if (errno == EHOSTUNREACH) { + code = ICMP6_UNREACH_ADDRESS; + } else if (errno == ENETUNREACH) { + code = ICMP6_UNREACH_NO_ROUTE; + } + + DEBUG_MISC((dfd, " rx error, tx icmp6 ICMP_UNREACH:%i\n", code)); + icmp6_send_error(so->so_m, ICMP6_UNREACH, code); + break; + default: + g_assert_not_reached(); + break; + } m_free(m); } else { /* @@ -525,9 +584,26 @@ sorecvfrom(struct socket *so) /* * If this packet was destined for CTL_ADDR, - * make it look like that's where it came from, done by udp_output + * make it look like that's where it came from */ - udp_output(so, m, &addr); + saddr = addr; + sotranslate_in(so, &saddr); + daddr = so->lhost.ss; + + switch (so->so_ffamily) { + case AF_INET: + udp_output(so, m, (struct sockaddr_in *) &saddr, + (struct sockaddr_in *) &daddr, + so->so_iptos); + break; + case AF_INET6: + udp6_output(so, m, (struct sockaddr_in6 *) &saddr, + (struct sockaddr_in6 *) &daddr); + break; + default: + g_assert_not_reached(); + break; + } } /* rx error */ } /* if ping packet */ } @@ -538,33 +614,20 @@ sorecvfrom(struct socket *so) int sosendto(struct socket *so, struct mbuf *m) { - Slirp *slirp = so->slirp; int ret; - struct sockaddr_in addr; + struct sockaddr_storage addr; DEBUG_CALL("sosendto"); - DEBUG_ARG("so = %lx", (long)so); - DEBUG_ARG("m = %lx", (long)m); - - addr.sin_family = AF_INET; - if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == - slirp->vnetwork_addr.s_addr) { - /* It's an alias */ - if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) { - if (get_dns_addr(&addr.sin_addr) < 0) - addr.sin_addr = loopback_addr; - } else { - addr.sin_addr = loopback_addr; - } - } else - addr.sin_addr = so->so_faddr; - addr.sin_port = so->so_fport; + DEBUG_ARG("so = %p", so); + DEBUG_ARG("m = %p", m); - DEBUG_MISC((dfd, " sendto()ing, addr.sin_port=%d, addr.sin_addr.s_addr=%.16s\n", ntohs(addr.sin_port), inet_ntoa(addr.sin_addr))); + addr = so->fhost.ss; + DEBUG_CALL(" sendto()ing)"); + sotranslate_out(so, &addr); /* Don't care what port we get */ ret = sendto(so->s, m->m_data, m->m_len, 0, - (struct sockaddr *)&addr, sizeof (struct sockaddr)); + (struct sockaddr *)&addr, sockaddr_size(&addr)); if (ret < 0) return -1; @@ -619,6 +682,7 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, so->so_state &= SS_PERSISTENT_MASK; so->so_state |= (SS_FACCEPTCONN | flags); + so->so_lfamily = AF_INET; so->so_lport = lport; /* Kept in network format */ so->so_laddr.s_addr = laddr; /* Ditto */ @@ -645,6 +709,7 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); getsockname(s,(struct sockaddr *)&addr,&addrlen); + so->so_ffamily = AF_INET; so->so_fport = addr.sin_port; if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr) so->so_faddr = slirp->vhost_addr; @@ -718,3 +783,113 @@ sofwdrain(struct socket *so) else sofcantsendmore(so); } + +/* + * Translate addr in host addr when it is a virtual address + */ +void sotranslate_out(struct socket *so, struct sockaddr_storage *addr) +{ + Slirp *slirp = so->slirp; + struct sockaddr_in *sin = (struct sockaddr_in *)addr; + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr; + + switch (addr->ss_family) { + case AF_INET: + if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == + slirp->vnetwork_addr.s_addr) { + /* It's an alias */ + if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) { + if (get_dns_addr(&sin->sin_addr) < 0) { + sin->sin_addr = loopback_addr; + } + } else { + sin->sin_addr = loopback_addr; + } + } + + DEBUG_MISC((dfd, " addr.sin_port=%d, " + "addr.sin_addr.s_addr=%.16s\n", + ntohs(sin->sin_port), inet_ntoa(sin->sin_addr))); + break; + + case AF_INET6: + if (in6_equal_net(&so->so_faddr6, &slirp->vprefix_addr6, + slirp->vprefix_len)) { + if (in6_equal(&so->so_faddr6, &slirp->vnameserver_addr6)) { + /*if (get_dns_addr(&addr) < 0) {*/ /* TODO */ + sin6->sin6_addr = in6addr_loopback; + /*}*/ + } else { + sin6->sin6_addr = in6addr_loopback; + } + } + break; + + default: + break; + } +} + +void sotranslate_in(struct socket *so, struct sockaddr_storage *addr) +{ + Slirp *slirp = so->slirp; + struct sockaddr_in *sin = (struct sockaddr_in *)addr; + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr; + + switch (addr->ss_family) { + case AF_INET: + if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == + slirp->vnetwork_addr.s_addr) { + uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr; + + if ((so->so_faddr.s_addr & inv_mask) == inv_mask) { + sin->sin_addr = slirp->vhost_addr; + } else if (sin->sin_addr.s_addr == loopback_addr.s_addr || + so->so_faddr.s_addr != slirp->vhost_addr.s_addr) { + sin->sin_addr = so->so_faddr; + } + } + break; + + case AF_INET6: + if (in6_equal_net(&so->so_faddr6, &slirp->vprefix_addr6, + slirp->vprefix_len)) { + if (in6_equal(&sin6->sin6_addr, &in6addr_loopback) + || !in6_equal(&so->so_faddr6, &slirp->vhost_addr6)) { + sin6->sin6_addr = so->so_faddr6; + } + } + break; + + default: + break; + } +} + +/* + * Translate connections from localhost to the real hostname + */ +void sotranslate_accept(struct socket *so) +{ + Slirp *slirp = so->slirp; + + switch (so->so_ffamily) { + case AF_INET: + if (so->so_faddr.s_addr == INADDR_ANY || + (so->so_faddr.s_addr & loopback_mask) == + (loopback_addr.s_addr & loopback_mask)) { + so->so_faddr = slirp->vhost_addr; + } + break; + + case AF_INET6: + if (in6_equal(&so->so_faddr6, &in6addr_any) || + in6_equal(&so->so_faddr6, &in6addr_loopback)) { + so->so_faddr6 = slirp->vhost_addr6; + } + break; + + default: + break; + } +} diff --git a/qemu/slirp/socket.h b/qemu/slirp/socket.h index 57e0407eb..b602e69b9 100644 --- a/qemu/slirp/socket.h +++ b/qemu/slirp/socket.h @@ -31,10 +31,27 @@ struct socket { struct tcpiphdr *so_ti; /* Pointer to the original ti within * so_mconn, for non-blocking connections */ int so_urgc; - struct in_addr so_faddr; /* foreign host table entry */ - struct in_addr so_laddr; /* local host table entry */ - uint16_t so_fport; /* foreign port */ - uint16_t so_lport; /* local port */ + union { /* foreign host */ + struct sockaddr_storage ss; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } fhost; +#define so_faddr fhost.sin.sin_addr +#define so_fport fhost.sin.sin_port +#define so_faddr6 fhost.sin6.sin6_addr +#define so_fport6 fhost.sin6.sin6_port +#define so_ffamily fhost.ss.ss_family + + union { /* local host */ + struct sockaddr_storage ss; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } lhost; +#define so_laddr lhost.sin.sin_addr +#define so_lport lhost.sin.sin_port +#define so_laddr6 lhost.sin6.sin6_addr +#define so_lport6 lhost.sin6.sin6_port +#define so_lfamily lhost.ss.ss_family uint8_t so_iptos; /* Type of service */ uint8_t so_emu; /* Is the socket emulated? */ @@ -76,11 +93,53 @@ struct socket { #define SS_HOSTFWD 0x1000 /* Socket describes host->guest forwarding */ #define SS_INCOMING 0x2000 /* Connection was initiated by a host on the internet */ -struct socket * solookup(struct socket *, struct in_addr, u_int, struct in_addr, u_int); -struct socket * socreate(Slirp *); +static inline int sockaddr_equal(struct sockaddr_storage *a, + struct sockaddr_storage *b) +{ + if (a->ss_family != b->ss_family) { + return 0; + } + + switch (a->ss_family) { + case AF_INET: + { + struct sockaddr_in *a4 = (struct sockaddr_in *) a; + struct sockaddr_in *b4 = (struct sockaddr_in *) b; + return a4->sin_addr.s_addr == b4->sin_addr.s_addr + && a4->sin_port == b4->sin_port; + } + case AF_INET6: + { + struct sockaddr_in6 *a6 = (struct sockaddr_in6 *) a; + struct sockaddr_in6 *b6 = (struct sockaddr_in6 *) b; + return (in6_equal(&a6->sin6_addr, &b6->sin6_addr) + && a6->sin6_port == b6->sin6_port); + } + default: + g_assert_not_reached(); + } + + return 0; +} + +static inline socklen_t sockaddr_size(struct sockaddr_storage *a) +{ + switch (a->ss_family) { + case AF_INET: + return sizeof(struct sockaddr_in); + case AF_INET6: + return sizeof(struct sockaddr_in6); + default: + g_assert_not_reached(); + } +} + +struct socket *solookup(struct socket **, struct socket *, + struct sockaddr_storage *, struct sockaddr_storage *); +struct socket *socreate(Slirp *); void sofree(struct socket *); int soread(struct socket *); -void sorecvoob(struct socket *); +int sorecvoob(struct socket *); int sosendoob(struct socket *); int sowrite(struct socket *); void sorecvfrom(struct socket *); @@ -94,4 +153,9 @@ struct iovec; /* For win32 */ size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np); int soreadbuf(struct socket *so, const char *buf, int size); +void sotranslate_out(struct socket *, struct sockaddr_storage *); +void sotranslate_in(struct socket *, struct sockaddr_storage *); +void sotranslate_accept(struct socket *); + + #endif /* _SOCKET_H_ */ diff --git a/qemu/slirp/tcp.h b/qemu/slirp/tcp.h index 2e2b4033a..61befcde5 100644 --- a/qemu/slirp/tcp.h +++ b/qemu/slirp/tcp.h @@ -106,6 +106,8 @@ struct tcphdr { */ #undef TCP_MSS #define TCP_MSS 1460 +#undef TCP6_MSS +#define TCP6_MSS 1440 #undef TCP_MAXWIN #define TCP_MAXWIN 65535 /* largest value for (unscaled) window */ diff --git a/qemu/slirp/tcp_input.c b/qemu/slirp/tcp_input.c index f946db8dc..e2b5d4ebb 100644 --- a/qemu/slirp/tcp_input.c +++ b/qemu/slirp/tcp_input.c @@ -38,6 +38,7 @@ * terms and conditions of the copyright. */ +#include "qemu/osdep.h" #include #include "ip_icmp.h" @@ -213,9 +214,10 @@ present: * protocol specification dated September, 1981 very closely. */ void -tcp_input(struct mbuf *m, int iphlen, struct socket *inso) +tcp_input(struct mbuf *m, int iphlen, struct socket *inso, unsigned short af) { - struct ip save_ip, *ip; + struct ip save_ip, *ip; + struct ip6 save_ip6, *ip6; register struct tcpiphdr *ti; caddr_t optp = NULL; int optlen = 0; @@ -227,12 +229,15 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso) int iss = 0; u_long tiwin; int ret; + struct sockaddr_storage lhost, fhost; + struct sockaddr_in *lhost4, *fhost4; + struct sockaddr_in6 *lhost6, *fhost6; struct ex_list *ex_ptr; Slirp *slirp; DEBUG_CALL("tcp_input"); - DEBUG_ARGS((dfd, " m = %8lx iphlen = %2d inso = %lx\n", - (long )m, iphlen, (long )inso )); + DEBUG_ARGS((dfd, " m = %p iphlen = %2d inso = %p\n", + m, iphlen, inso)); /* * If called with m == 0, then we're continuing the connect @@ -253,37 +258,83 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso) } slirp = m->slirp; - /* - * Get IP and TCP header together in first mbuf. - * Note: IP leaves IP header in first mbuf. - */ - ti = mtod(m, struct tcpiphdr *); - if (iphlen > sizeof(struct ip )) { - ip_stripoptions(m, (struct mbuf *)0); - iphlen=sizeof(struct ip ); - } - /* XXX Check if too short */ + ip = mtod(m, struct ip *); + ip6 = mtod(m, struct ip6 *); + switch (af) { + case AF_INET: + if (iphlen > sizeof(struct ip)) { + ip_stripoptions(m, (struct mbuf *)0); + iphlen = sizeof(struct ip); + } + /* XXX Check if too short */ - /* - * Save a copy of the IP header in case we want restore it - * for sending an ICMP error message in response. - */ - ip=mtod(m, struct ip *); - save_ip = *ip; - save_ip.ip_len+= iphlen; - /* - * Checksum extended TCP header and data. - */ - tlen = ((struct ip *)ti)->ip_len; - tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL; - memset(&ti->ti_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr)); - ti->ti_x1 = 0; - ti->ti_len = htons((uint16_t)tlen); - len = sizeof(struct ip ) + tlen; - if(cksum(m, len)) { - goto drop; + /* + * Save a copy of the IP header in case we want restore it + * for sending an ICMP error message in response. + */ + save_ip = *ip; + save_ip.ip_len += iphlen; + + /* + * Get IP and TCP header together in first mbuf. + * Note: IP leaves IP header in first mbuf. + */ + m->m_data -= sizeof(struct tcpiphdr) - sizeof(struct ip) + - sizeof(struct tcphdr); + m->m_len += sizeof(struct tcpiphdr) - sizeof(struct ip) + - sizeof(struct tcphdr); + ti = mtod(m, struct tcpiphdr *); + + /* + * Checksum extended TCP header and data. + */ + tlen = ip->ip_len; + tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL; + memset(&ti->ih_mbuf, 0 , sizeof(struct mbuf_ptr)); + memset(&ti->ti, 0, sizeof(ti->ti)); + ti->ti_x0 = 0; + ti->ti_src = save_ip.ip_src; + ti->ti_dst = save_ip.ip_dst; + ti->ti_pr = save_ip.ip_p; + ti->ti_len = htons((uint16_t)tlen); + break; + + case AF_INET6: + /* + * Save a copy of the IP header in case we want restore it + * for sending an ICMP error message in response. + */ + save_ip6 = *ip6; + /* + * Get IP and TCP header together in first mbuf. + * Note: IP leaves IP header in first mbuf. + */ + m->m_data -= sizeof(struct tcpiphdr) - (sizeof(struct ip6) + + sizeof(struct tcphdr)); + m->m_len += sizeof(struct tcpiphdr) - (sizeof(struct ip6) + + sizeof(struct tcphdr)); + ti = mtod(m, struct tcpiphdr *); + + tlen = ip6->ip_pl; + tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL; + memset(&ti->ih_mbuf, 0 , sizeof(struct mbuf_ptr)); + memset(&ti->ti, 0, sizeof(ti->ti)); + ti->ti_x0 = 0; + ti->ti_src6 = save_ip6.ip_src; + ti->ti_dst6 = save_ip6.ip_dst; + ti->ti_nh6 = save_ip6.ip_nh; + ti->ti_len = htons((uint16_t)tlen); + break; + + default: + g_assert_not_reached(); + } + + len = ((sizeof(struct tcpiphdr) - sizeof(struct tcphdr)) + tlen); + if (cksum(m, len)) { + goto drop; } /* @@ -320,17 +371,31 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso) * Locate pcb for segment. */ findso: - so = slirp->tcp_last_so; - if (so->so_fport != ti->ti_dport || - so->so_lport != ti->ti_sport || - so->so_laddr.s_addr != ti->ti_src.s_addr || - so->so_faddr.s_addr != ti->ti_dst.s_addr) { - so = solookup(&slirp->tcb, ti->ti_src, ti->ti_sport, - ti->ti_dst, ti->ti_dport); - if (so) - slirp->tcp_last_so = so; + lhost.ss_family = af; + fhost.ss_family = af; + switch (af) { + case AF_INET: + lhost4 = (struct sockaddr_in *) &lhost; + lhost4->sin_addr = ti->ti_src; + lhost4->sin_port = ti->ti_sport; + fhost4 = (struct sockaddr_in *) &fhost; + fhost4->sin_addr = ti->ti_dst; + fhost4->sin_port = ti->ti_dport; + break; + case AF_INET6: + lhost6 = (struct sockaddr_in6 *) &lhost; + lhost6->sin6_addr = ti->ti_src6; + lhost6->sin6_port = ti->ti_sport; + fhost6 = (struct sockaddr_in6 *) &fhost; + fhost6->sin6_addr = ti->ti_dst6; + fhost6->sin6_port = ti->ti_dport; + break; + default: + g_assert_not_reached(); } + so = solookup(&slirp->tcp_last_so, &slirp->tcb, &lhost, &fhost); + /* * If the state is CLOSED (i.e., TCB does not exist) then * all data in the incoming segment is discarded. @@ -374,13 +439,21 @@ findso: sbreserve(&so->so_snd, TCP_SNDSPACE); sbreserve(&so->so_rcv, TCP_RCVSPACE); - so->so_laddr = ti->ti_src; - so->so_lport = ti->ti_sport; - so->so_faddr = ti->ti_dst; - so->so_fport = ti->ti_dport; - - if ((so->so_iptos = tcp_tos(so)) == 0) - so->so_iptos = ((struct ip *)ti)->ip_tos; + so->lhost.ss = lhost; + so->fhost.ss = fhost; + + so->so_iptos = tcp_tos(so); + if (so->so_iptos == 0) { + switch (af) { + case AF_INET: + so->so_iptos = ((struct ip *)ti)->ip_tos; + break; + case AF_INET6: + break; + default: + g_assert_not_reached(); + } + } tp = sototcpcb(so); tp->t_state = TCPS_LISTEN; @@ -559,8 +632,9 @@ findso: * If this is destined for the control address, then flag to * tcp_ctl once connected, otherwise connect */ - if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == - slirp->vnetwork_addr.s_addr) { + if (af == AF_INET && + (so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == + slirp->vnetwork_addr.s_addr) { if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr && so->so_faddr.s_addr != slirp->vnameserver_addr.s_addr) { /* May be an add exec */ @@ -584,24 +658,60 @@ findso: goto cont_input; } - if((tcp_fconnect(so) == -1) && (errno != EINPROGRESS) && (errno != EWOULDBLOCK)) { - u_char code=ICMP_UNREACH_NET; + if ((tcp_fconnect(so, so->so_ffamily) == -1) && + (errno != EAGAIN) && + (errno != EINPROGRESS) && (errno != EWOULDBLOCK) + ) { + uint8_t code; DEBUG_MISC((dfd, " tcp fconnect errno = %d-%s\n", errno,strerror(errno))); if(errno == ECONNREFUSED) { /* ACK the SYN, send RST to refuse the connection */ - tcp_respond(tp, ti, m, ti->ti_seq+1, (tcp_seq)0, - TH_RST|TH_ACK); + tcp_respond(tp, ti, m, ti->ti_seq + 1, (tcp_seq) 0, + TH_RST | TH_ACK, af); } else { - if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST; + switch (af) { + case AF_INET: + code = ICMP_UNREACH_NET; + if (errno == EHOSTUNREACH) { + code = ICMP_UNREACH_HOST; + } + break; + case AF_INET6: + code = ICMP6_UNREACH_NO_ROUTE; + if (errno == EHOSTUNREACH) { + code = ICMP6_UNREACH_ADDRESS; + } + break; + default: + g_assert_not_reached(); + } HTONL(ti->ti_seq); /* restore tcp header */ HTONL(ti->ti_ack); HTONS(ti->ti_win); HTONS(ti->ti_urp); m->m_data -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr); m->m_len += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr); - *ip=save_ip; - icmp_error(m, ICMP_UNREACH,code, 0,strerror(errno)); + switch (af) { + case AF_INET: + m->m_data += sizeof(struct tcpiphdr) - sizeof(struct ip) + - sizeof(struct tcphdr); + m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct ip) + - sizeof(struct tcphdr); + *ip = save_ip; + icmp_send_error(m, ICMP_UNREACH, code, 0, strerror(errno)); + break; + case AF_INET6: + m->m_data += sizeof(struct tcpiphdr) - (sizeof(struct ip6) + + sizeof(struct tcphdr)); + m->m_len -= sizeof(struct tcpiphdr) - (sizeof(struct ip6) + + sizeof(struct tcphdr)); + *ip6 = save_ip6; + icmp6_send_error(m, ICMP6_UNREACH, code); + break; + default: + g_assert_not_reached(); + } } tcp_close(tp); m_free(m); @@ -616,6 +726,12 @@ findso: so->so_ti = ti; tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; tp->t_state = TCPS_SYN_RECEIVED; + /* + * Initialize receive sequence numbers now so that we can send a + * valid RST if the remote end rejects our connection. + */ + tp->irs = ti->ti_seq; + tcp_rcvseqinit(tp); tcp_template(tp); } return; @@ -917,8 +1033,8 @@ trimthenstep6: if (SEQ_LEQ(ti->ti_ack, tp->snd_una)) { if (ti->ti_len == 0 && tiwin == tp->snd_wnd) { - DEBUG_MISC((dfd, " dup ack m = %lx so = %lx\n", - (long )m, (long )so)); + DEBUG_MISC((dfd, " dup ack m = %p so = %p\n", + m, so)); /* * If we have outstanding data (other than * a window probe), this is a completely @@ -1273,11 +1389,11 @@ dropafterack: dropwithreset: /* reuses m if m!=NULL, m_free() unnecessary */ if (tiflags & TH_ACK) - tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST); + tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST, af); else { if (tiflags & TH_SYN) ti->ti_len++; - tcp_respond(tp, ti, m, ti->ti_seq+ti->ti_len, (tcp_seq)0, - TH_RST|TH_ACK); + tcp_respond(tp, ti, m, ti->ti_seq + ti->ti_len, (tcp_seq) 0, + TH_RST | TH_ACK, af); } return; @@ -1296,7 +1412,7 @@ tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, struct tcpiphdr *ti) int opt, optlen; DEBUG_CALL("tcp_dooptions"); - DEBUG_ARGS((dfd, " tp = %lx cnt=%i\n", (long)tp, cnt)); + DEBUG_ARGS((dfd, " tp = %p cnt=%i\n", tp, cnt)); for (; cnt > 0; cnt -= optlen, cp += optlen) { opt = cp[0]; @@ -1377,7 +1493,7 @@ tcp_xmit_timer(register struct tcpcb *tp, int rtt) register short delta; DEBUG_CALL("tcp_xmit_timer"); - DEBUG_ARG("tp = %lx", (long)tp); + DEBUG_ARG("tp = %p", tp); DEBUG_ARG("rtt = %d", rtt); if (tp->t_srtt != 0) { @@ -1465,10 +1581,22 @@ tcp_mss(struct tcpcb *tp, u_int offer) int mss; DEBUG_CALL("tcp_mss"); - DEBUG_ARG("tp = %lx", (long)tp); + DEBUG_ARG("tp = %p", tp); DEBUG_ARG("offer = %d", offer); - mss = min(IF_MTU, IF_MRU) - sizeof(struct tcpiphdr); + switch (so->so_ffamily) { + case AF_INET: + mss = min(IF_MTU, IF_MRU) - sizeof(struct tcphdr) + + sizeof(struct ip); + break; + case AF_INET6: + mss = min(IF_MTU, IF_MRU) - sizeof(struct tcphdr) + + sizeof(struct ip6); + break; + default: + g_assert_not_reached(); + } + if (offer) mss = min(mss, offer); mss = max(mss, 32); diff --git a/qemu/slirp/tcp_output.c b/qemu/slirp/tcp_output.c index 8aa3d9047..99b0a9b1c 100644 --- a/qemu/slirp/tcp_output.c +++ b/qemu/slirp/tcp_output.c @@ -38,6 +38,7 @@ * terms and conditions of the copyright. */ +#include "qemu/osdep.h" #include static const u_char tcp_outflags[TCP_NSTATES] = { @@ -60,13 +61,15 @@ tcp_output(struct tcpcb *tp) register long len, win; int off, flags, error; register struct mbuf *m; - register struct tcpiphdr *ti; + register struct tcpiphdr *ti, tcpiph_save; + struct ip *ip; + struct ip6 *ip6; u_char opt[MAX_TCPOPTLEN]; unsigned optlen, hdrlen; int idle, sendalot; DEBUG_CALL("tcp_output"); - DEBUG_ARG("tp = %lx", (long )tp); + DEBUG_ARG("tp = %p", tp); /* * Determine length of data that should be transmitted, @@ -446,16 +449,45 @@ send: * the template, but need a way to checksum without them. */ m->m_len = hdrlen + len; /* XXX Needed? m_len should be correct */ + tcpiph_save = *mtod(m, struct tcpiphdr *); + + switch (so->so_ffamily) { + case AF_INET: + m->m_data += sizeof(struct tcpiphdr) - sizeof(struct tcphdr) + - sizeof(struct ip); + m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct tcphdr) + - sizeof(struct ip); + ip = mtod(m, struct ip *); + + ip->ip_len = m->m_len; + ip->ip_dst = tcpiph_save.ti_dst; + ip->ip_src = tcpiph_save.ti_src; + ip->ip_p = tcpiph_save.ti_pr; + + ip->ip_ttl = IPDEFTTL; + ip->ip_tos = so->so_iptos; + error = ip_output(so, m); + break; + + case AF_INET6: + m->m_data += sizeof(struct tcpiphdr) - sizeof(struct tcphdr) + - sizeof(struct ip6); + m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct tcphdr) + - sizeof(struct ip6); + ip6 = mtod(m, struct ip6 *); + + ip6->ip_pl = tcpiph_save.ti_len; + ip6->ip_dst = tcpiph_save.ti_dst6; + ip6->ip_src = tcpiph_save.ti_src6; + ip6->ip_nh = tcpiph_save.ti_nh6; + + error = ip6_output(so, m, 0); + break; + + default: + g_assert_not_reached(); + } - { - - ((struct ip *)ti)->ip_len = m->m_len; - - ((struct ip *)ti)->ip_ttl = IPDEFTTL; - ((struct ip *)ti)->ip_tos = so->so_iptos; - - error = ip_output(so, m); - } if (error) { out: return (error); diff --git a/qemu/slirp/tcp_subr.c b/qemu/slirp/tcp_subr.c index 7571c5a28..6b9fef200 100644 --- a/qemu/slirp/tcp_subr.c +++ b/qemu/slirp/tcp_subr.c @@ -38,6 +38,7 @@ * terms and conditions of the copyright. */ +#include "qemu/osdep.h" #include /* patchable/settable parameters for tcp */ @@ -75,13 +76,30 @@ tcp_template(struct tcpcb *tp) register struct tcpiphdr *n = &tp->t_template; n->ti_mbuf = NULL; - n->ti_x1 = 0; - n->ti_pr = IPPROTO_TCP; - n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip)); - n->ti_src = so->so_faddr; - n->ti_dst = so->so_laddr; - n->ti_sport = so->so_fport; - n->ti_dport = so->so_lport; + memset(&n->ti, 0, sizeof(n->ti)); + n->ti_x0 = 0; + switch (so->so_ffamily) { + case AF_INET: + n->ti_pr = IPPROTO_TCP; + n->ti_len = htons(sizeof(struct tcphdr)); + n->ti_src = so->so_faddr; + n->ti_dst = so->so_laddr; + n->ti_sport = so->so_fport; + n->ti_dport = so->so_lport; + break; + + case AF_INET6: + n->ti_nh6 = IPPROTO_TCP; + n->ti_len = htons(sizeof(struct tcphdr)); + n->ti_src6 = so->so_faddr6; + n->ti_dst6 = so->so_laddr6; + n->ti_sport = so->so_fport6; + n->ti_dport = so->so_lport6; + break; + + default: + g_assert_not_reached(); + } n->ti_seq = 0; n->ti_ack = 0; @@ -108,7 +126,7 @@ tcp_template(struct tcpcb *tp) */ void tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m, - tcp_seq ack, tcp_seq seq, int flags) + tcp_seq ack, tcp_seq seq, int flags, unsigned short af) { register int tlen; int win = 0; @@ -130,6 +148,7 @@ tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m, m->m_data += IF_MAXLINKHDR; *mtod(m, struct tcpiphdr *) = *ti; ti = mtod(m, struct tcpiphdr *); + memset(&ti->ti, 0, sizeof(ti->ti)); flags = TH_ACK; } else { /* @@ -141,16 +160,26 @@ tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m, m->m_len = sizeof (struct tcpiphdr); tlen = 0; #define xchg(a,b,type) { type t; t=a; a=b; b=t; } - xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, uint32_t); - xchg(ti->ti_dport, ti->ti_sport, uint16_t); + switch (af) { + case AF_INET: + xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, uint32_t); + xchg(ti->ti_dport, ti->ti_sport, uint16_t); + break; + case AF_INET6: + xchg(ti->ti_dst6, ti->ti_src6, struct in6_addr); + xchg(ti->ti_dport, ti->ti_sport, uint16_t); + break; + default: + g_assert_not_reached(); + } #undef xchg } ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen)); tlen += sizeof (struct tcpiphdr); m->m_len = tlen; - ti->ti_mbuf = NULL; - ti->ti_x1 = 0; + ti->ti_mbuf = NULL; + ti->ti_x0 = 0; ti->ti_seq = htonl(seq); ti->ti_ack = htonl(ack); ti->ti_x2 = 0; @@ -163,14 +192,49 @@ tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m, ti->ti_urp = 0; ti->ti_sum = 0; ti->ti_sum = cksum(m, tlen); - ((struct ip *)ti)->ip_len = tlen; - - if(flags & TH_RST) - ((struct ip *)ti)->ip_ttl = MAXTTL; - else - ((struct ip *)ti)->ip_ttl = IPDEFTTL; - (void) ip_output((struct socket *)0, m); + struct tcpiphdr tcpiph_save = *(mtod(m, struct tcpiphdr *)); + struct ip *ip; + struct ip6 *ip6; + + switch (af) { + case AF_INET: + m->m_data += sizeof(struct tcpiphdr) - sizeof(struct tcphdr) + - sizeof(struct ip); + m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct tcphdr) + - sizeof(struct ip); + ip = mtod(m, struct ip *); + ip->ip_len = tlen; + ip->ip_dst = tcpiph_save.ti_dst; + ip->ip_src = tcpiph_save.ti_src; + ip->ip_p = tcpiph_save.ti_pr; + + if (flags & TH_RST) { + ip->ip_ttl = MAXTTL; + } else { + ip->ip_ttl = IPDEFTTL; + } + + ip_output(NULL, m); + break; + + case AF_INET6: + m->m_data += sizeof(struct tcpiphdr) - sizeof(struct tcphdr) + - sizeof(struct ip6); + m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct tcphdr) + - sizeof(struct ip6); + ip6 = mtod(m, struct ip6 *); + ip6->ip_pl = tlen; + ip6->ip_dst = tcpiph_save.ti_dst6; + ip6->ip_src = tcpiph_save.ti_src6; + ip6->ip_nh = tcpiph_save.ti_nh6; + + ip6_output(NULL, m, 0); + break; + + default: + g_assert_not_reached(); + } } /* @@ -189,7 +253,7 @@ tcp_newtcpcb(struct socket *so) memset((char *) tp, 0, sizeof(struct tcpcb)); tp->seg_next = tp->seg_prev = (struct tcpiphdr*)tp; - tp->t_maxseg = TCP_MSS; + tp->t_maxseg = (so->so_ffamily == AF_INET) ? TCP_MSS : TCP6_MSS; tp->t_flags = TCP_DO_RFC1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0; tp->t_socket = so; @@ -224,7 +288,7 @@ tcp_newtcpcb(struct socket *so) struct tcpcb *tcp_drop(struct tcpcb *tp, int err) { DEBUG_CALL("tcp_drop"); - DEBUG_ARG("tp = %lx", (long)tp); + DEBUG_ARG("tp = %p", tp); DEBUG_ARG("errno = %d", errno); if (TCPS_HAVERCVDSYN(tp->t_state)) { @@ -249,7 +313,7 @@ tcp_close(struct tcpcb *tp) register struct mbuf *m; DEBUG_CALL("tcp_close"); - DEBUG_ARG("tp = %lx", (long )tp); + DEBUG_ARG("tp = %p", tp); /* free the reassembly queue, if any */ t = tcpfrag_list_first(tp); @@ -290,7 +354,11 @@ tcp_sockclosed(struct tcpcb *tp) { DEBUG_CALL("tcp_sockclosed"); - DEBUG_ARG("tp = %lx", (long)tp); + DEBUG_ARG("tp = %p", tp); + + if (!tp) { + return; + } switch (tp->t_state) { @@ -310,8 +378,7 @@ tcp_sockclosed(struct tcpcb *tp) tp->t_state = TCPS_LAST_ACK; break; } - if (tp) - tcp_output(tp); + tcp_output(tp); } /* @@ -324,42 +391,29 @@ tcp_sockclosed(struct tcpcb *tp) * nonblocking. Connect returns after the SYN is sent, and does * not wait for ACK+SYN. */ -int tcp_fconnect(struct socket *so) +int tcp_fconnect(struct socket *so, unsigned short af) { - Slirp *slirp = so->slirp; int ret=0; DEBUG_CALL("tcp_fconnect"); - DEBUG_ARG("so = %lx", (long )so); + DEBUG_ARG("so = %p", so); - if( (ret = so->s = qemu_socket(AF_INET,SOCK_STREAM,0)) >= 0) { + ret = so->s = qemu_socket(af, SOCK_STREAM, 0); + if (ret >= 0) { int opt, s=so->s; - struct sockaddr_in addr; + struct sockaddr_storage addr; qemu_set_nonblock(s); socket_set_fast_reuse(s); opt = 1; qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt)); - addr.sin_family = AF_INET; - if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == - slirp->vnetwork_addr.s_addr) { - /* It's an alias */ - if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) { - if (get_dns_addr(&addr.sin_addr) < 0) - addr.sin_addr = loopback_addr; - } else { - addr.sin_addr = loopback_addr; - } - } else - addr.sin_addr = so->so_faddr; - addr.sin_port = so->so_fport; - - DEBUG_MISC((dfd, " connect()ing, addr.sin_port=%d, " - "addr.sin_addr.s_addr=%.16s\n", - ntohs(addr.sin_port), inet_ntoa(addr.sin_addr))); + addr = so->fhost.ss; + DEBUG_CALL(" connect()ing") + sotranslate_out(so, &addr); + /* We don't care what port we get */ - ret = connect(s,(struct sockaddr *)&addr,sizeof (addr)); + ret = connect(s, (struct sockaddr *)&addr, sockaddr_size(&addr)); /* * If it's not in progress, it failed, so we just return 0, @@ -387,13 +441,13 @@ void tcp_connect(struct socket *inso) { Slirp *slirp = inso->slirp; struct socket *so; - struct sockaddr_in addr; - socklen_t addrlen = sizeof(struct sockaddr_in); + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(struct sockaddr_storage); struct tcpcb *tp; int s, opt; DEBUG_CALL("tcp_connect"); - DEBUG_ARG("inso = %lx", (long)inso); + DEBUG_ARG("inso = %p", inso); /* * If it's an SS_ACCEPTONCE socket, no need to socreate() @@ -413,8 +467,8 @@ void tcp_connect(struct socket *inso) free(so); /* NOT sofree */ return; } - so->so_laddr = inso->so_laddr; - so->so_lport = inso->so_lport; + so->lhost = inso->lhost; + so->so_ffamily = inso->so_ffamily; } tcp_mss(sototcpcb(so), 0); @@ -430,14 +484,8 @@ void tcp_connect(struct socket *inso) qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); socket_set_nodelay(s); - so->so_fport = addr.sin_port; - so->so_faddr = addr.sin_addr; - /* Translate connections from localhost to the real hostname */ - if (so->so_faddr.s_addr == 0 || - (so->so_faddr.s_addr & loopback_mask) == - (loopback_addr.s_addr & loopback_mask)) { - so->so_faddr = slirp->vhost_addr; - } + so->fhost.ss = addr; + sotranslate_accept(so); /* Close the accept() socket, set right state */ if (inso->so_state & SS_FACCEPTONCE) { @@ -564,8 +612,8 @@ tcp_emu(struct socket *so, struct mbuf *m) char *bptr; DEBUG_CALL("tcp_emu"); - DEBUG_ARG("so = %lx", (long)so); - DEBUG_ARG("m = %lx", (long)m); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("m = %p", m); switch(so->so_emu) { int x, i; @@ -900,7 +948,7 @@ int tcp_ctl(struct socket *so) int do_pty; DEBUG_CALL("tcp_ctl"); - DEBUG_ARG("so = %lx", (long )so); + DEBUG_ARG("so = %p", so); if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr) { /* Check if it's pty_exec */ diff --git a/qemu/slirp/tcp_timer.c b/qemu/slirp/tcp_timer.c index 6c5bb11cc..8f5dd772a 100644 --- a/qemu/slirp/tcp_timer.c +++ b/qemu/slirp/tcp_timer.c @@ -30,6 +30,7 @@ * tcp_timer.c,v 1.2 1994/08/02 07:49:10 davidg Exp */ +#include "qemu/osdep.h" #include static struct tcpcb *tcp_timers(register struct tcpcb *tp, int timer); @@ -277,7 +278,8 @@ tcp_timers(register struct tcpcb *tp, int timer) * correspondent TCP to respond. */ tcp_respond(tp, &tp->t_template, (struct mbuf *)NULL, - tp->rcv_nxt, tp->snd_una - 1, 0); + tp->rcv_nxt, tp->snd_una - 1, 0, + tp->t_socket->so_ffamily); tp->t_timer[TCPT_KEEP] = TCPTV_KEEPINTVL; } else tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE; diff --git a/qemu/slirp/tcpip.h b/qemu/slirp/tcpip.h index 7974ce3d5..124b4a9f6 100644 --- a/qemu/slirp/tcpip.h +++ b/qemu/slirp/tcpip.h @@ -37,15 +37,32 @@ * Tcp+ip header, after ip options removed. */ struct tcpiphdr { - struct ipovly ti_i; /* overlaid ip structure */ - struct tcphdr ti_t; /* tcp header */ + struct mbuf_ptr ih_mbuf; /* backpointer to mbuf */ + union { + struct { + struct in_addr ih_src; /* source internet address */ + struct in_addr ih_dst; /* destination internet address */ + uint8_t ih_x1; /* (unused) */ + uint8_t ih_pr; /* protocol */ + } ti_i4; + struct { + struct in6_addr ih_src; + struct in6_addr ih_dst; + uint8_t ih_x1; + uint8_t ih_nh; + } ti_i6; + } ti; + uint16_t ti_x0; + uint16_t ti_len; /* protocol length */ + struct tcphdr ti_t; /* tcp header */ }; -#define ti_mbuf ti_i.ih_mbuf.mptr -#define ti_x1 ti_i.ih_x1 -#define ti_pr ti_i.ih_pr -#define ti_len ti_i.ih_len -#define ti_src ti_i.ih_src -#define ti_dst ti_i.ih_dst +#define ti_mbuf ih_mbuf.mptr +#define ti_pr ti.ti_i4.ih_pr +#define ti_src ti.ti_i4.ih_src +#define ti_dst ti.ti_i4.ih_dst +#define ti_src6 ti.ti_i6.ih_src +#define ti_dst6 ti.ti_i6.ih_dst +#define ti_nh6 ti.ti_i6.ih_nh #define ti_sport ti_t.th_sport #define ti_dport ti_t.th_dport #define ti_seq ti_t.th_seq @@ -65,6 +82,13 @@ struct tcpiphdr { #define tcpfrag_list_end(F, T) (tcpiphdr2qlink(F) == (struct qlink*)(T)) #define tcpfrag_list_empty(T) ((T)->seg_next == (struct tcpiphdr*)(T)) +/* This is the difference between the size of a tcpiphdr structure, and the + * size of actual ip+tcp headers, rounded up since we need to align data. */ +#define TCPIPHDR_DELTA\ + (max(0,\ + (sizeof(struct tcpiphdr)\ + - sizeof(struct ip) - sizeof(struct tcphdr) + 3) & ~3)) + /* * Just a clean way to get to the first byte * of the packet diff --git a/qemu/slirp/tftp.c b/qemu/slirp/tftp.c index a329fb281..12b5ff6e2 100644 --- a/qemu/slirp/tftp.c +++ b/qemu/slirp/tftp.c @@ -22,8 +22,10 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include "qemu-common.h" +#include "qemu/cutils.h" static inline int tftp_session_in_use(struct tftp_session *spt) { @@ -45,7 +47,8 @@ static void tftp_session_terminate(struct tftp_session *spt) spt->slirp = NULL; } -static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp) +static int tftp_session_allocate(Slirp *slirp, struct sockaddr_storage *srcsas, + struct tftp_t *tp) { struct tftp_session *spt; int k; @@ -67,7 +70,7 @@ static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp) found: memset(spt, 0, sizeof(*spt)); - memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip)); + spt->client_addr = *srcsas; spt->fd = -1; spt->client_port = tp->udp.uh_sport; spt->slirp = slirp; @@ -77,7 +80,8 @@ static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp) return k; } -static int tftp_session_find(Slirp *slirp, struct tftp_t *tp) +static int tftp_session_find(Slirp *slirp, struct sockaddr_storage *srcsas, + struct tftp_t *tp) { struct tftp_session *spt; int k; @@ -86,7 +90,7 @@ static int tftp_session_find(Slirp *slirp, struct tftp_t *tp) spt = &slirp->tftp_sessions[k]; if (tftp_session_in_use(spt)) { - if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip))) { + if (sockaddr_equal(&spt->client_addr, srcsas)) { if (spt->client_port == tp->udp.uh_sport) { return k; } @@ -119,11 +123,53 @@ static int tftp_read_data(struct tftp_session *spt, uint32_t block_nr, return bytes_read; } +static struct tftp_t *tftp_prep_mbuf_data(struct tftp_session *spt, + struct mbuf *m) +{ + struct tftp_t *tp; + + memset(m->m_data, 0, m->m_size); + + m->m_data += IF_MAXLINKHDR; + if (spt->client_addr.ss_family == AF_INET6) { + m->m_data += sizeof(struct ip6); + } else { + m->m_data += sizeof(struct ip); + } + tp = (void *)m->m_data; + m->m_data += sizeof(struct udphdr); + + return tp; +} + +static void tftp_udp_output(struct tftp_session *spt, struct mbuf *m, + struct tftp_t *recv_tp) +{ + if (spt->client_addr.ss_family == AF_INET6) { + struct sockaddr_in6 sa6, da6; + + sa6.sin6_addr = spt->slirp->vhost_addr6; + sa6.sin6_port = recv_tp->udp.uh_dport; + da6.sin6_addr = ((struct sockaddr_in6 *)&spt->client_addr)->sin6_addr; + da6.sin6_port = spt->client_port; + + udp6_output(NULL, m, &sa6, &da6); + } else { + struct sockaddr_in sa4, da4; + + sa4.sin_addr = spt->slirp->vhost_addr; + sa4.sin_port = recv_tp->udp.uh_dport; + da4.sin_addr = ((struct sockaddr_in *)&spt->client_addr)->sin_addr; + da4.sin_port = spt->client_port; + + udp_output(NULL, m, &sa4, &da4, IPTOS_LOWDELAY); + } +} + static int tftp_send_oack(struct tftp_session *spt, const char *keys[], uint32_t values[], int nb, struct tftp_t *recv_tp) { - struct sockaddr_in saddr, daddr; struct mbuf *m; struct tftp_t *tp; int i, n = 0; @@ -131,13 +177,9 @@ static int tftp_send_oack(struct tftp_session *spt, m = m_get(spt->slirp); if (!m) - return -1; - - memset(m->m_data, 0, m->m_size); + return -1; - m->m_data += IF_MAXLINKHDR; - tp = (void *)m->m_data; - m->m_data += sizeof(struct udpiphdr); + tp = tftp_prep_mbuf_data(spt, m); tp->tp_op = htons(TFTP_OACK); for (i = 0; i < nb; i++) { @@ -147,15 +189,8 @@ static int tftp_send_oack(struct tftp_session *spt, values[i]) + 1; } - saddr.sin_addr = recv_tp->ip.ip_dst; - saddr.sin_port = recv_tp->udp.uh_dport; - - daddr.sin_addr = spt->client_ip; - daddr.sin_port = spt->client_port; - - m->m_len = sizeof(struct tftp_t) - 514 + n - - sizeof(struct ip) - sizeof(struct udphdr); - udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); + m->m_len = sizeof(struct tftp_t) - 514 + n - sizeof(struct udphdr); + tftp_udp_output(spt, m, recv_tp); return 0; } @@ -164,7 +199,6 @@ static void tftp_send_error(struct tftp_session *spt, uint16_t errorcode, const char *msg, struct tftp_t *recv_tp) { - struct sockaddr_in saddr, daddr; struct mbuf *m; struct tftp_t *tp; @@ -176,24 +210,15 @@ static void tftp_send_error(struct tftp_session *spt, memset(m->m_data, 0, m->m_size); - m->m_data += IF_MAXLINKHDR; - tp = (void *)m->m_data; - m->m_data += sizeof(struct udpiphdr); + tp = tftp_prep_mbuf_data(spt, m); tp->tp_op = htons(TFTP_ERROR); tp->x.tp_error.tp_error_code = htons(errorcode); pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg), msg); - saddr.sin_addr = recv_tp->ip.ip_dst; - saddr.sin_port = recv_tp->udp.uh_dport; - - daddr.sin_addr = spt->client_ip; - daddr.sin_port = spt->client_port; - - m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) - - sizeof(struct ip) - sizeof(struct udphdr); - - udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); + m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) + - sizeof(struct udphdr); + tftp_udp_output(spt, m, recv_tp); out: tftp_session_terminate(spt); @@ -202,7 +227,6 @@ out: static void tftp_send_next_block(struct tftp_session *spt, struct tftp_t *recv_tp) { - struct sockaddr_in saddr, daddr; struct mbuf *m; struct tftp_t *tp; int nobytes; @@ -215,19 +239,11 @@ static void tftp_send_next_block(struct tftp_session *spt, memset(m->m_data, 0, m->m_size); - m->m_data += IF_MAXLINKHDR; - tp = (void *)m->m_data; - m->m_data += sizeof(struct udpiphdr); + tp = tftp_prep_mbuf_data(spt, m); tp->tp_op = htons(TFTP_DATA); tp->x.tp_data.tp_block_nr = htons((spt->block_nr + 1) & 0xffff); - saddr.sin_addr = recv_tp->ip.ip_dst; - saddr.sin_port = recv_tp->udp.uh_dport; - - daddr.sin_addr = spt->client_ip; - daddr.sin_port = spt->client_port; - nobytes = tftp_read_data(spt, spt->block_nr, tp->x.tp_data.tp_buf, 512); if (nobytes < 0) { @@ -240,10 +256,8 @@ static void tftp_send_next_block(struct tftp_session *spt, return; } - m->m_len = sizeof(struct tftp_t) - (512 - nobytes) - - sizeof(struct ip) - sizeof(struct udphdr); - - udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); + m->m_len = sizeof(struct tftp_t) - (512 - nobytes) - sizeof(struct udphdr); + tftp_udp_output(spt, m, recv_tp); if (nobytes == 512) { tftp_session_update(spt); @@ -255,7 +269,8 @@ static void tftp_send_next_block(struct tftp_session *spt, spt->block_nr++; } -static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen) +static void tftp_handle_rrq(Slirp *slirp, struct sockaddr_storage *srcsas, + struct tftp_t *tp, int pktlen) { struct tftp_session *spt; int s, k; @@ -266,12 +281,12 @@ static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen) int nb_options = 0; /* check if a session already exists and if so terminate it */ - s = tftp_session_find(slirp, tp); + s = tftp_session_find(slirp, srcsas, tp); if (s >= 0) { tftp_session_terminate(&slirp->tftp_sessions[s]); } - s = tftp_session_allocate(slirp, tp); + s = tftp_session_allocate(slirp, srcsas, tp); if (s < 0) { return; @@ -396,11 +411,12 @@ static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen) tftp_send_next_block(spt, tp); } -static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen) +static void tftp_handle_ack(Slirp *slirp, struct sockaddr_storage *srcsas, + struct tftp_t *tp, int pktlen) { int s; - s = tftp_session_find(slirp, tp); + s = tftp_session_find(slirp, srcsas, tp); if (s < 0) { return; @@ -409,11 +425,12 @@ static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen) tftp_send_next_block(&slirp->tftp_sessions[s], tp); } -static void tftp_handle_error(Slirp *slirp, struct tftp_t *tp, int pktlen) +static void tftp_handle_error(Slirp *slirp, struct sockaddr_storage *srcsas, + struct tftp_t *tp, int pktlen) { int s; - s = tftp_session_find(slirp, tp); + s = tftp_session_find(slirp, srcsas, tp); if (s < 0) { return; @@ -422,21 +439,21 @@ static void tftp_handle_error(Slirp *slirp, struct tftp_t *tp, int pktlen) tftp_session_terminate(&slirp->tftp_sessions[s]); } -void tftp_input(struct mbuf *m) +void tftp_input(struct sockaddr_storage *srcsas, struct mbuf *m) { struct tftp_t *tp = (struct tftp_t *)m->m_data; switch(ntohs(tp->tp_op)) { case TFTP_RRQ: - tftp_handle_rrq(m->slirp, tp, m->m_len); + tftp_handle_rrq(m->slirp, srcsas, tp, m->m_len); break; case TFTP_ACK: - tftp_handle_ack(m->slirp, tp, m->m_len); + tftp_handle_ack(m->slirp, srcsas, tp, m->m_len); break; case TFTP_ERROR: - tftp_handle_error(m->slirp, tp, m->m_len); + tftp_handle_error(m->slirp, srcsas, tp, m->m_len); break; } } diff --git a/qemu/slirp/tftp.h b/qemu/slirp/tftp.h index e1cc24b9b..1cb1adf59 100644 --- a/qemu/slirp/tftp.h +++ b/qemu/slirp/tftp.h @@ -16,7 +16,6 @@ #define TFTP_FILENAME_MAX 512 struct tftp_t { - struct ip ip; struct udphdr udp; uint16_t tp_op; union { @@ -30,20 +29,20 @@ struct tftp_t { } tp_error; char tp_buf[512 + 2]; } x; -}; +} __attribute__((packed)); struct tftp_session { Slirp *slirp; char *filename; int fd; - struct in_addr client_ip; + struct sockaddr_storage client_addr; uint16_t client_port; uint32_t block_nr; int timestamp; }; -void tftp_input(struct mbuf *m); +void tftp_input(struct sockaddr_storage *srcsas, struct mbuf *m); #endif diff --git a/qemu/slirp/udp.c b/qemu/slirp/udp.c index f77e00f5a..247024fd8 100644 --- a/qemu/slirp/udp.c +++ b/qemu/slirp/udp.c @@ -38,6 +38,7 @@ * terms and conditions of the copyright. */ +#include "qemu/osdep.h" #include #include "ip_icmp.h" @@ -70,9 +71,11 @@ udp_input(register struct mbuf *m, int iphlen) int len; struct ip save_ip; struct socket *so; + struct sockaddr_storage lhost; + struct sockaddr_in *lhost4; DEBUG_CALL("udp_input"); - DEBUG_ARG("m = %lx", (long)m); + DEBUG_ARG("m = %p", m); DEBUG_ARG("iphlen = %d", iphlen); /* @@ -125,6 +128,11 @@ udp_input(register struct mbuf *m, int iphlen) } } + lhost.ss_family = AF_INET; + lhost4 = (struct sockaddr_in *) &lhost; + lhost4->sin_addr = ip->ip_src; + lhost4->sin_port = uh->uh_sport; + /* * handle DHCP/BOOTP */ @@ -140,7 +148,11 @@ udp_input(register struct mbuf *m, int iphlen) */ if (ntohs(uh->uh_dport) == TFTP_SERVER && ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) { - tftp_input(m); + m->m_data += iphlen; + m->m_len -= iphlen; + tftp_input(&lhost, m); + m->m_data -= iphlen; + m->m_len += iphlen; goto bad; } @@ -151,25 +163,7 @@ udp_input(register struct mbuf *m, int iphlen) /* * Locate pcb for datagram. */ - so = slirp->udp_last_so; - if (so == &slirp->udb || so->so_lport != uh->uh_sport || - so->so_laddr.s_addr != ip->ip_src.s_addr) { - struct socket *tmp; - - for (tmp = slirp->udb.so_next; tmp != &slirp->udb; - tmp = tmp->so_next) { - if (tmp->so_lport == uh->uh_sport && - tmp->so_laddr.s_addr == ip->ip_src.s_addr) { - so = tmp; - break; - } - } - if (tmp == &slirp->udb) { - so = NULL; - } else { - slirp->udp_last_so = so; - } - } + so = solookup(&slirp->udp_last_so, &slirp->udb, &lhost, NULL); if (so == NULL) { /* @@ -180,7 +174,7 @@ udp_input(register struct mbuf *m, int iphlen) if (!so) { goto bad; } - if(udp_attach(so) == -1) { + if (udp_attach(so, AF_INET) == -1) { DEBUG_MISC((dfd," udp_attach errno = %d-%s\n", errno,strerror(errno))); sofree(so); @@ -190,6 +184,7 @@ udp_input(register struct mbuf *m, int iphlen) /* * Setup fields */ + so->so_lfamily = AF_INET; so->so_laddr = ip->ip_src; so->so_lport = uh->uh_sport; @@ -202,6 +197,7 @@ udp_input(register struct mbuf *m, int iphlen) */ } + so->so_ffamily = AF_INET; so->so_faddr = ip->ip_dst; /* XXX */ so->so_fport = uh->uh_dport; /* XXX */ @@ -217,7 +213,9 @@ udp_input(register struct mbuf *m, int iphlen) m->m_data -= iphlen; *ip=save_ip; DEBUG_MISC((dfd,"udp tx errno = %d-%s\n",errno,strerror(errno))); - icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno)); + icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, + strerror(errno)); + goto bad; } m_free(so->so_m); /* used for ICMP if error on sorecvfrom */ @@ -233,7 +231,7 @@ bad: m_free(m); } -int udp_output2(struct socket *so, struct mbuf *m, +int udp_output(struct socket *so, struct mbuf *m, struct sockaddr_in *saddr, struct sockaddr_in *daddr, int iptos) { @@ -241,8 +239,8 @@ int udp_output2(struct socket *so, struct mbuf *m, int error = 0; DEBUG_CALL("udp_output"); - DEBUG_ARG("so = %lx", (long)so); - DEBUG_ARG("m = %lx", (long)m); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("m = %p", m); DEBUG_ARG("saddr = %lx", (long)saddr->sin_addr.s_addr); DEBUG_ARG("daddr = %lx", (long)daddr->sin_addr.s_addr); @@ -284,35 +282,11 @@ int udp_output2(struct socket *so, struct mbuf *m, return (error); } -int udp_output(struct socket *so, struct mbuf *m, - struct sockaddr_in *addr) - -{ - Slirp *slirp = so->slirp; - struct sockaddr_in saddr, daddr; - - saddr = *addr; - if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == - slirp->vnetwork_addr.s_addr) { - uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr; - - if ((so->so_faddr.s_addr & inv_mask) == inv_mask) { - saddr.sin_addr = slirp->vhost_addr; - } else if (addr->sin_addr.s_addr == loopback_addr.s_addr || - so->so_faddr.s_addr != slirp->vhost_addr.s_addr) { - saddr.sin_addr = so->so_faddr; - } - } - daddr.sin_addr = so->so_laddr; - daddr.sin_port = so->so_lport; - - return udp_output2(so, m, &saddr, &daddr, so->so_iptos); -} - int -udp_attach(struct socket *so) +udp_attach(struct socket *so, unsigned short af) { - if((so->s = qemu_socket(AF_INET,SOCK_DGRAM,0)) != -1) { + so->s = qemu_socket(af, SOCK_DGRAM, 0); + if (so->s != -1) { so->so_expire = curtime + SO_EXPIRE; insque(so, &so->slirp->udb); } @@ -375,13 +349,9 @@ udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, socket_set_fast_reuse(so->s); getsockname(so->s,(struct sockaddr *)&addr,&addrlen); - so->so_fport = addr.sin_port; - if (addr.sin_addr.s_addr == 0 || - addr.sin_addr.s_addr == loopback_addr.s_addr) { - so->so_faddr = slirp->vhost_addr; - } else { - so->so_faddr = addr.sin_addr; - } + so->fhost.sin = addr; + sotranslate_accept(so); + so->so_lfamily = AF_INET; so->so_lport = lport; so->so_laddr.s_addr = laddr; if (flags != SS_FACCEPTONCE) diff --git a/qemu/slirp/udp.h b/qemu/slirp/udp.h index 9bf31fe7b..10cc7809b 100644 --- a/qemu/slirp/udp.h +++ b/qemu/slirp/udp.h @@ -76,12 +76,16 @@ struct mbuf; void udp_init(Slirp *); void udp_cleanup(Slirp *); void udp_input(register struct mbuf *, int); -int udp_output(struct socket *, struct mbuf *, struct sockaddr_in *); -int udp_attach(struct socket *); +int udp_attach(struct socket *, unsigned short af); void udp_detach(struct socket *); struct socket * udp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int, int); -int udp_output2(struct socket *so, struct mbuf *m, +int udp_output(struct socket *so, struct mbuf *m, struct sockaddr_in *saddr, struct sockaddr_in *daddr, int iptos); + +void udp6_input(register struct mbuf *); +int udp6_output(struct socket *so, struct mbuf *m, + struct sockaddr_in6 *saddr, struct sockaddr_in6 *daddr); + #endif diff --git a/qemu/slirp/udp6.c b/qemu/slirp/udp6.c new file mode 100644 index 000000000..a23026f2e --- /dev/null +++ b/qemu/slirp/udp6.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2013 + * Guillaume Subiron + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "slirp.h" +#include "qemu/osdep.h" +#include "udp.h" + +void udp6_input(struct mbuf *m) +{ + Slirp *slirp = m->slirp; + struct ip6 *ip, save_ip; + struct udphdr *uh; + int iphlen = sizeof(struct ip6); + int len; + struct socket *so; + struct sockaddr_in6 lhost; + + DEBUG_CALL("udp6_input"); + DEBUG_ARG("m = %lx", (long)m); + + if (slirp->restricted) { + goto bad; + } + + ip = mtod(m, struct ip6 *); + m->m_len -= iphlen; + m->m_data += iphlen; + uh = mtod(m, struct udphdr *); + m->m_len += iphlen; + m->m_data -= iphlen; + + if (ip6_cksum(m)) { + goto bad; + } + + len = ntohs((uint16_t)uh->uh_ulen); + + /* + * Make mbuf data length reflect UDP length. + * If not enough data to reflect UDP length, drop. + */ + if (ntohs(ip->ip_pl) != len) { + if (len > ntohs(ip->ip_pl)) { + goto bad; + } + m_adj(m, len - ntohs(ip->ip_pl)); + ip->ip_pl = htons(len); + } + + /* + * Save a copy of the IP header in case we want restore it + * for sending an ICMP error message in response. + */ + save_ip = *ip; + + /* Locate pcb for datagram. */ + lhost.sin6_family = AF_INET6; + lhost.sin6_addr = ip->ip_src; + lhost.sin6_port = uh->uh_sport; + + /* TODO handle DHCP/BOOTP */ + + /* handle TFTP */ + if (ntohs(uh->uh_dport) == TFTP_SERVER && + !memcmp(ip->ip_dst.s6_addr, slirp->vhost_addr6.s6_addr, 16)) { + m->m_data += iphlen; + m->m_len -= iphlen; + tftp_input((struct sockaddr_storage *)&lhost, m); + m->m_data -= iphlen; + m->m_len += iphlen; + goto bad; + } + + so = solookup(&slirp->udp_last_so, &slirp->udb, + (struct sockaddr_storage *) &lhost, NULL); + + if (so == NULL) { + /* If there's no socket for this packet, create one. */ + so = socreate(slirp); + if (!so) { + goto bad; + } + if (udp_attach(so, AF_INET6) == -1) { + DEBUG_MISC((dfd, " udp6_attach errno = %d-%s\n", + errno, strerror(errno))); + sofree(so); + goto bad; + } + + /* Setup fields */ + so->so_lfamily = AF_INET6; + so->so_laddr6 = ip->ip_src; + so->so_lport6 = uh->uh_sport; + } + + so->so_ffamily = AF_INET6; + so->so_faddr6 = ip->ip_dst; /* XXX */ + so->so_fport6 = uh->uh_dport; /* XXX */ + + iphlen += sizeof(struct udphdr); + m->m_len -= iphlen; + m->m_data += iphlen; + + /* + * Now we sendto() the packet. + */ + if (sosendto(so, m) == -1) { + m->m_len += iphlen; + m->m_data -= iphlen; + *ip = save_ip; + DEBUG_MISC((dfd, "udp tx errno = %d-%s\n", errno, strerror(errno))); + icmp6_send_error(m, ICMP6_UNREACH, ICMP6_UNREACH_NO_ROUTE); + goto bad; + } + + m_free(so->so_m); /* used for ICMP if error on sorecvfrom */ + + /* restore the orig mbuf packet */ + m->m_len += iphlen; + m->m_data -= iphlen; + *ip = save_ip; + so->so_m = m; + + return; +bad: + m_free(m); +} + +int udp6_output(struct socket *so, struct mbuf *m, + struct sockaddr_in6 *saddr, struct sockaddr_in6 *daddr) +{ + struct ip6 *ip; + struct udphdr *uh; + + DEBUG_CALL("udp6_output"); + DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("m = %lx", (long)m); + + /* adjust for header */ + m->m_data -= sizeof(struct udphdr); + m->m_len += sizeof(struct udphdr); + uh = mtod(m, struct udphdr *); + m->m_data -= sizeof(struct ip6); + m->m_len += sizeof(struct ip6); + ip = mtod(m, struct ip6 *); + + /* Build IP header */ + ip->ip_pl = htons(m->m_len - sizeof(struct ip6)); + ip->ip_nh = IPPROTO_UDP; + ip->ip_src = saddr->sin6_addr; + ip->ip_dst = daddr->sin6_addr; + + /* Build UDP header */ + uh->uh_sport = saddr->sin6_port; + uh->uh_dport = daddr->sin6_port; + uh->uh_ulen = ip->ip_pl; + uh->uh_sum = 0; + uh->uh_sum = ip6_cksum(m); + if (uh->uh_sum == 0) { + uh->uh_sum = 0xffff; + } + + return ip6_output(so, m, 0); +} diff --git a/qemu/softmmu_template.h b/qemu/softmmu_template.h index d42d89d54..208f808f3 100644 --- a/qemu/softmmu_template.h +++ b/qemu/softmmu_template.h @@ -150,11 +150,11 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env, uint64_t val; CPUState *cpu = ENV_GET_CPU(env); hwaddr physaddr = iotlbentry->addr; - MemoryRegion *mr = iotlb_to_region(cpu, physaddr); + MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs); physaddr = (physaddr & TARGET_PAGE_MASK) + addr; cpu->mem_io_pc = retaddr; - if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu_can_do_io(cpu)) { + if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) { cpu_io_recompile(cpu, retaddr); } @@ -165,9 +165,6 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env, } #endif -#ifdef SOFTMMU_CODE_ACCESS -static __attribute__((unused)) -#endif WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi, uintptr_t retaddr) { @@ -252,9 +249,6 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, } #if DATA_SIZE > 1 -#ifdef SOFTMMU_CODE_ACCESS -static __attribute__((unused)) -#endif WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi, uintptr_t retaddr) { @@ -335,14 +329,6 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, } #endif /* DATA_SIZE > 1 */ -DATA_TYPE -glue(glue(helper_ld, SUFFIX), MMUSUFFIX)(CPUArchState *env, target_ulong addr, - int mmu_idx) -{ - TCGMemOpIdx oi = make_memop_idx(SHIFT, mmu_idx); - return helper_te_ld_name (env, addr, oi, GETRA()); -} - #ifndef SOFTMMU_CODE_ACCESS /* Provide signed versions of the load routines as well. We can of course @@ -371,10 +357,10 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env, { CPUState *cpu = ENV_GET_CPU(env); hwaddr physaddr = iotlbentry->addr; - MemoryRegion *mr = iotlb_to_region(cpu, physaddr); + MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs); physaddr = (physaddr & TARGET_PAGE_MASK) + addr; - if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu_can_do_io(cpu)) { + if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) { cpu_io_recompile(cpu, retaddr); } @@ -540,14 +526,6 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, } #endif /* DATA_SIZE > 1 */ -void -glue(glue(helper_st, SUFFIX), MMUSUFFIX)(CPUArchState *env, target_ulong addr, - DATA_TYPE val, int mmu_idx) -{ - TCGMemOpIdx oi = make_memop_idx(SHIFT, mmu_idx); - helper_te_st_name(env, addr, val, oi, GETRA()); -} - #if DATA_SIZE == 1 /* Probe for whether the specified guest write access is permitted. * If it is not permitted then an exception will be taken in the same diff --git a/qemu/spice-qemu-char.c b/qemu/spice-qemu-char.c index d41bb745a..351fcaa03 100644 --- a/qemu/spice-qemu-char.c +++ b/qemu/spice-qemu-char.c @@ -1,11 +1,10 @@ -#include "config-host.h" +#include "qemu/osdep.h" #include "trace.h" #include "ui/qemu-spice.h" #include "sysemu/char.h" #include #include -#include "qemu/osdep.h" typedef struct SpiceCharDriver { CharDriverState* chr; @@ -271,13 +270,18 @@ static void spice_chr_accept_input(struct CharDriverState *chr) } static CharDriverState *chr_open(const char *subtype, - void (*set_fe_open)(struct CharDriverState *, int)) - + void (*set_fe_open)(struct CharDriverState *, + int), + ChardevCommon *backend, + Error **errp) { CharDriverState *chr; SpiceCharDriver *s; - chr = qemu_chr_alloc(); + chr = qemu_chr_alloc(backend, errp); + if (!chr) { + return NULL; + } s = g_malloc0(sizeof(SpiceCharDriver)); s->chr = chr; s->active = false; @@ -296,15 +300,16 @@ static CharDriverState *chr_open(const char *subtype, return chr; } -CharDriverState *qemu_chr_open_spice_vmc(const char *type) +static CharDriverState *qemu_chr_open_spice_vmc(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, + Error **errp) { + ChardevSpiceChannel *spicevmc = backend->u.spicevmc.data; + const char *type = spicevmc->type; const char **psubtype = spice_server_char_device_recognized_subtypes(); + ChardevCommon *common = qapi_ChardevSpiceChannel_base(spicevmc); - if (type == NULL) { - fprintf(stderr, "spice-qemu-char: missing name parameter\n"); - print_allowed_subtypes(); - return NULL; - } for (; *psubtype != NULL; ++psubtype) { if (strcmp(type, *psubtype) == 0) { break; @@ -316,12 +321,18 @@ CharDriverState *qemu_chr_open_spice_vmc(const char *type) return NULL; } - return chr_open(type, spice_vmc_set_fe_open); + return chr_open(type, spice_vmc_set_fe_open, common, errp); } #if SPICE_SERVER_VERSION >= 0x000c02 -CharDriverState *qemu_chr_open_spice_port(const char *name) +static CharDriverState *qemu_chr_open_spice_port(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, + Error **errp) { + ChardevSpicePort *spiceport = backend->u.spiceport.data; + const char *name = spiceport->fqdn; + ChardevCommon *common = qapi_ChardevSpicePort_base(spiceport); CharDriverState *chr; SpiceCharDriver *s; @@ -330,7 +341,10 @@ CharDriverState *qemu_chr_open_spice_port(const char *name) return NULL; } - chr = chr_open("port", spice_port_set_fe_open); + chr = chr_open("port", spice_port_set_fe_open, common, errp); + if (!chr) { + return NULL; + } s = chr->opaque; s->sin.portname = g_strdup(name); @@ -354,34 +368,38 @@ static void qemu_chr_parse_spice_vmc(QemuOpts *opts, ChardevBackend *backend, Error **errp) { const char *name = qemu_opt_get(opts, "name"); + ChardevSpiceChannel *spicevmc; if (name == NULL) { error_setg(errp, "chardev: spice channel: no name given"); return; } - backend->spicevmc = g_new0(ChardevSpiceChannel, 1); - backend->spicevmc->type = g_strdup(name); + spicevmc = backend->u.spicevmc.data = g_new0(ChardevSpiceChannel, 1); + qemu_chr_parse_common(opts, qapi_ChardevSpiceChannel_base(spicevmc)); + spicevmc->type = g_strdup(name); } static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend, Error **errp) { const char *name = qemu_opt_get(opts, "name"); + ChardevSpicePort *spiceport; if (name == NULL) { error_setg(errp, "chardev: spice port: no name given"); return; } - backend->spiceport = g_new0(ChardevSpicePort, 1); - backend->spiceport->fqdn = g_strdup(name); + spiceport = backend->u.spiceport.data = g_new0(ChardevSpicePort, 1); + qemu_chr_parse_common(opts, qapi_ChardevSpicePort_base(spiceport)); + spiceport->fqdn = g_strdup(name); } static void register_types(void) { register_char_driver("spicevmc", CHARDEV_BACKEND_KIND_SPICEVMC, - qemu_chr_parse_spice_vmc); + qemu_chr_parse_spice_vmc, qemu_chr_open_spice_vmc); register_char_driver("spiceport", CHARDEV_BACKEND_KIND_SPICEPORT, - qemu_chr_parse_spice_port); + qemu_chr_parse_spice_port, qemu_chr_open_spice_port); } type_init(register_types); diff --git a/qemu/stubs/Makefile.objs b/qemu/stubs/Makefile.objs index 9937a1295..4b258a673 100644 --- a/qemu/stubs/Makefile.objs +++ b/qemu/stubs/Makefile.objs @@ -1,8 +1,7 @@ stub-obj-y += arch-query-cpu-def.o -stub-obj-y += bdrv-commit-all.o -stub-obj-y += chr-baum-init.o -stub-obj-y += chr-msmouse.o -stub-obj-y += chr-testdev.o +stub-obj-y += bdrv-next-monitor-owned.o +stub-obj-y += blk-commit-all.o +stub-obj-y += blockdev-close-all-bdrv-states.o stub-obj-y += clock-warp.o stub-obj-y += cpu-get-clock.o stub-obj-y += cpu-get-icount.o @@ -23,18 +22,22 @@ stub-obj-y += mon-is-qmp.o stub-obj-y += mon-printf.o stub-obj-y += monitor-init.o stub-obj-y += notify-event.o -stub-obj-$(CONFIG_SPICE) += qemu-chr-open-spice.o stub-obj-y += qtest.o +stub-obj-y += replay.o +stub-obj-y += replay-user.o stub-obj-y += reset.o stub-obj-y += runstate-check.o stub-obj-y += set-fd-handler.o stub-obj-y += slirp.o stub-obj-y += sysbus.o stub-obj-y += uuid.o -stub-obj-y += vc-init.o stub-obj-y += vm-stop.o stub-obj-y += vmstate.o stub-obj-$(CONFIG_WIN32) += fd-register.o stub-obj-y += cpus.o stub-obj-y += kvm.o stub-obj-y += qmp_pc_dimm_device_list.o +stub-obj-y += target-monitor-defs.o +stub-obj-y += target-get-monitor-def.o +stub-obj-y += vhost.o +stub-obj-y += iohandler.o diff --git a/qemu/stubs/arch-query-cpu-def.c b/qemu/stubs/arch-query-cpu-def.c index a975ab453..cefe4beb8 100644 --- a/qemu/stubs/arch-query-cpu-def.c +++ b/qemu/stubs/arch-query-cpu-def.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "sysemu/arch_init.h" #include "qapi/qmp/qerror.h" diff --git a/qemu/stubs/bdrv-commit-all.c b/qemu/stubs/bdrv-commit-all.c deleted file mode 100644 index a8e0a9541..000000000 --- a/qemu/stubs/bdrv-commit-all.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "qemu-common.h" -#include "block/block.h" - -int bdrv_commit_all(void) -{ - return 0; -} diff --git a/qemu/stubs/bdrv-next-monitor-owned.c b/qemu/stubs/bdrv-next-monitor-owned.c new file mode 100644 index 000000000..2acf6c3ec --- /dev/null +++ b/qemu/stubs/bdrv-next-monitor-owned.c @@ -0,0 +1,8 @@ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "block/block.h" + +BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs) +{ + return NULL; +} diff --git a/qemu/stubs/blk-commit-all.c b/qemu/stubs/blk-commit-all.c new file mode 100644 index 000000000..c82fb7f5f --- /dev/null +++ b/qemu/stubs/blk-commit-all.c @@ -0,0 +1,8 @@ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "sysemu/block-backend.h" + +int blk_commit_all(void) +{ + return 0; +} diff --git a/qemu/stubs/blockdev-close-all-bdrv-states.c b/qemu/stubs/blockdev-close-all-bdrv-states.c new file mode 100644 index 000000000..f1f1d9cdc --- /dev/null +++ b/qemu/stubs/blockdev-close-all-bdrv-states.c @@ -0,0 +1,6 @@ +#include "qemu/osdep.h" +#include "block/block_int.h" + +void blockdev_close_all_bdrv_states(void) +{ +} diff --git a/qemu/stubs/chr-baum-init.c b/qemu/stubs/chr-baum-init.c deleted file mode 100644 index f5cc6ce1f..000000000 --- a/qemu/stubs/chr-baum-init.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "qemu-common.h" -#include "sysemu/char.h" - -CharDriverState *chr_baum_init(void) -{ - return NULL; -} diff --git a/qemu/stubs/chr-msmouse.c b/qemu/stubs/chr-msmouse.c deleted file mode 100644 index 812f8b0ab..000000000 --- a/qemu/stubs/chr-msmouse.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "qemu-common.h" -#include "sysemu/char.h" - -CharDriverState *qemu_chr_open_msmouse(void) -{ - return 0; -} diff --git a/qemu/stubs/chr-testdev.c b/qemu/stubs/chr-testdev.c deleted file mode 100644 index 23112a2c0..000000000 --- a/qemu/stubs/chr-testdev.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "qemu-common.h" -#include "sysemu/char.h" - -CharDriverState *chr_testdev_init(void) -{ - return 0; -} diff --git a/qemu/stubs/clock-warp.c b/qemu/stubs/clock-warp.c index 5565118d1..8acb58a77 100644 --- a/qemu/stubs/clock-warp.c +++ b/qemu/stubs/clock-warp.c @@ -1,7 +1,8 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/timer.h" -void qemu_clock_warp(QEMUClockType type) +void qemu_start_warp_timer(void) { } diff --git a/qemu/stubs/cpu-get-clock.c b/qemu/stubs/cpu-get-clock.c index 5b34c976d..1d07523b8 100644 --- a/qemu/stubs/cpu-get-clock.c +++ b/qemu/stubs/cpu-get-clock.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/timer.h" diff --git a/qemu/stubs/cpu-get-icount.c b/qemu/stubs/cpu-get-icount.c index d68585965..3a6f2ab00 100644 --- a/qemu/stubs/cpu-get-icount.c +++ b/qemu/stubs/cpu-get-icount.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/timer.h" diff --git a/qemu/stubs/cpus.c b/qemu/stubs/cpus.c index 8e6f06b11..e19272297 100644 --- a/qemu/stubs/cpus.c +++ b/qemu/stubs/cpus.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qom/cpu.h" diff --git a/qemu/stubs/dump.c b/qemu/stubs/dump.c index 8c24eda84..d9ee23f1e 100644 --- a/qemu/stubs/dump.c +++ b/qemu/stubs/dump.c @@ -11,6 +11,7 @@ * */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "sysemu/dump-arch.h" #include "qmp-commands.h" diff --git a/qemu/stubs/fd-register.c b/qemu/stubs/fd-register.c index d0c34fd2a..f91aa3418 100644 --- a/qemu/stubs/fd-register.c +++ b/qemu/stubs/fd-register.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/main-loop.h" diff --git a/qemu/stubs/fdset-add-fd.c b/qemu/stubs/fdset-add-fd.c index ee1643708..bf9e60aed 100644 --- a/qemu/stubs/fdset-add-fd.c +++ b/qemu/stubs/fdset-add-fd.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "monitor/monitor.h" diff --git a/qemu/stubs/fdset-find-fd.c b/qemu/stubs/fdset-find-fd.c index 4f18344ba..1d9caf37e 100644 --- a/qemu/stubs/fdset-find-fd.c +++ b/qemu/stubs/fdset-find-fd.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "monitor/monitor.h" diff --git a/qemu/stubs/fdset-get-fd.c b/qemu/stubs/fdset-get-fd.c index 7112c155e..5325044b5 100644 --- a/qemu/stubs/fdset-get-fd.c +++ b/qemu/stubs/fdset-get-fd.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "monitor/monitor.h" diff --git a/qemu/stubs/fdset-remove-fd.c b/qemu/stubs/fdset-remove-fd.c index 7f6d61e61..47ea29721 100644 --- a/qemu/stubs/fdset-remove-fd.c +++ b/qemu/stubs/fdset-remove-fd.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "monitor/monitor.h" diff --git a/qemu/stubs/gdbstub.c b/qemu/stubs/gdbstub.c index f6a4553a3..2b7aee50d 100644 --- a/qemu/stubs/gdbstub.c +++ b/qemu/stubs/gdbstub.c @@ -1,5 +1,4 @@ -#include "stdbool.h" /* bool (in exec/gdbstub.h) */ -#include "stddef.h" /* NULL */ +#include "qemu/osdep.h" #include "exec/gdbstub.h" /* xml_builtin */ const char *const xml_builtin[][2] = { diff --git a/qemu/stubs/get-fd.c b/qemu/stubs/get-fd.c index 9f2c65cf0..7dfdfb55f 100644 --- a/qemu/stubs/get-fd.c +++ b/qemu/stubs/get-fd.c @@ -1,3 +1,5 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "monitor/monitor.h" diff --git a/qemu/stubs/get-next-serial.c b/qemu/stubs/get-next-serial.c index 40c56d13d..6ff6a6d3b 100644 --- a/qemu/stubs/get-next-serial.c +++ b/qemu/stubs/get-next-serial.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" CharDriverState *serial_hds[0]; diff --git a/qemu/stubs/get-vm-name.c b/qemu/stubs/get-vm-name.c index e5f619ffa..fa990136b 100644 --- a/qemu/stubs/get-vm-name.c +++ b/qemu/stubs/get-vm-name.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" const char *qemu_get_vm_name(void) diff --git a/qemu/stubs/iohandler.c b/qemu/stubs/iohandler.c new file mode 100644 index 000000000..22b0ee5b0 --- /dev/null +++ b/qemu/stubs/iohandler.c @@ -0,0 +1,8 @@ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/main-loop.h" + +AioContext *iohandler_get_aio_context(void) +{ + abort(); +} diff --git a/qemu/stubs/iothread-lock.c b/qemu/stubs/iothread-lock.c index dda6f6b58..9b6db2e74 100644 --- a/qemu/stubs/iothread-lock.c +++ b/qemu/stubs/iothread-lock.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/main-loop.h" diff --git a/qemu/stubs/is-daemonized.c b/qemu/stubs/is-daemonized.c index c0ee9171a..d5cd1dc37 100644 --- a/qemu/stubs/is-daemonized.c +++ b/qemu/stubs/is-daemonized.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" /* Win32 has its own inline stub */ diff --git a/qemu/stubs/kvm.c b/qemu/stubs/kvm.c index e7c60b6e0..ddd620499 100644 --- a/qemu/stubs/kvm.c +++ b/qemu/stubs/kvm.c @@ -1,7 +1,8 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "sysemu/kvm.h" -int kvm_arch_irqchip_create(KVMState *s) +int kvm_arch_irqchip_create(MachineState *ms, KVMState *s) { return 0; } diff --git a/qemu/stubs/machine-init-done.c b/qemu/stubs/machine-init-done.c index 28a92555b..9a0d62514 100644 --- a/qemu/stubs/machine-init-done.c +++ b/qemu/stubs/machine-init-done.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "sysemu/sysemu.h" diff --git a/qemu/stubs/migr-blocker.c b/qemu/stubs/migr-blocker.c index 300df6e20..8ab3604df 100644 --- a/qemu/stubs/migr-blocker.c +++ b/qemu/stubs/migr-blocker.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "migration/migration.h" diff --git a/qemu/stubs/mon-is-qmp.c b/qemu/stubs/mon-is-qmp.c index dd26f19c8..a8344ced8 100644 --- a/qemu/stubs/mon-is-qmp.c +++ b/qemu/stubs/mon-is-qmp.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "monitor/monitor.h" diff --git a/qemu/stubs/mon-printf.c b/qemu/stubs/mon-printf.c index 0ce2ca692..e7c1e0cf7 100644 --- a/qemu/stubs/mon-printf.c +++ b/qemu/stubs/mon-printf.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "monitor/monitor.h" diff --git a/qemu/stubs/monitor-init.c b/qemu/stubs/monitor-init.c index 563902b41..de1bc7cd5 100644 --- a/qemu/stubs/monitor-init.c +++ b/qemu/stubs/monitor-init.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "monitor/monitor.h" diff --git a/qemu/stubs/notify-event.c b/qemu/stubs/notify-event.c index 32f7289d3..14e52268a 100644 --- a/qemu/stubs/notify-event.c +++ b/qemu/stubs/notify-event.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/main-loop.h" diff --git a/qemu/stubs/qemu-chr-open-spice.c b/qemu/stubs/qemu-chr-open-spice.c deleted file mode 100644 index f1c4849d9..000000000 --- a/qemu/stubs/qemu-chr-open-spice.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "qemu-common.h" -#include "ui/qemu-spice.h" - -CharDriverState *qemu_chr_open_spice_vmc(const char *type) -{ - return NULL; -} - -#if SPICE_SERVER_VERSION >= 0x000c02 -CharDriverState *qemu_chr_open_spice_port(const char *name) -{ - return NULL; -} -#endif diff --git a/qemu/stubs/qmp_pc_dimm_device_list.c b/qemu/stubs/qmp_pc_dimm_device_list.c index b584bd8b2..def211564 100644 --- a/qemu/stubs/qmp_pc_dimm_device_list.c +++ b/qemu/stubs/qmp_pc_dimm_device_list.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qom/object.h" #include "hw/mem/pc-dimm.h" @@ -5,8 +6,3 @@ int qmp_pc_dimm_device_list(Object *obj, void *opaque) { return 0; } - -ram_addr_t get_current_ram_size(void) -{ - return ram_size; -} diff --git a/qemu/stubs/qtest.c b/qemu/stubs/qtest.c index dc17594bb..891eb954f 100644 --- a/qemu/stubs/qtest.c +++ b/qemu/stubs/qtest.c @@ -8,7 +8,13 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "sysemu/qtest.h" /* Needed for qtest_allowed() */ bool qtest_allowed; + +bool qtest_driver(void) +{ + return false; +} diff --git a/qemu/stubs/replay-user.c b/qemu/stubs/replay-user.c new file mode 100644 index 000000000..b29e7ebba --- /dev/null +++ b/qemu/stubs/replay-user.c @@ -0,0 +1,33 @@ +/* + * replay.c + * + * Copyright (c) 2010-2015 Institute for System Programming + * of the Russian Academy of Sciences. + * + * 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 "sysemu/replay.h" + +bool replay_exception(void) +{ + return true; +} + +bool replay_has_exception(void) +{ + return false; +} + +bool replay_interrupt(void) +{ + return true; +} + +bool replay_has_interrupt(void) +{ + return false; +} diff --git a/qemu/stubs/replay.c b/qemu/stubs/replay.c new file mode 100644 index 000000000..de9fa1ec9 --- /dev/null +++ b/qemu/stubs/replay.c @@ -0,0 +1,69 @@ +#include "qemu/osdep.h" +#include "sysemu/replay.h" +#include "sysemu/sysemu.h" + +ReplayMode replay_mode; + +int64_t replay_save_clock(unsigned int kind, int64_t clock) +{ + abort(); + return 0; +} + +int64_t replay_read_clock(unsigned int kind) +{ + abort(); + return 0; +} + +bool replay_checkpoint(ReplayCheckpoint checkpoint) +{ + return true; +} + +bool replay_events_enabled(void) +{ + return false; +} + +void replay_finish(void) +{ +} + +void replay_register_char_driver(CharDriverState *chr) +{ +} + +void replay_chr_be_write(CharDriverState *s, uint8_t *buf, int len) +{ + abort(); +} + +void replay_char_write_event_save(int res, int offset) +{ + abort(); +} + +void replay_char_write_event_load(int *res, int *offset) +{ + abort(); +} + +int replay_char_read_all_load(uint8_t *buf) +{ + abort(); +} + +void replay_char_read_all_save_error(int res) +{ + abort(); +} + +void replay_char_read_all_save_buf(uint8_t *buf, int offset) +{ + abort(); +} + +void replay_block_event(QEMUBH *bh, uint64_t id) +{ +} diff --git a/qemu/stubs/reset.c b/qemu/stubs/reset.c index ad287251e..5d47711f9 100644 --- a/qemu/stubs/reset.c +++ b/qemu/stubs/reset.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/hw.h" /* Stub functions for binaries that never call qemu_devices_reset(), diff --git a/qemu/stubs/runstate-check.c b/qemu/stubs/runstate-check.c index bd2e3757a..7c5227e84 100644 --- a/qemu/stubs/runstate-check.c +++ b/qemu/stubs/runstate-check.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "sysemu/sysemu.h" bool runstate_check(RunState state) diff --git a/qemu/stubs/set-fd-handler.c b/qemu/stubs/set-fd-handler.c index a8481bc3c..06a5da48f 100644 --- a/qemu/stubs/set-fd-handler.c +++ b/qemu/stubs/set-fd-handler.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/main-loop.h" @@ -8,3 +9,13 @@ void qemu_set_fd_handler(int fd, { abort(); } + +void aio_set_fd_handler(AioContext *ctx, + int fd, + bool is_external, + IOHandler *io_read, + IOHandler *io_write, + void *opaque) +{ + abort(); +} diff --git a/qemu/stubs/slirp.c b/qemu/stubs/slirp.c index bd0ac7f27..dcae51f0e 100644 --- a/qemu/stubs/slirp.c +++ b/qemu/stubs/slirp.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "slirp/slirp.h" diff --git a/qemu/stubs/sysbus.c b/qemu/stubs/sysbus.c index e13496582..d8da90caa 100644 --- a/qemu/stubs/sysbus.c +++ b/qemu/stubs/sysbus.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/qdev-core.h" BusState *sysbus_get_default(void) diff --git a/qemu/stubs/target-get-monitor-def.c b/qemu/stubs/target-get-monitor-def.c new file mode 100644 index 000000000..394e0f9a7 --- /dev/null +++ b/qemu/stubs/target-get-monitor-def.c @@ -0,0 +1,29 @@ +/* + * Stub for target_get_monitor_def. + * + * Copyright IBM Corp., 2015 + * + * Author: Alexey Kardashevskiy + * + * 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 . + */ + +#include "qemu/osdep.h" + +int target_get_monitor_def(CPUState *cs, const char *name, uint64_t *pval); + +int target_get_monitor_def(CPUState *cs, const char *name, uint64_t *pval) +{ + return -1; +} diff --git a/qemu/stubs/target-monitor-defs.c b/qemu/stubs/target-monitor-defs.c new file mode 100644 index 000000000..ac07b1906 --- /dev/null +++ b/qemu/stubs/target-monitor-defs.c @@ -0,0 +1,8 @@ +#include "qemu/osdep.h" + +const MonitorDef *target_monitor_defs(void); + +const MonitorDef *target_monitor_defs(void) +{ + return NULL; +} diff --git a/qemu/stubs/uuid.c b/qemu/stubs/uuid.c index ffc0ed40a..92ad71783 100644 --- a/qemu/stubs/uuid.c +++ b/qemu/stubs/uuid.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "sysemu/sysemu.h" #include "qmp-commands.h" diff --git a/qemu/stubs/vc-init.c b/qemu/stubs/vc-init.c deleted file mode 100644 index 308dfa080..000000000 --- a/qemu/stubs/vc-init.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "qemu-common.h" -#include "sysemu/char.h" - -CharDriverState *vc_init(ChardevVC *vc) -{ - return 0; -} diff --git a/qemu/stubs/vhost.c b/qemu/stubs/vhost.c new file mode 100644 index 000000000..2d76cdebd --- /dev/null +++ b/qemu/stubs/vhost.c @@ -0,0 +1,7 @@ +#include "qemu/osdep.h" +#include "hw/virtio/vhost.h" + +bool vhost_has_free_slot(void) +{ + return true; +} diff --git a/qemu/stubs/vm-stop.c b/qemu/stubs/vm-stop.c index 69fd86b2e..8271cad65 100644 --- a/qemu/stubs/vm-stop.c +++ b/qemu/stubs/vm-stop.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "sysemu/sysemu.h" diff --git a/qemu/stubs/vmstate.c b/qemu/stubs/vmstate.c index 778bc3fc6..65906271d 100644 --- a/qemu/stubs/vmstate.c +++ b/qemu/stubs/vmstate.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "migration/vmstate.h" diff --git a/qemu/target-alpha/cpu.c b/qemu/target-alpha/cpu.c index 421d7e536..8a155cae9 100644 --- a/qemu/target-alpha/cpu.c +++ b/qemu/target-alpha/cpu.c @@ -19,6 +19,8 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "qemu-common.h" #include "migration/vmstate.h" @@ -46,6 +48,12 @@ static bool alpha_cpu_has_work(CPUState *cs) | CPU_INTERRUPT_MCHK); } +static void alpha_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) +{ + info->mach = bfd_mach_alpha_ev6; + info->print_insn = print_insn_alpha; +} + static void alpha_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); @@ -297,7 +305,16 @@ static void alpha_cpu_class_init(ObjectClass *oc, void *data) cc->get_phys_page_debug = alpha_cpu_get_phys_page_debug; dc->vmsd = &vmstate_alpha_cpu; #endif + cc->disas_set_info = alpha_cpu_disas_set_info; + cc->gdb_num_core_regs = 67; + + /* + * Reason: alpha_cpu_initfn() calls cpu_exec_init(), which saves + * the object in cpus -> dangling pointer after final + * object_unref(). + */ + dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo alpha_cpu_type_info = { diff --git a/qemu/target-alpha/cpu.h b/qemu/target-alpha/cpu.h index 91c56d6bc..420f2a53f 100644 --- a/qemu/target-alpha/cpu.h +++ b/qemu/target-alpha/cpu.h @@ -20,7 +20,6 @@ #if !defined (__CPU_ALPHA_H__) #define __CPU_ALPHA_H__ -#include "config.h" #include "qemu-common.h" #define TARGET_LONG_BITS 64 @@ -32,8 +31,6 @@ #include "fpu/softfloat.h" -#define ELF_MACHINE EM_ALPHA - #define ICACHE_LINE_SIZE 32 #define DCACHE_LINE_SIZE 32 @@ -289,7 +286,6 @@ struct CPUAlphaState { #define cpu_list alpha_cpu_list #define cpu_exec cpu_alpha_exec -#define cpu_gen_code cpu_alpha_gen_code #define cpu_signal_handler cpu_alpha_signal_handler #include "exec/cpu-all.h" @@ -376,7 +372,7 @@ enum { PS_USER_MODE = 8 }; -static inline int cpu_mmu_index(CPUAlphaState *env) +static inline int cpu_mmu_index(CPUAlphaState *env, bool ifetch) { if (env->pal_mode) { return MMU_KERNEL_IDX; @@ -445,8 +441,9 @@ void QEMU_NORETURN arith_excp(CPUAlphaState *, uintptr_t, int, uint64_t); uint64_t cpu_alpha_load_fpcr (CPUAlphaState *env); void cpu_alpha_store_fpcr (CPUAlphaState *env, uint64_t val); +uint64_t cpu_alpha_load_gr(CPUAlphaState *env, unsigned reg); +void cpu_alpha_store_gr(CPUAlphaState *env, unsigned reg, uint64_t val); #ifndef CONFIG_USER_ONLY -void swap_shadow_regs(CPUAlphaState *env); QEMU_NORETURN void alpha_cpu_unassigned_access(CPUState *cpu, hwaddr addr, bool is_write, bool is_exec, int unused, unsigned size); diff --git a/qemu/target-alpha/fpu_helper.c b/qemu/target-alpha/fpu_helper.c index b091aa842..5ab7d5e64 100644 --- a/qemu/target-alpha/fpu_helper.c +++ b/qemu/target-alpha/fpu_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" @@ -437,7 +438,7 @@ uint64_t helper_cvtqs(CPUAlphaState *env, uint64_t a) return float32_to_s(fr); } -/* Implement float64 to uint64 conversion without saturation -- we must +/* Implement float64 to uint64_t conversion without saturation -- we must supply the truncated result. This behaviour is used by the compiler to get unsigned conversion for free with the same instruction. */ diff --git a/qemu/target-alpha/gdbstub.c b/qemu/target-alpha/gdbstub.c index 980f140e7..199f02842 100644 --- a/qemu/target-alpha/gdbstub.c +++ b/qemu/target-alpha/gdbstub.c @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "exec/gdbstub.h" @@ -30,7 +30,7 @@ int alpha_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) switch (n) { case 0 ... 30: - val = env->ir[n]; + val = cpu_alpha_load_gr(env, n); break; case 32 ... 62: d.d = env->fir[n - 32]; @@ -66,7 +66,7 @@ int alpha_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) switch (n) { case 0 ... 30: - env->ir[n] = tmp; + cpu_alpha_store_gr(env, n, tmp); break; case 32 ... 62: d.ll = tmp; diff --git a/qemu/target-alpha/helper.c b/qemu/target-alpha/helper.c index 46b8ef914..6dec2639b 100644 --- a/qemu/target-alpha/helper.c +++ b/qemu/target-alpha/helper.c @@ -17,9 +17,7 @@ * License along with this library; if not, see . */ -#include -#include -#include +#include "qemu/osdep.h" #include "cpu.h" #include "fpu/softfloat.h" @@ -79,6 +77,30 @@ void helper_store_fpcr(CPUAlphaState *env, uint64_t val) cpu_alpha_store_fpcr(env, val); } +static uint64_t *cpu_alpha_addr_gr(CPUAlphaState *env, unsigned reg) +{ +#ifndef CONFIG_USER_ONLY + if (env->pal_mode) { + if (reg >= 8 && reg <= 14) { + return &env->shadow[reg - 8]; + } else if (reg == 25) { + return &env->shadow[7]; + } + } +#endif + return &env->ir[reg]; +} + +uint64_t cpu_alpha_load_gr(CPUAlphaState *env, unsigned reg) +{ + return *cpu_alpha_addr_gr(env, reg); +} + +void cpu_alpha_store_gr(CPUAlphaState *env, unsigned reg, uint64_t val) +{ + *cpu_alpha_addr_gr(env, reg) = val; +} + #if defined(CONFIG_USER_ONLY) int alpha_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, int mmu_idx) @@ -90,38 +112,6 @@ int alpha_cpu_handle_mmu_fault(CPUState *cs, vaddr address, return 1; } #else -void swap_shadow_regs(CPUAlphaState *env) -{ - uint64_t i0, i1, i2, i3, i4, i5, i6, i7; - - i0 = env->ir[8]; - i1 = env->ir[9]; - i2 = env->ir[10]; - i3 = env->ir[11]; - i4 = env->ir[12]; - i5 = env->ir[13]; - i6 = env->ir[14]; - i7 = env->ir[25]; - - env->ir[8] = env->shadow[0]; - env->ir[9] = env->shadow[1]; - env->ir[10] = env->shadow[2]; - env->ir[11] = env->shadow[3]; - env->ir[12] = env->shadow[4]; - env->ir[13] = env->shadow[5]; - env->ir[14] = env->shadow[6]; - env->ir[25] = env->shadow[7]; - - env->shadow[0] = i0; - env->shadow[1] = i1; - env->shadow[2] = i2; - env->shadow[3] = i3; - env->shadow[4] = i4; - env->shadow[5] = i5; - env->shadow[6] = i6; - env->shadow[7] = i7; -} - /* Returns the OSF/1 entMM failure indication, or -1 on success. */ static int get_physical_address(CPUAlphaState *env, target_ulong addr, int prot_need, int mmu_idx, @@ -375,10 +365,7 @@ void alpha_cpu_do_interrupt(CPUState *cs) env->pc = env->palbr + i; /* Switch to PALmode. */ - if (!env->pal_mode) { - env->pal_mode = 1; - swap_shadow_regs(env); - } + env->pal_mode = 1; #endif /* !USER_ONLY */ } @@ -443,7 +430,7 @@ void alpha_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, env->pc, env->ps); for (i = 0; i < 31; i++) { cpu_fprintf(f, "IR%02d %s " TARGET_FMT_lx " ", i, - linux_reg_names[i], env->ir[i]); + linux_reg_names[i], cpu_alpha_load_gr(env, i)); if ((i % 3) == 2) cpu_fprintf(f, "\n"); } diff --git a/qemu/target-alpha/helper.h b/qemu/target-alpha/helper.h index d221f0d7d..c3d8a3ee4 100644 --- a/qemu/target-alpha/helper.h +++ b/qemu/target-alpha/helper.h @@ -10,6 +10,7 @@ DEF_HELPER_FLAGS_1(cttz, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_FLAGS_2(zap, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(zapnot, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_1(cmpbe0, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_FLAGS_2(cmpbge, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(minub8, TCG_CALL_NO_RWG_SE, i64, i64, i64) @@ -91,9 +92,6 @@ DEF_HELPER_FLAGS_2(ieee_input_cmp, TCG_CALL_NO_WG, void, env, i64) DEF_HELPER_FLAGS_2(ieee_input_s, TCG_CALL_NO_WG, void, env, i64) #if !defined (CONFIG_USER_ONLY) -DEF_HELPER_2(hw_ret, void, env, i64) -DEF_HELPER_3(call_pal, void, env, i64, i64) - DEF_HELPER_2(ldl_phys, i64, env, i64) DEF_HELPER_2(ldq_phys, i64, env, i64) DEF_HELPER_2(ldl_l_phys, i64, env, i64) diff --git a/qemu/target-alpha/int_helper.c b/qemu/target-alpha/int_helper.c index 74f38cbe7..777e48d08 100644 --- a/qemu/target-alpha/int_helper.c +++ b/qemu/target-alpha/int_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" @@ -58,20 +59,47 @@ uint64_t helper_zap(uint64_t val, uint64_t mask) return helper_zapnot(val, ~mask); } -uint64_t helper_cmpbge(uint64_t op1, uint64_t op2) +uint64_t helper_cmpbe0(uint64_t a) { - uint8_t opa, opb, res; - int i; + uint64_t m = 0x7f7f7f7f7f7f7f7fULL; + uint64_t c = ~(((a & m) + m) | a | m); + /* a.......b.......c.......d.......e.......f.......g.......h....... */ + c |= c << 7; + /* ab......bc......cd......de......ef......fg......gh......h....... */ + c |= c << 14; + /* abcd....bcde....cdef....defg....efgh....fgh.....gh......h....... */ + c |= c << 28; + /* abcdefghbcdefgh.cdefgh..defgh...efgh....fgh.....gh......h....... */ + return c >> 56; +} - res = 0; - for (i = 0; i < 8; i++) { - opa = op1 >> (i * 8); - opb = op2 >> (i * 8); - if (opa >= opb) { - res |= 1 << i; - } - } - return res; +uint64_t helper_cmpbge(uint64_t a, uint64_t b) +{ + uint64_t mask = 0x00ff00ff00ff00ffULL; + uint64_t test = 0x0100010001000100ULL; + uint64_t al, ah, bl, bh, cl, ch; + + /* Separate the bytes to avoid false positives. */ + al = a & mask; + bl = b & mask; + ah = (a >> 8) & mask; + bh = (b >> 8) & mask; + + /* "Compare". If a byte in B is greater than a byte in A, + it will clear the test bit. */ + cl = ((al | test) - bl) & test; + ch = ((ah | test) - bh) & test; + + /* Fold all of the test bits into a contiguous set. */ + /* ch=.......a...............c...............e...............g........ */ + /* cl=.......b...............d...............f...............h........ */ + cl += ch << 1; + /* cl=......ab..............cd..............ef..............gh........ */ + cl |= cl << 14; + /* cl=......abcd............cdef............efgh............gh........ */ + cl |= cl << 28; + /* cl=......abcdefgh........cdefgh..........efgh............gh........ */ + return cl >> 50; } uint64_t helper_minub8(uint64_t op1, uint64_t op2) diff --git a/qemu/target-alpha/machine.c b/qemu/target-alpha/machine.c index e796bbe27..9ab092852 100644 --- a/qemu/target-alpha/machine.c +++ b/qemu/target-alpha/machine.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/boards.h" @@ -70,8 +71,8 @@ static VMStateField vmstate_env_fields[] = { static const VMStateDescription vmstate_env = { .name = "env", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = vmstate_env_fields, }; diff --git a/qemu/target-alpha/mem_helper.c b/qemu/target-alpha/mem_helper.c index 7b5e30ddb..7fee9a6e2 100644 --- a/qemu/target-alpha/mem_helper.c +++ b/qemu/target-alpha/mem_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" @@ -132,7 +133,7 @@ void alpha_cpu_unassigned_access(CPUState *cs, hwaddr addr, env->error_code = 0; /* ??? We should cpu_restore_state to the faulting insn, but this hook - does not have access to the retaddr value from the orignal helper. + does not have access to the retaddr value from the original helper. It's all moot until the QEMU PALcode grows an MCHK handler. */ cpu_loop_exit(cs); diff --git a/qemu/target-alpha/sys_helper.c b/qemu/target-alpha/sys_helper.c index 1c59e108b..e2dec15b6 100644 --- a/qemu/target-alpha/sys_helper.c +++ b/qemu/target-alpha/sys_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "sysemu/sysemu.h" @@ -34,34 +35,12 @@ uint64_t helper_load_pcc(CPUAlphaState *env) #else /* In user-mode, QEMU_CLOCK_VIRTUAL doesn't exist. Just pass through the host cpu clock ticks. Also, don't bother taking PCC_OFS into account. */ - return (uint32_t)cpu_get_real_ticks(); + return (uint32_t)cpu_get_host_ticks(); #endif } /* PALcode support special instructions */ #ifndef CONFIG_USER_ONLY -void helper_hw_ret(CPUAlphaState *env, uint64_t a) -{ - env->pc = a & ~3; - env->intr_flag = 0; - env->lock_addr = -1; - if ((a & 1) == 0) { - env->pal_mode = 0; - swap_shadow_regs(env); - } -} - -void helper_call_pal(CPUAlphaState *env, uint64_t pc, uint64_t entry_ofs) -{ - int pal_mode = env->pal_mode; - env->exc_addr = pc | pal_mode; - env->pc = env->palbr + entry_ofs; - if (!pal_mode) { - env->pal_mode = 1; - swap_shadow_regs(env); - } -} - void helper_tbia(CPUAlphaState *env) { tlb_flush(CPU(alpha_env_get_cpu(env)), 1); diff --git a/qemu/target-alpha/translate.c b/qemu/target-alpha/translate.c index 81d4ff827..5b86992dd 100644 --- a/qemu/target-alpha/translate.c +++ b/qemu/target-alpha/translate.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "disas/disas.h" #include "qemu/host-utils.h" @@ -27,6 +28,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" #undef ALPHA_DEBUG_DISAS @@ -42,6 +44,9 @@ typedef struct DisasContext DisasContext; struct DisasContext { struct TranslationBlock *tb; uint64_t pc; +#ifndef CONFIG_USER_ONLY + uint64_t palbr; +#endif int mem_idx; /* Current rounding mode for this TB. */ @@ -52,6 +57,9 @@ struct DisasContext { /* implver value for this CPU. */ int implver; + /* The set of registers active in the current context. */ + TCGv *ir; + /* Temporaries for $31 and $f31 as source and destination. */ TCGv zero; TCGv sink; @@ -85,14 +93,18 @@ typedef enum { } ExitStatus; /* global register indexes */ -static TCGv_ptr cpu_env; -static TCGv cpu_ir[31]; +static TCGv_env cpu_env; +static TCGv cpu_std_ir[31]; static TCGv cpu_fir[31]; static TCGv cpu_pc; static TCGv cpu_lock_addr; static TCGv cpu_lock_st_addr; static TCGv cpu_lock_value; +#ifndef CONFIG_USER_ONLY +static TCGv cpu_pal_ir[31]; +#endif + #include "exec/gen-icount.h" void alpha_translate_init(void) @@ -122,6 +134,12 @@ void alpha_translate_init(void) "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24", "f25", "f26", "f27", "f28", "f29", "f30" }; +#ifndef CONFIG_USER_ONLY + static const char shadow_names[8][8] = { + "pal_t7", "pal_s0", "pal_s1", "pal_s2", + "pal_s3", "pal_s4", "pal_s5", "pal_t11" + }; +#endif static bool done_init = 0; int i; @@ -134,20 +152,31 @@ void alpha_translate_init(void) cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); for (i = 0; i < 31; i++) { - cpu_ir[i] = tcg_global_mem_new_i64(TCG_AREG0, - offsetof(CPUAlphaState, ir[i]), - greg_names[i]); + cpu_std_ir[i] = tcg_global_mem_new_i64(cpu_env, + offsetof(CPUAlphaState, ir[i]), + greg_names[i]); } for (i = 0; i < 31; i++) { - cpu_fir[i] = tcg_global_mem_new_i64(TCG_AREG0, + cpu_fir[i] = tcg_global_mem_new_i64(cpu_env, offsetof(CPUAlphaState, fir[i]), freg_names[i]); } +#ifndef CONFIG_USER_ONLY + memcpy(cpu_pal_ir, cpu_std_ir, sizeof(cpu_pal_ir)); + for (i = 0; i < 8; i++) { + int r = (i == 7 ? 25 : i + 8); + cpu_pal_ir[r] = tcg_global_mem_new_i64(cpu_env, + offsetof(CPUAlphaState, + shadow[i]), + shadow_names[i]); + } +#endif + for (i = 0; i < ARRAY_SIZE(vars); ++i) { const GlobalVar *v = &vars[i]; - *v->var = tcg_global_mem_new_i64(TCG_AREG0, v->ofs, v->name); + *v->var = tcg_global_mem_new_i64(cpu_env, v->ofs, v->name); } } @@ -170,7 +199,7 @@ static TCGv dest_sink(DisasContext *ctx) static TCGv load_gpr(DisasContext *ctx, unsigned reg) { if (likely(reg < 31)) { - return cpu_ir[reg]; + return ctx->ir[reg]; } else { return load_zero(ctx); } @@ -183,7 +212,7 @@ static TCGv load_gpr_lit(DisasContext *ctx, unsigned reg, ctx->lit = tcg_const_i64(lit); return ctx->lit; } else if (likely(reg < 31)) { - return cpu_ir[reg]; + return ctx->ir[reg]; } else { return load_zero(ctx); } @@ -192,7 +221,7 @@ static TCGv load_gpr_lit(DisasContext *ctx, unsigned reg, static TCGv dest_gpr(DisasContext *ctx, unsigned reg) { if (likely(reg < 31)) { - return cpu_ir[reg]; + return ctx->ir[reg]; } else { return dest_sink(ctx); } @@ -304,7 +333,7 @@ static inline void gen_load_mem(DisasContext *ctx, addr = tmp; } - va = (fp ? cpu_fir[ra] : cpu_ir[ra]); + va = (fp ? cpu_fir[ra] : ctx->ir[ra]); tcg_gen_qemu_load(va, addr, ctx->mem_idx); tcg_temp_free(tmp); @@ -399,13 +428,13 @@ static ExitStatus gen_store_conditional(DisasContext *ctx, int ra, int rb, tcg_gen_qemu_ld_i64(val, addr, ctx->mem_idx, quad ? MO_LEQ : MO_LESL); tcg_gen_brcond_i64(TCG_COND_NE, val, cpu_lock_value, lab_fail); - tcg_gen_qemu_st_i64(cpu_ir[ra], addr, ctx->mem_idx, + tcg_gen_qemu_st_i64(ctx->ir[ra], addr, ctx->mem_idx, quad ? MO_LEQ : MO_LEUL); - tcg_gen_movi_i64(cpu_ir[ra], 1); + tcg_gen_movi_i64(ctx->ir[ra], 1); tcg_gen_br(lab_done); gen_set_label(lab_fail); - tcg_gen_movi_i64(cpu_ir[ra], 0); + tcg_gen_movi_i64(ctx->ir[ra], 0); gen_set_label(lab_done); tcg_gen_movi_i64(cpu_lock_addr, -1); @@ -444,7 +473,7 @@ static ExitStatus gen_bdirect(DisasContext *ctx, int ra, int32_t disp) uint64_t dest = ctx->pc + (disp << 2); if (ra != 31) { - tcg_gen_movi_i64(cpu_ir[ra], ctx->pc); + tcg_gen_movi_i64(ctx->ir[ra], ctx->pc); } /* Notice branch-to-next; used to initialize RA with the PC. */ @@ -1059,12 +1088,13 @@ static void gen_msk_l(DisasContext *ctx, TCGv vc, TCGv va, int rb, bool islit, } } -static void gen_rx(int ra, int set) +static void gen_rx(DisasContext *ctx, int ra, int set) { TCGv_i32 tmp; if (ra != 31) { - tcg_gen_ld8u_i64(cpu_ir[ra], cpu_env, offsetof(CPUAlphaState, intr_flag)); + tcg_gen_ld8u_i64(ctx->ir[ra], cpu_env, + offsetof(CPUAlphaState, intr_flag)); } tmp = tcg_const_i32(set); @@ -1086,12 +1116,12 @@ static ExitStatus gen_call_pal(DisasContext *ctx, int palcode) break; case 0x9E: /* RDUNIQUE */ - tcg_gen_ld_i64(cpu_ir[IR_V0], cpu_env, + tcg_gen_ld_i64(ctx->ir[IR_V0], cpu_env, offsetof(CPUAlphaState, unique)); break; case 0x9F: /* WRUNIQUE */ - tcg_gen_st_i64(cpu_ir[IR_A0], cpu_env, + tcg_gen_st_i64(ctx->ir[IR_A0], cpu_env, offsetof(CPUAlphaState, unique)); break; default: @@ -1115,17 +1145,17 @@ static ExitStatus gen_call_pal(DisasContext *ctx, int palcode) break; case 0x2D: /* WRVPTPTR */ - tcg_gen_st_i64(cpu_ir[IR_A0], cpu_env, + tcg_gen_st_i64(ctx->ir[IR_A0], cpu_env, offsetof(CPUAlphaState, vptptr)); break; case 0x31: /* WRVAL */ - tcg_gen_st_i64(cpu_ir[IR_A0], cpu_env, + tcg_gen_st_i64(ctx->ir[IR_A0], cpu_env, offsetof(CPUAlphaState, sysval)); break; case 0x32: /* RDVAL */ - tcg_gen_ld_i64(cpu_ir[IR_V0], cpu_env, + tcg_gen_ld_i64(ctx->ir[IR_V0], cpu_env, offsetof(CPUAlphaState, sysval)); break; @@ -1135,12 +1165,12 @@ static ExitStatus gen_call_pal(DisasContext *ctx, int palcode) /* Note that we already know we're in kernel mode, so we know that PS only contains the 3 IPL bits. */ - tcg_gen_ld8u_i64(cpu_ir[IR_V0], cpu_env, + tcg_gen_ld8u_i64(ctx->ir[IR_V0], cpu_env, offsetof(CPUAlphaState, ps)); /* But make sure and store only the 3 IPL bits from the user. */ tmp = tcg_temp_new(); - tcg_gen_andi_i64(tmp, cpu_ir[IR_A0], PS_INT_MASK); + tcg_gen_andi_i64(tmp, ctx->ir[IR_A0], PS_INT_MASK); tcg_gen_st8_i64(tmp, cpu_env, offsetof(CPUAlphaState, ps)); tcg_temp_free(tmp); break; @@ -1148,22 +1178,22 @@ static ExitStatus gen_call_pal(DisasContext *ctx, int palcode) case 0x36: /* RDPS */ - tcg_gen_ld8u_i64(cpu_ir[IR_V0], cpu_env, + tcg_gen_ld8u_i64(ctx->ir[IR_V0], cpu_env, offsetof(CPUAlphaState, ps)); break; case 0x38: /* WRUSP */ - tcg_gen_st_i64(cpu_ir[IR_A0], cpu_env, + tcg_gen_st_i64(ctx->ir[IR_A0], cpu_env, offsetof(CPUAlphaState, usp)); break; case 0x3A: /* RDUSP */ - tcg_gen_ld_i64(cpu_ir[IR_V0], cpu_env, + tcg_gen_ld_i64(ctx->ir[IR_V0], cpu_env, offsetof(CPUAlphaState, usp)); break; case 0x3C: /* WHAMI */ - tcg_gen_ld32s_i64(cpu_ir[IR_V0], cpu_env, + tcg_gen_ld32s_i64(ctx->ir[IR_V0], cpu_env, -offsetof(AlphaCPU, env) + offsetof(CPUState, cpu_index)); break; @@ -1181,15 +1211,24 @@ static ExitStatus gen_call_pal(DisasContext *ctx, int palcode) return gen_excp(ctx, EXCP_CALL_PAL, palcode); #else { - TCGv pc = tcg_const_i64(ctx->pc); - TCGv entry = tcg_const_i64(palcode & 0x80 - ? 0x2000 + (palcode - 0x80) * 64 - : 0x1000 + palcode * 64); + TCGv tmp = tcg_temp_new(); + uint64_t exc_addr = ctx->pc; + uint64_t entry = ctx->palbr; - gen_helper_call_pal(cpu_env, pc, entry); + if (ctx->tb->flags & TB_FLAGS_PAL_MODE) { + exc_addr |= 1; + } else { + tcg_gen_movi_i64(tmp, 1); + tcg_gen_st8_i64(tmp, cpu_env, offsetof(CPUAlphaState, pal_mode)); + } + + tcg_gen_movi_i64(tmp, exc_addr); + tcg_gen_st_i64(tmp, cpu_env, offsetof(CPUAlphaState, exc_addr)); + tcg_temp_free(tmp); - tcg_temp_free(entry); - tcg_temp_free(pc); + entry += (palcode & 0x80 + ? 0x2000 + (palcode - 0x80) * 64 + : 0x1000 + palcode * 64); /* Since the destination is running in PALmode, we don't really need the page permissions check. We'll see the existence of @@ -1197,11 +1236,13 @@ static ExitStatus gen_call_pal(DisasContext *ctx, int palcode) we change the PAL base register. */ if (!ctx->singlestep_enabled && !(ctx->tb->cflags & CF_LAST_IO)) { tcg_gen_goto_tb(0); + tcg_gen_movi_i64(cpu_pc, entry); tcg_gen_exit_tb((uintptr_t)ctx->tb); return EXIT_GOTO_TB; + } else { + tcg_gen_movi_i64(cpu_pc, entry); + return EXIT_PC_UPDATED; } - - return EXIT_PC_UPDATED; } #endif } @@ -1228,8 +1269,6 @@ static int cpu_pr_data(int pr) case 11: return offsetof(CPUAlphaState, sysval); case 12: return offsetof(CPUAlphaState, usp); - case 32 ... 39: - return offsetof(CPUAlphaState, shadow[pr - 32]); case 40 ... 63: return offsetof(CPUAlphaState, scratch[pr - 40]); @@ -1241,36 +1280,48 @@ static int cpu_pr_data(int pr) static ExitStatus gen_mfpr(DisasContext *ctx, TCGv va, int regno) { - int data = cpu_pr_data(regno); - - /* Special help for VMTIME and WALLTIME. */ - if (regno == 250 || regno == 249) { - void (*helper)(TCGv) = gen_helper_get_walltime; - if (regno == 249) { - helper = gen_helper_get_vmtime; - } - if (ctx->tb->cflags & CF_USE_ICOUNT) { + void (*helper)(TCGv); + int data; + + switch (regno) { + case 32 ... 39: + /* Accessing the "non-shadow" general registers. */ + regno = regno == 39 ? 25 : regno - 32 + 8; + tcg_gen_mov_i64(va, cpu_std_ir[regno]); + break; + + case 250: /* WALLTIME */ + helper = gen_helper_get_walltime; + goto do_helper; + case 249: /* VMTIME */ + helper = gen_helper_get_vmtime; + do_helper: + if (use_icount) { gen_io_start(); helper(va); gen_io_end(); return EXIT_PC_STALE; } else { helper(va); - return NO_EXIT; } - } + break; - /* The basic registers are data only, and unknown registers - are read-zero, write-ignore. */ - if (data == 0) { - tcg_gen_movi_i64(va, 0); - } else if (data & PR_BYTE) { - tcg_gen_ld8u_i64(va, cpu_env, data & ~PR_BYTE); - } else if (data & PR_LONG) { - tcg_gen_ld32s_i64(va, cpu_env, data & ~PR_LONG); - } else { - tcg_gen_ld_i64(va, cpu_env, data); + default: + /* The basic registers are data only, and unknown registers + are read-zero, write-ignore. */ + data = cpu_pr_data(regno); + if (data == 0) { + tcg_gen_movi_i64(va, 0); + } else if (data & PR_BYTE) { + tcg_gen_ld8u_i64(va, cpu_env, data & ~PR_BYTE); + } else if (data & PR_LONG) { + tcg_gen_ld32s_i64(va, cpu_env, data & ~PR_LONG); + } else { + tcg_gen_ld_i64(va, cpu_env, data); + } + break; } + return NO_EXIT; } @@ -1316,6 +1367,12 @@ static ExitStatus gen_mtpr(DisasContext *ctx, TCGv vb, int regno) gen_helper_tb_flush(cpu_env); return EXIT_PC_STALE; + case 32 ... 39: + /* Accessing the "non-shadow" general registers. */ + regno = regno == 39 ? 25 : regno - 32 + 8; + tcg_gen_mov_i64(cpu_std_ir[regno], vb); + break; + default: /* The basic registers are data only, and unknown registers are read-zero, write-ignore. */ @@ -1507,7 +1564,12 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) break; case 0x0F: /* CMPBGE */ - gen_helper_cmpbge(vc, va, vb); + if (ra == 31) { + /* Special case 0 >= X as X == 0. */ + gen_helper_cmpbe0(vc, vb); + } else { + gen_helper_cmpbge(vc, va, vb); + } break; case 0x12: /* S8ADDL */ @@ -1952,7 +2014,7 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) REQUIRE_REG_31(rb); t32 = tcg_temp_new_i32(); va = load_gpr(ctx, ra); - tcg_gen_trunc_i64_i32(t32, va); + tcg_gen_extrl_i64_i32(t32, va); gen_helper_memory_to_s(vc, t32); tcg_temp_free_i32(t32); break; @@ -1972,7 +2034,7 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) REQUIRE_REG_31(rb); t32 = tcg_temp_new_i32(); va = load_gpr(ctx, ra); - tcg_gen_trunc_i64_i32(t32, va); + tcg_gen_extrl_i64_i32(t32, va); gen_helper_memory_to_f(vc, t32); tcg_temp_free_i32(t32); break; @@ -2295,14 +2357,14 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) break; case 0xE000: /* RC */ - gen_rx(ra, 0); + gen_rx(ctx, ra, 0); break; case 0xE800: /* ECB */ break; case 0xF000: /* RS */ - gen_rx(ra, 1); + gen_rx(ctx, ra, 1); break; case 0xF800: /* WH64 */ @@ -2334,7 +2396,7 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) vb = load_gpr(ctx, rb); tcg_gen_andi_i64(cpu_pc, vb, ~3); if (ra != 31) { - tcg_gen_movi_i64(cpu_ir[ra], ctx->pc); + tcg_gen_movi_i64(ctx->ir[ra], ctx->pc); } ret = EXIT_PC_UPDATED; break; @@ -2374,10 +2436,10 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) goto invalid_opc; break; case 0x6: - /* Incpu_ir[ra]id */ + /* Invalid */ goto invalid_opc; case 0x7: - /* Incpu_ir[ra]id */ + /* Invaliid */ goto invalid_opc; case 0x8: /* Longword virtual access (hw_ldl) */ @@ -2580,13 +2642,18 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) /* Pre-EV6 CPUs interpreted this as HW_REI, loading the return address from EXC_ADDR. This turns out to be useful for our emulation PALcode, so continue to accept it. */ - tmp = tcg_temp_new(); - tcg_gen_ld_i64(tmp, cpu_env, offsetof(CPUAlphaState, exc_addr)); - gen_helper_hw_ret(cpu_env, tmp); - tcg_temp_free(tmp); + ctx->lit = vb = tcg_temp_new(); + tcg_gen_ld_i64(vb, cpu_env, offsetof(CPUAlphaState, exc_addr)); } else { - gen_helper_hw_ret(cpu_env, load_gpr(ctx, rb)); + vb = load_gpr(ctx, rb); } + tmp = tcg_temp_new(); + tcg_gen_movi_i64(tmp, 0); + tcg_gen_st8_i64(tmp, cpu_env, offsetof(CPUAlphaState, intr_flag)); + tcg_gen_movi_i64(cpu_lock_addr, -1); + tcg_gen_andi_i64(tmp, vb, 1); + tcg_gen_st8_i64(tmp, cpu_env, offsetof(CPUAlphaState, pal_mode)); + tcg_gen_andi_i64(cpu_pc, vb, ~3); ret = EXIT_PC_UPDATED; break; #else @@ -2793,18 +2860,14 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) return ret; } -static inline void gen_intermediate_code_internal(AlphaCPU *cpu, - TranslationBlock *tb, - bool search_pc) +void gen_intermediate_code(CPUAlphaState *env, struct TranslationBlock *tb) { + AlphaCPU *cpu = alpha_env_get_cpu(env); CPUState *cs = CPU(cpu); - CPUAlphaState *env = &cpu->env; DisasContext ctx, *ctxp = &ctx; target_ulong pc_start; target_ulong pc_mask; uint32_t insn; - CPUBreakpoint *bp; - int j, lj = -1; ExitStatus ret; int num_insns; int max_insns; @@ -2813,10 +2876,17 @@ static inline void gen_intermediate_code_internal(AlphaCPU *cpu, ctx.tb = tb; ctx.pc = pc_start; - ctx.mem_idx = cpu_mmu_index(env); + ctx.mem_idx = cpu_mmu_index(env, false); ctx.implver = env->implver; ctx.singlestep_enabled = cs->singlestep_enabled; +#ifdef CONFIG_USER_ONLY + ctx.ir = cpu_std_ir; +#else + ctx.palbr = env->palbr; + ctx.ir = (tb->flags & TB_FLAGS_PAL_MODE ? cpu_pal_ir : cpu_std_ir); +#endif + /* ??? Every TB begins with unset rounding mode, to be initialized on the first fp insn of the TB. Alternately we could define a proper default for every TB (e.g. QUAL_RM_N or QUAL_RM_D) and make sure @@ -2832,6 +2902,9 @@ static inline void gen_intermediate_code_internal(AlphaCPU *cpu, if (max_insns == 0) { max_insns = CF_COUNT_MASK; } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } if (in_superpage(&ctx, pc_start)) { pc_mask = (1ULL << 41) - 1; @@ -2841,35 +2914,22 @@ static inline void gen_intermediate_code_internal(AlphaCPU *cpu, gen_tb_start(tb); do { - if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { - QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - if (bp->pc == ctx.pc) { - gen_excp(&ctx, EXCP_DEBUG, 0); - break; - } - } - } - if (search_pc) { - j = tcg_op_buf_count(); - if (lj < j) { - lj++; - while (lj < j) { - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - } - tcg_ctx.gen_opc_pc[lj] = ctx.pc; - tcg_ctx.gen_opc_instr_start[lj] = 1; - tcg_ctx.gen_opc_icount[lj] = num_insns; + tcg_gen_insn_start(ctx.pc); + num_insns++; + + if (unlikely(cpu_breakpoint_test(cs, ctx.pc, BP_ANY))) { + ret = gen_excp(&ctx, EXCP_DEBUG, 0); + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + ctx.pc += 4; + break; } - if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) { + if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { gen_io_start(); } insn = cpu_ldl_code(env, ctx.pc); - num_insns++; - - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { - tcg_gen_debug_insn_start(ctx.pc); - } TCGV_UNUSED_I64(ctx.zero); TCGV_UNUSED_I64(ctx.sink); @@ -2925,16 +2985,8 @@ static inline void gen_intermediate_code_internal(AlphaCPU *cpu, gen_tb_end(tb, num_insns); - if (search_pc) { - j = tcg_op_buf_count(); - lj++; - while (lj <= j) { - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - } else { - tb->size = ctx.pc - pc_start; - tb->icount = num_insns; - } + tb->size = ctx.pc - pc_start; + tb->icount = num_insns; #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { @@ -2945,17 +2997,8 @@ static inline void gen_intermediate_code_internal(AlphaCPU *cpu, #endif } -void gen_intermediate_code (CPUAlphaState *env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(alpha_env_get_cpu(env), tb, false); -} - -void gen_intermediate_code_pc (CPUAlphaState *env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(alpha_env_get_cpu(env), tb, true); -} - -void restore_state_to_opc(CPUAlphaState *env, TranslationBlock *tb, int pc_pos) +void restore_state_to_opc(CPUAlphaState *env, TranslationBlock *tb, + target_ulong *data) { - env->pc = tcg_ctx.gen_opc_pc[pc_pos]; + env->pc = data[0]; } diff --git a/qemu/target-alpha/vax_helper.c b/qemu/target-alpha/vax_helper.c index 2e2f49971..e74ac3e04 100644 --- a/qemu/target-alpha/vax_helper.c +++ b/qemu/target-alpha/vax_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" diff --git a/qemu/target-arm/Makefile.objs b/qemu/target-arm/Makefile.objs index 9460b409a..82cbe6bba 100644 --- a/qemu/target-arm/Makefile.objs +++ b/qemu/target-arm/Makefile.objs @@ -1,5 +1,5 @@ obj-y += arm-semi.o -obj-$(CONFIG_SOFTMMU) += machine.o +obj-$(CONFIG_SOFTMMU) += machine.o psci.o arch_dump.o monitor.o obj-$(CONFIG_KVM) += kvm.o obj-$(call land,$(CONFIG_KVM),$(call lnot,$(TARGET_AARCH64))) += kvm32.o obj-$(call land,$(CONFIG_KVM),$(TARGET_AARCH64)) += kvm64.o @@ -7,6 +7,5 @@ obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o obj-y += translate.o op_helper.o helper.o cpu.o obj-y += neon_helper.o iwmmxt_helper.o obj-y += gdbstub.o -obj-$(CONFIG_SOFTMMU) += psci.o obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o helper-a64.o gdbstub64.o obj-y += crypto_helper.o diff --git a/qemu/target-arm/arch_dump.c b/qemu/target-arm/arch_dump.c new file mode 100644 index 000000000..1a9861f69 --- /dev/null +++ b/qemu/target-arm/arch_dump.c @@ -0,0 +1,337 @@ +/* Support for writing ELF notes for ARM architectures + * + * Copyright (C) 2015 Red Hat Inc. + * + * Author: Andrew Jones + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "elf.h" +#include "sysemu/dump.h" + +/* struct user_pt_regs from arch/arm64/include/uapi/asm/ptrace.h */ +struct aarch64_user_regs { + uint64_t regs[31]; + uint64_t sp; + uint64_t pc; + uint64_t pstate; +} QEMU_PACKED; + +QEMU_BUILD_BUG_ON(sizeof(struct aarch64_user_regs) != 272); + +/* struct elf_prstatus from include/uapi/linux/elfcore.h */ +struct aarch64_elf_prstatus { + char pad1[32]; /* 32 == offsetof(struct elf_prstatus, pr_pid) */ + uint32_t pr_pid; + char pad2[76]; /* 76 == offsetof(struct elf_prstatus, pr_reg) - + offsetof(struct elf_prstatus, pr_ppid) */ + struct aarch64_user_regs pr_reg; + uint32_t pr_fpvalid; + char pad3[4]; +} QEMU_PACKED; + +QEMU_BUILD_BUG_ON(sizeof(struct aarch64_elf_prstatus) != 392); + +/* struct user_fpsimd_state from arch/arm64/include/uapi/asm/ptrace.h + * + * While the vregs member of user_fpsimd_state is of type __uint128_t, + * QEMU uses an array of uint64_t, where the high half of the 128-bit + * value is always in the 2n+1'th index. Thus we also break the 128- + * bit values into two halves in this reproduction of user_fpsimd_state. + */ +struct aarch64_user_vfp_state { + uint64_t vregs[64]; + uint32_t fpsr; + uint32_t fpcr; + char pad[8]; +} QEMU_PACKED; + +QEMU_BUILD_BUG_ON(sizeof(struct aarch64_user_vfp_state) != 528); + +struct aarch64_note { + Elf64_Nhdr hdr; + char name[8]; /* align_up(sizeof("CORE"), 4) */ + union { + struct aarch64_elf_prstatus prstatus; + struct aarch64_user_vfp_state vfp; + }; +} QEMU_PACKED; + +#define AARCH64_NOTE_HEADER_SIZE offsetof(struct aarch64_note, prstatus) +#define AARCH64_PRSTATUS_NOTE_SIZE \ + (AARCH64_NOTE_HEADER_SIZE + sizeof(struct aarch64_elf_prstatus)) +#define AARCH64_PRFPREG_NOTE_SIZE \ + (AARCH64_NOTE_HEADER_SIZE + sizeof(struct aarch64_user_vfp_state)) + +static void aarch64_note_init(struct aarch64_note *note, DumpState *s, + const char *name, Elf64_Word namesz, + Elf64_Word type, Elf64_Word descsz) +{ + memset(note, 0, sizeof(*note)); + + note->hdr.n_namesz = cpu_to_dump32(s, namesz); + note->hdr.n_descsz = cpu_to_dump32(s, descsz); + note->hdr.n_type = cpu_to_dump32(s, type); + + memcpy(note->name, name, namesz); +} + +static int aarch64_write_elf64_prfpreg(WriteCoreDumpFunction f, + CPUARMState *env, int cpuid, + DumpState *s) +{ + struct aarch64_note note; + int ret, i; + + aarch64_note_init(¬e, s, "CORE", 5, NT_PRFPREG, sizeof(note.vfp)); + + for (i = 0; i < 64; ++i) { + note.vfp.vregs[i] = cpu_to_dump64(s, float64_val(env->vfp.regs[i])); + } + + if (s->dump_info.d_endian == ELFDATA2MSB) { + /* For AArch64 we must always swap the vfp.regs's 2n and 2n+1 + * entries when generating BE notes, because even big endian + * hosts use 2n+1 for the high half. + */ + for (i = 0; i < 32; ++i) { + uint64_t tmp = note.vfp.vregs[2*i]; + note.vfp.vregs[2*i] = note.vfp.vregs[2*i+1]; + note.vfp.vregs[2*i+1] = tmp; + } + } + + note.vfp.fpsr = cpu_to_dump32(s, vfp_get_fpsr(env)); + note.vfp.fpcr = cpu_to_dump32(s, vfp_get_fpcr(env)); + + ret = f(¬e, AARCH64_PRFPREG_NOTE_SIZE, s); + if (ret < 0) { + return -1; + } + + return 0; +} + +int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, + int cpuid, void *opaque) +{ + struct aarch64_note note; + CPUARMState *env = &ARM_CPU(cs)->env; + DumpState *s = opaque; + uint64_t pstate, sp; + int ret, i; + + aarch64_note_init(¬e, s, "CORE", 5, NT_PRSTATUS, sizeof(note.prstatus)); + + note.prstatus.pr_pid = cpu_to_dump32(s, cpuid); + note.prstatus.pr_fpvalid = cpu_to_dump32(s, 1); + + if (!is_a64(env)) { + aarch64_sync_32_to_64(env); + pstate = cpsr_read(env); + sp = 0; + } else { + pstate = pstate_read(env); + sp = env->xregs[31]; + } + + for (i = 0; i < 31; ++i) { + note.prstatus.pr_reg.regs[i] = cpu_to_dump64(s, env->xregs[i]); + } + note.prstatus.pr_reg.sp = cpu_to_dump64(s, sp); + note.prstatus.pr_reg.pc = cpu_to_dump64(s, env->pc); + note.prstatus.pr_reg.pstate = cpu_to_dump64(s, pstate); + + ret = f(¬e, AARCH64_PRSTATUS_NOTE_SIZE, s); + if (ret < 0) { + return -1; + } + + return aarch64_write_elf64_prfpreg(f, env, cpuid, s); +} + +/* struct pt_regs from arch/arm/include/asm/ptrace.h */ +struct arm_user_regs { + uint32_t regs[17]; + char pad[4]; +} QEMU_PACKED; + +QEMU_BUILD_BUG_ON(sizeof(struct arm_user_regs) != 72); + +/* struct elf_prstatus from include/uapi/linux/elfcore.h */ +struct arm_elf_prstatus { + char pad1[24]; /* 24 == offsetof(struct elf_prstatus, pr_pid) */ + uint32_t pr_pid; + char pad2[44]; /* 44 == offsetof(struct elf_prstatus, pr_reg) - + offsetof(struct elf_prstatus, pr_ppid) */ + struct arm_user_regs pr_reg; + uint32_t pr_fpvalid; +} QEMU_PACKED arm_elf_prstatus; + +QEMU_BUILD_BUG_ON(sizeof(struct arm_elf_prstatus) != 148); + +/* struct user_vfp from arch/arm/include/asm/user.h */ +struct arm_user_vfp_state { + uint64_t vregs[32]; + uint32_t fpscr; +} QEMU_PACKED; + +QEMU_BUILD_BUG_ON(sizeof(struct arm_user_vfp_state) != 260); + +struct arm_note { + Elf32_Nhdr hdr; + char name[8]; /* align_up(sizeof("LINUX"), 4) */ + union { + struct arm_elf_prstatus prstatus; + struct arm_user_vfp_state vfp; + }; +} QEMU_PACKED; + +#define ARM_NOTE_HEADER_SIZE offsetof(struct arm_note, prstatus) +#define ARM_PRSTATUS_NOTE_SIZE \ + (ARM_NOTE_HEADER_SIZE + sizeof(struct arm_elf_prstatus)) +#define ARM_VFP_NOTE_SIZE \ + (ARM_NOTE_HEADER_SIZE + sizeof(struct arm_user_vfp_state)) + +static void arm_note_init(struct arm_note *note, DumpState *s, + const char *name, Elf32_Word namesz, + Elf32_Word type, Elf32_Word descsz) +{ + memset(note, 0, sizeof(*note)); + + note->hdr.n_namesz = cpu_to_dump32(s, namesz); + note->hdr.n_descsz = cpu_to_dump32(s, descsz); + note->hdr.n_type = cpu_to_dump32(s, type); + + memcpy(note->name, name, namesz); +} + +static int arm_write_elf32_vfp(WriteCoreDumpFunction f, CPUARMState *env, + int cpuid, DumpState *s) +{ + struct arm_note note; + int ret, i; + + arm_note_init(¬e, s, "LINUX", 6, NT_ARM_VFP, sizeof(note.vfp)); + + for (i = 0; i < 32; ++i) { + note.vfp.vregs[i] = cpu_to_dump64(s, float64_val(env->vfp.regs[i])); + } + + note.vfp.fpscr = cpu_to_dump32(s, vfp_get_fpscr(env)); + + ret = f(¬e, ARM_VFP_NOTE_SIZE, s); + if (ret < 0) { + return -1; + } + + return 0; +} + +int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, + int cpuid, void *opaque) +{ + struct arm_note note; + CPUARMState *env = &ARM_CPU(cs)->env; + DumpState *s = opaque; + int ret, i, fpvalid = !!arm_feature(env, ARM_FEATURE_VFP); + + arm_note_init(¬e, s, "CORE", 5, NT_PRSTATUS, sizeof(note.prstatus)); + + note.prstatus.pr_pid = cpu_to_dump32(s, cpuid); + note.prstatus.pr_fpvalid = cpu_to_dump32(s, fpvalid); + + for (i = 0; i < 16; ++i) { + note.prstatus.pr_reg.regs[i] = cpu_to_dump32(s, env->regs[i]); + } + note.prstatus.pr_reg.regs[16] = cpu_to_dump32(s, cpsr_read(env)); + + ret = f(¬e, ARM_PRSTATUS_NOTE_SIZE, s); + if (ret < 0) { + return -1; + } else if (fpvalid) { + return arm_write_elf32_vfp(f, env, cpuid, s); + } + + return 0; +} + +int cpu_get_dump_info(ArchDumpInfo *info, + const GuestPhysBlockList *guest_phys_blocks) +{ + ARMCPU *cpu = ARM_CPU(first_cpu); + CPUARMState *env = &cpu->env; + GuestPhysBlock *block; + hwaddr lowest_addr = ULLONG_MAX; + + /* Take a best guess at the phys_base. If we get it wrong then crash + * will need '--machdep phys_offset=' added to its command + * line, which isn't any worse than assuming we can use zero, but being + * wrong. This is the same algorithm the crash utility uses when + * attempting to guess as it loads non-dumpfile formatted files. + */ + QTAILQ_FOREACH(block, &guest_phys_blocks->head, next) { + if (block->target_start < lowest_addr) { + lowest_addr = block->target_start; + } + } + + if (arm_feature(env, ARM_FEATURE_AARCH64)) { + info->d_machine = EM_AARCH64; + info->d_class = ELFCLASS64; + info->page_size = (1 << 16); /* aarch64 max pagesize */ + if (lowest_addr != ULLONG_MAX) { + info->phys_base = lowest_addr; + } + } else { + info->d_machine = EM_ARM; + info->d_class = ELFCLASS32; + info->page_size = (1 << 12); + if (lowest_addr < UINT_MAX) { + info->phys_base = lowest_addr; + } + } + + /* We assume the relevant endianness is that of EL1; this is right + * for kernels, but might give the wrong answer if you're trying to + * dump a hypervisor that happens to be running an opposite-endian + * kernel. + */ + info->d_endian = (env->cp15.sctlr_el[1] & SCTLR_EE) != 0 + ? ELFDATA2MSB : ELFDATA2LSB; + + return 0; +} + +ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) +{ + ARMCPU *cpu = ARM_CPU(first_cpu); + CPUARMState *env = &cpu->env; + size_t note_size; + + if (class == ELFCLASS64) { + note_size = AARCH64_PRSTATUS_NOTE_SIZE; + note_size += AARCH64_PRFPREG_NOTE_SIZE; + } else { + note_size = ARM_PRSTATUS_NOTE_SIZE; + if (arm_feature(env, ARM_FEATURE_VFP)) { + note_size += ARM_VFP_NOTE_SIZE; + } + } + + return note_size * nr_cpus; +} diff --git a/qemu/target-arm/arm-semi.c b/qemu/target-arm/arm-semi.c index a2a736956..8be0645eb 100644 --- a/qemu/target-arm/arm-semi.c +++ b/qemu/target-arm/arm-semi.c @@ -18,13 +18,7 @@ * along with this program; if not, see . */ -#include -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include "cpu.h" #include "exec/semihost.h" @@ -36,6 +30,7 @@ #include "qemu-common.h" #include "exec/gdbstub.h" #include "hw/arm/arm.h" +#include "qemu/cutils.h" #endif #define TARGET_SYS_OPEN 0x01 @@ -58,6 +53,7 @@ #define TARGET_SYS_GET_CMDLINE 0x15 #define TARGET_SYS_HEAPINFO 0x16 #define TARGET_SYS_EXIT 0x18 +#define TARGET_SYS_SYNCCACHE 0x19 /* ADP_Stopped_ApplicationExit is used for exit(0), * anything else is implemented as exit(1) */ @@ -134,6 +130,7 @@ static void arm_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) #ifdef CONFIG_USER_ONLY TaskState *ts = cs->opaque; #endif + target_ulong reg0 = is_a64(env) ? env->xregs[0] : env->regs[0]; if (ret == (target_ulong)-1) { #ifdef CONFIG_USER_ONLY @@ -141,22 +138,46 @@ static void arm_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) #else syscall_err = err; #endif - env->regs[0] = ret; + reg0 = ret; } else { /* Fixup syscalls that use nonstardard return conventions. */ - switch (env->regs[0]) { + switch (reg0) { case TARGET_SYS_WRITE: case TARGET_SYS_READ: - env->regs[0] = arm_semi_syscall_len - ret; + reg0 = arm_semi_syscall_len - ret; break; case TARGET_SYS_SEEK: - env->regs[0] = 0; + reg0 = 0; break; default: - env->regs[0] = ret; + reg0 = ret; break; } } + if (is_a64(env)) { + env->xregs[0] = reg0; + } else { + env->regs[0] = reg0; + } +} + +static target_ulong arm_flen_buf(ARMCPU *cpu) +{ + /* Return an address in target memory of 64 bytes where the remote + * gdb should write its stat struct. (The format of this structure + * is defined by GDB's remote protocol and is not target-specific.) + * We put this on the guest's stack just below SP. + */ + CPUARMState *env = &cpu->env; + target_ulong sp; + + if (is_a64(env)) { + sp = env->xregs[31]; + } else { + sp = env->regs[13]; + } + + return sp - 64; } static void arm_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err) @@ -166,8 +187,13 @@ static void arm_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err) /* The size is always stored in big-endian order, extract the value. We assume the size always fit in 32 bits. */ uint32_t size; - cpu_memory_rw_debug(cs, env->regs[13]-64+32, (uint8_t *)&size, 4, 0); - env->regs[0] = be32_to_cpu(size); + cpu_memory_rw_debug(cs, arm_flen_buf(cpu) + 32, (uint8_t *)&size, 4, 0); + size = be32_to_cpu(size); + if (is_a64(env)) { + env->xregs[0] = size; + } else { + env->regs[0] = size; + } #ifdef CONFIG_USER_ONLY ((TaskState *)cs->opaque)->swi_errno = err; #else @@ -175,17 +201,46 @@ static void arm_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err) #endif } +static target_ulong arm_gdb_syscall(ARMCPU *cpu, gdb_syscall_complete_cb cb, + const char *fmt, ...) +{ + va_list va; + CPUARMState *env = &cpu->env; + + va_start(va, fmt); + gdb_do_syscallv(cb, fmt, va); + va_end(va); + + /* FIXME: we are implicitly relying on the syscall completing + * before this point, which is not guaranteed. We should + * put in an explicit synchronization between this and + * the callback function. + */ + + return is_a64(env) ? env->xregs[0] : env->regs[0]; +} + /* Read the input value from the argument block; fail the semihosting * call if the memory read fails. */ #define GET_ARG(n) do { \ - if (get_user_ual(arg ## n, args + (n) * 4)) { \ - return (uint32_t)-1; \ + if (is_a64(env)) { \ + if (get_user_u64(arg ## n, args + (n) * 8)) { \ + return -1; \ + } \ + } else { \ + if (get_user_u32(arg ## n, args + (n) * 4)) { \ + return -1; \ + } \ } \ } while (0) -#define SET_ARG(n, val) put_user_ual(val, args + (n) * 4) -uint32_t do_arm_semihosting(CPUARMState *env) +#define SET_ARG(n, val) \ + (is_a64(env) ? \ + put_user_u64(val, args + (n) * 8) : \ + put_user_u32(val, args + (n) * 4)) + +target_ulong do_arm_semihosting(CPUARMState *env) { ARMCPU *cpu = arm_env_get_cpu(env); CPUState *cs = CPU(cpu); @@ -201,8 +256,15 @@ uint32_t do_arm_semihosting(CPUARMState *env) CPUARMState *ts = env; #endif - nr = env->regs[0]; - args = env->regs[1]; + if (is_a64(env)) { + /* Note that the syscall number is in W0, not X0 */ + nr = env->xregs[0] & 0xffffffffU; + args = env->xregs[1]; + } else { + nr = env->regs[0]; + args = env->regs[1]; + } + switch (nr) { case TARGET_SYS_OPEN: GET_ARG(0); @@ -223,9 +285,8 @@ uint32_t do_arm_semihosting(CPUARMState *env) return result_fileno; } if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", arg0, - (int)arg2+1, gdb_open_modeflags[arg1]); - ret = env->regs[0]; + ret = arm_gdb_syscall(cpu, arm_semi_cb, "open,%s,%x,1a4", arg0, + (int)arg2+1, gdb_open_modeflags[arg1]); } else { ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644)); } @@ -234,8 +295,7 @@ uint32_t do_arm_semihosting(CPUARMState *env) case TARGET_SYS_CLOSE: GET_ARG(0); if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_cb, "close,%x", arg0); - return env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_cb, "close,%x", arg0); } else { return set_swi_errno(ts, close(arg0)); } @@ -248,8 +308,7 @@ uint32_t do_arm_semihosting(CPUARMState *env) return (uint32_t)-1; /* Write to debug console. stderr is near enough. */ if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args); - return env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_cb, "write,2,%x,1", args); } else { return write(STDERR_FILENO, &c, 1); } @@ -260,8 +319,8 @@ uint32_t do_arm_semihosting(CPUARMState *env) return (uint32_t)-1; len = strlen(s); if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len); - ret = env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_cb, "write,2,%x,%x", + args, len); } else { ret = write(STDERR_FILENO, s, len); } @@ -274,8 +333,8 @@ uint32_t do_arm_semihosting(CPUARMState *env) len = arg2; if (use_gdb_syscalls()) { arm_semi_syscall_len = len; - gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", arg0, arg1, len); - return env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_cb, "write,%x,%x,%x", + arg0, arg1, len); } else { s = lock_user(VERIFY_READ, arg1, len, 1); if (!s) { @@ -295,8 +354,8 @@ uint32_t do_arm_semihosting(CPUARMState *env) len = arg2; if (use_gdb_syscalls()) { arm_semi_syscall_len = len; - gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", arg0, arg1, len); - return env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_cb, "read,%x,%x,%x", + arg0, arg1, len); } else { s = lock_user(VERIFY_WRITE, arg1, len, 0); if (!s) { @@ -317,8 +376,7 @@ uint32_t do_arm_semihosting(CPUARMState *env) case TARGET_SYS_ISTTY: GET_ARG(0); if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_cb, "isatty,%x", arg0); - return env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_cb, "isatty,%x", arg0); } else { return isatty(arg0); } @@ -326,8 +384,8 @@ uint32_t do_arm_semihosting(CPUARMState *env) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", arg0, arg1); - return env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_cb, "lseek,%x,%x,0", + arg0, arg1); } else { ret = set_swi_errno(ts, lseek(arg0, arg1, SEEK_SET)); if (ret == (uint32_t)-1) @@ -337,9 +395,8 @@ uint32_t do_arm_semihosting(CPUARMState *env) case TARGET_SYS_FLEN: GET_ARG(0); if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x", - arg0, env->regs[13]-64); - return env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_flen_cb, "fstat,%x,%x", + arg0, arm_flen_buf(cpu)); } else { struct stat buf; ret = set_swi_errno(ts, fstat(arg0, &buf)); @@ -354,8 +411,8 @@ uint32_t do_arm_semihosting(CPUARMState *env) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_cb, "unlink,%s", arg0, (int)arg1+1); - ret = env->regs[0]; + ret = arm_gdb_syscall(cpu, arm_semi_cb, "unlink,%s", + arg0, (int)arg1+1); } else { s = lock_user_string(arg0); if (!s) { @@ -372,9 +429,8 @@ uint32_t do_arm_semihosting(CPUARMState *env) GET_ARG(2); GET_ARG(3); if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_cb, "rename,%s,%s", - arg0, (int)arg1+1, arg2, (int)arg3+1); - return env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_cb, "rename,%s,%s", + arg0, (int)arg1+1, arg2, (int)arg3+1); } else { char *s2; s = lock_user_string(arg0); @@ -398,8 +454,8 @@ uint32_t do_arm_semihosting(CPUARMState *env) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_cb, "system,%s", arg0, (int)arg1+1); - return env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_cb, "system,%s", + arg0, (int)arg1+1); } else { s = lock_user_string(arg0); if (!s) { @@ -558,11 +614,35 @@ uint32_t do_arm_semihosting(CPUARMState *env) return 0; } case TARGET_SYS_EXIT: - /* ARM specifies only Stopped_ApplicationExit as normal - * exit, everything else is considered an error */ - ret = (args == ADP_Stopped_ApplicationExit) ? 0 : 1; + if (is_a64(env)) { + /* The A64 version of this call takes a parameter block, + * so the application-exit type can return a subcode which + * is the exit status code from the application. + */ + GET_ARG(0); + GET_ARG(1); + + if (arg0 == ADP_Stopped_ApplicationExit) { + ret = arg1; + } else { + ret = 1; + } + } else { + /* ARM specifies only Stopped_ApplicationExit as normal + * exit, everything else is considered an error */ + ret = (args == ADP_Stopped_ApplicationExit) ? 0 : 1; + } gdb_exit(env, ret); exit(ret); + case TARGET_SYS_SYNCCACHE: + /* Clean the D-cache and invalidate the I-cache for the specified + * virtual address range. This is a nop for us since we don't + * implement caches. This is only present on A64. + */ + if (is_a64(env)) { + return 0; + } + /* fall through -- invalid for A32/T32 */ default: fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr); cpu_dump_state(cs, stderr, fprintf, 0); diff --git a/qemu/target-arm/arm_ldst.h b/qemu/target-arm/arm_ldst.h index b1ece0173..35c2c4391 100644 --- a/qemu/target-arm/arm_ldst.h +++ b/qemu/target-arm/arm_ldst.h @@ -25,10 +25,10 @@ /* Load an instruction and return it in the standard little-endian order */ static inline uint32_t arm_ldl_code(CPUARMState *env, target_ulong addr, - bool do_swap) + bool sctlr_b) { uint32_t insn = cpu_ldl_code(env, addr); - if (do_swap) { + if (bswap_code(sctlr_b)) { return bswap32(insn); } return insn; @@ -36,10 +36,10 @@ static inline uint32_t arm_ldl_code(CPUARMState *env, target_ulong addr, /* Ditto, for a halfword (Thumb) instruction */ static inline uint16_t arm_lduw_code(CPUARMState *env, target_ulong addr, - bool do_swap) + bool sctlr_b) { uint16_t insn = cpu_lduw_code(env, addr); - if (do_swap) { + if (bswap_code(sctlr_b)) { return bswap16(insn); } return insn; diff --git a/qemu/target-arm/cpu-qom.h b/qemu/target-arm/cpu-qom.h index 3cbc4a006..1061c08a1 100644 --- a/qemu/target-arm/cpu-qom.h +++ b/qemu/target-arm/cpu-qom.h @@ -87,6 +87,9 @@ typedef struct ARMCPU { /* GPIO outputs for generic timer */ qemu_irq gt_timer_outputs[NUM_GTIMERS]; + /* MemoryRegion to use for secure physical accesses */ + MemoryRegion *secure_memory; + /* 'compatible' string for this CPU for Linux device trees */ const char *dtb_compatible; @@ -145,11 +148,14 @@ typedef struct ARMCPU { uint32_t id_pfr0; uint32_t id_pfr1; uint32_t id_dfr0; + uint32_t pmceid0; + uint32_t pmceid1; uint32_t id_afr0; uint32_t id_mmfr0; uint32_t id_mmfr1; uint32_t id_mmfr2; uint32_t id_mmfr3; + uint32_t id_mmfr4; uint32_t id_isar0; uint32_t id_isar1; uint32_t id_isar2; @@ -216,20 +222,39 @@ bool arm_cpu_exec_interrupt(CPUState *cpu, int int_req); void arm_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int flags); -hwaddr arm_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, + MemTxAttrs *attrs); int arm_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); int arm_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); +int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, + int cpuid, void *opaque); +int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, + int cpuid, void *opaque); + /* Callback functions for the generic timer's timers. */ void arm_gt_ptimer_cb(void *opaque); void arm_gt_vtimer_cb(void *opaque); +void arm_gt_htimer_cb(void *opaque); +void arm_gt_stimer_cb(void *opaque); + +#define ARM_AFF0_SHIFT 0 +#define ARM_AFF0_MASK (0xFFULL << ARM_AFF0_SHIFT) +#define ARM_AFF1_SHIFT 8 +#define ARM_AFF1_MASK (0xFFULL << ARM_AFF1_SHIFT) +#define ARM_AFF2_SHIFT 16 +#define ARM_AFF2_MASK (0xFFULL << ARM_AFF2_SHIFT) +#define ARM_AFF3_SHIFT 32 +#define ARM_AFF3_MASK (0xFFULL << ARM_AFF3_SHIFT) + +#define ARM32_AFFINITY_MASK (ARM_AFF0_MASK|ARM_AFF1_MASK|ARM_AFF2_MASK) +#define ARM64_AFFINITY_MASK \ + (ARM_AFF0_MASK|ARM_AFF1_MASK|ARM_AFF2_MASK|ARM_AFF3_MASK) #ifdef TARGET_AARCH64 int aarch64_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); int aarch64_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); - -void aarch64_cpu_do_interrupt(CPUState *cs); #endif #endif diff --git a/qemu/target-arm/cpu.c b/qemu/target-arm/cpu.c index 8b4323dd0..e48e83acb 100644 --- a/qemu/target-arm/cpu.c +++ b/qemu/target-arm/cpu.c @@ -18,6 +18,8 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "internals.h" #include "qemu-common.h" @@ -79,6 +81,27 @@ static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque) } } +static void cp_reg_check_reset(gpointer key, gpointer value, gpointer opaque) +{ + /* Purely an assertion check: we've already done reset once, + * so now check that running the reset for the cpreg doesn't + * change its value. This traps bugs where two different cpregs + * both try to reset the same state field but to different values. + */ + ARMCPRegInfo *ri = value; + ARMCPU *cpu = opaque; + uint64_t oldvalue, newvalue; + + if (ri->type & (ARM_CP_SPECIAL | ARM_CP_ALIAS | ARM_CP_NO_RAW)) { + return; + } + + oldvalue = read_raw_cp_reg(&cpu->env, ri); + cp_reg_reset(key, value, opaque); + newvalue = read_raw_cp_reg(&cpu->env, ri); + assert(oldvalue == newvalue); +} + /* CPUClass::reset() */ static void arm_cpu_reset(CPUState *s) { @@ -90,6 +113,8 @@ static void arm_cpu_reset(CPUState *s) memset(env, 0, offsetof(CPUARMState, features)); g_hash_table_foreach(cpu->cp_regs, cp_reg_reset, cpu); + g_hash_table_foreach(cpu->cp_regs, cp_reg_check_reset, cpu); + env->vfp.xregs[ARM_VFP_FPSID] = cpu->reset_fpsid; env->vfp.xregs[ARM_VFP_MVFR0] = cpu->mvfr0; env->vfp.xregs[ARM_VFP_MVFR1] = cpu->mvfr1; @@ -308,10 +333,7 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level) switch (irq) { case ARM_CPU_VIRQ: case ARM_CPU_VFIQ: - if (!arm_feature(env, ARM_FEATURE_EL2)) { - hw_error("%s: Virtual interrupt line %d with no EL2 support\n", - __func__, irq); - } + assert(arm_feature(env, ARM_FEATURE_EL2)); /* fall through */ case ARM_CPU_IRQ: case ARM_CPU_FIQ: @@ -322,7 +344,7 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level) } break; default: - hw_error("arm_cpu_set_irq: Bad interrupt line %d\n", irq); + g_assert_not_reached(); } } @@ -341,33 +363,20 @@ static void arm_cpu_kvm_set_irq(void *opaque, int irq, int level) kvm_irq |= KVM_ARM_IRQ_CPU_FIQ; break; default: - hw_error("arm_cpu_kvm_set_irq: Bad interrupt line %d\n", irq); + g_assert_not_reached(); } kvm_irq |= cs->cpu_index << KVM_ARM_IRQ_VCPU_SHIFT; kvm_set_irq(kvm_state, kvm_irq, level ? 1 : 0); #endif } -static bool arm_cpu_is_big_endian(CPUState *cs) +static bool arm_cpu_virtio_is_big_endian(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; - int cur_el; cpu_synchronize_state(cs); - - /* In 32bit guest endianness is determined by looking at CPSR's E bit */ - if (!is_a64(env)) { - return (env->uncached_cpsr & CPSR_E) ? 1 : 0; - } - - cur_el = arm_current_el(env); - - if (cur_el == 0) { - return (env->cp15.sctlr_el[1] & SCTLR_E0E) != 0; - } - - return (env->cp15.sctlr_el[cur_el] & SCTLR_EE) != 0; + return arm_cpu_data_is_big_endian(env); } #endif @@ -406,7 +415,7 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) } else { info->print_insn = print_insn_arm; } - if (env->bswap_code) { + if (bswap_code(arm_sctlr_b(env))) { #ifdef TARGET_WORDS_BIGENDIAN info->endian = BFD_ENDIAN_LITTLE; #else @@ -436,7 +445,7 @@ static void arm_cpu_initfn(Object *obj) */ Aff1 = cs->cpu_index / ARM_CPUS_PER_CLUSTER; Aff0 = cs->cpu_index % ARM_CPUS_PER_CLUSTER; - cpu->mp_affinity = (Aff1 << 8) | Aff0; + cpu->mp_affinity = (Aff1 << ARM_AFF1_SHIFT) | Aff0; #ifndef CONFIG_USER_ONLY /* Our inbound IRQ and FIQ lines */ @@ -453,6 +462,10 @@ static void arm_cpu_initfn(Object *obj) arm_gt_ptimer_cb, cpu); cpu->gt_timer[GTIMER_VIRT] = timer_new(QEMU_CLOCK_VIRTUAL, GTIMER_SCALE, arm_gt_vtimer_cb, cpu); + cpu->gt_timer[GTIMER_HYP] = timer_new(QEMU_CLOCK_VIRTUAL, GTIMER_SCALE, + arm_gt_htimer_cb, cpu); + cpu->gt_timer[GTIMER_SEC] = timer_new(QEMU_CLOCK_VIRTUAL, GTIMER_SCALE, + arm_gt_stimer_cb, cpu); qdev_init_gpio_out(DEVICE(cpu), cpu->gt_timer_outputs, ARRAY_SIZE(cpu->gt_timer_outputs)); #endif @@ -518,6 +531,15 @@ static void arm_cpu_post_init(Object *obj) */ qdev_property_add_static(DEVICE(obj), &arm_cpu_has_el3_property, &error_abort); + +#ifndef CONFIG_USER_ONLY + object_property_add_link(obj, "secure-memory", + TYPE_MEMORY_REGION, + (Object **)&cpu->secure_memory, + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + &error_abort); +#endif } if (arm_feature(&cpu->env, ARM_FEATURE_MPU)) { @@ -616,6 +638,15 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) cpu->id_aa64pfr0 &= ~0xf000; } + if (!arm_feature(env, ARM_FEATURE_EL2)) { + /* Disable the hypervisor feature bits in the processor feature + * registers if we don't have EL2. These are id_pfr1[15:12] and + * id_aa64pfr0_el1[11:8]. + */ + cpu->id_aa64pfr0 &= ~0xf00; + cpu->id_pfr1 &= ~0xf000; + } + if (!cpu->has_mpu) { unset_feature(env, ARM_FEATURE_MPU); } @@ -625,7 +656,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) uint32_t nr = cpu->pmsav7_dregion; if (nr > 0xff) { - error_setg(errp, "PMSAv7 MPU #regions invalid %" PRIu32 "\n", nr); + error_setg(errp, "PMSAv7 MPU #regions invalid %" PRIu32, nr); return; } @@ -641,6 +672,29 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) init_cpreg_list(cpu); +#ifndef CONFIG_USER_ONLY + if (cpu->has_el3) { + cs->num_ases = 2; + } else { + cs->num_ases = 1; + } + + if (cpu->has_el3) { + AddressSpace *as; + + if (!cpu->secure_memory) { + cpu->secure_memory = cs->memory; + } + as = address_space_init_shareable(cpu->secure_memory, + "cpu-secure-memory"); + cpu_address_space_init(cs, as, ARMASIdx_S); + } + cpu_address_space_init(cs, + address_space_init_shareable(cs->memory, + "cpu-memory"), + ARMASIdx_NS); +#endif + qemu_init_vcpu(cs); cpu_reset(cs); @@ -1090,6 +1144,8 @@ static void cortex_a15_initfn(Object *obj) cpu->id_pfr0 = 0x00001131; cpu->id_pfr1 = 0x00011011; cpu->id_dfr0 = 0x02010555; + cpu->pmceid0 = 0x0000000; + cpu->pmceid1 = 0x00000000; cpu->id_afr0 = 0x00000000; cpu->id_mmfr0 = 0x10201105; cpu->id_mmfr1 = 0x20000000; @@ -1369,6 +1425,17 @@ static int arm_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, } #endif +static gchar *arm_gdb_arch_name(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + if (arm_feature(env, ARM_FEATURE_IWMMXT)) { + return g_strdup("iwmmxt"); + } + return g_strdup("arm"); +} + static void arm_cpu_class_init(ObjectClass *oc, void *data) { ARMCPUClass *acc = ARM_CPU_CLASS(oc); @@ -1393,16 +1460,33 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) cc->handle_mmu_fault = arm_cpu_handle_mmu_fault; #else cc->do_interrupt = arm_cpu_do_interrupt; - cc->get_phys_page_debug = arm_cpu_get_phys_page_debug; + cc->do_unaligned_access = arm_cpu_do_unaligned_access; + cc->get_phys_page_attrs_debug = arm_cpu_get_phys_page_attrs_debug; + cc->asidx_from_attrs = arm_asidx_from_attrs; cc->vmsd = &vmstate_arm_cpu; - cc->virtio_is_big_endian = arm_cpu_is_big_endian; + cc->virtio_is_big_endian = arm_cpu_virtio_is_big_endian; + cc->write_elf64_note = arm_cpu_write_elf64_note; + cc->write_elf32_note = arm_cpu_write_elf32_note; #endif cc->gdb_num_core_regs = 26; cc->gdb_core_xml_file = "arm-core.xml"; + cc->gdb_arch_name = arm_gdb_arch_name; cc->gdb_stop_before_watchpoint = true; cc->debug_excp_handler = arm_debug_excp_handler; + cc->debug_check_watchpoint = arm_debug_check_watchpoint; cc->disas_set_info = arm_disas_set_info; + + /* + * Reason: arm_cpu_initfn() calls cpu_exec_init(), which saves + * the object in cpus -> dangling pointer after final + * object_unref(). + * + * Once this is fixed, the devices that create ARM CPUs should be + * updated not to set cannot_destroy_with_object_finalize_yet, + * unless they still screw up something else. + */ + dc->cannot_destroy_with_object_finalize_yet = true; } static void cpu_register(const ARMCPUInfo *info) diff --git a/qemu/target-arm/cpu.h b/qemu/target-arm/cpu.h index 7e89152bd..066ff678d 100644 --- a/qemu/target-arm/cpu.h +++ b/qemu/target-arm/cpu.h @@ -19,17 +19,14 @@ #ifndef CPU_ARM_H #define CPU_ARM_H -#include "config.h" #include "kvm-consts.h" #if defined(TARGET_AARCH64) /* AArch64 definitions */ # define TARGET_LONG_BITS 64 -# define ELF_MACHINE EM_AARCH64 #else # define TARGET_LONG_BITS 32 -# define ELF_MACHINE EM_ARM #endif #define TARGET_IS_BIENDIAN 1 @@ -56,6 +53,7 @@ #define EXCP_SMC 13 /* Secure Monitor Call */ #define EXCP_VIRQ 14 #define EXCP_VFIQ 15 +#define EXCP_SEMIHOST 16 /* semihosting call (A64 only) */ #define ARMV7M_EXCP_RESET 1 #define ARMV7M_EXCP_NMI 2 @@ -96,6 +94,7 @@ struct arm_boot_info; #define NB_MMU_MODES 7 +#define TARGET_INSN_START_EXTRA_WORDS 1 /* We currently assume float and double are IEEE single and double precision respectively. @@ -113,7 +112,9 @@ typedef struct ARMGenericTimer { #define GTIMER_PHYS 0 #define GTIMER_VIRT 1 -#define NUM_GTIMERS 2 +#define GTIMER_HYP 2 +#define GTIMER_SEC 3 +#define NUM_GTIMERS 4 typedef struct { uint64_t raw_tcr; @@ -170,7 +171,7 @@ typedef struct CPUARMState { uint32_t GE; /* cpsr[19:16] */ uint32_t thumb; /* cpsr[5]. 0 = arm mode, 1 = thumb mode. */ uint32_t condexec_bits; /* IT bits. cpsr[15:10,26:25]. */ - uint64_t daif; /* exception masks, in the bits they are in in PSTATE */ + uint64_t daif; /* exception masks, in the bits they are in PSTATE */ uint64_t elr_el[4]; /* AArch64 exception link regs */ uint64_t sp_el[4]; /* AArch64 banked stack pointers */ @@ -219,10 +220,12 @@ typedef struct CPUARMState { }; uint64_t ttbr1_el[4]; }; + uint64_t vttbr_el2; /* Virtualization Translation Table Base. */ /* MMU translation table base control. */ TCR tcr_el[4]; - uint32_t c2_data; /* MPU data cachable bits. */ - uint32_t c2_insn; /* MPU instruction cachable bits. */ + TCR vtcr_el2; /* Virtualization Translation Control. */ + uint32_t c2_data; /* MPU data cacheable bits. */ + uint32_t c2_insn; /* MPU instruction cacheable bits. */ union { /* MMU domain access control register * MPU write buffer control. */ @@ -275,6 +278,7 @@ typedef struct CPUARMState { }; uint64_t far_el[4]; }; + uint64_t hpfar_el2; union { /* Translation result. */ struct { uint64_t _unused_par_0; @@ -358,6 +362,8 @@ typedef struct CPUARMState { }; uint64_t c14_cntfrq; /* Counter Frequency register */ uint64_t c14_cntkctl; /* Timer Control register */ + uint32_t cnthctl_el2; /* Counter/Timer Hyp Control register */ + uint64_t cntvoff_el2; /* Counter Virtual Offset register */ ARMGenericTimer c14_timer[NUM_GTIMERS]; uint32_t c15_cpar; /* XScale Coprocessor Access Register */ uint32_t c15_ticonfig; /* TI925T configuration byte. */ @@ -373,11 +379,16 @@ typedef struct CPUARMState { uint64_t dbgwvr[16]; /* watchpoint value registers */ uint64_t dbgwcr[16]; /* watchpoint control registers */ uint64_t mdscr_el1; + uint64_t oslsr_el1; /* OS Lock Status */ + uint64_t mdcr_el2; + uint64_t mdcr_el3; /* If the counter is enabled, this stores the last time the counter * was reset. Otherwise it stores the counter value */ uint64_t c15_ccnt; uint64_t pmccfiltr_el0; /* Performance Monitor Filter Register */ + uint64_t vpidr_el2; /* Virtualization Processor ID Register */ + uint64_t vmpidr_el2; /* Virtualization Multiprocessor ID Register */ } cp15; struct { @@ -467,9 +478,6 @@ typedef struct CPUARMState { uint32_t cregs[16]; } iwmmxt; - /* For mixed endian mode. */ - bool bswap_code; - #if defined(CONFIG_USER_ONLY) /* For usermode syscall translation. */ int eabi; @@ -500,7 +508,7 @@ typedef struct CPUARMState { ARMCPU *cpu_arm_init(const char *cpu_model); int cpu_arm_exec(CPUState *cpu); -uint32_t do_arm_semihosting(CPUARMState *env); +target_ulong do_arm_semihosting(CPUARMState *env); void aarch64_sync_32_to_64(CPUARMState *env); void aarch64_sync_64_to_32(CPUARMState *env); @@ -583,6 +591,22 @@ void pmccntr_sync(CPUARMState *env); #define CPTR_TTA (1U << 20) #define CPTR_TFP (1U << 10) +#define MDCR_EPMAD (1U << 21) +#define MDCR_EDAD (1U << 20) +#define MDCR_SPME (1U << 17) +#define MDCR_SDD (1U << 16) +#define MDCR_SPD (3U << 14) +#define MDCR_TDRA (1U << 11) +#define MDCR_TDOSA (1U << 10) +#define MDCR_TDA (1U << 9) +#define MDCR_TDE (1U << 8) +#define MDCR_HPME (1U << 7) +#define MDCR_TPM (1U << 6) +#define MDCR_TPMCR (1U << 5) + +/* Not all of the MDCR_EL3 bits are present in the 32-bit SDCR */ +#define SDCR_VALID_MASK (MDCR_EPMAD | MDCR_EDAD | MDCR_SPME | MDCR_SPD) + #define CPSR_M (0x1fU) #define CPSR_T (1U << 5) #define CPSR_F (1U << 6) @@ -695,8 +719,17 @@ static inline void pstate_write(CPUARMState *env, uint32_t val) /* Return the current CPSR value. */ uint32_t cpsr_read(CPUARMState *env); -/* Set the CPSR. Note that some bits of mask must be all-set or all-clear. */ -void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask); + +typedef enum CPSRWriteType { + CPSRWriteByInstr = 0, /* from guest MSR or CPS */ + CPSRWriteExceptionReturn = 1, /* from guest exception return insn */ + CPSRWriteRaw = 2, /* trust values, do not switch reg banks */ + CPSRWriteByGDBStub = 3, /* from the GDB stub */ +} CPSRWriteType; + +/* Set the CPSR. Note that some bits of mask must be all-set or all-clear.*/ +void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, + CPSRWriteType write_type); /* Return the current xPSR value. */ static inline uint32_t xpsr_read(CPUARMState *env) @@ -920,7 +953,7 @@ static inline bool arm_is_secure_below_el3(CPUARMState *env) if (arm_feature(env, ARM_FEATURE_EL3)) { return !(env->cp15.scr_el3 & SCR_NS); } else { - /* If EL2 is not supported then the secure state is implementation + /* If EL3 is not supported then the secure state is implementation * defined, in which case QEMU defaults to non-secure. */ return false; @@ -958,18 +991,33 @@ static inline bool arm_is_secure(CPUARMState *env) /* Return true if the specified exception level is running in AArch64 state. */ static inline bool arm_el_is_aa64(CPUARMState *env, int el) { - /* We don't currently support EL2, and this isn't valid for EL0 - * (if we're in EL0, is_a64() is what you want, and if we're not in EL0 - * then the state of EL0 isn't well defined.) + /* This isn't valid for EL0 (if we're in EL0, is_a64() is what you want, + * and if we're not in EL0 then the state of EL0 isn't well defined.) */ - assert(el == 1 || el == 3); + assert(el >= 1 && el <= 3); + bool aa64 = arm_feature(env, ARM_FEATURE_AARCH64); - /* AArch64-capable CPUs always run with EL1 in AArch64 mode. This - * is a QEMU-imposed simplification which we may wish to change later. - * If we in future support EL2 and/or EL3, then the state of lower - * exception levels is controlled by the HCR.RW and SCR.RW bits. + /* The highest exception level is always at the maximum supported + * register width, and then lower levels have a register width controlled + * by bits in the SCR or HCR registers. */ - return arm_feature(env, ARM_FEATURE_AARCH64); + if (el == 3) { + return aa64; + } + + if (arm_feature(env, ARM_FEATURE_EL3)) { + aa64 = aa64 && (env->cp15.scr_el3 & SCR_RW); + } + + if (el == 2) { + return aa64; + } + + if (arm_feature(env, ARM_FEATURE_EL2) && !arm_is_secure_below_el3(env)) { + aa64 = aa64 && (env->cp15.hcr_el2 & HCR_RW); + } + + return aa64; } /* Function for determing whether guest cp register reads and writes should @@ -1008,11 +1056,11 @@ static inline bool access_secure_reg(CPUARMState *env) */ #define A32_BANKED_CURRENT_REG_GET(_env, _regname) \ A32_BANKED_REG_GET((_env), _regname, \ - ((!arm_el_is_aa64((_env), 3) && arm_is_secure(_env)))) + (arm_is_secure(_env) && !arm_el_is_aa64((_env), 3))) #define A32_BANKED_CURRENT_REG_SET(_env, _regname, _val) \ A32_BANKED_REG_SET((_env), _regname, \ - ((!arm_el_is_aa64((_env), 3) && arm_is_secure(_env))), \ + (arm_is_secure(_env) && !arm_el_is_aa64((_env), 3)), \ (_val)) void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf); @@ -1228,6 +1276,18 @@ static inline bool cptype_valid(int cptype) #define PL1_RW (PL1_R | PL1_W) #define PL0_RW (PL0_R | PL0_W) +/* Return the highest implemented Exception Level */ +static inline int arm_highest_el(CPUARMState *env) +{ + if (arm_feature(env, ARM_FEATURE_EL3)) { + return 3; + } + if (arm_feature(env, ARM_FEATURE_EL2)) { + return 2; + } + return 1; +} + /* Return the current Exception Level (as per ARMv8; note that this differs * from the ARMv7 Privilege Level). */ @@ -1280,6 +1340,14 @@ typedef enum CPAccessResult { /* As CP_ACCESS_TRAP, but for traps directly to EL2 or EL3 */ CP_ACCESS_TRAP_EL2 = 3, CP_ACCESS_TRAP_EL3 = 4, + /* As CP_ACCESS_UNCATEGORIZED, but for traps directly to EL2 or EL3 */ + CP_ACCESS_TRAP_UNCATEGORIZED_EL2 = 5, + CP_ACCESS_TRAP_UNCATEGORIZED_EL3 = 6, + /* Access fails and results in an exception syndrome for an FP access, + * trapped directly to EL2 or EL3 + */ + CP_ACCESS_TRAP_FP_EL2 = 7, + CP_ACCESS_TRAP_FP_EL3 = 8, } CPAccessResult; /* Access functions for coprocessor registers. These cannot fail and @@ -1289,7 +1357,9 @@ typedef uint64_t CPReadFn(CPUARMState *env, const ARMCPRegInfo *opaque); typedef void CPWriteFn(CPUARMState *env, const ARMCPRegInfo *opaque, uint64_t value); /* Access permission check functions for coprocessor registers. */ -typedef CPAccessResult CPAccessFn(CPUARMState *env, const ARMCPRegInfo *opaque); +typedef CPAccessResult CPAccessFn(CPUARMState *env, + const ARMCPRegInfo *opaque, + bool isread); /* Hook function for register reset */ typedef void CPResetFn(CPUARMState *env, const ARMCPRegInfo *opaque); @@ -1445,6 +1515,9 @@ static inline bool cp_access_ok(int current_el, return (ri->access >> ((current_el * 2) + isread)) & 1; } +/* Raw read of a coprocessor register (as needed for migration, etc) */ +uint64_t read_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri); + /** * write_list_to_cpustate * @cpu: ARMCPU @@ -1477,7 +1550,7 @@ bool write_list_to_cpustate(ARMCPU *cpu); */ bool write_cpustate_to_list(ARMCPU *cpu); -/* Does the core conform to the the "MicroController" profile. e.g. Cortex-M3. +/* Does the core conform to the "MicroController" profile. e.g. Cortex-M3. Note the M in older cores (eg. ARM7TDMI) stands for Multiply. These are conventional cores (ie. Application or Realtime profile). */ @@ -1509,8 +1582,6 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, CPUARMState *env = cs->env_ptr; unsigned int cur_el = arm_current_el(env); bool secure = arm_is_secure(env); - uint32_t scr; - uint32_t hcr; bool pstate_unmasked; int8_t unmasked = 0; @@ -1524,31 +1595,10 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, switch (excp_idx) { case EXCP_FIQ: - /* If FIQs are routed to EL3 or EL2 then there are cases where we - * override the CPSR.F in determining if the exception is masked or - * not. If neither of these are set then we fall back to the CPSR.F - * setting otherwise we further assess the state below. - */ - hcr = (env->cp15.hcr_el2 & HCR_FMO); - scr = (env->cp15.scr_el3 & SCR_FIQ); - - /* When EL3 is 32-bit, the SCR.FW bit controls whether the CPSR.F bit - * masks FIQ interrupts when taken in non-secure state. If SCR.FW is - * set then FIQs can be masked by CPSR.F when non-secure but only - * when FIQs are only routed to EL3. - */ - scr &= !((env->cp15.scr_el3 & SCR_FW) && !hcr); pstate_unmasked = !(env->daif & PSTATE_F); break; case EXCP_IRQ: - /* When EL3 execution state is 32-bit, if HCR.IMO is set then we may - * override the CPSR.I masking when in non-secure state. The SCR.IRQ - * setting has already been taken into consideration when setting the - * target EL, so it does not have a further affect here. - */ - hcr = (env->cp15.hcr_el2 & HCR_IMO); - scr = false; pstate_unmasked = !(env->daif & PSTATE_I); break; @@ -1573,8 +1623,58 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, * interrupt. */ if ((target_el > cur_el) && (target_el != 1)) { - if (arm_el_is_aa64(env, 3) || ((scr || hcr) && (!secure))) { - unmasked = 1; + /* Exceptions targeting a higher EL may not be maskable */ + if (arm_feature(env, ARM_FEATURE_AARCH64)) { + /* 64-bit masking rules are simple: exceptions to EL3 + * can't be masked, and exceptions to EL2 can only be + * masked from Secure state. The HCR and SCR settings + * don't affect the masking logic, only the interrupt routing. + */ + if (target_el == 3 || !secure) { + unmasked = 1; + } + } else { + /* The old 32-bit-only environment has a more complicated + * masking setup. HCR and SCR bits not only affect interrupt + * routing but also change the behaviour of masking. + */ + bool hcr, scr; + + switch (excp_idx) { + case EXCP_FIQ: + /* If FIQs are routed to EL3 or EL2 then there are cases where + * we override the CPSR.F in determining if the exception is + * masked or not. If neither of these are set then we fall back + * to the CPSR.F setting otherwise we further assess the state + * below. + */ + hcr = (env->cp15.hcr_el2 & HCR_FMO); + scr = (env->cp15.scr_el3 & SCR_FIQ); + + /* When EL3 is 32-bit, the SCR.FW bit controls whether the + * CPSR.F bit masks FIQ interrupts when taken in non-secure + * state. If SCR.FW is set then FIQs can be masked by CPSR.F + * when non-secure but only when FIQs are only routed to EL3. + */ + scr = scr && !((env->cp15.scr_el3 & SCR_FW) && !hcr); + break; + case EXCP_IRQ: + /* When EL3 execution state is 32-bit, if HCR.IMO is set then + * we may override the CPSR.I masking when in non-secure state. + * The SCR.IRQ setting has already been taken into consideration + * when setting the target EL, so it does not have a further + * affect here. + */ + hcr = (env->cp15.hcr_el2 & HCR_IMO); + scr = false; + break; + default: + g_assert_not_reached(); + } + + if ((scr || hcr) && !secure) { + unmasked = 1; + } } } @@ -1587,7 +1687,6 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, #define cpu_init(cpu_model) CPU(cpu_arm_init(cpu_model)) #define cpu_exec cpu_arm_exec -#define cpu_gen_code cpu_arm_gen_code #define cpu_signal_handler cpu_arm_signal_handler #define cpu_list arm_cpu_list @@ -1667,7 +1766,7 @@ static inline int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx) } /* Determine the current mmu_idx to use for normal loads/stores */ -static inline int cpu_mmu_index(CPUARMState *env) +static inline int cpu_mmu_index(CPUARMState *env, bool ifetch) { int el = arm_current_el(env); @@ -1677,16 +1776,43 @@ static inline int cpu_mmu_index(CPUARMState *env) return el; } -/* Return the Exception Level targeted by debug exceptions; - * currently always EL1 since we don't implement EL2 or EL3. - */ +/* Indexes used when registering address spaces with cpu_address_space_init */ +typedef enum ARMASIdx { + ARMASIdx_NS = 0, + ARMASIdx_S = 1, +} ARMASIdx; + +/* Return the Exception Level targeted by debug exceptions. */ static inline int arm_debug_target_el(CPUARMState *env) { - return 1; + bool secure = arm_is_secure(env); + bool route_to_el2 = false; + + if (arm_feature(env, ARM_FEATURE_EL2) && !secure) { + route_to_el2 = env->cp15.hcr_el2 & HCR_TGE || + env->cp15.mdcr_el2 & (1 << 8); + } + + if (route_to_el2) { + return 2; + } else if (arm_feature(env, ARM_FEATURE_EL3) && + !arm_el_is_aa64(env, 3) && secure) { + return 3; + } else { + return 1; + } } static inline bool aa64_generate_debug_exceptions(CPUARMState *env) { + if (arm_is_secure(env)) { + /* MDCR_EL3.SDD disables debug events from Secure state */ + if (extract32(env->cp15.mdcr_el3, 16, 1) != 0 + || arm_current_el(env) == 3) { + return false; + } + } + if (arm_current_el(env) == arm_debug_target_el(env)) { if ((extract32(env->cp15.mdscr_el1, 13, 1) == 0) || (env->daif & PSTATE_D)) { @@ -1698,10 +1824,42 @@ static inline bool aa64_generate_debug_exceptions(CPUARMState *env) static inline bool aa32_generate_debug_exceptions(CPUARMState *env) { - if (arm_current_el(env) == 0 && arm_el_is_aa64(env, 1)) { + int el = arm_current_el(env); + + if (el == 0 && arm_el_is_aa64(env, 1)) { return aa64_generate_debug_exceptions(env); } - return arm_current_el(env) != 2; + + if (arm_is_secure(env)) { + int spd; + + if (el == 0 && (env->cp15.sder & 1)) { + /* SDER.SUIDEN means debug exceptions from Secure EL0 + * are always enabled. Otherwise they are controlled by + * SDCR.SPD like those from other Secure ELs. + */ + return true; + } + + spd = extract32(env->cp15.mdcr_el3, 14, 2); + switch (spd) { + case 1: + /* SPD == 0b01 is reserved, but behaves as 0b00. */ + case 0: + /* For 0b00 we return true if external secure invasive debug + * is enabled. On real hardware this is controlled by external + * signals to the core. QEMU always permits debug, and behaves + * as if DBGEN, SPIDEN, NIDEN and SPNIDEN are all tied high. + */ + return true; + case 2: + return false; + case 3: + return true; + } + } + + return el != 2; } /* Return true if debugging exceptions are currently enabled. @@ -1737,6 +1895,53 @@ static inline bool arm_singlestep_active(CPUARMState *env) && arm_generate_debug_exceptions(env); } +static inline bool arm_sctlr_b(CPUARMState *env) +{ + return + /* We need not implement SCTLR.ITD in user-mode emulation, so + * let linux-user ignore the fact that it conflicts with SCTLR_B. + * This lets people run BE32 binaries with "-cpu any". + */ +#ifndef CONFIG_USER_ONLY + !arm_feature(env, ARM_FEATURE_V7) && +#endif + (env->cp15.sctlr_el[1] & SCTLR_B) != 0; +} + +/* Return true if the processor is in big-endian mode. */ +static inline bool arm_cpu_data_is_big_endian(CPUARMState *env) +{ + int cur_el; + + /* In 32bit endianness is determined by looking at CPSR's E bit */ + if (!is_a64(env)) { + return +#ifdef CONFIG_USER_ONLY + /* In system mode, BE32 is modelled in line with the + * architecture (as word-invariant big-endianness), where loads + * and stores are done little endian but from addresses which + * are adjusted by XORing with the appropriate constant. So the + * endianness to use for the raw data access is not affected by + * SCTLR.B. + * In user mode, however, we model BE32 as byte-invariant + * big-endianness (because user-only code cannot tell the + * difference), and so we need to use a data access endianness + * that depends on SCTLR.B. + */ + arm_sctlr_b(env) || +#endif + ((env->uncached_cpsr & CPSR_E) ? 1 : 0); + } + + cur_el = arm_current_el(env); + + if (cur_el == 0) { + return (env->cp15.sctlr_el[1] & SCTLR_E0E) != 0; + } + + return (env->cp15.sctlr_el[cur_el] & SCTLR_EE) != 0; +} + #include "exec/cpu-all.h" /* Bit usage in the TB flags field: bit 31 indicates whether we are @@ -1767,8 +1972,8 @@ static inline bool arm_singlestep_active(CPUARMState *env) #define ARM_TBFLAG_VFPEN_MASK (1 << ARM_TBFLAG_VFPEN_SHIFT) #define ARM_TBFLAG_CONDEXEC_SHIFT 8 #define ARM_TBFLAG_CONDEXEC_MASK (0xff << ARM_TBFLAG_CONDEXEC_SHIFT) -#define ARM_TBFLAG_BSWAP_CODE_SHIFT 16 -#define ARM_TBFLAG_BSWAP_CODE_MASK (1 << ARM_TBFLAG_BSWAP_CODE_SHIFT) +#define ARM_TBFLAG_SCTLR_B_SHIFT 16 +#define ARM_TBFLAG_SCTLR_B_MASK (1 << ARM_TBFLAG_SCTLR_B_SHIFT) /* We store the bottom two bits of the CPAR as TB flags and handle * checks on the other bits at runtime */ @@ -1780,6 +1985,8 @@ static inline bool arm_singlestep_active(CPUARMState *env) */ #define ARM_TBFLAG_NS_SHIFT 19 #define ARM_TBFLAG_NS_MASK (1 << ARM_TBFLAG_NS_SHIFT) +#define ARM_TBFLAG_BE_DATA_SHIFT 20 +#define ARM_TBFLAG_BE_DATA_MASK (1 << ARM_TBFLAG_BE_DATA_SHIFT) /* Bit usage when in AArch64 state: currently we have no A64 specific bits */ @@ -1804,12 +2011,34 @@ static inline bool arm_singlestep_active(CPUARMState *env) (((F) & ARM_TBFLAG_VFPEN_MASK) >> ARM_TBFLAG_VFPEN_SHIFT) #define ARM_TBFLAG_CONDEXEC(F) \ (((F) & ARM_TBFLAG_CONDEXEC_MASK) >> ARM_TBFLAG_CONDEXEC_SHIFT) -#define ARM_TBFLAG_BSWAP_CODE(F) \ - (((F) & ARM_TBFLAG_BSWAP_CODE_MASK) >> ARM_TBFLAG_BSWAP_CODE_SHIFT) +#define ARM_TBFLAG_SCTLR_B(F) \ + (((F) & ARM_TBFLAG_SCTLR_B_MASK) >> ARM_TBFLAG_SCTLR_B_SHIFT) #define ARM_TBFLAG_XSCALE_CPAR(F) \ (((F) & ARM_TBFLAG_XSCALE_CPAR_MASK) >> ARM_TBFLAG_XSCALE_CPAR_SHIFT) #define ARM_TBFLAG_NS(F) \ (((F) & ARM_TBFLAG_NS_MASK) >> ARM_TBFLAG_NS_SHIFT) +#define ARM_TBFLAG_BE_DATA(F) \ + (((F) & ARM_TBFLAG_BE_DATA_MASK) >> ARM_TBFLAG_BE_DATA_SHIFT) + +static inline bool bswap_code(bool sctlr_b) +{ +#ifdef CONFIG_USER_ONLY + /* BE8 (SCTLR.B = 0, TARGET_WORDS_BIGENDIAN = 1) is mixed endian. + * The invalid combination SCTLR.B=1/CPSR.E=1/TARGET_WORDS_BIGENDIAN=0 + * would also end up as a mixed-endian mode with BE code, LE data. + */ + return +#ifdef TARGET_WORDS_BIGENDIAN + 1 ^ +#endif + sctlr_b; +#else + /* All code access in ARM is little endian, and there are no loaders + * doing swaps that need to be reversed + */ + return 0; +#endif +} /* Return the exception level to which FP-disabled exceptions should * be taken, or 0 if FP is enabled. @@ -1876,6 +2105,17 @@ static inline int fp_exception_el(CPUARMState *env) return 0; } +#ifdef CONFIG_USER_ONLY +static inline bool arm_cpu_bswap_data(CPUARMState *env) +{ + return +#ifdef TARGET_WORDS_BIGENDIAN + 1 ^ +#endif + arm_cpu_data_is_big_endian(env); +} +#endif + static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, target_ulong *cs_base, int *flags) { @@ -1888,7 +2128,7 @@ static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, | (env->vfp.vec_len << ARM_TBFLAG_VECLEN_SHIFT) | (env->vfp.vec_stride << ARM_TBFLAG_VECSTRIDE_SHIFT) | (env->condexec_bits << ARM_TBFLAG_CONDEXEC_SHIFT) - | (env->bswap_code << ARM_TBFLAG_BSWAP_CODE_SHIFT); + | (arm_sctlr_b(env) << ARM_TBFLAG_SCTLR_B_SHIFT); if (!(access_secure_reg(env))) { *flags |= ARM_TBFLAG_NS_MASK; } @@ -1900,7 +2140,7 @@ static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, << ARM_TBFLAG_XSCALE_CPAR_SHIFT); } - *flags |= (cpu_mmu_index(env) << ARM_TBFLAG_MMUIDX_SHIFT); + *flags |= (cpu_mmu_index(env, false) << ARM_TBFLAG_MMUIDX_SHIFT); /* The SS_ACTIVE and PSTATE_SS bits correspond to the state machine * states defined in the ARM ARM for software singlestep: * SS_ACTIVE PSTATE.SS State @@ -1920,6 +2160,9 @@ static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, } } } + if (arm_cpu_data_is_big_endian(env)) { + *flags |= ARM_TBFLAG_BE_DATA_MASK; + } *flags |= fp_exception_el(env) << ARM_TBFLAG_FPEXC_EL_SHIFT; *cs_base = 0; @@ -1933,4 +2176,21 @@ enum { QEMU_PSCI_CONDUIT_HVC = 2, }; +#ifndef CONFIG_USER_ONLY +/* Return the address space index to use for a memory access */ +static inline int arm_asidx_from_attrs(CPUState *cs, MemTxAttrs attrs) +{ + return attrs.secure ? ARMASIdx_S : ARMASIdx_NS; +} + +/* Return the AddressSpace to use for a memory access + * (which depends on whether the access is S or NS, and whether + * the board gave us a separate AddressSpace for S accesses). + */ +static inline AddressSpace *arm_addressspace(CPUState *cs, MemTxAttrs attrs) +{ + return cpu_get_address_space(cs, arm_asidx_from_attrs(cs, attrs)); +} +#endif + #endif diff --git a/qemu/target-arm/cpu64.c b/qemu/target-arm/cpu64.c index 63c8b1cfa..1635debc1 100644 --- a/qemu/target-arm/cpu64.c +++ b/qemu/target-arm/cpu64.c @@ -18,6 +18,8 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "qemu-common.h" #if !defined(CONFIG_USER_ONLY) @@ -108,6 +110,7 @@ static void aarch64_a57_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8_SHA256); set_feature(&cpu->env, ARM_FEATURE_V8_PMULL); set_feature(&cpu->env, ARM_FEATURE_CRC); + set_feature(&cpu->env, ARM_FEATURE_EL3); cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A57; cpu->midr = 0x411fd070; cpu->revidr = 0x00000000; @@ -133,6 +136,8 @@ static void aarch64_a57_initfn(Object *obj) cpu->id_isar5 = 0x00011121; cpu->id_aa64pfr0 = 0x00002222; cpu->id_aa64dfr0 = 0x10305106; + cpu->pmceid0 = 0x00000000; + cpu->pmceid1 = 0x00000000; cpu->id_aa64isar0 = 0x00011120; cpu->id_aa64mmfr0 = 0x00001124; cpu->dbgdidr = 0x3516d000; @@ -160,6 +165,7 @@ static void aarch64_a53_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8_SHA256); set_feature(&cpu->env, ARM_FEATURE_V8_PMULL); set_feature(&cpu->env, ARM_FEATURE_CRC); + set_feature(&cpu->env, ARM_FEATURE_EL3); cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A53; cpu->midr = 0x410fd034; cpu->revidr = 0x00000000; @@ -286,19 +292,22 @@ static void aarch64_cpu_set_pc(CPUState *cs, vaddr value) } } +static gchar *aarch64_gdb_arch_name(CPUState *cs) +{ + return g_strdup("aarch64"); +} + static void aarch64_cpu_class_init(ObjectClass *oc, void *data) { CPUClass *cc = CPU_CLASS(oc); -#if !defined(CONFIG_USER_ONLY) - cc->do_interrupt = aarch64_cpu_do_interrupt; -#endif cc->cpu_exec_interrupt = arm_cpu_exec_interrupt; cc->set_pc = aarch64_cpu_set_pc; cc->gdb_read_register = aarch64_cpu_gdb_read_register; cc->gdb_write_register = aarch64_cpu_gdb_write_register; cc->gdb_num_core_regs = 34; cc->gdb_core_xml_file = "aarch64-core.xml"; + cc->gdb_arch_name = aarch64_gdb_arch_name; } static void aarch64_cpu_register(const ARMCPUInfo *info) diff --git a/qemu/target-arm/crypto_helper.c b/qemu/target-arm/crypto_helper.c index 5d2283806..3b6df3f41 100644 --- a/qemu/target-arm/crypto_helper.c +++ b/qemu/target-arm/crypto_helper.c @@ -9,7 +9,7 @@ * version 2 of the License, or (at your option) any later version. */ -#include +#include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" diff --git a/qemu/target-arm/gdbstub.c b/qemu/target-arm/gdbstub.c index 1c3439654..3ba9aadd4 100644 --- a/qemu/target-arm/gdbstub.c +++ b/qemu/target-arm/gdbstub.c @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "exec/gdbstub.h" @@ -94,7 +94,7 @@ int arm_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return 4; case 25: /* CPSR */ - cpsr_write(env, tmp, 0xffffffff); + cpsr_write(env, tmp, 0xffffffff, CPSRWriteByGDBStub); return 4; } /* Unknown register. */ diff --git a/qemu/target-arm/gdbstub64.c b/qemu/target-arm/gdbstub64.c index 8f3b8d177..634c6bc6f 100644 --- a/qemu/target-arm/gdbstub64.c +++ b/qemu/target-arm/gdbstub64.c @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "exec/gdbstub.h" diff --git a/qemu/target-arm/helper-a64.c b/qemu/target-arm/helper-a64.c index 08c95a3f5..c7bfb4d8f 100644 --- a/qemu/target-arm/helper-a64.c +++ b/qemu/target-arm/helper-a64.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/gdbstub.h" #include "exec/helper-proto.h" @@ -70,20 +71,7 @@ uint32_t HELPER(clz32)(uint32_t x) uint64_t HELPER(rbit64)(uint64_t x) { - /* assign the correct byte position */ - x = bswap64(x); - - /* assign the correct nibble position */ - x = ((x & 0xf0f0f0f0f0f0f0f0ULL) >> 4) - | ((x & 0x0f0f0f0f0f0f0f0fULL) << 4); - - /* assign the correct bit position */ - x = ((x & 0x8888888888888888ULL) >> 3) - | ((x & 0x4444444444444444ULL) >> 1) - | ((x & 0x2222222222222222ULL) << 1) - | ((x & 0x1111111111111111ULL) << 3); - - return x; + return revbit64(x); } /* Convert a softfloat float_relation_ (as returned by @@ -455,92 +443,3 @@ uint64_t HELPER(crc32c_64)(uint64_t acc, uint64_t val, uint32_t bytes) /* Linux crc32c converts the output to one's complement. */ return crc32c(acc, buf, bytes) ^ 0xffffffff; } - -#if !defined(CONFIG_USER_ONLY) - -/* Handle a CPU exception. */ -void aarch64_cpu_do_interrupt(CPUState *cs) -{ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - unsigned int new_el = env->exception.target_el; - target_ulong addr = env->cp15.vbar_el[new_el]; - unsigned int new_mode = aarch64_pstate_mode(new_el, true); - - if (arm_current_el(env) < new_el) { - if (env->aarch64) { - addr += 0x400; - } else { - addr += 0x600; - } - } else if (pstate_read(env) & PSTATE_SP) { - addr += 0x200; - } - - arm_log_exception(cs->exception_index); - qemu_log_mask(CPU_LOG_INT, "...from EL%d\n", arm_current_el(env)); - if (qemu_loglevel_mask(CPU_LOG_INT) - && !excp_is_internal(cs->exception_index)) { - qemu_log_mask(CPU_LOG_INT, "...with ESR 0x%" PRIx32 "\n", - env->exception.syndrome); - } - - if (arm_is_psci_call(cpu, cs->exception_index)) { - arm_handle_psci_call(cpu); - qemu_log_mask(CPU_LOG_INT, "...handled as PSCI call\n"); - return; - } - - switch (cs->exception_index) { - case EXCP_PREFETCH_ABORT: - case EXCP_DATA_ABORT: - env->cp15.far_el[new_el] = env->exception.vaddress; - qemu_log_mask(CPU_LOG_INT, "...with FAR 0x%" PRIx64 "\n", - env->cp15.far_el[new_el]); - /* fall through */ - case EXCP_BKPT: - case EXCP_UDEF: - case EXCP_SWI: - case EXCP_HVC: - case EXCP_HYP_TRAP: - case EXCP_SMC: - env->cp15.esr_el[new_el] = env->exception.syndrome; - break; - case EXCP_IRQ: - case EXCP_VIRQ: - addr += 0x80; - break; - case EXCP_FIQ: - case EXCP_VFIQ: - addr += 0x100; - break; - default: - cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index); - } - - if (is_a64(env)) { - env->banked_spsr[aarch64_banked_spsr_index(new_el)] = pstate_read(env); - aarch64_save_sp(env, arm_current_el(env)); - env->elr_el[new_el] = env->pc; - } else { - env->banked_spsr[aarch64_banked_spsr_index(new_el)] = cpsr_read(env); - if (!env->thumb) { - env->cp15.esr_el[new_el] |= 1 << 25; - } - env->elr_el[new_el] = env->regs[15]; - - aarch64_sync_32_to_64(env); - - env->condexec_bits = 0; - } - qemu_log_mask(CPU_LOG_INT, "...with ELR 0x%" PRIx64 "\n", - env->elr_el[new_el]); - - pstate_write(env, PSTATE_DAIF | new_mode); - env->aarch64 = 1; - aarch64_restore_sp(env, new_el); - - env->pc = addr; - cs->interrupt_request |= CPU_INTERRUPT_EXITTB; -} -#endif diff --git a/qemu/target-arm/helper.c b/qemu/target-arm/helper.c index 01f0d0dac..09638b2e7 100644 --- a/qemu/target-arm/helper.c +++ b/qemu/target-arm/helper.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "cpu.h" #include "internals.h" #include "exec/gdbstub.h" @@ -11,12 +12,22 @@ #include "arm_ldst.h" #include /* For crc32 */ #include "exec/semihost.h" +#include "sysemu/kvm.h" + +#define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */ #ifndef CONFIG_USER_ONLY -static inline bool get_phys_addr(CPUARMState *env, target_ulong address, - int access_type, ARMMMUIdx mmu_idx, - hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot, - target_ulong *page_size, uint32_t *fsr); +static bool get_phys_addr(CPUARMState *env, target_ulong address, + int access_type, ARMMMUIdx mmu_idx, + hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot, + target_ulong *page_size, uint32_t *fsr, + ARMMMUFaultInfo *fi); + +static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, + int access_type, ARMMMUIdx mmu_idx, + hwaddr *phys_ptr, MemTxAttrs *txattrs, int *prot, + target_ulong *page_size_ptr, uint32_t *fsr, + ARMMMUFaultInfo *fi); /* Definitions for the PMCCNTR and PMCR registers */ #define PMCRD 0x8 @@ -144,7 +155,7 @@ static void *raw_ptr(CPUARMState *env, const ARMCPRegInfo *ri) return (char *)env + ri->fieldoffset; } -static uint64_t read_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri) +uint64_t read_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri) { /* Raw read of a coprocessor register (as needed for migration, etc). */ if (ri->type & ARM_CP_CONST) { @@ -325,6 +336,127 @@ void init_cpreg_list(ARMCPU *cpu) g_list_free(keys); } +/* + * Some registers are not accessible if EL3.NS=0 and EL3 is using AArch32 but + * they are accessible when EL3 is using AArch64 regardless of EL3.NS. + * + * access_el3_aa32ns: Used to check AArch32 register views. + * access_el3_aa32ns_aa64any: Used to check both AArch32/64 register views. + */ +static CPAccessResult access_el3_aa32ns(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + bool secure = arm_is_secure_below_el3(env); + + assert(!arm_el_is_aa64(env, 3)); + if (secure) { + return CP_ACCESS_TRAP_UNCATEGORIZED; + } + return CP_ACCESS_OK; +} + +static CPAccessResult access_el3_aa32ns_aa64any(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + if (!arm_el_is_aa64(env, 3)) { + return access_el3_aa32ns(env, ri, isread); + } + return CP_ACCESS_OK; +} + +/* Some secure-only AArch32 registers trap to EL3 if used from + * Secure EL1 (but are just ordinary UNDEF in other non-EL3 contexts). + * Note that an access from Secure EL1 can only happen if EL3 is AArch64. + * We assume that the .access field is set to PL1_RW. + */ +static CPAccessResult access_trap_aa32s_el1(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 3) { + return CP_ACCESS_OK; + } + if (arm_is_secure_below_el3(env)) { + return CP_ACCESS_TRAP_EL3; + } + /* This will be EL1 NS and EL2 NS, which just UNDEF */ + return CP_ACCESS_TRAP_UNCATEGORIZED; +} + +/* Check for traps to "powerdown debug" registers, which are controlled + * by MDCR.TDOSA + */ +static CPAccessResult access_tdosa(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + int el = arm_current_el(env); + + if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TDOSA) + && !arm_is_secure_below_el3(env)) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDOSA)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +/* Check for traps to "debug ROM" registers, which are controlled + * by MDCR_EL2.TDRA for EL2 but by the more general MDCR_EL3.TDA for EL3. + */ +static CPAccessResult access_tdra(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + int el = arm_current_el(env); + + if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TDRA) + && !arm_is_secure_below_el3(env)) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +/* Check for traps to general debug registers, which are controlled + * by MDCR_EL2.TDA for EL2 and MDCR_EL3.TDA for EL3. + */ +static CPAccessResult access_tda(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + int el = arm_current_el(env); + + if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TDA) + && !arm_is_secure_below_el3(env)) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +/* Check for traps to performance monitor registers, which are controlled + * by MDCR_EL2.TPM for EL2 and MDCR_EL3.TPM for EL3. + */ +static CPAccessResult access_tpm(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + int el = arm_current_el(env); + + if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TPM) + && !arm_is_secure_below_el3(env)) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TPM)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + static void dacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { ARMCPU *cpu = arm_env_get_cpu(env); @@ -595,7 +727,8 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, env->cp15.cpacr_el1 = value; } -static CPAccessResult cpacr_access(CPUARMState *env, const ARMCPRegInfo *ri) +static CPAccessResult cpacr_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { if (arm_feature(env, ARM_FEATURE_V8)) { /* Check if CPACR accesses are to be trapped to EL2 */ @@ -612,7 +745,8 @@ static CPAccessResult cpacr_access(CPUARMState *env, const ARMCPRegInfo *ri) return CP_ACCESS_OK; } -static CPAccessResult cptr_access(CPUARMState *env, const ARMCPRegInfo *ri) +static CPAccessResult cptr_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { /* Check if CPTR accesses are set to trap to EL3 */ if (arm_current_el(env) == 2 && (env->cp15.cptr_el[3] & CPTR_TCPAC)) { @@ -627,8 +761,12 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { { .name = "MVA_prefetch", .cp = 15, .crn = 7, .crm = 13, .opc1 = 0, .opc2 = 1, .access = PL1_W, .type = ARM_CP_NOP }, + /* We need to break the TB after ISB to execute self-modifying code + * correctly and also to take any pending interrupts immediately. + * So use arm_cp_write_ignore() function instead of ARM_CP_NOP flag. + */ { .name = "ISB", .cp = 15, .crn = 7, .crm = 5, .opc1 = 0, .opc2 = 4, - .access = PL0_W, .type = ARM_CP_NOP }, + .access = PL0_W, .type = ARM_CP_NO_RAW, .writefn = arm_cp_write_ignore }, { .name = "DSB", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 4, .access = PL0_W, .type = ARM_CP_NOP }, { .name = "DMB", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 5, @@ -650,14 +788,26 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { REGINFO_SENTINEL }; -static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri) +static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { /* Performance monitor registers user accessibility is controlled - * by PMUSERENR. + * by PMUSERENR. MDCR_EL2.TPM and MDCR_EL3.TPM allow configurable + * trapping to EL2 or EL3 for other accesses. */ - if (arm_current_el(env) == 0 && !env->cp15.c9_pmuserenr) { + int el = arm_current_el(env); + + if (el == 0 && !env->cp15.c9_pmuserenr) { return CP_ACCESS_TRAP; } + if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TPM) + && !arm_is_secure_below_el3(env)) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TPM)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; } @@ -678,8 +828,8 @@ void pmccntr_sync(CPUARMState *env) { uint64_t temp_ticks; - temp_ticks = muldiv64(qemu_clock_get_us(QEMU_CLOCK_VIRTUAL), - get_ticks_per_sec(), 1000000); + temp_ticks = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + ARM_CPU_FREQ, NANOSECONDS_PER_SECOND); if (env->cp15.c9_pmcr & PMCRD) { /* Increment once every 64 processor clock cycles */ @@ -717,8 +867,8 @@ static uint64_t pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri) return env->cp15.c15_ccnt; } - total_ticks = muldiv64(qemu_clock_get_us(QEMU_CLOCK_VIRTUAL), - get_ticks_per_sec(), 1000000); + total_ticks = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + ARM_CPU_FREQ, NANOSECONDS_PER_SECOND); if (env->cp15.c9_pmcr & PMCRD) { /* Increment once every 64 processor clock cycles */ @@ -738,8 +888,8 @@ static void pmccntr_write(CPUARMState *env, const ARMCPRegInfo *ri, return; } - total_ticks = muldiv64(qemu_clock_get_us(QEMU_CLOCK_VIRTUAL), - get_ticks_per_sec(), 1000000); + total_ticks = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + ARM_CPU_FREQ, NANOSECONDS_PER_SECOND); if (env->cp15.c9_pmcr & PMCRD) { /* Increment once every 64 processor clock cycles */ @@ -936,6 +1086,13 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .accessfn = pmreg_access, .writefn = pmovsr_write, .raw_writefn = raw_write }, + { .name = "PMOVSCLR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 3, + .access = PL0_RW, .accessfn = pmreg_access, + .type = ARM_CP_ALIAS, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), + .writefn = pmovsr_write, + .raw_writefn = raw_write }, /* Unimplemented so WI. */ { .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4, .access = PL0_W, .accessfn = pmreg_access, .type = ARM_CP_NOP }, @@ -973,19 +1130,30 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0, .accessfn = pmreg_access }, { .name = "PMUSERENR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 0, - .access = PL0_R | PL1_RW, + .access = PL0_R | PL1_RW, .accessfn = access_tpm, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr), + .resetvalue = 0, + .writefn = pmuserenr_write, .raw_writefn = raw_write }, + { .name = "PMUSERENR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 0, + .access = PL0_R | PL1_RW, .accessfn = access_tpm, .type = ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr), .resetvalue = 0, .writefn = pmuserenr_write, .raw_writefn = raw_write }, { .name = "PMINTENSET", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 1, - .access = PL1_RW, + .access = PL1_RW, .accessfn = access_tpm, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), .resetvalue = 0, .writefn = pmintenset_write, .raw_writefn = raw_write }, { .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2, - .access = PL1_RW, .type = ARM_CP_ALIAS, + .access = PL1_RW, .accessfn = access_tpm, .type = ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), .writefn = pmintenclr_write, }, + { .name = "PMINTENCLR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 2, + .access = PL1_RW, .accessfn = access_tpm, .type = ARM_CP_ALIAS, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), + .writefn = pmintenclr_write }, { .name = "VBAR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .writefn = vbar_write, @@ -1022,6 +1190,10 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.mair_el[1]), .resetvalue = 0 }, + { .name = "MAIR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 10, .crm = 2, .opc2 = 0, + .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.mair_el[3]), + .resetvalue = 0 }, /* For non-long-descriptor page tables these are PRRR and NMRR; * regardless they still act as reads-as-written for QEMU. */ @@ -1090,7 +1262,8 @@ static void teecr_write(CPUARMState *env, const ARMCPRegInfo *ri, env->teecr = value; } -static CPAccessResult teehbr_access(CPUARMState *env, const ARMCPRegInfo *ri) +static CPAccessResult teehbr_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { if (arm_current_el(env) == 0 && (env->teecr & 1)) { return CP_ACCESS_TRAP; @@ -1143,57 +1316,132 @@ static const ARMCPRegInfo v6k_cp_reginfo[] = { #ifndef CONFIG_USER_ONLY -static CPAccessResult gt_cntfrq_access(CPUARMState *env, const ARMCPRegInfo *ri) +static CPAccessResult gt_cntfrq_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { - /* CNTFRQ: not visible from PL0 if both PL0PCTEN and PL0VCTEN are zero */ - if (arm_current_el(env) == 0 && !extract32(env->cp15.c14_cntkctl, 0, 2)) { - return CP_ACCESS_TRAP; + /* CNTFRQ: not visible from PL0 if both PL0PCTEN and PL0VCTEN are zero. + * Writable only at the highest implemented exception level. + */ + int el = arm_current_el(env); + + switch (el) { + case 0: + if (!extract32(env->cp15.c14_cntkctl, 0, 2)) { + return CP_ACCESS_TRAP; + } + break; + case 1: + if (!isread && ri->state == ARM_CP_STATE_AA32 && + arm_is_secure_below_el3(env)) { + /* Accesses from 32-bit Secure EL1 UNDEF (*not* trap to EL3!) */ + return CP_ACCESS_TRAP_UNCATEGORIZED; + } + break; + case 2: + case 3: + break; + } + + if (!isread && el < arm_highest_el(env)) { + return CP_ACCESS_TRAP_UNCATEGORIZED; } + return CP_ACCESS_OK; } -static CPAccessResult gt_counter_access(CPUARMState *env, int timeridx) +static CPAccessResult gt_counter_access(CPUARMState *env, int timeridx, + bool isread) { + unsigned int cur_el = arm_current_el(env); + bool secure = arm_is_secure(env); + /* CNT[PV]CT: not visible from PL0 if ELO[PV]CTEN is zero */ - if (arm_current_el(env) == 0 && + if (cur_el == 0 && !extract32(env->cp15.c14_cntkctl, timeridx, 1)) { return CP_ACCESS_TRAP; } + + if (arm_feature(env, ARM_FEATURE_EL2) && + timeridx == GTIMER_PHYS && !secure && cur_el < 2 && + !extract32(env->cp15.cnthctl_el2, 0, 1)) { + return CP_ACCESS_TRAP_EL2; + } return CP_ACCESS_OK; } -static CPAccessResult gt_timer_access(CPUARMState *env, int timeridx) +static CPAccessResult gt_timer_access(CPUARMState *env, int timeridx, + bool isread) { + unsigned int cur_el = arm_current_el(env); + bool secure = arm_is_secure(env); + /* CNT[PV]_CVAL, CNT[PV]_CTL, CNT[PV]_TVAL: not visible from PL0 if * EL0[PV]TEN is zero. */ - if (arm_current_el(env) == 0 && + if (cur_el == 0 && !extract32(env->cp15.c14_cntkctl, 9 - timeridx, 1)) { return CP_ACCESS_TRAP; } + + if (arm_feature(env, ARM_FEATURE_EL2) && + timeridx == GTIMER_PHYS && !secure && cur_el < 2 && + !extract32(env->cp15.cnthctl_el2, 1, 1)) { + return CP_ACCESS_TRAP_EL2; + } return CP_ACCESS_OK; } static CPAccessResult gt_pct_access(CPUARMState *env, - const ARMCPRegInfo *ri) + const ARMCPRegInfo *ri, + bool isread) { - return gt_counter_access(env, GTIMER_PHYS); + return gt_counter_access(env, GTIMER_PHYS, isread); } static CPAccessResult gt_vct_access(CPUARMState *env, - const ARMCPRegInfo *ri) + const ARMCPRegInfo *ri, + bool isread) +{ + return gt_counter_access(env, GTIMER_VIRT, isread); +} + +static CPAccessResult gt_ptimer_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { - return gt_counter_access(env, GTIMER_VIRT); + return gt_timer_access(env, GTIMER_PHYS, isread); } -static CPAccessResult gt_ptimer_access(CPUARMState *env, const ARMCPRegInfo *ri) +static CPAccessResult gt_vtimer_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { - return gt_timer_access(env, GTIMER_PHYS); + return gt_timer_access(env, GTIMER_VIRT, isread); } -static CPAccessResult gt_vtimer_access(CPUARMState *env, const ARMCPRegInfo *ri) +static CPAccessResult gt_stimer_access(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) { - return gt_timer_access(env, GTIMER_VIRT); + /* The AArch64 register view of the secure physical timer is + * always accessible from EL3, and configurably accessible from + * Secure EL1. + */ + switch (arm_current_el(env)) { + case 1: + if (!arm_is_secure(env)) { + return CP_ACCESS_TRAP; + } + if (!(env->cp15.scr_el3 & SCR_ST)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; + case 0: + case 2: + return CP_ACCESS_TRAP; + case 3: + return CP_ACCESS_OK; + default: + g_assert_not_reached(); + } } static uint64_t gt_get_countervalue(CPUARMState *env) @@ -1209,9 +1457,11 @@ static void gt_recalc_timer(ARMCPU *cpu, int timeridx) /* Timer enabled: calculate and set current ISTATUS, irq, and * reset timer to when ISTATUS next has to change */ + uint64_t offset = timeridx == GTIMER_VIRT ? + cpu->env.cp15.cntvoff_el2 : 0; uint64_t count = gt_get_countervalue(&cpu->env); /* Note that this must be unsigned 64 bit arithmetic: */ - int istatus = count >= gt->cval; + int istatus = count - offset >= gt->cval; uint64_t nexttick; gt->ctl = deposit32(gt->ctl, 2, 1, istatus); @@ -1222,7 +1472,7 @@ static void gt_recalc_timer(ARMCPU *cpu, int timeridx) nexttick = UINT64_MAX; } else { /* Next transition is when we hit cval */ - nexttick = gt->cval; + nexttick = gt->cval + offset; } /* Note that the desired next expiry time might be beyond the * signed-64-bit range of a QEMUTimer -- in this case we just @@ -1241,10 +1491,10 @@ static void gt_recalc_timer(ARMCPU *cpu, int timeridx) } } -static void gt_cnt_reset(CPUARMState *env, const ARMCPRegInfo *ri) +static void gt_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri, + int timeridx) { ARMCPU *cpu = arm_env_get_cpu(env); - int timeridx = ri->opc1 & 1; timer_del(cpu->gt_timer[timeridx]); } @@ -1254,38 +1504,44 @@ static uint64_t gt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri) return gt_get_countervalue(env); } +static uint64_t gt_virt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return gt_get_countervalue(env) - env->cp15.cntvoff_el2; +} + static void gt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, + int timeridx, uint64_t value) { - int timeridx = ri->opc1 & 1; - env->cp15.c14_timer[timeridx].cval = value; gt_recalc_timer(arm_env_get_cpu(env), timeridx); } -static uint64_t gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri) +static uint64_t gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, + int timeridx) { - int timeridx = ri->crm & 1; + uint64_t offset = timeridx == GTIMER_VIRT ? env->cp15.cntvoff_el2 : 0; return (uint32_t)(env->cp15.c14_timer[timeridx].cval - - gt_get_countervalue(env)); + (gt_get_countervalue(env) - offset)); } static void gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, + int timeridx, uint64_t value) { - int timeridx = ri->crm & 1; + uint64_t offset = timeridx == GTIMER_VIRT ? env->cp15.cntvoff_el2 : 0; - env->cp15.c14_timer[timeridx].cval = gt_get_countervalue(env) + + env->cp15.c14_timer[timeridx].cval = gt_get_countervalue(env) - offset + sextract64(value, 0, 32); gt_recalc_timer(arm_env_get_cpu(env), timeridx); } static void gt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, + int timeridx, uint64_t value) { ARMCPU *cpu = arm_env_get_cpu(env); - int timeridx = ri->crm & 1; uint32_t oldval = env->cp15.c14_timer[timeridx].ctl; env->cp15.c14_timer[timeridx].ctl = deposit64(oldval, 0, 2, value); @@ -1301,6 +1557,127 @@ static void gt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, } } +static void gt_phys_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + gt_timer_reset(env, ri, GTIMER_PHYS); +} + +static void gt_phys_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_cval_write(env, ri, GTIMER_PHYS, value); +} + +static uint64_t gt_phys_tval_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return gt_tval_read(env, ri, GTIMER_PHYS); +} + +static void gt_phys_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_tval_write(env, ri, GTIMER_PHYS, value); +} + +static void gt_phys_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_ctl_write(env, ri, GTIMER_PHYS, value); +} + +static void gt_virt_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + gt_timer_reset(env, ri, GTIMER_VIRT); +} + +static void gt_virt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_cval_write(env, ri, GTIMER_VIRT, value); +} + +static uint64_t gt_virt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return gt_tval_read(env, ri, GTIMER_VIRT); +} + +static void gt_virt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_tval_write(env, ri, GTIMER_VIRT, value); +} + +static void gt_virt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_ctl_write(env, ri, GTIMER_VIRT, value); +} + +static void gt_cntvoff_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + + raw_write(env, ri, value); + gt_recalc_timer(cpu, GTIMER_VIRT); +} + +static void gt_hyp_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + gt_timer_reset(env, ri, GTIMER_HYP); +} + +static void gt_hyp_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_cval_write(env, ri, GTIMER_HYP, value); +} + +static uint64_t gt_hyp_tval_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return gt_tval_read(env, ri, GTIMER_HYP); +} + +static void gt_hyp_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_tval_write(env, ri, GTIMER_HYP, value); +} + +static void gt_hyp_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_ctl_write(env, ri, GTIMER_HYP, value); +} + +static void gt_sec_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + gt_timer_reset(env, ri, GTIMER_SEC); +} + +static void gt_sec_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_cval_write(env, ri, GTIMER_SEC, value); +} + +static uint64_t gt_sec_tval_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return gt_tval_read(env, ri, GTIMER_SEC); +} + +static void gt_sec_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_tval_write(env, ri, GTIMER_SEC, value); +} + +static void gt_sec_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_ctl_write(env, ri, GTIMER_SEC, value); +} + void arm_gt_ptimer_cb(void *opaque) { ARMCPU *cpu = opaque; @@ -1315,6 +1692,20 @@ void arm_gt_vtimer_cb(void *opaque) gt_recalc_timer(cpu, GTIMER_VIRT); } +void arm_gt_htimer_cb(void *opaque) +{ + ARMCPU *cpu = opaque; + + gt_recalc_timer(cpu, GTIMER_HYP); +} + +void arm_gt_stimer_cb(void *opaque) +{ + ARMCPU *cpu = opaque; + + gt_recalc_timer(cpu, GTIMER_SEC); +} + static const ARMCPRegInfo generic_timer_cp_reginfo[] = { /* Note that CNTFRQ is purely reads-as-written for the benefit * of software; writing it doesn't actually change the timer frequency. @@ -1340,11 +1731,21 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { }, /* per-timer control */ { .name = "CNTP_CTL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 1, + .secure = ARM_CP_SECSTATE_NS, .type = ARM_CP_IO | ARM_CP_ALIAS, .access = PL1_RW | PL0_R, .accessfn = gt_ptimer_access, .fieldoffset = offsetoflow32(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl), - .writefn = gt_ctl_write, .raw_writefn = raw_write, + .writefn = gt_phys_ctl_write, .raw_writefn = raw_write, + }, + { .name = "CNTP_CTL(S)", + .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 1, + .secure = ARM_CP_SECSTATE_S, + .type = ARM_CP_IO | ARM_CP_ALIAS, .access = PL1_RW | PL0_R, + .accessfn = gt_ptimer_access, + .fieldoffset = offsetoflow32(CPUARMState, + cp15.c14_timer[GTIMER_SEC].ctl), + .writefn = gt_sec_ctl_write, .raw_writefn = raw_write, }, { .name = "CNTP_CTL_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 1, @@ -1352,14 +1753,14 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .accessfn = gt_ptimer_access, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl), .resetvalue = 0, - .writefn = gt_ctl_write, .raw_writefn = raw_write, + .writefn = gt_phys_ctl_write, .raw_writefn = raw_write, }, { .name = "CNTV_CTL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 1, .type = ARM_CP_IO | ARM_CP_ALIAS, .access = PL1_RW | PL0_R, .accessfn = gt_vtimer_access, .fieldoffset = offsetoflow32(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl), - .writefn = gt_ctl_write, .raw_writefn = raw_write, + .writefn = gt_virt_ctl_write, .raw_writefn = raw_write, }, { .name = "CNTV_CTL_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 1, @@ -1367,30 +1768,38 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .accessfn = gt_vtimer_access, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl), .resetvalue = 0, - .writefn = gt_ctl_write, .raw_writefn = raw_write, + .writefn = gt_virt_ctl_write, .raw_writefn = raw_write, }, /* TimerValue views: a 32 bit downcounting view of the underlying state */ { .name = "CNTP_TVAL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 0, + .secure = ARM_CP_SECSTATE_NS, .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, .accessfn = gt_ptimer_access, - .readfn = gt_tval_read, .writefn = gt_tval_write, + .readfn = gt_phys_tval_read, .writefn = gt_phys_tval_write, + }, + { .name = "CNTP_TVAL(S)", + .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 0, + .secure = ARM_CP_SECSTATE_S, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, + .accessfn = gt_ptimer_access, + .readfn = gt_sec_tval_read, .writefn = gt_sec_tval_write, }, { .name = "CNTP_TVAL_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 0, .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, - .accessfn = gt_ptimer_access, - .readfn = gt_tval_read, .writefn = gt_tval_write, + .accessfn = gt_ptimer_access, .resetfn = gt_phys_timer_reset, + .readfn = gt_phys_tval_read, .writefn = gt_phys_tval_write, }, { .name = "CNTV_TVAL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 0, .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, .accessfn = gt_vtimer_access, - .readfn = gt_tval_read, .writefn = gt_tval_write, + .readfn = gt_virt_tval_read, .writefn = gt_virt_tval_write, }, { .name = "CNTV_TVAL_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 0, .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, - .accessfn = gt_vtimer_access, - .readfn = gt_tval_read, .writefn = gt_tval_write, + .accessfn = gt_vtimer_access, .resetfn = gt_virt_timer_reset, + .readfn = gt_virt_tval_read, .writefn = gt_virt_tval_write, }, /* The counter itself */ { .name = "CNTPCT", .cp = 15, .crm = 14, .opc1 = 0, @@ -1401,27 +1810,34 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { { .name = "CNTPCT_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 1, .access = PL0_R, .type = ARM_CP_NO_RAW | ARM_CP_IO, - .accessfn = gt_pct_access, - .readfn = gt_cnt_read, .resetfn = gt_cnt_reset, + .accessfn = gt_pct_access, .readfn = gt_cnt_read, }, { .name = "CNTVCT", .cp = 15, .crm = 14, .opc1 = 1, .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_RAW | ARM_CP_IO, .accessfn = gt_vct_access, - .readfn = gt_cnt_read, .resetfn = arm_cp_reset_ignore, + .readfn = gt_virt_cnt_read, .resetfn = arm_cp_reset_ignore, }, { .name = "CNTVCT_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 2, .access = PL0_R, .type = ARM_CP_NO_RAW | ARM_CP_IO, - .accessfn = gt_vct_access, - .readfn = gt_cnt_read, .resetfn = gt_cnt_reset, + .accessfn = gt_vct_access, .readfn = gt_virt_cnt_read, }, /* Comparison value, indicating when the timer goes off */ { .name = "CNTP_CVAL", .cp = 15, .crm = 14, .opc1 = 2, + .secure = ARM_CP_SECSTATE_NS, .access = PL1_RW | PL0_R, .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), .accessfn = gt_ptimer_access, - .writefn = gt_cval_write, .raw_writefn = raw_write, + .writefn = gt_phys_cval_write, .raw_writefn = raw_write, + }, + { .name = "CNTP_CVAL(S)", .cp = 15, .crm = 14, .opc1 = 2, + .secure = ARM_CP_SECSTATE_S, + .access = PL1_RW | PL0_R, + .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_SEC].cval), + .accessfn = gt_ptimer_access, + .writefn = gt_sec_cval_write, .raw_writefn = raw_write, }, { .name = "CNTP_CVAL_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 2, @@ -1429,14 +1845,14 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), .resetvalue = 0, .accessfn = gt_ptimer_access, - .writefn = gt_cval_write, .raw_writefn = raw_write, + .writefn = gt_phys_cval_write, .raw_writefn = raw_write, }, { .name = "CNTV_CVAL", .cp = 15, .crm = 14, .opc1 = 3, .access = PL1_RW | PL0_R, .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), .accessfn = gt_vtimer_access, - .writefn = gt_cval_write, .raw_writefn = raw_write, + .writefn = gt_virt_cval_write, .raw_writefn = raw_write, }, { .name = "CNTV_CVAL_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 2, @@ -1444,7 +1860,33 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), .resetvalue = 0, .accessfn = gt_vtimer_access, - .writefn = gt_cval_write, .raw_writefn = raw_write, + .writefn = gt_virt_cval_write, .raw_writefn = raw_write, + }, + /* Secure timer -- this is actually restricted to only EL3 + * and configurably Secure-EL1 via the accessfn. + */ + { .name = "CNTPS_TVAL_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 7, .crn = 14, .crm = 2, .opc2 = 0, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW, + .accessfn = gt_stimer_access, + .readfn = gt_sec_tval_read, + .writefn = gt_sec_tval_write, + .resetfn = gt_sec_timer_reset, + }, + { .name = "CNTPS_CTL_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 7, .crn = 14, .crm = 2, .opc2 = 1, + .type = ARM_CP_IO, .access = PL1_RW, + .accessfn = gt_stimer_access, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_SEC].ctl), + .resetvalue = 0, + .writefn = gt_sec_ctl_write, .raw_writefn = raw_write, + }, + { .name = "CNTPS_CVAL_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 7, .crn = 14, .crm = 2, .opc2 = 2, + .type = ARM_CP_IO, .access = PL1_RW, + .accessfn = gt_stimer_access, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_SEC].cval), + .writefn = gt_sec_cval_write, .raw_writefn = raw_write, }, REGINFO_SENTINEL }; @@ -1474,15 +1916,21 @@ static void par_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) #ifndef CONFIG_USER_ONLY /* get_phys_addr() isn't present for user-mode-only targets */ -static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri) +static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { if (ri->opc2 & 4) { - /* Other states are only available with TrustZone; in - * a non-TZ implementation these registers don't exist - * at all, which is an Uncategorized trap. This underdecoding - * is safe because the reginfo is NO_RAW. + /* The ATS12NSO* operations must trap to EL3 if executed in + * Secure EL1 (which can only happen if EL3 is AArch64). + * They are simply UNDEF if executed from NS EL1. + * They function normally from EL2 or EL3. */ - return CP_ACCESS_TRAP_UNCATEGORIZED; + if (arm_current_el(env) == 1) { + if (arm_is_secure_below_el3(env)) { + return CP_ACCESS_TRAP_UNCATEGORIZED_EL3; + } + return CP_ACCESS_TRAP_UNCATEGORIZED; + } } return CP_ACCESS_OK; } @@ -1497,9 +1945,10 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value, bool ret; uint64_t par64; MemTxAttrs attrs = {}; + ARMMMUFaultInfo fi = {}; ret = get_phys_addr(env, value, access_type, mmu_idx, - &phys_addr, &attrs, &prot, &page_size, &fsr); + &phys_addr, &attrs, &prot, &page_size, &fsr, &fi); if (extended_addresses_enabled(env)) { /* fsr is a DFSR/IFSR value for the long descriptor * translation table format, but with WnR always clear. @@ -1602,21 +2051,41 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) A32_BANKED_CURRENT_REG_SET(env, par, par64); } -static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, +static void ats1h_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { int access_type = ri->opc2 & 1; - ARMMMUIdx mmu_idx; - int secure = arm_is_secure_below_el3(env); + uint64_t par64; - switch (ri->opc2 & 6) { - case 0: - switch (ri->opc1) { - case 0: /* AT S1E1R, AT S1E1W */ - mmu_idx = secure ? ARMMMUIdx_S1SE1 : ARMMMUIdx_S1NSE1; - break; - case 4: /* AT S1E2R, AT S1E2W */ - mmu_idx = ARMMMUIdx_S1E2; + par64 = do_ats_write(env, value, access_type, ARMMMUIdx_S2NS); + + A32_BANKED_CURRENT_REG_SET(env, par, par64); +} + +static CPAccessResult at_s1e2_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 3 && !(env->cp15.scr_el3 & SCR_NS)) { + return CP_ACCESS_TRAP; + } + return CP_ACCESS_OK; +} + +static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + int access_type = ri->opc2 & 1; + ARMMMUIdx mmu_idx; + int secure = arm_is_secure_below_el3(env); + + switch (ri->opc2 & 6) { + case 0: + switch (ri->opc1) { + case 0: /* AT S1E1R, AT S1E1W */ + mmu_idx = secure ? ARMMMUIdx_S1SE1 : ARMMMUIdx_S1NSE1; + break; + case 4: /* AT S1E2R, AT S1E2W */ + mmu_idx = ARMMMUIdx_S1E2; break; case 6: /* AT S1E3R, AT S1E3W */ mmu_idx = ARMMMUIdx_S1E3; @@ -1629,10 +2098,10 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, mmu_idx = secure ? ARMMMUIdx_S1SE0 : ARMMMUIdx_S1NSE0; break; case 4: /* AT S12E1R, AT S12E1W */ - mmu_idx = ARMMMUIdx_S12NSE1; + mmu_idx = secure ? ARMMMUIdx_S1SE1 : ARMMMUIdx_S12NSE1; break; case 6: /* AT S12E0R, AT S12E0W */ - mmu_idx = ARMMMUIdx_S12NSE0; + mmu_idx = secure ? ARMMMUIdx_S1SE0 : ARMMMUIdx_S12NSE0; break; default: g_assert_not_reached(); @@ -1649,6 +2118,7 @@ static const ARMCPRegInfo vapa_cp_reginfo[] = { offsetoflow32(CPUARMState, cp15.par_ns) }, .writefn = par_write }, #ifndef CONFIG_USER_ONLY + /* This underdecoding is safe because the reginfo is NO_RAW. */ { .name = "ATS", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = CP_ANY, .access = PL1_W, .accessfn = ats_access, .writefn = ats_write, .type = ARM_CP_NO_RAW }, @@ -1856,7 +2326,7 @@ static void vmsa_ttbcr_raw_write(CPUARMState *env, const ARMCPRegInfo *ri, } } - /* Update the masks corresponding to the the TCR bank being written + /* Update the masks corresponding to the TCR bank being written * Note that we always calculate mask and base_mask, but * they are only used for short-descriptor tables (ie if EAE is 0); * for long-descriptor tables the TCR fields are used differently @@ -1918,6 +2388,20 @@ static void vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri, raw_write(env, ri, value); } +static void vttbr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + CPUState *cs = CPU(cpu); + + /* Accesses to VTTBR may change the VMID so we must flush the TLB. */ + if (raw_read(env, ri) != value) { + tlb_flush_by_mmuidx(cs, ARMMMUIdx_S12NSE1, ARMMMUIdx_S12NSE0, + ARMMMUIdx_S2NS, -1); + raw_write(env, ri, value); + } +} + static const ARMCPRegInfo vmsa_pmsa_cp_reginfo[] = { { .name = "DFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_ALIAS, @@ -2136,7 +2620,19 @@ static const ARMCPRegInfo strongarm_cp_reginfo[] = { REGINFO_SENTINEL }; -static uint64_t mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri) +static uint64_t midr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + unsigned int cur_el = arm_current_el(env); + bool secure = arm_is_secure(env); + + if (arm_feature(&cpu->env, ARM_FEATURE_EL2) && !secure && cur_el == 1) { + return env->cp15.vpidr_el2; + } + return raw_read(env, ri); +} + +static uint64_t mpidr_read_val(CPUARMState *env) { ARMCPU *cpu = ARM_CPU(arm_env_get_cpu(env)); uint64_t mpidr = cpu->mp_affinity; @@ -2154,6 +2650,17 @@ static uint64_t mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri) return mpidr; } +static uint64_t mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + unsigned int cur_el = arm_current_el(env); + bool secure = arm_is_secure(env); + + if (arm_feature(env, ARM_FEATURE_EL2) && !secure && cur_el == 1) { + return env->cp15.vmpidr_el2; + } + return mpidr_read_val(env); +} + static const ARMCPRegInfo mpidr_cp_reginfo[] = { { .name = "MPIDR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 5, @@ -2210,7 +2717,8 @@ static void aa64_fpsr_write(CPUARMState *env, const ARMCPRegInfo *ri, vfp_set_fpsr(env, value); } -static CPAccessResult aa64_daif_access(CPUARMState *env, const ARMCPRegInfo *ri) +static CPAccessResult aa64_daif_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { if (arm_current_el(env) == 0 && !(env->cp15.sctlr_el[1] & SCTLR_UMA)) { return CP_ACCESS_TRAP; @@ -2225,7 +2733,8 @@ static void aa64_daif_write(CPUARMState *env, const ARMCPRegInfo *ri, } static CPAccessResult aa64_cacheop_access(CPUARMState *env, - const ARMCPRegInfo *ri) + const ARMCPRegInfo *ri, + bool isread) { /* Cache invalidate/clean: NOP, but EL0 must UNDEF unless * SCTLR_EL1.UCI is set. @@ -2240,69 +2749,249 @@ static CPAccessResult aa64_cacheop_access(CPUARMState *env, * Page D4-1736 (DDI0487A.b) */ -static void tlbi_aa64_va_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + CPUState *cs = CPU(cpu); + + if (arm_is_secure_below_el3(env)) { + tlb_flush_by_mmuidx(cs, ARMMMUIdx_S1SE1, ARMMMUIdx_S1SE0, -1); + } else { + tlb_flush_by_mmuidx(cs, ARMMMUIdx_S12NSE1, ARMMMUIdx_S12NSE0, -1); + } +} + +static void tlbi_aa64_vmalle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + bool sec = arm_is_secure_below_el3(env); + CPUState *other_cs; + + CPU_FOREACH(other_cs) { + if (sec) { + tlb_flush_by_mmuidx(other_cs, ARMMMUIdx_S1SE1, ARMMMUIdx_S1SE0, -1); + } else { + tlb_flush_by_mmuidx(other_cs, ARMMMUIdx_S12NSE1, + ARMMMUIdx_S12NSE0, -1); + } + } +} + +static void tlbi_aa64_alle1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Note that the 'ALL' scope must invalidate both stage 1 and + * stage 2 translations, whereas most other scopes only invalidate + * stage 1 translations. + */ + ARMCPU *cpu = arm_env_get_cpu(env); + CPUState *cs = CPU(cpu); + + if (arm_is_secure_below_el3(env)) { + tlb_flush_by_mmuidx(cs, ARMMMUIdx_S1SE1, ARMMMUIdx_S1SE0, -1); + } else { + if (arm_feature(env, ARM_FEATURE_EL2)) { + tlb_flush_by_mmuidx(cs, ARMMMUIdx_S12NSE1, ARMMMUIdx_S12NSE0, + ARMMMUIdx_S2NS, -1); + } else { + tlb_flush_by_mmuidx(cs, ARMMMUIdx_S12NSE1, ARMMMUIdx_S12NSE0, -1); + } + } +} + +static void tlbi_aa64_alle2_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + CPUState *cs = CPU(cpu); + + tlb_flush_by_mmuidx(cs, ARMMMUIdx_S1E2, -1); +} + +static void tlbi_aa64_alle3_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + CPUState *cs = CPU(cpu); + + tlb_flush_by_mmuidx(cs, ARMMMUIdx_S1E3, -1); +} + +static void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Note that the 'ALL' scope must invalidate both stage 1 and + * stage 2 translations, whereas most other scopes only invalidate + * stage 1 translations. + */ + bool sec = arm_is_secure_below_el3(env); + bool has_el2 = arm_feature(env, ARM_FEATURE_EL2); + CPUState *other_cs; + + CPU_FOREACH(other_cs) { + if (sec) { + tlb_flush_by_mmuidx(other_cs, ARMMMUIdx_S1SE1, ARMMMUIdx_S1SE0, -1); + } else if (has_el2) { + tlb_flush_by_mmuidx(other_cs, ARMMMUIdx_S12NSE1, + ARMMMUIdx_S12NSE0, ARMMMUIdx_S2NS, -1); + } else { + tlb_flush_by_mmuidx(other_cs, ARMMMUIdx_S12NSE1, + ARMMMUIdx_S12NSE0, -1); + } + } +} + +static void tlbi_aa64_alle2is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *other_cs; + + CPU_FOREACH(other_cs) { + tlb_flush_by_mmuidx(other_cs, ARMMMUIdx_S1E2, -1); + } +} + +static void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *other_cs; + + CPU_FOREACH(other_cs) { + tlb_flush_by_mmuidx(other_cs, ARMMMUIdx_S1E3, -1); + } +} + +static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { - /* Invalidate by VA (AArch64 version) */ + /* Invalidate by VA, EL1&0 (AArch64 version). + * Currently handles all of VAE1, VAAE1, VAALE1 and VALE1, + * since we don't support flush-for-specific-ASID-only or + * flush-last-level-only. + */ ARMCPU *cpu = arm_env_get_cpu(env); + CPUState *cs = CPU(cpu); uint64_t pageaddr = sextract64(value << 12, 0, 56); - tlb_flush_page(CPU(cpu), pageaddr); + if (arm_is_secure_below_el3(env)) { + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdx_S1SE1, + ARMMMUIdx_S1SE0, -1); + } else { + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdx_S12NSE1, + ARMMMUIdx_S12NSE0, -1); + } } -static void tlbi_aa64_vaa_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +static void tlbi_aa64_vae2_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { - /* Invalidate by VA, all ASIDs (AArch64 version) */ + /* Invalidate by VA, EL2 + * Currently handles both VAE2 and VALE2, since we don't support + * flush-last-level-only. + */ ARMCPU *cpu = arm_env_get_cpu(env); + CPUState *cs = CPU(cpu); uint64_t pageaddr = sextract64(value << 12, 0, 56); - tlb_flush_page(CPU(cpu), pageaddr); + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdx_S1E2, -1); } -static void tlbi_aa64_asid_write(CPUARMState *env, const ARMCPRegInfo *ri, +static void tlbi_aa64_vae3_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - /* Invalidate by ASID (AArch64 version) */ + /* Invalidate by VA, EL3 + * Currently handles both VAE3 and VALE3, since we don't support + * flush-last-level-only. + */ ARMCPU *cpu = arm_env_get_cpu(env); - int asid = extract64(value, 48, 16); - tlb_flush(CPU(cpu), asid == 0); + CPUState *cs = CPU(cpu); + uint64_t pageaddr = sextract64(value << 12, 0, 56); + + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdx_S1E3, -1); } -static void tlbi_aa64_va_is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +static void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { + bool sec = arm_is_secure_below_el3(env); CPUState *other_cs; uint64_t pageaddr = sextract64(value << 12, 0, 56); CPU_FOREACH(other_cs) { - tlb_flush_page(other_cs, pageaddr); + if (sec) { + tlb_flush_page_by_mmuidx(other_cs, pageaddr, ARMMMUIdx_S1SE1, + ARMMMUIdx_S1SE0, -1); + } else { + tlb_flush_page_by_mmuidx(other_cs, pageaddr, ARMMMUIdx_S12NSE1, + ARMMMUIdx_S12NSE0, -1); + } } } -static void tlbi_aa64_vaa_is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +static void tlbi_aa64_vae2is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { CPUState *other_cs; uint64_t pageaddr = sextract64(value << 12, 0, 56); CPU_FOREACH(other_cs) { - tlb_flush_page(other_cs, pageaddr); + tlb_flush_page_by_mmuidx(other_cs, pageaddr, ARMMMUIdx_S1E2, -1); } } -static void tlbi_aa64_asid_is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { CPUState *other_cs; - int asid = extract64(value, 48, 16); + uint64_t pageaddr = sextract64(value << 12, 0, 56); CPU_FOREACH(other_cs) { - tlb_flush(other_cs, asid == 0); + tlb_flush_page_by_mmuidx(other_cs, pageaddr, ARMMMUIdx_S1E3, -1); } } -static CPAccessResult aa64_zva_access(CPUARMState *env, const ARMCPRegInfo *ri) +static void tlbi_aa64_ipas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate by IPA. This has to invalidate any structures that + * contain only stage 2 translation information, but does not need + * to apply to structures that contain combined stage 1 and stage 2 + * translation information. + * This must NOP if EL2 isn't implemented or SCR_EL3.NS is zero. + */ + ARMCPU *cpu = arm_env_get_cpu(env); + CPUState *cs = CPU(cpu); + uint64_t pageaddr; + + if (!arm_feature(env, ARM_FEATURE_EL2) || !(env->cp15.scr_el3 & SCR_NS)) { + return; + } + + pageaddr = sextract64(value << 12, 0, 48); + + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdx_S2NS, -1); +} + +static void tlbi_aa64_ipas2e1is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *other_cs; + uint64_t pageaddr; + + if (!arm_feature(env, ARM_FEATURE_EL2) || !(env->cp15.scr_el3 & SCR_NS)) { + return; + } + + pageaddr = sextract64(value << 12, 0, 48); + + CPU_FOREACH(other_cs) { + tlb_flush_page_by_mmuidx(other_cs, pageaddr, ARMMMUIdx_S2NS, -1); + } +} + +static CPAccessResult aa64_zva_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { /* We don't implement EL2, so the only control on DC ZVA is the * bit in the SCTLR which can prohibit access for EL0. @@ -2319,13 +3008,14 @@ static uint64_t aa64_dczid_read(CPUARMState *env, const ARMCPRegInfo *ri) int dzp_bit = 1 << 4; /* DZP indicates whether DC ZVA access is allowed */ - if (aa64_zva_access(env, NULL) == CP_ACCESS_OK) { + if (aa64_zva_access(env, NULL, false) == CP_ACCESS_OK) { dzp_bit = 0; } return cpu->dcz_blocksize | dzp_bit; } -static CPAccessResult sp_el0_access(CPUARMState *env, const ARMCPRegInfo *ri) +static CPAccessResult sp_el0_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { if (!(env->pstate & PSTATE_SP)) { /* Access to SP_EL0 is undefined if it's being used as @@ -2364,6 +3054,24 @@ static void sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush(CPU(cpu), 1); } +static CPAccessResult fpexc32_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if ((env->cp15.cptr_el[2] & CPTR_TFP) && arm_current_el(env) == 2) { + return CP_ACCESS_TRAP_FP_EL2; + } + if (env->cp15.cptr_el[3] & CPTR_TFP) { + return CP_ACCESS_TRAP_FP_EL3; + } + return CP_ACCESS_OK; +} + +static void sdcr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + env->cp15.mdcr_el3 = value & SDCR_VALID_MASK; +} + static const ARMCPRegInfo v8_cp_reginfo[] = { /* Minimal set of EL0-visible registers. This will need to be expanded * significantly for system emulation of AArch64 CPUs. @@ -2434,62 +3142,86 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 2, .access = PL1_W, .type = ARM_CP_NOP }, /* TLBI operations */ - { .name = "TLBI_ALLE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 4, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbiall_write }, - { .name = "TLBI_ALLE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 4, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbiall_is_write }, { .name = "TLBI_VMALLE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, .access = PL1_W, .type = ARM_CP_NO_RAW, - .writefn = tlbiall_is_write }, + .writefn = tlbi_aa64_vmalle1is_write }, { .name = "TLBI_VAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1, .access = PL1_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_va_is_write }, + .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_ASIDE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2, .access = PL1_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_asid_is_write }, + .writefn = tlbi_aa64_vmalle1is_write }, { .name = "TLBI_VAAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, .access = PL1_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_vaa_is_write }, + .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5, .access = PL1_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_va_is_write }, + .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VAALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7, .access = PL1_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_vaa_is_write }, + .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VMALLE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0, .access = PL1_W, .type = ARM_CP_NO_RAW, - .writefn = tlbiall_write }, + .writefn = tlbi_aa64_vmalle1_write }, { .name = "TLBI_VAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1, .access = PL1_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_va_write }, + .writefn = tlbi_aa64_vae1_write }, { .name = "TLBI_ASIDE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2, .access = PL1_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_asid_write }, + .writefn = tlbi_aa64_vmalle1_write }, { .name = "TLBI_VAAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, .access = PL1_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_vaa_write }, + .writefn = tlbi_aa64_vae1_write }, { .name = "TLBI_VALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 5, .access = PL1_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_va_write }, + .writefn = tlbi_aa64_vae1_write }, { .name = "TLBI_VAALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7, .access = PL1_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_vaa_write }, + .writefn = tlbi_aa64_vae1_write }, + { .name = "TLBI_IPAS2E1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 1, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ipas2e1is_write }, + { .name = "TLBI_IPAS2LE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 5, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ipas2e1is_write }, + { .name = "TLBI_ALLE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 4, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_alle1is_write }, + { .name = "TLBI_VMALLS12E1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 6, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_alle1is_write }, + { .name = "TLBI_IPAS2E1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 1, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ipas2e1_write }, + { .name = "TLBI_IPAS2LE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 5, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ipas2e1_write }, + { .name = "TLBI_ALLE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 4, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_alle1_write }, + { .name = "TLBI_VMALLS12E1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 6, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_alle1is_write }, #ifndef CONFIG_USER_ONLY /* 64 bit address translation operations */ { .name = "AT_S1E1R", .state = ARM_CP_STATE_AA64, @@ -2504,6 +3236,31 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "AT_S1E0W", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 3, .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 }, + { .name = "AT_S12E1R", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 4, + .access = PL2_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 }, + { .name = "AT_S12E1W", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 5, + .access = PL2_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 }, + { .name = "AT_S12E0R", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 6, + .access = PL2_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 }, + { .name = "AT_S12E0W", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 7, + .access = PL2_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 }, + /* AT S1E2* are elsewhere as they UNDEF from EL3 if EL2 is not present */ + { .name = "AT_S1E3R", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 8, .opc2 = 0, + .access = PL3_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 }, + { .name = "AT_S1E3W", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 8, .opc2 = 1, + .access = PL3_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 }, + { .name = "PAR_EL1", .state = ARM_CP_STATE_AA64, + .type = ARM_CP_ALIAS, + .opc0 = 3, .opc1 = 0, .crn = 7, .crm = 4, .opc2 = 0, + .access = PL1_RW, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.par_el[1]), + .writefn = par_write }, #endif /* TLB invalidate last level of translation table walk */ { .name = "TLBIMVALIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5, @@ -2556,7 +3313,8 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "SPSR_EL1", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 0, - .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, banked_spsr[1]) }, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_SVC]) }, /* We rely on the access checks not allowing the guest to write to the * state field when SPSel indicates that it's being used as the stack * pointer. @@ -2574,6 +3332,49 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 2, .opc2 = 0, .type = ARM_CP_NO_RAW, .access = PL1_RW, .readfn = spsel_read, .writefn = spsel_write }, + { .name = "FPEXC32_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 3, .opc2 = 0, + .type = ARM_CP_ALIAS, + .fieldoffset = offsetof(CPUARMState, vfp.xregs[ARM_VFP_FPEXC]), + .access = PL2_RW, .accessfn = fpexc32_access }, + { .name = "DACR32_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 3, .crm = 0, .opc2 = 0, + .access = PL2_RW, .resetvalue = 0, + .writefn = dacr_write, .raw_writefn = raw_write, + .fieldoffset = offsetof(CPUARMState, cp15.dacr32_el2) }, + { .name = "IFSR32_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 0, .opc2 = 1, + .access = PL2_RW, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.ifsr32_el2) }, + { .name = "SPSR_IRQ", .state = ARM_CP_STATE_AA64, + .type = ARM_CP_ALIAS, + .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 0, + .access = PL2_RW, + .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_IRQ]) }, + { .name = "SPSR_ABT", .state = ARM_CP_STATE_AA64, + .type = ARM_CP_ALIAS, + .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 1, + .access = PL2_RW, + .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_ABT]) }, + { .name = "SPSR_UND", .state = ARM_CP_STATE_AA64, + .type = ARM_CP_ALIAS, + .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 2, + .access = PL2_RW, + .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_UND]) }, + { .name = "SPSR_FIQ", .state = ARM_CP_STATE_AA64, + .type = ARM_CP_ALIAS, + .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 3, + .access = PL2_RW, + .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_FIQ]) }, + { .name = "MDCR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 3, .opc2 = 1, + .resetvalue = 0, + .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.mdcr_el3) }, + { .name = "SDCR", .type = ARM_CP_ALIAS, + .cp = 15, .opc1 = 0, .crn = 1, .crm = 3, .opc2 = 1, + .access = PL1_RW, .accessfn = access_trap_aa32s_el1, + .writefn = sdcr_write, + .fieldoffset = offsetoflow32(CPUARMState, cp15.mdcr_el3) }, REGINFO_SENTINEL }; @@ -2598,9 +3399,36 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { { .name = "HMAIR1", .state = ARM_CP_STATE_AA32, .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "AMAIR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "HMAIR1", .state = ARM_CP_STATE_AA32, + .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 1, + .access = PL2_RW, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "AFSR0_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 1, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "AFSR1_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 1, .opc2 = 1, + .access = PL2_RW, .type = ARM_CP_CONST, + .resetvalue = 0 }, { .name = "TCR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 2, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "VTCR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 2, + .access = PL2_RW, .accessfn = access_el3_aa32ns_aa64any, + .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "VTTBR", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 6, .crm = 2, + .access = PL2_RW, .accessfn = access_el3_aa32ns, + .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, + { .name = "VTTBR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "SCTLR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 0, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, @@ -2613,6 +3441,35 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { { .name = "HTTBR", .cp = 15, .opc1 = 4, .crm = 2, .access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CNTHCTL_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 1, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CNTVOFF_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 0, .opc2 = 3, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CNTVOFF", .cp = 15, .opc1 = 4, .crm = 14, + .access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "CNTHP_CVAL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 2, .opc2 = 2, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CNTHP_CVAL", .cp = 15, .opc1 = 6, .crm = 14, + .access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "CNTHP_TVAL_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 2, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CNTHP_CTL_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 2, .opc2 = 1, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "MDCR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 1, + .access = PL2_RW, .accessfn = access_tda, + .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "HPFAR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 4, + .access = PL2_RW, .accessfn = access_el3_aa32ns_aa64any, + .type = ARM_CP_CONST, .resetvalue = 0 }, REGINFO_SENTINEL }; @@ -2646,31 +3503,22 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2), .writefn = hcr_write }, - { .name = "DACR32_EL2", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 4, .crn = 3, .crm = 0, .opc2 = 0, - .access = PL2_RW, .resetvalue = 0, - .writefn = dacr_write, .raw_writefn = raw_write, - .fieldoffset = offsetof(CPUARMState, cp15.dacr32_el2) }, { .name = "ELR_EL2", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 1, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, elr_el[2]) }, { .name = "ESR_EL2", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[2]) }, - { .name = "IFSR32_EL2", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 0, .opc2 = 1, - .access = PL2_RW, .resetvalue = 0, - .fieldoffset = offsetof(CPUARMState, cp15.ifsr32_el2) }, { .name = "FAR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[2]) }, { .name = "SPSR_EL2", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 0, - .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, banked_spsr[6]) }, + .access = PL2_RW, + .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_HYP]) }, { .name = "VBAR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 0, .access = PL2_RW, .writefn = vbar_write, @@ -2692,11 +3540,50 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1, .access = PL2_RW, .type = ARM_CP_ALIAS, .fieldoffset = offsetofhigh32(CPUARMState, cp15.mair_el[2]) }, + { .name = "AMAIR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_CONST, + .resetvalue = 0 }, + /* HAMAIR1 is mapped to AMAIR_EL2[63:32] */ + { .name = "HMAIR1", .state = ARM_CP_STATE_AA32, + .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 1, + .access = PL2_RW, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "AFSR0_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 1, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "AFSR1_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 1, .opc2 = 1, + .access = PL2_RW, .type = ARM_CP_CONST, + .resetvalue = 0 }, { .name = "TCR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 2, .access = PL2_RW, .writefn = vmsa_tcr_el1_write, .resetfn = vmsa_ttbcr_reset, .raw_writefn = raw_write, .fieldoffset = offsetof(CPUARMState, cp15.tcr_el[2]) }, + { .name = "VTCR", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 2, + .type = ARM_CP_ALIAS, + .access = PL2_RW, .accessfn = access_el3_aa32ns, + .fieldoffset = offsetof(CPUARMState, cp15.vtcr_el2) }, + { .name = "VTCR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 2, + .access = PL2_RW, + /* no .writefn needed as this can't cause an ASID change; + * no .raw_writefn or .resetfn needed as we never use mask/base_mask + */ + .fieldoffset = offsetof(CPUARMState, cp15.vtcr_el2) }, + { .name = "VTTBR", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 6, .crm = 2, + .type = ARM_CP_64BIT | ARM_CP_ALIAS, + .access = PL2_RW, .accessfn = access_el3_aa32ns, + .fieldoffset = offsetof(CPUARMState, cp15.vttbr_el2), + .writefn = vttbr_write }, + { .name = "VTTBR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 0, + .access = PL2_RW, .writefn = vttbr_write, + .fieldoffset = offsetof(CPUARMState, cp15.vttbr_el2) }, { .name = "SCTLR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 0, .access = PL2_RW, .raw_writefn = raw_write, .writefn = sctlr_write, @@ -2715,18 +3602,129 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { { .name = "TLBI_ALLE2", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 0, .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbiall_write }, + .writefn = tlbi_aa64_alle2_write }, { .name = "TLBI_VAE2", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 1, .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbi_aa64_vaa_write }, + .writefn = tlbi_aa64_vae2_write }, + { .name = "TLBI_VALE2", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 5, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_vae2_write }, + { .name = "TLBI_ALLE2IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 0, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_alle2is_write }, { .name = "TLBI_VAE2IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 1, .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbi_aa64_vaa_write }, + .writefn = tlbi_aa64_vae2is_write }, + { .name = "TLBI_VALE2IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 5, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_vae2is_write }, +#ifndef CONFIG_USER_ONLY + /* Unlike the other EL2-related AT operations, these must + * UNDEF from EL3 if EL2 is not implemented, which is why we + * define them here rather than with the rest of the AT ops. + */ + { .name = "AT_S1E2R", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 0, + .access = PL2_W, .accessfn = at_s1e2_access, + .type = ARM_CP_NO_RAW, .writefn = ats_write64 }, + { .name = "AT_S1E2W", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 1, + .access = PL2_W, .accessfn = at_s1e2_access, + .type = ARM_CP_NO_RAW, .writefn = ats_write64 }, + /* The AArch32 ATS1H* operations are CONSTRAINED UNPREDICTABLE + * if EL2 is not implemented; we choose to UNDEF. Behaviour at EL3 + * with SCR.NS == 0 outside Monitor mode is UNPREDICTABLE; we choose + * to behave as if SCR.NS was 1. + */ + { .name = "ATS1HR", .cp = 15, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 0, + .access = PL2_W, + .writefn = ats1h_write, .type = ARM_CP_NO_RAW }, + { .name = "ATS1HW", .cp = 15, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 1, + .access = PL2_W, + .writefn = ats1h_write, .type = ARM_CP_NO_RAW }, + { .name = "CNTHCTL_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 1, .opc2 = 0, + /* ARMv7 requires bit 0 and 1 to reset to 1. ARMv8 defines the + * reset values as IMPDEF. We choose to reset to 3 to comply with + * both ARMv7 and ARMv8. + */ + .access = PL2_RW, .resetvalue = 3, + .fieldoffset = offsetof(CPUARMState, cp15.cnthctl_el2) }, + { .name = "CNTVOFF_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 0, .opc2 = 3, + .access = PL2_RW, .type = ARM_CP_IO, .resetvalue = 0, + .writefn = gt_cntvoff_write, + .fieldoffset = offsetof(CPUARMState, cp15.cntvoff_el2) }, + { .name = "CNTVOFF", .cp = 15, .opc1 = 4, .crm = 14, + .access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS | ARM_CP_IO, + .writefn = gt_cntvoff_write, + .fieldoffset = offsetof(CPUARMState, cp15.cntvoff_el2) }, + { .name = "CNTHP_CVAL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 2, .opc2 = 2, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_HYP].cval), + .type = ARM_CP_IO, .access = PL2_RW, + .writefn = gt_hyp_cval_write, .raw_writefn = raw_write }, + { .name = "CNTHP_CVAL", .cp = 15, .opc1 = 6, .crm = 14, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_HYP].cval), + .access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_IO, + .writefn = gt_hyp_cval_write, .raw_writefn = raw_write }, + { .name = "CNTHP_TVAL_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 2, .opc2 = 0, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL2_RW, + .resetfn = gt_hyp_timer_reset, + .readfn = gt_hyp_tval_read, .writefn = gt_hyp_tval_write }, + { .name = "CNTHP_CTL_EL2", .state = ARM_CP_STATE_BOTH, + .type = ARM_CP_IO, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 2, .opc2 = 1, + .access = PL2_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_HYP].ctl), + .resetvalue = 0, + .writefn = gt_hyp_ctl_write, .raw_writefn = raw_write }, +#endif + /* The only field of MDCR_EL2 that has a defined architectural reset value + * is MDCR_EL2.HPMN which should reset to the value of PMCR_EL0.N; but we + * don't impelment any PMU event counters, so using zero as a reset + * value for MDCR_EL2 is okay + */ + { .name = "MDCR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 1, + .access = PL2_RW, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.mdcr_el2), }, + { .name = "HPFAR", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 4, + .access = PL2_RW, .accessfn = access_el3_aa32ns, + .fieldoffset = offsetof(CPUARMState, cp15.hpfar_el2) }, + { .name = "HPFAR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 4, + .access = PL2_RW, + .fieldoffset = offsetof(CPUARMState, cp15.hpfar_el2) }, REGINFO_SENTINEL }; +static CPAccessResult nsacr_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + /* The NSACR is RW at EL3, and RO for NS EL1 and NS EL2. + * At Secure EL1 it traps to EL3. + */ + if (arm_current_el(env) == 3) { + return CP_ACCESS_OK; + } + if (arm_is_secure_below_el3(env)) { + return CP_ACCESS_TRAP_EL3; + } + /* Accesses from EL1 NS and EL2 NS are UNDEF for write but allow reads. */ + if (isread) { + return CP_ACCESS_OK; + } + return CP_ACCESS_TRAP_UNCATEGORIZED; +} + static const ARMCPRegInfo el3_cp_reginfo[] = { { .name = "SCR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 1, .opc2 = 0, @@ -2734,7 +3732,8 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { .resetvalue = 0, .writefn = scr_write }, { .name = "SCR", .type = ARM_CP_ALIAS, .cp = 15, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 0, - .access = PL3_RW, .fieldoffset = offsetoflow32(CPUARMState, cp15.scr_el3), + .access = PL1_RW, .accessfn = access_trap_aa32s_el1, + .fieldoffset = offsetoflow32(CPUARMState, cp15.scr_el3), .writefn = scr_write }, { .name = "SDER32_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 1, .opc2 = 1, @@ -2744,18 +3743,10 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { .cp = 15, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 1, .access = PL3_RW, .resetvalue = 0, .fieldoffset = offsetoflow32(CPUARMState, cp15.sder) }, - /* TODO: Implement NSACR trapping of secure EL1 accesses to EL3 */ - { .name = "NSACR", .cp = 15, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 2, - .access = PL3_W | PL1_R, .resetvalue = 0, - .fieldoffset = offsetof(CPUARMState, cp15.nsacr) }, { .name = "MVBAR", .cp = 15, .opc1 = 0, .crn = 12, .crm = 0, .opc2 = 1, - .access = PL3_RW, .writefn = vbar_write, .resetvalue = 0, + .access = PL1_RW, .accessfn = access_trap_aa32s_el1, + .writefn = vbar_write, .resetvalue = 0, .fieldoffset = offsetof(CPUARMState, cp15.mvbar) }, - { .name = "SCTLR_EL3", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_ALIAS, /* reset handled by AArch32 view */ - .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 0, .opc2 = 0, - .access = PL3_RW, .raw_writefn = raw_write, .writefn = sctlr_write, - .fieldoffset = offsetof(CPUARMState, cp15.sctlr_el[3]) }, { .name = "TTBR0_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 2, .crm = 0, .opc2 = 0, .access = PL3_RW, .writefn = vmsa_ttbr_write, .resetvalue = 0, @@ -2771,7 +3762,6 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, elr_el[3]) }, { .name = "ESR_EL3", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 6, .crn = 5, .crm = 2, .opc2 = 0, .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[3]) }, { .name = "FAR_EL3", .state = ARM_CP_STATE_AA64, @@ -2780,7 +3770,8 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { { .name = "SPSR_EL3", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 6, .crn = 4, .crm = 0, .opc2 = 0, - .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, banked_spsr[7]) }, + .access = PL3_RW, + .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_MON]) }, { .name = "VBAR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 12, .crm = 0, .opc2 = 0, .access = PL3_RW, .writefn = vbar_write, @@ -2790,10 +3781,51 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 1, .opc2 = 2, .access = PL3_RW, .accessfn = cptr_access, .resetvalue = 0, .fieldoffset = offsetof(CPUARMState, cp15.cptr_el[3]) }, + { .name = "TPIDR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 13, .crm = 0, .opc2 = 2, + .access = PL3_RW, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.tpidr_el[3]) }, + { .name = "AMAIR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 10, .crm = 3, .opc2 = 0, + .access = PL3_RW, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "AFSR0_EL3", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 6, .crn = 5, .crm = 1, .opc2 = 0, + .access = PL3_RW, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "AFSR1_EL3", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 6, .crn = 5, .crm = 1, .opc2 = 1, + .access = PL3_RW, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "TLBI_ALLE3IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 3, .opc2 = 0, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_alle3is_write }, + { .name = "TLBI_VAE3IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 3, .opc2 = 1, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_vae3is_write }, + { .name = "TLBI_VALE3IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 3, .opc2 = 5, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_vae3is_write }, + { .name = "TLBI_ALLE3", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 0, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_alle3_write }, + { .name = "TLBI_VAE3", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 1, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_vae3_write }, + { .name = "TLBI_VALE3", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 5, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_vae3_write }, REGINFO_SENTINEL }; -static CPAccessResult ctr_el0_access(CPUARMState *env, const ARMCPRegInfo *ri) +static CPAccessResult ctr_el0_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { /* Only accessible in EL0 if SCTLR.UCT is set (and only in AArch64, * but the AArch32 CTR has its own reginfo struct) @@ -2804,6 +3836,23 @@ static CPAccessResult ctr_el0_access(CPUARMState *env, const ARMCPRegInfo *ri) return CP_ACCESS_OK; } +static void oslar_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Writes to OSLAR_EL1 may update the OS lock status, which can be + * read via a bit in OSLSR_EL1. + */ + int oslock; + + if (ri->state == ARM_CP_STATE_AA32) { + oslock = (value == 0xC5ACCE55); + } else { + oslock = value & 1; + } + + env->cp15.oslsr_el1 = deposit32(env->cp15.oslsr_el1, 1, 1, oslock); +} + static const ARMCPRegInfo debug_cp_reginfo[] = { /* DBGDRAR, DBGDSAR: always RAZ since we don't implement memory mapped * debug components. The AArch64 version of DBGDRAR is named MDRAR_EL1; @@ -2812,16 +3861,19 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { * accessor. */ { .name = "DBGDRAR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + .access = PL0_R, .accessfn = access_tdra, + .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "MDRAR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 0, - .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + .access = PL1_R, .accessfn = access_tdra, + .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "DBGDSAR", .cp = 14, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + .access = PL0_R, .accessfn = access_tdra, + .type = ARM_CP_CONST, .resetvalue = 0 }, /* Monitor debug system control register; the 32-bit alias is DBGDSCRext. */ { .name = "MDSCR_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2, - .access = PL1_RW, + .access = PL1_RW, .accessfn = access_tda, .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), .resetvalue = 0 }, /* MDCCSR_EL0, aka DBGDSCRint. This is a read-only mirror of MDSCR_EL1. @@ -2830,22 +3882,30 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { { .name = "MDCCSR_EL0", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0, .type = ARM_CP_ALIAS, - .access = PL1_R, + .access = PL1_R, .accessfn = access_tda, .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), }, - /* We define a dummy WI OSLAR_EL1, because Linux writes to it. */ { .name = "OSLAR_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 4, - .access = PL1_W, .type = ARM_CP_NOP }, + .access = PL1_W, .type = ARM_CP_NO_RAW, + .accessfn = access_tdosa, + .writefn = oslar_write }, + { .name = "OSLSR_EL1", .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 4, + .access = PL1_R, .resetvalue = 10, + .accessfn = access_tdosa, + .fieldoffset = offsetof(CPUARMState, cp15.oslsr_el1) }, /* Dummy OSDLR_EL1: 32-bit Linux will read this */ { .name = "OSDLR_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 3, .opc2 = 4, - .access = PL1_RW, .type = ARM_CP_NOP }, + .access = PL1_RW, .accessfn = access_tdosa, + .type = ARM_CP_NOP }, /* Dummy DBGVCR: Linux wants to clear this on startup, but we don't * implement vector catch debug events yet. */ { .name = "DBGVCR", .cp = 14, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_NOP }, + .access = PL1_RW, .accessfn = access_tda, + .type = ARM_CP_NOP }, REGINFO_SENTINEL }; @@ -3110,7 +4170,8 @@ static void define_debug_regs(ARMCPU *cpu) int wrps, brps, ctx_cmps; ARMCPRegInfo dbgdidr = { .name = "DBGDIDR", .cp = 14, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = cpu->dbgdidr, + .access = PL0_R, .accessfn = access_tda, + .type = ARM_CP_CONST, .resetvalue = cpu->dbgdidr, }; /* Note that all these register fields hold "number of Xs minus 1". */ @@ -3141,13 +4202,13 @@ static void define_debug_regs(ARMCPU *cpu) ARMCPRegInfo dbgregs[] = { { .name = "DBGBVR", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 4, - .access = PL1_RW, + .access = PL1_RW, .accessfn = access_tda, .fieldoffset = offsetof(CPUARMState, cp15.dbgbvr[i]), .writefn = dbgbvr_write, .raw_writefn = raw_write }, { .name = "DBGBCR", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 5, - .access = PL1_RW, + .access = PL1_RW, .accessfn = access_tda, .fieldoffset = offsetof(CPUARMState, cp15.dbgbcr[i]), .writefn = dbgbcr_write, .raw_writefn = raw_write }, @@ -3160,13 +4221,13 @@ static void define_debug_regs(ARMCPU *cpu) ARMCPRegInfo dbgregs[] = { { .name = "DBGWVR", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 6, - .access = PL1_RW, + .access = PL1_RW, .accessfn = access_tda, .fieldoffset = offsetof(CPUARMState, cp15.dbgwvr[i]), .writefn = dbgwvr_write, .raw_writefn = raw_write }, { .name = "DBGWCR", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 7, - .access = PL1_RW, + .access = PL1_RW, .accessfn = access_tda, .fieldoffset = offsetof(CPUARMState, cp15.dbgwcr[i]), .writefn = dbgwcr_write, .raw_writefn = raw_write }, @@ -3252,12 +4313,14 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_isar5 }, - /* 6..7 are as yet unallocated and must RAZ */ - { .name = "ID_ISAR6", .cp = 15, .crn = 0, .crm = 2, - .opc1 = 0, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST, - .resetvalue = 0 }, - { .name = "ID_ISAR7", .cp = 15, .crn = 0, .crm = 2, - .opc1 = 0, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, + { .name = "ID_MMFR4", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 6, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_mmfr4 }, + /* 7 is as yet unallocated and must RAZ */ + { .name = "ID_ISAR7_RESERVED", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 7, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, REGINFO_SENTINEL }; @@ -3311,7 +4374,11 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, not_v7_cp_reginfo); } if (arm_feature(env, ARM_FEATURE_V8)) { - /* AArch64 ID registers, which all have impdef reset values */ + /* AArch64 ID registers, which all have impdef reset values. + * Note that within the ID register ranges the unused slots + * must all RAZ, not UNDEF; future architecture versions may + * define new registers here. + */ ARMCPRegInfo v8_idregs[] = { { .name = "ID_AA64PFR0_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 0, @@ -3321,6 +4388,30 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_aa64pfr1}, + { .name = "ID_AA64PFR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 2, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "ID_AA64PFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 3, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "ID_AA64PFR4_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 4, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "ID_AA64PFR5_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 5, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "ID_AA64PFR6_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 6, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "ID_AA64PFR7_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 7, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, { .name = "ID_AA64DFR0_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, @@ -3334,6 +4425,14 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_aa64dfr1 }, + { .name = "ID_AA64DFR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 2, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "ID_AA64DFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 3, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, { .name = "ID_AA64AFR0_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, @@ -3342,6 +4441,14 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_aa64afr1 }, + { .name = "ID_AA64AFR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 6, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "ID_AA64AFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 7, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, { .name = "ID_AA64ISAR0_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, @@ -3350,6 +4457,30 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_aa64isar1 }, + { .name = "ID_AA64ISAR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 2, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "ID_AA64ISAR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 3, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "ID_AA64ISAR4_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 4, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "ID_AA64ISAR5_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 5, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "ID_AA64ISAR6_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 6, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "ID_AA64ISAR7_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 7, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, { .name = "ID_AA64MMFR0_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, @@ -3358,6 +4489,30 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_aa64mmfr1 }, + { .name = "ID_AA64MMFR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 2, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "ID_AA64MMFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 3, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "ID_AA64MMFR4_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 4, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "ID_AA64MMFR5_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 5, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "ID_AA64MMFR6_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 6, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "ID_AA64MMFR7_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 7, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, { .name = "MVFR0_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, @@ -3370,6 +4525,42 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->mvfr2 }, + { .name = "MVFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 3, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "MVFR4_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 4, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "MVFR5_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 5, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "MVFR6_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 6, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "MVFR7_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 7, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "PMCEID0", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 9, .crm = 12, .opc2 = 6, + .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .resetvalue = cpu->pmceid0 }, + { .name = "PMCEID0_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 6, + .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .resetvalue = cpu->pmceid0 }, + { .name = "PMCEID1", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 9, .crm = 12, .opc2 = 7, + .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .resetvalue = cpu->pmceid1 }, + { .name = "PMCEID1_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 7, + .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .resetvalue = cpu->pmceid1 }, REGINFO_SENTINEL }; /* RVBAR_EL1 is only implemented if EL1 is the highest EL */ @@ -3386,6 +4577,30 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, v8_cp_reginfo); } if (arm_feature(env, ARM_FEATURE_EL2)) { + uint64_t vmpidr_def = mpidr_read_val(env); + ARMCPRegInfo vpidr_regs[] = { + { .name = "VPIDR", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 0, + .access = PL2_RW, .accessfn = access_el3_aa32ns, + .resetvalue = cpu->midr, + .fieldoffset = offsetof(CPUARMState, cp15.vpidr_el2) }, + { .name = "VPIDR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 0, + .access = PL2_RW, .resetvalue = cpu->midr, + .fieldoffset = offsetof(CPUARMState, cp15.vpidr_el2) }, + { .name = "VMPIDR", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5, + .access = PL2_RW, .accessfn = access_el3_aa32ns, + .resetvalue = vmpidr_def, + .fieldoffset = offsetof(CPUARMState, cp15.vmpidr_el2) }, + { .name = "VMPIDR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5, + .access = PL2_RW, + .resetvalue = vmpidr_def, + .fieldoffset = offsetof(CPUARMState, cp15.vmpidr_el2) }, + REGINFO_SENTINEL + }; + define_arm_cp_regs(cpu, vpidr_regs); define_arm_cp_regs(cpu, el2_cp_reginfo); /* RVBAR_EL2 is only implemented if EL2 is the highest EL */ if (!arm_feature(env, ARM_FEATURE_EL3)) { @@ -3401,18 +4616,82 @@ void register_cp_regs_for_features(ARMCPU *cpu) * register the no_el2 reginfos. */ if (arm_feature(env, ARM_FEATURE_EL3)) { + /* When EL3 exists but not EL2, VPIDR and VMPIDR take the value + * of MIDR_EL1 and MPIDR_EL1. + */ + ARMCPRegInfo vpidr_regs[] = { + { .name = "VPIDR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 0, + .access = PL2_RW, .accessfn = access_el3_aa32ns_aa64any, + .type = ARM_CP_CONST, .resetvalue = cpu->midr, + .fieldoffset = offsetof(CPUARMState, cp15.vpidr_el2) }, + { .name = "VMPIDR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5, + .access = PL2_RW, .accessfn = access_el3_aa32ns_aa64any, + .type = ARM_CP_NO_RAW, + .writefn = arm_cp_write_ignore, .readfn = mpidr_read }, + REGINFO_SENTINEL + }; + define_arm_cp_regs(cpu, vpidr_regs); define_arm_cp_regs(cpu, el3_no_el2_cp_reginfo); } } if (arm_feature(env, ARM_FEATURE_EL3)) { define_arm_cp_regs(cpu, el3_cp_reginfo); - ARMCPRegInfo rvbar = { - .name = "RVBAR_EL3", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 6, .crn = 12, .crm = 0, .opc2 = 1, - .type = ARM_CP_CONST, .access = PL3_R, .resetvalue = cpu->rvbar + ARMCPRegInfo el3_regs[] = { + { .name = "RVBAR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 12, .crm = 0, .opc2 = 1, + .type = ARM_CP_CONST, .access = PL3_R, .resetvalue = cpu->rvbar }, + { .name = "SCTLR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 0, .opc2 = 0, + .access = PL3_RW, + .raw_writefn = raw_write, .writefn = sctlr_write, + .fieldoffset = offsetof(CPUARMState, cp15.sctlr_el[3]), + .resetvalue = cpu->reset_sctlr }, + REGINFO_SENTINEL }; - define_one_arm_cp_reg(cpu, &rvbar); + + define_arm_cp_regs(cpu, el3_regs); } + /* The behaviour of NSACR is sufficiently various that we don't + * try to describe it in a single reginfo: + * if EL3 is 64 bit, then trap to EL3 from S EL1, + * reads as constant 0xc00 from NS EL1 and NS EL2 + * if EL3 is 32 bit, then RW at EL3, RO at NS EL1 and NS EL2 + * if v7 without EL3, register doesn't exist + * if v8 without EL3, reads as constant 0xc00 from NS EL1 and NS EL2 + */ + if (arm_feature(env, ARM_FEATURE_EL3)) { + if (arm_feature(env, ARM_FEATURE_AARCH64)) { + ARMCPRegInfo nsacr = { + .name = "NSACR", .type = ARM_CP_CONST, + .cp = 15, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 2, + .access = PL1_RW, .accessfn = nsacr_access, + .resetvalue = 0xc00 + }; + define_one_arm_cp_reg(cpu, &nsacr); + } else { + ARMCPRegInfo nsacr = { + .name = "NSACR", + .cp = 15, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 2, + .access = PL3_RW | PL1_R, + .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.nsacr) + }; + define_one_arm_cp_reg(cpu, &nsacr); + } + } else { + if (arm_feature(env, ARM_FEATURE_V8)) { + ARMCPRegInfo nsacr = { + .name = "NSACR", .type = ARM_CP_CONST, + .cp = 15, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 2, + .access = PL1_R, + .resetvalue = 0xc00 + }; + define_one_arm_cp_reg(cpu, &nsacr); + } + } + if (arm_feature(env, ARM_FEATURE_MPU)) { if (arm_feature(env, ARM_FEATURE_V6)) { /* PMSAv6 not implemented */ @@ -3478,6 +4757,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = CP_ANY, .access = PL1_R, .resetvalue = cpu->midr, .writefn = arm_cp_write_ignore, .raw_writefn = raw_write, + .readfn = midr_read, .fieldoffset = offsetof(CPUARMState, cp15.c0_cpuid), .type = ARM_CP_OVERRIDE }, /* crn = 0 op1 = 0 crm = 3..7 : currently unassigned; we RAZ. */ @@ -3501,7 +4781,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) ARMCPRegInfo id_v8_midr_cp_reginfo[] = { { .name = "MIDR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 0, .opc2 = 0, - .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->midr }, + .access = PL1_R, .type = ARM_CP_NO_RAW, .resetvalue = cpu->midr, + .fieldoffset = offsetof(CPUARMState, cp15.c0_cpuid), + .readfn = midr_read }, /* crn = 0 op1 = 0 crm = 0 op2 = 4,7 : AArch32 aliases of MIDR */ { .name = "MIDR", .type = ARM_CP_ALIAS | ARM_CP_CONST, .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 4, @@ -3584,13 +4866,22 @@ void register_cp_regs_for_features(ARMCPU *cpu) } if (arm_feature(env, ARM_FEATURE_AUXCR)) { - ARMCPRegInfo auxcr = { - .name = "ACTLR_EL1", .state = ARM_CP_STATE_BOTH, - .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 1, - .access = PL1_RW, .type = ARM_CP_CONST, - .resetvalue = cpu->reset_auxcr + ARMCPRegInfo auxcr_reginfo[] = { + { .name = "ACTLR_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, + .resetvalue = cpu->reset_auxcr }, + { .name = "ACTLR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 1, + .access = PL2_RW, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "ACTLR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 0, .opc2 = 1, + .access = PL3_RW, .type = ARM_CP_CONST, + .resetvalue = 0 }, + REGINFO_SENTINEL }; - define_one_arm_cp_reg(cpu, &auxcr); + define_arm_cp_regs(cpu, auxcr_reginfo); } if (arm_feature(env, ARM_FEATURE_CBAR)) { @@ -4064,23 +5355,47 @@ void arm_cp_reset_ignore(CPUARMState *env, const ARMCPRegInfo *opaque) /* Helper coprocessor reset function for do-nothing-on-reset registers */ } -static int bad_mode_switch(CPUARMState *env, int mode) +static int bad_mode_switch(CPUARMState *env, int mode, CPSRWriteType write_type) { /* Return true if it is not valid for us to switch to * this CPU mode (ie all the UNPREDICTABLE cases in * the ARM ARM CPSRWriteByInstr pseudocode). */ + + /* Changes to or from Hyp via MSR and CPS are illegal. */ + if (write_type == CPSRWriteByInstr && + ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_HYP || + mode == ARM_CPU_MODE_HYP)) { + return 1; + } + switch (mode) { case ARM_CPU_MODE_USR: + return 0; case ARM_CPU_MODE_SYS: case ARM_CPU_MODE_SVC: case ARM_CPU_MODE_ABT: case ARM_CPU_MODE_UND: case ARM_CPU_MODE_IRQ: case ARM_CPU_MODE_FIQ: + /* Note that we don't implement the IMPDEF NSACR.RFR which in v7 + * allows FIQ mode to be Secure-only. (In v8 this doesn't exist.) + */ + /* If HCR.TGE is set then changes from Monitor to NS PL1 via MSR + * and CPS are treated as illegal mode changes. + */ + if (write_type == CPSRWriteByInstr && + (env->cp15.hcr_el2 & HCR_TGE) && + (env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_MON && + !arm_is_secure_below_el3(env)) { + return 1; + } return 0; + case ARM_CPU_MODE_HYP: + return !arm_feature(env, ARM_FEATURE_EL2) + || arm_current_el(env) < 2 || arm_is_secure(env); case ARM_CPU_MODE_MON: - return !arm_is_secure(env); + return arm_current_el(env) < 3; default: return 1; } @@ -4097,7 +5412,8 @@ uint32_t cpsr_read(CPUARMState *env) | (env->GE << 16) | (env->daif & CPSR_AIF); } -void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) +void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, + CPSRWriteType write_type) { uint32_t changed_daif; @@ -4131,7 +5447,7 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) * In a V8 implementation, it is permitted for privileged software to * change the CPSR A/F bits regardless of the SCR.AW/FW bits. */ - if (!arm_feature(env, ARM_FEATURE_V8) && + if (write_type != CPSRWriteRaw && !arm_feature(env, ARM_FEATURE_V8) && arm_feature(env, ARM_FEATURE_EL3) && !arm_feature(env, ARM_FEATURE_EL2) && !arm_is_secure(env)) { @@ -4178,13 +5494,31 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) env->daif &= ~(CPSR_AIF & mask); env->daif |= val & CPSR_AIF & mask; - if ((env->uncached_cpsr ^ val) & mask & CPSR_M) { - if (bad_mode_switch(env, val & CPSR_M)) { - /* Attempt to switch to an invalid mode: this is UNPREDICTABLE. - * We choose to ignore the attempt and leave the CPSR M field - * untouched. + if (write_type != CPSRWriteRaw && + ((env->uncached_cpsr ^ val) & mask & CPSR_M)) { + if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR) { + /* Note that we can only get here in USR mode if this is a + * gdb stub write; for this case we follow the architectural + * behaviour for guest writes in USR mode of ignoring an attempt + * to switch mode. (Those are caught by translate.c for writes + * triggered by guest instructions.) + */ + mask &= ~CPSR_M; + } else if (bad_mode_switch(env, val & CPSR_M, write_type)) { + /* Attempt to switch to an invalid mode: this is UNPREDICTABLE in + * v7, and has defined behaviour in v8: + * + leave CPSR.M untouched + * + allow changes to the other CPSR fields + * + set PSTATE.IL + * For user changes via the GDB stub, we don't set PSTATE.IL, + * as this would be unnecessarily harsh for a user error. */ mask &= ~CPSR_M; + if (write_type != CPSRWriteByGDBStub && + arm_feature(env, ARM_FEATURE_V8)) { + mask |= CPSR_IL; + val |= CPSR_IL; + } } else { switch_mode(env, val & CPSR_M); } @@ -4233,17 +5567,7 @@ uint32_t HELPER(udiv)(uint32_t num, uint32_t den) uint32_t HELPER(rbit)(uint32_t x) { - x = ((x & 0xff000000) >> 24) - | ((x & 0x00ff0000) >> 8) - | ((x & 0x0000ff00) << 8) - | ((x & 0x000000ff) << 24); - x = ((x & 0xf0f0f0f0) >> 4) - | ((x & 0x0f0f0f0f) << 4); - x = ((x & 0x88888888) >> 3) - | ((x & 0x44444444) >> 1) - | ((x & 0x22222222) << 1) - | ((x & 0x11111111) << 3); - return x; + return revbit32(x); } #if defined(CONFIG_USER_ONLY) @@ -4273,21 +5597,6 @@ void switch_mode(CPUARMState *env, int mode) } } -void HELPER(set_r13_banked)(CPUARMState *env, uint32_t mode, uint32_t val) -{ - ARMCPU *cpu = arm_env_get_cpu(env); - - cpu_abort(CPU(cpu), "banked r13 write\n"); -} - -uint32_t HELPER(get_r13_banked)(CPUARMState *env, uint32_t mode) -{ - ARMCPU *cpu = arm_env_get_cpu(env); - - cpu_abort(CPU(cpu), "banked r13 read\n"); - return 0; -} - uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, uint32_t cur_el, bool secure) { @@ -4301,31 +5610,6 @@ void aarch64_sync_64_to_32(CPUARMState *env) #else -/* Map CPU modes onto saved register banks. */ -int bank_number(int mode) -{ - switch (mode) { - case ARM_CPU_MODE_USR: - case ARM_CPU_MODE_SYS: - return 0; - case ARM_CPU_MODE_SVC: - return 1; - case ARM_CPU_MODE_ABT: - return 2; - case ARM_CPU_MODE_UND: - return 3; - case ARM_CPU_MODE_IRQ: - return 4; - case ARM_CPU_MODE_FIQ: - return 5; - case ARM_CPU_MODE_HYP: - return 6; - case ARM_CPU_MODE_MON: - return 7; - } - hw_error("bank number requested for bad CPSR mode value 0x%x\n", mode); -} - void switch_mode(CPUARMState *env, int mode) { int old_mode; @@ -4391,7 +5675,7 @@ void switch_mode(CPUARMState *env, int mode) * BIT IRQ IMO Non-secure Secure * EL3 FIQ RW FMO EL0 EL1 EL2 EL3 EL0 EL1 EL2 EL3 */ -const int8_t target_el_table[2][2][2][2][2][4] = { +static const int8_t target_el_table[2][2][2][2][2][4] = { {{{{/* 0 0 0 0 */{ 1, 1, 2, -1 },{ 3, -1, -1, 3 },}, {/* 0 0 0 1 */{ 2, 2, 2, -1 },{ 3, -1, -1, 3 },},}, {{/* 0 0 1 0 */{ 1, 1, 2, -1 },{ 3, -1, -1, 3 },}, @@ -4417,11 +5701,22 @@ uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, uint32_t cur_el, bool secure) { CPUARMState *env = cs->env_ptr; - int rw = ((env->cp15.scr_el3 & SCR_RW) == SCR_RW); + int rw; int scr; int hcr; int target_el; - int is64 = arm_el_is_aa64(env, 3); + /* Is the highest EL AArch64? */ + int is64 = arm_feature(env, ARM_FEATURE_AARCH64); + + if (arm_feature(env, ARM_FEATURE_EL3)) { + rw = ((env->cp15.scr_el3 & SCR_RW) == SCR_RW); + } else { + /* Either EL2 is the highest EL (and so the EL2 register width + * is given by is64); or there is no EL2 or EL3, in which case + * the value of 'rw' does not affect the table lookup anyway. + */ + rw = is64; + } switch (excp_idx) { case EXCP_IRQ: @@ -4558,11 +5853,13 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) case EXCP_BKPT: if (semihosting_enabled()) { int nr; - nr = arm_lduw_code(env, env->regs[15], env->bswap_code) & 0xff; + nr = arm_lduw_code(env, env->regs[15], arm_sctlr_b(env)) & 0xff; if (nr == 0xab) { env->regs[15] += 2; + qemu_log_mask(CPU_LOG_INT, + "...handling as semihosting call 0x%x\n", + env->regs[0]); env->regs[0] = do_arm_semihosting(env); - qemu_log_mask(CPU_LOG_INT, "...handled as semihosting call\n"); return; } } @@ -4655,35 +5952,35 @@ void aarch64_sync_32_to_64(CPUARMState *env) } if (mode == ARM_CPU_MODE_IRQ) { - env->xregs[16] = env->regs[13]; - env->xregs[17] = env->regs[14]; + env->xregs[16] = env->regs[14]; + env->xregs[17] = env->regs[13]; } else { - env->xregs[16] = env->banked_r13[bank_number(ARM_CPU_MODE_IRQ)]; - env->xregs[17] = env->banked_r14[bank_number(ARM_CPU_MODE_IRQ)]; + env->xregs[16] = env->banked_r14[bank_number(ARM_CPU_MODE_IRQ)]; + env->xregs[17] = env->banked_r13[bank_number(ARM_CPU_MODE_IRQ)]; } if (mode == ARM_CPU_MODE_SVC) { - env->xregs[18] = env->regs[13]; - env->xregs[19] = env->regs[14]; + env->xregs[18] = env->regs[14]; + env->xregs[19] = env->regs[13]; } else { - env->xregs[18] = env->banked_r13[bank_number(ARM_CPU_MODE_SVC)]; - env->xregs[19] = env->banked_r14[bank_number(ARM_CPU_MODE_SVC)]; + env->xregs[18] = env->banked_r14[bank_number(ARM_CPU_MODE_SVC)]; + env->xregs[19] = env->banked_r13[bank_number(ARM_CPU_MODE_SVC)]; } if (mode == ARM_CPU_MODE_ABT) { - env->xregs[20] = env->regs[13]; - env->xregs[21] = env->regs[14]; + env->xregs[20] = env->regs[14]; + env->xregs[21] = env->regs[13]; } else { - env->xregs[20] = env->banked_r13[bank_number(ARM_CPU_MODE_ABT)]; - env->xregs[21] = env->banked_r14[bank_number(ARM_CPU_MODE_ABT)]; + env->xregs[20] = env->banked_r14[bank_number(ARM_CPU_MODE_ABT)]; + env->xregs[21] = env->banked_r13[bank_number(ARM_CPU_MODE_ABT)]; } if (mode == ARM_CPU_MODE_UND) { - env->xregs[22] = env->regs[13]; - env->xregs[23] = env->regs[14]; + env->xregs[22] = env->regs[14]; + env->xregs[23] = env->regs[13]; } else { - env->xregs[22] = env->banked_r13[bank_number(ARM_CPU_MODE_UND)]; - env->xregs[23] = env->banked_r14[bank_number(ARM_CPU_MODE_UND)]; + env->xregs[22] = env->banked_r14[bank_number(ARM_CPU_MODE_UND)]; + env->xregs[23] = env->banked_r13[bank_number(ARM_CPU_MODE_UND)]; } /* Registers x24-x30 are mapped to r8-r14 in FIQ mode. If we are in FIQ @@ -4760,35 +6057,35 @@ void aarch64_sync_64_to_32(CPUARMState *env) } if (mode == ARM_CPU_MODE_IRQ) { - env->regs[13] = env->xregs[16]; - env->regs[14] = env->xregs[17]; + env->regs[14] = env->xregs[16]; + env->regs[13] = env->xregs[17]; } else { - env->banked_r13[bank_number(ARM_CPU_MODE_IRQ)] = env->xregs[16]; - env->banked_r14[bank_number(ARM_CPU_MODE_IRQ)] = env->xregs[17]; + env->banked_r14[bank_number(ARM_CPU_MODE_IRQ)] = env->xregs[16]; + env->banked_r13[bank_number(ARM_CPU_MODE_IRQ)] = env->xregs[17]; } if (mode == ARM_CPU_MODE_SVC) { - env->regs[13] = env->xregs[18]; - env->regs[14] = env->xregs[19]; + env->regs[14] = env->xregs[18]; + env->regs[13] = env->xregs[19]; } else { - env->banked_r13[bank_number(ARM_CPU_MODE_SVC)] = env->xregs[18]; - env->banked_r14[bank_number(ARM_CPU_MODE_SVC)] = env->xregs[19]; + env->banked_r14[bank_number(ARM_CPU_MODE_SVC)] = env->xregs[18]; + env->banked_r13[bank_number(ARM_CPU_MODE_SVC)] = env->xregs[19]; } if (mode == ARM_CPU_MODE_ABT) { - env->regs[13] = env->xregs[20]; - env->regs[14] = env->xregs[21]; + env->regs[14] = env->xregs[20]; + env->regs[13] = env->xregs[21]; } else { - env->banked_r13[bank_number(ARM_CPU_MODE_ABT)] = env->xregs[20]; - env->banked_r14[bank_number(ARM_CPU_MODE_ABT)] = env->xregs[21]; + env->banked_r14[bank_number(ARM_CPU_MODE_ABT)] = env->xregs[20]; + env->banked_r13[bank_number(ARM_CPU_MODE_ABT)] = env->xregs[21]; } if (mode == ARM_CPU_MODE_UND) { - env->regs[13] = env->xregs[22]; - env->regs[14] = env->xregs[23]; + env->regs[14] = env->xregs[22]; + env->regs[13] = env->xregs[23]; } else { - env->banked_r13[bank_number(ARM_CPU_MODE_UND)] = env->xregs[22]; - env->banked_r14[bank_number(ARM_CPU_MODE_UND)] = env->xregs[23]; + env->banked_r14[bank_number(ARM_CPU_MODE_UND)] = env->xregs[22]; + env->banked_r13[bank_number(ARM_CPU_MODE_UND)] = env->xregs[23]; } /* Registers x24-x30 are mapped to r8-r14 in FIQ mode. If we are in FIQ @@ -4810,8 +6107,7 @@ void aarch64_sync_64_to_32(CPUARMState *env) env->regs[15] = env->pc; } -/* Handle a CPU exception. */ -void arm_cpu_do_interrupt(CPUState *cs) +static void arm_cpu_do_interrupt_aarch32(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; @@ -4821,16 +6117,6 @@ void arm_cpu_do_interrupt(CPUState *cs) uint32_t offset; uint32_t moe; - assert(!IS_M(env)); - - arm_log_exception(cs->exception_index); - - if (arm_is_psci_call(cpu, cs->exception_index)) { - arm_handle_psci_call(cpu); - qemu_log_mask(CPU_LOG_INT, "...handled as PSCI call\n"); - return; - } - /* If this is a debug exception we must update the DBGDSCR.MOE bits */ switch (env->exception.syndrome >> ARM_EL_EC_SHIFT) { case EC_BREAKPOINT: @@ -4858,35 +6144,16 @@ void arm_cpu_do_interrupt(CPUState *cs) /* TODO: Vectored interrupt controller. */ switch (cs->exception_index) { - case EXCP_UDEF: - new_mode = ARM_CPU_MODE_UND; - addr = 0x04; - mask = CPSR_I; - if (env->thumb) - offset = 2; - else - offset = 4; - break; - case EXCP_SWI: - if (semihosting_enabled()) { - /* Check for semihosting interrupt. */ - if (env->thumb) { - mask = arm_lduw_code(env, env->regs[15] - 2, env->bswap_code) - & 0xff; - } else { - mask = arm_ldl_code(env, env->regs[15] - 4, env->bswap_code) - & 0xffffff; - } - /* Only intercept calls from privileged modes, to provide some - semblance of security. */ - if (((mask == 0x123456 && !env->thumb) - || (mask == 0xab && env->thumb)) - && (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) { - env->regs[0] = do_arm_semihosting(env); - qemu_log_mask(CPU_LOG_INT, "...handled as semihosting call\n"); - return; - } - } + case EXCP_UDEF: + new_mode = ARM_CPU_MODE_UND; + addr = 0x04; + mask = CPSR_I; + if (env->thumb) + offset = 2; + else + offset = 4; + break; + case EXCP_SWI: new_mode = ARM_CPU_MODE_SVC; addr = 0x08; mask = CPSR_I; @@ -4894,17 +6161,6 @@ void arm_cpu_do_interrupt(CPUState *cs) offset = 0; break; case EXCP_BKPT: - /* See if this is a semihosting syscall. */ - if (env->thumb && semihosting_enabled()) { - mask = arm_lduw_code(env, env->regs[15], env->bswap_code) & 0xff; - if (mask == 0xab - && (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) { - env->regs[15] += 2; - env->regs[0] = do_arm_semihosting(env); - qemu_log_mask(CPU_LOG_INT, "...handled as semihosting call\n"); - return; - } - } env->exception.fsr = 2; /* Fall through to prefetch abort. */ case EXCP_PREFETCH_ABORT: @@ -4990,6 +6246,11 @@ void arm_cpu_do_interrupt(CPUState *cs) env->condexec_bits = 0; /* Switch to the new mode, and to the correct instruction set. */ env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode; + /* Set new mode endianness */ + env->uncached_cpsr &= ~CPSR_E; + if (env->cp15.sctlr_el[arm_current_el(env)] & SCTLR_EE) { + env->uncached_cpsr |= ~CPSR_E; + } env->daif |= mask; /* this is a lie, as the was no c1_sys on V4T/V5, but who cares * and we should just guard the thumb mode on V4 */ @@ -4998,9 +6259,227 @@ void arm_cpu_do_interrupt(CPUState *cs) } env->regs[14] = env->regs[15] + offset; env->regs[15] = addr; - cs->interrupt_request |= CPU_INTERRUPT_EXITTB; } +/* Handle exception entry to a target EL which is using AArch64 */ +static void arm_cpu_do_interrupt_aarch64(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + unsigned int new_el = env->exception.target_el; + target_ulong addr = env->cp15.vbar_el[new_el]; + unsigned int new_mode = aarch64_pstate_mode(new_el, true); + + if (arm_current_el(env) < new_el) { + /* Entry vector offset depends on whether the implemented EL + * immediately lower than the target level is using AArch32 or AArch64 + */ + bool is_aa64; + + switch (new_el) { + case 3: + is_aa64 = (env->cp15.scr_el3 & SCR_RW) != 0; + break; + case 2: + is_aa64 = (env->cp15.hcr_el2 & HCR_RW) != 0; + break; + case 1: + is_aa64 = is_a64(env); + break; + default: + g_assert_not_reached(); + } + + if (is_aa64) { + addr += 0x400; + } else { + addr += 0x600; + } + } else if (pstate_read(env) & PSTATE_SP) { + addr += 0x200; + } + + switch (cs->exception_index) { + case EXCP_PREFETCH_ABORT: + case EXCP_DATA_ABORT: + env->cp15.far_el[new_el] = env->exception.vaddress; + qemu_log_mask(CPU_LOG_INT, "...with FAR 0x%" PRIx64 "\n", + env->cp15.far_el[new_el]); + /* fall through */ + case EXCP_BKPT: + case EXCP_UDEF: + case EXCP_SWI: + case EXCP_HVC: + case EXCP_HYP_TRAP: + case EXCP_SMC: + env->cp15.esr_el[new_el] = env->exception.syndrome; + break; + case EXCP_IRQ: + case EXCP_VIRQ: + addr += 0x80; + break; + case EXCP_FIQ: + case EXCP_VFIQ: + addr += 0x100; + break; + case EXCP_SEMIHOST: + qemu_log_mask(CPU_LOG_INT, + "...handling as semihosting call 0x%" PRIx64 "\n", + env->xregs[0]); + env->xregs[0] = do_arm_semihosting(env); + return; + default: + cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index); + } + + if (is_a64(env)) { + env->banked_spsr[aarch64_banked_spsr_index(new_el)] = pstate_read(env); + aarch64_save_sp(env, arm_current_el(env)); + env->elr_el[new_el] = env->pc; + } else { + env->banked_spsr[aarch64_banked_spsr_index(new_el)] = cpsr_read(env); + if (!env->thumb) { + env->cp15.esr_el[new_el] |= 1 << 25; + } + env->elr_el[new_el] = env->regs[15]; + + aarch64_sync_32_to_64(env); + + env->condexec_bits = 0; + } + qemu_log_mask(CPU_LOG_INT, "...with ELR 0x%" PRIx64 "\n", + env->elr_el[new_el]); + + pstate_write(env, PSTATE_DAIF | new_mode); + env->aarch64 = 1; + aarch64_restore_sp(env, new_el); + + env->pc = addr; + + qemu_log_mask(CPU_LOG_INT, "...to EL%d PC 0x%" PRIx64 " PSTATE 0x%x\n", + new_el, env->pc, pstate_read(env)); +} + +static inline bool check_for_semihosting(CPUState *cs) +{ + /* Check whether this exception is a semihosting call; if so + * then handle it and return true; otherwise return false. + */ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + if (is_a64(env)) { + if (cs->exception_index == EXCP_SEMIHOST) { + /* This is always the 64-bit semihosting exception. + * The "is this usermode" and "is semihosting enabled" + * checks have been done at translate time. + */ + qemu_log_mask(CPU_LOG_INT, + "...handling as semihosting call 0x%" PRIx64 "\n", + env->xregs[0]); + env->xregs[0] = do_arm_semihosting(env); + return true; + } + return false; + } else { + uint32_t imm; + + /* Only intercept calls from privileged modes, to provide some + * semblance of security. + */ + if (!semihosting_enabled() || + ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR)) { + return false; + } + + switch (cs->exception_index) { + case EXCP_SWI: + /* Check for semihosting interrupt. */ + if (env->thumb) { + imm = arm_lduw_code(env, env->regs[15] - 2, arm_sctlr_b(env)) + & 0xff; + if (imm == 0xab) { + break; + } + } else { + imm = arm_ldl_code(env, env->regs[15] - 4, arm_sctlr_b(env)) + & 0xffffff; + if (imm == 0x123456) { + break; + } + } + return false; + case EXCP_BKPT: + /* See if this is a semihosting syscall. */ + if (env->thumb) { + imm = arm_lduw_code(env, env->regs[15], arm_sctlr_b(env)) + & 0xff; + if (imm == 0xab) { + env->regs[15] += 2; + break; + } + } + return false; + default: + return false; + } + + qemu_log_mask(CPU_LOG_INT, + "...handling as semihosting call 0x%x\n", + env->regs[0]); + env->regs[0] = do_arm_semihosting(env); + return true; + } +} + +/* Handle a CPU exception for A and R profile CPUs. + * Do any appropriate logging, handle PSCI calls, and then hand off + * to the AArch64-entry or AArch32-entry function depending on the + * target exception level's register width. + */ +void arm_cpu_do_interrupt(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + unsigned int new_el = env->exception.target_el; + + assert(!IS_M(env)); + + arm_log_exception(cs->exception_index); + qemu_log_mask(CPU_LOG_INT, "...from EL%d to EL%d\n", arm_current_el(env), + new_el); + if (qemu_loglevel_mask(CPU_LOG_INT) + && !excp_is_internal(cs->exception_index)) { + qemu_log_mask(CPU_LOG_INT, "...with ESR %x/0x%" PRIx32 "\n", + env->exception.syndrome >> ARM_EL_EC_SHIFT, + env->exception.syndrome); + } + + if (arm_is_psci_call(cpu, cs->exception_index)) { + arm_handle_psci_call(cpu); + qemu_log_mask(CPU_LOG_INT, "...handled as PSCI call\n"); + return; + } + + /* Semihosting semantics depend on the register width of the + * code that caused the exception, not the target exception level, + * so must be handled here. + */ + if (check_for_semihosting(cs)) { + return; + } + + assert(!excp_is_internal(cs->exception_index)); + if (arm_el_is_aa64(env, new_el)) { + arm_cpu_do_interrupt_aarch64(cs); + } else { + arm_cpu_do_interrupt_aarch32(cs); + } + + if (!kvm_enabled()) { + cs->interrupt_request |= CPU_INTERRUPT_EXITTB; + } +} /* Return the exception level which controls this address translation regime */ static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx) @@ -5058,12 +6537,17 @@ static inline bool regime_translation_disabled(CPUARMState *env, return (regime_sctlr(env, mmu_idx) & SCTLR_M) == 0; } +static inline bool regime_translation_big_endian(CPUARMState *env, + ARMMMUIdx mmu_idx) +{ + return (regime_sctlr(env, mmu_idx) & SCTLR_EE) != 0; +} + /* Return the TCR controlling this translation regime */ static inline TCR *regime_tcr(CPUARMState *env, ARMMMUIdx mmu_idx) { if (mmu_idx == ARMMMUIdx_S2NS) { - /* TODO: return VTCR_EL2 */ - g_assert_not_reached(); + return &env->cp15.vtcr_el2; } return &env->cp15.tcr_el[regime_el(env, mmu_idx)]; } @@ -5073,8 +6557,7 @@ static inline uint64_t regime_ttbr(CPUARMState *env, ARMMMUIdx mmu_idx, int ttbrn) { if (mmu_idx == ARMMMUIdx_S2NS) { - /* TODO: return VTTBR_EL2 */ - g_assert_not_reached(); + return env->cp15.vttbr_el2; } if (ttbrn == 0) { return env->cp15.ttbr0_el[regime_el(env, mmu_idx)]; @@ -5098,6 +6581,18 @@ static inline bool regime_using_lpae_format(CPUARMState *env, return false; } +/* Returns true if the stage 1 translation regime is using LPAE format page + * tables. Used when raising alignment exceptions, whose FSR changes depending + * on whether the long or short descriptor format is in use. */ +bool arm_s1_regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx) +{ + if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) { + mmu_idx += ARMMMUIdx_S1NSE0; + } + + return regime_using_lpae_format(env, mmu_idx); +} + static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx) { switch (mmu_idx) { @@ -5196,6 +6691,28 @@ simple_ap_to_rw_prot(CPUARMState *env, ARMMMUIdx mmu_idx, int ap) return simple_ap_to_rw_prot_is_user(ap, regime_is_user(env, mmu_idx)); } +/* Translate S2 section/page access permissions to protection flags + * + * @env: CPUARMState + * @s2ap: The 2-bit stage2 access permissions (S2AP) + * @xn: XN (execute-never) bit + */ +static int get_S2prot(CPUARMState *env, int s2ap, int xn) +{ + int prot = 0; + + if (s2ap & 1) { + prot |= PAGE_READ; + } + if (s2ap & 2) { + prot |= PAGE_WRITE; + } + if (!xn) { + prot |= PAGE_EXEC; + } + return prot; +} + /* Translate section/page access permissions to protection flags * * @env: CPUARMState @@ -5300,6 +6817,32 @@ static bool get_level1_table_address(CPUARMState *env, ARMMMUIdx mmu_idx, return true; } +/* Translate a S1 pagetable walk through S2 if needed. */ +static hwaddr S1_ptw_translate(CPUARMState *env, ARMMMUIdx mmu_idx, + hwaddr addr, MemTxAttrs txattrs, + uint32_t *fsr, + ARMMMUFaultInfo *fi) +{ + if ((mmu_idx == ARMMMUIdx_S1NSE0 || mmu_idx == ARMMMUIdx_S1NSE1) && + !regime_translation_disabled(env, ARMMMUIdx_S2NS)) { + target_ulong s2size; + hwaddr s2pa; + int s2prot; + int ret; + + ret = get_phys_addr_lpae(env, addr, 0, ARMMMUIdx_S2NS, &s2pa, + &txattrs, &s2prot, &s2size, fsr, fi); + if (ret) { + fi->s2addr = addr; + fi->stage2 = true; + fi->s1ptw = true; + return ~0; + } + addr = s2pa; + } + return addr; +} + /* All loads done in the course of a page table walk go through here. * TODO: rather than ignoring errors from physical memory reads (which * are external aborts in ARM terminology) we should propagate this @@ -5307,26 +6850,55 @@ static bool get_level1_table_address(CPUARMState *env, ARMMMUIdx mmu_idx, * was being done for a CPU load/store or an address translation instruction * (but not if it was for a debug access). */ -static uint32_t arm_ldl_ptw(CPUState *cs, hwaddr addr, bool is_secure) +static uint32_t arm_ldl_ptw(CPUState *cs, hwaddr addr, bool is_secure, + ARMMMUIdx mmu_idx, uint32_t *fsr, + ARMMMUFaultInfo *fi) { + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; MemTxAttrs attrs = {}; + AddressSpace *as; attrs.secure = is_secure; - return address_space_ldl(cs->as, addr, attrs, NULL); + as = arm_addressspace(cs, attrs); + addr = S1_ptw_translate(env, mmu_idx, addr, attrs, fsr, fi); + if (fi->s1ptw) { + return 0; + } + if (regime_translation_big_endian(env, mmu_idx)) { + return address_space_ldl_be(as, addr, attrs, NULL); + } else { + return address_space_ldl_le(as, addr, attrs, NULL); + } } -static uint64_t arm_ldq_ptw(CPUState *cs, hwaddr addr, bool is_secure) +static uint64_t arm_ldq_ptw(CPUState *cs, hwaddr addr, bool is_secure, + ARMMMUIdx mmu_idx, uint32_t *fsr, + ARMMMUFaultInfo *fi) { + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; MemTxAttrs attrs = {}; + AddressSpace *as; attrs.secure = is_secure; - return address_space_ldq(cs->as, addr, attrs, NULL); + as = arm_addressspace(cs, attrs); + addr = S1_ptw_translate(env, mmu_idx, addr, attrs, fsr, fi); + if (fi->s1ptw) { + return 0; + } + if (regime_translation_big_endian(env, mmu_idx)) { + return address_space_ldq_be(as, addr, attrs, NULL); + } else { + return address_space_ldq_le(as, addr, attrs, NULL); + } } static bool get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type, ARMMMUIdx mmu_idx, hwaddr *phys_ptr, int *prot, - target_ulong *page_size, uint32_t *fsr) + target_ulong *page_size, uint32_t *fsr, + ARMMMUFaultInfo *fi) { CPUState *cs = CPU(arm_env_get_cpu(env)); int code; @@ -5346,7 +6918,8 @@ static bool get_phys_addr_v5(CPUARMState *env, uint32_t address, code = 5; goto do_fault; } - desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx)); + desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx), + mmu_idx, fsr, fi); type = (desc & 3); domain = (desc >> 5) & 0x0f; if (regime_el(env, mmu_idx) == 1) { @@ -5382,7 +6955,8 @@ static bool get_phys_addr_v5(CPUARMState *env, uint32_t address, /* Fine pagetable. */ table = (desc & 0xfffff000) | ((address >> 8) & 0xffc); } - desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx)); + desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx), + mmu_idx, fsr, fi); switch (desc & 3) { case 0: /* Page translation fault. */ code = 7; @@ -5439,7 +7013,8 @@ do_fault: static bool get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type, ARMMMUIdx mmu_idx, hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot, - target_ulong *page_size, uint32_t *fsr) + target_ulong *page_size, uint32_t *fsr, + ARMMMUFaultInfo *fi) { CPUState *cs = CPU(arm_env_get_cpu(env)); int code; @@ -5462,7 +7037,8 @@ static bool get_phys_addr_v6(CPUARMState *env, uint32_t address, code = 5; goto do_fault; } - desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx)); + desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx), + mmu_idx, fsr, fi); type = (desc & 3); if (type == 0 || (type == 3 && !arm_feature(env, ARM_FEATURE_PXN))) { /* Section translation fault, or attempt to use the encoding @@ -5513,7 +7089,8 @@ static bool get_phys_addr_v6(CPUARMState *env, uint32_t address, ns = extract32(desc, 3, 1); /* Lookup l2 entry. */ table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc); - desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx)); + desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx), + mmu_idx, fsr, fi); ap = ((desc >> 4) & 3) | ((desc >> 7) & 4); switch (desc & 3) { case 0: /* Page translation fault. */ @@ -5587,17 +7164,87 @@ typedef enum { permission_fault = 3, } MMUFaultType; +/* + * check_s2_mmu_setup + * @cpu: ARMCPU + * @is_aa64: True if the translation regime is in AArch64 state + * @startlevel: Suggested starting level + * @inputsize: Bitsize of IPAs + * @stride: Page-table stride (See the ARM ARM) + * + * Returns true if the suggested S2 translation parameters are OK and + * false otherwise. + */ +static bool check_s2_mmu_setup(ARMCPU *cpu, bool is_aa64, int level, + int inputsize, int stride) +{ + const int grainsize = stride + 3; + int startsizecheck; + + /* Negative levels are never allowed. */ + if (level < 0) { + return false; + } + + startsizecheck = inputsize - ((3 - level) * stride + grainsize); + if (startsizecheck < 1 || startsizecheck > stride + 4) { + return false; + } + + if (is_aa64) { + CPUARMState *env = &cpu->env; + unsigned int pamax = arm_pamax(cpu); + + switch (stride) { + case 13: /* 64KB Pages. */ + if (level == 0 || (level == 1 && pamax <= 42)) { + return false; + } + break; + case 11: /* 16KB Pages. */ + if (level == 0 || (level == 1 && pamax <= 40)) { + return false; + } + break; + case 9: /* 4KB Pages. */ + if (level == 0 && pamax <= 42) { + return false; + } + break; + default: + g_assert_not_reached(); + } + + /* Inputsize checks. */ + if (inputsize > pamax && + (arm_el_is_aa64(env, 1) || inputsize > 40)) { + /* This is CONSTRAINED UNPREDICTABLE and we choose to fault. */ + return false; + } + } else { + /* AArch32 only supports 4KB pages. Assert on that. */ + assert(stride == 9); + + if (level == 0) { + return false; + } + } + return true; +} + static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, int access_type, ARMMMUIdx mmu_idx, hwaddr *phys_ptr, MemTxAttrs *txattrs, int *prot, - target_ulong *page_size_ptr, uint32_t *fsr) + target_ulong *page_size_ptr, uint32_t *fsr, + ARMMMUFaultInfo *fi) { - CPUState *cs = CPU(arm_env_get_cpu(env)); + ARMCPU *cpu = arm_env_get_cpu(env); + CPUState *cs = CPU(cpu); /* Read an LPAE long-descriptor translation table. */ MMUFaultType fault_type = translation_fault; - uint32_t level = 1; - uint32_t epd; - int32_t tsz; + uint32_t level; + uint32_t epd = 0; + int32_t t0sz, t1sz; uint32_t tg; uint64_t ttbr; int ttbr_select; @@ -5605,13 +7252,15 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, uint32_t tableattrs; target_ulong page_size; uint32_t attrs; - int32_t granule_sz = 9; - int32_t va_size = 32; + int32_t stride = 9; + int32_t va_size; + int inputsize; int32_t tbi = 0; TCR *tcr = regime_tcr(env, mmu_idx); int ap, ns, xn, pxn; uint32_t el = regime_el(env, mmu_idx); bool ttbr1_valid = true; + uint64_t descaddrmask; /* TODO: * This code does not handle the different format TCR for VTCR_EL2. @@ -5620,9 +7269,12 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, * support for those page table walks. */ if (arm_el_is_aa64(env, el)) { + level = 0; va_size = 64; if (el > 1) { - tbi = extract64(tcr->raw_tcr, 20, 1); + if (mmu_idx != ARMMMUIdx_S2NS) { + tbi = extract64(tcr->raw_tcr, 20, 1); + } } else { if (extract64(address, 55, 1)) { tbi = extract64(tcr->raw_tcr, 38, 1); @@ -5638,6 +7290,13 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, if (el > 1) { ttbr1_valid = false; } + } else { + level = 1; + va_size = 32; + /* There is no TTBR1 for EL2 */ + if (el == 2) { + ttbr1_valid = false; + } } /* Determine whether this address is in the region controlled by @@ -5645,12 +7304,28 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, * This is a Non-secure PL0/1 stage 1 translation, so controlled by * TTBCR/TTBR0/TTBR1 in accordance with ARM ARM DDI0406C table B-32: */ - uint32_t t0sz = extract32(tcr->raw_tcr, 0, 6); if (va_size == 64) { + /* AArch64 translation. */ + t0sz = extract32(tcr->raw_tcr, 0, 6); t0sz = MIN(t0sz, 39); t0sz = MAX(t0sz, 16); + } else if (mmu_idx != ARMMMUIdx_S2NS) { + /* AArch32 stage 1 translation. */ + t0sz = extract32(tcr->raw_tcr, 0, 3); + } else { + /* AArch32 stage 2 translation. */ + bool sext = extract32(tcr->raw_tcr, 4, 1); + bool sign = extract32(tcr->raw_tcr, 3, 1); + t0sz = sextract32(tcr->raw_tcr, 0, 4); + + /* If the sign-extend bit is not the same as t0sz[3], the result + * is unpredictable. Flag this as a guest error. */ + if (sign != sext) { + qemu_log_mask(LOG_GUEST_ERROR, + "AArch32: VTCR.S / VTCR.T0SZ[3] missmatch\n"); + } } - uint32_t t1sz = extract32(tcr->raw_tcr, 16, 6); + t1sz = extract32(tcr->raw_tcr, 16, 6); if (va_size == 64) { t1sz = MIN(t1sz, 39); t1sz = MAX(t1sz, 16); @@ -5683,15 +7358,17 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, */ if (ttbr_select == 0) { ttbr = regime_ttbr(env, mmu_idx, 0); - epd = extract32(tcr->raw_tcr, 7, 1); - tsz = t0sz; + if (el < 2) { + epd = extract32(tcr->raw_tcr, 7, 1); + } + inputsize = va_size - t0sz; tg = extract32(tcr->raw_tcr, 14, 2); if (tg == 1) { /* 64KB pages */ - granule_sz = 13; + stride = 13; } if (tg == 2) { /* 16KB pages */ - granule_sz = 11; + stride = 11; } } else { /* We should only be here if TTBR1 is valid */ @@ -5699,19 +7376,19 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, ttbr = regime_ttbr(env, mmu_idx, 1); epd = extract32(tcr->raw_tcr, 23, 1); - tsz = t1sz; + inputsize = va_size - t1sz; tg = extract32(tcr->raw_tcr, 30, 2); if (tg == 3) { /* 64KB pages */ - granule_sz = 13; + stride = 13; } if (tg == 1) { /* 16KB pages */ - granule_sz = 11; + stride = 11; } } /* Here we should have set up all the parameters for the translation: - * va_size, ttbr, epd, tsz, granule_sz, tbi + * va_size, inputsize, ttbr, epd, stride, tbi */ if (epd) { @@ -5721,32 +7398,67 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, goto do_fault; } - /* The starting level depends on the virtual address size (which can be - * up to 48 bits) and the translation granule size. It indicates the number - * of strides (granule_sz bits at a time) needed to consume the bits - * of the input address. In the pseudocode this is: - * level = 4 - RoundUp((inputsize - grainsize) / stride) - * where their 'inputsize' is our 'va_size - tsz', 'grainsize' is - * our 'granule_sz + 3' and 'stride' is our 'granule_sz'. - * Applying the usual "rounded up m/n is (m+n-1)/n" and simplifying: - * = 4 - (va_size - tsz - granule_sz - 3 + granule_sz - 1) / granule_sz - * = 4 - (va_size - tsz - 4) / granule_sz; - */ - level = 4 - (va_size - tsz - 4) / granule_sz; + if (mmu_idx != ARMMMUIdx_S2NS) { + /* The starting level depends on the virtual address size (which can + * be up to 48 bits) and the translation granule size. It indicates + * the number of strides (stride bits at a time) needed to + * consume the bits of the input address. In the pseudocode this is: + * level = 4 - RoundUp((inputsize - grainsize) / stride) + * where their 'inputsize' is our 'inputsize', 'grainsize' is + * our 'stride + 3' and 'stride' is our 'stride'. + * Applying the usual "rounded up m/n is (m+n-1)/n" and simplifying: + * = 4 - (inputsize - stride - 3 + stride - 1) / stride + * = 4 - (inputsize - 4) / stride; + */ + level = 4 - (inputsize - 4) / stride; + } else { + /* For stage 2 translations the starting level is specified by the + * VTCR_EL2.SL0 field (whose interpretation depends on the page size) + */ + uint32_t sl0 = extract32(tcr->raw_tcr, 6, 2); + uint32_t startlevel; + bool ok; + + if (va_size == 32 || stride == 9) { + /* AArch32 or 4KB pages */ + startlevel = 2 - sl0; + } else { + /* 16KB or 64KB pages */ + startlevel = 3 - sl0; + } + + /* Check that the starting level is valid. */ + ok = check_s2_mmu_setup(cpu, va_size == 64, startlevel, + inputsize, stride); + if (!ok) { + fault_type = translation_fault; + goto do_fault; + } + level = startlevel; + } /* Clear the vaddr bits which aren't part of the within-region address, * so that we don't have to special case things when calculating the * first descriptor address. */ - if (tsz) { - address &= (1ULL << (va_size - tsz)) - 1; + if (va_size != inputsize) { + address &= (1ULL << inputsize) - 1; } - descmask = (1ULL << (granule_sz + 3)) - 1; + descmask = (1ULL << (stride + 3)) - 1; /* Now we can extract the actual base address from the TTBR */ descaddr = extract64(ttbr, 0, 48); - descaddr &= ~((1ULL << (va_size - tsz - (granule_sz * (4 - level)))) - 1); + descaddr &= ~((1ULL << (inputsize - (stride * (4 - level)))) - 1); + + /* The address field in the descriptor goes up to bit 39 for ARMv7 + * but up to bit 47 for ARMv8. + */ + if (arm_feature(env, ARM_FEATURE_V8)) { + descaddrmask = 0xfffffffff000ULL; + } else { + descaddrmask = 0xfffffff000ULL; + } /* Secure accesses start with the page table in secure memory and * can be downgraded to non-secure at any step. Non-secure accesses @@ -5758,16 +7470,20 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, uint64_t descriptor; bool nstable; - descaddr |= (address >> (granule_sz * (4 - level))) & descmask; + descaddr |= (address >> (stride * (4 - level))) & descmask; descaddr &= ~7ULL; nstable = extract32(tableattrs, 4, 1); - descriptor = arm_ldq_ptw(cs, descaddr, !nstable); + descriptor = arm_ldq_ptw(cs, descaddr, !nstable, mmu_idx, fsr, fi); + if (fi->s1ptw) { + goto do_fault; + } + if (!(descriptor & 1) || (!(descriptor & 2) && (level == 3))) { /* Invalid, or the Reserved level 3 encoding */ goto do_fault; } - descaddr = descriptor & 0xfffffff000ULL; + descaddr = descriptor & descaddrmask; if ((descriptor & 2) && (level < 3)) { /* Table entry. The top five bits are attributes which may @@ -5783,11 +7499,17 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, * These are basically the same thing, although the number * of bits we pull in from the vaddr varies. */ - page_size = (1ULL << ((granule_sz * (4 - level)) + 3)); + page_size = (1ULL << ((stride * (4 - level)) + 3)); descaddr |= (address & (page_size - 1)); - /* Extract attributes from the descriptor and merge with table attrs */ + /* Extract attributes from the descriptor */ attrs = extract64(descriptor, 2, 10) | (extract64(descriptor, 52, 12) << 10); + + if (mmu_idx == ARMMMUIdx_S2NS) { + /* Stage 2 table descriptors do not include any attribute fields */ + break; + } + /* Merge in attributes from table descriptors */ attrs |= extract32(tableattrs, 0, 2) << 11; /* XN, PXN */ attrs |= extract32(tableattrs, 3, 1) << 5; /* APTable[1] => AP[2] */ /* The sense of AP[1] vs APTable[0] is reversed, as APTable[0] == 1 @@ -5809,11 +7531,16 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, } ap = extract32(attrs, 4, 2); - ns = extract32(attrs, 3, 1); xn = extract32(attrs, 12, 1); - pxn = extract32(attrs, 11, 1); - *prot = get_S1prot(env, mmu_idx, va_size == 64, ap, ns, xn, pxn); + if (mmu_idx == ARMMMUIdx_S2NS) { + ns = true; + *prot = get_S2prot(env, ap, xn); + } else { + ns = extract32(attrs, 3, 1); + pxn = extract32(attrs, 11, 1); + *prot = get_S1prot(env, mmu_idx, va_size == 64, ap, ns, xn, pxn); + } fault_type = permission_fault; if (!(*prot & (1 << access_type))) { @@ -5834,6 +7561,8 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, do_fault: /* Long-descriptor format IFSR/DFSR value */ *fsr = (1 << 9) | (fault_type << 2) | level; + /* Tag the error as S2 for failed S1 PTW at S2 or ordinary S2. */ + fi->stage2 = fi->s1ptw || (mmu_idx == ARMMMUIdx_S2NS); return true; } @@ -6096,20 +7825,45 @@ static bool get_phys_addr_pmsav5(CPUARMState *env, uint32_t address, * @page_size: set to the size of the page containing phys_ptr * @fsr: set to the DFSR/IFSR value on failure */ -static inline bool get_phys_addr(CPUARMState *env, target_ulong address, - int access_type, ARMMMUIdx mmu_idx, - hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot, - target_ulong *page_size, uint32_t *fsr) +static bool get_phys_addr(CPUARMState *env, target_ulong address, + int access_type, ARMMMUIdx mmu_idx, + hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot, + target_ulong *page_size, uint32_t *fsr, + ARMMMUFaultInfo *fi) { if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) { - /* TODO: when we support EL2 we should here call ourselves recursively - * to do the stage 1 and then stage 2 translations. The arm_ld*_ptw - * functions will also need changing to perform ARMMMUIdx_S2NS loads - * rather than direct physical memory loads when appropriate. - * For non-EL2 CPUs a stage1+stage2 translation is just stage 1. + /* Call ourselves recursively to do the stage 1 and then stage 2 + * translations. */ - assert(!arm_feature(env, ARM_FEATURE_EL2)); - mmu_idx += ARMMMUIdx_S1NSE0; + if (arm_feature(env, ARM_FEATURE_EL2)) { + hwaddr ipa; + int s2_prot; + int ret; + + ret = get_phys_addr(env, address, access_type, + mmu_idx + ARMMMUIdx_S1NSE0, &ipa, attrs, + prot, page_size, fsr, fi); + + /* If S1 fails or S2 is disabled, return early. */ + if (ret || regime_translation_disabled(env, ARMMMUIdx_S2NS)) { + *phys_ptr = ipa; + return ret; + } + + /* S1 is done. Now do S2 translation. */ + ret = get_phys_addr_lpae(env, ipa, access_type, ARMMMUIdx_S2NS, + phys_ptr, attrs, &s2_prot, + page_size, fsr, fi); + fi->s2addr = ipa; + /* Combine the S1 and S2 perms. */ + *prot &= s2_prot; + return ret; + } else { + /* + * For non-EL2 CPUs a stage1+stage2 translation is just stage 1. + */ + mmu_idx += ARMMMUIdx_S1NSE0; + } } /* The page table entries may downgrade secure to non-secure, but @@ -6158,13 +7912,13 @@ static inline bool get_phys_addr(CPUARMState *env, target_ulong address, if (regime_using_lpae_format(env, mmu_idx)) { return get_phys_addr_lpae(env, address, access_type, mmu_idx, phys_ptr, - attrs, prot, page_size, fsr); + attrs, prot, page_size, fsr, fi); } else if (regime_sctlr(env, mmu_idx) & SCTLR_XP) { return get_phys_addr_v6(env, address, access_type, mmu_idx, phys_ptr, - attrs, prot, page_size, fsr); + attrs, prot, page_size, fsr, fi); } else { return get_phys_addr_v5(env, address, access_type, mmu_idx, phys_ptr, - prot, page_size, fsr); + prot, page_size, fsr, fi); } } @@ -6173,7 +7927,8 @@ static inline bool get_phys_addr(CPUARMState *env, target_ulong address, * fsr with ARM DFSR/IFSR fault register format value on failure. */ bool arm_tlb_fill(CPUState *cs, vaddr address, - int access_type, int mmu_idx, uint32_t *fsr) + int access_type, int mmu_idx, uint32_t *fsr, + ARMMMUFaultInfo *fi) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; @@ -6184,7 +7939,7 @@ bool arm_tlb_fill(CPUState *cs, vaddr address, MemTxAttrs attrs = {}; ret = get_phys_addr(env, address, access_type, mmu_idx, &phys_addr, - &attrs, &prot, &page_size, fsr); + &attrs, &prot, &page_size, fsr, fi); if (!ret) { /* Map a single [sub]page. */ phys_addr &= TARGET_PAGE_MASK; @@ -6197,7 +7952,8 @@ bool arm_tlb_fill(CPUState *cs, vaddr address, return ret; } -hwaddr arm_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr, + MemTxAttrs *attrs) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; @@ -6206,36 +7962,19 @@ hwaddr arm_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) int prot; bool ret; uint32_t fsr; - MemTxAttrs attrs = {}; + ARMMMUFaultInfo fi = {}; - ret = get_phys_addr(env, addr, 0, cpu_mmu_index(env), &phys_addr, - &attrs, &prot, &page_size, &fsr); + *attrs = (MemTxAttrs) {}; + + ret = get_phys_addr(env, addr, 0, cpu_mmu_index(env, false), &phys_addr, + attrs, &prot, &page_size, &fsr, &fi); if (ret) { return -1; } - return phys_addr; } -void HELPER(set_r13_banked)(CPUARMState *env, uint32_t mode, uint32_t val) -{ - if ((env->uncached_cpsr & CPSR_M) == mode) { - env->regs[13] = val; - } else { - env->banked_r13[bank_number(mode)] = val; - } -} - -uint32_t HELPER(get_r13_banked)(CPUARMState *env, uint32_t mode) -{ - if ((env->uncached_cpsr & CPSR_M) == mode) { - return env->regs[13]; - } else { - return env->banked_r13[bank_number(mode)]; - } -} - uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) { ARMCPU *cpu = arm_env_get_cpu(env); @@ -6373,7 +8112,7 @@ void HELPER(dc_zva)(CPUARMState *env, uint64_t vaddr_in) int maxidx = DIV_ROUND_UP(blocklen, TARGET_PAGE_SIZE); void *hostaddr[maxidx]; int try, i; - unsigned mmu_idx = cpu_mmu_index(env); + unsigned mmu_idx = cpu_mmu_index(env, false); TCGMemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); for (try = 0; try < 2; try++) { diff --git a/qemu/target-arm/helper.h b/qemu/target-arm/helper.h index 827b33dfe..84aa63762 100644 --- a/qemu/target-arm/helper.h +++ b/qemu/target-arm/helper.h @@ -48,19 +48,23 @@ DEF_HELPER_FLAGS_3(sel_flags, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) DEF_HELPER_2(exception_internal, void, env, i32) DEF_HELPER_4(exception_with_syndrome, void, env, i32, i32, i32) +DEF_HELPER_1(setend, void, env) DEF_HELPER_1(wfi, void, env) DEF_HELPER_1(wfe, void, env) DEF_HELPER_1(yield, void, env) DEF_HELPER_1(pre_hvc, void, env) DEF_HELPER_2(pre_smc, void, env, i32) +DEF_HELPER_1(check_breakpoints, void, env) + DEF_HELPER_3(cpsr_write, void, env, i32, i32) +DEF_HELPER_2(cpsr_write_eret, void, env, i32) DEF_HELPER_1(cpsr_read, i32, env) DEF_HELPER_3(v7m_msr, void, env, i32, i32) DEF_HELPER_2(v7m_mrs, i32, env, i32) -DEF_HELPER_3(access_check_cp_reg, void, env, ptr, i32) +DEF_HELPER_4(access_check_cp_reg, void, env, ptr, i32, i32) DEF_HELPER_3(set_cp_reg, void, env, ptr, i32) DEF_HELPER_2(get_cp_reg, i32, env, ptr) DEF_HELPER_3(set_cp_reg64, void, env, ptr, i64) @@ -73,6 +77,9 @@ DEF_HELPER_1(exception_return, void, env) DEF_HELPER_2(get_r13_banked, i32, env, i32) DEF_HELPER_3(set_r13_banked, void, env, i32, i32) +DEF_HELPER_3(mrs_banked, i32, env, i32, i32) +DEF_HELPER_4(msr_banked, void, env, i32, i32, i32) + DEF_HELPER_2(get_user_reg, i32, env, i32) DEF_HELPER_3(set_user_reg, void, env, i32, i32) diff --git a/qemu/target-arm/internals.h b/qemu/target-arm/internals.h index 924aff9d0..2e70272be 100644 --- a/qemu/target-arm/internals.h +++ b/qemu/target-arm/internals.h @@ -25,6 +25,16 @@ #ifndef TARGET_ARM_INTERNALS_H #define TARGET_ARM_INTERNALS_H +/* register banks for CPU modes */ +#define BANK_USRSYS 0 +#define BANK_SVC 1 +#define BANK_ABT 2 +#define BANK_UND 3 +#define BANK_IRQ 4 +#define BANK_FIQ 5 +#define BANK_HYP 6 +#define BANK_MON 7 + static inline bool excp_is_internal(int excp) { /* Return true if this exception number represents a QEMU-internal @@ -36,6 +46,7 @@ static inline bool excp_is_internal(int excp) || excp == EXCP_HALTED || excp == EXCP_EXCEPTION_EXIT || excp == EXCP_KERNEL_TRAP + || excp == EXCP_SEMIHOST || excp == EXCP_STREX; } @@ -58,6 +69,7 @@ static const char * const excnames[] = { [EXCP_SMC] = "Secure Monitor Call", [EXCP_VIRQ] = "Virtual IRQ", [EXCP_VFIQ] = "Virtual FIQ", + [EXCP_SEMIHOST] = "Semihosting call", }; static inline void arm_log_exception(int idx) @@ -89,15 +101,39 @@ static inline void arm_log_exception(int idx) static inline unsigned int aarch64_banked_spsr_index(unsigned int el) { static const unsigned int map[4] = { - [1] = 1, /* EL1. */ - [2] = 6, /* EL2. */ - [3] = 7, /* EL3. */ + [1] = BANK_SVC, /* EL1. */ + [2] = BANK_HYP, /* EL2. */ + [3] = BANK_MON, /* EL3. */ }; assert(el >= 1 && el <= 3); return map[el]; } -int bank_number(int mode); +/* Map CPU modes onto saved register banks. */ +static inline int bank_number(int mode) +{ + switch (mode) { + case ARM_CPU_MODE_USR: + case ARM_CPU_MODE_SYS: + return BANK_USRSYS; + case ARM_CPU_MODE_SVC: + return BANK_SVC; + case ARM_CPU_MODE_ABT: + return BANK_ABT; + case ARM_CPU_MODE_UND: + return BANK_UND; + case ARM_CPU_MODE_IRQ: + return BANK_IRQ; + case ARM_CPU_MODE_FIQ: + return BANK_FIQ; + case ARM_CPU_MODE_HYP: + return BANK_HYP; + case ARM_CPU_MODE_MON: + return BANK_MON; + } + g_assert_not_reached(); +} + void switch_mode(CPUARMState *, int); void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu); void arm_translate_init(void); @@ -150,6 +186,31 @@ static inline void update_spsel(CPUARMState *env, uint32_t imm) aarch64_restore_sp(env, cur_el); } +/* + * arm_pamax + * @cpu: ARMCPU + * + * Returns the implementation defined bit-width of physical addresses. + * The ARMv8 reference manuals refer to this as PAMax(). + */ +static inline unsigned int arm_pamax(ARMCPU *cpu) +{ + static const unsigned int pamax_map[] = { + [0] = 32, + [1] = 36, + [2] = 40, + [3] = 42, + [4] = 44, + [5] = 48, + }; + unsigned int parange = extract32(cpu->id_aa64mmfr0, 0, 4); + + /* id_aa64mmfr0 is a read-only register so values outside of the + * supported mappings can be considered an implementation error. */ + assert(parange < ARRAY_SIZE(pamax_map)); + return pamax_map[parange]; +} + /* Return true if extended addresses are enabled. * This is always the case if our translation regime is 64 bit, * but depends on TTBCR.EAE for 32 bit. @@ -233,10 +294,10 @@ static inline uint32_t syn_aa64_smc(uint32_t imm16) return (EC_AA64_SMC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff); } -static inline uint32_t syn_aa32_svc(uint32_t imm16, bool is_thumb) +static inline uint32_t syn_aa32_svc(uint32_t imm16, bool is_16bit) { return (EC_AA32_SVC << ARM_EL_EC_SHIFT) | (imm16 & 0xffff) - | (is_thumb ? 0 : ARM_EL_IL); + | (is_16bit ? 0 : ARM_EL_IL); } static inline uint32_t syn_aa32_hvc(uint32_t imm16) @@ -254,10 +315,10 @@ static inline uint32_t syn_aa64_bkpt(uint32_t imm16) return (EC_AA64_BKPT << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff); } -static inline uint32_t syn_aa32_bkpt(uint32_t imm16, bool is_thumb) +static inline uint32_t syn_aa32_bkpt(uint32_t imm16, bool is_16bit) { return (EC_AA32_BKPT << ARM_EL_EC_SHIFT) | (imm16 & 0xffff) - | (is_thumb ? 0 : ARM_EL_IL); + | (is_16bit ? 0 : ARM_EL_IL); } static inline uint32_t syn_aa64_sysregtrap(int op0, int op1, int op2, @@ -271,48 +332,48 @@ static inline uint32_t syn_aa64_sysregtrap(int op0, int op1, int op2, static inline uint32_t syn_cp14_rt_trap(int cv, int cond, int opc1, int opc2, int crn, int crm, int rt, int isread, - bool is_thumb) + bool is_16bit) { return (EC_CP14RTTRAP << ARM_EL_EC_SHIFT) - | (is_thumb ? 0 : ARM_EL_IL) + | (is_16bit ? 0 : ARM_EL_IL) | (cv << 24) | (cond << 20) | (opc2 << 17) | (opc1 << 14) | (crn << 10) | (rt << 5) | (crm << 1) | isread; } static inline uint32_t syn_cp15_rt_trap(int cv, int cond, int opc1, int opc2, int crn, int crm, int rt, int isread, - bool is_thumb) + bool is_16bit) { return (EC_CP15RTTRAP << ARM_EL_EC_SHIFT) - | (is_thumb ? 0 : ARM_EL_IL) + | (is_16bit ? 0 : ARM_EL_IL) | (cv << 24) | (cond << 20) | (opc2 << 17) | (opc1 << 14) | (crn << 10) | (rt << 5) | (crm << 1) | isread; } static inline uint32_t syn_cp14_rrt_trap(int cv, int cond, int opc1, int crm, int rt, int rt2, int isread, - bool is_thumb) + bool is_16bit) { return (EC_CP14RRTTRAP << ARM_EL_EC_SHIFT) - | (is_thumb ? 0 : ARM_EL_IL) + | (is_16bit ? 0 : ARM_EL_IL) | (cv << 24) | (cond << 20) | (opc1 << 16) | (rt2 << 10) | (rt << 5) | (crm << 1) | isread; } static inline uint32_t syn_cp15_rrt_trap(int cv, int cond, int opc1, int crm, int rt, int rt2, int isread, - bool is_thumb) + bool is_16bit) { return (EC_CP15RRTTRAP << ARM_EL_EC_SHIFT) - | (is_thumb ? 0 : ARM_EL_IL) + | (is_16bit ? 0 : ARM_EL_IL) | (cv << 24) | (cond << 20) | (opc1 << 16) | (rt2 << 10) | (rt << 5) | (crm << 1) | isread; } -static inline uint32_t syn_fp_access_trap(int cv, int cond, bool is_thumb) +static inline uint32_t syn_fp_access_trap(int cv, int cond, bool is_16bit) { return (EC_ADVSIMDFPACCESSTRAP << ARM_EL_EC_SHIFT) - | (is_thumb ? 0 : ARM_EL_IL) + | (is_16bit ? 0 : ARM_EL_IL) | (cv << 24) | (cond << 20); } @@ -372,6 +433,9 @@ void hw_breakpoint_update(ARMCPU *cpu, int n); */ void hw_breakpoint_update_all(ARMCPU *cpu); +/* Callback function for checking if a watchpoint should trigger. */ +bool arm_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp); + /* Callback function for when a watchpoint or breakpoint triggers. */ void arm_debug_excp_handler(CPUState *cs); @@ -387,8 +451,29 @@ bool arm_is_psci_call(ARMCPU *cpu, int excp_type); void arm_handle_psci_call(ARMCPU *cpu); #endif +/** + * ARMMMUFaultInfo: Information describing an ARM MMU Fault + * @s2addr: Address that caused a fault at stage 2 + * @stage2: True if we faulted at stage 2 + * @s1ptw: True if we faulted at stage 2 while doing a stage 1 page-table walk + */ +typedef struct ARMMMUFaultInfo ARMMMUFaultInfo; +struct ARMMMUFaultInfo { + target_ulong s2addr; + bool stage2; + bool s1ptw; +}; + /* Do a page table walk and add page to TLB if possible */ bool arm_tlb_fill(CPUState *cpu, vaddr address, int rw, int mmu_idx, - uint32_t *fsr); + uint32_t *fsr, ARMMMUFaultInfo *fi); + +/* Return true if the stage 1 translation regime is using LPAE format page + * tables */ +bool arm_s1_regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx); + +/* Raise a data fault alignment exception for the specified virtual address */ +void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, int is_write, + int is_user, uintptr_t retaddr); #endif diff --git a/qemu/target-arm/iwmmxt_helper.c b/qemu/target-arm/iwmmxt_helper.c index a5069144d..7d87e1a0a 100644 --- a/qemu/target-arm/iwmmxt_helper.c +++ b/qemu/target-arm/iwmmxt_helper.c @@ -19,8 +19,7 @@ * License along with this library; if not, see . */ -#include -#include +#include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" diff --git a/qemu/target-arm/kvm-consts.h b/qemu/target-arm/kvm-consts.h index 943bf8980..a2c951859 100644 --- a/qemu/target-arm/kvm-consts.h +++ b/qemu/target-arm/kvm-consts.h @@ -15,7 +15,6 @@ #define ARM_KVM_CONSTS_H #ifdef CONFIG_KVM -#include "qemu/compiler.h" #include #include diff --git a/qemu/target-arm/kvm-stub.c b/qemu/target-arm/kvm-stub.c index db2edc2c4..38bf43387 100644 --- a/qemu/target-arm/kvm-stub.c +++ b/qemu/target-arm/kvm-stub.c @@ -9,6 +9,7 @@ * See the COPYING file in the top-level directory. * */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "kvm_arm.h" diff --git a/qemu/target-arm/kvm.c b/qemu/target-arm/kvm.c index b27854208..36710320f 100644 --- a/qemu/target-arm/kvm.c +++ b/qemu/target-arm/kvm.c @@ -8,8 +8,7 @@ * */ -#include -#include +#include "qemu/osdep.h" #include #include @@ -17,6 +16,7 @@ #include "qemu-common.h" #include "qemu/timer.h" +#include "qemu/error-report.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" #include "kvm_arm.h" @@ -24,6 +24,7 @@ #include "internals.h" #include "hw/arm/arm.h" #include "exec/memattrs.h" +#include "hw/boards.h" const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_LAST_INFO @@ -61,13 +62,18 @@ bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try, goto err; } + if (!init) { + /* Caller doesn't want the VCPU to be initialized, so skip it */ + goto finish; + } + ret = ioctl(vmfd, KVM_ARM_PREFERRED_TARGET, init); if (ret >= 0) { ret = ioctl(cpufd, KVM_ARM_VCPU_INIT, init); if (ret < 0) { goto err; } - } else { + } else if (cpus_to_try) { /* Old kernel which doesn't know about the * PREFERRED_TARGET ioctl: we know it will only support * creating one kind of guest CPU which is its preferred @@ -84,8 +90,15 @@ bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try, if (ret < 0) { goto err; } + } else { + /* Treat a NULL cpus_to_try argument the same as an empty + * list, which means we will fail the call since this must + * be an old kernel which doesn't support PREFERRED_TARGET. + */ + goto err; } +finish: fdarray[0] = kvmfd; fdarray[1] = vmfd; fdarray[2] = cpufd; @@ -516,9 +529,23 @@ MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run) return MEMTXATTRS_UNSPECIFIED; } + int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) { - return 0; + int ret = 0; + + switch (run->exit_reason) { + case KVM_EXIT_DEBUG: + if (kvm_arm_handle_debug(cs, &run->debug.arch)) { + ret = EXCP_DEBUG; + } /* otherwise return to guest */ + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: un-handled exit reason %d\n", + __func__, run->exit_reason); + break; + } + return ret; } bool kvm_arch_stop_on_emulation_error(CPUState *cs) @@ -541,66 +568,56 @@ int kvm_arch_on_sigbus(int code, void *addr) return 1; } +/* The #ifdef protections are until 32bit headers are imported and can + * be removed once both 32 and 64 bit reach feature parity. + */ void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg) { - qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__); -} - -int kvm_arch_insert_sw_breakpoint(CPUState *cs, - struct kvm_sw_breakpoint *bp) -{ - qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__); - return -EINVAL; -} - -int kvm_arch_insert_hw_breakpoint(target_ulong addr, - target_ulong len, int type) -{ - qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__); - return -EINVAL; -} - -int kvm_arch_remove_hw_breakpoint(target_ulong addr, - target_ulong len, int type) -{ - qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__); - return -EINVAL; -} - -int kvm_arch_remove_sw_breakpoint(CPUState *cs, - struct kvm_sw_breakpoint *bp) -{ - qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__); - return -EINVAL; -} - -void kvm_arch_remove_all_hw_breakpoints(void) -{ - qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__); +#ifdef KVM_GUESTDBG_USE_SW_BP + if (kvm_sw_breakpoints_active(cs)) { + dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; + } +#endif +#ifdef KVM_GUESTDBG_USE_HW + if (kvm_arm_hw_debug_active(cs)) { + dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW; + kvm_arm_copy_hw_debug_data(&dbg->arch); + } +#endif } void kvm_arch_init_irq_routing(KVMState *s) { } -int kvm_arch_irqchip_create(KVMState *s) +int kvm_arch_irqchip_create(MachineState *ms, KVMState *s) { - int ret; + if (machine_kernel_irqchip_split(ms)) { + perror("-machine kernel_irqchip=split is not supported on ARM."); + exit(1); + } /* If we can create the VGIC using the newer device control API, we * let the device do this when it initializes itself, otherwise we * fall back to the old API */ + return kvm_check_extension(s, KVM_CAP_DEVICE_CTRL); +} - ret = kvm_create_device(s, KVM_DEV_TYPE_ARM_VGIC_V2, true); - if (ret == 0) { - return 1; +int kvm_arm_vgic_probe(void) +{ + if (kvm_create_device(kvm_state, + KVM_DEV_TYPE_ARM_VGIC_V3, true) == 0) { + return 3; + } else if (kvm_create_device(kvm_state, + KVM_DEV_TYPE_ARM_VGIC_V2, true) == 0) { + return 2; + } else { + return 0; } - - return 0; } int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, - uint64_t address, uint32_t data) + uint64_t address, uint32_t data, PCIDevice *dev) { return 0; } diff --git a/qemu/target-arm/kvm32.c b/qemu/target-arm/kvm32.c index 421ce0ea0..d44a7f92b 100644 --- a/qemu/target-arm/kvm32.c +++ b/qemu/target-arm/kvm32.c @@ -8,8 +8,7 @@ * */ -#include -#include +#include "qemu/osdep.h" #include #include @@ -181,7 +180,6 @@ int kvm_arm_cpreg_level(uint64_t regidx) return KVM_PUT_RUNTIME_STATE; } -#define ARM_MPIDR_HWID_BITMASK 0xFFFFFF #define ARM_CPU_ID_MPIDR 0, 0, 0, 5 int kvm_arch_init_vcpu(CPUState *cs) @@ -234,7 +232,7 @@ int kvm_arch_init_vcpu(CPUState *cs) if (ret) { return ret; } - cpu->mp_affinity = mpidr & ARM_MPIDR_HWID_BITMASK; + cpu->mp_affinity = mpidr & ARM32_AFFINITY_MASK; return kvm_arm_init_cpreg_list(cpu); } @@ -281,30 +279,30 @@ static const Reg regs[] = { COREREG(usr_regs.uregs[10], usr_regs[2]), COREREG(usr_regs.uregs[11], usr_regs[3]), COREREG(usr_regs.uregs[12], usr_regs[4]), - COREREG(usr_regs.uregs[13], banked_r13[0]), - COREREG(usr_regs.uregs[14], banked_r14[0]), + COREREG(usr_regs.uregs[13], banked_r13[BANK_USRSYS]), + COREREG(usr_regs.uregs[14], banked_r14[BANK_USRSYS]), /* R13, R14, SPSR for SVC, ABT, UND, IRQ banks */ - COREREG(svc_regs[0], banked_r13[1]), - COREREG(svc_regs[1], banked_r14[1]), - COREREG64(svc_regs[2], banked_spsr[1]), - COREREG(abt_regs[0], banked_r13[2]), - COREREG(abt_regs[1], banked_r14[2]), - COREREG64(abt_regs[2], banked_spsr[2]), - COREREG(und_regs[0], banked_r13[3]), - COREREG(und_regs[1], banked_r14[3]), - COREREG64(und_regs[2], banked_spsr[3]), - COREREG(irq_regs[0], banked_r13[4]), - COREREG(irq_regs[1], banked_r14[4]), - COREREG64(irq_regs[2], banked_spsr[4]), + COREREG(svc_regs[0], banked_r13[BANK_SVC]), + COREREG(svc_regs[1], banked_r14[BANK_SVC]), + COREREG64(svc_regs[2], banked_spsr[BANK_SVC]), + COREREG(abt_regs[0], banked_r13[BANK_ABT]), + COREREG(abt_regs[1], banked_r14[BANK_ABT]), + COREREG64(abt_regs[2], banked_spsr[BANK_ABT]), + COREREG(und_regs[0], banked_r13[BANK_UND]), + COREREG(und_regs[1], banked_r14[BANK_UND]), + COREREG64(und_regs[2], banked_spsr[BANK_UND]), + COREREG(irq_regs[0], banked_r13[BANK_IRQ]), + COREREG(irq_regs[1], banked_r14[BANK_IRQ]), + COREREG64(irq_regs[2], banked_spsr[BANK_IRQ]), /* R8_fiq .. R14_fiq and SPSR_fiq */ COREREG(fiq_regs[0], fiq_regs[0]), COREREG(fiq_regs[1], fiq_regs[1]), COREREG(fiq_regs[2], fiq_regs[2]), COREREG(fiq_regs[3], fiq_regs[3]), COREREG(fiq_regs[4], fiq_regs[4]), - COREREG(fiq_regs[5], banked_r13[5]), - COREREG(fiq_regs[6], banked_r14[5]), - COREREG64(fiq_regs[7], banked_spsr[5]), + COREREG(fiq_regs[5], banked_r13[BANK_FIQ]), + COREREG(fiq_regs[6], banked_r14[BANK_FIQ]), + COREREG64(fiq_regs[7], banked_spsr[BANK_FIQ]), /* R15 */ COREREG(usr_regs.uregs[15], regs[15]), /* VFP system registers */ @@ -430,7 +428,7 @@ int kvm_arch_get_registers(CPUState *cs) if (ret) { return ret; } - cpsr_write(env, cpsr, 0xffffffff); + cpsr_write(env, cpsr, 0xffffffff, CPSRWriteRaw); /* Make sure the current mode regs are properly set */ mode = env->uncached_cpsr & CPSR_M; @@ -476,3 +474,50 @@ int kvm_arch_get_registers(CPUState *cs) return 0; } + +int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) +{ + qemu_log_mask(LOG_UNIMP, "%s: guest debug not yet implemented\n", __func__); + return -EINVAL; +} + +int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) +{ + qemu_log_mask(LOG_UNIMP, "%s: guest debug not yet implemented\n", __func__); + return -EINVAL; +} + +bool kvm_arm_handle_debug(CPUState *cs, struct kvm_debug_exit_arch *debug_exit) +{ + qemu_log_mask(LOG_UNIMP, "%s: guest debug not yet implemented\n", __func__); + return false; +} + +int kvm_arch_insert_hw_breakpoint(target_ulong addr, + target_ulong len, int type) +{ + qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__); + return -EINVAL; +} + +int kvm_arch_remove_hw_breakpoint(target_ulong addr, + target_ulong len, int type) +{ + qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__); + return -EINVAL; +} + +void kvm_arch_remove_all_hw_breakpoints(void) +{ + qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__); +} + +void kvm_arm_copy_hw_debug_data(struct kvm_guest_debug_arch *ptr) +{ + qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__); +} + +bool kvm_arm_hw_debug_active(CPUState *cs) +{ + return false; +} diff --git a/qemu/target-arm/kvm64.c b/qemu/target-arm/kvm64.c index bd60889d1..e8527bf0c 100644 --- a/qemu/target-arm/kvm64.c +++ b/qemu/target-arm/kvm64.c @@ -2,22 +2,26 @@ * ARM implementation of KVM hooks, 64 bit specific code * * Copyright Mian-M. Hamayun 2013, Virtual Open Systems + * Copyright Alex Bennée 2014, Linaro * * 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 -#include +#include "qemu/osdep.h" #include #include +#include +#include #include -#include "config-host.h" #include "qemu-common.h" #include "qemu/timer.h" +#include "qemu/error-report.h" +#include "qemu/host-utils.h" +#include "exec/gdbstub.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" #include "kvm_arm.h" @@ -25,6 +29,360 @@ #include "internals.h" #include "hw/arm/arm.h" +static bool have_guest_debug; + +/* + * Although the ARM implementation of hardware assisted debugging + * allows for different breakpoints per-core, the current GDB + * interface treats them as a global pool of registers (which seems to + * be the case for x86, ppc and s390). As a result we store one copy + * of registers which is used for all active cores. + * + * Write access is serialised by virtue of the GDB protocol which + * updates things. Read access (i.e. when the values are copied to the + * vCPU) is also gated by GDB's run control. + * + * This is not unreasonable as most of the time debugging kernels you + * never know which core will eventually execute your function. + */ + +typedef struct { + uint64_t bcr; + uint64_t bvr; +} HWBreakpoint; + +/* The watchpoint registers can cover more area than the requested + * watchpoint so we need to store the additional information + * somewhere. We also need to supply a CPUWatchpoint to the GDB stub + * when the watchpoint is hit. + */ +typedef struct { + uint64_t wcr; + uint64_t wvr; + CPUWatchpoint details; +} HWWatchpoint; + +/* Maximum and current break/watch point counts */ +int max_hw_bps, max_hw_wps; +GArray *hw_breakpoints, *hw_watchpoints; + +#define cur_hw_wps (hw_watchpoints->len) +#define cur_hw_bps (hw_breakpoints->len) +#define get_hw_bp(i) (&g_array_index(hw_breakpoints, HWBreakpoint, i)) +#define get_hw_wp(i) (&g_array_index(hw_watchpoints, HWWatchpoint, i)) + +/** + * kvm_arm_init_debug() - check for guest debug capabilities + * @cs: CPUState + * + * kvm_check_extension returns the number of debug registers we have + * or 0 if we have none. + * + */ +static void kvm_arm_init_debug(CPUState *cs) +{ + have_guest_debug = kvm_check_extension(cs->kvm_state, + KVM_CAP_SET_GUEST_DEBUG); + + max_hw_wps = kvm_check_extension(cs->kvm_state, KVM_CAP_GUEST_DEBUG_HW_WPS); + hw_watchpoints = g_array_sized_new(true, true, + sizeof(HWWatchpoint), max_hw_wps); + + max_hw_bps = kvm_check_extension(cs->kvm_state, KVM_CAP_GUEST_DEBUG_HW_BPS); + hw_breakpoints = g_array_sized_new(true, true, + sizeof(HWBreakpoint), max_hw_bps); + return; +} + +/** + * insert_hw_breakpoint() + * @addr: address of breakpoint + * + * See ARM ARM D2.9.1 for details but here we are only going to create + * simple un-linked breakpoints (i.e. we don't chain breakpoints + * together to match address and context or vmid). The hardware is + * capable of fancier matching but that will require exposing that + * fanciness to GDB's interface + * + * D7.3.2 DBGBCR_EL1, Debug Breakpoint Control Registers + * + * 31 24 23 20 19 16 15 14 13 12 9 8 5 4 3 2 1 0 + * +------+------+-------+-----+----+------+-----+------+-----+---+ + * | RES0 | BT | LBN | SSC | HMC| RES0 | BAS | RES0 | PMC | E | + * +------+------+-------+-----+----+------+-----+------+-----+---+ + * + * BT: Breakpoint type (0 = unlinked address match) + * LBN: Linked BP number (0 = unused) + * SSC/HMC/PMC: Security, Higher and Priv access control (Table D-12) + * BAS: Byte Address Select (RES1 for AArch64) + * E: Enable bit + */ +static int insert_hw_breakpoint(target_ulong addr) +{ + HWBreakpoint brk = { + .bcr = 0x1, /* BCR E=1, enable */ + .bvr = addr + }; + + if (cur_hw_bps >= max_hw_bps) { + return -ENOBUFS; + } + + brk.bcr = deposit32(brk.bcr, 1, 2, 0x3); /* PMC = 11 */ + brk.bcr = deposit32(brk.bcr, 5, 4, 0xf); /* BAS = RES1 */ + + g_array_append_val(hw_breakpoints, brk); + + return 0; +} + +/** + * delete_hw_breakpoint() + * @pc: address of breakpoint + * + * Delete a breakpoint and shuffle any above down + */ + +static int delete_hw_breakpoint(target_ulong pc) +{ + int i; + for (i = 0; i < hw_breakpoints->len; i++) { + HWBreakpoint *brk = get_hw_bp(i); + if (brk->bvr == pc) { + g_array_remove_index(hw_breakpoints, i); + return 0; + } + } + return -ENOENT; +} + +/** + * insert_hw_watchpoint() + * @addr: address of watch point + * @len: size of area + * @type: type of watch point + * + * See ARM ARM D2.10. As with the breakpoints we can do some advanced + * stuff if we want to. The watch points can be linked with the break + * points above to make them context aware. However for simplicity + * currently we only deal with simple read/write watch points. + * + * D7.3.11 DBGWCR_EL1, Debug Watchpoint Control Registers + * + * 31 29 28 24 23 21 20 19 16 15 14 13 12 5 4 3 2 1 0 + * +------+-------+------+----+-----+-----+-----+-----+-----+-----+---+ + * | RES0 | MASK | RES0 | WT | LBN | SSC | HMC | BAS | LSC | PAC | E | + * +------+-------+------+----+-----+-----+-----+-----+-----+-----+---+ + * + * MASK: num bits addr mask (0=none,01/10=res,11=3 bits (8 bytes)) + * WT: 0 - unlinked, 1 - linked (not currently used) + * LBN: Linked BP number (not currently used) + * SSC/HMC/PAC: Security, Higher and Priv access control (Table D2-11) + * BAS: Byte Address Select + * LSC: Load/Store control (01: load, 10: store, 11: both) + * E: Enable + * + * The bottom 2 bits of the value register are masked. Therefore to + * break on any sizes smaller than an unaligned word you need to set + * MASK=0, BAS=bit per byte in question. For larger regions (^2) you + * need to ensure you mask the address as required and set BAS=0xff + */ + +static int insert_hw_watchpoint(target_ulong addr, + target_ulong len, int type) +{ + HWWatchpoint wp = { + .wcr = 1, /* E=1, enable */ + .wvr = addr & (~0x7ULL), + .details = { .vaddr = addr, .len = len } + }; + + if (cur_hw_wps >= max_hw_wps) { + return -ENOBUFS; + } + + /* + * HMC=0 SSC=0 PAC=3 will hit EL0 or EL1, any security state, + * valid whether EL3 is implemented or not + */ + wp.wcr = deposit32(wp.wcr, 1, 2, 3); + + switch (type) { + case GDB_WATCHPOINT_READ: + wp.wcr = deposit32(wp.wcr, 3, 2, 1); + wp.details.flags = BP_MEM_READ; + break; + case GDB_WATCHPOINT_WRITE: + wp.wcr = deposit32(wp.wcr, 3, 2, 2); + wp.details.flags = BP_MEM_WRITE; + break; + case GDB_WATCHPOINT_ACCESS: + wp.wcr = deposit32(wp.wcr, 3, 2, 3); + wp.details.flags = BP_MEM_ACCESS; + break; + default: + g_assert_not_reached(); + break; + } + if (len <= 8) { + /* we align the address and set the bits in BAS */ + int off = addr & 0x7; + int bas = (1 << len) - 1; + + wp.wcr = deposit32(wp.wcr, 5 + off, 8 - off, bas); + } else { + /* For ranges above 8 bytes we need to be a power of 2 */ + if (is_power_of_2(len)) { + int bits = ctz64(len); + + wp.wvr &= ~((1 << bits) - 1); + wp.wcr = deposit32(wp.wcr, 24, 4, bits); + wp.wcr = deposit32(wp.wcr, 5, 8, 0xff); + } else { + return -ENOBUFS; + } + } + + g_array_append_val(hw_watchpoints, wp); + return 0; +} + + +static bool check_watchpoint_in_range(int i, target_ulong addr) +{ + HWWatchpoint *wp = get_hw_wp(i); + uint64_t addr_top, addr_bottom = wp->wvr; + int bas = extract32(wp->wcr, 5, 8); + int mask = extract32(wp->wcr, 24, 4); + + if (mask) { + addr_top = addr_bottom + (1 << mask); + } else { + /* BAS must be contiguous but can offset against the base + * address in DBGWVR */ + addr_bottom = addr_bottom + ctz32(bas); + addr_top = addr_bottom + clo32(bas); + } + + if (addr >= addr_bottom && addr <= addr_top) { + return true; + } + + return false; +} + +/** + * delete_hw_watchpoint() + * @addr: address of breakpoint + * + * Delete a breakpoint and shuffle any above down + */ + +static int delete_hw_watchpoint(target_ulong addr, + target_ulong len, int type) +{ + int i; + for (i = 0; i < cur_hw_wps; i++) { + if (check_watchpoint_in_range(i, addr)) { + g_array_remove_index(hw_watchpoints, i); + return 0; + } + } + return -ENOENT; +} + + +int kvm_arch_insert_hw_breakpoint(target_ulong addr, + target_ulong len, int type) +{ + switch (type) { + case GDB_BREAKPOINT_HW: + return insert_hw_breakpoint(addr); + break; + case GDB_WATCHPOINT_READ: + case GDB_WATCHPOINT_WRITE: + case GDB_WATCHPOINT_ACCESS: + return insert_hw_watchpoint(addr, len, type); + default: + return -ENOSYS; + } +} + +int kvm_arch_remove_hw_breakpoint(target_ulong addr, + target_ulong len, int type) +{ + switch (type) { + case GDB_BREAKPOINT_HW: + return delete_hw_breakpoint(addr); + break; + case GDB_WATCHPOINT_READ: + case GDB_WATCHPOINT_WRITE: + case GDB_WATCHPOINT_ACCESS: + return delete_hw_watchpoint(addr, len, type); + default: + return -ENOSYS; + } +} + + +void kvm_arch_remove_all_hw_breakpoints(void) +{ + if (cur_hw_wps > 0) { + g_array_remove_range(hw_watchpoints, 0, cur_hw_wps); + } + if (cur_hw_bps > 0) { + g_array_remove_range(hw_breakpoints, 0, cur_hw_bps); + } +} + +void kvm_arm_copy_hw_debug_data(struct kvm_guest_debug_arch *ptr) +{ + int i; + memset(ptr, 0, sizeof(struct kvm_guest_debug_arch)); + + for (i = 0; i < max_hw_wps; i++) { + HWWatchpoint *wp = get_hw_wp(i); + ptr->dbg_wcr[i] = wp->wcr; + ptr->dbg_wvr[i] = wp->wvr; + } + for (i = 0; i < max_hw_bps; i++) { + HWBreakpoint *bp = get_hw_bp(i); + ptr->dbg_bcr[i] = bp->bcr; + ptr->dbg_bvr[i] = bp->bvr; + } +} + +bool kvm_arm_hw_debug_active(CPUState *cs) +{ + return ((cur_hw_wps > 0) || (cur_hw_bps > 0)); +} + +static bool find_hw_breakpoint(CPUState *cpu, target_ulong pc) +{ + int i; + + for (i = 0; i < cur_hw_bps; i++) { + HWBreakpoint *bp = get_hw_bp(i); + if (bp->bvr == pc) { + return true; + } + } + return false; +} + +static CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, target_ulong addr) +{ + int i; + + for (i = 0; i < cur_hw_wps; i++) { + if (check_watchpoint_in_range(i, addr)) { + return &get_hw_wp(i)->details; + } + } + return NULL; +} + + static inline void set_feature(uint64_t *features, int feature) { *features |= 1ULL << feature; @@ -77,7 +435,6 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc) return true; } -#define ARM_MPIDR_HWID_BITMASK 0xFF00FFFFFFULL #define ARM_CPU_ID_MPIDR 3, 0, 0, 0, 5 int kvm_arch_init_vcpu(CPUState *cs) @@ -120,7 +477,9 @@ int kvm_arch_init_vcpu(CPUState *cs) if (ret) { return ret; } - cpu->mp_affinity = mpidr & ARM_MPIDR_HWID_BITMASK; + cpu->mp_affinity = mpidr & ARM64_AFFINITY_MASK; + + kvm_arm_init_debug(cs); return kvm_arm_init_cpreg_list(cpu); } @@ -363,8 +722,7 @@ int kvm_arch_get_registers(CPUState *cs) if (is_a64(env)) { pstate_write(env, val); } else { - env->uncached_cpsr = val & CPSR_M; - cpsr_write(env, val, 0xffffffff); + cpsr_write(env, val, 0xffffffff, CPSRWriteRaw); } /* KVM puts SP_EL0 in regs.sp and SP_EL1 in regs.sp_el1. On the @@ -464,3 +822,105 @@ int kvm_arch_get_registers(CPUState *cs) /* TODO: other registers */ return ret; } + +/* C6.6.29 BRK instruction */ +static const uint32_t brk_insn = 0xd4200000; + +int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) +{ + if (have_guest_debug) { + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) || + cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&brk_insn, 4, 1)) { + return -EINVAL; + } + return 0; + } else { + error_report("guest debug not supported on this kernel"); + return -EINVAL; + } +} + +int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) +{ + static uint32_t brk; + + if (have_guest_debug) { + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&brk, 4, 0) || + brk != brk_insn || + cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 1)) { + return -EINVAL; + } + return 0; + } else { + error_report("guest debug not supported on this kernel"); + return -EINVAL; + } +} + +/* See v8 ARM ARM D7.2.27 ESR_ELx, Exception Syndrome Register + * + * To minimise translating between kernel and user-space the kernel + * ABI just provides user-space with the full exception syndrome + * register value to be decoded in QEMU. + */ + +bool kvm_arm_handle_debug(CPUState *cs, struct kvm_debug_exit_arch *debug_exit) +{ + int hsr_ec = debug_exit->hsr >> ARM_EL_EC_SHIFT; + ARMCPU *cpu = ARM_CPU(cs); + CPUClass *cc = CPU_GET_CLASS(cs); + CPUARMState *env = &cpu->env; + + /* Ensure PC is synchronised */ + kvm_cpu_synchronize_state(cs); + + switch (hsr_ec) { + case EC_SOFTWARESTEP: + if (cs->singlestep_enabled) { + return true; + } else { + /* + * The kernel should have suppressed the guest's ability to + * single step at this point so something has gone wrong. + */ + error_report("%s: guest single-step while debugging unsupported" + " (%"PRIx64", %"PRIx32")\n", + __func__, env->pc, debug_exit->hsr); + return false; + } + break; + case EC_AA64_BKPT: + if (kvm_find_sw_breakpoint(cs, env->pc)) { + return true; + } + break; + case EC_BREAKPOINT: + if (find_hw_breakpoint(cs, env->pc)) { + return true; + } + break; + case EC_WATCHPOINT: + { + CPUWatchpoint *wp = find_hw_watchpoint(cs, debug_exit->far); + if (wp) { + cs->watchpoint_hit = wp; + return true; + } + break; + } + default: + error_report("%s: unhandled debug exit (%"PRIx32", %"PRIx64")\n", + __func__, debug_exit->hsr, env->pc); + } + + /* If we are not handling the debug exception it must belong to + * the guest. Let's re-use the existing TCG interrupt code to set + * everything up properly. + */ + cs->exception_index = EXCP_BKPT; + env->exception.syndrome = debug_exit->hsr; + env->exception.vaddress = debug_exit->far; + cc->do_interrupt(cs); + + return false; +} diff --git a/qemu/target-arm/kvm_arm.h b/qemu/target-arm/kvm_arm.h index 7912d7433..345233c18 100644 --- a/qemu/target-arm/kvm_arm.h +++ b/qemu/target-arm/kvm_arm.h @@ -124,9 +124,12 @@ void kvm_arm_reset_vcpu(ARMCPU *cpu); * kvm_arm_create_scratch_host_vcpu: * @cpus_to_try: array of QEMU_KVM_ARM_TARGET_* values (terminated with * QEMU_KVM_ARM_TARGET_NONE) to try as fallback if the kernel does not - * know the PREFERRED_TARGET ioctl + * know the PREFERRED_TARGET ioctl. Passing NULL is the same as passing + * an empty array. * @fdarray: filled in with kvmfd, vmfd, cpufd file descriptors in that order - * @init: filled in with the necessary values for creating a host vcpu + * @init: filled in with the necessary values for creating a host + * vcpu. If NULL is provided, will not init the vCPU (though the cpufd + * will still be set up). * * Create a scratch vcpu in its own VM of the type preferred by the host * kernel (as would be used for '-cpu host'), for purposes of probing it @@ -189,6 +192,60 @@ int kvm_arm_sync_mpstate_to_kvm(ARMCPU *cpu); */ int kvm_arm_sync_mpstate_to_qemu(ARMCPU *cpu); +int kvm_arm_vgic_probe(void); + +#else + +static inline int kvm_arm_vgic_probe(void) +{ + return 0; +} + #endif +static inline const char *gic_class_name(void) +{ + return kvm_irqchip_in_kernel() ? "kvm-arm-gic" : "arm_gic"; +} + +/** + * gicv3_class_name + * + * Return name of GICv3 class to use depending on whether KVM acceleration is + * in use. May throw an error if the chosen implementation is not available. + * + * Returns: class name to use + */ +const char *gicv3_class_name(void); + +/** + * kvm_arm_handle_debug: + * @cs: CPUState + * @debug_exit: debug part of the KVM exit structure + * + * Returns: TRUE if the debug exception was handled. + */ +bool kvm_arm_handle_debug(CPUState *cs, struct kvm_debug_exit_arch *debug_exit); + +/** + * kvm_arm_hw_debug_active: + * @cs: CPU State + * + * Return: TRUE if any hardware breakpoints in use. + */ + +bool kvm_arm_hw_debug_active(CPUState *cs); + +/** + * kvm_arm_copy_hw_debug_data: + * + * @ptr: kvm_guest_debug_arch structure + * + * Copy the architecture specific debug registers into the + * kvm_guest_debug ioctl structure. + */ +struct kvm_guest_debug_arch; + +void kvm_arm_copy_hw_debug_data(struct kvm_guest_debug_arch *ptr); + #endif diff --git a/qemu/target-arm/machine.c b/qemu/target-arm/machine.c index 32adfe792..03a73d950 100644 --- a/qemu/target-arm/machine.c +++ b/qemu/target-arm/machine.c @@ -1,5 +1,7 @@ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/boards.h" +#include "qemu/error-report.h" #include "sysemu/kvm.h" #include "kvm_arm.h" #include "internals.h" @@ -171,9 +173,7 @@ static int get_cpsr(QEMUFile *f, void *opaque, size_t size) return 0; } - /* Avoid mode switch when restoring CPSR */ - env->uncached_cpsr = val & CPSR_M; - cpsr_write(env, val, 0xffffffff); + cpsr_write(env, val, 0xffffffff, CPSRWriteRaw); return 0; } @@ -328,3 +328,20 @@ const VMStateDescription vmstate_arm_cpu = { NULL } }; + +const char *gicv3_class_name(void) +{ + if (kvm_irqchip_in_kernel()) { +#ifdef TARGET_AARCH64 + return "kvm-arm-gicv3"; +#else + error_report("KVM GICv3 acceleration is not supported on this " + "platform"); +#endif + } else { + /* TODO: Software emulation is not implemented yet */ + error_report("KVM is currently required for GICv3 emulation"); + } + + exit(1); +} diff --git a/qemu/target-arm/monitor.c b/qemu/target-arm/monitor.c new file mode 100644 index 000000000..1ee59a2e4 --- /dev/null +++ b/qemu/target-arm/monitor.c @@ -0,0 +1,84 @@ +/* + * QEMU monitor.c for ARM. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "qmp-commands.h" +#include "hw/boards.h" +#include "kvm_arm.h" + +static GICCapability *gic_cap_new(int version) +{ + GICCapability *cap = g_new0(GICCapability, 1); + cap->version = version; + /* by default, support none */ + cap->emulated = false; + cap->kernel = false; + return cap; +} + +static GICCapabilityList *gic_cap_list_add(GICCapabilityList *head, + GICCapability *cap) +{ + GICCapabilityList *item = g_new0(GICCapabilityList, 1); + item->value = cap; + item->next = head; + return item; +} + +static inline void gic_cap_kvm_probe(GICCapability *v2, GICCapability *v3) +{ +#ifdef CONFIG_KVM + int fdarray[3]; + + if (!kvm_arm_create_scratch_host_vcpu(NULL, fdarray, NULL)) { + return; + } + + /* Test KVM GICv2 */ + if (kvm_device_supported(fdarray[1], KVM_DEV_TYPE_ARM_VGIC_V2)) { + v2->kernel = true; + } + + /* Test KVM GICv3 */ + if (kvm_device_supported(fdarray[1], KVM_DEV_TYPE_ARM_VGIC_V3)) { + v3->kernel = true; + } + + kvm_arm_destroy_scratch_host_vcpu(fdarray); +#endif +} + +GICCapabilityList *qmp_query_gic_capabilities(Error **errp) +{ + GICCapabilityList *head = NULL; + GICCapability *v2 = gic_cap_new(2), *v3 = gic_cap_new(3); + + v2->emulated = true; + /* TODO: we'd change to true after we get emulated GICv3. */ + v3->emulated = false; + + gic_cap_kvm_probe(v2, v3); + + head = gic_cap_list_add(head, v2); + head = gic_cap_list_add(head, v3); + + return head; +} diff --git a/qemu/target-arm/neon_helper.c b/qemu/target-arm/neon_helper.c index 47d13e908..1f1844f5b 100644 --- a/qemu/target-arm/neon_helper.c +++ b/qemu/target-arm/neon_helper.c @@ -6,8 +6,7 @@ * * This code is licensed under the GNU GPL v2. */ -#include -#include +#include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" diff --git a/qemu/target-arm/op_helper.c b/qemu/target-arm/op_helper.c index 663c05d1d..d626ff1a2 100644 --- a/qemu/target-arm/op_helper.c +++ b/qemu/target-arm/op_helper.c @@ -16,6 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "internals.h" @@ -83,19 +84,27 @@ void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx, { bool ret; uint32_t fsr = 0; + ARMMMUFaultInfo fi = {}; - ret = arm_tlb_fill(cs, addr, is_write, mmu_idx, &fsr); + ret = arm_tlb_fill(cs, addr, is_write, mmu_idx, &fsr, &fi); if (unlikely(ret)) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; uint32_t syn, exc; - bool same_el = (arm_current_el(env) != 0); + unsigned int target_el; + bool same_el; if (retaddr) { /* now we have a real cpu fault */ cpu_restore_state(cs, retaddr); } + target_el = exception_target_el(env); + if (fi.stage2) { + target_el = 2; + env->cp15.hpfar_el2 = extract64(fi.s2addr, 12, 47) << 4; + } + same_el = arm_current_el(env) == target_el; /* AArch64 syndrome does not have an LPAE bit */ syn = fsr & ~(1 << 9); @@ -103,10 +112,10 @@ void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx, * information; this is always true for exceptions reported to EL1. */ if (is_write == 2) { - syn = syn_insn_abort(same_el, 0, 0, syn); + syn = syn_insn_abort(same_el, 0, fi.s1ptw, syn); exc = EXCP_PREFETCH_ABORT; } else { - syn = syn_data_abort(same_el, 0, 0, 0, is_write == 1, syn); + syn = syn_data_abort(same_el, 0, 0, fi.s1ptw, is_write == 1, syn); if (is_write == 1 && arm_feature(env, ARM_FEATURE_V6)) { fsr |= (1 << 11); } @@ -115,10 +124,48 @@ void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx, env->exception.vaddress = addr; env->exception.fsr = fsr; - raise_exception(env, exc, syn, exception_target_el(env)); + raise_exception(env, exc, syn, target_el); + } +} + +/* Raise a data fault alignment exception for the specified virtual address */ +void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, int is_write, + int is_user, uintptr_t retaddr) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + int target_el; + bool same_el; + + if (retaddr) { + /* now we have a real cpu fault */ + cpu_restore_state(cs, retaddr); } + + target_el = exception_target_el(env); + same_el = (arm_current_el(env) == target_el); + + env->exception.vaddress = vaddr; + + /* the DFSR for an alignment fault depends on whether we're using + * the LPAE long descriptor format, or the short descriptor format + */ + if (arm_s1_regime_using_lpae_format(env, cpu_mmu_index(env, false))) { + env->exception.fsr = 0x21; + } else { + env->exception.fsr = 0x1; + } + + if (is_write == 1 && arm_feature(env, ARM_FEATURE_V6)) { + env->exception.fsr |= (1 << 11); + } + + raise_exception(env, EXCP_DATA_ABORT, + syn_data_abort(same_el, 0, 0, 0, is_write == 1, 0x21), + target_el); } -#endif + +#endif /* !defined(CONFIG_USER_ONLY) */ uint32_t HELPER(add_setq)(CPUARMState *env, uint32_t a, uint32_t b) { @@ -249,6 +296,11 @@ uint32_t HELPER(usat16)(CPUARMState *env, uint32_t x, uint32_t shift) return res; } +void HELPER(setend)(CPUARMState *env) +{ + env->uncached_cpsr ^= CPSR_E; +} + /* Function checks whether WFx (WFI/WFE) instructions are set up to be trapped. * The function returns the target EL (1-3) if the instruction is to be trapped; * otherwise it returns 0 indicating it is not trapped. @@ -375,7 +427,13 @@ uint32_t HELPER(cpsr_read)(CPUARMState *env) void HELPER(cpsr_write)(CPUARMState *env, uint32_t val, uint32_t mask) { - cpsr_write(env, val, mask); + cpsr_write(env, val, mask, CPSRWriteByInstr); +} + +/* Write the CPSR for a 32-bit exception return */ +void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val) +{ + cpsr_write(env, val, CPSR_ERET_MASK, CPSRWriteExceptionReturn); } /* Access to user mode registers from privileged modes. */ @@ -384,9 +442,9 @@ uint32_t HELPER(get_user_reg)(CPUARMState *env, uint32_t regno) uint32_t val; if (regno == 13) { - val = env->banked_r13[0]; + val = env->banked_r13[BANK_USRSYS]; } else if (regno == 14) { - val = env->banked_r14[0]; + val = env->banked_r14[BANK_USRSYS]; } else if (regno >= 8 && (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) { val = env->usr_regs[regno - 8]; @@ -399,9 +457,9 @@ uint32_t HELPER(get_user_reg)(CPUARMState *env, uint32_t regno) void HELPER(set_user_reg)(CPUARMState *env, uint32_t regno, uint32_t val) { if (regno == 13) { - env->banked_r13[0] = val; + env->banked_r13[BANK_USRSYS] = val; } else if (regno == 14) { - env->banked_r14[0] = val; + env->banked_r14[BANK_USRSYS] = val; } else if (regno >= 8 && (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) { env->usr_regs[regno - 8] = val; @@ -410,7 +468,154 @@ void HELPER(set_user_reg)(CPUARMState *env, uint32_t regno, uint32_t val) } } -void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome) +void HELPER(set_r13_banked)(CPUARMState *env, uint32_t mode, uint32_t val) +{ + if ((env->uncached_cpsr & CPSR_M) == mode) { + env->regs[13] = val; + } else { + env->banked_r13[bank_number(mode)] = val; + } +} + +uint32_t HELPER(get_r13_banked)(CPUARMState *env, uint32_t mode) +{ + if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_SYS) { + /* SRS instruction is UNPREDICTABLE from System mode; we UNDEF. + * Other UNPREDICTABLE and UNDEF cases were caught at translate time. + */ + raise_exception(env, EXCP_UDEF, syn_uncategorized(), + exception_target_el(env)); + } + + if ((env->uncached_cpsr & CPSR_M) == mode) { + return env->regs[13]; + } else { + return env->banked_r13[bank_number(mode)]; + } +} + +static void msr_mrs_banked_exc_checks(CPUARMState *env, uint32_t tgtmode, + uint32_t regno) +{ + /* Raise an exception if the requested access is one of the UNPREDICTABLE + * cases; otherwise return. This broadly corresponds to the pseudocode + * BankedRegisterAccessValid() and SPSRAccessValid(), + * except that we have already handled some cases at translate time. + */ + int curmode = env->uncached_cpsr & CPSR_M; + + if (curmode == tgtmode) { + goto undef; + } + + if (tgtmode == ARM_CPU_MODE_USR) { + switch (regno) { + case 8 ... 12: + if (curmode != ARM_CPU_MODE_FIQ) { + goto undef; + } + break; + case 13: + if (curmode == ARM_CPU_MODE_SYS) { + goto undef; + } + break; + case 14: + if (curmode == ARM_CPU_MODE_HYP || curmode == ARM_CPU_MODE_SYS) { + goto undef; + } + break; + default: + break; + } + } + + if (tgtmode == ARM_CPU_MODE_HYP) { + switch (regno) { + case 17: /* ELR_Hyp */ + if (curmode != ARM_CPU_MODE_HYP && curmode != ARM_CPU_MODE_MON) { + goto undef; + } + break; + default: + if (curmode != ARM_CPU_MODE_MON) { + goto undef; + } + break; + } + } + + return; + +undef: + raise_exception(env, EXCP_UDEF, syn_uncategorized(), + exception_target_el(env)); +} + +void HELPER(msr_banked)(CPUARMState *env, uint32_t value, uint32_t tgtmode, + uint32_t regno) +{ + msr_mrs_banked_exc_checks(env, tgtmode, regno); + + switch (regno) { + case 16: /* SPSRs */ + env->banked_spsr[bank_number(tgtmode)] = value; + break; + case 17: /* ELR_Hyp */ + env->elr_el[2] = value; + break; + case 13: + env->banked_r13[bank_number(tgtmode)] = value; + break; + case 14: + env->banked_r14[bank_number(tgtmode)] = value; + break; + case 8 ... 12: + switch (tgtmode) { + case ARM_CPU_MODE_USR: + env->usr_regs[regno - 8] = value; + break; + case ARM_CPU_MODE_FIQ: + env->fiq_regs[regno - 8] = value; + break; + default: + g_assert_not_reached(); + } + break; + default: + g_assert_not_reached(); + } +} + +uint32_t HELPER(mrs_banked)(CPUARMState *env, uint32_t tgtmode, uint32_t regno) +{ + msr_mrs_banked_exc_checks(env, tgtmode, regno); + + switch (regno) { + case 16: /* SPSRs */ + return env->banked_spsr[bank_number(tgtmode)]; + case 17: /* ELR_Hyp */ + return env->elr_el[2]; + case 13: + return env->banked_r13[bank_number(tgtmode)]; + case 14: + return env->banked_r14[bank_number(tgtmode)]; + case 8 ... 12: + switch (tgtmode) { + case ARM_CPU_MODE_USR: + return env->usr_regs[regno - 8]; + case ARM_CPU_MODE_FIQ: + return env->fiq_regs[regno - 8]; + default: + g_assert_not_reached(); + } + default: + g_assert_not_reached(); + } +} + +void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome, + uint32_t isread) { const ARMCPRegInfo *ri = rip; int target_el; @@ -424,7 +629,7 @@ void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome) return; } - switch (ri->accessfn(env, ri)) { + switch (ri->accessfn(env, ri, isread)) { case CP_ACCESS_OK: return; case CP_ACCESS_TRAP: @@ -444,6 +649,27 @@ void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome) target_el = exception_target_el(env); syndrome = syn_uncategorized(); break; + case CP_ACCESS_TRAP_UNCATEGORIZED_EL2: + target_el = 2; + syndrome = syn_uncategorized(); + break; + case CP_ACCESS_TRAP_UNCATEGORIZED_EL3: + target_el = 3; + syndrome = syn_uncategorized(); + break; + case CP_ACCESS_TRAP_FP_EL2: + target_el = 2; + /* Since we are an implementation that takes exceptions on a trapped + * conditional insn only if the insn has passed its condition code + * check, we take the IMPDEF choice to always report CV=1 COND=0xe + * (which is also the required value for AArch64 traps). + */ + syndrome = syn_fp_access_trap(1, 0xe, false); + break; + case CP_ACCESS_TRAP_FP_EL3: + target_el = 3; + syndrome = syn_fp_access_trap(1, 0xe, false); + break; default: g_assert_not_reached(); } @@ -558,12 +784,14 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome) int cur_el = arm_current_el(env); bool secure = arm_is_secure(env); bool smd = env->cp15.scr_el3 & SCR_SMD; - /* On ARMv8 AArch32, SMD only applies to NS state. - * On ARMv7 SMD only applies to NS state and only if EL2 is available. - * For ARMv7 non EL2, we force SMD to zero so we don't need to re-check - * the EL2 condition here. + /* On ARMv8 with EL3 AArch64, SMD applies to both S and NS state. + * On ARMv8 with EL3 AArch32, or ARMv7 with the Virtualization + * extensions, SMD only applies to NS state. + * On ARMv7 without the Virtualization extensions, the SMD bit + * doesn't exist, but we forbid the guest to set it to 1 in scr_write(), + * so we need not special case this here. */ - bool undef = is_a64(env) ? smd : (!secure && smd); + bool undef = arm_feature(env, ARM_FEATURE_AARCH64) ? smd : smd && !secure; if (arm_is_psci_call(cpu, EXCP_SMC)) { /* If PSCI is enabled and this looks like a valid PSCI call then @@ -586,12 +814,51 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome) } } +static int el_from_spsr(uint32_t spsr) +{ + /* Return the exception level that this SPSR is requesting a return to, + * or -1 if it is invalid (an illegal return) + */ + if (spsr & PSTATE_nRW) { + switch (spsr & CPSR_M) { + case ARM_CPU_MODE_USR: + return 0; + case ARM_CPU_MODE_HYP: + return 2; + case ARM_CPU_MODE_FIQ: + case ARM_CPU_MODE_IRQ: + case ARM_CPU_MODE_SVC: + case ARM_CPU_MODE_ABT: + case ARM_CPU_MODE_UND: + case ARM_CPU_MODE_SYS: + return 1; + case ARM_CPU_MODE_MON: + /* Returning to Mon from AArch64 is never possible, + * so this is an illegal return. + */ + default: + return -1; + } + } else { + if (extract32(spsr, 1, 1)) { + /* Return with reserved M[1] bit set */ + return -1; + } + if (extract32(spsr, 0, 4) == 1) { + /* return to EL0 with M[0] bit set */ + return -1; + } + return extract32(spsr, 2, 2); + } +} + void HELPER(exception_return)(CPUARMState *env) { int cur_el = arm_current_el(env); unsigned int spsr_idx = aarch64_banked_spsr_index(cur_el); uint32_t spsr = env->banked_spsr[spsr_idx]; int new_el; + bool return_to_aa64 = (spsr & PSTATE_nRW) == 0; aarch64_save_sp(env, cur_el); @@ -608,35 +875,51 @@ void HELPER(exception_return)(CPUARMState *env) spsr &= ~PSTATE_SS; } - if (spsr & PSTATE_nRW) { - /* TODO: We currently assume EL1/2/3 are running in AArch64. */ + new_el = el_from_spsr(spsr); + if (new_el == -1) { + goto illegal_return; + } + if (new_el > cur_el + || (new_el == 2 && !arm_feature(env, ARM_FEATURE_EL2))) { + /* Disallow return to an EL which is unimplemented or higher + * than the current one. + */ + goto illegal_return; + } + + if (new_el != 0 && arm_el_is_aa64(env, new_el) != return_to_aa64) { + /* Return to an EL which is configured for a different register width */ + goto illegal_return; + } + + if (new_el == 2 && arm_is_secure_below_el3(env)) { + /* Return to the non-existent secure-EL2 */ + goto illegal_return; + } + + if (new_el == 1 && (env->cp15.hcr_el2 & HCR_TGE) + && !arm_is_secure_below_el3(env)) { + goto illegal_return; + } + + if (!return_to_aa64) { env->aarch64 = 0; - new_el = 0; - env->uncached_cpsr = 0x10; - cpsr_write(env, spsr, ~0); + /* We do a raw CPSR write because aarch64_sync_64_to_32() + * will sort the register banks out for us, and we've already + * caught all the bad-mode cases in el_from_spsr(). + */ + cpsr_write(env, spsr, ~0, CPSRWriteRaw); if (!arm_singlestep_active(env)) { env->uncached_cpsr &= ~PSTATE_SS; } aarch64_sync_64_to_32(env); - env->regs[15] = env->elr_el[1] & ~0x1; - } else { - new_el = extract32(spsr, 2, 2); - if (new_el > cur_el - || (new_el == 2 && !arm_feature(env, ARM_FEATURE_EL2))) { - /* Disallow return to an EL which is unimplemented or higher - * than the current one. - */ - goto illegal_return; - } - if (extract32(spsr, 1, 1)) { - /* Return with reserved M[1] bit set */ - goto illegal_return; - } - if (new_el == 0 && (spsr & PSTATE_SP)) { - /* Return to EL0 with M[0] bit set */ - goto illegal_return; + if (spsr & CPSR_T) { + env->regs[15] = env->elr_el[cur_el] & ~0x1; + } else { + env->regs[15] = env->elr_el[cur_el] & ~0x3; } + } else { env->aarch64 = 1; pstate_write(env, spsr); if (!arm_singlestep_active(env)) { @@ -859,6 +1142,25 @@ static bool check_breakpoints(ARMCPU *cpu) return false; } +void HELPER(check_breakpoints)(CPUARMState *env) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + + if (check_breakpoints(cpu)) { + HELPER(exception_internal(env, EXCP_DEBUG)); + } +} + +bool arm_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) +{ + /* Called by core code when a CPU watchpoint fires; need to check if this + * is also an architectural watchpoint match. + */ + ARMCPU *cpu = ARM_CPU(cs); + + return check_watchpoints(cpu); +} + void arm_debug_excp_handler(CPUState *cs) { /* Called by core code when a watchpoint or breakpoint fires; @@ -870,37 +1172,44 @@ void arm_debug_excp_handler(CPUState *cs) if (wp_hit) { if (wp_hit->flags & BP_CPU) { + bool wnr = (wp_hit->flags & BP_WATCHPOINT_HIT_WRITE) != 0; + bool same_el = arm_debug_target_el(env) == arm_current_el(env); + cs->watchpoint_hit = NULL; - if (check_watchpoints(cpu)) { - bool wnr = (wp_hit->flags & BP_WATCHPOINT_HIT_WRITE) != 0; - bool same_el = arm_debug_target_el(env) == arm_current_el(env); - - if (extended_addresses_enabled(env)) { - env->exception.fsr = (1 << 9) | 0x22; - } else { - env->exception.fsr = 0x2; - } - env->exception.vaddress = wp_hit->hitaddr; - raise_exception(env, EXCP_DATA_ABORT, - syn_watchpoint(same_el, 0, wnr), - arm_debug_target_el(env)); - } else { - cpu_resume_from_signal(cs, NULL); - } - } - } else { - if (check_breakpoints(cpu)) { - bool same_el = (arm_debug_target_el(env) == arm_current_el(env)); + if (extended_addresses_enabled(env)) { env->exception.fsr = (1 << 9) | 0x22; } else { env->exception.fsr = 0x2; } - /* FAR is UNKNOWN, so doesn't need setting */ - raise_exception(env, EXCP_PREFETCH_ABORT, - syn_breakpoint(same_el), - arm_debug_target_el(env)); + env->exception.vaddress = wp_hit->hitaddr; + raise_exception(env, EXCP_DATA_ABORT, + syn_watchpoint(same_el, 0, wnr), + arm_debug_target_el(env)); + } + } else { + uint64_t pc = is_a64(env) ? env->pc : env->regs[15]; + bool same_el = (arm_debug_target_el(env) == arm_current_el(env)); + + /* (1) GDB breakpoints should be handled first. + * (2) Do not raise a CPU exception if no CPU breakpoint has fired, + * since singlestep is also done by generating a debug internal + * exception. + */ + if (cpu_breakpoint_test(cs, pc, BP_GDB) + || !cpu_breakpoint_test(cs, pc, BP_CPU)) { + return; + } + + if (extended_addresses_enabled(env)) { + env->exception.fsr = (1 << 9) | 0x22; + } else { + env->exception.fsr = 0x2; } + /* FAR is UNKNOWN, so doesn't need setting */ + raise_exception(env, EXCP_PREFETCH_ABORT, + syn_breakpoint(same_el), + arm_debug_target_el(env)); } } diff --git a/qemu/target-arm/psci.c b/qemu/target-arm/psci.c index 20e4cb6f9..c55487f87 100644 --- a/qemu/target-arm/psci.c +++ b/qemu/target-arm/psci.c @@ -15,6 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" #include #include #include diff --git a/qemu/target-arm/translate-a64.c b/qemu/target-arm/translate-a64.c index 689f2be89..b13cff756 100644 --- a/qemu/target-arm/translate-a64.c +++ b/qemu/target-arm/translate-a64.c @@ -16,11 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include "cpu.h" #include "tcg-op.h" @@ -30,25 +26,20 @@ #include "internals.h" #include "qemu/host-utils.h" +#include "exec/semihost.h" #include "exec/gen-icount.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" +#include "exec/log.h" #include "trace-tcg.h" static TCGv_i64 cpu_X[32]; static TCGv_i64 cpu_pc; -static TCGv_i32 cpu_NF, cpu_ZF, cpu_CF, cpu_VF; /* Load/store exclusive handling */ -static TCGv_i64 cpu_exclusive_addr; -static TCGv_i64 cpu_exclusive_val; static TCGv_i64 cpu_exclusive_high; -#ifdef CONFIG_USER_ONLY -static TCGv_i64 cpu_exclusive_test; -static TCGv_i32 cpu_exclusive_info; -#endif static const char *regnames[] = { "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", @@ -95,32 +86,17 @@ void a64_translate_init(void) { int i; - cpu_pc = tcg_global_mem_new_i64(TCG_AREG0, + cpu_pc = tcg_global_mem_new_i64(cpu_env, offsetof(CPUARMState, pc), "pc"); for (i = 0; i < 32; i++) { - cpu_X[i] = tcg_global_mem_new_i64(TCG_AREG0, + cpu_X[i] = tcg_global_mem_new_i64(cpu_env, offsetof(CPUARMState, xregs[i]), regnames[i]); } - cpu_NF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, NF), "NF"); - cpu_ZF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, ZF), "ZF"); - cpu_CF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, CF), "CF"); - cpu_VF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, VF), "VF"); - - cpu_exclusive_addr = tcg_global_mem_new_i64(TCG_AREG0, - offsetof(CPUARMState, exclusive_addr), "exclusive_addr"); - cpu_exclusive_val = tcg_global_mem_new_i64(TCG_AREG0, - offsetof(CPUARMState, exclusive_val), "exclusive_val"); - cpu_exclusive_high = tcg_global_mem_new_i64(TCG_AREG0, + cpu_exclusive_high = tcg_global_mem_new_i64(cpu_env, offsetof(CPUARMState, exclusive_high), "exclusive_high"); -#ifdef CONFIG_USER_ONLY - cpu_exclusive_test = tcg_global_mem_new_i64(TCG_AREG0, - offsetof(CPUARMState, exclusive_test), "exclusive_test"); - cpu_exclusive_info = tcg_global_mem_new_i32(TCG_AREG0, - offsetof(CPUARMState, exclusive_info), "exclusive_info"); -#endif } static inline ARMMMUIdx get_a64_user_mem_index(DisasContext *s) @@ -147,6 +123,8 @@ void aarch64_cpu_dump_state(CPUState *cs, FILE *f, CPUARMState *env = &cpu->env; uint32_t psr = pstate_read(env); int i; + int el = arm_current_el(env); + const char *ns_status; cpu_fprintf(f, "PC=%016"PRIx64" SP=%016"PRIx64"\n", env->pc, env->xregs[31]); @@ -158,13 +136,22 @@ void aarch64_cpu_dump_state(CPUState *cs, FILE *f, cpu_fprintf(f, " "); } } - cpu_fprintf(f, "PSTATE=%08x (flags %c%c%c%c)\n", + + if (arm_feature(env, ARM_FEATURE_EL3) && el != 3) { + ns_status = env->cp15.scr_el3 & SCR_NS ? "NS " : "S "; + } else { + ns_status = ""; + } + + cpu_fprintf(f, "\nPSTATE=%08x %c%c%c%c %sEL%d%c\n", psr, psr & PSTATE_N ? 'N' : '-', psr & PSTATE_Z ? 'Z' : '-', psr & PSTATE_C ? 'C' : '-', - psr & PSTATE_V ? 'V' : '-'); - cpu_fprintf(f, "\n"); + psr & PSTATE_V ? 'V' : '-', + ns_status, + el, + psr & PSTATE_SP ? 'h' : 't'); if (flags & CPU_DUMP_FPU) { int numvfpregs = 32; @@ -188,6 +175,31 @@ void gen_a64_set_pc_im(uint64_t val) tcg_gen_movi_i64(cpu_pc, val); } +typedef struct DisasCompare64 { + TCGCond cond; + TCGv_i64 value; +} DisasCompare64; + +static void a64_test_cc(DisasCompare64 *c64, int cc) +{ + DisasCompare c32; + + arm_test_cc(&c32, cc); + + /* Sign-extend the 32-bit value so that the GE/LT comparisons work + * properly. The NE/EQ comparisons are also fine with this choice. */ + c64->cond = c32.cond; + c64->value = tcg_temp_new_i64(); + tcg_gen_ext_i32_i64(c64->value, c32.value); + + arm_free_cc(&c32); +} + +static void a64_free_cc(DisasCompare64 *c64) +{ + tcg_temp_free_i64(c64->value); +} + static void gen_exception_internal(int excp) { TCGv_i32 tcg_excp = tcg_const_i32(excp); @@ -525,13 +537,8 @@ static TCGv_ptr get_fpstatus_ptr(void) */ static inline void gen_set_NZ64(TCGv_i64 result) { - TCGv_i64 flag = tcg_temp_new_i64(); - - tcg_gen_setcondi_i64(TCG_COND_NE, flag, result, 0); - tcg_gen_trunc_i64_i32(cpu_ZF, flag); - tcg_gen_shri_i64(flag, result, 32); - tcg_gen_trunc_i64_i32(cpu_NF, flag); - tcg_temp_free_i64(flag); + tcg_gen_extr_i64_i32(cpu_ZF, cpu_NF, result); + tcg_gen_or_i32(cpu_ZF, cpu_ZF, cpu_NF); } /* Set NZCV as for a logical operation: NZ as per result, CV cleared. */ @@ -540,8 +547,8 @@ static inline void gen_logic_CC(int sf, TCGv_i64 result) if (sf) { gen_set_NZ64(result); } else { - tcg_gen_trunc_i64_i32(cpu_ZF, result); - tcg_gen_trunc_i64_i32(cpu_NF, result); + tcg_gen_extrl_i64_i32(cpu_ZF, result); + tcg_gen_mov_i32(cpu_NF, cpu_ZF); } tcg_gen_movi_i32(cpu_CF, 0); tcg_gen_movi_i32(cpu_VF, 0); @@ -559,7 +566,7 @@ static void gen_add_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) tcg_gen_movi_i64(tmp, 0); tcg_gen_add2_i64(result, flag, t0, tmp, t1, tmp); - tcg_gen_trunc_i64_i32(cpu_CF, flag); + tcg_gen_extrl_i64_i32(cpu_CF, flag); gen_set_NZ64(result); @@ -567,8 +574,7 @@ static void gen_add_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) tcg_gen_xor_i64(tmp, t0, t1); tcg_gen_andc_i64(flag, flag, tmp); tcg_temp_free_i64(tmp); - tcg_gen_shri_i64(flag, flag, 32); - tcg_gen_trunc_i64_i32(cpu_VF, flag); + tcg_gen_extrh_i64_i32(cpu_VF, flag); tcg_gen_mov_i64(dest, result); tcg_temp_free_i64(result); @@ -580,8 +586,8 @@ static void gen_add_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, 0); - tcg_gen_trunc_i64_i32(t0_32, t0); - tcg_gen_trunc_i64_i32(t1_32, t1); + tcg_gen_extrl_i64_i32(t0_32, t0); + tcg_gen_extrl_i64_i32(t1_32, t1); tcg_gen_add2_i32(cpu_NF, cpu_CF, t0_32, tmp, t1_32, tmp); tcg_gen_mov_i32(cpu_ZF, cpu_NF); tcg_gen_xor_i32(cpu_VF, cpu_NF, t0_32); @@ -609,15 +615,14 @@ static void gen_sub_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) gen_set_NZ64(result); tcg_gen_setcond_i64(TCG_COND_GEU, flag, t0, t1); - tcg_gen_trunc_i64_i32(cpu_CF, flag); + tcg_gen_extrl_i64_i32(cpu_CF, flag); tcg_gen_xor_i64(flag, result, t0); tmp = tcg_temp_new_i64(); tcg_gen_xor_i64(tmp, t0, t1); tcg_gen_and_i64(flag, flag, tmp); tcg_temp_free_i64(tmp); - tcg_gen_shri_i64(flag, flag, 32); - tcg_gen_trunc_i64_i32(cpu_VF, flag); + tcg_gen_extrh_i64_i32(cpu_VF, flag); tcg_gen_mov_i64(dest, result); tcg_temp_free_i64(flag); tcg_temp_free_i64(result); @@ -627,8 +632,8 @@ static void gen_sub_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) TCGv_i32 t1_32 = tcg_temp_new_i32(); TCGv_i32 tmp; - tcg_gen_trunc_i64_i32(t0_32, t0); - tcg_gen_trunc_i64_i32(t1_32, t1); + tcg_gen_extrl_i64_i32(t0_32, t0); + tcg_gen_extrl_i64_i32(t1_32, t1); tcg_gen_sub_i32(cpu_NF, t0_32, t1_32); tcg_gen_mov_i32(cpu_ZF, cpu_NF); tcg_gen_setcond_i32(TCG_COND_GEU, cpu_CF, t0_32, t1_32); @@ -670,14 +675,13 @@ static void gen_adc_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) tcg_gen_extu_i32_i64(cf_64, cpu_CF); tcg_gen_add2_i64(result, cf_64, t0, tmp, cf_64, tmp); tcg_gen_add2_i64(result, cf_64, result, cf_64, t1, tmp); - tcg_gen_trunc_i64_i32(cpu_CF, cf_64); + tcg_gen_extrl_i64_i32(cpu_CF, cf_64); gen_set_NZ64(result); tcg_gen_xor_i64(vf_64, result, t0); tcg_gen_xor_i64(tmp, t0, t1); tcg_gen_andc_i64(vf_64, vf_64, tmp); - tcg_gen_shri_i64(vf_64, vf_64, 32); - tcg_gen_trunc_i64_i32(cpu_VF, vf_64); + tcg_gen_extrh_i64_i32(cpu_VF, vf_64); tcg_gen_mov_i64(dest, result); @@ -691,8 +695,8 @@ static void gen_adc_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) t1_32 = tcg_temp_new_i32(); tmp = tcg_const_i32(0); - tcg_gen_trunc_i64_i32(t0_32, t0); - tcg_gen_trunc_i64_i32(t1_32, t1); + tcg_gen_extrl_i64_i32(t0_32, t0); + tcg_gen_extrl_i64_i32(t1_32, t1); tcg_gen_add2_i32(cpu_NF, cpu_CF, t0_32, tmp, cpu_CF, tmp); tcg_gen_add2_i32(cpu_NF, cpu_CF, cpu_NF, cpu_CF, t1_32, tmp); @@ -719,7 +723,7 @@ static void do_gpr_st_memidx(DisasContext *s, TCGv_i64 source, TCGv_i64 tcg_addr, int size, int memidx) { g_assert(size <= 3); - tcg_gen_qemu_st_i64(source, tcg_addr, memidx, MO_TE + size); + tcg_gen_qemu_st_i64(source, tcg_addr, memidx, s->be_data + size); } static void do_gpr_st(DisasContext *s, TCGv_i64 source, @@ -734,7 +738,7 @@ static void do_gpr_st(DisasContext *s, TCGv_i64 source, static void do_gpr_ld_memidx(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr, int size, bool is_signed, bool extend, int memidx) { - TCGMemOp memop = MO_TE + size; + TCGMemOp memop = s->be_data + size; g_assert(size <= 3); @@ -766,13 +770,18 @@ static void do_fp_st(DisasContext *s, int srcidx, TCGv_i64 tcg_addr, int size) TCGv_i64 tmp = tcg_temp_new_i64(); tcg_gen_ld_i64(tmp, cpu_env, fp_reg_offset(s, srcidx, MO_64)); if (size < 4) { - tcg_gen_qemu_st_i64(tmp, tcg_addr, get_mem_index(s), MO_TE + size); + tcg_gen_qemu_st_i64(tmp, tcg_addr, get_mem_index(s), + s->be_data + size); } else { + bool be = s->be_data == MO_BE; TCGv_i64 tcg_hiaddr = tcg_temp_new_i64(); - tcg_gen_qemu_st_i64(tmp, tcg_addr, get_mem_index(s), MO_TEQ); - tcg_gen_ld_i64(tmp, cpu_env, fp_reg_hi_offset(s, srcidx)); + tcg_gen_addi_i64(tcg_hiaddr, tcg_addr, 8); - tcg_gen_qemu_st_i64(tmp, tcg_hiaddr, get_mem_index(s), MO_TEQ); + tcg_gen_qemu_st_i64(tmp, be ? tcg_hiaddr : tcg_addr, get_mem_index(s), + s->be_data | MO_Q); + tcg_gen_ld_i64(tmp, cpu_env, fp_reg_hi_offset(s, srcidx)); + tcg_gen_qemu_st_i64(tmp, be ? tcg_addr : tcg_hiaddr, get_mem_index(s), + s->be_data | MO_Q); tcg_temp_free_i64(tcg_hiaddr); } @@ -789,17 +798,21 @@ static void do_fp_ld(DisasContext *s, int destidx, TCGv_i64 tcg_addr, int size) TCGv_i64 tmphi; if (size < 4) { - TCGMemOp memop = MO_TE + size; + TCGMemOp memop = s->be_data + size; tmphi = tcg_const_i64(0); tcg_gen_qemu_ld_i64(tmplo, tcg_addr, get_mem_index(s), memop); } else { + bool be = s->be_data == MO_BE; TCGv_i64 tcg_hiaddr; + tmphi = tcg_temp_new_i64(); tcg_hiaddr = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(tmplo, tcg_addr, get_mem_index(s), MO_TEQ); tcg_gen_addi_i64(tcg_hiaddr, tcg_addr, 8); - tcg_gen_qemu_ld_i64(tmphi, tcg_hiaddr, get_mem_index(s), MO_TEQ); + tcg_gen_qemu_ld_i64(tmplo, be ? tcg_hiaddr : tcg_addr, get_mem_index(s), + s->be_data | MO_Q); + tcg_gen_qemu_ld_i64(tmphi, be ? tcg_addr : tcg_hiaddr, get_mem_index(s), + s->be_data | MO_Q); tcg_temp_free_i64(tcg_hiaddr); } @@ -938,7 +951,7 @@ static void clear_vec_high(DisasContext *s, int rd) static void do_vec_st(DisasContext *s, int srcidx, int element, TCGv_i64 tcg_addr, int size) { - TCGMemOp memop = MO_TE + size; + TCGMemOp memop = s->be_data + size; TCGv_i64 tcg_tmp = tcg_temp_new_i64(); read_vec_element(s, tcg_tmp, srcidx, element, size); @@ -951,7 +964,7 @@ static void do_vec_st(DisasContext *s, int srcidx, int element, static void do_vec_ld(DisasContext *s, int destidx, int element, TCGv_i64 tcg_addr, int size) { - TCGMemOp memop = MO_TE + size; + TCGMemOp memop = s->be_data + size; TCGv_i64 tcg_tmp = tcg_temp_new_i64(); tcg_gen_qemu_ld_i64(tcg_tmp, tcg_addr, get_mem_index(s), memop); @@ -1234,9 +1247,15 @@ static void handle_sync(DisasContext *s, uint32_t insn, return; case 4: /* DSB */ case 5: /* DMB */ - case 6: /* ISB */ /* We don't emulate caches so barriers are no-ops */ return; + case 6: /* ISB */ + /* We need to break the TB after this insn to execute + * a self-modified code correctly and also to take + * any pending interrupts immediately. + */ + s->is_jmp = DISAS_UPDATE; + return; default: unallocated_encoding(s); return; @@ -1301,7 +1320,7 @@ static void gen_set_nzcv(TCGv_i64 tcg_rt) TCGv_i32 nzcv = tcg_temp_new_i32(); /* take NZCV from R[t] */ - tcg_gen_trunc_i64_i32(nzcv, tcg_rt); + tcg_gen_extrl_i64_i32(nzcv, tcg_rt); /* bit 31, N */ tcg_gen_andi_i32(cpu_NF, nzcv, (1U << 31)); @@ -1357,16 +1376,18 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, * runtime; this may result in an exception. */ TCGv_ptr tmpptr; - TCGv_i32 tcg_syn; + TCGv_i32 tcg_syn, tcg_isread; uint32_t syndrome; gen_a64_set_pc_im(s->pc - 4); tmpptr = tcg_const_ptr(ri); syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread); tcg_syn = tcg_const_i32(syndrome); - gen_helper_access_check_cp_reg(cpu_env, tmpptr, tcg_syn); + tcg_isread = tcg_const_i32(isread); + gen_helper_access_check_cp_reg(cpu_env, tmpptr, tcg_syn, tcg_isread); tcg_temp_free_ptr(tmpptr); tcg_temp_free_i32(tcg_syn); + tcg_temp_free_i32(tcg_isread); } /* Handle special cases first */ @@ -1553,8 +1574,27 @@ static void disas_exc(DisasContext *s, uint32_t insn) unallocated_encoding(s); break; } - /* HLT */ - unsupported_encoding(s, insn); + /* HLT. This has two purposes. + * Architecturally, it is an external halting debug instruction. + * Since QEMU doesn't implement external debug, we treat this as + * it is required for halting debug disabled: it will UNDEF. + * Secondly, "HLT 0xf000" is the A64 semihosting syscall instruction. + */ + if (semihosting_enabled() && imm16 == 0xf000) { +#ifndef CONFIG_USER_ONLY + /* In system mode, don't allow userspace access to semihosting, + * to provide some semblance of security (and for consistency + * with our 32-bit semihosting). + */ + if (s->current_el == 0) { + unsupported_encoding(s, insn); + break; + } +#endif + gen_exception_internal_insn(s, 0, EXCP_SEMIHOST); + } else { + unsupported_encoding(s, insn); + } break; case 5: if (op2_ll < 1 || op2_ll > 3) { @@ -1671,7 +1711,7 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, TCGv_i64 addr, int size, bool is_pair) { TCGv_i64 tmp = tcg_temp_new_i64(); - TCGMemOp memop = MO_TE + size; + TCGMemOp memop = s->be_data + size; g_assert(size <= 3); tcg_gen_qemu_ld_i64(tmp, addr, get_mem_index(s), memop); @@ -1733,7 +1773,7 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, tcg_gen_brcond_i64(TCG_COND_NE, addr, cpu_exclusive_addr, fail_label); tmp = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(tmp, addr, get_mem_index(s), MO_TE + size); + tcg_gen_qemu_ld_i64(tmp, addr, get_mem_index(s), s->be_data + size); tcg_gen_brcond_i64(TCG_COND_NE, tmp, cpu_exclusive_val, fail_label); tcg_temp_free_i64(tmp); @@ -1742,7 +1782,8 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, TCGv_i64 tmphi = tcg_temp_new_i64(); tcg_gen_addi_i64(addrhi, addr, 1 << size); - tcg_gen_qemu_ld_i64(tmphi, addrhi, get_mem_index(s), MO_TE + size); + tcg_gen_qemu_ld_i64(tmphi, addrhi, get_mem_index(s), + s->be_data + size); tcg_gen_brcond_i64(TCG_COND_NE, tmphi, cpu_exclusive_high, fail_label); tcg_temp_free_i64(tmphi); @@ -1750,13 +1791,14 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, } /* We seem to still have the exclusive monitor, so do the store */ - tcg_gen_qemu_st_i64(cpu_reg(s, rt), addr, get_mem_index(s), MO_TE + size); + tcg_gen_qemu_st_i64(cpu_reg(s, rt), addr, get_mem_index(s), + s->be_data + size); if (is_pair) { TCGv_i64 addrhi = tcg_temp_new_i64(); tcg_gen_addi_i64(addrhi, addr, 1 << size); tcg_gen_qemu_st_i64(cpu_reg(s, rt2), addrhi, - get_mem_index(s), MO_TE + size); + get_mem_index(s), s->be_data + size); tcg_temp_free_i64(addrhi); } @@ -1784,9 +1826,6 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, * o2: 0 -> exclusive, 1 -> not * o1: 0 -> single register, 1 -> register pair * o0: 1 -> load-acquire/store-release, 0 -> not - * - * o0 == 0 AND o2 == 1 is un-allocated - * o1 == 1 is un-allocated except for 32 and 64 bit sizes */ static void disas_ldst_excl(DisasContext *s, uint32_t insn) { @@ -1801,7 +1840,8 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) int size = extract32(insn, 30, 2); TCGv_i64 tcg_addr; - if ((!is_excl && !is_lasr) || + if ((!is_excl && !is_pair && !is_lasr) || + (!is_excl && is_pair) || (is_pair && size < 2)) { unallocated_encoding(s); return; @@ -1830,15 +1870,6 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) } else { do_gpr_ld(s, tcg_rt, tcg_addr, size, false, false); } - if (is_pair) { - TCGv_i64 tcg_rt2 = cpu_reg(s, rt); - tcg_gen_addi_i64(tcg_addr, tcg_addr, 1 << size); - if (is_store) { - do_gpr_st(s, tcg_rt2, tcg_addr, size); - } else { - do_gpr_ld(s, tcg_rt2, tcg_addr, size, false, false); - } - } } } @@ -2582,7 +2613,7 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn) TCGv_i64 tcg_tmp = tcg_temp_new_i64(); tcg_gen_qemu_ld_i64(tcg_tmp, tcg_addr, - get_mem_index(s), MO_TE + scale); + get_mem_index(s), s->be_data + scale); switch (scale) { case 0: mulconst = 0x0101010101010101ULL; @@ -2612,9 +2643,9 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn) } else { /* Load/store one element per register */ if (is_load) { - do_vec_ld(s, rt, index, tcg_addr, MO_TE + scale); + do_vec_ld(s, rt, index, tcg_addr, s->be_data + scale); } else { - do_vec_st(s, rt, index, tcg_addr, MO_TE + scale); + do_vec_st(s, rt, index, tcg_addr, s->be_data + scale); } } tcg_gen_addi_i64(tcg_addr, tcg_addr, ebytes); @@ -2992,9 +3023,51 @@ static void disas_bitfield(DisasContext *s, uint32_t insn) } tcg_rd = cpu_reg(s, rd); - tcg_tmp = read_cpu_reg(s, rn, sf); - /* OPTME: probably worth recognizing common cases of ext{8,16,32}{u,s} */ + /* Suppress the zero-extend for !sf. Since RI and SI are constrained + to be smaller than bitsize, we'll never reference data outside the + low 32-bits anyway. */ + tcg_tmp = read_cpu_reg(s, rn, 1); + + /* Recognize the common aliases. */ + if (opc == 0) { /* SBFM */ + if (ri == 0) { + if (si == 7) { /* SXTB */ + tcg_gen_ext8s_i64(tcg_rd, tcg_tmp); + goto done; + } else if (si == 15) { /* SXTH */ + tcg_gen_ext16s_i64(tcg_rd, tcg_tmp); + goto done; + } else if (si == 31) { /* SXTW */ + tcg_gen_ext32s_i64(tcg_rd, tcg_tmp); + goto done; + } + } + if (si == 63 || (si == 31 && ri <= si)) { /* ASR */ + if (si == 31) { + tcg_gen_ext32s_i64(tcg_tmp, tcg_tmp); + } + tcg_gen_sari_i64(tcg_rd, tcg_tmp, ri); + goto done; + } + } else if (opc == 2) { /* UBFM */ + if (ri == 0) { /* UXTB, UXTH, plus non-canonical AND */ + tcg_gen_andi_i64(tcg_rd, tcg_tmp, bitmask64(si + 1)); + return; + } + if (si == 63 || (si == 31 && ri <= si)) { /* LSR */ + if (si == 31) { + tcg_gen_ext32u_i64(tcg_tmp, tcg_tmp); + } + tcg_gen_shri_i64(tcg_rd, tcg_tmp, ri); + return; + } + if (si + 1 == ri && si != bitsize - 1) { /* LSL */ + int shift = bitsize - 1 - si; + tcg_gen_shli_i64(tcg_rd, tcg_tmp, shift); + goto done; + } + } if (opc != 1) { /* SBFM or UBFM */ tcg_gen_movi_i64(tcg_rd, 0); @@ -3019,6 +3092,7 @@ static void disas_bitfield(DisasContext *s, uint32_t insn) tcg_gen_sari_i64(tcg_rd, tcg_rd, 64 - (pos + len)); } + done: if (!sf) { /* zero extend final result */ tcg_gen_ext32u_i64(tcg_rd, tcg_rd); } @@ -3051,17 +3125,7 @@ static void disas_extract(DisasContext *s, uint32_t insn) tcg_rd = cpu_reg(s, rd); - if (imm) { - /* OPTME: we can special case rm==rn as a rotate */ - tcg_rm = read_cpu_reg(s, rm, sf); - tcg_rn = read_cpu_reg(s, rn, sf); - tcg_gen_shri_i64(tcg_rm, tcg_rm, imm); - tcg_gen_shli_i64(tcg_rn, tcg_rn, bitsize - imm); - tcg_gen_or_i64(tcg_rd, tcg_rm, tcg_rn); - if (!sf) { - tcg_gen_ext32u_i64(tcg_rd, tcg_rd); - } - } else { + if (unlikely(imm == 0)) { /* tcg shl_i32/shl_i64 is undefined for 32/64 bit shifts, * so an extract from bit 0 is a special case. */ @@ -3070,8 +3134,27 @@ static void disas_extract(DisasContext *s, uint32_t insn) } else { tcg_gen_ext32u_i64(tcg_rd, cpu_reg(s, rm)); } + } else if (rm == rn) { /* ROR */ + tcg_rm = cpu_reg(s, rm); + if (sf) { + tcg_gen_rotri_i64(tcg_rd, tcg_rm, imm); + } else { + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(tmp, tcg_rm); + tcg_gen_rotri_i32(tmp, tmp, imm); + tcg_gen_extu_i32_i64(tcg_rd, tmp); + tcg_temp_free_i32(tmp); + } + } else { + tcg_rm = read_cpu_reg(s, rm, sf); + tcg_rn = read_cpu_reg(s, rn, sf); + tcg_gen_shri_i64(tcg_rm, tcg_rm, imm); + tcg_gen_shli_i64(tcg_rn, tcg_rn, bitsize - imm); + tcg_gen_or_i64(tcg_rd, tcg_rm, tcg_rn); + if (!sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } } - } } @@ -3131,8 +3214,8 @@ static void shift_reg(TCGv_i64 dst, TCGv_i64 src, int sf, TCGv_i32 t0, t1; t0 = tcg_temp_new_i32(); t1 = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(t0, src); - tcg_gen_trunc_i64_i32(t1, shift_amount); + tcg_gen_extrl_i64_i32(t0, src); + tcg_gen_extrl_i64_i32(t1, shift_amount); tcg_gen_rotr_i32(t0, t0, t1); tcg_gen_extu_i32_i64(dst, t0); tcg_temp_free_i32(t0); @@ -3547,8 +3630,9 @@ static void disas_adc_sbc(DisasContext *s, uint32_t insn) static void disas_cc(DisasContext *s, uint32_t insn) { unsigned int sf, op, y, cond, rn, nzcv, is_imm; - TCGLabel *label_continue = NULL; + TCGv_i32 tcg_t0, tcg_t1, tcg_t2; TCGv_i64 tcg_tmp, tcg_y, tcg_rn; + DisasCompare c; if (!extract32(insn, 29, 1)) { unallocated_encoding(s); @@ -3566,19 +3650,13 @@ static void disas_cc(DisasContext *s, uint32_t insn) rn = extract32(insn, 5, 5); nzcv = extract32(insn, 0, 4); - if (cond < 0x0e) { /* not always */ - TCGLabel *label_match = gen_new_label(); - label_continue = gen_new_label(); - arm_gen_test_cc(cond, label_match); - /* nomatch: */ - tcg_tmp = tcg_temp_new_i64(); - tcg_gen_movi_i64(tcg_tmp, nzcv << 28); - gen_set_nzcv(tcg_tmp); - tcg_temp_free_i64(tcg_tmp); - tcg_gen_br(label_continue); - gen_set_label(label_match); - } - /* match, or condition is always */ + /* Set T0 = !COND. */ + tcg_t0 = tcg_temp_new_i32(); + arm_test_cc(&c, cond); + tcg_gen_setcondi_i32(tcg_invert_cond(c.cond), tcg_t0, c.value, 0); + arm_free_cc(&c); + + /* Load the arguments for the new comparison. */ if (is_imm) { tcg_y = new_tmp_a64(s); tcg_gen_movi_i64(tcg_y, y); @@ -3587,6 +3665,7 @@ static void disas_cc(DisasContext *s, uint32_t insn) } tcg_rn = cpu_reg(s, rn); + /* Set the flags for the new comparison. */ tcg_tmp = tcg_temp_new_i64(); if (op) { gen_sub_CC(sf, tcg_tmp, tcg_rn, tcg_y); @@ -3595,9 +3674,55 @@ static void disas_cc(DisasContext *s, uint32_t insn) } tcg_temp_free_i64(tcg_tmp); - if (cond < 0x0e) { /* continue */ - gen_set_label(label_continue); + /* If COND was false, force the flags to #nzcv. Compute two masks + * to help with this: T1 = (COND ? 0 : -1), T2 = (COND ? -1 : 0). + * For tcg hosts that support ANDC, we can make do with just T1. + * In either case, allow the tcg optimizer to delete any unused mask. + */ + tcg_t1 = tcg_temp_new_i32(); + tcg_t2 = tcg_temp_new_i32(); + tcg_gen_neg_i32(tcg_t1, tcg_t0); + tcg_gen_subi_i32(tcg_t2, tcg_t0, 1); + + if (nzcv & 8) { /* N */ + tcg_gen_or_i32(cpu_NF, cpu_NF, tcg_t1); + } else { + if (TCG_TARGET_HAS_andc_i32) { + tcg_gen_andc_i32(cpu_NF, cpu_NF, tcg_t1); + } else { + tcg_gen_and_i32(cpu_NF, cpu_NF, tcg_t2); + } } + if (nzcv & 4) { /* Z */ + if (TCG_TARGET_HAS_andc_i32) { + tcg_gen_andc_i32(cpu_ZF, cpu_ZF, tcg_t1); + } else { + tcg_gen_and_i32(cpu_ZF, cpu_ZF, tcg_t2); + } + } else { + tcg_gen_or_i32(cpu_ZF, cpu_ZF, tcg_t0); + } + if (nzcv & 2) { /* C */ + tcg_gen_or_i32(cpu_CF, cpu_CF, tcg_t0); + } else { + if (TCG_TARGET_HAS_andc_i32) { + tcg_gen_andc_i32(cpu_CF, cpu_CF, tcg_t1); + } else { + tcg_gen_and_i32(cpu_CF, cpu_CF, tcg_t2); + } + } + if (nzcv & 1) { /* V */ + tcg_gen_or_i32(cpu_VF, cpu_VF, tcg_t1); + } else { + if (TCG_TARGET_HAS_andc_i32) { + tcg_gen_andc_i32(cpu_VF, cpu_VF, tcg_t1); + } else { + tcg_gen_and_i32(cpu_VF, cpu_VF, tcg_t2); + } + } + tcg_temp_free_i32(tcg_t0); + tcg_temp_free_i32(tcg_t1); + tcg_temp_free_i32(tcg_t2); } /* C3.5.6 Conditional select @@ -3609,7 +3734,8 @@ static void disas_cc(DisasContext *s, uint32_t insn) static void disas_cond_select(DisasContext *s, uint32_t insn) { unsigned int sf, else_inv, rm, cond, else_inc, rn, rd; - TCGv_i64 tcg_rd, tcg_src; + TCGv_i64 tcg_rd, zero; + DisasCompare64 c; if (extract32(insn, 29, 1) || extract32(insn, 11, 1)) { /* S == 1 or op2<1> == 1 */ @@ -3624,48 +3750,35 @@ static void disas_cond_select(DisasContext *s, uint32_t insn) rn = extract32(insn, 5, 5); rd = extract32(insn, 0, 5); - if (rd == 31) { - /* silly no-op write; until we use movcond we must special-case - * this to avoid a dead temporary across basic blocks. - */ - return; - } - tcg_rd = cpu_reg(s, rd); - if (cond >= 0x0e) { /* condition "always" */ - tcg_src = read_cpu_reg(s, rn, sf); - tcg_gen_mov_i64(tcg_rd, tcg_src); - } else { - /* OPTME: we could use movcond here, at the cost of duplicating - * a lot of the arm_gen_test_cc() logic. - */ - TCGLabel *label_match = gen_new_label(); - TCGLabel *label_continue = gen_new_label(); - - arm_gen_test_cc(cond, label_match); - /* nomatch: */ - tcg_src = cpu_reg(s, rm); + a64_test_cc(&c, cond); + zero = tcg_const_i64(0); + if (rn == 31 && rm == 31 && (else_inc ^ else_inv)) { + /* CSET & CSETM. */ + tcg_gen_setcond_i64(tcg_invert_cond(c.cond), tcg_rd, c.value, zero); + if (else_inv) { + tcg_gen_neg_i64(tcg_rd, tcg_rd); + } + } else { + TCGv_i64 t_true = cpu_reg(s, rn); + TCGv_i64 t_false = read_cpu_reg(s, rm, 1); if (else_inv && else_inc) { - tcg_gen_neg_i64(tcg_rd, tcg_src); + tcg_gen_neg_i64(t_false, t_false); } else if (else_inv) { - tcg_gen_not_i64(tcg_rd, tcg_src); + tcg_gen_not_i64(t_false, t_false); } else if (else_inc) { - tcg_gen_addi_i64(tcg_rd, tcg_src, 1); - } else { - tcg_gen_mov_i64(tcg_rd, tcg_src); - } - if (!sf) { - tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + tcg_gen_addi_i64(t_false, t_false, 1); } - tcg_gen_br(label_continue); - /* match: */ - gen_set_label(label_match); - tcg_src = read_cpu_reg(s, rn, sf); - tcg_gen_mov_i64(tcg_rd, tcg_src); - /* continue: */ - gen_set_label(label_continue); + tcg_gen_movcond_i64(c.cond, tcg_rd, c.value, zero, t_true, t_false); + } + + tcg_temp_free_i64(zero); + a64_free_cc(&c); + + if (!sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); } } @@ -3680,7 +3793,7 @@ static void handle_clz(DisasContext *s, unsigned int sf, gen_helper_clz64(tcg_rd, tcg_rn); } else { TCGv_i32 tcg_tmp32 = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(tcg_tmp32, tcg_rn); + tcg_gen_extrl_i64_i32(tcg_tmp32, tcg_rn); gen_helper_clz(tcg_tmp32, tcg_tmp32); tcg_gen_extu_i32_i64(tcg_rd, tcg_tmp32); tcg_temp_free_i32(tcg_tmp32); @@ -3698,7 +3811,7 @@ static void handle_cls(DisasContext *s, unsigned int sf, gen_helper_cls64(tcg_rd, tcg_rn); } else { TCGv_i32 tcg_tmp32 = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(tcg_tmp32, tcg_rn); + tcg_gen_extrl_i64_i32(tcg_tmp32, tcg_rn); gen_helper_cls32(tcg_tmp32, tcg_tmp32); tcg_gen_extu_i32_i64(tcg_rd, tcg_tmp32); tcg_temp_free_i32(tcg_tmp32); @@ -3716,7 +3829,7 @@ static void handle_rbit(DisasContext *s, unsigned int sf, gen_helper_rbit64(tcg_rd, tcg_rn); } else { TCGv_i32 tcg_tmp32 = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(tcg_tmp32, tcg_rn); + tcg_gen_extrl_i64_i32(tcg_tmp32, tcg_rn); gen_helper_rbit(tcg_tmp32, tcg_tmp32); tcg_gen_extu_i32_i64(tcg_rd, tcg_tmp32); tcg_temp_free_i32(tcg_tmp32); @@ -4152,20 +4265,6 @@ static void disas_fp_ccomp(DisasContext *s, uint32_t insn) } } -/* copy src FP register to dst FP register; type specifies single or double */ -static void gen_mov_fp2fp(DisasContext *s, int type, int dst, int src) -{ - if (type) { - TCGv_i64 v = read_fp_dreg(s, src); - write_fp_dreg(s, dst, v); - tcg_temp_free_i64(v); - } else { - TCGv_i32 v = read_fp_sreg(s, src); - write_fp_sreg(s, dst, v); - tcg_temp_free_i32(v); - } -} - /* C3.6.24 Floating point conditional select * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0 * +---+---+---+-----------+------+---+------+------+-----+------+------+ @@ -4175,7 +4274,8 @@ static void gen_mov_fp2fp(DisasContext *s, int type, int dst, int src) static void disas_fp_csel(DisasContext *s, uint32_t insn) { unsigned int mos, type, rm, cond, rn, rd; - TCGLabel *label_continue = NULL; + TCGv_i64 t_true, t_false, t_zero; + DisasCompare64 c; mos = extract32(insn, 29, 3); type = extract32(insn, 22, 2); /* 0 = single, 1 = double */ @@ -4193,21 +4293,23 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn) return; } - if (cond < 0x0e) { /* not always */ - TCGLabel *label_match = gen_new_label(); - label_continue = gen_new_label(); - arm_gen_test_cc(cond, label_match); - /* nomatch: */ - gen_mov_fp2fp(s, type, rd, rm); - tcg_gen_br(label_continue); - gen_set_label(label_match); - } + /* Zero extend sreg inputs to 64 bits now. */ + t_true = tcg_temp_new_i64(); + t_false = tcg_temp_new_i64(); + read_vec_element(s, t_true, rn, 0, type ? MO_64 : MO_32); + read_vec_element(s, t_false, rm, 0, type ? MO_64 : MO_32); - gen_mov_fp2fp(s, type, rd, rn); + a64_test_cc(&c, cond); + t_zero = tcg_const_i64(0); + tcg_gen_movcond_i64(c.cond, t_true, c.value, t_zero, t_true, t_false); + tcg_temp_free_i64(t_zero); + tcg_temp_free_i64(t_false); + a64_free_cc(&c); - if (cond < 0x0e) { /* continue */ - gen_set_label(label_continue); - } + /* Note that sregs write back zeros to the high bits, + and we've already done the zero-extension. */ + write_fp_dreg(s, rd, t_true); + tcg_temp_free_i64(t_true); } /* C3.6.25 Floating-point data-processing (1 source) - single precision */ @@ -5475,16 +5577,16 @@ static void disas_simd_across_lanes(DisasContext *s, uint32_t insn) assert(elements == 4); read_vec_element(s, tcg_elt, rn, 0, MO_32); - tcg_gen_trunc_i64_i32(tcg_elt1, tcg_elt); + tcg_gen_extrl_i64_i32(tcg_elt1, tcg_elt); read_vec_element(s, tcg_elt, rn, 1, MO_32); - tcg_gen_trunc_i64_i32(tcg_elt2, tcg_elt); + tcg_gen_extrl_i64_i32(tcg_elt2, tcg_elt); do_minmaxop(s, tcg_elt1, tcg_elt2, opcode, is_min, fpst); read_vec_element(s, tcg_elt, rn, 2, MO_32); - tcg_gen_trunc_i64_i32(tcg_elt2, tcg_elt); + tcg_gen_extrl_i64_i32(tcg_elt2, tcg_elt); read_vec_element(s, tcg_elt, rn, 3, MO_32); - tcg_gen_trunc_i64_i32(tcg_elt3, tcg_elt); + tcg_gen_extrl_i64_i32(tcg_elt3, tcg_elt); do_minmaxop(s, tcg_elt2, tcg_elt3, opcode, is_min, fpst); @@ -7647,7 +7749,7 @@ static void handle_2misc_narrow(DisasContext *s, bool scalar, static NeonGenNarrowFn * const xtnfns[3] = { gen_helper_neon_narrow_u8, gen_helper_neon_narrow_u16, - tcg_gen_trunc_i64_i32, + tcg_gen_extrl_i64_i32, }; static NeonGenNarrowEnvFn * const sqxtunfns[3] = { gen_helper_neon_unarrow_sat8, @@ -7681,10 +7783,8 @@ static void handle_2misc_narrow(DisasContext *s, bool scalar, } else { TCGv_i32 tcg_lo = tcg_temp_new_i32(); TCGv_i32 tcg_hi = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(tcg_lo, tcg_op); + tcg_gen_extr_i64_i32(tcg_lo, tcg_hi, tcg_op); gen_helper_vfp_fcvt_f32_to_f16(tcg_lo, tcg_lo, cpu_env); - tcg_gen_shri_i64(tcg_op, tcg_op, 32); - tcg_gen_trunc_i64_i32(tcg_hi, tcg_op); gen_helper_vfp_fcvt_f32_to_f16(tcg_hi, tcg_hi, cpu_env); tcg_gen_deposit_i32(tcg_res[pass], tcg_lo, tcg_hi, 16, 16); tcg_temp_free_i32(tcg_lo); @@ -8590,16 +8690,10 @@ static void handle_3rd_wide(DisasContext *s, int is_q, int is_u, int size, } } -static void do_narrow_high_u32(TCGv_i32 res, TCGv_i64 in) -{ - tcg_gen_shri_i64(in, in, 32); - tcg_gen_trunc_i64_i32(res, in); -} - static void do_narrow_round_high_u32(TCGv_i32 res, TCGv_i64 in) { tcg_gen_addi_i64(in, in, 1U << 31); - do_narrow_high_u32(res, in); + tcg_gen_extrh_i64_i32(res, in); } static void handle_3rd_narrowing(DisasContext *s, int is_q, int is_u, int size, @@ -8618,7 +8712,7 @@ static void handle_3rd_narrowing(DisasContext *s, int is_q, int is_u, int size, gen_helper_neon_narrow_round_high_u8 }, { gen_helper_neon_narrow_high_u16, gen_helper_neon_narrow_round_high_u16 }, - { do_narrow_high_u32, do_narrow_round_high_u32 }, + { tcg_gen_extrh_i64_i32, do_narrow_round_high_u32 }, }; NeonGenNarrowFn *gennarrow = narrowfns[size][is_u]; @@ -10883,7 +10977,7 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s) { uint32_t insn; - insn = arm_ldl_code(env, s->pc, s->bswap_code); + insn = arm_ldl_code(env, s->pc, s->sctlr_b); s->insn = insn; s->pc += 4; @@ -10922,15 +11016,11 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s) free_tmp_a64(s); } -void gen_intermediate_code_internal_a64(ARMCPU *cpu, - TranslationBlock *tb, - bool search_pc) +void gen_intermediate_code_a64(ARMCPU *cpu, TranslationBlock *tb) { CPUState *cs = CPU(cpu); CPUARMState *env = &cpu->env; DisasContext dc1, *dc = &dc1; - CPUBreakpoint *bp; - int j, lj; target_ulong pc_start; target_ulong next_page_start; int num_insns; @@ -10946,9 +11036,14 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, dc->condjmp = 0; dc->aarch64 = 1; - dc->el3_is_aa64 = arm_el_is_aa64(env, 3); + /* If we are coming from secure EL0 in a system with a 32-bit EL3, then + * there is no secure EL1, so we route exceptions to EL3. + */ + dc->secure_routed_to_el3 = arm_feature(env, ARM_FEATURE_EL3) && + !arm_el_is_aa64(env, 3); dc->thumb = 0; - dc->bswap_code = 0; + dc->sctlr_b = 0; + dc->be_data = ARM_TBFLAG_BE_DATA(tb->flags) ? MO_BE : MO_LE; dc->condexec_mask = 0; dc->condexec_cond = 0; dc->mmu_idx = ARM_TBFLAG_MMUIDX(tb->flags); @@ -10985,51 +11080,51 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, init_tmp_a64_array(dc); next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; - lj = -1; num_insns = 0; max_insns = tb->cflags & CF_COUNT_MASK; if (max_insns == 0) { max_insns = CF_COUNT_MASK; } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } gen_tb_start(tb); tcg_clear_temp_count(); do { + tcg_gen_insn_start(dc->pc, 0); + num_insns++; + if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { + CPUBreakpoint *bp; QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { if (bp->pc == dc->pc) { - gen_exception_internal_insn(dc, 0, EXCP_DEBUG); - /* Advance PC so that clearing the breakpoint will - invalidate this TB. */ - dc->pc += 2; - goto done_generating; - } - } - } - - if (search_pc) { - j = tcg_op_buf_count(); - if (lj < j) { - lj++; - while (lj < j) { - tcg_ctx.gen_opc_instr_start[lj++] = 0; + if (bp->flags & BP_CPU) { + gen_a64_set_pc_im(dc->pc); + gen_helper_check_breakpoints(cpu_env); + /* End the TB early; it likely won't be executed */ + dc->is_jmp = DISAS_UPDATE; + } else { + gen_exception_internal_insn(dc, 0, EXCP_DEBUG); + /* The address covered by the breakpoint must be + included in [tb->pc, tb->pc + tb->size) in order + to for it to be properly cleared -- thus we + increment the PC here so that the logic setting + tb->size below does the right thing. */ + dc->pc += 4; + goto done_generating; + } + break; } } - tcg_ctx.gen_opc_pc[lj] = dc->pc; - tcg_ctx.gen_opc_instr_start[lj] = 1; - tcg_ctx.gen_opc_icount[lj] = num_insns; } - if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) { + if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { gen_io_start(); } - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { - tcg_gen_debug_insn_start(dc->pc); - } - if (dc->ss_active && !dc->pstate_ss) { /* Singlestep state is Active-pending. * If we're in this state at the start of a TB then either @@ -11041,7 +11136,7 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, * "did not step an insn" case, and so the syndrome ISV and EX * bits should be zero. */ - assert(num_insns == 0); + assert(num_insns == 1); gen_exception(EXCP_UDEF, syn_swstep(dc->ss_same_el, 0, 0), default_exception_el(dc)); dc->is_jmp = DISAS_EXC; @@ -11060,7 +11155,6 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, * Also stop translation when a page boundary is reached. This * ensures prefetch aborts occur at the right place. */ - num_insns++; } while (!dc->is_jmp && !tcg_op_buf_full() && !cs->singlestep_enabled && !singlestep && @@ -11131,22 +11225,15 @@ done_generating: gen_tb_end(tb, num_insns); #ifdef DEBUG_DISAS - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) && + qemu_log_in_addr_range(pc_start)) { qemu_log("----------------\n"); qemu_log("IN: %s\n", lookup_symbol(pc_start)); log_target_disas(cs, pc_start, dc->pc - pc_start, - 4 | (dc->bswap_code << 1)); + 4 | (bswap_code(dc->sctlr_b) ? 2 : 0)); qemu_log("\n"); } #endif - if (search_pc) { - j = tcg_op_buf_count(); - lj++; - while (lj <= j) { - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - } else { - tb->size = dc->pc - pc_start; - tb->icount = num_insns; - } + tb->size = dc->pc - pc_start; + tb->icount = num_insns; } diff --git a/qemu/target-arm/translate.c b/qemu/target-arm/translate.c index 69ac18c10..940ec8d98 100644 --- a/qemu/target-arm/translate.c +++ b/qemu/target-arm/translate.c @@ -18,11 +18,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include "cpu.h" #include "internals.h" @@ -36,6 +32,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" #define ENABLE_ARCH_4T arm_dc_feature(s, ARM_FEATURE_V4T) @@ -52,7 +49,6 @@ #define ARCH(x) do { if (!ENABLE_ARCH_##x) goto illegal_op; } while(0) #include "translate.h" -static uint32_t gen_opc_condexec_bits[OPC_BUF_SIZE]; #if defined(CONFIG_USER_ONLY) #define IS_USER(s) 1 @@ -60,16 +56,16 @@ static uint32_t gen_opc_condexec_bits[OPC_BUF_SIZE]; #define IS_USER(s) (s->user) #endif -TCGv_ptr cpu_env; +TCGv_env cpu_env; /* We reuse the same 64-bit temporaries for efficiency. */ static TCGv_i64 cpu_V0, cpu_V1, cpu_M0; static TCGv_i32 cpu_R[16]; -static TCGv_i32 cpu_CF, cpu_NF, cpu_VF, cpu_ZF; -static TCGv_i64 cpu_exclusive_addr; -static TCGv_i64 cpu_exclusive_val; +TCGv_i32 cpu_CF, cpu_NF, cpu_VF, cpu_ZF; +TCGv_i64 cpu_exclusive_addr; +TCGv_i64 cpu_exclusive_val; #ifdef CONFIG_USER_ONLY -static TCGv_i64 cpu_exclusive_test; -static TCGv_i32 cpu_exclusive_info; +TCGv_i64 cpu_exclusive_test; +TCGv_i32 cpu_exclusive_info; #endif /* FIXME: These should be removed. */ @@ -90,23 +86,23 @@ void arm_translate_init(void) cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); for (i = 0; i < 16; i++) { - cpu_R[i] = tcg_global_mem_new_i32(TCG_AREG0, + cpu_R[i] = tcg_global_mem_new_i32(cpu_env, offsetof(CPUARMState, regs[i]), regnames[i]); } - cpu_CF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, CF), "CF"); - cpu_NF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, NF), "NF"); - cpu_VF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, VF), "VF"); - cpu_ZF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, ZF), "ZF"); + cpu_CF = tcg_global_mem_new_i32(cpu_env, offsetof(CPUARMState, CF), "CF"); + cpu_NF = tcg_global_mem_new_i32(cpu_env, offsetof(CPUARMState, NF), "NF"); + cpu_VF = tcg_global_mem_new_i32(cpu_env, offsetof(CPUARMState, VF), "VF"); + cpu_ZF = tcg_global_mem_new_i32(cpu_env, offsetof(CPUARMState, ZF), "ZF"); - cpu_exclusive_addr = tcg_global_mem_new_i64(TCG_AREG0, + cpu_exclusive_addr = tcg_global_mem_new_i64(cpu_env, offsetof(CPUARMState, exclusive_addr), "exclusive_addr"); - cpu_exclusive_val = tcg_global_mem_new_i64(TCG_AREG0, + cpu_exclusive_val = tcg_global_mem_new_i64(cpu_env, offsetof(CPUARMState, exclusive_val), "exclusive_val"); #ifdef CONFIG_USER_ONLY - cpu_exclusive_test = tcg_global_mem_new_i64(TCG_AREG0, + cpu_exclusive_test = tcg_global_mem_new_i64(cpu_env, offsetof(CPUARMState, exclusive_test), "exclusive_test"); - cpu_exclusive_info = tcg_global_mem_new_i32(TCG_AREG0, + cpu_exclusive_info = tcg_global_mem_new_i32(cpu_env, offsetof(CPUARMState, exclusive_info), "exclusive_info"); #endif @@ -738,81 +734,113 @@ static void gen_thumb2_parallel_addsub(int op1, int op2, TCGv_i32 a, TCGv_i32 b) #undef PAS_OP /* - * generate a conditional branch based on ARM condition code cc. + * Generate a conditional based on ARM condition code cc. * This is common between ARM and Aarch64 targets. */ -void arm_gen_test_cc(int cc, TCGLabel *label) +void arm_test_cc(DisasCompare *cmp, int cc) { - TCGv_i32 tmp; - TCGLabel *inv; + TCGv_i32 value; + TCGCond cond; + bool global = true; switch (cc) { case 0: /* eq: Z */ - tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ZF, 0, label); - break; case 1: /* ne: !Z */ - tcg_gen_brcondi_i32(TCG_COND_NE, cpu_ZF, 0, label); + cond = TCG_COND_EQ; + value = cpu_ZF; break; + case 2: /* cs: C */ - tcg_gen_brcondi_i32(TCG_COND_NE, cpu_CF, 0, label); - break; case 3: /* cc: !C */ - tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_CF, 0, label); + cond = TCG_COND_NE; + value = cpu_CF; break; + case 4: /* mi: N */ - tcg_gen_brcondi_i32(TCG_COND_LT, cpu_NF, 0, label); - break; case 5: /* pl: !N */ - tcg_gen_brcondi_i32(TCG_COND_GE, cpu_NF, 0, label); + cond = TCG_COND_LT; + value = cpu_NF; break; + case 6: /* vs: V */ - tcg_gen_brcondi_i32(TCG_COND_LT, cpu_VF, 0, label); - break; case 7: /* vc: !V */ - tcg_gen_brcondi_i32(TCG_COND_GE, cpu_VF, 0, label); + cond = TCG_COND_LT; + value = cpu_VF; break; + case 8: /* hi: C && !Z */ - inv = gen_new_label(); - tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_CF, 0, inv); - tcg_gen_brcondi_i32(TCG_COND_NE, cpu_ZF, 0, label); - gen_set_label(inv); - break; - case 9: /* ls: !C || Z */ - tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_CF, 0, label); - tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ZF, 0, label); + case 9: /* ls: !C || Z -> !(C && !Z) */ + cond = TCG_COND_NE; + value = tcg_temp_new_i32(); + global = false; + /* CF is 1 for C, so -CF is an all-bits-set mask for C; + ZF is non-zero for !Z; so AND the two subexpressions. */ + tcg_gen_neg_i32(value, cpu_CF); + tcg_gen_and_i32(value, value, cpu_ZF); break; + case 10: /* ge: N == V -> N ^ V == 0 */ - tmp = tcg_temp_new_i32(); - tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); - tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label); - tcg_temp_free_i32(tmp); - break; case 11: /* lt: N != V -> N ^ V != 0 */ - tmp = tcg_temp_new_i32(); - tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); - tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label); - tcg_temp_free_i32(tmp); + /* Since we're only interested in the sign bit, == 0 is >= 0. */ + cond = TCG_COND_GE; + value = tcg_temp_new_i32(); + global = false; + tcg_gen_xor_i32(value, cpu_VF, cpu_NF); break; + case 12: /* gt: !Z && N == V */ - inv = gen_new_label(); - tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ZF, 0, inv); - tmp = tcg_temp_new_i32(); - tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); - tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label); - tcg_temp_free_i32(tmp); - gen_set_label(inv); - break; case 13: /* le: Z || N != V */ - tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ZF, 0, label); - tmp = tcg_temp_new_i32(); - tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); - tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label); - tcg_temp_free_i32(tmp); + cond = TCG_COND_NE; + value = tcg_temp_new_i32(); + global = false; + /* (N == V) is equal to the sign bit of ~(NF ^ VF). Propagate + * the sign bit then AND with ZF to yield the result. */ + tcg_gen_xor_i32(value, cpu_VF, cpu_NF); + tcg_gen_sari_i32(value, value, 31); + tcg_gen_andc_i32(value, cpu_ZF, value); break; + + case 14: /* always */ + case 15: /* always */ + /* Use the ALWAYS condition, which will fold early. + * It doesn't matter what we use for the value. */ + cond = TCG_COND_ALWAYS; + value = cpu_ZF; + goto no_invert; + default: fprintf(stderr, "Bad condition code 0x%x\n", cc); abort(); } + + if (cc & 1) { + cond = tcg_invert_cond(cond); + } + + no_invert: + cmp->cond = cond; + cmp->value = value; + cmp->value_global = global; +} + +void arm_free_cc(DisasCompare *cmp) +{ + if (!cmp->value_global) { + tcg_temp_free_i32(cmp->value); + } +} + +void arm_jump_cc(DisasCompare *cmp, TCGLabel *label) +{ + tcg_gen_brcondi_i32(cmp->cond, cmp->value, 0, label); +} + +void arm_gen_test_cc(int cc, TCGLabel *label) +{ + DisasCompare cmp; + arm_test_cc(&cmp, cc); + arm_jump_cc(&cmp, label); + arm_free_cc(&cmp); } static const uint8_t table_logic_cc[16] = { @@ -839,7 +867,7 @@ static inline void gen_bx_im(DisasContext *s, uint32_t addr) { TCGv_i32 tmp; - s->is_jmp = DISAS_UPDATE; + s->is_jmp = DISAS_JUMP; if (s->thumb != (addr & 1)) { tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, addr & 1); @@ -852,7 +880,7 @@ static inline void gen_bx_im(DisasContext *s, uint32_t addr) /* Set PC and Thumb state from var. var is marked as dead. */ static inline void gen_bx(DisasContext *s, TCGv_i32 var) { - s->is_jmp = DISAS_UPDATE; + s->is_jmp = DISAS_JUMP; tcg_gen_andi_i32(cpu_R[15], var, ~1); tcg_gen_andi_i32(var, var, 1); store_cpu_field(var, thumb); @@ -883,6 +911,12 @@ static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var) } } +#ifdef CONFIG_USER_ONLY +#define IS_USER_ONLY 1 +#else +#define IS_USER_ONLY 0 +#endif + /* Abstractions of "generate code to do a guest load/store for * AArch32", where a vaddr is always 32 bits (and is zero * extended if we're a 64 bit core) and data is also @@ -892,74 +926,143 @@ static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var) */ #if TARGET_LONG_BITS == 32 -#define DO_GEN_LD(SUFF, OPC) \ -static inline void gen_aa32_ld##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ +#define DO_GEN_LD(SUFF, OPC, BE32_XOR) \ +static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \ + TCGv_i32 addr, int index) \ { \ - tcg_gen_qemu_ld_i32(val, addr, index, OPC); \ -} - -#define DO_GEN_ST(SUFF, OPC) \ -static inline void gen_aa32_st##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ + TCGMemOp opc = (OPC) | s->be_data; \ + /* Not needed for user-mode BE32, where we use MO_BE instead. */ \ + if (!IS_USER_ONLY && s->sctlr_b && BE32_XOR) { \ + TCGv addr_be = tcg_temp_new(); \ + tcg_gen_xori_i32(addr_be, addr, BE32_XOR); \ + tcg_gen_qemu_ld_i32(val, addr_be, index, opc); \ + tcg_temp_free(addr_be); \ + return; \ + } \ + tcg_gen_qemu_ld_i32(val, addr, index, opc); \ +} + +#define DO_GEN_ST(SUFF, OPC, BE32_XOR) \ +static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val, \ + TCGv_i32 addr, int index) \ { \ - tcg_gen_qemu_st_i32(val, addr, index, OPC); \ -} - -static inline void gen_aa32_ld64(TCGv_i64 val, TCGv_i32 addr, int index) -{ - tcg_gen_qemu_ld_i64(val, addr, index, MO_TEQ); + TCGMemOp opc = (OPC) | s->be_data; \ + /* Not needed for user-mode BE32, where we use MO_BE instead. */ \ + if (!IS_USER_ONLY && s->sctlr_b && BE32_XOR) { \ + TCGv addr_be = tcg_temp_new(); \ + tcg_gen_xori_i32(addr_be, addr, BE32_XOR); \ + tcg_gen_qemu_st_i32(val, addr_be, index, opc); \ + tcg_temp_free(addr_be); \ + return; \ + } \ + tcg_gen_qemu_st_i32(val, addr, index, opc); \ +} + +static inline void gen_aa32_ld64(DisasContext *s, TCGv_i64 val, + TCGv_i32 addr, int index) +{ + TCGMemOp opc = MO_Q | s->be_data; + tcg_gen_qemu_ld_i64(val, addr, index, opc); + /* Not needed for user-mode BE32, where we use MO_BE instead. */ + if (!IS_USER_ONLY && s->sctlr_b) { + tcg_gen_rotri_i64(val, val, 32); + } } -static inline void gen_aa32_st64(TCGv_i64 val, TCGv_i32 addr, int index) +static inline void gen_aa32_st64(DisasContext *s, TCGv_i64 val, + TCGv_i32 addr, int index) { - tcg_gen_qemu_st_i64(val, addr, index, MO_TEQ); + TCGMemOp opc = MO_Q | s->be_data; + /* Not needed for user-mode BE32, where we use MO_BE instead. */ + if (!IS_USER_ONLY && s->sctlr_b) { + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_rotri_i64(tmp, val, 32); + tcg_gen_qemu_st_i64(tmp, addr, index, opc); + tcg_temp_free_i64(tmp); + return; + } + tcg_gen_qemu_st_i64(val, addr, index, opc); } #else -#define DO_GEN_LD(SUFF, OPC) \ -static inline void gen_aa32_ld##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ +#define DO_GEN_LD(SUFF, OPC, BE32_XOR) \ +static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \ + TCGv_i32 addr, int index) \ { \ + TCGMemOp opc = (OPC) | s->be_data; \ TCGv addr64 = tcg_temp_new(); \ tcg_gen_extu_i32_i64(addr64, addr); \ - tcg_gen_qemu_ld_i32(val, addr64, index, OPC); \ + /* Not needed for user-mode BE32, where we use MO_BE instead. */ \ + if (!IS_USER_ONLY && s->sctlr_b && BE32_XOR) { \ + tcg_gen_xori_i64(addr64, addr64, BE32_XOR); \ + } \ + tcg_gen_qemu_ld_i32(val, addr64, index, opc); \ tcg_temp_free(addr64); \ } -#define DO_GEN_ST(SUFF, OPC) \ -static inline void gen_aa32_st##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ +#define DO_GEN_ST(SUFF, OPC, BE32_XOR) \ +static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val, \ + TCGv_i32 addr, int index) \ { \ + TCGMemOp opc = (OPC) | s->be_data; \ TCGv addr64 = tcg_temp_new(); \ tcg_gen_extu_i32_i64(addr64, addr); \ - tcg_gen_qemu_st_i32(val, addr64, index, OPC); \ + /* Not needed for user-mode BE32, where we use MO_BE instead. */ \ + if (!IS_USER_ONLY && s->sctlr_b && BE32_XOR) { \ + tcg_gen_xori_i64(addr64, addr64, BE32_XOR); \ + } \ + tcg_gen_qemu_st_i32(val, addr64, index, opc); \ tcg_temp_free(addr64); \ } -static inline void gen_aa32_ld64(TCGv_i64 val, TCGv_i32 addr, int index) +static inline void gen_aa32_ld64(DisasContext *s, TCGv_i64 val, + TCGv_i32 addr, int index) { + TCGMemOp opc = MO_Q | s->be_data; TCGv addr64 = tcg_temp_new(); tcg_gen_extu_i32_i64(addr64, addr); - tcg_gen_qemu_ld_i64(val, addr64, index, MO_TEQ); + tcg_gen_qemu_ld_i64(val, addr64, index, opc); + + /* Not needed for user-mode BE32, where we use MO_BE instead. */ + if (!IS_USER_ONLY && s->sctlr_b) { + tcg_gen_rotri_i64(val, val, 32); + } tcg_temp_free(addr64); } -static inline void gen_aa32_st64(TCGv_i64 val, TCGv_i32 addr, int index) +static inline void gen_aa32_st64(DisasContext *s, TCGv_i64 val, + TCGv_i32 addr, int index) { + TCGMemOp opc = MO_Q | s->be_data; TCGv addr64 = tcg_temp_new(); tcg_gen_extu_i32_i64(addr64, addr); - tcg_gen_qemu_st_i64(val, addr64, index, MO_TEQ); + + /* Not needed for user-mode BE32, where we use MO_BE instead. */ + if (!IS_USER_ONLY && s->sctlr_b) { + TCGv tmp = tcg_temp_new(); + tcg_gen_rotri_i64(tmp, val, 32); + tcg_gen_qemu_st_i64(tmp, addr64, index, opc); + tcg_temp_free(tmp); + } else { + tcg_gen_qemu_st_i64(val, addr64, index, opc); + } tcg_temp_free(addr64); } #endif -DO_GEN_LD(8s, MO_SB) -DO_GEN_LD(8u, MO_UB) -DO_GEN_LD(16s, MO_TESW) -DO_GEN_LD(16u, MO_TEUW) -DO_GEN_LD(32u, MO_TEUL) -DO_GEN_ST(8, MO_UB) -DO_GEN_ST(16, MO_TEUW) -DO_GEN_ST(32, MO_TEUL) +DO_GEN_LD(8s, MO_SB, 3) +DO_GEN_LD(8u, MO_UB, 3) +DO_GEN_LD(16s, MO_SW, 2) +DO_GEN_LD(16u, MO_UW, 2) +DO_GEN_LD(32u, MO_UL, 0) +/* 'a' variants include an alignment check */ +DO_GEN_LD(16ua, MO_UW | MO_ALIGN, 2) +DO_GEN_LD(32ua, MO_UL | MO_ALIGN, 0) +DO_GEN_ST(8, MO_UB, 3) +DO_GEN_ST(16, MO_UW, 2) +DO_GEN_ST(32, MO_UL, 0) static inline void gen_set_pc_im(DisasContext *s, target_ulong val) { @@ -1031,7 +1134,7 @@ static void gen_exception_insn(DisasContext *s, int offset, int excp, static inline void gen_lookup_tb(DisasContext *s) { tcg_gen_movi_i32(cpu_R[15], s->pc & ~1); - s->is_jmp = DISAS_UPDATE; + s->is_jmp = DISAS_JUMP; } static inline void gen_add_data_offset(DisasContext *s, unsigned int insn, @@ -1254,18 +1357,18 @@ VFP_GEN_FIX(ulto, ) static inline void gen_vfp_ld(DisasContext *s, int dp, TCGv_i32 addr) { if (dp) { - gen_aa32_ld64(cpu_F0d, addr, get_mem_index(s)); + gen_aa32_ld64(s, cpu_F0d, addr, get_mem_index(s)); } else { - gen_aa32_ld32u(cpu_F0s, addr, get_mem_index(s)); + gen_aa32_ld32u(s, cpu_F0s, addr, get_mem_index(s)); } } static inline void gen_vfp_st(DisasContext *s, int dp, TCGv_i32 addr) { if (dp) { - gen_aa32_st64(cpu_F0d, addr, get_mem_index(s)); + gen_aa32_st64(s, cpu_F0d, addr, get_mem_index(s)); } else { - gen_aa32_st32(cpu_F0s, addr, get_mem_index(s)); + gen_aa32_st32(s, cpu_F0s, addr, get_mem_index(s)); } } @@ -1557,7 +1660,7 @@ static inline int gen_iwmmxt_shift(uint32_t insn, uint32_t mask, TCGv_i32 dest) } else { tmp = tcg_temp_new_i32(); iwmmxt_load_reg(cpu_V0, rd); - tcg_gen_trunc_i64_i32(tmp, cpu_V0); + tcg_gen_extrl_i64_i32(tmp, cpu_V0); } tcg_gen_andi_i32(tmp, tmp, mask); tcg_gen_mov_i32(dest, tmp); @@ -1581,9 +1684,9 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) rdhi = (insn >> 16) & 0xf; if (insn & ARM_CP_RW_BIT) { /* TMRRC */ iwmmxt_load_reg(cpu_V0, wrd); - tcg_gen_trunc_i64_i32(cpu_R[rdlo], cpu_V0); + tcg_gen_extrl_i64_i32(cpu_R[rdlo], cpu_V0); tcg_gen_shri_i64(cpu_V0, cpu_V0, 32); - tcg_gen_trunc_i64_i32(cpu_R[rdhi], cpu_V0); + tcg_gen_extrl_i64_i32(cpu_R[rdhi], cpu_V0); } else { /* TMCRR */ tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]); iwmmxt_store_reg(cpu_V0, wrd); @@ -1601,24 +1704,24 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) if (insn & ARM_CP_RW_BIT) { if ((insn >> 28) == 0xf) { /* WLDRW wCx */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); iwmmxt_store_creg(wrd, tmp); } else { i = 1; if (insn & (1 << 8)) { if (insn & (1 << 22)) { /* WLDRD */ - gen_aa32_ld64(cpu_M0, addr, get_mem_index(s)); + gen_aa32_ld64(s, cpu_M0, addr, get_mem_index(s)); i = 0; } else { /* WLDRW wRd */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); } } else { tmp = tcg_temp_new_i32(); if (insn & (1 << 22)) { /* WLDRH */ - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); } else { /* WLDRB */ - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); } } if (i) { @@ -1630,24 +1733,24 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) } else { if ((insn >> 28) == 0xf) { /* WSTRW wCx */ tmp = iwmmxt_load_creg(wrd); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); } else { gen_op_iwmmxt_movq_M0_wRn(wrd); tmp = tcg_temp_new_i32(); if (insn & (1 << 8)) { if (insn & (1 << 22)) { /* WSTRD */ - gen_aa32_st64(cpu_M0, addr, get_mem_index(s)); + gen_aa32_st64(s, cpu_M0, addr, get_mem_index(s)); } else { /* WSTRW wRd */ - tcg_gen_trunc_i64_i32(tmp, cpu_M0); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + tcg_gen_extrl_i64_i32(tmp, cpu_M0); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); } } else { if (insn & (1 << 22)) { /* WSTRH */ - tcg_gen_trunc_i64_i32(tmp, cpu_M0); - gen_aa32_st16(tmp, addr, get_mem_index(s)); + tcg_gen_extrl_i64_i32(tmp, cpu_M0); + gen_aa32_st16(s, tmp, addr, get_mem_index(s)); } else { /* WSTRB */ - tcg_gen_trunc_i64_i32(tmp, cpu_M0); - gen_aa32_st8(tmp, addr, get_mem_index(s)); + tcg_gen_extrl_i64_i32(tmp, cpu_M0); + gen_aa32_st8(s, tmp, addr, get_mem_index(s)); } } } @@ -1946,7 +2049,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) switch ((insn >> 22) & 3) { case 0: tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 7) << 3); - tcg_gen_trunc_i64_i32(tmp, cpu_M0); + tcg_gen_extrl_i64_i32(tmp, cpu_M0); if (insn & 8) { tcg_gen_ext8s_i32(tmp, tmp); } else { @@ -1955,7 +2058,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) break; case 1: tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 3) << 4); - tcg_gen_trunc_i64_i32(tmp, cpu_M0); + tcg_gen_extrl_i64_i32(tmp, cpu_M0); if (insn & 8) { tcg_gen_ext16s_i32(tmp, tmp); } else { @@ -1964,7 +2067,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) break; case 2: tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 1) << 5); - tcg_gen_trunc_i64_i32(tmp, cpu_M0); + tcg_gen_extrl_i64_i32(tmp, cpu_M0); break; } store_reg(s, rd, tmp); @@ -2627,9 +2730,9 @@ static int disas_dsp_insn(DisasContext *s, uint32_t insn) if (insn & ARM_CP_RW_BIT) { /* MRA */ iwmmxt_load_reg(cpu_V0, acc); - tcg_gen_trunc_i64_i32(cpu_R[rdlo], cpu_V0); + tcg_gen_extrl_i64_i32(cpu_R[rdlo], cpu_V0); tcg_gen_shri_i64(cpu_V0, cpu_V0, 32); - tcg_gen_trunc_i64_i32(cpu_R[rdhi], cpu_V0); + tcg_gen_extrl_i64_i32(cpu_R[rdhi], cpu_V0); tcg_gen_andi_i32(cpu_R[rdhi], cpu_R[rdhi], (1 << (40 - 32)) - 1); } else { /* MAR */ tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]); @@ -2712,15 +2815,15 @@ static TCGv_i32 gen_load_and_replicate(DisasContext *s, TCGv_i32 addr, int size) TCGv_i32 tmp = tcg_temp_new_i32(); switch (size) { case 0: - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); gen_neon_dup_u8(tmp, 0); break; case 1: - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); gen_neon_dup_low16(tmp); break; case 2: - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); break; default: /* Avoid compiler warnings. */ abort(); @@ -2951,7 +3054,7 @@ static int handle_vcvt(uint32_t insn, uint32_t rd, uint32_t rm, uint32_t dp, } else { gen_helper_vfp_tould(tcg_res, tcg_double, tcg_shift, fpst); } - tcg_gen_trunc_i64_i32(tcg_tmp, tcg_res); + tcg_gen_extrl_i64_i32(tcg_tmp, tcg_res); tcg_gen_st_f32(tcg_tmp, cpu_env, vfp_reg_offset(0, rd)); tcg_temp_free_i32(tcg_tmp); tcg_temp_free_i64(tcg_res); @@ -3046,7 +3149,7 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn) */ if (s->fp_excp_el) { gen_exception_insn(s, 4, EXCP_UDEF, - syn_fp_access_trap(1, 0xe, s->thumb), s->fp_excp_el); + syn_fp_access_trap(1, 0xe, false), s->fp_excp_el); return 0; } @@ -4057,24 +4160,213 @@ static int gen_set_psr_im(DisasContext *s, uint32_t mask, int spsr, uint32_t val return gen_set_psr(s, mask, spsr, tmp); } +static bool msr_banked_access_decode(DisasContext *s, int r, int sysm, int rn, + int *tgtmode, int *regno) +{ + /* Decode the r and sysm fields of MSR/MRS banked accesses into + * the target mode and register number, and identify the various + * unpredictable cases. + * MSR (banked) and MRS (banked) are CONSTRAINED UNPREDICTABLE if: + * + executed in user mode + * + using R15 as the src/dest register + * + accessing an unimplemented register + * + accessing a register that's inaccessible at current PL/security state* + * + accessing a register that you could access with a different insn + * We choose to UNDEF in all these cases. + * Since we don't know which of the various AArch32 modes we are in + * we have to defer some checks to runtime. + * Accesses to Monitor mode registers from Secure EL1 (which implies + * that EL3 is AArch64) must trap to EL3. + * + * If the access checks fail this function will emit code to take + * an exception and return false. Otherwise it will return true, + * and set *tgtmode and *regno appropriately. + */ + int exc_target = default_exception_el(s); + + /* These instructions are present only in ARMv8, or in ARMv7 with the + * Virtualization Extensions. + */ + if (!arm_dc_feature(s, ARM_FEATURE_V8) && + !arm_dc_feature(s, ARM_FEATURE_EL2)) { + goto undef; + } + + if (IS_USER(s) || rn == 15) { + goto undef; + } + + /* The table in the v8 ARM ARM section F5.2.3 describes the encoding + * of registers into (r, sysm). + */ + if (r) { + /* SPSRs for other modes */ + switch (sysm) { + case 0xe: /* SPSR_fiq */ + *tgtmode = ARM_CPU_MODE_FIQ; + break; + case 0x10: /* SPSR_irq */ + *tgtmode = ARM_CPU_MODE_IRQ; + break; + case 0x12: /* SPSR_svc */ + *tgtmode = ARM_CPU_MODE_SVC; + break; + case 0x14: /* SPSR_abt */ + *tgtmode = ARM_CPU_MODE_ABT; + break; + case 0x16: /* SPSR_und */ + *tgtmode = ARM_CPU_MODE_UND; + break; + case 0x1c: /* SPSR_mon */ + *tgtmode = ARM_CPU_MODE_MON; + break; + case 0x1e: /* SPSR_hyp */ + *tgtmode = ARM_CPU_MODE_HYP; + break; + default: /* unallocated */ + goto undef; + } + /* We arbitrarily assign SPSR a register number of 16. */ + *regno = 16; + } else { + /* general purpose registers for other modes */ + switch (sysm) { + case 0x0 ... 0x6: /* 0b00xxx : r8_usr ... r14_usr */ + *tgtmode = ARM_CPU_MODE_USR; + *regno = sysm + 8; + break; + case 0x8 ... 0xe: /* 0b01xxx : r8_fiq ... r14_fiq */ + *tgtmode = ARM_CPU_MODE_FIQ; + *regno = sysm; + break; + case 0x10 ... 0x11: /* 0b1000x : r14_irq, r13_irq */ + *tgtmode = ARM_CPU_MODE_IRQ; + *regno = sysm & 1 ? 13 : 14; + break; + case 0x12 ... 0x13: /* 0b1001x : r14_svc, r13_svc */ + *tgtmode = ARM_CPU_MODE_SVC; + *regno = sysm & 1 ? 13 : 14; + break; + case 0x14 ... 0x15: /* 0b1010x : r14_abt, r13_abt */ + *tgtmode = ARM_CPU_MODE_ABT; + *regno = sysm & 1 ? 13 : 14; + break; + case 0x16 ... 0x17: /* 0b1011x : r14_und, r13_und */ + *tgtmode = ARM_CPU_MODE_UND; + *regno = sysm & 1 ? 13 : 14; + break; + case 0x1c ... 0x1d: /* 0b1110x : r14_mon, r13_mon */ + *tgtmode = ARM_CPU_MODE_MON; + *regno = sysm & 1 ? 13 : 14; + break; + case 0x1e ... 0x1f: /* 0b1111x : elr_hyp, r13_hyp */ + *tgtmode = ARM_CPU_MODE_HYP; + /* Arbitrarily pick 17 for ELR_Hyp (which is not a banked LR!) */ + *regno = sysm & 1 ? 13 : 17; + break; + default: /* unallocated */ + goto undef; + } + } + + /* Catch the 'accessing inaccessible register' cases we can detect + * at translate time. + */ + switch (*tgtmode) { + case ARM_CPU_MODE_MON: + if (!arm_dc_feature(s, ARM_FEATURE_EL3) || s->ns) { + goto undef; + } + if (s->current_el == 1) { + /* If we're in Secure EL1 (which implies that EL3 is AArch64) + * then accesses to Mon registers trap to EL3 + */ + exc_target = 3; + goto undef; + } + break; + case ARM_CPU_MODE_HYP: + /* Note that we can forbid accesses from EL2 here because they + * must be from Hyp mode itself + */ + if (!arm_dc_feature(s, ARM_FEATURE_EL2) || s->current_el < 3) { + goto undef; + } + break; + default: + break; + } + + return true; + +undef: + /* If we get here then some access check did not pass */ + gen_exception_insn(s, 4, EXCP_UDEF, syn_uncategorized(), exc_target); + return false; +} + +static void gen_msr_banked(DisasContext *s, int r, int sysm, int rn) +{ + TCGv_i32 tcg_reg, tcg_tgtmode, tcg_regno; + int tgtmode = 0, regno = 0; + + if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, ®no)) { + return; + } + + /* Sync state because msr_banked() can raise exceptions */ + gen_set_condexec(s); + gen_set_pc_im(s, s->pc - 4); + tcg_reg = load_reg(s, rn); + tcg_tgtmode = tcg_const_i32(tgtmode); + tcg_regno = tcg_const_i32(regno); + gen_helper_msr_banked(cpu_env, tcg_reg, tcg_tgtmode, tcg_regno); + tcg_temp_free_i32(tcg_tgtmode); + tcg_temp_free_i32(tcg_regno); + tcg_temp_free_i32(tcg_reg); + s->is_jmp = DISAS_UPDATE; +} + +static void gen_mrs_banked(DisasContext *s, int r, int sysm, int rn) +{ + TCGv_i32 tcg_reg, tcg_tgtmode, tcg_regno; + int tgtmode = 0, regno = 0; + + if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, ®no)) { + return; + } + + /* Sync state because mrs_banked() can raise exceptions */ + gen_set_condexec(s); + gen_set_pc_im(s, s->pc - 4); + tcg_reg = tcg_temp_new_i32(); + tcg_tgtmode = tcg_const_i32(tgtmode); + tcg_regno = tcg_const_i32(regno); + gen_helper_mrs_banked(tcg_reg, cpu_env, tcg_tgtmode, tcg_regno); + tcg_temp_free_i32(tcg_tgtmode); + tcg_temp_free_i32(tcg_regno); + store_reg(s, rn, tcg_reg); + s->is_jmp = DISAS_UPDATE; +} + /* Generate an old-style exception return. Marks pc as dead. */ static void gen_exception_return(DisasContext *s, TCGv_i32 pc) { TCGv_i32 tmp; store_reg(s, 15, pc); tmp = load_cpu_field(spsr); - gen_set_cpsr(tmp, CPSR_ERET_MASK); + gen_helper_cpsr_write_eret(cpu_env, tmp); tcg_temp_free_i32(tmp); - s->is_jmp = DISAS_UPDATE; + s->is_jmp = DISAS_JUMP; } /* Generate a v6 exception return. Marks both values as dead. */ static void gen_rfe(DisasContext *s, TCGv_i32 pc, TCGv_i32 cpsr) { - gen_set_cpsr(cpsr, CPSR_ERET_MASK); + gen_helper_cpsr_write_eret(cpu_env, cpsr); tcg_temp_free_i32(cpsr); store_reg(s, 15, pc); - s->is_jmp = DISAS_UPDATE; + s->is_jmp = DISAS_JUMP; } static void gen_nop_hint(DisasContext *s, int val) @@ -4368,7 +4660,7 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) */ if (s->fp_excp_el) { gen_exception_insn(s, 4, EXCP_UDEF, - syn_fp_access_trap(1, 0xe, s->thumb), s->fp_excp_el); + syn_fp_access_trap(1, 0xe, false), s->fp_excp_el); return 0; } @@ -4418,11 +4710,11 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) if (size == 3) { tmp64 = tcg_temp_new_i64(); if (load) { - gen_aa32_ld64(tmp64, addr, get_mem_index(s)); + gen_aa32_ld64(s, tmp64, addr, get_mem_index(s)); neon_store_reg64(tmp64, rd); } else { neon_load_reg64(tmp64, rd); - gen_aa32_st64(tmp64, addr, get_mem_index(s)); + gen_aa32_st64(s, tmp64, addr, get_mem_index(s)); } tcg_temp_free_i64(tmp64); tcg_gen_addi_i32(addr, addr, stride); @@ -4431,21 +4723,21 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) if (size == 2) { if (load) { tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); neon_store_reg(rd, pass, tmp); } else { tmp = neon_load_reg(rd, pass); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } tcg_gen_addi_i32(addr, addr, stride); } else if (size == 1) { if (load) { tmp = tcg_temp_new_i32(); - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); tcg_gen_addi_i32(addr, addr, stride); tmp2 = tcg_temp_new_i32(); - gen_aa32_ld16u(tmp2, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp2, addr, get_mem_index(s)); tcg_gen_addi_i32(addr, addr, stride); tcg_gen_shli_i32(tmp2, tmp2, 16); tcg_gen_or_i32(tmp, tmp, tmp2); @@ -4455,10 +4747,10 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) tmp = neon_load_reg(rd, pass); tmp2 = tcg_temp_new_i32(); tcg_gen_shri_i32(tmp2, tmp, 16); - gen_aa32_st16(tmp, addr, get_mem_index(s)); + gen_aa32_st16(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); tcg_gen_addi_i32(addr, addr, stride); - gen_aa32_st16(tmp2, addr, get_mem_index(s)); + gen_aa32_st16(s, tmp2, addr, get_mem_index(s)); tcg_temp_free_i32(tmp2); tcg_gen_addi_i32(addr, addr, stride); } @@ -4467,7 +4759,7 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) TCGV_UNUSED_I32(tmp2); for (n = 0; n < 4; n++) { tmp = tcg_temp_new_i32(); - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); tcg_gen_addi_i32(addr, addr, stride); if (n == 0) { tmp2 = tmp; @@ -4487,7 +4779,7 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) } else { tcg_gen_shri_i32(tmp, tmp2, n * 8); } - gen_aa32_st8(tmp, addr, get_mem_index(s)); + gen_aa32_st8(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); tcg_gen_addi_i32(addr, addr, stride); } @@ -4611,13 +4903,13 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) tmp = tcg_temp_new_i32(); switch (size) { case 0: - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); break; case 1: - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); break; case 2: - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); break; default: /* Avoid compiler warnings. */ abort(); @@ -4635,13 +4927,13 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) tcg_gen_shri_i32(tmp, tmp, shift); switch (size) { case 0: - gen_aa32_st8(tmp, addr, get_mem_index(s)); + gen_aa32_st8(s, tmp, addr, get_mem_index(s)); break; case 1: - gen_aa32_st16(tmp, addr, get_mem_index(s)); + gen_aa32_st16(s, tmp, addr, get_mem_index(s)); break; case 2: - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); break; } tcg_temp_free_i32(tmp); @@ -4683,7 +4975,7 @@ static inline void gen_neon_narrow(int size, TCGv_i32 dest, TCGv_i64 src) switch (size) { case 0: gen_helper_neon_narrow_u8(dest, src); break; case 1: gen_helper_neon_narrow_u16(dest, src); break; - case 2: tcg_gen_trunc_i64_i32(dest, src); break; + case 2: tcg_gen_extrl_i64_i32(dest, src); break; default: abort(); } } @@ -5106,7 +5398,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) */ if (s->fp_excp_el) { gen_exception_insn(s, 4, EXCP_UDEF, - syn_fp_access_trap(1, 0xe, s->thumb), s->fp_excp_el); + syn_fp_access_trap(1, 0xe, false), s->fp_excp_el); return 0; } @@ -6254,7 +6546,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) break; case 2: tcg_gen_shri_i64(cpu_V0, cpu_V0, 32); - tcg_gen_trunc_i64_i32(tmp, cpu_V0); + tcg_gen_extrl_i64_i32(tmp, cpu_V0); break; default: abort(); } @@ -6269,7 +6561,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) case 2: tcg_gen_addi_i64(cpu_V0, cpu_V0, 1u << 31); tcg_gen_shri_i64(cpu_V0, cpu_V0, 32); - tcg_gen_trunc_i64_i32(tmp, cpu_V0); + tcg_gen_extrl_i64_i32(tmp, cpu_V0); break; default: abort(); } @@ -7138,7 +7430,7 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn) * call in order to handle c15_cpar. */ TCGv_ptr tmpptr; - TCGv_i32 tcg_syn; + TCGv_i32 tcg_syn, tcg_isread; uint32_t syndrome; /* Note that since we are an implementation which takes an @@ -7153,19 +7445,19 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn) case 14: if (is64) { syndrome = syn_cp14_rrt_trap(1, 0xe, opc1, crm, rt, rt2, - isread, s->thumb); + isread, false); } else { syndrome = syn_cp14_rt_trap(1, 0xe, opc1, opc2, crn, crm, - rt, isread, s->thumb); + rt, isread, false); } break; case 15: if (is64) { syndrome = syn_cp15_rrt_trap(1, 0xe, opc1, crm, rt, rt2, - isread, s->thumb); + isread, false); } else { syndrome = syn_cp15_rt_trap(1, 0xe, opc1, opc2, crn, crm, - rt, isread, s->thumb); + rt, isread, false); } break; default: @@ -7179,12 +7471,16 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn) break; } + gen_set_condexec(s); gen_set_pc_im(s, s->pc - 4); tmpptr = tcg_const_ptr(ri); tcg_syn = tcg_const_i32(syndrome); - gen_helper_access_check_cp_reg(cpu_env, tmpptr, tcg_syn); + tcg_isread = tcg_const_i32(isread); + gen_helper_access_check_cp_reg(cpu_env, tmpptr, tcg_syn, + tcg_isread); tcg_temp_free_ptr(tmpptr); tcg_temp_free_i32(tcg_syn); + tcg_temp_free_i32(tcg_isread); } /* Handle special cases first */ @@ -7224,11 +7520,11 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn) tcg_gen_ld_i64(tmp64, cpu_env, ri->fieldoffset); } tmp = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(tmp, tmp64); + tcg_gen_extrl_i64_i32(tmp, tmp64); store_reg(s, rt, tmp); tcg_gen_shri_i64(tmp64, tmp64, 32); tmp = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(tmp, tmp64); + tcg_gen_extrl_i64_i32(tmp, tmp64); tcg_temp_free_i64(tmp64); store_reg(s, rt2, tmp); } else { @@ -7334,11 +7630,11 @@ static void gen_storeq_reg(DisasContext *s, int rlow, int rhigh, TCGv_i64 val) { TCGv_i32 tmp; tmp = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(tmp, val); + tcg_gen_extrl_i64_i32(tmp, val); store_reg(s, rlow, tmp); tmp = tcg_temp_new_i32(); tcg_gen_shri_i64(val, val, 32); - tcg_gen_trunc_i64_i32(tmp, val); + tcg_gen_extrl_i64_i32(tmp, val); store_reg(s, rhigh, tmp); } @@ -7400,14 +7696,14 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, switch (size) { case 0: - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); break; case 1: - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16ua(s, tmp, addr, get_mem_index(s)); break; case 2: case 3: - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32ua(s, tmp, addr, get_mem_index(s)); break; default: abort(); @@ -7418,7 +7714,7 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, TCGv_i32 tmp3 = tcg_temp_new_i32(); tcg_gen_addi_i32(tmp2, addr, 4); - gen_aa32_ld32u(tmp3, tmp2, get_mem_index(s)); + gen_aa32_ld32u(s, tmp3, tmp2, get_mem_index(s)); tcg_temp_free_i32(tmp2); tcg_gen_concat_i32_i64(cpu_exclusive_val, tmp, tmp3); store_reg(s, rt2, tmp3); @@ -7469,14 +7765,14 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, tmp = tcg_temp_new_i32(); switch (size) { case 0: - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); break; case 1: - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); break; case 2: case 3: - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); break; default: abort(); @@ -7487,7 +7783,7 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, TCGv_i32 tmp2 = tcg_temp_new_i32(); TCGv_i32 tmp3 = tcg_temp_new_i32(); tcg_gen_addi_i32(tmp2, addr, 4); - gen_aa32_ld32u(tmp3, tmp2, get_mem_index(s)); + gen_aa32_ld32u(s, tmp3, tmp2, get_mem_index(s)); tcg_temp_free_i32(tmp2); tcg_gen_concat_i32_i64(val64, tmp, tmp3); tcg_temp_free_i32(tmp3); @@ -7502,14 +7798,14 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, tmp = load_reg(s, rt); switch (size) { case 0: - gen_aa32_st8(tmp, addr, get_mem_index(s)); + gen_aa32_st8(s, tmp, addr, get_mem_index(s)); break; case 1: - gen_aa32_st16(tmp, addr, get_mem_index(s)); + gen_aa32_st16(s, tmp, addr, get_mem_index(s)); break; case 2: case 3: - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); break; default: abort(); @@ -7518,7 +7814,7 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, if (size == 3) { tcg_gen_addi_i32(addr, addr, 4); tmp = load_reg(s, rt2); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } tcg_gen_movi_i32(cpu_R[rd], 0); @@ -7543,8 +7839,68 @@ static void gen_srs(DisasContext *s, uint32_t mode, uint32_t amode, bool writeback) { int32_t offset; - TCGv_i32 addr = tcg_temp_new_i32(); - TCGv_i32 tmp = tcg_const_i32(mode); + TCGv_i32 addr, tmp; + bool undef = false; + + /* SRS is: + * - trapped to EL3 if EL3 is AArch64 and we are at Secure EL1 + * and specified mode is monitor mode + * - UNDEFINED in Hyp mode + * - UNPREDICTABLE in User or System mode + * - UNPREDICTABLE if the specified mode is: + * -- not implemented + * -- not a valid mode number + * -- a mode that's at a higher exception level + * -- Monitor, if we are Non-secure + * For the UNPREDICTABLE cases we choose to UNDEF. + */ + if (s->current_el == 1 && !s->ns && mode == ARM_CPU_MODE_MON) { + gen_exception_insn(s, 4, EXCP_UDEF, syn_uncategorized(), 3); + return; + } + + if (s->current_el == 0 || s->current_el == 2) { + undef = true; + } + + switch (mode) { + case ARM_CPU_MODE_USR: + case ARM_CPU_MODE_FIQ: + case ARM_CPU_MODE_IRQ: + case ARM_CPU_MODE_SVC: + case ARM_CPU_MODE_ABT: + case ARM_CPU_MODE_UND: + case ARM_CPU_MODE_SYS: + break; + case ARM_CPU_MODE_HYP: + if (s->current_el == 1 || !arm_dc_feature(s, ARM_FEATURE_EL2)) { + undef = true; + } + break; + case ARM_CPU_MODE_MON: + /* No need to check specifically for "are we non-secure" because + * we've already made EL0 UNDEF and handled the trap for S-EL1; + * so if this isn't EL3 then we must be non-secure. + */ + if (s->current_el != 3) { + undef = true; + } + break; + default: + undef = true; + } + + if (undef) { + gen_exception_insn(s, 4, EXCP_UDEF, syn_uncategorized(), + default_exception_el(s)); + return; + } + + addr = tcg_temp_new_i32(); + tmp = tcg_const_i32(mode); + /* get_r13_banked() will raise an exception if called from System mode */ + gen_set_condexec(s); + gen_set_pc_im(s, s->pc - 4); gen_helper_get_r13_banked(addr, cpu_env, tmp); tcg_temp_free_i32(tmp); switch (amode) { @@ -7565,11 +7921,11 @@ static void gen_srs(DisasContext *s, } tcg_gen_addi_i32(addr, addr, offset); tmp = load_reg(s, 14); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); tmp = load_cpu_field(spsr); tcg_gen_addi_i32(addr, addr, 4); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); if (writeback) { switch (amode) { @@ -7594,6 +7950,7 @@ static void gen_srs(DisasContext *s, tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); + s->is_jmp = DISAS_UPDATE; } static void disas_arm_insn(DisasContext *s, unsigned int insn) @@ -7675,10 +8032,9 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) if ((insn & 0x0ffffdff) == 0x01010000) { ARCH(6); /* setend */ - if (((insn >> 9) & 1) != s->bswap_code) { - /* Dynamic endianness switching not implemented. */ - qemu_log_mask(LOG_UNIMP, "arm: unimplemented setend\n"); - goto illegal_op; + if (((insn >> 9) & 1) != !!(s->be_data == MO_BE)) { + gen_helper_setend(cpu_env); + s->is_jmp = DISAS_UPDATE; } return; } else if ((insn & 0x0fffff00) == 0x057ff000) { @@ -7689,18 +8045,21 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) return; case 4: /* dsb */ case 5: /* dmb */ - case 6: /* isb */ ARCH(7); /* We don't emulate caches so these are a no-op. */ return; + case 6: /* isb */ + /* We need to break the TB after this insn to execute + * self-modifying code correctly and also to take + * any pending interrupts immediately. + */ + gen_lookup_tb(s); + return; default: goto illegal_op; } } else if ((insn & 0x0e5fffe0) == 0x084d0500) { /* srs */ - if (IS_USER(s)) { - goto illegal_op; - } ARCH(6); gen_srs(s, (insn & 0x1f), (insn >> 23) & 3, insn & (1 << 21)); return; @@ -7724,10 +8083,10 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tcg_gen_addi_i32(addr, addr, offset); /* Load PC into tmp and CPSR into tmp2. */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); tcg_gen_addi_i32(addr, addr, 4); tmp2 = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp2, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp2, addr, get_mem_index(s)); if (insn & (1 << 21)) { /* Base writeback. */ switch (i) { @@ -7852,7 +8211,26 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) sh = (insn >> 4) & 0xf; rm = insn & 0xf; switch (sh) { - case 0x0: /* move program status register */ + case 0x0: /* MSR, MRS */ + if (insn & (1 << 9)) { + /* MSR (banked) and MRS (banked) */ + int sysm = extract32(insn, 16, 4) | + (extract32(insn, 8, 1) << 4); + int r = extract32(insn, 22, 1); + + if (op1 & 1) { + /* MSR (banked) */ + gen_msr_banked(s, r, sysm, rm); + } else { + /* MRS (banked) */ + int rd = extract32(insn, 12, 4); + + gen_mrs_banked(s, r, sysm, rd); + } + break; + } + + /* MSR, MRS (for PSRs) */ if (op1 & 1) { /* PSR = reg */ tmp = load_reg(s, rm); @@ -8013,7 +8391,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tmp64 = gen_muls_i64_i32(tmp, tmp2); tcg_gen_shri_i64(tmp64, tmp64, 16); tmp = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(tmp, tmp64); + tcg_gen_extrl_i64_i32(tmp, tmp64); tcg_temp_free_i64(tmp64); if ((sh & 2) == 0) { tmp2 = load_reg(s, rn); @@ -8343,13 +8721,16 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tmp = tcg_temp_new_i32(); switch (op1) { case 0: /* lda */ - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, + get_mem_index(s)); break; case 2: /* ldab */ - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, + get_mem_index(s)); break; case 3: /* ldah */ - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, + get_mem_index(s)); break; default: abort(); @@ -8360,13 +8741,16 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tmp = load_reg(s, rm); switch (op1) { case 0: /* stl */ - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, + get_mem_index(s)); break; case 2: /* stlb */ - gen_aa32_st8(tmp, addr, get_mem_index(s)); + gen_aa32_st8(s, tmp, addr, + get_mem_index(s)); break; case 3: /* stlh */ - gen_aa32_st16(tmp, addr, get_mem_index(s)); + gen_aa32_st16(s, tmp, addr, + get_mem_index(s)); break; default: abort(); @@ -8421,11 +8805,11 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tmp = load_reg(s, rm); tmp2 = tcg_temp_new_i32(); if (insn & (1 << 22)) { - gen_aa32_ld8u(tmp2, addr, get_mem_index(s)); - gen_aa32_st8(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp2, addr, get_mem_index(s)); + gen_aa32_st8(s, tmp, addr, get_mem_index(s)); } else { - gen_aa32_ld32u(tmp2, addr, get_mem_index(s)); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp2, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); } tcg_temp_free_i32(tmp); tcg_temp_free_i32(addr); @@ -8460,20 +8844,20 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) if (!load) { /* store */ tmp = load_reg(s, rd); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); tcg_gen_addi_i32(addr, addr, 4); tmp = load_reg(s, rd + 1); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } else { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); store_reg(s, rd, tmp); tcg_gen_addi_i32(addr, addr, 4); tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); rd++; } address_offset = -4; @@ -8482,25 +8866,25 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tmp = tcg_temp_new_i32(); switch (sh) { case 1: - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); break; case 2: - gen_aa32_ld8s(tmp, addr, get_mem_index(s)); + gen_aa32_ld8s(s, tmp, addr, get_mem_index(s)); break; default: case 3: - gen_aa32_ld16s(tmp, addr, get_mem_index(s)); + gen_aa32_ld16s(s, tmp, addr, get_mem_index(s)); break; } } else { /* store */ tmp = load_reg(s, rd); - gen_aa32_st16(tmp, addr, get_mem_index(s)); + gen_aa32_st16(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } /* Perform base writeback before the loaded value to ensure correct behavior with overlapping index registers. - ldrd with base writeback is is undefined if the + ldrd with base writeback is undefined if the destination and index registers overlap. */ if (!(insn & (1 << 24))) { gen_add_datah_offset(s, insn, address_offset, addr); @@ -8679,7 +9063,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) } tcg_gen_shri_i64(tmp64, tmp64, 32); tmp = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(tmp, tmp64); + tcg_gen_extrl_i64_i32(tmp, tmp64); tcg_temp_free_i64(tmp64); store_reg(s, rn, tmp); break; @@ -8848,17 +9232,17 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) /* load */ tmp = tcg_temp_new_i32(); if (insn & (1 << 22)) { - gen_aa32_ld8u(tmp, tmp2, i); + gen_aa32_ld8u(s, tmp, tmp2, i); } else { - gen_aa32_ld32u(tmp, tmp2, i); + gen_aa32_ld32u(s, tmp, tmp2, i); } } else { /* store */ tmp = load_reg(s, rd); if (insn & (1 << 22)) { - gen_aa32_st8(tmp, tmp2, i); + gen_aa32_st8(s, tmp, tmp2, i); } else { - gen_aa32_st32(tmp, tmp2, i); + gen_aa32_st32(s, tmp, tmp2, i); } tcg_temp_free_i32(tmp); } @@ -8931,7 +9315,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) if (is_load) { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); if (user) { tmp2 = tcg_const_i32(i); gen_helper_set_user_reg(cpu_env, tmp2, tmp); @@ -8958,7 +9342,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) } else { tmp = load_reg(s, i); } - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } j++; @@ -8996,9 +9380,9 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) if (exc_return) { /* Restore CPSR from SPSR. */ tmp = load_cpu_field(spsr); - gen_set_cpsr(tmp, CPSR_ERET_MASK); + gen_helper_cpsr_write_eret(cpu_env, tmp); tcg_temp_free_i32(tmp); - s->is_jmp = DISAS_UPDATE; + s->is_jmp = DISAS_JUMP; } } break; @@ -9188,7 +9572,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw /* Fall through to 32-bit decode. */ } - insn = arm_lduw_code(env, s->pc, s->bswap_code); + insn = arm_lduw_code(env, s->pc, s->sctlr_b); s->pc += 2; insn |= (uint32_t)insn_hw1 << 16; @@ -9225,20 +9609,20 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw if (insn & (1 << 20)) { /* ldrd */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); store_reg(s, rs, tmp); tcg_gen_addi_i32(addr, addr, 4); tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); store_reg(s, rd, tmp); } else { /* strd */ tmp = load_reg(s, rs); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); tcg_gen_addi_i32(addr, addr, 4); tmp = load_reg(s, rd); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } if (insn & (1 << 21)) { @@ -9276,11 +9660,11 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tcg_gen_add_i32(addr, addr, tmp); tcg_temp_free_i32(tmp); tmp = tcg_temp_new_i32(); - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); } else { /* tbb */ tcg_temp_free_i32(tmp); tmp = tcg_temp_new_i32(); - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); } tcg_temp_free_i32(addr); tcg_gen_shli_i32(tmp, tmp, 1); @@ -9317,13 +9701,13 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp = tcg_temp_new_i32(); switch (op) { case 0: /* ldab */ - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); break; case 1: /* ldah */ - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); break; case 2: /* lda */ - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); break; default: abort(); @@ -9333,13 +9717,13 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp = load_reg(s, rs); switch (op) { case 0: /* stlb */ - gen_aa32_st8(tmp, addr, get_mem_index(s)); + gen_aa32_st8(s, tmp, addr, get_mem_index(s)); break; case 1: /* stlh */ - gen_aa32_st16(tmp, addr, get_mem_index(s)); + gen_aa32_st16(s, tmp, addr, get_mem_index(s)); break; case 2: /* stl */ - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); break; default: abort(); @@ -9367,10 +9751,10 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tcg_gen_addi_i32(addr, addr, -8); /* Load PC into tmp and CPSR into tmp2. */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); tcg_gen_addi_i32(addr, addr, 4); tmp2 = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp2, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp2, addr, get_mem_index(s)); if (insn & (1 << 21)) { /* Base writeback. */ if (insn & (1 << 24)) { @@ -9409,7 +9793,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw if (insn & (1 << 20)) { /* Load. */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); if (i == 15) { gen_bx(s, tmp); } else if (i == rn) { @@ -9421,7 +9805,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw } else { /* Store. */ tmp = load_reg(s, i); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } tcg_gen_addi_i32(addr, addr, 4); @@ -9749,7 +10133,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp64 = gen_muls_i64_i32(tmp, tmp2); tcg_gen_shri_i64(tmp64, tmp64, 16); tmp = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(tmp, tmp64); + tcg_gen_extrl_i64_i32(tmp, tmp64); tcg_temp_free_i64(tmp64); if (rs != 15) { @@ -9773,7 +10157,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw } tcg_gen_shri_i64(tmp64, tmp64, 32); tmp = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(tmp, tmp64); + tcg_gen_extrl_i64_i32(tmp, tmp64); tcg_temp_free_i64(tmp64); break; case 7: /* Unsigned sum of absolute differences. */ @@ -9957,6 +10341,18 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw if (arm_dc_feature(s, ARM_FEATURE_M)) { goto illegal_op; } + + if (extract32(insn, 5, 1)) { + /* MSR (banked) */ + int sysm = extract32(insn, 8, 4) | + (extract32(insn, 4, 1) << 4); + int r = op & 1; + + gen_msr_banked(s, r, sysm, rm); + break; + } + + /* MSR (for PSRs) */ tmp = load_reg(s, rn); if (gen_set_psr(s, msr_mask(s, (insn >> 8) & 0xf, op == 1), @@ -9999,9 +10395,16 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw break; case 4: /* dsb */ case 5: /* dmb */ - case 6: /* isb */ /* These execute as NOPs. */ break; + case 6: /* isb */ + /* We need to break the TB after this insn + * to execute self-modifying code correctly + * and also to take any pending interrupts + * immediately. + */ + gen_lookup_tb(s); + break; default: goto illegal_op; } @@ -10022,7 +10425,17 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tcg_gen_subi_i32(tmp, tmp, insn & 0xff); gen_exception_return(s, tmp); break; - case 6: /* mrs cpsr. */ + case 6: /* MRS */ + if (extract32(insn, 5, 1)) { + /* MRS (banked) */ + int sysm = extract32(insn, 16, 4) | + (extract32(insn, 4, 1) << 4); + + gen_mrs_banked(s, 0, sysm, rd); + break; + } + + /* mrs cpsr */ tmp = tcg_temp_new_i32(); if (arm_dc_feature(s, ARM_FEATURE_M)) { addr = tcg_const_i32(insn & 0xff); @@ -10033,7 +10446,17 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw } store_reg(s, rd, tmp); break; - case 7: /* mrs spsr. */ + case 7: /* MRS */ + if (extract32(insn, 5, 1)) { + /* MRS (banked) */ + int sysm = extract32(insn, 16, 4) | + (extract32(insn, 4, 1) << 4); + + gen_mrs_banked(s, 1, sysm, rd); + break; + } + + /* mrs spsr. */ /* Not accessible in user mode. */ if (IS_USER(s) || arm_dc_feature(s, ARM_FEATURE_M)) { goto illegal_op; @@ -10344,19 +10767,19 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp = tcg_temp_new_i32(); switch (op) { case 0: - gen_aa32_ld8u(tmp, addr, memidx); + gen_aa32_ld8u(s, tmp, addr, memidx); break; case 4: - gen_aa32_ld8s(tmp, addr, memidx); + gen_aa32_ld8s(s, tmp, addr, memidx); break; case 1: - gen_aa32_ld16u(tmp, addr, memidx); + gen_aa32_ld16u(s, tmp, addr, memidx); break; case 5: - gen_aa32_ld16s(tmp, addr, memidx); + gen_aa32_ld16s(s, tmp, addr, memidx); break; case 2: - gen_aa32_ld32u(tmp, addr, memidx); + gen_aa32_ld32u(s, tmp, addr, memidx); break; default: tcg_temp_free_i32(tmp); @@ -10373,13 +10796,13 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp = load_reg(s, rs); switch (op) { case 0: - gen_aa32_st8(tmp, addr, memidx); + gen_aa32_st8(s, tmp, addr, memidx); break; case 1: - gen_aa32_st16(tmp, addr, memidx); + gen_aa32_st16(s, tmp, addr, memidx); break; case 2: - gen_aa32_st32(tmp, addr, memidx); + gen_aa32_st32(s, tmp, addr, memidx); break; default: tcg_temp_free_i32(tmp); @@ -10423,7 +10846,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) } } - insn = arm_lduw_code(env, s->pc, s->bswap_code); + insn = arm_lduw_code(env, s->pc, s->sctlr_b); s->pc += 2; switch (insn >> 12) { @@ -10516,7 +10939,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) addr = tcg_temp_new_i32(); tcg_gen_movi_i32(addr, val); tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(addr); store_reg(s, rd, tmp); break; @@ -10719,28 +11142,28 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) switch (op) { case 0: /* str */ - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); break; case 1: /* strh */ - gen_aa32_st16(tmp, addr, get_mem_index(s)); + gen_aa32_st16(s, tmp, addr, get_mem_index(s)); break; case 2: /* strb */ - gen_aa32_st8(tmp, addr, get_mem_index(s)); + gen_aa32_st8(s, tmp, addr, get_mem_index(s)); break; case 3: /* ldrsb */ - gen_aa32_ld8s(tmp, addr, get_mem_index(s)); + gen_aa32_ld8s(s, tmp, addr, get_mem_index(s)); break; case 4: /* ldr */ - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); break; case 5: /* ldrh */ - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); break; case 6: /* ldrb */ - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); break; case 7: /* ldrsh */ - gen_aa32_ld16s(tmp, addr, get_mem_index(s)); + gen_aa32_ld16s(s, tmp, addr, get_mem_index(s)); break; } if (op >= 3) { /* load */ @@ -10762,12 +11185,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); @@ -10784,12 +11207,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); - gen_aa32_st8(tmp, addr, get_mem_index(s)); + gen_aa32_st8(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); @@ -10806,12 +11229,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); - gen_aa32_st16(tmp, addr, get_mem_index(s)); + gen_aa32_st16(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); @@ -10827,12 +11250,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); @@ -10900,12 +11323,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* pop */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); store_reg(s, i, tmp); } else { /* push */ tmp = load_reg(s, i); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } /* advance to the next address. */ @@ -10917,13 +11340,13 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* pop pc */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); /* don't set the pc until the rest of the instruction has completed */ } else { /* push lr */ tmp = load_reg(s, 14); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } tcg_gen_addi_i32(addr, addr, 4); @@ -10994,10 +11417,9 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) case 2: /* setend */ ARCH(6); - if (((insn >> 3) & 1) != s->bswap_code) { - /* Dynamic endianness switching not implemented. */ - qemu_log_mask(LOG_UNIMP, "arm: unimplemented setend\n"); - goto illegal_op; + if (((insn >> 3) & 1) != !!(s->be_data == MO_BE)) { + gen_helper_setend(cpu_env); + s->is_jmp = DISAS_UPDATE; } break; case 3: @@ -11053,7 +11475,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); if (i == rn) { loaded_var = tmp; } else { @@ -11062,7 +11484,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) } else { /* store */ tmp = load_reg(s, i); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } /* advance to the next address */ @@ -11135,22 +11557,46 @@ undef: default_exception_el(s)); } -/* generate intermediate code in gen_opc_buf and gen_opparam_buf for - basic block 'tb'. If search_pc is TRUE, also generate PC - information for each intermediate instruction. */ -static inline void gen_intermediate_code_internal(ARMCPU *cpu, - TranslationBlock *tb, - bool search_pc) +static bool insn_crosses_page(CPUARMState *env, DisasContext *s) +{ + /* Return true if the insn at dc->pc might cross a page boundary. + * (False positives are OK, false negatives are not.) + */ + uint16_t insn; + + if ((s->pc & 3) == 0) { + /* At a 4-aligned address we can't be crossing a page */ + return false; + } + + /* This must be a Thumb insn */ + insn = arm_lduw_code(env, s->pc, s->sctlr_b); + + if ((insn >> 11) >= 0x1d) { + /* Top five bits 0b11101 / 0b11110 / 0b11111 : this is the + * First half of a 32-bit Thumb insn. Thumb-1 cores might + * end up actually treating this as two 16-bit insns (see the + * code at the start of disas_thumb2_insn()) but we don't bother + * to check for that as it is unlikely, and false positives here + * are harmless. + */ + return true; + } + /* Definitely a 16-bit insn, can't be crossing a page. */ + return false; +} + +/* generate intermediate code for basic block 'tb'. */ +void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb) { + ARMCPU *cpu = arm_env_get_cpu(env); CPUState *cs = CPU(cpu); - CPUARMState *env = &cpu->env; DisasContext dc1, *dc = &dc1; - CPUBreakpoint *bp; - int j, lj; target_ulong pc_start; target_ulong next_page_start; int num_insns; int max_insns; + bool end_of_page; /* generate intermediate code */ @@ -11158,7 +11604,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, * the A32/T32 complexity to do with conditional execution/IT blocks/etc. */ if (ARM_TBFLAG_AARCH64_STATE(tb->flags)) { - gen_intermediate_code_internal_a64(cpu, tb, search_pc); + gen_intermediate_code_a64(cpu, tb); return; } @@ -11172,9 +11618,14 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, dc->condjmp = 0; dc->aarch64 = 0; - dc->el3_is_aa64 = arm_el_is_aa64(env, 3); + /* If we are coming from secure EL0 in a system with a 32-bit EL3, then + * there is no secure EL1, so we route exceptions to EL3. + */ + dc->secure_routed_to_el3 = arm_feature(env, ARM_FEATURE_EL3) && + !arm_el_is_aa64(env, 3); dc->thumb = ARM_TBFLAG_THUMB(tb->flags); - dc->bswap_code = ARM_TBFLAG_BSWAP_CODE(tb->flags); + dc->sctlr_b = ARM_TBFLAG_SCTLR_B(tb->flags); + dc->be_data = ARM_TBFLAG_BE_DATA(tb->flags) ? MO_BE : MO_LE; dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1; dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4; dc->mmu_idx = ARM_TBFLAG_MMUIDX(tb->flags); @@ -11220,11 +11671,14 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, /* FIXME: cpu_M0 can probably be the same as cpu_V0. */ cpu_M0 = tcg_temp_new_i64(); next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; - lj = -1; num_insns = 0; max_insns = tb->cflags & CF_COUNT_MASK; - if (max_insns == 0) + if (max_insns == 0) { max_insns = CF_COUNT_MASK; + } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } gen_tb_start(tb); @@ -11250,10 +11704,9 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, * (3) if we leave the TB unexpectedly (eg a data abort on a load) * then the CPUARMState will be wrong and we need to reset it. * This is handled in the same way as restoration of the - * PC in these situations: we will be called again with search_pc=1 - * and generate a mapping of the condexec bits for each PC in - * gen_opc_condexec_bits[]. restore_state_to_opc() then uses - * this to restore the condexec bits. + * PC in these situations; we save the value of the condexec bits + * for each PC via tcg_gen_insn_start(), and restore_state_to_opc() + * then uses this to restore them after an exception. * * Note that there are no instructions which can read the condexec * bits, and none which can write non-static values to them, so @@ -11270,13 +11723,17 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, store_cpu_field(tmp, condexec_bits); } do { + tcg_gen_insn_start(dc->pc, + (dc->condexec_cond << 4) | (dc->condexec_mask >> 1)); + num_insns++; + #ifdef CONFIG_USER_ONLY /* Intercept jump to the magic kernel page. */ if (dc->pc >= 0xffff0000) { /* We always get here via a jump, so know we are not in a conditional execution block. */ gen_exception_internal(EXCP_KERNEL_TRAP); - dc->is_jmp = DISAS_UPDATE; + dc->is_jmp = DISAS_EXC; break; } #else @@ -11284,40 +11741,40 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, /* We always get here via a jump, so know we are not in a conditional execution block. */ gen_exception_internal(EXCP_EXCEPTION_EXIT); - dc->is_jmp = DISAS_UPDATE; + dc->is_jmp = DISAS_EXC; break; } #endif if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { + CPUBreakpoint *bp; QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { if (bp->pc == dc->pc) { - gen_exception_internal_insn(dc, 0, EXCP_DEBUG); - /* Advance PC so that clearing the breakpoint will - invalidate this TB. */ - dc->pc += 2; - goto done_generating; + if (bp->flags & BP_CPU) { + gen_set_condexec(dc); + gen_set_pc_im(dc, dc->pc); + gen_helper_check_breakpoints(cpu_env); + /* End the TB early; it's likely not going to be executed */ + dc->is_jmp = DISAS_UPDATE; + } else { + gen_exception_internal_insn(dc, 0, EXCP_DEBUG); + /* The address covered by the breakpoint must be + included in [tb->pc, tb->pc + tb->size) in order + to for it to be properly cleared -- thus we + increment the PC here so that the logic setting + tb->size below does the right thing. */ + /* TODO: Advance PC by correct instruction length to + * avoid disassembler error messages */ + dc->pc += 2; + goto done_generating; + } + break; } } } - if (search_pc) { - j = tcg_op_buf_count(); - if (lj < j) { - lj++; - while (lj < j) - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - tcg_ctx.gen_opc_pc[lj] = dc->pc; - gen_opc_condexec_bits[lj] = (dc->condexec_cond << 4) | (dc->condexec_mask >> 1); - tcg_ctx.gen_opc_instr_start[lj] = 1; - tcg_ctx.gen_opc_icount[lj] = num_insns; - } - if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) + if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { gen_io_start(); - - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { - tcg_gen_debug_insn_start(dc->pc); } if (dc->ss_active && !dc->pstate_ss) { @@ -11331,7 +11788,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, * "did not step an insn" case, and so the syndrome ISV and EX * bits should be zero. */ - assert(num_insns == 0); + assert(num_insns == 1); gen_exception(EXCP_UDEF, syn_swstep(dc->ss_same_el, 0, 0), default_exception_el(dc)); goto done_generating; @@ -11348,7 +11805,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, } } } else { - unsigned int insn = arm_ldl_code(env, dc->pc, dc->bswap_code); + unsigned int insn = arm_ldl_code(env, dc->pc, dc->sctlr_b); dc->pc += 4; disas_arm_insn(dc, insn); } @@ -11367,12 +11824,24 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, * Otherwise the subsequent code could get translated several times. * Also stop translation when a page boundary is reached. This * ensures prefetch aborts occur at the right place. */ - num_insns ++; + + /* We want to stop the TB if the next insn starts in a new page, + * or if it spans between this page and the next. This means that + * if we're looking at the last halfword in the page we need to + * see if it's a 16-bit Thumb insn (which will fit in this TB) + * or a 32-bit Thumb insn (which won't). + * This is to avoid generating a silly TB with a single 16-bit insn + * in it at the end of this page (which would execute correctly + * but isn't very efficient). + */ + end_of_page = (dc->pc >= next_page_start) || + ((dc->pc >= next_page_start - 3) && insn_crosses_page(env, dc)); + } while (!dc->is_jmp && !tcg_op_buf_full() && !cs->singlestep_enabled && !singlestep && !dc->ss_active && - dc->pc < next_page_start && + !end_of_page && num_insns < max_insns); if (tb->cflags & CF_LAST_IO) { @@ -11388,47 +11857,45 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, instruction was a conditional branch or trap, and the PC has already been written. */ if (unlikely(cs->singlestep_enabled || dc->ss_active)) { - /* Make sure the pc is updated, and raise a debug exception. */ - if (dc->condjmp) { - gen_set_condexec(dc); - if (dc->is_jmp == DISAS_SWI) { - gen_ss_advance(dc); - gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb), - default_exception_el(dc)); - } else if (dc->is_jmp == DISAS_HVC) { - gen_ss_advance(dc); - gen_exception(EXCP_HVC, syn_aa32_hvc(dc->svc_imm), 2); - } else if (dc->is_jmp == DISAS_SMC) { - gen_ss_advance(dc); - gen_exception(EXCP_SMC, syn_aa32_smc(), 3); - } else if (dc->ss_active) { - gen_step_complete_exception(dc); - } else { - gen_exception_internal(EXCP_DEBUG); - } - gen_set_label(dc->condlabel); - } - if (dc->condjmp || !dc->is_jmp) { - gen_set_pc_im(dc, dc->pc); - dc->condjmp = 0; - } + /* Unconditional and "condition passed" instruction codepath. */ gen_set_condexec(dc); - if (dc->is_jmp == DISAS_SWI && !dc->condjmp) { + switch (dc->is_jmp) { + case DISAS_SWI: gen_ss_advance(dc); gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb), default_exception_el(dc)); - } else if (dc->is_jmp == DISAS_HVC && !dc->condjmp) { + break; + case DISAS_HVC: gen_ss_advance(dc); gen_exception(EXCP_HVC, syn_aa32_hvc(dc->svc_imm), 2); - } else if (dc->is_jmp == DISAS_SMC && !dc->condjmp) { + break; + case DISAS_SMC: gen_ss_advance(dc); gen_exception(EXCP_SMC, syn_aa32_smc(), 3); - } else if (dc->ss_active) { - gen_step_complete_exception(dc); - } else { - /* FIXME: Single stepping a WFI insn will not halt - the CPU. */ - gen_exception_internal(EXCP_DEBUG); + break; + case DISAS_NEXT: + case DISAS_UPDATE: + gen_set_pc_im(dc, dc->pc); + /* fall through */ + default: + if (dc->ss_active) { + gen_step_complete_exception(dc); + } else { + /* FIXME: Single stepping a WFI insn will not halt + the CPU. */ + gen_exception_internal(EXCP_DEBUG); + } + } + if (dc->condjmp) { + /* "Condition failed" instruction codepath. */ + gen_set_label(dc->condlabel); + gen_set_condexec(dc); + gen_set_pc_im(dc, dc->pc); + if (dc->ss_active) { + gen_step_complete_exception(dc); + } else { + gen_exception_internal(EXCP_DEBUG); + } } } else { /* While branches must always occur at the end of an IT block, @@ -11444,9 +11911,11 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, case DISAS_NEXT: gen_goto_tb(dc, 1, dc->pc); break; - default: - case DISAS_JUMP: case DISAS_UPDATE: + gen_set_pc_im(dc, dc->pc); + /* fall through */ + case DISAS_JUMP: + default: /* indicate that the hash table must be used to find the next TB */ tcg_gen_exit_tb(0); break; @@ -11489,33 +11958,17 @@ done_generating: gen_tb_end(tb, num_insns); #ifdef DEBUG_DISAS - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) && + qemu_log_in_addr_range(pc_start)) { qemu_log("----------------\n"); qemu_log("IN: %s\n", lookup_symbol(pc_start)); log_target_disas(cs, pc_start, dc->pc - pc_start, - dc->thumb | (dc->bswap_code << 1)); + dc->thumb | (dc->sctlr_b << 1)); qemu_log("\n"); } #endif - if (search_pc) { - j = tcg_op_buf_count(); - lj++; - while (lj <= j) - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } else { - tb->size = dc->pc - pc_start; - tb->icount = num_insns; - } -} - -void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb) -{ - gen_intermediate_code_internal(arm_env_get_cpu(env), tb, false); -} - -void gen_intermediate_code_pc(CPUARMState *env, TranslationBlock *tb) -{ - gen_intermediate_code_internal(arm_env_get_cpu(env), tb, true); + tb->size = dc->pc - pc_start; + tb->icount = num_insns; } static const char *cpu_mode_names[16] = { @@ -11530,6 +11983,7 @@ void arm_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, CPUARMState *env = &cpu->env; int i; uint32_t psr; + const char *ns_status; if (is_a64(env)) { aarch64_cpu_dump_state(cs, f, cpu_fprintf, flags); @@ -11544,13 +11998,22 @@ void arm_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, cpu_fprintf(f, " "); } psr = cpsr_read(env); - cpu_fprintf(f, "PSR=%08x %c%c%c%c %c %s%d\n", + + if (arm_feature(env, ARM_FEATURE_EL3) && + (psr & CPSR_M) != ARM_CPU_MODE_MON) { + ns_status = env->cp15.scr_el3 & SCR_NS ? "NS " : "S "; + } else { + ns_status = ""; + } + + cpu_fprintf(f, "PSR=%08x %c%c%c%c %c %s%s%d\n", psr, psr & (1 << 31) ? 'N' : '-', psr & (1 << 30) ? 'Z' : '-', psr & (1 << 29) ? 'C' : '-', psr & (1 << 28) ? 'V' : '-', psr & CPSR_T ? 'T' : 'A', + ns_status, cpu_mode_names[psr & 0xf], (psr & 0x10) ? 32 : 26); if (flags & CPU_DUMP_FPU) { @@ -11572,13 +12035,14 @@ void arm_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, } } -void restore_state_to_opc(CPUARMState *env, TranslationBlock *tb, int pc_pos) +void restore_state_to_opc(CPUARMState *env, TranslationBlock *tb, + target_ulong *data) { if (is_a64(env)) { - env->pc = tcg_ctx.gen_opc_pc[pc_pos]; + env->pc = data[0]; env->condexec_bits = 0; } else { - env->regs[15] = tcg_ctx.gen_opc_pc[pc_pos]; - env->condexec_bits = gen_opc_condexec_bits[pc_pos]; + env->regs[15] = data[0]; + env->condexec_bits = data[1]; } } diff --git a/qemu/target-arm/translate.h b/qemu/target-arm/translate.h index 9ab978fb7..6a18d7bad 100644 --- a/qemu/target-arm/translate.h +++ b/qemu/target-arm/translate.h @@ -16,14 +16,16 @@ typedef struct DisasContext { struct TranslationBlock *tb; int singlestep_enabled; int thumb; - int bswap_code; + int sctlr_b; + TCGMemOp be_data; #if !defined(CONFIG_USER_ONLY) int user; #endif ARMMMUIdx mmu_idx; /* MMU index to use for normal loads/stores */ bool ns; /* Use non-secure CPREG bank on access */ int fp_excp_el; /* FP exception EL or 0 if enabled */ - bool el3_is_aa64; /* Flag indicating whether EL3 is AArch64 or not */ + /* Flag indicating that exceptions from secure mode are routed to EL3. */ + bool secure_routed_to_el3; bool vfp_enabled; /* FP enabled via FPSCR.EN */ int vec_len; int vec_stride; @@ -62,7 +64,21 @@ typedef struct DisasContext { TCGv_i64 tmp_a64[TMP_A64_MAX]; } DisasContext; -extern TCGv_ptr cpu_env; +typedef struct DisasCompare { + TCGCond cond; + TCGv_i32 value; + bool value_global; +} DisasCompare; + +/* Share the TCG temporaries common between 32 and 64 bit modes. */ +extern TCGv_env cpu_env; +extern TCGv_i32 cpu_NF, cpu_ZF, cpu_CF, cpu_VF; +extern TCGv_i64 cpu_exclusive_addr; +extern TCGv_i64 cpu_exclusive_val; +#ifdef CONFIG_USER_ONLY +extern TCGv_i64 cpu_exclusive_test; +extern TCGv_i32 cpu_exclusive_info; +#endif static inline int arm_dc_feature(DisasContext *dc, int feature) { @@ -84,7 +100,7 @@ static inline int default_exception_el(DisasContext *s) * exceptions can only be routed to ELs above 1, so we target the higher of * 1 or the current EL. */ - return (s->mmu_idx == ARMMMUIdx_S1SE0 && !s->el3_is_aa64) + return (s->mmu_idx == ARMMMUIdx_S1SE0 && s->secure_routed_to_el3) ? 3 : MAX(1, s->current_el); } @@ -107,9 +123,7 @@ static inline int default_exception_el(DisasContext *s) #ifdef TARGET_AARCH64 void a64_translate_init(void); -void gen_intermediate_code_internal_a64(ARMCPU *cpu, - TranslationBlock *tb, - bool search_pc); +void gen_intermediate_code_a64(ARMCPU *cpu, TranslationBlock *tb); void gen_a64_set_pc_im(uint64_t val); void aarch64_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int flags); @@ -118,9 +132,7 @@ static inline void a64_translate_init(void) { } -static inline void gen_intermediate_code_internal_a64(ARMCPU *cpu, - TranslationBlock *tb, - bool search_pc) +static inline void gen_intermediate_code_a64(ARMCPU *cpu, TranslationBlock *tb) { } @@ -135,6 +147,9 @@ static inline void aarch64_cpu_dump_state(CPUState *cs, FILE *f, } #endif +void arm_test_cc(DisasCompare *cmp, int cc); +void arm_free_cc(DisasCompare *cmp); +void arm_jump_cc(DisasCompare *cmp, TCGLabel *label); void arm_gen_test_cc(int cc, TCGLabel *label); #endif /* TARGET_ARM_TRANSLATE_H */ diff --git a/qemu/target-cris/cpu-qom.h b/qemu/target-cris/cpu-qom.h index 6fc30c208..df4c0b50a 100644 --- a/qemu/target-cris/cpu-qom.h +++ b/qemu/target-cris/cpu-qom.h @@ -73,6 +73,10 @@ static inline CRISCPU *cris_env_get_cpu(CPUCRISState *env) #define ENV_OFFSET offsetof(CRISCPU, env) +#ifndef CONFIG_USER_ONLY +extern const struct VMStateDescription vmstate_cris_cpu; +#endif + void cris_cpu_do_interrupt(CPUState *cpu); void crisv10_cpu_do_interrupt(CPUState *cpu); bool cris_cpu_exec_interrupt(CPUState *cpu, int int_req); diff --git a/qemu/target-cris/cpu.c b/qemu/target-cris/cpu.c index b17e849e2..1cb79dd97 100644 --- a/qemu/target-cris/cpu.c +++ b/qemu/target-cris/cpu.c @@ -21,6 +21,8 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "qemu-common.h" #include "mmu.h" @@ -302,12 +304,20 @@ static void cris_cpu_class_init(ObjectClass *oc, void *data) cc->handle_mmu_fault = cris_cpu_handle_mmu_fault; #else cc->get_phys_page_debug = cris_cpu_get_phys_page_debug; + dc->vmsd = &vmstate_cris_cpu; #endif cc->gdb_num_core_regs = 49; cc->gdb_stop_before_watchpoint = true; cc->disas_set_info = cris_disas_set_info; + + /* + * Reason: cris_cpu_initfn() calls cpu_exec_init(), which saves + * the object in cpus -> dangling pointer after final + * object_unref(). + */ + dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo cris_cpu_type_info = { diff --git a/qemu/target-cris/cpu.h b/qemu/target-cris/cpu.h index d422e3571..415cf9143 100644 --- a/qemu/target-cris/cpu.h +++ b/qemu/target-cris/cpu.h @@ -20,7 +20,6 @@ #ifndef CPU_CRIS_H #define CPU_CRIS_H -#include "config.h" #include "qemu-common.h" #define TARGET_LONG_BITS 32 @@ -29,8 +28,6 @@ #include "exec/cpu-defs.h" -#define ELF_MACHINE EM_CRIS - #define EXCP_NMI 1 #define EXCP_GURU 2 #define EXCP_BUSFAULT 3 @@ -108,6 +105,11 @@ #define NB_MMU_MODES 2 +typedef struct { + uint32_t hi; + uint32_t lo; +} TLBSet; + typedef struct CPUCRISState { uint32_t regs[16]; /* P0 - P15 are referred to as special registers in the docs. */ @@ -151,7 +153,7 @@ typedef struct CPUCRISState { uint32_t sregs[4][16]; /* Linear feedback shift reg in the mmu. Used to provide pseudo - randomness for the 'hint' the mmu gives to sw for chosing valid + randomness for the 'hint' the mmu gives to sw for choosing valid sets on TLB refills. */ uint32_t mmu_rand_lfsr; @@ -161,11 +163,7 @@ typedef struct CPUCRISState { * * One for I and another for D. */ - struct - { - uint32_t hi; - uint32_t lo; - } tlbsets[2][4][16]; + TLBSet tlbsets[2][4][16]; CPU_COMMON @@ -224,16 +222,13 @@ enum { #define cpu_init(cpu_model) CPU(cpu_cris_init(cpu_model)) #define cpu_exec cpu_cris_exec -#define cpu_gen_code cpu_cris_gen_code #define cpu_signal_handler cpu_cris_signal_handler -#define CPU_SAVE_VERSION 1 - /* MMU modes definitions */ #define MMU_MODE0_SUFFIX _kernel #define MMU_MODE1_SUFFIX _user #define MMU_USER_IDX 1 -static inline int cpu_mmu_index (CPUCRISState *env) +static inline int cpu_mmu_index (CPUCRISState *env, bool ifetch) { return !!(env->pregs[PR_CCS] & U_FLAG); } diff --git a/qemu/target-cris/gdbstub.c b/qemu/target-cris/gdbstub.c index 5db3683ab..1bbf17b04 100644 --- a/qemu/target-cris/gdbstub.c +++ b/qemu/target-cris/gdbstub.c @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "exec/gdbstub.h" diff --git a/qemu/target-cris/helper.c b/qemu/target-cris/helper.c index df6c9fdcb..1eb9fd918 100644 --- a/qemu/target-cris/helper.c +++ b/qemu/target-cris/helper.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "mmu.h" #include "qemu/host-utils.h" diff --git a/qemu/target-cris/helper.h b/qemu/target-cris/helper.h index 0b383b25a..ff3595641 100644 --- a/qemu/target-cris/helper.h +++ b/qemu/target-cris/helper.h @@ -1,7 +1,6 @@ DEF_HELPER_2(raise_exception, void, env, i32) DEF_HELPER_2(tlb_flush_pid, void, env, i32) DEF_HELPER_2(spc_write, void, env, i32) -DEF_HELPER_3(dump, void, i32, i32, i32) DEF_HELPER_1(rfe, void, env) DEF_HELPER_1(rfn, void, env) diff --git a/qemu/target-cris/machine.c b/qemu/target-cris/machine.c index 8f9c0dd59..9cc2820e8 100644 --- a/qemu/target-cris/machine.c +++ b/qemu/target-cris/machine.c @@ -1,90 +1,92 @@ -#include "hw/hw.h" -#include "hw/boards.h" - -void cpu_save(QEMUFile *f, void *opaque) -{ - CPUCRISState *env = opaque; - int i; - int s; - int mmu; - - for (i = 0; i < 16; i++) - qemu_put_be32(f, env->regs[i]); - for (i = 0; i < 16; i++) - qemu_put_be32(f, env->pregs[i]); - - qemu_put_be32(f, env->pc); - qemu_put_be32(f, env->ksp); +/* + * CRIS virtual CPU state save/load support + * + * Copyright (c) 2012 Red Hat, Inc. + * Written by Juan Quintela + * + * 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 + * 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 . + */ - qemu_put_be32(f, env->dslot); - qemu_put_be32(f, env->btaken); - qemu_put_be32(f, env->btarget); - - qemu_put_be32(f, env->cc_op); - qemu_put_be32(f, env->cc_mask); - qemu_put_be32(f, env->cc_dest); - qemu_put_be32(f, env->cc_src); - qemu_put_be32(f, env->cc_result); - qemu_put_be32(f, env->cc_size); - qemu_put_be32(f, env->cc_x); - - for (s = 0; s < 4; s++) { - for (i = 0; i < 16; i++) - qemu_put_be32(f, env->sregs[s][i]); - } +#include "qemu/osdep.h" +#include "hw/hw.h" - qemu_put_be32(f, env->mmu_rand_lfsr); - for (mmu = 0; mmu < 2; mmu++) { - for (s = 0; s < 4; s++) { - for (i = 0; i < 16; i++) { - qemu_put_be32(f, env->tlbsets[mmu][s][i].lo); - qemu_put_be32(f, env->tlbsets[mmu][s][i].hi); - } - } +static const VMStateDescription vmstate_tlbset = { + .name = "cpu/tlbset", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(lo, TLBSet), + VMSTATE_UINT32(hi, TLBSet), + VMSTATE_END_OF_LIST() } -} - -int cpu_load(QEMUFile *f, void *opaque, int version_id) -{ - CPUCRISState *env = opaque; - int i; - int s; - int mmu; - - for (i = 0; i < 16; i++) - env->regs[i] = qemu_get_be32(f); - for (i = 0; i < 16; i++) - env->pregs[i] = qemu_get_be32(f); - - env->pc = qemu_get_be32(f); - env->ksp = qemu_get_be32(f); +}; - env->dslot = qemu_get_be32(f); - env->btaken = qemu_get_be32(f); - env->btarget = qemu_get_be32(f); - - env->cc_op = qemu_get_be32(f); - env->cc_mask = qemu_get_be32(f); - env->cc_dest = qemu_get_be32(f); - env->cc_src = qemu_get_be32(f); - env->cc_result = qemu_get_be32(f); - env->cc_size = qemu_get_be32(f); - env->cc_x = qemu_get_be32(f); - - for (s = 0; s < 4; s++) { - for (i = 0; i < 16; i++) - env->sregs[s][i] = qemu_get_be32(f); +static const VMStateDescription vmstate_cris_env = { + .name = "env", + .version_id = 2, + .minimum_version_id = 2, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, CPUCRISState, 16), + VMSTATE_UINT32_ARRAY(pregs, CPUCRISState, 16), + VMSTATE_UINT32(pc, CPUCRISState), + VMSTATE_UINT32(ksp, CPUCRISState), + VMSTATE_INT32(dslot, CPUCRISState), + VMSTATE_INT32(btaken, CPUCRISState), + VMSTATE_UINT32(btarget, CPUCRISState), + VMSTATE_UINT32(cc_op, CPUCRISState), + VMSTATE_UINT32(cc_mask, CPUCRISState), + VMSTATE_UINT32(cc_dest, CPUCRISState), + VMSTATE_UINT32(cc_src, CPUCRISState), + VMSTATE_UINT32(cc_result, CPUCRISState), + VMSTATE_INT32(cc_size, CPUCRISState), + VMSTATE_INT32(cc_x, CPUCRISState), + VMSTATE_INT32(locked_irq, CPUCRISState), + VMSTATE_INT32(interrupt_vector, CPUCRISState), + VMSTATE_INT32(fault_vector, CPUCRISState), + VMSTATE_INT32(trap_vector, CPUCRISState), + VMSTATE_UINT32_ARRAY(sregs[0], CPUCRISState, 16), + VMSTATE_UINT32_ARRAY(sregs[1], CPUCRISState, 16), + VMSTATE_UINT32_ARRAY(sregs[2], CPUCRISState, 16), + VMSTATE_UINT32_ARRAY(sregs[3], CPUCRISState, 16), + VMSTATE_UINT32(mmu_rand_lfsr, CPUCRISState), + VMSTATE_STRUCT_ARRAY(tlbsets[0][0], CPUCRISState, 16, 0, + vmstate_tlbset, TLBSet), + VMSTATE_STRUCT_ARRAY(tlbsets[0][1], CPUCRISState, 16, 0, + vmstate_tlbset, TLBSet), + VMSTATE_STRUCT_ARRAY(tlbsets[0][2], CPUCRISState, 16, 0, + vmstate_tlbset, TLBSet), + VMSTATE_STRUCT_ARRAY(tlbsets[0][3], CPUCRISState, 16, 0, + vmstate_tlbset, TLBSet), + VMSTATE_STRUCT_ARRAY(tlbsets[1][0], CPUCRISState, 16, 0, + vmstate_tlbset, TLBSet), + VMSTATE_STRUCT_ARRAY(tlbsets[1][1], CPUCRISState, 16, 0, + vmstate_tlbset, TLBSet), + VMSTATE_STRUCT_ARRAY(tlbsets[1][2], CPUCRISState, 16, 0, + vmstate_tlbset, TLBSet), + VMSTATE_STRUCT_ARRAY(tlbsets[1][3], CPUCRISState, 16, 0, + vmstate_tlbset, TLBSet), + VMSTATE_END_OF_LIST() } +}; - env->mmu_rand_lfsr = qemu_get_be32(f); - for (mmu = 0; mmu < 2; mmu++) { - for (s = 0; s < 4; s++) { - for (i = 0; i < 16; i++) { - env->tlbsets[mmu][s][i].lo = qemu_get_be32(f); - env->tlbsets[mmu][s][i].hi = qemu_get_be32(f); - } - } +const VMStateDescription vmstate_cris_cpu = { + .name = "cpu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_CPU(), + VMSTATE_STRUCT(env, CRISCPU, 1, vmstate_cris_env, CPUCRISState), + VMSTATE_END_OF_LIST() } - - return 0; -} +}; diff --git a/qemu/target-cris/mmu.c b/qemu/target-cris/mmu.c index 1c95a415f..4278d2dce 100644 --- a/qemu/target-cris/mmu.c +++ b/qemu/target-cris/mmu.c @@ -18,8 +18,7 @@ * License along with this library; if not, see . */ -#ifndef CONFIG_USER_ONLY - +#include "qemu/osdep.h" #include "cpu.h" #include "mmu.h" @@ -360,4 +359,3 @@ int cris_mmu_translate(struct cris_mmu_result *res, env->pregs[PR_SRS] = old_srs; return miss; } -#endif diff --git a/qemu/target-cris/op_helper.c b/qemu/target-cris/op_helper.c index 5c0c14d99..320f2b80d 100644 --- a/qemu/target-cris/op_helper.c +++ b/qemu/target-cris/op_helper.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "mmu.h" #include "exec/helper-proto.h" @@ -91,11 +92,6 @@ void helper_spc_write(CPUCRISState *env, uint32_t new_spc) #endif } -void helper_dump(uint32_t a0, uint32_t a1, uint32_t a2) -{ - qemu_log("%s: a0=%x a1=%x\n", __func__, a0, a1); -} - /* Used by the tlb decoder. */ #define EXTRACT_FIELD(src, start, end) \ (((src) >> start) & ((1 << (end - start + 1)) - 1)) diff --git a/qemu/target-cris/translate.c b/qemu/target-cris/translate.c index 3e59601eb..a73176c11 100644 --- a/qemu/target-cris/translate.c +++ b/qemu/target-cris/translate.c @@ -23,6 +23,7 @@ * The condition code translation is in need of attention. */ +#include "qemu/osdep.h" #include "cpu.h" #include "disas/disas.h" #include "tcg-op.h" @@ -34,6 +35,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" #define DISAS_CRIS 0 @@ -58,7 +60,7 @@ #define CC_MASK_NZVC 0xf #define CC_MASK_RNZV 0x10e -static TCGv_ptr cpu_env; +static TCGv_env cpu_env; static TCGv cpu_R[16]; static TCGv cpu_PR[16]; static TCGv cc_x; @@ -102,9 +104,9 @@ typedef struct DisasContext { int cc_size_uptodate; /* -1 invalid or last written value. */ - int cc_x_uptodate; /* 1 - ccs, 2 - known | X_FLAG. 0 not uptodate. */ - int flags_uptodate; /* Wether or not $ccs is uptodate. */ - int flagx_known; /* Wether or not flags_x has the x flag known at + int cc_x_uptodate; /* 1 - ccs, 2 - known | X_FLAG. 0 not up-to-date. */ + int flags_uptodate; /* Whether or not $ccs is up-to-date. */ + int flagx_known; /* Whether or not flags_x has the x flag known at translation time. */ int flags_x; @@ -130,8 +132,10 @@ typedef struct DisasContext { static void gen_BUG(DisasContext *dc, const char *file, int line) { - printf("BUG: pc=%x %s %d\n", dc->pc, file, line); - qemu_log("BUG: pc=%x %s %d\n", dc->pc, file, line); + fprintf(stderr, "BUG: pc=%x %s %d\n", dc->pc, file, line); + if (qemu_log_separate()) { + qemu_log("BUG: pc=%x %s %d\n", dc->pc, file, line); + } cpu_abort(CPU(dc->cpu), "%s:%d\n", file, line); } @@ -311,7 +315,7 @@ static void t_gen_asr(TCGv d, TCGv a, TCGv b) static void t_gen_cris_dstep(TCGv d, TCGv a, TCGv b) { - TCGLabel *l1 = gen_new_label(); + TCGv t = tcg_temp_new(); /* * d <<= 1 @@ -319,9 +323,9 @@ static void t_gen_cris_dstep(TCGv d, TCGv a, TCGv b) * d -= s; */ tcg_gen_shli_tl(d, a, 1); - tcg_gen_brcond_tl(TCG_COND_LTU, d, b, l1); - tcg_gen_sub_tl(d, d, b); - gen_set_label(l1); + tcg_gen_sub_tl(t, d, b); + tcg_gen_movcond_tl(TCG_COND_GEU, d, d, b, t, d); + tcg_temp_free(t); } static void t_gen_cris_mstep(TCGv d, TCGv a, TCGv b, TCGv ccs) @@ -769,13 +773,7 @@ static void cris_alu_op_exec(DisasContext *dc, int op, t_gen_cris_mstep(dst, a, b, cpu_PR[PR_CCS]); break; case CC_OP_BOUND: - { - TCGLabel *l1 = gen_new_label(); - tcg_gen_mov_tl(dst, a); - tcg_gen_brcond_tl(TCG_COND_LEU, a, b, l1); - tcg_gen_mov_tl(dst, b); - gen_set_label(l1); - } + tcg_gen_movcond_tl(TCG_COND_LEU, dst, a, b, a, b); break; case CC_OP_CMP: tcg_gen_sub_tl(dst, a, b); @@ -783,7 +781,7 @@ static void cris_alu_op_exec(DisasContext *dc, int op, t_gen_subx_carry(dc, dst); break; default: - qemu_log("illegal ALU op.\n"); + qemu_log_mask(LOG_GUEST_ERROR, "illegal ALU op.\n"); BUG(); break; } @@ -1089,7 +1087,7 @@ static inline void cris_prepare_jmp (DisasContext *dc, unsigned int type) static void gen_load64(DisasContext *dc, TCGv_i64 dst, TCGv addr) { - int mem_index = cpu_mmu_index(&dc->cpu->env); + int mem_index = cpu_mmu_index(&dc->cpu->env, false); /* If we get a fault on a delayslot we must keep the jmp state in the cpu-state to be able to re-execute the jmp. */ @@ -1103,7 +1101,7 @@ static void gen_load64(DisasContext *dc, TCGv_i64 dst, TCGv addr) static void gen_load(DisasContext *dc, TCGv dst, TCGv addr, unsigned int size, int sign) { - int mem_index = cpu_mmu_index(&dc->cpu->env); + int mem_index = cpu_mmu_index(&dc->cpu->env, false); /* If we get a fault on a delayslot we must keep the jmp state in the cpu-state to be able to re-execute the jmp. */ @@ -1118,7 +1116,7 @@ static void gen_load(DisasContext *dc, TCGv dst, TCGv addr, static void gen_store (DisasContext *dc, TCGv addr, TCGv val, unsigned int size) { - int mem_index = cpu_mmu_index(&dc->cpu->env); + int mem_index = cpu_mmu_index(&dc->cpu->env, false); /* If we get a fault on a delayslot we must keep the jmp state in the cpu-state to be able to re-execute the jmp. */ @@ -1482,15 +1480,8 @@ static int dec_scc_r(CPUCRISState *env, DisasContext *dc) LOG_DIS("s%s $r%u\n", cc_name(cond), dc->op1); - if (cond != CC_A) { - TCGLabel *l1 = gen_new_label(); - gen_tst_cc(dc, cpu_R[dc->op1], cond); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_R[dc->op1], 0, l1); - tcg_gen_movi_tl(cpu_R[dc->op1], 1); - gen_set_label(l1); - } else { - tcg_gen_movi_tl(cpu_R[dc->op1], 1); - } + gen_tst_cc(dc, cpu_R[dc->op1], cond); + tcg_gen_setcondi_tl(TCG_COND_NE, cpu_R[dc->op1], cpu_R[dc->op1], 0); cris_cc_mask(dc, 0); return 2; @@ -2604,9 +2595,9 @@ static int dec_movem_mr(CPUCRISState *env, DisasContext *dc) tcg_temp_free(addr); for (i = 0; i < (nr >> 1); i++) { - tcg_gen_trunc_i64_i32(cpu_R[i * 2], tmp[i]); + tcg_gen_extrl_i64_i32(cpu_R[i * 2], tmp[i]); tcg_gen_shri_i64(tmp[i], tmp[i], 32); - tcg_gen_trunc_i64_i32(cpu_R[i * 2 + 1], tmp[i]); + tcg_gen_extrl_i64_i32(cpu_R[i * 2 + 1], tmp[i]); tcg_temp_free_i64(tmp[i]); } if (nr & 1) { @@ -3007,10 +2998,6 @@ static unsigned int crisv32_decoder(CPUCRISState *env, DisasContext *dc) int insn_len = 2; int i; - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { - tcg_gen_debug_insn_start(dc->pc); - } - /* Load a halfword onto the instruction register. */ dc->ir = cris_fetch(env, dc, dc->pc, 2, 0); @@ -3047,23 +3034,6 @@ static unsigned int crisv32_decoder(CPUCRISState *env, DisasContext *dc) return insn_len; } -static void check_breakpoint(CPUCRISState *env, DisasContext *dc) -{ - CPUState *cs = CPU(cris_env_get_cpu(env)); - CPUBreakpoint *bp; - - if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { - QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - if (bp->pc == dc->pc) { - cris_evaluate_flags(dc); - tcg_gen_movi_tl(env_pc, dc->pc); - t_gen_raise_exception(EXCP_DEBUG); - dc->is_jmp = DISAS_UPDATE; - } - } - } -} - #include "translate_v10.c" /* @@ -3101,15 +3071,12 @@ static void check_breakpoint(CPUCRISState *env, DisasContext *dc) */ /* generate intermediate code for basic block 'tb'. */ -static inline void -gen_intermediate_code_internal(CRISCPU *cpu, TranslationBlock *tb, - bool search_pc) +void gen_intermediate_code(CPUCRISState *env, struct TranslationBlock *tb) { + CRISCPU *cpu = cris_env_get_cpu(env); CPUState *cs = CPU(cpu); - CPUCRISState *env = &cpu->env; uint32_t pc_start; unsigned int insn_len; - int j, lj; struct DisasContext ctx; struct DisasContext *dc = &ctx; uint32_t next_page_start; @@ -3161,13 +3128,13 @@ gen_intermediate_code_internal(CRISCPU *cpu, TranslationBlock *tb, if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { qemu_log( - "srch=%d pc=%x %x flg=%" PRIx64 " bt=%x ds=%u ccs=%x\n" + "pc=%x %x flg=%" PRIx64 " bt=%x ds=%u ccs=%x\n" "pid=%x usp=%x\n" "%x.%x.%x.%x\n" "%x.%x.%x.%x\n" "%x.%x.%x.%x\n" "%x.%x.%x.%x\n", - search_pc, dc->pc, dc->ppc, + dc->pc, dc->ppc, (uint64_t)tb->flags, env->btarget, (unsigned)tb->flags & 7, env->pregs[PR_CCS], @@ -3183,38 +3150,38 @@ gen_intermediate_code_internal(CRISCPU *cpu, TranslationBlock *tb, } next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; - lj = -1; num_insns = 0; max_insns = tb->cflags & CF_COUNT_MASK; if (max_insns == 0) { max_insns = CF_COUNT_MASK; } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } gen_tb_start(tb); do { - check_breakpoint(env, dc); - - if (search_pc) { - j = tcg_op_buf_count(); - if (lj < j) { - lj++; - while (lj < j) { - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - } - if (dc->delayed_branch == 1) { - tcg_ctx.gen_opc_pc[lj] = dc->ppc | 1; - } else { - tcg_ctx.gen_opc_pc[lj] = dc->pc; - } - tcg_ctx.gen_opc_instr_start[lj] = 1; - tcg_ctx.gen_opc_icount[lj] = num_insns; + tcg_gen_insn_start(dc->delayed_branch == 1 + ? dc->ppc | 1 : dc->pc); + num_insns++; + + if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { + cris_evaluate_flags(dc); + tcg_gen_movi_tl(env_pc, dc->pc); + t_gen_raise_exception(EXCP_DEBUG); + dc->is_jmp = DISAS_UPDATE; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + dc->pc += 2; + break; } /* Pretty disas. */ LOG_DIS("%8.8x:\t", dc->pc); - if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) { + if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { gen_io_start(); } dc->clear_x = 1; @@ -3226,7 +3193,6 @@ gen_intermediate_code_internal(CRISCPU *cpu, TranslationBlock *tb, cris_clear_x_flag(dc); } - num_insns++; /* Check for delayed branches here. If we do it before actually generating any host code, the simulator will just loop doing nothing for on this program location. */ @@ -3331,16 +3297,8 @@ gen_intermediate_code_internal(CRISCPU *cpu, TranslationBlock *tb, } gen_tb_end(tb, num_insns); - if (search_pc) { - j = tcg_op_buf_count(); - lj++; - while (lj <= j) { - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - } else { - tb->size = dc->pc - pc_start; - tb->icount = num_insns; - } + tb->size = dc->pc - pc_start; + tb->icount = num_insns; #ifdef DEBUG_DISAS #if !DISAS_CRIS @@ -3354,16 +3312,6 @@ gen_intermediate_code_internal(CRISCPU *cpu, TranslationBlock *tb, #endif } -void gen_intermediate_code (CPUCRISState *env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(cris_env_get_cpu(env), tb, false); -} - -void gen_intermediate_code_pc (CPUCRISState *env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(cris_env_get_cpu(env), tb, true); -} - void cris_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int flags) { @@ -3416,47 +3364,48 @@ void cris_initialize_tcg(void) int i; cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); - cc_x = tcg_global_mem_new(TCG_AREG0, + cc_x = tcg_global_mem_new(cpu_env, offsetof(CPUCRISState, cc_x), "cc_x"); - cc_src = tcg_global_mem_new(TCG_AREG0, + cc_src = tcg_global_mem_new(cpu_env, offsetof(CPUCRISState, cc_src), "cc_src"); - cc_dest = tcg_global_mem_new(TCG_AREG0, + cc_dest = tcg_global_mem_new(cpu_env, offsetof(CPUCRISState, cc_dest), "cc_dest"); - cc_result = tcg_global_mem_new(TCG_AREG0, + cc_result = tcg_global_mem_new(cpu_env, offsetof(CPUCRISState, cc_result), "cc_result"); - cc_op = tcg_global_mem_new(TCG_AREG0, + cc_op = tcg_global_mem_new(cpu_env, offsetof(CPUCRISState, cc_op), "cc_op"); - cc_size = tcg_global_mem_new(TCG_AREG0, + cc_size = tcg_global_mem_new(cpu_env, offsetof(CPUCRISState, cc_size), "cc_size"); - cc_mask = tcg_global_mem_new(TCG_AREG0, + cc_mask = tcg_global_mem_new(cpu_env, offsetof(CPUCRISState, cc_mask), "cc_mask"); - env_pc = tcg_global_mem_new(TCG_AREG0, + env_pc = tcg_global_mem_new(cpu_env, offsetof(CPUCRISState, pc), "pc"); - env_btarget = tcg_global_mem_new(TCG_AREG0, + env_btarget = tcg_global_mem_new(cpu_env, offsetof(CPUCRISState, btarget), "btarget"); - env_btaken = tcg_global_mem_new(TCG_AREG0, + env_btaken = tcg_global_mem_new(cpu_env, offsetof(CPUCRISState, btaken), "btaken"); for (i = 0; i < 16; i++) { - cpu_R[i] = tcg_global_mem_new(TCG_AREG0, + cpu_R[i] = tcg_global_mem_new(cpu_env, offsetof(CPUCRISState, regs[i]), regnames[i]); } for (i = 0; i < 16; i++) { - cpu_PR[i] = tcg_global_mem_new(TCG_AREG0, + cpu_PR[i] = tcg_global_mem_new(cpu_env, offsetof(CPUCRISState, pregs[i]), pregnames[i]); } } -void restore_state_to_opc(CPUCRISState *env, TranslationBlock *tb, int pc_pos) +void restore_state_to_opc(CPUCRISState *env, TranslationBlock *tb, + target_ulong *data) { - env->pc = tcg_ctx.gen_opc_pc[pc_pos]; + env->pc = data[0]; } diff --git a/qemu/target-cris/translate_v10.c b/qemu/target-cris/translate_v10.c index b742c4cd0..7607eadfb 100644 --- a/qemu/target-cris/translate_v10.c +++ b/qemu/target-cris/translate_v10.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "crisv10-decode.h" static const char *regnames_v10[] = @@ -58,7 +59,7 @@ static inline int dec10_size(unsigned int size) static inline void cris_illegal_insn(DisasContext *dc) { - qemu_log("illegal insn at pc=%x\n", dc->pc); + qemu_log_mask(LOG_GUEST_ERROR, "illegal insn at pc=%x\n", dc->pc); t_gen_raise_exception(EXCP_BREAK); } @@ -96,7 +97,7 @@ static void gen_store_v10_conditional(DisasContext *dc, TCGv addr, TCGv val, static void gen_store_v10(DisasContext *dc, TCGv addr, TCGv val, unsigned int size) { - int mem_index = cpu_mmu_index(&dc->cpu->env); + int mem_index = cpu_mmu_index(&dc->cpu->env, false); /* If we get a fault on a delayslot we must keep the jmp state in the cpu-state to be able to re-execute the jmp. */ @@ -535,16 +536,8 @@ static void dec10_reg_scc(DisasContext *dc) LOG_DIS("s%s $r%u\n", cc_name(cond), dc->src); - if (cond != CC_A) - { - TCGLabel *l1 = gen_new_label(); - gen_tst_cc (dc, cpu_R[dc->src], cond); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_R[dc->src], 0, l1); - tcg_gen_movi_tl(cpu_R[dc->src], 1); - gen_set_label(l1); - } else { - tcg_gen_movi_tl(cpu_R[dc->src], 1); - } + gen_tst_cc(dc, cpu_R[dc->src], cond); + tcg_gen_setcondi_tl(TCG_COND_NE, cpu_R[dc->src], cpu_R[dc->src], 0); cris_cc_mask(dc, 0); } @@ -1207,9 +1200,6 @@ static unsigned int crisv10_decoder(CPUCRISState *env, DisasContext *dc) { unsigned int insn_len = 2; - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) - tcg_gen_debug_insn_start(dc->pc); - /* Load a halfword onto the instruction register. */ dc->ir = cpu_lduw_code(env, dc->pc); @@ -1257,45 +1247,45 @@ static unsigned int crisv10_decoder(CPUCRISState *env, DisasContext *dc) void cris_initialize_crisv10_tcg(void) { - int i; - - cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); - cc_x = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUCRISState, cc_x), "cc_x"); - cc_src = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUCRISState, cc_src), "cc_src"); - cc_dest = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUCRISState, cc_dest), - "cc_dest"); - cc_result = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUCRISState, cc_result), - "cc_result"); - cc_op = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUCRISState, cc_op), "cc_op"); - cc_size = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUCRISState, cc_size), - "cc_size"); - cc_mask = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUCRISState, cc_mask), - "cc_mask"); - - env_pc = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUCRISState, pc), - "pc"); - env_btarget = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUCRISState, btarget), - "btarget"); - env_btaken = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUCRISState, btaken), - "btaken"); - for (i = 0; i < 16; i++) { - cpu_R[i] = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUCRISState, regs[i]), - regnames_v10[i]); - } - for (i = 0; i < 16; i++) { - cpu_PR[i] = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUCRISState, pregs[i]), - pregnames_v10[i]); - } + int i; + + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + cc_x = tcg_global_mem_new(cpu_env, + offsetof(CPUCRISState, cc_x), "cc_x"); + cc_src = tcg_global_mem_new(cpu_env, + offsetof(CPUCRISState, cc_src), "cc_src"); + cc_dest = tcg_global_mem_new(cpu_env, + offsetof(CPUCRISState, cc_dest), + "cc_dest"); + cc_result = tcg_global_mem_new(cpu_env, + offsetof(CPUCRISState, cc_result), + "cc_result"); + cc_op = tcg_global_mem_new(cpu_env, + offsetof(CPUCRISState, cc_op), "cc_op"); + cc_size = tcg_global_mem_new(cpu_env, + offsetof(CPUCRISState, cc_size), + "cc_size"); + cc_mask = tcg_global_mem_new(cpu_env, + offsetof(CPUCRISState, cc_mask), + "cc_mask"); + + env_pc = tcg_global_mem_new(cpu_env, + offsetof(CPUCRISState, pc), + "pc"); + env_btarget = tcg_global_mem_new(cpu_env, + offsetof(CPUCRISState, btarget), + "btarget"); + env_btaken = tcg_global_mem_new(cpu_env, + offsetof(CPUCRISState, btaken), + "btaken"); + for (i = 0; i < 16; i++) { + cpu_R[i] = tcg_global_mem_new(cpu_env, + offsetof(CPUCRISState, regs[i]), + regnames_v10[i]); + } + for (i = 0; i < 16; i++) { + cpu_PR[i] = tcg_global_mem_new(cpu_env, + offsetof(CPUCRISState, pregs[i]), + pregnames_v10[i]); + } } diff --git a/qemu/target-i386/Makefile.objs b/qemu/target-i386/Makefile.objs index 7a1df2c98..b223d7932 100644 --- a/qemu/target-i386/Makefile.objs +++ b/qemu/target-i386/Makefile.objs @@ -1,7 +1,7 @@ -obj-y += translate.o helper.o cpu.o +obj-y += translate.o helper.o cpu.o bpt_helper.o obj-y += excp_helper.o fpu_helper.o cc_helper.o int_helper.o svm_helper.o -obj-y += smm_helper.o misc_helper.o mem_helper.o seg_helper.o +obj-y += smm_helper.o misc_helper.o mem_helper.o seg_helper.o mpx_helper.o obj-y += gdbstub.o -obj-$(CONFIG_SOFTMMU) += machine.o arch_memory_mapping.o arch_dump.o -obj-$(CONFIG_KVM) += kvm.o +obj-$(CONFIG_SOFTMMU) += machine.o arch_memory_mapping.o arch_dump.o monitor.o +obj-$(CONFIG_KVM) += kvm.o hyperv.o obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o diff --git a/qemu/target-i386/arch_dump.c b/qemu/target-i386/arch_dump.c index eccd8031a..5a2e4be5d 100644 --- a/qemu/target-i386/arch_dump.c +++ b/qemu/target-i386/arch_dump.c @@ -11,6 +11,7 @@ * */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/cpu-all.h" #include "sysemu/dump.h" diff --git a/qemu/target-i386/arch_memory_mapping.c b/qemu/target-i386/arch_memory_mapping.c index 01563fecc..88f341e1b 100644 --- a/qemu/target-i386/arch_memory_mapping.c +++ b/qemu/target-i386/arch_memory_mapping.c @@ -11,6 +11,7 @@ * */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/cpu-all.h" #include "sysemu/memory_mapping.h" diff --git a/qemu/target-i386/bpt_helper.c b/qemu/target-i386/bpt_helper.c new file mode 100644 index 000000000..f47df1998 --- /dev/null +++ b/qemu/target-i386/bpt_helper.c @@ -0,0 +1,327 @@ +/* + * i386 breakpoint helpers + * + * Copyright (c) 2003 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 . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" + + +#ifndef CONFIG_USER_ONLY +static inline bool hw_local_breakpoint_enabled(unsigned long dr7, int index) +{ + return (dr7 >> (index * 2)) & 1; +} + +static inline bool hw_global_breakpoint_enabled(unsigned long dr7, int index) +{ + return (dr7 >> (index * 2)) & 2; + +} +static inline bool hw_breakpoint_enabled(unsigned long dr7, int index) +{ + return hw_global_breakpoint_enabled(dr7, index) || + hw_local_breakpoint_enabled(dr7, index); +} + +static inline int hw_breakpoint_type(unsigned long dr7, int index) +{ + return (dr7 >> (DR7_TYPE_SHIFT + (index * 4))) & 3; +} + +static inline int hw_breakpoint_len(unsigned long dr7, int index) +{ + int len = ((dr7 >> (DR7_LEN_SHIFT + (index * 4))) & 3); + return (len == 2) ? 8 : len + 1; +} + +static int hw_breakpoint_insert(CPUX86State *env, int index) +{ + CPUState *cs = CPU(x86_env_get_cpu(env)); + target_ulong dr7 = env->dr[7]; + target_ulong drN = env->dr[index]; + int err = 0; + + switch (hw_breakpoint_type(dr7, index)) { + case DR7_TYPE_BP_INST: + if (hw_breakpoint_enabled(dr7, index)) { + err = cpu_breakpoint_insert(cs, drN, BP_CPU, + &env->cpu_breakpoint[index]); + } + break; + + case DR7_TYPE_IO_RW: + /* Notice when we should enable calls to bpt_io. */ + return hw_breakpoint_enabled(env->dr[7], index) + ? HF_IOBPT_MASK : 0; + + case DR7_TYPE_DATA_WR: + if (hw_breakpoint_enabled(dr7, index)) { + err = cpu_watchpoint_insert(cs, drN, + hw_breakpoint_len(dr7, index), + BP_CPU | BP_MEM_WRITE, + &env->cpu_watchpoint[index]); + } + break; + + case DR7_TYPE_DATA_RW: + if (hw_breakpoint_enabled(dr7, index)) { + err = cpu_watchpoint_insert(cs, drN, + hw_breakpoint_len(dr7, index), + BP_CPU | BP_MEM_ACCESS, + &env->cpu_watchpoint[index]); + } + break; + } + if (err) { + env->cpu_breakpoint[index] = NULL; + } + return 0; +} + +static void hw_breakpoint_remove(CPUX86State *env, int index) +{ + CPUState *cs = CPU(x86_env_get_cpu(env)); + + switch (hw_breakpoint_type(env->dr[7], index)) { + case DR7_TYPE_BP_INST: + if (env->cpu_breakpoint[index]) { + cpu_breakpoint_remove_by_ref(cs, env->cpu_breakpoint[index]); + env->cpu_breakpoint[index] = NULL; + } + break; + + case DR7_TYPE_DATA_WR: + case DR7_TYPE_DATA_RW: + if (env->cpu_breakpoint[index]) { + cpu_watchpoint_remove_by_ref(cs, env->cpu_watchpoint[index]); + env->cpu_breakpoint[index] = NULL; + } + break; + + case DR7_TYPE_IO_RW: + /* HF_IOBPT_MASK cleared elsewhere. */ + break; + } +} + +void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7) +{ + target_ulong old_dr7 = env->dr[7]; + int iobpt = 0; + int i; + + new_dr7 |= DR7_FIXED_1; + + /* If nothing is changing except the global/local enable bits, + then we can make the change more efficient. */ + if (((old_dr7 ^ new_dr7) & ~0xff) == 0) { + /* Fold the global and local enable bits together into the + global fields, then xor to show which registers have + changed collective enable state. */ + int mod = ((old_dr7 | old_dr7 * 2) ^ (new_dr7 | new_dr7 * 2)) & 0xff; + + for (i = 0; i < DR7_MAX_BP; i++) { + if ((mod & (2 << i * 2)) && !hw_breakpoint_enabled(new_dr7, i)) { + hw_breakpoint_remove(env, i); + } + } + env->dr[7] = new_dr7; + for (i = 0; i < DR7_MAX_BP; i++) { + if (mod & (2 << i * 2) && hw_breakpoint_enabled(new_dr7, i)) { + iobpt |= hw_breakpoint_insert(env, i); + } else if (hw_breakpoint_type(new_dr7, i) == DR7_TYPE_IO_RW + && hw_breakpoint_enabled(new_dr7, i)) { + iobpt |= HF_IOBPT_MASK; + } + } + } else { + for (i = 0; i < DR7_MAX_BP; i++) { + hw_breakpoint_remove(env, i); + } + env->dr[7] = new_dr7; + for (i = 0; i < DR7_MAX_BP; i++) { + iobpt |= hw_breakpoint_insert(env, i); + } + } + + env->hflags = (env->hflags & ~HF_IOBPT_MASK) | iobpt; +} + +static bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update) +{ + target_ulong dr6; + int reg; + bool hit_enabled = false; + + dr6 = env->dr[6] & ~0xf; + for (reg = 0; reg < DR7_MAX_BP; reg++) { + bool bp_match = false; + bool wp_match = false; + + switch (hw_breakpoint_type(env->dr[7], reg)) { + case DR7_TYPE_BP_INST: + if (env->dr[reg] == env->eip) { + bp_match = true; + } + break; + case DR7_TYPE_DATA_WR: + case DR7_TYPE_DATA_RW: + if (env->cpu_watchpoint[reg] && + env->cpu_watchpoint[reg]->flags & BP_WATCHPOINT_HIT) { + wp_match = true; + } + break; + case DR7_TYPE_IO_RW: + break; + } + if (bp_match || wp_match) { + dr6 |= 1 << reg; + if (hw_breakpoint_enabled(env->dr[7], reg)) { + hit_enabled = true; + } + } + } + + if (hit_enabled || force_dr6_update) { + env->dr[6] = dr6; + } + + return hit_enabled; +} + +void breakpoint_handler(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + CPUBreakpoint *bp; + + if (cs->watchpoint_hit) { + if (cs->watchpoint_hit->flags & BP_CPU) { + cs->watchpoint_hit = NULL; + if (check_hw_breakpoints(env, false)) { + raise_exception(env, EXCP01_DB); + } else { + cpu_resume_from_signal(cs, NULL); + } + } + } else { + QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { + if (bp->pc == env->eip) { + if (bp->flags & BP_CPU) { + check_hw_breakpoints(env, true); + raise_exception(env, EXCP01_DB); + } + break; + } + } + } +} +#endif + +void helper_single_step(CPUX86State *env) +{ +#ifndef CONFIG_USER_ONLY + check_hw_breakpoints(env, true); + env->dr[6] |= DR6_BS; +#endif + raise_exception(env, EXCP01_DB); +} + +void helper_set_dr(CPUX86State *env, int reg, target_ulong t0) +{ +#ifndef CONFIG_USER_ONLY + switch (reg) { + case 0: case 1: case 2: case 3: + if (hw_breakpoint_enabled(env->dr[7], reg) + && hw_breakpoint_type(env->dr[7], reg) != DR7_TYPE_IO_RW) { + hw_breakpoint_remove(env, reg); + env->dr[reg] = t0; + hw_breakpoint_insert(env, reg); + } else { + env->dr[reg] = t0; + } + return; + case 4: + if (env->cr[4] & CR4_DE_MASK) { + break; + } + /* fallthru */ + case 6: + env->dr[6] = t0 | DR6_FIXED_1; + return; + case 5: + if (env->cr[4] & CR4_DE_MASK) { + break; + } + /* fallthru */ + case 7: + cpu_x86_update_dr7(env, t0); + return; + } + raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); +#endif +} + +target_ulong helper_get_dr(CPUX86State *env, int reg) +{ + switch (reg) { + case 0: case 1: case 2: case 3: case 6: case 7: + return env->dr[reg]; + case 4: + if (env->cr[4] & CR4_DE_MASK) { + break; + } else { + return env->dr[6]; + } + case 5: + if (env->cr[4] & CR4_DE_MASK) { + break; + } else { + return env->dr[7]; + } + } + raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); +} + +/* Check if Port I/O is trapped by a breakpoint. */ +void helper_bpt_io(CPUX86State *env, uint32_t port, + uint32_t size, target_ulong next_eip) +{ +#ifndef CONFIG_USER_ONLY + target_ulong dr7 = env->dr[7]; + int i, hit = 0; + + for (i = 0; i < DR7_MAX_BP; ++i) { + if (hw_breakpoint_type(dr7, i) == DR7_TYPE_IO_RW + && hw_breakpoint_enabled(dr7, i)) { + int bpt_len = hw_breakpoint_len(dr7, i); + if (port + size - 1 >= env->dr[i] + && port <= env->dr[i] + bpt_len - 1) { + hit |= 1 << i; + } + } + } + + if (hit) { + env->dr[6] = (env->dr[6] & ~0xf) | hit; + env->eip = next_eip; + raise_exception(env, EXCP01_DB); + } +#endif +} diff --git a/qemu/target-i386/cc_helper.c b/qemu/target-i386/cc_helper.c index ecbf0ec09..83af223c9 100644 --- a/qemu/target-i386/cc_helper.c +++ b/qemu/target-i386/cc_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" @@ -378,17 +379,7 @@ void helper_sti_vm(CPUX86State *env) { env->eflags |= VIF_MASK; if (env->eflags & VIP_MASK) { - raise_exception(env, EXCP0D_GPF); + raise_exception_ra(env, EXCP0D_GPF, GETPC()); } } #endif - -void helper_set_inhibit_irq(CPUX86State *env) -{ - env->hflags |= HF_INHIBIT_IRQ_MASK; -} - -void helper_reset_inhibit_irq(CPUX86State *env) -{ - env->hflags &= ~HF_INHIBIT_IRQ_MASK; -} diff --git a/qemu/target-i386/cpu-qom.h b/qemu/target-i386/cpu-qom.h index 7a4fddd85..cb750176c 100644 --- a/qemu/target-i386/cpu-qom.h +++ b/qemu/target-i386/cpu-qom.h @@ -22,7 +22,6 @@ #include "qom/cpu.h" #include "cpu.h" -#include "qapi/error.h" #include "qemu/notify.h" #ifdef TARGET_X86_64 @@ -88,7 +87,14 @@ typedef struct X86CPU { bool hyperv_vapic; bool hyperv_relaxed_timing; int hyperv_spinlock_attempts; + char *hyperv_vendor_id; bool hyperv_time; + bool hyperv_crash; + bool hyperv_reset; + bool hyperv_vpindex; + bool hyperv_runtime; + bool hyperv_synic; + bool hyperv_stimer; bool check_cpuid; bool enforce_cpuid; bool expose_kvm; diff --git a/qemu/target-i386/cpu.c b/qemu/target-i386/cpu.c index 7a779b165..d0b5b6915 100644 --- a/qemu/target-i386/cpu.c +++ b/qemu/target-i386/cpu.c @@ -16,10 +16,8 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include -#include -#include -#include +#include "qemu/osdep.h" +#include "qemu/cutils.h" #include "cpu.h" #include "sysemu/kvm.h" @@ -43,7 +41,6 @@ #include "sysemu/sysemu.h" #include "hw/qdev-properties.h" -#include "hw/cpu/icc_bus.h" #ifndef CONFIG_USER_ONLY #include "exec/address-spaces.h" #include "hw/xen/xen.h" @@ -260,8 +257,19 @@ static const char *svm_feature_name[] = { static const char *cpuid_7_0_ebx_feature_name[] = { "fsgsbase", "tsc_adjust", NULL, "bmi1", "hle", "avx2", NULL, "smep", "bmi2", "erms", "invpcid", "rtm", NULL, NULL, "mpx", NULL, - "avx512f", NULL, "rdseed", "adx", "smap", NULL, NULL, NULL, - NULL, NULL, "avx512pf", "avx512er", "avx512cd", NULL, NULL, NULL, + "avx512f", NULL, "rdseed", "adx", "smap", NULL, "pcommit", "clflushopt", + "clwb", NULL, "avx512pf", "avx512er", "avx512cd", NULL, NULL, NULL, +}; + +static const char *cpuid_7_0_ecx_feature_name[] = { + NULL, NULL, NULL, "pku", + "ospke", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, }; static const char *cpuid_apm_edx_feature_name[] = { @@ -313,7 +321,7 @@ static const char *cpuid_6_feature_name[] = { CPUID_PAE | CPUID_MCE | CPUID_CX8 | CPUID_APIC | CPUID_SEP | \ CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV | CPUID_PAT | \ CPUID_PSE36 | CPUID_CLFLUSH | CPUID_ACPI | CPUID_MMX | \ - CPUID_FXSR | CPUID_SSE | CPUID_SSE2 | CPUID_SS) + CPUID_FXSR | CPUID_SSE | CPUID_SSE2 | CPUID_SS | CPUID_DE) /* partly implemented: CPUID_MTRR, CPUID_MCA, CPUID_CLFLUSH (needed for Win64) */ /* missing: @@ -321,14 +329,14 @@ static const char *cpuid_6_feature_name[] = { #define TCG_EXT_FEATURES (CPUID_EXT_SSE3 | CPUID_EXT_PCLMULQDQ | \ CPUID_EXT_MONITOR | CPUID_EXT_SSSE3 | CPUID_EXT_CX16 | \ CPUID_EXT_SSE41 | CPUID_EXT_SSE42 | CPUID_EXT_POPCNT | \ + CPUID_EXT_XSAVE | /* CPUID_EXT_OSXSAVE is dynamic */ \ CPUID_EXT_MOVBE | CPUID_EXT_AES | CPUID_EXT_HYPERVISOR) /* missing: CPUID_EXT_DTES64, CPUID_EXT_DSCPL, CPUID_EXT_VMX, CPUID_EXT_SMX, CPUID_EXT_EST, CPUID_EXT_TM2, CPUID_EXT_CID, CPUID_EXT_FMA, CPUID_EXT_XTPR, CPUID_EXT_PDCM, CPUID_EXT_PCID, CPUID_EXT_DCA, - CPUID_EXT_X2APIC, CPUID_EXT_TSC_DEADLINE_TIMER, CPUID_EXT_XSAVE, - CPUID_EXT_OSXSAVE, CPUID_EXT_AVX, CPUID_EXT_F16C, - CPUID_EXT_RDRAND */ + CPUID_EXT_X2APIC, CPUID_EXT_TSC_DEADLINE_TIMER, CPUID_EXT_AVX, + CPUID_EXT_F16C, CPUID_EXT_RDRAND */ #ifdef TARGET_X86_64 #define TCG_EXT2_X86_64_FEATURES (CPUID_EXT2_SYSCALL | CPUID_EXT2_LM) @@ -346,14 +354,19 @@ static const char *cpuid_6_feature_name[] = { #define TCG_SVM_FEATURES 0 #define TCG_KVM_FEATURES 0 #define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP | \ - CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX) + CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX | \ + CPUID_7_0_EBX_PCOMMIT | CPUID_7_0_EBX_CLFLUSHOPT | \ + CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_FSGSBASE) /* missing: - CPUID_7_0_EBX_FSGSBASE, CPUID_7_0_EBX_HLE, CPUID_7_0_EBX_AVX2, + CPUID_7_0_EBX_HLE, CPUID_7_0_EBX_AVX2, CPUID_7_0_EBX_ERMS, CPUID_7_0_EBX_INVPCID, CPUID_7_0_EBX_RTM, CPUID_7_0_EBX_RDSEED */ +#define TCG_7_0_ECX_FEATURES (CPUID_7_0_ECX_PKU | CPUID_7_0_ECX_OSPKE) #define TCG_APM_FEATURES 0 #define TCG_6_EAX_FEATURES CPUID_6_EAX_ARAT - +#define TCG_XSAVE_FEATURES (CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XGETBV1) + /* missing: + CPUID_XSAVE_XSAVEC, CPUID_XSAVE_XSAVES */ typedef struct FeatureWordInfo { const char **feat_names; @@ -408,6 +421,13 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .cpuid_reg = R_EBX, .tcg_features = TCG_7_0_EBX_FEATURES, }, + [FEAT_7_0_ECX] = { + .feat_names = cpuid_7_0_ecx_feature_name, + .cpuid_eax = 7, + .cpuid_needs_ecx = true, .cpuid_ecx = 0, + .cpuid_reg = R_ECX, + .tcg_features = TCG_7_0_ECX_FEATURES, + }, [FEAT_8000_0007_EDX] = { .feat_names = cpuid_apm_edx_feature_name, .cpuid_eax = 0x80000007, @@ -420,7 +440,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .cpuid_eax = 0xd, .cpuid_needs_ecx = true, .cpuid_ecx = 1, .cpuid_reg = R_EAX, - .tcg_features = 0, + .tcg_features = TCG_XSAVE_FEATURES, }, [FEAT_6_EAX] = { .feat_names = cpuid_6_feature_name, @@ -450,24 +470,28 @@ static const X86RegisterInfo32 x86_reg_info_32[CPU_NB_REGS32] = { }; #undef REGISTER -typedef struct ExtSaveArea { - uint32_t feature, bits; - uint32_t offset, size; -} ExtSaveArea; - -static const ExtSaveArea ext_save_areas[] = { - [2] = { .feature = FEAT_1_ECX, .bits = CPUID_EXT_AVX, +const ExtSaveArea x86_ext_save_areas[] = { + [XSTATE_YMM_BIT] = + { .feature = FEAT_1_ECX, .bits = CPUID_EXT_AVX, .offset = 0x240, .size = 0x100 }, - [3] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX, + [XSTATE_BNDREGS_BIT] = + { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX, .offset = 0x3c0, .size = 0x40 }, - [4] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX, + [XSTATE_BNDCSR_BIT] = + { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX, .offset = 0x400, .size = 0x40 }, - [5] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F, + [XSTATE_OPMASK_BIT] = + { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F, .offset = 0x440, .size = 0x40 }, - [6] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F, + [XSTATE_ZMM_Hi256_BIT] = + { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F, .offset = 0x480, .size = 0x200 }, - [7] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F, + [XSTATE_Hi16_ZMM_BIT] = + { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F, .offset = 0x680, .size = 0x400 }, + [XSTATE_PKRU_BIT] = + { .feature = FEAT_7_0_ECX, .bits = CPUID_7_0_ECX_PKU, + .offset = 0xA80, .size = 0x8 }, }; const char *get_register_name_32(unsigned int reg) @@ -478,38 +502,6 @@ const char *get_register_name_32(unsigned int reg) return x86_reg_info_32[reg].name; } -/* KVM-specific features that are automatically added to all CPU models - * when KVM is enabled. - */ -static uint32_t kvm_default_features[FEATURE_WORDS] = { - [FEAT_KVM] = (1 << KVM_FEATURE_CLOCKSOURCE) | - (1 << KVM_FEATURE_NOP_IO_DELAY) | - (1 << KVM_FEATURE_CLOCKSOURCE2) | - (1 << KVM_FEATURE_ASYNC_PF) | - (1 << KVM_FEATURE_STEAL_TIME) | - (1 << KVM_FEATURE_PV_EOI) | - (1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT), - [FEAT_1_ECX] = CPUID_EXT_X2APIC, -}; - -/* Features that are not added by default to any CPU model when KVM is enabled. - */ -static uint32_t kvm_default_unset_features[FEATURE_WORDS] = { - [FEAT_1_EDX] = CPUID_ACPI, - [FEAT_1_ECX] = CPUID_EXT_MONITOR, - [FEAT_8000_0001_ECX] = CPUID_EXT3_SVM, -}; - -void x86_cpu_compat_kvm_no_autoenable(FeatureWord w, uint32_t features) -{ - kvm_default_features[w] &= ~features; -} - -void x86_cpu_compat_kvm_no_autodisable(FeatureWord w, uint32_t features) -{ - kvm_default_unset_features[w] &= ~features; -} - /* * Returns the set of feature flags that are supported and migratable by * QEMU, for a given FeatureWord. @@ -689,7 +681,6 @@ struct X86CPUDefinition { int stepping; FeatureWordArray features; char model_id[48]; - bool cache_info_passthrough; }; static X86CPUDefinition builtin_x86_defs[] = { @@ -705,12 +696,11 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_PSE36, .features[FEAT_1_ECX] = - CPUID_EXT_SSE3 | CPUID_EXT_CX16 | CPUID_EXT_POPCNT, + CPUID_EXT_SSE3 | CPUID_EXT_CX16, .features[FEAT_8000_0001_EDX] = CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, .features[FEAT_8000_0001_ECX] = - CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | - CPUID_EXT3_ABM | CPUID_EXT3_SSE4A, + CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM, .xlevel = 0x8000000A, }, { @@ -806,7 +796,7 @@ static X86CPUDefinition builtin_x86_defs[] = { .features[FEAT_1_EDX] = PPRO_FEATURES, .features[FEAT_1_ECX] = - CPUID_EXT_SSE3 | CPUID_EXT_POPCNT, + CPUID_EXT_SSE3, .xlevel = 0x80000004, }, { @@ -1113,7 +1103,7 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, .features[FEAT_8000_0001_ECX] = - CPUID_EXT3_LAHF_LM, + CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM, .features[FEAT_7_0_EBX] = CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | @@ -1148,7 +1138,7 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, .features[FEAT_8000_0001_ECX] = - CPUID_EXT3_LAHF_LM, + CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM, .features[FEAT_7_0_EBX] = CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | @@ -1185,7 +1175,7 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, .features[FEAT_8000_0001_ECX] = - CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH, + CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH, .features[FEAT_7_0_EBX] = CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | @@ -1223,7 +1213,7 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, .features[FEAT_8000_0001_ECX] = - CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH, + CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH, .features[FEAT_7_0_EBX] = CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | @@ -1277,8 +1267,9 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_DE | CPUID_FP87, .features[FEAT_1_ECX] = CPUID_EXT_CX16 | CPUID_EXT_SSE3, + /* Missing: CPUID_EXT2_RDTSCP */ .features[FEAT_8000_0001_EDX] = - CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_FXSR | + CPUID_EXT2_LM | CPUID_EXT2_FXSR | CPUID_EXT2_MMX | CPUID_EXT2_NX | CPUID_EXT2_PSE36 | CPUID_EXT2_PAT | CPUID_EXT2_CMOV | CPUID_EXT2_MCA | CPUID_EXT2_PGE | CPUID_EXT2_MTRR | CPUID_EXT2_SYSCALL | @@ -1306,8 +1297,9 @@ static X86CPUDefinition builtin_x86_defs[] = { .features[FEAT_1_ECX] = CPUID_EXT_POPCNT | CPUID_EXT_CX16 | CPUID_EXT_MONITOR | CPUID_EXT_SSE3, + /* Missing: CPUID_EXT2_RDTSCP */ .features[FEAT_8000_0001_EDX] = - CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_FXSR | + CPUID_EXT2_LM | CPUID_EXT2_FXSR | CPUID_EXT2_MMX | CPUID_EXT2_NX | CPUID_EXT2_PSE36 | CPUID_EXT2_PAT | CPUID_EXT2_CMOV | CPUID_EXT2_MCA | CPUID_EXT2_PGE | CPUID_EXT2_MTRR | CPUID_EXT2_SYSCALL | @@ -1338,8 +1330,9 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_EXT_POPCNT | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3, + /* Missing: CPUID_EXT2_RDTSCP */ .features[FEAT_8000_0001_EDX] = - CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | + CPUID_EXT2_LM | CPUID_EXT2_PDPE1GB | CPUID_EXT2_FXSR | CPUID_EXT2_MMX | CPUID_EXT2_NX | CPUID_EXT2_PSE36 | CPUID_EXT2_PAT | CPUID_EXT2_CMOV | CPUID_EXT2_MCA | CPUID_EXT2_PGE | @@ -1373,8 +1366,9 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_EXT_AES | CPUID_EXT_POPCNT | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_FMA | CPUID_EXT_SSSE3 | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3, + /* Missing: CPUID_EXT2_RDTSCP */ .features[FEAT_8000_0001_EDX] = - CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | + CPUID_EXT2_LM | CPUID_EXT2_PDPE1GB | CPUID_EXT2_FXSR | CPUID_EXT2_MMX | CPUID_EXT2_NX | CPUID_EXT2_PSE36 | CPUID_EXT2_PAT | CPUID_EXT2_CMOV | CPUID_EXT2_MCA | CPUID_EXT2_PGE | @@ -1392,30 +1386,41 @@ static X86CPUDefinition builtin_x86_defs[] = { }, }; -/** - * x86_cpu_compat_set_features: - * @cpu_model: CPU model name to be changed. If NULL, all CPU models are changed - * @w: Identifies the feature word to be changed. - * @feat_add: Feature bits to be added to feature word - * @feat_remove: Feature bits to be removed from feature word - * - * Change CPU model feature bits for compatibility. - * - * This function may be used by machine-type compatibility functions - * to enable or disable feature bits on specific CPU models. +typedef struct PropValue { + const char *prop, *value; +} PropValue; + +/* KVM-specific features that are automatically added/removed + * from all CPU models when KVM is enabled. */ -void x86_cpu_compat_set_features(const char *cpu_model, FeatureWord w, - uint32_t feat_add, uint32_t feat_remove) +static PropValue kvm_default_props[] = { + { "kvmclock", "on" }, + { "kvm-nopiodelay", "on" }, + { "kvm-asyncpf", "on" }, + { "kvm-steal-time", "on" }, + { "kvm-pv-eoi", "on" }, + { "kvmclock-stable-bit", "on" }, + { "x2apic", "on" }, + { "acpi", "off" }, + { "monitor", "off" }, + { "svm", "off" }, + { NULL, NULL }, +}; + +void x86_cpu_change_kvm_default(const char *prop, const char *value) { - X86CPUDefinition *def; - int i; - for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) { - def = &builtin_x86_defs[i]; - if (!cpu_model || !strcmp(cpu_model, def->name)) { - def->features[w] |= feat_add; - def->features[w] &= ~feat_remove; + PropValue *pv; + for (pv = kvm_default_props; pv->prop; pv++) { + if (!strcmp(pv->prop, prop)) { + pv->value = value; + break; } } + + /* It is valid to call this function only for properties that + * are already present in the kvm_default_props table. + */ + assert(pv->prop); } static uint32_t x86_cpu_get_supported_feature_word(FeatureWord w, @@ -1442,6 +1447,7 @@ static X86CPUDefinition host_cpudef; static Property host_x86_cpu_properties[] = { DEFINE_PROP_BOOL("migratable", X86CPU, migratable, true), + DEFINE_PROP_BOOL("host-cache-info", X86CPU, cache_info_passthrough, false), DEFINE_PROP_END_OF_LIST() }; @@ -1468,13 +1474,14 @@ static void host_x86_cpu_class_init(ObjectClass *oc, void *data) cpu_x86_fill_model_id(host_cpudef.model_id); xcc->cpu_def = &host_cpudef; - host_cpudef.cache_info_passthrough = true; /* level, xlevel, xlevel2, and the feature words are initialized on * instance_init, because they require KVM to be initialized. */ dc->props = host_x86_cpu_properties; + /* Reason: host_x86_cpu_initfn() dies when !kvm_enabled() */ + dc->cannot_destroy_with_object_finalize_yet = true; } static void host_x86_cpu_initfn(Object *obj) @@ -1512,7 +1519,7 @@ static void report_unavailable_features(FeatureWord w, uint32_t mask) int i; for (i = 0; i < 32; ++i) { - if (1 << i & mask) { + if ((1UL << i) & mask) { const char *reg = get_register_name_32(f->cpuid_reg); assert(reg); fprintf(stderr, "warning: %s doesn't support requested feature: " @@ -1525,8 +1532,9 @@ static void report_unavailable_features(FeatureWord w, uint32_t mask) } } -static void x86_cpuid_version_get_family(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void x86_cpuid_version_get_family(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { X86CPU *cpu = X86_CPU(obj); CPUX86State *env = &cpu->env; @@ -1536,11 +1544,12 @@ static void x86_cpuid_version_get_family(Object *obj, Visitor *v, void *opaque, if (value == 0xf) { value += (env->cpuid_version >> 20) & 0xff; } - visit_type_int(v, &value, name, errp); + visit_type_int(v, name, &value, errp); } -static void x86_cpuid_version_set_family(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void x86_cpuid_version_set_family(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { X86CPU *cpu = X86_CPU(obj); CPUX86State *env = &cpu->env; @@ -1549,7 +1558,7 @@ static void x86_cpuid_version_set_family(Object *obj, Visitor *v, void *opaque, Error *local_err = NULL; int64_t value; - visit_type_int(v, &value, name, &local_err); + visit_type_int(v, name, &value, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -1568,8 +1577,9 @@ static void x86_cpuid_version_set_family(Object *obj, Visitor *v, void *opaque, } } -static void x86_cpuid_version_get_model(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void x86_cpuid_version_get_model(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { X86CPU *cpu = X86_CPU(obj); CPUX86State *env = &cpu->env; @@ -1577,11 +1587,12 @@ static void x86_cpuid_version_get_model(Object *obj, Visitor *v, void *opaque, value = (env->cpuid_version >> 4) & 0xf; value |= ((env->cpuid_version >> 16) & 0xf) << 4; - visit_type_int(v, &value, name, errp); + visit_type_int(v, name, &value, errp); } -static void x86_cpuid_version_set_model(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void x86_cpuid_version_set_model(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { X86CPU *cpu = X86_CPU(obj); CPUX86State *env = &cpu->env; @@ -1590,7 +1601,7 @@ static void x86_cpuid_version_set_model(Object *obj, Visitor *v, void *opaque, Error *local_err = NULL; int64_t value; - visit_type_int(v, &value, name, &local_err); + visit_type_int(v, name, &value, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -1606,7 +1617,7 @@ static void x86_cpuid_version_set_model(Object *obj, Visitor *v, void *opaque, } static void x86_cpuid_version_get_stepping(Object *obj, Visitor *v, - void *opaque, const char *name, + const char *name, void *opaque, Error **errp) { X86CPU *cpu = X86_CPU(obj); @@ -1614,11 +1625,11 @@ static void x86_cpuid_version_get_stepping(Object *obj, Visitor *v, int64_t value; value = env->cpuid_version & 0xf; - visit_type_int(v, &value, name, errp); + visit_type_int(v, name, &value, errp); } static void x86_cpuid_version_set_stepping(Object *obj, Visitor *v, - void *opaque, const char *name, + const char *name, void *opaque, Error **errp) { X86CPU *cpu = X86_CPU(obj); @@ -1628,7 +1639,7 @@ static void x86_cpuid_version_set_stepping(Object *obj, Visitor *v, Error *local_err = NULL; int64_t value; - visit_type_int(v, &value, name, &local_err); + visit_type_int(v, name, &value, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -1714,18 +1725,18 @@ static void x86_cpuid_set_model_id(Object *obj, const char *model_id, } } -static void x86_cpuid_get_tsc_freq(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void x86_cpuid_get_tsc_freq(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { X86CPU *cpu = X86_CPU(obj); int64_t value; value = cpu->env.tsc_khz * 1000; - visit_type_int(v, &value, name, errp); + visit_type_int(v, name, &value, errp); } -static void x86_cpuid_set_tsc_freq(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void x86_cpuid_set_tsc_freq(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { X86CPU *cpu = X86_CPU(obj); const int64_t min = 0; @@ -1733,7 +1744,7 @@ static void x86_cpuid_set_tsc_freq(Object *obj, Visitor *v, void *opaque, Error *local_err = NULL; int64_t value; - visit_type_int(v, &value, name, &local_err); + visit_type_int(v, name, &value, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -1744,20 +1755,20 @@ static void x86_cpuid_set_tsc_freq(Object *obj, Visitor *v, void *opaque, return; } - cpu->env.tsc_khz = value / 1000; + cpu->env.tsc_khz = cpu->env.user_tsc_khz = value / 1000; } -static void x86_cpuid_get_apic_id(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void x86_cpuid_get_apic_id(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { X86CPU *cpu = X86_CPU(obj); int64_t value = cpu->apic_id; - visit_type_int(v, &value, name, errp); + visit_type_int(v, name, &value, errp); } -static void x86_cpuid_set_apic_id(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void x86_cpuid_set_apic_id(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { X86CPU *cpu = X86_CPU(obj); DeviceState *dev = DEVICE(obj); @@ -1772,7 +1783,7 @@ static void x86_cpuid_set_apic_id(Object *obj, Visitor *v, void *opaque, return; } - visit_type_int(v, &value, name, &error); + visit_type_int(v, name, &value, &error); if (error) { error_propagate(errp, error); return; @@ -1792,8 +1803,9 @@ static void x86_cpuid_set_apic_id(Object *obj, Visitor *v, void *opaque, } /* Generic getter for "feature-words" and "filtered-features" properties */ -static void x86_cpu_get_feature_words(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void x86_cpu_get_feature_words(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { uint32_t *array = (uint32_t *)opaque; FeatureWord w; @@ -1817,21 +1829,21 @@ static void x86_cpu_get_feature_words(Object *obj, Visitor *v, void *opaque, list = &list_entries[w]; } - visit_type_X86CPUFeatureWordInfoList(v, &list, "feature-words", &err); + visit_type_X86CPUFeatureWordInfoList(v, "feature-words", &list, &err); error_propagate(errp, err); } -static void x86_get_hv_spinlocks(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void x86_get_hv_spinlocks(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { X86CPU *cpu = X86_CPU(obj); int64_t value = cpu->hyperv_spinlock_attempts; - visit_type_int(v, &value, name, errp); + visit_type_int(v, name, &value, errp); } -static void x86_set_hv_spinlocks(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void x86_set_hv_spinlocks(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { const int64_t min = 0xFFF; const int64_t max = UINT_MAX; @@ -1839,7 +1851,7 @@ static void x86_set_hv_spinlocks(Object *obj, Visitor *v, void *opaque, Error *err = NULL; int64_t value; - visit_type_int(v, &value, name, &err); + visit_type_int(v, name, &value, &err); if (err) { error_propagate(errp, err); return; @@ -1919,8 +1931,8 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features, char *err; char num[32]; - tsc_freq = strtosz_suffix_unit(val, &err, - STRTOSZ_DEFSUFFIX_B, 1000); + tsc_freq = qemu_strtosz_suffix_unit(val, &err, + QEMU_STRTOSZ_DEFSUFFIX_B, 1000); if (tsc_freq < 0 || *err) { error_setg(errp, "bad numerical value %s", val); return; @@ -2087,6 +2099,18 @@ static int x86_cpu_filter_features(X86CPU *cpu) return rv; } +static void x86_cpu_apply_props(X86CPU *cpu, PropValue *props) +{ + PropValue *pv; + for (pv = props; pv->prop; pv++) { + if (!pv->value) { + continue; + } + object_property_parse(OBJECT(cpu), pv->value, pv->prop, + &error_abort); + } +} + /* Load data from X86CPUDefinition */ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp) @@ -2102,7 +2126,6 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp) object_property_set_int(OBJECT(cpu), def->stepping, "stepping", errp); object_property_set_int(OBJECT(cpu), def->xlevel, "xlevel", errp); object_property_set_int(OBJECT(cpu), def->xlevel2, "xlevel2", errp); - cpu->cache_info_passthrough = def->cache_info_passthrough; object_property_set_str(OBJECT(cpu), def->model_id, "model-id", errp); for (w = 0; w < FEATURE_WORDS; w++) { env->features[w] = def->features[w]; @@ -2110,11 +2133,11 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp) /* Special cases not set in the X86CPUDefinition structs: */ if (kvm_enabled()) { - FeatureWord w; - for (w = 0; w < FEATURE_WORDS; w++) { - env->features[w] |= kvm_default_features[w]; - env->features[w] &= ~kvm_default_unset_features[w]; + if (!kvm_irqchip_in_kernel()) { + x86_cpu_change_kvm_default("x2apic", "off"); } + + x86_cpu_apply_props(cpu, kvm_default_props); } env->features[FEAT_1_ECX] |= CPUID_EXT_HYPERVISOR; @@ -2257,7 +2280,7 @@ void x86_cpudef_setup(void) pstrcpy(def->model_id, sizeof(def->model_id), "QEMU Virtual CPU version "); pstrcat(def->model_id, sizeof(def->model_id), - qemu_get_version()); + qemu_hw_version()); break; } } @@ -2306,10 +2329,13 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ebx = (cpu->apic_id << 24) | 8 << 8; /* CLFLUSH size in quad words, Linux wants it. */ *ecx = env->features[FEAT_1_ECX]; + if ((*ecx & CPUID_EXT_XSAVE) && (env->cr[4] & CR4_OSXSAVE_MASK)) { + *ecx |= CPUID_EXT_OSXSAVE; + } *edx = env->features[FEAT_1_EDX]; if (cs->nr_cores * cs->nr_threads > 1) { *ebx |= (cs->nr_cores * cs->nr_threads) << 16; - *edx |= 1 << 28; /* HTT bit */ + *edx |= CPUID_HT; } break; case 2: @@ -2399,7 +2425,10 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, if (count == 0) { *eax = 0; /* Maximum ECX value for sub-leaves */ *ebx = env->features[FEAT_7_0_EBX]; /* Feature flags */ - *ecx = 0; /* Reserved */ + *ecx = env->features[FEAT_7_0_ECX]; /* Feature flags */ + if ((*ecx & CPUID_7_0_ECX_PKU) && env->cr[4] & CR4_PKE_MASK) { + *ecx |= CPUID_7_0_ECX_OSPKE; + } *edx = 0; /* Reserved */ } else { *eax = 0; @@ -2433,7 +2462,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 0xD: { KVMState *s = cs->kvm_state; - uint64_t kvm_mask; + uint64_t ena_mask; int i; /* Processor Extended State */ @@ -2441,35 +2470,39 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ebx = 0; *ecx = 0; *edx = 0; - if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) || !kvm_enabled()) { + if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE)) { break; } - kvm_mask = - kvm_arch_get_supported_cpuid(s, 0xd, 0, R_EAX) | - ((uint64_t)kvm_arch_get_supported_cpuid(s, 0xd, 0, R_EDX) << 32); + if (kvm_enabled()) { + ena_mask = kvm_arch_get_supported_cpuid(s, 0xd, 0, R_EDX); + ena_mask <<= 32; + ena_mask |= kvm_arch_get_supported_cpuid(s, 0xd, 0, R_EAX); + } else { + ena_mask = -1; + } if (count == 0) { *ecx = 0x240; - for (i = 2; i < ARRAY_SIZE(ext_save_areas); i++) { - const ExtSaveArea *esa = &ext_save_areas[i]; - if ((env->features[esa->feature] & esa->bits) == esa->bits && - (kvm_mask & (1 << i)) != 0) { + for (i = 2; i < ARRAY_SIZE(x86_ext_save_areas); i++) { + const ExtSaveArea *esa = &x86_ext_save_areas[i]; + if ((env->features[esa->feature] & esa->bits) == esa->bits + && ((ena_mask >> i) & 1) != 0) { if (i < 32) { - *eax |= 1 << i; + *eax |= 1u << i; } else { - *edx |= 1 << (i - 32); + *edx |= 1u << (i - 32); } *ecx = MAX(*ecx, esa->offset + esa->size); } } - *eax |= kvm_mask & (XSTATE_FP | XSTATE_SSE); + *eax |= ena_mask & (XSTATE_FP_MASK | XSTATE_SSE_MASK); *ebx = *ecx; } else if (count == 1) { *eax = env->features[FEAT_XSAVE]; - } else if (count < ARRAY_SIZE(ext_save_areas)) { - const ExtSaveArea *esa = &ext_save_areas[count]; - if ((env->features[esa->feature] & esa->bits) == esa->bits && - (kvm_mask & (1 << count)) != 0) { + } else if (count < ARRAY_SIZE(x86_ext_save_areas)) { + const ExtSaveArea *esa = &x86_ext_save_areas[count]; + if ((env->features[esa->feature] & esa->bits) == esa->bits + && ((ena_mask >> count) & 1) != 0) { *eax = esa->size; *ebx = esa->offset; } @@ -2622,6 +2655,8 @@ static void x86_cpu_reset(CPUState *s) X86CPU *cpu = X86_CPU(s); X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); CPUX86State *env = &cpu->env; + target_ulong cr4; + uint64_t xcr0; int i; xcc->parent_reset(s); @@ -2681,7 +2716,8 @@ static void x86_cpu_reset(CPUState *s) cpu_set_fpuc(env, 0x37f); env->mxcsr = 0x1f80; - env->xstate_bv = XSTATE_FP | XSTATE_SSE; + /* All units are in INIT state. */ + env->xstate_bv = 0; env->pat = 0x0007040600070406ULL; env->msr_ia32_misc_enable = MSR_IA32_MISC_ENABLE_DEFAULT; @@ -2692,7 +2728,31 @@ static void x86_cpu_reset(CPUState *s) cpu_breakpoint_remove_all(s, BP_CPU); cpu_watchpoint_remove_all(s, BP_CPU); - env->xcr0 = 1; + cr4 = 0; + xcr0 = XSTATE_FP_MASK; + +#ifdef CONFIG_USER_ONLY + /* Enable all the features for user-mode. */ + if (env->features[FEAT_1_EDX] & CPUID_SSE) { + xcr0 |= XSTATE_SSE_MASK; + } + for (i = 2; i < ARRAY_SIZE(x86_ext_save_areas); i++) { + const ExtSaveArea *esa = &x86_ext_save_areas[i]; + if ((env->features[esa->feature] & esa->bits) == esa->bits) { + xcr0 |= 1ull << i; + } + } + + if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) { + cr4 |= CR4_OSFXSR_MASK | CR4_OSXSAVE_MASK; + } + if (env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_FSGSBASE) { + cr4 |= CR4_FSGSBASE_MASK; + } +#endif + + env->xcr0 = xcr0; + cpu_x86_update_cr4(env, cr4); /* * SDM 11.11.5 requires: @@ -2749,21 +2809,16 @@ static void mce_init(X86CPU *cpu) #ifndef CONFIG_USER_ONLY static void x86_cpu_apic_create(X86CPU *cpu, Error **errp) { - DeviceState *dev = DEVICE(cpu); APICCommonState *apic; const char *apic_type = "apic"; - if (kvm_irqchip_in_kernel()) { + if (kvm_apic_in_kernel()) { apic_type = "kvm-apic"; } else if (xen_enabled()) { apic_type = "xen-apic"; } - cpu->apic_state = qdev_try_create(qdev_get_parent_bus(dev), apic_type); - if (cpu->apic_state == NULL) { - error_setg(errp, "APIC device '%s' could not be created", apic_type); - return; - } + cpu->apic_state = DEVICE(object_new(apic_type)); object_property_add_child(OBJECT(cpu), "apic", OBJECT(cpu->apic_state), NULL); @@ -2771,15 +2826,30 @@ static void x86_cpu_apic_create(X86CPU *cpu, Error **errp) /* TODO: convert to link<> */ apic = APIC_COMMON(cpu->apic_state); apic->cpu = cpu; + apic->apicbase = APIC_DEFAULT_ADDRESS | MSR_IA32_APICBASE_ENABLE; } static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp) { + APICCommonState *apic; + static bool apic_mmio_map_once; + if (cpu->apic_state == NULL) { return; } object_property_set_bool(OBJECT(cpu->apic_state), true, "realized", errp); + + /* Map APIC MMIO area */ + apic = APIC_COMMON(cpu->apic_state); + if (!apic_mmio_map_once) { + memory_region_add_subregion_overlap(get_system_memory(), + apic->apicbase & + MSR_IA32_APICBASE_BASE, + &apic->io_memory, + 0x1000); + apic_mmio_map_once = true; + } } static void x86_cpu_machine_done(Notifier *n, void *unused) @@ -2827,6 +2897,14 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) env->cpuid_level = 7; } + if (x86_cpu_filter_features(cpu) && cpu->enforce_cpuid) { + error_setg(&local_err, + kvm_enabled() ? + "Host doesn't support requested features" : + "TCG doesn't support requested features"); + goto out; + } + /* On AMD CPUs, some CPUID[8000_0001].EDX bits must match the bits on * CPUID[1].EDX. */ @@ -2837,14 +2915,6 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) } - if (x86_cpu_filter_features(cpu) && cpu->enforce_cpuid) { - error_setg(&local_err, - kvm_enabled() ? - "Host doesn't support requested features" : - "TCG doesn't support requested features"); - goto out; - } - #ifndef CONFIG_USER_ONLY qemu_register_reset(x86_cpu_machine_reset_cb, cpu); @@ -2860,9 +2930,10 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) #ifndef CONFIG_USER_ONLY if (tcg_enabled()) { + AddressSpace *newas = g_new(AddressSpace, 1); + cpu->cpu_as_mem = g_new(MemoryRegion, 1); cpu->cpu_as_root = g_new(MemoryRegion, 1); - cs->as = g_new(AddressSpace, 1); /* Outer container... */ memory_region_init(cpu->cpu_as_root, OBJECT(cpu), "memory", ~0ull); @@ -2875,7 +2946,9 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) get_system_memory(), 0, ~0ull); memory_region_add_subregion_overlap(cpu->cpu_as_root, 0, cpu->cpu_as_mem, 0); memory_region_set_enabled(cpu->cpu_as_mem, true); - address_space_init(cs->as, cpu->cpu_as_root, "CPU"); + address_space_init(newas, cpu->cpu_as_root, "CPU"); + cs->num_ases = 1; + cpu_address_space_init(cs, newas, 0); /* ... SMRAM with higher priority, linked from /machine/smram. */ cpu->machine_done.notify = x86_cpu_machine_done; @@ -2919,22 +2992,16 @@ typedef struct BitProperty { uint32_t mask; } BitProperty; -static void x86_cpu_get_bit_prop(Object *obj, - struct Visitor *v, - void *opaque, - const char *name, - Error **errp) +static void x86_cpu_get_bit_prop(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { BitProperty *fp = opaque; bool value = (*fp->ptr & fp->mask) == fp->mask; - visit_type_bool(v, &value, name, errp); + visit_type_bool(v, name, &value, errp); } -static void x86_cpu_set_bit_prop(Object *obj, - struct Visitor *v, - void *opaque, - const char *name, - Error **errp) +static void x86_cpu_set_bit_prop(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); BitProperty *fp = opaque; @@ -2946,7 +3013,7 @@ static void x86_cpu_set_bit_prop(Object *obj, return; } - visit_type_bool(v, &value, name, &local_err); + visit_type_bool(v, name, &value, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -3087,7 +3154,7 @@ static void x86_cpu_initfn(Object *obj) /* init various static tables used in TCG mode */ if (tcg_enabled() && !inited) { inited = 1; - optimize_flags_init(); + tcg_x86_init(); } } @@ -3124,14 +3191,8 @@ static bool x86_cpu_has_work(CPUState *cs) X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; -#if !defined(CONFIG_USER_ONLY) - if (cs->interrupt_request & CPU_INTERRUPT_POLL) { - apic_poll_irq(cpu->apic_state); - cpu_reset_interrupt(cs, CPU_INTERRUPT_POLL); - } -#endif - - return ((cs->interrupt_request & CPU_INTERRUPT_HARD) && + return ((cs->interrupt_request & (CPU_INTERRUPT_HARD | + CPU_INTERRUPT_POLL)) && (env->eflags & IF_MASK)) || (cs->interrupt_request & (CPU_INTERRUPT_NMI | CPU_INTERRUPT_INIT | @@ -3147,12 +3208,19 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_BOOL("hv-relaxed", X86CPU, hyperv_relaxed_timing, false), DEFINE_PROP_BOOL("hv-vapic", X86CPU, hyperv_vapic, false), DEFINE_PROP_BOOL("hv-time", X86CPU, hyperv_time, false), - DEFINE_PROP_BOOL("check", X86CPU, check_cpuid, false), + DEFINE_PROP_BOOL("hv-crash", X86CPU, hyperv_crash, false), + DEFINE_PROP_BOOL("hv-reset", X86CPU, hyperv_reset, false), + DEFINE_PROP_BOOL("hv-vpindex", X86CPU, hyperv_vpindex, false), + DEFINE_PROP_BOOL("hv-runtime", X86CPU, hyperv_runtime, false), + DEFINE_PROP_BOOL("hv-synic", X86CPU, hyperv_synic, false), + DEFINE_PROP_BOOL("hv-stimer", X86CPU, hyperv_stimer, false), + DEFINE_PROP_BOOL("check", X86CPU, check_cpuid, true), DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false), DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true), DEFINE_PROP_UINT32("level", X86CPU, env.cpuid_level, 0), DEFINE_PROP_UINT32("xlevel", X86CPU, env.cpuid_xlevel, 0), DEFINE_PROP_UINT32("xlevel2", X86CPU, env.cpuid_xlevel2, 0), + DEFINE_PROP_STRING("hv-vendor-id", X86CPU, hyperv_vendor_id), DEFINE_PROP_END_OF_LIST() }; @@ -3164,7 +3232,6 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) xcc->parent_realize = dc->realize; dc->realize = x86_cpu_realizefn; - dc->bus_type = TYPE_ICC_BUS; dc->props = x86_cpu_properties; xcc->parent_reset = cc->reset; @@ -3200,6 +3267,12 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) #endif cc->cpu_exec_enter = x86_cpu_exec_enter; cc->cpu_exec_exit = x86_cpu_exec_exit; + + /* + * Reason: x86_cpu_initfn() calls cpu_exec_init(), which saves the + * object in cpus -> dangling pointer after final object_unref(). + */ + dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo x86_cpu_type_info = { diff --git a/qemu/target-i386/cpu.h b/qemu/target-i386/cpu.h index ead28325b..732eb6d7e 100644 --- a/qemu/target-i386/cpu.h +++ b/qemu/target-i386/cpu.h @@ -19,8 +19,8 @@ #ifndef CPU_I386_H #define CPU_I386_H -#include "config.h" #include "qemu-common.h" +#include "standard-headers/asm-x86/hyperv.h" #ifdef TARGET_X86_64 #define TARGET_LONG_BITS 64 @@ -36,10 +36,10 @@ #define TARGET_HAS_PRECISE_SMC #ifdef TARGET_X86_64 -#define ELF_MACHINE EM_X86_64 +#define I386_ELF_MACHINE EM_X86_64 #define ELF_MACHINE_UNAME "x86_64" #else -#define ELF_MACHINE EM_386 +#define I386_ELF_MACHINE EM_386 #define ELF_MACHINE_UNAME "i686" #endif @@ -154,6 +154,9 @@ #define HF_SVMI_SHIFT 21 /* SVM intercepts are active */ #define HF_OSFXSR_SHIFT 22 /* CR4.OSFXSR */ #define HF_SMAP_SHIFT 23 /* CR4.SMAP */ +#define HF_IOBPT_SHIFT 24 /* an io breakpoint enabled */ +#define HF_MPX_EN_SHIFT 25 /* MPX Enabled (CR4+XCR0+BNDCFGx) */ +#define HF_MPX_IU_SHIFT 26 /* BND registers in-use */ #define HF_CPL_MASK (3 << HF_CPL_SHIFT) #define HF_SOFTMMU_MASK (1 << HF_SOFTMMU_SHIFT) @@ -177,6 +180,9 @@ #define HF_SVMI_MASK (1 << HF_SVMI_SHIFT) #define HF_OSFXSR_MASK (1 << HF_OSFXSR_SHIFT) #define HF_SMAP_MASK (1 << HF_SMAP_SHIFT) +#define HF_IOBPT_MASK (1 << HF_IOBPT_SHIFT) +#define HF_MPX_EN_MASK (1 << HF_MPX_EN_SHIFT) +#define HF_MPX_IU_MASK (1 << HF_MPX_IU_SHIFT) /* hflags2 */ @@ -185,12 +191,14 @@ #define HF2_NMI_SHIFT 2 /* CPU serving NMI */ #define HF2_VINTR_SHIFT 3 /* value of V_INTR_MASKING bit */ #define HF2_SMM_INSIDE_NMI_SHIFT 4 /* CPU serving SMI nested inside NMI */ +#define HF2_MPX_PR_SHIFT 5 /* BNDCFGx.BNDPRESERVE */ #define HF2_GIF_MASK (1 << HF2_GIF_SHIFT) #define HF2_HIF_MASK (1 << HF2_HIF_SHIFT) #define HF2_NMI_MASK (1 << HF2_NMI_SHIFT) #define HF2_VINTR_MASK (1 << HF2_VINTR_SHIFT) #define HF2_SMM_INSIDE_NMI_MASK (1 << HF2_SMM_INSIDE_NMI_SHIFT) +#define HF2_MPX_PR_MASK (1 << HF2_MPX_PR_SHIFT) #define CR0_PE_SHIFT 0 #define CR0_MP_SHIFT 1 @@ -224,6 +232,7 @@ #define CR4_OSXSAVE_MASK (1U << 18) #define CR4_SMEP_MASK (1U << 20) #define CR4_SMAP_MASK (1U << 21) +#define CR4_PKE_MASK (1U << 22) #define DR6_BD (1 << 13) #define DR6_BS (1 << 14) @@ -234,6 +243,7 @@ #define DR7_TYPE_SHIFT 16 #define DR7_LEN_SHIFT 18 #define DR7_FIXED_1 0x00000400 +#define DR7_GLOBAL_BP_MASK 0xaa #define DR7_LOCAL_BP_MASK 0x55 #define DR7_MAX_BP 4 #define DR7_TYPE_BP_INST 0x0 @@ -251,6 +261,7 @@ #define PG_PSE_BIT 7 #define PG_GLOBAL_BIT 8 #define PG_PSE_PAT_BIT 12 +#define PG_PKRU_BIT 59 #define PG_NX_BIT 63 #define PG_PRESENT_MASK (1 << PG_PRESENT_BIT) @@ -266,7 +277,8 @@ #define PG_ADDRESS_MASK 0x000ffffffffff000LL #define PG_HI_RSVD_MASK (PG_ADDRESS_MASK & ~PHYS_ADDR_MASK) #define PG_HI_USER_MASK 0x7ff0000000000000LL -#define PG_NX_MASK (1LL << PG_NX_BIT) +#define PG_PKRU_MASK (15ULL << PG_PKRU_BIT) +#define PG_NX_MASK (1ULL << PG_NX_BIT) #define PG_ERROR_W_BIT 1 @@ -275,6 +287,7 @@ #define PG_ERROR_U_MASK 0x04 #define PG_ERROR_RSVD_MASK 0x08 #define PG_ERROR_I_D_MASK 0x10 +#define PG_ERROR_PK_MASK 0x20 #define MCG_CTL_P (1ULL<<8) /* MCG_CAP register available */ #define MCG_SER_P (1ULL<<24) /* MCA recovery/new status bits */ @@ -282,6 +295,8 @@ #define MCE_CAP_DEF (MCG_CTL_P|MCG_SER_P) #define MCE_BANKS_DEF 10 +#define MCG_CAP_BANKS_MASK 0xff + #define MCG_STATUS_RIPV (1ULL<<0) /* restart ip valid */ #define MCG_STATUS_EIPV (1ULL<<1) /* ip points to correct instruction */ #define MCG_STATUS_MCIP (1ULL<<2) /* machine check in progress */ @@ -393,21 +408,32 @@ #define MSR_IA32_BNDCFGS 0x00000d90 #define MSR_IA32_XSS 0x00000da0 -#define XSTATE_FP (1ULL << 0) -#define XSTATE_SSE (1ULL << 1) -#define XSTATE_YMM (1ULL << 2) -#define XSTATE_BNDREGS (1ULL << 3) -#define XSTATE_BNDCSR (1ULL << 4) -#define XSTATE_OPMASK (1ULL << 5) -#define XSTATE_ZMM_Hi256 (1ULL << 6) -#define XSTATE_Hi16_ZMM (1ULL << 7) - +#define XSTATE_FP_BIT 0 +#define XSTATE_SSE_BIT 1 +#define XSTATE_YMM_BIT 2 +#define XSTATE_BNDREGS_BIT 3 +#define XSTATE_BNDCSR_BIT 4 +#define XSTATE_OPMASK_BIT 5 +#define XSTATE_ZMM_Hi256_BIT 6 +#define XSTATE_Hi16_ZMM_BIT 7 +#define XSTATE_PKRU_BIT 9 + +#define XSTATE_FP_MASK (1ULL << XSTATE_FP_BIT) +#define XSTATE_SSE_MASK (1ULL << XSTATE_SSE_BIT) +#define XSTATE_YMM_MASK (1ULL << XSTATE_YMM_BIT) +#define XSTATE_BNDREGS_MASK (1ULL << XSTATE_BNDREGS_BIT) +#define XSTATE_BNDCSR_MASK (1ULL << XSTATE_BNDCSR_BIT) +#define XSTATE_OPMASK_MASK (1ULL << XSTATE_OPMASK_BIT) +#define XSTATE_ZMM_Hi256_MASK (1ULL << XSTATE_ZMM_Hi256_BIT) +#define XSTATE_Hi16_ZMM_MASK (1ULL << XSTATE_Hi16_ZMM_BIT) +#define XSTATE_PKRU_MASK (1ULL << XSTATE_PKRU_BIT) /* CPUID feature words */ typedef enum FeatureWord { FEAT_1_EDX, /* CPUID[1].EDX */ FEAT_1_ECX, /* CPUID[1].ECX */ FEAT_7_0_EBX, /* CPUID[EAX=7,ECX=0].EBX */ + FEAT_7_0_ECX, /* CPUID[EAX=7,ECX=0].ECX */ FEAT_8000_0001_EDX, /* CPUID[8000_0001].EDX */ FEAT_8000_0001_ECX, /* CPUID[8000_0001].ECX */ FEAT_8000_0007_EDX, /* CPUID[8000_0007].EDX */ @@ -572,10 +598,16 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_7_0_EBX_RDSEED (1U << 18) #define CPUID_7_0_EBX_ADX (1U << 19) #define CPUID_7_0_EBX_SMAP (1U << 20) +#define CPUID_7_0_EBX_PCOMMIT (1U << 22) /* Persistent Commit */ +#define CPUID_7_0_EBX_CLFLUSHOPT (1U << 23) /* Flush a Cache Line Optimized */ +#define CPUID_7_0_EBX_CLWB (1U << 24) /* Cache Line Write Back */ #define CPUID_7_0_EBX_AVX512PF (1U << 26) /* AVX-512 Prefetch */ #define CPUID_7_0_EBX_AVX512ER (1U << 27) /* AVX-512 Exponential and Reciprocal */ #define CPUID_7_0_EBX_AVX512CD (1U << 28) /* AVX-512 Conflict Detection */ +#define CPUID_7_0_ECX_PKU (1U << 3) +#define CPUID_7_0_ECX_OSPKE (1U << 4) + #define CPUID_XSAVE_XSAVEOPT (1U << 0) #define CPUID_XSAVE_XSAVEC (1U << 1) #define CPUID_XSAVE_XGETBV1 (1U << 2) @@ -716,22 +748,18 @@ typedef struct SegmentCache { uint32_t flags; } SegmentCache; -typedef union { - uint8_t _b[64]; - uint16_t _w[32]; - uint32_t _l[16]; - uint64_t _q[8]; - float32 _s[16]; - float64 _d[8]; -} XMMReg; /* really zmm */ +#define MMREG_UNION(n, bits) \ + union n { \ + uint8_t _b_##n[(bits)/8]; \ + uint16_t _w_##n[(bits)/16]; \ + uint32_t _l_##n[(bits)/32]; \ + uint64_t _q_##n[(bits)/64]; \ + float32 _s_##n[(bits)/32]; \ + float64 _d_##n[(bits)/64]; \ + } -typedef union { - uint8_t _b[8]; - uint16_t _w[4]; - uint32_t _l[2]; - float32 _s[2]; - uint64_t q; -} MMXReg; +typedef MMREG_UNION(ZMMReg, 512) ZMMReg; +typedef MMREG_UNION(MMXReg, 64) MMXReg; typedef struct BNDReg { uint64_t lb; @@ -743,32 +771,36 @@ typedef struct BNDCSReg { uint64_t sts; } BNDCSReg; +#define BNDCFG_ENABLE 1ULL +#define BNDCFG_BNDPRESERVE 2ULL +#define BNDCFG_BDIR_MASK TARGET_PAGE_MASK + #ifdef HOST_WORDS_BIGENDIAN -#define XMM_B(n) _b[63 - (n)] -#define XMM_W(n) _w[31 - (n)] -#define XMM_L(n) _l[15 - (n)] -#define XMM_S(n) _s[15 - (n)] -#define XMM_Q(n) _q[7 - (n)] -#define XMM_D(n) _d[7 - (n)] - -#define MMX_B(n) _b[7 - (n)] -#define MMX_W(n) _w[3 - (n)] -#define MMX_L(n) _l[1 - (n)] -#define MMX_S(n) _s[1 - (n)] +#define ZMM_B(n) _b_ZMMReg[63 - (n)] +#define ZMM_W(n) _w_ZMMReg[31 - (n)] +#define ZMM_L(n) _l_ZMMReg[15 - (n)] +#define ZMM_S(n) _s_ZMMReg[15 - (n)] +#define ZMM_Q(n) _q_ZMMReg[7 - (n)] +#define ZMM_D(n) _d_ZMMReg[7 - (n)] + +#define MMX_B(n) _b_MMXReg[7 - (n)] +#define MMX_W(n) _w_MMXReg[3 - (n)] +#define MMX_L(n) _l_MMXReg[1 - (n)] +#define MMX_S(n) _s_MMXReg[1 - (n)] #else -#define XMM_B(n) _b[n] -#define XMM_W(n) _w[n] -#define XMM_L(n) _l[n] -#define XMM_S(n) _s[n] -#define XMM_Q(n) _q[n] -#define XMM_D(n) _d[n] - -#define MMX_B(n) _b[n] -#define MMX_W(n) _w[n] -#define MMX_L(n) _l[n] -#define MMX_S(n) _s[n] +#define ZMM_B(n) _b_ZMMReg[n] +#define ZMM_W(n) _w_ZMMReg[n] +#define ZMM_L(n) _l_ZMMReg[n] +#define ZMM_S(n) _s_ZMMReg[n] +#define ZMM_Q(n) _q_ZMMReg[n] +#define ZMM_D(n) _d_ZMMReg[n] + +#define MMX_B(n) _b_MMXReg[n] +#define MMX_W(n) _w_MMXReg[n] +#define MMX_L(n) _l_MMXReg[n] +#define MMX_S(n) _s_MMXReg[n] #endif -#define MMX_Q(n) q +#define MMX_Q(n) _q_MMXReg[n] typedef union { floatx80 d __attribute__((aligned(16))); @@ -793,6 +825,7 @@ typedef struct { #define MAX_GP_COUNTERS (MSR_IA32_PERF_STATUS - MSR_P6_EVNTSEL0) #define NB_MMU_MODES 3 +#define TARGET_INSN_START_EXTRA_WORDS 1 #define NB_OPMASK_REGS 8 @@ -832,6 +865,7 @@ typedef struct CPUX86State { BNDReg bnd_regs[4]; BNDCSReg bndcs_regs; uint64_t msr_bndcfgs; + uint64_t efer; /* Beginning of state preserved by INIT (dummy marker). */ struct {} start_init_save; @@ -854,8 +888,8 @@ typedef struct CPUX86State { float_status mmx_status; /* for 3DNow! float ops */ float_status sse_status; uint32_t mxcsr; - XMMReg xmm_regs[CPU_NB_REGS == 8 ? 8 : 32]; - XMMReg xmm_t0; + ZMMReg xmm_regs[CPU_NB_REGS == 8 ? 8 : 32]; + ZMMReg xmm_t0; MMXReg mmx_t0; uint64_t opmask_regs[NB_OPMASK_REGS]; @@ -864,7 +898,6 @@ typedef struct CPUX86State { uint32_t sysenter_cs; target_ulong sysenter_esp; target_ulong sysenter_eip; - uint64_t efer; uint64_t star; uint64_t vm_hsave; @@ -908,12 +941,21 @@ typedef struct CPUX86State { uint64_t msr_hv_guest_os_id; uint64_t msr_hv_vapic; uint64_t msr_hv_tsc; + uint64_t msr_hv_crash_params[HV_X64_MSR_CRASH_PARAMS]; + uint64_t msr_hv_runtime; + uint64_t msr_hv_synic_control; + uint64_t msr_hv_synic_version; + uint64_t msr_hv_synic_evt_page; + uint64_t msr_hv_synic_msg_page; + uint64_t msr_hv_synic_sint[HV_SYNIC_SINT_COUNT]; + uint64_t msr_hv_stimer_config[HV_SYNIC_STIMER_COUNT]; + uint64_t msr_hv_stimer_count[HV_SYNIC_STIMER_COUNT]; /* exception/interrupt handling */ int error_code; int exception_is_int; target_ulong exception_next_eip; - target_ulong dr[8]; /* debug registers */ + target_ulong dr[8]; /* debug registers; note dr4 and dr5 are unused */ union { struct CPUBreakpoint *cpu_breakpoint[4]; struct CPUWatchpoint *cpu_watchpoint[4]; @@ -963,6 +1005,7 @@ typedef struct CPUX86State { uint32_t sipi_vector; bool tsc_valid; int64_t tsc_khz; + int64_t user_tsc_khz; /* for sanity check only */ void *kvm_xsave_buf; uint64_t mcg_cap; @@ -980,6 +1023,8 @@ typedef struct CPUX86State { uint64_t xcr0; uint64_t xss; + uint32_t pkru; + TPRAccess tpr_access_type; } CPUX86State; @@ -1098,7 +1143,14 @@ void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32); int cpu_x86_signal_handler(int host_signum, void *pinfo, void *puc); -/* cpuid.c */ +/* cpu.c */ +typedef struct ExtSaveArea { + uint32_t feature, bits; + uint32_t offset, size; +} ExtSaveArea; + +extern const ExtSaveArea x86_ext_save_areas[]; + void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); @@ -1123,42 +1175,13 @@ void x86_stl_phys(CPUState *cs, hwaddr addr, uint32_t val); void x86_stq_phys(CPUState *cs, hwaddr addr, uint64_t val); #endif -static inline bool hw_local_breakpoint_enabled(unsigned long dr7, int index) -{ - return (dr7 >> (index * 2)) & 1; -} - -static inline bool hw_global_breakpoint_enabled(unsigned long dr7, int index) -{ - return (dr7 >> (index * 2)) & 2; - -} -static inline bool hw_breakpoint_enabled(unsigned long dr7, int index) -{ - return hw_global_breakpoint_enabled(dr7, index) || - hw_local_breakpoint_enabled(dr7, index); -} - -static inline int hw_breakpoint_type(unsigned long dr7, int index) -{ - return (dr7 >> (DR7_TYPE_SHIFT + (index * 4))) & 3; -} - -static inline int hw_breakpoint_len(unsigned long dr7, int index) -{ - int len = ((dr7 >> (DR7_LEN_SHIFT + (index * 4))) & 3); - return (len == 2) ? 8 : len + 1; -} - -void hw_breakpoint_insert(CPUX86State *env, int index); -void hw_breakpoint_remove(CPUX86State *env, int index); -bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update); void breakpoint_handler(CPUState *cs); /* will be suppressed */ void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0); void cpu_x86_update_cr3(CPUX86State *env, target_ulong new_cr3); void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4); +void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7); /* hw/pc.c */ uint64_t cpu_get_tsc(CPUX86State *env); @@ -1187,7 +1210,6 @@ uint64_t cpu_get_tsc(CPUX86State *env); #define cpu_init(cpu_model) CPU(cpu_x86_init(cpu_model)) #define cpu_exec cpu_x86_exec -#define cpu_gen_code cpu_x86_gen_code #define cpu_signal_handler cpu_x86_signal_handler #define cpu_list x86_cpu_list #define cpudef_setup x86_cpudef_setup @@ -1199,7 +1221,7 @@ uint64_t cpu_get_tsc(CPUX86State *env); #define MMU_KSMAP_IDX 0 #define MMU_USER_IDX 1 #define MMU_KNOSMAP_IDX 2 -static inline int cpu_mmu_index(CPUX86State *env) +static inline int cpu_mmu_index(CPUX86State *env, bool ifetch) { return (env->hflags & HF_CPL_MASK) == 3 ? MMU_USER_IDX : (!(env->hflags & HF_SMAP_MASK) || (env->eflags & AC_MASK)) @@ -1235,7 +1257,7 @@ static inline target_long lshift(target_long x, int n) #define ST1 ST(1) /* translate.c */ -void optimize_flags_init(void); +void tcg_x86_init(void); #include "exec/cpu-all.h" #include "svm.h" @@ -1267,8 +1289,12 @@ void cpu_x86_inject_mce(Monitor *mon, X86CPU *cpu, int bank, /* excp_helper.c */ void QEMU_NORETURN raise_exception(CPUX86State *env, int exception_index); +void QEMU_NORETURN raise_exception_ra(CPUX86State *env, int exception_index, + uintptr_t retaddr); void QEMU_NORETURN raise_exception_err(CPUX86State *env, int exception_index, int error_code); +void QEMU_NORETURN raise_exception_err_ra(CPUX86State *env, int exception_index, + int error_code, uintptr_t retaddr); void QEMU_NORETURN raise_interrupt(CPUX86State *nenv, int intno, int is_int, int error_code, int next_eip_addend); @@ -1318,6 +1344,9 @@ static inline MemTxAttrs cpu_get_mem_attrs(CPUX86State *env) void cpu_set_mxcsr(CPUX86State *env, uint32_t val); void cpu_set_fpuc(CPUX86State *env, uint16_t val); +/* mem_helper.c */ +void helper_lock_init(void); + /* svm_helper.c */ void cpu_svm_check_intercept_param(CPUX86State *env1, uint32_t type, uint64_t param); @@ -1332,12 +1361,18 @@ void cpu_smm_update(X86CPU *cpu); void cpu_report_tpr_access(CPUX86State *env, TPRAccess access); -void x86_cpu_compat_set_features(const char *cpu_model, FeatureWord w, - uint32_t feat_add, uint32_t feat_remove); - -void x86_cpu_compat_kvm_no_autoenable(FeatureWord w, uint32_t features); -void x86_cpu_compat_kvm_no_autodisable(FeatureWord w, uint32_t features); +/* Change the value of a KVM-specific default + * + * If value is NULL, no default will be set and the original + * value from the CPU model table will be kept. + * + * It is valid to call this funciton only for properties that + * are already present in the kvm_default_props table. + */ +void x86_cpu_change_kvm_default(const char *prop, const char *value); +/* mpx_helper.c */ +void cpu_sync_bndcs_hflags(CPUX86State *env); /* Return name of 32-bit register, from a R_* constant */ const char *get_register_name_32(unsigned int reg); @@ -1347,4 +1382,7 @@ void enable_compat_apic_id_mode(void); #define APIC_DEFAULT_ADDRESS 0xfee00000 #define APIC_SPACE_SIZE 0x100000 +void x86_cpu_dump_local_apic_state(CPUState *cs, FILE *f, + fprintf_function cpu_fprintf, int flags); + #endif /* CPU_I386_H */ diff --git a/qemu/target-i386/excp_helper.c b/qemu/target-i386/excp_helper.c index 99fca847d..ef37f4240 100644 --- a/qemu/target-i386/excp_helper.c +++ b/qemu/target-i386/excp_helper.c @@ -17,19 +17,12 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "qemu/log.h" #include "sysemu/sysemu.h" #include "exec/helper-proto.h" -#if 0 -#define raise_exception_err(env, a, b) \ - do { \ - qemu_log("raise_exception line=%d\n", __LINE__); \ - (raise_exception_err)(env, a, b); \ - } while (0) -#endif - void helper_raise_interrupt(CPUX86State *env, int intno, int next_eip_addend) { raise_interrupt(env, intno, 1, 0, next_eip_addend); @@ -92,7 +85,8 @@ static int check_exception(CPUX86State *env, int intno, int *error_code) */ static void QEMU_NORETURN raise_interrupt2(CPUX86State *env, int intno, int is_int, int error_code, - int next_eip_addend) + int next_eip_addend, + uintptr_t retaddr) { CPUState *cs = CPU(x86_env_get_cpu(env)); @@ -108,7 +102,7 @@ static void QEMU_NORETURN raise_interrupt2(CPUX86State *env, int intno, env->error_code = error_code; env->exception_is_int = is_int; env->exception_next_eip = env->eip + next_eip_addend; - cpu_loop_exit(cs); + cpu_loop_exit_restore(cs, retaddr); } /* shortcuts to generate exceptions */ @@ -116,16 +110,27 @@ static void QEMU_NORETURN raise_interrupt2(CPUX86State *env, int intno, void QEMU_NORETURN raise_interrupt(CPUX86State *env, int intno, int is_int, int error_code, int next_eip_addend) { - raise_interrupt2(env, intno, is_int, error_code, next_eip_addend); + raise_interrupt2(env, intno, is_int, error_code, next_eip_addend, 0); } void raise_exception_err(CPUX86State *env, int exception_index, int error_code) { - raise_interrupt2(env, exception_index, 0, error_code, 0); + raise_interrupt2(env, exception_index, 0, error_code, 0, 0); +} + +void raise_exception_err_ra(CPUX86State *env, int exception_index, + int error_code, uintptr_t retaddr) +{ + raise_interrupt2(env, exception_index, 0, error_code, 0, retaddr); } void raise_exception(CPUX86State *env, int exception_index) { - raise_interrupt2(env, exception_index, 0, 0, 0); + raise_interrupt2(env, exception_index, 0, 0, 0, 0); +} + +void raise_exception_ra(CPUX86State *env, int exception_index, uintptr_t retaddr) +{ + raise_interrupt2(env, exception_index, 0, 0, 0, retaddr); } diff --git a/qemu/target-i386/fpu_helper.c b/qemu/target-i386/fpu_helper.c index 1f954e0c5..fee5573a1 100644 --- a/qemu/target-i386/fpu_helper.c +++ b/qemu/target-i386/fpu_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include #include "cpu.h" #include "exec/helper-proto.h" @@ -67,22 +68,24 @@ static inline void fpop(CPUX86State *env) env->fpstt = (env->fpstt + 1) & 7; } -static inline floatx80 helper_fldt(CPUX86State *env, target_ulong ptr) +static inline floatx80 helper_fldt(CPUX86State *env, target_ulong ptr, + uintptr_t retaddr) { CPU_LDoubleU temp; - temp.l.lower = cpu_ldq_data(env, ptr); - temp.l.upper = cpu_lduw_data(env, ptr + 8); + temp.l.lower = cpu_ldq_data_ra(env, ptr, retaddr); + temp.l.upper = cpu_lduw_data_ra(env, ptr + 8, retaddr); return temp.d; } -static inline void helper_fstt(CPUX86State *env, floatx80 f, target_ulong ptr) +static inline void helper_fstt(CPUX86State *env, floatx80 f, target_ulong ptr, + uintptr_t retaddr) { CPU_LDoubleU temp; temp.d = f; - cpu_stq_data(env, ptr, temp.l.lower); - cpu_stw_data(env, ptr + 8, temp.l.upper); + cpu_stq_data_ra(env, ptr, temp.l.lower, retaddr); + cpu_stw_data_ra(env, ptr + 8, temp.l.upper, retaddr); } /* x87 FPU helpers */ @@ -125,10 +128,10 @@ static inline floatx80 helper_fdiv(CPUX86State *env, floatx80 a, floatx80 b) return floatx80_div(a, b, &env->fp_status); } -static void fpu_raise_exception(CPUX86State *env) +static void fpu_raise_exception(CPUX86State *env, uintptr_t retaddr) { if (env->cr[0] & CR0_NE_MASK) { - raise_exception(env, EXCP10_COPR); + raise_exception_ra(env, EXCP10_COPR, retaddr); } #if !defined(CONFIG_USER_ONLY) else { @@ -313,14 +316,14 @@ void helper_fldt_ST0(CPUX86State *env, target_ulong ptr) int new_fpstt; new_fpstt = (env->fpstt - 1) & 7; - env->fpregs[new_fpstt].d = helper_fldt(env, ptr); + env->fpregs[new_fpstt].d = helper_fldt(env, ptr, GETPC()); env->fpstt = new_fpstt; env->fptags[new_fpstt] = 0; /* validate stack entry */ } void helper_fstt_ST0(CPUX86State *env, target_ulong ptr) { - helper_fstt(env, ST0, ptr); + helper_fstt(env, ST0, ptr, GETPC()); } void helper_fpush(CPUX86State *env) @@ -603,7 +606,7 @@ void helper_fclex(CPUX86State *env) void helper_fwait(CPUX86State *env) { if (env->fpus & FPUS_SE) { - fpu_raise_exception(env); + fpu_raise_exception(env, GETPC()); } } @@ -633,11 +636,11 @@ void helper_fbld_ST0(CPUX86State *env, target_ulong ptr) val = 0; for (i = 8; i >= 0; i--) { - v = cpu_ldub_data(env, ptr + i); + v = cpu_ldub_data_ra(env, ptr + i, GETPC()); val = (val * 100) + ((v >> 4) * 10) + (v & 0xf); } tmp = int64_to_floatx80(val, &env->fp_status); - if (cpu_ldub_data(env, ptr + 9) & 0x80) { + if (cpu_ldub_data_ra(env, ptr + 9, GETPC()) & 0x80) { tmp = floatx80_chs(tmp); } fpush(env); @@ -654,10 +657,10 @@ void helper_fbst_ST0(CPUX86State *env, target_ulong ptr) mem_ref = ptr; mem_end = mem_ref + 9; if (val < 0) { - cpu_stb_data(env, mem_end, 0x80); + cpu_stb_data_ra(env, mem_end, 0x80, GETPC()); val = -val; } else { - cpu_stb_data(env, mem_end, 0x00); + cpu_stb_data_ra(env, mem_end, 0x00, GETPC()); } while (mem_ref < mem_end) { if (val == 0) { @@ -666,10 +669,10 @@ void helper_fbst_ST0(CPUX86State *env, target_ulong ptr) v = val % 100; val = val / 100; v = ((v / 10) << 4) | (v % 10); - cpu_stb_data(env, mem_ref++, v); + cpu_stb_data_ra(env, mem_ref++, v, GETPC()); } while (mem_ref < mem_end) { - cpu_stb_data(env, mem_ref++, 0); + cpu_stb_data_ra(env, mem_ref++, 0, GETPC()); } } @@ -977,7 +980,8 @@ void helper_fxam_ST0(CPUX86State *env) } } -void helper_fstenv(CPUX86State *env, target_ulong ptr, int data32) +static void do_fstenv(CPUX86State *env, target_ulong ptr, int data32, + uintptr_t retaddr) { int fpus, fptag, exp, i; uint64_t mant; @@ -1005,37 +1009,43 @@ void helper_fstenv(CPUX86State *env, target_ulong ptr, int data32) } if (data32) { /* 32 bit */ - cpu_stl_data(env, ptr, env->fpuc); - cpu_stl_data(env, ptr + 4, fpus); - cpu_stl_data(env, ptr + 8, fptag); - cpu_stl_data(env, ptr + 12, 0); /* fpip */ - cpu_stl_data(env, ptr + 16, 0); /* fpcs */ - cpu_stl_data(env, ptr + 20, 0); /* fpoo */ - cpu_stl_data(env, ptr + 24, 0); /* fpos */ + cpu_stl_data_ra(env, ptr, env->fpuc, retaddr); + cpu_stl_data_ra(env, ptr + 4, fpus, retaddr); + cpu_stl_data_ra(env, ptr + 8, fptag, retaddr); + cpu_stl_data_ra(env, ptr + 12, 0, retaddr); /* fpip */ + cpu_stl_data_ra(env, ptr + 16, 0, retaddr); /* fpcs */ + cpu_stl_data_ra(env, ptr + 20, 0, retaddr); /* fpoo */ + cpu_stl_data_ra(env, ptr + 24, 0, retaddr); /* fpos */ } else { /* 16 bit */ - cpu_stw_data(env, ptr, env->fpuc); - cpu_stw_data(env, ptr + 2, fpus); - cpu_stw_data(env, ptr + 4, fptag); - cpu_stw_data(env, ptr + 6, 0); - cpu_stw_data(env, ptr + 8, 0); - cpu_stw_data(env, ptr + 10, 0); - cpu_stw_data(env, ptr + 12, 0); + cpu_stw_data_ra(env, ptr, env->fpuc, retaddr); + cpu_stw_data_ra(env, ptr + 2, fpus, retaddr); + cpu_stw_data_ra(env, ptr + 4, fptag, retaddr); + cpu_stw_data_ra(env, ptr + 6, 0, retaddr); + cpu_stw_data_ra(env, ptr + 8, 0, retaddr); + cpu_stw_data_ra(env, ptr + 10, 0, retaddr); + cpu_stw_data_ra(env, ptr + 12, 0, retaddr); } } -void helper_fldenv(CPUX86State *env, target_ulong ptr, int data32) +void helper_fstenv(CPUX86State *env, target_ulong ptr, int data32) +{ + do_fstenv(env, ptr, data32, GETPC()); +} + +static void do_fldenv(CPUX86State *env, target_ulong ptr, int data32, + uintptr_t retaddr) { int i, fpus, fptag; if (data32) { - cpu_set_fpuc(env, cpu_lduw_data(env, ptr)); - fpus = cpu_lduw_data(env, ptr + 4); - fptag = cpu_lduw_data(env, ptr + 8); + cpu_set_fpuc(env, cpu_lduw_data_ra(env, ptr, retaddr)); + fpus = cpu_lduw_data_ra(env, ptr + 4, retaddr); + fptag = cpu_lduw_data_ra(env, ptr + 8, retaddr); } else { - cpu_set_fpuc(env, cpu_lduw_data(env, ptr)); - fpus = cpu_lduw_data(env, ptr + 2); - fptag = cpu_lduw_data(env, ptr + 4); + cpu_set_fpuc(env, cpu_lduw_data_ra(env, ptr, retaddr)); + fpus = cpu_lduw_data_ra(env, ptr + 2, retaddr); + fptag = cpu_lduw_data_ra(env, ptr + 4, retaddr); } env->fpstt = (fpus >> 11) & 7; env->fpus = fpus & ~0x3800; @@ -1045,17 +1055,22 @@ void helper_fldenv(CPUX86State *env, target_ulong ptr, int data32) } } +void helper_fldenv(CPUX86State *env, target_ulong ptr, int data32) +{ + do_fldenv(env, ptr, data32, GETPC()); +} + void helper_fsave(CPUX86State *env, target_ulong ptr, int data32) { floatx80 tmp; int i; - helper_fstenv(env, ptr, data32); + do_fstenv(env, ptr, data32, GETPC()); ptr += (14 << data32); for (i = 0; i < 8; i++) { tmp = ST(i); - helper_fstt(env, tmp, ptr); + helper_fstt(env, tmp, ptr, GETPC()); ptr += 10; } @@ -1078,11 +1093,11 @@ void helper_frstor(CPUX86State *env, target_ulong ptr, int data32) floatx80 tmp; int i; - helper_fldenv(env, ptr, data32); + do_fldenv(env, ptr, data32, GETPC()); ptr += (14 << data32); for (i = 0; i < 8; i++) { - tmp = helper_fldt(env, ptr); + tmp = helper_fldt(env, ptr, GETPC()); ST(i) = tmp; ptr += 10; } @@ -1100,82 +1115,183 @@ void cpu_x86_frstor(CPUX86State *env, target_ulong ptr, int data32) } #endif -void helper_fxsave(CPUX86State *env, target_ulong ptr, int data64) +static void do_xsave_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) { - int fpus, fptag, i, nb_xmm_regs; - floatx80 tmp; + int fpus, fptag, i; target_ulong addr; - /* The operand must be 16 byte aligned */ - if (ptr & 0xf) { - raise_exception(env, EXCP0D_GPF); - } - fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; fptag = 0; for (i = 0; i < 8; i++) { fptag |= (env->fptags[i] << i); } - cpu_stw_data(env, ptr, env->fpuc); - cpu_stw_data(env, ptr + 2, fpus); - cpu_stw_data(env, ptr + 4, fptag ^ 0xff); -#ifdef TARGET_X86_64 - if (data64) { - cpu_stq_data(env, ptr + 0x08, 0); /* rip */ - cpu_stq_data(env, ptr + 0x10, 0); /* rdp */ - } else -#endif - { - cpu_stl_data(env, ptr + 0x08, 0); /* eip */ - cpu_stl_data(env, ptr + 0x0c, 0); /* sel */ - cpu_stl_data(env, ptr + 0x10, 0); /* dp */ - cpu_stl_data(env, ptr + 0x14, 0); /* sel */ - } + cpu_stw_data_ra(env, ptr, env->fpuc, ra); + cpu_stw_data_ra(env, ptr + 2, fpus, ra); + cpu_stw_data_ra(env, ptr + 4, fptag ^ 0xff, ra); + + /* In 32-bit mode this is eip, sel, dp, sel. + In 64-bit mode this is rip, rdp. + But in either case we don't write actual data, just zeros. */ + cpu_stq_data_ra(env, ptr + 0x08, 0, ra); /* eip+sel; rip */ + cpu_stq_data_ra(env, ptr + 0x10, 0, ra); /* edp+sel; rdp */ addr = ptr + 0x20; for (i = 0; i < 8; i++) { - tmp = ST(i); - helper_fstt(env, tmp, addr); + floatx80 tmp = ST(i); + helper_fstt(env, tmp, addr, ra); addr += 16; } +} + +static void do_xsave_mxcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + cpu_stl_data_ra(env, ptr + 0x18, env->mxcsr, ra); /* mxcsr */ + cpu_stl_data_ra(env, ptr + 0x1c, 0x0000ffff, ra); /* mxcsr_mask */ +} + +static void do_xsave_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + int i, nb_xmm_regs; + target_ulong addr; + + if (env->hflags & HF_CS64_MASK) { + nb_xmm_regs = 16; + } else { + nb_xmm_regs = 8; + } + + addr = ptr + 0xa0; + for (i = 0; i < nb_xmm_regs; i++) { + cpu_stq_data_ra(env, addr, env->xmm_regs[i].ZMM_Q(0), ra); + cpu_stq_data_ra(env, addr + 8, env->xmm_regs[i].ZMM_Q(1), ra); + addr += 16; + } +} + +static void do_xsave_bndregs(CPUX86State *env, target_ulong addr, uintptr_t ra) +{ + int i; + + for (i = 0; i < 4; i++, addr += 16) { + cpu_stq_data_ra(env, addr, env->bnd_regs[i].lb, ra); + cpu_stq_data_ra(env, addr + 8, env->bnd_regs[i].ub, ra); + } +} + +static void do_xsave_bndcsr(CPUX86State *env, target_ulong addr, uintptr_t ra) +{ + cpu_stq_data_ra(env, addr, env->bndcs_regs.cfgu, ra); + cpu_stq_data_ra(env, addr + 8, env->bndcs_regs.sts, ra); +} + +static void do_xsave_pkru(CPUX86State *env, target_ulong addr, uintptr_t ra) +{ + cpu_stq_data_ra(env, addr, env->pkru, ra); +} + +void helper_fxsave(CPUX86State *env, target_ulong ptr) +{ + uintptr_t ra = GETPC(); + + /* The operand must be 16 byte aligned */ + if (ptr & 0xf) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + do_xsave_fpu(env, ptr, ra); if (env->cr[4] & CR4_OSFXSR_MASK) { - /* XXX: finish it */ - cpu_stl_data(env, ptr + 0x18, env->mxcsr); /* mxcsr */ - cpu_stl_data(env, ptr + 0x1c, 0x0000ffff); /* mxcsr_mask */ - if (env->hflags & HF_CS64_MASK) { - nb_xmm_regs = 16; - } else { - nb_xmm_regs = 8; - } - addr = ptr + 0xa0; + do_xsave_mxcsr(env, ptr, ra); /* Fast FXSAVE leaves out the XMM registers */ if (!(env->efer & MSR_EFER_FFXSR) || (env->hflags & HF_CPL_MASK) || !(env->hflags & HF_LMA_MASK)) { - for (i = 0; i < nb_xmm_regs; i++) { - cpu_stq_data(env, addr, env->xmm_regs[i].XMM_Q(0)); - cpu_stq_data(env, addr + 8, env->xmm_regs[i].XMM_Q(1)); - addr += 16; - } + do_xsave_sse(env, ptr, ra); } } } -void helper_fxrstor(CPUX86State *env, target_ulong ptr, int data64) +static uint64_t get_xinuse(CPUX86State *env) { - int i, fpus, fptag, nb_xmm_regs; - floatx80 tmp; - target_ulong addr; + uint64_t inuse = -1; - /* The operand must be 16 byte aligned */ - if (ptr & 0xf) { - raise_exception(env, EXCP0D_GPF); + /* For the most part, we don't track XINUSE. We could calculate it + here for all components, but it's probably less work to simply + indicate in use. That said, the state of BNDREGS is important + enough to track in HFLAGS, so we might as well use that here. */ + if ((env->hflags & HF_MPX_IU_MASK) == 0) { + inuse &= ~XSTATE_BNDREGS_MASK; + } + return inuse; +} + +static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm, + uint64_t inuse, uint64_t opt, uintptr_t ra) +{ + uint64_t old_bv, new_bv; + + /* The OS must have enabled XSAVE. */ + if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { + raise_exception_ra(env, EXCP06_ILLOP, ra); + } + + /* The operand must be 64 byte aligned. */ + if (ptr & 63) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + /* Never save anything not enabled by XCR0. */ + rfbm &= env->xcr0; + opt &= rfbm; + + if (opt & XSTATE_FP_MASK) { + do_xsave_fpu(env, ptr, ra); + } + if (rfbm & XSTATE_SSE_MASK) { + /* Note that saving MXCSR is not suppressed by XSAVEOPT. */ + do_xsave_mxcsr(env, ptr, ra); + } + if (opt & XSTATE_SSE_MASK) { + do_xsave_sse(env, ptr, ra); + } + if (opt & XSTATE_BNDREGS_MASK) { + target_ulong off = x86_ext_save_areas[XSTATE_BNDREGS_BIT].offset; + do_xsave_bndregs(env, ptr + off, ra); + } + if (opt & XSTATE_BNDCSR_MASK) { + target_ulong off = x86_ext_save_areas[XSTATE_BNDCSR_BIT].offset; + do_xsave_bndcsr(env, ptr + off, ra); } + if (opt & XSTATE_PKRU_MASK) { + target_ulong off = x86_ext_save_areas[XSTATE_PKRU_BIT].offset; + do_xsave_pkru(env, ptr + off, ra); + } + + /* Update the XSTATE_BV field. */ + old_bv = cpu_ldq_data_ra(env, ptr + 512, ra); + new_bv = (old_bv & ~rfbm) | (inuse & rfbm); + cpu_stq_data_ra(env, ptr + 512, new_bv, ra); +} + +void helper_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm) +{ + do_xsave(env, ptr, rfbm, get_xinuse(env), -1, GETPC()); +} + +void helper_xsaveopt(CPUX86State *env, target_ulong ptr, uint64_t rfbm) +{ + uint64_t inuse = get_xinuse(env); + do_xsave(env, ptr, rfbm, inuse, inuse, GETPC()); +} - cpu_set_fpuc(env, cpu_lduw_data(env, ptr)); - fpus = cpu_lduw_data(env, ptr + 2); - fptag = cpu_lduw_data(env, ptr + 4); +static void do_xrstor_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + int i, fpus, fptag; + target_ulong addr; + + cpu_set_fpuc(env, cpu_lduw_data_ra(env, ptr, ra)); + fpus = cpu_lduw_data_ra(env, ptr + 2, ra); + fptag = cpu_lduw_data_ra(env, ptr + 4, ra); env->fpstt = (fpus >> 11) & 7; env->fpus = fpus & ~0x3800; fptag ^= 0xff; @@ -1185,34 +1301,227 @@ void helper_fxrstor(CPUX86State *env, target_ulong ptr, int data64) addr = ptr + 0x20; for (i = 0; i < 8; i++) { - tmp = helper_fldt(env, addr); + floatx80 tmp = helper_fldt(env, addr, ra); ST(i) = tmp; addr += 16; } +} + +static void do_xrstor_mxcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + cpu_set_mxcsr(env, cpu_ldl_data_ra(env, ptr + 0x18, ra)); +} + +static void do_xrstor_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + int i, nb_xmm_regs; + target_ulong addr; + + if (env->hflags & HF_CS64_MASK) { + nb_xmm_regs = 16; + } else { + nb_xmm_regs = 8; + } + + addr = ptr + 0xa0; + for (i = 0; i < nb_xmm_regs; i++) { + env->xmm_regs[i].ZMM_Q(0) = cpu_ldq_data_ra(env, addr, ra); + env->xmm_regs[i].ZMM_Q(1) = cpu_ldq_data_ra(env, addr + 8, ra); + addr += 16; + } +} + +static void do_xrstor_bndregs(CPUX86State *env, target_ulong addr, uintptr_t ra) +{ + int i; + + for (i = 0; i < 4; i++, addr += 16) { + env->bnd_regs[i].lb = cpu_ldq_data_ra(env, addr, ra); + env->bnd_regs[i].ub = cpu_ldq_data_ra(env, addr + 8, ra); + } +} + +static void do_xrstor_bndcsr(CPUX86State *env, target_ulong addr, uintptr_t ra) +{ + /* FIXME: Extend highest implemented bit of linear address. */ + env->bndcs_regs.cfgu = cpu_ldq_data_ra(env, addr, ra); + env->bndcs_regs.sts = cpu_ldq_data_ra(env, addr + 8, ra); +} + +static void do_xrstor_pkru(CPUX86State *env, target_ulong addr, uintptr_t ra) +{ + env->pkru = cpu_ldq_data_ra(env, addr, ra); +} + +void helper_fxrstor(CPUX86State *env, target_ulong ptr) +{ + uintptr_t ra = GETPC(); + + /* The operand must be 16 byte aligned */ + if (ptr & 0xf) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + do_xrstor_fpu(env, ptr, ra); if (env->cr[4] & CR4_OSFXSR_MASK) { - /* XXX: finish it */ - cpu_set_mxcsr(env, cpu_ldl_data(env, ptr + 0x18)); - /* cpu_ldl_data(env, ptr + 0x1c); */ - if (env->hflags & HF_CS64_MASK) { - nb_xmm_regs = 16; - } else { - nb_xmm_regs = 8; - } - addr = ptr + 0xa0; - /* Fast FXRESTORE leaves out the XMM registers */ + do_xrstor_mxcsr(env, ptr, ra); + /* Fast FXRSTOR leaves out the XMM registers */ if (!(env->efer & MSR_EFER_FFXSR) || (env->hflags & HF_CPL_MASK) || !(env->hflags & HF_LMA_MASK)) { - for (i = 0; i < nb_xmm_regs; i++) { - env->xmm_regs[i].XMM_Q(0) = cpu_ldq_data(env, addr); - env->xmm_regs[i].XMM_Q(1) = cpu_ldq_data(env, addr + 8); - addr += 16; - } + do_xrstor_sse(env, ptr, ra); + } + } +} + +void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm) +{ + uintptr_t ra = GETPC(); + uint64_t xstate_bv, xcomp_bv0, xcomp_bv1; + + rfbm &= env->xcr0; + + /* The OS must have enabled XSAVE. */ + if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { + raise_exception_ra(env, EXCP06_ILLOP, ra); + } + + /* The operand must be 64 byte aligned. */ + if (ptr & 63) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + xstate_bv = cpu_ldq_data_ra(env, ptr + 512, ra); + + if ((int64_t)xstate_bv < 0) { + /* FIXME: Compact form. */ + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + /* Standard form. */ + + /* The XSTATE field must not set bits not present in XCR0. */ + if (xstate_bv & ~env->xcr0) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + /* The XCOMP field must be zero. */ + xcomp_bv0 = cpu_ldq_data_ra(env, ptr + 520, ra); + xcomp_bv1 = cpu_ldq_data_ra(env, ptr + 528, ra); + if (xcomp_bv0 || xcomp_bv1) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + if (rfbm & XSTATE_FP_MASK) { + if (xstate_bv & XSTATE_FP_MASK) { + do_xrstor_fpu(env, ptr, ra); + } else { + helper_fninit(env); + memset(env->fpregs, 0, sizeof(env->fpregs)); + } + } + if (rfbm & XSTATE_SSE_MASK) { + /* Note that the standard form of XRSTOR loads MXCSR from memory + whether or not the XSTATE_BV bit is set. */ + do_xrstor_mxcsr(env, ptr, ra); + if (xstate_bv & XSTATE_SSE_MASK) { + do_xrstor_sse(env, ptr, ra); + } else { + /* ??? When AVX is implemented, we may have to be more + selective in the clearing. */ + memset(env->xmm_regs, 0, sizeof(env->xmm_regs)); + } + } + if (rfbm & XSTATE_BNDREGS_MASK) { + if (xstate_bv & XSTATE_BNDREGS_MASK) { + target_ulong off = x86_ext_save_areas[XSTATE_BNDREGS_BIT].offset; + do_xrstor_bndregs(env, ptr + off, ra); + env->hflags |= HF_MPX_IU_MASK; + } else { + memset(env->bnd_regs, 0, sizeof(env->bnd_regs)); + env->hflags &= ~HF_MPX_IU_MASK; + } + } + if (rfbm & XSTATE_BNDCSR_MASK) { + if (xstate_bv & XSTATE_BNDCSR_MASK) { + target_ulong off = x86_ext_save_areas[XSTATE_BNDCSR_BIT].offset; + do_xrstor_bndcsr(env, ptr + off, ra); + } else { + memset(&env->bndcs_regs, 0, sizeof(env->bndcs_regs)); + } + cpu_sync_bndcs_hflags(env); + } + if (rfbm & XSTATE_PKRU_MASK) { + uint64_t old_pkru = env->pkru; + if (xstate_bv & XSTATE_PKRU_MASK) { + target_ulong off = x86_ext_save_areas[XSTATE_PKRU_BIT].offset; + do_xrstor_pkru(env, ptr + off, ra); + } else { + env->pkru = 0; + } + if (env->pkru != old_pkru) { + CPUState *cs = CPU(x86_env_get_cpu(env)); + tlb_flush(cs, 1); } } } +uint64_t helper_xgetbv(CPUX86State *env, uint32_t ecx) +{ + /* The OS must have enabled XSAVE. */ + if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { + raise_exception_ra(env, EXCP06_ILLOP, GETPC()); + } + + switch (ecx) { + case 0: + return env->xcr0; + case 1: + if (env->features[FEAT_XSAVE] & CPUID_XSAVE_XGETBV1) { + return env->xcr0 & get_xinuse(env); + } + break; + } + raise_exception_ra(env, EXCP0D_GPF, GETPC()); +} + +void helper_xsetbv(CPUX86State *env, uint32_t ecx, uint64_t mask) +{ + uint32_t dummy, ena_lo, ena_hi; + uint64_t ena; + + /* The OS must have enabled XSAVE. */ + if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { + raise_exception_ra(env, EXCP06_ILLOP, GETPC()); + } + + /* Only XCR0 is defined at present; the FPU may not be disabled. */ + if (ecx != 0 || (mask & XSTATE_FP_MASK) == 0) { + goto do_gpf; + } + + /* Disallow enabling unimplemented features. */ + cpu_x86_cpuid(env, 0x0d, 0, &ena_lo, &dummy, &dummy, &ena_hi); + ena = ((uint64_t)ena_hi << 32) | ena_lo; + if (mask & ~ena) { + goto do_gpf; + } + + /* Disallow enabling only half of MPX. */ + if ((mask ^ (mask * (XSTATE_BNDCSR_MASK / XSTATE_BNDREGS_MASK))) + & XSTATE_BNDCSR_MASK) { + goto do_gpf; + } + + env->xcr0 = mask; + cpu_sync_bndcs_hflags(env); + return; + + do_gpf: + raise_exception_ra(env, EXCP0D_GPF, GETPC()); +} + void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, floatx80 f) { CPU_LDoubleU temp; diff --git a/qemu/target-i386/gdbstub.c b/qemu/target-i386/gdbstub.c index ff99cfb00..4b5071398 100644 --- a/qemu/target-i386/gdbstub.c +++ b/qemu/target-i386/gdbstub.c @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "exec/gdbstub.h" @@ -61,8 +61,8 @@ int x86_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) n -= IDX_XMM_REGS; if (n < CPU_NB_REGS32 || (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK)) { - stq_p(mem_buf, env->xmm_regs[n].XMM_Q(0)); - stq_p(mem_buf + 8, env->xmm_regs[n].XMM_Q(1)); + stq_p(mem_buf, env->xmm_regs[n].ZMM_Q(0)); + stq_p(mem_buf + 8, env->xmm_regs[n].ZMM_Q(1)); return 16; } } else { @@ -170,8 +170,8 @@ int x86_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) n -= IDX_XMM_REGS; if (n < CPU_NB_REGS32 || (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK)) { - env->xmm_regs[n].XMM_Q(0) = ldq_p(mem_buf); - env->xmm_regs[n].XMM_Q(1) = ldq_p(mem_buf + 8); + env->xmm_regs[n].ZMM_Q(0) = ldq_p(mem_buf); + env->xmm_regs[n].ZMM_Q(1) = ldq_p(mem_buf + 8); return 16; } } else { diff --git a/qemu/target-i386/helper.c b/qemu/target-i386/helper.c index 5480a96a0..bf3e76207 100644 --- a/qemu/target-i386/helper.c +++ b/qemu/target-i386/helper.c @@ -17,12 +17,14 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "sysemu/kvm.h" #include "kvm_i386.h" #ifndef CONFIG_USER_ONLY #include "sysemu/sysemu.h" #include "monitor/monitor.h" +#include "hw/i386/apic_internal.h" #endif static void cpu_x86_version(CPUX86State *env, int *family, int *model) @@ -177,6 +179,196 @@ done: cpu_fprintf(f, "\n"); } +#ifndef CONFIG_USER_ONLY + +/* ARRAY_SIZE check is not required because + * DeliveryMode(dm) has a size of 3 bit. + */ +static inline const char *dm2str(uint32_t dm) +{ + static const char *str[] = { + "Fixed", + "...", + "SMI", + "...", + "NMI", + "INIT", + "...", + "ExtINT" + }; + return str[dm]; +} + +static void dump_apic_lvt(FILE *f, fprintf_function cpu_fprintf, + const char *name, uint32_t lvt, bool is_timer) +{ + uint32_t dm = (lvt & APIC_LVT_DELIV_MOD) >> APIC_LVT_DELIV_MOD_SHIFT; + cpu_fprintf(f, + "%s\t 0x%08x %s %-5s %-6s %-7s %-12s %-6s", + name, lvt, + lvt & APIC_LVT_INT_POLARITY ? "active-lo" : "active-hi", + lvt & APIC_LVT_LEVEL_TRIGGER ? "level" : "edge", + lvt & APIC_LVT_MASKED ? "masked" : "", + lvt & APIC_LVT_DELIV_STS ? "pending" : "", + !is_timer ? + "" : lvt & APIC_LVT_TIMER_PERIODIC ? + "periodic" : lvt & APIC_LVT_TIMER_TSCDEADLINE ? + "tsc-deadline" : "one-shot", + dm2str(dm)); + if (dm != APIC_DM_NMI) { + cpu_fprintf(f, " (vec %u)\n", lvt & APIC_VECTOR_MASK); + } else { + cpu_fprintf(f, "\n"); + } +} + +/* ARRAY_SIZE check is not required because + * destination shorthand has a size of 2 bit. + */ +static inline const char *shorthand2str(uint32_t shorthand) +{ + const char *str[] = { + "no-shorthand", "self", "all-self", "all" + }; + return str[shorthand]; +} + +static inline uint8_t divider_conf(uint32_t divide_conf) +{ + uint8_t divide_val = ((divide_conf & 0x8) >> 1) | (divide_conf & 0x3); + + return divide_val == 7 ? 1 : 2 << divide_val; +} + +static inline void mask2str(char *str, uint32_t val, uint8_t size) +{ + while (size--) { + *str++ = (val >> size) & 1 ? '1' : '0'; + } + *str = 0; +} + +#define MAX_LOGICAL_APIC_ID_MASK_SIZE 16 + +static void dump_apic_icr(FILE *f, fprintf_function cpu_fprintf, + APICCommonState *s, CPUX86State *env) +{ + uint32_t icr = s->icr[0], icr2 = s->icr[1]; + uint8_t dest_shorthand = \ + (icr & APIC_ICR_DEST_SHORT) >> APIC_ICR_DEST_SHORT_SHIFT; + bool logical_mod = icr & APIC_ICR_DEST_MOD; + char apic_id_str[MAX_LOGICAL_APIC_ID_MASK_SIZE + 1]; + uint32_t dest_field; + bool x2apic; + + cpu_fprintf(f, "ICR\t 0x%08x %s %s %s %s\n", + icr, + logical_mod ? "logical" : "physical", + icr & APIC_ICR_TRIGGER_MOD ? "level" : "edge", + icr & APIC_ICR_LEVEL ? "assert" : "de-assert", + shorthand2str(dest_shorthand)); + + cpu_fprintf(f, "ICR2\t 0x%08x", icr2); + if (dest_shorthand != 0) { + cpu_fprintf(f, "\n"); + return; + } + x2apic = env->features[FEAT_1_ECX] & CPUID_EXT_X2APIC; + dest_field = x2apic ? icr2 : icr2 >> APIC_ICR_DEST_SHIFT; + + if (!logical_mod) { + if (x2apic) { + cpu_fprintf(f, " cpu %u (X2APIC ID)\n", dest_field); + } else { + cpu_fprintf(f, " cpu %u (APIC ID)\n", + dest_field & APIC_LOGDEST_XAPIC_ID); + } + return; + } + + if (s->dest_mode == 0xf) { /* flat mode */ + mask2str(apic_id_str, icr2 >> APIC_ICR_DEST_SHIFT, 8); + cpu_fprintf(f, " mask %s (APIC ID)\n", apic_id_str); + } else if (s->dest_mode == 0) { /* cluster mode */ + if (x2apic) { + mask2str(apic_id_str, dest_field & APIC_LOGDEST_X2APIC_ID, 16); + cpu_fprintf(f, " cluster %u mask %s (X2APIC ID)\n", + dest_field >> APIC_LOGDEST_X2APIC_SHIFT, apic_id_str); + } else { + mask2str(apic_id_str, dest_field & APIC_LOGDEST_XAPIC_ID, 4); + cpu_fprintf(f, " cluster %u mask %s (APIC ID)\n", + dest_field >> APIC_LOGDEST_XAPIC_SHIFT, apic_id_str); + } + } +} + +static void dump_apic_interrupt(FILE *f, fprintf_function cpu_fprintf, + const char *name, uint32_t *ireg_tab, + uint32_t *tmr_tab) +{ + int i, empty = true; + + cpu_fprintf(f, "%s\t ", name); + for (i = 0; i < 256; i++) { + if (apic_get_bit(ireg_tab, i)) { + cpu_fprintf(f, "%u%s ", i, + apic_get_bit(tmr_tab, i) ? "(level)" : ""); + empty = false; + } + } + cpu_fprintf(f, "%s\n", empty ? "(none)" : ""); +} + +void x86_cpu_dump_local_apic_state(CPUState *cs, FILE *f, + fprintf_function cpu_fprintf, int flags) +{ + X86CPU *cpu = X86_CPU(cs); + APICCommonState *s = APIC_COMMON(cpu->apic_state); + uint32_t *lvt = s->lvt; + + cpu_fprintf(f, "dumping local APIC state for CPU %-2u\n\n", + CPU(cpu)->cpu_index); + dump_apic_lvt(f, cpu_fprintf, "LVT0", lvt[APIC_LVT_LINT0], false); + dump_apic_lvt(f, cpu_fprintf, "LVT1", lvt[APIC_LVT_LINT1], false); + dump_apic_lvt(f, cpu_fprintf, "LVTPC", lvt[APIC_LVT_PERFORM], false); + dump_apic_lvt(f, cpu_fprintf, "LVTERR", lvt[APIC_LVT_ERROR], false); + dump_apic_lvt(f, cpu_fprintf, "LVTTHMR", lvt[APIC_LVT_THERMAL], false); + dump_apic_lvt(f, cpu_fprintf, "LVTT", lvt[APIC_LVT_TIMER], true); + + cpu_fprintf(f, "Timer\t DCR=0x%x (divide by %u) initial_count = %u\n", + s->divide_conf & APIC_DCR_MASK, + divider_conf(s->divide_conf), + s->initial_count); + + cpu_fprintf(f, "SPIV\t 0x%08x APIC %s, focus=%s, spurious vec %u\n", + s->spurious_vec, + s->spurious_vec & APIC_SPURIO_ENABLED ? "enabled" : "disabled", + s->spurious_vec & APIC_SPURIO_FOCUS ? "on" : "off", + s->spurious_vec & APIC_VECTOR_MASK); + + dump_apic_icr(f, cpu_fprintf, s, &cpu->env); + + cpu_fprintf(f, "ESR\t 0x%08x\n", s->esr); + + dump_apic_interrupt(f, cpu_fprintf, "ISR", s->isr, s->tmr); + dump_apic_interrupt(f, cpu_fprintf, "IRR", s->irr, s->tmr); + + cpu_fprintf(f, "\nAPR 0x%02x TPR 0x%02x DFR 0x%02x LDR 0x%02x", + s->arb_id, s->tpr, s->dest_mode, s->log_dest); + if (s->dest_mode == 0) { + cpu_fprintf(f, "(cluster %u: id %u)", + s->log_dest >> APIC_LOGDEST_XAPIC_SHIFT, + s->log_dest & APIC_LOGDEST_XAPIC_ID); + } + cpu_fprintf(f, " PPR 0x%02x\n", apic_get_ppr(s)); +} +#else +void x86_cpu_dump_local_apic_state(CPUState *cs, FILE *f, + fprintf_function cpu_fprintf, int flags) +{ +} +#endif /* !CONFIG_USER_ONLY */ + #define DUMP_CODE_BYTES_TOTAL 50 #define DUMP_CODE_BYTES_BACKWARD 20 @@ -344,10 +536,10 @@ void x86_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, for(i=0;ixmm_regs[i].XMM_L(3), - env->xmm_regs[i].XMM_L(2), - env->xmm_regs[i].XMM_L(1), - env->xmm_regs[i].XMM_L(0)); + env->xmm_regs[i].ZMM_L(3), + env->xmm_regs[i].ZMM_L(2), + env->xmm_regs[i].ZMM_L(1), + env->xmm_regs[i].ZMM_L(0)); if ((i & 1) == 1) cpu_fprintf(f, "\n"); else @@ -455,6 +647,7 @@ void cpu_x86_update_cr3(CPUX86State *env, target_ulong new_cr3) void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4) { X86CPU *cpu = x86_env_get_cpu(env); + uint32_t hflags; #if defined(DEBUG_MMU) printf("CR4 update: CR4=%08x\n", (uint32_t)env->cr[4]); @@ -464,24 +657,33 @@ void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4) CR4_SMEP_MASK | CR4_SMAP_MASK)) { tlb_flush(CPU(cpu), 1); } + + /* Clear bits we're going to recompute. */ + hflags = env->hflags & ~(HF_OSFXSR_MASK | HF_SMAP_MASK); + /* SSE handling */ if (!(env->features[FEAT_1_EDX] & CPUID_SSE)) { new_cr4 &= ~CR4_OSFXSR_MASK; } - env->hflags &= ~HF_OSFXSR_MASK; if (new_cr4 & CR4_OSFXSR_MASK) { - env->hflags |= HF_OSFXSR_MASK; + hflags |= HF_OSFXSR_MASK; } if (!(env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_SMAP)) { new_cr4 &= ~CR4_SMAP_MASK; } - env->hflags &= ~HF_SMAP_MASK; if (new_cr4 & CR4_SMAP_MASK) { - env->hflags |= HF_SMAP_MASK; + hflags |= HF_SMAP_MASK; + } + + if (!(env->features[FEAT_7_0_ECX] & CPUID_7_0_ECX_PKU)) { + new_cr4 &= ~CR4_PKE_MASK; } env->cr[4] = new_cr4; + env->hflags = hflags; + + cpu_sync_bndcs_hflags(env); } #if defined(CONFIG_USER_ONLY) @@ -669,7 +871,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, /* Bits 20-13 provide bits 39-32 of the address, bit 21 is reserved. * Leave bits 20-13 in place for setting accessed/dirty bits below. */ - pte = pde | ((pde & 0x1fe000) << (32 - 13)); + pte = pde | ((pde & 0x1fe000LL) << (32 - 13)); rsvd_mask = 0x200000; goto do_check_protect_pse36; } @@ -699,38 +901,50 @@ do_check_protect_pse36: goto do_fault_rsvd; } ptep ^= PG_NX_MASK; - if ((ptep & PG_NX_MASK) && is_write1 == 2) { + + /* can the page can be put in the TLB? prot will tell us */ + if (is_user && !(ptep & PG_USER_MASK)) { goto do_fault_protect; } - switch (mmu_idx) { - case MMU_USER_IDX: - if (!(ptep & PG_USER_MASK)) { - goto do_fault_protect; - } - if (is_write && !(ptep & PG_RW_MASK)) { - goto do_fault_protect; - } - break; - case MMU_KSMAP_IDX: - if (is_write1 != 2 && (ptep & PG_USER_MASK)) { - goto do_fault_protect; + prot = 0; + if (mmu_idx != MMU_KSMAP_IDX || !(ptep & PG_USER_MASK)) { + prot |= PAGE_READ; + if ((ptep & PG_RW_MASK) || (!is_user && !(env->cr[0] & CR0_WP_MASK))) { + prot |= PAGE_WRITE; } - /* fall through */ - case MMU_KNOSMAP_IDX: - if (is_write1 == 2 && (env->cr[4] & CR4_SMEP_MASK) && - (ptep & PG_USER_MASK)) { - goto do_fault_protect; + } + if (!(ptep & PG_NX_MASK) && + (mmu_idx == MMU_USER_IDX || + !((env->cr[4] & CR4_SMEP_MASK) && (ptep & PG_USER_MASK)))) { + prot |= PAGE_EXEC; + } + if ((env->cr[4] & CR4_PKE_MASK) && (env->hflags & HF_LMA_MASK) && + (ptep & PG_USER_MASK) && env->pkru) { + uint32_t pk = (pte & PG_PKRU_MASK) >> PG_PKRU_BIT; + uint32_t pkru_ad = (env->pkru >> pk * 2) & 1; + uint32_t pkru_wd = (env->pkru >> pk * 2) & 2; + uint32_t pkru_prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + + if (pkru_ad) { + pkru_prot &= ~(PAGE_READ | PAGE_WRITE); + } else if (pkru_wd && (is_user || env->cr[0] & CR0_WP_MASK)) { + pkru_prot &= ~PAGE_WRITE; } - if ((env->cr[0] & CR0_WP_MASK) && - is_write && !(ptep & PG_RW_MASK)) { + + prot &= pkru_prot; + if ((pkru_prot & (1 << is_write1)) == 0) { + assert(is_write1 != 2); + error_code |= PG_ERROR_PK_MASK; goto do_fault_protect; } - break; + } - default: /* cannot happen */ - break; + if ((prot & (1 << is_write1)) == 0) { + goto do_fault_protect; } + + /* yes, it can! */ is_dirty = is_write && !(pte & PG_DIRTY_MASK); if (!(pte & PG_ACCESSED_MASK) || is_dirty) { pte |= PG_ACCESSED_MASK; @@ -740,25 +954,13 @@ do_check_protect_pse36: x86_stl_phys_notdirty(cs, pte_addr, pte); } - /* the page can be put in the TLB */ - prot = PAGE_READ; - if (!(ptep & PG_NX_MASK) && - (mmu_idx == MMU_USER_IDX || - !((env->cr[4] & CR4_SMEP_MASK) && (ptep & PG_USER_MASK)))) { - prot |= PAGE_EXEC; - } - if (pte & PG_DIRTY_MASK) { + if (!(pte & PG_DIRTY_MASK)) { /* only set write access if already dirty... otherwise wait for dirty access */ - if (is_user) { - if (ptep & PG_RW_MASK) - prot |= PAGE_WRITE; - } else { - if (!(env->cr[0] & CR0_WP_MASK) || - (ptep & PG_RW_MASK)) - prot |= PAGE_WRITE; - } + assert(!is_write); + prot &= ~PAGE_WRITE; } + do_mapping: pte = pte & env->a20_mask; @@ -771,6 +973,7 @@ do_check_protect_pse36: page_offset = vaddr & (page_size - 1); paddr = pte + page_offset; + assert(prot & (1 << is_write1)); tlb_set_page_with_attrs(cs, vaddr, paddr, cpu_get_mem_attrs(env), prot, mmu_idx, page_size); return 0; @@ -883,7 +1086,7 @@ hwaddr x86_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) if (!(pde & PG_PRESENT_MASK)) return -1; if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { - pte = pde | ((pde & 0x1fe000) << (32 - 13)); + pte = pde | ((pde & 0x1fe000LL) << (32 - 13)); page_size = 4096 * 1024; } else { /* page directory entry */ @@ -905,134 +1108,6 @@ out: return pte | page_offset; } -void hw_breakpoint_insert(CPUX86State *env, int index) -{ - CPUState *cs = CPU(x86_env_get_cpu(env)); - int type = 0, err = 0; - - switch (hw_breakpoint_type(env->dr[7], index)) { - case DR7_TYPE_BP_INST: - if (hw_breakpoint_enabled(env->dr[7], index)) { - err = cpu_breakpoint_insert(cs, env->dr[index], BP_CPU, - &env->cpu_breakpoint[index]); - } - break; - case DR7_TYPE_DATA_WR: - type = BP_CPU | BP_MEM_WRITE; - break; - case DR7_TYPE_IO_RW: - /* No support for I/O watchpoints yet */ - break; - case DR7_TYPE_DATA_RW: - type = BP_CPU | BP_MEM_ACCESS; - break; - } - - if (type != 0) { - err = cpu_watchpoint_insert(cs, env->dr[index], - hw_breakpoint_len(env->dr[7], index), - type, &env->cpu_watchpoint[index]); - } - - if (err) { - env->cpu_breakpoint[index] = NULL; - } -} - -void hw_breakpoint_remove(CPUX86State *env, int index) -{ - CPUState *cs; - - if (!env->cpu_breakpoint[index]) { - return; - } - cs = CPU(x86_env_get_cpu(env)); - switch (hw_breakpoint_type(env->dr[7], index)) { - case DR7_TYPE_BP_INST: - if (hw_breakpoint_enabled(env->dr[7], index)) { - cpu_breakpoint_remove_by_ref(cs, env->cpu_breakpoint[index]); - } - break; - case DR7_TYPE_DATA_WR: - case DR7_TYPE_DATA_RW: - cpu_watchpoint_remove_by_ref(cs, env->cpu_watchpoint[index]); - break; - case DR7_TYPE_IO_RW: - /* No support for I/O watchpoints yet */ - break; - } -} - -bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update) -{ - target_ulong dr6; - int reg; - bool hit_enabled = false; - - dr6 = env->dr[6] & ~0xf; - for (reg = 0; reg < DR7_MAX_BP; reg++) { - bool bp_match = false; - bool wp_match = false; - - switch (hw_breakpoint_type(env->dr[7], reg)) { - case DR7_TYPE_BP_INST: - if (env->dr[reg] == env->eip) { - bp_match = true; - } - break; - case DR7_TYPE_DATA_WR: - case DR7_TYPE_DATA_RW: - if (env->cpu_watchpoint[reg] && - env->cpu_watchpoint[reg]->flags & BP_WATCHPOINT_HIT) { - wp_match = true; - } - break; - case DR7_TYPE_IO_RW: - break; - } - if (bp_match || wp_match) { - dr6 |= 1 << reg; - if (hw_breakpoint_enabled(env->dr[7], reg)) { - hit_enabled = true; - } - } - } - - if (hit_enabled || force_dr6_update) { - env->dr[6] = dr6; - } - - return hit_enabled; -} - -void breakpoint_handler(CPUState *cs) -{ - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - CPUBreakpoint *bp; - - if (cs->watchpoint_hit) { - if (cs->watchpoint_hit->flags & BP_CPU) { - cs->watchpoint_hit = NULL; - if (check_hw_breakpoints(env, false)) { - raise_exception(env, EXCP01_DB); - } else { - cpu_resume_from_signal(cs, NULL); - } - } - } else { - QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - if (bp->pc == env->eip) { - if (bp->flags & BP_CPU) { - check_hw_breakpoints(env, true); - raise_exception(env, EXCP01_DB); - } - break; - } - } - } -} - typedef struct MCEInjectionParams { Monitor *mon; X86CPU *cpu; diff --git a/qemu/target-i386/helper.h b/qemu/target-i386/helper.h index 74308f442..1320edc01 100644 --- a/qemu/target-i386/helper.h +++ b/qemu/target-i386/helper.h @@ -15,6 +15,14 @@ DEF_HELPER_2(idivl_EAX, void, env, tl) DEF_HELPER_2(divq_EAX, void, env, tl) DEF_HELPER_2(idivq_EAX, void, env, tl) #endif +DEF_HELPER_FLAGS_2(cr4_testbit, TCG_CALL_NO_WG, void, env, i32) + +DEF_HELPER_FLAGS_2(bndck, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_FLAGS_3(bndldx32, TCG_CALL_NO_WG, i64, env, tl, tl) +DEF_HELPER_FLAGS_3(bndldx64, TCG_CALL_NO_WG, i64, env, tl, tl) +DEF_HELPER_FLAGS_5(bndstx32, TCG_CALL_NO_WG, void, env, tl, tl, i64, i64) +DEF_HELPER_FLAGS_5(bndstx64, TCG_CALL_NO_WG, void, env, tl, tl, i64, i64) +DEF_HELPER_1(bnd_jmp, void, env) DEF_HELPER_2(aam, void, env, int) DEF_HELPER_2(aad, void, env, int) @@ -30,9 +38,9 @@ DEF_HELPER_2(verw, void, env, tl) DEF_HELPER_2(lldt, void, env, int) DEF_HELPER_2(ltr, void, env, int) DEF_HELPER_3(load_seg, void, env, int, int) -DEF_HELPER_4(ljmp_protected, void, env, int, tl, int) +DEF_HELPER_4(ljmp_protected, void, env, int, tl, tl) DEF_HELPER_5(lcall_real, void, env, int, tl, int, int) -DEF_HELPER_5(lcall_protected, void, env, int, tl, int, int) +DEF_HELPER_5(lcall_protected, void, env, int, tl, int, tl) DEF_HELPER_2(iret_real, void, env, int) DEF_HELPER_3(iret_protected, void, env, int, int) DEF_HELPER_3(lret_protected, void, env, int, int) @@ -40,13 +48,10 @@ DEF_HELPER_2(read_crN, tl, env, int) DEF_HELPER_3(write_crN, void, env, int, tl) DEF_HELPER_2(lmsw, void, env, tl) DEF_HELPER_1(clts, void, env) -DEF_HELPER_3(movl_drN_T0, void, env, int, tl) +DEF_HELPER_FLAGS_3(set_dr, TCG_CALL_NO_WG, void, env, int, tl) +DEF_HELPER_FLAGS_2(get_dr, TCG_CALL_NO_WG, tl, env, int) DEF_HELPER_2(invlpg, void, env, tl) -DEF_HELPER_4(enter_level, void, env, int, int, tl) -#ifdef TARGET_X86_64 -DEF_HELPER_4(enter64_level, void, env, int, int, tl) -#endif DEF_HELPER_1(sysenter, void, env) DEF_HELPER_2(sysexit, void, env, int) #ifdef TARGET_X86_64 @@ -65,8 +70,6 @@ DEF_HELPER_1(cli, void, env) DEF_HELPER_1(sti, void, env) DEF_HELPER_1(clac, void, env) DEF_HELPER_1(stac, void, env) -DEF_HELPER_1(set_inhibit_irq, void, env) -DEF_HELPER_1(reset_inhibit_irq, void, env) DEF_HELPER_3(boundw, void, env, tl, int) DEF_HELPER_3(boundl, void, env, tl, int) DEF_HELPER_1(rsm, void, env) @@ -92,6 +95,7 @@ DEF_HELPER_3(outw, void, env, i32, i32) DEF_HELPER_2(inw, tl, env, i32) DEF_HELPER_3(outl, void, env, i32, i32) DEF_HELPER_2(inl, tl, env, i32) +DEF_HELPER_FLAGS_4(bpt_io, TCG_CALL_NO_WG, void, env, i32, i32, tl) DEF_HELPER_3(svm_check_intercept_param, void, env, i32, i64) DEF_HELPER_3(vmexit, void, env, i32, i64) @@ -187,8 +191,15 @@ DEF_HELPER_3(fstenv, void, env, tl, int) DEF_HELPER_3(fldenv, void, env, tl, int) DEF_HELPER_3(fsave, void, env, tl, int) DEF_HELPER_3(frstor, void, env, tl, int) -DEF_HELPER_3(fxsave, void, env, tl, int) -DEF_HELPER_3(fxrstor, void, env, tl, int) +DEF_HELPER_FLAGS_2(fxsave, TCG_CALL_NO_WG, void, env, tl) +DEF_HELPER_FLAGS_2(fxrstor, TCG_CALL_NO_WG, void, env, tl) +DEF_HELPER_FLAGS_3(xsave, TCG_CALL_NO_WG, void, env, tl, i64) +DEF_HELPER_FLAGS_3(xsaveopt, TCG_CALL_NO_WG, void, env, tl, i64) +DEF_HELPER_FLAGS_3(xrstor, TCG_CALL_NO_WG, void, env, tl, i64) +DEF_HELPER_FLAGS_2(xgetbv, TCG_CALL_NO_WG, i64, env, i32) +DEF_HELPER_FLAGS_3(xsetbv, TCG_CALL_NO_WG, void, env, i32, i64) +DEF_HELPER_FLAGS_2(rdpkru, TCG_CALL_NO_WG, i64, env, i32) +DEF_HELPER_FLAGS_3(wrpkru, TCG_CALL_NO_WG, void, env, i32, i64) DEF_HELPER_FLAGS_1(clz, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_1(ctz, TCG_CALL_NO_RWG_SE, tl, tl) diff --git a/qemu/target-i386/hyperv.c b/qemu/target-i386/hyperv.c new file mode 100644 index 000000000..39a230f11 --- /dev/null +++ b/qemu/target-i386/hyperv.c @@ -0,0 +1,140 @@ +/* + * QEMU KVM Hyper-V support + * + * Copyright (C) 2015 Andrey Smetanin + * + * Authors: + * Andrey Smetanin + * + * 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 "hyperv.h" +#include "standard-headers/asm-x86/hyperv.h" + +int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit) +{ + CPUX86State *env = &cpu->env; + + switch (exit->type) { + case KVM_EXIT_HYPERV_SYNIC: + if (!cpu->hyperv_synic) { + return -1; + } + + /* + * For now just track changes in SynIC control and msg/evt pages msr's. + * When SynIC messaging/events processing will be added in future + * here we will do messages queues flushing and pages remapping. + */ + switch (exit->u.synic.msr) { + case HV_X64_MSR_SCONTROL: + env->msr_hv_synic_control = exit->u.synic.control; + break; + case HV_X64_MSR_SIMP: + env->msr_hv_synic_msg_page = exit->u.synic.msg_page; + break; + case HV_X64_MSR_SIEFP: + env->msr_hv_synic_evt_page = exit->u.synic.evt_page; + break; + default: + return -1; + } + return 0; + case KVM_EXIT_HYPERV_HCALL: { + uint16_t code; + + code = exit->u.hcall.input & 0xffff; + switch (code) { + case HVCALL_POST_MESSAGE: + case HVCALL_SIGNAL_EVENT: + default: + exit->u.hcall.result = HV_STATUS_INVALID_HYPERCALL_CODE; + return 0; + } + } + default: + return -1; + } +} + +static void kvm_hv_sint_ack_handler(EventNotifier *notifier) +{ + HvSintRoute *sint_route = container_of(notifier, HvSintRoute, + sint_ack_notifier); + event_notifier_test_and_clear(notifier); + if (sint_route->sint_ack_clb) { + sint_route->sint_ack_clb(sint_route); + } +} + +HvSintRoute *kvm_hv_sint_route_create(uint32_t vcpu_id, uint32_t sint, + HvSintAckClb sint_ack_clb) +{ + HvSintRoute *sint_route; + int r, gsi; + + sint_route = g_malloc0(sizeof(*sint_route)); + r = event_notifier_init(&sint_route->sint_set_notifier, false); + if (r) { + goto err; + } + + r = event_notifier_init(&sint_route->sint_ack_notifier, false); + if (r) { + goto err_sint_set_notifier; + } + + event_notifier_set_handler(&sint_route->sint_ack_notifier, false, + kvm_hv_sint_ack_handler); + + gsi = kvm_irqchip_add_hv_sint_route(kvm_state, vcpu_id, sint); + if (gsi < 0) { + goto err_gsi; + } + + r = kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, + &sint_route->sint_set_notifier, + &sint_route->sint_ack_notifier, gsi); + if (r) { + goto err_irqfd; + } + sint_route->gsi = gsi; + sint_route->sint_ack_clb = sint_ack_clb; + sint_route->vcpu_id = vcpu_id; + sint_route->sint = sint; + + return sint_route; + +err_irqfd: + kvm_irqchip_release_virq(kvm_state, gsi); +err_gsi: + event_notifier_set_handler(&sint_route->sint_ack_notifier, false, NULL); + event_notifier_cleanup(&sint_route->sint_ack_notifier); +err_sint_set_notifier: + event_notifier_cleanup(&sint_route->sint_set_notifier); +err: + g_free(sint_route); + + return NULL; +} + +void kvm_hv_sint_route_destroy(HvSintRoute *sint_route) +{ + kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, + &sint_route->sint_set_notifier, + sint_route->gsi); + kvm_irqchip_release_virq(kvm_state, sint_route->gsi); + event_notifier_set_handler(&sint_route->sint_ack_notifier, false, NULL); + event_notifier_cleanup(&sint_route->sint_ack_notifier); + event_notifier_cleanup(&sint_route->sint_set_notifier); + g_free(sint_route); +} + +int kvm_hv_sint_route_set_sint(HvSintRoute *sint_route) +{ + return event_notifier_set(&sint_route->sint_set_notifier); +} diff --git a/qemu/target-i386/hyperv.h b/qemu/target-i386/hyperv.h new file mode 100644 index 000000000..b26201f8b --- /dev/null +++ b/qemu/target-i386/hyperv.h @@ -0,0 +1,42 @@ +/* + * QEMU KVM Hyper-V support + * + * Copyright (C) 2015 Andrey Smetanin + * + * Authors: + * Andrey Smetanin + * + * 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 HYPERV_I386_H +#define HYPERV_I386_H + +#include "cpu.h" +#include "sysemu/kvm.h" +#include "qemu/event_notifier.h" + +typedef struct HvSintRoute HvSintRoute; +typedef void (*HvSintAckClb)(HvSintRoute *sint_route); + +struct HvSintRoute { + uint32_t sint; + uint32_t vcpu_id; + int gsi; + EventNotifier sint_set_notifier; + EventNotifier sint_ack_notifier; + HvSintAckClb sint_ack_clb; +}; + +int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit); + +HvSintRoute *kvm_hv_sint_route_create(uint32_t vcpu_id, uint32_t sint, + HvSintAckClb sint_ack_clb); + +void kvm_hv_sint_route_destroy(HvSintRoute *sint_route); + +int kvm_hv_sint_route_set_sint(HvSintRoute *sint_route); + +#endif diff --git a/qemu/target-i386/int_helper.c b/qemu/target-i386/int_helper.c index b0d78e6ee..cf5bbb048 100644 --- a/qemu/target-i386/int_helper.c +++ b/qemu/target-i386/int_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" @@ -48,11 +49,11 @@ void helper_divb_AL(CPUX86State *env, target_ulong t0) num = (env->regs[R_EAX] & 0xffff); den = (t0 & 0xff); if (den == 0) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q = (num / den); if (q > 0xff) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q &= 0xff; r = (num % den) & 0xff; @@ -66,11 +67,11 @@ void helper_idivb_AL(CPUX86State *env, target_ulong t0) num = (int16_t)env->regs[R_EAX]; den = (int8_t)t0; if (den == 0) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q = (num / den); if (q != (int8_t)q) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q &= 0xff; r = (num % den) & 0xff; @@ -84,11 +85,11 @@ void helper_divw_AX(CPUX86State *env, target_ulong t0) num = (env->regs[R_EAX] & 0xffff) | ((env->regs[R_EDX] & 0xffff) << 16); den = (t0 & 0xffff); if (den == 0) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q = (num / den); if (q > 0xffff) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q &= 0xffff; r = (num % den) & 0xffff; @@ -103,11 +104,11 @@ void helper_idivw_AX(CPUX86State *env, target_ulong t0) num = (env->regs[R_EAX] & 0xffff) | ((env->regs[R_EDX] & 0xffff) << 16); den = (int16_t)t0; if (den == 0) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q = (num / den); if (q != (int16_t)q) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q &= 0xffff; r = (num % den) & 0xffff; @@ -123,12 +124,12 @@ void helper_divl_EAX(CPUX86State *env, target_ulong t0) num = ((uint32_t)env->regs[R_EAX]) | ((uint64_t)((uint32_t)env->regs[R_EDX]) << 32); den = t0; if (den == 0) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q = (num / den); r = (num % den); if (q > 0xffffffff) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } env->regs[R_EAX] = (uint32_t)q; env->regs[R_EDX] = (uint32_t)r; @@ -142,12 +143,12 @@ void helper_idivl_EAX(CPUX86State *env, target_ulong t0) num = ((uint32_t)env->regs[R_EAX]) | ((uint64_t)((uint32_t)env->regs[R_EDX]) << 32); den = t0; if (den == 0) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q = (num / den); r = (num % den); if (q != (int32_t)q) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } env->regs[R_EAX] = (uint32_t)q; env->regs[R_EDX] = (uint32_t)r; @@ -379,12 +380,12 @@ void helper_divq_EAX(CPUX86State *env, target_ulong t0) uint64_t r0, r1; if (t0 == 0) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } r0 = env->regs[R_EAX]; r1 = env->regs[R_EDX]; if (div64(&r0, &r1, t0)) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } env->regs[R_EAX] = r0; env->regs[R_EDX] = r1; @@ -395,12 +396,12 @@ void helper_idivq_EAX(CPUX86State *env, target_ulong t0) uint64_t r0, r1; if (t0 == 0) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } r0 = env->regs[R_EAX]; r1 = env->regs[R_EDX]; if (idiv64(&r0, &r1, t0)) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } env->regs[R_EAX] = r0; env->regs[R_EDX] = r1; @@ -469,3 +470,13 @@ target_ulong helper_pext(target_ulong src, target_ulong mask) #include "shift_helper_template.h" #undef SHIFT #endif + +/* Test that BIT is enabled in CR4. If not, raise an illegal opcode + exception. This reduces the requirements for rare CR4 bits being + mapped into HFLAGS. */ +void helper_cr4_testbit(CPUX86State *env, uint32_t bit) +{ + if (unlikely((env->cr[4] & bit) == 0)) { + raise_exception_ra(env, EXCP06_ILLOP, GETPC()); + } +} diff --git a/qemu/target-i386/kvm-stub.c b/qemu/target-i386/kvm-stub.c index 6fefd65c2..8df9c5953 100644 --- a/qemu/target-i386/kvm-stub.c +++ b/qemu/target-i386/kvm-stub.c @@ -9,6 +9,7 @@ * See the COPYING file in the top-level directory. * */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "kvm_i386.h" diff --git a/qemu/target-i386/kvm.c b/qemu/target-i386/kvm.c index 721c580ed..799fdfa68 100644 --- a/qemu/target-i386/kvm.c +++ b/qemu/target-i386/kvm.c @@ -12,7 +12,8 @@ * */ -#include +#include "qemu/osdep.h" +#include "qapi/error.h" #include #include #include @@ -25,16 +26,21 @@ #include "sysemu/kvm_int.h" #include "kvm_i386.h" #include "cpu.h" +#include "hyperv.h" + #include "exec/gdbstub.h" #include "qemu/host-utils.h" #include "qemu/config-file.h" +#include "qemu/error-report.h" #include "hw/i386/pc.h" #include "hw/i386/apic.h" #include "hw/i386/apic_internal.h" #include "hw/i386/apic-msidef.h" + #include "exec/ioport.h" -#include +#include "standard-headers/asm-x86/hyperv.h" #include "hw/pci/pci.h" +#include "hw/pci/msi.h" #include "migration/migration.h" #include "exec/memattrs.h" @@ -67,6 +73,7 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { static bool has_msr_star; static bool has_msr_hsave_pa; +static bool has_msr_tsc_aux; static bool has_msr_tsc_adjust; static bool has_msr_tsc_deadline; static bool has_msr_feature_control; @@ -80,12 +87,27 @@ static int lm_capable_kernel; static bool has_msr_hv_hypercall; static bool has_msr_hv_vapic; static bool has_msr_hv_tsc; +static bool has_msr_hv_crash; +static bool has_msr_hv_reset; +static bool has_msr_hv_vpindex; +static bool has_msr_hv_runtime; +static bool has_msr_hv_synic; +static bool has_msr_hv_stimer; static bool has_msr_mtrr; static bool has_msr_xss; static bool has_msr_architectural_pmu; static uint32_t num_architectural_pmu_counters; +static int has_xsave; +static int has_xcrs; +static int has_pit_state2; + +int kvm_has_pit_state2(void) +{ + return has_pit_state2; +} + bool kvm_has_smm(void) { return kvm_check_extension(kvm_state, KVM_CAP_X86_SMM); @@ -119,6 +141,7 @@ static int kvm_get_tsc(CPUState *cs) return ret; } + assert(ret == 1); env->tsc = msr_data.entries[0].data; return 0; } @@ -502,7 +525,43 @@ static bool hyperv_enabled(X86CPU *cpu) return kvm_check_extension(cs->kvm_state, KVM_CAP_HYPERV) > 0 && (hyperv_hypercall_available(cpu) || cpu->hyperv_time || - cpu->hyperv_relaxed_timing); + cpu->hyperv_relaxed_timing || + cpu->hyperv_crash || + cpu->hyperv_reset || + cpu->hyperv_vpindex || + cpu->hyperv_runtime || + cpu->hyperv_synic || + cpu->hyperv_stimer); +} + +static int kvm_arch_set_tsc_khz(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + int r; + + if (!env->tsc_khz) { + return 0; + } + + r = kvm_check_extension(cs->kvm_state, KVM_CAP_TSC_CONTROL) ? + kvm_vcpu_ioctl(cs, KVM_SET_TSC_KHZ, env->tsc_khz) : + -ENOTSUP; + if (r < 0) { + /* When KVM_SET_TSC_KHZ fails, it's an error only if the current + * TSC frequency doesn't match the one we want. + */ + int cur_freq = kvm_check_extension(cs->kvm_state, KVM_CAP_GET_TSC_KHZ) ? + kvm_vcpu_ioctl(cs, KVM_GET_TSC_KHZ) : + -ENOTSUP; + if (cur_freq <= 0 || cur_freq != env->tsc_khz) { + error_report("warning: TSC frequency mismatch between " + "VM and host, and TSC scaling unavailable"); + return r; + } + } + + return 0; } static Error *invtsc_mig_blocker; @@ -532,7 +591,18 @@ int kvm_arch_init_vcpu(CPUState *cs) if (hyperv_enabled(cpu)) { c = &cpuid_data.entries[cpuid_i++]; c->function = HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS; - memcpy(signature, "Microsoft Hv", 12); + if (!cpu->hyperv_vendor_id) { + memcpy(signature, "Microsoft Hv", 12); + } else { + size_t len = strlen(cpu->hyperv_vendor_id); + + if (len > 12) { + error_report("hv-vendor-id truncated to 12 characters"); + len = 12; + } + memset(signature, 0, 12); + memcpy(signature, cpu->hyperv_vendor_id, len); + } c->eax = HYPERV_CPUID_MIN; c->ebx = signature[0]; c->ecx = signature[1]; @@ -568,6 +638,41 @@ int kvm_arch_init_vcpu(CPUState *cs) c->eax |= 0x200; has_msr_hv_tsc = true; } + if (cpu->hyperv_crash && has_msr_hv_crash) { + c->edx |= HV_X64_GUEST_CRASH_MSR_AVAILABLE; + } + c->edx |= HV_X64_CPU_DYNAMIC_PARTITIONING_AVAILABLE; + if (cpu->hyperv_reset && has_msr_hv_reset) { + c->eax |= HV_X64_MSR_RESET_AVAILABLE; + } + if (cpu->hyperv_vpindex && has_msr_hv_vpindex) { + c->eax |= HV_X64_MSR_VP_INDEX_AVAILABLE; + } + if (cpu->hyperv_runtime && has_msr_hv_runtime) { + c->eax |= HV_X64_MSR_VP_RUNTIME_AVAILABLE; + } + if (cpu->hyperv_synic) { + int sint; + + if (!has_msr_hv_synic || + kvm_vcpu_enable_cap(cs, KVM_CAP_HYPERV_SYNIC, 0)) { + fprintf(stderr, "Hyper-V SynIC is not supported by kernel\n"); + return -ENOSYS; + } + + c->eax |= HV_X64_MSR_SYNIC_AVAILABLE; + env->msr_hv_synic_version = HV_SYNIC_VERSION_1; + for (sint = 0; sint < ARRAY_SIZE(env->msr_hv_synic_sint); sint++) { + env->msr_hv_synic_sint[sint] = HV_SYNIC_SINT_MASKED; + } + } + if (cpu->hyperv_stimer) { + if (!has_msr_hv_stimer) { + fprintf(stderr, "Hyper-V timers aren't supported by kernel\n"); + return -ENOSYS; + } + c->eax |= HV_X64_MSR_SYNTIMER_AVAILABLE; + } c = &cpuid_data.entries[cpuid_i++]; c->function = HYPERV_CPUID_ENLIGHTMENT_INFO; if (cpu->hyperv_relaxed_timing) { @@ -732,7 +837,7 @@ int kvm_arch_init_vcpu(CPUState *cs) && (env->features[FEAT_1_EDX] & (CPUID_MCE | CPUID_MCA)) == (CPUID_MCE | CPUID_MCA) && kvm_check_extension(cs->kvm_state, KVM_CAP_MCE) > 0) { - uint64_t mcg_cap; + uint64_t mcg_cap, unsupported_caps; int banks; int ret; @@ -742,18 +847,24 @@ int kvm_arch_init_vcpu(CPUState *cs) return ret; } - if (banks > MCE_BANKS_DEF) { - banks = MCE_BANKS_DEF; + if (banks < (env->mcg_cap & MCG_CAP_BANKS_MASK)) { + error_report("kvm: Unsupported MCE bank count (QEMU = %d, KVM = %d)", + (int)(env->mcg_cap & MCG_CAP_BANKS_MASK), banks); + return -ENOTSUP; + } + + unsupported_caps = env->mcg_cap & ~(mcg_cap | MCG_CAP_BANKS_MASK); + if (unsupported_caps) { + error_report("warning: Unsupported MCG_CAP bits: 0x%" PRIx64, + unsupported_caps); } - mcg_cap &= MCE_CAP_DEF; - mcg_cap |= banks; - ret = kvm_vcpu_ioctl(cs, KVM_X86_SETUP_MCE, &mcg_cap); + + env->mcg_cap &= mcg_cap | MCG_CAP_BANKS_MASK; + ret = kvm_vcpu_ioctl(cs, KVM_X86_SETUP_MCE, &env->mcg_cap); if (ret < 0) { fprintf(stderr, "KVM_X86_SETUP_MCE: %s", strerror(-ret)); return ret; } - - env->mcg_cap = mcg_cap; } qemu_add_vm_change_state_handler(cpu_update_state, env); @@ -781,22 +892,35 @@ int kvm_arch_init_vcpu(CPUState *cs) return r; } - r = kvm_check_extension(cs->kvm_state, KVM_CAP_TSC_CONTROL); - if (r && env->tsc_khz) { - r = kvm_vcpu_ioctl(cs, KVM_SET_TSC_KHZ, env->tsc_khz); - if (r < 0) { - fprintf(stderr, "KVM_SET_TSC_KHZ failed\n"); - return r; + r = kvm_arch_set_tsc_khz(cs); + if (r < 0) { + return r; + } + + /* vcpu's TSC frequency is either specified by user, or following + * the value used by KVM if the former is not present. In the + * latter case, we query it from KVM and record in env->tsc_khz, + * so that vcpu's TSC frequency can be migrated later via this field. + */ + if (!env->tsc_khz) { + r = kvm_check_extension(cs->kvm_state, KVM_CAP_GET_TSC_KHZ) ? + kvm_vcpu_ioctl(cs, KVM_GET_TSC_KHZ) : + -ENOTSUP; + if (r > 0) { + env->tsc_khz = r; } } - if (kvm_has_xsave()) { + if (has_xsave) { env->kvm_xsave_buf = qemu_memalign(4096, sizeof(struct kvm_xsave)); } if (env->features[FEAT_1_EDX] & CPUID_MTRR) { has_msr_mtrr = true; } + if (!(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_RDTSCP)) { + has_msr_tsc_aux = false; + } return 0; } @@ -864,6 +988,10 @@ static int kvm_get_supported_msrs(KVMState *s) has_msr_hsave_pa = true; continue; } + if (kvm_msr_list->indices[i] == MSR_TSC_AUX) { + has_msr_tsc_aux = true; + continue; + } if (kvm_msr_list->indices[i] == MSR_TSC_ADJUST) { has_msr_tsc_adjust = true; continue; @@ -888,6 +1016,30 @@ static int kvm_get_supported_msrs(KVMState *s) has_msr_xss = true; continue; } + if (kvm_msr_list->indices[i] == HV_X64_MSR_CRASH_CTL) { + has_msr_hv_crash = true; + continue; + } + if (kvm_msr_list->indices[i] == HV_X64_MSR_RESET) { + has_msr_hv_reset = true; + continue; + } + if (kvm_msr_list->indices[i] == HV_X64_MSR_VP_INDEX) { + has_msr_hv_vpindex = true; + continue; + } + if (kvm_msr_list->indices[i] == HV_X64_MSR_VP_RUNTIME) { + has_msr_hv_runtime = true; + continue; + } + if (kvm_msr_list->indices[i] == HV_X64_MSR_SCONTROL) { + has_msr_hv_synic = true; + continue; + } + if (kvm_msr_list->indices[i] == HV_X64_MSR_STIMER0_CONFIG) { + has_msr_hv_stimer = true; + continue; + } } } @@ -938,6 +1090,18 @@ int kvm_arch_init(MachineState *ms, KVMState *s) int ret; struct utsname utsname; +#ifdef KVM_CAP_XSAVE + has_xsave = kvm_check_extension(s, KVM_CAP_XSAVE); +#endif + +#ifdef KVM_CAP_XCRS + has_xcrs = kvm_check_extension(s, KVM_CAP_XCRS); +#endif + +#ifdef KVM_CAP_PIT_STATE2 + has_pit_state2 = kvm_check_extension(s, KVM_CAP_PIT_STATE2); +#endif + ret = kvm_get_supported_msrs(s); if (ret < 0) { return ret; @@ -1027,7 +1191,7 @@ static void set_seg(struct kvm_segment *lhs, const SegmentCache *rhs) lhs->l = (flags >> DESC_L_SHIFT) & 1; lhs->g = (flags & DESC_G_MASK) != 0; lhs->avl = (flags & DESC_AVL_MASK) != 0; - lhs->unusable = 0; + lhs->unusable = !lhs->present; lhs->padding = 0; } @@ -1036,14 +1200,18 @@ static void get_seg(SegmentCache *lhs, const struct kvm_segment *rhs) lhs->selector = rhs->selector; lhs->base = rhs->base; lhs->limit = rhs->limit; - lhs->flags = (rhs->type << DESC_TYPE_SHIFT) | - (rhs->present * DESC_P_MASK) | - (rhs->dpl << DESC_DPL_SHIFT) | - (rhs->db << DESC_B_SHIFT) | - (rhs->s * DESC_S_MASK) | - (rhs->l << DESC_L_SHIFT) | - (rhs->g * DESC_G_MASK) | - (rhs->avl * DESC_AVL_MASK); + if (rhs->unusable) { + lhs->flags = 0; + } else { + lhs->flags = (rhs->type << DESC_TYPE_SHIFT) | + (rhs->present * DESC_P_MASK) | + (rhs->dpl << DESC_DPL_SHIFT) | + (rhs->db << DESC_B_SHIFT) | + (rhs->s * DESC_S_MASK) | + (rhs->l << DESC_L_SHIFT) | + (rhs->g * DESC_G_MASK) | + (rhs->avl * DESC_AVL_MASK); + } } static void kvm_getput_reg(__u64 *kvm_reg, target_ulong *qemu_reg, int set) @@ -1115,8 +1283,8 @@ static int kvm_put_fpu(X86CPU *cpu) } memcpy(fpu.fpr, env->fpregs, sizeof env->fpregs); for (i = 0; i < CPU_NB_REGS; i++) { - stq_p(&fpu.xmm[i][0], env->xmm_regs[i].XMM_Q(0)); - stq_p(&fpu.xmm[i][8], env->xmm_regs[i].XMM_Q(1)); + stq_p(&fpu.xmm[i][0], env->xmm_regs[i].ZMM_Q(0)); + stq_p(&fpu.xmm[i][8], env->xmm_regs[i].ZMM_Q(1)); } fpu.mxcsr = env->mxcsr; @@ -1137,6 +1305,7 @@ static int kvm_put_fpu(X86CPU *cpu) #define XSAVE_OPMASK 272 #define XSAVE_ZMM_Hi256 288 #define XSAVE_Hi16_ZMM 416 +#define XSAVE_PKRU 672 static int kvm_put_xsave(X86CPU *cpu) { @@ -1146,7 +1315,7 @@ static int kvm_put_xsave(X86CPU *cpu) uint8_t *xmm, *ymmh, *zmmh; int i, r; - if (!kvm_has_xsave()) { + if (!has_xsave) { return kvm_put_fpu(cpu); } @@ -1177,19 +1346,20 @@ static int kvm_put_xsave(X86CPU *cpu) ymmh = (uint8_t *)&xsave->region[XSAVE_YMMH_SPACE]; zmmh = (uint8_t *)&xsave->region[XSAVE_ZMM_Hi256]; for (i = 0; i < CPU_NB_REGS; i++, xmm += 16, ymmh += 16, zmmh += 32) { - stq_p(xmm, env->xmm_regs[i].XMM_Q(0)); - stq_p(xmm+8, env->xmm_regs[i].XMM_Q(1)); - stq_p(ymmh, env->xmm_regs[i].XMM_Q(2)); - stq_p(ymmh+8, env->xmm_regs[i].XMM_Q(3)); - stq_p(zmmh, env->xmm_regs[i].XMM_Q(4)); - stq_p(zmmh+8, env->xmm_regs[i].XMM_Q(5)); - stq_p(zmmh+16, env->xmm_regs[i].XMM_Q(6)); - stq_p(zmmh+24, env->xmm_regs[i].XMM_Q(7)); + stq_p(xmm, env->xmm_regs[i].ZMM_Q(0)); + stq_p(xmm+8, env->xmm_regs[i].ZMM_Q(1)); + stq_p(ymmh, env->xmm_regs[i].ZMM_Q(2)); + stq_p(ymmh+8, env->xmm_regs[i].ZMM_Q(3)); + stq_p(zmmh, env->xmm_regs[i].ZMM_Q(4)); + stq_p(zmmh+8, env->xmm_regs[i].ZMM_Q(5)); + stq_p(zmmh+16, env->xmm_regs[i].ZMM_Q(6)); + stq_p(zmmh+24, env->xmm_regs[i].ZMM_Q(7)); } #ifdef TARGET_X86_64 memcpy(&xsave->region[XSAVE_Hi16_ZMM], &env->xmm_regs[16], 16 * sizeof env->xmm_regs[16]); + memcpy(&xsave->region[XSAVE_PKRU], &env->pkru, sizeof env->pkru); #endif r = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_XSAVE, xsave); return r; @@ -1200,7 +1370,7 @@ static int kvm_put_xcrs(X86CPU *cpu) CPUX86State *env = &cpu->env; struct kvm_xcrs xcrs = {}; - if (!kvm_has_xcrs()) { + if (!has_xcrs) { return 0; } @@ -1277,6 +1447,7 @@ static int kvm_put_tscdeadline_msr(X86CPU *cpu) struct kvm_msr_entry entries[1]; } msr_data; struct kvm_msr_entry *msrs = msr_data.entries; + int ret; if (!has_msr_tsc_deadline) { return 0; @@ -1288,7 +1459,13 @@ static int kvm_put_tscdeadline_msr(X86CPU *cpu) .nmsrs = 1, }; - return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MSRS, &msr_data); + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MSRS, &msr_data); + if (ret < 0) { + return ret; + } + + assert(ret == 1); + return 0; } /* @@ -1303,6 +1480,11 @@ static int kvm_put_msr_feature_control(X86CPU *cpu) struct kvm_msrs info; struct kvm_msr_entry entry; } msr_data; + int ret; + + if (!has_msr_feature_control) { + return 0; + } kvm_msr_entry_set(&msr_data.entry, MSR_IA32_FEATURE_CONTROL, cpu->env.msr_ia32_feature_control); @@ -1311,7 +1493,13 @@ static int kvm_put_msr_feature_control(X86CPU *cpu) .nmsrs = 1, }; - return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MSRS, &msr_data); + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MSRS, &msr_data); + if (ret < 0) { + return ret; + } + + assert(ret == 1); + return 0; } static int kvm_put_msrs(X86CPU *cpu, int level) @@ -1323,6 +1511,7 @@ static int kvm_put_msrs(X86CPU *cpu, int level) } msr_data; struct kvm_msr_entry *msrs = msr_data.entries; int n = 0, i; + int ret; kvm_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_CS, env->sysenter_cs); kvm_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_ESP, env->sysenter_esp); @@ -1334,6 +1523,9 @@ static int kvm_put_msrs(X86CPU *cpu, int level) if (has_msr_hsave_pa) { kvm_msr_entry_set(&msrs[n++], MSR_VM_HSAVE_PA, env->vm_hsave); } + if (has_msr_tsc_aux) { + kvm_msr_entry_set(&msrs[n++], MSR_TSC_AUX, env->tsc_aux); + } if (has_msr_tsc_adjust) { kvm_msr_entry_set(&msrs[n++], MSR_TSC_ADJUST, env->tsc_adjust); } @@ -1420,6 +1612,50 @@ static int kvm_put_msrs(X86CPU *cpu, int level) kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_REFERENCE_TSC, env->msr_hv_tsc); } + if (has_msr_hv_crash) { + int j; + + for (j = 0; j < HV_X64_MSR_CRASH_PARAMS; j++) + kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_CRASH_P0 + j, + env->msr_hv_crash_params[j]); + + kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_CRASH_CTL, + HV_X64_MSR_CRASH_CTL_NOTIFY); + } + if (has_msr_hv_runtime) { + kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_VP_RUNTIME, + env->msr_hv_runtime); + } + if (cpu->hyperv_synic) { + int j; + + kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_SCONTROL, + env->msr_hv_synic_control); + kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_SVERSION, + env->msr_hv_synic_version); + kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_SIEFP, + env->msr_hv_synic_evt_page); + kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_SIMP, + env->msr_hv_synic_msg_page); + + for (j = 0; j < ARRAY_SIZE(env->msr_hv_synic_sint); j++) { + kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_SINT0 + j, + env->msr_hv_synic_sint[j]); + } + } + if (has_msr_hv_stimer) { + int j; + + for (j = 0; j < ARRAY_SIZE(env->msr_hv_stimer_config); j++) { + kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_STIMER0_CONFIG + j*2, + env->msr_hv_stimer_config[j]); + } + + for (j = 0; j < ARRAY_SIZE(env->msr_hv_stimer_count); j++) { + kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_STIMER0_COUNT + j*2, + env->msr_hv_stimer_count[j]); + } + } if (has_msr_mtrr) { kvm_msr_entry_set(&msrs[n++], MSR_MTRRdefType, env->mtrr_deftype); kvm_msr_entry_set(&msrs[n++], @@ -1469,8 +1705,13 @@ static int kvm_put_msrs(X86CPU *cpu, int level) .nmsrs = n, }; - return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MSRS, &msr_data); + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MSRS, &msr_data); + if (ret < 0) { + return ret; + } + assert(ret == n); + return 0; } @@ -1496,8 +1737,8 @@ static int kvm_get_fpu(X86CPU *cpu) } memcpy(env->fpregs, fpu.fpr, sizeof env->fpregs); for (i = 0; i < CPU_NB_REGS; i++) { - env->xmm_regs[i].XMM_Q(0) = ldq_p(&fpu.xmm[i][0]); - env->xmm_regs[i].XMM_Q(1) = ldq_p(&fpu.xmm[i][8]); + env->xmm_regs[i].ZMM_Q(0) = ldq_p(&fpu.xmm[i][0]); + env->xmm_regs[i].ZMM_Q(1) = ldq_p(&fpu.xmm[i][8]); } env->mxcsr = fpu.mxcsr; @@ -1512,7 +1753,7 @@ static int kvm_get_xsave(X86CPU *cpu) const uint8_t *xmm, *ymmh, *zmmh; uint16_t cwd, swd, twd; - if (!kvm_has_xsave()) { + if (!has_xsave) { return kvm_get_fpu(cpu); } @@ -1548,19 +1789,20 @@ static int kvm_get_xsave(X86CPU *cpu) ymmh = (const uint8_t *)&xsave->region[XSAVE_YMMH_SPACE]; zmmh = (const uint8_t *)&xsave->region[XSAVE_ZMM_Hi256]; for (i = 0; i < CPU_NB_REGS; i++, xmm += 16, ymmh += 16, zmmh += 32) { - env->xmm_regs[i].XMM_Q(0) = ldq_p(xmm); - env->xmm_regs[i].XMM_Q(1) = ldq_p(xmm+8); - env->xmm_regs[i].XMM_Q(2) = ldq_p(ymmh); - env->xmm_regs[i].XMM_Q(3) = ldq_p(ymmh+8); - env->xmm_regs[i].XMM_Q(4) = ldq_p(zmmh); - env->xmm_regs[i].XMM_Q(5) = ldq_p(zmmh+8); - env->xmm_regs[i].XMM_Q(6) = ldq_p(zmmh+16); - env->xmm_regs[i].XMM_Q(7) = ldq_p(zmmh+24); + env->xmm_regs[i].ZMM_Q(0) = ldq_p(xmm); + env->xmm_regs[i].ZMM_Q(1) = ldq_p(xmm+8); + env->xmm_regs[i].ZMM_Q(2) = ldq_p(ymmh); + env->xmm_regs[i].ZMM_Q(3) = ldq_p(ymmh+8); + env->xmm_regs[i].ZMM_Q(4) = ldq_p(zmmh); + env->xmm_regs[i].ZMM_Q(5) = ldq_p(zmmh+8); + env->xmm_regs[i].ZMM_Q(6) = ldq_p(zmmh+16); + env->xmm_regs[i].ZMM_Q(7) = ldq_p(zmmh+24); } #ifdef TARGET_X86_64 memcpy(&env->xmm_regs[16], &xsave->region[XSAVE_Hi16_ZMM], 16 * sizeof env->xmm_regs[16]); + memcpy(&env->pkru, &xsave->region[XSAVE_PKRU], sizeof env->pkru); #endif return 0; } @@ -1571,7 +1813,7 @@ static int kvm_get_xcrs(X86CPU *cpu) int i, ret; struct kvm_xcrs xcrs; - if (!kvm_has_xcrs()) { + if (!has_xcrs) { return 0; } @@ -1643,13 +1885,16 @@ static int kvm_get_sregs(X86CPU *cpu) HF_OSFXSR_MASK | HF_LMA_MASK | HF_CS32_MASK | \ HF_SS32_MASK | HF_CS64_MASK | HF_ADDSEG_MASK) - hflags = (env->segs[R_SS].flags >> DESC_DPL_SHIFT) & HF_CPL_MASK; + hflags = env->hflags & HFLAG_COPY_MASK; + hflags |= (env->segs[R_SS].flags >> DESC_DPL_SHIFT) & HF_CPL_MASK; hflags |= (env->cr[0] & CR0_PE_MASK) << (HF_PE_SHIFT - CR0_PE_SHIFT); hflags |= (env->cr[0] << (HF_MP_SHIFT - CR0_MP_SHIFT)) & (HF_MP_MASK | HF_EM_MASK | HF_TS_MASK); hflags |= (env->eflags & (HF_TF_MASK | HF_VM_MASK | HF_IOPL_MASK)); - hflags |= (env->cr[4] & CR4_OSFXSR_MASK) << - (HF_OSFXSR_SHIFT - CR4_OSFXSR_SHIFT); + + if (env->cr[4] & CR4_OSFXSR_MASK) { + hflags |= HF_OSFXSR_MASK; + } if (env->efer & MSR_EFER_LMA) { hflags |= HF_LMA_MASK; @@ -1670,7 +1915,7 @@ static int kvm_get_sregs(X86CPU *cpu) env->segs[R_SS].base) != 0) << HF_ADDSEG_SHIFT; } } - env->hflags = (env->hflags & HFLAG_COPY_MASK) | hflags; + env->hflags = hflags; return 0; } @@ -1696,6 +1941,9 @@ static int kvm_get_msrs(X86CPU *cpu) if (has_msr_hsave_pa) { msrs[n++].index = MSR_VM_HSAVE_PA; } + if (has_msr_tsc_aux) { + msrs[n++].index = MSR_TSC_AUX; + } if (has_msr_tsc_adjust) { msrs[n++].index = MSR_TSC_ADJUST; } @@ -1775,6 +2023,35 @@ static int kvm_get_msrs(X86CPU *cpu) if (has_msr_hv_tsc) { msrs[n++].index = HV_X64_MSR_REFERENCE_TSC; } + if (has_msr_hv_crash) { + int j; + + for (j = 0; j < HV_X64_MSR_CRASH_PARAMS; j++) { + msrs[n++].index = HV_X64_MSR_CRASH_P0 + j; + } + } + if (has_msr_hv_runtime) { + msrs[n++].index = HV_X64_MSR_VP_RUNTIME; + } + if (cpu->hyperv_synic) { + uint32_t msr; + + msrs[n++].index = HV_X64_MSR_SCONTROL; + msrs[n++].index = HV_X64_MSR_SVERSION; + msrs[n++].index = HV_X64_MSR_SIEFP; + msrs[n++].index = HV_X64_MSR_SIMP; + for (msr = HV_X64_MSR_SINT0; msr <= HV_X64_MSR_SINT15; msr++) { + msrs[n++].index = msr; + } + } + if (has_msr_hv_stimer) { + uint32_t msr; + + for (msr = HV_X64_MSR_STIMER0_CONFIG; msr <= HV_X64_MSR_STIMER3_COUNT; + msr++) { + msrs[n++].index = msr; + } + } if (has_msr_mtrr) { msrs[n++].index = MSR_MTRRdefType; msrs[n++].index = MSR_MTRRfix64K_00000; @@ -1803,6 +2080,7 @@ static int kvm_get_msrs(X86CPU *cpu) return ret; } + assert(ret == n); for (i = 0; i < ret; i++) { uint32_t index = msrs[i].index; switch (index) { @@ -1838,6 +2116,9 @@ static int kvm_get_msrs(X86CPU *cpu) case MSR_IA32_TSC: env->tsc = msrs[i].data; break; + case MSR_TSC_AUX: + env->tsc_aux = msrs[i].data; + break; case MSR_TSC_ADJUST: env->tsc_adjust = msrs[i].data; break; @@ -1922,6 +2203,41 @@ static int kvm_get_msrs(X86CPU *cpu) case HV_X64_MSR_REFERENCE_TSC: env->msr_hv_tsc = msrs[i].data; break; + case HV_X64_MSR_CRASH_P0 ... HV_X64_MSR_CRASH_P4: + env->msr_hv_crash_params[index - HV_X64_MSR_CRASH_P0] = msrs[i].data; + break; + case HV_X64_MSR_VP_RUNTIME: + env->msr_hv_runtime = msrs[i].data; + break; + case HV_X64_MSR_SCONTROL: + env->msr_hv_synic_control = msrs[i].data; + break; + case HV_X64_MSR_SVERSION: + env->msr_hv_synic_version = msrs[i].data; + break; + case HV_X64_MSR_SIEFP: + env->msr_hv_synic_evt_page = msrs[i].data; + break; + case HV_X64_MSR_SIMP: + env->msr_hv_synic_msg_page = msrs[i].data; + break; + case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15: + env->msr_hv_synic_sint[index - HV_X64_MSR_SINT0] = msrs[i].data; + break; + case HV_X64_MSR_STIMER0_CONFIG: + case HV_X64_MSR_STIMER1_CONFIG: + case HV_X64_MSR_STIMER2_CONFIG: + case HV_X64_MSR_STIMER3_CONFIG: + env->msr_hv_stimer_config[(index - HV_X64_MSR_STIMER0_CONFIG)/2] = + msrs[i].data; + break; + case HV_X64_MSR_STIMER0_COUNT: + case HV_X64_MSR_STIMER1_COUNT: + case HV_X64_MSR_STIMER2_COUNT: + case HV_X64_MSR_STIMER3_COUNT: + env->msr_hv_stimer_count[(index - HV_X64_MSR_STIMER0_COUNT)/2] = + msrs[i].data; + break; case MSR_MTRRdefType: env->mtrr_deftype = msrs[i].data; break; @@ -2221,13 +2537,22 @@ int kvm_arch_put_registers(CPUState *cpu, int level) assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); - if (level >= KVM_PUT_RESET_STATE && has_msr_feature_control) { + if (level >= KVM_PUT_RESET_STATE) { ret = kvm_put_msr_feature_control(x86_cpu); if (ret < 0) { return ret; } } + if (level == KVM_PUT_FULL_STATE) { + /* We don't check for kvm_arch_set_tsc_khz() errors here, + * because TSC frequency mismatch shouldn't abort migration, + * unless the user explicitly asked for a more strict TSC + * setting (e.g. using an explicit "tsc-freq" option). + */ + kvm_arch_set_tsc_khz(cpu); + } + ret = kvm_getput_regs(x86_cpu, 1); if (ret < 0) { return ret; @@ -2294,41 +2619,44 @@ int kvm_arch_get_registers(CPUState *cs) ret = kvm_getput_regs(cpu, 0); if (ret < 0) { - return ret; + goto out; } ret = kvm_get_xsave(cpu); if (ret < 0) { - return ret; + goto out; } ret = kvm_get_xcrs(cpu); if (ret < 0) { - return ret; + goto out; } ret = kvm_get_sregs(cpu); if (ret < 0) { - return ret; + goto out; } ret = kvm_get_msrs(cpu); if (ret < 0) { - return ret; + goto out; } ret = kvm_get_mp_state(cpu); if (ret < 0) { - return ret; + goto out; } ret = kvm_get_apic(cpu); if (ret < 0) { - return ret; + goto out; } ret = kvm_get_vcpu_events(cpu); if (ret < 0) { - return ret; + goto out; } ret = kvm_get_debugregs(cpu); if (ret < 0) { - return ret; + goto out; } - return 0; + ret = 0; + out: + cpu_sync_bndcs_hflags(&cpu->env); + return ret; } void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) @@ -2363,7 +2691,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) } } - if (!kvm_irqchip_in_kernel()) { + if (!kvm_pic_in_kernel()) { qemu_mutex_lock_iothread(); } @@ -2381,7 +2709,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) } } - if (!kvm_irqchip_in_kernel()) { + if (!kvm_pic_in_kernel()) { /* Try to inject an interrupt if the guest can accept it */ if (run->ready_for_interrupt_injection && (cpu->interrupt_request & CPU_INTERRUPT_HARD) && @@ -2780,6 +3108,13 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) ret = kvm_handle_debug(cpu, &run->debug.arch); qemu_mutex_unlock_iothread(); break; + case KVM_EXIT_HYPERV: + ret = kvm_hv_handle_exit(cpu, &run->hyperv); + break; + case KVM_EXIT_IOAPIC_EOI: + ioapic_eoi_broadcast(run->eoi.vector); + ret = 0; + break; default: fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); ret = -1; @@ -2814,6 +3149,39 @@ void kvm_arch_init_irq_routing(KVMState *s) */ kvm_msi_via_irqfd_allowed = true; kvm_gsi_routing_allowed = true; + + if (kvm_irqchip_is_split()) { + int i; + + /* If the ioapic is in QEMU and the lapics are in KVM, reserve + MSI routes for signaling interrupts to the local apics. */ + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + struct MSIMessage msg = { 0x0, 0x0 }; + if (kvm_irqchip_add_msi_route(s, msg, NULL) < 0) { + error_report("Could not enable split IRQ mode."); + exit(1); + } + } + } +} + +int kvm_arch_irqchip_create(MachineState *ms, KVMState *s) +{ + int ret; + if (machine_kernel_irqchip_split(ms)) { + ret = kvm_vm_enable_cap(s, KVM_CAP_SPLIT_IRQCHIP, 0, 24); + if (ret) { + error_report("Could not enable split irqchip mode: %s\n", + strerror(-ret)); + exit(1); + } else { + DPRINTF("Enabled KVM_CAP_SPLIT_IRQCHIP\n"); + kvm_split_irqchip = true; + return 1; + } + } else { + return 0; + } } /* Classic KVM device assignment interface. Will remain x86 only. */ @@ -2957,7 +3325,7 @@ int kvm_device_msix_deassign(KVMState *s, uint32_t dev_id) } int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, - uint64_t address, uint32_t data) + uint64_t address, uint32_t data, PCIDevice *dev) { return 0; } diff --git a/qemu/target-i386/kvm_i386.h b/qemu/target-i386/kvm_i386.h index c1b312ba2..42b00af1b 100644 --- a/qemu/target-i386/kvm_i386.h +++ b/qemu/target-i386/kvm_i386.h @@ -13,6 +13,8 @@ #include "sysemu/kvm.h" +#define kvm_apic_in_kernel() (kvm_irqchip_in_kernel()) + bool kvm_allows_irq0_override(void); bool kvm_has_smm(void); void kvm_synchronize_all_tsc(void); diff --git a/qemu/target-i386/machine.c b/qemu/target-i386/machine.c index a0df64b57..ee5b94922 100644 --- a/qemu/target-i386/machine.c +++ b/qemu/target-i386/machine.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/boards.h" #include "hw/i386/pc.h" @@ -6,6 +7,8 @@ #include "cpu.h" #include "sysemu/kvm.h" +#include "qemu/error-report.h" + static const VMStateDescription vmstate_segment = { .name = "segment", .version_id = 1, @@ -36,15 +39,15 @@ static const VMStateDescription vmstate_xmm_reg = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_UINT64(XMM_Q(0), XMMReg), - VMSTATE_UINT64(XMM_Q(1), XMMReg), + VMSTATE_UINT64(ZMM_Q(0), ZMMReg), + VMSTATE_UINT64(ZMM_Q(1), ZMMReg), VMSTATE_END_OF_LIST() } }; #define VMSTATE_XMM_REGS(_field, _state, _start) \ VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, CPU_NB_REGS, 0, \ - vmstate_xmm_reg, XMMReg) + vmstate_xmm_reg, ZMMReg) /* YMMH format is the same as XMM, but for bits 128-255 */ static const VMStateDescription vmstate_ymmh_reg = { @@ -52,32 +55,32 @@ static const VMStateDescription vmstate_ymmh_reg = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_UINT64(XMM_Q(2), XMMReg), - VMSTATE_UINT64(XMM_Q(3), XMMReg), + VMSTATE_UINT64(ZMM_Q(2), ZMMReg), + VMSTATE_UINT64(ZMM_Q(3), ZMMReg), VMSTATE_END_OF_LIST() } }; #define VMSTATE_YMMH_REGS_VARS(_field, _state, _start, _v) \ VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, CPU_NB_REGS, _v, \ - vmstate_ymmh_reg, XMMReg) + vmstate_ymmh_reg, ZMMReg) static const VMStateDescription vmstate_zmmh_reg = { .name = "zmmh_reg", .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_UINT64(XMM_Q(4), XMMReg), - VMSTATE_UINT64(XMM_Q(5), XMMReg), - VMSTATE_UINT64(XMM_Q(6), XMMReg), - VMSTATE_UINT64(XMM_Q(7), XMMReg), + VMSTATE_UINT64(ZMM_Q(4), ZMMReg), + VMSTATE_UINT64(ZMM_Q(5), ZMMReg), + VMSTATE_UINT64(ZMM_Q(6), ZMMReg), + VMSTATE_UINT64(ZMM_Q(7), ZMMReg), VMSTATE_END_OF_LIST() } }; #define VMSTATE_ZMMH_REGS_VARS(_field, _state, _start) \ VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, CPU_NB_REGS, 0, \ - vmstate_zmmh_reg, XMMReg) + vmstate_zmmh_reg, ZMMReg) #ifdef TARGET_X86_64 static const VMStateDescription vmstate_hi16_zmm_reg = { @@ -85,21 +88,21 @@ static const VMStateDescription vmstate_hi16_zmm_reg = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_UINT64(XMM_Q(0), XMMReg), - VMSTATE_UINT64(XMM_Q(1), XMMReg), - VMSTATE_UINT64(XMM_Q(2), XMMReg), - VMSTATE_UINT64(XMM_Q(3), XMMReg), - VMSTATE_UINT64(XMM_Q(4), XMMReg), - VMSTATE_UINT64(XMM_Q(5), XMMReg), - VMSTATE_UINT64(XMM_Q(6), XMMReg), - VMSTATE_UINT64(XMM_Q(7), XMMReg), + VMSTATE_UINT64(ZMM_Q(0), ZMMReg), + VMSTATE_UINT64(ZMM_Q(1), ZMMReg), + VMSTATE_UINT64(ZMM_Q(2), ZMMReg), + VMSTATE_UINT64(ZMM_Q(3), ZMMReg), + VMSTATE_UINT64(ZMM_Q(4), ZMMReg), + VMSTATE_UINT64(ZMM_Q(5), ZMMReg), + VMSTATE_UINT64(ZMM_Q(6), ZMMReg), + VMSTATE_UINT64(ZMM_Q(7), ZMMReg), VMSTATE_END_OF_LIST() } }; #define VMSTATE_Hi16_ZMM_REGS_VARS(_field, _state, _start) \ VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, CPU_NB_REGS, 0, \ - vmstate_hi16_zmm_reg, XMMReg) + vmstate_hi16_zmm_reg, ZMMReg) #endif static const VMStateDescription vmstate_bnd_regs = { @@ -331,6 +334,13 @@ static int cpu_post_load(void *opaque, int version_id) CPUX86State *env = &cpu->env; int i; + if (env->tsc_khz && env->user_tsc_khz && + env->tsc_khz != env->user_tsc_khz) { + error_report("Mismatch between user-specified TSC frequency and " + "migrated TSC frequency"); + return -EINVAL; + } + /* * Real mode guest segments register DPL should be zero. * Older KVM version were setting it wrongly. @@ -367,8 +377,12 @@ static int cpu_post_load(void *opaque, int version_id) cpu_breakpoint_remove_all(cs, BP_CPU); cpu_watchpoint_remove_all(cs, BP_CPU); - for (i = 0; i < DR7_MAX_BP; i++) { - hw_breakpoint_insert(env, i); + { + /* Indicate all breakpoints disabled, as they are, then + let the helper re-enable them. */ + target_ulong dr7 = env->dr[7]; + env->dr[7] = dr7 & ~(DR7_GLOBAL_BP_MASK | DR7_LOCAL_BP_MASK); + cpu_x86_update_dr7(env, dr7); } tlb_flush(cs, 1); @@ -661,6 +675,115 @@ static const VMStateDescription vmstate_msr_hyperv_time = { } }; +static bool hyperv_crash_enable_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + int i; + + for (i = 0; i < HV_X64_MSR_CRASH_PARAMS; i++) { + if (env->msr_hv_crash_params[i]) { + return true; + } + } + return false; +} + +static const VMStateDescription vmstate_msr_hyperv_crash = { + .name = "cpu/msr_hyperv_crash", + .version_id = 1, + .minimum_version_id = 1, + .needed = hyperv_crash_enable_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64_ARRAY(env.msr_hv_crash_params, + X86CPU, HV_X64_MSR_CRASH_PARAMS), + VMSTATE_END_OF_LIST() + } +}; + +static bool hyperv_runtime_enable_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return env->msr_hv_runtime != 0; +} + +static const VMStateDescription vmstate_msr_hyperv_runtime = { + .name = "cpu/msr_hyperv_runtime", + .version_id = 1, + .minimum_version_id = 1, + .needed = hyperv_runtime_enable_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(env.msr_hv_runtime, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + +static bool hyperv_synic_enable_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + int i; + + if (env->msr_hv_synic_control != 0 || + env->msr_hv_synic_evt_page != 0 || + env->msr_hv_synic_msg_page != 0) { + return true; + } + + for (i = 0; i < ARRAY_SIZE(env->msr_hv_synic_sint); i++) { + if (env->msr_hv_synic_sint[i] != 0) { + return true; + } + } + + return false; +} + +static const VMStateDescription vmstate_msr_hyperv_synic = { + .name = "cpu/msr_hyperv_synic", + .version_id = 1, + .minimum_version_id = 1, + .needed = hyperv_synic_enable_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(env.msr_hv_synic_control, X86CPU), + VMSTATE_UINT64(env.msr_hv_synic_evt_page, X86CPU), + VMSTATE_UINT64(env.msr_hv_synic_msg_page, X86CPU), + VMSTATE_UINT64_ARRAY(env.msr_hv_synic_sint, X86CPU, + HV_SYNIC_SINT_COUNT), + VMSTATE_END_OF_LIST() + } +}; + +static bool hyperv_stimer_enable_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + int i; + + for (i = 0; i < ARRAY_SIZE(env->msr_hv_stimer_config); i++) { + if (env->msr_hv_stimer_config[i] || env->msr_hv_stimer_count[i]) { + return true; + } + } + return false; +} + +static const VMStateDescription vmstate_msr_hyperv_stimer = { + .name = "cpu/msr_hyperv_stimer", + .version_id = 1, + .minimum_version_id = 1, + .needed = hyperv_stimer_enable_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64_ARRAY(env.msr_hv_stimer_config, + X86CPU, HV_SYNIC_STIMER_COUNT), + VMSTATE_UINT64_ARRAY(env.msr_hv_stimer_count, + X86CPU, HV_SYNIC_STIMER_COUNT), + VMSTATE_END_OF_LIST() + } +}; + static bool avx512_needed(void *opaque) { X86CPU *cpu = opaque; @@ -674,7 +797,7 @@ static bool avx512_needed(void *opaque) } for (i = 0; i < CPU_NB_REGS; i++) { -#define ENV_XMM(reg, field) (env->xmm_regs[reg].XMM_Q(field)) +#define ENV_XMM(reg, field) (env->xmm_regs[reg].ZMM_Q(field)) if (ENV_XMM(i, 4) || ENV_XMM(i, 6) || ENV_XMM(i, 5) || ENV_XMM(i, 7)) { return true; @@ -726,6 +849,47 @@ static const VMStateDescription vmstate_xss = { } }; +#ifdef TARGET_X86_64 +static bool pkru_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return env->pkru != 0; +} + +static const VMStateDescription vmstate_pkru = { + .name = "cpu/pkru", + .version_id = 1, + .minimum_version_id = 1, + .needed = pkru_needed, + .fields = (VMStateField[]){ + VMSTATE_UINT32(env.pkru, X86CPU), + VMSTATE_END_OF_LIST() + } +}; +#endif + +static bool tsc_khz_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); + PCMachineClass *pcmc = PC_MACHINE_CLASS(mc); + return env->tsc_khz && pcmc->save_tsc_khz; +} + +static const VMStateDescription vmstate_tsc_khz = { + .name = "cpu/tsc_khz", + .version_id = 1, + .minimum_version_id = 1, + .needed = tsc_khz_needed, + .fields = (VMStateField[]) { + VMSTATE_INT64(env.tsc_khz, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + VMStateDescription vmstate_x86_cpu = { .name = "cpu", .version_id = 12, @@ -842,8 +1006,16 @@ VMStateDescription vmstate_x86_cpu = { &vmstate_msr_hypercall_hypercall, &vmstate_msr_hyperv_vapic, &vmstate_msr_hyperv_time, + &vmstate_msr_hyperv_crash, + &vmstate_msr_hyperv_runtime, + &vmstate_msr_hyperv_synic, + &vmstate_msr_hyperv_stimer, &vmstate_avx512, &vmstate_xss, + &vmstate_tsc_khz, +#ifdef TARGET_X86_64 + &vmstate_pkru, +#endif NULL } }; diff --git a/qemu/target-i386/mem_helper.c b/qemu/target-i386/mem_helper.c index 1aec8a5f1..85e75161b 100644 --- a/qemu/target-i386/mem_helper.c +++ b/qemu/target-i386/mem_helper.c @@ -17,37 +17,58 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" /* broken thread support */ -static spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED; +#if defined(CONFIG_USER_ONLY) +QemuMutex global_cpu_lock; void helper_lock(void) { - spin_lock(&global_cpu_lock); + qemu_mutex_lock(&global_cpu_lock); } void helper_unlock(void) { - spin_unlock(&global_cpu_lock); + qemu_mutex_unlock(&global_cpu_lock); } +void helper_lock_init(void) +{ + qemu_mutex_init(&global_cpu_lock); +} +#else +void helper_lock(void) +{ +} + +void helper_unlock(void) +{ +} + +void helper_lock_init(void) +{ +} +#endif + void helper_cmpxchg8b(CPUX86State *env, target_ulong a0) { uint64_t d; int eflags; eflags = cpu_cc_compute_all(env, CC_OP); - d = cpu_ldq_data(env, a0); + d = cpu_ldq_data_ra(env, a0, GETPC()); if (d == (((uint64_t)env->regs[R_EDX] << 32) | (uint32_t)env->regs[R_EAX])) { - cpu_stq_data(env, a0, ((uint64_t)env->regs[R_ECX] << 32) | (uint32_t)env->regs[R_EBX]); + cpu_stq_data_ra(env, a0, ((uint64_t)env->regs[R_ECX] << 32) + | (uint32_t)env->regs[R_EBX], GETPC()); eflags |= CC_Z; } else { /* always do the store */ - cpu_stq_data(env, a0, d); + cpu_stq_data_ra(env, a0, d, GETPC()); env->regs[R_EDX] = (uint32_t)(d >> 32); env->regs[R_EAX] = (uint32_t)d; eflags &= ~CC_Z; @@ -62,19 +83,19 @@ void helper_cmpxchg16b(CPUX86State *env, target_ulong a0) int eflags; if ((a0 & 0xf) != 0) { - raise_exception(env, EXCP0D_GPF); + raise_exception_ra(env, EXCP0D_GPF, GETPC()); } eflags = cpu_cc_compute_all(env, CC_OP); - d0 = cpu_ldq_data(env, a0); - d1 = cpu_ldq_data(env, a0 + 8); + d0 = cpu_ldq_data_ra(env, a0, GETPC()); + d1 = cpu_ldq_data_ra(env, a0 + 8, GETPC()); if (d0 == env->regs[R_EAX] && d1 == env->regs[R_EDX]) { - cpu_stq_data(env, a0, env->regs[R_EBX]); - cpu_stq_data(env, a0 + 8, env->regs[R_ECX]); + cpu_stq_data_ra(env, a0, env->regs[R_EBX], GETPC()); + cpu_stq_data_ra(env, a0 + 8, env->regs[R_ECX], GETPC()); eflags |= CC_Z; } else { /* always do the store */ - cpu_stq_data(env, a0, d0); - cpu_stq_data(env, a0 + 8, d1); + cpu_stq_data_ra(env, a0, d0, GETPC()); + cpu_stq_data_ra(env, a0 + 8, d1, GETPC()); env->regs[R_EDX] = d1; env->regs[R_EAX] = d0; eflags &= ~CC_Z; @@ -87,11 +108,14 @@ void helper_boundw(CPUX86State *env, target_ulong a0, int v) { int low, high; - low = cpu_ldsw_data(env, a0); - high = cpu_ldsw_data(env, a0 + 2); + low = cpu_ldsw_data_ra(env, a0, GETPC()); + high = cpu_ldsw_data_ra(env, a0 + 2, GETPC()); v = (int16_t)v; if (v < low || v > high) { - raise_exception(env, EXCP05_BOUND); + if (env->hflags & HF_MPX_EN_MASK) { + env->bndcs_regs.sts = 0; + } + raise_exception_ra(env, EXCP05_BOUND, GETPC()); } } @@ -99,10 +123,13 @@ void helper_boundl(CPUX86State *env, target_ulong a0, int v) { int low, high; - low = cpu_ldl_data(env, a0); - high = cpu_ldl_data(env, a0 + 4); + low = cpu_ldl_data_ra(env, a0, GETPC()); + high = cpu_ldl_data_ra(env, a0 + 4, GETPC()); if (v < low || v > high) { - raise_exception(env, EXCP05_BOUND); + if (env->hflags & HF_MPX_EN_MASK) { + env->bndcs_regs.sts = 0; + } + raise_exception_ra(env, EXCP05_BOUND, GETPC()); } } @@ -122,11 +149,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx, X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; - if (retaddr) { - /* now we have a real cpu fault */ - cpu_restore_state(cs, retaddr); - } - raise_exception_err(env, cs->exception_index, env->error_code); + raise_exception_err_ra(env, cs->exception_index, env->error_code, retaddr); } } #endif diff --git a/qemu/target-i386/misc_helper.c b/qemu/target-i386/misc_helper.c index 52c5d65e9..e31ec976a 100644 --- a/qemu/target-i386/misc_helper.c +++ b/qemu/target-i386/misc_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" @@ -95,15 +96,6 @@ void helper_into(CPUX86State *env, int next_eip_addend) } } -void helper_single_step(CPUX86State *env) -{ -#ifndef CONFIG_USER_ONLY - check_hw_breakpoints(env, true); - env->dr[6] |= DR6_BS; -#endif - raise_exception(env, EXCP01_DB); -} - void helper_cpuid(CPUX86State *env) { uint32_t eax, ebx, ecx, edx; @@ -127,10 +119,6 @@ target_ulong helper_read_crN(CPUX86State *env, int reg) void helper_write_crN(CPUX86State *env, int reg, target_ulong t0) { } - -void helper_movl_drN_T0(CPUX86State *env, int reg, target_ulong t0) -{ -} #else target_ulong helper_read_crN(CPUX86State *env, int reg) { @@ -176,27 +164,6 @@ void helper_write_crN(CPUX86State *env, int reg, target_ulong t0) break; } } - -void helper_movl_drN_T0(CPUX86State *env, int reg, target_ulong t0) -{ - int i; - - if (reg < 4) { - hw_breakpoint_remove(env, reg); - env->dr[reg] = t0; - hw_breakpoint_insert(env, reg); - } else if (reg == 7) { - for (i = 0; i < DR7_MAX_BP; i++) { - hw_breakpoint_remove(env, i); - } - env->dr[7] = t0; - for (i = 0; i < DR7_MAX_BP; i++) { - hw_breakpoint_insert(env, i); - } - } else { - env->dr[reg] = t0; - } -} #endif void helper_lmsw(CPUX86State *env, target_ulong t0) @@ -220,7 +187,7 @@ void helper_rdtsc(CPUX86State *env) uint64_t val; if ((env->cr[4] & CR4_TSD_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) { - raise_exception(env, EXCP0D_GPF); + raise_exception_ra(env, EXCP0D_GPF, GETPC()); } cpu_svm_check_intercept_param(env, SVM_EXIT_RDTSC, 0); @@ -238,7 +205,7 @@ void helper_rdtscp(CPUX86State *env) void helper_rdpmc(CPUX86State *env) { if ((env->cr[4] & CR4_PCE_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) { - raise_exception(env, EXCP0D_GPF); + raise_exception_ra(env, EXCP0D_GPF, GETPC()); } cpu_svm_check_intercept_param(env, SVM_EXIT_RDPMC, 0); @@ -394,6 +361,12 @@ void helper_wrmsr(CPUX86State *env) case MSR_IA32_MISC_ENABLE: env->msr_ia32_misc_enable = val; break; + case MSR_IA32_BNDCFGS: + /* FIXME: #GP if reserved bits are set. */ + /* FIXME: Extend highest implemented bit of linear address. */ + env->msr_bndcfgs = val; + cpu_sync_bndcs_hflags(env); + break; default: if ((uint32_t)env->regs[R_ECX] >= MSR_MC0_CTL && (uint32_t)env->regs[R_ECX] < MSR_MC0_CTL + @@ -539,6 +512,9 @@ void helper_rdmsr(CPUX86State *env) case MSR_IA32_MISC_ENABLE: val = env->msr_ia32_misc_enable; break; + case MSR_IA32_BNDCFGS: + val = env->msr_bndcfgs; + break; default: if ((uint32_t)env->regs[R_ECX] >= MSR_MC0_CTL && (uint32_t)env->regs[R_ECX] < MSR_MC0_CTL + @@ -589,7 +565,7 @@ void helper_hlt(CPUX86State *env, int next_eip_addend) void helper_monitor(CPUX86State *env, target_ulong ptr) { if ((uint32_t)env->regs[R_ECX] != 0) { - raise_exception(env, EXCP0D_GPF); + raise_exception_ra(env, EXCP0D_GPF, GETPC()); } /* XXX: store address? */ cpu_svm_check_intercept_param(env, SVM_EXIT_MONITOR, 0); @@ -601,7 +577,7 @@ void helper_mwait(CPUX86State *env, int next_eip_addend) X86CPU *cpu; if ((uint32_t)env->regs[R_ECX] != 0) { - raise_exception(env, EXCP0D_GPF); + raise_exception_ra(env, EXCP0D_GPF, GETPC()); } cpu_svm_check_intercept_param(env, SVM_EXIT_MWAIT, 0); env->eip += next_eip_addend; @@ -633,3 +609,30 @@ void helper_debug(CPUX86State *env) cs->exception_index = EXCP_DEBUG; cpu_loop_exit(cs); } + +uint64_t helper_rdpkru(CPUX86State *env, uint32_t ecx) +{ + if ((env->cr[4] & CR4_PKE_MASK) == 0) { + raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); + } + if (ecx != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } + + return env->pkru; +} + +void helper_wrpkru(CPUX86State *env, uint32_t ecx, uint64_t val) +{ + CPUState *cs = CPU(x86_env_get_cpu(env)); + + if ((env->cr[4] & CR4_PKE_MASK) == 0) { + raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); + } + if (ecx != 0 || (val & 0xFFFFFFFF00000000ull)) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } + + env->pkru = val; + tlb_flush(cs, 1); +} diff --git a/qemu/target-i386/monitor.c b/qemu/target-i386/monitor.c new file mode 100644 index 000000000..fccfe40ab --- /dev/null +++ b/qemu/target-i386/monitor.c @@ -0,0 +1,512 @@ +/* + * QEMU monitor + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "cpu.h" +#include "monitor/monitor.h" +#include "monitor/hmp-target.h" +#include "hw/i386/pc.h" +#include "sysemu/kvm.h" +#include "hmp.h" + + +static void print_pte(Monitor *mon, hwaddr addr, + hwaddr pte, + hwaddr mask) +{ +#ifdef TARGET_X86_64 + if (addr & (1ULL << 47)) { + addr |= -1LL << 48; + } +#endif + monitor_printf(mon, TARGET_FMT_plx ": " TARGET_FMT_plx + " %c%c%c%c%c%c%c%c%c\n", + addr, + pte & mask, + pte & PG_NX_MASK ? 'X' : '-', + pte & PG_GLOBAL_MASK ? 'G' : '-', + pte & PG_PSE_MASK ? 'P' : '-', + pte & PG_DIRTY_MASK ? 'D' : '-', + pte & PG_ACCESSED_MASK ? 'A' : '-', + pte & PG_PCD_MASK ? 'C' : '-', + pte & PG_PWT_MASK ? 'T' : '-', + pte & PG_USER_MASK ? 'U' : '-', + pte & PG_RW_MASK ? 'W' : '-'); +} + +static void tlb_info_32(Monitor *mon, CPUArchState *env) +{ + unsigned int l1, l2; + uint32_t pgd, pde, pte; + + pgd = env->cr[3] & ~0xfff; + for(l1 = 0; l1 < 1024; l1++) { + cpu_physical_memory_read(pgd + l1 * 4, &pde, 4); + pde = le32_to_cpu(pde); + if (pde & PG_PRESENT_MASK) { + if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { + /* 4M pages */ + print_pte(mon, (l1 << 22), pde, ~((1 << 21) - 1)); + } else { + for(l2 = 0; l2 < 1024; l2++) { + cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, &pte, 4); + pte = le32_to_cpu(pte); + if (pte & PG_PRESENT_MASK) { + print_pte(mon, (l1 << 22) + (l2 << 12), + pte & ~PG_PSE_MASK, + ~0xfff); + } + } + } + } + } +} + +static void tlb_info_pae32(Monitor *mon, CPUArchState *env) +{ + unsigned int l1, l2, l3; + uint64_t pdpe, pde, pte; + uint64_t pdp_addr, pd_addr, pt_addr; + + pdp_addr = env->cr[3] & ~0x1f; + for (l1 = 0; l1 < 4; l1++) { + cpu_physical_memory_read(pdp_addr + l1 * 8, &pdpe, 8); + pdpe = le64_to_cpu(pdpe); + if (pdpe & PG_PRESENT_MASK) { + pd_addr = pdpe & 0x3fffffffff000ULL; + for (l2 = 0; l2 < 512; l2++) { + cpu_physical_memory_read(pd_addr + l2 * 8, &pde, 8); + pde = le64_to_cpu(pde); + if (pde & PG_PRESENT_MASK) { + if (pde & PG_PSE_MASK) { + /* 2M pages with PAE, CR4.PSE is ignored */ + print_pte(mon, (l1 << 30 ) + (l2 << 21), pde, + ~((hwaddr)(1 << 20) - 1)); + } else { + pt_addr = pde & 0x3fffffffff000ULL; + for (l3 = 0; l3 < 512; l3++) { + cpu_physical_memory_read(pt_addr + l3 * 8, &pte, 8); + pte = le64_to_cpu(pte); + if (pte & PG_PRESENT_MASK) { + print_pte(mon, (l1 << 30 ) + (l2 << 21) + + (l3 << 12), + pte & ~PG_PSE_MASK, + ~(hwaddr)0xfff); + } + } + } + } + } + } + } +} + +#ifdef TARGET_X86_64 +static void tlb_info_64(Monitor *mon, CPUArchState *env) +{ + uint64_t l1, l2, l3, l4; + uint64_t pml4e, pdpe, pde, pte; + uint64_t pml4_addr, pdp_addr, pd_addr, pt_addr; + + pml4_addr = env->cr[3] & 0x3fffffffff000ULL; + for (l1 = 0; l1 < 512; l1++) { + cpu_physical_memory_read(pml4_addr + l1 * 8, &pml4e, 8); + pml4e = le64_to_cpu(pml4e); + if (pml4e & PG_PRESENT_MASK) { + pdp_addr = pml4e & 0x3fffffffff000ULL; + for (l2 = 0; l2 < 512; l2++) { + cpu_physical_memory_read(pdp_addr + l2 * 8, &pdpe, 8); + pdpe = le64_to_cpu(pdpe); + if (pdpe & PG_PRESENT_MASK) { + if (pdpe & PG_PSE_MASK) { + /* 1G pages, CR4.PSE is ignored */ + print_pte(mon, (l1 << 39) + (l2 << 30), pdpe, + 0x3ffffc0000000ULL); + } else { + pd_addr = pdpe & 0x3fffffffff000ULL; + for (l3 = 0; l3 < 512; l3++) { + cpu_physical_memory_read(pd_addr + l3 * 8, &pde, 8); + pde = le64_to_cpu(pde); + if (pde & PG_PRESENT_MASK) { + if (pde & PG_PSE_MASK) { + /* 2M pages, CR4.PSE is ignored */ + print_pte(mon, (l1 << 39) + (l2 << 30) + + (l3 << 21), pde, + 0x3ffffffe00000ULL); + } else { + pt_addr = pde & 0x3fffffffff000ULL; + for (l4 = 0; l4 < 512; l4++) { + cpu_physical_memory_read(pt_addr + + l4 * 8, + &pte, 8); + pte = le64_to_cpu(pte); + if (pte & PG_PRESENT_MASK) { + print_pte(mon, (l1 << 39) + + (l2 << 30) + + (l3 << 21) + (l4 << 12), + pte & ~PG_PSE_MASK, + 0x3fffffffff000ULL); + } + } + } + } + } + } + } + } + } + } +} +#endif /* TARGET_X86_64 */ + +void hmp_info_tlb(Monitor *mon, const QDict *qdict) +{ + CPUArchState *env; + + env = mon_get_cpu_env(); + + if (!(env->cr[0] & CR0_PG_MASK)) { + monitor_printf(mon, "PG disabled\n"); + return; + } + if (env->cr[4] & CR4_PAE_MASK) { +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + tlb_info_64(mon, env); + } else +#endif + { + tlb_info_pae32(mon, env); + } + } else { + tlb_info_32(mon, env); + } +} + +static void mem_print(Monitor *mon, hwaddr *pstart, + int *plast_prot, + hwaddr end, int prot) +{ + int prot1; + prot1 = *plast_prot; + if (prot != prot1) { + if (*pstart != -1) { + monitor_printf(mon, TARGET_FMT_plx "-" TARGET_FMT_plx " " + TARGET_FMT_plx " %c%c%c\n", + *pstart, end, end - *pstart, + prot1 & PG_USER_MASK ? 'u' : '-', + 'r', + prot1 & PG_RW_MASK ? 'w' : '-'); + } + if (prot != 0) + *pstart = end; + else + *pstart = -1; + *plast_prot = prot; + } +} + +static void mem_info_32(Monitor *mon, CPUArchState *env) +{ + unsigned int l1, l2; + int prot, last_prot; + uint32_t pgd, pde, pte; + hwaddr start, end; + + pgd = env->cr[3] & ~0xfff; + last_prot = 0; + start = -1; + for(l1 = 0; l1 < 1024; l1++) { + cpu_physical_memory_read(pgd + l1 * 4, &pde, 4); + pde = le32_to_cpu(pde); + end = l1 << 22; + if (pde & PG_PRESENT_MASK) { + if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { + prot = pde & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); + mem_print(mon, &start, &last_prot, end, prot); + } else { + for(l2 = 0; l2 < 1024; l2++) { + cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, &pte, 4); + pte = le32_to_cpu(pte); + end = (l1 << 22) + (l2 << 12); + if (pte & PG_PRESENT_MASK) { + prot = pte & pde & + (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); + } else { + prot = 0; + } + mem_print(mon, &start, &last_prot, end, prot); + } + } + } else { + prot = 0; + mem_print(mon, &start, &last_prot, end, prot); + } + } + /* Flush last range */ + mem_print(mon, &start, &last_prot, (hwaddr)1 << 32, 0); +} + +static void mem_info_pae32(Monitor *mon, CPUArchState *env) +{ + unsigned int l1, l2, l3; + int prot, last_prot; + uint64_t pdpe, pde, pte; + uint64_t pdp_addr, pd_addr, pt_addr; + hwaddr start, end; + + pdp_addr = env->cr[3] & ~0x1f; + last_prot = 0; + start = -1; + for (l1 = 0; l1 < 4; l1++) { + cpu_physical_memory_read(pdp_addr + l1 * 8, &pdpe, 8); + pdpe = le64_to_cpu(pdpe); + end = l1 << 30; + if (pdpe & PG_PRESENT_MASK) { + pd_addr = pdpe & 0x3fffffffff000ULL; + for (l2 = 0; l2 < 512; l2++) { + cpu_physical_memory_read(pd_addr + l2 * 8, &pde, 8); + pde = le64_to_cpu(pde); + end = (l1 << 30) + (l2 << 21); + if (pde & PG_PRESENT_MASK) { + if (pde & PG_PSE_MASK) { + prot = pde & (PG_USER_MASK | PG_RW_MASK | + PG_PRESENT_MASK); + mem_print(mon, &start, &last_prot, end, prot); + } else { + pt_addr = pde & 0x3fffffffff000ULL; + for (l3 = 0; l3 < 512; l3++) { + cpu_physical_memory_read(pt_addr + l3 * 8, &pte, 8); + pte = le64_to_cpu(pte); + end = (l1 << 30) + (l2 << 21) + (l3 << 12); + if (pte & PG_PRESENT_MASK) { + prot = pte & pde & (PG_USER_MASK | PG_RW_MASK | + PG_PRESENT_MASK); + } else { + prot = 0; + } + mem_print(mon, &start, &last_prot, end, prot); + } + } + } else { + prot = 0; + mem_print(mon, &start, &last_prot, end, prot); + } + } + } else { + prot = 0; + mem_print(mon, &start, &last_prot, end, prot); + } + } + /* Flush last range */ + mem_print(mon, &start, &last_prot, (hwaddr)1 << 32, 0); +} + + +#ifdef TARGET_X86_64 +static void mem_info_64(Monitor *mon, CPUArchState *env) +{ + int prot, last_prot; + uint64_t l1, l2, l3, l4; + uint64_t pml4e, pdpe, pde, pte; + uint64_t pml4_addr, pdp_addr, pd_addr, pt_addr, start, end; + + pml4_addr = env->cr[3] & 0x3fffffffff000ULL; + last_prot = 0; + start = -1; + for (l1 = 0; l1 < 512; l1++) { + cpu_physical_memory_read(pml4_addr + l1 * 8, &pml4e, 8); + pml4e = le64_to_cpu(pml4e); + end = l1 << 39; + if (pml4e & PG_PRESENT_MASK) { + pdp_addr = pml4e & 0x3fffffffff000ULL; + for (l2 = 0; l2 < 512; l2++) { + cpu_physical_memory_read(pdp_addr + l2 * 8, &pdpe, 8); + pdpe = le64_to_cpu(pdpe); + end = (l1 << 39) + (l2 << 30); + if (pdpe & PG_PRESENT_MASK) { + if (pdpe & PG_PSE_MASK) { + prot = pdpe & (PG_USER_MASK | PG_RW_MASK | + PG_PRESENT_MASK); + prot &= pml4e; + mem_print(mon, &start, &last_prot, end, prot); + } else { + pd_addr = pdpe & 0x3fffffffff000ULL; + for (l3 = 0; l3 < 512; l3++) { + cpu_physical_memory_read(pd_addr + l3 * 8, &pde, 8); + pde = le64_to_cpu(pde); + end = (l1 << 39) + (l2 << 30) + (l3 << 21); + if (pde & PG_PRESENT_MASK) { + if (pde & PG_PSE_MASK) { + prot = pde & (PG_USER_MASK | PG_RW_MASK | + PG_PRESENT_MASK); + prot &= pml4e & pdpe; + mem_print(mon, &start, &last_prot, end, prot); + } else { + pt_addr = pde & 0x3fffffffff000ULL; + for (l4 = 0; l4 < 512; l4++) { + cpu_physical_memory_read(pt_addr + + l4 * 8, + &pte, 8); + pte = le64_to_cpu(pte); + end = (l1 << 39) + (l2 << 30) + + (l3 << 21) + (l4 << 12); + if (pte & PG_PRESENT_MASK) { + prot = pte & (PG_USER_MASK | PG_RW_MASK | + PG_PRESENT_MASK); + prot &= pml4e & pdpe & pde; + } else { + prot = 0; + } + mem_print(mon, &start, &last_prot, end, prot); + } + } + } else { + prot = 0; + mem_print(mon, &start, &last_prot, end, prot); + } + } + } + } else { + prot = 0; + mem_print(mon, &start, &last_prot, end, prot); + } + } + } else { + prot = 0; + mem_print(mon, &start, &last_prot, end, prot); + } + } + /* Flush last range */ + mem_print(mon, &start, &last_prot, (hwaddr)1 << 48, 0); +} +#endif /* TARGET_X86_64 */ + +void hmp_info_mem(Monitor *mon, const QDict *qdict) +{ + CPUArchState *env; + + env = mon_get_cpu_env(); + + if (!(env->cr[0] & CR0_PG_MASK)) { + monitor_printf(mon, "PG disabled\n"); + return; + } + if (env->cr[4] & CR4_PAE_MASK) { +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + mem_info_64(mon, env); + } else +#endif + { + mem_info_pae32(mon, env); + } + } else { + mem_info_32(mon, env); + } +} + +void hmp_mce(Monitor *mon, const QDict *qdict) +{ + X86CPU *cpu; + CPUState *cs; + int cpu_index = qdict_get_int(qdict, "cpu_index"); + int bank = qdict_get_int(qdict, "bank"); + uint64_t status = qdict_get_int(qdict, "status"); + uint64_t mcg_status = qdict_get_int(qdict, "mcg_status"); + uint64_t addr = qdict_get_int(qdict, "addr"); + uint64_t misc = qdict_get_int(qdict, "misc"); + int flags = MCE_INJECT_UNCOND_AO; + + if (qdict_get_try_bool(qdict, "broadcast", false)) { + flags |= MCE_INJECT_BROADCAST; + } + cs = qemu_get_cpu(cpu_index); + if (cs != NULL) { + cpu = X86_CPU(cs); + cpu_x86_inject_mce(mon, cpu, bank, status, mcg_status, addr, misc, + flags); + } +} + +static target_long monitor_get_pc(const struct MonitorDef *md, int val) +{ + CPUArchState *env = mon_get_cpu_env(); + return env->eip + env->segs[R_CS].base; +} + +const MonitorDef monitor_defs[] = { +#define SEG(name, seg) \ + { name, offsetof(CPUX86State, segs[seg].selector), NULL, MD_I32 },\ + { name ".base", offsetof(CPUX86State, segs[seg].base) },\ + { name ".limit", offsetof(CPUX86State, segs[seg].limit), NULL, MD_I32 }, + + { "eax", offsetof(CPUX86State, regs[0]) }, + { "ecx", offsetof(CPUX86State, regs[1]) }, + { "edx", offsetof(CPUX86State, regs[2]) }, + { "ebx", offsetof(CPUX86State, regs[3]) }, + { "esp|sp", offsetof(CPUX86State, regs[4]) }, + { "ebp|fp", offsetof(CPUX86State, regs[5]) }, + { "esi", offsetof(CPUX86State, regs[6]) }, + { "edi", offsetof(CPUX86State, regs[7]) }, +#ifdef TARGET_X86_64 + { "r8", offsetof(CPUX86State, regs[8]) }, + { "r9", offsetof(CPUX86State, regs[9]) }, + { "r10", offsetof(CPUX86State, regs[10]) }, + { "r11", offsetof(CPUX86State, regs[11]) }, + { "r12", offsetof(CPUX86State, regs[12]) }, + { "r13", offsetof(CPUX86State, regs[13]) }, + { "r14", offsetof(CPUX86State, regs[14]) }, + { "r15", offsetof(CPUX86State, regs[15]) }, +#endif + { "eflags", offsetof(CPUX86State, eflags) }, + { "eip", offsetof(CPUX86State, eip) }, + SEG("cs", R_CS) + SEG("ds", R_DS) + SEG("es", R_ES) + SEG("ss", R_SS) + SEG("fs", R_FS) + SEG("gs", R_GS) + { "pc", 0, monitor_get_pc, }, + { NULL }, +}; + +const MonitorDef *target_monitor_defs(void) +{ + return monitor_defs; +} + +void hmp_info_local_apic(Monitor *mon, const QDict *qdict) +{ + x86_cpu_dump_local_apic_state(mon_get_cpu(), (FILE *)mon, monitor_fprintf, + CPU_DUMP_FPU); +} + +void hmp_info_io_apic(Monitor *mon, const QDict *qdict) +{ + if (kvm_irqchip_in_kernel()) { + kvm_ioapic_dump_state(mon, qdict); + } else { + ioapic_dump_state(mon, qdict); + } +} diff --git a/qemu/target-i386/mpx_helper.c b/qemu/target-i386/mpx_helper.c new file mode 100644 index 000000000..4d1785ece --- /dev/null +++ b/qemu/target-i386/mpx_helper.c @@ -0,0 +1,167 @@ +/* + * x86 MPX helpers + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "exec/cpu_ldst.h" + + +void cpu_sync_bndcs_hflags(CPUX86State *env) +{ + uint32_t hflags = env->hflags; + uint32_t hflags2 = env->hflags2; + uint32_t bndcsr; + + if ((hflags & HF_CPL_MASK) == 3) { + bndcsr = env->bndcs_regs.cfgu; + } else { + bndcsr = env->msr_bndcfgs; + } + + if ((env->cr[4] & CR4_OSXSAVE_MASK) + && (env->xcr0 & XSTATE_BNDCSR_MASK) + && (bndcsr & BNDCFG_ENABLE)) { + hflags |= HF_MPX_EN_MASK; + } else { + hflags &= ~HF_MPX_EN_MASK; + } + + if (bndcsr & BNDCFG_BNDPRESERVE) { + hflags2 |= HF2_MPX_PR_MASK; + } else { + hflags2 &= ~HF2_MPX_PR_MASK; + } + + env->hflags = hflags; + env->hflags2 = hflags2; +} + +void helper_bndck(CPUX86State *env, uint32_t fail) +{ + if (unlikely(fail)) { + env->bndcs_regs.sts = 1; + raise_exception_ra(env, EXCP05_BOUND, GETPC()); + } +} + +static uint64_t lookup_bte64(CPUX86State *env, uint64_t base, uintptr_t ra) +{ + uint64_t bndcsr, bde, bt; + + if ((env->hflags & HF_CPL_MASK) == 3) { + bndcsr = env->bndcs_regs.cfgu; + } else { + bndcsr = env->msr_bndcfgs; + } + + bde = (extract64(base, 20, 28) << 3) + (extract64(bndcsr, 20, 44) << 12); + bt = cpu_ldq_data_ra(env, bde, ra); + if ((bt & 1) == 0) { + env->bndcs_regs.sts = bde | 2; + raise_exception_ra(env, EXCP05_BOUND, ra); + } + + return (extract64(base, 3, 17) << 5) + (bt & ~7); +} + +static uint32_t lookup_bte32(CPUX86State *env, uint32_t base, uintptr_t ra) +{ + uint32_t bndcsr, bde, bt; + + if ((env->hflags & HF_CPL_MASK) == 3) { + bndcsr = env->bndcs_regs.cfgu; + } else { + bndcsr = env->msr_bndcfgs; + } + + bde = (extract32(base, 12, 20) << 2) + (bndcsr & TARGET_PAGE_MASK); + bt = cpu_ldl_data_ra(env, bde, ra); + if ((bt & 1) == 0) { + env->bndcs_regs.sts = bde | 2; + raise_exception_ra(env, EXCP05_BOUND, ra); + } + + return (extract32(base, 2, 10) << 4) + (bt & ~3); +} + +uint64_t helper_bndldx64(CPUX86State *env, target_ulong base, target_ulong ptr) +{ + uintptr_t ra = GETPC(); + uint64_t bte, lb, ub, pt; + + bte = lookup_bte64(env, base, ra); + lb = cpu_ldq_data_ra(env, bte, ra); + ub = cpu_ldq_data_ra(env, bte + 8, ra); + pt = cpu_ldq_data_ra(env, bte + 16, ra); + + if (pt != ptr) { + lb = ub = 0; + } + env->mmx_t0.MMX_Q(0) = ub; + return lb; +} + +uint64_t helper_bndldx32(CPUX86State *env, target_ulong base, target_ulong ptr) +{ + uintptr_t ra = GETPC(); + uint32_t bte, lb, ub, pt; + + bte = lookup_bte32(env, base, ra); + lb = cpu_ldl_data_ra(env, bte, ra); + ub = cpu_ldl_data_ra(env, bte + 4, ra); + pt = cpu_ldl_data_ra(env, bte + 8, ra); + + if (pt != ptr) { + lb = ub = 0; + } + return ((uint64_t)ub << 32) | lb; +} + +void helper_bndstx64(CPUX86State *env, target_ulong base, target_ulong ptr, + uint64_t lb, uint64_t ub) +{ + uintptr_t ra = GETPC(); + uint64_t bte; + + bte = lookup_bte64(env, base, ra); + cpu_stq_data_ra(env, bte, lb, ra); + cpu_stq_data_ra(env, bte + 8, ub, ra); + cpu_stq_data_ra(env, bte + 16, ptr, ra); +} + +void helper_bndstx32(CPUX86State *env, target_ulong base, target_ulong ptr, + uint64_t lb, uint64_t ub) +{ + uintptr_t ra = GETPC(); + uint32_t bte; + + bte = lookup_bte32(env, base, ra); + cpu_stl_data_ra(env, bte, lb, ra); + cpu_stl_data_ra(env, bte + 4, ub, ra); + cpu_stl_data_ra(env, bte + 8, ptr, ra); +} + +void helper_bnd_jmp(CPUX86State *env) +{ + if (!(env->hflags2 & HF2_MPX_PR_MASK)) { + memset(env->bnd_regs, 0, sizeof(env->bnd_regs)); + env->hflags &= ~HF_MPX_IU_MASK; + } +} diff --git a/qemu/target-i386/ops_sse.h b/qemu/target-i386/ops_sse.h index bee134bae..7a98f5386 100644 --- a/qemu/target-i386/ops_sse.h +++ b/qemu/target-i386/ops_sse.h @@ -26,15 +26,15 @@ #define B(n) MMX_B(n) #define W(n) MMX_W(n) #define L(n) MMX_L(n) -#define Q(n) q +#define Q(n) MMX_Q(n) #define SUFFIX _mmx #else -#define Reg XMMReg +#define Reg ZMMReg #define XMM_ONLY(...) __VA_ARGS__ -#define B(n) XMM_B(n) -#define W(n) XMM_W(n) -#define L(n) XMM_L(n) -#define Q(n) XMM_Q(n) +#define B(n) ZMM_B(n) +#define W(n) ZMM_W(n) +#define L(n) ZMM_L(n) +#define Q(n) ZMM_Q(n) #define SUFFIX _xmm #endif @@ -483,7 +483,7 @@ void glue(helper_maskmov, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, for (i = 0; i < (8 << SHIFT); i++) { if (s->B(i) & 0x80) { - cpu_stb_data(env, a0 + i, d->B(i)); + cpu_stb_data_ra(env, a0 + i, d->B(i), GETPC()); } } } @@ -582,26 +582,26 @@ void glue(helper_pshufhw, SUFFIX)(Reg *d, Reg *s, int order) #define SSE_HELPER_S(name, F) \ void helper_ ## name ## ps(CPUX86State *env, Reg *d, Reg *s) \ { \ - d->XMM_S(0) = F(32, d->XMM_S(0), s->XMM_S(0)); \ - d->XMM_S(1) = F(32, d->XMM_S(1), s->XMM_S(1)); \ - d->XMM_S(2) = F(32, d->XMM_S(2), s->XMM_S(2)); \ - d->XMM_S(3) = F(32, d->XMM_S(3), s->XMM_S(3)); \ + d->ZMM_S(0) = F(32, d->ZMM_S(0), s->ZMM_S(0)); \ + d->ZMM_S(1) = F(32, d->ZMM_S(1), s->ZMM_S(1)); \ + d->ZMM_S(2) = F(32, d->ZMM_S(2), s->ZMM_S(2)); \ + d->ZMM_S(3) = F(32, d->ZMM_S(3), s->ZMM_S(3)); \ } \ \ void helper_ ## name ## ss(CPUX86State *env, Reg *d, Reg *s) \ { \ - d->XMM_S(0) = F(32, d->XMM_S(0), s->XMM_S(0)); \ + d->ZMM_S(0) = F(32, d->ZMM_S(0), s->ZMM_S(0)); \ } \ \ void helper_ ## name ## pd(CPUX86State *env, Reg *d, Reg *s) \ { \ - d->XMM_D(0) = F(64, d->XMM_D(0), s->XMM_D(0)); \ - d->XMM_D(1) = F(64, d->XMM_D(1), s->XMM_D(1)); \ + d->ZMM_D(0) = F(64, d->ZMM_D(0), s->ZMM_D(0)); \ + d->ZMM_D(1) = F(64, d->ZMM_D(1), s->ZMM_D(1)); \ } \ \ void helper_ ## name ## sd(CPUX86State *env, Reg *d, Reg *s) \ { \ - d->XMM_D(0) = F(64, d->XMM_D(0), s->XMM_D(0)); \ + d->ZMM_D(0) = F(64, d->ZMM_D(0), s->ZMM_D(0)); \ } #define FPU_ADD(size, a, b) float ## size ## _add(a, b, &env->sse_status) @@ -633,216 +633,216 @@ void helper_cvtps2pd(CPUX86State *env, Reg *d, Reg *s) { float32 s0, s1; - s0 = s->XMM_S(0); - s1 = s->XMM_S(1); - d->XMM_D(0) = float32_to_float64(s0, &env->sse_status); - d->XMM_D(1) = float32_to_float64(s1, &env->sse_status); + s0 = s->ZMM_S(0); + s1 = s->ZMM_S(1); + d->ZMM_D(0) = float32_to_float64(s0, &env->sse_status); + d->ZMM_D(1) = float32_to_float64(s1, &env->sse_status); } void helper_cvtpd2ps(CPUX86State *env, Reg *d, Reg *s) { - d->XMM_S(0) = float64_to_float32(s->XMM_D(0), &env->sse_status); - d->XMM_S(1) = float64_to_float32(s->XMM_D(1), &env->sse_status); + d->ZMM_S(0) = float64_to_float32(s->ZMM_D(0), &env->sse_status); + d->ZMM_S(1) = float64_to_float32(s->ZMM_D(1), &env->sse_status); d->Q(1) = 0; } void helper_cvtss2sd(CPUX86State *env, Reg *d, Reg *s) { - d->XMM_D(0) = float32_to_float64(s->XMM_S(0), &env->sse_status); + d->ZMM_D(0) = float32_to_float64(s->ZMM_S(0), &env->sse_status); } void helper_cvtsd2ss(CPUX86State *env, Reg *d, Reg *s) { - d->XMM_S(0) = float64_to_float32(s->XMM_D(0), &env->sse_status); + d->ZMM_S(0) = float64_to_float32(s->ZMM_D(0), &env->sse_status); } /* integer to float */ void helper_cvtdq2ps(CPUX86State *env, Reg *d, Reg *s) { - d->XMM_S(0) = int32_to_float32(s->XMM_L(0), &env->sse_status); - d->XMM_S(1) = int32_to_float32(s->XMM_L(1), &env->sse_status); - d->XMM_S(2) = int32_to_float32(s->XMM_L(2), &env->sse_status); - d->XMM_S(3) = int32_to_float32(s->XMM_L(3), &env->sse_status); + d->ZMM_S(0) = int32_to_float32(s->ZMM_L(0), &env->sse_status); + d->ZMM_S(1) = int32_to_float32(s->ZMM_L(1), &env->sse_status); + d->ZMM_S(2) = int32_to_float32(s->ZMM_L(2), &env->sse_status); + d->ZMM_S(3) = int32_to_float32(s->ZMM_L(3), &env->sse_status); } void helper_cvtdq2pd(CPUX86State *env, Reg *d, Reg *s) { int32_t l0, l1; - l0 = (int32_t)s->XMM_L(0); - l1 = (int32_t)s->XMM_L(1); - d->XMM_D(0) = int32_to_float64(l0, &env->sse_status); - d->XMM_D(1) = int32_to_float64(l1, &env->sse_status); + l0 = (int32_t)s->ZMM_L(0); + l1 = (int32_t)s->ZMM_L(1); + d->ZMM_D(0) = int32_to_float64(l0, &env->sse_status); + d->ZMM_D(1) = int32_to_float64(l1, &env->sse_status); } -void helper_cvtpi2ps(CPUX86State *env, XMMReg *d, MMXReg *s) +void helper_cvtpi2ps(CPUX86State *env, ZMMReg *d, MMXReg *s) { - d->XMM_S(0) = int32_to_float32(s->MMX_L(0), &env->sse_status); - d->XMM_S(1) = int32_to_float32(s->MMX_L(1), &env->sse_status); + d->ZMM_S(0) = int32_to_float32(s->MMX_L(0), &env->sse_status); + d->ZMM_S(1) = int32_to_float32(s->MMX_L(1), &env->sse_status); } -void helper_cvtpi2pd(CPUX86State *env, XMMReg *d, MMXReg *s) +void helper_cvtpi2pd(CPUX86State *env, ZMMReg *d, MMXReg *s) { - d->XMM_D(0) = int32_to_float64(s->MMX_L(0), &env->sse_status); - d->XMM_D(1) = int32_to_float64(s->MMX_L(1), &env->sse_status); + d->ZMM_D(0) = int32_to_float64(s->MMX_L(0), &env->sse_status); + d->ZMM_D(1) = int32_to_float64(s->MMX_L(1), &env->sse_status); } -void helper_cvtsi2ss(CPUX86State *env, XMMReg *d, uint32_t val) +void helper_cvtsi2ss(CPUX86State *env, ZMMReg *d, uint32_t val) { - d->XMM_S(0) = int32_to_float32(val, &env->sse_status); + d->ZMM_S(0) = int32_to_float32(val, &env->sse_status); } -void helper_cvtsi2sd(CPUX86State *env, XMMReg *d, uint32_t val) +void helper_cvtsi2sd(CPUX86State *env, ZMMReg *d, uint32_t val) { - d->XMM_D(0) = int32_to_float64(val, &env->sse_status); + d->ZMM_D(0) = int32_to_float64(val, &env->sse_status); } #ifdef TARGET_X86_64 -void helper_cvtsq2ss(CPUX86State *env, XMMReg *d, uint64_t val) +void helper_cvtsq2ss(CPUX86State *env, ZMMReg *d, uint64_t val) { - d->XMM_S(0) = int64_to_float32(val, &env->sse_status); + d->ZMM_S(0) = int64_to_float32(val, &env->sse_status); } -void helper_cvtsq2sd(CPUX86State *env, XMMReg *d, uint64_t val) +void helper_cvtsq2sd(CPUX86State *env, ZMMReg *d, uint64_t val) { - d->XMM_D(0) = int64_to_float64(val, &env->sse_status); + d->ZMM_D(0) = int64_to_float64(val, &env->sse_status); } #endif /* float to integer */ -void helper_cvtps2dq(CPUX86State *env, XMMReg *d, XMMReg *s) +void helper_cvtps2dq(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - d->XMM_L(0) = float32_to_int32(s->XMM_S(0), &env->sse_status); - d->XMM_L(1) = float32_to_int32(s->XMM_S(1), &env->sse_status); - d->XMM_L(2) = float32_to_int32(s->XMM_S(2), &env->sse_status); - d->XMM_L(3) = float32_to_int32(s->XMM_S(3), &env->sse_status); + d->ZMM_L(0) = float32_to_int32(s->ZMM_S(0), &env->sse_status); + d->ZMM_L(1) = float32_to_int32(s->ZMM_S(1), &env->sse_status); + d->ZMM_L(2) = float32_to_int32(s->ZMM_S(2), &env->sse_status); + d->ZMM_L(3) = float32_to_int32(s->ZMM_S(3), &env->sse_status); } -void helper_cvtpd2dq(CPUX86State *env, XMMReg *d, XMMReg *s) +void helper_cvtpd2dq(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - d->XMM_L(0) = float64_to_int32(s->XMM_D(0), &env->sse_status); - d->XMM_L(1) = float64_to_int32(s->XMM_D(1), &env->sse_status); - d->XMM_Q(1) = 0; + d->ZMM_L(0) = float64_to_int32(s->ZMM_D(0), &env->sse_status); + d->ZMM_L(1) = float64_to_int32(s->ZMM_D(1), &env->sse_status); + d->ZMM_Q(1) = 0; } -void helper_cvtps2pi(CPUX86State *env, MMXReg *d, XMMReg *s) +void helper_cvtps2pi(CPUX86State *env, MMXReg *d, ZMMReg *s) { - d->MMX_L(0) = float32_to_int32(s->XMM_S(0), &env->sse_status); - d->MMX_L(1) = float32_to_int32(s->XMM_S(1), &env->sse_status); + d->MMX_L(0) = float32_to_int32(s->ZMM_S(0), &env->sse_status); + d->MMX_L(1) = float32_to_int32(s->ZMM_S(1), &env->sse_status); } -void helper_cvtpd2pi(CPUX86State *env, MMXReg *d, XMMReg *s) +void helper_cvtpd2pi(CPUX86State *env, MMXReg *d, ZMMReg *s) { - d->MMX_L(0) = float64_to_int32(s->XMM_D(0), &env->sse_status); - d->MMX_L(1) = float64_to_int32(s->XMM_D(1), &env->sse_status); + d->MMX_L(0) = float64_to_int32(s->ZMM_D(0), &env->sse_status); + d->MMX_L(1) = float64_to_int32(s->ZMM_D(1), &env->sse_status); } -int32_t helper_cvtss2si(CPUX86State *env, XMMReg *s) +int32_t helper_cvtss2si(CPUX86State *env, ZMMReg *s) { - return float32_to_int32(s->XMM_S(0), &env->sse_status); + return float32_to_int32(s->ZMM_S(0), &env->sse_status); } -int32_t helper_cvtsd2si(CPUX86State *env, XMMReg *s) +int32_t helper_cvtsd2si(CPUX86State *env, ZMMReg *s) { - return float64_to_int32(s->XMM_D(0), &env->sse_status); + return float64_to_int32(s->ZMM_D(0), &env->sse_status); } #ifdef TARGET_X86_64 -int64_t helper_cvtss2sq(CPUX86State *env, XMMReg *s) +int64_t helper_cvtss2sq(CPUX86State *env, ZMMReg *s) { - return float32_to_int64(s->XMM_S(0), &env->sse_status); + return float32_to_int64(s->ZMM_S(0), &env->sse_status); } -int64_t helper_cvtsd2sq(CPUX86State *env, XMMReg *s) +int64_t helper_cvtsd2sq(CPUX86State *env, ZMMReg *s) { - return float64_to_int64(s->XMM_D(0), &env->sse_status); + return float64_to_int64(s->ZMM_D(0), &env->sse_status); } #endif /* float to integer truncated */ -void helper_cvttps2dq(CPUX86State *env, XMMReg *d, XMMReg *s) +void helper_cvttps2dq(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - d->XMM_L(0) = float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status); - d->XMM_L(1) = float32_to_int32_round_to_zero(s->XMM_S(1), &env->sse_status); - d->XMM_L(2) = float32_to_int32_round_to_zero(s->XMM_S(2), &env->sse_status); - d->XMM_L(3) = float32_to_int32_round_to_zero(s->XMM_S(3), &env->sse_status); + d->ZMM_L(0) = float32_to_int32_round_to_zero(s->ZMM_S(0), &env->sse_status); + d->ZMM_L(1) = float32_to_int32_round_to_zero(s->ZMM_S(1), &env->sse_status); + d->ZMM_L(2) = float32_to_int32_round_to_zero(s->ZMM_S(2), &env->sse_status); + d->ZMM_L(3) = float32_to_int32_round_to_zero(s->ZMM_S(3), &env->sse_status); } -void helper_cvttpd2dq(CPUX86State *env, XMMReg *d, XMMReg *s) +void helper_cvttpd2dq(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - d->XMM_L(0) = float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status); - d->XMM_L(1) = float64_to_int32_round_to_zero(s->XMM_D(1), &env->sse_status); - d->XMM_Q(1) = 0; + d->ZMM_L(0) = float64_to_int32_round_to_zero(s->ZMM_D(0), &env->sse_status); + d->ZMM_L(1) = float64_to_int32_round_to_zero(s->ZMM_D(1), &env->sse_status); + d->ZMM_Q(1) = 0; } -void helper_cvttps2pi(CPUX86State *env, MMXReg *d, XMMReg *s) +void helper_cvttps2pi(CPUX86State *env, MMXReg *d, ZMMReg *s) { - d->MMX_L(0) = float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status); - d->MMX_L(1) = float32_to_int32_round_to_zero(s->XMM_S(1), &env->sse_status); + d->MMX_L(0) = float32_to_int32_round_to_zero(s->ZMM_S(0), &env->sse_status); + d->MMX_L(1) = float32_to_int32_round_to_zero(s->ZMM_S(1), &env->sse_status); } -void helper_cvttpd2pi(CPUX86State *env, MMXReg *d, XMMReg *s) +void helper_cvttpd2pi(CPUX86State *env, MMXReg *d, ZMMReg *s) { - d->MMX_L(0) = float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status); - d->MMX_L(1) = float64_to_int32_round_to_zero(s->XMM_D(1), &env->sse_status); + d->MMX_L(0) = float64_to_int32_round_to_zero(s->ZMM_D(0), &env->sse_status); + d->MMX_L(1) = float64_to_int32_round_to_zero(s->ZMM_D(1), &env->sse_status); } -int32_t helper_cvttss2si(CPUX86State *env, XMMReg *s) +int32_t helper_cvttss2si(CPUX86State *env, ZMMReg *s) { - return float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status); + return float32_to_int32_round_to_zero(s->ZMM_S(0), &env->sse_status); } -int32_t helper_cvttsd2si(CPUX86State *env, XMMReg *s) +int32_t helper_cvttsd2si(CPUX86State *env, ZMMReg *s) { - return float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status); + return float64_to_int32_round_to_zero(s->ZMM_D(0), &env->sse_status); } #ifdef TARGET_X86_64 -int64_t helper_cvttss2sq(CPUX86State *env, XMMReg *s) +int64_t helper_cvttss2sq(CPUX86State *env, ZMMReg *s) { - return float32_to_int64_round_to_zero(s->XMM_S(0), &env->sse_status); + return float32_to_int64_round_to_zero(s->ZMM_S(0), &env->sse_status); } -int64_t helper_cvttsd2sq(CPUX86State *env, XMMReg *s) +int64_t helper_cvttsd2sq(CPUX86State *env, ZMMReg *s) { - return float64_to_int64_round_to_zero(s->XMM_D(0), &env->sse_status); + return float64_to_int64_round_to_zero(s->ZMM_D(0), &env->sse_status); } #endif -void helper_rsqrtps(CPUX86State *env, XMMReg *d, XMMReg *s) +void helper_rsqrtps(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - d->XMM_S(0) = float32_div(float32_one, - float32_sqrt(s->XMM_S(0), &env->sse_status), + d->ZMM_S(0) = float32_div(float32_one, + float32_sqrt(s->ZMM_S(0), &env->sse_status), &env->sse_status); - d->XMM_S(1) = float32_div(float32_one, - float32_sqrt(s->XMM_S(1), &env->sse_status), + d->ZMM_S(1) = float32_div(float32_one, + float32_sqrt(s->ZMM_S(1), &env->sse_status), &env->sse_status); - d->XMM_S(2) = float32_div(float32_one, - float32_sqrt(s->XMM_S(2), &env->sse_status), + d->ZMM_S(2) = float32_div(float32_one, + float32_sqrt(s->ZMM_S(2), &env->sse_status), &env->sse_status); - d->XMM_S(3) = float32_div(float32_one, - float32_sqrt(s->XMM_S(3), &env->sse_status), + d->ZMM_S(3) = float32_div(float32_one, + float32_sqrt(s->ZMM_S(3), &env->sse_status), &env->sse_status); } -void helper_rsqrtss(CPUX86State *env, XMMReg *d, XMMReg *s) +void helper_rsqrtss(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - d->XMM_S(0) = float32_div(float32_one, - float32_sqrt(s->XMM_S(0), &env->sse_status), + d->ZMM_S(0) = float32_div(float32_one, + float32_sqrt(s->ZMM_S(0), &env->sse_status), &env->sse_status); } -void helper_rcpps(CPUX86State *env, XMMReg *d, XMMReg *s) +void helper_rcpps(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - d->XMM_S(0) = float32_div(float32_one, s->XMM_S(0), &env->sse_status); - d->XMM_S(1) = float32_div(float32_one, s->XMM_S(1), &env->sse_status); - d->XMM_S(2) = float32_div(float32_one, s->XMM_S(2), &env->sse_status); - d->XMM_S(3) = float32_div(float32_one, s->XMM_S(3), &env->sse_status); + d->ZMM_S(0) = float32_div(float32_one, s->ZMM_S(0), &env->sse_status); + d->ZMM_S(1) = float32_div(float32_one, s->ZMM_S(1), &env->sse_status); + d->ZMM_S(2) = float32_div(float32_one, s->ZMM_S(2), &env->sse_status); + d->ZMM_S(3) = float32_div(float32_one, s->ZMM_S(3), &env->sse_status); } -void helper_rcpss(CPUX86State *env, XMMReg *d, XMMReg *s) +void helper_rcpss(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - d->XMM_S(0) = float32_div(float32_one, s->XMM_S(0), &env->sse_status); + d->ZMM_S(0) = float32_div(float32_one, s->ZMM_S(0), &env->sse_status); } static inline uint64_t helper_extrq(uint64_t src, int shift, int len) @@ -857,14 +857,14 @@ static inline uint64_t helper_extrq(uint64_t src, int shift, int len) return (src >> shift) & mask; } -void helper_extrq_r(CPUX86State *env, XMMReg *d, XMMReg *s) +void helper_extrq_r(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - d->XMM_Q(0) = helper_extrq(d->XMM_Q(0), s->XMM_B(1), s->XMM_B(0)); + d->ZMM_Q(0) = helper_extrq(d->ZMM_Q(0), s->ZMM_B(1), s->ZMM_B(0)); } -void helper_extrq_i(CPUX86State *env, XMMReg *d, int index, int length) +void helper_extrq_i(CPUX86State *env, ZMMReg *d, int index, int length) { - d->XMM_Q(0) = helper_extrq(d->XMM_Q(0), index, length); + d->ZMM_Q(0) = helper_extrq(d->ZMM_Q(0), index, length); } static inline uint64_t helper_insertq(uint64_t src, int shift, int len) @@ -879,94 +879,94 @@ static inline uint64_t helper_insertq(uint64_t src, int shift, int len) return (src & ~(mask << shift)) | ((src & mask) << shift); } -void helper_insertq_r(CPUX86State *env, XMMReg *d, XMMReg *s) +void helper_insertq_r(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - d->XMM_Q(0) = helper_insertq(s->XMM_Q(0), s->XMM_B(9), s->XMM_B(8)); + d->ZMM_Q(0) = helper_insertq(s->ZMM_Q(0), s->ZMM_B(9), s->ZMM_B(8)); } -void helper_insertq_i(CPUX86State *env, XMMReg *d, int index, int length) +void helper_insertq_i(CPUX86State *env, ZMMReg *d, int index, int length) { - d->XMM_Q(0) = helper_insertq(d->XMM_Q(0), index, length); + d->ZMM_Q(0) = helper_insertq(d->ZMM_Q(0), index, length); } -void helper_haddps(CPUX86State *env, XMMReg *d, XMMReg *s) +void helper_haddps(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - XMMReg r; + ZMMReg r; - r.XMM_S(0) = float32_add(d->XMM_S(0), d->XMM_S(1), &env->sse_status); - r.XMM_S(1) = float32_add(d->XMM_S(2), d->XMM_S(3), &env->sse_status); - r.XMM_S(2) = float32_add(s->XMM_S(0), s->XMM_S(1), &env->sse_status); - r.XMM_S(3) = float32_add(s->XMM_S(2), s->XMM_S(3), &env->sse_status); + r.ZMM_S(0) = float32_add(d->ZMM_S(0), d->ZMM_S(1), &env->sse_status); + r.ZMM_S(1) = float32_add(d->ZMM_S(2), d->ZMM_S(3), &env->sse_status); + r.ZMM_S(2) = float32_add(s->ZMM_S(0), s->ZMM_S(1), &env->sse_status); + r.ZMM_S(3) = float32_add(s->ZMM_S(2), s->ZMM_S(3), &env->sse_status); *d = r; } -void helper_haddpd(CPUX86State *env, XMMReg *d, XMMReg *s) +void helper_haddpd(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - XMMReg r; + ZMMReg r; - r.XMM_D(0) = float64_add(d->XMM_D(0), d->XMM_D(1), &env->sse_status); - r.XMM_D(1) = float64_add(s->XMM_D(0), s->XMM_D(1), &env->sse_status); + r.ZMM_D(0) = float64_add(d->ZMM_D(0), d->ZMM_D(1), &env->sse_status); + r.ZMM_D(1) = float64_add(s->ZMM_D(0), s->ZMM_D(1), &env->sse_status); *d = r; } -void helper_hsubps(CPUX86State *env, XMMReg *d, XMMReg *s) +void helper_hsubps(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - XMMReg r; + ZMMReg r; - r.XMM_S(0) = float32_sub(d->XMM_S(0), d->XMM_S(1), &env->sse_status); - r.XMM_S(1) = float32_sub(d->XMM_S(2), d->XMM_S(3), &env->sse_status); - r.XMM_S(2) = float32_sub(s->XMM_S(0), s->XMM_S(1), &env->sse_status); - r.XMM_S(3) = float32_sub(s->XMM_S(2), s->XMM_S(3), &env->sse_status); + r.ZMM_S(0) = float32_sub(d->ZMM_S(0), d->ZMM_S(1), &env->sse_status); + r.ZMM_S(1) = float32_sub(d->ZMM_S(2), d->ZMM_S(3), &env->sse_status); + r.ZMM_S(2) = float32_sub(s->ZMM_S(0), s->ZMM_S(1), &env->sse_status); + r.ZMM_S(3) = float32_sub(s->ZMM_S(2), s->ZMM_S(3), &env->sse_status); *d = r; } -void helper_hsubpd(CPUX86State *env, XMMReg *d, XMMReg *s) +void helper_hsubpd(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - XMMReg r; + ZMMReg r; - r.XMM_D(0) = float64_sub(d->XMM_D(0), d->XMM_D(1), &env->sse_status); - r.XMM_D(1) = float64_sub(s->XMM_D(0), s->XMM_D(1), &env->sse_status); + r.ZMM_D(0) = float64_sub(d->ZMM_D(0), d->ZMM_D(1), &env->sse_status); + r.ZMM_D(1) = float64_sub(s->ZMM_D(0), s->ZMM_D(1), &env->sse_status); *d = r; } -void helper_addsubps(CPUX86State *env, XMMReg *d, XMMReg *s) +void helper_addsubps(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - d->XMM_S(0) = float32_sub(d->XMM_S(0), s->XMM_S(0), &env->sse_status); - d->XMM_S(1) = float32_add(d->XMM_S(1), s->XMM_S(1), &env->sse_status); - d->XMM_S(2) = float32_sub(d->XMM_S(2), s->XMM_S(2), &env->sse_status); - d->XMM_S(3) = float32_add(d->XMM_S(3), s->XMM_S(3), &env->sse_status); + d->ZMM_S(0) = float32_sub(d->ZMM_S(0), s->ZMM_S(0), &env->sse_status); + d->ZMM_S(1) = float32_add(d->ZMM_S(1), s->ZMM_S(1), &env->sse_status); + d->ZMM_S(2) = float32_sub(d->ZMM_S(2), s->ZMM_S(2), &env->sse_status); + d->ZMM_S(3) = float32_add(d->ZMM_S(3), s->ZMM_S(3), &env->sse_status); } -void helper_addsubpd(CPUX86State *env, XMMReg *d, XMMReg *s) +void helper_addsubpd(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - d->XMM_D(0) = float64_sub(d->XMM_D(0), s->XMM_D(0), &env->sse_status); - d->XMM_D(1) = float64_add(d->XMM_D(1), s->XMM_D(1), &env->sse_status); + d->ZMM_D(0) = float64_sub(d->ZMM_D(0), s->ZMM_D(0), &env->sse_status); + d->ZMM_D(1) = float64_add(d->ZMM_D(1), s->ZMM_D(1), &env->sse_status); } /* XXX: unordered */ #define SSE_HELPER_CMP(name, F) \ void helper_ ## name ## ps(CPUX86State *env, Reg *d, Reg *s) \ { \ - d->XMM_L(0) = F(32, d->XMM_S(0), s->XMM_S(0)); \ - d->XMM_L(1) = F(32, d->XMM_S(1), s->XMM_S(1)); \ - d->XMM_L(2) = F(32, d->XMM_S(2), s->XMM_S(2)); \ - d->XMM_L(3) = F(32, d->XMM_S(3), s->XMM_S(3)); \ + d->ZMM_L(0) = F(32, d->ZMM_S(0), s->ZMM_S(0)); \ + d->ZMM_L(1) = F(32, d->ZMM_S(1), s->ZMM_S(1)); \ + d->ZMM_L(2) = F(32, d->ZMM_S(2), s->ZMM_S(2)); \ + d->ZMM_L(3) = F(32, d->ZMM_S(3), s->ZMM_S(3)); \ } \ \ void helper_ ## name ## ss(CPUX86State *env, Reg *d, Reg *s) \ { \ - d->XMM_L(0) = F(32, d->XMM_S(0), s->XMM_S(0)); \ + d->ZMM_L(0) = F(32, d->ZMM_S(0), s->ZMM_S(0)); \ } \ \ void helper_ ## name ## pd(CPUX86State *env, Reg *d, Reg *s) \ { \ - d->XMM_Q(0) = F(64, d->XMM_D(0), s->XMM_D(0)); \ - d->XMM_Q(1) = F(64, d->XMM_D(1), s->XMM_D(1)); \ + d->ZMM_Q(0) = F(64, d->ZMM_D(0), s->ZMM_D(0)); \ + d->ZMM_Q(1) = F(64, d->ZMM_D(1), s->ZMM_D(1)); \ } \ \ void helper_ ## name ## sd(CPUX86State *env, Reg *d, Reg *s) \ { \ - d->XMM_Q(0) = F(64, d->XMM_D(0), s->XMM_D(0)); \ + d->ZMM_Q(0) = F(64, d->ZMM_D(0), s->ZMM_D(0)); \ } #define FPU_CMPEQ(size, a, b) \ @@ -1002,8 +1002,8 @@ void helper_ucomiss(CPUX86State *env, Reg *d, Reg *s) int ret; float32 s0, s1; - s0 = d->XMM_S(0); - s1 = s->XMM_S(0); + s0 = d->ZMM_S(0); + s1 = s->ZMM_S(0); ret = float32_compare_quiet(s0, s1, &env->sse_status); CC_SRC = comis_eflags[ret + 1]; } @@ -1013,8 +1013,8 @@ void helper_comiss(CPUX86State *env, Reg *d, Reg *s) int ret; float32 s0, s1; - s0 = d->XMM_S(0); - s1 = s->XMM_S(0); + s0 = d->ZMM_S(0); + s1 = s->ZMM_S(0); ret = float32_compare(s0, s1, &env->sse_status); CC_SRC = comis_eflags[ret + 1]; } @@ -1024,8 +1024,8 @@ void helper_ucomisd(CPUX86State *env, Reg *d, Reg *s) int ret; float64 d0, d1; - d0 = d->XMM_D(0); - d1 = s->XMM_D(0); + d0 = d->ZMM_D(0); + d1 = s->ZMM_D(0); ret = float64_compare_quiet(d0, d1, &env->sse_status); CC_SRC = comis_eflags[ret + 1]; } @@ -1035,8 +1035,8 @@ void helper_comisd(CPUX86State *env, Reg *d, Reg *s) int ret; float64 d0, d1; - d0 = d->XMM_D(0); - d1 = s->XMM_D(0); + d0 = d->ZMM_D(0); + d1 = s->ZMM_D(0); ret = float64_compare(d0, d1, &env->sse_status); CC_SRC = comis_eflags[ret + 1]; } @@ -1045,10 +1045,10 @@ uint32_t helper_movmskps(CPUX86State *env, Reg *s) { int b0, b1, b2, b3; - b0 = s->XMM_L(0) >> 31; - b1 = s->XMM_L(1) >> 31; - b2 = s->XMM_L(2) >> 31; - b3 = s->XMM_L(3) >> 31; + b0 = s->ZMM_L(0) >> 31; + b1 = s->ZMM_L(1) >> 31; + b2 = s->ZMM_L(2) >> 31; + b3 = s->ZMM_L(3) >> 31; return b0 | (b1 << 1) | (b2 << 2) | (b3 << 3); } @@ -1056,8 +1056,8 @@ uint32_t helper_movmskpd(CPUX86State *env, Reg *s) { int b0, b1; - b0 = s->XMM_L(1) >> 31; - b1 = s->XMM_L(3) >> 31; + b0 = s->ZMM_L(1) >> 31; + b1 = s->ZMM_L(3) >> 31; return b0 | (b1 << 1); } @@ -1736,10 +1736,10 @@ void glue(helper_roundps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, } } - d->XMM_S(0) = float32_round_to_int(s->XMM_S(0), &env->sse_status); - d->XMM_S(1) = float32_round_to_int(s->XMM_S(1), &env->sse_status); - d->XMM_S(2) = float32_round_to_int(s->XMM_S(2), &env->sse_status); - d->XMM_S(3) = float32_round_to_int(s->XMM_S(3), &env->sse_status); + d->ZMM_S(0) = float32_round_to_int(s->ZMM_S(0), &env->sse_status); + d->ZMM_S(1) = float32_round_to_int(s->ZMM_S(1), &env->sse_status); + d->ZMM_S(2) = float32_round_to_int(s->ZMM_S(2), &env->sse_status); + d->ZMM_S(3) = float32_round_to_int(s->ZMM_S(3), &env->sse_status); #if 0 /* TODO */ if (mode & (1 << 3)) { @@ -1774,8 +1774,8 @@ void glue(helper_roundpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, } } - d->XMM_D(0) = float64_round_to_int(s->XMM_D(0), &env->sse_status); - d->XMM_D(1) = float64_round_to_int(s->XMM_D(1), &env->sse_status); + d->ZMM_D(0) = float64_round_to_int(s->ZMM_D(0), &env->sse_status); + d->ZMM_D(1) = float64_round_to_int(s->ZMM_D(1), &env->sse_status); #if 0 /* TODO */ if (mode & (1 << 3)) { @@ -1810,7 +1810,7 @@ void glue(helper_roundss, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, } } - d->XMM_S(0) = float32_round_to_int(s->XMM_S(0), &env->sse_status); + d->ZMM_S(0) = float32_round_to_int(s->ZMM_S(0), &env->sse_status); #if 0 /* TODO */ if (mode & (1 << 3)) { @@ -1845,7 +1845,7 @@ void glue(helper_roundsd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, } } - d->XMM_D(0) = float64_round_to_int(s->XMM_D(0), &env->sse_status); + d->ZMM_D(0) = float64_round_to_int(s->ZMM_D(0), &env->sse_status); #if 0 /* TODO */ if (mode & (1 << 3)) { @@ -1868,32 +1868,32 @@ void glue(helper_dpps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, uint32_t mask) if (mask & (1 << 4)) { iresult = float32_add(iresult, - float32_mul(d->XMM_S(0), s->XMM_S(0), + float32_mul(d->ZMM_S(0), s->ZMM_S(0), &env->sse_status), &env->sse_status); } if (mask & (1 << 5)) { iresult = float32_add(iresult, - float32_mul(d->XMM_S(1), s->XMM_S(1), + float32_mul(d->ZMM_S(1), s->ZMM_S(1), &env->sse_status), &env->sse_status); } if (mask & (1 << 6)) { iresult = float32_add(iresult, - float32_mul(d->XMM_S(2), s->XMM_S(2), + float32_mul(d->ZMM_S(2), s->ZMM_S(2), &env->sse_status), &env->sse_status); } if (mask & (1 << 7)) { iresult = float32_add(iresult, - float32_mul(d->XMM_S(3), s->XMM_S(3), + float32_mul(d->ZMM_S(3), s->ZMM_S(3), &env->sse_status), &env->sse_status); } - d->XMM_S(0) = (mask & (1 << 0)) ? iresult : float32_zero; - d->XMM_S(1) = (mask & (1 << 1)) ? iresult : float32_zero; - d->XMM_S(2) = (mask & (1 << 2)) ? iresult : float32_zero; - d->XMM_S(3) = (mask & (1 << 3)) ? iresult : float32_zero; + d->ZMM_S(0) = (mask & (1 << 0)) ? iresult : float32_zero; + d->ZMM_S(1) = (mask & (1 << 1)) ? iresult : float32_zero; + d->ZMM_S(2) = (mask & (1 << 2)) ? iresult : float32_zero; + d->ZMM_S(3) = (mask & (1 << 3)) ? iresult : float32_zero; } void glue(helper_dppd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, uint32_t mask) @@ -1902,18 +1902,18 @@ void glue(helper_dppd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, uint32_t mask) if (mask & (1 << 4)) { iresult = float64_add(iresult, - float64_mul(d->XMM_D(0), s->XMM_D(0), + float64_mul(d->ZMM_D(0), s->ZMM_D(0), &env->sse_status), &env->sse_status); } if (mask & (1 << 5)) { iresult = float64_add(iresult, - float64_mul(d->XMM_D(1), s->XMM_D(1), + float64_mul(d->ZMM_D(1), s->ZMM_D(1), &env->sse_status), &env->sse_status); } - d->XMM_D(0) = (mask & (1 << 0)) ? iresult : float64_zero; - d->XMM_D(1) = (mask & (1 << 1)) ? iresult : float64_zero; + d->ZMM_D(0) = (mask & (1 << 0)) ? iresult : float64_zero; + d->ZMM_D(1) = (mask & (1 << 1)) ? iresult : float64_zero; } void glue(helper_mpsadbw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, @@ -2037,10 +2037,10 @@ static inline unsigned pcmpxstrx(CPUX86State *env, Reg *d, Reg *s, } break; case 3: - for (j = valids - validd; j >= 0; j--) { + for (j = valids; j >= 0; j--) { res <<= 1; v = 1; - for (i = MIN(upper - j, validd); i >= 0; i--) { + for (i = MIN(valids - j, validd); i >= 0; i--) { v &= (pcmp_val(s, ctrl, i + j) == pcmp_val(d, ctrl, i)); } res |= v; diff --git a/qemu/target-i386/ops_sse_header.h b/qemu/target-i386/ops_sse_header.h index a68c7cc0c..64c5857cf 100644 --- a/qemu/target-i386/ops_sse_header.h +++ b/qemu/target-i386/ops_sse_header.h @@ -20,18 +20,18 @@ #define Reg MMXReg #define SUFFIX _mmx #else -#define Reg XMMReg +#define Reg ZMMReg #define SUFFIX _xmm #endif #define dh_alias_Reg ptr -#define dh_alias_XMMReg ptr +#define dh_alias_ZMMReg ptr #define dh_alias_MMXReg ptr #define dh_ctype_Reg Reg * -#define dh_ctype_XMMReg XMMReg * +#define dh_ctype_ZMMReg ZMMReg * #define dh_ctype_MMXReg MMXReg * #define dh_is_signed_Reg dh_is_signed_ptr -#define dh_is_signed_XMMReg dh_is_signed_ptr +#define dh_is_signed_ZMMReg dh_is_signed_ptr #define dh_is_signed_MMXReg dh_is_signed_ptr DEF_HELPER_3(glue(psrlw, SUFFIX), void, env, Reg, Reg) @@ -154,52 +154,52 @@ DEF_HELPER_3(cvtss2sd, void, env, Reg, Reg) DEF_HELPER_3(cvtsd2ss, void, env, Reg, Reg) DEF_HELPER_3(cvtdq2ps, void, env, Reg, Reg) DEF_HELPER_3(cvtdq2pd, void, env, Reg, Reg) -DEF_HELPER_3(cvtpi2ps, void, env, XMMReg, MMXReg) -DEF_HELPER_3(cvtpi2pd, void, env, XMMReg, MMXReg) -DEF_HELPER_3(cvtsi2ss, void, env, XMMReg, i32) -DEF_HELPER_3(cvtsi2sd, void, env, XMMReg, i32) +DEF_HELPER_3(cvtpi2ps, void, env, ZMMReg, MMXReg) +DEF_HELPER_3(cvtpi2pd, void, env, ZMMReg, MMXReg) +DEF_HELPER_3(cvtsi2ss, void, env, ZMMReg, i32) +DEF_HELPER_3(cvtsi2sd, void, env, ZMMReg, i32) #ifdef TARGET_X86_64 -DEF_HELPER_3(cvtsq2ss, void, env, XMMReg, i64) -DEF_HELPER_3(cvtsq2sd, void, env, XMMReg, i64) +DEF_HELPER_3(cvtsq2ss, void, env, ZMMReg, i64) +DEF_HELPER_3(cvtsq2sd, void, env, ZMMReg, i64) #endif -DEF_HELPER_3(cvtps2dq, void, env, XMMReg, XMMReg) -DEF_HELPER_3(cvtpd2dq, void, env, XMMReg, XMMReg) -DEF_HELPER_3(cvtps2pi, void, env, MMXReg, XMMReg) -DEF_HELPER_3(cvtpd2pi, void, env, MMXReg, XMMReg) -DEF_HELPER_2(cvtss2si, s32, env, XMMReg) -DEF_HELPER_2(cvtsd2si, s32, env, XMMReg) +DEF_HELPER_3(cvtps2dq, void, env, ZMMReg, ZMMReg) +DEF_HELPER_3(cvtpd2dq, void, env, ZMMReg, ZMMReg) +DEF_HELPER_3(cvtps2pi, void, env, MMXReg, ZMMReg) +DEF_HELPER_3(cvtpd2pi, void, env, MMXReg, ZMMReg) +DEF_HELPER_2(cvtss2si, s32, env, ZMMReg) +DEF_HELPER_2(cvtsd2si, s32, env, ZMMReg) #ifdef TARGET_X86_64 -DEF_HELPER_2(cvtss2sq, s64, env, XMMReg) -DEF_HELPER_2(cvtsd2sq, s64, env, XMMReg) +DEF_HELPER_2(cvtss2sq, s64, env, ZMMReg) +DEF_HELPER_2(cvtsd2sq, s64, env, ZMMReg) #endif -DEF_HELPER_3(cvttps2dq, void, env, XMMReg, XMMReg) -DEF_HELPER_3(cvttpd2dq, void, env, XMMReg, XMMReg) -DEF_HELPER_3(cvttps2pi, void, env, MMXReg, XMMReg) -DEF_HELPER_3(cvttpd2pi, void, env, MMXReg, XMMReg) -DEF_HELPER_2(cvttss2si, s32, env, XMMReg) -DEF_HELPER_2(cvttsd2si, s32, env, XMMReg) +DEF_HELPER_3(cvttps2dq, void, env, ZMMReg, ZMMReg) +DEF_HELPER_3(cvttpd2dq, void, env, ZMMReg, ZMMReg) +DEF_HELPER_3(cvttps2pi, void, env, MMXReg, ZMMReg) +DEF_HELPER_3(cvttpd2pi, void, env, MMXReg, ZMMReg) +DEF_HELPER_2(cvttss2si, s32, env, ZMMReg) +DEF_HELPER_2(cvttsd2si, s32, env, ZMMReg) #ifdef TARGET_X86_64 -DEF_HELPER_2(cvttss2sq, s64, env, XMMReg) -DEF_HELPER_2(cvttsd2sq, s64, env, XMMReg) +DEF_HELPER_2(cvttss2sq, s64, env, ZMMReg) +DEF_HELPER_2(cvttsd2sq, s64, env, ZMMReg) #endif -DEF_HELPER_3(rsqrtps, void, env, XMMReg, XMMReg) -DEF_HELPER_3(rsqrtss, void, env, XMMReg, XMMReg) -DEF_HELPER_3(rcpps, void, env, XMMReg, XMMReg) -DEF_HELPER_3(rcpss, void, env, XMMReg, XMMReg) -DEF_HELPER_3(extrq_r, void, env, XMMReg, XMMReg) -DEF_HELPER_4(extrq_i, void, env, XMMReg, int, int) -DEF_HELPER_3(insertq_r, void, env, XMMReg, XMMReg) -DEF_HELPER_4(insertq_i, void, env, XMMReg, int, int) -DEF_HELPER_3(haddps, void, env, XMMReg, XMMReg) -DEF_HELPER_3(haddpd, void, env, XMMReg, XMMReg) -DEF_HELPER_3(hsubps, void, env, XMMReg, XMMReg) -DEF_HELPER_3(hsubpd, void, env, XMMReg, XMMReg) -DEF_HELPER_3(addsubps, void, env, XMMReg, XMMReg) -DEF_HELPER_3(addsubpd, void, env, XMMReg, XMMReg) +DEF_HELPER_3(rsqrtps, void, env, ZMMReg, ZMMReg) +DEF_HELPER_3(rsqrtss, void, env, ZMMReg, ZMMReg) +DEF_HELPER_3(rcpps, void, env, ZMMReg, ZMMReg) +DEF_HELPER_3(rcpss, void, env, ZMMReg, ZMMReg) +DEF_HELPER_3(extrq_r, void, env, ZMMReg, ZMMReg) +DEF_HELPER_4(extrq_i, void, env, ZMMReg, int, int) +DEF_HELPER_3(insertq_r, void, env, ZMMReg, ZMMReg) +DEF_HELPER_4(insertq_i, void, env, ZMMReg, int, int) +DEF_HELPER_3(haddps, void, env, ZMMReg, ZMMReg) +DEF_HELPER_3(haddpd, void, env, ZMMReg, ZMMReg) +DEF_HELPER_3(hsubps, void, env, ZMMReg, ZMMReg) +DEF_HELPER_3(hsubpd, void, env, ZMMReg, ZMMReg) +DEF_HELPER_3(addsubps, void, env, ZMMReg, ZMMReg) +DEF_HELPER_3(addsubpd, void, env, ZMMReg, ZMMReg) #define SSE_HELPER_CMP(name, F) \ DEF_HELPER_3(name ## ps, void, env, Reg, Reg) \ diff --git a/qemu/target-i386/seg_helper.c b/qemu/target-i386/seg_helper.c index 8a4271ebe..b5f3d72fe 100644 --- a/qemu/target-i386/seg_helper.c +++ b/qemu/target-i386/seg_helper.c @@ -18,10 +18,12 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "qemu/log.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" +#include "exec/log.h" //#define DEBUG_PCALL @@ -67,8 +69,9 @@ #endif /* return non zero if error */ -static inline int load_segment(CPUX86State *env, uint32_t *e1_ptr, - uint32_t *e2_ptr, int selector) +static inline int load_segment_ra(CPUX86State *env, uint32_t *e1_ptr, + uint32_t *e2_ptr, int selector, + uintptr_t retaddr) { SegmentCache *dt; int index; @@ -84,11 +87,17 @@ static inline int load_segment(CPUX86State *env, uint32_t *e1_ptr, return -1; } ptr = dt->base + index; - *e1_ptr = cpu_ldl_kernel(env, ptr); - *e2_ptr = cpu_ldl_kernel(env, ptr + 4); + *e1_ptr = cpu_ldl_kernel_ra(env, ptr, retaddr); + *e2_ptr = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); return 0; } +static inline int load_segment(CPUX86State *env, uint32_t *e1_ptr, + uint32_t *e2_ptr, int selector) +{ + return load_segment_ra(env, e1_ptr, e2_ptr, selector, 0); +} + static inline unsigned int get_seg_limit(uint32_t e1, uint32_t e2) { unsigned int limit; @@ -124,7 +133,8 @@ static inline void load_seg_vm(CPUX86State *env, int seg, int selector) } static inline void get_ss_esp_from_tss(CPUX86State *env, uint32_t *ss_ptr, - uint32_t *esp_ptr, int dpl) + uint32_t *esp_ptr, int dpl, + uintptr_t retaddr) { X86CPU *cpu = x86_env_get_cpu(env); int type, index, shift; @@ -153,60 +163,61 @@ static inline void get_ss_esp_from_tss(CPUX86State *env, uint32_t *ss_ptr, shift = type >> 3; index = (dpl * 4 + 2) << shift; if (index + (4 << shift) - 1 > env->tr.limit) { - raise_exception_err(env, EXCP0A_TSS, env->tr.selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, env->tr.selector & 0xfffc, retaddr); } if (shift == 0) { - *esp_ptr = cpu_lduw_kernel(env, env->tr.base + index); - *ss_ptr = cpu_lduw_kernel(env, env->tr.base + index + 2); + *esp_ptr = cpu_lduw_kernel_ra(env, env->tr.base + index, retaddr); + *ss_ptr = cpu_lduw_kernel_ra(env, env->tr.base + index + 2, retaddr); } else { - *esp_ptr = cpu_ldl_kernel(env, env->tr.base + index); - *ss_ptr = cpu_lduw_kernel(env, env->tr.base + index + 4); + *esp_ptr = cpu_ldl_kernel_ra(env, env->tr.base + index, retaddr); + *ss_ptr = cpu_lduw_kernel_ra(env, env->tr.base + index + 4, retaddr); } } -static void tss_load_seg(CPUX86State *env, int seg_reg, int selector, int cpl) +static void tss_load_seg(CPUX86State *env, int seg_reg, int selector, int cpl, + uintptr_t retaddr) { uint32_t e1, e2; int rpl, dpl; if ((selector & 0xfffc) != 0) { - if (load_segment(env, &e1, &e2, selector) != 0) { - raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); + if (load_segment_ra(env, &e1, &e2, selector, retaddr) != 0) { + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); } if (!(e2 & DESC_S_MASK)) { - raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); } rpl = selector & 3; dpl = (e2 >> DESC_DPL_SHIFT) & 3; if (seg_reg == R_CS) { if (!(e2 & DESC_CS_MASK)) { - raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); } if (dpl != rpl) { - raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); } } else if (seg_reg == R_SS) { /* SS must be writable data */ if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) { - raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); } if (dpl != cpl || dpl != rpl) { - raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); } } else { /* not readable code */ if ((e2 & DESC_CS_MASK) && !(e2 & DESC_R_MASK)) { - raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); } /* if data or non conforming code, checks the rights */ if (((e2 >> DESC_TYPE_SHIFT) & 0xf) < 12) { if (dpl < cpl || dpl < rpl) { - raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); } } } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, retaddr); } cpu_x86_load_seg_cache(env, seg_reg, selector, get_seg_base(e1, e2), @@ -214,7 +225,7 @@ static void tss_load_seg(CPUX86State *env, int seg_reg, int selector, int cpl) e2); } else { if (seg_reg == R_SS || seg_reg == R_CS) { - raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); } } } @@ -224,9 +235,9 @@ static void tss_load_seg(CPUX86State *env, int seg_reg, int selector, int cpl) #define SWITCH_TSS_CALL 2 /* XXX: restore CPU state in registers (PowerPC case) */ -static void switch_tss(CPUX86State *env, int tss_selector, - uint32_t e1, uint32_t e2, int source, - uint32_t next_eip) +static void switch_tss_ra(CPUX86State *env, int tss_selector, + uint32_t e1, uint32_t e2, int source, + uint32_t next_eip, uintptr_t retaddr) { int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i; target_ulong tss_base; @@ -244,26 +255,26 @@ static void switch_tss(CPUX86State *env, int tss_selector, /* if task gate, we read the TSS segment and we load it */ if (type == 5) { if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, tss_selector & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, tss_selector & 0xfffc, retaddr); } tss_selector = e1 >> 16; if (tss_selector & 4) { - raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, retaddr); } - if (load_segment(env, &e1, &e2, tss_selector) != 0) { - raise_exception_err(env, EXCP0D_GPF, tss_selector & 0xfffc); + if (load_segment_ra(env, &e1, &e2, tss_selector, retaddr) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, tss_selector & 0xfffc, retaddr); } if (e2 & DESC_S_MASK) { - raise_exception_err(env, EXCP0D_GPF, tss_selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, tss_selector & 0xfffc, retaddr); } type = (e2 >> DESC_TYPE_SHIFT) & 0xf; if ((type & 7) != 1) { - raise_exception_err(env, EXCP0D_GPF, tss_selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, tss_selector & 0xfffc, retaddr); } } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, tss_selector & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, tss_selector & 0xfffc, retaddr); } if (type & 8) { @@ -275,7 +286,7 @@ static void switch_tss(CPUX86State *env, int tss_selector, tss_base = get_seg_base(e1, e2); if ((tss_selector & 4) != 0 || tss_limit < tss_limit_max) { - raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, retaddr); } old_type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; if (old_type & 8) { @@ -287,30 +298,33 @@ static void switch_tss(CPUX86State *env, int tss_selector, /* read all the registers from the new TSS */ if (type & 8) { /* 32 bit */ - new_cr3 = cpu_ldl_kernel(env, tss_base + 0x1c); - new_eip = cpu_ldl_kernel(env, tss_base + 0x20); - new_eflags = cpu_ldl_kernel(env, tss_base + 0x24); + new_cr3 = cpu_ldl_kernel_ra(env, tss_base + 0x1c, retaddr); + new_eip = cpu_ldl_kernel_ra(env, tss_base + 0x20, retaddr); + new_eflags = cpu_ldl_kernel_ra(env, tss_base + 0x24, retaddr); for (i = 0; i < 8; i++) { - new_regs[i] = cpu_ldl_kernel(env, tss_base + (0x28 + i * 4)); + new_regs[i] = cpu_ldl_kernel_ra(env, tss_base + (0x28 + i * 4), + retaddr); } for (i = 0; i < 6; i++) { - new_segs[i] = cpu_lduw_kernel(env, tss_base + (0x48 + i * 4)); + new_segs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x48 + i * 4), + retaddr); } - new_ldt = cpu_lduw_kernel(env, tss_base + 0x60); - new_trap = cpu_ldl_kernel(env, tss_base + 0x64); + new_ldt = cpu_lduw_kernel_ra(env, tss_base + 0x60, retaddr); + new_trap = cpu_ldl_kernel_ra(env, tss_base + 0x64, retaddr); } else { /* 16 bit */ new_cr3 = 0; - new_eip = cpu_lduw_kernel(env, tss_base + 0x0e); - new_eflags = cpu_lduw_kernel(env, tss_base + 0x10); + new_eip = cpu_lduw_kernel_ra(env, tss_base + 0x0e, retaddr); + new_eflags = cpu_lduw_kernel_ra(env, tss_base + 0x10, retaddr); for (i = 0; i < 8; i++) { - new_regs[i] = cpu_lduw_kernel(env, tss_base + (0x12 + i * 2)) | - 0xffff0000; + new_regs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x12 + i * 2), + retaddr) | 0xffff0000; } for (i = 0; i < 4; i++) { - new_segs[i] = cpu_lduw_kernel(env, tss_base + (0x22 + i * 4)); + new_segs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x22 + i * 4), + retaddr); } - new_ldt = cpu_lduw_kernel(env, tss_base + 0x2a); + new_ldt = cpu_lduw_kernel_ra(env, tss_base + 0x2a, retaddr); new_segs[R_FS] = 0; new_segs[R_GS] = 0; new_trap = 0; @@ -325,10 +339,10 @@ static void switch_tss(CPUX86State *env, int tss_selector, /* XXX: it can still fail in some cases, so a bigger hack is necessary to valid the TLB after having done the accesses */ - v1 = cpu_ldub_kernel(env, env->tr.base); - v2 = cpu_ldub_kernel(env, env->tr.base + old_tss_limit_max); - cpu_stb_kernel(env, env->tr.base, v1); - cpu_stb_kernel(env, env->tr.base + old_tss_limit_max, v2); + v1 = cpu_ldub_kernel_ra(env, env->tr.base, retaddr); + v2 = cpu_ldub_kernel_ra(env, env->tr.base + old_tss_limit_max, retaddr); + cpu_stb_kernel_ra(env, env->tr.base, v1, retaddr); + cpu_stb_kernel_ra(env, env->tr.base + old_tss_limit_max, v2, retaddr); /* clear busy bit (it is restartable) */ if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) { @@ -336,9 +350,9 @@ static void switch_tss(CPUX86State *env, int tss_selector, uint32_t e2; ptr = env->gdt.base + (env->tr.selector & ~7); - e2 = cpu_ldl_kernel(env, ptr + 4); + e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); e2 &= ~DESC_TSS_BUSY_MASK; - cpu_stl_kernel(env, ptr + 4, e2); + cpu_stl_kernel_ra(env, ptr + 4, e2, retaddr); } old_eflags = cpu_compute_eflags(env); if (source == SWITCH_TSS_IRET) { @@ -348,35 +362,35 @@ static void switch_tss(CPUX86State *env, int tss_selector, /* save the current state in the old TSS */ if (type & 8) { /* 32 bit */ - cpu_stl_kernel(env, env->tr.base + 0x20, next_eip); - cpu_stl_kernel(env, env->tr.base + 0x24, old_eflags); - cpu_stl_kernel(env, env->tr.base + (0x28 + 0 * 4), env->regs[R_EAX]); - cpu_stl_kernel(env, env->tr.base + (0x28 + 1 * 4), env->regs[R_ECX]); - cpu_stl_kernel(env, env->tr.base + (0x28 + 2 * 4), env->regs[R_EDX]); - cpu_stl_kernel(env, env->tr.base + (0x28 + 3 * 4), env->regs[R_EBX]); - cpu_stl_kernel(env, env->tr.base + (0x28 + 4 * 4), env->regs[R_ESP]); - cpu_stl_kernel(env, env->tr.base + (0x28 + 5 * 4), env->regs[R_EBP]); - cpu_stl_kernel(env, env->tr.base + (0x28 + 6 * 4), env->regs[R_ESI]); - cpu_stl_kernel(env, env->tr.base + (0x28 + 7 * 4), env->regs[R_EDI]); + cpu_stl_kernel_ra(env, env->tr.base + 0x20, next_eip, retaddr); + cpu_stl_kernel_ra(env, env->tr.base + 0x24, old_eflags, retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 0 * 4), env->regs[R_EAX], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 1 * 4), env->regs[R_ECX], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 2 * 4), env->regs[R_EDX], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 3 * 4), env->regs[R_EBX], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 4 * 4), env->regs[R_ESP], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 5 * 4), env->regs[R_EBP], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 6 * 4), env->regs[R_ESI], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 7 * 4), env->regs[R_EDI], retaddr); for (i = 0; i < 6; i++) { - cpu_stw_kernel(env, env->tr.base + (0x48 + i * 4), - env->segs[i].selector); + cpu_stw_kernel_ra(env, env->tr.base + (0x48 + i * 4), + env->segs[i].selector, retaddr); } } else { /* 16 bit */ - cpu_stw_kernel(env, env->tr.base + 0x0e, next_eip); - cpu_stw_kernel(env, env->tr.base + 0x10, old_eflags); - cpu_stw_kernel(env, env->tr.base + (0x12 + 0 * 2), env->regs[R_EAX]); - cpu_stw_kernel(env, env->tr.base + (0x12 + 1 * 2), env->regs[R_ECX]); - cpu_stw_kernel(env, env->tr.base + (0x12 + 2 * 2), env->regs[R_EDX]); - cpu_stw_kernel(env, env->tr.base + (0x12 + 3 * 2), env->regs[R_EBX]); - cpu_stw_kernel(env, env->tr.base + (0x12 + 4 * 2), env->regs[R_ESP]); - cpu_stw_kernel(env, env->tr.base + (0x12 + 5 * 2), env->regs[R_EBP]); - cpu_stw_kernel(env, env->tr.base + (0x12 + 6 * 2), env->regs[R_ESI]); - cpu_stw_kernel(env, env->tr.base + (0x12 + 7 * 2), env->regs[R_EDI]); + cpu_stw_kernel_ra(env, env->tr.base + 0x0e, next_eip, retaddr); + cpu_stw_kernel_ra(env, env->tr.base + 0x10, old_eflags, retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 0 * 2), env->regs[R_EAX], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 1 * 2), env->regs[R_ECX], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 2 * 2), env->regs[R_EDX], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 3 * 2), env->regs[R_EBX], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 4 * 2), env->regs[R_ESP], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 5 * 2), env->regs[R_EBP], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 6 * 2), env->regs[R_ESI], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 7 * 2), env->regs[R_EDI], retaddr); for (i = 0; i < 4; i++) { - cpu_stw_kernel(env, env->tr.base + (0x22 + i * 4), - env->segs[i].selector); + cpu_stw_kernel_ra(env, env->tr.base + (0x22 + i * 4), + env->segs[i].selector, retaddr); } } @@ -384,7 +398,7 @@ static void switch_tss(CPUX86State *env, int tss_selector, context */ if (source == SWITCH_TSS_CALL) { - cpu_stw_kernel(env, tss_base, env->tr.selector); + cpu_stw_kernel_ra(env, tss_base, env->tr.selector, retaddr); new_eflags |= NT_MASK; } @@ -394,9 +408,9 @@ static void switch_tss(CPUX86State *env, int tss_selector, uint32_t e2; ptr = env->gdt.base + (tss_selector & ~7); - e2 = cpu_ldl_kernel(env, ptr + 4); + e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); e2 |= DESC_TSS_BUSY_MASK; - cpu_stl_kernel(env, ptr + 4, e2); + cpu_stl_kernel_ra(env, ptr + 4, e2, retaddr); } /* set the new CPU state */ @@ -448,23 +462,23 @@ static void switch_tss(CPUX86State *env, int tss_selector, /* load the LDT */ if (new_ldt & 4) { - raise_exception_err(env, EXCP0A_TSS, new_ldt & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr); } if ((new_ldt & 0xfffc) != 0) { dt = &env->gdt; index = new_ldt & ~7; if ((index + 7) > dt->limit) { - raise_exception_err(env, EXCP0A_TSS, new_ldt & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr); } ptr = dt->base + index; - e1 = cpu_ldl_kernel(env, ptr); - e2 = cpu_ldl_kernel(env, ptr + 4); + e1 = cpu_ldl_kernel_ra(env, ptr, retaddr); + e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2) { - raise_exception_err(env, EXCP0A_TSS, new_ldt & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr); } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0A_TSS, new_ldt & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr); } load_seg_cache_raw_dt(&env->ldt, e1, e2); } @@ -472,34 +486,35 @@ static void switch_tss(CPUX86State *env, int tss_selector, /* load the segments */ if (!(new_eflags & VM_MASK)) { int cpl = new_segs[R_CS] & 3; - tss_load_seg(env, R_CS, new_segs[R_CS], cpl); - tss_load_seg(env, R_SS, new_segs[R_SS], cpl); - tss_load_seg(env, R_ES, new_segs[R_ES], cpl); - tss_load_seg(env, R_DS, new_segs[R_DS], cpl); - tss_load_seg(env, R_FS, new_segs[R_FS], cpl); - tss_load_seg(env, R_GS, new_segs[R_GS], cpl); + tss_load_seg(env, R_CS, new_segs[R_CS], cpl, retaddr); + tss_load_seg(env, R_SS, new_segs[R_SS], cpl, retaddr); + tss_load_seg(env, R_ES, new_segs[R_ES], cpl, retaddr); + tss_load_seg(env, R_DS, new_segs[R_DS], cpl, retaddr); + tss_load_seg(env, R_FS, new_segs[R_FS], cpl, retaddr); + tss_load_seg(env, R_GS, new_segs[R_GS], cpl, retaddr); } /* check that env->eip is in the CS segment limits */ if (new_eip > env->segs[R_CS].limit) { /* XXX: different exception if CALL? */ - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, retaddr); } #ifndef CONFIG_USER_ONLY /* reset local breakpoints */ if (env->dr[7] & DR7_LOCAL_BP_MASK) { - for (i = 0; i < DR7_MAX_BP; i++) { - if (hw_local_breakpoint_enabled(env->dr[7], i) && - !hw_global_breakpoint_enabled(env->dr[7], i)) { - hw_breakpoint_remove(env, i); - } - } - env->dr[7] &= ~DR7_LOCAL_BP_MASK; + cpu_x86_update_dr7(env, env->dr[7] & ~DR7_LOCAL_BP_MASK); } #endif } +static void switch_tss(CPUX86State *env, int tss_selector, + uint32_t e1, uint32_t e2, int source, + uint32_t next_eip) +{ + switch_tss_ra(env, tss_selector, e1, e2, source, next_eip, 0); +} + static inline unsigned int get_sp_mask(unsigned int e2) { if (e2 & DESC_B_MASK) { @@ -549,30 +564,35 @@ static int exception_has_error_code(int intno) #define SEG_ADDL(ssp, sp, sp_mask) ((uint32_t)((ssp) + (sp & (sp_mask)))) /* XXX: add a is_user flag to have proper security support */ -#define PUSHW(ssp, sp, sp_mask, val) \ +#define PUSHW_RA(ssp, sp, sp_mask, val, ra) \ { \ sp -= 2; \ - cpu_stw_kernel(env, (ssp) + (sp & (sp_mask)), (val)); \ + cpu_stw_kernel_ra(env, (ssp) + (sp & (sp_mask)), (val), ra); \ } -#define PUSHL(ssp, sp, sp_mask, val) \ +#define PUSHL_RA(ssp, sp, sp_mask, val, ra) \ { \ sp -= 4; \ - cpu_stl_kernel(env, SEG_ADDL(ssp, sp, sp_mask), (uint32_t)(val)); \ + cpu_stl_kernel_ra(env, SEG_ADDL(ssp, sp, sp_mask), (uint32_t)(val), ra); \ } -#define POPW(ssp, sp, sp_mask, val) \ +#define POPW_RA(ssp, sp, sp_mask, val, ra) \ { \ - val = cpu_lduw_kernel(env, (ssp) + (sp & (sp_mask))); \ + val = cpu_lduw_kernel_ra(env, (ssp) + (sp & (sp_mask)), ra); \ sp += 2; \ } -#define POPL(ssp, sp, sp_mask, val) \ +#define POPL_RA(ssp, sp, sp_mask, val, ra) \ { \ - val = (uint32_t)cpu_ldl_kernel(env, SEG_ADDL(ssp, sp, sp_mask)); \ + val = (uint32_t)cpu_ldl_kernel_ra(env, SEG_ADDL(ssp, sp, sp_mask), ra); \ sp += 4; \ } +#define PUSHW(ssp, sp, sp_mask, val) PUSHW_RA(ssp, sp, sp_mask, val, 0) +#define PUSHL(ssp, sp, sp_mask, val) PUSHL_RA(ssp, sp, sp_mask, val, 0) +#define POPW(ssp, sp, sp_mask, val) POPW_RA(ssp, sp, sp_mask, val, 0) +#define POPL(ssp, sp, sp_mask, val) POPL_RA(ssp, sp, sp_mask, val, 0) + /* protected mode interrupt */ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, int error_code, unsigned int next_eip, @@ -673,7 +693,7 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, } if (!(e2 & DESC_C_MASK) && dpl < cpl) { /* to inner privilege */ - get_ss_esp_from_tss(env, &ss, &esp, dpl); + get_ss_esp_from_tss(env, &ss, &esp, dpl, 0); if ((ss & 0xfffc) == 0) { raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); } @@ -791,18 +811,21 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, #ifdef TARGET_X86_64 -#define PUSHQ(sp, val) \ +#define PUSHQ_RA(sp, val, ra) \ { \ sp -= 8; \ - cpu_stq_kernel(env, sp, (val)); \ + cpu_stq_kernel_ra(env, sp, (val), ra); \ } -#define POPQ(sp, val) \ +#define POPQ_RA(sp, val, ra) \ { \ - val = cpu_ldq_kernel(env, sp); \ + val = cpu_ldq_kernel_ra(env, sp, ra); \ sp += 8; \ } +#define PUSHQ(sp, val) PUSHQ_RA(sp, val, 0) +#define POPQ(sp, val) POPQ_RA(sp, val, 0) + static inline target_ulong get_rsp_from_tss(CPUX86State *env, int level) { X86CPU *cpu = x86_env_get_cpu(env); @@ -961,7 +984,7 @@ void helper_syscall(CPUX86State *env, int next_eip_addend) int selector; if (!(env->efer & MSR_EFER_SCE)) { - raise_exception_err(env, EXCP06_ILLOP, 0); + raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); } selector = (env->star >> 32) & 0xffff; if (env->hflags & HF_LMA_MASK) { @@ -1016,11 +1039,11 @@ void helper_sysret(CPUX86State *env, int dflag) int cpl, selector; if (!(env->efer & MSR_EFER_SCE)) { - raise_exception_err(env, EXCP06_ILLOP, 0); + raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); } cpl = env->hflags & HF_CPL_MASK; if (!(env->cr[0] & CR0_PE_MASK) || cpl != 0) { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } selector = (env->star >> 48) & 0xffff; if (env->hflags & HF_LMA_MASK) { @@ -1294,6 +1317,9 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) if (interrupt_request & CPU_INTERRUPT_POLL) { cs->interrupt_request &= ~CPU_INTERRUPT_POLL; apic_poll_irq(cpu->apic_state); + /* Don't process multiple interrupt requests in a single call. + This is required to make icount-driven execution deterministic. */ + return true; } #endif if (interrupt_request & CPU_INTERRUPT_SIPI) { @@ -1353,74 +1379,6 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) return ret; } -void helper_enter_level(CPUX86State *env, int level, int data32, - target_ulong t1) -{ - target_ulong ssp; - uint32_t esp_mask, esp, ebp; - - esp_mask = get_sp_mask(env->segs[R_SS].flags); - ssp = env->segs[R_SS].base; - ebp = env->regs[R_EBP]; - esp = env->regs[R_ESP]; - if (data32) { - /* 32 bit */ - esp -= 4; - while (--level) { - esp -= 4; - ebp -= 4; - cpu_stl_data(env, ssp + (esp & esp_mask), - cpu_ldl_data(env, ssp + (ebp & esp_mask))); - } - esp -= 4; - cpu_stl_data(env, ssp + (esp & esp_mask), t1); - } else { - /* 16 bit */ - esp -= 2; - while (--level) { - esp -= 2; - ebp -= 2; - cpu_stw_data(env, ssp + (esp & esp_mask), - cpu_lduw_data(env, ssp + (ebp & esp_mask))); - } - esp -= 2; - cpu_stw_data(env, ssp + (esp & esp_mask), t1); - } -} - -#ifdef TARGET_X86_64 -void helper_enter64_level(CPUX86State *env, int level, int data64, - target_ulong t1) -{ - target_ulong esp, ebp; - - ebp = env->regs[R_EBP]; - esp = env->regs[R_ESP]; - - if (data64) { - /* 64 bit */ - esp -= 8; - while (--level) { - esp -= 8; - ebp -= 8; - cpu_stq_data(env, esp, cpu_ldq_data(env, ebp)); - } - esp -= 8; - cpu_stq_data(env, esp, t1); - } else { - /* 16 bit */ - esp -= 2; - while (--level) { - esp -= 2; - ebp -= 2; - cpu_stw_data(env, esp, cpu_lduw_data(env, ebp)); - } - esp -= 2; - cpu_stw_data(env, esp, t1); - } -} -#endif - void helper_lldt(CPUX86State *env, int selector) { SegmentCache *dt; @@ -1435,7 +1393,7 @@ void helper_lldt(CPUX86State *env, int selector) env->ldt.limit = 0; } else { if (selector & 0x4) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } dt = &env->gdt; index = selector & ~7; @@ -1448,22 +1406,22 @@ void helper_lldt(CPUX86State *env, int selector) entry_limit = 7; } if ((index + entry_limit) > dt->limit) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } ptr = dt->base + index; - e1 = cpu_ldl_kernel(env, ptr); - e2 = cpu_ldl_kernel(env, ptr + 4); + e1 = cpu_ldl_kernel_ra(env, ptr, GETPC()); + e2 = cpu_ldl_kernel_ra(env, ptr + 4, GETPC()); if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC()); } #ifdef TARGET_X86_64 if (env->hflags & HF_LMA_MASK) { uint32_t e3; - e3 = cpu_ldl_kernel(env, ptr + 8); + e3 = cpu_ldl_kernel_ra(env, ptr + 8, GETPC()); load_seg_cache_raw_dt(&env->ldt, e1, e2); env->ldt.base |= (target_ulong)e3 << 32; } else @@ -1490,7 +1448,7 @@ void helper_ltr(CPUX86State *env, int selector) env->tr.flags = 0; } else { if (selector & 0x4) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } dt = &env->gdt; index = selector & ~7; @@ -1503,27 +1461,27 @@ void helper_ltr(CPUX86State *env, int selector) entry_limit = 7; } if ((index + entry_limit) > dt->limit) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } ptr = dt->base + index; - e1 = cpu_ldl_kernel(env, ptr); - e2 = cpu_ldl_kernel(env, ptr + 4); + e1 = cpu_ldl_kernel_ra(env, ptr, GETPC()); + e2 = cpu_ldl_kernel_ra(env, ptr + 4, GETPC()); type = (e2 >> DESC_TYPE_SHIFT) & 0xf; if ((e2 & DESC_S_MASK) || (type != 1 && type != 9)) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC()); } #ifdef TARGET_X86_64 if (env->hflags & HF_LMA_MASK) { uint32_t e3, e4; - e3 = cpu_ldl_kernel(env, ptr + 8); - e4 = cpu_ldl_kernel(env, ptr + 12); + e3 = cpu_ldl_kernel_ra(env, ptr + 8, GETPC()); + e4 = cpu_ldl_kernel_ra(env, ptr + 12, GETPC()); if ((e4 >> DESC_TYPE_SHIFT) & 0xf) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } load_seg_cache_raw_dt(&env->tr, e1, e2); env->tr.base |= (target_ulong)e3 << 32; @@ -1533,7 +1491,7 @@ void helper_ltr(CPUX86State *env, int selector) load_seg_cache_raw_dt(&env->tr, e1, e2); } e2 |= DESC_TSS_BUSY_MASK; - cpu_stl_kernel(env, ptr + 4, e2); + cpu_stl_kernel_ra(env, ptr + 4, e2, GETPC()); } env->tr.selector = selector; } @@ -1556,7 +1514,7 @@ void helper_load_seg(CPUX86State *env, int seg_reg, int selector) && (!(env->hflags & HF_CS64_MASK) || cpl == 3) #endif ) { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } cpu_x86_load_seg_cache(env, seg_reg, selector, 0, 0, 0); } else { @@ -1568,51 +1526,51 @@ void helper_load_seg(CPUX86State *env, int seg_reg, int selector) } index = selector & ~7; if ((index + 7) > dt->limit) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } ptr = dt->base + index; - e1 = cpu_ldl_kernel(env, ptr); - e2 = cpu_ldl_kernel(env, ptr + 4); + e1 = cpu_ldl_kernel_ra(env, ptr, GETPC()); + e2 = cpu_ldl_kernel_ra(env, ptr + 4, GETPC()); if (!(e2 & DESC_S_MASK)) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } rpl = selector & 3; dpl = (e2 >> DESC_DPL_SHIFT) & 3; if (seg_reg == R_SS) { /* must be writable segment */ if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } if (rpl != cpl || dpl != cpl) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } } else { /* must be readable segment */ if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) { /* if not conforming code, test rights */ if (dpl < cpl || dpl < rpl) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } } } if (!(e2 & DESC_P_MASK)) { if (seg_reg == R_SS) { - raise_exception_err(env, EXCP0C_STACK, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0C_STACK, selector & 0xfffc, GETPC()); } else { - raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC()); } } /* set the access bit if not already set */ if (!(e2 & DESC_A_MASK)) { e2 |= DESC_A_MASK; - cpu_stl_kernel(env, ptr + 4, e2); + cpu_stl_kernel_ra(env, ptr + 4, e2, GETPC()); } cpu_x86_load_seg_cache(env, seg_reg, selector, @@ -1628,46 +1586,45 @@ void helper_load_seg(CPUX86State *env, int seg_reg, int selector) /* protected mode jump */ void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip, - int next_eip_addend) + target_ulong next_eip) { int gate_cs, type; uint32_t e1, e2, cpl, dpl, rpl, limit; - target_ulong next_eip; if ((new_cs & 0xfffc) == 0) { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } - if (load_segment(env, &e1, &e2, new_cs) != 0) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + if (load_segment_ra(env, &e1, &e2, new_cs, GETPC()) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } cpl = env->hflags & HF_CPL_MASK; if (e2 & DESC_S_MASK) { if (!(e2 & DESC_CS_MASK)) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } dpl = (e2 >> DESC_DPL_SHIFT) & 3; if (e2 & DESC_C_MASK) { /* conforming code segment */ if (dpl > cpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } } else { /* non conforming code segment */ rpl = new_cs & 3; if (rpl > cpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } if (dpl != cpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC()); } limit = get_seg_limit(e1, e2); if (new_eip > limit && !(env->hflags & HF_LMA_MASK) && !(e2 & DESC_L_MASK)) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, get_seg_base(e1, e2), limit, e2); @@ -1683,50 +1640,49 @@ void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip, case 9: /* 386 TSS */ case 5: /* task gate */ if (dpl < cpl || dpl < rpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } - next_eip = env->eip + next_eip_addend; - switch_tss(env, new_cs, e1, e2, SWITCH_TSS_JMP, next_eip); + switch_tss_ra(env, new_cs, e1, e2, SWITCH_TSS_JMP, next_eip, GETPC()); break; case 4: /* 286 call gate */ case 12: /* 386 call gate */ if ((dpl < cpl) || (dpl < rpl)) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC()); } gate_cs = e1 >> 16; new_eip = (e1 & 0xffff); if (type == 12) { new_eip |= (e2 & 0xffff0000); } - if (load_segment(env, &e1, &e2, gate_cs) != 0) { - raise_exception_err(env, EXCP0D_GPF, gate_cs & 0xfffc); + if (load_segment_ra(env, &e1, &e2, gate_cs, GETPC()) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); } dpl = (e2 >> DESC_DPL_SHIFT) & 3; /* must be code segment */ if (((e2 & (DESC_S_MASK | DESC_CS_MASK)) != (DESC_S_MASK | DESC_CS_MASK))) { - raise_exception_err(env, EXCP0D_GPF, gate_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); } if (((e2 & DESC_C_MASK) && (dpl > cpl)) || (!(e2 & DESC_C_MASK) && (dpl != cpl))) { - raise_exception_err(env, EXCP0D_GPF, gate_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0D_GPF, gate_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); } limit = get_seg_limit(e1, e2); if (new_eip > limit) { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } cpu_x86_load_seg_cache(env, R_CS, (gate_cs & 0xfffc) | cpl, get_seg_base(e1, e2), limit, e2); env->eip = new_eip; break; default: - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); break; } } @@ -1745,11 +1701,11 @@ void helper_lcall_real(CPUX86State *env, int new_cs, target_ulong new_eip1, esp_mask = get_sp_mask(env->segs[R_SS].flags); ssp = env->segs[R_SS].base; if (shift) { - PUSHL(ssp, esp, esp_mask, env->segs[R_CS].selector); - PUSHL(ssp, esp, esp_mask, next_eip); + PUSHL_RA(ssp, esp, esp_mask, env->segs[R_CS].selector, GETPC()); + PUSHL_RA(ssp, esp, esp_mask, next_eip, GETPC()); } else { - PUSHW(ssp, esp, esp_mask, env->segs[R_CS].selector); - PUSHW(ssp, esp, esp_mask, next_eip); + PUSHW_RA(ssp, esp, esp_mask, env->segs[R_CS].selector, GETPC()); + PUSHW_RA(ssp, esp, esp_mask, next_eip, GETPC()); } SET_ESP(esp, esp_mask); @@ -1760,47 +1716,46 @@ void helper_lcall_real(CPUX86State *env, int new_cs, target_ulong new_eip1, /* protected mode call */ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, - int shift, int next_eip_addend) + int shift, target_ulong next_eip) { int new_stack, i; uint32_t e1, e2, cpl, dpl, rpl, selector, offset, param_count; uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, sp, type, ss_dpl, sp_mask; uint32_t val, limit, old_sp_mask; - target_ulong ssp, old_ssp, next_eip; + target_ulong ssp, old_ssp; - next_eip = env->eip + next_eip_addend; LOG_PCALL("lcall %04x:%08x s=%d\n", new_cs, (uint32_t)new_eip, shift); LOG_PCALL_STATE(CPU(x86_env_get_cpu(env))); if ((new_cs & 0xfffc) == 0) { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } - if (load_segment(env, &e1, &e2, new_cs) != 0) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + if (load_segment_ra(env, &e1, &e2, new_cs, GETPC()) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } cpl = env->hflags & HF_CPL_MASK; LOG_PCALL("desc=%08x:%08x\n", e1, e2); if (e2 & DESC_S_MASK) { if (!(e2 & DESC_CS_MASK)) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } dpl = (e2 >> DESC_DPL_SHIFT) & 3; if (e2 & DESC_C_MASK) { /* conforming code segment */ if (dpl > cpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } } else { /* non conforming code segment */ rpl = new_cs & 3; if (rpl > cpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } if (dpl != cpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC()); } #ifdef TARGET_X86_64 @@ -1810,8 +1765,8 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, /* 64 bit case */ rsp = env->regs[R_ESP]; - PUSHQ(rsp, env->segs[R_CS].selector); - PUSHQ(rsp, next_eip); + PUSHQ_RA(rsp, env->segs[R_CS].selector, GETPC()); + PUSHQ_RA(rsp, next_eip, GETPC()); /* from this point, not restartable */ env->regs[R_ESP] = rsp; cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, @@ -1825,16 +1780,16 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, sp_mask = get_sp_mask(env->segs[R_SS].flags); ssp = env->segs[R_SS].base; if (shift) { - PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector); - PUSHL(ssp, sp, sp_mask, next_eip); + PUSHL_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); + PUSHL_RA(ssp, sp, sp_mask, next_eip, GETPC()); } else { - PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector); - PUSHW(ssp, sp, sp_mask, next_eip); + PUSHW_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); + PUSHW_RA(ssp, sp, sp_mask, next_eip, GETPC()); } limit = get_seg_limit(e1, e2); if (new_eip > limit) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } /* from this point, not restartable */ SET_ESP(sp, sp_mask); @@ -1852,73 +1807,73 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, case 9: /* available 386 TSS */ case 5: /* task gate */ if (dpl < cpl || dpl < rpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } - switch_tss(env, new_cs, e1, e2, SWITCH_TSS_CALL, next_eip); + switch_tss_ra(env, new_cs, e1, e2, SWITCH_TSS_CALL, next_eip, GETPC()); return; case 4: /* 286 call gate */ case 12: /* 386 call gate */ break; default: - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); break; } shift = type >> 3; if (dpl < cpl || dpl < rpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } /* check valid bit */ if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC()); } selector = e1 >> 16; offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff); param_count = e2 & 0x1f; if ((selector & 0xfffc) == 0) { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } - if (load_segment(env, &e1, &e2, selector) != 0) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK))) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } dpl = (e2 >> DESC_DPL_SHIFT) & 3; if (dpl > cpl) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC()); } if (!(e2 & DESC_C_MASK) && dpl < cpl) { /* to inner privilege */ - get_ss_esp_from_tss(env, &ss, &sp, dpl); + get_ss_esp_from_tss(env, &ss, &sp, dpl, GETPC()); LOG_PCALL("new ss:esp=%04x:%08x param_count=%d env->regs[R_ESP]=" TARGET_FMT_lx "\n", ss, sp, param_count, env->regs[R_ESP]); if ((ss & 0xfffc) == 0) { - raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); } if ((ss & 3) != dpl) { - raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); } - if (load_segment(env, &ss_e1, &ss_e2, ss) != 0) { - raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); + if (load_segment_ra(env, &ss_e1, &ss_e2, ss, GETPC()) != 0) { + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); } ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3; if (ss_dpl != dpl) { - raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); } if (!(ss_e2 & DESC_S_MASK) || (ss_e2 & DESC_CS_MASK) || !(ss_e2 & DESC_W_MASK)) { - raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); } if (!(ss_e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); } /* push_size = ((param_count * 2) + 8) << shift; */ @@ -1929,22 +1884,22 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, sp_mask = get_sp_mask(ss_e2); ssp = get_seg_base(ss_e1, ss_e2); if (shift) { - PUSHL(ssp, sp, sp_mask, env->segs[R_SS].selector); - PUSHL(ssp, sp, sp_mask, env->regs[R_ESP]); + PUSHL_RA(ssp, sp, sp_mask, env->segs[R_SS].selector, GETPC()); + PUSHL_RA(ssp, sp, sp_mask, env->regs[R_ESP], GETPC()); for (i = param_count - 1; i >= 0; i--) { - val = cpu_ldl_kernel(env, old_ssp + - ((env->regs[R_ESP] + i * 4) & - old_sp_mask)); - PUSHL(ssp, sp, sp_mask, val); + val = cpu_ldl_kernel_ra(env, old_ssp + + ((env->regs[R_ESP] + i * 4) & + old_sp_mask), GETPC()); + PUSHL_RA(ssp, sp, sp_mask, val, GETPC()); } } else { - PUSHW(ssp, sp, sp_mask, env->segs[R_SS].selector); - PUSHW(ssp, sp, sp_mask, env->regs[R_ESP]); + PUSHW_RA(ssp, sp, sp_mask, env->segs[R_SS].selector, GETPC()); + PUSHW_RA(ssp, sp, sp_mask, env->regs[R_ESP], GETPC()); for (i = param_count - 1; i >= 0; i--) { - val = cpu_lduw_kernel(env, old_ssp + - ((env->regs[R_ESP] + i * 2) & - old_sp_mask)); - PUSHW(ssp, sp, sp_mask, val); + val = cpu_lduw_kernel_ra(env, old_ssp + + ((env->regs[R_ESP] + i * 2) & + old_sp_mask), GETPC()); + PUSHW_RA(ssp, sp, sp_mask, val, GETPC()); } } new_stack = 1; @@ -1958,11 +1913,11 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, } if (shift) { - PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector); - PUSHL(ssp, sp, sp_mask, next_eip); + PUSHL_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); + PUSHL_RA(ssp, sp, sp_mask, next_eip, GETPC()); } else { - PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector); - PUSHW(ssp, sp, sp_mask, next_eip); + PUSHW_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); + PUSHW_RA(ssp, sp, sp_mask, next_eip, GETPC()); } /* from this point, not restartable */ @@ -1997,15 +1952,15 @@ void helper_iret_real(CPUX86State *env, int shift) ssp = env->segs[R_SS].base; if (shift == 1) { /* 32 bits */ - POPL(ssp, sp, sp_mask, new_eip); - POPL(ssp, sp, sp_mask, new_cs); + POPL_RA(ssp, sp, sp_mask, new_eip, GETPC()); + POPL_RA(ssp, sp, sp_mask, new_cs, GETPC()); new_cs &= 0xffff; - POPL(ssp, sp, sp_mask, new_eflags); + POPL_RA(ssp, sp, sp_mask, new_eflags, GETPC()); } else { /* 16 bits */ - POPW(ssp, sp, sp_mask, new_eip); - POPW(ssp, sp, sp_mask, new_cs); - POPW(ssp, sp, sp_mask, new_eflags); + POPW_RA(ssp, sp, sp_mask, new_eip, GETPC()); + POPW_RA(ssp, sp, sp_mask, new_cs, GETPC()); + POPW_RA(ssp, sp, sp_mask, new_eflags, GETPC()); } env->regs[R_ESP] = (env->regs[R_ESP] & ~sp_mask) | (sp & sp_mask); env->segs[R_CS].selector = new_cs; @@ -2050,7 +2005,8 @@ static inline void validate_seg(CPUX86State *env, int seg_reg, int cpl) /* protected mode iret */ static inline void helper_ret_protected(CPUX86State *env, int shift, - int is_iret, int addend) + int is_iret, int addend, + uintptr_t retaddr) { uint32_t new_cs, new_eflags, new_ss; uint32_t new_es, new_ds, new_fs, new_gs; @@ -2071,32 +2027,32 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, new_eflags = 0; /* avoid warning */ #ifdef TARGET_X86_64 if (shift == 2) { - POPQ(sp, new_eip); - POPQ(sp, new_cs); + POPQ_RA(sp, new_eip, retaddr); + POPQ_RA(sp, new_cs, retaddr); new_cs &= 0xffff; if (is_iret) { - POPQ(sp, new_eflags); + POPQ_RA(sp, new_eflags, retaddr); } } else #endif { if (shift == 1) { /* 32 bits */ - POPL(ssp, sp, sp_mask, new_eip); - POPL(ssp, sp, sp_mask, new_cs); + POPL_RA(ssp, sp, sp_mask, new_eip, retaddr); + POPL_RA(ssp, sp, sp_mask, new_cs, retaddr); new_cs &= 0xffff; if (is_iret) { - POPL(ssp, sp, sp_mask, new_eflags); + POPL_RA(ssp, sp, sp_mask, new_eflags, retaddr); if (new_eflags & VM_MASK) { goto return_to_vm86; } } } else { /* 16 bits */ - POPW(ssp, sp, sp_mask, new_eip); - POPW(ssp, sp, sp_mask, new_cs); + POPW_RA(ssp, sp, sp_mask, new_eip, retaddr); + POPW_RA(ssp, sp, sp_mask, new_cs, retaddr); if (is_iret) { - POPW(ssp, sp, sp_mask, new_eflags); + POPW_RA(ssp, sp, sp_mask, new_eflags, retaddr); } } } @@ -2104,32 +2060,32 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, new_cs, new_eip, shift, addend); LOG_PCALL_STATE(CPU(x86_env_get_cpu(env))); if ((new_cs & 0xfffc) == 0) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); } - if (load_segment(env, &e1, &e2, new_cs) != 0) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + if (load_segment_ra(env, &e1, &e2, new_cs, retaddr) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); } if (!(e2 & DESC_S_MASK) || !(e2 & DESC_CS_MASK)) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); } cpl = env->hflags & HF_CPL_MASK; rpl = new_cs & 3; if (rpl < cpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); } dpl = (e2 >> DESC_DPL_SHIFT) & 3; if (e2 & DESC_C_MASK) { if (dpl > rpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); } } else { if (dpl != rpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); } } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, retaddr); } sp += addend; @@ -2144,21 +2100,21 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, /* return to different privilege level */ #ifdef TARGET_X86_64 if (shift == 2) { - POPQ(sp, new_esp); - POPQ(sp, new_ss); + POPQ_RA(sp, new_esp, retaddr); + POPQ_RA(sp, new_ss, retaddr); new_ss &= 0xffff; } else #endif { if (shift == 1) { /* 32 bits */ - POPL(ssp, sp, sp_mask, new_esp); - POPL(ssp, sp, sp_mask, new_ss); + POPL_RA(ssp, sp, sp_mask, new_esp, retaddr); + POPL_RA(ssp, sp, sp_mask, new_ss, retaddr); new_ss &= 0xffff; } else { /* 16 bits */ - POPW(ssp, sp, sp_mask, new_esp); - POPW(ssp, sp, sp_mask, new_ss); + POPW_RA(ssp, sp, sp_mask, new_esp, retaddr); + POPW_RA(ssp, sp, sp_mask, new_ss, retaddr); } } LOG_PCALL("new ss:esp=%04x:" TARGET_FMT_lx "\n", @@ -2177,26 +2133,26 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, } else #endif { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, retaddr); } } else { if ((new_ss & 3) != rpl) { - raise_exception_err(env, EXCP0D_GPF, new_ss & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr); } - if (load_segment(env, &ss_e1, &ss_e2, new_ss) != 0) { - raise_exception_err(env, EXCP0D_GPF, new_ss & 0xfffc); + if (load_segment_ra(env, &ss_e1, &ss_e2, new_ss, retaddr) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr); } if (!(ss_e2 & DESC_S_MASK) || (ss_e2 & DESC_CS_MASK) || !(ss_e2 & DESC_W_MASK)) { - raise_exception_err(env, EXCP0D_GPF, new_ss & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr); } dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3; if (dpl != rpl) { - raise_exception_err(env, EXCP0D_GPF, new_ss & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr); } if (!(ss_e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, new_ss & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, new_ss & 0xfffc, retaddr); } cpu_x86_load_seg_cache(env, R_SS, new_ss, get_seg_base(ss_e1, ss_e2), @@ -2246,12 +2202,12 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, return; return_to_vm86: - POPL(ssp, sp, sp_mask, new_esp); - POPL(ssp, sp, sp_mask, new_ss); - POPL(ssp, sp, sp_mask, new_es); - POPL(ssp, sp, sp_mask, new_ds); - POPL(ssp, sp, sp_mask, new_fs); - POPL(ssp, sp, sp_mask, new_gs); + POPL_RA(ssp, sp, sp_mask, new_esp, retaddr); + POPL_RA(ssp, sp, sp_mask, new_ss, retaddr); + POPL_RA(ssp, sp, sp_mask, new_es, retaddr); + POPL_RA(ssp, sp, sp_mask, new_ds, retaddr); + POPL_RA(ssp, sp, sp_mask, new_fs, retaddr); + POPL_RA(ssp, sp, sp_mask, new_gs, retaddr); /* modify processor state */ cpu_load_eflags(env, new_eflags, TF_MASK | AC_MASK | ID_MASK | @@ -2277,37 +2233,37 @@ void helper_iret_protected(CPUX86State *env, int shift, int next_eip) if (env->eflags & NT_MASK) { #ifdef TARGET_X86_64 if (env->hflags & HF_LMA_MASK) { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } #endif - tss_selector = cpu_lduw_kernel(env, env->tr.base + 0); + tss_selector = cpu_lduw_kernel_ra(env, env->tr.base + 0, GETPC()); if (tss_selector & 4) { - raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, GETPC()); } - if (load_segment(env, &e1, &e2, tss_selector) != 0) { - raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc); + if (load_segment_ra(env, &e1, &e2, tss_selector, GETPC()) != 0) { + raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, GETPC()); } type = (e2 >> DESC_TYPE_SHIFT) & 0x17; /* NOTE: we check both segment and busy TSS */ if (type != 3) { - raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, GETPC()); } - switch_tss(env, tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip); + switch_tss_ra(env, tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip, GETPC()); } else { - helper_ret_protected(env, shift, 1, 0); + helper_ret_protected(env, shift, 1, 0, GETPC()); } env->hflags2 &= ~HF2_NMI_MASK; } void helper_lret_protected(CPUX86State *env, int shift, int addend) { - helper_ret_protected(env, shift, 0, addend); + helper_ret_protected(env, shift, 0, addend, GETPC()); } void helper_sysenter(CPUX86State *env) { if (env->sysenter_cs == 0) { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } env->eflags &= ~(VM_MASK | IF_MASK | RF_MASK); @@ -2343,7 +2299,7 @@ void helper_sysexit(CPUX86State *env, int dflag) cpl = env->hflags & HF_CPL_MASK; if (env->sysenter_cs == 0 || cpl != 0) { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } #ifdef TARGET_X86_64 if (dflag == 2) { @@ -2387,7 +2343,7 @@ target_ulong helper_lsl(CPUX86State *env, target_ulong selector1) if ((selector & 0xfffc) == 0) { goto fail; } - if (load_segment(env, &e1, &e2, selector) != 0) { + if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { goto fail; } rpl = selector & 3; @@ -2434,7 +2390,7 @@ target_ulong helper_lar(CPUX86State *env, target_ulong selector1) if ((selector & 0xfffc) == 0) { goto fail; } - if (load_segment(env, &e1, &e2, selector) != 0) { + if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { goto fail; } rpl = selector & 3; @@ -2483,7 +2439,7 @@ void helper_verr(CPUX86State *env, target_ulong selector1) if ((selector & 0xfffc) == 0) { goto fail; } - if (load_segment(env, &e1, &e2, selector) != 0) { + if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { goto fail; } if (!(e2 & DESC_S_MASK)) { @@ -2521,7 +2477,7 @@ void helper_verw(CPUX86State *env, target_ulong selector1) if ((selector & 0xfffc) == 0) { goto fail; } - if (load_segment(env, &e1, &e2, selector) != 0) { + if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { goto fail; } if (!(e2 & DESC_S_MASK)) { @@ -2562,7 +2518,8 @@ void cpu_x86_load_seg(CPUX86State *env, int seg_reg, int selector) #endif /* check if Port I/O is allowed in TSS */ -static inline void check_io(CPUX86State *env, int addr, int size) +static inline void check_io(CPUX86State *env, int addr, int size, + uintptr_t retaddr) { int io_offset, val, mask; @@ -2572,33 +2529,33 @@ static inline void check_io(CPUX86State *env, int addr, int size) env->tr.limit < 103) { goto fail; } - io_offset = cpu_lduw_kernel(env, env->tr.base + 0x66); + io_offset = cpu_lduw_kernel_ra(env, env->tr.base + 0x66, retaddr); io_offset += (addr >> 3); /* Note: the check needs two bytes */ if ((io_offset + 1) > env->tr.limit) { goto fail; } - val = cpu_lduw_kernel(env, env->tr.base + io_offset); + val = cpu_lduw_kernel_ra(env, env->tr.base + io_offset, retaddr); val >>= (addr & 7); mask = (1 << size) - 1; /* all bits must be zero to allow the I/O */ if ((val & mask) != 0) { fail: - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, retaddr); } } void helper_check_iob(CPUX86State *env, uint32_t t0) { - check_io(env, t0, 1); + check_io(env, t0, 1, GETPC()); } void helper_check_iow(CPUX86State *env, uint32_t t0) { - check_io(env, t0, 2); + check_io(env, t0, 2, GETPC()); } void helper_check_iol(CPUX86State *env, uint32_t t0) { - check_io(env, t0, 4); + check_io(env, t0, 4, GETPC()); } diff --git a/qemu/target-i386/smm_helper.c b/qemu/target-i386/smm_helper.c index 02e24b923..4dd6a2c54 100644 --- a/qemu/target-i386/smm_helper.c +++ b/qemu/target-i386/smm_helper.c @@ -17,8 +17,10 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" +#include "exec/log.h" /* SMM support */ @@ -97,6 +99,10 @@ void do_smm_enter(X86CPU *cpu) x86_stl_phys(cs, sm_state + 0x7e94, env->tr.limit); x86_stw_phys(cs, sm_state + 0x7e92, (env->tr.flags >> 8) & 0xf0ff); + /* ??? Vol 1, 16.5.6 Intel MPX and SMM says that IA32_BNDCFGS + is saved at offset 7ED0. Vol 3, 34.4.1.1, Table 32-2, has + 7EA0-7ED7 as "reserved". What's this, and what's really + supposed to happen? */ x86_stq_phys(cs, sm_state + 0x7ed0, env->efer); x86_stq_phys(cs, sm_state + 0x7ff8, env->regs[R_EAX]); @@ -266,7 +272,7 @@ void helper_rsm(CPUX86State *env) val = x86_ldl_phys(cs, sm_state + 0x7efc); /* revision ID */ if (val & 0x20000) { - env->smbase = x86_ldl_phys(cs, sm_state + 0x7f00) & ~0x7fff; + env->smbase = x86_ldl_phys(cs, sm_state + 0x7f00); } #else cpu_x86_update_cr0(env, x86_ldl_phys(cs, sm_state + 0x7ffc)); @@ -319,7 +325,7 @@ void helper_rsm(CPUX86State *env) val = x86_ldl_phys(cs, sm_state + 0x7efc); /* revision ID */ if (val & 0x20000) { - env->smbase = x86_ldl_phys(cs, sm_state + 0x7ef8) & ~0x7fff; + env->smbase = x86_ldl_phys(cs, sm_state + 0x7ef8); } #endif if ((env->hflags2 & HF2_SMM_INSIDE_NMI_MASK) == 0) { diff --git a/qemu/target-i386/svm_helper.c b/qemu/target-i386/svm_helper.c index f1fabf54e..ab472f6ee 100644 --- a/qemu/target-i386/svm_helper.c +++ b/qemu/target-i386/svm_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/cpu-all.h" #include "exec/helper-proto.h" diff --git a/qemu/target-i386/translate.c b/qemu/target-i386/translate.c index 82e2245bf..1a1214dcb 100644 --- a/qemu/target-i386/translate.c +++ b/qemu/target-i386/translate.c @@ -16,12 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include "qemu/host-utils.h" #include "cpu.h" @@ -33,6 +28,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" #define PREFIX_REPZ 0x01 @@ -60,24 +56,37 @@ # define clztl clz32 #endif +/* For a switch indexed by MODRM, match all memory operands for a given OP. */ +#define CASE_MODRM_MEM_OP(OP) \ + case (0 << 6) | (OP << 3) | 0 ... (0 << 6) | (OP << 3) | 7: \ + case (1 << 6) | (OP << 3) | 0 ... (1 << 6) | (OP << 3) | 7: \ + case (2 << 6) | (OP << 3) | 0 ... (2 << 6) | (OP << 3) | 7 + +#define CASE_MODRM_OP(OP) \ + case (0 << 6) | (OP << 3) | 0 ... (0 << 6) | (OP << 3) | 7: \ + case (1 << 6) | (OP << 3) | 0 ... (1 << 6) | (OP << 3) | 7: \ + case (2 << 6) | (OP << 3) | 0 ... (2 << 6) | (OP << 3) | 7: \ + case (3 << 6) | (OP << 3) | 0 ... (3 << 6) | (OP << 3) | 7 + //#define MACRO_TEST 1 /* global register indexes */ -static TCGv_ptr cpu_env; +static TCGv_env cpu_env; static TCGv cpu_A0; static TCGv cpu_cc_dst, cpu_cc_src, cpu_cc_src2, cpu_cc_srcT; static TCGv_i32 cpu_cc_op; static TCGv cpu_regs[CPU_NB_REGS]; +static TCGv cpu_seg_base[6]; +static TCGv_i64 cpu_bndl[4]; +static TCGv_i64 cpu_bndu[4]; /* local temps */ -static TCGv cpu_T[2]; +static TCGv cpu_T0, cpu_T1; /* local register indexes (only used inside old micro ops) */ static TCGv cpu_tmp0, cpu_tmp4; static TCGv_ptr cpu_ptr0, cpu_ptr1; static TCGv_i32 cpu_tmp2_i32, cpu_tmp3_i32; static TCGv_i64 cpu_tmp1_i64; -static uint8_t gen_opc_cc_op[OPC_BUF_SIZE]; - #include "exec/gen-icount.h" #ifdef TARGET_X86_64 @@ -90,6 +99,7 @@ typedef struct DisasContext { int prefix; TCGMemOp aflag; TCGMemOp dflag; + target_ulong pc_start; target_ulong pc; /* pc = eip + cs_base */ int is_jmp; /* 1 = means jump (stop translation), 2 means CPU static state change (stop translation) */ @@ -126,6 +136,7 @@ typedef struct DisasContext { int cpuid_ext2_features; int cpuid_ext3_features; int cpuid_7_0_ebx_features; + int cpuid_xsave_features; } DisasContext; static void gen_eob(DisasContext *s); @@ -310,6 +321,12 @@ static inline TCGMemOp mo_pushpop(DisasContext *s, TCGMemOp ot) } } +/* Select the size of the stack pointer. */ +static inline TCGMemOp mo_stacksize(DisasContext *s) +{ + return CODE64(s) ? MO_64 : s->ss32 ? MO_32 : MO_16; +} + /* Select only size 64 else 32. Used for SSE operand sizes. */ static inline TCGMemOp mo_64_32(TCGMemOp ot) { @@ -372,34 +389,12 @@ static inline void gen_op_mov_v_reg(TCGMemOp ot, TCGv t0, int reg) } } -static inline void gen_op_movl_A0_reg(int reg) -{ - tcg_gen_mov_tl(cpu_A0, cpu_regs[reg]); -} - -static inline void gen_op_addl_A0_im(int32_t val) -{ - tcg_gen_addi_tl(cpu_A0, cpu_A0, val); -#ifdef TARGET_X86_64 - tcg_gen_andi_tl(cpu_A0, cpu_A0, 0xffffffff); -#endif -} - -#ifdef TARGET_X86_64 -static inline void gen_op_addq_A0_im(int64_t val) -{ - tcg_gen_addi_tl(cpu_A0, cpu_A0, val); -} -#endif - static void gen_add_A0_im(DisasContext *s, int val) { -#ifdef TARGET_X86_64 - if (CODE64(s)) - gen_op_addq_A0_im(val); - else -#endif - gen_op_addl_A0_im(val); + tcg_gen_addi_tl(cpu_A0, cpu_A0, val); + if (!CODE64(s)) { + tcg_gen_ext32u_tl(cpu_A0, cpu_A0); + } } static inline void gen_op_jmp_v(TCGv dest) @@ -415,68 +410,10 @@ static inline void gen_op_add_reg_im(TCGMemOp size, int reg, int32_t val) static inline void gen_op_add_reg_T0(TCGMemOp size, int reg) { - tcg_gen_add_tl(cpu_tmp0, cpu_regs[reg], cpu_T[0]); + tcg_gen_add_tl(cpu_tmp0, cpu_regs[reg], cpu_T0); gen_op_mov_reg_v(size, reg, cpu_tmp0); } -static inline void gen_op_addl_A0_reg_sN(int shift, int reg) -{ - tcg_gen_mov_tl(cpu_tmp0, cpu_regs[reg]); - if (shift != 0) - tcg_gen_shli_tl(cpu_tmp0, cpu_tmp0, shift); - tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); - /* For x86_64, this sets the higher half of register to zero. - For i386, this is equivalent to a nop. */ - tcg_gen_ext32u_tl(cpu_A0, cpu_A0); -} - -static inline void gen_op_movl_A0_seg(int reg) -{ - tcg_gen_ld32u_tl(cpu_A0, cpu_env, offsetof(CPUX86State, segs[reg].base) + REG_L_OFFSET); -} - -static inline void gen_op_addl_A0_seg(DisasContext *s, int reg) -{ - tcg_gen_ld_tl(cpu_tmp0, cpu_env, offsetof(CPUX86State, segs[reg].base)); -#ifdef TARGET_X86_64 - if (CODE64(s)) { - tcg_gen_andi_tl(cpu_A0, cpu_A0, 0xffffffff); - tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); - } else { - tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); - tcg_gen_andi_tl(cpu_A0, cpu_A0, 0xffffffff); - } -#else - tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); -#endif -} - -#ifdef TARGET_X86_64 -static inline void gen_op_movq_A0_seg(int reg) -{ - tcg_gen_ld_tl(cpu_A0, cpu_env, offsetof(CPUX86State, segs[reg].base)); -} - -static inline void gen_op_addq_A0_seg(int reg) -{ - tcg_gen_ld_tl(cpu_tmp0, cpu_env, offsetof(CPUX86State, segs[reg].base)); - tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); -} - -static inline void gen_op_movq_A0_reg(int reg) -{ - tcg_gen_mov_tl(cpu_A0, cpu_regs[reg]); -} - -static inline void gen_op_addq_A0_reg_sN(int shift, int reg) -{ - tcg_gen_mov_tl(cpu_tmp0, cpu_regs[reg]); - if (shift != 0) - tcg_gen_shli_tl(cpu_tmp0, cpu_tmp0, shift); - tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); -} -#endif - static inline void gen_op_ld_v(DisasContext *s, int idx, TCGv t0, TCGv a0) { tcg_gen_qemu_ld_tl(t0, a0, s->mem_index, idx | MO_LE); @@ -490,9 +427,9 @@ static inline void gen_op_st_v(DisasContext *s, int idx, TCGv t0, TCGv a0) static inline void gen_op_st_rm_T0_A0(DisasContext *s, int idx, int d) { if (d == OR_TMP0) { - gen_op_st_v(s, idx, cpu_T[0], cpu_A0); + gen_op_st_v(s, idx, cpu_T0, cpu_A0); } else { - gen_op_mov_reg_v(idx, d, cpu_T[0]); + gen_op_mov_reg_v(idx, d, cpu_T0); } } @@ -502,74 +439,77 @@ static inline void gen_jmp_im(target_ulong pc) gen_op_jmp_v(cpu_tmp0); } -static inline void gen_string_movl_A0_ESI(DisasContext *s) +/* Compute SEG:REG into A0. SEG is selected from the override segment + (OVR_SEG) and the default segment (DEF_SEG). OVR_SEG may be -1 to + indicate no override. */ +static void gen_lea_v_seg(DisasContext *s, TCGMemOp aflag, TCGv a0, + int def_seg, int ovr_seg) { - int override; - - override = s->override; - switch (s->aflag) { + switch (aflag) { #ifdef TARGET_X86_64 case MO_64: - if (override >= 0) { - gen_op_movq_A0_seg(override); - gen_op_addq_A0_reg_sN(0, R_ESI); - } else { - gen_op_movq_A0_reg(R_ESI); + if (ovr_seg < 0) { + tcg_gen_mov_tl(cpu_A0, a0); + return; } break; #endif case MO_32: /* 32 bit address */ - if (s->addseg && override < 0) - override = R_DS; - if (override >= 0) { - gen_op_movl_A0_seg(override); - gen_op_addl_A0_reg_sN(0, R_ESI); - } else { - gen_op_movl_A0_reg(R_ESI); + if (ovr_seg < 0) { + if (s->addseg) { + ovr_seg = def_seg; + } else { + tcg_gen_ext32u_tl(cpu_A0, a0); + return; + } } break; case MO_16: - /* 16 address, always override */ - if (override < 0) - override = R_DS; - tcg_gen_ext16u_tl(cpu_A0, cpu_regs[R_ESI]); - gen_op_addl_A0_seg(s, override); + /* 16 bit address */ + tcg_gen_ext16u_tl(cpu_A0, a0); + a0 = cpu_A0; + if (ovr_seg < 0) { + if (s->addseg) { + ovr_seg = def_seg; + } else { + return; + } + } break; default: tcg_abort(); } -} -static inline void gen_string_movl_A0_EDI(DisasContext *s) -{ - switch (s->aflag) { -#ifdef TARGET_X86_64 - case MO_64: - gen_op_movq_A0_reg(R_EDI); - break; -#endif - case MO_32: - if (s->addseg) { - gen_op_movl_A0_seg(R_ES); - gen_op_addl_A0_reg_sN(0, R_EDI); + if (ovr_seg >= 0) { + TCGv seg = cpu_seg_base[ovr_seg]; + + if (aflag == MO_64) { + tcg_gen_add_tl(cpu_A0, a0, seg); + } else if (CODE64(s)) { + tcg_gen_ext32u_tl(cpu_A0, a0); + tcg_gen_add_tl(cpu_A0, cpu_A0, seg); } else { - gen_op_movl_A0_reg(R_EDI); + tcg_gen_add_tl(cpu_A0, a0, seg); + tcg_gen_ext32u_tl(cpu_A0, cpu_A0); } - break; - case MO_16: - tcg_gen_ext16u_tl(cpu_A0, cpu_regs[R_EDI]); - gen_op_addl_A0_seg(s, R_ES); - break; - default: - tcg_abort(); } } +static inline void gen_string_movl_A0_ESI(DisasContext *s) +{ + gen_lea_v_seg(s, s->aflag, cpu_regs[R_ESI], R_DS, s->override); +} + +static inline void gen_string_movl_A0_EDI(DisasContext *s) +{ + gen_lea_v_seg(s, s->aflag, cpu_regs[R_EDI], R_ES, -1); +} + static inline void gen_op_movl_T0_Dshift(TCGMemOp ot) { - tcg_gen_ld32s_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, df)); - tcg_gen_shli_tl(cpu_T[0], cpu_T[0], ot); + tcg_gen_ld32s_tl(cpu_T0, cpu_env, offsetof(CPUX86State, df)); + tcg_gen_shli_tl(cpu_T0, cpu_T0, ot); }; static TCGv gen_ext_tl(TCGv dst, TCGv src, TCGMemOp size, bool sign) @@ -664,15 +604,10 @@ static void gen_helper_out_func(TCGMemOp ot, TCGv_i32 v, TCGv_i32 n) static void gen_check_io(DisasContext *s, TCGMemOp ot, target_ulong cur_eip, uint32_t svm_flags) { - int state_saved; target_ulong next_eip; - state_saved = 0; if (s->pe && (s->cpl > s->iopl || s->vm86)) { - gen_update_cc_op(s); - gen_jmp_im(cur_eip); - state_saved = 1; - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); switch (ot) { case MO_8: gen_helper_check_iob(cpu_env, cpu_tmp2_i32); @@ -688,13 +623,11 @@ static void gen_check_io(DisasContext *s, TCGMemOp ot, target_ulong cur_eip, } } if(s->flags & HF_SVMI_MASK) { - if (!state_saved) { - gen_update_cc_op(s); - gen_jmp_im(cur_eip); - } + gen_update_cc_op(s); + gen_jmp_im(cur_eip); svm_flags |= (1 << (4 + ot)); next_eip = s->pc - s->cs_base; - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); gen_helper_svm_check_io(cpu_env, cpu_tmp2_i32, tcg_const_i32(svm_flags), tcg_const_i32(next_eip - cur_eip)); @@ -704,9 +637,9 @@ static void gen_check_io(DisasContext *s, TCGMemOp ot, target_ulong cur_eip, static inline void gen_movs(DisasContext *s, TCGMemOp ot) { gen_string_movl_A0_ESI(s); - gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); + gen_op_ld_v(s, ot, cpu_T0, cpu_A0); gen_string_movl_A0_EDI(s); - gen_op_st_v(s, ot, cpu_T[0], cpu_A0); + gen_op_st_v(s, ot, cpu_T0, cpu_A0); gen_op_movl_T0_Dshift(ot); gen_op_add_reg_T0(s->aflag, R_ESI); gen_op_add_reg_T0(s->aflag, R_EDI); @@ -714,31 +647,31 @@ static inline void gen_movs(DisasContext *s, TCGMemOp ot) static void gen_op_update1_cc(void) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); + tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); } static void gen_op_update2_cc(void) { - tcg_gen_mov_tl(cpu_cc_src, cpu_T[1]); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); + tcg_gen_mov_tl(cpu_cc_src, cpu_T1); + tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); } static void gen_op_update3_cc(TCGv reg) { tcg_gen_mov_tl(cpu_cc_src2, reg); - tcg_gen_mov_tl(cpu_cc_src, cpu_T[1]); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); + tcg_gen_mov_tl(cpu_cc_src, cpu_T1); + tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); } static inline void gen_op_testl_T0_T1_cc(void) { - tcg_gen_and_tl(cpu_cc_dst, cpu_T[0], cpu_T[1]); + tcg_gen_and_tl(cpu_cc_dst, cpu_T0, cpu_T1); } static void gen_op_update_neg_cc(void) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); - tcg_gen_neg_tl(cpu_cc_src, cpu_T[0]); + tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); + tcg_gen_neg_tl(cpu_cc_src, cpu_T0); tcg_gen_movi_tl(cpu_cc_srcT, 0); } @@ -1080,11 +1013,11 @@ static inline void gen_compute_eflags_c(DisasContext *s, TCGv reg) value 'b'. In the fast case, T0 is guaranted not to be used. */ static inline void gen_jcc1_noeob(DisasContext *s, int b, TCGLabel *l1) { - CCPrepare cc = gen_prepare_cc(s, b, cpu_T[0]); + CCPrepare cc = gen_prepare_cc(s, b, cpu_T0); if (cc.mask != -1) { - tcg_gen_andi_tl(cpu_T[0], cc.reg, cc.mask); - cc.reg = cpu_T[0]; + tcg_gen_andi_tl(cpu_T0, cc.reg, cc.mask); + cc.reg = cpu_T0; } if (cc.use_reg2) { tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1); @@ -1098,12 +1031,12 @@ static inline void gen_jcc1_noeob(DisasContext *s, int b, TCGLabel *l1) A translation block must end soon. */ static inline void gen_jcc1(DisasContext *s, int b, TCGLabel *l1) { - CCPrepare cc = gen_prepare_cc(s, b, cpu_T[0]); + CCPrepare cc = gen_prepare_cc(s, b, cpu_T0); gen_update_cc_op(s); if (cc.mask != -1) { - tcg_gen_andi_tl(cpu_T[0], cc.reg, cc.mask); - cc.reg = cpu_T[0]; + tcg_gen_andi_tl(cpu_T0, cc.reg, cc.mask); + cc.reg = cpu_T0; } set_cc_op(s, CC_OP_DYNAMIC); if (cc.use_reg2) { @@ -1128,9 +1061,9 @@ static TCGLabel *gen_jz_ecx_string(DisasContext *s, target_ulong next_eip) static inline void gen_stos(DisasContext *s, TCGMemOp ot) { - gen_op_mov_v_reg(MO_32, cpu_T[0], R_EAX); + gen_op_mov_v_reg(MO_32, cpu_T0, R_EAX); gen_string_movl_A0_EDI(s); - gen_op_st_v(s, ot, cpu_T[0], cpu_A0); + gen_op_st_v(s, ot, cpu_T0, cpu_A0); gen_op_movl_T0_Dshift(ot); gen_op_add_reg_T0(s->aflag, R_EDI); } @@ -1138,8 +1071,8 @@ static inline void gen_stos(DisasContext *s, TCGMemOp ot) static inline void gen_lods(DisasContext *s, TCGMemOp ot) { gen_string_movl_A0_ESI(s); - gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); - gen_op_mov_reg_v(ot, R_EAX, cpu_T[0]); + gen_op_ld_v(s, ot, cpu_T0, cpu_A0); + gen_op_mov_reg_v(ot, R_EAX, cpu_T0); gen_op_movl_T0_Dshift(ot); gen_op_add_reg_T0(s->aflag, R_ESI); } @@ -1147,7 +1080,7 @@ static inline void gen_lods(DisasContext *s, TCGMemOp ot) static inline void gen_scas(DisasContext *s, TCGMemOp ot) { gen_string_movl_A0_EDI(s); - gen_op_ld_v(s, ot, cpu_T[1], cpu_A0); + gen_op_ld_v(s, ot, cpu_T1, cpu_A0); gen_op(s, OP_CMPL, ot, R_EAX); gen_op_movl_T0_Dshift(ot); gen_op_add_reg_T0(s->aflag, R_EDI); @@ -1156,7 +1089,7 @@ static inline void gen_scas(DisasContext *s, TCGMemOp ot) static inline void gen_cmps(DisasContext *s, TCGMemOp ot) { gen_string_movl_A0_EDI(s); - gen_op_ld_v(s, ot, cpu_T[1], cpu_A0); + gen_op_ld_v(s, ot, cpu_T1, cpu_A0); gen_string_movl_A0_ESI(s); gen_op(s, OP_CMPL, ot, OR_TMP0); gen_op_movl_T0_Dshift(ot); @@ -1164,6 +1097,19 @@ static inline void gen_cmps(DisasContext *s, TCGMemOp ot) gen_op_add_reg_T0(s->aflag, R_EDI); } +static void gen_bpt_io(DisasContext *s, TCGv_i32 t_port, int ot) +{ + if (s->flags & HF_IOBPT_MASK) { + TCGv_i32 t_size = tcg_const_i32(1 << ot); + TCGv t_next = tcg_const_tl(s->pc - s->cs_base); + + gen_helper_bpt_io(cpu_env, t_port, t_size, t_next); + tcg_temp_free_i32(t_size); + tcg_temp_free(t_next); + } +} + + static inline void gen_ins(DisasContext *s, TCGMemOp ot) { if (s->tb->cflags & CF_USE_ICOUNT) { @@ -1172,14 +1118,15 @@ static inline void gen_ins(DisasContext *s, TCGMemOp ot) gen_string_movl_A0_EDI(s); /* Note: we must do this dummy write first to be restartable in case of page fault. */ - tcg_gen_movi_tl(cpu_T[0], 0); - gen_op_st_v(s, ot, cpu_T[0], cpu_A0); + tcg_gen_movi_tl(cpu_T0, 0); + gen_op_st_v(s, ot, cpu_T0, cpu_A0); tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_regs[R_EDX]); tcg_gen_andi_i32(cpu_tmp2_i32, cpu_tmp2_i32, 0xffff); - gen_helper_in_func(ot, cpu_T[0], cpu_tmp2_i32); - gen_op_st_v(s, ot, cpu_T[0], cpu_A0); + gen_helper_in_func(ot, cpu_T0, cpu_tmp2_i32); + gen_op_st_v(s, ot, cpu_T0, cpu_A0); gen_op_movl_T0_Dshift(ot); gen_op_add_reg_T0(s->aflag, R_EDI); + gen_bpt_io(s, cpu_tmp2_i32, ot); if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); } @@ -1191,15 +1138,15 @@ static inline void gen_outs(DisasContext *s, TCGMemOp ot) gen_io_start(); } gen_string_movl_A0_ESI(s); - gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); + gen_op_ld_v(s, ot, cpu_T0, cpu_A0); tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_regs[R_EDX]); tcg_gen_andi_i32(cpu_tmp2_i32, cpu_tmp2_i32, 0xffff); - tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T0); gen_helper_out_func(ot, cpu_tmp2_i32, cpu_tmp3_i32); - gen_op_movl_T0_Dshift(ot); gen_op_add_reg_T0(s->aflag, R_ESI); + gen_bpt_io(s, cpu_tmp2_i32, ot); if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); } @@ -1309,63 +1256,63 @@ static void gen_helper_fp_arith_STN_ST0(int op, int opreg) static void gen_op(DisasContext *s1, int op, TCGMemOp ot, int d) { if (d != OR_TMP0) { - gen_op_mov_v_reg(ot, cpu_T[0], d); + gen_op_mov_v_reg(ot, cpu_T0, d); } else { - gen_op_ld_v(s1, ot, cpu_T[0], cpu_A0); + gen_op_ld_v(s1, ot, cpu_T0, cpu_A0); } switch(op) { case OP_ADCL: gen_compute_eflags_c(s1, cpu_tmp4); - tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_T[1]); - tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_tmp4); + tcg_gen_add_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_add_tl(cpu_T0, cpu_T0, cpu_tmp4); gen_op_st_rm_T0_A0(s1, ot, d); gen_op_update3_cc(cpu_tmp4); set_cc_op(s1, CC_OP_ADCB + ot); break; case OP_SBBL: gen_compute_eflags_c(s1, cpu_tmp4); - tcg_gen_sub_tl(cpu_T[0], cpu_T[0], cpu_T[1]); - tcg_gen_sub_tl(cpu_T[0], cpu_T[0], cpu_tmp4); + tcg_gen_sub_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_sub_tl(cpu_T0, cpu_T0, cpu_tmp4); gen_op_st_rm_T0_A0(s1, ot, d); gen_op_update3_cc(cpu_tmp4); set_cc_op(s1, CC_OP_SBBB + ot); break; case OP_ADDL: - tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + tcg_gen_add_tl(cpu_T0, cpu_T0, cpu_T1); gen_op_st_rm_T0_A0(s1, ot, d); gen_op_update2_cc(); set_cc_op(s1, CC_OP_ADDB + ot); break; case OP_SUBL: - tcg_gen_mov_tl(cpu_cc_srcT, cpu_T[0]); - tcg_gen_sub_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + tcg_gen_mov_tl(cpu_cc_srcT, cpu_T0); + tcg_gen_sub_tl(cpu_T0, cpu_T0, cpu_T1); gen_op_st_rm_T0_A0(s1, ot, d); gen_op_update2_cc(); set_cc_op(s1, CC_OP_SUBB + ot); break; default: case OP_ANDL: - tcg_gen_and_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + tcg_gen_and_tl(cpu_T0, cpu_T0, cpu_T1); gen_op_st_rm_T0_A0(s1, ot, d); gen_op_update1_cc(); set_cc_op(s1, CC_OP_LOGICB + ot); break; case OP_ORL: - tcg_gen_or_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + tcg_gen_or_tl(cpu_T0, cpu_T0, cpu_T1); gen_op_st_rm_T0_A0(s1, ot, d); gen_op_update1_cc(); set_cc_op(s1, CC_OP_LOGICB + ot); break; case OP_XORL: - tcg_gen_xor_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + tcg_gen_xor_tl(cpu_T0, cpu_T0, cpu_T1); gen_op_st_rm_T0_A0(s1, ot, d); gen_op_update1_cc(); set_cc_op(s1, CC_OP_LOGICB + ot); break; case OP_CMPL: - tcg_gen_mov_tl(cpu_cc_src, cpu_T[1]); - tcg_gen_mov_tl(cpu_cc_srcT, cpu_T[0]); - tcg_gen_sub_tl(cpu_cc_dst, cpu_T[0], cpu_T[1]); + tcg_gen_mov_tl(cpu_cc_src, cpu_T1); + tcg_gen_mov_tl(cpu_cc_srcT, cpu_T0); + tcg_gen_sub_tl(cpu_cc_dst, cpu_T0, cpu_T1); set_cc_op(s1, CC_OP_SUBB + ot); break; } @@ -1375,20 +1322,20 @@ static void gen_op(DisasContext *s1, int op, TCGMemOp ot, int d) static void gen_inc(DisasContext *s1, TCGMemOp ot, int d, int c) { if (d != OR_TMP0) { - gen_op_mov_v_reg(ot, cpu_T[0], d); + gen_op_mov_v_reg(ot, cpu_T0, d); } else { - gen_op_ld_v(s1, ot, cpu_T[0], cpu_A0); + gen_op_ld_v(s1, ot, cpu_T0, cpu_A0); } gen_compute_eflags_c(s1, cpu_cc_src); if (c > 0) { - tcg_gen_addi_tl(cpu_T[0], cpu_T[0], 1); + tcg_gen_addi_tl(cpu_T0, cpu_T0, 1); set_cc_op(s1, CC_OP_INCB + ot); } else { - tcg_gen_addi_tl(cpu_T[0], cpu_T[0], -1); + tcg_gen_addi_tl(cpu_T0, cpu_T0, -1); set_cc_op(s1, CC_OP_DECB + ot); } gen_op_st_rm_T0_A0(s1, ot, d); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); + tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); } static void gen_shift_flags(DisasContext *s, TCGMemOp ot, TCGv result, @@ -1443,33 +1390,33 @@ static void gen_shift_rm_T1(DisasContext *s, TCGMemOp ot, int op1, /* load */ if (op1 == OR_TMP0) { - gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); + gen_op_ld_v(s, ot, cpu_T0, cpu_A0); } else { - gen_op_mov_v_reg(ot, cpu_T[0], op1); + gen_op_mov_v_reg(ot, cpu_T0, op1); } - tcg_gen_andi_tl(cpu_T[1], cpu_T[1], mask); - tcg_gen_subi_tl(cpu_tmp0, cpu_T[1], 1); + tcg_gen_andi_tl(cpu_T1, cpu_T1, mask); + tcg_gen_subi_tl(cpu_tmp0, cpu_T1, 1); if (is_right) { if (is_arith) { - gen_exts(ot, cpu_T[0]); - tcg_gen_sar_tl(cpu_tmp0, cpu_T[0], cpu_tmp0); - tcg_gen_sar_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + gen_exts(ot, cpu_T0); + tcg_gen_sar_tl(cpu_tmp0, cpu_T0, cpu_tmp0); + tcg_gen_sar_tl(cpu_T0, cpu_T0, cpu_T1); } else { - gen_extu(ot, cpu_T[0]); - tcg_gen_shr_tl(cpu_tmp0, cpu_T[0], cpu_tmp0); - tcg_gen_shr_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + gen_extu(ot, cpu_T0); + tcg_gen_shr_tl(cpu_tmp0, cpu_T0, cpu_tmp0); + tcg_gen_shr_tl(cpu_T0, cpu_T0, cpu_T1); } } else { - tcg_gen_shl_tl(cpu_tmp0, cpu_T[0], cpu_tmp0); - tcg_gen_shl_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + tcg_gen_shl_tl(cpu_tmp0, cpu_T0, cpu_tmp0); + tcg_gen_shl_tl(cpu_T0, cpu_T0, cpu_T1); } /* store */ gen_op_st_rm_T0_A0(s, ot, op1); - gen_shift_flags(s, ot, cpu_T[0], cpu_tmp0, cpu_T[1], is_right); + gen_shift_flags(s, ot, cpu_T0, cpu_tmp0, cpu_T1, is_right); } static void gen_shift_rm_im(DisasContext *s, TCGMemOp ot, int op1, int op2, @@ -1479,25 +1426,25 @@ static void gen_shift_rm_im(DisasContext *s, TCGMemOp ot, int op1, int op2, /* load */ if (op1 == OR_TMP0) - gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); + gen_op_ld_v(s, ot, cpu_T0, cpu_A0); else - gen_op_mov_v_reg(ot, cpu_T[0], op1); + gen_op_mov_v_reg(ot, cpu_T0, op1); op2 &= mask; if (op2 != 0) { if (is_right) { if (is_arith) { - gen_exts(ot, cpu_T[0]); - tcg_gen_sari_tl(cpu_tmp4, cpu_T[0], op2 - 1); - tcg_gen_sari_tl(cpu_T[0], cpu_T[0], op2); + gen_exts(ot, cpu_T0); + tcg_gen_sari_tl(cpu_tmp4, cpu_T0, op2 - 1); + tcg_gen_sari_tl(cpu_T0, cpu_T0, op2); } else { - gen_extu(ot, cpu_T[0]); - tcg_gen_shri_tl(cpu_tmp4, cpu_T[0], op2 - 1); - tcg_gen_shri_tl(cpu_T[0], cpu_T[0], op2); + gen_extu(ot, cpu_T0); + tcg_gen_shri_tl(cpu_tmp4, cpu_T0, op2 - 1); + tcg_gen_shri_tl(cpu_T0, cpu_T0, op2); } } else { - tcg_gen_shli_tl(cpu_tmp4, cpu_T[0], op2 - 1); - tcg_gen_shli_tl(cpu_T[0], cpu_T[0], op2); + tcg_gen_shli_tl(cpu_tmp4, cpu_T0, op2 - 1); + tcg_gen_shli_tl(cpu_T0, cpu_T0, op2); } } @@ -1507,7 +1454,7 @@ static void gen_shift_rm_im(DisasContext *s, TCGMemOp ot, int op1, int op2, /* update eflags if non zero shift */ if (op2 != 0) { tcg_gen_mov_tl(cpu_cc_src, cpu_tmp4); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); + tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); set_cc_op(s, (is_right ? CC_OP_SARB : CC_OP_SHLB) + ot); } } @@ -1519,41 +1466,41 @@ static void gen_rot_rm_T1(DisasContext *s, TCGMemOp ot, int op1, int is_right) /* load */ if (op1 == OR_TMP0) { - gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); + gen_op_ld_v(s, ot, cpu_T0, cpu_A0); } else { - gen_op_mov_v_reg(ot, cpu_T[0], op1); + gen_op_mov_v_reg(ot, cpu_T0, op1); } - tcg_gen_andi_tl(cpu_T[1], cpu_T[1], mask); + tcg_gen_andi_tl(cpu_T1, cpu_T1, mask); switch (ot) { case MO_8: /* Replicate the 8-bit input so that a 32-bit rotate works. */ - tcg_gen_ext8u_tl(cpu_T[0], cpu_T[0]); - tcg_gen_muli_tl(cpu_T[0], cpu_T[0], 0x01010101); + tcg_gen_ext8u_tl(cpu_T0, cpu_T0); + tcg_gen_muli_tl(cpu_T0, cpu_T0, 0x01010101); goto do_long; case MO_16: /* Replicate the 16-bit input so that a 32-bit rotate works. */ - tcg_gen_deposit_tl(cpu_T[0], cpu_T[0], cpu_T[0], 16, 16); + tcg_gen_deposit_tl(cpu_T0, cpu_T0, cpu_T0, 16, 16); goto do_long; do_long: #ifdef TARGET_X86_64 case MO_32: - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); - tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); + tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T1); if (is_right) { tcg_gen_rotr_i32(cpu_tmp2_i32, cpu_tmp2_i32, cpu_tmp3_i32); } else { tcg_gen_rotl_i32(cpu_tmp2_i32, cpu_tmp2_i32, cpu_tmp3_i32); } - tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32); + tcg_gen_extu_i32_tl(cpu_T0, cpu_tmp2_i32); break; #endif default: if (is_right) { - tcg_gen_rotr_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + tcg_gen_rotr_tl(cpu_T0, cpu_T0, cpu_T1); } else { - tcg_gen_rotl_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + tcg_gen_rotl_tl(cpu_T0, cpu_T0, cpu_T1); } break; } @@ -1569,12 +1516,12 @@ static void gen_rot_rm_T1(DisasContext *s, TCGMemOp ot, int op1, int is_right) since we've computed the flags into CC_SRC, these variables are currently dead. */ if (is_right) { - tcg_gen_shri_tl(cpu_cc_src2, cpu_T[0], mask - 1); - tcg_gen_shri_tl(cpu_cc_dst, cpu_T[0], mask); + tcg_gen_shri_tl(cpu_cc_src2, cpu_T0, mask - 1); + tcg_gen_shri_tl(cpu_cc_dst, cpu_T0, mask); tcg_gen_andi_tl(cpu_cc_dst, cpu_cc_dst, 1); } else { - tcg_gen_shri_tl(cpu_cc_src2, cpu_T[0], mask); - tcg_gen_andi_tl(cpu_cc_dst, cpu_T[0], 1); + tcg_gen_shri_tl(cpu_cc_src2, cpu_T0, mask); + tcg_gen_andi_tl(cpu_cc_dst, cpu_T0, 1); } tcg_gen_andi_tl(cpu_cc_src2, cpu_cc_src2, 1); tcg_gen_xor_tl(cpu_cc_src2, cpu_cc_src2, cpu_cc_dst); @@ -1585,7 +1532,7 @@ static void gen_rot_rm_T1(DisasContext *s, TCGMemOp ot, int op1, int is_right) exactly as we computed above. */ t0 = tcg_const_i32(0); t1 = tcg_temp_new_i32(); - tcg_gen_trunc_tl_i32(t1, cpu_T[1]); + tcg_gen_trunc_tl_i32(t1, cpu_T1); tcg_gen_movi_i32(cpu_tmp2_i32, CC_OP_ADCOX); tcg_gen_movi_i32(cpu_tmp3_i32, CC_OP_EFLAGS); tcg_gen_movcond_i32(TCG_COND_NE, cpu_cc_op, t1, t0, @@ -1605,9 +1552,9 @@ static void gen_rot_rm_im(DisasContext *s, TCGMemOp ot, int op1, int op2, /* load */ if (op1 == OR_TMP0) { - gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); + gen_op_ld_v(s, ot, cpu_T0, cpu_A0); } else { - gen_op_mov_v_reg(ot, cpu_T[0], op1); + gen_op_mov_v_reg(ot, cpu_T0, op1); } op2 &= mask; @@ -1615,20 +1562,20 @@ static void gen_rot_rm_im(DisasContext *s, TCGMemOp ot, int op1, int op2, switch (ot) { #ifdef TARGET_X86_64 case MO_32: - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); if (is_right) { tcg_gen_rotri_i32(cpu_tmp2_i32, cpu_tmp2_i32, op2); } else { tcg_gen_rotli_i32(cpu_tmp2_i32, cpu_tmp2_i32, op2); } - tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32); + tcg_gen_extu_i32_tl(cpu_T0, cpu_tmp2_i32); break; #endif default: if (is_right) { - tcg_gen_rotri_tl(cpu_T[0], cpu_T[0], op2); + tcg_gen_rotri_tl(cpu_T0, cpu_T0, op2); } else { - tcg_gen_rotli_tl(cpu_T[0], cpu_T[0], op2); + tcg_gen_rotli_tl(cpu_T0, cpu_T0, op2); } break; case MO_8: @@ -1641,10 +1588,10 @@ static void gen_rot_rm_im(DisasContext *s, TCGMemOp ot, int op1, int op2, if (is_right) { shift = mask + 1 - shift; } - gen_extu(ot, cpu_T[0]); - tcg_gen_shli_tl(cpu_tmp0, cpu_T[0], shift); - tcg_gen_shri_tl(cpu_T[0], cpu_T[0], mask + 1 - shift); - tcg_gen_or_tl(cpu_T[0], cpu_T[0], cpu_tmp0); + gen_extu(ot, cpu_T0); + tcg_gen_shli_tl(cpu_tmp0, cpu_T0, shift); + tcg_gen_shri_tl(cpu_T0, cpu_T0, mask + 1 - shift); + tcg_gen_or_tl(cpu_T0, cpu_T0, cpu_tmp0); break; } } @@ -1661,12 +1608,12 @@ static void gen_rot_rm_im(DisasContext *s, TCGMemOp ot, int op1, int op2, since we've computed the flags into CC_SRC, these variables are currently dead. */ if (is_right) { - tcg_gen_shri_tl(cpu_cc_src2, cpu_T[0], mask - 1); - tcg_gen_shri_tl(cpu_cc_dst, cpu_T[0], mask); + tcg_gen_shri_tl(cpu_cc_src2, cpu_T0, mask - 1); + tcg_gen_shri_tl(cpu_cc_dst, cpu_T0, mask); tcg_gen_andi_tl(cpu_cc_dst, cpu_cc_dst, 1); } else { - tcg_gen_shri_tl(cpu_cc_src2, cpu_T[0], mask); - tcg_gen_andi_tl(cpu_cc_dst, cpu_T[0], 1); + tcg_gen_shri_tl(cpu_cc_src2, cpu_T0, mask); + tcg_gen_andi_tl(cpu_cc_dst, cpu_T0, 1); } tcg_gen_andi_tl(cpu_cc_src2, cpu_cc_src2, 1); tcg_gen_xor_tl(cpu_cc_src2, cpu_cc_src2, cpu_cc_dst); @@ -1683,24 +1630,24 @@ static void gen_rotc_rm_T1(DisasContext *s, TCGMemOp ot, int op1, /* load */ if (op1 == OR_TMP0) - gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); + gen_op_ld_v(s, ot, cpu_T0, cpu_A0); else - gen_op_mov_v_reg(ot, cpu_T[0], op1); + gen_op_mov_v_reg(ot, cpu_T0, op1); if (is_right) { switch (ot) { case MO_8: - gen_helper_rcrb(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); + gen_helper_rcrb(cpu_T0, cpu_env, cpu_T0, cpu_T1); break; case MO_16: - gen_helper_rcrw(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); + gen_helper_rcrw(cpu_T0, cpu_env, cpu_T0, cpu_T1); break; case MO_32: - gen_helper_rcrl(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); + gen_helper_rcrl(cpu_T0, cpu_env, cpu_T0, cpu_T1); break; #ifdef TARGET_X86_64 case MO_64: - gen_helper_rcrq(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); + gen_helper_rcrq(cpu_T0, cpu_env, cpu_T0, cpu_T1); break; #endif default: @@ -1709,17 +1656,17 @@ static void gen_rotc_rm_T1(DisasContext *s, TCGMemOp ot, int op1, } else { switch (ot) { case MO_8: - gen_helper_rclb(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); + gen_helper_rclb(cpu_T0, cpu_env, cpu_T0, cpu_T1); break; case MO_16: - gen_helper_rclw(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); + gen_helper_rclw(cpu_T0, cpu_env, cpu_T0, cpu_T1); break; case MO_32: - gen_helper_rcll(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); + gen_helper_rcll(cpu_T0, cpu_env, cpu_T0, cpu_T1); break; #ifdef TARGET_X86_64 case MO_64: - gen_helper_rclq(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); + gen_helper_rclq(cpu_T0, cpu_env, cpu_T0, cpu_T1); break; #endif default: @@ -1739,9 +1686,9 @@ static void gen_shiftd_rm_T1(DisasContext *s, TCGMemOp ot, int op1, /* load */ if (op1 == OR_TMP0) { - gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); + gen_op_ld_v(s, ot, cpu_T0, cpu_A0); } else { - gen_op_mov_v_reg(ot, cpu_T[0], op1); + gen_op_mov_v_reg(ot, cpu_T0, op1); } count = tcg_temp_new(); @@ -1753,11 +1700,11 @@ static void gen_shiftd_rm_T1(DisasContext *s, TCGMemOp ot, int op1, This means "shrdw C, B, A" shifts A:B:A >> C. Build the B:A portion by constructing it as a 32-bit value. */ if (is_right) { - tcg_gen_deposit_tl(cpu_tmp0, cpu_T[0], cpu_T[1], 16, 16); - tcg_gen_mov_tl(cpu_T[1], cpu_T[0]); - tcg_gen_mov_tl(cpu_T[0], cpu_tmp0); + tcg_gen_deposit_tl(cpu_tmp0, cpu_T0, cpu_T1, 16, 16); + tcg_gen_mov_tl(cpu_T1, cpu_T0); + tcg_gen_mov_tl(cpu_T0, cpu_tmp0); } else { - tcg_gen_deposit_tl(cpu_T[1], cpu_T[0], cpu_T[1], 16, 16); + tcg_gen_deposit_tl(cpu_T1, cpu_T0, cpu_T1, 16, 16); } /* FALLTHRU */ #ifdef TARGET_X86_64 @@ -1765,57 +1712,57 @@ static void gen_shiftd_rm_T1(DisasContext *s, TCGMemOp ot, int op1, /* Concatenate the two 32-bit values and use a 64-bit shift. */ tcg_gen_subi_tl(cpu_tmp0, count, 1); if (is_right) { - tcg_gen_concat_tl_i64(cpu_T[0], cpu_T[0], cpu_T[1]); - tcg_gen_shr_i64(cpu_tmp0, cpu_T[0], cpu_tmp0); - tcg_gen_shr_i64(cpu_T[0], cpu_T[0], count); + tcg_gen_concat_tl_i64(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_shr_i64(cpu_tmp0, cpu_T0, cpu_tmp0); + tcg_gen_shr_i64(cpu_T0, cpu_T0, count); } else { - tcg_gen_concat_tl_i64(cpu_T[0], cpu_T[1], cpu_T[0]); - tcg_gen_shl_i64(cpu_tmp0, cpu_T[0], cpu_tmp0); - tcg_gen_shl_i64(cpu_T[0], cpu_T[0], count); + tcg_gen_concat_tl_i64(cpu_T0, cpu_T1, cpu_T0); + tcg_gen_shl_i64(cpu_tmp0, cpu_T0, cpu_tmp0); + tcg_gen_shl_i64(cpu_T0, cpu_T0, count); tcg_gen_shri_i64(cpu_tmp0, cpu_tmp0, 32); - tcg_gen_shri_i64(cpu_T[0], cpu_T[0], 32); + tcg_gen_shri_i64(cpu_T0, cpu_T0, 32); } break; #endif default: tcg_gen_subi_tl(cpu_tmp0, count, 1); if (is_right) { - tcg_gen_shr_tl(cpu_tmp0, cpu_T[0], cpu_tmp0); + tcg_gen_shr_tl(cpu_tmp0, cpu_T0, cpu_tmp0); tcg_gen_subfi_tl(cpu_tmp4, mask + 1, count); - tcg_gen_shr_tl(cpu_T[0], cpu_T[0], count); - tcg_gen_shl_tl(cpu_T[1], cpu_T[1], cpu_tmp4); + tcg_gen_shr_tl(cpu_T0, cpu_T0, count); + tcg_gen_shl_tl(cpu_T1, cpu_T1, cpu_tmp4); } else { - tcg_gen_shl_tl(cpu_tmp0, cpu_T[0], cpu_tmp0); + tcg_gen_shl_tl(cpu_tmp0, cpu_T0, cpu_tmp0); if (ot == MO_16) { /* Only needed if count > 16, for Intel behaviour. */ tcg_gen_subfi_tl(cpu_tmp4, 33, count); - tcg_gen_shr_tl(cpu_tmp4, cpu_T[1], cpu_tmp4); + tcg_gen_shr_tl(cpu_tmp4, cpu_T1, cpu_tmp4); tcg_gen_or_tl(cpu_tmp0, cpu_tmp0, cpu_tmp4); } tcg_gen_subfi_tl(cpu_tmp4, mask + 1, count); - tcg_gen_shl_tl(cpu_T[0], cpu_T[0], count); - tcg_gen_shr_tl(cpu_T[1], cpu_T[1], cpu_tmp4); + tcg_gen_shl_tl(cpu_T0, cpu_T0, count); + tcg_gen_shr_tl(cpu_T1, cpu_T1, cpu_tmp4); } tcg_gen_movi_tl(cpu_tmp4, 0); - tcg_gen_movcond_tl(TCG_COND_EQ, cpu_T[1], count, cpu_tmp4, - cpu_tmp4, cpu_T[1]); - tcg_gen_or_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + tcg_gen_movcond_tl(TCG_COND_EQ, cpu_T1, count, cpu_tmp4, + cpu_tmp4, cpu_T1); + tcg_gen_or_tl(cpu_T0, cpu_T0, cpu_T1); break; } /* store */ gen_op_st_rm_T0_A0(s, ot, op1); - gen_shift_flags(s, ot, cpu_T[0], cpu_tmp0, count, is_right); + gen_shift_flags(s, ot, cpu_T0, cpu_tmp0, count, is_right); tcg_temp_free(count); } static void gen_shift(DisasContext *s1, int op, TCGMemOp ot, int d, int s) { if (s != OR_TMP1) - gen_op_mov_v_reg(ot, cpu_T[1], s); + gen_op_mov_v_reg(ot, cpu_T1, s); switch(op) { case OP_ROL: gen_rot_rm_T1(s1, ot, d, 0); @@ -1863,48 +1810,58 @@ static void gen_shifti(DisasContext *s1, int op, TCGMemOp ot, int d, int c) break; default: /* currently not optimized */ - tcg_gen_movi_tl(cpu_T[1], c); + tcg_gen_movi_tl(cpu_T1, c); gen_shift(s1, op, ot, d, OR_TMP1); break; } } -static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm) -{ - target_long disp; - int havesib; +/* Decompose an address. */ + +typedef struct AddressParts { + int def_seg; int base; int index; int scale; - int mod, rm, code, override, must_add_seg; - TCGv sum; + target_long disp; +} AddressParts; + +static AddressParts gen_lea_modrm_0(CPUX86State *env, DisasContext *s, + int modrm) +{ + int def_seg, base, index, scale, mod, rm; + target_long disp; + bool havesib; + + def_seg = R_DS; + index = -1; + scale = 0; + disp = 0; - override = s->override; - must_add_seg = s->addseg; - if (override >= 0) - must_add_seg = 1; mod = (modrm >> 6) & 3; rm = modrm & 7; + base = rm | REX_B(s); + + if (mod == 3) { + /* Normally filtered out earlier, but including this path + simplifies multi-byte nop, as well as bndcl, bndcu, bndcn. */ + goto done; + } switch (s->aflag) { case MO_64: case MO_32: havesib = 0; - base = rm; - index = -1; - scale = 0; - - if (base == 4) { - havesib = 1; - code = cpu_ldub_code(env, s->pc++); + if (rm == 4) { + int code = cpu_ldub_code(env, s->pc++); scale = (code >> 6) & 3; index = ((code >> 3) & 7) | REX_X(s); if (index == 4) { index = -1; /* no index */ } - base = (code & 7); + base = (code & 7) | REX_B(s); + havesib = 1; } - base |= REX_B(s); switch (mod) { case 0: @@ -1913,10 +1870,9 @@ static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm) disp = (int32_t)cpu_ldl_code(env, s->pc); s->pc += 4; if (CODE64(s) && !havesib) { + base = -2; disp += s->pc + s->rip_offset; } - } else { - disp = 0; } break; case 1: @@ -1933,204 +1889,132 @@ static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm) if (base == R_ESP && s->popl_esp_hack) { disp += s->popl_esp_hack; } - - /* Compute the address, with a minimum number of TCG ops. */ - TCGV_UNUSED(sum); - if (index >= 0) { - if (scale == 0) { - sum = cpu_regs[index]; - } else { - tcg_gen_shli_tl(cpu_A0, cpu_regs[index], scale); - sum = cpu_A0; - } - if (base >= 0) { - tcg_gen_add_tl(cpu_A0, sum, cpu_regs[base]); - sum = cpu_A0; - } - } else if (base >= 0) { - sum = cpu_regs[base]; - } - if (TCGV_IS_UNUSED(sum)) { - tcg_gen_movi_tl(cpu_A0, disp); - } else { - tcg_gen_addi_tl(cpu_A0, sum, disp); - } - - if (must_add_seg) { - if (override < 0) { - if (base == R_EBP || base == R_ESP) { - override = R_SS; - } else { - override = R_DS; - } - } - - tcg_gen_ld_tl(cpu_tmp0, cpu_env, - offsetof(CPUX86State, segs[override].base)); - if (CODE64(s)) { - if (s->aflag == MO_32) { - tcg_gen_ext32u_tl(cpu_A0, cpu_A0); - } - tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); - return; - } - - tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); - } - - if (s->aflag == MO_32) { - tcg_gen_ext32u_tl(cpu_A0, cpu_A0); + if (base == R_EBP || base == R_ESP) { + def_seg = R_SS; } break; case MO_16: - switch (mod) { - case 0: + if (mod == 0) { if (rm == 6) { + base = -1; disp = cpu_lduw_code(env, s->pc); s->pc += 2; - tcg_gen_movi_tl(cpu_A0, disp); - rm = 0; /* avoid SS override */ - goto no_rm; - } else { - disp = 0; + break; } - break; - case 1: + } else if (mod == 1) { disp = (int8_t)cpu_ldub_code(env, s->pc++); - break; - default: - case 2: + } else { disp = (int16_t)cpu_lduw_code(env, s->pc); s->pc += 2; - break; } - sum = cpu_A0; switch (rm) { case 0: - tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBX], cpu_regs[R_ESI]); + base = R_EBX; + index = R_ESI; break; case 1: - tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBX], cpu_regs[R_EDI]); + base = R_EBX; + index = R_EDI; break; case 2: - tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBP], cpu_regs[R_ESI]); + base = R_EBP; + index = R_ESI; + def_seg = R_SS; break; case 3: - tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBP], cpu_regs[R_EDI]); + base = R_EBP; + index = R_EDI; + def_seg = R_SS; break; case 4: - sum = cpu_regs[R_ESI]; + base = R_ESI; break; case 5: - sum = cpu_regs[R_EDI]; + base = R_EDI; break; case 6: - sum = cpu_regs[R_EBP]; + base = R_EBP; + def_seg = R_SS; break; default: case 7: - sum = cpu_regs[R_EBX]; + base = R_EBX; break; } - tcg_gen_addi_tl(cpu_A0, sum, disp); - tcg_gen_ext16u_tl(cpu_A0, cpu_A0); - no_rm: - if (must_add_seg) { - if (override < 0) { - if (rm == 2 || rm == 3 || rm == 6) { - override = R_SS; - } else { - override = R_DS; - } - } - gen_op_addl_A0_seg(s, override); - } break; default: tcg_abort(); } + + done: + return (AddressParts){ def_seg, base, index, scale, disp }; } -static void gen_nop_modrm(CPUX86State *env, DisasContext *s, int modrm) +/* Compute the address, with a minimum number of TCG ops. */ +static TCGv gen_lea_modrm_1(AddressParts a) { - int mod, rm, base, code; + TCGv ea; - mod = (modrm >> 6) & 3; - if (mod == 3) - return; - rm = modrm & 7; + TCGV_UNUSED(ea); + if (a.index >= 0) { + if (a.scale == 0) { + ea = cpu_regs[a.index]; + } else { + tcg_gen_shli_tl(cpu_A0, cpu_regs[a.index], a.scale); + ea = cpu_A0; + } + if (a.base >= 0) { + tcg_gen_add_tl(cpu_A0, ea, cpu_regs[a.base]); + ea = cpu_A0; + } + } else if (a.base >= 0) { + ea = cpu_regs[a.base]; + } + if (TCGV_IS_UNUSED(ea)) { + tcg_gen_movi_tl(cpu_A0, a.disp); + ea = cpu_A0; + } else if (a.disp != 0) { + tcg_gen_addi_tl(cpu_A0, ea, a.disp); + ea = cpu_A0; + } - switch (s->aflag) { - case MO_64: - case MO_32: - base = rm; + return ea; +} - if (base == 4) { - code = cpu_ldub_code(env, s->pc++); - base = (code & 7); - } +static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm) +{ + AddressParts a = gen_lea_modrm_0(env, s, modrm); + TCGv ea = gen_lea_modrm_1(a); + gen_lea_v_seg(s, s->aflag, ea, a.def_seg, s->override); +} - switch (mod) { - case 0: - if (base == 5) { - s->pc += 4; - } - break; - case 1: - s->pc++; - break; - default: - case 2: - s->pc += 4; - break; - } - break; +static void gen_nop_modrm(CPUX86State *env, DisasContext *s, int modrm) +{ + (void)gen_lea_modrm_0(env, s, modrm); +} - case MO_16: - switch (mod) { - case 0: - if (rm == 6) { - s->pc += 2; - } - break; - case 1: - s->pc++; - break; - default: - case 2: - s->pc += 2; - break; - } - break; +/* Used for BNDCL, BNDCU, BNDCN. */ +static void gen_bndck(CPUX86State *env, DisasContext *s, int modrm, + TCGCond cond, TCGv_i64 bndv) +{ + TCGv ea = gen_lea_modrm_1(gen_lea_modrm_0(env, s, modrm)); - default: - tcg_abort(); + tcg_gen_extu_tl_i64(cpu_tmp1_i64, ea); + if (!CODE64(s)) { + tcg_gen_ext32u_i64(cpu_tmp1_i64, cpu_tmp1_i64); } + tcg_gen_setcond_i64(cond, cpu_tmp1_i64, cpu_tmp1_i64, bndv); + tcg_gen_extrl_i64_i32(cpu_tmp2_i32, cpu_tmp1_i64); + gen_helper_bndck(cpu_env, cpu_tmp2_i32); } /* used for LEA and MOV AX, mem */ static void gen_add_A0_ds_seg(DisasContext *s) { - int override, must_add_seg; - must_add_seg = s->addseg; - override = R_DS; - if (s->override >= 0) { - override = s->override; - must_add_seg = 1; - } - if (must_add_seg) { -#ifdef TARGET_X86_64 - if (CODE64(s)) { - gen_op_addq_A0_seg(override); - } else -#endif - { - gen_op_addl_A0_seg(s, override); - } - } + gen_lea_v_seg(s, s->aflag, cpu_A0, R_DS, s->override); } /* generate modrm memory load or store of 'reg'. TMP0 is used if reg == @@ -2145,23 +2029,23 @@ static void gen_ldst_modrm(CPUX86State *env, DisasContext *s, int modrm, if (mod == 3) { if (is_store) { if (reg != OR_TMP0) - gen_op_mov_v_reg(ot, cpu_T[0], reg); - gen_op_mov_reg_v(ot, rm, cpu_T[0]); + gen_op_mov_v_reg(ot, cpu_T0, reg); + gen_op_mov_reg_v(ot, rm, cpu_T0); } else { - gen_op_mov_v_reg(ot, cpu_T[0], rm); + gen_op_mov_v_reg(ot, cpu_T0, rm); if (reg != OR_TMP0) - gen_op_mov_reg_v(ot, reg, cpu_T[0]); + gen_op_mov_reg_v(ot, reg, cpu_T0); } } else { gen_lea_modrm(env, s, modrm); if (is_store) { if (reg != OR_TMP0) - gen_op_mov_v_reg(ot, cpu_T[0], reg); - gen_op_st_v(s, ot, cpu_T[0], cpu_A0); + gen_op_mov_v_reg(ot, cpu_T0, reg); + gen_op_st_v(s, ot, cpu_T0, cpu_A0); } else { - gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); + gen_op_ld_v(s, ot, cpu_T0, cpu_A0); if (reg != OR_TMP0) - gen_op_mov_reg_v(ot, reg, cpu_T[0]); + gen_op_mov_reg_v(ot, reg, cpu_T0); } } } @@ -2258,7 +2142,7 @@ static void gen_cmovcc1(CPUX86State *env, DisasContext *s, TCGMemOp ot, int b, gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - cc = gen_prepare_cc(s, b, cpu_T[1]); + cc = gen_prepare_cc(s, b, cpu_T1); if (cc.mask != -1) { TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, cc.reg, cc.mask); @@ -2268,9 +2152,9 @@ static void gen_cmovcc1(CPUX86State *env, DisasContext *s, TCGMemOp ot, int b, cc.reg2 = tcg_const_tl(cc.imm); } - tcg_gen_movcond_tl(cc.cond, cpu_T[0], cc.reg, cc.reg2, - cpu_T[0], cpu_regs[reg]); - gen_op_mov_reg_v(ot, reg, cpu_T[0]); + tcg_gen_movcond_tl(cc.cond, cpu_T0, cc.reg, cc.reg2, + cpu_T0, cpu_regs[reg]); + gen_op_mov_reg_v(ot, reg, cpu_T0); if (cc.mask != -1) { tcg_temp_free(cc.reg); @@ -2282,29 +2166,24 @@ static void gen_cmovcc1(CPUX86State *env, DisasContext *s, TCGMemOp ot, int b, static inline void gen_op_movl_T0_seg(int seg_reg) { - tcg_gen_ld32u_tl(cpu_T[0], cpu_env, + tcg_gen_ld32u_tl(cpu_T0, cpu_env, offsetof(CPUX86State,segs[seg_reg].selector)); } static inline void gen_op_movl_seg_T0_vm(int seg_reg) { - tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 0xffff); - tcg_gen_st32_tl(cpu_T[0], cpu_env, + tcg_gen_ext16u_tl(cpu_T0, cpu_T0); + tcg_gen_st32_tl(cpu_T0, cpu_env, offsetof(CPUX86State,segs[seg_reg].selector)); - tcg_gen_shli_tl(cpu_T[0], cpu_T[0], 4); - tcg_gen_st_tl(cpu_T[0], cpu_env, - offsetof(CPUX86State,segs[seg_reg].base)); + tcg_gen_shli_tl(cpu_seg_base[seg_reg], cpu_T0, 4); } /* move T0 to seg_reg and compute if the CPU state may change. Never call this function with seg_reg == R_CS */ -static void gen_movl_seg_T0(DisasContext *s, int seg_reg, target_ulong cur_eip) +static void gen_movl_seg_T0(DisasContext *s, int seg_reg) { if (s->pe && !s->vm86) { - /* XXX: optimize by finding processor state dynamically */ - gen_update_cc_op(s); - gen_jmp_im(cur_eip); - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); gen_helper_load_seg(cpu_env, tcg_const_i32(seg_reg), cpu_tmp2_i32); /* abort translation because the addseg value may change or because ss32 may change. For R_SS, translation must always @@ -2345,44 +2224,25 @@ gen_svm_check_intercept(DisasContext *s, target_ulong pc_start, uint64_t type) static inline void gen_stack_update(DisasContext *s, int addend) { -#ifdef TARGET_X86_64 - if (CODE64(s)) { - gen_op_add_reg_im(MO_64, R_ESP, addend); - } else -#endif - if (s->ss32) { - gen_op_add_reg_im(MO_32, R_ESP, addend); - } else { - gen_op_add_reg_im(MO_16, R_ESP, addend); - } + gen_op_add_reg_im(mo_stacksize(s), R_ESP, addend); } /* Generate a push. It depends on ss32, addseg and dflag. */ static void gen_push_v(DisasContext *s, TCGv val) { - TCGMemOp a_ot, d_ot = mo_pushpop(s, s->dflag); + TCGMemOp d_ot = mo_pushpop(s, s->dflag); + TCGMemOp a_ot = mo_stacksize(s); int size = 1 << d_ot; TCGv new_esp = cpu_A0; tcg_gen_subi_tl(cpu_A0, cpu_regs[R_ESP], size); - if (CODE64(s)) { - a_ot = MO_64; - } else if (s->ss32) { - a_ot = MO_32; + if (!CODE64(s)) { if (s->addseg) { new_esp = cpu_tmp4; tcg_gen_mov_tl(new_esp, cpu_A0); - gen_op_addl_A0_seg(s, R_SS); - } else { - tcg_gen_ext32u_tl(cpu_A0, cpu_A0); } - } else { - a_ot = MO_16; - new_esp = cpu_tmp4; - tcg_gen_ext16u_tl(cpu_A0, cpu_A0); - tcg_gen_mov_tl(new_esp, cpu_A0); - gen_op_addl_A0_seg(s, R_SS); + gen_lea_v_seg(s, a_ot, cpu_A0, R_SS, -1); } gen_op_st_v(s, d_ot, val, cpu_A0); @@ -2393,127 +2253,112 @@ static void gen_push_v(DisasContext *s, TCGv val) static TCGMemOp gen_pop_T0(DisasContext *s) { TCGMemOp d_ot = mo_pushpop(s, s->dflag); - TCGv addr = cpu_A0; - if (CODE64(s)) { - addr = cpu_regs[R_ESP]; - } else if (!s->ss32) { - tcg_gen_ext16u_tl(cpu_A0, cpu_regs[R_ESP]); - gen_op_addl_A0_seg(s, R_SS); - } else if (s->addseg) { - tcg_gen_mov_tl(cpu_A0, cpu_regs[R_ESP]); - gen_op_addl_A0_seg(s, R_SS); - } else { - tcg_gen_ext32u_tl(cpu_A0, cpu_regs[R_ESP]); - } + gen_lea_v_seg(s, mo_stacksize(s), cpu_regs[R_ESP], R_SS, -1); + gen_op_ld_v(s, d_ot, cpu_T0, cpu_A0); - gen_op_ld_v(s, d_ot, cpu_T[0], addr); return d_ot; } -static void gen_pop_update(DisasContext *s, TCGMemOp ot) +static inline void gen_pop_update(DisasContext *s, TCGMemOp ot) { gen_stack_update(s, 1 << ot); } -static void gen_stack_A0(DisasContext *s) +static inline void gen_stack_A0(DisasContext *s) { - gen_op_movl_A0_reg(R_ESP); - if (!s->ss32) - tcg_gen_ext16u_tl(cpu_A0, cpu_A0); - tcg_gen_mov_tl(cpu_T[1], cpu_A0); - if (s->addseg) - gen_op_addl_A0_seg(s, R_SS); + gen_lea_v_seg(s, s->ss32 ? MO_32 : MO_16, cpu_regs[R_ESP], R_SS, -1); } -/* NOTE: wrap around in 16 bit not fully handled */ static void gen_pusha(DisasContext *s) { + TCGMemOp s_ot = s->ss32 ? MO_32 : MO_16; + TCGMemOp d_ot = s->dflag; + int size = 1 << d_ot; int i; - gen_op_movl_A0_reg(R_ESP); - gen_op_addl_A0_im(-8 << s->dflag); - if (!s->ss32) - tcg_gen_ext16u_tl(cpu_A0, cpu_A0); - tcg_gen_mov_tl(cpu_T[1], cpu_A0); - if (s->addseg) - gen_op_addl_A0_seg(s, R_SS); - for(i = 0;i < 8; i++) { - gen_op_mov_v_reg(MO_32, cpu_T[0], 7 - i); - gen_op_st_v(s, s->dflag, cpu_T[0], cpu_A0); - gen_op_addl_A0_im(1 << s->dflag); + + for (i = 0; i < 8; i++) { + tcg_gen_addi_tl(cpu_A0, cpu_regs[R_ESP], (i - 8) * size); + gen_lea_v_seg(s, s_ot, cpu_A0, R_SS, -1); + gen_op_st_v(s, d_ot, cpu_regs[7 - i], cpu_A0); } - gen_op_mov_reg_v(MO_16 + s->ss32, R_ESP, cpu_T[1]); + + gen_stack_update(s, -8 * size); } -/* NOTE: wrap around in 16 bit not fully handled */ static void gen_popa(DisasContext *s) { + TCGMemOp s_ot = s->ss32 ? MO_32 : MO_16; + TCGMemOp d_ot = s->dflag; + int size = 1 << d_ot; int i; - gen_op_movl_A0_reg(R_ESP); - if (!s->ss32) - tcg_gen_ext16u_tl(cpu_A0, cpu_A0); - tcg_gen_mov_tl(cpu_T[1], cpu_A0); - tcg_gen_addi_tl(cpu_T[1], cpu_T[1], 8 << s->dflag); - if (s->addseg) - gen_op_addl_A0_seg(s, R_SS); - for(i = 0;i < 8; i++) { + + for (i = 0; i < 8; i++) { /* ESP is not reloaded */ - if (i != 3) { - gen_op_ld_v(s, s->dflag, cpu_T[0], cpu_A0); - gen_op_mov_reg_v(s->dflag, 7 - i, cpu_T[0]); + if (7 - i == R_ESP) { + continue; } - gen_op_addl_A0_im(1 << s->dflag); + tcg_gen_addi_tl(cpu_A0, cpu_regs[R_ESP], i * size); + gen_lea_v_seg(s, s_ot, cpu_A0, R_SS, -1); + gen_op_ld_v(s, d_ot, cpu_T0, cpu_A0); + gen_op_mov_reg_v(d_ot, 7 - i, cpu_T0); } - gen_op_mov_reg_v(MO_16 + s->ss32, R_ESP, cpu_T[1]); + + gen_stack_update(s, 8 * size); } static void gen_enter(DisasContext *s, int esp_addend, int level) { - TCGMemOp ot = mo_pushpop(s, s->dflag); - int opsize = 1 << ot; + TCGMemOp d_ot = mo_pushpop(s, s->dflag); + TCGMemOp a_ot = CODE64(s) ? MO_64 : s->ss32 ? MO_32 : MO_16; + int size = 1 << d_ot; - level &= 0x1f; -#ifdef TARGET_X86_64 - if (CODE64(s)) { - gen_op_movl_A0_reg(R_ESP); - gen_op_addq_A0_im(-opsize); - tcg_gen_mov_tl(cpu_T[1], cpu_A0); - - /* push bp */ - gen_op_mov_v_reg(MO_32, cpu_T[0], R_EBP); - gen_op_st_v(s, ot, cpu_T[0], cpu_A0); - if (level) { - /* XXX: must save state */ - gen_helper_enter64_level(cpu_env, tcg_const_i32(level), - tcg_const_i32((ot == MO_64)), - cpu_T[1]); - } - gen_op_mov_reg_v(ot, R_EBP, cpu_T[1]); - tcg_gen_addi_tl(cpu_T[1], cpu_T[1], -esp_addend + (-opsize * level)); - gen_op_mov_reg_v(MO_64, R_ESP, cpu_T[1]); - } else -#endif - { - gen_op_movl_A0_reg(R_ESP); - gen_op_addl_A0_im(-opsize); - if (!s->ss32) - tcg_gen_ext16u_tl(cpu_A0, cpu_A0); - tcg_gen_mov_tl(cpu_T[1], cpu_A0); - if (s->addseg) - gen_op_addl_A0_seg(s, R_SS); - /* push bp */ - gen_op_mov_v_reg(MO_32, cpu_T[0], R_EBP); - gen_op_st_v(s, ot, cpu_T[0], cpu_A0); - if (level) { - /* XXX: must save state */ - gen_helper_enter_level(cpu_env, tcg_const_i32(level), - tcg_const_i32(s->dflag - 1), - cpu_T[1]); - } - gen_op_mov_reg_v(ot, R_EBP, cpu_T[1]); - tcg_gen_addi_tl(cpu_T[1], cpu_T[1], -esp_addend + (-opsize * level)); - gen_op_mov_reg_v(MO_16 + s->ss32, R_ESP, cpu_T[1]); + /* Push BP; compute FrameTemp into T1. */ + tcg_gen_subi_tl(cpu_T1, cpu_regs[R_ESP], size); + gen_lea_v_seg(s, a_ot, cpu_T1, R_SS, -1); + gen_op_st_v(s, d_ot, cpu_regs[R_EBP], cpu_A0); + + level &= 31; + if (level != 0) { + int i; + + /* Copy level-1 pointers from the previous frame. */ + for (i = 1; i < level; ++i) { + tcg_gen_subi_tl(cpu_A0, cpu_regs[R_EBP], size * i); + gen_lea_v_seg(s, a_ot, cpu_A0, R_SS, -1); + gen_op_ld_v(s, d_ot, cpu_tmp0, cpu_A0); + + tcg_gen_subi_tl(cpu_A0, cpu_T1, size * i); + gen_lea_v_seg(s, a_ot, cpu_A0, R_SS, -1); + gen_op_st_v(s, d_ot, cpu_tmp0, cpu_A0); + } + + /* Push the current FrameTemp as the last level. */ + tcg_gen_subi_tl(cpu_A0, cpu_T1, size * level); + gen_lea_v_seg(s, a_ot, cpu_A0, R_SS, -1); + gen_op_st_v(s, d_ot, cpu_T1, cpu_A0); } + + /* Copy the FrameTemp value to EBP. */ + gen_op_mov_reg_v(a_ot, R_EBP, cpu_T1); + + /* Compute the final value of ESP. */ + tcg_gen_subi_tl(cpu_T1, cpu_T1, esp_addend + size * level); + gen_op_mov_reg_v(a_ot, R_ESP, cpu_T1); +} + +static void gen_leave(DisasContext *s) +{ + TCGMemOp d_ot = mo_pushpop(s, s->dflag); + TCGMemOp a_ot = mo_stacksize(s); + + gen_lea_v_seg(s, a_ot, cpu_regs[R_EBP], R_SS, -1); + gen_op_ld_v(s, d_ot, cpu_T0, cpu_A0); + + tcg_gen_addi_tl(cpu_T1, cpu_regs[R_EBP], 1 << d_ot); + + gen_op_mov_reg_v(d_ot, R_EBP, cpu_T0); + gen_op_mov_reg_v(a_ot, R_ESP, cpu_T1); } static void gen_exception(DisasContext *s, int trapno, target_ulong cur_eip) @@ -2524,6 +2369,30 @@ static void gen_exception(DisasContext *s, int trapno, target_ulong cur_eip) s->is_jmp = DISAS_TB_JUMP; } +/* Generate #UD for the current instruction. The assumption here is that + the instruction is known, but it isn't allowed in the current cpu mode. */ +static void gen_illegal_opcode(DisasContext *s) +{ + gen_exception(s, EXCP06_ILLOP, s->pc_start - s->cs_base); +} + +/* Similarly, except that the assumption here is that we don't decode + the instruction at all -- either a missing opcode, an unimplemented + feature, or just a bogus instruction stream. */ +static void gen_unknown_opcode(CPUX86State *env, DisasContext *s) +{ + gen_illegal_opcode(s); + + if (qemu_loglevel_mask(LOG_UNIMP)) { + target_ulong pc = s->pc_start, end = s->pc; + qemu_log("ILLOPC: " TARGET_FMT_lx ":", pc); + for (; pc < end; ++pc) { + qemu_log(" %02x", cpu_ldub_code(env, pc)); + } + qemu_log("\n"); + } +} + /* an interrupt is different from an exception because of the privilege checks */ static void gen_interrupt(DisasContext *s, int intno, @@ -2544,14 +2413,56 @@ static void gen_debug(DisasContext *s, target_ulong cur_eip) s->is_jmp = DISAS_TB_JUMP; } -/* generate a generic end of block. Trace exception is also generated - if needed */ -static void gen_eob(DisasContext *s) +static void gen_set_hflag(DisasContext *s, uint32_t mask) +{ + if ((s->flags & mask) == 0) { + TCGv_i32 t = tcg_temp_new_i32(); + tcg_gen_ld_i32(t, cpu_env, offsetof(CPUX86State, hflags)); + tcg_gen_ori_i32(t, t, mask); + tcg_gen_st_i32(t, cpu_env, offsetof(CPUX86State, hflags)); + tcg_temp_free_i32(t); + s->flags |= mask; + } +} + +static void gen_reset_hflag(DisasContext *s, uint32_t mask) +{ + if (s->flags & mask) { + TCGv_i32 t = tcg_temp_new_i32(); + tcg_gen_ld_i32(t, cpu_env, offsetof(CPUX86State, hflags)); + tcg_gen_andi_i32(t, t, ~mask); + tcg_gen_st_i32(t, cpu_env, offsetof(CPUX86State, hflags)); + tcg_temp_free_i32(t); + s->flags &= ~mask; + } +} + +/* Clear BND registers during legacy branches. */ +static void gen_bnd_jmp(DisasContext *s) +{ + /* Clear the registers only if BND prefix is missing, MPX is enabled, + and if the BNDREGs are known to be in use (non-zero) already. + The helper itself will check BNDPRESERVE at runtime. */ + if ((s->prefix & PREFIX_REPNZ) == 0 + && (s->flags & HF_MPX_EN_MASK) != 0 + && (s->flags & HF_MPX_IU_MASK) != 0) { + gen_helper_bnd_jmp(cpu_env); + } +} + +/* Generate an end of block. Trace exception is also generated if needed. + If IIM, set HF_INHIBIT_IRQ_MASK if it isn't already set. */ +static void gen_eob_inhibit_irq(DisasContext *s, bool inhibit) { gen_update_cc_op(s); - if (s->tb->flags & HF_INHIBIT_IRQ_MASK) { - gen_helper_reset_inhibit_irq(cpu_env); + + /* If several instructions disable interrupts, only the first does it. */ + if (inhibit && !(s->flags & HF_INHIBIT_IRQ_MASK)) { + gen_set_hflag(s, HF_INHIBIT_IRQ_MASK); + } else { + gen_reset_hflag(s, HF_INHIBIT_IRQ_MASK); } + if (s->tb->flags & HF_RF_MASK) { gen_helper_reset_rf(cpu_env); } @@ -2565,6 +2476,12 @@ static void gen_eob(DisasContext *s) s->is_jmp = DISAS_TB_JUMP; } +/* End of block, resetting the inhibit irq flag. */ +static void gen_eob(DisasContext *s) +{ + gen_eob_inhibit_irq(s, false); +} + /* generate a jump to eip. No segment change must happen before as a direct call to the next block may occur */ static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num) @@ -2601,28 +2518,28 @@ static inline void gen_ldo_env_A0(DisasContext *s, int offset) { int mem_index = s->mem_index; tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_A0, mem_index, MO_LEQ); - tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(XMMReg, XMM_Q(0))); + tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(0))); tcg_gen_addi_tl(cpu_tmp0, cpu_A0, 8); tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_tmp0, mem_index, MO_LEQ); - tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(XMMReg, XMM_Q(1))); + tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(1))); } static inline void gen_sto_env_A0(DisasContext *s, int offset) { int mem_index = s->mem_index; - tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(XMMReg, XMM_Q(0))); + tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(0))); tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_A0, mem_index, MO_LEQ); tcg_gen_addi_tl(cpu_tmp0, cpu_A0, 8); - tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(XMMReg, XMM_Q(1))); + tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(1))); tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_tmp0, mem_index, MO_LEQ); } static inline void gen_op_movo(int d_offset, int s_offset) { - tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, s_offset + offsetof(XMMReg, XMM_Q(0))); - tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, d_offset + offsetof(XMMReg, XMM_Q(0))); - tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, s_offset + offsetof(XMMReg, XMM_Q(1))); - tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, d_offset + offsetof(XMMReg, XMM_Q(1))); + tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, s_offset + offsetof(ZMMReg, ZMM_Q(0))); + tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, d_offset + offsetof(ZMMReg, ZMM_Q(0))); + tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, s_offset + offsetof(ZMMReg, ZMM_Q(1))); + tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, d_offset + offsetof(ZMMReg, ZMM_Q(1))); } static inline void gen_op_movq(int d_offset, int s_offset) @@ -2995,7 +2912,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, b1 = 0; sse_fn_epp = sse_op_table1[b][b1]; if (!sse_fn_epp) { - goto illegal_op; + goto unknown_op; } if ((b <= 0x5f && b >= 0x10) || b == 0xc6 || b == 0xc2) { is_xmm = 1; @@ -3014,15 +2931,19 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, } if (s->flags & HF_EM_MASK) { illegal_op: - gen_exception(s, EXCP06_ILLOP, pc_start - s->cs_base); + gen_illegal_opcode(s); return; } - if (is_xmm && !(s->flags & HF_OSFXSR_MASK)) - if ((b != 0x38 && b != 0x3a) || (s->prefix & PREFIX_DATA)) - goto illegal_op; + if (is_xmm + && !(s->flags & HF_OSFXSR_MASK) + && ((b != 0x38 && b != 0x3a) || (s->prefix & PREFIX_DATA))) { + goto unknown_op; + } if (b == 0x0e) { - if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW)) - goto illegal_op; + if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW)) { + /* If we were fully decoding this we might use illegal_op. */ + goto unknown_op; + } /* femms */ gen_helper_emms(cpu_env); return; @@ -3047,8 +2968,9 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, b |= (b1 << 8); switch(b) { case 0x0e7: /* movntq */ - if (mod == 3) + if (mod == 3) { goto illegal_op; + } gen_lea_modrm(env, s, modrm); gen_stq_env_A0(s, offsetof(CPUX86State, fpregs[reg].mmx)); break; @@ -3073,25 +2995,25 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, gen_lea_modrm(env, s, modrm); if (b1 & 1) { gen_stq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].XMM_Q(0))); + xmm_regs[reg].ZMM_Q(0))); } else { - tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, - xmm_regs[reg].XMM_L(0))); - gen_op_st_v(s, MO_32, cpu_T[0], cpu_A0); + tcg_gen_ld32u_tl(cpu_T0, cpu_env, offsetof(CPUX86State, + xmm_regs[reg].ZMM_L(0))); + gen_op_st_v(s, MO_32, cpu_T0, cpu_A0); } break; case 0x6e: /* movd mm, ea */ #ifdef TARGET_X86_64 if (s->dflag == MO_64) { gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 0); - tcg_gen_st_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,fpregs[reg].mmx)); + tcg_gen_st_tl(cpu_T0, cpu_env, offsetof(CPUX86State,fpregs[reg].mmx)); } else #endif { gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 0); tcg_gen_addi_ptr(cpu_ptr0, cpu_env, offsetof(CPUX86State,fpregs[reg].mmx)); - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); gen_helper_movl_mm_T0_mmx(cpu_ptr0, cpu_tmp2_i32); } break; @@ -3101,14 +3023,14 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 0); tcg_gen_addi_ptr(cpu_ptr0, cpu_env, offsetof(CPUX86State,xmm_regs[reg])); - gen_helper_movq_mm_T0_xmm(cpu_ptr0, cpu_T[0]); + gen_helper_movq_mm_T0_xmm(cpu_ptr0, cpu_T0); } else #endif { gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 0); tcg_gen_addi_ptr(cpu_ptr0, cpu_env, offsetof(CPUX86State,xmm_regs[reg])); - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); gen_helper_movl_mm_T0_xmm(cpu_ptr0, cpu_tmp2_i32); } break; @@ -3142,30 +3064,30 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, case 0x210: /* movss xmm, ea */ if (mod != 3) { gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, MO_32, cpu_T[0], cpu_A0); - tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(0))); - tcg_gen_movi_tl(cpu_T[0], 0); - tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(1))); - tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(2))); - tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(3))); + gen_op_ld_v(s, MO_32, cpu_T0, cpu_A0); + tcg_gen_st32_tl(cpu_T0, cpu_env, offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0))); + tcg_gen_movi_tl(cpu_T0, 0); + tcg_gen_st32_tl(cpu_T0, cpu_env, offsetof(CPUX86State,xmm_regs[reg].ZMM_L(1))); + tcg_gen_st32_tl(cpu_T0, cpu_env, offsetof(CPUX86State,xmm_regs[reg].ZMM_L(2))); + tcg_gen_st32_tl(cpu_T0, cpu_env, offsetof(CPUX86State,xmm_regs[reg].ZMM_L(3))); } else { rm = (modrm & 7) | REX_B(s); - gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)), - offsetof(CPUX86State,xmm_regs[rm].XMM_L(0))); + gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_L(0))); } break; case 0x310: /* movsd xmm, ea */ if (mod != 3) { gen_lea_modrm(env, s, modrm); gen_ldq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].XMM_Q(0))); - tcg_gen_movi_tl(cpu_T[0], 0); - tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(2))); - tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(3))); + xmm_regs[reg].ZMM_Q(0))); + tcg_gen_movi_tl(cpu_T0, 0); + tcg_gen_st32_tl(cpu_T0, cpu_env, offsetof(CPUX86State,xmm_regs[reg].ZMM_L(2))); + tcg_gen_st32_tl(cpu_T0, cpu_env, offsetof(CPUX86State,xmm_regs[reg].ZMM_L(3))); } else { rm = (modrm & 7) | REX_B(s); - gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)), - offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0))); + gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); } break; case 0x012: /* movlps */ @@ -3173,12 +3095,12 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, if (mod != 3) { gen_lea_modrm(env, s, modrm); gen_ldq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].XMM_Q(0))); + xmm_regs[reg].ZMM_Q(0))); } else { /* movhlps */ rm = (modrm & 7) | REX_B(s); - gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)), - offsetof(CPUX86State,xmm_regs[rm].XMM_Q(1))); + gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(1))); } break; case 0x212: /* movsldup */ @@ -3187,40 +3109,40 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); } else { rm = (modrm & 7) | REX_B(s); - gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)), - offsetof(CPUX86State,xmm_regs[rm].XMM_L(0))); - gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)), - offsetof(CPUX86State,xmm_regs[rm].XMM_L(2))); + gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_L(0))); + gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].ZMM_L(2)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_L(2))); } - gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(1)), - offsetof(CPUX86State,xmm_regs[reg].XMM_L(0))); - gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(3)), - offsetof(CPUX86State,xmm_regs[reg].XMM_L(2))); + gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].ZMM_L(1)), + offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0))); + gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].ZMM_L(3)), + offsetof(CPUX86State,xmm_regs[reg].ZMM_L(2))); break; case 0x312: /* movddup */ if (mod != 3) { gen_lea_modrm(env, s, modrm); gen_ldq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].XMM_Q(0))); + xmm_regs[reg].ZMM_Q(0))); } else { rm = (modrm & 7) | REX_B(s); - gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)), - offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0))); + gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); } - gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)), - offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); + gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(1)), + offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0))); break; case 0x016: /* movhps */ case 0x116: /* movhpd */ if (mod != 3) { gen_lea_modrm(env, s, modrm); gen_ldq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].XMM_Q(1))); + xmm_regs[reg].ZMM_Q(1))); } else { /* movlhps */ rm = (modrm & 7) | REX_B(s); - gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)), - offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0))); + gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(1)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); } break; case 0x216: /* movshdup */ @@ -3229,15 +3151,15 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); } else { rm = (modrm & 7) | REX_B(s); - gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(1)), - offsetof(CPUX86State,xmm_regs[rm].XMM_L(1))); - gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(3)), - offsetof(CPUX86State,xmm_regs[rm].XMM_L(3))); + gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].ZMM_L(1)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_L(1))); + gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].ZMM_L(3)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_L(3))); } - gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)), - offsetof(CPUX86State,xmm_regs[reg].XMM_L(1))); - gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)), - offsetof(CPUX86State,xmm_regs[reg].XMM_L(3))); + gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0)), + offsetof(CPUX86State,xmm_regs[reg].ZMM_L(1))); + gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].ZMM_L(2)), + offsetof(CPUX86State,xmm_regs[reg].ZMM_L(3))); break; case 0x178: case 0x378: @@ -3263,13 +3185,13 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, case 0x7e: /* movd ea, mm */ #ifdef TARGET_X86_64 if (s->dflag == MO_64) { - tcg_gen_ld_i64(cpu_T[0], cpu_env, + tcg_gen_ld_i64(cpu_T0, cpu_env, offsetof(CPUX86State,fpregs[reg].mmx)); gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 1); } else #endif { - tcg_gen_ld32u_tl(cpu_T[0], cpu_env, + tcg_gen_ld32u_tl(cpu_T0, cpu_env, offsetof(CPUX86State,fpregs[reg].mmx.MMX_L(0))); gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 1); } @@ -3277,14 +3199,14 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, case 0x17e: /* movd ea, xmm */ #ifdef TARGET_X86_64 if (s->dflag == MO_64) { - tcg_gen_ld_i64(cpu_T[0], cpu_env, - offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); + tcg_gen_ld_i64(cpu_T0, cpu_env, + offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0))); gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 1); } else #endif { - tcg_gen_ld32u_tl(cpu_T[0], cpu_env, - offsetof(CPUX86State,xmm_regs[reg].XMM_L(0))); + tcg_gen_ld32u_tl(cpu_T0, cpu_env, + offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0))); gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 1); } break; @@ -3292,13 +3214,13 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, if (mod != 3) { gen_lea_modrm(env, s, modrm); gen_ldq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].XMM_Q(0))); + xmm_regs[reg].ZMM_Q(0))); } else { rm = (modrm & 7) | REX_B(s); - gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)), - offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0))); + gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); } - gen_op_movq_env_0(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1))); + gen_op_movq_env_0(offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(1))); break; case 0x7f: /* movq ea, mm */ if (mod != 3) { @@ -3328,23 +3250,23 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, case 0x211: /* movss ea, xmm */ if (mod != 3) { gen_lea_modrm(env, s, modrm); - tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(0))); - gen_op_st_v(s, MO_32, cpu_T[0], cpu_A0); + tcg_gen_ld32u_tl(cpu_T0, cpu_env, offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0))); + gen_op_st_v(s, MO_32, cpu_T0, cpu_A0); } else { rm = (modrm & 7) | REX_B(s); - gen_op_movl(offsetof(CPUX86State,xmm_regs[rm].XMM_L(0)), - offsetof(CPUX86State,xmm_regs[reg].XMM_L(0))); + gen_op_movl(offsetof(CPUX86State,xmm_regs[rm].ZMM_L(0)), + offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0))); } break; case 0x311: /* movsd ea, xmm */ if (mod != 3) { gen_lea_modrm(env, s, modrm); gen_stq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].XMM_Q(0))); + xmm_regs[reg].ZMM_Q(0))); } else { rm = (modrm & 7) | REX_B(s); - gen_op_movq(offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)), - offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); + gen_op_movq(offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0)), + offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0))); } break; case 0x013: /* movlps */ @@ -3352,7 +3274,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, if (mod != 3) { gen_lea_modrm(env, s, modrm); gen_stq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].XMM_Q(0))); + xmm_regs[reg].ZMM_Q(0))); } else { goto illegal_op; } @@ -3362,7 +3284,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, if (mod != 3) { gen_lea_modrm(env, s, modrm); gen_stq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].XMM_Q(1))); + xmm_regs[reg].ZMM_Q(1))); } else { goto illegal_op; } @@ -3374,26 +3296,26 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, case 0x172: case 0x173: if (b1 >= 2) { - goto illegal_op; + goto unknown_op; } val = cpu_ldub_code(env, s->pc++); if (is_xmm) { - tcg_gen_movi_tl(cpu_T[0], val); - tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(0))); - tcg_gen_movi_tl(cpu_T[0], 0); - tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(1))); + tcg_gen_movi_tl(cpu_T0, val); + tcg_gen_st32_tl(cpu_T0, cpu_env, offsetof(CPUX86State,xmm_t0.ZMM_L(0))); + tcg_gen_movi_tl(cpu_T0, 0); + tcg_gen_st32_tl(cpu_T0, cpu_env, offsetof(CPUX86State,xmm_t0.ZMM_L(1))); op1_offset = offsetof(CPUX86State,xmm_t0); } else { - tcg_gen_movi_tl(cpu_T[0], val); - tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,mmx_t0.MMX_L(0))); - tcg_gen_movi_tl(cpu_T[0], 0); - tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,mmx_t0.MMX_L(1))); + tcg_gen_movi_tl(cpu_T0, val); + tcg_gen_st32_tl(cpu_T0, cpu_env, offsetof(CPUX86State,mmx_t0.MMX_L(0))); + tcg_gen_movi_tl(cpu_T0, 0); + tcg_gen_st32_tl(cpu_T0, cpu_env, offsetof(CPUX86State,mmx_t0.MMX_L(1))); op1_offset = offsetof(CPUX86State,mmx_t0); } sse_fn_epp = sse_op_table2[((b - 1) & 3) * 8 + (((modrm >> 3)) & 7)][b1]; if (!sse_fn_epp) { - goto illegal_op; + goto unknown_op; } if (is_xmm) { rm = (modrm & 7) | REX_B(s); @@ -3452,12 +3374,12 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); if (ot == MO_32) { SSEFunc_0_epi sse_fn_epi = sse_op_table3ai[(b >> 8) & 1]; - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); sse_fn_epi(cpu_env, cpu_ptr0, cpu_tmp2_i32); } else { #ifdef TARGET_X86_64 SSEFunc_0_epl sse_fn_epl = sse_op_table3aq[(b >> 8) & 1]; - sse_fn_epl(cpu_env, cpu_ptr0, cpu_T[0]); + sse_fn_epl(cpu_env, cpu_ptr0, cpu_T0); #else goto illegal_op; #endif @@ -3502,10 +3424,10 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, if (mod != 3) { gen_lea_modrm(env, s, modrm); if ((b >> 8) & 1) { - gen_ldq_env_A0(s, offsetof(CPUX86State, xmm_t0.XMM_Q(0))); + gen_ldq_env_A0(s, offsetof(CPUX86State, xmm_t0.ZMM_Q(0))); } else { - gen_op_ld_v(s, MO_32, cpu_T[0], cpu_A0); - tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(0))); + gen_op_ld_v(s, MO_32, cpu_T0, cpu_A0); + tcg_gen_st32_tl(cpu_T0, cpu_env, offsetof(CPUX86State,xmm_t0.ZMM_L(0))); } op2_offset = offsetof(CPUX86State,xmm_t0); } else { @@ -3517,17 +3439,17 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, SSEFunc_i_ep sse_fn_i_ep = sse_op_table3bi[((b >> 7) & 2) | (b & 1)]; sse_fn_i_ep(cpu_tmp2_i32, cpu_env, cpu_ptr0); - tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32); + tcg_gen_extu_i32_tl(cpu_T0, cpu_tmp2_i32); } else { #ifdef TARGET_X86_64 SSEFunc_l_ep sse_fn_l_ep = sse_op_table3bq[((b >> 7) & 2) | (b & 1)]; - sse_fn_l_ep(cpu_T[0], cpu_env, cpu_ptr0); + sse_fn_l_ep(cpu_T0, cpu_env, cpu_ptr0); #else goto illegal_op; #endif } - gen_op_mov_reg_v(ot, reg, cpu_T[0]); + gen_op_mov_reg_v(ot, reg, cpu_T0); break; case 0xc4: /* pinsrw */ case 0x1c4: @@ -3536,11 +3458,11 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, val = cpu_ldub_code(env, s->pc++); if (b1) { val &= 7; - tcg_gen_st16_tl(cpu_T[0], cpu_env, - offsetof(CPUX86State,xmm_regs[reg].XMM_W(val))); + tcg_gen_st16_tl(cpu_T0, cpu_env, + offsetof(CPUX86State,xmm_regs[reg].ZMM_W(val))); } else { val &= 3; - tcg_gen_st16_tl(cpu_T[0], cpu_env, + tcg_gen_st16_tl(cpu_T0, cpu_env, offsetof(CPUX86State,fpregs[reg].mmx.MMX_W(val))); } break; @@ -3553,41 +3475,41 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, if (b1) { val &= 7; rm = (modrm & 7) | REX_B(s); - tcg_gen_ld16u_tl(cpu_T[0], cpu_env, - offsetof(CPUX86State,xmm_regs[rm].XMM_W(val))); + tcg_gen_ld16u_tl(cpu_T0, cpu_env, + offsetof(CPUX86State,xmm_regs[rm].ZMM_W(val))); } else { val &= 3; rm = (modrm & 7); - tcg_gen_ld16u_tl(cpu_T[0], cpu_env, + tcg_gen_ld16u_tl(cpu_T0, cpu_env, offsetof(CPUX86State,fpregs[rm].mmx.MMX_W(val))); } reg = ((modrm >> 3) & 7) | rex_r; - gen_op_mov_reg_v(ot, reg, cpu_T[0]); + gen_op_mov_reg_v(ot, reg, cpu_T0); break; case 0x1d6: /* movq ea, xmm */ if (mod != 3) { gen_lea_modrm(env, s, modrm); gen_stq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].XMM_Q(0))); + xmm_regs[reg].ZMM_Q(0))); } else { rm = (modrm & 7) | REX_B(s); - gen_op_movq(offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)), - offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); - gen_op_movq_env_0(offsetof(CPUX86State,xmm_regs[rm].XMM_Q(1))); + gen_op_movq(offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0)), + offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0))); + gen_op_movq_env_0(offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(1))); } break; case 0x2d6: /* movq2dq */ gen_helper_enter_mmx(cpu_env); rm = (modrm & 7); - gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)), + gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0)), offsetof(CPUX86State,fpregs[rm].mmx)); - gen_op_movq_env_0(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1))); + gen_op_movq_env_0(offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(1))); break; case 0x3d6: /* movdq2q */ gen_helper_enter_mmx(cpu_env); rm = (modrm & 7) | REX_B(s); gen_op_movq(offsetof(CPUX86State,fpregs[reg & 7].mmx), - offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0))); + offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); break; case 0xd7: /* pmovmskb */ case 0x1d7: @@ -3617,12 +3539,12 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, reg = ((modrm >> 3) & 7) | rex_r; mod = (modrm >> 6) & 3; if (b1 >= 2) { - goto illegal_op; + goto unknown_op; } sse_fn_epp = sse_op_table6[b].op[b1]; if (!sse_fn_epp) { - goto illegal_op; + goto unknown_op; } if (!(s->cpuid_ext_features & sse_op_table6[b].ext_mask)) goto illegal_op; @@ -3639,20 +3561,20 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, case 0x23: case 0x33: /* pmovsxwd, pmovzxwd */ case 0x25: case 0x35: /* pmovsxdq, pmovzxdq */ gen_ldq_env_A0(s, op2_offset + - offsetof(XMMReg, XMM_Q(0))); + offsetof(ZMMReg, ZMM_Q(0))); break; case 0x21: case 0x31: /* pmovsxbd, pmovzxbd */ case 0x24: case 0x34: /* pmovsxwq, pmovzxwq */ tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, s->mem_index, MO_LEUL); tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, op2_offset + - offsetof(XMMReg, XMM_L(0))); + offsetof(ZMMReg, ZMM_L(0))); break; case 0x22: case 0x32: /* pmovsxbq, pmovzxbq */ tcg_gen_qemu_ld_tl(cpu_tmp0, cpu_A0, s->mem_index, MO_LEUW); tcg_gen_st16_tl(cpu_tmp0, cpu_env, op2_offset + - offsetof(XMMReg, XMM_W(0))); + offsetof(ZMMReg, ZMM_W(0))); break; case 0x2a: /* movntqda */ gen_ldo_env_A0(s, op1_offset); @@ -3672,7 +3594,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, } } if (sse_fn_epp == SSE_SPECIAL) { - goto illegal_op; + goto unknown_op; } tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); @@ -3709,11 +3631,11 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_regs[reg]); gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_helper_crc32(cpu_T[0], cpu_tmp2_i32, - cpu_T[0], tcg_const_i32(8 << ot)); + gen_helper_crc32(cpu_T0, cpu_tmp2_i32, + cpu_T0, tcg_const_i32(8 << ot)); ot = mo_64_32(s->dflag); - gen_op_mov_reg_v(ot, reg, cpu_T[0]); + gen_op_mov_reg_v(ot, reg, cpu_T0); break; case 0x1f0: /* crc32 or movbe */ @@ -3738,9 +3660,9 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, gen_lea_modrm(env, s, modrm); if ((b & 1) == 0) { - tcg_gen_qemu_ld_tl(cpu_T[0], cpu_A0, + tcg_gen_qemu_ld_tl(cpu_T0, cpu_A0, s->mem_index, ot | MO_BE); - gen_op_mov_reg_v(ot, reg, cpu_T[0]); + gen_op_mov_reg_v(ot, reg, cpu_T0); } else { tcg_gen_qemu_st_tl(cpu_regs[reg], cpu_A0, s->mem_index, ot | MO_BE); @@ -3755,8 +3677,8 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, } ot = mo_64_32(s->dflag); gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - tcg_gen_andc_tl(cpu_T[0], cpu_regs[s->vex_v], cpu_T[0]); - gen_op_mov_reg_v(ot, reg, cpu_T[0]); + tcg_gen_andc_tl(cpu_T0, cpu_regs[s->vex_v], cpu_T0); + gen_op_mov_reg_v(ot, reg, cpu_T0); gen_op_update1_cc(); set_cc_op(s, CC_OP_LOGICB + ot); break; @@ -3775,12 +3697,12 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, /* Extract START, and shift the operand. Shifts larger than operand size get zeros. */ tcg_gen_ext8u_tl(cpu_A0, cpu_regs[s->vex_v]); - tcg_gen_shr_tl(cpu_T[0], cpu_T[0], cpu_A0); + tcg_gen_shr_tl(cpu_T0, cpu_T0, cpu_A0); bound = tcg_const_tl(ot == MO_64 ? 63 : 31); zero = tcg_const_tl(0); - tcg_gen_movcond_tl(TCG_COND_LEU, cpu_T[0], cpu_A0, bound, - cpu_T[0], zero); + tcg_gen_movcond_tl(TCG_COND_LEU, cpu_T0, cpu_A0, bound, + cpu_T0, zero); tcg_temp_free(zero); /* Extract the LEN into a mask. Lengths larger than @@ -3790,12 +3712,12 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, tcg_gen_movcond_tl(TCG_COND_LEU, cpu_A0, cpu_A0, bound, cpu_A0, bound); tcg_temp_free(bound); - tcg_gen_movi_tl(cpu_T[1], 1); - tcg_gen_shl_tl(cpu_T[1], cpu_T[1], cpu_A0); - tcg_gen_subi_tl(cpu_T[1], cpu_T[1], 1); - tcg_gen_and_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + tcg_gen_movi_tl(cpu_T1, 1); + tcg_gen_shl_tl(cpu_T1, cpu_T1, cpu_A0); + tcg_gen_subi_tl(cpu_T1, cpu_T1, 1); + tcg_gen_and_tl(cpu_T0, cpu_T0, cpu_T1); - gen_op_mov_reg_v(ot, reg, cpu_T[0]); + gen_op_mov_reg_v(ot, reg, cpu_T0); gen_op_update1_cc(); set_cc_op(s, CC_OP_LOGICB + ot); } @@ -3809,21 +3731,21 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, } ot = mo_64_32(s->dflag); gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - tcg_gen_ext8u_tl(cpu_T[1], cpu_regs[s->vex_v]); + tcg_gen_ext8u_tl(cpu_T1, cpu_regs[s->vex_v]); { TCGv bound = tcg_const_tl(ot == MO_64 ? 63 : 31); /* Note that since we're using BMILG (in order to get O cleared) we need to store the inverse into C. */ tcg_gen_setcond_tl(TCG_COND_LT, cpu_cc_src, - cpu_T[1], bound); - tcg_gen_movcond_tl(TCG_COND_GT, cpu_T[1], cpu_T[1], - bound, bound, cpu_T[1]); + cpu_T1, bound); + tcg_gen_movcond_tl(TCG_COND_GT, cpu_T1, cpu_T1, + bound, bound, cpu_T1); tcg_temp_free(bound); } tcg_gen_movi_tl(cpu_A0, -1); - tcg_gen_shl_tl(cpu_A0, cpu_A0, cpu_T[1]); - tcg_gen_andc_tl(cpu_T[0], cpu_T[0], cpu_A0); - gen_op_mov_reg_v(ot, reg, cpu_T[0]); + tcg_gen_shl_tl(cpu_A0, cpu_A0, cpu_T1); + tcg_gen_andc_tl(cpu_T0, cpu_T0, cpu_A0); + gen_op_mov_reg_v(ot, reg, cpu_T0); gen_op_update1_cc(); set_cc_op(s, CC_OP_BMILGB + ot); break; @@ -3838,7 +3760,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); switch (ot) { default: - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_regs[R_EDX]); tcg_gen_mulu2_i32(cpu_tmp2_i32, cpu_tmp3_i32, cpu_tmp2_i32, cpu_tmp3_i32); @@ -3847,8 +3769,10 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, break; #ifdef TARGET_X86_64 case MO_64: - tcg_gen_mulu2_i64(cpu_regs[s->vex_v], cpu_regs[reg], - cpu_T[0], cpu_regs[R_EDX]); + tcg_gen_mulu2_i64(cpu_T0, cpu_T1, + cpu_T0, cpu_regs[R_EDX]); + tcg_gen_mov_i64(cpu_regs[s->vex_v], cpu_T0); + tcg_gen_mov_i64(cpu_regs[reg], cpu_T1); break; #endif } @@ -3865,11 +3789,11 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, /* Note that by zero-extending the mask operand, we automatically handle zero-extending the result. */ if (ot == MO_64) { - tcg_gen_mov_tl(cpu_T[1], cpu_regs[s->vex_v]); + tcg_gen_mov_tl(cpu_T1, cpu_regs[s->vex_v]); } else { - tcg_gen_ext32u_tl(cpu_T[1], cpu_regs[s->vex_v]); + tcg_gen_ext32u_tl(cpu_T1, cpu_regs[s->vex_v]); } - gen_helper_pdep(cpu_regs[reg], cpu_T[0], cpu_T[1]); + gen_helper_pdep(cpu_regs[reg], cpu_T0, cpu_T1); break; case 0x2f5: /* pext Gy, By, Ey */ @@ -3883,11 +3807,11 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, /* Note that by zero-extending the mask operand, we automatically handle zero-extending the result. */ if (ot == MO_64) { - tcg_gen_mov_tl(cpu_T[1], cpu_regs[s->vex_v]); + tcg_gen_mov_tl(cpu_T1, cpu_regs[s->vex_v]); } else { - tcg_gen_ext32u_tl(cpu_T[1], cpu_regs[s->vex_v]); + tcg_gen_ext32u_tl(cpu_T1, cpu_regs[s->vex_v]); } - gen_helper_pext(cpu_regs[reg], cpu_T[0], cpu_T[1]); + gen_helper_pext(cpu_regs[reg], cpu_T0, cpu_T1); break; case 0x1f6: /* adcx Gy, Ey */ @@ -3946,22 +3870,22 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, /* If we know TL is 64-bit, and we want a 32-bit result, just do everything in 64-bit arithmetic. */ tcg_gen_ext32u_i64(cpu_regs[reg], cpu_regs[reg]); - tcg_gen_ext32u_i64(cpu_T[0], cpu_T[0]); - tcg_gen_add_i64(cpu_T[0], cpu_T[0], cpu_regs[reg]); - tcg_gen_add_i64(cpu_T[0], cpu_T[0], carry_in); - tcg_gen_ext32u_i64(cpu_regs[reg], cpu_T[0]); - tcg_gen_shri_i64(carry_out, cpu_T[0], 32); + tcg_gen_ext32u_i64(cpu_T0, cpu_T0); + tcg_gen_add_i64(cpu_T0, cpu_T0, cpu_regs[reg]); + tcg_gen_add_i64(cpu_T0, cpu_T0, carry_in); + tcg_gen_ext32u_i64(cpu_regs[reg], cpu_T0); + tcg_gen_shri_i64(carry_out, cpu_T0, 32); break; #endif default: /* Otherwise compute the carry-out in two steps. */ zero = tcg_const_tl(0); - tcg_gen_add2_tl(cpu_T[0], carry_out, - cpu_T[0], zero, + tcg_gen_add2_tl(cpu_T0, carry_out, + cpu_T0, zero, carry_in, zero); tcg_gen_add2_tl(cpu_regs[reg], carry_out, cpu_regs[reg], carry_out, - cpu_T[0], zero); + cpu_T0, zero); tcg_temp_free(zero); break; } @@ -3980,24 +3904,24 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, ot = mo_64_32(s->dflag); gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); if (ot == MO_64) { - tcg_gen_andi_tl(cpu_T[1], cpu_regs[s->vex_v], 63); + tcg_gen_andi_tl(cpu_T1, cpu_regs[s->vex_v], 63); } else { - tcg_gen_andi_tl(cpu_T[1], cpu_regs[s->vex_v], 31); + tcg_gen_andi_tl(cpu_T1, cpu_regs[s->vex_v], 31); } if (b == 0x1f7) { - tcg_gen_shl_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + tcg_gen_shl_tl(cpu_T0, cpu_T0, cpu_T1); } else if (b == 0x2f7) { if (ot != MO_64) { - tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]); + tcg_gen_ext32s_tl(cpu_T0, cpu_T0); } - tcg_gen_sar_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + tcg_gen_sar_tl(cpu_T0, cpu_T0, cpu_T1); } else { if (ot != MO_64) { - tcg_gen_ext32u_tl(cpu_T[0], cpu_T[0]); + tcg_gen_ext32u_tl(cpu_T0, cpu_T0); } - tcg_gen_shr_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + tcg_gen_shr_tl(cpu_T0, cpu_T0, cpu_T1); } - gen_op_mov_reg_v(ot, reg, cpu_T[0]); + gen_op_mov_reg_v(ot, reg, cpu_T0); break; case 0x0f3: @@ -4014,36 +3938,36 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, switch (reg & 7) { case 1: /* blsr By,Ey */ - tcg_gen_neg_tl(cpu_T[1], cpu_T[0]); - tcg_gen_and_tl(cpu_T[0], cpu_T[0], cpu_T[1]); - gen_op_mov_reg_v(ot, s->vex_v, cpu_T[0]); + tcg_gen_neg_tl(cpu_T1, cpu_T0); + tcg_gen_and_tl(cpu_T0, cpu_T0, cpu_T1); + gen_op_mov_reg_v(ot, s->vex_v, cpu_T0); gen_op_update2_cc(); set_cc_op(s, CC_OP_BMILGB + ot); break; case 2: /* blsmsk By,Ey */ - tcg_gen_mov_tl(cpu_cc_src, cpu_T[0]); - tcg_gen_subi_tl(cpu_T[0], cpu_T[0], 1); - tcg_gen_xor_tl(cpu_T[0], cpu_T[0], cpu_cc_src); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); + tcg_gen_mov_tl(cpu_cc_src, cpu_T0); + tcg_gen_subi_tl(cpu_T0, cpu_T0, 1); + tcg_gen_xor_tl(cpu_T0, cpu_T0, cpu_cc_src); + tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); set_cc_op(s, CC_OP_BMILGB + ot); break; case 3: /* blsi By, Ey */ - tcg_gen_mov_tl(cpu_cc_src, cpu_T[0]); - tcg_gen_subi_tl(cpu_T[0], cpu_T[0], 1); - tcg_gen_and_tl(cpu_T[0], cpu_T[0], cpu_cc_src); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); + tcg_gen_mov_tl(cpu_cc_src, cpu_T0); + tcg_gen_subi_tl(cpu_T0, cpu_T0, 1); + tcg_gen_and_tl(cpu_T0, cpu_T0, cpu_cc_src); + tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); set_cc_op(s, CC_OP_BMILGB + ot); break; default: - goto illegal_op; + goto unknown_op; } break; default: - goto illegal_op; + goto unknown_op; } break; @@ -4055,12 +3979,12 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, reg = ((modrm >> 3) & 7) | rex_r; mod = (modrm >> 6) & 3; if (b1 >= 2) { - goto illegal_op; + goto unknown_op; } sse_fn_eppi = sse_op_table7[b].op[b1]; if (!sse_fn_eppi) { - goto illegal_op; + goto unknown_op; } if (!(s->cpuid_ext_features & sse_op_table7[b].ext_mask)) goto illegal_op; @@ -4074,22 +3998,22 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, val = cpu_ldub_code(env, s->pc++); switch (b) { case 0x14: /* pextrb */ - tcg_gen_ld8u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, - xmm_regs[reg].XMM_B(val & 15))); + tcg_gen_ld8u_tl(cpu_T0, cpu_env, offsetof(CPUX86State, + xmm_regs[reg].ZMM_B(val & 15))); if (mod == 3) { - gen_op_mov_reg_v(ot, rm, cpu_T[0]); + gen_op_mov_reg_v(ot, rm, cpu_T0); } else { - tcg_gen_qemu_st_tl(cpu_T[0], cpu_A0, + tcg_gen_qemu_st_tl(cpu_T0, cpu_A0, s->mem_index, MO_UB); } break; case 0x15: /* pextrw */ - tcg_gen_ld16u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, - xmm_regs[reg].XMM_W(val & 7))); + tcg_gen_ld16u_tl(cpu_T0, cpu_env, offsetof(CPUX86State, + xmm_regs[reg].ZMM_W(val & 7))); if (mod == 3) { - gen_op_mov_reg_v(ot, rm, cpu_T[0]); + gen_op_mov_reg_v(ot, rm, cpu_T0); } else { - tcg_gen_qemu_st_tl(cpu_T[0], cpu_A0, + tcg_gen_qemu_st_tl(cpu_T0, cpu_A0, s->mem_index, MO_LEUW); } break; @@ -4097,7 +4021,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, if (ot == MO_32) { /* pextrd */ tcg_gen_ld_i32(cpu_tmp2_i32, cpu_env, offsetof(CPUX86State, - xmm_regs[reg].XMM_L(val & 3))); + xmm_regs[reg].ZMM_L(val & 3))); if (mod == 3) { tcg_gen_extu_i32_tl(cpu_regs[rm], cpu_tmp2_i32); } else { @@ -4108,7 +4032,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, #ifdef TARGET_X86_64 tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, offsetof(CPUX86State, - xmm_regs[reg].XMM_Q(val & 1))); + xmm_regs[reg].ZMM_Q(val & 1))); if (mod == 3) { tcg_gen_mov_i64(cpu_regs[rm], cpu_tmp1_i64); } else { @@ -4121,53 +4045,53 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, } break; case 0x17: /* extractps */ - tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, - xmm_regs[reg].XMM_L(val & 3))); + tcg_gen_ld32u_tl(cpu_T0, cpu_env, offsetof(CPUX86State, + xmm_regs[reg].ZMM_L(val & 3))); if (mod == 3) { - gen_op_mov_reg_v(ot, rm, cpu_T[0]); + gen_op_mov_reg_v(ot, rm, cpu_T0); } else { - tcg_gen_qemu_st_tl(cpu_T[0], cpu_A0, + tcg_gen_qemu_st_tl(cpu_T0, cpu_A0, s->mem_index, MO_LEUL); } break; case 0x20: /* pinsrb */ if (mod == 3) { - gen_op_mov_v_reg(MO_32, cpu_T[0], rm); + gen_op_mov_v_reg(MO_32, cpu_T0, rm); } else { - tcg_gen_qemu_ld_tl(cpu_T[0], cpu_A0, + tcg_gen_qemu_ld_tl(cpu_T0, cpu_A0, s->mem_index, MO_UB); } - tcg_gen_st8_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, - xmm_regs[reg].XMM_B(val & 15))); + tcg_gen_st8_tl(cpu_T0, cpu_env, offsetof(CPUX86State, + xmm_regs[reg].ZMM_B(val & 15))); break; case 0x21: /* insertps */ if (mod == 3) { tcg_gen_ld_i32(cpu_tmp2_i32, cpu_env, offsetof(CPUX86State,xmm_regs[rm] - .XMM_L((val >> 6) & 3))); + .ZMM_L((val >> 6) & 3))); } else { tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, s->mem_index, MO_LEUL); } tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, offsetof(CPUX86State,xmm_regs[reg] - .XMM_L((val >> 4) & 3))); + .ZMM_L((val >> 4) & 3))); if ((val >> 0) & 1) tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), cpu_env, offsetof(CPUX86State, - xmm_regs[reg].XMM_L(0))); + xmm_regs[reg].ZMM_L(0))); if ((val >> 1) & 1) tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), cpu_env, offsetof(CPUX86State, - xmm_regs[reg].XMM_L(1))); + xmm_regs[reg].ZMM_L(1))); if ((val >> 2) & 1) tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), cpu_env, offsetof(CPUX86State, - xmm_regs[reg].XMM_L(2))); + xmm_regs[reg].ZMM_L(2))); if ((val >> 3) & 1) tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), cpu_env, offsetof(CPUX86State, - xmm_regs[reg].XMM_L(3))); + xmm_regs[reg].ZMM_L(3))); break; case 0x22: if (ot == MO_32) { /* pinsrd */ @@ -4179,7 +4103,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, } tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, offsetof(CPUX86State, - xmm_regs[reg].XMM_L(val & 3))); + xmm_regs[reg].ZMM_L(val & 3))); } else { /* pinsrq */ #ifdef TARGET_X86_64 if (mod == 3) { @@ -4190,7 +4114,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, } tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, offsetof(CPUX86State, - xmm_regs[reg].XMM_Q(val & 1))); + xmm_regs[reg].ZMM_Q(val & 1))); #else goto illegal_op; #endif @@ -4252,22 +4176,24 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); b = cpu_ldub_code(env, s->pc++); if (ot == MO_64) { - tcg_gen_rotri_tl(cpu_T[0], cpu_T[0], b & 63); + tcg_gen_rotri_tl(cpu_T0, cpu_T0, b & 63); } else { - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); tcg_gen_rotri_i32(cpu_tmp2_i32, cpu_tmp2_i32, b & 31); - tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32); + tcg_gen_extu_i32_tl(cpu_T0, cpu_tmp2_i32); } - gen_op_mov_reg_v(ot, reg, cpu_T[0]); + gen_op_mov_reg_v(ot, reg, cpu_T0); break; default: - goto illegal_op; + goto unknown_op; } break; default: - goto illegal_op; + unknown_op: + gen_unknown_opcode(env, s); + return; } } else { /* generic MMX or SSE operation */ @@ -4313,13 +4239,13 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, switch (sz) { case 2: /* 32 bit access */ - gen_op_ld_v(s, MO_32, cpu_T[0], cpu_A0); - tcg_gen_st32_tl(cpu_T[0], cpu_env, - offsetof(CPUX86State,xmm_t0.XMM_L(0))); + gen_op_ld_v(s, MO_32, cpu_T0, cpu_A0); + tcg_gen_st32_tl(cpu_T0, cpu_env, + offsetof(CPUX86State,xmm_t0.ZMM_L(0))); break; case 3: /* 64 bit access */ - gen_ldq_env_A0(s, offsetof(CPUX86State, xmm_t0.XMM_D(0))); + gen_ldq_env_A0(s, offsetof(CPUX86State, xmm_t0.ZMM_D(0))); break; default: /* 128 bit access */ @@ -4343,11 +4269,12 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, } switch(b) { case 0x0f: /* 3DNow! data insns */ - if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW)) - goto illegal_op; val = cpu_ldub_code(env, s->pc++); sse_fn_epp = sse_op_table5[val]; if (!sse_fn_epp) { + goto unknown_op; + } + if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW)) { goto illegal_op; } tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); @@ -4367,7 +4294,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, /* compare insns */ val = cpu_ldub_code(env, s->pc++); if (val >= 8) - goto illegal_op; + goto unknown_op; sse_fn_epp = sse_op_table4[val][b1]; tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); @@ -4412,10 +4339,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, target_ulong next_eip, tval; int rex_w, rex_r; - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { - tcg_gen_debug_insn_start(pc_start); - } - s->pc = pc_start; + s->pc_start = s->pc = pc_start; prefixes = 0; s->override = -1; rex_w = -1; @@ -4528,7 +4452,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, b = 0x13a; break; default: /* Reserved for future use. */ - goto illegal_op; + goto unknown_op; } } s->vex_v = (~vex3 >> 3) & 0xf; @@ -4608,13 +4532,13 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, xor_zero: /* xor reg, reg optimisation */ set_cc_op(s, CC_OP_CLR); - tcg_gen_movi_tl(cpu_T[0], 0); - gen_op_mov_reg_v(ot, reg, cpu_T[0]); + tcg_gen_movi_tl(cpu_T0, 0); + gen_op_mov_reg_v(ot, reg, cpu_T0); break; } else { opreg = rm; } - gen_op_mov_v_reg(ot, cpu_T[1], reg); + gen_op_mov_v_reg(ot, cpu_T1, reg); gen_op(s, op, ot, opreg); break; case 1: /* OP Gv, Ev */ @@ -4624,17 +4548,17 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, rm = (modrm & 7) | REX_B(s); if (mod != 3) { gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, cpu_T[1], cpu_A0); + gen_op_ld_v(s, ot, cpu_T1, cpu_A0); } else if (op == OP_XORL && rm == reg) { goto xor_zero; } else { - gen_op_mov_v_reg(ot, cpu_T[1], rm); + gen_op_mov_v_reg(ot, cpu_T1, rm); } gen_op(s, op, ot, reg); break; case 2: /* OP A, Iv */ val = insn_get(env, s, ot); - tcg_gen_movi_tl(cpu_T[1], val); + tcg_gen_movi_tl(cpu_T1, val); gen_op(s, op, ot, OR_EAX); break; } @@ -4679,7 +4603,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, val = (int8_t)insn_get(env, s, MO_8); break; } - tcg_gen_movi_tl(cpu_T[1], val); + tcg_gen_movi_tl(cpu_T1, val); gen_op(s, op, ot, opreg); } break; @@ -4706,32 +4630,32 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (op == 0) s->rip_offset = insn_const_size(ot); gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); + gen_op_ld_v(s, ot, cpu_T0, cpu_A0); } else { - gen_op_mov_v_reg(ot, cpu_T[0], rm); + gen_op_mov_v_reg(ot, cpu_T0, rm); } switch(op) { case 0: /* test */ val = insn_get(env, s, ot); - tcg_gen_movi_tl(cpu_T[1], val); + tcg_gen_movi_tl(cpu_T1, val); gen_op_testl_T0_T1_cc(); set_cc_op(s, CC_OP_LOGICB + ot); break; case 2: /* not */ - tcg_gen_not_tl(cpu_T[0], cpu_T[0]); + tcg_gen_not_tl(cpu_T0, cpu_T0); if (mod != 3) { - gen_op_st_v(s, ot, cpu_T[0], cpu_A0); + gen_op_st_v(s, ot, cpu_T0, cpu_A0); } else { - gen_op_mov_reg_v(ot, rm, cpu_T[0]); + gen_op_mov_reg_v(ot, rm, cpu_T0); } break; case 3: /* neg */ - tcg_gen_neg_tl(cpu_T[0], cpu_T[0]); + tcg_gen_neg_tl(cpu_T0, cpu_T0); if (mod != 3) { - gen_op_st_v(s, ot, cpu_T[0], cpu_A0); + gen_op_st_v(s, ot, cpu_T0, cpu_A0); } else { - gen_op_mov_reg_v(ot, rm, cpu_T[0]); + gen_op_mov_reg_v(ot, rm, cpu_T0); } gen_op_update_neg_cc(); set_cc_op(s, CC_OP_SUBB + ot); @@ -4739,32 +4663,32 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 4: /* mul */ switch(ot) { case MO_8: - gen_op_mov_v_reg(MO_8, cpu_T[1], R_EAX); - tcg_gen_ext8u_tl(cpu_T[0], cpu_T[0]); - tcg_gen_ext8u_tl(cpu_T[1], cpu_T[1]); + gen_op_mov_v_reg(MO_8, cpu_T1, R_EAX); + tcg_gen_ext8u_tl(cpu_T0, cpu_T0); + tcg_gen_ext8u_tl(cpu_T1, cpu_T1); /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]); - gen_op_mov_reg_v(MO_16, R_EAX, cpu_T[0]); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); - tcg_gen_andi_tl(cpu_cc_src, cpu_T[0], 0xff00); + tcg_gen_mul_tl(cpu_T0, cpu_T0, cpu_T1); + gen_op_mov_reg_v(MO_16, R_EAX, cpu_T0); + tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); + tcg_gen_andi_tl(cpu_cc_src, cpu_T0, 0xff00); set_cc_op(s, CC_OP_MULB); break; case MO_16: - gen_op_mov_v_reg(MO_16, cpu_T[1], R_EAX); - tcg_gen_ext16u_tl(cpu_T[0], cpu_T[0]); - tcg_gen_ext16u_tl(cpu_T[1], cpu_T[1]); + gen_op_mov_v_reg(MO_16, cpu_T1, R_EAX); + tcg_gen_ext16u_tl(cpu_T0, cpu_T0); + tcg_gen_ext16u_tl(cpu_T1, cpu_T1); /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]); - gen_op_mov_reg_v(MO_16, R_EAX, cpu_T[0]); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); - tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 16); - gen_op_mov_reg_v(MO_16, R_EDX, cpu_T[0]); - tcg_gen_mov_tl(cpu_cc_src, cpu_T[0]); + tcg_gen_mul_tl(cpu_T0, cpu_T0, cpu_T1); + gen_op_mov_reg_v(MO_16, R_EAX, cpu_T0); + tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); + tcg_gen_shri_tl(cpu_T0, cpu_T0, 16); + gen_op_mov_reg_v(MO_16, R_EDX, cpu_T0); + tcg_gen_mov_tl(cpu_cc_src, cpu_T0); set_cc_op(s, CC_OP_MULW); break; default: case MO_32: - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_regs[R_EAX]); tcg_gen_mulu2_i32(cpu_tmp2_i32, cpu_tmp3_i32, cpu_tmp2_i32, cpu_tmp3_i32); @@ -4777,7 +4701,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, #ifdef TARGET_X86_64 case MO_64: tcg_gen_mulu2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX], - cpu_T[0], cpu_regs[R_EAX]); + cpu_T0, cpu_regs[R_EAX]); tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); tcg_gen_mov_tl(cpu_cc_src, cpu_regs[R_EDX]); set_cc_op(s, CC_OP_MULQ); @@ -4788,34 +4712,34 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 5: /* imul */ switch(ot) { case MO_8: - gen_op_mov_v_reg(MO_8, cpu_T[1], R_EAX); - tcg_gen_ext8s_tl(cpu_T[0], cpu_T[0]); - tcg_gen_ext8s_tl(cpu_T[1], cpu_T[1]); + gen_op_mov_v_reg(MO_8, cpu_T1, R_EAX); + tcg_gen_ext8s_tl(cpu_T0, cpu_T0); + tcg_gen_ext8s_tl(cpu_T1, cpu_T1); /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]); - gen_op_mov_reg_v(MO_16, R_EAX, cpu_T[0]); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); - tcg_gen_ext8s_tl(cpu_tmp0, cpu_T[0]); - tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0); + tcg_gen_mul_tl(cpu_T0, cpu_T0, cpu_T1); + gen_op_mov_reg_v(MO_16, R_EAX, cpu_T0); + tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); + tcg_gen_ext8s_tl(cpu_tmp0, cpu_T0); + tcg_gen_sub_tl(cpu_cc_src, cpu_T0, cpu_tmp0); set_cc_op(s, CC_OP_MULB); break; case MO_16: - gen_op_mov_v_reg(MO_16, cpu_T[1], R_EAX); - tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]); - tcg_gen_ext16s_tl(cpu_T[1], cpu_T[1]); + gen_op_mov_v_reg(MO_16, cpu_T1, R_EAX); + tcg_gen_ext16s_tl(cpu_T0, cpu_T0); + tcg_gen_ext16s_tl(cpu_T1, cpu_T1); /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]); - gen_op_mov_reg_v(MO_16, R_EAX, cpu_T[0]); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); - tcg_gen_ext16s_tl(cpu_tmp0, cpu_T[0]); - tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0); - tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 16); - gen_op_mov_reg_v(MO_16, R_EDX, cpu_T[0]); + tcg_gen_mul_tl(cpu_T0, cpu_T0, cpu_T1); + gen_op_mov_reg_v(MO_16, R_EAX, cpu_T0); + tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); + tcg_gen_ext16s_tl(cpu_tmp0, cpu_T0); + tcg_gen_sub_tl(cpu_cc_src, cpu_T0, cpu_tmp0); + tcg_gen_shri_tl(cpu_T0, cpu_T0, 16); + gen_op_mov_reg_v(MO_16, R_EDX, cpu_T0); set_cc_op(s, CC_OP_MULW); break; default: case MO_32: - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_regs[R_EAX]); tcg_gen_muls2_i32(cpu_tmp2_i32, cpu_tmp3_i32, cpu_tmp2_i32, cpu_tmp3_i32); @@ -4830,7 +4754,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, #ifdef TARGET_X86_64 case MO_64: tcg_gen_muls2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX], - cpu_T[0], cpu_regs[R_EAX]); + cpu_T0, cpu_regs[R_EAX]); tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); tcg_gen_sari_tl(cpu_cc_src, cpu_regs[R_EAX], 63); tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, cpu_regs[R_EDX]); @@ -4842,22 +4766,18 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 6: /* div */ switch(ot) { case MO_8: - gen_jmp_im(pc_start - s->cs_base); - gen_helper_divb_AL(cpu_env, cpu_T[0]); + gen_helper_divb_AL(cpu_env, cpu_T0); break; case MO_16: - gen_jmp_im(pc_start - s->cs_base); - gen_helper_divw_AX(cpu_env, cpu_T[0]); + gen_helper_divw_AX(cpu_env, cpu_T0); break; default: case MO_32: - gen_jmp_im(pc_start - s->cs_base); - gen_helper_divl_EAX(cpu_env, cpu_T[0]); + gen_helper_divl_EAX(cpu_env, cpu_T0); break; #ifdef TARGET_X86_64 case MO_64: - gen_jmp_im(pc_start - s->cs_base); - gen_helper_divq_EAX(cpu_env, cpu_T[0]); + gen_helper_divq_EAX(cpu_env, cpu_T0); break; #endif } @@ -4865,28 +4785,24 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 7: /* idiv */ switch(ot) { case MO_8: - gen_jmp_im(pc_start - s->cs_base); - gen_helper_idivb_AL(cpu_env, cpu_T[0]); + gen_helper_idivb_AL(cpu_env, cpu_T0); break; case MO_16: - gen_jmp_im(pc_start - s->cs_base); - gen_helper_idivw_AX(cpu_env, cpu_T[0]); + gen_helper_idivw_AX(cpu_env, cpu_T0); break; default: case MO_32: - gen_jmp_im(pc_start - s->cs_base); - gen_helper_idivl_EAX(cpu_env, cpu_T[0]); + gen_helper_idivl_EAX(cpu_env, cpu_T0); break; #ifdef TARGET_X86_64 case MO_64: - gen_jmp_im(pc_start - s->cs_base); - gen_helper_idivq_EAX(cpu_env, cpu_T[0]); + gen_helper_idivq_EAX(cpu_env, cpu_T0); break; #endif } break; default: - goto illegal_op; + goto unknown_op; } break; @@ -4899,7 +4815,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, rm = (modrm & 7) | REX_B(s); op = (modrm >> 3) & 7; if (op >= 2 && b == 0xfe) { - goto illegal_op; + goto unknown_op; } if (CODE64(s)) { if (op == 2 || op == 4) { @@ -4915,9 +4831,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (mod != 3) { gen_lea_modrm(env, s, modrm); if (op >= 2 && op != 3 && op != 5) - gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); + gen_op_ld_v(s, ot, cpu_T0, cpu_A0); } else { - gen_op_mov_v_reg(ot, cpu_T[0], rm); + gen_op_mov_v_reg(ot, cpu_T0, rm); } switch(op) { @@ -4938,29 +4854,28 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 2: /* call Ev */ /* XXX: optimize if memory (no 'and' is necessary) */ if (dflag == MO_16) { - tcg_gen_ext16u_tl(cpu_T[0], cpu_T[0]); + tcg_gen_ext16u_tl(cpu_T0, cpu_T0); } next_eip = s->pc - s->cs_base; - tcg_gen_movi_tl(cpu_T[1], next_eip); - gen_push_v(s, cpu_T[1]); - gen_op_jmp_v(cpu_T[0]); + tcg_gen_movi_tl(cpu_T1, next_eip); + gen_push_v(s, cpu_T1); + gen_op_jmp_v(cpu_T0); + gen_bnd_jmp(s); gen_eob(s); break; case 3: /* lcall Ev */ - gen_op_ld_v(s, ot, cpu_T[1], cpu_A0); + gen_op_ld_v(s, ot, cpu_T1, cpu_A0); gen_add_A0_im(s, 1 << ot); - gen_op_ld_v(s, MO_16, cpu_T[0], cpu_A0); + gen_op_ld_v(s, MO_16, cpu_T0, cpu_A0); do_lcall: if (s->pe && !s->vm86) { - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); - gen_helper_lcall_protected(cpu_env, cpu_tmp2_i32, cpu_T[1], + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); + gen_helper_lcall_protected(cpu_env, cpu_tmp2_i32, cpu_T1, tcg_const_i32(dflag - 1), - tcg_const_i32(s->pc - pc_start)); + tcg_const_tl(s->pc - s->cs_base)); } else { - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); - gen_helper_lcall_real(cpu_env, cpu_tmp2_i32, cpu_T[1], + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); + gen_helper_lcall_real(cpu_env, cpu_tmp2_i32, cpu_T1, tcg_const_i32(dflag - 1), tcg_const_i32(s->pc - s->cs_base)); } @@ -4968,33 +4883,32 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, break; case 4: /* jmp Ev */ if (dflag == MO_16) { - tcg_gen_ext16u_tl(cpu_T[0], cpu_T[0]); + tcg_gen_ext16u_tl(cpu_T0, cpu_T0); } - gen_op_jmp_v(cpu_T[0]); + gen_op_jmp_v(cpu_T0); + gen_bnd_jmp(s); gen_eob(s); break; case 5: /* ljmp Ev */ - gen_op_ld_v(s, ot, cpu_T[1], cpu_A0); + gen_op_ld_v(s, ot, cpu_T1, cpu_A0); gen_add_A0_im(s, 1 << ot); - gen_op_ld_v(s, MO_16, cpu_T[0], cpu_A0); + gen_op_ld_v(s, MO_16, cpu_T0, cpu_A0); do_ljmp: if (s->pe && !s->vm86) { - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); - gen_helper_ljmp_protected(cpu_env, cpu_tmp2_i32, cpu_T[1], - tcg_const_i32(s->pc - pc_start)); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); + gen_helper_ljmp_protected(cpu_env, cpu_tmp2_i32, cpu_T1, + tcg_const_tl(s->pc - s->cs_base)); } else { gen_op_movl_seg_T0_vm(R_CS); - gen_op_jmp_v(cpu_T[1]); + gen_op_jmp_v(cpu_T1); } gen_eob(s); break; case 6: /* push Ev */ - gen_push_v(s, cpu_T[0]); + gen_push_v(s, cpu_T0); break; default: - goto illegal_op; + goto unknown_op; } break; @@ -5006,7 +4920,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, reg = ((modrm >> 3) & 7) | rex_r; gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_op_mov_v_reg(ot, cpu_T[1], reg); + gen_op_mov_v_reg(ot, cpu_T1, reg); gen_op_testl_T0_T1_cc(); set_cc_op(s, CC_OP_LOGICB + ot); break; @@ -5016,8 +4930,8 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, ot = mo_b_d(b, dflag); val = insn_get(env, s, ot); - gen_op_mov_v_reg(ot, cpu_T[0], OR_EAX); - tcg_gen_movi_tl(cpu_T[1], val); + gen_op_mov_v_reg(ot, cpu_T0, OR_EAX); + tcg_gen_movi_tl(cpu_T1, val); gen_op_testl_T0_T1_cc(); set_cc_op(s, CC_OP_LOGICB + ot); break; @@ -5026,20 +4940,20 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, switch (dflag) { #ifdef TARGET_X86_64 case MO_64: - gen_op_mov_v_reg(MO_32, cpu_T[0], R_EAX); - tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]); - gen_op_mov_reg_v(MO_64, R_EAX, cpu_T[0]); + gen_op_mov_v_reg(MO_32, cpu_T0, R_EAX); + tcg_gen_ext32s_tl(cpu_T0, cpu_T0); + gen_op_mov_reg_v(MO_64, R_EAX, cpu_T0); break; #endif case MO_32: - gen_op_mov_v_reg(MO_16, cpu_T[0], R_EAX); - tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]); - gen_op_mov_reg_v(MO_32, R_EAX, cpu_T[0]); + gen_op_mov_v_reg(MO_16, cpu_T0, R_EAX); + tcg_gen_ext16s_tl(cpu_T0, cpu_T0); + gen_op_mov_reg_v(MO_32, R_EAX, cpu_T0); break; case MO_16: - gen_op_mov_v_reg(MO_8, cpu_T[0], R_EAX); - tcg_gen_ext8s_tl(cpu_T[0], cpu_T[0]); - gen_op_mov_reg_v(MO_16, R_EAX, cpu_T[0]); + gen_op_mov_v_reg(MO_8, cpu_T0, R_EAX); + tcg_gen_ext8s_tl(cpu_T0, cpu_T0); + gen_op_mov_reg_v(MO_16, R_EAX, cpu_T0); break; default: tcg_abort(); @@ -5049,22 +4963,22 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, switch (dflag) { #ifdef TARGET_X86_64 case MO_64: - gen_op_mov_v_reg(MO_64, cpu_T[0], R_EAX); - tcg_gen_sari_tl(cpu_T[0], cpu_T[0], 63); - gen_op_mov_reg_v(MO_64, R_EDX, cpu_T[0]); + gen_op_mov_v_reg(MO_64, cpu_T0, R_EAX); + tcg_gen_sari_tl(cpu_T0, cpu_T0, 63); + gen_op_mov_reg_v(MO_64, R_EDX, cpu_T0); break; #endif case MO_32: - gen_op_mov_v_reg(MO_32, cpu_T[0], R_EAX); - tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]); - tcg_gen_sari_tl(cpu_T[0], cpu_T[0], 31); - gen_op_mov_reg_v(MO_32, R_EDX, cpu_T[0]); + gen_op_mov_v_reg(MO_32, cpu_T0, R_EAX); + tcg_gen_ext32s_tl(cpu_T0, cpu_T0); + tcg_gen_sari_tl(cpu_T0, cpu_T0, 31); + gen_op_mov_reg_v(MO_32, R_EDX, cpu_T0); break; case MO_16: - gen_op_mov_v_reg(MO_16, cpu_T[0], R_EAX); - tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]); - tcg_gen_sari_tl(cpu_T[0], cpu_T[0], 15); - gen_op_mov_reg_v(MO_16, R_EDX, cpu_T[0]); + gen_op_mov_v_reg(MO_16, cpu_T0, R_EAX); + tcg_gen_ext16s_tl(cpu_T0, cpu_T0); + tcg_gen_sari_tl(cpu_T0, cpu_T0, 15); + gen_op_mov_reg_v(MO_16, R_EDX, cpu_T0); break; default: tcg_abort(); @@ -5083,25 +4997,25 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); if (b == 0x69) { val = insn_get(env, s, ot); - tcg_gen_movi_tl(cpu_T[1], val); + tcg_gen_movi_tl(cpu_T1, val); } else if (b == 0x6b) { val = (int8_t)insn_get(env, s, MO_8); - tcg_gen_movi_tl(cpu_T[1], val); + tcg_gen_movi_tl(cpu_T1, val); } else { - gen_op_mov_v_reg(ot, cpu_T[1], reg); + gen_op_mov_v_reg(ot, cpu_T1, reg); } switch (ot) { #ifdef TARGET_X86_64 case MO_64: - tcg_gen_muls2_i64(cpu_regs[reg], cpu_T[1], cpu_T[0], cpu_T[1]); + tcg_gen_muls2_i64(cpu_regs[reg], cpu_T1, cpu_T0, cpu_T1); tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[reg]); tcg_gen_sari_tl(cpu_cc_src, cpu_cc_dst, 63); - tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, cpu_T[1]); + tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, cpu_T1); break; #endif case MO_32: - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); - tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); + tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T1); tcg_gen_muls2_i32(cpu_tmp2_i32, cpu_tmp3_i32, cpu_tmp2_i32, cpu_tmp3_i32); tcg_gen_extu_i32_tl(cpu_regs[reg], cpu_tmp2_i32); @@ -5111,14 +5025,14 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, tcg_gen_extu_i32_tl(cpu_cc_src, cpu_tmp2_i32); break; default: - tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]); - tcg_gen_ext16s_tl(cpu_T[1], cpu_T[1]); + tcg_gen_ext16s_tl(cpu_T0, cpu_T0); + tcg_gen_ext16s_tl(cpu_T1, cpu_T1); /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); - tcg_gen_ext16s_tl(cpu_tmp0, cpu_T[0]); - tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0); - gen_op_mov_reg_v(ot, reg, cpu_T[0]); + tcg_gen_mul_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); + tcg_gen_ext16s_tl(cpu_tmp0, cpu_T0); + tcg_gen_sub_tl(cpu_cc_src, cpu_T0, cpu_tmp0); + gen_op_mov_reg_v(ot, reg, cpu_T0); break; } set_cc_op(s, CC_OP_MULB + ot); @@ -5131,18 +5045,18 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, mod = (modrm >> 6) & 3; if (mod == 3) { rm = (modrm & 7) | REX_B(s); - gen_op_mov_v_reg(ot, cpu_T[0], reg); - gen_op_mov_v_reg(ot, cpu_T[1], rm); - tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_T[1]); - gen_op_mov_reg_v(ot, reg, cpu_T[1]); - gen_op_mov_reg_v(ot, rm, cpu_T[0]); + gen_op_mov_v_reg(ot, cpu_T0, reg); + gen_op_mov_v_reg(ot, cpu_T1, rm); + tcg_gen_add_tl(cpu_T0, cpu_T0, cpu_T1); + gen_op_mov_reg_v(ot, reg, cpu_T1); + gen_op_mov_reg_v(ot, rm, cpu_T0); } else { gen_lea_modrm(env, s, modrm); - gen_op_mov_v_reg(ot, cpu_T[0], reg); - gen_op_ld_v(s, ot, cpu_T[1], cpu_A0); - tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_T[1]); - gen_op_st_v(s, ot, cpu_T[0], cpu_A0); - gen_op_mov_reg_v(ot, reg, cpu_T[1]); + gen_op_mov_v_reg(ot, cpu_T0, reg); + gen_op_ld_v(s, ot, cpu_T1, cpu_A0); + tcg_gen_add_tl(cpu_T0, cpu_T0, cpu_T1); + gen_op_st_v(s, ot, cpu_T0, cpu_A0); + gen_op_mov_reg_v(ot, reg, cpu_T1); } gen_op_update2_cc(); set_cc_op(s, CC_OP_ADDB + ot); @@ -5212,8 +5126,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (dflag == MO_64) { if (!(s->cpuid_ext_features & CPUID_EXT_CX16)) goto illegal_op; - gen_jmp_im(pc_start - s->cs_base); - gen_update_cc_op(s); gen_lea_modrm(env, s, modrm); gen_helper_cmpxchg16b(cpu_env, cpu_A0); } else @@ -5221,8 +5133,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, { if (!(s->cpuid_features & CPUID_CX8)) goto illegal_op; - gen_jmp_im(pc_start - s->cs_base); - gen_update_cc_op(s); gen_lea_modrm(env, s, modrm); gen_helper_cmpxchg8b(cpu_env, cpu_A0); } @@ -5232,14 +5142,14 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, /**************************/ /* push/pop */ case 0x50 ... 0x57: /* push */ - gen_op_mov_v_reg(MO_32, cpu_T[0], (b & 7) | REX_B(s)); - gen_push_v(s, cpu_T[0]); + gen_op_mov_v_reg(MO_32, cpu_T0, (b & 7) | REX_B(s)); + gen_push_v(s, cpu_T0); break; case 0x58 ... 0x5f: /* pop */ ot = gen_pop_T0(s); /* NOTE: order is important for pop %sp */ gen_pop_update(s, ot); - gen_op_mov_reg_v(ot, (b & 7) | REX_B(s), cpu_T[0]); + gen_op_mov_reg_v(ot, (b & 7) | REX_B(s), cpu_T0); break; case 0x60: /* pusha */ if (CODE64(s)) @@ -5258,8 +5168,8 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, val = insn_get(env, s, ot); else val = (int8_t)insn_get(env, s, MO_8); - tcg_gen_movi_tl(cpu_T[0], val); - gen_push_v(s, cpu_T[0]); + tcg_gen_movi_tl(cpu_T0, val); + gen_push_v(s, cpu_T0); break; case 0x8f: /* pop Ev */ modrm = cpu_ldub_code(env, s->pc++); @@ -5269,7 +5179,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, /* NOTE: order is important for pop %sp */ gen_pop_update(s, ot); rm = (modrm & 7) | REX_B(s); - gen_op_mov_reg_v(ot, rm, cpu_T[0]); + gen_op_mov_reg_v(ot, rm, cpu_T0); } else { /* NOTE: order is important too for MMU exceptions */ s->popl_esp_hack = 1 << ot; @@ -5288,20 +5198,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } break; case 0xc9: /* leave */ - /* XXX: exception not precise (ESP is updated before potential exception) */ - if (CODE64(s)) { - gen_op_mov_v_reg(MO_64, cpu_T[0], R_EBP); - gen_op_mov_reg_v(MO_64, R_ESP, cpu_T[0]); - } else if (s->ss32) { - gen_op_mov_v_reg(MO_32, cpu_T[0], R_EBP); - gen_op_mov_reg_v(MO_32, R_ESP, cpu_T[0]); - } else { - gen_op_mov_v_reg(MO_16, cpu_T[0], R_EBP); - gen_op_mov_reg_v(MO_16, R_ESP, cpu_T[0]); - } - ot = gen_pop_T0(s); - gen_op_mov_reg_v(ot, R_EBP, cpu_T[0]); - gen_pop_update(s, ot); + gen_leave(s); break; case 0x06: /* push es */ case 0x0e: /* push cs */ @@ -5310,12 +5207,12 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (CODE64(s)) goto illegal_op; gen_op_movl_T0_seg(b >> 3); - gen_push_v(s, cpu_T[0]); + gen_push_v(s, cpu_T0); break; case 0x1a0: /* push fs */ case 0x1a8: /* push gs */ gen_op_movl_T0_seg((b >> 3) & 7); - gen_push_v(s, cpu_T[0]); + gen_push_v(s, cpu_T0); break; case 0x07: /* pop es */ case 0x17: /* pop ss */ @@ -5324,25 +5221,23 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, goto illegal_op; reg = b >> 3; ot = gen_pop_T0(s); - gen_movl_seg_T0(s, reg, pc_start - s->cs_base); + gen_movl_seg_T0(s, reg); gen_pop_update(s, ot); - if (reg == R_SS) { - /* if reg == SS, inhibit interrupts/trace. */ - /* If several instructions disable interrupts, only the - _first_ does it */ - if (!(s->tb->flags & HF_INHIBIT_IRQ_MASK)) - gen_helper_set_inhibit_irq(cpu_env); - s->tf = 0; - } + /* Note that reg == R_SS in gen_movl_seg_T0 always sets is_jmp. */ if (s->is_jmp) { gen_jmp_im(s->pc - s->cs_base); - gen_eob(s); + if (reg == R_SS) { + s->tf = 0; + gen_eob_inhibit_irq(s, true); + } else { + gen_eob(s); + } } break; case 0x1a1: /* pop fs */ case 0x1a9: /* pop gs */ ot = gen_pop_T0(s); - gen_movl_seg_T0(s, (b >> 3) & 7, pc_start - s->cs_base); + gen_movl_seg_T0(s, (b >> 3) & 7); gen_pop_update(s, ot); if (s->is_jmp) { gen_jmp_im(s->pc - s->cs_base); @@ -5371,11 +5266,11 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_lea_modrm(env, s, modrm); } val = insn_get(env, s, ot); - tcg_gen_movi_tl(cpu_T[0], val); + tcg_gen_movi_tl(cpu_T0, val); if (mod != 3) { - gen_op_st_v(s, ot, cpu_T[0], cpu_A0); + gen_op_st_v(s, ot, cpu_T0, cpu_A0); } else { - gen_op_mov_reg_v(ot, (modrm & 7) | REX_B(s), cpu_T[0]); + gen_op_mov_reg_v(ot, (modrm & 7) | REX_B(s), cpu_T0); } break; case 0x8a: @@ -5385,7 +5280,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, reg = ((modrm >> 3) & 7) | rex_r; gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_op_mov_reg_v(ot, reg, cpu_T[0]); + gen_op_mov_reg_v(ot, reg, cpu_T0); break; case 0x8e: /* mov seg, Gv */ modrm = cpu_ldub_code(env, s->pc++); @@ -5393,18 +5288,16 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (reg >= 6 || reg == R_CS) goto illegal_op; gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - gen_movl_seg_T0(s, reg, pc_start - s->cs_base); - if (reg == R_SS) { - /* if reg == SS, inhibit interrupts/trace */ - /* If several instructions disable interrupts, only the - _first_ does it */ - if (!(s->tb->flags & HF_INHIBIT_IRQ_MASK)) - gen_helper_set_inhibit_irq(cpu_env); - s->tf = 0; - } + gen_movl_seg_T0(s, reg); + /* Note that reg == R_SS in gen_movl_seg_T0 always sets is_jmp. */ if (s->is_jmp) { gen_jmp_im(s->pc - s->cs_base); - gen_eob(s); + if (reg == R_SS) { + s->tf = 0; + gen_eob_inhibit_irq(s, true); + } else { + gen_eob(s); + } } break; case 0x8c: /* mov Gv, seg */ @@ -5439,45 +5332,42 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, rm = (modrm & 7) | REX_B(s); if (mod == 3) { - gen_op_mov_v_reg(ot, cpu_T[0], rm); + gen_op_mov_v_reg(ot, cpu_T0, rm); switch (s_ot) { case MO_UB: - tcg_gen_ext8u_tl(cpu_T[0], cpu_T[0]); + tcg_gen_ext8u_tl(cpu_T0, cpu_T0); break; case MO_SB: - tcg_gen_ext8s_tl(cpu_T[0], cpu_T[0]); + tcg_gen_ext8s_tl(cpu_T0, cpu_T0); break; case MO_UW: - tcg_gen_ext16u_tl(cpu_T[0], cpu_T[0]); + tcg_gen_ext16u_tl(cpu_T0, cpu_T0); break; default: case MO_SW: - tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]); + tcg_gen_ext16s_tl(cpu_T0, cpu_T0); break; } - gen_op_mov_reg_v(d_ot, reg, cpu_T[0]); + gen_op_mov_reg_v(d_ot, reg, cpu_T0); } else { gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, s_ot, cpu_T[0], cpu_A0); - gen_op_mov_reg_v(d_ot, reg, cpu_T[0]); + gen_op_ld_v(s, s_ot, cpu_T0, cpu_A0); + gen_op_mov_reg_v(d_ot, reg, cpu_T0); } } break; case 0x8d: /* lea */ - ot = dflag; modrm = cpu_ldub_code(env, s->pc++); mod = (modrm >> 6) & 3; if (mod == 3) goto illegal_op; reg = ((modrm >> 3) & 7) | rex_r; - /* we must ensure that no segment is added */ - s->override = -1; - val = s->addseg; - s->addseg = 0; - gen_lea_modrm(env, s, modrm); - s->addseg = val; - gen_op_mov_reg_v(ot, reg, cpu_A0); + { + AddressParts a = gen_lea_modrm_0(env, s, modrm); + TCGv ea = gen_lea_modrm_1(a); + gen_op_mov_reg_v(dflag, reg, ea); + } break; case 0xa0: /* mov EAX, Ov */ @@ -5502,27 +5392,27 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, tcg_gen_movi_tl(cpu_A0, offset_addr); gen_add_A0_ds_seg(s); if ((b & 2) == 0) { - gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); - gen_op_mov_reg_v(ot, R_EAX, cpu_T[0]); + gen_op_ld_v(s, ot, cpu_T0, cpu_A0); + gen_op_mov_reg_v(ot, R_EAX, cpu_T0); } else { - gen_op_mov_v_reg(ot, cpu_T[0], R_EAX); - gen_op_st_v(s, ot, cpu_T[0], cpu_A0); + gen_op_mov_v_reg(ot, cpu_T0, R_EAX); + gen_op_st_v(s, ot, cpu_T0, cpu_A0); } } break; case 0xd7: /* xlat */ tcg_gen_mov_tl(cpu_A0, cpu_regs[R_EBX]); - tcg_gen_ext8u_tl(cpu_T[0], cpu_regs[R_EAX]); - tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_T[0]); + tcg_gen_ext8u_tl(cpu_T0, cpu_regs[R_EAX]); + tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_T0); gen_extu(s->aflag, cpu_A0); gen_add_A0_ds_seg(s); - gen_op_ld_v(s, MO_8, cpu_T[0], cpu_A0); - gen_op_mov_reg_v(MO_8, R_EAX, cpu_T[0]); + gen_op_ld_v(s, MO_8, cpu_T0, cpu_A0); + gen_op_mov_reg_v(MO_8, R_EAX, cpu_T0); break; case 0xb0 ... 0xb7: /* mov R, Ib */ val = insn_get(env, s, MO_8); - tcg_gen_movi_tl(cpu_T[0], val); - gen_op_mov_reg_v(MO_8, (b & 7) | REX_B(s), cpu_T[0]); + tcg_gen_movi_tl(cpu_T0, val); + gen_op_mov_reg_v(MO_8, (b & 7) | REX_B(s), cpu_T0); break; case 0xb8 ... 0xbf: /* mov R, Iv */ #ifdef TARGET_X86_64 @@ -5532,16 +5422,16 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, tmp = cpu_ldq_code(env, s->pc); s->pc += 8; reg = (b & 7) | REX_B(s); - tcg_gen_movi_tl(cpu_T[0], tmp); - gen_op_mov_reg_v(MO_64, reg, cpu_T[0]); + tcg_gen_movi_tl(cpu_T0, tmp); + gen_op_mov_reg_v(MO_64, reg, cpu_T0); } else #endif { ot = dflag; val = insn_get(env, s, ot); reg = (b & 7) | REX_B(s); - tcg_gen_movi_tl(cpu_T[0], val); - gen_op_mov_reg_v(ot, reg, cpu_T[0]); + tcg_gen_movi_tl(cpu_T0, val); + gen_op_mov_reg_v(ot, reg, cpu_T0); } break; @@ -5560,21 +5450,21 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (mod == 3) { rm = (modrm & 7) | REX_B(s); do_xchg_reg: - gen_op_mov_v_reg(ot, cpu_T[0], reg); - gen_op_mov_v_reg(ot, cpu_T[1], rm); - gen_op_mov_reg_v(ot, rm, cpu_T[0]); - gen_op_mov_reg_v(ot, reg, cpu_T[1]); + gen_op_mov_v_reg(ot, cpu_T0, reg); + gen_op_mov_v_reg(ot, cpu_T1, rm); + gen_op_mov_reg_v(ot, rm, cpu_T0); + gen_op_mov_reg_v(ot, reg, cpu_T1); } else { gen_lea_modrm(env, s, modrm); - gen_op_mov_v_reg(ot, cpu_T[0], reg); + gen_op_mov_v_reg(ot, cpu_T0, reg); /* for xchg, lock is implicit */ if (!(prefixes & PREFIX_LOCK)) gen_helper_lock(); - gen_op_ld_v(s, ot, cpu_T[1], cpu_A0); - gen_op_st_v(s, ot, cpu_T[0], cpu_A0); + gen_op_ld_v(s, ot, cpu_T1, cpu_A0); + gen_op_st_v(s, ot, cpu_T0, cpu_A0); if (!(prefixes & PREFIX_LOCK)) gen_helper_unlock(); - gen_op_mov_reg_v(ot, reg, cpu_T[1]); + gen_op_mov_reg_v(ot, reg, cpu_T1); } break; case 0xc4: /* les Gv */ @@ -5601,13 +5491,13 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (mod == 3) goto illegal_op; gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, cpu_T[1], cpu_A0); + gen_op_ld_v(s, ot, cpu_T1, cpu_A0); gen_add_A0_im(s, 1 << ot); /* load the segment first to handle exceptions properly */ - gen_op_ld_v(s, MO_16, cpu_T[0], cpu_A0); - gen_movl_seg_T0(s, op, pc_start - s->cs_base); + gen_op_ld_v(s, MO_16, cpu_T0, cpu_A0); + gen_movl_seg_T0(s, op); /* then put the data */ - gen_op_mov_reg_v(ot, reg, cpu_T[1]); + gen_op_mov_reg_v(ot, reg, cpu_T1); if (s->is_jmp) { gen_jmp_im(s->pc - s->cs_base); gen_eob(s); @@ -5686,7 +5576,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } else { opreg = rm; } - gen_op_mov_v_reg(ot, cpu_T[1], reg); + gen_op_mov_v_reg(ot, cpu_T1, reg); if (shift) { TCGv imm = tcg_const_tl(cpu_ldub_code(env, s->pc++)); @@ -5837,8 +5727,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } break; case 0x0c: /* fldenv mem */ - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fldenv(cpu_env, cpu_A0, tcg_const_i32(dflag - 1)); break; case 0x0d: /* fldcw mem */ @@ -5847,8 +5735,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_helper_fldcw(cpu_env, cpu_tmp2_i32); break; case 0x0e: /* fnstenv mem */ - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fstenv(cpu_env, cpu_A0, tcg_const_i32(dflag - 1)); break; case 0x0f: /* fnstcw mem */ @@ -5857,24 +5743,16 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, s->mem_index, MO_LEUW); break; case 0x1d: /* fldt mem */ - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fldt_ST0(cpu_env, cpu_A0); break; case 0x1f: /* fstpt mem */ - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fstt_ST0(cpu_env, cpu_A0); gen_helper_fpop(cpu_env); break; case 0x2c: /* frstor mem */ - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_frstor(cpu_env, cpu_A0, tcg_const_i32(dflag - 1)); break; case 0x2e: /* fnsave mem */ - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fsave(cpu_env, cpu_A0, tcg_const_i32(dflag - 1)); break; case 0x2f: /* fnstsw mem */ @@ -5883,13 +5761,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, s->mem_index, MO_LEUW); break; case 0x3c: /* fbld */ - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fbld_ST0(cpu_env, cpu_A0); break; case 0x3e: /* fbstp */ - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fbst_ST0(cpu_env, cpu_A0); gen_helper_fpop(cpu_env); break; @@ -5903,7 +5777,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_helper_fpop(cpu_env); break; default: - goto illegal_op; + goto unknown_op; } } else { /* register float ops */ @@ -5924,12 +5798,10 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, switch(rm) { case 0: /* fnop */ /* check exceptions (FreeBSD FPU probe) */ - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fwait(cpu_env); break; default: - goto illegal_op; + goto unknown_op; } break; case 0x0c: /* grp d9/4 */ @@ -5948,7 +5820,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_helper_fxam_ST0(cpu_env); break; default: - goto illegal_op; + goto unknown_op; } break; case 0x0d: /* grp d9/5 */ @@ -5983,7 +5855,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_helper_fldz_ST0(cpu_env); break; default: - goto illegal_op; + goto unknown_op; } } break; @@ -6083,7 +5955,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_helper_fpop(cpu_env); break; default: - goto illegal_op; + goto unknown_op; } break; case 0x1c: @@ -6101,7 +5973,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 4: /* fsetpm (287 only, just do nop here) */ break; default: - goto illegal_op; + goto unknown_op; } break; case 0x1d: /* fucomi */ @@ -6153,7 +6025,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_helper_fpop(cpu_env); break; default: - goto illegal_op; + goto unknown_op; } break; case 0x38: /* ffreep sti, undocumented op */ @@ -6164,11 +6036,11 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, switch(rm) { case 0: gen_helper_fnstsw(cpu_tmp2_i32, cpu_env); - tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32); - gen_op_mov_reg_v(MO_16, R_EAX, cpu_T[0]); + tcg_gen_extu_i32_tl(cpu_T0, cpu_tmp2_i32); + gen_op_mov_reg_v(MO_16, R_EAX, cpu_T0); break; default: - goto illegal_op; + goto unknown_op; } break; case 0x3d: /* fucomip */ @@ -6214,7 +6086,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } break; default: - goto illegal_op; + goto unknown_op; } } break; @@ -6275,7 +6147,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 0x6c: /* insS */ case 0x6d: ot = mo_b_d32(b, dflag); - tcg_gen_ext16u_tl(cpu_T[0], cpu_regs[R_EDX]); + tcg_gen_ext16u_tl(cpu_T0, cpu_regs[R_EDX]); gen_check_io(s, ot, pc_start - s->cs_base, SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes) | 4); if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { @@ -6290,7 +6162,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 0x6e: /* outsS */ case 0x6f: ot = mo_b_d32(b, dflag); - tcg_gen_ext16u_tl(cpu_T[0], cpu_regs[R_EDX]); + tcg_gen_ext16u_tl(cpu_T0, cpu_regs[R_EDX]); gen_check_io(s, ot, pc_start - s->cs_base, svm_is_rep(prefixes) | 4); if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { @@ -6310,15 +6182,16 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 0xe5: ot = mo_b_d32(b, dflag); val = cpu_ldub_code(env, s->pc++); - tcg_gen_movi_tl(cpu_T[0], val); + tcg_gen_movi_tl(cpu_T0, val); gen_check_io(s, ot, pc_start - s->cs_base, SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes)); if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); } tcg_gen_movi_i32(cpu_tmp2_i32, val); - gen_helper_in_func(ot, cpu_T[1], cpu_tmp2_i32); - gen_op_mov_reg_v(ot, R_EAX, cpu_T[1]); + gen_helper_in_func(ot, cpu_T1, cpu_tmp2_i32); + gen_op_mov_reg_v(ot, R_EAX, cpu_T1); + gen_bpt_io(s, cpu_tmp2_i32, ot); if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); gen_jmp(s, s->pc - s->cs_base); @@ -6328,17 +6201,18 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 0xe7: ot = mo_b_d32(b, dflag); val = cpu_ldub_code(env, s->pc++); - tcg_gen_movi_tl(cpu_T[0], val); + tcg_gen_movi_tl(cpu_T0, val); gen_check_io(s, ot, pc_start - s->cs_base, svm_is_rep(prefixes)); - gen_op_mov_v_reg(ot, cpu_T[1], R_EAX); + gen_op_mov_v_reg(ot, cpu_T1, R_EAX); if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); } tcg_gen_movi_i32(cpu_tmp2_i32, val); - tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]); + tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T1); gen_helper_out_func(ot, cpu_tmp2_i32, cpu_tmp3_i32); + gen_bpt_io(s, cpu_tmp2_i32, ot); if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); gen_jmp(s, s->pc - s->cs_base); @@ -6347,15 +6221,16 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 0xec: case 0xed: ot = mo_b_d32(b, dflag); - tcg_gen_ext16u_tl(cpu_T[0], cpu_regs[R_EDX]); + tcg_gen_ext16u_tl(cpu_T0, cpu_regs[R_EDX]); gen_check_io(s, ot, pc_start - s->cs_base, SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes)); if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); } - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); - gen_helper_in_func(ot, cpu_T[1], cpu_tmp2_i32); - gen_op_mov_reg_v(ot, R_EAX, cpu_T[1]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); + gen_helper_in_func(ot, cpu_T1, cpu_tmp2_i32); + gen_op_mov_reg_v(ot, R_EAX, cpu_T1); + gen_bpt_io(s, cpu_tmp2_i32, ot); if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); gen_jmp(s, s->pc - s->cs_base); @@ -6364,17 +6239,18 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 0xee: case 0xef: ot = mo_b_d32(b, dflag); - tcg_gen_ext16u_tl(cpu_T[0], cpu_regs[R_EDX]); + tcg_gen_ext16u_tl(cpu_T0, cpu_regs[R_EDX]); gen_check_io(s, ot, pc_start - s->cs_base, svm_is_rep(prefixes)); - gen_op_mov_v_reg(ot, cpu_T[1], R_EAX); + gen_op_mov_v_reg(ot, cpu_T1, R_EAX); if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); } - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); - tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); + tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T1); gen_helper_out_func(ot, cpu_tmp2_i32, cpu_tmp3_i32); + gen_bpt_io(s, cpu_tmp2_i32, ot); if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); gen_jmp(s, s->pc - s->cs_base); @@ -6389,14 +6265,16 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, ot = gen_pop_T0(s); gen_stack_update(s, val + (1 << ot)); /* Note that gen_pop_T0 uses a zero-extending load. */ - gen_op_jmp_v(cpu_T[0]); + gen_op_jmp_v(cpu_T0); + gen_bnd_jmp(s); gen_eob(s); break; case 0xc3: /* ret */ ot = gen_pop_T0(s); gen_pop_update(s, ot); /* Note that gen_pop_T0 uses a zero-extending load. */ - gen_op_jmp_v(cpu_T[0]); + gen_op_jmp_v(cpu_T0); + gen_bnd_jmp(s); gen_eob(s); break; case 0xca: /* lret im */ @@ -6411,13 +6289,13 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } else { gen_stack_A0(s); /* pop offset */ - gen_op_ld_v(s, dflag, cpu_T[0], cpu_A0); + gen_op_ld_v(s, dflag, cpu_T0, cpu_A0); /* NOTE: keeping EIP updated is not a problem in case of exception */ - gen_op_jmp_v(cpu_T[0]); + gen_op_jmp_v(cpu_T0); /* pop selector */ - gen_op_addl_A0_im(1 << dflag); - gen_op_ld_v(s, dflag, cpu_T[0], cpu_A0); + gen_add_A0_im(s, 1 << dflag); + gen_op_ld_v(s, dflag, cpu_T0, cpu_A0); gen_op_movl_seg_T0_vm(R_CS); /* add stack offset */ gen_stack_update(s, val + (2 << dflag)); @@ -6441,8 +6319,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, set_cc_op(s, CC_OP_EFLAGS); } } else { - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_iret_protected(cpu_env, tcg_const_i32(dflag - 1), tcg_const_i32(s->pc - s->cs_base)); set_cc_op(s, CC_OP_EFLAGS); @@ -6463,8 +6339,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } else if (!CODE64(s)) { tval &= 0xffffffff; } - tcg_gen_movi_tl(cpu_T[0], next_eip); - gen_push_v(s, cpu_T[0]); + tcg_gen_movi_tl(cpu_T0, next_eip); + gen_push_v(s, cpu_T0); + gen_bnd_jmp(s); gen_jmp(s, tval); } break; @@ -6478,8 +6355,8 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, offset = insn_get(env, s, ot); selector = insn_get(env, s, MO_16); - tcg_gen_movi_tl(cpu_T[0], selector); - tcg_gen_movi_tl(cpu_T[1], offset); + tcg_gen_movi_tl(cpu_T0, selector); + tcg_gen_movi_tl(cpu_T1, offset); } goto do_lcall; case 0xe9: /* jmp im */ @@ -6494,6 +6371,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } else if (!CODE64(s)) { tval &= 0xffffffff; } + gen_bnd_jmp(s); gen_jmp(s, tval); break; case 0xea: /* ljmp im */ @@ -6506,8 +6384,8 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, offset = insn_get(env, s, ot); selector = insn_get(env, s, MO_16); - tcg_gen_movi_tl(cpu_T[0], selector); - tcg_gen_movi_tl(cpu_T[1], offset); + tcg_gen_movi_tl(cpu_T0, selector); + tcg_gen_movi_tl(cpu_T1, offset); } goto do_ljmp; case 0xeb: /* jmp Jb */ @@ -6533,12 +6411,13 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (dflag == MO_16) { tval &= 0xffff; } + gen_bnd_jmp(s); gen_jcc(s, b, tval, next_eip); break; case 0x190 ... 0x19f: /* setcc Gv */ modrm = cpu_ldub_code(env, s->pc++); - gen_setcc1(s, b, cpu_T[0]); + gen_setcc1(s, b, cpu_T0); gen_ldst_modrm(env, s, modrm, MO_8, OR_TMP0, 1); break; case 0x140 ... 0x14f: /* cmov Gv, Ev */ @@ -6559,8 +6438,8 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { gen_update_cc_op(s); - gen_helper_read_eflags(cpu_T[0], cpu_env); - gen_push_v(s, cpu_T[0]); + gen_helper_read_eflags(cpu_T0, cpu_env); + gen_push_v(s, cpu_T0); } break; case 0x9d: /* popf */ @@ -6571,13 +6450,13 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, ot = gen_pop_T0(s); if (s->cpl == 0) { if (dflag != MO_16) { - gen_helper_write_eflags(cpu_env, cpu_T[0], + gen_helper_write_eflags(cpu_env, cpu_T0, tcg_const_i32((TF_MASK | AC_MASK | ID_MASK | NT_MASK | IF_MASK | IOPL_MASK))); } else { - gen_helper_write_eflags(cpu_env, cpu_T[0], + gen_helper_write_eflags(cpu_env, cpu_T0, tcg_const_i32((TF_MASK | AC_MASK | ID_MASK | NT_MASK | IF_MASK | IOPL_MASK) @@ -6586,14 +6465,14 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } else { if (s->cpl <= s->iopl) { if (dflag != MO_16) { - gen_helper_write_eflags(cpu_env, cpu_T[0], + gen_helper_write_eflags(cpu_env, cpu_T0, tcg_const_i32((TF_MASK | AC_MASK | ID_MASK | NT_MASK | IF_MASK))); } else { - gen_helper_write_eflags(cpu_env, cpu_T[0], + gen_helper_write_eflags(cpu_env, cpu_T0, tcg_const_i32((TF_MASK | AC_MASK | ID_MASK | @@ -6603,11 +6482,11 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } } else { if (dflag != MO_16) { - gen_helper_write_eflags(cpu_env, cpu_T[0], + gen_helper_write_eflags(cpu_env, cpu_T0, tcg_const_i32((TF_MASK | AC_MASK | ID_MASK | NT_MASK))); } else { - gen_helper_write_eflags(cpu_env, cpu_T[0], + gen_helper_write_eflags(cpu_env, cpu_T0, tcg_const_i32((TF_MASK | AC_MASK | ID_MASK | NT_MASK) & 0xffff)); @@ -6624,19 +6503,19 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 0x9e: /* sahf */ if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) goto illegal_op; - gen_op_mov_v_reg(MO_8, cpu_T[0], R_AH); + gen_op_mov_v_reg(MO_8, cpu_T0, R_AH); gen_compute_eflags(s); tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, CC_O); - tcg_gen_andi_tl(cpu_T[0], cpu_T[0], CC_S | CC_Z | CC_A | CC_P | CC_C); - tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, cpu_T[0]); + tcg_gen_andi_tl(cpu_T0, cpu_T0, CC_S | CC_Z | CC_A | CC_P | CC_C); + tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, cpu_T0); break; case 0x9f: /* lahf */ if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) goto illegal_op; gen_compute_eflags(s); /* Note: gen_compute_eflags() only gives the condition codes */ - tcg_gen_ori_tl(cpu_T[0], cpu_cc_src, 0x02); - gen_op_mov_reg_v(MO_8, R_AH, cpu_T[0]); + tcg_gen_ori_tl(cpu_T0, cpu_cc_src, 0x02); + gen_op_mov_reg_v(MO_8, R_AH, cpu_T0); break; case 0xf5: /* cmc */ gen_compute_eflags(s); @@ -6670,15 +6549,15 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (mod != 3) { s->rip_offset = 1; gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); + gen_op_ld_v(s, ot, cpu_T0, cpu_A0); } else { - gen_op_mov_v_reg(ot, cpu_T[0], rm); + gen_op_mov_v_reg(ot, cpu_T0, rm); } /* load shift */ val = cpu_ldub_code(env, s->pc++); - tcg_gen_movi_tl(cpu_T[1], val); + tcg_gen_movi_tl(cpu_T1, val); if (op < 4) - goto illegal_op; + goto unknown_op; op -= 4; goto bt_op; case 0x1a3: /* bt Gv, Ev */ @@ -6698,46 +6577,46 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, reg = ((modrm >> 3) & 7) | rex_r; mod = (modrm >> 6) & 3; rm = (modrm & 7) | REX_B(s); - gen_op_mov_v_reg(MO_32, cpu_T[1], reg); + gen_op_mov_v_reg(MO_32, cpu_T1, reg); if (mod != 3) { gen_lea_modrm(env, s, modrm); /* specific case: we need to add a displacement */ - gen_exts(ot, cpu_T[1]); - tcg_gen_sari_tl(cpu_tmp0, cpu_T[1], 3 + ot); + gen_exts(ot, cpu_T1); + tcg_gen_sari_tl(cpu_tmp0, cpu_T1, 3 + ot); tcg_gen_shli_tl(cpu_tmp0, cpu_tmp0, ot); tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); - gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); + gen_op_ld_v(s, ot, cpu_T0, cpu_A0); } else { - gen_op_mov_v_reg(ot, cpu_T[0], rm); + gen_op_mov_v_reg(ot, cpu_T0, rm); } bt_op: - tcg_gen_andi_tl(cpu_T[1], cpu_T[1], (1 << (3 + ot)) - 1); - tcg_gen_shr_tl(cpu_tmp4, cpu_T[0], cpu_T[1]); + tcg_gen_andi_tl(cpu_T1, cpu_T1, (1 << (3 + ot)) - 1); + tcg_gen_shr_tl(cpu_tmp4, cpu_T0, cpu_T1); switch(op) { case 0: break; case 1: tcg_gen_movi_tl(cpu_tmp0, 1); - tcg_gen_shl_tl(cpu_tmp0, cpu_tmp0, cpu_T[1]); - tcg_gen_or_tl(cpu_T[0], cpu_T[0], cpu_tmp0); + tcg_gen_shl_tl(cpu_tmp0, cpu_tmp0, cpu_T1); + tcg_gen_or_tl(cpu_T0, cpu_T0, cpu_tmp0); break; case 2: tcg_gen_movi_tl(cpu_tmp0, 1); - tcg_gen_shl_tl(cpu_tmp0, cpu_tmp0, cpu_T[1]); - tcg_gen_andc_tl(cpu_T[0], cpu_T[0], cpu_tmp0); + tcg_gen_shl_tl(cpu_tmp0, cpu_tmp0, cpu_T1); + tcg_gen_andc_tl(cpu_T0, cpu_T0, cpu_tmp0); break; default: case 3: tcg_gen_movi_tl(cpu_tmp0, 1); - tcg_gen_shl_tl(cpu_tmp0, cpu_tmp0, cpu_T[1]); - tcg_gen_xor_tl(cpu_T[0], cpu_T[0], cpu_tmp0); + tcg_gen_shl_tl(cpu_tmp0, cpu_tmp0, cpu_T1); + tcg_gen_xor_tl(cpu_T0, cpu_T0, cpu_tmp0); break; } if (op != 0) { if (mod != 3) { - gen_op_st_v(s, ot, cpu_T[0], cpu_A0); + gen_op_st_v(s, ot, cpu_T0, cpu_A0); } else { - gen_op_mov_reg_v(ot, rm, cpu_T[0]); + gen_op_mov_reg_v(ot, rm, cpu_T0); } } @@ -6777,7 +6656,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, modrm = cpu_ldub_code(env, s->pc++); reg = ((modrm >> 3) & 7) | rex_r; gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_extu(ot, cpu_T[0]); + gen_extu(ot, cpu_T0); /* Note that lzcnt and tzcnt are in different extensions. */ if ((prefixes & PREFIX_REPZ) @@ -6785,18 +6664,18 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, ? s->cpuid_ext3_features & CPUID_EXT3_ABM : s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1)) { int size = 8 << ot; - tcg_gen_mov_tl(cpu_cc_src, cpu_T[0]); + tcg_gen_mov_tl(cpu_cc_src, cpu_T0); if (b & 1) { /* For lzcnt, reduce the target_ulong result by the number of zeros that we expect to find at the top. */ - gen_helper_clz(cpu_T[0], cpu_T[0]); - tcg_gen_subi_tl(cpu_T[0], cpu_T[0], TARGET_LONG_BITS - size); + gen_helper_clz(cpu_T0, cpu_T0); + tcg_gen_subi_tl(cpu_T0, cpu_T0, TARGET_LONG_BITS - size); } else { /* For tzcnt, a zero input must return the operand size: force all bits outside the operand size to 1. */ target_ulong mask = (target_ulong)-2 << (size - 1); - tcg_gen_ori_tl(cpu_T[0], cpu_T[0], mask); - gen_helper_ctz(cpu_T[0], cpu_T[0]); + tcg_gen_ori_tl(cpu_T0, cpu_T0, mask); + gen_helper_ctz(cpu_T0, cpu_T0); } /* For lzcnt/tzcnt, C and Z bits are defined and are related to the result. */ @@ -6805,24 +6684,24 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } else { /* For bsr/bsf, only the Z bit is defined and it is related to the input and not the result. */ - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); + tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); set_cc_op(s, CC_OP_LOGICB + ot); if (b & 1) { /* For bsr, return the bit index of the first 1 bit, not the count of leading zeros. */ - gen_helper_clz(cpu_T[0], cpu_T[0]); - tcg_gen_xori_tl(cpu_T[0], cpu_T[0], TARGET_LONG_BITS - 1); + gen_helper_clz(cpu_T0, cpu_T0); + tcg_gen_xori_tl(cpu_T0, cpu_T0, TARGET_LONG_BITS - 1); } else { - gen_helper_ctz(cpu_T[0], cpu_T[0]); + gen_helper_ctz(cpu_T0, cpu_T0); } /* ??? The manual says that the output is undefined when the input is zero, but real hardware leaves it unchanged, and real programs appear to depend on that. */ tcg_gen_movi_tl(cpu_tmp0, 0); - tcg_gen_movcond_tl(TCG_COND_EQ, cpu_T[0], cpu_cc_dst, cpu_tmp0, - cpu_regs[reg], cpu_T[0]); + tcg_gen_movcond_tl(TCG_COND_EQ, cpu_T0, cpu_cc_dst, cpu_tmp0, + cpu_regs[reg], cpu_T0); } - gen_op_mov_reg_v(ot, reg, cpu_T[0]); + gen_op_mov_reg_v(ot, reg, cpu_T0); break; /************************/ /* bcd */ @@ -6895,8 +6774,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, (HF_MP_MASK | HF_TS_MASK)) { gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); } else { - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fwait(cpu_env); } break; @@ -6946,27 +6823,13 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } break; case 0xfb: /* sti */ - if (!s->vm86) { - if (s->cpl <= s->iopl) { - gen_sti: - gen_helper_sti(cpu_env); - /* interruptions are enabled only the first insn after sti */ - /* If several instructions disable interrupts, only the - _first_ does it */ - if (!(s->tb->flags & HF_INHIBIT_IRQ_MASK)) - gen_helper_set_inhibit_irq(cpu_env); - /* give a chance to handle pending irqs */ - gen_jmp_im(s->pc - s->cs_base); - gen_eob(s); - } else { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } + if (s->vm86 ? s->iopl == 3 : s->cpl <= s->iopl) { + gen_helper_sti(cpu_env); + /* interruptions are enabled only the first insn after sti */ + gen_jmp_im(s->pc - s->cs_base); + gen_eob_inhibit_irq(s, true); } else { - if (s->iopl == 3) { - goto gen_sti; - } else { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } break; case 0x62: /* bound */ @@ -6978,10 +6841,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, mod = (modrm >> 6) & 3; if (mod == 3) goto illegal_op; - gen_op_mov_v_reg(ot, cpu_T[0], reg); + gen_op_mov_v_reg(ot, cpu_T0, reg); gen_lea_modrm(env, s, modrm); - gen_jmp_im(pc_start - s->cs_base); - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); if (ot == MO_16) { gen_helper_boundw(cpu_env, cpu_A0, cpu_tmp2_i32); } else { @@ -6992,24 +6854,24 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, reg = (b & 7) | REX_B(s); #ifdef TARGET_X86_64 if (dflag == MO_64) { - gen_op_mov_v_reg(MO_64, cpu_T[0], reg); - tcg_gen_bswap64_i64(cpu_T[0], cpu_T[0]); - gen_op_mov_reg_v(MO_64, reg, cpu_T[0]); + gen_op_mov_v_reg(MO_64, cpu_T0, reg); + tcg_gen_bswap64_i64(cpu_T0, cpu_T0); + gen_op_mov_reg_v(MO_64, reg, cpu_T0); } else #endif { - gen_op_mov_v_reg(MO_32, cpu_T[0], reg); - tcg_gen_ext32u_tl(cpu_T[0], cpu_T[0]); - tcg_gen_bswap32_tl(cpu_T[0], cpu_T[0]); - gen_op_mov_reg_v(MO_32, reg, cpu_T[0]); + gen_op_mov_v_reg(MO_32, cpu_T0, reg); + tcg_gen_ext32u_tl(cpu_T0, cpu_T0); + tcg_gen_bswap32_tl(cpu_T0, cpu_T0); + gen_op_mov_reg_v(MO_32, reg, cpu_T0); } break; case 0xd6: /* salc */ if (CODE64(s)) goto illegal_op; - gen_compute_eflags_c(s, cpu_T[0]); - tcg_gen_neg_tl(cpu_T[0], cpu_T[0]); - gen_op_mov_reg_v(MO_8, R_EAX, cpu_T[0]); + gen_compute_eflags_c(s, cpu_T0); + tcg_gen_neg_tl(cpu_T0, cpu_T0); + gen_op_mov_reg_v(MO_8, R_EAX, cpu_T0); break; case 0xe0: /* loopnz */ case 0xe1: /* loopz */ @@ -7094,8 +6956,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (!s->pe) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_sysenter(cpu_env); gen_eob(s); } @@ -7107,8 +6967,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (!s->pe) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_sysexit(cpu_env, tcg_const_i32(dflag - 1)); gen_eob(s); } @@ -7125,8 +6983,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (!s->pe) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_sysret(cpu_env, tcg_const_i32(dflag - 1)); /* condition codes are modified only in long mode */ if (s->lma) { @@ -7160,7 +7016,8 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (!s->pe || s->vm86) goto illegal_op; gen_svm_check_intercept(s, pc_start, SVM_EXIT_LDTR_READ); - tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,ldt.selector)); + tcg_gen_ld32u_tl(cpu_T0, cpu_env, + offsetof(CPUX86State, ldt.selector)); ot = mod == 3 ? dflag : MO_16; gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); break; @@ -7172,8 +7029,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } else { gen_svm_check_intercept(s, pc_start, SVM_EXIT_LDTR_WRITE); gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - gen_jmp_im(pc_start - s->cs_base); - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); gen_helper_lldt(cpu_env, cpu_tmp2_i32); } break; @@ -7181,7 +7037,8 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (!s->pe || s->vm86) goto illegal_op; gen_svm_check_intercept(s, pc_start, SVM_EXIT_TR_READ); - tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,tr.selector)); + tcg_gen_ld32u_tl(cpu_T0, cpu_env, + offsetof(CPUX86State, tr.selector)); ot = mod == 3 ? dflag : MO_16; gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); break; @@ -7193,8 +7050,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } else { gen_svm_check_intercept(s, pc_start, SVM_EXIT_TR_WRITE); gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - gen_jmp_im(pc_start - s->cs_base); - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); gen_helper_ltr(cpu_env, cpu_tmp2_i32); } break; @@ -7205,279 +7061,347 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); gen_update_cc_op(s); if (op == 4) { - gen_helper_verr(cpu_env, cpu_T[0]); + gen_helper_verr(cpu_env, cpu_T0); } else { - gen_helper_verw(cpu_env, cpu_T[0]); + gen_helper_verw(cpu_env, cpu_T0); } set_cc_op(s, CC_OP_EFLAGS); break; default: - goto illegal_op; + goto unknown_op; } break; + case 0x101: modrm = cpu_ldub_code(env, s->pc++); - mod = (modrm >> 6) & 3; - op = (modrm >> 3) & 7; - rm = modrm & 7; - switch(op) { - case 0: /* sgdt */ - if (mod == 3) - goto illegal_op; + switch (modrm) { + CASE_MODRM_MEM_OP(0): /* sgdt */ gen_svm_check_intercept(s, pc_start, SVM_EXIT_GDTR_READ); gen_lea_modrm(env, s, modrm); - tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, gdt.limit)); - gen_op_st_v(s, MO_16, cpu_T[0], cpu_A0); + tcg_gen_ld32u_tl(cpu_T0, + cpu_env, offsetof(CPUX86State, gdt.limit)); + gen_op_st_v(s, MO_16, cpu_T0, cpu_A0); gen_add_A0_im(s, 2); - tcg_gen_ld_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, gdt.base)); + tcg_gen_ld_tl(cpu_T0, cpu_env, offsetof(CPUX86State, gdt.base)); if (dflag == MO_16) { - tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 0xffffff); + tcg_gen_andi_tl(cpu_T0, cpu_T0, 0xffffff); } - gen_op_st_v(s, CODE64(s) + MO_32, cpu_T[0], cpu_A0); + gen_op_st_v(s, CODE64(s) + MO_32, cpu_T0, cpu_A0); break; - case 1: - if (mod == 3) { - switch (rm) { - case 0: /* monitor */ - if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) || - s->cpl != 0) - goto illegal_op; - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); - tcg_gen_mov_tl(cpu_A0, cpu_regs[R_EAX]); - gen_extu(s->aflag, cpu_A0); - gen_add_A0_ds_seg(s); - gen_helper_monitor(cpu_env, cpu_A0); - break; - case 1: /* mwait */ - if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) || - s->cpl != 0) - goto illegal_op; - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); - gen_helper_mwait(cpu_env, tcg_const_i32(s->pc - pc_start)); - gen_eob(s); - break; - case 2: /* clac */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP) || - s->cpl != 0) { - goto illegal_op; - } - gen_helper_clac(cpu_env); - gen_jmp_im(s->pc - s->cs_base); - gen_eob(s); - break; - case 3: /* stac */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP) || - s->cpl != 0) { - goto illegal_op; - } - gen_helper_stac(cpu_env); - gen_jmp_im(s->pc - s->cs_base); - gen_eob(s); - break; - default: - goto illegal_op; - } - } else { /* sidt */ - gen_svm_check_intercept(s, pc_start, SVM_EXIT_IDTR_READ); - gen_lea_modrm(env, s, modrm); - tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, idt.limit)); - gen_op_st_v(s, MO_16, cpu_T[0], cpu_A0); - gen_add_A0_im(s, 2); - tcg_gen_ld_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, idt.base)); - if (dflag == MO_16) { - tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 0xffffff); - } - gen_op_st_v(s, CODE64(s) + MO_32, cpu_T[0], cpu_A0); + + case 0xc8: /* monitor */ + if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) || s->cpl != 0) { + goto illegal_op; } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + tcg_gen_mov_tl(cpu_A0, cpu_regs[R_EAX]); + gen_extu(s->aflag, cpu_A0); + gen_add_A0_ds_seg(s); + gen_helper_monitor(cpu_env, cpu_A0); break; - case 2: /* lgdt */ - case 3: /* lidt */ - if (mod == 3) { - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); - switch(rm) { - case 0: /* VMRUN */ - if (!(s->flags & HF_SVME_MASK) || !s->pe) - goto illegal_op; - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } else { - gen_helper_vmrun(cpu_env, tcg_const_i32(s->aflag - 1), - tcg_const_i32(s->pc - pc_start)); - tcg_gen_exit_tb(0); - s->is_jmp = DISAS_TB_JUMP; - } - break; - case 1: /* VMMCALL */ - if (!(s->flags & HF_SVME_MASK)) - goto illegal_op; - gen_helper_vmmcall(cpu_env); - break; - case 2: /* VMLOAD */ - if (!(s->flags & HF_SVME_MASK) || !s->pe) - goto illegal_op; - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } else { - gen_helper_vmload(cpu_env, tcg_const_i32(s->aflag - 1)); - } - break; - case 3: /* VMSAVE */ - if (!(s->flags & HF_SVME_MASK) || !s->pe) - goto illegal_op; - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } else { - gen_helper_vmsave(cpu_env, tcg_const_i32(s->aflag - 1)); - } - break; - case 4: /* STGI */ - if ((!(s->flags & HF_SVME_MASK) && - !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) || - !s->pe) - goto illegal_op; - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } else { - gen_helper_stgi(cpu_env); - } - break; - case 5: /* CLGI */ - if (!(s->flags & HF_SVME_MASK) || !s->pe) - goto illegal_op; - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } else { - gen_helper_clgi(cpu_env); - } - break; - case 6: /* SKINIT */ - if ((!(s->flags & HF_SVME_MASK) && - !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) || - !s->pe) - goto illegal_op; - gen_helper_skinit(cpu_env); - break; - case 7: /* INVLPGA */ - if (!(s->flags & HF_SVME_MASK) || !s->pe) - goto illegal_op; - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } else { - gen_helper_invlpga(cpu_env, - tcg_const_i32(s->aflag - 1)); - } - break; - default: - goto illegal_op; - } - } else if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - gen_svm_check_intercept(s, pc_start, - op==2 ? SVM_EXIT_GDTR_WRITE : SVM_EXIT_IDTR_WRITE); - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, MO_16, cpu_T[1], cpu_A0); - gen_add_A0_im(s, 2); - gen_op_ld_v(s, CODE64(s) + MO_32, cpu_T[0], cpu_A0); - if (dflag == MO_16) { - tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 0xffffff); - } - if (op == 2) { - tcg_gen_st_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,gdt.base)); - tcg_gen_st32_tl(cpu_T[1], cpu_env, offsetof(CPUX86State,gdt.limit)); - } else { - tcg_gen_st_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,idt.base)); - tcg_gen_st32_tl(cpu_T[1], cpu_env, offsetof(CPUX86State,idt.limit)); - } + + case 0xc9: /* mwait */ + if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) || s->cpl != 0) { + goto illegal_op; } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_helper_mwait(cpu_env, tcg_const_i32(s->pc - pc_start)); + gen_eob(s); break; - case 4: /* smsw */ - gen_svm_check_intercept(s, pc_start, SVM_EXIT_READ_CR0); -#if defined TARGET_X86_64 && defined HOST_WORDS_BIGENDIAN - tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,cr[0]) + 4); -#else - tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,cr[0])); -#endif - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 1); + + case 0xca: /* clac */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP) + || s->cpl != 0) { + goto illegal_op; + } + gen_helper_clac(cpu_env); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); break; - case 6: /* lmsw */ - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_CR0); - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - gen_helper_lmsw(cpu_env, cpu_T[0]); - gen_jmp_im(s->pc - s->cs_base); - gen_eob(s); + + case 0xcb: /* stac */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP) + || s->cpl != 0) { + goto illegal_op; } + gen_helper_stac(cpu_env); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); break; - case 7: - if (mod != 3) { /* invlpg */ + + CASE_MODRM_MEM_OP(1): /* sidt */ + gen_svm_check_intercept(s, pc_start, SVM_EXIT_IDTR_READ); + gen_lea_modrm(env, s, modrm); + tcg_gen_ld32u_tl(cpu_T0, cpu_env, offsetof(CPUX86State, idt.limit)); + gen_op_st_v(s, MO_16, cpu_T0, cpu_A0); + gen_add_A0_im(s, 2); + tcg_gen_ld_tl(cpu_T0, cpu_env, offsetof(CPUX86State, idt.base)); + if (dflag == MO_16) { + tcg_gen_andi_tl(cpu_T0, cpu_T0, 0xffffff); + } + gen_op_st_v(s, CODE64(s) + MO_32, cpu_T0, cpu_A0); + break; + + case 0xd0: /* xgetbv */ + if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 + || (s->prefix & (PREFIX_LOCK | PREFIX_DATA + | PREFIX_REPZ | PREFIX_REPNZ))) { + goto illegal_op; + } + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_regs[R_ECX]); + gen_helper_xgetbv(cpu_tmp1_i64, cpu_env, cpu_tmp2_i32); + tcg_gen_extr_i64_tl(cpu_regs[R_EAX], cpu_regs[R_EDX], cpu_tmp1_i64); + break; + + case 0xd1: /* xsetbv */ + if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 + || (s->prefix & (PREFIX_LOCK | PREFIX_DATA + | PREFIX_REPZ | PREFIX_REPNZ))) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + tcg_gen_concat_tl_i64(cpu_tmp1_i64, cpu_regs[R_EAX], + cpu_regs[R_EDX]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_regs[R_ECX]); + gen_helper_xsetbv(cpu_env, cpu_tmp2_i32, cpu_tmp1_i64); + /* End TB because translation flags may change. */ + gen_jmp_im(s->pc - pc_start); + gen_eob(s); + break; + + case 0xd8: /* VMRUN */ + if (!(s->flags & HF_SVME_MASK) || !s->pe) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_helper_vmrun(cpu_env, tcg_const_i32(s->aflag - 1), + tcg_const_i32(s->pc - pc_start)); + tcg_gen_exit_tb(0); + s->is_jmp = DISAS_TB_JUMP; + break; + + case 0xd9: /* VMMCALL */ + if (!(s->flags & HF_SVME_MASK)) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_helper_vmmcall(cpu_env); + break; + + case 0xda: /* VMLOAD */ + if (!(s->flags & HF_SVME_MASK) || !s->pe) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_helper_vmload(cpu_env, tcg_const_i32(s->aflag - 1)); + break; + + case 0xdb: /* VMSAVE */ + if (!(s->flags & HF_SVME_MASK) || !s->pe) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_helper_vmsave(cpu_env, tcg_const_i32(s->aflag - 1)); + break; + + case 0xdc: /* STGI */ + if ((!(s->flags & HF_SVME_MASK) + && !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) + || !s->pe) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_helper_stgi(cpu_env); + break; + + case 0xdd: /* CLGI */ + if (!(s->flags & HF_SVME_MASK) || !s->pe) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_helper_clgi(cpu_env); + break; + + case 0xde: /* SKINIT */ + if ((!(s->flags & HF_SVME_MASK) + && !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) + || !s->pe) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_helper_skinit(cpu_env); + break; + + case 0xdf: /* INVLPGA */ + if (!(s->flags & HF_SVME_MASK) || !s->pe) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_helper_invlpga(cpu_env, tcg_const_i32(s->aflag - 1)); + break; + + CASE_MODRM_MEM_OP(2): /* lgdt */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_svm_check_intercept(s, pc_start, SVM_EXIT_GDTR_WRITE); + gen_lea_modrm(env, s, modrm); + gen_op_ld_v(s, MO_16, cpu_T1, cpu_A0); + gen_add_A0_im(s, 2); + gen_op_ld_v(s, CODE64(s) + MO_32, cpu_T0, cpu_A0); + if (dflag == MO_16) { + tcg_gen_andi_tl(cpu_T0, cpu_T0, 0xffffff); + } + tcg_gen_st_tl(cpu_T0, cpu_env, offsetof(CPUX86State, gdt.base)); + tcg_gen_st32_tl(cpu_T1, cpu_env, offsetof(CPUX86State, gdt.limit)); + break; + + CASE_MODRM_MEM_OP(3): /* lidt */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_svm_check_intercept(s, pc_start, SVM_EXIT_IDTR_WRITE); + gen_lea_modrm(env, s, modrm); + gen_op_ld_v(s, MO_16, cpu_T1, cpu_A0); + gen_add_A0_im(s, 2); + gen_op_ld_v(s, CODE64(s) + MO_32, cpu_T0, cpu_A0); + if (dflag == MO_16) { + tcg_gen_andi_tl(cpu_T0, cpu_T0, 0xffffff); + } + tcg_gen_st_tl(cpu_T0, cpu_env, offsetof(CPUX86State, idt.base)); + tcg_gen_st32_tl(cpu_T1, cpu_env, offsetof(CPUX86State, idt.limit)); + break; + + CASE_MODRM_OP(4): /* smsw */ + gen_svm_check_intercept(s, pc_start, SVM_EXIT_READ_CR0); + tcg_gen_ld_tl(cpu_T0, cpu_env, offsetof(CPUX86State, cr[0])); + if (CODE64(s)) { + mod = (modrm >> 6) & 3; + ot = (mod != 3 ? MO_16 : s->dflag); + } else { + ot = MO_16; + } + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); + break; + case 0xee: /* rdpkru */ + if (prefixes & PREFIX_LOCK) { + goto illegal_op; + } + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_regs[R_ECX]); + gen_helper_rdpkru(cpu_tmp1_i64, cpu_env, cpu_tmp2_i32); + tcg_gen_extr_i64_tl(cpu_regs[R_EAX], cpu_regs[R_EDX], cpu_tmp1_i64); + break; + case 0xef: /* wrpkru */ + if (prefixes & PREFIX_LOCK) { + goto illegal_op; + } + tcg_gen_concat_tl_i64(cpu_tmp1_i64, cpu_regs[R_EAX], + cpu_regs[R_EDX]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_regs[R_ECX]); + gen_helper_wrpkru(cpu_env, cpu_tmp2_i32, cpu_tmp1_i64); + break; + CASE_MODRM_OP(6): /* lmsw */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_CR0); + gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); + gen_helper_lmsw(cpu_env, cpu_T0); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + break; + + CASE_MODRM_MEM_OP(7): /* invlpg */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_lea_modrm(env, s, modrm); + gen_helper_invlpg(cpu_env, cpu_A0); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + break; + + case 0xf8: /* swapgs */ +#ifdef TARGET_X86_64 + if (CODE64(s)) { if (s->cpl != 0) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); - gen_lea_modrm(env, s, modrm); - gen_helper_invlpg(cpu_env, cpu_A0); - gen_jmp_im(s->pc - s->cs_base); - gen_eob(s); + tcg_gen_mov_tl(cpu_T0, cpu_seg_base[R_GS]); + tcg_gen_ld_tl(cpu_seg_base[R_GS], cpu_env, + offsetof(CPUX86State, kernelgsbase)); + tcg_gen_st_tl(cpu_T0, cpu_env, + offsetof(CPUX86State, kernelgsbase)); } - } else { - switch (rm) { - case 0: /* swapgs */ -#ifdef TARGET_X86_64 - if (CODE64(s)) { - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - tcg_gen_ld_tl(cpu_T[0], cpu_env, - offsetof(CPUX86State,segs[R_GS].base)); - tcg_gen_ld_tl(cpu_T[1], cpu_env, - offsetof(CPUX86State,kernelgsbase)); - tcg_gen_st_tl(cpu_T[1], cpu_env, - offsetof(CPUX86State,segs[R_GS].base)); - tcg_gen_st_tl(cpu_T[0], cpu_env, - offsetof(CPUX86State,kernelgsbase)); - } - } else + break; + } #endif - { - goto illegal_op; - } - break; - case 1: /* rdtscp */ - if (!(s->cpuid_ext2_features & CPUID_EXT2_RDTSCP)) - goto illegal_op; - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); - if (s->tb->cflags & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_rdtscp(cpu_env); - if (s->tb->cflags & CF_USE_ICOUNT) { - gen_io_end(); - gen_jmp(s, s->pc - s->cs_base); - } - break; - default: - goto illegal_op; - } + goto illegal_op; + + case 0xf9: /* rdtscp */ + if (!(s->cpuid_ext2_features & CPUID_EXT2_RDTSCP)) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + if (s->tb->cflags & CF_USE_ICOUNT) { + gen_io_start(); + } + gen_helper_rdtscp(cpu_env); + if (s->tb->cflags & CF_USE_ICOUNT) { + gen_io_end(); + gen_jmp(s, s->pc - s->cs_base); } break; + default: - goto illegal_op; + goto unknown_op; } break; + case 0x108: /* invd */ case 0x109: /* wbinvd */ if (s->cpl != 0) { @@ -7500,16 +7424,16 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, rm = (modrm & 7) | REX_B(s); if (mod == 3) { - gen_op_mov_v_reg(MO_32, cpu_T[0], rm); + gen_op_mov_v_reg(MO_32, cpu_T0, rm); /* sign extend */ if (d_ot == MO_64) { - tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]); + tcg_gen_ext32s_tl(cpu_T0, cpu_T0); } - gen_op_mov_reg_v(d_ot, reg, cpu_T[0]); + gen_op_mov_reg_v(d_ot, reg, cpu_T0); } else { gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, MO_32 | MO_SIGN, cpu_T[0], cpu_A0); - gen_op_mov_reg_v(d_ot, reg, cpu_T[0]); + gen_op_ld_v(s, MO_32 | MO_SIGN, cpu_T0, cpu_A0); + gen_op_mov_reg_v(d_ot, reg, cpu_T0); } } else #endif @@ -7574,9 +7498,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, t0 = tcg_temp_local_new(); gen_update_cc_op(s); if (b == 0x102) { - gen_helper_lar(t0, cpu_env, cpu_T[0]); + gen_helper_lar(t0, cpu_env, cpu_T0); } else { - gen_helper_lsl(t0, cpu_env, cpu_T[0]); + gen_helper_lsl(t0, cpu_env, cpu_T0); } tcg_gen_andi_tl(cpu_tmp0, cpu_cc_src, CC_Z); label1 = gen_new_label(); @@ -7598,7 +7522,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 3: /* prefetchnt0 */ if (mod == 3) goto illegal_op; - gen_lea_modrm(env, s, modrm); + gen_nop_modrm(env, s, modrm); /* nothing more to do */ break; default: /* nop (multi byte) */ @@ -7606,7 +7530,199 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, break; } break; - case 0x119 ... 0x11f: /* nop (multi byte) */ + case 0x11a: + modrm = cpu_ldub_code(env, s->pc++); + if (s->flags & HF_MPX_EN_MASK) { + mod = (modrm >> 6) & 3; + reg = ((modrm >> 3) & 7) | rex_r; + if (prefixes & PREFIX_REPZ) { + /* bndcl */ + if (reg >= 4 + || (prefixes & PREFIX_LOCK) + || s->aflag == MO_16) { + goto illegal_op; + } + gen_bndck(env, s, modrm, TCG_COND_LTU, cpu_bndl[reg]); + } else if (prefixes & PREFIX_REPNZ) { + /* bndcu */ + if (reg >= 4 + || (prefixes & PREFIX_LOCK) + || s->aflag == MO_16) { + goto illegal_op; + } + TCGv_i64 notu = tcg_temp_new_i64(); + tcg_gen_not_i64(notu, cpu_bndu[reg]); + gen_bndck(env, s, modrm, TCG_COND_GTU, notu); + tcg_temp_free_i64(notu); + } else if (prefixes & PREFIX_DATA) { + /* bndmov -- from reg/mem */ + if (reg >= 4 || s->aflag == MO_16) { + goto illegal_op; + } + if (mod == 3) { + int reg2 = (modrm & 7) | REX_B(s); + if (reg2 >= 4 || (prefixes & PREFIX_LOCK)) { + goto illegal_op; + } + if (s->flags & HF_MPX_IU_MASK) { + tcg_gen_mov_i64(cpu_bndl[reg], cpu_bndl[reg2]); + tcg_gen_mov_i64(cpu_bndu[reg], cpu_bndu[reg2]); + } + } else { + gen_lea_modrm(env, s, modrm); + if (CODE64(s)) { + tcg_gen_qemu_ld_i64(cpu_bndl[reg], cpu_A0, + s->mem_index, MO_LEQ); + tcg_gen_addi_tl(cpu_A0, cpu_A0, 8); + tcg_gen_qemu_ld_i64(cpu_bndu[reg], cpu_A0, + s->mem_index, MO_LEQ); + } else { + tcg_gen_qemu_ld_i64(cpu_bndl[reg], cpu_A0, + s->mem_index, MO_LEUL); + tcg_gen_addi_tl(cpu_A0, cpu_A0, 4); + tcg_gen_qemu_ld_i64(cpu_bndu[reg], cpu_A0, + s->mem_index, MO_LEUL); + } + /* bnd registers are now in-use */ + gen_set_hflag(s, HF_MPX_IU_MASK); + } + } else if (mod != 3) { + /* bndldx */ + AddressParts a = gen_lea_modrm_0(env, s, modrm); + if (reg >= 4 + || (prefixes & PREFIX_LOCK) + || s->aflag == MO_16 + || a.base < -1) { + goto illegal_op; + } + if (a.base >= 0) { + tcg_gen_addi_tl(cpu_A0, cpu_regs[a.base], a.disp); + } else { + tcg_gen_movi_tl(cpu_A0, 0); + } + gen_lea_v_seg(s, s->aflag, cpu_A0, a.def_seg, s->override); + if (a.index >= 0) { + tcg_gen_mov_tl(cpu_T0, cpu_regs[a.index]); + } else { + tcg_gen_movi_tl(cpu_T0, 0); + } + if (CODE64(s)) { + gen_helper_bndldx64(cpu_bndl[reg], cpu_env, cpu_A0, cpu_T0); + tcg_gen_ld_i64(cpu_bndu[reg], cpu_env, + offsetof(CPUX86State, mmx_t0.MMX_Q(0))); + } else { + gen_helper_bndldx32(cpu_bndu[reg], cpu_env, cpu_A0, cpu_T0); + tcg_gen_ext32u_i64(cpu_bndl[reg], cpu_bndu[reg]); + tcg_gen_shri_i64(cpu_bndu[reg], cpu_bndu[reg], 32); + } + gen_set_hflag(s, HF_MPX_IU_MASK); + } + } + gen_nop_modrm(env, s, modrm); + break; + case 0x11b: + modrm = cpu_ldub_code(env, s->pc++); + if (s->flags & HF_MPX_EN_MASK) { + mod = (modrm >> 6) & 3; + reg = ((modrm >> 3) & 7) | rex_r; + if (mod != 3 && (prefixes & PREFIX_REPZ)) { + /* bndmk */ + if (reg >= 4 + || (prefixes & PREFIX_LOCK) + || s->aflag == MO_16) { + goto illegal_op; + } + AddressParts a = gen_lea_modrm_0(env, s, modrm); + if (a.base >= 0) { + tcg_gen_extu_tl_i64(cpu_bndl[reg], cpu_regs[a.base]); + if (!CODE64(s)) { + tcg_gen_ext32u_i64(cpu_bndl[reg], cpu_bndl[reg]); + } + } else if (a.base == -1) { + /* no base register has lower bound of 0 */ + tcg_gen_movi_i64(cpu_bndl[reg], 0); + } else { + /* rip-relative generates #ud */ + goto illegal_op; + } + tcg_gen_not_tl(cpu_A0, gen_lea_modrm_1(a)); + if (!CODE64(s)) { + tcg_gen_ext32u_tl(cpu_A0, cpu_A0); + } + tcg_gen_extu_tl_i64(cpu_bndu[reg], cpu_A0); + /* bnd registers are now in-use */ + gen_set_hflag(s, HF_MPX_IU_MASK); + break; + } else if (prefixes & PREFIX_REPNZ) { + /* bndcn */ + if (reg >= 4 + || (prefixes & PREFIX_LOCK) + || s->aflag == MO_16) { + goto illegal_op; + } + gen_bndck(env, s, modrm, TCG_COND_GTU, cpu_bndu[reg]); + } else if (prefixes & PREFIX_DATA) { + /* bndmov -- to reg/mem */ + if (reg >= 4 || s->aflag == MO_16) { + goto illegal_op; + } + if (mod == 3) { + int reg2 = (modrm & 7) | REX_B(s); + if (reg2 >= 4 || (prefixes & PREFIX_LOCK)) { + goto illegal_op; + } + if (s->flags & HF_MPX_IU_MASK) { + tcg_gen_mov_i64(cpu_bndl[reg2], cpu_bndl[reg]); + tcg_gen_mov_i64(cpu_bndu[reg2], cpu_bndu[reg]); + } + } else { + gen_lea_modrm(env, s, modrm); + if (CODE64(s)) { + tcg_gen_qemu_st_i64(cpu_bndl[reg], cpu_A0, + s->mem_index, MO_LEQ); + tcg_gen_addi_tl(cpu_A0, cpu_A0, 8); + tcg_gen_qemu_st_i64(cpu_bndu[reg], cpu_A0, + s->mem_index, MO_LEQ); + } else { + tcg_gen_qemu_st_i64(cpu_bndl[reg], cpu_A0, + s->mem_index, MO_LEUL); + tcg_gen_addi_tl(cpu_A0, cpu_A0, 4); + tcg_gen_qemu_st_i64(cpu_bndu[reg], cpu_A0, + s->mem_index, MO_LEUL); + } + } + } else if (mod != 3) { + /* bndstx */ + AddressParts a = gen_lea_modrm_0(env, s, modrm); + if (reg >= 4 + || (prefixes & PREFIX_LOCK) + || s->aflag == MO_16 + || a.base < -1) { + goto illegal_op; + } + if (a.base >= 0) { + tcg_gen_addi_tl(cpu_A0, cpu_regs[a.base], a.disp); + } else { + tcg_gen_movi_tl(cpu_A0, 0); + } + gen_lea_v_seg(s, s->aflag, cpu_A0, a.def_seg, s->override); + if (a.index >= 0) { + tcg_gen_mov_tl(cpu_T0, cpu_regs[a.index]); + } else { + tcg_gen_movi_tl(cpu_T0, 0); + } + if (CODE64(s)) { + gen_helper_bndstx64(cpu_env, cpu_A0, cpu_T0, + cpu_bndl[reg], cpu_bndu[reg]); + } else { + gen_helper_bndstx32(cpu_env, cpu_A0, cpu_T0, + cpu_bndl[reg], cpu_bndu[reg]); + } + } + } + gen_nop_modrm(env, s, modrm); + break; + case 0x119: case 0x11c ... 0x11f: /* nop (multi byte) */ modrm = cpu_ldub_code(env, s->pc++); gen_nop_modrm(env, s, modrm); break; @@ -7640,18 +7756,18 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); if (b & 2) { - gen_op_mov_v_reg(ot, cpu_T[0], rm); + gen_op_mov_v_reg(ot, cpu_T0, rm); gen_helper_write_crN(cpu_env, tcg_const_i32(reg), - cpu_T[0]); + cpu_T0); gen_jmp_im(s->pc - s->cs_base); gen_eob(s); } else { - gen_helper_read_crN(cpu_T[0], cpu_env, tcg_const_i32(reg)); - gen_op_mov_reg_v(ot, rm, cpu_T[0]); + gen_helper_read_crN(cpu_T0, cpu_env, tcg_const_i32(reg)); + gen_op_mov_reg_v(ot, rm, cpu_T0); } break; default: - goto illegal_op; + goto unknown_op; } } break; @@ -7672,19 +7788,21 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, ot = MO_64; else ot = MO_32; - /* XXX: do it dynamically with CR4.DE bit */ - if (reg == 4 || reg == 5 || reg >= 8) + if (reg >= 8) { goto illegal_op; + } if (b & 2) { gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_DR0 + reg); - gen_op_mov_v_reg(ot, cpu_T[0], rm); - gen_helper_movl_drN_T0(cpu_env, tcg_const_i32(reg), cpu_T[0]); + gen_op_mov_v_reg(ot, cpu_T0, rm); + tcg_gen_movi_i32(cpu_tmp2_i32, reg); + gen_helper_set_dr(cpu_env, cpu_tmp2_i32, cpu_T0); gen_jmp_im(s->pc - s->cs_base); gen_eob(s); } else { gen_svm_check_intercept(s, pc_start, SVM_EXIT_READ_DR0 + reg); - tcg_gen_ld_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,dr[reg])); - gen_op_mov_reg_v(ot, rm, cpu_T[0]); + tcg_gen_movi_i32(cpu_tmp2_i32, reg); + gen_helper_get_dr(cpu_T0, cpu_env, cpu_tmp2_i32); + gen_op_mov_reg_v(ot, rm, cpu_T0); } } break; @@ -7714,83 +7832,195 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, break; case 0x1ae: modrm = cpu_ldub_code(env, s->pc++); - mod = (modrm >> 6) & 3; - op = (modrm >> 3) & 7; - switch(op) { - case 0: /* fxsave */ - if (mod == 3 || !(s->cpuid_features & CPUID_FXSR) || - (s->prefix & PREFIX_LOCK)) + switch (modrm) { + CASE_MODRM_MEM_OP(0): /* fxsave */ + if (!(s->cpuid_features & CPUID_FXSR) + || (prefixes & PREFIX_LOCK)) { goto illegal_op; + } if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); break; } gen_lea_modrm(env, s, modrm); - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); - gen_helper_fxsave(cpu_env, cpu_A0, tcg_const_i32(dflag == MO_64)); + gen_helper_fxsave(cpu_env, cpu_A0); break; - case 1: /* fxrstor */ - if (mod == 3 || !(s->cpuid_features & CPUID_FXSR) || - (s->prefix & PREFIX_LOCK)) + + CASE_MODRM_MEM_OP(1): /* fxrstor */ + if (!(s->cpuid_features & CPUID_FXSR) + || (prefixes & PREFIX_LOCK)) { goto illegal_op; + } if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); break; } gen_lea_modrm(env, s, modrm); - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); - gen_helper_fxrstor(cpu_env, cpu_A0, tcg_const_i32(dflag == MO_64)); + gen_helper_fxrstor(cpu_env, cpu_A0); break; - case 2: /* ldmxcsr */ - case 3: /* stmxcsr */ + + CASE_MODRM_MEM_OP(2): /* ldmxcsr */ + if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK)) { + goto illegal_op; + } if (s->flags & HF_TS_MASK) { gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); break; } - if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK) || - mod == 3) + gen_lea_modrm(env, s, modrm); + tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, s->mem_index, MO_LEUL); + gen_helper_ldmxcsr(cpu_env, cpu_tmp2_i32); + break; + + CASE_MODRM_MEM_OP(3): /* stmxcsr */ + if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK)) { goto illegal_op; + } + if (s->flags & HF_TS_MASK) { + gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + break; + } gen_lea_modrm(env, s, modrm); - if (op == 2) { - tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, - s->mem_index, MO_LEUL); - gen_helper_ldmxcsr(cpu_env, cpu_tmp2_i32); - } else { - tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, mxcsr)); - gen_op_st_v(s, MO_32, cpu_T[0], cpu_A0); + tcg_gen_ld32u_tl(cpu_T0, cpu_env, offsetof(CPUX86State, mxcsr)); + gen_op_st_v(s, MO_32, cpu_T0, cpu_A0); + break; + + CASE_MODRM_MEM_OP(4): /* xsave */ + if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 + || (prefixes & (PREFIX_LOCK | PREFIX_DATA + | PREFIX_REPZ | PREFIX_REPNZ))) { + goto illegal_op; } + gen_lea_modrm(env, s, modrm); + tcg_gen_concat_tl_i64(cpu_tmp1_i64, cpu_regs[R_EAX], + cpu_regs[R_EDX]); + gen_helper_xsave(cpu_env, cpu_A0, cpu_tmp1_i64); break; - case 5: /* lfence */ - case 6: /* mfence */ - if ((modrm & 0xc7) != 0xc0 || !(s->cpuid_features & CPUID_SSE2)) + + CASE_MODRM_MEM_OP(5): /* xrstor */ + if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 + || (prefixes & (PREFIX_LOCK | PREFIX_DATA + | PREFIX_REPZ | PREFIX_REPNZ))) { goto illegal_op; + } + gen_lea_modrm(env, s, modrm); + tcg_gen_concat_tl_i64(cpu_tmp1_i64, cpu_regs[R_EAX], + cpu_regs[R_EDX]); + gen_helper_xrstor(cpu_env, cpu_A0, cpu_tmp1_i64); + /* XRSTOR is how MPX is enabled, which changes how + we translate. Thus we need to end the TB. */ + gen_update_cc_op(s); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); break; - case 7: /* sfence / clflush */ - if ((modrm & 0xc7) == 0xc0) { - /* sfence */ - /* XXX: also check for cpuid_ext2_features & CPUID_EXT2_EMMX */ - if (!(s->cpuid_features & CPUID_SSE)) + + CASE_MODRM_MEM_OP(6): /* xsaveopt / clwb */ + if (prefixes & PREFIX_LOCK) { + goto illegal_op; + } + if (prefixes & PREFIX_DATA) { + /* clwb */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_CLWB)) { goto illegal_op; + } + gen_nop_modrm(env, s, modrm); } else { - /* clflush */ - if (!(s->cpuid_features & CPUID_CLFLUSH)) + /* xsaveopt */ + if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 + || (s->cpuid_xsave_features & CPUID_XSAVE_XSAVEOPT) == 0 + || (prefixes & (PREFIX_REPZ | PREFIX_REPNZ))) { goto illegal_op; + } gen_lea_modrm(env, s, modrm); + tcg_gen_concat_tl_i64(cpu_tmp1_i64, cpu_regs[R_EAX], + cpu_regs[R_EDX]); + gen_helper_xsaveopt(cpu_env, cpu_A0, cpu_tmp1_i64); + } + break; + + CASE_MODRM_MEM_OP(7): /* clflush / clflushopt */ + if (prefixes & PREFIX_LOCK) { + goto illegal_op; + } + if (prefixes & PREFIX_DATA) { + /* clflushopt */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_CLFLUSHOPT)) { + goto illegal_op; + } + } else { + /* clflush */ + if ((s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) + || !(s->cpuid_features & CPUID_CLFLUSH)) { + goto illegal_op; + } + } + gen_nop_modrm(env, s, modrm); + break; + + case 0xc0 ... 0xc7: /* rdfsbase (f3 0f ae /0) */ + case 0xc8 ... 0xc8: /* rdgsbase (f3 0f ae /1) */ + case 0xd0 ... 0xd7: /* wrfsbase (f3 0f ae /2) */ + case 0xd8 ... 0xd8: /* wrgsbase (f3 0f ae /3) */ + if (CODE64(s) + && (prefixes & PREFIX_REPZ) + && !(prefixes & PREFIX_LOCK) + && (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_FSGSBASE)) { + TCGv base, treg, src, dst; + + /* Preserve hflags bits by testing CR4 at runtime. */ + tcg_gen_movi_i32(cpu_tmp2_i32, CR4_FSGSBASE_MASK); + gen_helper_cr4_testbit(cpu_env, cpu_tmp2_i32); + + base = cpu_seg_base[modrm & 8 ? R_GS : R_FS]; + treg = cpu_regs[(modrm & 7) | REX_B(s)]; + + if (modrm & 0x10) { + /* wr*base */ + dst = base, src = treg; + } else { + /* rd*base */ + dst = treg, src = base; + } + + if (s->dflag == MO_32) { + tcg_gen_ext32u_tl(dst, src); + } else { + tcg_gen_mov_tl(dst, src); + } + break; + } + goto unknown_op; + + case 0xf8: /* sfence / pcommit */ + if (prefixes & PREFIX_DATA) { + /* pcommit */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_PCOMMIT) + || (prefixes & PREFIX_LOCK)) { + goto illegal_op; + } + break; + } + /* fallthru */ + case 0xf9 ... 0xff: /* sfence */ + case 0xe8 ... 0xef: /* lfence */ + case 0xf0 ... 0xf7: /* mfence */ + if (!(s->cpuid_features & CPUID_SSE2) + || (prefixes & PREFIX_LOCK)) { + goto illegal_op; } break; + default: - goto illegal_op; + goto unknown_op; } break; + case 0x10d: /* 3DNow! prefetch(w) */ modrm = cpu_ldub_code(env, s->pc++); mod = (modrm >> 6) & 3; if (mod == 3) goto illegal_op; - gen_lea_modrm(env, s, modrm); - /* ignore for now */ + gen_nop_modrm(env, s, modrm); break; case 0x1aa: /* rsm */ gen_svm_check_intercept(s, pc_start, SVM_EXIT_RSM); @@ -7818,8 +8048,8 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_helper_popcnt(cpu_T[0], cpu_env, cpu_T[0], tcg_const_i32(ot)); - gen_op_mov_reg_v(ot, reg, cpu_T[0]); + gen_helper_popcnt(cpu_T0, cpu_env, cpu_T0, tcg_const_i32(ot)); + gen_op_mov_reg_v(ot, reg, cpu_T0); set_cc_op(s, CC_OP_EFLAGS); break; @@ -7837,7 +8067,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_sse(env, s, b, pc_start, rex_r); break; default: - goto illegal_op; + goto unknown_op; } /* lock generation */ if (s->prefix & PREFIX_LOCK) @@ -7847,11 +8077,17 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (s->prefix & PREFIX_LOCK) gen_helper_unlock(); /* XXX: ensure that no lock was generated */ - gen_exception(s, EXCP06_ILLOP, pc_start - s->cs_base); + gen_illegal_opcode(s); + return s->pc; + unknown_op: + if (s->prefix & PREFIX_LOCK) + gen_helper_unlock(); + /* XXX: ensure that no lock was generated */ + gen_unknown_opcode(env, s); return s->pc; } -void optimize_flags_init(void) +void tcg_x86_init(void) { static const char reg_names[CPU_NB_REGS][4] = { #ifdef TARGET_X86_64 @@ -7882,38 +8118,66 @@ void optimize_flags_init(void) [R_ESP] = "esp", #endif }; + static const char seg_base_names[6][8] = { + [R_CS] = "cs_base", + [R_DS] = "ds_base", + [R_ES] = "es_base", + [R_FS] = "fs_base", + [R_GS] = "gs_base", + [R_SS] = "ss_base", + }; + static const char bnd_regl_names[4][8] = { + "bnd0_lb", "bnd1_lb", "bnd2_lb", "bnd3_lb" + }; + static const char bnd_regu_names[4][8] = { + "bnd0_ub", "bnd1_ub", "bnd2_ub", "bnd3_ub" + }; int i; cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); - cpu_cc_op = tcg_global_mem_new_i32(TCG_AREG0, + cpu_cc_op = tcg_global_mem_new_i32(cpu_env, offsetof(CPUX86State, cc_op), "cc_op"); - cpu_cc_dst = tcg_global_mem_new(TCG_AREG0, offsetof(CPUX86State, cc_dst), + cpu_cc_dst = tcg_global_mem_new(cpu_env, offsetof(CPUX86State, cc_dst), "cc_dst"); - cpu_cc_src = tcg_global_mem_new(TCG_AREG0, offsetof(CPUX86State, cc_src), + cpu_cc_src = tcg_global_mem_new(cpu_env, offsetof(CPUX86State, cc_src), "cc_src"); - cpu_cc_src2 = tcg_global_mem_new(TCG_AREG0, offsetof(CPUX86State, cc_src2), + cpu_cc_src2 = tcg_global_mem_new(cpu_env, offsetof(CPUX86State, cc_src2), "cc_src2"); for (i = 0; i < CPU_NB_REGS; ++i) { - cpu_regs[i] = tcg_global_mem_new(TCG_AREG0, + cpu_regs[i] = tcg_global_mem_new(cpu_env, offsetof(CPUX86State, regs[i]), reg_names[i]); } + + for (i = 0; i < 6; ++i) { + cpu_seg_base[i] + = tcg_global_mem_new(cpu_env, + offsetof(CPUX86State, segs[i].base), + seg_base_names[i]); + } + + for (i = 0; i < 4; ++i) { + cpu_bndl[i] + = tcg_global_mem_new_i64(cpu_env, + offsetof(CPUX86State, bnd_regs[i].lb), + bnd_regl_names[i]); + cpu_bndu[i] + = tcg_global_mem_new_i64(cpu_env, + offsetof(CPUX86State, bnd_regs[i].ub), + bnd_regu_names[i]); + } + + helper_lock_init(); } -/* generate intermediate code in gen_opc_buf and gen_opparam_buf for - basic block 'tb'. If search_pc is TRUE, also generate PC - information for each intermediate instruction. */ -static inline void gen_intermediate_code_internal(X86CPU *cpu, - TranslationBlock *tb, - bool search_pc) +/* generate intermediate code for basic block 'tb'. */ +void gen_intermediate_code(CPUX86State *env, TranslationBlock *tb) { + X86CPU *cpu = x86_env_get_cpu(env); CPUState *cs = CPU(cpu); - CPUX86State *env = &cpu->env; DisasContext dc1, *dc = &dc1; target_ulong pc_ptr; - CPUBreakpoint *bp; - int j, lj; uint64_t flags; target_ulong pc_start; target_ulong cs_base; @@ -7943,13 +8207,14 @@ static inline void gen_intermediate_code_internal(X86CPU *cpu, /* select memory access functions */ dc->mem_index = 0; if (flags & HF_SOFTMMU_MASK) { - dc->mem_index = cpu_mmu_index(env); + dc->mem_index = cpu_mmu_index(env, false); } dc->cpuid_features = env->features[FEAT_1_EDX]; dc->cpuid_ext_features = env->features[FEAT_1_ECX]; dc->cpuid_ext2_features = env->features[FEAT_8000_0001_EDX]; dc->cpuid_ext3_features = env->features[FEAT_8000_0001_ECX]; dc->cpuid_7_0_ebx_features = env->features[FEAT_7_0_EBX]; + dc->cpuid_xsave_features = env->features[FEAT_XSAVE]; #ifdef TARGET_X86_64 dc->lma = (flags >> HF_LMA_SHIFT) & 1; dc->code64 = (flags >> HF_CS64_SHIFT) & 1; @@ -7978,8 +8243,8 @@ static inline void gen_intermediate_code_internal(X86CPU *cpu, printf("ERROR addseg\n"); #endif - cpu_T[0] = tcg_temp_new(); - cpu_T[1] = tcg_temp_new(); + cpu_T0 = tcg_temp_new(); + cpu_T1 = tcg_temp_new(); cpu_A0 = tcg_temp_new(); cpu_tmp0 = tcg_temp_new(); @@ -7993,40 +8258,37 @@ static inline void gen_intermediate_code_internal(X86CPU *cpu, dc->is_jmp = DISAS_NEXT; pc_ptr = pc_start; - lj = -1; num_insns = 0; max_insns = tb->cflags & CF_COUNT_MASK; - if (max_insns == 0) + if (max_insns == 0) { max_insns = CF_COUNT_MASK; + } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } gen_tb_start(tb); for(;;) { - if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { - QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - if (bp->pc == pc_ptr && - !((bp->flags & BP_CPU) && (tb->flags & HF_RF_MASK))) { - gen_debug(dc, pc_ptr - dc->cs_base); - goto done_generating; - } - } - } - if (search_pc) { - j = tcg_op_buf_count(); - if (lj < j) { - lj++; - while (lj < j) - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - tcg_ctx.gen_opc_pc[lj] = pc_ptr; - gen_opc_cc_op[lj] = dc->cc_op; - tcg_ctx.gen_opc_instr_start[lj] = 1; - tcg_ctx.gen_opc_icount[lj] = num_insns; - } - if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) + tcg_gen_insn_start(pc_ptr, dc->cc_op); + num_insns++; + + /* If RF is set, suppress an internally generated breakpoint. */ + if (unlikely(cpu_breakpoint_test(cs, pc_ptr, + tb->flags & HF_RF_MASK + ? BP_GDB : BP_ANY))) { + gen_debug(dc, pc_ptr - dc->cs_base); + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + pc_ptr += 1; + goto done_generating; + } + if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { gen_io_start(); + } pc_ptr = disas_insn(env, dc, pc_ptr); - num_insns++; /* stop translation if indicated */ if (dc->is_jmp) break; @@ -8074,14 +8336,6 @@ static inline void gen_intermediate_code_internal(X86CPU *cpu, done_generating: gen_tb_end(tb, num_insns); - /* we don't forget to fill the last values */ - if (search_pc) { - j = tcg_op_buf_count(); - lj++; - while (lj <= j) - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { int disas_flags; @@ -8098,42 +8352,16 @@ done_generating: } #endif - if (!search_pc) { - tb->size = pc_ptr - pc_start; - tb->icount = num_insns; - } + tb->size = pc_ptr - pc_start; + tb->icount = num_insns; } -void gen_intermediate_code(CPUX86State *env, TranslationBlock *tb) +void restore_state_to_opc(CPUX86State *env, TranslationBlock *tb, + target_ulong *data) { - gen_intermediate_code_internal(x86_env_get_cpu(env), tb, false); -} - -void gen_intermediate_code_pc(CPUX86State *env, TranslationBlock *tb) -{ - gen_intermediate_code_internal(x86_env_get_cpu(env), tb, true); -} - -void restore_state_to_opc(CPUX86State *env, TranslationBlock *tb, int pc_pos) -{ - int cc_op; -#ifdef DEBUG_DISAS - if (qemu_loglevel_mask(CPU_LOG_TB_OP)) { - int i; - qemu_log("RESTORE:\n"); - for(i = 0;i <= pc_pos; i++) { - if (tcg_ctx.gen_opc_instr_start[i]) { - qemu_log("0x%04x: " TARGET_FMT_lx "\n", i, - tcg_ctx.gen_opc_pc[i]); - } - } - qemu_log("pc_pos=0x%x eip=" TARGET_FMT_lx " cs_base=%x\n", - pc_pos, tcg_ctx.gen_opc_pc[pc_pos] - tb->cs_base, - (uint32_t)tb->cs_base); - } -#endif - env->eip = tcg_ctx.gen_opc_pc[pc_pos] - tb->cs_base; - cc_op = gen_opc_cc_op[pc_pos]; - if (cc_op != CC_OP_DYNAMIC) + int cc_op = data[1]; + env->eip = data[0] - tb->cs_base; + if (cc_op != CC_OP_DYNAMIC) { env->cc_op = cc_op; + } } diff --git a/qemu/target-lm32/cpu.c b/qemu/target-lm32/cpu.c index c2b77c698..6e7e1b8e6 100644 --- a/qemu/target-lm32/cpu.c +++ b/qemu/target-lm32/cpu.c @@ -18,6 +18,8 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "qemu-common.h" @@ -131,6 +133,12 @@ static void lm32_cpu_reset(CPUState *s) tlb_flush(s, 1); } +static void lm32_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) +{ + info->mach = bfd_mach_lm32; + info->print_insn = print_insn_lm32; +} + static void lm32_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); @@ -275,6 +283,14 @@ static void lm32_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_num_core_regs = 32 + 7; cc->gdb_stop_before_watchpoint = true; cc->debug_excp_handler = lm32_debug_excp_handler; + cc->disas_set_info = lm32_cpu_disas_set_info; + + /* + * Reason: lm32_cpu_initfn() calls cpu_exec_init(), which saves + * the object in cpus -> dangling pointer after final + * object_unref(). + */ + dc->cannot_destroy_with_object_finalize_yet = true; } static void lm32_register_cpu_type(const LM32CPUInfo *info) diff --git a/qemu/target-lm32/cpu.h b/qemu/target-lm32/cpu.h index 944777d05..f220fc0bb 100644 --- a/qemu/target-lm32/cpu.h +++ b/qemu/target-lm32/cpu.h @@ -24,17 +24,14 @@ #define CPUArchState struct CPULM32State -#include "config.h" #include "qemu-common.h" #include "exec/cpu-defs.h" struct CPULM32State; typedef struct CPULM32State CPULM32State; -#define ELF_MACHINE EM_LATTICEMICO32 - #define NB_MMU_MODES 1 #define TARGET_PAGE_BITS 12 -static inline int cpu_mmu_index(CPULM32State *env) +static inline int cpu_mmu_index(CPULM32State *env, bool ifetch) { return 0; } @@ -221,7 +218,6 @@ bool lm32_cpu_do_semihosting(CPUState *cs); #define cpu_list lm32_cpu_list #define cpu_exec cpu_lm32_exec -#define cpu_gen_code cpu_lm32_gen_code #define cpu_signal_handler cpu_lm32_signal_handler int lm32_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw, diff --git a/qemu/target-lm32/gdbstub.c b/qemu/target-lm32/gdbstub.c index 4979a98d7..8ac1288bb 100644 --- a/qemu/target-lm32/gdbstub.c +++ b/qemu/target-lm32/gdbstub.c @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "exec/gdbstub.h" #include "hw/lm32/lm32_pic.h" diff --git a/qemu/target-lm32/helper.c b/qemu/target-lm32/helper.c index a88aa5ad1..655248f81 100644 --- a/qemu/target-lm32/helper.c +++ b/qemu/target-lm32/helper.c @@ -17,10 +17,12 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "qemu/host-utils.h" #include "sysemu/sysemu.h" #include "exec/semihost.h" +#include "exec/log.h" int lm32_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, int mmu_idx) @@ -81,7 +83,7 @@ void lm32_watchpoint_insert(CPULM32State *env, int idx, target_ulong address, switch (wp_type) { case LM32_WP_DISABLED: - /* nothing to to */ + /* nothing to do */ break; case LM32_WP_READ: flags = BP_CPU | BP_STOP_BEFORE_ACCESS | BP_MEM_READ; diff --git a/qemu/target-lm32/lm32-semi.c b/qemu/target-lm32/lm32-semi.c index ec6524f37..20f1a1cd4 100644 --- a/qemu/target-lm32/lm32-semi.c +++ b/qemu/target-lm32/lm32-semi.c @@ -10,10 +10,7 @@ * See the COPYING file in the top-level directory. */ -#include -#include -#include -#include +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/log.h" diff --git a/qemu/target-lm32/machine.c b/qemu/target-lm32/machine.c index 8327c6d97..91c943d19 100644 --- a/qemu/target-lm32/machine.c +++ b/qemu/target-lm32/machine.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/boards.h" diff --git a/qemu/target-lm32/op_helper.c b/qemu/target-lm32/op_helper.c index 61209c19b..b6759e022 100644 --- a/qemu/target-lm32/op_helper.c +++ b/qemu/target-lm32/op_helper.c @@ -1,4 +1,4 @@ -#include +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" diff --git a/qemu/target-lm32/translate.c b/qemu/target-lm32/translate.c index cf7042e3e..256a51f84 100644 --- a/qemu/target-lm32/translate.c +++ b/qemu/target-lm32/translate.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "disas/disas.h" #include "exec/helper-proto.h" @@ -28,6 +29,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" #define DISAS_LM32 1 @@ -42,7 +44,7 @@ #define MEM_INDEX 0 -static TCGv_ptr cpu_env; +static TCGv_env cpu_env; static TCGv cpu_R[32]; static TCGv cpu_pc; static TCGv cpu_ie; @@ -1005,10 +1007,6 @@ static const DecoderInfo decinfo[] = { static inline void decode(DisasContext *dc, uint32_t ir) { - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { - tcg_gen_debug_insn_start(dc->pc); - } - dc->ir = ir; LOG_DIS("%8.8x\t", dc->ir); @@ -1036,32 +1034,13 @@ static inline void decode(DisasContext *dc, uint32_t ir) decinfo[dc->opcode](dc); } -static void check_breakpoint(CPULM32State *env, DisasContext *dc) -{ - CPUState *cs = CPU(lm32_env_get_cpu(env)); - CPUBreakpoint *bp; - - if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { - QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - if (bp->pc == dc->pc) { - tcg_gen_movi_tl(cpu_pc, dc->pc); - t_gen_raise_exception(dc, EXCP_DEBUG); - dc->is_jmp = DISAS_UPDATE; - } - } - } -} - /* generate intermediate code for basic block 'tb'. */ -static inline -void gen_intermediate_code_internal(LM32CPU *cpu, - TranslationBlock *tb, bool search_pc) +void gen_intermediate_code(CPULM32State *env, struct TranslationBlock *tb) { + LM32CPU *cpu = lm32_env_get_cpu(env); CPUState *cs = CPU(cpu); - CPULM32State *env = &cpu->env; struct DisasContext ctx, *dc = &ctx; uint32_t pc_start; - int j, lj; uint32_t next_page_start; int num_insns; int max_insns; @@ -1083,41 +1062,41 @@ void gen_intermediate_code_internal(LM32CPU *cpu, } next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; - lj = -1; num_insns = 0; max_insns = tb->cflags & CF_COUNT_MASK; if (max_insns == 0) { max_insns = CF_COUNT_MASK; } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } gen_tb_start(tb); do { - check_breakpoint(env, dc); - - if (search_pc) { - j = tcg_op_buf_count(); - if (lj < j) { - lj++; - while (lj < j) { - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - } - tcg_ctx.gen_opc_pc[lj] = dc->pc; - tcg_ctx.gen_opc_instr_start[lj] = 1; - tcg_ctx.gen_opc_icount[lj] = num_insns; + tcg_gen_insn_start(dc->pc); + num_insns++; + + if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { + tcg_gen_movi_tl(cpu_pc, dc->pc); + t_gen_raise_exception(dc, EXCP_DEBUG); + dc->is_jmp = DISAS_UPDATE; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + dc->pc += 4; + break; } /* Pretty disas. */ LOG_DIS("%8.8x:\t", dc->pc); - if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) { + if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { gen_io_start(); } decode(dc, cpu_ldl_code(env, dc->pc)); dc->pc += 4; - num_insns++; - } while (!dc->is_jmp && !tcg_op_buf_full() && !cs->singlestep_enabled @@ -1154,16 +1133,8 @@ void gen_intermediate_code_internal(LM32CPU *cpu, gen_tb_end(tb, num_insns); - if (search_pc) { - j = tcg_op_buf_count(); - lj++; - while (lj <= j) { - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - } else { - tb->size = dc->pc - pc_start; - tb->icount = num_insns; - } + tb->size = dc->pc - pc_start; + tb->icount = num_insns; #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { @@ -1175,16 +1146,6 @@ void gen_intermediate_code_internal(LM32CPU *cpu, #endif } -void gen_intermediate_code(CPULM32State *env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(lm32_env_get_cpu(env), tb, false); -} - -void gen_intermediate_code_pc(CPULM32State *env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(lm32_env_get_cpu(env), tb, true); -} - void lm32_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int flags) { @@ -1219,9 +1180,10 @@ void lm32_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, cpu_fprintf(f, "\n\n"); } -void restore_state_to_opc(CPULM32State *env, TranslationBlock *tb, int pc_pos) +void restore_state_to_opc(CPULM32State *env, TranslationBlock *tb, + target_ulong *data) { - env->pc = tcg_ctx.gen_opc_pc[pc_pos]; + env->pc = data[0]; } void lm32_translate_init(void) @@ -1231,48 +1193,48 @@ void lm32_translate_init(void) cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); for (i = 0; i < ARRAY_SIZE(cpu_R); i++) { - cpu_R[i] = tcg_global_mem_new(TCG_AREG0, + cpu_R[i] = tcg_global_mem_new(cpu_env, offsetof(CPULM32State, regs[i]), regnames[i]); } for (i = 0; i < ARRAY_SIZE(cpu_bp); i++) { - cpu_bp[i] = tcg_global_mem_new(TCG_AREG0, + cpu_bp[i] = tcg_global_mem_new(cpu_env, offsetof(CPULM32State, bp[i]), regnames[32+i]); } for (i = 0; i < ARRAY_SIZE(cpu_wp); i++) { - cpu_wp[i] = tcg_global_mem_new(TCG_AREG0, + cpu_wp[i] = tcg_global_mem_new(cpu_env, offsetof(CPULM32State, wp[i]), regnames[36+i]); } - cpu_pc = tcg_global_mem_new(TCG_AREG0, + cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPULM32State, pc), "pc"); - cpu_ie = tcg_global_mem_new(TCG_AREG0, + cpu_ie = tcg_global_mem_new(cpu_env, offsetof(CPULM32State, ie), "ie"); - cpu_icc = tcg_global_mem_new(TCG_AREG0, + cpu_icc = tcg_global_mem_new(cpu_env, offsetof(CPULM32State, icc), "icc"); - cpu_dcc = tcg_global_mem_new(TCG_AREG0, + cpu_dcc = tcg_global_mem_new(cpu_env, offsetof(CPULM32State, dcc), "dcc"); - cpu_cc = tcg_global_mem_new(TCG_AREG0, + cpu_cc = tcg_global_mem_new(cpu_env, offsetof(CPULM32State, cc), "cc"); - cpu_cfg = tcg_global_mem_new(TCG_AREG0, + cpu_cfg = tcg_global_mem_new(cpu_env, offsetof(CPULM32State, cfg), "cfg"); - cpu_eba = tcg_global_mem_new(TCG_AREG0, + cpu_eba = tcg_global_mem_new(cpu_env, offsetof(CPULM32State, eba), "eba"); - cpu_dc = tcg_global_mem_new(TCG_AREG0, + cpu_dc = tcg_global_mem_new(cpu_env, offsetof(CPULM32State, dc), "dc"); - cpu_deba = tcg_global_mem_new(TCG_AREG0, + cpu_deba = tcg_global_mem_new(cpu_env, offsetof(CPULM32State, deba), "deba"); } diff --git a/qemu/target-m68k/cpu.c b/qemu/target-m68k/cpu.c index 4f246da74..0b5f9a581 100644 --- a/qemu/target-m68k/cpu.c +++ b/qemu/target-m68k/cpu.c @@ -18,6 +18,8 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "qemu-common.h" #include "migration/vmstate.h" @@ -61,6 +63,11 @@ static void m68k_cpu_reset(CPUState *s) tlb_flush(s, 1); } +static void m68k_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) +{ + info->print_insn = print_insn_m68k; +} + /* CPU models */ static ObjectClass *m68k_cpu_class_by_name(const char *cpu_model) @@ -208,10 +215,19 @@ static void m68k_cpu_class_init(ObjectClass *c, void *data) #endif cc->cpu_exec_enter = m68k_cpu_exec_enter; cc->cpu_exec_exit = m68k_cpu_exec_exit; + cc->disas_set_info = m68k_cpu_disas_set_info; - dc->vmsd = &vmstate_m68k_cpu; cc->gdb_num_core_regs = 18; cc->gdb_core_xml_file = "cf-core.xml"; + + dc->vmsd = &vmstate_m68k_cpu; + + /* + * Reason: m68k_cpu_initfn() calls cpu_exec_init(), which saves + * the object in cpus -> dangling pointer after final + * object_unref(). + */ + dc->cannot_destroy_with_object_finalize_yet = true; } static void register_cpu_type(const M68kCPUInfo *info) diff --git a/qemu/target-m68k/cpu.h b/qemu/target-m68k/cpu.h index 9a62f6cb5..48b4c872f 100644 --- a/qemu/target-m68k/cpu.h +++ b/qemu/target-m68k/cpu.h @@ -24,7 +24,6 @@ #define CPUArchState struct CPUM68KState -#include "config.h" #include "qemu-common.h" #include "exec/cpu-defs.h" @@ -32,8 +31,6 @@ #define MAX_QREGS 32 -#define ELF_MACHINE EM_68K - #define EXCP_ACCESS 2 /* Access (MMU) error. */ #define EXCP_ADDRESS 3 /* Address error. */ #define EXCP_ILLEGAL 4 /* Illegal instruction. */ @@ -215,7 +212,6 @@ void register_m68k_insns (CPUM68KState *env); #define cpu_init(cpu_model) CPU(cpu_m68k_init(cpu_model)) #define cpu_exec cpu_m68k_exec -#define cpu_gen_code cpu_m68k_gen_code #define cpu_signal_handler cpu_m68k_signal_handler #define cpu_list m68k_cpu_list @@ -223,7 +219,7 @@ void register_m68k_insns (CPUM68KState *env); #define MMU_MODE0_SUFFIX _kernel #define MMU_MODE1_SUFFIX _user #define MMU_USER_IDX 1 -static inline int cpu_mmu_index (CPUM68KState *env) +static inline int cpu_mmu_index (CPUM68KState *env, bool ifetch) { return (env->sr & SR_S) == 0 ? 1 : 0; } diff --git a/qemu/target-m68k/gdbstub.c b/qemu/target-m68k/gdbstub.c index ae8179c01..f02bb5caf 100644 --- a/qemu/target-m68k/gdbstub.c +++ b/qemu/target-m68k/gdbstub.c @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "exec/gdbstub.h" diff --git a/qemu/target-m68k/helper.c b/qemu/target-m68k/helper.c index 77225a200..a8f6d9d6a 100644 --- a/qemu/target-m68k/helper.c +++ b/qemu/target-m68k/helper.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/gdbstub.h" diff --git a/qemu/target-m68k/m68k-semi.c b/qemu/target-m68k/m68k-semi.c index 9dffe8de6..f360ef3e1 100644 --- a/qemu/target-m68k/m68k-semi.c +++ b/qemu/target-m68k/m68k-semi.c @@ -17,15 +17,7 @@ * along with this program; if not, see . */ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include "cpu.h" #if defined(CONFIG_USER_ONLY) diff --git a/qemu/target-m68k/op_helper.c b/qemu/target-m68k/op_helper.c index 1af0ca647..17d0a1191 100644 --- a/qemu/target-m68k/op_helper.c +++ b/qemu/target-m68k/op_helper.c @@ -16,6 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" diff --git a/qemu/target-m68k/translate.c b/qemu/target-m68k/translate.c index a57d2415c..7560c3a80 100644 --- a/qemu/target-m68k/translate.c +++ b/qemu/target-m68k/translate.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "disas/disas.h" #include "tcg-op.h" @@ -28,6 +29,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" //#define DEBUG_DISPATCH 1 @@ -48,7 +50,7 @@ static TCGv_i32 cpu_halted; static TCGv_i32 cpu_exception_index; -static TCGv_ptr cpu_env; +static TCGv_env cpu_env; static char cpu_reg_names[3*8*3 + 5*4]; static TCGv cpu_dregs[8]; @@ -74,48 +76,52 @@ void m68k_tcg_init(void) char *p; int i; -#define DEFO32(name, offset) QREG_##name = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUM68KState, offset), #name); -#define DEFO64(name, offset) QREG_##name = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUM68KState, offset), #name); -#define DEFF64(name, offset) DEFO64(name, offset) + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + +#define DEFO32(name, offset) \ + QREG_##name = tcg_global_mem_new_i32(cpu_env, \ + offsetof(CPUM68KState, offset), #name); +#define DEFO64(name, offset) \ + QREG_##name = tcg_global_mem_new_i64(cpu_env, \ + offsetof(CPUM68KState, offset), #name); +#define DEFF64(name, offset) DEFO64(name, offset) #include "qregs.def" #undef DEFO32 #undef DEFO64 #undef DEFF64 - cpu_halted = tcg_global_mem_new_i32(TCG_AREG0, + cpu_halted = tcg_global_mem_new_i32(cpu_env, -offsetof(M68kCPU, env) + offsetof(CPUState, halted), "HALTED"); - cpu_exception_index = tcg_global_mem_new_i32(TCG_AREG0, + cpu_exception_index = tcg_global_mem_new_i32(cpu_env, -offsetof(M68kCPU, env) + offsetof(CPUState, exception_index), "EXCEPTION"); - cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); - p = cpu_reg_names; for (i = 0; i < 8; i++) { sprintf(p, "D%d", i); - cpu_dregs[i] = tcg_global_mem_new(TCG_AREG0, + cpu_dregs[i] = tcg_global_mem_new(cpu_env, offsetof(CPUM68KState, dregs[i]), p); p += 3; sprintf(p, "A%d", i); - cpu_aregs[i] = tcg_global_mem_new(TCG_AREG0, + cpu_aregs[i] = tcg_global_mem_new(cpu_env, offsetof(CPUM68KState, aregs[i]), p); p += 3; sprintf(p, "F%d", i); - cpu_fregs[i] = tcg_global_mem_new_i64(TCG_AREG0, + cpu_fregs[i] = tcg_global_mem_new_i64(cpu_env, offsetof(CPUM68KState, fregs[i]), p); p += 3; } for (i = 0; i < 4; i++) { sprintf(p, "ACC%d", i); - cpu_macc[i] = tcg_global_mem_new_i64(TCG_AREG0, + cpu_macc[i] = tcg_global_mem_new_i64(cpu_env, offsetof(CPUM68KState, macc[i]), p); p += 5; } - NULL_QREG = tcg_global_mem_new(TCG_AREG0, -4, "NULL"); - store_dummy = tcg_global_mem_new(TCG_AREG0, -8, "NULL"); + NULL_QREG = tcg_global_mem_new(cpu_env, -4, "NULL"); + store_dummy = tcg_global_mem_new(cpu_env, -8, "NULL"); } /* internal defines */ @@ -2680,7 +2686,7 @@ DISAS_INSN(from_mac) if (s->env->macsr & MACSR_FI) { gen_helper_get_macf(rx, cpu_env, acc); } else if ((s->env->macsr & MACSR_OMC) == 0) { - tcg_gen_trunc_i64_i32(rx, acc); + tcg_gen_extrl_i64_i32(rx, acc); } else if (s->env->macsr & MACSR_SU) { gen_helper_get_macs(rx, acc); } else { @@ -2955,10 +2961,6 @@ static void disas_m68k_insn(CPUM68KState * env, DisasContext *s) { uint16_t insn; - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { - tcg_gen_debug_insn_start(s->pc); - } - insn = cpu_lduw_code(env, s->pc); s->pc += 2; @@ -2966,15 +2968,11 @@ static void disas_m68k_insn(CPUM68KState * env, DisasContext *s) } /* generate intermediate code for basic block 'tb'. */ -static inline void -gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb, - bool search_pc) +void gen_intermediate_code(CPUM68KState *env, TranslationBlock *tb) { + M68kCPU *cpu = m68k_env_get_cpu(env); CPUState *cs = CPU(cpu); - CPUM68KState *env = &cpu->env; DisasContext dc1, *dc = &dc1; - CPUBreakpoint *bp; - int j, lj; target_ulong pc_start; int pc_offset; int num_insns; @@ -2993,43 +2991,39 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb, dc->fpcr = env->fpcr; dc->user = (env->sr & SR_S) == 0; dc->done_mac = 0; - lj = -1; num_insns = 0; max_insns = tb->cflags & CF_COUNT_MASK; - if (max_insns == 0) + if (max_insns == 0) { max_insns = CF_COUNT_MASK; + } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } gen_tb_start(tb); do { pc_offset = dc->pc - pc_start; gen_throws_exception = NULL; - if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { - QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - if (bp->pc == dc->pc) { - gen_exception(dc, dc->pc, EXCP_DEBUG); - dc->is_jmp = DISAS_JUMP; - break; - } - } - if (dc->is_jmp) - break; - } - if (search_pc) { - j = tcg_op_buf_count(); - if (lj < j) { - lj++; - while (lj < j) - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - tcg_ctx.gen_opc_pc[lj] = dc->pc; - tcg_ctx.gen_opc_instr_start[lj] = 1; - tcg_ctx.gen_opc_icount[lj] = num_insns; + tcg_gen_insn_start(dc->pc); + num_insns++; + + if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { + gen_exception(dc, dc->pc, EXCP_DEBUG); + dc->is_jmp = DISAS_JUMP; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + dc->pc += 2; + break; } - if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) + + if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { gen_io_start(); + } + dc->insn_pc = dc->pc; disas_m68k_insn(env, dc); - num_insns++; } while (!dc->is_jmp && !tcg_op_buf_full() && !cs->singlestep_enabled && !singlestep && @@ -3073,28 +3067,8 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb, qemu_log("\n"); } #endif - if (search_pc) { - j = tcg_op_buf_count(); - lj++; - while (lj <= j) - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } else { - tb->size = dc->pc - pc_start; - tb->icount = num_insns; - } - - //optimize_flags(); - //expand_target_qops(); -} - -void gen_intermediate_code(CPUM68KState *env, TranslationBlock *tb) -{ - gen_intermediate_code_internal(m68k_env_get_cpu(env), tb, false); -} - -void gen_intermediate_code_pc(CPUM68KState *env, TranslationBlock *tb) -{ - gen_intermediate_code_internal(m68k_env_get_cpu(env), tb, true); + tb->size = dc->pc - pc_start; + tb->icount = num_insns; } void m68k_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, @@ -3120,7 +3094,8 @@ void m68k_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, cpu_fprintf (f, "FPRESULT = %12g\n", *(double *)&env->fp_result); } -void restore_state_to_opc(CPUM68KState *env, TranslationBlock *tb, int pc_pos) +void restore_state_to_opc(CPUM68KState *env, TranslationBlock *tb, + target_ulong *data) { - env->pc = tcg_ctx.gen_opc_pc[pc_pos]; + env->pc = data[0]; } diff --git a/qemu/target-microblaze/cpu.c b/qemu/target-microblaze/cpu.c index 9ac509af3..fdfb01917 100644 --- a/qemu/target-microblaze/cpu.c +++ b/qemu/target-microblaze/cpu.c @@ -21,6 +21,8 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "qemu-common.h" #include "hw/qdev-properties.h" @@ -107,6 +109,8 @@ static void mb_cpu_reset(CPUState *s) /* Disable stack protector. */ env->shr = ~0; + env->sregs[SR_PC] = cpu->cfg.base_vectors; + #if defined(CONFIG_USER_ONLY) /* start in user mode with interrupts enabled. */ env->sregs[SR_MSR] = MSR_EE | MSR_IE | MSR_VM | MSR_UM; @@ -183,8 +187,6 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) env->pvr.regs[10] = 0x0c000000; /* Default to spartan 3a dsp family. */ env->pvr.regs[11] = PVR11_USE_MMU | (16 << 17); - env->sregs[SR_PC] = cpu->cfg.base_vectors; - mcc->parent_realize(dev, errp); } @@ -264,6 +266,12 @@ static void mb_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_num_core_regs = 32 + 5; cc->disas_set_info = mb_disas_set_info; + + /* + * Reason: mb_cpu_initfn() calls cpu_exec_init(), which saves the + * object in cpus -> dangling pointer after final object_unref(). + */ + dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo mb_cpu_type_info = { diff --git a/qemu/target-microblaze/cpu.h b/qemu/target-microblaze/cpu.h index 7e20e59b8..2f7335eaa 100644 --- a/qemu/target-microblaze/cpu.h +++ b/qemu/target-microblaze/cpu.h @@ -19,7 +19,6 @@ #ifndef CPU_MICROBLAZE_H #define CPU_MICROBLAZE_H -#include "config.h" #include "qemu-common.h" #define TARGET_LONG_BITS 32 @@ -34,8 +33,6 @@ typedef struct CPUMBState CPUMBState; #include "mmu.h" #endif -#define ELF_MACHINE EM_MICROBLAZE - #define EXCP_MMU 1 #define EXCP_IRQ 2 #define EXCP_BREAK 3 @@ -297,7 +294,6 @@ int cpu_mb_signal_handler(int host_signum, void *pinfo, #define cpu_init(cpu_model) CPU(cpu_mb_init(cpu_model)) #define cpu_exec cpu_mb_exec -#define cpu_gen_code cpu_mb_gen_code #define cpu_signal_handler cpu_mb_signal_handler /* MMU modes definitions */ @@ -309,7 +305,7 @@ int cpu_mb_signal_handler(int host_signum, void *pinfo, #define MMU_USER_IDX 2 /* See NB_MMU_MODES further up the file. */ -static inline int cpu_mmu_index (CPUMBState *env) +static inline int cpu_mmu_index (CPUMBState *env, bool ifetch) { /* Are we in nommu mode?. */ if (!(env->sregs[SR_MSR] & MSR_VM)) diff --git a/qemu/target-microblaze/gdbstub.c b/qemu/target-microblaze/gdbstub.c index a70e2ee3c..89d38980b 100644 --- a/qemu/target-microblaze/gdbstub.c +++ b/qemu/target-microblaze/gdbstub.c @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "exec/gdbstub.h" diff --git a/qemu/target-microblaze/helper.c b/qemu/target-microblaze/helper.c index 8257b0e0f..4de6bdbf8 100644 --- a/qemu/target-microblaze/helper.c +++ b/qemu/target-microblaze/helper.c @@ -18,8 +18,10 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "qemu/host-utils.h" +#include "exec/log.h" #define D(x) @@ -128,7 +130,7 @@ void mb_cpu_do_interrupt(CPUState *cs) switch (cs->exception_index) { case EXCP_HW_EXCP: if (!(env->pvr.regs[0] & PVR0_USE_EXC_MASK)) { - qemu_log("Exception raised on system without exceptions!\n"); + qemu_log_mask(LOG_GUEST_ERROR, "Exception raised on system without exceptions!\n"); return; } diff --git a/qemu/target-microblaze/mmu.c b/qemu/target-microblaze/mmu.c index 728da133d..4ac304035 100644 --- a/qemu/target-microblaze/mmu.c +++ b/qemu/target-microblaze/mmu.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #define D(x) @@ -60,7 +61,7 @@ static void mmu_change_pid(CPUMBState *env, unsigned int newpid) uint32_t t; if (newpid & ~0xff) - qemu_log("Illegal rpid=%x\n", newpid); + qemu_log_mask(LOG_GUEST_ERROR, "Illegal rpid=%x\n", newpid); for (i = 0; i < ARRAY_SIZE(mmu->rams[RAM_TAG]); i++) { /* Lookup and decode. */ @@ -121,7 +122,7 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu, t0 &= 0x3; if (tlb_zsel > mmu->c_mmu_zones) { - qemu_log("tlb zone select out of range! %d\n", tlb_zsel); + qemu_log_mask(LOG_GUEST_ERROR, "tlb zone select out of range! %d\n", tlb_zsel); t0 = 1; /* Ignore. */ } @@ -183,7 +184,7 @@ uint32_t mmu_read(CPUMBState *env, uint32_t rn) uint32_t r; if (env->mmu.c_mmu < 2 || !env->mmu.c_mmu_tlb_access) { - qemu_log("MMU access on MMU-less system\n"); + qemu_log_mask(LOG_GUEST_ERROR, "MMU access on MMU-less system\n"); return 0; } @@ -192,7 +193,7 @@ uint32_t mmu_read(CPUMBState *env, uint32_t rn) case MMU_R_TLBLO: case MMU_R_TLBHI: if (!(env->mmu.c_mmu_tlb_access & 1)) { - qemu_log("Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); return 0; } @@ -204,7 +205,7 @@ uint32_t mmu_read(CPUMBState *env, uint32_t rn) case MMU_R_PID: case MMU_R_ZPR: if (!(env->mmu.c_mmu_tlb_access & 1)) { - qemu_log("Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); return 0; } r = env->mmu.regs[rn]; @@ -224,7 +225,7 @@ void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) D(qemu_log("%s rn=%d=%x old=%x\n", __func__, rn, v, env->mmu.regs[rn])); if (env->mmu.c_mmu < 2 || !env->mmu.c_mmu_tlb_access) { - qemu_log("MMU access on MMU-less system\n"); + qemu_log_mask(LOG_GUEST_ERROR, "MMU access on MMU-less system\n"); return; } @@ -235,7 +236,7 @@ void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) i = env->mmu.regs[MMU_R_TLBX] & 0xff; if (rn == MMU_R_TLBHI) { if (i < 3 && !(v & TLB_VALID) && qemu_loglevel_mask(~0)) - qemu_log("invalidating index %x at pc=%x\n", + qemu_log_mask(LOG_GUEST_ERROR, "invalidating index %x at pc=%x\n", i, env->sregs[SR_PC]); env->mmu.tids[i] = env->mmu.regs[MMU_R_PID] & 0xff; mmu_flush_idx(env, i); @@ -246,7 +247,7 @@ void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) break; case MMU_R_ZPR: if (env->mmu.c_mmu_tlb_access <= 1) { - qemu_log("Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); return; } @@ -259,7 +260,7 @@ void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) break; case MMU_R_PID: if (env->mmu.c_mmu_tlb_access <= 1) { - qemu_log("Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); return; } @@ -274,12 +275,12 @@ void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) int hit; if (env->mmu.c_mmu_tlb_access <= 1) { - qemu_log("Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); return; } hit = mmu_translate(&env->mmu, &lu, - v & TLB_EPN_MASK, 0, cpu_mmu_index(env)); + v & TLB_EPN_MASK, 0, cpu_mmu_index(env, false)); if (hit) { env->mmu.regs[MMU_R_TLBX] = lu.idx; } else diff --git a/qemu/target-microblaze/op_helper.c b/qemu/target-microblaze/op_helper.c index d2b362451..97333881f 100644 --- a/qemu/target-microblaze/op_helper.c +++ b/qemu/target-microblaze/op_helper.c @@ -18,7 +18,7 @@ * License along with this library; if not, see . */ -#include +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" @@ -56,7 +56,7 @@ void helper_put(uint32_t id, uint32_t ctrl, uint32_t data) int nonblock = ctrl & STREAM_NONBLOCK; int exception = ctrl & STREAM_EXCEPTION; - qemu_log("Unhandled stream put to stream-id=%d data=%x %s%s%s%s%s\n", + qemu_log_mask(LOG_UNIMP, "Unhandled stream put to stream-id=%d data=%x %s%s%s%s%s\n", id, data, test ? "t" : "", nonblock ? "n" : "", @@ -73,7 +73,7 @@ uint32_t helper_get(uint32_t id, uint32_t ctrl) int nonblock = ctrl & STREAM_NONBLOCK; int exception = ctrl & STREAM_EXCEPTION; - qemu_log("Unhandled stream get from stream-id=%d %s%s%s%s%s\n", + qemu_log_mask(LOG_UNIMP, "Unhandled stream get from stream-id=%d %s%s%s%s%s\n", id, test ? "t" : "", nonblock ? "n" : "", @@ -151,9 +151,7 @@ uint32_t helper_clz(uint32_t t0) uint32_t helper_carry(uint32_t a, uint32_t b, uint32_t cf) { - uint32_t ncf; - ncf = compute_carry(a, b, cf); - return ncf; + return compute_carry(a, b, cf); } static inline int div_prepare(CPUMBState *env, uint32_t a, uint32_t b) @@ -468,8 +466,8 @@ void helper_memalign(CPUMBState *env, uint32_t addr, uint32_t dr, uint32_t wr, void helper_stackprot(CPUMBState *env, uint32_t addr) { if (addr < env->slr || addr > env->shr) { - qemu_log("Stack protector violation at %x %x %x\n", - addr, env->slr, env->shr); + qemu_log_mask(CPU_LOG_INT, "Stack protector violation at %x %x %x\n", + addr, env->slr, env->shr); env->sregs[SR_EAR] = addr; env->sregs[SR_ESR] = ESR_EC_STACKPROT; helper_raise_exception(env, EXCP_HW_EXCP); diff --git a/qemu/target-microblaze/translate.c b/qemu/target-microblaze/translate.c index f4e969b29..f944965a1 100644 --- a/qemu/target-microblaze/translate.c +++ b/qemu/target-microblaze/translate.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "disas/disas.h" #include "tcg-op.h" @@ -27,6 +28,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" #define SIM_COMPAT 0 @@ -44,7 +46,7 @@ (((src) >> start) & ((1 << (end - start + 1)) - 1)) static TCGv env_debug; -static TCGv_ptr cpu_env; +static TCGv_env cpu_env; static TCGv cpu_R[32]; static TCGv cpu_SR[18]; static TCGv env_imm; @@ -313,7 +315,6 @@ static void dec_sub(DisasContext *dc) static void dec_pattern(DisasContext *dc) { unsigned int mode; - TCGLabel *l1; if ((dc->tb_flags & MSR_EE_FLAG) && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) @@ -333,29 +334,15 @@ static void dec_pattern(DisasContext *dc) case 2: LOG_DIS("pcmpeq r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); if (dc->rd) { - TCGv t0 = tcg_temp_local_new(); - l1 = gen_new_label(); - tcg_gen_movi_tl(t0, 1); - tcg_gen_brcond_tl(TCG_COND_EQ, - cpu_R[dc->ra], cpu_R[dc->rb], l1); - tcg_gen_movi_tl(t0, 0); - gen_set_label(l1); - tcg_gen_mov_tl(cpu_R[dc->rd], t0); - tcg_temp_free(t0); + tcg_gen_setcond_tl(TCG_COND_EQ, cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); } break; case 3: LOG_DIS("pcmpne r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - l1 = gen_new_label(); if (dc->rd) { - TCGv t0 = tcg_temp_local_new(); - tcg_gen_movi_tl(t0, 1); - tcg_gen_brcond_tl(TCG_COND_NE, - cpu_R[dc->ra], cpu_R[dc->rb], l1); - tcg_gen_movi_tl(t0, 0); - gen_set_label(l1); - tcg_gen_mov_tl(cpu_R[dc->rd], t0); - tcg_temp_free(t0); + tcg_gen_setcond_tl(TCG_COND_NE, cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); } break; default: @@ -433,7 +420,7 @@ static void dec_msr(DisasContext *dc) CPUState *cs = CPU(dc->cpu); TCGv t0, t1; unsigned int sr, to, rn; - int mem_index = cpu_mmu_index(&dc->cpu->env); + int mem_index = cpu_mmu_index(&dc->cpu->env, false); sr = dc->imm & ((1 << 14) - 1); to = dc->imm & (1 << 14); @@ -598,9 +585,9 @@ static void t_gen_muls(TCGv d, TCGv d2, TCGv a, TCGv b) tcg_gen_ext_i32_i64(t1, b); tcg_gen_mul_i64(t0, t0, t1); - tcg_gen_trunc_i64_i32(d, t0); + tcg_gen_extrl_i64_i32(d, t0); tcg_gen_shri_i64(t0, t0, 32); - tcg_gen_trunc_i64_i32(d2, t0); + tcg_gen_extrl_i64_i32(d2, t0); tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); @@ -618,9 +605,9 @@ static void t_gen_mulu(TCGv d, TCGv d2, TCGv a, TCGv b) tcg_gen_extu_i32_i64(t1, b); tcg_gen_mul_i64(t0, t0, t1); - tcg_gen_trunc_i64_i32(d, t0); + tcg_gen_extrl_i64_i32(d, t0); tcg_gen_shri_i64(t0, t0, 32); - tcg_gen_trunc_i64_i32(d2, t0); + tcg_gen_extrl_i64_i32(d2, t0); tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); @@ -745,7 +732,7 @@ static void dec_bit(DisasContext *dc) CPUState *cs = CPU(dc->cpu); TCGv t0; unsigned int op; - int mem_index = cpu_mmu_index(&dc->cpu->env); + int mem_index = cpu_mmu_index(&dc->cpu->env, false); op = dc->ir & ((1 << 9) - 1); switch (op) { @@ -1009,7 +996,7 @@ static void dec_load(DisasContext *dc) * address and if that succeeds we write into the destination reg. */ v = tcg_temp_new(); - tcg_gen_qemu_ld_tl(v, *addr, cpu_mmu_index(&dc->cpu->env), mop); + tcg_gen_qemu_ld_tl(v, *addr, cpu_mmu_index(&dc->cpu->env, false), mop); if ((dc->cpu->env.pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); @@ -1027,7 +1014,7 @@ static void dec_load(DisasContext *dc) tcg_temp_free(v); if (ex) { /* lwx */ - /* no support for for AXI exclusive so always clear C */ + /* no support for AXI exclusive so always clear C */ write_carryi(dc, 0); } @@ -1087,7 +1074,7 @@ static void dec_store(DisasContext *dc) this compare and the following write to be atomic. For user emulation we need to add atomicity between threads. */ tval = tcg_temp_new(); - tcg_gen_qemu_ld_tl(tval, swx_addr, cpu_mmu_index(&dc->cpu->env), + tcg_gen_qemu_ld_tl(tval, swx_addr, cpu_mmu_index(&dc->cpu->env, false), MO_TEUL); tcg_gen_brcond_tl(TCG_COND_NE, env_res_val, tval, swx_skip); write_carryi(dc, 0); @@ -1138,7 +1125,7 @@ static void dec_store(DisasContext *dc) break; } } - tcg_gen_qemu_st_tl(cpu_R[dc->rd], *addr, cpu_mmu_index(&dc->cpu->env), mop); + tcg_gen_qemu_st_tl(cpu_R[dc->rd], *addr, cpu_mmu_index(&dc->cpu->env, false), mop); /* Verify alignment if needed. */ if ((dc->cpu->env.pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { @@ -1234,7 +1221,7 @@ static void dec_bcc(DisasContext *dc) static void dec_br(DisasContext *dc) { unsigned int dslot, link, abs, mbar; - int mem_index = cpu_mmu_index(&dc->cpu->env); + int mem_index = cpu_mmu_index(&dc->cpu->env, false); dslot = dc->ir & (1 << 20); abs = dc->ir & (1 << 19); @@ -1366,7 +1353,7 @@ static inline void do_rte(DisasContext *dc) static void dec_rts(DisasContext *dc) { unsigned int b_bit, i_bit, e_bit; - int mem_index = cpu_mmu_index(&dc->cpu->env); + int mem_index = cpu_mmu_index(&dc->cpu->env, false); i_bit = dc->ir & (1 << 21); b_bit = dc->ir & (1 << 22); @@ -1531,14 +1518,14 @@ static void dec_null(DisasContext *dc) t_gen_raise_exception(dc, EXCP_HW_EXCP); return; } - qemu_log ("unknown insn pc=%x opc=%x\n", dc->pc, dc->opcode); + qemu_log_mask(LOG_GUEST_ERROR, "unknown insn pc=%x opc=%x\n", dc->pc, dc->opcode); dc->abort_at_next_insn = 1; } /* Insns connected to FSL or AXI stream attached devices. */ static void dec_stream(DisasContext *dc) { - int mem_index = cpu_mmu_index(&dc->cpu->env); + int mem_index = cpu_mmu_index(&dc->cpu->env, false); TCGv_i32 t_id, t_ctrl; int ctrl; @@ -1603,10 +1590,6 @@ static inline void decode(DisasContext *dc, uint32_t ir) { int i; - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { - tcg_gen_debug_insn_start(dc->pc); - } - dc->ir = ir; LOG_DIS("%8.8x\t", dc->ir); @@ -1645,30 +1628,12 @@ static inline void decode(DisasContext *dc, uint32_t ir) } } -static void check_breakpoint(CPUMBState *env, DisasContext *dc) -{ - CPUState *cs = CPU(mb_env_get_cpu(env)); - CPUBreakpoint *bp; - - if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { - QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - if (bp->pc == dc->pc) { - t_gen_raise_exception(dc, EXCP_DEBUG); - dc->is_jmp = DISAS_UPDATE; - } - } - } -} - /* generate intermediate code for basic block 'tb'. */ -static inline void -gen_intermediate_code_internal(MicroBlazeCPU *cpu, TranslationBlock *tb, - bool search_pc) +void gen_intermediate_code(CPUMBState *env, struct TranslationBlock *tb) { + MicroBlazeCPU *cpu = mb_env_get_cpu(env); CPUState *cs = CPU(cpu); - CPUMBState *env = &cpu->env; uint32_t pc_start; - int j, lj; struct DisasContext ctx; struct DisasContext *dc = &ctx; uint32_t next_page_start, org_flags; @@ -1705,47 +1670,51 @@ gen_intermediate_code_internal(MicroBlazeCPU *cpu, TranslationBlock *tb, } next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; - lj = -1; num_insns = 0; max_insns = tb->cflags & CF_COUNT_MASK; - if (max_insns == 0) + if (max_insns == 0) { max_insns = CF_COUNT_MASK; + } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } gen_tb_start(tb); do { + tcg_gen_insn_start(dc->pc); + num_insns++; + #if SIM_COMPAT if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); gen_helper_debug(); } #endif - check_breakpoint(env, dc); - - if (search_pc) { - j = tcg_op_buf_count(); - if (lj < j) { - lj++; - while (lj < j) - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - tcg_ctx.gen_opc_pc[lj] = dc->pc; - tcg_ctx.gen_opc_instr_start[lj] = 1; - tcg_ctx.gen_opc_icount[lj] = num_insns; + + if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { + t_gen_raise_exception(dc, EXCP_DEBUG); + dc->is_jmp = DISAS_UPDATE; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + dc->pc += 4; + break; } /* Pretty disas. */ LOG_DIS("%8.8x:\t", dc->pc); - if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) + if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { gen_io_start(); + } dc->clear_imm = 1; decode(dc, cpu_ldl_code(env, dc->pc)); if (dc->clear_imm) dc->tb_flags &= ~IMM_FLAG; dc->pc += 4; - num_insns++; if (dc->delayed_branch) { dc->delayed_branch--; @@ -1836,15 +1805,8 @@ gen_intermediate_code_internal(MicroBlazeCPU *cpu, TranslationBlock *tb, } gen_tb_end(tb, num_insns); - if (search_pc) { - j = tcg_op_buf_count(); - lj++; - while (lj <= j) - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } else { - tb->size = dc->pc - pc_start; - tb->icount = num_insns; - } + tb->size = dc->pc - pc_start; + tb->icount = num_insns; #ifdef DEBUG_DISAS #if !SIM_COMPAT @@ -1861,16 +1823,6 @@ gen_intermediate_code_internal(MicroBlazeCPU *cpu, TranslationBlock *tb, assert(!dc->abort_at_next_insn); } -void gen_intermediate_code (CPUMBState *env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(mb_env_get_cpu(env), tb, false); -} - -void gen_intermediate_code_pc (CPUMBState *env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(mb_env_get_cpu(env), tb, true); -} - void mb_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int flags) { @@ -1918,40 +1870,41 @@ void mb_tcg_init(void) cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); - env_debug = tcg_global_mem_new(TCG_AREG0, + env_debug = tcg_global_mem_new(cpu_env, offsetof(CPUMBState, debug), "debug0"); - env_iflags = tcg_global_mem_new(TCG_AREG0, + env_iflags = tcg_global_mem_new(cpu_env, offsetof(CPUMBState, iflags), "iflags"); - env_imm = tcg_global_mem_new(TCG_AREG0, + env_imm = tcg_global_mem_new(cpu_env, offsetof(CPUMBState, imm), "imm"); - env_btarget = tcg_global_mem_new(TCG_AREG0, + env_btarget = tcg_global_mem_new(cpu_env, offsetof(CPUMBState, btarget), "btarget"); - env_btaken = tcg_global_mem_new(TCG_AREG0, + env_btaken = tcg_global_mem_new(cpu_env, offsetof(CPUMBState, btaken), "btaken"); - env_res_addr = tcg_global_mem_new(TCG_AREG0, + env_res_addr = tcg_global_mem_new(cpu_env, offsetof(CPUMBState, res_addr), "res_addr"); - env_res_val = tcg_global_mem_new(TCG_AREG0, + env_res_val = tcg_global_mem_new(cpu_env, offsetof(CPUMBState, res_val), "res_val"); for (i = 0; i < ARRAY_SIZE(cpu_R); i++) { - cpu_R[i] = tcg_global_mem_new(TCG_AREG0, + cpu_R[i] = tcg_global_mem_new(cpu_env, offsetof(CPUMBState, regs[i]), regnames[i]); } for (i = 0; i < ARRAY_SIZE(cpu_SR); i++) { - cpu_SR[i] = tcg_global_mem_new(TCG_AREG0, + cpu_SR[i] = tcg_global_mem_new(cpu_env, offsetof(CPUMBState, sregs[i]), special_regnames[i]); } } -void restore_state_to_opc(CPUMBState *env, TranslationBlock *tb, int pc_pos) +void restore_state_to_opc(CPUMBState *env, TranslationBlock *tb, + target_ulong *data) { - env->sregs[SR_PC] = tcg_ctx.gen_opc_pc[pc_pos]; + env->sregs[SR_PC] = data[0]; } diff --git a/qemu/target-mips/cpu.c b/qemu/target-mips/cpu.c index 4027d0f41..0e2ecbebe 100644 --- a/qemu/target-mips/cpu.c +++ b/qemu/target-mips/cpu.c @@ -18,6 +18,8 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "kvm_mips.h" #include "qemu-common.h" @@ -53,12 +55,15 @@ static bool mips_cpu_has_work(CPUState *cs) CPUMIPSState *env = &cpu->env; bool has_work = false; - /* It is implementation dependent if non-enabled interrupts - wake-up the CPU, however most of the implementations only + /* Prior to MIPS Release 6 it is implementation dependent if non-enabled + interrupts wake-up the CPU, however most of the implementations only check for interrupts that can be taken. */ if ((cs->interrupt_request & CPU_INTERRUPT_HARD) && cpu_mips_hw_interrupts_pending(env)) { - has_work = true; + if (cpu_mips_hw_interrupts_enabled(env) || + (env->insn_flags & ISA_MIPS32R6)) { + has_work = true; + } } /* MIPS-MT has the ability to halt the CPU. */ @@ -73,6 +78,15 @@ static bool mips_cpu_has_work(CPUState *cs) has_work = false; } } + /* MIPS Release 6 has the ability to halt the CPU. */ + if (env->CP0_Config5 & (1 << CP0C5_VP)) { + if (cs->interrupt_request & CPU_INTERRUPT_WAKE) { + has_work = true; + } + if (!mips_vp_active(env)) { + has_work = false; + } + } return has_work; } @@ -97,6 +111,14 @@ static void mips_cpu_reset(CPUState *s) #endif } +static void mips_cpu_disas_set_info(CPUState *s, disassemble_info *info) { +#ifdef TARGET_WORDS_BIGENDIAN + info->print_insn = print_insn_big_mips; +#else + info->print_insn = print_insn_little_mips; +#endif +} + static void mips_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); @@ -150,9 +172,17 @@ static void mips_cpu_class_init(ObjectClass *c, void *data) cc->get_phys_page_debug = mips_cpu_get_phys_page_debug; cc->vmsd = &vmstate_mips_cpu; #endif + cc->disas_set_info = mips_cpu_disas_set_info; cc->gdb_num_core_regs = 73; cc->gdb_stop_before_watchpoint = true; + + /* + * Reason: mips_cpu_initfn() calls cpu_exec_init(), which saves + * the object in cpus -> dangling pointer after final + * object_unref(). + */ + dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo mips_cpu_type_info = { diff --git a/qemu/target-mips/cpu.h b/qemu/target-mips/cpu.h index 075c561c8..866924d18 100644 --- a/qemu/target-mips/cpu.h +++ b/qemu/target-mips/cpu.h @@ -5,11 +5,8 @@ #define ALIGNED_ONLY -#define ELF_MACHINE EM_MIPS - #define CPUArchState struct CPUMIPSState -#include "config.h" #include "qemu-common.h" #include "mips-defs.h" #include "exec/cpu-defs.h" @@ -21,19 +18,19 @@ typedef struct r4k_tlb_t r4k_tlb_t; struct r4k_tlb_t { target_ulong VPN; uint32_t PageMask; - uint_fast8_t ASID; - uint_fast16_t G:1; - uint_fast16_t C0:3; - uint_fast16_t C1:3; - uint_fast16_t V0:1; - uint_fast16_t V1:1; - uint_fast16_t D0:1; - uint_fast16_t D1:1; - uint_fast16_t XI0:1; - uint_fast16_t XI1:1; - uint_fast16_t RI0:1; - uint_fast16_t RI1:1; - uint_fast16_t EHINV:1; + uint8_t ASID; + unsigned int G:1; + unsigned int C0:3; + unsigned int C1:3; + unsigned int V0:1; + unsigned int V1:1; + unsigned int D0:1; + unsigned int D1:1; + unsigned int XI0:1; + unsigned int XI1:1; + unsigned int RI0:1; + unsigned int RI1:1; + unsigned int EHINV:1; uint64_t PFN[2]; }; @@ -102,6 +99,7 @@ struct CPUMIPSFPUContext { uint32_t fcr0; #define FCR0_FREP 29 #define FCR0_UFRP 28 +#define FCR0_HAS2008 23 #define FCR0_F64 22 #define FCR0_L 21 #define FCR0_W 20 @@ -113,6 +111,8 @@ struct CPUMIPSFPUContext { #define FCR0_REV 0 /* fcsr */ uint32_t fcr31; +#define FCR31_ABS2008 19 +#define FCR31_NAN2008 18 #define SET_FP_COND(num,env) do { ((env).fcr31) |= ((num) ? (1 << ((num) + 24)) : (1 << 23)); } while(0) #define CLEAR_FP_COND(num,env) do { ((env).fcr31) &= ~((num) ? (1 << ((num) + 24)) : (1 << 23)); } while(0) #define GET_FP_COND(env) ((((env).fcr31 >> 24) & 0xfe) | (((env).fcr31 >> 23) & 0x1)) @@ -132,6 +132,7 @@ struct CPUMIPSFPUContext { }; #define NB_MMU_MODES 3 +#define TARGET_INSN_START_EXTRA_WORDS 2 typedef struct CPUMIPSMVPContext CPUMIPSMVPContext; struct CPUMIPSMVPContext { @@ -164,6 +165,7 @@ typedef struct mips_def_t mips_def_t; #define MIPS_FPU_MAX 1 #define MIPS_DSP_ACC 4 #define MIPS_KSCRATCH_NUM 6 +#define MIPS_MAAR_MAX 16 /* Must be an even number. */ typedef struct TCState TCState; struct TCState { @@ -239,6 +241,8 @@ struct CPUMIPSState { int32_t CP0_Index; /* CP0_MVP* are per MVP registers. */ + int32_t CP0_VPControl; +#define CP0VPCtl_DIS 0 int32_t CP0_Random; int32_t CP0_VPEControl; #define CP0VPECo_YSI 21 @@ -288,6 +292,8 @@ struct CPUMIPSState { # define CP0EnLo_RI 31 # define CP0EnLo_XI 30 #endif + int32_t CP0_GlobalNumber; +#define CP0GN_VPId 0 target_ulong CP0_Context; target_ulong CP0_KScratch[MIPS_KSCRATCH_NUM]; int32_t CP0_PageMask; @@ -359,7 +365,7 @@ struct CPUMIPSState { #define CP0St_IE 0 int32_t CP0_IntCtl; #define CP0IntCtl_IPTI 29 -#define CP0IntCtl_IPPC1 26 +#define CP0IntCtl_IPPCI 26 #define CP0IntCtl_VS 5 int32_t CP0_SRSCtl; #define CP0SRSCtl_HSS 26 @@ -390,12 +396,13 @@ struct CPUMIPSState { target_ulong CP0_EPC; int32_t CP0_PRid; int32_t CP0_EBase; + target_ulong CP0_CMGCRBase; int32_t CP0_Config0; #define CP0C0_M 31 #define CP0C0_K23 28 #define CP0C0_KU 25 #define CP0C0_MDU 20 -#define CP0C0_MM 17 +#define CP0C0_MM 18 #define CP0C0_BM 16 #define CP0C0_BE 15 #define CP0C0_AT 13 @@ -432,7 +439,7 @@ struct CPUMIPSState { int32_t CP0_Config3; #define CP0C3_M 31 #define CP0C3_BPG 30 -#define CP0C3_CMCGR 29 +#define CP0C3_CMGCR 29 #define CP0C3_MSAP 28 #define CP0C3_BP 27 #define CP0C3_BI 26 @@ -470,15 +477,20 @@ struct CPUMIPSState { #define CP0C5_CV 29 #define CP0C5_EVA 28 #define CP0C5_MSAEn 27 +#define CP0C5_XNP 13 #define CP0C5_UFE 9 #define CP0C5_FRE 8 +#define CP0C5_VP 7 #define CP0C5_SBRI 6 #define CP0C5_MVH 5 #define CP0C5_LLB 4 +#define CP0C5_MRP 3 #define CP0C5_UFR 2 #define CP0C5_NFExists 0 int32_t CP0_Config6; int32_t CP0_Config7; + uint64_t CP0_MAAR[MIPS_MAAR_MAX]; + int32_t CP0_MAARI; /* XXX: Maybe make LLAddr per-TC? */ uint64_t lladdr; target_ulong llval; @@ -511,6 +523,10 @@ struct CPUMIPSState { #define CP0DB_DSS 0 target_ulong CP0_DEPC; int32_t CP0_Performance0; + int32_t CP0_ErrCtl; +#define CP0EC_WST 29 +#define CP0EC_SPR 28 +#define CP0EC_ITC 26 uint64_t CP0_TagLo; int32_t CP0_DataLo; int32_t CP0_TagHi; @@ -526,7 +542,7 @@ struct CPUMIPSState { #define EXCP_INST_NOTAVAIL 0x2 /* No valid instruction word for BadInstr */ uint32_t hflags; /* CPU State */ /* TMASK defines different execution modes */ -#define MIPS_HFLAG_TMASK 0x75807FF +#define MIPS_HFLAG_TMASK 0xF5807FF #define MIPS_HFLAG_MODE 0x00007 /* execution modes */ /* The KSU flags must be the lowest bits in hflags. The flag order must be the same as defined for CP0 Status. This allows to use @@ -575,6 +591,7 @@ struct CPUMIPSState { #define MIPS_HFLAG_MSA 0x1000000 #define MIPS_HFLAG_FRE 0x2000000 /* FRE enabled */ #define MIPS_HFLAG_ELPA 0x4000000 +#define MIPS_HFLAG_ITC_CACHE 0x8000000 /* CACHE instr. operates on ITC tag */ target_ulong btarget; /* Jump / branch target */ target_ulong bcond; /* Branch condition (if needed) */ @@ -595,6 +612,7 @@ struct CPUMIPSState { const mips_def_t *cpu_model; void *irq[8]; QEMUTimer *timer; /* Internal timer */ + MemoryRegion *itc_tag; /* ITC Configuration Tags */ }; #include "cpu-qom.h" @@ -621,7 +639,6 @@ void mips_cpu_unassigned_access(CPUState *cpu, hwaddr addr, void mips_cpu_list (FILE *f, fprintf_function cpu_fprintf); #define cpu_exec cpu_mips_exec -#define cpu_gen_code cpu_mips_gen_code #define cpu_signal_handler cpu_mips_signal_handler #define cpu_list mips_cpu_list @@ -634,28 +651,29 @@ extern uint32_t cpu_rddsp(uint32_t mask_num, CPUMIPSState *env); #define MMU_MODE1_SUFFIX _super #define MMU_MODE2_SUFFIX _user #define MMU_USER_IDX 2 -static inline int cpu_mmu_index (CPUMIPSState *env) +static inline int cpu_mmu_index (CPUMIPSState *env, bool ifetch) { return env->hflags & MIPS_HFLAG_KSU; } -static inline int cpu_mips_hw_interrupts_pending(CPUMIPSState *env) +static inline bool cpu_mips_hw_interrupts_enabled(CPUMIPSState *env) { - int32_t pending; - int32_t status; - int r; - - if (!(env->CP0_Status & (1 << CP0St_IE)) || - (env->CP0_Status & (1 << CP0St_EXL)) || - (env->CP0_Status & (1 << CP0St_ERL)) || + return (env->CP0_Status & (1 << CP0St_IE)) && + !(env->CP0_Status & (1 << CP0St_EXL)) && + !(env->CP0_Status & (1 << CP0St_ERL)) && + !(env->hflags & MIPS_HFLAG_DM) && /* Note that the TCStatus IXMT field is initialized to zero, and only MT capable cores can set it to one. So we don't need to check for MT capabilities here. */ - (env->active_tc.CP0_TCStatus & (1 << CP0TCSt_IXMT)) || - (env->hflags & MIPS_HFLAG_DM)) { - /* Interrupts are disabled */ - return 0; - } + !(env->active_tc.CP0_TCStatus & (1 << CP0TCSt_IXMT)); +} + +/* Check if there is pending and not masked out interrupt */ +static inline bool cpu_mips_hw_interrupts_pending(CPUMIPSState *env) +{ + int32_t pending; + int32_t status; + bool r; pending = env->CP0_Cause & CP0Ca_IP_mask; status = env->CP0_Status & CP0Ca_IP_mask; @@ -669,7 +687,7 @@ static inline int cpu_mips_hw_interrupts_pending(CPUMIPSState *env) /* A MIPS configured with compatibility or VInt (Vectored Interrupts) treats the pending lines as individual interrupt lines, the status lines are individual masks. */ - r = pending & status; + r = (pending & status) != 0; } return r; } @@ -752,6 +770,7 @@ MIPSCPU *cpu_mips_init(const char *cpu_model); int cpu_mips_signal_handler(int host_signum, void *pinfo, void *puc); #define cpu_init(cpu_model) CPU(cpu_mips_init(cpu_model)) +bool cpu_supports_cps_smp(const char *cpu_model); /* TODO QOM'ify CPU reset and remove */ void cpu_state_reset(CPUMIPSState *s); @@ -859,6 +878,26 @@ static inline int mips_vpe_active(CPUMIPSState *env) return active; } +static inline int mips_vp_active(CPUMIPSState *env) +{ + CPUState *other_cs = first_cpu; + + /* Check if the VP disabled other VPs (which means the VP is enabled) */ + if ((env->CP0_VPControl >> CP0VPCtl_DIS) & 1) { + return 1; + } + + /* Check if the virtual processor is disabled due to a DVP */ + CPU_FOREACH(other_cs) { + MIPSCPU *other_cpu = MIPS_CPU(other_cs); + if ((&other_cpu->env != env) && + ((other_cpu->env.CP0_VPControl >> CP0VPCtl_DIS) & 1)) { + return 0; + } + } + return 1; +} + #include "exec/exec-all.h" static inline void compute_hflags(CPUMIPSState *env) @@ -961,6 +1000,15 @@ static inline void compute_hflags(CPUMIPSState *env) } #ifndef CONFIG_USER_ONLY +static inline void cpu_mips_tlb_flush(CPUMIPSState *env, int flush_global) +{ + MIPSCPU *cpu = mips_env_get_cpu(env); + + /* Flush qemu's TLB and discard all shadowed entries. */ + tlb_flush(CPU(cpu), flush_global); + env->tlb->tlb_in_use = env->tlb->nb_tlb; +} + /* Called for updates to CP0_Status. */ static inline void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc) { @@ -999,17 +1047,29 @@ static inline void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc) static inline void cpu_mips_store_status(CPUMIPSState *env, target_ulong val) { uint32_t mask = env->CP0_Status_rw_bitmask; + target_ulong old = env->CP0_Status; if (env->insn_flags & ISA_MIPS32R6) { bool has_supervisor = extract32(mask, CP0St_KSU, 2) == 0x3; - +#if defined(TARGET_MIPS64) + uint32_t ksux = (1 << CP0St_KX) & val; + ksux |= (ksux >> 1) & val; /* KX = 0 forces SX to be 0 */ + ksux |= (ksux >> 1) & val; /* SX = 0 forces UX to be 0 */ + val = (val & ~(7 << CP0St_UX)) | ksux; +#endif if (has_supervisor && extract32(val, CP0St_KSU, 2) == 0x3) { mask &= ~(3 << CP0St_KSU); } mask &= ~(((1 << CP0St_SR) | (1 << CP0St_NMI)) & val); } - env->CP0_Status = (env->CP0_Status & ~mask) | (val & mask); + env->CP0_Status = (old & ~mask) | (val & mask); +#if defined(TARGET_MIPS64) + if ((env->CP0_Status ^ old) & (old & (7 << CP0St_UX))) { + /* Access to at least one of the 64-bit segments has been disabled */ + cpu_mips_tlb_flush(env, 1); + } +#endif if (env->CP0_Config3 & (1 << CP0C3_MT)) { sync_c0_status(env, env, env->current_tc); } else { @@ -1049,4 +1109,28 @@ static inline void cpu_mips_store_cause(CPUMIPSState *env, target_ulong val) } #endif +static inline void QEMU_NORETURN do_raise_exception_err(CPUMIPSState *env, + uint32_t exception, + int error_code, + uintptr_t pc) +{ + CPUState *cs = CPU(mips_env_get_cpu(env)); + + if (exception < EXCP_SC) { + qemu_log_mask(CPU_LOG_INT, "%s: %d %d\n", + __func__, exception, error_code); + } + cs->exception_index = exception; + env->error_code = error_code; + + cpu_loop_exit_restore(cs, pc); +} + +static inline void QEMU_NORETURN do_raise_exception(CPUMIPSState *env, + uint32_t exception, + uintptr_t pc) +{ + do_raise_exception_err(env, exception, 0, pc); +} + #endif /* !defined (__MIPS_CPU_H__) */ diff --git a/qemu/target-mips/dsp_helper.c b/qemu/target-mips/dsp_helper.c index 46528de32..df7d2204b 100644 --- a/qemu/target-mips/dsp_helper.c +++ b/qemu/target-mips/dsp_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/bitops.h" diff --git a/qemu/target-mips/gdbstub.c b/qemu/target-mips/gdbstub.c index 9845d880a..b0b4a32ec 100644 --- a/qemu/target-mips/gdbstub.c +++ b/qemu/target-mips/gdbstub.c @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "exec/gdbstub.h" diff --git a/qemu/target-mips/helper.c b/qemu/target-mips/helper.c index 04ba19fd4..1004edee0 100644 --- a/qemu/target-mips/helper.c +++ b/qemu/target-mips/helper.c @@ -16,16 +16,12 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include "cpu.h" #include "sysemu/kvm.h" #include "exec/cpu_ldst.h" +#include "exec/log.h" enum { TLBRET_XI = -6, @@ -127,10 +123,6 @@ static int get_physical_address (CPUMIPSState *env, hwaddr *physical, /* effective address (modified for KVM T&E kernel segments) */ target_ulong address = real_address; -#if 0 - qemu_log("user mode %d h %08x\n", user_mode, env->hflags); -#endif - #define USEG_LIMIT 0x7FFFFFFFUL #define KSEG0_BASE 0x80000000UL #define KSEG1_BASE 0xA0000000UL @@ -227,11 +219,6 @@ static int get_physical_address (CPUMIPSState *env, hwaddr *physical, ret = TLBRET_BADADDR; } } -#if 0 - qemu_log(TARGET_FMT_lx " %d %d => %" HWADDR_PRIx " %d (%d)\n", - address, rw, access_type, *physical, *prot, ret); -#endif - return ret; } #endif @@ -303,9 +290,10 @@ static void raise_mmu_exception(CPUMIPSState *env, target_ulong address, (env->CP0_EntryHi & 0xFF) | (address & (TARGET_PAGE_MASK << 1)); #if defined(TARGET_MIPS64) env->CP0_EntryHi &= env->SEGMask; - env->CP0_XContext = (env->CP0_XContext & ((~0ULL) << (env->SEGBITS - 7))) | - ((address & 0xC00000000000ULL) >> (55 - env->SEGBITS)) | - ((address & ((1ULL << env->SEGBITS) - 1) & 0xFFFFFFFFFFFFE000ULL) >> 9); + env->CP0_XContext = + /* PTEBase */ (env->CP0_XContext & ((~0ULL) << (env->SEGBITS - 7))) | + /* R */ (extract64(address, 62, 2) << (env->SEGBITS - 9)) | + /* BadVPN2 */ (extract64(address, 13, env->SEGBITS - 13) << 4); #endif cs->exception_index = exception; env->error_code = error_code; @@ -487,14 +475,16 @@ void mips_cpu_do_interrupt(CPUState *cs) int cause = -1; const char *name; - if (qemu_log_enabled() && cs->exception_index != EXCP_EXT_INTERRUPT) { + if (qemu_loglevel_mask(CPU_LOG_INT) + && cs->exception_index != EXCP_EXT_INTERRUPT) { if (cs->exception_index < 0 || cs->exception_index > EXCP_LAST) { name = "unknown"; } else { name = excp_names[cs->exception_index]; } - qemu_log("%s enter: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " %s exception\n", + qemu_log("%s enter: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx + " %s exception\n", __func__, env->active_tc.PC, env->CP0_EPC, name); } if (cs->exception_index == EXCP_EXT_INTERRUPT && @@ -531,6 +521,10 @@ void mips_cpu_do_interrupt(CPUState *cs) enter_debug_mode: if (env->insn_flags & ISA_MIPS3) { env->hflags |= MIPS_HFLAG_64; + if (!(env->insn_flags & ISA_MIPS64R6) || + env->CP0_Status & (1 << CP0St_KX)) { + env->hflags &= ~MIPS_HFLAG_AWRAP; + } } env->hflags |= MIPS_HFLAG_DM | MIPS_HFLAG_CP0; env->hflags &= ~(MIPS_HFLAG_KSU); @@ -555,6 +549,10 @@ void mips_cpu_do_interrupt(CPUState *cs) env->CP0_Status |= (1 << CP0St_ERL) | (1 << CP0St_BEV); if (env->insn_flags & ISA_MIPS3) { env->hflags |= MIPS_HFLAG_64; + if (!(env->insn_flags & ISA_MIPS64R6) || + env->CP0_Status & (1 << CP0St_KX)) { + env->hflags &= ~MIPS_HFLAG_AWRAP; + } } env->hflags |= MIPS_HFLAG_CP0; env->hflags &= ~(MIPS_HFLAG_KSU); @@ -700,7 +698,7 @@ void mips_cpu_do_interrupt(CPUState *cs) goto set_EPC; case EXCP_DWATCH: cause = 23; - /* XXX: TODO: manage defered watch exceptions */ + /* XXX: TODO: manage deferred watch exceptions */ goto set_EPC; case EXCP_MCHECK: cause = 24; @@ -732,6 +730,10 @@ void mips_cpu_do_interrupt(CPUState *cs) env->CP0_Status |= (1 << CP0St_EXL); if (env->insn_flags & ISA_MIPS3) { env->hflags |= MIPS_HFLAG_64; + if (!(env->insn_flags & ISA_MIPS64R6) || + env->CP0_Status & (1 << CP0St_KX)) { + env->hflags &= ~MIPS_HFLAG_AWRAP; + } } env->hflags |= MIPS_HFLAG_CP0; env->hflags &= ~(MIPS_HFLAG_KSU); @@ -747,16 +749,15 @@ void mips_cpu_do_interrupt(CPUState *cs) env->CP0_Cause = (env->CP0_Cause & ~(0x1f << CP0Ca_EC)) | (cause << CP0Ca_EC); break; default: - qemu_log("Invalid MIPS exception %d. Exiting\n", cs->exception_index); - printf("Invalid MIPS exception %d. Exiting\n", cs->exception_index); - exit(1); + abort(); } - if (qemu_log_enabled() && cs->exception_index != EXCP_EXT_INTERRUPT) { + if (qemu_loglevel_mask(CPU_LOG_INT) + && cs->exception_index != EXCP_EXT_INTERRUPT) { qemu_log("%s: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " cause %d\n" - " S %08x C %08x A " TARGET_FMT_lx " D " TARGET_FMT_lx "\n", - __func__, env->active_tc.PC, env->CP0_EPC, cause, - env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr, - env->CP0_DEPC); + " S %08x C %08x A " TARGET_FMT_lx " D " TARGET_FMT_lx "\n", + __func__, env->active_tc.PC, env->CP0_EPC, cause, + env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr, + env->CP0_DEPC); } #endif cs->exception_index = EXCP_NONE; @@ -768,7 +769,8 @@ bool mips_cpu_exec_interrupt(CPUState *cs, int interrupt_request) MIPSCPU *cpu = MIPS_CPU(cs); CPUMIPSState *env = &cpu->env; - if (cpu_mips_hw_interrupts_pending(env)) { + if (cpu_mips_hw_interrupts_enabled(env) && + cpu_mips_hw_interrupts_pending(env)) { /* Raise it */ cs->exception_index = EXCP_EXT_INTERRUPT; env->error_code = 0; diff --git a/qemu/target-mips/helper.h b/qemu/target-mips/helper.h index 2b28e875c..594341d25 100644 --- a/qemu/target-mips/helper.h +++ b/qemu/target-mips/helper.h @@ -1,5 +1,6 @@ DEF_HELPER_3(raise_exception_err, noreturn, env, i32, int) DEF_HELPER_2(raise_exception, noreturn, env, i32) +DEF_HELPER_1(raise_exception_debug, noreturn, env) DEF_HELPER_1(do_semihosting, void, env) @@ -76,6 +77,8 @@ DEF_HELPER_1(mftc0_epc, tl, env) DEF_HELPER_1(mftc0_ebase, tl, env) DEF_HELPER_2(mftc0_configx, tl, env, tl) DEF_HELPER_1(mfc0_lladdr, tl, env) +DEF_HELPER_1(mfc0_maar, tl, env) +DEF_HELPER_1(mfhc0_maar, tl, env) DEF_HELPER_2(mfc0_watchlo, tl, env, i32) DEF_HELPER_2(mfc0_watchhi, tl, env, i32) DEF_HELPER_1(mfc0_debug, tl, env) @@ -87,6 +90,7 @@ DEF_HELPER_1(dmfc0_tccontext, tl, env) DEF_HELPER_1(dmfc0_tcschedule, tl, env) DEF_HELPER_1(dmfc0_tcschefback, tl, env) DEF_HELPER_1(dmfc0_lladdr, tl, env) +DEF_HELPER_1(dmfc0_maar, tl, env) DEF_HELPER_2(dmfc0_watchlo, tl, env, i32) #endif /* TARGET_MIPS64 */ @@ -143,6 +147,9 @@ DEF_HELPER_2(mtc0_config3, void, env, tl) DEF_HELPER_2(mtc0_config4, void, env, tl) DEF_HELPER_2(mtc0_config5, void, env, tl) DEF_HELPER_2(mtc0_lladdr, void, env, tl) +DEF_HELPER_2(mtc0_maar, void, env, tl) +DEF_HELPER_2(mthc0_maar, void, env, tl) +DEF_HELPER_2(mtc0_maari, void, env, tl) DEF_HELPER_3(mtc0_watchlo, void, env, tl, i32) DEF_HELPER_3(mtc0_watchhi, void, env, tl, i32) DEF_HELPER_2(mtc0_xcontext, void, env, tl) @@ -150,6 +157,7 @@ DEF_HELPER_2(mtc0_framemask, void, env, tl) DEF_HELPER_2(mtc0_debug, void, env, tl) DEF_HELPER_2(mttc0_debug, void, env, tl) DEF_HELPER_2(mtc0_performance0, void, env, tl) +DEF_HELPER_2(mtc0_errctl, void, env, tl) DEF_HELPER_2(mtc0_taglo, void, env, tl) DEF_HELPER_2(mtc0_datalo, void, env, tl) DEF_HELPER_2(mtc0_taghi, void, env, tl) @@ -175,6 +183,10 @@ DEF_HELPER_0(dmt, tl) DEF_HELPER_0(emt, tl) DEF_HELPER_1(dvpe, tl, env) DEF_HELPER_1(evpe, tl, env) + +/* R6 Multi-threading */ +DEF_HELPER_1(dvp, tl, env) +DEF_HELPER_1(evp, tl, env) #endif /* !CONFIG_USER_ONLY */ /* microMIPS functions */ @@ -357,6 +369,8 @@ DEF_HELPER_1(rdhwr_cpunum, tl, env) DEF_HELPER_1(rdhwr_synci_step, tl, env) DEF_HELPER_1(rdhwr_cc, tl, env) DEF_HELPER_1(rdhwr_ccres, tl, env) +DEF_HELPER_1(rdhwr_performance, tl, env) +DEF_HELPER_1(rdhwr_xnp, tl, env) DEF_HELPER_2(pmon, void, env, int) DEF_HELPER_1(wait, void, env) @@ -942,3 +956,5 @@ MSALDST_PROTO(h) MSALDST_PROTO(w) MSALDST_PROTO(d) #undef MSALDST_PROTO + +DEF_HELPER_3(cache, void, env, tl, i32) diff --git a/qemu/target-mips/kvm.c b/qemu/target-mips/kvm.c index d287d4288..950bc05b7 100644 --- a/qemu/target-mips/kvm.c +++ b/qemu/target-mips/kvm.c @@ -9,7 +9,7 @@ * Authors: Sanjay Lal */ -#include +#include "qemu/osdep.h" #include #include @@ -30,6 +30,9 @@ #define DPRINTF(fmt, ...) \ do { if (DEBUG_KVM) { fprintf(stderr, fmt, ## __VA_ARGS__); } } while (0) +static int kvm_mips_fpu_cap; +static int kvm_mips_msa_cap; + const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_LAST_INFO }; @@ -46,16 +49,39 @@ int kvm_arch_init(MachineState *ms, KVMState *s) /* MIPS has 128 signals */ kvm_set_sigmask_len(s, 16); + kvm_mips_fpu_cap = kvm_check_extension(s, KVM_CAP_MIPS_FPU); + kvm_mips_msa_cap = kvm_check_extension(s, KVM_CAP_MIPS_MSA); + DPRINTF("%s\n", __func__); return 0; } int kvm_arch_init_vcpu(CPUState *cs) { + MIPSCPU *cpu = MIPS_CPU(cs); + CPUMIPSState *env = &cpu->env; int ret = 0; qemu_add_vm_change_state_handler(kvm_mips_update_state, cs); + if (kvm_mips_fpu_cap && env->CP0_Config1 & (1 << CP0C1_FP)) { + ret = kvm_vcpu_enable_cap(cs, KVM_CAP_MIPS_FPU, 0, 0); + if (ret < 0) { + /* mark unsupported so it gets disabled on reset */ + kvm_mips_fpu_cap = 0; + ret = 0; + } + } + + if (kvm_mips_msa_cap && env->CP0_Config3 & (1 << CP0C3_MSAP)) { + ret = kvm_vcpu_enable_cap(cs, KVM_CAP_MIPS_MSA, 0, 0); + if (ret < 0) { + /* mark unsupported so it gets disabled on reset */ + kvm_mips_msa_cap = 0; + ret = 0; + } + } + DPRINTF("%s\n", __func__); return ret; } @@ -64,10 +90,14 @@ void kvm_mips_reset_vcpu(MIPSCPU *cpu) { CPUMIPSState *env = &cpu->env; - if (env->CP0_Config1 & (1 << CP0C1_FP)) { - fprintf(stderr, "Warning: FPU not supported with KVM, disabling\n"); + if (!kvm_mips_fpu_cap && env->CP0_Config1 & (1 << CP0C1_FP)) { + fprintf(stderr, "Warning: KVM does not support FPU, disabling\n"); env->CP0_Config1 &= ~(1 << CP0C1_FP); } + if (!kvm_mips_msa_cap && env->CP0_Config3 & (1 << CP0C3_MSAP)) { + fprintf(stderr, "Warning: KVM does not support MSA, disabling\n"); + env->CP0_Config3 &= ~(1 << CP0C3_MSAP); + } DPRINTF("%s\n", __func__); } @@ -88,7 +118,6 @@ static inline int cpu_mips_io_interrupts_pending(MIPSCPU *cpu) { CPUMIPSState *env = &cpu->env; - DPRINTF("%s: %#x\n", __func__, env->CP0_Cause & (1 << (2 + CP0Ca_IP))); return env->CP0_Cause & (0x1 << (2 + CP0Ca_IP)); } @@ -117,7 +146,6 @@ void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run) { - DPRINTF("%s\n", __func__); return MEMTXATTRS_UNSPECIFIED; } @@ -230,6 +258,13 @@ int kvm_mips_set_ipi_interrupt(MIPSCPU *cpu, int irq, int level) #define KVM_REG_MIPS_CP0_STATUS MIPS_CP0_32(12, 0) #define KVM_REG_MIPS_CP0_CAUSE MIPS_CP0_32(13, 0) #define KVM_REG_MIPS_CP0_EPC MIPS_CP0_64(14, 0) +#define KVM_REG_MIPS_CP0_PRID MIPS_CP0_32(15, 0) +#define KVM_REG_MIPS_CP0_CONFIG MIPS_CP0_32(16, 0) +#define KVM_REG_MIPS_CP0_CONFIG1 MIPS_CP0_32(16, 1) +#define KVM_REG_MIPS_CP0_CONFIG2 MIPS_CP0_32(16, 2) +#define KVM_REG_MIPS_CP0_CONFIG3 MIPS_CP0_32(16, 3) +#define KVM_REG_MIPS_CP0_CONFIG4 MIPS_CP0_32(16, 4) +#define KVM_REG_MIPS_CP0_CONFIG5 MIPS_CP0_32(16, 5) #define KVM_REG_MIPS_CP0_ERROREPC MIPS_CP0_64(30, 0) static inline int kvm_mips_put_one_reg(CPUState *cs, uint64_t reg_id, @@ -243,6 +278,17 @@ static inline int kvm_mips_put_one_reg(CPUState *cs, uint64_t reg_id, return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &cp0reg); } +static inline int kvm_mips_put_one_ureg(CPUState *cs, uint64_t reg_id, + uint32_t *addr) +{ + struct kvm_one_reg cp0reg = { + .id = reg_id, + .addr = (uintptr_t)addr + }; + + return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &cp0reg); +} + static inline int kvm_mips_put_one_ulreg(CPUState *cs, uint64_t reg_id, target_ulong *addr) { @@ -256,7 +302,18 @@ static inline int kvm_mips_put_one_ulreg(CPUState *cs, uint64_t reg_id, } static inline int kvm_mips_put_one_reg64(CPUState *cs, uint64_t reg_id, - uint64_t *addr) + int64_t *addr) +{ + struct kvm_one_reg cp0reg = { + .id = reg_id, + .addr = (uintptr_t)addr + }; + + return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &cp0reg); +} + +static inline int kvm_mips_put_one_ureg64(CPUState *cs, uint64_t reg_id, + uint64_t *addr) { struct kvm_one_reg cp0reg = { .id = reg_id, @@ -277,7 +334,18 @@ static inline int kvm_mips_get_one_reg(CPUState *cs, uint64_t reg_id, return kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &cp0reg); } -static inline int kvm_mips_get_one_ulreg(CPUState *cs, uint64 reg_id, +static inline int kvm_mips_get_one_ureg(CPUState *cs, uint64_t reg_id, + uint32_t *addr) +{ + struct kvm_one_reg cp0reg = { + .id = reg_id, + .addr = (uintptr_t)addr + }; + + return kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &cp0reg); +} + +static inline int kvm_mips_get_one_ulreg(CPUState *cs, uint64_t reg_id, target_ulong *addr) { int ret; @@ -294,8 +362,8 @@ static inline int kvm_mips_get_one_ulreg(CPUState *cs, uint64 reg_id, return ret; } -static inline int kvm_mips_get_one_reg64(CPUState *cs, uint64 reg_id, - uint64_t *addr) +static inline int kvm_mips_get_one_reg64(CPUState *cs, uint64_t reg_id, + int64_t *addr) { struct kvm_one_reg cp0reg = { .id = reg_id, @@ -305,6 +373,50 @@ static inline int kvm_mips_get_one_reg64(CPUState *cs, uint64 reg_id, return kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &cp0reg); } +static inline int kvm_mips_get_one_ureg64(CPUState *cs, uint64_t reg_id, + uint64_t *addr) +{ + struct kvm_one_reg cp0reg = { + .id = reg_id, + .addr = (uintptr_t)addr + }; + + return kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &cp0reg); +} + +#define KVM_REG_MIPS_CP0_CONFIG_MASK (1U << CP0C0_M) +#define KVM_REG_MIPS_CP0_CONFIG1_MASK ((1U << CP0C1_M) | \ + (1U << CP0C1_FP)) +#define KVM_REG_MIPS_CP0_CONFIG2_MASK (1U << CP0C2_M) +#define KVM_REG_MIPS_CP0_CONFIG3_MASK ((1U << CP0C3_M) | \ + (1U << CP0C3_MSAP)) +#define KVM_REG_MIPS_CP0_CONFIG4_MASK (1U << CP0C4_M) +#define KVM_REG_MIPS_CP0_CONFIG5_MASK ((1U << CP0C5_MSAEn) | \ + (1U << CP0C5_UFE) | \ + (1U << CP0C5_FRE) | \ + (1U << CP0C5_UFR)) + +static inline int kvm_mips_change_one_reg(CPUState *cs, uint64_t reg_id, + int32_t *addr, int32_t mask) +{ + int err; + int32_t tmp, change; + + err = kvm_mips_get_one_reg(cs, reg_id, &tmp); + if (err < 0) { + return err; + } + + /* only change bits in mask */ + change = (*addr ^ tmp) & mask; + if (!change) { + return 0; + } + + tmp = tmp ^ change; + return kvm_mips_put_one_reg(cs, reg_id, &tmp); +} + /* * We freeze the KVM timer when either the VM clock is stopped or the state is * saved (the state is dirty). @@ -322,13 +434,13 @@ static int kvm_mips_save_count(CPUState *cs) int err, ret = 0; /* freeze KVM timer */ - err = kvm_mips_get_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl); + err = kvm_mips_get_one_ureg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl); if (err < 0) { DPRINTF("%s: Failed to get COUNT_CTL (%d)\n", __func__, err); ret = err; } else if (!(count_ctl & KVM_REG_MIPS_COUNT_CTL_DC)) { count_ctl |= KVM_REG_MIPS_COUNT_CTL_DC; - err = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl); + err = kvm_mips_put_one_ureg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl); if (err < 0) { DPRINTF("%s: Failed to set COUNT_CTL.DC=1 (%d)\n", __func__, err); ret = err; @@ -364,14 +476,14 @@ static int kvm_mips_restore_count(CPUState *cs) int err_dc, err, ret = 0; /* check the timer is frozen */ - err_dc = kvm_mips_get_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl); + err_dc = kvm_mips_get_one_ureg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl); if (err_dc < 0) { DPRINTF("%s: Failed to get COUNT_CTL (%d)\n", __func__, err_dc); ret = err_dc; } else if (!(count_ctl & KVM_REG_MIPS_COUNT_CTL_DC)) { /* freeze timer (sets COUNT_RESUME for us) */ count_ctl |= KVM_REG_MIPS_COUNT_CTL_DC; - err = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl); + err = kvm_mips_put_one_ureg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl); if (err < 0) { DPRINTF("%s: Failed to set COUNT_CTL.DC=1 (%d)\n", __func__, err); ret = err; @@ -395,7 +507,7 @@ static int kvm_mips_restore_count(CPUState *cs) /* resume KVM timer */ if (err_dc >= 0) { count_ctl &= ~KVM_REG_MIPS_COUNT_CTL_DC; - err = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl); + err = kvm_mips_put_one_ureg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl); if (err < 0) { DPRINTF("%s: Failed to set COUNT_CTL.DC=0 (%d)\n", __func__, err); ret = err; @@ -428,8 +540,8 @@ static void kvm_mips_update_state(void *opaque, int running, RunState state) } else { /* Set clock restore time to now */ count_resume = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - ret = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_RESUME, - &count_resume); + ret = kvm_mips_put_one_ureg64(cs, KVM_REG_MIPS_COUNT_RESUME, + &count_resume); if (ret < 0) { fprintf(stderr, "Failed setting COUNT_RESUME\n"); return; @@ -444,6 +556,167 @@ static void kvm_mips_update_state(void *opaque, int running, RunState state) } } +static int kvm_mips_put_fpu_registers(CPUState *cs, int level) +{ + MIPSCPU *cpu = MIPS_CPU(cs); + CPUMIPSState *env = &cpu->env; + int err, ret = 0; + unsigned int i; + + /* Only put FPU state if we're emulating a CPU with an FPU */ + if (env->CP0_Config1 & (1 << CP0C1_FP)) { + /* FPU Control Registers */ + if (level == KVM_PUT_FULL_STATE) { + err = kvm_mips_put_one_ureg(cs, KVM_REG_MIPS_FCR_IR, + &env->active_fpu.fcr0); + if (err < 0) { + DPRINTF("%s: Failed to put FCR_IR (%d)\n", __func__, err); + ret = err; + } + } + err = kvm_mips_put_one_ureg(cs, KVM_REG_MIPS_FCR_CSR, + &env->active_fpu.fcr31); + if (err < 0) { + DPRINTF("%s: Failed to put FCR_CSR (%d)\n", __func__, err); + ret = err; + } + + /* + * FPU register state is a subset of MSA vector state, so don't put FPU + * registers if we're emulating a CPU with MSA. + */ + if (!(env->CP0_Config3 & (1 << CP0C3_MSAP))) { + /* Floating point registers */ + for (i = 0; i < 32; ++i) { + if (env->CP0_Status & (1 << CP0St_FR)) { + err = kvm_mips_put_one_ureg64(cs, KVM_REG_MIPS_FPR_64(i), + &env->active_fpu.fpr[i].d); + } else { + err = kvm_mips_get_one_ureg(cs, KVM_REG_MIPS_FPR_32(i), + &env->active_fpu.fpr[i].w[FP_ENDIAN_IDX]); + } + if (err < 0) { + DPRINTF("%s: Failed to put FPR%u (%d)\n", __func__, i, err); + ret = err; + } + } + } + } + + /* Only put MSA state if we're emulating a CPU with MSA */ + if (env->CP0_Config3 & (1 << CP0C3_MSAP)) { + /* MSA Control Registers */ + if (level == KVM_PUT_FULL_STATE) { + err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_MSA_IR, + &env->msair); + if (err < 0) { + DPRINTF("%s: Failed to put MSA_IR (%d)\n", __func__, err); + ret = err; + } + } + err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_MSA_CSR, + &env->active_tc.msacsr); + if (err < 0) { + DPRINTF("%s: Failed to put MSA_CSR (%d)\n", __func__, err); + ret = err; + } + + /* Vector registers (includes FP registers) */ + for (i = 0; i < 32; ++i) { + /* Big endian MSA not supported by QEMU yet anyway */ + err = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_VEC_128(i), + env->active_fpu.fpr[i].wr.d); + if (err < 0) { + DPRINTF("%s: Failed to put VEC%u (%d)\n", __func__, i, err); + ret = err; + } + } + } + + return ret; +} + +static int kvm_mips_get_fpu_registers(CPUState *cs) +{ + MIPSCPU *cpu = MIPS_CPU(cs); + CPUMIPSState *env = &cpu->env; + int err, ret = 0; + unsigned int i; + + /* Only get FPU state if we're emulating a CPU with an FPU */ + if (env->CP0_Config1 & (1 << CP0C1_FP)) { + /* FPU Control Registers */ + err = kvm_mips_get_one_ureg(cs, KVM_REG_MIPS_FCR_IR, + &env->active_fpu.fcr0); + if (err < 0) { + DPRINTF("%s: Failed to get FCR_IR (%d)\n", __func__, err); + ret = err; + } + err = kvm_mips_get_one_ureg(cs, KVM_REG_MIPS_FCR_CSR, + &env->active_fpu.fcr31); + if (err < 0) { + DPRINTF("%s: Failed to get FCR_CSR (%d)\n", __func__, err); + ret = err; + } else { + restore_fp_status(env); + } + + /* + * FPU register state is a subset of MSA vector state, so don't save FPU + * registers if we're emulating a CPU with MSA. + */ + if (!(env->CP0_Config3 & (1 << CP0C3_MSAP))) { + /* Floating point registers */ + for (i = 0; i < 32; ++i) { + if (env->CP0_Status & (1 << CP0St_FR)) { + err = kvm_mips_get_one_ureg64(cs, KVM_REG_MIPS_FPR_64(i), + &env->active_fpu.fpr[i].d); + } else { + err = kvm_mips_get_one_ureg(cs, KVM_REG_MIPS_FPR_32(i), + &env->active_fpu.fpr[i].w[FP_ENDIAN_IDX]); + } + if (err < 0) { + DPRINTF("%s: Failed to get FPR%u (%d)\n", __func__, i, err); + ret = err; + } + } + } + } + + /* Only get MSA state if we're emulating a CPU with MSA */ + if (env->CP0_Config3 & (1 << CP0C3_MSAP)) { + /* MSA Control Registers */ + err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_MSA_IR, + &env->msair); + if (err < 0) { + DPRINTF("%s: Failed to get MSA_IR (%d)\n", __func__, err); + ret = err; + } + err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_MSA_CSR, + &env->active_tc.msacsr); + if (err < 0) { + DPRINTF("%s: Failed to get MSA_CSR (%d)\n", __func__, err); + ret = err; + } else { + restore_msa_fp_status(env); + } + + /* Vector registers (includes FP registers) */ + for (i = 0; i < 32; ++i) { + /* Big endian MSA not supported by QEMU yet anyway */ + err = kvm_mips_get_one_reg64(cs, KVM_REG_MIPS_VEC_128(i), + env->active_fpu.fpr[i].wr.d); + if (err < 0) { + DPRINTF("%s: Failed to get VEC%u (%d)\n", __func__, i, err); + ret = err; + } + } + } + + return ret; +} + + static int kvm_mips_put_cp0_registers(CPUState *cs, int level) { MIPSCPU *cpu = MIPS_CPU(cs); @@ -522,6 +795,53 @@ static int kvm_mips_put_cp0_registers(CPUState *cs, int level) DPRINTF("%s: Failed to put CP0_EPC (%d)\n", __func__, err); ret = err; } + err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_PRID, &env->CP0_PRid); + if (err < 0) { + DPRINTF("%s: Failed to put CP0_PRID (%d)\n", __func__, err); + ret = err; + } + err = kvm_mips_change_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG, + &env->CP0_Config0, + KVM_REG_MIPS_CP0_CONFIG_MASK); + if (err < 0) { + DPRINTF("%s: Failed to change CP0_CONFIG (%d)\n", __func__, err); + ret = err; + } + err = kvm_mips_change_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG1, + &env->CP0_Config1, + KVM_REG_MIPS_CP0_CONFIG1_MASK); + if (err < 0) { + DPRINTF("%s: Failed to change CP0_CONFIG1 (%d)\n", __func__, err); + ret = err; + } + err = kvm_mips_change_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG2, + &env->CP0_Config2, + KVM_REG_MIPS_CP0_CONFIG2_MASK); + if (err < 0) { + DPRINTF("%s: Failed to change CP0_CONFIG2 (%d)\n", __func__, err); + ret = err; + } + err = kvm_mips_change_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG3, + &env->CP0_Config3, + KVM_REG_MIPS_CP0_CONFIG3_MASK); + if (err < 0) { + DPRINTF("%s: Failed to change CP0_CONFIG3 (%d)\n", __func__, err); + ret = err; + } + err = kvm_mips_change_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG4, + &env->CP0_Config4, + KVM_REG_MIPS_CP0_CONFIG4_MASK); + if (err < 0) { + DPRINTF("%s: Failed to change CP0_CONFIG4 (%d)\n", __func__, err); + ret = err; + } + err = kvm_mips_change_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG5, + &env->CP0_Config5, + KVM_REG_MIPS_CP0_CONFIG5_MASK); + if (err < 0) { + DPRINTF("%s: Failed to change CP0_CONFIG5 (%d)\n", __func__, err); + ret = err; + } err = kvm_mips_put_one_ulreg(cs, KVM_REG_MIPS_CP0_ERROREPC, &env->CP0_ErrorEPC); if (err < 0) { @@ -608,6 +928,41 @@ static int kvm_mips_get_cp0_registers(CPUState *cs) DPRINTF("%s: Failed to get CP0_EPC (%d)\n", __func__, err); ret = err; } + err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_PRID, &env->CP0_PRid); + if (err < 0) { + DPRINTF("%s: Failed to get CP0_PRID (%d)\n", __func__, err); + ret = err; + } + err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG, &env->CP0_Config0); + if (err < 0) { + DPRINTF("%s: Failed to get CP0_CONFIG (%d)\n", __func__, err); + ret = err; + } + err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG1, &env->CP0_Config1); + if (err < 0) { + DPRINTF("%s: Failed to get CP0_CONFIG1 (%d)\n", __func__, err); + ret = err; + } + err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG2, &env->CP0_Config2); + if (err < 0) { + DPRINTF("%s: Failed to get CP0_CONFIG2 (%d)\n", __func__, err); + ret = err; + } + err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG3, &env->CP0_Config3); + if (err < 0) { + DPRINTF("%s: Failed to get CP0_CONFIG3 (%d)\n", __func__, err); + ret = err; + } + err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG4, &env->CP0_Config4); + if (err < 0) { + DPRINTF("%s: Failed to get CP0_CONFIG4 (%d)\n", __func__, err); + ret = err; + } + err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG5, &env->CP0_Config5); + if (err < 0) { + DPRINTF("%s: Failed to get CP0_CONFIG5 (%d)\n", __func__, err); + ret = err; + } err = kvm_mips_get_one_ulreg(cs, KVM_REG_MIPS_CP0_ERROREPC, &env->CP0_ErrorEPC); if (err < 0) { @@ -646,6 +1001,11 @@ int kvm_arch_put_registers(CPUState *cs, int level) return ret; } + ret = kvm_mips_put_fpu_registers(cs, level); + if (ret < 0) { + return ret; + } + return ret; } @@ -673,12 +1033,13 @@ int kvm_arch_get_registers(CPUState *cs) env->active_tc.PC = regs.pc; kvm_mips_get_cp0_registers(cs); + kvm_mips_get_fpu_registers(cs); return ret; } int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, - uint64_t address, uint32_t data) + uint64_t address, uint32_t data, PCIDevice *dev) { return 0; } diff --git a/qemu/target-mips/lmi_helper.c b/qemu/target-mips/lmi_helper.c index bbfcd59cd..fb1245b39 100644 --- a/qemu/target-mips/lmi_helper.c +++ b/qemu/target-mips/lmi_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" diff --git a/qemu/target-mips/machine.c b/qemu/target-mips/machine.c index b15c43a10..22bca1814 100644 --- a/qemu/target-mips/machine.c +++ b/qemu/target-mips/machine.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/hw.h" #include "cpu.h" @@ -203,8 +204,8 @@ const VMStateDescription vmstate_tlb = { const VMStateDescription vmstate_mips_cpu = { .name = "cpu", - .version_id = 7, - .minimum_version_id = 7, + .version_id = 8, + .minimum_version_id = 8, .post_load = cpu_post_load, .fields = (VMStateField[]) { /* Active TC */ @@ -271,6 +272,8 @@ const VMStateDescription vmstate_mips_cpu = { VMSTATE_INT32(env.CP0_Config3, MIPSCPU), VMSTATE_INT32(env.CP0_Config6, MIPSCPU), VMSTATE_INT32(env.CP0_Config7, MIPSCPU), + VMSTATE_UINT64_ARRAY(env.CP0_MAAR, MIPSCPU, MIPS_MAAR_MAX), + VMSTATE_INT32(env.CP0_MAARI, MIPSCPU), VMSTATE_UINT64(env.lladdr, MIPSCPU), VMSTATE_UINTTL_ARRAY(env.CP0_WatchLo, MIPSCPU, 8), VMSTATE_INT32_ARRAY(env.CP0_WatchHi, MIPSCPU, 8), diff --git a/qemu/target-mips/mips-semi.c b/qemu/target-mips/mips-semi.c index 5050940c2..ed235de99 100644 --- a/qemu/target-mips/mips-semi.c +++ b/qemu/target-mips/mips-semi.c @@ -17,7 +17,7 @@ * License along with this library; if not, see . */ -#include +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "exec/softmmu-semi.h" diff --git a/qemu/target-mips/msa_helper.c b/qemu/target-mips/msa_helper.c index a1cb48f2a..654a0d272 100644 --- a/qemu/target-mips/msa_helper.c +++ b/qemu/target-mips/msa_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" @@ -1352,7 +1353,7 @@ void helper_msa_ctcmsa(CPUMIPSState *env, target_ulong elm, uint32_t cd) /* check exception */ if ((GET_FP_ENABLE(env->active_tc.msacsr) | FP_UNIMPLEMENTED) & GET_FP_CAUSE(env->active_tc.msacsr)) { - helper_raise_exception(env, EXCP_MSAFPE); + do_raise_exception(env, EXCP_MSAFPE, GETPC()); } break; } @@ -1505,14 +1506,14 @@ static inline void clear_msacsr_cause(CPUMIPSState *env) SET_FP_CAUSE(env->active_tc.msacsr, 0); } -static inline void check_msacsr_cause(CPUMIPSState *env) +static inline void check_msacsr_cause(CPUMIPSState *env, uintptr_t retaddr) { if ((GET_FP_CAUSE(env->active_tc.msacsr) & (GET_FP_ENABLE(env->active_tc.msacsr) | FP_UNIMPLEMENTED)) == 0) { UPDATE_FP_FLAGS(env->active_tc.msacsr, GET_FP_CAUSE(env->active_tc.msacsr)); } else { - helper_raise_exception(env, EXCP_MSAFPE); + do_raise_exception(env, EXCP_MSAFPE, retaddr); } } @@ -1604,7 +1605,7 @@ static inline int get_enabled_exceptions(const CPUMIPSState *env, int c) return c & enable; } -static inline float16 float16_from_float32(int32 a, flag ieee, +static inline float16 float16_from_float32(int32_t a, flag ieee, float_status *status) { float16 f_val; @@ -1615,7 +1616,7 @@ static inline float16 float16_from_float32(int32 a, flag ieee, return a < 0 ? (f_val | (1 << 15)) : f_val; } -static inline float32 float32_from_float64(int64 a, float_status *status) +static inline float32 float32_from_float64(int64_t a, float_status *status) { float32 f_val; @@ -1636,7 +1637,7 @@ static inline float32 float32_from_float16(int16_t a, flag ieee, return a < 0 ? (f_val | (1 << 31)) : f_val; } -static inline float64 float64_from_float32(int32 a, float_status *status) +static inline float64 float64_from_float32(int32_t a, float_status *status) { float64 f_val; @@ -1657,7 +1658,7 @@ static inline float32 float32_from_q16(int16_t a, float_status *status) return f_val; } -static inline float64 float64_from_q32(int32 a, float_status *status) +static inline float64 float64_from_q32(int32_t a, float_status *status) { float64 f_val; @@ -1670,9 +1671,9 @@ static inline float64 float64_from_q32(int32 a, float_status *status) static inline int16_t float32_to_q16(float32 a, float_status *status) { - int32 q_val; - int32 q_min = 0xffff8000; - int32 q_max = 0x00007fff; + int32_t q_val; + int32_t q_min = 0xffff8000; + int32_t q_max = 0x00007fff; int ieee_ex; @@ -1690,7 +1691,7 @@ static inline int16_t float32_to_q16(float32 a, float_status *status) if (ieee_ex & float_flag_overflow) { float_raise(float_flag_inexact, status); - return (int32)a < 0 ? q_min : q_max; + return (int32_t)a < 0 ? q_min : q_max; } /* conversion to int */ @@ -1704,7 +1705,7 @@ static inline int16_t float32_to_q16(float32 a, float_status *status) set_float_exception_flags(ieee_ex & (~float_flag_invalid) , status); float_raise(float_flag_overflow | float_flag_inexact, status); - return (int32)a < 0 ? q_min : q_max; + return (int32_t)a < 0 ? q_min : q_max; } if (q_val < q_min) { @@ -1720,11 +1721,11 @@ static inline int16_t float32_to_q16(float32 a, float_status *status) return (int16_t)q_val; } -static inline int32 float64_to_q32(float64 a, float_status *status) +static inline int32_t float64_to_q32(float64 a, float_status *status) { - int64 q_val; - int64 q_min = 0xffffffff80000000LL; - int64 q_max = 0x000000007fffffffLL; + int64_t q_val; + int64_t q_min = 0xffffffff80000000LL; + int64_t q_max = 0x000000007fffffffLL; int ieee_ex; @@ -1742,7 +1743,7 @@ static inline int32 float64_to_q32(float64 a, float_status *status) if (ieee_ex & float_flag_overflow) { float_raise(float_flag_inexact, status); - return (int64)a < 0 ? q_min : q_max; + return (int64_t)a < 0 ? q_min : q_max; } /* conversion to integer */ @@ -1756,20 +1757,20 @@ static inline int32 float64_to_q32(float64 a, float_status *status) set_float_exception_flags(ieee_ex & (~float_flag_invalid) , status); float_raise(float_flag_overflow | float_flag_inexact, status); - return (int64)a < 0 ? q_min : q_max; + return (int64_t)a < 0 ? q_min : q_max; } if (q_val < q_min) { float_raise(float_flag_overflow | float_flag_inexact, status); - return (int32)q_min; + return (int32_t)q_min; } if (q_max < q_val) { float_raise(float_flag_overflow | float_flag_inexact, status); - return (int32)q_max; + return (int32_t)q_max; } - return (int32)q_val; + return (int32_t)q_val; } #define MSA_FLOAT_COND(DEST, OP, ARG1, ARG2, BITS, QUIET) \ @@ -1851,7 +1852,8 @@ static inline int32 float64_to_q32(float64 a, float_status *status) } while (0) static inline void compare_af(CPUMIPSState *env, wr_t *pwd, wr_t *pws, - wr_t *pwt, uint32_t df, int quiet) + wr_t *pwt, uint32_t df, int quiet, + uintptr_t retaddr) { wr_t wx, *pwx = &wx; uint32_t i; @@ -1873,13 +1875,14 @@ static inline void compare_af(CPUMIPSState *env, wr_t *pwd, wr_t *pws, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, retaddr); msa_move_v(pwd, pwx); } static inline void compare_un(CPUMIPSState *env, wr_t *pwd, wr_t *pws, - wr_t *pwt, uint32_t df, int quiet) + wr_t *pwt, uint32_t df, int quiet, + uintptr_t retaddr) { wr_t wx, *pwx = &wx; uint32_t i; @@ -1903,13 +1906,14 @@ static inline void compare_un(CPUMIPSState *env, wr_t *pwd, wr_t *pws, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, retaddr); msa_move_v(pwd, pwx); } static inline void compare_eq(CPUMIPSState *env, wr_t *pwd, wr_t *pws, - wr_t *pwt, uint32_t df, int quiet) + wr_t *pwt, uint32_t df, int quiet, + uintptr_t retaddr) { wr_t wx, *pwx = &wx; uint32_t i; @@ -1931,13 +1935,14 @@ static inline void compare_eq(CPUMIPSState *env, wr_t *pwd, wr_t *pws, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, retaddr); msa_move_v(pwd, pwx); } static inline void compare_ueq(CPUMIPSState *env, wr_t *pwd, wr_t *pws, - wr_t *pwt, uint32_t df, int quiet) + wr_t *pwt, uint32_t df, int quiet, + uintptr_t retaddr) { wr_t wx, *pwx = &wx; uint32_t i; @@ -1959,13 +1964,14 @@ static inline void compare_ueq(CPUMIPSState *env, wr_t *pwd, wr_t *pws, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, retaddr); msa_move_v(pwd, pwx); } static inline void compare_lt(CPUMIPSState *env, wr_t *pwd, wr_t *pws, - wr_t *pwt, uint32_t df, int quiet) + wr_t *pwt, uint32_t df, int quiet, + uintptr_t retaddr) { wr_t wx, *pwx = &wx; uint32_t i; @@ -1987,13 +1993,14 @@ static inline void compare_lt(CPUMIPSState *env, wr_t *pwd, wr_t *pws, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, retaddr); msa_move_v(pwd, pwx); } static inline void compare_ult(CPUMIPSState *env, wr_t *pwd, wr_t *pws, - wr_t *pwt, uint32_t df, int quiet) + wr_t *pwt, uint32_t df, int quiet, + uintptr_t retaddr) { wr_t wx, *pwx = &wx; uint32_t i; @@ -2015,13 +2022,14 @@ static inline void compare_ult(CPUMIPSState *env, wr_t *pwd, wr_t *pws, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, retaddr); msa_move_v(pwd, pwx); } static inline void compare_le(CPUMIPSState *env, wr_t *pwd, wr_t *pws, - wr_t *pwt, uint32_t df, int quiet) + wr_t *pwt, uint32_t df, int quiet, + uintptr_t retaddr) { wr_t wx, *pwx = &wx; uint32_t i; @@ -2043,13 +2051,14 @@ static inline void compare_le(CPUMIPSState *env, wr_t *pwd, wr_t *pws, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, retaddr); msa_move_v(pwd, pwx); } static inline void compare_ule(CPUMIPSState *env, wr_t *pwd, wr_t *pws, - wr_t *pwt, uint32_t df, int quiet) + wr_t *pwt, uint32_t df, int quiet, + uintptr_t retaddr) { wr_t wx, *pwx = &wx; uint32_t i; @@ -2071,13 +2080,14 @@ static inline void compare_ule(CPUMIPSState *env, wr_t *pwd, wr_t *pws, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, retaddr); msa_move_v(pwd, pwx); } static inline void compare_or(CPUMIPSState *env, wr_t *pwd, wr_t *pws, - wr_t *pwt, uint32_t df, int quiet) + wr_t *pwt, uint32_t df, int quiet, + uintptr_t retaddr) { wr_t wx, *pwx = &wx; uint32_t i; @@ -2099,13 +2109,14 @@ static inline void compare_or(CPUMIPSState *env, wr_t *pwd, wr_t *pws, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, retaddr); msa_move_v(pwd, pwx); } static inline void compare_une(CPUMIPSState *env, wr_t *pwd, wr_t *pws, - wr_t *pwt, uint32_t df, int quiet) + wr_t *pwt, uint32_t df, int quiet, + uintptr_t retaddr) { wr_t wx, *pwx = &wx; uint32_t i; @@ -2127,13 +2138,15 @@ static inline void compare_une(CPUMIPSState *env, wr_t *pwd, wr_t *pws, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, retaddr); msa_move_v(pwd, pwx); } static inline void compare_ne(CPUMIPSState *env, wr_t *pwd, wr_t *pws, - wr_t *pwt, uint32_t df, int quiet) { + wr_t *pwt, uint32_t df, int quiet, + uintptr_t retaddr) +{ wr_t wx, *pwx = &wx; uint32_t i; @@ -2154,7 +2167,7 @@ static inline void compare_ne(CPUMIPSState *env, wr_t *pwd, wr_t *pws, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, retaddr); msa_move_v(pwd, pwx); } @@ -2165,7 +2178,7 @@ void helper_msa_fcaf_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_af(env, pwd, pws, pwt, df, 1); + compare_af(env, pwd, pws, pwt, df, 1, GETPC()); } void helper_msa_fcun_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2174,7 +2187,7 @@ void helper_msa_fcun_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_un(env, pwd, pws, pwt, df, 1); + compare_un(env, pwd, pws, pwt, df, 1, GETPC()); } void helper_msa_fceq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2183,7 +2196,7 @@ void helper_msa_fceq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_eq(env, pwd, pws, pwt, df, 1); + compare_eq(env, pwd, pws, pwt, df, 1, GETPC()); } void helper_msa_fcueq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2192,7 +2205,7 @@ void helper_msa_fcueq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_ueq(env, pwd, pws, pwt, df, 1); + compare_ueq(env, pwd, pws, pwt, df, 1, GETPC()); } void helper_msa_fclt_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2201,7 +2214,7 @@ void helper_msa_fclt_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_lt(env, pwd, pws, pwt, df, 1); + compare_lt(env, pwd, pws, pwt, df, 1, GETPC()); } void helper_msa_fcult_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2210,7 +2223,7 @@ void helper_msa_fcult_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_ult(env, pwd, pws, pwt, df, 1); + compare_ult(env, pwd, pws, pwt, df, 1, GETPC()); } void helper_msa_fcle_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2219,7 +2232,7 @@ void helper_msa_fcle_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_le(env, pwd, pws, pwt, df, 1); + compare_le(env, pwd, pws, pwt, df, 1, GETPC()); } void helper_msa_fcule_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2228,7 +2241,7 @@ void helper_msa_fcule_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_ule(env, pwd, pws, pwt, df, 1); + compare_ule(env, pwd, pws, pwt, df, 1, GETPC()); } void helper_msa_fsaf_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2237,7 +2250,7 @@ void helper_msa_fsaf_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_af(env, pwd, pws, pwt, df, 0); + compare_af(env, pwd, pws, pwt, df, 0, GETPC()); } void helper_msa_fsun_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2246,7 +2259,7 @@ void helper_msa_fsun_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_un(env, pwd, pws, pwt, df, 0); + compare_un(env, pwd, pws, pwt, df, 0, GETPC()); } void helper_msa_fseq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2255,7 +2268,7 @@ void helper_msa_fseq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_eq(env, pwd, pws, pwt, df, 0); + compare_eq(env, pwd, pws, pwt, df, 0, GETPC()); } void helper_msa_fsueq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2264,7 +2277,7 @@ void helper_msa_fsueq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_ueq(env, pwd, pws, pwt, df, 0); + compare_ueq(env, pwd, pws, pwt, df, 0, GETPC()); } void helper_msa_fslt_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2273,7 +2286,7 @@ void helper_msa_fslt_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_lt(env, pwd, pws, pwt, df, 0); + compare_lt(env, pwd, pws, pwt, df, 0, GETPC()); } void helper_msa_fsult_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2282,7 +2295,7 @@ void helper_msa_fsult_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_ult(env, pwd, pws, pwt, df, 0); + compare_ult(env, pwd, pws, pwt, df, 0, GETPC()); } void helper_msa_fsle_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2291,7 +2304,7 @@ void helper_msa_fsle_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_le(env, pwd, pws, pwt, df, 0); + compare_le(env, pwd, pws, pwt, df, 0, GETPC()); } void helper_msa_fsule_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2300,7 +2313,7 @@ void helper_msa_fsule_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_ule(env, pwd, pws, pwt, df, 0); + compare_ule(env, pwd, pws, pwt, df, 0, GETPC()); } void helper_msa_fcor_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2309,7 +2322,7 @@ void helper_msa_fcor_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_or(env, pwd, pws, pwt, df, 1); + compare_or(env, pwd, pws, pwt, df, 1, GETPC()); } void helper_msa_fcune_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2318,7 +2331,7 @@ void helper_msa_fcune_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_une(env, pwd, pws, pwt, df, 1); + compare_une(env, pwd, pws, pwt, df, 1, GETPC()); } void helper_msa_fcne_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2327,7 +2340,7 @@ void helper_msa_fcne_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_ne(env, pwd, pws, pwt, df, 1); + compare_ne(env, pwd, pws, pwt, df, 1, GETPC()); } void helper_msa_fsor_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2336,7 +2349,7 @@ void helper_msa_fsor_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_or(env, pwd, pws, pwt, df, 0); + compare_or(env, pwd, pws, pwt, df, 0, GETPC()); } void helper_msa_fsune_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2345,7 +2358,7 @@ void helper_msa_fsune_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_une(env, pwd, pws, pwt, df, 0); + compare_une(env, pwd, pws, pwt, df, 0, GETPC()); } void helper_msa_fsne_df(CPUMIPSState *env, uint32_t df, uint32_t wd, @@ -2354,7 +2367,7 @@ void helper_msa_fsne_df(CPUMIPSState *env, uint32_t df, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - compare_ne(env, pwd, pws, pwt, df, 0); + compare_ne(env, pwd, pws, pwt, df, 0, GETPC()); } #define float16_is_zero(ARG) 0 @@ -2404,7 +2417,7 @@ void helper_msa_fadd_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -2434,7 +2447,7 @@ void helper_msa_fsub_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -2464,7 +2477,7 @@ void helper_msa_fmul_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -2495,7 +2508,7 @@ void helper_msa_fdiv_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -2542,7 +2555,7 @@ void helper_msa_fmadd_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -2577,7 +2590,7 @@ void helper_msa_fmsub_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -2614,7 +2627,7 @@ void helper_msa_fexp2_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -2666,7 +2679,7 @@ void helper_msa_fexdo_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -2712,7 +2725,7 @@ void helper_msa_ftq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -2791,7 +2804,7 @@ void helper_msa_fmin_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -2822,7 +2835,7 @@ void helper_msa_fmin_a_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -2865,7 +2878,7 @@ void helper_msa_fmax_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -2896,7 +2909,7 @@ void helper_msa_fmax_a_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -2958,7 +2971,7 @@ void helper_msa_ftrunc_s_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -2988,7 +3001,7 @@ void helper_msa_ftrunc_u_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -3018,7 +3031,7 @@ void helper_msa_fsqrt_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -3067,7 +3080,7 @@ void helper_msa_frsqrt_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -3097,7 +3110,7 @@ void helper_msa_frcp_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -3127,7 +3140,7 @@ void helper_msa_frint_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -3181,7 +3194,7 @@ void helper_msa_flog2_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -3216,7 +3229,7 @@ void helper_msa_fexupl_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -3250,7 +3263,7 @@ void helper_msa_fexupr_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -3331,7 +3344,7 @@ void helper_msa_ftint_s_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -3361,7 +3374,7 @@ void helper_msa_ftint_u_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -3397,7 +3410,7 @@ void helper_msa_ffint_s_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } @@ -3427,7 +3440,7 @@ void helper_msa_ffint_u_df(CPUMIPSState *env, uint32_t df, uint32_t wd, assert(0); } - check_msacsr_cause(env); + check_msacsr_cause(env, GETPC()); msa_move_v(pwd, pwx); } diff --git a/qemu/target-mips/op_helper.c b/qemu/target-mips/op_helper.c index db4f6b946..4417e6ba2 100644 --- a/qemu/target-mips/op_helper.c +++ b/qemu/target-mips/op_helper.c @@ -16,55 +16,33 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include +#include "qemu/osdep.h" #include "cpu.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" #include "sysemu/kvm.h" -#ifndef CONFIG_USER_ONLY -static inline void cpu_mips_tlb_flush (CPUMIPSState *env, int flush_global); -#endif - /*****************************************************************************/ /* Exceptions processing helpers */ -static inline void QEMU_NORETURN do_raise_exception_err(CPUMIPSState *env, - uint32_t exception, - int error_code, - uintptr_t pc) +void helper_raise_exception_err(CPUMIPSState *env, uint32_t exception, + int error_code) { - CPUState *cs = CPU(mips_env_get_cpu(env)); - - if (exception < EXCP_SC) { - qemu_log("%s: %d %d\n", __func__, exception, error_code); - } - cs->exception_index = exception; - env->error_code = error_code; - - if (pc) { - /* now we have a real cpu fault */ - cpu_restore_state(cs, pc); - } - - cpu_loop_exit(cs); + do_raise_exception_err(env, exception, error_code, 0); } -static inline void QEMU_NORETURN do_raise_exception(CPUMIPSState *env, - uint32_t exception, - uintptr_t pc) +void helper_raise_exception(CPUMIPSState *env, uint32_t exception) { - do_raise_exception_err(env, exception, 0, pc); + do_raise_exception(env, exception, GETPC()); } -void helper_raise_exception_err(CPUMIPSState *env, uint32_t exception, - int error_code) +void helper_raise_exception_debug(CPUMIPSState *env) { - do_raise_exception_err(env, exception, error_code, 0); + do_raise_exception(env, EXCP_DEBUG, 0); } -void helper_raise_exception(CPUMIPSState *env, uint32_t exception) +static void raise_exception(CPUMIPSState *env, uint32_t exception) { do_raise_exception(env, exception, 0); } @@ -72,21 +50,21 @@ void helper_raise_exception(CPUMIPSState *env, uint32_t exception) #if defined(CONFIG_USER_ONLY) #define HELPER_LD(name, insn, type) \ static inline type do_##name(CPUMIPSState *env, target_ulong addr, \ - int mem_idx) \ + int mem_idx, uintptr_t retaddr) \ { \ - return (type) cpu_##insn##_data(env, addr); \ + return (type) cpu_##insn##_data_ra(env, addr, retaddr); \ } #else #define HELPER_LD(name, insn, type) \ static inline type do_##name(CPUMIPSState *env, target_ulong addr, \ - int mem_idx) \ + int mem_idx, uintptr_t retaddr) \ { \ switch (mem_idx) \ { \ - case 0: return (type) cpu_##insn##_kernel(env, addr); break; \ - case 1: return (type) cpu_##insn##_super(env, addr); break; \ + case 0: return (type) cpu_##insn##_kernel_ra(env, addr, retaddr); \ + case 1: return (type) cpu_##insn##_super_ra(env, addr, retaddr); \ default: \ - case 2: return (type) cpu_##insn##_user(env, addr); break; \ + case 2: return (type) cpu_##insn##_user_ra(env, addr, retaddr); \ } \ } #endif @@ -99,21 +77,21 @@ HELPER_LD(ld, ldq, int64_t) #if defined(CONFIG_USER_ONLY) #define HELPER_ST(name, insn, type) \ static inline void do_##name(CPUMIPSState *env, target_ulong addr, \ - type val, int mem_idx) \ + type val, int mem_idx, uintptr_t retaddr) \ { \ - cpu_##insn##_data(env, addr, val); \ + cpu_##insn##_data_ra(env, addr, val, retaddr); \ } #else #define HELPER_ST(name, insn, type) \ static inline void do_##name(CPUMIPSState *env, target_ulong addr, \ - type val, int mem_idx) \ + type val, int mem_idx, uintptr_t retaddr) \ { \ switch (mem_idx) \ { \ - case 0: cpu_##insn##_kernel(env, addr, val); break; \ - case 1: cpu_##insn##_super(env, addr, val); break; \ + case 0: cpu_##insn##_kernel_ra(env, addr, val, retaddr); break; \ + case 1: cpu_##insn##_super_ra(env, addr, val, retaddr); break; \ default: \ - case 2: cpu_##insn##_user(env, addr, val); break; \ + case 2: cpu_##insn##_user_ra(env, addr, val, retaddr); break; \ } \ } #endif @@ -292,14 +270,15 @@ target_ulong helper_bitswap(target_ulong rt) static inline hwaddr do_translate_address(CPUMIPSState *env, target_ulong address, - int rw) + int rw, uintptr_t retaddr) { hwaddr lladdr; + CPUState *cs = CPU(mips_env_get_cpu(env)); lladdr = cpu_mips_translate_address(env, address, rw); if (lladdr == -1LL) { - cpu_loop_exit(CPU(mips_env_get_cpu(env))); + cpu_loop_exit_restore(cs, retaddr); } else { return lladdr; } @@ -310,10 +289,10 @@ target_ulong helper_##name(CPUMIPSState *env, target_ulong arg, int mem_idx) \ { \ if (arg & almask) { \ env->CP0_BadVAddr = arg; \ - helper_raise_exception(env, EXCP_AdEL); \ + do_raise_exception(env, EXCP_AdEL, GETPC()); \ } \ - env->lladdr = do_translate_address(env, arg, 0); \ - env->llval = do_##insn(env, arg, mem_idx); \ + env->lladdr = do_translate_address(env, arg, 0, GETPC()); \ + env->llval = do_##insn(env, arg, mem_idx, GETPC()); \ return env->llval; \ } HELPER_LD_ATOMIC(ll, lw, 0x3) @@ -330,12 +309,12 @@ target_ulong helper_##name(CPUMIPSState *env, target_ulong arg1, \ \ if (arg2 & almask) { \ env->CP0_BadVAddr = arg2; \ - helper_raise_exception(env, EXCP_AdES); \ + do_raise_exception(env, EXCP_AdES, GETPC()); \ } \ - if (do_translate_address(env, arg2, 1) == env->lladdr) { \ - tmp = do_##ld_insn(env, arg2, mem_idx); \ + if (do_translate_address(env, arg2, 1, GETPC()) == env->lladdr) { \ + tmp = do_##ld_insn(env, arg2, mem_idx, GETPC()); \ if (tmp == env->llval) { \ - do_##st_insn(env, arg2, arg1, mem_idx); \ + do_##st_insn(env, arg2, arg1, mem_idx, GETPC()); \ return 1; \ } \ } \ @@ -359,31 +338,43 @@ HELPER_ST_ATOMIC(scd, ld, sd, 0x7) void helper_swl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { - do_sb(env, arg2, (uint8_t)(arg1 >> 24), mem_idx); + do_sb(env, arg2, (uint8_t)(arg1 >> 24), mem_idx, GETPC()); - if (GET_LMASK(arg2) <= 2) - do_sb(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 16), mem_idx); + if (GET_LMASK(arg2) <= 2) { + do_sb(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 16), mem_idx, + GETPC()); + } - if (GET_LMASK(arg2) <= 1) - do_sb(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 8), mem_idx); + if (GET_LMASK(arg2) <= 1) { + do_sb(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 8), mem_idx, + GETPC()); + } - if (GET_LMASK(arg2) == 0) - do_sb(env, GET_OFFSET(arg2, 3), (uint8_t)arg1, mem_idx); + if (GET_LMASK(arg2) == 0) { + do_sb(env, GET_OFFSET(arg2, 3), (uint8_t)arg1, mem_idx, + GETPC()); + } } void helper_swr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { - do_sb(env, arg2, (uint8_t)arg1, mem_idx); + do_sb(env, arg2, (uint8_t)arg1, mem_idx, GETPC()); - if (GET_LMASK(arg2) >= 1) - do_sb(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), mem_idx); + if (GET_LMASK(arg2) >= 1) { + do_sb(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), mem_idx, + GETPC()); + } - if (GET_LMASK(arg2) >= 2) - do_sb(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), mem_idx); + if (GET_LMASK(arg2) >= 2) { + do_sb(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), mem_idx, + GETPC()); + } - if (GET_LMASK(arg2) == 3) - do_sb(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), mem_idx); + if (GET_LMASK(arg2) == 3) { + do_sb(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), mem_idx, + GETPC()); + } } #if defined(TARGET_MIPS64) @@ -399,55 +390,83 @@ void helper_swr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, void helper_sdl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { - do_sb(env, arg2, (uint8_t)(arg1 >> 56), mem_idx); + do_sb(env, arg2, (uint8_t)(arg1 >> 56), mem_idx, GETPC()); - if (GET_LMASK64(arg2) <= 6) - do_sb(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 48), mem_idx); + if (GET_LMASK64(arg2) <= 6) { + do_sb(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 48), mem_idx, + GETPC()); + } - if (GET_LMASK64(arg2) <= 5) - do_sb(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 40), mem_idx); + if (GET_LMASK64(arg2) <= 5) { + do_sb(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 40), mem_idx, + GETPC()); + } - if (GET_LMASK64(arg2) <= 4) - do_sb(env, GET_OFFSET(arg2, 3), (uint8_t)(arg1 >> 32), mem_idx); + if (GET_LMASK64(arg2) <= 4) { + do_sb(env, GET_OFFSET(arg2, 3), (uint8_t)(arg1 >> 32), mem_idx, + GETPC()); + } - if (GET_LMASK64(arg2) <= 3) - do_sb(env, GET_OFFSET(arg2, 4), (uint8_t)(arg1 >> 24), mem_idx); + if (GET_LMASK64(arg2) <= 3) { + do_sb(env, GET_OFFSET(arg2, 4), (uint8_t)(arg1 >> 24), mem_idx, + GETPC()); + } - if (GET_LMASK64(arg2) <= 2) - do_sb(env, GET_OFFSET(arg2, 5), (uint8_t)(arg1 >> 16), mem_idx); + if (GET_LMASK64(arg2) <= 2) { + do_sb(env, GET_OFFSET(arg2, 5), (uint8_t)(arg1 >> 16), mem_idx, + GETPC()); + } - if (GET_LMASK64(arg2) <= 1) - do_sb(env, GET_OFFSET(arg2, 6), (uint8_t)(arg1 >> 8), mem_idx); + if (GET_LMASK64(arg2) <= 1) { + do_sb(env, GET_OFFSET(arg2, 6), (uint8_t)(arg1 >> 8), mem_idx, + GETPC()); + } - if (GET_LMASK64(arg2) <= 0) - do_sb(env, GET_OFFSET(arg2, 7), (uint8_t)arg1, mem_idx); + if (GET_LMASK64(arg2) <= 0) { + do_sb(env, GET_OFFSET(arg2, 7), (uint8_t)arg1, mem_idx, + GETPC()); + } } void helper_sdr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { - do_sb(env, arg2, (uint8_t)arg1, mem_idx); + do_sb(env, arg2, (uint8_t)arg1, mem_idx, GETPC()); - if (GET_LMASK64(arg2) >= 1) - do_sb(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), mem_idx); + if (GET_LMASK64(arg2) >= 1) { + do_sb(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), mem_idx, + GETPC()); + } - if (GET_LMASK64(arg2) >= 2) - do_sb(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), mem_idx); + if (GET_LMASK64(arg2) >= 2) { + do_sb(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), mem_idx, + GETPC()); + } - if (GET_LMASK64(arg2) >= 3) - do_sb(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), mem_idx); + if (GET_LMASK64(arg2) >= 3) { + do_sb(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), mem_idx, + GETPC()); + } - if (GET_LMASK64(arg2) >= 4) - do_sb(env, GET_OFFSET(arg2, -4), (uint8_t)(arg1 >> 32), mem_idx); + if (GET_LMASK64(arg2) >= 4) { + do_sb(env, GET_OFFSET(arg2, -4), (uint8_t)(arg1 >> 32), mem_idx, + GETPC()); + } - if (GET_LMASK64(arg2) >= 5) - do_sb(env, GET_OFFSET(arg2, -5), (uint8_t)(arg1 >> 40), mem_idx); + if (GET_LMASK64(arg2) >= 5) { + do_sb(env, GET_OFFSET(arg2, -5), (uint8_t)(arg1 >> 40), mem_idx, + GETPC()); + } - if (GET_LMASK64(arg2) >= 6) - do_sb(env, GET_OFFSET(arg2, -6), (uint8_t)(arg1 >> 48), mem_idx); + if (GET_LMASK64(arg2) >= 6) { + do_sb(env, GET_OFFSET(arg2, -6), (uint8_t)(arg1 >> 48), mem_idx, + GETPC()); + } - if (GET_LMASK64(arg2) == 7) - do_sb(env, GET_OFFSET(arg2, -7), (uint8_t)(arg1 >> 56), mem_idx); + if (GET_LMASK64(arg2) == 7) { + do_sb(env, GET_OFFSET(arg2, -7), (uint8_t)(arg1 >> 56), mem_idx, + GETPC()); + } } #endif /* TARGET_MIPS64 */ @@ -464,13 +483,14 @@ void helper_lwm(CPUMIPSState *env, target_ulong addr, target_ulong reglist, for (i = 0; i < base_reglist; i++) { env->active_tc.gpr[multiple_regs[i]] = - (target_long)do_lw(env, addr, mem_idx); + (target_long)do_lw(env, addr, mem_idx, GETPC()); addr += 4; } } if (do_r31) { - env->active_tc.gpr[31] = (target_long)do_lw(env, addr, mem_idx); + env->active_tc.gpr[31] = (target_long)do_lw(env, addr, mem_idx, + GETPC()); } } @@ -484,13 +504,14 @@ void helper_swm(CPUMIPSState *env, target_ulong addr, target_ulong reglist, target_ulong i; for (i = 0; i < base_reglist; i++) { - do_sw(env, addr, env->active_tc.gpr[multiple_regs[i]], mem_idx); + do_sw(env, addr, env->active_tc.gpr[multiple_regs[i]], mem_idx, + GETPC()); addr += 4; } } if (do_r31) { - do_sw(env, addr, env->active_tc.gpr[31], mem_idx); + do_sw(env, addr, env->active_tc.gpr[31], mem_idx, GETPC()); } } @@ -505,13 +526,14 @@ void helper_ldm(CPUMIPSState *env, target_ulong addr, target_ulong reglist, target_ulong i; for (i = 0; i < base_reglist; i++) { - env->active_tc.gpr[multiple_regs[i]] = do_ld(env, addr, mem_idx); + env->active_tc.gpr[multiple_regs[i]] = do_ld(env, addr, mem_idx, + GETPC()); addr += 8; } } if (do_r31) { - env->active_tc.gpr[31] = do_ld(env, addr, mem_idx); + env->active_tc.gpr[31] = do_ld(env, addr, mem_idx, GETPC()); } } @@ -525,13 +547,14 @@ void helper_sdm(CPUMIPSState *env, target_ulong addr, target_ulong reglist, target_ulong i; for (i = 0; i < base_reglist; i++) { - do_sd(env, addr, env->active_tc.gpr[multiple_regs[i]], mem_idx); + do_sd(env, addr, env->active_tc.gpr[multiple_regs[i]], mem_idx, + GETPC()); addr += 8; } } if (do_r31) { - do_sd(env, addr, env->active_tc.gpr[31], mem_idx); + do_sd(env, addr, env->active_tc.gpr[31], mem_idx, GETPC()); } } #endif @@ -548,6 +571,14 @@ static bool mips_vpe_is_wfi(MIPSCPU *c) return cpu->halted && mips_vpe_active(env); } +static bool mips_vp_is_wfi(MIPSCPU *c) +{ + CPUState *cpu = CPU(c); + CPUMIPSState *env = &c->env; + + return cpu->halted && mips_vp_active(env); +} + static inline void mips_vpe_wake(MIPSCPU *c) { /* Dont set ->halted = 0 directly, let it be done via cpu_has_work @@ -858,6 +889,16 @@ target_ulong helper_mfc0_lladdr(CPUMIPSState *env) return (int32_t)(env->lladdr >> env->CP0_LLAddr_shift); } +target_ulong helper_mfc0_maar(CPUMIPSState *env) +{ + return (int32_t) env->CP0_MAAR[env->CP0_MAARI]; +} + +target_ulong helper_mfhc0_maar(CPUMIPSState *env) +{ + return env->CP0_MAAR[env->CP0_MAARI] >> 32; +} + target_ulong helper_mfc0_watchlo(CPUMIPSState *env, uint32_t sel) { return (int32_t)env->CP0_WatchLo[sel]; @@ -924,6 +965,11 @@ target_ulong helper_dmfc0_lladdr(CPUMIPSState *env) return env->lladdr >> env->CP0_LLAddr_shift; } +target_ulong helper_dmfc0_maar(CPUMIPSState *env) +{ + return env->CP0_MAAR[env->CP0_MAARI]; +} + target_ulong helper_dmfc0_watchlo(CPUMIPSState *env, uint32_t sel) { return env->CP0_WatchLo[sel]; @@ -1330,6 +1376,13 @@ void helper_mtc0_hwrena(CPUMIPSState *env, target_ulong arg1) { uint32_t mask = 0x0000000F; + if ((env->CP0_Config1 & (1 << CP0C1_PC)) && + (env->insn_flags & ISA_MIPS32R6)) { + mask |= (1 << 4); + } + if (env->insn_flags & ISA_MIPS32R6) { + mask |= (1 << 5); + } if (env->CP0_Config3 & (1 << CP0C3_ULRI)) { mask |= (1 << 29); @@ -1540,6 +1593,36 @@ void helper_mtc0_lladdr(CPUMIPSState *env, target_ulong arg1) env->lladdr = (env->lladdr & ~mask) | (arg1 & mask); } +#define MTC0_MAAR_MASK(env) \ + ((0x1ULL << 63) | ((env->PAMask >> 4) & ~0xFFFull) | 0x3) + +void helper_mtc0_maar(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_MAAR[env->CP0_MAARI] = arg1 & MTC0_MAAR_MASK(env); +} + +void helper_mthc0_maar(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_MAAR[env->CP0_MAARI] = + (((uint64_t) arg1 << 32) & MTC0_MAAR_MASK(env)) | + (env->CP0_MAAR[env->CP0_MAARI] & 0x00000000ffffffffULL); +} + +void helper_mtc0_maari(CPUMIPSState *env, target_ulong arg1) +{ + int index = arg1 & 0x3f; + if (index == 0x3f) { + /* Software may write all ones to INDEX to determine the + maximum value supported. */ + env->CP0_MAARI = MIPS_MAAR_MAX - 1; + } else if (index < MIPS_MAAR_MAX) { + env->CP0_MAARI = index; + } + /* Other than the all ones, if the + value written is not supported, then INDEX is unchanged + from its previous value. */ +} + void helper_mtc0_watchlo(CPUMIPSState *env, target_ulong arg1, uint32_t sel) { /* Watch exceptions for instructions, data loads, data stores @@ -1594,9 +1677,31 @@ void helper_mtc0_performance0(CPUMIPSState *env, target_ulong arg1) env->CP0_Performance0 = arg1 & 0x000007ff; } +void helper_mtc0_errctl(CPUMIPSState *env, target_ulong arg1) +{ + int32_t wst = arg1 & (1 << CP0EC_WST); + int32_t spr = arg1 & (1 << CP0EC_SPR); + int32_t itc = env->itc_tag ? (arg1 & (1 << CP0EC_ITC)) : 0; + + env->CP0_ErrCtl = wst | spr | itc; + + if (itc && !wst && !spr) { + env->hflags |= MIPS_HFLAG_ITC_CACHE; + } else { + env->hflags &= ~MIPS_HFLAG_ITC_CACHE; + } +} + void helper_mtc0_taglo(CPUMIPSState *env, target_ulong arg1) { - env->CP0_TagLo = arg1 & 0xFFFFFCF6; + if (env->hflags & MIPS_HFLAG_ITC_CACHE) { + /* If CACHE instruction is configured for ITC tags then make all + CP0.TagLo bits writable. The actual write to ITC Configuration + Tag will take care of the read-only bits. */ + env->CP0_TagLo = arg1; + } else { + env->CP0_TagLo = arg1 & 0xFFFFFCF6; + } } void helper_mtc0_datalo(CPUMIPSState *env, target_ulong arg1) @@ -1791,13 +1896,13 @@ target_ulong helper_yield(CPUMIPSState *env, target_ulong arg) env->active_tc.CP0_TCStatus & (1 << CP0TCSt_DT)) { env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); env->CP0_VPEControl |= 4 << CP0VPECo_EXCPT; - helper_raise_exception(env, EXCP_THREAD); + do_raise_exception(env, EXCP_THREAD, GETPC()); } } } else if (arg1 == 0) { if (0 /* TODO: TC underflow */) { env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); - helper_raise_exception(env, EXCP_THREAD); + do_raise_exception(env, EXCP_THREAD, GETPC()); } else { // TODO: Deallocate TC } @@ -1805,22 +1910,53 @@ target_ulong helper_yield(CPUMIPSState *env, target_ulong arg) /* Yield qualifier inputs not implemented. */ env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); env->CP0_VPEControl |= 2 << CP0VPECo_EXCPT; - helper_raise_exception(env, EXCP_THREAD); + do_raise_exception(env, EXCP_THREAD, GETPC()); } return env->CP0_YQMask; } +/* R6 Multi-threading */ #ifndef CONFIG_USER_ONLY -/* TLB management */ -static void cpu_mips_tlb_flush (CPUMIPSState *env, int flush_global) +target_ulong helper_dvp(CPUMIPSState *env) { - MIPSCPU *cpu = mips_env_get_cpu(env); + CPUState *other_cs = first_cpu; + target_ulong prev = env->CP0_VPControl; + + if (!((env->CP0_VPControl >> CP0VPCtl_DIS) & 1)) { + CPU_FOREACH(other_cs) { + MIPSCPU *other_cpu = MIPS_CPU(other_cs); + /* Turn off all VPs except the one executing the dvp. */ + if (&other_cpu->env != env) { + mips_vpe_sleep(other_cpu); + } + } + env->CP0_VPControl |= (1 << CP0VPCtl_DIS); + } + return prev; +} - /* Flush qemu's TLB and discard all shadowed entries. */ - tlb_flush(CPU(cpu), flush_global); - env->tlb->tlb_in_use = env->tlb->nb_tlb; +target_ulong helper_evp(CPUMIPSState *env) +{ + CPUState *other_cs = first_cpu; + target_ulong prev = env->CP0_VPControl; + + if ((env->CP0_VPControl >> CP0VPCtl_DIS) & 1) { + CPU_FOREACH(other_cs) { + MIPSCPU *other_cpu = MIPS_CPU(other_cs); + if ((&other_cpu->env != env) && !mips_vp_is_wfi(other_cpu)) { + /* If the VP is WFI, don't disturb its sleep. + * Otherwise, wake it up. */ + mips_vpe_wake(other_cpu); + } + } + env->CP0_VPControl &= ~(1 << CP0VPCtl_DIS); + } + return prev; } +#endif /* !CONFIG_USER_ONLY */ +#ifndef CONFIG_USER_ONLY +/* TLB management */ static void r4k_mips_tlb_flush_extra (CPUMIPSState *env, int first) { /* Discard entries from env->tlb[first] onwards. */ @@ -2158,48 +2294,52 @@ void helper_deret(CPUMIPSState *env) } #endif /* !CONFIG_USER_ONLY */ -target_ulong helper_rdhwr_cpunum(CPUMIPSState *env) +static inline void check_hwrena(CPUMIPSState *env, int reg, uintptr_t pc) { - if ((env->hflags & MIPS_HFLAG_CP0) || - (env->CP0_HWREna & (1 << 0))) - return env->CP0_EBase & 0x3ff; - else - helper_raise_exception(env, EXCP_RI); + if ((env->hflags & MIPS_HFLAG_CP0) || (env->CP0_HWREna & (1 << reg))) { + return; + } + do_raise_exception(env, EXCP_RI, pc); +} - return 0; +target_ulong helper_rdhwr_cpunum(CPUMIPSState *env) +{ + check_hwrena(env, 0, GETPC()); + return env->CP0_EBase & 0x3ff; } target_ulong helper_rdhwr_synci_step(CPUMIPSState *env) { - if ((env->hflags & MIPS_HFLAG_CP0) || - (env->CP0_HWREna & (1 << 1))) - return env->SYNCI_Step; - else - helper_raise_exception(env, EXCP_RI); - - return 0; + check_hwrena(env, 1, GETPC()); + return env->SYNCI_Step; } target_ulong helper_rdhwr_cc(CPUMIPSState *env) { - if ((env->hflags & MIPS_HFLAG_CP0) || - (env->CP0_HWREna & (1 << 2))) - return env->CP0_Count; - else - helper_raise_exception(env, EXCP_RI); - - return 0; + check_hwrena(env, 2, GETPC()); +#ifdef CONFIG_USER_ONLY + return env->CP0_Count; +#else + return (int32_t)cpu_mips_get_count(env); +#endif } target_ulong helper_rdhwr_ccres(CPUMIPSState *env) { - if ((env->hflags & MIPS_HFLAG_CP0) || - (env->CP0_HWREna & (1 << 3))) - return env->CCRes; - else - helper_raise_exception(env, EXCP_RI); + check_hwrena(env, 3, GETPC()); + return env->CCRes; +} - return 0; +target_ulong helper_rdhwr_performance(CPUMIPSState *env) +{ + check_hwrena(env, 4, GETPC()); + return env->CP0_Performance0; +} + +target_ulong helper_rdhwr_xnp(CPUMIPSState *env) +{ + check_hwrena(env, 5, GETPC()); + return (env->CP0_Config5 >> CP0C5_XNP) & 1; } void helper_pmon(CPUMIPSState *env, int function) @@ -2234,7 +2374,9 @@ void helper_wait(CPUMIPSState *env) cs->halted = 1; cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE); - helper_raise_exception(env, EXCP_HLT); + /* Last instruction in the block, PC was updated before + - no need to recover PC and icount */ + raise_exception(env, EXCP_HLT); } #if !defined(CONFIG_USER_ONLY) @@ -2295,9 +2437,9 @@ void mips_cpu_unassigned_access(CPUState *cs, hwaddr addr, } if (is_exec) { - helper_raise_exception(env, EXCP_IBE); + raise_exception(env, EXCP_IBE); } else { - helper_raise_exception(env, EXCP_DBE); + raise_exception(env, EXCP_DBE); } } #endif /* !CONFIG_USER_ONLY */ @@ -2332,7 +2474,7 @@ target_ulong helper_cfc1(CPUMIPSState *env, uint32_t reg) arg1 = (int32_t) ((env->CP0_Status & (1 << CP0St_FR)) >> CP0St_FR); } else { - helper_raise_exception(env, EXCP_RI); + do_raise_exception(env, EXCP_RI, GETPC()); } } break; @@ -2375,7 +2517,7 @@ void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt) env->CP0_Status &= ~(1 << CP0St_FR); compute_hflags(env); } else { - helper_raise_exception(env, EXCP_RI); + do_raise_exception(env, EXCP_RI, GETPC()); } break; case 4: @@ -2387,7 +2529,7 @@ void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt) env->CP0_Status |= (1 << CP0St_FR); compute_hflags(env); } else { - helper_raise_exception(env, EXCP_RI); + do_raise_exception(env, EXCP_RI, GETPC()); } break; case 5: @@ -2518,6 +2660,7 @@ uint64_t helper_float_cvtd_s(CPUMIPSState *env, uint32_t fst0) uint64_t fdt2; fdt2 = float32_to_float64(fst0, &env->active_fpu.fp_status); + fdt2 = float64_maybe_silence_nan(fdt2); update_fcr31(env, GETPC()); return fdt2; } @@ -2607,6 +2750,7 @@ uint32_t helper_float_cvts_d(CPUMIPSState *env, uint64_t fdt0) uint32_t fst2; fst2 = float64_to_float32(fdt0, &env->active_fpu.fp_status); + fst2 = float32_maybe_silence_nan(fst2); update_fcr31(env, GETPC()); return fst2; } @@ -3628,7 +3772,7 @@ FOP_CONDN_S(sne, (float32_lt(fst1, fst0, &env->active_fpu.fp_status) #if !defined(CONFIG_USER_ONLY) #define MEMOP_IDX(DF) \ TCGMemOpIdx oi = make_memop_idx(MO_TE | DF | MO_UNALN, \ - cpu_mmu_index(env)); + cpu_mmu_index(env, false)); #else #define MEMOP_IDX(DF) #endif @@ -3684,7 +3828,7 @@ void helper_msa_st_ ## TYPE(CPUMIPSState *env, uint32_t wd, \ target_ulong addr) \ { \ wr_t *pwd = &(env->active_fpu.fpr[wd].wr); \ - int mmu_idx = cpu_mmu_index(env); \ + int mmu_idx = cpu_mmu_index(env, false); \ int i; \ MEMOP_IDX(DF) \ ensure_writable_pages(env, addr, mmu_idx, GETRA()); \ @@ -3704,3 +3848,19 @@ MSA_ST_DF(DF_HALF, h, cpu_stw_data) MSA_ST_DF(DF_WORD, w, cpu_stl_data) MSA_ST_DF(DF_DOUBLE, d, cpu_stq_data) #endif + +void helper_cache(CPUMIPSState *env, target_ulong addr, uint32_t op) +{ +#ifndef CONFIG_USER_ONLY + target_ulong index = addr & 0x1fffffff; + if (op == 9) { + /* Index Store Tag */ + memory_region_dispatch_write(env->itc_tag, index, env->CP0_TagLo, + 8, MEMTXATTRS_UNSPECIFIED); + } else if (op == 5) { + /* Index Load Tag */ + memory_region_dispatch_read(env->itc_tag, index, &env->CP0_TagLo, + 8, MEMTXATTRS_UNSPECIFIED); + } +#endif +} diff --git a/qemu/target-mips/translate.c b/qemu/target-mips/translate.c index 22ef84df9..a3a05ec66 100644 --- a/qemu/target-mips/translate.c +++ b/qemu/target-mips/translate.c @@ -21,6 +21,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "disas/disas.h" #include "tcg-op.h" @@ -32,10 +33,9 @@ #include "exec/semihost.h" #include "trace-tcg.h" - +#include "exec/log.h" #define MIPS_DEBUG_DISAS 0 -//#define MIPS_DEBUG_SIGN_EXTENSIONS /* MIPS major opcodes */ #define MASK_OP_MAJOR(op) (op & (0x3F << 26)) @@ -325,6 +325,7 @@ enum { OPC_TLTIU = (0x0B << 16) | OPC_REGIMM, OPC_TEQI = (0x0C << 16) | OPC_REGIMM, OPC_TNEI = (0x0E << 16) | OPC_REGIMM, + OPC_SIGRIE = (0x17 << 16) | OPC_REGIMM, OPC_SYNCI = (0x1F << 16) | OPC_REGIMM, OPC_DAHI = (0x06 << 16) | OPC_REGIMM, @@ -893,6 +894,8 @@ enum { OPC_EVPE = 0x01 | (1 << 5) | OPC_MFMC0, OPC_DI = (0 << 5) | (0x0C << 11) | OPC_MFMC0, OPC_EI = (1 << 5) | (0x0C << 11) | OPC_MFMC0, + OPC_DVP = 0x04 | (0 << 3) | (1 << 5) | (0 << 11) | OPC_MFMC0, + OPC_EVP = 0x04 | (0 << 3) | (0 << 5) | (0 << 11) | OPC_MFMC0, }; /* Coprocessor 0 (with rs == C0) */ @@ -1352,7 +1355,7 @@ enum { }; /* global register indices */ -static TCGv_ptr cpu_env; +static TCGv_env cpu_env; static TCGv cpu_gpr[32], cpu_PC; static TCGv cpu_HI[MIPS_DSP_ACC], cpu_LO[MIPS_DSP_ACC]; static TCGv cpu_dspctrl, btarget, bcond; @@ -1361,9 +1364,6 @@ static TCGv_i32 fpu_fcr0, fpu_fcr31; static TCGv_i64 fpu_f64[32]; static TCGv_i64 msa_wr_d[64]; -static uint32_t gen_opc_hflags[OPC_BUF_SIZE]; -static target_ulong gen_opc_btarget[OPC_BUF_SIZE]; - #include "exec/gen-icount.h" #define gen_helper_0e0i(name, arg) do { \ @@ -1431,6 +1431,9 @@ typedef struct DisasContext { bool mvh; int CP0_LLAddr_shift; bool ps; + bool vp; + bool cmgcr; + bool mrp; } DisasContext; enum { @@ -1482,26 +1485,23 @@ static const char * const msaregnames[] = { "w30.d0", "w30.d1", "w31.d0", "w31.d1", }; -#define MIPS_DEBUG(fmt, ...) \ +#define LOG_DISAS(...) \ do { \ if (MIPS_DEBUG_DISAS) { \ - qemu_log_mask(CPU_LOG_TB_IN_ASM, \ - TARGET_FMT_lx ": %08x " fmt "\n", \ - ctx->pc, ctx->opcode , ## __VA_ARGS__); \ + qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__); \ } \ } while (0) -#define LOG_DISAS(...) \ +#define MIPS_INVAL(op) \ do { \ if (MIPS_DEBUG_DISAS) { \ - qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__); \ + qemu_log_mask(CPU_LOG_TB_IN_ASM, \ + TARGET_FMT_lx ": %08x Invalid %s %03x %03x %03x\n", \ + ctx->pc, ctx->opcode, op, ctx->opcode >> 26, \ + ctx->opcode & 0x3F, ((ctx->opcode >> 16) & 0x1F)); \ } \ } while (0) -#define MIPS_INVAL(op) \ - MIPS_DEBUG("Invalid %s %03x %03x %03x", op, ctx->opcode >> 26, \ - ctx->opcode & 0x3F, ((ctx->opcode >> 16) & 0x1F)) - /* General purpose registers moves. */ static inline void gen_load_gpr (TCGv t, int reg) { @@ -1615,21 +1615,26 @@ static inline void generate_exception_err(DisasContext *ctx, int excp, int err) gen_helper_raise_exception_err(cpu_env, texcp, terr); tcg_temp_free_i32(terr); tcg_temp_free_i32(texcp); + ctx->bstate = BS_EXCP; } static inline void generate_exception(DisasContext *ctx, int excp) { - save_cpu_state(ctx, 1); gen_helper_0e0i(raise_exception, excp); } +static inline void generate_exception_end(DisasContext *ctx, int excp) +{ + generate_exception_err(ctx, excp, 0); +} + /* Floating point register moves. */ static void gen_load_fpr32(DisasContext *ctx, TCGv_i32 t, int reg) { if (ctx->hflags & MIPS_HFLAG_FRE) { generate_exception(ctx, EXCP_RI); } - tcg_gen_trunc_i64_i32(t, fpu_f64[reg]); + tcg_gen_extrl_i64_i32(t, fpu_f64[reg]); } static void gen_store_fpr32(DisasContext *ctx, TCGv_i32 t, int reg) @@ -1647,10 +1652,7 @@ static void gen_store_fpr32(DisasContext *ctx, TCGv_i32 t, int reg) static void gen_load_fpr32h(DisasContext *ctx, TCGv_i32 t, int reg) { if (ctx->hflags & MIPS_HFLAG_F64) { - TCGv_i64 t64 = tcg_temp_new_i64(); - tcg_gen_shri_i64(t64, fpu_f64[reg], 32); - tcg_gen_trunc_i64_i32(t, t64); - tcg_temp_free_i64(t64); + tcg_gen_extrh_i64_i32(t, fpu_f64[reg]); } else { gen_load_fpr32(ctx, t, reg | 1); } @@ -1725,12 +1727,23 @@ static target_long addr_add(DisasContext *ctx, target_long base, return sum; } +/* Sign-extract the low 32-bits to a target_long. */ static inline void gen_move_low32(TCGv ret, TCGv_i64 arg) { #if defined(TARGET_MIPS64) - tcg_gen_ext32s_tl(ret, arg); + tcg_gen_ext32s_i64(ret, arg); +#else + tcg_gen_extrl_i64_i32(ret, arg); +#endif +} + +/* Sign-extract the high 32-bits to a target_long. */ +static inline void gen_move_high32(TCGv ret, TCGv_i64 arg) +{ +#if defined(TARGET_MIPS64) + tcg_gen_sari_i64(ret, arg, 32); #else - tcg_gen_trunc_i64_tl(ret, arg); + tcg_gen_extrh_i64_i32(ret, arg); #endif } @@ -1753,7 +1766,7 @@ static inline void check_cp1_enabled(DisasContext *ctx) static inline void check_cop1x(DisasContext *ctx) { if (unlikely(!(ctx->hflags & MIPS_HFLAG_COP1X))) - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } /* Verify that the processor is running with 64-bit floating-point @@ -1762,7 +1775,7 @@ static inline void check_cop1x(DisasContext *ctx) static inline void check_cp1_64bitmode(DisasContext *ctx) { if (unlikely(~ctx->hflags & (MIPS_HFLAG_F64 | MIPS_HFLAG_COP1X))) - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } /* @@ -1779,7 +1792,7 @@ static inline void check_cp1_64bitmode(DisasContext *ctx) static inline void check_cp1_registers(DisasContext *ctx, int regs) { if (unlikely(!(ctx->hflags & MIPS_HFLAG_F64) && (regs & 1))) - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } /* Verify that the processor is running with DSP instructions enabled. @@ -1790,9 +1803,9 @@ static inline void check_dsp(DisasContext *ctx) { if (unlikely(!(ctx->hflags & MIPS_HFLAG_DSP))) { if (ctx->insn_flags & ASE_DSP) { - generate_exception(ctx, EXCP_DSPDIS); + generate_exception_end(ctx, EXCP_DSPDIS); } else { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } } } @@ -1801,9 +1814,9 @@ static inline void check_dspr2(DisasContext *ctx) { if (unlikely(!(ctx->hflags & MIPS_HFLAG_DSPR2))) { if (ctx->insn_flags & ASE_DSP) { - generate_exception(ctx, EXCP_DSPDIS); + generate_exception_end(ctx, EXCP_DSPDIS); } else { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } } } @@ -1813,7 +1826,7 @@ static inline void check_dspr2(DisasContext *ctx) static inline void check_insn(DisasContext *ctx, int flags) { if (unlikely(!(ctx->insn_flags & flags))) { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } } @@ -1823,7 +1836,7 @@ static inline void check_insn(DisasContext *ctx, int flags) static inline void check_insn_opc_removed(DisasContext *ctx, int flags) { if (unlikely(ctx->insn_flags & flags)) { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } } @@ -1843,7 +1856,7 @@ static inline void check_ps(DisasContext *ctx) static inline void check_mips_64(DisasContext *ctx) { if (unlikely(!(ctx->hflags & MIPS_HFLAG_64))) - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } #endif @@ -2052,7 +2065,7 @@ static inline void op_st_##insn(TCGv arg1, TCGv arg2, int rt, DisasContext *ctx) tcg_gen_movi_tl(t0, rt | ((almask << 3) & 0x20)); \ tcg_gen_st_tl(t0, cpu_env, offsetof(CPUMIPSState, llreg)); \ tcg_gen_st_tl(arg1, cpu_env, offsetof(CPUMIPSState, llnewval)); \ - gen_helper_0e0i(raise_exception, EXCP_SC); \ + generate_exception_end(ctx, EXCP_SC); \ gen_set_label(l2); \ tcg_gen_movi_tl(t0, 0); \ gen_store_gpr(t0, rt); \ @@ -2105,14 +2118,12 @@ static target_ulong pc_relative_pc (DisasContext *ctx) static void gen_ld(DisasContext *ctx, uint32_t opc, int rt, int base, int16_t offset) { - const char *opn = "ld"; TCGv t0, t1, t2; if (rt == 0 && ctx->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)) { /* Loongson CPU uses a load to zero register for prefetch. We emulate it as a NOP. On other CPU we must perform the actual memory access. */ - MIPS_DEBUG("NOP"); return; } @@ -2125,20 +2136,16 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUL | ctx->default_tcg_memop_mask); gen_store_gpr(t0, rt); - opn = "lwu"; break; case OPC_LD: tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ | ctx->default_tcg_memop_mask); gen_store_gpr(t0, rt); - opn = "ld"; break; case OPC_LLD: case R6_OPC_LLD: - save_cpu_state(ctx, 1); op_ld_lld(t0, t0, ctx); gen_store_gpr(t0, rt); - opn = "lld"; break; case OPC_LDL: t1 = tcg_temp_new(); @@ -2153,16 +2160,14 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, tcg_gen_andi_tl(t0, t0, ~7); tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ); tcg_gen_shl_tl(t0, t0, t1); - tcg_gen_xori_tl(t1, t1, 63); - t2 = tcg_const_tl(0x7fffffffffffffffull); - tcg_gen_shr_tl(t2, t2, t1); + t2 = tcg_const_tl(-1); + tcg_gen_shl_tl(t2, t2, t1); gen_load_gpr(t1, rt); - tcg_gen_and_tl(t1, t1, t2); + tcg_gen_andc_tl(t1, t1, t2); tcg_temp_free(t2); tcg_gen_or_tl(t0, t0, t1); tcg_temp_free(t1); gen_store_gpr(t0, rt); - opn = "ldl"; break; case OPC_LDR: t1 = tcg_temp_new(); @@ -2186,7 +2191,6 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, tcg_gen_or_tl(t0, t0, t1); tcg_temp_free(t1); gen_store_gpr(t0, rt); - opn = "ldr"; break; case OPC_LDPC: t1 = tcg_const_tl(pc_relative_pc(ctx)); @@ -2194,7 +2198,6 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, tcg_temp_free(t1); tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ); gen_store_gpr(t0, rt); - opn = "ldpc"; break; #endif case OPC_LWPC: @@ -2203,35 +2206,29 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, tcg_temp_free(t1); tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL); gen_store_gpr(t0, rt); - opn = "lwpc"; break; case OPC_LW: tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL | ctx->default_tcg_memop_mask); gen_store_gpr(t0, rt); - opn = "lw"; break; case OPC_LH: tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESW | ctx->default_tcg_memop_mask); gen_store_gpr(t0, rt); - opn = "lh"; break; case OPC_LHU: tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUW | ctx->default_tcg_memop_mask); gen_store_gpr(t0, rt); - opn = "lhu"; break; case OPC_LB: tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_SB); gen_store_gpr(t0, rt); - opn = "lb"; break; case OPC_LBU: tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_UB); gen_store_gpr(t0, rt); - opn = "lbu"; break; case OPC_LWL: t1 = tcg_temp_new(); @@ -2246,17 +2243,15 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, tcg_gen_andi_tl(t0, t0, ~3); tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUL); tcg_gen_shl_tl(t0, t0, t1); - tcg_gen_xori_tl(t1, t1, 31); - t2 = tcg_const_tl(0x7fffffffull); - tcg_gen_shr_tl(t2, t2, t1); + t2 = tcg_const_tl(-1); + tcg_gen_shl_tl(t2, t2, t1); gen_load_gpr(t1, rt); - tcg_gen_and_tl(t1, t1, t2); + tcg_gen_andc_tl(t1, t1, t2); tcg_temp_free(t2); tcg_gen_or_tl(t0, t0, t1); tcg_temp_free(t1); tcg_gen_ext32s_tl(t0, t0); gen_store_gpr(t0, rt); - opn = "lwl"; break; case OPC_LWR: t1 = tcg_temp_new(); @@ -2281,18 +2276,13 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, tcg_temp_free(t1); tcg_gen_ext32s_tl(t0, t0); gen_store_gpr(t0, rt); - opn = "lwr"; break; case OPC_LL: case R6_OPC_LL: - save_cpu_state(ctx, 1); op_ld_ll(t0, t0, ctx); gen_store_gpr(t0, rt); - opn = "ll"; break; } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s, %d(%s)", opn, regnames[rt], offset, regnames[base]); tcg_temp_free(t0); } @@ -2300,7 +2290,6 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, static void gen_st (DisasContext *ctx, uint32_t opc, int rt, int base, int16_t offset) { - const char *opn = "st"; TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); @@ -2311,46 +2300,32 @@ static void gen_st (DisasContext *ctx, uint32_t opc, int rt, case OPC_SD: tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEQ | ctx->default_tcg_memop_mask); - opn = "sd"; break; case OPC_SDL: - save_cpu_state(ctx, 1); gen_helper_0e2i(sdl, t1, t0, ctx->mem_idx); - opn = "sdl"; break; case OPC_SDR: - save_cpu_state(ctx, 1); gen_helper_0e2i(sdr, t1, t0, ctx->mem_idx); - opn = "sdr"; break; #endif case OPC_SW: tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL | ctx->default_tcg_memop_mask); - opn = "sw"; break; case OPC_SH: tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUW | ctx->default_tcg_memop_mask); - opn = "sh"; break; case OPC_SB: tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_8); - opn = "sb"; break; case OPC_SWL: - save_cpu_state(ctx, 1); gen_helper_0e2i(swl, t1, t0, ctx->mem_idx); - opn = "swl"; break; case OPC_SWR: - save_cpu_state(ctx, 1); gen_helper_0e2i(swr, t1, t0, ctx->mem_idx); - opn = "swr"; break; } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s, %d(%s)", opn, regnames[rt], offset, regnames[base]); tcg_temp_free(t0); tcg_temp_free(t1); } @@ -2360,7 +2335,6 @@ static void gen_st (DisasContext *ctx, uint32_t opc, int rt, static void gen_st_cond (DisasContext *ctx, uint32_t opc, int rt, int base, int16_t offset) { - const char *opn = "st_cond"; TCGv t0, t1; #ifdef CONFIG_USER_ONLY @@ -2376,20 +2350,14 @@ static void gen_st_cond (DisasContext *ctx, uint32_t opc, int rt, #if defined(TARGET_MIPS64) case OPC_SCD: case R6_OPC_SCD: - save_cpu_state(ctx, 1); op_st_scd(t1, t0, rt, ctx); - opn = "scd"; break; #endif case OPC_SC: case R6_OPC_SC: - save_cpu_state(ctx, 1); op_st_sc(t1, t0, rt, ctx); - opn = "sc"; break; } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s, %d(%s)", opn, regnames[rt], offset, regnames[base]); tcg_temp_free(t1); tcg_temp_free(t0); } @@ -2398,7 +2366,6 @@ static void gen_st_cond (DisasContext *ctx, uint32_t opc, int rt, static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft, int base, int16_t offset) { - const char *opn = "flt_ldst"; TCGv t0 = tcg_temp_new(); gen_base_offset_addr(ctx, t0, base, offset); @@ -2413,7 +2380,6 @@ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft, gen_store_fpr32(ctx, fp0, ft); tcg_temp_free_i32(fp0); } - opn = "lwc1"; break; case OPC_SWC1: { @@ -2423,7 +2389,6 @@ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft, ctx->default_tcg_memop_mask); tcg_temp_free_i32(fp0); } - opn = "swc1"; break; case OPC_LDC1: { @@ -2433,7 +2398,6 @@ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft, gen_store_fpr64(ctx, fp0, ft); tcg_temp_free_i64(fp0); } - opn = "ldc1"; break; case OPC_SDC1: { @@ -2443,15 +2407,12 @@ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft, ctx->default_tcg_memop_mask); tcg_temp_free_i64(fp0); } - opn = "sdc1"; break; default: - MIPS_INVAL(opn); - generate_exception(ctx, EXCP_RI); + MIPS_INVAL("flt_ldst"); + generate_exception_end(ctx, EXCP_RI); goto out; } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s, %d(%s)", opn, fregnames[ft], offset, regnames[base]); out: tcg_temp_free(t0); } @@ -2479,12 +2440,10 @@ static void gen_arith_imm(DisasContext *ctx, uint32_t opc, int rt, int rs, int16_t imm) { target_ulong uimm = (target_long)imm; /* Sign extend to 32/64 bits */ - const char *opn = "imm arith"; if (rt == 0 && opc != OPC_ADDI && opc != OPC_DADDI) { /* If no destination, treat it as a NOP. For addi, we must generate the overflow exception when needed. */ - MIPS_DEBUG("NOP"); return; } switch (opc) { @@ -2512,7 +2471,6 @@ static void gen_arith_imm(DisasContext *ctx, uint32_t opc, gen_store_gpr(t0, rt); tcg_temp_free(t0); } - opn = "addi"; break; case OPC_ADDIU: if (rs != 0) { @@ -2521,7 +2479,6 @@ static void gen_arith_imm(DisasContext *ctx, uint32_t opc, } else { tcg_gen_movi_tl(cpu_gpr[rt], uimm); } - opn = "addiu"; break; #if defined(TARGET_MIPS64) case OPC_DADDI: @@ -2546,7 +2503,6 @@ static void gen_arith_imm(DisasContext *ctx, uint32_t opc, gen_store_gpr(t0, rt); tcg_temp_free(t0); } - opn = "daddi"; break; case OPC_DADDIU: if (rs != 0) { @@ -2554,12 +2510,9 @@ static void gen_arith_imm(DisasContext *ctx, uint32_t opc, } else { tcg_gen_movi_tl(cpu_gpr[rt], uimm); } - opn = "daddiu"; break; #endif } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm); } /* Logic with immediate operand */ @@ -2570,7 +2523,6 @@ static void gen_logic_imm(DisasContext *ctx, uint32_t opc, if (rt == 0) { /* If no destination, treat it as a NOP. */ - MIPS_DEBUG("NOP"); return; } uimm = (uint16_t)imm; @@ -2580,39 +2532,30 @@ static void gen_logic_imm(DisasContext *ctx, uint32_t opc, tcg_gen_andi_tl(cpu_gpr[rt], cpu_gpr[rs], uimm); else tcg_gen_movi_tl(cpu_gpr[rt], 0); - MIPS_DEBUG("andi %s, %s, " TARGET_FMT_lx, regnames[rt], - regnames[rs], uimm); break; case OPC_ORI: if (rs != 0) tcg_gen_ori_tl(cpu_gpr[rt], cpu_gpr[rs], uimm); else tcg_gen_movi_tl(cpu_gpr[rt], uimm); - MIPS_DEBUG("ori %s, %s, " TARGET_FMT_lx, regnames[rt], - regnames[rs], uimm); break; case OPC_XORI: if (likely(rs != 0)) tcg_gen_xori_tl(cpu_gpr[rt], cpu_gpr[rs], uimm); else tcg_gen_movi_tl(cpu_gpr[rt], uimm); - MIPS_DEBUG("xori %s, %s, " TARGET_FMT_lx, regnames[rt], - regnames[rs], uimm); break; case OPC_LUI: if (rs != 0 && (ctx->insn_flags & ISA_MIPS32R6)) { /* OPC_AUI */ tcg_gen_addi_tl(cpu_gpr[rt], cpu_gpr[rs], imm << 16); tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); - MIPS_DEBUG("aui %s, %s, %04x", regnames[rt], regnames[rs], imm); } else { tcg_gen_movi_tl(cpu_gpr[rt], imm << 16); - MIPS_DEBUG("lui %s, " TARGET_FMT_lx, regnames[rt], uimm); } break; default: - MIPS_DEBUG("Unknown logical immediate opcode %08x", opc); break; } } @@ -2622,12 +2565,10 @@ static void gen_slt_imm(DisasContext *ctx, uint32_t opc, int rt, int rs, int16_t imm) { target_ulong uimm = (target_long)imm; /* Sign extend to 32/64 bits */ - const char *opn = "imm arith"; TCGv t0; if (rt == 0) { /* If no destination, treat it as a NOP. */ - MIPS_DEBUG("NOP"); return; } t0 = tcg_temp_new(); @@ -2635,15 +2576,11 @@ static void gen_slt_imm(DisasContext *ctx, uint32_t opc, switch (opc) { case OPC_SLTI: tcg_gen_setcondi_tl(TCG_COND_LT, cpu_gpr[rt], t0, uimm); - opn = "slti"; break; case OPC_SLTIU: tcg_gen_setcondi_tl(TCG_COND_LTU, cpu_gpr[rt], t0, uimm); - opn = "sltiu"; break; } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm); tcg_temp_free(t0); } @@ -2652,12 +2589,10 @@ static void gen_shift_imm(DisasContext *ctx, uint32_t opc, int rt, int rs, int16_t imm) { target_ulong uimm = ((uint16_t)imm) & 0x1f; - const char *opn = "imm shift"; TCGv t0; if (rt == 0) { /* If no destination, treat it as a NOP. */ - MIPS_DEBUG("NOP"); return; } @@ -2667,11 +2602,9 @@ static void gen_shift_imm(DisasContext *ctx, uint32_t opc, case OPC_SLL: tcg_gen_shli_tl(t0, t0, uimm); tcg_gen_ext32s_tl(cpu_gpr[rt], t0); - opn = "sll"; break; case OPC_SRA: tcg_gen_sari_tl(cpu_gpr[rt], t0, uimm); - opn = "sra"; break; case OPC_SRL: if (uimm != 0) { @@ -2680,7 +2613,6 @@ static void gen_shift_imm(DisasContext *ctx, uint32_t opc, } else { tcg_gen_ext32s_tl(cpu_gpr[rt], t0); } - opn = "srl"; break; case OPC_ROTR: if (uimm != 0) { @@ -2693,20 +2625,16 @@ static void gen_shift_imm(DisasContext *ctx, uint32_t opc, } else { tcg_gen_ext32s_tl(cpu_gpr[rt], t0); } - opn = "rotr"; break; #if defined(TARGET_MIPS64) case OPC_DSLL: tcg_gen_shli_tl(cpu_gpr[rt], t0, uimm); - opn = "dsll"; break; case OPC_DSRA: tcg_gen_sari_tl(cpu_gpr[rt], t0, uimm); - opn = "dsra"; break; case OPC_DSRL: tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm); - opn = "dsrl"; break; case OPC_DROTR: if (uimm != 0) { @@ -2714,28 +2642,21 @@ static void gen_shift_imm(DisasContext *ctx, uint32_t opc, } else { tcg_gen_mov_tl(cpu_gpr[rt], t0); } - opn = "drotr"; break; case OPC_DSLL32: tcg_gen_shli_tl(cpu_gpr[rt], t0, uimm + 32); - opn = "dsll32"; break; case OPC_DSRA32: tcg_gen_sari_tl(cpu_gpr[rt], t0, uimm + 32); - opn = "dsra32"; break; case OPC_DSRL32: tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm + 32); - opn = "dsrl32"; break; case OPC_DROTR32: tcg_gen_rotri_tl(cpu_gpr[rt], t0, uimm + 32); - opn = "drotr32"; break; #endif } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm); tcg_temp_free(t0); } @@ -2743,13 +2664,10 @@ static void gen_shift_imm(DisasContext *ctx, uint32_t opc, static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs, int rt) { - const char *opn = "arith"; - if (rd == 0 && opc != OPC_ADD && opc != OPC_SUB && opc != OPC_DADD && opc != OPC_DSUB) { /* If no destination, treat it as a NOP. For add & sub, we must generate the overflow exception when needed. */ - MIPS_DEBUG("NOP"); return; } @@ -2777,7 +2695,6 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, gen_store_gpr(t0, rd); tcg_temp_free(t0); } - opn = "add"; break; case OPC_ADDU: if (rs != 0 && rt != 0) { @@ -2790,7 +2707,6 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, } else { tcg_gen_movi_tl(cpu_gpr[rd], 0); } - opn = "addu"; break; case OPC_SUB: { @@ -2815,7 +2731,6 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, gen_store_gpr(t0, rd); tcg_temp_free(t0); } - opn = "sub"; break; case OPC_SUBU: if (rs != 0 && rt != 0) { @@ -2829,7 +2744,6 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, } else { tcg_gen_movi_tl(cpu_gpr[rd], 0); } - opn = "subu"; break; #if defined(TARGET_MIPS64) case OPC_DADD: @@ -2854,7 +2768,6 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, gen_store_gpr(t0, rd); tcg_temp_free(t0); } - opn = "dadd"; break; case OPC_DADDU: if (rs != 0 && rt != 0) { @@ -2866,7 +2779,6 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, } else { tcg_gen_movi_tl(cpu_gpr[rd], 0); } - opn = "daddu"; break; case OPC_DSUB: { @@ -2890,7 +2802,6 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, gen_store_gpr(t0, rd); tcg_temp_free(t0); } - opn = "dsub"; break; case OPC_DSUBU: if (rs != 0 && rt != 0) { @@ -2902,7 +2813,6 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, } else { tcg_gen_movi_tl(cpu_gpr[rd], 0); } - opn = "dsubu"; break; #endif case OPC_MUL: @@ -2912,23 +2822,18 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, } else { tcg_gen_movi_tl(cpu_gpr[rd], 0); } - opn = "mul"; break; } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]); } /* Conditional move */ static void gen_cond_move(DisasContext *ctx, uint32_t opc, int rd, int rs, int rt) { - const char *opn = "cond move"; TCGv t0, t1, t2; if (rd == 0) { /* If no destination, treat it as a NOP. */ - MIPS_DEBUG("NOP"); return; } @@ -2940,38 +2845,28 @@ static void gen_cond_move(DisasContext *ctx, uint32_t opc, switch (opc) { case OPC_MOVN: tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr[rd], t0, t1, t2, cpu_gpr[rd]); - opn = "movn"; break; case OPC_MOVZ: tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr[rd], t0, t1, t2, cpu_gpr[rd]); - opn = "movz"; break; case OPC_SELNEZ: tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr[rd], t0, t1, t2, t1); - opn = "selnez"; break; case OPC_SELEQZ: tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr[rd], t0, t1, t2, t1); - opn = "seleqz"; break; } tcg_temp_free(t2); tcg_temp_free(t1); tcg_temp_free(t0); - - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]); } /* Logic */ static void gen_logic(DisasContext *ctx, uint32_t opc, int rd, int rs, int rt) { - const char *opn = "logic"; - if (rd == 0) { /* If no destination, treat it as a NOP. */ - MIPS_DEBUG("NOP"); return; } @@ -2982,7 +2877,6 @@ static void gen_logic(DisasContext *ctx, uint32_t opc, } else { tcg_gen_movi_tl(cpu_gpr[rd], 0); } - opn = "and"; break; case OPC_NOR: if (rs != 0 && rt != 0) { @@ -2994,7 +2888,6 @@ static void gen_logic(DisasContext *ctx, uint32_t opc, } else { tcg_gen_movi_tl(cpu_gpr[rd], ~((target_ulong)0)); } - opn = "nor"; break; case OPC_OR: if (likely(rs != 0 && rt != 0)) { @@ -3006,7 +2899,6 @@ static void gen_logic(DisasContext *ctx, uint32_t opc, } else { tcg_gen_movi_tl(cpu_gpr[rd], 0); } - opn = "or"; break; case OPC_XOR: if (likely(rs != 0 && rt != 0)) { @@ -3018,23 +2910,18 @@ static void gen_logic(DisasContext *ctx, uint32_t opc, } else { tcg_gen_movi_tl(cpu_gpr[rd], 0); } - opn = "xor"; break; } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]); } /* Set on lower than */ static void gen_slt(DisasContext *ctx, uint32_t opc, int rd, int rs, int rt) { - const char *opn = "slt"; TCGv t0, t1; if (rd == 0) { /* If no destination, treat it as a NOP. */ - MIPS_DEBUG("NOP"); return; } @@ -3045,15 +2932,11 @@ static void gen_slt(DisasContext *ctx, uint32_t opc, switch (opc) { case OPC_SLT: tcg_gen_setcond_tl(TCG_COND_LT, cpu_gpr[rd], t0, t1); - opn = "slt"; break; case OPC_SLTU: tcg_gen_setcond_tl(TCG_COND_LTU, cpu_gpr[rd], t0, t1); - opn = "sltu"; break; } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]); tcg_temp_free(t0); tcg_temp_free(t1); } @@ -3062,13 +2945,11 @@ static void gen_slt(DisasContext *ctx, uint32_t opc, static void gen_shift(DisasContext *ctx, uint32_t opc, int rd, int rs, int rt) { - const char *opn = "shifts"; TCGv t0, t1; if (rd == 0) { /* If no destination, treat it as a NOP. For add & sub, we must generate the overflow exception when needed. */ - MIPS_DEBUG("NOP"); return; } @@ -3081,19 +2962,16 @@ static void gen_shift(DisasContext *ctx, uint32_t opc, tcg_gen_andi_tl(t0, t0, 0x1f); tcg_gen_shl_tl(t0, t1, t0); tcg_gen_ext32s_tl(cpu_gpr[rd], t0); - opn = "sllv"; break; case OPC_SRAV: tcg_gen_andi_tl(t0, t0, 0x1f); tcg_gen_sar_tl(cpu_gpr[rd], t1, t0); - opn = "srav"; break; case OPC_SRLV: tcg_gen_ext32u_tl(t1, t1); tcg_gen_andi_tl(t0, t0, 0x1f); tcg_gen_shr_tl(t0, t1, t0); tcg_gen_ext32s_tl(cpu_gpr[rd], t0); - opn = "srlv"; break; case OPC_ROTRV: { @@ -3107,34 +2985,27 @@ static void gen_shift(DisasContext *ctx, uint32_t opc, tcg_gen_ext_i32_tl(cpu_gpr[rd], t2); tcg_temp_free_i32(t2); tcg_temp_free_i32(t3); - opn = "rotrv"; } break; #if defined(TARGET_MIPS64) case OPC_DSLLV: tcg_gen_andi_tl(t0, t0, 0x3f); tcg_gen_shl_tl(cpu_gpr[rd], t1, t0); - opn = "dsllv"; break; case OPC_DSRAV: tcg_gen_andi_tl(t0, t0, 0x3f); tcg_gen_sar_tl(cpu_gpr[rd], t1, t0); - opn = "dsrav"; break; case OPC_DSRLV: tcg_gen_andi_tl(t0, t0, 0x3f); tcg_gen_shr_tl(cpu_gpr[rd], t1, t0); - opn = "dsrlv"; break; case OPC_DROTRV: tcg_gen_andi_tl(t0, t0, 0x3f); tcg_gen_rotr_tl(cpu_gpr[rd], t1, t0); - opn = "drotrv"; break; #endif } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]); tcg_temp_free(t0); tcg_temp_free(t1); } @@ -3142,11 +3013,8 @@ static void gen_shift(DisasContext *ctx, uint32_t opc, /* Arithmetic on HI/LO registers */ static void gen_HILO(DisasContext *ctx, uint32_t opc, int acc, int reg) { - const char *opn = "hilo"; - if (reg == 0 && (opc == OPC_MFHI || opc == OPC_MFLO)) { /* Treat as NOP. */ - MIPS_DEBUG("NOP"); return; } @@ -3164,7 +3032,6 @@ static void gen_HILO(DisasContext *ctx, uint32_t opc, int acc, int reg) { tcg_gen_mov_tl(cpu_gpr[reg], cpu_HI[acc]); } - opn = "mfhi"; break; case OPC_MFLO: #if defined(TARGET_MIPS64) @@ -3175,7 +3042,6 @@ static void gen_HILO(DisasContext *ctx, uint32_t opc, int acc, int reg) { tcg_gen_mov_tl(cpu_gpr[reg], cpu_LO[acc]); } - opn = "mflo"; break; case OPC_MTHI: if (reg != 0) { @@ -3190,7 +3056,6 @@ static void gen_HILO(DisasContext *ctx, uint32_t opc, int acc, int reg) } else { tcg_gen_movi_tl(cpu_HI[acc], 0); } - opn = "mthi"; break; case OPC_MTLO: if (reg != 0) { @@ -3205,11 +3070,8 @@ static void gen_HILO(DisasContext *ctx, uint32_t opc, int acc, int reg) } else { tcg_gen_movi_tl(cpu_LO[acc], 0); } - opn = "mtlo"; break; } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s", opn, regnames[reg]); } static inline void gen_r6_ld(target_long addr, int reg, int memidx, @@ -3277,7 +3139,7 @@ static inline void gen_pcrel(DisasContext *ctx, int opc, target_ulong pc, #endif default: MIPS_INVAL("OPC_PCREL"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -3286,12 +3148,10 @@ static inline void gen_pcrel(DisasContext *ctx, int opc, target_ulong pc, static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) { - const char *opn = "r6 mul/div"; TCGv t0, t1; if (rd == 0) { /* Treat as NOP. */ - MIPS_DEBUG("NOP"); return; } @@ -3320,7 +3180,6 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_temp_free(t3); tcg_temp_free(t2); } - opn = "div"; break; case R6_OPC_MOD: { @@ -3340,7 +3199,6 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_temp_free(t3); tcg_temp_free(t2); } - opn = "mod"; break; case R6_OPC_DIVU: { @@ -3354,7 +3212,6 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_temp_free(t3); tcg_temp_free(t2); } - opn = "divu"; break; case R6_OPC_MODU: { @@ -3368,7 +3225,6 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_temp_free(t3); tcg_temp_free(t2); } - opn = "modu"; break; case R6_OPC_MUL: { @@ -3381,7 +3237,6 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_temp_free_i32(t2); tcg_temp_free_i32(t3); } - opn = "mul"; break; case R6_OPC_MUH: { @@ -3394,7 +3249,6 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_temp_free_i32(t2); tcg_temp_free_i32(t3); } - opn = "muh"; break; case R6_OPC_MULU: { @@ -3407,7 +3261,6 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_temp_free_i32(t2); tcg_temp_free_i32(t3); } - opn = "mulu"; break; case R6_OPC_MUHU: { @@ -3420,7 +3273,6 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_temp_free_i32(t2); tcg_temp_free_i32(t3); } - opn = "muhu"; break; #if defined(TARGET_MIPS64) case R6_OPC_DDIV: @@ -3438,7 +3290,6 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_temp_free(t3); tcg_temp_free(t2); } - opn = "ddiv"; break; case R6_OPC_DMOD: { @@ -3455,7 +3306,6 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_temp_free(t3); tcg_temp_free(t2); } - opn = "dmod"; break; case R6_OPC_DDIVU: { @@ -3466,7 +3316,6 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_temp_free(t3); tcg_temp_free(t2); } - opn = "ddivu"; break; case R6_OPC_DMODU: { @@ -3477,11 +3326,9 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_temp_free(t3); tcg_temp_free(t2); } - opn = "dmodu"; break; case R6_OPC_DMUL: tcg_gen_mul_i64(cpu_gpr[rd], t0, t1); - opn = "dmul"; break; case R6_OPC_DMUH: { @@ -3489,11 +3336,9 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_gen_muls2_i64(t2, cpu_gpr[rd], t0, t1); tcg_temp_free(t2); } - opn = "dmuh"; break; case R6_OPC_DMULU: tcg_gen_mul_i64(cpu_gpr[rd], t0, t1); - opn = "dmulu"; break; case R6_OPC_DMUHU: { @@ -3501,16 +3346,13 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_gen_mulu2_i64(t2, cpu_gpr[rd], t0, t1); tcg_temp_free(t2); } - opn = "dmuhu"; break; #endif default: - MIPS_INVAL(opn); - generate_exception(ctx, EXCP_RI); + MIPS_INVAL("r6 mul/div"); + generate_exception_end(ctx, EXCP_RI); goto out; } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s %s", opn, regnames[rs], regnames[rt]); out: tcg_temp_free(t0); tcg_temp_free(t1); @@ -3519,7 +3361,6 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) static void gen_muldiv(DisasContext *ctx, uint32_t opc, int acc, int rs, int rt) { - const char *opn = "mul/div"; TCGv t0, t1; t0 = tcg_temp_new(); @@ -3553,7 +3394,6 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_temp_free(t3); tcg_temp_free(t2); } - opn = "div"; break; case OPC_DIVU: { @@ -3569,7 +3409,6 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_temp_free(t3); tcg_temp_free(t2); } - opn = "divu"; break; case OPC_MULT: { @@ -3583,7 +3422,6 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_temp_free_i32(t2); tcg_temp_free_i32(t3); } - opn = "mult"; break; case OPC_MULTU: { @@ -3597,7 +3435,6 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_temp_free_i32(t2); tcg_temp_free_i32(t3); } - opn = "multu"; break; #if defined(TARGET_MIPS64) case OPC_DDIV: @@ -3616,7 +3453,6 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_temp_free(t3); tcg_temp_free(t2); } - opn = "ddiv"; break; case OPC_DDIVU: { @@ -3628,15 +3464,12 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_temp_free(t3); tcg_temp_free(t2); } - opn = "ddivu"; break; case OPC_DMULT: tcg_gen_muls2_i64(cpu_LO[acc], cpu_HI[acc], t0, t1); - opn = "dmult"; break; case OPC_DMULTU: tcg_gen_mulu2_i64(cpu_LO[acc], cpu_HI[acc], t0, t1); - opn = "dmultu"; break; #endif case OPC_MADD: @@ -3650,14 +3483,10 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); tcg_gen_add_i64(t2, t2, t3); tcg_temp_free_i64(t3); - tcg_gen_trunc_i64_tl(t0, t2); - tcg_gen_shri_i64(t2, t2, 32); - tcg_gen_trunc_i64_tl(t1, t2); + gen_move_low32(cpu_LO[acc], t2); + gen_move_high32(cpu_HI[acc], t2); tcg_temp_free_i64(t2); - tcg_gen_ext32s_tl(cpu_LO[acc], t0); - tcg_gen_ext32s_tl(cpu_HI[acc], t1); } - opn = "madd"; break; case OPC_MADDU: { @@ -3672,14 +3501,10 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); tcg_gen_add_i64(t2, t2, t3); tcg_temp_free_i64(t3); - tcg_gen_trunc_i64_tl(t0, t2); - tcg_gen_shri_i64(t2, t2, 32); - tcg_gen_trunc_i64_tl(t1, t2); + gen_move_low32(cpu_LO[acc], t2); + gen_move_high32(cpu_HI[acc], t2); tcg_temp_free_i64(t2); - tcg_gen_ext32s_tl(cpu_LO[acc], t0); - tcg_gen_ext32s_tl(cpu_HI[acc], t1); } - opn = "maddu"; break; case OPC_MSUB: { @@ -3692,14 +3517,10 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); tcg_gen_sub_i64(t2, t3, t2); tcg_temp_free_i64(t3); - tcg_gen_trunc_i64_tl(t0, t2); - tcg_gen_shri_i64(t2, t2, 32); - tcg_gen_trunc_i64_tl(t1, t2); + gen_move_low32(cpu_LO[acc], t2); + gen_move_high32(cpu_HI[acc], t2); tcg_temp_free_i64(t2); - tcg_gen_ext32s_tl(cpu_LO[acc], t0); - tcg_gen_ext32s_tl(cpu_HI[acc], t1); } - opn = "msub"; break; case OPC_MSUBU: { @@ -3714,22 +3535,16 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); tcg_gen_sub_i64(t2, t3, t2); tcg_temp_free_i64(t3); - tcg_gen_trunc_i64_tl(t0, t2); - tcg_gen_shri_i64(t2, t2, 32); - tcg_gen_trunc_i64_tl(t1, t2); + gen_move_low32(cpu_LO[acc], t2); + gen_move_high32(cpu_HI[acc], t2); tcg_temp_free_i64(t2); - tcg_gen_ext32s_tl(cpu_LO[acc], t0); - tcg_gen_ext32s_tl(cpu_HI[acc], t1); } - opn = "msubu"; break; default: - MIPS_INVAL(opn); - generate_exception(ctx, EXCP_RI); + MIPS_INVAL("mul/div"); + generate_exception_end(ctx, EXCP_RI); goto out; } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s %s", opn, regnames[rs], regnames[rt]); out: tcg_temp_free(t0); tcg_temp_free(t1); @@ -3738,7 +3553,6 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, static void gen_mul_vr54xx (DisasContext *ctx, uint32_t opc, int rd, int rs, int rt) { - const char *opn = "mul vr54xx"; TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); @@ -3748,68 +3562,52 @@ static void gen_mul_vr54xx (DisasContext *ctx, uint32_t opc, switch (opc) { case OPC_VR54XX_MULS: gen_helper_muls(t0, cpu_env, t0, t1); - opn = "muls"; break; case OPC_VR54XX_MULSU: gen_helper_mulsu(t0, cpu_env, t0, t1); - opn = "mulsu"; break; case OPC_VR54XX_MACC: gen_helper_macc(t0, cpu_env, t0, t1); - opn = "macc"; break; case OPC_VR54XX_MACCU: gen_helper_maccu(t0, cpu_env, t0, t1); - opn = "maccu"; break; case OPC_VR54XX_MSAC: gen_helper_msac(t0, cpu_env, t0, t1); - opn = "msac"; break; case OPC_VR54XX_MSACU: gen_helper_msacu(t0, cpu_env, t0, t1); - opn = "msacu"; break; case OPC_VR54XX_MULHI: gen_helper_mulhi(t0, cpu_env, t0, t1); - opn = "mulhi"; break; case OPC_VR54XX_MULHIU: gen_helper_mulhiu(t0, cpu_env, t0, t1); - opn = "mulhiu"; break; case OPC_VR54XX_MULSHI: gen_helper_mulshi(t0, cpu_env, t0, t1); - opn = "mulshi"; break; case OPC_VR54XX_MULSHIU: gen_helper_mulshiu(t0, cpu_env, t0, t1); - opn = "mulshiu"; break; case OPC_VR54XX_MACCHI: gen_helper_macchi(t0, cpu_env, t0, t1); - opn = "macchi"; break; case OPC_VR54XX_MACCHIU: gen_helper_macchiu(t0, cpu_env, t0, t1); - opn = "macchiu"; break; case OPC_VR54XX_MSACHI: gen_helper_msachi(t0, cpu_env, t0, t1); - opn = "msachi"; break; case OPC_VR54XX_MSACHIU: gen_helper_msachiu(t0, cpu_env, t0, t1); - opn = "msachiu"; break; default: MIPS_INVAL("mul vr54xx"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); goto out; } gen_store_gpr(t0, rd); - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]); out: tcg_temp_free(t0); @@ -3819,12 +3617,10 @@ static void gen_mul_vr54xx (DisasContext *ctx, uint32_t opc, static void gen_cl (DisasContext *ctx, uint32_t opc, int rd, int rs) { - const char *opn = "CLx"; TCGv t0; if (rd == 0) { /* Treat as NOP. */ - MIPS_DEBUG("NOP"); return; } t0 = tcg_temp_new(); @@ -3833,28 +3629,22 @@ static void gen_cl (DisasContext *ctx, uint32_t opc, case OPC_CLO: case R6_OPC_CLO: gen_helper_clo(cpu_gpr[rd], t0); - opn = "clo"; break; case OPC_CLZ: case R6_OPC_CLZ: gen_helper_clz(cpu_gpr[rd], t0); - opn = "clz"; break; #if defined(TARGET_MIPS64) case OPC_DCLO: case R6_OPC_DCLO: gen_helper_dclo(cpu_gpr[rd], t0); - opn = "dclo"; break; case OPC_DCLZ: case R6_OPC_DCLZ: gen_helper_dclz(cpu_gpr[rd], t0); - opn = "dclz"; break; #endif } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s, %s", opn, regnames[rd], regnames[rs]); tcg_temp_free(t0); } @@ -3862,12 +3652,10 @@ static void gen_cl (DisasContext *ctx, uint32_t opc, static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, int rd, int rs, int rt) { - const char *opn = "loongson"; TCGv t0, t1; if (rd == 0) { /* Treat as NOP. */ - MIPS_DEBUG("NOP"); return; } @@ -3899,7 +3687,6 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, case OPC_MULT_G_2F: tcg_gen_mul_tl(cpu_gpr[rd], t0, t1); tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); - opn = "mult.g"; break; case OPC_MULTU_G_2E: case OPC_MULTU_G_2F: @@ -3907,7 +3694,6 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, tcg_gen_ext32u_tl(t1, t1); tcg_gen_mul_tl(cpu_gpr[rd], t0, t1); tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); - opn = "multu.g"; break; case OPC_DIV_G_2E: case OPC_DIV_G_2F: @@ -3930,7 +3716,6 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); gen_set_label(l3); } - opn = "div.g"; break; case OPC_DIVU_G_2E: case OPC_DIVU_G_2F: @@ -3947,7 +3732,6 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); gen_set_label(l2); } - opn = "divu.g"; break; case OPC_MOD_G_2E: case OPC_MOD_G_2F: @@ -3968,7 +3752,6 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); gen_set_label(l3); } - opn = "mod.g"; break; case OPC_MODU_G_2E: case OPC_MODU_G_2F: @@ -3985,18 +3768,15 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); gen_set_label(l2); } - opn = "modu.g"; break; #if defined(TARGET_MIPS64) case OPC_DMULT_G_2E: case OPC_DMULT_G_2F: tcg_gen_mul_tl(cpu_gpr[rd], t0, t1); - opn = "dmult.g"; break; case OPC_DMULTU_G_2E: case OPC_DMULTU_G_2F: tcg_gen_mul_tl(cpu_gpr[rd], t0, t1); - opn = "dmultu.g"; break; case OPC_DDIV_G_2E: case OPC_DDIV_G_2F: @@ -4016,7 +3796,6 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, tcg_gen_div_tl(cpu_gpr[rd], t0, t1); gen_set_label(l3); } - opn = "ddiv.g"; break; case OPC_DDIVU_G_2E: case OPC_DDIVU_G_2F: @@ -4030,7 +3809,6 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, tcg_gen_divu_tl(cpu_gpr[rd], t0, t1); gen_set_label(l2); } - opn = "ddivu.g"; break; case OPC_DMOD_G_2E: case OPC_DMOD_G_2F: @@ -4048,7 +3826,6 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, tcg_gen_rem_tl(cpu_gpr[rd], t0, t1); gen_set_label(l3); } - opn = "dmod.g"; break; case OPC_DMODU_G_2E: case OPC_DMODU_G_2F: @@ -4062,13 +3839,10 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, tcg_gen_remu_tl(cpu_gpr[rd], t0, t1); gen_set_label(l2); } - opn = "dmodu.g"; break; #endif } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s, %s", opn, regnames[rd], regnames[rs]); tcg_temp_free(t0); tcg_temp_free(t1); } @@ -4076,7 +3850,6 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, /* Loongson multimedia instructions */ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) { - const char *opn = "loongson_cp2"; uint32_t opc, shift_max; TCGv_i64 t0, t1; @@ -4099,11 +3872,11 @@ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) gen_load_fpr64(ctx, t1, rt); #define LMI_HELPER(UP, LO) \ - case OPC_##UP: gen_helper_##LO(t0, t0, t1); opn = #LO; break + case OPC_##UP: gen_helper_##LO(t0, t0, t1); break #define LMI_HELPER_1(UP, LO) \ - case OPC_##UP: gen_helper_##LO(t0, t0); opn = #LO; break + case OPC_##UP: gen_helper_##LO(t0, t0); break #define LMI_DIRECT(UP, LO, OP) \ - case OPC_##UP: tcg_gen_##OP##_i64(t0, t0, t1); opn = #LO; break + case OPC_##UP: tcg_gen_##OP##_i64(t0, t0, t1); break switch (opc) { LMI_HELPER(PADDSH, paddsh); @@ -4174,19 +3947,15 @@ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) case OPC_PINSRH_0: tcg_gen_deposit_i64(t0, t0, t1, 0, 16); - opn = "pinsrh_0"; break; case OPC_PINSRH_1: tcg_gen_deposit_i64(t0, t0, t1, 16, 16); - opn = "pinsrh_1"; break; case OPC_PINSRH_2: tcg_gen_deposit_i64(t0, t0, t1, 32, 16); - opn = "pinsrh_2"; break; case OPC_PINSRH_3: tcg_gen_deposit_i64(t0, t0, t1, 48, 16); - opn = "pinsrh_3"; break; case OPC_PEXTRH: @@ -4194,42 +3963,33 @@ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) tcg_gen_shli_i64(t1, t1, 4); tcg_gen_shr_i64(t0, t0, t1); tcg_gen_ext16u_i64(t0, t0); - opn = "pextrh"; break; case OPC_ADDU_CP2: tcg_gen_add_i64(t0, t0, t1); tcg_gen_ext32s_i64(t0, t0); - opn = "addu"; break; case OPC_SUBU_CP2: tcg_gen_sub_i64(t0, t0, t1); tcg_gen_ext32s_i64(t0, t0); - opn = "addu"; break; case OPC_SLL_CP2: - opn = "sll"; shift_max = 32; goto do_shift; case OPC_SRL_CP2: - opn = "srl"; shift_max = 32; goto do_shift; case OPC_SRA_CP2: - opn = "sra"; shift_max = 32; goto do_shift; case OPC_DSLL_CP2: - opn = "dsll"; shift_max = 64; goto do_shift; case OPC_DSRL_CP2: - opn = "dsrl"; shift_max = 64; goto do_shift; case OPC_DSRA_CP2: - opn = "dsra"; shift_max = 64; goto do_shift; do_shift: @@ -4284,8 +4044,6 @@ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) tcg_gen_brcondi_i64(TCG_COND_GE, t1, 0, lab); generate_exception(ctx, EXCP_OVERFLOW); gen_set_label(lab); - - opn = (opc == OPC_ADD_CP2 ? "add" : "dadd"); break; } @@ -4307,8 +4065,6 @@ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) tcg_gen_brcondi_i64(TCG_COND_GE, t1, 0, lab); generate_exception(ctx, EXCP_OVERFLOW); gen_set_label(lab); - - opn = (opc == OPC_SUB_CP2 ? "sub" : "dsub"); break; } @@ -4316,7 +4072,6 @@ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) tcg_gen_ext32u_i64(t0, t0); tcg_gen_ext32u_i64(t1, t1); tcg_gen_mul_i64(t0, t0, t1); - opn = "pmuluw"; break; case OPC_SEQU_CP2: @@ -4328,8 +4083,8 @@ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) /* ??? Document is unclear: Set FCC[CC]. Does that mean the FD field is the CC field? */ default: - MIPS_INVAL(opn); - generate_exception(ctx, EXCP_RI); + MIPS_INVAL("loongson_cp2"); + generate_exception_end(ctx, EXCP_RI); return; } @@ -4338,9 +4093,6 @@ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) gen_store_fpr64(ctx, t0, rd); - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s, %s, %s", opn, - fregnames[rd], fregnames[rs], fregnames[rt]); tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); } @@ -4392,7 +4144,7 @@ static void gen_trap (DisasContext *ctx, uint32_t opc, case OPC_TGEU: /* rs >= rs unsigned */ case OPC_TGEIU: /* r0 >= 0 unsigned */ /* Always trap */ - generate_exception(ctx, EXCP_TRAP); + generate_exception_end(ctx, EXCP_TRAP); break; case OPC_TLT: /* rs < rs */ case OPC_TLTI: /* r0 < 0 */ @@ -4452,7 +4204,7 @@ static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) gen_save_pc(dest); if (ctx->singlestep_enabled) { save_cpu_state(ctx, 0); - gen_helper_0e0i(raise_exception, EXCP_DEBUG); + gen_helper_raise_exception_debug(cpu_env); } tcg_gen_exit_tb(0); } @@ -4475,7 +4227,7 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, LOG_DISAS("Branch in delay / forbidden slot at PC 0x" TARGET_FMT_lx "\n", ctx->pc); #endif - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); goto out; } @@ -4535,14 +4287,14 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, /* Hint = 0 is JR/JALR, hint 16 is JR.HB/JALR.HB, the others are reserved. */ MIPS_INVAL("jump hint"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); goto out; } gen_load_gpr(btarget, rs); break; default: MIPS_INVAL("branch/jump"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); goto out; } if (bcond_compute == 0) { @@ -4556,20 +4308,17 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, case OPC_BLEZL: /* 0 <= 0 likely */ /* Always take */ ctx->hflags |= MIPS_HFLAG_B; - MIPS_DEBUG("balways"); break; case OPC_BGEZAL: /* 0 >= 0 */ case OPC_BGEZALL: /* 0 >= 0 likely */ /* Always take and link */ blink = 31; ctx->hflags |= MIPS_HFLAG_B; - MIPS_DEBUG("balways and link"); break; case OPC_BNE: /* rx != rx */ case OPC_BGTZ: /* 0 > 0 */ case OPC_BLTZ: /* 0 < 0 */ /* Treat as NOP. */ - MIPS_DEBUG("bnever (NOP)"); goto out; case OPC_BLTZAL: /* 0 < 0 */ /* Handle as an unconditional branch to get correct delay @@ -4577,24 +4326,20 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, blink = 31; btgt = ctx->pc + insn_bytes + delayslot_size; ctx->hflags |= MIPS_HFLAG_B; - MIPS_DEBUG("bnever and link"); break; case OPC_BLTZALL: /* 0 < 0 likely */ tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 8); /* Skip the instruction in the delay slot */ - MIPS_DEBUG("bnever, link and skip"); ctx->pc += 4; goto out; case OPC_BNEL: /* rx != rx likely */ case OPC_BGTZL: /* 0 > 0 likely */ case OPC_BLTZL: /* 0 < 0 likely */ /* Skip the instruction in the delay slot */ - MIPS_DEBUG("bnever and skip"); ctx->pc += 4; goto out; case OPC_J: ctx->hflags |= MIPS_HFLAG_B; - MIPS_DEBUG("j " TARGET_FMT_lx, btgt); break; case OPC_JALX: ctx->hflags |= MIPS_HFLAG_BX; @@ -4602,118 +4347,91 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, case OPC_JAL: blink = 31; ctx->hflags |= MIPS_HFLAG_B; - MIPS_DEBUG("jal " TARGET_FMT_lx, btgt); break; case OPC_JR: ctx->hflags |= MIPS_HFLAG_BR; - MIPS_DEBUG("jr %s", regnames[rs]); break; case OPC_JALR: blink = rt; ctx->hflags |= MIPS_HFLAG_BR; - MIPS_DEBUG("jalr %s, %s", regnames[rt], regnames[rs]); break; default: MIPS_INVAL("branch/jump"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); goto out; } } else { switch (opc) { case OPC_BEQ: tcg_gen_setcond_tl(TCG_COND_EQ, bcond, t0, t1); - MIPS_DEBUG("beq %s, %s, " TARGET_FMT_lx, - regnames[rs], regnames[rt], btgt); goto not_likely; case OPC_BEQL: tcg_gen_setcond_tl(TCG_COND_EQ, bcond, t0, t1); - MIPS_DEBUG("beql %s, %s, " TARGET_FMT_lx, - regnames[rs], regnames[rt], btgt); goto likely; case OPC_BNE: tcg_gen_setcond_tl(TCG_COND_NE, bcond, t0, t1); - MIPS_DEBUG("bne %s, %s, " TARGET_FMT_lx, - regnames[rs], regnames[rt], btgt); goto not_likely; case OPC_BNEL: tcg_gen_setcond_tl(TCG_COND_NE, bcond, t0, t1); - MIPS_DEBUG("bnel %s, %s, " TARGET_FMT_lx, - regnames[rs], regnames[rt], btgt); goto likely; case OPC_BGEZ: tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0); - MIPS_DEBUG("bgez %s, " TARGET_FMT_lx, regnames[rs], btgt); goto not_likely; case OPC_BGEZL: tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0); - MIPS_DEBUG("bgezl %s, " TARGET_FMT_lx, regnames[rs], btgt); goto likely; case OPC_BGEZAL: tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0); - MIPS_DEBUG("bgezal %s, " TARGET_FMT_lx, regnames[rs], btgt); blink = 31; goto not_likely; case OPC_BGEZALL: tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0); blink = 31; - MIPS_DEBUG("bgezall %s, " TARGET_FMT_lx, regnames[rs], btgt); goto likely; case OPC_BGTZ: tcg_gen_setcondi_tl(TCG_COND_GT, bcond, t0, 0); - MIPS_DEBUG("bgtz %s, " TARGET_FMT_lx, regnames[rs], btgt); goto not_likely; case OPC_BGTZL: tcg_gen_setcondi_tl(TCG_COND_GT, bcond, t0, 0); - MIPS_DEBUG("bgtzl %s, " TARGET_FMT_lx, regnames[rs], btgt); goto likely; case OPC_BLEZ: tcg_gen_setcondi_tl(TCG_COND_LE, bcond, t0, 0); - MIPS_DEBUG("blez %s, " TARGET_FMT_lx, regnames[rs], btgt); goto not_likely; case OPC_BLEZL: tcg_gen_setcondi_tl(TCG_COND_LE, bcond, t0, 0); - MIPS_DEBUG("blezl %s, " TARGET_FMT_lx, regnames[rs], btgt); goto likely; case OPC_BLTZ: tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0); - MIPS_DEBUG("bltz %s, " TARGET_FMT_lx, regnames[rs], btgt); goto not_likely; case OPC_BLTZL: tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0); - MIPS_DEBUG("bltzl %s, " TARGET_FMT_lx, regnames[rs], btgt); goto likely; case OPC_BPOSGE32: tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 32); - MIPS_DEBUG("bposge32 " TARGET_FMT_lx, btgt); goto not_likely; #if defined(TARGET_MIPS64) case OPC_BPOSGE64: tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 64); - MIPS_DEBUG("bposge64 " TARGET_FMT_lx, btgt); goto not_likely; #endif case OPC_BLTZAL: tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0); blink = 31; - MIPS_DEBUG("bltzal %s, " TARGET_FMT_lx, regnames[rs], btgt); not_likely: ctx->hflags |= MIPS_HFLAG_BC; break; case OPC_BLTZALL: tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0); blink = 31; - MIPS_DEBUG("bltzall %s, " TARGET_FMT_lx, regnames[rs], btgt); likely: ctx->hflags |= MIPS_HFLAG_BL; break; default: MIPS_INVAL("conditional branch/jump"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); goto out; } } - MIPS_DEBUG("enter ds: link %d cond %02x target " TARGET_FMT_lx, - blink, ctx->hflags, btgt); ctx->btarget = btgt; @@ -4804,7 +4522,7 @@ static void gen_bitops (DisasContext *ctx, uint32_t opc, int rt, default: fail: MIPS_INVAL("bitops"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); tcg_temp_free(t0); tcg_temp_free(t1); return; @@ -4820,7 +4538,6 @@ static void gen_bshfl (DisasContext *ctx, uint32_t op2, int rt, int rd) if (rd == 0) { /* If no destination, treat it as a NOP. */ - MIPS_DEBUG("NOP"); return; } @@ -4877,7 +4594,7 @@ static void gen_bshfl (DisasContext *ctx, uint32_t op2, int rt, int rd) #endif default: MIPS_INVAL("bsfhl"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); tcg_temp_free(t0); return; } @@ -4920,7 +4637,16 @@ static void gen_align(DisasContext *ctx, int opc, int rd, int rs, int rt, t0 = tcg_temp_new(); gen_load_gpr(t0, rt); if (bp == 0) { - tcg_gen_mov_tl(cpu_gpr[rd], t0); + switch (opc) { + case OPC_ALIGN: + tcg_gen_ext32s_tl(cpu_gpr[rd], t0); + break; +#if defined(TARGET_MIPS64) + case OPC_DALIGN: + tcg_gen_mov_tl(cpu_gpr[rd], t0); + break; +#endif + } } else { TCGv t1 = tcg_temp_new(); gen_load_gpr(t1, rs); @@ -5050,19 +4776,18 @@ static inline void gen_mtc0_store32 (TCGv arg, target_ulong off) tcg_temp_free_i32(t0); } -static inline void gen_mtc0_store64 (TCGv arg, target_ulong off) -{ - tcg_gen_ext32s_tl(arg, arg); - tcg_gen_st_tl(arg, cpu_env, off); -} +#define CP0_CHECK(c) \ + do { \ + if (!(c)) { \ + goto cp0_unimplemented; \ + } \ + } while (0) static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel) { const char *rn = "invalid"; - if (!(ctx->hflags & MIPS_HFLAG_ELPA)) { - goto mfhc0_read_zero; - } + CP0_CHECK(ctx->hflags & MIPS_HFLAG_ELPA); switch (reg) { case 2: @@ -5072,7 +4797,7 @@ static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EntryLo0"; break; default: - goto mfhc0_read_zero; + goto cp0_unimplemented; } break; case 3: @@ -5082,7 +4807,7 @@ static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EntryLo1"; break; default: - goto mfhc0_read_zero; + goto cp0_unimplemented; } break; case 17: @@ -5092,8 +4817,13 @@ static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel) ctx->CP0_LLAddr_shift); rn = "LLAddr"; break; + case 1: + CP0_CHECK(ctx->mrp); + gen_helper_mfhc0_maar(arg, cpu_env); + rn = "MAAR"; + break; default: - goto mfhc0_read_zero; + goto cp0_unimplemented; } break; case 28: @@ -5106,18 +4836,18 @@ static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "TagLo"; break; default: - goto mfhc0_read_zero; + goto cp0_unimplemented; } break; default: - goto mfhc0_read_zero; + goto cp0_unimplemented; } (void)rn; /* avoid a compiler warning */ LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel); return; -mfhc0_read_zero: +cp0_unimplemented: LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel); tcg_gen_movi_tl(arg, 0); } @@ -5127,9 +4857,7 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel) const char *rn = "invalid"; uint64_t mask = ctx->PAMask >> 36; - if (!(ctx->hflags & MIPS_HFLAG_ELPA)) { - goto mthc0_nop; - } + CP0_CHECK(ctx->hflags & MIPS_HFLAG_ELPA); switch (reg) { case 2: @@ -5140,7 +4868,7 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EntryLo0"; break; default: - goto mthc0_nop; + goto cp0_unimplemented; } break; case 3: @@ -5151,7 +4879,7 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EntryLo1"; break; default: - goto mthc0_nop; + goto cp0_unimplemented; } break; case 17: @@ -5163,8 +4891,13 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel) treating MTHC0 to LLAddr as NOP. */ rn = "LLAddr"; break; + case 1: + CP0_CHECK(ctx->mrp); + gen_helper_mthc0_maar(cpu_env, arg); + rn = "MAAR"; + break; default: - goto mthc0_nop; + goto cp0_unimplemented; } break; case 28: @@ -5178,15 +4911,15 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "TagLo"; break; default: - goto mthc0_nop; + goto cp0_unimplemented; } break; default: - goto mthc0_nop; + goto cp0_unimplemented; } (void)rn; /* avoid a compiler warning */ -mthc0_nop: +cp0_unimplemented: LOG_DISAS("mthc0 %s (reg %d sel %d)\n", rn, reg, sel); } @@ -5199,13 +4932,6 @@ static inline void gen_mfc0_unimplemented(DisasContext *ctx, TCGv arg) } } -#define CP0_CHECK(c) \ - do { \ - if (!(c)) { \ - goto cp0_unimplemented; \ - } \ - } while (0) - static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) { const char *rn = "invalid"; @@ -5235,6 +4961,11 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mfc0_mvpconf1(arg, cpu_env); rn = "MVPConf1"; break; + case 4: + CP0_CHECK(ctx->vp); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPControl)); + rn = "VPControl"; + break; default: goto cp0_unimplemented; } @@ -5362,6 +5093,11 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) } rn = "EntryLo1"; break; + case 1: + CP0_CHECK(ctx->vp); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_GlobalNumber)); + rn = "GlobalNumber"; + break; default: goto cp0_unimplemented; } @@ -5570,6 +5306,13 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_EBase)); rn = "EBase"; break; + case 3: + check_insn(ctx, ISA_MIPS32R2); + CP0_CHECK(ctx->cmgcr); + tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_CMGCRBase)); + tcg_gen_ext32s_tl(arg, arg); + rn = "CMGCRBase"; + break; default: goto cp0_unimplemented; } @@ -5619,6 +5362,16 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mfc0_lladdr(arg, cpu_env); rn = "LLAddr"; break; + case 1: + CP0_CHECK(ctx->mrp); + gen_helper_mfc0_maar(arg, cpu_env); + rn = "MAAR"; + break; + case 2: + CP0_CHECK(ctx->mrp); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_MAARI)); + rn = "MAARI"; + break; default: goto cp0_unimplemented; } @@ -5750,8 +5503,14 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) } break; case 26: - tcg_gen_movi_tl(arg, 0); /* unimplemented */ - rn = "ECC"; + switch (sel) { + case 0: + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_ErrCtl)); + rn = "ErrCtl"; + break; + default: + goto cp0_unimplemented; + } break; case 27: switch (sel) { @@ -5882,6 +5641,11 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) /* ignored */ rn = "MVPConf1"; break; + case 4: + CP0_CHECK(ctx->vp); + /* ignored */ + rn = "VPControl"; + break; default: goto cp0_unimplemented; } @@ -5914,12 +5678,14 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; case 5: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_mtc0_store64(arg, offsetof(CPUMIPSState, CP0_VPESchedule)); + tcg_gen_st_tl(arg, cpu_env, + offsetof(CPUMIPSState, CP0_VPESchedule)); rn = "VPESchedule"; break; case 6: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_mtc0_store64(arg, offsetof(CPUMIPSState, CP0_VPEScheFBack)); + tcg_gen_st_tl(arg, cpu_env, + offsetof(CPUMIPSState, CP0_VPEScheFBack)); rn = "VPEScheFBack"; break; case 7: @@ -5982,6 +5748,11 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mtc0_entrylo1(cpu_env, arg); rn = "EntryLo1"; break; + case 1: + CP0_CHECK(ctx->vp); + /* ignored */ + rn = "GlobalNumber"; + break; default: goto cp0_unimplemented; } @@ -6169,7 +5940,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 14: switch (sel) { case 0: - gen_mtc0_store64(arg, offsetof(CPUMIPSState, CP0_EPC)); + tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EPC)); rn = "EPC"; break; default: @@ -6246,6 +6017,16 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mtc0_lladdr(cpu_env, arg); rn = "LLAddr"; break; + case 1: + CP0_CHECK(ctx->mrp); + gen_helper_mtc0_maar(cpu_env, arg); + rn = "MAAR"; + break; + case 2: + CP0_CHECK(ctx->mrp); + gen_helper_mtc0_maari(cpu_env, arg); + rn = "MAARI"; + break; default: goto cp0_unimplemented; } @@ -6342,7 +6123,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case 0: /* EJTAG support */ - gen_mtc0_store64(arg, offsetof(CPUMIPSState, CP0_DEPC)); + tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_DEPC)); rn = "DEPC"; break; default: @@ -6388,8 +6169,15 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) } break; case 26: - /* ignored */ - rn = "ECC"; + switch (sel) { + case 0: + gen_helper_mtc0_errctl(cpu_env, arg); + ctx->bstate = BS_STOP; + rn = "ErrCtl"; + break; + default: + goto cp0_unimplemented; + } break; case 27: switch (sel) { @@ -6445,7 +6233,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 30: switch (sel) { case 0: - gen_mtc0_store64(arg, offsetof(CPUMIPSState, CP0_ErrorEPC)); + tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_ErrorEPC)); rn = "ErrorEPC"; break; default: @@ -6517,6 +6305,11 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mfc0_mvpconf1(arg, cpu_env); rn = "MVPConf1"; break; + case 4: + CP0_CHECK(ctx->vp); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPControl)); + rn = "VPControl"; + break; default: goto cp0_unimplemented; } @@ -6618,6 +6411,11 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryLo1)); rn = "EntryLo1"; break; + case 1: + CP0_CHECK(ctx->vp); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_GlobalNumber)); + rn = "GlobalNumber"; + break; default: goto cp0_unimplemented; } @@ -6822,6 +6620,12 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_EBase)); rn = "EBase"; break; + case 3: + check_insn(ctx, ISA_MIPS32R2); + CP0_CHECK(ctx->cmgcr); + tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_CMGCRBase)); + rn = "CMGCRBase"; + break; default: goto cp0_unimplemented; } @@ -6871,6 +6675,16 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_dmfc0_lladdr(arg, cpu_env); rn = "LLAddr"; break; + case 1: + CP0_CHECK(ctx->mrp); + gen_helper_dmfc0_maar(arg, cpu_env); + rn = "MAAR"; + break; + case 2: + CP0_CHECK(ctx->mrp); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_MAARI)); + rn = "MAARI"; + break; default: goto cp0_unimplemented; } @@ -6998,8 +6812,14 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) } break; case 26: - tcg_gen_movi_tl(arg, 0); /* unimplemented */ - rn = "ECC"; + switch (sel) { + case 0: + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_ErrCtl)); + rn = "ErrCtl"; + break; + default: + goto cp0_unimplemented; + } break; case 27: switch (sel) { @@ -7124,6 +6944,11 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) /* ignored */ rn = "MVPConf1"; break; + case 4: + CP0_CHECK(ctx->vp); + /* ignored */ + rn = "VPControl"; + break; default: goto cp0_unimplemented; } @@ -7224,6 +7049,11 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_dmtc0_entrylo1(cpu_env, arg); rn = "EntryLo1"; break; + case 1: + CP0_CHECK(ctx->vp); + /* ignored */ + rn = "GlobalNumber"; + break; default: goto cp0_unimplemented; } @@ -7492,6 +7322,16 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mtc0_lladdr(cpu_env, arg); rn = "LLAddr"; break; + case 1: + CP0_CHECK(ctx->mrp); + gen_helper_mtc0_maar(cpu_env, arg); + rn = "MAAR"; + break; + case 2: + CP0_CHECK(ctx->mrp); + gen_helper_mtc0_maari(cpu_env, arg); + rn = "MAARI"; + break; default: goto cp0_unimplemented; } @@ -7630,8 +7470,15 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) } break; case 26: - /* ignored */ - rn = "ECC"; + switch (sel) { + case 0: + gen_helper_mtc0_errctl(cpu_env, arg); + ctx->bstate = BS_STOP; + rn = "ErrCtl"; + break; + default: + goto cp0_unimplemented; + } break; case 27: switch (sel) { @@ -7944,7 +7791,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, die: tcg_temp_free(t0); LOG_DISAS("mftr (reg %d u %d sel %d h %d)\n", rt, u, sel, h); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, @@ -8125,7 +7972,6 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, break; case 3: /* XXX: For now we support only a single FPU context. */ - save_cpu_state(ctx, 1); { TCGv_i32 fs_tmp = tcg_const_i32(rd); @@ -8149,7 +7995,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, die: tcg_temp_free(t0); LOG_DISAS("mttr (reg %d u %d sel %d h %d)\n", rd, u, sel, h); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, int rd) @@ -8278,7 +8124,6 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, case OPC_ERET: /* OPC_ERETNC */ if ((ctx->insn_flags & ISA_MIPS32R6) && (ctx->hflags & MIPS_HFLAG_BMASK)) { - MIPS_DEBUG("CTI in delay / forbidden slot"); goto die; } else { int bit_shift = (ctx->hflags & MIPS_HFLAG_M16) ? 16 : 6; @@ -8301,12 +8146,11 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, check_insn(ctx, ISA_MIPS32); if ((ctx->insn_flags & ISA_MIPS32R6) && (ctx->hflags & MIPS_HFLAG_BMASK)) { - MIPS_DEBUG("CTI in delay / forbidden slot"); goto die; } if (!(ctx->hflags & MIPS_HFLAG_DM)) { MIPS_INVAL(opn); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } else { gen_helper_deret(cpu_env); ctx->bstate = BS_EXCP; @@ -8317,7 +8161,6 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, check_insn(ctx, ISA_MIPS3 | ISA_MIPS32); if ((ctx->insn_flags & ISA_MIPS32R6) && (ctx->hflags & MIPS_HFLAG_BMASK)) { - MIPS_DEBUG("CTI in delay / forbidden slot"); goto die; } /* If we get an exception, we want to restart at next instruction */ @@ -8330,11 +8173,10 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, default: die: MIPS_INVAL(opn); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); return; } (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s %d", opn, regnames[rt], rd); } #endif /* !CONFIG_USER_ONLY */ @@ -8343,12 +8185,10 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op, int32_t cc, int32_t offset) { target_ulong btarget; - const char *opn = "cp1 cond branch"; TCGv_i32 t0 = tcg_temp_new_i32(); if ((ctx->insn_flags & ISA_MIPS32R6) && (ctx->hflags & MIPS_HFLAG_BMASK)) { - MIPS_DEBUG("CTI in delay / forbidden slot"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); goto out; } @@ -8363,26 +8203,22 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op, tcg_gen_not_i32(t0, t0); tcg_gen_andi_i32(t0, t0, 1); tcg_gen_extu_i32_tl(bcond, t0); - opn = "bc1f"; goto not_likely; case OPC_BC1FL: tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc)); tcg_gen_not_i32(t0, t0); tcg_gen_andi_i32(t0, t0, 1); tcg_gen_extu_i32_tl(bcond, t0); - opn = "bc1fl"; goto likely; case OPC_BC1T: tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc)); tcg_gen_andi_i32(t0, t0, 1); tcg_gen_extu_i32_tl(bcond, t0); - opn = "bc1t"; goto not_likely; case OPC_BC1TL: tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc)); tcg_gen_andi_i32(t0, t0, 1); tcg_gen_extu_i32_tl(bcond, t0); - opn = "bc1tl"; likely: ctx->hflags |= MIPS_HFLAG_BL; break; @@ -8396,7 +8232,6 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op, tcg_gen_andi_i32(t0, t0, 1); tcg_gen_extu_i32_tl(bcond, t0); } - opn = "bc1any2f"; goto not_likely; case OPC_BC1TANY2: { @@ -8408,7 +8243,6 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op, tcg_gen_andi_i32(t0, t0, 1); tcg_gen_extu_i32_tl(bcond, t0); } - opn = "bc1any2t"; goto not_likely; case OPC_BC1FANY4: { @@ -8424,7 +8258,6 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op, tcg_gen_andi_i32(t0, t0, 1); tcg_gen_extu_i32_tl(bcond, t0); } - opn = "bc1any4f"; goto not_likely; case OPC_BC1TANY4: { @@ -8440,18 +8273,14 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op, tcg_gen_andi_i32(t0, t0, 1); tcg_gen_extu_i32_tl(bcond, t0); } - opn = "bc1any4t"; not_likely: ctx->hflags |= MIPS_HFLAG_BC; break; default: - MIPS_INVAL(opn); - generate_exception (ctx, EXCP_RI); + MIPS_INVAL("cp1 cond branch"); + generate_exception_end(ctx, EXCP_RI); goto out; } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s: cond %02x target " TARGET_FMT_lx, opn, - ctx->hflags, btarget); ctx->btarget = btarget; ctx->hflags |= MIPS_HFLAG_BDS32; out: @@ -8464,7 +8293,6 @@ static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op, int delayslot_size) { target_ulong btarget; - const char *opn = "cp1 cond branch"; TCGv_i64 t0 = tcg_temp_new_i64(); if (ctx->hflags & MIPS_HFLAG_BMASK) { @@ -8472,7 +8300,7 @@ static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op, LOG_DISAS("Branch in delay / forbidden slot at PC 0x" TARGET_FMT_lx "\n", ctx->pc); #endif - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); goto out; } @@ -8484,25 +8312,20 @@ static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op, switch (op) { case OPC_BC1EQZ: tcg_gen_xori_i64(t0, t0, 1); - opn = "bc1eqz"; ctx->hflags |= MIPS_HFLAG_BC; break; case OPC_BC1NEZ: /* t0 already set */ - opn = "bc1nez"; ctx->hflags |= MIPS_HFLAG_BC; break; default: - MIPS_INVAL(opn); - generate_exception(ctx, EXCP_RI); + MIPS_INVAL("cp1 cond branch"); + generate_exception_end(ctx, EXCP_RI); goto out; } tcg_gen_trunc_i64_tl(bcond, t0); - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s: cond %02x target " TARGET_FMT_lx, opn, - ctx->hflags, btarget); ctx->btarget = btarget; switch (delayslot_size) { @@ -8733,7 +8556,6 @@ enum r6_f_cmp_op { }; static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) { - const char *opn = "cp1 move"; TCGv t0 = tcg_temp_new(); switch (opc) { @@ -8746,7 +8568,6 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) tcg_temp_free_i32(fp0); } gen_store_gpr(t0, rt); - opn = "mfc1"; break; case OPC_MTC1: gen_load_gpr(t0, rt); @@ -8757,16 +8578,14 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) gen_store_fpr32(ctx, fp0, fs); tcg_temp_free_i32(fp0); } - opn = "mtc1"; break; case OPC_CFC1: gen_helper_1e0i(cfc1, t0, fs); gen_store_gpr(t0, rt); - opn = "cfc1"; break; case OPC_CTC1: gen_load_gpr(t0, rt); - save_cpu_state(ctx, 1); + save_cpu_state(ctx, 0); { TCGv_i32 fs_tmp = tcg_const_i32(fs); @@ -8775,18 +8594,15 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) } /* Stop translation as we may have changed hflags */ ctx->bstate = BS_STOP; - opn = "ctc1"; break; #if defined(TARGET_MIPS64) case OPC_DMFC1: gen_load_fpr64(ctx, t0, fs); gen_store_gpr(t0, rt); - opn = "dmfc1"; break; case OPC_DMTC1: gen_load_gpr(t0, rt); gen_store_fpr64(ctx, t0, fs); - opn = "dmtc1"; break; #endif case OPC_MFHC1: @@ -8798,7 +8614,6 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) tcg_temp_free_i32(fp0); } gen_store_gpr(t0, rt); - opn = "mfhc1"; break; case OPC_MTHC1: gen_load_gpr(t0, rt); @@ -8809,15 +8624,12 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) gen_store_fpr32h(ctx, fp0, fs); tcg_temp_free_i32(fp0); } - opn = "mthc1"; break; default: - MIPS_INVAL(opn); - generate_exception (ctx, EXCP_RI); + MIPS_INVAL("cp1 move"); + generate_exception_end(ctx, EXCP_RI); goto out; } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s %s", opn, regnames[rt], fregnames[fs]); out: tcg_temp_free(t0); @@ -8947,7 +8759,7 @@ static void gen_sel_s(DisasContext *ctx, enum fopcode op1, int fd, int ft, break; default: MIPS_INVAL("gen_sel_s"); - generate_exception (ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } @@ -8984,7 +8796,7 @@ static void gen_sel_d(DisasContext *ctx, enum fopcode op1, int fd, int ft, break; default: MIPS_INVAL("gen_sel_d"); - generate_exception (ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } @@ -8998,44 +8810,6 @@ static void gen_sel_d(DisasContext *ctx, enum fopcode op1, int fd, int ft, static void gen_farith (DisasContext *ctx, enum fopcode op1, int ft, int fs, int fd, int cc) { - const char *opn = "farith"; - const char *condnames[] = { - "c.f", - "c.un", - "c.eq", - "c.ueq", - "c.olt", - "c.ult", - "c.ole", - "c.ule", - "c.sf", - "c.ngle", - "c.seq", - "c.ngl", - "c.lt", - "c.nge", - "c.le", - "c.ngt", - }; - const char *condnames_abs[] = { - "cabs.f", - "cabs.un", - "cabs.eq", - "cabs.ueq", - "cabs.olt", - "cabs.ult", - "cabs.ole", - "cabs.ule", - "cabs.sf", - "cabs.ngle", - "cabs.seq", - "cabs.ngl", - "cabs.lt", - "cabs.nge", - "cabs.le", - "cabs.ngt", - }; - enum { BINOP, CMPOP, OTHEROP } optype = OTHEROP; uint32_t func = ctx->opcode & 0x3f; switch (op1) { case OPC_ADD_S: @@ -9050,8 +8824,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "add.s"; - optype = BINOP; break; case OPC_SUB_S: { @@ -9065,8 +8837,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "sub.s"; - optype = BINOP; break; case OPC_MUL_S: { @@ -9080,8 +8850,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "mul.s"; - optype = BINOP; break; case OPC_DIV_S: { @@ -9095,8 +8863,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "div.s"; - optype = BINOP; break; case OPC_SQRT_S: { @@ -9107,7 +8873,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "sqrt.s"; break; case OPC_ABS_S: { @@ -9118,7 +8883,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "abs.s"; break; case OPC_MOV_S: { @@ -9128,7 +8892,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "mov.s"; break; case OPC_NEG_S: { @@ -9139,7 +8902,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "neg.s"; break; case OPC_ROUND_L_S: check_cp1_64bitmode(ctx); @@ -9153,7 +8915,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp64, fd); tcg_temp_free_i64(fp64); } - opn = "round.l.s"; break; case OPC_TRUNC_L_S: check_cp1_64bitmode(ctx); @@ -9167,7 +8928,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp64, fd); tcg_temp_free_i64(fp64); } - opn = "trunc.l.s"; break; case OPC_CEIL_L_S: check_cp1_64bitmode(ctx); @@ -9181,7 +8941,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp64, fd); tcg_temp_free_i64(fp64); } - opn = "ceil.l.s"; break; case OPC_FLOOR_L_S: check_cp1_64bitmode(ctx); @@ -9195,7 +8954,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp64, fd); tcg_temp_free_i64(fp64); } - opn = "floor.l.s"; break; case OPC_ROUND_W_S: { @@ -9206,7 +8964,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "round.w.s"; break; case OPC_TRUNC_W_S: { @@ -9217,7 +8974,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "trunc.w.s"; break; case OPC_CEIL_W_S: { @@ -9228,7 +8984,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "ceil.w.s"; break; case OPC_FLOOR_W_S: { @@ -9239,27 +8994,22 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "floor.w.s"; break; case OPC_SEL_S: check_insn(ctx, ISA_MIPS32R6); gen_sel_s(ctx, op1, fd, ft, fs); - opn = "sel.s"; break; case OPC_SELEQZ_S: check_insn(ctx, ISA_MIPS32R6); gen_sel_s(ctx, op1, fd, ft, fs); - opn = "seleqz.s"; break; case OPC_SELNEZ_S: check_insn(ctx, ISA_MIPS32R6); gen_sel_s(ctx, op1, fd, ft, fs); - opn = "selnez.s"; break; case OPC_MOVCF_S: check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_movcf_s(ctx, fs, fd, (ft >> 2) & 0x7, ft & 0x1); - opn = "movcf.s"; break; case OPC_MOVZ_S: check_insn_opc_removed(ctx, ISA_MIPS32R6); @@ -9276,7 +9026,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, tcg_temp_free_i32(fp0); gen_set_label(l1); } - opn = "movz.s"; break; case OPC_MOVN_S: check_insn_opc_removed(ctx, ISA_MIPS32R6); @@ -9293,10 +9042,8 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_set_label(l1); } } - opn = "movn.s"; break; case OPC_RECIP_S: - check_cop1x(ctx); { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -9305,10 +9052,8 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "recip.s"; break; case OPC_RSQRT_S: - check_cop1x(ctx); { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -9317,7 +9062,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "rsqrt.s"; break; case OPC_MADDF_S: check_insn(ctx, ISA_MIPS32R6); @@ -9333,7 +9077,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, tcg_temp_free_i32(fp2); tcg_temp_free_i32(fp1); tcg_temp_free_i32(fp0); - opn = "maddf.s"; } break; case OPC_MSUBF_S: @@ -9350,7 +9093,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, tcg_temp_free_i32(fp2); tcg_temp_free_i32(fp1); tcg_temp_free_i32(fp0); - opn = "msubf.s"; } break; case OPC_RINT_S: @@ -9361,7 +9103,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_helper_float_rint_s(fp0, cpu_env, fp0); gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); - opn = "rint.s"; } break; case OPC_CLASS_S: @@ -9372,7 +9113,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_helper_float_class_s(fp0, fp0); gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); - opn = "class.s"; } break; case OPC_MIN_S: /* OPC_RECIP2_S */ @@ -9388,7 +9128,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, tcg_temp_free_i32(fp2); tcg_temp_free_i32(fp1); tcg_temp_free_i32(fp0); - opn = "min.s"; } else { /* OPC_RECIP2_S */ check_cp1_64bitmode(ctx); @@ -9403,7 +9142,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "recip2.s"; } break; case OPC_MINA_S: /* OPC_RECIP1_S */ @@ -9419,7 +9157,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, tcg_temp_free_i32(fp2); tcg_temp_free_i32(fp1); tcg_temp_free_i32(fp0); - opn = "mina.s"; } else { /* OPC_RECIP1_S */ check_cp1_64bitmode(ctx); @@ -9431,7 +9168,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "recip1.s"; } break; case OPC_MAX_S: /* OPC_RSQRT1_S */ @@ -9445,7 +9181,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp1, fd); tcg_temp_free_i32(fp1); tcg_temp_free_i32(fp0); - opn = "max.s"; } else { /* OPC_RSQRT1_S */ check_cp1_64bitmode(ctx); @@ -9457,7 +9192,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "rsqrt1.s"; } break; case OPC_MAXA_S: /* OPC_RSQRT2_S */ @@ -9471,7 +9205,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp1, fd); tcg_temp_free_i32(fp1); tcg_temp_free_i32(fp0); - opn = "maxa.s"; } else { /* OPC_RSQRT2_S */ check_cp1_64bitmode(ctx); @@ -9486,7 +9219,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "rsqrt2.s"; } break; case OPC_CVT_D_S: @@ -9501,7 +9233,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp64, fd); tcg_temp_free_i64(fp64); } - opn = "cvt.d.s"; break; case OPC_CVT_W_S: { @@ -9512,7 +9243,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "cvt.w.s"; break; case OPC_CVT_L_S: check_cp1_64bitmode(ctx); @@ -9526,7 +9256,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp64, fd); tcg_temp_free_i64(fp64); } - opn = "cvt.l.s"; break; case OPC_CVT_PS_S: check_ps(ctx); @@ -9543,7 +9272,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp64, fd); tcg_temp_free_i64(fp64); } - opn = "cvt.ps.s"; break; case OPC_CMP_F_S: case OPC_CMP_UN_S: @@ -9564,12 +9292,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, check_insn_opc_removed(ctx, ISA_MIPS32R6); if (ctx->opcode & (1 << 6)) { gen_cmpabs_s(ctx, func-48, ft, fs, cc); - opn = condnames_abs[func-48]; } else { gen_cmp_s(ctx, func-48, ft, fs, cc); - opn = condnames[func-48]; } - optype = CMPOP; break; case OPC_ADD_D: check_cp1_registers(ctx, fs | ft | fd); @@ -9584,8 +9309,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "add.d"; - optype = BINOP; break; case OPC_SUB_D: check_cp1_registers(ctx, fs | ft | fd); @@ -9600,8 +9323,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "sub.d"; - optype = BINOP; break; case OPC_MUL_D: check_cp1_registers(ctx, fs | ft | fd); @@ -9616,8 +9337,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "mul.d"; - optype = BINOP; break; case OPC_DIV_D: check_cp1_registers(ctx, fs | ft | fd); @@ -9632,8 +9351,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "div.d"; - optype = BINOP; break; case OPC_SQRT_D: check_cp1_registers(ctx, fs | fd); @@ -9645,7 +9362,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "sqrt.d"; break; case OPC_ABS_D: check_cp1_registers(ctx, fs | fd); @@ -9657,7 +9373,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "abs.d"; break; case OPC_MOV_D: check_cp1_registers(ctx, fs | fd); @@ -9668,7 +9383,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "mov.d"; break; case OPC_NEG_D: check_cp1_registers(ctx, fs | fd); @@ -9680,7 +9394,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "neg.d"; break; case OPC_ROUND_L_D: check_cp1_64bitmode(ctx); @@ -9692,7 +9405,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "round.l.d"; break; case OPC_TRUNC_L_D: check_cp1_64bitmode(ctx); @@ -9704,7 +9416,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "trunc.l.d"; break; case OPC_CEIL_L_D: check_cp1_64bitmode(ctx); @@ -9716,7 +9427,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "ceil.l.d"; break; case OPC_FLOOR_L_D: check_cp1_64bitmode(ctx); @@ -9728,7 +9438,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "floor.l.d"; break; case OPC_ROUND_W_D: check_cp1_registers(ctx, fs); @@ -9742,7 +9451,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp32, fd); tcg_temp_free_i32(fp32); } - opn = "round.w.d"; break; case OPC_TRUNC_W_D: check_cp1_registers(ctx, fs); @@ -9756,7 +9464,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp32, fd); tcg_temp_free_i32(fp32); } - opn = "trunc.w.d"; break; case OPC_CEIL_W_D: check_cp1_registers(ctx, fs); @@ -9770,7 +9477,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp32, fd); tcg_temp_free_i32(fp32); } - opn = "ceil.w.d"; break; case OPC_FLOOR_W_D: check_cp1_registers(ctx, fs); @@ -9784,27 +9490,22 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp32, fd); tcg_temp_free_i32(fp32); } - opn = "floor.w.d"; break; case OPC_SEL_D: check_insn(ctx, ISA_MIPS32R6); gen_sel_d(ctx, op1, fd, ft, fs); - opn = "sel.d"; break; case OPC_SELEQZ_D: check_insn(ctx, ISA_MIPS32R6); gen_sel_d(ctx, op1, fd, ft, fs); - opn = "seleqz.d"; break; case OPC_SELNEZ_D: check_insn(ctx, ISA_MIPS32R6); gen_sel_d(ctx, op1, fd, ft, fs); - opn = "selnez.d"; break; case OPC_MOVCF_D: check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_movcf_d(ctx, fs, fd, (ft >> 2) & 0x7, ft & 0x1); - opn = "movcf.d"; break; case OPC_MOVZ_D: check_insn_opc_removed(ctx, ISA_MIPS32R6); @@ -9821,7 +9522,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, tcg_temp_free_i64(fp0); gen_set_label(l1); } - opn = "movz.d"; break; case OPC_MOVN_D: check_insn_opc_removed(ctx, ISA_MIPS32R6); @@ -9838,10 +9538,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_set_label(l1); } } - opn = "movn.d"; break; case OPC_RECIP_D: - check_cp1_64bitmode(ctx); + check_cp1_registers(ctx, fs | fd); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -9850,10 +9549,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "recip.d"; break; case OPC_RSQRT_D: - check_cp1_64bitmode(ctx); + check_cp1_registers(ctx, fs | fd); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -9862,7 +9560,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "rsqrt.d"; break; case OPC_MADDF_D: check_insn(ctx, ISA_MIPS32R6); @@ -9878,7 +9575,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, tcg_temp_free_i64(fp2); tcg_temp_free_i64(fp1); tcg_temp_free_i64(fp0); - opn = "maddf.d"; } break; case OPC_MSUBF_D: @@ -9895,7 +9591,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, tcg_temp_free_i64(fp2); tcg_temp_free_i64(fp1); tcg_temp_free_i64(fp0); - opn = "msubf.d"; } break; case OPC_RINT_D: @@ -9906,7 +9601,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_helper_float_rint_d(fp0, cpu_env, fp0); gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); - opn = "rint.d"; } break; case OPC_CLASS_D: @@ -9917,7 +9611,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_helper_float_class_d(fp0, fp0); gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); - opn = "class.d"; } break; case OPC_MIN_D: /* OPC_RECIP2_D */ @@ -9931,7 +9624,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp1, fd); tcg_temp_free_i64(fp1); tcg_temp_free_i64(fp0); - opn = "min.d"; } else { /* OPC_RECIP2_D */ check_cp1_64bitmode(ctx); @@ -9946,7 +9638,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "recip2.d"; } break; case OPC_MINA_D: /* OPC_RECIP1_D */ @@ -9960,7 +9651,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp1, fd); tcg_temp_free_i64(fp1); tcg_temp_free_i64(fp0); - opn = "mina.d"; } else { /* OPC_RECIP1_D */ check_cp1_64bitmode(ctx); @@ -9972,7 +9662,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "recip1.d"; } break; case OPC_MAX_D: /* OPC_RSQRT1_D */ @@ -9986,7 +9675,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp1, fd); tcg_temp_free_i64(fp1); tcg_temp_free_i64(fp0); - opn = "max.d"; } else { /* OPC_RSQRT1_D */ check_cp1_64bitmode(ctx); @@ -9998,7 +9686,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "rsqrt1.d"; } break; case OPC_MAXA_D: /* OPC_RSQRT2_D */ @@ -10012,7 +9699,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp1, fd); tcg_temp_free_i64(fp1); tcg_temp_free_i64(fp0); - opn = "maxa.d"; } else { /* OPC_RSQRT2_D */ check_cp1_64bitmode(ctx); @@ -10027,7 +9713,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "rsqrt2.d"; } break; case OPC_CMP_F_D: @@ -10049,12 +9734,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, check_insn_opc_removed(ctx, ISA_MIPS32R6); if (ctx->opcode & (1 << 6)) { gen_cmpabs_d(ctx, func-48, ft, fs, cc); - opn = condnames_abs[func-48]; } else { gen_cmp_d(ctx, func-48, ft, fs, cc); - opn = condnames[func-48]; } - optype = CMPOP; break; case OPC_CVT_S_D: check_cp1_registers(ctx, fs); @@ -10068,7 +9750,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp32, fd); tcg_temp_free_i32(fp32); } - opn = "cvt.s.d"; break; case OPC_CVT_W_D: check_cp1_registers(ctx, fs); @@ -10082,7 +9763,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp32, fd); tcg_temp_free_i32(fp32); } - opn = "cvt.w.d"; break; case OPC_CVT_L_D: check_cp1_64bitmode(ctx); @@ -10094,7 +9774,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "cvt.l.d"; break; case OPC_CVT_S_W: { @@ -10105,7 +9784,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "cvt.s.w"; break; case OPC_CVT_D_W: check_cp1_registers(ctx, fd); @@ -10119,7 +9797,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp64, fd); tcg_temp_free_i64(fp64); } - opn = "cvt.d.w"; break; case OPC_CVT_S_L: check_cp1_64bitmode(ctx); @@ -10133,7 +9810,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp32, fd); tcg_temp_free_i32(fp32); } - opn = "cvt.s.l"; break; case OPC_CVT_D_L: check_cp1_64bitmode(ctx); @@ -10145,7 +9821,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "cvt.d.l"; break; case OPC_CVT_PS_PW: check_ps(ctx); @@ -10157,7 +9832,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "cvt.ps.pw"; break; case OPC_ADD_PS: check_ps(ctx); @@ -10172,7 +9846,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "add.ps"; break; case OPC_SUB_PS: check_ps(ctx); @@ -10187,7 +9860,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "sub.ps"; break; case OPC_MUL_PS: check_ps(ctx); @@ -10202,7 +9874,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "mul.ps"; break; case OPC_ABS_PS: check_ps(ctx); @@ -10214,7 +9885,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "abs.ps"; break; case OPC_MOV_PS: check_ps(ctx); @@ -10225,7 +9895,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "mov.ps"; break; case OPC_NEG_PS: check_ps(ctx); @@ -10237,12 +9906,10 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "neg.ps"; break; case OPC_MOVCF_PS: check_ps(ctx); gen_movcf_ps(ctx, fs, fd, (ft >> 2) & 0x7, ft & 0x1); - opn = "movcf.ps"; break; case OPC_MOVZ_PS: check_ps(ctx); @@ -10258,7 +9925,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, tcg_temp_free_i64(fp0); gen_set_label(l1); } - opn = "movz.ps"; break; case OPC_MOVN_PS: check_ps(ctx); @@ -10275,7 +9941,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_set_label(l1); } } - opn = "movn.ps"; break; case OPC_ADDR_PS: check_ps(ctx); @@ -10290,7 +9955,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "addr.ps"; break; case OPC_MULR_PS: check_ps(ctx); @@ -10305,7 +9969,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "mulr.ps"; break; case OPC_RECIP2_PS: check_ps(ctx); @@ -10320,7 +9983,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "recip2.ps"; break; case OPC_RECIP1_PS: check_ps(ctx); @@ -10332,7 +9994,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "recip1.ps"; break; case OPC_RSQRT1_PS: check_ps(ctx); @@ -10344,7 +10005,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "rsqrt1.ps"; break; case OPC_RSQRT2_PS: check_ps(ctx); @@ -10359,7 +10019,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "rsqrt2.ps"; break; case OPC_CVT_S_PU: check_cp1_64bitmode(ctx); @@ -10371,7 +10030,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "cvt.s.pu"; break; case OPC_CVT_PW_PS: check_ps(ctx); @@ -10383,7 +10041,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "cvt.pw.ps"; break; case OPC_CVT_S_PL: check_cp1_64bitmode(ctx); @@ -10395,7 +10052,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "cvt.s.pl"; break; case OPC_PLL_PS: check_ps(ctx); @@ -10410,7 +10066,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, tcg_temp_free_i32(fp0); tcg_temp_free_i32(fp1); } - opn = "pll.ps"; break; case OPC_PLU_PS: check_ps(ctx); @@ -10425,7 +10080,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, tcg_temp_free_i32(fp0); tcg_temp_free_i32(fp1); } - opn = "plu.ps"; break; case OPC_PUL_PS: check_ps(ctx); @@ -10440,7 +10094,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, tcg_temp_free_i32(fp0); tcg_temp_free_i32(fp1); } - opn = "pul.ps"; break; case OPC_PUU_PS: check_ps(ctx); @@ -10455,7 +10108,6 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, tcg_temp_free_i32(fp0); tcg_temp_free_i32(fp1); } - opn = "puu.ps"; break; case OPC_CMP_F_PS: case OPC_CMP_UN_PS: @@ -10475,38 +10127,21 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, case OPC_CMP_NGT_PS: if (ctx->opcode & (1 << 6)) { gen_cmpabs_ps(ctx, func-48, ft, fs, cc); - opn = condnames_abs[func-48]; } else { gen_cmp_ps(ctx, func-48, ft, fs, cc); - opn = condnames[func-48]; } - optype = CMPOP; break; default: - MIPS_INVAL(opn); - generate_exception (ctx, EXCP_RI); + MIPS_INVAL("farith"); + generate_exception_end(ctx, EXCP_RI); return; } - (void)opn; /* avoid a compiler warning */ - switch (optype) { - case BINOP: - MIPS_DEBUG("%s %s, %s, %s", opn, fregnames[fd], fregnames[fs], fregnames[ft]); - break; - case CMPOP: - MIPS_DEBUG("%s %s,%s", opn, fregnames[fs], fregnames[ft]); - break; - default: - MIPS_DEBUG("%s %s,%s", opn, fregnames[fd], fregnames[fs]); - break; - } } /* Coprocessor 3 (FPU) */ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc, int fd, int fs, int base, int index) { - const char *opn = "extended float load/store"; - int store = 0; TCGv t0 = tcg_temp_new(); if (base == 0) { @@ -10529,7 +10164,6 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc, gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } - opn = "lwxc1"; break; case OPC_LDXC1: check_cop1x(ctx); @@ -10540,7 +10174,6 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "ldxc1"; break; case OPC_LUXC1: check_cp1_64bitmode(ctx); @@ -10552,7 +10185,6 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc, gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); } - opn = "luxc1"; break; case OPC_SWXC1: check_cop1x(ctx); @@ -10562,8 +10194,6 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc, tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, MO_TEUL); tcg_temp_free_i32(fp0); } - opn = "swxc1"; - store = 1; break; case OPC_SDXC1: check_cop1x(ctx); @@ -10574,8 +10204,6 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc, tcg_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, MO_TEQ); tcg_temp_free_i64(fp0); } - opn = "sdxc1"; - store = 1; break; case OPC_SUXC1: check_cp1_64bitmode(ctx); @@ -10586,21 +10214,14 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc, tcg_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, MO_TEQ); tcg_temp_free_i64(fp0); } - opn = "suxc1"; - store = 1; break; } tcg_temp_free(t0); - (void)opn; (void)store; /* avoid compiler warnings */ - MIPS_DEBUG("%s %s, %s(%s)", opn, fregnames[store ? fs : fd], - regnames[index], regnames[base]); } static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, int fd, int fr, int fs, int ft) { - const char *opn = "flt3_arith"; - switch (opc) { case OPC_ALNV_PS: check_ps(ctx); @@ -10638,7 +10259,6 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, tcg_temp_free_i32(fp); tcg_temp_free_i32(fph); } - opn = "alnv.ps"; break; case OPC_MADD_S: check_cop1x(ctx); @@ -10656,7 +10276,6 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, gen_store_fpr32(ctx, fp2, fd); tcg_temp_free_i32(fp2); } - opn = "madd.s"; break; case OPC_MADD_D: check_cop1x(ctx); @@ -10675,7 +10294,6 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, gen_store_fpr64(ctx, fp2, fd); tcg_temp_free_i64(fp2); } - opn = "madd.d"; break; case OPC_MADD_PS: check_ps(ctx); @@ -10693,7 +10311,6 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, gen_store_fpr64(ctx, fp2, fd); tcg_temp_free_i64(fp2); } - opn = "madd.ps"; break; case OPC_MSUB_S: check_cop1x(ctx); @@ -10711,7 +10328,6 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, gen_store_fpr32(ctx, fp2, fd); tcg_temp_free_i32(fp2); } - opn = "msub.s"; break; case OPC_MSUB_D: check_cop1x(ctx); @@ -10730,7 +10346,6 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, gen_store_fpr64(ctx, fp2, fd); tcg_temp_free_i64(fp2); } - opn = "msub.d"; break; case OPC_MSUB_PS: check_ps(ctx); @@ -10748,7 +10363,6 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, gen_store_fpr64(ctx, fp2, fd); tcg_temp_free_i64(fp2); } - opn = "msub.ps"; break; case OPC_NMADD_S: check_cop1x(ctx); @@ -10766,7 +10380,6 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, gen_store_fpr32(ctx, fp2, fd); tcg_temp_free_i32(fp2); } - opn = "nmadd.s"; break; case OPC_NMADD_D: check_cop1x(ctx); @@ -10785,7 +10398,6 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, gen_store_fpr64(ctx, fp2, fd); tcg_temp_free_i64(fp2); } - opn = "nmadd.d"; break; case OPC_NMADD_PS: check_ps(ctx); @@ -10803,7 +10415,6 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, gen_store_fpr64(ctx, fp2, fd); tcg_temp_free_i64(fp2); } - opn = "nmadd.ps"; break; case OPC_NMSUB_S: check_cop1x(ctx); @@ -10821,7 +10432,6 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, gen_store_fpr32(ctx, fp2, fd); tcg_temp_free_i32(fp2); } - opn = "nmsub.s"; break; case OPC_NMSUB_D: check_cop1x(ctx); @@ -10840,7 +10450,6 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, gen_store_fpr64(ctx, fp2, fd); tcg_temp_free_i64(fp2); } - opn = "nmsub.d"; break; case OPC_NMSUB_PS: check_ps(ctx); @@ -10858,19 +10467,15 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, gen_store_fpr64(ctx, fp2, fd); tcg_temp_free_i64(fp2); } - opn = "nmsub.ps"; break; default: - MIPS_INVAL(opn); - generate_exception (ctx, EXCP_RI); + MIPS_INVAL("flt3_arith"); + generate_exception_end(ctx, EXCP_RI); return; } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s, %s, %s, %s", opn, fregnames[fd], fregnames[fr], - fregnames[fs], fregnames[ft]); } -static void gen_rdhwr(DisasContext *ctx, int rt, int rd) +static void gen_rdhwr(DisasContext *ctx, int rt, int rd, int sel) { TCGv t0; @@ -10883,25 +10488,37 @@ static void gen_rdhwr(DisasContext *ctx, int rt, int rd) switch (rd) { case 0: - save_cpu_state(ctx, 1); gen_helper_rdhwr_cpunum(t0, cpu_env); gen_store_gpr(t0, rt); break; case 1: - save_cpu_state(ctx, 1); gen_helper_rdhwr_synci_step(t0, cpu_env); gen_store_gpr(t0, rt); break; case 2: - save_cpu_state(ctx, 1); gen_helper_rdhwr_cc(t0, cpu_env); gen_store_gpr(t0, rt); break; case 3: - save_cpu_state(ctx, 1); gen_helper_rdhwr_ccres(t0, cpu_env); gen_store_gpr(t0, rt); break; + case 4: + check_insn(ctx, ISA_MIPS32R6); + if (sel != 0) { + /* Performance counter registers are not implemented other than + * control register 0. + */ + generate_exception(ctx, EXCP_RI); + } + gen_helper_rdhwr_performance(t0, cpu_env); + gen_store_gpr(t0, rt); + break; + case 5: + check_insn(ctx, ISA_MIPS32R6); + gen_helper_rdhwr_xnp(t0, cpu_env); + gen_store_gpr(t0, rt); + break; case 29: #if defined(CONFIG_USER_ONLY) tcg_gen_ld_tl(t0, cpu_env, @@ -10915,13 +10532,13 @@ static void gen_rdhwr(DisasContext *ctx, int rt, int rd) offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); gen_store_gpr(t0, rt); } else { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } break; #endif default: /* Invalid */ MIPS_INVAL("rdhwr"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } tcg_temp_free(t0); @@ -10949,12 +10566,10 @@ static void gen_branch(DisasContext *ctx, int insn_bytes) /* FIXME: Need to clear can_do_io. */ switch (proc_hflags & MIPS_HFLAG_BMASK_BASE) { case MIPS_HFLAG_FBNSLOT: - MIPS_DEBUG("forbidden slot"); gen_goto_tb(ctx, 0, ctx->pc + insn_bytes); break; case MIPS_HFLAG_B: /* unconditional branch */ - MIPS_DEBUG("unconditional branch"); if (proc_hflags & MIPS_HFLAG_BX) { tcg_gen_xori_i32(hflags, hflags, MIPS_HFLAG_M16); } @@ -10962,12 +10577,10 @@ static void gen_branch(DisasContext *ctx, int insn_bytes) break; case MIPS_HFLAG_BL: /* blikely taken case */ - MIPS_DEBUG("blikely branch taken"); gen_goto_tb(ctx, 0, ctx->btarget); break; case MIPS_HFLAG_BC: /* Conditional branch */ - MIPS_DEBUG("conditional branch"); { TCGLabel *l1 = gen_new_label(); @@ -10979,7 +10592,6 @@ static void gen_branch(DisasContext *ctx, int insn_bytes) break; case MIPS_HFLAG_BR: /* unconditional branch to register */ - MIPS_DEBUG("branch to register"); if (ctx->insn_flags & (ASE_MIPS16 | ASE_MICROMIPS)) { TCGv t0 = tcg_temp_new(); TCGv_i32 t1 = tcg_temp_new_i32(); @@ -10998,7 +10610,7 @@ static void gen_branch(DisasContext *ctx, int insn_bytes) } if (ctx->singlestep_enabled) { save_cpu_state(ctx, 0); - gen_helper_0e0i(raise_exception, EXCP_DEBUG); + gen_helper_raise_exception_debug(cpu_env); } tcg_gen_exit_tb(0); break; @@ -11023,7 +10635,7 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, LOG_DISAS("Branch in delay / forbidden slot at PC 0x" TARGET_FMT_lx "\n", ctx->pc); #endif - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); goto out; } @@ -11085,7 +10697,7 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, break; default: MIPS_INVAL("Compact branch/jump"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); goto out; } @@ -11106,7 +10718,7 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, break; default: MIPS_INVAL("Compact branch/jump"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); goto out; } @@ -11229,7 +10841,7 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, break; default: MIPS_INVAL("Compact conditional branch/jump"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); goto out; } @@ -11238,7 +10850,6 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, gen_set_label(fs); ctx->hflags |= MIPS_HFLAG_FBNSLOT; - MIPS_DEBUG("Compact conditional branch"); } out: @@ -11404,7 +11015,7 @@ static void gen_mips16_save (DisasContext *ctx, args = 4; break; default: - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); return; } @@ -11500,7 +11111,7 @@ static void gen_mips16_save (DisasContext *ctx, astatic = 4; break; default: - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); return; } @@ -11606,7 +11217,7 @@ static void gen_mips16_restore (DisasContext *ctx, astatic = 4; break; default: - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); return; } @@ -11637,7 +11248,7 @@ static void gen_addiupc (DisasContext *ctx, int rx, int imm, TCGv t0; if (extended && (ctx->hflags & MIPS_HFLAG_BMASK)) { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); return; } @@ -11652,6 +11263,15 @@ static void gen_addiupc (DisasContext *ctx, int rx, int imm, tcg_temp_free(t0); } +static void gen_cache_operation(DisasContext *ctx, uint32_t op, int base, + int16_t offset) +{ + TCGv_i32 t0 = tcg_const_i32(op); + TCGv t1 = tcg_temp_new(); + gen_base_offset_addr(ctx, t1, base, offset); + gen_helper_cache(cpu_env, t1, t0); +} + #if defined(TARGET_MIPS64) static void decode_i64_mips16 (DisasContext *ctx, int ry, int funct, int16_t offset, @@ -11686,7 +11306,7 @@ static void decode_i64_mips16 (DisasContext *ctx, check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); if (extended && (ctx->hflags & MIPS_HFLAG_BMASK)) { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } else { offset = extended ? offset : offset << 3; gen_ld(ctx, OPC_LDPC, ry, 0, offset); @@ -11761,7 +11381,7 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx) check_mips_64(ctx); gen_shift_imm(ctx, OPC_DSLL, rx, ry, sa); #else - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); #endif break; case 0x2: @@ -11789,7 +11409,7 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx) check_mips_64(ctx); gen_arith_imm(ctx, OPC_DADDIU, ry, rx, imm); #else - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); #endif } else { gen_arith_imm(ctx, OPC_ADDIU, ry, rx, imm); @@ -11841,7 +11461,7 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx) } break; default: - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -11904,7 +11524,7 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx) break; #endif default: - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } @@ -11985,7 +11605,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) check_mips_64(ctx); gen_shift_imm(ctx, OPC_DSLL, rx, ry, sa); #else - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); #endif break; case 0x2: @@ -12013,7 +11633,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) check_mips_64(ctx); gen_arith_imm(ctx, OPC_DADDIU, ry, rx, imm); #else - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); #endif } else { gen_arith_imm(ctx, OPC_ADDIU, ry, rx, imm); @@ -12097,7 +11717,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) gen_arith(ctx, OPC_ADDU, ry, reg32, 0); break; default: - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } } @@ -12187,7 +11807,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) break; #endif default: - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); goto done; } @@ -12226,7 +11846,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) * when in debug mode... */ check_insn(ctx, ISA_MIPS32); - generate_exception(ctx, EXCP_DBp); + generate_exception_end(ctx, EXCP_DBp); } break; case RR_SLT: @@ -12236,7 +11856,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) gen_slt(ctx, OPC_SLTU, 24, rx, ry); break; case RR_BREAK: - generate_exception(ctx, EXCP_BREAK); + generate_exception_end(ctx, EXCP_BREAK); break; case RR_SLLV: gen_shift(ctx, OPC_SLLV, ry, rx, ry); @@ -12303,7 +11923,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) break; #endif default: - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -12367,7 +11987,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) break; #endif default: - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -12382,7 +12002,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) break; #endif default: - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } @@ -12526,6 +12146,7 @@ enum { ROTR = 0x3, SELEQZ = 0x5, SELNEZ = 0x6, + R6_RDHWR = 0x7, SLLV = 0x0, SRLV = 0x1, @@ -12556,11 +12177,13 @@ enum { MODU = 0x7, /* The following can be distinguished by their lower 6 bits. */ + BREAK32 = 0x07, INS = 0x0c, LSA = 0x0f, ALIGN = 0x1f, EXT = 0x2c, - POOL32AXF = 0x3c + POOL32AXF = 0x3c, + SIGRIE = 0x3f }; /* POOL32AXF encoding of minor opcode field extension */ @@ -13029,12 +12652,11 @@ static void gen_andi16(DisasContext *ctx) static void gen_ldst_multiple (DisasContext *ctx, uint32_t opc, int reglist, int base, int16_t offset) { - const char *opn = "ldst_multiple"; TCGv t0, t1; TCGv_i32 t2; if (ctx->hflags & MIPS_HFLAG_BMASK) { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); return; } @@ -13049,25 +12671,19 @@ static void gen_ldst_multiple (DisasContext *ctx, uint32_t opc, int reglist, switch (opc) { case LWM32: gen_helper_lwm(cpu_env, t0, t1, t2); - opn = "lwm"; break; case SWM32: gen_helper_swm(cpu_env, t0, t1, t2); - opn = "swm"; break; #ifdef TARGET_MIPS64 case LDM: gen_helper_ldm(cpu_env, t0, t1, t2); - opn = "ldm"; break; case SDM: gen_helper_sdm(cpu_env, t0, t1, t2); - opn = "sdm"; break; #endif } - (void)opn; - MIPS_DEBUG("%s, %x, %d(%s)", opn, reglist, offset, regnames[base]); tcg_temp_free(t0); tcg_temp_free(t1); tcg_temp_free_i32(t2); @@ -13164,7 +12780,7 @@ static void gen_pool16c_insn(DisasContext *ctx) gen_HILO(ctx, OPC_MFLO, 0, uMIPS_RS5(ctx->opcode)); break; case BREAK16: - generate_exception(ctx, EXCP_BREAK); + generate_exception_end(ctx, EXCP_BREAK); break; case SDBBP16: if (is_uhi(extract32(ctx->opcode, 0, 4))) { @@ -13174,7 +12790,7 @@ static void gen_pool16c_insn(DisasContext *ctx) * when in debug mode... */ check_insn(ctx, ISA_MIPS32); - generate_exception(ctx, EXCP_DBp); + generate_exception_end(ctx, EXCP_DBp); } break; case JRADDIUSP + 0: @@ -13188,7 +12804,7 @@ static void gen_pool16c_insn(DisasContext *ctx) } break; default: - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } } @@ -13324,11 +12940,10 @@ static void gen_ldxs (DisasContext *ctx, int base, int index, int rd) static void gen_ldst_pair (DisasContext *ctx, uint32_t opc, int rd, int base, int16_t offset) { - const char *opn = "ldst_pair"; TCGv t0, t1; if (ctx->hflags & MIPS_HFLAG_BMASK || rd == 31) { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); return; } @@ -13340,7 +12955,7 @@ static void gen_ldst_pair (DisasContext *ctx, uint32_t opc, int rd, switch (opc) { case LWP: if (rd == base) { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); return; } tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL); @@ -13349,7 +12964,6 @@ static void gen_ldst_pair (DisasContext *ctx, uint32_t opc, int rd, gen_op_addr_add(ctx, t0, t0, t1); tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL); gen_store_gpr(t1, rd+1); - opn = "lwp"; break; case SWP: gen_load_gpr(t1, rd); @@ -13358,12 +12972,11 @@ static void gen_ldst_pair (DisasContext *ctx, uint32_t opc, int rd, gen_op_addr_add(ctx, t0, t0, t1); gen_load_gpr(t1, rd+1); tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); - opn = "swp"; break; #ifdef TARGET_MIPS64 case LDP: if (rd == base) { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); return; } tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TEQ); @@ -13372,7 +12985,6 @@ static void gen_ldst_pair (DisasContext *ctx, uint32_t opc, int rd, gen_op_addr_add(ctx, t0, t0, t1); tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TEQ); gen_store_gpr(t1, rd+1); - opn = "ldp"; break; case SDP: gen_load_gpr(t1, rd); @@ -13381,12 +12993,9 @@ static void gen_ldst_pair (DisasContext *ctx, uint32_t opc, int rd, gen_op_addr_add(ctx, t0, t0, t1); gen_load_gpr(t1, rd+1); tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEQ); - opn = "sdp"; break; #endif } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s, %s, %d(%s)", opn, regnames[rd], offset, regnames[base]); tcg_temp_free(t0); tcg_temp_free(t1); } @@ -13492,7 +13101,8 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) gen_cl(ctx, mips32_op, rt, rs); break; case RDHWR: - gen_rdhwr(ctx, rt, rs); + check_insn_opc_removed(ctx, ISA_MIPS32R6); + gen_rdhwr(ctx, rt, rs, 0); break; case WSBH: gen_bshfl(ctx, OPC_WSBH, rs, rt); @@ -13668,8 +13278,7 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) /* NOP */ break; case SYSCALL: - generate_exception(ctx, EXCP_SYSCALL); - ctx->bstate = BS_STOP; + generate_exception_end(ctx, EXCP_SYSCALL); break; case SDBBP: if (is_uhi(extract32(ctx->opcode, 16, 10))) { @@ -13677,9 +13286,9 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) } else { check_insn(ctx, ISA_MIPS32); if (ctx->hflags & MIPS_HFLAG_SBRI) { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } else { - generate_exception(ctx, EXCP_DBp); + generate_exception_end(ctx, EXCP_DBp); } } break; @@ -13727,7 +13336,7 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) default: pool32axf_invalid: MIPS_INVAL("pool32axf"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } } @@ -13995,7 +13604,7 @@ static void gen_pool32fxf(DisasContext *ctx, int rt, int rs) break; default: MIPS_INVAL("pool32fxf"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } } @@ -14048,6 +13657,10 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) check_insn(ctx, ISA_MIPS32R6); gen_cond_move(ctx, OPC_SELNEZ, rd, rs, rt); break; + case R6_RDHWR: + check_insn(ctx, ISA_MIPS32R6); + gen_rdhwr(ctx, rt, rs, extract32(ctx->opcode, 11, 3)); + break; default: goto pool32a_invalid; } @@ -14191,13 +13804,17 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) case POOL32AXF: gen_pool32axf(env, ctx, rt, rs); break; - case 0x07: - generate_exception(ctx, EXCP_BREAK); + case BREAK32: + generate_exception_end(ctx, EXCP_BREAK); + break; + case SIGRIE: + check_insn(ctx, ISA_MIPS32R6); + generate_exception_end(ctx, EXCP_RI); break; default: pool32a_invalid: MIPS_INVAL("pool32a"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -14206,7 +13823,9 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) switch (minor) { case CACHE: check_cp0_enabled(ctx); - /* Treat as no-op. */ + if (ctx->hflags & MIPS_HFLAG_ITC_CACHE) { + gen_cache_operation(ctx, rt, rs, imm); + } break; case LWC2: case SWC2: @@ -14237,7 +13856,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) break; default: MIPS_INVAL("pool32b"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -14717,7 +14336,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) default: pool32f_invalid: MIPS_INVAL("pool32f"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } } else { @@ -14873,7 +14492,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) /* Fall through */ default: MIPS_INVAL("pool32i"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -14962,7 +14581,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) break; default: MIPS_INVAL("pool32c"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -15240,7 +14859,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) gen_st(ctx, mips32_op, rt, rs, imm); break; default: - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } } @@ -15252,8 +14871,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) /* make sure instructions are on a halfword boundary */ if (ctx->pc & 0x1) { env->CP0_BadVAddr = ctx->pc; - generate_exception(ctx, EXCP_AdEL); - ctx->bstate = BS_STOP; + generate_exception_end(ctx, EXCP_AdEL); return 2; } @@ -15272,9 +14890,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) case 7: /* LB32, LH32, LWC132, LDC132, LW32 */ if (ctx->hflags & MIPS_HFLAG_BDS16) { - generate_exception(ctx, EXCP_RI); - /* Just stop translation; the user is confused. */ - ctx->bstate = BS_STOP; + generate_exception_end(ctx, EXCP_RI); return 2; } break; @@ -15285,9 +14901,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) case 3: /* MOVE16, ANDI16, POOL16D, POOL16E, BEQZ16, BNEZ16, B16, LI16 */ if (ctx->hflags & MIPS_HFLAG_BDS32) { - generate_exception(ctx, EXCP_RI); - /* Just stop translation; the user is confused. */ - ctx->bstate = BS_STOP; + generate_exception_end(ctx, EXCP_RI); return 2; } break; @@ -15359,7 +14973,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) case POOL16F: check_insn_opc_removed(ctx, ISA_MIPS32R6); if (ctx->opcode & 1) { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } else { /* MOVEP */ int enc_dest = uMIPS_RD(ctx->opcode); @@ -15497,7 +15111,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) case RES_29: case RES_31: case RES_39: - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; default: decode_micromips32_opc(env, ctx); @@ -15519,7 +15133,6 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) static void gen_mipsdsp_ld(DisasContext *ctx, uint32_t opc, int rd, int base, int offset) { - const char *opn = "ldx"; TCGv t0; check_dsp(ctx); @@ -15537,42 +15150,33 @@ static void gen_mipsdsp_ld(DisasContext *ctx, uint32_t opc, case OPC_LBUX: tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_UB); gen_store_gpr(t0, rd); - opn = "lbux"; break; case OPC_LHX: tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESW); gen_store_gpr(t0, rd); - opn = "lhx"; break; case OPC_LWX: tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL); gen_store_gpr(t0, rd); - opn = "lwx"; break; #if defined(TARGET_MIPS64) case OPC_LDX: tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ); gen_store_gpr(t0, rd); - opn = "ldx"; break; #endif } - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s %s, %s(%s)", opn, - regnames[rd], regnames[offset], regnames[base]); tcg_temp_free(t0); } static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, int ret, int v1, int v2) { - const char *opn = "mipsdsp arith"; TCGv v1_t; TCGv v2_t; if (ret == 0) { /* Treat as NOP. */ - MIPS_DEBUG("NOP"); return; } @@ -16012,23 +15616,18 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, tcg_temp_free(v1_t); tcg_temp_free(v2_t); - - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s", opn); } static void gen_mipsdsp_shift(DisasContext *ctx, uint32_t opc, int ret, int v1, int v2) { uint32_t op2; - const char *opn = "mipsdsp shift"; TCGv t0; TCGv v1_t; TCGv v2_t; if (ret == 0) { /* Treat as NOP. */ - MIPS_DEBUG("NOP"); return; } @@ -16135,7 +15734,7 @@ static void gen_mipsdsp_shift(DisasContext *ctx, uint32_t opc, break; default: /* Invalid */ MIPS_INVAL("MASK SHLL.QB"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -16250,7 +15849,7 @@ static void gen_mipsdsp_shift(DisasContext *ctx, uint32_t opc, break; default: /* Invalid */ MIPS_INVAL("MASK SHLL.OB"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -16260,21 +15859,17 @@ static void gen_mipsdsp_shift(DisasContext *ctx, uint32_t opc, tcg_temp_free(t0); tcg_temp_free(v1_t); tcg_temp_free(v2_t); - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s", opn); } static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, int ret, int v1, int v2, int check_ret) { - const char *opn = "mipsdsp multiply"; TCGv_i32 t0; TCGv v1_t; TCGv v2_t; if ((ret == 0) && (check_ret == 1)) { /* Treat as NOP. */ - MIPS_DEBUG("NOP"); return; } @@ -16572,23 +16167,17 @@ static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, tcg_temp_free_i32(t0); tcg_temp_free(v1_t); tcg_temp_free(v2_t); - - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s", opn); - } static void gen_mipsdsp_bitinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, int ret, int val) { - const char *opn = "mipsdsp Bit/ Manipulation"; int16_t imm; TCGv t0; TCGv val_t; if (ret == 0) { /* Treat as NOP. */ - MIPS_DEBUG("NOP"); return; } @@ -16716,23 +16305,18 @@ static void gen_mipsdsp_bitinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, } tcg_temp_free(t0); tcg_temp_free(val_t); - - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s", opn); } static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, uint32_t op1, uint32_t op2, int ret, int v1, int v2, int check_ret) { - const char *opn = "mipsdsp add compare pick"; TCGv t1; TCGv v1_t; TCGv v2_t; if ((ret == 0) && (check_ret == 1)) { /* Treat as NOP. */ - MIPS_DEBUG("NOP"); return; } @@ -16907,22 +16491,17 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, tcg_temp_free(t1); tcg_temp_free(v1_t); tcg_temp_free(v2_t); - - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s", opn); } static void gen_mipsdsp_append(CPUMIPSState *env, DisasContext *ctx, uint32_t op1, int rt, int rs, int sa) { - const char *opn = "mipsdsp append/dappend"; TCGv t0; check_dspr2(ctx); if (rt == 0) { /* Treat as NOP. */ - MIPS_DEBUG("NOP"); return; } @@ -16959,7 +16538,7 @@ static void gen_mipsdsp_append(CPUMIPSState *env, DisasContext *ctx, break; default: /* Invalid */ MIPS_INVAL("MASK APPEND"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -16993,22 +16572,19 @@ static void gen_mipsdsp_append(CPUMIPSState *env, DisasContext *ctx, break; default: /* Invalid */ MIPS_INVAL("MASK DAPPEND"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; #endif } tcg_temp_free(t0); - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s", opn); } static void gen_mipsdsp_accinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, int ret, int v1, int v2, int check_ret) { - const char *opn = "mipsdsp accumulator"; TCGv t0; TCGv t1; TCGv v1_t; @@ -17017,7 +16593,6 @@ static void gen_mipsdsp_accinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, if ((ret == 0) && (check_ret == 1)) { /* Treat as NOP. */ - MIPS_DEBUG("NOP"); return; } @@ -17229,9 +16804,6 @@ static void gen_mipsdsp_accinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, tcg_temp_free(t1); tcg_temp_free(v1_t); tcg_temp_free(v2_t); - - (void)opn; /* avoid a compiler warning */ - MIPS_DEBUG("%s", opn); } /* End MIPSDSP functions. */ @@ -17266,7 +16838,7 @@ static void decode_opc_special_r6(CPUMIPSState *env, DisasContext *ctx) break; default: MIPS_INVAL("special_r6 muldiv"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -17281,7 +16853,7 @@ static void decode_opc_special_r6(CPUMIPSState *env, DisasContext *ctx) We need additionally to check other fields */ gen_cl(ctx, op1, rd, rs); } else { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } break; case R6_OPC_SDBBP: @@ -17289,9 +16861,9 @@ static void decode_opc_special_r6(CPUMIPSState *env, DisasContext *ctx) gen_helper_do_semihosting(cpu_env); } else { if (ctx->hflags & MIPS_HFLAG_SBRI) { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } else { - generate_exception(ctx, EXCP_DBp); + generate_exception_end(ctx, EXCP_DBp); } } break; @@ -17308,7 +16880,7 @@ static void decode_opc_special_r6(CPUMIPSState *env, DisasContext *ctx) check_mips_64(ctx); gen_cl(ctx, op1, rd, rs); } else { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } break; case OPC_DMULT ... OPC_DDIVU: @@ -17327,14 +16899,14 @@ static void decode_opc_special_r6(CPUMIPSState *env, DisasContext *ctx) break; default: MIPS_INVAL("special_r6 muldiv"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; #endif default: /* Invalid */ MIPS_INVAL("special_r6"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } } @@ -17402,16 +16974,16 @@ static void decode_opc_special_legacy(CPUMIPSState *env, DisasContext *ctx) case OPC_SPIM: #ifdef MIPS_STRICT_STANDARD MIPS_INVAL("SPIM"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); #else /* Implemented as RI exception for now. */ MIPS_INVAL("spim (unofficial)"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); #endif break; default: /* Invalid */ MIPS_INVAL("special_legacy"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } } @@ -17433,8 +17005,7 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) rs == 0 && rt == 0) { /* PAUSE */ if ((ctx->insn_flags & ISA_MIPS32R6) && (ctx->hflags & MIPS_HFLAG_BMASK)) { - MIPS_DEBUG("CTI in delay / forbidden slot"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } } @@ -17454,7 +17025,7 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) gen_shift_imm(ctx, op1, rd, rt, sa); break; default: - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -17477,7 +17048,7 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) gen_shift(ctx, op1, rd, rs, rt); break; default: - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -17507,18 +17078,17 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) /* Pmon entry point, also R4010 selsl */ #ifdef MIPS_STRICT_STANDARD MIPS_INVAL("PMON / selsl"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); #else gen_helper_0e0i(pmon, sa); #endif } break; case OPC_SYSCALL: - generate_exception(ctx, EXCP_SYSCALL); - ctx->bstate = BS_STOP; + generate_exception_end(ctx, EXCP_SYSCALL); break; case OPC_BREAK: - generate_exception(ctx, EXCP_BREAK); + generate_exception_end(ctx, EXCP_BREAK); break; case OPC_SYNC: check_insn(ctx, ISA_MIPS2); @@ -17549,7 +17119,7 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) gen_shift_imm(ctx, op1, rd, rt, sa); break; default: - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -17567,7 +17137,7 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) gen_shift_imm(ctx, op1, rd, rt, sa); break; default: - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -17596,7 +17166,7 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) gen_shift(ctx, op1, rd, rs, rt); break; default: - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -17659,7 +17229,7 @@ static void decode_opc_special2_legacy(CPUMIPSState *env, DisasContext *ctx) * when in debug mode... */ check_insn(ctx, ISA_MIPS32); - generate_exception(ctx, EXCP_DBp); + generate_exception_end(ctx, EXCP_DBp); } break; #if defined(TARGET_MIPS64) @@ -17681,7 +17251,7 @@ static void decode_opc_special2_legacy(CPUMIPSState *env, DisasContext *ctx) #endif default: /* Invalid */ MIPS_INVAL("special2_legacy"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } } @@ -17703,12 +17273,15 @@ static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx) case R6_OPC_PREF: if (rt >= 24) { /* hint codes 24-31 are reserved and signal RI */ - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } /* Treat as NOP. */ break; case R6_OPC_CACHE: - /* Treat as NOP. */ + check_cp0_enabled(ctx); + if (ctx->hflags & MIPS_HFLAG_ITC_CACHE) { + gen_cache_operation(ctx, rt, rs, imm); + } break; case R6_OPC_SC: gen_st_cond(ctx, op1, rt, rs, imm); @@ -17762,7 +17335,7 @@ static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx) #endif default: /* Invalid */ MIPS_INVAL("special3_r6"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } } @@ -17808,13 +17381,13 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) break; default: MIPS_INVAL("MASK ADDUH.QB"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } } else if (ctx->insn_flags & INSN_LOONGSON2E) { gen_loongson_integer(ctx, op1, rd, rs, rt); } else { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } break; case OPC_LX_DSP: @@ -17830,7 +17403,7 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) break; default: /* Invalid */ MIPS_INVAL("MASK LX"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -17861,7 +17434,7 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) break; default: MIPS_INVAL("MASK ABSQ_S.PH"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -17898,7 +17471,7 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) break; default: /* Invalid */ MIPS_INVAL("MASK ADDU.QB"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } @@ -17938,7 +17511,7 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) break; default: /* Invalid */ MIPS_INVAL("MASK CMPU.EQ.QB"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -17974,7 +17547,7 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) break; default: /* Invalid */ MIPS_INVAL("MASK DPAW.PH"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -17987,7 +17560,6 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) TCGv t0, t1; if (rt == 0) { - MIPS_DEBUG("NOP"); break; } @@ -18005,7 +17577,7 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) } default: /* Invalid */ MIPS_INVAL("MASK INSV"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -18040,7 +17612,7 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) break; default: /* Invalid */ MIPS_INVAL("MASK EXTR.W"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -18083,7 +17655,7 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) break; default: /* Invalid */ MIPS_INVAL("MASK ABSQ_S.QH"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -18122,7 +17694,7 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) break; default: /* Invalid */ MIPS_INVAL("MASK ADDU.OB"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -18167,7 +17739,7 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) break; default: /* Invalid */ MIPS_INVAL("MASK CMPU_EQ.OB"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -18204,7 +17776,7 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) break; default: /* Invalid */ MIPS_INVAL("MASK EXTR.W"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -18243,7 +17815,7 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) break; default: /* Invalid */ MIPS_INVAL("MASK DPAQ.W.QH"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -18255,7 +17827,6 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) TCGv t0, t1; if (rt == 0) { - MIPS_DEBUG("NOP"); break; } check_dsp(ctx); @@ -18274,7 +17845,7 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) } default: /* Invalid */ MIPS_INVAL("MASK DINSV"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -18284,7 +17855,7 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) #endif default: /* Invalid */ MIPS_INVAL("special3_legacy"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } } @@ -18345,7 +17916,7 @@ static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx) break; #endif case OPC_RDHWR: - gen_rdhwr(ctx, rt, rd); + gen_rdhwr(ctx, rt, rd, extract32(ctx->opcode, 6, 3)); break; case OPC_FORK: check_insn(ctx, ASE_MT); @@ -18365,7 +17936,6 @@ static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx) { TCGv t0 = tcg_temp_new(); - save_cpu_state(ctx, 1); gen_load_gpr(t0, rs); gen_helper_yield(t0, cpu_env, t0); gen_store_gpr(t0, rd); @@ -18386,16 +17956,16 @@ static inline int check_msa_access(DisasContext *ctx) { if (unlikely((ctx->hflags & MIPS_HFLAG_FPU) && !(ctx->hflags & MIPS_HFLAG_F64))) { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); return 0; } if (unlikely(!(ctx->hflags & MIPS_HFLAG_MSA))) { if (ctx->insn_flags & ASE_MSA) { - generate_exception(ctx, EXCP_MSADIS); + generate_exception_end(ctx, EXCP_MSADIS); return 0; } else { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); return 0; } } @@ -18452,8 +18022,7 @@ static void gen_msa_branch(CPUMIPSState *env, DisasContext *ctx, uint32_t op1) check_msa_access(ctx); if (ctx->insn_flags & ISA_MIPS32R6 && ctx->hflags & MIPS_HFLAG_BMASK) { - MIPS_DEBUG("CTI in delay / forbidden slot"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); return; } switch (op1) { @@ -18528,7 +18097,7 @@ static void gen_msa_i8(CPUMIPSState *env, DisasContext *ctx) { uint8_t df = (ctx->opcode >> 24) & 0x3; if (df == DF_DOUBLE) { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } else { TCGv_i32 tdf = tcg_const_i32(df); gen_helper_msa_shf_df(cpu_env, tdf, twd, tws, ti8); @@ -18538,7 +18107,7 @@ static void gen_msa_i8(CPUMIPSState *env, DisasContext *ctx) break; default: MIPS_INVAL("MSA instruction"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } @@ -18610,7 +18179,7 @@ static void gen_msa_i5(CPUMIPSState *env, DisasContext *ctx) break; default: MIPS_INVAL("MSA instruction"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } @@ -18646,7 +18215,7 @@ static void gen_msa_bit(CPUMIPSState *env, DisasContext *ctx) m = dfm & 0x7; df = DF_BYTE; } else { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); return; } @@ -18694,7 +18263,7 @@ static void gen_msa_bit(CPUMIPSState *env, DisasContext *ctx) break; default: MIPS_INVAL("MSA instruction"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } @@ -18889,7 +18458,8 @@ static void gen_msa_3r(CPUMIPSState *env, DisasContext *ctx) case OPC_HSUB_S_df: case OPC_HSUB_U_df: if (df == DF_BYTE) { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); + break; } switch (MASK_MSA_3R(ctx->opcode)) { case OPC_DOTP_S_df: @@ -18926,7 +18496,7 @@ static void gen_msa_3r(CPUMIPSState *env, DisasContext *ctx) break; default: MIPS_INVAL("MSA instruction"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } tcg_temp_free_i32(twd); @@ -18958,7 +18528,7 @@ static void gen_msa_elm_3e(CPUMIPSState *env, DisasContext *ctx) break; default: MIPS_INVAL("MSA instruction"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } @@ -18995,7 +18565,7 @@ static void gen_msa_elm_df(CPUMIPSState *env, DisasContext *ctx, uint32_t df, #if !defined(TARGET_MIPS64) /* Double format valid only for MIPS64 */ if (df == DF_DOUBLE) { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } #endif @@ -19013,7 +18583,7 @@ static void gen_msa_elm_df(CPUMIPSState *env, DisasContext *ctx, uint32_t df, break; default: MIPS_INVAL("MSA instruction"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } tcg_temp_free_i32(twd); tcg_temp_free_i32(tws); @@ -19043,7 +18613,7 @@ static void gen_msa_elm(CPUMIPSState *env, DisasContext *ctx) gen_msa_elm_3e(env, ctx); return; } else { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); return; } @@ -19198,7 +18768,7 @@ static void gen_msa_3rf(CPUMIPSState *env, DisasContext *ctx) break; default: MIPS_INVAL("MSA instruction"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } @@ -19226,7 +18796,7 @@ static void gen_msa_2r(CPUMIPSState *env, DisasContext *ctx) #if !defined(TARGET_MIPS64) /* Double format valid only for MIPS64 */ if (df == DF_DOUBLE) { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } #endif @@ -19243,7 +18813,7 @@ static void gen_msa_2r(CPUMIPSState *env, DisasContext *ctx) break; default: MIPS_INVAL("MSA instruction"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } @@ -19358,7 +18928,7 @@ static void gen_msa_vec_v(CPUMIPSState *env, DisasContext *ctx) break; default: MIPS_INVAL("MSA instruction"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } @@ -19387,7 +18957,7 @@ static void gen_msa_vec(CPUMIPSState *env, DisasContext *ctx) break; default: MIPS_INVAL("MSA instruction"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } } @@ -19485,7 +19055,7 @@ static void gen_msa(CPUMIPSState *env, DisasContext *ctx) break; default: MIPS_INVAL("MSA instruction"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } @@ -19502,7 +19072,6 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) if (ctx->pc & 0x3) { env->CP0_BadVAddr = ctx->pc; generate_exception_err(ctx, EXCP_AdEL, EXCP_INST_NOTAVAIL); - ctx->bstate = BS_STOP; return; } @@ -19510,17 +19079,12 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) if ((ctx->hflags & MIPS_HFLAG_BMASK_BASE) == MIPS_HFLAG_BL) { TCGLabel *l1 = gen_new_label(); - MIPS_DEBUG("blikely condition (" TARGET_FMT_lx ")", ctx->pc + 4); tcg_gen_brcondi_tl(TCG_COND_NE, bcond, 0, l1); tcg_gen_movi_i32(hflags, ctx->hflags & ~MIPS_HFLAG_BMASK); gen_goto_tb(ctx, 1, ctx->pc + 4); gen_set_label(l1); } - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { - tcg_gen_debug_insn_start(ctx->pc); - } - op = MASK_OP_MAJOR(ctx->opcode); rs = (ctx->opcode >> 21) & 0x1f; rt = (ctx->opcode >> 16) & 0x1f; @@ -19558,7 +19122,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) /* OPC_NAL, OPC_BAL */ gen_compute_branch(ctx, op1, 4, 0, -1, imm << 2, 4); } else { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } } else { gen_compute_branch(ctx, op1, 4, rs, -1, imm << 2, 4); @@ -19570,6 +19134,10 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_trap(ctx, op1, rs, -1, imm); break; + case OPC_SIGRIE: + check_insn(ctx, ISA_MIPS32R6); + generate_exception_end(ctx, EXCP_RI); + break; case OPC_SYNCI: check_insn(ctx, ISA_MIPS32R2); /* Break the TB to be able to sync copied instructions @@ -19590,7 +19158,6 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) if (rs != 0) { tcg_gen_addi_tl(cpu_gpr[rs], cpu_gpr[rs], (int64_t)imm << 32); } - MIPS_DEBUG("dahi %s, %04x", regnames[rs], imm); break; case OPC_DATI: check_insn(ctx, ISA_MIPS32R6); @@ -19598,12 +19165,11 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) if (rs != 0) { tcg_gen_addi_tl(cpu_gpr[rs], cpu_gpr[rs], (int64_t)imm << 48); } - MIPS_DEBUG("dati %s, %04x", regnames[rs], imm); break; #endif default: /* Invalid */ MIPS_INVAL("regimm"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -19658,6 +19224,20 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) gen_helper_evpe(t0, cpu_env); gen_store_gpr(t0, rt); break; + case OPC_DVP: + check_insn(ctx, ISA_MIPS32R6); + if (ctx->vp) { + gen_helper_dvp(t0, cpu_env); + gen_store_gpr(t0, rt); + } + break; + case OPC_EVP: + check_insn(ctx, ISA_MIPS32R6); + if (ctx->vp) { + gen_helper_evp(t0, cpu_env); + gen_store_gpr(t0, rt); + } + break; case OPC_DI: check_insn(ctx, ISA_MIPS32R2); save_cpu_state(ctx, 1); @@ -19678,7 +19258,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) break; default: /* Invalid */ MIPS_INVAL("mfmc0"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } tcg_temp_free(t0); @@ -19695,7 +19275,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) break; default: MIPS_INVAL("cp0"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -19730,7 +19310,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) case OPC_BLEZC: /* OPC_BGEZC, OPC_BGEC, OPC_BLEZL */ if (ctx->insn_flags & ISA_MIPS32R6) { if (rt == 0) { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } /* OPC_BLEZC, OPC_BGEZC, OPC_BGEC */ @@ -19743,7 +19323,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) case OPC_BGTZC: /* OPC_BLTZC, OPC_BLTC, OPC_BGTZL */ if (ctx->insn_flags & ISA_MIPS32R6) { if (rt == 0) { - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } /* OPC_BGTZC, OPC_BLTZC, OPC_BLTC */ @@ -19810,6 +19390,9 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) check_insn_opc_removed(ctx, ISA_MIPS32R6); check_cp0_enabled(ctx); check_insn(ctx, ISA_MIPS3 | ISA_MIPS32); + if (ctx->hflags & MIPS_HFLAG_ITC_CACHE) { + gen_cache_operation(ctx, rt, rs, imm); + } /* Treat as NOP. */ break; case OPC_PREF: @@ -19973,7 +19556,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) break; default: MIPS_INVAL("cp1"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } break; @@ -20055,7 +19638,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) break; default: MIPS_INVAL("cp3"); - generate_exception (ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } } else { @@ -20111,7 +19694,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) gen_compute_compact_branch(ctx, op, rs, rt, imm << 2); } else { MIPS_INVAL("major opcode"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); } break; #endif @@ -20120,15 +19703,16 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) #if defined(TARGET_MIPS64) /* OPC_DAUI */ check_mips_64(ctx); - if (rt != 0) { + if (rs == 0) { + generate_exception(ctx, EXCP_RI); + } else if (rt != 0) { TCGv t0 = tcg_temp_new(); gen_load_gpr(t0, rs); tcg_gen_addi_tl(cpu_gpr[rt], t0, imm << 16); tcg_temp_free(t0); } - MIPS_DEBUG("daui %s, %s, %04x", regnames[rt], regnames[rs], imm); #else - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); MIPS_INVAL("major opcode"); #endif } else { @@ -20148,30 +19732,23 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) break; default: /* Invalid */ MIPS_INVAL("major opcode"); - generate_exception(ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } } -static inline void -gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, - bool search_pc) +void gen_intermediate_code(CPUMIPSState *env, struct TranslationBlock *tb) { + MIPSCPU *cpu = mips_env_get_cpu(env); CPUState *cs = CPU(cpu); - CPUMIPSState *env = &cpu->env; DisasContext ctx; target_ulong pc_start; target_ulong next_page_start; - CPUBreakpoint *bp; - int j, lj = -1; int num_insns; int max_insns; int insn_bytes; int is_slot; - if (search_pc) - qemu_log("search pc %d\n", search_pc); - pc_start = tb->pc; next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; ctx.pc = pc_start; @@ -20181,6 +19758,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, ctx.CP0_Config1 = env->CP0_Config1; ctx.tb = tb; ctx.bstate = BS_NONE; + ctx.btarget = 0; ctx.kscrexist = (env->CP0_Config4 >> CP0C4_KScrExist) & 0xff; ctx.rxi = (env->CP0_Config3 >> CP0C3_RXI) & 1; ctx.ie = (env->CP0_Config4 >> CP0C4_IE) & 3; @@ -20189,11 +19767,14 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, ctx.PAMask = env->PAMask; ctx.mvh = (env->CP0_Config5 >> CP0C5_MVH) & 1; ctx.CP0_LLAddr_shift = env->CP0_LLAddr_shift; + ctx.cmgcr = (env->CP0_Config3 >> CP0C3_CMGCR) & 1; /* Restore delay slot state from the tb context. */ ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */ ctx.ulri = (env->CP0_Config3 >> CP0C3_ULRI) & 1; ctx.ps = ((env->active_fpu.fcr0 >> FCR0_PS) & 1) || (env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)); + ctx.vp = (env->CP0_Config5 >> CP0C5_VP) & 1; + ctx.mrp = (env->CP0_Config5 >> CP0C5_MRP) & 1; restore_cpu_state(env, &ctx); #ifdef CONFIG_USER_ONLY ctx.mem_idx = MIPS_HFLAG_UM; @@ -20204,40 +19785,34 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, MO_UNALN : MO_ALIGN; num_insns = 0; max_insns = tb->cflags & CF_COUNT_MASK; - if (max_insns == 0) + if (max_insns == 0) { max_insns = CF_COUNT_MASK; + } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } + LOG_DISAS("\ntb %p idx %d hflags %04x\n", tb, ctx.mem_idx, ctx.hflags); gen_tb_start(tb); while (ctx.bstate == BS_NONE) { - if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { - QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - if (bp->pc == ctx.pc) { - save_cpu_state(&ctx, 1); - ctx.bstate = BS_BRANCH; - gen_helper_0e0i(raise_exception, EXCP_DEBUG); - /* Include the breakpoint location or the tb won't - * be flushed when it must be. */ - ctx.pc += 4; - goto done_generating; - } - } - } + tcg_gen_insn_start(ctx.pc, ctx.hflags & MIPS_HFLAG_BMASK, ctx.btarget); + num_insns++; - if (search_pc) { - j = tcg_op_buf_count(); - if (lj < j) { - lj++; - while (lj < j) - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - tcg_ctx.gen_opc_pc[lj] = ctx.pc; - gen_opc_hflags[lj] = ctx.hflags & MIPS_HFLAG_BMASK; - gen_opc_btarget[lj] = ctx.btarget; - tcg_ctx.gen_opc_instr_start[lj] = 1; - tcg_ctx.gen_opc_icount[lj] = num_insns; + if (unlikely(cpu_breakpoint_test(cs, ctx.pc, BP_ANY))) { + save_cpu_state(&ctx, 1); + ctx.bstate = BS_BRANCH; + gen_helper_raise_exception_debug(cpu_env); + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + ctx.pc += 4; + goto done_generating; } - if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) + + if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { gen_io_start(); + } is_slot = ctx.hflags & MIPS_HFLAG_BMASK; if (!(ctx.hflags & MIPS_HFLAG_M16)) { @@ -20251,8 +19826,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, ctx.opcode = cpu_lduw_code(env, ctx.pc); insn_bytes = decode_mips16_opc(env, &ctx); } else { - generate_exception(&ctx, EXCP_RI); - ctx.bstate = BS_STOP; + generate_exception_end(&ctx, EXCP_RI); break; } @@ -20275,8 +19849,6 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, } ctx.pc += insn_bytes; - num_insns++; - /* Execute a branch and its delay slot as a single instruction. This is what GDB expects and is consistent with what the hardware does (e.g. if a delay slot instruction faults, the @@ -20304,7 +19876,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, } if (cs->singlestep_enabled && ctx.bstate != BS_BRANCH) { save_cpu_state(&ctx, ctx.bstate != BS_EXCP); - gen_helper_0e0i(raise_exception, EXCP_DEBUG); + gen_helper_raise_exception_debug(cpu_env); } else { switch (ctx.bstate) { case BS_STOP: @@ -20325,15 +19897,9 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, done_generating: gen_tb_end(tb, num_insns); - if (search_pc) { - j = tcg_op_buf_count(); - lj++; - while (lj <= j) - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } else { - tb->size = ctx.pc - pc_start; - tb->icount = num_insns; - } + tb->size = ctx.pc - pc_start; + tb->icount = num_insns; + #ifdef DEBUG_DISAS LOG_DISAS("\n"); if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { @@ -20344,16 +19910,6 @@ done_generating: #endif } -void gen_intermediate_code (CPUMIPSState *env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(mips_env_get_cpu(env), tb, false); -} - -void gen_intermediate_code_pc (CPUMIPSState *env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(mips_env_get_cpu(env), tb, true); -} - static void fpu_dump_state(CPUMIPSState *env, FILE *f, fprintf_function fpu_fprintf, int flags) { @@ -20394,40 +19950,6 @@ static void fpu_dump_state(CPUMIPSState *env, FILE *f, fprintf_function fpu_fpri #undef printfpr } -#if defined(TARGET_MIPS64) && defined(MIPS_DEBUG_SIGN_EXTENSIONS) -/* Debug help: The architecture requires 32bit code to maintain proper - sign-extended values on 64bit machines. */ - -#define SIGN_EXT_P(val) ((((val) & ~0x7fffffff) == 0) || (((val) & ~0x7fffffff) == ~0x7fffffff)) - -static void -cpu_mips_check_sign_extensions (CPUMIPSState *env, FILE *f, - fprintf_function cpu_fprintf, - int flags) -{ - int i; - - if (!SIGN_EXT_P(env->active_tc.PC)) - cpu_fprintf(f, "BROKEN: pc=0x" TARGET_FMT_lx "\n", env->active_tc.PC); - if (!SIGN_EXT_P(env->active_tc.HI[0])) - cpu_fprintf(f, "BROKEN: HI=0x" TARGET_FMT_lx "\n", env->active_tc.HI[0]); - if (!SIGN_EXT_P(env->active_tc.LO[0])) - cpu_fprintf(f, "BROKEN: LO=0x" TARGET_FMT_lx "\n", env->active_tc.LO[0]); - if (!SIGN_EXT_P(env->btarget)) - cpu_fprintf(f, "BROKEN: btarget=0x" TARGET_FMT_lx "\n", env->btarget); - - for (i = 0; i < 32; i++) { - if (!SIGN_EXT_P(env->active_tc.gpr[i])) - cpu_fprintf(f, "BROKEN: %s=0x" TARGET_FMT_lx "\n", regnames[i], env->active_tc.gpr[i]); - } - - if (!SIGN_EXT_P(env->CP0_EPC)) - cpu_fprintf(f, "BROKEN: EPC=0x" TARGET_FMT_lx "\n", env->CP0_EPC); - if (!SIGN_EXT_P(env->lladdr)) - cpu_fprintf(f, "BROKEN: LLAddr=0x" TARGET_FMT_lx "\n", env->lladdr); -} -#endif - void mips_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int flags) { @@ -20459,9 +19981,6 @@ void mips_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, env->CP0_Config4, env->CP0_Config5); if (env->hflags & MIPS_HFLAG_FPU) fpu_dump_state(env, f, cpu_fprintf, flags); -#if defined(TARGET_MIPS64) && defined(MIPS_DEBUG_SIGN_EXTENSIONS) - cpu_mips_check_sign_extensions(env, f, cpu_fprintf, flags); -#endif } void mips_tcg_init(void) @@ -20474,48 +19993,49 @@ void mips_tcg_init(void) return; cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + TCGV_UNUSED(cpu_gpr[0]); for (i = 1; i < 32; i++) - cpu_gpr[i] = tcg_global_mem_new(TCG_AREG0, + cpu_gpr[i] = tcg_global_mem_new(cpu_env, offsetof(CPUMIPSState, active_tc.gpr[i]), regnames[i]); for (i = 0; i < 32; i++) { int off = offsetof(CPUMIPSState, active_fpu.fpr[i].wr.d[0]); msa_wr_d[i * 2] = - tcg_global_mem_new_i64(TCG_AREG0, off, msaregnames[i * 2]); + tcg_global_mem_new_i64(cpu_env, off, msaregnames[i * 2]); /* The scalar floating-point unit (FPU) registers are mapped on * the MSA vector registers. */ fpu_f64[i] = msa_wr_d[i * 2]; off = offsetof(CPUMIPSState, active_fpu.fpr[i].wr.d[1]); msa_wr_d[i * 2 + 1] = - tcg_global_mem_new_i64(TCG_AREG0, off, msaregnames[i * 2 + 1]); + tcg_global_mem_new_i64(cpu_env, off, msaregnames[i * 2 + 1]); } - cpu_PC = tcg_global_mem_new(TCG_AREG0, + cpu_PC = tcg_global_mem_new(cpu_env, offsetof(CPUMIPSState, active_tc.PC), "PC"); for (i = 0; i < MIPS_DSP_ACC; i++) { - cpu_HI[i] = tcg_global_mem_new(TCG_AREG0, + cpu_HI[i] = tcg_global_mem_new(cpu_env, offsetof(CPUMIPSState, active_tc.HI[i]), regnames_HI[i]); - cpu_LO[i] = tcg_global_mem_new(TCG_AREG0, + cpu_LO[i] = tcg_global_mem_new(cpu_env, offsetof(CPUMIPSState, active_tc.LO[i]), regnames_LO[i]); } - cpu_dspctrl = tcg_global_mem_new(TCG_AREG0, + cpu_dspctrl = tcg_global_mem_new(cpu_env, offsetof(CPUMIPSState, active_tc.DSPControl), "DSPControl"); - bcond = tcg_global_mem_new(TCG_AREG0, + bcond = tcg_global_mem_new(cpu_env, offsetof(CPUMIPSState, bcond), "bcond"); - btarget = tcg_global_mem_new(TCG_AREG0, + btarget = tcg_global_mem_new(cpu_env, offsetof(CPUMIPSState, btarget), "btarget"); - hflags = tcg_global_mem_new_i32(TCG_AREG0, + hflags = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMIPSState, hflags), "hflags"); - fpu_fcr0 = tcg_global_mem_new_i32(TCG_AREG0, + fpu_fcr0 = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMIPSState, active_fpu.fcr0), "fcr0"); - fpu_fcr31 = tcg_global_mem_new_i32(TCG_AREG0, + fpu_fcr31 = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMIPSState, active_fpu.fcr31), "fcr31"); @@ -20548,6 +20068,16 @@ MIPSCPU *cpu_mips_init(const char *cpu_model) return cpu; } +bool cpu_supports_cps_smp(const char *cpu_model) +{ + const mips_def_t *def = cpu_mips_find_by_name(cpu_model); + if (!def) { + return false; + } + + return (def->CP0_Config3 & (1 << CP0C3_CMGCR)) != 0; +} + void cpu_state_reset(CPUMIPSState *env) { MIPSCPU *cpu = mips_env_get_cpu(env); @@ -20598,6 +20128,7 @@ void cpu_state_reset(CPUMIPSState *env) env->CP0_PageGrain_rw_bitmask = env->cpu_model->CP0_PageGrain_rw_bitmask; env->CP0_PageGrain = env->cpu_model->CP0_PageGrain; env->active_fpu.fcr0 = env->cpu_model->CP1_fcr0; + env->active_fpu.fcr31 = env->cpu_model->CP1_fcr31; env->msair = env->cpu_model->MSAIR; env->insn_flags = env->cpu_model->insn_flags; @@ -20640,12 +20171,16 @@ void cpu_state_reset(CPUMIPSState *env) env->CP0_Random = env->tlb->nb_tlb - 1; env->tlb->tlb_in_use = env->tlb->nb_tlb; env->CP0_Wired = 0; + env->CP0_GlobalNumber = (cs->cpu_index & 0xFF) << CP0GN_VPId; env->CP0_EBase = (cs->cpu_index & 0x3FF); if (kvm_enabled()) { env->CP0_EBase |= 0x40000000; } else { env->CP0_EBase |= 0x80000000; } + if (env->CP0_Config3 & (1 << CP0C3_CMGCR)) { + env->CP0_CMGCRBase = 0x1fbf8000 >> 4; + } env->CP0_Status = (1 << CP0St_BEV) | (1 << CP0St_ERL); /* vectored interrupts not implemented, timer on int 7, no performance counters. */ @@ -20714,18 +20249,19 @@ void cpu_state_reset(CPUMIPSState *env) } } -void restore_state_to_opc(CPUMIPSState *env, TranslationBlock *tb, int pc_pos) +void restore_state_to_opc(CPUMIPSState *env, TranslationBlock *tb, + target_ulong *data) { - env->active_tc.PC = tcg_ctx.gen_opc_pc[pc_pos]; + env->active_tc.PC = data[0]; env->hflags &= ~MIPS_HFLAG_BMASK; - env->hflags |= gen_opc_hflags[pc_pos]; + env->hflags |= data[1]; switch (env->hflags & MIPS_HFLAG_BMASK_BASE) { case MIPS_HFLAG_BR: break; case MIPS_HFLAG_BC: case MIPS_HFLAG_BL: case MIPS_HFLAG_B: - env->btarget = gen_opc_btarget[pc_pos]; + env->btarget = data[2]; break; } } diff --git a/qemu/target-mips/translate_init.c b/qemu/target-mips/translate_init.c index 9304e746b..5af077d0d 100644 --- a/qemu/target-mips/translate_init.c +++ b/qemu/target-mips/translate_init.c @@ -84,6 +84,7 @@ struct mips_def_t { int32_t CP0_TCStatus_rw_bitmask; int32_t CP0_SRSCtl; int32_t CP1_fcr0; + int32_t CP1_fcr31; int32_t MSAIR; int32_t SEGBITS; int32_t PABITS; @@ -389,39 +390,46 @@ static const mips_def_t mips_defs[] = .mmu_type = MMU_TYPE_R4000, }, { - /* A generic CPU providing MIPS32 Release 5 features. - FIXME: Eventually this should be replaced by a real CPU model. */ - .name = "mips32r5-generic", - .CP0_PRid = 0x00019700, - .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | + /* FIXME: + * Config3: CMGCR, SC, PW, VZ, CTXTC, CDMM, TL + * Config4: MMUExtDef + * Config5: EVA, MRP + * FIR(FCR0): Has2008 + * */ + .name = "P5600", + .CP0_PRid = 0x0001A800, + .CP0_Config0 = MIPS_CONFIG0 | (1 << CP0C0_MM) | (1 << CP0C0_AR) | (MMU_TYPE_R4000 << CP0C0_MT), - .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (15 << CP0C1_MMU) | - (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | - (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | - (1 << CP0C1_CA), + .CP0_Config1 = MIPS_CONFIG1 | (0x3F << CP0C1_MMU) | + (2 << CP0C1_IS) | (4 << CP0C1_IL) | (3 << CP0C1_IA) | + (2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) | + (1 << CP0C1_PC) | (1 << CP0C1_FP), .CP0_Config2 = MIPS_CONFIG2, .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) | (1 << CP0C3_MSAP) | - (1 << CP0C3_LPA), - .CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M), + (1 << CP0C3_BP) | (1 << CP0C3_BI) | (1 << CP0C3_ULRI) | + (1 << CP0C3_RXI) | (1 << CP0C3_LPA) | (1 << CP0C3_VInt), + .CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | (2 << CP0C4_IE) | + (0x1c << CP0C4_KScrExist), .CP0_Config4_rw_bitmask = 0, - .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_UFR) | (1 << CP0C5_LLB) | - (1 << CP0C5_MVH), - .CP0_Config5_rw_bitmask = (0 << CP0C5_M) | (1 << CP0C5_K) | - (1 << CP0C5_CV) | (0 << CP0C5_EVA) | - (1 << CP0C5_MSAEn) | (1 << CP0C5_UFR) | - (0 << CP0C5_NFExists), + .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_MVH) | (1 << CP0C5_LLB) | + (1 << CP0C5_MRP), + .CP0_Config5_rw_bitmask = (1 << CP0C5_K) | (1 << CP0C5_CV) | + (1 << CP0C5_MSAEn) | (1 << CP0C5_UFE) | + (1 << CP0C5_FRE) | (1 << CP0C5_UFR), .CP0_LLAddr_rw_bitmask = 0, - .CP0_LLAddr_shift = 4, + .CP0_LLAddr_shift = 0, .SYNCI_Step = 32, .CCRes = 2, - .CP0_Status_rw_bitmask = 0x3778FF1F, - .CP0_PageGrain_rw_bitmask = (1 << CP0PG_ELPA), - .CP1_fcr0 = (1 << FCR0_UFRP) | (1 << FCR0_F64) | (1 << FCR0_L) | - (1 << FCR0_W) | (1 << FCR0_D) | (1 << FCR0_S) | - (0x93 << FCR0_PRID), + .CP0_Status_rw_bitmask = 0x3C68FF1F, + .CP0_PageGrain_rw_bitmask = (1U << CP0PG_RIE) | (1 << CP0PG_XIE) | + (1 << CP0PG_ELPA) | (1 << CP0PG_IEC), + .CP1_fcr0 = (1 << FCR0_FREP) | (1 << FCR0_UFRP) | (1 << FCR0_HAS2008) | + (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) | + (1 << FCR0_D) | (1 << FCR0_S) | (0x03 << FCR0_PRID), + .CP1_fcr31 = (1 << FCR31_ABS2008) | (1 << FCR31_NAN2008), .SEGBITS = 32, .PABITS = 40, - .insn_flags = CPU_MIPS32R5 | ASE_MIPS16 | ASE_MSA, + .insn_flags = CPU_MIPS32R5 | ASE_MSA, .mmu_type = MMU_TYPE_R4000, }, { @@ -442,7 +450,7 @@ static const mips_def_t mips_defs[] = (1 << CP0C3_RXI) | (1U << CP0C3_M), .CP0_Config4 = MIPS_CONFIG4 | (0xfc << CP0C4_KScrExist) | (3 << CP0C4_IE) | (1U << CP0C4_M), - .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_LLB), + .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_XNP) | (1 << CP0C5_LLB), .CP0_Config5_rw_bitmask = (1 << CP0C5_SBRI) | (1 << CP0C5_FRE) | (1 << CP0C5_UFE), .CP0_LLAddr_rw_bitmask = 0, @@ -453,9 +461,10 @@ static const mips_def_t mips_defs[] = .CP0_PageGrain = (1 << CP0PG_IEC) | (1 << CP0PG_XIE) | (1U << CP0PG_RIE), .CP0_PageGrain_rw_bitmask = 0, - .CP1_fcr0 = (1 << FCR0_FREP) | (1 << FCR0_F64) | (1 << FCR0_L) | - (1 << FCR0_W) | (1 << FCR0_D) | (1 << FCR0_S) | - (0x00 << FCR0_PRID) | (0x0 << FCR0_REV), + .CP1_fcr0 = (1 << FCR0_FREP) | (1 << FCR0_HAS2008) | (1 << FCR0_F64) | + (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) | + (1 << FCR0_S) | (0x00 << FCR0_PRID) | (0x0 << FCR0_REV), + .CP1_fcr31 = (1 << FCR31_ABS2008) | (1 << FCR31_NAN2008), .SEGBITS = 32, .PABITS = 32, .insn_flags = CPU_MIPS32R6 | ASE_MICROMIPS, @@ -655,12 +664,14 @@ static const mips_def_t mips_defs[] = (2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) | (0 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), .CP0_Config2 = MIPS_CONFIG2, - .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) | (1 << CP0C3_MSAP) | + .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) | + (1 << CP0C3_CMGCR) | (1 << CP0C3_MSAP) | (1 << CP0C3_BP) | (1 << CP0C3_BI) | (1 << CP0C3_ULRI) | (1 << CP0C3_RXI) | (1 << CP0C3_LPA), .CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | (3 << CP0C4_IE) | (0xfc << CP0C4_KScrExist), - .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_LLB), + .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_XNP) | (1 << CP0C5_VP) | + (1 << CP0C5_LLB), .CP0_Config5_rw_bitmask = (1 << CP0C5_MSAEn) | (1 << CP0C5_SBRI) | (1 << CP0C5_FRE) | (1 << CP0C5_UFE), .CP0_LLAddr_rw_bitmask = 0, @@ -671,9 +682,10 @@ static const mips_def_t mips_defs[] = .CP0_PageGrain = (1 << CP0PG_IEC) | (1 << CP0PG_XIE) | (1U << CP0PG_RIE), .CP0_PageGrain_rw_bitmask = (1 << CP0PG_ELPA), - .CP1_fcr0 = (1 << FCR0_FREP) | (1 << FCR0_F64) | (1 << FCR0_L) | - (1 << FCR0_W) | (1 << FCR0_D) | (1 << FCR0_S) | - (0x00 << FCR0_PRID) | (0x0 << FCR0_REV), + .CP1_fcr0 = (1 << FCR0_FREP) | (1 << FCR0_HAS2008) | (1 << FCR0_F64) | + (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) | + (1 << FCR0_S) | (0x00 << FCR0_PRID) | (0x0 << FCR0_REV), + .CP1_fcr31 = (1 << FCR31_ABS2008) | (1 << FCR31_NAN2008), .SEGBITS = 48, .PABITS = 48, .insn_flags = CPU_MIPS64R6 | ASE_MSA, diff --git a/qemu/target-moxie/cpu.c b/qemu/target-moxie/cpu.c index 6b035aaab..b4ee84e90 100644 --- a/qemu/target-moxie/cpu.c +++ b/qemu/target-moxie/cpu.c @@ -17,6 +17,8 @@ * along with this program. If not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "qemu-common.h" #include "migration/vmstate.h" @@ -48,6 +50,12 @@ static void moxie_cpu_reset(CPUState *s) tlb_flush(s, 1); } +static void moxie_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) +{ + info->mach = bfd_arch_moxie; + info->print_insn = print_insn_moxie; +} + static void moxie_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); @@ -114,6 +122,14 @@ static void moxie_cpu_class_init(ObjectClass *oc, void *data) cc->get_phys_page_debug = moxie_cpu_get_phys_page_debug; cc->vmsd = &vmstate_moxie_cpu; #endif + cc->disas_set_info = moxie_cpu_disas_set_info; + + /* + * Reason: moxie_cpu_initfn() calls cpu_exec_init(), which saves + * the object in cpus -> dangling pointer after final + * object_unref(). + */ + dc->cannot_destroy_with_object_finalize_yet = true; } static void moxielite_initfn(Object *obj) diff --git a/qemu/target-moxie/cpu.h b/qemu/target-moxie/cpu.h index 29572aaba..4ee207796 100644 --- a/qemu/target-moxie/cpu.h +++ b/qemu/target-moxie/cpu.h @@ -19,15 +19,12 @@ #ifndef _CPU_MOXIE_H #define _CPU_MOXIE_H -#include "config.h" #include "qemu-common.h" #define TARGET_LONG_BITS 32 #define CPUArchState struct CPUMoxieState -#define ELF_MACHINE 0xFEED /* EM_MOXIE */ - #define MOXIE_EX_DIV0 0 #define MOXIE_EX_BAD 1 #define MOXIE_EX_IRQ 2 @@ -124,10 +121,9 @@ int cpu_moxie_signal_handler(int host_signum, void *pinfo, #define cpu_init(cpu_model) CPU(cpu_moxie_init(cpu_model)) #define cpu_exec cpu_moxie_exec -#define cpu_gen_code cpu_moxie_gen_code #define cpu_signal_handler cpu_moxie_signal_handler -static inline int cpu_mmu_index(CPUMoxieState *env) +static inline int cpu_mmu_index(CPUMoxieState *env, bool ifetch) { return 0; } diff --git a/qemu/target-moxie/helper.c b/qemu/target-moxie/helper.c index 6c98965b9..d51e9b9cc 100644 --- a/qemu/target-moxie/helper.c +++ b/qemu/target-moxie/helper.c @@ -17,11 +17,8 @@ * along with this program. If not, see . */ -#include -#include -#include +#include "qemu/osdep.h" -#include "config.h" #include "cpu.h" #include "mmu.h" #include "exec/exec-all.h" @@ -56,7 +53,7 @@ void helper_raise_exception(CPUMoxieState *env, int ex) /* Stash the address where the exception occurred. */ cpu_restore_state(cs, GETPC()); env->sregs[5] = env->pc; - /* Jump the the exception handline routine. */ + /* Jump to the exception handline routine. */ env->pc = env->sregs[1]; cpu_loop_exit(cs); } diff --git a/qemu/target-moxie/machine.c b/qemu/target-moxie/machine.c index b9316f0ec..912b79139 100644 --- a/qemu/target-moxie/machine.c +++ b/qemu/target-moxie/machine.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/boards.h" #include "machine.h" diff --git a/qemu/target-moxie/mmu.c b/qemu/target-moxie/mmu.c index 5217eedac..9203330b3 100644 --- a/qemu/target-moxie/mmu.c +++ b/qemu/target-moxie/mmu.c @@ -17,11 +17,8 @@ * along with this program. If not, see . */ -#include -#include -#include +#include "qemu/osdep.h" -#include "config.h" #include "cpu.h" #include "mmu.h" #include "exec/exec-all.h" diff --git a/qemu/target-moxie/translate.c b/qemu/target-moxie/translate.c index e3e913906..a437e2ab6 100644 --- a/qemu/target-moxie/translate.c +++ b/qemu/target-moxie/translate.c @@ -21,12 +21,7 @@ * http://moxielogic.org/wiki */ -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" @@ -36,6 +31,7 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" +#include "exec/log.h" /* This is the state at translation time. */ typedef struct DisasContext { @@ -60,7 +56,7 @@ enum { static TCGv cpu_pc; static TCGv cpu_gregs[16]; -static TCGv_ptr cpu_env; +static TCGv_env cpu_env; static TCGv cc_a, cc_b; #include "exec/gen-icount.h" @@ -110,16 +106,16 @@ void moxie_translate_init(void) return; } cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); - cpu_pc = tcg_global_mem_new_i32(TCG_AREG0, + cpu_pc = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMoxieState, pc), "$pc"); for (i = 0; i < 16; i++) - cpu_gregs[i] = tcg_global_mem_new_i32(TCG_AREG0, + cpu_gregs[i] = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMoxieState, gregs[i]), gregnames[i]); - cc_a = tcg_global_mem_new_i32(TCG_AREG0, + cc_a = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMoxieState, cc_a), "cc_a"); - cc_b = tcg_global_mem_new_i32(TCG_AREG0, + cc_b = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMoxieState, cc_b), "cc_b"); done_init = 1; @@ -154,10 +150,6 @@ static int decode_opc(MoxieCPU *cpu, DisasContext *ctx) /* Set the default instruction length. */ int length = 2; - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { - tcg_gen_debug_insn_start(ctx->pc); - } - /* Examine the 16-bit opcode. */ opcode = ctx->opcode; @@ -820,17 +812,13 @@ static int decode_opc(MoxieCPU *cpu, DisasContext *ctx) } /* generate intermediate code for basic block 'tb'. */ -static inline void -gen_intermediate_code_internal(MoxieCPU *cpu, TranslationBlock *tb, - bool search_pc) +void gen_intermediate_code(CPUMoxieState *env, struct TranslationBlock *tb) { + MoxieCPU *cpu = moxie_env_get_cpu(env); CPUState *cs = CPU(cpu); DisasContext ctx; target_ulong pc_start; - CPUBreakpoint *bp; - int j, lj = -1; - CPUMoxieState *env = &cpu->env; - int num_insns; + int num_insns, max_insns; pc_start = tb->pc; ctx.pc = pc_start; @@ -840,40 +828,40 @@ gen_intermediate_code_internal(MoxieCPU *cpu, TranslationBlock *tb, ctx.singlestep_enabled = 0; ctx.bstate = BS_NONE; num_insns = 0; + max_insns = tb->cflags & CF_COUNT_MASK; + if (max_insns == 0) { + max_insns = CF_COUNT_MASK; + } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } gen_tb_start(tb); do { - if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { - QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - if (ctx.pc == bp->pc) { - tcg_gen_movi_i32(cpu_pc, ctx.pc); - gen_helper_debug(cpu_env); - ctx.bstate = BS_EXCP; - goto done_generating; - } - } - } + tcg_gen_insn_start(ctx.pc); + num_insns++; - if (search_pc) { - j = tcg_op_buf_count(); - if (lj < j) { - lj++; - while (lj < j) { - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - } - tcg_ctx.gen_opc_pc[lj] = ctx.pc; - tcg_ctx.gen_opc_instr_start[lj] = 1; - tcg_ctx.gen_opc_icount[lj] = num_insns; + if (unlikely(cpu_breakpoint_test(cs, ctx.pc, BP_ANY))) { + tcg_gen_movi_i32(cpu_pc, ctx.pc); + gen_helper_debug(cpu_env); + ctx.bstate = BS_EXCP; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + ctx.pc += 2; + goto done_generating; } + ctx.opcode = cpu_lduw_code(env, ctx.pc); ctx.pc += decode_opc(cpu, &ctx); - num_insns++; + if (num_insns >= max_insns) { + break; + } if (cs->singlestep_enabled) { break; } - if ((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0) { break; } @@ -899,29 +887,12 @@ gen_intermediate_code_internal(MoxieCPU *cpu, TranslationBlock *tb, done_generating: gen_tb_end(tb, num_insns); - if (search_pc) { - j = tcg_op_buf_count(); - lj++; - while (lj <= j) { - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - } else { - tb->size = ctx.pc - pc_start; - tb->icount = num_insns; - } -} - -void gen_intermediate_code(CPUMoxieState *env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(moxie_env_get_cpu(env), tb, false); -} - -void gen_intermediate_code_pc(CPUMoxieState *env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(moxie_env_get_cpu(env), tb, true); + tb->size = ctx.pc - pc_start; + tb->icount = num_insns; } -void restore_state_to_opc(CPUMoxieState *env, TranslationBlock *tb, int pc_pos) +void restore_state_to_opc(CPUMoxieState *env, TranslationBlock *tb, + target_ulong *data) { - env->pc = tcg_ctx.gen_opc_pc[pc_pos]; + env->pc = data[0]; } diff --git a/qemu/target-openrisc/cpu.c b/qemu/target-openrisc/cpu.c index d97f3c03c..ae6ed9e92 100644 --- a/qemu/target-openrisc/cpu.c +++ b/qemu/target-openrisc/cpu.c @@ -17,6 +17,8 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "qemu-common.h" @@ -177,6 +179,13 @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data) dc->vmsd = &vmstate_openrisc_cpu; #endif cc->gdb_num_core_regs = 32 + 3; + + /* + * Reason: openrisc_cpu_initfn() calls cpu_exec_init(), which saves + * the object in cpus -> dangling pointer after final + * object_unref(). + */ + dc->cannot_destroy_with_object_finalize_yet = true; } static void cpu_register(const OpenRISCCPUInfo *info) diff --git a/qemu/target-openrisc/cpu.h b/qemu/target-openrisc/cpu.h index 36c4f20e2..4b63f2580 100644 --- a/qemu/target-openrisc/cpu.h +++ b/qemu/target-openrisc/cpu.h @@ -21,14 +21,12 @@ #define CPU_OPENRISC_H #define TARGET_LONG_BITS 32 -#define ELF_MACHINE EM_OPENRISC #define CPUArchState struct CPUOpenRISCState /* cpu_openrisc_map_address_* in CPUOpenRISCTLBContext need this decl. */ struct OpenRISCCPU; -#include "config.h" #include "qemu-common.h" #include "exec/cpu-defs.h" #include "fpu/softfloat.h" @@ -361,7 +359,6 @@ int cpu_openrisc_signal_handler(int host_signum, void *pinfo, void *puc); #define cpu_list cpu_openrisc_list #define cpu_exec cpu_openrisc_exec -#define cpu_gen_code cpu_openrisc_gen_code #define cpu_signal_handler cpu_openrisc_signal_handler #ifndef CONFIG_USER_ONLY @@ -403,7 +400,7 @@ static inline void cpu_get_tb_cpu_state(CPUOpenRISCState *env, *flags = (env->flags & D_FLAG); } -static inline int cpu_mmu_index(CPUOpenRISCState *env) +static inline int cpu_mmu_index(CPUOpenRISCState *env, bool ifetch) { if (!(env->sr & SR_IME)) { return MMU_NOMMU_IDX; diff --git a/qemu/target-openrisc/exception.c b/qemu/target-openrisc/exception.c index 74652a58f..ace3184d5 100644 --- a/qemu/target-openrisc/exception.c +++ b/qemu/target-openrisc/exception.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exception.h" diff --git a/qemu/target-openrisc/exception_helper.c b/qemu/target-openrisc/exception_helper.c index 6093953c9..329a9e400 100644 --- a/qemu/target-openrisc/exception_helper.c +++ b/qemu/target-openrisc/exception_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "exception.h" diff --git a/qemu/target-openrisc/fpu_helper.c b/qemu/target-openrisc/fpu_helper.c index c94ed35af..c54404b80 100644 --- a/qemu/target-openrisc/fpu_helper.c +++ b/qemu/target-openrisc/fpu_helper.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "exception.h" diff --git a/qemu/target-openrisc/gdbstub.c b/qemu/target-openrisc/gdbstub.c index 18bcc4616..edc301a7c 100644 --- a/qemu/target-openrisc/gdbstub.c +++ b/qemu/target-openrisc/gdbstub.c @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "exec/gdbstub.h" diff --git a/qemu/target-openrisc/int_helper.c b/qemu/target-openrisc/int_helper.c index 6e27aebd9..4d1f95890 100644 --- a/qemu/target-openrisc/int_helper.c +++ b/qemu/target-openrisc/int_helper.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "exception.h" diff --git a/qemu/target-openrisc/interrupt.c b/qemu/target-openrisc/interrupt.c index e480cfd1b..963eb1478 100644 --- a/qemu/target-openrisc/interrupt.c +++ b/qemu/target-openrisc/interrupt.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "qemu-common.h" #include "exec/gdbstub.h" diff --git a/qemu/target-openrisc/interrupt_helper.c b/qemu/target-openrisc/interrupt_helper.c index 55a780c7b..11b4b2056 100644 --- a/qemu/target-openrisc/interrupt_helper.c +++ b/qemu/target-openrisc/interrupt_helper.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" diff --git a/qemu/target-openrisc/machine.c b/qemu/target-openrisc/machine.c index 9f66a9cef..b4dc08dfe 100644 --- a/qemu/target-openrisc/machine.c +++ b/qemu/target-openrisc/machine.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/boards.h" diff --git a/qemu/target-openrisc/mmu.c b/qemu/target-openrisc/mmu.c index 750a93636..4ab414a68 100644 --- a/qemu/target-openrisc/mmu.c +++ b/qemu/target-openrisc/mmu.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "qemu-common.h" #include "exec/gdbstub.h" diff --git a/qemu/target-openrisc/mmu_helper.c b/qemu/target-openrisc/mmu_helper.c index ee1c6f611..d7952d449 100644 --- a/qemu/target-openrisc/mmu_helper.c +++ b/qemu/target-openrisc/mmu_helper.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/cpu_ldst.h" diff --git a/qemu/target-openrisc/sys_helper.c b/qemu/target-openrisc/sys_helper.c index 53ca6bcef..f917be6be 100644 --- a/qemu/target-openrisc/sys_helper.c +++ b/qemu/target-openrisc/sys_helper.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" diff --git a/qemu/target-openrisc/translate.c b/qemu/target-openrisc/translate.c index a62cbf401..5d0ab442a 100644 --- a/qemu/target-openrisc/translate.c +++ b/qemu/target-openrisc/translate.c @@ -18,13 +18,13 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" #include "disas/disas.h" #include "tcg-op.h" #include "qemu-common.h" #include "qemu/log.h" -#include "config.h" #include "qemu/bitops.h" #include "exec/cpu_ldst.h" @@ -32,6 +32,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" #define OPENRISC_DISAS @@ -52,7 +53,7 @@ typedef struct DisasContext { uint32_t delayed_branch; } DisasContext; -static TCGv_ptr cpu_env; +static TCGv_env cpu_env; static TCGv cpu_sr; static TCGv cpu_R[32]; static TCGv cpu_pc; @@ -77,39 +78,39 @@ void openrisc_translate_init(void) int i; cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); - cpu_sr = tcg_global_mem_new(TCG_AREG0, + cpu_sr = tcg_global_mem_new(cpu_env, offsetof(CPUOpenRISCState, sr), "sr"); - env_flags = tcg_global_mem_new_i32(TCG_AREG0, + env_flags = tcg_global_mem_new_i32(cpu_env, offsetof(CPUOpenRISCState, flags), "flags"); - cpu_pc = tcg_global_mem_new(TCG_AREG0, + cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPUOpenRISCState, pc), "pc"); - cpu_npc = tcg_global_mem_new(TCG_AREG0, + cpu_npc = tcg_global_mem_new(cpu_env, offsetof(CPUOpenRISCState, npc), "npc"); - cpu_ppc = tcg_global_mem_new(TCG_AREG0, + cpu_ppc = tcg_global_mem_new(cpu_env, offsetof(CPUOpenRISCState, ppc), "ppc"); - jmp_pc = tcg_global_mem_new(TCG_AREG0, + jmp_pc = tcg_global_mem_new(cpu_env, offsetof(CPUOpenRISCState, jmp_pc), "jmp_pc"); - env_btaken = tcg_global_mem_new_i32(TCG_AREG0, + env_btaken = tcg_global_mem_new_i32(cpu_env, offsetof(CPUOpenRISCState, btaken), "btaken"); - fpcsr = tcg_global_mem_new_i32(TCG_AREG0, + fpcsr = tcg_global_mem_new_i32(cpu_env, offsetof(CPUOpenRISCState, fpcsr), "fpcsr"); - machi = tcg_global_mem_new(TCG_AREG0, + machi = tcg_global_mem_new(cpu_env, offsetof(CPUOpenRISCState, machi), "machi"); - maclo = tcg_global_mem_new(TCG_AREG0, + maclo = tcg_global_mem_new(cpu_env, offsetof(CPUOpenRISCState, maclo), "maclo"); - fpmaddhi = tcg_global_mem_new(TCG_AREG0, + fpmaddhi = tcg_global_mem_new(cpu_env, offsetof(CPUOpenRISCState, fpmaddhi), "fpmaddhi"); - fpmaddlo = tcg_global_mem_new(TCG_AREG0, + fpmaddlo = tcg_global_mem_new(cpu_env, offsetof(CPUOpenRISCState, fpmaddlo), "fpmaddlo"); for (i = 0; i < 32; i++) { - cpu_R[i] = tcg_global_mem_new(TCG_AREG0, + cpu_R[i] = tcg_global_mem_new(cpu_env, offsetof(CPUOpenRISCState, gpr[i]), regnames[i]); } @@ -279,7 +280,7 @@ static void dec_calc(DisasContext *dc, uint32_t insn) tcg_gen_extu_i32_i64(ta, cpu_R[ra]); tcg_gen_extu_i32_i64(tb, cpu_R[rb]); tcg_gen_add_i64(td, ta, tb); - tcg_gen_trunc_i64_i32(res, td); + tcg_gen_extrl_i64_i32(res, td); tcg_gen_shri_i64(td, td, 31); tcg_gen_andi_i64(td, td, 0x3); /* Jump to lab when no overflow. */ @@ -324,7 +325,7 @@ static void dec_calc(DisasContext *dc, uint32_t insn) tcg_gen_shri_i64(tcy, tcy, 10); tcg_gen_add_i64(td, ta, tb); tcg_gen_add_i64(td, td, tcy); - tcg_gen_trunc_i64_i32(res, td); + tcg_gen_extrl_i64_i32(res, td); tcg_gen_shri_i64(td, td, 32); tcg_gen_andi_i64(td, td, 0x3); /* Jump to lab when no overflow. */ @@ -366,7 +367,7 @@ static void dec_calc(DisasContext *dc, uint32_t insn) tcg_gen_extu_i32_i64(ta, cpu_R[ra]); tcg_gen_extu_i32_i64(tb, cpu_R[rb]); tcg_gen_sub_i64(td, ta, tb); - tcg_gen_trunc_i64_i32(res, td); + tcg_gen_extrl_i64_i32(res, td); tcg_gen_shri_i64(td, td, 31); tcg_gen_andi_i64(td, td, 0x3); /* Jump to lab when no overflow. */ @@ -779,9 +780,9 @@ static void dec_misc(DisasContext *dc, uint32_t insn) tcg_gen_ext_i32_i64(t1, dst); tcg_gen_concat_i32_i64(t2, maclo, machi); tcg_gen_add_i64(t2, t2, t1); - tcg_gen_trunc_i64_i32(maclo, t2); + tcg_gen_extrl_i64_i32(maclo, t2); tcg_gen_shri_i64(t2, t2, 32); - tcg_gen_trunc_i64_i32(machi, t2); + tcg_gen_extrl_i64_i32(machi, t2); tcg_temp_free_i32(dst); tcg_temp_free(ttmp); tcg_temp_free_i64(t1); @@ -898,7 +899,7 @@ static void dec_misc(DisasContext *dc, uint32_t insn) TCGv_i32 sr_ove = tcg_temp_local_new_i32(); tcg_gen_extu_i32_i64(ta, cpu_R[ra]); tcg_gen_addi_i64(td, ta, sign_extend(I16, 16)); - tcg_gen_trunc_i64_i32(res, td); + tcg_gen_extrl_i64_i32(res, td); tcg_gen_shri_i64(td, td, 32); tcg_gen_andi_i64(td, td, 0x3); /* Jump to lab when no overflow. */ @@ -934,7 +935,7 @@ static void dec_misc(DisasContext *dc, uint32_t insn) tcg_gen_extu_i32_i64(tcy, sr_cy); tcg_gen_addi_i64(td, ta, sign_extend(I16, 16)); tcg_gen_add_i64(td, td, tcy); - tcg_gen_trunc_i64_i32(res, td); + tcg_gen_extrl_i64_i32(res, td); tcg_gen_shri_i64(td, td, 32); tcg_gen_andi_i64(td, td, 0x3); /* Jump to lab when no overflow. */ @@ -1073,9 +1074,9 @@ static void dec_mac(DisasContext *dc, uint32_t insn) tcg_gen_ext_i32_i64(t1, t0); tcg_gen_concat_i32_i64(t2, maclo, machi); tcg_gen_add_i64(t2, t2, t1); - tcg_gen_trunc_i64_i32(maclo, t2); + tcg_gen_extrl_i64_i32(maclo, t2); tcg_gen_shri_i64(t2, t2, 32); - tcg_gen_trunc_i64_i32(machi, t2); + tcg_gen_extrl_i64_i32(machi, t2); tcg_temp_free_i32(t0); tcg_temp_free_i64(t1); tcg_temp_free_i64(t2); @@ -1092,9 +1093,9 @@ static void dec_mac(DisasContext *dc, uint32_t insn) tcg_gen_ext_i32_i64(t1, t0); tcg_gen_concat_i32_i64(t2, maclo, machi); tcg_gen_sub_i64(t2, t2, t1); - tcg_gen_trunc_i64_i32(maclo, t2); + tcg_gen_extrl_i64_i32(maclo, t2); tcg_gen_shri_i64(t2, t2, 32); - tcg_gen_trunc_i64_i32(machi, t2); + tcg_gen_extrl_i64_i32(machi, t2); tcg_temp_free_i32(t0); tcg_temp_free_i64(t1); tcg_temp_free_i64(t2); @@ -1618,30 +1619,12 @@ static void disas_openrisc_insn(DisasContext *dc, OpenRISCCPU *cpu) } } -static void check_breakpoint(OpenRISCCPU *cpu, DisasContext *dc) -{ - CPUState *cs = CPU(cpu); - CPUBreakpoint *bp; - - if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { - QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - if (bp->pc == dc->pc) { - tcg_gen_movi_tl(cpu_pc, dc->pc); - gen_exception(dc, EXCP_DEBUG); - dc->is_jmp = DISAS_UPDATE; - } - } - } -} - -static inline void gen_intermediate_code_internal(OpenRISCCPU *cpu, - TranslationBlock *tb, - int search_pc) +void gen_intermediate_code(CPUOpenRISCState *env, struct TranslationBlock *tb) { + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); CPUState *cs = CPU(cpu); struct DisasContext ctx, *dc = &ctx; uint32_t pc_start; - int j, k; uint32_t next_page_start; int num_insns; int max_insns; @@ -1653,7 +1636,7 @@ static inline void gen_intermediate_code_internal(OpenRISCCPU *cpu, dc->ppc = pc_start; dc->pc = pc_start; dc->flags = cpu->env.cpucfgr; - dc->mem_idx = cpu_mmu_index(&cpu->env); + dc->mem_idx = cpu_mmu_index(&cpu->env, false); dc->synced_flags = dc->tb_flags = tb->flags; dc->delayed_branch = !!(dc->tb_flags & D_FLAG); dc->singlestep_enabled = cs->singlestep_enabled; @@ -1663,36 +1646,35 @@ static inline void gen_intermediate_code_internal(OpenRISCCPU *cpu, } next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; - k = -1; num_insns = 0; max_insns = tb->cflags & CF_COUNT_MASK; if (max_insns == 0) { max_insns = CF_COUNT_MASK; } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } gen_tb_start(tb); do { - check_breakpoint(cpu, dc); - if (search_pc) { - j = tcg_op_buf_count(); - if (k < j) { - k++; - while (k < j) { - tcg_ctx.gen_opc_instr_start[k++] = 0; - } - } - tcg_ctx.gen_opc_pc[k] = dc->pc; - tcg_ctx.gen_opc_instr_start[k] = 1; - tcg_ctx.gen_opc_icount[k] = num_insns; - } + tcg_gen_insn_start(dc->pc); + num_insns++; - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { - tcg_gen_debug_insn_start(dc->pc); + if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { + tcg_gen_movi_tl(cpu_pc, dc->pc); + gen_exception(dc, EXCP_DEBUG); + dc->is_jmp = DISAS_UPDATE; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + dc->pc += 4; + break; } - if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) { + if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { gen_io_start(); } dc->ppc = dc->pc - 4; @@ -1701,7 +1683,6 @@ static inline void gen_intermediate_code_internal(OpenRISCCPU *cpu, tcg_gen_movi_tl(cpu_npc, dc->npc); disas_openrisc_insn(dc, cpu); dc->pc = dc->npc; - num_insns++; /* delay slot */ if (dc->delayed_branch) { dc->delayed_branch--; @@ -1756,16 +1737,8 @@ static inline void gen_intermediate_code_internal(OpenRISCCPU *cpu, gen_tb_end(tb, num_insns); - if (search_pc) { - j = tcg_op_buf_count(); - k++; - while (k <= j) { - tcg_ctx.gen_opc_instr_start[k++] = 0; - } - } else { - tb->size = dc->pc - pc_start; - tb->icount = num_insns; - } + tb->size = dc->pc - pc_start; + tb->icount = num_insns; #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { @@ -1777,17 +1750,6 @@ static inline void gen_intermediate_code_internal(OpenRISCCPU *cpu, #endif } -void gen_intermediate_code(CPUOpenRISCState *env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(openrisc_env_get_cpu(env), tb, 0); -} - -void gen_intermediate_code_pc(CPUOpenRISCState *env, - struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(openrisc_env_get_cpu(env), tb, 1); -} - void openrisc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int flags) @@ -1804,7 +1766,7 @@ void openrisc_cpu_dump_state(CPUState *cs, FILE *f, } void restore_state_to_opc(CPUOpenRISCState *env, TranslationBlock *tb, - int pc_pos) + target_ulong *data) { - env->pc = tcg_ctx.gen_opc_pc[pc_pos]; + env->pc = data[0]; } diff --git a/qemu/target-ppc/Makefile.objs b/qemu/target-ppc/Makefile.objs index a7ae392cc..e667e6970 100644 --- a/qemu/target-ppc/Makefile.objs +++ b/qemu/target-ppc/Makefile.objs @@ -1,10 +1,10 @@ obj-y += cpu-models.o obj-y += translate.o ifeq ($(CONFIG_SOFTMMU),y) -obj-y += machine.o mmu_helper.o mmu-hash32.o +obj-y += machine.o mmu_helper.o mmu-hash32.o monitor.o obj-$(TARGET_PPC64) += mmu-hash64.o arch_dump.o endif -obj-$(CONFIG_KVM) += kvm.o kvm_ppc.o +obj-$(CONFIG_KVM) += kvm.o obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o obj-y += dfp_helper.o obj-y += excp_helper.o diff --git a/qemu/target-ppc/arch_dump.c b/qemu/target-ppc/arch_dump.c index 5acafc68a..df1fd8c33 100644 --- a/qemu/target-ppc/arch_dump.c +++ b/qemu/target-ppc/arch_dump.c @@ -12,6 +12,7 @@ * */ +#include "qemu/osdep.h" #include "cpu.h" #include "elf.h" #include "exec/cpu-all.h" @@ -278,9 +279,3 @@ int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, PowerPCCPU *cpu = POWERPC_CPU(cs); return ppc64_write_all_elf64_notes("CORE", f, cpu, cpuid, opaque); } - -int ppc64_cpu_write_elf64_qemunote(WriteCoreDumpFunction f, - CPUState *cpu, void *opaque) -{ - return 0; -} diff --git a/qemu/target-ppc/cpu-models.c b/qemu/target-ppc/cpu-models.c index 4d5ab4ba1..5209e63a7 100644 --- a/qemu/target-ppc/cpu-models.c +++ b/qemu/target-ppc/cpu-models.c @@ -24,6 +24,7 @@ * inside "#if defined(TODO) ... #endif" statements to make tests easier. */ +#include "qemu/osdep.h" #include "cpu.h" #include "cpu-models.h" @@ -1138,10 +1139,12 @@ "POWER7 v2.3") POWERPC_DEF("POWER7+_v2.1", CPU_POWERPC_POWER7P_v21, POWER7, "POWER7+ v2.1") - POWERPC_DEF("POWER8E_v1.0", CPU_POWERPC_POWER8E_v10, POWER8, - "POWER8E v1.0") - POWERPC_DEF("POWER8_v1.0", CPU_POWERPC_POWER8_v10, POWER8, - "POWER8 v1.0") + POWERPC_DEF("POWER8E_v2.1", CPU_POWERPC_POWER8E_v21, POWER8, + "POWER8E v2.1") + POWERPC_DEF("POWER8_v2.0", CPU_POWERPC_POWER8_v20, POWER8, + "POWER8 v2.0") + POWERPC_DEF("POWER8NVL_v1.0",CPU_POWERPC_POWER8NVL_v10, POWER8, + "POWER8NVL v1.0") POWERPC_DEF("970_v2.2", CPU_POWERPC_970_v22, 970, "PowerPC 970 v2.2") POWERPC_DEF("970fx_v1.0", CPU_POWERPC_970FX_v10, 970, @@ -1389,8 +1392,9 @@ PowerPCCPUAlias ppc_cpu_aliases[] = { { "POWER5gs", "POWER5+_v2.1" }, { "POWER7", "POWER7_v2.3" }, { "POWER7+", "POWER7+_v2.1" }, - { "POWER8E", "POWER8E_v1.0" }, - { "POWER8", "POWER8_v1.0" }, + { "POWER8E", "POWER8E_v2.1" }, + { "POWER8", "POWER8_v2.0" }, + { "POWER8NVL", "POWER8NVL_v1.0" }, { "970", "970_v2.2" }, { "970fx", "970fx_v3.1" }, { "970mp", "970mp_v1.1" }, diff --git a/qemu/target-ppc/cpu-models.h b/qemu/target-ppc/cpu-models.h index 9d80e7227..f21a44c83 100644 --- a/qemu/target-ppc/cpu-models.h +++ b/qemu/target-ppc/cpu-models.h @@ -557,9 +557,11 @@ enum { CPU_POWERPC_POWER7P_BASE = 0x004A0000, CPU_POWERPC_POWER7P_v21 = 0x004A0201, CPU_POWERPC_POWER8E_BASE = 0x004B0000, - CPU_POWERPC_POWER8E_v10 = 0x004B0100, + CPU_POWERPC_POWER8E_v21 = 0x004B0201, CPU_POWERPC_POWER8_BASE = 0x004D0000, - CPU_POWERPC_POWER8_v10 = 0x004D0100, + CPU_POWERPC_POWER8_v20 = 0x004D0200, + CPU_POWERPC_POWER8NVL_BASE = 0x004C0000, + CPU_POWERPC_POWER8NVL_v10 = 0x004C0100, CPU_POWERPC_970_v22 = 0x00390202, CPU_POWERPC_970FX_v10 = 0x00391100, CPU_POWERPC_970FX_v20 = 0x003C0200, diff --git a/qemu/target-ppc/cpu-qom.h b/qemu/target-ppc/cpu-qom.h index 6967a8028..7d5e2b36a 100644 --- a/qemu/target-ppc/cpu-qom.h +++ b/qemu/target-ppc/cpu-qom.h @@ -118,13 +118,13 @@ void ppc_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags); void ppc_cpu_dump_statistics(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags); +int ppc_cpu_get_monitor_def(CPUState *cs, const char *name, + uint64_t *pval); hwaddr ppc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int ppc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); int ppc_cpu_gdb_read_register_apple(CPUState *cpu, uint8_t *buf, int reg); int ppc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); int ppc_cpu_gdb_write_register_apple(CPUState *cpu, uint8_t *buf, int reg); -int ppc64_cpu_write_elf64_qemunote(WriteCoreDumpFunction f, - CPUState *cpu, void *opaque); int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque); #ifndef CONFIG_USER_ONLY diff --git a/qemu/target-ppc/cpu.h b/qemu/target-ppc/cpu.h index 6f76674a4..5282533b3 100644 --- a/qemu/target-ppc/cpu.h +++ b/qemu/target-ppc/cpu.h @@ -19,7 +19,6 @@ #if !defined (__CPU_PPC_H__) #define __CPU_PPC_H__ -#include "config.h" #include "qemu-common.h" //#define PPC_EMULATE_32BITS_HYPV @@ -81,9 +80,9 @@ #include "fpu/softfloat.h" #if defined (TARGET_PPC64) -#define ELF_MACHINE EM_PPC64 +#define PPC_ELF_MACHINE EM_PPC64 #else -#define ELF_MACHINE EM_PPC +#define PPC_ELF_MACHINE EM_PPC #endif /*****************************************************************************/ @@ -117,14 +116,20 @@ enum powerpc_mmu_t { #define POWERPC_MMU_AMR 0x00040000 /* 64 bits PowerPC MMU */ POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001, + /* Architecture 2.03 and later (has LPCR) */ + POWERPC_MMU_2_03 = POWERPC_MMU_64 | 0x00000002, /* Architecture 2.06 variant */ POWERPC_MMU_2_06 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG | POWERPC_MMU_AMR | 0x00000003, /* Architecture 2.06 "degraded" (no 1T segments) */ POWERPC_MMU_2_06a = POWERPC_MMU_64 | POWERPC_MMU_AMR | 0x00000003, - /* Architecture 2.06 "degraded" (no 1T segments or AMR) */ - POWERPC_MMU_2_06d = POWERPC_MMU_64 | 0x00000003, + /* Architecture 2.07 variant */ + POWERPC_MMU_2_07 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG + | POWERPC_MMU_AMR | 0x00000004, + /* Architecture 2.07 "degraded" (no 1T segments) */ + POWERPC_MMU_2_07a = POWERPC_MMU_64 | POWERPC_MMU_AMR + | 0x00000004, #endif /* defined(TARGET_PPC64) */ }; @@ -162,6 +167,8 @@ enum powerpc_excp_t { POWERPC_EXCP_970, /* POWER7 exception model */ POWERPC_EXCP_POWER7, + /* POWER8 exception model */ + POWERPC_EXCP_POWER8, #endif /* defined(TARGET_PPC64) */ }; @@ -413,6 +420,7 @@ typedef struct ppc_slb_t ppc_slb_t; struct ppc_slb_t { uint64_t esid; uint64_t vsid; + const struct ppc_one_seg_page_size *sps; }; #define MAX_SLB_ENTRIES 64 @@ -468,9 +476,17 @@ struct ppc_slb_t { #define MSR_RI 1 /* Recoverable interrupt 1 */ #define MSR_LE 0 /* Little-endian mode 1 hflags */ -#define LPCR_ILE (1 << (63-38)) -#define LPCR_AIL_SHIFT (63-40) /* Alternate interrupt location */ -#define LPCR_AIL (3 << LPCR_AIL_SHIFT) +/* LPCR bits */ +#define LPCR_VPM0 (1ull << (63 - 0)) +#define LPCR_VPM1 (1ull << (63 - 1)) +#define LPCR_ISL (1ull << (63 - 2)) +#define LPCR_KBV (1ull << (63 - 3)) +#define LPCR_ILE (1ull << (63 - 38)) +#define LPCR_MER (1ull << (63 - 52)) +#define LPCR_LPES0 (1ull << (63 - 60)) +#define LPCR_LPES1 (1ull << (63 - 61)) +#define LPCR_AIL_SHIFT (63 - 40) /* Alternate interrupt location */ +#define LPCR_AIL (3ull << LPCR_AIL_SHIFT) #define msr_sf ((env->msr >> MSR_SF) & 1) #define msr_isf ((env->msr >> MSR_ISF) & 1) @@ -678,6 +694,46 @@ enum { #define fpscr_eex (((env->fpscr) >> FPSCR_XX) & ((env->fpscr) >> FPSCR_XE) & \ 0x1F) +#define FP_FX (1ull << FPSCR_FX) +#define FP_FEX (1ull << FPSCR_FEX) +#define FP_VX (1ull << FPSCR_VX) +#define FP_OX (1ull << FPSCR_OX) +#define FP_UX (1ull << FPSCR_UX) +#define FP_ZX (1ull << FPSCR_ZX) +#define FP_XX (1ull << FPSCR_XX) +#define FP_VXSNAN (1ull << FPSCR_VXSNAN) +#define FP_VXISI (1ull << FPSCR_VXISI) +#define FP_VXIDI (1ull << FPSCR_VXIDI) +#define FP_VXZDZ (1ull << FPSCR_VXZDZ) +#define FP_VXIMZ (1ull << FPSCR_VXIMZ) +#define FP_VXVC (1ull << FPSCR_VXVC) +#define FP_FR (1ull << FSPCR_FR) +#define FP_FI (1ull << FPSCR_FI) +#define FP_C (1ull << FPSCR_C) +#define FP_FL (1ull << FPSCR_FL) +#define FP_FG (1ull << FPSCR_FG) +#define FP_FE (1ull << FPSCR_FE) +#define FP_FU (1ull << FPSCR_FU) +#define FP_FPCC (FP_FL | FP_FG | FP_FE | FP_FU) +#define FP_FPRF (FP_C | FP_FL | FP_FG | FP_FE | FP_FU) +#define FP_VXSOFT (1ull << FPSCR_VXSOFT) +#define FP_VXSQRT (1ull << FPSCR_VXSQRT) +#define FP_VXCVI (1ull << FPSCR_VXCVI) +#define FP_VE (1ull << FPSCR_VE) +#define FP_OE (1ull << FPSCR_OE) +#define FP_UE (1ull << FPSCR_UE) +#define FP_ZE (1ull << FPSCR_ZE) +#define FP_XE (1ull << FPSCR_XE) +#define FP_NI (1ull << FPSCR_NI) +#define FP_RN1 (1ull << FPSCR_RN1) +#define FP_RN (1ull << FPSCR_RN) + +/* the exception bits which can be cleared by mcrfs - includes FX */ +#define FP_EX_CLEAR_BITS (FP_FX | FP_OX | FP_UX | FP_ZX | \ + FP_XX | FP_VXSNAN | FP_VXISI | FP_VXIDI | \ + FP_VXZDZ | FP_VXIMZ | FP_VXVC | FP_VXSOFT | \ + FP_VXSQRT | FP_VXCVI) + /*****************************************************************************/ /* Vector status and control register */ #define VSCR_NJ 16 /* Vector non-java */ @@ -1073,6 +1129,7 @@ struct CPUPPCState { uint64_t insns_flags2; #if defined(TARGET_PPC64) struct ppc_segment_page_sizes sps; + bool ci_large_pages; #endif #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) @@ -1182,7 +1239,7 @@ void ppc_store_msr (CPUPPCState *env, target_ulong value); void ppc_cpu_list (FILE *f, fprintf_function cpu_fprintf); int ppc_get_compat_smt_threads(PowerPCCPU *cpu); -int ppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version); +void ppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version, Error **errp); /* Time-base and decrementer management */ #ifndef NO_CPU_IO_DEFS @@ -1213,6 +1270,7 @@ void store_booke_tcr (CPUPPCState *env, target_ulong val); void store_booke_tsr (CPUPPCState *env, target_ulong val); void ppc_tlb_invalidate_all (CPUPPCState *env); void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr); +void cpu_ppc_set_papr(PowerPCCPU *cpu); #endif #endif @@ -1241,7 +1299,6 @@ int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val); #define cpu_init(cpu_model) CPU(cpu_ppc_init(cpu_model)) #define cpu_exec cpu_ppc_exec -#define cpu_gen_code cpu_ppc_gen_code #define cpu_signal_handler cpu_ppc_signal_handler #define cpu_list ppc_cpu_list @@ -1250,7 +1307,7 @@ int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val); #define MMU_MODE1_SUFFIX _kernel #define MMU_MODE2_SUFFIX _hypv #define MMU_USER_IDX 0 -static inline int cpu_mmu_index (CPUPPCState *env) +static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch) { return env->mmu_idx; } @@ -1300,11 +1357,14 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define SPR_SRR1 (0x01B) #define SPR_CFAR (0x01C) #define SPR_AMR (0x01D) +#define SPR_ACOP (0x01F) #define SPR_BOOKE_PID (0x030) +#define SPR_BOOKS_PID (0x030) #define SPR_BOOKE_DECAR (0x036) #define SPR_BOOKE_CSRR0 (0x03A) #define SPR_BOOKE_CSRR1 (0x03B) #define SPR_BOOKE_DEAR (0x03D) +#define SPR_IAMR (0x03D) #define SPR_BOOKE_ESR (0x03E) #define SPR_BOOKE_IVPR (0x03F) #define SPR_MPC_EIE (0x050) @@ -1334,6 +1394,12 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define SPR_UAMOR (0x09D) #define SPR_MPC_ICTRL (0x09E) #define SPR_MPC_BAR (0x09F) +#define SPR_PSPB (0x09F) +#define SPR_DAWR (0x0B4) +#define SPR_RPR (0x0BA) +#define SPR_CIABR (0x0BB) +#define SPR_DAWRX (0x0BC) +#define SPR_HFSCR (0x0BE) #define SPR_VRSAVE (0x100) #define SPR_USPRG0 (0x100) #define SPR_USPRG1 (0x101) @@ -1388,19 +1454,25 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define SPR_HSRR1 (0x13B) #define SPR_BOOKE_IAC4 (0x13B) #define SPR_BOOKE_DAC1 (0x13C) -#define SPR_LPIDR (0x13D) +#define SPR_MMCRH (0x13C) #define SPR_DABR2 (0x13D) #define SPR_BOOKE_DAC2 (0x13D) +#define SPR_TFMR (0x13D) #define SPR_BOOKE_DVC1 (0x13E) #define SPR_LPCR (0x13E) #define SPR_BOOKE_DVC2 (0x13F) +#define SPR_LPIDR (0x13F) #define SPR_BOOKE_TSR (0x150) +#define SPR_HMER (0x150) +#define SPR_HMEER (0x151) #define SPR_PCR (0x152) +#define SPR_BOOKE_LPIDR (0x152) #define SPR_BOOKE_TCR (0x154) #define SPR_BOOKE_TLB0PS (0x158) #define SPR_BOOKE_TLB1PS (0x159) #define SPR_BOOKE_TLB2PS (0x15A) #define SPR_BOOKE_TLB3PS (0x15B) +#define SPR_AMOR (0x15D) #define SPR_BOOKE_MAS7_MAS3 (0x174) #define SPR_BOOKE_IVOR0 (0x190) #define SPR_BOOKE_IVOR1 (0x191) @@ -1517,6 +1589,7 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define SPR_PERF0 (0x300) #define SPR_RCPU_MI_RBA0 (0x300) #define SPR_MPC_MI_CTR (0x300) +#define SPR_POWER_USIER (0x300) #define SPR_PERF1 (0x301) #define SPR_RCPU_MI_RBA1 (0x301) #define SPR_POWER_UMMCR2 (0x301) @@ -1566,6 +1639,7 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define SPR_PERFF (0x30F) #define SPR_MPC_MD_TW (0x30F) #define SPR_UPERF0 (0x310) +#define SPR_POWER_SIER (0x310) #define SPR_UPERF1 (0x311) #define SPR_POWER_MMCR2 (0x311) #define SPR_UPERF2 (0x312) @@ -1617,7 +1691,9 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define SPR_MPC_MD_DBRAM1 (0x32A) #define SPR_RCPU_L2U_RA3 (0x32B) #define SPR_TAR (0x32F) +#define SPR_IC (0x350) #define SPR_VTB (0x351) +#define SPR_MMCRC (0x353) #define SPR_440_INV0 (0x370) #define SPR_440_INV1 (0x371) #define SPR_440_INV2 (0x372) @@ -1627,8 +1703,14 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define SPR_440_ITV2 (0x376) #define SPR_440_ITV3 (0x377) #define SPR_440_CCR1 (0x378) +#define SPR_TACR (0x378) +#define SPR_TCSCR (0x379) +#define SPR_CSIGR (0x37a) #define SPR_DCRIPR (0x37B) +#define SPR_POWER_SPMC1 (0x37C) +#define SPR_POWER_SPMC2 (0x37D) #define SPR_POWER_MMCRS (0x37E) +#define SPR_WORT (0x37F) #define SPR_PPR (0x380) #define SPR_750_GQR0 (0x390) #define SPR_440_DNV0 (0x390) @@ -1651,6 +1733,7 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define SPR_440_DVLIM (0x398) #define SPR_750_WPAR (0x399) #define SPR_440_IVLIM (0x399) +#define SPR_TSCR (0x399) #define SPR_750_DMAU (0x39A) #define SPR_750_DMAL (0x39B) #define SPR_440_RSTCFG (0x39B) @@ -1825,9 +1908,10 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define L1CSR1_ICE 0x00000001 /* Instruction Cache Enable */ /* HID0 bits */ -#define HID0_DEEPNAP (1 << 24) -#define HID0_DOZE (1 << 23) -#define HID0_NAP (1 << 22) +#define HID0_DEEPNAP (1 << 24) /* pre-2.06 */ +#define HID0_DOZE (1 << 23) /* pre-2.06 */ +#define HID0_NAP (1 << 22) /* pre-2.06 */ +#define HID0_HILE (1ull << (63 - 19)) /* POWER8 */ /*****************************************************************************/ /* PowerPC Instructions types definitions */ @@ -2176,6 +2260,33 @@ enum { PCR_TM_DIS = 1ull << (63-2), /* Trans. memory disable (POWER8) */ }; +/* HMER/HMEER */ +enum { + HMER_MALFUNCTION_ALERT = 1ull << (63 - 0), + HMER_PROC_RECV_DONE = 1ull << (63 - 2), + HMER_PROC_RECV_ERROR_MASKED = 1ull << (63 - 3), + HMER_TFAC_ERROR = 1ull << (63 - 4), + HMER_TFMR_PARITY_ERROR = 1ull << (63 - 5), + HMER_XSCOM_FAIL = 1ull << (63 - 8), + HMER_XSCOM_DONE = 1ull << (63 - 9), + HMER_PROC_RECV_AGAIN = 1ull << (63 - 11), + HMER_WARN_RISE = 1ull << (63 - 14), + HMER_WARN_FALL = 1ull << (63 - 15), + HMER_SCOM_FIR_HMI = 1ull << (63 - 16), + HMER_TRIG_FIR_HMI = 1ull << (63 - 17), + HMER_HYP_RESOURCE_ERR = 1ull << (63 - 20), + HMER_XSCOM_STATUS_MASK = 7ull << (63 - 23), + HMER_XSCOM_STATUS_LSH = (63 - 23), +}; + +/* Alternate Interrupt Location (AIL) */ +enum { + AIL_NONE = 0, + AIL_RESERVED = 1, + AIL_0001_8000 = 2, + AIL_C000_0000_0000_4000 = 3, +}; + /*****************************************************************************/ static inline target_ulong cpu_read_xer(CPUPPCState *env) @@ -2304,6 +2415,16 @@ static inline bool msr_is_64bit(CPUPPCState *env, target_ulong msr) return msr & (1ULL << MSR_SF); } +/** + * Check whether register rx is in the range between start and + * start + nregs (as needed by the LSWX and LSWI instructions) + */ +static inline bool lsw_reg_in_range(int start, int nregs, int rx) +{ + return (start + nregs <= 32 && rx >= start && rx < start + nregs) || + (start + nregs > 32 && (rx >= start || rx < start + nregs - 32)); +} + extern void (*cpu_ppc_hypercall)(PowerPCCPU *); #include "exec/exec-all.h" @@ -2328,4 +2449,5 @@ int ppc_get_vcpu_dt_id(PowerPCCPU *cpu); */ PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id); +void ppc_maybe_bswap_register(CPUPPCState *env, uint8_t *mem_buf, int len); #endif /* !defined (__CPU_PPC_H__) */ diff --git a/qemu/target-ppc/dfp_helper.c b/qemu/target-ppc/dfp_helper.c index 49820bf21..db0ede698 100644 --- a/qemu/target-ppc/dfp_helper.c +++ b/qemu/target-ppc/dfp_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" @@ -170,27 +171,6 @@ static void dfp_prepare_decimal128(struct PPC_DFP *dfp, uint64_t *a, } } -#define FP_FX (1ull << FPSCR_FX) -#define FP_FEX (1ull << FPSCR_FEX) -#define FP_OX (1ull << FPSCR_OX) -#define FP_OE (1ull << FPSCR_OE) -#define FP_UX (1ull << FPSCR_UX) -#define FP_UE (1ull << FPSCR_UE) -#define FP_XX (1ull << FPSCR_XX) -#define FP_XE (1ull << FPSCR_XE) -#define FP_ZX (1ull << FPSCR_ZX) -#define FP_ZE (1ull << FPSCR_ZE) -#define FP_VX (1ull << FPSCR_VX) -#define FP_VXSNAN (1ull << FPSCR_VXSNAN) -#define FP_VXISI (1ull << FPSCR_VXISI) -#define FP_VXIMZ (1ull << FPSCR_VXIMZ) -#define FP_VXZDZ (1ull << FPSCR_VXZDZ) -#define FP_VXIDI (1ull << FPSCR_VXIDI) -#define FP_VXVC (1ull << FPSCR_VXVC) -#define FP_VXCVI (1ull << FPSCR_VXCVI) -#define FP_VE (1ull << FPSCR_VE) -#define FP_FI (1ull << FPSCR_FI) - static void dfp_set_FPSCR_flag(struct PPC_DFP *dfp, uint64_t flag, uint64_t enabled) { diff --git a/qemu/target-ppc/excp_helper.c b/qemu/target-ppc/excp_helper.c index b80347506..ca4ffe8ad 100644 --- a/qemu/target-ppc/excp_helper.c +++ b/qemu/target-ppc/excp_helper.c @@ -16,6 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" @@ -23,6 +24,7 @@ #include "helper_regs.h" //#define DEBUG_OP +//#define DEBUG_SOFTWARE_TLB //#define DEBUG_EXCEPTIONS #ifdef DEBUG_EXCEPTIONS @@ -75,7 +77,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) CPUPPCState *env = &cpu->env; target_ulong msr, new_msr, vector; int srr0, srr1, asrr0, asrr1; - int lpes0, lpes1, lev; + int lpes0, lpes1, lev, ail; if (0) { /* XXX: find a suitable condition to enable the hypervisor mode */ @@ -106,6 +108,25 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) asrr0 = -1; asrr1 = -1; + /* Exception targetting modifiers + * + * AIL is initialized here but can be cleared by + * selected exceptions + */ +#if defined(TARGET_PPC64) + if (excp_model == POWERPC_EXCP_POWER7 || + excp_model == POWERPC_EXCP_POWER8) { + if (excp_model == POWERPC_EXCP_POWER8) { + ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT; + } else { + ail = 0; + } + } else +#endif /* defined(TARGET_PPC64) */ + { + ail = 0; + } + switch (excp) { case POWERPC_EXCP_NONE: /* Should never happen */ @@ -131,12 +152,11 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) /* Machine check exception is not enabled. * Enter checkstop state. */ - if (qemu_log_enabled()) { + fprintf(stderr, "Machine check while not allowed. " + "Entering checkstop state\n"); + if (qemu_log_separate()) { qemu_log("Machine check while not allowed. " "Entering checkstop state\n"); - } else { - fprintf(stderr, "Machine check while not allowed. " - "Entering checkstop state\n"); } cs->halted = 1; cs->interrupt_request |= CPU_INTERRUPT_EXITTB; @@ -145,6 +165,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) /* XXX: find a suitable condition to enable the hypervisor mode */ new_msr |= (target_ulong)MSR_HVB; } + ail = 0; /* machine check exceptions don't have ME set */ new_msr &= ~((target_ulong)1 << MSR_ME); @@ -200,7 +221,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) /* Get rS/rD and rA from faulting opcode */ env->spr[SPR_DSISR] |= (cpu_ldl_code(env, (env->nip - 4)) & 0x03FF0000) >> 16; - goto store_current; + goto store_next; case POWERPC_EXCP_PROGRAM: /* Program exception */ switch (env->error_code & ~0xF) { case POWERPC_EXCP_FP: @@ -343,6 +364,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) /* XXX: find a suitable condition to enable the hypervisor mode */ new_msr |= (target_ulong)MSR_HVB; } + ail = 0; goto store_next; case POWERPC_EXCP_DSEG: /* Data segment exception */ if (lpes1 == 0) { @@ -629,7 +651,8 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) } #ifdef TARGET_PPC64 - if (excp_model == POWERPC_EXCP_POWER7) { + if (excp_model == POWERPC_EXCP_POWER7 || + excp_model == POWERPC_EXCP_POWER8) { if (env->spr[SPR_LPCR] & LPCR_ILE) { new_msr |= (target_ulong)1 << MSR_LE; } @@ -649,6 +672,29 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) excp); } vector |= env->excp_prefix; + + /* AIL only works if there is no HV transition and we are running with + * translations enabled + */ + if (!((msr >> MSR_IR) & 1) || !((msr >> MSR_DR) & 1)) { + ail = 0; + } + /* Handle AIL */ + if (ail) { + new_msr |= (1 << MSR_IR) | (1 << MSR_DR); + switch(ail) { + case AIL_0001_8000: + vector |= 0x18000; + break; + case AIL_C000_0000_0000_4000: + vector |= 0xc000000000004000ull; + break; + default: + cpu_abort(cs, "Invalid AIL combination %d\n", ail); + break; + } + } + #if defined(TARGET_PPC64) if (excp_model == POWERPC_EXCP_BOOKE) { if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { diff --git a/qemu/target-ppc/fpu_helper.c b/qemu/target-ppc/fpu_helper.c index 6cceffc55..b67ebca12 100644 --- a/qemu/target-ppc/fpu_helper.c +++ b/qemu/target-ppc/fpu_helper.c @@ -16,6 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" @@ -194,7 +195,7 @@ static inline uint64_t fload_invalid_op_excp(CPUPPCState *env, int op, /* Update the floating-point invalid operation summary */ env->fpscr |= 1 << FPSCR_VX; /* Update the floating-point exception summary */ - env->fpscr |= 1 << FPSCR_FX; + env->fpscr |= FP_FX; if (ve != 0) { /* Update the floating-point enabled exception summary */ env->fpscr |= 1 << FPSCR_FEX; @@ -211,7 +212,7 @@ static inline void float_zero_divide_excp(CPUPPCState *env) env->fpscr |= 1 << FPSCR_ZX; env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI)); /* Update the floating-point exception summary */ - env->fpscr |= 1 << FPSCR_FX; + env->fpscr |= FP_FX; if (fpscr_ze != 0) { /* Update the floating-point enabled exception summary */ env->fpscr |= 1 << FPSCR_FEX; @@ -228,7 +229,7 @@ static inline void float_overflow_excp(CPUPPCState *env) env->fpscr |= 1 << FPSCR_OX; /* Update the floating-point exception summary */ - env->fpscr |= 1 << FPSCR_FX; + env->fpscr |= FP_FX; if (fpscr_oe != 0) { /* XXX: should adjust the result */ /* Update the floating-point enabled exception summary */ @@ -248,7 +249,7 @@ static inline void float_underflow_excp(CPUPPCState *env) env->fpscr |= 1 << FPSCR_UX; /* Update the floating-point exception summary */ - env->fpscr |= 1 << FPSCR_FX; + env->fpscr |= FP_FX; if (fpscr_ue != 0) { /* XXX: should adjust the result */ /* Update the floating-point enabled exception summary */ @@ -265,7 +266,7 @@ static inline void float_inexact_excp(CPUPPCState *env) env->fpscr |= 1 << FPSCR_XX; /* Update the floating-point exception summary */ - env->fpscr |= 1 << FPSCR_FX; + env->fpscr |= FP_FX; if (fpscr_xe != 0) { /* Update the floating-point enabled exception summary */ env->fpscr |= 1 << FPSCR_FEX; @@ -330,31 +331,31 @@ void helper_fpscr_setbit(CPUPPCState *env, uint32_t bit) if (prev == 0) { switch (bit) { case FPSCR_VX: - env->fpscr |= 1 << FPSCR_FX; + env->fpscr |= FP_FX; if (fpscr_ve) { goto raise_ve; } break; case FPSCR_OX: - env->fpscr |= 1 << FPSCR_FX; + env->fpscr |= FP_FX; if (fpscr_oe) { goto raise_oe; } break; case FPSCR_UX: - env->fpscr |= 1 << FPSCR_FX; + env->fpscr |= FP_FX; if (fpscr_ue) { goto raise_ue; } break; case FPSCR_ZX: - env->fpscr |= 1 << FPSCR_FX; + env->fpscr |= FP_FX; if (fpscr_ze) { goto raise_ze; } break; case FPSCR_XX: - env->fpscr |= 1 << FPSCR_FX; + env->fpscr |= FP_FX; if (fpscr_xe) { goto raise_xe; } @@ -369,7 +370,7 @@ void helper_fpscr_setbit(CPUPPCState *env, uint32_t bit) case FPSCR_VXSQRT: case FPSCR_VXCVI: env->fpscr |= 1 << FPSCR_VX; - env->fpscr |= 1 << FPSCR_FX; + env->fpscr |= FP_FX; if (fpscr_ve != 0) { goto raise_ve; } diff --git a/qemu/target-ppc/gdbstub.c b/qemu/target-ppc/gdbstub.c index 14675f456..569c380cf 100644 --- a/qemu/target-ppc/gdbstub.c +++ b/qemu/target-ppc/gdbstub.c @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "exec/gdbstub.h" @@ -88,7 +88,7 @@ static int ppc_gdb_register_len(int n) the proper ordering for the binary, and cannot be changed. For system mode, TARGET_WORDS_BIGENDIAN is always set, and we must check the current mode of the chip to see if we're running in little-endian. */ -static void maybe_bswap_register(CPUPPCState *env, uint8_t *mem_buf, int len) +void ppc_maybe_bswap_register(CPUPPCState *env, uint8_t *mem_buf, int len) { #ifndef CONFIG_USER_ONLY if (!msr_le) { @@ -158,7 +158,7 @@ int ppc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) break; } } - maybe_bswap_register(env, mem_buf, r); + ppc_maybe_bswap_register(env, mem_buf, r); return r; } @@ -214,7 +214,7 @@ int ppc_cpu_gdb_read_register_apple(CPUState *cs, uint8_t *mem_buf, int n) break; } } - maybe_bswap_register(env, mem_buf, r); + ppc_maybe_bswap_register(env, mem_buf, r); return r; } @@ -227,7 +227,7 @@ int ppc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) if (!r) { return r; } - maybe_bswap_register(env, mem_buf, r); + ppc_maybe_bswap_register(env, mem_buf, r); if (n < 32) { /* gprs */ env->gpr[n] = ldtul_p(mem_buf); @@ -277,7 +277,7 @@ int ppc_cpu_gdb_write_register_apple(CPUState *cs, uint8_t *mem_buf, int n) if (!r) { return r; } - maybe_bswap_register(env, mem_buf, r); + ppc_maybe_bswap_register(env, mem_buf, r); if (n < 32) { /* gprs */ env->gpr[n] = ldq_p(mem_buf); diff --git a/qemu/target-ppc/helper.h b/qemu/target-ppc/helper.h index 869be1509..e5a8f7b9b 100644 --- a/qemu/target-ppc/helper.h +++ b/qemu/target-ppc/helper.h @@ -544,6 +544,7 @@ DEF_HELPER_2(74xx_tlbd, void, env, tl) DEF_HELPER_2(74xx_tlbi, void, env, tl) DEF_HELPER_FLAGS_1(tlbia, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_2(tlbie, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_2(tlbiva, TCG_CALL_NO_RWG, void, env, tl) #if defined(TARGET_PPC64) DEF_HELPER_FLAGS_3(store_slb, TCG_CALL_NO_RWG, void, env, tl, tl) DEF_HELPER_2(load_slb_esid, tl, env, tl) diff --git a/qemu/target-ppc/int_helper.c b/qemu/target-ppc/int_helper.c index 0a55d5e54..27b0258d3 100644 --- a/qemu/target-ppc/int_helper.c +++ b/qemu/target-ppc/int_helper.c @@ -16,6 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" @@ -2327,24 +2328,28 @@ void helper_vsbox(ppc_avr_t *r, ppc_avr_t *a) void helper_vcipher(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { + ppc_avr_t result; int i; VECTOR_FOR_INORDER_I(i, u32) { - r->AVRW(i) = b->AVRW(i) ^ + result.AVRW(i) = b->AVRW(i) ^ (AES_Te0[a->AVRB(AES_shifts[4*i + 0])] ^ AES_Te1[a->AVRB(AES_shifts[4*i + 1])] ^ AES_Te2[a->AVRB(AES_shifts[4*i + 2])] ^ AES_Te3[a->AVRB(AES_shifts[4*i + 3])]); } + *r = result; } void helper_vcipherlast(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { + ppc_avr_t result; int i; VECTOR_FOR_INORDER_I(i, u8) { - r->AVRB(i) = b->AVRB(i) ^ (AES_sbox[a->AVRB(AES_shifts[i])]); + result.AVRB(i) = b->AVRB(i) ^ (AES_sbox[a->AVRB(AES_shifts[i])]); } + *r = result; } void helper_vncipher(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) @@ -2369,11 +2374,13 @@ void helper_vncipher(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) void helper_vncipherlast(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { + ppc_avr_t result; int i; VECTOR_FOR_INORDER_I(i, u8) { - r->AVRB(i) = b->AVRB(i) ^ (AES_isbox[a->AVRB(AES_ishifts[i])]); + result.AVRB(i) = b->AVRB(i) ^ (AES_isbox[a->AVRB(AES_ishifts[i])]); } + *r = result; } #define ROTRu32(v, n) (((v) >> (n)) | ((v) << (32-n))) @@ -2460,16 +2467,19 @@ void helper_vshasigmad(ppc_avr_t *r, ppc_avr_t *a, uint32_t st_six) void helper_vpermxor(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) { + ppc_avr_t result; int i; + VECTOR_FOR_INORDER_I(i, u8) { int indexA = c->u8[i] >> 4; int indexB = c->u8[i] & 0xF; #if defined(HOST_WORDS_BIGENDIAN) - r->u8[i] = a->u8[indexA] ^ b->u8[indexB]; + result.u8[i] = a->u8[indexA] ^ b->u8[indexB]; #else - r->u8[i] = a->u8[15-indexA] ^ b->u8[15-indexB]; + result.u8[i] = a->u8[15-indexA] ^ b->u8[15-indexB]; #endif } + *r = result; } #undef VECTOR_FOR_INORDER_I diff --git a/qemu/target-ppc/kvm-stub.c b/qemu/target-ppc/kvm-stub.c index ee3f5d2f7..627bcb432 100644 --- a/qemu/target-ppc/kvm-stub.c +++ b/qemu/target-ppc/kvm-stub.c @@ -9,6 +9,7 @@ * See the COPYING file in the top-level directory. * */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/ppc/openpic.h" diff --git a/qemu/target-ppc/kvm.c b/qemu/target-ppc/kvm.c index 110436d08..c4c81467e 100644 --- a/qemu/target-ppc/kvm.c +++ b/qemu/target-ppc/kvm.c @@ -14,8 +14,8 @@ * */ +#include "qemu/osdep.h" #include -#include #include #include #include @@ -23,6 +23,7 @@ #include #include "qemu-common.h" +#include "qemu/error-report.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" @@ -41,6 +42,7 @@ #include "exec/gdbstub.h" #include "exec/memattrs.h" #include "sysemu/hostmem.h" +#include "qemu/cutils.h" //#define DEBUG_KVM @@ -259,7 +261,8 @@ static void kvm_get_fallback_smmu_info(PowerPCCPU *cpu, info->flags |= KVM_PPC_1T_SEGMENTS; } - if (env->mmu_model == POWERPC_MMU_2_06) { + if (env->mmu_model == POWERPC_MMU_2_06 || + env->mmu_model == POWERPC_MMU_2_07) { info->slb_size = 32; } else { info->slb_size = 64; @@ -272,8 +275,9 @@ static void kvm_get_fallback_smmu_info(PowerPCCPU *cpu, info->sps[i].enc[0].pte_enc = 0; i++; - /* 64K on MMU 2.06 */ - if (env->mmu_model == POWERPC_MMU_2_06) { + /* 64K on MMU 2.06 and later */ + if (env->mmu_model == POWERPC_MMU_2_06 || + env->mmu_model == POWERPC_MMU_2_07) { info->sps[i].page_shift = 16; info->sps[i].slb_enc = 0x110; info->sps[i].enc[0].page_shift = 16; @@ -330,6 +334,12 @@ static long gethugepagesize(const char *mem_path) return fs.f_bsize; } +/* + * FIXME TOCTTOU: this iterates over memory backends' mem-path, which + * may or may not name the same files / on the same filesystem now as + * when we actually open and map them. Iterate over the file + * descriptors instead, and use qemu_fd_getpagesize(). + */ static int find_max_supported_pagesize(Object *obj, void *opaque) { char *mem_path; @@ -412,6 +422,13 @@ static void kvm_fixup_page_sizes(PowerPCCPU *cpu) /* Convert to QEMU form */ memset(&env->sps, 0, sizeof(env->sps)); + /* If we have HV KVM, we need to forbid CI large pages if our + * host page size is smaller than 64K. + */ + if (smmu_info.flags & KVM_PPC_PAGE_SIZES_REAL) { + env->ci_large_pages = getpagesize() >= 0x10000; + } + /* * XXX This loop should be an entry wide AND of the capabilities that * the selected CPU has with the capabilities that KVM supports. @@ -503,6 +520,10 @@ int kvm_arch_init_vcpu(CPUState *cs) /* Synchronize sregs with kvm */ ret = kvm_arch_sync_sregs(cpu); if (ret) { + if (ret == -EINVAL) { + error_report("Register sync failed... If you're using kvm-hv.ko," + " only \"-cpu host\" is possible"); + } return ret; } @@ -641,8 +662,13 @@ static int kvm_put_fp(CPUState *cs) for (i = 0; i < 32; i++) { uint64_t vsr[2]; +#ifdef HOST_WORDS_BIGENDIAN vsr[0] = float64_val(env->fpr[i]); vsr[1] = env->vsr[i]; +#else + vsr[0] = env->vsr[i]; + vsr[1] = float64_val(env->fpr[i]); +#endif reg.addr = (uintptr_t) &vsr; reg.id = vsx ? KVM_REG_PPC_VSR(i) : KVM_REG_PPC_FPR(i); @@ -712,10 +738,17 @@ static int kvm_get_fp(CPUState *cs) vsx ? "VSR" : "FPR", i, strerror(errno)); return ret; } else { +#ifdef HOST_WORDS_BIGENDIAN env->fpr[i] = vsr[0]; if (vsx) { env->vsr[i] = vsr[1]; } +#else + env->fpr[i] = vsr[1]; + if (vsx) { + env->vsr[i] = vsr[0]; + } +#endif } } } @@ -841,6 +874,44 @@ static int kvm_put_vpa(CPUState *cs) } #endif /* TARGET_PPC64 */ +int kvmppc_put_books_sregs(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + struct kvm_sregs sregs; + int i; + + sregs.pvr = env->spr[SPR_PVR]; + + sregs.u.s.sdr1 = env->spr[SPR_SDR1]; + + /* Sync SLB */ +#ifdef TARGET_PPC64 + for (i = 0; i < ARRAY_SIZE(env->slb); i++) { + sregs.u.s.ppc64.slb[i].slbe = env->slb[i].esid; + if (env->slb[i].esid & SLB_ESID_V) { + sregs.u.s.ppc64.slb[i].slbe |= i; + } + sregs.u.s.ppc64.slb[i].slbv = env->slb[i].vsid; + } +#endif + + /* Sync SRs */ + for (i = 0; i < 16; i++) { + sregs.u.s.ppc32.sr[i] = env->sr[i]; + } + + /* Sync BATs */ + for (i = 0; i < 8; i++) { + /* Beware. We have to swap upper and lower bits here */ + sregs.u.s.ppc32.dbat[i] = ((uint64_t)env->DBAT[0][i] << 32) + | env->DBAT[1][i]; + sregs.u.s.ppc32.ibat[i] = ((uint64_t)env->IBAT[0][i] << 32) + | env->IBAT[1][i]; + } + + return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_SREGS, &sregs); +} + int kvm_arch_put_registers(CPUState *cs, int level) { PowerPCCPU *cpu = POWERPC_CPU(cs); @@ -894,39 +965,8 @@ int kvm_arch_put_registers(CPUState *cs, int level) } if (cap_segstate && (level >= KVM_PUT_RESET_STATE)) { - struct kvm_sregs sregs; - - sregs.pvr = env->spr[SPR_PVR]; - - sregs.u.s.sdr1 = env->spr[SPR_SDR1]; - - /* Sync SLB */ -#ifdef TARGET_PPC64 - for (i = 0; i < ARRAY_SIZE(env->slb); i++) { - sregs.u.s.ppc64.slb[i].slbe = env->slb[i].esid; - if (env->slb[i].esid & SLB_ESID_V) { - sregs.u.s.ppc64.slb[i].slbe |= i; - } - sregs.u.s.ppc64.slb[i].slbv = env->slb[i].vsid; - } -#endif - - /* Sync SRs */ - for (i = 0; i < 16; i++) { - sregs.u.s.ppc32.sr[i] = env->sr[i]; - } - - /* Sync BATs */ - for (i = 0; i < 8; i++) { - /* Beware. We have to swap upper and lower bits here */ - sregs.u.s.ppc32.dbat[i] = ((uint64_t)env->DBAT[0][i] << 32) - | env->DBAT[1][i]; - sregs.u.s.ppc32.ibat[i] = ((uint64_t)env->IBAT[0][i] << 32) - | env->IBAT[1][i]; - } - - ret = kvm_vcpu_ioctl(cs, KVM_SET_SREGS, &sregs); - if (ret) { + ret = kvmppc_put_books_sregs(cpu); + if (ret < 0) { return ret; } } @@ -988,12 +1028,197 @@ static void kvm_sync_excp(CPUPPCState *env, int vector, int ivor) env->excp_vectors[vector] = env->spr[ivor] + env->spr[SPR_BOOKE_IVPR]; } +static int kvmppc_get_booke_sregs(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + struct kvm_sregs sregs; + int ret; + + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_SREGS, &sregs); + if (ret < 0) { + return ret; + } + + if (sregs.u.e.features & KVM_SREGS_E_BASE) { + env->spr[SPR_BOOKE_CSRR0] = sregs.u.e.csrr0; + env->spr[SPR_BOOKE_CSRR1] = sregs.u.e.csrr1; + env->spr[SPR_BOOKE_ESR] = sregs.u.e.esr; + env->spr[SPR_BOOKE_DEAR] = sregs.u.e.dear; + env->spr[SPR_BOOKE_MCSR] = sregs.u.e.mcsr; + env->spr[SPR_BOOKE_TSR] = sregs.u.e.tsr; + env->spr[SPR_BOOKE_TCR] = sregs.u.e.tcr; + env->spr[SPR_DECR] = sregs.u.e.dec; + env->spr[SPR_TBL] = sregs.u.e.tb & 0xffffffff; + env->spr[SPR_TBU] = sregs.u.e.tb >> 32; + env->spr[SPR_VRSAVE] = sregs.u.e.vrsave; + } + + if (sregs.u.e.features & KVM_SREGS_E_ARCH206) { + env->spr[SPR_BOOKE_PIR] = sregs.u.e.pir; + env->spr[SPR_BOOKE_MCSRR0] = sregs.u.e.mcsrr0; + env->spr[SPR_BOOKE_MCSRR1] = sregs.u.e.mcsrr1; + env->spr[SPR_BOOKE_DECAR] = sregs.u.e.decar; + env->spr[SPR_BOOKE_IVPR] = sregs.u.e.ivpr; + } + + if (sregs.u.e.features & KVM_SREGS_E_64) { + env->spr[SPR_BOOKE_EPCR] = sregs.u.e.epcr; + } + + if (sregs.u.e.features & KVM_SREGS_E_SPRG8) { + env->spr[SPR_BOOKE_SPRG8] = sregs.u.e.sprg8; + } + + if (sregs.u.e.features & KVM_SREGS_E_IVOR) { + env->spr[SPR_BOOKE_IVOR0] = sregs.u.e.ivor_low[0]; + kvm_sync_excp(env, POWERPC_EXCP_CRITICAL, SPR_BOOKE_IVOR0); + env->spr[SPR_BOOKE_IVOR1] = sregs.u.e.ivor_low[1]; + kvm_sync_excp(env, POWERPC_EXCP_MCHECK, SPR_BOOKE_IVOR1); + env->spr[SPR_BOOKE_IVOR2] = sregs.u.e.ivor_low[2]; + kvm_sync_excp(env, POWERPC_EXCP_DSI, SPR_BOOKE_IVOR2); + env->spr[SPR_BOOKE_IVOR3] = sregs.u.e.ivor_low[3]; + kvm_sync_excp(env, POWERPC_EXCP_ISI, SPR_BOOKE_IVOR3); + env->spr[SPR_BOOKE_IVOR4] = sregs.u.e.ivor_low[4]; + kvm_sync_excp(env, POWERPC_EXCP_EXTERNAL, SPR_BOOKE_IVOR4); + env->spr[SPR_BOOKE_IVOR5] = sregs.u.e.ivor_low[5]; + kvm_sync_excp(env, POWERPC_EXCP_ALIGN, SPR_BOOKE_IVOR5); + env->spr[SPR_BOOKE_IVOR6] = sregs.u.e.ivor_low[6]; + kvm_sync_excp(env, POWERPC_EXCP_PROGRAM, SPR_BOOKE_IVOR6); + env->spr[SPR_BOOKE_IVOR7] = sregs.u.e.ivor_low[7]; + kvm_sync_excp(env, POWERPC_EXCP_FPU, SPR_BOOKE_IVOR7); + env->spr[SPR_BOOKE_IVOR8] = sregs.u.e.ivor_low[8]; + kvm_sync_excp(env, POWERPC_EXCP_SYSCALL, SPR_BOOKE_IVOR8); + env->spr[SPR_BOOKE_IVOR9] = sregs.u.e.ivor_low[9]; + kvm_sync_excp(env, POWERPC_EXCP_APU, SPR_BOOKE_IVOR9); + env->spr[SPR_BOOKE_IVOR10] = sregs.u.e.ivor_low[10]; + kvm_sync_excp(env, POWERPC_EXCP_DECR, SPR_BOOKE_IVOR10); + env->spr[SPR_BOOKE_IVOR11] = sregs.u.e.ivor_low[11]; + kvm_sync_excp(env, POWERPC_EXCP_FIT, SPR_BOOKE_IVOR11); + env->spr[SPR_BOOKE_IVOR12] = sregs.u.e.ivor_low[12]; + kvm_sync_excp(env, POWERPC_EXCP_WDT, SPR_BOOKE_IVOR12); + env->spr[SPR_BOOKE_IVOR13] = sregs.u.e.ivor_low[13]; + kvm_sync_excp(env, POWERPC_EXCP_DTLB, SPR_BOOKE_IVOR13); + env->spr[SPR_BOOKE_IVOR14] = sregs.u.e.ivor_low[14]; + kvm_sync_excp(env, POWERPC_EXCP_ITLB, SPR_BOOKE_IVOR14); + env->spr[SPR_BOOKE_IVOR15] = sregs.u.e.ivor_low[15]; + kvm_sync_excp(env, POWERPC_EXCP_DEBUG, SPR_BOOKE_IVOR15); + + if (sregs.u.e.features & KVM_SREGS_E_SPE) { + env->spr[SPR_BOOKE_IVOR32] = sregs.u.e.ivor_high[0]; + kvm_sync_excp(env, POWERPC_EXCP_SPEU, SPR_BOOKE_IVOR32); + env->spr[SPR_BOOKE_IVOR33] = sregs.u.e.ivor_high[1]; + kvm_sync_excp(env, POWERPC_EXCP_EFPDI, SPR_BOOKE_IVOR33); + env->spr[SPR_BOOKE_IVOR34] = sregs.u.e.ivor_high[2]; + kvm_sync_excp(env, POWERPC_EXCP_EFPRI, SPR_BOOKE_IVOR34); + } + + if (sregs.u.e.features & KVM_SREGS_E_PM) { + env->spr[SPR_BOOKE_IVOR35] = sregs.u.e.ivor_high[3]; + kvm_sync_excp(env, POWERPC_EXCP_EPERFM, SPR_BOOKE_IVOR35); + } + + if (sregs.u.e.features & KVM_SREGS_E_PC) { + env->spr[SPR_BOOKE_IVOR36] = sregs.u.e.ivor_high[4]; + kvm_sync_excp(env, POWERPC_EXCP_DOORI, SPR_BOOKE_IVOR36); + env->spr[SPR_BOOKE_IVOR37] = sregs.u.e.ivor_high[5]; + kvm_sync_excp(env, POWERPC_EXCP_DOORCI, SPR_BOOKE_IVOR37); + } + } + + if (sregs.u.e.features & KVM_SREGS_E_ARCH206_MMU) { + env->spr[SPR_BOOKE_MAS0] = sregs.u.e.mas0; + env->spr[SPR_BOOKE_MAS1] = sregs.u.e.mas1; + env->spr[SPR_BOOKE_MAS2] = sregs.u.e.mas2; + env->spr[SPR_BOOKE_MAS3] = sregs.u.e.mas7_3 & 0xffffffff; + env->spr[SPR_BOOKE_MAS4] = sregs.u.e.mas4; + env->spr[SPR_BOOKE_MAS6] = sregs.u.e.mas6; + env->spr[SPR_BOOKE_MAS7] = sregs.u.e.mas7_3 >> 32; + env->spr[SPR_MMUCFG] = sregs.u.e.mmucfg; + env->spr[SPR_BOOKE_TLB0CFG] = sregs.u.e.tlbcfg[0]; + env->spr[SPR_BOOKE_TLB1CFG] = sregs.u.e.tlbcfg[1]; + } + + if (sregs.u.e.features & KVM_SREGS_EXP) { + env->spr[SPR_BOOKE_EPR] = sregs.u.e.epr; + } + + if (sregs.u.e.features & KVM_SREGS_E_PD) { + env->spr[SPR_BOOKE_EPLC] = sregs.u.e.eplc; + env->spr[SPR_BOOKE_EPSC] = sregs.u.e.epsc; + } + + if (sregs.u.e.impl_id == KVM_SREGS_E_IMPL_FSL) { + env->spr[SPR_E500_SVR] = sregs.u.e.impl.fsl.svr; + env->spr[SPR_Exxx_MCAR] = sregs.u.e.impl.fsl.mcar; + env->spr[SPR_HID0] = sregs.u.e.impl.fsl.hid0; + + if (sregs.u.e.impl.fsl.features & KVM_SREGS_E_FSL_PIDn) { + env->spr[SPR_BOOKE_PID1] = sregs.u.e.impl.fsl.pid1; + env->spr[SPR_BOOKE_PID2] = sregs.u.e.impl.fsl.pid2; + } + } + + return 0; +} + +static int kvmppc_get_books_sregs(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + struct kvm_sregs sregs; + int ret; + int i; + + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_SREGS, &sregs); + if (ret < 0) { + return ret; + } + + if (!env->external_htab) { + ppc_store_sdr1(env, sregs.u.s.sdr1); + } + + /* Sync SLB */ +#ifdef TARGET_PPC64 + /* + * The packed SLB array we get from KVM_GET_SREGS only contains + * information about valid entries. So we flush our internal copy + * to get rid of stale ones, then put all valid SLB entries back + * in. + */ + memset(env->slb, 0, sizeof(env->slb)); + for (i = 0; i < ARRAY_SIZE(env->slb); i++) { + target_ulong rb = sregs.u.s.ppc64.slb[i].slbe; + target_ulong rs = sregs.u.s.ppc64.slb[i].slbv; + /* + * Only restore valid entries + */ + if (rb & SLB_ESID_V) { + ppc_store_slb(cpu, rb & 0xfff, rb & ~0xfffULL, rs); + } + } +#endif + + /* Sync SRs */ + for (i = 0; i < 16; i++) { + env->sr[i] = sregs.u.s.ppc32.sr[i]; + } + + /* Sync BATs */ + for (i = 0; i < 8; i++) { + env->DBAT[0][i] = sregs.u.s.ppc32.dbat[i] & 0xffffffff; + env->DBAT[1][i] = sregs.u.s.ppc32.dbat[i] >> 32; + env->IBAT[0][i] = sregs.u.s.ppc32.ibat[i] & 0xffffffff; + env->IBAT[1][i] = sregs.u.s.ppc32.ibat[i] >> 32; + } + + return 0; +} + int kvm_arch_get_registers(CPUState *cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; struct kvm_regs regs; - struct kvm_sregs sregs; uint32_t cr; int i, ret; @@ -1033,174 +1258,17 @@ int kvm_arch_get_registers(CPUState *cs) kvm_get_fp(cs); if (cap_booke_sregs) { - ret = kvm_vcpu_ioctl(cs, KVM_GET_SREGS, &sregs); + ret = kvmppc_get_booke_sregs(cpu); if (ret < 0) { return ret; } - - if (sregs.u.e.features & KVM_SREGS_E_BASE) { - env->spr[SPR_BOOKE_CSRR0] = sregs.u.e.csrr0; - env->spr[SPR_BOOKE_CSRR1] = sregs.u.e.csrr1; - env->spr[SPR_BOOKE_ESR] = sregs.u.e.esr; - env->spr[SPR_BOOKE_DEAR] = sregs.u.e.dear; - env->spr[SPR_BOOKE_MCSR] = sregs.u.e.mcsr; - env->spr[SPR_BOOKE_TSR] = sregs.u.e.tsr; - env->spr[SPR_BOOKE_TCR] = sregs.u.e.tcr; - env->spr[SPR_DECR] = sregs.u.e.dec; - env->spr[SPR_TBL] = sregs.u.e.tb & 0xffffffff; - env->spr[SPR_TBU] = sregs.u.e.tb >> 32; - env->spr[SPR_VRSAVE] = sregs.u.e.vrsave; - } - - if (sregs.u.e.features & KVM_SREGS_E_ARCH206) { - env->spr[SPR_BOOKE_PIR] = sregs.u.e.pir; - env->spr[SPR_BOOKE_MCSRR0] = sregs.u.e.mcsrr0; - env->spr[SPR_BOOKE_MCSRR1] = sregs.u.e.mcsrr1; - env->spr[SPR_BOOKE_DECAR] = sregs.u.e.decar; - env->spr[SPR_BOOKE_IVPR] = sregs.u.e.ivpr; - } - - if (sregs.u.e.features & KVM_SREGS_E_64) { - env->spr[SPR_BOOKE_EPCR] = sregs.u.e.epcr; - } - - if (sregs.u.e.features & KVM_SREGS_E_SPRG8) { - env->spr[SPR_BOOKE_SPRG8] = sregs.u.e.sprg8; - } - - if (sregs.u.e.features & KVM_SREGS_E_IVOR) { - env->spr[SPR_BOOKE_IVOR0] = sregs.u.e.ivor_low[0]; - kvm_sync_excp(env, POWERPC_EXCP_CRITICAL, SPR_BOOKE_IVOR0); - env->spr[SPR_BOOKE_IVOR1] = sregs.u.e.ivor_low[1]; - kvm_sync_excp(env, POWERPC_EXCP_MCHECK, SPR_BOOKE_IVOR1); - env->spr[SPR_BOOKE_IVOR2] = sregs.u.e.ivor_low[2]; - kvm_sync_excp(env, POWERPC_EXCP_DSI, SPR_BOOKE_IVOR2); - env->spr[SPR_BOOKE_IVOR3] = sregs.u.e.ivor_low[3]; - kvm_sync_excp(env, POWERPC_EXCP_ISI, SPR_BOOKE_IVOR3); - env->spr[SPR_BOOKE_IVOR4] = sregs.u.e.ivor_low[4]; - kvm_sync_excp(env, POWERPC_EXCP_EXTERNAL, SPR_BOOKE_IVOR4); - env->spr[SPR_BOOKE_IVOR5] = sregs.u.e.ivor_low[5]; - kvm_sync_excp(env, POWERPC_EXCP_ALIGN, SPR_BOOKE_IVOR5); - env->spr[SPR_BOOKE_IVOR6] = sregs.u.e.ivor_low[6]; - kvm_sync_excp(env, POWERPC_EXCP_PROGRAM, SPR_BOOKE_IVOR6); - env->spr[SPR_BOOKE_IVOR7] = sregs.u.e.ivor_low[7]; - kvm_sync_excp(env, POWERPC_EXCP_FPU, SPR_BOOKE_IVOR7); - env->spr[SPR_BOOKE_IVOR8] = sregs.u.e.ivor_low[8]; - kvm_sync_excp(env, POWERPC_EXCP_SYSCALL, SPR_BOOKE_IVOR8); - env->spr[SPR_BOOKE_IVOR9] = sregs.u.e.ivor_low[9]; - kvm_sync_excp(env, POWERPC_EXCP_APU, SPR_BOOKE_IVOR9); - env->spr[SPR_BOOKE_IVOR10] = sregs.u.e.ivor_low[10]; - kvm_sync_excp(env, POWERPC_EXCP_DECR, SPR_BOOKE_IVOR10); - env->spr[SPR_BOOKE_IVOR11] = sregs.u.e.ivor_low[11]; - kvm_sync_excp(env, POWERPC_EXCP_FIT, SPR_BOOKE_IVOR11); - env->spr[SPR_BOOKE_IVOR12] = sregs.u.e.ivor_low[12]; - kvm_sync_excp(env, POWERPC_EXCP_WDT, SPR_BOOKE_IVOR12); - env->spr[SPR_BOOKE_IVOR13] = sregs.u.e.ivor_low[13]; - kvm_sync_excp(env, POWERPC_EXCP_DTLB, SPR_BOOKE_IVOR13); - env->spr[SPR_BOOKE_IVOR14] = sregs.u.e.ivor_low[14]; - kvm_sync_excp(env, POWERPC_EXCP_ITLB, SPR_BOOKE_IVOR14); - env->spr[SPR_BOOKE_IVOR15] = sregs.u.e.ivor_low[15]; - kvm_sync_excp(env, POWERPC_EXCP_DEBUG, SPR_BOOKE_IVOR15); - - if (sregs.u.e.features & KVM_SREGS_E_SPE) { - env->spr[SPR_BOOKE_IVOR32] = sregs.u.e.ivor_high[0]; - kvm_sync_excp(env, POWERPC_EXCP_SPEU, SPR_BOOKE_IVOR32); - env->spr[SPR_BOOKE_IVOR33] = sregs.u.e.ivor_high[1]; - kvm_sync_excp(env, POWERPC_EXCP_EFPDI, SPR_BOOKE_IVOR33); - env->spr[SPR_BOOKE_IVOR34] = sregs.u.e.ivor_high[2]; - kvm_sync_excp(env, POWERPC_EXCP_EFPRI, SPR_BOOKE_IVOR34); - } - - if (sregs.u.e.features & KVM_SREGS_E_PM) { - env->spr[SPR_BOOKE_IVOR35] = sregs.u.e.ivor_high[3]; - kvm_sync_excp(env, POWERPC_EXCP_EPERFM, SPR_BOOKE_IVOR35); - } - - if (sregs.u.e.features & KVM_SREGS_E_PC) { - env->spr[SPR_BOOKE_IVOR36] = sregs.u.e.ivor_high[4]; - kvm_sync_excp(env, POWERPC_EXCP_DOORI, SPR_BOOKE_IVOR36); - env->spr[SPR_BOOKE_IVOR37] = sregs.u.e.ivor_high[5]; - kvm_sync_excp(env, POWERPC_EXCP_DOORCI, SPR_BOOKE_IVOR37); - } - } - - if (sregs.u.e.features & KVM_SREGS_E_ARCH206_MMU) { - env->spr[SPR_BOOKE_MAS0] = sregs.u.e.mas0; - env->spr[SPR_BOOKE_MAS1] = sregs.u.e.mas1; - env->spr[SPR_BOOKE_MAS2] = sregs.u.e.mas2; - env->spr[SPR_BOOKE_MAS3] = sregs.u.e.mas7_3 & 0xffffffff; - env->spr[SPR_BOOKE_MAS4] = sregs.u.e.mas4; - env->spr[SPR_BOOKE_MAS6] = sregs.u.e.mas6; - env->spr[SPR_BOOKE_MAS7] = sregs.u.e.mas7_3 >> 32; - env->spr[SPR_MMUCFG] = sregs.u.e.mmucfg; - env->spr[SPR_BOOKE_TLB0CFG] = sregs.u.e.tlbcfg[0]; - env->spr[SPR_BOOKE_TLB1CFG] = sregs.u.e.tlbcfg[1]; - } - - if (sregs.u.e.features & KVM_SREGS_EXP) { - env->spr[SPR_BOOKE_EPR] = sregs.u.e.epr; - } - - if (sregs.u.e.features & KVM_SREGS_E_PD) { - env->spr[SPR_BOOKE_EPLC] = sregs.u.e.eplc; - env->spr[SPR_BOOKE_EPSC] = sregs.u.e.epsc; - } - - if (sregs.u.e.impl_id == KVM_SREGS_E_IMPL_FSL) { - env->spr[SPR_E500_SVR] = sregs.u.e.impl.fsl.svr; - env->spr[SPR_Exxx_MCAR] = sregs.u.e.impl.fsl.mcar; - env->spr[SPR_HID0] = sregs.u.e.impl.fsl.hid0; - - if (sregs.u.e.impl.fsl.features & KVM_SREGS_E_FSL_PIDn) { - env->spr[SPR_BOOKE_PID1] = sregs.u.e.impl.fsl.pid1; - env->spr[SPR_BOOKE_PID2] = sregs.u.e.impl.fsl.pid2; - } - } } if (cap_segstate) { - ret = kvm_vcpu_ioctl(cs, KVM_GET_SREGS, &sregs); + ret = kvmppc_get_books_sregs(cpu); if (ret < 0) { return ret; } - - if (!env->external_htab) { - ppc_store_sdr1(env, sregs.u.s.sdr1); - } - - /* Sync SLB */ -#ifdef TARGET_PPC64 - /* - * The packed SLB array we get from KVM_GET_SREGS only contains - * information about valid entries. So we flush our internal - * copy to get rid of stale ones, then put all valid SLB entries - * back in. - */ - memset(env->slb, 0, sizeof(env->slb)); - for (i = 0; i < ARRAY_SIZE(env->slb); i++) { - target_ulong rb = sregs.u.s.ppc64.slb[i].slbe; - target_ulong rs = sregs.u.s.ppc64.slb[i].slbv; - /* - * Only restore valid entries - */ - if (rb & SLB_ESID_V) { - ppc_store_slb(env, rb, rs); - } - } -#endif - - /* Sync SRs */ - for (i = 0; i < 16; i++) { - env->sr[i] = sregs.u.s.ppc32.sr[i]; - } - - /* Sync BATs */ - for (i = 0; i < 8; i++) { - env->DBAT[0][i] = sregs.u.s.ppc32.dbat[i] & 0xffffffff; - env->DBAT[1][i] = sregs.u.s.ppc32.dbat[i] >> 32; - env->IBAT[0][i] = sregs.u.s.ppc32.ibat[i] & 0xffffffff; - env->IBAT[1][i] = sregs.u.s.ppc32.ibat[i] >> 32; - } } if (cap_hior) { @@ -1310,7 +1378,7 @@ void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) /* Always wake up soon in case the interrupt was level based */ timer_mod(idle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (get_ticks_per_sec() / 50)); + (NANOSECONDS_PER_SECOND / 50)); } /* We don't know if there are more interrupts pending after this. However, @@ -1770,7 +1838,7 @@ uint32_t kvmppc_get_tbfreq(void) { char line[512]; char *ns; - uint32_t retval = get_ticks_per_sec(); + uint32_t retval = NANOSECONDS_PER_SECOND; if (read_cpuinfo("timebase", line, sizeof(line))) { return retval; @@ -1782,8 +1850,7 @@ uint32_t kvmppc_get_tbfreq(void) ns++; - retval = atoi(ns); - return retval; + return atoi(ns); } bool kvmppc_get_host_serial(char **value) @@ -1830,13 +1897,8 @@ static int kvmppc_find_cpu_dt(char *buf, int buf_len) return 0; } -/* Read a CPU node property from the host device tree that's a single - * integer (32-bit or 64-bit). Returns 0 if anything goes wrong - * (can't find or open the property, or doesn't understand the - * format) */ -static uint64_t kvmppc_read_int_cpu_dt(const char *propname) +static uint64_t kvmppc_read_int_dt(const char *filename) { - char buf[PATH_MAX], *tmp; union { uint32_t v32; uint64_t v64; @@ -1844,14 +1906,7 @@ static uint64_t kvmppc_read_int_cpu_dt(const char *propname) FILE *f; int len; - if (kvmppc_find_cpu_dt(buf, sizeof(buf))) { - return -1; - } - - tmp = g_strdup_printf("%s/%s", buf, propname); - - f = fopen(tmp, "rb"); - g_free(tmp); + f = fopen(filename, "rb"); if (!f) { return -1; } @@ -1869,6 +1924,26 @@ static uint64_t kvmppc_read_int_cpu_dt(const char *propname) return 0; } +/* Read a CPU node property from the host device tree that's a single + * integer (32-bit or 64-bit). Returns 0 if anything goes wrong + * (can't find or open the property, or doesn't understand the + * format) */ +static uint64_t kvmppc_read_int_cpu_dt(const char *propname) +{ + char buf[PATH_MAX], *tmp; + uint64_t val; + + if (kvmppc_find_cpu_dt(buf, sizeof(buf))) { + return -1; + } + + tmp = g_strdup_printf("%s/%s", buf, propname); + val = kvmppc_read_int_dt(tmp); + g_free(tmp); + + return val; +} + uint64_t kvmppc_get_clockfreq(void) { return kvmppc_read_int_cpu_dt("clock-frequency"); @@ -1933,7 +2008,7 @@ int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_len) hc[2] = cpu_to_be32(0x48000008); hc[3] = cpu_to_be32(bswap32(0x3860ffff)); - return 0; + return 1; } static inline int kvmppc_enable_hcall(KVMState *s, target_ulong hcall) @@ -1953,6 +2028,11 @@ void kvmppc_enable_logical_ci_hcalls(void) kvmppc_enable_hcall(kvm_state, H_LOGICAL_CI_STORE); } +void kvmppc_enable_set_mode_hcall(void) +{ + kvmppc_enable_hcall(kvm_state, H_SET_MODE); +} + void kvmppc_set_papr(PowerPCCPU *cpu) { CPUState *cs = CPU(cpu); @@ -1960,7 +2040,8 @@ void kvmppc_set_papr(PowerPCCPU *cpu) ret = kvm_vcpu_enable_cap(cs, KVM_CAP_PPC_PAPR, 0); if (ret) { - cpu_abort(cs, "This KVM version does not support PAPR\n"); + error_report("This vCPU type or KVM version does not support PAPR"); + exit(1); } /* Update the capability flag so we sync the right information @@ -1980,7 +2061,8 @@ void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy) ret = kvm_vcpu_enable_cap(cs, KVM_CAP_PPC_EPR, 0, mpic_proxy); if (ret && mpic_proxy) { - cpu_abort(cs, "This KVM version does not support EPR\n"); + error_report("This KVM version does not support EPR"); + exit(1); } } @@ -2066,7 +2148,7 @@ bool kvmppc_spapr_use_multitce(void) } void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd, - bool vfio_accel) + bool need_vfio) { struct kvm_create_spapr_tce args = { .liobn = liobn, @@ -2080,7 +2162,7 @@ void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd, * destroying the table, which the upper layers -will- do */ *pfd = -1; - if (!cap_spapr_tce || (vfio_accel && !cap_spapr_vfio)) { + if (!cap_spapr_tce || (need_vfio && !cap_spapr_vfio)) { return NULL; } @@ -2188,6 +2270,7 @@ static void kvmppc_host_cpu_initfn(Object *obj) static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data) { + DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); uint32_t vmx = kvmppc_get_vmx(); uint32_t dfp = kvmppc_get_dfp(); @@ -2214,6 +2297,9 @@ static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data) if (icache_size != -1) { pcc->l1_icache_size = icache_size; } + + /* Reason: kvmppc_host_cpu_initfn() dies when !kvm_enabled() */ + dc->cannot_destroy_with_object_finalize_yet = true; } bool kvmppc_has_cap_epr(void) @@ -2475,7 +2561,7 @@ error_out: } int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, - uint64_t address, uint32_t data) + uint64_t address, uint32_t data, PCIDevice *dev) { return 0; } @@ -2484,3 +2570,12 @@ int kvm_arch_msi_data_to_gsi(uint32_t data) { return data & 0xffff; } + +int kvmppc_enable_hwrng(void) +{ + if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_PPC_HWRNG)) { + return -1; + } + + return kvmppc_enable_hcall(kvm_state, H_RANDOM); +} diff --git a/qemu/target-ppc/kvm_ppc.c b/qemu/target-ppc/kvm_ppc.c deleted file mode 100644 index f769acd44..000000000 --- a/qemu/target-ppc/kvm_ppc.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * PowerPC KVM support - * - * Copyright IBM Corp. 2008 - * - * Authors: - * Hollis Blanchard - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu-common.h" -#include "qemu/timer.h" -#include "kvm_ppc.h" -#include "sysemu/device_tree.h" -#include "qemu/main-loop.h" - -#define PROC_DEVTREE_PATH "/proc/device-tree" - -static QEMUTimer *kvmppc_timer; -static unsigned int kvmppc_timer_rate; - -static void kvmppc_timer_hack(void *opaque) -{ - qemu_notify_event(); - timer_mod(kvmppc_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + kvmppc_timer_rate); -} - -void kvmppc_init(void) -{ - /* XXX The only reason KVM yields control back to qemu is device IO. Since - * an idle guest does no IO, qemu's device model will never get a chance to - * run. So, until QEMU gains IO threads, we create this timer to ensure - * that the device model gets a chance to run. */ - kvmppc_timer_rate = get_ticks_per_sec() / 10; - kvmppc_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &kvmppc_timer_hack, NULL); - timer_mod(kvmppc_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + kvmppc_timer_rate); -} - diff --git a/qemu/target-ppc/kvm_ppc.h b/qemu/target-ppc/kvm_ppc.h index 4d30e2795..fc7931227 100644 --- a/qemu/target-ppc/kvm_ppc.h +++ b/qemu/target-ppc/kvm_ppc.h @@ -11,8 +11,6 @@ #define TYPE_HOST_POWERPC_CPU "host-" TYPE_POWERPC_CPU -void kvmppc_init(void); - #ifdef CONFIG_KVM uint32_t kvmppc_get_tbfreq(void); @@ -25,6 +23,7 @@ int kvmppc_get_hasidle(CPUPPCState *env); int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_len); int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level); void kvmppc_enable_logical_ci_hcalls(void); +void kvmppc_enable_set_mode_hcall(void); void kvmppc_set_papr(PowerPCCPU *cpu); int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version); void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy); @@ -37,7 +36,7 @@ int kvmppc_booke_watchdog_enable(PowerPCCPU *cpu); off_t kvmppc_alloc_rma(void **rma); bool kvmppc_spapr_use_multitce(void); void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd, - bool vfio_accel); + bool need_vfio); int kvmppc_remove_spapr_tce(void *table, int pfd, uint32_t window_size); int kvmppc_reset_htab(int shift_hint); uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift); @@ -55,6 +54,8 @@ void kvmppc_hash64_free_pteg(uint64_t token); void kvmppc_hash64_write_pte(CPUPPCState *env, target_ulong pte_index, target_ulong pte0, target_ulong pte1); bool kvmppc_has_cap_fixup_hcalls(void); +int kvmppc_enable_hwrng(void); +int kvmppc_put_books_sregs(PowerPCCPU *cpu); #else @@ -98,17 +99,16 @@ static inline int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_l return -1; } -static inline int kvmppc_read_segment_page_sizes(uint32_t *prop, int maxcells) +static inline int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level) { return -1; } -static inline int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level) +static inline void kvmppc_enable_logical_ci_hcalls(void) { - return -1; } -static inline void kvmppc_enable_logical_ci_hcalls(void) +static inline void kvmppc_enable_set_mode_hcall(void) { } @@ -176,7 +176,7 @@ static inline int kvmppc_remove_spapr_tce(void *table, int pfd, static inline int kvmppc_reset_htab(int shift_hint) { - return -1; + return 0; } static inline uint64_t kvmppc_rma_size(uint64_t current_size, @@ -185,11 +185,6 @@ static inline uint64_t kvmppc_rma_size(uint64_t current_size, return ram_size; } -static inline int kvmppc_update_sdr1(CPUPPCState *env) -{ - return 0; -} - #endif /* !CONFIG_USER_ONLY */ static inline bool kvmppc_has_cap_epr(void) @@ -248,18 +243,59 @@ static inline bool kvmppc_has_cap_fixup_hcalls(void) abort(); } +static inline int kvmppc_enable_hwrng(void) +{ + return -1; +} + +static inline int kvmppc_put_books_sregs(PowerPCCPU *cpu) +{ + abort(); +} #endif #ifndef CONFIG_KVM + #define kvmppc_eieio() do { } while (0) -#else + +static inline void kvmppc_dcbst_range(PowerPCCPU *cpu, uint8_t *addr, int len) +{ +} + +static inline void kvmppc_icbi_range(PowerPCCPU *cpu, uint8_t *addr, int len) +{ +} + +#else /* CONFIG_KVM */ + #define kvmppc_eieio() \ do { \ if (kvm_enabled()) { \ asm volatile("eieio" : : : "memory"); \ } \ } while (0) -#endif + +/* Store data cache blocks back to memory */ +static inline void kvmppc_dcbst_range(PowerPCCPU *cpu, uint8_t *addr, int len) +{ + uint8_t *p; + + for (p = addr; p < addr + len; p += cpu->env.dcache_line_size) { + asm volatile("dcbst 0,%0" : : "r"(p) : "memory"); + } +} + +/* Invalidate instruction cache blocks */ +static inline void kvmppc_icbi_range(PowerPCCPU *cpu, uint8_t *addr, int len) +{ + uint8_t *p; + + for (p = addr; p < addr + len; p += cpu->env.icache_line_size) { + asm volatile("icbi 0,%0" : : "r"(p)); + } +} + +#endif /* CONFIG_KVM */ #ifndef KVM_INTERRUPT_SET #define KVM_INTERRUPT_SET -1 diff --git a/qemu/target-ppc/machine.c b/qemu/target-ppc/machine.c index f4ac7611d..46684fb93 100644 --- a/qemu/target-ppc/machine.c +++ b/qemu/target-ppc/machine.c @@ -1,7 +1,9 @@ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/boards.h" #include "sysemu/kvm.h" #include "helper_regs.h" +#include "mmu-hash64.h" static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) { @@ -134,7 +136,7 @@ static void cpu_pre_save(void *opaque) env->spr[SPR_LR] = env->lr; env->spr[SPR_CTR] = env->ctr; - env->spr[SPR_XER] = env->xer; + env->spr[SPR_XER] = cpu_read_xer(env); #if defined(TARGET_PPC64) env->spr[SPR_CFAR] = env->cfar; #endif @@ -168,7 +170,7 @@ static int cpu_post_load(void *opaque, int version_id) env->spr[SPR_PVR] = env->spr_cb[SPR_PVR].default_value; env->lr = env->spr[SPR_LR]; env->ctr = env->spr[SPR_CTR]; - env->xer = env->spr[SPR_XER]; + cpu_write_xer(env, env->spr[SPR_XER]); #if defined(TARGET_PPC64) env->cfar = env->spr[SPR_CFAR]; #endif @@ -352,11 +354,30 @@ static bool slb_needed(void *opaque) return (cpu->env.mmu_model & POWERPC_MMU_64); } +static int slb_post_load(void *opaque, int version_id) +{ + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; + int i; + + /* We've pulled in the raw esid and vsid values from the migration + * stream, but we need to recompute the page size pointers */ + for (i = 0; i < env->slb_nr; i++) { + if (ppc_store_slb(cpu, i, env->slb[i].esid, env->slb[i].vsid) < 0) { + /* Migration source had bad values in its SLB */ + return -1; + } + } + + return 0; +} + static const VMStateDescription vmstate_slb = { .name = "cpu/slb", .version_id = 1, .minimum_version_id = 1, .needed = slb_needed, + .post_load = slb_post_load, .fields = (VMStateField[]) { VMSTATE_INT32_EQUAL(env.slb_nr, PowerPCCPU), VMSTATE_SLB_ARRAY(env.slb, PowerPCCPU, MAX_SLB_ENTRIES), diff --git a/qemu/target-ppc/mem_helper.c b/qemu/target-ppc/mem_helper.c index 6d37dae7b..6d584c912 100644 --- a/qemu/target-ppc/mem_helper.c +++ b/qemu/target-ppc/mem_helper.c @@ -16,6 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" @@ -100,8 +101,10 @@ void helper_lswx(CPUPPCState *env, target_ulong addr, uint32_t reg, uint32_t ra, uint32_t rb) { if (likely(xer_bc != 0)) { - if (unlikely((ra != 0 && reg < ra && (reg + xer_bc) > ra) || - (reg < rb && (reg + xer_bc) > rb))) { + int num_used_regs = (xer_bc + 3) / 4; + if (unlikely((ra != 0 && lsw_reg_in_range(reg, num_used_regs, ra)) || + lsw_reg_in_range(reg, num_used_regs, rb))) { + env->nip += 4; /* Compensate the "nip - 4" from gen_lswx() */ helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_LSWX); diff --git a/qemu/target-ppc/mfrom_table_gen.c b/qemu/target-ppc/mfrom_table_gen.c index a140ded47..631791808 100644 --- a/qemu/target-ppc/mfrom_table_gen.c +++ b/qemu/target-ppc/mfrom_table_gen.c @@ -1,6 +1,5 @@ #define _GNU_SOURCE -#include -#include +#include "qemu/osdep.h" #include int main (void) diff --git a/qemu/target-ppc/misc_helper.c b/qemu/target-ppc/misc_helper.c index 6b12ca86a..73e3b0583 100644 --- a/qemu/target-ppc/misc_helper.c +++ b/qemu/target-ppc/misc_helper.c @@ -16,6 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" diff --git a/qemu/target-ppc/mmu-hash32.c b/qemu/target-ppc/mmu-hash32.c index dfee358d6..39abb2fd3 100644 --- a/qemu/target-ppc/mmu-hash32.c +++ b/qemu/target-ppc/mmu-hash32.c @@ -18,23 +18,18 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "mmu-hash32.h" +#include "exec/log.h" -//#define DEBUG_MMU //#define DEBUG_BAT -#ifdef DEBUG_MMU -# define LOG_MMU_STATE(cpu) log_cpu_state((cpu), 0) -#else -# define LOG_MMU_STATE(cpu) do { } while (0) -#endif - #ifdef DEBUG_BATS -# define LOG_BATS(...) qemu_log(__VA_ARGS__) +# define LOG_BATS(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__) #else # define LOG_BATS(...) do { } while (0) #endif @@ -90,9 +85,10 @@ static int ppc_hash32_pp_prot(int key, int pp, int nx) return prot; } -static int ppc_hash32_pte_prot(CPUPPCState *env, +static int ppc_hash32_pte_prot(PowerPCCPU *cpu, target_ulong sr, ppc_hash_pte32_t pte) { + CPUPPCState *env = &cpu->env; unsigned pp, key; key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS)); @@ -101,9 +97,11 @@ static int ppc_hash32_pte_prot(CPUPPCState *env, return ppc_hash32_pp_prot(key, pp, !!(sr & SR32_NX)); } -static target_ulong hash32_bat_size(CPUPPCState *env, +static target_ulong hash32_bat_size(PowerPCCPU *cpu, target_ulong batu, target_ulong batl) { + CPUPPCState *env = &cpu->env; + if ((msr_pr && !(batu & BATU32_VP)) || (!msr_pr && !(batu & BATU32_VS))) { return 0; @@ -112,7 +110,7 @@ static target_ulong hash32_bat_size(CPUPPCState *env, return BATU32_BEPI & ~((batu & BATU32_BL) << 15); } -static int hash32_bat_prot(CPUPPCState *env, +static int hash32_bat_prot(PowerPCCPU *cpu, target_ulong batu, target_ulong batl) { int pp, prot; @@ -128,7 +126,7 @@ static int hash32_bat_prot(CPUPPCState *env, return prot; } -static target_ulong hash32_bat_601_size(CPUPPCState *env, +static target_ulong hash32_bat_601_size(PowerPCCPU *cpu, target_ulong batu, target_ulong batl) { if (!(batl & BATL32_601_V)) { @@ -138,9 +136,10 @@ static target_ulong hash32_bat_601_size(CPUPPCState *env, return BATU32_BEPI & ~((batl & BATL32_601_BL) << 17); } -static int hash32_bat_601_prot(CPUPPCState *env, +static int hash32_bat_601_prot(PowerPCCPU *cpu, target_ulong batu, target_ulong batl) { + CPUPPCState *env = &cpu->env; int key, pp; pp = batu & BATU32_601_PP; @@ -152,9 +151,10 @@ static int hash32_bat_601_prot(CPUPPCState *env, return ppc_hash32_pp_prot(key, pp, 0); } -static hwaddr ppc_hash32_bat_lookup(CPUPPCState *env, target_ulong ea, int rwx, +static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, int rwx, int *prot) { + CPUPPCState *env = &cpu->env; target_ulong *BATlt, *BATut; int i; @@ -173,9 +173,9 @@ static hwaddr ppc_hash32_bat_lookup(CPUPPCState *env, target_ulong ea, int rwx, target_ulong mask; if (unlikely(env->mmu_model == POWERPC_MMU_601)) { - mask = hash32_bat_601_size(env, batu, batl); + mask = hash32_bat_601_size(cpu, batu, batl); } else { - mask = hash32_bat_size(env, batu, batl); + mask = hash32_bat_size(cpu, batu, batl); } LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, @@ -185,9 +185,9 @@ static hwaddr ppc_hash32_bat_lookup(CPUPPCState *env, target_ulong ea, int rwx, hwaddr raddr = (batl & mask) | (ea & ~mask); if (unlikely(env->mmu_model == POWERPC_MMU_601)) { - *prot = hash32_bat_601_prot(env, batu, batl); + *prot = hash32_bat_601_prot(cpu, batu, batl); } else { - *prot = hash32_bat_prot(env, batu, batl); + *prot = hash32_bat_prot(cpu, batu, batl); } return raddr & TARGET_PAGE_MASK; @@ -216,11 +216,12 @@ static hwaddr ppc_hash32_bat_lookup(CPUPPCState *env, target_ulong ea, int rwx, return -1; } -static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr, +static int ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, target_ulong eaddr, int rwx, hwaddr *raddr, int *prot) { - CPUState *cs = CPU(ppc_env_get_cpu(env)); + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; int key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS)); qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); @@ -281,9 +282,8 @@ static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr, } return 1; default: - qemu_log("ERROR: instruction should not need " + cpu_abort(cs, "ERROR: instruction should not need " "address translation\n"); - abort(); } if ((rwx == 1 || key != 1) && (rwx == 0 || key != 0)) { *raddr = eaddr; @@ -301,12 +301,14 @@ static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr, } } -hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash) +hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash) { + CPUPPCState *env = &cpu->env; + return (hash * HASH_PTEG_SIZE_32) & env->htab_mask; } -static hwaddr ppc_hash32_pteg_search(CPUPPCState *env, hwaddr pteg_off, +static hwaddr ppc_hash32_pteg_search(PowerPCCPU *cpu, hwaddr pteg_off, bool secondary, target_ulong ptem, ppc_hash_pte32_t *pte) { @@ -315,8 +317,8 @@ static hwaddr ppc_hash32_pteg_search(CPUPPCState *env, hwaddr pteg_off, int i; for (i = 0; i < HPTES_PER_GROUP; i++) { - pte0 = ppc_hash32_load_hpte0(env, pte_offset); - pte1 = ppc_hash32_load_hpte1(env, pte_offset); + pte0 = ppc_hash32_load_hpte0(cpu, pte_offset); + pte1 = ppc_hash32_load_hpte1(cpu, pte_offset); if ((pte0 & HPTE32_V_VALID) && (secondary == !!(pte0 & HPTE32_V_SECONDARY)) @@ -332,10 +334,11 @@ static hwaddr ppc_hash32_pteg_search(CPUPPCState *env, hwaddr pteg_off, return -1; } -static hwaddr ppc_hash32_htab_lookup(CPUPPCState *env, +static hwaddr ppc_hash32_htab_lookup(PowerPCCPU *cpu, target_ulong sr, target_ulong eaddr, ppc_hash_pte32_t *pte) { + CPUPPCState *env = &cpu->env; hwaddr pteg_off, pte_offset; hwaddr hash; uint32_t vsid, pgidx, ptem; @@ -356,16 +359,16 @@ static hwaddr ppc_hash32_htab_lookup(CPUPPCState *env, " vsid=%" PRIx32 " ptem=%" PRIx32 " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ptem, hash); - pteg_off = get_pteg_offset32(env, hash); - pte_offset = ppc_hash32_pteg_search(env, pteg_off, 0, ptem, pte); + pteg_off = get_pteg_offset32(cpu, hash); + pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 0, ptem, pte); if (pte_offset == -1) { /* Secondary PTEG lookup */ qemu_log_mask(CPU_LOG_MMU, "1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx " vsid=%" PRIx32 " api=%" PRIx32 " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ptem, ~hash); - pteg_off = get_pteg_offset32(env, ~hash); - pte_offset = ppc_hash32_pteg_search(env, pteg_off, 1, ptem, pte); + pteg_off = get_pteg_offset32(cpu, ~hash); + pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 1, ptem, pte); } return pte_offset; @@ -407,7 +410,7 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, int rwx, /* 2. Check Block Address Translation entries (BATs) */ if (env->nb_BATs != 0) { - raddr = ppc_hash32_bat_lookup(env, eaddr, rwx, &prot); + raddr = ppc_hash32_bat_lookup(cpu, eaddr, rwx, &prot); if (raddr != -1) { if (need_prot[rwx] & ~prot) { if (rwx == 2) { @@ -438,7 +441,7 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, int rwx, /* 4. Handle direct store segments */ if (sr & SR32_T) { - if (ppc_hash32_direct_store(env, sr, eaddr, rwx, + if (ppc_hash32_direct_store(cpu, sr, eaddr, rwx, &raddr, &prot) == 0) { tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, prot, mmu_idx, @@ -457,7 +460,7 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, int rwx, } /* 6. Locate the PTE in the hash table */ - pte_offset = ppc_hash32_htab_lookup(env, sr, eaddr, &pte); + pte_offset = ppc_hash32_htab_lookup(cpu, sr, eaddr, &pte); if (pte_offset == -1) { if (rwx == 2) { cs->exception_index = POWERPC_EXCP_ISI; @@ -480,7 +483,7 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, int rwx, /* 7. Check access permissions */ - prot = ppc_hash32_pte_prot(env, sr, pte); + prot = ppc_hash32_pte_prot(cpu, sr, pte); if (need_prot[rwx] & ~prot) { /* Access right violation */ @@ -515,7 +518,7 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, int rwx, } if (new_pte1 != pte.pte1) { - ppc_hash32_store_hpte1(env, pte_offset, new_pte1); + ppc_hash32_store_hpte1(cpu, pte_offset, new_pte1); } /* 9. Determine the real address from the PTE */ @@ -528,8 +531,9 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, int rwx, return 0; } -hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong eaddr) +hwaddr ppc_hash32_get_phys_page_debug(PowerPCCPU *cpu, target_ulong eaddr) { + CPUPPCState *env = &cpu->env; target_ulong sr; hwaddr pte_offset; ppc_hash_pte32_t pte; @@ -541,7 +545,7 @@ hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong eaddr) } if (env->nb_BATs != 0) { - hwaddr raddr = ppc_hash32_bat_lookup(env, eaddr, 0, &prot); + hwaddr raddr = ppc_hash32_bat_lookup(cpu, eaddr, 0, &prot); if (raddr != -1) { return raddr; } @@ -554,7 +558,7 @@ hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong eaddr) return -1; } - pte_offset = ppc_hash32_htab_lookup(env, sr, eaddr, &pte); + pte_offset = ppc_hash32_htab_lookup(cpu, sr, eaddr, &pte); if (pte_offset == -1) { return -1; } diff --git a/qemu/target-ppc/mmu-hash32.h b/qemu/target-ppc/mmu-hash32.h index d515d4ff7..afbb9dd3d 100644 --- a/qemu/target-ppc/mmu-hash32.h +++ b/qemu/target-ppc/mmu-hash32.h @@ -3,8 +3,8 @@ #ifndef CONFIG_USER_ONLY -hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash); -hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong addr); +hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash); +hwaddr ppc_hash32_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr); int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, target_ulong address, int rw, int mmu_idx); @@ -65,40 +65,42 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, target_ulong address, int rw, #define HPTE32_R_WIMG 0x00000078 #define HPTE32_R_PP 0x00000003 -static inline target_ulong ppc_hash32_load_hpte0(CPUPPCState *env, +static inline target_ulong ppc_hash32_load_hpte0(PowerPCCPU *cpu, hwaddr pte_offset) { - CPUState *cs = CPU(ppc_env_get_cpu(env)); + CPUPPCState *env = &cpu->env; assert(!env->external_htab); /* Not supported on 32-bit for now */ - return ldl_phys(cs->as, env->htab_base + pte_offset); + return ldl_phys(CPU(cpu)->as, env->htab_base + pte_offset); } -static inline target_ulong ppc_hash32_load_hpte1(CPUPPCState *env, +static inline target_ulong ppc_hash32_load_hpte1(PowerPCCPU *cpu, hwaddr pte_offset) { - CPUState *cs = CPU(ppc_env_get_cpu(env)); + CPUPPCState *env = &cpu->env; assert(!env->external_htab); /* Not supported on 32-bit for now */ - return ldl_phys(cs->as, env->htab_base + pte_offset + HASH_PTE_SIZE_32/2); + return ldl_phys(CPU(cpu)->as, + env->htab_base + pte_offset + HASH_PTE_SIZE_32 / 2); } -static inline void ppc_hash32_store_hpte0(CPUPPCState *env, +static inline void ppc_hash32_store_hpte0(PowerPCCPU *cpu, hwaddr pte_offset, target_ulong pte0) { - CPUState *cs = CPU(ppc_env_get_cpu(env)); + CPUPPCState *env = &cpu->env; assert(!env->external_htab); /* Not supported on 32-bit for now */ - stl_phys(cs->as, env->htab_base + pte_offset, pte0); + stl_phys(CPU(cpu)->as, env->htab_base + pte_offset, pte0); } -static inline void ppc_hash32_store_hpte1(CPUPPCState *env, +static inline void ppc_hash32_store_hpte1(PowerPCCPU *cpu, hwaddr pte_offset, target_ulong pte1) { - CPUState *cs = CPU(ppc_env_get_cpu(env)); + CPUPPCState *env = &cpu->env; assert(!env->external_htab); /* Not supported on 32-bit for now */ - stl_phys(cs->as, env->htab_base + pte_offset + HASH_PTE_SIZE_32/2, pte1); + stl_phys(CPU(cpu)->as, + env->htab_base + pte_offset + HASH_PTE_SIZE_32 / 2, pte1); } typedef struct { diff --git a/qemu/target-ppc/mmu-hash64.c b/qemu/target-ppc/mmu-hash64.c index 7df6edebf..72c4ab5d7 100644 --- a/qemu/target-ppc/mmu-hash64.c +++ b/qemu/target-ppc/mmu-hash64.c @@ -17,38 +17,38 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "exec/helper-proto.h" +#include "qemu/error-report.h" #include "sysemu/kvm.h" +#include "qemu/error-report.h" #include "kvm_ppc.h" #include "mmu-hash64.h" +#include "exec/log.h" -//#define DEBUG_MMU //#define DEBUG_SLB -#ifdef DEBUG_MMU -# define LOG_MMU_STATE(cpu) log_cpu_state((cpu), 0) -#else -# define LOG_MMU_STATE(cpu) do { } while (0) -#endif - #ifdef DEBUG_SLB -# define LOG_SLB(...) qemu_log(__VA_ARGS__) +# define LOG_SLB(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__) #else # define LOG_SLB(...) do { } while (0) #endif /* - * Used to indicate whether we have allocated htab in the - * host kernel + * Used to indicate that a CPU has its hash page table (HPT) managed + * within the host kernel */ -bool kvmppc_kern_htab; +#define MMU_HASH64_KVM_MANAGED_HPT ((void *)-1) + /* * SLB handling */ -static ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr) +static ppc_slb_t *slb_lookup(PowerPCCPU *cpu, target_ulong eaddr) { + CPUPPCState *env = &cpu->env; uint64_t esid_256M, esid_1T; int n; @@ -76,12 +76,13 @@ static ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr) return NULL; } -void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env) +void dump_slb(FILE *f, fprintf_function cpu_fprintf, PowerPCCPU *cpu) { + CPUPPCState *env = &cpu->env; int i; uint64_t slbe, slbv; - cpu_synchronize_state(CPU(ppc_env_get_cpu(env))); + cpu_synchronize_state(CPU(cpu)); cpu_fprintf(f, "SLB\tESID\t\t\tVSID\n"); for (i = 0; i < env->slb_nr; i++) { @@ -124,7 +125,7 @@ void helper_slbie(CPUPPCState *env, target_ulong addr) PowerPCCPU *cpu = ppc_env_get_cpu(env); ppc_slb_t *slb; - slb = slb_lookup(env, addr); + slb = slb_lookup(cpu, addr); if (!slb) { return; } @@ -140,35 +141,62 @@ void helper_slbie(CPUPPCState *env, target_ulong addr) } } -int ppc_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) +int ppc_store_slb(PowerPCCPU *cpu, target_ulong slot, + target_ulong esid, target_ulong vsid) { - int slot = rb & 0xfff; + CPUPPCState *env = &cpu->env; ppc_slb_t *slb = &env->slb[slot]; + const struct ppc_one_seg_page_size *sps = NULL; + int i; - if (rb & (0x1000 - env->slb_nr)) { - return -1; /* Reserved bits set or slot too high */ + if (slot >= env->slb_nr) { + return -1; /* Bad slot number */ + } + if (esid & ~(SLB_ESID_ESID | SLB_ESID_V)) { + return -1; /* Reserved bits set */ } - if (rs & (SLB_VSID_B & ~SLB_VSID_B_1T)) { + if (vsid & (SLB_VSID_B & ~SLB_VSID_B_1T)) { return -1; /* Bad segment size */ } - if ((rs & SLB_VSID_B) && !(env->mmu_model & POWERPC_MMU_1TSEG)) { + if ((vsid & SLB_VSID_B) && !(env->mmu_model & POWERPC_MMU_1TSEG)) { return -1; /* 1T segment on MMU that doesn't support it */ } - /* Mask out the slot number as we store the entry */ - slb->esid = rb & (SLB_ESID_ESID | SLB_ESID_V); - slb->vsid = rs; + for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { + const struct ppc_one_seg_page_size *sps1 = &env->sps.sps[i]; + + if (!sps1->page_shift) { + break; + } + + if ((vsid & SLB_VSID_LLP_MASK) == sps1->slb_enc) { + sps = sps1; + break; + } + } + + if (!sps) { + error_report("Bad page size encoding in SLB store: slot "TARGET_FMT_lu + " esid 0x"TARGET_FMT_lx" vsid 0x"TARGET_FMT_lx, + slot, esid, vsid); + return -1; + } + + slb->esid = esid; + slb->vsid = vsid; + slb->sps = sps; LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016" PRIx64 - " %016" PRIx64 "\n", __func__, slot, rb, rs, + " %016" PRIx64 "\n", __func__, slot, esid, vsid, slb->esid, slb->vsid); return 0; } -static int ppc_load_slb_esid(CPUPPCState *env, target_ulong rb, +static int ppc_load_slb_esid(PowerPCCPU *cpu, target_ulong rb, target_ulong *rt) { + CPUPPCState *env = &cpu->env; int slot = rb & 0xfff; ppc_slb_t *slb = &env->slb[slot]; @@ -180,9 +208,10 @@ static int ppc_load_slb_esid(CPUPPCState *env, target_ulong rb, return 0; } -static int ppc_load_slb_vsid(CPUPPCState *env, target_ulong rb, +static int ppc_load_slb_vsid(PowerPCCPU *cpu, target_ulong rb, target_ulong *rt) { + CPUPPCState *env = &cpu->env; int slot = rb & 0xfff; ppc_slb_t *slb = &env->slb[slot]; @@ -196,7 +225,9 @@ static int ppc_load_slb_vsid(CPUPPCState *env, target_ulong rb, void helper_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) { - if (ppc_store_slb(env, rb, rs) < 0) { + PowerPCCPU *cpu = ppc_env_get_cpu(env); + + if (ppc_store_slb(cpu, rb & 0xfff, rb & ~0xfffULL, rs) < 0) { helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL); } @@ -204,9 +235,10 @@ void helper_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) target_ulong helper_load_slb_esid(CPUPPCState *env, target_ulong rb) { + PowerPCCPU *cpu = ppc_env_get_cpu(env); target_ulong rt = 0; - if (ppc_load_slb_esid(env, rb, &rt) < 0) { + if (ppc_load_slb_esid(cpu, rb, &rt) < 0) { helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL); } @@ -215,9 +247,10 @@ target_ulong helper_load_slb_esid(CPUPPCState *env, target_ulong rb) target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb) { + PowerPCCPU *cpu = ppc_env_get_cpu(env); target_ulong rt = 0; - if (ppc_load_slb_vsid(env, rb, &rt) < 0) { + if (ppc_load_slb_vsid(cpu, rb, &rt) < 0) { helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL); } @@ -227,10 +260,58 @@ target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb) /* * 64-bit hash table MMU handling */ +void ppc_hash64_set_sdr1(PowerPCCPU *cpu, target_ulong value, + Error **errp) +{ + CPUPPCState *env = &cpu->env; + target_ulong htabsize = value & SDR_64_HTABSIZE; + + env->spr[SPR_SDR1] = value; + if (htabsize > 28) { + error_setg(errp, + "Invalid HTABSIZE 0x" TARGET_FMT_lx" stored in SDR1", + htabsize); + htabsize = 28; + } + env->htab_mask = (1ULL << (htabsize + 18 - 7)) - 1; + env->htab_base = value & SDR_64_HTABORG; +} + +void ppc_hash64_set_external_hpt(PowerPCCPU *cpu, void *hpt, int shift, + Error **errp) +{ + CPUPPCState *env = &cpu->env; + Error *local_err = NULL; -static int ppc_hash64_pte_prot(CPUPPCState *env, + cpu_synchronize_state(CPU(cpu)); + + if (hpt) { + env->external_htab = hpt; + } else { + env->external_htab = MMU_HASH64_KVM_MANAGED_HPT; + } + ppc_hash64_set_sdr1(cpu, (target_ulong)(uintptr_t)hpt | (shift - 18), + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* Not strictly necessary, but makes it clearer that an external + * htab is in use when debugging */ + env->htab_base = -1; + + if (kvm_enabled()) { + if (kvmppc_put_books_sregs(cpu) < 0) { + error_setg(errp, "Unable to update SDR1 in KVM"); + } + } +} + +static int ppc_hash64_pte_prot(PowerPCCPU *cpu, ppc_slb_t *slb, ppc_hash_pte64_t pte) { + CPUPPCState *env = &cpu->env; unsigned pp, key; /* Some pp bit combinations have undefined behaviour, so default * to no access in those cases */ @@ -280,12 +361,12 @@ static int ppc_hash64_pte_prot(CPUPPCState *env, return prot; } -static int ppc_hash64_amr_prot(CPUPPCState *env, ppc_hash_pte64_t pte) +static int ppc_hash64_amr_prot(PowerPCCPU *cpu, ppc_hash_pte64_t pte) { + CPUPPCState *env = &cpu->env; int key, amrbits; int prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - /* Only recent MMUs implement Virtual Page Class Key Protection */ if (!(env->mmu_model & POWERPC_MMU_AMR)) { return prot; @@ -321,25 +402,16 @@ uint64_t ppc_hash64_start_access(PowerPCCPU *cpu, target_ulong pte_index) hwaddr pte_offset; pte_offset = pte_index * HASH_PTE_SIZE_64; - if (kvmppc_kern_htab) { + if (cpu->env.external_htab == MMU_HASH64_KVM_MANAGED_HPT) { /* * HTAB is controlled by KVM. Fetch the PTEG into a new buffer. */ token = kvmppc_hash64_read_pteg(cpu, pte_index); - if (token) { - return token; - } + } else if (cpu->env.external_htab) { /* - * pteg read failed, even though we have allocated htab via - * kvmppc_reset_htab. + * HTAB is controlled by QEMU. Just point to the internally + * accessible PTEG. */ - return 0; - } - /* - * HTAB is controlled by QEMU. Just point to the internally - * accessible PTEG. - */ - if (cpu->env.external_htab) { token = (uint64_t)(uintptr_t) cpu->env.external_htab + pte_offset; } else if (cpu->env.htab_base) { token = cpu->env.htab_base + pte_offset; @@ -347,86 +419,73 @@ uint64_t ppc_hash64_start_access(PowerPCCPU *cpu, target_ulong pte_index) return token; } -void ppc_hash64_stop_access(uint64_t token) +void ppc_hash64_stop_access(PowerPCCPU *cpu, uint64_t token) { - if (kvmppc_kern_htab) { + if (cpu->env.external_htab == MMU_HASH64_KVM_MANAGED_HPT) { kvmppc_hash64_free_pteg(token); } } -static hwaddr ppc_hash64_pteg_search(CPUPPCState *env, hwaddr hash, +static hwaddr ppc_hash64_pteg_search(PowerPCCPU *cpu, hwaddr hash, bool secondary, target_ulong ptem, ppc_hash_pte64_t *pte) { + CPUPPCState *env = &cpu->env; int i; uint64_t token; target_ulong pte0, pte1; target_ulong pte_index; pte_index = (hash & env->htab_mask) * HPTES_PER_GROUP; - token = ppc_hash64_start_access(ppc_env_get_cpu(env), pte_index); + token = ppc_hash64_start_access(cpu, pte_index); if (!token) { return -1; } for (i = 0; i < HPTES_PER_GROUP; i++) { - pte0 = ppc_hash64_load_hpte0(env, token, i); - pte1 = ppc_hash64_load_hpte1(env, token, i); + pte0 = ppc_hash64_load_hpte0(cpu, token, i); + pte1 = ppc_hash64_load_hpte1(cpu, token, i); if ((pte0 & HPTE64_V_VALID) && (secondary == !!(pte0 & HPTE64_V_SECONDARY)) && HPTE64_V_COMPARE(pte0, ptem)) { pte->pte0 = pte0; pte->pte1 = pte1; - ppc_hash64_stop_access(token); + ppc_hash64_stop_access(cpu, token); return (pte_index + i) * HASH_PTE_SIZE_64; } } - ppc_hash64_stop_access(token); + ppc_hash64_stop_access(cpu, token); /* * We didn't find a valid entry. */ return -1; } -static uint64_t ppc_hash64_page_shift(ppc_slb_t *slb) -{ - uint64_t epnshift; - - /* Page size according to the SLB, which we use to generate the - * EPN for hash table lookup.. When we implement more recent MMU - * extensions this might be different from the actual page size - * encoded in the PTE */ - if ((slb->vsid & SLB_VSID_LLP_MASK) == SLB_VSID_4K) { - epnshift = TARGET_PAGE_BITS; - } else if ((slb->vsid & SLB_VSID_LLP_MASK) == SLB_VSID_64K) { - epnshift = TARGET_PAGE_BITS_64K; - } else { - epnshift = TARGET_PAGE_BITS_16M; - } - return epnshift; -} - -static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, +static hwaddr ppc_hash64_htab_lookup(PowerPCCPU *cpu, ppc_slb_t *slb, target_ulong eaddr, ppc_hash_pte64_t *pte) { + CPUPPCState *env = &cpu->env; hwaddr pte_offset; hwaddr hash; - uint64_t vsid, epnshift, epnmask, epn, ptem; + uint64_t vsid, epnmask, epn, ptem; + + /* The SLB store path should prevent any bad page size encodings + * getting in there, so: */ + assert(slb->sps); - epnshift = ppc_hash64_page_shift(slb); - epnmask = ~((1ULL << epnshift) - 1); + epnmask = ~((1ULL << slb->sps->page_shift) - 1); if (slb->vsid & SLB_VSID_B) { /* 1TB segment */ vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T; epn = (eaddr & ~SEGMENT_MASK_1T) & epnmask; - hash = vsid ^ (vsid << 25) ^ (epn >> epnshift); + hash = vsid ^ (vsid << 25) ^ (epn >> slb->sps->page_shift); } else { /* 256M segment */ vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT; epn = (eaddr & ~SEGMENT_MASK_256M) & epnmask; - hash = vsid ^ (epn >> epnshift); + hash = vsid ^ (epn >> slb->sps->page_shift); } ptem = (slb->vsid & SLB_VSID_PTEM) | ((epn >> 16) & HPTE64_V_AVPN); @@ -442,7 +501,7 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ptem, hash); - pte_offset = ppc_hash64_pteg_search(env, hash, 0, ptem, pte); + pte_offset = ppc_hash64_pteg_search(cpu, hash, 0, ptem, pte); if (pte_offset == -1) { /* Secondary PTEG lookup */ @@ -452,24 +511,82 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ptem, ~hash); - pte_offset = ppc_hash64_pteg_search(env, ~hash, 1, ptem, pte); + pte_offset = ppc_hash64_pteg_search(cpu, ~hash, 1, ptem, pte); } return pte_offset; } -static hwaddr ppc_hash64_pte_raddr(ppc_slb_t *slb, ppc_hash_pte64_t pte, - target_ulong eaddr) +static unsigned hpte_page_shift(const struct ppc_one_seg_page_size *sps, + uint64_t pte0, uint64_t pte1) +{ + int i; + + if (!(pte0 & HPTE64_V_LARGE)) { + if (sps->page_shift != 12) { + /* 4kiB page in a non 4kiB segment */ + return 0; + } + /* Normal 4kiB page */ + return 12; + } + + for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { + const struct ppc_one_page_size *ps = &sps->enc[i]; + uint64_t mask; + + if (!ps->page_shift) { + break; + } + + if (ps->page_shift == 12) { + /* L bit is set so this can't be a 4kiB page */ + continue; + } + + mask = ((1ULL << ps->page_shift) - 1) & HPTE64_R_RPN; + + if ((pte1 & mask) == (ps->pte_enc << HPTE64_R_RPN_SHIFT)) { + return ps->page_shift; + } + } + + return 0; /* Bad page size encoding */ +} + +unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, + uint64_t pte0, uint64_t pte1, + unsigned *seg_page_shift) { - hwaddr mask; - int target_page_bits; - hwaddr rpn = pte.pte1 & HPTE64_R_RPN; + CPUPPCState *env = &cpu->env; + int i; + + if (!(pte0 & HPTE64_V_LARGE)) { + *seg_page_shift = 12; + return 12; + } + /* - * We support 4K, 64K and 16M now + * The encodings in env->sps need to be carefully chosen so that + * this gives an unambiguous result. */ - target_page_bits = ppc_hash64_page_shift(slb); - mask = (1ULL << target_page_bits) - 1; - return (rpn & ~mask) | (eaddr & mask); + for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { + const struct ppc_one_seg_page_size *sps = &env->sps.sps[i]; + unsigned shift; + + if (!sps->page_shift) { + break; + } + + shift = hpte_page_shift(sps, pte0, pte1); + if (shift) { + *seg_page_shift = sps->page_shift; + return shift; + } + } + + *seg_page_shift = 0; + return 0; } int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, @@ -478,6 +595,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; ppc_slb_t *slb; + unsigned apshift; hwaddr pte_offset; ppc_hash_pte64_t pte; int pp_prot, amr_prot, prot; @@ -499,7 +617,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, } /* 2. Translation is on, so look up the SLB */ - slb = slb_lookup(env, eaddr); + slb = slb_lookup(cpu, eaddr); if (!slb) { if (rwx == 2) { @@ -521,7 +639,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, } /* 4. Locate the PTE in the hash table */ - pte_offset = ppc_hash64_htab_lookup(env, slb, eaddr, &pte); + pte_offset = ppc_hash64_htab_lookup(cpu, slb, eaddr, &pte); if (pte_offset == -1) { if (rwx == 2) { cs->exception_index = POWERPC_EXCP_ISI; @@ -541,10 +659,22 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, qemu_log_mask(CPU_LOG_MMU, "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); + /* Validate page size encoding */ + apshift = hpte_page_shift(slb->sps, pte.pte0, pte.pte1); + if (!apshift) { + error_report("Bad page size encoding in HPTE 0x%"PRIx64" - 0x%"PRIx64 + " @ 0x%"HWADDR_PRIx, pte.pte0, pte.pte1, pte_offset); + /* Not entirely sure what the right action here, but machine + * check seems reasonable */ + cs->exception_index = POWERPC_EXCP_MCHECK; + env->error_code = 0; + return 1; + } + /* 5. Check access permissions */ - pp_prot = ppc_hash64_pte_prot(env, slb, pte); - amr_prot = ppc_hash64_amr_prot(env, pte); + pp_prot = ppc_hash64_pte_prot(cpu, slb, pte); + amr_prot = ppc_hash64_amr_prot(cpu, pte); prot = pp_prot & amr_prot; if ((need_prot[rwx] & ~prot) != 0) { @@ -587,51 +717,59 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, } if (new_pte1 != pte.pte1) { - ppc_hash64_store_hpte(env, pte_offset / HASH_PTE_SIZE_64, + ppc_hash64_store_hpte(cpu, pte_offset / HASH_PTE_SIZE_64, pte.pte0, new_pte1); } /* 7. Determine the real address from the PTE */ - raddr = ppc_hash64_pte_raddr(slb, pte, eaddr); + raddr = deposit64(pte.pte1 & HPTE64_R_RPN, 0, apshift, eaddr); tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, - prot, mmu_idx, TARGET_PAGE_SIZE); + prot, mmu_idx, 1ULL << apshift); return 0; } -hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr) +hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr) { + CPUPPCState *env = &cpu->env; ppc_slb_t *slb; hwaddr pte_offset; ppc_hash_pte64_t pte; + unsigned apshift; if (msr_dr == 0) { /* In real mode the top 4 effective address bits are ignored */ return addr & 0x0FFFFFFFFFFFFFFFULL; } - slb = slb_lookup(env, addr); + slb = slb_lookup(cpu, addr); if (!slb) { return -1; } - pte_offset = ppc_hash64_htab_lookup(env, slb, addr, &pte); + pte_offset = ppc_hash64_htab_lookup(cpu, slb, addr, &pte); if (pte_offset == -1) { return -1; } - return ppc_hash64_pte_raddr(slb, pte, addr) & TARGET_PAGE_MASK; + apshift = hpte_page_shift(slb->sps, pte.pte0, pte.pte1); + if (!apshift) { + return -1; + } + + return deposit64(pte.pte1 & HPTE64_R_RPN, 0, apshift, addr) + & TARGET_PAGE_MASK; } -void ppc_hash64_store_hpte(CPUPPCState *env, +void ppc_hash64_store_hpte(PowerPCCPU *cpu, target_ulong pte_index, target_ulong pte0, target_ulong pte1) { - CPUState *cs = CPU(ppc_env_get_cpu(env)); + CPUPPCState *env = &cpu->env; - if (kvmppc_kern_htab) { + if (env->external_htab == MMU_HASH64_KVM_MANAGED_HPT) { kvmppc_hash64_write_pte(env, pte_index, pte0, pte1); return; } @@ -639,9 +777,22 @@ void ppc_hash64_store_hpte(CPUPPCState *env, pte_index *= HASH_PTE_SIZE_64; if (env->external_htab) { stq_p(env->external_htab + pte_index, pte0); - stq_p(env->external_htab + pte_index + HASH_PTE_SIZE_64/2, pte1); + stq_p(env->external_htab + pte_index + HASH_PTE_SIZE_64 / 2, pte1); } else { - stq_phys(cs->as, env->htab_base + pte_index, pte0); - stq_phys(cs->as, env->htab_base + pte_index + HASH_PTE_SIZE_64/2, pte1); + stq_phys(CPU(cpu)->as, env->htab_base + pte_index, pte0); + stq_phys(CPU(cpu)->as, + env->htab_base + pte_index + HASH_PTE_SIZE_64 / 2, pte1); } } + +void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, + target_ulong pte_index, + target_ulong pte0, target_ulong pte1) +{ + /* + * XXX: given the fact that there are too many segments to + * invalidate, and we still don't have a tlb_flush_mask(env, n, + * mask) in QEMU, we just invalidate all TLBs + */ + tlb_flush(CPU(cpu), 1); +} diff --git a/qemu/target-ppc/mmu-hash64.h b/qemu/target-ppc/mmu-hash64.h index 291750f3e..9bf8b9b26 100644 --- a/qemu/target-ppc/mmu-hash64.h +++ b/qemu/target-ppc/mmu-hash64.h @@ -4,13 +4,21 @@ #ifndef CONFIG_USER_ONLY #ifdef TARGET_PPC64 -void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env); -int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs); -hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr); +void ppc_hash64_check_page_sizes(PowerPCCPU *cpu, Error **errp); +void dump_slb(FILE *f, fprintf_function cpu_fprintf, PowerPCCPU *cpu); +int ppc_store_slb(PowerPCCPU *cpu, target_ulong slot, + target_ulong esid, target_ulong vsid); +hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr); int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong address, int rw, int mmu_idx); -void ppc_hash64_store_hpte(CPUPPCState *env, target_ulong index, +void ppc_hash64_store_hpte(PowerPCCPU *cpu, target_ulong index, target_ulong pte0, target_ulong pte1); +void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, + target_ulong pte_index, + target_ulong pte0, target_ulong pte1); +unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, + uint64_t pte0, uint64_t pte1, + unsigned *seg_page_shift); #endif /* @@ -40,6 +48,8 @@ void ppc_hash64_store_hpte(CPUPPCState *env, target_ulong index, #define SLB_VSID_LLP_MASK (SLB_VSID_L | SLB_VSID_LP) #define SLB_VSID_4K 0x0000000000000000ULL #define SLB_VSID_64K 0x0000000000000110ULL +#define SLB_VSID_16M 0x0000000000000100ULL +#define SLB_VSID_16G 0x0000000000000120ULL /* * Hash page table definitions @@ -80,36 +90,39 @@ void ppc_hash64_store_hpte(CPUPPCState *env, target_ulong index, #define HPTE64_V_1TB_SEG 0x4000000000000000ULL #define HPTE64_V_VRMA_MASK 0x4001ffffff000000ULL +void ppc_hash64_set_sdr1(PowerPCCPU *cpu, target_ulong value, + Error **errp); +void ppc_hash64_set_external_hpt(PowerPCCPU *cpu, void *hpt, int shift, + Error **errp); -extern bool kvmppc_kern_htab; uint64_t ppc_hash64_start_access(PowerPCCPU *cpu, target_ulong pte_index); -void ppc_hash64_stop_access(uint64_t token); +void ppc_hash64_stop_access(PowerPCCPU *cpu, uint64_t token); -static inline target_ulong ppc_hash64_load_hpte0(CPUPPCState *env, +static inline target_ulong ppc_hash64_load_hpte0(PowerPCCPU *cpu, uint64_t token, int index) { - CPUState *cs = CPU(ppc_env_get_cpu(env)); + CPUPPCState *env = &cpu->env; uint64_t addr; addr = token + (index * HASH_PTE_SIZE_64); if (env->external_htab) { return ldq_p((const void *)(uintptr_t)addr); } else { - return ldq_phys(cs->as, addr); + return ldq_phys(CPU(cpu)->as, addr); } } -static inline target_ulong ppc_hash64_load_hpte1(CPUPPCState *env, +static inline target_ulong ppc_hash64_load_hpte1(PowerPCCPU *cpu, uint64_t token, int index) { - CPUState *cs = CPU(ppc_env_get_cpu(env)); + CPUPPCState *env = &cpu->env; uint64_t addr; addr = token + (index * HASH_PTE_SIZE_64) + HASH_PTE_SIZE_64/2; if (env->external_htab) { return ldq_p((const void *)(uintptr_t)addr); } else { - return ldq_phys(cs->as, addr); + return ldq_phys(CPU(cpu)->as, addr); } } diff --git a/qemu/target-ppc/mmu_helper.c b/qemu/target-ppc/mmu_helper.c index 527c6adca..ff217941b 100644 --- a/qemu/target-ppc/mmu_helper.c +++ b/qemu/target-ppc/mmu_helper.c @@ -16,6 +16,8 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "exec/helper-proto.h" #include "sysemu/kvm.h" @@ -23,28 +25,28 @@ #include "mmu-hash64.h" #include "mmu-hash32.h" #include "exec/cpu_ldst.h" +#include "exec/log.h" //#define DEBUG_MMU //#define DEBUG_BATS //#define DEBUG_SOFTWARE_TLB //#define DUMP_PAGE_TABLES -//#define DEBUG_SOFTWARE_TLB //#define FLUSH_ALL_TLBS #ifdef DEBUG_MMU -# define LOG_MMU_STATE(cpu) log_cpu_state((cpu), 0) +# define LOG_MMU_STATE(cpu) log_cpu_state_mask(CPU_LOG_MMU, (cpu), 0) #else # define LOG_MMU_STATE(cpu) do { } while (0) #endif #ifdef DEBUG_SOFTWARE_TLB -# define LOG_SWTLB(...) qemu_log(__VA_ARGS__) +# define LOG_SWTLB(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__) #else # define LOG_SWTLB(...) do { } while (0) #endif #ifdef DEBUG_BATS -# define LOG_BATS(...) qemu_log(__VA_ARGS__) +# define LOG_BATS(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__) #else # define LOG_BATS(...) do { } while (0) #endif @@ -162,7 +164,7 @@ static inline int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, if (ctx->raddr != (hwaddr)-1ULL) { /* all matches should have equal RPN, WIMG & PP */ if ((ctx->raddr & mmask) != (pte1 & mmask)) { - qemu_log("Bad RPN/WIMG/PP\n"); + qemu_log_mask(CPU_LOG_MMU, "Bad RPN/WIMG/PP\n"); return -3; } } @@ -508,7 +510,7 @@ static inline int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, /* Software TLB search */ ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type); #if defined(DUMP_PAGE_TABLES) - if (qemu_log_enabled()) { + if (qemu_log_mask(CPU_LOG_MMU)) { hwaddr curaddr; uint32_t a0, a1, a2, a3; @@ -575,8 +577,8 @@ static inline int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, /* eciwx or ecowx */ return -4; default: - qemu_log("ERROR: instruction should not need " - "address translation\n"); + qemu_log_mask(CPU_LOG_MMU, "ERROR: instruction should not need " + "address translation\n"); return -4; } if ((rw == 1 || ctx->key != 1) && (rw == 0 || ctx->key != 0)) { @@ -658,32 +660,6 @@ static inline void ppc4xx_tlb_invalidate_all(CPUPPCState *env) tlb_flush(CPU(cpu), 1); } -static inline void ppc4xx_tlb_invalidate_virt(CPUPPCState *env, - target_ulong eaddr, uint32_t pid) -{ -#if !defined(FLUSH_ALL_TLBS) - CPUState *cs = CPU(ppc_env_get_cpu(env)); - ppcemb_tlb_t *tlb; - hwaddr raddr; - target_ulong page, end; - int i; - - for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb.tlbe[i]; - if (ppcemb_tlb_check(env, tlb, &raddr, eaddr, pid, 0, i) == 0) { - end = tlb->EPN + tlb->size; - for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) { - tlb_flush_page(cs, page); - } - tlb->prot &= ~PAGE_VALID; - break; - } - } -#else - ppc4xx_tlb_invalidate_all(env); -#endif -} - static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong address, int rw, int access_type) @@ -1293,10 +1269,12 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env) break; #if defined(TARGET_PPC64) case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: case POWERPC_MMU_2_06: case POWERPC_MMU_2_06a: - case POWERPC_MMU_2_06d: - dump_slb(f, cpu_fprintf, env); + case POWERPC_MMU_2_07: + case POWERPC_MMU_2_07a: + dump_slb(f, cpu_fprintf, ppc_env_get_cpu(env)); break; #endif default: @@ -1433,15 +1411,17 @@ hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) switch (env->mmu_model) { #if defined(TARGET_PPC64) case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: case POWERPC_MMU_2_06: case POWERPC_MMU_2_06a: - case POWERPC_MMU_2_06d: - return ppc_hash64_get_phys_page_debug(env, addr); + case POWERPC_MMU_2_07: + case POWERPC_MMU_2_07a: + return ppc_hash64_get_phys_page_debug(cpu, addr); #endif case POWERPC_MMU_32B: case POWERPC_MMU_601: - return ppc_hash32_get_phys_page_debug(env, addr); + return ppc_hash32_get_phys_page_debug(cpu, addr); default: ; @@ -1507,6 +1487,7 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, int mmu_idx) { CPUState *cs = CPU(ppc_env_get_cpu(env)); + PowerPCCPU *cpu = POWERPC_CPU(cs); mmu_ctx_t ctx; int access_type; int ret = 0; @@ -1608,9 +1589,9 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, tlb_miss: env->error_code |= ctx.key << 19; env->spr[SPR_HASH1] = env->htab_base + - get_pteg_offset32(env, ctx.hash[0]); + get_pteg_offset32(cpu, ctx.hash[0]); env->spr[SPR_HASH2] = env->htab_base + - get_pteg_offset32(env, ctx.hash[1]); + get_pteg_offset32(cpu, ctx.hash[1]); break; case POWERPC_MMU_SOFT_74xx: if (rw == 1) { @@ -1937,9 +1918,11 @@ void ppc_tlb_invalidate_all(CPUPPCState *env) case POWERPC_MMU_601: #if defined(TARGET_PPC64) case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: case POWERPC_MMU_2_06: case POWERPC_MMU_2_06a: - case POWERPC_MMU_2_06d: + case POWERPC_MMU_2_07: + case POWERPC_MMU_2_07a: #endif /* defined(TARGET_PPC64) */ tlb_flush(CPU(cpu), 1); break; @@ -1965,25 +1948,6 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) ppc6xx_tlb_invalidate_virt(env, addr, 1); } break; - case POWERPC_MMU_SOFT_4xx: - case POWERPC_MMU_SOFT_4xx_Z: - ppc4xx_tlb_invalidate_virt(env, addr, env->spr[SPR_40x_PID]); - break; - case POWERPC_MMU_REAL: - cpu_abort(CPU(cpu), "No TLB for PowerPC 4xx in real mode\n"); - break; - case POWERPC_MMU_MPC8xx: - /* XXX: TODO */ - cpu_abort(CPU(cpu), "MPC8xx MMU model is not implemented\n"); - break; - case POWERPC_MMU_BOOKE: - /* XXX: TODO */ - cpu_abort(CPU(cpu), "BookE MMU model is not implemented\n"); - break; - case POWERPC_MMU_BOOKE206: - /* XXX: TODO */ - cpu_abort(CPU(cpu), "BookE 2.06 MMU model is not implemented\n"); - break; case POWERPC_MMU_32B: case POWERPC_MMU_601: /* tlbie invalidate TLBs for all segments */ @@ -2011,9 +1975,11 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) break; #if defined(TARGET_PPC64) case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: case POWERPC_MMU_2_06: case POWERPC_MMU_2_06a: - case POWERPC_MMU_2_06d: + case POWERPC_MMU_2_07: + case POWERPC_MMU_2_07a: /* tlbie invalidate TLBs for all segments */ /* XXX: given the fact that there are too many segments to invalidate, * and we still don't have a tlb_flush_mask(env, n, mask) in QEMU, @@ -2023,9 +1989,8 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) break; #endif /* defined(TARGET_PPC64) */ default: - /* XXX: TODO */ - cpu_abort(CPU(cpu), "Unknown MMU model\n"); - break; + /* Should never reach here with other MMU models */ + assert(0); } #else ppc_tlb_invalidate_all(env); @@ -2041,15 +2006,14 @@ void ppc_store_sdr1(CPUPPCState *env, target_ulong value) env->spr[SPR_SDR1] = value; #if defined(TARGET_PPC64) if (env->mmu_model & POWERPC_MMU_64) { - target_ulong htabsize = value & SDR_64_HTABSIZE; + PowerPCCPU *cpu = ppc_env_get_cpu(env); + Error *local_err = NULL; - if (htabsize > 28) { - fprintf(stderr, "Invalid HTABSIZE 0x" TARGET_FMT_lx - " stored in SDR1\n", htabsize); - htabsize = 28; + ppc_hash64_set_sdr1(cpu, value, &local_err); + if (local_err) { + error_report_err(local_err); + error_free(local_err); } - env->htab_mask = (1ULL << (htabsize + 18 - 7)) - 1; - env->htab_base = value & SDR_64_HTABORG; } else #endif /* defined(TARGET_PPC64) */ { @@ -2080,21 +2044,17 @@ void helper_store_sr(CPUPPCState *env, target_ulong srnum, target_ulong value) (int)srnum, value, env->sr[srnum]); #if defined(TARGET_PPC64) if (env->mmu_model & POWERPC_MMU_64) { - uint64_t rb = 0, rs = 0; + uint64_t esid, vsid; /* ESID = srnum */ - rb |= ((uint32_t)srnum & 0xf) << 28; - /* Set the valid bit */ - rb |= SLB_ESID_V; - /* Index = ESID */ - rb |= (uint32_t)srnum; + esid = ((uint64_t)(srnum & 0xf) << 28) | SLB_ESID_V; /* VSID = VSID */ - rs |= (value & 0xfffffff) << 12; + vsid = (value & 0xfffffff) << 12; /* flags = flags */ - rs |= ((value >> 27) & 0xf) << 8; + vsid |= ((value >> 27) & 0xf) << 8; - ppc_store_slb(env, rb, rs); + ppc_store_slb(cpu, srnum, esid, vsid); } else #endif if (env->sr[srnum] != value) { @@ -2128,6 +2088,16 @@ void helper_tlbie(CPUPPCState *env, target_ulong addr) ppc_tlb_invalidate_one(env, addr); } +void helper_tlbiva(CPUPPCState *env, target_ulong addr) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + + /* tlbiva instruction only exists on BookE */ + assert(env->mmu_model == POWERPC_MMU_BOOKE); + /* XXX: TODO */ + cpu_abort(CPU(cpu), "BookE MMU model is not implemented\n"); +} + /* Software driven TLBs management */ /* PowerPC 602/603 software TLB load instructions helpers */ static void do_6xx_tlb(CPUPPCState *env, target_ulong new_EPN, int is_code) diff --git a/qemu/target-ppc/monitor.c b/qemu/target-ppc/monitor.c new file mode 100644 index 000000000..c2d0806dd --- /dev/null +++ b/qemu/target-ppc/monitor.c @@ -0,0 +1,147 @@ +/* + * QEMU monitor + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "cpu.h" +#include "monitor/monitor.h" +#include "monitor/hmp-target.h" +#include "hmp.h" + +static target_long monitor_get_ccr (const struct MonitorDef *md, int val) +{ + CPUArchState *env = mon_get_cpu_env(); + unsigned int u; + int i; + + u = 0; + for (i = 0; i < 8; i++) + u |= env->crf[i] << (32 - (4 * (i + 1))); + + return u; +} + +static target_long monitor_get_decr (const struct MonitorDef *md, int val) +{ + CPUArchState *env = mon_get_cpu_env(); + return cpu_ppc_load_decr(env); +} + +static target_long monitor_get_tbu (const struct MonitorDef *md, int val) +{ + CPUArchState *env = mon_get_cpu_env(); + return cpu_ppc_load_tbu(env); +} + +static target_long monitor_get_tbl (const struct MonitorDef *md, int val) +{ + CPUArchState *env = mon_get_cpu_env(); + return cpu_ppc_load_tbl(env); +} + +void hmp_info_tlb(Monitor *mon, const QDict *qdict) +{ + CPUArchState *env1 = mon_get_cpu_env(); + + dump_mmu((FILE*)mon, (fprintf_function)monitor_printf, env1); +} + +const MonitorDef monitor_defs[] = { + { "fpscr", offsetof(CPUPPCState, fpscr) }, + /* Next instruction pointer */ + { "nip|pc", offsetof(CPUPPCState, nip) }, + { "lr", offsetof(CPUPPCState, lr) }, + { "ctr", offsetof(CPUPPCState, ctr) }, + { "decr", 0, &monitor_get_decr, }, + { "ccr|cr", 0, &monitor_get_ccr, }, + /* Machine state register */ + { "xer", offsetof(CPUPPCState, xer) }, + { "msr", offsetof(CPUPPCState, msr) }, + { "tbu", 0, &monitor_get_tbu, }, + { "tbl", 0, &monitor_get_tbl, }, + { NULL }, +}; + +const MonitorDef *target_monitor_defs(void) +{ + return monitor_defs; +} + +static int ppc_cpu_get_reg_num(const char *numstr, int maxnum, int *pregnum) +{ + int regnum; + char *endptr = NULL; + + if (!*numstr) { + return false; + } + + regnum = strtoul(numstr, &endptr, 10); + if (*endptr || (regnum >= maxnum)) { + return false; + } + *pregnum = regnum; + + return true; +} + +int target_get_monitor_def(CPUState *cs, const char *name, uint64_t *pval) +{ + int i, regnum; + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + /* General purpose registers */ + if ((tolower(name[0]) == 'r') && + ppc_cpu_get_reg_num(name + 1, ARRAY_SIZE(env->gpr), ®num)) { + *pval = env->gpr[regnum]; + return 0; + } + + /* Floating point registers */ + if ((tolower(name[0]) == 'f') && + ppc_cpu_get_reg_num(name + 1, ARRAY_SIZE(env->fpr), ®num)) { + *pval = env->fpr[regnum]; + return 0; + } + + /* Special purpose registers */ + for (i = 0; i < ARRAY_SIZE(env->spr_cb); ++i) { + ppc_spr_t *spr = &env->spr_cb[i]; + + if (spr->name && (strcasecmp(name, spr->name) == 0)) { + *pval = env->spr[i]; + return 0; + } + } + + /* Segment registers */ +#if !defined(CONFIG_USER_ONLY) + if ((strncasecmp(name, "sr", 2) == 0) && + ppc_cpu_get_reg_num(name + 2, ARRAY_SIZE(env->sr), ®num)) { + *pval = env->sr[regnum]; + return 0; + } +#endif + + return -EINVAL; +} diff --git a/qemu/target-ppc/timebase_helper.c b/qemu/target-ppc/timebase_helper.c index 865dcbed2..3b340d70d 100644 --- a/qemu/target-ppc/timebase_helper.c +++ b/qemu/target-ppc/timebase_helper.c @@ -16,6 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" @@ -130,13 +131,14 @@ target_ulong helper_load_dcr(CPUPPCState *env, target_ulong dcrn) uint32_t val = 0; if (unlikely(env->dcr_env == NULL)) { - qemu_log("No DCR environment\n"); + qemu_log_mask(LOG_GUEST_ERROR, "No DCR environment\n"); helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL); } else if (unlikely(ppc_dcr_read(env->dcr_env, (uint32_t)dcrn, &val) != 0)) { - qemu_log("DCR read error %d %03x\n", (uint32_t)dcrn, (uint32_t)dcrn); + qemu_log_mask(LOG_GUEST_ERROR, "DCR read error %d %03x\n", + (uint32_t)dcrn, (uint32_t)dcrn); helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL | POWERPC_EXCP_PRIV_REG); } @@ -146,13 +148,14 @@ target_ulong helper_load_dcr(CPUPPCState *env, target_ulong dcrn) void helper_store_dcr(CPUPPCState *env, target_ulong dcrn, target_ulong val) { if (unlikely(env->dcr_env == NULL)) { - qemu_log("No DCR environment\n"); + qemu_log_mask(LOG_GUEST_ERROR, "No DCR environment\n"); helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL); } else if (unlikely(ppc_dcr_write(env->dcr_env, (uint32_t)dcrn, (uint32_t)val) != 0)) { - qemu_log("DCR write error %d %03x\n", (uint32_t)dcrn, (uint32_t)dcrn); + qemu_log_mask(LOG_GUEST_ERROR, "DCR write error %d %03x\n", + (uint32_t)dcrn, (uint32_t)dcrn); helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL | POWERPC_EXCP_PRIV_REG); } diff --git a/qemu/target-ppc/translate.c b/qemu/target-ppc/translate.c index 84c5cead6..b3860ecde 100644 --- a/qemu/target-ppc/translate.c +++ b/qemu/target-ppc/translate.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "disas/disas.h" #include "tcg-op.h" @@ -28,6 +29,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" #define CPU_SINGLE_STEP 0x1 @@ -47,7 +49,7 @@ /* Code translation helpers */ /* global register indexes */ -static TCGv_ptr cpu_env; +static TCGv_env cpu_env; static char cpu_reg_names[10*3 + 22*4 /* GPR */ + 10*4 + 22*5 /* SPE GPRh */ + 10*4 + 22*5 /* FPR */ @@ -91,7 +93,7 @@ void ppc_translate_init(void) for (i = 0; i < 8; i++) { snprintf(p, cpu_reg_names_size, "crf%d", i); - cpu_crf[i] = tcg_global_mem_new_i32(TCG_AREG0, + cpu_crf[i] = tcg_global_mem_new_i32(cpu_env, offsetof(CPUPPCState, crf[i]), p); p += 5; cpu_reg_names_size -= 5; @@ -99,28 +101,28 @@ void ppc_translate_init(void) for (i = 0; i < 32; i++) { snprintf(p, cpu_reg_names_size, "r%d", i); - cpu_gpr[i] = tcg_global_mem_new(TCG_AREG0, + cpu_gpr[i] = tcg_global_mem_new(cpu_env, offsetof(CPUPPCState, gpr[i]), p); p += (i < 10) ? 3 : 4; cpu_reg_names_size -= (i < 10) ? 3 : 4; snprintf(p, cpu_reg_names_size, "r%dH", i); - cpu_gprh[i] = tcg_global_mem_new(TCG_AREG0, + cpu_gprh[i] = tcg_global_mem_new(cpu_env, offsetof(CPUPPCState, gprh[i]), p); p += (i < 10) ? 4 : 5; cpu_reg_names_size -= (i < 10) ? 4 : 5; snprintf(p, cpu_reg_names_size, "fp%d", i); - cpu_fpr[i] = tcg_global_mem_new_i64(TCG_AREG0, + cpu_fpr[i] = tcg_global_mem_new_i64(cpu_env, offsetof(CPUPPCState, fpr[i]), p); p += (i < 10) ? 4 : 5; cpu_reg_names_size -= (i < 10) ? 4 : 5; snprintf(p, cpu_reg_names_size, "avr%dH", i); #ifdef HOST_WORDS_BIGENDIAN - cpu_avrh[i] = tcg_global_mem_new_i64(TCG_AREG0, + cpu_avrh[i] = tcg_global_mem_new_i64(cpu_env, offsetof(CPUPPCState, avr[i].u64[0]), p); #else - cpu_avrh[i] = tcg_global_mem_new_i64(TCG_AREG0, + cpu_avrh[i] = tcg_global_mem_new_i64(cpu_env, offsetof(CPUPPCState, avr[i].u64[1]), p); #endif p += (i < 10) ? 6 : 7; @@ -128,55 +130,55 @@ void ppc_translate_init(void) snprintf(p, cpu_reg_names_size, "avr%dL", i); #ifdef HOST_WORDS_BIGENDIAN - cpu_avrl[i] = tcg_global_mem_new_i64(TCG_AREG0, + cpu_avrl[i] = tcg_global_mem_new_i64(cpu_env, offsetof(CPUPPCState, avr[i].u64[1]), p); #else - cpu_avrl[i] = tcg_global_mem_new_i64(TCG_AREG0, + cpu_avrl[i] = tcg_global_mem_new_i64(cpu_env, offsetof(CPUPPCState, avr[i].u64[0]), p); #endif p += (i < 10) ? 6 : 7; cpu_reg_names_size -= (i < 10) ? 6 : 7; snprintf(p, cpu_reg_names_size, "vsr%d", i); - cpu_vsr[i] = tcg_global_mem_new_i64(TCG_AREG0, - offsetof(CPUPPCState, vsr[i]), p); + cpu_vsr[i] = tcg_global_mem_new_i64(cpu_env, + offsetof(CPUPPCState, vsr[i]), p); p += (i < 10) ? 5 : 6; cpu_reg_names_size -= (i < 10) ? 5 : 6; } - cpu_nip = tcg_global_mem_new(TCG_AREG0, + cpu_nip = tcg_global_mem_new(cpu_env, offsetof(CPUPPCState, nip), "nip"); - cpu_msr = tcg_global_mem_new(TCG_AREG0, + cpu_msr = tcg_global_mem_new(cpu_env, offsetof(CPUPPCState, msr), "msr"); - cpu_ctr = tcg_global_mem_new(TCG_AREG0, + cpu_ctr = tcg_global_mem_new(cpu_env, offsetof(CPUPPCState, ctr), "ctr"); - cpu_lr = tcg_global_mem_new(TCG_AREG0, + cpu_lr = tcg_global_mem_new(cpu_env, offsetof(CPUPPCState, lr), "lr"); #if defined(TARGET_PPC64) - cpu_cfar = tcg_global_mem_new(TCG_AREG0, + cpu_cfar = tcg_global_mem_new(cpu_env, offsetof(CPUPPCState, cfar), "cfar"); #endif - cpu_xer = tcg_global_mem_new(TCG_AREG0, + cpu_xer = tcg_global_mem_new(cpu_env, offsetof(CPUPPCState, xer), "xer"); - cpu_so = tcg_global_mem_new(TCG_AREG0, + cpu_so = tcg_global_mem_new(cpu_env, offsetof(CPUPPCState, so), "SO"); - cpu_ov = tcg_global_mem_new(TCG_AREG0, + cpu_ov = tcg_global_mem_new(cpu_env, offsetof(CPUPPCState, ov), "OV"); - cpu_ca = tcg_global_mem_new(TCG_AREG0, + cpu_ca = tcg_global_mem_new(cpu_env, offsetof(CPUPPCState, ca), "CA"); - cpu_reserve = tcg_global_mem_new(TCG_AREG0, + cpu_reserve = tcg_global_mem_new(cpu_env, offsetof(CPUPPCState, reserve_addr), "reserve_addr"); - cpu_fpscr = tcg_global_mem_new(TCG_AREG0, + cpu_fpscr = tcg_global_mem_new(cpu_env, offsetof(CPUPPCState, fpscr), "fpscr"); - cpu_access_type = tcg_global_mem_new_i32(TCG_AREG0, + cpu_access_type = tcg_global_mem_new_i32(cpu_env, offsetof(CPUPPCState, access_type), "access_type"); done_init = 1; @@ -2500,18 +2502,31 @@ static void gen_fmrgow(DisasContext *ctx) static void gen_mcrfs(DisasContext *ctx) { TCGv tmp = tcg_temp_new(); + TCGv_i32 tmask; + TCGv_i64 tnew_fpscr = tcg_temp_new_i64(); int bfa; + int nibble; + int shift; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; } - bfa = 4 * (7 - crfS(ctx->opcode)); - tcg_gen_shri_tl(tmp, cpu_fpscr, bfa); + bfa = crfS(ctx->opcode); + nibble = 7 - bfa; + shift = 4 * nibble; + tcg_gen_shri_tl(tmp, cpu_fpscr, shift); tcg_gen_trunc_tl_i32(cpu_crf[crfD(ctx->opcode)], tmp); - tcg_temp_free(tmp); tcg_gen_andi_i32(cpu_crf[crfD(ctx->opcode)], cpu_crf[crfD(ctx->opcode)], 0xf); - tcg_gen_andi_tl(cpu_fpscr, cpu_fpscr, ~(0xF << bfa)); + tcg_temp_free(tmp); + tcg_gen_extu_tl_i64(tnew_fpscr, cpu_fpscr); + /* Only the exception bits (including FX) should be cleared if read */ + tcg_gen_andi_i64(tnew_fpscr, tnew_fpscr, ~((0xF << shift) & FP_EX_CLEAR_BITS)); + /* FEX and VX need to be updated, so don't set fpscr directly */ + tmask = tcg_const_i32(1 << nibble); + gen_helper_store_fpscr(cpu_env, tnew_fpscr, tmask); + tcg_temp_free_i32(tmask); + tcg_temp_free_i64(tnew_fpscr); } /* mffs */ @@ -3212,10 +3227,8 @@ static void gen_lswi(DisasContext *ctx) if (nb == 0) nb = 32; - nr = nb / 4; - if (unlikely(((start + nr) > 32 && - start <= ra && (start + nr - 32) > ra) || - ((start + nr) <= 32 && start <= ra && (start + nr) > ra))) { + nr = (nb + 3) / 4; + if (unlikely(lsw_reg_in_range(start, nr, ra))) { gen_inval_exception(ctx, POWERPC_EXCP_INVAL_LSWX); return; } @@ -4267,14 +4280,17 @@ static inline void gen_op_mfspr(DisasContext *ctx) void (*read_cb)(DisasContext *ctx, int gprn, int sprn); uint32_t sprn = SPR(ctx->opcode); -#if !defined(CONFIG_USER_ONLY) - if (ctx->hv) +#if defined(CONFIG_USER_ONLY) + read_cb = ctx->spr_cb[sprn].uea_read; +#else + if (ctx->pr) { + read_cb = ctx->spr_cb[sprn].uea_read; + } else if (ctx->hv) { read_cb = ctx->spr_cb[sprn].hea_read; - else if (!ctx->pr) + } else { read_cb = ctx->spr_cb[sprn].oea_read; - else + } #endif - read_cb = ctx->spr_cb[sprn].uea_read; if (likely(read_cb != NULL)) { if (likely(read_cb != SPR_NOACCESS)) { (*read_cb)(ctx, rD(ctx->opcode), sprn); @@ -4285,19 +4301,23 @@ static inline void gen_op_mfspr(DisasContext *ctx) * allowing userland application to read the PVR */ if (sprn != SPR_PVR) { - qemu_log("Trying to read privileged spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); - printf("Trying to read privileged spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + fprintf(stderr, "Trying to read privileged spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + if (qemu_log_separate()) { + qemu_log("Trying to read privileged spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + } } gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); } } else { /* Not defined */ - qemu_log("Trying to read invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); - printf("Trying to read invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + fprintf(stderr, "Trying to read invalid spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + if (qemu_log_separate()) { + qemu_log("Trying to read invalid spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + } gen_inval_exception(ctx, POWERPC_EXCP_INVAL_SPR); } } @@ -4418,31 +4438,38 @@ static void gen_mtspr(DisasContext *ctx) void (*write_cb)(DisasContext *ctx, int sprn, int gprn); uint32_t sprn = SPR(ctx->opcode); -#if !defined(CONFIG_USER_ONLY) - if (ctx->hv) +#if defined(CONFIG_USER_ONLY) + write_cb = ctx->spr_cb[sprn].uea_write; +#else + if (ctx->pr) { + write_cb = ctx->spr_cb[sprn].uea_write; + } else if (ctx->hv) { write_cb = ctx->spr_cb[sprn].hea_write; - else if (!ctx->pr) + } else { write_cb = ctx->spr_cb[sprn].oea_write; - else + } #endif - write_cb = ctx->spr_cb[sprn].uea_write; if (likely(write_cb != NULL)) { if (likely(write_cb != SPR_NOACCESS)) { (*write_cb)(ctx, sprn, rS(ctx->opcode)); } else { /* Privilege exception */ - qemu_log("Trying to write privileged spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); - printf("Trying to write privileged spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + fprintf(stderr, "Trying to write privileged spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + if (qemu_log_separate()) { + qemu_log("Trying to write privileged spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + } gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); } } else { /* Not defined */ - qemu_log("Trying to write invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); - printf("Trying to write invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + if (qemu_log_separate()) { + qemu_log("Trying to write invalid spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + } + fprintf(stderr, "Trying to write invalid spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); gen_inval_exception(ctx, POWERPC_EXCP_INVAL_SPR); } } @@ -5896,7 +5923,7 @@ static void gen_tlbiva(DisasContext *ctx) } t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); + gen_helper_tlbiva(cpu_env, cpu_gpr[rB(ctx->opcode)]); tcg_temp_free(t0); #endif } @@ -9884,7 +9911,7 @@ GEN_HANDLER(mtcrf, 0x1F, 0x10, 0x04, 0x00000801, PPC_MISC), GEN_HANDLER(mtmsrd, 0x1F, 0x12, 0x05, 0x001EF801, PPC_64B), #endif GEN_HANDLER(mtmsr, 0x1F, 0x12, 0x04, 0x001FF801, PPC_MISC), -GEN_HANDLER(mtspr, 0x1F, 0x13, 0x0E, 0x00000001, PPC_MISC), +GEN_HANDLER(mtspr, 0x1F, 0x13, 0x0E, 0x00000000, PPC_MISC), GEN_HANDLER(dcbf, 0x1F, 0x16, 0x02, 0x03C00001, PPC_CACHE), GEN_HANDLER(dcbi, 0x1F, 0x16, 0x0E, 0x03E00001, PPC_CACHE), GEN_HANDLER(dcbst, 0x1F, 0x16, 0x01, 0x03E00001, PPC_CACHE), @@ -10670,6 +10697,13 @@ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2), \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 2, opc3, 0, PPC_NONE, fl2), \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 3, opc3, 0, PPC_NONE, fl2) +#undef GEN_XX2IFORM +#define GEN_XX2IFORM(name, opc2, opc3, fl2) \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 1, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 1, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 2, opc3, 1, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 3, opc3, 1, PPC_NONE, fl2) + #undef GEN_XX3_RC_FORM #define GEN_XX3_RC_FORM(name, opc2, opc3, fl2) \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x00, opc3 | 0x00, 0, PPC_NONE, fl2), \ @@ -10731,8 +10765,8 @@ GEN_XX3FORM(xsnmaddadp, 0x04, 0x14, PPC2_VSX), GEN_XX3FORM(xsnmaddmdp, 0x04, 0x15, PPC2_VSX), GEN_XX3FORM(xsnmsubadp, 0x04, 0x16, PPC2_VSX), GEN_XX3FORM(xsnmsubmdp, 0x04, 0x17, PPC2_VSX), -GEN_XX2FORM(xscmpodp, 0x0C, 0x05, PPC2_VSX), -GEN_XX2FORM(xscmpudp, 0x0C, 0x04, PPC2_VSX), +GEN_XX2IFORM(xscmpodp, 0x0C, 0x05, PPC2_VSX), +GEN_XX2IFORM(xscmpudp, 0x0C, 0x04, PPC2_VSX), GEN_XX3FORM(xsmaxdp, 0x00, 0x14, PPC2_VSX), GEN_XX3FORM(xsmindp, 0x00, 0x15, PPC2_VSX), GEN_XX2FORM(xscvdpsp, 0x12, 0x10, PPC2_VSX), @@ -11320,9 +11354,11 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, case POWERPC_MMU_SOFT_74xx: #if defined(TARGET_PPC64) case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: case POWERPC_MMU_2_06: case POWERPC_MMU_2_06a: - case POWERPC_MMU_2_06d: + case POWERPC_MMU_2_07: + case POWERPC_MMU_2_07a: #endif cpu_fprintf(f, " SDR1 " TARGET_FMT_lx " DAR " TARGET_FMT_lx " DSISR " TARGET_FMT_lx "\n", env->spr[SPR_SDR1], @@ -11402,17 +11438,13 @@ void ppc_cpu_dump_statistics(CPUState *cs, FILE*f, } /*****************************************************************************/ -static inline void gen_intermediate_code_internal(PowerPCCPU *cpu, - TranslationBlock *tb, - bool search_pc) +void gen_intermediate_code(CPUPPCState *env, struct TranslationBlock *tb) { + PowerPCCPU *cpu = ppc_env_get_cpu(env); CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; DisasContext ctx, *ctxp = &ctx; opc_handler_t **table, *handler; target_ulong pc_start; - CPUBreakpoint *bp; - int j, lj = -1; int num_insns; int max_insns; @@ -11469,36 +11501,34 @@ static inline void gen_intermediate_code_internal(PowerPCCPU *cpu, #endif num_insns = 0; max_insns = tb->cflags & CF_COUNT_MASK; - if (max_insns == 0) + if (max_insns == 0) { max_insns = CF_COUNT_MASK; + } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } gen_tb_start(tb); tcg_clear_temp_count(); /* Set env in case of segfault during code fetch */ while (ctx.exception == POWERPC_EXCP_NONE && !tcg_op_buf_full()) { - if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { - QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - if (bp->pc == ctx.nip) { - gen_debug_exception(ctxp); - break; - } - } - } - if (unlikely(search_pc)) { - j = tcg_op_buf_count(); - if (lj < j) { - lj++; - while (lj < j) - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - tcg_ctx.gen_opc_pc[lj] = ctx.nip; - tcg_ctx.gen_opc_instr_start[lj] = 1; - tcg_ctx.gen_opc_icount[lj] = num_insns; + tcg_gen_insn_start(ctx.nip); + num_insns++; + + if (unlikely(cpu_breakpoint_test(cs, ctx.nip, BP_ANY))) { + gen_debug_exception(ctxp); + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + ctx.nip += 4; + break; } + LOG_DISAS("----------------\n"); LOG_DISAS("nip=" TARGET_FMT_lx " super=%d ir=%d\n", ctx.nip, ctx.mem_idx, (int)msr_ir); - if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) + if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) gen_io_start(); if (unlikely(need_byteswap(&ctx))) { ctx.opcode = bswap32(cpu_ldl_code(env, ctx.nip)); @@ -11508,12 +11538,8 @@ static inline void gen_intermediate_code_internal(PowerPCCPU *cpu, LOG_DISAS("translate opcode %08x (%02x %02x %02x) (%s)\n", ctx.opcode, opc1(ctx.opcode), opc2(ctx.opcode), opc3(ctx.opcode), ctx.le_mode ? "little" : "big"); - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { - tcg_gen_debug_insn_start(ctx.nip); - } ctx.nip += 4; table = env->opcodes; - num_insns++; handler = table[opc1(ctx.opcode)]; if (is_indirect_opcode(handler)) { table = ind_table(handler); @@ -11525,12 +11551,10 @@ static inline void gen_intermediate_code_internal(PowerPCCPU *cpu, } /* Is opcode *REALLY* valid ? */ if (unlikely(handler->handler == &gen_invalid)) { - if (qemu_log_enabled()) { - qemu_log("invalid/unsupported opcode: " - "%02x - %02x - %02x (%08x) " TARGET_FMT_lx " %d\n", - opc1(ctx.opcode), opc2(ctx.opcode), - opc3(ctx.opcode), ctx.opcode, ctx.nip - 4, (int)msr_ir); - } + qemu_log_mask(LOG_GUEST_ERROR, "invalid/unsupported opcode: " + "%02x - %02x - %02x (%08x) " TARGET_FMT_lx " %d\n", + opc1(ctx.opcode), opc2(ctx.opcode), + opc3(ctx.opcode), ctx.opcode, ctx.nip - 4, (int)msr_ir); } else { uint32_t inval; @@ -11541,13 +11565,11 @@ static inline void gen_intermediate_code_internal(PowerPCCPU *cpu, } if (unlikely((ctx.opcode & inval) != 0)) { - if (qemu_log_enabled()) { - qemu_log("invalid bits: %08x for opcode: " - "%02x - %02x - %02x (%08x) " TARGET_FMT_lx "\n", - ctx.opcode & inval, opc1(ctx.opcode), - opc2(ctx.opcode), opc3(ctx.opcode), - ctx.opcode, ctx.nip - 4); - } + qemu_log_mask(LOG_GUEST_ERROR, "invalid bits: %08x for opcode: " + "%02x - %02x - %02x (%08x) " TARGET_FMT_lx "\n", + ctx.opcode & inval, opc1(ctx.opcode), + opc2(ctx.opcode), opc3(ctx.opcode), + ctx.opcode, ctx.nip - 4); gen_inval_exception(ctxp, POWERPC_EXCP_INVAL_INVAL); break; } @@ -11592,15 +11614,9 @@ static inline void gen_intermediate_code_internal(PowerPCCPU *cpu, } gen_tb_end(tb, num_insns); - if (unlikely(search_pc)) { - j = tcg_op_buf_count(); - lj++; - while (lj <= j) - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } else { - tb->size = ctx.nip - pc_start; - tb->icount = num_insns; - } + tb->size = ctx.nip - pc_start; + tb->icount = num_insns; + #if defined(DEBUG_DISAS) if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { int flags; @@ -11613,17 +11629,8 @@ static inline void gen_intermediate_code_internal(PowerPCCPU *cpu, #endif } -void gen_intermediate_code (CPUPPCState *env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(ppc_env_get_cpu(env), tb, false); -} - -void gen_intermediate_code_pc (CPUPPCState *env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(ppc_env_get_cpu(env), tb, true); -} - -void restore_state_to_opc(CPUPPCState *env, TranslationBlock *tb, int pc_pos) +void restore_state_to_opc(CPUPPCState *env, TranslationBlock *tb, + target_ulong *data) { - env->nip = tcg_ctx.gen_opc_pc[pc_pos]; + env->nip = data[0]; } diff --git a/qemu/target-ppc/translate_init.c b/qemu/target-ppc/translate_init.c index 16d7b16ac..f51572552 100644 --- a/qemu/target-ppc/translate_init.c +++ b/qemu/target-ppc/translate_init.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "disas/bfd.h" #include "exec/gdbstub.h" #include @@ -305,7 +306,7 @@ static void spr_read_ibat (DisasContext *ctx, int gprn, int sprn) static void spr_read_ibat_h (DisasContext *ctx, int gprn, int sprn) { - tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, IBAT[sprn & 1][(sprn - SPR_IBAT4U) / 2])); + tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, IBAT[sprn & 1][((sprn - SPR_IBAT4U) / 2) + 4])); } static void spr_write_ibatu (DisasContext *ctx, int sprn, int gprn) @@ -578,17 +579,33 @@ static inline void vscr_init (CPUPPCState *env, uint32_t val) #define spr_register_kvm(env, num, name, uea_read, uea_write, \ oea_read, oea_write, one_reg_id, initial_value) \ _spr_register(env, num, name, uea_read, uea_write, initial_value) +#define spr_register_kvm_hv(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, hea_read, hea_write, \ + one_reg_id, initial_value) \ + _spr_register(env, num, name, uea_read, uea_write, initial_value) #else #if !defined(CONFIG_KVM) #define spr_register_kvm(env, num, name, uea_read, uea_write, \ - oea_read, oea_write, one_reg_id, initial_value) \ + oea_read, oea_write, one_reg_id, initial_value) \ + _spr_register(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, oea_read, oea_write, initial_value) +#define spr_register_kvm_hv(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, hea_read, hea_write, \ + one_reg_id, initial_value) \ _spr_register(env, num, name, uea_read, uea_write, \ - oea_read, oea_write, initial_value) + oea_read, oea_write, hea_read, hea_write, initial_value) #else #define spr_register_kvm(env, num, name, uea_read, uea_write, \ - oea_read, oea_write, one_reg_id, initial_value) \ + oea_read, oea_write, one_reg_id, initial_value) \ + _spr_register(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, oea_read, oea_write, \ + one_reg_id, initial_value) +#define spr_register_kvm_hv(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, hea_read, hea_write, \ + one_reg_id, initial_value) \ _spr_register(env, num, name, uea_read, uea_write, \ - oea_read, oea_write, one_reg_id, initial_value) + oea_read, oea_write, hea_read, hea_write, \ + one_reg_id, initial_value) #endif #endif @@ -597,6 +614,13 @@ static inline void vscr_init (CPUPPCState *env, uint32_t val) spr_register_kvm(env, num, name, uea_read, uea_write, \ oea_read, oea_write, 0, initial_value) +#define spr_register_hv(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, hea_read, hea_write, \ + initial_value) \ + spr_register_kvm_hv(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, hea_read, hea_write, \ + 0, initial_value) + static inline void _spr_register(CPUPPCState *env, int num, const char *name, void (*uea_read)(DisasContext *ctx, int gprn, int sprn), @@ -605,6 +629,8 @@ static inline void _spr_register(CPUPPCState *env, int num, void (*oea_read)(DisasContext *ctx, int gprn, int sprn), void (*oea_write)(DisasContext *ctx, int sprn, int gprn), + void (*hea_read)(DisasContext *opaque, int gprn, int sprn), + void (*hea_write)(DisasContext *opaque, int sprn, int gprn), #endif #if defined(CONFIG_KVM) uint64_t one_reg_id, @@ -632,6 +658,8 @@ static inline void _spr_register(CPUPPCState *env, int num, #if !defined(CONFIG_USER_ONLY) spr->oea_read = oea_read; spr->oea_write = oea_write; + spr->hea_read = hea_read; + spr->hea_write = hea_write; #endif #if defined(CONFIG_KVM) spr->one_reg_id = one_reg_id, @@ -1035,30 +1063,102 @@ static void gen_spr_7xx (CPUPPCState *env) #ifdef TARGET_PPC64 #ifndef CONFIG_USER_ONLY -static void spr_read_uamr (DisasContext *ctx, int gprn, int sprn) +static void spr_write_amr(DisasContext *ctx, int sprn, int gprn) { - gen_load_spr(cpu_gpr[gprn], SPR_AMR); - spr_load_dump_spr(SPR_AMR); -} + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); -static void spr_write_uamr (DisasContext *ctx, int sprn, int gprn) -{ - gen_store_spr(SPR_AMR, cpu_gpr[gprn]); + /* Note, the HV=1 PR=0 case is handled earlier by simply using + * spr_write_generic for HV mode in the SPR table + */ + + /* Build insertion mask into t1 based on context */ + if (ctx->pr) { + gen_load_spr(t1, SPR_UAMOR); + } else { + gen_load_spr(t1, SPR_AMOR); + } + + /* Mask new bits into t2 */ + tcg_gen_and_tl(t2, t1, cpu_gpr[gprn]); + + /* Load AMR and clear new bits in t0 */ + gen_load_spr(t0, SPR_AMR); + tcg_gen_andc_tl(t0, t0, t1); + + /* Or'in new bits and write it out */ + tcg_gen_or_tl(t0, t0, t2); + gen_store_spr(SPR_AMR, t0); spr_store_dump_spr(SPR_AMR); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); } -static void spr_write_uamr_pr (DisasContext *ctx, int sprn, int gprn) +static void spr_write_uamor(DisasContext *ctx, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + /* Note, the HV=1 case is handled earlier by simply using + * spr_write_generic for HV mode in the SPR table + */ + + /* Build insertion mask into t1 based on context */ + gen_load_spr(t1, SPR_AMOR); + /* Mask new bits into t2 */ + tcg_gen_and_tl(t2, t1, cpu_gpr[gprn]); + + /* Load AMR and clear new bits in t0 */ gen_load_spr(t0, SPR_UAMOR); - tcg_gen_and_tl(t0, t0, cpu_gpr[gprn]); - gen_store_spr(SPR_AMR, t0); - spr_store_dump_spr(SPR_AMR); + tcg_gen_andc_tl(t0, t0, t1); + + /* Or'in new bits and write it out */ + tcg_gen_or_tl(t0, t0, t2); + gen_store_spr(SPR_UAMOR, t0); + spr_store_dump_spr(SPR_UAMOR); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); +} + +static void spr_write_iamr(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + /* Note, the HV=1 case is handled earlier by simply using + * spr_write_generic for HV mode in the SPR table + */ + + /* Build insertion mask into t1 based on context */ + gen_load_spr(t1, SPR_AMOR); + + /* Mask new bits into t2 */ + tcg_gen_and_tl(t2, t1, cpu_gpr[gprn]); + + /* Load AMR and clear new bits in t0 */ + gen_load_spr(t0, SPR_IAMR); + tcg_gen_andc_tl(t0, t0, t1); + + /* Or'in new bits and write it out */ + tcg_gen_or_tl(t0, t0, t2); + gen_store_spr(SPR_IAMR, t0); + spr_store_dump_spr(SPR_IAMR); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); } #endif /* CONFIG_USER_ONLY */ -static void gen_spr_amr (CPUPPCState *env) +static void gen_spr_amr(CPUPPCState *env, bool has_iamr) { #ifndef CONFIG_USER_ONLY /* Virtual Page Class Key protection */ @@ -1066,17 +1166,31 @@ static void gen_spr_amr (CPUPPCState *env) * userspace accessible, 29 is privileged. So we only need to set * the kvm ONE_REG id on one of them, we use 29 */ spr_register(env, SPR_UAMR, "UAMR", - &spr_read_uamr, &spr_write_uamr_pr, - &spr_read_uamr, &spr_write_uamr, + &spr_read_generic, &spr_write_amr, + &spr_read_generic, &spr_write_amr, 0); - spr_register_kvm(env, SPR_AMR, "AMR", + spr_register_kvm_hv(env, SPR_AMR, "AMR", SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_amr, &spr_read_generic, &spr_write_generic, KVM_REG_PPC_AMR, 0); - spr_register_kvm(env, SPR_UAMOR, "UAMOR", + spr_register_kvm_hv(env, SPR_UAMOR, "UAMOR", SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_uamor, &spr_read_generic, &spr_write_generic, KVM_REG_PPC_UAMOR, 0); + spr_register_hv(env, SPR_AMOR, "AMOR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0); + if (has_iamr) { + spr_register_kvm_hv(env, SPR_IAMR, "IAMR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_iamr, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_IAMR, 0); + } #endif /* !CONFIG_USER_ONLY */ } #endif /* TARGET_PPC64 */ @@ -7463,6 +7577,25 @@ static void gen_spr_book3s_dbg(CPUPPCState *env) KVM_REG_PPC_DABRX, 0x00000000); } +static void gen_spr_book3s_207_dbg(CPUPPCState *env) +{ + spr_register_kvm_hv(env, SPR_DAWR, "DAWR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DAWR, 0x00000000); + spr_register_kvm_hv(env, SPR_DAWRX, "DAWRX", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DAWRX, 0x00000000); + spr_register_kvm_hv(env, SPR_CIABR, "CIABR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_CIABR, 0x00000000); +} + static void gen_spr_970_dbg(CPUPPCState *env) { /* Breakpoints */ @@ -7602,6 +7735,30 @@ static void gen_spr_power8_pmu_sup(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, KVM_REG_PPC_MMCRS, 0x00000000); + spr_register_kvm(env, SPR_POWER_SIER, "SIER", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_SIER, 0x00000000); + spr_register_kvm(env, SPR_POWER_SPMC1, "SPMC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_SPMC1, 0x00000000); + spr_register_kvm(env, SPR_POWER_SPMC2, "SPMC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_SPMC2, 0x00000000); + spr_register_kvm(env, SPR_TACR, "TACR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_TACR, 0x00000000); + spr_register_kvm(env, SPR_TCSCR, "TCSCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_TCSCR, 0x00000000); + spr_register_kvm(env, SPR_CSIGR, "CSIGR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_CSIGR, 0x00000000); } static void gen_spr_power8_pmu_user(CPUPPCState *env) @@ -7610,6 +7767,10 @@ static void gen_spr_power8_pmu_user(CPUPPCState *env) &spr_read_ureg, SPR_NOACCESS, &spr_read_ureg, &spr_write_ureg, 0x00000000); + spr_register(env, SPR_POWER_USIER, "USIER", + &spr_read_generic, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); } static void gen_spr_power5p_ear(CPUPPCState *env) @@ -7713,10 +7874,10 @@ static void spr_write_tar(DisasContext *ctx, int sprn, int gprn) static void gen_spr_power8_tce_address_control(CPUPPCState *env) { - spr_register(env, SPR_TAR, "TAR", - &spr_read_tar, &spr_write_tar, - &spr_read_generic, &spr_write_generic, - 0x00000000); + spr_register_kvm(env, SPR_TAR, "TAR", + &spr_read_tar, &spr_write_tar, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_TAR, 0x00000000); } static void spr_read_tm(DisasContext *ctx, int gprn, int sprn) @@ -7841,6 +8002,44 @@ static void gen_spr_power8_fscr(CPUPPCState *env) KVM_REG_PPC_FSCR, initval); } +static void gen_spr_power8_pspb(CPUPPCState *env) +{ + spr_register_kvm(env, SPR_PSPB, "PSPB", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic32, + KVM_REG_PPC_PSPB, 0); +} + +static void gen_spr_power8_ic(CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + spr_register_hv(env, SPR_IC, "IC", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0); +#endif +} + +static void gen_spr_power8_book4(CPUPPCState *env) +{ + /* Add a number of P8 book4 registers */ +#if !defined(CONFIG_USER_ONLY) + spr_register_kvm(env, SPR_ACOP, "ACOP", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_ACOP, 0); + spr_register_kvm(env, SPR_BOOKS_PID, "PID", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_PID, 0); + spr_register_kvm(env, SPR_WORT, "WORT", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_WORT, 0); +#endif +} + static void init_proc_book3s_64(CPUPPCState *env, int version) { gen_spr_ne_601(env); @@ -7862,8 +8061,9 @@ static void init_proc_book3s_64(CPUPPCState *env, int version) case BOOK3S_CPU_POWER7: case BOOK3S_CPU_POWER8: gen_spr_book3s_ids(env); - gen_spr_amr(env); + gen_spr_amr(env, version >= BOOK3S_CPU_POWER8); gen_spr_book3s_purr(env); + env->ci_large_pages = true; break; default: g_assert_not_reached(); @@ -7890,10 +8090,15 @@ static void init_proc_book3s_64(CPUPPCState *env, int version) gen_spr_power8_pmu_sup(env); gen_spr_power8_pmu_user(env); gen_spr_power8_tm(env); + gen_spr_power8_pspb(env); gen_spr_vtb(env); + gen_spr_power8_ic(env); + gen_spr_power8_book4(env); } if (version < BOOK3S_CPU_POWER8) { gen_spr_book3s_dbg(env); + } else { + gen_spr_book3s_207_dbg(env); } #if !defined(CONFIG_USER_ONLY) switch (version) { @@ -8019,7 +8224,7 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) (1ull << MSR_DR) | (1ull << MSR_PMM) | (1ull << MSR_RI); - pcc->mmu_model = POWERPC_MMU_64B; + pcc->mmu_model = POWERPC_MMU_2_03; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; #endif @@ -8033,8 +8238,8 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) pcc->l1_icache_size = 0x10000; } -static void powerpc_get_compat(Object *obj, Visitor *v, - void *opaque, const char *name, Error **errp) +static void powerpc_get_compat(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { char *value = (char *)""; Property *prop = opaque; @@ -8058,18 +8263,18 @@ static void powerpc_get_compat(Object *obj, Visitor *v, break; } - visit_type_str(v, &value, name, errp); + visit_type_str(v, name, &value, errp); } -static void powerpc_set_compat(Object *obj, Visitor *v, - void *opaque, const char *name, Error **errp) +static void powerpc_set_compat(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { Error *error = NULL; char *value = NULL; Property *prop = opaque; uint32_t *max_compat = qdev_get_prop_ptr(DEVICE(obj), prop); - visit_type_str(v, &value, name, &error); + visit_type_str(v, name, &value, &error); if (error) { error_propagate(errp, error); return; @@ -8103,6 +8308,36 @@ static Property powerpc_servercpu_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +#ifdef CONFIG_SOFTMMU +static const struct ppc_segment_page_sizes POWER7_POWER8_sps = { + .sps = { + { + .page_shift = 12, /* 4K */ + .slb_enc = 0, + .enc = { { .page_shift = 12, .pte_enc = 0 }, + { .page_shift = 16, .pte_enc = 0x7 }, + { .page_shift = 24, .pte_enc = 0x38 }, }, + }, + { + .page_shift = 16, /* 64K */ + .slb_enc = SLB_VSID_64K, + .enc = { { .page_shift = 16, .pte_enc = 0x1 }, + { .page_shift = 24, .pte_enc = 0x8 }, }, + }, + { + .page_shift = 24, /* 16M */ + .slb_enc = SLB_VSID_16M, + .enc = { { .page_shift = 24, .pte_enc = 0 }, }, + }, + { + .page_shift = 34, /* 16G */ + .slb_enc = SLB_VSID_16G, + .enc = { { .page_shift = 34, .pte_enc = 0x3 }, }, + }, + } +}; +#endif /* CONFIG_SOFTMMU */ + static void init_proc_POWER7 (CPUPPCState *env) { init_proc_book3s_64(env, BOOK3S_CPU_POWER7); @@ -8166,6 +8401,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) pcc->mmu_model = POWERPC_MMU_2_06; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; + pcc->sps = &POWER7_POWER8_sps; #endif pcc->excp_model = POWERPC_EXCP_POWER7; pcc->bus_model = PPC_FLAGS_INPUT_POWER7; @@ -8186,6 +8422,9 @@ static void init_proc_POWER8(CPUPPCState *env) static bool ppc_pvr_match_power8(PowerPCCPUClass *pcc, uint32_t pvr) { + if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER8NVL_BASE) { + return true; + } if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER8E_BASE) { return true; } @@ -8243,11 +8482,12 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) (1ull << MSR_PMM) | (1ull << MSR_RI) | (1ull << MSR_LE); - pcc->mmu_model = POWERPC_MMU_2_06; + pcc->mmu_model = POWERPC_MMU_2_07; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; + pcc->sps = &POWER7_POWER8_sps; #endif - pcc->excp_model = POWERPC_EXCP_POWER7; + pcc->excp_model = POWERPC_EXCP_POWER8; pcc->bus_model = PPC_FLAGS_INPUT_POWER7; pcc->bfd_mach = bfd_mach_ppc64; pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | @@ -8258,8 +8498,33 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) pcc->l1_icache_size = 0x8000; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; } -#endif /* defined (TARGET_PPC64) */ +#if !defined(CONFIG_USER_ONLY) + +void cpu_ppc_set_papr(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + ppc_spr_t *amor = &env->spr_cb[SPR_AMOR]; + + /* PAPR always has exception vectors in RAM not ROM. To ensure this, + * MSR[IP] should never be set. + * + * We also disallow setting of MSR_HV + */ + env->msr_mask &= ~((1ull << MSR_EP) | MSR_HVB); + + /* Set a full AMOR so guest can use the AMR as it sees fit */ + env->spr[SPR_AMOR] = amor->default_value = 0xffffffffffffffffull; + + /* Tell KVM that we're in PAPR mode */ + if (kvm_enabled()) { + kvmppc_set_papr(cpu); + } +} + +#endif /* !defined(CONFIG_USER_ONLY) */ + +#endif /* defined (TARGET_PPC64) */ /*****************************************************************************/ /* Generic CPU instantiation routine */ @@ -8470,8 +8735,6 @@ static void dump_ppc_sprs (CPUPPCState *env) #endif /*****************************************************************************/ -#include -#include /* Opcode types */ enum { @@ -8577,11 +8840,7 @@ static int register_ind_insn (opc_handler_t **ppc_opcodes, unsigned char idx1, unsigned char idx2, opc_handler_t *handler) { - int ret; - - ret = register_ind_in_table(ppc_opcodes, idx1, idx2, handler); - - return ret; + return register_ind_in_table(ppc_opcodes, idx1, idx2, handler); } static int register_dblind_insn (opc_handler_t **ppc_opcodes, @@ -8754,14 +9013,25 @@ static void dump_ppc_insns (CPUPPCState *env) } #endif +static bool avr_need_swap(CPUPPCState *env) +{ +#ifdef HOST_WORDS_BIGENDIAN + return msr_le; +#else + return !msr_le; +#endif +} + static int gdb_get_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n) { if (n < 32) { stfq_p(mem_buf, env->fpr[n]); + ppc_maybe_bswap_register(env, mem_buf, 8); return 8; } if (n == 32) { stl_p(mem_buf, env->fpscr); + ppc_maybe_bswap_register(env, mem_buf, 4); return 4; } return 0; @@ -8770,10 +9040,12 @@ static int gdb_get_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n) static int gdb_set_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n) { if (n < 32) { + ppc_maybe_bswap_register(env, mem_buf, 8); env->fpr[n] = ldfq_p(mem_buf); return 8; } if (n == 32) { + ppc_maybe_bswap_register(env, mem_buf, 4); helper_store_fpscr(env, ldl_p(mem_buf), 0xffffffff); return 4; } @@ -8783,21 +9055,25 @@ static int gdb_set_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n) static int gdb_get_avr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) { if (n < 32) { -#ifdef HOST_WORDS_BIGENDIAN - stq_p(mem_buf, env->avr[n].u64[0]); - stq_p(mem_buf+8, env->avr[n].u64[1]); -#else - stq_p(mem_buf, env->avr[n].u64[1]); - stq_p(mem_buf+8, env->avr[n].u64[0]); -#endif + if (!avr_need_swap(env)) { + stq_p(mem_buf, env->avr[n].u64[0]); + stq_p(mem_buf+8, env->avr[n].u64[1]); + } else { + stq_p(mem_buf, env->avr[n].u64[1]); + stq_p(mem_buf+8, env->avr[n].u64[0]); + } + ppc_maybe_bswap_register(env, mem_buf, 8); + ppc_maybe_bswap_register(env, mem_buf + 8, 8); return 16; } if (n == 32) { stl_p(mem_buf, env->vscr); + ppc_maybe_bswap_register(env, mem_buf, 4); return 4; } if (n == 33) { stl_p(mem_buf, (uint32_t)env->spr[SPR_VRSAVE]); + ppc_maybe_bswap_register(env, mem_buf, 4); return 4; } return 0; @@ -8806,20 +9082,24 @@ static int gdb_get_avr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) static int gdb_set_avr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) { if (n < 32) { -#ifdef HOST_WORDS_BIGENDIAN - env->avr[n].u64[0] = ldq_p(mem_buf); - env->avr[n].u64[1] = ldq_p(mem_buf+8); -#else - env->avr[n].u64[1] = ldq_p(mem_buf); - env->avr[n].u64[0] = ldq_p(mem_buf+8); -#endif + ppc_maybe_bswap_register(env, mem_buf, 8); + ppc_maybe_bswap_register(env, mem_buf + 8, 8); + if (!avr_need_swap(env)) { + env->avr[n].u64[0] = ldq_p(mem_buf); + env->avr[n].u64[1] = ldq_p(mem_buf+8); + } else { + env->avr[n].u64[1] = ldq_p(mem_buf); + env->avr[n].u64[0] = ldq_p(mem_buf+8); + } return 16; } if (n == 32) { + ppc_maybe_bswap_register(env, mem_buf, 4); env->vscr = ldl_p(mem_buf); return 4; } if (n == 33) { + ppc_maybe_bswap_register(env, mem_buf, 4); env->spr[SPR_VRSAVE] = (target_ulong)ldl_p(mem_buf); return 4; } @@ -8831,6 +9111,7 @@ static int gdb_get_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n) if (n < 32) { #if defined(TARGET_PPC64) stl_p(mem_buf, env->gpr[n] >> 32); + ppc_maybe_bswap_register(env, mem_buf, 4); #else stl_p(mem_buf, env->gprh[n]); #endif @@ -8838,10 +9119,12 @@ static int gdb_get_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n) } if (n == 32) { stq_p(mem_buf, env->spe_acc); + ppc_maybe_bswap_register(env, mem_buf, 8); return 8; } if (n == 33) { stl_p(mem_buf, env->spe_fscr); + ppc_maybe_bswap_register(env, mem_buf, 4); return 4; } return 0; @@ -8852,7 +9135,11 @@ static int gdb_set_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n) if (n < 32) { #if defined(TARGET_PPC64) target_ulong lo = (uint32_t)env->gpr[n]; - target_ulong hi = (target_ulong)ldl_p(mem_buf) << 32; + target_ulong hi; + + ppc_maybe_bswap_register(env, mem_buf, 4); + + hi = (target_ulong)ldl_p(mem_buf) << 32; env->gpr[n] = lo | hi; #else env->gprh[n] = ldl_p(mem_buf); @@ -8860,16 +9147,38 @@ static int gdb_set_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n) return 4; } if (n == 32) { + ppc_maybe_bswap_register(env, mem_buf, 8); env->spe_acc = ldq_p(mem_buf); return 8; } if (n == 33) { + ppc_maybe_bswap_register(env, mem_buf, 4); env->spe_fscr = ldl_p(mem_buf); return 4; } return 0; } +static int gdb_get_vsx_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +{ + if (n < 32) { + stq_p(mem_buf, env->vsr[n]); + ppc_maybe_bswap_register(env, mem_buf, 8); + return 8; + } + return 0; +} + +static int gdb_set_vsx_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +{ + if (n < 32) { + ppc_maybe_bswap_register(env, mem_buf, 8); + env->vsr[n] = ldq_p(mem_buf); + return 8; + } + return 0; +} + static int ppc_fixup_cpu(PowerPCCPU *cpu) { CPUPPCState *env = &cpu->env; @@ -8975,6 +9284,10 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) gdb_register_coprocessor(cs, gdb_get_spe_reg, gdb_set_spe_reg, 34, "power-spe.xml", 0); } + if (pcc->insns_flags2 & PPC2_VSX) { + gdb_register_coprocessor(cs, gdb_get_vsx_reg, gdb_set_vsx_reg, + 32, "power-vsx.xml", 0); + } qemu_init_vcpu(cs); @@ -9189,7 +9502,7 @@ int ppc_get_compat_smt_threads(PowerPCCPU *cpu) return ret; } -int ppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version) +void ppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version, Error **errp) { int ret = 0; CPUPPCState *env = &cpu->env; @@ -9211,12 +9524,13 @@ int ppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version) break; } - if (kvm_enabled() && kvmppc_set_compat(cpu, cpu->cpu_version) < 0) { - error_report("Unable to set compatibility mode in KVM"); - ret = -1; + if (kvm_enabled()) { + ret = kvmppc_set_compat(cpu, cpu->cpu_version); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Unable to set CPU compatibility mode in KVM"); + } } - - return ret; } static gint ppc_cpu_compare_class_pvr(gconstpointer a, gconstpointer b) @@ -9306,7 +9620,6 @@ static gint ppc_cpu_compare_class_name(gconstpointer a, gconstpointer b) return -1; } -#include static ObjectClass *ppc_cpu_class_by_name(const char *name); @@ -9350,8 +9663,7 @@ static ObjectClass *ppc_cpu_class_by_name(const char *name) break; } if (i == 8) { - ret = OBJECT_CLASS(ppc_cpu_class_by_pvr(strtoul(name, NULL, 16))); - return ret; + return OBJECT_CLASS(ppc_cpu_class_by_pvr(strtoul(name, NULL, 16))); } } @@ -9582,7 +9894,7 @@ static void ppc_cpu_reset(CPUState *s) #if defined(TARGET_PPC64) if (env->mmu_model & POWERPC_MMU_64) { - env->msr |= (1ULL << MSR_SF); + msr |= (1ULL << MSR_SF); } #endif @@ -9685,6 +9997,15 @@ static bool ppc_pvr_match_default(PowerPCCPUClass *pcc, uint32_t pvr) return pcc->pvr == pvr; } +static gchar *ppc_gdb_arch_name(CPUState *cs) +{ +#if defined(TARGET_PPC64) + return g_strdup("powerpc:common64"); +#else + return g_strdup("powerpc:common"); +#endif +} + static void ppc_cpu_class_init(ObjectClass *oc, void *data) { PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -9716,7 +10037,6 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) cc->vmsd = &vmstate_ppc_cpu; #if defined(TARGET_PPC64) cc->write_elf64_note = ppc64_cpu_write_elf64_note; - cc->write_elf64_qemunote = ppc64_cpu_write_elf64_qemunote; #endif #endif cc->cpu_exec_enter = ppc_cpu_exec_enter; @@ -9729,6 +10049,7 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_num_core_regs = 71 + 32; #endif + cc->gdb_arch_name = ppc_gdb_arch_name; #if defined(TARGET_PPC64) cc->gdb_core_xml_file = "power64-core.xml"; #else diff --git a/qemu/target-ppc/user_only_helper.c b/qemu/target-ppc/user_only_helper.c index 829f66f50..6aff34713 100644 --- a/qemu/target-ppc/user_only_helper.c +++ b/qemu/target-ppc/user_only_helper.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" int ppc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, diff --git a/qemu/target-s390x/arch_dump.c b/qemu/target-s390x/arch_dump.c index dab63eb44..4731869f6 100644 --- a/qemu/target-s390x/arch_dump.c +++ b/qemu/target-s390x/arch_dump.c @@ -11,6 +11,7 @@ * */ +#include "qemu/osdep.h" #include "cpu.h" #include "elf.h" #include "exec/cpu-all.h" @@ -246,9 +247,3 @@ ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) return (elf_note_size) * nr_cpus; } - -int s390_cpu_write_elf64_qemunote(WriteCoreDumpFunction f, - CPUState *cpu, void *opaque) -{ - return 0; -} diff --git a/qemu/target-s390x/cc_helper.c b/qemu/target-s390x/cc_helper.c index bfce3f1e6..0d9411bdf 100644 --- a/qemu/target-s390x/cc_helper.c +++ b/qemu/target-s390x/cc_helper.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" @@ -560,7 +561,7 @@ void HELPER(sacf)(CPUS390XState *env, uint64_t a1) env->psw.mask |= PSW_ASC_HOME; break; default: - qemu_log("unknown sacf mode: %" PRIx64 "\n", a1); + HELPER_LOG("unknown sacf mode: %" PRIx64 "\n", a1); program_interrupt(env, PGM_SPECIFICATION, 2); break; } diff --git a/qemu/target-s390x/cpu-qom.h b/qemu/target-s390x/cpu-qom.h index 491c1b876..1c9093396 100644 --- a/qemu/target-s390x/cpu-qom.h +++ b/qemu/target-s390x/cpu-qom.h @@ -47,6 +47,8 @@ typedef struct S390CPUClass { CPUClass parent_class; /*< public >*/ + int64_t next_cpu_id; + DeviceRealize parent_realize; void (*parent_reset)(CPUState *cpu); void (*load_normal)(CPUState *cpu); @@ -66,6 +68,7 @@ typedef struct S390CPU { /*< public >*/ CPUS390XState env; + int64_t id; /* needed for live migration */ void *irqstate; uint32_t irqstate_saved_size; @@ -91,8 +94,6 @@ void s390_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int s390_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque); -int s390_cpu_write_elf64_qemunote(WriteCoreDumpFunction f, - CPUState *cpu, void *opaque); hwaddr s390_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); hwaddr s390_cpu_get_phys_addr_debug(CPUState *cpu, vaddr addr); int s390_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); diff --git a/qemu/target-s390x/cpu.c b/qemu/target-s390x/cpu.c index c3e21b445..4bfff341d 100644 --- a/qemu/target-s390x/cpu.c +++ b/qemu/target-s390x/cpu.c @@ -23,14 +23,20 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "qemu-common.h" +#include "qemu/cutils.h" #include "qemu/timer.h" #include "qemu/error-report.h" #include "hw/hw.h" #include "trace.h" +#include "qapi/visitor.h" #ifndef CONFIG_USER_ONLY #include "sysemu/arch_init.h" +#include "sysemu/sysemu.h" +#include "hw/s390x/sclp.h" #endif #define CR0_RESET 0xE0UL @@ -184,11 +190,49 @@ static void s390_cpu_machine_reset_cb(void *opaque) } #endif +static void s390_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) +{ + info->mach = bfd_mach_s390_64; + info->print_insn = print_insn_s390; +} + static void s390_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); S390CPUClass *scc = S390_CPU_GET_CLASS(dev); + S390CPU *cpu = S390_CPU(dev); + CPUS390XState *env = &cpu->env; + Error *err = NULL; + +#if !defined(CONFIG_USER_ONLY) + if (cpu->id >= max_cpus) { + error_setg(&err, "Unable to add CPU: %" PRIi64 + ", max allowed: %d", cpu->id, max_cpus - 1); + goto out; + } +#endif + if (cpu_exists(cpu->id)) { + error_setg(&err, "Unable to add CPU: %" PRIi64 + ", it already exists", cpu->id); + goto out; + } + if (cpu->id != scc->next_cpu_id) { + error_setg(&err, "Unable to add CPU: %" PRIi64 + ", The next available id is %" PRIi64, cpu->id, + scc->next_cpu_id); + goto out; + } + cpu_exec_init(cs, &err); + if (err != NULL) { + goto out; + } + scc->next_cpu_id++; + +#if !defined(CONFIG_USER_ONLY) + qemu_register_reset(s390_cpu_machine_reset_cb, cpu); +#endif + env->cpu_num = cpu->id; s390_cpu_gdb_init(cs); qemu_init_vcpu(cs); #if !defined(CONFIG_USER_ONLY) @@ -197,7 +241,55 @@ static void s390_cpu_realizefn(DeviceState *dev, Error **errp) cpu_reset(cs); #endif - scc->parent_realize(dev, errp); + scc->parent_realize(dev, &err); + +#if !defined(CONFIG_USER_ONLY) + if (dev->hotplugged) { + raise_irq_cpu_hotplug(); + } +#endif + +out: + error_propagate(errp, err); +} + +static void s390x_cpu_get_id(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + S390CPU *cpu = S390_CPU(obj); + int64_t value = cpu->id; + + visit_type_int(v, name, &value, errp); +} + +static void s390x_cpu_set_id(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + S390CPU *cpu = S390_CPU(obj); + DeviceState *dev = DEVICE(obj); + const int64_t min = 0; + const int64_t max = UINT32_MAX; + Error *err = NULL; + int64_t value; + + if (dev->realized) { + error_setg(errp, "Attempt to set property '%s' on '%s' after " + "it was realized", name, object_get_typename(obj)); + return; + } + + visit_type_int(v, name, &value, &err); + if (err) { + error_propagate(errp, err); + return; + } + if (value < min || value > max) { + error_setg(errp, "Property %s.%s doesn't take value %" PRId64 + " (minimum: %" PRId64 ", maximum: %" PRId64 ")" , + object_get_typename(obj), name, value, min, max); + return; + } + cpu->id = value; } static void s390_cpu_initfn(Object *obj) @@ -206,15 +298,16 @@ static void s390_cpu_initfn(Object *obj) S390CPU *cpu = S390_CPU(obj); CPUS390XState *env = &cpu->env; static bool inited; - static int cpu_num = 0; #if !defined(CONFIG_USER_ONLY) struct tm tm; #endif cs->env_ptr = env; - cpu_exec_init(cs, &error_abort); + cs->halted = 1; + cs->exception_index = EXCP_HLT; + object_property_add(OBJECT(cpu), "id", "int64_t", s390x_cpu_get_id, + s390x_cpu_set_id, NULL, NULL, NULL); #if !defined(CONFIG_USER_ONLY) - qemu_register_reset(s390_cpu_machine_reset_cb, cpu); qemu_get_timedate(&tm, 0); env->tod_offset = TOD_UNIX_EPOCH + (time2tod(mktimegm(&tm)) * 1000000000ULL); @@ -223,7 +316,6 @@ static void s390_cpu_initfn(Object *obj) env->cpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_cpu_timer, cpu); s390_cpu_set_state(CPU_STATE_STOPPED, cpu); #endif - env->cpu_num = cpu_num++; if (tcg_enabled() && !inited) { inited = true; @@ -319,12 +411,18 @@ unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu) } #endif +static gchar *s390_gdb_arch_name(CPUState *cs) +{ + return g_strdup("s390:64-bit"); +} + static void s390_cpu_class_init(ObjectClass *oc, void *data) { S390CPUClass *scc = S390_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(scc); DeviceClass *dc = DEVICE_CLASS(oc); + scc->next_cpu_id = 0; scc->parent_realize = dc->realize; dc->realize = s390_cpu_realizefn; @@ -347,12 +445,21 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) cc->get_phys_page_debug = s390_cpu_get_phys_page_debug; cc->vmsd = &vmstate_s390_cpu; cc->write_elf64_note = s390_cpu_write_elf64_note; - cc->write_elf64_qemunote = s390_cpu_write_elf64_qemunote; cc->cpu_exec_interrupt = s390_cpu_exec_interrupt; cc->debug_excp_handler = s390x_cpu_debug_excp_handler; #endif + cc->disas_set_info = s390_cpu_disas_set_info; + cc->gdb_num_core_regs = S390_NUM_CORE_REGS; cc->gdb_core_xml_file = "s390x-core64.xml"; + cc->gdb_arch_name = s390_gdb_arch_name; + + /* + * Reason: s390_cpu_realizefn() calls cpu_exec_init(), which saves + * the object in cpus -> dangling pointer after final + * object_unref(). + */ + dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo s390_cpu_type_info = { diff --git a/qemu/target-s390x/cpu.h b/qemu/target-s390x/cpu.h index 63aebf484..6d97c089a 100644 --- a/qemu/target-s390x/cpu.h +++ b/qemu/target-s390x/cpu.h @@ -22,12 +22,10 @@ #ifndef CPU_S390X_H #define CPU_S390X_H -#include "config.h" #include "qemu-common.h" #define TARGET_LONG_BITS 64 -#define ELF_MACHINE EM_S390 #define ELF_MACHINE_UNAME "S390X" #define CPUArchState struct CPUS390XState @@ -43,6 +41,7 @@ #include "fpu/softfloat.h" #define NB_MMU_MODES 3 +#define TARGET_INSN_START_EXTRA_WORDS 1 #define MMU_MODE0_SUFFIX _primary #define MMU_MODE1_SUFFIX _secondary @@ -143,8 +142,6 @@ typedef struct CPUS390XState { uint32_t cpu_num; uint32_t machine_type; - uint8_t *storage_keys; - uint64_t tod_offset; uint64_t tod_basetime; QEMUTimer *tod_timer; @@ -310,7 +307,7 @@ static inline CPU_DoubleU *get_freg(CPUS390XState *cs, int nr) #define MMU_SECONDARY_IDX 1 #define MMU_HOME_IDX 2 -static inline int cpu_mmu_index (CPUS390XState *env) +static inline int cpu_mmu_index (CPUS390XState *env, bool ifetch) { switch (env->psw.mask & PSW_MASK_ASC) { case PSW_ASC_PRIMARY: @@ -416,6 +413,8 @@ void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen); #endif S390CPU *cpu_s390x_init(const char *cpu_model); +S390CPU *s390x_new_cpu(const char *cpu_model, int64_t id, Error **errp); +S390CPU *cpu_s390x_create(const char *cpu_model, Error **errp); void s390x_translate_init(void); int cpu_s390x_exec(CPUState *cpu); @@ -473,10 +472,8 @@ void s390x_tod_timer(void *opaque); void s390x_cpu_timer(void *opaque); int s390_virtio_hypercall(CPUS390XState *env); -void s390_virtio_irq(int config_change, uint64_t token); #ifdef CONFIG_KVM -void kvm_s390_virtio_irq(int config_change, uint64_t token); void kvm_s390_service_interrupt(uint32_t parm); void kvm_s390_vcpu_interrupt(S390CPU *cpu, struct kvm_s390_irq *irq); void kvm_s390_floating_interrupt(struct kvm_s390_irq *irq); @@ -487,9 +484,6 @@ int kvm_s390_mem_op(S390CPU *cpu, vaddr addr, uint8_t ar, void *hostbuf, int kvm_s390_get_clock(uint8_t *tod_high, uint64_t *tod_clock); int kvm_s390_set_clock(uint8_t *tod_high, uint64_t *tod_clock); #else -static inline void kvm_s390_virtio_irq(int config_change, uint64_t token) -{ -} static inline void kvm_s390_service_interrupt(uint32_t parm) { } @@ -547,9 +541,6 @@ int gtod_load(QEMUFile *f, void *opaque, int version_id); /* service interrupts are floating therefore we must not pass an cpustate */ void s390_sclp_extint(uint32_t parm); -/* from s390-virtio-bus */ -extern const hwaddr virtio_size; - #else static inline unsigned int s390_cpu_halt(S390CPU *cpu) { @@ -571,7 +562,7 @@ void cpu_unlock(void); typedef struct SubchDev SubchDev; #ifndef CONFIG_USER_ONLY -extern void io_subsystem_reset(void); +extern void subsystem_reset(void); SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid); bool css_subch_visible(SubchDev *sch); @@ -600,7 +591,6 @@ bool css_present(uint8_t cssid); #define cpu_init(model) CPU(cpu_s390x_init(model)) #define cpu_exec cpu_s390x_exec -#define cpu_gen_code cpu_s390x_gen_code #define cpu_signal_handler cpu_s390x_signal_handler void s390_cpu_list(FILE *f, fprintf_function cpu_fprintf); @@ -1163,12 +1153,13 @@ int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch, int vq, bool assign); int kvm_s390_cpu_restart(S390CPU *cpu); int kvm_s390_get_memslot_count(KVMState *s); -void kvm_s390_clear_cmma_callback(void *opaque); +void kvm_s390_cmma_reset(void); int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state); void kvm_s390_reset_vcpu(S390CPU *cpu); int kvm_s390_set_mem_limit(KVMState *s, uint64_t new_limit, uint64_t *hw_limit); void kvm_s390_vcpu_interrupt_pre_save(S390CPU *cpu); int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu); +void kvm_s390_crypto_reset(void); #else static inline void kvm_s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr, @@ -1192,7 +1183,7 @@ static inline int kvm_s390_cpu_restart(S390CPU *cpu) { return -ENOSYS; } -static inline void kvm_s390_clear_cmma_callback(void *opaque) +static inline void kvm_s390_cmma_reset(void) { } static inline int kvm_s390_get_memslot_count(KVMState *s) @@ -1218,6 +1209,9 @@ static inline int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu) { return 0; } +static inline void kvm_s390_crypto_reset(void) +{ +} #endif static inline int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit) @@ -1228,11 +1222,10 @@ static inline int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit) return 0; } -static inline void cmma_reset(S390CPU *cpu) +static inline void s390_cmma_reset(void) { if (kvm_enabled()) { - CPUState *cs = CPU(cpu); - kvm_s390_clear_cmma_callback(cs->kvm_state); + kvm_s390_cmma_reset(); } } @@ -1264,6 +1257,13 @@ static inline int s390_assign_subch_ioeventfd(EventNotifier *notifier, return kvm_s390_assign_subch_ioeventfd(notifier, sch_id, vq, assign); } +static inline void s390_crypto_reset(void) +{ + if (kvm_enabled()) { + kvm_s390_crypto_reset(); + } +} + #ifdef CONFIG_KVM static inline bool vregs_needed(void *opaque) { @@ -1278,4 +1278,49 @@ static inline bool vregs_needed(void *opaque) return 0; } #endif + +/* machine check interruption code */ + +/* subclasses */ +#define MCIC_SC_SD 0x8000000000000000ULL +#define MCIC_SC_PD 0x4000000000000000ULL +#define MCIC_SC_SR 0x2000000000000000ULL +#define MCIC_SC_CD 0x0800000000000000ULL +#define MCIC_SC_ED 0x0400000000000000ULL +#define MCIC_SC_DG 0x0100000000000000ULL +#define MCIC_SC_W 0x0080000000000000ULL +#define MCIC_SC_CP 0x0040000000000000ULL +#define MCIC_SC_SP 0x0020000000000000ULL +#define MCIC_SC_CK 0x0010000000000000ULL + +/* subclass modifiers */ +#define MCIC_SCM_B 0x0002000000000000ULL +#define MCIC_SCM_DA 0x0000000020000000ULL +#define MCIC_SCM_AP 0x0000000000080000ULL + +/* storage errors */ +#define MCIC_SE_SE 0x0000800000000000ULL +#define MCIC_SE_SC 0x0000400000000000ULL +#define MCIC_SE_KE 0x0000200000000000ULL +#define MCIC_SE_DS 0x0000100000000000ULL +#define MCIC_SE_IE 0x0000000080000000ULL + +/* validity bits */ +#define MCIC_VB_WP 0x0000080000000000ULL +#define MCIC_VB_MS 0x0000040000000000ULL +#define MCIC_VB_PM 0x0000020000000000ULL +#define MCIC_VB_IA 0x0000010000000000ULL +#define MCIC_VB_FA 0x0000008000000000ULL +#define MCIC_VB_VR 0x0000004000000000ULL +#define MCIC_VB_EC 0x0000002000000000ULL +#define MCIC_VB_FP 0x0000001000000000ULL +#define MCIC_VB_GR 0x0000000800000000ULL +#define MCIC_VB_CR 0x0000000400000000ULL +#define MCIC_VB_ST 0x0000000100000000ULL +#define MCIC_VB_AR 0x0000000040000000ULL +#define MCIC_VB_PR 0x0000000000200000ULL +#define MCIC_VB_FC 0x0000000000100000ULL +#define MCIC_VB_CT 0x0000000000020000ULL +#define MCIC_VB_CC 0x0000000000010000ULL + #endif diff --git a/qemu/target-s390x/fpu_helper.c b/qemu/target-s390x/fpu_helper.c index 45b7ddfbe..1c7f67354 100644 --- a/qemu/target-s390x/fpu_helper.c +++ b/qemu/target-s390x/fpu_helper.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" diff --git a/qemu/target-s390x/gdbstub.c b/qemu/target-s390x/gdbstub.c index 31f204964..9fc36cb54 100644 --- a/qemu/target-s390x/gdbstub.c +++ b/qemu/target-s390x/gdbstub.c @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "exec/gdbstub.h" #include "qemu/bitops.h" @@ -174,6 +174,115 @@ static int cpu_write_vreg(CPUS390XState *env, uint8_t *mem_buf, int n) } } +/* the values represent the positions in s390-cr.xml */ +#define S390_C0_REGNUM 0 +#define S390_C15_REGNUM 15 +/* total number of registers in s390-cr.xml */ +#define S390_NUM_C_REGS 16 + +#ifndef CONFIG_USER_ONLY +static int cpu_read_c_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +{ + switch (n) { + case S390_C0_REGNUM ... S390_C15_REGNUM: + return gdb_get_regl(mem_buf, env->cregs[n]); + default: + return 0; + } +} + +static int cpu_write_c_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +{ + switch (n) { + case S390_C0_REGNUM ... S390_C15_REGNUM: + env->cregs[n] = ldtul_p(mem_buf); + if (tcg_enabled()) { + tlb_flush(ENV_GET_CPU(env), 1); + } + cpu_synchronize_post_init(ENV_GET_CPU(env)); + return 8; + default: + return 0; + } +} + +/* the values represent the positions in s390-virt.xml */ +#define S390_VIRT_CKC_REGNUM 0 +#define S390_VIRT_CPUTM_REGNUM 1 +#define S390_VIRT_BEA_REGNUM 2 +#define S390_VIRT_PREFIX_REGNUM 3 +#define S390_VIRT_PP_REGNUM 4 +#define S390_VIRT_PFT_REGNUM 5 +#define S390_VIRT_PFS_REGNUM 6 +#define S390_VIRT_PFC_REGNUM 7 +/* total number of registers in s390-virt.xml */ +#define S390_NUM_VIRT_REGS 8 + +static int cpu_read_virt_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +{ + switch (n) { + case S390_VIRT_CKC_REGNUM: + return gdb_get_regl(mem_buf, env->ckc); + case S390_VIRT_CPUTM_REGNUM: + return gdb_get_regl(mem_buf, env->cputm); + case S390_VIRT_BEA_REGNUM: + return gdb_get_regl(mem_buf, env->gbea); + case S390_VIRT_PREFIX_REGNUM: + return gdb_get_regl(mem_buf, env->psa); + case S390_VIRT_PP_REGNUM: + return gdb_get_regl(mem_buf, env->pp); + case S390_VIRT_PFT_REGNUM: + return gdb_get_regl(mem_buf, env->pfault_token); + case S390_VIRT_PFS_REGNUM: + return gdb_get_regl(mem_buf, env->pfault_select); + case S390_VIRT_PFC_REGNUM: + return gdb_get_regl(mem_buf, env->pfault_compare); + default: + return 0; + } +} + +static int cpu_write_virt_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +{ + switch (n) { + case S390_VIRT_CKC_REGNUM: + env->ckc = ldtul_p(mem_buf); + cpu_synchronize_post_init(ENV_GET_CPU(env)); + return 8; + case S390_VIRT_CPUTM_REGNUM: + env->cputm = ldtul_p(mem_buf); + cpu_synchronize_post_init(ENV_GET_CPU(env)); + return 8; + case S390_VIRT_BEA_REGNUM: + env->gbea = ldtul_p(mem_buf); + cpu_synchronize_post_init(ENV_GET_CPU(env)); + return 8; + case S390_VIRT_PREFIX_REGNUM: + env->psa = ldtul_p(mem_buf); + cpu_synchronize_post_init(ENV_GET_CPU(env)); + return 8; + case S390_VIRT_PP_REGNUM: + env->pp = ldtul_p(mem_buf); + cpu_synchronize_post_init(ENV_GET_CPU(env)); + return 8; + case S390_VIRT_PFT_REGNUM: + env->pfault_token = ldtul_p(mem_buf); + cpu_synchronize_post_init(ENV_GET_CPU(env)); + return 8; + case S390_VIRT_PFS_REGNUM: + env->pfault_select = ldtul_p(mem_buf); + cpu_synchronize_post_init(ENV_GET_CPU(env)); + return 8; + case S390_VIRT_PFC_REGNUM: + env->pfault_compare = ldtul_p(mem_buf); + cpu_synchronize_post_init(ENV_GET_CPU(env)); + return 8; + default: + return 0; + } +} +#endif + void s390_cpu_gdb_init(CPUState *cs) { gdb_register_coprocessor(cs, cpu_read_ac_reg, @@ -187,4 +296,16 @@ void s390_cpu_gdb_init(CPUState *cs) gdb_register_coprocessor(cs, cpu_read_vreg, cpu_write_vreg, S390_NUM_VREGS, "s390-vx.xml", 0); + +#ifndef CONFIG_USER_ONLY + gdb_register_coprocessor(cs, cpu_read_c_reg, + cpu_write_c_reg, + S390_NUM_C_REGS, "s390-cr.xml", 0); + + if (kvm_enabled()) { + gdb_register_coprocessor(cs, cpu_read_virt_reg, + cpu_write_virt_reg, + S390_NUM_VIRT_REGS, "s390-virt.xml", 0); + } +#endif } diff --git a/qemu/target-s390x/helper.c b/qemu/target-s390x/helper.c index d88700695..92abe7e67 100644 --- a/qemu/target-s390x/helper.c +++ b/qemu/target-s390x/helper.c @@ -18,6 +18,8 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "exec/gdbstub.h" #include "qemu/timer.h" @@ -33,7 +35,7 @@ #ifdef DEBUG_S390_STDOUT #define DPRINTF(fmt, ...) \ do { fprintf(stderr, fmt, ## __VA_ARGS__); \ - qemu_log(fmt, ##__VA_ARGS__); } while (0) + if (qemu_log_separate()) qemu_log(fmt, ##__VA_ARGS__); } while (0) #else #define DPRINTF(fmt, ...) \ do { qemu_log(fmt, ## __VA_ARGS__); } while (0) @@ -64,14 +66,51 @@ void s390x_cpu_timer(void *opaque) } #endif -S390CPU *cpu_s390x_init(const char *cpu_model) +S390CPU *cpu_s390x_create(const char *cpu_model, Error **errp) { S390CPU *cpu; cpu = S390_CPU(object_new(TYPE_S390_CPU)); - object_property_set_bool(OBJECT(cpu), true, "realized", NULL); + return cpu; +} + +S390CPU *s390x_new_cpu(const char *cpu_model, int64_t id, Error **errp) +{ + S390CPU *cpu; + Error *err = NULL; + cpu = cpu_s390x_create(cpu_model, &err); + if (err != NULL) { + goto out; + } + + object_property_set_int(OBJECT(cpu), id, "id", &err); + if (err != NULL) { + goto out; + } + object_property_set_bool(OBJECT(cpu), true, "realized", &err); + +out: + if (err) { + error_propagate(errp, err); + object_unref(OBJECT(cpu)); + cpu = NULL; + } + return cpu; +} + +S390CPU *cpu_s390x_init(const char *cpu_model) +{ + Error *err = NULL; + S390CPU *cpu; + /* Use to track CPU ID for linux-user only */ + static int64_t next_cpu_id; + + cpu = s390x_new_cpu(cpu_model, next_cpu_id++, &err); + if (err) { + error_report_err(err); + } return cpu; } @@ -133,7 +172,7 @@ int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr, } /* check out of RAM access */ - if (raddr > (ram_size + virtio_size)) { + if (raddr > ram_size) { DPRINTF("%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__, (uint64_t)raddr, (uint64_t)ram_size); trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_LATER); @@ -162,8 +201,9 @@ hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr) vaddr &= 0x7fffffff; } - mmu_translate(env, vaddr, MMU_INST_FETCH, asc, &raddr, &prot, false); - + if (mmu_translate(env, vaddr, MMU_INST_FETCH, asc, &raddr, &prot, false)) { + return -1; + } return raddr; } diff --git a/qemu/target-s390x/int_helper.c b/qemu/target-s390x/int_helper.c index a46c736d6..cc1071eea 100644 --- a/qemu/target-s390x/int_helper.c +++ b/qemu/target-s390x/int_helper.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" diff --git a/qemu/target-s390x/interrupt.c b/qemu/target-s390x/interrupt.c index 1404d0afd..bad60a7e1 100644 --- a/qemu/target-s390x/interrupt.c +++ b/qemu/target-s390x/interrupt.c @@ -7,6 +7,7 @@ * option) any later version. See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "cpu.h" #include "sysemu/kvm.h" @@ -27,17 +28,6 @@ void s390_sclp_extint(uint32_t parm) } } -void s390_virtio_irq(int config_change, uint64_t token) -{ - if (kvm_enabled()) { - kvm_s390_virtio_irq(config_change, token); - } else { - S390CPU *dummy_cpu = s390_cpu_addr2state(0); - - cpu_inject_ext(dummy_cpu, EXT_VIRTIO, config_change, token); - } -} - void s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr, uint32_t io_int_parm, uint32_t io_int_word) { diff --git a/qemu/target-s390x/ioinst.c b/qemu/target-s390x/ioinst.c index 77f2a1fb9..142ff9384 100644 --- a/qemu/target-s390x/ioinst.c +++ b/qemu/target-s390x/ioinst.c @@ -9,7 +9,7 @@ * directory. */ -#include +#include "qemu/osdep.h" #include "cpu.h" #include "ioinst.h" @@ -614,9 +614,11 @@ static void ioinst_handle_chsc_sei(ChscReq *req, ChscResp *res) (*res_flags) |= 0x80; } else { (*res_flags) &= ~0x80; + css_clear_sei_pending(); } } else { - res->code = cpu_to_be16(0x0004); + res->code = cpu_to_be16(0x0005); + res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); } } diff --git a/qemu/target-s390x/kvm.c b/qemu/target-s390x/kvm.c index ae3a0affe..e1859cae0 100644 --- a/qemu/target-s390x/kvm.c +++ b/qemu/target-s390x/kvm.c @@ -21,7 +21,7 @@ * License along with this library; if not, see . */ -#include +#include "qemu/osdep.h" #include #include @@ -173,16 +173,15 @@ int kvm_s390_set_mem_limit(KVMState *s, uint64_t new_limit, uint64_t *hw_limit) return kvm_vm_ioctl(s, KVM_SET_DEVICE_ATTR, &attr); } -void kvm_s390_clear_cmma_callback(void *opaque) +void kvm_s390_cmma_reset(void) { int rc; - KVMState *s = opaque; struct kvm_device_attr attr = { .group = KVM_S390_VM_MEM_CTRL, .attr = KVM_S390_VM_MEM_CLR_CMMA, }; - rc = kvm_vm_ioctl(s, KVM_SET_DEVICE_ATTR, &attr); + rc = kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); trace_kvm_clear_cmma(rc); } @@ -200,9 +199,6 @@ static void kvm_s390_enable_cmma(KVMState *s) } rc = kvm_vm_ioctl(s, KVM_SET_DEVICE_ATTR, &attr); - if (!rc) { - qemu_register_reset(kvm_s390_clear_cmma_callback, s); - } trace_kvm_enable_cmma(rc); } @@ -249,7 +245,7 @@ static void kvm_s390_init_dea_kw(void) } } -static void kvm_s390_init_crypto(void) +void kvm_s390_crypto_reset(void) { kvm_s390_init_aes_kw(); kvm_s390_init_dea_kw(); @@ -262,7 +258,9 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_mem_op = kvm_check_extension(s, KVM_CAP_S390_MEM_OP); cap_s390_irq = kvm_check_extension(s, KVM_CAP_S390_INJECT_IRQ); - kvm_s390_enable_cmma(s); + if (!mem_path) { + kvm_s390_enable_cmma(s); + } if (!kvm_check_extension(s, KVM_CAP_S390_GMAP) || !kvm_check_extension(s, KVM_CAP_S390_COW)) { @@ -301,8 +299,6 @@ void kvm_s390_reset_vcpu(S390CPU *cpu) if (kvm_vcpu_ioctl(cs, KVM_S390_INITIAL_RESET, NULL)) { error_report("Initial CPU reset failed on CPU %i", cs->cpu_index); } - - kvm_s390_init_crypto(); } static int can_sync_regs(CPUState *cs, int regs) @@ -346,6 +342,12 @@ int kvm_arch_put_registers(CPUState *cs, int level) } cs->kvm_run->s.regs.fpc = env->fpc; cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_VRS; + } else if (can_sync_regs(cs, KVM_SYNC_FPRS)) { + for (i = 0; i < 16; i++) { + cs->kvm_run->s.regs.fprs[i] = get_freg(env, i)->ll; + } + cs->kvm_run->s.regs.fpc = env->fpc; + cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_FPRS; } else { /* Floating point */ for (i = 0; i < 16; i++) { @@ -486,6 +488,11 @@ int kvm_arch_get_registers(CPUState *cs) env->vregs[i][1].ll = cs->kvm_run->s.regs.vrs[i][1]; } env->fpc = cs->kvm_run->s.regs.fpc; + } else if (can_sync_regs(cs, KVM_SYNC_FPRS)) { + for (i = 0; i < 16; i++) { + get_freg(env, i)->ll = cs->kvm_run->s.regs.fprs[i]; + } + env->fpc = cs->kvm_run->s.regs.fpc; } else { r = kvm_vcpu_ioctl(cs, KVM_GET_FPU, &fpu); if (r < 0) { @@ -588,9 +595,9 @@ int kvm_s390_set_clock(uint8_t *tod_high, uint64_t *tod_low) * @addr: the logical start address in guest memory * @ar: the access register number * @hostbuf: buffer in host memory. NULL = do only checks w/o copying - * @len: length that should be transfered + * @len: length that should be transferred * @is_write: true = write, false = read - * Returns: 0 on success, non-zero if an exception or error occured + * Returns: 0 on success, non-zero if an exception or error occurred * * Use KVM ioctl to read/write from/to guest memory. An access exception * is injected into the vCPU in case of translation errors. @@ -929,17 +936,6 @@ void kvm_s390_floating_interrupt(struct kvm_s390_irq *irq) __kvm_s390_floating_interrupt(irq); } -void kvm_s390_virtio_irq(int config_change, uint64_t token) -{ - struct kvm_s390_irq irq = { - .type = KVM_S390_INT_VIRTIO, - .u.ext.ext_params = config_change, - .u.ext.ext_params2 = token, - }; - - kvm_s390_floating_interrupt(&irq); -} - void kvm_s390_service_interrupt(uint32_t parm) { struct kvm_s390_irq irq = { @@ -1437,7 +1433,7 @@ static int kvm_s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch) cpu_physical_memory_write(offsetof(LowCore, ar_access_id), &ar_id, 1); } for (i = 0; i < 16; ++i) { - *((uint64 *)mem + i) = get_freg(&cpu->env, i)->ll; + *((uint64_t *)mem + i) = get_freg(&cpu->env, i)->ll; } memcpy(mem + 128, &cpu->env.regs, 128); memcpy(mem + 256, &cpu->env.psw, 16); @@ -1796,13 +1792,6 @@ static bool is_special_wait_psw(CPUState *cs) return cs->kvm_run->psw_addr == 0xfffUL; } -static void guest_panicked(void) -{ - qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, - &error_abort); - vm_stop(RUN_STATE_GUEST_PANICKED); -} - static void unmanageable_intercept(S390CPU *cpu, const char *str, int pswoffset) { CPUState *cs = CPU(cpu); @@ -1811,7 +1800,7 @@ static void unmanageable_intercept(S390CPU *cpu, const char *str, int pswoffset) str, cs->cpu_index, ldq_phys(cs->as, cpu->env.psa + pswoffset), ldq_phys(cs->as, cpu->env.psa + pswoffset + 8)); s390_cpu_halt(cpu); - guest_panicked(); + qemu_system_guest_panicked(); } static int handle_intercept(S390CPU *cpu) @@ -1844,7 +1833,7 @@ static int handle_intercept(S390CPU *cpu) if (is_special_wait_psw(cs)) { qemu_system_shutdown_request(); } else { - guest_panicked(); + qemu_system_guest_panicked(); } } r = EXCP_HALTED; @@ -2072,12 +2061,30 @@ void kvm_s390_io_interrupt(uint16_t subchannel_id, kvm_s390_floating_interrupt(&irq); } +static uint64_t build_channel_report_mcic(void) +{ + uint64_t mcic; + + /* subclass: indicate channel report pending */ + mcic = MCIC_SC_CP | + /* subclass modifiers: none */ + /* storage errors: none */ + /* validity bits: no damage */ + MCIC_VB_WP | MCIC_VB_MS | MCIC_VB_PM | MCIC_VB_IA | MCIC_VB_FP | + MCIC_VB_GR | MCIC_VB_CR | MCIC_VB_ST | MCIC_VB_AR | MCIC_VB_PR | + MCIC_VB_FC | MCIC_VB_CT | MCIC_VB_CC; + if (kvm_check_extension(kvm_state, KVM_CAP_S390_VECTOR_REGISTERS)) { + mcic |= MCIC_VB_VR; + } + return mcic; +} + void kvm_s390_crw_mchk(void) { struct kvm_s390_irq irq = { .type = KVM_S390_MCHK, .u.mchk.cr14 = 1 << 28, - .u.mchk.mcic = 0x00400f1d40330000ULL, + .u.mchk.mcic = build_channel_report_mcic(), }; kvm_s390_floating_interrupt(&irq); } @@ -2215,7 +2222,7 @@ int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu) } int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, - uint64_t address, uint32_t data) + uint64_t address, uint32_t data, PCIDevice *dev) { S390PCIBusDevice *pbdev; uint32_t fid = data >> ZPCI_MSI_VEC_BITS; diff --git a/qemu/target-s390x/machine.c b/qemu/target-s390x/machine.c index b76fb0831..6b2609054 100644 --- a/qemu/target-s390x/machine.c +++ b/qemu/target-s390x/machine.c @@ -14,6 +14,7 @@ * or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "cpu.h" #include "sysemu/kvm.h" @@ -48,7 +49,7 @@ static inline bool fpu_needed(void *opaque) return true; } -const VMStateDescription vmstate_fpu = { +static const VMStateDescription vmstate_fpu = { .name = "cpu/fpu", .version_id = 1, .minimum_version_id = 1, @@ -75,7 +76,7 @@ const VMStateDescription vmstate_fpu = { } }; -const VMStateDescription vmstate_vregs = { +static const VMStateDescription vmstate_vregs = { .name = "cpu/vregs", .version_id = 1, .minimum_version_id = 1, diff --git a/qemu/target-s390x/mem_helper.c b/qemu/target-s390x/mem_helper.c index 6f8bd796a..707862203 100644 --- a/qemu/target-s390x/mem_helper.c +++ b/qemu/target-s390x/mem_helper.c @@ -18,9 +18,11 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" +#include "hw/s390x/storage-keys.h" /*****************************************************************************/ /* Softmmu support */ @@ -68,7 +70,7 @@ static inline uint64_t adj_len_to_page(uint64_t len, uint64_t addr) static void fast_memset(CPUS390XState *env, uint64_t dest, uint8_t byte, uint32_t l) { - int mmu_idx = cpu_mmu_index(env); + int mmu_idx = cpu_mmu_index(env, false); while (l > 0) { void *p = tlb_vaddr_to_host(env, dest, MMU_DATA_STORE, mmu_idx); @@ -91,7 +93,7 @@ static void fast_memset(CPUS390XState *env, uint64_t dest, uint8_t byte, static void fast_memmove(CPUS390XState *env, uint64_t dest, uint64_t src, uint32_t l) { - int mmu_idx = cpu_mmu_index(env); + int mmu_idx = cpu_mmu_index(env, false); while (l > 0) { void *src_p = tlb_vaddr_to_host(env, src, MMU_DATA_LOAD, mmu_idx); @@ -937,40 +939,73 @@ uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2) /* insert storage key extended */ uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2) { + static S390SKeysState *ss; + static S390SKeysClass *skeyclass; uint64_t addr = get_address(env, 0, 0, r2); + uint8_t key; if (addr > ram_size) { return 0; } - return env->storage_keys[addr / TARGET_PAGE_SIZE]; + if (unlikely(!ss)) { + ss = s390_get_skeys_device(); + skeyclass = S390_SKEYS_GET_CLASS(ss); + } + + if (skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key)) { + return 0; + } + return key; } /* set storage key extended */ void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2) { + static S390SKeysState *ss; + static S390SKeysClass *skeyclass; uint64_t addr = get_address(env, 0, 0, r2); + uint8_t key; if (addr > ram_size) { return; } - env->storage_keys[addr / TARGET_PAGE_SIZE] = r1; + if (unlikely(!ss)) { + ss = s390_get_skeys_device(); + skeyclass = S390_SKEYS_GET_CLASS(ss); + } + + key = (uint8_t) r1; + skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); } /* reset reference bit extended */ uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2) { - uint8_t re; - uint8_t key; + static S390SKeysState *ss; + static S390SKeysClass *skeyclass; + uint8_t re, key; if (r2 > ram_size) { return 0; } - key = env->storage_keys[r2 / TARGET_PAGE_SIZE]; + if (unlikely(!ss)) { + ss = s390_get_skeys_device(); + skeyclass = S390_SKEYS_GET_CLASS(ss); + } + + if (skeyclass->get_skeys(ss, r2 / TARGET_PAGE_SIZE, 1, &key)) { + return 0; + } + re = key & (SK_R | SK_C); - env->storage_keys[r2 / TARGET_PAGE_SIZE] = (key & ~SK_R); + key &= ~SK_R; + + if (skeyclass->set_skeys(ss, r2 / TARGET_PAGE_SIZE, 1, &key)) { + return 0; + } /* * cc diff --git a/qemu/target-s390x/misc_helper.c b/qemu/target-s390x/misc_helper.c index 8eac0e12b..71cbe34e0 100644 --- a/qemu/target-s390x/misc_helper.c +++ b/qemu/target-s390x/misc_helper.c @@ -18,11 +18,11 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/memory.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" -#include #include "sysemu/kvm.h" #include "qemu/timer.h" #include "exec/address-spaces.h" @@ -127,8 +127,9 @@ static int modified_clear_reset(S390CPU *cpu) CPU_FOREACH(t) { run_on_cpu(t, s390_do_cpu_full_reset, t); } - cmma_reset(cpu); - io_subsystem_reset(); + s390_cmma_reset(); + subsystem_reset(); + s390_crypto_reset(); scc->load_normal(CPU(cpu)); cpu_synchronize_all_post_reset(); resume_all_vcpus(); @@ -145,8 +146,8 @@ static int load_normal_reset(S390CPU *cpu) CPU_FOREACH(t) { run_on_cpu(t, s390_do_cpu_reset, t); } - cmma_reset(cpu); - io_subsystem_reset(); + s390_cmma_reset(); + subsystem_reset(); scc->initial_cpu_reset(CPU(cpu)); scc->load_normal(CPU(cpu)); cpu_synchronize_all_post_reset(); @@ -233,11 +234,8 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3) } iplb = g_malloc0(sizeof(struct IplParameterBlock)); cpu_physical_memory_read(addr, iplb, sizeof(struct IplParameterBlock)); - if (!s390_ipl_update_diag308(iplb)) { - env->regs[r1 + 1] = DIAG_308_RC_OK; - } else { - env->regs[r1 + 1] = DIAG_308_RC_INVALID; - } + s390_ipl_update_diag308(iplb); + env->regs[r1 + 1] = DIAG_308_RC_OK; g_free(iplb); return; case 6: @@ -301,7 +299,7 @@ void HELPER(spx)(CPUS390XState *env, uint64_t a1) uint32_t prefix = a1 & 0x7fffe000; env->psa = prefix; - qemu_log("prefix: %#x\n", prefix); + HELPER_LOG("prefix: %#x\n", prefix); tlb_flush_page(cs, 0); tlb_flush_page(cs, TARGET_PAGE_SIZE); } diff --git a/qemu/target-s390x/mmu_helper.c b/qemu/target-s390x/mmu_helper.c index 1ea6d812c..b11a02706 100644 --- a/qemu/target-s390x/mmu_helper.c +++ b/qemu/target-s390x/mmu_helper.c @@ -15,10 +15,13 @@ * GNU General Public License for more details. */ +#include "qemu/osdep.h" #include "qemu/error-report.h" #include "exec/address-spaces.h" #include "cpu.h" #include "sysemu/kvm.h" +#include "trace.h" +#include "hw/s390x/storage-keys.h" /* #define DEBUG_S390 */ /* #define DEBUG_S390_PTE */ @@ -28,7 +31,7 @@ #ifdef DEBUG_S390_STDOUT #define DPRINTF(fmt, ...) \ do { fprintf(stderr, fmt, ## __VA_ARGS__); \ - qemu_log(fmt, ##__VA_ARGS__); } while (0) + if (qemu_log_separate()) qemu_log(fmt, ##__VA_ARGS__); } while (0) #else #define DPRINTF(fmt, ...) \ do { qemu_log(fmt, ## __VA_ARGS__); } while (0) @@ -87,7 +90,7 @@ static void trigger_page_fault(CPUS390XState *env, target_ulong vaddr, tec = vaddr | (rw == MMU_DATA_STORE ? FS_WRITE : FS_READ) | asc >> 46; - DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits); + DPRINTF("%s: trans_exc_code=%016" PRIx64 "\n", __func__, tec); if (!exc) { return; @@ -309,8 +312,15 @@ static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr, int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, target_ulong *raddr, int *flags, bool exc) { + static S390SKeysState *ss; + static S390SKeysClass *skeyclass; int r = -1; - uint8_t *sk; + uint8_t key; + + if (unlikely(!ss)) { + ss = s390_get_skeys_device(); + skeyclass = S390_SKEYS_GET_CLASS(ss); + } *flags = PAGE_READ | PAGE_WRITE | PAGE_EXEC; vaddr &= TARGET_PAGE_MASK; @@ -358,14 +368,23 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, /* Convert real address -> absolute address */ *raddr = mmu_real2abs(env, *raddr); - if (*raddr < ram_size) { - sk = &env->storage_keys[*raddr / TARGET_PAGE_SIZE]; + if (r == 0 && *raddr < ram_size) { + if (skeyclass->get_skeys(ss, *raddr / TARGET_PAGE_SIZE, 1, &key)) { + trace_get_skeys_nonzero(r); + return 0; + } + if (*flags & PAGE_READ) { - *sk |= SK_R; + key |= SK_R; } if (*flags & PAGE_WRITE) { - *sk |= SK_C; + key |= SK_C; + } + + if (skeyclass->set_skeys(ss, *raddr / TARGET_PAGE_SIZE, 1, &key)) { + trace_set_skeys_nonzero(r); + return 0; } } diff --git a/qemu/target-s390x/translate.c b/qemu/target-s390x/translate.c index c748290d5..c871ef2bb 100644 --- a/qemu/target-s390x/translate.c +++ b/qemu/target-s390x/translate.c @@ -28,6 +28,7 @@ # define LOG_DISAS(...) do { } while (0) #endif +#include "qemu/osdep.h" #include "cpu.h" #include "disas/disas.h" #include "tcg-op.h" @@ -36,13 +37,14 @@ #include "exec/cpu_ldst.h" /* global register indexes */ -static TCGv_ptr cpu_env; +static TCGv_env cpu_env; #include "exec/gen-icount.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" /* Information that (most) every instruction needs to manipulate. */ @@ -124,7 +126,7 @@ void s390_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, for (i = 0; i < 32; i++) { cpu_fprintf(f, "V%02d=%016" PRIx64 "%016" PRIx64, i, env->vregs[i][0].ll, env->vregs[i][1].ll); - cpu_fprintf(f, (i % 2) ? " " : "\n"); + cpu_fprintf(f, (i % 2) ? "\n" : " "); } #ifndef CONFIG_USER_ONLY @@ -161,42 +163,40 @@ static char cpu_reg_names[32][4]; static TCGv_i64 regs[16]; static TCGv_i64 fregs[16]; -static uint8_t gen_opc_cc_op[OPC_BUF_SIZE]; - void s390x_translate_init(void) { int i; cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); - psw_addr = tcg_global_mem_new_i64(TCG_AREG0, + psw_addr = tcg_global_mem_new_i64(cpu_env, offsetof(CPUS390XState, psw.addr), "psw_addr"); - psw_mask = tcg_global_mem_new_i64(TCG_AREG0, + psw_mask = tcg_global_mem_new_i64(cpu_env, offsetof(CPUS390XState, psw.mask), "psw_mask"); - gbea = tcg_global_mem_new_i64(TCG_AREG0, + gbea = tcg_global_mem_new_i64(cpu_env, offsetof(CPUS390XState, gbea), "gbea"); - cc_op = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUS390XState, cc_op), + cc_op = tcg_global_mem_new_i32(cpu_env, offsetof(CPUS390XState, cc_op), "cc_op"); - cc_src = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUS390XState, cc_src), + cc_src = tcg_global_mem_new_i64(cpu_env, offsetof(CPUS390XState, cc_src), "cc_src"); - cc_dst = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUS390XState, cc_dst), + cc_dst = tcg_global_mem_new_i64(cpu_env, offsetof(CPUS390XState, cc_dst), "cc_dst"); - cc_vr = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUS390XState, cc_vr), + cc_vr = tcg_global_mem_new_i64(cpu_env, offsetof(CPUS390XState, cc_vr), "cc_vr"); for (i = 0; i < 16; i++) { snprintf(cpu_reg_names[i], sizeof(cpu_reg_names[0]), "r%d", i); - regs[i] = tcg_global_mem_new(TCG_AREG0, + regs[i] = tcg_global_mem_new(cpu_env, offsetof(CPUS390XState, regs[i]), cpu_reg_names[i]); } for (i = 0; i < 16; i++) { snprintf(cpu_reg_names[i + 16], sizeof(cpu_reg_names[0]), "f%d", i); - fregs[i] = tcg_global_mem_new(TCG_AREG0, + fregs[i] = tcg_global_mem_new(cpu_env, offsetof(CPUS390XState, vregs[i][0].d), cpu_reg_names[i + 16]); } @@ -811,7 +811,7 @@ static void disas_jcc(DisasContext *s, DisasCompare *c, uint32_t mask) case CC_OP_LTGT0_32: c->is_64 = false; c->u.s32.a = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(c->u.s32.a, cc_dst); + tcg_gen_extrl_i64_i32(c->u.s32.a, cc_dst); c->u.s32.b = tcg_const_i32(0); break; case CC_OP_LTGT_32: @@ -819,9 +819,9 @@ static void disas_jcc(DisasContext *s, DisasCompare *c, uint32_t mask) case CC_OP_SUBU_32: c->is_64 = false; c->u.s32.a = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(c->u.s32.a, cc_src); + tcg_gen_extrl_i64_i32(c->u.s32.a, cc_src); c->u.s32.b = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(c->u.s32.b, cc_dst); + tcg_gen_extrl_i64_i32(c->u.s32.b, cc_dst); break; case CC_OP_LTGT0_64: @@ -851,11 +851,11 @@ static void disas_jcc(DisasContext *s, DisasCompare *c, uint32_t mask) c->is_64 = false; c->u.s32.a = tcg_temp_new_i32(); c->u.s32.b = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(c->u.s32.a, cc_vr); + tcg_gen_extrl_i64_i32(c->u.s32.a, cc_vr); if (cond == TCG_COND_EQ || cond == TCG_COND_NE) { tcg_gen_movi_i32(c->u.s32.b, 0); } else { - tcg_gen_trunc_i64_i32(c->u.s32.b, cc_src); + tcg_gen_extrl_i64_i32(c->u.s32.b, cc_src); } break; @@ -1532,7 +1532,7 @@ static ExitStatus op_bct32(DisasContext *s, DisasOps *o) store_reg32_i64(r1, t); c.u.s32.a = tcg_temp_new_i32(); c.u.s32.b = tcg_const_i32(0); - tcg_gen_trunc_i64_i32(c.u.s32.a, t); + tcg_gen_extrl_i64_i32(c.u.s32.a, t); tcg_temp_free_i64(t); return help_branch(s, &c, is_imm, imm, o->in2); @@ -1556,7 +1556,7 @@ static ExitStatus op_bcth(DisasContext *s, DisasOps *o) store_reg32h_i64(r1, t); c.u.s32.a = tcg_temp_new_i32(); c.u.s32.b = tcg_const_i32(0); - tcg_gen_trunc_i64_i32(c.u.s32.a, t); + tcg_gen_extrl_i64_i32(c.u.s32.a, t); tcg_temp_free_i64(t); return help_branch(s, &c, 1, imm, o->in2); @@ -1599,8 +1599,8 @@ static ExitStatus op_bx32(DisasContext *s, DisasOps *o) tcg_gen_add_i64(t, regs[r1], regs[r3]); c.u.s32.a = tcg_temp_new_i32(); c.u.s32.b = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(c.u.s32.a, t); - tcg_gen_trunc_i64_i32(c.u.s32.b, regs[r3 | 1]); + tcg_gen_extrl_i64_i32(c.u.s32.a, t); + tcg_gen_extrl_i64_i32(c.u.s32.b, regs[r3 | 1]); store_reg32_i64(r1, t); tcg_temp_free_i64(t); @@ -1905,7 +1905,7 @@ static ExitStatus op_clm(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); TCGv_i32 t1 = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(t1, o->in1); + tcg_gen_extrl_i64_i32(t1, o->in1); potential_page_fault(s); gen_helper_clm(cc_op, cpu_env, t1, m3, o->in2); set_cc_static(s); @@ -1977,7 +1977,7 @@ static ExitStatus op_cs(DisasContext *s, DisasOps *o) /* Store CC back to cc_op. Wait until after the store so that any exception gets the old cc_op value. */ - tcg_gen_trunc_i64_i32(cc_op, cc); + tcg_gen_extrl_i64_i32(cc_op, cc); tcg_temp_free_i64(cc); set_cc_static(s); return NO_EXIT; @@ -2027,7 +2027,7 @@ static ExitStatus op_cdsg(DisasContext *s, DisasOps *o) /* Save back state now that we've passed all exceptions. */ tcg_gen_mov_i64(regs[r1], outh); tcg_gen_mov_i64(regs[r1 + 1], outl); - tcg_gen_trunc_i64_i32(cc_op, cc); + tcg_gen_extrl_i64_i32(cc_op, cc); tcg_temp_free_i64(outh); tcg_temp_free_i64(outl); tcg_temp_free_i64(cc); @@ -2051,7 +2051,7 @@ static ExitStatus op_cvd(DisasContext *s, DisasOps *o) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i32 t2 = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(t2, o->in1); + tcg_gen_extrl_i64_i32(t2, o->in1); gen_helper_cvd(t1, t2); tcg_temp_free_i32(t2); tcg_gen_qemu_st64(t1, o->in2, get_mem_index(s)); @@ -3235,8 +3235,8 @@ static ExitStatus op_rll32(DisasContext *s, DisasOps *o) TCGv_i32 t1 = tcg_temp_new_i32(); TCGv_i32 t2 = tcg_temp_new_i32(); TCGv_i32 to = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(t1, o->in1); - tcg_gen_trunc_i64_i32(t2, o->in2); + tcg_gen_extrl_i64_i32(t1, o->in1); + tcg_gen_extrl_i64_i32(t2, o->in2); tcg_gen_rotl_i32(to, t1, t2); tcg_gen_extu_i32_i64(o->out, to); tcg_temp_free_i32(t1); @@ -5319,18 +5319,14 @@ static ExitStatus translate_one(CPUS390XState *env, DisasContext *s) return ret; } -static inline void gen_intermediate_code_internal(S390CPU *cpu, - TranslationBlock *tb, - bool search_pc) +void gen_intermediate_code(CPUS390XState *env, struct TranslationBlock *tb) { + S390CPU *cpu = s390_env_get_cpu(env); CPUState *cs = CPU(cpu); - CPUS390XState *env = &cpu->env; DisasContext dc; target_ulong pc_start; uint64_t next_page_start; - int j, lj = -1; int num_insns, max_insns; - CPUBreakpoint *bp; ExitStatus status; bool do_debug; @@ -5353,41 +5349,32 @@ static inline void gen_intermediate_code_internal(S390CPU *cpu, if (max_insns == 0) { max_insns = CF_COUNT_MASK; } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } gen_tb_start(tb); do { - if (search_pc) { - j = tcg_op_buf_count(); - if (lj < j) { - lj++; - while (lj < j) { - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - } - tcg_ctx.gen_opc_pc[lj] = dc.pc; - gen_opc_cc_op[lj] = dc.cc_op; - tcg_ctx.gen_opc_instr_start[lj] = 1; - tcg_ctx.gen_opc_icount[lj] = num_insns; - } - if (++num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { - gen_io_start(); + tcg_gen_insn_start(dc.pc, dc.cc_op); + num_insns++; + + if (unlikely(cpu_breakpoint_test(cs, dc.pc, BP_ANY))) { + status = EXIT_PC_STALE; + do_debug = true; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + dc.pc += 2; + break; } - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { - tcg_gen_debug_insn_start(dc.pc); + if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { + gen_io_start(); } status = NO_EXIT; - if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { - QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - if (bp->pc == dc.pc) { - status = EXIT_PC_STALE; - do_debug = true; - break; - } - } - } if (status == NO_EXIT) { status = translate_one(env, &dc); } @@ -5432,16 +5419,8 @@ static inline void gen_intermediate_code_internal(S390CPU *cpu, gen_tb_end(tb, num_insns); - if (search_pc) { - j = tcg_op_buf_count(); - lj++; - while (lj <= j) { - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - } else { - tb->size = dc.pc - pc_start; - tb->icount = num_insns; - } + tb->size = dc.pc - pc_start; + tb->icount = num_insns; #if defined(S390X_DEBUG_DISAS) if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { @@ -5452,21 +5431,11 @@ static inline void gen_intermediate_code_internal(S390CPU *cpu, #endif } -void gen_intermediate_code (CPUS390XState *env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(s390_env_get_cpu(env), tb, false); -} - -void gen_intermediate_code_pc (CPUS390XState *env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(s390_env_get_cpu(env), tb, true); -} - -void restore_state_to_opc(CPUS390XState *env, TranslationBlock *tb, int pc_pos) +void restore_state_to_opc(CPUS390XState *env, TranslationBlock *tb, + target_ulong *data) { - int cc_op; - env->psw.addr = tcg_ctx.gen_opc_pc[pc_pos]; - cc_op = gen_opc_cc_op[pc_pos]; + int cc_op = data[1]; + env->psw.addr = data[0]; if ((cc_op != CC_OP_DYNAMIC) && (cc_op != CC_OP_STATIC)) { env->cc_op = cc_op; } diff --git a/qemu/target-sh4/Makefile.objs b/qemu/target-sh4/Makefile.objs index a285358ad..2c25d96e6 100644 --- a/qemu/target-sh4/Makefile.objs +++ b/qemu/target-sh4/Makefile.objs @@ -1,2 +1,3 @@ obj-y += translate.o op_helper.o helper.o cpu.o +obj-$(CONFIG_SOFTMMU) += monitor.o obj-y += gdbstub.o diff --git a/qemu/target-sh4/cpu.c b/qemu/target-sh4/cpu.c index 5c65ab4df..86ba38808 100644 --- a/qemu/target-sh4/cpu.c +++ b/qemu/target-sh4/cpu.c @@ -19,6 +19,8 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "qemu-common.h" #include "migration/vmstate.h" @@ -70,6 +72,12 @@ static void superh_cpu_reset(CPUState *s) set_default_nan_mode(1, &env->fp_status); } +static void superh_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) +{ + info->mach = bfd_mach_sh4; + info->print_insn = print_insn_sh; +} + typedef struct SuperHCPUListState { fprintf_function cpu_fprintf; FILE *file; @@ -288,8 +296,18 @@ static void superh_cpu_class_init(ObjectClass *oc, void *data) #else cc->get_phys_page_debug = superh_cpu_get_phys_page_debug; #endif - dc->vmsd = &vmstate_sh_cpu; + cc->disas_set_info = superh_cpu_disas_set_info; + cc->gdb_num_core_regs = 59; + + dc->vmsd = &vmstate_sh_cpu; + + /* + * Reason: superh_cpu_initfn() calls cpu_exec_init(), which saves + * the object in cpus -> dangling pointer after final + * object_unref(). + */ + dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo superh_cpu_type_info = { diff --git a/qemu/target-sh4/cpu.h b/qemu/target-sh4/cpu.h index 34bb3d779..3b23e967b 100644 --- a/qemu/target-sh4/cpu.h +++ b/qemu/target-sh4/cpu.h @@ -19,13 +19,10 @@ #ifndef _CPU_SH4_H #define _CPU_SH4_H -#include "config.h" #include "qemu-common.h" #define TARGET_LONG_BITS 32 -#define ELF_MACHINE EM_SH - /* CPU Subtypes */ #define SH_CPU_SH7750 (1 << 0) #define SH_CPU_SH7750S (1 << 1) @@ -122,6 +119,7 @@ typedef struct tlb_t { #define ITLB_SIZE 4 #define NB_MMU_MODES 2 +#define TARGET_INSN_START_EXTRA_WORDS 1 enum sh_features { SH_FEATURE_SH4A = 1, @@ -227,7 +225,6 @@ void cpu_load_tlb(CPUSH4State * env); #define cpu_init(cpu_model) CPU(cpu_sh4_init(cpu_model)) #define cpu_exec cpu_sh4_exec -#define cpu_gen_code cpu_sh4_gen_code #define cpu_signal_handler cpu_sh4_signal_handler #define cpu_list sh4_cpu_list @@ -235,7 +232,7 @@ void cpu_load_tlb(CPUSH4State * env); #define MMU_MODE0_SUFFIX _kernel #define MMU_MODE1_SUFFIX _user #define MMU_USER_IDX 1 -static inline int cpu_mmu_index (CPUSH4State *env) +static inline int cpu_mmu_index (CPUSH4State *env, bool ifetch) { return (env->sr & (1u << SR_MD)) == 0 ? 1 : 0; } diff --git a/qemu/target-sh4/gdbstub.c b/qemu/target-sh4/gdbstub.c index a365a27aa..1b59ea8c7 100644 --- a/qemu/target-sh4/gdbstub.c +++ b/qemu/target-sh4/gdbstub.c @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "exec/gdbstub.h" diff --git a/qemu/target-sh4/helper.c b/qemu/target-sh4/helper.c index a533f08ea..6438338f2 100644 --- a/qemu/target-sh4/helper.c +++ b/qemu/target-sh4/helper.c @@ -16,14 +16,10 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include "cpu.h" +#include "exec/log.h" #if !defined(CONFIG_USER_ONLY) #include "hw/sh4/sh_intc.h" @@ -60,7 +56,7 @@ int superh_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr) { - /* For user mode, only U0 area is cachable. */ + /* For user mode, only U0 area is cacheable. */ return !(addr & 0x80000000); } @@ -826,11 +822,11 @@ int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr) /* check area */ if (env->sr & (1u << SR_MD)) { - /* For previledged mode, P2 and P4 area is not cachable. */ + /* For privileged mode, P2 and P4 area is not cacheable. */ if ((0xA0000000 <= addr && addr < 0xC0000000) || 0xE0000000 <= addr) return 0; } else { - /* For user mode, only U0 area is cachable. */ + /* For user mode, only U0 area is cacheable. */ if (0x80000000 <= addr) return 0; } diff --git a/qemu/target-sh4/helper.h b/qemu/target-sh4/helper.h index c9bc40704..dce859cae 100644 --- a/qemu/target-sh4/helper.h +++ b/qemu/target-sh4/helper.h @@ -18,28 +18,28 @@ DEF_HELPER_2(ld_fpscr, void, env, i32) DEF_HELPER_FLAGS_1(fabs_FT, TCG_CALL_NO_RWG_SE, f32, f32) DEF_HELPER_FLAGS_1(fabs_DT, TCG_CALL_NO_RWG_SE, f64, f64) -DEF_HELPER_3(fadd_FT, f32, env, f32, f32) -DEF_HELPER_3(fadd_DT, f64, env, f64, f64) -DEF_HELPER_2(fcnvsd_FT_DT, f64, env, f32) -DEF_HELPER_2(fcnvds_DT_FT, f32, env, f64) +DEF_HELPER_FLAGS_3(fadd_FT, TCG_CALL_NO_WG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fadd_DT, TCG_CALL_NO_WG, f64, env, f64, f64) +DEF_HELPER_FLAGS_2(fcnvsd_FT_DT, TCG_CALL_NO_WG, f64, env, f32) +DEF_HELPER_FLAGS_2(fcnvds_DT_FT, TCG_CALL_NO_WG, f32, env, f64) DEF_HELPER_3(fcmp_eq_FT, void, env, f32, f32) DEF_HELPER_3(fcmp_eq_DT, void, env, f64, f64) DEF_HELPER_3(fcmp_gt_FT, void, env, f32, f32) DEF_HELPER_3(fcmp_gt_DT, void, env, f64, f64) -DEF_HELPER_3(fdiv_FT, f32, env, f32, f32) -DEF_HELPER_3(fdiv_DT, f64, env, f64, f64) -DEF_HELPER_2(float_FT, f32, env, i32) -DEF_HELPER_2(float_DT, f64, env, i32) -DEF_HELPER_4(fmac_FT, f32, env, f32, f32, f32) -DEF_HELPER_3(fmul_FT, f32, env, f32, f32) -DEF_HELPER_3(fmul_DT, f64, env, f64, f64) +DEF_HELPER_FLAGS_3(fdiv_FT, TCG_CALL_NO_WG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fdiv_DT, TCG_CALL_NO_WG, f64, env, f64, f64) +DEF_HELPER_FLAGS_2(float_FT, TCG_CALL_NO_WG, f32, env, i32) +DEF_HELPER_FLAGS_2(float_DT, TCG_CALL_NO_WG, f64, env, i32) +DEF_HELPER_FLAGS_4(fmac_FT, TCG_CALL_NO_WG, f32, env, f32, f32, f32) +DEF_HELPER_FLAGS_3(fmul_FT, TCG_CALL_NO_WG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fmul_DT, TCG_CALL_NO_WG, f64, env, f64, f64) DEF_HELPER_FLAGS_1(fneg_T, TCG_CALL_NO_RWG_SE, f32, f32) -DEF_HELPER_3(fsub_FT, f32, env, f32, f32) -DEF_HELPER_3(fsub_DT, f64, env, f64, f64) -DEF_HELPER_2(fsqrt_FT, f32, env, f32) -DEF_HELPER_2(fsqrt_DT, f64, env, f64) -DEF_HELPER_2(ftrc_FT, i32, env, f32) -DEF_HELPER_2(ftrc_DT, i32, env, f64) +DEF_HELPER_FLAGS_3(fsub_FT, TCG_CALL_NO_WG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fsub_DT, TCG_CALL_NO_WG, f64, env, f64, f64) +DEF_HELPER_FLAGS_2(fsqrt_FT, TCG_CALL_NO_WG, f32, env, f32) +DEF_HELPER_FLAGS_2(fsqrt_DT, TCG_CALL_NO_WG, f64, env, f64) +DEF_HELPER_FLAGS_2(ftrc_FT, TCG_CALL_NO_WG, i32, env, f32) +DEF_HELPER_FLAGS_2(ftrc_DT, TCG_CALL_NO_WG, i32, env, f64) DEF_HELPER_3(fipr, void, env, i32, i32) DEF_HELPER_2(ftrv, void, env, i32) diff --git a/qemu/target-sh4/monitor.c b/qemu/target-sh4/monitor.c new file mode 100644 index 000000000..426e5d491 --- /dev/null +++ b/qemu/target-sh4/monitor.c @@ -0,0 +1,53 @@ +/* + * QEMU monitor + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "cpu.h" +#include "monitor/monitor.h" +#include "monitor/hmp-target.h" +#include "hmp.h" + +static void print_tlb(Monitor *mon, int idx, tlb_t *tlb) +{ + monitor_printf(mon, " tlb%i:\t" + "asid=%hhu vpn=%x\tppn=%x\tsz=%hhu size=%u\t" + "v=%hhu shared=%hhu cached=%hhu prot=%hhu " + "dirty=%hhu writethrough=%hhu\n", + idx, + tlb->asid, tlb->vpn, tlb->ppn, tlb->sz, tlb->size, + tlb->v, tlb->sh, tlb->c, tlb->pr, + tlb->d, tlb->wt); +} + +void hmp_info_tlb(Monitor *mon, const QDict *qdict) +{ + CPUArchState *env = mon_get_cpu_env(); + int i; + + monitor_printf (mon, "ITLB:\n"); + for (i = 0 ; i < ITLB_SIZE ; i++) + print_tlb (mon, i, &env->itlb[i]); + monitor_printf (mon, "UTLB:\n"); + for (i = 0 ; i < UTLB_SIZE ; i++) + print_tlb (mon, i, &env->utlb[i]); +} diff --git a/qemu/target-sh4/op_helper.c b/qemu/target-sh4/op_helper.c index cbc11aecc..368e687d6 100644 --- a/qemu/target-sh4/op_helper.c +++ b/qemu/target-sh4/op_helper.c @@ -16,8 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include -#include +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" diff --git a/qemu/target-sh4/translate.c b/qemu/target-sh4/translate.c index 3b4a1b5ce..7c189680a 100644 --- a/qemu/target-sh4/translate.c +++ b/qemu/target-sh4/translate.c @@ -19,6 +19,7 @@ #define DEBUG_DISAS +#include "qemu/osdep.h" #include "cpu.h" #include "disas/disas.h" #include "tcg-op.h" @@ -28,6 +29,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" typedef struct DisasContext { @@ -59,7 +61,7 @@ enum { }; /* global register indexes */ -static TCGv_ptr cpu_env; +static TCGv_env cpu_env; static TCGv cpu_gregs[24]; static TCGv cpu_sr, cpu_sr_m, cpu_sr_q, cpu_sr_t; static TCGv cpu_pc, cpu_ssr, cpu_spc, cpu_gbr; @@ -70,8 +72,6 @@ static TCGv cpu_fregs[32]; /* internal register indexes */ static TCGv cpu_flags, cpu_delayed_pc; -static uint32_t gen_opc_hflags[OPC_BUF_SIZE]; - #include "exec/gen-icount.h" void sh4_translate_init(void) @@ -102,53 +102,53 @@ void sh4_translate_init(void) cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); for (i = 0; i < 24; i++) - cpu_gregs[i] = tcg_global_mem_new_i32(TCG_AREG0, + cpu_gregs[i] = tcg_global_mem_new_i32(cpu_env, offsetof(CPUSH4State, gregs[i]), gregnames[i]); - cpu_pc = tcg_global_mem_new_i32(TCG_AREG0, + cpu_pc = tcg_global_mem_new_i32(cpu_env, offsetof(CPUSH4State, pc), "PC"); - cpu_sr = tcg_global_mem_new_i32(TCG_AREG0, + cpu_sr = tcg_global_mem_new_i32(cpu_env, offsetof(CPUSH4State, sr), "SR"); - cpu_sr_m = tcg_global_mem_new_i32(TCG_AREG0, - offsetof(CPUSH4State, sr_m), "SR_M"); - cpu_sr_q = tcg_global_mem_new_i32(TCG_AREG0, - offsetof(CPUSH4State, sr_q), "SR_Q"); - cpu_sr_t = tcg_global_mem_new_i32(TCG_AREG0, - offsetof(CPUSH4State, sr_t), "SR_T"); - cpu_ssr = tcg_global_mem_new_i32(TCG_AREG0, + cpu_sr_m = tcg_global_mem_new_i32(cpu_env, + offsetof(CPUSH4State, sr_m), "SR_M"); + cpu_sr_q = tcg_global_mem_new_i32(cpu_env, + offsetof(CPUSH4State, sr_q), "SR_Q"); + cpu_sr_t = tcg_global_mem_new_i32(cpu_env, + offsetof(CPUSH4State, sr_t), "SR_T"); + cpu_ssr = tcg_global_mem_new_i32(cpu_env, offsetof(CPUSH4State, ssr), "SSR"); - cpu_spc = tcg_global_mem_new_i32(TCG_AREG0, + cpu_spc = tcg_global_mem_new_i32(cpu_env, offsetof(CPUSH4State, spc), "SPC"); - cpu_gbr = tcg_global_mem_new_i32(TCG_AREG0, + cpu_gbr = tcg_global_mem_new_i32(cpu_env, offsetof(CPUSH4State, gbr), "GBR"); - cpu_vbr = tcg_global_mem_new_i32(TCG_AREG0, + cpu_vbr = tcg_global_mem_new_i32(cpu_env, offsetof(CPUSH4State, vbr), "VBR"); - cpu_sgr = tcg_global_mem_new_i32(TCG_AREG0, + cpu_sgr = tcg_global_mem_new_i32(cpu_env, offsetof(CPUSH4State, sgr), "SGR"); - cpu_dbr = tcg_global_mem_new_i32(TCG_AREG0, + cpu_dbr = tcg_global_mem_new_i32(cpu_env, offsetof(CPUSH4State, dbr), "DBR"); - cpu_mach = tcg_global_mem_new_i32(TCG_AREG0, + cpu_mach = tcg_global_mem_new_i32(cpu_env, offsetof(CPUSH4State, mach), "MACH"); - cpu_macl = tcg_global_mem_new_i32(TCG_AREG0, + cpu_macl = tcg_global_mem_new_i32(cpu_env, offsetof(CPUSH4State, macl), "MACL"); - cpu_pr = tcg_global_mem_new_i32(TCG_AREG0, + cpu_pr = tcg_global_mem_new_i32(cpu_env, offsetof(CPUSH4State, pr), "PR"); - cpu_fpscr = tcg_global_mem_new_i32(TCG_AREG0, + cpu_fpscr = tcg_global_mem_new_i32(cpu_env, offsetof(CPUSH4State, fpscr), "FPSCR"); - cpu_fpul = tcg_global_mem_new_i32(TCG_AREG0, + cpu_fpul = tcg_global_mem_new_i32(cpu_env, offsetof(CPUSH4State, fpul), "FPUL"); - cpu_flags = tcg_global_mem_new_i32(TCG_AREG0, + cpu_flags = tcg_global_mem_new_i32(cpu_env, offsetof(CPUSH4State, flags), "_flags_"); - cpu_delayed_pc = tcg_global_mem_new_i32(TCG_AREG0, + cpu_delayed_pc = tcg_global_mem_new_i32(cpu_env, offsetof(CPUSH4State, delayed_pc), "_delayed_pc_"); - cpu_ldst = tcg_global_mem_new_i32(TCG_AREG0, + cpu_ldst = tcg_global_mem_new_i32(cpu_env, offsetof(CPUSH4State, ldst), "_ldst_"); for (i = 0; i < 32; i++) - cpu_fregs[i] = tcg_global_mem_new_i32(TCG_AREG0, + cpu_fregs[i] = tcg_global_mem_new_i32(cpu_env, offsetof(CPUSH4State, fregs[i]), fregnames[i]); @@ -288,10 +288,10 @@ static inline void gen_load_fpr64(TCGv_i64 t, int reg) static inline void gen_store_fpr64 (TCGv_i64 t, int reg) { TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(tmp, t); + tcg_gen_extrl_i64_i32(tmp, t); tcg_gen_mov_i32(cpu_fregs[reg + 1], tmp); tcg_gen_shri_i64(t, t, 32); - tcg_gen_trunc_i64_i32(tmp, t); + tcg_gen_extrl_i64_i32(tmp, t); tcg_gen_mov_i32(cpu_fregs[reg], tmp); tcg_temp_free_i32(tmp); } @@ -612,15 +612,11 @@ static void _decode_opc(DisasContext * ctx) return; case 0x6008: /* swap.b Rm,Rn */ { - TCGv high, low; - high = tcg_temp_new(); - tcg_gen_andi_i32(high, REG(B7_4), 0xffff0000); - low = tcg_temp_new(); + TCGv low = tcg_temp_new();; tcg_gen_ext16u_i32(low, REG(B7_4)); tcg_gen_bswap16_i32(low, low); - tcg_gen_or_i32(REG(B11_8), high, low); + tcg_gen_deposit_i32(REG(B11_8), REG(B7_4), low, 0, 16); tcg_temp_free(low); - tcg_temp_free(high); } return; case 0x6009: /* swap.w Rm,Rn */ @@ -692,18 +688,11 @@ static void _decode_opc(DisasContext * ctx) { TCGv cmp1 = tcg_temp_new(); TCGv cmp2 = tcg_temp_new(); - tcg_gen_xor_i32(cmp1, REG(B7_4), REG(B11_8)); - tcg_gen_andi_i32(cmp2, cmp1, 0xff000000); - tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, cmp2, 0); - tcg_gen_andi_i32(cmp2, cmp1, 0x00ff0000); - tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); - tcg_gen_or_i32(cpu_sr_t, cpu_sr_t, cmp2); - tcg_gen_andi_i32(cmp2, cmp1, 0x0000ff00); - tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); - tcg_gen_or_i32(cpu_sr_t, cpu_sr_t, cmp2); - tcg_gen_andi_i32(cmp2, cmp1, 0x000000ff); - tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); - tcg_gen_or_i32(cpu_sr_t, cpu_sr_t, cmp2); + tcg_gen_xor_i32(cmp2, REG(B7_4), REG(B11_8)); + tcg_gen_subi_i32(cmp1, cmp2, 0x01010101); + tcg_gen_andc_i32(cmp1, cmp1, cmp2); + tcg_gen_andi_i32(cmp1, cmp1, 0x80808080); + tcg_gen_setcondi_i32(TCG_COND_NE, cpu_sr_t, cmp1, 0); tcg_temp_free(cmp2); tcg_temp_free(cmp1); } @@ -843,67 +832,54 @@ static void _decode_opc(DisasContext * ctx) return; case 0x400c: /* shad Rm,Rn */ { - TCGLabel *label1 = gen_new_label(); - TCGLabel *label2 = gen_new_label(); - TCGLabel *label3 = gen_new_label(); - TCGLabel *label4 = gen_new_label(); - TCGv shift; - tcg_gen_brcondi_i32(TCG_COND_LT, REG(B7_4), 0, label1); - /* Rm positive, shift to the left */ - shift = tcg_temp_new(); - tcg_gen_andi_i32(shift, REG(B7_4), 0x1f); - tcg_gen_shl_i32(REG(B11_8), REG(B11_8), shift); - tcg_temp_free(shift); - tcg_gen_br(label4); - /* Rm negative, shift to the right */ - gen_set_label(label1); - shift = tcg_temp_new(); - tcg_gen_andi_i32(shift, REG(B7_4), 0x1f); - tcg_gen_brcondi_i32(TCG_COND_EQ, shift, 0, label2); - tcg_gen_not_i32(shift, REG(B7_4)); - tcg_gen_andi_i32(shift, shift, 0x1f); - tcg_gen_addi_i32(shift, shift, 1); - tcg_gen_sar_i32(REG(B11_8), REG(B11_8), shift); - tcg_temp_free(shift); - tcg_gen_br(label4); - /* Rm = -32 */ - gen_set_label(label2); - tcg_gen_brcondi_i32(TCG_COND_LT, REG(B11_8), 0, label3); - tcg_gen_movi_i32(REG(B11_8), 0); - tcg_gen_br(label4); - gen_set_label(label3); - tcg_gen_movi_i32(REG(B11_8), 0xffffffff); - gen_set_label(label4); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + tcg_gen_andi_i32(t0, REG(B7_4), 0x1f); + + /* positive case: shift to the left */ + tcg_gen_shl_i32(t1, REG(B11_8), t0); + + /* negative case: shift to the right in two steps to + correctly handle the -32 case */ + tcg_gen_xori_i32(t0, t0, 0x1f); + tcg_gen_sar_i32(t2, REG(B11_8), t0); + tcg_gen_sari_i32(t2, t2, 1); + + /* select between the two cases */ + tcg_gen_movi_i32(t0, 0); + tcg_gen_movcond_i32(TCG_COND_GE, REG(B11_8), REG(B7_4), t0, t1, t2); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); } return; case 0x400d: /* shld Rm,Rn */ { - TCGLabel *label1 = gen_new_label(); - TCGLabel *label2 = gen_new_label(); - TCGLabel *label3 = gen_new_label(); - TCGv shift; - tcg_gen_brcondi_i32(TCG_COND_LT, REG(B7_4), 0, label1); - /* Rm positive, shift to the left */ - shift = tcg_temp_new(); - tcg_gen_andi_i32(shift, REG(B7_4), 0x1f); - tcg_gen_shl_i32(REG(B11_8), REG(B11_8), shift); - tcg_temp_free(shift); - tcg_gen_br(label3); - /* Rm negative, shift to the right */ - gen_set_label(label1); - shift = tcg_temp_new(); - tcg_gen_andi_i32(shift, REG(B7_4), 0x1f); - tcg_gen_brcondi_i32(TCG_COND_EQ, shift, 0, label2); - tcg_gen_not_i32(shift, REG(B7_4)); - tcg_gen_andi_i32(shift, shift, 0x1f); - tcg_gen_addi_i32(shift, shift, 1); - tcg_gen_shr_i32(REG(B11_8), REG(B11_8), shift); - tcg_temp_free(shift); - tcg_gen_br(label3); - /* Rm = -32 */ - gen_set_label(label2); - tcg_gen_movi_i32(REG(B11_8), 0); - gen_set_label(label3); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + tcg_gen_andi_i32(t0, REG(B7_4), 0x1f); + + /* positive case: shift to the left */ + tcg_gen_shl_i32(t1, REG(B11_8), t0); + + /* negative case: shift to the right in two steps to + correctly handle the -32 case */ + tcg_gen_xori_i32(t0, t0, 0x1f); + tcg_gen_shr_i32(t2, REG(B11_8), t0); + tcg_gen_shri_i32(t2, t2, 1); + + /* select between the two cases */ + tcg_gen_movi_i32(t0, 0); + tcg_gen_movcond_i32(TCG_COND_GE, REG(B11_8), REG(B7_4), t0, t1, t2); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); } return; case 0x3008: /* sub Rm,Rn */ @@ -1814,10 +1790,6 @@ static void decode_opc(DisasContext * ctx) { uint32_t old_flags = ctx->flags; - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { - tcg_gen_debug_insn_start(ctx->pc); - } - _decode_opc(ctx); if (old_flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { @@ -1844,16 +1816,12 @@ static void decode_opc(DisasContext * ctx) gen_store_flags(ctx->flags); } -static inline void -gen_intermediate_code_internal(SuperHCPU *cpu, TranslationBlock *tb, - bool search_pc) +void gen_intermediate_code(CPUSH4State * env, struct TranslationBlock *tb) { + SuperHCPU *cpu = sh_env_get_cpu(env); CPUState *cs = CPU(cpu); - CPUSH4State *env = &cpu->env; DisasContext ctx; target_ulong pc_start; - CPUBreakpoint *bp; - int i, ii; int num_insns; int max_insns; @@ -1870,45 +1838,39 @@ gen_intermediate_code_internal(SuperHCPU *cpu, TranslationBlock *tb, ctx.features = env->features; ctx.has_movcal = (ctx.flags & TB_FLAG_PENDING_MOVCA); - ii = -1; num_insns = 0; max_insns = tb->cflags & CF_COUNT_MASK; - if (max_insns == 0) + if (max_insns == 0) { max_insns = CF_COUNT_MASK; + } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } + gen_tb_start(tb); while (ctx.bstate == BS_NONE && !tcg_op_buf_full()) { - if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { - QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - if (ctx.pc == bp->pc) { - /* We have hit a breakpoint - make sure PC is up-to-date */ - tcg_gen_movi_i32(cpu_pc, ctx.pc); - gen_helper_debug(cpu_env); - ctx.bstate = BS_BRANCH; - break; - } - } - } - if (search_pc) { - i = tcg_op_buf_count(); - if (ii < i) { - ii++; - while (ii < i) - tcg_ctx.gen_opc_instr_start[ii++] = 0; - } - tcg_ctx.gen_opc_pc[ii] = ctx.pc; - gen_opc_hflags[ii] = ctx.flags; - tcg_ctx.gen_opc_instr_start[ii] = 1; - tcg_ctx.gen_opc_icount[ii] = num_insns; + tcg_gen_insn_start(ctx.pc, ctx.flags); + num_insns++; + + if (unlikely(cpu_breakpoint_test(cs, ctx.pc, BP_ANY))) { + /* We have hit a breakpoint - make sure PC is up-to-date */ + tcg_gen_movi_i32(cpu_pc, ctx.pc); + gen_helper_debug(cpu_env); + ctx.bstate = BS_BRANCH; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + ctx.pc += 2; + break; } - if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) + + if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { gen_io_start(); -#if 0 - fprintf(stderr, "Loading opcode at address 0x%08x\n", ctx.pc); - fflush(stderr); -#endif + } + ctx.opcode = cpu_lduw_code(env, ctx.pc); decode_opc(&ctx); - num_insns++; ctx.pc += 2; if ((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0) break; @@ -1948,15 +1910,8 @@ gen_intermediate_code_internal(SuperHCPU *cpu, TranslationBlock *tb, gen_tb_end(tb, num_insns); - if (search_pc) { - i = tcg_op_buf_count(); - ii++; - while (ii <= i) - tcg_ctx.gen_opc_instr_start[ii++] = 0; - } else { - tb->size = ctx.pc - pc_start; - tb->icount = num_insns; - } + tb->size = ctx.pc - pc_start; + tb->icount = num_insns; #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { @@ -1967,18 +1922,9 @@ gen_intermediate_code_internal(SuperHCPU *cpu, TranslationBlock *tb, #endif } -void gen_intermediate_code(CPUSH4State * env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(sh_env_get_cpu(env), tb, false); -} - -void gen_intermediate_code_pc(CPUSH4State * env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(sh_env_get_cpu(env), tb, true); -} - -void restore_state_to_opc(CPUSH4State *env, TranslationBlock *tb, int pc_pos) +void restore_state_to_opc(CPUSH4State *env, TranslationBlock *tb, + target_ulong *data) { - env->pc = tcg_ctx.gen_opc_pc[pc_pos]; - env->flags = gen_opc_hflags[pc_pos]; + env->pc = data[0]; + env->flags = data[1]; } diff --git a/qemu/target-sparc/Makefile.objs b/qemu/target-sparc/Makefile.objs index 1cd81cccc..ec905698c 100644 --- a/qemu/target-sparc/Makefile.objs +++ b/qemu/target-sparc/Makefile.objs @@ -1,4 +1,4 @@ -obj-$(CONFIG_SOFTMMU) += machine.o +obj-$(CONFIG_SOFTMMU) += machine.o monitor.o obj-y += translate.o helper.o cpu.o obj-y += fop_helper.o cc_helper.o win_helper.o mmu_helper.o ldst_helper.o obj-$(TARGET_SPARC) += int32_helper.o diff --git a/qemu/target-sparc/cc_helper.c b/qemu/target-sparc/cc_helper.c index 35dab7321..44c440934 100644 --- a/qemu/target-sparc/cc_helper.c +++ b/qemu/target-sparc/cc_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" diff --git a/qemu/target-sparc/cpu-qom.h b/qemu/target-sparc/cpu-qom.h index 477c4d513..5096b1047 100644 --- a/qemu/target-sparc/cpu-qom.h +++ b/qemu/target-sparc/cpu-qom.h @@ -75,6 +75,10 @@ static inline SPARCCPU *sparc_env_get_cpu(CPUSPARCState *env) #define ENV_OFFSET offsetof(SPARCCPU, env) +#ifndef CONFIG_USER_ONLY +extern const struct VMStateDescription vmstate_sparc_cpu; +#endif + void sparc_cpu_do_interrupt(CPUState *cpu); void sparc_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags); diff --git a/qemu/target-sparc/cpu.c b/qemu/target-sparc/cpu.c index 9528e3afb..fe4119e2b 100644 --- a/qemu/target-sparc/cpu.c +++ b/qemu/target-sparc/cpu.c @@ -17,6 +17,8 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "qemu/error-report.h" @@ -90,6 +92,14 @@ static bool sparc_cpu_exec_interrupt(CPUState *cs, int interrupt_request) return false; } +static void cpu_sparc_disas_set_info(CPUState *cpu, disassemble_info *info) +{ + info->print_insn = print_insn_sparc; +#ifdef TARGET_SPARC64 + info->mach = bfd_mach_sparc_v9b; +#endif +} + static int cpu_sparc_register(SPARCCPU *cpu, const char *cpu_model) { CPUClass *cc = CPU_GET_CLASS(cpu); @@ -847,13 +857,22 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data) cc->do_unassigned_access = sparc_cpu_unassigned_access; cc->do_unaligned_access = sparc_cpu_do_unaligned_access; cc->get_phys_page_debug = sparc_cpu_get_phys_page_debug; + cc->vmsd = &vmstate_sparc_cpu; #endif + cc->disas_set_info = cpu_sparc_disas_set_info; #if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) cc->gdb_num_core_regs = 86; #else cc->gdb_num_core_regs = 72; #endif + + /* + * Reason: sparc_cpu_initfn() calls cpu_exec_init(), which saves + * the object in cpus -> dangling pointer after final + * object_unref(). + */ + dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo sparc_cpu_type_info = { diff --git a/qemu/target-sparc/cpu.h b/qemu/target-sparc/cpu.h index 0522b65f1..dc4612275 100644 --- a/qemu/target-sparc/cpu.h +++ b/qemu/target-sparc/cpu.h @@ -1,7 +1,6 @@ #ifndef CPU_SPARC_H #define CPU_SPARC_H -#include "config.h" #include "qemu-common.h" #include "qemu/bswap.h" @@ -31,12 +30,6 @@ #include "fpu/softfloat.h" -#if !defined(TARGET_SPARC64) -#define ELF_MACHINE EM_SPARC -#else -#define ELF_MACHINE EM_SPARCV9 -#endif - /*#define EXCP_INTERRUPT 0x100*/ /* trap definitions */ @@ -236,6 +229,7 @@ typedef struct trap_state { uint32_t tt; } trap_state; #endif +#define TARGET_INSN_START_EXTRA_WORDS 1 typedef struct sparc_def_t { const char *name; @@ -371,16 +365,14 @@ struct CPUTimer uint32_t frequency; uint32_t disabled; uint64_t disabled_mask; + uint32_t npt; + uint64_t npt_mask; int64_t clock_offset; QEMUTimer *qtimer; }; typedef struct CPUTimer CPUTimer; -struct QEMUFile; -void cpu_put_timer(struct QEMUFile *f, CPUTimer *s); -void cpu_get_timer(struct QEMUFile *f, CPUTimer *s); - typedef struct CPUSPARCState CPUSPARCState; struct CPUSPARCState { @@ -542,6 +534,7 @@ int cpu_sparc_exec(CPUState *cpu); /* win_helper.c */ target_ulong cpu_get_psr(CPUSPARCState *env1); void cpu_put_psr(CPUSPARCState *env1, target_ulong val); +void cpu_put_psr_raw(CPUSPARCState *env1, target_ulong val); #ifdef TARGET_SPARC64 target_ulong cpu_get_ccr(CPUSPARCState *env1); void cpu_put_ccr(CPUSPARCState *env1, target_ulong val); @@ -598,12 +591,9 @@ int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc); #endif #define cpu_exec cpu_sparc_exec -#define cpu_gen_code cpu_sparc_gen_code #define cpu_signal_handler cpu_sparc_signal_handler #define cpu_list sparc_cpu_list -#define CPU_SAVE_VERSION 7 - /* MMU modes definitions */ #if defined (TARGET_SPARC64) #define MMU_USER_IDX 0 @@ -642,7 +632,7 @@ static inline int cpu_supervisor_mode(CPUSPARCState *env1) } #endif -static inline int cpu_mmu_index(CPUSPARCState *env1) +static inline int cpu_mmu_index(CPUSPARCState *env1, bool ifetch) { #if defined(CONFIG_USER_ONLY) return MMU_USER_IDX; diff --git a/qemu/target-sparc/fop_helper.c b/qemu/target-sparc/fop_helper.c index ee4592ef2..08306436a 100644 --- a/qemu/target-sparc/fop_helper.c +++ b/qemu/target-sparc/fop_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" diff --git a/qemu/target-sparc/gdbstub.c b/qemu/target-sparc/gdbstub.c index 3de3242b2..e530dc52f 100644 --- a/qemu/target-sparc/gdbstub.c +++ b/qemu/target-sparc/gdbstub.c @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "exec/gdbstub.h" diff --git a/qemu/target-sparc/helper.c b/qemu/target-sparc/helper.c index 4850c7cec..8349cbe2c 100644 --- a/qemu/target-sparc/helper.c +++ b/qemu/target-sparc/helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" @@ -51,10 +52,16 @@ void helper_tick_set_count(void *opaque, uint64_t count) #endif } -uint64_t helper_tick_get_count(void *opaque) +uint64_t helper_tick_get_count(CPUSPARCState *env, void *opaque, int mem_idx) { #if !defined(CONFIG_USER_ONLY) - return cpu_tick_get_count(opaque); + CPUTimer *timer = opaque; + + if (timer->npt && mem_idx < MMU_KERNEL_IDX) { + helper_raise_exception(env, TT_PRIV_INSN); + } + + return cpu_tick_get_count(timer); #else return 0; #endif diff --git a/qemu/target-sparc/helper.h b/qemu/target-sparc/helper.h index 1ad23e8db..4374f0dd2 100644 --- a/qemu/target-sparc/helper.h +++ b/qemu/target-sparc/helper.h @@ -25,7 +25,7 @@ DEF_HELPER_2(set_softint, void, env, i64) DEF_HELPER_2(clear_softint, void, env, i64) DEF_HELPER_2(write_softint, void, env, i64) DEF_HELPER_2(tick_set_count, void, ptr, i64) -DEF_HELPER_1(tick_get_count, i64, ptr) +DEF_HELPER_3(tick_get_count, i64, env, ptr, int) DEF_HELPER_2(tick_set_limit, void, ptr, i64) #endif #if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) diff --git a/qemu/target-sparc/int32_helper.c b/qemu/target-sparc/int32_helper.c index 7c380ba2a..09afe136e 100644 --- a/qemu/target-sparc/int32_helper.c +++ b/qemu/target-sparc/int32_helper.c @@ -17,9 +17,11 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "trace.h" #include "sysemu/sysemu.h" +#include "exec/log.h" #define DEBUG_PCALL diff --git a/qemu/target-sparc/int64_helper.c b/qemu/target-sparc/int64_helper.c index b02d22b19..29360fa5f 100644 --- a/qemu/target-sparc/int64_helper.c +++ b/qemu/target-sparc/int64_helper.c @@ -17,8 +17,10 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" +#include "exec/log.h" #include "trace.h" #define DEBUG_PCALL @@ -156,9 +158,8 @@ void sparc_cpu_do_interrupt(CPUState *cs) } else if ((intno & 0x1c0) == TT_FILL) { cpu_set_cwp(env, cpu_cwp_inc(env, env->cwp + 1)); } - env->tbr &= ~0x7fffULL; - env->tbr |= ((env->tl > 1) ? 1 << 14 : 0) | (intno << 5); - env->pc = env->tbr; + env->pc = env->tbr & ~0x7fffULL; + env->pc |= ((env->tl > 1) ? 1 << 14 : 0) | (intno << 5); env->npc = env->pc + 4; cs->exception_index = -1; } diff --git a/qemu/target-sparc/ldst_helper.c b/qemu/target-sparc/ldst_helper.c index c7ad47d35..658e7d858 100644 --- a/qemu/target-sparc/ldst_helper.c +++ b/qemu/target-sparc/ldst_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" @@ -2058,11 +2059,11 @@ void helper_ldda_asi(CPUSPARCState *env, target_ulong addr, int asi, int rd) bswap64s(&env->gregs[rd + 1]); } } else { - env->regwptr[rd] = cpu_ldq_nucleus(env, addr); - env->regwptr[rd + 1] = cpu_ldq_nucleus(env, addr + 8); + env->regwptr[rd - 8] = cpu_ldq_nucleus(env, addr); + env->regwptr[rd + 1 - 8] = cpu_ldq_nucleus(env, addr + 8); if (asi == 0x2c) { - bswap64s(&env->regwptr[rd]); - bswap64s(&env->regwptr[rd + 1]); + bswap64s(&env->regwptr[rd - 8]); + bswap64s(&env->regwptr[rd + 1 - 8]); } } break; @@ -2075,8 +2076,8 @@ void helper_ldda_asi(CPUSPARCState *env, target_ulong addr, int asi, int rd) env->gregs[rd] = helper_ld_asi(env, addr, asi, 4, 0); env->gregs[rd + 1] = helper_ld_asi(env, addr + 4, asi, 4, 0); } else { - env->regwptr[rd] = helper_ld_asi(env, addr, asi, 4, 0); - env->regwptr[rd + 1] = helper_ld_asi(env, addr + 4, asi, 4, 0); + env->regwptr[rd - 8] = helper_ld_asi(env, addr, asi, 4, 0); + env->regwptr[rd + 1 - 8] = helper_ld_asi(env, addr + 4, asi, 4, 0); } break; } diff --git a/qemu/target-sparc/machine.c b/qemu/target-sparc/machine.c index 3f3de4c65..1046016f3 100644 --- a/qemu/target-sparc/machine.c +++ b/qemu/target-sparc/machine.c @@ -1,218 +1,191 @@ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/boards.h" #include "qemu/timer.h" #include "cpu.h" -void cpu_save(QEMUFile *f, void *opaque) -{ - CPUSPARCState *env = opaque; - int i; - uint32_t tmp; - - // if env->cwp == env->nwindows - 1, this will set the ins of the last - // window as the outs of the first window - cpu_set_cwp(env, env->cwp); +#ifdef TARGET_SPARC64 +static const VMStateDescription vmstate_cpu_timer = { + .name = "cpu_timer", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(frequency, CPUTimer), + VMSTATE_UINT32(disabled, CPUTimer), + VMSTATE_UINT64(disabled_mask, CPUTimer), + VMSTATE_UINT32(npt, CPUTimer), + VMSTATE_UINT64(npt_mask, CPUTimer), + VMSTATE_INT64(clock_offset, CPUTimer), + VMSTATE_TIMER_PTR(qtimer, CPUTimer), + VMSTATE_END_OF_LIST() + } +}; - for(i = 0; i < 8; i++) - qemu_put_betls(f, &env->gregs[i]); - qemu_put_be32s(f, &env->nwindows); - for(i = 0; i < env->nwindows * 16; i++) - qemu_put_betls(f, &env->regbase[i]); +#define VMSTATE_CPU_TIMER(_f, _s) \ + VMSTATE_STRUCT_POINTER(_f, _s, vmstate_cpu_timer, CPUTimer) - /* FPU */ - for (i = 0; i < TARGET_DPREGS; i++) { - qemu_put_be32(f, env->fpr[i].l.upper); - qemu_put_be32(f, env->fpr[i].l.lower); +static const VMStateDescription vmstate_trap_state = { + .name = "trap_state", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(tpc, trap_state), + VMSTATE_UINT64(tnpc, trap_state), + VMSTATE_UINT64(tstate, trap_state), + VMSTATE_UINT32(tt, trap_state), + VMSTATE_END_OF_LIST() } +}; - qemu_put_betls(f, &env->pc); - qemu_put_betls(f, &env->npc); - qemu_put_betls(f, &env->y); - tmp = cpu_get_psr(env); - qemu_put_be32(f, tmp); - qemu_put_betls(f, &env->fsr); - qemu_put_betls(f, &env->tbr); - tmp = env->interrupt_index; - qemu_put_be32(f, tmp); - qemu_put_be32s(f, &env->pil_in); -#ifndef TARGET_SPARC64 - qemu_put_be32s(f, &env->wim); - /* MMU */ - for (i = 0; i < 32; i++) - qemu_put_be32s(f, &env->mmuregs[i]); - for (i = 0; i < 4; i++) { - qemu_put_be64s(f, &env->mxccdata[i]); - } - for (i = 0; i < 8; i++) { - qemu_put_be64s(f, &env->mxccregs[i]); - } - qemu_put_be32s(f, &env->mmubpctrv); - qemu_put_be32s(f, &env->mmubpctrc); - qemu_put_be32s(f, &env->mmubpctrs); - qemu_put_be64s(f, &env->mmubpaction); - for (i = 0; i < 4; i++) { - qemu_put_be64s(f, &env->mmubpregs[i]); - } -#else - qemu_put_be64s(f, &env->lsu); - for (i = 0; i < 16; i++) { - qemu_put_be64s(f, &env->immuregs[i]); - qemu_put_be64s(f, &env->dmmuregs[i]); - } - for (i = 0; i < 64; i++) { - qemu_put_be64s(f, &env->itlb[i].tag); - qemu_put_be64s(f, &env->itlb[i].tte); - qemu_put_be64s(f, &env->dtlb[i].tag); - qemu_put_be64s(f, &env->dtlb[i].tte); +static const VMStateDescription vmstate_tlb_entry = { + .name = "tlb_entry", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(tag, SparcTLBEntry), + VMSTATE_UINT64(tte, SparcTLBEntry), + VMSTATE_END_OF_LIST() } - qemu_put_be32s(f, &env->mmu_version); - for (i = 0; i < MAXTL_MAX; i++) { - qemu_put_be64s(f, &env->ts[i].tpc); - qemu_put_be64s(f, &env->ts[i].tnpc); - qemu_put_be64s(f, &env->ts[i].tstate); - qemu_put_be32s(f, &env->ts[i].tt); - } - qemu_put_be32s(f, &env->xcc); - qemu_put_be32s(f, &env->asi); - qemu_put_be32s(f, &env->pstate); - qemu_put_be32s(f, &env->tl); - qemu_put_be32s(f, &env->cansave); - qemu_put_be32s(f, &env->canrestore); - qemu_put_be32s(f, &env->otherwin); - qemu_put_be32s(f, &env->wstate); - qemu_put_be32s(f, &env->cleanwin); - for (i = 0; i < 8; i++) - qemu_put_be64s(f, &env->agregs[i]); - for (i = 0; i < 8; i++) - qemu_put_be64s(f, &env->bgregs[i]); - for (i = 0; i < 8; i++) - qemu_put_be64s(f, &env->igregs[i]); - for (i = 0; i < 8; i++) - qemu_put_be64s(f, &env->mgregs[i]); - qemu_put_be64s(f, &env->fprs); - qemu_put_be64s(f, &env->tick_cmpr); - qemu_put_be64s(f, &env->stick_cmpr); - cpu_put_timer(f, env->tick); - cpu_put_timer(f, env->stick); - qemu_put_be64s(f, &env->gsr); - qemu_put_be32s(f, &env->gl); - qemu_put_be64s(f, &env->hpstate); - for (i = 0; i < MAXTL_MAX; i++) - qemu_put_be64s(f, &env->htstate[i]); - qemu_put_be64s(f, &env->hintp); - qemu_put_be64s(f, &env->htba); - qemu_put_be64s(f, &env->hver); - qemu_put_be64s(f, &env->hstick_cmpr); - qemu_put_be64s(f, &env->ssr); - cpu_put_timer(f, env->hstick); +}; #endif + +static int get_psr(QEMUFile *f, void *opaque, size_t size) +{ + SPARCCPU *cpu = opaque; + CPUSPARCState *env = &cpu->env; + uint32_t val = qemu_get_be32(f); + + /* needed to ensure that the wrapping registers are correctly updated */ + env->cwp = 0; + cpu_put_psr_raw(env, val); + + return 0; } -int cpu_load(QEMUFile *f, void *opaque, int version_id) +static void put_psr(QEMUFile *f, void *opaque, size_t size) { - CPUSPARCState *env = opaque; - SPARCCPU *cpu = sparc_env_get_cpu(env); - int i; - uint32_t tmp; - - if (version_id < 6) - return -EINVAL; - for(i = 0; i < 8; i++) - qemu_get_betls(f, &env->gregs[i]); - qemu_get_be32s(f, &env->nwindows); - for(i = 0; i < env->nwindows * 16; i++) - qemu_get_betls(f, &env->regbase[i]); - - /* FPU */ - for (i = 0; i < TARGET_DPREGS; i++) { - env->fpr[i].l.upper = qemu_get_be32(f); - env->fpr[i].l.lower = qemu_get_be32(f); - } + SPARCCPU *cpu = opaque; + CPUSPARCState *env = &cpu->env; + uint32_t val; + + val = cpu_get_psr(env); + + qemu_put_be32(f, val); +} - qemu_get_betls(f, &env->pc); - qemu_get_betls(f, &env->npc); - qemu_get_betls(f, &env->y); - tmp = qemu_get_be32(f); - env->cwp = 0; /* needed to ensure that the wrapping registers are - correctly updated */ - cpu_put_psr(env, tmp); - qemu_get_betls(f, &env->fsr); - qemu_get_betls(f, &env->tbr); - tmp = qemu_get_be32(f); - env->interrupt_index = tmp; - qemu_get_be32s(f, &env->pil_in); +static const VMStateInfo vmstate_psr = { + .name = "psr", + .get = get_psr, + .put = put_psr, +}; + +static void cpu_pre_save(void *opaque) +{ + SPARCCPU *cpu = opaque; + CPUSPARCState *env = &cpu->env; + + /* if env->cwp == env->nwindows - 1, this will set the ins of the last + * window as the outs of the first window + */ + cpu_set_cwp(env, env->cwp); +} + +/* 32-bit SPARC retains migration compatibility with older versions + * of QEMU; 64-bit SPARC has had a migration break since then, so the + * versions are different. + */ #ifndef TARGET_SPARC64 - qemu_get_be32s(f, &env->wim); - /* MMU */ - for (i = 0; i < 32; i++) - qemu_get_be32s(f, &env->mmuregs[i]); - for (i = 0; i < 4; i++) { - qemu_get_be64s(f, &env->mxccdata[i]); - } - for (i = 0; i < 8; i++) { - qemu_get_be64s(f, &env->mxccregs[i]); - } - qemu_get_be32s(f, &env->mmubpctrv); - qemu_get_be32s(f, &env->mmubpctrc); - qemu_get_be32s(f, &env->mmubpctrs); - qemu_get_be64s(f, &env->mmubpaction); - for (i = 0; i < 4; i++) { - qemu_get_be64s(f, &env->mmubpregs[i]); - } +#define SPARC_VMSTATE_VER 7 #else - qemu_get_be64s(f, &env->lsu); - for (i = 0; i < 16; i++) { - qemu_get_be64s(f, &env->immuregs[i]); - qemu_get_be64s(f, &env->dmmuregs[i]); - } - for (i = 0; i < 64; i++) { - qemu_get_be64s(f, &env->itlb[i].tag); - qemu_get_be64s(f, &env->itlb[i].tte); - qemu_get_be64s(f, &env->dtlb[i].tag); - qemu_get_be64s(f, &env->dtlb[i].tte); - } - qemu_get_be32s(f, &env->mmu_version); - for (i = 0; i < MAXTL_MAX; i++) { - qemu_get_be64s(f, &env->ts[i].tpc); - qemu_get_be64s(f, &env->ts[i].tnpc); - qemu_get_be64s(f, &env->ts[i].tstate); - qemu_get_be32s(f, &env->ts[i].tt); - } - qemu_get_be32s(f, &env->xcc); - qemu_get_be32s(f, &env->asi); - qemu_get_be32s(f, &env->pstate); - qemu_get_be32s(f, &env->tl); - qemu_get_be32s(f, &env->cansave); - qemu_get_be32s(f, &env->canrestore); - qemu_get_be32s(f, &env->otherwin); - qemu_get_be32s(f, &env->wstate); - qemu_get_be32s(f, &env->cleanwin); - for (i = 0; i < 8; i++) - qemu_get_be64s(f, &env->agregs[i]); - for (i = 0; i < 8; i++) - qemu_get_be64s(f, &env->bgregs[i]); - for (i = 0; i < 8; i++) - qemu_get_be64s(f, &env->igregs[i]); - for (i = 0; i < 8; i++) - qemu_get_be64s(f, &env->mgregs[i]); - qemu_get_be64s(f, &env->fprs); - qemu_get_be64s(f, &env->tick_cmpr); - qemu_get_be64s(f, &env->stick_cmpr); - cpu_get_timer(f, env->tick); - cpu_get_timer(f, env->stick); - qemu_get_be64s(f, &env->gsr); - qemu_get_be32s(f, &env->gl); - qemu_get_be64s(f, &env->hpstate); - for (i = 0; i < MAXTL_MAX; i++) - qemu_get_be64s(f, &env->htstate[i]); - qemu_get_be64s(f, &env->hintp); - qemu_get_be64s(f, &env->htba); - qemu_get_be64s(f, &env->hver); - qemu_get_be64s(f, &env->hstick_cmpr); - qemu_get_be64s(f, &env->ssr); - cpu_get_timer(f, env->hstick); +#define SPARC_VMSTATE_VER 9 #endif - tlb_flush(CPU(cpu), 1); - return 0; -} + +const VMStateDescription vmstate_sparc_cpu = { + .name = "cpu", + .version_id = SPARC_VMSTATE_VER, + .minimum_version_id = SPARC_VMSTATE_VER, + .minimum_version_id_old = SPARC_VMSTATE_VER, + .pre_save = cpu_pre_save, + .fields = (VMStateField[]) { + VMSTATE_UINTTL_ARRAY(env.gregs, SPARCCPU, 8), + VMSTATE_UINT32(env.nwindows, SPARCCPU), + VMSTATE_VARRAY_MULTIPLY(env.regbase, SPARCCPU, env.nwindows, 16, + vmstate_info_uinttl, target_ulong), + VMSTATE_CPUDOUBLE_ARRAY(env.fpr, SPARCCPU, TARGET_DPREGS), + VMSTATE_UINTTL(env.pc, SPARCCPU), + VMSTATE_UINTTL(env.npc, SPARCCPU), + VMSTATE_UINTTL(env.y, SPARCCPU), + { + + .name = "psr", + .version_id = 0, + .size = sizeof(uint32_t), + .info = &vmstate_psr, + .flags = VMS_SINGLE, + .offset = 0, + }, + VMSTATE_UINTTL(env.fsr, SPARCCPU), + VMSTATE_UINTTL(env.tbr, SPARCCPU), + VMSTATE_INT32(env.interrupt_index, SPARCCPU), + VMSTATE_UINT32(env.pil_in, SPARCCPU), +#ifndef TARGET_SPARC64 + /* MMU */ + VMSTATE_UINT32(env.wim, SPARCCPU), + VMSTATE_UINT32_ARRAY(env.mmuregs, SPARCCPU, 32), + VMSTATE_UINT64_ARRAY(env.mxccdata, SPARCCPU, 4), + VMSTATE_UINT64_ARRAY(env.mxccregs, SPARCCPU, 8), + VMSTATE_UINT32(env.mmubpctrv, SPARCCPU), + VMSTATE_UINT32(env.mmubpctrc, SPARCCPU), + VMSTATE_UINT32(env.mmubpctrs, SPARCCPU), + VMSTATE_UINT64(env.mmubpaction, SPARCCPU), + VMSTATE_UINT64_ARRAY(env.mmubpregs, SPARCCPU, 4), +#else + VMSTATE_UINT64(env.lsu, SPARCCPU), + VMSTATE_UINT64_ARRAY(env.immuregs, SPARCCPU, 16), + VMSTATE_UINT64_ARRAY(env.dmmuregs, SPARCCPU, 16), + VMSTATE_STRUCT_ARRAY(env.itlb, SPARCCPU, 64, 0, + vmstate_tlb_entry, SparcTLBEntry), + VMSTATE_STRUCT_ARRAY(env.dtlb, SPARCCPU, 64, 0, + vmstate_tlb_entry, SparcTLBEntry), + VMSTATE_UINT32(env.mmu_version, SPARCCPU), + VMSTATE_STRUCT_ARRAY(env.ts, SPARCCPU, MAXTL_MAX, 0, + vmstate_trap_state, trap_state), + VMSTATE_UINT32(env.xcc, SPARCCPU), + VMSTATE_UINT32(env.asi, SPARCCPU), + VMSTATE_UINT32(env.pstate, SPARCCPU), + VMSTATE_UINT32(env.tl, SPARCCPU), + VMSTATE_UINT32(env.cansave, SPARCCPU), + VMSTATE_UINT32(env.canrestore, SPARCCPU), + VMSTATE_UINT32(env.otherwin, SPARCCPU), + VMSTATE_UINT32(env.wstate, SPARCCPU), + VMSTATE_UINT32(env.cleanwin, SPARCCPU), + VMSTATE_UINT64_ARRAY(env.agregs, SPARCCPU, 8), + VMSTATE_UINT64_ARRAY(env.bgregs, SPARCCPU, 8), + VMSTATE_UINT64_ARRAY(env.igregs, SPARCCPU, 8), + VMSTATE_UINT64_ARRAY(env.mgregs, SPARCCPU, 8), + VMSTATE_UINT64(env.fprs, SPARCCPU), + VMSTATE_UINT64(env.tick_cmpr, SPARCCPU), + VMSTATE_UINT64(env.stick_cmpr, SPARCCPU), + VMSTATE_CPU_TIMER(env.tick, SPARCCPU), + VMSTATE_CPU_TIMER(env.stick, SPARCCPU), + VMSTATE_UINT64(env.gsr, SPARCCPU), + VMSTATE_UINT32(env.gl, SPARCCPU), + VMSTATE_UINT64(env.hpstate, SPARCCPU), + VMSTATE_UINT64_ARRAY(env.htstate, SPARCCPU, MAXTL_MAX), + VMSTATE_UINT64(env.hintp, SPARCCPU), + VMSTATE_UINT64(env.htba, SPARCCPU), + VMSTATE_UINT64(env.hver, SPARCCPU), + VMSTATE_UINT64(env.hstick_cmpr, SPARCCPU), + VMSTATE_UINT64(env.ssr, SPARCCPU), + VMSTATE_CPU_TIMER(env.hstick, SPARCCPU), + /* On SPARC32 env.psrpil and env.cwp are migrated as part of the PSR */ + VMSTATE_UINT32(env.psrpil, SPARCCPU), + VMSTATE_UINT32(env.cwp, SPARCCPU), +#endif + VMSTATE_END_OF_LIST() + }, +}; diff --git a/qemu/target-sparc/mmu_helper.c b/qemu/target-sparc/mmu_helper.c index 2a0c6f0d3..aa80c4829 100644 --- a/qemu/target-sparc/mmu_helper.c +++ b/qemu/target-sparc/mmu_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "trace.h" #include "exec/address-spaces.h" @@ -849,7 +850,7 @@ hwaddr sparc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) SPARCCPU *cpu = SPARC_CPU(cs); CPUSPARCState *env = &cpu->env; hwaddr phys_addr; - int mmu_idx = cpu_mmu_index(env); + int mmu_idx = cpu_mmu_index(env, false); MemoryRegionSection section; if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 2, mmu_idx) != 0) { diff --git a/qemu/target-sparc/monitor.c b/qemu/target-sparc/monitor.c new file mode 100644 index 000000000..7cc1b0f87 --- /dev/null +++ b/qemu/target-sparc/monitor.c @@ -0,0 +1,159 @@ +/* + * QEMU monitor + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "cpu.h" +#include "monitor/monitor.h" +#include "monitor/hmp-target.h" +#include "hmp.h" + + +void hmp_info_tlb(Monitor *mon, const QDict *qdict) +{ + CPUArchState *env1 = mon_get_cpu_env(); + + dump_mmu((FILE*)mon, (fprintf_function)monitor_printf, env1); +} + +#ifndef TARGET_SPARC64 +static target_long monitor_get_psr (const struct MonitorDef *md, int val) +{ + CPUArchState *env = mon_get_cpu_env(); + + return cpu_get_psr(env); +} +#endif + +static target_long monitor_get_reg(const struct MonitorDef *md, int val) +{ + CPUArchState *env = mon_get_cpu_env(); + return env->regwptr[val]; +} + +const MonitorDef monitor_defs[] = { + { "g0", offsetof(CPUSPARCState, gregs[0]) }, + { "g1", offsetof(CPUSPARCState, gregs[1]) }, + { "g2", offsetof(CPUSPARCState, gregs[2]) }, + { "g3", offsetof(CPUSPARCState, gregs[3]) }, + { "g4", offsetof(CPUSPARCState, gregs[4]) }, + { "g5", offsetof(CPUSPARCState, gregs[5]) }, + { "g6", offsetof(CPUSPARCState, gregs[6]) }, + { "g7", offsetof(CPUSPARCState, gregs[7]) }, + { "o0", 0, monitor_get_reg }, + { "o1", 1, monitor_get_reg }, + { "o2", 2, monitor_get_reg }, + { "o3", 3, monitor_get_reg }, + { "o4", 4, monitor_get_reg }, + { "o5", 5, monitor_get_reg }, + { "o6", 6, monitor_get_reg }, + { "o7", 7, monitor_get_reg }, + { "l0", 8, monitor_get_reg }, + { "l1", 9, monitor_get_reg }, + { "l2", 10, monitor_get_reg }, + { "l3", 11, monitor_get_reg }, + { "l4", 12, monitor_get_reg }, + { "l5", 13, monitor_get_reg }, + { "l6", 14, monitor_get_reg }, + { "l7", 15, monitor_get_reg }, + { "i0", 16, monitor_get_reg }, + { "i1", 17, monitor_get_reg }, + { "i2", 18, monitor_get_reg }, + { "i3", 19, monitor_get_reg }, + { "i4", 20, monitor_get_reg }, + { "i5", 21, monitor_get_reg }, + { "i6", 22, monitor_get_reg }, + { "i7", 23, monitor_get_reg }, + { "pc", offsetof(CPUSPARCState, pc) }, + { "npc", offsetof(CPUSPARCState, npc) }, + { "y", offsetof(CPUSPARCState, y) }, +#ifndef TARGET_SPARC64 + { "psr", 0, &monitor_get_psr, }, + { "wim", offsetof(CPUSPARCState, wim) }, +#endif + { "tbr", offsetof(CPUSPARCState, tbr) }, + { "fsr", offsetof(CPUSPARCState, fsr) }, + { "f0", offsetof(CPUSPARCState, fpr[0].l.upper) }, + { "f1", offsetof(CPUSPARCState, fpr[0].l.lower) }, + { "f2", offsetof(CPUSPARCState, fpr[1].l.upper) }, + { "f3", offsetof(CPUSPARCState, fpr[1].l.lower) }, + { "f4", offsetof(CPUSPARCState, fpr[2].l.upper) }, + { "f5", offsetof(CPUSPARCState, fpr[2].l.lower) }, + { "f6", offsetof(CPUSPARCState, fpr[3].l.upper) }, + { "f7", offsetof(CPUSPARCState, fpr[3].l.lower) }, + { "f8", offsetof(CPUSPARCState, fpr[4].l.upper) }, + { "f9", offsetof(CPUSPARCState, fpr[4].l.lower) }, + { "f10", offsetof(CPUSPARCState, fpr[5].l.upper) }, + { "f11", offsetof(CPUSPARCState, fpr[5].l.lower) }, + { "f12", offsetof(CPUSPARCState, fpr[6].l.upper) }, + { "f13", offsetof(CPUSPARCState, fpr[6].l.lower) }, + { "f14", offsetof(CPUSPARCState, fpr[7].l.upper) }, + { "f15", offsetof(CPUSPARCState, fpr[7].l.lower) }, + { "f16", offsetof(CPUSPARCState, fpr[8].l.upper) }, + { "f17", offsetof(CPUSPARCState, fpr[8].l.lower) }, + { "f18", offsetof(CPUSPARCState, fpr[9].l.upper) }, + { "f19", offsetof(CPUSPARCState, fpr[9].l.lower) }, + { "f20", offsetof(CPUSPARCState, fpr[10].l.upper) }, + { "f21", offsetof(CPUSPARCState, fpr[10].l.lower) }, + { "f22", offsetof(CPUSPARCState, fpr[11].l.upper) }, + { "f23", offsetof(CPUSPARCState, fpr[11].l.lower) }, + { "f24", offsetof(CPUSPARCState, fpr[12].l.upper) }, + { "f25", offsetof(CPUSPARCState, fpr[12].l.lower) }, + { "f26", offsetof(CPUSPARCState, fpr[13].l.upper) }, + { "f27", offsetof(CPUSPARCState, fpr[13].l.lower) }, + { "f28", offsetof(CPUSPARCState, fpr[14].l.upper) }, + { "f29", offsetof(CPUSPARCState, fpr[14].l.lower) }, + { "f30", offsetof(CPUSPARCState, fpr[15].l.upper) }, + { "f31", offsetof(CPUSPARCState, fpr[15].l.lower) }, +#ifdef TARGET_SPARC64 + { "f32", offsetof(CPUSPARCState, fpr[16]) }, + { "f34", offsetof(CPUSPARCState, fpr[17]) }, + { "f36", offsetof(CPUSPARCState, fpr[18]) }, + { "f38", offsetof(CPUSPARCState, fpr[19]) }, + { "f40", offsetof(CPUSPARCState, fpr[20]) }, + { "f42", offsetof(CPUSPARCState, fpr[21]) }, + { "f44", offsetof(CPUSPARCState, fpr[22]) }, + { "f46", offsetof(CPUSPARCState, fpr[23]) }, + { "f48", offsetof(CPUSPARCState, fpr[24]) }, + { "f50", offsetof(CPUSPARCState, fpr[25]) }, + { "f52", offsetof(CPUSPARCState, fpr[26]) }, + { "f54", offsetof(CPUSPARCState, fpr[27]) }, + { "f56", offsetof(CPUSPARCState, fpr[28]) }, + { "f58", offsetof(CPUSPARCState, fpr[29]) }, + { "f60", offsetof(CPUSPARCState, fpr[30]) }, + { "f62", offsetof(CPUSPARCState, fpr[31]) }, + { "asi", offsetof(CPUSPARCState, asi) }, + { "pstate", offsetof(CPUSPARCState, pstate) }, + { "cansave", offsetof(CPUSPARCState, cansave) }, + { "canrestore", offsetof(CPUSPARCState, canrestore) }, + { "otherwin", offsetof(CPUSPARCState, otherwin) }, + { "wstate", offsetof(CPUSPARCState, wstate) }, + { "cleanwin", offsetof(CPUSPARCState, cleanwin) }, + { "fprs", offsetof(CPUSPARCState, fprs) }, +#endif + { NULL }, +}; + +const MonitorDef *target_monitor_defs(void) +{ + return monitor_defs; +} diff --git a/qemu/target-sparc/translate.c b/qemu/target-sparc/translate.c index c58dd4e95..7998ff57b 100644 --- a/qemu/target-sparc/translate.c +++ b/qemu/target-sparc/translate.c @@ -18,11 +18,7 @@ License along with this library; if not, see . */ -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include "cpu.h" #include "disas/disas.h" @@ -33,6 +29,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" #define DEBUG_DISAS @@ -42,11 +39,13 @@ according to jump_pc[T2] */ /* global register indexes */ -static TCGv_ptr cpu_env, cpu_regwptr; +static TCGv_env cpu_env; +static TCGv_ptr cpu_regwptr; static TCGv cpu_cc_src, cpu_cc_src2, cpu_cc_dst; static TCGv_i32 cpu_cc_op; static TCGv_i32 cpu_psr; -static TCGv cpu_fsr, cpu_pc, cpu_npc, cpu_gregs[8]; +static TCGv cpu_fsr, cpu_pc, cpu_npc; +static TCGv cpu_regs[32]; static TCGv cpu_y; #ifndef CONFIG_USER_ONLY static TCGv cpu_tbr; @@ -64,9 +63,6 @@ static TCGv cpu_wim; /* Floating point registers */ static TCGv_i64 cpu_fpr[TARGET_DPREGS]; -static target_ulong gen_opc_npc[OPC_BUF_SIZE]; -static target_ulong gen_opc_jump_pc[2]; - #include "exec/gen-icount.h" typedef struct DisasContext { @@ -164,7 +160,7 @@ static TCGv_i32 gen_load_fpr_F(DisasContext *dc, unsigned int src) TCGv_i64 t = tcg_temp_new_i64(); tcg_gen_shri_i64(t, cpu_fpr[src / 2], 32); - tcg_gen_trunc_i64_i32(ret, t); + tcg_gen_extrl_i64_i32(ret, t); tcg_temp_free_i64(t); return ret; @@ -279,36 +275,31 @@ static inline void gen_address_mask(DisasContext *dc, TCGv addr) static inline TCGv gen_load_gpr(DisasContext *dc, int reg) { - if (reg == 0 || reg >= 8) { + if (reg > 0) { + assert(reg < 32); + return cpu_regs[reg]; + } else { TCGv t = get_temp_tl(dc); - if (reg == 0) { - tcg_gen_movi_tl(t, 0); - } else { - tcg_gen_ld_tl(t, cpu_regwptr, (reg - 8) * sizeof(target_ulong)); - } + tcg_gen_movi_tl(t, 0); return t; - } else { - return cpu_gregs[reg]; } } static inline void gen_store_gpr(DisasContext *dc, int reg, TCGv v) { if (reg > 0) { - if (reg < 8) { - tcg_gen_mov_tl(cpu_gregs[reg], v); - } else { - tcg_gen_st_tl(v, cpu_regwptr, (reg - 8) * sizeof(target_ulong)); - } + assert(reg < 32); + tcg_gen_mov_tl(cpu_regs[reg], v); } } static inline TCGv gen_dest_gpr(DisasContext *dc, int reg) { - if (reg == 0 || reg >= 8) { - return get_temp_tl(dc); + if (reg > 0) { + assert(reg < 32); + return cpu_regs[reg]; } else { - return cpu_gregs[reg]; + return get_temp_tl(dc); } } @@ -379,8 +370,8 @@ static TCGv_i32 gen_add32_carry32(void) #if TARGET_LONG_BITS == 64 cc_src1_32 = tcg_temp_new_i32(); cc_src2_32 = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(cc_src1_32, cpu_cc_dst); - tcg_gen_trunc_i64_i32(cc_src2_32, cpu_cc_src); + tcg_gen_extrl_i64_i32(cc_src1_32, cpu_cc_dst); + tcg_gen_extrl_i64_i32(cc_src2_32, cpu_cc_src); #else cc_src1_32 = cpu_cc_dst; cc_src2_32 = cpu_cc_src; @@ -405,8 +396,8 @@ static TCGv_i32 gen_sub32_carry32(void) #if TARGET_LONG_BITS == 64 cc_src1_32 = tcg_temp_new_i32(); cc_src2_32 = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(cc_src1_32, cpu_cc_src); - tcg_gen_trunc_i64_i32(cc_src2_32, cpu_cc_src2); + tcg_gen_extrl_i64_i32(cc_src1_32, cpu_cc_src); + tcg_gen_extrl_i64_i32(cc_src2_32, cpu_cc_src2); #else cc_src1_32 = cpu_cc_src; cc_src2_32 = cpu_cc_src2; @@ -955,17 +946,44 @@ static inline void gen_branch2(DisasContext *dc, target_ulong pc1, gen_goto_tb(dc, 1, pc2, pc2 + 4); } -static inline void gen_branch_a(DisasContext *dc, target_ulong pc1, - target_ulong pc2, TCGv r_cond) +static void gen_branch_a(DisasContext *dc, target_ulong pc1) { TCGLabel *l1 = gen_new_label(); + target_ulong npc = dc->npc; - tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, 0, l1); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_cond, 0, l1); - gen_goto_tb(dc, 0, pc2, pc1); + gen_goto_tb(dc, 0, npc, pc1); gen_set_label(l1); - gen_goto_tb(dc, 1, pc2 + 4, pc2 + 8); + gen_goto_tb(dc, 1, npc + 4, npc + 8); + + dc->is_br = 1; +} + +static void gen_branch_n(DisasContext *dc, target_ulong pc1) +{ + target_ulong npc = dc->npc; + + if (likely(npc != DYNAMIC_PC)) { + dc->pc = npc; + dc->jump_pc[0] = pc1; + dc->jump_pc[1] = npc + 4; + dc->npc = JUMP_PC; + } else { + TCGv t, z; + + tcg_gen_mov_tl(cpu_pc, cpu_npc); + + tcg_gen_addi_tl(cpu_npc, cpu_npc, 4); + t = tcg_const_tl(pc1); + z = tcg_const_tl(0); + tcg_gen_movcond_tl(TCG_COND_NE, cpu_npc, cpu_cond, z, t, cpu_npc); + tcg_temp_free(t); + tcg_temp_free(z); + + dc->pc = DYNAMIC_PC; + } } static inline void gen_generic_branch(DisasContext *dc) @@ -1398,18 +1416,9 @@ static void do_branch(DisasContext *dc, int32_t offset, uint32_t insn, int cc) flush_cond(dc); gen_cond(cpu_cond, cc, cond, dc); if (a) { - gen_branch_a(dc, target, dc->npc, cpu_cond); - dc->is_br = 1; + gen_branch_a(dc, target); } else { - dc->pc = dc->npc; - dc->jump_pc[0] = target; - if (unlikely(dc->npc == DYNAMIC_PC)) { - dc->jump_pc[1] = DYNAMIC_PC; - tcg_gen_addi_tl(cpu_pc, cpu_npc, 4); - } else { - dc->jump_pc[1] = dc->npc + 4; - dc->npc = JUMP_PC; - } + gen_branch_n(dc, target); } } } @@ -1447,18 +1456,9 @@ static void do_fbranch(DisasContext *dc, int32_t offset, uint32_t insn, int cc) flush_cond(dc); gen_fcond(cpu_cond, cc, cond); if (a) { - gen_branch_a(dc, target, dc->npc, cpu_cond); - dc->is_br = 1; + gen_branch_a(dc, target); } else { - dc->pc = dc->npc; - dc->jump_pc[0] = target; - if (unlikely(dc->npc == DYNAMIC_PC)) { - dc->jump_pc[1] = DYNAMIC_PC; - tcg_gen_addi_tl(cpu_pc, cpu_npc, 4); - } else { - dc->jump_pc[1] = dc->npc + 4; - dc->npc = JUMP_PC; - } + gen_branch_n(dc, target); } } } @@ -1476,18 +1476,9 @@ static void do_branch_reg(DisasContext *dc, int32_t offset, uint32_t insn, flush_cond(dc); gen_cond_reg(cpu_cond, cond, r_reg); if (a) { - gen_branch_a(dc, target, dc->npc, cpu_cond); - dc->is_br = 1; + gen_branch_a(dc, target); } else { - dc->pc = dc->npc; - dc->jump_pc[0] = target; - if (unlikely(dc->npc == DYNAMIC_PC)) { - dc->jump_pc[1] = DYNAMIC_PC; - tcg_gen_addi_tl(cpu_pc, cpu_npc, 4); - } else { - dc->jump_pc[1] = dc->npc + 4; - dc->npc = JUMP_PC; - } + gen_branch_n(dc, target); } } @@ -2164,9 +2155,13 @@ static inline void gen_ldda_asi(DisasContext *dc, TCGv hi, TCGv addr, tcg_temp_free_i32(r_size); tcg_temp_free_i32(r_asi); - t = gen_dest_gpr(dc, rd + 1); + /* ??? Work around an apparent bug in Ubuntu gcc 4.8.2-10ubuntu2+12, + whereby "rd + 1" elicits "error: array subscript is above array". + Since we have already asserted that rd is even, the semantics + are unchanged. */ + t = gen_dest_gpr(dc, rd | 1); tcg_gen_trunc_i64_tl(t, t64); - gen_store_gpr(dc, rd + 1, t); + gen_store_gpr(dc, rd | 1, t); tcg_gen_shri_i64(t64, t64, 32); tcg_gen_trunc_i64_tl(hi, t64); @@ -2254,11 +2249,11 @@ static void gen_fmovs(DisasContext *dc, DisasCompare *cmp, int rd, int rs) the later. */ c32 = tcg_temp_new_i32(); if (cmp->is_bool) { - tcg_gen_trunc_i64_i32(c32, cmp->c1); + tcg_gen_extrl_i64_i32(c32, cmp->c1); } else { TCGv_i64 c64 = tcg_temp_new_i64(); tcg_gen_setcond_i64(cmp->cond, c64, cmp->c1, cmp->c2); - tcg_gen_trunc_i64_i32(c32, c64); + tcg_gen_extrl_i64_i32(c32, c64); tcg_temp_free_i64(c64); } @@ -2297,7 +2292,7 @@ static void gen_fmovq(DisasContext *dc, DisasCompare *cmp, int rd, int rs) } #ifndef CONFIG_USER_ONLY -static inline void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr, TCGv_ptr cpu_env) +static inline void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr, TCGv_env cpu_env) { TCGv_i32 r_tl = tcg_temp_new_i32(); @@ -2482,10 +2477,6 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) TCGv_i64 cpu_src1_64, cpu_src2_64, cpu_dst_64; target_long simm; - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { - tcg_gen_debug_insn_start(dc->pc); - } - opc = GET_FIELD(insn, 0, 1); rd = GET_FIELD(insn, 2, 6); @@ -2715,12 +2706,16 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) case 0x4: /* V9 rdtick */ { TCGv_ptr r_tickptr; + TCGv_i32 r_const; r_tickptr = tcg_temp_new_ptr(); + r_const = tcg_const_i32(dc->mem_idx); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, tick)); - gen_helper_tick_get_count(cpu_dst, r_tickptr); + gen_helper_tick_get_count(cpu_dst, cpu_env, r_tickptr, + r_const); tcg_temp_free_ptr(r_tickptr); + tcg_temp_free_i32(r_const); gen_store_gpr(dc, rd, cpu_dst); } break; @@ -2757,12 +2752,16 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) case 0x18: /* System tick */ { TCGv_ptr r_tickptr; + TCGv_i32 r_const; r_tickptr = tcg_temp_new_ptr(); + r_const = tcg_const_i32(dc->mem_idx); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, stick)); - gen_helper_tick_get_count(cpu_dst, r_tickptr); + gen_helper_tick_get_count(cpu_dst, cpu_env, r_tickptr, + r_const); tcg_temp_free_ptr(r_tickptr); + tcg_temp_free_i32(r_const); gen_store_gpr(dc, rd, cpu_dst); } break; @@ -2870,12 +2869,16 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) case 4: // tick { TCGv_ptr r_tickptr; + TCGv_i32 r_const; r_tickptr = tcg_temp_new_ptr(); + r_const = tcg_const_i32(dc->mem_idx); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, tick)); - gen_helper_tick_get_count(cpu_tmp0, r_tickptr); + gen_helper_tick_get_count(cpu_tmp0, cpu_env, + r_tickptr, r_const); tcg_temp_free_ptr(r_tickptr); + tcg_temp_free_i32(r_const); } break; case 5: // tba @@ -4667,7 +4670,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) TCGv r_const; gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld8s(cpu_val, cpu_addr, dc->mem_idx); + tcg_gen_qemu_ld8u(cpu_val, cpu_addr, dc->mem_idx); r_const = tcg_const_tl(0xff); tcg_gen_qemu_st8(r_const, cpu_addr, dc->mem_idx); tcg_temp_free(r_const); @@ -5104,7 +5107,8 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) if (IS_IMM) { goto illegal_insn; } - if (!supervisor(dc)) { + /* LEON3 allows CASA from user space with ASI 0xa */ + if ((GET_FIELD(insn, 19, 26) != 0xa) && !supervisor(dc)) { goto priv_insn; } #endif @@ -5213,16 +5217,12 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) } } -static inline void gen_intermediate_code_internal(SPARCCPU *cpu, - TranslationBlock *tb, - bool spc) +void gen_intermediate_code(CPUSPARCState * env, TranslationBlock * tb) { + SPARCCPU *cpu = sparc_env_get_cpu(env); CPUState *cs = CPU(cpu); - CPUSPARCState *env = &cpu->env; target_ulong pc_start, last_pc; DisasContext dc1, *dc = &dc1; - CPUBreakpoint *bp; - int j, lj = -1; int num_insns; int max_insns; unsigned int insn; @@ -5234,7 +5234,7 @@ static inline void gen_intermediate_code_internal(SPARCCPU *cpu, last_pc = dc->pc; dc->npc = (target_ulong) tb->cs_base; dc->cc_op = CC_OP_DYNAMIC; - dc->mem_idx = cpu_mmu_index(env); + dc->mem_idx = cpu_mmu_index(env, false); dc->def = env->def; dc->fpu_enabled = tb_fpu_enabled(tb->flags); dc->address_mask_32bit = tb_am_enabled(tb->flags); @@ -5242,42 +5242,41 @@ static inline void gen_intermediate_code_internal(SPARCCPU *cpu, num_insns = 0; max_insns = tb->cflags & CF_COUNT_MASK; - if (max_insns == 0) + if (max_insns == 0) { max_insns = CF_COUNT_MASK; + } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } + gen_tb_start(tb); do { - if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { - QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - if (bp->pc == dc->pc) { - if (dc->pc != pc_start) - save_state(dc); - gen_helper_debug(cpu_env); - tcg_gen_exit_tb(0); - dc->is_br = 1; - goto exit_gen_loop; - } - } + if (dc->npc & JUMP_PC) { + assert(dc->jump_pc[1] == dc->pc + 4); + tcg_gen_insn_start(dc->pc, dc->jump_pc[0] | JUMP_PC); + } else { + tcg_gen_insn_start(dc->pc, dc->npc); } - if (spc) { - qemu_log("Search PC...\n"); - j = tcg_op_buf_count(); - if (lj < j) { - lj++; - while (lj < j) - tcg_ctx.gen_opc_instr_start[lj++] = 0; - tcg_ctx.gen_opc_pc[lj] = dc->pc; - gen_opc_npc[lj] = dc->npc; - tcg_ctx.gen_opc_instr_start[lj] = 1; - tcg_ctx.gen_opc_icount[lj] = num_insns; + num_insns++; + last_pc = dc->pc; + + if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { + if (dc->pc != pc_start) { + save_state(dc); } + gen_helper_debug(cpu_env); + tcg_gen_exit_tb(0); + dc->is_br = 1; + goto exit_gen_loop; } - if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) + + if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { gen_io_start(); - last_pc = dc->pc; + } + insn = cpu_ldl_code(env, dc->pc); disas_sparc_insn(dc, insn); - num_insns++; if (dc->is_br) break; @@ -5316,20 +5315,9 @@ static inline void gen_intermediate_code_internal(SPARCCPU *cpu, } gen_tb_end(tb, num_insns); - if (spc) { - j = tcg_op_buf_count(); - lj++; - while (lj <= j) - tcg_ctx.gen_opc_instr_start[lj++] = 0; -#if 0 - log_page_dump(); -#endif - gen_opc_jump_pc[0] = dc->jump_pc[0]; - gen_opc_jump_pc[1] = dc->jump_pc[1]; - } else { - tb->size = last_pc + 4 - pc_start; - tb->icount = num_insns; - } + tb->size = last_pc + 4 - pc_start; + tb->icount = num_insns; + #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { qemu_log("--------------\n"); @@ -5340,130 +5328,118 @@ static inline void gen_intermediate_code_internal(SPARCCPU *cpu, #endif } -void gen_intermediate_code(CPUSPARCState * env, TranslationBlock * tb) -{ - gen_intermediate_code_internal(sparc_env_get_cpu(env), tb, false); -} - -void gen_intermediate_code_pc(CPUSPARCState * env, TranslationBlock * tb) -{ - gen_intermediate_code_internal(sparc_env_get_cpu(env), tb, true); -} - void gen_intermediate_code_init(CPUSPARCState *env) { - unsigned int i; static int inited; - static const char * const gregnames[8] = { - NULL, // g0 not used - "g1", - "g2", - "g3", - "g4", - "g5", - "g6", - "g7", + static const char gregnames[32][4] = { + "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7", + "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", + "l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7", + "i0", "i1", "i2", "i3", "i4", "i5", "i6", "i7", }; - static const char * const fregnames[32] = { + static const char fregnames[32][4] = { "f0", "f2", "f4", "f6", "f8", "f10", "f12", "f14", "f16", "f18", "f20", "f22", "f24", "f26", "f28", "f30", "f32", "f34", "f36", "f38", "f40", "f42", "f44", "f46", "f48", "f50", "f52", "f54", "f56", "f58", "f60", "f62", }; - /* init various static tables */ - if (!inited) { - inited = 1; - - cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); - cpu_regwptr = tcg_global_mem_new_ptr(TCG_AREG0, - offsetof(CPUSPARCState, regwptr), - "regwptr"); + static const struct { TCGv_i32 *ptr; int off; const char *name; } r32[] = { #ifdef TARGET_SPARC64 - cpu_xcc = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUSPARCState, xcc), - "xcc"); - cpu_asi = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUSPARCState, asi), - "asi"); - cpu_fprs = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUSPARCState, fprs), - "fprs"); - cpu_gsr = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, gsr), - "gsr"); - cpu_tick_cmpr = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUSPARCState, tick_cmpr), - "tick_cmpr"); - cpu_stick_cmpr = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUSPARCState, stick_cmpr), - "stick_cmpr"); - cpu_hstick_cmpr = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUSPARCState, hstick_cmpr), - "hstick_cmpr"); - cpu_hintp = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, hintp), - "hintp"); - cpu_htba = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, htba), - "htba"); - cpu_hver = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, hver), - "hver"); - cpu_ssr = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUSPARCState, ssr), "ssr"); - cpu_ver = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUSPARCState, version), "ver"); - cpu_softint = tcg_global_mem_new_i32(TCG_AREG0, - offsetof(CPUSPARCState, softint), - "softint"); + { &cpu_xcc, offsetof(CPUSPARCState, xcc), "xcc" }, + { &cpu_asi, offsetof(CPUSPARCState, asi), "asi" }, + { &cpu_fprs, offsetof(CPUSPARCState, fprs), "fprs" }, + { &cpu_softint, offsetof(CPUSPARCState, softint), "softint" }, #else - cpu_wim = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, wim), - "wim"); + { &cpu_wim, offsetof(CPUSPARCState, wim), "wim" }, #endif - cpu_cond = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, cond), - "cond"); - cpu_cc_src = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, cc_src), - "cc_src"); - cpu_cc_src2 = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUSPARCState, cc_src2), - "cc_src2"); - cpu_cc_dst = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, cc_dst), - "cc_dst"); - cpu_cc_op = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUSPARCState, cc_op), - "cc_op"); - cpu_psr = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUSPARCState, psr), - "psr"); - cpu_fsr = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, fsr), - "fsr"); - cpu_pc = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, pc), - "pc"); - cpu_npc = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, npc), - "npc"); - cpu_y = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, y), "y"); + { &cpu_cc_op, offsetof(CPUSPARCState, cc_op), "cc_op" }, + { &cpu_psr, offsetof(CPUSPARCState, psr), "psr" }, + }; + + static const struct { TCGv *ptr; int off; const char *name; } rtl[] = { +#ifdef TARGET_SPARC64 + { &cpu_gsr, offsetof(CPUSPARCState, gsr), "gsr" }, + { &cpu_tick_cmpr, offsetof(CPUSPARCState, tick_cmpr), "tick_cmpr" }, + { &cpu_stick_cmpr, offsetof(CPUSPARCState, stick_cmpr), "stick_cmpr" }, + { &cpu_hstick_cmpr, offsetof(CPUSPARCState, hstick_cmpr), + "hstick_cmpr" }, + { &cpu_hintp, offsetof(CPUSPARCState, hintp), "hintp" }, + { &cpu_htba, offsetof(CPUSPARCState, htba), "htba" }, + { &cpu_hver, offsetof(CPUSPARCState, hver), "hver" }, + { &cpu_ssr, offsetof(CPUSPARCState, ssr), "ssr" }, + { &cpu_ver, offsetof(CPUSPARCState, version), "ver" }, +#endif + { &cpu_cond, offsetof(CPUSPARCState, cond), "cond" }, + { &cpu_cc_src, offsetof(CPUSPARCState, cc_src), "cc_src" }, + { &cpu_cc_src2, offsetof(CPUSPARCState, cc_src2), "cc_src2" }, + { &cpu_cc_dst, offsetof(CPUSPARCState, cc_dst), "cc_dst" }, + { &cpu_fsr, offsetof(CPUSPARCState, fsr), "fsr" }, + { &cpu_pc, offsetof(CPUSPARCState, pc), "pc" }, + { &cpu_npc, offsetof(CPUSPARCState, npc), "npc" }, + { &cpu_y, offsetof(CPUSPARCState, y), "y" }, #ifndef CONFIG_USER_ONLY - cpu_tbr = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, tbr), - "tbr"); + { &cpu_tbr, offsetof(CPUSPARCState, tbr), "tbr" }, #endif - for (i = 1; i < 8; i++) { - cpu_gregs[i] = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUSPARCState, gregs[i]), - gregnames[i]); - } - for (i = 0; i < TARGET_DPREGS; i++) { - cpu_fpr[i] = tcg_global_mem_new_i64(TCG_AREG0, - offsetof(CPUSPARCState, fpr[i]), - fregnames[i]); - } + }; + + unsigned int i; + + /* init various static tables */ + if (inited) { + return; + } + inited = 1; + + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + + cpu_regwptr = tcg_global_mem_new_ptr(cpu_env, + offsetof(CPUSPARCState, regwptr), + "regwptr"); + + for (i = 0; i < ARRAY_SIZE(r32); ++i) { + *r32[i].ptr = tcg_global_mem_new_i32(cpu_env, r32[i].off, r32[i].name); + } + + for (i = 0; i < ARRAY_SIZE(rtl); ++i) { + *rtl[i].ptr = tcg_global_mem_new(cpu_env, rtl[i].off, rtl[i].name); + } + + TCGV_UNUSED(cpu_regs[0]); + for (i = 1; i < 8; ++i) { + cpu_regs[i] = tcg_global_mem_new(cpu_env, + offsetof(CPUSPARCState, gregs[i]), + gregnames[i]); + } + + for (i = 8; i < 32; ++i) { + cpu_regs[i] = tcg_global_mem_new(cpu_regwptr, + (i - 8) * sizeof(target_ulong), + gregnames[i]); + } + + for (i = 0; i < TARGET_DPREGS; i++) { + cpu_fpr[i] = tcg_global_mem_new_i64(cpu_env, + offsetof(CPUSPARCState, fpr[i]), + fregnames[i]); } } -void restore_state_to_opc(CPUSPARCState *env, TranslationBlock *tb, int pc_pos) +void restore_state_to_opc(CPUSPARCState *env, TranslationBlock *tb, + target_ulong *data) { - target_ulong npc; - env->pc = tcg_ctx.gen_opc_pc[pc_pos]; - npc = gen_opc_npc[pc_pos]; - if (npc == 1) { + target_ulong pc = data[0]; + target_ulong npc = data[1]; + + env->pc = pc; + if (npc == DYNAMIC_PC) { /* dynamic NPC: already stored */ - } else if (npc == 2) { + } else if (npc & JUMP_PC) { /* jump PC: use 'cond' and the jump targets of the translation */ if (env->cond) { - env->npc = gen_opc_jump_pc[0]; + env->npc = npc & ~3; } else { - env->npc = gen_opc_jump_pc[1]; + env->npc = pc + 4; } } else { env->npc = npc; diff --git a/qemu/target-sparc/vis_helper.c b/qemu/target-sparc/vis_helper.c index 383cc8bdf..8a9b763d0 100644 --- a/qemu/target-sparc/vis_helper.c +++ b/qemu/target-sparc/vis_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" @@ -447,7 +448,7 @@ uint32_t helper_fpackfix(uint64_t gsr, uint64_t rs2) for (word = 0; word < 2; word++) { uint32_t val; int32_t src = rs2 >> (word * 32); - int64_t scaled = src << scale; + int64_t scaled = (int64_t)src << scale; int64_t from_fixed = scaled >> 16; val = (from_fixed < -32768 ? -32768 : diff --git a/qemu/target-sparc/win_helper.c b/qemu/target-sparc/win_helper.c index f01ae08f6..a8a6c0cfc 100644 --- a/qemu/target-sparc/win_helper.c +++ b/qemu/target-sparc/win_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "trace.h" @@ -64,23 +65,28 @@ target_ulong cpu_get_psr(CPUSPARCState *env) #endif } -void cpu_put_psr(CPUSPARCState *env, target_ulong val) +void cpu_put_psr_raw(CPUSPARCState *env, target_ulong val) { env->psr = val & PSR_ICC; #if !defined(TARGET_SPARC64) env->psref = (val & PSR_EF) ? 1 : 0; env->psrpil = (val & PSR_PIL) >> 8; -#endif -#if ((!defined(TARGET_SPARC64)) && !defined(CONFIG_USER_ONLY)) - cpu_check_irqs(env); -#endif -#if !defined(TARGET_SPARC64) env->psrs = (val & PSR_S) ? 1 : 0; env->psrps = (val & PSR_PS) ? 1 : 0; env->psret = (val & PSR_ET) ? 1 : 0; - cpu_set_cwp(env, val & PSR_CWP); #endif env->cc_op = CC_OP_FLAGS; +#if !defined(TARGET_SPARC64) + cpu_set_cwp(env, val & PSR_CWP); +#endif +} + +void cpu_put_psr(CPUSPARCState *env, target_ulong val) +{ + cpu_put_psr_raw(env, val); +#if ((!defined(TARGET_SPARC64)) && !defined(CONFIG_USER_ONLY)) + cpu_check_irqs(env); +#endif } int cpu_cwp_inc(CPUSPARCState *env, int cwp) diff --git a/qemu/target-tilegx/Makefile.objs b/qemu/target-tilegx/Makefile.objs new file mode 100644 index 000000000..0db778f40 --- /dev/null +++ b/qemu/target-tilegx/Makefile.objs @@ -0,0 +1 @@ +obj-y += cpu.o translate.o helper.o simd_helper.o diff --git a/qemu/target-tilegx/cpu.c b/qemu/target-tilegx/cpu.c new file mode 100644 index 000000000..d2d091203 --- /dev/null +++ b/qemu/target-tilegx/cpu.c @@ -0,0 +1,187 @@ +/* + * QEMU TILE-Gx CPU + * + * Copyright (c) 2015 Chen Gang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "cpu.h" +#include "qemu-common.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "linux-user/syscall_defs.h" + +static void tilegx_cpu_dump_state(CPUState *cs, FILE *f, + fprintf_function cpu_fprintf, int flags) +{ + static const char * const reg_names[TILEGX_R_COUNT] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", + "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39", + "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47", + "r48", "r49", "r50", "r51", "bp", "tp", "sp", "lr" + }; + + TileGXCPU *cpu = TILEGX_CPU(cs); + CPUTLGState *env = &cpu->env; + int i; + + for (i = 0; i < TILEGX_R_COUNT; i++) { + cpu_fprintf(f, "%-4s" TARGET_FMT_lx "%s", + reg_names[i], env->regs[i], + (i % 4) == 3 ? "\n" : " "); + } + cpu_fprintf(f, "PC " TARGET_FMT_lx " CEX " TARGET_FMT_lx "\n\n", + env->pc, env->spregs[TILEGX_SPR_CMPEXCH]); +} + +TileGXCPU *cpu_tilegx_init(const char *cpu_model) +{ + TileGXCPU *cpu; + + cpu = TILEGX_CPU(object_new(TYPE_TILEGX_CPU)); + + object_property_set_bool(OBJECT(cpu), true, "realized", NULL); + + return cpu; +} + +static void tilegx_cpu_set_pc(CPUState *cs, vaddr value) +{ + TileGXCPU *cpu = TILEGX_CPU(cs); + + cpu->env.pc = value; +} + +static bool tilegx_cpu_has_work(CPUState *cs) +{ + return true; +} + +static void tilegx_cpu_reset(CPUState *s) +{ + TileGXCPU *cpu = TILEGX_CPU(s); + TileGXCPUClass *tcc = TILEGX_CPU_GET_CLASS(cpu); + CPUTLGState *env = &cpu->env; + + tcc->parent_reset(s); + + memset(env, 0, sizeof(CPUTLGState)); + tlb_flush(s, 1); +} + +static void tilegx_cpu_realizefn(DeviceState *dev, Error **errp) +{ + CPUState *cs = CPU(dev); + TileGXCPUClass *tcc = TILEGX_CPU_GET_CLASS(dev); + + cpu_reset(cs); + qemu_init_vcpu(cs); + + tcc->parent_realize(dev, errp); +} + +static void tilegx_cpu_initfn(Object *obj) +{ + CPUState *cs = CPU(obj); + TileGXCPU *cpu = TILEGX_CPU(obj); + CPUTLGState *env = &cpu->env; + static bool tcg_initialized; + + cs->env_ptr = env; + cpu_exec_init(cs, &error_abort); + + if (tcg_enabled() && !tcg_initialized) { + tcg_initialized = true; + tilegx_tcg_init(); + } +} + +static void tilegx_cpu_do_interrupt(CPUState *cs) +{ + cs->exception_index = -1; +} + +static int tilegx_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, + int mmu_idx) +{ + TileGXCPU *cpu = TILEGX_CPU(cs); + + /* The sigcode field will be filled in by do_signal in main.c. */ + cs->exception_index = TILEGX_EXCP_SIGNAL; + cpu->env.excaddr = address; + cpu->env.signo = TARGET_SIGSEGV; + cpu->env.sigcode = 0; + + return 1; +} + +static bool tilegx_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + if (interrupt_request & CPU_INTERRUPT_HARD) { + tilegx_cpu_do_interrupt(cs); + return true; + } + return false; +} + +static void tilegx_cpu_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + CPUClass *cc = CPU_CLASS(oc); + TileGXCPUClass *tcc = TILEGX_CPU_CLASS(oc); + + tcc->parent_realize = dc->realize; + dc->realize = tilegx_cpu_realizefn; + + tcc->parent_reset = cc->reset; + cc->reset = tilegx_cpu_reset; + + cc->has_work = tilegx_cpu_has_work; + cc->do_interrupt = tilegx_cpu_do_interrupt; + cc->cpu_exec_interrupt = tilegx_cpu_exec_interrupt; + cc->dump_state = tilegx_cpu_dump_state; + cc->set_pc = tilegx_cpu_set_pc; + cc->handle_mmu_fault = tilegx_cpu_handle_mmu_fault; + cc->gdb_num_core_regs = 0; + + /* + * Reason: tilegx_cpu_initfn() calls cpu_exec_init(), which saves + * the object in cpus -> dangling pointer after final + * object_unref(). + */ + dc->cannot_destroy_with_object_finalize_yet = true; +} + +static const TypeInfo tilegx_cpu_type_info = { + .name = TYPE_TILEGX_CPU, + .parent = TYPE_CPU, + .instance_size = sizeof(TileGXCPU), + .instance_init = tilegx_cpu_initfn, + .class_size = sizeof(TileGXCPUClass), + .class_init = tilegx_cpu_class_init, +}; + +static void tilegx_cpu_register_types(void) +{ + type_register_static(&tilegx_cpu_type_info); +} + +type_init(tilegx_cpu_register_types) diff --git a/qemu/target-tilegx/cpu.h b/qemu/target-tilegx/cpu.h new file mode 100644 index 000000000..022cad186 --- /dev/null +++ b/qemu/target-tilegx/cpu.h @@ -0,0 +1,181 @@ +/* + * TILE-Gx virtual CPU header + * + * Copyright (c) 2015 Chen Gang + * + * 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 + * 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 . + */ +#ifndef CPU_TILEGX_H +#define CPU_TILEGX_H + +#include "qemu-common.h" + +#define TARGET_LONG_BITS 64 + +#define CPUArchState struct CPUTLGState + +#include "exec/cpu-defs.h" + + +/* TILE-Gx common register alias */ +#define TILEGX_R_RE 0 /* 0 register, for function/syscall return value */ +#define TILEGX_R_ERR 1 /* 1 register, for syscall errno flag */ +#define TILEGX_R_NR 10 /* 10 register, for syscall number */ +#define TILEGX_R_BP 52 /* 52 register, optional frame pointer */ +#define TILEGX_R_TP 53 /* TP register, thread local storage data */ +#define TILEGX_R_SP 54 /* SP register, stack pointer */ +#define TILEGX_R_LR 55 /* LR register, may save pc, but it is not pc */ +#define TILEGX_R_COUNT 56 /* Only 56 registers are really useful */ +#define TILEGX_R_SN 56 /* SN register, obsoleted, it likes zero register */ +#define TILEGX_R_IDN0 57 /* IDN0 register, cause IDN_ACCESS exception */ +#define TILEGX_R_IDN1 58 /* IDN1 register, cause IDN_ACCESS exception */ +#define TILEGX_R_UDN0 59 /* UDN0 register, cause UDN_ACCESS exception */ +#define TILEGX_R_UDN1 60 /* UDN1 register, cause UDN_ACCESS exception */ +#define TILEGX_R_UDN2 61 /* UDN2 register, cause UDN_ACCESS exception */ +#define TILEGX_R_UDN3 62 /* UDN3 register, cause UDN_ACCESS exception */ +#define TILEGX_R_ZERO 63 /* Zero register, always zero */ +#define TILEGX_R_NOREG 255 /* Invalid register value */ + +/* TILE-Gx special registers used by outside */ +enum { + TILEGX_SPR_CMPEXCH = 0, + TILEGX_SPR_CRITICAL_SEC = 1, + TILEGX_SPR_SIM_CONTROL = 2, + TILEGX_SPR_EX_CONTEXT_0_0 = 3, + TILEGX_SPR_EX_CONTEXT_0_1 = 4, + TILEGX_SPR_COUNT +}; + +/* Exception numbers */ +typedef enum { + TILEGX_EXCP_NONE = 0, + TILEGX_EXCP_SYSCALL = 1, + TILEGX_EXCP_SIGNAL = 2, + TILEGX_EXCP_OPCODE_UNKNOWN = 0x101, + TILEGX_EXCP_OPCODE_UNIMPLEMENTED = 0x102, + TILEGX_EXCP_OPCODE_CMPEXCH = 0x103, + TILEGX_EXCP_OPCODE_CMPEXCH4 = 0x104, + TILEGX_EXCP_OPCODE_EXCH = 0x105, + TILEGX_EXCP_OPCODE_EXCH4 = 0x106, + TILEGX_EXCP_OPCODE_FETCHADD = 0x107, + TILEGX_EXCP_OPCODE_FETCHADD4 = 0x108, + TILEGX_EXCP_OPCODE_FETCHADDGEZ = 0x109, + TILEGX_EXCP_OPCODE_FETCHADDGEZ4 = 0x10a, + TILEGX_EXCP_OPCODE_FETCHAND = 0x10b, + TILEGX_EXCP_OPCODE_FETCHAND4 = 0x10c, + TILEGX_EXCP_OPCODE_FETCHOR = 0x10d, + TILEGX_EXCP_OPCODE_FETCHOR4 = 0x10e, + TILEGX_EXCP_REG_IDN_ACCESS = 0x181, + TILEGX_EXCP_REG_UDN_ACCESS = 0x182, + TILEGX_EXCP_UNALIGNMENT = 0x201, + TILEGX_EXCP_DBUG_BREAK = 0x301 +} TileExcp; + +typedef struct CPUTLGState { + uint64_t regs[TILEGX_R_COUNT]; /* Common used registers by outside */ + uint64_t spregs[TILEGX_SPR_COUNT]; /* Special used registers by outside */ + uint64_t pc; /* Current pc */ + +#if defined(CONFIG_USER_ONLY) + uint64_t excaddr; /* exception address */ + uint64_t atomic_srca; /* Arguments to atomic "exceptions" */ + uint64_t atomic_srcb; + uint32_t atomic_dstr; + uint32_t signo; /* Signal number */ + uint32_t sigcode; /* Signal code */ +#endif + + CPU_COMMON +} CPUTLGState; + +#include "qom/cpu.h" + +#define TYPE_TILEGX_CPU "tilegx-cpu" + +#define TILEGX_CPU_CLASS(klass) \ + OBJECT_CLASS_CHECK(TileGXCPUClass, (klass), TYPE_TILEGX_CPU) +#define TILEGX_CPU(obj) \ + OBJECT_CHECK(TileGXCPU, (obj), TYPE_TILEGX_CPU) +#define TILEGX_CPU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(TileGXCPUClass, (obj), TYPE_TILEGX_CPU) + +/** + * TileGXCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * + * A Tile-Gx CPU model. + */ +typedef struct TileGXCPUClass { + /*< private >*/ + CPUClass parent_class; + /*< public >*/ + + DeviceRealize parent_realize; + void (*parent_reset)(CPUState *cpu); +} TileGXCPUClass; + +/** + * TileGXCPU: + * @env: #CPUTLGState + * + * A Tile-GX CPU. + */ +typedef struct TileGXCPU { + /*< private >*/ + CPUState parent_obj; + /*< public >*/ + + CPUTLGState env; +} TileGXCPU; + +static inline TileGXCPU *tilegx_env_get_cpu(CPUTLGState *env) +{ + return container_of(env, TileGXCPU, env); +} + +#define ENV_GET_CPU(e) CPU(tilegx_env_get_cpu(e)) + +#define ENV_OFFSET offsetof(TileGXCPU, env) + +/* TILE-Gx memory attributes */ +#define TARGET_PAGE_BITS 16 /* TILE-Gx uses 64KB page size */ +#define TARGET_PHYS_ADDR_SPACE_BITS 42 +#define TARGET_VIRT_ADDR_SPACE_BITS 64 +#define MMU_USER_IDX 0 /* Current memory operation is in user mode */ + +#include "exec/cpu-all.h" + +void tilegx_tcg_init(void); +int cpu_tilegx_exec(CPUState *s); +int cpu_tilegx_signal_handler(int host_signum, void *pinfo, void *puc); + +TileGXCPU *cpu_tilegx_init(const char *cpu_model); + +#define cpu_init(cpu_model) CPU(cpu_tilegx_init(cpu_model)) + +#define cpu_exec cpu_tilegx_exec +#define cpu_signal_handler cpu_tilegx_signal_handler + +static inline void cpu_get_tb_cpu_state(CPUTLGState *env, target_ulong *pc, + target_ulong *cs_base, int *flags) +{ + *pc = env->pc; + *cs_base = 0; + *flags = 0; +} + +#include "exec/exec-all.h" + +#endif diff --git a/qemu/target-tilegx/helper.c b/qemu/target-tilegx/helper.c new file mode 100644 index 000000000..616c5c7cf --- /dev/null +++ b/qemu/target-tilegx/helper.c @@ -0,0 +1,162 @@ +/* + * QEMU TILE-Gx helpers + * + * Copyright (c) 2015 Chen Gang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "qemu-common.h" +#include "exec/helper-proto.h" +#include /* For crc32 */ +#include "syscall_defs.h" + +void helper_exception(CPUTLGState *env, uint32_t excp) +{ + CPUState *cs = CPU(tilegx_env_get_cpu(env)); + + cs->exception_index = excp; + cpu_loop_exit(cs); +} + +void helper_ext01_ics(CPUTLGState *env) +{ + uint64_t val = env->spregs[TILEGX_SPR_EX_CONTEXT_0_1]; + + switch (val) { + case 0: + case 1: + env->spregs[TILEGX_SPR_CRITICAL_SEC] = val; + break; + default: +#if defined(CONFIG_USER_ONLY) + env->signo = TARGET_SIGILL; + env->sigcode = TARGET_ILL_ILLOPC; + helper_exception(env, TILEGX_EXCP_SIGNAL); +#else + helper_exception(env, TILEGX_EXCP_OPCODE_UNIMPLEMENTED); +#endif + break; + } +} + +uint64_t helper_cntlz(uint64_t arg) +{ + return clz64(arg); +} + +uint64_t helper_cnttz(uint64_t arg) +{ + return ctz64(arg); +} + +uint64_t helper_pcnt(uint64_t arg) +{ + return ctpop64(arg); +} + +uint64_t helper_revbits(uint64_t arg) +{ + return revbit64(arg); +} + +/* + * Functional Description + * uint64_t a = rf[SrcA]; + * uint64_t b = rf[SrcB]; + * uint64_t d = rf[Dest]; + * uint64_t output = 0; + * unsigned int counter; + * for (counter = 0; counter < (WORD_SIZE / BYTE_SIZE); counter++) + * { + * int sel = getByte (b, counter) & 0xf; + * uint8_t byte = (sel < 8) ? getByte (d, sel) : getByte (a, (sel - 8)); + * output = setByte (output, counter, byte); + * } + * rf[Dest] = output; + */ +uint64_t helper_shufflebytes(uint64_t dest, uint64_t srca, uint64_t srcb) +{ + uint64_t vdst = 0; + int count; + + for (count = 0; count < 64; count += 8) { + uint64_t sel = srcb >> count; + uint64_t src = (sel & 8) ? srca : dest; + vdst |= extract64(src, (sel & 7) * 8, 8) << count; + } + + return vdst; +} + +uint64_t helper_crc32_8(uint64_t accum, uint64_t input) +{ + uint8_t buf = input; + + /* zlib crc32 converts the accumulator and output to one's complement. */ + return crc32(accum ^ 0xffffffff, &buf, 1) ^ 0xffffffff; +} + +uint64_t helper_crc32_32(uint64_t accum, uint64_t input) +{ + uint8_t buf[4]; + + stl_le_p(buf, input); + + /* zlib crc32 converts the accumulator and output to one's complement. */ + return crc32(accum ^ 0xffffffff, buf, 4) ^ 0xffffffff; +} + +uint64_t helper_cmula(uint64_t srcd, uint64_t srca, uint64_t srcb) +{ + uint32_t reala = (int16_t)srca; + uint32_t imaga = (int16_t)(srca >> 16); + uint32_t realb = (int16_t)srcb; + uint32_t imagb = (int16_t)(srcb >> 16); + uint32_t reald = srcd; + uint32_t imagd = srcd >> 32; + uint32_t realr = reala * realb - imaga * imagb + reald; + uint32_t imagr = reala * imagb + imaga * realb + imagd; + + return deposit64(realr, 32, 32, imagr); +} + +uint64_t helper_cmulaf(uint64_t srcd, uint64_t srca, uint64_t srcb) +{ + uint32_t reala = (int16_t)srca; + uint32_t imaga = (int16_t)(srca >> 16); + uint32_t realb = (int16_t)srcb; + uint32_t imagb = (int16_t)(srcb >> 16); + uint32_t reald = (int16_t)srcd; + uint32_t imagd = (int16_t)(srcd >> 16); + int32_t realr = reala * realb - imaga * imagb; + int32_t imagr = reala * imagb + imaga * realb; + + return deposit32((realr >> 15) + reald, 16, 16, (imagr >> 15) + imagd); +} + +uint64_t helper_cmul2(uint64_t srca, uint64_t srcb, int shift, int round) +{ + uint32_t reala = (int16_t)srca; + uint32_t imaga = (int16_t)(srca >> 16); + uint32_t realb = (int16_t)srcb; + uint32_t imagb = (int16_t)(srcb >> 16); + int32_t realr = reala * realb - imaga * imagb + round; + int32_t imagr = reala * imagb + imaga * realb + round; + + return deposit32(realr >> shift, 16, 16, imagr >> shift); +} diff --git a/qemu/target-tilegx/helper.h b/qemu/target-tilegx/helper.h new file mode 100644 index 000000000..9281d0f42 --- /dev/null +++ b/qemu/target-tilegx/helper.h @@ -0,0 +1,26 @@ +DEF_HELPER_2(exception, noreturn, env, i32) +DEF_HELPER_1(ext01_ics, void, env) +DEF_HELPER_FLAGS_1(cntlz, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_FLAGS_1(cnttz, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_FLAGS_1(pcnt, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_FLAGS_1(revbits, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_FLAGS_3(shufflebytes, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) +DEF_HELPER_FLAGS_2(crc32_8, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(crc32_32, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_3(cmula, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) +DEF_HELPER_FLAGS_3(cmulaf, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) +DEF_HELPER_FLAGS_4(cmul2, TCG_CALL_NO_RWG_SE, i64, i64, i64, int, int) + +DEF_HELPER_FLAGS_2(v1int_h, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(v1int_l, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(v2int_h, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(v2int_l, TCG_CALL_NO_RWG_SE, i64, i64, i64) + +DEF_HELPER_FLAGS_2(v1multu, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(v2mults, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(v1shl, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(v1shru, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(v1shrs, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(v2shl, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(v2shru, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(v2shrs, TCG_CALL_NO_RWG_SE, i64, i64, i64) diff --git a/qemu/target-tilegx/opcode_tilegx.h b/qemu/target-tilegx/opcode_tilegx.h new file mode 100644 index 000000000..989436d2f --- /dev/null +++ b/qemu/target-tilegx/opcode_tilegx.h @@ -0,0 +1,1406 @@ +/* TILE-Gx opcode information. + * + * Copyright 2011 Tilera Corporation. 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, version 2. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for + * more details. + * + * + * + * + * + */ + +#ifndef __ARCH_OPCODE_H__ +#define __ARCH_OPCODE_H__ + +#ifndef __ASSEMBLER__ + +typedef uint64_t tilegx_bundle_bits; + +/* These are the bits that determine if a bundle is in the X encoding. */ +#define TILEGX_BUNDLE_MODE_MASK ((tilegx_bundle_bits)3 << 62) + +enum +{ + /* Maximum number of instructions in a bundle (2 for X, 3 for Y). */ + TILEGX_MAX_INSTRUCTIONS_PER_BUNDLE = 3, + + /* How many different pipeline encodings are there? X0, X1, Y0, Y1, Y2. */ + TILEGX_NUM_PIPELINE_ENCODINGS = 5, + + /* Log base 2 of TILEGX_BUNDLE_SIZE_IN_BYTES. */ + TILEGX_LOG2_BUNDLE_SIZE_IN_BYTES = 3, + + /* Instructions take this many bytes. */ + TILEGX_BUNDLE_SIZE_IN_BYTES = 1 << TILEGX_LOG2_BUNDLE_SIZE_IN_BYTES, + + /* Log base 2 of TILEGX_BUNDLE_ALIGNMENT_IN_BYTES. */ + TILEGX_LOG2_BUNDLE_ALIGNMENT_IN_BYTES = 3, + + /* Bundles should be aligned modulo this number of bytes. */ + TILEGX_BUNDLE_ALIGNMENT_IN_BYTES = + (1 << TILEGX_LOG2_BUNDLE_ALIGNMENT_IN_BYTES), + + /* Number of registers (some are magic, such as network I/O). */ + TILEGX_NUM_REGISTERS = 64, +}; + +/* Make a few "tile_" variables to simplify common code between + architectures. */ + +typedef tilegx_bundle_bits tile_bundle_bits; +#define TILE_BUNDLE_SIZE_IN_BYTES TILEGX_BUNDLE_SIZE_IN_BYTES +#define TILE_BUNDLE_ALIGNMENT_IN_BYTES TILEGX_BUNDLE_ALIGNMENT_IN_BYTES +#define TILE_LOG2_BUNDLE_ALIGNMENT_IN_BYTES \ + TILEGX_LOG2_BUNDLE_ALIGNMENT_IN_BYTES +#define TILE_BPT_BUNDLE TILEGX_BPT_BUNDLE + +/* 64-bit pattern for a { bpt ; nop } bundle. */ +#define TILEGX_BPT_BUNDLE 0x286a44ae51485000ULL + +static inline unsigned int +get_BFEnd_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0x3f); +} + +static inline unsigned int +get_BFOpcodeExtension_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 24)) & 0xf); +} + +static inline unsigned int +get_BFStart_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 18)) & 0x3f); +} + +static inline unsigned int +get_BrOff_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 31)) & 0x0000003f) | + (((unsigned int)(n >> 37)) & 0x0001ffc0); +} + +static inline unsigned int +get_BrType_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 54)) & 0x1f); +} + +static inline unsigned int +get_Dest_Imm8_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 31)) & 0x0000003f) | + (((unsigned int)(n >> 43)) & 0x000000c0); +} + +static inline unsigned int +get_Dest_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 0)) & 0x3f); +} + +static inline unsigned int +get_Dest_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 31)) & 0x3f); +} + +static inline unsigned int +get_Dest_Y0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 0)) & 0x3f); +} + +static inline unsigned int +get_Dest_Y1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 31)) & 0x3f); +} + +static inline unsigned int +get_Imm16_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0xffff); +} + +static inline unsigned int +get_Imm16_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 43)) & 0xffff); +} + +static inline unsigned int +get_Imm8OpcodeExtension_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 20)) & 0xff); +} + +static inline unsigned int +get_Imm8OpcodeExtension_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 51)) & 0xff); +} + +static inline unsigned int +get_Imm8_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0xff); +} + +static inline unsigned int +get_Imm8_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 43)) & 0xff); +} + +static inline unsigned int +get_Imm8_Y0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0xff); +} + +static inline unsigned int +get_Imm8_Y1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 43)) & 0xff); +} + +static inline unsigned int +get_JumpOff_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 31)) & 0x7ffffff); +} + +static inline unsigned int +get_JumpOpcodeExtension_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 58)) & 0x1); +} + +static inline unsigned int +get_MF_Imm14_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 37)) & 0x3fff); +} + +static inline unsigned int +get_MT_Imm14_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 31)) & 0x0000003f) | + (((unsigned int)(n >> 37)) & 0x00003fc0); +} + +static inline unsigned int +get_Mode(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 62)) & 0x3); +} + +static inline unsigned int +get_Opcode_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 28)) & 0x7); +} + +static inline unsigned int +get_Opcode_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 59)) & 0x7); +} + +static inline unsigned int +get_Opcode_Y0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 27)) & 0xf); +} + +static inline unsigned int +get_Opcode_Y1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 58)) & 0xf); +} + +static inline unsigned int +get_Opcode_Y2(tilegx_bundle_bits n) +{ + return (((n >> 26)) & 0x00000001) | + (((unsigned int)(n >> 56)) & 0x00000002); +} + +static inline unsigned int +get_RRROpcodeExtension_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 18)) & 0x3ff); +} + +static inline unsigned int +get_RRROpcodeExtension_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 49)) & 0x3ff); +} + +static inline unsigned int +get_RRROpcodeExtension_Y0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 18)) & 0x3); +} + +static inline unsigned int +get_RRROpcodeExtension_Y1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 49)) & 0x3); +} + +static inline unsigned int +get_ShAmt_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0x3f); +} + +static inline unsigned int +get_ShAmt_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 43)) & 0x3f); +} + +static inline unsigned int +get_ShAmt_Y0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0x3f); +} + +static inline unsigned int +get_ShAmt_Y1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 43)) & 0x3f); +} + +static inline unsigned int +get_ShiftOpcodeExtension_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 18)) & 0x3ff); +} + +static inline unsigned int +get_ShiftOpcodeExtension_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 49)) & 0x3ff); +} + +static inline unsigned int +get_ShiftOpcodeExtension_Y0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 18)) & 0x3); +} + +static inline unsigned int +get_ShiftOpcodeExtension_Y1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 49)) & 0x3); +} + +static inline unsigned int +get_SrcA_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 6)) & 0x3f); +} + +static inline unsigned int +get_SrcA_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 37)) & 0x3f); +} + +static inline unsigned int +get_SrcA_Y0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 6)) & 0x3f); +} + +static inline unsigned int +get_SrcA_Y1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 37)) & 0x3f); +} + +static inline unsigned int +get_SrcA_Y2(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 20)) & 0x3f); +} + +static inline unsigned int +get_SrcBDest_Y2(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 51)) & 0x3f); +} + +static inline unsigned int +get_SrcB_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0x3f); +} + +static inline unsigned int +get_SrcB_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 43)) & 0x3f); +} + +static inline unsigned int +get_SrcB_Y0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0x3f); +} + +static inline unsigned int +get_SrcB_Y1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 43)) & 0x3f); +} + +static inline unsigned int +get_UnaryOpcodeExtension_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0x3f); +} + +static inline unsigned int +get_UnaryOpcodeExtension_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 43)) & 0x3f); +} + +static inline unsigned int +get_UnaryOpcodeExtension_Y0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0x3f); +} + +static inline unsigned int +get_UnaryOpcodeExtension_Y1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 43)) & 0x3f); +} + + +static inline int +sign_extend(int n, int num_bits) +{ + int shift = (int)(sizeof(int) * 8 - num_bits); + return (n << shift) >> shift; +} + + + +static inline tilegx_bundle_bits +create_BFEnd_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 12); +} + +static inline tilegx_bundle_bits +create_BFOpcodeExtension_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0xf) << 24); +} + +static inline tilegx_bundle_bits +create_BFStart_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 18); +} + +static inline tilegx_bundle_bits +create_BrOff_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x0000003f)) << 31) | + (((tilegx_bundle_bits)(n & 0x0001ffc0)) << 37); +} + +static inline tilegx_bundle_bits +create_BrType_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x1f)) << 54); +} + +static inline tilegx_bundle_bits +create_Dest_Imm8_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x0000003f)) << 31) | + (((tilegx_bundle_bits)(n & 0x000000c0)) << 43); +} + +static inline tilegx_bundle_bits +create_Dest_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 0); +} + +static inline tilegx_bundle_bits +create_Dest_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 31); +} + +static inline tilegx_bundle_bits +create_Dest_Y0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 0); +} + +static inline tilegx_bundle_bits +create_Dest_Y1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 31); +} + +static inline tilegx_bundle_bits +create_Imm16_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0xffff) << 12); +} + +static inline tilegx_bundle_bits +create_Imm16_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0xffff)) << 43); +} + +static inline tilegx_bundle_bits +create_Imm8OpcodeExtension_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0xff) << 20); +} + +static inline tilegx_bundle_bits +create_Imm8OpcodeExtension_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0xff)) << 51); +} + +static inline tilegx_bundle_bits +create_Imm8_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0xff) << 12); +} + +static inline tilegx_bundle_bits +create_Imm8_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0xff)) << 43); +} + +static inline tilegx_bundle_bits +create_Imm8_Y0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0xff) << 12); +} + +static inline tilegx_bundle_bits +create_Imm8_Y1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0xff)) << 43); +} + +static inline tilegx_bundle_bits +create_JumpOff_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x7ffffff)) << 31); +} + +static inline tilegx_bundle_bits +create_JumpOpcodeExtension_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x1)) << 58); +} + +static inline tilegx_bundle_bits +create_MF_Imm14_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3fff)) << 37); +} + +static inline tilegx_bundle_bits +create_MT_Imm14_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x0000003f)) << 31) | + (((tilegx_bundle_bits)(n & 0x00003fc0)) << 37); +} + +static inline tilegx_bundle_bits +create_Mode(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3)) << 62); +} + +static inline tilegx_bundle_bits +create_Opcode_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x7) << 28); +} + +static inline tilegx_bundle_bits +create_Opcode_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x7)) << 59); +} + +static inline tilegx_bundle_bits +create_Opcode_Y0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0xf) << 27); +} + +static inline tilegx_bundle_bits +create_Opcode_Y1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0xf)) << 58); +} + +static inline tilegx_bundle_bits +create_Opcode_Y2(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x00000001) << 26) | + (((tilegx_bundle_bits)(n & 0x00000002)) << 56); +} + +static inline tilegx_bundle_bits +create_RRROpcodeExtension_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3ff) << 18); +} + +static inline tilegx_bundle_bits +create_RRROpcodeExtension_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3ff)) << 49); +} + +static inline tilegx_bundle_bits +create_RRROpcodeExtension_Y0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3) << 18); +} + +static inline tilegx_bundle_bits +create_RRROpcodeExtension_Y1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3)) << 49); +} + +static inline tilegx_bundle_bits +create_ShAmt_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 12); +} + +static inline tilegx_bundle_bits +create_ShAmt_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 43); +} + +static inline tilegx_bundle_bits +create_ShAmt_Y0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 12); +} + +static inline tilegx_bundle_bits +create_ShAmt_Y1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 43); +} + +static inline tilegx_bundle_bits +create_ShiftOpcodeExtension_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3ff) << 18); +} + +static inline tilegx_bundle_bits +create_ShiftOpcodeExtension_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3ff)) << 49); +} + +static inline tilegx_bundle_bits +create_ShiftOpcodeExtension_Y0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3) << 18); +} + +static inline tilegx_bundle_bits +create_ShiftOpcodeExtension_Y1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3)) << 49); +} + +static inline tilegx_bundle_bits +create_SrcA_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 6); +} + +static inline tilegx_bundle_bits +create_SrcA_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 37); +} + +static inline tilegx_bundle_bits +create_SrcA_Y0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 6); +} + +static inline tilegx_bundle_bits +create_SrcA_Y1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 37); +} + +static inline tilegx_bundle_bits +create_SrcA_Y2(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 20); +} + +static inline tilegx_bundle_bits +create_SrcBDest_Y2(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 51); +} + +static inline tilegx_bundle_bits +create_SrcB_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 12); +} + +static inline tilegx_bundle_bits +create_SrcB_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 43); +} + +static inline tilegx_bundle_bits +create_SrcB_Y0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 12); +} + +static inline tilegx_bundle_bits +create_SrcB_Y1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 43); +} + +static inline tilegx_bundle_bits +create_UnaryOpcodeExtension_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 12); +} + +static inline tilegx_bundle_bits +create_UnaryOpcodeExtension_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 43); +} + +static inline tilegx_bundle_bits +create_UnaryOpcodeExtension_Y0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 12); +} + +static inline tilegx_bundle_bits +create_UnaryOpcodeExtension_Y1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 43); +} + + +enum +{ + ADDI_IMM8_OPCODE_X0 = 1, + ADDI_IMM8_OPCODE_X1 = 1, + ADDI_OPCODE_Y0 = 0, + ADDI_OPCODE_Y1 = 1, + ADDLI_OPCODE_X0 = 1, + ADDLI_OPCODE_X1 = 0, + ADDXI_IMM8_OPCODE_X0 = 2, + ADDXI_IMM8_OPCODE_X1 = 2, + ADDXI_OPCODE_Y0 = 1, + ADDXI_OPCODE_Y1 = 2, + ADDXLI_OPCODE_X0 = 2, + ADDXLI_OPCODE_X1 = 1, + ADDXSC_RRR_0_OPCODE_X0 = 1, + ADDXSC_RRR_0_OPCODE_X1 = 1, + ADDX_RRR_0_OPCODE_X0 = 2, + ADDX_RRR_0_OPCODE_X1 = 2, + ADDX_RRR_0_OPCODE_Y0 = 0, + ADDX_RRR_0_OPCODE_Y1 = 0, + ADD_RRR_0_OPCODE_X0 = 3, + ADD_RRR_0_OPCODE_X1 = 3, + ADD_RRR_0_OPCODE_Y0 = 1, + ADD_RRR_0_OPCODE_Y1 = 1, + ANDI_IMM8_OPCODE_X0 = 3, + ANDI_IMM8_OPCODE_X1 = 3, + ANDI_OPCODE_Y0 = 2, + ANDI_OPCODE_Y1 = 3, + AND_RRR_0_OPCODE_X0 = 4, + AND_RRR_0_OPCODE_X1 = 4, + AND_RRR_5_OPCODE_Y0 = 0, + AND_RRR_5_OPCODE_Y1 = 0, + BEQZT_BRANCH_OPCODE_X1 = 16, + BEQZ_BRANCH_OPCODE_X1 = 17, + BFEXTS_BF_OPCODE_X0 = 4, + BFEXTU_BF_OPCODE_X0 = 5, + BFINS_BF_OPCODE_X0 = 6, + BF_OPCODE_X0 = 3, + BGEZT_BRANCH_OPCODE_X1 = 18, + BGEZ_BRANCH_OPCODE_X1 = 19, + BGTZT_BRANCH_OPCODE_X1 = 20, + BGTZ_BRANCH_OPCODE_X1 = 21, + BLBCT_BRANCH_OPCODE_X1 = 22, + BLBC_BRANCH_OPCODE_X1 = 23, + BLBST_BRANCH_OPCODE_X1 = 24, + BLBS_BRANCH_OPCODE_X1 = 25, + BLEZT_BRANCH_OPCODE_X1 = 26, + BLEZ_BRANCH_OPCODE_X1 = 27, + BLTZT_BRANCH_OPCODE_X1 = 28, + BLTZ_BRANCH_OPCODE_X1 = 29, + BNEZT_BRANCH_OPCODE_X1 = 30, + BNEZ_BRANCH_OPCODE_X1 = 31, + BRANCH_OPCODE_X1 = 2, + CMOVEQZ_RRR_0_OPCODE_X0 = 5, + CMOVEQZ_RRR_4_OPCODE_Y0 = 0, + CMOVNEZ_RRR_0_OPCODE_X0 = 6, + CMOVNEZ_RRR_4_OPCODE_Y0 = 1, + CMPEQI_IMM8_OPCODE_X0 = 4, + CMPEQI_IMM8_OPCODE_X1 = 4, + CMPEQI_OPCODE_Y0 = 3, + CMPEQI_OPCODE_Y1 = 4, + CMPEQ_RRR_0_OPCODE_X0 = 7, + CMPEQ_RRR_0_OPCODE_X1 = 5, + CMPEQ_RRR_3_OPCODE_Y0 = 0, + CMPEQ_RRR_3_OPCODE_Y1 = 2, + CMPEXCH4_RRR_0_OPCODE_X1 = 6, + CMPEXCH_RRR_0_OPCODE_X1 = 7, + CMPLES_RRR_0_OPCODE_X0 = 8, + CMPLES_RRR_0_OPCODE_X1 = 8, + CMPLES_RRR_2_OPCODE_Y0 = 0, + CMPLES_RRR_2_OPCODE_Y1 = 0, + CMPLEU_RRR_0_OPCODE_X0 = 9, + CMPLEU_RRR_0_OPCODE_X1 = 9, + CMPLEU_RRR_2_OPCODE_Y0 = 1, + CMPLEU_RRR_2_OPCODE_Y1 = 1, + CMPLTSI_IMM8_OPCODE_X0 = 5, + CMPLTSI_IMM8_OPCODE_X1 = 5, + CMPLTSI_OPCODE_Y0 = 4, + CMPLTSI_OPCODE_Y1 = 5, + CMPLTS_RRR_0_OPCODE_X0 = 10, + CMPLTS_RRR_0_OPCODE_X1 = 10, + CMPLTS_RRR_2_OPCODE_Y0 = 2, + CMPLTS_RRR_2_OPCODE_Y1 = 2, + CMPLTUI_IMM8_OPCODE_X0 = 6, + CMPLTUI_IMM8_OPCODE_X1 = 6, + CMPLTU_RRR_0_OPCODE_X0 = 11, + CMPLTU_RRR_0_OPCODE_X1 = 11, + CMPLTU_RRR_2_OPCODE_Y0 = 3, + CMPLTU_RRR_2_OPCODE_Y1 = 3, + CMPNE_RRR_0_OPCODE_X0 = 12, + CMPNE_RRR_0_OPCODE_X1 = 12, + CMPNE_RRR_3_OPCODE_Y0 = 1, + CMPNE_RRR_3_OPCODE_Y1 = 3, + CMULAF_RRR_0_OPCODE_X0 = 13, + CMULA_RRR_0_OPCODE_X0 = 14, + CMULFR_RRR_0_OPCODE_X0 = 15, + CMULF_RRR_0_OPCODE_X0 = 16, + CMULHR_RRR_0_OPCODE_X0 = 17, + CMULH_RRR_0_OPCODE_X0 = 18, + CMUL_RRR_0_OPCODE_X0 = 19, + CNTLZ_UNARY_OPCODE_X0 = 1, + CNTLZ_UNARY_OPCODE_Y0 = 1, + CNTTZ_UNARY_OPCODE_X0 = 2, + CNTTZ_UNARY_OPCODE_Y0 = 2, + CRC32_32_RRR_0_OPCODE_X0 = 20, + CRC32_8_RRR_0_OPCODE_X0 = 21, + DBLALIGN2_RRR_0_OPCODE_X0 = 22, + DBLALIGN2_RRR_0_OPCODE_X1 = 13, + DBLALIGN4_RRR_0_OPCODE_X0 = 23, + DBLALIGN4_RRR_0_OPCODE_X1 = 14, + DBLALIGN6_RRR_0_OPCODE_X0 = 24, + DBLALIGN6_RRR_0_OPCODE_X1 = 15, + DBLALIGN_RRR_0_OPCODE_X0 = 25, + DRAIN_UNARY_OPCODE_X1 = 1, + DTLBPR_UNARY_OPCODE_X1 = 2, + EXCH4_RRR_0_OPCODE_X1 = 16, + EXCH_RRR_0_OPCODE_X1 = 17, + FDOUBLE_ADDSUB_RRR_0_OPCODE_X0 = 26, + FDOUBLE_ADD_FLAGS_RRR_0_OPCODE_X0 = 27, + FDOUBLE_MUL_FLAGS_RRR_0_OPCODE_X0 = 28, + FDOUBLE_PACK1_RRR_0_OPCODE_X0 = 29, + FDOUBLE_PACK2_RRR_0_OPCODE_X0 = 30, + FDOUBLE_SUB_FLAGS_RRR_0_OPCODE_X0 = 31, + FDOUBLE_UNPACK_MAX_RRR_0_OPCODE_X0 = 32, + FDOUBLE_UNPACK_MIN_RRR_0_OPCODE_X0 = 33, + FETCHADD4_RRR_0_OPCODE_X1 = 18, + FETCHADDGEZ4_RRR_0_OPCODE_X1 = 19, + FETCHADDGEZ_RRR_0_OPCODE_X1 = 20, + FETCHADD_RRR_0_OPCODE_X1 = 21, + FETCHAND4_RRR_0_OPCODE_X1 = 22, + FETCHAND_RRR_0_OPCODE_X1 = 23, + FETCHOR4_RRR_0_OPCODE_X1 = 24, + FETCHOR_RRR_0_OPCODE_X1 = 25, + FINV_UNARY_OPCODE_X1 = 3, + FLUSHWB_UNARY_OPCODE_X1 = 4, + FLUSH_UNARY_OPCODE_X1 = 5, + FNOP_UNARY_OPCODE_X0 = 3, + FNOP_UNARY_OPCODE_X1 = 6, + FNOP_UNARY_OPCODE_Y0 = 3, + FNOP_UNARY_OPCODE_Y1 = 8, + FSINGLE_ADD1_RRR_0_OPCODE_X0 = 34, + FSINGLE_ADDSUB2_RRR_0_OPCODE_X0 = 35, + FSINGLE_MUL1_RRR_0_OPCODE_X0 = 36, + FSINGLE_MUL2_RRR_0_OPCODE_X0 = 37, + FSINGLE_PACK1_UNARY_OPCODE_X0 = 4, + FSINGLE_PACK1_UNARY_OPCODE_Y0 = 4, + FSINGLE_PACK2_RRR_0_OPCODE_X0 = 38, + FSINGLE_SUB1_RRR_0_OPCODE_X0 = 39, + ICOH_UNARY_OPCODE_X1 = 7, + ILL_UNARY_OPCODE_X1 = 8, + ILL_UNARY_OPCODE_Y1 = 9, + IMM8_OPCODE_X0 = 4, + IMM8_OPCODE_X1 = 3, + INV_UNARY_OPCODE_X1 = 9, + IRET_UNARY_OPCODE_X1 = 10, + JALRP_UNARY_OPCODE_X1 = 11, + JALRP_UNARY_OPCODE_Y1 = 10, + JALR_UNARY_OPCODE_X1 = 12, + JALR_UNARY_OPCODE_Y1 = 11, + JAL_JUMP_OPCODE_X1 = 0, + JRP_UNARY_OPCODE_X1 = 13, + JRP_UNARY_OPCODE_Y1 = 12, + JR_UNARY_OPCODE_X1 = 14, + JR_UNARY_OPCODE_Y1 = 13, + JUMP_OPCODE_X1 = 4, + J_JUMP_OPCODE_X1 = 1, + LD1S_ADD_IMM8_OPCODE_X1 = 7, + LD1S_OPCODE_Y2 = 0, + LD1S_UNARY_OPCODE_X1 = 15, + LD1U_ADD_IMM8_OPCODE_X1 = 8, + LD1U_OPCODE_Y2 = 1, + LD1U_UNARY_OPCODE_X1 = 16, + LD2S_ADD_IMM8_OPCODE_X1 = 9, + LD2S_OPCODE_Y2 = 2, + LD2S_UNARY_OPCODE_X1 = 17, + LD2U_ADD_IMM8_OPCODE_X1 = 10, + LD2U_OPCODE_Y2 = 3, + LD2U_UNARY_OPCODE_X1 = 18, + LD4S_ADD_IMM8_OPCODE_X1 = 11, + LD4S_OPCODE_Y2 = 1, + LD4S_UNARY_OPCODE_X1 = 19, + LD4U_ADD_IMM8_OPCODE_X1 = 12, + LD4U_OPCODE_Y2 = 2, + LD4U_UNARY_OPCODE_X1 = 20, + LDNA_UNARY_OPCODE_X1 = 21, + LDNT1S_ADD_IMM8_OPCODE_X1 = 13, + LDNT1S_UNARY_OPCODE_X1 = 22, + LDNT1U_ADD_IMM8_OPCODE_X1 = 14, + LDNT1U_UNARY_OPCODE_X1 = 23, + LDNT2S_ADD_IMM8_OPCODE_X1 = 15, + LDNT2S_UNARY_OPCODE_X1 = 24, + LDNT2U_ADD_IMM8_OPCODE_X1 = 16, + LDNT2U_UNARY_OPCODE_X1 = 25, + LDNT4S_ADD_IMM8_OPCODE_X1 = 17, + LDNT4S_UNARY_OPCODE_X1 = 26, + LDNT4U_ADD_IMM8_OPCODE_X1 = 18, + LDNT4U_UNARY_OPCODE_X1 = 27, + LDNT_ADD_IMM8_OPCODE_X1 = 19, + LDNT_UNARY_OPCODE_X1 = 28, + LD_ADD_IMM8_OPCODE_X1 = 20, + LD_OPCODE_Y2 = 3, + LD_UNARY_OPCODE_X1 = 29, + LNK_UNARY_OPCODE_X1 = 30, + LNK_UNARY_OPCODE_Y1 = 14, + LDNA_ADD_IMM8_OPCODE_X1 = 21, + MFSPR_IMM8_OPCODE_X1 = 22, + MF_UNARY_OPCODE_X1 = 31, + MM_BF_OPCODE_X0 = 7, + MNZ_RRR_0_OPCODE_X0 = 40, + MNZ_RRR_0_OPCODE_X1 = 26, + MNZ_RRR_4_OPCODE_Y0 = 2, + MNZ_RRR_4_OPCODE_Y1 = 2, + MODE_OPCODE_YA2 = 1, + MODE_OPCODE_YB2 = 2, + MODE_OPCODE_YC2 = 3, + MTSPR_IMM8_OPCODE_X1 = 23, + MULAX_RRR_0_OPCODE_X0 = 41, + MULAX_RRR_3_OPCODE_Y0 = 2, + MULA_HS_HS_RRR_0_OPCODE_X0 = 42, + MULA_HS_HS_RRR_9_OPCODE_Y0 = 0, + MULA_HS_HU_RRR_0_OPCODE_X0 = 43, + MULA_HS_LS_RRR_0_OPCODE_X0 = 44, + MULA_HS_LU_RRR_0_OPCODE_X0 = 45, + MULA_HU_HU_RRR_0_OPCODE_X0 = 46, + MULA_HU_HU_RRR_9_OPCODE_Y0 = 1, + MULA_HU_LS_RRR_0_OPCODE_X0 = 47, + MULA_HU_LU_RRR_0_OPCODE_X0 = 48, + MULA_LS_LS_RRR_0_OPCODE_X0 = 49, + MULA_LS_LS_RRR_9_OPCODE_Y0 = 2, + MULA_LS_LU_RRR_0_OPCODE_X0 = 50, + MULA_LU_LU_RRR_0_OPCODE_X0 = 51, + MULA_LU_LU_RRR_9_OPCODE_Y0 = 3, + MULX_RRR_0_OPCODE_X0 = 52, + MULX_RRR_3_OPCODE_Y0 = 3, + MUL_HS_HS_RRR_0_OPCODE_X0 = 53, + MUL_HS_HS_RRR_8_OPCODE_Y0 = 0, + MUL_HS_HU_RRR_0_OPCODE_X0 = 54, + MUL_HS_LS_RRR_0_OPCODE_X0 = 55, + MUL_HS_LU_RRR_0_OPCODE_X0 = 56, + MUL_HU_HU_RRR_0_OPCODE_X0 = 57, + MUL_HU_HU_RRR_8_OPCODE_Y0 = 1, + MUL_HU_LS_RRR_0_OPCODE_X0 = 58, + MUL_HU_LU_RRR_0_OPCODE_X0 = 59, + MUL_LS_LS_RRR_0_OPCODE_X0 = 60, + MUL_LS_LS_RRR_8_OPCODE_Y0 = 2, + MUL_LS_LU_RRR_0_OPCODE_X0 = 61, + MUL_LU_LU_RRR_0_OPCODE_X0 = 62, + MUL_LU_LU_RRR_8_OPCODE_Y0 = 3, + MZ_RRR_0_OPCODE_X0 = 63, + MZ_RRR_0_OPCODE_X1 = 27, + MZ_RRR_4_OPCODE_Y0 = 3, + MZ_RRR_4_OPCODE_Y1 = 3, + NAP_UNARY_OPCODE_X1 = 32, + NOP_UNARY_OPCODE_X0 = 5, + NOP_UNARY_OPCODE_X1 = 33, + NOP_UNARY_OPCODE_Y0 = 5, + NOP_UNARY_OPCODE_Y1 = 15, + NOR_RRR_0_OPCODE_X0 = 64, + NOR_RRR_0_OPCODE_X1 = 28, + NOR_RRR_5_OPCODE_Y0 = 1, + NOR_RRR_5_OPCODE_Y1 = 1, + ORI_IMM8_OPCODE_X0 = 7, + ORI_IMM8_OPCODE_X1 = 24, + OR_RRR_0_OPCODE_X0 = 65, + OR_RRR_0_OPCODE_X1 = 29, + OR_RRR_5_OPCODE_Y0 = 2, + OR_RRR_5_OPCODE_Y1 = 2, + PCNT_UNARY_OPCODE_X0 = 6, + PCNT_UNARY_OPCODE_Y0 = 6, + REVBITS_UNARY_OPCODE_X0 = 7, + REVBITS_UNARY_OPCODE_Y0 = 7, + REVBYTES_UNARY_OPCODE_X0 = 8, + REVBYTES_UNARY_OPCODE_Y0 = 8, + ROTLI_SHIFT_OPCODE_X0 = 1, + ROTLI_SHIFT_OPCODE_X1 = 1, + ROTLI_SHIFT_OPCODE_Y0 = 0, + ROTLI_SHIFT_OPCODE_Y1 = 0, + ROTL_RRR_0_OPCODE_X0 = 66, + ROTL_RRR_0_OPCODE_X1 = 30, + ROTL_RRR_6_OPCODE_Y0 = 0, + ROTL_RRR_6_OPCODE_Y1 = 0, + RRR_0_OPCODE_X0 = 5, + RRR_0_OPCODE_X1 = 5, + RRR_0_OPCODE_Y0 = 5, + RRR_0_OPCODE_Y1 = 6, + RRR_1_OPCODE_Y0 = 6, + RRR_1_OPCODE_Y1 = 7, + RRR_2_OPCODE_Y0 = 7, + RRR_2_OPCODE_Y1 = 8, + RRR_3_OPCODE_Y0 = 8, + RRR_3_OPCODE_Y1 = 9, + RRR_4_OPCODE_Y0 = 9, + RRR_4_OPCODE_Y1 = 10, + RRR_5_OPCODE_Y0 = 10, + RRR_5_OPCODE_Y1 = 11, + RRR_6_OPCODE_Y0 = 11, + RRR_6_OPCODE_Y1 = 12, + RRR_7_OPCODE_Y0 = 12, + RRR_7_OPCODE_Y1 = 13, + RRR_8_OPCODE_Y0 = 13, + RRR_9_OPCODE_Y0 = 14, + SHIFT_OPCODE_X0 = 6, + SHIFT_OPCODE_X1 = 6, + SHIFT_OPCODE_Y0 = 15, + SHIFT_OPCODE_Y1 = 14, + SHL16INSLI_OPCODE_X0 = 7, + SHL16INSLI_OPCODE_X1 = 7, + SHL1ADDX_RRR_0_OPCODE_X0 = 67, + SHL1ADDX_RRR_0_OPCODE_X1 = 31, + SHL1ADDX_RRR_7_OPCODE_Y0 = 1, + SHL1ADDX_RRR_7_OPCODE_Y1 = 1, + SHL1ADD_RRR_0_OPCODE_X0 = 68, + SHL1ADD_RRR_0_OPCODE_X1 = 32, + SHL1ADD_RRR_1_OPCODE_Y0 = 0, + SHL1ADD_RRR_1_OPCODE_Y1 = 0, + SHL2ADDX_RRR_0_OPCODE_X0 = 69, + SHL2ADDX_RRR_0_OPCODE_X1 = 33, + SHL2ADDX_RRR_7_OPCODE_Y0 = 2, + SHL2ADDX_RRR_7_OPCODE_Y1 = 2, + SHL2ADD_RRR_0_OPCODE_X0 = 70, + SHL2ADD_RRR_0_OPCODE_X1 = 34, + SHL2ADD_RRR_1_OPCODE_Y0 = 1, + SHL2ADD_RRR_1_OPCODE_Y1 = 1, + SHL3ADDX_RRR_0_OPCODE_X0 = 71, + SHL3ADDX_RRR_0_OPCODE_X1 = 35, + SHL3ADDX_RRR_7_OPCODE_Y0 = 3, + SHL3ADDX_RRR_7_OPCODE_Y1 = 3, + SHL3ADD_RRR_0_OPCODE_X0 = 72, + SHL3ADD_RRR_0_OPCODE_X1 = 36, + SHL3ADD_RRR_1_OPCODE_Y0 = 2, + SHL3ADD_RRR_1_OPCODE_Y1 = 2, + SHLI_SHIFT_OPCODE_X0 = 2, + SHLI_SHIFT_OPCODE_X1 = 2, + SHLI_SHIFT_OPCODE_Y0 = 1, + SHLI_SHIFT_OPCODE_Y1 = 1, + SHLXI_SHIFT_OPCODE_X0 = 3, + SHLXI_SHIFT_OPCODE_X1 = 3, + SHLX_RRR_0_OPCODE_X0 = 73, + SHLX_RRR_0_OPCODE_X1 = 37, + SHL_RRR_0_OPCODE_X0 = 74, + SHL_RRR_0_OPCODE_X1 = 38, + SHL_RRR_6_OPCODE_Y0 = 1, + SHL_RRR_6_OPCODE_Y1 = 1, + SHRSI_SHIFT_OPCODE_X0 = 4, + SHRSI_SHIFT_OPCODE_X1 = 4, + SHRSI_SHIFT_OPCODE_Y0 = 2, + SHRSI_SHIFT_OPCODE_Y1 = 2, + SHRS_RRR_0_OPCODE_X0 = 75, + SHRS_RRR_0_OPCODE_X1 = 39, + SHRS_RRR_6_OPCODE_Y0 = 2, + SHRS_RRR_6_OPCODE_Y1 = 2, + SHRUI_SHIFT_OPCODE_X0 = 5, + SHRUI_SHIFT_OPCODE_X1 = 5, + SHRUI_SHIFT_OPCODE_Y0 = 3, + SHRUI_SHIFT_OPCODE_Y1 = 3, + SHRUXI_SHIFT_OPCODE_X0 = 6, + SHRUXI_SHIFT_OPCODE_X1 = 6, + SHRUX_RRR_0_OPCODE_X0 = 76, + SHRUX_RRR_0_OPCODE_X1 = 40, + SHRU_RRR_0_OPCODE_X0 = 77, + SHRU_RRR_0_OPCODE_X1 = 41, + SHRU_RRR_6_OPCODE_Y0 = 3, + SHRU_RRR_6_OPCODE_Y1 = 3, + SHUFFLEBYTES_RRR_0_OPCODE_X0 = 78, + ST1_ADD_IMM8_OPCODE_X1 = 25, + ST1_OPCODE_Y2 = 0, + ST1_RRR_0_OPCODE_X1 = 42, + ST2_ADD_IMM8_OPCODE_X1 = 26, + ST2_OPCODE_Y2 = 1, + ST2_RRR_0_OPCODE_X1 = 43, + ST4_ADD_IMM8_OPCODE_X1 = 27, + ST4_OPCODE_Y2 = 2, + ST4_RRR_0_OPCODE_X1 = 44, + STNT1_ADD_IMM8_OPCODE_X1 = 28, + STNT1_RRR_0_OPCODE_X1 = 45, + STNT2_ADD_IMM8_OPCODE_X1 = 29, + STNT2_RRR_0_OPCODE_X1 = 46, + STNT4_ADD_IMM8_OPCODE_X1 = 30, + STNT4_RRR_0_OPCODE_X1 = 47, + STNT_ADD_IMM8_OPCODE_X1 = 31, + STNT_RRR_0_OPCODE_X1 = 48, + ST_ADD_IMM8_OPCODE_X1 = 32, + ST_OPCODE_Y2 = 3, + ST_RRR_0_OPCODE_X1 = 49, + SUBXSC_RRR_0_OPCODE_X0 = 79, + SUBXSC_RRR_0_OPCODE_X1 = 50, + SUBX_RRR_0_OPCODE_X0 = 80, + SUBX_RRR_0_OPCODE_X1 = 51, + SUBX_RRR_0_OPCODE_Y0 = 2, + SUBX_RRR_0_OPCODE_Y1 = 2, + SUB_RRR_0_OPCODE_X0 = 81, + SUB_RRR_0_OPCODE_X1 = 52, + SUB_RRR_0_OPCODE_Y0 = 3, + SUB_RRR_0_OPCODE_Y1 = 3, + SWINT0_UNARY_OPCODE_X1 = 34, + SWINT1_UNARY_OPCODE_X1 = 35, + SWINT2_UNARY_OPCODE_X1 = 36, + SWINT3_UNARY_OPCODE_X1 = 37, + TBLIDXB0_UNARY_OPCODE_X0 = 9, + TBLIDXB0_UNARY_OPCODE_Y0 = 9, + TBLIDXB1_UNARY_OPCODE_X0 = 10, + TBLIDXB1_UNARY_OPCODE_Y0 = 10, + TBLIDXB2_UNARY_OPCODE_X0 = 11, + TBLIDXB2_UNARY_OPCODE_Y0 = 11, + TBLIDXB3_UNARY_OPCODE_X0 = 12, + TBLIDXB3_UNARY_OPCODE_Y0 = 12, + UNARY_RRR_0_OPCODE_X0 = 82, + UNARY_RRR_0_OPCODE_X1 = 53, + UNARY_RRR_1_OPCODE_Y0 = 3, + UNARY_RRR_1_OPCODE_Y1 = 3, + V1ADDI_IMM8_OPCODE_X0 = 8, + V1ADDI_IMM8_OPCODE_X1 = 33, + V1ADDUC_RRR_0_OPCODE_X0 = 83, + V1ADDUC_RRR_0_OPCODE_X1 = 54, + V1ADD_RRR_0_OPCODE_X0 = 84, + V1ADD_RRR_0_OPCODE_X1 = 55, + V1ADIFFU_RRR_0_OPCODE_X0 = 85, + V1AVGU_RRR_0_OPCODE_X0 = 86, + V1CMPEQI_IMM8_OPCODE_X0 = 9, + V1CMPEQI_IMM8_OPCODE_X1 = 34, + V1CMPEQ_RRR_0_OPCODE_X0 = 87, + V1CMPEQ_RRR_0_OPCODE_X1 = 56, + V1CMPLES_RRR_0_OPCODE_X0 = 88, + V1CMPLES_RRR_0_OPCODE_X1 = 57, + V1CMPLEU_RRR_0_OPCODE_X0 = 89, + V1CMPLEU_RRR_0_OPCODE_X1 = 58, + V1CMPLTSI_IMM8_OPCODE_X0 = 10, + V1CMPLTSI_IMM8_OPCODE_X1 = 35, + V1CMPLTS_RRR_0_OPCODE_X0 = 90, + V1CMPLTS_RRR_0_OPCODE_X1 = 59, + V1CMPLTUI_IMM8_OPCODE_X0 = 11, + V1CMPLTUI_IMM8_OPCODE_X1 = 36, + V1CMPLTU_RRR_0_OPCODE_X0 = 91, + V1CMPLTU_RRR_0_OPCODE_X1 = 60, + V1CMPNE_RRR_0_OPCODE_X0 = 92, + V1CMPNE_RRR_0_OPCODE_X1 = 61, + V1DDOTPUA_RRR_0_OPCODE_X0 = 161, + V1DDOTPUSA_RRR_0_OPCODE_X0 = 93, + V1DDOTPUS_RRR_0_OPCODE_X0 = 94, + V1DDOTPU_RRR_0_OPCODE_X0 = 162, + V1DOTPA_RRR_0_OPCODE_X0 = 95, + V1DOTPUA_RRR_0_OPCODE_X0 = 163, + V1DOTPUSA_RRR_0_OPCODE_X0 = 96, + V1DOTPUS_RRR_0_OPCODE_X0 = 97, + V1DOTPU_RRR_0_OPCODE_X0 = 164, + V1DOTP_RRR_0_OPCODE_X0 = 98, + V1INT_H_RRR_0_OPCODE_X0 = 99, + V1INT_H_RRR_0_OPCODE_X1 = 62, + V1INT_L_RRR_0_OPCODE_X0 = 100, + V1INT_L_RRR_0_OPCODE_X1 = 63, + V1MAXUI_IMM8_OPCODE_X0 = 12, + V1MAXUI_IMM8_OPCODE_X1 = 37, + V1MAXU_RRR_0_OPCODE_X0 = 101, + V1MAXU_RRR_0_OPCODE_X1 = 64, + V1MINUI_IMM8_OPCODE_X0 = 13, + V1MINUI_IMM8_OPCODE_X1 = 38, + V1MINU_RRR_0_OPCODE_X0 = 102, + V1MINU_RRR_0_OPCODE_X1 = 65, + V1MNZ_RRR_0_OPCODE_X0 = 103, + V1MNZ_RRR_0_OPCODE_X1 = 66, + V1MULTU_RRR_0_OPCODE_X0 = 104, + V1MULUS_RRR_0_OPCODE_X0 = 105, + V1MULU_RRR_0_OPCODE_X0 = 106, + V1MZ_RRR_0_OPCODE_X0 = 107, + V1MZ_RRR_0_OPCODE_X1 = 67, + V1SADAU_RRR_0_OPCODE_X0 = 108, + V1SADU_RRR_0_OPCODE_X0 = 109, + V1SHLI_SHIFT_OPCODE_X0 = 7, + V1SHLI_SHIFT_OPCODE_X1 = 7, + V1SHL_RRR_0_OPCODE_X0 = 110, + V1SHL_RRR_0_OPCODE_X1 = 68, + V1SHRSI_SHIFT_OPCODE_X0 = 8, + V1SHRSI_SHIFT_OPCODE_X1 = 8, + V1SHRS_RRR_0_OPCODE_X0 = 111, + V1SHRS_RRR_0_OPCODE_X1 = 69, + V1SHRUI_SHIFT_OPCODE_X0 = 9, + V1SHRUI_SHIFT_OPCODE_X1 = 9, + V1SHRU_RRR_0_OPCODE_X0 = 112, + V1SHRU_RRR_0_OPCODE_X1 = 70, + V1SUBUC_RRR_0_OPCODE_X0 = 113, + V1SUBUC_RRR_0_OPCODE_X1 = 71, + V1SUB_RRR_0_OPCODE_X0 = 114, + V1SUB_RRR_0_OPCODE_X1 = 72, + V2ADDI_IMM8_OPCODE_X0 = 14, + V2ADDI_IMM8_OPCODE_X1 = 39, + V2ADDSC_RRR_0_OPCODE_X0 = 115, + V2ADDSC_RRR_0_OPCODE_X1 = 73, + V2ADD_RRR_0_OPCODE_X0 = 116, + V2ADD_RRR_0_OPCODE_X1 = 74, + V2ADIFFS_RRR_0_OPCODE_X0 = 117, + V2AVGS_RRR_0_OPCODE_X0 = 118, + V2CMPEQI_IMM8_OPCODE_X0 = 15, + V2CMPEQI_IMM8_OPCODE_X1 = 40, + V2CMPEQ_RRR_0_OPCODE_X0 = 119, + V2CMPEQ_RRR_0_OPCODE_X1 = 75, + V2CMPLES_RRR_0_OPCODE_X0 = 120, + V2CMPLES_RRR_0_OPCODE_X1 = 76, + V2CMPLEU_RRR_0_OPCODE_X0 = 121, + V2CMPLEU_RRR_0_OPCODE_X1 = 77, + V2CMPLTSI_IMM8_OPCODE_X0 = 16, + V2CMPLTSI_IMM8_OPCODE_X1 = 41, + V2CMPLTS_RRR_0_OPCODE_X0 = 122, + V2CMPLTS_RRR_0_OPCODE_X1 = 78, + V2CMPLTUI_IMM8_OPCODE_X0 = 17, + V2CMPLTUI_IMM8_OPCODE_X1 = 42, + V2CMPLTU_RRR_0_OPCODE_X0 = 123, + V2CMPLTU_RRR_0_OPCODE_X1 = 79, + V2CMPNE_RRR_0_OPCODE_X0 = 124, + V2CMPNE_RRR_0_OPCODE_X1 = 80, + V2DOTPA_RRR_0_OPCODE_X0 = 125, + V2DOTP_RRR_0_OPCODE_X0 = 126, + V2INT_H_RRR_0_OPCODE_X0 = 127, + V2INT_H_RRR_0_OPCODE_X1 = 81, + V2INT_L_RRR_0_OPCODE_X0 = 128, + V2INT_L_RRR_0_OPCODE_X1 = 82, + V2MAXSI_IMM8_OPCODE_X0 = 18, + V2MAXSI_IMM8_OPCODE_X1 = 43, + V2MAXS_RRR_0_OPCODE_X0 = 129, + V2MAXS_RRR_0_OPCODE_X1 = 83, + V2MINSI_IMM8_OPCODE_X0 = 19, + V2MINSI_IMM8_OPCODE_X1 = 44, + V2MINS_RRR_0_OPCODE_X0 = 130, + V2MINS_RRR_0_OPCODE_X1 = 84, + V2MNZ_RRR_0_OPCODE_X0 = 131, + V2MNZ_RRR_0_OPCODE_X1 = 85, + V2MULFSC_RRR_0_OPCODE_X0 = 132, + V2MULS_RRR_0_OPCODE_X0 = 133, + V2MULTS_RRR_0_OPCODE_X0 = 134, + V2MZ_RRR_0_OPCODE_X0 = 135, + V2MZ_RRR_0_OPCODE_X1 = 86, + V2PACKH_RRR_0_OPCODE_X0 = 136, + V2PACKH_RRR_0_OPCODE_X1 = 87, + V2PACKL_RRR_0_OPCODE_X0 = 137, + V2PACKL_RRR_0_OPCODE_X1 = 88, + V2PACKUC_RRR_0_OPCODE_X0 = 138, + V2PACKUC_RRR_0_OPCODE_X1 = 89, + V2SADAS_RRR_0_OPCODE_X0 = 139, + V2SADAU_RRR_0_OPCODE_X0 = 140, + V2SADS_RRR_0_OPCODE_X0 = 141, + V2SADU_RRR_0_OPCODE_X0 = 142, + V2SHLI_SHIFT_OPCODE_X0 = 10, + V2SHLI_SHIFT_OPCODE_X1 = 10, + V2SHLSC_RRR_0_OPCODE_X0 = 143, + V2SHLSC_RRR_0_OPCODE_X1 = 90, + V2SHL_RRR_0_OPCODE_X0 = 144, + V2SHL_RRR_0_OPCODE_X1 = 91, + V2SHRSI_SHIFT_OPCODE_X0 = 11, + V2SHRSI_SHIFT_OPCODE_X1 = 11, + V2SHRS_RRR_0_OPCODE_X0 = 145, + V2SHRS_RRR_0_OPCODE_X1 = 92, + V2SHRUI_SHIFT_OPCODE_X0 = 12, + V2SHRUI_SHIFT_OPCODE_X1 = 12, + V2SHRU_RRR_0_OPCODE_X0 = 146, + V2SHRU_RRR_0_OPCODE_X1 = 93, + V2SUBSC_RRR_0_OPCODE_X0 = 147, + V2SUBSC_RRR_0_OPCODE_X1 = 94, + V2SUB_RRR_0_OPCODE_X0 = 148, + V2SUB_RRR_0_OPCODE_X1 = 95, + V4ADDSC_RRR_0_OPCODE_X0 = 149, + V4ADDSC_RRR_0_OPCODE_X1 = 96, + V4ADD_RRR_0_OPCODE_X0 = 150, + V4ADD_RRR_0_OPCODE_X1 = 97, + V4INT_H_RRR_0_OPCODE_X0 = 151, + V4INT_H_RRR_0_OPCODE_X1 = 98, + V4INT_L_RRR_0_OPCODE_X0 = 152, + V4INT_L_RRR_0_OPCODE_X1 = 99, + V4PACKSC_RRR_0_OPCODE_X0 = 153, + V4PACKSC_RRR_0_OPCODE_X1 = 100, + V4SHLSC_RRR_0_OPCODE_X0 = 154, + V4SHLSC_RRR_0_OPCODE_X1 = 101, + V4SHL_RRR_0_OPCODE_X0 = 155, + V4SHL_RRR_0_OPCODE_X1 = 102, + V4SHRS_RRR_0_OPCODE_X0 = 156, + V4SHRS_RRR_0_OPCODE_X1 = 103, + V4SHRU_RRR_0_OPCODE_X0 = 157, + V4SHRU_RRR_0_OPCODE_X1 = 104, + V4SUBSC_RRR_0_OPCODE_X0 = 158, + V4SUBSC_RRR_0_OPCODE_X1 = 105, + V4SUB_RRR_0_OPCODE_X0 = 159, + V4SUB_RRR_0_OPCODE_X1 = 106, + WH64_UNARY_OPCODE_X1 = 38, + XORI_IMM8_OPCODE_X0 = 20, + XORI_IMM8_OPCODE_X1 = 45, + XOR_RRR_0_OPCODE_X0 = 160, + XOR_RRR_0_OPCODE_X1 = 107, + XOR_RRR_5_OPCODE_Y0 = 3, + XOR_RRR_5_OPCODE_Y1 = 3 +}; + + +#endif /* __ASSEMBLER__ */ + +#endif /* __ARCH_OPCODE_H__ */ diff --git a/qemu/target-tilegx/simd_helper.c b/qemu/target-tilegx/simd_helper.c new file mode 100644 index 000000000..2d40ddb63 --- /dev/null +++ b/qemu/target-tilegx/simd_helper.c @@ -0,0 +1,166 @@ +/* + * QEMU TILE-Gx helpers + * + * Copyright (c) 2015 Chen Gang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "qemu-common.h" +#include "exec/helper-proto.h" + + +/* Broadcast a value to all elements of a vector. */ +#define V1(X) (((X) & 0xff) * 0x0101010101010101ull) +#define V2(X) (((X) & 0xffff) * 0x0001000100010001ull) + + +uint64_t helper_v1multu(uint64_t a, uint64_t b) +{ + uint64_t r = 0; + int i; + + for (i = 0; i < 64; i += 8) { + unsigned ae = extract64(a, i, 8); + unsigned be = extract64(b, i, 8); + r = deposit64(r, i, 8, ae * be); + } + return r; +} + +uint64_t helper_v2mults(uint64_t a, uint64_t b) +{ + uint64_t r = 0; + int i; + + /* While the instruction talks about signed inputs, with a + truncated result the sign of the inputs doesn't matter. */ + for (i = 0; i < 64; i += 16) { + unsigned ae = extract64(a, i, 16); + unsigned be = extract64(b, i, 16); + r = deposit64(r, i, 16, ae * be); + } + return r; +} + +uint64_t helper_v1shl(uint64_t a, uint64_t b) +{ + uint64_t m; + + b &= 7; + m = V1(0xff >> b); + return (a & m) << b; +} + +uint64_t helper_v2shl(uint64_t a, uint64_t b) +{ + uint64_t m; + + b &= 15; + m = V2(0xffff >> b); + return (a & m) << b; +} + +uint64_t helper_v1shru(uint64_t a, uint64_t b) +{ + uint64_t m; + + b &= 7; + m = V1(0xff << b); + return (a & m) >> b; +} + +uint64_t helper_v2shru(uint64_t a, uint64_t b) +{ + uint64_t m; + + b &= 15; + m = V2(0xffff << b); + return (a & m) >> b; +} + +uint64_t helper_v1shrs(uint64_t a, uint64_t b) +{ + uint64_t r = 0; + int i; + + b &= 7; + for (i = 0; i < 64; i += 8) { + r = deposit64(r, i, 8, sextract64(a, i + b, 8 - b)); + } + return r; +} + +uint64_t helper_v2shrs(uint64_t a, uint64_t b) +{ + uint64_t r = 0; + int i; + + b &= 15; + for (i = 0; i < 64; i += 16) { + r = deposit64(r, i, 16, sextract64(a, i + b, 16 - b)); + } + return r; +} + +uint64_t helper_v1int_h(uint64_t a, uint64_t b) +{ + uint64_t r = 0; + int i; + + for (i = 0; i < 32; i += 8) { + r = deposit64(r, 2 * i + 8, 8, extract64(a, i + 32, 8)); + r = deposit64(r, 2 * i, 8, extract64(b, i + 32, 8)); + } + return r; +} + +uint64_t helper_v1int_l(uint64_t a, uint64_t b) +{ + uint64_t r = 0; + int i; + + for (i = 0; i < 32; i += 8) { + r = deposit64(r, 2 * i + 8, 8, extract64(a, i, 8)); + r = deposit64(r, 2 * i, 8, extract64(b, i, 8)); + } + return r; +} + +uint64_t helper_v2int_h(uint64_t a, uint64_t b) +{ + uint64_t r = 0; + int i; + + for (i = 0; i < 32; i += 16) { + r = deposit64(r, 2 * i + 16, 16, extract64(a, i + 32, 16)); + r = deposit64(r, 2 * i, 16, extract64(b, i + 32, 16)); + } + return r; +} + +uint64_t helper_v2int_l(uint64_t a, uint64_t b) +{ + uint64_t r = 0; + int i; + + for (i = 0; i < 32; i += 16) { + r = deposit64(r, 2 * i + 16, 16, extract64(a, i, 16)); + r = deposit64(r, 2 * i, 16, extract64(b, i, 16)); + } + return r; +} diff --git a/qemu/target-tilegx/spr_def_64.h b/qemu/target-tilegx/spr_def_64.h new file mode 100644 index 000000000..67a6c1751 --- /dev/null +++ b/qemu/target-tilegx/spr_def_64.h @@ -0,0 +1,216 @@ +/* + * Copyright 2011 Tilera Corporation. 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, version 2. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for + * more details. + */ + +#ifndef __DOXYGEN__ + +#ifndef __ARCH_SPR_DEF_64_H__ +#define __ARCH_SPR_DEF_64_H__ + +#define SPR_AUX_PERF_COUNT_0 0x2105 +#define SPR_AUX_PERF_COUNT_1 0x2106 +#define SPR_AUX_PERF_COUNT_CTL 0x2107 +#define SPR_AUX_PERF_COUNT_STS 0x2108 +#define SPR_CMPEXCH_VALUE 0x2780 +#define SPR_CYCLE 0x2781 +#define SPR_DONE 0x2705 +#define SPR_DSTREAM_PF 0x2706 +#define SPR_EVENT_BEGIN 0x2782 +#define SPR_EVENT_END 0x2783 +#define SPR_EX_CONTEXT_0_0 0x2580 +#define SPR_EX_CONTEXT_0_1 0x2581 +#define SPR_EX_CONTEXT_0_1__PL_SHIFT 0 +#define SPR_EX_CONTEXT_0_1__PL_RMASK 0x3 +#define SPR_EX_CONTEXT_0_1__PL_MASK 0x3 +#define SPR_EX_CONTEXT_0_1__ICS_SHIFT 2 +#define SPR_EX_CONTEXT_0_1__ICS_RMASK 0x1 +#define SPR_EX_CONTEXT_0_1__ICS_MASK 0x4 +#define SPR_EX_CONTEXT_1_0 0x2480 +#define SPR_EX_CONTEXT_1_1 0x2481 +#define SPR_EX_CONTEXT_1_1__PL_SHIFT 0 +#define SPR_EX_CONTEXT_1_1__PL_RMASK 0x3 +#define SPR_EX_CONTEXT_1_1__PL_MASK 0x3 +#define SPR_EX_CONTEXT_1_1__ICS_SHIFT 2 +#define SPR_EX_CONTEXT_1_1__ICS_RMASK 0x1 +#define SPR_EX_CONTEXT_1_1__ICS_MASK 0x4 +#define SPR_EX_CONTEXT_2_0 0x2380 +#define SPR_EX_CONTEXT_2_1 0x2381 +#define SPR_EX_CONTEXT_2_1__PL_SHIFT 0 +#define SPR_EX_CONTEXT_2_1__PL_RMASK 0x3 +#define SPR_EX_CONTEXT_2_1__PL_MASK 0x3 +#define SPR_EX_CONTEXT_2_1__ICS_SHIFT 2 +#define SPR_EX_CONTEXT_2_1__ICS_RMASK 0x1 +#define SPR_EX_CONTEXT_2_1__ICS_MASK 0x4 +#define SPR_FAIL 0x2707 +#define SPR_IDN_AVAIL_EN 0x1a05 +#define SPR_IDN_DATA_AVAIL 0x0a80 +#define SPR_IDN_DEADLOCK_TIMEOUT 0x1806 +#define SPR_IDN_DEMUX_COUNT_0 0x0a05 +#define SPR_IDN_DEMUX_COUNT_1 0x0a06 +#define SPR_IDN_DIRECTION_PROTECT 0x1405 +#define SPR_IDN_PENDING 0x0a08 +#define SPR_ILL_TRANS_REASON__I_STREAM_VA_RMASK 0x1 +#define SPR_INTCTRL_0_STATUS 0x2505 +#define SPR_INTCTRL_1_STATUS 0x2405 +#define SPR_INTCTRL_2_STATUS 0x2305 +#define SPR_INTERRUPT_CRITICAL_SECTION 0x2708 +#define SPR_INTERRUPT_MASK_0 0x2506 +#define SPR_INTERRUPT_MASK_1 0x2406 +#define SPR_INTERRUPT_MASK_2 0x2306 +#define SPR_INTERRUPT_MASK_RESET_0 0x2507 +#define SPR_INTERRUPT_MASK_RESET_1 0x2407 +#define SPR_INTERRUPT_MASK_RESET_2 0x2307 +#define SPR_INTERRUPT_MASK_SET_0 0x2508 +#define SPR_INTERRUPT_MASK_SET_1 0x2408 +#define SPR_INTERRUPT_MASK_SET_2 0x2308 +#define SPR_INTERRUPT_VECTOR_BASE_0 0x2509 +#define SPR_INTERRUPT_VECTOR_BASE_1 0x2409 +#define SPR_INTERRUPT_VECTOR_BASE_2 0x2309 +#define SPR_INTERRUPT_VECTOR_BASE_3 0x2209 +#define SPR_IPI_EVENT_0 0x1f05 +#define SPR_IPI_EVENT_1 0x1e05 +#define SPR_IPI_EVENT_2 0x1d05 +#define SPR_IPI_EVENT_RESET_0 0x1f06 +#define SPR_IPI_EVENT_RESET_1 0x1e06 +#define SPR_IPI_EVENT_RESET_2 0x1d06 +#define SPR_IPI_EVENT_SET_0 0x1f07 +#define SPR_IPI_EVENT_SET_1 0x1e07 +#define SPR_IPI_EVENT_SET_2 0x1d07 +#define SPR_IPI_MASK_0 0x1f08 +#define SPR_IPI_MASK_1 0x1e08 +#define SPR_IPI_MASK_2 0x1d08 +#define SPR_IPI_MASK_RESET_0 0x1f09 +#define SPR_IPI_MASK_RESET_1 0x1e09 +#define SPR_IPI_MASK_RESET_2 0x1d09 +#define SPR_IPI_MASK_SET_0 0x1f0a +#define SPR_IPI_MASK_SET_1 0x1e0a +#define SPR_IPI_MASK_SET_2 0x1d0a +#define SPR_MPL_AUX_PERF_COUNT_SET_0 0x2100 +#define SPR_MPL_AUX_PERF_COUNT_SET_1 0x2101 +#define SPR_MPL_AUX_PERF_COUNT_SET_2 0x2102 +#define SPR_MPL_AUX_TILE_TIMER_SET_0 0x1700 +#define SPR_MPL_AUX_TILE_TIMER_SET_1 0x1701 +#define SPR_MPL_AUX_TILE_TIMER_SET_2 0x1702 +#define SPR_MPL_IDN_ACCESS_SET_0 0x0a00 +#define SPR_MPL_IDN_ACCESS_SET_1 0x0a01 +#define SPR_MPL_IDN_ACCESS_SET_2 0x0a02 +#define SPR_MPL_IDN_AVAIL_SET_0 0x1a00 +#define SPR_MPL_IDN_AVAIL_SET_1 0x1a01 +#define SPR_MPL_IDN_AVAIL_SET_2 0x1a02 +#define SPR_MPL_IDN_COMPLETE_SET_0 0x0500 +#define SPR_MPL_IDN_COMPLETE_SET_1 0x0501 +#define SPR_MPL_IDN_COMPLETE_SET_2 0x0502 +#define SPR_MPL_IDN_FIREWALL_SET_0 0x1400 +#define SPR_MPL_IDN_FIREWALL_SET_1 0x1401 +#define SPR_MPL_IDN_FIREWALL_SET_2 0x1402 +#define SPR_MPL_IDN_TIMER_SET_0 0x1800 +#define SPR_MPL_IDN_TIMER_SET_1 0x1801 +#define SPR_MPL_IDN_TIMER_SET_2 0x1802 +#define SPR_MPL_INTCTRL_0_SET_0 0x2500 +#define SPR_MPL_INTCTRL_0_SET_1 0x2501 +#define SPR_MPL_INTCTRL_0_SET_2 0x2502 +#define SPR_MPL_INTCTRL_1_SET_0 0x2400 +#define SPR_MPL_INTCTRL_1_SET_1 0x2401 +#define SPR_MPL_INTCTRL_1_SET_2 0x2402 +#define SPR_MPL_INTCTRL_2_SET_0 0x2300 +#define SPR_MPL_INTCTRL_2_SET_1 0x2301 +#define SPR_MPL_INTCTRL_2_SET_2 0x2302 +#define SPR_MPL_IPI_0 0x1f04 +#define SPR_MPL_IPI_0_SET_0 0x1f00 +#define SPR_MPL_IPI_0_SET_1 0x1f01 +#define SPR_MPL_IPI_0_SET_2 0x1f02 +#define SPR_MPL_IPI_1 0x1e04 +#define SPR_MPL_IPI_1_SET_0 0x1e00 +#define SPR_MPL_IPI_1_SET_1 0x1e01 +#define SPR_MPL_IPI_1_SET_2 0x1e02 +#define SPR_MPL_IPI_2 0x1d04 +#define SPR_MPL_IPI_2_SET_0 0x1d00 +#define SPR_MPL_IPI_2_SET_1 0x1d01 +#define SPR_MPL_IPI_2_SET_2 0x1d02 +#define SPR_MPL_PERF_COUNT_SET_0 0x2000 +#define SPR_MPL_PERF_COUNT_SET_1 0x2001 +#define SPR_MPL_PERF_COUNT_SET_2 0x2002 +#define SPR_MPL_UDN_ACCESS_SET_0 0x0b00 +#define SPR_MPL_UDN_ACCESS_SET_1 0x0b01 +#define SPR_MPL_UDN_ACCESS_SET_2 0x0b02 +#define SPR_MPL_UDN_AVAIL_SET_0 0x1b00 +#define SPR_MPL_UDN_AVAIL_SET_1 0x1b01 +#define SPR_MPL_UDN_AVAIL_SET_2 0x1b02 +#define SPR_MPL_UDN_COMPLETE_SET_0 0x0600 +#define SPR_MPL_UDN_COMPLETE_SET_1 0x0601 +#define SPR_MPL_UDN_COMPLETE_SET_2 0x0602 +#define SPR_MPL_UDN_FIREWALL_SET_0 0x1500 +#define SPR_MPL_UDN_FIREWALL_SET_1 0x1501 +#define SPR_MPL_UDN_FIREWALL_SET_2 0x1502 +#define SPR_MPL_UDN_TIMER_SET_0 0x1900 +#define SPR_MPL_UDN_TIMER_SET_1 0x1901 +#define SPR_MPL_UDN_TIMER_SET_2 0x1902 +#define SPR_MPL_WORLD_ACCESS_SET_0 0x2700 +#define SPR_MPL_WORLD_ACCESS_SET_1 0x2701 +#define SPR_MPL_WORLD_ACCESS_SET_2 0x2702 +#define SPR_PASS 0x2709 +#define SPR_PERF_COUNT_0 0x2005 +#define SPR_PERF_COUNT_1 0x2006 +#define SPR_PERF_COUNT_CTL 0x2007 +#define SPR_PERF_COUNT_DN_CTL 0x2008 +#define SPR_PERF_COUNT_STS 0x2009 +#define SPR_PROC_STATUS 0x2784 +#define SPR_SIM_CONTROL 0x2785 +#define SPR_SINGLE_STEP_CONTROL_0 0x0405 +#define SPR_SINGLE_STEP_CONTROL_0__CANCELED_MASK 0x1 +#define SPR_SINGLE_STEP_CONTROL_0__INHIBIT_MASK 0x2 +#define SPR_SINGLE_STEP_CONTROL_1 0x0305 +#define SPR_SINGLE_STEP_CONTROL_1__CANCELED_MASK 0x1 +#define SPR_SINGLE_STEP_CONTROL_1__INHIBIT_MASK 0x2 +#define SPR_SINGLE_STEP_CONTROL_2 0x0205 +#define SPR_SINGLE_STEP_CONTROL_2__CANCELED_MASK 0x1 +#define SPR_SINGLE_STEP_CONTROL_2__INHIBIT_MASK 0x2 +#define SPR_SINGLE_STEP_EN_0_0 0x250a +#define SPR_SINGLE_STEP_EN_0_1 0x240a +#define SPR_SINGLE_STEP_EN_0_2 0x230a +#define SPR_SINGLE_STEP_EN_1_0 0x250b +#define SPR_SINGLE_STEP_EN_1_1 0x240b +#define SPR_SINGLE_STEP_EN_1_2 0x230b +#define SPR_SINGLE_STEP_EN_2_0 0x250c +#define SPR_SINGLE_STEP_EN_2_1 0x240c +#define SPR_SINGLE_STEP_EN_2_2 0x230c +#define SPR_SYSTEM_SAVE_0_0 0x2582 +#define SPR_SYSTEM_SAVE_0_1 0x2583 +#define SPR_SYSTEM_SAVE_0_2 0x2584 +#define SPR_SYSTEM_SAVE_0_3 0x2585 +#define SPR_SYSTEM_SAVE_1_0 0x2482 +#define SPR_SYSTEM_SAVE_1_1 0x2483 +#define SPR_SYSTEM_SAVE_1_2 0x2484 +#define SPR_SYSTEM_SAVE_1_3 0x2485 +#define SPR_SYSTEM_SAVE_2_0 0x2382 +#define SPR_SYSTEM_SAVE_2_1 0x2383 +#define SPR_SYSTEM_SAVE_2_2 0x2384 +#define SPR_SYSTEM_SAVE_2_3 0x2385 +#define SPR_TILE_COORD 0x270b +#define SPR_TILE_RTF_HWM 0x270c +#define SPR_TILE_TIMER_CONTROL 0x1605 +#define SPR_UDN_AVAIL_EN 0x1b05 +#define SPR_UDN_DATA_AVAIL 0x0b80 +#define SPR_UDN_DEADLOCK_TIMEOUT 0x1906 +#define SPR_UDN_DEMUX_COUNT_0 0x0b05 +#define SPR_UDN_DEMUX_COUNT_1 0x0b06 +#define SPR_UDN_DEMUX_COUNT_2 0x0b07 +#define SPR_UDN_DEMUX_COUNT_3 0x0b08 +#define SPR_UDN_DIRECTION_PROTECT 0x1505 +#define SPR_UDN_PENDING 0x0b0a +#define SPR_WATCH_MASK 0x200a +#define SPR_WATCH_VAL 0x200b + +#endif /* !defined(__ARCH_SPR_DEF_64_H__) */ + +#endif /* !defined(__DOXYGEN__) */ diff --git a/qemu/target-tilegx/translate.c b/qemu/target-tilegx/translate.c new file mode 100644 index 000000000..03918ebd5 --- /dev/null +++ b/qemu/target-tilegx/translate.c @@ -0,0 +1,2451 @@ +/* + * QEMU TILE-Gx CPU + * + * Copyright (c) 2015 Chen Gang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "qemu/log.h" +#include "exec/log.h" +#include "disas/disas.h" +#include "tcg-op.h" +#include "exec/cpu_ldst.h" +#include "linux-user/syscall_defs.h" + +#include "opcode_tilegx.h" +#include "spr_def_64.h" + +#define FMT64X "%016" PRIx64 + +static TCGv_env cpu_env; +static TCGv cpu_pc; +static TCGv cpu_regs[TILEGX_R_COUNT]; + +static const char * const reg_names[64] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", + "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39", + "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47", + "r48", "r49", "r50", "r51", "bp", "tp", "sp", "lr", + "sn", "idn0", "idn1", "udn0", "udn1", "udn2", "udn2", "zero" +}; + +/* Modified registers are cached in temporaries until the end of the bundle. */ +typedef struct { + unsigned reg; + TCGv val; +} DisasContextTemp; + +#define MAX_WRITEBACK 4 + +/* This is the state at translation time. */ +typedef struct { + uint64_t pc; /* Current pc */ + + TCGv zero; /* For zero register */ + + DisasContextTemp wb[MAX_WRITEBACK]; + int num_wb; + int mmuidx; + bool exit_tb; + TileExcp atomic_excp; + + struct { + TCGCond cond; /* branch condition */ + TCGv dest; /* branch destination */ + TCGv val1; /* value to be compared against zero, for cond */ + } jmp; /* Jump object, only once in each TB block */ +} DisasContext; + +#include "exec/gen-icount.h" + +/* Differentiate the various pipe encodings. */ +#define TY_X0 0 +#define TY_X1 1 +#define TY_Y0 2 +#define TY_Y1 3 + +/* Remerge the base opcode and extension fields for switching. + The X opcode fields are 3 bits; Y0/Y1 opcode fields are 4 bits; + Y2 opcode field is 2 bits. */ +#define OE(OP, EXT, XY) (TY_##XY + OP * 4 + EXT * 64) + +/* Similar, but for Y2 only. */ +#define OEY2(OP, MODE) (OP + MODE * 4) + +/* Similar, but make sure opcode names match up. */ +#define OE_RR_X0(E) OE(RRR_0_OPCODE_X0, E##_UNARY_OPCODE_X0, X0) +#define OE_RR_X1(E) OE(RRR_0_OPCODE_X1, E##_UNARY_OPCODE_X1, X1) +#define OE_RR_Y0(E) OE(RRR_1_OPCODE_Y0, E##_UNARY_OPCODE_Y0, Y0) +#define OE_RR_Y1(E) OE(RRR_1_OPCODE_Y1, E##_UNARY_OPCODE_Y1, Y1) +#define OE_RRR(E,N,XY) OE(RRR_##N##_OPCODE_##XY, E##_RRR_##N##_OPCODE_##XY, XY) +#define OE_IM(E,XY) OE(IMM8_OPCODE_##XY, E##_IMM8_OPCODE_##XY, XY) +#define OE_SH(E,XY) OE(SHIFT_OPCODE_##XY, E##_SHIFT_OPCODE_##XY, XY) + +#define V1_IMM(X) (((X) & 0xff) * 0x0101010101010101ull) +#define V2_IMM(X) (((X) & 0xffff) * 0x0001000100010001ull) + + +static void gen_exception(DisasContext *dc, TileExcp num) +{ + TCGv_i32 tmp; + + tcg_gen_movi_tl(cpu_pc, dc->pc + TILEGX_BUNDLE_SIZE_IN_BYTES); + + tmp = tcg_const_i32(num); + gen_helper_exception(cpu_env, tmp); + tcg_temp_free_i32(tmp); + dc->exit_tb = true; +} + +static bool check_gr(DisasContext *dc, uint8_t reg) +{ + if (likely(reg < TILEGX_R_COUNT)) { + return true; + } + + switch (reg) { + case TILEGX_R_SN: + case TILEGX_R_ZERO: + break; + case TILEGX_R_IDN0: + case TILEGX_R_IDN1: + gen_exception(dc, TILEGX_EXCP_REG_IDN_ACCESS); + break; + case TILEGX_R_UDN0: + case TILEGX_R_UDN1: + case TILEGX_R_UDN2: + case TILEGX_R_UDN3: + gen_exception(dc, TILEGX_EXCP_REG_UDN_ACCESS); + break; + default: + g_assert_not_reached(); + } + return false; +} + +static TCGv load_zero(DisasContext *dc) +{ + if (TCGV_IS_UNUSED_I64(dc->zero)) { + dc->zero = tcg_const_i64(0); + } + return dc->zero; +} + +static TCGv load_gr(DisasContext *dc, unsigned reg) +{ + if (check_gr(dc, reg)) { + return cpu_regs[reg]; + } + return load_zero(dc); +} + +static TCGv dest_gr(DisasContext *dc, unsigned reg) +{ + int n; + + /* Skip the result, mark the exception if necessary, and continue */ + check_gr(dc, reg); + + n = dc->num_wb++; + dc->wb[n].reg = reg; + return dc->wb[n].val = tcg_temp_new_i64(); +} + +static void gen_saturate_op(TCGv tdest, TCGv tsrca, TCGv tsrcb, + void (*operate)(TCGv, TCGv, TCGv)) +{ + TCGv t0 = tcg_temp_new(); + + tcg_gen_ext32s_tl(tdest, tsrca); + tcg_gen_ext32s_tl(t0, tsrcb); + operate(tdest, tdest, t0); + + tcg_gen_movi_tl(t0, 0x7fffffff); + tcg_gen_movcond_tl(TCG_COND_GT, tdest, tdest, t0, t0, tdest); + tcg_gen_movi_tl(t0, -0x80000000LL); + tcg_gen_movcond_tl(TCG_COND_LT, tdest, tdest, t0, t0, tdest); + + tcg_temp_free(t0); +} + +static void gen_atomic_excp(DisasContext *dc, unsigned dest, TCGv tdest, + TCGv tsrca, TCGv tsrcb, TileExcp excp) +{ +#ifdef CONFIG_USER_ONLY + TCGv_i32 t; + + tcg_gen_st_tl(tsrca, cpu_env, offsetof(CPUTLGState, atomic_srca)); + tcg_gen_st_tl(tsrcb, cpu_env, offsetof(CPUTLGState, atomic_srcb)); + t = tcg_const_i32(dest); + tcg_gen_st_i32(t, cpu_env, offsetof(CPUTLGState, atomic_dstr)); + tcg_temp_free_i32(t); + + /* We're going to write the real result in the exception. But in + the meantime we've already created a writeback register, and + we don't want that to remain uninitialized. */ + tcg_gen_movi_tl(tdest, 0); + + /* Note that we need to delay issuing the exception that implements + the atomic operation until after writing back the results of the + instruction occupying the X0 pipe. */ + dc->atomic_excp = excp; +#else + gen_exception(dc, TILEGX_EXCP_OPCODE_UNIMPLEMENTED); +#endif +} + +/* Shift the 128-bit value TSRCA:TSRCD right by the number of bytes + specified by the bottom 3 bits of TSRCB, and set TDEST to the + low 64 bits of the resulting value. */ +static void gen_dblalign(TCGv tdest, TCGv tsrcd, TCGv tsrca, TCGv tsrcb) +{ + TCGv t0 = tcg_temp_new(); + + tcg_gen_andi_tl(t0, tsrcb, 7); + tcg_gen_shli_tl(t0, t0, 3); + tcg_gen_shr_tl(tdest, tsrcd, t0); + + /* We want to do "t0 = tsrca << (64 - t0)". Two's complement + arithmetic on a 6-bit field tells us that 64 - t0 is equal + to (t0 ^ 63) + 1. So we can do the shift in two parts, + neither of which will be an invalid shift by 64. */ + tcg_gen_xori_tl(t0, t0, 63); + tcg_gen_shl_tl(t0, tsrca, t0); + tcg_gen_shli_tl(t0, t0, 1); + tcg_gen_or_tl(tdest, tdest, t0); + + tcg_temp_free(t0); +} + +/* Similarly, except that the 128-bit value is TSRCA:TSRCB, and the + right shift is an immediate. */ +static void gen_dblaligni(TCGv tdest, TCGv tsrca, TCGv tsrcb, int shr) +{ + TCGv t0 = tcg_temp_new(); + + tcg_gen_shri_tl(t0, tsrcb, shr); + tcg_gen_shli_tl(tdest, tsrca, 64 - shr); + tcg_gen_or_tl(tdest, tdest, t0); + + tcg_temp_free(t0); +} + +typedef enum { + LU, LS, HU, HS +} MulHalf; + +static void gen_ext_half(TCGv d, TCGv s, MulHalf h) +{ + switch (h) { + case LU: + tcg_gen_ext32u_tl(d, s); + break; + case LS: + tcg_gen_ext32s_tl(d, s); + break; + case HU: + tcg_gen_shri_tl(d, s, 32); + break; + case HS: + tcg_gen_sari_tl(d, s, 32); + break; + } +} + +static void gen_mul_half(TCGv tdest, TCGv tsrca, TCGv tsrcb, + MulHalf ha, MulHalf hb) +{ + TCGv t = tcg_temp_new(); + gen_ext_half(t, tsrca, ha); + gen_ext_half(tdest, tsrcb, hb); + tcg_gen_mul_tl(tdest, tdest, t); + tcg_temp_free(t); +} + +static void gen_cmul2(TCGv tdest, TCGv tsrca, TCGv tsrcb, int sh, int rd) +{ + TCGv_i32 tsh = tcg_const_i32(sh); + TCGv_i32 trd = tcg_const_i32(rd); + gen_helper_cmul2(tdest, tsrca, tsrcb, tsh, trd); + tcg_temp_free_i32(tsh); + tcg_temp_free_i32(trd); +} + +static TileExcp gen_st_opcode(DisasContext *dc, unsigned dest, unsigned srca, + unsigned srcb, TCGMemOp memop, const char *name) +{ + if (dest) { + return TILEGX_EXCP_OPCODE_UNKNOWN; + } + + tcg_gen_qemu_st_tl(load_gr(dc, srcb), load_gr(dc, srca), + dc->mmuidx, memop); + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s %s, %s", name, + reg_names[srca], reg_names[srcb]); + return TILEGX_EXCP_NONE; +} + +static TileExcp gen_st_add_opcode(DisasContext *dc, unsigned srca, unsigned srcb, + int imm, TCGMemOp memop, const char *name) +{ + TCGv tsrca = load_gr(dc, srca); + TCGv tsrcb = load_gr(dc, srcb); + + tcg_gen_qemu_st_tl(tsrcb, tsrca, dc->mmuidx, memop); + tcg_gen_addi_tl(dest_gr(dc, srca), tsrca, imm); + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s %s, %s, %d", name, + reg_names[srca], reg_names[srcb], imm); + return TILEGX_EXCP_NONE; +} + +/* Equality comparison with zero can be done quickly and efficiently. */ +static void gen_v1cmpeq0(TCGv v) +{ + TCGv m = tcg_const_tl(V1_IMM(0x7f)); + TCGv c = tcg_temp_new(); + + /* ~(((v & m) + m) | m | v). Sets the msb for each byte == 0. */ + tcg_gen_and_tl(c, v, m); + tcg_gen_add_tl(c, c, m); + tcg_gen_or_tl(c, c, m); + tcg_gen_nor_tl(c, c, v); + tcg_temp_free(m); + + /* Shift the msb down to form the lsb boolean result. */ + tcg_gen_shri_tl(v, c, 7); + tcg_temp_free(c); +} + +static void gen_v1cmpne0(TCGv v) +{ + TCGv m = tcg_const_tl(V1_IMM(0x7f)); + TCGv c = tcg_temp_new(); + + /* (((v & m) + m) | v) & ~m. Sets the msb for each byte != 0. */ + tcg_gen_and_tl(c, v, m); + tcg_gen_add_tl(c, c, m); + tcg_gen_or_tl(c, c, v); + tcg_gen_andc_tl(c, c, m); + tcg_temp_free(m); + + /* Shift the msb down to form the lsb boolean result. */ + tcg_gen_shri_tl(v, c, 7); + tcg_temp_free(c); +} + +/* Vector addition can be performed via arithmetic plus masking. It is + efficient this way only for 4 or more elements. */ +static void gen_v12add(TCGv tdest, TCGv tsrca, TCGv tsrcb, uint64_t sign) +{ + TCGv tmask = tcg_const_tl(~sign); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + /* ((a & ~sign) + (b & ~sign)) ^ ((a ^ b) & sign). */ + tcg_gen_and_tl(t0, tsrca, tmask); + tcg_gen_and_tl(t1, tsrcb, tmask); + tcg_gen_add_tl(tdest, t0, t1); + tcg_gen_xor_tl(t0, tsrca, tsrcb); + tcg_gen_andc_tl(t0, t0, tmask); + tcg_gen_xor_tl(tdest, tdest, t0); + + tcg_temp_free(t1); + tcg_temp_free(t0); + tcg_temp_free(tmask); +} + +/* Similarly for vector subtraction. */ +static void gen_v12sub(TCGv tdest, TCGv tsrca, TCGv tsrcb, uint64_t sign) +{ + TCGv tsign = tcg_const_tl(sign); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + /* ((a | sign) - (b & ~sign)) ^ ((a ^ ~b) & sign). */ + tcg_gen_or_tl(t0, tsrca, tsign); + tcg_gen_andc_tl(t1, tsrcb, tsign); + tcg_gen_sub_tl(tdest, t0, t1); + tcg_gen_eqv_tl(t0, tsrca, tsrcb); + tcg_gen_and_tl(t0, t0, tsign); + tcg_gen_xor_tl(tdest, tdest, t0); + + tcg_temp_free(t1); + tcg_temp_free(t0); + tcg_temp_free(tsign); +} + +static void gen_v4sh(TCGv d64, TCGv a64, TCGv b64, + void (*generate)(TCGv_i32, TCGv_i32, TCGv_i32)) +{ + TCGv_i32 al = tcg_temp_new_i32(); + TCGv_i32 ah = tcg_temp_new_i32(); + TCGv_i32 bl = tcg_temp_new_i32(); + + tcg_gen_extr_i64_i32(al, ah, a64); + tcg_gen_extrl_i64_i32(bl, b64); + tcg_gen_andi_i32(bl, bl, 31); + generate(al, al, bl); + generate(ah, ah, bl); + tcg_gen_concat_i32_i64(d64, al, ah); + + tcg_temp_free_i32(al); + tcg_temp_free_i32(ah); + tcg_temp_free_i32(bl); +} + +static void gen_v4op(TCGv d64, TCGv a64, TCGv b64, + void (*generate)(TCGv_i32, TCGv_i32, TCGv_i32)) +{ + TCGv_i32 al = tcg_temp_new_i32(); + TCGv_i32 ah = tcg_temp_new_i32(); + TCGv_i32 bl = tcg_temp_new_i32(); + TCGv_i32 bh = tcg_temp_new_i32(); + + tcg_gen_extr_i64_i32(al, ah, a64); + tcg_gen_extr_i64_i32(bl, bh, b64); + generate(al, al, bl); + generate(ah, ah, bh); + tcg_gen_concat_i32_i64(d64, al, ah); + + tcg_temp_free_i32(al); + tcg_temp_free_i32(ah); + tcg_temp_free_i32(bl); + tcg_temp_free_i32(bh); +} + +static TileExcp gen_signal(DisasContext *dc, int signo, int sigcode, + const char *mnemonic) +{ + TCGv_i32 t0 = tcg_const_i32(signo); + TCGv_i32 t1 = tcg_const_i32(sigcode); + + tcg_gen_st_i32(t0, cpu_env, offsetof(CPUTLGState, signo)); + tcg_gen_st_i32(t1, cpu_env, offsetof(CPUTLGState, sigcode)); + + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s", mnemonic); + return TILEGX_EXCP_SIGNAL; +} + +static bool parse_from_addli(uint64_t bundle, int *signo, int *sigcode) +{ + int imm; + + if ((get_Opcode_X0(bundle) != ADDLI_OPCODE_X0) + || (get_Dest_X0(bundle) != TILEGX_R_ZERO) + || (get_SrcA_X0(bundle) != TILEGX_R_ZERO)) { + return false; + } + + imm = get_Imm16_X0(bundle); + *signo = imm & 0x3f; + *sigcode = (imm >> 6) & 0xf; + + /* ??? The linux kernel validates both signo and the sigcode vs the + known max for each signal. Don't bother here. */ + return true; +} + +static TileExcp gen_specill(DisasContext *dc, unsigned dest, unsigned srca, + uint64_t bundle) +{ + const char *mnemonic; + int signo; + int sigcode; + + if (dest == 0x1c && srca == 0x25) { + signo = TARGET_SIGTRAP; + sigcode = TARGET_TRAP_BRKPT; + mnemonic = "bpt"; + } else if (dest == 0x1d && srca == 0x25 + && parse_from_addli(bundle, &signo, &sigcode)) { + mnemonic = "raise"; + } else { + signo = TARGET_SIGILL; + sigcode = TARGET_ILL_ILLOPC; + mnemonic = "ill"; + } + + return gen_signal(dc, signo, sigcode, mnemonic); +} + +static TileExcp gen_rr_opcode(DisasContext *dc, unsigned opext, + unsigned dest, unsigned srca, uint64_t bundle) +{ + TCGv tdest, tsrca; + const char *mnemonic; + TCGMemOp memop; + TileExcp ret = TILEGX_EXCP_NONE; + bool prefetch_nofault = false; + + /* Eliminate instructions with no output before doing anything else. */ + switch (opext) { + case OE_RR_Y0(NOP): + case OE_RR_Y1(NOP): + case OE_RR_X0(NOP): + case OE_RR_X1(NOP): + mnemonic = "nop"; + goto done0; + case OE_RR_Y0(FNOP): + case OE_RR_Y1(FNOP): + case OE_RR_X0(FNOP): + case OE_RR_X1(FNOP): + mnemonic = "fnop"; + goto done0; + case OE_RR_X1(DRAIN): + mnemonic = "drain"; + goto done0; + case OE_RR_X1(FLUSHWB): + mnemonic = "flushwb"; + goto done0; + case OE_RR_X1(ILL): + return gen_specill(dc, dest, srca, bundle); + case OE_RR_Y1(ILL): + return gen_signal(dc, TARGET_SIGILL, TARGET_ILL_ILLOPC, "ill"); + case OE_RR_X1(MF): + mnemonic = "mf"; + goto done0; + case OE_RR_X1(NAP): + /* ??? This should yield, especially in system mode. */ + mnemonic = "nap"; + goto done0; + case OE_RR_X1(IRET): + gen_helper_ext01_ics(cpu_env); + dc->jmp.cond = TCG_COND_ALWAYS; + dc->jmp.dest = tcg_temp_new(); + tcg_gen_ld_tl(dc->jmp.dest, cpu_env, + offsetof(CPUTLGState, spregs[TILEGX_SPR_EX_CONTEXT_0_0])); + tcg_gen_andi_tl(dc->jmp.dest, dc->jmp.dest, ~7); + mnemonic = "iret"; + goto done0; + case OE_RR_X1(SWINT0): + case OE_RR_X1(SWINT2): + case OE_RR_X1(SWINT3): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RR_X1(SWINT1): + ret = TILEGX_EXCP_SYSCALL; + mnemonic = "swint1"; + done0: + if (srca || dest) { + return TILEGX_EXCP_OPCODE_UNKNOWN; + } + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s", mnemonic); + return ret; + + case OE_RR_X1(DTLBPR): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RR_X1(FINV): + mnemonic = "finv"; + goto done1; + case OE_RR_X1(FLUSH): + mnemonic = "flush"; + goto done1; + case OE_RR_X1(ICOH): + mnemonic = "icoh"; + goto done1; + case OE_RR_X1(INV): + mnemonic = "inv"; + goto done1; + case OE_RR_X1(WH64): + mnemonic = "wh64"; + goto done1; + case OE_RR_X1(JRP): + case OE_RR_Y1(JRP): + mnemonic = "jrp"; + goto do_jr; + case OE_RR_X1(JR): + case OE_RR_Y1(JR): + mnemonic = "jr"; + goto do_jr; + case OE_RR_X1(JALRP): + case OE_RR_Y1(JALRP): + mnemonic = "jalrp"; + goto do_jalr; + case OE_RR_X1(JALR): + case OE_RR_Y1(JALR): + mnemonic = "jalr"; + do_jalr: + tcg_gen_movi_tl(dest_gr(dc, TILEGX_R_LR), + dc->pc + TILEGX_BUNDLE_SIZE_IN_BYTES); + do_jr: + dc->jmp.cond = TCG_COND_ALWAYS; + dc->jmp.dest = tcg_temp_new(); + tcg_gen_andi_tl(dc->jmp.dest, load_gr(dc, srca), ~7); + done1: + if (dest) { + return TILEGX_EXCP_OPCODE_UNKNOWN; + } + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s %s", mnemonic, reg_names[srca]); + return ret; + } + + tdest = dest_gr(dc, dest); + tsrca = load_gr(dc, srca); + + switch (opext) { + case OE_RR_X0(CNTLZ): + case OE_RR_Y0(CNTLZ): + gen_helper_cntlz(tdest, tsrca); + mnemonic = "cntlz"; + break; + case OE_RR_X0(CNTTZ): + case OE_RR_Y0(CNTTZ): + gen_helper_cnttz(tdest, tsrca); + mnemonic = "cnttz"; + break; + case OE_RR_X0(FSINGLE_PACK1): + case OE_RR_Y0(FSINGLE_PACK1): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RR_X1(LD1S): + memop = MO_SB; + mnemonic = "ld1s"; /* prefetch_l1_fault */ + goto do_load; + case OE_RR_X1(LD1U): + memop = MO_UB; + mnemonic = "ld1u"; /* prefetch, prefetch_l1 */ + prefetch_nofault = (dest == TILEGX_R_ZERO); + goto do_load; + case OE_RR_X1(LD2S): + memop = MO_TESW; + mnemonic = "ld2s"; /* prefetch_l2_fault */ + goto do_load; + case OE_RR_X1(LD2U): + memop = MO_TEUW; + mnemonic = "ld2u"; /* prefetch_l2 */ + prefetch_nofault = (dest == TILEGX_R_ZERO); + goto do_load; + case OE_RR_X1(LD4S): + memop = MO_TESL; + mnemonic = "ld4s"; /* prefetch_l3_fault */ + goto do_load; + case OE_RR_X1(LD4U): + memop = MO_TEUL; + mnemonic = "ld4u"; /* prefetch_l3 */ + prefetch_nofault = (dest == TILEGX_R_ZERO); + goto do_load; + case OE_RR_X1(LDNT1S): + memop = MO_SB; + mnemonic = "ldnt1s"; + goto do_load; + case OE_RR_X1(LDNT1U): + memop = MO_UB; + mnemonic = "ldnt1u"; + goto do_load; + case OE_RR_X1(LDNT2S): + memop = MO_TESW; + mnemonic = "ldnt2s"; + goto do_load; + case OE_RR_X1(LDNT2U): + memop = MO_TEUW; + mnemonic = "ldnt2u"; + goto do_load; + case OE_RR_X1(LDNT4S): + memop = MO_TESL; + mnemonic = "ldnt4s"; + goto do_load; + case OE_RR_X1(LDNT4U): + memop = MO_TEUL; + mnemonic = "ldnt4u"; + goto do_load; + case OE_RR_X1(LDNT): + memop = MO_TEQ; + mnemonic = "ldnt"; + goto do_load; + case OE_RR_X1(LD): + memop = MO_TEQ; + mnemonic = "ld"; + do_load: + if (!prefetch_nofault) { + tcg_gen_qemu_ld_tl(tdest, tsrca, dc->mmuidx, memop); + } + break; + case OE_RR_X1(LDNA): + tcg_gen_andi_tl(tdest, tsrca, ~7); + tcg_gen_qemu_ld_tl(tdest, tdest, dc->mmuidx, MO_TEQ); + mnemonic = "ldna"; + break; + case OE_RR_X1(LNK): + case OE_RR_Y1(LNK): + if (srca) { + return TILEGX_EXCP_OPCODE_UNKNOWN; + } + tcg_gen_movi_tl(tdest, dc->pc + TILEGX_BUNDLE_SIZE_IN_BYTES); + mnemonic = "lnk"; + break; + case OE_RR_X0(PCNT): + case OE_RR_Y0(PCNT): + gen_helper_pcnt(tdest, tsrca); + mnemonic = "pcnt"; + break; + case OE_RR_X0(REVBITS): + case OE_RR_Y0(REVBITS): + gen_helper_revbits(tdest, tsrca); + mnemonic = "revbits"; + break; + case OE_RR_X0(REVBYTES): + case OE_RR_Y0(REVBYTES): + tcg_gen_bswap64_tl(tdest, tsrca); + mnemonic = "revbytes"; + break; + case OE_RR_X0(TBLIDXB0): + case OE_RR_Y0(TBLIDXB0): + tcg_gen_deposit_tl(tdest, load_gr(dc, dest), tsrca, 2, 8); + mnemonic = "tblidxb0"; + break; + case OE_RR_X0(TBLIDXB1): + case OE_RR_Y0(TBLIDXB1): + tcg_gen_shri_tl(tdest, tsrca, 8); + tcg_gen_deposit_tl(tdest, load_gr(dc, dest), tdest, 2, 8); + mnemonic = "tblidxb1"; + break; + case OE_RR_X0(TBLIDXB2): + case OE_RR_Y0(TBLIDXB2): + tcg_gen_shri_tl(tdest, tsrca, 16); + tcg_gen_deposit_tl(tdest, load_gr(dc, dest), tdest, 2, 8); + mnemonic = "tblidxb2"; + break; + case OE_RR_X0(TBLIDXB3): + case OE_RR_Y0(TBLIDXB3): + tcg_gen_shri_tl(tdest, tsrca, 24); + tcg_gen_deposit_tl(tdest, load_gr(dc, dest), tdest, 2, 8); + mnemonic = "tblidxb3"; + break; + default: + return TILEGX_EXCP_OPCODE_UNKNOWN; + } + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s %s, %s", mnemonic, + reg_names[dest], reg_names[srca]); + return ret; +} + +static TileExcp gen_rrr_opcode(DisasContext *dc, unsigned opext, + unsigned dest, unsigned srca, unsigned srcb) +{ + TCGv tdest = dest_gr(dc, dest); + TCGv tsrca = load_gr(dc, srca); + TCGv tsrcb = load_gr(dc, srcb); + TCGv t0; + const char *mnemonic; + + switch (opext) { + case OE_RRR(ADDXSC, 0, X0): + case OE_RRR(ADDXSC, 0, X1): + gen_saturate_op(tdest, tsrca, tsrcb, tcg_gen_add_tl); + mnemonic = "addxsc"; + break; + case OE_RRR(ADDX, 0, X0): + case OE_RRR(ADDX, 0, X1): + case OE_RRR(ADDX, 0, Y0): + case OE_RRR(ADDX, 0, Y1): + tcg_gen_add_tl(tdest, tsrca, tsrcb); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "addx"; + break; + case OE_RRR(ADD, 0, X0): + case OE_RRR(ADD, 0, X1): + case OE_RRR(ADD, 0, Y0): + case OE_RRR(ADD, 0, Y1): + tcg_gen_add_tl(tdest, tsrca, tsrcb); + mnemonic = "add"; + break; + case OE_RRR(AND, 0, X0): + case OE_RRR(AND, 0, X1): + case OE_RRR(AND, 5, Y0): + case OE_RRR(AND, 5, Y1): + tcg_gen_and_tl(tdest, tsrca, tsrcb); + mnemonic = "and"; + break; + case OE_RRR(CMOVEQZ, 0, X0): + case OE_RRR(CMOVEQZ, 4, Y0): + tcg_gen_movcond_tl(TCG_COND_EQ, tdest, tsrca, load_zero(dc), + tsrcb, load_gr(dc, dest)); + mnemonic = "cmoveqz"; + break; + case OE_RRR(CMOVNEZ, 0, X0): + case OE_RRR(CMOVNEZ, 4, Y0): + tcg_gen_movcond_tl(TCG_COND_NE, tdest, tsrca, load_zero(dc), + tsrcb, load_gr(dc, dest)); + mnemonic = "cmovnez"; + break; + case OE_RRR(CMPEQ, 0, X0): + case OE_RRR(CMPEQ, 0, X1): + case OE_RRR(CMPEQ, 3, Y0): + case OE_RRR(CMPEQ, 3, Y1): + tcg_gen_setcond_tl(TCG_COND_EQ, tdest, tsrca, tsrcb); + mnemonic = "cmpeq"; + break; + case OE_RRR(CMPEXCH4, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_CMPEXCH4); + mnemonic = "cmpexch4"; + break; + case OE_RRR(CMPEXCH, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_CMPEXCH); + mnemonic = "cmpexch"; + break; + case OE_RRR(CMPLES, 0, X0): + case OE_RRR(CMPLES, 0, X1): + case OE_RRR(CMPLES, 2, Y0): + case OE_RRR(CMPLES, 2, Y1): + tcg_gen_setcond_tl(TCG_COND_LE, tdest, tsrca, tsrcb); + mnemonic = "cmples"; + break; + case OE_RRR(CMPLEU, 0, X0): + case OE_RRR(CMPLEU, 0, X1): + case OE_RRR(CMPLEU, 2, Y0): + case OE_RRR(CMPLEU, 2, Y1): + tcg_gen_setcond_tl(TCG_COND_LEU, tdest, tsrca, tsrcb); + mnemonic = "cmpleu"; + break; + case OE_RRR(CMPLTS, 0, X0): + case OE_RRR(CMPLTS, 0, X1): + case OE_RRR(CMPLTS, 2, Y0): + case OE_RRR(CMPLTS, 2, Y1): + tcg_gen_setcond_tl(TCG_COND_LT, tdest, tsrca, tsrcb); + mnemonic = "cmplts"; + break; + case OE_RRR(CMPLTU, 0, X0): + case OE_RRR(CMPLTU, 0, X1): + case OE_RRR(CMPLTU, 2, Y0): + case OE_RRR(CMPLTU, 2, Y1): + tcg_gen_setcond_tl(TCG_COND_LTU, tdest, tsrca, tsrcb); + mnemonic = "cmpltu"; + break; + case OE_RRR(CMPNE, 0, X0): + case OE_RRR(CMPNE, 0, X1): + case OE_RRR(CMPNE, 3, Y0): + case OE_RRR(CMPNE, 3, Y1): + tcg_gen_setcond_tl(TCG_COND_NE, tdest, tsrca, tsrcb); + mnemonic = "cmpne"; + break; + case OE_RRR(CMULAF, 0, X0): + gen_helper_cmulaf(tdest, load_gr(dc, dest), tsrca, tsrcb); + mnemonic = "cmulaf"; + break; + case OE_RRR(CMULA, 0, X0): + gen_helper_cmula(tdest, load_gr(dc, dest), tsrca, tsrcb); + mnemonic = "cmula"; + break; + case OE_RRR(CMULFR, 0, X0): + gen_cmul2(tdest, tsrca, tsrcb, 15, 1 << 14); + mnemonic = "cmulfr"; + break; + case OE_RRR(CMULF, 0, X0): + gen_cmul2(tdest, tsrca, tsrcb, 15, 0); + mnemonic = "cmulf"; + break; + case OE_RRR(CMULHR, 0, X0): + gen_cmul2(tdest, tsrca, tsrcb, 16, 1 << 15); + mnemonic = "cmulhr"; + break; + case OE_RRR(CMULH, 0, X0): + gen_cmul2(tdest, tsrca, tsrcb, 16, 0); + mnemonic = "cmulh"; + break; + case OE_RRR(CMUL, 0, X0): + gen_helper_cmula(tdest, load_zero(dc), tsrca, tsrcb); + mnemonic = "cmul"; + break; + case OE_RRR(CRC32_32, 0, X0): + gen_helper_crc32_32(tdest, tsrca, tsrcb); + mnemonic = "crc32_32"; + break; + case OE_RRR(CRC32_8, 0, X0): + gen_helper_crc32_8(tdest, tsrca, tsrcb); + mnemonic = "crc32_8"; + break; + case OE_RRR(DBLALIGN2, 0, X0): + case OE_RRR(DBLALIGN2, 0, X1): + gen_dblaligni(tdest, tsrca, tsrcb, 16); + mnemonic = "dblalign2"; + break; + case OE_RRR(DBLALIGN4, 0, X0): + case OE_RRR(DBLALIGN4, 0, X1): + gen_dblaligni(tdest, tsrca, tsrcb, 32); + mnemonic = "dblalign4"; + break; + case OE_RRR(DBLALIGN6, 0, X0): + case OE_RRR(DBLALIGN6, 0, X1): + gen_dblaligni(tdest, tsrca, tsrcb, 48); + mnemonic = "dblalign6"; + break; + case OE_RRR(DBLALIGN, 0, X0): + gen_dblalign(tdest, load_gr(dc, dest), tsrca, tsrcb); + mnemonic = "dblalign"; + break; + case OE_RRR(EXCH4, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_EXCH4); + mnemonic = "exch4"; + break; + case OE_RRR(EXCH, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_EXCH); + mnemonic = "exch"; + break; + case OE_RRR(FDOUBLE_ADDSUB, 0, X0): + case OE_RRR(FDOUBLE_ADD_FLAGS, 0, X0): + case OE_RRR(FDOUBLE_MUL_FLAGS, 0, X0): + case OE_RRR(FDOUBLE_PACK1, 0, X0): + case OE_RRR(FDOUBLE_PACK2, 0, X0): + case OE_RRR(FDOUBLE_SUB_FLAGS, 0, X0): + case OE_RRR(FDOUBLE_UNPACK_MAX, 0, X0): + case OE_RRR(FDOUBLE_UNPACK_MIN, 0, X0): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(FETCHADD4, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_FETCHADD4); + mnemonic = "fetchadd4"; + break; + case OE_RRR(FETCHADDGEZ4, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_FETCHADDGEZ4); + mnemonic = "fetchaddgez4"; + break; + case OE_RRR(FETCHADDGEZ, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_FETCHADDGEZ); + mnemonic = "fetchaddgez"; + break; + case OE_RRR(FETCHADD, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_FETCHADD); + mnemonic = "fetchadd"; + break; + case OE_RRR(FETCHAND4, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_FETCHAND4); + mnemonic = "fetchand4"; + break; + case OE_RRR(FETCHAND, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_FETCHAND); + mnemonic = "fetchand"; + break; + case OE_RRR(FETCHOR4, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_FETCHOR4); + mnemonic = "fetchor4"; + break; + case OE_RRR(FETCHOR, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_FETCHOR); + mnemonic = "fetchor"; + break; + case OE_RRR(FSINGLE_ADD1, 0, X0): + case OE_RRR(FSINGLE_ADDSUB2, 0, X0): + case OE_RRR(FSINGLE_MUL1, 0, X0): + case OE_RRR(FSINGLE_MUL2, 0, X0): + case OE_RRR(FSINGLE_PACK2, 0, X0): + case OE_RRR(FSINGLE_SUB1, 0, X0): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(MNZ, 0, X0): + case OE_RRR(MNZ, 0, X1): + case OE_RRR(MNZ, 4, Y0): + case OE_RRR(MNZ, 4, Y1): + t0 = load_zero(dc); + tcg_gen_movcond_tl(TCG_COND_NE, tdest, tsrca, t0, tsrcb, t0); + mnemonic = "mnz"; + break; + case OE_RRR(MULAX, 0, X0): + case OE_RRR(MULAX, 3, Y0): + tcg_gen_mul_tl(tdest, tsrca, tsrcb); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "mulax"; + break; + case OE_RRR(MULA_HS_HS, 0, X0): + case OE_RRR(MULA_HS_HS, 9, Y0): + gen_mul_half(tdest, tsrca, tsrcb, HS, HS); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_hs_hs"; + break; + case OE_RRR(MULA_HS_HU, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HS, HU); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_hs_hu"; + break; + case OE_RRR(MULA_HS_LS, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HS, LS); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_hs_ls"; + break; + case OE_RRR(MULA_HS_LU, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HS, LU); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_hs_lu"; + break; + case OE_RRR(MULA_HU_HU, 0, X0): + case OE_RRR(MULA_HU_HU, 9, Y0): + gen_mul_half(tdest, tsrca, tsrcb, HU, HU); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_hu_hu"; + break; + case OE_RRR(MULA_HU_LS, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HU, LS); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_hu_ls"; + break; + case OE_RRR(MULA_HU_LU, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HU, LU); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_hu_lu"; + break; + case OE_RRR(MULA_LS_LS, 0, X0): + case OE_RRR(MULA_LS_LS, 9, Y0): + gen_mul_half(tdest, tsrca, tsrcb, LS, LS); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_ls_ls"; + break; + case OE_RRR(MULA_LS_LU, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, LS, LU); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_ls_lu"; + break; + case OE_RRR(MULA_LU_LU, 0, X0): + case OE_RRR(MULA_LU_LU, 9, Y0): + gen_mul_half(tdest, tsrca, tsrcb, LU, LU); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_lu_lu"; + break; + case OE_RRR(MULX, 0, X0): + case OE_RRR(MULX, 3, Y0): + tcg_gen_mul_tl(tdest, tsrca, tsrcb); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "mulx"; + break; + case OE_RRR(MUL_HS_HS, 0, X0): + case OE_RRR(MUL_HS_HS, 8, Y0): + gen_mul_half(tdest, tsrca, tsrcb, HS, HS); + mnemonic = "mul_hs_hs"; + break; + case OE_RRR(MUL_HS_HU, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HS, HU); + mnemonic = "mul_hs_hu"; + break; + case OE_RRR(MUL_HS_LS, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HS, LS); + mnemonic = "mul_hs_ls"; + break; + case OE_RRR(MUL_HS_LU, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HS, LU); + mnemonic = "mul_hs_lu"; + break; + case OE_RRR(MUL_HU_HU, 0, X0): + case OE_RRR(MUL_HU_HU, 8, Y0): + gen_mul_half(tdest, tsrca, tsrcb, HU, HU); + mnemonic = "mul_hu_hu"; + break; + case OE_RRR(MUL_HU_LS, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HU, LS); + mnemonic = "mul_hu_ls"; + break; + case OE_RRR(MUL_HU_LU, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HU, LU); + mnemonic = "mul_hu_lu"; + break; + case OE_RRR(MUL_LS_LS, 0, X0): + case OE_RRR(MUL_LS_LS, 8, Y0): + gen_mul_half(tdest, tsrca, tsrcb, LS, LS); + mnemonic = "mul_ls_ls"; + break; + case OE_RRR(MUL_LS_LU, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, LS, LU); + mnemonic = "mul_ls_lu"; + break; + case OE_RRR(MUL_LU_LU, 0, X0): + case OE_RRR(MUL_LU_LU, 8, Y0): + gen_mul_half(tdest, tsrca, tsrcb, LU, LU); + mnemonic = "mul_lu_lu"; + break; + case OE_RRR(MZ, 0, X0): + case OE_RRR(MZ, 0, X1): + case OE_RRR(MZ, 4, Y0): + case OE_RRR(MZ, 4, Y1): + t0 = load_zero(dc); + tcg_gen_movcond_tl(TCG_COND_EQ, tdest, tsrca, t0, tsrcb, t0); + mnemonic = "mz"; + break; + case OE_RRR(NOR, 0, X0): + case OE_RRR(NOR, 0, X1): + case OE_RRR(NOR, 5, Y0): + case OE_RRR(NOR, 5, Y1): + tcg_gen_nor_tl(tdest, tsrca, tsrcb); + mnemonic = "nor"; + break; + case OE_RRR(OR, 0, X0): + case OE_RRR(OR, 0, X1): + case OE_RRR(OR, 5, Y0): + case OE_RRR(OR, 5, Y1): + tcg_gen_or_tl(tdest, tsrca, tsrcb); + mnemonic = "or"; + break; + case OE_RRR(ROTL, 0, X0): + case OE_RRR(ROTL, 0, X1): + case OE_RRR(ROTL, 6, Y0): + case OE_RRR(ROTL, 6, Y1): + tcg_gen_andi_tl(tdest, tsrcb, 63); + tcg_gen_rotl_tl(tdest, tsrca, tdest); + mnemonic = "rotl"; + break; + case OE_RRR(SHL1ADDX, 0, X0): + case OE_RRR(SHL1ADDX, 0, X1): + case OE_RRR(SHL1ADDX, 7, Y0): + case OE_RRR(SHL1ADDX, 7, Y1): + tcg_gen_shli_tl(tdest, tsrca, 1); + tcg_gen_add_tl(tdest, tdest, tsrcb); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "shl1addx"; + break; + case OE_RRR(SHL1ADD, 0, X0): + case OE_RRR(SHL1ADD, 0, X1): + case OE_RRR(SHL1ADD, 1, Y0): + case OE_RRR(SHL1ADD, 1, Y1): + tcg_gen_shli_tl(tdest, tsrca, 1); + tcg_gen_add_tl(tdest, tdest, tsrcb); + mnemonic = "shl1add"; + break; + case OE_RRR(SHL2ADDX, 0, X0): + case OE_RRR(SHL2ADDX, 0, X1): + case OE_RRR(SHL2ADDX, 7, Y0): + case OE_RRR(SHL2ADDX, 7, Y1): + tcg_gen_shli_tl(tdest, tsrca, 2); + tcg_gen_add_tl(tdest, tdest, tsrcb); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "shl2addx"; + break; + case OE_RRR(SHL2ADD, 0, X0): + case OE_RRR(SHL2ADD, 0, X1): + case OE_RRR(SHL2ADD, 1, Y0): + case OE_RRR(SHL2ADD, 1, Y1): + tcg_gen_shli_tl(tdest, tsrca, 2); + tcg_gen_add_tl(tdest, tdest, tsrcb); + mnemonic = "shl2add"; + break; + case OE_RRR(SHL3ADDX, 0, X0): + case OE_RRR(SHL3ADDX, 0, X1): + case OE_RRR(SHL3ADDX, 7, Y0): + case OE_RRR(SHL3ADDX, 7, Y1): + tcg_gen_shli_tl(tdest, tsrca, 3); + tcg_gen_add_tl(tdest, tdest, tsrcb); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "shl3addx"; + break; + case OE_RRR(SHL3ADD, 0, X0): + case OE_RRR(SHL3ADD, 0, X1): + case OE_RRR(SHL3ADD, 1, Y0): + case OE_RRR(SHL3ADD, 1, Y1): + tcg_gen_shli_tl(tdest, tsrca, 3); + tcg_gen_add_tl(tdest, tdest, tsrcb); + mnemonic = "shl3add"; + break; + case OE_RRR(SHLX, 0, X0): + case OE_RRR(SHLX, 0, X1): + tcg_gen_andi_tl(tdest, tsrcb, 31); + tcg_gen_shl_tl(tdest, tsrca, tdest); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "shlx"; + break; + case OE_RRR(SHL, 0, X0): + case OE_RRR(SHL, 0, X1): + case OE_RRR(SHL, 6, Y0): + case OE_RRR(SHL, 6, Y1): + tcg_gen_andi_tl(tdest, tsrcb, 63); + tcg_gen_shl_tl(tdest, tsrca, tdest); + mnemonic = "shl"; + break; + case OE_RRR(SHRS, 0, X0): + case OE_RRR(SHRS, 0, X1): + case OE_RRR(SHRS, 6, Y0): + case OE_RRR(SHRS, 6, Y1): + tcg_gen_andi_tl(tdest, tsrcb, 63); + tcg_gen_sar_tl(tdest, tsrca, tdest); + mnemonic = "shrs"; + break; + case OE_RRR(SHRUX, 0, X0): + case OE_RRR(SHRUX, 0, X1): + t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, tsrcb, 31); + tcg_gen_ext32u_tl(tdest, tsrca); + tcg_gen_shr_tl(tdest, tdest, t0); + tcg_gen_ext32s_tl(tdest, tdest); + tcg_temp_free(t0); + mnemonic = "shrux"; + break; + case OE_RRR(SHRU, 0, X0): + case OE_RRR(SHRU, 0, X1): + case OE_RRR(SHRU, 6, Y0): + case OE_RRR(SHRU, 6, Y1): + tcg_gen_andi_tl(tdest, tsrcb, 63); + tcg_gen_shr_tl(tdest, tsrca, tdest); + mnemonic = "shru"; + break; + case OE_RRR(SHUFFLEBYTES, 0, X0): + gen_helper_shufflebytes(tdest, load_gr(dc, dest), tsrca, tsrca); + mnemonic = "shufflebytes"; + break; + case OE_RRR(SUBXSC, 0, X0): + case OE_RRR(SUBXSC, 0, X1): + gen_saturate_op(tdest, tsrca, tsrcb, tcg_gen_sub_tl); + mnemonic = "subxsc"; + break; + case OE_RRR(SUBX, 0, X0): + case OE_RRR(SUBX, 0, X1): + case OE_RRR(SUBX, 0, Y0): + case OE_RRR(SUBX, 0, Y1): + tcg_gen_sub_tl(tdest, tsrca, tsrcb); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "subx"; + break; + case OE_RRR(SUB, 0, X0): + case OE_RRR(SUB, 0, X1): + case OE_RRR(SUB, 0, Y0): + case OE_RRR(SUB, 0, Y1): + tcg_gen_sub_tl(tdest, tsrca, tsrcb); + mnemonic = "sub"; + break; + case OE_RRR(V1ADDUC, 0, X0): + case OE_RRR(V1ADDUC, 0, X1): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(V1ADD, 0, X0): + case OE_RRR(V1ADD, 0, X1): + gen_v12add(tdest, tsrca, tsrcb, V1_IMM(0x80)); + mnemonic = "v1add"; + break; + case OE_RRR(V1ADIFFU, 0, X0): + case OE_RRR(V1AVGU, 0, X0): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(V1CMPEQ, 0, X0): + case OE_RRR(V1CMPEQ, 0, X1): + tcg_gen_xor_tl(tdest, tsrca, tsrcb); + gen_v1cmpeq0(tdest); + mnemonic = "v1cmpeq"; + break; + case OE_RRR(V1CMPLES, 0, X0): + case OE_RRR(V1CMPLES, 0, X1): + case OE_RRR(V1CMPLEU, 0, X0): + case OE_RRR(V1CMPLEU, 0, X1): + case OE_RRR(V1CMPLTS, 0, X0): + case OE_RRR(V1CMPLTS, 0, X1): + case OE_RRR(V1CMPLTU, 0, X0): + case OE_RRR(V1CMPLTU, 0, X1): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(V1CMPNE, 0, X0): + case OE_RRR(V1CMPNE, 0, X1): + tcg_gen_xor_tl(tdest, tsrca, tsrcb); + gen_v1cmpne0(tdest); + mnemonic = "v1cmpne"; + break; + case OE_RRR(V1DDOTPUA, 0, X0): + case OE_RRR(V1DDOTPUSA, 0, X0): + case OE_RRR(V1DDOTPUS, 0, X0): + case OE_RRR(V1DDOTPU, 0, X0): + case OE_RRR(V1DOTPA, 0, X0): + case OE_RRR(V1DOTPUA, 0, X0): + case OE_RRR(V1DOTPUSA, 0, X0): + case OE_RRR(V1DOTPUS, 0, X0): + case OE_RRR(V1DOTPU, 0, X0): + case OE_RRR(V1DOTP, 0, X0): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(V1INT_H, 0, X0): + case OE_RRR(V1INT_H, 0, X1): + gen_helper_v1int_h(tdest, tsrca, tsrcb); + mnemonic = "v1int_h"; + break; + case OE_RRR(V1INT_L, 0, X0): + case OE_RRR(V1INT_L, 0, X1): + gen_helper_v1int_l(tdest, tsrca, tsrcb); + mnemonic = "v1int_l"; + break; + case OE_RRR(V1MAXU, 0, X0): + case OE_RRR(V1MAXU, 0, X1): + case OE_RRR(V1MINU, 0, X0): + case OE_RRR(V1MINU, 0, X1): + case OE_RRR(V1MNZ, 0, X0): + case OE_RRR(V1MNZ, 0, X1): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(V1MULTU, 0, X0): + gen_helper_v1multu(tdest, tsrca, tsrcb); + mnemonic = "v1multu"; + break; + case OE_RRR(V1MULUS, 0, X0): + case OE_RRR(V1MULU, 0, X0): + case OE_RRR(V1MZ, 0, X0): + case OE_RRR(V1MZ, 0, X1): + case OE_RRR(V1SADAU, 0, X0): + case OE_RRR(V1SADU, 0, X0): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(V1SHL, 0, X0): + case OE_RRR(V1SHL, 0, X1): + gen_helper_v1shl(tdest, tsrca, tsrcb); + mnemonic = "v1shl"; + break; + case OE_RRR(V1SHRS, 0, X0): + case OE_RRR(V1SHRS, 0, X1): + gen_helper_v1shrs(tdest, tsrca, tsrcb); + mnemonic = "v1shrs"; + break; + case OE_RRR(V1SHRU, 0, X0): + case OE_RRR(V1SHRU, 0, X1): + gen_helper_v1shru(tdest, tsrca, tsrcb); + mnemonic = "v1shru"; + break; + case OE_RRR(V1SUBUC, 0, X0): + case OE_RRR(V1SUBUC, 0, X1): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(V1SUB, 0, X0): + case OE_RRR(V1SUB, 0, X1): + gen_v12sub(tdest, tsrca, tsrcb, V1_IMM(0x80)); + mnemonic = "v1sub"; + break; + case OE_RRR(V2ADDSC, 0, X0): + case OE_RRR(V2ADDSC, 0, X1): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(V2ADD, 0, X0): + case OE_RRR(V2ADD, 0, X1): + gen_v12add(tdest, tsrca, tsrcb, V2_IMM(0x8000)); + mnemonic = "v2add"; + break; + case OE_RRR(V2ADIFFS, 0, X0): + case OE_RRR(V2AVGS, 0, X0): + case OE_RRR(V2CMPEQ, 0, X0): + case OE_RRR(V2CMPEQ, 0, X1): + case OE_RRR(V2CMPLES, 0, X0): + case OE_RRR(V2CMPLES, 0, X1): + case OE_RRR(V2CMPLEU, 0, X0): + case OE_RRR(V2CMPLEU, 0, X1): + case OE_RRR(V2CMPLTS, 0, X0): + case OE_RRR(V2CMPLTS, 0, X1): + case OE_RRR(V2CMPLTU, 0, X0): + case OE_RRR(V2CMPLTU, 0, X1): + case OE_RRR(V2CMPNE, 0, X0): + case OE_RRR(V2CMPNE, 0, X1): + case OE_RRR(V2DOTPA, 0, X0): + case OE_RRR(V2DOTP, 0, X0): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(V2INT_H, 0, X0): + case OE_RRR(V2INT_H, 0, X1): + gen_helper_v2int_h(tdest, tsrca, tsrcb); + mnemonic = "v2int_h"; + break; + case OE_RRR(V2INT_L, 0, X0): + case OE_RRR(V2INT_L, 0, X1): + gen_helper_v2int_l(tdest, tsrca, tsrcb); + mnemonic = "v2int_l"; + break; + case OE_RRR(V2MAXS, 0, X0): + case OE_RRR(V2MAXS, 0, X1): + case OE_RRR(V2MINS, 0, X0): + case OE_RRR(V2MINS, 0, X1): + case OE_RRR(V2MNZ, 0, X0): + case OE_RRR(V2MNZ, 0, X1): + case OE_RRR(V2MULFSC, 0, X0): + case OE_RRR(V2MULS, 0, X0): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(V2MULTS, 0, X0): + gen_helper_v2mults(tdest, tsrca, tsrcb); + mnemonic = "v2mults"; + break; + case OE_RRR(V2MZ, 0, X0): + case OE_RRR(V2MZ, 0, X1): + case OE_RRR(V2PACKH, 0, X0): + case OE_RRR(V2PACKH, 0, X1): + case OE_RRR(V2PACKL, 0, X0): + case OE_RRR(V2PACKL, 0, X1): + case OE_RRR(V2PACKUC, 0, X0): + case OE_RRR(V2PACKUC, 0, X1): + case OE_RRR(V2SADAS, 0, X0): + case OE_RRR(V2SADAU, 0, X0): + case OE_RRR(V2SADS, 0, X0): + case OE_RRR(V2SADU, 0, X0): + case OE_RRR(V2SHLSC, 0, X0): + case OE_RRR(V2SHLSC, 0, X1): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(V2SHL, 0, X0): + case OE_RRR(V2SHL, 0, X1): + gen_helper_v2shl(tdest, tsrca, tsrcb); + mnemonic = "v2shl"; + break; + case OE_RRR(V2SHRS, 0, X0): + case OE_RRR(V2SHRS, 0, X1): + gen_helper_v2shrs(tdest, tsrca, tsrcb); + mnemonic = "v2shrs"; + break; + case OE_RRR(V2SHRU, 0, X0): + case OE_RRR(V2SHRU, 0, X1): + gen_helper_v2shru(tdest, tsrca, tsrcb); + mnemonic = "v2shru"; + break; + case OE_RRR(V2SUBSC, 0, X0): + case OE_RRR(V2SUBSC, 0, X1): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(V2SUB, 0, X0): + case OE_RRR(V2SUB, 0, X1): + gen_v12sub(tdest, tsrca, tsrcb, V2_IMM(0x8000)); + mnemonic = "v2sub"; + break; + case OE_RRR(V4ADDSC, 0, X0): + case OE_RRR(V4ADDSC, 0, X1): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(V4ADD, 0, X0): + case OE_RRR(V4ADD, 0, X1): + gen_v4op(tdest, tsrca, tsrcb, tcg_gen_add_i32); + mnemonic = "v4add"; + break; + case OE_RRR(V4INT_H, 0, X0): + case OE_RRR(V4INT_H, 0, X1): + tcg_gen_shri_tl(tdest, tsrcb, 32); + tcg_gen_deposit_tl(tdest, tsrca, tdest, 0, 32); + mnemonic = "v4int_h"; + break; + case OE_RRR(V4INT_L, 0, X0): + case OE_RRR(V4INT_L, 0, X1): + tcg_gen_deposit_tl(tdest, tsrcb, tsrca, 32, 32); + mnemonic = "v4int_l"; + break; + case OE_RRR(V4PACKSC, 0, X0): + case OE_RRR(V4PACKSC, 0, X1): + case OE_RRR(V4SHLSC, 0, X0): + case OE_RRR(V4SHLSC, 0, X1): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(V4SHL, 0, X0): + case OE_RRR(V4SHL, 0, X1): + gen_v4sh(tdest, tsrca, tsrcb, tcg_gen_shl_i32); + mnemonic = "v4shl"; + break; + case OE_RRR(V4SHRS, 0, X0): + case OE_RRR(V4SHRS, 0, X1): + gen_v4sh(tdest, tsrca, tsrcb, tcg_gen_sar_i32); + mnemonic = "v4shrs"; + break; + case OE_RRR(V4SHRU, 0, X0): + case OE_RRR(V4SHRU, 0, X1): + gen_v4sh(tdest, tsrca, tsrcb, tcg_gen_shr_i32); + mnemonic = "v4shru"; + break; + case OE_RRR(V4SUBSC, 0, X0): + case OE_RRR(V4SUBSC, 0, X1): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(V4SUB, 0, X0): + case OE_RRR(V4SUB, 0, X1): + gen_v4op(tdest, tsrca, tsrcb, tcg_gen_sub_i32); + mnemonic = "v2sub"; + break; + case OE_RRR(XOR, 0, X0): + case OE_RRR(XOR, 0, X1): + case OE_RRR(XOR, 5, Y0): + case OE_RRR(XOR, 5, Y1): + tcg_gen_xor_tl(tdest, tsrca, tsrcb); + mnemonic = "xor"; + break; + default: + return TILEGX_EXCP_OPCODE_UNKNOWN; + } + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s %s, %s, %s", mnemonic, + reg_names[dest], reg_names[srca], reg_names[srcb]); + return TILEGX_EXCP_NONE; +} + +static TileExcp gen_rri_opcode(DisasContext *dc, unsigned opext, + unsigned dest, unsigned srca, int imm) +{ + TCGv tdest = dest_gr(dc, dest); + TCGv tsrca = load_gr(dc, srca); + bool prefetch_nofault = false; + const char *mnemonic; + TCGMemOp memop; + int i2, i3; + TCGv t0; + + switch (opext) { + case OE(ADDI_OPCODE_Y0, 0, Y0): + case OE(ADDI_OPCODE_Y1, 0, Y1): + case OE_IM(ADDI, X0): + case OE_IM(ADDI, X1): + tcg_gen_addi_tl(tdest, tsrca, imm); + mnemonic = "addi"; + break; + case OE(ADDXI_OPCODE_Y0, 0, Y0): + case OE(ADDXI_OPCODE_Y1, 0, Y1): + case OE_IM(ADDXI, X0): + case OE_IM(ADDXI, X1): + tcg_gen_addi_tl(tdest, tsrca, imm); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "addxi"; + break; + case OE(ANDI_OPCODE_Y0, 0, Y0): + case OE(ANDI_OPCODE_Y1, 0, Y1): + case OE_IM(ANDI, X0): + case OE_IM(ANDI, X1): + tcg_gen_andi_tl(tdest, tsrca, imm); + mnemonic = "andi"; + break; + case OE(CMPEQI_OPCODE_Y0, 0, Y0): + case OE(CMPEQI_OPCODE_Y1, 0, Y1): + case OE_IM(CMPEQI, X0): + case OE_IM(CMPEQI, X1): + tcg_gen_setcondi_tl(TCG_COND_EQ, tdest, tsrca, imm); + mnemonic = "cmpeqi"; + break; + case OE(CMPLTSI_OPCODE_Y0, 0, Y0): + case OE(CMPLTSI_OPCODE_Y1, 0, Y1): + case OE_IM(CMPLTSI, X0): + case OE_IM(CMPLTSI, X1): + tcg_gen_setcondi_tl(TCG_COND_LT, tdest, tsrca, imm); + mnemonic = "cmpltsi"; + break; + case OE_IM(CMPLTUI, X0): + case OE_IM(CMPLTUI, X1): + tcg_gen_setcondi_tl(TCG_COND_LTU, tdest, tsrca, imm); + mnemonic = "cmpltui"; + break; + case OE_IM(LD1S_ADD, X1): + memop = MO_SB; + mnemonic = "ld1s_add"; /* prefetch_add_l1_fault */ + goto do_load_add; + case OE_IM(LD1U_ADD, X1): + memop = MO_UB; + mnemonic = "ld1u_add"; /* prefetch_add_l1 */ + prefetch_nofault = (dest == TILEGX_R_ZERO); + goto do_load_add; + case OE_IM(LD2S_ADD, X1): + memop = MO_TESW; + mnemonic = "ld2s_add"; /* prefetch_add_l2_fault */ + goto do_load_add; + case OE_IM(LD2U_ADD, X1): + memop = MO_TEUW; + mnemonic = "ld2u_add"; /* prefetch_add_l2 */ + prefetch_nofault = (dest == TILEGX_R_ZERO); + goto do_load_add; + case OE_IM(LD4S_ADD, X1): + memop = MO_TESL; + mnemonic = "ld4s_add"; /* prefetch_add_l3_fault */ + goto do_load_add; + case OE_IM(LD4U_ADD, X1): + memop = MO_TEUL; + mnemonic = "ld4u_add"; /* prefetch_add_l3 */ + prefetch_nofault = (dest == TILEGX_R_ZERO); + goto do_load_add; + case OE_IM(LDNT1S_ADD, X1): + memop = MO_SB; + mnemonic = "ldnt1s_add"; + goto do_load_add; + case OE_IM(LDNT1U_ADD, X1): + memop = MO_UB; + mnemonic = "ldnt1u_add"; + goto do_load_add; + case OE_IM(LDNT2S_ADD, X1): + memop = MO_TESW; + mnemonic = "ldnt2s_add"; + goto do_load_add; + case OE_IM(LDNT2U_ADD, X1): + memop = MO_TEUW; + mnemonic = "ldnt2u_add"; + goto do_load_add; + case OE_IM(LDNT4S_ADD, X1): + memop = MO_TESL; + mnemonic = "ldnt4s_add"; + goto do_load_add; + case OE_IM(LDNT4U_ADD, X1): + memop = MO_TEUL; + mnemonic = "ldnt4u_add"; + goto do_load_add; + case OE_IM(LDNT_ADD, X1): + memop = MO_TEQ; + mnemonic = "ldnt_add"; + goto do_load_add; + case OE_IM(LD_ADD, X1): + memop = MO_TEQ; + mnemonic = "ld_add"; + do_load_add: + if (!prefetch_nofault) { + tcg_gen_qemu_ld_tl(tdest, tsrca, dc->mmuidx, memop); + } + tcg_gen_addi_tl(dest_gr(dc, srca), tsrca, imm); + break; + case OE_IM(LDNA_ADD, X1): + tcg_gen_andi_tl(tdest, tsrca, ~7); + tcg_gen_qemu_ld_tl(tdest, tdest, dc->mmuidx, MO_TEQ); + tcg_gen_addi_tl(dest_gr(dc, srca), tsrca, imm); + mnemonic = "ldna_add"; + break; + case OE_IM(ORI, X0): + case OE_IM(ORI, X1): + tcg_gen_ori_tl(tdest, tsrca, imm); + mnemonic = "ori"; + break; + case OE_IM(V1ADDI, X0): + case OE_IM(V1ADDI, X1): + t0 = tcg_const_tl(V1_IMM(imm)); + gen_v12add(tdest, tsrca, t0, V1_IMM(0x80)); + tcg_temp_free(t0); + mnemonic = "v1addi"; + break; + case OE_IM(V1CMPEQI, X0): + case OE_IM(V1CMPEQI, X1): + tcg_gen_xori_tl(tdest, tsrca, V1_IMM(imm)); + gen_v1cmpeq0(tdest); + mnemonic = "v1cmpeqi"; + break; + case OE_IM(V1CMPLTSI, X0): + case OE_IM(V1CMPLTSI, X1): + case OE_IM(V1CMPLTUI, X0): + case OE_IM(V1CMPLTUI, X1): + case OE_IM(V1MAXUI, X0): + case OE_IM(V1MAXUI, X1): + case OE_IM(V1MINUI, X0): + case OE_IM(V1MINUI, X1): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_IM(V2ADDI, X0): + case OE_IM(V2ADDI, X1): + t0 = tcg_const_tl(V2_IMM(imm)); + gen_v12add(tdest, tsrca, t0, V2_IMM(0x8000)); + tcg_temp_free(t0); + mnemonic = "v2addi"; + break; + case OE_IM(V2CMPEQI, X0): + case OE_IM(V2CMPEQI, X1): + case OE_IM(V2CMPLTSI, X0): + case OE_IM(V2CMPLTSI, X1): + case OE_IM(V2CMPLTUI, X0): + case OE_IM(V2CMPLTUI, X1): + case OE_IM(V2MAXSI, X0): + case OE_IM(V2MAXSI, X1): + case OE_IM(V2MINSI, X0): + case OE_IM(V2MINSI, X1): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_IM(XORI, X0): + case OE_IM(XORI, X1): + tcg_gen_xori_tl(tdest, tsrca, imm); + mnemonic = "xori"; + break; + + case OE_SH(ROTLI, X0): + case OE_SH(ROTLI, X1): + case OE_SH(ROTLI, Y0): + case OE_SH(ROTLI, Y1): + tcg_gen_rotli_tl(tdest, tsrca, imm); + mnemonic = "rotli"; + break; + case OE_SH(SHLI, X0): + case OE_SH(SHLI, X1): + case OE_SH(SHLI, Y0): + case OE_SH(SHLI, Y1): + tcg_gen_shli_tl(tdest, tsrca, imm); + mnemonic = "shli"; + break; + case OE_SH(SHLXI, X0): + case OE_SH(SHLXI, X1): + tcg_gen_shli_tl(tdest, tsrca, imm & 31); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "shlxi"; + break; + case OE_SH(SHRSI, X0): + case OE_SH(SHRSI, X1): + case OE_SH(SHRSI, Y0): + case OE_SH(SHRSI, Y1): + tcg_gen_sari_tl(tdest, tsrca, imm); + mnemonic = "shrsi"; + break; + case OE_SH(SHRUI, X0): + case OE_SH(SHRUI, X1): + case OE_SH(SHRUI, Y0): + case OE_SH(SHRUI, Y1): + tcg_gen_shri_tl(tdest, tsrca, imm); + mnemonic = "shrui"; + break; + case OE_SH(SHRUXI, X0): + case OE_SH(SHRUXI, X1): + if ((imm & 31) == 0) { + tcg_gen_ext32s_tl(tdest, tsrca); + } else { + tcg_gen_ext32u_tl(tdest, tsrca); + tcg_gen_shri_tl(tdest, tdest, imm & 31); + } + mnemonic = "shlxi"; + break; + case OE_SH(V1SHLI, X0): + case OE_SH(V1SHLI, X1): + i2 = imm & 7; + i3 = 0xff >> i2; + tcg_gen_andi_tl(tdest, tsrca, V1_IMM(i3)); + tcg_gen_shli_tl(tdest, tdest, i2); + mnemonic = "v1shli"; + break; + case OE_SH(V1SHRSI, X0): + case OE_SH(V1SHRSI, X1): + t0 = tcg_const_tl(imm & 7); + gen_helper_v1shrs(tdest, tsrca, t0); + tcg_temp_free(t0); + mnemonic = "v1shrsi"; + break; + case OE_SH(V1SHRUI, X0): + case OE_SH(V1SHRUI, X1): + i2 = imm & 7; + i3 = (0xff << i2) & 0xff; + tcg_gen_andi_tl(tdest, tsrca, V1_IMM(i3)); + tcg_gen_shri_tl(tdest, tdest, i2); + mnemonic = "v1shrui"; + break; + case OE_SH(V2SHLI, X0): + case OE_SH(V2SHLI, X1): + i2 = imm & 15; + i3 = 0xffff >> i2; + tcg_gen_andi_tl(tdest, tsrca, V2_IMM(i3)); + tcg_gen_shli_tl(tdest, tdest, i2); + mnemonic = "v2shli"; + break; + case OE_SH(V2SHRSI, X0): + case OE_SH(V2SHRSI, X1): + t0 = tcg_const_tl(imm & 15); + gen_helper_v2shrs(tdest, tsrca, t0); + tcg_temp_free(t0); + mnemonic = "v2shrsi"; + break; + case OE_SH(V2SHRUI, X0): + case OE_SH(V2SHRUI, X1): + i2 = imm & 15; + i3 = (0xffff << i2) & 0xffff; + tcg_gen_andi_tl(tdest, tsrca, V2_IMM(i3)); + tcg_gen_shri_tl(tdest, tdest, i2); + mnemonic = "v2shrui"; + break; + + case OE(ADDLI_OPCODE_X0, 0, X0): + case OE(ADDLI_OPCODE_X1, 0, X1): + tcg_gen_addi_tl(tdest, tsrca, imm); + mnemonic = "addli"; + break; + case OE(ADDXLI_OPCODE_X0, 0, X0): + case OE(ADDXLI_OPCODE_X1, 0, X1): + tcg_gen_addi_tl(tdest, tsrca, imm); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "addxli"; + break; + case OE(SHL16INSLI_OPCODE_X0, 0, X0): + case OE(SHL16INSLI_OPCODE_X1, 0, X1): + tcg_gen_shli_tl(tdest, tsrca, 16); + tcg_gen_ori_tl(tdest, tdest, imm & 0xffff); + mnemonic = "shl16insli"; + break; + + default: + return TILEGX_EXCP_OPCODE_UNKNOWN; + } + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s %s, %s, %d", mnemonic, + reg_names[dest], reg_names[srca], imm); + return TILEGX_EXCP_NONE; +} + +static TileExcp gen_bf_opcode_x0(DisasContext *dc, unsigned ext, + unsigned dest, unsigned srca, + unsigned bfs, unsigned bfe) +{ + TCGv tdest = dest_gr(dc, dest); + TCGv tsrca = load_gr(dc, srca); + TCGv tsrcd; + int len; + const char *mnemonic; + + /* The bitfield is either between E and S inclusive, + or up from S and down from E inclusive. */ + if (bfs <= bfe) { + len = bfe - bfs + 1; + } else { + len = (64 - bfs) + (bfe + 1); + } + + switch (ext) { + case BFEXTU_BF_OPCODE_X0: + if (bfs == 0 && bfe == 7) { + tcg_gen_ext8u_tl(tdest, tsrca); + } else if (bfs == 0 && bfe == 15) { + tcg_gen_ext16u_tl(tdest, tsrca); + } else if (bfs == 0 && bfe == 31) { + tcg_gen_ext32u_tl(tdest, tsrca); + } else { + int rol = 63 - bfe; + if (bfs <= bfe) { + tcg_gen_shli_tl(tdest, tsrca, rol); + } else { + tcg_gen_rotli_tl(tdest, tsrca, rol); + } + tcg_gen_shri_tl(tdest, tdest, (bfs + rol) & 63); + } + mnemonic = "bfextu"; + break; + + case BFEXTS_BF_OPCODE_X0: + if (bfs == 0 && bfe == 7) { + tcg_gen_ext8s_tl(tdest, tsrca); + } else if (bfs == 0 && bfe == 15) { + tcg_gen_ext16s_tl(tdest, tsrca); + } else if (bfs == 0 && bfe == 31) { + tcg_gen_ext32s_tl(tdest, tsrca); + } else { + int rol = 63 - bfe; + if (bfs <= bfe) { + tcg_gen_shli_tl(tdest, tsrca, rol); + } else { + tcg_gen_rotli_tl(tdest, tsrca, rol); + } + tcg_gen_sari_tl(tdest, tdest, (bfs + rol) & 63); + } + mnemonic = "bfexts"; + break; + + case BFINS_BF_OPCODE_X0: + tsrcd = load_gr(dc, dest); + if (bfs <= bfe) { + tcg_gen_deposit_tl(tdest, tsrcd, tsrca, bfs, len); + } else { + tcg_gen_rotri_tl(tdest, tsrcd, bfs); + tcg_gen_deposit_tl(tdest, tdest, tsrca, 0, len); + tcg_gen_rotli_tl(tdest, tdest, bfs); + } + mnemonic = "bfins"; + break; + + case MM_BF_OPCODE_X0: + tsrcd = load_gr(dc, dest); + if (bfs == 0) { + tcg_gen_deposit_tl(tdest, tsrca, tsrcd, 0, len); + } else { + uint64_t mask = len == 64 ? -1 : rol64((1ULL << len) - 1, bfs); + TCGv tmp = tcg_const_tl(mask); + + tcg_gen_and_tl(tdest, tsrcd, tmp); + tcg_gen_andc_tl(tmp, tsrca, tmp); + tcg_gen_or_tl(tdest, tdest, tmp); + tcg_temp_free(tmp); + } + mnemonic = "mm"; + break; + + default: + return TILEGX_EXCP_OPCODE_UNKNOWN; + } + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s %s, %s, %u, %u", mnemonic, + reg_names[dest], reg_names[srca], bfs, bfe); + return TILEGX_EXCP_NONE; +} + +static TileExcp gen_branch_opcode_x1(DisasContext *dc, unsigned ext, + unsigned srca, int off) +{ + target_ulong tgt = dc->pc + off * TILEGX_BUNDLE_SIZE_IN_BYTES; + const char *mnemonic; + + dc->jmp.dest = tcg_const_tl(tgt); + dc->jmp.val1 = tcg_temp_new(); + tcg_gen_mov_tl(dc->jmp.val1, load_gr(dc, srca)); + + /* Note that the "predict taken" opcodes have bit 0 clear. + Therefore, fold the two cases together by setting bit 0. */ + switch (ext | 1) { + case BEQZ_BRANCH_OPCODE_X1: + dc->jmp.cond = TCG_COND_EQ; + mnemonic = "beqz"; + break; + case BNEZ_BRANCH_OPCODE_X1: + dc->jmp.cond = TCG_COND_NE; + mnemonic = "bnez"; + break; + case BGEZ_BRANCH_OPCODE_X1: + dc->jmp.cond = TCG_COND_GE; + mnemonic = "bgez"; + break; + case BGTZ_BRANCH_OPCODE_X1: + dc->jmp.cond = TCG_COND_GT; + mnemonic = "bgtz"; + break; + case BLEZ_BRANCH_OPCODE_X1: + dc->jmp.cond = TCG_COND_LE; + mnemonic = "blez"; + break; + case BLTZ_BRANCH_OPCODE_X1: + dc->jmp.cond = TCG_COND_LT; + mnemonic = "bltz"; + break; + case BLBC_BRANCH_OPCODE_X1: + dc->jmp.cond = TCG_COND_EQ; + tcg_gen_andi_tl(dc->jmp.val1, dc->jmp.val1, 1); + mnemonic = "blbc"; + break; + case BLBS_BRANCH_OPCODE_X1: + dc->jmp.cond = TCG_COND_NE; + tcg_gen_andi_tl(dc->jmp.val1, dc->jmp.val1, 1); + mnemonic = "blbs"; + break; + default: + return TILEGX_EXCP_OPCODE_UNKNOWN; + } + + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { + qemu_log("%s%s %s, " TARGET_FMT_lx " <%s>", + mnemonic, ext & 1 ? "" : "t", + reg_names[srca], tgt, lookup_symbol(tgt)); + } + return TILEGX_EXCP_NONE; +} + +static TileExcp gen_jump_opcode_x1(DisasContext *dc, unsigned ext, int off) +{ + target_ulong tgt = dc->pc + off * TILEGX_BUNDLE_SIZE_IN_BYTES; + const char *mnemonic = "j"; + + /* The extension field is 1 bit, therefore we only have JAL and J. */ + if (ext == JAL_JUMP_OPCODE_X1) { + tcg_gen_movi_tl(dest_gr(dc, TILEGX_R_LR), + dc->pc + TILEGX_BUNDLE_SIZE_IN_BYTES); + mnemonic = "jal"; + } + dc->jmp.cond = TCG_COND_ALWAYS; + dc->jmp.dest = tcg_const_tl(tgt); + + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { + qemu_log("%s " TARGET_FMT_lx " <%s>", + mnemonic, tgt, lookup_symbol(tgt)); + } + return TILEGX_EXCP_NONE; +} + +typedef struct { + const char *name; + intptr_t offset; + void (*get)(TCGv, TCGv_ptr); + void (*put)(TCGv_ptr, TCGv); +} TileSPR; + +static const TileSPR *find_spr(unsigned spr) +{ + /* Allow the compiler to construct the binary search tree. */ +#define D(N, O, G, P) \ + case SPR_##N: { static const TileSPR x = { #N, O, G, P }; return &x; } + + switch (spr) { + D(CMPEXCH_VALUE, + offsetof(CPUTLGState, spregs[TILEGX_SPR_CMPEXCH]), 0, 0) + D(INTERRUPT_CRITICAL_SECTION, + offsetof(CPUTLGState, spregs[TILEGX_SPR_CRITICAL_SEC]), 0, 0) + D(SIM_CONTROL, + offsetof(CPUTLGState, spregs[TILEGX_SPR_SIM_CONTROL]), 0, 0) + D(EX_CONTEXT_0_0, + offsetof(CPUTLGState, spregs[TILEGX_SPR_EX_CONTEXT_0_0]), 0, 0) + D(EX_CONTEXT_0_1, + offsetof(CPUTLGState, spregs[TILEGX_SPR_EX_CONTEXT_0_1]), 0, 0) + } + +#undef D + + qemu_log_mask(LOG_UNIMP, "UNIMP SPR %u\n", spr); + return NULL; +} + +static TileExcp gen_mtspr_x1(DisasContext *dc, unsigned spr, unsigned srca) +{ + const TileSPR *def = find_spr(spr); + TCGv tsrca; + + if (def == NULL) { + qemu_log_mask(CPU_LOG_TB_IN_ASM, "mtspr spr[%u], %s", spr, reg_names[srca]); + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + } + + tsrca = load_gr(dc, srca); + if (def->put) { + def->put(cpu_env, tsrca); + } else { + tcg_gen_st_tl(tsrca, cpu_env, def->offset); + } + qemu_log_mask(CPU_LOG_TB_IN_ASM, "mtspr %s, %s", def->name, reg_names[srca]); + return TILEGX_EXCP_NONE; +} + +static TileExcp gen_mfspr_x1(DisasContext *dc, unsigned dest, unsigned spr) +{ + const TileSPR *def = find_spr(spr); + TCGv tdest; + + if (def == NULL) { + qemu_log_mask(CPU_LOG_TB_IN_ASM, "mtspr %s, spr[%u]", reg_names[dest], spr); + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + } + + tdest = dest_gr(dc, dest); + if (def->get) { + def->get(tdest, cpu_env); + } else { + tcg_gen_ld_tl(tdest, cpu_env, def->offset); + } + qemu_log_mask(CPU_LOG_TB_IN_ASM, "mfspr %s, %s", reg_names[dest], def->name); + return TILEGX_EXCP_NONE; +} + +static TileExcp decode_y0(DisasContext *dc, tilegx_bundle_bits bundle) +{ + unsigned opc = get_Opcode_Y0(bundle); + unsigned ext = get_RRROpcodeExtension_Y0(bundle); + unsigned dest = get_Dest_Y0(bundle); + unsigned srca = get_SrcA_Y0(bundle); + unsigned srcb; + int imm; + + switch (opc) { + case RRR_1_OPCODE_Y0: + if (ext == UNARY_RRR_1_OPCODE_Y0) { + ext = get_UnaryOpcodeExtension_Y0(bundle); + return gen_rr_opcode(dc, OE(opc, ext, Y0), dest, srca, bundle); + } + /* fallthru */ + case RRR_0_OPCODE_Y0: + case RRR_2_OPCODE_Y0: + case RRR_3_OPCODE_Y0: + case RRR_4_OPCODE_Y0: + case RRR_5_OPCODE_Y0: + case RRR_6_OPCODE_Y0: + case RRR_7_OPCODE_Y0: + case RRR_8_OPCODE_Y0: + case RRR_9_OPCODE_Y0: + srcb = get_SrcB_Y0(bundle); + return gen_rrr_opcode(dc, OE(opc, ext, Y0), dest, srca, srcb); + + case SHIFT_OPCODE_Y0: + ext = get_ShiftOpcodeExtension_Y0(bundle); + imm = get_ShAmt_Y0(bundle); + return gen_rri_opcode(dc, OE(opc, ext, Y0), dest, srca, imm); + + case ADDI_OPCODE_Y0: + case ADDXI_OPCODE_Y0: + case ANDI_OPCODE_Y0: + case CMPEQI_OPCODE_Y0: + case CMPLTSI_OPCODE_Y0: + imm = (int8_t)get_Imm8_Y0(bundle); + return gen_rri_opcode(dc, OE(opc, 0, Y0), dest, srca, imm); + + default: + return TILEGX_EXCP_OPCODE_UNKNOWN; + } +} + +static TileExcp decode_y1(DisasContext *dc, tilegx_bundle_bits bundle) +{ + unsigned opc = get_Opcode_Y1(bundle); + unsigned ext = get_RRROpcodeExtension_Y1(bundle); + unsigned dest = get_Dest_Y1(bundle); + unsigned srca = get_SrcA_Y1(bundle); + unsigned srcb; + int imm; + + switch (get_Opcode_Y1(bundle)) { + case RRR_1_OPCODE_Y1: + if (ext == UNARY_RRR_1_OPCODE_Y0) { + ext = get_UnaryOpcodeExtension_Y1(bundle); + return gen_rr_opcode(dc, OE(opc, ext, Y1), dest, srca, bundle); + } + /* fallthru */ + case RRR_0_OPCODE_Y1: + case RRR_2_OPCODE_Y1: + case RRR_3_OPCODE_Y1: + case RRR_4_OPCODE_Y1: + case RRR_5_OPCODE_Y1: + case RRR_6_OPCODE_Y1: + case RRR_7_OPCODE_Y1: + srcb = get_SrcB_Y1(bundle); + return gen_rrr_opcode(dc, OE(opc, ext, Y1), dest, srca, srcb); + + case SHIFT_OPCODE_Y1: + ext = get_ShiftOpcodeExtension_Y1(bundle); + imm = get_ShAmt_Y1(bundle); + return gen_rri_opcode(dc, OE(opc, ext, Y1), dest, srca, imm); + + case ADDI_OPCODE_Y1: + case ADDXI_OPCODE_Y1: + case ANDI_OPCODE_Y1: + case CMPEQI_OPCODE_Y1: + case CMPLTSI_OPCODE_Y1: + imm = (int8_t)get_Imm8_Y1(bundle); + return gen_rri_opcode(dc, OE(opc, 0, Y1), dest, srca, imm); + + default: + return TILEGX_EXCP_OPCODE_UNKNOWN; + } +} + +static TileExcp decode_y2(DisasContext *dc, tilegx_bundle_bits bundle) +{ + unsigned mode = get_Mode(bundle); + unsigned opc = get_Opcode_Y2(bundle); + unsigned srca = get_SrcA_Y2(bundle); + unsigned srcbdest = get_SrcBDest_Y2(bundle); + const char *mnemonic; + TCGMemOp memop; + bool prefetch_nofault = false; + + switch (OEY2(opc, mode)) { + case OEY2(LD1S_OPCODE_Y2, MODE_OPCODE_YA2): + memop = MO_SB; + mnemonic = "ld1s"; /* prefetch_l1_fault */ + goto do_load; + case OEY2(LD1U_OPCODE_Y2, MODE_OPCODE_YA2): + memop = MO_UB; + mnemonic = "ld1u"; /* prefetch, prefetch_l1 */ + prefetch_nofault = (srcbdest == TILEGX_R_ZERO); + goto do_load; + case OEY2(LD2S_OPCODE_Y2, MODE_OPCODE_YA2): + memop = MO_TESW; + mnemonic = "ld2s"; /* prefetch_l2_fault */ + goto do_load; + case OEY2(LD2U_OPCODE_Y2, MODE_OPCODE_YA2): + memop = MO_TEUW; + mnemonic = "ld2u"; /* prefetch_l2 */ + prefetch_nofault = (srcbdest == TILEGX_R_ZERO); + goto do_load; + case OEY2(LD4S_OPCODE_Y2, MODE_OPCODE_YB2): + memop = MO_TESL; + mnemonic = "ld4s"; /* prefetch_l3_fault */ + goto do_load; + case OEY2(LD4U_OPCODE_Y2, MODE_OPCODE_YB2): + memop = MO_TEUL; + mnemonic = "ld4u"; /* prefetch_l3 */ + prefetch_nofault = (srcbdest == TILEGX_R_ZERO); + goto do_load; + case OEY2(LD_OPCODE_Y2, MODE_OPCODE_YB2): + memop = MO_TEQ; + mnemonic = "ld"; + do_load: + if (!prefetch_nofault) { + tcg_gen_qemu_ld_tl(dest_gr(dc, srcbdest), load_gr(dc, srca), + dc->mmuidx, memop); + } + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s %s, %s", mnemonic, + reg_names[srcbdest], reg_names[srca]); + return TILEGX_EXCP_NONE; + + case OEY2(ST1_OPCODE_Y2, MODE_OPCODE_YC2): + return gen_st_opcode(dc, 0, srca, srcbdest, MO_UB, "st1"); + case OEY2(ST2_OPCODE_Y2, MODE_OPCODE_YC2): + return gen_st_opcode(dc, 0, srca, srcbdest, MO_TEUW, "st2"); + case OEY2(ST4_OPCODE_Y2, MODE_OPCODE_YC2): + return gen_st_opcode(dc, 0, srca, srcbdest, MO_TEUL, "st4"); + case OEY2(ST_OPCODE_Y2, MODE_OPCODE_YC2): + return gen_st_opcode(dc, 0, srca, srcbdest, MO_TEQ, "st"); + + default: + return TILEGX_EXCP_OPCODE_UNKNOWN; + } +} + +static TileExcp decode_x0(DisasContext *dc, tilegx_bundle_bits bundle) +{ + unsigned opc = get_Opcode_X0(bundle); + unsigned dest = get_Dest_X0(bundle); + unsigned srca = get_SrcA_X0(bundle); + unsigned ext, srcb, bfs, bfe; + int imm; + + switch (opc) { + case RRR_0_OPCODE_X0: + ext = get_RRROpcodeExtension_X0(bundle); + if (ext == UNARY_RRR_0_OPCODE_X0) { + ext = get_UnaryOpcodeExtension_X0(bundle); + return gen_rr_opcode(dc, OE(opc, ext, X0), dest, srca, bundle); + } + srcb = get_SrcB_X0(bundle); + return gen_rrr_opcode(dc, OE(opc, ext, X0), dest, srca, srcb); + + case SHIFT_OPCODE_X0: + ext = get_ShiftOpcodeExtension_X0(bundle); + imm = get_ShAmt_X0(bundle); + return gen_rri_opcode(dc, OE(opc, ext, X0), dest, srca, imm); + + case IMM8_OPCODE_X0: + ext = get_Imm8OpcodeExtension_X0(bundle); + imm = (int8_t)get_Imm8_X0(bundle); + return gen_rri_opcode(dc, OE(opc, ext, X0), dest, srca, imm); + + case BF_OPCODE_X0: + ext = get_BFOpcodeExtension_X0(bundle); + bfs = get_BFStart_X0(bundle); + bfe = get_BFEnd_X0(bundle); + return gen_bf_opcode_x0(dc, ext, dest, srca, bfs, bfe); + + case ADDLI_OPCODE_X0: + case SHL16INSLI_OPCODE_X0: + case ADDXLI_OPCODE_X0: + imm = (int16_t)get_Imm16_X0(bundle); + return gen_rri_opcode(dc, OE(opc, 0, X0), dest, srca, imm); + + default: + return TILEGX_EXCP_OPCODE_UNKNOWN; + } +} + +static TileExcp decode_x1(DisasContext *dc, tilegx_bundle_bits bundle) +{ + unsigned opc = get_Opcode_X1(bundle); + unsigned dest = get_Dest_X1(bundle); + unsigned srca = get_SrcA_X1(bundle); + unsigned ext, srcb; + int imm; + + switch (opc) { + case RRR_0_OPCODE_X1: + ext = get_RRROpcodeExtension_X1(bundle); + srcb = get_SrcB_X1(bundle); + switch (ext) { + case UNARY_RRR_0_OPCODE_X1: + ext = get_UnaryOpcodeExtension_X1(bundle); + return gen_rr_opcode(dc, OE(opc, ext, X1), dest, srca, bundle); + case ST1_RRR_0_OPCODE_X1: + return gen_st_opcode(dc, dest, srca, srcb, MO_UB, "st1"); + case ST2_RRR_0_OPCODE_X1: + return gen_st_opcode(dc, dest, srca, srcb, MO_TEUW, "st2"); + case ST4_RRR_0_OPCODE_X1: + return gen_st_opcode(dc, dest, srca, srcb, MO_TEUL, "st4"); + case STNT1_RRR_0_OPCODE_X1: + return gen_st_opcode(dc, dest, srca, srcb, MO_UB, "stnt1"); + case STNT2_RRR_0_OPCODE_X1: + return gen_st_opcode(dc, dest, srca, srcb, MO_TEUW, "stnt2"); + case STNT4_RRR_0_OPCODE_X1: + return gen_st_opcode(dc, dest, srca, srcb, MO_TEUL, "stnt4"); + case STNT_RRR_0_OPCODE_X1: + return gen_st_opcode(dc, dest, srca, srcb, MO_TEQ, "stnt"); + case ST_RRR_0_OPCODE_X1: + return gen_st_opcode(dc, dest, srca, srcb, MO_TEQ, "st"); + } + return gen_rrr_opcode(dc, OE(opc, ext, X1), dest, srca, srcb); + + case SHIFT_OPCODE_X1: + ext = get_ShiftOpcodeExtension_X1(bundle); + imm = get_ShAmt_X1(bundle); + return gen_rri_opcode(dc, OE(opc, ext, X1), dest, srca, imm); + + case IMM8_OPCODE_X1: + ext = get_Imm8OpcodeExtension_X1(bundle); + imm = (int8_t)get_Dest_Imm8_X1(bundle); + srcb = get_SrcB_X1(bundle); + switch (ext) { + case ST1_ADD_IMM8_OPCODE_X1: + return gen_st_add_opcode(dc, srca, srcb, imm, MO_UB, "st1_add"); + case ST2_ADD_IMM8_OPCODE_X1: + return gen_st_add_opcode(dc, srca, srcb, imm, MO_TEUW, "st2_add"); + case ST4_ADD_IMM8_OPCODE_X1: + return gen_st_add_opcode(dc, srca, srcb, imm, MO_TEUL, "st4_add"); + case STNT1_ADD_IMM8_OPCODE_X1: + return gen_st_add_opcode(dc, srca, srcb, imm, MO_UB, "stnt1_add"); + case STNT2_ADD_IMM8_OPCODE_X1: + return gen_st_add_opcode(dc, srca, srcb, imm, MO_TEUW, "stnt2_add"); + case STNT4_ADD_IMM8_OPCODE_X1: + return gen_st_add_opcode(dc, srca, srcb, imm, MO_TEUL, "stnt4_add"); + case STNT_ADD_IMM8_OPCODE_X1: + return gen_st_add_opcode(dc, srca, srcb, imm, MO_TEQ, "stnt_add"); + case ST_ADD_IMM8_OPCODE_X1: + return gen_st_add_opcode(dc, srca, srcb, imm, MO_TEQ, "st_add"); + case MFSPR_IMM8_OPCODE_X1: + return gen_mfspr_x1(dc, dest, get_MF_Imm14_X1(bundle)); + case MTSPR_IMM8_OPCODE_X1: + return gen_mtspr_x1(dc, get_MT_Imm14_X1(bundle), srca); + } + imm = (int8_t)get_Imm8_X1(bundle); + return gen_rri_opcode(dc, OE(opc, ext, X1), dest, srca, imm); + + case BRANCH_OPCODE_X1: + ext = get_BrType_X1(bundle); + imm = sextract32(get_BrOff_X1(bundle), 0, 17); + return gen_branch_opcode_x1(dc, ext, srca, imm); + + case JUMP_OPCODE_X1: + ext = get_JumpOpcodeExtension_X1(bundle); + imm = sextract32(get_JumpOff_X1(bundle), 0, 27); + return gen_jump_opcode_x1(dc, ext, imm); + + case ADDLI_OPCODE_X1: + case SHL16INSLI_OPCODE_X1: + case ADDXLI_OPCODE_X1: + imm = (int16_t)get_Imm16_X1(bundle); + return gen_rri_opcode(dc, OE(opc, 0, X1), dest, srca, imm); + + default: + return TILEGX_EXCP_OPCODE_UNKNOWN; + } +} + +static void notice_excp(DisasContext *dc, uint64_t bundle, + const char *type, TileExcp excp) +{ + if (likely(excp == TILEGX_EXCP_NONE)) { + return; + } + gen_exception(dc, excp); + switch (excp) { + case TILEGX_EXCP_OPCODE_UNIMPLEMENTED: + qemu_log_mask(LOG_UNIMP, "UNIMP %s, [" FMT64X "]\n", type, bundle); + break; + case TILEGX_EXCP_OPCODE_UNKNOWN: + qemu_log_mask(LOG_UNIMP, "UNKNOWN %s, [" FMT64X "]\n", type, bundle); + break; + default: + break; + } +} + +static void translate_one_bundle(DisasContext *dc, uint64_t bundle) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dc->wb); i++) { + DisasContextTemp *wb = &dc->wb[i]; + wb->reg = TILEGX_R_NOREG; + TCGV_UNUSED_I64(wb->val); + } + dc->num_wb = 0; + + qemu_log_mask(CPU_LOG_TB_IN_ASM, " %" PRIx64 ": { ", dc->pc); + if (get_Mode(bundle)) { + notice_excp(dc, bundle, "y0", decode_y0(dc, bundle)); + qemu_log_mask(CPU_LOG_TB_IN_ASM, " ; "); + notice_excp(dc, bundle, "y1", decode_y1(dc, bundle)); + qemu_log_mask(CPU_LOG_TB_IN_ASM, " ; "); + notice_excp(dc, bundle, "y2", decode_y2(dc, bundle)); + } else { + notice_excp(dc, bundle, "x0", decode_x0(dc, bundle)); + qemu_log_mask(CPU_LOG_TB_IN_ASM, " ; "); + notice_excp(dc, bundle, "x1", decode_x1(dc, bundle)); + } + qemu_log_mask(CPU_LOG_TB_IN_ASM, " }\n"); + + for (i = dc->num_wb - 1; i >= 0; --i) { + DisasContextTemp *wb = &dc->wb[i]; + if (wb->reg < TILEGX_R_COUNT) { + tcg_gen_mov_i64(cpu_regs[wb->reg], wb->val); + } + tcg_temp_free_i64(wb->val); + } + + if (dc->jmp.cond != TCG_COND_NEVER) { + if (dc->jmp.cond == TCG_COND_ALWAYS) { + tcg_gen_mov_i64(cpu_pc, dc->jmp.dest); + } else { + TCGv next = tcg_const_i64(dc->pc + TILEGX_BUNDLE_SIZE_IN_BYTES); + tcg_gen_movcond_i64(dc->jmp.cond, cpu_pc, + dc->jmp.val1, load_zero(dc), + dc->jmp.dest, next); + tcg_temp_free_i64(dc->jmp.val1); + tcg_temp_free_i64(next); + } + tcg_temp_free_i64(dc->jmp.dest); + tcg_gen_exit_tb(0); + dc->exit_tb = true; + } else if (dc->atomic_excp != TILEGX_EXCP_NONE) { + gen_exception(dc, dc->atomic_excp); + } +} + +void gen_intermediate_code(CPUTLGState *env, struct TranslationBlock *tb) +{ + TileGXCPU *cpu = tilegx_env_get_cpu(env); + DisasContext ctx; + DisasContext *dc = &ctx; + CPUState *cs = CPU(cpu); + uint64_t pc_start = tb->pc; + uint64_t next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + int num_insns = 0; + int max_insns = tb->cflags & CF_COUNT_MASK; + + dc->pc = pc_start; + dc->mmuidx = 0; + dc->exit_tb = false; + dc->atomic_excp = TILEGX_EXCP_NONE; + dc->jmp.cond = TCG_COND_NEVER; + TCGV_UNUSED_I64(dc->jmp.dest); + TCGV_UNUSED_I64(dc->jmp.val1); + TCGV_UNUSED_I64(dc->zero); + + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { + qemu_log("IN: %s\n", lookup_symbol(pc_start)); + } + if (!max_insns) { + max_insns = CF_COUNT_MASK; + } + if (cs->singlestep_enabled || singlestep) { + max_insns = 1; + } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } + gen_tb_start(tb); + + while (1) { + tcg_gen_insn_start(dc->pc); + num_insns++; + + translate_one_bundle(dc, cpu_ldq_data(env, dc->pc)); + + if (dc->exit_tb) { + /* PC updated and EXIT_TB/GOTO_TB/exception emitted. */ + break; + } + dc->pc += TILEGX_BUNDLE_SIZE_IN_BYTES; + if (num_insns >= max_insns + || dc->pc >= next_page_start + || tcg_op_buf_full()) { + /* Ending the TB due to TB size or page boundary. Set PC. */ + tcg_gen_movi_tl(cpu_pc, dc->pc); + tcg_gen_exit_tb(0); + break; + } + } + + gen_tb_end(tb, num_insns); + tb->size = dc->pc - pc_start; + tb->icount = num_insns; + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "\n"); +} + +void restore_state_to_opc(CPUTLGState *env, TranslationBlock *tb, + target_ulong *data) +{ + env->pc = data[0]; +} + +void tilegx_tcg_init(void) +{ + int i; + + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + cpu_pc = tcg_global_mem_new_i64(cpu_env, offsetof(CPUTLGState, pc), "pc"); + for (i = 0; i < TILEGX_R_COUNT; i++) { + cpu_regs[i] = tcg_global_mem_new_i64(cpu_env, + offsetof(CPUTLGState, regs[i]), + reg_names[i]); + } +} diff --git a/qemu/target-tricore/Makefile.objs b/qemu/target-tricore/Makefile.objs index 21e820d8f..7a0567071 100644 --- a/qemu/target-tricore/Makefile.objs +++ b/qemu/target-tricore/Makefile.objs @@ -1 +1 @@ -obj-y += translate.o helper.o cpu.o op_helper.o +obj-y += translate.o helper.o cpu.o op_helper.o fpu_helper.o diff --git a/qemu/target-tricore/cpu.c b/qemu/target-tricore/cpu.c index 2029ef651..69fca8c06 100644 --- a/qemu/target-tricore/cpu.c +++ b/qemu/target-tricore/cpu.c @@ -17,6 +17,8 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "qemu-common.h" @@ -170,6 +172,12 @@ static void tricore_cpu_class_init(ObjectClass *c, void *data) cc->set_pc = tricore_cpu_set_pc; cc->synchronize_from_tb = tricore_cpu_synchronize_from_tb; + /* + * Reason: tricore_cpu_initfn() calls cpu_exec_init(), which saves + * the object in cpus -> dangling pointer after final + * object_unref(). + */ + dc->cannot_destroy_with_object_finalize_yet = true; } static void cpu_register(const TriCoreCPUInfo *info) diff --git a/qemu/target-tricore/cpu.h b/qemu/target-tricore/cpu.h index 916ee27ad..90045a93d 100644 --- a/qemu/target-tricore/cpu.h +++ b/qemu/target-tricore/cpu.h @@ -20,13 +20,10 @@ #define __TRICORE_CPU_H__ #include "tricore-defs.h" -#include "config.h" #include "qemu-common.h" #include "exec/cpu-defs.h" #include "fpu/softfloat.h" -#define ELF_MACHINE EM_TRICORE - #define CPUArchState struct CPUTriCoreState struct CPUTriCoreState; @@ -186,8 +183,7 @@ struct CPUTriCoreState { uint32_t M2CNT; uint32_t M3CNT; /* Floating Point Registers */ - /* XXX: */ - + float_status fp_status; /* QEMU */ int error_code; uint32_t hflags; /* CPU State */ @@ -220,6 +216,7 @@ struct CPUTriCoreState { #define MASK_PSW_GW 0x00000100 #define MASK_PSW_CDE 0x00000080 #define MASK_PSW_CDC 0x0000007f +#define MASK_PSW_FPU_RM 0x3000000 #define MASK_SYSCON_PRO_TEN 0x2 #define MASK_SYSCON_FCD_SF 0x1 @@ -273,6 +270,7 @@ enum { TRAPC_ASSERT = 5, TRAPC_SYSCALL = 6, TRAPC_NMI = 7, + TRAPC_IRQ = 8 }; /* Class 0 TIN */ @@ -341,6 +339,8 @@ enum { uint32_t psw_read(CPUTriCoreState *env); void psw_write(CPUTriCoreState *env, uint32_t val); +void fpu_set_state(CPUTriCoreState *env); + #include "cpu-qom.h" #define MMU_USER_IDX 2 @@ -350,7 +350,7 @@ void tricore_cpu_list(FILE *f, fprintf_function cpu_fprintf); #define cpu_signal_handler cpu_tricore_signal_handler #define cpu_list tricore_cpu_list -static inline int cpu_mmu_index(CPUTriCoreState *env) +static inline int cpu_mmu_index(CPUTriCoreState *env, bool ifetch) { return 0; } diff --git a/qemu/target-tricore/fpu_helper.c b/qemu/target-tricore/fpu_helper.c new file mode 100644 index 000000000..98fe9472b --- /dev/null +++ b/qemu/target-tricore/fpu_helper.c @@ -0,0 +1,217 @@ +/* + * TriCore emulation for qemu: fpu helper. + * + * Copyright (c) 2016 Bastian Koppelmann University of Paderborn + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" + +#define ADD_NAN 0x7cf00001 +#define DIV_NAN 0x7fc00008 +#define MUL_NAN 0x7fc00002 +#define FPU_FS PSW_USB_C +#define FPU_FI PSW_USB_V +#define FPU_FV PSW_USB_SV +#define FPU_FZ PSW_USB_AV +#define FPU_FU PSW_USB_SAV + +/* we don't care about input_denormal */ +static inline uint8_t f_get_excp_flags(CPUTriCoreState *env) +{ + return get_float_exception_flags(&env->fp_status) + & (float_flag_invalid + | float_flag_overflow + | float_flag_underflow + | float_flag_output_denormal + | float_flag_divbyzero + | float_flag_inexact); +} + +static inline bool f_is_denormal(float32 arg) +{ + return float32_is_zero_or_denormal(arg) && !float32_is_zero(arg); +} + +static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags) +{ + uint8_t some_excp = 0; + set_float_exception_flags(0, &env->fp_status); + + if (flags & float_flag_invalid) { + env->FPU_FI = 1 << 31; + some_excp = 1; + } + + if (flags & float_flag_overflow) { + env->FPU_FV = 1 << 31; + some_excp = 1; + } + + if (flags & float_flag_underflow || flags & float_flag_output_denormal) { + env->FPU_FU = 1 << 31; + some_excp = 1; + } + + if (flags & float_flag_divbyzero) { + env->FPU_FZ = 1 << 31; + some_excp = 1; + } + + if (flags & float_flag_inexact || flags & float_flag_output_denormal) { + env->PSW |= 1 << 26; + some_excp = 1; + } + + env->FPU_FS = some_excp; +} + +#define FADD_SUB(op) \ +uint32_t helper_f##op(CPUTriCoreState *env, uint32_t r1, uint32_t r2) \ +{ \ + float32 arg1 = make_float32(r1); \ + float32 arg2 = make_float32(r2); \ + uint32_t flags; \ + float32 f_result; \ + \ + f_result = float32_##op(arg2, arg1, &env->fp_status); \ + flags = f_get_excp_flags(env); \ + if (flags) { \ + /* If the output is a NaN, but the inputs aren't, \ + we return a unique value. */ \ + if ((flags & float_flag_invalid) \ + && !float32_is_any_nan(arg1) \ + && !float32_is_any_nan(arg2)) { \ + f_result = ADD_NAN; \ + } \ + f_update_psw_flags(env, flags); \ + } else { \ + env->FPU_FS = 0; \ + } \ + return (uint32_t)f_result; \ +} +FADD_SUB(add) +FADD_SUB(sub) + +uint32_t helper_fmul(CPUTriCoreState *env, uint32_t r1, uint32_t r2) +{ + uint32_t flags; + float32 arg1 = make_float32(r1); + float32 arg2 = make_float32(r2); + float32 f_result; + + f_result = float32_mul(arg1, arg2, &env->fp_status); + + flags = f_get_excp_flags(env); + if (flags) { + /* If the output is a NaN, but the inputs aren't, + we return a unique value. */ + if ((flags & float_flag_invalid) + && !float32_is_any_nan(arg1) + && !float32_is_any_nan(arg2)) { + f_result = MUL_NAN; + } + f_update_psw_flags(env, flags); + } else { + env->FPU_FS = 0; + } + return (uint32_t)f_result; + +} + +uint32_t helper_fdiv(CPUTriCoreState *env, uint32_t r1, uint32_t r2) +{ + uint32_t flags; + float32 arg1 = make_float32(r1); + float32 arg2 = make_float32(r2); + float32 f_result; + + f_result = float32_div(arg1, arg2 , &env->fp_status); + + flags = f_get_excp_flags(env); + if (flags) { + /* If the output is a NaN, but the inputs aren't, + we return a unique value. */ + if ((flags & float_flag_invalid) + && !float32_is_any_nan(arg1) + && !float32_is_any_nan(arg2)) { + f_result = DIV_NAN; + } + f_update_psw_flags(env, flags); + } else { + env->FPU_FS = 0; + } + + return (uint32_t)f_result; +} + +uint32_t helper_fcmp(CPUTriCoreState *env, uint32_t r1, uint32_t r2) +{ + uint32_t result, flags; + float32 arg1 = make_float32(r1); + float32 arg2 = make_float32(r2); + + set_flush_inputs_to_zero(0, &env->fp_status); + + result = 1 << (float32_compare_quiet(arg1, arg2, &env->fp_status) + 1); + result |= f_is_denormal(arg1) << 4; + result |= f_is_denormal(arg2) << 5; + + flags = f_get_excp_flags(env); + if (flags) { + f_update_psw_flags(env, flags); + } else { + env->FPU_FS = 0; + } + + set_flush_inputs_to_zero(1, &env->fp_status); + return result; +} + +uint32_t helper_ftoi(CPUTriCoreState *env, uint32_t arg) +{ + float32 f_arg = make_float32(arg); + int32_t result, flags; + + result = float32_to_int32(f_arg, &env->fp_status); + + flags = f_get_excp_flags(env); + if (flags) { + if (float32_is_any_nan(f_arg)) { + result = 0; + } + f_update_psw_flags(env, flags); + } else { + env->FPU_FS = 0; + } + return (uint32_t)result; +} + +uint32_t helper_itof(CPUTriCoreState *env, uint32_t arg) +{ + float32 f_result; + uint32_t flags; + f_result = int32_to_float32(arg, &env->fp_status); + + flags = f_get_excp_flags(env); + if (flags) { + f_update_psw_flags(env, flags); + } else { + env->FPU_FS = 0; + } + return (uint32_t)f_result; +} diff --git a/qemu/target-tricore/helper.c b/qemu/target-tricore/helper.c index f52504c9f..71b31cdb9 100644 --- a/qemu/target-tricore/helper.c +++ b/qemu/target-tricore/helper.c @@ -15,12 +15,7 @@ * License along with this library; if not, see . */ -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include "cpu.h" @@ -66,8 +61,8 @@ int cpu_tricore_handle_mmu_fault(CPUState *cs, target_ulong address, access_type = ACCESS_INT; ret = get_physical_address(env, &physical, &prot, address, rw, access_type); - qemu_log("%s address=" TARGET_FMT_lx " ret %d physical " TARGET_FMT_plx - " prot %d\n", __func__, address, ret, physical, prot); + qemu_log_mask(CPU_LOG_MMU, "%s address=" TARGET_FMT_lx " ret %d physical " TARGET_FMT_plx + " prot %d\n", __func__, address, ret, physical, prot); if (ret == TLBRET_MATCH) { tlb_set_page(cs, address & TARGET_PAGE_MASK, @@ -115,10 +110,18 @@ void tricore_cpu_list(FILE *f, fprintf_function cpu_fprintf) g_slist_free(list); } +void fpu_set_state(CPUTriCoreState *env) +{ + set_float_rounding_mode(env->PSW & MASK_PSW_FPU_RM, &env->fp_status); + set_flush_inputs_to_zero(1, &env->fp_status); + set_flush_to_zero(1, &env->fp_status); + set_default_nan_mode(1, &env->fp_status); +} + uint32_t psw_read(CPUTriCoreState *env) { /* clear all USB bits */ - env->PSW &= 0xffffff; + env->PSW &= 0x6ffffff; /* now set them from the cache */ env->PSW |= ((env->PSW_USB_C != 0) << 31); env->PSW |= ((env->PSW_USB_V & (1 << 31)) >> 1); @@ -132,9 +135,11 @@ uint32_t psw_read(CPUTriCoreState *env) void psw_write(CPUTriCoreState *env, uint32_t val) { env->PSW_USB_C = (val & MASK_USB_C); - env->PSW_USB_V = (val & MASK_USB_V << 1); - env->PSW_USB_SV = (val & MASK_USB_SV << 2); - env->PSW_USB_AV = ((val & MASK_USB_AV) << 3); - env->PSW_USB_SAV = ((val & MASK_USB_SAV) << 4); + env->PSW_USB_V = (val & MASK_USB_V) << 1; + env->PSW_USB_SV = (val & MASK_USB_SV) << 2; + env->PSW_USB_AV = (val & MASK_USB_AV) << 3; + env->PSW_USB_SAV = (val & MASK_USB_SAV) << 4; env->PSW = val; + + fpu_set_state(env); } diff --git a/qemu/target-tricore/helper.h b/qemu/target-tricore/helper.h index cc221f1a9..9333e161a 100644 --- a/qemu/target-tricore/helper.h +++ b/qemu/target-tricore/helper.h @@ -105,6 +105,13 @@ DEF_HELPER_FLAGS_1(parity, TCG_CALL_NO_RWG_SE, i32, i32) /* float */ DEF_HELPER_FLAGS_4(pack, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32, i32) DEF_HELPER_1(unpack, i64, i32) +DEF_HELPER_3(fadd, i32, env, i32, i32) +DEF_HELPER_3(fsub, i32, env, i32, i32) +DEF_HELPER_3(fmul, i32, env, i32, i32) +DEF_HELPER_3(fdiv, i32, env, i32, i32) +DEF_HELPER_3(fcmp, i32, env, i32, i32) +DEF_HELPER_2(ftoi, i32, env, i32) +DEF_HELPER_2(itof, i32, env, i32) /* dvinit */ DEF_HELPER_3(dvinit_b_13, i64, env, i32, i32) DEF_HELPER_3(dvinit_b_131, i64, env, i32, i32) @@ -132,6 +139,7 @@ DEF_HELPER_2(lducx, void, env, i32) DEF_HELPER_2(stlcx, void, env, i32) DEF_HELPER_2(stucx, void, env, i32) DEF_HELPER_1(svlcx, void, env) +DEF_HELPER_1(svucx, void, env) DEF_HELPER_1(rslcx, void, env) /* Address mode helper */ DEF_HELPER_1(br_update, i32, i32) @@ -139,3 +147,5 @@ DEF_HELPER_2(circ_update, i32, i32, i32) /* PSW cache helper */ DEF_HELPER_2(psw_write, void, env, i32) DEF_HELPER_1(psw_read, i32, env) +/* Exceptions */ +DEF_HELPER_3(raise_exception_sync, noreturn, env, i32, i32) diff --git a/qemu/target-tricore/op_helper.c b/qemu/target-tricore/op_helper.c index 53edbdae1..40656c357 100644 --- a/qemu/target-tricore/op_helper.c +++ b/qemu/target-tricore/op_helper.c @@ -14,13 +14,100 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include +#include "qemu/osdep.h" #include "cpu.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" #include /* for crc32 */ + +/* Exception helpers */ + +static void QEMU_NORETURN +raise_exception_sync_internal(CPUTriCoreState *env, uint32_t class, int tin, + uintptr_t pc, uint32_t fcd_pc) +{ + CPUState *cs = CPU(tricore_env_get_cpu(env)); + /* in case we come from a helper-call we need to restore the PC */ + if (pc) { + cpu_restore_state(cs, pc); + } + + /* Tin is loaded into d[15] */ + env->gpr_d[15] = tin; + + if (class == TRAPC_CTX_MNG && tin == TIN3_FCU) { + /* upper context cannot be saved, if the context list is empty */ + } else { + helper_svucx(env); + } + + /* The return address in a[11] is updated */ + if (class == TRAPC_CTX_MNG && tin == TIN3_FCD) { + env->SYSCON |= MASK_SYSCON_FCD_SF; + /* when we run out of CSAs after saving a context a FCD trap is taken + and the return address is the start of the trap handler which used + the last CSA */ + env->gpr_a[11] = fcd_pc; + } else if (class == TRAPC_SYSCALL) { + env->gpr_a[11] = env->PC + 4; + } else { + env->gpr_a[11] = env->PC; + } + /* The stack pointer in A[10] is set to the Interrupt Stack Pointer (ISP) + when the processor was not previously using the interrupt stack + (in case of PSW.IS = 0). The stack pointer bit is set for using the + interrupt stack: PSW.IS = 1. */ + if ((env->PSW & MASK_PSW_IS) == 0) { + env->gpr_a[10] = env->ISP; + } + env->PSW |= MASK_PSW_IS; + /* The I/O mode is set to Supervisor mode, which means all permissions + are enabled: PSW.IO = 10 B .*/ + env->PSW |= (2 << 10); + + /*The current Protection Register Set is set to 0: PSW.PRS = 00 B .*/ + env->PSW &= ~MASK_PSW_PRS; + + /* The Call Depth Counter (CDC) is cleared, and the call depth limit is + set for 64: PSW.CDC = 0000000 B .*/ + env->PSW &= ~MASK_PSW_CDC; + + /* Call Depth Counter is enabled, PSW.CDE = 1. */ + env->PSW |= MASK_PSW_CDE; + + /* Write permission to global registers A[0], A[1], A[8], A[9] is + disabled: PSW.GW = 0. */ + env->PSW &= ~MASK_PSW_GW; + + /*The interrupt system is globally disabled: ICR.IE = 0. The ‘old’ + ICR.IE and ICR.CCPN are saved */ + + /* PCXI.PIE = ICR.IE */ + env->PCXI = ((env->PCXI & ~MASK_PCXI_PIE) + + ((env->ICR & MASK_ICR_IE) << 15)); + /* PCXI.PCPN = ICR.CCPN */ + env->PCXI = (env->PCXI & 0xffffff) + + ((env->ICR & MASK_ICR_CCPN) << 24); + /* Update PC using the trap vector table */ + env->PC = env->BTV | (class << 5); + + cpu_loop_exit(cs); +} + +void helper_raise_exception_sync(CPUTriCoreState *env, uint32_t class, + uint32_t tin) +{ + raise_exception_sync_internal(env, class, tin, 0, 0); +} + +static void raise_exception_sync_helper(CPUTriCoreState *env, uint32_t class, + uint32_t tin, uintptr_t pc) +{ + raise_exception_sync_internal(env, class, tin, pc, 0); +} + /* Addressing mode helper */ static uint16_t reverse16(uint16_t val) @@ -958,6 +1045,8 @@ uint64_t helper_msub64_q_ssov(CPUTriCoreState *env, uint64_t r1, uint32_t r2, } else { result = INT64_MIN; } + } else { + env->PSW_USB_V = 0; } } else { if (ovf < 0) { @@ -2279,7 +2368,7 @@ static bool cdc_zero(target_ulong *psw) static void save_context_upper(CPUTriCoreState *env, int ea) { cpu_stl_data(env, ea, env->PCXI); - cpu_stl_data(env, ea+4, env->PSW); + cpu_stl_data(env, ea+4, psw_read(env)); cpu_stl_data(env, ea+8, env->gpr_a[10]); cpu_stl_data(env, ea+12, env->gpr_a[11]); cpu_stl_data(env, ea+16, env->gpr_d[8]); @@ -2369,11 +2458,13 @@ void helper_call(CPUTriCoreState *env, uint32_t next_pc) /* if (FCX == 0) trap(FCU); */ if (env->FCX == 0) { /* FCU trap */ + raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_FCU, GETPC()); } /* if (PSW.CDE) then if (cdc_increment()) then trap(CDO); */ if (psw & MASK_PSW_CDE) { if (cdc_increment(&psw)) { /* CDO trap */ + raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_CDO, GETPC()); } } /* PSW.CDE = 1;*/ @@ -2409,6 +2500,7 @@ void helper_call(CPUTriCoreState *env, uint32_t next_pc) /* if (tmp_FCX == LCX) trap(FCD);*/ if (tmp_FCX == env->LCX) { /* FCD trap */ + raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_FCD, GETPC()); } psw_write(env, psw); } @@ -2421,18 +2513,25 @@ void helper_ret(CPUTriCoreState *env) psw = psw_read(env); /* if (PSW.CDE) then if (cdc_decrement()) then trap(CDU);*/ - if (env->PSW & MASK_PSW_CDE) { - if (cdc_decrement(&(env->PSW))) { + if (psw & MASK_PSW_CDE) { + if (cdc_decrement(&psw)) { /* CDU trap */ + psw_write(env, psw); + raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_CDU, GETPC()); } } /* if (PCXI[19: 0] == 0) then trap(CSU); */ if ((env->PCXI & 0xfffff) == 0) { /* CSU trap */ + psw_write(env, psw); + raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_CSU, GETPC()); } /* if (PCXI.UL == 0) then trap(CTYP); */ if ((env->PCXI & MASK_PCXI_UL) == 0) { /* CTYP trap */ + cdc_increment(&psw); /* restore to the start of helper */ + psw_write(env, psw); + raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_CTYP, GETPC()); } /* PC = {A11 [31: 1], 1’b0}; */ env->PC = env->gpr_a[11] & 0xfffffffe; @@ -2467,6 +2566,7 @@ void helper_bisr(CPUTriCoreState *env, uint32_t const9) if (env->FCX == 0) { /* FCU trap */ + raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_FCU, GETPC()); } tmp_FCX = env->FCX; @@ -2498,6 +2598,7 @@ void helper_bisr(CPUTriCoreState *env, uint32_t const9) if (tmp_FCX == env->LCX) { /* FCD trap */ + raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_FCD, GETPC()); } } @@ -2509,14 +2610,17 @@ void helper_rfe(CPUTriCoreState *env) /* if (PCXI[19: 0] == 0) then trap(CSU); */ if ((env->PCXI & 0xfffff) == 0) { /* raise csu trap */ + raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_CSU, GETPC()); } /* if (PCXI.UL == 0) then trap(CTYP); */ if ((env->PCXI & MASK_PCXI_UL) == 0) { /* raise CTYP trap */ + raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_CTYP, GETPC()); } /* if (!cdc_zero() AND PSW.CDE) then trap(NEST); */ if (!cdc_zero(&(env->PSW)) && (env->PSW & MASK_PSW_CDE)) { - /* raise MNG trap */ + /* raise NEST trap */ + raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_NEST, GETPC()); } env->PC = env->gpr_a[11] & ~0x1; /* ICR.IE = PCXI.PIE; */ @@ -2592,6 +2696,7 @@ void helper_svlcx(CPUTriCoreState *env) if (env->FCX == 0) { /* FCU trap */ + raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_FCU, GETPC()); } /* tmp_FCX = FCX; */ tmp_FCX = env->FCX; @@ -2622,6 +2727,50 @@ void helper_svlcx(CPUTriCoreState *env) /* if (tmp_FCX == LCX) trap(FCD);*/ if (tmp_FCX == env->LCX) { /* FCD trap */ + raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_FCD, GETPC()); + } +} + +void helper_svucx(CPUTriCoreState *env) +{ + target_ulong tmp_FCX; + target_ulong ea; + target_ulong new_FCX; + + if (env->FCX == 0) { + /* FCU trap */ + raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_FCU, GETPC()); + } + /* tmp_FCX = FCX; */ + tmp_FCX = env->FCX; + /* EA = {FCX.FCXS, 6'b0, FCX.FCXO, 6'b0}; */ + ea = ((env->FCX & MASK_FCX_FCXS) << 12) + + ((env->FCX & MASK_FCX_FCXO) << 6); + /* new_FCX = M(EA, word); */ + new_FCX = cpu_ldl_data(env, ea); + /* M(EA, 16 * word) = {PCXI, PSW, A[10], A[11], D[8], D[9], D[10], D[11], + A[12], A[13], A[14], A[15], D[12], D[13], D[14], + D[15]}; */ + save_context_upper(env, ea); + + /* PCXI.PCPN = ICR.CCPN; */ + env->PCXI = (env->PCXI & 0xffffff) + + ((env->ICR & MASK_ICR_CCPN) << 24); + /* PCXI.PIE = ICR.IE; */ + env->PCXI = ((env->PCXI & ~MASK_PCXI_PIE) + + ((env->ICR & MASK_ICR_IE) << 15)); + /* PCXI.UL = 1; */ + env->PCXI |= MASK_PCXI_UL; + + /* PCXI[19: 0] = FCX[19: 0]; */ + env->PCXI = (env->PCXI & 0xfff00000) + (env->FCX & 0xfffff); + /* FCX[19: 0] = new_FCX[19: 0]; */ + env->FCX = (env->FCX & 0xfff00000) + (new_FCX & 0xfffff); + + /* if (tmp_FCX == LCX) trap(FCD);*/ + if (tmp_FCX == env->LCX) { + /* FCD trap */ + raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_FCD, GETPC()); } } @@ -2632,10 +2781,12 @@ void helper_rslcx(CPUTriCoreState *env) /* if (PCXI[19: 0] == 0) then trap(CSU); */ if ((env->PCXI & 0xfffff) == 0) { /* CSU trap */ + raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_CSU, GETPC()); } /* if (PCXI.UL == 1) then trap(CTYP); */ if ((env->PCXI & MASK_PCXI_UL) != 0) { /* CTYP trap */ + raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_CTYP, GETPC()); } /* EA = {PCXI.PCXS, 6'b0, PCXI.PCXO, 6'b0}; */ ea = ((env->PCXI & MASK_PCXI_PCXS) << 12) + diff --git a/qemu/target-tricore/translate.c b/qemu/target-tricore/translate.c index 7dc7a325b..912bf226b 100644 --- a/qemu/target-tricore/translate.c +++ b/qemu/target-tricore/translate.c @@ -18,6 +18,7 @@ */ +#include "qemu/osdep.h" #include "cpu.h" #include "disas/disas.h" #include "tcg-op.h" @@ -27,6 +28,7 @@ #include "exec/helper-gen.h" #include "tricore-opcodes.h" +#include "exec/log.h" /* * TCG registers @@ -45,7 +47,7 @@ static TCGv cpu_PSW_SV; static TCGv cpu_PSW_AV; static TCGv cpu_PSW_SAV; /* CPU env */ -static TCGv_ptr cpu_env; +static TCGv_env cpu_env; #include "exec/gen-icount.h" @@ -214,6 +216,15 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f, #define EA_B_ABSOLUT(con) (((offset & 0xf00000) << 8) | \ ((offset & 0x0fffff) << 1)) +/* For two 32-bit registers used a 64-bit register, the first + registernumber needs to be even. Otherwise we trap. */ +static inline void generate_trap(DisasContext *ctx, int class, int tin); +#define CHECK_REG_PAIR(reg) do { \ + if (reg & 0x1) { \ + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_OPD); \ + } \ +} while (0) + /* Functions for load/save to/from memory */ static inline void gen_offset_ld(DisasContext *ctx, TCGv r1, TCGv r2, @@ -299,6 +310,7 @@ static void gen_ldmst(DisasContext *ctx, int ereg, TCGv ea) TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); + CHECK_REG_PAIR(ereg); /* temp = (M(EA, word) */ tcg_gen_qemu_ld_tl(temp, ea, ctx->mem_idx, MO_LEUL); /* temp = temp & ~E[a][63:32]) */ @@ -457,11 +469,11 @@ gen_add64_d(TCGv_i64 ret, TCGv_i64 r1, TCGv_i64 r2) tcg_gen_xor_i64(t1, result, r1); tcg_gen_xor_i64(t0, r1, r2); tcg_gen_andc_i64(t1, t1, t0); - tcg_gen_trunc_shr_i64_i32(cpu_PSW_V, t1, 32); + tcg_gen_extrh_i64_i32(cpu_PSW_V, t1); /* calc SV bit */ tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* calc AV/SAV bits */ - tcg_gen_trunc_shr_i64_i32(temp, result, 32); + tcg_gen_extrh_i64_i32(temp, result); tcg_gen_add_tl(cpu_PSW_AV, temp, temp); tcg_gen_xor_tl(cpu_PSW_AV, temp, cpu_PSW_AV); /* calc SAV */ @@ -540,14 +552,14 @@ static inline void gen_madd32_d(TCGv ret, TCGv r1, TCGv r2, TCGv r3) tcg_gen_mul_i64(t1, t1, t3); tcg_gen_add_i64(t1, t2, t1); - tcg_gen_trunc_i64_i32(ret, t1); + tcg_gen_extrl_i64_i32(ret, t1); /* calc V t1 > 0x7fffffff */ tcg_gen_setcondi_i64(TCG_COND_GT, t3, t1, 0x7fffffffLL); /* t1 < -0x80000000 */ tcg_gen_setcondi_i64(TCG_COND_LT, t2, t1, -0x80000000LL); tcg_gen_or_i64(t2, t2, t3); - tcg_gen_trunc_i64_i32(cpu_PSW_V, t2); + tcg_gen_extrl_i64_i32(cpu_PSW_V, t2); tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); /* Calc SV bit */ tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); @@ -621,7 +633,7 @@ gen_maddu64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, /* only the add overflows, if t2 < t1 calc V bit */ tcg_gen_setcond_i64(TCG_COND_LTU, t2, t2, t1); - tcg_gen_trunc_i64_i32(cpu_PSW_V, t2); + tcg_gen_extrl_i64_i32(cpu_PSW_V, t2); tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); /* Calc SV bit */ tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); @@ -1110,12 +1122,12 @@ gen_madd32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, tcg_gen_sari_i64(t2, t2, up_shift); tcg_gen_add_i64(t3, t1, t2); - tcg_gen_trunc_i64_i32(temp3, t3); + tcg_gen_extrl_i64_i32(temp3, t3); /* calc v bit */ tcg_gen_setcondi_i64(TCG_COND_GT, t1, t3, 0x7fffffffLL); tcg_gen_setcondi_i64(TCG_COND_LT, t2, t3, -0x80000000LL); tcg_gen_or_i64(t1, t1, t2); - tcg_gen_trunc_i64_i32(cpu_PSW_V, t1); + tcg_gen_extrl_i64_i32(cpu_PSW_V, t1); tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); /* We produce an overflow on the host if the mul before was (0x80000000 * 0x80000000) << 1). If this is the @@ -1273,7 +1285,7 @@ gen_madd64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_xor_i64(t3, t4, t1); tcg_gen_xor_i64(t2, t1, t2); tcg_gen_andc_i64(t3, t3, t2); - tcg_gen_trunc_shr_i64_i32(cpu_PSW_V, t3, 32); + tcg_gen_extrh_i64_i32(cpu_PSW_V, t3); /* We produce an overflow on the host if the mul before was (0x80000000 * 0x80000000) << 1). If this is the case, we negate the ovf. */ @@ -1356,14 +1368,14 @@ static inline void gen_msub32_d(TCGv ret, TCGv r1, TCGv r2, TCGv r3) tcg_gen_mul_i64(t1, t1, t3); tcg_gen_sub_i64(t1, t2, t1); - tcg_gen_trunc_i64_i32(ret, t1); + tcg_gen_extrl_i64_i32(ret, t1); /* calc V t2 > 0x7fffffff */ tcg_gen_setcondi_i64(TCG_COND_GT, t3, t1, 0x7fffffffLL); /* result < -0x80000000 */ tcg_gen_setcondi_i64(TCG_COND_LT, t2, t1, -0x80000000LL); tcg_gen_or_i64(t2, t2, t3); - tcg_gen_trunc_i64_i32(cpu_PSW_V, t2); + tcg_gen_extrl_i64_i32(cpu_PSW_V, t2); tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); /* Calc SV bit */ @@ -1445,7 +1457,7 @@ gen_msubu64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, tcg_gen_extr_i64_i32(ret_low, ret_high, t3); /* calc V bit, only the sub can overflow, if t1 > t2 */ tcg_gen_setcond_i64(TCG_COND_GTU, t1, t1, t2); - tcg_gen_trunc_i64_i32(cpu_PSW_V, t1); + tcg_gen_extrl_i64_i32(cpu_PSW_V, t1); tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); /* Calc SV bit */ tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); @@ -1630,11 +1642,11 @@ gen_sub64_d(TCGv_i64 ret, TCGv_i64 r1, TCGv_i64 r2) tcg_gen_xor_i64(t1, result, r1); tcg_gen_xor_i64(t0, r1, r2); tcg_gen_and_i64(t1, t1, t0); - tcg_gen_trunc_shr_i64_i32(cpu_PSW_V, t1, 32); + tcg_gen_extrh_i64_i32(cpu_PSW_V, t1); /* calc SV bit */ tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); /* calc AV/SAV bits */ - tcg_gen_trunc_shr_i64_i32(temp, result, 32); + tcg_gen_extrh_i64_i32(temp, result); tcg_gen_add_tl(cpu_PSW_AV, temp, temp); tcg_gen_xor_tl(cpu_PSW_AV, temp, cpu_PSW_AV); /* calc SAV */ @@ -1973,12 +1985,12 @@ gen_msub32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, tcg_gen_add_i64(t2, t2, t4); tcg_gen_sub_i64(t3, t1, t2); - tcg_gen_trunc_i64_i32(temp3, t3); + tcg_gen_extrl_i64_i32(temp3, t3); /* calc v bit */ tcg_gen_setcondi_i64(TCG_COND_GT, t1, t3, 0x7fffffffLL); tcg_gen_setcondi_i64(TCG_COND_LT, t2, t3, -0x80000000LL); tcg_gen_or_i64(t1, t1, t2); - tcg_gen_trunc_i64_i32(cpu_PSW_V, t1); + tcg_gen_extrl_i64_i32(cpu_PSW_V, t1); tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); /* Calc SV bit */ tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); @@ -2126,7 +2138,7 @@ gen_msub64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_xor_i64(t3, t4, t1); tcg_gen_xor_i64(t2, t1, t2); tcg_gen_and_i64(t3, t3, t2); - tcg_gen_trunc_shr_i64_i32(cpu_PSW_V, t3, 32); + tcg_gen_extrh_i64_i32(cpu_PSW_V, t3); /* We produce an overflow on the host if the mul before was (0x80000000 * 0x80000000) << 1). If this is the case, we negate the ovf. */ @@ -3242,6 +3254,19 @@ static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) } } +static void generate_trap(DisasContext *ctx, int class, int tin) +{ + TCGv_i32 classtemp = tcg_const_i32(class); + TCGv_i32 tintemp = tcg_const_i32(tin); + + gen_save_pc(ctx->pc); + gen_helper_raise_exception_sync(cpu_env, classtemp, tintemp); + ctx->bstate = BS_EXCP; + + tcg_temp_free(classtemp); + tcg_temp_free(tintemp); +} + static inline void gen_branch_cond(DisasContext *ctx, TCGCond cond, TCGv r1, TCGv r2, int16_t address) { @@ -3540,7 +3565,7 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, } break; default: - printf("Branch Error at %x\n", ctx->pc); + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } ctx->bstate = BS_BRANCH; } @@ -3615,7 +3640,9 @@ static void decode_src_opc(CPUTriCoreState *env, DisasContext *ctx, int op1) if (tricore_feature(env, TRICORE_FEATURE_16)) { tcg_gen_movi_tl(cpu_gpr_d[r1], const4); tcg_gen_sari_tl(cpu_gpr_d[r1+1], cpu_gpr_d[r1], 31); - } /* TODO: else raise illegal opcode trap */ + } else { + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); + } break; case OPC1_16_SRC_SH: gen_shi(cpu_gpr_d[r1], cpu_gpr_d[r1], const4); @@ -3623,6 +3650,8 @@ static void decode_src_opc(CPUTriCoreState *env, DisasContext *ctx, int op1) case OPC1_16_SRC_SHA: gen_shaci(cpu_gpr_d[r1], cpu_gpr_d[r1], const4); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -3706,6 +3735,8 @@ static void decode_srr_opc(DisasContext *ctx, int op1) case OPC1_16_SRR_XOR: tcg_gen_xor_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -3745,6 +3776,8 @@ static void decode_ssr_opc(DisasContext *ctx, int op1) tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 4); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -3782,6 +3815,8 @@ static void decode_sc_opc(DisasContext *ctx, int op1) case OPC1_16_SC_SUB_A: tcg_gen_subi_tl(cpu_gpr_a[10], cpu_gpr_a[10], const16); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -3822,6 +3857,8 @@ static void decode_slr_opc(DisasContext *ctx, int op1) tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESL); tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 4); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -3859,6 +3896,8 @@ static void decode_sro_opc(DisasContext *ctx, int op1) case OPC1_16_SRO_ST_W: gen_offset_st(ctx, cpu_gpr_d[15], cpu_gpr_a[r2], address * 4, MO_LESL); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -3883,6 +3922,9 @@ static void decode_sr_system(CPUTriCoreState *env, DisasContext *ctx) break; case OPC2_16_SR_FRET: gen_fret(ctx); + break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -3925,6 +3967,8 @@ static void decode_sr_accu(CPUTriCoreState *env, DisasContext *ctx) case OPC2_16_SR_SAT_HU: gen_saturate_u(cpu_gpr_d[r1], cpu_gpr_d[r1], 0xffff); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -4135,6 +4179,8 @@ static void decode_16Bit_opc(CPUTriCoreState *env, DisasContext *ctx) r1 = MASK_OP_SR_S1D(ctx->opcode); tcg_gen_not_tl(cpu_gpr_d[r1], cpu_gpr_d[r1]); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -4161,14 +4207,18 @@ static void decode_abs_ldw(CPUTriCoreState *env, DisasContext *ctx) tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp, ctx->mem_idx, MO_LESL); break; case OPC2_32_ABS_LD_D: + CHECK_REG_PAIR(r1); gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx); break; case OPC2_32_ABS_LD_DA: + CHECK_REG_PAIR(r1); gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx); break; case OPC2_32_ABS_LD_W: tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LESL); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_temp_free(temp); @@ -4200,6 +4250,8 @@ static void decode_abs_ldb(CPUTriCoreState *env, DisasContext *ctx) case OPC2_32_ABS_LD_HU: tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUW); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_temp_free(temp); @@ -4225,6 +4277,8 @@ static void decode_abs_ldst_swap(CPUTriCoreState *env, DisasContext *ctx) case OPC2_32_ABS_SWAP_W: gen_swap(ctx, r1, temp); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_temp_free(temp); @@ -4251,6 +4305,8 @@ static void decode_abs_ldst_context(CPUTriCoreState *env, DisasContext *ctx) case OPC2_32_ABS_STUCX: gen_helper_1arg(stucx, EA_ABS_FORMAT(off18)); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -4272,15 +4328,18 @@ static void decode_abs_store(CPUTriCoreState *env, DisasContext *ctx) tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp, ctx->mem_idx, MO_LESL); break; case OPC2_32_ABS_ST_D: + CHECK_REG_PAIR(r1); gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx); break; case OPC2_32_ABS_ST_DA: + CHECK_REG_PAIR(r1); gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx); break; case OPC2_32_ABS_ST_W: tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LESL); break; - + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_temp_free(temp); } @@ -4305,6 +4364,8 @@ static void decode_abs_storeb_h(CPUTriCoreState *env, DisasContext *ctx) case OPC2_32_ABS_ST_H: tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUW); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_temp_free(temp); } @@ -4347,6 +4408,8 @@ static void decode_bit_andacc(CPUTriCoreState *env, DisasContext *ctx) gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], pos1, pos2, &tcg_gen_or_tl, &tcg_gen_and_tl); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -4379,6 +4442,8 @@ static void decode_bit_logical_t(CPUTriCoreState *env, DisasContext *ctx) gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], pos1, pos2, &tcg_gen_or_tl); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -4436,6 +4501,8 @@ static void decode_bit_logical_t2(CPUTriCoreState *env, DisasContext *ctx) gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], pos1, pos2, &tcg_gen_xor_tl); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -4475,6 +4542,8 @@ static void decode_bit_orand(CPUTriCoreState *env, DisasContext *ctx) gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], pos1, pos2, &tcg_gen_or_tl, &tcg_gen_or_tl); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -4511,6 +4580,8 @@ static void decode_bit_sh_logic1(CPUTriCoreState *env, DisasContext *ctx) gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2], pos1, pos2, &tcg_gen_or_tl); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], 1); tcg_gen_add_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], temp); @@ -4550,6 +4621,8 @@ static void decode_bit_sh_logic2(CPUTriCoreState *env, DisasContext *ctx) gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2], pos1, pos2, &tcg_gen_xor_tl); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], 1); tcg_gen_add_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], temp); @@ -4594,20 +4667,25 @@ static void decode_bo_addrmode_post_pre_base(CPUTriCoreState *env, break; case OPC2_32_BO_CACHEI_WI_SHORTOFF: case OPC2_32_BO_CACHEI_W_SHORTOFF: - /* TODO: Raise illegal opcode trap, - if !tricore_feature(TRICORE_FEATURE_131) */ + if (!tricore_feature(env, TRICORE_FEATURE_131)) { + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); + } break; case OPC2_32_BO_CACHEI_W_POSTINC: case OPC2_32_BO_CACHEI_WI_POSTINC: if (tricore_feature(env, TRICORE_FEATURE_131)) { tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); - } /* TODO: else raise illegal opcode trap */ + } else { + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); + } break; case OPC2_32_BO_CACHEI_W_PREINC: case OPC2_32_BO_CACHEI_WI_PREINC: if (tricore_feature(env, TRICORE_FEATURE_131)) { tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); - } /* TODO: else raise illegal opcode trap */ + } else { + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); + } break; case OPC2_32_BO_ST_A_SHORTOFF: gen_offset_st(ctx, cpu_gpr_a[r1], cpu_gpr_a[r2], off10, MO_LESL); @@ -4632,14 +4710,17 @@ static void decode_bo_addrmode_post_pre_base(CPUTriCoreState *env, gen_st_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_UB); break; case OPC2_32_BO_ST_D_SHORTOFF: + CHECK_REG_PAIR(r1); gen_offset_st_2regs(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2], off10, ctx); break; case OPC2_32_BO_ST_D_POSTINC: + CHECK_REG_PAIR(r1); gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2], ctx); tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_ST_D_PREINC: + CHECK_REG_PAIR(r1); temp = tcg_temp_new(); tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx); @@ -4647,14 +4728,17 @@ static void decode_bo_addrmode_post_pre_base(CPUTriCoreState *env, tcg_temp_free(temp); break; case OPC2_32_BO_ST_DA_SHORTOFF: + CHECK_REG_PAIR(r1); gen_offset_st_2regs(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2], off10, ctx); break; case OPC2_32_BO_ST_DA_POSTINC: + CHECK_REG_PAIR(r1); gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2], ctx); tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_ST_DA_PREINC: + CHECK_REG_PAIR(r1); temp = tcg_temp_new(); tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx); @@ -4703,6 +4787,8 @@ static void decode_bo_addrmode_post_pre_base(CPUTriCoreState *env, case OPC2_32_BO_ST_W_PREINC: gen_st_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUL); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -4722,7 +4808,7 @@ static void decode_bo_addrmode_bitreverse_circular(CPUTriCoreState *env, temp = tcg_temp_new(); temp2 = tcg_temp_new(); temp3 = tcg_const_i32(off10); - + CHECK_REG_PAIR(r2); tcg_gen_ext16u_tl(temp, cpu_gpr_a[r2+1]); tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); @@ -4754,10 +4840,12 @@ static void decode_bo_addrmode_bitreverse_circular(CPUTriCoreState *env, gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; case OPC2_32_BO_ST_D_BR: + CHECK_REG_PAIR(r1); gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp2, ctx); gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; case OPC2_32_BO_ST_D_CIRC: + CHECK_REG_PAIR(r1); tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16); tcg_gen_addi_tl(temp, temp, 4); @@ -4767,10 +4855,12 @@ static void decode_bo_addrmode_bitreverse_circular(CPUTriCoreState *env, gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; case OPC2_32_BO_ST_DA_BR: + CHECK_REG_PAIR(r1); gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp2, ctx); gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; case OPC2_32_BO_ST_DA_CIRC: + CHECK_REG_PAIR(r1); tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16); tcg_gen_addi_tl(temp, temp, 4); @@ -4805,6 +4895,8 @@ static void decode_bo_addrmode_bitreverse_circular(CPUTriCoreState *env, tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_temp_free(temp); tcg_temp_free(temp2); @@ -4859,14 +4951,17 @@ static void decode_bo_addrmode_ld_post_pre_base(CPUTriCoreState *env, gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_SB); break; case OPC2_32_BO_LD_D_SHORTOFF: + CHECK_REG_PAIR(r1); gen_offset_ld_2regs(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2], off10, ctx); break; case OPC2_32_BO_LD_D_POSTINC: + CHECK_REG_PAIR(r1); gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2], ctx); tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_LD_D_PREINC: + CHECK_REG_PAIR(r1); temp = tcg_temp_new(); tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx); @@ -4874,14 +4969,17 @@ static void decode_bo_addrmode_ld_post_pre_base(CPUTriCoreState *env, tcg_temp_free(temp); break; case OPC2_32_BO_LD_DA_SHORTOFF: + CHECK_REG_PAIR(r1); gen_offset_ld_2regs(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2], off10, ctx); break; case OPC2_32_BO_LD_DA_POSTINC: + CHECK_REG_PAIR(r1); gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2], ctx); tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_LD_DA_PREINC: + CHECK_REG_PAIR(r1); temp = tcg_temp_new(); tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx); @@ -4935,6 +5033,8 @@ static void decode_bo_addrmode_ld_post_pre_base(CPUTriCoreState *env, case OPC2_32_BO_LD_W_PREINC: gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUL); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -4955,7 +5055,7 @@ static void decode_bo_addrmode_ld_bitreverse_circular(CPUTriCoreState *env, temp = tcg_temp_new(); temp2 = tcg_temp_new(); temp3 = tcg_const_i32(off10); - + CHECK_REG_PAIR(r2); tcg_gen_ext16u_tl(temp, cpu_gpr_a[r2+1]); tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); @@ -4986,10 +5086,12 @@ static void decode_bo_addrmode_ld_bitreverse_circular(CPUTriCoreState *env, gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; case OPC2_32_BO_LD_D_BR: + CHECK_REG_PAIR(r1); gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp2, ctx); gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; case OPC2_32_BO_LD_D_CIRC: + CHECK_REG_PAIR(r1); tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16); tcg_gen_addi_tl(temp, temp, 4); @@ -4999,10 +5101,12 @@ static void decode_bo_addrmode_ld_bitreverse_circular(CPUTriCoreState *env, gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; case OPC2_32_BO_LD_DA_BR: + CHECK_REG_PAIR(r1); gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp2, ctx); gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; case OPC2_32_BO_LD_DA_CIRC: + CHECK_REG_PAIR(r1); tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16); tcg_gen_addi_tl(temp, temp, 4); @@ -5045,6 +5149,8 @@ static void decode_bo_addrmode_ld_bitreverse_circular(CPUTriCoreState *env, tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_temp_free(temp); tcg_temp_free(temp2); @@ -5137,6 +5243,8 @@ static void decode_bo_addrmode_stctx_post_pre_base(CPUTriCoreState *env, tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); gen_swapmsk(ctx, r1, cpu_gpr_a[r2]); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_temp_free(temp); tcg_temp_free(temp2); @@ -5159,7 +5267,7 @@ static void decode_bo_addrmode_ldmst_bitreverse_circular(CPUTriCoreState *env, temp = tcg_temp_new(); temp2 = tcg_temp_new(); temp3 = tcg_const_i32(off10); - + CHECK_REG_PAIR(r2); tcg_gen_ext16u_tl(temp, cpu_gpr_a[r2+1]); tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); @@ -5196,6 +5304,8 @@ static void decode_bo_addrmode_ldmst_bitreverse_circular(CPUTriCoreState *env, gen_swapmsk(ctx, r1, temp2); gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_temp_free(temp); @@ -5233,7 +5343,7 @@ static void decode_bol_opc(CPUTriCoreState *env, DisasContext *ctx, int32_t op1) if (tricore_feature(env, TRICORE_FEATURE_16)) { gen_offset_st(ctx, cpu_gpr_a[r1], cpu_gpr_a[r2], address, MO_LEUL); } else { - /* raise illegal opcode trap */ + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } break; case OPC1_32_BOL_ST_W_LONGOFF: @@ -5243,44 +5353,46 @@ static void decode_bol_opc(CPUTriCoreState *env, DisasContext *ctx, int32_t op1) if (tricore_feature(env, TRICORE_FEATURE_16)) { gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], address, MO_SB); } else { - /* raise illegal opcode trap */ + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } break; case OPC1_32_BOL_LD_BU_LONGOFF: if (tricore_feature(env, TRICORE_FEATURE_16)) { gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], address, MO_UB); } else { - /* raise illegal opcode trap */ + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } break; case OPC1_32_BOL_LD_H_LONGOFF: if (tricore_feature(env, TRICORE_FEATURE_16)) { gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], address, MO_LESW); } else { - /* raise illegal opcode trap */ + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } break; case OPC1_32_BOL_LD_HU_LONGOFF: if (tricore_feature(env, TRICORE_FEATURE_16)) { gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], address, MO_LEUW); } else { - /* raise illegal opcode trap */ + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } break; case OPC1_32_BOL_ST_B_LONGOFF: if (tricore_feature(env, TRICORE_FEATURE_16)) { gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], address, MO_SB); } else { - /* raise illegal opcode trap */ + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } break; case OPC1_32_BOL_ST_H_LONGOFF: if (tricore_feature(env, TRICORE_FEATURE_16)) { gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], address, MO_LESW); } else { - /* raise illegal opcode trap */ + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -5346,6 +5458,8 @@ static void decode_rc_logical_shift(CPUTriCoreState *env, DisasContext *ctx) case OPC2_32_RC_XOR: tcg_gen_xori_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_temp_free(temp); } @@ -5544,6 +5658,8 @@ static void decode_rc_accumulator(CPUTriCoreState *env, DisasContext *ctx) gen_accumulating_condi(TCG_COND_NE, cpu_gpr_d[r2], cpu_gpr_d[r1], const9, &tcg_gen_xor_tl); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_temp_free(temp); } @@ -5563,6 +5679,8 @@ static void decode_rc_serviceroutine(CPUTriCoreState *env, DisasContext *ctx) case OPC2_32_RC_SYSCALL: /* TODO: Add exception generation */ break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -5583,6 +5701,7 @@ static void decode_rc_mul(CPUTriCoreState *env, DisasContext *ctx) gen_muli_i32s(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; case OPC2_32_RC_MUL_64: + CHECK_REG_PAIR(r2); gen_muli_i64s(cpu_gpr_d[r2], cpu_gpr_d[r2+1], cpu_gpr_d[r1], const9); break; case OPC2_32_RC_MULS_32: @@ -5590,12 +5709,15 @@ static void decode_rc_mul(CPUTriCoreState *env, DisasContext *ctx) break; case OPC2_32_RC_MUL_U_64: const9 = MASK_OP_RC_CONST9(ctx->opcode); + CHECK_REG_PAIR(r2); gen_muli_i64u(cpu_gpr_d[r2], cpu_gpr_d[r2+1], cpu_gpr_d[r1], const9); break; case OPC2_32_RC_MULS_U_32: const9 = MASK_OP_RC_CONST9(ctx->opcode); gen_mulsui_i32(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -5617,6 +5739,7 @@ static void decode_rcpw_insert(CPUTriCoreState *env, DisasContext *ctx) switch (op2) { case OPC2_32_RCPW_IMASK: + CHECK_REG_PAIR(r2); /* if pos + width > 31 undefined result */ if (pos + width <= 31) { tcg_gen_movi_tl(cpu_gpr_d[r2+1], ((1u << width) - 1) << pos); @@ -5631,6 +5754,8 @@ static void decode_rcpw_insert(CPUTriCoreState *env, DisasContext *ctx) tcg_temp_free(temp); } break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -5672,6 +5797,8 @@ static void decode_rcrw_insert(CPUTriCoreState *env, DisasContext *ctx) tcg_temp_free(temp3); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_temp_free(temp); tcg_temp_free(temp2); @@ -5718,6 +5845,8 @@ static void decode_rcr_cond_select(CPUTriCoreState *env, DisasContext *ctx) tcg_temp_free(temp); tcg_temp_free(temp2); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -5739,6 +5868,8 @@ static void decode_rcr_madd(CPUTriCoreState *env, DisasContext *ctx) gen_maddi32_d(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], const9); break; case OPC2_32_RCR_MADD_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddi64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); break; @@ -5746,10 +5877,14 @@ static void decode_rcr_madd(CPUTriCoreState *env, DisasContext *ctx) gen_maddsi_32(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], const9); break; case OPC2_32_RCR_MADDS_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddsi_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); break; case OPC2_32_RCR_MADD_U_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); const9 = MASK_OP_RCR_CONST9(ctx->opcode); gen_maddui64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); @@ -5759,10 +5894,14 @@ static void decode_rcr_madd(CPUTriCoreState *env, DisasContext *ctx) gen_maddsui_32(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], const9); break; case OPC2_32_RCR_MADDS_U_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); const9 = MASK_OP_RCR_CONST9(ctx->opcode); gen_maddsui_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -5784,6 +5923,8 @@ static void decode_rcr_msub(CPUTriCoreState *env, DisasContext *ctx) gen_msubi32_d(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], const9); break; case OPC2_32_RCR_MSUB_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubi64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); break; @@ -5791,10 +5932,14 @@ static void decode_rcr_msub(CPUTriCoreState *env, DisasContext *ctx) gen_msubsi_32(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], const9); break; case OPC2_32_RCR_MSUBS_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubsi_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); break; case OPC2_32_RCR_MSUB_U_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); const9 = MASK_OP_RCR_CONST9(ctx->opcode); gen_msubui64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); @@ -5804,10 +5949,14 @@ static void decode_rcr_msub(CPUTriCoreState *env, DisasContext *ctx) gen_msubsui_32(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], const9); break; case OPC2_32_RCR_MSUBS_U_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); const9 = MASK_OP_RCR_CONST9(ctx->opcode); gen_msubsui_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -5842,13 +5991,11 @@ static void decode_rlc_opc(CPUTriCoreState *env, DisasContext *ctx, break; case OPC1_32_RLC_MOV_64: if (tricore_feature(env, TRICORE_FEATURE_16)) { - if ((r2 & 0x1) != 0) { - /* TODO: raise OPD trap */ - } + CHECK_REG_PAIR(r2); tcg_gen_movi_tl(cpu_gpr_d[r2], const16); tcg_gen_movi_tl(cpu_gpr_d[r2+1], const16 >> 15); } else { - /* TODO: raise illegal opcode trap */ + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } break; case OPC1_32_RLC_MOV_U: @@ -5865,6 +6012,8 @@ static void decode_rlc_opc(CPUTriCoreState *env, DisasContext *ctx, const16 = MASK_OP_RLC_CONST16(ctx->opcode); gen_mtcr(env, ctx, cpu_gpr_d[r1], const16); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -6181,6 +6330,8 @@ static void decode_rr_accumulator(CPUTriCoreState *env, DisasContext *ctx) gen_accumulating_cond(TCG_COND_NE, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], &tcg_gen_xor_tl); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -6255,6 +6406,8 @@ static void decode_rr_logical_shift(CPUTriCoreState *env, DisasContext *ctx) case OPC2_32_RR_XOR: tcg_gen_xor_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_temp_free(temp); } @@ -6322,6 +6475,8 @@ static void decode_rr_address(CPUTriCoreState *env, DisasContext *ctx) case OPC2_32_RR_SUB_A: tcg_gen_sub_tl(cpu_gpr_a[r3], cpu_gpr_a[r1], cpu_gpr_a[r2]); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -6349,6 +6504,8 @@ static void decode_rr_idirect(CPUTriCoreState *env, DisasContext *ctx) gen_fcall_save_ctx(ctx); tcg_gen_andi_tl(cpu_PC, cpu_gpr_a[r1], ~0x1); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_gen_exit_tb(0); ctx->bstate = BS_BRANCH; @@ -6371,9 +6528,11 @@ static void decode_rr_divide(CPUTriCoreState *env, DisasContext *ctx) gen_helper_bmerge(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_BSPLIT: + CHECK_REG_PAIR(r3); gen_bsplit(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1]); break; case OPC2_32_RR_DVINIT_B: + CHECK_REG_PAIR(r3); gen_dvinit_b(env, cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; @@ -6381,7 +6540,7 @@ static void decode_rr_divide(CPUTriCoreState *env, DisasContext *ctx) temp = tcg_temp_new(); temp2 = tcg_temp_new(); temp3 = tcg_temp_new(); - + CHECK_REG_PAIR(r3); tcg_gen_shri_tl(temp3, cpu_gpr_d[r1], 8); /* reset av */ tcg_gen_movi_tl(cpu_PSW_AV, 0); @@ -6411,6 +6570,7 @@ static void decode_rr_divide(CPUTriCoreState *env, DisasContext *ctx) tcg_temp_free(temp3); break; case OPC2_32_RR_DVINIT_H: + CHECK_REG_PAIR(r3); gen_dvinit_h(env, cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; @@ -6418,7 +6578,7 @@ static void decode_rr_divide(CPUTriCoreState *env, DisasContext *ctx) temp = tcg_temp_new(); temp2 = tcg_temp_new(); temp3 = tcg_temp_new(); - + CHECK_REG_PAIR(r3); tcg_gen_shri_tl(temp3, cpu_gpr_d[r1], 16); /* reset av */ tcg_gen_movi_tl(cpu_PSW_AV, 0); @@ -6449,6 +6609,7 @@ static void decode_rr_divide(CPUTriCoreState *env, DisasContext *ctx) case OPC2_32_RR_DVINIT: temp = tcg_temp_new(); temp2 = tcg_temp_new(); + CHECK_REG_PAIR(r3); /* overflow = ((D[b] == 0) || ((D[b] == 0xFFFFFFFF) && (D[a] == 0x80000000))) */ tcg_gen_setcondi_tl(TCG_COND_EQ, temp, cpu_gpr_d[r2], 0xffffffff); @@ -6485,25 +6646,49 @@ static void decode_rr_divide(CPUTriCoreState *env, DisasContext *ctx) gen_helper_parity(cpu_gpr_d[r3], cpu_gpr_d[r1]); break; case OPC2_32_RR_UNPACK: + CHECK_REG_PAIR(r3); gen_unpack(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1]); break; case OPC2_32_RR_CRC32: if (tricore_feature(env, TRICORE_FEATURE_161)) { gen_helper_crc32(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); - } /* TODO: else raise illegal opcode trap */ + } else { + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); + } break; case OPC2_32_RR_DIV: if (tricore_feature(env, TRICORE_FEATURE_16)) { GEN_HELPER_RR(divide, cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2]); - } /* TODO: else raise illegal opcode trap */ + } else { + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); + } break; case OPC2_32_RR_DIV_U: if (tricore_feature(env, TRICORE_FEATURE_16)) { GEN_HELPER_RR(divide_u, cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2]); - } /* TODO: else raise illegal opcode trap */ + } else { + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); + } + break; + case OPC2_32_RR_MUL_F: + gen_helper_fmul(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; + case OPC2_32_RR_DIV_F: + gen_helper_fdiv(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_CMP_F: + gen_helper_fcmp(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_FTOI: + gen_helper_ftoi(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1]); + break; + case OPC2_32_RR_ITOF: + gen_helper_itof(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1]); + break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -6525,6 +6710,7 @@ static void decode_rr1_mul(CPUTriCoreState *env, DisasContext *ctx) switch (op2) { case OPC2_32_RR1_MUL_H_32_LL: temp64 = tcg_temp_new_i64(); + CHECK_REG_PAIR(r3); GEN_HELPER_LL(mul_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3+1]); @@ -6532,6 +6718,7 @@ static void decode_rr1_mul(CPUTriCoreState *env, DisasContext *ctx) break; case OPC2_32_RR1_MUL_H_32_LU: temp64 = tcg_temp_new_i64(); + CHECK_REG_PAIR(r3); GEN_HELPER_LU(mul_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3+1]); @@ -6539,6 +6726,7 @@ static void decode_rr1_mul(CPUTriCoreState *env, DisasContext *ctx) break; case OPC2_32_RR1_MUL_H_32_UL: temp64 = tcg_temp_new_i64(); + CHECK_REG_PAIR(r3); GEN_HELPER_UL(mul_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3+1]); @@ -6546,6 +6734,7 @@ static void decode_rr1_mul(CPUTriCoreState *env, DisasContext *ctx) break; case OPC2_32_RR1_MUL_H_32_UU: temp64 = tcg_temp_new_i64(); + CHECK_REG_PAIR(r3); GEN_HELPER_UU(mul_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3+1]); @@ -6553,6 +6742,7 @@ static void decode_rr1_mul(CPUTriCoreState *env, DisasContext *ctx) break; case OPC2_32_RR1_MULM_H_64_LL: temp64 = tcg_temp_new_i64(); + CHECK_REG_PAIR(r3); GEN_HELPER_LL(mulm_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); /* reset V bit */ @@ -6563,6 +6753,7 @@ static void decode_rr1_mul(CPUTriCoreState *env, DisasContext *ctx) break; case OPC2_32_RR1_MULM_H_64_LU: temp64 = tcg_temp_new_i64(); + CHECK_REG_PAIR(r3); GEN_HELPER_LU(mulm_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); /* reset V bit */ @@ -6573,6 +6764,7 @@ static void decode_rr1_mul(CPUTriCoreState *env, DisasContext *ctx) break; case OPC2_32_RR1_MULM_H_64_UL: temp64 = tcg_temp_new_i64(); + CHECK_REG_PAIR(r3); GEN_HELPER_UL(mulm_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); /* reset V bit */ @@ -6583,6 +6775,7 @@ static void decode_rr1_mul(CPUTriCoreState *env, DisasContext *ctx) break; case OPC2_32_RR1_MULM_H_64_UU: temp64 = tcg_temp_new_i64(); + CHECK_REG_PAIR(r3); GEN_HELPER_UU(mulm_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); /* reset V bit */ @@ -6608,6 +6801,8 @@ static void decode_rr1_mul(CPUTriCoreState *env, DisasContext *ctx) GEN_HELPER_UU(mulr_h, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], n); gen_calc_usb_mulr_h(cpu_gpr_d[r3]); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_temp_free(n); } @@ -6634,6 +6829,7 @@ static void decode_rr1_mulq(CPUTriCoreState *env, DisasContext *ctx) gen_mul_q(cpu_gpr_d[r3], temp, cpu_gpr_d[r1], cpu_gpr_d[r2], n, 32); break; case OPC2_32_RR1_MUL_Q_64: + CHECK_REG_PAIR(r3); gen_mul_q(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, 0); break; @@ -6642,6 +6838,7 @@ static void decode_rr1_mulq(CPUTriCoreState *env, DisasContext *ctx) gen_mul_q(cpu_gpr_d[r3], temp, cpu_gpr_d[r1], temp, n, 16); break; case OPC2_32_RR1_MUL_Q_64_L: + CHECK_REG_PAIR(r3); tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); gen_mul_q(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, n, 0); break; @@ -6650,6 +6847,7 @@ static void decode_rr1_mulq(CPUTriCoreState *env, DisasContext *ctx) gen_mul_q(cpu_gpr_d[r3], temp, cpu_gpr_d[r1], temp, n, 16); break; case OPC2_32_RR1_MUL_Q_64_U: + CHECK_REG_PAIR(r3); tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); gen_mul_q(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, n, 0); break; @@ -6673,6 +6871,8 @@ static void decode_rr1_mulq(CPUTriCoreState *env, DisasContext *ctx) tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); gen_mulr_q(cpu_gpr_d[r3], temp, temp2, n); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_temp_free(temp); tcg_temp_free(temp2); @@ -6693,6 +6893,7 @@ static void decode_rr2_mul(CPUTriCoreState *env, DisasContext *ctx) gen_mul_i32s(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR2_MUL_64: + CHECK_REG_PAIR(r3); gen_mul_i64s(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; @@ -6701,6 +6902,7 @@ static void decode_rr2_mul(CPUTriCoreState *env, DisasContext *ctx) cpu_gpr_d[r2]); break; case OPC2_32_RR2_MUL_U_64: + CHECK_REG_PAIR(r3); gen_mul_i64u(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; @@ -6708,6 +6910,8 @@ static void decode_rr2_mul(CPUTriCoreState *env, DisasContext *ctx) gen_helper_mul_suov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -6748,6 +6952,7 @@ static void decode_rrpw_extract_insert(CPUTriCoreState *env, DisasContext *ctx) } break; case OPC2_32_RRPW_IMASK: + CHECK_REG_PAIR(r3); if (pos + width <= 31) { tcg_gen_movi_tl(cpu_gpr_d[r3+1], ((1u << width) - 1) << pos); tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r2], pos); @@ -6759,6 +6964,8 @@ static void decode_rrpw_extract_insert(CPUTriCoreState *env, DisasContext *ctx) width, pos); } break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -6804,6 +7011,8 @@ static void decode_rrr_cond_select(CPUTriCoreState *env, DisasContext *ctx) cpu_gpr_d[r1], cpu_gpr_d[r2]); tcg_temp_free(temp); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -6821,37 +7030,60 @@ static void decode_rrr_divide(CPUTriCoreState *env, DisasContext *ctx) switch (op2) { case OPC2_32_RRR_DVADJ: + CHECK_REG_PAIR(r3); + CHECK_REG_PAIR(r4); GEN_HELPER_RRR(dvadj, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_DVSTEP: + CHECK_REG_PAIR(r3); + CHECK_REG_PAIR(r4); GEN_HELPER_RRR(dvstep, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_DVSTEP_U: + CHECK_REG_PAIR(r3); + CHECK_REG_PAIR(r4); GEN_HELPER_RRR(dvstep_u, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_IXMAX: + CHECK_REG_PAIR(r3); + CHECK_REG_PAIR(r4); GEN_HELPER_RRR(ixmax, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_IXMAX_U: + CHECK_REG_PAIR(r3); + CHECK_REG_PAIR(r4); GEN_HELPER_RRR(ixmax_u, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_IXMIN: + CHECK_REG_PAIR(r3); + CHECK_REG_PAIR(r4); GEN_HELPER_RRR(ixmin, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_IXMIN_U: + CHECK_REG_PAIR(r3); + CHECK_REG_PAIR(r4); GEN_HELPER_RRR(ixmin_u, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_PACK: + CHECK_REG_PAIR(r3); gen_helper_pack(cpu_gpr_d[r4], cpu_PSW_C, cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1]); break; + case OPC2_32_RRR_ADD_F: + gen_helper_fadd(cpu_gpr_d[r4], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r3]); + break; + case OPC2_32_RRR_SUB_F: + gen_helper_fsub(cpu_gpr_d[r4], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r3]); + break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -6872,6 +7104,8 @@ static void decode_rrr2_madd(CPUTriCoreState *env, DisasContext *ctx) cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MADD_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_madd64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; @@ -6880,10 +7114,14 @@ static void decode_rrr2_madd(CPUTriCoreState *env, DisasContext *ctx) cpu_gpr_d[r3], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MADDS_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_madds_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MADD_U_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddu64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; @@ -6892,9 +7130,13 @@ static void decode_rrr2_madd(CPUTriCoreState *env, DisasContext *ctx) cpu_gpr_d[r3], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MADDS_U_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddsu_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -6915,6 +7157,8 @@ static void decode_rrr2_msub(CPUTriCoreState *env, DisasContext *ctx) cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MSUB_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msub64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; @@ -6923,6 +7167,8 @@ static void decode_rrr2_msub(CPUTriCoreState *env, DisasContext *ctx) cpu_gpr_d[r3], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MSUBS_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubs_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; @@ -6935,9 +7181,13 @@ static void decode_rrr2_msub(CPUTriCoreState *env, DisasContext *ctx) cpu_gpr_d[r3], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MSUBS_U_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubsu_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -6956,66 +7206,98 @@ static void decode_rrr1_madd(CPUTriCoreState *env, DisasContext *ctx) switch (op2) { case OPC2_32_RRR1_MADD_H_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_madd_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MADD_H_LU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_madd_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MADD_H_UL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_madd_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MADD_H_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_madd_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MADDS_H_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_madds_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MADDS_H_LU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_madds_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MADDS_H_UL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_madds_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MADDS_H_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_madds_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MADDM_H_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MADDM_H_LU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MADDM_H_UL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MADDM_H_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MADDMS_H_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MADDMS_H_LU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MADDMS_H_UL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MADDMS_H_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; @@ -7051,6 +7333,8 @@ static void decode_rrr1_madd(CPUTriCoreState *env, DisasContext *ctx) gen_maddr32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -7076,6 +7360,8 @@ static void decode_rrr1_maddq_h(CPUTriCoreState *env, DisasContext *ctx) cpu_gpr_d[r2], n, 32, env); break; case OPC2_32_RRR1_MADD_Q_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_madd64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, env); @@ -7086,6 +7372,8 @@ static void decode_rrr1_maddq_h(CPUTriCoreState *env, DisasContext *ctx) temp, n, 16, env); break; case OPC2_32_RRR1_MADD_Q_64_L: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); gen_madd64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, @@ -7097,6 +7385,8 @@ static void decode_rrr1_maddq_h(CPUTriCoreState *env, DisasContext *ctx) temp, n, 16, env); break; case OPC2_32_RRR1_MADD_Q_64_U: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); gen_madd64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, @@ -7108,6 +7398,8 @@ static void decode_rrr1_maddq_h(CPUTriCoreState *env, DisasContext *ctx) gen_m16add32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MADD_Q_64_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); gen_m16add64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], @@ -7119,6 +7411,8 @@ static void decode_rrr1_maddq_h(CPUTriCoreState *env, DisasContext *ctx) gen_m16add32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MADD_Q_64_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); gen_m16add64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], @@ -7129,6 +7423,8 @@ static void decode_rrr1_maddq_h(CPUTriCoreState *env, DisasContext *ctx) cpu_gpr_d[r2], n, 32); break; case OPC2_32_RRR1_MADDS_Q_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_madds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n); @@ -7139,6 +7435,8 @@ static void decode_rrr1_maddq_h(CPUTriCoreState *env, DisasContext *ctx) temp, n, 16); break; case OPC2_32_RRR1_MADDS_Q_64_L: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); gen_madds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, @@ -7150,6 +7448,8 @@ static void decode_rrr1_maddq_h(CPUTriCoreState *env, DisasContext *ctx) temp, n, 16); break; case OPC2_32_RRR1_MADDS_Q_64_U: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); gen_madds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, @@ -7161,6 +7461,8 @@ static void decode_rrr1_maddq_h(CPUTriCoreState *env, DisasContext *ctx) gen_m16adds32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MADDS_Q_64_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); gen_m16adds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], @@ -7172,16 +7474,20 @@ static void decode_rrr1_maddq_h(CPUTriCoreState *env, DisasContext *ctx) gen_m16adds32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MADDS_Q_64_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); gen_m16adds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp, temp2, n); break; case OPC2_32_RRR1_MADDR_H_64_UL: + CHECK_REG_PAIR(r3); gen_maddr64_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, 2); break; case OPC2_32_RRR1_MADDRS_H_64_UL: + CHECK_REG_PAIR(r3); gen_maddr64s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, 2); break; @@ -7205,6 +7511,8 @@ static void decode_rrr1_maddq_h(CPUTriCoreState *env, DisasContext *ctx) tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); gen_maddrs_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_temp_free(temp); tcg_temp_free(temp2); @@ -7224,77 +7532,109 @@ static void decode_rrr1_maddsu_h(CPUTriCoreState *env, DisasContext *ctx) switch (op2) { case OPC2_32_RRR1_MADDSU_H_32_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddsu_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MADDSU_H_32_LU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddsu_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MADDSU_H_32_UL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddsu_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MADDSU_H_32_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddsu_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MADDSUS_H_32_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddsus_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MADDSUS_H_32_LU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddsus_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MADDSUS_H_32_UL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddsus_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MADDSUS_H_32_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddsus_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MADDSUM_H_64_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddsum_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MADDSUM_H_64_LU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddsum_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MADDSUM_H_64_UL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddsum_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MADDSUM_H_64_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddsum_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MADDSUMS_H_64_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddsums_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MADDSUMS_H_64_LU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddsums_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MADDSUMS_H_64_UL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddsums_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MADDSUMS_H_64_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_maddsums_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); @@ -7331,6 +7671,8 @@ static void decode_rrr1_maddsu_h(CPUTriCoreState *env, DisasContext *ctx) gen_maddsur32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -7348,66 +7690,98 @@ static void decode_rrr1_msub(CPUTriCoreState *env, DisasContext *ctx) switch (op2) { case OPC2_32_RRR1_MSUB_H_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msub_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MSUB_H_LU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msub_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MSUB_H_UL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msub_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MSUB_H_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msub_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MSUBS_H_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubs_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MSUBS_H_LU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubs_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MSUBS_H_UL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubs_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MSUBS_H_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubs_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MSUBM_H_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MSUBM_H_LU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MSUBM_H_UL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MSUBM_H_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MSUBMS_H_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MSUBMS_H_LU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MSUBMS_H_UL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MSUBMS_H_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; @@ -7443,6 +7817,8 @@ static void decode_rrr1_msub(CPUTriCoreState *env, DisasContext *ctx) gen_msubr32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -7468,6 +7844,8 @@ static void decode_rrr1_msubq_h(CPUTriCoreState *env, DisasContext *ctx) cpu_gpr_d[r2], n, 32, env); break; case OPC2_32_RRR1_MSUB_Q_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, env); @@ -7478,6 +7856,8 @@ static void decode_rrr1_msubq_h(CPUTriCoreState *env, DisasContext *ctx) temp, n, 16, env); break; case OPC2_32_RRR1_MSUB_Q_64_L: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); gen_msub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, @@ -7489,6 +7869,8 @@ static void decode_rrr1_msubq_h(CPUTriCoreState *env, DisasContext *ctx) temp, n, 16, env); break; case OPC2_32_RRR1_MSUB_Q_64_U: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); gen_msub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, @@ -7500,6 +7882,8 @@ static void decode_rrr1_msubq_h(CPUTriCoreState *env, DisasContext *ctx) gen_m16sub32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MSUB_Q_64_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); gen_m16sub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], @@ -7511,6 +7895,8 @@ static void decode_rrr1_msubq_h(CPUTriCoreState *env, DisasContext *ctx) gen_m16sub32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MSUB_Q_64_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); gen_m16sub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], @@ -7521,6 +7907,8 @@ static void decode_rrr1_msubq_h(CPUTriCoreState *env, DisasContext *ctx) cpu_gpr_d[r2], n, 32); break; case OPC2_32_RRR1_MSUBS_Q_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n); @@ -7531,6 +7919,8 @@ static void decode_rrr1_msubq_h(CPUTriCoreState *env, DisasContext *ctx) temp, n, 16); break; case OPC2_32_RRR1_MSUBS_Q_64_L: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); gen_msubs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, @@ -7542,6 +7932,8 @@ static void decode_rrr1_msubq_h(CPUTriCoreState *env, DisasContext *ctx) temp, n, 16); break; case OPC2_32_RRR1_MSUBS_Q_64_U: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); gen_msubs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, @@ -7553,6 +7945,8 @@ static void decode_rrr1_msubq_h(CPUTriCoreState *env, DisasContext *ctx) gen_m16subs32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MSUBS_Q_64_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); gen_m16subs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], @@ -7564,16 +7958,20 @@ static void decode_rrr1_msubq_h(CPUTriCoreState *env, DisasContext *ctx) gen_m16subs32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; case OPC2_32_RRR1_MSUBS_Q_64_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); gen_m16subs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp, temp2, n); break; case OPC2_32_RRR1_MSUBR_H_64_UL: + CHECK_REG_PAIR(r3); gen_msubr64_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, 2); break; case OPC2_32_RRR1_MSUBRS_H_64_UL: + CHECK_REG_PAIR(r3); gen_msubr64s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, 2); break; @@ -7597,6 +7995,8 @@ static void decode_rrr1_msubq_h(CPUTriCoreState *env, DisasContext *ctx) tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); gen_msubrs_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_temp_free(temp); tcg_temp_free(temp2); @@ -7616,77 +8016,109 @@ static void decode_rrr1_msubad_h(CPUTriCoreState *env, DisasContext *ctx) switch (op2) { case OPC2_32_RRR1_MSUBAD_H_32_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubad_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MSUBAD_H_32_LU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubad_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MSUBAD_H_32_UL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubad_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MSUBAD_H_32_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubad_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MSUBADS_H_32_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubads_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MSUBADS_H_32_LU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubads_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MSUBADS_H_32_UL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubads_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MSUBADS_H_32_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubads_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MSUBADM_H_64_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubadm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MSUBADM_H_64_LU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubadm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MSUBADM_H_64_UL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubadm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MSUBADM_H_64_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubadm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; case OPC2_32_RRR1_MSUBADMS_H_64_LL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubadms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; case OPC2_32_RRR1_MSUBADMS_H_64_LU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubadms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; case OPC2_32_RRR1_MSUBADMS_H_64_UL: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubadms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; case OPC2_32_RRR1_MSUBADMS_H_64_UU: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubadms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); @@ -7723,6 +8155,8 @@ static void decode_rrr1_msubad_h(CPUTriCoreState *env, DisasContext *ctx) gen_msubadr32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -7756,6 +8190,7 @@ static void decode_rrrr_extract_insert(CPUTriCoreState *env, DisasContext *ctx) break; case OPC2_32_RRRR_EXTR: case OPC2_32_RRRR_EXTR_U: + CHECK_REG_PAIR(r3); tcg_gen_andi_tl(tmp_width, cpu_gpr_d[r3+1], 0x1f); tcg_gen_andi_tl(tmp_pos, cpu_gpr_d[r3], 0x1f); tcg_gen_add_tl(tmp_pos, tmp_pos, tmp_width); @@ -7769,11 +8204,14 @@ static void decode_rrrr_extract_insert(CPUTriCoreState *env, DisasContext *ctx) } break; case OPC2_32_RRRR_INSERT: + CHECK_REG_PAIR(r3); tcg_gen_andi_tl(tmp_width, cpu_gpr_d[r3+1], 0x1f); tcg_gen_andi_tl(tmp_pos, cpu_gpr_d[r3], 0x1f); gen_insert(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r2], tmp_width, tmp_pos); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_temp_free(tmp_pos); tcg_temp_free(tmp_width); @@ -7834,6 +8272,8 @@ static void decode_rrrw_extract_insert(CPUTriCoreState *env, DisasContext *ctx) tcg_temp_free(temp2); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } tcg_temp_free(temp); } @@ -7905,14 +8345,24 @@ static void decode_sys_interrupts(CPUTriCoreState *env, DisasContext *ctx) (ctx->hflags & TRICORE_HFLAG_KUU) == TRICORE_HFLAG_UM1) { tcg_gen_deposit_tl(cpu_ICR, cpu_ICR, cpu_gpr_d[r1], 8, 1); } /* else raise privilege trap */ - } /* else raise illegal opcode trap */ + } else { + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); + } break; case OPC2_32_SYS_TRAPSV: - /* TODO: raise sticky overflow trap */ + l1 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_GE, cpu_PSW_SV, 0, l1); + generate_trap(ctx, TRAPC_ASSERT, TIN5_SOVF); + gen_set_label(l1); break; case OPC2_32_SYS_TRAPV: - /* TODO: raise overflow trap */ + l1 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_GE, cpu_PSW_V, 0, l1); + generate_trap(ctx, TRAPC_ASSERT, TIN5_OVF); + gen_set_label(l1); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -8119,6 +8569,8 @@ static void decode_32Bit_opc(CPUTriCoreState *env, DisasContext *ctx) temp2 = tcg_temp_new(); /* width*/ temp3 = tcg_temp_new(); /* pos */ + CHECK_REG_PAIR(r3); + tcg_gen_andi_tl(temp2, cpu_gpr_d[r3+1], 0x1f); tcg_gen_andi_tl(temp3, cpu_gpr_d[r3], 0x1f); @@ -8207,6 +8659,7 @@ static void decode_32Bit_opc(CPUTriCoreState *env, DisasContext *ctx) break; case OPCM_32_RRR_DIVIDE: decode_rrr_divide(env, ctx); + break; /* RRR2 Format */ case OPCM_32_RRR2_MADD: decode_rrr2_madd(env, ctx); @@ -8236,6 +8689,7 @@ static void decode_32Bit_opc(CPUTriCoreState *env, DisasContext *ctx) /* RRRR format */ case OPCM_32_RRRR_EXTRACT_INSERT: decode_rrrr_extract_insert(env, ctx); + break; /* RRRW format */ case OPCM_32_RRRW_EXTRACT_INSERT: decode_rrrw_extract_insert(env, ctx); @@ -8250,6 +8704,8 @@ static void decode_32Bit_opc(CPUTriCoreState *env, DisasContext *ctx) tcg_gen_mov_tl(cpu_PSW_AV, cpu_PSW_V); tcg_gen_mov_tl(cpu_PSW_SAV, cpu_PSW_V); break; + default: + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } } @@ -8266,43 +8722,44 @@ static void decode_opc(CPUTriCoreState *env, DisasContext *ctx, int *is_branch) } } -static inline void -gen_intermediate_code_internal(TriCoreCPU *cpu, struct TranslationBlock *tb, - int search_pc) +void gen_intermediate_code(CPUTriCoreState *env, struct TranslationBlock *tb) { + TriCoreCPU *cpu = tricore_env_get_cpu(env); CPUState *cs = CPU(cpu); - CPUTriCoreState *env = &cpu->env; DisasContext ctx; target_ulong pc_start; - int num_insns; + int num_insns, max_insns; - if (search_pc) { - qemu_log("search pc %d\n", search_pc); + num_insns = 0; + max_insns = tb->cflags & CF_COUNT_MASK; + if (max_insns == 0) { + max_insns = CF_COUNT_MASK; + } + if (singlestep) { + max_insns = 1; + } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; } - num_insns = 0; pc_start = tb->pc; ctx.pc = pc_start; ctx.saved_pc = -1; ctx.tb = tb; ctx.singlestep_enabled = cs->singlestep_enabled; ctx.bstate = BS_NONE; - ctx.mem_idx = cpu_mmu_index(env); + ctx.mem_idx = cpu_mmu_index(env, false); tcg_clear_temp_count(); gen_tb_start(tb); while (ctx.bstate == BS_NONE) { + tcg_gen_insn_start(ctx.pc); + num_insns++; + ctx.opcode = cpu_ldl_code(env, ctx.pc); decode_opc(env, &ctx, 0); - num_insns++; - - if (tcg_op_buf_full()) { - gen_save_pc(ctx.next_pc); - tcg_gen_exit_tb(0); - break; - } - if (singlestep) { + if (num_insns >= max_insns || tcg_op_buf_full()) { gen_save_pc(ctx.next_pc); tcg_gen_exit_tb(0); break; @@ -8311,12 +8768,9 @@ gen_intermediate_code_internal(TriCoreCPU *cpu, struct TranslationBlock *tb, } gen_tb_end(tb, num_insns); - if (search_pc) { - printf("done_generating search pc\n"); - } else { - tb->size = ctx.pc - pc_start; - tb->icount = num_insns; - } + tb->size = ctx.pc - pc_start; + tb->icount = num_insns; + if (tcg_check_temp_count()) { printf("LEAK at %08x\n", env->PC); } @@ -8331,21 +8785,10 @@ gen_intermediate_code_internal(TriCoreCPU *cpu, struct TranslationBlock *tb, } void -gen_intermediate_code(CPUTriCoreState *env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(tricore_env_get_cpu(env), tb, false); -} - -void -gen_intermediate_code_pc(CPUTriCoreState *env, struct TranslationBlock *tb) -{ - gen_intermediate_code_internal(tricore_env_get_cpu(env), tb, true); -} - -void -restore_state_to_opc(CPUTriCoreState *env, TranslationBlock *tb, int pc_pos) +restore_state_to_opc(CPUTriCoreState *env, TranslationBlock *tb, + target_ulong *data) { - env->PC = tcg_ctx.gen_opc_pc[pc_pos]; + env->PC = data[0]; } /* * @@ -8357,17 +8800,18 @@ void cpu_state_reset(CPUTriCoreState *env) { /* Reset Regs to Default Value */ env->PSW = 0xb80; + fpu_set_state(env); } static void tricore_tcg_init_csfr(void) { - cpu_PCXI = tcg_global_mem_new(TCG_AREG0, + cpu_PCXI = tcg_global_mem_new(cpu_env, offsetof(CPUTriCoreState, PCXI), "PCXI"); - cpu_PSW = tcg_global_mem_new(TCG_AREG0, + cpu_PSW = tcg_global_mem_new(cpu_env, offsetof(CPUTriCoreState, PSW), "PSW"); - cpu_PC = tcg_global_mem_new(TCG_AREG0, + cpu_PC = tcg_global_mem_new(cpu_env, offsetof(CPUTriCoreState, PC), "PC"); - cpu_ICR = tcg_global_mem_new(TCG_AREG0, + cpu_ICR = tcg_global_mem_new(cpu_env, offsetof(CPUTriCoreState, ICR), "ICR"); } @@ -8381,30 +8825,30 @@ void tricore_tcg_init(void) cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); /* reg init */ for (i = 0 ; i < 16 ; i++) { - cpu_gpr_a[i] = tcg_global_mem_new(TCG_AREG0, + cpu_gpr_a[i] = tcg_global_mem_new(cpu_env, offsetof(CPUTriCoreState, gpr_a[i]), regnames_a[i]); } for (i = 0 ; i < 16 ; i++) { - cpu_gpr_d[i] = tcg_global_mem_new(TCG_AREG0, + cpu_gpr_d[i] = tcg_global_mem_new(cpu_env, offsetof(CPUTriCoreState, gpr_d[i]), regnames_d[i]); } tricore_tcg_init_csfr(); /* init PSW flag cache */ - cpu_PSW_C = tcg_global_mem_new(TCG_AREG0, + cpu_PSW_C = tcg_global_mem_new(cpu_env, offsetof(CPUTriCoreState, PSW_USB_C), "PSW_C"); - cpu_PSW_V = tcg_global_mem_new(TCG_AREG0, + cpu_PSW_V = tcg_global_mem_new(cpu_env, offsetof(CPUTriCoreState, PSW_USB_V), "PSW_V"); - cpu_PSW_SV = tcg_global_mem_new(TCG_AREG0, + cpu_PSW_SV = tcg_global_mem_new(cpu_env, offsetof(CPUTriCoreState, PSW_USB_SV), "PSW_SV"); - cpu_PSW_AV = tcg_global_mem_new(TCG_AREG0, + cpu_PSW_AV = tcg_global_mem_new(cpu_env, offsetof(CPUTriCoreState, PSW_USB_AV), "PSW_AV"); - cpu_PSW_SAV = tcg_global_mem_new(TCG_AREG0, + cpu_PSW_SAV = tcg_global_mem_new(cpu_env, offsetof(CPUTriCoreState, PSW_USB_SAV), "PSW_SAV"); } diff --git a/qemu/target-tricore/tricore-opcodes.h b/qemu/target-tricore/tricore-opcodes.h index 1bfed0ce4..df666b081 100644 --- a/qemu/target-tricore/tricore-opcodes.h +++ b/qemu/target-tricore/tricore-opcodes.h @@ -1126,6 +1126,20 @@ enum { OPC2_32_RR_CRC32 = 0x03, OPC2_32_RR_DIV = 0x20, OPC2_32_RR_DIV_U = 0x21, + OPC2_32_RR_MUL_F = 0x04, + OPC2_32_RR_DIV_F = 0x05, + OPC2_32_RR_FTOI = 0x10, + OPC2_32_RR_ITOF = 0x14, + OPC2_32_RR_CMP_F = 0x00, + OPC2_32_RR_FTOIZ = 0x13, + OPC2_32_RR_FTOQ31 = 0x11, + OPC2_32_RR_FTOQ31Z = 0x18, + OPC2_32_RR_FTOU = 0x12, + OPC2_32_RR_FTOUZ = 0x17, + OPC2_32_RR_Q31TOF = 0x15, + OPC2_32_RR_QSEED_F = 0x19, + OPC2_32_RR_UPDFL = 0x0c, + OPC2_32_RR_UTOF = 0x16, }; /* OPCM_32_RR_IDIRECT */ enum { @@ -1209,6 +1223,10 @@ enum { OPC2_32_RRR_IXMIN = 0x08, OPC2_32_RRR_IXMIN_U = 0x09, OPC2_32_RRR_PACK = 0x00, + OPC2_32_RRR_ADD_F = 0x02, + OPC2_32_RRR_SUB_F = 0x03, + OPC2_32_RRR_MADD_F = 0x06, + OPC2_32_RRR_MSUB_F = 0x07, }; /* * RRR1 Format diff --git a/qemu/target-unicore32/cpu.c b/qemu/target-unicore32/cpu.c index fc451a1a3..66f43acff 100644 --- a/qemu/target-unicore32/cpu.c +++ b/qemu/target-unicore32/cpu.c @@ -12,6 +12,8 @@ * or (at your option) any later version. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "qemu-common.h" #include "migration/vmstate.h" @@ -155,6 +157,13 @@ static void uc32_cpu_class_init(ObjectClass *oc, void *data) cc->get_phys_page_debug = uc32_cpu_get_phys_page_debug; #endif dc->vmsd = &vmstate_uc32_cpu; + + /* + * Reason: uc32_cpu_initfn() calls cpu_exec_init(), which saves + * the object in cpus -> dangling pointer after final + * object_unref(). + */ + dc->cannot_destroy_with_object_finalize_yet = true; } static void uc32_register_cpu_type(const UniCore32CPUInfo *info) diff --git a/qemu/target-unicore32/cpu.h b/qemu/target-unicore32/cpu.h index 45e31e54b..9c1fbf9b7 100644 --- a/qemu/target-unicore32/cpu.h +++ b/qemu/target-unicore32/cpu.h @@ -17,11 +17,8 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 -#define ELF_MACHINE EM_UNICORE32 - #define CPUArchState struct CPUUniCore32State -#include "config.h" #include "qemu-common.h" #include "exec/cpu-defs.h" #include "fpu/softfloat.h" @@ -131,7 +128,7 @@ int uc32_cpu_signal_handler(int host_signum, void *pinfo, void *puc); #define MMU_MODE0_SUFFIX _kernel #define MMU_MODE1_SUFFIX _user #define MMU_USER_IDX 1 -static inline int cpu_mmu_index(CPUUniCore32State *env) +static inline int cpu_mmu_index(CPUUniCore32State *env, bool ifetch) { return (env->uncached_asr & ASR_M) == ASR_MODE_USER ? 1 : 0; } diff --git a/qemu/target-unicore32/helper.c b/qemu/target-unicore32/helper.c index ae63277c8..21f5f3574 100644 --- a/qemu/target-unicore32/helper.c +++ b/qemu/target-unicore32/helper.c @@ -9,6 +9,7 @@ * or (at your option) any later version. */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/gdbstub.h" #include "exec/helper-proto.h" diff --git a/qemu/target-unicore32/op_helper.c b/qemu/target-unicore32/op_helper.c index 0266dbdf7..f5847307a 100644 --- a/qemu/target-unicore32/op_helper.c +++ b/qemu/target-unicore32/op_helper.c @@ -8,6 +8,7 @@ * published by the Free Software Foundation, or (at your option) any * later version. See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" diff --git a/qemu/target-unicore32/softmmu.c b/qemu/target-unicore32/softmmu.c index 9a3786ddd..d267fed87 100644 --- a/qemu/target-unicore32/softmmu.c +++ b/qemu/target-unicore32/softmmu.c @@ -12,6 +12,7 @@ #error This file only exist under softmmu circumstance #endif +#include "qemu/osdep.h" #include #undef DEBUG_UC32 diff --git a/qemu/target-unicore32/translate.c b/qemu/target-unicore32/translate.c index 2fc78e6f3..39af3af05 100644 --- a/qemu/target-unicore32/translate.c +++ b/qemu/target-unicore32/translate.c @@ -8,11 +8,7 @@ * published by the Free Software Foundation, or (at your option) any * later version. See the COPYING file in the top-level directory. */ -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include "cpu.h" #include "disas/disas.h" @@ -24,6 +20,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" /* internal defines */ @@ -51,7 +48,7 @@ typedef struct DisasContext { conditional executions state has been updated. */ #define DISAS_SYSCALL 5 -static TCGv_ptr cpu_env; +static TCGv_env cpu_env; static TCGv_i32 cpu_R[32]; /* FIXME: These should be removed. */ @@ -74,7 +71,7 @@ void uc32_translate_init(void) cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); for (i = 0; i < 32; i++) { - cpu_R[i] = tcg_global_mem_new_i32(TCG_AREG0, + cpu_R[i] = tcg_global_mem_new_i32(cpu_env, offsetof(CPUUniCore32State, regs[i]), regnames[i]); } } @@ -1794,10 +1791,6 @@ static void disas_uc32_insn(CPUUniCore32State *env, DisasContext *s) UniCore32CPU *cpu = uc32_env_get_cpu(env); unsigned int insn; - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { - tcg_gen_debug_insn_start(s->pc); - } - insn = cpu_ldl_code(env, s->pc); s->pc += 4; @@ -1867,17 +1860,12 @@ static void disas_uc32_insn(CPUUniCore32State *env, DisasContext *s) } } -/* generate intermediate code in gen_opc_buf and gen_opparam_buf for - basic block 'tb'. If search_pc is TRUE, also generate PC - information for each intermediate instruction. */ -static inline void gen_intermediate_code_internal(UniCore32CPU *cpu, - TranslationBlock *tb, bool search_pc) +/* generate intermediate code for basic block 'tb'. */ +void gen_intermediate_code(CPUUniCore32State *env, TranslationBlock *tb) { + UniCore32CPU *cpu = uc32_env_get_cpu(env); CPUState *cs = CPU(cpu); - CPUUniCore32State *env = &cpu->env; DisasContext dc1, *dc = &dc1; - CPUBreakpoint *bp; - int j, lj; target_ulong pc_start; uint32_t next_page_start; int num_insns; @@ -1899,12 +1887,14 @@ static inline void gen_intermediate_code_internal(UniCore32CPU *cpu, cpu_F0d = tcg_temp_new_i64(); cpu_F1d = tcg_temp_new_i64(); next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; - lj = -1; num_insns = 0; max_insns = tb->cflags & CF_COUNT_MASK; if (max_insns == 0) { max_insns = CF_COUNT_MASK; } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } #ifndef CONFIG_USER_ONLY if ((env->uncached_asr & ASR_M) == ASR_MODE_USER) { @@ -1916,33 +1906,22 @@ static inline void gen_intermediate_code_internal(UniCore32CPU *cpu, gen_tb_start(tb); do { - if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { - QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - if (bp->pc == dc->pc) { - gen_set_pc_im(dc->pc); - gen_exception(EXCP_DEBUG); - dc->is_jmp = DISAS_JUMP; - /* Advance PC so that clearing the breakpoint will - invalidate this TB. */ - dc->pc += 2; /* FIXME */ - goto done_generating; - } - } - } - if (search_pc) { - j = tcg_op_buf_count(); - if (lj < j) { - lj++; - while (lj < j) { - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - } - tcg_ctx.gen_opc_pc[lj] = dc->pc; - tcg_ctx.gen_opc_instr_start[lj] = 1; - tcg_ctx.gen_opc_icount[lj] = num_insns; + tcg_gen_insn_start(dc->pc); + num_insns++; + + if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { + gen_set_pc_im(dc->pc); + gen_exception(EXCP_DEBUG); + dc->is_jmp = DISAS_JUMP; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + dc->pc += 4; + goto done_generating; } - if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) { + if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { gen_io_start(); } @@ -1961,7 +1940,6 @@ static inline void gen_intermediate_code_internal(UniCore32CPU *cpu, * Otherwise the subsequent code could get translated several times. * Also stop translation when a page boundary is reached. This * ensures prefetch aborts occur at the right place. */ - num_insns++; } while (!dc->is_jmp && !tcg_op_buf_full() && !cs->singlestep_enabled && !singlestep && @@ -2043,26 +2021,8 @@ done_generating: qemu_log("\n"); } #endif - if (search_pc) { - j = tcg_op_buf_count(); - lj++; - while (lj <= j) { - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - } else { - tb->size = dc->pc - pc_start; - tb->icount = num_insns; - } -} - -void gen_intermediate_code(CPUUniCore32State *env, TranslationBlock *tb) -{ - gen_intermediate_code_internal(uc32_env_get_cpu(env), tb, false); -} - -void gen_intermediate_code_pc(CPUUniCore32State *env, TranslationBlock *tb) -{ - gen_intermediate_code_internal(uc32_env_get_cpu(env), tb, true); + tb->size = dc->pc - pc_start; + tb->icount = num_insns; } static const char *cpu_mode_names[16] = { @@ -2133,7 +2093,8 @@ void uc32_cpu_dump_state(CPUState *cs, FILE *f, cpu_dump_state_ucf64(env, f, cpu_fprintf, flags); } -void restore_state_to_opc(CPUUniCore32State *env, TranslationBlock *tb, int pc_pos) +void restore_state_to_opc(CPUUniCore32State *env, TranslationBlock *tb, + target_ulong *data) { - env->regs[31] = tcg_ctx.gen_opc_pc[pc_pos]; + env->regs[31] = data[0]; } diff --git a/qemu/target-unicore32/ucf64_helper.c b/qemu/target-unicore32/ucf64_helper.c index 5af008fc6..6c919010c 100644 --- a/qemu/target-unicore32/ucf64_helper.c +++ b/qemu/target-unicore32/ucf64_helper.c @@ -8,6 +8,7 @@ * published by the Free Software Foundation, or any later version. * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" diff --git a/qemu/target-xtensa/Makefile.objs b/qemu/target-xtensa/Makefile.objs index 5c150a870..481de9197 100644 --- a/qemu/target-xtensa/Makefile.objs +++ b/qemu/target-xtensa/Makefile.objs @@ -2,5 +2,6 @@ obj-y += xtensa-semi.o obj-y += core-dc232b.o obj-y += core-dc233c.o obj-y += core-fsf.o +obj-$(CONFIG_SOFTMMU) += monitor.o obj-y += translate.o op_helper.o helper.o cpu.o obj-y += gdbstub.o diff --git a/qemu/target-xtensa/core-dc232b.c b/qemu/target-xtensa/core-dc232b.c index 06826c042..bb8ed4197 100644 --- a/qemu/target-xtensa/core-dc232b.c +++ b/qemu/target-xtensa/core-dc232b.c @@ -25,6 +25,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" #include "exec/gdbstub.h" diff --git a/qemu/target-xtensa/core-dc233c.c b/qemu/target-xtensa/core-dc233c.c index 8daf7d9f8..40475e520 100644 --- a/qemu/target-xtensa/core-dc233c.c +++ b/qemu/target-xtensa/core-dc233c.c @@ -25,6 +25,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" #include "exec/gdbstub.h" diff --git a/qemu/target-xtensa/core-fsf.c b/qemu/target-xtensa/core-fsf.c index f6ea6b944..15ef470e8 100644 --- a/qemu/target-xtensa/core-fsf.c +++ b/qemu/target-xtensa/core-fsf.c @@ -25,6 +25,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" #include "exec/gdbstub.h" diff --git a/qemu/target-xtensa/cpu.c b/qemu/target-xtensa/cpu.c index da8129db5..01b251fdc 100644 --- a/qemu/target-xtensa/cpu.c +++ b/qemu/target-xtensa/cpu.c @@ -28,6 +28,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "qemu-common.h" #include "migration/vmstate.h" @@ -155,6 +157,13 @@ static void xtensa_cpu_class_init(ObjectClass *oc, void *data) #endif cc->debug_excp_handler = xtensa_breakpoint_handler; dc->vmsd = &vmstate_xtensa_cpu; + + /* + * Reason: xtensa_cpu_initfn() calls cpu_exec_init(), which saves + * the object in cpus -> dangling pointer after final + * object_unref(). + */ + dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo xtensa_cpu_type_info = { diff --git a/qemu/target-xtensa/cpu.h b/qemu/target-xtensa/cpu.h index 96bfc82e9..d0bd9dada 100644 --- a/qemu/target-xtensa/cpu.h +++ b/qemu/target-xtensa/cpu.h @@ -30,11 +30,9 @@ #define ALIGNED_ONLY #define TARGET_LONG_BITS 32 -#define ELF_MACHINE EM_XTENSA #define CPUArchState struct CPUXtensaState -#include "config.h" #include "qemu-common.h" #include "exec/cpu-defs.h" #include "fpu/softfloat.h" @@ -65,6 +63,7 @@ enum { XTENSA_OPTION_MP_SYNCHRO, XTENSA_OPTION_CONDITIONAL_STORE, XTENSA_OPTION_ATOMCTL, + XTENSA_OPTION_DEPBITS, /* Interrupts and exceptions */ XTENSA_OPTION_EXCEPTION, @@ -383,7 +382,6 @@ typedef struct CPUXtensaState { #include "cpu-qom.h" #define cpu_exec cpu_xtensa_exec -#define cpu_gen_code cpu_xtensa_gen_code #define cpu_signal_handler cpu_xtensa_signal_handler #define cpu_list xtensa_cpu_list @@ -492,7 +490,7 @@ static inline uint32_t xtensa_replicate_windowstart(CPUXtensaState *env) #define MMU_MODE2_SUFFIX _ring2 #define MMU_MODE3_SUFFIX _ring3 -static inline int cpu_mmu_index(CPUXtensaState *env) +static inline int cpu_mmu_index(CPUXtensaState *env, bool ifetch) { return xtensa_get_cring(env); } diff --git a/qemu/target-xtensa/gdbstub.c b/qemu/target-xtensa/gdbstub.c index bc2e1b55f..51d4db083 100644 --- a/qemu/target-xtensa/gdbstub.c +++ b/qemu/target-xtensa/gdbstub.c @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "exec/gdbstub.h" @@ -63,8 +63,8 @@ int xtensa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) return gdb_get_reg32(mem_buf, env->regs[reg->targno & 0x0f]); default: - qemu_log("%s from reg %d of unsupported type %d\n", - __func__, n, reg->type); + qemu_log_mask(LOG_UNIMP, "%s from reg %d of unsupported type %d\n", + __func__, n, reg->type); return 0; } } @@ -117,8 +117,8 @@ int xtensa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) break; default: - qemu_log("%s to reg %d of unsupported type %d\n", - __func__, n, reg->type); + qemu_log_mask(LOG_UNIMP, "%s to reg %d of unsupported type %d\n", + __func__, n, reg->type); return 0; } diff --git a/qemu/target-xtensa/helper.c b/qemu/target-xtensa/helper.c index 76be50d09..839f4a74a 100644 --- a/qemu/target-xtensa/helper.c +++ b/qemu/target-xtensa/helper.c @@ -25,6 +25,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" #include "exec/gdbstub.h" @@ -254,8 +255,8 @@ void xtensa_cpu_do_interrupt(CPUState *cs) env->config->exception_vector[cs->exception_index]); env->exception_taken = 1; } else { - qemu_log("%s(pc = %08x) bad exception_index: %d\n", - __func__, env->pc, cs->exception_index); + qemu_log_mask(CPU_LOG_INT, "%s(pc = %08x) bad exception_index: %d\n", + __func__, env->pc, cs->exception_index); } break; @@ -541,8 +542,8 @@ static int get_physical_addr_mmu(CPUXtensaState *env, bool update_tlb, wi = ++env->autorefill_idx & 0x3; xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, pte); env->sregs[EXCVADDR] = vaddr; - qemu_log("%s: autorefill(%08x): %08x -> %08x\n", - __func__, vaddr, vpn, pte); + qemu_log_mask(CPU_LOG_MMU, "%s: autorefill(%08x): %08x -> %08x\n", + __func__, vaddr, vpn, pte); } else { xtensa_tlb_set_entry_mmu(env, &tmp_entry, dtlb, wi, ei, vpn, pte); entry = &tmp_entry; @@ -590,8 +591,8 @@ static int get_pte(CPUXtensaState *env, uint32_t vaddr, uint32_t *pte) int ret = get_physical_addr_mmu(env, false, pt_vaddr, 0, 0, &paddr, &page_size, &access, false); - qemu_log("%s: trying autorefill(%08x) -> %08x\n", __func__, - vaddr, ret ? ~0 : paddr); + qemu_log_mask(CPU_LOG_MMU, "%s: trying autorefill(%08x) -> %08x\n", + __func__, vaddr, ret ? ~0 : paddr); if (ret == 0) { *pte = ldl_phys(cs->as, paddr); diff --git a/qemu/target-xtensa/monitor.c b/qemu/target-xtensa/monitor.c new file mode 100644 index 000000000..f3fa4cd27 --- /dev/null +++ b/qemu/target-xtensa/monitor.c @@ -0,0 +1,35 @@ +/* + * QEMU monitor + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "cpu.h" +#include "monitor/monitor.h" +#include "monitor/hmp-target.h" +#include "hmp.h" + +void hmp_info_tlb(Monitor *mon, const QDict *qdict) +{ + CPUArchState *env1 = mon_get_cpu_env(); + + dump_mmu((FILE*)mon, (fprintf_function)monitor_printf, env1); +} diff --git a/qemu/target-xtensa/op_helper.c b/qemu/target-xtensa/op_helper.c index be657e615..62fa33d8d 100644 --- a/qemu/target-xtensa/op_helper.c +++ b/qemu/target-xtensa/op_helper.c @@ -25,6 +25,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" @@ -57,8 +58,8 @@ void tlb_fill(CPUState *cs, int ret = xtensa_get_physical_addr(env, true, vaddr, is_write, mmu_idx, &paddr, &page_size, &access); - qemu_log("%s(%08x, %d, %d) -> %08x, ret = %d\n", __func__, - vaddr, is_write, mmu_idx, paddr, ret); + qemu_log_mask(CPU_LOG_MMU, "%s(%08x, %d, %d) -> %08x, ret = %d\n", + __func__, vaddr, is_write, mmu_idx, paddr, ret); if (ret == 0) { tlb_set_page(cs, @@ -245,8 +246,8 @@ void HELPER(entry)(CPUXtensaState *env, uint32_t pc, uint32_t s, uint32_t imm) { int callinc = (env->sregs[PS] & PS_CALLINC) >> PS_CALLINC_SHIFT; if (s > 3 || ((env->sregs[PS] & (PS_WOE | PS_EXCM)) ^ PS_WOE) != 0) { - qemu_log("Illegal entry instruction(pc = %08x), PS = %08x\n", - pc, env->sregs[PS]); + qemu_log_mask(LOG_GUEST_ERROR, "Illegal entry instruction(pc = %08x), PS = %08x\n", + pc, env->sregs[PS]); HELPER(exception_cause)(env, pc, ILLEGAL_INSTRUCTION_CAUSE); } else { uint32_t windowstart = xtensa_replicate_windowstart(env) >> @@ -307,9 +308,9 @@ uint32_t HELPER(retw)(CPUXtensaState *env, uint32_t pc) if (n == 0 || (m != 0 && m != n) || ((env->sregs[PS] & (PS_WOE | PS_EXCM)) ^ PS_WOE) != 0) { - qemu_log("Illegal retw instruction(pc = %08x), " - "PS = %08x, m = %d, n = %d\n", - pc, env->sregs[PS], m, n); + qemu_log_mask(LOG_GUEST_ERROR, "Illegal retw instruction(pc = %08x), " + "PS = %08x, m = %d, n = %d\n", + pc, env->sregs[PS], m, n); HELPER(exception_cause)(env, pc, ILLEGAL_INSTRUCTION_CAUSE); } else { int owb = windowbase; @@ -743,8 +744,8 @@ void xtensa_tlb_set_entry(CPUXtensaState *env, bool dtlb, xtensa_tlb_set_entry_mmu(env, entry, dtlb, wi, ei, vpn, pte); tlb_flush_page(cs, entry->vaddr); } else { - qemu_log("%s %d, %d, %d trying to set immutable entry\n", - __func__, dtlb, wi, ei); + qemu_log_mask(LOG_GUEST_ERROR, "%s %d, %d, %d trying to set immutable entry\n", + __func__, dtlb, wi, ei); } } else { tlb_flush_page(cs, entry->vaddr); @@ -806,15 +807,15 @@ static void set_dbreak(CPUXtensaState *env, unsigned i, uint32_t dbreaka, } /* contiguous mask after inversion is one less than some power of 2 */ if ((~mask + 1) & ~mask) { - qemu_log("DBREAKC mask is not contiguous: 0x%08x\n", dbreakc); + qemu_log_mask(LOG_GUEST_ERROR, "DBREAKC mask is not contiguous: 0x%08x\n", dbreakc); /* cut mask after the first zero bit */ mask = 0xffffffff << (32 - clo32(mask)); } if (cpu_watchpoint_insert(cs, dbreaka & mask, ~mask + 1, flags, &env->cpu_watchpoint[i])) { env->cpu_watchpoint[i] = NULL; - qemu_log("Failed to set data breakpoint at 0x%08x/%d\n", - dbreaka & mask, ~mask + 1); + qemu_log_mask(LOG_GUEST_ERROR, "Failed to set data breakpoint at 0x%08x/%d\n", + dbreaka & mask, ~mask + 1); } } diff --git a/qemu/target-xtensa/overlay_tool.h b/qemu/target-xtensa/overlay_tool.h index eda03aaca..e8a7fda3d 100644 --- a/qemu/target-xtensa/overlay_tool.h +++ b/qemu/target-xtensa/overlay_tool.h @@ -30,6 +30,10 @@ { .targno = (no), .type = (typ), .group = (grp), .size = (sz) }, #define XTREG_END { .targno = -1 }, +#ifndef XCHAL_HAVE_DEPBITS +#define XCHAL_HAVE_DEPBITS 0 +#endif + #ifndef XCHAL_HAVE_DIV32 #define XCHAL_HAVE_DIV32 0 #endif @@ -69,6 +73,7 @@ XCHAL_OPTION(XCHAL_HAVE_S32C1I, XTENSA_OPTION_CONDITIONAL_STORE) | \ XCHAL_OPTION(XCHAL_HAVE_S32C1I && XCHAL_HW_MIN_VERSION >= 230000, \ XTENSA_OPTION_ATOMCTL) | \ + XCHAL_OPTION(XCHAL_HAVE_DEPBITS, XTENSA_OPTION_DEPBITS) | \ /* Interrupts and exceptions */ \ XCHAL_OPTION(XCHAL_HAVE_EXCEPTIONS, XTENSA_OPTION_EXCEPTION) | \ XCHAL_OPTION(XCHAL_HAVE_VECBASE, XTENSA_OPTION_RELOCATABLE_VECTOR) | \ diff --git a/qemu/target-xtensa/translate.c b/qemu/target-xtensa/translate.c index f2118c24c..989448846 100644 --- a/qemu/target-xtensa/translate.c +++ b/qemu/target-xtensa/translate.c @@ -28,7 +28,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include +#include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" @@ -43,6 +43,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" typedef struct DisasContext { @@ -73,7 +74,7 @@ typedef struct DisasContext { unsigned cpenable; } DisasContext; -static TCGv_ptr cpu_env; +static TCGv_env cpu_env; static TCGv_i32 cpu_pc; static TCGv_i32 cpu_R[16]; static TCGv_i32 cpu_FR[16]; @@ -217,24 +218,24 @@ void xtensa_translate_init(void) int i; cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); - cpu_pc = tcg_global_mem_new_i32(TCG_AREG0, + cpu_pc = tcg_global_mem_new_i32(cpu_env, offsetof(CPUXtensaState, pc), "pc"); for (i = 0; i < 16; i++) { - cpu_R[i] = tcg_global_mem_new_i32(TCG_AREG0, + cpu_R[i] = tcg_global_mem_new_i32(cpu_env, offsetof(CPUXtensaState, regs[i]), regnames[i]); } for (i = 0; i < 16; i++) { - cpu_FR[i] = tcg_global_mem_new_i32(TCG_AREG0, + cpu_FR[i] = tcg_global_mem_new_i32(cpu_env, offsetof(CPUXtensaState, fregs[i].f32[FP_F32_LOW]), fregnames[i]); } for (i = 0; i < 256; ++i) { if (sregnames[i].name) { - cpu_SR[i] = tcg_global_mem_new_i32(TCG_AREG0, + cpu_SR[i] = tcg_global_mem_new_i32(cpu_env, offsetof(CPUXtensaState, sregs[i]), sregnames[i].name); } @@ -242,7 +243,7 @@ void xtensa_translate_init(void) for (i = 0; i < 256; ++i) { if (uregnames[i].name) { - cpu_UR[i] = tcg_global_mem_new_i32(TCG_AREG0, + cpu_UR[i] = tcg_global_mem_new_i32(cpu_env, offsetof(CPUXtensaState, uregs[i]), uregnames[i].name); } @@ -501,9 +502,9 @@ static bool gen_check_sr(DisasContext *dc, uint32_t sr, unsigned access) { if (!xtensa_option_bits_enabled(dc->config, sregnames[sr].opt_bits)) { if (sregnames[sr].name) { - qemu_log("SR %s is not configured\n", sregnames[sr].name); + qemu_log_mask(LOG_GUEST_ERROR, "SR %s is not configured\n", sregnames[sr].name); } else { - qemu_log("SR %d is not implemented\n", sr); + qemu_log_mask(LOG_UNIMP, "SR %d is not implemented\n", sr); } gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE); return false; @@ -514,8 +515,8 @@ static bool gen_check_sr(DisasContext *dc, uint32_t sr, unsigned access) [SR_X] = "xsr", }; assert(access < ARRAY_SIZE(access_text) && access_text[access]); - qemu_log("SR %s is not available for %s\n", sregnames[sr].name, - access_text[access]); + qemu_log_mask(LOG_GUEST_ERROR, "SR %s is not available for %s\n", sregnames[sr].name, + access_text[access]); gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE); return false; } @@ -875,18 +876,18 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) { #define HAS_OPTION_BITS(opt) do { \ if (!option_bits_enabled(dc, opt)) { \ - qemu_log("Option is not enabled %s:%d\n", \ - __FILE__, __LINE__); \ + qemu_log_mask(LOG_GUEST_ERROR, "Option is not enabled %s:%d\n", \ + __FILE__, __LINE__); \ goto invalid_opcode; \ } \ } while (0) #define HAS_OPTION(opt) HAS_OPTION_BITS(XTENSA_OPTION_BIT(opt)) -#define TBD() qemu_log("TBD(pc = %08x): %s:%d\n", dc->pc, __FILE__, __LINE__) +#define TBD() qemu_log_mask(LOG_UNIMP, "TBD(pc = %08x): %s:%d\n", dc->pc, __FILE__, __LINE__) #define RESERVED() do { \ - qemu_log("RESERVED(pc = %08x, %02x%02x%02x): %s:%d\n", \ - dc->pc, b0, b1, b2, __FILE__, __LINE__); \ + qemu_log_mask(LOG_GUEST_ERROR, "RESERVED(pc = %08x, %02x%02x%02x): %s:%d\n", \ + dc->pc, b0, b1, b2, __FILE__, __LINE__); \ goto invalid_opcode; \ } while (0) @@ -1186,7 +1187,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) gen_jump(dc, cpu_SR[EPC1 + RRR_S - 1]); } } else { - qemu_log("RFI %d is illegal\n", RRR_S); + qemu_log_mask(LOG_GUEST_ERROR, "RFI %d is illegal\n", RRR_S); gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE); } break; @@ -1222,7 +1223,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) gen_helper_simcall(cpu_env); } } else { - qemu_log("SIMCALL but semihosting is disabled\n"); + qemu_log_mask(LOG_GUEST_ERROR, "SIMCALL but semihosting is disabled\n"); gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE); } break; @@ -1544,7 +1545,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) TCGv_i64 tmp = tcg_temp_new_i64(); \ tcg_gen_extu_i32_i64(tmp, reg); \ tcg_gen_##cmd##_i64(v, v, tmp); \ - tcg_gen_trunc_i64_i32(cpu_R[RRR_R], v); \ + tcg_gen_extrl_i64_i32(cpu_R[RRR_R], v); \ tcg_temp_free_i64(v); \ tcg_temp_free_i64(tmp); \ } while (0) @@ -1865,7 +1866,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) if (uregnames[st].name) { tcg_gen_mov_i32(cpu_R[RRR_R], cpu_UR[st]); } else { - qemu_log("RUR %d not implemented, ", st); + qemu_log_mask(LOG_UNIMP, "RUR %d not implemented, ", st); TBD(); } } @@ -1876,7 +1877,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) if (uregnames[RSR_SR].name) { gen_wur(RSR_SR, cpu_R[RRR_T]); } else { - qemu_log("WUR %d not implemented, ", RSR_SR); + qemu_log_mask(LOG_UNIMP, "WUR %d not implemented, ", RSR_SR); TBD(); } } @@ -1943,7 +1944,8 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) switch (OP2) { case 0: /*L32E*/ HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); - if (gen_check_privilege(dc)) { + if (gen_check_privilege(dc) && + gen_window_check2(dc, RRR_S, RRR_T)) { TCGv_i32 addr = tcg_temp_new_i32(); tcg_gen_addi_i32(addr, cpu_R[RRR_S], (0xffffffc0 | (RRR_R << 2))); @@ -1954,7 +1956,8 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 4: /*S32E*/ HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); - if (gen_check_privilege(dc)) { + if (gen_check_privilege(dc) && + gen_window_check2(dc, RRR_S, RRR_T)) { TCGv_i32 addr = tcg_temp_new_i32(); tcg_gen_addi_i32(addr, cpu_R[RRR_S], (0xffffffc0 | (RRR_R << 2))); @@ -1963,6 +1966,17 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) } break; + case 5: /*S32N*/ + if (gen_window_check2(dc, RRI4_S, RRI4_T)) { + TCGv_i32 addr = tcg_temp_new_i32(); + + tcg_gen_addi_i32(addr, cpu_R[RRI4_S], RRI4_IMM4 << 2); + gen_load_store_alignment(dc, 2, addr, false); + tcg_gen_qemu_st32(cpu_R[RRI4_T], addr, dc->cring); + tcg_temp_free(addr); + } + break; + default: RESERVED(); break; @@ -1970,6 +1984,16 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 10: /*FP0*/ + /*DEPBITS*/ + if (option_enabled(dc, XTENSA_OPTION_DEPBITS)) { + if (!gen_window_check2(dc, RRR_S, RRR_T)) { + break; + } + tcg_gen_deposit_i32(cpu_R[RRR_T], cpu_R[RRR_T], cpu_R[RRR_S], + OP2, RRR_R + 1); + break; + } + HAS_OPTION(XTENSA_OPTION_FP_COPROCESSOR); switch (OP2) { case 0: /*ADD.Sf*/ @@ -2104,6 +2128,16 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 11: /*FP1*/ + /*DEPBITS*/ + if (option_enabled(dc, XTENSA_OPTION_DEPBITS)) { + if (!gen_window_check2(dc, RRR_S, RRR_T)) { + break; + } + tcg_gen_deposit_i32(cpu_R[RRR_T], cpu_R[RRR_T], cpu_R[RRR_S], + OP2 + 16, RRR_R + 1); + break; + } + HAS_OPTION(XTENSA_OPTION_FP_COPROCESSOR); #define gen_compare(rel, br, a, b) \ @@ -2973,7 +3007,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) return; invalid_opcode: - qemu_log("INVALID(pc = %08x)\n", dc->pc); + qemu_log_mask(LOG_GUEST_ERROR, "INVALID(pc = %08x)\n", dc->pc); gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE); #undef HAS_OPTION } @@ -2984,22 +3018,6 @@ static inline unsigned xtensa_insn_len(CPUXtensaState *env, DisasContext *dc) return xtensa_op0_insn_len(OP0); } -static void check_breakpoint(CPUXtensaState *env, DisasContext *dc) -{ - CPUState *cs = CPU(xtensa_env_get_cpu(env)); - CPUBreakpoint *bp; - - if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { - QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - if (bp->pc == dc->pc) { - tcg_gen_movi_i32(cpu_pc, dc->pc); - gen_exception(dc, EXCP_DEBUG); - dc->is_jmp = DISAS_UPDATE; - } - } - } -} - static void gen_ibreak_check(CPUXtensaState *env, DisasContext *dc) { unsigned i; @@ -3013,15 +3031,12 @@ static void gen_ibreak_check(CPUXtensaState *env, DisasContext *dc) } } -static inline -void gen_intermediate_code_internal(XtensaCPU *cpu, - TranslationBlock *tb, bool search_pc) +void gen_intermediate_code(CPUXtensaState *env, TranslationBlock *tb) { + XtensaCPU *cpu = xtensa_env_get_cpu(env); CPUState *cs = CPU(cpu); - CPUXtensaState *env = &cpu->env; DisasContext dc; int insn_count = 0; - int j, lj = -1; int max_insns = tb->cflags & CF_COUNT_MASK; uint32_t pc_start = tb->pc; uint32_t next_page_start = @@ -3030,6 +3045,9 @@ void gen_intermediate_code_internal(XtensaCPU *cpu, if (max_insns == 0) { max_insns = CF_COUNT_MASK; } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } dc.config = env->config; dc.singlestep_enabled = cs->singlestep_enabled; @@ -3062,28 +3080,24 @@ void gen_intermediate_code_internal(XtensaCPU *cpu, } do { - check_breakpoint(env, &dc); + tcg_gen_insn_start(dc.pc); + ++insn_count; - if (search_pc) { - j = tcg_op_buf_count(); - if (lj < j) { - lj++; - while (lj < j) { - tcg_ctx.gen_opc_instr_start[lj++] = 0; - } - } - tcg_ctx.gen_opc_pc[lj] = dc.pc; - tcg_ctx.gen_opc_instr_start[lj] = 1; - tcg_ctx.gen_opc_icount[lj] = insn_count; - } + ++dc.ccount_delta; - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { - tcg_gen_debug_insn_start(dc.pc); + if (unlikely(cpu_breakpoint_test(cs, dc.pc, BP_ANY))) { + tcg_gen_movi_i32(cpu_pc, dc.pc); + gen_exception(&dc, EXCP_DEBUG); + dc.is_jmp = DISAS_UPDATE; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + dc.pc += 2; + break; } - ++dc.ccount_delta; - - if (insn_count + 1 == max_insns && (tb->cflags & CF_LAST_IO)) { + if (insn_count == max_insns && (tb->cflags & CF_LAST_IO)) { gen_io_start(); } @@ -3104,7 +3118,6 @@ void gen_intermediate_code_internal(XtensaCPU *cpu, } disas_xtensa_insn(env, &dc); - ++insn_count; if (dc.icount) { tcg_gen_mov_i32(cpu_SR[ICOUNT], dc.next_icount); } @@ -3142,24 +3155,8 @@ void gen_intermediate_code_internal(XtensaCPU *cpu, qemu_log("\n"); } #endif - if (search_pc) { - j = tcg_op_buf_count(); - memset(tcg_ctx.gen_opc_instr_start + lj + 1, 0, - (j - lj) * sizeof(tcg_ctx.gen_opc_instr_start[0])); - } else { - tb->size = dc.pc - pc_start; - tb->icount = insn_count; - } -} - -void gen_intermediate_code(CPUXtensaState *env, TranslationBlock *tb) -{ - gen_intermediate_code_internal(xtensa_env_get_cpu(env), tb, false); -} - -void gen_intermediate_code_pc(CPUXtensaState *env, TranslationBlock *tb) -{ - gen_intermediate_code_internal(xtensa_env_get_cpu(env), tb, true); + tb->size = dc.pc - pc_start; + tb->icount = insn_count; } void xtensa_cpu_dump_state(CPUState *cs, FILE *f, @@ -3213,7 +3210,8 @@ void xtensa_cpu_dump_state(CPUState *cs, FILE *f, } } -void restore_state_to_opc(CPUXtensaState *env, TranslationBlock *tb, int pc_pos) +void restore_state_to_opc(CPUXtensaState *env, TranslationBlock *tb, + target_ulong *data) { - env->pc = tcg_ctx.gen_opc_pc[pc_pos]; + env->pc = data[0]; } diff --git a/qemu/target-xtensa/xtensa-semi.c b/qemu/target-xtensa/xtensa-semi.c index 16e9d8c7b..370e365c6 100644 --- a/qemu/target-xtensa/xtensa-semi.c +++ b/qemu/target-xtensa/xtensa-semi.c @@ -25,10 +25,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include -#include -#include -#include +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/log.h" @@ -313,7 +310,7 @@ void HELPER(simcall)(CPUXtensaState *env) break; default: - qemu_log("%s(%d): not implemented\n", __func__, regs[2]); + qemu_log_mask(LOG_GUEST_ERROR, "%s(%d): not implemented\n", __func__, regs[2]); regs[2] = -1; regs[3] = TARGET_ENOSYS; break; diff --git a/qemu/tcg-runtime.c b/qemu/tcg-runtime.c index 9daba6945..ea2ad649c 100644 --- a/qemu/tcg-runtime.c +++ b/qemu/tcg-runtime.c @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include +#include "qemu/osdep.h" #include "qemu/host-utils.h" /* This file is compiled once, and thus we can't include the standard diff --git a/qemu/tcg/README b/qemu/tcg/README index a550ff176..f4a8ac170 100644 --- a/qemu/tcg/README +++ b/qemu/tcg/README @@ -314,11 +314,17 @@ This operation would be equivalent to dest = (t1 & ~0x0f00) | ((t2 << 8) & 0x0f00) -* trunc_shr_i32 t0, t1, pos +* extrl_i64_i32 t0, t1 -For 64-bit hosts only, right shift the 64-bit input T1 by POS and -truncate to 32-bit output T0. Depending on the host, this may be -a simple mov/shift, or may require additional canonicalization. +For 64-bit hosts only, extract the low 32-bits of input T1 and place it +into 32-bit output T0. Depending on the host, this may be a simple move, +or may require additional canonicalization. + +* extrh_i64_i32 t0, t1 + +For 64-bit hosts only, extract the high 32-bits of input T1 and place it +into 32-bit output T0. Depending on the host, this may be a simple shift, +or may require additional canonicalization. ********* Conditional moves @@ -454,8 +460,9 @@ function tcg_gen_xxx(args). 4) Backend -tcg-target.h contains the target specific definitions. tcg-target.c -contains the target specific code. +tcg-target.h contains the target specific definitions. tcg-target.inc.c +contains the target specific code; it is #included by tcg/tcg.c, rather +than being a standalone C file. 4.1) Assumptions @@ -466,13 +473,25 @@ On a 32 bit target, all 64 bit operations are converted to 32 bits. A few specific operations must be implemented to allow it (see add2_i32, sub2_i32, brcond2_i32). +On a 64 bit target, the values are transfered between 32 and 64-bit +registers using the following ops: +- trunc_shr_i64_i32 +- ext_i32_i64 +- extu_i32_i64 + +They ensure that the values are correctly truncated or extended when +moved from a 32-bit to a 64-bit register or vice-versa. Note that the +trunc_shr_i64_i32 is an optional op. It is not necessary to implement +it if all the following conditions are met: +- 64-bit registers can hold 32-bit values +- 32-bit values in a 64-bit register do not need to stay zero or + sign extended +- all 32-bit TCG ops ignore the high part of 64-bit registers + Floating point operations are not supported in this version. A previous incarnation of the code generator had full support of them, but it is better to concentrate on integer operations first. -On a 64 bit target, no assumption is made in TCG about the storage of -the 32 bit values in 64 bit registers. - 4.2) Constraints GCC like constraints are used to define the constraints of every diff --git a/qemu/tcg/aarch64/tcg-target.c b/qemu/tcg/aarch64/tcg-target.c deleted file mode 100644 index b7ec4f5ac..000000000 --- a/qemu/tcg/aarch64/tcg-target.c +++ /dev/null @@ -1,1868 +0,0 @@ -/* - * Initial TCG Implementation for aarch64 - * - * Copyright (c) 2013 Huawei Technologies Duesseldorf GmbH - * Written by Claudio Fontana - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * (at your option) any later version. - * - * See the COPYING file in the top-level directory for details. - */ - -#include "tcg-be-ldst.h" -#include "qemu/bitops.h" - -/* We're going to re-use TCGType in setting of the SF bit, which controls - the size of the operation performed. If we know the values match, it - makes things much cleaner. */ -QEMU_BUILD_BUG_ON(TCG_TYPE_I32 != 0 || TCG_TYPE_I64 != 1); - -#ifndef NDEBUG -static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { - "%x0", "%x1", "%x2", "%x3", "%x4", "%x5", "%x6", "%x7", - "%x8", "%x9", "%x10", "%x11", "%x12", "%x13", "%x14", "%x15", - "%x16", "%x17", "%x18", "%x19", "%x20", "%x21", "%x22", "%x23", - "%x24", "%x25", "%x26", "%x27", "%x28", "%fp", "%x30", "%sp", -}; -#endif /* NDEBUG */ - -static const int tcg_target_reg_alloc_order[] = { - TCG_REG_X20, TCG_REG_X21, TCG_REG_X22, TCG_REG_X23, - TCG_REG_X24, TCG_REG_X25, TCG_REG_X26, TCG_REG_X27, - TCG_REG_X28, /* we will reserve this for GUEST_BASE if configured */ - - TCG_REG_X8, TCG_REG_X9, TCG_REG_X10, TCG_REG_X11, - TCG_REG_X12, TCG_REG_X13, TCG_REG_X14, TCG_REG_X15, - TCG_REG_X16, TCG_REG_X17, - - TCG_REG_X0, TCG_REG_X1, TCG_REG_X2, TCG_REG_X3, - TCG_REG_X4, TCG_REG_X5, TCG_REG_X6, TCG_REG_X7, - - /* X18 reserved by system */ - /* X19 reserved for AREG0 */ - /* X29 reserved as fp */ - /* X30 reserved as temporary */ -}; - -static const int tcg_target_call_iarg_regs[8] = { - TCG_REG_X0, TCG_REG_X1, TCG_REG_X2, TCG_REG_X3, - TCG_REG_X4, TCG_REG_X5, TCG_REG_X6, TCG_REG_X7 -}; -static const int tcg_target_call_oarg_regs[1] = { - TCG_REG_X0 -}; - -#define TCG_REG_TMP TCG_REG_X30 - -#ifndef CONFIG_SOFTMMU -# ifdef CONFIG_USE_GUEST_BASE -# define TCG_REG_GUEST_BASE TCG_REG_X28 -# else -# define TCG_REG_GUEST_BASE TCG_REG_XZR -# endif -#endif - -static inline void reloc_pc26(tcg_insn_unit *code_ptr, tcg_insn_unit *target) -{ - ptrdiff_t offset = target - code_ptr; - assert(offset == sextract64(offset, 0, 26)); - /* read instruction, mask away previous PC_REL26 parameter contents, - set the proper offset, then write back the instruction. */ - *code_ptr = deposit32(*code_ptr, 0, 26, offset); -} - -static inline void reloc_pc19(tcg_insn_unit *code_ptr, tcg_insn_unit *target) -{ - ptrdiff_t offset = target - code_ptr; - assert(offset == sextract64(offset, 0, 19)); - *code_ptr = deposit32(*code_ptr, 5, 19, offset); -} - -static inline void patch_reloc(tcg_insn_unit *code_ptr, int type, - intptr_t value, intptr_t addend) -{ - assert(addend == 0); - switch (type) { - case R_AARCH64_JUMP26: - case R_AARCH64_CALL26: - reloc_pc26(code_ptr, (tcg_insn_unit *)value); - break; - case R_AARCH64_CONDBR19: - reloc_pc19(code_ptr, (tcg_insn_unit *)value); - break; - default: - tcg_abort(); - } -} - -#define TCG_CT_CONST_AIMM 0x100 -#define TCG_CT_CONST_LIMM 0x200 -#define TCG_CT_CONST_ZERO 0x400 -#define TCG_CT_CONST_MONE 0x800 - -/* parse target specific constraints */ -static int target_parse_constraint(TCGArgConstraint *ct, - const char **pct_str) -{ - const char *ct_str = *pct_str; - - switch (ct_str[0]) { - case 'r': - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, (1ULL << TCG_TARGET_NB_REGS) - 1); - break; - case 'l': /* qemu_ld / qemu_st address, data_reg */ - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, (1ULL << TCG_TARGET_NB_REGS) - 1); -#ifdef CONFIG_SOFTMMU - /* x0 and x1 will be overwritten when reading the tlb entry, - and x2, and x3 for helper args, better to avoid using them. */ - tcg_regset_reset_reg(ct->u.regs, TCG_REG_X0); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_X1); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_X2); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_X3); -#endif - break; - case 'A': /* Valid for arithmetic immediate (positive or negative). */ - ct->ct |= TCG_CT_CONST_AIMM; - break; - case 'L': /* Valid for logical immediate. */ - ct->ct |= TCG_CT_CONST_LIMM; - break; - case 'M': /* minus one */ - ct->ct |= TCG_CT_CONST_MONE; - break; - case 'Z': /* zero */ - ct->ct |= TCG_CT_CONST_ZERO; - break; - default: - return -1; - } - - ct_str++; - *pct_str = ct_str; - return 0; -} - -static inline bool is_aimm(uint64_t val) -{ - return (val & ~0xfff) == 0 || (val & ~0xfff000) == 0; -} - -static inline bool is_limm(uint64_t val) -{ - /* Taking a simplified view of the logical immediates for now, ignoring - the replication that can happen across the field. Match bit patterns - of the forms - 0....01....1 - 0..01..10..0 - and their inverses. */ - - /* Make things easier below, by testing the form with msb clear. */ - if ((int64_t)val < 0) { - val = ~val; - } - if (val == 0) { - return false; - } - val += val & -val; - return (val & (val - 1)) == 0; -} - -static int tcg_target_const_match(tcg_target_long val, TCGType type, - const TCGArgConstraint *arg_ct) -{ - int ct = arg_ct->ct; - - if (ct & TCG_CT_CONST) { - return 1; - } - if (type == TCG_TYPE_I32) { - val = (int32_t)val; - } - if ((ct & TCG_CT_CONST_AIMM) && (is_aimm(val) || is_aimm(-val))) { - return 1; - } - if ((ct & TCG_CT_CONST_LIMM) && is_limm(val)) { - return 1; - } - if ((ct & TCG_CT_CONST_ZERO) && val == 0) { - return 1; - } - if ((ct & TCG_CT_CONST_MONE) && val == -1) { - return 1; - } - - return 0; -} - -enum aarch64_cond_code { - COND_EQ = 0x0, - COND_NE = 0x1, - COND_CS = 0x2, /* Unsigned greater or equal */ - COND_HS = COND_CS, /* ALIAS greater or equal */ - COND_CC = 0x3, /* Unsigned less than */ - COND_LO = COND_CC, /* ALIAS Lower */ - COND_MI = 0x4, /* Negative */ - COND_PL = 0x5, /* Zero or greater */ - COND_VS = 0x6, /* Overflow */ - COND_VC = 0x7, /* No overflow */ - COND_HI = 0x8, /* Unsigned greater than */ - COND_LS = 0x9, /* Unsigned less or equal */ - COND_GE = 0xa, - COND_LT = 0xb, - COND_GT = 0xc, - COND_LE = 0xd, - COND_AL = 0xe, - COND_NV = 0xf, /* behaves like COND_AL here */ -}; - -static const enum aarch64_cond_code tcg_cond_to_aarch64[] = { - [TCG_COND_EQ] = COND_EQ, - [TCG_COND_NE] = COND_NE, - [TCG_COND_LT] = COND_LT, - [TCG_COND_GE] = COND_GE, - [TCG_COND_LE] = COND_LE, - [TCG_COND_GT] = COND_GT, - /* unsigned */ - [TCG_COND_LTU] = COND_LO, - [TCG_COND_GTU] = COND_HI, - [TCG_COND_GEU] = COND_HS, - [TCG_COND_LEU] = COND_LS, -}; - -typedef enum { - LDST_ST = 0, /* store */ - LDST_LD = 1, /* load */ - LDST_LD_S_X = 2, /* load and sign-extend into Xt */ - LDST_LD_S_W = 3, /* load and sign-extend into Wt */ -} AArch64LdstType; - -/* We encode the format of the insn into the beginning of the name, so that - we can have the preprocessor help "typecheck" the insn vs the output - function. Arm didn't provide us with nice names for the formats, so we - use the section number of the architecture reference manual in which the - instruction group is described. */ -typedef enum { - /* Compare and branch (immediate). */ - I3201_CBZ = 0x34000000, - I3201_CBNZ = 0x35000000, - - /* Conditional branch (immediate). */ - I3202_B_C = 0x54000000, - - /* Unconditional branch (immediate). */ - I3206_B = 0x14000000, - I3206_BL = 0x94000000, - - /* Unconditional branch (register). */ - I3207_BR = 0xd61f0000, - I3207_BLR = 0xd63f0000, - I3207_RET = 0xd65f0000, - - /* Load/store register. Described here as 3.3.12, but the helper - that emits them can transform to 3.3.10 or 3.3.13. */ - I3312_STRB = 0x38000000 | LDST_ST << 22 | MO_8 << 30, - I3312_STRH = 0x38000000 | LDST_ST << 22 | MO_16 << 30, - I3312_STRW = 0x38000000 | LDST_ST << 22 | MO_32 << 30, - I3312_STRX = 0x38000000 | LDST_ST << 22 | MO_64 << 30, - - I3312_LDRB = 0x38000000 | LDST_LD << 22 | MO_8 << 30, - I3312_LDRH = 0x38000000 | LDST_LD << 22 | MO_16 << 30, - I3312_LDRW = 0x38000000 | LDST_LD << 22 | MO_32 << 30, - I3312_LDRX = 0x38000000 | LDST_LD << 22 | MO_64 << 30, - - I3312_LDRSBW = 0x38000000 | LDST_LD_S_W << 22 | MO_8 << 30, - I3312_LDRSHW = 0x38000000 | LDST_LD_S_W << 22 | MO_16 << 30, - - I3312_LDRSBX = 0x38000000 | LDST_LD_S_X << 22 | MO_8 << 30, - I3312_LDRSHX = 0x38000000 | LDST_LD_S_X << 22 | MO_16 << 30, - I3312_LDRSWX = 0x38000000 | LDST_LD_S_X << 22 | MO_32 << 30, - - I3312_TO_I3310 = 0x00200800, - I3312_TO_I3313 = 0x01000000, - - /* Load/store register pair instructions. */ - I3314_LDP = 0x28400000, - I3314_STP = 0x28000000, - - /* Add/subtract immediate instructions. */ - I3401_ADDI = 0x11000000, - I3401_ADDSI = 0x31000000, - I3401_SUBI = 0x51000000, - I3401_SUBSI = 0x71000000, - - /* Bitfield instructions. */ - I3402_BFM = 0x33000000, - I3402_SBFM = 0x13000000, - I3402_UBFM = 0x53000000, - - /* Extract instruction. */ - I3403_EXTR = 0x13800000, - - /* Logical immediate instructions. */ - I3404_ANDI = 0x12000000, - I3404_ORRI = 0x32000000, - I3404_EORI = 0x52000000, - - /* Move wide immediate instructions. */ - I3405_MOVN = 0x12800000, - I3405_MOVZ = 0x52800000, - I3405_MOVK = 0x72800000, - - /* PC relative addressing instructions. */ - I3406_ADR = 0x10000000, - I3406_ADRP = 0x90000000, - - /* Add/subtract shifted register instructions (without a shift). */ - I3502_ADD = 0x0b000000, - I3502_ADDS = 0x2b000000, - I3502_SUB = 0x4b000000, - I3502_SUBS = 0x6b000000, - - /* Add/subtract shifted register instructions (with a shift). */ - I3502S_ADD_LSL = I3502_ADD, - - /* Add/subtract with carry instructions. */ - I3503_ADC = 0x1a000000, - I3503_SBC = 0x5a000000, - - /* Conditional select instructions. */ - I3506_CSEL = 0x1a800000, - I3506_CSINC = 0x1a800400, - - /* Data-processing (1 source) instructions. */ - I3507_REV16 = 0x5ac00400, - I3507_REV32 = 0x5ac00800, - I3507_REV64 = 0x5ac00c00, - - /* Data-processing (2 source) instructions. */ - I3508_LSLV = 0x1ac02000, - I3508_LSRV = 0x1ac02400, - I3508_ASRV = 0x1ac02800, - I3508_RORV = 0x1ac02c00, - I3508_SMULH = 0x9b407c00, - I3508_UMULH = 0x9bc07c00, - I3508_UDIV = 0x1ac00800, - I3508_SDIV = 0x1ac00c00, - - /* Data-processing (3 source) instructions. */ - I3509_MADD = 0x1b000000, - I3509_MSUB = 0x1b008000, - - /* Logical shifted register instructions (without a shift). */ - I3510_AND = 0x0a000000, - I3510_BIC = 0x0a200000, - I3510_ORR = 0x2a000000, - I3510_ORN = 0x2a200000, - I3510_EOR = 0x4a000000, - I3510_EON = 0x4a200000, - I3510_ANDS = 0x6a000000, -} AArch64Insn; - -static inline uint32_t tcg_in32(TCGContext *s) -{ - uint32_t v = *(uint32_t *)s->code_ptr; - return v; -} - -/* Emit an opcode with "type-checking" of the format. */ -#define tcg_out_insn(S, FMT, OP, ...) \ - glue(tcg_out_insn_,FMT)(S, glue(glue(glue(I,FMT),_),OP), ## __VA_ARGS__) - -static void tcg_out_insn_3201(TCGContext *s, AArch64Insn insn, TCGType ext, - TCGReg rt, int imm19) -{ - tcg_out32(s, insn | ext << 31 | (imm19 & 0x7ffff) << 5 | rt); -} - -static void tcg_out_insn_3202(TCGContext *s, AArch64Insn insn, - TCGCond c, int imm19) -{ - tcg_out32(s, insn | tcg_cond_to_aarch64[c] | (imm19 & 0x7ffff) << 5); -} - -static void tcg_out_insn_3206(TCGContext *s, AArch64Insn insn, int imm26) -{ - tcg_out32(s, insn | (imm26 & 0x03ffffff)); -} - -static void tcg_out_insn_3207(TCGContext *s, AArch64Insn insn, TCGReg rn) -{ - tcg_out32(s, insn | rn << 5); -} - -static void tcg_out_insn_3314(TCGContext *s, AArch64Insn insn, - TCGReg r1, TCGReg r2, TCGReg rn, - tcg_target_long ofs, bool pre, bool w) -{ - insn |= 1u << 31; /* ext */ - insn |= pre << 24; - insn |= w << 23; - - assert(ofs >= -0x200 && ofs < 0x200 && (ofs & 7) == 0); - insn |= (ofs & (0x7f << 3)) << (15 - 3); - - tcg_out32(s, insn | r2 << 10 | rn << 5 | r1); -} - -static void tcg_out_insn_3401(TCGContext *s, AArch64Insn insn, TCGType ext, - TCGReg rd, TCGReg rn, uint64_t aimm) -{ - if (aimm > 0xfff) { - assert((aimm & 0xfff) == 0); - aimm >>= 12; - assert(aimm <= 0xfff); - aimm |= 1 << 12; /* apply LSL 12 */ - } - tcg_out32(s, insn | ext << 31 | aimm << 10 | rn << 5 | rd); -} - -/* This function can be used for both 3.4.2 (Bitfield) and 3.4.4 - (Logical immediate). Both insn groups have N, IMMR and IMMS fields - that feed the DecodeBitMasks pseudo function. */ -static void tcg_out_insn_3402(TCGContext *s, AArch64Insn insn, TCGType ext, - TCGReg rd, TCGReg rn, int n, int immr, int imms) -{ - tcg_out32(s, insn | ext << 31 | n << 22 | immr << 16 | imms << 10 - | rn << 5 | rd); -} - -#define tcg_out_insn_3404 tcg_out_insn_3402 - -static void tcg_out_insn_3403(TCGContext *s, AArch64Insn insn, TCGType ext, - TCGReg rd, TCGReg rn, TCGReg rm, int imms) -{ - tcg_out32(s, insn | ext << 31 | ext << 22 | rm << 16 | imms << 10 - | rn << 5 | rd); -} - -/* This function is used for the Move (wide immediate) instruction group. - Note that SHIFT is a full shift count, not the 2 bit HW field. */ -static void tcg_out_insn_3405(TCGContext *s, AArch64Insn insn, TCGType ext, - TCGReg rd, uint16_t half, unsigned shift) -{ - assert((shift & ~0x30) == 0); - tcg_out32(s, insn | ext << 31 | shift << (21 - 4) | half << 5 | rd); -} - -static void tcg_out_insn_3406(TCGContext *s, AArch64Insn insn, - TCGReg rd, int64_t disp) -{ - tcg_out32(s, insn | (disp & 3) << 29 | (disp & 0x1ffffc) << (5 - 2) | rd); -} - -/* This function is for both 3.5.2 (Add/Subtract shifted register), for - the rare occasion when we actually want to supply a shift amount. */ -static inline void tcg_out_insn_3502S(TCGContext *s, AArch64Insn insn, - TCGType ext, TCGReg rd, TCGReg rn, - TCGReg rm, int imm6) -{ - tcg_out32(s, insn | ext << 31 | rm << 16 | imm6 << 10 | rn << 5 | rd); -} - -/* This function is for 3.5.2 (Add/subtract shifted register), - and 3.5.10 (Logical shifted register), for the vast majorty of cases - when we don't want to apply a shift. Thus it can also be used for - 3.5.3 (Add/subtract with carry) and 3.5.8 (Data processing 2 source). */ -static void tcg_out_insn_3502(TCGContext *s, AArch64Insn insn, TCGType ext, - TCGReg rd, TCGReg rn, TCGReg rm) -{ - tcg_out32(s, insn | ext << 31 | rm << 16 | rn << 5 | rd); -} - -#define tcg_out_insn_3503 tcg_out_insn_3502 -#define tcg_out_insn_3508 tcg_out_insn_3502 -#define tcg_out_insn_3510 tcg_out_insn_3502 - -static void tcg_out_insn_3506(TCGContext *s, AArch64Insn insn, TCGType ext, - TCGReg rd, TCGReg rn, TCGReg rm, TCGCond c) -{ - tcg_out32(s, insn | ext << 31 | rm << 16 | rn << 5 | rd - | tcg_cond_to_aarch64[c] << 12); -} - -static void tcg_out_insn_3507(TCGContext *s, AArch64Insn insn, TCGType ext, - TCGReg rd, TCGReg rn) -{ - tcg_out32(s, insn | ext << 31 | rn << 5 | rd); -} - -static void tcg_out_insn_3509(TCGContext *s, AArch64Insn insn, TCGType ext, - TCGReg rd, TCGReg rn, TCGReg rm, TCGReg ra) -{ - tcg_out32(s, insn | ext << 31 | rm << 16 | ra << 10 | rn << 5 | rd); -} - -static void tcg_out_insn_3310(TCGContext *s, AArch64Insn insn, - TCGReg rd, TCGReg base, TCGType ext, - TCGReg regoff) -{ - /* Note the AArch64Insn constants above are for C3.3.12. Adjust. */ - tcg_out32(s, insn | I3312_TO_I3310 | regoff << 16 | - 0x4000 | ext << 13 | base << 5 | rd); -} - -static void tcg_out_insn_3312(TCGContext *s, AArch64Insn insn, - TCGReg rd, TCGReg rn, intptr_t offset) -{ - tcg_out32(s, insn | (offset & 0x1ff) << 12 | rn << 5 | rd); -} - -static void tcg_out_insn_3313(TCGContext *s, AArch64Insn insn, - TCGReg rd, TCGReg rn, uintptr_t scaled_uimm) -{ - /* Note the AArch64Insn constants above are for C3.3.12. Adjust. */ - tcg_out32(s, insn | I3312_TO_I3313 | scaled_uimm << 10 | rn << 5 | rd); -} - -/* Register to register move using ORR (shifted register with no shift). */ -static void tcg_out_movr(TCGContext *s, TCGType ext, TCGReg rd, TCGReg rm) -{ - tcg_out_insn(s, 3510, ORR, ext, rd, TCG_REG_XZR, rm); -} - -/* Register to register move using ADDI (move to/from SP). */ -static void tcg_out_movr_sp(TCGContext *s, TCGType ext, TCGReg rd, TCGReg rn) -{ - tcg_out_insn(s, 3401, ADDI, ext, rd, rn, 0); -} - -/* This function is used for the Logical (immediate) instruction group. - The value of LIMM must satisfy IS_LIMM. See the comment above about - only supporting simplified logical immediates. */ -static void tcg_out_logicali(TCGContext *s, AArch64Insn insn, TCGType ext, - TCGReg rd, TCGReg rn, uint64_t limm) -{ - unsigned h, l, r, c; - - assert(is_limm(limm)); - - h = clz64(limm); - l = ctz64(limm); - if (l == 0) { - r = 0; /* form 0....01....1 */ - c = ctz64(~limm) - 1; - if (h == 0) { - r = clz64(~limm); /* form 1..10..01..1 */ - c += r; - } - } else { - r = 64 - l; /* form 1....10....0 or 0..01..10..0 */ - c = r - h - 1; - } - if (ext == TCG_TYPE_I32) { - r &= 31; - c &= 31; - } - - tcg_out_insn_3404(s, insn, ext, rd, rn, ext, r, c); -} - -static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg rd, - tcg_target_long value) -{ - AArch64Insn insn; - int i, wantinv, shift; - tcg_target_long svalue = value; - tcg_target_long ivalue = ~value; - tcg_target_long imask; - - /* For 32-bit values, discard potential garbage in value. For 64-bit - values within [2**31, 2**32-1], we can create smaller sequences by - interpreting this as a negative 32-bit number, while ensuring that - the high 32 bits are cleared by setting SF=0. */ - if (type == TCG_TYPE_I32 || (value & ~0xffffffffull) == 0) { - svalue = (int32_t)value; - value = (uint32_t)value; - ivalue = (uint32_t)ivalue; - type = TCG_TYPE_I32; - } - - /* Speed things up by handling the common case of small positive - and negative values specially. */ - if ((value & ~0xffffull) == 0) { - tcg_out_insn(s, 3405, MOVZ, type, rd, value, 0); - return; - } else if ((ivalue & ~0xffffull) == 0) { - tcg_out_insn(s, 3405, MOVN, type, rd, ivalue, 0); - return; - } - - /* Check for bitfield immediates. For the benefit of 32-bit quantities, - use the sign-extended value. That lets us match rotated values such - as 0xff0000ff with the same 64-bit logic matching 0xffffffffff0000ff. */ - if (is_limm(svalue)) { - tcg_out_logicali(s, I3404_ORRI, type, rd, TCG_REG_XZR, svalue); - return; - } - - /* Look for host pointer values within 4G of the PC. This happens - often when loading pointers to QEMU's own data structures. */ - if (type == TCG_TYPE_I64) { - tcg_target_long disp = (value >> 12) - ((intptr_t)s->code_ptr >> 12); - if (disp == sextract64(disp, 0, 21)) { - tcg_out_insn(s, 3406, ADRP, rd, disp); - if (value & 0xfff) { - tcg_out_insn(s, 3401, ADDI, type, rd, rd, value & 0xfff); - } - return; - } - } - - /* Would it take fewer insns to begin with MOVN? For the value and its - inverse, count the number of 16-bit lanes that are 0. */ - for (i = wantinv = imask = 0; i < 64; i += 16) { - tcg_target_long mask = 0xffffull << i; - if ((value & mask) == 0) { - wantinv -= 1; - } - if ((ivalue & mask) == 0) { - wantinv += 1; - imask |= mask; - } - } - - /* If we had more 0xffff than 0x0000, invert VALUE and use MOVN. */ - insn = I3405_MOVZ; - if (wantinv > 0) { - value = ivalue; - insn = I3405_MOVN; - } - - /* Find the lowest lane that is not 0x0000. */ - shift = ctz64(value) & (63 & -16); - tcg_out_insn_3405(s, insn, type, rd, value >> shift, shift); - - if (wantinv > 0) { - /* Re-invert the value, so MOVK sees non-inverted bits. */ - value = ~value; - /* Clear out all the 0xffff lanes. */ - value ^= imask; - } - /* Clear out the lane that we just set. */ - value &= ~(0xffffUL << shift); - - /* Iterate until all lanes have been set, and thus cleared from VALUE. */ - while (value) { - shift = ctz64(value) & (63 & -16); - tcg_out_insn(s, 3405, MOVK, type, rd, value >> shift, shift); - value &= ~(0xffffUL << shift); - } -} - -/* Define something more legible for general use. */ -#define tcg_out_ldst_r tcg_out_insn_3310 - -static void tcg_out_ldst(TCGContext *s, AArch64Insn insn, - TCGReg rd, TCGReg rn, intptr_t offset) -{ - TCGMemOp size = (uint32_t)insn >> 30; - - /* If the offset is naturally aligned and in range, then we can - use the scaled uimm12 encoding */ - if (offset >= 0 && !(offset & ((1 << size) - 1))) { - uintptr_t scaled_uimm = offset >> size; - if (scaled_uimm <= 0xfff) { - tcg_out_insn_3313(s, insn, rd, rn, scaled_uimm); - return; - } - } - - /* Small signed offsets can use the unscaled encoding. */ - if (offset >= -256 && offset < 256) { - tcg_out_insn_3312(s, insn, rd, rn, offset); - return; - } - - /* Worst-case scenario, move offset to temp register, use reg offset. */ - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_TMP, offset); - tcg_out_ldst_r(s, insn, rd, rn, TCG_TYPE_I64, TCG_REG_TMP); -} - -static inline void tcg_out_mov(TCGContext *s, - TCGType type, TCGReg ret, TCGReg arg) -{ - if (ret != arg) { - tcg_out_movr(s, type, ret, arg); - } -} - -static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg arg, - TCGReg arg1, intptr_t arg2) -{ - tcg_out_ldst(s, type == TCG_TYPE_I32 ? I3312_LDRW : I3312_LDRX, - arg, arg1, arg2); -} - -static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, - TCGReg arg1, intptr_t arg2) -{ - tcg_out_ldst(s, type == TCG_TYPE_I32 ? I3312_STRW : I3312_STRX, - arg, arg1, arg2); -} - -static inline void tcg_out_bfm(TCGContext *s, TCGType ext, TCGReg rd, - TCGReg rn, unsigned int a, unsigned int b) -{ - tcg_out_insn(s, 3402, BFM, ext, rd, rn, ext, a, b); -} - -static inline void tcg_out_ubfm(TCGContext *s, TCGType ext, TCGReg rd, - TCGReg rn, unsigned int a, unsigned int b) -{ - tcg_out_insn(s, 3402, UBFM, ext, rd, rn, ext, a, b); -} - -static inline void tcg_out_sbfm(TCGContext *s, TCGType ext, TCGReg rd, - TCGReg rn, unsigned int a, unsigned int b) -{ - tcg_out_insn(s, 3402, SBFM, ext, rd, rn, ext, a, b); -} - -static inline void tcg_out_extr(TCGContext *s, TCGType ext, TCGReg rd, - TCGReg rn, TCGReg rm, unsigned int a) -{ - tcg_out_insn(s, 3403, EXTR, ext, rd, rn, rm, a); -} - -static inline void tcg_out_shl(TCGContext *s, TCGType ext, - TCGReg rd, TCGReg rn, unsigned int m) -{ - int bits = ext ? 64 : 32; - int max = bits - 1; - tcg_out_ubfm(s, ext, rd, rn, bits - (m & max), max - (m & max)); -} - -static inline void tcg_out_shr(TCGContext *s, TCGType ext, - TCGReg rd, TCGReg rn, unsigned int m) -{ - int max = ext ? 63 : 31; - tcg_out_ubfm(s, ext, rd, rn, m & max, max); -} - -static inline void tcg_out_sar(TCGContext *s, TCGType ext, - TCGReg rd, TCGReg rn, unsigned int m) -{ - int max = ext ? 63 : 31; - tcg_out_sbfm(s, ext, rd, rn, m & max, max); -} - -static inline void tcg_out_rotr(TCGContext *s, TCGType ext, - TCGReg rd, TCGReg rn, unsigned int m) -{ - int max = ext ? 63 : 31; - tcg_out_extr(s, ext, rd, rn, rn, m & max); -} - -static inline void tcg_out_rotl(TCGContext *s, TCGType ext, - TCGReg rd, TCGReg rn, unsigned int m) -{ - int bits = ext ? 64 : 32; - int max = bits - 1; - tcg_out_extr(s, ext, rd, rn, rn, bits - (m & max)); -} - -static inline void tcg_out_dep(TCGContext *s, TCGType ext, TCGReg rd, - TCGReg rn, unsigned lsb, unsigned width) -{ - unsigned size = ext ? 64 : 32; - unsigned a = (size - lsb) & (size - 1); - unsigned b = width - 1; - tcg_out_bfm(s, ext, rd, rn, a, b); -} - -static void tcg_out_cmp(TCGContext *s, TCGType ext, TCGReg a, - tcg_target_long b, bool const_b) -{ - if (const_b) { - /* Using CMP or CMN aliases. */ - if (b >= 0) { - tcg_out_insn(s, 3401, SUBSI, ext, TCG_REG_XZR, a, b); - } else { - tcg_out_insn(s, 3401, ADDSI, ext, TCG_REG_XZR, a, -b); - } - } else { - /* Using CMP alias SUBS wzr, Wn, Wm */ - tcg_out_insn(s, 3502, SUBS, ext, TCG_REG_XZR, a, b); - } -} - -static inline void tcg_out_goto(TCGContext *s, tcg_insn_unit *target) -{ - ptrdiff_t offset = target - s->code_ptr; - assert(offset == sextract64(offset, 0, 26)); - tcg_out_insn(s, 3206, B, offset); -} - -static inline void tcg_out_goto_noaddr(TCGContext *s) -{ - /* We pay attention here to not modify the branch target by reading from - the buffer. This ensure that caches and memory are kept coherent during - retranslation. Mask away possible garbage in the high bits for the - first translation, while keeping the offset bits for retranslation. */ - uint32_t old = tcg_in32(s); - tcg_out_insn(s, 3206, B, old); -} - -static inline void tcg_out_goto_cond_noaddr(TCGContext *s, TCGCond c) -{ - /* See comments in tcg_out_goto_noaddr. */ - uint32_t old = tcg_in32(s) >> 5; - tcg_out_insn(s, 3202, B_C, c, old); -} - -static inline void tcg_out_callr(TCGContext *s, TCGReg reg) -{ - tcg_out_insn(s, 3207, BLR, reg); -} - -static inline void tcg_out_call(TCGContext *s, tcg_insn_unit *target) -{ - ptrdiff_t offset = target - s->code_ptr; - if (offset == sextract64(offset, 0, 26)) { - tcg_out_insn(s, 3206, BL, offset); - } else { - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_TMP, (intptr_t)target); - tcg_out_callr(s, TCG_REG_TMP); - } -} - -void aarch64_tb_set_jmp_target(uintptr_t jmp_addr, uintptr_t addr) -{ - tcg_insn_unit *code_ptr = (tcg_insn_unit *)jmp_addr; - tcg_insn_unit *target = (tcg_insn_unit *)addr; - - reloc_pc26(code_ptr, target); - flush_icache_range(jmp_addr, jmp_addr + 4); -} - -static inline void tcg_out_goto_label(TCGContext *s, TCGLabel *l) -{ - if (!l->has_value) { - tcg_out_reloc(s, s->code_ptr, R_AARCH64_JUMP26, l, 0); - tcg_out_goto_noaddr(s); - } else { - tcg_out_goto(s, l->u.value_ptr); - } -} - -static void tcg_out_brcond(TCGContext *s, TCGMemOp ext, TCGCond c, TCGArg a, - TCGArg b, bool b_const, TCGLabel *l) -{ - intptr_t offset; - bool need_cmp; - - if (b_const && b == 0 && (c == TCG_COND_EQ || c == TCG_COND_NE)) { - need_cmp = false; - } else { - need_cmp = true; - tcg_out_cmp(s, ext, a, b, b_const); - } - - if (!l->has_value) { - tcg_out_reloc(s, s->code_ptr, R_AARCH64_CONDBR19, l, 0); - offset = tcg_in32(s) >> 5; - } else { - offset = l->u.value_ptr - s->code_ptr; - assert(offset == sextract64(offset, 0, 19)); - } - - if (need_cmp) { - tcg_out_insn(s, 3202, B_C, c, offset); - } else if (c == TCG_COND_EQ) { - tcg_out_insn(s, 3201, CBZ, ext, a, offset); - } else { - tcg_out_insn(s, 3201, CBNZ, ext, a, offset); - } -} - -static inline void tcg_out_rev64(TCGContext *s, TCGReg rd, TCGReg rn) -{ - tcg_out_insn(s, 3507, REV64, TCG_TYPE_I64, rd, rn); -} - -static inline void tcg_out_rev32(TCGContext *s, TCGReg rd, TCGReg rn) -{ - tcg_out_insn(s, 3507, REV32, TCG_TYPE_I32, rd, rn); -} - -static inline void tcg_out_rev16(TCGContext *s, TCGReg rd, TCGReg rn) -{ - tcg_out_insn(s, 3507, REV16, TCG_TYPE_I32, rd, rn); -} - -static inline void tcg_out_sxt(TCGContext *s, TCGType ext, TCGMemOp s_bits, - TCGReg rd, TCGReg rn) -{ - /* Using ALIASes SXTB, SXTH, SXTW, of SBFM Xd, Xn, #0, #7|15|31 */ - int bits = (8 << s_bits) - 1; - tcg_out_sbfm(s, ext, rd, rn, 0, bits); -} - -static inline void tcg_out_uxt(TCGContext *s, TCGMemOp s_bits, - TCGReg rd, TCGReg rn) -{ - /* Using ALIASes UXTB, UXTH of UBFM Wd, Wn, #0, #7|15 */ - int bits = (8 << s_bits) - 1; - tcg_out_ubfm(s, 0, rd, rn, 0, bits); -} - -static void tcg_out_addsubi(TCGContext *s, int ext, TCGReg rd, - TCGReg rn, int64_t aimm) -{ - if (aimm >= 0) { - tcg_out_insn(s, 3401, ADDI, ext, rd, rn, aimm); - } else { - tcg_out_insn(s, 3401, SUBI, ext, rd, rn, -aimm); - } -} - -static inline void tcg_out_addsub2(TCGContext *s, int ext, TCGReg rl, - TCGReg rh, TCGReg al, TCGReg ah, - tcg_target_long bl, tcg_target_long bh, - bool const_bl, bool const_bh, bool sub) -{ - TCGReg orig_rl = rl; - AArch64Insn insn; - - if (rl == ah || (!const_bh && rl == bh)) { - rl = TCG_REG_TMP; - } - - if (const_bl) { - insn = I3401_ADDSI; - if ((bl < 0) ^ sub) { - insn = I3401_SUBSI; - bl = -bl; - } - tcg_out_insn_3401(s, insn, ext, rl, al, bl); - } else { - tcg_out_insn_3502(s, sub ? I3502_SUBS : I3502_ADDS, ext, rl, al, bl); - } - - insn = I3503_ADC; - if (const_bh) { - /* Note that the only two constants we support are 0 and -1, and - that SBC = rn + ~rm + c, so adc -1 is sbc 0, and vice-versa. */ - if ((bh != 0) ^ sub) { - insn = I3503_SBC; - } - bh = TCG_REG_XZR; - } else if (sub) { - insn = I3503_SBC; - } - tcg_out_insn_3503(s, insn, ext, rh, ah, bh); - - tcg_out_mov(s, ext, orig_rl, rl); -} - -#ifdef CONFIG_SOFTMMU -/* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, - * TCGMemOpIdx oi, uintptr_t ra) - */ -static void * const qemu_ld_helpers[16] = { - [MO_UB] = helper_ret_ldub_mmu, - [MO_LEUW] = helper_le_lduw_mmu, - [MO_LEUL] = helper_le_ldul_mmu, - [MO_LEQ] = helper_le_ldq_mmu, - [MO_BEUW] = helper_be_lduw_mmu, - [MO_BEUL] = helper_be_ldul_mmu, - [MO_BEQ] = helper_be_ldq_mmu, -}; - -/* helper signature: helper_ret_st_mmu(CPUState *env, target_ulong addr, - * uintxx_t val, TCGMemOpIdx oi, - * uintptr_t ra) - */ -static void * const qemu_st_helpers[16] = { - [MO_UB] = helper_ret_stb_mmu, - [MO_LEUW] = helper_le_stw_mmu, - [MO_LEUL] = helper_le_stl_mmu, - [MO_LEQ] = helper_le_stq_mmu, - [MO_BEUW] = helper_be_stw_mmu, - [MO_BEUL] = helper_be_stl_mmu, - [MO_BEQ] = helper_be_stq_mmu, -}; - -static inline void tcg_out_adr(TCGContext *s, TCGReg rd, void *target) -{ - ptrdiff_t offset = tcg_pcrel_diff(s, target); - assert(offset == sextract64(offset, 0, 21)); - tcg_out_insn(s, 3406, ADR, rd, offset); -} - -static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) -{ - TCGMemOpIdx oi = lb->oi; - TCGMemOp opc = get_memop(oi); - TCGMemOp size = opc & MO_SIZE; - - reloc_pc19(lb->label_ptr[0], s->code_ptr); - - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_X0, TCG_AREG0); - tcg_out_mov(s, TARGET_LONG_BITS == 64, TCG_REG_X1, lb->addrlo_reg); - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_X2, oi); - tcg_out_adr(s, TCG_REG_X3, lb->raddr); - tcg_out_call(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SIZE)]); - if (opc & MO_SIGN) { - tcg_out_sxt(s, lb->type, size, lb->datalo_reg, TCG_REG_X0); - } else { - tcg_out_mov(s, size == MO_64, lb->datalo_reg, TCG_REG_X0); - } - - tcg_out_goto(s, lb->raddr); -} - -static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) -{ - TCGMemOpIdx oi = lb->oi; - TCGMemOp opc = get_memop(oi); - TCGMemOp size = opc & MO_SIZE; - - reloc_pc19(lb->label_ptr[0], s->code_ptr); - - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_X0, TCG_AREG0); - tcg_out_mov(s, TARGET_LONG_BITS == 64, TCG_REG_X1, lb->addrlo_reg); - tcg_out_mov(s, size == MO_64, TCG_REG_X2, lb->datalo_reg); - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_X3, oi); - tcg_out_adr(s, TCG_REG_X4, lb->raddr); - tcg_out_call(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)]); - tcg_out_goto(s, lb->raddr); -} - -static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOpIdx oi, - TCGType ext, TCGReg data_reg, TCGReg addr_reg, - tcg_insn_unit *raddr, tcg_insn_unit *label_ptr) -{ - TCGLabelQemuLdst *label = new_ldst_label(s); - - label->is_ld = is_ld; - label->oi = oi; - label->type = ext; - label->datalo_reg = data_reg; - label->addrlo_reg = addr_reg; - label->raddr = raddr; - label->label_ptr[0] = label_ptr; -} - -/* Load and compare a TLB entry, emitting the conditional jump to the - slow path for the failure case, which will be patched later when finalizing - the slow path. Generated code returns the host addend in X1, - clobbers X0,X2,X3,TMP. */ -static void tcg_out_tlb_read(TCGContext *s, TCGReg addr_reg, TCGMemOp s_bits, - tcg_insn_unit **label_ptr, int mem_index, - bool is_read) -{ - TCGReg base = TCG_AREG0; - int tlb_offset = is_read ? - offsetof(CPUArchState, tlb_table[mem_index][0].addr_read) - : offsetof(CPUArchState, tlb_table[mem_index][0].addr_write); - - /* Extract the TLB index from the address into X0. - X0 = - addr_reg */ - tcg_out_ubfm(s, TARGET_LONG_BITS == 64, TCG_REG_X0, addr_reg, - TARGET_PAGE_BITS, TARGET_PAGE_BITS + CPU_TLB_BITS); - - /* Store the page mask part of the address and the low s_bits into X3. - Later this allows checking for equality and alignment at the same time. - X3 = addr_reg & (PAGE_MASK | ((1 << s_bits) - 1)) */ - tcg_out_logicali(s, I3404_ANDI, TARGET_LONG_BITS == 64, TCG_REG_X3, - addr_reg, TARGET_PAGE_MASK | ((1 << s_bits) - 1)); - - /* Add any "high bits" from the tlb offset to the env address into X2, - to take advantage of the LSL12 form of the ADDI instruction. - X2 = env + (tlb_offset & 0xfff000) */ - if (tlb_offset & 0xfff000) { - tcg_out_insn(s, 3401, ADDI, TCG_TYPE_I64, TCG_REG_X2, base, - tlb_offset & 0xfff000); - base = TCG_REG_X2; - } - - /* Merge the tlb index contribution into X2. - X2 = X2 + (X0 << CPU_TLB_ENTRY_BITS) */ - tcg_out_insn(s, 3502S, ADD_LSL, TCG_TYPE_I64, TCG_REG_X2, base, - TCG_REG_X0, CPU_TLB_ENTRY_BITS); - - /* Merge "low bits" from tlb offset, load the tlb comparator into X0. - X0 = load [X2 + (tlb_offset & 0x000fff)] */ - tcg_out_ldst(s, TARGET_LONG_BITS == 32 ? I3312_LDRW : I3312_LDRX, - TCG_REG_X0, TCG_REG_X2, tlb_offset & 0xfff); - - /* Load the tlb addend. Do that early to avoid stalling. - X1 = load [X2 + (tlb_offset & 0xfff) + offsetof(addend)] */ - tcg_out_ldst(s, I3312_LDRX, TCG_REG_X1, TCG_REG_X2, - (tlb_offset & 0xfff) + (offsetof(CPUTLBEntry, addend)) - - (is_read ? offsetof(CPUTLBEntry, addr_read) - : offsetof(CPUTLBEntry, addr_write))); - - /* Perform the address comparison. */ - tcg_out_cmp(s, (TARGET_LONG_BITS == 64), TCG_REG_X0, TCG_REG_X3, 0); - - /* If not equal, we jump to the slow path. */ - *label_ptr = s->code_ptr; - tcg_out_goto_cond_noaddr(s, TCG_COND_NE); -} - -#endif /* CONFIG_SOFTMMU */ - -static void tcg_out_qemu_ld_direct(TCGContext *s, TCGMemOp memop, TCGType ext, - TCGReg data_r, TCGReg addr_r, - TCGType otype, TCGReg off_r) -{ - const TCGMemOp bswap = memop & MO_BSWAP; - - switch (memop & MO_SSIZE) { - case MO_UB: - tcg_out_ldst_r(s, I3312_LDRB, data_r, addr_r, otype, off_r); - break; - case MO_SB: - tcg_out_ldst_r(s, ext ? I3312_LDRSBX : I3312_LDRSBW, - data_r, addr_r, otype, off_r); - break; - case MO_UW: - tcg_out_ldst_r(s, I3312_LDRH, data_r, addr_r, otype, off_r); - if (bswap) { - tcg_out_rev16(s, data_r, data_r); - } - break; - case MO_SW: - if (bswap) { - tcg_out_ldst_r(s, I3312_LDRH, data_r, addr_r, otype, off_r); - tcg_out_rev16(s, data_r, data_r); - tcg_out_sxt(s, ext, MO_16, data_r, data_r); - } else { - tcg_out_ldst_r(s, (ext ? I3312_LDRSHX : I3312_LDRSHW), - data_r, addr_r, otype, off_r); - } - break; - case MO_UL: - tcg_out_ldst_r(s, I3312_LDRW, data_r, addr_r, otype, off_r); - if (bswap) { - tcg_out_rev32(s, data_r, data_r); - } - break; - case MO_SL: - if (bswap) { - tcg_out_ldst_r(s, I3312_LDRW, data_r, addr_r, otype, off_r); - tcg_out_rev32(s, data_r, data_r); - tcg_out_sxt(s, TCG_TYPE_I64, MO_32, data_r, data_r); - } else { - tcg_out_ldst_r(s, I3312_LDRSWX, data_r, addr_r, otype, off_r); - } - break; - case MO_Q: - tcg_out_ldst_r(s, I3312_LDRX, data_r, addr_r, otype, off_r); - if (bswap) { - tcg_out_rev64(s, data_r, data_r); - } - break; - default: - tcg_abort(); - } -} - -static void tcg_out_qemu_st_direct(TCGContext *s, TCGMemOp memop, - TCGReg data_r, TCGReg addr_r, - TCGType otype, TCGReg off_r) -{ - const TCGMemOp bswap = memop & MO_BSWAP; - - switch (memop & MO_SIZE) { - case MO_8: - tcg_out_ldst_r(s, I3312_STRB, data_r, addr_r, otype, off_r); - break; - case MO_16: - if (bswap && data_r != TCG_REG_XZR) { - tcg_out_rev16(s, TCG_REG_TMP, data_r); - data_r = TCG_REG_TMP; - } - tcg_out_ldst_r(s, I3312_STRH, data_r, addr_r, otype, off_r); - break; - case MO_32: - if (bswap && data_r != TCG_REG_XZR) { - tcg_out_rev32(s, TCG_REG_TMP, data_r); - data_r = TCG_REG_TMP; - } - tcg_out_ldst_r(s, I3312_STRW, data_r, addr_r, otype, off_r); - break; - case MO_64: - if (bswap && data_r != TCG_REG_XZR) { - tcg_out_rev64(s, TCG_REG_TMP, data_r); - data_r = TCG_REG_TMP; - } - tcg_out_ldst_r(s, I3312_STRX, data_r, addr_r, otype, off_r); - break; - default: - tcg_abort(); - } -} - -static void tcg_out_qemu_ld(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - TCGMemOpIdx oi, TCGType ext) -{ - TCGMemOp memop = get_memop(oi); - const TCGType otype = TARGET_LONG_BITS == 64 ? TCG_TYPE_I64 : TCG_TYPE_I32; -#ifdef CONFIG_SOFTMMU - unsigned mem_index = get_mmuidx(oi); - TCGMemOp s_bits = memop & MO_SIZE; - tcg_insn_unit *label_ptr; - - tcg_out_tlb_read(s, addr_reg, s_bits, &label_ptr, mem_index, 1); - tcg_out_qemu_ld_direct(s, memop, ext, data_reg, - TCG_REG_X1, otype, addr_reg); - add_qemu_ldst_label(s, true, oi, ext, data_reg, addr_reg, - s->code_ptr, label_ptr); -#else /* !CONFIG_SOFTMMU */ - tcg_out_qemu_ld_direct(s, memop, ext, data_reg, - GUEST_BASE ? TCG_REG_GUEST_BASE : TCG_REG_XZR, - otype, addr_reg); -#endif /* CONFIG_SOFTMMU */ -} - -static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - TCGMemOpIdx oi) -{ - TCGMemOp memop = get_memop(oi); - const TCGType otype = TARGET_LONG_BITS == 64 ? TCG_TYPE_I64 : TCG_TYPE_I32; -#ifdef CONFIG_SOFTMMU - unsigned mem_index = get_mmuidx(oi); - TCGMemOp s_bits = memop & MO_SIZE; - tcg_insn_unit *label_ptr; - - tcg_out_tlb_read(s, addr_reg, s_bits, &label_ptr, mem_index, 0); - tcg_out_qemu_st_direct(s, memop, data_reg, - TCG_REG_X1, otype, addr_reg); - add_qemu_ldst_label(s, false, oi, s_bits == MO_64, data_reg, addr_reg, - s->code_ptr, label_ptr); -#else /* !CONFIG_SOFTMMU */ - tcg_out_qemu_st_direct(s, memop, data_reg, - GUEST_BASE ? TCG_REG_GUEST_BASE : TCG_REG_XZR, - otype, addr_reg); -#endif /* CONFIG_SOFTMMU */ -} - -static tcg_insn_unit *tb_ret_addr; - -static void tcg_out_op(TCGContext *s, TCGOpcode opc, - const TCGArg args[TCG_MAX_OP_ARGS], - const int const_args[TCG_MAX_OP_ARGS]) -{ - /* 99% of the time, we can signal the use of extension registers - by looking to see if the opcode handles 64-bit data. */ - TCGType ext = (tcg_op_defs[opc].flags & TCG_OPF_64BIT) != 0; - - /* Hoist the loads of the most common arguments. */ - TCGArg a0 = args[0]; - TCGArg a1 = args[1]; - TCGArg a2 = args[2]; - int c2 = const_args[2]; - - /* Some operands are defined with "rZ" constraint, a register or - the zero register. These need not actually test args[I] == 0. */ -#define REG0(I) (const_args[I] ? TCG_REG_XZR : (TCGReg)args[I]) - - switch (opc) { - case INDEX_op_exit_tb: - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_X0, a0); - tcg_out_goto(s, tb_ret_addr); - break; - - case INDEX_op_goto_tb: -#ifndef USE_DIRECT_JUMP -#error "USE_DIRECT_JUMP required for aarch64" -#endif - assert(s->tb_jmp_offset != NULL); /* consistency for USE_DIRECT_JUMP */ - s->tb_jmp_offset[a0] = tcg_current_code_size(s); - /* actual branch destination will be patched by - aarch64_tb_set_jmp_target later, beware retranslation. */ - tcg_out_goto_noaddr(s); - s->tb_next_offset[a0] = tcg_current_code_size(s); - break; - - case INDEX_op_br: - tcg_out_goto_label(s, arg_label(a0)); - break; - - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - tcg_out_ldst(s, I3312_LDRB, a0, a1, a2); - break; - case INDEX_op_ld8s_i32: - tcg_out_ldst(s, I3312_LDRSBW, a0, a1, a2); - break; - case INDEX_op_ld8s_i64: - tcg_out_ldst(s, I3312_LDRSBX, a0, a1, a2); - break; - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - tcg_out_ldst(s, I3312_LDRH, a0, a1, a2); - break; - case INDEX_op_ld16s_i32: - tcg_out_ldst(s, I3312_LDRSHW, a0, a1, a2); - break; - case INDEX_op_ld16s_i64: - tcg_out_ldst(s, I3312_LDRSHX, a0, a1, a2); - break; - case INDEX_op_ld_i32: - case INDEX_op_ld32u_i64: - tcg_out_ldst(s, I3312_LDRW, a0, a1, a2); - break; - case INDEX_op_ld32s_i64: - tcg_out_ldst(s, I3312_LDRSWX, a0, a1, a2); - break; - case INDEX_op_ld_i64: - tcg_out_ldst(s, I3312_LDRX, a0, a1, a2); - break; - - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - tcg_out_ldst(s, I3312_STRB, REG0(0), a1, a2); - break; - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - tcg_out_ldst(s, I3312_STRH, REG0(0), a1, a2); - break; - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - tcg_out_ldst(s, I3312_STRW, REG0(0), a1, a2); - break; - case INDEX_op_st_i64: - tcg_out_ldst(s, I3312_STRX, REG0(0), a1, a2); - break; - - case INDEX_op_add_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_add_i64: - if (c2) { - tcg_out_addsubi(s, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3502, ADD, ext, a0, a1, a2); - } - break; - - case INDEX_op_sub_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_sub_i64: - if (c2) { - tcg_out_addsubi(s, ext, a0, a1, -a2); - } else { - tcg_out_insn(s, 3502, SUB, ext, a0, a1, a2); - } - break; - - case INDEX_op_neg_i64: - case INDEX_op_neg_i32: - tcg_out_insn(s, 3502, SUB, ext, a0, TCG_REG_XZR, a1); - break; - - case INDEX_op_and_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_and_i64: - if (c2) { - tcg_out_logicali(s, I3404_ANDI, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3510, AND, ext, a0, a1, a2); - } - break; - - case INDEX_op_andc_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_andc_i64: - if (c2) { - tcg_out_logicali(s, I3404_ANDI, ext, a0, a1, ~a2); - } else { - tcg_out_insn(s, 3510, BIC, ext, a0, a1, a2); - } - break; - - case INDEX_op_or_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_or_i64: - if (c2) { - tcg_out_logicali(s, I3404_ORRI, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3510, ORR, ext, a0, a1, a2); - } - break; - - case INDEX_op_orc_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_orc_i64: - if (c2) { - tcg_out_logicali(s, I3404_ORRI, ext, a0, a1, ~a2); - } else { - tcg_out_insn(s, 3510, ORN, ext, a0, a1, a2); - } - break; - - case INDEX_op_xor_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_xor_i64: - if (c2) { - tcg_out_logicali(s, I3404_EORI, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3510, EOR, ext, a0, a1, a2); - } - break; - - case INDEX_op_eqv_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_eqv_i64: - if (c2) { - tcg_out_logicali(s, I3404_EORI, ext, a0, a1, ~a2); - } else { - tcg_out_insn(s, 3510, EON, ext, a0, a1, a2); - } - break; - - case INDEX_op_not_i64: - case INDEX_op_not_i32: - tcg_out_insn(s, 3510, ORN, ext, a0, TCG_REG_XZR, a1); - break; - - case INDEX_op_mul_i64: - case INDEX_op_mul_i32: - tcg_out_insn(s, 3509, MADD, ext, a0, a1, a2, TCG_REG_XZR); - break; - - case INDEX_op_div_i64: - case INDEX_op_div_i32: - tcg_out_insn(s, 3508, SDIV, ext, a0, a1, a2); - break; - case INDEX_op_divu_i64: - case INDEX_op_divu_i32: - tcg_out_insn(s, 3508, UDIV, ext, a0, a1, a2); - break; - - case INDEX_op_rem_i64: - case INDEX_op_rem_i32: - tcg_out_insn(s, 3508, SDIV, ext, TCG_REG_TMP, a1, a2); - tcg_out_insn(s, 3509, MSUB, ext, a0, TCG_REG_TMP, a2, a1); - break; - case INDEX_op_remu_i64: - case INDEX_op_remu_i32: - tcg_out_insn(s, 3508, UDIV, ext, TCG_REG_TMP, a1, a2); - tcg_out_insn(s, 3509, MSUB, ext, a0, TCG_REG_TMP, a2, a1); - break; - - case INDEX_op_shl_i64: - case INDEX_op_shl_i32: - if (c2) { - tcg_out_shl(s, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3508, LSLV, ext, a0, a1, a2); - } - break; - - case INDEX_op_shr_i64: - case INDEX_op_shr_i32: - if (c2) { - tcg_out_shr(s, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3508, LSRV, ext, a0, a1, a2); - } - break; - - case INDEX_op_sar_i64: - case INDEX_op_sar_i32: - if (c2) { - tcg_out_sar(s, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3508, ASRV, ext, a0, a1, a2); - } - break; - - case INDEX_op_rotr_i64: - case INDEX_op_rotr_i32: - if (c2) { - tcg_out_rotr(s, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3508, RORV, ext, a0, a1, a2); - } - break; - - case INDEX_op_rotl_i64: - case INDEX_op_rotl_i32: - if (c2) { - tcg_out_rotl(s, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3502, SUB, 0, TCG_REG_TMP, TCG_REG_XZR, a2); - tcg_out_insn(s, 3508, RORV, ext, a0, a1, TCG_REG_TMP); - } - break; - - case INDEX_op_brcond_i32: - a1 = (int32_t)a1; - /* FALLTHRU */ - case INDEX_op_brcond_i64: - tcg_out_brcond(s, ext, a2, a0, a1, const_args[1], arg_label(args[3])); - break; - - case INDEX_op_setcond_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_setcond_i64: - tcg_out_cmp(s, ext, a1, a2, c2); - /* Use CSET alias of CSINC Wd, WZR, WZR, invert(cond). */ - tcg_out_insn(s, 3506, CSINC, TCG_TYPE_I32, a0, TCG_REG_XZR, - TCG_REG_XZR, tcg_invert_cond(args[3])); - break; - - case INDEX_op_movcond_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_movcond_i64: - tcg_out_cmp(s, ext, a1, a2, c2); - tcg_out_insn(s, 3506, CSEL, ext, a0, REG0(3), REG0(4), args[5]); - break; - - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, a0, a1, a2, ext); - break; - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, REG0(0), a1, a2); - break; - - case INDEX_op_bswap64_i64: - tcg_out_rev64(s, a0, a1); - break; - case INDEX_op_bswap32_i64: - case INDEX_op_bswap32_i32: - tcg_out_rev32(s, a0, a1); - break; - case INDEX_op_bswap16_i64: - case INDEX_op_bswap16_i32: - tcg_out_rev16(s, a0, a1); - break; - - case INDEX_op_ext8s_i64: - case INDEX_op_ext8s_i32: - tcg_out_sxt(s, ext, MO_8, a0, a1); - break; - case INDEX_op_ext16s_i64: - case INDEX_op_ext16s_i32: - tcg_out_sxt(s, ext, MO_16, a0, a1); - break; - case INDEX_op_ext32s_i64: - tcg_out_sxt(s, TCG_TYPE_I64, MO_32, a0, a1); - break; - case INDEX_op_ext8u_i64: - case INDEX_op_ext8u_i32: - tcg_out_uxt(s, MO_8, a0, a1); - break; - case INDEX_op_ext16u_i64: - case INDEX_op_ext16u_i32: - tcg_out_uxt(s, MO_16, a0, a1); - break; - case INDEX_op_ext32u_i64: - tcg_out_movr(s, TCG_TYPE_I32, a0, a1); - break; - - case INDEX_op_deposit_i64: - case INDEX_op_deposit_i32: - tcg_out_dep(s, ext, a0, REG0(2), args[3], args[4]); - break; - - case INDEX_op_add2_i32: - tcg_out_addsub2(s, TCG_TYPE_I32, a0, a1, REG0(2), REG0(3), - (int32_t)args[4], args[5], const_args[4], - const_args[5], false); - break; - case INDEX_op_add2_i64: - tcg_out_addsub2(s, TCG_TYPE_I64, a0, a1, REG0(2), REG0(3), args[4], - args[5], const_args[4], const_args[5], false); - break; - case INDEX_op_sub2_i32: - tcg_out_addsub2(s, TCG_TYPE_I32, a0, a1, REG0(2), REG0(3), - (int32_t)args[4], args[5], const_args[4], - const_args[5], true); - break; - case INDEX_op_sub2_i64: - tcg_out_addsub2(s, TCG_TYPE_I64, a0, a1, REG0(2), REG0(3), args[4], - args[5], const_args[4], const_args[5], true); - break; - - case INDEX_op_muluh_i64: - tcg_out_insn(s, 3508, UMULH, TCG_TYPE_I64, a0, a1, a2); - break; - case INDEX_op_mulsh_i64: - tcg_out_insn(s, 3508, SMULH, TCG_TYPE_I64, a0, a1, a2); - break; - - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_mov_i64: - case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ - case INDEX_op_movi_i64: - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - default: - tcg_abort(); - } - -#undef REG0 -} - -static const TCGTargetOpDef aarch64_op_defs[] = { - { INDEX_op_exit_tb, { } }, - { INDEX_op_goto_tb, { } }, - { INDEX_op_br, { } }, - - { INDEX_op_ld8u_i32, { "r", "r" } }, - { INDEX_op_ld8s_i32, { "r", "r" } }, - { INDEX_op_ld16u_i32, { "r", "r" } }, - { INDEX_op_ld16s_i32, { "r", "r" } }, - { INDEX_op_ld_i32, { "r", "r" } }, - { INDEX_op_ld8u_i64, { "r", "r" } }, - { INDEX_op_ld8s_i64, { "r", "r" } }, - { INDEX_op_ld16u_i64, { "r", "r" } }, - { INDEX_op_ld16s_i64, { "r", "r" } }, - { INDEX_op_ld32u_i64, { "r", "r" } }, - { INDEX_op_ld32s_i64, { "r", "r" } }, - { INDEX_op_ld_i64, { "r", "r" } }, - - { INDEX_op_st8_i32, { "rZ", "r" } }, - { INDEX_op_st16_i32, { "rZ", "r" } }, - { INDEX_op_st_i32, { "rZ", "r" } }, - { INDEX_op_st8_i64, { "rZ", "r" } }, - { INDEX_op_st16_i64, { "rZ", "r" } }, - { INDEX_op_st32_i64, { "rZ", "r" } }, - { INDEX_op_st_i64, { "rZ", "r" } }, - - { INDEX_op_add_i32, { "r", "r", "rA" } }, - { INDEX_op_add_i64, { "r", "r", "rA" } }, - { INDEX_op_sub_i32, { "r", "r", "rA" } }, - { INDEX_op_sub_i64, { "r", "r", "rA" } }, - { INDEX_op_mul_i32, { "r", "r", "r" } }, - { INDEX_op_mul_i64, { "r", "r", "r" } }, - { INDEX_op_div_i32, { "r", "r", "r" } }, - { INDEX_op_div_i64, { "r", "r", "r" } }, - { INDEX_op_divu_i32, { "r", "r", "r" } }, - { INDEX_op_divu_i64, { "r", "r", "r" } }, - { INDEX_op_rem_i32, { "r", "r", "r" } }, - { INDEX_op_rem_i64, { "r", "r", "r" } }, - { INDEX_op_remu_i32, { "r", "r", "r" } }, - { INDEX_op_remu_i64, { "r", "r", "r" } }, - { INDEX_op_and_i32, { "r", "r", "rL" } }, - { INDEX_op_and_i64, { "r", "r", "rL" } }, - { INDEX_op_or_i32, { "r", "r", "rL" } }, - { INDEX_op_or_i64, { "r", "r", "rL" } }, - { INDEX_op_xor_i32, { "r", "r", "rL" } }, - { INDEX_op_xor_i64, { "r", "r", "rL" } }, - { INDEX_op_andc_i32, { "r", "r", "rL" } }, - { INDEX_op_andc_i64, { "r", "r", "rL" } }, - { INDEX_op_orc_i32, { "r", "r", "rL" } }, - { INDEX_op_orc_i64, { "r", "r", "rL" } }, - { INDEX_op_eqv_i32, { "r", "r", "rL" } }, - { INDEX_op_eqv_i64, { "r", "r", "rL" } }, - - { INDEX_op_neg_i32, { "r", "r" } }, - { INDEX_op_neg_i64, { "r", "r" } }, - { INDEX_op_not_i32, { "r", "r" } }, - { INDEX_op_not_i64, { "r", "r" } }, - - { INDEX_op_shl_i32, { "r", "r", "ri" } }, - { INDEX_op_shr_i32, { "r", "r", "ri" } }, - { INDEX_op_sar_i32, { "r", "r", "ri" } }, - { INDEX_op_rotl_i32, { "r", "r", "ri" } }, - { INDEX_op_rotr_i32, { "r", "r", "ri" } }, - { INDEX_op_shl_i64, { "r", "r", "ri" } }, - { INDEX_op_shr_i64, { "r", "r", "ri" } }, - { INDEX_op_sar_i64, { "r", "r", "ri" } }, - { INDEX_op_rotl_i64, { "r", "r", "ri" } }, - { INDEX_op_rotr_i64, { "r", "r", "ri" } }, - - { INDEX_op_brcond_i32, { "r", "rA" } }, - { INDEX_op_brcond_i64, { "r", "rA" } }, - { INDEX_op_setcond_i32, { "r", "r", "rA" } }, - { INDEX_op_setcond_i64, { "r", "r", "rA" } }, - { INDEX_op_movcond_i32, { "r", "r", "rA", "rZ", "rZ" } }, - { INDEX_op_movcond_i64, { "r", "r", "rA", "rZ", "rZ" } }, - - { INDEX_op_qemu_ld_i32, { "r", "l" } }, - { INDEX_op_qemu_ld_i64, { "r", "l" } }, - { INDEX_op_qemu_st_i32, { "lZ", "l" } }, - { INDEX_op_qemu_st_i64, { "lZ", "l" } }, - - { INDEX_op_bswap16_i32, { "r", "r" } }, - { INDEX_op_bswap32_i32, { "r", "r" } }, - { INDEX_op_bswap16_i64, { "r", "r" } }, - { INDEX_op_bswap32_i64, { "r", "r" } }, - { INDEX_op_bswap64_i64, { "r", "r" } }, - - { INDEX_op_ext8s_i32, { "r", "r" } }, - { INDEX_op_ext16s_i32, { "r", "r" } }, - { INDEX_op_ext8u_i32, { "r", "r" } }, - { INDEX_op_ext16u_i32, { "r", "r" } }, - - { INDEX_op_ext8s_i64, { "r", "r" } }, - { INDEX_op_ext16s_i64, { "r", "r" } }, - { INDEX_op_ext32s_i64, { "r", "r" } }, - { INDEX_op_ext8u_i64, { "r", "r" } }, - { INDEX_op_ext16u_i64, { "r", "r" } }, - { INDEX_op_ext32u_i64, { "r", "r" } }, - - { INDEX_op_deposit_i32, { "r", "0", "rZ" } }, - { INDEX_op_deposit_i64, { "r", "0", "rZ" } }, - - { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rA", "rMZ" } }, - { INDEX_op_add2_i64, { "r", "r", "rZ", "rZ", "rA", "rMZ" } }, - { INDEX_op_sub2_i32, { "r", "r", "rZ", "rZ", "rA", "rMZ" } }, - { INDEX_op_sub2_i64, { "r", "r", "rZ", "rZ", "rA", "rMZ" } }, - - { INDEX_op_muluh_i64, { "r", "r", "r" } }, - { INDEX_op_mulsh_i64, { "r", "r", "r" } }, - - { -1 }, -}; - -static void tcg_target_init(TCGContext *s) -{ - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffffffff); - - tcg_regset_set32(tcg_target_call_clobber_regs, 0, - (1 << TCG_REG_X0) | (1 << TCG_REG_X1) | - (1 << TCG_REG_X2) | (1 << TCG_REG_X3) | - (1 << TCG_REG_X4) | (1 << TCG_REG_X5) | - (1 << TCG_REG_X6) | (1 << TCG_REG_X7) | - (1 << TCG_REG_X8) | (1 << TCG_REG_X9) | - (1 << TCG_REG_X10) | (1 << TCG_REG_X11) | - (1 << TCG_REG_X12) | (1 << TCG_REG_X13) | - (1 << TCG_REG_X14) | (1 << TCG_REG_X15) | - (1 << TCG_REG_X16) | (1 << TCG_REG_X17) | - (1 << TCG_REG_X18) | (1 << TCG_REG_X30)); - - tcg_regset_clear(s->reserved_regs); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_FP); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_X18); /* platform register */ - - tcg_add_target_add_op_defs(aarch64_op_defs); -} - -/* Saving pairs: (X19, X20) .. (X27, X28), (X29(fp), X30(lr)). */ -#define PUSH_SIZE ((30 - 19 + 1) * 8) - -#define FRAME_SIZE \ - ((PUSH_SIZE \ - + TCG_STATIC_CALL_ARGS_SIZE \ - + CPU_TEMP_BUF_NLONGS * sizeof(long) \ - + TCG_TARGET_STACK_ALIGN - 1) \ - & ~(TCG_TARGET_STACK_ALIGN - 1)) - -/* We're expecting a 2 byte uleb128 encoded value. */ -QEMU_BUILD_BUG_ON(FRAME_SIZE >= (1 << 14)); - -/* We're expecting to use a single ADDI insn. */ -QEMU_BUILD_BUG_ON(FRAME_SIZE - PUSH_SIZE > 0xfff); - -static void tcg_target_qemu_prologue(TCGContext *s) -{ - TCGReg r; - - /* Push (FP, LR) and allocate space for all saved registers. */ - tcg_out_insn(s, 3314, STP, TCG_REG_FP, TCG_REG_LR, - TCG_REG_SP, -PUSH_SIZE, 1, 1); - - /* Set up frame pointer for canonical unwinding. */ - tcg_out_movr_sp(s, TCG_TYPE_I64, TCG_REG_FP, TCG_REG_SP); - - /* Store callee-preserved regs x19..x28. */ - for (r = TCG_REG_X19; r <= TCG_REG_X27; r += 2) { - int ofs = (r - TCG_REG_X19 + 2) * 8; - tcg_out_insn(s, 3314, STP, r, r + 1, TCG_REG_SP, ofs, 1, 0); - } - - /* Make stack space for TCG locals. */ - tcg_out_insn(s, 3401, SUBI, TCG_TYPE_I64, TCG_REG_SP, TCG_REG_SP, - FRAME_SIZE - PUSH_SIZE); - - /* Inform TCG about how to find TCG locals with register, offset, size. */ - tcg_set_frame(s, TCG_REG_SP, TCG_STATIC_CALL_ARGS_SIZE, - CPU_TEMP_BUF_NLONGS * sizeof(long)); - -#if defined(CONFIG_USE_GUEST_BASE) - if (GUEST_BASE) { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_GUEST_BASE, GUEST_BASE); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_GUEST_BASE); - } -#endif - - tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); - tcg_out_insn(s, 3207, BR, tcg_target_call_iarg_regs[1]); - - tb_ret_addr = s->code_ptr; - - /* Remove TCG locals stack space. */ - tcg_out_insn(s, 3401, ADDI, TCG_TYPE_I64, TCG_REG_SP, TCG_REG_SP, - FRAME_SIZE - PUSH_SIZE); - - /* Restore registers x19..x28. */ - for (r = TCG_REG_X19; r <= TCG_REG_X27; r += 2) { - int ofs = (r - TCG_REG_X19 + 2) * 8; - tcg_out_insn(s, 3314, LDP, r, r + 1, TCG_REG_SP, ofs, 1, 0); - } - - /* Pop (FP, LR), restore SP to previous frame. */ - tcg_out_insn(s, 3314, LDP, TCG_REG_FP, TCG_REG_LR, - TCG_REG_SP, PUSH_SIZE, 0, 1); - tcg_out_insn(s, 3207, RET, TCG_REG_LR); -} - -typedef struct { - DebugFrameHeader h; - uint8_t fde_def_cfa[4]; - uint8_t fde_reg_ofs[24]; -} DebugFrame; - -#define ELF_HOST_MACHINE EM_AARCH64 - -static const DebugFrame debug_frame = { - .h.cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ - .h.cie.id = -1, - .h.cie.version = 1, - .h.cie.code_align = 1, - .h.cie.data_align = 0x78, /* sleb128 -8 */ - .h.cie.return_column = TCG_REG_LR, - - /* Total FDE size does not include the "len" member. */ - .h.fde.len = sizeof(DebugFrame) - offsetof(DebugFrame, h.fde.cie_offset), - - .fde_def_cfa = { - 12, TCG_REG_SP, /* DW_CFA_def_cfa sp, ... */ - (FRAME_SIZE & 0x7f) | 0x80, /* ... uleb128 FRAME_SIZE */ - (FRAME_SIZE >> 7) - }, - .fde_reg_ofs = { - 0x80 + 28, 1, /* DW_CFA_offset, x28, -8 */ - 0x80 + 27, 2, /* DW_CFA_offset, x27, -16 */ - 0x80 + 26, 3, /* DW_CFA_offset, x26, -24 */ - 0x80 + 25, 4, /* DW_CFA_offset, x25, -32 */ - 0x80 + 24, 5, /* DW_CFA_offset, x24, -40 */ - 0x80 + 23, 6, /* DW_CFA_offset, x23, -48 */ - 0x80 + 22, 7, /* DW_CFA_offset, x22, -56 */ - 0x80 + 21, 8, /* DW_CFA_offset, x21, -64 */ - 0x80 + 20, 9, /* DW_CFA_offset, x20, -72 */ - 0x80 + 19, 10, /* DW_CFA_offset, x1p, -80 */ - 0x80 + 30, 11, /* DW_CFA_offset, lr, -88 */ - 0x80 + 29, 12, /* DW_CFA_offset, fp, -96 */ - } -}; - -void tcg_register_jit(void *buf, size_t buf_size) -{ - tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); -} diff --git a/qemu/tcg/aarch64/tcg-target.h b/qemu/tcg/aarch64/tcg-target.h index 8aec04d2b..19a04a6e7 100644 --- a/qemu/tcg/aarch64/tcg-target.h +++ b/qemu/tcg/aarch64/tcg-target.h @@ -70,7 +70,8 @@ typedef enum { #define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_muluh_i32 0 #define TCG_TARGET_HAS_mulsh_i32 0 -#define TCG_TARGET_HAS_trunc_shr_i32 0 +#define TCG_TARGET_HAS_extrl_i64_i32 0 +#define TCG_TARGET_HAS_extrh_i64_i32 0 #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 diff --git a/qemu/tcg/aarch64/tcg-target.inc.c b/qemu/tcg/aarch64/tcg-target.inc.c new file mode 100644 index 000000000..a8fb4420d --- /dev/null +++ b/qemu/tcg/aarch64/tcg-target.inc.c @@ -0,0 +1,1892 @@ +/* + * Initial TCG Implementation for aarch64 + * + * Copyright (c) 2013 Huawei Technologies Duesseldorf GmbH + * Written by Claudio Fontana + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. + * + * See the COPYING file in the top-level directory for details. + */ + +#include "tcg-be-ldst.h" +#include "qemu/bitops.h" + +/* We're going to re-use TCGType in setting of the SF bit, which controls + the size of the operation performed. If we know the values match, it + makes things much cleaner. */ +QEMU_BUILD_BUG_ON(TCG_TYPE_I32 != 0 || TCG_TYPE_I64 != 1); + +#ifdef CONFIG_DEBUG_TCG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "%x0", "%x1", "%x2", "%x3", "%x4", "%x5", "%x6", "%x7", + "%x8", "%x9", "%x10", "%x11", "%x12", "%x13", "%x14", "%x15", + "%x16", "%x17", "%x18", "%x19", "%x20", "%x21", "%x22", "%x23", + "%x24", "%x25", "%x26", "%x27", "%x28", "%fp", "%x30", "%sp", +}; +#endif /* CONFIG_DEBUG_TCG */ + +static const int tcg_target_reg_alloc_order[] = { + TCG_REG_X20, TCG_REG_X21, TCG_REG_X22, TCG_REG_X23, + TCG_REG_X24, TCG_REG_X25, TCG_REG_X26, TCG_REG_X27, + TCG_REG_X28, /* we will reserve this for guest_base if configured */ + + TCG_REG_X8, TCG_REG_X9, TCG_REG_X10, TCG_REG_X11, + TCG_REG_X12, TCG_REG_X13, TCG_REG_X14, TCG_REG_X15, + TCG_REG_X16, TCG_REG_X17, + + TCG_REG_X0, TCG_REG_X1, TCG_REG_X2, TCG_REG_X3, + TCG_REG_X4, TCG_REG_X5, TCG_REG_X6, TCG_REG_X7, + + /* X18 reserved by system */ + /* X19 reserved for AREG0 */ + /* X29 reserved as fp */ + /* X30 reserved as temporary */ +}; + +static const int tcg_target_call_iarg_regs[8] = { + TCG_REG_X0, TCG_REG_X1, TCG_REG_X2, TCG_REG_X3, + TCG_REG_X4, TCG_REG_X5, TCG_REG_X6, TCG_REG_X7 +}; +static const int tcg_target_call_oarg_regs[1] = { + TCG_REG_X0 +}; + +#define TCG_REG_TMP TCG_REG_X30 + +#ifndef CONFIG_SOFTMMU +/* Note that XZR cannot be encoded in the address base register slot, + as that actaully encodes SP. So if we need to zero-extend the guest + address, via the address index register slot, we need to load even + a zero guest base into a register. */ +#define USE_GUEST_BASE (guest_base != 0 || TARGET_LONG_BITS == 32) +#define TCG_REG_GUEST_BASE TCG_REG_X28 +#endif + +static inline void reloc_pc26(tcg_insn_unit *code_ptr, tcg_insn_unit *target) +{ + ptrdiff_t offset = target - code_ptr; + tcg_debug_assert(offset == sextract64(offset, 0, 26)); + /* read instruction, mask away previous PC_REL26 parameter contents, + set the proper offset, then write back the instruction. */ + *code_ptr = deposit32(*code_ptr, 0, 26, offset); +} + +static inline void reloc_pc19(tcg_insn_unit *code_ptr, tcg_insn_unit *target) +{ + ptrdiff_t offset = target - code_ptr; + tcg_debug_assert(offset == sextract64(offset, 0, 19)); + *code_ptr = deposit32(*code_ptr, 5, 19, offset); +} + +static inline void patch_reloc(tcg_insn_unit *code_ptr, int type, + intptr_t value, intptr_t addend) +{ + tcg_debug_assert(addend == 0); + switch (type) { + case R_AARCH64_JUMP26: + case R_AARCH64_CALL26: + reloc_pc26(code_ptr, (tcg_insn_unit *)value); + break; + case R_AARCH64_CONDBR19: + reloc_pc19(code_ptr, (tcg_insn_unit *)value); + break; + default: + tcg_abort(); + } +} + +#define TCG_CT_CONST_AIMM 0x100 +#define TCG_CT_CONST_LIMM 0x200 +#define TCG_CT_CONST_ZERO 0x400 +#define TCG_CT_CONST_MONE 0x800 + +/* parse target specific constraints */ +static int target_parse_constraint(TCGArgConstraint *ct, + const char **pct_str) +{ + const char *ct_str = *pct_str; + + switch (ct_str[0]) { + case 'r': + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, (1ULL << TCG_TARGET_NB_REGS) - 1); + break; + case 'l': /* qemu_ld / qemu_st address, data_reg */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, (1ULL << TCG_TARGET_NB_REGS) - 1); +#ifdef CONFIG_SOFTMMU + /* x0 and x1 will be overwritten when reading the tlb entry, + and x2, and x3 for helper args, better to avoid using them. */ + tcg_regset_reset_reg(ct->u.regs, TCG_REG_X0); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_X1); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_X2); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_X3); +#endif + break; + case 'A': /* Valid for arithmetic immediate (positive or negative). */ + ct->ct |= TCG_CT_CONST_AIMM; + break; + case 'L': /* Valid for logical immediate. */ + ct->ct |= TCG_CT_CONST_LIMM; + break; + case 'M': /* minus one */ + ct->ct |= TCG_CT_CONST_MONE; + break; + case 'Z': /* zero */ + ct->ct |= TCG_CT_CONST_ZERO; + break; + default: + return -1; + } + + ct_str++; + *pct_str = ct_str; + return 0; +} + +static inline bool is_aimm(uint64_t val) +{ + return (val & ~0xfff) == 0 || (val & ~0xfff000) == 0; +} + +static inline bool is_limm(uint64_t val) +{ + /* Taking a simplified view of the logical immediates for now, ignoring + the replication that can happen across the field. Match bit patterns + of the forms + 0....01....1 + 0..01..10..0 + and their inverses. */ + + /* Make things easier below, by testing the form with msb clear. */ + if ((int64_t)val < 0) { + val = ~val; + } + if (val == 0) { + return false; + } + val += val & -val; + return (val & (val - 1)) == 0; +} + +static int tcg_target_const_match(tcg_target_long val, TCGType type, + const TCGArgConstraint *arg_ct) +{ + int ct = arg_ct->ct; + + if (ct & TCG_CT_CONST) { + return 1; + } + if (type == TCG_TYPE_I32) { + val = (int32_t)val; + } + if ((ct & TCG_CT_CONST_AIMM) && (is_aimm(val) || is_aimm(-val))) { + return 1; + } + if ((ct & TCG_CT_CONST_LIMM) && is_limm(val)) { + return 1; + } + if ((ct & TCG_CT_CONST_ZERO) && val == 0) { + return 1; + } + if ((ct & TCG_CT_CONST_MONE) && val == -1) { + return 1; + } + + return 0; +} + +enum aarch64_cond_code { + COND_EQ = 0x0, + COND_NE = 0x1, + COND_CS = 0x2, /* Unsigned greater or equal */ + COND_HS = COND_CS, /* ALIAS greater or equal */ + COND_CC = 0x3, /* Unsigned less than */ + COND_LO = COND_CC, /* ALIAS Lower */ + COND_MI = 0x4, /* Negative */ + COND_PL = 0x5, /* Zero or greater */ + COND_VS = 0x6, /* Overflow */ + COND_VC = 0x7, /* No overflow */ + COND_HI = 0x8, /* Unsigned greater than */ + COND_LS = 0x9, /* Unsigned less or equal */ + COND_GE = 0xa, + COND_LT = 0xb, + COND_GT = 0xc, + COND_LE = 0xd, + COND_AL = 0xe, + COND_NV = 0xf, /* behaves like COND_AL here */ +}; + +static const enum aarch64_cond_code tcg_cond_to_aarch64[] = { + [TCG_COND_EQ] = COND_EQ, + [TCG_COND_NE] = COND_NE, + [TCG_COND_LT] = COND_LT, + [TCG_COND_GE] = COND_GE, + [TCG_COND_LE] = COND_LE, + [TCG_COND_GT] = COND_GT, + /* unsigned */ + [TCG_COND_LTU] = COND_LO, + [TCG_COND_GTU] = COND_HI, + [TCG_COND_GEU] = COND_HS, + [TCG_COND_LEU] = COND_LS, +}; + +typedef enum { + LDST_ST = 0, /* store */ + LDST_LD = 1, /* load */ + LDST_LD_S_X = 2, /* load and sign-extend into Xt */ + LDST_LD_S_W = 3, /* load and sign-extend into Wt */ +} AArch64LdstType; + +/* We encode the format of the insn into the beginning of the name, so that + we can have the preprocessor help "typecheck" the insn vs the output + function. Arm didn't provide us with nice names for the formats, so we + use the section number of the architecture reference manual in which the + instruction group is described. */ +typedef enum { + /* Compare and branch (immediate). */ + I3201_CBZ = 0x34000000, + I3201_CBNZ = 0x35000000, + + /* Conditional branch (immediate). */ + I3202_B_C = 0x54000000, + + /* Unconditional branch (immediate). */ + I3206_B = 0x14000000, + I3206_BL = 0x94000000, + + /* Unconditional branch (register). */ + I3207_BR = 0xd61f0000, + I3207_BLR = 0xd63f0000, + I3207_RET = 0xd65f0000, + + /* Load/store register. Described here as 3.3.12, but the helper + that emits them can transform to 3.3.10 or 3.3.13. */ + I3312_STRB = 0x38000000 | LDST_ST << 22 | MO_8 << 30, + I3312_STRH = 0x38000000 | LDST_ST << 22 | MO_16 << 30, + I3312_STRW = 0x38000000 | LDST_ST << 22 | MO_32 << 30, + I3312_STRX = 0x38000000 | LDST_ST << 22 | MO_64 << 30, + + I3312_LDRB = 0x38000000 | LDST_LD << 22 | MO_8 << 30, + I3312_LDRH = 0x38000000 | LDST_LD << 22 | MO_16 << 30, + I3312_LDRW = 0x38000000 | LDST_LD << 22 | MO_32 << 30, + I3312_LDRX = 0x38000000 | LDST_LD << 22 | MO_64 << 30, + + I3312_LDRSBW = 0x38000000 | LDST_LD_S_W << 22 | MO_8 << 30, + I3312_LDRSHW = 0x38000000 | LDST_LD_S_W << 22 | MO_16 << 30, + + I3312_LDRSBX = 0x38000000 | LDST_LD_S_X << 22 | MO_8 << 30, + I3312_LDRSHX = 0x38000000 | LDST_LD_S_X << 22 | MO_16 << 30, + I3312_LDRSWX = 0x38000000 | LDST_LD_S_X << 22 | MO_32 << 30, + + I3312_TO_I3310 = 0x00200800, + I3312_TO_I3313 = 0x01000000, + + /* Load/store register pair instructions. */ + I3314_LDP = 0x28400000, + I3314_STP = 0x28000000, + + /* Add/subtract immediate instructions. */ + I3401_ADDI = 0x11000000, + I3401_ADDSI = 0x31000000, + I3401_SUBI = 0x51000000, + I3401_SUBSI = 0x71000000, + + /* Bitfield instructions. */ + I3402_BFM = 0x33000000, + I3402_SBFM = 0x13000000, + I3402_UBFM = 0x53000000, + + /* Extract instruction. */ + I3403_EXTR = 0x13800000, + + /* Logical immediate instructions. */ + I3404_ANDI = 0x12000000, + I3404_ORRI = 0x32000000, + I3404_EORI = 0x52000000, + + /* Move wide immediate instructions. */ + I3405_MOVN = 0x12800000, + I3405_MOVZ = 0x52800000, + I3405_MOVK = 0x72800000, + + /* PC relative addressing instructions. */ + I3406_ADR = 0x10000000, + I3406_ADRP = 0x90000000, + + /* Add/subtract shifted register instructions (without a shift). */ + I3502_ADD = 0x0b000000, + I3502_ADDS = 0x2b000000, + I3502_SUB = 0x4b000000, + I3502_SUBS = 0x6b000000, + + /* Add/subtract shifted register instructions (with a shift). */ + I3502S_ADD_LSL = I3502_ADD, + + /* Add/subtract with carry instructions. */ + I3503_ADC = 0x1a000000, + I3503_SBC = 0x5a000000, + + /* Conditional select instructions. */ + I3506_CSEL = 0x1a800000, + I3506_CSINC = 0x1a800400, + + /* Data-processing (1 source) instructions. */ + I3507_REV16 = 0x5ac00400, + I3507_REV32 = 0x5ac00800, + I3507_REV64 = 0x5ac00c00, + + /* Data-processing (2 source) instructions. */ + I3508_LSLV = 0x1ac02000, + I3508_LSRV = 0x1ac02400, + I3508_ASRV = 0x1ac02800, + I3508_RORV = 0x1ac02c00, + I3508_SMULH = 0x9b407c00, + I3508_UMULH = 0x9bc07c00, + I3508_UDIV = 0x1ac00800, + I3508_SDIV = 0x1ac00c00, + + /* Data-processing (3 source) instructions. */ + I3509_MADD = 0x1b000000, + I3509_MSUB = 0x1b008000, + + /* Logical shifted register instructions (without a shift). */ + I3510_AND = 0x0a000000, + I3510_BIC = 0x0a200000, + I3510_ORR = 0x2a000000, + I3510_ORN = 0x2a200000, + I3510_EOR = 0x4a000000, + I3510_EON = 0x4a200000, + I3510_ANDS = 0x6a000000, +} AArch64Insn; + +static inline uint32_t tcg_in32(TCGContext *s) +{ + uint32_t v = *(uint32_t *)s->code_ptr; + return v; +} + +/* Emit an opcode with "type-checking" of the format. */ +#define tcg_out_insn(S, FMT, OP, ...) \ + glue(tcg_out_insn_,FMT)(S, glue(glue(glue(I,FMT),_),OP), ## __VA_ARGS__) + +static void tcg_out_insn_3201(TCGContext *s, AArch64Insn insn, TCGType ext, + TCGReg rt, int imm19) +{ + tcg_out32(s, insn | ext << 31 | (imm19 & 0x7ffff) << 5 | rt); +} + +static void tcg_out_insn_3202(TCGContext *s, AArch64Insn insn, + TCGCond c, int imm19) +{ + tcg_out32(s, insn | tcg_cond_to_aarch64[c] | (imm19 & 0x7ffff) << 5); +} + +static void tcg_out_insn_3206(TCGContext *s, AArch64Insn insn, int imm26) +{ + tcg_out32(s, insn | (imm26 & 0x03ffffff)); +} + +static void tcg_out_insn_3207(TCGContext *s, AArch64Insn insn, TCGReg rn) +{ + tcg_out32(s, insn | rn << 5); +} + +static void tcg_out_insn_3314(TCGContext *s, AArch64Insn insn, + TCGReg r1, TCGReg r2, TCGReg rn, + tcg_target_long ofs, bool pre, bool w) +{ + insn |= 1u << 31; /* ext */ + insn |= pre << 24; + insn |= w << 23; + + tcg_debug_assert(ofs >= -0x200 && ofs < 0x200 && (ofs & 7) == 0); + insn |= (ofs & (0x7f << 3)) << (15 - 3); + + tcg_out32(s, insn | r2 << 10 | rn << 5 | r1); +} + +static void tcg_out_insn_3401(TCGContext *s, AArch64Insn insn, TCGType ext, + TCGReg rd, TCGReg rn, uint64_t aimm) +{ + if (aimm > 0xfff) { + tcg_debug_assert((aimm & 0xfff) == 0); + aimm >>= 12; + tcg_debug_assert(aimm <= 0xfff); + aimm |= 1 << 12; /* apply LSL 12 */ + } + tcg_out32(s, insn | ext << 31 | aimm << 10 | rn << 5 | rd); +} + +/* This function can be used for both 3.4.2 (Bitfield) and 3.4.4 + (Logical immediate). Both insn groups have N, IMMR and IMMS fields + that feed the DecodeBitMasks pseudo function. */ +static void tcg_out_insn_3402(TCGContext *s, AArch64Insn insn, TCGType ext, + TCGReg rd, TCGReg rn, int n, int immr, int imms) +{ + tcg_out32(s, insn | ext << 31 | n << 22 | immr << 16 | imms << 10 + | rn << 5 | rd); +} + +#define tcg_out_insn_3404 tcg_out_insn_3402 + +static void tcg_out_insn_3403(TCGContext *s, AArch64Insn insn, TCGType ext, + TCGReg rd, TCGReg rn, TCGReg rm, int imms) +{ + tcg_out32(s, insn | ext << 31 | ext << 22 | rm << 16 | imms << 10 + | rn << 5 | rd); +} + +/* This function is used for the Move (wide immediate) instruction group. + Note that SHIFT is a full shift count, not the 2 bit HW field. */ +static void tcg_out_insn_3405(TCGContext *s, AArch64Insn insn, TCGType ext, + TCGReg rd, uint16_t half, unsigned shift) +{ + tcg_debug_assert((shift & ~0x30) == 0); + tcg_out32(s, insn | ext << 31 | shift << (21 - 4) | half << 5 | rd); +} + +static void tcg_out_insn_3406(TCGContext *s, AArch64Insn insn, + TCGReg rd, int64_t disp) +{ + tcg_out32(s, insn | (disp & 3) << 29 | (disp & 0x1ffffc) << (5 - 2) | rd); +} + +/* This function is for both 3.5.2 (Add/Subtract shifted register), for + the rare occasion when we actually want to supply a shift amount. */ +static inline void tcg_out_insn_3502S(TCGContext *s, AArch64Insn insn, + TCGType ext, TCGReg rd, TCGReg rn, + TCGReg rm, int imm6) +{ + tcg_out32(s, insn | ext << 31 | rm << 16 | imm6 << 10 | rn << 5 | rd); +} + +/* This function is for 3.5.2 (Add/subtract shifted register), + and 3.5.10 (Logical shifted register), for the vast majorty of cases + when we don't want to apply a shift. Thus it can also be used for + 3.5.3 (Add/subtract with carry) and 3.5.8 (Data processing 2 source). */ +static void tcg_out_insn_3502(TCGContext *s, AArch64Insn insn, TCGType ext, + TCGReg rd, TCGReg rn, TCGReg rm) +{ + tcg_out32(s, insn | ext << 31 | rm << 16 | rn << 5 | rd); +} + +#define tcg_out_insn_3503 tcg_out_insn_3502 +#define tcg_out_insn_3508 tcg_out_insn_3502 +#define tcg_out_insn_3510 tcg_out_insn_3502 + +static void tcg_out_insn_3506(TCGContext *s, AArch64Insn insn, TCGType ext, + TCGReg rd, TCGReg rn, TCGReg rm, TCGCond c) +{ + tcg_out32(s, insn | ext << 31 | rm << 16 | rn << 5 | rd + | tcg_cond_to_aarch64[c] << 12); +} + +static void tcg_out_insn_3507(TCGContext *s, AArch64Insn insn, TCGType ext, + TCGReg rd, TCGReg rn) +{ + tcg_out32(s, insn | ext << 31 | rn << 5 | rd); +} + +static void tcg_out_insn_3509(TCGContext *s, AArch64Insn insn, TCGType ext, + TCGReg rd, TCGReg rn, TCGReg rm, TCGReg ra) +{ + tcg_out32(s, insn | ext << 31 | rm << 16 | ra << 10 | rn << 5 | rd); +} + +static void tcg_out_insn_3310(TCGContext *s, AArch64Insn insn, + TCGReg rd, TCGReg base, TCGType ext, + TCGReg regoff) +{ + /* Note the AArch64Insn constants above are for C3.3.12. Adjust. */ + tcg_out32(s, insn | I3312_TO_I3310 | regoff << 16 | + 0x4000 | ext << 13 | base << 5 | rd); +} + +static void tcg_out_insn_3312(TCGContext *s, AArch64Insn insn, + TCGReg rd, TCGReg rn, intptr_t offset) +{ + tcg_out32(s, insn | (offset & 0x1ff) << 12 | rn << 5 | rd); +} + +static void tcg_out_insn_3313(TCGContext *s, AArch64Insn insn, + TCGReg rd, TCGReg rn, uintptr_t scaled_uimm) +{ + /* Note the AArch64Insn constants above are for C3.3.12. Adjust. */ + tcg_out32(s, insn | I3312_TO_I3313 | scaled_uimm << 10 | rn << 5 | rd); +} + +/* Register to register move using ORR (shifted register with no shift). */ +static void tcg_out_movr(TCGContext *s, TCGType ext, TCGReg rd, TCGReg rm) +{ + tcg_out_insn(s, 3510, ORR, ext, rd, TCG_REG_XZR, rm); +} + +/* Register to register move using ADDI (move to/from SP). */ +static void tcg_out_movr_sp(TCGContext *s, TCGType ext, TCGReg rd, TCGReg rn) +{ + tcg_out_insn(s, 3401, ADDI, ext, rd, rn, 0); +} + +/* This function is used for the Logical (immediate) instruction group. + The value of LIMM must satisfy IS_LIMM. See the comment above about + only supporting simplified logical immediates. */ +static void tcg_out_logicali(TCGContext *s, AArch64Insn insn, TCGType ext, + TCGReg rd, TCGReg rn, uint64_t limm) +{ + unsigned h, l, r, c; + + tcg_debug_assert(is_limm(limm)); + + h = clz64(limm); + l = ctz64(limm); + if (l == 0) { + r = 0; /* form 0....01....1 */ + c = ctz64(~limm) - 1; + if (h == 0) { + r = clz64(~limm); /* form 1..10..01..1 */ + c += r; + } + } else { + r = 64 - l; /* form 1....10....0 or 0..01..10..0 */ + c = r - h - 1; + } + if (ext == TCG_TYPE_I32) { + r &= 31; + c &= 31; + } + + tcg_out_insn_3404(s, insn, ext, rd, rn, ext, r, c); +} + +static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg rd, + tcg_target_long value) +{ + AArch64Insn insn; + int i, wantinv, shift; + tcg_target_long svalue = value; + tcg_target_long ivalue = ~value; + tcg_target_long imask; + + /* For 32-bit values, discard potential garbage in value. For 64-bit + values within [2**31, 2**32-1], we can create smaller sequences by + interpreting this as a negative 32-bit number, while ensuring that + the high 32 bits are cleared by setting SF=0. */ + if (type == TCG_TYPE_I32 || (value & ~0xffffffffull) == 0) { + svalue = (int32_t)value; + value = (uint32_t)value; + ivalue = (uint32_t)ivalue; + type = TCG_TYPE_I32; + } + + /* Speed things up by handling the common case of small positive + and negative values specially. */ + if ((value & ~0xffffull) == 0) { + tcg_out_insn(s, 3405, MOVZ, type, rd, value, 0); + return; + } else if ((ivalue & ~0xffffull) == 0) { + tcg_out_insn(s, 3405, MOVN, type, rd, ivalue, 0); + return; + } + + /* Check for bitfield immediates. For the benefit of 32-bit quantities, + use the sign-extended value. That lets us match rotated values such + as 0xff0000ff with the same 64-bit logic matching 0xffffffffff0000ff. */ + if (is_limm(svalue)) { + tcg_out_logicali(s, I3404_ORRI, type, rd, TCG_REG_XZR, svalue); + return; + } + + /* Look for host pointer values within 4G of the PC. This happens + often when loading pointers to QEMU's own data structures. */ + if (type == TCG_TYPE_I64) { + tcg_target_long disp = (value >> 12) - ((intptr_t)s->code_ptr >> 12); + if (disp == sextract64(disp, 0, 21)) { + tcg_out_insn(s, 3406, ADRP, rd, disp); + if (value & 0xfff) { + tcg_out_insn(s, 3401, ADDI, type, rd, rd, value & 0xfff); + } + return; + } + } + + /* Would it take fewer insns to begin with MOVN? For the value and its + inverse, count the number of 16-bit lanes that are 0. */ + for (i = wantinv = imask = 0; i < 64; i += 16) { + tcg_target_long mask = 0xffffull << i; + if ((value & mask) == 0) { + wantinv -= 1; + } + if ((ivalue & mask) == 0) { + wantinv += 1; + imask |= mask; + } + } + + /* If we had more 0xffff than 0x0000, invert VALUE and use MOVN. */ + insn = I3405_MOVZ; + if (wantinv > 0) { + value = ivalue; + insn = I3405_MOVN; + } + + /* Find the lowest lane that is not 0x0000. */ + shift = ctz64(value) & (63 & -16); + tcg_out_insn_3405(s, insn, type, rd, value >> shift, shift); + + if (wantinv > 0) { + /* Re-invert the value, so MOVK sees non-inverted bits. */ + value = ~value; + /* Clear out all the 0xffff lanes. */ + value ^= imask; + } + /* Clear out the lane that we just set. */ + value &= ~(0xffffUL << shift); + + /* Iterate until all lanes have been set, and thus cleared from VALUE. */ + while (value) { + shift = ctz64(value) & (63 & -16); + tcg_out_insn(s, 3405, MOVK, type, rd, value >> shift, shift); + value &= ~(0xffffUL << shift); + } +} + +/* Define something more legible for general use. */ +#define tcg_out_ldst_r tcg_out_insn_3310 + +static void tcg_out_ldst(TCGContext *s, AArch64Insn insn, + TCGReg rd, TCGReg rn, intptr_t offset) +{ + TCGMemOp size = (uint32_t)insn >> 30; + + /* If the offset is naturally aligned and in range, then we can + use the scaled uimm12 encoding */ + if (offset >= 0 && !(offset & ((1 << size) - 1))) { + uintptr_t scaled_uimm = offset >> size; + if (scaled_uimm <= 0xfff) { + tcg_out_insn_3313(s, insn, rd, rn, scaled_uimm); + return; + } + } + + /* Small signed offsets can use the unscaled encoding. */ + if (offset >= -256 && offset < 256) { + tcg_out_insn_3312(s, insn, rd, rn, offset); + return; + } + + /* Worst-case scenario, move offset to temp register, use reg offset. */ + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_TMP, offset); + tcg_out_ldst_r(s, insn, rd, rn, TCG_TYPE_I64, TCG_REG_TMP); +} + +static inline void tcg_out_mov(TCGContext *s, + TCGType type, TCGReg ret, TCGReg arg) +{ + if (ret != arg) { + tcg_out_movr(s, type, ret, arg); + } +} + +static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg arg, + TCGReg arg1, intptr_t arg2) +{ + tcg_out_ldst(s, type == TCG_TYPE_I32 ? I3312_LDRW : I3312_LDRX, + arg, arg1, arg2); +} + +static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, + TCGReg arg1, intptr_t arg2) +{ + tcg_out_ldst(s, type == TCG_TYPE_I32 ? I3312_STRW : I3312_STRX, + arg, arg1, arg2); +} + +static inline void tcg_out_bfm(TCGContext *s, TCGType ext, TCGReg rd, + TCGReg rn, unsigned int a, unsigned int b) +{ + tcg_out_insn(s, 3402, BFM, ext, rd, rn, ext, a, b); +} + +static inline void tcg_out_ubfm(TCGContext *s, TCGType ext, TCGReg rd, + TCGReg rn, unsigned int a, unsigned int b) +{ + tcg_out_insn(s, 3402, UBFM, ext, rd, rn, ext, a, b); +} + +static inline void tcg_out_sbfm(TCGContext *s, TCGType ext, TCGReg rd, + TCGReg rn, unsigned int a, unsigned int b) +{ + tcg_out_insn(s, 3402, SBFM, ext, rd, rn, ext, a, b); +} + +static inline void tcg_out_extr(TCGContext *s, TCGType ext, TCGReg rd, + TCGReg rn, TCGReg rm, unsigned int a) +{ + tcg_out_insn(s, 3403, EXTR, ext, rd, rn, rm, a); +} + +static inline void tcg_out_shl(TCGContext *s, TCGType ext, + TCGReg rd, TCGReg rn, unsigned int m) +{ + int bits = ext ? 64 : 32; + int max = bits - 1; + tcg_out_ubfm(s, ext, rd, rn, bits - (m & max), max - (m & max)); +} + +static inline void tcg_out_shr(TCGContext *s, TCGType ext, + TCGReg rd, TCGReg rn, unsigned int m) +{ + int max = ext ? 63 : 31; + tcg_out_ubfm(s, ext, rd, rn, m & max, max); +} + +static inline void tcg_out_sar(TCGContext *s, TCGType ext, + TCGReg rd, TCGReg rn, unsigned int m) +{ + int max = ext ? 63 : 31; + tcg_out_sbfm(s, ext, rd, rn, m & max, max); +} + +static inline void tcg_out_rotr(TCGContext *s, TCGType ext, + TCGReg rd, TCGReg rn, unsigned int m) +{ + int max = ext ? 63 : 31; + tcg_out_extr(s, ext, rd, rn, rn, m & max); +} + +static inline void tcg_out_rotl(TCGContext *s, TCGType ext, + TCGReg rd, TCGReg rn, unsigned int m) +{ + int bits = ext ? 64 : 32; + int max = bits - 1; + tcg_out_extr(s, ext, rd, rn, rn, bits - (m & max)); +} + +static inline void tcg_out_dep(TCGContext *s, TCGType ext, TCGReg rd, + TCGReg rn, unsigned lsb, unsigned width) +{ + unsigned size = ext ? 64 : 32; + unsigned a = (size - lsb) & (size - 1); + unsigned b = width - 1; + tcg_out_bfm(s, ext, rd, rn, a, b); +} + +static void tcg_out_cmp(TCGContext *s, TCGType ext, TCGReg a, + tcg_target_long b, bool const_b) +{ + if (const_b) { + /* Using CMP or CMN aliases. */ + if (b >= 0) { + tcg_out_insn(s, 3401, SUBSI, ext, TCG_REG_XZR, a, b); + } else { + tcg_out_insn(s, 3401, ADDSI, ext, TCG_REG_XZR, a, -b); + } + } else { + /* Using CMP alias SUBS wzr, Wn, Wm */ + tcg_out_insn(s, 3502, SUBS, ext, TCG_REG_XZR, a, b); + } +} + +static inline void tcg_out_goto(TCGContext *s, tcg_insn_unit *target) +{ + ptrdiff_t offset = target - s->code_ptr; + tcg_debug_assert(offset == sextract64(offset, 0, 26)); + tcg_out_insn(s, 3206, B, offset); +} + +static inline void tcg_out_goto_noaddr(TCGContext *s) +{ + /* We pay attention here to not modify the branch target by reading from + the buffer. This ensure that caches and memory are kept coherent during + retranslation. Mask away possible garbage in the high bits for the + first translation, while keeping the offset bits for retranslation. */ + uint32_t old = tcg_in32(s); + tcg_out_insn(s, 3206, B, old); +} + +static inline void tcg_out_goto_cond_noaddr(TCGContext *s, TCGCond c) +{ + /* See comments in tcg_out_goto_noaddr. */ + uint32_t old = tcg_in32(s) >> 5; + tcg_out_insn(s, 3202, B_C, c, old); +} + +static inline void tcg_out_callr(TCGContext *s, TCGReg reg) +{ + tcg_out_insn(s, 3207, BLR, reg); +} + +static inline void tcg_out_call(TCGContext *s, tcg_insn_unit *target) +{ + ptrdiff_t offset = target - s->code_ptr; + if (offset == sextract64(offset, 0, 26)) { + tcg_out_insn(s, 3206, BL, offset); + } else { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_TMP, (intptr_t)target); + tcg_out_callr(s, TCG_REG_TMP); + } +} + +void aarch64_tb_set_jmp_target(uintptr_t jmp_addr, uintptr_t addr) +{ + tcg_insn_unit *code_ptr = (tcg_insn_unit *)jmp_addr; + tcg_insn_unit *target = (tcg_insn_unit *)addr; + + reloc_pc26(code_ptr, target); + flush_icache_range(jmp_addr, jmp_addr + 4); +} + +static inline void tcg_out_goto_label(TCGContext *s, TCGLabel *l) +{ + if (!l->has_value) { + tcg_out_reloc(s, s->code_ptr, R_AARCH64_JUMP26, l, 0); + tcg_out_goto_noaddr(s); + } else { + tcg_out_goto(s, l->u.value_ptr); + } +} + +static void tcg_out_brcond(TCGContext *s, TCGMemOp ext, TCGCond c, TCGArg a, + TCGArg b, bool b_const, TCGLabel *l) +{ + intptr_t offset; + bool need_cmp; + + if (b_const && b == 0 && (c == TCG_COND_EQ || c == TCG_COND_NE)) { + need_cmp = false; + } else { + need_cmp = true; + tcg_out_cmp(s, ext, a, b, b_const); + } + + if (!l->has_value) { + tcg_out_reloc(s, s->code_ptr, R_AARCH64_CONDBR19, l, 0); + offset = tcg_in32(s) >> 5; + } else { + offset = l->u.value_ptr - s->code_ptr; + tcg_debug_assert(offset == sextract64(offset, 0, 19)); + } + + if (need_cmp) { + tcg_out_insn(s, 3202, B_C, c, offset); + } else if (c == TCG_COND_EQ) { + tcg_out_insn(s, 3201, CBZ, ext, a, offset); + } else { + tcg_out_insn(s, 3201, CBNZ, ext, a, offset); + } +} + +static inline void tcg_out_rev64(TCGContext *s, TCGReg rd, TCGReg rn) +{ + tcg_out_insn(s, 3507, REV64, TCG_TYPE_I64, rd, rn); +} + +static inline void tcg_out_rev32(TCGContext *s, TCGReg rd, TCGReg rn) +{ + tcg_out_insn(s, 3507, REV32, TCG_TYPE_I32, rd, rn); +} + +static inline void tcg_out_rev16(TCGContext *s, TCGReg rd, TCGReg rn) +{ + tcg_out_insn(s, 3507, REV16, TCG_TYPE_I32, rd, rn); +} + +static inline void tcg_out_sxt(TCGContext *s, TCGType ext, TCGMemOp s_bits, + TCGReg rd, TCGReg rn) +{ + /* Using ALIASes SXTB, SXTH, SXTW, of SBFM Xd, Xn, #0, #7|15|31 */ + int bits = (8 << s_bits) - 1; + tcg_out_sbfm(s, ext, rd, rn, 0, bits); +} + +static inline void tcg_out_uxt(TCGContext *s, TCGMemOp s_bits, + TCGReg rd, TCGReg rn) +{ + /* Using ALIASes UXTB, UXTH of UBFM Wd, Wn, #0, #7|15 */ + int bits = (8 << s_bits) - 1; + tcg_out_ubfm(s, 0, rd, rn, 0, bits); +} + +static void tcg_out_addsubi(TCGContext *s, int ext, TCGReg rd, + TCGReg rn, int64_t aimm) +{ + if (aimm >= 0) { + tcg_out_insn(s, 3401, ADDI, ext, rd, rn, aimm); + } else { + tcg_out_insn(s, 3401, SUBI, ext, rd, rn, -aimm); + } +} + +static inline void tcg_out_addsub2(TCGContext *s, int ext, TCGReg rl, + TCGReg rh, TCGReg al, TCGReg ah, + tcg_target_long bl, tcg_target_long bh, + bool const_bl, bool const_bh, bool sub) +{ + TCGReg orig_rl = rl; + AArch64Insn insn; + + if (rl == ah || (!const_bh && rl == bh)) { + rl = TCG_REG_TMP; + } + + if (const_bl) { + insn = I3401_ADDSI; + if ((bl < 0) ^ sub) { + insn = I3401_SUBSI; + bl = -bl; + } + tcg_out_insn_3401(s, insn, ext, rl, al, bl); + } else { + tcg_out_insn_3502(s, sub ? I3502_SUBS : I3502_ADDS, ext, rl, al, bl); + } + + insn = I3503_ADC; + if (const_bh) { + /* Note that the only two constants we support are 0 and -1, and + that SBC = rn + ~rm + c, so adc -1 is sbc 0, and vice-versa. */ + if ((bh != 0) ^ sub) { + insn = I3503_SBC; + } + bh = TCG_REG_XZR; + } else if (sub) { + insn = I3503_SBC; + } + tcg_out_insn_3503(s, insn, ext, rh, ah, bh); + + tcg_out_mov(s, ext, orig_rl, rl); +} + +#ifdef CONFIG_SOFTMMU +/* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, + * TCGMemOpIdx oi, uintptr_t ra) + */ +static void * const qemu_ld_helpers[16] = { + [MO_UB] = helper_ret_ldub_mmu, + [MO_LEUW] = helper_le_lduw_mmu, + [MO_LEUL] = helper_le_ldul_mmu, + [MO_LEQ] = helper_le_ldq_mmu, + [MO_BEUW] = helper_be_lduw_mmu, + [MO_BEUL] = helper_be_ldul_mmu, + [MO_BEQ] = helper_be_ldq_mmu, +}; + +/* helper signature: helper_ret_st_mmu(CPUState *env, target_ulong addr, + * uintxx_t val, TCGMemOpIdx oi, + * uintptr_t ra) + */ +static void * const qemu_st_helpers[16] = { + [MO_UB] = helper_ret_stb_mmu, + [MO_LEUW] = helper_le_stw_mmu, + [MO_LEUL] = helper_le_stl_mmu, + [MO_LEQ] = helper_le_stq_mmu, + [MO_BEUW] = helper_be_stw_mmu, + [MO_BEUL] = helper_be_stl_mmu, + [MO_BEQ] = helper_be_stq_mmu, +}; + +static inline void tcg_out_adr(TCGContext *s, TCGReg rd, void *target) +{ + ptrdiff_t offset = tcg_pcrel_diff(s, target); + tcg_debug_assert(offset == sextract64(offset, 0, 21)); + tcg_out_insn(s, 3406, ADR, rd, offset); +} + +static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) +{ + TCGMemOpIdx oi = lb->oi; + TCGMemOp opc = get_memop(oi); + TCGMemOp size = opc & MO_SIZE; + + reloc_pc19(lb->label_ptr[0], s->code_ptr); + + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_X0, TCG_AREG0); + tcg_out_mov(s, TARGET_LONG_BITS == 64, TCG_REG_X1, lb->addrlo_reg); + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_X2, oi); + tcg_out_adr(s, TCG_REG_X3, lb->raddr); + tcg_out_call(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SIZE)]); + if (opc & MO_SIGN) { + tcg_out_sxt(s, lb->type, size, lb->datalo_reg, TCG_REG_X0); + } else { + tcg_out_mov(s, size == MO_64, lb->datalo_reg, TCG_REG_X0); + } + + tcg_out_goto(s, lb->raddr); +} + +static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) +{ + TCGMemOpIdx oi = lb->oi; + TCGMemOp opc = get_memop(oi); + TCGMemOp size = opc & MO_SIZE; + + reloc_pc19(lb->label_ptr[0], s->code_ptr); + + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_X0, TCG_AREG0); + tcg_out_mov(s, TARGET_LONG_BITS == 64, TCG_REG_X1, lb->addrlo_reg); + tcg_out_mov(s, size == MO_64, TCG_REG_X2, lb->datalo_reg); + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_X3, oi); + tcg_out_adr(s, TCG_REG_X4, lb->raddr); + tcg_out_call(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)]); + tcg_out_goto(s, lb->raddr); +} + +static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOpIdx oi, + TCGType ext, TCGReg data_reg, TCGReg addr_reg, + tcg_insn_unit *raddr, tcg_insn_unit *label_ptr) +{ + TCGLabelQemuLdst *label = new_ldst_label(s); + + label->is_ld = is_ld; + label->oi = oi; + label->type = ext; + label->datalo_reg = data_reg; + label->addrlo_reg = addr_reg; + label->raddr = raddr; + label->label_ptr[0] = label_ptr; +} + +/* Load and compare a TLB entry, emitting the conditional jump to the + slow path for the failure case, which will be patched later when finalizing + the slow path. Generated code returns the host addend in X1, + clobbers X0,X2,X3,TMP. */ +static void tcg_out_tlb_read(TCGContext *s, TCGReg addr_reg, TCGMemOp opc, + tcg_insn_unit **label_ptr, int mem_index, + bool is_read) +{ + int tlb_offset = is_read ? + offsetof(CPUArchState, tlb_table[mem_index][0].addr_read) + : offsetof(CPUArchState, tlb_table[mem_index][0].addr_write); + int s_mask = (1 << (opc & MO_SIZE)) - 1; + TCGReg base = TCG_AREG0, x3; + uint64_t tlb_mask; + + /* For aligned accesses, we check the first byte and include the alignment + bits within the address. For unaligned access, we check that we don't + cross pages using the address of the last byte of the access. */ + if ((opc & MO_AMASK) == MO_ALIGN || s_mask == 0) { + tlb_mask = TARGET_PAGE_MASK | s_mask; + x3 = addr_reg; + } else { + tcg_out_insn(s, 3401, ADDI, TARGET_LONG_BITS == 64, + TCG_REG_X3, addr_reg, s_mask); + tlb_mask = TARGET_PAGE_MASK; + x3 = TCG_REG_X3; + } + + /* Extract the TLB index from the address into X0. + X0 = + addr_reg */ + tcg_out_ubfm(s, TARGET_LONG_BITS == 64, TCG_REG_X0, addr_reg, + TARGET_PAGE_BITS, TARGET_PAGE_BITS + CPU_TLB_BITS); + + /* Store the page mask part of the address into X3. */ + tcg_out_logicali(s, I3404_ANDI, TARGET_LONG_BITS == 64, + TCG_REG_X3, x3, tlb_mask); + + /* Add any "high bits" from the tlb offset to the env address into X2, + to take advantage of the LSL12 form of the ADDI instruction. + X2 = env + (tlb_offset & 0xfff000) */ + if (tlb_offset & 0xfff000) { + tcg_out_insn(s, 3401, ADDI, TCG_TYPE_I64, TCG_REG_X2, base, + tlb_offset & 0xfff000); + base = TCG_REG_X2; + } + + /* Merge the tlb index contribution into X2. + X2 = X2 + (X0 << CPU_TLB_ENTRY_BITS) */ + tcg_out_insn(s, 3502S, ADD_LSL, TCG_TYPE_I64, TCG_REG_X2, base, + TCG_REG_X0, CPU_TLB_ENTRY_BITS); + + /* Merge "low bits" from tlb offset, load the tlb comparator into X0. + X0 = load [X2 + (tlb_offset & 0x000fff)] */ + tcg_out_ldst(s, TARGET_LONG_BITS == 32 ? I3312_LDRW : I3312_LDRX, + TCG_REG_X0, TCG_REG_X2, tlb_offset & 0xfff); + + /* Load the tlb addend. Do that early to avoid stalling. + X1 = load [X2 + (tlb_offset & 0xfff) + offsetof(addend)] */ + tcg_out_ldst(s, I3312_LDRX, TCG_REG_X1, TCG_REG_X2, + (tlb_offset & 0xfff) + (offsetof(CPUTLBEntry, addend)) - + (is_read ? offsetof(CPUTLBEntry, addr_read) + : offsetof(CPUTLBEntry, addr_write))); + + /* Perform the address comparison. */ + tcg_out_cmp(s, (TARGET_LONG_BITS == 64), TCG_REG_X0, TCG_REG_X3, 0); + + /* If not equal, we jump to the slow path. */ + *label_ptr = s->code_ptr; + tcg_out_goto_cond_noaddr(s, TCG_COND_NE); +} + +#endif /* CONFIG_SOFTMMU */ + +static void tcg_out_qemu_ld_direct(TCGContext *s, TCGMemOp memop, TCGType ext, + TCGReg data_r, TCGReg addr_r, + TCGType otype, TCGReg off_r) +{ + const TCGMemOp bswap = memop & MO_BSWAP; + + switch (memop & MO_SSIZE) { + case MO_UB: + tcg_out_ldst_r(s, I3312_LDRB, data_r, addr_r, otype, off_r); + break; + case MO_SB: + tcg_out_ldst_r(s, ext ? I3312_LDRSBX : I3312_LDRSBW, + data_r, addr_r, otype, off_r); + break; + case MO_UW: + tcg_out_ldst_r(s, I3312_LDRH, data_r, addr_r, otype, off_r); + if (bswap) { + tcg_out_rev16(s, data_r, data_r); + } + break; + case MO_SW: + if (bswap) { + tcg_out_ldst_r(s, I3312_LDRH, data_r, addr_r, otype, off_r); + tcg_out_rev16(s, data_r, data_r); + tcg_out_sxt(s, ext, MO_16, data_r, data_r); + } else { + tcg_out_ldst_r(s, (ext ? I3312_LDRSHX : I3312_LDRSHW), + data_r, addr_r, otype, off_r); + } + break; + case MO_UL: + tcg_out_ldst_r(s, I3312_LDRW, data_r, addr_r, otype, off_r); + if (bswap) { + tcg_out_rev32(s, data_r, data_r); + } + break; + case MO_SL: + if (bswap) { + tcg_out_ldst_r(s, I3312_LDRW, data_r, addr_r, otype, off_r); + tcg_out_rev32(s, data_r, data_r); + tcg_out_sxt(s, TCG_TYPE_I64, MO_32, data_r, data_r); + } else { + tcg_out_ldst_r(s, I3312_LDRSWX, data_r, addr_r, otype, off_r); + } + break; + case MO_Q: + tcg_out_ldst_r(s, I3312_LDRX, data_r, addr_r, otype, off_r); + if (bswap) { + tcg_out_rev64(s, data_r, data_r); + } + break; + default: + tcg_abort(); + } +} + +static void tcg_out_qemu_st_direct(TCGContext *s, TCGMemOp memop, + TCGReg data_r, TCGReg addr_r, + TCGType otype, TCGReg off_r) +{ + const TCGMemOp bswap = memop & MO_BSWAP; + + switch (memop & MO_SIZE) { + case MO_8: + tcg_out_ldst_r(s, I3312_STRB, data_r, addr_r, otype, off_r); + break; + case MO_16: + if (bswap && data_r != TCG_REG_XZR) { + tcg_out_rev16(s, TCG_REG_TMP, data_r); + data_r = TCG_REG_TMP; + } + tcg_out_ldst_r(s, I3312_STRH, data_r, addr_r, otype, off_r); + break; + case MO_32: + if (bswap && data_r != TCG_REG_XZR) { + tcg_out_rev32(s, TCG_REG_TMP, data_r); + data_r = TCG_REG_TMP; + } + tcg_out_ldst_r(s, I3312_STRW, data_r, addr_r, otype, off_r); + break; + case MO_64: + if (bswap && data_r != TCG_REG_XZR) { + tcg_out_rev64(s, TCG_REG_TMP, data_r); + data_r = TCG_REG_TMP; + } + tcg_out_ldst_r(s, I3312_STRX, data_r, addr_r, otype, off_r); + break; + default: + tcg_abort(); + } +} + +static void tcg_out_qemu_ld(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, + TCGMemOpIdx oi, TCGType ext) +{ + TCGMemOp memop = get_memop(oi); + const TCGType otype = TARGET_LONG_BITS == 64 ? TCG_TYPE_I64 : TCG_TYPE_I32; +#ifdef CONFIG_SOFTMMU + unsigned mem_index = get_mmuidx(oi); + tcg_insn_unit *label_ptr; + + tcg_out_tlb_read(s, addr_reg, memop, &label_ptr, mem_index, 1); + tcg_out_qemu_ld_direct(s, memop, ext, data_reg, + TCG_REG_X1, otype, addr_reg); + add_qemu_ldst_label(s, true, oi, ext, data_reg, addr_reg, + s->code_ptr, label_ptr); +#else /* !CONFIG_SOFTMMU */ + if (USE_GUEST_BASE) { + tcg_out_qemu_ld_direct(s, memop, ext, data_reg, + TCG_REG_GUEST_BASE, otype, addr_reg); + } else { + tcg_out_qemu_ld_direct(s, memop, ext, data_reg, + addr_reg, TCG_TYPE_I64, TCG_REG_XZR); + } +#endif /* CONFIG_SOFTMMU */ +} + +static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, + TCGMemOpIdx oi) +{ + TCGMemOp memop = get_memop(oi); + const TCGType otype = TARGET_LONG_BITS == 64 ? TCG_TYPE_I64 : TCG_TYPE_I32; +#ifdef CONFIG_SOFTMMU + unsigned mem_index = get_mmuidx(oi); + tcg_insn_unit *label_ptr; + + tcg_out_tlb_read(s, addr_reg, memop, &label_ptr, mem_index, 0); + tcg_out_qemu_st_direct(s, memop, data_reg, + TCG_REG_X1, otype, addr_reg); + add_qemu_ldst_label(s, false, oi, (memop & MO_SIZE)== MO_64, + data_reg, addr_reg, s->code_ptr, label_ptr); +#else /* !CONFIG_SOFTMMU */ + if (USE_GUEST_BASE) { + tcg_out_qemu_st_direct(s, memop, data_reg, + TCG_REG_GUEST_BASE, otype, addr_reg); + } else { + tcg_out_qemu_st_direct(s, memop, data_reg, + addr_reg, TCG_TYPE_I64, TCG_REG_XZR); + } +#endif /* CONFIG_SOFTMMU */ +} + +static tcg_insn_unit *tb_ret_addr; + +static void tcg_out_op(TCGContext *s, TCGOpcode opc, + const TCGArg args[TCG_MAX_OP_ARGS], + const int const_args[TCG_MAX_OP_ARGS]) +{ + /* 99% of the time, we can signal the use of extension registers + by looking to see if the opcode handles 64-bit data. */ + TCGType ext = (tcg_op_defs[opc].flags & TCG_OPF_64BIT) != 0; + + /* Hoist the loads of the most common arguments. */ + TCGArg a0 = args[0]; + TCGArg a1 = args[1]; + TCGArg a2 = args[2]; + int c2 = const_args[2]; + + /* Some operands are defined with "rZ" constraint, a register or + the zero register. These need not actually test args[I] == 0. */ +#define REG0(I) (const_args[I] ? TCG_REG_XZR : (TCGReg)args[I]) + + switch (opc) { + case INDEX_op_exit_tb: + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_X0, a0); + tcg_out_goto(s, tb_ret_addr); + break; + + case INDEX_op_goto_tb: +#ifndef USE_DIRECT_JUMP +#error "USE_DIRECT_JUMP required for aarch64" +#endif + tcg_debug_assert(s->tb_jmp_offset != NULL); /* consistency for USE_DIRECT_JUMP */ + s->tb_jmp_offset[a0] = tcg_current_code_size(s); + /* actual branch destination will be patched by + aarch64_tb_set_jmp_target later, beware retranslation. */ + tcg_out_goto_noaddr(s); + s->tb_next_offset[a0] = tcg_current_code_size(s); + break; + + case INDEX_op_br: + tcg_out_goto_label(s, arg_label(a0)); + break; + + case INDEX_op_ld8u_i32: + case INDEX_op_ld8u_i64: + tcg_out_ldst(s, I3312_LDRB, a0, a1, a2); + break; + case INDEX_op_ld8s_i32: + tcg_out_ldst(s, I3312_LDRSBW, a0, a1, a2); + break; + case INDEX_op_ld8s_i64: + tcg_out_ldst(s, I3312_LDRSBX, a0, a1, a2); + break; + case INDEX_op_ld16u_i32: + case INDEX_op_ld16u_i64: + tcg_out_ldst(s, I3312_LDRH, a0, a1, a2); + break; + case INDEX_op_ld16s_i32: + tcg_out_ldst(s, I3312_LDRSHW, a0, a1, a2); + break; + case INDEX_op_ld16s_i64: + tcg_out_ldst(s, I3312_LDRSHX, a0, a1, a2); + break; + case INDEX_op_ld_i32: + case INDEX_op_ld32u_i64: + tcg_out_ldst(s, I3312_LDRW, a0, a1, a2); + break; + case INDEX_op_ld32s_i64: + tcg_out_ldst(s, I3312_LDRSWX, a0, a1, a2); + break; + case INDEX_op_ld_i64: + tcg_out_ldst(s, I3312_LDRX, a0, a1, a2); + break; + + case INDEX_op_st8_i32: + case INDEX_op_st8_i64: + tcg_out_ldst(s, I3312_STRB, REG0(0), a1, a2); + break; + case INDEX_op_st16_i32: + case INDEX_op_st16_i64: + tcg_out_ldst(s, I3312_STRH, REG0(0), a1, a2); + break; + case INDEX_op_st_i32: + case INDEX_op_st32_i64: + tcg_out_ldst(s, I3312_STRW, REG0(0), a1, a2); + break; + case INDEX_op_st_i64: + tcg_out_ldst(s, I3312_STRX, REG0(0), a1, a2); + break; + + case INDEX_op_add_i32: + a2 = (int32_t)a2; + /* FALLTHRU */ + case INDEX_op_add_i64: + if (c2) { + tcg_out_addsubi(s, ext, a0, a1, a2); + } else { + tcg_out_insn(s, 3502, ADD, ext, a0, a1, a2); + } + break; + + case INDEX_op_sub_i32: + a2 = (int32_t)a2; + /* FALLTHRU */ + case INDEX_op_sub_i64: + if (c2) { + tcg_out_addsubi(s, ext, a0, a1, -a2); + } else { + tcg_out_insn(s, 3502, SUB, ext, a0, a1, a2); + } + break; + + case INDEX_op_neg_i64: + case INDEX_op_neg_i32: + tcg_out_insn(s, 3502, SUB, ext, a0, TCG_REG_XZR, a1); + break; + + case INDEX_op_and_i32: + a2 = (int32_t)a2; + /* FALLTHRU */ + case INDEX_op_and_i64: + if (c2) { + tcg_out_logicali(s, I3404_ANDI, ext, a0, a1, a2); + } else { + tcg_out_insn(s, 3510, AND, ext, a0, a1, a2); + } + break; + + case INDEX_op_andc_i32: + a2 = (int32_t)a2; + /* FALLTHRU */ + case INDEX_op_andc_i64: + if (c2) { + tcg_out_logicali(s, I3404_ANDI, ext, a0, a1, ~a2); + } else { + tcg_out_insn(s, 3510, BIC, ext, a0, a1, a2); + } + break; + + case INDEX_op_or_i32: + a2 = (int32_t)a2; + /* FALLTHRU */ + case INDEX_op_or_i64: + if (c2) { + tcg_out_logicali(s, I3404_ORRI, ext, a0, a1, a2); + } else { + tcg_out_insn(s, 3510, ORR, ext, a0, a1, a2); + } + break; + + case INDEX_op_orc_i32: + a2 = (int32_t)a2; + /* FALLTHRU */ + case INDEX_op_orc_i64: + if (c2) { + tcg_out_logicali(s, I3404_ORRI, ext, a0, a1, ~a2); + } else { + tcg_out_insn(s, 3510, ORN, ext, a0, a1, a2); + } + break; + + case INDEX_op_xor_i32: + a2 = (int32_t)a2; + /* FALLTHRU */ + case INDEX_op_xor_i64: + if (c2) { + tcg_out_logicali(s, I3404_EORI, ext, a0, a1, a2); + } else { + tcg_out_insn(s, 3510, EOR, ext, a0, a1, a2); + } + break; + + case INDEX_op_eqv_i32: + a2 = (int32_t)a2; + /* FALLTHRU */ + case INDEX_op_eqv_i64: + if (c2) { + tcg_out_logicali(s, I3404_EORI, ext, a0, a1, ~a2); + } else { + tcg_out_insn(s, 3510, EON, ext, a0, a1, a2); + } + break; + + case INDEX_op_not_i64: + case INDEX_op_not_i32: + tcg_out_insn(s, 3510, ORN, ext, a0, TCG_REG_XZR, a1); + break; + + case INDEX_op_mul_i64: + case INDEX_op_mul_i32: + tcg_out_insn(s, 3509, MADD, ext, a0, a1, a2, TCG_REG_XZR); + break; + + case INDEX_op_div_i64: + case INDEX_op_div_i32: + tcg_out_insn(s, 3508, SDIV, ext, a0, a1, a2); + break; + case INDEX_op_divu_i64: + case INDEX_op_divu_i32: + tcg_out_insn(s, 3508, UDIV, ext, a0, a1, a2); + break; + + case INDEX_op_rem_i64: + case INDEX_op_rem_i32: + tcg_out_insn(s, 3508, SDIV, ext, TCG_REG_TMP, a1, a2); + tcg_out_insn(s, 3509, MSUB, ext, a0, TCG_REG_TMP, a2, a1); + break; + case INDEX_op_remu_i64: + case INDEX_op_remu_i32: + tcg_out_insn(s, 3508, UDIV, ext, TCG_REG_TMP, a1, a2); + tcg_out_insn(s, 3509, MSUB, ext, a0, TCG_REG_TMP, a2, a1); + break; + + case INDEX_op_shl_i64: + case INDEX_op_shl_i32: + if (c2) { + tcg_out_shl(s, ext, a0, a1, a2); + } else { + tcg_out_insn(s, 3508, LSLV, ext, a0, a1, a2); + } + break; + + case INDEX_op_shr_i64: + case INDEX_op_shr_i32: + if (c2) { + tcg_out_shr(s, ext, a0, a1, a2); + } else { + tcg_out_insn(s, 3508, LSRV, ext, a0, a1, a2); + } + break; + + case INDEX_op_sar_i64: + case INDEX_op_sar_i32: + if (c2) { + tcg_out_sar(s, ext, a0, a1, a2); + } else { + tcg_out_insn(s, 3508, ASRV, ext, a0, a1, a2); + } + break; + + case INDEX_op_rotr_i64: + case INDEX_op_rotr_i32: + if (c2) { + tcg_out_rotr(s, ext, a0, a1, a2); + } else { + tcg_out_insn(s, 3508, RORV, ext, a0, a1, a2); + } + break; + + case INDEX_op_rotl_i64: + case INDEX_op_rotl_i32: + if (c2) { + tcg_out_rotl(s, ext, a0, a1, a2); + } else { + tcg_out_insn(s, 3502, SUB, 0, TCG_REG_TMP, TCG_REG_XZR, a2); + tcg_out_insn(s, 3508, RORV, ext, a0, a1, TCG_REG_TMP); + } + break; + + case INDEX_op_brcond_i32: + a1 = (int32_t)a1; + /* FALLTHRU */ + case INDEX_op_brcond_i64: + tcg_out_brcond(s, ext, a2, a0, a1, const_args[1], arg_label(args[3])); + break; + + case INDEX_op_setcond_i32: + a2 = (int32_t)a2; + /* FALLTHRU */ + case INDEX_op_setcond_i64: + tcg_out_cmp(s, ext, a1, a2, c2); + /* Use CSET alias of CSINC Wd, WZR, WZR, invert(cond). */ + tcg_out_insn(s, 3506, CSINC, TCG_TYPE_I32, a0, TCG_REG_XZR, + TCG_REG_XZR, tcg_invert_cond(args[3])); + break; + + case INDEX_op_movcond_i32: + a2 = (int32_t)a2; + /* FALLTHRU */ + case INDEX_op_movcond_i64: + tcg_out_cmp(s, ext, a1, a2, c2); + tcg_out_insn(s, 3506, CSEL, ext, a0, REG0(3), REG0(4), args[5]); + break; + + case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_ld_i64: + tcg_out_qemu_ld(s, a0, a1, a2, ext); + break; + case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st_i64: + tcg_out_qemu_st(s, REG0(0), a1, a2); + break; + + case INDEX_op_bswap64_i64: + tcg_out_rev64(s, a0, a1); + break; + case INDEX_op_bswap32_i64: + case INDEX_op_bswap32_i32: + tcg_out_rev32(s, a0, a1); + break; + case INDEX_op_bswap16_i64: + case INDEX_op_bswap16_i32: + tcg_out_rev16(s, a0, a1); + break; + + case INDEX_op_ext8s_i64: + case INDEX_op_ext8s_i32: + tcg_out_sxt(s, ext, MO_8, a0, a1); + break; + case INDEX_op_ext16s_i64: + case INDEX_op_ext16s_i32: + tcg_out_sxt(s, ext, MO_16, a0, a1); + break; + case INDEX_op_ext_i32_i64: + case INDEX_op_ext32s_i64: + tcg_out_sxt(s, TCG_TYPE_I64, MO_32, a0, a1); + break; + case INDEX_op_ext8u_i64: + case INDEX_op_ext8u_i32: + tcg_out_uxt(s, MO_8, a0, a1); + break; + case INDEX_op_ext16u_i64: + case INDEX_op_ext16u_i32: + tcg_out_uxt(s, MO_16, a0, a1); + break; + case INDEX_op_extu_i32_i64: + case INDEX_op_ext32u_i64: + tcg_out_movr(s, TCG_TYPE_I32, a0, a1); + break; + + case INDEX_op_deposit_i64: + case INDEX_op_deposit_i32: + tcg_out_dep(s, ext, a0, REG0(2), args[3], args[4]); + break; + + case INDEX_op_add2_i32: + tcg_out_addsub2(s, TCG_TYPE_I32, a0, a1, REG0(2), REG0(3), + (int32_t)args[4], args[5], const_args[4], + const_args[5], false); + break; + case INDEX_op_add2_i64: + tcg_out_addsub2(s, TCG_TYPE_I64, a0, a1, REG0(2), REG0(3), args[4], + args[5], const_args[4], const_args[5], false); + break; + case INDEX_op_sub2_i32: + tcg_out_addsub2(s, TCG_TYPE_I32, a0, a1, REG0(2), REG0(3), + (int32_t)args[4], args[5], const_args[4], + const_args[5], true); + break; + case INDEX_op_sub2_i64: + tcg_out_addsub2(s, TCG_TYPE_I64, a0, a1, REG0(2), REG0(3), args[4], + args[5], const_args[4], const_args[5], true); + break; + + case INDEX_op_muluh_i64: + tcg_out_insn(s, 3508, UMULH, TCG_TYPE_I64, a0, a1, a2); + break; + case INDEX_op_mulsh_i64: + tcg_out_insn(s, 3508, SMULH, TCG_TYPE_I64, a0, a1, a2); + break; + + case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ + case INDEX_op_mov_i64: + case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ + case INDEX_op_movi_i64: + case INDEX_op_call: /* Always emitted via tcg_out_call. */ + default: + tcg_abort(); + } + +#undef REG0 +} + +static const TCGTargetOpDef aarch64_op_defs[] = { + { INDEX_op_exit_tb, { } }, + { INDEX_op_goto_tb, { } }, + { INDEX_op_br, { } }, + + { INDEX_op_ld8u_i32, { "r", "r" } }, + { INDEX_op_ld8s_i32, { "r", "r" } }, + { INDEX_op_ld16u_i32, { "r", "r" } }, + { INDEX_op_ld16s_i32, { "r", "r" } }, + { INDEX_op_ld_i32, { "r", "r" } }, + { INDEX_op_ld8u_i64, { "r", "r" } }, + { INDEX_op_ld8s_i64, { "r", "r" } }, + { INDEX_op_ld16u_i64, { "r", "r" } }, + { INDEX_op_ld16s_i64, { "r", "r" } }, + { INDEX_op_ld32u_i64, { "r", "r" } }, + { INDEX_op_ld32s_i64, { "r", "r" } }, + { INDEX_op_ld_i64, { "r", "r" } }, + + { INDEX_op_st8_i32, { "rZ", "r" } }, + { INDEX_op_st16_i32, { "rZ", "r" } }, + { INDEX_op_st_i32, { "rZ", "r" } }, + { INDEX_op_st8_i64, { "rZ", "r" } }, + { INDEX_op_st16_i64, { "rZ", "r" } }, + { INDEX_op_st32_i64, { "rZ", "r" } }, + { INDEX_op_st_i64, { "rZ", "r" } }, + + { INDEX_op_add_i32, { "r", "r", "rA" } }, + { INDEX_op_add_i64, { "r", "r", "rA" } }, + { INDEX_op_sub_i32, { "r", "r", "rA" } }, + { INDEX_op_sub_i64, { "r", "r", "rA" } }, + { INDEX_op_mul_i32, { "r", "r", "r" } }, + { INDEX_op_mul_i64, { "r", "r", "r" } }, + { INDEX_op_div_i32, { "r", "r", "r" } }, + { INDEX_op_div_i64, { "r", "r", "r" } }, + { INDEX_op_divu_i32, { "r", "r", "r" } }, + { INDEX_op_divu_i64, { "r", "r", "r" } }, + { INDEX_op_rem_i32, { "r", "r", "r" } }, + { INDEX_op_rem_i64, { "r", "r", "r" } }, + { INDEX_op_remu_i32, { "r", "r", "r" } }, + { INDEX_op_remu_i64, { "r", "r", "r" } }, + { INDEX_op_and_i32, { "r", "r", "rL" } }, + { INDEX_op_and_i64, { "r", "r", "rL" } }, + { INDEX_op_or_i32, { "r", "r", "rL" } }, + { INDEX_op_or_i64, { "r", "r", "rL" } }, + { INDEX_op_xor_i32, { "r", "r", "rL" } }, + { INDEX_op_xor_i64, { "r", "r", "rL" } }, + { INDEX_op_andc_i32, { "r", "r", "rL" } }, + { INDEX_op_andc_i64, { "r", "r", "rL" } }, + { INDEX_op_orc_i32, { "r", "r", "rL" } }, + { INDEX_op_orc_i64, { "r", "r", "rL" } }, + { INDEX_op_eqv_i32, { "r", "r", "rL" } }, + { INDEX_op_eqv_i64, { "r", "r", "rL" } }, + + { INDEX_op_neg_i32, { "r", "r" } }, + { INDEX_op_neg_i64, { "r", "r" } }, + { INDEX_op_not_i32, { "r", "r" } }, + { INDEX_op_not_i64, { "r", "r" } }, + + { INDEX_op_shl_i32, { "r", "r", "ri" } }, + { INDEX_op_shr_i32, { "r", "r", "ri" } }, + { INDEX_op_sar_i32, { "r", "r", "ri" } }, + { INDEX_op_rotl_i32, { "r", "r", "ri" } }, + { INDEX_op_rotr_i32, { "r", "r", "ri" } }, + { INDEX_op_shl_i64, { "r", "r", "ri" } }, + { INDEX_op_shr_i64, { "r", "r", "ri" } }, + { INDEX_op_sar_i64, { "r", "r", "ri" } }, + { INDEX_op_rotl_i64, { "r", "r", "ri" } }, + { INDEX_op_rotr_i64, { "r", "r", "ri" } }, + + { INDEX_op_brcond_i32, { "r", "rA" } }, + { INDEX_op_brcond_i64, { "r", "rA" } }, + { INDEX_op_setcond_i32, { "r", "r", "rA" } }, + { INDEX_op_setcond_i64, { "r", "r", "rA" } }, + { INDEX_op_movcond_i32, { "r", "r", "rA", "rZ", "rZ" } }, + { INDEX_op_movcond_i64, { "r", "r", "rA", "rZ", "rZ" } }, + + { INDEX_op_qemu_ld_i32, { "r", "l" } }, + { INDEX_op_qemu_ld_i64, { "r", "l" } }, + { INDEX_op_qemu_st_i32, { "lZ", "l" } }, + { INDEX_op_qemu_st_i64, { "lZ", "l" } }, + + { INDEX_op_bswap16_i32, { "r", "r" } }, + { INDEX_op_bswap32_i32, { "r", "r" } }, + { INDEX_op_bswap16_i64, { "r", "r" } }, + { INDEX_op_bswap32_i64, { "r", "r" } }, + { INDEX_op_bswap64_i64, { "r", "r" } }, + + { INDEX_op_ext8s_i32, { "r", "r" } }, + { INDEX_op_ext16s_i32, { "r", "r" } }, + { INDEX_op_ext8u_i32, { "r", "r" } }, + { INDEX_op_ext16u_i32, { "r", "r" } }, + + { INDEX_op_ext8s_i64, { "r", "r" } }, + { INDEX_op_ext16s_i64, { "r", "r" } }, + { INDEX_op_ext32s_i64, { "r", "r" } }, + { INDEX_op_ext8u_i64, { "r", "r" } }, + { INDEX_op_ext16u_i64, { "r", "r" } }, + { INDEX_op_ext32u_i64, { "r", "r" } }, + { INDEX_op_ext_i32_i64, { "r", "r" } }, + { INDEX_op_extu_i32_i64, { "r", "r" } }, + + { INDEX_op_deposit_i32, { "r", "0", "rZ" } }, + { INDEX_op_deposit_i64, { "r", "0", "rZ" } }, + + { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rA", "rMZ" } }, + { INDEX_op_add2_i64, { "r", "r", "rZ", "rZ", "rA", "rMZ" } }, + { INDEX_op_sub2_i32, { "r", "r", "rZ", "rZ", "rA", "rMZ" } }, + { INDEX_op_sub2_i64, { "r", "r", "rZ", "rZ", "rA", "rMZ" } }, + + { INDEX_op_muluh_i64, { "r", "r", "r" } }, + { INDEX_op_mulsh_i64, { "r", "r", "r" } }, + + { -1 }, +}; + +static void tcg_target_init(TCGContext *s) +{ + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffffffff); + + tcg_regset_set32(tcg_target_call_clobber_regs, 0, + (1 << TCG_REG_X0) | (1 << TCG_REG_X1) | + (1 << TCG_REG_X2) | (1 << TCG_REG_X3) | + (1 << TCG_REG_X4) | (1 << TCG_REG_X5) | + (1 << TCG_REG_X6) | (1 << TCG_REG_X7) | + (1 << TCG_REG_X8) | (1 << TCG_REG_X9) | + (1 << TCG_REG_X10) | (1 << TCG_REG_X11) | + (1 << TCG_REG_X12) | (1 << TCG_REG_X13) | + (1 << TCG_REG_X14) | (1 << TCG_REG_X15) | + (1 << TCG_REG_X16) | (1 << TCG_REG_X17) | + (1 << TCG_REG_X18) | (1 << TCG_REG_X30)); + + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_FP); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_X18); /* platform register */ + + tcg_add_target_add_op_defs(aarch64_op_defs); +} + +/* Saving pairs: (X19, X20) .. (X27, X28), (X29(fp), X30(lr)). */ +#define PUSH_SIZE ((30 - 19 + 1) * 8) + +#define FRAME_SIZE \ + ((PUSH_SIZE \ + + TCG_STATIC_CALL_ARGS_SIZE \ + + CPU_TEMP_BUF_NLONGS * sizeof(long) \ + + TCG_TARGET_STACK_ALIGN - 1) \ + & ~(TCG_TARGET_STACK_ALIGN - 1)) + +/* We're expecting a 2 byte uleb128 encoded value. */ +QEMU_BUILD_BUG_ON(FRAME_SIZE >= (1 << 14)); + +/* We're expecting to use a single ADDI insn. */ +QEMU_BUILD_BUG_ON(FRAME_SIZE - PUSH_SIZE > 0xfff); + +static void tcg_target_qemu_prologue(TCGContext *s) +{ + TCGReg r; + + /* Push (FP, LR) and allocate space for all saved registers. */ + tcg_out_insn(s, 3314, STP, TCG_REG_FP, TCG_REG_LR, + TCG_REG_SP, -PUSH_SIZE, 1, 1); + + /* Set up frame pointer for canonical unwinding. */ + tcg_out_movr_sp(s, TCG_TYPE_I64, TCG_REG_FP, TCG_REG_SP); + + /* Store callee-preserved regs x19..x28. */ + for (r = TCG_REG_X19; r <= TCG_REG_X27; r += 2) { + int ofs = (r - TCG_REG_X19 + 2) * 8; + tcg_out_insn(s, 3314, STP, r, r + 1, TCG_REG_SP, ofs, 1, 0); + } + + /* Make stack space for TCG locals. */ + tcg_out_insn(s, 3401, SUBI, TCG_TYPE_I64, TCG_REG_SP, TCG_REG_SP, + FRAME_SIZE - PUSH_SIZE); + + /* Inform TCG about how to find TCG locals with register, offset, size. */ + tcg_set_frame(s, TCG_REG_SP, TCG_STATIC_CALL_ARGS_SIZE, + CPU_TEMP_BUF_NLONGS * sizeof(long)); + +#if !defined(CONFIG_SOFTMMU) + if (USE_GUEST_BASE) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_GUEST_BASE, guest_base); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_GUEST_BASE); + } +#endif + + tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); + tcg_out_insn(s, 3207, BR, tcg_target_call_iarg_regs[1]); + + tb_ret_addr = s->code_ptr; + + /* Remove TCG locals stack space. */ + tcg_out_insn(s, 3401, ADDI, TCG_TYPE_I64, TCG_REG_SP, TCG_REG_SP, + FRAME_SIZE - PUSH_SIZE); + + /* Restore registers x19..x28. */ + for (r = TCG_REG_X19; r <= TCG_REG_X27; r += 2) { + int ofs = (r - TCG_REG_X19 + 2) * 8; + tcg_out_insn(s, 3314, LDP, r, r + 1, TCG_REG_SP, ofs, 1, 0); + } + + /* Pop (FP, LR), restore SP to previous frame. */ + tcg_out_insn(s, 3314, LDP, TCG_REG_FP, TCG_REG_LR, + TCG_REG_SP, PUSH_SIZE, 0, 1); + tcg_out_insn(s, 3207, RET, TCG_REG_LR); +} + +typedef struct { + DebugFrameHeader h; + uint8_t fde_def_cfa[4]; + uint8_t fde_reg_ofs[24]; +} DebugFrame; + +#define ELF_HOST_MACHINE EM_AARCH64 + +static const DebugFrame debug_frame = { + .h.cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ + .h.cie.id = -1, + .h.cie.version = 1, + .h.cie.code_align = 1, + .h.cie.data_align = 0x78, /* sleb128 -8 */ + .h.cie.return_column = TCG_REG_LR, + + /* Total FDE size does not include the "len" member. */ + .h.fde.len = sizeof(DebugFrame) - offsetof(DebugFrame, h.fde.cie_offset), + + .fde_def_cfa = { + 12, TCG_REG_SP, /* DW_CFA_def_cfa sp, ... */ + (FRAME_SIZE & 0x7f) | 0x80, /* ... uleb128 FRAME_SIZE */ + (FRAME_SIZE >> 7) + }, + .fde_reg_ofs = { + 0x80 + 28, 1, /* DW_CFA_offset, x28, -8 */ + 0x80 + 27, 2, /* DW_CFA_offset, x27, -16 */ + 0x80 + 26, 3, /* DW_CFA_offset, x26, -24 */ + 0x80 + 25, 4, /* DW_CFA_offset, x25, -32 */ + 0x80 + 24, 5, /* DW_CFA_offset, x24, -40 */ + 0x80 + 23, 6, /* DW_CFA_offset, x23, -48 */ + 0x80 + 22, 7, /* DW_CFA_offset, x22, -56 */ + 0x80 + 21, 8, /* DW_CFA_offset, x21, -64 */ + 0x80 + 20, 9, /* DW_CFA_offset, x20, -72 */ + 0x80 + 19, 10, /* DW_CFA_offset, x1p, -80 */ + 0x80 + 30, 11, /* DW_CFA_offset, lr, -88 */ + 0x80 + 29, 12, /* DW_CFA_offset, fp, -96 */ + } +}; + +void tcg_register_jit(void *buf, size_t buf_size) +{ + tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); +} diff --git a/qemu/tcg/arm/tcg-target.c b/qemu/tcg/arm/tcg-target.c deleted file mode 100644 index ae2ec7a92..000000000 --- a/qemu/tcg/arm/tcg-target.c +++ /dev/null @@ -1,2128 +0,0 @@ -/* - * Tiny Code Generator for QEMU - * - * Copyright (c) 2008 Andrzej Zaborowski - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "elf.h" -#include "tcg-be-ldst.h" - -/* The __ARM_ARCH define is provided by gcc 4.8. Construct it otherwise. */ -#ifndef __ARM_ARCH -# if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \ - || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \ - || defined(__ARM_ARCH_7EM__) -# define __ARM_ARCH 7 -# elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ - || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) \ - || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) -# define __ARM_ARCH 6 -# elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5E__) \ - || defined(__ARM_ARCH_5T__) || defined(__ARM_ARCH_5TE__) \ - || defined(__ARM_ARCH_5TEJ__) -# define __ARM_ARCH 5 -# else -# define __ARM_ARCH 4 -# endif -#endif - -static int arm_arch = __ARM_ARCH; - -#if defined(__ARM_ARCH_5T__) \ - || defined(__ARM_ARCH_5TE__) || defined(__ARM_ARCH_5TEJ__) -# define use_armv5t_instructions 1 -#else -# define use_armv5t_instructions use_armv6_instructions -#endif - -#define use_armv6_instructions (__ARM_ARCH >= 6 || arm_arch >= 6) -#define use_armv7_instructions (__ARM_ARCH >= 7 || arm_arch >= 7) - -#ifndef use_idiv_instructions -bool use_idiv_instructions; -#endif - -/* ??? Ought to think about changing CONFIG_SOFTMMU to always defined. */ -#ifdef CONFIG_SOFTMMU -# define USING_SOFTMMU 1 -#else -# define USING_SOFTMMU 0 -#endif - -#ifndef NDEBUG -static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { - "%r0", - "%r1", - "%r2", - "%r3", - "%r4", - "%r5", - "%r6", - "%r7", - "%r8", - "%r9", - "%r10", - "%r11", - "%r12", - "%r13", - "%r14", - "%pc", -}; -#endif - -static const int tcg_target_reg_alloc_order[] = { - TCG_REG_R4, - TCG_REG_R5, - TCG_REG_R6, - TCG_REG_R7, - TCG_REG_R8, - TCG_REG_R9, - TCG_REG_R10, - TCG_REG_R11, - TCG_REG_R13, - TCG_REG_R0, - TCG_REG_R1, - TCG_REG_R2, - TCG_REG_R3, - TCG_REG_R12, - TCG_REG_R14, -}; - -static const int tcg_target_call_iarg_regs[4] = { - TCG_REG_R0, TCG_REG_R1, TCG_REG_R2, TCG_REG_R3 -}; -static const int tcg_target_call_oarg_regs[2] = { - TCG_REG_R0, TCG_REG_R1 -}; - -#define TCG_REG_TMP TCG_REG_R12 - -static inline void reloc_pc24(tcg_insn_unit *code_ptr, tcg_insn_unit *target) -{ - ptrdiff_t offset = (tcg_ptr_byte_diff(target, code_ptr) - 8) >> 2; - *code_ptr = (*code_ptr & ~0xffffff) | (offset & 0xffffff); -} - -static void patch_reloc(tcg_insn_unit *code_ptr, int type, - intptr_t value, intptr_t addend) -{ - assert(type == R_ARM_PC24); - assert(addend == 0); - reloc_pc24(code_ptr, (tcg_insn_unit *)value); -} - -#define TCG_CT_CONST_ARM 0x100 -#define TCG_CT_CONST_INV 0x200 -#define TCG_CT_CONST_NEG 0x400 -#define TCG_CT_CONST_ZERO 0x800 - -/* parse target specific constraints */ -static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) -{ - const char *ct_str; - - ct_str = *pct_str; - switch (ct_str[0]) { - case 'I': - ct->ct |= TCG_CT_CONST_ARM; - break; - case 'K': - ct->ct |= TCG_CT_CONST_INV; - break; - case 'N': /* The gcc constraint letter is L, already used here. */ - ct->ct |= TCG_CT_CONST_NEG; - break; - case 'Z': - ct->ct |= TCG_CT_CONST_ZERO; - break; - - case 'r': - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); - break; - - /* qemu_ld address */ - case 'l': - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); -#ifdef CONFIG_SOFTMMU - /* r0-r2,lr will be overwritten when reading the tlb entry, - so don't use these. */ - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R2); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R14); -#endif - break; - - /* qemu_st address & data */ - case 's': - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); - /* r0-r2 will be overwritten when reading the tlb entry (softmmu only) - and r0-r1 doing the byte swapping, so don't use these. */ - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); -#if defined(CONFIG_SOFTMMU) - /* Avoid clashes with registers being used for helper args */ - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R2); -#if TARGET_LONG_BITS == 64 - /* Avoid clashes with registers being used for helper args */ - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3); -#endif - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R14); -#endif - break; - - default: - return -1; - } - ct_str++; - *pct_str = ct_str; - - return 0; -} - -static inline uint32_t rotl(uint32_t val, int n) -{ - return (val << n) | (val >> (32 - n)); -} - -/* ARM immediates for ALU instructions are made of an unsigned 8-bit - right-rotated by an even amount between 0 and 30. */ -static inline int encode_imm(uint32_t imm) -{ - int shift; - - /* simple case, only lower bits */ - if ((imm & ~0xff) == 0) - return 0; - /* then try a simple even shift */ - shift = ctz32(imm) & ~1; - if (((imm >> shift) & ~0xff) == 0) - return 32 - shift; - /* now try harder with rotations */ - if ((rotl(imm, 2) & ~0xff) == 0) - return 2; - if ((rotl(imm, 4) & ~0xff) == 0) - return 4; - if ((rotl(imm, 6) & ~0xff) == 0) - return 6; - /* imm can't be encoded */ - return -1; -} - -static inline int check_fit_imm(uint32_t imm) -{ - return encode_imm(imm) >= 0; -} - -/* Test if a constant matches the constraint. - * TODO: define constraints for: - * - * ldr/str offset: between -0xfff and 0xfff - * ldrh/strh offset: between -0xff and 0xff - * mov operand2: values represented with x << (2 * y), x < 0x100 - * add, sub, eor...: ditto - */ -static inline int tcg_target_const_match(tcg_target_long val, TCGType type, - const TCGArgConstraint *arg_ct) -{ - int ct; - ct = arg_ct->ct; - if (ct & TCG_CT_CONST) { - return 1; - } else if ((ct & TCG_CT_CONST_ARM) && check_fit_imm(val)) { - return 1; - } else if ((ct & TCG_CT_CONST_INV) && check_fit_imm(~val)) { - return 1; - } else if ((ct & TCG_CT_CONST_NEG) && check_fit_imm(-val)) { - return 1; - } else if ((ct & TCG_CT_CONST_ZERO) && val == 0) { - return 1; - } else { - return 0; - } -} - -#define TO_CPSR (1 << 20) - -typedef enum { - ARITH_AND = 0x0 << 21, - ARITH_EOR = 0x1 << 21, - ARITH_SUB = 0x2 << 21, - ARITH_RSB = 0x3 << 21, - ARITH_ADD = 0x4 << 21, - ARITH_ADC = 0x5 << 21, - ARITH_SBC = 0x6 << 21, - ARITH_RSC = 0x7 << 21, - ARITH_TST = 0x8 << 21 | TO_CPSR, - ARITH_CMP = 0xa << 21 | TO_CPSR, - ARITH_CMN = 0xb << 21 | TO_CPSR, - ARITH_ORR = 0xc << 21, - ARITH_MOV = 0xd << 21, - ARITH_BIC = 0xe << 21, - ARITH_MVN = 0xf << 21, - - INSN_LDR_IMM = 0x04100000, - INSN_LDR_REG = 0x06100000, - INSN_STR_IMM = 0x04000000, - INSN_STR_REG = 0x06000000, - - INSN_LDRH_IMM = 0x005000b0, - INSN_LDRH_REG = 0x001000b0, - INSN_LDRSH_IMM = 0x005000f0, - INSN_LDRSH_REG = 0x001000f0, - INSN_STRH_IMM = 0x004000b0, - INSN_STRH_REG = 0x000000b0, - - INSN_LDRB_IMM = 0x04500000, - INSN_LDRB_REG = 0x06500000, - INSN_LDRSB_IMM = 0x005000d0, - INSN_LDRSB_REG = 0x001000d0, - INSN_STRB_IMM = 0x04400000, - INSN_STRB_REG = 0x06400000, - - INSN_LDRD_IMM = 0x004000d0, - INSN_LDRD_REG = 0x000000d0, - INSN_STRD_IMM = 0x004000f0, - INSN_STRD_REG = 0x000000f0, -} ARMInsn; - -#define SHIFT_IMM_LSL(im) (((im) << 7) | 0x00) -#define SHIFT_IMM_LSR(im) (((im) << 7) | 0x20) -#define SHIFT_IMM_ASR(im) (((im) << 7) | 0x40) -#define SHIFT_IMM_ROR(im) (((im) << 7) | 0x60) -#define SHIFT_REG_LSL(rs) (((rs) << 8) | 0x10) -#define SHIFT_REG_LSR(rs) (((rs) << 8) | 0x30) -#define SHIFT_REG_ASR(rs) (((rs) << 8) | 0x50) -#define SHIFT_REG_ROR(rs) (((rs) << 8) | 0x70) - -enum arm_cond_code_e { - COND_EQ = 0x0, - COND_NE = 0x1, - COND_CS = 0x2, /* Unsigned greater or equal */ - COND_CC = 0x3, /* Unsigned less than */ - COND_MI = 0x4, /* Negative */ - COND_PL = 0x5, /* Zero or greater */ - COND_VS = 0x6, /* Overflow */ - COND_VC = 0x7, /* No overflow */ - COND_HI = 0x8, /* Unsigned greater than */ - COND_LS = 0x9, /* Unsigned less or equal */ - COND_GE = 0xa, - COND_LT = 0xb, - COND_GT = 0xc, - COND_LE = 0xd, - COND_AL = 0xe, -}; - -static const uint8_t tcg_cond_to_arm_cond[] = { - [TCG_COND_EQ] = COND_EQ, - [TCG_COND_NE] = COND_NE, - [TCG_COND_LT] = COND_LT, - [TCG_COND_GE] = COND_GE, - [TCG_COND_LE] = COND_LE, - [TCG_COND_GT] = COND_GT, - /* unsigned */ - [TCG_COND_LTU] = COND_CC, - [TCG_COND_GEU] = COND_CS, - [TCG_COND_LEU] = COND_LS, - [TCG_COND_GTU] = COND_HI, -}; - -static inline void tcg_out_bx(TCGContext *s, int cond, int rn) -{ - tcg_out32(s, (cond << 28) | 0x012fff10 | rn); -} - -static inline void tcg_out_b(TCGContext *s, int cond, int32_t offset) -{ - tcg_out32(s, (cond << 28) | 0x0a000000 | - (((offset - 8) >> 2) & 0x00ffffff)); -} - -static inline void tcg_out_b_noaddr(TCGContext *s, int cond) -{ - /* We pay attention here to not modify the branch target by masking - the corresponding bytes. This ensure that caches and memory are - kept coherent during retranslation. */ - tcg_out32(s, deposit32(*s->code_ptr, 24, 8, (cond << 4) | 0x0a)); -} - -static inline void tcg_out_bl_noaddr(TCGContext *s, int cond) -{ - /* We pay attention here to not modify the branch target by masking - the corresponding bytes. This ensure that caches and memory are - kept coherent during retranslation. */ - tcg_out32(s, deposit32(*s->code_ptr, 24, 8, (cond << 4) | 0x0b)); -} - -static inline void tcg_out_bl(TCGContext *s, int cond, int32_t offset) -{ - tcg_out32(s, (cond << 28) | 0x0b000000 | - (((offset - 8) >> 2) & 0x00ffffff)); -} - -static inline void tcg_out_blx(TCGContext *s, int cond, int rn) -{ - tcg_out32(s, (cond << 28) | 0x012fff30 | rn); -} - -static inline void tcg_out_blx_imm(TCGContext *s, int32_t offset) -{ - tcg_out32(s, 0xfa000000 | ((offset & 2) << 23) | - (((offset - 8) >> 2) & 0x00ffffff)); -} - -static inline void tcg_out_dat_reg(TCGContext *s, - int cond, int opc, int rd, int rn, int rm, int shift) -{ - tcg_out32(s, (cond << 28) | (0 << 25) | opc | - (rn << 16) | (rd << 12) | shift | rm); -} - -static inline void tcg_out_nop(TCGContext *s) -{ - if (use_armv7_instructions) { - /* Architected nop introduced in v6k. */ - /* ??? This is an MSR (imm) 0,0,0 insn. Anyone know if this - also Just So Happened to do nothing on pre-v6k so that we - don't need to conditionalize it? */ - tcg_out32(s, 0xe320f000); - } else { - /* Prior to that the assembler uses mov r0, r0. */ - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, 0, 0, 0, SHIFT_IMM_LSL(0)); - } -} - -static inline void tcg_out_mov_reg(TCGContext *s, int cond, int rd, int rm) -{ - /* Simple reg-reg move, optimising out the 'do nothing' case */ - if (rd != rm) { - tcg_out_dat_reg(s, cond, ARITH_MOV, rd, 0, rm, SHIFT_IMM_LSL(0)); - } -} - -static inline void tcg_out_dat_imm(TCGContext *s, - int cond, int opc, int rd, int rn, int im) -{ - tcg_out32(s, (cond << 28) | (1 << 25) | opc | - (rn << 16) | (rd << 12) | im); -} - -static void tcg_out_movi32(TCGContext *s, int cond, int rd, uint32_t arg) -{ - int rot, opc, rn; - - /* For armv7, make sure not to use movw+movt when mov/mvn would do. - Speed things up by only checking when movt would be required. - Prior to armv7, have one go at fully rotated immediates before - doing the decomposition thing below. */ - if (!use_armv7_instructions || (arg & 0xffff0000)) { - rot = encode_imm(arg); - if (rot >= 0) { - tcg_out_dat_imm(s, cond, ARITH_MOV, rd, 0, - rotl(arg, rot) | (rot << 7)); - return; - } - rot = encode_imm(~arg); - if (rot >= 0) { - tcg_out_dat_imm(s, cond, ARITH_MVN, rd, 0, - rotl(~arg, rot) | (rot << 7)); - return; - } - } - - /* Use movw + movt. */ - if (use_armv7_instructions) { - /* movw */ - tcg_out32(s, (cond << 28) | 0x03000000 | (rd << 12) - | ((arg << 4) & 0x000f0000) | (arg & 0xfff)); - if (arg & 0xffff0000) { - /* movt */ - tcg_out32(s, (cond << 28) | 0x03400000 | (rd << 12) - | ((arg >> 12) & 0x000f0000) | ((arg >> 16) & 0xfff)); - } - return; - } - - /* TODO: This is very suboptimal, we can easily have a constant - pool somewhere after all the instructions. */ - opc = ARITH_MOV; - rn = 0; - /* If we have lots of leading 1's, we can shorten the sequence by - beginning with mvn and then clearing higher bits with eor. */ - if (clz32(~arg) > clz32(arg)) { - opc = ARITH_MVN, arg = ~arg; - } - do { - int i = ctz32(arg) & ~1; - rot = ((32 - i) << 7) & 0xf00; - tcg_out_dat_imm(s, cond, opc, rd, rn, ((arg >> i) & 0xff) | rot); - arg &= ~(0xff << i); - - opc = ARITH_EOR; - rn = rd; - } while (arg); -} - -static inline void tcg_out_dat_rI(TCGContext *s, int cond, int opc, TCGArg dst, - TCGArg lhs, TCGArg rhs, int rhs_is_const) -{ - /* Emit either the reg,imm or reg,reg form of a data-processing insn. - * rhs must satisfy the "rI" constraint. - */ - if (rhs_is_const) { - int rot = encode_imm(rhs); - assert(rot >= 0); - tcg_out_dat_imm(s, cond, opc, dst, lhs, rotl(rhs, rot) | (rot << 7)); - } else { - tcg_out_dat_reg(s, cond, opc, dst, lhs, rhs, SHIFT_IMM_LSL(0)); - } -} - -static void tcg_out_dat_rIK(TCGContext *s, int cond, int opc, int opinv, - TCGReg dst, TCGReg lhs, TCGArg rhs, - bool rhs_is_const) -{ - /* Emit either the reg,imm or reg,reg form of a data-processing insn. - * rhs must satisfy the "rIK" constraint. - */ - if (rhs_is_const) { - int rot = encode_imm(rhs); - if (rot < 0) { - rhs = ~rhs; - rot = encode_imm(rhs); - assert(rot >= 0); - opc = opinv; - } - tcg_out_dat_imm(s, cond, opc, dst, lhs, rotl(rhs, rot) | (rot << 7)); - } else { - tcg_out_dat_reg(s, cond, opc, dst, lhs, rhs, SHIFT_IMM_LSL(0)); - } -} - -static void tcg_out_dat_rIN(TCGContext *s, int cond, int opc, int opneg, - TCGArg dst, TCGArg lhs, TCGArg rhs, - bool rhs_is_const) -{ - /* Emit either the reg,imm or reg,reg form of a data-processing insn. - * rhs must satisfy the "rIN" constraint. - */ - if (rhs_is_const) { - int rot = encode_imm(rhs); - if (rot < 0) { - rhs = -rhs; - rot = encode_imm(rhs); - assert(rot >= 0); - opc = opneg; - } - tcg_out_dat_imm(s, cond, opc, dst, lhs, rotl(rhs, rot) | (rot << 7)); - } else { - tcg_out_dat_reg(s, cond, opc, dst, lhs, rhs, SHIFT_IMM_LSL(0)); - } -} - -static inline void tcg_out_mul32(TCGContext *s, int cond, TCGReg rd, - TCGReg rn, TCGReg rm) -{ - /* if ArchVersion() < 6 && d == n then UNPREDICTABLE; */ - if (!use_armv6_instructions && rd == rn) { - if (rd == rm) { - /* rd == rn == rm; copy an input to tmp first. */ - tcg_out_mov_reg(s, cond, TCG_REG_TMP, rn); - rm = rn = TCG_REG_TMP; - } else { - rn = rm; - rm = rd; - } - } - /* mul */ - tcg_out32(s, (cond << 28) | 0x90 | (rd << 16) | (rm << 8) | rn); -} - -static inline void tcg_out_umull32(TCGContext *s, int cond, TCGReg rd0, - TCGReg rd1, TCGReg rn, TCGReg rm) -{ - /* if ArchVersion() < 6 && (dHi == n || dLo == n) then UNPREDICTABLE; */ - if (!use_armv6_instructions && (rd0 == rn || rd1 == rn)) { - if (rd0 == rm || rd1 == rm) { - tcg_out_mov_reg(s, cond, TCG_REG_TMP, rn); - rn = TCG_REG_TMP; - } else { - TCGReg t = rn; - rn = rm; - rm = t; - } - } - /* umull */ - tcg_out32(s, (cond << 28) | 0x00800090 | - (rd1 << 16) | (rd0 << 12) | (rm << 8) | rn); -} - -static inline void tcg_out_smull32(TCGContext *s, int cond, TCGReg rd0, - TCGReg rd1, TCGReg rn, TCGReg rm) -{ - /* if ArchVersion() < 6 && (dHi == n || dLo == n) then UNPREDICTABLE; */ - if (!use_armv6_instructions && (rd0 == rn || rd1 == rn)) { - if (rd0 == rm || rd1 == rm) { - tcg_out_mov_reg(s, cond, TCG_REG_TMP, rn); - rn = TCG_REG_TMP; - } else { - TCGReg t = rn; - rn = rm; - rm = t; - } - } - /* smull */ - tcg_out32(s, (cond << 28) | 0x00c00090 | - (rd1 << 16) | (rd0 << 12) | (rm << 8) | rn); -} - -static inline void tcg_out_sdiv(TCGContext *s, int cond, int rd, int rn, int rm) -{ - tcg_out32(s, 0x0710f010 | (cond << 28) | (rd << 16) | rn | (rm << 8)); -} - -static inline void tcg_out_udiv(TCGContext *s, int cond, int rd, int rn, int rm) -{ - tcg_out32(s, 0x0730f010 | (cond << 28) | (rd << 16) | rn | (rm << 8)); -} - -static inline void tcg_out_ext8s(TCGContext *s, int cond, - int rd, int rn) -{ - if (use_armv6_instructions) { - /* sxtb */ - tcg_out32(s, 0x06af0070 | (cond << 28) | (rd << 12) | rn); - } else { - tcg_out_dat_reg(s, cond, ARITH_MOV, - rd, 0, rn, SHIFT_IMM_LSL(24)); - tcg_out_dat_reg(s, cond, ARITH_MOV, - rd, 0, rd, SHIFT_IMM_ASR(24)); - } -} - -static inline void tcg_out_ext8u(TCGContext *s, int cond, - int rd, int rn) -{ - tcg_out_dat_imm(s, cond, ARITH_AND, rd, rn, 0xff); -} - -static inline void tcg_out_ext16s(TCGContext *s, int cond, - int rd, int rn) -{ - if (use_armv6_instructions) { - /* sxth */ - tcg_out32(s, 0x06bf0070 | (cond << 28) | (rd << 12) | rn); - } else { - tcg_out_dat_reg(s, cond, ARITH_MOV, - rd, 0, rn, SHIFT_IMM_LSL(16)); - tcg_out_dat_reg(s, cond, ARITH_MOV, - rd, 0, rd, SHIFT_IMM_ASR(16)); - } -} - -static inline void tcg_out_ext16u(TCGContext *s, int cond, - int rd, int rn) -{ - if (use_armv6_instructions) { - /* uxth */ - tcg_out32(s, 0x06ff0070 | (cond << 28) | (rd << 12) | rn); - } else { - tcg_out_dat_reg(s, cond, ARITH_MOV, - rd, 0, rn, SHIFT_IMM_LSL(16)); - tcg_out_dat_reg(s, cond, ARITH_MOV, - rd, 0, rd, SHIFT_IMM_LSR(16)); - } -} - -static inline void tcg_out_bswap16s(TCGContext *s, int cond, int rd, int rn) -{ - if (use_armv6_instructions) { - /* revsh */ - tcg_out32(s, 0x06ff0fb0 | (cond << 28) | (rd << 12) | rn); - } else { - tcg_out_dat_reg(s, cond, ARITH_MOV, - TCG_REG_TMP, 0, rn, SHIFT_IMM_LSL(24)); - tcg_out_dat_reg(s, cond, ARITH_MOV, - TCG_REG_TMP, 0, TCG_REG_TMP, SHIFT_IMM_ASR(16)); - tcg_out_dat_reg(s, cond, ARITH_ORR, - rd, TCG_REG_TMP, rn, SHIFT_IMM_LSR(8)); - } -} - -static inline void tcg_out_bswap16(TCGContext *s, int cond, int rd, int rn) -{ - if (use_armv6_instructions) { - /* rev16 */ - tcg_out32(s, 0x06bf0fb0 | (cond << 28) | (rd << 12) | rn); - } else { - tcg_out_dat_reg(s, cond, ARITH_MOV, - TCG_REG_TMP, 0, rn, SHIFT_IMM_LSL(24)); - tcg_out_dat_reg(s, cond, ARITH_MOV, - TCG_REG_TMP, 0, TCG_REG_TMP, SHIFT_IMM_LSR(16)); - tcg_out_dat_reg(s, cond, ARITH_ORR, - rd, TCG_REG_TMP, rn, SHIFT_IMM_LSR(8)); - } -} - -/* swap the two low bytes assuming that the two high input bytes and the - two high output bit can hold any value. */ -static inline void tcg_out_bswap16st(TCGContext *s, int cond, int rd, int rn) -{ - if (use_armv6_instructions) { - /* rev16 */ - tcg_out32(s, 0x06bf0fb0 | (cond << 28) | (rd << 12) | rn); - } else { - tcg_out_dat_reg(s, cond, ARITH_MOV, - TCG_REG_TMP, 0, rn, SHIFT_IMM_LSR(8)); - tcg_out_dat_imm(s, cond, ARITH_AND, TCG_REG_TMP, TCG_REG_TMP, 0xff); - tcg_out_dat_reg(s, cond, ARITH_ORR, - rd, TCG_REG_TMP, rn, SHIFT_IMM_LSL(8)); - } -} - -static inline void tcg_out_bswap32(TCGContext *s, int cond, int rd, int rn) -{ - if (use_armv6_instructions) { - /* rev */ - tcg_out32(s, 0x06bf0f30 | (cond << 28) | (rd << 12) | rn); - } else { - tcg_out_dat_reg(s, cond, ARITH_EOR, - TCG_REG_TMP, rn, rn, SHIFT_IMM_ROR(16)); - tcg_out_dat_imm(s, cond, ARITH_BIC, - TCG_REG_TMP, TCG_REG_TMP, 0xff | 0x800); - tcg_out_dat_reg(s, cond, ARITH_MOV, - rd, 0, rn, SHIFT_IMM_ROR(8)); - tcg_out_dat_reg(s, cond, ARITH_EOR, - rd, rd, TCG_REG_TMP, SHIFT_IMM_LSR(8)); - } -} - -bool tcg_target_deposit_valid(int ofs, int len) -{ - /* ??? Without bfi, we could improve over generic code by combining - the right-shift from a non-zero ofs with the orr. We do run into - problems when rd == rs, and the mask generated from ofs+len doesn't - fit into an immediate. We would have to be careful not to pessimize - wrt the optimizations performed on the expanded code. */ - return use_armv7_instructions; -} - -static inline void tcg_out_deposit(TCGContext *s, int cond, TCGReg rd, - TCGArg a1, int ofs, int len, bool const_a1) -{ - if (const_a1) { - /* bfi becomes bfc with rn == 15. */ - a1 = 15; - } - /* bfi/bfc */ - tcg_out32(s, 0x07c00010 | (cond << 28) | (rd << 12) | a1 - | (ofs << 7) | ((ofs + len - 1) << 16)); -} - -/* Note that this routine is used for both LDR and LDRH formats, so we do - not wish to include an immediate shift at this point. */ -static void tcg_out_memop_r(TCGContext *s, int cond, ARMInsn opc, TCGReg rt, - TCGReg rn, TCGReg rm, bool u, bool p, bool w) -{ - tcg_out32(s, (cond << 28) | opc | (u << 23) | (p << 24) - | (w << 21) | (rn << 16) | (rt << 12) | rm); -} - -static void tcg_out_memop_8(TCGContext *s, int cond, ARMInsn opc, TCGReg rt, - TCGReg rn, int imm8, bool p, bool w) -{ - bool u = 1; - if (imm8 < 0) { - imm8 = -imm8; - u = 0; - } - tcg_out32(s, (cond << 28) | opc | (u << 23) | (p << 24) | (w << 21) | - (rn << 16) | (rt << 12) | ((imm8 & 0xf0) << 4) | (imm8 & 0xf)); -} - -static void tcg_out_memop_12(TCGContext *s, int cond, ARMInsn opc, TCGReg rt, - TCGReg rn, int imm12, bool p, bool w) -{ - bool u = 1; - if (imm12 < 0) { - imm12 = -imm12; - u = 0; - } - tcg_out32(s, (cond << 28) | opc | (u << 23) | (p << 24) | (w << 21) | - (rn << 16) | (rt << 12) | imm12); -} - -static inline void tcg_out_ld32_12(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, int imm12) -{ - tcg_out_memop_12(s, cond, INSN_LDR_IMM, rt, rn, imm12, 1, 0); -} - -static inline void tcg_out_st32_12(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, int imm12) -{ - tcg_out_memop_12(s, cond, INSN_STR_IMM, rt, rn, imm12, 1, 0); -} - -static inline void tcg_out_ld32_r(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, TCGReg rm) -{ - tcg_out_memop_r(s, cond, INSN_LDR_REG, rt, rn, rm, 1, 1, 0); -} - -static inline void tcg_out_st32_r(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, TCGReg rm) -{ - tcg_out_memop_r(s, cond, INSN_STR_REG, rt, rn, rm, 1, 1, 0); -} - -static inline void tcg_out_ldrd_8(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, int imm8) -{ - tcg_out_memop_8(s, cond, INSN_LDRD_IMM, rt, rn, imm8, 1, 0); -} - -static inline void tcg_out_ldrd_r(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, TCGReg rm) -{ - tcg_out_memop_r(s, cond, INSN_LDRD_REG, rt, rn, rm, 1, 1, 0); -} - -static inline void tcg_out_strd_8(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, int imm8) -{ - tcg_out_memop_8(s, cond, INSN_STRD_IMM, rt, rn, imm8, 1, 0); -} - -static inline void tcg_out_strd_r(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, TCGReg rm) -{ - tcg_out_memop_r(s, cond, INSN_STRD_REG, rt, rn, rm, 1, 1, 0); -} - -/* Register pre-increment with base writeback. */ -static inline void tcg_out_ld32_rwb(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, TCGReg rm) -{ - tcg_out_memop_r(s, cond, INSN_LDR_REG, rt, rn, rm, 1, 1, 1); -} - -static inline void tcg_out_st32_rwb(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, TCGReg rm) -{ - tcg_out_memop_r(s, cond, INSN_STR_REG, rt, rn, rm, 1, 1, 1); -} - -static inline void tcg_out_ld16u_8(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, int imm8) -{ - tcg_out_memop_8(s, cond, INSN_LDRH_IMM, rt, rn, imm8, 1, 0); -} - -static inline void tcg_out_st16_8(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, int imm8) -{ - tcg_out_memop_8(s, cond, INSN_STRH_IMM, rt, rn, imm8, 1, 0); -} - -static inline void tcg_out_ld16u_r(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, TCGReg rm) -{ - tcg_out_memop_r(s, cond, INSN_LDRH_REG, rt, rn, rm, 1, 1, 0); -} - -static inline void tcg_out_st16_r(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, TCGReg rm) -{ - tcg_out_memop_r(s, cond, INSN_STRH_REG, rt, rn, rm, 1, 1, 0); -} - -static inline void tcg_out_ld16s_8(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, int imm8) -{ - tcg_out_memop_8(s, cond, INSN_LDRSH_IMM, rt, rn, imm8, 1, 0); -} - -static inline void tcg_out_ld16s_r(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, TCGReg rm) -{ - tcg_out_memop_r(s, cond, INSN_LDRSH_REG, rt, rn, rm, 1, 1, 0); -} - -static inline void tcg_out_ld8_12(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, int imm12) -{ - tcg_out_memop_12(s, cond, INSN_LDRB_IMM, rt, rn, imm12, 1, 0); -} - -static inline void tcg_out_st8_12(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, int imm12) -{ - tcg_out_memop_12(s, cond, INSN_STRB_IMM, rt, rn, imm12, 1, 0); -} - -static inline void tcg_out_ld8_r(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, TCGReg rm) -{ - tcg_out_memop_r(s, cond, INSN_LDRB_REG, rt, rn, rm, 1, 1, 0); -} - -static inline void tcg_out_st8_r(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, TCGReg rm) -{ - tcg_out_memop_r(s, cond, INSN_STRB_REG, rt, rn, rm, 1, 1, 0); -} - -static inline void tcg_out_ld8s_8(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, int imm8) -{ - tcg_out_memop_8(s, cond, INSN_LDRSB_IMM, rt, rn, imm8, 1, 0); -} - -static inline void tcg_out_ld8s_r(TCGContext *s, int cond, TCGReg rt, - TCGReg rn, TCGReg rm) -{ - tcg_out_memop_r(s, cond, INSN_LDRSB_REG, rt, rn, rm, 1, 1, 0); -} - -static inline void tcg_out_ld32u(TCGContext *s, int cond, - int rd, int rn, int32_t offset) -{ - if (offset > 0xfff || offset < -0xfff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_ld32_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_ld32_12(s, cond, rd, rn, offset); -} - -static inline void tcg_out_st32(TCGContext *s, int cond, - int rd, int rn, int32_t offset) -{ - if (offset > 0xfff || offset < -0xfff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_st32_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_st32_12(s, cond, rd, rn, offset); -} - -static inline void tcg_out_ld16u(TCGContext *s, int cond, - int rd, int rn, int32_t offset) -{ - if (offset > 0xff || offset < -0xff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_ld16u_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_ld16u_8(s, cond, rd, rn, offset); -} - -static inline void tcg_out_ld16s(TCGContext *s, int cond, - int rd, int rn, int32_t offset) -{ - if (offset > 0xff || offset < -0xff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_ld16s_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_ld16s_8(s, cond, rd, rn, offset); -} - -static inline void tcg_out_st16(TCGContext *s, int cond, - int rd, int rn, int32_t offset) -{ - if (offset > 0xff || offset < -0xff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_st16_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_st16_8(s, cond, rd, rn, offset); -} - -static inline void tcg_out_ld8u(TCGContext *s, int cond, - int rd, int rn, int32_t offset) -{ - if (offset > 0xfff || offset < -0xfff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_ld8_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_ld8_12(s, cond, rd, rn, offset); -} - -static inline void tcg_out_ld8s(TCGContext *s, int cond, - int rd, int rn, int32_t offset) -{ - if (offset > 0xff || offset < -0xff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_ld8s_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_ld8s_8(s, cond, rd, rn, offset); -} - -static inline void tcg_out_st8(TCGContext *s, int cond, - int rd, int rn, int32_t offset) -{ - if (offset > 0xfff || offset < -0xfff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_st8_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_st8_12(s, cond, rd, rn, offset); -} - -/* The _goto case is normally between TBs within the same code buffer, and - * with the code buffer limited to 16MB we wouldn't need the long case. - * But we also use it for the tail-call to the qemu_ld/st helpers, which does. - */ -static inline void tcg_out_goto(TCGContext *s, int cond, tcg_insn_unit *addr) -{ - intptr_t addri = (intptr_t)addr; - ptrdiff_t disp = tcg_pcrel_diff(s, addr); - - if ((addri & 1) == 0 && disp - 8 < 0x01fffffd && disp - 8 > -0x01fffffd) { - tcg_out_b(s, cond, disp); - return; - } - - tcg_out_movi32(s, cond, TCG_REG_TMP, addri); - if (use_armv5t_instructions) { - tcg_out_bx(s, cond, TCG_REG_TMP); - } else { - if (addri & 1) { - tcg_abort(); - } - tcg_out_mov_reg(s, cond, TCG_REG_PC, TCG_REG_TMP); - } -} - -/* The call case is mostly used for helpers - so it's not unreasonable - * for them to be beyond branch range */ -static void tcg_out_call(TCGContext *s, tcg_insn_unit *addr) -{ - intptr_t addri = (intptr_t)addr; - ptrdiff_t disp = tcg_pcrel_diff(s, addr); - - if (disp - 8 < 0x02000000 && disp - 8 >= -0x02000000) { - if (addri & 1) { - /* Use BLX if the target is in Thumb mode */ - if (!use_armv5t_instructions) { - tcg_abort(); - } - tcg_out_blx_imm(s, disp); - } else { - tcg_out_bl(s, COND_AL, disp); - } - } else if (use_armv7_instructions) { - tcg_out_movi32(s, COND_AL, TCG_REG_TMP, addri); - tcg_out_blx(s, COND_AL, TCG_REG_TMP); - } else { - tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R14, TCG_REG_PC, 4); - tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4); - tcg_out32(s, addri); - } -} - -static inline void tcg_out_goto_label(TCGContext *s, int cond, TCGLabel *l) -{ - if (l->has_value) { - tcg_out_goto(s, cond, l->u.value_ptr); - } else { - tcg_out_reloc(s, s->code_ptr, R_ARM_PC24, l, 0); - tcg_out_b_noaddr(s, cond); - } -} - -#ifdef CONFIG_SOFTMMU -/* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, - * int mmu_idx, uintptr_t ra) - */ -static void * const qemu_ld_helpers[16] = { - [MO_UB] = helper_ret_ldub_mmu, - [MO_SB] = helper_ret_ldsb_mmu, - - [MO_LEUW] = helper_le_lduw_mmu, - [MO_LEUL] = helper_le_ldul_mmu, - [MO_LEQ] = helper_le_ldq_mmu, - [MO_LESW] = helper_le_ldsw_mmu, - [MO_LESL] = helper_le_ldul_mmu, - - [MO_BEUW] = helper_be_lduw_mmu, - [MO_BEUL] = helper_be_ldul_mmu, - [MO_BEQ] = helper_be_ldq_mmu, - [MO_BESW] = helper_be_ldsw_mmu, - [MO_BESL] = helper_be_ldul_mmu, -}; - -/* helper signature: helper_ret_st_mmu(CPUState *env, target_ulong addr, - * uintxx_t val, int mmu_idx, uintptr_t ra) - */ -static void * const qemu_st_helpers[16] = { - [MO_UB] = helper_ret_stb_mmu, - [MO_LEUW] = helper_le_stw_mmu, - [MO_LEUL] = helper_le_stl_mmu, - [MO_LEQ] = helper_le_stq_mmu, - [MO_BEUW] = helper_be_stw_mmu, - [MO_BEUL] = helper_be_stl_mmu, - [MO_BEQ] = helper_be_stq_mmu, -}; - -/* Helper routines for marshalling helper function arguments into - * the correct registers and stack. - * argreg is where we want to put this argument, arg is the argument itself. - * Return value is the updated argreg ready for the next call. - * Note that argreg 0..3 is real registers, 4+ on stack. - * - * We provide routines for arguments which are: immediate, 32 bit - * value in register, 16 and 8 bit values in register (which must be zero - * extended before use) and 64 bit value in a lo:hi register pair. - */ -#define DEFINE_TCG_OUT_ARG(NAME, ARGTYPE, MOV_ARG, EXT_ARG) \ -static TCGReg NAME(TCGContext *s, TCGReg argreg, ARGTYPE arg) \ -{ \ - if (argreg < 4) { \ - MOV_ARG(s, COND_AL, argreg, arg); \ - } else { \ - int ofs = (argreg - 4) * 4; \ - EXT_ARG; \ - assert(ofs + 4 <= TCG_STATIC_CALL_ARGS_SIZE); \ - tcg_out_st32_12(s, COND_AL, arg, TCG_REG_CALL_STACK, ofs); \ - } \ - return argreg + 1; \ -} - -DEFINE_TCG_OUT_ARG(tcg_out_arg_imm32, uint32_t, tcg_out_movi32, - (tcg_out_movi32(s, COND_AL, TCG_REG_TMP, arg), arg = TCG_REG_TMP)) -DEFINE_TCG_OUT_ARG(tcg_out_arg_reg8, TCGReg, tcg_out_ext8u, - (tcg_out_ext8u(s, COND_AL, TCG_REG_TMP, arg), arg = TCG_REG_TMP)) -DEFINE_TCG_OUT_ARG(tcg_out_arg_reg16, TCGReg, tcg_out_ext16u, - (tcg_out_ext16u(s, COND_AL, TCG_REG_TMP, arg), arg = TCG_REG_TMP)) -DEFINE_TCG_OUT_ARG(tcg_out_arg_reg32, TCGReg, tcg_out_mov_reg, ) - -static TCGReg tcg_out_arg_reg64(TCGContext *s, TCGReg argreg, - TCGReg arglo, TCGReg arghi) -{ - /* 64 bit arguments must go in even/odd register pairs - * and in 8-aligned stack slots. - */ - if (argreg & 1) { - argreg++; - } - if (use_armv6_instructions && argreg >= 4 - && (arglo & 1) == 0 && arghi == arglo + 1) { - tcg_out_strd_8(s, COND_AL, arglo, - TCG_REG_CALL_STACK, (argreg - 4) * 4); - return argreg + 2; - } else { - argreg = tcg_out_arg_reg32(s, argreg, arglo); - argreg = tcg_out_arg_reg32(s, argreg, arghi); - return argreg; - } -} - -#define TLB_SHIFT (CPU_TLB_ENTRY_BITS + CPU_TLB_BITS) - -/* We're expecting to use an 8-bit immediate and to mask. */ -QEMU_BUILD_BUG_ON(CPU_TLB_BITS > 8); - -/* We're expecting to use an 8-bit immediate add + 8-bit ldrd offset. - Using the offset of the second entry in the last tlb table ensures - that we can index all of the elements of the first entry. */ -QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table[NB_MMU_MODES - 1][1]) - > 0xffff); - -/* Load and compare a TLB entry, leaving the flags set. Returns the register - containing the addend of the tlb entry. Clobbers R0, R1, R2, TMP. */ - -static TCGReg tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi, - TCGMemOp s_bits, int mem_index, bool is_load) -{ - TCGReg base = TCG_AREG0; - int cmp_off = - (is_load - ? offsetof(CPUArchState, tlb_table[mem_index][0].addr_read) - : offsetof(CPUArchState, tlb_table[mem_index][0].addr_write)); - int add_off = offsetof(CPUArchState, tlb_table[mem_index][0].addend); - - /* Should generate something like the following: - * shr tmp, addrlo, #TARGET_PAGE_BITS (1) - * add r2, env, #high - * and r0, tmp, #(CPU_TLB_SIZE - 1) (2) - * add r2, r2, r0, lsl #CPU_TLB_ENTRY_BITS (3) - * ldr r0, [r2, #cmp] (4) - * tst addrlo, #s_mask - * ldr r2, [r2, #add] (5) - * cmpeq r0, tmp, lsl #TARGET_PAGE_BITS - */ - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, TCG_REG_TMP, - 0, addrlo, SHIFT_IMM_LSR(TARGET_PAGE_BITS)); - - /* We checked that the offset is contained within 16 bits above. */ - if (add_off > 0xfff || (use_armv6_instructions && cmp_off > 0xff)) { - tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R2, base, - (24 << 7) | (cmp_off >> 8)); - base = TCG_REG_R2; - add_off -= cmp_off & 0xff00; - cmp_off &= 0xff; - } - - tcg_out_dat_imm(s, COND_AL, ARITH_AND, - TCG_REG_R0, TCG_REG_TMP, CPU_TLB_SIZE - 1); - tcg_out_dat_reg(s, COND_AL, ARITH_ADD, TCG_REG_R2, base, - TCG_REG_R0, SHIFT_IMM_LSL(CPU_TLB_ENTRY_BITS)); - - /* Load the tlb comparator. Use ldrd if needed and available, - but due to how the pointer needs setting up, ldm isn't useful. - Base arm5 doesn't have ldrd, but armv5te does. */ - if (use_armv6_instructions && TARGET_LONG_BITS == 64) { - tcg_out_ldrd_8(s, COND_AL, TCG_REG_R0, TCG_REG_R2, cmp_off); - } else { - tcg_out_ld32_12(s, COND_AL, TCG_REG_R0, TCG_REG_R2, cmp_off); - if (TARGET_LONG_BITS == 64) { - tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R2, cmp_off + 4); - } - } - - /* Check alignment. */ - if (s_bits) { - tcg_out_dat_imm(s, COND_AL, ARITH_TST, - 0, addrlo, (1 << s_bits) - 1); - } - - /* Load the tlb addend. */ - tcg_out_ld32_12(s, COND_AL, TCG_REG_R2, TCG_REG_R2, add_off); - - tcg_out_dat_reg(s, (s_bits ? COND_EQ : COND_AL), ARITH_CMP, 0, - TCG_REG_R0, TCG_REG_TMP, SHIFT_IMM_LSL(TARGET_PAGE_BITS)); - - if (TARGET_LONG_BITS == 64) { - tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0, - TCG_REG_R1, addrhi, SHIFT_IMM_LSL(0)); - } - - return TCG_REG_R2; -} - -/* Record the context of a call to the out of line helper code for the slow - path for a load or store, so that we can later generate the correct - helper code. */ -static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOpIdx oi, - TCGReg datalo, TCGReg datahi, TCGReg addrlo, - TCGReg addrhi, tcg_insn_unit *raddr, - tcg_insn_unit *label_ptr) -{ - TCGLabelQemuLdst *label = new_ldst_label(s); - - label->is_ld = is_ld; - label->oi = oi; - label->datalo_reg = datalo; - label->datahi_reg = datahi; - label->addrlo_reg = addrlo; - label->addrhi_reg = addrhi; - label->raddr = raddr; - label->label_ptr[0] = label_ptr; -} - -static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) -{ - TCGReg argreg, datalo, datahi; - TCGMemOpIdx oi = lb->oi; - TCGMemOp opc = get_memop(oi); - void *func; - - reloc_pc24(lb->label_ptr[0], s->code_ptr); - - argreg = tcg_out_arg_reg32(s, TCG_REG_R0, TCG_AREG0); - if (TARGET_LONG_BITS == 64) { - argreg = tcg_out_arg_reg64(s, argreg, lb->addrlo_reg, lb->addrhi_reg); - } else { - argreg = tcg_out_arg_reg32(s, argreg, lb->addrlo_reg); - } - argreg = tcg_out_arg_imm32(s, argreg, oi); - argreg = tcg_out_arg_reg32(s, argreg, TCG_REG_R14); - - /* For armv6 we can use the canonical unsigned helpers and minimize - icache usage. For pre-armv6, use the signed helpers since we do - not have a single insn sign-extend. */ - if (use_armv6_instructions) { - func = qemu_ld_helpers[opc & (MO_BSWAP | MO_SIZE)]; - } else { - func = qemu_ld_helpers[opc & (MO_BSWAP | MO_SSIZE)]; - if (opc & MO_SIGN) { - opc = MO_UL; - } - } - tcg_out_call(s, func); - - datalo = lb->datalo_reg; - datahi = lb->datahi_reg; - switch (opc & MO_SSIZE) { - case MO_SB: - tcg_out_ext8s(s, COND_AL, datalo, TCG_REG_R0); - break; - case MO_SW: - tcg_out_ext16s(s, COND_AL, datalo, TCG_REG_R0); - break; - default: - tcg_out_mov_reg(s, COND_AL, datalo, TCG_REG_R0); - break; - case MO_Q: - if (datalo != TCG_REG_R1) { - tcg_out_mov_reg(s, COND_AL, datalo, TCG_REG_R0); - tcg_out_mov_reg(s, COND_AL, datahi, TCG_REG_R1); - } else if (datahi != TCG_REG_R0) { - tcg_out_mov_reg(s, COND_AL, datahi, TCG_REG_R1); - tcg_out_mov_reg(s, COND_AL, datalo, TCG_REG_R0); - } else { - tcg_out_mov_reg(s, COND_AL, TCG_REG_TMP, TCG_REG_R0); - tcg_out_mov_reg(s, COND_AL, datahi, TCG_REG_R1); - tcg_out_mov_reg(s, COND_AL, datalo, TCG_REG_TMP); - } - break; - } - - tcg_out_goto(s, COND_AL, lb->raddr); -} - -static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) -{ - TCGReg argreg, datalo, datahi; - TCGMemOpIdx oi = lb->oi; - TCGMemOp opc = get_memop(oi); - - reloc_pc24(lb->label_ptr[0], s->code_ptr); - - argreg = TCG_REG_R0; - argreg = tcg_out_arg_reg32(s, argreg, TCG_AREG0); - if (TARGET_LONG_BITS == 64) { - argreg = tcg_out_arg_reg64(s, argreg, lb->addrlo_reg, lb->addrhi_reg); - } else { - argreg = tcg_out_arg_reg32(s, argreg, lb->addrlo_reg); - } - - datalo = lb->datalo_reg; - datahi = lb->datahi_reg; - switch (opc & MO_SIZE) { - case MO_8: - argreg = tcg_out_arg_reg8(s, argreg, datalo); - break; - case MO_16: - argreg = tcg_out_arg_reg16(s, argreg, datalo); - break; - case MO_32: - default: - argreg = tcg_out_arg_reg32(s, argreg, datalo); - break; - case MO_64: - argreg = tcg_out_arg_reg64(s, argreg, datalo, datahi); - break; - } - - argreg = tcg_out_arg_imm32(s, argreg, oi); - argreg = tcg_out_arg_reg32(s, argreg, TCG_REG_R14); - - /* Tail-call to the helper, which will return to the fast path. */ - tcg_out_goto(s, COND_AL, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)]); -} -#endif /* SOFTMMU */ - -static inline void tcg_out_qemu_ld_index(TCGContext *s, TCGMemOp opc, - TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addend) -{ - TCGMemOp bswap = opc & MO_BSWAP; - - switch (opc & MO_SSIZE) { - case MO_UB: - tcg_out_ld8_r(s, COND_AL, datalo, addrlo, addend); - break; - case MO_SB: - tcg_out_ld8s_r(s, COND_AL, datalo, addrlo, addend); - break; - case MO_UW: - tcg_out_ld16u_r(s, COND_AL, datalo, addrlo, addend); - if (bswap) { - tcg_out_bswap16(s, COND_AL, datalo, datalo); - } - break; - case MO_SW: - if (bswap) { - tcg_out_ld16u_r(s, COND_AL, datalo, addrlo, addend); - tcg_out_bswap16s(s, COND_AL, datalo, datalo); - } else { - tcg_out_ld16s_r(s, COND_AL, datalo, addrlo, addend); - } - break; - case MO_UL: - default: - tcg_out_ld32_r(s, COND_AL, datalo, addrlo, addend); - if (bswap) { - tcg_out_bswap32(s, COND_AL, datalo, datalo); - } - break; - case MO_Q: - { - TCGReg dl = (bswap ? datahi : datalo); - TCGReg dh = (bswap ? datalo : datahi); - - /* Avoid ldrd for user-only emulation, to handle unaligned. */ - if (USING_SOFTMMU && use_armv6_instructions - && (dl & 1) == 0 && dh == dl + 1) { - tcg_out_ldrd_r(s, COND_AL, dl, addrlo, addend); - } else if (dl != addend) { - tcg_out_ld32_rwb(s, COND_AL, dl, addend, addrlo); - tcg_out_ld32_12(s, COND_AL, dh, addend, 4); - } else { - tcg_out_dat_reg(s, COND_AL, ARITH_ADD, TCG_REG_TMP, - addend, addrlo, SHIFT_IMM_LSL(0)); - tcg_out_ld32_12(s, COND_AL, dl, TCG_REG_TMP, 0); - tcg_out_ld32_12(s, COND_AL, dh, TCG_REG_TMP, 4); - } - if (bswap) { - tcg_out_bswap32(s, COND_AL, dl, dl); - tcg_out_bswap32(s, COND_AL, dh, dh); - } - } - break; - } -} - -static inline void tcg_out_qemu_ld_direct(TCGContext *s, TCGMemOp opc, - TCGReg datalo, TCGReg datahi, - TCGReg addrlo) -{ - TCGMemOp bswap = opc & MO_BSWAP; - - switch (opc & MO_SSIZE) { - case MO_UB: - tcg_out_ld8_12(s, COND_AL, datalo, addrlo, 0); - break; - case MO_SB: - tcg_out_ld8s_8(s, COND_AL, datalo, addrlo, 0); - break; - case MO_UW: - tcg_out_ld16u_8(s, COND_AL, datalo, addrlo, 0); - if (bswap) { - tcg_out_bswap16(s, COND_AL, datalo, datalo); - } - break; - case MO_SW: - if (bswap) { - tcg_out_ld16u_8(s, COND_AL, datalo, addrlo, 0); - tcg_out_bswap16s(s, COND_AL, datalo, datalo); - } else { - tcg_out_ld16s_8(s, COND_AL, datalo, addrlo, 0); - } - break; - case MO_UL: - default: - tcg_out_ld32_12(s, COND_AL, datalo, addrlo, 0); - if (bswap) { - tcg_out_bswap32(s, COND_AL, datalo, datalo); - } - break; - case MO_Q: - { - TCGReg dl = (bswap ? datahi : datalo); - TCGReg dh = (bswap ? datalo : datahi); - - /* Avoid ldrd for user-only emulation, to handle unaligned. */ - if (USING_SOFTMMU && use_armv6_instructions - && (dl & 1) == 0 && dh == dl + 1) { - tcg_out_ldrd_8(s, COND_AL, dl, addrlo, 0); - } else if (dl == addrlo) { - tcg_out_ld32_12(s, COND_AL, dh, addrlo, bswap ? 0 : 4); - tcg_out_ld32_12(s, COND_AL, dl, addrlo, bswap ? 4 : 0); - } else { - tcg_out_ld32_12(s, COND_AL, dl, addrlo, bswap ? 4 : 0); - tcg_out_ld32_12(s, COND_AL, dh, addrlo, bswap ? 0 : 4); - } - if (bswap) { - tcg_out_bswap32(s, COND_AL, dl, dl); - tcg_out_bswap32(s, COND_AL, dh, dh); - } - } - break; - } -} - -static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is64) -{ - TCGReg addrlo, datalo, datahi, addrhi __attribute__((unused)); - TCGMemOpIdx oi; - TCGMemOp opc; -#ifdef CONFIG_SOFTMMU - int mem_index; - TCGReg addend; - tcg_insn_unit *label_ptr; -#endif - - datalo = *args++; - datahi = (is64 ? *args++ : 0); - addrlo = *args++; - addrhi = (TARGET_LONG_BITS == 64 ? *args++ : 0); - oi = *args++; - opc = get_memop(oi); - -#ifdef CONFIG_SOFTMMU - mem_index = get_mmuidx(oi); - addend = tcg_out_tlb_read(s, addrlo, addrhi, opc & MO_SIZE, mem_index, 1); - - /* This a conditional BL only to load a pointer within this opcode into LR - for the slow path. We will not be using the value for a tail call. */ - label_ptr = s->code_ptr; - tcg_out_bl_noaddr(s, COND_NE); - - tcg_out_qemu_ld_index(s, opc, datalo, datahi, addrlo, addend); - - add_qemu_ldst_label(s, true, oi, datalo, datahi, addrlo, addrhi, - s->code_ptr, label_ptr); -#else /* !CONFIG_SOFTMMU */ - if (GUEST_BASE) { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP, GUEST_BASE); - tcg_out_qemu_ld_index(s, opc, datalo, datahi, addrlo, TCG_REG_TMP); - } else { - tcg_out_qemu_ld_direct(s, opc, datalo, datahi, addrlo); - } -#endif -} - -static inline void tcg_out_qemu_st_index(TCGContext *s, int cond, TCGMemOp opc, - TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addend) -{ - TCGMemOp bswap = opc & MO_BSWAP; - - switch (opc & MO_SIZE) { - case MO_8: - tcg_out_st8_r(s, cond, datalo, addrlo, addend); - break; - case MO_16: - if (bswap) { - tcg_out_bswap16st(s, cond, TCG_REG_R0, datalo); - tcg_out_st16_r(s, cond, TCG_REG_R0, addrlo, addend); - } else { - tcg_out_st16_r(s, cond, datalo, addrlo, addend); - } - break; - case MO_32: - default: - if (bswap) { - tcg_out_bswap32(s, cond, TCG_REG_R0, datalo); - tcg_out_st32_r(s, cond, TCG_REG_R0, addrlo, addend); - } else { - tcg_out_st32_r(s, cond, datalo, addrlo, addend); - } - break; - case MO_64: - /* Avoid strd for user-only emulation, to handle unaligned. */ - if (bswap) { - tcg_out_bswap32(s, cond, TCG_REG_R0, datahi); - tcg_out_st32_rwb(s, cond, TCG_REG_R0, addend, addrlo); - tcg_out_bswap32(s, cond, TCG_REG_R0, datalo); - tcg_out_st32_12(s, cond, TCG_REG_R0, addend, 4); - } else if (USING_SOFTMMU && use_armv6_instructions - && (datalo & 1) == 0 && datahi == datalo + 1) { - tcg_out_strd_r(s, cond, datalo, addrlo, addend); - } else { - tcg_out_st32_rwb(s, cond, datalo, addend, addrlo); - tcg_out_st32_12(s, cond, datahi, addend, 4); - } - break; - } -} - -static inline void tcg_out_qemu_st_direct(TCGContext *s, TCGMemOp opc, - TCGReg datalo, TCGReg datahi, - TCGReg addrlo) -{ - TCGMemOp bswap = opc & MO_BSWAP; - - switch (opc & MO_SIZE) { - case MO_8: - tcg_out_st8_12(s, COND_AL, datalo, addrlo, 0); - break; - case MO_16: - if (bswap) { - tcg_out_bswap16st(s, COND_AL, TCG_REG_R0, datalo); - tcg_out_st16_8(s, COND_AL, TCG_REG_R0, addrlo, 0); - } else { - tcg_out_st16_8(s, COND_AL, datalo, addrlo, 0); - } - break; - case MO_32: - default: - if (bswap) { - tcg_out_bswap32(s, COND_AL, TCG_REG_R0, datalo); - tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addrlo, 0); - } else { - tcg_out_st32_12(s, COND_AL, datalo, addrlo, 0); - } - break; - case MO_64: - /* Avoid strd for user-only emulation, to handle unaligned. */ - if (bswap) { - tcg_out_bswap32(s, COND_AL, TCG_REG_R0, datahi); - tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addrlo, 0); - tcg_out_bswap32(s, COND_AL, TCG_REG_R0, datalo); - tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addrlo, 4); - } else if (USING_SOFTMMU && use_armv6_instructions - && (datalo & 1) == 0 && datahi == datalo + 1) { - tcg_out_strd_8(s, COND_AL, datalo, addrlo, 0); - } else { - tcg_out_st32_12(s, COND_AL, datalo, addrlo, 0); - tcg_out_st32_12(s, COND_AL, datahi, addrlo, 4); - } - break; - } -} - -static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64) -{ - TCGReg addrlo, datalo, datahi, addrhi __attribute__((unused)); - TCGMemOpIdx oi; - TCGMemOp opc; -#ifdef CONFIG_SOFTMMU - int mem_index; - TCGReg addend; - tcg_insn_unit *label_ptr; -#endif - - datalo = *args++; - datahi = (is64 ? *args++ : 0); - addrlo = *args++; - addrhi = (TARGET_LONG_BITS == 64 ? *args++ : 0); - oi = *args++; - opc = get_memop(oi); - -#ifdef CONFIG_SOFTMMU - mem_index = get_mmuidx(oi); - addend = tcg_out_tlb_read(s, addrlo, addrhi, opc & MO_SIZE, mem_index, 0); - - tcg_out_qemu_st_index(s, COND_EQ, opc, datalo, datahi, addrlo, addend); - - /* The conditional call must come last, as we're going to return here. */ - label_ptr = s->code_ptr; - tcg_out_bl_noaddr(s, COND_NE); - - add_qemu_ldst_label(s, false, oi, datalo, datahi, addrlo, addrhi, - s->code_ptr, label_ptr); -#else /* !CONFIG_SOFTMMU */ - if (GUEST_BASE) { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP, GUEST_BASE); - tcg_out_qemu_st_index(s, COND_AL, opc, datalo, - datahi, addrlo, TCG_REG_TMP); - } else { - tcg_out_qemu_st_direct(s, opc, datalo, datahi, addrlo); - } -#endif -} - -static tcg_insn_unit *tb_ret_addr; - -static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, - const TCGArg *args, const int *const_args) -{ - TCGArg a0, a1, a2, a3, a4, a5; - int c; - - switch (opc) { - case INDEX_op_exit_tb: - tcg_out_movi32(s, COND_AL, TCG_REG_R0, args[0]); - tcg_out_goto(s, COND_AL, tb_ret_addr); - break; - case INDEX_op_goto_tb: - if (s->tb_jmp_offset) { - /* Direct jump method */ - s->tb_jmp_offset[args[0]] = tcg_current_code_size(s); - tcg_out_b_noaddr(s, COND_AL); - } else { - /* Indirect jump method */ - intptr_t ptr = (intptr_t)(s->tb_next + args[0]); - tcg_out_movi32(s, COND_AL, TCG_REG_R0, ptr & ~0xfff); - tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_R0, ptr & 0xfff); - } - s->tb_next_offset[args[0]] = tcg_current_code_size(s); - break; - case INDEX_op_br: - tcg_out_goto_label(s, COND_AL, arg_label(args[0])); - break; - - case INDEX_op_ld8u_i32: - tcg_out_ld8u(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_ld8s_i32: - tcg_out_ld8s(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_ld16u_i32: - tcg_out_ld16u(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_ld16s_i32: - tcg_out_ld16s(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_ld_i32: - tcg_out_ld32u(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_st8_i32: - tcg_out_st8(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_st16_i32: - tcg_out_st16(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_st_i32: - tcg_out_st32(s, COND_AL, args[0], args[1], args[2]); - break; - - case INDEX_op_movcond_i32: - /* Constraints mean that v2 is always in the same register as dest, - * so we only need to do "if condition passed, move v1 to dest". - */ - tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, - args[1], args[2], const_args[2]); - tcg_out_dat_rIK(s, tcg_cond_to_arm_cond[args[5]], ARITH_MOV, - ARITH_MVN, args[0], 0, args[3], const_args[3]); - break; - case INDEX_op_add_i32: - tcg_out_dat_rIN(s, COND_AL, ARITH_ADD, ARITH_SUB, - args[0], args[1], args[2], const_args[2]); - break; - case INDEX_op_sub_i32: - if (const_args[1]) { - if (const_args[2]) { - tcg_out_movi32(s, COND_AL, args[0], args[1] - args[2]); - } else { - tcg_out_dat_rI(s, COND_AL, ARITH_RSB, - args[0], args[2], args[1], 1); - } - } else { - tcg_out_dat_rIN(s, COND_AL, ARITH_SUB, ARITH_ADD, - args[0], args[1], args[2], const_args[2]); - } - break; - case INDEX_op_and_i32: - tcg_out_dat_rIK(s, COND_AL, ARITH_AND, ARITH_BIC, - args[0], args[1], args[2], const_args[2]); - break; - case INDEX_op_andc_i32: - tcg_out_dat_rIK(s, COND_AL, ARITH_BIC, ARITH_AND, - args[0], args[1], args[2], const_args[2]); - break; - case INDEX_op_or_i32: - c = ARITH_ORR; - goto gen_arith; - case INDEX_op_xor_i32: - c = ARITH_EOR; - /* Fall through. */ - gen_arith: - tcg_out_dat_rI(s, COND_AL, c, args[0], args[1], args[2], const_args[2]); - break; - case INDEX_op_add2_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - a3 = args[3], a4 = args[4], a5 = args[5]; - if (a0 == a3 || (a0 == a5 && !const_args[5])) { - a0 = TCG_REG_TMP; - } - tcg_out_dat_rIN(s, COND_AL, ARITH_ADD | TO_CPSR, ARITH_SUB | TO_CPSR, - a0, a2, a4, const_args[4]); - tcg_out_dat_rIK(s, COND_AL, ARITH_ADC, ARITH_SBC, - a1, a3, a5, const_args[5]); - tcg_out_mov_reg(s, COND_AL, args[0], a0); - break; - case INDEX_op_sub2_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - a3 = args[3], a4 = args[4], a5 = args[5]; - if ((a0 == a3 && !const_args[3]) || (a0 == a5 && !const_args[5])) { - a0 = TCG_REG_TMP; - } - if (const_args[2]) { - if (const_args[4]) { - tcg_out_movi32(s, COND_AL, a0, a4); - a4 = a0; - } - tcg_out_dat_rI(s, COND_AL, ARITH_RSB | TO_CPSR, a0, a4, a2, 1); - } else { - tcg_out_dat_rIN(s, COND_AL, ARITH_SUB | TO_CPSR, - ARITH_ADD | TO_CPSR, a0, a2, a4, const_args[4]); - } - if (const_args[3]) { - if (const_args[5]) { - tcg_out_movi32(s, COND_AL, a1, a5); - a5 = a1; - } - tcg_out_dat_rI(s, COND_AL, ARITH_RSC, a1, a5, a3, 1); - } else { - tcg_out_dat_rIK(s, COND_AL, ARITH_SBC, ARITH_ADC, - a1, a3, a5, const_args[5]); - } - tcg_out_mov_reg(s, COND_AL, args[0], a0); - break; - case INDEX_op_neg_i32: - tcg_out_dat_imm(s, COND_AL, ARITH_RSB, args[0], args[1], 0); - break; - case INDEX_op_not_i32: - tcg_out_dat_reg(s, COND_AL, - ARITH_MVN, args[0], 0, args[1], SHIFT_IMM_LSL(0)); - break; - case INDEX_op_mul_i32: - tcg_out_mul32(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_mulu2_i32: - tcg_out_umull32(s, COND_AL, args[0], args[1], args[2], args[3]); - break; - case INDEX_op_muls2_i32: - tcg_out_smull32(s, COND_AL, args[0], args[1], args[2], args[3]); - break; - /* XXX: Perhaps args[2] & 0x1f is wrong */ - case INDEX_op_shl_i32: - c = const_args[2] ? - SHIFT_IMM_LSL(args[2] & 0x1f) : SHIFT_REG_LSL(args[2]); - goto gen_shift32; - case INDEX_op_shr_i32: - c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_LSR(args[2] & 0x1f) : - SHIFT_IMM_LSL(0) : SHIFT_REG_LSR(args[2]); - goto gen_shift32; - case INDEX_op_sar_i32: - c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_ASR(args[2] & 0x1f) : - SHIFT_IMM_LSL(0) : SHIFT_REG_ASR(args[2]); - goto gen_shift32; - case INDEX_op_rotr_i32: - c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_ROR(args[2] & 0x1f) : - SHIFT_IMM_LSL(0) : SHIFT_REG_ROR(args[2]); - /* Fall through. */ - gen_shift32: - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], c); - break; - - case INDEX_op_rotl_i32: - if (const_args[2]) { - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], - ((0x20 - args[2]) & 0x1f) ? - SHIFT_IMM_ROR((0x20 - args[2]) & 0x1f) : - SHIFT_IMM_LSL(0)); - } else { - tcg_out_dat_imm(s, COND_AL, ARITH_RSB, TCG_REG_TMP, args[2], 0x20); - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], - SHIFT_REG_ROR(TCG_REG_TMP)); - } - break; - - case INDEX_op_brcond_i32: - tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, - args[0], args[1], const_args[1]); - tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[2]], - arg_label(args[3])); - break; - case INDEX_op_brcond2_i32: - /* The resulting conditions are: - * TCG_COND_EQ --> a0 == a2 && a1 == a3, - * TCG_COND_NE --> (a0 != a2 && a1 == a3) || a1 != a3, - * TCG_COND_LT(U) --> (a0 < a2 && a1 == a3) || a1 < a3, - * TCG_COND_GE(U) --> (a0 >= a2 && a1 == a3) || (a1 >= a3 && a1 != a3), - * TCG_COND_LE(U) --> (a0 <= a2 && a1 == a3) || (a1 <= a3 && a1 != a3), - * TCG_COND_GT(U) --> (a0 > a2 && a1 == a3) || a1 > a3, - */ - tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, - args[1], args[3], const_args[3]); - tcg_out_dat_rIN(s, COND_EQ, ARITH_CMP, ARITH_CMN, 0, - args[0], args[2], const_args[2]); - tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[4]], - arg_label(args[5])); - break; - case INDEX_op_setcond_i32: - tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, - args[1], args[2], const_args[2]); - tcg_out_dat_imm(s, tcg_cond_to_arm_cond[args[3]], - ARITH_MOV, args[0], 0, 1); - tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(args[3])], - ARITH_MOV, args[0], 0, 0); - break; - case INDEX_op_setcond2_i32: - /* See brcond2_i32 comment */ - tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, - args[2], args[4], const_args[4]); - tcg_out_dat_rIN(s, COND_EQ, ARITH_CMP, ARITH_CMN, 0, - args[1], args[3], const_args[3]); - tcg_out_dat_imm(s, tcg_cond_to_arm_cond[args[5]], - ARITH_MOV, args[0], 0, 1); - tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(args[5])], - ARITH_MOV, args[0], 0, 0); - break; - - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, args, 0); - break; - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, args, 1); - break; - case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, args, 0); - break; - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, args, 1); - break; - - case INDEX_op_bswap16_i32: - tcg_out_bswap16(s, COND_AL, args[0], args[1]); - break; - case INDEX_op_bswap32_i32: - tcg_out_bswap32(s, COND_AL, args[0], args[1]); - break; - - case INDEX_op_ext8s_i32: - tcg_out_ext8s(s, COND_AL, args[0], args[1]); - break; - case INDEX_op_ext16s_i32: - tcg_out_ext16s(s, COND_AL, args[0], args[1]); - break; - case INDEX_op_ext16u_i32: - tcg_out_ext16u(s, COND_AL, args[0], args[1]); - break; - - case INDEX_op_deposit_i32: - tcg_out_deposit(s, COND_AL, args[0], args[2], - args[3], args[4], const_args[2]); - break; - - case INDEX_op_div_i32: - tcg_out_sdiv(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_divu_i32: - tcg_out_udiv(s, COND_AL, args[0], args[1], args[2]); - break; - - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - default: - tcg_abort(); - } -} - -static const TCGTargetOpDef arm_op_defs[] = { - { INDEX_op_exit_tb, { } }, - { INDEX_op_goto_tb, { } }, - { INDEX_op_br, { } }, - - { INDEX_op_ld8u_i32, { "r", "r" } }, - { INDEX_op_ld8s_i32, { "r", "r" } }, - { INDEX_op_ld16u_i32, { "r", "r" } }, - { INDEX_op_ld16s_i32, { "r", "r" } }, - { INDEX_op_ld_i32, { "r", "r" } }, - { INDEX_op_st8_i32, { "r", "r" } }, - { INDEX_op_st16_i32, { "r", "r" } }, - { INDEX_op_st_i32, { "r", "r" } }, - - /* TODO: "r", "r", "ri" */ - { INDEX_op_add_i32, { "r", "r", "rIN" } }, - { INDEX_op_sub_i32, { "r", "rI", "rIN" } }, - { INDEX_op_mul_i32, { "r", "r", "r" } }, - { INDEX_op_mulu2_i32, { "r", "r", "r", "r" } }, - { INDEX_op_muls2_i32, { "r", "r", "r", "r" } }, - { INDEX_op_and_i32, { "r", "r", "rIK" } }, - { INDEX_op_andc_i32, { "r", "r", "rIK" } }, - { INDEX_op_or_i32, { "r", "r", "rI" } }, - { INDEX_op_xor_i32, { "r", "r", "rI" } }, - { INDEX_op_neg_i32, { "r", "r" } }, - { INDEX_op_not_i32, { "r", "r" } }, - - { INDEX_op_shl_i32, { "r", "r", "ri" } }, - { INDEX_op_shr_i32, { "r", "r", "ri" } }, - { INDEX_op_sar_i32, { "r", "r", "ri" } }, - { INDEX_op_rotl_i32, { "r", "r", "ri" } }, - { INDEX_op_rotr_i32, { "r", "r", "ri" } }, - - { INDEX_op_brcond_i32, { "r", "rIN" } }, - { INDEX_op_setcond_i32, { "r", "r", "rIN" } }, - { INDEX_op_movcond_i32, { "r", "r", "rIN", "rIK", "0" } }, - - { INDEX_op_add2_i32, { "r", "r", "r", "r", "rIN", "rIK" } }, - { INDEX_op_sub2_i32, { "r", "r", "rI", "rI", "rIN", "rIK" } }, - { INDEX_op_brcond2_i32, { "r", "r", "rIN", "rIN" } }, - { INDEX_op_setcond2_i32, { "r", "r", "r", "rIN", "rIN" } }, - -#if TARGET_LONG_BITS == 32 - { INDEX_op_qemu_ld_i32, { "r", "l" } }, - { INDEX_op_qemu_ld_i64, { "r", "r", "l" } }, - { INDEX_op_qemu_st_i32, { "s", "s" } }, - { INDEX_op_qemu_st_i64, { "s", "s", "s" } }, -#else - { INDEX_op_qemu_ld_i32, { "r", "l", "l" } }, - { INDEX_op_qemu_ld_i64, { "r", "r", "l", "l" } }, - { INDEX_op_qemu_st_i32, { "s", "s", "s" } }, - { INDEX_op_qemu_st_i64, { "s", "s", "s", "s" } }, -#endif - - { INDEX_op_bswap16_i32, { "r", "r" } }, - { INDEX_op_bswap32_i32, { "r", "r" } }, - - { INDEX_op_ext8s_i32, { "r", "r" } }, - { INDEX_op_ext16s_i32, { "r", "r" } }, - { INDEX_op_ext16u_i32, { "r", "r" } }, - - { INDEX_op_deposit_i32, { "r", "0", "rZ" } }, - - { INDEX_op_div_i32, { "r", "r", "r" } }, - { INDEX_op_divu_i32, { "r", "r", "r" } }, - - { -1 }, -}; - -static void tcg_target_init(TCGContext *s) -{ - /* Only probe for the platform and capabilities if we havn't already - determined maximum values at compile time. */ -#ifndef use_idiv_instructions - { - unsigned long hwcap = qemu_getauxval(AT_HWCAP); - use_idiv_instructions = (hwcap & HWCAP_ARM_IDIVA) != 0; - } -#endif - if (__ARM_ARCH < 7) { - const char *pl = (const char *)qemu_getauxval(AT_PLATFORM); - if (pl != NULL && pl[0] == 'v' && pl[1] >= '4' && pl[1] <= '9') { - arm_arch = pl[1] - '0'; - } - } - - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff); - tcg_regset_set32(tcg_target_call_clobber_regs, 0, - (1 << TCG_REG_R0) | - (1 << TCG_REG_R1) | - (1 << TCG_REG_R2) | - (1 << TCG_REG_R3) | - (1 << TCG_REG_R12) | - (1 << TCG_REG_R14)); - - tcg_regset_clear(s->reserved_regs); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_PC); - - tcg_add_target_add_op_defs(arm_op_defs); -} - -static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg arg, - TCGReg arg1, intptr_t arg2) -{ - tcg_out_ld32u(s, COND_AL, arg, arg1, arg2); -} - -static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, - TCGReg arg1, intptr_t arg2) -{ - tcg_out_st32(s, COND_AL, arg, arg1, arg2); -} - -static inline void tcg_out_mov(TCGContext *s, TCGType type, - TCGReg ret, TCGReg arg) -{ - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, ret, 0, arg, SHIFT_IMM_LSL(0)); -} - -static inline void tcg_out_movi(TCGContext *s, TCGType type, - TCGReg ret, tcg_target_long arg) -{ - tcg_out_movi32(s, COND_AL, ret, arg); -} - -/* Compute frame size via macros, to share between tcg_target_qemu_prologue - and tcg_register_jit. */ - -#define PUSH_SIZE ((11 - 4 + 1 + 1) * sizeof(tcg_target_long)) - -#define FRAME_SIZE \ - ((PUSH_SIZE \ - + TCG_STATIC_CALL_ARGS_SIZE \ - + CPU_TEMP_BUF_NLONGS * sizeof(long) \ - + TCG_TARGET_STACK_ALIGN - 1) \ - & -TCG_TARGET_STACK_ALIGN) - -static void tcg_target_qemu_prologue(TCGContext *s) -{ - int stack_addend; - - /* Calling convention requires us to save r4-r11 and lr. */ - /* stmdb sp!, { r4 - r11, lr } */ - tcg_out32(s, (COND_AL << 28) | 0x092d4ff0); - - /* Reserve callee argument and tcg temp space. */ - stack_addend = FRAME_SIZE - PUSH_SIZE; - - tcg_out_dat_rI(s, COND_AL, ARITH_SUB, TCG_REG_CALL_STACK, - TCG_REG_CALL_STACK, stack_addend, 1); - tcg_set_frame(s, TCG_REG_CALL_STACK, TCG_STATIC_CALL_ARGS_SIZE, - CPU_TEMP_BUF_NLONGS * sizeof(long)); - - tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); - - tcg_out_bx(s, COND_AL, tcg_target_call_iarg_regs[1]); - tb_ret_addr = s->code_ptr; - - /* Epilogue. We branch here via tb_ret_addr. */ - tcg_out_dat_rI(s, COND_AL, ARITH_ADD, TCG_REG_CALL_STACK, - TCG_REG_CALL_STACK, stack_addend, 1); - - /* ldmia sp!, { r4 - r11, pc } */ - tcg_out32(s, (COND_AL << 28) | 0x08bd8ff0); -} - -typedef struct { - DebugFrameHeader h; - uint8_t fde_def_cfa[4]; - uint8_t fde_reg_ofs[18]; -} DebugFrame; - -#define ELF_HOST_MACHINE EM_ARM - -/* We're expecting a 2 byte uleb128 encoded value. */ -QEMU_BUILD_BUG_ON(FRAME_SIZE >= (1 << 14)); - -static const DebugFrame debug_frame = { - .h.cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ - .h.cie.id = -1, - .h.cie.version = 1, - .h.cie.code_align = 1, - .h.cie.data_align = 0x7c, /* sleb128 -4 */ - .h.cie.return_column = 14, - - /* Total FDE size does not include the "len" member. */ - .h.fde.len = sizeof(DebugFrame) - offsetof(DebugFrame, h.fde.cie_offset), - - .fde_def_cfa = { - 12, 13, /* DW_CFA_def_cfa sp, ... */ - (FRAME_SIZE & 0x7f) | 0x80, /* ... uleb128 FRAME_SIZE */ - (FRAME_SIZE >> 7) - }, - .fde_reg_ofs = { - /* The following must match the stmdb in the prologue. */ - 0x8e, 1, /* DW_CFA_offset, lr, -4 */ - 0x8b, 2, /* DW_CFA_offset, r11, -8 */ - 0x8a, 3, /* DW_CFA_offset, r10, -12 */ - 0x89, 4, /* DW_CFA_offset, r9, -16 */ - 0x88, 5, /* DW_CFA_offset, r8, -20 */ - 0x87, 6, /* DW_CFA_offset, r7, -24 */ - 0x86, 7, /* DW_CFA_offset, r6, -28 */ - 0x85, 8, /* DW_CFA_offset, r5, -32 */ - 0x84, 9, /* DW_CFA_offset, r4, -36 */ - } -}; - -void tcg_register_jit(void *buf, size_t buf_size) -{ - tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); -} diff --git a/qemu/tcg/arm/tcg-target.inc.c b/qemu/tcg/arm/tcg-target.inc.c new file mode 100644 index 000000000..2b7fbddbf --- /dev/null +++ b/qemu/tcg/arm/tcg-target.inc.c @@ -0,0 +1,2128 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Andrzej Zaborowski + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "elf.h" +#include "tcg-be-ldst.h" + +/* The __ARM_ARCH define is provided by gcc 4.8. Construct it otherwise. */ +#ifndef __ARM_ARCH +# if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \ + || defined(__ARM_ARCH_7EM__) +# define __ARM_ARCH 7 +# elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) \ + || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) +# define __ARM_ARCH 6 +# elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5E__) \ + || defined(__ARM_ARCH_5T__) || defined(__ARM_ARCH_5TE__) \ + || defined(__ARM_ARCH_5TEJ__) +# define __ARM_ARCH 5 +# else +# define __ARM_ARCH 4 +# endif +#endif + +static int arm_arch = __ARM_ARCH; + +#if defined(__ARM_ARCH_5T__) \ + || defined(__ARM_ARCH_5TE__) || defined(__ARM_ARCH_5TEJ__) +# define use_armv5t_instructions 1 +#else +# define use_armv5t_instructions use_armv6_instructions +#endif + +#define use_armv6_instructions (__ARM_ARCH >= 6 || arm_arch >= 6) +#define use_armv7_instructions (__ARM_ARCH >= 7 || arm_arch >= 7) + +#ifndef use_idiv_instructions +bool use_idiv_instructions; +#endif + +/* ??? Ought to think about changing CONFIG_SOFTMMU to always defined. */ +#ifdef CONFIG_SOFTMMU +# define USING_SOFTMMU 1 +#else +# define USING_SOFTMMU 0 +#endif + +#ifdef CONFIG_DEBUG_TCG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "%r0", + "%r1", + "%r2", + "%r3", + "%r4", + "%r5", + "%r6", + "%r7", + "%r8", + "%r9", + "%r10", + "%r11", + "%r12", + "%r13", + "%r14", + "%pc", +}; +#endif + +static const int tcg_target_reg_alloc_order[] = { + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R13, + TCG_REG_R0, + TCG_REG_R1, + TCG_REG_R2, + TCG_REG_R3, + TCG_REG_R12, + TCG_REG_R14, +}; + +static const int tcg_target_call_iarg_regs[4] = { + TCG_REG_R0, TCG_REG_R1, TCG_REG_R2, TCG_REG_R3 +}; +static const int tcg_target_call_oarg_regs[2] = { + TCG_REG_R0, TCG_REG_R1 +}; + +#define TCG_REG_TMP TCG_REG_R12 + +static inline void reloc_pc24(tcg_insn_unit *code_ptr, tcg_insn_unit *target) +{ + ptrdiff_t offset = (tcg_ptr_byte_diff(target, code_ptr) - 8) >> 2; + *code_ptr = (*code_ptr & ~0xffffff) | (offset & 0xffffff); +} + +static void patch_reloc(tcg_insn_unit *code_ptr, int type, + intptr_t value, intptr_t addend) +{ + tcg_debug_assert(type == R_ARM_PC24); + tcg_debug_assert(addend == 0); + reloc_pc24(code_ptr, (tcg_insn_unit *)value); +} + +#define TCG_CT_CONST_ARM 0x100 +#define TCG_CT_CONST_INV 0x200 +#define TCG_CT_CONST_NEG 0x400 +#define TCG_CT_CONST_ZERO 0x800 + +/* parse target specific constraints */ +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +{ + const char *ct_str; + + ct_str = *pct_str; + switch (ct_str[0]) { + case 'I': + ct->ct |= TCG_CT_CONST_ARM; + break; + case 'K': + ct->ct |= TCG_CT_CONST_INV; + break; + case 'N': /* The gcc constraint letter is L, already used here. */ + ct->ct |= TCG_CT_CONST_NEG; + break; + case 'Z': + ct->ct |= TCG_CT_CONST_ZERO; + break; + + case 'r': + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); + break; + + /* qemu_ld address */ + case 'l': + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); +#ifdef CONFIG_SOFTMMU + /* r0-r2,lr will be overwritten when reading the tlb entry, + so don't use these. */ + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R2); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R14); +#endif + break; + + /* qemu_st address & data */ + case 's': + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); + /* r0-r2 will be overwritten when reading the tlb entry (softmmu only) + and r0-r1 doing the byte swapping, so don't use these. */ + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); +#if defined(CONFIG_SOFTMMU) + /* Avoid clashes with registers being used for helper args */ + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R2); +#if TARGET_LONG_BITS == 64 + /* Avoid clashes with registers being used for helper args */ + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3); +#endif + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R14); +#endif + break; + + default: + return -1; + } + ct_str++; + *pct_str = ct_str; + + return 0; +} + +static inline uint32_t rotl(uint32_t val, int n) +{ + return (val << n) | (val >> (32 - n)); +} + +/* ARM immediates for ALU instructions are made of an unsigned 8-bit + right-rotated by an even amount between 0 and 30. */ +static inline int encode_imm(uint32_t imm) +{ + int shift; + + /* simple case, only lower bits */ + if ((imm & ~0xff) == 0) + return 0; + /* then try a simple even shift */ + shift = ctz32(imm) & ~1; + if (((imm >> shift) & ~0xff) == 0) + return 32 - shift; + /* now try harder with rotations */ + if ((rotl(imm, 2) & ~0xff) == 0) + return 2; + if ((rotl(imm, 4) & ~0xff) == 0) + return 4; + if ((rotl(imm, 6) & ~0xff) == 0) + return 6; + /* imm can't be encoded */ + return -1; +} + +static inline int check_fit_imm(uint32_t imm) +{ + return encode_imm(imm) >= 0; +} + +/* Test if a constant matches the constraint. + * TODO: define constraints for: + * + * ldr/str offset: between -0xfff and 0xfff + * ldrh/strh offset: between -0xff and 0xff + * mov operand2: values represented with x << (2 * y), x < 0x100 + * add, sub, eor...: ditto + */ +static inline int tcg_target_const_match(tcg_target_long val, TCGType type, + const TCGArgConstraint *arg_ct) +{ + int ct; + ct = arg_ct->ct; + if (ct & TCG_CT_CONST) { + return 1; + } else if ((ct & TCG_CT_CONST_ARM) && check_fit_imm(val)) { + return 1; + } else if ((ct & TCG_CT_CONST_INV) && check_fit_imm(~val)) { + return 1; + } else if ((ct & TCG_CT_CONST_NEG) && check_fit_imm(-val)) { + return 1; + } else if ((ct & TCG_CT_CONST_ZERO) && val == 0) { + return 1; + } else { + return 0; + } +} + +#define TO_CPSR (1 << 20) + +typedef enum { + ARITH_AND = 0x0 << 21, + ARITH_EOR = 0x1 << 21, + ARITH_SUB = 0x2 << 21, + ARITH_RSB = 0x3 << 21, + ARITH_ADD = 0x4 << 21, + ARITH_ADC = 0x5 << 21, + ARITH_SBC = 0x6 << 21, + ARITH_RSC = 0x7 << 21, + ARITH_TST = 0x8 << 21 | TO_CPSR, + ARITH_CMP = 0xa << 21 | TO_CPSR, + ARITH_CMN = 0xb << 21 | TO_CPSR, + ARITH_ORR = 0xc << 21, + ARITH_MOV = 0xd << 21, + ARITH_BIC = 0xe << 21, + ARITH_MVN = 0xf << 21, + + INSN_LDR_IMM = 0x04100000, + INSN_LDR_REG = 0x06100000, + INSN_STR_IMM = 0x04000000, + INSN_STR_REG = 0x06000000, + + INSN_LDRH_IMM = 0x005000b0, + INSN_LDRH_REG = 0x001000b0, + INSN_LDRSH_IMM = 0x005000f0, + INSN_LDRSH_REG = 0x001000f0, + INSN_STRH_IMM = 0x004000b0, + INSN_STRH_REG = 0x000000b0, + + INSN_LDRB_IMM = 0x04500000, + INSN_LDRB_REG = 0x06500000, + INSN_LDRSB_IMM = 0x005000d0, + INSN_LDRSB_REG = 0x001000d0, + INSN_STRB_IMM = 0x04400000, + INSN_STRB_REG = 0x06400000, + + INSN_LDRD_IMM = 0x004000d0, + INSN_LDRD_REG = 0x000000d0, + INSN_STRD_IMM = 0x004000f0, + INSN_STRD_REG = 0x000000f0, +} ARMInsn; + +#define SHIFT_IMM_LSL(im) (((im) << 7) | 0x00) +#define SHIFT_IMM_LSR(im) (((im) << 7) | 0x20) +#define SHIFT_IMM_ASR(im) (((im) << 7) | 0x40) +#define SHIFT_IMM_ROR(im) (((im) << 7) | 0x60) +#define SHIFT_REG_LSL(rs) (((rs) << 8) | 0x10) +#define SHIFT_REG_LSR(rs) (((rs) << 8) | 0x30) +#define SHIFT_REG_ASR(rs) (((rs) << 8) | 0x50) +#define SHIFT_REG_ROR(rs) (((rs) << 8) | 0x70) + +enum arm_cond_code_e { + COND_EQ = 0x0, + COND_NE = 0x1, + COND_CS = 0x2, /* Unsigned greater or equal */ + COND_CC = 0x3, /* Unsigned less than */ + COND_MI = 0x4, /* Negative */ + COND_PL = 0x5, /* Zero or greater */ + COND_VS = 0x6, /* Overflow */ + COND_VC = 0x7, /* No overflow */ + COND_HI = 0x8, /* Unsigned greater than */ + COND_LS = 0x9, /* Unsigned less or equal */ + COND_GE = 0xa, + COND_LT = 0xb, + COND_GT = 0xc, + COND_LE = 0xd, + COND_AL = 0xe, +}; + +static const uint8_t tcg_cond_to_arm_cond[] = { + [TCG_COND_EQ] = COND_EQ, + [TCG_COND_NE] = COND_NE, + [TCG_COND_LT] = COND_LT, + [TCG_COND_GE] = COND_GE, + [TCG_COND_LE] = COND_LE, + [TCG_COND_GT] = COND_GT, + /* unsigned */ + [TCG_COND_LTU] = COND_CC, + [TCG_COND_GEU] = COND_CS, + [TCG_COND_LEU] = COND_LS, + [TCG_COND_GTU] = COND_HI, +}; + +static inline void tcg_out_bx(TCGContext *s, int cond, int rn) +{ + tcg_out32(s, (cond << 28) | 0x012fff10 | rn); +} + +static inline void tcg_out_b(TCGContext *s, int cond, int32_t offset) +{ + tcg_out32(s, (cond << 28) | 0x0a000000 | + (((offset - 8) >> 2) & 0x00ffffff)); +} + +static inline void tcg_out_b_noaddr(TCGContext *s, int cond) +{ + /* We pay attention here to not modify the branch target by masking + the corresponding bytes. This ensure that caches and memory are + kept coherent during retranslation. */ + tcg_out32(s, deposit32(*s->code_ptr, 24, 8, (cond << 4) | 0x0a)); +} + +static inline void tcg_out_bl_noaddr(TCGContext *s, int cond) +{ + /* We pay attention here to not modify the branch target by masking + the corresponding bytes. This ensure that caches and memory are + kept coherent during retranslation. */ + tcg_out32(s, deposit32(*s->code_ptr, 24, 8, (cond << 4) | 0x0b)); +} + +static inline void tcg_out_bl(TCGContext *s, int cond, int32_t offset) +{ + tcg_out32(s, (cond << 28) | 0x0b000000 | + (((offset - 8) >> 2) & 0x00ffffff)); +} + +static inline void tcg_out_blx(TCGContext *s, int cond, int rn) +{ + tcg_out32(s, (cond << 28) | 0x012fff30 | rn); +} + +static inline void tcg_out_blx_imm(TCGContext *s, int32_t offset) +{ + tcg_out32(s, 0xfa000000 | ((offset & 2) << 23) | + (((offset - 8) >> 2) & 0x00ffffff)); +} + +static inline void tcg_out_dat_reg(TCGContext *s, + int cond, int opc, int rd, int rn, int rm, int shift) +{ + tcg_out32(s, (cond << 28) | (0 << 25) | opc | + (rn << 16) | (rd << 12) | shift | rm); +} + +static inline void tcg_out_nop(TCGContext *s) +{ + if (use_armv7_instructions) { + /* Architected nop introduced in v6k. */ + /* ??? This is an MSR (imm) 0,0,0 insn. Anyone know if this + also Just So Happened to do nothing on pre-v6k so that we + don't need to conditionalize it? */ + tcg_out32(s, 0xe320f000); + } else { + /* Prior to that the assembler uses mov r0, r0. */ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, 0, 0, 0, SHIFT_IMM_LSL(0)); + } +} + +static inline void tcg_out_mov_reg(TCGContext *s, int cond, int rd, int rm) +{ + /* Simple reg-reg move, optimising out the 'do nothing' case */ + if (rd != rm) { + tcg_out_dat_reg(s, cond, ARITH_MOV, rd, 0, rm, SHIFT_IMM_LSL(0)); + } +} + +static inline void tcg_out_dat_imm(TCGContext *s, + int cond, int opc, int rd, int rn, int im) +{ + tcg_out32(s, (cond << 28) | (1 << 25) | opc | + (rn << 16) | (rd << 12) | im); +} + +static void tcg_out_movi32(TCGContext *s, int cond, int rd, uint32_t arg) +{ + int rot, opc, rn; + + /* For armv7, make sure not to use movw+movt when mov/mvn would do. + Speed things up by only checking when movt would be required. + Prior to armv7, have one go at fully rotated immediates before + doing the decomposition thing below. */ + if (!use_armv7_instructions || (arg & 0xffff0000)) { + rot = encode_imm(arg); + if (rot >= 0) { + tcg_out_dat_imm(s, cond, ARITH_MOV, rd, 0, + rotl(arg, rot) | (rot << 7)); + return; + } + rot = encode_imm(~arg); + if (rot >= 0) { + tcg_out_dat_imm(s, cond, ARITH_MVN, rd, 0, + rotl(~arg, rot) | (rot << 7)); + return; + } + } + + /* Use movw + movt. */ + if (use_armv7_instructions) { + /* movw */ + tcg_out32(s, (cond << 28) | 0x03000000 | (rd << 12) + | ((arg << 4) & 0x000f0000) | (arg & 0xfff)); + if (arg & 0xffff0000) { + /* movt */ + tcg_out32(s, (cond << 28) | 0x03400000 | (rd << 12) + | ((arg >> 12) & 0x000f0000) | ((arg >> 16) & 0xfff)); + } + return; + } + + /* TODO: This is very suboptimal, we can easily have a constant + pool somewhere after all the instructions. */ + opc = ARITH_MOV; + rn = 0; + /* If we have lots of leading 1's, we can shorten the sequence by + beginning with mvn and then clearing higher bits with eor. */ + if (clz32(~arg) > clz32(arg)) { + opc = ARITH_MVN, arg = ~arg; + } + do { + int i = ctz32(arg) & ~1; + rot = ((32 - i) << 7) & 0xf00; + tcg_out_dat_imm(s, cond, opc, rd, rn, ((arg >> i) & 0xff) | rot); + arg &= ~(0xff << i); + + opc = ARITH_EOR; + rn = rd; + } while (arg); +} + +static inline void tcg_out_dat_rI(TCGContext *s, int cond, int opc, TCGArg dst, + TCGArg lhs, TCGArg rhs, int rhs_is_const) +{ + /* Emit either the reg,imm or reg,reg form of a data-processing insn. + * rhs must satisfy the "rI" constraint. + */ + if (rhs_is_const) { + int rot = encode_imm(rhs); + tcg_debug_assert(rot >= 0); + tcg_out_dat_imm(s, cond, opc, dst, lhs, rotl(rhs, rot) | (rot << 7)); + } else { + tcg_out_dat_reg(s, cond, opc, dst, lhs, rhs, SHIFT_IMM_LSL(0)); + } +} + +static void tcg_out_dat_rIK(TCGContext *s, int cond, int opc, int opinv, + TCGReg dst, TCGReg lhs, TCGArg rhs, + bool rhs_is_const) +{ + /* Emit either the reg,imm or reg,reg form of a data-processing insn. + * rhs must satisfy the "rIK" constraint. + */ + if (rhs_is_const) { + int rot = encode_imm(rhs); + if (rot < 0) { + rhs = ~rhs; + rot = encode_imm(rhs); + tcg_debug_assert(rot >= 0); + opc = opinv; + } + tcg_out_dat_imm(s, cond, opc, dst, lhs, rotl(rhs, rot) | (rot << 7)); + } else { + tcg_out_dat_reg(s, cond, opc, dst, lhs, rhs, SHIFT_IMM_LSL(0)); + } +} + +static void tcg_out_dat_rIN(TCGContext *s, int cond, int opc, int opneg, + TCGArg dst, TCGArg lhs, TCGArg rhs, + bool rhs_is_const) +{ + /* Emit either the reg,imm or reg,reg form of a data-processing insn. + * rhs must satisfy the "rIN" constraint. + */ + if (rhs_is_const) { + int rot = encode_imm(rhs); + if (rot < 0) { + rhs = -rhs; + rot = encode_imm(rhs); + tcg_debug_assert(rot >= 0); + opc = opneg; + } + tcg_out_dat_imm(s, cond, opc, dst, lhs, rotl(rhs, rot) | (rot << 7)); + } else { + tcg_out_dat_reg(s, cond, opc, dst, lhs, rhs, SHIFT_IMM_LSL(0)); + } +} + +static inline void tcg_out_mul32(TCGContext *s, int cond, TCGReg rd, + TCGReg rn, TCGReg rm) +{ + /* if ArchVersion() < 6 && d == n then UNPREDICTABLE; */ + if (!use_armv6_instructions && rd == rn) { + if (rd == rm) { + /* rd == rn == rm; copy an input to tmp first. */ + tcg_out_mov_reg(s, cond, TCG_REG_TMP, rn); + rm = rn = TCG_REG_TMP; + } else { + rn = rm; + rm = rd; + } + } + /* mul */ + tcg_out32(s, (cond << 28) | 0x90 | (rd << 16) | (rm << 8) | rn); +} + +static inline void tcg_out_umull32(TCGContext *s, int cond, TCGReg rd0, + TCGReg rd1, TCGReg rn, TCGReg rm) +{ + /* if ArchVersion() < 6 && (dHi == n || dLo == n) then UNPREDICTABLE; */ + if (!use_armv6_instructions && (rd0 == rn || rd1 == rn)) { + if (rd0 == rm || rd1 == rm) { + tcg_out_mov_reg(s, cond, TCG_REG_TMP, rn); + rn = TCG_REG_TMP; + } else { + TCGReg t = rn; + rn = rm; + rm = t; + } + } + /* umull */ + tcg_out32(s, (cond << 28) | 0x00800090 | + (rd1 << 16) | (rd0 << 12) | (rm << 8) | rn); +} + +static inline void tcg_out_smull32(TCGContext *s, int cond, TCGReg rd0, + TCGReg rd1, TCGReg rn, TCGReg rm) +{ + /* if ArchVersion() < 6 && (dHi == n || dLo == n) then UNPREDICTABLE; */ + if (!use_armv6_instructions && (rd0 == rn || rd1 == rn)) { + if (rd0 == rm || rd1 == rm) { + tcg_out_mov_reg(s, cond, TCG_REG_TMP, rn); + rn = TCG_REG_TMP; + } else { + TCGReg t = rn; + rn = rm; + rm = t; + } + } + /* smull */ + tcg_out32(s, (cond << 28) | 0x00c00090 | + (rd1 << 16) | (rd0 << 12) | (rm << 8) | rn); +} + +static inline void tcg_out_sdiv(TCGContext *s, int cond, int rd, int rn, int rm) +{ + tcg_out32(s, 0x0710f010 | (cond << 28) | (rd << 16) | rn | (rm << 8)); +} + +static inline void tcg_out_udiv(TCGContext *s, int cond, int rd, int rn, int rm) +{ + tcg_out32(s, 0x0730f010 | (cond << 28) | (rd << 16) | rn | (rm << 8)); +} + +static inline void tcg_out_ext8s(TCGContext *s, int cond, + int rd, int rn) +{ + if (use_armv6_instructions) { + /* sxtb */ + tcg_out32(s, 0x06af0070 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rn, SHIFT_IMM_LSL(24)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rd, SHIFT_IMM_ASR(24)); + } +} + +static inline void tcg_out_ext8u(TCGContext *s, int cond, + int rd, int rn) +{ + tcg_out_dat_imm(s, cond, ARITH_AND, rd, rn, 0xff); +} + +static inline void tcg_out_ext16s(TCGContext *s, int cond, + int rd, int rn) +{ + if (use_armv6_instructions) { + /* sxth */ + tcg_out32(s, 0x06bf0070 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rn, SHIFT_IMM_LSL(16)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rd, SHIFT_IMM_ASR(16)); + } +} + +static inline void tcg_out_ext16u(TCGContext *s, int cond, + int rd, int rn) +{ + if (use_armv6_instructions) { + /* uxth */ + tcg_out32(s, 0x06ff0070 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rn, SHIFT_IMM_LSL(16)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rd, SHIFT_IMM_LSR(16)); + } +} + +static inline void tcg_out_bswap16s(TCGContext *s, int cond, int rd, int rn) +{ + if (use_armv6_instructions) { + /* revsh */ + tcg_out32(s, 0x06ff0fb0 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + TCG_REG_TMP, 0, rn, SHIFT_IMM_LSL(24)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + TCG_REG_TMP, 0, TCG_REG_TMP, SHIFT_IMM_ASR(16)); + tcg_out_dat_reg(s, cond, ARITH_ORR, + rd, TCG_REG_TMP, rn, SHIFT_IMM_LSR(8)); + } +} + +static inline void tcg_out_bswap16(TCGContext *s, int cond, int rd, int rn) +{ + if (use_armv6_instructions) { + /* rev16 */ + tcg_out32(s, 0x06bf0fb0 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + TCG_REG_TMP, 0, rn, SHIFT_IMM_LSL(24)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + TCG_REG_TMP, 0, TCG_REG_TMP, SHIFT_IMM_LSR(16)); + tcg_out_dat_reg(s, cond, ARITH_ORR, + rd, TCG_REG_TMP, rn, SHIFT_IMM_LSR(8)); + } +} + +/* swap the two low bytes assuming that the two high input bytes and the + two high output bit can hold any value. */ +static inline void tcg_out_bswap16st(TCGContext *s, int cond, int rd, int rn) +{ + if (use_armv6_instructions) { + /* rev16 */ + tcg_out32(s, 0x06bf0fb0 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + TCG_REG_TMP, 0, rn, SHIFT_IMM_LSR(8)); + tcg_out_dat_imm(s, cond, ARITH_AND, TCG_REG_TMP, TCG_REG_TMP, 0xff); + tcg_out_dat_reg(s, cond, ARITH_ORR, + rd, TCG_REG_TMP, rn, SHIFT_IMM_LSL(8)); + } +} + +static inline void tcg_out_bswap32(TCGContext *s, int cond, int rd, int rn) +{ + if (use_armv6_instructions) { + /* rev */ + tcg_out32(s, 0x06bf0f30 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_EOR, + TCG_REG_TMP, rn, rn, SHIFT_IMM_ROR(16)); + tcg_out_dat_imm(s, cond, ARITH_BIC, + TCG_REG_TMP, TCG_REG_TMP, 0xff | 0x800); + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rn, SHIFT_IMM_ROR(8)); + tcg_out_dat_reg(s, cond, ARITH_EOR, + rd, rd, TCG_REG_TMP, SHIFT_IMM_LSR(8)); + } +} + +bool tcg_target_deposit_valid(int ofs, int len) +{ + /* ??? Without bfi, we could improve over generic code by combining + the right-shift from a non-zero ofs with the orr. We do run into + problems when rd == rs, and the mask generated from ofs+len doesn't + fit into an immediate. We would have to be careful not to pessimize + wrt the optimizations performed on the expanded code. */ + return use_armv7_instructions; +} + +static inline void tcg_out_deposit(TCGContext *s, int cond, TCGReg rd, + TCGArg a1, int ofs, int len, bool const_a1) +{ + if (const_a1) { + /* bfi becomes bfc with rn == 15. */ + a1 = 15; + } + /* bfi/bfc */ + tcg_out32(s, 0x07c00010 | (cond << 28) | (rd << 12) | a1 + | (ofs << 7) | ((ofs + len - 1) << 16)); +} + +/* Note that this routine is used for both LDR and LDRH formats, so we do + not wish to include an immediate shift at this point. */ +static void tcg_out_memop_r(TCGContext *s, int cond, ARMInsn opc, TCGReg rt, + TCGReg rn, TCGReg rm, bool u, bool p, bool w) +{ + tcg_out32(s, (cond << 28) | opc | (u << 23) | (p << 24) + | (w << 21) | (rn << 16) | (rt << 12) | rm); +} + +static void tcg_out_memop_8(TCGContext *s, int cond, ARMInsn opc, TCGReg rt, + TCGReg rn, int imm8, bool p, bool w) +{ + bool u = 1; + if (imm8 < 0) { + imm8 = -imm8; + u = 0; + } + tcg_out32(s, (cond << 28) | opc | (u << 23) | (p << 24) | (w << 21) | + (rn << 16) | (rt << 12) | ((imm8 & 0xf0) << 4) | (imm8 & 0xf)); +} + +static void tcg_out_memop_12(TCGContext *s, int cond, ARMInsn opc, TCGReg rt, + TCGReg rn, int imm12, bool p, bool w) +{ + bool u = 1; + if (imm12 < 0) { + imm12 = -imm12; + u = 0; + } + tcg_out32(s, (cond << 28) | opc | (u << 23) | (p << 24) | (w << 21) | + (rn << 16) | (rt << 12) | imm12); +} + +static inline void tcg_out_ld32_12(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, int imm12) +{ + tcg_out_memop_12(s, cond, INSN_LDR_IMM, rt, rn, imm12, 1, 0); +} + +static inline void tcg_out_st32_12(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, int imm12) +{ + tcg_out_memop_12(s, cond, INSN_STR_IMM, rt, rn, imm12, 1, 0); +} + +static inline void tcg_out_ld32_r(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, TCGReg rm) +{ + tcg_out_memop_r(s, cond, INSN_LDR_REG, rt, rn, rm, 1, 1, 0); +} + +static inline void tcg_out_st32_r(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, TCGReg rm) +{ + tcg_out_memop_r(s, cond, INSN_STR_REG, rt, rn, rm, 1, 1, 0); +} + +static inline void tcg_out_ldrd_8(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, int imm8) +{ + tcg_out_memop_8(s, cond, INSN_LDRD_IMM, rt, rn, imm8, 1, 0); +} + +static inline void tcg_out_ldrd_r(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, TCGReg rm) +{ + tcg_out_memop_r(s, cond, INSN_LDRD_REG, rt, rn, rm, 1, 1, 0); +} + +static inline void tcg_out_strd_8(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, int imm8) +{ + tcg_out_memop_8(s, cond, INSN_STRD_IMM, rt, rn, imm8, 1, 0); +} + +static inline void tcg_out_strd_r(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, TCGReg rm) +{ + tcg_out_memop_r(s, cond, INSN_STRD_REG, rt, rn, rm, 1, 1, 0); +} + +/* Register pre-increment with base writeback. */ +static inline void tcg_out_ld32_rwb(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, TCGReg rm) +{ + tcg_out_memop_r(s, cond, INSN_LDR_REG, rt, rn, rm, 1, 1, 1); +} + +static inline void tcg_out_st32_rwb(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, TCGReg rm) +{ + tcg_out_memop_r(s, cond, INSN_STR_REG, rt, rn, rm, 1, 1, 1); +} + +static inline void tcg_out_ld16u_8(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, int imm8) +{ + tcg_out_memop_8(s, cond, INSN_LDRH_IMM, rt, rn, imm8, 1, 0); +} + +static inline void tcg_out_st16_8(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, int imm8) +{ + tcg_out_memop_8(s, cond, INSN_STRH_IMM, rt, rn, imm8, 1, 0); +} + +static inline void tcg_out_ld16u_r(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, TCGReg rm) +{ + tcg_out_memop_r(s, cond, INSN_LDRH_REG, rt, rn, rm, 1, 1, 0); +} + +static inline void tcg_out_st16_r(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, TCGReg rm) +{ + tcg_out_memop_r(s, cond, INSN_STRH_REG, rt, rn, rm, 1, 1, 0); +} + +static inline void tcg_out_ld16s_8(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, int imm8) +{ + tcg_out_memop_8(s, cond, INSN_LDRSH_IMM, rt, rn, imm8, 1, 0); +} + +static inline void tcg_out_ld16s_r(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, TCGReg rm) +{ + tcg_out_memop_r(s, cond, INSN_LDRSH_REG, rt, rn, rm, 1, 1, 0); +} + +static inline void tcg_out_ld8_12(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, int imm12) +{ + tcg_out_memop_12(s, cond, INSN_LDRB_IMM, rt, rn, imm12, 1, 0); +} + +static inline void tcg_out_st8_12(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, int imm12) +{ + tcg_out_memop_12(s, cond, INSN_STRB_IMM, rt, rn, imm12, 1, 0); +} + +static inline void tcg_out_ld8_r(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, TCGReg rm) +{ + tcg_out_memop_r(s, cond, INSN_LDRB_REG, rt, rn, rm, 1, 1, 0); +} + +static inline void tcg_out_st8_r(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, TCGReg rm) +{ + tcg_out_memop_r(s, cond, INSN_STRB_REG, rt, rn, rm, 1, 1, 0); +} + +static inline void tcg_out_ld8s_8(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, int imm8) +{ + tcg_out_memop_8(s, cond, INSN_LDRSB_IMM, rt, rn, imm8, 1, 0); +} + +static inline void tcg_out_ld8s_r(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, TCGReg rm) +{ + tcg_out_memop_r(s, cond, INSN_LDRSB_REG, rt, rn, rm, 1, 1, 0); +} + +static inline void tcg_out_ld32u(TCGContext *s, int cond, + int rd, int rn, int32_t offset) +{ + if (offset > 0xfff || offset < -0xfff) { + tcg_out_movi32(s, cond, TCG_REG_TMP, offset); + tcg_out_ld32_r(s, cond, rd, rn, TCG_REG_TMP); + } else + tcg_out_ld32_12(s, cond, rd, rn, offset); +} + +static inline void tcg_out_st32(TCGContext *s, int cond, + int rd, int rn, int32_t offset) +{ + if (offset > 0xfff || offset < -0xfff) { + tcg_out_movi32(s, cond, TCG_REG_TMP, offset); + tcg_out_st32_r(s, cond, rd, rn, TCG_REG_TMP); + } else + tcg_out_st32_12(s, cond, rd, rn, offset); +} + +static inline void tcg_out_ld16u(TCGContext *s, int cond, + int rd, int rn, int32_t offset) +{ + if (offset > 0xff || offset < -0xff) { + tcg_out_movi32(s, cond, TCG_REG_TMP, offset); + tcg_out_ld16u_r(s, cond, rd, rn, TCG_REG_TMP); + } else + tcg_out_ld16u_8(s, cond, rd, rn, offset); +} + +static inline void tcg_out_ld16s(TCGContext *s, int cond, + int rd, int rn, int32_t offset) +{ + if (offset > 0xff || offset < -0xff) { + tcg_out_movi32(s, cond, TCG_REG_TMP, offset); + tcg_out_ld16s_r(s, cond, rd, rn, TCG_REG_TMP); + } else + tcg_out_ld16s_8(s, cond, rd, rn, offset); +} + +static inline void tcg_out_st16(TCGContext *s, int cond, + int rd, int rn, int32_t offset) +{ + if (offset > 0xff || offset < -0xff) { + tcg_out_movi32(s, cond, TCG_REG_TMP, offset); + tcg_out_st16_r(s, cond, rd, rn, TCG_REG_TMP); + } else + tcg_out_st16_8(s, cond, rd, rn, offset); +} + +static inline void tcg_out_ld8u(TCGContext *s, int cond, + int rd, int rn, int32_t offset) +{ + if (offset > 0xfff || offset < -0xfff) { + tcg_out_movi32(s, cond, TCG_REG_TMP, offset); + tcg_out_ld8_r(s, cond, rd, rn, TCG_REG_TMP); + } else + tcg_out_ld8_12(s, cond, rd, rn, offset); +} + +static inline void tcg_out_ld8s(TCGContext *s, int cond, + int rd, int rn, int32_t offset) +{ + if (offset > 0xff || offset < -0xff) { + tcg_out_movi32(s, cond, TCG_REG_TMP, offset); + tcg_out_ld8s_r(s, cond, rd, rn, TCG_REG_TMP); + } else + tcg_out_ld8s_8(s, cond, rd, rn, offset); +} + +static inline void tcg_out_st8(TCGContext *s, int cond, + int rd, int rn, int32_t offset) +{ + if (offset > 0xfff || offset < -0xfff) { + tcg_out_movi32(s, cond, TCG_REG_TMP, offset); + tcg_out_st8_r(s, cond, rd, rn, TCG_REG_TMP); + } else + tcg_out_st8_12(s, cond, rd, rn, offset); +} + +/* The _goto case is normally between TBs within the same code buffer, and + * with the code buffer limited to 16MB we wouldn't need the long case. + * But we also use it for the tail-call to the qemu_ld/st helpers, which does. + */ +static inline void tcg_out_goto(TCGContext *s, int cond, tcg_insn_unit *addr) +{ + intptr_t addri = (intptr_t)addr; + ptrdiff_t disp = tcg_pcrel_diff(s, addr); + + if ((addri & 1) == 0 && disp - 8 < 0x01fffffd && disp - 8 > -0x01fffffd) { + tcg_out_b(s, cond, disp); + return; + } + + tcg_out_movi32(s, cond, TCG_REG_TMP, addri); + if (use_armv5t_instructions) { + tcg_out_bx(s, cond, TCG_REG_TMP); + } else { + if (addri & 1) { + tcg_abort(); + } + tcg_out_mov_reg(s, cond, TCG_REG_PC, TCG_REG_TMP); + } +} + +/* The call case is mostly used for helpers - so it's not unreasonable + * for them to be beyond branch range */ +static void tcg_out_call(TCGContext *s, tcg_insn_unit *addr) +{ + intptr_t addri = (intptr_t)addr; + ptrdiff_t disp = tcg_pcrel_diff(s, addr); + + if (disp - 8 < 0x02000000 && disp - 8 >= -0x02000000) { + if (addri & 1) { + /* Use BLX if the target is in Thumb mode */ + if (!use_armv5t_instructions) { + tcg_abort(); + } + tcg_out_blx_imm(s, disp); + } else { + tcg_out_bl(s, COND_AL, disp); + } + } else if (use_armv7_instructions) { + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, addri); + tcg_out_blx(s, COND_AL, TCG_REG_TMP); + } else { + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R14, TCG_REG_PC, 4); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4); + tcg_out32(s, addri); + } +} + +static inline void tcg_out_goto_label(TCGContext *s, int cond, TCGLabel *l) +{ + if (l->has_value) { + tcg_out_goto(s, cond, l->u.value_ptr); + } else { + tcg_out_reloc(s, s->code_ptr, R_ARM_PC24, l, 0); + tcg_out_b_noaddr(s, cond); + } +} + +#ifdef CONFIG_SOFTMMU +/* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, + * int mmu_idx, uintptr_t ra) + */ +static void * const qemu_ld_helpers[16] = { + [MO_UB] = helper_ret_ldub_mmu, + [MO_SB] = helper_ret_ldsb_mmu, + + [MO_LEUW] = helper_le_lduw_mmu, + [MO_LEUL] = helper_le_ldul_mmu, + [MO_LEQ] = helper_le_ldq_mmu, + [MO_LESW] = helper_le_ldsw_mmu, + [MO_LESL] = helper_le_ldul_mmu, + + [MO_BEUW] = helper_be_lduw_mmu, + [MO_BEUL] = helper_be_ldul_mmu, + [MO_BEQ] = helper_be_ldq_mmu, + [MO_BESW] = helper_be_ldsw_mmu, + [MO_BESL] = helper_be_ldul_mmu, +}; + +/* helper signature: helper_ret_st_mmu(CPUState *env, target_ulong addr, + * uintxx_t val, int mmu_idx, uintptr_t ra) + */ +static void * const qemu_st_helpers[16] = { + [MO_UB] = helper_ret_stb_mmu, + [MO_LEUW] = helper_le_stw_mmu, + [MO_LEUL] = helper_le_stl_mmu, + [MO_LEQ] = helper_le_stq_mmu, + [MO_BEUW] = helper_be_stw_mmu, + [MO_BEUL] = helper_be_stl_mmu, + [MO_BEQ] = helper_be_stq_mmu, +}; + +/* Helper routines for marshalling helper function arguments into + * the correct registers and stack. + * argreg is where we want to put this argument, arg is the argument itself. + * Return value is the updated argreg ready for the next call. + * Note that argreg 0..3 is real registers, 4+ on stack. + * + * We provide routines for arguments which are: immediate, 32 bit + * value in register, 16 and 8 bit values in register (which must be zero + * extended before use) and 64 bit value in a lo:hi register pair. + */ +#define DEFINE_TCG_OUT_ARG(NAME, ARGTYPE, MOV_ARG, EXT_ARG) \ +static TCGReg NAME(TCGContext *s, TCGReg argreg, ARGTYPE arg) \ +{ \ + if (argreg < 4) { \ + MOV_ARG(s, COND_AL, argreg, arg); \ + } else { \ + int ofs = (argreg - 4) * 4; \ + EXT_ARG; \ + tcg_debug_assert(ofs + 4 <= TCG_STATIC_CALL_ARGS_SIZE); \ + tcg_out_st32_12(s, COND_AL, arg, TCG_REG_CALL_STACK, ofs); \ + } \ + return argreg + 1; \ +} + +DEFINE_TCG_OUT_ARG(tcg_out_arg_imm32, uint32_t, tcg_out_movi32, + (tcg_out_movi32(s, COND_AL, TCG_REG_TMP, arg), arg = TCG_REG_TMP)) +DEFINE_TCG_OUT_ARG(tcg_out_arg_reg8, TCGReg, tcg_out_ext8u, + (tcg_out_ext8u(s, COND_AL, TCG_REG_TMP, arg), arg = TCG_REG_TMP)) +DEFINE_TCG_OUT_ARG(tcg_out_arg_reg16, TCGReg, tcg_out_ext16u, + (tcg_out_ext16u(s, COND_AL, TCG_REG_TMP, arg), arg = TCG_REG_TMP)) +DEFINE_TCG_OUT_ARG(tcg_out_arg_reg32, TCGReg, tcg_out_mov_reg, ) + +static TCGReg tcg_out_arg_reg64(TCGContext *s, TCGReg argreg, + TCGReg arglo, TCGReg arghi) +{ + /* 64 bit arguments must go in even/odd register pairs + * and in 8-aligned stack slots. + */ + if (argreg & 1) { + argreg++; + } + if (use_armv6_instructions && argreg >= 4 + && (arglo & 1) == 0 && arghi == arglo + 1) { + tcg_out_strd_8(s, COND_AL, arglo, + TCG_REG_CALL_STACK, (argreg - 4) * 4); + return argreg + 2; + } else { + argreg = tcg_out_arg_reg32(s, argreg, arglo); + argreg = tcg_out_arg_reg32(s, argreg, arghi); + return argreg; + } +} + +#define TLB_SHIFT (CPU_TLB_ENTRY_BITS + CPU_TLB_BITS) + +/* We're expecting to use an 8-bit immediate and to mask. */ +QEMU_BUILD_BUG_ON(CPU_TLB_BITS > 8); + +/* We're expecting to use an 8-bit immediate add + 8-bit ldrd offset. + Using the offset of the second entry in the last tlb table ensures + that we can index all of the elements of the first entry. */ +QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table[NB_MMU_MODES - 1][1]) + > 0xffff); + +/* Load and compare a TLB entry, leaving the flags set. Returns the register + containing the addend of the tlb entry. Clobbers R0, R1, R2, TMP. */ + +static TCGReg tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi, + TCGMemOp s_bits, int mem_index, bool is_load) +{ + TCGReg base = TCG_AREG0; + int cmp_off = + (is_load + ? offsetof(CPUArchState, tlb_table[mem_index][0].addr_read) + : offsetof(CPUArchState, tlb_table[mem_index][0].addr_write)); + int add_off = offsetof(CPUArchState, tlb_table[mem_index][0].addend); + + /* Should generate something like the following: + * shr tmp, addrlo, #TARGET_PAGE_BITS (1) + * add r2, env, #high + * and r0, tmp, #(CPU_TLB_SIZE - 1) (2) + * add r2, r2, r0, lsl #CPU_TLB_ENTRY_BITS (3) + * ldr r0, [r2, #cmp] (4) + * tst addrlo, #s_mask + * ldr r2, [r2, #add] (5) + * cmpeq r0, tmp, lsl #TARGET_PAGE_BITS + */ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, TCG_REG_TMP, + 0, addrlo, SHIFT_IMM_LSR(TARGET_PAGE_BITS)); + + /* We checked that the offset is contained within 16 bits above. */ + if (add_off > 0xfff || (use_armv6_instructions && cmp_off > 0xff)) { + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R2, base, + (24 << 7) | (cmp_off >> 8)); + base = TCG_REG_R2; + add_off -= cmp_off & 0xff00; + cmp_off &= 0xff; + } + + tcg_out_dat_imm(s, COND_AL, ARITH_AND, + TCG_REG_R0, TCG_REG_TMP, CPU_TLB_SIZE - 1); + tcg_out_dat_reg(s, COND_AL, ARITH_ADD, TCG_REG_R2, base, + TCG_REG_R0, SHIFT_IMM_LSL(CPU_TLB_ENTRY_BITS)); + + /* Load the tlb comparator. Use ldrd if needed and available, + but due to how the pointer needs setting up, ldm isn't useful. + Base arm5 doesn't have ldrd, but armv5te does. */ + if (use_armv6_instructions && TARGET_LONG_BITS == 64) { + tcg_out_ldrd_8(s, COND_AL, TCG_REG_R0, TCG_REG_R2, cmp_off); + } else { + tcg_out_ld32_12(s, COND_AL, TCG_REG_R0, TCG_REG_R2, cmp_off); + if (TARGET_LONG_BITS == 64) { + tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R2, cmp_off + 4); + } + } + + /* Check alignment. */ + if (s_bits) { + tcg_out_dat_imm(s, COND_AL, ARITH_TST, + 0, addrlo, (1 << s_bits) - 1); + } + + /* Load the tlb addend. */ + tcg_out_ld32_12(s, COND_AL, TCG_REG_R2, TCG_REG_R2, add_off); + + tcg_out_dat_reg(s, (s_bits ? COND_EQ : COND_AL), ARITH_CMP, 0, + TCG_REG_R0, TCG_REG_TMP, SHIFT_IMM_LSL(TARGET_PAGE_BITS)); + + if (TARGET_LONG_BITS == 64) { + tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0, + TCG_REG_R1, addrhi, SHIFT_IMM_LSL(0)); + } + + return TCG_REG_R2; +} + +/* Record the context of a call to the out of line helper code for the slow + path for a load or store, so that we can later generate the correct + helper code. */ +static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOpIdx oi, + TCGReg datalo, TCGReg datahi, TCGReg addrlo, + TCGReg addrhi, tcg_insn_unit *raddr, + tcg_insn_unit *label_ptr) +{ + TCGLabelQemuLdst *label = new_ldst_label(s); + + label->is_ld = is_ld; + label->oi = oi; + label->datalo_reg = datalo; + label->datahi_reg = datahi; + label->addrlo_reg = addrlo; + label->addrhi_reg = addrhi; + label->raddr = raddr; + label->label_ptr[0] = label_ptr; +} + +static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) +{ + TCGReg argreg, datalo, datahi; + TCGMemOpIdx oi = lb->oi; + TCGMemOp opc = get_memop(oi); + void *func; + + reloc_pc24(lb->label_ptr[0], s->code_ptr); + + argreg = tcg_out_arg_reg32(s, TCG_REG_R0, TCG_AREG0); + if (TARGET_LONG_BITS == 64) { + argreg = tcg_out_arg_reg64(s, argreg, lb->addrlo_reg, lb->addrhi_reg); + } else { + argreg = tcg_out_arg_reg32(s, argreg, lb->addrlo_reg); + } + argreg = tcg_out_arg_imm32(s, argreg, oi); + argreg = tcg_out_arg_reg32(s, argreg, TCG_REG_R14); + + /* For armv6 we can use the canonical unsigned helpers and minimize + icache usage. For pre-armv6, use the signed helpers since we do + not have a single insn sign-extend. */ + if (use_armv6_instructions) { + func = qemu_ld_helpers[opc & (MO_BSWAP | MO_SIZE)]; + } else { + func = qemu_ld_helpers[opc & (MO_BSWAP | MO_SSIZE)]; + if (opc & MO_SIGN) { + opc = MO_UL; + } + } + tcg_out_call(s, func); + + datalo = lb->datalo_reg; + datahi = lb->datahi_reg; + switch (opc & MO_SSIZE) { + case MO_SB: + tcg_out_ext8s(s, COND_AL, datalo, TCG_REG_R0); + break; + case MO_SW: + tcg_out_ext16s(s, COND_AL, datalo, TCG_REG_R0); + break; + default: + tcg_out_mov_reg(s, COND_AL, datalo, TCG_REG_R0); + break; + case MO_Q: + if (datalo != TCG_REG_R1) { + tcg_out_mov_reg(s, COND_AL, datalo, TCG_REG_R0); + tcg_out_mov_reg(s, COND_AL, datahi, TCG_REG_R1); + } else if (datahi != TCG_REG_R0) { + tcg_out_mov_reg(s, COND_AL, datahi, TCG_REG_R1); + tcg_out_mov_reg(s, COND_AL, datalo, TCG_REG_R0); + } else { + tcg_out_mov_reg(s, COND_AL, TCG_REG_TMP, TCG_REG_R0); + tcg_out_mov_reg(s, COND_AL, datahi, TCG_REG_R1); + tcg_out_mov_reg(s, COND_AL, datalo, TCG_REG_TMP); + } + break; + } + + tcg_out_goto(s, COND_AL, lb->raddr); +} + +static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) +{ + TCGReg argreg, datalo, datahi; + TCGMemOpIdx oi = lb->oi; + TCGMemOp opc = get_memop(oi); + + reloc_pc24(lb->label_ptr[0], s->code_ptr); + + argreg = TCG_REG_R0; + argreg = tcg_out_arg_reg32(s, argreg, TCG_AREG0); + if (TARGET_LONG_BITS == 64) { + argreg = tcg_out_arg_reg64(s, argreg, lb->addrlo_reg, lb->addrhi_reg); + } else { + argreg = tcg_out_arg_reg32(s, argreg, lb->addrlo_reg); + } + + datalo = lb->datalo_reg; + datahi = lb->datahi_reg; + switch (opc & MO_SIZE) { + case MO_8: + argreg = tcg_out_arg_reg8(s, argreg, datalo); + break; + case MO_16: + argreg = tcg_out_arg_reg16(s, argreg, datalo); + break; + case MO_32: + default: + argreg = tcg_out_arg_reg32(s, argreg, datalo); + break; + case MO_64: + argreg = tcg_out_arg_reg64(s, argreg, datalo, datahi); + break; + } + + argreg = tcg_out_arg_imm32(s, argreg, oi); + argreg = tcg_out_arg_reg32(s, argreg, TCG_REG_R14); + + /* Tail-call to the helper, which will return to the fast path. */ + tcg_out_goto(s, COND_AL, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)]); +} +#endif /* SOFTMMU */ + +static inline void tcg_out_qemu_ld_index(TCGContext *s, TCGMemOp opc, + TCGReg datalo, TCGReg datahi, + TCGReg addrlo, TCGReg addend) +{ + TCGMemOp bswap = opc & MO_BSWAP; + + switch (opc & MO_SSIZE) { + case MO_UB: + tcg_out_ld8_r(s, COND_AL, datalo, addrlo, addend); + break; + case MO_SB: + tcg_out_ld8s_r(s, COND_AL, datalo, addrlo, addend); + break; + case MO_UW: + tcg_out_ld16u_r(s, COND_AL, datalo, addrlo, addend); + if (bswap) { + tcg_out_bswap16(s, COND_AL, datalo, datalo); + } + break; + case MO_SW: + if (bswap) { + tcg_out_ld16u_r(s, COND_AL, datalo, addrlo, addend); + tcg_out_bswap16s(s, COND_AL, datalo, datalo); + } else { + tcg_out_ld16s_r(s, COND_AL, datalo, addrlo, addend); + } + break; + case MO_UL: + default: + tcg_out_ld32_r(s, COND_AL, datalo, addrlo, addend); + if (bswap) { + tcg_out_bswap32(s, COND_AL, datalo, datalo); + } + break; + case MO_Q: + { + TCGReg dl = (bswap ? datahi : datalo); + TCGReg dh = (bswap ? datalo : datahi); + + /* Avoid ldrd for user-only emulation, to handle unaligned. */ + if (USING_SOFTMMU && use_armv6_instructions + && (dl & 1) == 0 && dh == dl + 1) { + tcg_out_ldrd_r(s, COND_AL, dl, addrlo, addend); + } else if (dl != addend) { + tcg_out_ld32_rwb(s, COND_AL, dl, addend, addrlo); + tcg_out_ld32_12(s, COND_AL, dh, addend, 4); + } else { + tcg_out_dat_reg(s, COND_AL, ARITH_ADD, TCG_REG_TMP, + addend, addrlo, SHIFT_IMM_LSL(0)); + tcg_out_ld32_12(s, COND_AL, dl, TCG_REG_TMP, 0); + tcg_out_ld32_12(s, COND_AL, dh, TCG_REG_TMP, 4); + } + if (bswap) { + tcg_out_bswap32(s, COND_AL, dl, dl); + tcg_out_bswap32(s, COND_AL, dh, dh); + } + } + break; + } +} + +static inline void tcg_out_qemu_ld_direct(TCGContext *s, TCGMemOp opc, + TCGReg datalo, TCGReg datahi, + TCGReg addrlo) +{ + TCGMemOp bswap = opc & MO_BSWAP; + + switch (opc & MO_SSIZE) { + case MO_UB: + tcg_out_ld8_12(s, COND_AL, datalo, addrlo, 0); + break; + case MO_SB: + tcg_out_ld8s_8(s, COND_AL, datalo, addrlo, 0); + break; + case MO_UW: + tcg_out_ld16u_8(s, COND_AL, datalo, addrlo, 0); + if (bswap) { + tcg_out_bswap16(s, COND_AL, datalo, datalo); + } + break; + case MO_SW: + if (bswap) { + tcg_out_ld16u_8(s, COND_AL, datalo, addrlo, 0); + tcg_out_bswap16s(s, COND_AL, datalo, datalo); + } else { + tcg_out_ld16s_8(s, COND_AL, datalo, addrlo, 0); + } + break; + case MO_UL: + default: + tcg_out_ld32_12(s, COND_AL, datalo, addrlo, 0); + if (bswap) { + tcg_out_bswap32(s, COND_AL, datalo, datalo); + } + break; + case MO_Q: + { + TCGReg dl = (bswap ? datahi : datalo); + TCGReg dh = (bswap ? datalo : datahi); + + /* Avoid ldrd for user-only emulation, to handle unaligned. */ + if (USING_SOFTMMU && use_armv6_instructions + && (dl & 1) == 0 && dh == dl + 1) { + tcg_out_ldrd_8(s, COND_AL, dl, addrlo, 0); + } else if (dl == addrlo) { + tcg_out_ld32_12(s, COND_AL, dh, addrlo, bswap ? 0 : 4); + tcg_out_ld32_12(s, COND_AL, dl, addrlo, bswap ? 4 : 0); + } else { + tcg_out_ld32_12(s, COND_AL, dl, addrlo, bswap ? 4 : 0); + tcg_out_ld32_12(s, COND_AL, dh, addrlo, bswap ? 0 : 4); + } + if (bswap) { + tcg_out_bswap32(s, COND_AL, dl, dl); + tcg_out_bswap32(s, COND_AL, dh, dh); + } + } + break; + } +} + +static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is64) +{ + TCGReg addrlo, datalo, datahi, addrhi __attribute__((unused)); + TCGMemOpIdx oi; + TCGMemOp opc; +#ifdef CONFIG_SOFTMMU + int mem_index; + TCGReg addend; + tcg_insn_unit *label_ptr; +#endif + + datalo = *args++; + datahi = (is64 ? *args++ : 0); + addrlo = *args++; + addrhi = (TARGET_LONG_BITS == 64 ? *args++ : 0); + oi = *args++; + opc = get_memop(oi); + +#ifdef CONFIG_SOFTMMU + mem_index = get_mmuidx(oi); + addend = tcg_out_tlb_read(s, addrlo, addrhi, opc & MO_SIZE, mem_index, 1); + + /* This a conditional BL only to load a pointer within this opcode into LR + for the slow path. We will not be using the value for a tail call. */ + label_ptr = s->code_ptr; + tcg_out_bl_noaddr(s, COND_NE); + + tcg_out_qemu_ld_index(s, opc, datalo, datahi, addrlo, addend); + + add_qemu_ldst_label(s, true, oi, datalo, datahi, addrlo, addrhi, + s->code_ptr, label_ptr); +#else /* !CONFIG_SOFTMMU */ + if (guest_base) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP, guest_base); + tcg_out_qemu_ld_index(s, opc, datalo, datahi, addrlo, TCG_REG_TMP); + } else { + tcg_out_qemu_ld_direct(s, opc, datalo, datahi, addrlo); + } +#endif +} + +static inline void tcg_out_qemu_st_index(TCGContext *s, int cond, TCGMemOp opc, + TCGReg datalo, TCGReg datahi, + TCGReg addrlo, TCGReg addend) +{ + TCGMemOp bswap = opc & MO_BSWAP; + + switch (opc & MO_SIZE) { + case MO_8: + tcg_out_st8_r(s, cond, datalo, addrlo, addend); + break; + case MO_16: + if (bswap) { + tcg_out_bswap16st(s, cond, TCG_REG_R0, datalo); + tcg_out_st16_r(s, cond, TCG_REG_R0, addrlo, addend); + } else { + tcg_out_st16_r(s, cond, datalo, addrlo, addend); + } + break; + case MO_32: + default: + if (bswap) { + tcg_out_bswap32(s, cond, TCG_REG_R0, datalo); + tcg_out_st32_r(s, cond, TCG_REG_R0, addrlo, addend); + } else { + tcg_out_st32_r(s, cond, datalo, addrlo, addend); + } + break; + case MO_64: + /* Avoid strd for user-only emulation, to handle unaligned. */ + if (bswap) { + tcg_out_bswap32(s, cond, TCG_REG_R0, datahi); + tcg_out_st32_rwb(s, cond, TCG_REG_R0, addend, addrlo); + tcg_out_bswap32(s, cond, TCG_REG_R0, datalo); + tcg_out_st32_12(s, cond, TCG_REG_R0, addend, 4); + } else if (USING_SOFTMMU && use_armv6_instructions + && (datalo & 1) == 0 && datahi == datalo + 1) { + tcg_out_strd_r(s, cond, datalo, addrlo, addend); + } else { + tcg_out_st32_rwb(s, cond, datalo, addend, addrlo); + tcg_out_st32_12(s, cond, datahi, addend, 4); + } + break; + } +} + +static inline void tcg_out_qemu_st_direct(TCGContext *s, TCGMemOp opc, + TCGReg datalo, TCGReg datahi, + TCGReg addrlo) +{ + TCGMemOp bswap = opc & MO_BSWAP; + + switch (opc & MO_SIZE) { + case MO_8: + tcg_out_st8_12(s, COND_AL, datalo, addrlo, 0); + break; + case MO_16: + if (bswap) { + tcg_out_bswap16st(s, COND_AL, TCG_REG_R0, datalo); + tcg_out_st16_8(s, COND_AL, TCG_REG_R0, addrlo, 0); + } else { + tcg_out_st16_8(s, COND_AL, datalo, addrlo, 0); + } + break; + case MO_32: + default: + if (bswap) { + tcg_out_bswap32(s, COND_AL, TCG_REG_R0, datalo); + tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addrlo, 0); + } else { + tcg_out_st32_12(s, COND_AL, datalo, addrlo, 0); + } + break; + case MO_64: + /* Avoid strd for user-only emulation, to handle unaligned. */ + if (bswap) { + tcg_out_bswap32(s, COND_AL, TCG_REG_R0, datahi); + tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addrlo, 0); + tcg_out_bswap32(s, COND_AL, TCG_REG_R0, datalo); + tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addrlo, 4); + } else if (USING_SOFTMMU && use_armv6_instructions + && (datalo & 1) == 0 && datahi == datalo + 1) { + tcg_out_strd_8(s, COND_AL, datalo, addrlo, 0); + } else { + tcg_out_st32_12(s, COND_AL, datalo, addrlo, 0); + tcg_out_st32_12(s, COND_AL, datahi, addrlo, 4); + } + break; + } +} + +static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64) +{ + TCGReg addrlo, datalo, datahi, addrhi __attribute__((unused)); + TCGMemOpIdx oi; + TCGMemOp opc; +#ifdef CONFIG_SOFTMMU + int mem_index; + TCGReg addend; + tcg_insn_unit *label_ptr; +#endif + + datalo = *args++; + datahi = (is64 ? *args++ : 0); + addrlo = *args++; + addrhi = (TARGET_LONG_BITS == 64 ? *args++ : 0); + oi = *args++; + opc = get_memop(oi); + +#ifdef CONFIG_SOFTMMU + mem_index = get_mmuidx(oi); + addend = tcg_out_tlb_read(s, addrlo, addrhi, opc & MO_SIZE, mem_index, 0); + + tcg_out_qemu_st_index(s, COND_EQ, opc, datalo, datahi, addrlo, addend); + + /* The conditional call must come last, as we're going to return here. */ + label_ptr = s->code_ptr; + tcg_out_bl_noaddr(s, COND_NE); + + add_qemu_ldst_label(s, false, oi, datalo, datahi, addrlo, addrhi, + s->code_ptr, label_ptr); +#else /* !CONFIG_SOFTMMU */ + if (guest_base) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP, guest_base); + tcg_out_qemu_st_index(s, COND_AL, opc, datalo, + datahi, addrlo, TCG_REG_TMP); + } else { + tcg_out_qemu_st_direct(s, opc, datalo, datahi, addrlo); + } +#endif +} + +static tcg_insn_unit *tb_ret_addr; + +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, + const TCGArg *args, const int *const_args) +{ + TCGArg a0, a1, a2, a3, a4, a5; + int c; + + switch (opc) { + case INDEX_op_exit_tb: + tcg_out_movi32(s, COND_AL, TCG_REG_R0, args[0]); + tcg_out_goto(s, COND_AL, tb_ret_addr); + break; + case INDEX_op_goto_tb: + if (s->tb_jmp_offset) { + /* Direct jump method */ + s->tb_jmp_offset[args[0]] = tcg_current_code_size(s); + tcg_out_b_noaddr(s, COND_AL); + } else { + /* Indirect jump method */ + intptr_t ptr = (intptr_t)(s->tb_next + args[0]); + tcg_out_movi32(s, COND_AL, TCG_REG_R0, ptr & ~0xfff); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_R0, ptr & 0xfff); + } + s->tb_next_offset[args[0]] = tcg_current_code_size(s); + break; + case INDEX_op_br: + tcg_out_goto_label(s, COND_AL, arg_label(args[0])); + break; + + case INDEX_op_ld8u_i32: + tcg_out_ld8u(s, COND_AL, args[0], args[1], args[2]); + break; + case INDEX_op_ld8s_i32: + tcg_out_ld8s(s, COND_AL, args[0], args[1], args[2]); + break; + case INDEX_op_ld16u_i32: + tcg_out_ld16u(s, COND_AL, args[0], args[1], args[2]); + break; + case INDEX_op_ld16s_i32: + tcg_out_ld16s(s, COND_AL, args[0], args[1], args[2]); + break; + case INDEX_op_ld_i32: + tcg_out_ld32u(s, COND_AL, args[0], args[1], args[2]); + break; + case INDEX_op_st8_i32: + tcg_out_st8(s, COND_AL, args[0], args[1], args[2]); + break; + case INDEX_op_st16_i32: + tcg_out_st16(s, COND_AL, args[0], args[1], args[2]); + break; + case INDEX_op_st_i32: + tcg_out_st32(s, COND_AL, args[0], args[1], args[2]); + break; + + case INDEX_op_movcond_i32: + /* Constraints mean that v2 is always in the same register as dest, + * so we only need to do "if condition passed, move v1 to dest". + */ + tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, + args[1], args[2], const_args[2]); + tcg_out_dat_rIK(s, tcg_cond_to_arm_cond[args[5]], ARITH_MOV, + ARITH_MVN, args[0], 0, args[3], const_args[3]); + break; + case INDEX_op_add_i32: + tcg_out_dat_rIN(s, COND_AL, ARITH_ADD, ARITH_SUB, + args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_sub_i32: + if (const_args[1]) { + if (const_args[2]) { + tcg_out_movi32(s, COND_AL, args[0], args[1] - args[2]); + } else { + tcg_out_dat_rI(s, COND_AL, ARITH_RSB, + args[0], args[2], args[1], 1); + } + } else { + tcg_out_dat_rIN(s, COND_AL, ARITH_SUB, ARITH_ADD, + args[0], args[1], args[2], const_args[2]); + } + break; + case INDEX_op_and_i32: + tcg_out_dat_rIK(s, COND_AL, ARITH_AND, ARITH_BIC, + args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_andc_i32: + tcg_out_dat_rIK(s, COND_AL, ARITH_BIC, ARITH_AND, + args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_or_i32: + c = ARITH_ORR; + goto gen_arith; + case INDEX_op_xor_i32: + c = ARITH_EOR; + /* Fall through. */ + gen_arith: + tcg_out_dat_rI(s, COND_AL, c, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_add2_i32: + a0 = args[0], a1 = args[1], a2 = args[2]; + a3 = args[3], a4 = args[4], a5 = args[5]; + if (a0 == a3 || (a0 == a5 && !const_args[5])) { + a0 = TCG_REG_TMP; + } + tcg_out_dat_rIN(s, COND_AL, ARITH_ADD | TO_CPSR, ARITH_SUB | TO_CPSR, + a0, a2, a4, const_args[4]); + tcg_out_dat_rIK(s, COND_AL, ARITH_ADC, ARITH_SBC, + a1, a3, a5, const_args[5]); + tcg_out_mov_reg(s, COND_AL, args[0], a0); + break; + case INDEX_op_sub2_i32: + a0 = args[0], a1 = args[1], a2 = args[2]; + a3 = args[3], a4 = args[4], a5 = args[5]; + if ((a0 == a3 && !const_args[3]) || (a0 == a5 && !const_args[5])) { + a0 = TCG_REG_TMP; + } + if (const_args[2]) { + if (const_args[4]) { + tcg_out_movi32(s, COND_AL, a0, a4); + a4 = a0; + } + tcg_out_dat_rI(s, COND_AL, ARITH_RSB | TO_CPSR, a0, a4, a2, 1); + } else { + tcg_out_dat_rIN(s, COND_AL, ARITH_SUB | TO_CPSR, + ARITH_ADD | TO_CPSR, a0, a2, a4, const_args[4]); + } + if (const_args[3]) { + if (const_args[5]) { + tcg_out_movi32(s, COND_AL, a1, a5); + a5 = a1; + } + tcg_out_dat_rI(s, COND_AL, ARITH_RSC, a1, a5, a3, 1); + } else { + tcg_out_dat_rIK(s, COND_AL, ARITH_SBC, ARITH_ADC, + a1, a3, a5, const_args[5]); + } + tcg_out_mov_reg(s, COND_AL, args[0], a0); + break; + case INDEX_op_neg_i32: + tcg_out_dat_imm(s, COND_AL, ARITH_RSB, args[0], args[1], 0); + break; + case INDEX_op_not_i32: + tcg_out_dat_reg(s, COND_AL, + ARITH_MVN, args[0], 0, args[1], SHIFT_IMM_LSL(0)); + break; + case INDEX_op_mul_i32: + tcg_out_mul32(s, COND_AL, args[0], args[1], args[2]); + break; + case INDEX_op_mulu2_i32: + tcg_out_umull32(s, COND_AL, args[0], args[1], args[2], args[3]); + break; + case INDEX_op_muls2_i32: + tcg_out_smull32(s, COND_AL, args[0], args[1], args[2], args[3]); + break; + /* XXX: Perhaps args[2] & 0x1f is wrong */ + case INDEX_op_shl_i32: + c = const_args[2] ? + SHIFT_IMM_LSL(args[2] & 0x1f) : SHIFT_REG_LSL(args[2]); + goto gen_shift32; + case INDEX_op_shr_i32: + c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_LSR(args[2] & 0x1f) : + SHIFT_IMM_LSL(0) : SHIFT_REG_LSR(args[2]); + goto gen_shift32; + case INDEX_op_sar_i32: + c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_ASR(args[2] & 0x1f) : + SHIFT_IMM_LSL(0) : SHIFT_REG_ASR(args[2]); + goto gen_shift32; + case INDEX_op_rotr_i32: + c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_ROR(args[2] & 0x1f) : + SHIFT_IMM_LSL(0) : SHIFT_REG_ROR(args[2]); + /* Fall through. */ + gen_shift32: + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], c); + break; + + case INDEX_op_rotl_i32: + if (const_args[2]) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], + ((0x20 - args[2]) & 0x1f) ? + SHIFT_IMM_ROR((0x20 - args[2]) & 0x1f) : + SHIFT_IMM_LSL(0)); + } else { + tcg_out_dat_imm(s, COND_AL, ARITH_RSB, TCG_REG_TMP, args[2], 0x20); + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], + SHIFT_REG_ROR(TCG_REG_TMP)); + } + break; + + case INDEX_op_brcond_i32: + tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, + args[0], args[1], const_args[1]); + tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[2]], + arg_label(args[3])); + break; + case INDEX_op_brcond2_i32: + /* The resulting conditions are: + * TCG_COND_EQ --> a0 == a2 && a1 == a3, + * TCG_COND_NE --> (a0 != a2 && a1 == a3) || a1 != a3, + * TCG_COND_LT(U) --> (a0 < a2 && a1 == a3) || a1 < a3, + * TCG_COND_GE(U) --> (a0 >= a2 && a1 == a3) || (a1 >= a3 && a1 != a3), + * TCG_COND_LE(U) --> (a0 <= a2 && a1 == a3) || (a1 <= a3 && a1 != a3), + * TCG_COND_GT(U) --> (a0 > a2 && a1 == a3) || a1 > a3, + */ + tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, + args[1], args[3], const_args[3]); + tcg_out_dat_rIN(s, COND_EQ, ARITH_CMP, ARITH_CMN, 0, + args[0], args[2], const_args[2]); + tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[4]], + arg_label(args[5])); + break; + case INDEX_op_setcond_i32: + tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, + args[1], args[2], const_args[2]); + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[args[3]], + ARITH_MOV, args[0], 0, 1); + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(args[3])], + ARITH_MOV, args[0], 0, 0); + break; + case INDEX_op_setcond2_i32: + /* See brcond2_i32 comment */ + tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, + args[2], args[4], const_args[4]); + tcg_out_dat_rIN(s, COND_EQ, ARITH_CMP, ARITH_CMN, 0, + args[1], args[3], const_args[3]); + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[args[5]], + ARITH_MOV, args[0], 0, 1); + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(args[5])], + ARITH_MOV, args[0], 0, 0); + break; + + case INDEX_op_qemu_ld_i32: + tcg_out_qemu_ld(s, args, 0); + break; + case INDEX_op_qemu_ld_i64: + tcg_out_qemu_ld(s, args, 1); + break; + case INDEX_op_qemu_st_i32: + tcg_out_qemu_st(s, args, 0); + break; + case INDEX_op_qemu_st_i64: + tcg_out_qemu_st(s, args, 1); + break; + + case INDEX_op_bswap16_i32: + tcg_out_bswap16(s, COND_AL, args[0], args[1]); + break; + case INDEX_op_bswap32_i32: + tcg_out_bswap32(s, COND_AL, args[0], args[1]); + break; + + case INDEX_op_ext8s_i32: + tcg_out_ext8s(s, COND_AL, args[0], args[1]); + break; + case INDEX_op_ext16s_i32: + tcg_out_ext16s(s, COND_AL, args[0], args[1]); + break; + case INDEX_op_ext16u_i32: + tcg_out_ext16u(s, COND_AL, args[0], args[1]); + break; + + case INDEX_op_deposit_i32: + tcg_out_deposit(s, COND_AL, args[0], args[2], + args[3], args[4], const_args[2]); + break; + + case INDEX_op_div_i32: + tcg_out_sdiv(s, COND_AL, args[0], args[1], args[2]); + break; + case INDEX_op_divu_i32: + tcg_out_udiv(s, COND_AL, args[0], args[1], args[2]); + break; + + case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ + case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ + case INDEX_op_call: /* Always emitted via tcg_out_call. */ + default: + tcg_abort(); + } +} + +static const TCGTargetOpDef arm_op_defs[] = { + { INDEX_op_exit_tb, { } }, + { INDEX_op_goto_tb, { } }, + { INDEX_op_br, { } }, + + { INDEX_op_ld8u_i32, { "r", "r" } }, + { INDEX_op_ld8s_i32, { "r", "r" } }, + { INDEX_op_ld16u_i32, { "r", "r" } }, + { INDEX_op_ld16s_i32, { "r", "r" } }, + { INDEX_op_ld_i32, { "r", "r" } }, + { INDEX_op_st8_i32, { "r", "r" } }, + { INDEX_op_st16_i32, { "r", "r" } }, + { INDEX_op_st_i32, { "r", "r" } }, + + /* TODO: "r", "r", "ri" */ + { INDEX_op_add_i32, { "r", "r", "rIN" } }, + { INDEX_op_sub_i32, { "r", "rI", "rIN" } }, + { INDEX_op_mul_i32, { "r", "r", "r" } }, + { INDEX_op_mulu2_i32, { "r", "r", "r", "r" } }, + { INDEX_op_muls2_i32, { "r", "r", "r", "r" } }, + { INDEX_op_and_i32, { "r", "r", "rIK" } }, + { INDEX_op_andc_i32, { "r", "r", "rIK" } }, + { INDEX_op_or_i32, { "r", "r", "rI" } }, + { INDEX_op_xor_i32, { "r", "r", "rI" } }, + { INDEX_op_neg_i32, { "r", "r" } }, + { INDEX_op_not_i32, { "r", "r" } }, + + { INDEX_op_shl_i32, { "r", "r", "ri" } }, + { INDEX_op_shr_i32, { "r", "r", "ri" } }, + { INDEX_op_sar_i32, { "r", "r", "ri" } }, + { INDEX_op_rotl_i32, { "r", "r", "ri" } }, + { INDEX_op_rotr_i32, { "r", "r", "ri" } }, + + { INDEX_op_brcond_i32, { "r", "rIN" } }, + { INDEX_op_setcond_i32, { "r", "r", "rIN" } }, + { INDEX_op_movcond_i32, { "r", "r", "rIN", "rIK", "0" } }, + + { INDEX_op_add2_i32, { "r", "r", "r", "r", "rIN", "rIK" } }, + { INDEX_op_sub2_i32, { "r", "r", "rI", "rI", "rIN", "rIK" } }, + { INDEX_op_brcond2_i32, { "r", "r", "rIN", "rIN" } }, + { INDEX_op_setcond2_i32, { "r", "r", "r", "rIN", "rIN" } }, + +#if TARGET_LONG_BITS == 32 + { INDEX_op_qemu_ld_i32, { "r", "l" } }, + { INDEX_op_qemu_ld_i64, { "r", "r", "l" } }, + { INDEX_op_qemu_st_i32, { "s", "s" } }, + { INDEX_op_qemu_st_i64, { "s", "s", "s" } }, +#else + { INDEX_op_qemu_ld_i32, { "r", "l", "l" } }, + { INDEX_op_qemu_ld_i64, { "r", "r", "l", "l" } }, + { INDEX_op_qemu_st_i32, { "s", "s", "s" } }, + { INDEX_op_qemu_st_i64, { "s", "s", "s", "s" } }, +#endif + + { INDEX_op_bswap16_i32, { "r", "r" } }, + { INDEX_op_bswap32_i32, { "r", "r" } }, + + { INDEX_op_ext8s_i32, { "r", "r" } }, + { INDEX_op_ext16s_i32, { "r", "r" } }, + { INDEX_op_ext16u_i32, { "r", "r" } }, + + { INDEX_op_deposit_i32, { "r", "0", "rZ" } }, + + { INDEX_op_div_i32, { "r", "r", "r" } }, + { INDEX_op_divu_i32, { "r", "r", "r" } }, + + { -1 }, +}; + +static void tcg_target_init(TCGContext *s) +{ + /* Only probe for the platform and capabilities if we havn't already + determined maximum values at compile time. */ +#ifndef use_idiv_instructions + { + unsigned long hwcap = qemu_getauxval(AT_HWCAP); + use_idiv_instructions = (hwcap & HWCAP_ARM_IDIVA) != 0; + } +#endif + if (__ARM_ARCH < 7) { + const char *pl = (const char *)qemu_getauxval(AT_PLATFORM); + if (pl != NULL && pl[0] == 'v' && pl[1] >= '4' && pl[1] <= '9') { + arm_arch = pl[1] - '0'; + } + } + + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff); + tcg_regset_set32(tcg_target_call_clobber_regs, 0, + (1 << TCG_REG_R0) | + (1 << TCG_REG_R1) | + (1 << TCG_REG_R2) | + (1 << TCG_REG_R3) | + (1 << TCG_REG_R12) | + (1 << TCG_REG_R14)); + + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_PC); + + tcg_add_target_add_op_defs(arm_op_defs); +} + +static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg arg, + TCGReg arg1, intptr_t arg2) +{ + tcg_out_ld32u(s, COND_AL, arg, arg1, arg2); +} + +static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, + TCGReg arg1, intptr_t arg2) +{ + tcg_out_st32(s, COND_AL, arg, arg1, arg2); +} + +static inline void tcg_out_mov(TCGContext *s, TCGType type, + TCGReg ret, TCGReg arg) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, ret, 0, arg, SHIFT_IMM_LSL(0)); +} + +static inline void tcg_out_movi(TCGContext *s, TCGType type, + TCGReg ret, tcg_target_long arg) +{ + tcg_out_movi32(s, COND_AL, ret, arg); +} + +/* Compute frame size via macros, to share between tcg_target_qemu_prologue + and tcg_register_jit. */ + +#define PUSH_SIZE ((11 - 4 + 1 + 1) * sizeof(tcg_target_long)) + +#define FRAME_SIZE \ + ((PUSH_SIZE \ + + TCG_STATIC_CALL_ARGS_SIZE \ + + CPU_TEMP_BUF_NLONGS * sizeof(long) \ + + TCG_TARGET_STACK_ALIGN - 1) \ + & -TCG_TARGET_STACK_ALIGN) + +static void tcg_target_qemu_prologue(TCGContext *s) +{ + int stack_addend; + + /* Calling convention requires us to save r4-r11 and lr. */ + /* stmdb sp!, { r4 - r11, lr } */ + tcg_out32(s, (COND_AL << 28) | 0x092d4ff0); + + /* Reserve callee argument and tcg temp space. */ + stack_addend = FRAME_SIZE - PUSH_SIZE; + + tcg_out_dat_rI(s, COND_AL, ARITH_SUB, TCG_REG_CALL_STACK, + TCG_REG_CALL_STACK, stack_addend, 1); + tcg_set_frame(s, TCG_REG_CALL_STACK, TCG_STATIC_CALL_ARGS_SIZE, + CPU_TEMP_BUF_NLONGS * sizeof(long)); + + tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); + + tcg_out_bx(s, COND_AL, tcg_target_call_iarg_regs[1]); + tb_ret_addr = s->code_ptr; + + /* Epilogue. We branch here via tb_ret_addr. */ + tcg_out_dat_rI(s, COND_AL, ARITH_ADD, TCG_REG_CALL_STACK, + TCG_REG_CALL_STACK, stack_addend, 1); + + /* ldmia sp!, { r4 - r11, pc } */ + tcg_out32(s, (COND_AL << 28) | 0x08bd8ff0); +} + +typedef struct { + DebugFrameHeader h; + uint8_t fde_def_cfa[4]; + uint8_t fde_reg_ofs[18]; +} DebugFrame; + +#define ELF_HOST_MACHINE EM_ARM + +/* We're expecting a 2 byte uleb128 encoded value. */ +QEMU_BUILD_BUG_ON(FRAME_SIZE >= (1 << 14)); + +static const DebugFrame debug_frame = { + .h.cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ + .h.cie.id = -1, + .h.cie.version = 1, + .h.cie.code_align = 1, + .h.cie.data_align = 0x7c, /* sleb128 -4 */ + .h.cie.return_column = 14, + + /* Total FDE size does not include the "len" member. */ + .h.fde.len = sizeof(DebugFrame) - offsetof(DebugFrame, h.fde.cie_offset), + + .fde_def_cfa = { + 12, 13, /* DW_CFA_def_cfa sp, ... */ + (FRAME_SIZE & 0x7f) | 0x80, /* ... uleb128 FRAME_SIZE */ + (FRAME_SIZE >> 7) + }, + .fde_reg_ofs = { + /* The following must match the stmdb in the prologue. */ + 0x8e, 1, /* DW_CFA_offset, lr, -4 */ + 0x8b, 2, /* DW_CFA_offset, r11, -8 */ + 0x8a, 3, /* DW_CFA_offset, r10, -12 */ + 0x89, 4, /* DW_CFA_offset, r9, -16 */ + 0x88, 5, /* DW_CFA_offset, r8, -20 */ + 0x87, 6, /* DW_CFA_offset, r7, -24 */ + 0x86, 7, /* DW_CFA_offset, r6, -28 */ + 0x85, 8, /* DW_CFA_offset, r5, -32 */ + 0x84, 9, /* DW_CFA_offset, r4, -36 */ + } +}; + +void tcg_register_jit(void *buf, size_t buf_size) +{ + tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); +} diff --git a/qemu/tcg/i386/tcg-target.c b/qemu/tcg/i386/tcg-target.c deleted file mode 100644 index 887f22f67..000000000 --- a/qemu/tcg/i386/tcg-target.c +++ /dev/null @@ -1,2451 +0,0 @@ -/* - * Tiny Code Generator for QEMU - * - * Copyright (c) 2008 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "tcg-be-ldst.h" - -#ifndef NDEBUG -static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { -#if TCG_TARGET_REG_BITS == 64 - "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi", - "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", -#else - "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi", -#endif -}; -#endif - -static const int tcg_target_reg_alloc_order[] = { -#if TCG_TARGET_REG_BITS == 64 - TCG_REG_RBP, - TCG_REG_RBX, - TCG_REG_R12, - TCG_REG_R13, - TCG_REG_R14, - TCG_REG_R15, - TCG_REG_R10, - TCG_REG_R11, - TCG_REG_R9, - TCG_REG_R8, - TCG_REG_RCX, - TCG_REG_RDX, - TCG_REG_RSI, - TCG_REG_RDI, - TCG_REG_RAX, -#else - TCG_REG_EBX, - TCG_REG_ESI, - TCG_REG_EDI, - TCG_REG_EBP, - TCG_REG_ECX, - TCG_REG_EDX, - TCG_REG_EAX, -#endif -}; - -static const int tcg_target_call_iarg_regs[] = { -#if TCG_TARGET_REG_BITS == 64 -#if defined(_WIN64) - TCG_REG_RCX, - TCG_REG_RDX, -#else - TCG_REG_RDI, - TCG_REG_RSI, - TCG_REG_RDX, - TCG_REG_RCX, -#endif - TCG_REG_R8, - TCG_REG_R9, -#else - /* 32 bit mode uses stack based calling convention (GCC default). */ -#endif -}; - -static const int tcg_target_call_oarg_regs[] = { - TCG_REG_EAX, -#if TCG_TARGET_REG_BITS == 32 - TCG_REG_EDX -#endif -}; - -/* Constants we accept. */ -#define TCG_CT_CONST_S32 0x100 -#define TCG_CT_CONST_U32 0x200 -#define TCG_CT_CONST_I32 0x400 - -/* Registers used with L constraint, which are the first argument - registers on x86_64, and two random call clobbered registers on - i386. */ -#if TCG_TARGET_REG_BITS == 64 -# define TCG_REG_L0 tcg_target_call_iarg_regs[0] -# define TCG_REG_L1 tcg_target_call_iarg_regs[1] -#else -# define TCG_REG_L0 TCG_REG_EAX -# define TCG_REG_L1 TCG_REG_EDX -#endif - -/* The host compiler should supply to enable runtime features - detection, as we're not going to go so far as our own inline assembly. - If not available, default values will be assumed. */ -#if defined(CONFIG_CPUID_H) -#include -#endif - -/* For 32-bit, we are going to attempt to determine at runtime whether cmov - is available. */ -#if TCG_TARGET_REG_BITS == 64 -# define have_cmov 1 -#elif defined(CONFIG_CPUID_H) && defined(bit_CMOV) -static bool have_cmov; -#else -# define have_cmov 0 -#endif - -/* If bit_MOVBE is defined in cpuid.h (added in GCC version 4.6), we are - going to attempt to determine at runtime whether movbe is available. */ -#if defined(CONFIG_CPUID_H) && defined(bit_MOVBE) -static bool have_movbe; -#else -# define have_movbe 0 -#endif - -/* We need this symbol in tcg-target.h, and we can't properly conditionalize - it there. Therefore we always define the variable. */ -bool have_bmi1; - -#if defined(CONFIG_CPUID_H) && defined(bit_BMI2) -static bool have_bmi2; -#else -# define have_bmi2 0 -#endif - -static tcg_insn_unit *tb_ret_addr; - -static void patch_reloc(tcg_insn_unit *code_ptr, int type, - intptr_t value, intptr_t addend) -{ - value += addend; - switch(type) { - case R_386_PC32: - value -= (uintptr_t)code_ptr; - if (value != (int32_t)value) { - tcg_abort(); - } - tcg_patch32(code_ptr, value); - break; - case R_386_PC8: - value -= (uintptr_t)code_ptr; - if (value != (int8_t)value) { - tcg_abort(); - } - tcg_patch8(code_ptr, value); - break; - default: - tcg_abort(); - } -} - -/* parse target specific constraints */ -static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) -{ - const char *ct_str; - - ct_str = *pct_str; - switch(ct_str[0]) { - case 'a': - ct->ct |= TCG_CT_REG; - tcg_regset_set_reg(ct->u.regs, TCG_REG_EAX); - break; - case 'b': - ct->ct |= TCG_CT_REG; - tcg_regset_set_reg(ct->u.regs, TCG_REG_EBX); - break; - case 'c': - case_c: - ct->ct |= TCG_CT_REG; - tcg_regset_set_reg(ct->u.regs, TCG_REG_ECX); - break; - case 'd': - ct->ct |= TCG_CT_REG; - tcg_regset_set_reg(ct->u.regs, TCG_REG_EDX); - break; - case 'S': - ct->ct |= TCG_CT_REG; - tcg_regset_set_reg(ct->u.regs, TCG_REG_ESI); - break; - case 'D': - ct->ct |= TCG_CT_REG; - tcg_regset_set_reg(ct->u.regs, TCG_REG_EDI); - break; - case 'q': - ct->ct |= TCG_CT_REG; - if (TCG_TARGET_REG_BITS == 64) { - tcg_regset_set32(ct->u.regs, 0, 0xffff); - } else { - tcg_regset_set32(ct->u.regs, 0, 0xf); - } - break; - case 'Q': - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xf); - break; - case 'r': - case_r: - ct->ct |= TCG_CT_REG; - if (TCG_TARGET_REG_BITS == 64) { - tcg_regset_set32(ct->u.regs, 0, 0xffff); - } else { - tcg_regset_set32(ct->u.regs, 0, 0xff); - } - break; - case 'C': - /* With SHRX et al, we need not use ECX as shift count register. */ - if (have_bmi2) { - goto case_r; - } else { - goto case_c; - } - - /* qemu_ld/st address constraint */ - case 'L': - ct->ct |= TCG_CT_REG; - if (TCG_TARGET_REG_BITS == 64) { - tcg_regset_set32(ct->u.regs, 0, 0xffff); - } else { - tcg_regset_set32(ct->u.regs, 0, 0xff); - } - tcg_regset_reset_reg(ct->u.regs, TCG_REG_L0); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_L1); - break; - - case 'e': - ct->ct |= TCG_CT_CONST_S32; - break; - case 'Z': - ct->ct |= TCG_CT_CONST_U32; - break; - case 'I': - ct->ct |= TCG_CT_CONST_I32; - break; - - default: - return -1; - } - ct_str++; - *pct_str = ct_str; - return 0; -} - -/* test if a constant matches the constraint */ -static inline int tcg_target_const_match(tcg_target_long val, TCGType type, - const TCGArgConstraint *arg_ct) -{ - int ct = arg_ct->ct; - if (ct & TCG_CT_CONST) { - return 1; - } - if ((ct & TCG_CT_CONST_S32) && val == (int32_t)val) { - return 1; - } - if ((ct & TCG_CT_CONST_U32) && val == (uint32_t)val) { - return 1; - } - if ((ct & TCG_CT_CONST_I32) && ~val == (int32_t)~val) { - return 1; - } - return 0; -} - -#if TCG_TARGET_REG_BITS == 64 -# define LOWREGMASK(x) ((x) & 7) -#else -# define LOWREGMASK(x) (x) -#endif - -#define P_EXT 0x100 /* 0x0f opcode prefix */ -#define P_EXT38 0x200 /* 0x0f 0x38 opcode prefix */ -#define P_DATA16 0x400 /* 0x66 opcode prefix */ -#if TCG_TARGET_REG_BITS == 64 -# define P_ADDR32 0x800 /* 0x67 opcode prefix */ -# define P_REXW 0x1000 /* Set REX.W = 1 */ -# define P_REXB_R 0x2000 /* REG field as byte register */ -# define P_REXB_RM 0x4000 /* R/M field as byte register */ -# define P_GS 0x8000 /* gs segment override */ -#else -# define P_ADDR32 0 -# define P_REXW 0 -# define P_REXB_R 0 -# define P_REXB_RM 0 -# define P_GS 0 -#endif -#define P_SIMDF3 0x10000 /* 0xf3 opcode prefix */ -#define P_SIMDF2 0x20000 /* 0xf2 opcode prefix */ - -#define OPC_ARITH_EvIz (0x81) -#define OPC_ARITH_EvIb (0x83) -#define OPC_ARITH_GvEv (0x03) /* ... plus (ARITH_FOO << 3) */ -#define OPC_ANDN (0xf2 | P_EXT38) -#define OPC_ADD_GvEv (OPC_ARITH_GvEv | (ARITH_ADD << 3)) -#define OPC_BSWAP (0xc8 | P_EXT) -#define OPC_CALL_Jz (0xe8) -#define OPC_CMOVCC (0x40 | P_EXT) /* ... plus condition code */ -#define OPC_CMP_GvEv (OPC_ARITH_GvEv | (ARITH_CMP << 3)) -#define OPC_DEC_r32 (0x48) -#define OPC_IMUL_GvEv (0xaf | P_EXT) -#define OPC_IMUL_GvEvIb (0x6b) -#define OPC_IMUL_GvEvIz (0x69) -#define OPC_INC_r32 (0x40) -#define OPC_JCC_long (0x80 | P_EXT) /* ... plus condition code */ -#define OPC_JCC_short (0x70) /* ... plus condition code */ -#define OPC_JMP_long (0xe9) -#define OPC_JMP_short (0xeb) -#define OPC_LEA (0x8d) -#define OPC_MOVB_EvGv (0x88) /* stores, more or less */ -#define OPC_MOVL_EvGv (0x89) /* stores, more or less */ -#define OPC_MOVL_GvEv (0x8b) /* loads, more or less */ -#define OPC_MOVB_EvIz (0xc6) -#define OPC_MOVL_EvIz (0xc7) -#define OPC_MOVL_Iv (0xb8) -#define OPC_MOVBE_GyMy (0xf0 | P_EXT38) -#define OPC_MOVBE_MyGy (0xf1 | P_EXT38) -#define OPC_MOVSBL (0xbe | P_EXT) -#define OPC_MOVSWL (0xbf | P_EXT) -#define OPC_MOVSLQ (0x63 | P_REXW) -#define OPC_MOVZBL (0xb6 | P_EXT) -#define OPC_MOVZWL (0xb7 | P_EXT) -#define OPC_POP_r32 (0x58) -#define OPC_PUSH_r32 (0x50) -#define OPC_PUSH_Iv (0x68) -#define OPC_PUSH_Ib (0x6a) -#define OPC_RET (0xc3) -#define OPC_SETCC (0x90 | P_EXT | P_REXB_RM) /* ... plus cc */ -#define OPC_SHIFT_1 (0xd1) -#define OPC_SHIFT_Ib (0xc1) -#define OPC_SHIFT_cl (0xd3) -#define OPC_SARX (0xf7 | P_EXT38 | P_SIMDF3) -#define OPC_SHLX (0xf7 | P_EXT38 | P_DATA16) -#define OPC_SHRX (0xf7 | P_EXT38 | P_SIMDF2) -#define OPC_TESTL (0x85) -#define OPC_XCHG_ax_r32 (0x90) - -#define OPC_GRP3_Ev (0xf7) -#define OPC_GRP5 (0xff) - -/* Group 1 opcode extensions for 0x80-0x83. - These are also used as modifiers for OPC_ARITH. */ -#define ARITH_ADD 0 -#define ARITH_OR 1 -#define ARITH_ADC 2 -#define ARITH_SBB 3 -#define ARITH_AND 4 -#define ARITH_SUB 5 -#define ARITH_XOR 6 -#define ARITH_CMP 7 - -/* Group 2 opcode extensions for 0xc0, 0xc1, 0xd0-0xd3. */ -#define SHIFT_ROL 0 -#define SHIFT_ROR 1 -#define SHIFT_SHL 4 -#define SHIFT_SHR 5 -#define SHIFT_SAR 7 - -/* Group 3 opcode extensions for 0xf6, 0xf7. To be used with OPC_GRP3. */ -#define EXT3_NOT 2 -#define EXT3_NEG 3 -#define EXT3_MUL 4 -#define EXT3_IMUL 5 -#define EXT3_DIV 6 -#define EXT3_IDIV 7 - -/* Group 5 opcode extensions for 0xff. To be used with OPC_GRP5. */ -#define EXT5_INC_Ev 0 -#define EXT5_DEC_Ev 1 -#define EXT5_CALLN_Ev 2 -#define EXT5_JMPN_Ev 4 - -/* Condition codes to be added to OPC_JCC_{long,short}. */ -#define JCC_JMP (-1) -#define JCC_JO 0x0 -#define JCC_JNO 0x1 -#define JCC_JB 0x2 -#define JCC_JAE 0x3 -#define JCC_JE 0x4 -#define JCC_JNE 0x5 -#define JCC_JBE 0x6 -#define JCC_JA 0x7 -#define JCC_JS 0x8 -#define JCC_JNS 0x9 -#define JCC_JP 0xa -#define JCC_JNP 0xb -#define JCC_JL 0xc -#define JCC_JGE 0xd -#define JCC_JLE 0xe -#define JCC_JG 0xf - -static const uint8_t tcg_cond_to_jcc[] = { - [TCG_COND_EQ] = JCC_JE, - [TCG_COND_NE] = JCC_JNE, - [TCG_COND_LT] = JCC_JL, - [TCG_COND_GE] = JCC_JGE, - [TCG_COND_LE] = JCC_JLE, - [TCG_COND_GT] = JCC_JG, - [TCG_COND_LTU] = JCC_JB, - [TCG_COND_GEU] = JCC_JAE, - [TCG_COND_LEU] = JCC_JBE, - [TCG_COND_GTU] = JCC_JA, -}; - -#if TCG_TARGET_REG_BITS == 64 -static void tcg_out_opc(TCGContext *s, int opc, int r, int rm, int x) -{ - int rex; - - if (opc & P_GS) { - tcg_out8(s, 0x65); - } - if (opc & P_DATA16) { - /* We should never be asking for both 16 and 64-bit operation. */ - assert((opc & P_REXW) == 0); - tcg_out8(s, 0x66); - } - if (opc & P_ADDR32) { - tcg_out8(s, 0x67); - } - - rex = 0; - rex |= (opc & P_REXW) ? 0x8 : 0x0; /* REX.W */ - rex |= (r & 8) >> 1; /* REX.R */ - rex |= (x & 8) >> 2; /* REX.X */ - rex |= (rm & 8) >> 3; /* REX.B */ - - /* P_REXB_{R,RM} indicates that the given register is the low byte. - For %[abcd]l we need no REX prefix, but for %{si,di,bp,sp}l we do, - as otherwise the encoding indicates %[abcd]h. Note that the values - that are ORed in merely indicate that the REX byte must be present; - those bits get discarded in output. */ - rex |= opc & (r >= 4 ? P_REXB_R : 0); - rex |= opc & (rm >= 4 ? P_REXB_RM : 0); - - if (rex) { - tcg_out8(s, (uint8_t)(rex | 0x40)); - } - - if (opc & (P_EXT | P_EXT38)) { - tcg_out8(s, 0x0f); - if (opc & P_EXT38) { - tcg_out8(s, 0x38); - } - } - - tcg_out8(s, opc); -} -#else -static void tcg_out_opc(TCGContext *s, int opc) -{ - if (opc & P_DATA16) { - tcg_out8(s, 0x66); - } - if (opc & (P_EXT | P_EXT38)) { - tcg_out8(s, 0x0f); - if (opc & P_EXT38) { - tcg_out8(s, 0x38); - } - } - tcg_out8(s, opc); -} -/* Discard the register arguments to tcg_out_opc early, so as not to penalize - the 32-bit compilation paths. This method works with all versions of gcc, - whereas relying on optimization may not be able to exclude them. */ -#define tcg_out_opc(s, opc, r, rm, x) (tcg_out_opc)(s, opc) -#endif - -static void tcg_out_modrm(TCGContext *s, int opc, int r, int rm) -{ - tcg_out_opc(s, opc, r, rm, 0); - tcg_out8(s, 0xc0 | (LOWREGMASK(r) << 3) | LOWREGMASK(rm)); -} - -static void tcg_out_vex_modrm(TCGContext *s, int opc, int r, int v, int rm) -{ - int tmp; - - if ((opc & (P_REXW | P_EXT | P_EXT38)) || (rm & 8)) { - /* Three byte VEX prefix. */ - tcg_out8(s, 0xc4); - - /* VEX.m-mmmm */ - if (opc & P_EXT38) { - tmp = 2; - } else if (opc & P_EXT) { - tmp = 1; - } else { - tcg_abort(); - } - tmp |= 0x40; /* VEX.X */ - tmp |= (r & 8 ? 0 : 0x80); /* VEX.R */ - tmp |= (rm & 8 ? 0 : 0x20); /* VEX.B */ - tcg_out8(s, tmp); - - tmp = (opc & P_REXW ? 0x80 : 0); /* VEX.W */ - } else { - /* Two byte VEX prefix. */ - tcg_out8(s, 0xc5); - - tmp = (r & 8 ? 0 : 0x80); /* VEX.R */ - } - /* VEX.pp */ - if (opc & P_DATA16) { - tmp |= 1; /* 0x66 */ - } else if (opc & P_SIMDF3) { - tmp |= 2; /* 0xf3 */ - } else if (opc & P_SIMDF2) { - tmp |= 3; /* 0xf2 */ - } - tmp |= (~v & 15) << 3; /* VEX.vvvv */ - tcg_out8(s, tmp); - tcg_out8(s, opc); - tcg_out8(s, 0xc0 | (LOWREGMASK(r) << 3) | LOWREGMASK(rm)); -} - -/* Output an opcode with a full "rm + (index<code_ptr + 5 + ~rm; - intptr_t disp = offset - pc; - if (disp == (int32_t)disp) { - tcg_out_opc(s, opc, r, 0, 0); - tcg_out8(s, (LOWREGMASK(r) << 3) | 5); - tcg_out32(s, disp); - return; - } - - /* Try for an absolute address encoding. This requires the - use of the MODRM+SIB encoding and is therefore larger than - rip-relative addressing. */ - if (offset == (int32_t)offset) { - tcg_out_opc(s, opc, r, 0, 0); - tcg_out8(s, (LOWREGMASK(r) << 3) | 4); - tcg_out8(s, (4 << 3) | 5); - tcg_out32(s, offset); - return; - } - - /* ??? The memory isn't directly addressable. */ - tcg_abort(); - } else { - /* Absolute address. */ - tcg_out_opc(s, opc, r, 0, 0); - tcg_out8(s, (r << 3) | 5); - tcg_out32(s, offset); - return; - } - } - - /* Find the length of the immediate addend. Note that the encoding - that would be used for (%ebp) indicates absolute addressing. */ - if (rm < 0) { - mod = 0, len = 4, rm = 5; - } else if (offset == 0 && LOWREGMASK(rm) != TCG_REG_EBP) { - mod = 0, len = 0; - } else if (offset == (int8_t)offset) { - mod = 0x40, len = 1; - } else { - mod = 0x80, len = 4; - } - - /* Use a single byte MODRM format if possible. Note that the encoding - that would be used for %esp is the escape to the two byte form. */ - if (index < 0 && LOWREGMASK(rm) != TCG_REG_ESP) { - /* Single byte MODRM format. */ - tcg_out_opc(s, opc, r, rm, 0); - tcg_out8(s, mod | (LOWREGMASK(r) << 3) | LOWREGMASK(rm)); - } else { - /* Two byte MODRM+SIB format. */ - - /* Note that the encoding that would place %esp into the index - field indicates no index register. In 64-bit mode, the REX.X - bit counts, so %r12 can be used as the index. */ - if (index < 0) { - index = 4; - } else { - assert(index != TCG_REG_ESP); - } - - tcg_out_opc(s, opc, r, rm, index); - tcg_out8(s, mod | (LOWREGMASK(r) << 3) | 4); - tcg_out8(s, (shift << 6) | (LOWREGMASK(index) << 3) | LOWREGMASK(rm)); - } - - if (len == 1) { - tcg_out8(s, offset); - } else if (len == 4) { - tcg_out32(s, offset); - } -} - -/* A simplification of the above with no index or shift. */ -static inline void tcg_out_modrm_offset(TCGContext *s, int opc, int r, - int rm, intptr_t offset) -{ - tcg_out_modrm_sib_offset(s, opc, r, rm, -1, 0, offset); -} - -/* Generate dest op= src. Uses the same ARITH_* codes as tgen_arithi. */ -static inline void tgen_arithr(TCGContext *s, int subop, int dest, int src) -{ - /* Propagate an opcode prefix, such as P_REXW. */ - int ext = subop & ~0x7; - subop &= 0x7; - - tcg_out_modrm(s, OPC_ARITH_GvEv + (subop << 3) + ext, dest, src); -} - -static inline void tcg_out_mov(TCGContext *s, TCGType type, - TCGReg ret, TCGReg arg) -{ - if (arg != ret) { - int opc = OPC_MOVL_GvEv + (type == TCG_TYPE_I64 ? P_REXW : 0); - tcg_out_modrm(s, opc, ret, arg); - } -} - -static void tcg_out_movi(TCGContext *s, TCGType type, - TCGReg ret, tcg_target_long arg) -{ - tcg_target_long diff; - - if (arg == 0) { - tgen_arithr(s, ARITH_XOR, ret, ret); - return; - } - if (arg == (uint32_t)arg || type == TCG_TYPE_I32) { - tcg_out_opc(s, OPC_MOVL_Iv + LOWREGMASK(ret), 0, ret, 0); - tcg_out32(s, arg); - return; - } - if (arg == (int32_t)arg) { - tcg_out_modrm(s, OPC_MOVL_EvIz + P_REXW, 0, ret); - tcg_out32(s, arg); - return; - } - - /* Try a 7 byte pc-relative lea before the 10 byte movq. */ - diff = arg - ((uintptr_t)s->code_ptr + 7); - if (diff == (int32_t)diff) { - tcg_out_opc(s, OPC_LEA | P_REXW, ret, 0, 0); - tcg_out8(s, (LOWREGMASK(ret) << 3) | 5); - tcg_out32(s, diff); - return; - } - - tcg_out_opc(s, OPC_MOVL_Iv + P_REXW + LOWREGMASK(ret), 0, ret, 0); - tcg_out64(s, arg); -} - -static inline void tcg_out_pushi(TCGContext *s, tcg_target_long val) -{ - if (val == (int8_t)val) { - tcg_out_opc(s, OPC_PUSH_Ib, 0, 0, 0); - tcg_out8(s, val); - } else if (val == (int32_t)val) { - tcg_out_opc(s, OPC_PUSH_Iv, 0, 0, 0); - tcg_out32(s, val); - } else { - tcg_abort(); - } -} - -static inline void tcg_out_push(TCGContext *s, int reg) -{ - tcg_out_opc(s, OPC_PUSH_r32 + LOWREGMASK(reg), 0, reg, 0); -} - -static inline void tcg_out_pop(TCGContext *s, int reg) -{ - tcg_out_opc(s, OPC_POP_r32 + LOWREGMASK(reg), 0, reg, 0); -} - -static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, - TCGReg arg1, intptr_t arg2) -{ - int opc = OPC_MOVL_GvEv + (type == TCG_TYPE_I64 ? P_REXW : 0); - tcg_out_modrm_offset(s, opc, ret, arg1, arg2); -} - -static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, - TCGReg arg1, intptr_t arg2) -{ - int opc = OPC_MOVL_EvGv + (type == TCG_TYPE_I64 ? P_REXW : 0); - tcg_out_modrm_offset(s, opc, arg, arg1, arg2); -} - -static inline void tcg_out_sti(TCGContext *s, TCGType type, TCGReg base, - tcg_target_long ofs, tcg_target_long val) -{ - int opc = OPC_MOVL_EvIz + (type == TCG_TYPE_I64 ? P_REXW : 0); - tcg_out_modrm_offset(s, opc, 0, base, ofs); - tcg_out32(s, val); -} - -static void tcg_out_shifti(TCGContext *s, int subopc, int reg, int count) -{ - /* Propagate an opcode prefix, such as P_DATA16. */ - int ext = subopc & ~0x7; - subopc &= 0x7; - - if (count == 1) { - tcg_out_modrm(s, OPC_SHIFT_1 + ext, subopc, reg); - } else { - tcg_out_modrm(s, OPC_SHIFT_Ib + ext, subopc, reg); - tcg_out8(s, count); - } -} - -static inline void tcg_out_bswap32(TCGContext *s, int reg) -{ - tcg_out_opc(s, OPC_BSWAP + LOWREGMASK(reg), 0, reg, 0); -} - -static inline void tcg_out_rolw_8(TCGContext *s, int reg) -{ - tcg_out_shifti(s, SHIFT_ROL + P_DATA16, reg, 8); -} - -static inline void tcg_out_ext8u(TCGContext *s, int dest, int src) -{ - /* movzbl */ - assert(src < 4 || TCG_TARGET_REG_BITS == 64); - tcg_out_modrm(s, OPC_MOVZBL + P_REXB_RM, dest, src); -} - -static void tcg_out_ext8s(TCGContext *s, int dest, int src, int rexw) -{ - /* movsbl */ - assert(src < 4 || TCG_TARGET_REG_BITS == 64); - tcg_out_modrm(s, OPC_MOVSBL + P_REXB_RM + rexw, dest, src); -} - -static inline void tcg_out_ext16u(TCGContext *s, int dest, int src) -{ - /* movzwl */ - tcg_out_modrm(s, OPC_MOVZWL, dest, src); -} - -static inline void tcg_out_ext16s(TCGContext *s, int dest, int src, int rexw) -{ - /* movsw[lq] */ - tcg_out_modrm(s, OPC_MOVSWL + rexw, dest, src); -} - -static inline void tcg_out_ext32u(TCGContext *s, int dest, int src) -{ - /* 32-bit mov zero extends. */ - tcg_out_modrm(s, OPC_MOVL_GvEv, dest, src); -} - -static inline void tcg_out_ext32s(TCGContext *s, int dest, int src) -{ - tcg_out_modrm(s, OPC_MOVSLQ, dest, src); -} - -static inline void tcg_out_bswap64(TCGContext *s, int reg) -{ - tcg_out_opc(s, OPC_BSWAP + P_REXW + LOWREGMASK(reg), 0, reg, 0); -} - -static void tgen_arithi(TCGContext *s, int c, int r0, - tcg_target_long val, int cf) -{ - int rexw = 0; - - if (TCG_TARGET_REG_BITS == 64) { - rexw = c & -8; - c &= 7; - } - - /* ??? While INC is 2 bytes shorter than ADDL $1, they also induce - partial flags update stalls on Pentium4 and are not recommended - by current Intel optimization manuals. */ - if (!cf && (c == ARITH_ADD || c == ARITH_SUB) && (val == 1 || val == -1)) { - int is_inc = (c == ARITH_ADD) ^ (val < 0); - if (TCG_TARGET_REG_BITS == 64) { - /* The single-byte increment encodings are re-tasked as the - REX prefixes. Use the MODRM encoding. */ - tcg_out_modrm(s, OPC_GRP5 + rexw, - (is_inc ? EXT5_INC_Ev : EXT5_DEC_Ev), r0); - } else { - tcg_out8(s, (is_inc ? OPC_INC_r32 : OPC_DEC_r32) + r0); - } - return; - } - - if (c == ARITH_AND) { - if (TCG_TARGET_REG_BITS == 64) { - if (val == 0xffffffffu) { - tcg_out_ext32u(s, r0, r0); - return; - } - if (val == (uint32_t)val) { - /* AND with no high bits set can use a 32-bit operation. */ - rexw = 0; - } - } - if (val == 0xffu && (r0 < 4 || TCG_TARGET_REG_BITS == 64)) { - tcg_out_ext8u(s, r0, r0); - return; - } - if (val == 0xffffu) { - tcg_out_ext16u(s, r0, r0); - return; - } - } - - if (val == (int8_t)val) { - tcg_out_modrm(s, OPC_ARITH_EvIb + rexw, c, r0); - tcg_out8(s, val); - return; - } - if (rexw == 0 || val == (int32_t)val) { - tcg_out_modrm(s, OPC_ARITH_EvIz + rexw, c, r0); - tcg_out32(s, val); - return; - } - - tcg_abort(); -} - -static void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) -{ - if (val != 0) { - tgen_arithi(s, ARITH_ADD + P_REXW, reg, val, 0); - } -} - -/* Use SMALL != 0 to force a short forward branch. */ -static void tcg_out_jxx(TCGContext *s, int opc, TCGLabel *l, int small) -{ - int32_t val, val1; - - if (l->has_value) { - val = tcg_pcrel_diff(s, l->u.value_ptr); - val1 = val - 2; - if ((int8_t)val1 == val1) { - if (opc == -1) { - tcg_out8(s, OPC_JMP_short); - } else { - tcg_out8(s, OPC_JCC_short + opc); - } - tcg_out8(s, val1); - } else { - if (small) { - tcg_abort(); - } - if (opc == -1) { - tcg_out8(s, OPC_JMP_long); - tcg_out32(s, val - 5); - } else { - tcg_out_opc(s, OPC_JCC_long + opc, 0, 0, 0); - tcg_out32(s, val - 6); - } - } - } else if (small) { - if (opc == -1) { - tcg_out8(s, OPC_JMP_short); - } else { - tcg_out8(s, OPC_JCC_short + opc); - } - tcg_out_reloc(s, s->code_ptr, R_386_PC8, l, -1); - s->code_ptr += 1; - } else { - if (opc == -1) { - tcg_out8(s, OPC_JMP_long); - } else { - tcg_out_opc(s, OPC_JCC_long + opc, 0, 0, 0); - } - tcg_out_reloc(s, s->code_ptr, R_386_PC32, l, -4); - s->code_ptr += 4; - } -} - -static void tcg_out_cmp(TCGContext *s, TCGArg arg1, TCGArg arg2, - int const_arg2, int rexw) -{ - if (const_arg2) { - if (arg2 == 0) { - /* test r, r */ - tcg_out_modrm(s, OPC_TESTL + rexw, arg1, arg1); - } else { - tgen_arithi(s, ARITH_CMP + rexw, arg1, arg2, 0); - } - } else { - tgen_arithr(s, ARITH_CMP + rexw, arg1, arg2); - } -} - -static void tcg_out_brcond32(TCGContext *s, TCGCond cond, - TCGArg arg1, TCGArg arg2, int const_arg2, - TCGLabel *label, int small) -{ - tcg_out_cmp(s, arg1, arg2, const_arg2, 0); - tcg_out_jxx(s, tcg_cond_to_jcc[cond], label, small); -} - -#if TCG_TARGET_REG_BITS == 64 -static void tcg_out_brcond64(TCGContext *s, TCGCond cond, - TCGArg arg1, TCGArg arg2, int const_arg2, - TCGLabel *label, int small) -{ - tcg_out_cmp(s, arg1, arg2, const_arg2, P_REXW); - tcg_out_jxx(s, tcg_cond_to_jcc[cond], label, small); -} -#else -/* XXX: we implement it at the target level to avoid having to - handle cross basic blocks temporaries */ -static void tcg_out_brcond2(TCGContext *s, const TCGArg *args, - const int *const_args, int small) -{ - TCGLabel *label_next = gen_new_label(); - TCGLabel *label_this = arg_label(args[5]); - - switch(args[4]) { - case TCG_COND_EQ: - tcg_out_brcond32(s, TCG_COND_NE, args[0], args[2], const_args[2], - label_next, 1); - tcg_out_brcond32(s, TCG_COND_EQ, args[1], args[3], const_args[3], - label_this, small); - break; - case TCG_COND_NE: - tcg_out_brcond32(s, TCG_COND_NE, args[0], args[2], const_args[2], - label_this, small); - tcg_out_brcond32(s, TCG_COND_NE, args[1], args[3], const_args[3], - label_this, small); - break; - case TCG_COND_LT: - tcg_out_brcond32(s, TCG_COND_LT, args[1], args[3], const_args[3], - label_this, small); - tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond32(s, TCG_COND_LTU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_LE: - tcg_out_brcond32(s, TCG_COND_LT, args[1], args[3], const_args[3], - label_this, small); - tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond32(s, TCG_COND_LEU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_GT: - tcg_out_brcond32(s, TCG_COND_GT, args[1], args[3], const_args[3], - label_this, small); - tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond32(s, TCG_COND_GTU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_GE: - tcg_out_brcond32(s, TCG_COND_GT, args[1], args[3], const_args[3], - label_this, small); - tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond32(s, TCG_COND_GEU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_LTU: - tcg_out_brcond32(s, TCG_COND_LTU, args[1], args[3], const_args[3], - label_this, small); - tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond32(s, TCG_COND_LTU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_LEU: - tcg_out_brcond32(s, TCG_COND_LTU, args[1], args[3], const_args[3], - label_this, small); - tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond32(s, TCG_COND_LEU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_GTU: - tcg_out_brcond32(s, TCG_COND_GTU, args[1], args[3], const_args[3], - label_this, small); - tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond32(s, TCG_COND_GTU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_GEU: - tcg_out_brcond32(s, TCG_COND_GTU, args[1], args[3], const_args[3], - label_this, small); - tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond32(s, TCG_COND_GEU, args[0], args[2], const_args[2], - label_this, small); - break; - default: - tcg_abort(); - } - tcg_out_label(s, label_next, s->code_ptr); -} -#endif - -static void tcg_out_setcond32(TCGContext *s, TCGCond cond, TCGArg dest, - TCGArg arg1, TCGArg arg2, int const_arg2) -{ - tcg_out_cmp(s, arg1, arg2, const_arg2, 0); - tcg_out_modrm(s, OPC_SETCC | tcg_cond_to_jcc[cond], 0, dest); - tcg_out_ext8u(s, dest, dest); -} - -#if TCG_TARGET_REG_BITS == 64 -static void tcg_out_setcond64(TCGContext *s, TCGCond cond, TCGArg dest, - TCGArg arg1, TCGArg arg2, int const_arg2) -{ - tcg_out_cmp(s, arg1, arg2, const_arg2, P_REXW); - tcg_out_modrm(s, OPC_SETCC | tcg_cond_to_jcc[cond], 0, dest); - tcg_out_ext8u(s, dest, dest); -} -#else -static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, - const int *const_args) -{ - TCGArg new_args[6]; - TCGLabel *label_true, *label_over; - - memcpy(new_args, args+1, 5*sizeof(TCGArg)); - - if (args[0] == args[1] || args[0] == args[2] - || (!const_args[3] && args[0] == args[3]) - || (!const_args[4] && args[0] == args[4])) { - /* When the destination overlaps with one of the argument - registers, don't do anything tricky. */ - label_true = gen_new_label(); - label_over = gen_new_label(); - - new_args[5] = label_arg(label_true); - tcg_out_brcond2(s, new_args, const_args+1, 1); - - tcg_out_movi(s, TCG_TYPE_I32, args[0], 0); - tcg_out_jxx(s, JCC_JMP, label_over, 1); - tcg_out_label(s, label_true, s->code_ptr); - - tcg_out_movi(s, TCG_TYPE_I32, args[0], 1); - tcg_out_label(s, label_over, s->code_ptr); - } else { - /* When the destination does not overlap one of the arguments, - clear the destination first, jump if cond false, and emit an - increment in the true case. This results in smaller code. */ - - tcg_out_movi(s, TCG_TYPE_I32, args[0], 0); - - label_over = gen_new_label(); - new_args[4] = tcg_invert_cond(new_args[4]); - new_args[5] = label_arg(label_over); - tcg_out_brcond2(s, new_args, const_args+1, 1); - - tgen_arithi(s, ARITH_ADD, args[0], 1, 0); - tcg_out_label(s, label_over, s->code_ptr); - } -} -#endif - -static void tcg_out_movcond32(TCGContext *s, TCGCond cond, TCGArg dest, - TCGArg c1, TCGArg c2, int const_c2, - TCGArg v1) -{ - tcg_out_cmp(s, c1, c2, const_c2, 0); - if (have_cmov) { - tcg_out_modrm(s, OPC_CMOVCC | tcg_cond_to_jcc[cond], dest, v1); - } else { - TCGLabel *over = gen_new_label(); - tcg_out_jxx(s, tcg_cond_to_jcc[tcg_invert_cond(cond)], over, 1); - tcg_out_mov(s, TCG_TYPE_I32, dest, v1); - tcg_out_label(s, over, s->code_ptr); - } -} - -#if TCG_TARGET_REG_BITS == 64 -static void tcg_out_movcond64(TCGContext *s, TCGCond cond, TCGArg dest, - TCGArg c1, TCGArg c2, int const_c2, - TCGArg v1) -{ - tcg_out_cmp(s, c1, c2, const_c2, P_REXW); - tcg_out_modrm(s, OPC_CMOVCC | tcg_cond_to_jcc[cond] | P_REXW, dest, v1); -} -#endif - -static void tcg_out_branch(TCGContext *s, int call, tcg_insn_unit *dest) -{ - intptr_t disp = tcg_pcrel_diff(s, dest) - 5; - - if (disp == (int32_t)disp) { - tcg_out_opc(s, call ? OPC_CALL_Jz : OPC_JMP_long, 0, 0, 0); - tcg_out32(s, disp); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R10, (uintptr_t)dest); - tcg_out_modrm(s, OPC_GRP5, - call ? EXT5_CALLN_Ev : EXT5_JMPN_Ev, TCG_REG_R10); - } -} - -static inline void tcg_out_call(TCGContext *s, tcg_insn_unit *dest) -{ - tcg_out_branch(s, 1, dest); -} - -static void tcg_out_jmp(TCGContext *s, tcg_insn_unit *dest) -{ - tcg_out_branch(s, 0, dest); -} - -#if defined(CONFIG_SOFTMMU) -/* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, - * int mmu_idx, uintptr_t ra) - */ -static void * const qemu_ld_helpers[16] = { - [MO_UB] = helper_ret_ldub_mmu, - [MO_LEUW] = helper_le_lduw_mmu, - [MO_LEUL] = helper_le_ldul_mmu, - [MO_LEQ] = helper_le_ldq_mmu, - [MO_BEUW] = helper_be_lduw_mmu, - [MO_BEUL] = helper_be_ldul_mmu, - [MO_BEQ] = helper_be_ldq_mmu, -}; - -/* helper signature: helper_ret_st_mmu(CPUState *env, target_ulong addr, - * uintxx_t val, int mmu_idx, uintptr_t ra) - */ -static void * const qemu_st_helpers[16] = { - [MO_UB] = helper_ret_stb_mmu, - [MO_LEUW] = helper_le_stw_mmu, - [MO_LEUL] = helper_le_stl_mmu, - [MO_LEQ] = helper_le_stq_mmu, - [MO_BEUW] = helper_be_stw_mmu, - [MO_BEUL] = helper_be_stl_mmu, - [MO_BEQ] = helper_be_stq_mmu, -}; - -/* Perform the TLB load and compare. - - Inputs: - ADDRLO and ADDRHI contain the low and high part of the address. - - MEM_INDEX and S_BITS are the memory context and log2 size of the load. - - WHICH is the offset into the CPUTLBEntry structure of the slot to read. - This should be offsetof addr_read or addr_write. - - Outputs: - LABEL_PTRS is filled with 1 (32-bit addresses) or 2 (64-bit addresses) - positions of the displacements of forward jumps to the TLB miss case. - - Second argument register is loaded with the low part of the address. - In the TLB hit case, it has been adjusted as indicated by the TLB - and so is a host address. In the TLB miss case, it continues to - hold a guest address. - - First argument register is clobbered. */ - -static inline void tcg_out_tlb_load(TCGContext *s, TCGReg addrlo, TCGReg addrhi, - int mem_index, TCGMemOp s_bits, - tcg_insn_unit **label_ptr, int which) -{ - const TCGReg r0 = TCG_REG_L0; - const TCGReg r1 = TCG_REG_L1; - TCGType ttype = TCG_TYPE_I32; - TCGType htype = TCG_TYPE_I32; - int trexw = 0, hrexw = 0; - - if (TCG_TARGET_REG_BITS == 64) { - if (TARGET_LONG_BITS == 64) { - ttype = TCG_TYPE_I64; - trexw = P_REXW; - } - if (TCG_TYPE_PTR == TCG_TYPE_I64) { - htype = TCG_TYPE_I64; - hrexw = P_REXW; - } - } - - tcg_out_mov(s, htype, r0, addrlo); - tcg_out_mov(s, ttype, r1, addrlo); - - tcg_out_shifti(s, SHIFT_SHR + hrexw, r0, - TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - - tgen_arithi(s, ARITH_AND + trexw, r1, - TARGET_PAGE_MASK | ((1 << s_bits) - 1), 0); - tgen_arithi(s, ARITH_AND + hrexw, r0, - (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS, 0); - - tcg_out_modrm_sib_offset(s, OPC_LEA + hrexw, r0, TCG_AREG0, r0, 0, - offsetof(CPUArchState, tlb_table[mem_index][0]) - + which); - - /* cmp 0(r0), r1 */ - tcg_out_modrm_offset(s, OPC_CMP_GvEv + trexw, r1, r0, 0); - - /* Prepare for both the fast path add of the tlb addend, and the slow - path function argument setup. There are two cases worth note: - For 32-bit guest and x86_64 host, MOVL zero-extends the guest address - before the fastpath ADDQ below. For 64-bit guest and x32 host, MOVQ - copies the entire guest address for the slow path, while truncation - for the 32-bit host happens with the fastpath ADDL below. */ - tcg_out_mov(s, ttype, r1, addrlo); - - /* jne slow_path */ - tcg_out_opc(s, OPC_JCC_long + JCC_JNE, 0, 0, 0); - label_ptr[0] = s->code_ptr; - s->code_ptr += 4; - - if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { - /* cmp 4(r0), addrhi */ - tcg_out_modrm_offset(s, OPC_CMP_GvEv, addrhi, r0, 4); - - /* jne slow_path */ - tcg_out_opc(s, OPC_JCC_long + JCC_JNE, 0, 0, 0); - label_ptr[1] = s->code_ptr; - s->code_ptr += 4; - } - - /* TLB Hit. */ - - /* add addend(r0), r1 */ - tcg_out_modrm_offset(s, OPC_ADD_GvEv + hrexw, r1, r0, - offsetof(CPUTLBEntry, addend) - which); -} - -/* - * Record the context of a call to the out of line helper code for the slow path - * for a load or store, so that we can later generate the correct helper code - */ -static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOpIdx oi, - TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - tcg_insn_unit *raddr, - tcg_insn_unit **label_ptr) -{ - TCGLabelQemuLdst *label = new_ldst_label(s); - - label->is_ld = is_ld; - label->oi = oi; - label->datalo_reg = datalo; - label->datahi_reg = datahi; - label->addrlo_reg = addrlo; - label->addrhi_reg = addrhi; - label->raddr = raddr; - label->label_ptr[0] = label_ptr[0]; - if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { - label->label_ptr[1] = label_ptr[1]; - } -} - -/* - * Generate code for the slow path for a load at the end of block - */ -static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) -{ - TCGMemOpIdx oi = l->oi; - TCGMemOp opc = get_memop(oi); - TCGReg data_reg; - tcg_insn_unit **label_ptr = &l->label_ptr[0]; - - /* resolve label address */ - tcg_patch32(label_ptr[0], s->code_ptr - label_ptr[0] - 4); - if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { - tcg_patch32(label_ptr[1], s->code_ptr - label_ptr[1] - 4); - } - - if (TCG_TARGET_REG_BITS == 32) { - int ofs = 0; - - tcg_out_st(s, TCG_TYPE_PTR, TCG_AREG0, TCG_REG_ESP, ofs); - ofs += 4; - - tcg_out_st(s, TCG_TYPE_I32, l->addrlo_reg, TCG_REG_ESP, ofs); - ofs += 4; - - if (TARGET_LONG_BITS == 64) { - tcg_out_st(s, TCG_TYPE_I32, l->addrhi_reg, TCG_REG_ESP, ofs); - ofs += 4; - } - - tcg_out_sti(s, TCG_TYPE_I32, TCG_REG_ESP, ofs, oi); - ofs += 4; - - tcg_out_sti(s, TCG_TYPE_PTR, TCG_REG_ESP, ofs, (uintptr_t)l->raddr); - } else { - tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0); - /* The second argument is already loaded with addrlo. */ - tcg_out_movi(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[2], oi); - tcg_out_movi(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[3], - (uintptr_t)l->raddr); - } - - tcg_out_call(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SIZE)]); - - data_reg = l->datalo_reg; - switch (opc & MO_SSIZE) { - case MO_SB: - tcg_out_ext8s(s, data_reg, TCG_REG_EAX, P_REXW); - break; - case MO_SW: - tcg_out_ext16s(s, data_reg, TCG_REG_EAX, P_REXW); - break; -#if TCG_TARGET_REG_BITS == 64 - case MO_SL: - tcg_out_ext32s(s, data_reg, TCG_REG_EAX); - break; -#endif - case MO_UB: - case MO_UW: - /* Note that the helpers have zero-extended to tcg_target_long. */ - case MO_UL: - tcg_out_mov(s, TCG_TYPE_I32, data_reg, TCG_REG_EAX); - break; - case MO_Q: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_mov(s, TCG_TYPE_I64, data_reg, TCG_REG_RAX); - } else if (data_reg == TCG_REG_EDX) { - /* xchg %edx, %eax */ - tcg_out_opc(s, OPC_XCHG_ax_r32 + TCG_REG_EDX, 0, 0, 0); - tcg_out_mov(s, TCG_TYPE_I32, l->datahi_reg, TCG_REG_EAX); - } else { - tcg_out_mov(s, TCG_TYPE_I32, data_reg, TCG_REG_EAX); - tcg_out_mov(s, TCG_TYPE_I32, l->datahi_reg, TCG_REG_EDX); - } - break; - default: - tcg_abort(); - } - - /* Jump to the code corresponding to next IR of qemu_st */ - tcg_out_jmp(s, l->raddr); -} - -/* - * Generate code for the slow path for a store at the end of block - */ -static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) -{ - TCGMemOpIdx oi = l->oi; - TCGMemOp opc = get_memop(oi); - TCGMemOp s_bits = opc & MO_SIZE; - tcg_insn_unit **label_ptr = &l->label_ptr[0]; - TCGReg retaddr; - - /* resolve label address */ - tcg_patch32(label_ptr[0], s->code_ptr - label_ptr[0] - 4); - if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { - tcg_patch32(label_ptr[1], s->code_ptr - label_ptr[1] - 4); - } - - if (TCG_TARGET_REG_BITS == 32) { - int ofs = 0; - - tcg_out_st(s, TCG_TYPE_PTR, TCG_AREG0, TCG_REG_ESP, ofs); - ofs += 4; - - tcg_out_st(s, TCG_TYPE_I32, l->addrlo_reg, TCG_REG_ESP, ofs); - ofs += 4; - - if (TARGET_LONG_BITS == 64) { - tcg_out_st(s, TCG_TYPE_I32, l->addrhi_reg, TCG_REG_ESP, ofs); - ofs += 4; - } - - tcg_out_st(s, TCG_TYPE_I32, l->datalo_reg, TCG_REG_ESP, ofs); - ofs += 4; - - if (s_bits == MO_64) { - tcg_out_st(s, TCG_TYPE_I32, l->datahi_reg, TCG_REG_ESP, ofs); - ofs += 4; - } - - tcg_out_sti(s, TCG_TYPE_I32, TCG_REG_ESP, ofs, oi); - ofs += 4; - - retaddr = TCG_REG_EAX; - tcg_out_movi(s, TCG_TYPE_PTR, retaddr, (uintptr_t)l->raddr); - tcg_out_st(s, TCG_TYPE_PTR, retaddr, TCG_REG_ESP, ofs); - } else { - tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0); - /* The second argument is already loaded with addrlo. */ - tcg_out_mov(s, (s_bits == MO_64 ? TCG_TYPE_I64 : TCG_TYPE_I32), - tcg_target_call_iarg_regs[2], l->datalo_reg); - tcg_out_movi(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[3], oi); - - if (ARRAY_SIZE(tcg_target_call_iarg_regs) > 4) { - retaddr = tcg_target_call_iarg_regs[4]; - tcg_out_movi(s, TCG_TYPE_PTR, retaddr, (uintptr_t)l->raddr); - } else { - retaddr = TCG_REG_RAX; - tcg_out_movi(s, TCG_TYPE_PTR, retaddr, (uintptr_t)l->raddr); - tcg_out_st(s, TCG_TYPE_PTR, retaddr, TCG_REG_ESP, - TCG_TARGET_CALL_STACK_OFFSET); - } - } - - /* "Tail call" to the helper, with the return address back inline. */ - tcg_out_push(s, retaddr); - tcg_out_jmp(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)]); -} -#elif defined(__x86_64__) && defined(__linux__) -# include -# include - -int arch_prctl(int code, unsigned long addr); - -static int guest_base_flags; -static inline void setup_guest_base_seg(void) -{ - if (arch_prctl(ARCH_SET_GS, GUEST_BASE) == 0) { - guest_base_flags = P_GS; - } -} -#else -# define guest_base_flags 0 -static inline void setup_guest_base_seg(void) { } -#endif /* SOFTMMU */ - -static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg base, int index, intptr_t ofs, - int seg, TCGMemOp memop) -{ - const TCGMemOp real_bswap = memop & MO_BSWAP; - TCGMemOp bswap = real_bswap; - int movop = OPC_MOVL_GvEv; - - if (have_movbe && real_bswap) { - bswap = 0; - movop = OPC_MOVBE_GyMy; - } - - switch (memop & MO_SSIZE) { - case MO_UB: - tcg_out_modrm_sib_offset(s, OPC_MOVZBL + seg, datalo, - base, index, 0, ofs); - break; - case MO_SB: - tcg_out_modrm_sib_offset(s, OPC_MOVSBL + P_REXW + seg, datalo, - base, index, 0, ofs); - break; - case MO_UW: - tcg_out_modrm_sib_offset(s, OPC_MOVZWL + seg, datalo, - base, index, 0, ofs); - if (real_bswap) { - tcg_out_rolw_8(s, datalo); - } - break; - case MO_SW: - if (real_bswap) { - if (have_movbe) { - tcg_out_modrm_sib_offset(s, OPC_MOVBE_GyMy + P_DATA16 + seg, - datalo, base, index, 0, ofs); - } else { - tcg_out_modrm_sib_offset(s, OPC_MOVZWL + seg, datalo, - base, index, 0, ofs); - tcg_out_rolw_8(s, datalo); - } - tcg_out_modrm(s, OPC_MOVSWL + P_REXW, datalo, datalo); - } else { - tcg_out_modrm_sib_offset(s, OPC_MOVSWL + P_REXW + seg, - datalo, base, index, 0, ofs); - } - break; - case MO_UL: - tcg_out_modrm_sib_offset(s, movop + seg, datalo, base, index, 0, ofs); - if (bswap) { - tcg_out_bswap32(s, datalo); - } - break; -#if TCG_TARGET_REG_BITS == 64 - case MO_SL: - if (real_bswap) { - tcg_out_modrm_sib_offset(s, movop + seg, datalo, - base, index, 0, ofs); - if (bswap) { - tcg_out_bswap32(s, datalo); - } - tcg_out_ext32s(s, datalo, datalo); - } else { - tcg_out_modrm_sib_offset(s, OPC_MOVSLQ + seg, datalo, - base, index, 0, ofs); - } - break; -#endif - case MO_Q: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_modrm_sib_offset(s, movop + P_REXW + seg, datalo, - base, index, 0, ofs); - if (bswap) { - tcg_out_bswap64(s, datalo); - } - } else { - if (real_bswap) { - int t = datalo; - datalo = datahi; - datahi = t; - } - if (base != datalo) { - tcg_out_modrm_sib_offset(s, movop + seg, datalo, - base, index, 0, ofs); - tcg_out_modrm_sib_offset(s, movop + seg, datahi, - base, index, 0, ofs + 4); - } else { - tcg_out_modrm_sib_offset(s, movop + seg, datahi, - base, index, 0, ofs + 4); - tcg_out_modrm_sib_offset(s, movop + seg, datalo, - base, index, 0, ofs); - } - if (bswap) { - tcg_out_bswap32(s, datalo); - tcg_out_bswap32(s, datahi); - } - } - break; - default: - tcg_abort(); - } -} - -/* XXX: qemu_ld and qemu_st could be modified to clobber only EDX and - EAX. It will be useful once fixed registers globals are less - common. */ -static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is64) -{ - TCGReg datalo, datahi, addrlo; - TCGReg addrhi __attribute__((unused)); - TCGMemOpIdx oi; - TCGMemOp opc; -#if defined(CONFIG_SOFTMMU) - int mem_index; - TCGMemOp s_bits; - tcg_insn_unit *label_ptr[2]; -#endif - - datalo = *args++; - datahi = (TCG_TARGET_REG_BITS == 32 && is64 ? *args++ : 0); - addrlo = *args++; - addrhi = (TARGET_LONG_BITS > TCG_TARGET_REG_BITS ? *args++ : 0); - oi = *args++; - opc = get_memop(oi); - -#if defined(CONFIG_SOFTMMU) - mem_index = get_mmuidx(oi); - s_bits = opc & MO_SIZE; - - tcg_out_tlb_load(s, addrlo, addrhi, mem_index, s_bits, - label_ptr, offsetof(CPUTLBEntry, addr_read)); - - /* TLB Hit. */ - tcg_out_qemu_ld_direct(s, datalo, datahi, TCG_REG_L1, -1, 0, 0, opc); - - /* Record the current context of a load into ldst label */ - add_qemu_ldst_label(s, true, oi, datalo, datahi, addrlo, addrhi, - s->code_ptr, label_ptr); -#else - { - int32_t offset = GUEST_BASE; - TCGReg base = addrlo; - int index = -1; - int seg = 0; - - /* For a 32-bit guest, the high 32 bits may contain garbage. - We can do this with the ADDR32 prefix if we're not using - a guest base, or when using segmentation. Otherwise we - need to zero-extend manually. */ - if (GUEST_BASE == 0 || guest_base_flags) { - seg = guest_base_flags; - offset = 0; - if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { - seg |= P_ADDR32; - } - } else if (TCG_TARGET_REG_BITS == 64) { - if (TARGET_LONG_BITS == 32) { - tcg_out_ext32u(s, TCG_REG_L0, base); - base = TCG_REG_L0; - } - if (offset != GUEST_BASE) { - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_L1, GUEST_BASE); - index = TCG_REG_L1; - offset = 0; - } - } - - tcg_out_qemu_ld_direct(s, datalo, datahi, - base, index, offset, seg, opc); - } -#endif -} - -static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg base, intptr_t ofs, int seg, - TCGMemOp memop) -{ - /* ??? Ideally we wouldn't need a scratch register. For user-only, - we could perform the bswap twice to restore the original value - instead of moving to the scratch. But as it is, the L constraint - means that TCG_REG_L0 is definitely free here. */ - const TCGReg scratch = TCG_REG_L0; - const TCGMemOp real_bswap = memop & MO_BSWAP; - TCGMemOp bswap = real_bswap; - int movop = OPC_MOVL_EvGv; - - if (have_movbe && real_bswap) { - bswap = 0; - movop = OPC_MOVBE_MyGy; - } - - switch (memop & MO_SIZE) { - case MO_8: - /* In 32-bit mode, 8-bit stores can only happen from [abcd]x. - Use the scratch register if necessary. */ - if (TCG_TARGET_REG_BITS == 32 && datalo >= 4) { - tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo); - datalo = scratch; - } - tcg_out_modrm_offset(s, OPC_MOVB_EvGv + P_REXB_R + seg, - datalo, base, ofs); - break; - case MO_16: - if (bswap) { - tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo); - tcg_out_rolw_8(s, scratch); - datalo = scratch; - } - tcg_out_modrm_offset(s, movop + P_DATA16 + seg, datalo, base, ofs); - break; - case MO_32: - if (bswap) { - tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo); - tcg_out_bswap32(s, scratch); - datalo = scratch; - } - tcg_out_modrm_offset(s, movop + seg, datalo, base, ofs); - break; - case MO_64: - if (TCG_TARGET_REG_BITS == 64) { - if (bswap) { - tcg_out_mov(s, TCG_TYPE_I64, scratch, datalo); - tcg_out_bswap64(s, scratch); - datalo = scratch; - } - tcg_out_modrm_offset(s, movop + P_REXW + seg, datalo, base, ofs); - } else if (bswap) { - tcg_out_mov(s, TCG_TYPE_I32, scratch, datahi); - tcg_out_bswap32(s, scratch); - tcg_out_modrm_offset(s, OPC_MOVL_EvGv + seg, scratch, base, ofs); - tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo); - tcg_out_bswap32(s, scratch); - tcg_out_modrm_offset(s, OPC_MOVL_EvGv + seg, scratch, base, ofs+4); - } else { - if (real_bswap) { - int t = datalo; - datalo = datahi; - datahi = t; - } - tcg_out_modrm_offset(s, movop + seg, datalo, base, ofs); - tcg_out_modrm_offset(s, movop + seg, datahi, base, ofs+4); - } - break; - default: - tcg_abort(); - } -} - -static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64) -{ - TCGReg datalo, datahi, addrlo; - TCGReg addrhi __attribute__((unused)); - TCGMemOpIdx oi; - TCGMemOp opc; -#if defined(CONFIG_SOFTMMU) - int mem_index; - TCGMemOp s_bits; - tcg_insn_unit *label_ptr[2]; -#endif - - datalo = *args++; - datahi = (TCG_TARGET_REG_BITS == 32 && is64 ? *args++ : 0); - addrlo = *args++; - addrhi = (TARGET_LONG_BITS > TCG_TARGET_REG_BITS ? *args++ : 0); - oi = *args++; - opc = get_memop(oi); - -#if defined(CONFIG_SOFTMMU) - mem_index = get_mmuidx(oi); - s_bits = opc & MO_SIZE; - - tcg_out_tlb_load(s, addrlo, addrhi, mem_index, s_bits, - label_ptr, offsetof(CPUTLBEntry, addr_write)); - - /* TLB Hit. */ - tcg_out_qemu_st_direct(s, datalo, datahi, TCG_REG_L1, 0, 0, opc); - - /* Record the current context of a store into ldst label */ - add_qemu_ldst_label(s, false, oi, datalo, datahi, addrlo, addrhi, - s->code_ptr, label_ptr); -#else - { - int32_t offset = GUEST_BASE; - TCGReg base = addrlo; - int seg = 0; - - /* See comment in tcg_out_qemu_ld re zero-extension of addrlo. */ - if (GUEST_BASE == 0 || guest_base_flags) { - seg = guest_base_flags; - offset = 0; - if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { - seg |= P_ADDR32; - } - } else if (TCG_TARGET_REG_BITS == 64) { - /* ??? Note that we can't use the same SIB addressing scheme - as for loads, since we require L0 free for bswap. */ - if (offset != GUEST_BASE) { - if (TARGET_LONG_BITS == 32) { - tcg_out_ext32u(s, TCG_REG_L0, base); - base = TCG_REG_L0; - } - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_L1, GUEST_BASE); - tgen_arithr(s, ARITH_ADD + P_REXW, TCG_REG_L1, base); - base = TCG_REG_L1; - offset = 0; - } else if (TARGET_LONG_BITS == 32) { - tcg_out_ext32u(s, TCG_REG_L1, base); - base = TCG_REG_L1; - } - } - - tcg_out_qemu_st_direct(s, datalo, datahi, base, offset, seg, opc); - } -#endif -} - -static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, - const TCGArg *args, const int *const_args) -{ - int c, vexop, rexw = 0; - -#if TCG_TARGET_REG_BITS == 64 -# define OP_32_64(x) \ - case glue(glue(INDEX_op_, x), _i64): \ - rexw = P_REXW; /* FALLTHRU */ \ - case glue(glue(INDEX_op_, x), _i32) -#else -# define OP_32_64(x) \ - case glue(glue(INDEX_op_, x), _i32) -#endif - - switch(opc) { - case INDEX_op_exit_tb: - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_EAX, args[0]); - tcg_out_jmp(s, tb_ret_addr); - break; - case INDEX_op_goto_tb: - if (s->tb_jmp_offset) { - /* direct jump method */ - tcg_out8(s, OPC_JMP_long); /* jmp im */ - s->tb_jmp_offset[args[0]] = tcg_current_code_size(s); - tcg_out32(s, 0); - } else { - /* indirect jump method */ - tcg_out_modrm_offset(s, OPC_GRP5, EXT5_JMPN_Ev, -1, - (intptr_t)(s->tb_next + args[0])); - } - s->tb_next_offset[args[0]] = tcg_current_code_size(s); - break; - case INDEX_op_br: - tcg_out_jxx(s, JCC_JMP, arg_label(args[0]), 0); - break; - OP_32_64(ld8u): - /* Note that we can ignore REXW for the zero-extend to 64-bit. */ - tcg_out_modrm_offset(s, OPC_MOVZBL, args[0], args[1], args[2]); - break; - OP_32_64(ld8s): - tcg_out_modrm_offset(s, OPC_MOVSBL + rexw, args[0], args[1], args[2]); - break; - OP_32_64(ld16u): - /* Note that we can ignore REXW for the zero-extend to 64-bit. */ - tcg_out_modrm_offset(s, OPC_MOVZWL, args[0], args[1], args[2]); - break; - OP_32_64(ld16s): - tcg_out_modrm_offset(s, OPC_MOVSWL + rexw, args[0], args[1], args[2]); - break; -#if TCG_TARGET_REG_BITS == 64 - case INDEX_op_ld32u_i64: -#endif - case INDEX_op_ld_i32: - tcg_out_ld(s, TCG_TYPE_I32, args[0], args[1], args[2]); - break; - - OP_32_64(st8): - if (const_args[0]) { - tcg_out_modrm_offset(s, OPC_MOVB_EvIz, - 0, args[1], args[2]); - tcg_out8(s, args[0]); - } else { - tcg_out_modrm_offset(s, OPC_MOVB_EvGv | P_REXB_R, - args[0], args[1], args[2]); - } - break; - OP_32_64(st16): - if (const_args[0]) { - tcg_out_modrm_offset(s, OPC_MOVL_EvIz | P_DATA16, - 0, args[1], args[2]); - tcg_out16(s, args[0]); - } else { - tcg_out_modrm_offset(s, OPC_MOVL_EvGv | P_DATA16, - args[0], args[1], args[2]); - } - break; -#if TCG_TARGET_REG_BITS == 64 - case INDEX_op_st32_i64: -#endif - case INDEX_op_st_i32: - if (const_args[0]) { - tcg_out_modrm_offset(s, OPC_MOVL_EvIz, 0, args[1], args[2]); - tcg_out32(s, args[0]); - } else { - tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); - } - break; - - OP_32_64(add): - /* For 3-operand addition, use LEA. */ - if (args[0] != args[1]) { - TCGArg a0 = args[0], a1 = args[1], a2 = args[2], c3 = 0; - - if (const_args[2]) { - c3 = a2, a2 = -1; - } else if (a0 == a2) { - /* Watch out for dest = src + dest, since we've removed - the matching constraint on the add. */ - tgen_arithr(s, ARITH_ADD + rexw, a0, a1); - break; - } - - tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, a1, a2, 0, c3); - break; - } - c = ARITH_ADD; - goto gen_arith; - OP_32_64(sub): - c = ARITH_SUB; - goto gen_arith; - OP_32_64(and): - c = ARITH_AND; - goto gen_arith; - OP_32_64(or): - c = ARITH_OR; - goto gen_arith; - OP_32_64(xor): - c = ARITH_XOR; - goto gen_arith; - gen_arith: - if (const_args[2]) { - tgen_arithi(s, c + rexw, args[0], args[2], 0); - } else { - tgen_arithr(s, c + rexw, args[0], args[2]); - } - break; - - OP_32_64(andc): - if (const_args[2]) { - tcg_out_mov(s, rexw ? TCG_TYPE_I64 : TCG_TYPE_I32, - args[0], args[1]); - tgen_arithi(s, ARITH_AND + rexw, args[0], ~args[2], 0); - } else { - tcg_out_vex_modrm(s, OPC_ANDN + rexw, args[0], args[2], args[1]); - } - break; - - OP_32_64(mul): - if (const_args[2]) { - int32_t val; - val = args[2]; - if (val == (int8_t)val) { - tcg_out_modrm(s, OPC_IMUL_GvEvIb + rexw, args[0], args[0]); - tcg_out8(s, val); - } else { - tcg_out_modrm(s, OPC_IMUL_GvEvIz + rexw, args[0], args[0]); - tcg_out32(s, val); - } - } else { - tcg_out_modrm(s, OPC_IMUL_GvEv + rexw, args[0], args[2]); - } - break; - - OP_32_64(div2): - tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_IDIV, args[4]); - break; - OP_32_64(divu2): - tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_DIV, args[4]); - break; - - OP_32_64(shl): - c = SHIFT_SHL; - vexop = OPC_SHLX; - goto gen_shift_maybe_vex; - OP_32_64(shr): - c = SHIFT_SHR; - vexop = OPC_SHRX; - goto gen_shift_maybe_vex; - OP_32_64(sar): - c = SHIFT_SAR; - vexop = OPC_SARX; - goto gen_shift_maybe_vex; - OP_32_64(rotl): - c = SHIFT_ROL; - goto gen_shift; - OP_32_64(rotr): - c = SHIFT_ROR; - goto gen_shift; - gen_shift_maybe_vex: - if (have_bmi2 && !const_args[2]) { - tcg_out_vex_modrm(s, vexop + rexw, args[0], args[2], args[1]); - break; - } - /* FALLTHRU */ - gen_shift: - if (const_args[2]) { - tcg_out_shifti(s, c + rexw, args[0], args[2]); - } else { - tcg_out_modrm(s, OPC_SHIFT_cl + rexw, c, args[0]); - } - break; - - case INDEX_op_brcond_i32: - tcg_out_brcond32(s, args[2], args[0], args[1], const_args[1], - arg_label(args[3]), 0); - break; - case INDEX_op_setcond_i32: - tcg_out_setcond32(s, args[3], args[0], args[1], - args[2], const_args[2]); - break; - case INDEX_op_movcond_i32: - tcg_out_movcond32(s, args[5], args[0], args[1], - args[2], const_args[2], args[3]); - break; - - OP_32_64(bswap16): - tcg_out_rolw_8(s, args[0]); - break; - OP_32_64(bswap32): - tcg_out_bswap32(s, args[0]); - break; - - OP_32_64(neg): - tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NEG, args[0]); - break; - OP_32_64(not): - tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NOT, args[0]); - break; - - OP_32_64(ext8s): - tcg_out_ext8s(s, args[0], args[1], rexw); - break; - OP_32_64(ext16s): - tcg_out_ext16s(s, args[0], args[1], rexw); - break; - OP_32_64(ext8u): - tcg_out_ext8u(s, args[0], args[1]); - break; - OP_32_64(ext16u): - tcg_out_ext16u(s, args[0], args[1]); - break; - - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, args, 0); - break; - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, args, 1); - break; - case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, args, 0); - break; - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, args, 1); - break; - - OP_32_64(mulu2): - tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_MUL, args[3]); - break; - OP_32_64(muls2): - tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_IMUL, args[3]); - break; - OP_32_64(add2): - if (const_args[4]) { - tgen_arithi(s, ARITH_ADD + rexw, args[0], args[4], 1); - } else { - tgen_arithr(s, ARITH_ADD + rexw, args[0], args[4]); - } - if (const_args[5]) { - tgen_arithi(s, ARITH_ADC + rexw, args[1], args[5], 1); - } else { - tgen_arithr(s, ARITH_ADC + rexw, args[1], args[5]); - } - break; - OP_32_64(sub2): - if (const_args[4]) { - tgen_arithi(s, ARITH_SUB + rexw, args[0], args[4], 1); - } else { - tgen_arithr(s, ARITH_SUB + rexw, args[0], args[4]); - } - if (const_args[5]) { - tgen_arithi(s, ARITH_SBB + rexw, args[1], args[5], 1); - } else { - tgen_arithr(s, ARITH_SBB + rexw, args[1], args[5]); - } - break; - -#if TCG_TARGET_REG_BITS == 32 - case INDEX_op_brcond2_i32: - tcg_out_brcond2(s, args, const_args, 0); - break; - case INDEX_op_setcond2_i32: - tcg_out_setcond2(s, args, const_args); - break; -#else /* TCG_TARGET_REG_BITS == 64 */ - case INDEX_op_ld32s_i64: - tcg_out_modrm_offset(s, OPC_MOVSLQ, args[0], args[1], args[2]); - break; - case INDEX_op_ld_i64: - tcg_out_ld(s, TCG_TYPE_I64, args[0], args[1], args[2]); - break; - case INDEX_op_st_i64: - if (const_args[0]) { - tcg_out_modrm_offset(s, OPC_MOVL_EvIz | P_REXW, - 0, args[1], args[2]); - tcg_out32(s, args[0]); - } else { - tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]); - } - break; - - case INDEX_op_brcond_i64: - tcg_out_brcond64(s, args[2], args[0], args[1], const_args[1], - arg_label(args[3]), 0); - break; - case INDEX_op_setcond_i64: - tcg_out_setcond64(s, args[3], args[0], args[1], - args[2], const_args[2]); - break; - case INDEX_op_movcond_i64: - tcg_out_movcond64(s, args[5], args[0], args[1], - args[2], const_args[2], args[3]); - break; - - case INDEX_op_bswap64_i64: - tcg_out_bswap64(s, args[0]); - break; - case INDEX_op_ext32u_i64: - tcg_out_ext32u(s, args[0], args[1]); - break; - case INDEX_op_ext32s_i64: - tcg_out_ext32s(s, args[0], args[1]); - break; -#endif - - OP_32_64(deposit): - if (args[3] == 0 && args[4] == 8) { - /* load bits 0..7 */ - tcg_out_modrm(s, OPC_MOVB_EvGv | P_REXB_R | P_REXB_RM, - args[2], args[0]); - } else if (args[3] == 8 && args[4] == 8) { - /* load bits 8..15 */ - tcg_out_modrm(s, OPC_MOVB_EvGv, args[2], args[0] + 4); - } else if (args[3] == 0 && args[4] == 16) { - /* load bits 0..15 */ - tcg_out_modrm(s, OPC_MOVL_EvGv | P_DATA16, args[2], args[0]); - } else { - tcg_abort(); - } - break; - - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_mov_i64: - case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ - case INDEX_op_movi_i64: - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - default: - tcg_abort(); - } - -#undef OP_32_64 -} - -static const TCGTargetOpDef x86_op_defs[] = { - { INDEX_op_exit_tb, { } }, - { INDEX_op_goto_tb, { } }, - { INDEX_op_br, { } }, - { INDEX_op_ld8u_i32, { "r", "r" } }, - { INDEX_op_ld8s_i32, { "r", "r" } }, - { INDEX_op_ld16u_i32, { "r", "r" } }, - { INDEX_op_ld16s_i32, { "r", "r" } }, - { INDEX_op_ld_i32, { "r", "r" } }, - { INDEX_op_st8_i32, { "qi", "r" } }, - { INDEX_op_st16_i32, { "ri", "r" } }, - { INDEX_op_st_i32, { "ri", "r" } }, - - { INDEX_op_add_i32, { "r", "r", "ri" } }, - { INDEX_op_sub_i32, { "r", "0", "ri" } }, - { INDEX_op_mul_i32, { "r", "0", "ri" } }, - { INDEX_op_div2_i32, { "a", "d", "0", "1", "r" } }, - { INDEX_op_divu2_i32, { "a", "d", "0", "1", "r" } }, - { INDEX_op_and_i32, { "r", "0", "ri" } }, - { INDEX_op_or_i32, { "r", "0", "ri" } }, - { INDEX_op_xor_i32, { "r", "0", "ri" } }, - { INDEX_op_andc_i32, { "r", "r", "ri" } }, - - { INDEX_op_shl_i32, { "r", "0", "Ci" } }, - { INDEX_op_shr_i32, { "r", "0", "Ci" } }, - { INDEX_op_sar_i32, { "r", "0", "Ci" } }, - { INDEX_op_rotl_i32, { "r", "0", "ci" } }, - { INDEX_op_rotr_i32, { "r", "0", "ci" } }, - - { INDEX_op_brcond_i32, { "r", "ri" } }, - - { INDEX_op_bswap16_i32, { "r", "0" } }, - { INDEX_op_bswap32_i32, { "r", "0" } }, - - { INDEX_op_neg_i32, { "r", "0" } }, - - { INDEX_op_not_i32, { "r", "0" } }, - - { INDEX_op_ext8s_i32, { "r", "q" } }, - { INDEX_op_ext16s_i32, { "r", "r" } }, - { INDEX_op_ext8u_i32, { "r", "q" } }, - { INDEX_op_ext16u_i32, { "r", "r" } }, - - { INDEX_op_setcond_i32, { "q", "r", "ri" } }, - - { INDEX_op_deposit_i32, { "Q", "0", "Q" } }, - { INDEX_op_movcond_i32, { "r", "r", "ri", "r", "0" } }, - - { INDEX_op_mulu2_i32, { "a", "d", "a", "r" } }, - { INDEX_op_muls2_i32, { "a", "d", "a", "r" } }, - { INDEX_op_add2_i32, { "r", "r", "0", "1", "ri", "ri" } }, - { INDEX_op_sub2_i32, { "r", "r", "0", "1", "ri", "ri" } }, - -#if TCG_TARGET_REG_BITS == 32 - { INDEX_op_brcond2_i32, { "r", "r", "ri", "ri" } }, - { INDEX_op_setcond2_i32, { "r", "r", "r", "ri", "ri" } }, -#else - { INDEX_op_ld8u_i64, { "r", "r" } }, - { INDEX_op_ld8s_i64, { "r", "r" } }, - { INDEX_op_ld16u_i64, { "r", "r" } }, - { INDEX_op_ld16s_i64, { "r", "r" } }, - { INDEX_op_ld32u_i64, { "r", "r" } }, - { INDEX_op_ld32s_i64, { "r", "r" } }, - { INDEX_op_ld_i64, { "r", "r" } }, - { INDEX_op_st8_i64, { "ri", "r" } }, - { INDEX_op_st16_i64, { "ri", "r" } }, - { INDEX_op_st32_i64, { "ri", "r" } }, - { INDEX_op_st_i64, { "re", "r" } }, - - { INDEX_op_add_i64, { "r", "r", "re" } }, - { INDEX_op_mul_i64, { "r", "0", "re" } }, - { INDEX_op_div2_i64, { "a", "d", "0", "1", "r" } }, - { INDEX_op_divu2_i64, { "a", "d", "0", "1", "r" } }, - { INDEX_op_sub_i64, { "r", "0", "re" } }, - { INDEX_op_and_i64, { "r", "0", "reZ" } }, - { INDEX_op_or_i64, { "r", "0", "re" } }, - { INDEX_op_xor_i64, { "r", "0", "re" } }, - { INDEX_op_andc_i64, { "r", "r", "rI" } }, - - { INDEX_op_shl_i64, { "r", "0", "Ci" } }, - { INDEX_op_shr_i64, { "r", "0", "Ci" } }, - { INDEX_op_sar_i64, { "r", "0", "Ci" } }, - { INDEX_op_rotl_i64, { "r", "0", "ci" } }, - { INDEX_op_rotr_i64, { "r", "0", "ci" } }, - - { INDEX_op_brcond_i64, { "r", "re" } }, - { INDEX_op_setcond_i64, { "r", "r", "re" } }, - - { INDEX_op_bswap16_i64, { "r", "0" } }, - { INDEX_op_bswap32_i64, { "r", "0" } }, - { INDEX_op_bswap64_i64, { "r", "0" } }, - { INDEX_op_neg_i64, { "r", "0" } }, - { INDEX_op_not_i64, { "r", "0" } }, - - { INDEX_op_ext8s_i64, { "r", "r" } }, - { INDEX_op_ext16s_i64, { "r", "r" } }, - { INDEX_op_ext32s_i64, { "r", "r" } }, - { INDEX_op_ext8u_i64, { "r", "r" } }, - { INDEX_op_ext16u_i64, { "r", "r" } }, - { INDEX_op_ext32u_i64, { "r", "r" } }, - - { INDEX_op_deposit_i64, { "Q", "0", "Q" } }, - { INDEX_op_movcond_i64, { "r", "r", "re", "r", "0" } }, - - { INDEX_op_mulu2_i64, { "a", "d", "a", "r" } }, - { INDEX_op_muls2_i64, { "a", "d", "a", "r" } }, - { INDEX_op_add2_i64, { "r", "r", "0", "1", "re", "re" } }, - { INDEX_op_sub2_i64, { "r", "r", "0", "1", "re", "re" } }, -#endif - -#if TCG_TARGET_REG_BITS == 64 - { INDEX_op_qemu_ld_i32, { "r", "L" } }, - { INDEX_op_qemu_st_i32, { "L", "L" } }, - { INDEX_op_qemu_ld_i64, { "r", "L" } }, - { INDEX_op_qemu_st_i64, { "L", "L" } }, -#elif TARGET_LONG_BITS <= TCG_TARGET_REG_BITS - { INDEX_op_qemu_ld_i32, { "r", "L" } }, - { INDEX_op_qemu_st_i32, { "L", "L" } }, - { INDEX_op_qemu_ld_i64, { "r", "r", "L" } }, - { INDEX_op_qemu_st_i64, { "L", "L", "L" } }, -#else - { INDEX_op_qemu_ld_i32, { "r", "L", "L" } }, - { INDEX_op_qemu_st_i32, { "L", "L", "L" } }, - { INDEX_op_qemu_ld_i64, { "r", "r", "L", "L" } }, - { INDEX_op_qemu_st_i64, { "L", "L", "L", "L" } }, -#endif - { -1 }, -}; - -static int tcg_target_callee_save_regs[] = { -#if TCG_TARGET_REG_BITS == 64 - TCG_REG_RBP, - TCG_REG_RBX, -#if defined(_WIN64) - TCG_REG_RDI, - TCG_REG_RSI, -#endif - TCG_REG_R12, - TCG_REG_R13, - TCG_REG_R14, /* Currently used for the global env. */ - TCG_REG_R15, -#else - TCG_REG_EBP, /* Currently used for the global env. */ - TCG_REG_EBX, - TCG_REG_ESI, - TCG_REG_EDI, -#endif -}; - -/* Compute frame size via macros, to share between tcg_target_qemu_prologue - and tcg_register_jit. */ - -#define PUSH_SIZE \ - ((1 + ARRAY_SIZE(tcg_target_callee_save_regs)) \ - * (TCG_TARGET_REG_BITS / 8)) - -#define FRAME_SIZE \ - ((PUSH_SIZE \ - + TCG_STATIC_CALL_ARGS_SIZE \ - + CPU_TEMP_BUF_NLONGS * sizeof(long) \ - + TCG_TARGET_STACK_ALIGN - 1) \ - & ~(TCG_TARGET_STACK_ALIGN - 1)) - -/* Generate global QEMU prologue and epilogue code */ -static void tcg_target_qemu_prologue(TCGContext *s) -{ - int i, stack_addend; - - /* TB prologue */ - - /* Reserve some stack space, also for TCG temps. */ - stack_addend = FRAME_SIZE - PUSH_SIZE; - tcg_set_frame(s, TCG_REG_CALL_STACK, TCG_STATIC_CALL_ARGS_SIZE, - CPU_TEMP_BUF_NLONGS * sizeof(long)); - - /* Save all callee saved registers. */ - for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) { - tcg_out_push(s, tcg_target_callee_save_regs[i]); - } - -#if TCG_TARGET_REG_BITS == 32 - tcg_out_ld(s, TCG_TYPE_PTR, TCG_AREG0, TCG_REG_ESP, - (ARRAY_SIZE(tcg_target_callee_save_regs) + 1) * 4); - tcg_out_addi(s, TCG_REG_ESP, -stack_addend); - /* jmp *tb. */ - tcg_out_modrm_offset(s, OPC_GRP5, EXT5_JMPN_Ev, TCG_REG_ESP, - (ARRAY_SIZE(tcg_target_callee_save_regs) + 2) * 4 - + stack_addend); -#else - tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); - tcg_out_addi(s, TCG_REG_ESP, -stack_addend); - /* jmp *tb. */ - tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, tcg_target_call_iarg_regs[1]); -#endif - - /* TB epilogue */ - tb_ret_addr = s->code_ptr; - - tcg_out_addi(s, TCG_REG_CALL_STACK, stack_addend); - - for (i = ARRAY_SIZE(tcg_target_callee_save_regs) - 1; i >= 0; i--) { - tcg_out_pop(s, tcg_target_callee_save_regs[i]); - } - tcg_out_opc(s, OPC_RET, 0, 0, 0); - -#if !defined(CONFIG_SOFTMMU) - /* Try to set up a segment register to point to GUEST_BASE. */ - if (GUEST_BASE) { - setup_guest_base_seg(); - } -#endif -} - -static void tcg_target_init(TCGContext *s) -{ -#ifdef CONFIG_CPUID_H - unsigned a, b, c, d; - int max = __get_cpuid_max(0, 0); - - if (max >= 1) { - __cpuid(1, a, b, c, d); -#ifndef have_cmov - /* For 32-bit, 99% certainty that we're running on hardware that - supports cmov, but we still need to check. In case cmov is not - available, we'll use a small forward branch. */ - have_cmov = (d & bit_CMOV) != 0; -#endif -#ifndef have_movbe - /* MOVBE is only available on Intel Atom and Haswell CPUs, so we - need to probe for it. */ - have_movbe = (c & bit_MOVBE) != 0; -#endif - } - - if (max >= 7) { - /* BMI1 is available on AMD Piledriver and Intel Haswell CPUs. */ - __cpuid_count(7, 0, a, b, c, d); -#ifdef bit_BMI - have_bmi1 = (b & bit_BMI) != 0; -#endif -#ifndef have_bmi2 - have_bmi2 = (b & bit_BMI2) != 0; -#endif - } -#endif - - if (TCG_TARGET_REG_BITS == 64) { - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff); - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffff); - } else { - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xff); - } - - tcg_regset_clear(tcg_target_call_clobber_regs); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_EAX); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_EDX); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_ECX); - if (TCG_TARGET_REG_BITS == 64) { -#if !defined(_WIN64) - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RDI); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RSI); -#endif - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R8); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R9); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R10); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R11); - } - - tcg_regset_clear(s->reserved_regs); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); - - tcg_add_target_add_op_defs(x86_op_defs); -} - -typedef struct { - DebugFrameHeader h; - uint8_t fde_def_cfa[4]; - uint8_t fde_reg_ofs[14]; -} DebugFrame; - -/* We're expecting a 2 byte uleb128 encoded value. */ -QEMU_BUILD_BUG_ON(FRAME_SIZE >= (1 << 14)); - -#if !defined(__ELF__) - /* Host machine without ELF. */ -#elif TCG_TARGET_REG_BITS == 64 -#define ELF_HOST_MACHINE EM_X86_64 -static const DebugFrame debug_frame = { - .h.cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ - .h.cie.id = -1, - .h.cie.version = 1, - .h.cie.code_align = 1, - .h.cie.data_align = 0x78, /* sleb128 -8 */ - .h.cie.return_column = 16, - - /* Total FDE size does not include the "len" member. */ - .h.fde.len = sizeof(DebugFrame) - offsetof(DebugFrame, h.fde.cie_offset), - - .fde_def_cfa = { - 12, 7, /* DW_CFA_def_cfa %rsp, ... */ - (FRAME_SIZE & 0x7f) | 0x80, /* ... uleb128 FRAME_SIZE */ - (FRAME_SIZE >> 7) - }, - .fde_reg_ofs = { - 0x90, 1, /* DW_CFA_offset, %rip, -8 */ - /* The following ordering must match tcg_target_callee_save_regs. */ - 0x86, 2, /* DW_CFA_offset, %rbp, -16 */ - 0x83, 3, /* DW_CFA_offset, %rbx, -24 */ - 0x8c, 4, /* DW_CFA_offset, %r12, -32 */ - 0x8d, 5, /* DW_CFA_offset, %r13, -40 */ - 0x8e, 6, /* DW_CFA_offset, %r14, -48 */ - 0x8f, 7, /* DW_CFA_offset, %r15, -56 */ - } -}; -#else -#define ELF_HOST_MACHINE EM_386 -static const DebugFrame debug_frame = { - .h.cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ - .h.cie.id = -1, - .h.cie.version = 1, - .h.cie.code_align = 1, - .h.cie.data_align = 0x7c, /* sleb128 -4 */ - .h.cie.return_column = 8, - - /* Total FDE size does not include the "len" member. */ - .h.fde.len = sizeof(DebugFrame) - offsetof(DebugFrame, h.fde.cie_offset), - - .fde_def_cfa = { - 12, 4, /* DW_CFA_def_cfa %esp, ... */ - (FRAME_SIZE & 0x7f) | 0x80, /* ... uleb128 FRAME_SIZE */ - (FRAME_SIZE >> 7) - }, - .fde_reg_ofs = { - 0x88, 1, /* DW_CFA_offset, %eip, -4 */ - /* The following ordering must match tcg_target_callee_save_regs. */ - 0x85, 2, /* DW_CFA_offset, %ebp, -8 */ - 0x83, 3, /* DW_CFA_offset, %ebx, -12 */ - 0x86, 4, /* DW_CFA_offset, %esi, -16 */ - 0x87, 5, /* DW_CFA_offset, %edi, -20 */ - } -}; -#endif - -#if defined(ELF_HOST_MACHINE) -void tcg_register_jit(void *buf, size_t buf_size) -{ - tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); -} -#endif diff --git a/qemu/tcg/i386/tcg-target.h b/qemu/tcg/i386/tcg-target.h index 25b513354..92be34171 100644 --- a/qemu/tcg/i386/tcg-target.h +++ b/qemu/tcg/i386/tcg-target.h @@ -102,7 +102,8 @@ extern bool have_bmi1; #define TCG_TARGET_HAS_mulsh_i32 0 #if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_trunc_shr_i32 0 +#define TCG_TARGET_HAS_extrl_i64_i32 0 +#define TCG_TARGET_HAS_extrh_i64_i32 0 #define TCG_TARGET_HAS_div2_i64 1 #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_ext8s_i64 1 diff --git a/qemu/tcg/i386/tcg-target.inc.c b/qemu/tcg/i386/tcg-target.inc.c new file mode 100644 index 000000000..007407c3f --- /dev/null +++ b/qemu/tcg/i386/tcg-target.inc.c @@ -0,0 +1,2463 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "tcg-be-ldst.h" + +#ifdef CONFIG_DEBUG_TCG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { +#if TCG_TARGET_REG_BITS == 64 + "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi", + "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", +#else + "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi", +#endif +}; +#endif + +static const int tcg_target_reg_alloc_order[] = { +#if TCG_TARGET_REG_BITS == 64 + TCG_REG_RBP, + TCG_REG_RBX, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R9, + TCG_REG_R8, + TCG_REG_RCX, + TCG_REG_RDX, + TCG_REG_RSI, + TCG_REG_RDI, + TCG_REG_RAX, +#else + TCG_REG_EBX, + TCG_REG_ESI, + TCG_REG_EDI, + TCG_REG_EBP, + TCG_REG_ECX, + TCG_REG_EDX, + TCG_REG_EAX, +#endif +}; + +static const int tcg_target_call_iarg_regs[] = { +#if TCG_TARGET_REG_BITS == 64 +#if defined(_WIN64) + TCG_REG_RCX, + TCG_REG_RDX, +#else + TCG_REG_RDI, + TCG_REG_RSI, + TCG_REG_RDX, + TCG_REG_RCX, +#endif + TCG_REG_R8, + TCG_REG_R9, +#else + /* 32 bit mode uses stack based calling convention (GCC default). */ +#endif +}; + +static const int tcg_target_call_oarg_regs[] = { + TCG_REG_EAX, +#if TCG_TARGET_REG_BITS == 32 + TCG_REG_EDX +#endif +}; + +/* Constants we accept. */ +#define TCG_CT_CONST_S32 0x100 +#define TCG_CT_CONST_U32 0x200 +#define TCG_CT_CONST_I32 0x400 + +/* Registers used with L constraint, which are the first argument + registers on x86_64, and two random call clobbered registers on + i386. */ +#if TCG_TARGET_REG_BITS == 64 +# define TCG_REG_L0 tcg_target_call_iarg_regs[0] +# define TCG_REG_L1 tcg_target_call_iarg_regs[1] +#else +# define TCG_REG_L0 TCG_REG_EAX +# define TCG_REG_L1 TCG_REG_EDX +#endif + +/* The host compiler should supply to enable runtime features + detection, as we're not going to go so far as our own inline assembly. + If not available, default values will be assumed. */ +#if defined(CONFIG_CPUID_H) +#include +#endif + +/* For 32-bit, we are going to attempt to determine at runtime whether cmov + is available. */ +#if TCG_TARGET_REG_BITS == 64 +# define have_cmov 1 +#elif defined(CONFIG_CPUID_H) && defined(bit_CMOV) +static bool have_cmov; +#else +# define have_cmov 0 +#endif + +/* If bit_MOVBE is defined in cpuid.h (added in GCC version 4.6), we are + going to attempt to determine at runtime whether movbe is available. */ +#if defined(CONFIG_CPUID_H) && defined(bit_MOVBE) +static bool have_movbe; +#else +# define have_movbe 0 +#endif + +/* We need this symbol in tcg-target.h, and we can't properly conditionalize + it there. Therefore we always define the variable. */ +bool have_bmi1; + +#if defined(CONFIG_CPUID_H) && defined(bit_BMI2) +static bool have_bmi2; +#else +# define have_bmi2 0 +#endif + +static tcg_insn_unit *tb_ret_addr; + +static void patch_reloc(tcg_insn_unit *code_ptr, int type, + intptr_t value, intptr_t addend) +{ + value += addend; + switch(type) { + case R_386_PC32: + value -= (uintptr_t)code_ptr; + if (value != (int32_t)value) { + tcg_abort(); + } + tcg_patch32(code_ptr, value); + break; + case R_386_PC8: + value -= (uintptr_t)code_ptr; + if (value != (int8_t)value) { + tcg_abort(); + } + tcg_patch8(code_ptr, value); + break; + default: + tcg_abort(); + } +} + +/* parse target specific constraints */ +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +{ + const char *ct_str; + + ct_str = *pct_str; + switch(ct_str[0]) { + case 'a': + ct->ct |= TCG_CT_REG; + tcg_regset_set_reg(ct->u.regs, TCG_REG_EAX); + break; + case 'b': + ct->ct |= TCG_CT_REG; + tcg_regset_set_reg(ct->u.regs, TCG_REG_EBX); + break; + case 'c': + case_c: + ct->ct |= TCG_CT_REG; + tcg_regset_set_reg(ct->u.regs, TCG_REG_ECX); + break; + case 'd': + ct->ct |= TCG_CT_REG; + tcg_regset_set_reg(ct->u.regs, TCG_REG_EDX); + break; + case 'S': + ct->ct |= TCG_CT_REG; + tcg_regset_set_reg(ct->u.regs, TCG_REG_ESI); + break; + case 'D': + ct->ct |= TCG_CT_REG; + tcg_regset_set_reg(ct->u.regs, TCG_REG_EDI); + break; + case 'q': + ct->ct |= TCG_CT_REG; + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set32(ct->u.regs, 0, 0xffff); + } else { + tcg_regset_set32(ct->u.regs, 0, 0xf); + } + break; + case 'Q': + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xf); + break; + case 'r': + case_r: + ct->ct |= TCG_CT_REG; + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set32(ct->u.regs, 0, 0xffff); + } else { + tcg_regset_set32(ct->u.regs, 0, 0xff); + } + break; + case 'C': + /* With SHRX et al, we need not use ECX as shift count register. */ + if (have_bmi2) { + goto case_r; + } else { + goto case_c; + } + + /* qemu_ld/st address constraint */ + case 'L': + ct->ct |= TCG_CT_REG; + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set32(ct->u.regs, 0, 0xffff); + } else { + tcg_regset_set32(ct->u.regs, 0, 0xff); + } + tcg_regset_reset_reg(ct->u.regs, TCG_REG_L0); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_L1); + break; + + case 'e': + ct->ct |= TCG_CT_CONST_S32; + break; + case 'Z': + ct->ct |= TCG_CT_CONST_U32; + break; + case 'I': + ct->ct |= TCG_CT_CONST_I32; + break; + + default: + return -1; + } + ct_str++; + *pct_str = ct_str; + return 0; +} + +/* test if a constant matches the constraint */ +static inline int tcg_target_const_match(tcg_target_long val, TCGType type, + const TCGArgConstraint *arg_ct) +{ + int ct = arg_ct->ct; + if (ct & TCG_CT_CONST) { + return 1; + } + if ((ct & TCG_CT_CONST_S32) && val == (int32_t)val) { + return 1; + } + if ((ct & TCG_CT_CONST_U32) && val == (uint32_t)val) { + return 1; + } + if ((ct & TCG_CT_CONST_I32) && ~val == (int32_t)~val) { + return 1; + } + return 0; +} + +#if TCG_TARGET_REG_BITS == 64 +# define LOWREGMASK(x) ((x) & 7) +#else +# define LOWREGMASK(x) (x) +#endif + +#define P_EXT 0x100 /* 0x0f opcode prefix */ +#define P_EXT38 0x200 /* 0x0f 0x38 opcode prefix */ +#define P_DATA16 0x400 /* 0x66 opcode prefix */ +#if TCG_TARGET_REG_BITS == 64 +# define P_ADDR32 0x800 /* 0x67 opcode prefix */ +# define P_REXW 0x1000 /* Set REX.W = 1 */ +# define P_REXB_R 0x2000 /* REG field as byte register */ +# define P_REXB_RM 0x4000 /* R/M field as byte register */ +# define P_GS 0x8000 /* gs segment override */ +#else +# define P_ADDR32 0 +# define P_REXW 0 +# define P_REXB_R 0 +# define P_REXB_RM 0 +# define P_GS 0 +#endif +#define P_SIMDF3 0x10000 /* 0xf3 opcode prefix */ +#define P_SIMDF2 0x20000 /* 0xf2 opcode prefix */ + +#define OPC_ARITH_EvIz (0x81) +#define OPC_ARITH_EvIb (0x83) +#define OPC_ARITH_GvEv (0x03) /* ... plus (ARITH_FOO << 3) */ +#define OPC_ANDN (0xf2 | P_EXT38) +#define OPC_ADD_GvEv (OPC_ARITH_GvEv | (ARITH_ADD << 3)) +#define OPC_BSWAP (0xc8 | P_EXT) +#define OPC_CALL_Jz (0xe8) +#define OPC_CMOVCC (0x40 | P_EXT) /* ... plus condition code */ +#define OPC_CMP_GvEv (OPC_ARITH_GvEv | (ARITH_CMP << 3)) +#define OPC_DEC_r32 (0x48) +#define OPC_IMUL_GvEv (0xaf | P_EXT) +#define OPC_IMUL_GvEvIb (0x6b) +#define OPC_IMUL_GvEvIz (0x69) +#define OPC_INC_r32 (0x40) +#define OPC_JCC_long (0x80 | P_EXT) /* ... plus condition code */ +#define OPC_JCC_short (0x70) /* ... plus condition code */ +#define OPC_JMP_long (0xe9) +#define OPC_JMP_short (0xeb) +#define OPC_LEA (0x8d) +#define OPC_MOVB_EvGv (0x88) /* stores, more or less */ +#define OPC_MOVL_EvGv (0x89) /* stores, more or less */ +#define OPC_MOVL_GvEv (0x8b) /* loads, more or less */ +#define OPC_MOVB_EvIz (0xc6) +#define OPC_MOVL_EvIz (0xc7) +#define OPC_MOVL_Iv (0xb8) +#define OPC_MOVBE_GyMy (0xf0 | P_EXT38) +#define OPC_MOVBE_MyGy (0xf1 | P_EXT38) +#define OPC_MOVSBL (0xbe | P_EXT) +#define OPC_MOVSWL (0xbf | P_EXT) +#define OPC_MOVSLQ (0x63 | P_REXW) +#define OPC_MOVZBL (0xb6 | P_EXT) +#define OPC_MOVZWL (0xb7 | P_EXT) +#define OPC_POP_r32 (0x58) +#define OPC_PUSH_r32 (0x50) +#define OPC_PUSH_Iv (0x68) +#define OPC_PUSH_Ib (0x6a) +#define OPC_RET (0xc3) +#define OPC_SETCC (0x90 | P_EXT | P_REXB_RM) /* ... plus cc */ +#define OPC_SHIFT_1 (0xd1) +#define OPC_SHIFT_Ib (0xc1) +#define OPC_SHIFT_cl (0xd3) +#define OPC_SARX (0xf7 | P_EXT38 | P_SIMDF3) +#define OPC_SHLX (0xf7 | P_EXT38 | P_DATA16) +#define OPC_SHRX (0xf7 | P_EXT38 | P_SIMDF2) +#define OPC_TESTL (0x85) +#define OPC_XCHG_ax_r32 (0x90) + +#define OPC_GRP3_Ev (0xf7) +#define OPC_GRP5 (0xff) + +/* Group 1 opcode extensions for 0x80-0x83. + These are also used as modifiers for OPC_ARITH. */ +#define ARITH_ADD 0 +#define ARITH_OR 1 +#define ARITH_ADC 2 +#define ARITH_SBB 3 +#define ARITH_AND 4 +#define ARITH_SUB 5 +#define ARITH_XOR 6 +#define ARITH_CMP 7 + +/* Group 2 opcode extensions for 0xc0, 0xc1, 0xd0-0xd3. */ +#define SHIFT_ROL 0 +#define SHIFT_ROR 1 +#define SHIFT_SHL 4 +#define SHIFT_SHR 5 +#define SHIFT_SAR 7 + +/* Group 3 opcode extensions for 0xf6, 0xf7. To be used with OPC_GRP3. */ +#define EXT3_NOT 2 +#define EXT3_NEG 3 +#define EXT3_MUL 4 +#define EXT3_IMUL 5 +#define EXT3_DIV 6 +#define EXT3_IDIV 7 + +/* Group 5 opcode extensions for 0xff. To be used with OPC_GRP5. */ +#define EXT5_INC_Ev 0 +#define EXT5_DEC_Ev 1 +#define EXT5_CALLN_Ev 2 +#define EXT5_JMPN_Ev 4 + +/* Condition codes to be added to OPC_JCC_{long,short}. */ +#define JCC_JMP (-1) +#define JCC_JO 0x0 +#define JCC_JNO 0x1 +#define JCC_JB 0x2 +#define JCC_JAE 0x3 +#define JCC_JE 0x4 +#define JCC_JNE 0x5 +#define JCC_JBE 0x6 +#define JCC_JA 0x7 +#define JCC_JS 0x8 +#define JCC_JNS 0x9 +#define JCC_JP 0xa +#define JCC_JNP 0xb +#define JCC_JL 0xc +#define JCC_JGE 0xd +#define JCC_JLE 0xe +#define JCC_JG 0xf + +static const uint8_t tcg_cond_to_jcc[] = { + [TCG_COND_EQ] = JCC_JE, + [TCG_COND_NE] = JCC_JNE, + [TCG_COND_LT] = JCC_JL, + [TCG_COND_GE] = JCC_JGE, + [TCG_COND_LE] = JCC_JLE, + [TCG_COND_GT] = JCC_JG, + [TCG_COND_LTU] = JCC_JB, + [TCG_COND_GEU] = JCC_JAE, + [TCG_COND_LEU] = JCC_JBE, + [TCG_COND_GTU] = JCC_JA, +}; + +#if TCG_TARGET_REG_BITS == 64 +static void tcg_out_opc(TCGContext *s, int opc, int r, int rm, int x) +{ + int rex; + + if (opc & P_GS) { + tcg_out8(s, 0x65); + } + if (opc & P_DATA16) { + /* We should never be asking for both 16 and 64-bit operation. */ + tcg_debug_assert((opc & P_REXW) == 0); + tcg_out8(s, 0x66); + } + if (opc & P_ADDR32) { + tcg_out8(s, 0x67); + } + + rex = 0; + rex |= (opc & P_REXW) ? 0x8 : 0x0; /* REX.W */ + rex |= (r & 8) >> 1; /* REX.R */ + rex |= (x & 8) >> 2; /* REX.X */ + rex |= (rm & 8) >> 3; /* REX.B */ + + /* P_REXB_{R,RM} indicates that the given register is the low byte. + For %[abcd]l we need no REX prefix, but for %{si,di,bp,sp}l we do, + as otherwise the encoding indicates %[abcd]h. Note that the values + that are ORed in merely indicate that the REX byte must be present; + those bits get discarded in output. */ + rex |= opc & (r >= 4 ? P_REXB_R : 0); + rex |= opc & (rm >= 4 ? P_REXB_RM : 0); + + if (rex) { + tcg_out8(s, (uint8_t)(rex | 0x40)); + } + + if (opc & (P_EXT | P_EXT38)) { + tcg_out8(s, 0x0f); + if (opc & P_EXT38) { + tcg_out8(s, 0x38); + } + } + + tcg_out8(s, opc); +} +#else +static void tcg_out_opc(TCGContext *s, int opc) +{ + if (opc & P_DATA16) { + tcg_out8(s, 0x66); + } + if (opc & (P_EXT | P_EXT38)) { + tcg_out8(s, 0x0f); + if (opc & P_EXT38) { + tcg_out8(s, 0x38); + } + } + tcg_out8(s, opc); +} +/* Discard the register arguments to tcg_out_opc early, so as not to penalize + the 32-bit compilation paths. This method works with all versions of gcc, + whereas relying on optimization may not be able to exclude them. */ +#define tcg_out_opc(s, opc, r, rm, x) (tcg_out_opc)(s, opc) +#endif + +static void tcg_out_modrm(TCGContext *s, int opc, int r, int rm) +{ + tcg_out_opc(s, opc, r, rm, 0); + tcg_out8(s, 0xc0 | (LOWREGMASK(r) << 3) | LOWREGMASK(rm)); +} + +static void tcg_out_vex_modrm(TCGContext *s, int opc, int r, int v, int rm) +{ + int tmp; + + if ((opc & (P_REXW | P_EXT | P_EXT38)) || (rm & 8)) { + /* Three byte VEX prefix. */ + tcg_out8(s, 0xc4); + + /* VEX.m-mmmm */ + if (opc & P_EXT38) { + tmp = 2; + } else if (opc & P_EXT) { + tmp = 1; + } else { + tcg_abort(); + } + tmp |= 0x40; /* VEX.X */ + tmp |= (r & 8 ? 0 : 0x80); /* VEX.R */ + tmp |= (rm & 8 ? 0 : 0x20); /* VEX.B */ + tcg_out8(s, tmp); + + tmp = (opc & P_REXW ? 0x80 : 0); /* VEX.W */ + } else { + /* Two byte VEX prefix. */ + tcg_out8(s, 0xc5); + + tmp = (r & 8 ? 0 : 0x80); /* VEX.R */ + } + /* VEX.pp */ + if (opc & P_DATA16) { + tmp |= 1; /* 0x66 */ + } else if (opc & P_SIMDF3) { + tmp |= 2; /* 0xf3 */ + } else if (opc & P_SIMDF2) { + tmp |= 3; /* 0xf2 */ + } + tmp |= (~v & 15) << 3; /* VEX.vvvv */ + tcg_out8(s, tmp); + tcg_out8(s, opc); + tcg_out8(s, 0xc0 | (LOWREGMASK(r) << 3) | LOWREGMASK(rm)); +} + +/* Output an opcode with a full "rm + (index<code_ptr + 5 + ~rm; + intptr_t disp = offset - pc; + if (disp == (int32_t)disp) { + tcg_out_opc(s, opc, r, 0, 0); + tcg_out8(s, (LOWREGMASK(r) << 3) | 5); + tcg_out32(s, disp); + return; + } + + /* Try for an absolute address encoding. This requires the + use of the MODRM+SIB encoding and is therefore larger than + rip-relative addressing. */ + if (offset == (int32_t)offset) { + tcg_out_opc(s, opc, r, 0, 0); + tcg_out8(s, (LOWREGMASK(r) << 3) | 4); + tcg_out8(s, (4 << 3) | 5); + tcg_out32(s, offset); + return; + } + + /* ??? The memory isn't directly addressable. */ + tcg_abort(); + } else { + /* Absolute address. */ + tcg_out_opc(s, opc, r, 0, 0); + tcg_out8(s, (r << 3) | 5); + tcg_out32(s, offset); + return; + } + } + + /* Find the length of the immediate addend. Note that the encoding + that would be used for (%ebp) indicates absolute addressing. */ + if (rm < 0) { + mod = 0, len = 4, rm = 5; + } else if (offset == 0 && LOWREGMASK(rm) != TCG_REG_EBP) { + mod = 0, len = 0; + } else if (offset == (int8_t)offset) { + mod = 0x40, len = 1; + } else { + mod = 0x80, len = 4; + } + + /* Use a single byte MODRM format if possible. Note that the encoding + that would be used for %esp is the escape to the two byte form. */ + if (index < 0 && LOWREGMASK(rm) != TCG_REG_ESP) { + /* Single byte MODRM format. */ + tcg_out_opc(s, opc, r, rm, 0); + tcg_out8(s, mod | (LOWREGMASK(r) << 3) | LOWREGMASK(rm)); + } else { + /* Two byte MODRM+SIB format. */ + + /* Note that the encoding that would place %esp into the index + field indicates no index register. In 64-bit mode, the REX.X + bit counts, so %r12 can be used as the index. */ + if (index < 0) { + index = 4; + } else { + tcg_debug_assert(index != TCG_REG_ESP); + } + + tcg_out_opc(s, opc, r, rm, index); + tcg_out8(s, mod | (LOWREGMASK(r) << 3) | 4); + tcg_out8(s, (shift << 6) | (LOWREGMASK(index) << 3) | LOWREGMASK(rm)); + } + + if (len == 1) { + tcg_out8(s, offset); + } else if (len == 4) { + tcg_out32(s, offset); + } +} + +/* A simplification of the above with no index or shift. */ +static inline void tcg_out_modrm_offset(TCGContext *s, int opc, int r, + int rm, intptr_t offset) +{ + tcg_out_modrm_sib_offset(s, opc, r, rm, -1, 0, offset); +} + +/* Generate dest op= src. Uses the same ARITH_* codes as tgen_arithi. */ +static inline void tgen_arithr(TCGContext *s, int subop, int dest, int src) +{ + /* Propagate an opcode prefix, such as P_REXW. */ + int ext = subop & ~0x7; + subop &= 0x7; + + tcg_out_modrm(s, OPC_ARITH_GvEv + (subop << 3) + ext, dest, src); +} + +static inline void tcg_out_mov(TCGContext *s, TCGType type, + TCGReg ret, TCGReg arg) +{ + if (arg != ret) { + int opc = OPC_MOVL_GvEv + (type == TCG_TYPE_I64 ? P_REXW : 0); + tcg_out_modrm(s, opc, ret, arg); + } +} + +static void tcg_out_movi(TCGContext *s, TCGType type, + TCGReg ret, tcg_target_long arg) +{ + tcg_target_long diff; + + if (arg == 0) { + tgen_arithr(s, ARITH_XOR, ret, ret); + return; + } + if (arg == (uint32_t)arg || type == TCG_TYPE_I32) { + tcg_out_opc(s, OPC_MOVL_Iv + LOWREGMASK(ret), 0, ret, 0); + tcg_out32(s, arg); + return; + } + if (arg == (int32_t)arg) { + tcg_out_modrm(s, OPC_MOVL_EvIz + P_REXW, 0, ret); + tcg_out32(s, arg); + return; + } + + /* Try a 7 byte pc-relative lea before the 10 byte movq. */ + diff = arg - ((uintptr_t)s->code_ptr + 7); + if (diff == (int32_t)diff) { + tcg_out_opc(s, OPC_LEA | P_REXW, ret, 0, 0); + tcg_out8(s, (LOWREGMASK(ret) << 3) | 5); + tcg_out32(s, diff); + return; + } + + tcg_out_opc(s, OPC_MOVL_Iv + P_REXW + LOWREGMASK(ret), 0, ret, 0); + tcg_out64(s, arg); +} + +static inline void tcg_out_pushi(TCGContext *s, tcg_target_long val) +{ + if (val == (int8_t)val) { + tcg_out_opc(s, OPC_PUSH_Ib, 0, 0, 0); + tcg_out8(s, val); + } else if (val == (int32_t)val) { + tcg_out_opc(s, OPC_PUSH_Iv, 0, 0, 0); + tcg_out32(s, val); + } else { + tcg_abort(); + } +} + +static inline void tcg_out_push(TCGContext *s, int reg) +{ + tcg_out_opc(s, OPC_PUSH_r32 + LOWREGMASK(reg), 0, reg, 0); +} + +static inline void tcg_out_pop(TCGContext *s, int reg) +{ + tcg_out_opc(s, OPC_POP_r32 + LOWREGMASK(reg), 0, reg, 0); +} + +static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, + TCGReg arg1, intptr_t arg2) +{ + int opc = OPC_MOVL_GvEv + (type == TCG_TYPE_I64 ? P_REXW : 0); + tcg_out_modrm_offset(s, opc, ret, arg1, arg2); +} + +static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, + TCGReg arg1, intptr_t arg2) +{ + int opc = OPC_MOVL_EvGv + (type == TCG_TYPE_I64 ? P_REXW : 0); + tcg_out_modrm_offset(s, opc, arg, arg1, arg2); +} + +static inline void tcg_out_sti(TCGContext *s, TCGType type, TCGReg base, + tcg_target_long ofs, tcg_target_long val) +{ + int opc = OPC_MOVL_EvIz + (type == TCG_TYPE_I64 ? P_REXW : 0); + tcg_out_modrm_offset(s, opc, 0, base, ofs); + tcg_out32(s, val); +} + +static void tcg_out_shifti(TCGContext *s, int subopc, int reg, int count) +{ + /* Propagate an opcode prefix, such as P_DATA16. */ + int ext = subopc & ~0x7; + subopc &= 0x7; + + if (count == 1) { + tcg_out_modrm(s, OPC_SHIFT_1 + ext, subopc, reg); + } else { + tcg_out_modrm(s, OPC_SHIFT_Ib + ext, subopc, reg); + tcg_out8(s, count); + } +} + +static inline void tcg_out_bswap32(TCGContext *s, int reg) +{ + tcg_out_opc(s, OPC_BSWAP + LOWREGMASK(reg), 0, reg, 0); +} + +static inline void tcg_out_rolw_8(TCGContext *s, int reg) +{ + tcg_out_shifti(s, SHIFT_ROL + P_DATA16, reg, 8); +} + +static inline void tcg_out_ext8u(TCGContext *s, int dest, int src) +{ + /* movzbl */ + tcg_debug_assert(src < 4 || TCG_TARGET_REG_BITS == 64); + tcg_out_modrm(s, OPC_MOVZBL + P_REXB_RM, dest, src); +} + +static void tcg_out_ext8s(TCGContext *s, int dest, int src, int rexw) +{ + /* movsbl */ + tcg_debug_assert(src < 4 || TCG_TARGET_REG_BITS == 64); + tcg_out_modrm(s, OPC_MOVSBL + P_REXB_RM + rexw, dest, src); +} + +static inline void tcg_out_ext16u(TCGContext *s, int dest, int src) +{ + /* movzwl */ + tcg_out_modrm(s, OPC_MOVZWL, dest, src); +} + +static inline void tcg_out_ext16s(TCGContext *s, int dest, int src, int rexw) +{ + /* movsw[lq] */ + tcg_out_modrm(s, OPC_MOVSWL + rexw, dest, src); +} + +static inline void tcg_out_ext32u(TCGContext *s, int dest, int src) +{ + /* 32-bit mov zero extends. */ + tcg_out_modrm(s, OPC_MOVL_GvEv, dest, src); +} + +static inline void tcg_out_ext32s(TCGContext *s, int dest, int src) +{ + tcg_out_modrm(s, OPC_MOVSLQ, dest, src); +} + +static inline void tcg_out_bswap64(TCGContext *s, int reg) +{ + tcg_out_opc(s, OPC_BSWAP + P_REXW + LOWREGMASK(reg), 0, reg, 0); +} + +static void tgen_arithi(TCGContext *s, int c, int r0, + tcg_target_long val, int cf) +{ + int rexw = 0; + + if (TCG_TARGET_REG_BITS == 64) { + rexw = c & -8; + c &= 7; + } + + /* ??? While INC is 2 bytes shorter than ADDL $1, they also induce + partial flags update stalls on Pentium4 and are not recommended + by current Intel optimization manuals. */ + if (!cf && (c == ARITH_ADD || c == ARITH_SUB) && (val == 1 || val == -1)) { + int is_inc = (c == ARITH_ADD) ^ (val < 0); + if (TCG_TARGET_REG_BITS == 64) { + /* The single-byte increment encodings are re-tasked as the + REX prefixes. Use the MODRM encoding. */ + tcg_out_modrm(s, OPC_GRP5 + rexw, + (is_inc ? EXT5_INC_Ev : EXT5_DEC_Ev), r0); + } else { + tcg_out8(s, (is_inc ? OPC_INC_r32 : OPC_DEC_r32) + r0); + } + return; + } + + if (c == ARITH_AND) { + if (TCG_TARGET_REG_BITS == 64) { + if (val == 0xffffffffu) { + tcg_out_ext32u(s, r0, r0); + return; + } + if (val == (uint32_t)val) { + /* AND with no high bits set can use a 32-bit operation. */ + rexw = 0; + } + } + if (val == 0xffu && (r0 < 4 || TCG_TARGET_REG_BITS == 64)) { + tcg_out_ext8u(s, r0, r0); + return; + } + if (val == 0xffffu) { + tcg_out_ext16u(s, r0, r0); + return; + } + } + + if (val == (int8_t)val) { + tcg_out_modrm(s, OPC_ARITH_EvIb + rexw, c, r0); + tcg_out8(s, val); + return; + } + if (rexw == 0 || val == (int32_t)val) { + tcg_out_modrm(s, OPC_ARITH_EvIz + rexw, c, r0); + tcg_out32(s, val); + return; + } + + tcg_abort(); +} + +static void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) +{ + if (val != 0) { + tgen_arithi(s, ARITH_ADD + P_REXW, reg, val, 0); + } +} + +/* Use SMALL != 0 to force a short forward branch. */ +static void tcg_out_jxx(TCGContext *s, int opc, TCGLabel *l, int small) +{ + int32_t val, val1; + + if (l->has_value) { + val = tcg_pcrel_diff(s, l->u.value_ptr); + val1 = val - 2; + if ((int8_t)val1 == val1) { + if (opc == -1) { + tcg_out8(s, OPC_JMP_short); + } else { + tcg_out8(s, OPC_JCC_short + opc); + } + tcg_out8(s, val1); + } else { + if (small) { + tcg_abort(); + } + if (opc == -1) { + tcg_out8(s, OPC_JMP_long); + tcg_out32(s, val - 5); + } else { + tcg_out_opc(s, OPC_JCC_long + opc, 0, 0, 0); + tcg_out32(s, val - 6); + } + } + } else if (small) { + if (opc == -1) { + tcg_out8(s, OPC_JMP_short); + } else { + tcg_out8(s, OPC_JCC_short + opc); + } + tcg_out_reloc(s, s->code_ptr, R_386_PC8, l, -1); + s->code_ptr += 1; + } else { + if (opc == -1) { + tcg_out8(s, OPC_JMP_long); + } else { + tcg_out_opc(s, OPC_JCC_long + opc, 0, 0, 0); + } + tcg_out_reloc(s, s->code_ptr, R_386_PC32, l, -4); + s->code_ptr += 4; + } +} + +static void tcg_out_cmp(TCGContext *s, TCGArg arg1, TCGArg arg2, + int const_arg2, int rexw) +{ + if (const_arg2) { + if (arg2 == 0) { + /* test r, r */ + tcg_out_modrm(s, OPC_TESTL + rexw, arg1, arg1); + } else { + tgen_arithi(s, ARITH_CMP + rexw, arg1, arg2, 0); + } + } else { + tgen_arithr(s, ARITH_CMP + rexw, arg1, arg2); + } +} + +static void tcg_out_brcond32(TCGContext *s, TCGCond cond, + TCGArg arg1, TCGArg arg2, int const_arg2, + TCGLabel *label, int small) +{ + tcg_out_cmp(s, arg1, arg2, const_arg2, 0); + tcg_out_jxx(s, tcg_cond_to_jcc[cond], label, small); +} + +#if TCG_TARGET_REG_BITS == 64 +static void tcg_out_brcond64(TCGContext *s, TCGCond cond, + TCGArg arg1, TCGArg arg2, int const_arg2, + TCGLabel *label, int small) +{ + tcg_out_cmp(s, arg1, arg2, const_arg2, P_REXW); + tcg_out_jxx(s, tcg_cond_to_jcc[cond], label, small); +} +#else +/* XXX: we implement it at the target level to avoid having to + handle cross basic blocks temporaries */ +static void tcg_out_brcond2(TCGContext *s, const TCGArg *args, + const int *const_args, int small) +{ + TCGLabel *label_next = gen_new_label(); + TCGLabel *label_this = arg_label(args[5]); + + switch(args[4]) { + case TCG_COND_EQ: + tcg_out_brcond32(s, TCG_COND_NE, args[0], args[2], const_args[2], + label_next, 1); + tcg_out_brcond32(s, TCG_COND_EQ, args[1], args[3], const_args[3], + label_this, small); + break; + case TCG_COND_NE: + tcg_out_brcond32(s, TCG_COND_NE, args[0], args[2], const_args[2], + label_this, small); + tcg_out_brcond32(s, TCG_COND_NE, args[1], args[3], const_args[3], + label_this, small); + break; + case TCG_COND_LT: + tcg_out_brcond32(s, TCG_COND_LT, args[1], args[3], const_args[3], + label_this, small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_LTU, args[0], args[2], const_args[2], + label_this, small); + break; + case TCG_COND_LE: + tcg_out_brcond32(s, TCG_COND_LT, args[1], args[3], const_args[3], + label_this, small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_LEU, args[0], args[2], const_args[2], + label_this, small); + break; + case TCG_COND_GT: + tcg_out_brcond32(s, TCG_COND_GT, args[1], args[3], const_args[3], + label_this, small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_GTU, args[0], args[2], const_args[2], + label_this, small); + break; + case TCG_COND_GE: + tcg_out_brcond32(s, TCG_COND_GT, args[1], args[3], const_args[3], + label_this, small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_GEU, args[0], args[2], const_args[2], + label_this, small); + break; + case TCG_COND_LTU: + tcg_out_brcond32(s, TCG_COND_LTU, args[1], args[3], const_args[3], + label_this, small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_LTU, args[0], args[2], const_args[2], + label_this, small); + break; + case TCG_COND_LEU: + tcg_out_brcond32(s, TCG_COND_LTU, args[1], args[3], const_args[3], + label_this, small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_LEU, args[0], args[2], const_args[2], + label_this, small); + break; + case TCG_COND_GTU: + tcg_out_brcond32(s, TCG_COND_GTU, args[1], args[3], const_args[3], + label_this, small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_GTU, args[0], args[2], const_args[2], + label_this, small); + break; + case TCG_COND_GEU: + tcg_out_brcond32(s, TCG_COND_GTU, args[1], args[3], const_args[3], + label_this, small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_GEU, args[0], args[2], const_args[2], + label_this, small); + break; + default: + tcg_abort(); + } + tcg_out_label(s, label_next, s->code_ptr); +} +#endif + +static void tcg_out_setcond32(TCGContext *s, TCGCond cond, TCGArg dest, + TCGArg arg1, TCGArg arg2, int const_arg2) +{ + tcg_out_cmp(s, arg1, arg2, const_arg2, 0); + tcg_out_modrm(s, OPC_SETCC | tcg_cond_to_jcc[cond], 0, dest); + tcg_out_ext8u(s, dest, dest); +} + +#if TCG_TARGET_REG_BITS == 64 +static void tcg_out_setcond64(TCGContext *s, TCGCond cond, TCGArg dest, + TCGArg arg1, TCGArg arg2, int const_arg2) +{ + tcg_out_cmp(s, arg1, arg2, const_arg2, P_REXW); + tcg_out_modrm(s, OPC_SETCC | tcg_cond_to_jcc[cond], 0, dest); + tcg_out_ext8u(s, dest, dest); +} +#else +static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, + const int *const_args) +{ + TCGArg new_args[6]; + TCGLabel *label_true, *label_over; + + memcpy(new_args, args+1, 5*sizeof(TCGArg)); + + if (args[0] == args[1] || args[0] == args[2] + || (!const_args[3] && args[0] == args[3]) + || (!const_args[4] && args[0] == args[4])) { + /* When the destination overlaps with one of the argument + registers, don't do anything tricky. */ + label_true = gen_new_label(); + label_over = gen_new_label(); + + new_args[5] = label_arg(label_true); + tcg_out_brcond2(s, new_args, const_args+1, 1); + + tcg_out_movi(s, TCG_TYPE_I32, args[0], 0); + tcg_out_jxx(s, JCC_JMP, label_over, 1); + tcg_out_label(s, label_true, s->code_ptr); + + tcg_out_movi(s, TCG_TYPE_I32, args[0], 1); + tcg_out_label(s, label_over, s->code_ptr); + } else { + /* When the destination does not overlap one of the arguments, + clear the destination first, jump if cond false, and emit an + increment in the true case. This results in smaller code. */ + + tcg_out_movi(s, TCG_TYPE_I32, args[0], 0); + + label_over = gen_new_label(); + new_args[4] = tcg_invert_cond(new_args[4]); + new_args[5] = label_arg(label_over); + tcg_out_brcond2(s, new_args, const_args+1, 1); + + tgen_arithi(s, ARITH_ADD, args[0], 1, 0); + tcg_out_label(s, label_over, s->code_ptr); + } +} +#endif + +static void tcg_out_movcond32(TCGContext *s, TCGCond cond, TCGArg dest, + TCGArg c1, TCGArg c2, int const_c2, + TCGArg v1) +{ + tcg_out_cmp(s, c1, c2, const_c2, 0); + if (have_cmov) { + tcg_out_modrm(s, OPC_CMOVCC | tcg_cond_to_jcc[cond], dest, v1); + } else { + TCGLabel *over = gen_new_label(); + tcg_out_jxx(s, tcg_cond_to_jcc[tcg_invert_cond(cond)], over, 1); + tcg_out_mov(s, TCG_TYPE_I32, dest, v1); + tcg_out_label(s, over, s->code_ptr); + } +} + +#if TCG_TARGET_REG_BITS == 64 +static void tcg_out_movcond64(TCGContext *s, TCGCond cond, TCGArg dest, + TCGArg c1, TCGArg c2, int const_c2, + TCGArg v1) +{ + tcg_out_cmp(s, c1, c2, const_c2, P_REXW); + tcg_out_modrm(s, OPC_CMOVCC | tcg_cond_to_jcc[cond] | P_REXW, dest, v1); +} +#endif + +static void tcg_out_branch(TCGContext *s, int call, tcg_insn_unit *dest) +{ + intptr_t disp = tcg_pcrel_diff(s, dest) - 5; + + if (disp == (int32_t)disp) { + tcg_out_opc(s, call ? OPC_CALL_Jz : OPC_JMP_long, 0, 0, 0); + tcg_out32(s, disp); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R10, (uintptr_t)dest); + tcg_out_modrm(s, OPC_GRP5, + call ? EXT5_CALLN_Ev : EXT5_JMPN_Ev, TCG_REG_R10); + } +} + +static inline void tcg_out_call(TCGContext *s, tcg_insn_unit *dest) +{ + tcg_out_branch(s, 1, dest); +} + +static void tcg_out_jmp(TCGContext *s, tcg_insn_unit *dest) +{ + tcg_out_branch(s, 0, dest); +} + +#if defined(CONFIG_SOFTMMU) +/* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, + * int mmu_idx, uintptr_t ra) + */ +static void * const qemu_ld_helpers[16] = { + [MO_UB] = helper_ret_ldub_mmu, + [MO_LEUW] = helper_le_lduw_mmu, + [MO_LEUL] = helper_le_ldul_mmu, + [MO_LEQ] = helper_le_ldq_mmu, + [MO_BEUW] = helper_be_lduw_mmu, + [MO_BEUL] = helper_be_ldul_mmu, + [MO_BEQ] = helper_be_ldq_mmu, +}; + +/* helper signature: helper_ret_st_mmu(CPUState *env, target_ulong addr, + * uintxx_t val, int mmu_idx, uintptr_t ra) + */ +static void * const qemu_st_helpers[16] = { + [MO_UB] = helper_ret_stb_mmu, + [MO_LEUW] = helper_le_stw_mmu, + [MO_LEUL] = helper_le_stl_mmu, + [MO_LEQ] = helper_le_stq_mmu, + [MO_BEUW] = helper_be_stw_mmu, + [MO_BEUL] = helper_be_stl_mmu, + [MO_BEQ] = helper_be_stq_mmu, +}; + +/* Perform the TLB load and compare. + + Inputs: + ADDRLO and ADDRHI contain the low and high part of the address. + + MEM_INDEX and S_BITS are the memory context and log2 size of the load. + + WHICH is the offset into the CPUTLBEntry structure of the slot to read. + This should be offsetof addr_read or addr_write. + + Outputs: + LABEL_PTRS is filled with 1 (32-bit addresses) or 2 (64-bit addresses) + positions of the displacements of forward jumps to the TLB miss case. + + Second argument register is loaded with the low part of the address. + In the TLB hit case, it has been adjusted as indicated by the TLB + and so is a host address. In the TLB miss case, it continues to + hold a guest address. + + First argument register is clobbered. */ + +static inline void tcg_out_tlb_load(TCGContext *s, TCGReg addrlo, TCGReg addrhi, + int mem_index, TCGMemOp opc, + tcg_insn_unit **label_ptr, int which) +{ + const TCGReg r0 = TCG_REG_L0; + const TCGReg r1 = TCG_REG_L1; + TCGType ttype = TCG_TYPE_I32; + TCGType tlbtype = TCG_TYPE_I32; + int trexw = 0, hrexw = 0, tlbrexw = 0; + int s_mask = (1 << (opc & MO_SIZE)) - 1; + bool aligned = (opc & MO_AMASK) == MO_ALIGN || s_mask == 0; + + if (TCG_TARGET_REG_BITS == 64) { + if (TARGET_LONG_BITS == 64) { + ttype = TCG_TYPE_I64; + trexw = P_REXW; + } + if (TCG_TYPE_PTR == TCG_TYPE_I64) { + hrexw = P_REXW; + if (TARGET_PAGE_BITS + CPU_TLB_BITS > 32) { + tlbtype = TCG_TYPE_I64; + tlbrexw = P_REXW; + } + } + } + + tcg_out_mov(s, tlbtype, r0, addrlo); + if (aligned) { + tcg_out_mov(s, ttype, r1, addrlo); + } else { + /* For unaligned access check that we don't cross pages using + the page address of the last byte. */ + tcg_out_modrm_offset(s, OPC_LEA + trexw, r1, addrlo, s_mask); + } + + tcg_out_shifti(s, SHIFT_SHR + tlbrexw, r0, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); + + tgen_arithi(s, ARITH_AND + trexw, r1, + TARGET_PAGE_MASK | (aligned ? s_mask : 0), 0); + tgen_arithi(s, ARITH_AND + tlbrexw, r0, + (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS, 0); + + tcg_out_modrm_sib_offset(s, OPC_LEA + hrexw, r0, TCG_AREG0, r0, 0, + offsetof(CPUArchState, tlb_table[mem_index][0]) + + which); + + /* cmp 0(r0), r1 */ + tcg_out_modrm_offset(s, OPC_CMP_GvEv + trexw, r1, r0, 0); + + /* Prepare for both the fast path add of the tlb addend, and the slow + path function argument setup. There are two cases worth note: + For 32-bit guest and x86_64 host, MOVL zero-extends the guest address + before the fastpath ADDQ below. For 64-bit guest and x32 host, MOVQ + copies the entire guest address for the slow path, while truncation + for the 32-bit host happens with the fastpath ADDL below. */ + tcg_out_mov(s, ttype, r1, addrlo); + + /* jne slow_path */ + tcg_out_opc(s, OPC_JCC_long + JCC_JNE, 0, 0, 0); + label_ptr[0] = s->code_ptr; + s->code_ptr += 4; + + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + /* cmp 4(r0), addrhi */ + tcg_out_modrm_offset(s, OPC_CMP_GvEv, addrhi, r0, 4); + + /* jne slow_path */ + tcg_out_opc(s, OPC_JCC_long + JCC_JNE, 0, 0, 0); + label_ptr[1] = s->code_ptr; + s->code_ptr += 4; + } + + /* TLB Hit. */ + + /* add addend(r0), r1 */ + tcg_out_modrm_offset(s, OPC_ADD_GvEv + hrexw, r1, r0, + offsetof(CPUTLBEntry, addend) - which); +} + +/* + * Record the context of a call to the out of line helper code for the slow path + * for a load or store, so that we can later generate the correct helper code + */ +static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOpIdx oi, + TCGReg datalo, TCGReg datahi, + TCGReg addrlo, TCGReg addrhi, + tcg_insn_unit *raddr, + tcg_insn_unit **label_ptr) +{ + TCGLabelQemuLdst *label = new_ldst_label(s); + + label->is_ld = is_ld; + label->oi = oi; + label->datalo_reg = datalo; + label->datahi_reg = datahi; + label->addrlo_reg = addrlo; + label->addrhi_reg = addrhi; + label->raddr = raddr; + label->label_ptr[0] = label_ptr[0]; + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + label->label_ptr[1] = label_ptr[1]; + } +} + +/* + * Generate code for the slow path for a load at the end of block + */ +static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + TCGMemOpIdx oi = l->oi; + TCGMemOp opc = get_memop(oi); + TCGReg data_reg; + tcg_insn_unit **label_ptr = &l->label_ptr[0]; + + /* resolve label address */ + tcg_patch32(label_ptr[0], s->code_ptr - label_ptr[0] - 4); + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + tcg_patch32(label_ptr[1], s->code_ptr - label_ptr[1] - 4); + } + + if (TCG_TARGET_REG_BITS == 32) { + int ofs = 0; + + tcg_out_st(s, TCG_TYPE_PTR, TCG_AREG0, TCG_REG_ESP, ofs); + ofs += 4; + + tcg_out_st(s, TCG_TYPE_I32, l->addrlo_reg, TCG_REG_ESP, ofs); + ofs += 4; + + if (TARGET_LONG_BITS == 64) { + tcg_out_st(s, TCG_TYPE_I32, l->addrhi_reg, TCG_REG_ESP, ofs); + ofs += 4; + } + + tcg_out_sti(s, TCG_TYPE_I32, TCG_REG_ESP, ofs, oi); + ofs += 4; + + tcg_out_sti(s, TCG_TYPE_PTR, TCG_REG_ESP, ofs, (uintptr_t)l->raddr); + } else { + tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0); + /* The second argument is already loaded with addrlo. */ + tcg_out_movi(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[2], oi); + tcg_out_movi(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[3], + (uintptr_t)l->raddr); + } + + tcg_out_call(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SIZE)]); + + data_reg = l->datalo_reg; + switch (opc & MO_SSIZE) { + case MO_SB: + tcg_out_ext8s(s, data_reg, TCG_REG_EAX, P_REXW); + break; + case MO_SW: + tcg_out_ext16s(s, data_reg, TCG_REG_EAX, P_REXW); + break; +#if TCG_TARGET_REG_BITS == 64 + case MO_SL: + tcg_out_ext32s(s, data_reg, TCG_REG_EAX); + break; +#endif + case MO_UB: + case MO_UW: + /* Note that the helpers have zero-extended to tcg_target_long. */ + case MO_UL: + tcg_out_mov(s, TCG_TYPE_I32, data_reg, TCG_REG_EAX); + break; + case MO_Q: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_mov(s, TCG_TYPE_I64, data_reg, TCG_REG_RAX); + } else if (data_reg == TCG_REG_EDX) { + /* xchg %edx, %eax */ + tcg_out_opc(s, OPC_XCHG_ax_r32 + TCG_REG_EDX, 0, 0, 0); + tcg_out_mov(s, TCG_TYPE_I32, l->datahi_reg, TCG_REG_EAX); + } else { + tcg_out_mov(s, TCG_TYPE_I32, data_reg, TCG_REG_EAX); + tcg_out_mov(s, TCG_TYPE_I32, l->datahi_reg, TCG_REG_EDX); + } + break; + default: + tcg_abort(); + } + + /* Jump to the code corresponding to next IR of qemu_st */ + tcg_out_jmp(s, l->raddr); +} + +/* + * Generate code for the slow path for a store at the end of block + */ +static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + TCGMemOpIdx oi = l->oi; + TCGMemOp opc = get_memop(oi); + TCGMemOp s_bits = opc & MO_SIZE; + tcg_insn_unit **label_ptr = &l->label_ptr[0]; + TCGReg retaddr; + + /* resolve label address */ + tcg_patch32(label_ptr[0], s->code_ptr - label_ptr[0] - 4); + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + tcg_patch32(label_ptr[1], s->code_ptr - label_ptr[1] - 4); + } + + if (TCG_TARGET_REG_BITS == 32) { + int ofs = 0; + + tcg_out_st(s, TCG_TYPE_PTR, TCG_AREG0, TCG_REG_ESP, ofs); + ofs += 4; + + tcg_out_st(s, TCG_TYPE_I32, l->addrlo_reg, TCG_REG_ESP, ofs); + ofs += 4; + + if (TARGET_LONG_BITS == 64) { + tcg_out_st(s, TCG_TYPE_I32, l->addrhi_reg, TCG_REG_ESP, ofs); + ofs += 4; + } + + tcg_out_st(s, TCG_TYPE_I32, l->datalo_reg, TCG_REG_ESP, ofs); + ofs += 4; + + if (s_bits == MO_64) { + tcg_out_st(s, TCG_TYPE_I32, l->datahi_reg, TCG_REG_ESP, ofs); + ofs += 4; + } + + tcg_out_sti(s, TCG_TYPE_I32, TCG_REG_ESP, ofs, oi); + ofs += 4; + + retaddr = TCG_REG_EAX; + tcg_out_movi(s, TCG_TYPE_PTR, retaddr, (uintptr_t)l->raddr); + tcg_out_st(s, TCG_TYPE_PTR, retaddr, TCG_REG_ESP, ofs); + } else { + tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0); + /* The second argument is already loaded with addrlo. */ + tcg_out_mov(s, (s_bits == MO_64 ? TCG_TYPE_I64 : TCG_TYPE_I32), + tcg_target_call_iarg_regs[2], l->datalo_reg); + tcg_out_movi(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[3], oi); + + if (ARRAY_SIZE(tcg_target_call_iarg_regs) > 4) { + retaddr = tcg_target_call_iarg_regs[4]; + tcg_out_movi(s, TCG_TYPE_PTR, retaddr, (uintptr_t)l->raddr); + } else { + retaddr = TCG_REG_RAX; + tcg_out_movi(s, TCG_TYPE_PTR, retaddr, (uintptr_t)l->raddr); + tcg_out_st(s, TCG_TYPE_PTR, retaddr, TCG_REG_ESP, + TCG_TARGET_CALL_STACK_OFFSET); + } + } + + /* "Tail call" to the helper, with the return address back inline. */ + tcg_out_push(s, retaddr); + tcg_out_jmp(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)]); +} +#elif defined(__x86_64__) && defined(__linux__) +# include +# include + +int arch_prctl(int code, unsigned long addr); + +static int guest_base_flags; +static inline void setup_guest_base_seg(void) +{ + if (arch_prctl(ARCH_SET_GS, guest_base) == 0) { + guest_base_flags = P_GS; + } +} +#else +# define guest_base_flags 0 +static inline void setup_guest_base_seg(void) { } +#endif /* SOFTMMU */ + +static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, + TCGReg base, int index, intptr_t ofs, + int seg, TCGMemOp memop) +{ + const TCGMemOp real_bswap = memop & MO_BSWAP; + TCGMemOp bswap = real_bswap; + int movop = OPC_MOVL_GvEv; + + if (have_movbe && real_bswap) { + bswap = 0; + movop = OPC_MOVBE_GyMy; + } + + switch (memop & MO_SSIZE) { + case MO_UB: + tcg_out_modrm_sib_offset(s, OPC_MOVZBL + seg, datalo, + base, index, 0, ofs); + break; + case MO_SB: + tcg_out_modrm_sib_offset(s, OPC_MOVSBL + P_REXW + seg, datalo, + base, index, 0, ofs); + break; + case MO_UW: + tcg_out_modrm_sib_offset(s, OPC_MOVZWL + seg, datalo, + base, index, 0, ofs); + if (real_bswap) { + tcg_out_rolw_8(s, datalo); + } + break; + case MO_SW: + if (real_bswap) { + if (have_movbe) { + tcg_out_modrm_sib_offset(s, OPC_MOVBE_GyMy + P_DATA16 + seg, + datalo, base, index, 0, ofs); + } else { + tcg_out_modrm_sib_offset(s, OPC_MOVZWL + seg, datalo, + base, index, 0, ofs); + tcg_out_rolw_8(s, datalo); + } + tcg_out_modrm(s, OPC_MOVSWL + P_REXW, datalo, datalo); + } else { + tcg_out_modrm_sib_offset(s, OPC_MOVSWL + P_REXW + seg, + datalo, base, index, 0, ofs); + } + break; + case MO_UL: + tcg_out_modrm_sib_offset(s, movop + seg, datalo, base, index, 0, ofs); + if (bswap) { + tcg_out_bswap32(s, datalo); + } + break; +#if TCG_TARGET_REG_BITS == 64 + case MO_SL: + if (real_bswap) { + tcg_out_modrm_sib_offset(s, movop + seg, datalo, + base, index, 0, ofs); + if (bswap) { + tcg_out_bswap32(s, datalo); + } + tcg_out_ext32s(s, datalo, datalo); + } else { + tcg_out_modrm_sib_offset(s, OPC_MOVSLQ + seg, datalo, + base, index, 0, ofs); + } + break; +#endif + case MO_Q: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_modrm_sib_offset(s, movop + P_REXW + seg, datalo, + base, index, 0, ofs); + if (bswap) { + tcg_out_bswap64(s, datalo); + } + } else { + if (real_bswap) { + int t = datalo; + datalo = datahi; + datahi = t; + } + if (base != datalo) { + tcg_out_modrm_sib_offset(s, movop + seg, datalo, + base, index, 0, ofs); + tcg_out_modrm_sib_offset(s, movop + seg, datahi, + base, index, 0, ofs + 4); + } else { + tcg_out_modrm_sib_offset(s, movop + seg, datahi, + base, index, 0, ofs + 4); + tcg_out_modrm_sib_offset(s, movop + seg, datalo, + base, index, 0, ofs); + } + if (bswap) { + tcg_out_bswap32(s, datalo); + tcg_out_bswap32(s, datahi); + } + } + break; + default: + tcg_abort(); + } +} + +/* XXX: qemu_ld and qemu_st could be modified to clobber only EDX and + EAX. It will be useful once fixed registers globals are less + common. */ +static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is64) +{ + TCGReg datalo, datahi, addrlo; + TCGReg addrhi __attribute__((unused)); + TCGMemOpIdx oi; + TCGMemOp opc; +#if defined(CONFIG_SOFTMMU) + int mem_index; + tcg_insn_unit *label_ptr[2]; +#endif + + datalo = *args++; + datahi = (TCG_TARGET_REG_BITS == 32 && is64 ? *args++ : 0); + addrlo = *args++; + addrhi = (TARGET_LONG_BITS > TCG_TARGET_REG_BITS ? *args++ : 0); + oi = *args++; + opc = get_memop(oi); + +#if defined(CONFIG_SOFTMMU) + mem_index = get_mmuidx(oi); + + tcg_out_tlb_load(s, addrlo, addrhi, mem_index, opc, + label_ptr, offsetof(CPUTLBEntry, addr_read)); + + /* TLB Hit. */ + tcg_out_qemu_ld_direct(s, datalo, datahi, TCG_REG_L1, -1, 0, 0, opc); + + /* Record the current context of a load into ldst label */ + add_qemu_ldst_label(s, true, oi, datalo, datahi, addrlo, addrhi, + s->code_ptr, label_ptr); +#else + { + int32_t offset = guest_base; + TCGReg base = addrlo; + int index = -1; + int seg = 0; + + /* For a 32-bit guest, the high 32 bits may contain garbage. + We can do this with the ADDR32 prefix if we're not using + a guest base, or when using segmentation. Otherwise we + need to zero-extend manually. */ + if (guest_base == 0 || guest_base_flags) { + seg = guest_base_flags; + offset = 0; + if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { + seg |= P_ADDR32; + } + } else if (TCG_TARGET_REG_BITS == 64) { + if (TARGET_LONG_BITS == 32) { + tcg_out_ext32u(s, TCG_REG_L0, base); + base = TCG_REG_L0; + } + if (offset != guest_base) { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_L1, guest_base); + index = TCG_REG_L1; + offset = 0; + } + } + + tcg_out_qemu_ld_direct(s, datalo, datahi, + base, index, offset, seg, opc); + } +#endif +} + +static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, + TCGReg base, intptr_t ofs, int seg, + TCGMemOp memop) +{ + /* ??? Ideally we wouldn't need a scratch register. For user-only, + we could perform the bswap twice to restore the original value + instead of moving to the scratch. But as it is, the L constraint + means that TCG_REG_L0 is definitely free here. */ + const TCGReg scratch = TCG_REG_L0; + const TCGMemOp real_bswap = memop & MO_BSWAP; + TCGMemOp bswap = real_bswap; + int movop = OPC_MOVL_EvGv; + + if (have_movbe && real_bswap) { + bswap = 0; + movop = OPC_MOVBE_MyGy; + } + + switch (memop & MO_SIZE) { + case MO_8: + /* In 32-bit mode, 8-bit stores can only happen from [abcd]x. + Use the scratch register if necessary. */ + if (TCG_TARGET_REG_BITS == 32 && datalo >= 4) { + tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo); + datalo = scratch; + } + tcg_out_modrm_offset(s, OPC_MOVB_EvGv + P_REXB_R + seg, + datalo, base, ofs); + break; + case MO_16: + if (bswap) { + tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo); + tcg_out_rolw_8(s, scratch); + datalo = scratch; + } + tcg_out_modrm_offset(s, movop + P_DATA16 + seg, datalo, base, ofs); + break; + case MO_32: + if (bswap) { + tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo); + tcg_out_bswap32(s, scratch); + datalo = scratch; + } + tcg_out_modrm_offset(s, movop + seg, datalo, base, ofs); + break; + case MO_64: + if (TCG_TARGET_REG_BITS == 64) { + if (bswap) { + tcg_out_mov(s, TCG_TYPE_I64, scratch, datalo); + tcg_out_bswap64(s, scratch); + datalo = scratch; + } + tcg_out_modrm_offset(s, movop + P_REXW + seg, datalo, base, ofs); + } else if (bswap) { + tcg_out_mov(s, TCG_TYPE_I32, scratch, datahi); + tcg_out_bswap32(s, scratch); + tcg_out_modrm_offset(s, OPC_MOVL_EvGv + seg, scratch, base, ofs); + tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo); + tcg_out_bswap32(s, scratch); + tcg_out_modrm_offset(s, OPC_MOVL_EvGv + seg, scratch, base, ofs+4); + } else { + if (real_bswap) { + int t = datalo; + datalo = datahi; + datahi = t; + } + tcg_out_modrm_offset(s, movop + seg, datalo, base, ofs); + tcg_out_modrm_offset(s, movop + seg, datahi, base, ofs+4); + } + break; + default: + tcg_abort(); + } +} + +static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64) +{ + TCGReg datalo, datahi, addrlo; + TCGReg addrhi __attribute__((unused)); + TCGMemOpIdx oi; + TCGMemOp opc; +#if defined(CONFIG_SOFTMMU) + int mem_index; + tcg_insn_unit *label_ptr[2]; +#endif + + datalo = *args++; + datahi = (TCG_TARGET_REG_BITS == 32 && is64 ? *args++ : 0); + addrlo = *args++; + addrhi = (TARGET_LONG_BITS > TCG_TARGET_REG_BITS ? *args++ : 0); + oi = *args++; + opc = get_memop(oi); + +#if defined(CONFIG_SOFTMMU) + mem_index = get_mmuidx(oi); + + tcg_out_tlb_load(s, addrlo, addrhi, mem_index, opc, + label_ptr, offsetof(CPUTLBEntry, addr_write)); + + /* TLB Hit. */ + tcg_out_qemu_st_direct(s, datalo, datahi, TCG_REG_L1, 0, 0, opc); + + /* Record the current context of a store into ldst label */ + add_qemu_ldst_label(s, false, oi, datalo, datahi, addrlo, addrhi, + s->code_ptr, label_ptr); +#else + { + int32_t offset = guest_base; + TCGReg base = addrlo; + int seg = 0; + + /* See comment in tcg_out_qemu_ld re zero-extension of addrlo. */ + if (guest_base == 0 || guest_base_flags) { + seg = guest_base_flags; + offset = 0; + if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { + seg |= P_ADDR32; + } + } else if (TCG_TARGET_REG_BITS == 64) { + /* ??? Note that we can't use the same SIB addressing scheme + as for loads, since we require L0 free for bswap. */ + if (offset != guest_base) { + if (TARGET_LONG_BITS == 32) { + tcg_out_ext32u(s, TCG_REG_L0, base); + base = TCG_REG_L0; + } + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_L1, guest_base); + tgen_arithr(s, ARITH_ADD + P_REXW, TCG_REG_L1, base); + base = TCG_REG_L1; + offset = 0; + } else if (TARGET_LONG_BITS == 32) { + tcg_out_ext32u(s, TCG_REG_L1, base); + base = TCG_REG_L1; + } + } + + tcg_out_qemu_st_direct(s, datalo, datahi, base, offset, seg, opc); + } +#endif +} + +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, + const TCGArg *args, const int *const_args) +{ + int c, vexop, rexw = 0; + +#if TCG_TARGET_REG_BITS == 64 +# define OP_32_64(x) \ + case glue(glue(INDEX_op_, x), _i64): \ + rexw = P_REXW; /* FALLTHRU */ \ + case glue(glue(INDEX_op_, x), _i32) +#else +# define OP_32_64(x) \ + case glue(glue(INDEX_op_, x), _i32) +#endif + + switch(opc) { + case INDEX_op_exit_tb: + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_EAX, args[0]); + tcg_out_jmp(s, tb_ret_addr); + break; + case INDEX_op_goto_tb: + if (s->tb_jmp_offset) { + /* direct jump method */ + tcg_out8(s, OPC_JMP_long); /* jmp im */ + s->tb_jmp_offset[args[0]] = tcg_current_code_size(s); + tcg_out32(s, 0); + } else { + /* indirect jump method */ + tcg_out_modrm_offset(s, OPC_GRP5, EXT5_JMPN_Ev, -1, + (intptr_t)(s->tb_next + args[0])); + } + s->tb_next_offset[args[0]] = tcg_current_code_size(s); + break; + case INDEX_op_br: + tcg_out_jxx(s, JCC_JMP, arg_label(args[0]), 0); + break; + OP_32_64(ld8u): + /* Note that we can ignore REXW for the zero-extend to 64-bit. */ + tcg_out_modrm_offset(s, OPC_MOVZBL, args[0], args[1], args[2]); + break; + OP_32_64(ld8s): + tcg_out_modrm_offset(s, OPC_MOVSBL + rexw, args[0], args[1], args[2]); + break; + OP_32_64(ld16u): + /* Note that we can ignore REXW for the zero-extend to 64-bit. */ + tcg_out_modrm_offset(s, OPC_MOVZWL, args[0], args[1], args[2]); + break; + OP_32_64(ld16s): + tcg_out_modrm_offset(s, OPC_MOVSWL + rexw, args[0], args[1], args[2]); + break; +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_ld32u_i64: +#endif + case INDEX_op_ld_i32: + tcg_out_ld(s, TCG_TYPE_I32, args[0], args[1], args[2]); + break; + + OP_32_64(st8): + if (const_args[0]) { + tcg_out_modrm_offset(s, OPC_MOVB_EvIz, + 0, args[1], args[2]); + tcg_out8(s, args[0]); + } else { + tcg_out_modrm_offset(s, OPC_MOVB_EvGv | P_REXB_R, + args[0], args[1], args[2]); + } + break; + OP_32_64(st16): + if (const_args[0]) { + tcg_out_modrm_offset(s, OPC_MOVL_EvIz | P_DATA16, + 0, args[1], args[2]); + tcg_out16(s, args[0]); + } else { + tcg_out_modrm_offset(s, OPC_MOVL_EvGv | P_DATA16, + args[0], args[1], args[2]); + } + break; +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_st32_i64: +#endif + case INDEX_op_st_i32: + if (const_args[0]) { + tcg_out_modrm_offset(s, OPC_MOVL_EvIz, 0, args[1], args[2]); + tcg_out32(s, args[0]); + } else { + tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); + } + break; + + OP_32_64(add): + /* For 3-operand addition, use LEA. */ + if (args[0] != args[1]) { + TCGArg a0 = args[0], a1 = args[1], a2 = args[2], c3 = 0; + + if (const_args[2]) { + c3 = a2, a2 = -1; + } else if (a0 == a2) { + /* Watch out for dest = src + dest, since we've removed + the matching constraint on the add. */ + tgen_arithr(s, ARITH_ADD + rexw, a0, a1); + break; + } + + tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, a1, a2, 0, c3); + break; + } + c = ARITH_ADD; + goto gen_arith; + OP_32_64(sub): + c = ARITH_SUB; + goto gen_arith; + OP_32_64(and): + c = ARITH_AND; + goto gen_arith; + OP_32_64(or): + c = ARITH_OR; + goto gen_arith; + OP_32_64(xor): + c = ARITH_XOR; + goto gen_arith; + gen_arith: + if (const_args[2]) { + tgen_arithi(s, c + rexw, args[0], args[2], 0); + } else { + tgen_arithr(s, c + rexw, args[0], args[2]); + } + break; + + OP_32_64(andc): + if (const_args[2]) { + tcg_out_mov(s, rexw ? TCG_TYPE_I64 : TCG_TYPE_I32, + args[0], args[1]); + tgen_arithi(s, ARITH_AND + rexw, args[0], ~args[2], 0); + } else { + tcg_out_vex_modrm(s, OPC_ANDN + rexw, args[0], args[2], args[1]); + } + break; + + OP_32_64(mul): + if (const_args[2]) { + int32_t val; + val = args[2]; + if (val == (int8_t)val) { + tcg_out_modrm(s, OPC_IMUL_GvEvIb + rexw, args[0], args[0]); + tcg_out8(s, val); + } else { + tcg_out_modrm(s, OPC_IMUL_GvEvIz + rexw, args[0], args[0]); + tcg_out32(s, val); + } + } else { + tcg_out_modrm(s, OPC_IMUL_GvEv + rexw, args[0], args[2]); + } + break; + + OP_32_64(div2): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_IDIV, args[4]); + break; + OP_32_64(divu2): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_DIV, args[4]); + break; + + OP_32_64(shl): + c = SHIFT_SHL; + vexop = OPC_SHLX; + goto gen_shift_maybe_vex; + OP_32_64(shr): + c = SHIFT_SHR; + vexop = OPC_SHRX; + goto gen_shift_maybe_vex; + OP_32_64(sar): + c = SHIFT_SAR; + vexop = OPC_SARX; + goto gen_shift_maybe_vex; + OP_32_64(rotl): + c = SHIFT_ROL; + goto gen_shift; + OP_32_64(rotr): + c = SHIFT_ROR; + goto gen_shift; + gen_shift_maybe_vex: + if (have_bmi2 && !const_args[2]) { + tcg_out_vex_modrm(s, vexop + rexw, args[0], args[2], args[1]); + break; + } + /* FALLTHRU */ + gen_shift: + if (const_args[2]) { + tcg_out_shifti(s, c + rexw, args[0], args[2]); + } else { + tcg_out_modrm(s, OPC_SHIFT_cl + rexw, c, args[0]); + } + break; + + case INDEX_op_brcond_i32: + tcg_out_brcond32(s, args[2], args[0], args[1], const_args[1], + arg_label(args[3]), 0); + break; + case INDEX_op_setcond_i32: + tcg_out_setcond32(s, args[3], args[0], args[1], + args[2], const_args[2]); + break; + case INDEX_op_movcond_i32: + tcg_out_movcond32(s, args[5], args[0], args[1], + args[2], const_args[2], args[3]); + break; + + OP_32_64(bswap16): + tcg_out_rolw_8(s, args[0]); + break; + OP_32_64(bswap32): + tcg_out_bswap32(s, args[0]); + break; + + OP_32_64(neg): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NEG, args[0]); + break; + OP_32_64(not): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NOT, args[0]); + break; + + OP_32_64(ext8s): + tcg_out_ext8s(s, args[0], args[1], rexw); + break; + OP_32_64(ext16s): + tcg_out_ext16s(s, args[0], args[1], rexw); + break; + OP_32_64(ext8u): + tcg_out_ext8u(s, args[0], args[1]); + break; + OP_32_64(ext16u): + tcg_out_ext16u(s, args[0], args[1]); + break; + + case INDEX_op_qemu_ld_i32: + tcg_out_qemu_ld(s, args, 0); + break; + case INDEX_op_qemu_ld_i64: + tcg_out_qemu_ld(s, args, 1); + break; + case INDEX_op_qemu_st_i32: + tcg_out_qemu_st(s, args, 0); + break; + case INDEX_op_qemu_st_i64: + tcg_out_qemu_st(s, args, 1); + break; + + OP_32_64(mulu2): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_MUL, args[3]); + break; + OP_32_64(muls2): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_IMUL, args[3]); + break; + OP_32_64(add2): + if (const_args[4]) { + tgen_arithi(s, ARITH_ADD + rexw, args[0], args[4], 1); + } else { + tgen_arithr(s, ARITH_ADD + rexw, args[0], args[4]); + } + if (const_args[5]) { + tgen_arithi(s, ARITH_ADC + rexw, args[1], args[5], 1); + } else { + tgen_arithr(s, ARITH_ADC + rexw, args[1], args[5]); + } + break; + OP_32_64(sub2): + if (const_args[4]) { + tgen_arithi(s, ARITH_SUB + rexw, args[0], args[4], 1); + } else { + tgen_arithr(s, ARITH_SUB + rexw, args[0], args[4]); + } + if (const_args[5]) { + tgen_arithi(s, ARITH_SBB + rexw, args[1], args[5], 1); + } else { + tgen_arithr(s, ARITH_SBB + rexw, args[1], args[5]); + } + break; + +#if TCG_TARGET_REG_BITS == 32 + case INDEX_op_brcond2_i32: + tcg_out_brcond2(s, args, const_args, 0); + break; + case INDEX_op_setcond2_i32: + tcg_out_setcond2(s, args, const_args); + break; +#else /* TCG_TARGET_REG_BITS == 64 */ + case INDEX_op_ld32s_i64: + tcg_out_modrm_offset(s, OPC_MOVSLQ, args[0], args[1], args[2]); + break; + case INDEX_op_ld_i64: + tcg_out_ld(s, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + case INDEX_op_st_i64: + if (const_args[0]) { + tcg_out_modrm_offset(s, OPC_MOVL_EvIz | P_REXW, + 0, args[1], args[2]); + tcg_out32(s, args[0]); + } else { + tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]); + } + break; + + case INDEX_op_brcond_i64: + tcg_out_brcond64(s, args[2], args[0], args[1], const_args[1], + arg_label(args[3]), 0); + break; + case INDEX_op_setcond_i64: + tcg_out_setcond64(s, args[3], args[0], args[1], + args[2], const_args[2]); + break; + case INDEX_op_movcond_i64: + tcg_out_movcond64(s, args[5], args[0], args[1], + args[2], const_args[2], args[3]); + break; + + case INDEX_op_bswap64_i64: + tcg_out_bswap64(s, args[0]); + break; + case INDEX_op_extu_i32_i64: + case INDEX_op_ext32u_i64: + tcg_out_ext32u(s, args[0], args[1]); + break; + case INDEX_op_ext_i32_i64: + case INDEX_op_ext32s_i64: + tcg_out_ext32s(s, args[0], args[1]); + break; +#endif + + OP_32_64(deposit): + if (args[3] == 0 && args[4] == 8) { + /* load bits 0..7 */ + tcg_out_modrm(s, OPC_MOVB_EvGv | P_REXB_R | P_REXB_RM, + args[2], args[0]); + } else if (args[3] == 8 && args[4] == 8) { + /* load bits 8..15 */ + tcg_out_modrm(s, OPC_MOVB_EvGv, args[2], args[0] + 4); + } else if (args[3] == 0 && args[4] == 16) { + /* load bits 0..15 */ + tcg_out_modrm(s, OPC_MOVL_EvGv | P_DATA16, args[2], args[0]); + } else { + tcg_abort(); + } + break; + + case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ + case INDEX_op_mov_i64: + case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ + case INDEX_op_movi_i64: + case INDEX_op_call: /* Always emitted via tcg_out_call. */ + default: + tcg_abort(); + } + +#undef OP_32_64 +} + +static const TCGTargetOpDef x86_op_defs[] = { + { INDEX_op_exit_tb, { } }, + { INDEX_op_goto_tb, { } }, + { INDEX_op_br, { } }, + { INDEX_op_ld8u_i32, { "r", "r" } }, + { INDEX_op_ld8s_i32, { "r", "r" } }, + { INDEX_op_ld16u_i32, { "r", "r" } }, + { INDEX_op_ld16s_i32, { "r", "r" } }, + { INDEX_op_ld_i32, { "r", "r" } }, + { INDEX_op_st8_i32, { "qi", "r" } }, + { INDEX_op_st16_i32, { "ri", "r" } }, + { INDEX_op_st_i32, { "ri", "r" } }, + + { INDEX_op_add_i32, { "r", "r", "ri" } }, + { INDEX_op_sub_i32, { "r", "0", "ri" } }, + { INDEX_op_mul_i32, { "r", "0", "ri" } }, + { INDEX_op_div2_i32, { "a", "d", "0", "1", "r" } }, + { INDEX_op_divu2_i32, { "a", "d", "0", "1", "r" } }, + { INDEX_op_and_i32, { "r", "0", "ri" } }, + { INDEX_op_or_i32, { "r", "0", "ri" } }, + { INDEX_op_xor_i32, { "r", "0", "ri" } }, + { INDEX_op_andc_i32, { "r", "r", "ri" } }, + + { INDEX_op_shl_i32, { "r", "0", "Ci" } }, + { INDEX_op_shr_i32, { "r", "0", "Ci" } }, + { INDEX_op_sar_i32, { "r", "0", "Ci" } }, + { INDEX_op_rotl_i32, { "r", "0", "ci" } }, + { INDEX_op_rotr_i32, { "r", "0", "ci" } }, + + { INDEX_op_brcond_i32, { "r", "ri" } }, + + { INDEX_op_bswap16_i32, { "r", "0" } }, + { INDEX_op_bswap32_i32, { "r", "0" } }, + + { INDEX_op_neg_i32, { "r", "0" } }, + + { INDEX_op_not_i32, { "r", "0" } }, + + { INDEX_op_ext8s_i32, { "r", "q" } }, + { INDEX_op_ext16s_i32, { "r", "r" } }, + { INDEX_op_ext8u_i32, { "r", "q" } }, + { INDEX_op_ext16u_i32, { "r", "r" } }, + + { INDEX_op_setcond_i32, { "q", "r", "ri" } }, + + { INDEX_op_deposit_i32, { "Q", "0", "Q" } }, + { INDEX_op_movcond_i32, { "r", "r", "ri", "r", "0" } }, + + { INDEX_op_mulu2_i32, { "a", "d", "a", "r" } }, + { INDEX_op_muls2_i32, { "a", "d", "a", "r" } }, + { INDEX_op_add2_i32, { "r", "r", "0", "1", "ri", "ri" } }, + { INDEX_op_sub2_i32, { "r", "r", "0", "1", "ri", "ri" } }, + +#if TCG_TARGET_REG_BITS == 32 + { INDEX_op_brcond2_i32, { "r", "r", "ri", "ri" } }, + { INDEX_op_setcond2_i32, { "r", "r", "r", "ri", "ri" } }, +#else + { INDEX_op_ld8u_i64, { "r", "r" } }, + { INDEX_op_ld8s_i64, { "r", "r" } }, + { INDEX_op_ld16u_i64, { "r", "r" } }, + { INDEX_op_ld16s_i64, { "r", "r" } }, + { INDEX_op_ld32u_i64, { "r", "r" } }, + { INDEX_op_ld32s_i64, { "r", "r" } }, + { INDEX_op_ld_i64, { "r", "r" } }, + { INDEX_op_st8_i64, { "ri", "r" } }, + { INDEX_op_st16_i64, { "ri", "r" } }, + { INDEX_op_st32_i64, { "ri", "r" } }, + { INDEX_op_st_i64, { "re", "r" } }, + + { INDEX_op_add_i64, { "r", "r", "re" } }, + { INDEX_op_mul_i64, { "r", "0", "re" } }, + { INDEX_op_div2_i64, { "a", "d", "0", "1", "r" } }, + { INDEX_op_divu2_i64, { "a", "d", "0", "1", "r" } }, + { INDEX_op_sub_i64, { "r", "0", "re" } }, + { INDEX_op_and_i64, { "r", "0", "reZ" } }, + { INDEX_op_or_i64, { "r", "0", "re" } }, + { INDEX_op_xor_i64, { "r", "0", "re" } }, + { INDEX_op_andc_i64, { "r", "r", "rI" } }, + + { INDEX_op_shl_i64, { "r", "0", "Ci" } }, + { INDEX_op_shr_i64, { "r", "0", "Ci" } }, + { INDEX_op_sar_i64, { "r", "0", "Ci" } }, + { INDEX_op_rotl_i64, { "r", "0", "ci" } }, + { INDEX_op_rotr_i64, { "r", "0", "ci" } }, + + { INDEX_op_brcond_i64, { "r", "re" } }, + { INDEX_op_setcond_i64, { "r", "r", "re" } }, + + { INDEX_op_bswap16_i64, { "r", "0" } }, + { INDEX_op_bswap32_i64, { "r", "0" } }, + { INDEX_op_bswap64_i64, { "r", "0" } }, + { INDEX_op_neg_i64, { "r", "0" } }, + { INDEX_op_not_i64, { "r", "0" } }, + + { INDEX_op_ext8s_i64, { "r", "r" } }, + { INDEX_op_ext16s_i64, { "r", "r" } }, + { INDEX_op_ext32s_i64, { "r", "r" } }, + { INDEX_op_ext8u_i64, { "r", "r" } }, + { INDEX_op_ext16u_i64, { "r", "r" } }, + { INDEX_op_ext32u_i64, { "r", "r" } }, + + { INDEX_op_ext_i32_i64, { "r", "r" } }, + { INDEX_op_extu_i32_i64, { "r", "r" } }, + + { INDEX_op_deposit_i64, { "Q", "0", "Q" } }, + { INDEX_op_movcond_i64, { "r", "r", "re", "r", "0" } }, + + { INDEX_op_mulu2_i64, { "a", "d", "a", "r" } }, + { INDEX_op_muls2_i64, { "a", "d", "a", "r" } }, + { INDEX_op_add2_i64, { "r", "r", "0", "1", "re", "re" } }, + { INDEX_op_sub2_i64, { "r", "r", "0", "1", "re", "re" } }, +#endif + +#if TCG_TARGET_REG_BITS == 64 + { INDEX_op_qemu_ld_i32, { "r", "L" } }, + { INDEX_op_qemu_st_i32, { "L", "L" } }, + { INDEX_op_qemu_ld_i64, { "r", "L" } }, + { INDEX_op_qemu_st_i64, { "L", "L" } }, +#elif TARGET_LONG_BITS <= TCG_TARGET_REG_BITS + { INDEX_op_qemu_ld_i32, { "r", "L" } }, + { INDEX_op_qemu_st_i32, { "L", "L" } }, + { INDEX_op_qemu_ld_i64, { "r", "r", "L" } }, + { INDEX_op_qemu_st_i64, { "L", "L", "L" } }, +#else + { INDEX_op_qemu_ld_i32, { "r", "L", "L" } }, + { INDEX_op_qemu_st_i32, { "L", "L", "L" } }, + { INDEX_op_qemu_ld_i64, { "r", "r", "L", "L" } }, + { INDEX_op_qemu_st_i64, { "L", "L", "L", "L" } }, +#endif + { -1 }, +}; + +static int tcg_target_callee_save_regs[] = { +#if TCG_TARGET_REG_BITS == 64 + TCG_REG_RBP, + TCG_REG_RBX, +#if defined(_WIN64) + TCG_REG_RDI, + TCG_REG_RSI, +#endif + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, /* Currently used for the global env. */ + TCG_REG_R15, +#else + TCG_REG_EBP, /* Currently used for the global env. */ + TCG_REG_EBX, + TCG_REG_ESI, + TCG_REG_EDI, +#endif +}; + +/* Compute frame size via macros, to share between tcg_target_qemu_prologue + and tcg_register_jit. */ + +#define PUSH_SIZE \ + ((1 + ARRAY_SIZE(tcg_target_callee_save_regs)) \ + * (TCG_TARGET_REG_BITS / 8)) + +#define FRAME_SIZE \ + ((PUSH_SIZE \ + + TCG_STATIC_CALL_ARGS_SIZE \ + + CPU_TEMP_BUF_NLONGS * sizeof(long) \ + + TCG_TARGET_STACK_ALIGN - 1) \ + & ~(TCG_TARGET_STACK_ALIGN - 1)) + +/* Generate global QEMU prologue and epilogue code */ +static void tcg_target_qemu_prologue(TCGContext *s) +{ + int i, stack_addend; + + /* TB prologue */ + + /* Reserve some stack space, also for TCG temps. */ + stack_addend = FRAME_SIZE - PUSH_SIZE; + tcg_set_frame(s, TCG_REG_CALL_STACK, TCG_STATIC_CALL_ARGS_SIZE, + CPU_TEMP_BUF_NLONGS * sizeof(long)); + + /* Save all callee saved registers. */ + for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) { + tcg_out_push(s, tcg_target_callee_save_regs[i]); + } + +#if TCG_TARGET_REG_BITS == 32 + tcg_out_ld(s, TCG_TYPE_PTR, TCG_AREG0, TCG_REG_ESP, + (ARRAY_SIZE(tcg_target_callee_save_regs) + 1) * 4); + tcg_out_addi(s, TCG_REG_ESP, -stack_addend); + /* jmp *tb. */ + tcg_out_modrm_offset(s, OPC_GRP5, EXT5_JMPN_Ev, TCG_REG_ESP, + (ARRAY_SIZE(tcg_target_callee_save_regs) + 2) * 4 + + stack_addend); +#else + tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); + tcg_out_addi(s, TCG_REG_ESP, -stack_addend); + /* jmp *tb. */ + tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, tcg_target_call_iarg_regs[1]); +#endif + + /* TB epilogue */ + tb_ret_addr = s->code_ptr; + + tcg_out_addi(s, TCG_REG_CALL_STACK, stack_addend); + + for (i = ARRAY_SIZE(tcg_target_callee_save_regs) - 1; i >= 0; i--) { + tcg_out_pop(s, tcg_target_callee_save_regs[i]); + } + tcg_out_opc(s, OPC_RET, 0, 0, 0); + +#if !defined(CONFIG_SOFTMMU) + /* Try to set up a segment register to point to guest_base. */ + if (guest_base) { + setup_guest_base_seg(); + } +#endif +} + +static void tcg_target_init(TCGContext *s) +{ +#ifdef CONFIG_CPUID_H + unsigned a, b, c, d; + int max = __get_cpuid_max(0, 0); + + if (max >= 1) { + __cpuid(1, a, b, c, d); +#ifndef have_cmov + /* For 32-bit, 99% certainty that we're running on hardware that + supports cmov, but we still need to check. In case cmov is not + available, we'll use a small forward branch. */ + have_cmov = (d & bit_CMOV) != 0; +#endif +#ifndef have_movbe + /* MOVBE is only available on Intel Atom and Haswell CPUs, so we + need to probe for it. */ + have_movbe = (c & bit_MOVBE) != 0; +#endif + } + + if (max >= 7) { + /* BMI1 is available on AMD Piledriver and Intel Haswell CPUs. */ + __cpuid_count(7, 0, a, b, c, d); +#ifdef bit_BMI + have_bmi1 = (b & bit_BMI) != 0; +#endif +#ifndef have_bmi2 + have_bmi2 = (b & bit_BMI2) != 0; +#endif + } +#endif + + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff); + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffff); + } else { + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xff); + } + + tcg_regset_clear(tcg_target_call_clobber_regs); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_EAX); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_EDX); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_ECX); + if (TCG_TARGET_REG_BITS == 64) { +#if !defined(_WIN64) + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RDI); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RSI); +#endif + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R8); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R9); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R10); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R11); + } + + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); + + tcg_add_target_add_op_defs(x86_op_defs); +} + +typedef struct { + DebugFrameHeader h; + uint8_t fde_def_cfa[4]; + uint8_t fde_reg_ofs[14]; +} DebugFrame; + +/* We're expecting a 2 byte uleb128 encoded value. */ +QEMU_BUILD_BUG_ON(FRAME_SIZE >= (1 << 14)); + +#if !defined(__ELF__) + /* Host machine without ELF. */ +#elif TCG_TARGET_REG_BITS == 64 +#define ELF_HOST_MACHINE EM_X86_64 +static const DebugFrame debug_frame = { + .h.cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ + .h.cie.id = -1, + .h.cie.version = 1, + .h.cie.code_align = 1, + .h.cie.data_align = 0x78, /* sleb128 -8 */ + .h.cie.return_column = 16, + + /* Total FDE size does not include the "len" member. */ + .h.fde.len = sizeof(DebugFrame) - offsetof(DebugFrame, h.fde.cie_offset), + + .fde_def_cfa = { + 12, 7, /* DW_CFA_def_cfa %rsp, ... */ + (FRAME_SIZE & 0x7f) | 0x80, /* ... uleb128 FRAME_SIZE */ + (FRAME_SIZE >> 7) + }, + .fde_reg_ofs = { + 0x90, 1, /* DW_CFA_offset, %rip, -8 */ + /* The following ordering must match tcg_target_callee_save_regs. */ + 0x86, 2, /* DW_CFA_offset, %rbp, -16 */ + 0x83, 3, /* DW_CFA_offset, %rbx, -24 */ + 0x8c, 4, /* DW_CFA_offset, %r12, -32 */ + 0x8d, 5, /* DW_CFA_offset, %r13, -40 */ + 0x8e, 6, /* DW_CFA_offset, %r14, -48 */ + 0x8f, 7, /* DW_CFA_offset, %r15, -56 */ + } +}; +#else +#define ELF_HOST_MACHINE EM_386 +static const DebugFrame debug_frame = { + .h.cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ + .h.cie.id = -1, + .h.cie.version = 1, + .h.cie.code_align = 1, + .h.cie.data_align = 0x7c, /* sleb128 -4 */ + .h.cie.return_column = 8, + + /* Total FDE size does not include the "len" member. */ + .h.fde.len = sizeof(DebugFrame) - offsetof(DebugFrame, h.fde.cie_offset), + + .fde_def_cfa = { + 12, 4, /* DW_CFA_def_cfa %esp, ... */ + (FRAME_SIZE & 0x7f) | 0x80, /* ... uleb128 FRAME_SIZE */ + (FRAME_SIZE >> 7) + }, + .fde_reg_ofs = { + 0x88, 1, /* DW_CFA_offset, %eip, -4 */ + /* The following ordering must match tcg_target_callee_save_regs. */ + 0x85, 2, /* DW_CFA_offset, %ebp, -8 */ + 0x83, 3, /* DW_CFA_offset, %ebx, -12 */ + 0x86, 4, /* DW_CFA_offset, %esi, -16 */ + 0x87, 5, /* DW_CFA_offset, %edi, -20 */ + } +}; +#endif + +#if defined(ELF_HOST_MACHINE) +void tcg_register_jit(void *buf, size_t buf_size) +{ + tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); +} +#endif diff --git a/qemu/tcg/ia64/tcg-target.c b/qemu/tcg/ia64/tcg-target.c deleted file mode 100644 index 81cb9f79f..000000000 --- a/qemu/tcg/ia64/tcg-target.c +++ /dev/null @@ -1,2445 +0,0 @@ -/* - * Tiny Code Generator for QEMU - * - * Copyright (c) 2009-2010 Aurelien Jarno - * Based on i386/tcg-target.c - Copyright (c) 2008 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* - * Register definitions - */ - -#ifndef NDEBUG -static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { - "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", - "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", - "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", - "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", - "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39", - "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47", - "r48", "r49", "r50", "r51", "r52", "r53", "r54", "r55", - "r56", "r57", "r58", "r59", "r60", "r61", "r62", "r63", -}; -#endif - -#ifdef CONFIG_USE_GUEST_BASE -#define TCG_GUEST_BASE_REG TCG_REG_R55 -#else -#define TCG_GUEST_BASE_REG TCG_REG_R0 -#endif -#ifndef GUEST_BASE -#define GUEST_BASE 0 -#endif - -/* Branch registers */ -enum { - TCG_REG_B0 = 0, - TCG_REG_B1, - TCG_REG_B2, - TCG_REG_B3, - TCG_REG_B4, - TCG_REG_B5, - TCG_REG_B6, - TCG_REG_B7, -}; - -/* Floating point registers */ -enum { - TCG_REG_F0 = 0, - TCG_REG_F1, - TCG_REG_F2, - TCG_REG_F3, - TCG_REG_F4, - TCG_REG_F5, - TCG_REG_F6, - TCG_REG_F7, - TCG_REG_F8, - TCG_REG_F9, - TCG_REG_F10, - TCG_REG_F11, - TCG_REG_F12, - TCG_REG_F13, - TCG_REG_F14, - TCG_REG_F15, -}; - -/* Predicate registers */ -enum { - TCG_REG_P0 = 0, - TCG_REG_P1, - TCG_REG_P2, - TCG_REG_P3, - TCG_REG_P4, - TCG_REG_P5, - TCG_REG_P6, - TCG_REG_P7, - TCG_REG_P8, - TCG_REG_P9, - TCG_REG_P10, - TCG_REG_P11, - TCG_REG_P12, - TCG_REG_P13, - TCG_REG_P14, - TCG_REG_P15, -}; - -/* Application registers */ -enum { - TCG_REG_PFS = 64, -}; - -static const int tcg_target_reg_alloc_order[] = { - TCG_REG_R35, - TCG_REG_R36, - TCG_REG_R37, - TCG_REG_R38, - TCG_REG_R39, - TCG_REG_R40, - TCG_REG_R41, - TCG_REG_R42, - TCG_REG_R43, - TCG_REG_R44, - TCG_REG_R45, - TCG_REG_R46, - TCG_REG_R47, - TCG_REG_R48, - TCG_REG_R49, - TCG_REG_R50, - TCG_REG_R51, - TCG_REG_R52, - TCG_REG_R53, - TCG_REG_R54, - TCG_REG_R55, - TCG_REG_R14, - TCG_REG_R15, - TCG_REG_R16, - TCG_REG_R17, - TCG_REG_R18, - TCG_REG_R19, - TCG_REG_R20, - TCG_REG_R21, - TCG_REG_R22, - TCG_REG_R23, - TCG_REG_R24, - TCG_REG_R25, - TCG_REG_R26, - TCG_REG_R27, - TCG_REG_R28, - TCG_REG_R29, - TCG_REG_R30, - TCG_REG_R31, - TCG_REG_R56, - TCG_REG_R57, - TCG_REG_R58, - TCG_REG_R59, - TCG_REG_R60, - TCG_REG_R61, - TCG_REG_R62, - TCG_REG_R63, - TCG_REG_R8, - TCG_REG_R9, - TCG_REG_R10, - TCG_REG_R11 -}; - -static const int tcg_target_call_iarg_regs[8] = { - TCG_REG_R56, - TCG_REG_R57, - TCG_REG_R58, - TCG_REG_R59, - TCG_REG_R60, - TCG_REG_R61, - TCG_REG_R62, - TCG_REG_R63, -}; - -static const int tcg_target_call_oarg_regs[] = { - TCG_REG_R8 -}; - -/* - * opcode formation - */ - -/* bundle templates: stops (double bar in the IA64 manual) are marked with - an uppercase letter. */ -enum { - mii = 0x00, - miI = 0x01, - mIi = 0x02, - mII = 0x03, - mlx = 0x04, - mLX = 0x05, - mmi = 0x08, - mmI = 0x09, - Mmi = 0x0a, - MmI = 0x0b, - mfi = 0x0c, - mfI = 0x0d, - mmf = 0x0e, - mmF = 0x0f, - mib = 0x10, - miB = 0x11, - mbb = 0x12, - mbB = 0x13, - bbb = 0x16, - bbB = 0x17, - mmb = 0x18, - mmB = 0x19, - mfb = 0x1c, - mfB = 0x1d, -}; - -enum { - OPC_ADD_A1 = 0x10000000000ull, - OPC_AND_A1 = 0x10060000000ull, - OPC_AND_A3 = 0x10160000000ull, - OPC_ANDCM_A1 = 0x10068000000ull, - OPC_ANDCM_A3 = 0x10168000000ull, - OPC_ADDS_A4 = 0x10800000000ull, - OPC_ADDL_A5 = 0x12000000000ull, - OPC_ALLOC_M34 = 0x02c00000000ull, - OPC_BR_DPTK_FEW_B1 = 0x08400000000ull, - OPC_BR_SPTK_MANY_B1 = 0x08000001000ull, - OPC_BR_CALL_SPNT_FEW_B3 = 0x0a200000000ull, - OPC_BR_SPTK_MANY_B4 = 0x00100001000ull, - OPC_BR_CALL_SPTK_MANY_B5 = 0x02100001000ull, - OPC_BR_RET_SPTK_MANY_B4 = 0x00108001100ull, - OPC_BRL_SPTK_MANY_X3 = 0x18000001000ull, - OPC_BRL_CALL_SPNT_MANY_X4 = 0x1a200001000ull, - OPC_BRL_CALL_SPTK_MANY_X4 = 0x1a000001000ull, - OPC_CMP_LT_A6 = 0x18000000000ull, - OPC_CMP_LTU_A6 = 0x1a000000000ull, - OPC_CMP_EQ_A6 = 0x1c000000000ull, - OPC_CMP4_LT_A6 = 0x18400000000ull, - OPC_CMP4_LTU_A6 = 0x1a400000000ull, - OPC_CMP4_EQ_A6 = 0x1c400000000ull, - OPC_DEP_I14 = 0x0ae00000000ull, - OPC_DEP_I15 = 0x08000000000ull, - OPC_DEP_Z_I12 = 0x0a600000000ull, - OPC_EXTR_I11 = 0x0a400002000ull, - OPC_EXTR_U_I11 = 0x0a400000000ull, - OPC_FCVT_FX_TRUNC_S1_F10 = 0x004d0000000ull, - OPC_FCVT_FXU_TRUNC_S1_F10 = 0x004d8000000ull, - OPC_FCVT_XF_F11 = 0x000e0000000ull, - OPC_FMA_S1_F1 = 0x10400000000ull, - OPC_FNMA_S1_F1 = 0x18400000000ull, - OPC_FRCPA_S1_F6 = 0x00600000000ull, - OPC_GETF_SIG_M19 = 0x08708000000ull, - OPC_LD1_M1 = 0x08000000000ull, - OPC_LD1_M3 = 0x0a000000000ull, - OPC_LD2_M1 = 0x08040000000ull, - OPC_LD2_M3 = 0x0a040000000ull, - OPC_LD4_M1 = 0x08080000000ull, - OPC_LD4_M3 = 0x0a080000000ull, - OPC_LD8_M1 = 0x080c0000000ull, - OPC_LD8_M3 = 0x0a0c0000000ull, - OPC_MUX1_I3 = 0x0eca0000000ull, - OPC_NOP_B9 = 0x04008000000ull, - OPC_NOP_F16 = 0x00008000000ull, - OPC_NOP_I18 = 0x00008000000ull, - OPC_NOP_M48 = 0x00008000000ull, - OPC_MOV_I21 = 0x00e00100000ull, - OPC_MOV_RET_I21 = 0x00e00500000ull, - OPC_MOV_I22 = 0x00188000000ull, - OPC_MOV_I_I26 = 0x00150000000ull, - OPC_MOVL_X2 = 0x0c000000000ull, - OPC_OR_A1 = 0x10070000000ull, - OPC_OR_A3 = 0x10170000000ull, - OPC_SETF_EXP_M18 = 0x0c748000000ull, - OPC_SETF_SIG_M18 = 0x0c708000000ull, - OPC_SHL_I7 = 0x0f240000000ull, - OPC_SHR_I5 = 0x0f220000000ull, - OPC_SHR_U_I5 = 0x0f200000000ull, - OPC_SHRP_I10 = 0x0ac00000000ull, - OPC_SXT1_I29 = 0x000a0000000ull, - OPC_SXT2_I29 = 0x000a8000000ull, - OPC_SXT4_I29 = 0x000b0000000ull, - OPC_ST1_M4 = 0x08c00000000ull, - OPC_ST2_M4 = 0x08c40000000ull, - OPC_ST4_M4 = 0x08c80000000ull, - OPC_ST8_M4 = 0x08cc0000000ull, - OPC_SUB_A1 = 0x10028000000ull, - OPC_SUB_A3 = 0x10128000000ull, - OPC_UNPACK4_L_I2 = 0x0f860000000ull, - OPC_XMA_L_F2 = 0x1d000000000ull, - OPC_XOR_A1 = 0x10078000000ull, - OPC_XOR_A3 = 0x10178000000ull, - OPC_ZXT1_I29 = 0x00080000000ull, - OPC_ZXT2_I29 = 0x00088000000ull, - OPC_ZXT4_I29 = 0x00090000000ull, - - INSN_NOP_M = OPC_NOP_M48, /* nop.m 0 */ - INSN_NOP_I = OPC_NOP_I18, /* nop.i 0 */ -}; - -static inline uint64_t tcg_opc_a1(int qp, uint64_t opc, int r1, - int r2, int r3) -{ - return opc - | ((r3 & 0x7f) << 20) - | ((r2 & 0x7f) << 13) - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_a3(int qp, uint64_t opc, int r1, - uint64_t imm, int r3) -{ - return opc - | ((imm & 0x80) << 29) /* s */ - | ((imm & 0x7f) << 13) /* imm7b */ - | ((r3 & 0x7f) << 20) - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_a4(int qp, uint64_t opc, int r1, - uint64_t imm, int r3) -{ - return opc - | ((imm & 0x2000) << 23) /* s */ - | ((imm & 0x1f80) << 20) /* imm6d */ - | ((imm & 0x007f) << 13) /* imm7b */ - | ((r3 & 0x7f) << 20) - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_a5(int qp, uint64_t opc, int r1, - uint64_t imm, int r3) -{ - return opc - | ((imm & 0x200000) << 15) /* s */ - | ((imm & 0x1f0000) << 6) /* imm5c */ - | ((imm & 0x00ff80) << 20) /* imm9d */ - | ((imm & 0x00007f) << 13) /* imm7b */ - | ((r3 & 0x03) << 20) - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_a6(int qp, uint64_t opc, int p1, - int p2, int r2, int r3) -{ - return opc - | ((p2 & 0x3f) << 27) - | ((r3 & 0x7f) << 20) - | ((r2 & 0x7f) << 13) - | ((p1 & 0x3f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_b1(int qp, uint64_t opc, uint64_t imm) -{ - return opc - | ((imm & 0x100000) << 16) /* s */ - | ((imm & 0x0fffff) << 13) /* imm20b */ - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_b3(int qp, uint64_t opc, int b1, uint64_t imm) -{ - return opc - | ((imm & 0x100000) << 16) /* s */ - | ((imm & 0x0fffff) << 13) /* imm20b */ - | ((b1 & 0x7) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_b4(int qp, uint64_t opc, int b2) -{ - return opc - | ((b2 & 0x7) << 13) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_b5(int qp, uint64_t opc, int b1, int b2) -{ - return opc - | ((b2 & 0x7) << 13) - | ((b1 & 0x7) << 6) - | (qp & 0x3f); -} - - -static inline uint64_t tcg_opc_b9(int qp, uint64_t opc, uint64_t imm) -{ - return opc - | ((imm & 0x100000) << 16) /* i */ - | ((imm & 0x0fffff) << 6) /* imm20a */ - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_f1(int qp, uint64_t opc, int f1, - int f3, int f4, int f2) -{ - return opc - | ((f4 & 0x7f) << 27) - | ((f3 & 0x7f) << 20) - | ((f2 & 0x7f) << 13) - | ((f1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_f2(int qp, uint64_t opc, int f1, - int f3, int f4, int f2) -{ - return opc - | ((f4 & 0x7f) << 27) - | ((f3 & 0x7f) << 20) - | ((f2 & 0x7f) << 13) - | ((f1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_f6(int qp, uint64_t opc, int f1, - int p2, int f2, int f3) -{ - return opc - | ((p2 & 0x3f) << 27) - | ((f3 & 0x7f) << 20) - | ((f2 & 0x7f) << 13) - | ((f1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_f10(int qp, uint64_t opc, int f1, int f2) -{ - return opc - | ((f2 & 0x7f) << 13) - | ((f1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_f11(int qp, uint64_t opc, int f1, int f2) -{ - return opc - | ((f2 & 0x7f) << 13) - | ((f1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_f16(int qp, uint64_t opc, uint64_t imm) -{ - return opc - | ((imm & 0x100000) << 16) /* i */ - | ((imm & 0x0fffff) << 6) /* imm20a */ - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_i2(int qp, uint64_t opc, int r1, - int r2, int r3) -{ - return opc - | ((r3 & 0x7f) << 20) - | ((r2 & 0x7f) << 13) - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_i3(int qp, uint64_t opc, int r1, - int r2, int mbtype) -{ - return opc - | ((mbtype & 0x0f) << 20) - | ((r2 & 0x7f) << 13) - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_i5(int qp, uint64_t opc, int r1, - int r3, int r2) -{ - return opc - | ((r3 & 0x7f) << 20) - | ((r2 & 0x7f) << 13) - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_i7(int qp, uint64_t opc, int r1, - int r2, int r3) -{ - return opc - | ((r3 & 0x7f) << 20) - | ((r2 & 0x7f) << 13) - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_i10(int qp, uint64_t opc, int r1, - int r2, int r3, uint64_t count) -{ - return opc - | ((count & 0x3f) << 27) - | ((r3 & 0x7f) << 20) - | ((r2 & 0x7f) << 13) - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_i11(int qp, uint64_t opc, int r1, - int r3, uint64_t pos, uint64_t len) -{ - return opc - | ((len & 0x3f) << 27) - | ((r3 & 0x7f) << 20) - | ((pos & 0x3f) << 14) - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_i12(int qp, uint64_t opc, int r1, - int r2, uint64_t pos, uint64_t len) -{ - return opc - | ((len & 0x3f) << 27) - | ((pos & 0x3f) << 20) - | ((r2 & 0x7f) << 13) - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_i14(int qp, uint64_t opc, int r1, uint64_t imm, - int r3, uint64_t pos, uint64_t len) -{ - return opc - | ((imm & 0x01) << 36) - | ((len & 0x3f) << 27) - | ((r3 & 0x7f) << 20) - | ((pos & 0x3f) << 14) - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_i15(int qp, uint64_t opc, int r1, int r2, - int r3, uint64_t pos, uint64_t len) -{ - return opc - | ((pos & 0x3f) << 31) - | ((len & 0x0f) << 27) - | ((r3 & 0x7f) << 20) - | ((r2 & 0x7f) << 13) - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_i18(int qp, uint64_t opc, uint64_t imm) -{ - return opc - | ((imm & 0x100000) << 16) /* i */ - | ((imm & 0x0fffff) << 6) /* imm20a */ - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_i21(int qp, uint64_t opc, int b1, - int r2, uint64_t imm) -{ - return opc - | ((imm & 0x1ff) << 24) - | ((r2 & 0x7f) << 13) - | ((b1 & 0x7) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_i22(int qp, uint64_t opc, int r1, int b2) -{ - return opc - | ((b2 & 0x7) << 13) - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_i26(int qp, uint64_t opc, int ar3, int r2) -{ - return opc - | ((ar3 & 0x7f) << 20) - | ((r2 & 0x7f) << 13) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_i29(int qp, uint64_t opc, int r1, int r3) -{ - return opc - | ((r3 & 0x7f) << 20) - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_l2(uint64_t imm) -{ - return (imm & 0x7fffffffffc00000ull) >> 22; -} - -static inline uint64_t tcg_opc_l3(uint64_t imm) -{ - return (imm & 0x07fffffffff00000ull) >> 18; -} - -#define tcg_opc_l4 tcg_opc_l3 - -static inline uint64_t tcg_opc_m1(int qp, uint64_t opc, int r1, int r3) -{ - return opc - | ((r3 & 0x7f) << 20) - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_m3(int qp, uint64_t opc, int r1, - int r3, uint64_t imm) -{ - return opc - | ((imm & 0x100) << 28) /* s */ - | ((imm & 0x080) << 20) /* i */ - | ((imm & 0x07f) << 13) /* imm7b */ - | ((r3 & 0x7f) << 20) - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_m4(int qp, uint64_t opc, int r2, int r3) -{ - return opc - | ((r3 & 0x7f) << 20) - | ((r2 & 0x7f) << 13) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_m18(int qp, uint64_t opc, int f1, int r2) -{ - return opc - | ((r2 & 0x7f) << 13) - | ((f1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_m19(int qp, uint64_t opc, int r1, int f2) -{ - return opc - | ((f2 & 0x7f) << 13) - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_m34(int qp, uint64_t opc, int r1, - int sof, int sol, int sor) -{ - return opc - | ((sor & 0x0f) << 27) - | ((sol & 0x7f) << 20) - | ((sof & 0x7f) << 13) - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_m48(int qp, uint64_t opc, uint64_t imm) -{ - return opc - | ((imm & 0x100000) << 16) /* i */ - | ((imm & 0x0fffff) << 6) /* imm20a */ - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_x2(int qp, uint64_t opc, - int r1, uint64_t imm) -{ - return opc - | ((imm & 0x8000000000000000ull) >> 27) /* i */ - | (imm & 0x0000000000200000ull) /* ic */ - | ((imm & 0x00000000001f0000ull) << 6) /* imm5c */ - | ((imm & 0x000000000000ff80ull) << 20) /* imm9d */ - | ((imm & 0x000000000000007full) << 13) /* imm7b */ - | ((r1 & 0x7f) << 6) - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_x3(int qp, uint64_t opc, uint64_t imm) -{ - return opc - | ((imm & 0x0800000000000000ull) >> 23) /* i */ - | ((imm & 0x00000000000fffffull) << 13) /* imm20b */ - | (qp & 0x3f); -} - -static inline uint64_t tcg_opc_x4(int qp, uint64_t opc, int b1, uint64_t imm) -{ - return opc - | ((imm & 0x0800000000000000ull) >> 23) /* i */ - | ((imm & 0x00000000000fffffull) << 13) /* imm20b */ - | ((b1 & 0x7) << 6) - | (qp & 0x3f); -} - - -/* - * Relocations - Note that we never encode branches elsewhere than slot 2. - */ - -static void reloc_pcrel21b_slot2(tcg_insn_unit *pc, tcg_insn_unit *target) -{ - uint64_t imm = target - pc; - - pc->hi = (pc->hi & 0xf700000fffffffffull) - | ((imm & 0x100000) << 39) /* s */ - | ((imm & 0x0fffff) << 36); /* imm20b */ -} - -static uint64_t get_reloc_pcrel21b_slot2(tcg_insn_unit *pc) -{ - int64_t high = pc->hi; - - return ((high >> 39) & 0x100000) + /* s */ - ((high >> 36) & 0x0fffff); /* imm20b */ -} - -static void patch_reloc(tcg_insn_unit *code_ptr, int type, - intptr_t value, intptr_t addend) -{ - assert(addend == 0); - assert(type == R_IA64_PCREL21B); - reloc_pcrel21b_slot2(code_ptr, (tcg_insn_unit *)value); -} - -/* - * Constraints - */ - -/* parse target specific constraints */ -static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) -{ - const char *ct_str; - - ct_str = *pct_str; - switch(ct_str[0]) { - case 'r': - ct->ct |= TCG_CT_REG; - tcg_regset_set(ct->u.regs, 0xffffffffffffffffull); - break; - case 'I': - ct->ct |= TCG_CT_CONST_S22; - break; - case 'S': - ct->ct |= TCG_CT_REG; - tcg_regset_set(ct->u.regs, 0xffffffffffffffffull); -#if defined(CONFIG_SOFTMMU) - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R56); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R57); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R58); -#endif - break; - case 'Z': - /* We are cheating a bit here, using the fact that the register - r0 is also the register number 0. Hence there is no need - to check for const_args in each instruction. */ - ct->ct |= TCG_CT_CONST_ZERO; - break; - default: - return -1; - } - ct_str++; - *pct_str = ct_str; - return 0; -} - -/* test if a constant matches the constraint */ -static inline int tcg_target_const_match(tcg_target_long val, TCGType type, - const TCGArgConstraint *arg_ct) -{ - int ct; - ct = arg_ct->ct; - if (ct & TCG_CT_CONST) - return 1; - else if ((ct & TCG_CT_CONST_ZERO) && val == 0) - return 1; - else if ((ct & TCG_CT_CONST_S22) && val == ((int32_t)val << 10) >> 10) - return 1; - else - return 0; -} - -/* - * Code generation - */ - -static tcg_insn_unit *tb_ret_addr; - -static inline void tcg_out_bundle(TCGContext *s, int template, - uint64_t slot0, uint64_t slot1, - uint64_t slot2) -{ - template &= 0x1f; /* 5 bits */ - slot0 &= 0x1ffffffffffull; /* 41 bits */ - slot1 &= 0x1ffffffffffull; /* 41 bits */ - slot2 &= 0x1ffffffffffull; /* 41 bits */ - - *s->code_ptr++ = (tcg_insn_unit){ - (slot1 << 46) | (slot0 << 5) | template, - (slot2 << 23) | (slot1 >> 18) - }; -} - -static inline uint64_t tcg_opc_mov_a(int qp, TCGReg dst, TCGReg src) -{ - return tcg_opc_a4(qp, OPC_ADDS_A4, dst, 0, src); -} - -static inline void tcg_out_mov(TCGContext *s, TCGType type, - TCGReg ret, TCGReg arg) -{ - tcg_out_bundle(s, mmI, - INSN_NOP_M, - INSN_NOP_M, - tcg_opc_mov_a(TCG_REG_P0, ret, arg)); -} - -static inline uint64_t tcg_opc_movi_a(int qp, TCGReg dst, int64_t src) -{ - assert(src == sextract64(src, 0, 22)); - return tcg_opc_a5(qp, OPC_ADDL_A5, dst, src, TCG_REG_R0); -} - -static inline void tcg_out_movi(TCGContext *s, TCGType type, - TCGReg reg, tcg_target_long arg) -{ - tcg_out_bundle(s, mLX, - INSN_NOP_M, - tcg_opc_l2 (arg), - tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, reg, arg)); -} - -static void tcg_out_br(TCGContext *s, TCGLabel *l) -{ - uint64_t imm; - - /* We pay attention here to not modify the branch target by reading - the existing value and using it again. This ensure that caches and - memory are kept coherent during retranslation. */ - if (l->has_value) { - imm = l->u.value_ptr - s->code_ptr; - } else { - imm = get_reloc_pcrel21b_slot2(s->code_ptr); - tcg_out_reloc(s, s->code_ptr, R_IA64_PCREL21B, l, 0); - } - - tcg_out_bundle(s, mmB, - INSN_NOP_M, - INSN_NOP_M, - tcg_opc_b1(TCG_REG_P0, OPC_BR_SPTK_MANY_B1, imm)); -} - -static inline void tcg_out_call(TCGContext *s, tcg_insn_unit *desc) -{ - uintptr_t func = desc->lo, gp = desc->hi, disp; - - /* Look through the function descriptor. */ - tcg_out_bundle(s, mlx, - INSN_NOP_M, - tcg_opc_l2 (gp), - tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, TCG_REG_R1, gp)); - disp = (tcg_insn_unit *)func - s->code_ptr; - tcg_out_bundle(s, mLX, - INSN_NOP_M, - tcg_opc_l4 (disp), - tcg_opc_x4 (TCG_REG_P0, OPC_BRL_CALL_SPTK_MANY_X4, - TCG_REG_B0, disp)); -} - -static void tcg_out_exit_tb(TCGContext *s, tcg_target_long arg) -{ - uint64_t imm, opc1; - - /* At least arg == 0 is a common operation. */ - if (arg == sextract64(arg, 0, 22)) { - opc1 = tcg_opc_movi_a(TCG_REG_P0, TCG_REG_R8, arg); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R8, arg); - opc1 = INSN_NOP_M; - } - - imm = tb_ret_addr - s->code_ptr; - - tcg_out_bundle(s, mLX, - opc1, - tcg_opc_l3 (imm), - tcg_opc_x3 (TCG_REG_P0, OPC_BRL_SPTK_MANY_X3, imm)); -} - -static inline void tcg_out_goto_tb(TCGContext *s, TCGArg arg) -{ - if (s->tb_jmp_offset) { - /* direct jump method */ - tcg_abort(); - } else { - /* indirect jump method */ - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, - (tcg_target_long)(s->tb_next + arg)); - tcg_out_bundle(s, MmI, - tcg_opc_m1 (TCG_REG_P0, OPC_LD8_M1, - TCG_REG_R2, TCG_REG_R2), - INSN_NOP_M, - tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, TCG_REG_B6, - TCG_REG_R2, 0)); - tcg_out_bundle(s, mmB, - INSN_NOP_M, - INSN_NOP_M, - tcg_opc_b4 (TCG_REG_P0, OPC_BR_SPTK_MANY_B4, - TCG_REG_B6)); - } - s->tb_next_offset[arg] = tcg_current_code_size(s); -} - -static inline void tcg_out_jmp(TCGContext *s, TCGArg addr) -{ - tcg_out_bundle(s, mmI, - INSN_NOP_M, - INSN_NOP_M, - tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, TCG_REG_B6, addr, 0)); - tcg_out_bundle(s, mmB, - INSN_NOP_M, - INSN_NOP_M, - tcg_opc_b4(TCG_REG_P0, OPC_BR_SPTK_MANY_B4, TCG_REG_B6)); -} - -static inline void tcg_out_ld_rel(TCGContext *s, uint64_t opc_m4, TCGArg arg, - TCGArg arg1, tcg_target_long arg2) -{ - if (arg2 == ((int16_t)arg2 >> 2) << 2) { - tcg_out_bundle(s, MmI, - tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, - TCG_REG_R2, arg2, arg1), - tcg_opc_m1 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), - INSN_NOP_I); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, arg2); - tcg_out_bundle(s, MmI, - tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, - TCG_REG_R2, TCG_REG_R2, arg1), - tcg_opc_m1 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), - INSN_NOP_I); - } -} - -static inline void tcg_out_st_rel(TCGContext *s, uint64_t opc_m4, TCGArg arg, - TCGArg arg1, tcg_target_long arg2) -{ - if (arg2 == ((int16_t)arg2 >> 2) << 2) { - tcg_out_bundle(s, MmI, - tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, - TCG_REG_R2, arg2, arg1), - tcg_opc_m4 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), - INSN_NOP_I); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, arg2); - tcg_out_bundle(s, MmI, - tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, - TCG_REG_R2, TCG_REG_R2, arg1), - tcg_opc_m4 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), - INSN_NOP_I); - } -} - -static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg arg, - TCGReg arg1, intptr_t arg2) -{ - if (type == TCG_TYPE_I32) { - tcg_out_ld_rel(s, OPC_LD4_M1, arg, arg1, arg2); - } else { - tcg_out_ld_rel(s, OPC_LD8_M1, arg, arg1, arg2); - } -} - -static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, - TCGReg arg1, intptr_t arg2) -{ - if (type == TCG_TYPE_I32) { - tcg_out_st_rel(s, OPC_ST4_M4, arg, arg1, arg2); - } else { - tcg_out_st_rel(s, OPC_ST8_M4, arg, arg1, arg2); - } -} - -static inline void tcg_out_alu(TCGContext *s, uint64_t opc_a1, uint64_t opc_a3, - TCGReg ret, TCGArg arg1, int const_arg1, - TCGArg arg2, int const_arg2) -{ - uint64_t opc1 = 0, opc2 = 0, opc3 = 0; - - if (const_arg2 && arg2 != 0) { - opc2 = tcg_opc_movi_a(TCG_REG_P0, TCG_REG_R3, arg2); - arg2 = TCG_REG_R3; - } - if (const_arg1 && arg1 != 0) { - if (opc_a3 && arg1 == (int8_t)arg1) { - opc3 = tcg_opc_a3(TCG_REG_P0, opc_a3, ret, arg1, arg2); - } else { - opc1 = tcg_opc_movi_a(TCG_REG_P0, TCG_REG_R2, arg1); - arg1 = TCG_REG_R2; - } - } - if (opc3 == 0) { - opc3 = tcg_opc_a1(TCG_REG_P0, opc_a1, ret, arg1, arg2); - } - - tcg_out_bundle(s, (opc1 || opc2 ? mII : miI), - opc1 ? opc1 : INSN_NOP_M, - opc2 ? opc2 : INSN_NOP_I, - opc3); -} - -static inline void tcg_out_add(TCGContext *s, TCGReg ret, TCGReg arg1, - TCGArg arg2, int const_arg2) -{ - if (const_arg2 && arg2 == sextract64(arg2, 0, 14)) { - tcg_out_bundle(s, mmI, - INSN_NOP_M, - INSN_NOP_M, - tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, ret, arg2, arg1)); - } else { - tcg_out_alu(s, OPC_ADD_A1, 0, ret, arg1, 0, arg2, const_arg2); - } -} - -static inline void tcg_out_sub(TCGContext *s, TCGReg ret, TCGArg arg1, - int const_arg1, TCGArg arg2, int const_arg2) -{ - if (!const_arg1 && const_arg2 && -arg2 == sextract64(-arg2, 0, 14)) { - tcg_out_bundle(s, mmI, - INSN_NOP_M, - INSN_NOP_M, - tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, ret, -arg2, arg1)); - } else { - tcg_out_alu(s, OPC_SUB_A1, OPC_SUB_A3, ret, - arg1, const_arg1, arg2, const_arg2); - } -} - -static inline void tcg_out_eqv(TCGContext *s, TCGArg ret, - TCGArg arg1, int const_arg1, - TCGArg arg2, int const_arg2) -{ - tcg_out_bundle(s, mII, - INSN_NOP_M, - tcg_opc_a1 (TCG_REG_P0, OPC_XOR_A1, ret, arg1, arg2), - tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, ret, -1, ret)); -} - -static inline void tcg_out_nand(TCGContext *s, TCGArg ret, - TCGArg arg1, int const_arg1, - TCGArg arg2, int const_arg2) -{ - tcg_out_bundle(s, mII, - INSN_NOP_M, - tcg_opc_a1 (TCG_REG_P0, OPC_AND_A1, ret, arg1, arg2), - tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, ret, -1, ret)); -} - -static inline void tcg_out_nor(TCGContext *s, TCGArg ret, - TCGArg arg1, int const_arg1, - TCGArg arg2, int const_arg2) -{ - tcg_out_bundle(s, mII, - INSN_NOP_M, - tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, arg1, arg2), - tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, ret, -1, ret)); -} - -static inline void tcg_out_orc(TCGContext *s, TCGArg ret, - TCGArg arg1, int const_arg1, - TCGArg arg2, int const_arg2) -{ - tcg_out_bundle(s, mII, - INSN_NOP_M, - tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, TCG_REG_R2, -1, arg2), - tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, arg1, TCG_REG_R2)); -} - -static inline void tcg_out_mul(TCGContext *s, TCGArg ret, - TCGArg arg1, TCGArg arg2) -{ - tcg_out_bundle(s, mmI, - tcg_opc_m18(TCG_REG_P0, OPC_SETF_SIG_M18, TCG_REG_F6, arg1), - tcg_opc_m18(TCG_REG_P0, OPC_SETF_SIG_M18, TCG_REG_F7, arg2), - INSN_NOP_I); - tcg_out_bundle(s, mmF, - INSN_NOP_M, - INSN_NOP_M, - tcg_opc_f2 (TCG_REG_P0, OPC_XMA_L_F2, TCG_REG_F6, TCG_REG_F6, - TCG_REG_F7, TCG_REG_F0)); - tcg_out_bundle(s, miI, - tcg_opc_m19(TCG_REG_P0, OPC_GETF_SIG_M19, ret, TCG_REG_F6), - INSN_NOP_I, - INSN_NOP_I); -} - -static inline void tcg_out_sar_i32(TCGContext *s, TCGArg ret, TCGArg arg1, - TCGArg arg2, int const_arg2) -{ - if (const_arg2) { - tcg_out_bundle(s, miI, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_i11(TCG_REG_P0, OPC_EXTR_I11, - ret, arg1, arg2, 31 - arg2)); - } else { - tcg_out_bundle(s, mII, - tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, - TCG_REG_R3, 0x1f, arg2), - tcg_opc_i29(TCG_REG_P0, OPC_SXT4_I29, TCG_REG_R2, arg1), - tcg_opc_i5 (TCG_REG_P0, OPC_SHR_I5, ret, - TCG_REG_R2, TCG_REG_R3)); - } -} - -static inline void tcg_out_sar_i64(TCGContext *s, TCGArg ret, TCGArg arg1, - TCGArg arg2, int const_arg2) -{ - if (const_arg2) { - tcg_out_bundle(s, miI, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_i11(TCG_REG_P0, OPC_EXTR_I11, - ret, arg1, arg2, 63 - arg2)); - } else { - tcg_out_bundle(s, miI, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_i5 (TCG_REG_P0, OPC_SHR_I5, ret, arg1, arg2)); - } -} - -static inline void tcg_out_shl_i32(TCGContext *s, TCGArg ret, TCGArg arg1, - TCGArg arg2, int const_arg2) -{ - if (const_arg2) { - tcg_out_bundle(s, miI, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, - arg1, 63 - arg2, 31 - arg2)); - } else { - tcg_out_bundle(s, mII, - INSN_NOP_M, - tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R2, - 0x1f, arg2), - tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, ret, - arg1, TCG_REG_R2)); - } -} - -static inline void tcg_out_shl_i64(TCGContext *s, TCGArg ret, TCGArg arg1, - TCGArg arg2, int const_arg2) -{ - if (const_arg2) { - tcg_out_bundle(s, miI, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, - arg1, 63 - arg2, 63 - arg2)); - } else { - tcg_out_bundle(s, miI, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, ret, - arg1, arg2)); - } -} - -static inline void tcg_out_shr_i32(TCGContext *s, TCGArg ret, TCGArg arg1, - TCGArg arg2, int const_arg2) -{ - if (const_arg2) { - tcg_out_bundle(s, miI, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, - arg1, arg2, 31 - arg2)); - } else { - tcg_out_bundle(s, mII, - tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R3, - 0x1f, arg2), - tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, TCG_REG_R2, arg1), - tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret, - TCG_REG_R2, TCG_REG_R3)); - } -} - -static inline void tcg_out_shr_i64(TCGContext *s, TCGArg ret, TCGArg arg1, - TCGArg arg2, int const_arg2) -{ - if (const_arg2) { - tcg_out_bundle(s, miI, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, - arg1, arg2, 63 - arg2)); - } else { - tcg_out_bundle(s, miI, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret, - arg1, arg2)); - } -} - -static inline void tcg_out_rotl_i32(TCGContext *s, TCGArg ret, TCGArg arg1, - TCGArg arg2, int const_arg2) -{ - if (const_arg2) { - tcg_out_bundle(s, mII, - INSN_NOP_M, - tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2, - TCG_REG_R2, arg1, arg1), - tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, - TCG_REG_R2, 32 - arg2, 31)); - } else { - tcg_out_bundle(s, miI, - INSN_NOP_M, - tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2, - TCG_REG_R2, arg1, arg1), - tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R3, - 0x1f, arg2)); - tcg_out_bundle(s, mII, - INSN_NOP_M, - tcg_opc_a3 (TCG_REG_P0, OPC_SUB_A3, TCG_REG_R3, - 0x20, TCG_REG_R3), - tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret, - TCG_REG_R2, TCG_REG_R3)); - } -} - -static inline void tcg_out_rotl_i64(TCGContext *s, TCGArg ret, TCGArg arg1, - TCGArg arg2, int const_arg2) -{ - if (const_arg2) { - tcg_out_bundle(s, miI, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_i10(TCG_REG_P0, OPC_SHRP_I10, ret, arg1, - arg1, 0x40 - arg2)); - } else { - tcg_out_bundle(s, mII, - tcg_opc_a3 (TCG_REG_P0, OPC_SUB_A3, TCG_REG_R2, - 0x40, arg2), - tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, TCG_REG_R3, - arg1, arg2), - tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, TCG_REG_R2, - arg1, TCG_REG_R2)); - tcg_out_bundle(s, miI, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, - TCG_REG_R2, TCG_REG_R3)); - } -} - -static inline void tcg_out_rotr_i32(TCGContext *s, TCGArg ret, TCGArg arg1, - TCGArg arg2, int const_arg2) -{ - if (const_arg2) { - tcg_out_bundle(s, mII, - INSN_NOP_M, - tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2, - TCG_REG_R2, arg1, arg1), - tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, - TCG_REG_R2, arg2, 31)); - } else { - tcg_out_bundle(s, mII, - tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R3, - 0x1f, arg2), - tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2, - TCG_REG_R2, arg1, arg1), - tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret, - TCG_REG_R2, TCG_REG_R3)); - } -} - -static inline void tcg_out_rotr_i64(TCGContext *s, TCGArg ret, TCGArg arg1, - TCGArg arg2, int const_arg2) -{ - if (const_arg2) { - tcg_out_bundle(s, miI, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_i10(TCG_REG_P0, OPC_SHRP_I10, ret, arg1, - arg1, arg2)); - } else { - tcg_out_bundle(s, mII, - tcg_opc_a3 (TCG_REG_P0, OPC_SUB_A3, TCG_REG_R2, - 0x40, arg2), - tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, TCG_REG_R3, - arg1, arg2), - tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, TCG_REG_R2, - arg1, TCG_REG_R2)); - tcg_out_bundle(s, miI, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, - TCG_REG_R2, TCG_REG_R3)); - } -} - -static const uint64_t opc_ext_i29[8] = { - OPC_ZXT1_I29, OPC_ZXT2_I29, OPC_ZXT4_I29, 0, - OPC_SXT1_I29, OPC_SXT2_I29, OPC_SXT4_I29, 0 -}; - -static inline uint64_t tcg_opc_ext_i(int qp, TCGMemOp opc, TCGReg d, TCGReg s) -{ - if ((opc & MO_SIZE) == MO_64) { - return tcg_opc_mov_a(qp, d, s); - } else { - return tcg_opc_i29(qp, opc_ext_i29[opc & MO_SSIZE], d, s); - } -} - -static inline void tcg_out_ext(TCGContext *s, uint64_t opc_i29, - TCGArg ret, TCGArg arg) -{ - tcg_out_bundle(s, miI, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_i29(TCG_REG_P0, opc_i29, ret, arg)); -} - -static inline uint64_t tcg_opc_bswap64_i(int qp, TCGReg d, TCGReg s) -{ - return tcg_opc_i3(qp, OPC_MUX1_I3, d, s, 0xb); -} - -static inline void tcg_out_bswap16(TCGContext *s, TCGArg ret, TCGArg arg) -{ - tcg_out_bundle(s, mII, - INSN_NOP_M, - tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, arg, 15, 15), - tcg_opc_bswap64_i(TCG_REG_P0, ret, ret)); -} - -static inline void tcg_out_bswap32(TCGContext *s, TCGArg ret, TCGArg arg) -{ - tcg_out_bundle(s, mII, - INSN_NOP_M, - tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, arg, 31, 31), - tcg_opc_bswap64_i(TCG_REG_P0, ret, ret)); -} - -static inline void tcg_out_bswap64(TCGContext *s, TCGArg ret, TCGArg arg) -{ - tcg_out_bundle(s, miI, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_bswap64_i(TCG_REG_P0, ret, arg)); -} - -static inline void tcg_out_deposit(TCGContext *s, TCGArg ret, TCGArg a1, - TCGArg a2, int const_a2, int pos, int len) -{ - uint64_t i1 = 0, i2 = 0; - int cpos = 63 - pos, lm1 = len - 1; - - if (const_a2) { - /* Truncate the value of a constant a2 to the width of the field. */ - int mask = (1u << len) - 1; - a2 &= mask; - - if (a2 == 0 || a2 == mask) { - /* 1-bit signed constant inserted into register. */ - i2 = tcg_opc_i14(TCG_REG_P0, OPC_DEP_I14, ret, a2, a1, cpos, lm1); - } else { - /* Otherwise, load any constant into a temporary. Do this into - the first I slot to help out with cross-unit delays. */ - i1 = tcg_opc_movi_a(TCG_REG_P0, TCG_REG_R2, a2); - a2 = TCG_REG_R2; - } - } - if (i2 == 0) { - i2 = tcg_opc_i15(TCG_REG_P0, OPC_DEP_I15, ret, a2, a1, cpos, lm1); - } - tcg_out_bundle(s, (i1 ? mII : miI), - INSN_NOP_M, - i1 ? i1 : INSN_NOP_I, - i2); -} - -static inline uint64_t tcg_opc_cmp_a(int qp, TCGCond cond, TCGArg arg1, - TCGArg arg2, int cmp4) -{ - uint64_t opc_eq_a6, opc_lt_a6, opc_ltu_a6; - - if (cmp4) { - opc_eq_a6 = OPC_CMP4_EQ_A6; - opc_lt_a6 = OPC_CMP4_LT_A6; - opc_ltu_a6 = OPC_CMP4_LTU_A6; - } else { - opc_eq_a6 = OPC_CMP_EQ_A6; - opc_lt_a6 = OPC_CMP_LT_A6; - opc_ltu_a6 = OPC_CMP_LTU_A6; - } - - switch (cond) { - case TCG_COND_EQ: - return tcg_opc_a6 (qp, opc_eq_a6, TCG_REG_P6, TCG_REG_P7, arg1, arg2); - case TCG_COND_NE: - return tcg_opc_a6 (qp, opc_eq_a6, TCG_REG_P7, TCG_REG_P6, arg1, arg2); - case TCG_COND_LT: - return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P6, TCG_REG_P7, arg1, arg2); - case TCG_COND_LTU: - return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P6, TCG_REG_P7, arg1, arg2); - case TCG_COND_GE: - return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P7, TCG_REG_P6, arg1, arg2); - case TCG_COND_GEU: - return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P7, TCG_REG_P6, arg1, arg2); - case TCG_COND_LE: - return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P7, TCG_REG_P6, arg2, arg1); - case TCG_COND_LEU: - return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P7, TCG_REG_P6, arg2, arg1); - case TCG_COND_GT: - return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P6, TCG_REG_P7, arg2, arg1); - case TCG_COND_GTU: - return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P6, TCG_REG_P7, arg2, arg1); - default: - tcg_abort(); - break; - } -} - -static inline void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, - TCGReg arg2, TCGLabel *l, int cmp4) -{ - uint64_t imm; - - /* We pay attention here to not modify the branch target by reading - the existing value and using it again. This ensure that caches and - memory are kept coherent during retranslation. */ - if (l->has_value) { - imm = l->u.value_ptr - s->code_ptr; - } else { - imm = get_reloc_pcrel21b_slot2(s->code_ptr); - tcg_out_reloc(s, s->code_ptr, R_IA64_PCREL21B, l, 0); - } - - tcg_out_bundle(s, miB, - INSN_NOP_M, - tcg_opc_cmp_a(TCG_REG_P0, cond, arg1, arg2, cmp4), - tcg_opc_b1(TCG_REG_P6, OPC_BR_DPTK_FEW_B1, imm)); -} - -static inline void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGArg ret, - TCGArg arg1, TCGArg arg2, int cmp4) -{ - tcg_out_bundle(s, MmI, - tcg_opc_cmp_a(TCG_REG_P0, cond, arg1, arg2, cmp4), - tcg_opc_movi_a(TCG_REG_P6, ret, 1), - tcg_opc_movi_a(TCG_REG_P7, ret, 0)); -} - -static inline void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGArg ret, - TCGArg c1, TCGArg c2, - TCGArg v1, int const_v1, - TCGArg v2, int const_v2, int cmp4) -{ - uint64_t opc1, opc2; - - if (const_v1) { - opc1 = tcg_opc_movi_a(TCG_REG_P6, ret, v1); - } else if (ret == v1) { - opc1 = INSN_NOP_M; - } else { - opc1 = tcg_opc_mov_a(TCG_REG_P6, ret, v1); - } - if (const_v2) { - opc2 = tcg_opc_movi_a(TCG_REG_P7, ret, v2); - } else if (ret == v2) { - opc2 = INSN_NOP_I; - } else { - opc2 = tcg_opc_mov_a(TCG_REG_P7, ret, v2); - } - - tcg_out_bundle(s, MmI, - tcg_opc_cmp_a(TCG_REG_P0, cond, c1, c2, cmp4), - opc1, - opc2); -} - -#if defined(CONFIG_SOFTMMU) -/* We're expecting to use an signed 22-bit immediate add. */ -QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table[NB_MMU_MODES - 1][1]) - > 0x1fffff) - -/* Load and compare a TLB entry, and return the result in (p6, p7). - R2 is loaded with the addend TLB entry. - R57 is loaded with the address, zero extented on 32-bit targets. - R1, R3 are clobbered, leaving R56 free for... - BSWAP_1, BSWAP_2 and I-slot insns for swapping data for store. */ -static inline void tcg_out_qemu_tlb(TCGContext *s, TCGReg addr_reg, - TCGMemOp s_bits, int off_rw, int off_add, - uint64_t bswap1, uint64_t bswap2) -{ - /* - .mii - mov r2 = off_rw - extr.u r3 = addr_reg, ... # extract tlb page - zxt4 r57 = addr_reg # or mov for 64-bit guest - ;; - .mii - addl r2 = r2, areg0 - shl r3 = r3, cteb # via dep.z - dep r1 = 0, r57, ... # zero page ofs, keep align - ;; - .mmi - add r2 = r2, r3 - ;; - ld4 r3 = [r2], off_add-off_rw # or ld8 for 64-bit guest - nop - ;; - .mmi - nop - cmp.eq p6, p7 = r3, r58 - nop - ;; - */ - tcg_out_bundle(s, miI, - tcg_opc_movi_a(TCG_REG_P0, TCG_REG_R2, off_rw), - tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, TCG_REG_R3, - addr_reg, TARGET_PAGE_BITS, CPU_TLB_BITS - 1), - tcg_opc_ext_i(TCG_REG_P0, - TARGET_LONG_BITS == 32 ? MO_UL : MO_Q, - TCG_REG_R57, addr_reg)); - tcg_out_bundle(s, miI, - tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, - TCG_REG_R2, TCG_AREG0), - tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, TCG_REG_R3, - TCG_REG_R3, 63 - CPU_TLB_ENTRY_BITS, - 63 - CPU_TLB_ENTRY_BITS), - tcg_opc_i14(TCG_REG_P0, OPC_DEP_I14, TCG_REG_R1, 0, - TCG_REG_R57, 63 - s_bits, - TARGET_PAGE_BITS - s_bits - 1)); - tcg_out_bundle(s, MmI, - tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, - TCG_REG_R2, TCG_REG_R2, TCG_REG_R3), - tcg_opc_m3 (TCG_REG_P0, - (TARGET_LONG_BITS == 32 - ? OPC_LD4_M3 : OPC_LD8_M3), TCG_REG_R3, - TCG_REG_R2, off_add - off_rw), - bswap1); - tcg_out_bundle(s, mmI, - tcg_opc_m1 (TCG_REG_P0, OPC_LD8_M1, TCG_REG_R2, TCG_REG_R2), - tcg_opc_a6 (TCG_REG_P0, OPC_CMP_EQ_A6, TCG_REG_P6, - TCG_REG_P7, TCG_REG_R1, TCG_REG_R3), - bswap2); -} - -typedef struct TCGLabelQemuLdst { - bool is_ld; - TCGMemOp size; - tcg_insn_unit *label_ptr; /* label pointers to be updated */ - struct TCGLabelQemuLdst *next; -} TCGLabelQemuLdst; - -typedef struct TCGBackendData { - TCGLabelQemuLdst *labels; -} TCGBackendData; - -static inline void tcg_out_tb_init(TCGContext *s) -{ - s->be->labels = NULL; -} - -static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOp opc, - tcg_insn_unit *label_ptr) -{ - TCGBackendData *be = s->be; - TCGLabelQemuLdst *l = tcg_malloc(sizeof(*l)); - - l->is_ld = is_ld; - l->size = opc & MO_SIZE; - l->label_ptr = label_ptr; - l->next = be->labels; - be->labels = l; -} - -static void tcg_out_tb_finalize(TCGContext *s) -{ - static const void * const helpers[8] = { - helper_ret_stb_mmu, - helper_le_stw_mmu, - helper_le_stl_mmu, - helper_le_stq_mmu, - helper_ret_ldub_mmu, - helper_le_lduw_mmu, - helper_le_ldul_mmu, - helper_le_ldq_mmu, - }; - tcg_insn_unit *thunks[8] = { }; - TCGLabelQemuLdst *l; - - for (l = s->be->labels; l != NULL; l = l->next) { - long x = l->is_ld * 4 + l->size; - tcg_insn_unit *dest = thunks[x]; - - /* The out-of-line thunks are all the same; load the return address - from B0, load the GP, and branch to the code. Note that we are - always post-call, so the register window has rolled, so we're - using incomming parameter register numbers, not outgoing. */ - if (dest == NULL) { - uintptr_t *desc = (uintptr_t *)helpers[x]; - uintptr_t func = desc[0], gp = desc[1], disp; - - thunks[x] = dest = s->code_ptr; - - tcg_out_bundle(s, mlx, - INSN_NOP_M, - tcg_opc_l2 (gp), - tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, - TCG_REG_R1, gp)); - tcg_out_bundle(s, mii, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_i22(TCG_REG_P0, OPC_MOV_I22, - l->is_ld ? TCG_REG_R35 : TCG_REG_R36, - TCG_REG_B0)); - disp = (tcg_insn_unit *)func - s->code_ptr; - tcg_out_bundle(s, mLX, - INSN_NOP_M, - tcg_opc_l3 (disp), - tcg_opc_x3 (TCG_REG_P0, OPC_BRL_SPTK_MANY_X3, disp)); - } - - reloc_pcrel21b_slot2(l->label_ptr, dest); - } -} - -static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args) -{ - static const uint64_t opc_ld_m1[4] = { - OPC_LD1_M1, OPC_LD2_M1, OPC_LD4_M1, OPC_LD8_M1 - }; - int addr_reg, data_reg, mem_index; - TCGMemOpIdx oi; - TCGMemOp opc, s_bits; - uint64_t fin1, fin2; - tcg_insn_unit *label_ptr; - - data_reg = args[0]; - addr_reg = args[1]; - oi = args[2]; - opc = get_memop(oi); - mem_index = get_mmuidx(oi); - s_bits = opc & MO_SIZE; - - /* Read the TLB entry */ - tcg_out_qemu_tlb(s, addr_reg, s_bits, - offsetof(CPUArchState, tlb_table[mem_index][0].addr_read), - offsetof(CPUArchState, tlb_table[mem_index][0].addend), - INSN_NOP_I, INSN_NOP_I); - - /* P6 is the fast path, and P7 the slow path */ - - fin2 = 0; - if (opc & MO_BSWAP) { - fin1 = tcg_opc_bswap64_i(TCG_REG_P0, data_reg, TCG_REG_R8); - if (s_bits < MO_64) { - int shift = 64 - (8 << s_bits); - fin2 = (opc & MO_SIGN ? OPC_EXTR_I11 : OPC_EXTR_U_I11); - fin2 = tcg_opc_i11(TCG_REG_P0, fin2, - data_reg, data_reg, shift, 63 - shift); - } - } else { - fin1 = tcg_opc_ext_i(TCG_REG_P0, opc, data_reg, TCG_REG_R8); - } - - tcg_out_bundle(s, mmI, - tcg_opc_mov_a(TCG_REG_P7, TCG_REG_R56, TCG_AREG0), - tcg_opc_a1 (TCG_REG_P6, OPC_ADD_A1, TCG_REG_R2, - TCG_REG_R2, TCG_REG_R57), - tcg_opc_movi_a(TCG_REG_P7, TCG_REG_R58, oi)); - label_ptr = s->code_ptr; - tcg_out_bundle(s, miB, - tcg_opc_m1 (TCG_REG_P6, opc_ld_m1[s_bits], - TCG_REG_R8, TCG_REG_R2), - INSN_NOP_I, - tcg_opc_b3 (TCG_REG_P7, OPC_BR_CALL_SPNT_FEW_B3, TCG_REG_B0, - get_reloc_pcrel21b_slot2(label_ptr))); - - add_qemu_ldst_label(s, 1, opc, label_ptr); - - /* Note that we always use LE helper functions, so the bswap insns - here for the fast path also apply to the slow path. */ - tcg_out_bundle(s, (fin2 ? mII : miI), - INSN_NOP_M, - fin1, - fin2 ? fin2 : INSN_NOP_I); -} - -static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args) -{ - static const uint64_t opc_st_m4[4] = { - OPC_ST1_M4, OPC_ST2_M4, OPC_ST4_M4, OPC_ST8_M4 - }; - TCGReg addr_reg, data_reg; - int mem_index; - uint64_t pre1, pre2; - TCGMemOpIdx oi; - TCGMemOp opc, s_bits; - tcg_insn_unit *label_ptr; - - data_reg = args[0]; - addr_reg = args[1]; - oi = args[2]; - opc = get_memop(oi); - mem_index = get_mmuidx(oi); - s_bits = opc & MO_SIZE; - - /* Note that we always use LE helper functions, so the bswap insns - that are here for the fast path also apply to the slow path, - and move the data into the argument register. */ - pre2 = INSN_NOP_I; - if (opc & MO_BSWAP) { - pre1 = tcg_opc_bswap64_i(TCG_REG_P0, TCG_REG_R58, data_reg); - if (s_bits < MO_64) { - int shift = 64 - (8 << s_bits); - pre2 = tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, - TCG_REG_R58, TCG_REG_R58, shift, 63 - shift); - } - } else { - /* Just move the data into place for the slow path. */ - pre1 = tcg_opc_ext_i(TCG_REG_P0, opc, TCG_REG_R58, data_reg); - } - - tcg_out_qemu_tlb(s, addr_reg, s_bits, - offsetof(CPUArchState, tlb_table[mem_index][0].addr_write), - offsetof(CPUArchState, tlb_table[mem_index][0].addend), - pre1, pre2); - - /* P6 is the fast path, and P7 the slow path */ - tcg_out_bundle(s, mmI, - tcg_opc_mov_a(TCG_REG_P7, TCG_REG_R56, TCG_AREG0), - tcg_opc_a1 (TCG_REG_P6, OPC_ADD_A1, TCG_REG_R2, - TCG_REG_R2, TCG_REG_R57), - tcg_opc_movi_a(TCG_REG_P7, TCG_REG_R59, oi)); - label_ptr = s->code_ptr; - tcg_out_bundle(s, miB, - tcg_opc_m4 (TCG_REG_P6, opc_st_m4[s_bits], - TCG_REG_R58, TCG_REG_R2), - INSN_NOP_I, - tcg_opc_b3 (TCG_REG_P7, OPC_BR_CALL_SPNT_FEW_B3, TCG_REG_B0, - get_reloc_pcrel21b_slot2(label_ptr))); - - add_qemu_ldst_label(s, 0, opc, label_ptr); -} - -#else /* !CONFIG_SOFTMMU */ -# include "tcg-be-null.h" - -static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args) -{ - static uint64_t const opc_ld_m1[4] = { - OPC_LD1_M1, OPC_LD2_M1, OPC_LD4_M1, OPC_LD8_M1 - }; - int addr_reg, data_reg; - TCGMemOp opc, s_bits, bswap; - - data_reg = args[0]; - addr_reg = args[1]; - opc = args[2]; - s_bits = opc & MO_SIZE; - bswap = opc & MO_BSWAP; - -#if TARGET_LONG_BITS == 32 - if (GUEST_BASE != 0) { - tcg_out_bundle(s, mII, - INSN_NOP_M, - tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, - TCG_REG_R3, addr_reg), - tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, - TCG_GUEST_BASE_REG, TCG_REG_R3)); - } else { - tcg_out_bundle(s, miI, - INSN_NOP_M, - tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, - TCG_REG_R2, addr_reg), - INSN_NOP_I); - } - - if (!bswap) { - if (!(opc & MO_SIGN)) { - tcg_out_bundle(s, miI, - tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], - data_reg, TCG_REG_R2), - INSN_NOP_I, - INSN_NOP_I); - } else { - tcg_out_bundle(s, mII, - tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], - data_reg, TCG_REG_R2), - INSN_NOP_I, - tcg_opc_ext_i(TCG_REG_P0, opc, data_reg, data_reg)); - } - } else if (s_bits == MO_64) { - tcg_out_bundle(s, mII, - tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], - data_reg, TCG_REG_R2), - INSN_NOP_I, - tcg_opc_bswap64_i(TCG_REG_P0, data_reg, data_reg)); - } else { - if (s_bits == MO_16) { - tcg_out_bundle(s, mII, - tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], - data_reg, TCG_REG_R2), - INSN_NOP_I, - tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, - data_reg, data_reg, 15, 15)); - } else { - tcg_out_bundle(s, mII, - tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], - data_reg, TCG_REG_R2), - INSN_NOP_I, - tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, - data_reg, data_reg, 31, 31)); - } - if (!(opc & MO_SIGN)) { - tcg_out_bundle(s, miI, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_bswap64_i(TCG_REG_P0, data_reg, data_reg)); - } else { - tcg_out_bundle(s, mII, - INSN_NOP_M, - tcg_opc_bswap64_i(TCG_REG_P0, data_reg, data_reg), - tcg_opc_ext_i(TCG_REG_P0, opc, data_reg, data_reg)); - } - } -#else - if (GUEST_BASE != 0) { - tcg_out_bundle(s, MmI, - tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, - TCG_GUEST_BASE_REG, addr_reg), - tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], - data_reg, TCG_REG_R2), - INSN_NOP_I); - } else { - tcg_out_bundle(s, mmI, - INSN_NOP_M, - tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], - data_reg, addr_reg), - INSN_NOP_I); - } - - if (bswap && s_bits == MO_16) { - tcg_out_bundle(s, mII, - INSN_NOP_M, - tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, - data_reg, data_reg, 15, 15), - tcg_opc_bswap64_i(TCG_REG_P0, data_reg, data_reg)); - } else if (bswap && s_bits == MO_32) { - tcg_out_bundle(s, mII, - INSN_NOP_M, - tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, - data_reg, data_reg, 31, 31), - tcg_opc_bswap64_i(TCG_REG_P0, data_reg, data_reg)); - } else if (bswap && s_bits == MO_64) { - tcg_out_bundle(s, miI, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_bswap64_i(TCG_REG_P0, data_reg, data_reg)); - } - if (opc & MO_SIGN) { - tcg_out_bundle(s, miI, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_ext_i(TCG_REG_P0, opc, data_reg, data_reg)); - } -#endif -} - -static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args) -{ - static uint64_t const opc_st_m4[4] = { - OPC_ST1_M4, OPC_ST2_M4, OPC_ST4_M4, OPC_ST8_M4 - }; - int addr_reg, data_reg; -#if TARGET_LONG_BITS == 64 - uint64_t add_guest_base; -#endif - TCGMemOp opc, s_bits, bswap; - - data_reg = args[0]; - addr_reg = args[1]; - opc = args[2]; - s_bits = opc & MO_SIZE; - bswap = opc & MO_BSWAP; - -#if TARGET_LONG_BITS == 32 - if (GUEST_BASE != 0) { - tcg_out_bundle(s, mII, - INSN_NOP_M, - tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, - TCG_REG_R3, addr_reg), - tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, - TCG_GUEST_BASE_REG, TCG_REG_R3)); - } else { - tcg_out_bundle(s, miI, - INSN_NOP_M, - tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, - TCG_REG_R2, addr_reg), - INSN_NOP_I); - } - - if (bswap) { - if (s_bits == MO_16) { - tcg_out_bundle(s, mII, - INSN_NOP_M, - tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, - TCG_REG_R3, data_reg, 15, 15), - tcg_opc_bswap64_i(TCG_REG_P0, - TCG_REG_R3, TCG_REG_R3)); - data_reg = TCG_REG_R3; - } else if (s_bits == MO_32) { - tcg_out_bundle(s, mII, - INSN_NOP_M, - tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, - TCG_REG_R3, data_reg, 31, 31), - tcg_opc_bswap64_i(TCG_REG_P0, - TCG_REG_R3, TCG_REG_R3)); - data_reg = TCG_REG_R3; - } else if (s_bits == MO_64) { - tcg_out_bundle(s, miI, - INSN_NOP_M, - INSN_NOP_I, - tcg_opc_bswap64_i(TCG_REG_P0, TCG_REG_R3, data_reg)); - data_reg = TCG_REG_R3; - } - } - tcg_out_bundle(s, mmI, - tcg_opc_m4 (TCG_REG_P0, opc_st_m4[s_bits], - data_reg, TCG_REG_R2), - INSN_NOP_M, - INSN_NOP_I); -#else - if (GUEST_BASE != 0) { - add_guest_base = tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, - TCG_GUEST_BASE_REG, addr_reg); - addr_reg = TCG_REG_R2; - } else { - add_guest_base = INSN_NOP_M; - } - - if (!bswap) { - tcg_out_bundle(s, (GUEST_BASE ? MmI : mmI), - add_guest_base, - tcg_opc_m4 (TCG_REG_P0, opc_st_m4[s_bits], - data_reg, addr_reg), - INSN_NOP_I); - } else { - if (s_bits == MO_16) { - tcg_out_bundle(s, mII, - add_guest_base, - tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, - TCG_REG_R3, data_reg, 15, 15), - tcg_opc_bswap64_i(TCG_REG_P0, - TCG_REG_R3, TCG_REG_R3)); - data_reg = TCG_REG_R3; - } else if (s_bits == MO_32) { - tcg_out_bundle(s, mII, - add_guest_base, - tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, - TCG_REG_R3, data_reg, 31, 31), - tcg_opc_bswap64_i(TCG_REG_P0, - TCG_REG_R3, TCG_REG_R3)); - data_reg = TCG_REG_R3; - } else if (s_bits == MO_64) { - tcg_out_bundle(s, miI, - add_guest_base, - INSN_NOP_I, - tcg_opc_bswap64_i(TCG_REG_P0, TCG_REG_R3, data_reg)); - data_reg = TCG_REG_R3; - } - tcg_out_bundle(s, miI, - tcg_opc_m4 (TCG_REG_P0, opc_st_m4[s_bits], - data_reg, addr_reg), - INSN_NOP_I, - INSN_NOP_I); - } -#endif -} - -#endif - -static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, - const TCGArg *args, const int *const_args) -{ - switch(opc) { - case INDEX_op_exit_tb: - tcg_out_exit_tb(s, args[0]); - break; - case INDEX_op_br: - tcg_out_br(s, arg_label(args[0])); - break; - case INDEX_op_goto_tb: - tcg_out_goto_tb(s, args[0]); - break; - - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - tcg_out_ld_rel(s, OPC_LD1_M1, args[0], args[1], args[2]); - break; - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - tcg_out_ld_rel(s, OPC_LD1_M1, args[0], args[1], args[2]); - tcg_out_ext(s, OPC_SXT1_I29, args[0], args[0]); - break; - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - tcg_out_ld_rel(s, OPC_LD2_M1, args[0], args[1], args[2]); - break; - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - tcg_out_ld_rel(s, OPC_LD2_M1, args[0], args[1], args[2]); - tcg_out_ext(s, OPC_SXT2_I29, args[0], args[0]); - break; - case INDEX_op_ld_i32: - case INDEX_op_ld32u_i64: - tcg_out_ld_rel(s, OPC_LD4_M1, args[0], args[1], args[2]); - break; - case INDEX_op_ld32s_i64: - tcg_out_ld_rel(s, OPC_LD4_M1, args[0], args[1], args[2]); - tcg_out_ext(s, OPC_SXT4_I29, args[0], args[0]); - break; - case INDEX_op_ld_i64: - tcg_out_ld_rel(s, OPC_LD8_M1, args[0], args[1], args[2]); - break; - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - tcg_out_st_rel(s, OPC_ST1_M4, args[0], args[1], args[2]); - break; - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - tcg_out_st_rel(s, OPC_ST2_M4, args[0], args[1], args[2]); - break; - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - tcg_out_st_rel(s, OPC_ST4_M4, args[0], args[1], args[2]); - break; - case INDEX_op_st_i64: - tcg_out_st_rel(s, OPC_ST8_M4, args[0], args[1], args[2]); - break; - - case INDEX_op_add_i32: - case INDEX_op_add_i64: - tcg_out_add(s, args[0], args[1], args[2], const_args[2]); - break; - case INDEX_op_sub_i32: - case INDEX_op_sub_i64: - tcg_out_sub(s, args[0], args[1], const_args[1], args[2], const_args[2]); - break; - - case INDEX_op_and_i32: - case INDEX_op_and_i64: - /* TCG expects arg2 constant; A3 expects arg1 constant. Swap. */ - tcg_out_alu(s, OPC_AND_A1, OPC_AND_A3, args[0], - args[2], const_args[2], args[1], const_args[1]); - break; - case INDEX_op_andc_i32: - case INDEX_op_andc_i64: - tcg_out_alu(s, OPC_ANDCM_A1, OPC_ANDCM_A3, args[0], - args[1], const_args[1], args[2], const_args[2]); - break; - case INDEX_op_eqv_i32: - case INDEX_op_eqv_i64: - tcg_out_eqv(s, args[0], args[1], const_args[1], - args[2], const_args[2]); - break; - case INDEX_op_nand_i32: - case INDEX_op_nand_i64: - tcg_out_nand(s, args[0], args[1], const_args[1], - args[2], const_args[2]); - break; - case INDEX_op_nor_i32: - case INDEX_op_nor_i64: - tcg_out_nor(s, args[0], args[1], const_args[1], - args[2], const_args[2]); - break; - case INDEX_op_or_i32: - case INDEX_op_or_i64: - /* TCG expects arg2 constant; A3 expects arg1 constant. Swap. */ - tcg_out_alu(s, OPC_OR_A1, OPC_OR_A3, args[0], - args[2], const_args[2], args[1], const_args[1]); - break; - case INDEX_op_orc_i32: - case INDEX_op_orc_i64: - tcg_out_orc(s, args[0], args[1], const_args[1], - args[2], const_args[2]); - break; - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: - /* TCG expects arg2 constant; A3 expects arg1 constant. Swap. */ - tcg_out_alu(s, OPC_XOR_A1, OPC_XOR_A3, args[0], - args[2], const_args[2], args[1], const_args[1]); - break; - - case INDEX_op_mul_i32: - case INDEX_op_mul_i64: - tcg_out_mul(s, args[0], args[1], args[2]); - break; - - case INDEX_op_sar_i32: - tcg_out_sar_i32(s, args[0], args[1], args[2], const_args[2]); - break; - case INDEX_op_sar_i64: - tcg_out_sar_i64(s, args[0], args[1], args[2], const_args[2]); - break; - case INDEX_op_shl_i32: - tcg_out_shl_i32(s, args[0], args[1], args[2], const_args[2]); - break; - case INDEX_op_shl_i64: - tcg_out_shl_i64(s, args[0], args[1], args[2], const_args[2]); - break; - case INDEX_op_shr_i32: - tcg_out_shr_i32(s, args[0], args[1], args[2], const_args[2]); - break; - case INDEX_op_shr_i64: - tcg_out_shr_i64(s, args[0], args[1], args[2], const_args[2]); - break; - case INDEX_op_rotl_i32: - tcg_out_rotl_i32(s, args[0], args[1], args[2], const_args[2]); - break; - case INDEX_op_rotl_i64: - tcg_out_rotl_i64(s, args[0], args[1], args[2], const_args[2]); - break; - case INDEX_op_rotr_i32: - tcg_out_rotr_i32(s, args[0], args[1], args[2], const_args[2]); - break; - case INDEX_op_rotr_i64: - tcg_out_rotr_i64(s, args[0], args[1], args[2], const_args[2]); - break; - - case INDEX_op_ext8s_i32: - case INDEX_op_ext8s_i64: - tcg_out_ext(s, OPC_SXT1_I29, args[0], args[1]); - break; - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - tcg_out_ext(s, OPC_ZXT1_I29, args[0], args[1]); - break; - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - tcg_out_ext(s, OPC_SXT2_I29, args[0], args[1]); - break; - case INDEX_op_ext16u_i32: - case INDEX_op_ext16u_i64: - tcg_out_ext(s, OPC_ZXT2_I29, args[0], args[1]); - break; - case INDEX_op_ext32s_i64: - tcg_out_ext(s, OPC_SXT4_I29, args[0], args[1]); - break; - case INDEX_op_ext32u_i64: - tcg_out_ext(s, OPC_ZXT4_I29, args[0], args[1]); - break; - - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: - tcg_out_bswap16(s, args[0], args[1]); - break; - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: - tcg_out_bswap32(s, args[0], args[1]); - break; - case INDEX_op_bswap64_i64: - tcg_out_bswap64(s, args[0], args[1]); - break; - - case INDEX_op_deposit_i32: - case INDEX_op_deposit_i64: - tcg_out_deposit(s, args[0], args[1], args[2], const_args[2], - args[3], args[4]); - break; - - case INDEX_op_brcond_i32: - tcg_out_brcond(s, args[2], args[0], args[1], arg_label(args[3]), 1); - break; - case INDEX_op_brcond_i64: - tcg_out_brcond(s, args[2], args[0], args[1], arg_label(args[3]), 0); - break; - case INDEX_op_setcond_i32: - tcg_out_setcond(s, args[3], args[0], args[1], args[2], 1); - break; - case INDEX_op_setcond_i64: - tcg_out_setcond(s, args[3], args[0], args[1], args[2], 0); - break; - case INDEX_op_movcond_i32: - tcg_out_movcond(s, args[5], args[0], args[1], args[2], - args[3], const_args[3], args[4], const_args[4], 1); - break; - case INDEX_op_movcond_i64: - tcg_out_movcond(s, args[5], args[0], args[1], args[2], - args[3], const_args[3], args[4], const_args[4], 0); - break; - - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, args); - break; - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, args); - break; - case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, args); - break; - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, args); - break; - - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_mov_i64: - case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ - case INDEX_op_movi_i64: - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - default: - tcg_abort(); - } -} - -static const TCGTargetOpDef ia64_op_defs[] = { - { INDEX_op_br, { } }, - { INDEX_op_exit_tb, { } }, - { INDEX_op_goto_tb, { } }, - - { INDEX_op_ld8u_i32, { "r", "r" } }, - { INDEX_op_ld8s_i32, { "r", "r" } }, - { INDEX_op_ld16u_i32, { "r", "r" } }, - { INDEX_op_ld16s_i32, { "r", "r" } }, - { INDEX_op_ld_i32, { "r", "r" } }, - { INDEX_op_st8_i32, { "rZ", "r" } }, - { INDEX_op_st16_i32, { "rZ", "r" } }, - { INDEX_op_st_i32, { "rZ", "r" } }, - - { INDEX_op_add_i32, { "r", "rZ", "rI" } }, - { INDEX_op_sub_i32, { "r", "rI", "rI" } }, - - { INDEX_op_and_i32, { "r", "rI", "rI" } }, - { INDEX_op_andc_i32, { "r", "rI", "rI" } }, - { INDEX_op_eqv_i32, { "r", "rZ", "rZ" } }, - { INDEX_op_nand_i32, { "r", "rZ", "rZ" } }, - { INDEX_op_nor_i32, { "r", "rZ", "rZ" } }, - { INDEX_op_or_i32, { "r", "rI", "rI" } }, - { INDEX_op_orc_i32, { "r", "rZ", "rZ" } }, - { INDEX_op_xor_i32, { "r", "rI", "rI" } }, - - { INDEX_op_mul_i32, { "r", "rZ", "rZ" } }, - - { INDEX_op_sar_i32, { "r", "rZ", "ri" } }, - { INDEX_op_shl_i32, { "r", "rZ", "ri" } }, - { INDEX_op_shr_i32, { "r", "rZ", "ri" } }, - { INDEX_op_rotl_i32, { "r", "rZ", "ri" } }, - { INDEX_op_rotr_i32, { "r", "rZ", "ri" } }, - - { INDEX_op_ext8s_i32, { "r", "rZ"} }, - { INDEX_op_ext8u_i32, { "r", "rZ"} }, - { INDEX_op_ext16s_i32, { "r", "rZ"} }, - { INDEX_op_ext16u_i32, { "r", "rZ"} }, - - { INDEX_op_bswap16_i32, { "r", "rZ" } }, - { INDEX_op_bswap32_i32, { "r", "rZ" } }, - - { INDEX_op_brcond_i32, { "rZ", "rZ" } }, - { INDEX_op_setcond_i32, { "r", "rZ", "rZ" } }, - { INDEX_op_movcond_i32, { "r", "rZ", "rZ", "rI", "rI" } }, - - { INDEX_op_ld8u_i64, { "r", "r" } }, - { INDEX_op_ld8s_i64, { "r", "r" } }, - { INDEX_op_ld16u_i64, { "r", "r" } }, - { INDEX_op_ld16s_i64, { "r", "r" } }, - { INDEX_op_ld32u_i64, { "r", "r" } }, - { INDEX_op_ld32s_i64, { "r", "r" } }, - { INDEX_op_ld_i64, { "r", "r" } }, - { INDEX_op_st8_i64, { "rZ", "r" } }, - { INDEX_op_st16_i64, { "rZ", "r" } }, - { INDEX_op_st32_i64, { "rZ", "r" } }, - { INDEX_op_st_i64, { "rZ", "r" } }, - - { INDEX_op_add_i64, { "r", "rZ", "rI" } }, - { INDEX_op_sub_i64, { "r", "rI", "rI" } }, - - { INDEX_op_and_i64, { "r", "rI", "rI" } }, - { INDEX_op_andc_i64, { "r", "rI", "rI" } }, - { INDEX_op_eqv_i64, { "r", "rZ", "rZ" } }, - { INDEX_op_nand_i64, { "r", "rZ", "rZ" } }, - { INDEX_op_nor_i64, { "r", "rZ", "rZ" } }, - { INDEX_op_or_i64, { "r", "rI", "rI" } }, - { INDEX_op_orc_i64, { "r", "rZ", "rZ" } }, - { INDEX_op_xor_i64, { "r", "rI", "rI" } }, - - { INDEX_op_mul_i64, { "r", "rZ", "rZ" } }, - - { INDEX_op_sar_i64, { "r", "rZ", "ri" } }, - { INDEX_op_shl_i64, { "r", "rZ", "ri" } }, - { INDEX_op_shr_i64, { "r", "rZ", "ri" } }, - { INDEX_op_rotl_i64, { "r", "rZ", "ri" } }, - { INDEX_op_rotr_i64, { "r", "rZ", "ri" } }, - - { INDEX_op_ext8s_i64, { "r", "rZ"} }, - { INDEX_op_ext8u_i64, { "r", "rZ"} }, - { INDEX_op_ext16s_i64, { "r", "rZ"} }, - { INDEX_op_ext16u_i64, { "r", "rZ"} }, - { INDEX_op_ext32s_i64, { "r", "rZ"} }, - { INDEX_op_ext32u_i64, { "r", "rZ"} }, - - { INDEX_op_bswap16_i64, { "r", "rZ" } }, - { INDEX_op_bswap32_i64, { "r", "rZ" } }, - { INDEX_op_bswap64_i64, { "r", "rZ" } }, - - { INDEX_op_brcond_i64, { "rZ", "rZ" } }, - { INDEX_op_setcond_i64, { "r", "rZ", "rZ" } }, - { INDEX_op_movcond_i64, { "r", "rZ", "rZ", "rI", "rI" } }, - - { INDEX_op_deposit_i32, { "r", "rZ", "ri" } }, - { INDEX_op_deposit_i64, { "r", "rZ", "ri" } }, - - { INDEX_op_qemu_ld_i32, { "r", "r" } }, - { INDEX_op_qemu_ld_i64, { "r", "r" } }, - { INDEX_op_qemu_st_i32, { "SZ", "r" } }, - { INDEX_op_qemu_st_i64, { "SZ", "r" } }, - - { -1 }, -}; - -/* Generate global QEMU prologue and epilogue code */ -static void tcg_target_qemu_prologue(TCGContext *s) -{ - int frame_size; - - /* reserve some stack space */ - frame_size = TCG_STATIC_CALL_ARGS_SIZE + - CPU_TEMP_BUF_NLONGS * sizeof(long); - frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) & - ~(TCG_TARGET_STACK_ALIGN - 1); - tcg_set_frame(s, TCG_REG_CALL_STACK, TCG_STATIC_CALL_ARGS_SIZE, - CPU_TEMP_BUF_NLONGS * sizeof(long)); - - /* First emit adhoc function descriptor */ - *s->code_ptr = (tcg_insn_unit){ - (uint64_t)(s->code_ptr + 1), /* entry point */ - 0 /* skip gp */ - }; - s->code_ptr++; - - /* prologue */ - tcg_out_bundle(s, miI, - tcg_opc_m34(TCG_REG_P0, OPC_ALLOC_M34, - TCG_REG_R34, 32, 24, 0), - INSN_NOP_I, - tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, - TCG_REG_B6, TCG_REG_R33, 0)); - - /* ??? If GUEST_BASE < 0x200000, we could load the register via - an ADDL in the M slot of the next bundle. */ - if (GUEST_BASE != 0) { - tcg_out_bundle(s, mlx, - INSN_NOP_M, - tcg_opc_l2 (GUEST_BASE), - tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, - TCG_GUEST_BASE_REG, GUEST_BASE)); - tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); - } - - tcg_out_bundle(s, miB, - tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4, - TCG_REG_R12, -frame_size, TCG_REG_R12), - tcg_opc_i22(TCG_REG_P0, OPC_MOV_I22, - TCG_REG_R33, TCG_REG_B0), - tcg_opc_b4 (TCG_REG_P0, OPC_BR_SPTK_MANY_B4, TCG_REG_B6)); - - /* epilogue */ - tb_ret_addr = s->code_ptr; - tcg_out_bundle(s, miI, - INSN_NOP_M, - tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, - TCG_REG_B0, TCG_REG_R33, 0), - tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4, - TCG_REG_R12, frame_size, TCG_REG_R12)); - tcg_out_bundle(s, miB, - INSN_NOP_M, - tcg_opc_i26(TCG_REG_P0, OPC_MOV_I_I26, - TCG_REG_PFS, TCG_REG_R34), - tcg_opc_b4 (TCG_REG_P0, OPC_BR_RET_SPTK_MANY_B4, - TCG_REG_B0)); -} - -static void tcg_target_init(TCGContext *s) -{ - tcg_regset_set(tcg_target_available_regs[TCG_TYPE_I32], - 0xffffffffffffffffull); - tcg_regset_set(tcg_target_available_regs[TCG_TYPE_I64], - 0xffffffffffffffffull); - - tcg_regset_clear(tcg_target_call_clobber_regs); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R8); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R9); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R10); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R11); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R14); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R15); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R16); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R17); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R18); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R19); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R20); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R21); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R22); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R23); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R24); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R25); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R26); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R27); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R28); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R29); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R30); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R31); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R56); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R57); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R58); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R59); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R60); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R61); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R62); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R63); - - tcg_regset_clear(s->reserved_regs); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); /* zero register */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R1); /* global pointer */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R2); /* internal use */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R3); /* internal use */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R12); /* stack pointer */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R13); /* thread pointer */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R33); /* return address */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R34); /* PFS */ - - /* The following 4 are not in use, are call-saved, but *not* saved - by the prologue. Therefore we cannot use them without modifying - the prologue. There doesn't seem to be any good reason to use - these as opposed to the windowed registers. */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R4); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R5); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R6); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R7); - - tcg_add_target_add_op_defs(ia64_op_defs); -} diff --git a/qemu/tcg/ia64/tcg-target.h b/qemu/tcg/ia64/tcg-target.h index a04ed8126..ae9b79f02 100644 --- a/qemu/tcg/ia64/tcg-target.h +++ b/qemu/tcg/ia64/tcg-target.h @@ -160,7 +160,8 @@ typedef enum { #define TCG_TARGET_HAS_muluh_i64 0 #define TCG_TARGET_HAS_mulsh_i32 0 #define TCG_TARGET_HAS_mulsh_i64 0 -#define TCG_TARGET_HAS_trunc_shr_i32 0 +#define TCG_TARGET_HAS_extrl_i64_i32 0 +#define TCG_TARGET_HAS_extrh_i64_i32 0 #define TCG_TARGET_deposit_i32_valid(ofs, len) ((len) <= 16) #define TCG_TARGET_deposit_i64_valid(ofs, len) ((len) <= 16) diff --git a/qemu/tcg/ia64/tcg-target.inc.c b/qemu/tcg/ia64/tcg-target.inc.c new file mode 100644 index 000000000..7557e6a9d --- /dev/null +++ b/qemu/tcg/ia64/tcg-target.inc.c @@ -0,0 +1,2453 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2009-2010 Aurelien Jarno + * Based on i386/tcg-target.c - Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Register definitions + */ + +#ifdef CONFIG_DEBUG_TCG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", + "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39", + "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47", + "r48", "r49", "r50", "r51", "r52", "r53", "r54", "r55", + "r56", "r57", "r58", "r59", "r60", "r61", "r62", "r63", +}; +#endif + +#ifndef CONFIG_SOFTMMU +#define TCG_GUEST_BASE_REG TCG_REG_R55 +#endif + +/* Branch registers */ +enum { + TCG_REG_B0 = 0, + TCG_REG_B1, + TCG_REG_B2, + TCG_REG_B3, + TCG_REG_B4, + TCG_REG_B5, + TCG_REG_B6, + TCG_REG_B7, +}; + +/* Floating point registers */ +enum { + TCG_REG_F0 = 0, + TCG_REG_F1, + TCG_REG_F2, + TCG_REG_F3, + TCG_REG_F4, + TCG_REG_F5, + TCG_REG_F6, + TCG_REG_F7, + TCG_REG_F8, + TCG_REG_F9, + TCG_REG_F10, + TCG_REG_F11, + TCG_REG_F12, + TCG_REG_F13, + TCG_REG_F14, + TCG_REG_F15, +}; + +/* Predicate registers */ +enum { + TCG_REG_P0 = 0, + TCG_REG_P1, + TCG_REG_P2, + TCG_REG_P3, + TCG_REG_P4, + TCG_REG_P5, + TCG_REG_P6, + TCG_REG_P7, + TCG_REG_P8, + TCG_REG_P9, + TCG_REG_P10, + TCG_REG_P11, + TCG_REG_P12, + TCG_REG_P13, + TCG_REG_P14, + TCG_REG_P15, +}; + +/* Application registers */ +enum { + TCG_REG_PFS = 64, +}; + +static const int tcg_target_reg_alloc_order[] = { + TCG_REG_R35, + TCG_REG_R36, + TCG_REG_R37, + TCG_REG_R38, + TCG_REG_R39, + TCG_REG_R40, + TCG_REG_R41, + TCG_REG_R42, + TCG_REG_R43, + TCG_REG_R44, + TCG_REG_R45, + TCG_REG_R46, + TCG_REG_R47, + TCG_REG_R48, + TCG_REG_R49, + TCG_REG_R50, + TCG_REG_R51, + TCG_REG_R52, + TCG_REG_R53, + TCG_REG_R54, + TCG_REG_R55, + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R16, + TCG_REG_R17, + TCG_REG_R18, + TCG_REG_R19, + TCG_REG_R20, + TCG_REG_R21, + TCG_REG_R22, + TCG_REG_R23, + TCG_REG_R24, + TCG_REG_R25, + TCG_REG_R26, + TCG_REG_R27, + TCG_REG_R28, + TCG_REG_R29, + TCG_REG_R30, + TCG_REG_R31, + TCG_REG_R56, + TCG_REG_R57, + TCG_REG_R58, + TCG_REG_R59, + TCG_REG_R60, + TCG_REG_R61, + TCG_REG_R62, + TCG_REG_R63, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11 +}; + +static const int tcg_target_call_iarg_regs[8] = { + TCG_REG_R56, + TCG_REG_R57, + TCG_REG_R58, + TCG_REG_R59, + TCG_REG_R60, + TCG_REG_R61, + TCG_REG_R62, + TCG_REG_R63, +}; + +static const int tcg_target_call_oarg_regs[] = { + TCG_REG_R8 +}; + +/* + * opcode formation + */ + +/* bundle templates: stops (double bar in the IA64 manual) are marked with + an uppercase letter. */ +enum { + mii = 0x00, + miI = 0x01, + mIi = 0x02, + mII = 0x03, + mlx = 0x04, + mLX = 0x05, + mmi = 0x08, + mmI = 0x09, + Mmi = 0x0a, + MmI = 0x0b, + mfi = 0x0c, + mfI = 0x0d, + mmf = 0x0e, + mmF = 0x0f, + mib = 0x10, + miB = 0x11, + mbb = 0x12, + mbB = 0x13, + bbb = 0x16, + bbB = 0x17, + mmb = 0x18, + mmB = 0x19, + mfb = 0x1c, + mfB = 0x1d, +}; + +enum { + OPC_ADD_A1 = 0x10000000000ull, + OPC_AND_A1 = 0x10060000000ull, + OPC_AND_A3 = 0x10160000000ull, + OPC_ANDCM_A1 = 0x10068000000ull, + OPC_ANDCM_A3 = 0x10168000000ull, + OPC_ADDS_A4 = 0x10800000000ull, + OPC_ADDL_A5 = 0x12000000000ull, + OPC_ALLOC_M34 = 0x02c00000000ull, + OPC_BR_DPTK_FEW_B1 = 0x08400000000ull, + OPC_BR_SPTK_MANY_B1 = 0x08000001000ull, + OPC_BR_CALL_SPNT_FEW_B3 = 0x0a200000000ull, + OPC_BR_SPTK_MANY_B4 = 0x00100001000ull, + OPC_BR_CALL_SPTK_MANY_B5 = 0x02100001000ull, + OPC_BR_RET_SPTK_MANY_B4 = 0x00108001100ull, + OPC_BRL_SPTK_MANY_X3 = 0x18000001000ull, + OPC_BRL_CALL_SPNT_MANY_X4 = 0x1a200001000ull, + OPC_BRL_CALL_SPTK_MANY_X4 = 0x1a000001000ull, + OPC_CMP_LT_A6 = 0x18000000000ull, + OPC_CMP_LTU_A6 = 0x1a000000000ull, + OPC_CMP_EQ_A6 = 0x1c000000000ull, + OPC_CMP4_LT_A6 = 0x18400000000ull, + OPC_CMP4_LTU_A6 = 0x1a400000000ull, + OPC_CMP4_EQ_A6 = 0x1c400000000ull, + OPC_DEP_I14 = 0x0ae00000000ull, + OPC_DEP_I15 = 0x08000000000ull, + OPC_DEP_Z_I12 = 0x0a600000000ull, + OPC_EXTR_I11 = 0x0a400002000ull, + OPC_EXTR_U_I11 = 0x0a400000000ull, + OPC_FCVT_FX_TRUNC_S1_F10 = 0x004d0000000ull, + OPC_FCVT_FXU_TRUNC_S1_F10 = 0x004d8000000ull, + OPC_FCVT_XF_F11 = 0x000e0000000ull, + OPC_FMA_S1_F1 = 0x10400000000ull, + OPC_FNMA_S1_F1 = 0x18400000000ull, + OPC_FRCPA_S1_F6 = 0x00600000000ull, + OPC_GETF_SIG_M19 = 0x08708000000ull, + OPC_LD1_M1 = 0x08000000000ull, + OPC_LD1_M3 = 0x0a000000000ull, + OPC_LD2_M1 = 0x08040000000ull, + OPC_LD2_M3 = 0x0a040000000ull, + OPC_LD4_M1 = 0x08080000000ull, + OPC_LD4_M3 = 0x0a080000000ull, + OPC_LD8_M1 = 0x080c0000000ull, + OPC_LD8_M3 = 0x0a0c0000000ull, + OPC_MUX1_I3 = 0x0eca0000000ull, + OPC_NOP_B9 = 0x04008000000ull, + OPC_NOP_F16 = 0x00008000000ull, + OPC_NOP_I18 = 0x00008000000ull, + OPC_NOP_M48 = 0x00008000000ull, + OPC_MOV_I21 = 0x00e00100000ull, + OPC_MOV_RET_I21 = 0x00e00500000ull, + OPC_MOV_I22 = 0x00188000000ull, + OPC_MOV_I_I26 = 0x00150000000ull, + OPC_MOVL_X2 = 0x0c000000000ull, + OPC_OR_A1 = 0x10070000000ull, + OPC_OR_A3 = 0x10170000000ull, + OPC_SETF_EXP_M18 = 0x0c748000000ull, + OPC_SETF_SIG_M18 = 0x0c708000000ull, + OPC_SHL_I7 = 0x0f240000000ull, + OPC_SHR_I5 = 0x0f220000000ull, + OPC_SHR_U_I5 = 0x0f200000000ull, + OPC_SHRP_I10 = 0x0ac00000000ull, + OPC_SXT1_I29 = 0x000a0000000ull, + OPC_SXT2_I29 = 0x000a8000000ull, + OPC_SXT4_I29 = 0x000b0000000ull, + OPC_ST1_M4 = 0x08c00000000ull, + OPC_ST2_M4 = 0x08c40000000ull, + OPC_ST4_M4 = 0x08c80000000ull, + OPC_ST8_M4 = 0x08cc0000000ull, + OPC_SUB_A1 = 0x10028000000ull, + OPC_SUB_A3 = 0x10128000000ull, + OPC_UNPACK4_L_I2 = 0x0f860000000ull, + OPC_XMA_L_F2 = 0x1d000000000ull, + OPC_XOR_A1 = 0x10078000000ull, + OPC_XOR_A3 = 0x10178000000ull, + OPC_ZXT1_I29 = 0x00080000000ull, + OPC_ZXT2_I29 = 0x00088000000ull, + OPC_ZXT4_I29 = 0x00090000000ull, + + INSN_NOP_M = OPC_NOP_M48, /* nop.m 0 */ + INSN_NOP_I = OPC_NOP_I18, /* nop.i 0 */ +}; + +static inline uint64_t tcg_opc_a1(int qp, uint64_t opc, int r1, + int r2, int r3) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_a3(int qp, uint64_t opc, int r1, + uint64_t imm, int r3) +{ + return opc + | ((imm & 0x80) << 29) /* s */ + | ((imm & 0x7f) << 13) /* imm7b */ + | ((r3 & 0x7f) << 20) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_a4(int qp, uint64_t opc, int r1, + uint64_t imm, int r3) +{ + return opc + | ((imm & 0x2000) << 23) /* s */ + | ((imm & 0x1f80) << 20) /* imm6d */ + | ((imm & 0x007f) << 13) /* imm7b */ + | ((r3 & 0x7f) << 20) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_a5(int qp, uint64_t opc, int r1, + uint64_t imm, int r3) +{ + return opc + | ((imm & 0x200000) << 15) /* s */ + | ((imm & 0x1f0000) << 6) /* imm5c */ + | ((imm & 0x00ff80) << 20) /* imm9d */ + | ((imm & 0x00007f) << 13) /* imm7b */ + | ((r3 & 0x03) << 20) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_a6(int qp, uint64_t opc, int p1, + int p2, int r2, int r3) +{ + return opc + | ((p2 & 0x3f) << 27) + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | ((p1 & 0x3f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_b1(int qp, uint64_t opc, uint64_t imm) +{ + return opc + | ((imm & 0x100000) << 16) /* s */ + | ((imm & 0x0fffff) << 13) /* imm20b */ + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_b3(int qp, uint64_t opc, int b1, uint64_t imm) +{ + return opc + | ((imm & 0x100000) << 16) /* s */ + | ((imm & 0x0fffff) << 13) /* imm20b */ + | ((b1 & 0x7) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_b4(int qp, uint64_t opc, int b2) +{ + return opc + | ((b2 & 0x7) << 13) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_b5(int qp, uint64_t opc, int b1, int b2) +{ + return opc + | ((b2 & 0x7) << 13) + | ((b1 & 0x7) << 6) + | (qp & 0x3f); +} + + +static inline uint64_t tcg_opc_b9(int qp, uint64_t opc, uint64_t imm) +{ + return opc + | ((imm & 0x100000) << 16) /* i */ + | ((imm & 0x0fffff) << 6) /* imm20a */ + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_f1(int qp, uint64_t opc, int f1, + int f3, int f4, int f2) +{ + return opc + | ((f4 & 0x7f) << 27) + | ((f3 & 0x7f) << 20) + | ((f2 & 0x7f) << 13) + | ((f1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_f2(int qp, uint64_t opc, int f1, + int f3, int f4, int f2) +{ + return opc + | ((f4 & 0x7f) << 27) + | ((f3 & 0x7f) << 20) + | ((f2 & 0x7f) << 13) + | ((f1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_f6(int qp, uint64_t opc, int f1, + int p2, int f2, int f3) +{ + return opc + | ((p2 & 0x3f) << 27) + | ((f3 & 0x7f) << 20) + | ((f2 & 0x7f) << 13) + | ((f1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_f10(int qp, uint64_t opc, int f1, int f2) +{ + return opc + | ((f2 & 0x7f) << 13) + | ((f1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_f11(int qp, uint64_t opc, int f1, int f2) +{ + return opc + | ((f2 & 0x7f) << 13) + | ((f1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_f16(int qp, uint64_t opc, uint64_t imm) +{ + return opc + | ((imm & 0x100000) << 16) /* i */ + | ((imm & 0x0fffff) << 6) /* imm20a */ + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i2(int qp, uint64_t opc, int r1, + int r2, int r3) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i3(int qp, uint64_t opc, int r1, + int r2, int mbtype) +{ + return opc + | ((mbtype & 0x0f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i5(int qp, uint64_t opc, int r1, + int r3, int r2) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i7(int qp, uint64_t opc, int r1, + int r2, int r3) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i10(int qp, uint64_t opc, int r1, + int r2, int r3, uint64_t count) +{ + return opc + | ((count & 0x3f) << 27) + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i11(int qp, uint64_t opc, int r1, + int r3, uint64_t pos, uint64_t len) +{ + return opc + | ((len & 0x3f) << 27) + | ((r3 & 0x7f) << 20) + | ((pos & 0x3f) << 14) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i12(int qp, uint64_t opc, int r1, + int r2, uint64_t pos, uint64_t len) +{ + return opc + | ((len & 0x3f) << 27) + | ((pos & 0x3f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i14(int qp, uint64_t opc, int r1, uint64_t imm, + int r3, uint64_t pos, uint64_t len) +{ + return opc + | ((imm & 0x01) << 36) + | ((len & 0x3f) << 27) + | ((r3 & 0x7f) << 20) + | ((pos & 0x3f) << 14) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i15(int qp, uint64_t opc, int r1, int r2, + int r3, uint64_t pos, uint64_t len) +{ + return opc + | ((pos & 0x3f) << 31) + | ((len & 0x0f) << 27) + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i18(int qp, uint64_t opc, uint64_t imm) +{ + return opc + | ((imm & 0x100000) << 16) /* i */ + | ((imm & 0x0fffff) << 6) /* imm20a */ + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i21(int qp, uint64_t opc, int b1, + int r2, uint64_t imm) +{ + return opc + | ((imm & 0x1ff) << 24) + | ((r2 & 0x7f) << 13) + | ((b1 & 0x7) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i22(int qp, uint64_t opc, int r1, int b2) +{ + return opc + | ((b2 & 0x7) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i26(int qp, uint64_t opc, int ar3, int r2) +{ + return opc + | ((ar3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i29(int qp, uint64_t opc, int r1, int r3) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_l2(uint64_t imm) +{ + return (imm & 0x7fffffffffc00000ull) >> 22; +} + +static inline uint64_t tcg_opc_l3(uint64_t imm) +{ + return (imm & 0x07fffffffff00000ull) >> 18; +} + +#define tcg_opc_l4 tcg_opc_l3 + +static inline uint64_t tcg_opc_m1(int qp, uint64_t opc, int r1, int r3) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_m3(int qp, uint64_t opc, int r1, + int r3, uint64_t imm) +{ + return opc + | ((imm & 0x100) << 28) /* s */ + | ((imm & 0x080) << 20) /* i */ + | ((imm & 0x07f) << 13) /* imm7b */ + | ((r3 & 0x7f) << 20) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_m4(int qp, uint64_t opc, int r2, int r3) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_m18(int qp, uint64_t opc, int f1, int r2) +{ + return opc + | ((r2 & 0x7f) << 13) + | ((f1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_m19(int qp, uint64_t opc, int r1, int f2) +{ + return opc + | ((f2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_m34(int qp, uint64_t opc, int r1, + int sof, int sol, int sor) +{ + return opc + | ((sor & 0x0f) << 27) + | ((sol & 0x7f) << 20) + | ((sof & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_m48(int qp, uint64_t opc, uint64_t imm) +{ + return opc + | ((imm & 0x100000) << 16) /* i */ + | ((imm & 0x0fffff) << 6) /* imm20a */ + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_x2(int qp, uint64_t opc, + int r1, uint64_t imm) +{ + return opc + | ((imm & 0x8000000000000000ull) >> 27) /* i */ + | (imm & 0x0000000000200000ull) /* ic */ + | ((imm & 0x00000000001f0000ull) << 6) /* imm5c */ + | ((imm & 0x000000000000ff80ull) << 20) /* imm9d */ + | ((imm & 0x000000000000007full) << 13) /* imm7b */ + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_x3(int qp, uint64_t opc, uint64_t imm) +{ + return opc + | ((imm & 0x0800000000000000ull) >> 23) /* i */ + | ((imm & 0x00000000000fffffull) << 13) /* imm20b */ + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_x4(int qp, uint64_t opc, int b1, uint64_t imm) +{ + return opc + | ((imm & 0x0800000000000000ull) >> 23) /* i */ + | ((imm & 0x00000000000fffffull) << 13) /* imm20b */ + | ((b1 & 0x7) << 6) + | (qp & 0x3f); +} + + +/* + * Relocations - Note that we never encode branches elsewhere than slot 2. + */ + +static void reloc_pcrel21b_slot2(tcg_insn_unit *pc, tcg_insn_unit *target) +{ + uint64_t imm = target - pc; + + pc->hi = (pc->hi & 0xf700000fffffffffull) + | ((imm & 0x100000) << 39) /* s */ + | ((imm & 0x0fffff) << 36); /* imm20b */ +} + +static uint64_t get_reloc_pcrel21b_slot2(tcg_insn_unit *pc) +{ + int64_t high = pc->hi; + + return ((high >> 39) & 0x100000) + /* s */ + ((high >> 36) & 0x0fffff); /* imm20b */ +} + +static void patch_reloc(tcg_insn_unit *code_ptr, int type, + intptr_t value, intptr_t addend) +{ + tcg_debug_assert(addend == 0); + tcg_debug_assert(type == R_IA64_PCREL21B); + reloc_pcrel21b_slot2(code_ptr, (tcg_insn_unit *)value); +} + +/* + * Constraints + */ + +/* parse target specific constraints */ +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +{ + const char *ct_str; + + ct_str = *pct_str; + switch(ct_str[0]) { + case 'r': + ct->ct |= TCG_CT_REG; + tcg_regset_set(ct->u.regs, 0xffffffffffffffffull); + break; + case 'I': + ct->ct |= TCG_CT_CONST_S22; + break; + case 'S': + ct->ct |= TCG_CT_REG; + tcg_regset_set(ct->u.regs, 0xffffffffffffffffull); +#if defined(CONFIG_SOFTMMU) + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R56); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R57); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R58); +#endif + break; + case 'Z': + /* We are cheating a bit here, using the fact that the register + r0 is also the register number 0. Hence there is no need + to check for const_args in each instruction. */ + ct->ct |= TCG_CT_CONST_ZERO; + break; + default: + return -1; + } + ct_str++; + *pct_str = ct_str; + return 0; +} + +/* test if a constant matches the constraint */ +static inline int tcg_target_const_match(tcg_target_long val, TCGType type, + const TCGArgConstraint *arg_ct) +{ + int ct; + ct = arg_ct->ct; + if (ct & TCG_CT_CONST) + return 1; + else if ((ct & TCG_CT_CONST_ZERO) && val == 0) + return 1; + else if ((ct & TCG_CT_CONST_S22) && val == ((int32_t)val << 10) >> 10) + return 1; + else + return 0; +} + +/* + * Code generation + */ + +static tcg_insn_unit *tb_ret_addr; + +static inline void tcg_out_bundle(TCGContext *s, int template, + uint64_t slot0, uint64_t slot1, + uint64_t slot2) +{ + template &= 0x1f; /* 5 bits */ + slot0 &= 0x1ffffffffffull; /* 41 bits */ + slot1 &= 0x1ffffffffffull; /* 41 bits */ + slot2 &= 0x1ffffffffffull; /* 41 bits */ + + *s->code_ptr++ = (tcg_insn_unit){ + (slot1 << 46) | (slot0 << 5) | template, + (slot2 << 23) | (slot1 >> 18) + }; +} + +static inline uint64_t tcg_opc_mov_a(int qp, TCGReg dst, TCGReg src) +{ + return tcg_opc_a4(qp, OPC_ADDS_A4, dst, 0, src); +} + +static inline void tcg_out_mov(TCGContext *s, TCGType type, + TCGReg ret, TCGReg arg) +{ + tcg_out_bundle(s, mmI, + INSN_NOP_M, + INSN_NOP_M, + tcg_opc_mov_a(TCG_REG_P0, ret, arg)); +} + +static inline uint64_t tcg_opc_movi_a(int qp, TCGReg dst, int64_t src) +{ + tcg_debug_assert(src == sextract64(src, 0, 22)); + return tcg_opc_a5(qp, OPC_ADDL_A5, dst, src, TCG_REG_R0); +} + +static inline void tcg_out_movi(TCGContext *s, TCGType type, + TCGReg reg, tcg_target_long arg) +{ + tcg_out_bundle(s, mLX, + INSN_NOP_M, + tcg_opc_l2 (arg), + tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, reg, arg)); +} + +static void tcg_out_br(TCGContext *s, TCGLabel *l) +{ + uint64_t imm; + + /* We pay attention here to not modify the branch target by reading + the existing value and using it again. This ensure that caches and + memory are kept coherent during retranslation. */ + if (l->has_value) { + imm = l->u.value_ptr - s->code_ptr; + } else { + imm = get_reloc_pcrel21b_slot2(s->code_ptr); + tcg_out_reloc(s, s->code_ptr, R_IA64_PCREL21B, l, 0); + } + + tcg_out_bundle(s, mmB, + INSN_NOP_M, + INSN_NOP_M, + tcg_opc_b1(TCG_REG_P0, OPC_BR_SPTK_MANY_B1, imm)); +} + +static inline void tcg_out_call(TCGContext *s, tcg_insn_unit *desc) +{ + uintptr_t func = desc->lo, gp = desc->hi, disp; + + /* Look through the function descriptor. */ + tcg_out_bundle(s, mlx, + INSN_NOP_M, + tcg_opc_l2 (gp), + tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, TCG_REG_R1, gp)); + disp = (tcg_insn_unit *)func - s->code_ptr; + tcg_out_bundle(s, mLX, + INSN_NOP_M, + tcg_opc_l4 (disp), + tcg_opc_x4 (TCG_REG_P0, OPC_BRL_CALL_SPTK_MANY_X4, + TCG_REG_B0, disp)); +} + +static void tcg_out_exit_tb(TCGContext *s, tcg_target_long arg) +{ + uint64_t imm, opc1; + + /* At least arg == 0 is a common operation. */ + if (arg == sextract64(arg, 0, 22)) { + opc1 = tcg_opc_movi_a(TCG_REG_P0, TCG_REG_R8, arg); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R8, arg); + opc1 = INSN_NOP_M; + } + + imm = tb_ret_addr - s->code_ptr; + + tcg_out_bundle(s, mLX, + opc1, + tcg_opc_l3 (imm), + tcg_opc_x3 (TCG_REG_P0, OPC_BRL_SPTK_MANY_X3, imm)); +} + +static inline void tcg_out_goto_tb(TCGContext *s, TCGArg arg) +{ + if (s->tb_jmp_offset) { + /* direct jump method */ + tcg_abort(); + } else { + /* indirect jump method */ + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, + (tcg_target_long)(s->tb_next + arg)); + tcg_out_bundle(s, MmI, + tcg_opc_m1 (TCG_REG_P0, OPC_LD8_M1, + TCG_REG_R2, TCG_REG_R2), + INSN_NOP_M, + tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, TCG_REG_B6, + TCG_REG_R2, 0)); + tcg_out_bundle(s, mmB, + INSN_NOP_M, + INSN_NOP_M, + tcg_opc_b4 (TCG_REG_P0, OPC_BR_SPTK_MANY_B4, + TCG_REG_B6)); + } + s->tb_next_offset[arg] = tcg_current_code_size(s); +} + +static inline void tcg_out_jmp(TCGContext *s, TCGArg addr) +{ + tcg_out_bundle(s, mmI, + INSN_NOP_M, + INSN_NOP_M, + tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, TCG_REG_B6, addr, 0)); + tcg_out_bundle(s, mmB, + INSN_NOP_M, + INSN_NOP_M, + tcg_opc_b4(TCG_REG_P0, OPC_BR_SPTK_MANY_B4, TCG_REG_B6)); +} + +static inline void tcg_out_ld_rel(TCGContext *s, uint64_t opc_m4, TCGArg arg, + TCGArg arg1, tcg_target_long arg2) +{ + if (arg2 == ((int16_t)arg2 >> 2) << 2) { + tcg_out_bundle(s, MmI, + tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, + TCG_REG_R2, arg2, arg1), + tcg_opc_m1 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), + INSN_NOP_I); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, arg2); + tcg_out_bundle(s, MmI, + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, + TCG_REG_R2, TCG_REG_R2, arg1), + tcg_opc_m1 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), + INSN_NOP_I); + } +} + +static inline void tcg_out_st_rel(TCGContext *s, uint64_t opc_m4, TCGArg arg, + TCGArg arg1, tcg_target_long arg2) +{ + if (arg2 == ((int16_t)arg2 >> 2) << 2) { + tcg_out_bundle(s, MmI, + tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, + TCG_REG_R2, arg2, arg1), + tcg_opc_m4 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), + INSN_NOP_I); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, arg2); + tcg_out_bundle(s, MmI, + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, + TCG_REG_R2, TCG_REG_R2, arg1), + tcg_opc_m4 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), + INSN_NOP_I); + } +} + +static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg arg, + TCGReg arg1, intptr_t arg2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_ld_rel(s, OPC_LD4_M1, arg, arg1, arg2); + } else { + tcg_out_ld_rel(s, OPC_LD8_M1, arg, arg1, arg2); + } +} + +static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, + TCGReg arg1, intptr_t arg2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_st_rel(s, OPC_ST4_M4, arg, arg1, arg2); + } else { + tcg_out_st_rel(s, OPC_ST8_M4, arg, arg1, arg2); + } +} + +static inline void tcg_out_alu(TCGContext *s, uint64_t opc_a1, uint64_t opc_a3, + TCGReg ret, TCGArg arg1, int const_arg1, + TCGArg arg2, int const_arg2) +{ + uint64_t opc1 = 0, opc2 = 0, opc3 = 0; + + if (const_arg2 && arg2 != 0) { + opc2 = tcg_opc_movi_a(TCG_REG_P0, TCG_REG_R3, arg2); + arg2 = TCG_REG_R3; + } + if (const_arg1 && arg1 != 0) { + if (opc_a3 && arg1 == (int8_t)arg1) { + opc3 = tcg_opc_a3(TCG_REG_P0, opc_a3, ret, arg1, arg2); + } else { + opc1 = tcg_opc_movi_a(TCG_REG_P0, TCG_REG_R2, arg1); + arg1 = TCG_REG_R2; + } + } + if (opc3 == 0) { + opc3 = tcg_opc_a1(TCG_REG_P0, opc_a1, ret, arg1, arg2); + } + + tcg_out_bundle(s, (opc1 || opc2 ? mII : miI), + opc1 ? opc1 : INSN_NOP_M, + opc2 ? opc2 : INSN_NOP_I, + opc3); +} + +static inline void tcg_out_add(TCGContext *s, TCGReg ret, TCGReg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2 && arg2 == sextract64(arg2, 0, 14)) { + tcg_out_bundle(s, mmI, + INSN_NOP_M, + INSN_NOP_M, + tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, ret, arg2, arg1)); + } else { + tcg_out_alu(s, OPC_ADD_A1, 0, ret, arg1, 0, arg2, const_arg2); + } +} + +static inline void tcg_out_sub(TCGContext *s, TCGReg ret, TCGArg arg1, + int const_arg1, TCGArg arg2, int const_arg2) +{ + if (!const_arg1 && const_arg2 && -arg2 == sextract64(-arg2, 0, 14)) { + tcg_out_bundle(s, mmI, + INSN_NOP_M, + INSN_NOP_M, + tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, ret, -arg2, arg1)); + } else { + tcg_out_alu(s, OPC_SUB_A1, OPC_SUB_A3, ret, + arg1, const_arg1, arg2, const_arg2); + } +} + +static inline void tcg_out_eqv(TCGContext *s, TCGArg ret, + TCGArg arg1, int const_arg1, + TCGArg arg2, int const_arg2) +{ + tcg_out_bundle(s, mII, + INSN_NOP_M, + tcg_opc_a1 (TCG_REG_P0, OPC_XOR_A1, ret, arg1, arg2), + tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, ret, -1, ret)); +} + +static inline void tcg_out_nand(TCGContext *s, TCGArg ret, + TCGArg arg1, int const_arg1, + TCGArg arg2, int const_arg2) +{ + tcg_out_bundle(s, mII, + INSN_NOP_M, + tcg_opc_a1 (TCG_REG_P0, OPC_AND_A1, ret, arg1, arg2), + tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, ret, -1, ret)); +} + +static inline void tcg_out_nor(TCGContext *s, TCGArg ret, + TCGArg arg1, int const_arg1, + TCGArg arg2, int const_arg2) +{ + tcg_out_bundle(s, mII, + INSN_NOP_M, + tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, arg1, arg2), + tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, ret, -1, ret)); +} + +static inline void tcg_out_orc(TCGContext *s, TCGArg ret, + TCGArg arg1, int const_arg1, + TCGArg arg2, int const_arg2) +{ + tcg_out_bundle(s, mII, + INSN_NOP_M, + tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, TCG_REG_R2, -1, arg2), + tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, arg1, TCG_REG_R2)); +} + +static inline void tcg_out_mul(TCGContext *s, TCGArg ret, + TCGArg arg1, TCGArg arg2) +{ + tcg_out_bundle(s, mmI, + tcg_opc_m18(TCG_REG_P0, OPC_SETF_SIG_M18, TCG_REG_F6, arg1), + tcg_opc_m18(TCG_REG_P0, OPC_SETF_SIG_M18, TCG_REG_F7, arg2), + INSN_NOP_I); + tcg_out_bundle(s, mmF, + INSN_NOP_M, + INSN_NOP_M, + tcg_opc_f2 (TCG_REG_P0, OPC_XMA_L_F2, TCG_REG_F6, TCG_REG_F6, + TCG_REG_F7, TCG_REG_F0)); + tcg_out_bundle(s, miI, + tcg_opc_m19(TCG_REG_P0, OPC_GETF_SIG_M19, ret, TCG_REG_F6), + INSN_NOP_I, + INSN_NOP_I); +} + +static inline void tcg_out_sar_i32(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_I11, + ret, arg1, arg2, 31 - arg2)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, + TCG_REG_R3, 0x1f, arg2), + tcg_opc_i29(TCG_REG_P0, OPC_SXT4_I29, TCG_REG_R2, arg1), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_I5, ret, + TCG_REG_R2, TCG_REG_R3)); + } +} + +static inline void tcg_out_sar_i64(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_I11, + ret, arg1, arg2, 63 - arg2)); + } else { + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_I5, ret, arg1, arg2)); + } +} + +static inline void tcg_out_shl_i32(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, + arg1, 63 - arg2, 31 - arg2)); + } else { + tcg_out_bundle(s, mII, + INSN_NOP_M, + tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R2, + 0x1f, arg2), + tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, ret, + arg1, TCG_REG_R2)); + } +} + +static inline void tcg_out_shl_i64(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, + arg1, 63 - arg2, 63 - arg2)); + } else { + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, ret, + arg1, arg2)); + } +} + +static inline void tcg_out_shr_i32(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, + arg1, arg2, 31 - arg2)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R3, + 0x1f, arg2), + tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, TCG_REG_R2, arg1), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret, + TCG_REG_R2, TCG_REG_R3)); + } +} + +static inline void tcg_out_shr_i64(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, + arg1, arg2, 63 - arg2)); + } else { + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret, + arg1, arg2)); + } +} + +static inline void tcg_out_rotl_i32(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, mII, + INSN_NOP_M, + tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2, + TCG_REG_R2, arg1, arg1), + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, + TCG_REG_R2, 32 - arg2, 31)); + } else { + tcg_out_bundle(s, miI, + INSN_NOP_M, + tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2, + TCG_REG_R2, arg1, arg1), + tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R3, + 0x1f, arg2)); + tcg_out_bundle(s, mII, + INSN_NOP_M, + tcg_opc_a3 (TCG_REG_P0, OPC_SUB_A3, TCG_REG_R3, + 0x20, TCG_REG_R3), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret, + TCG_REG_R2, TCG_REG_R3)); + } +} + +static inline void tcg_out_rotl_i64(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_i10(TCG_REG_P0, OPC_SHRP_I10, ret, arg1, + arg1, 0x40 - arg2)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_a3 (TCG_REG_P0, OPC_SUB_A3, TCG_REG_R2, + 0x40, arg2), + tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, TCG_REG_R3, + arg1, arg2), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, TCG_REG_R2, + arg1, TCG_REG_R2)); + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, + TCG_REG_R2, TCG_REG_R3)); + } +} + +static inline void tcg_out_rotr_i32(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, mII, + INSN_NOP_M, + tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2, + TCG_REG_R2, arg1, arg1), + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, + TCG_REG_R2, arg2, 31)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R3, + 0x1f, arg2), + tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2, + TCG_REG_R2, arg1, arg1), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret, + TCG_REG_R2, TCG_REG_R3)); + } +} + +static inline void tcg_out_rotr_i64(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_i10(TCG_REG_P0, OPC_SHRP_I10, ret, arg1, + arg1, arg2)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_a3 (TCG_REG_P0, OPC_SUB_A3, TCG_REG_R2, + 0x40, arg2), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, TCG_REG_R3, + arg1, arg2), + tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, TCG_REG_R2, + arg1, TCG_REG_R2)); + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, + TCG_REG_R2, TCG_REG_R3)); + } +} + +static const uint64_t opc_ext_i29[8] = { + OPC_ZXT1_I29, OPC_ZXT2_I29, OPC_ZXT4_I29, 0, + OPC_SXT1_I29, OPC_SXT2_I29, OPC_SXT4_I29, 0 +}; + +static inline uint64_t tcg_opc_ext_i(int qp, TCGMemOp opc, TCGReg d, TCGReg s) +{ + if ((opc & MO_SIZE) == MO_64) { + return tcg_opc_mov_a(qp, d, s); + } else { + return tcg_opc_i29(qp, opc_ext_i29[opc & MO_SSIZE], d, s); + } +} + +static inline void tcg_out_ext(TCGContext *s, uint64_t opc_i29, + TCGArg ret, TCGArg arg) +{ + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_i29(TCG_REG_P0, opc_i29, ret, arg)); +} + +static inline uint64_t tcg_opc_bswap64_i(int qp, TCGReg d, TCGReg s) +{ + return tcg_opc_i3(qp, OPC_MUX1_I3, d, s, 0xb); +} + +static inline void tcg_out_bswap16(TCGContext *s, TCGArg ret, TCGArg arg) +{ + tcg_out_bundle(s, mII, + INSN_NOP_M, + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, arg, 15, 15), + tcg_opc_bswap64_i(TCG_REG_P0, ret, ret)); +} + +static inline void tcg_out_bswap32(TCGContext *s, TCGArg ret, TCGArg arg) +{ + tcg_out_bundle(s, mII, + INSN_NOP_M, + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, arg, 31, 31), + tcg_opc_bswap64_i(TCG_REG_P0, ret, ret)); +} + +static inline void tcg_out_bswap64(TCGContext *s, TCGArg ret, TCGArg arg) +{ + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_bswap64_i(TCG_REG_P0, ret, arg)); +} + +static inline void tcg_out_deposit(TCGContext *s, TCGArg ret, TCGArg a1, + TCGArg a2, int const_a2, int pos, int len) +{ + uint64_t i1 = 0, i2 = 0; + int cpos = 63 - pos, lm1 = len - 1; + + if (const_a2) { + /* Truncate the value of a constant a2 to the width of the field. */ + int mask = (1u << len) - 1; + a2 &= mask; + + if (a2 == 0 || a2 == mask) { + /* 1-bit signed constant inserted into register. */ + i2 = tcg_opc_i14(TCG_REG_P0, OPC_DEP_I14, ret, a2, a1, cpos, lm1); + } else { + /* Otherwise, load any constant into a temporary. Do this into + the first I slot to help out with cross-unit delays. */ + i1 = tcg_opc_movi_a(TCG_REG_P0, TCG_REG_R2, a2); + a2 = TCG_REG_R2; + } + } + if (i2 == 0) { + i2 = tcg_opc_i15(TCG_REG_P0, OPC_DEP_I15, ret, a2, a1, cpos, lm1); + } + tcg_out_bundle(s, (i1 ? mII : miI), + INSN_NOP_M, + i1 ? i1 : INSN_NOP_I, + i2); +} + +static inline uint64_t tcg_opc_cmp_a(int qp, TCGCond cond, TCGArg arg1, + TCGArg arg2, int cmp4) +{ + uint64_t opc_eq_a6, opc_lt_a6, opc_ltu_a6; + + if (cmp4) { + opc_eq_a6 = OPC_CMP4_EQ_A6; + opc_lt_a6 = OPC_CMP4_LT_A6; + opc_ltu_a6 = OPC_CMP4_LTU_A6; + } else { + opc_eq_a6 = OPC_CMP_EQ_A6; + opc_lt_a6 = OPC_CMP_LT_A6; + opc_ltu_a6 = OPC_CMP_LTU_A6; + } + + switch (cond) { + case TCG_COND_EQ: + return tcg_opc_a6 (qp, opc_eq_a6, TCG_REG_P6, TCG_REG_P7, arg1, arg2); + case TCG_COND_NE: + return tcg_opc_a6 (qp, opc_eq_a6, TCG_REG_P7, TCG_REG_P6, arg1, arg2); + case TCG_COND_LT: + return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P6, TCG_REG_P7, arg1, arg2); + case TCG_COND_LTU: + return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P6, TCG_REG_P7, arg1, arg2); + case TCG_COND_GE: + return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P7, TCG_REG_P6, arg1, arg2); + case TCG_COND_GEU: + return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P7, TCG_REG_P6, arg1, arg2); + case TCG_COND_LE: + return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P7, TCG_REG_P6, arg2, arg1); + case TCG_COND_LEU: + return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P7, TCG_REG_P6, arg2, arg1); + case TCG_COND_GT: + return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P6, TCG_REG_P7, arg2, arg1); + case TCG_COND_GTU: + return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P6, TCG_REG_P7, arg2, arg1); + default: + tcg_abort(); + break; + } +} + +static inline void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, + TCGReg arg2, TCGLabel *l, int cmp4) +{ + uint64_t imm; + + /* We pay attention here to not modify the branch target by reading + the existing value and using it again. This ensure that caches and + memory are kept coherent during retranslation. */ + if (l->has_value) { + imm = l->u.value_ptr - s->code_ptr; + } else { + imm = get_reloc_pcrel21b_slot2(s->code_ptr); + tcg_out_reloc(s, s->code_ptr, R_IA64_PCREL21B, l, 0); + } + + tcg_out_bundle(s, miB, + INSN_NOP_M, + tcg_opc_cmp_a(TCG_REG_P0, cond, arg1, arg2, cmp4), + tcg_opc_b1(TCG_REG_P6, OPC_BR_DPTK_FEW_B1, imm)); +} + +static inline void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGArg ret, + TCGArg arg1, TCGArg arg2, int cmp4) +{ + tcg_out_bundle(s, MmI, + tcg_opc_cmp_a(TCG_REG_P0, cond, arg1, arg2, cmp4), + tcg_opc_movi_a(TCG_REG_P6, ret, 1), + tcg_opc_movi_a(TCG_REG_P7, ret, 0)); +} + +static inline void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGArg ret, + TCGArg c1, TCGArg c2, + TCGArg v1, int const_v1, + TCGArg v2, int const_v2, int cmp4) +{ + uint64_t opc1, opc2; + + if (const_v1) { + opc1 = tcg_opc_movi_a(TCG_REG_P6, ret, v1); + } else if (ret == v1) { + opc1 = INSN_NOP_M; + } else { + opc1 = tcg_opc_mov_a(TCG_REG_P6, ret, v1); + } + if (const_v2) { + opc2 = tcg_opc_movi_a(TCG_REG_P7, ret, v2); + } else if (ret == v2) { + opc2 = INSN_NOP_I; + } else { + opc2 = tcg_opc_mov_a(TCG_REG_P7, ret, v2); + } + + tcg_out_bundle(s, MmI, + tcg_opc_cmp_a(TCG_REG_P0, cond, c1, c2, cmp4), + opc1, + opc2); +} + +#if defined(CONFIG_SOFTMMU) +/* We're expecting to use an signed 22-bit immediate add. */ +QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table[NB_MMU_MODES - 1][1]) + > 0x1fffff) + +/* Load and compare a TLB entry, and return the result in (p6, p7). + R2 is loaded with the addend TLB entry. + R57 is loaded with the address, zero extented on 32-bit targets. + R1, R3 are clobbered, leaving R56 free for... + BSWAP_1, BSWAP_2 and I-slot insns for swapping data for store. */ +static inline void tcg_out_qemu_tlb(TCGContext *s, TCGReg addr_reg, + TCGMemOp s_bits, int off_rw, int off_add, + uint64_t bswap1, uint64_t bswap2) +{ + /* + .mii + mov r2 = off_rw + extr.u r3 = addr_reg, ... # extract tlb page + zxt4 r57 = addr_reg # or mov for 64-bit guest + ;; + .mii + addl r2 = r2, areg0 + shl r3 = r3, cteb # via dep.z + dep r1 = 0, r57, ... # zero page ofs, keep align + ;; + .mmi + add r2 = r2, r3 + ;; + ld4 r3 = [r2], off_add-off_rw # or ld8 for 64-bit guest + nop + ;; + .mmi + nop + cmp.eq p6, p7 = r3, r58 + nop + ;; + */ + tcg_out_bundle(s, miI, + tcg_opc_movi_a(TCG_REG_P0, TCG_REG_R2, off_rw), + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, TCG_REG_R3, + addr_reg, TARGET_PAGE_BITS, CPU_TLB_BITS - 1), + tcg_opc_ext_i(TCG_REG_P0, + TARGET_LONG_BITS == 32 ? MO_UL : MO_Q, + TCG_REG_R57, addr_reg)); + tcg_out_bundle(s, miI, + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, + TCG_REG_R2, TCG_AREG0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, TCG_REG_R3, + TCG_REG_R3, 63 - CPU_TLB_ENTRY_BITS, + 63 - CPU_TLB_ENTRY_BITS), + tcg_opc_i14(TCG_REG_P0, OPC_DEP_I14, TCG_REG_R1, 0, + TCG_REG_R57, 63 - s_bits, + TARGET_PAGE_BITS - s_bits - 1)); + tcg_out_bundle(s, MmI, + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, + TCG_REG_R2, TCG_REG_R2, TCG_REG_R3), + tcg_opc_m3 (TCG_REG_P0, + (TARGET_LONG_BITS == 32 + ? OPC_LD4_M3 : OPC_LD8_M3), TCG_REG_R3, + TCG_REG_R2, off_add - off_rw), + bswap1); + tcg_out_bundle(s, mmI, + tcg_opc_m1 (TCG_REG_P0, OPC_LD8_M1, TCG_REG_R2, TCG_REG_R2), + tcg_opc_a6 (TCG_REG_P0, OPC_CMP_EQ_A6, TCG_REG_P6, + TCG_REG_P7, TCG_REG_R1, TCG_REG_R3), + bswap2); +} + +typedef struct TCGLabelQemuLdst { + bool is_ld; + TCGMemOp size; + tcg_insn_unit *label_ptr; /* label pointers to be updated */ + struct TCGLabelQemuLdst *next; +} TCGLabelQemuLdst; + +typedef struct TCGBackendData { + TCGLabelQemuLdst *labels; +} TCGBackendData; + +static inline void tcg_out_tb_init(TCGContext *s) +{ + s->be->labels = NULL; +} + +static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOp opc, + tcg_insn_unit *label_ptr) +{ + TCGBackendData *be = s->be; + TCGLabelQemuLdst *l = tcg_malloc(sizeof(*l)); + + l->is_ld = is_ld; + l->size = opc & MO_SIZE; + l->label_ptr = label_ptr; + l->next = be->labels; + be->labels = l; +} + +static bool tcg_out_tb_finalize(TCGContext *s) +{ + static const void * const helpers[8] = { + helper_ret_stb_mmu, + helper_le_stw_mmu, + helper_le_stl_mmu, + helper_le_stq_mmu, + helper_ret_ldub_mmu, + helper_le_lduw_mmu, + helper_le_ldul_mmu, + helper_le_ldq_mmu, + }; + tcg_insn_unit *thunks[8] = { }; + TCGLabelQemuLdst *l; + + for (l = s->be->labels; l != NULL; l = l->next) { + long x = l->is_ld * 4 + l->size; + tcg_insn_unit *dest = thunks[x]; + + /* The out-of-line thunks are all the same; load the return address + from B0, load the GP, and branch to the code. Note that we are + always post-call, so the register window has rolled, so we're + using incoming parameter register numbers, not outgoing. */ + if (dest == NULL) { + uintptr_t *desc = (uintptr_t *)helpers[x]; + uintptr_t func = desc[0], gp = desc[1], disp; + + thunks[x] = dest = s->code_ptr; + + tcg_out_bundle(s, mlx, + INSN_NOP_M, + tcg_opc_l2 (gp), + tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, + TCG_REG_R1, gp)); + tcg_out_bundle(s, mii, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_i22(TCG_REG_P0, OPC_MOV_I22, + l->is_ld ? TCG_REG_R35 : TCG_REG_R36, + TCG_REG_B0)); + disp = (tcg_insn_unit *)func - s->code_ptr; + tcg_out_bundle(s, mLX, + INSN_NOP_M, + tcg_opc_l3 (disp), + tcg_opc_x3 (TCG_REG_P0, OPC_BRL_SPTK_MANY_X3, disp)); + } + + reloc_pcrel21b_slot2(l->label_ptr, dest); + + /* Test for (pending) buffer overflow. The assumption is that any + one operation beginning below the high water mark cannot overrun + the buffer completely. Thus we can test for overflow after + generating code without having to check during generation. */ + if (unlikely((void *)s->code_ptr > s->code_gen_highwater)) { + return false; + } + } + return true; +} + +static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args) +{ + static const uint64_t opc_ld_m1[4] = { + OPC_LD1_M1, OPC_LD2_M1, OPC_LD4_M1, OPC_LD8_M1 + }; + int addr_reg, data_reg, mem_index; + TCGMemOpIdx oi; + TCGMemOp opc, s_bits; + uint64_t fin1, fin2; + tcg_insn_unit *label_ptr; + + data_reg = args[0]; + addr_reg = args[1]; + oi = args[2]; + opc = get_memop(oi); + mem_index = get_mmuidx(oi); + s_bits = opc & MO_SIZE; + + /* Read the TLB entry */ + tcg_out_qemu_tlb(s, addr_reg, s_bits, + offsetof(CPUArchState, tlb_table[mem_index][0].addr_read), + offsetof(CPUArchState, tlb_table[mem_index][0].addend), + INSN_NOP_I, INSN_NOP_I); + + /* P6 is the fast path, and P7 the slow path */ + + fin2 = 0; + if (opc & MO_BSWAP) { + fin1 = tcg_opc_bswap64_i(TCG_REG_P0, data_reg, TCG_REG_R8); + if (s_bits < MO_64) { + int shift = 64 - (8 << s_bits); + fin2 = (opc & MO_SIGN ? OPC_EXTR_I11 : OPC_EXTR_U_I11); + fin2 = tcg_opc_i11(TCG_REG_P0, fin2, + data_reg, data_reg, shift, 63 - shift); + } + } else { + fin1 = tcg_opc_ext_i(TCG_REG_P0, opc, data_reg, TCG_REG_R8); + } + + tcg_out_bundle(s, mmI, + tcg_opc_mov_a(TCG_REG_P7, TCG_REG_R56, TCG_AREG0), + tcg_opc_a1 (TCG_REG_P6, OPC_ADD_A1, TCG_REG_R2, + TCG_REG_R2, TCG_REG_R57), + tcg_opc_movi_a(TCG_REG_P7, TCG_REG_R58, oi)); + label_ptr = s->code_ptr; + tcg_out_bundle(s, miB, + tcg_opc_m1 (TCG_REG_P6, opc_ld_m1[s_bits], + TCG_REG_R8, TCG_REG_R2), + INSN_NOP_I, + tcg_opc_b3 (TCG_REG_P7, OPC_BR_CALL_SPNT_FEW_B3, TCG_REG_B0, + get_reloc_pcrel21b_slot2(label_ptr))); + + add_qemu_ldst_label(s, 1, opc, label_ptr); + + /* Note that we always use LE helper functions, so the bswap insns + here for the fast path also apply to the slow path. */ + tcg_out_bundle(s, (fin2 ? mII : miI), + INSN_NOP_M, + fin1, + fin2 ? fin2 : INSN_NOP_I); +} + +static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args) +{ + static const uint64_t opc_st_m4[4] = { + OPC_ST1_M4, OPC_ST2_M4, OPC_ST4_M4, OPC_ST8_M4 + }; + TCGReg addr_reg, data_reg; + int mem_index; + uint64_t pre1, pre2; + TCGMemOpIdx oi; + TCGMemOp opc, s_bits; + tcg_insn_unit *label_ptr; + + data_reg = args[0]; + addr_reg = args[1]; + oi = args[2]; + opc = get_memop(oi); + mem_index = get_mmuidx(oi); + s_bits = opc & MO_SIZE; + + /* Note that we always use LE helper functions, so the bswap insns + that are here for the fast path also apply to the slow path, + and move the data into the argument register. */ + pre2 = INSN_NOP_I; + if (opc & MO_BSWAP) { + pre1 = tcg_opc_bswap64_i(TCG_REG_P0, TCG_REG_R58, data_reg); + if (s_bits < MO_64) { + int shift = 64 - (8 << s_bits); + pre2 = tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, + TCG_REG_R58, TCG_REG_R58, shift, 63 - shift); + } + } else { + /* Just move the data into place for the slow path. */ + pre1 = tcg_opc_ext_i(TCG_REG_P0, opc, TCG_REG_R58, data_reg); + } + + tcg_out_qemu_tlb(s, addr_reg, s_bits, + offsetof(CPUArchState, tlb_table[mem_index][0].addr_write), + offsetof(CPUArchState, tlb_table[mem_index][0].addend), + pre1, pre2); + + /* P6 is the fast path, and P7 the slow path */ + tcg_out_bundle(s, mmI, + tcg_opc_mov_a(TCG_REG_P7, TCG_REG_R56, TCG_AREG0), + tcg_opc_a1 (TCG_REG_P6, OPC_ADD_A1, TCG_REG_R2, + TCG_REG_R2, TCG_REG_R57), + tcg_opc_movi_a(TCG_REG_P7, TCG_REG_R59, oi)); + label_ptr = s->code_ptr; + tcg_out_bundle(s, miB, + tcg_opc_m4 (TCG_REG_P6, opc_st_m4[s_bits], + TCG_REG_R58, TCG_REG_R2), + INSN_NOP_I, + tcg_opc_b3 (TCG_REG_P7, OPC_BR_CALL_SPNT_FEW_B3, TCG_REG_B0, + get_reloc_pcrel21b_slot2(label_ptr))); + + add_qemu_ldst_label(s, 0, opc, label_ptr); +} + +#else /* !CONFIG_SOFTMMU */ +# include "tcg-be-null.h" + +static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args) +{ + static uint64_t const opc_ld_m1[4] = { + OPC_LD1_M1, OPC_LD2_M1, OPC_LD4_M1, OPC_LD8_M1 + }; + int addr_reg, data_reg; + TCGMemOp opc, s_bits, bswap; + + data_reg = args[0]; + addr_reg = args[1]; + opc = args[2]; + s_bits = opc & MO_SIZE; + bswap = opc & MO_BSWAP; + +#if TARGET_LONG_BITS == 32 + if (guest_base != 0) { + tcg_out_bundle(s, mII, + INSN_NOP_M, + tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, + TCG_REG_R3, addr_reg), + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, + TCG_GUEST_BASE_REG, TCG_REG_R3)); + } else { + tcg_out_bundle(s, miI, + INSN_NOP_M, + tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, + TCG_REG_R2, addr_reg), + INSN_NOP_I); + } + + if (!bswap) { + if (!(opc & MO_SIGN)) { + tcg_out_bundle(s, miI, + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, TCG_REG_R2), + INSN_NOP_I, + INSN_NOP_I); + } else { + tcg_out_bundle(s, mII, + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, TCG_REG_R2), + INSN_NOP_I, + tcg_opc_ext_i(TCG_REG_P0, opc, data_reg, data_reg)); + } + } else if (s_bits == MO_64) { + tcg_out_bundle(s, mII, + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, TCG_REG_R2), + INSN_NOP_I, + tcg_opc_bswap64_i(TCG_REG_P0, data_reg, data_reg)); + } else { + if (s_bits == MO_16) { + tcg_out_bundle(s, mII, + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, TCG_REG_R2), + INSN_NOP_I, + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + data_reg, data_reg, 15, 15)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, TCG_REG_R2), + INSN_NOP_I, + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + data_reg, data_reg, 31, 31)); + } + if (!(opc & MO_SIGN)) { + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_bswap64_i(TCG_REG_P0, data_reg, data_reg)); + } else { + tcg_out_bundle(s, mII, + INSN_NOP_M, + tcg_opc_bswap64_i(TCG_REG_P0, data_reg, data_reg), + tcg_opc_ext_i(TCG_REG_P0, opc, data_reg, data_reg)); + } + } +#else + if (guest_base != 0) { + tcg_out_bundle(s, MmI, + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, + TCG_GUEST_BASE_REG, addr_reg), + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, TCG_REG_R2), + INSN_NOP_I); + } else { + tcg_out_bundle(s, mmI, + INSN_NOP_M, + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, addr_reg), + INSN_NOP_I); + } + + if (bswap && s_bits == MO_16) { + tcg_out_bundle(s, mII, + INSN_NOP_M, + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + data_reg, data_reg, 15, 15), + tcg_opc_bswap64_i(TCG_REG_P0, data_reg, data_reg)); + } else if (bswap && s_bits == MO_32) { + tcg_out_bundle(s, mII, + INSN_NOP_M, + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + data_reg, data_reg, 31, 31), + tcg_opc_bswap64_i(TCG_REG_P0, data_reg, data_reg)); + } else if (bswap && s_bits == MO_64) { + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_bswap64_i(TCG_REG_P0, data_reg, data_reg)); + } + if (opc & MO_SIGN) { + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_ext_i(TCG_REG_P0, opc, data_reg, data_reg)); + } +#endif +} + +static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args) +{ + static uint64_t const opc_st_m4[4] = { + OPC_ST1_M4, OPC_ST2_M4, OPC_ST4_M4, OPC_ST8_M4 + }; + int addr_reg, data_reg; +#if TARGET_LONG_BITS == 64 + uint64_t add_guest_base; +#endif + TCGMemOp opc, s_bits, bswap; + + data_reg = args[0]; + addr_reg = args[1]; + opc = args[2]; + s_bits = opc & MO_SIZE; + bswap = opc & MO_BSWAP; + +#if TARGET_LONG_BITS == 32 + if (guest_base != 0) { + tcg_out_bundle(s, mII, + INSN_NOP_M, + tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, + TCG_REG_R3, addr_reg), + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, + TCG_GUEST_BASE_REG, TCG_REG_R3)); + } else { + tcg_out_bundle(s, miI, + INSN_NOP_M, + tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, + TCG_REG_R2, addr_reg), + INSN_NOP_I); + } + + if (bswap) { + if (s_bits == MO_16) { + tcg_out_bundle(s, mII, + INSN_NOP_M, + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + TCG_REG_R3, data_reg, 15, 15), + tcg_opc_bswap64_i(TCG_REG_P0, + TCG_REG_R3, TCG_REG_R3)); + data_reg = TCG_REG_R3; + } else if (s_bits == MO_32) { + tcg_out_bundle(s, mII, + INSN_NOP_M, + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + TCG_REG_R3, data_reg, 31, 31), + tcg_opc_bswap64_i(TCG_REG_P0, + TCG_REG_R3, TCG_REG_R3)); + data_reg = TCG_REG_R3; + } else if (s_bits == MO_64) { + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_bswap64_i(TCG_REG_P0, TCG_REG_R3, data_reg)); + data_reg = TCG_REG_R3; + } + } + tcg_out_bundle(s, mmI, + tcg_opc_m4 (TCG_REG_P0, opc_st_m4[s_bits], + data_reg, TCG_REG_R2), + INSN_NOP_M, + INSN_NOP_I); +#else + if (guest_base != 0) { + add_guest_base = tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, + TCG_GUEST_BASE_REG, addr_reg); + addr_reg = TCG_REG_R2; + } else { + add_guest_base = INSN_NOP_M; + } + + if (!bswap) { + tcg_out_bundle(s, (guest_base ? MmI : mmI), + add_guest_base, + tcg_opc_m4 (TCG_REG_P0, opc_st_m4[s_bits], + data_reg, addr_reg), + INSN_NOP_I); + } else { + if (s_bits == MO_16) { + tcg_out_bundle(s, mII, + add_guest_base, + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + TCG_REG_R3, data_reg, 15, 15), + tcg_opc_bswap64_i(TCG_REG_P0, + TCG_REG_R3, TCG_REG_R3)); + data_reg = TCG_REG_R3; + } else if (s_bits == MO_32) { + tcg_out_bundle(s, mII, + add_guest_base, + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + TCG_REG_R3, data_reg, 31, 31), + tcg_opc_bswap64_i(TCG_REG_P0, + TCG_REG_R3, TCG_REG_R3)); + data_reg = TCG_REG_R3; + } else if (s_bits == MO_64) { + tcg_out_bundle(s, miI, + add_guest_base, + INSN_NOP_I, + tcg_opc_bswap64_i(TCG_REG_P0, TCG_REG_R3, data_reg)); + data_reg = TCG_REG_R3; + } + tcg_out_bundle(s, miI, + tcg_opc_m4 (TCG_REG_P0, opc_st_m4[s_bits], + data_reg, addr_reg), + INSN_NOP_I, + INSN_NOP_I); + } +#endif +} + +#endif + +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, + const TCGArg *args, const int *const_args) +{ + switch(opc) { + case INDEX_op_exit_tb: + tcg_out_exit_tb(s, args[0]); + break; + case INDEX_op_br: + tcg_out_br(s, arg_label(args[0])); + break; + case INDEX_op_goto_tb: + tcg_out_goto_tb(s, args[0]); + break; + + case INDEX_op_ld8u_i32: + case INDEX_op_ld8u_i64: + tcg_out_ld_rel(s, OPC_LD1_M1, args[0], args[1], args[2]); + break; + case INDEX_op_ld8s_i32: + case INDEX_op_ld8s_i64: + tcg_out_ld_rel(s, OPC_LD1_M1, args[0], args[1], args[2]); + tcg_out_ext(s, OPC_SXT1_I29, args[0], args[0]); + break; + case INDEX_op_ld16u_i32: + case INDEX_op_ld16u_i64: + tcg_out_ld_rel(s, OPC_LD2_M1, args[0], args[1], args[2]); + break; + case INDEX_op_ld16s_i32: + case INDEX_op_ld16s_i64: + tcg_out_ld_rel(s, OPC_LD2_M1, args[0], args[1], args[2]); + tcg_out_ext(s, OPC_SXT2_I29, args[0], args[0]); + break; + case INDEX_op_ld_i32: + case INDEX_op_ld32u_i64: + tcg_out_ld_rel(s, OPC_LD4_M1, args[0], args[1], args[2]); + break; + case INDEX_op_ld32s_i64: + tcg_out_ld_rel(s, OPC_LD4_M1, args[0], args[1], args[2]); + tcg_out_ext(s, OPC_SXT4_I29, args[0], args[0]); + break; + case INDEX_op_ld_i64: + tcg_out_ld_rel(s, OPC_LD8_M1, args[0], args[1], args[2]); + break; + case INDEX_op_st8_i32: + case INDEX_op_st8_i64: + tcg_out_st_rel(s, OPC_ST1_M4, args[0], args[1], args[2]); + break; + case INDEX_op_st16_i32: + case INDEX_op_st16_i64: + tcg_out_st_rel(s, OPC_ST2_M4, args[0], args[1], args[2]); + break; + case INDEX_op_st_i32: + case INDEX_op_st32_i64: + tcg_out_st_rel(s, OPC_ST4_M4, args[0], args[1], args[2]); + break; + case INDEX_op_st_i64: + tcg_out_st_rel(s, OPC_ST8_M4, args[0], args[1], args[2]); + break; + + case INDEX_op_add_i32: + case INDEX_op_add_i64: + tcg_out_add(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_sub_i32: + case INDEX_op_sub_i64: + tcg_out_sub(s, args[0], args[1], const_args[1], args[2], const_args[2]); + break; + + case INDEX_op_and_i32: + case INDEX_op_and_i64: + /* TCG expects arg2 constant; A3 expects arg1 constant. Swap. */ + tcg_out_alu(s, OPC_AND_A1, OPC_AND_A3, args[0], + args[2], const_args[2], args[1], const_args[1]); + break; + case INDEX_op_andc_i32: + case INDEX_op_andc_i64: + tcg_out_alu(s, OPC_ANDCM_A1, OPC_ANDCM_A3, args[0], + args[1], const_args[1], args[2], const_args[2]); + break; + case INDEX_op_eqv_i32: + case INDEX_op_eqv_i64: + tcg_out_eqv(s, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_nand_i32: + case INDEX_op_nand_i64: + tcg_out_nand(s, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_nor_i32: + case INDEX_op_nor_i64: + tcg_out_nor(s, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_or_i32: + case INDEX_op_or_i64: + /* TCG expects arg2 constant; A3 expects arg1 constant. Swap. */ + tcg_out_alu(s, OPC_OR_A1, OPC_OR_A3, args[0], + args[2], const_args[2], args[1], const_args[1]); + break; + case INDEX_op_orc_i32: + case INDEX_op_orc_i64: + tcg_out_orc(s, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_xor_i32: + case INDEX_op_xor_i64: + /* TCG expects arg2 constant; A3 expects arg1 constant. Swap. */ + tcg_out_alu(s, OPC_XOR_A1, OPC_XOR_A3, args[0], + args[2], const_args[2], args[1], const_args[1]); + break; + + case INDEX_op_mul_i32: + case INDEX_op_mul_i64: + tcg_out_mul(s, args[0], args[1], args[2]); + break; + + case INDEX_op_sar_i32: + tcg_out_sar_i32(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_sar_i64: + tcg_out_sar_i64(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_shl_i32: + tcg_out_shl_i32(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_shl_i64: + tcg_out_shl_i64(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_shr_i32: + tcg_out_shr_i32(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_shr_i64: + tcg_out_shr_i64(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_rotl_i32: + tcg_out_rotl_i32(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_rotl_i64: + tcg_out_rotl_i64(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_rotr_i32: + tcg_out_rotr_i32(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_rotr_i64: + tcg_out_rotr_i64(s, args[0], args[1], args[2], const_args[2]); + break; + + case INDEX_op_ext8s_i32: + case INDEX_op_ext8s_i64: + tcg_out_ext(s, OPC_SXT1_I29, args[0], args[1]); + break; + case INDEX_op_ext8u_i32: + case INDEX_op_ext8u_i64: + tcg_out_ext(s, OPC_ZXT1_I29, args[0], args[1]); + break; + case INDEX_op_ext16s_i32: + case INDEX_op_ext16s_i64: + tcg_out_ext(s, OPC_SXT2_I29, args[0], args[1]); + break; + case INDEX_op_ext16u_i32: + case INDEX_op_ext16u_i64: + tcg_out_ext(s, OPC_ZXT2_I29, args[0], args[1]); + break; + case INDEX_op_ext_i32_i64: + case INDEX_op_ext32s_i64: + tcg_out_ext(s, OPC_SXT4_I29, args[0], args[1]); + break; + case INDEX_op_extu_i32_i64: + case INDEX_op_ext32u_i64: + tcg_out_ext(s, OPC_ZXT4_I29, args[0], args[1]); + break; + + case INDEX_op_bswap16_i32: + case INDEX_op_bswap16_i64: + tcg_out_bswap16(s, args[0], args[1]); + break; + case INDEX_op_bswap32_i32: + case INDEX_op_bswap32_i64: + tcg_out_bswap32(s, args[0], args[1]); + break; + case INDEX_op_bswap64_i64: + tcg_out_bswap64(s, args[0], args[1]); + break; + + case INDEX_op_deposit_i32: + case INDEX_op_deposit_i64: + tcg_out_deposit(s, args[0], args[1], args[2], const_args[2], + args[3], args[4]); + break; + + case INDEX_op_brcond_i32: + tcg_out_brcond(s, args[2], args[0], args[1], arg_label(args[3]), 1); + break; + case INDEX_op_brcond_i64: + tcg_out_brcond(s, args[2], args[0], args[1], arg_label(args[3]), 0); + break; + case INDEX_op_setcond_i32: + tcg_out_setcond(s, args[3], args[0], args[1], args[2], 1); + break; + case INDEX_op_setcond_i64: + tcg_out_setcond(s, args[3], args[0], args[1], args[2], 0); + break; + case INDEX_op_movcond_i32: + tcg_out_movcond(s, args[5], args[0], args[1], args[2], + args[3], const_args[3], args[4], const_args[4], 1); + break; + case INDEX_op_movcond_i64: + tcg_out_movcond(s, args[5], args[0], args[1], args[2], + args[3], const_args[3], args[4], const_args[4], 0); + break; + + case INDEX_op_qemu_ld_i32: + tcg_out_qemu_ld(s, args); + break; + case INDEX_op_qemu_ld_i64: + tcg_out_qemu_ld(s, args); + break; + case INDEX_op_qemu_st_i32: + tcg_out_qemu_st(s, args); + break; + case INDEX_op_qemu_st_i64: + tcg_out_qemu_st(s, args); + break; + + case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ + case INDEX_op_mov_i64: + case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ + case INDEX_op_movi_i64: + case INDEX_op_call: /* Always emitted via tcg_out_call. */ + default: + tcg_abort(); + } +} + +static const TCGTargetOpDef ia64_op_defs[] = { + { INDEX_op_br, { } }, + { INDEX_op_exit_tb, { } }, + { INDEX_op_goto_tb, { } }, + + { INDEX_op_ld8u_i32, { "r", "r" } }, + { INDEX_op_ld8s_i32, { "r", "r" } }, + { INDEX_op_ld16u_i32, { "r", "r" } }, + { INDEX_op_ld16s_i32, { "r", "r" } }, + { INDEX_op_ld_i32, { "r", "r" } }, + { INDEX_op_st8_i32, { "rZ", "r" } }, + { INDEX_op_st16_i32, { "rZ", "r" } }, + { INDEX_op_st_i32, { "rZ", "r" } }, + + { INDEX_op_add_i32, { "r", "rZ", "rI" } }, + { INDEX_op_sub_i32, { "r", "rI", "rI" } }, + + { INDEX_op_and_i32, { "r", "rI", "rI" } }, + { INDEX_op_andc_i32, { "r", "rI", "rI" } }, + { INDEX_op_eqv_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_nand_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_nor_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_or_i32, { "r", "rI", "rI" } }, + { INDEX_op_orc_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_xor_i32, { "r", "rI", "rI" } }, + + { INDEX_op_mul_i32, { "r", "rZ", "rZ" } }, + + { INDEX_op_sar_i32, { "r", "rZ", "ri" } }, + { INDEX_op_shl_i32, { "r", "rZ", "ri" } }, + { INDEX_op_shr_i32, { "r", "rZ", "ri" } }, + { INDEX_op_rotl_i32, { "r", "rZ", "ri" } }, + { INDEX_op_rotr_i32, { "r", "rZ", "ri" } }, + + { INDEX_op_ext8s_i32, { "r", "rZ"} }, + { INDEX_op_ext8u_i32, { "r", "rZ"} }, + { INDEX_op_ext16s_i32, { "r", "rZ"} }, + { INDEX_op_ext16u_i32, { "r", "rZ"} }, + + { INDEX_op_bswap16_i32, { "r", "rZ" } }, + { INDEX_op_bswap32_i32, { "r", "rZ" } }, + + { INDEX_op_brcond_i32, { "rZ", "rZ" } }, + { INDEX_op_setcond_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_movcond_i32, { "r", "rZ", "rZ", "rI", "rI" } }, + + { INDEX_op_ld8u_i64, { "r", "r" } }, + { INDEX_op_ld8s_i64, { "r", "r" } }, + { INDEX_op_ld16u_i64, { "r", "r" } }, + { INDEX_op_ld16s_i64, { "r", "r" } }, + { INDEX_op_ld32u_i64, { "r", "r" } }, + { INDEX_op_ld32s_i64, { "r", "r" } }, + { INDEX_op_ld_i64, { "r", "r" } }, + { INDEX_op_st8_i64, { "rZ", "r" } }, + { INDEX_op_st16_i64, { "rZ", "r" } }, + { INDEX_op_st32_i64, { "rZ", "r" } }, + { INDEX_op_st_i64, { "rZ", "r" } }, + + { INDEX_op_add_i64, { "r", "rZ", "rI" } }, + { INDEX_op_sub_i64, { "r", "rI", "rI" } }, + + { INDEX_op_and_i64, { "r", "rI", "rI" } }, + { INDEX_op_andc_i64, { "r", "rI", "rI" } }, + { INDEX_op_eqv_i64, { "r", "rZ", "rZ" } }, + { INDEX_op_nand_i64, { "r", "rZ", "rZ" } }, + { INDEX_op_nor_i64, { "r", "rZ", "rZ" } }, + { INDEX_op_or_i64, { "r", "rI", "rI" } }, + { INDEX_op_orc_i64, { "r", "rZ", "rZ" } }, + { INDEX_op_xor_i64, { "r", "rI", "rI" } }, + + { INDEX_op_mul_i64, { "r", "rZ", "rZ" } }, + + { INDEX_op_sar_i64, { "r", "rZ", "ri" } }, + { INDEX_op_shl_i64, { "r", "rZ", "ri" } }, + { INDEX_op_shr_i64, { "r", "rZ", "ri" } }, + { INDEX_op_rotl_i64, { "r", "rZ", "ri" } }, + { INDEX_op_rotr_i64, { "r", "rZ", "ri" } }, + + { INDEX_op_ext8s_i64, { "r", "rZ"} }, + { INDEX_op_ext8u_i64, { "r", "rZ"} }, + { INDEX_op_ext16s_i64, { "r", "rZ"} }, + { INDEX_op_ext16u_i64, { "r", "rZ"} }, + { INDEX_op_ext32s_i64, { "r", "rZ"} }, + { INDEX_op_ext32u_i64, { "r", "rZ"} }, + { INDEX_op_ext_i32_i64, { "r", "rZ" } }, + { INDEX_op_extu_i32_i64, { "r", "rZ" } }, + + { INDEX_op_bswap16_i64, { "r", "rZ" } }, + { INDEX_op_bswap32_i64, { "r", "rZ" } }, + { INDEX_op_bswap64_i64, { "r", "rZ" } }, + + { INDEX_op_brcond_i64, { "rZ", "rZ" } }, + { INDEX_op_setcond_i64, { "r", "rZ", "rZ" } }, + { INDEX_op_movcond_i64, { "r", "rZ", "rZ", "rI", "rI" } }, + + { INDEX_op_deposit_i32, { "r", "rZ", "ri" } }, + { INDEX_op_deposit_i64, { "r", "rZ", "ri" } }, + + { INDEX_op_qemu_ld_i32, { "r", "r" } }, + { INDEX_op_qemu_ld_i64, { "r", "r" } }, + { INDEX_op_qemu_st_i32, { "SZ", "r" } }, + { INDEX_op_qemu_st_i64, { "SZ", "r" } }, + + { -1 }, +}; + +/* Generate global QEMU prologue and epilogue code */ +static void tcg_target_qemu_prologue(TCGContext *s) +{ + int frame_size; + + /* reserve some stack space */ + frame_size = TCG_STATIC_CALL_ARGS_SIZE + + CPU_TEMP_BUF_NLONGS * sizeof(long); + frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) & + ~(TCG_TARGET_STACK_ALIGN - 1); + tcg_set_frame(s, TCG_REG_CALL_STACK, TCG_STATIC_CALL_ARGS_SIZE, + CPU_TEMP_BUF_NLONGS * sizeof(long)); + + /* First emit adhoc function descriptor */ + *s->code_ptr = (tcg_insn_unit){ + (uint64_t)(s->code_ptr + 1), /* entry point */ + 0 /* skip gp */ + }; + s->code_ptr++; + + /* prologue */ + tcg_out_bundle(s, miI, + tcg_opc_m34(TCG_REG_P0, OPC_ALLOC_M34, + TCG_REG_R34, 32, 24, 0), + INSN_NOP_I, + tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, + TCG_REG_B6, TCG_REG_R33, 0)); + + /* ??? If guest_base < 0x200000, we could load the register via + an ADDL in the M slot of the next bundle. */ + if (guest_base != 0) { + tcg_out_bundle(s, mlx, + INSN_NOP_M, + tcg_opc_l2(guest_base), + tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, + TCG_GUEST_BASE_REG, guest_base)); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + } + + tcg_out_bundle(s, miB, + tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4, + TCG_REG_R12, -frame_size, TCG_REG_R12), + tcg_opc_i22(TCG_REG_P0, OPC_MOV_I22, + TCG_REG_R33, TCG_REG_B0), + tcg_opc_b4 (TCG_REG_P0, OPC_BR_SPTK_MANY_B4, TCG_REG_B6)); + + /* epilogue */ + tb_ret_addr = s->code_ptr; + tcg_out_bundle(s, miI, + INSN_NOP_M, + tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, + TCG_REG_B0, TCG_REG_R33, 0), + tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4, + TCG_REG_R12, frame_size, TCG_REG_R12)); + tcg_out_bundle(s, miB, + INSN_NOP_M, + tcg_opc_i26(TCG_REG_P0, OPC_MOV_I_I26, + TCG_REG_PFS, TCG_REG_R34), + tcg_opc_b4 (TCG_REG_P0, OPC_BR_RET_SPTK_MANY_B4, + TCG_REG_B0)); +} + +static void tcg_target_init(TCGContext *s) +{ + tcg_regset_set(tcg_target_available_regs[TCG_TYPE_I32], + 0xffffffffffffffffull); + tcg_regset_set(tcg_target_available_regs[TCG_TYPE_I64], + 0xffffffffffffffffull); + + tcg_regset_clear(tcg_target_call_clobber_regs); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R8); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R9); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R10); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R11); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R14); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R15); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R16); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R17); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R18); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R19); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R20); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R21); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R22); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R23); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R24); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R25); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R26); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R27); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R28); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R29); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R30); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R31); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R56); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R57); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R58); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R59); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R60); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R61); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R62); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R63); + + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); /* zero register */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R1); /* global pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R2); /* internal use */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R3); /* internal use */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R12); /* stack pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R13); /* thread pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R33); /* return address */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R34); /* PFS */ + + /* The following 4 are not in use, are call-saved, but *not* saved + by the prologue. Therefore we cannot use them without modifying + the prologue. There doesn't seem to be any good reason to use + these as opposed to the windowed registers. */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R4); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R5); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R6); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R7); + + tcg_add_target_add_op_defs(ia64_op_defs); +} diff --git a/qemu/tcg/mips/tcg-target.c b/qemu/tcg/mips/tcg-target.c deleted file mode 100644 index e97980df0..000000000 --- a/qemu/tcg/mips/tcg-target.c +++ /dev/null @@ -1,1824 +0,0 @@ -/* - * Tiny Code Generator for QEMU - * - * Copyright (c) 2008-2009 Arnaud Patard - * Copyright (c) 2009 Aurelien Jarno - * Based on i386/tcg-target.c - Copyright (c) 2008 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "tcg-be-ldst.h" - -#ifdef HOST_WORDS_BIGENDIAN -# define MIPS_BE 1 -#else -# define MIPS_BE 0 -#endif - -#define LO_OFF (MIPS_BE * 4) -#define HI_OFF (4 - LO_OFF) - -#ifndef NDEBUG -static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { - "zero", - "at", - "v0", - "v1", - "a0", - "a1", - "a2", - "a3", - "t0", - "t1", - "t2", - "t3", - "t4", - "t5", - "t6", - "t7", - "s0", - "s1", - "s2", - "s3", - "s4", - "s5", - "s6", - "s7", - "t8", - "t9", - "k0", - "k1", - "gp", - "sp", - "s8", - "ra", -}; -#endif - -#define TCG_TMP0 TCG_REG_AT -#define TCG_TMP1 TCG_REG_T9 - -/* check if we really need so many registers :P */ -static const TCGReg tcg_target_reg_alloc_order[] = { - /* Call saved registers. */ - TCG_REG_S0, - TCG_REG_S1, - TCG_REG_S2, - TCG_REG_S3, - TCG_REG_S4, - TCG_REG_S5, - TCG_REG_S6, - TCG_REG_S7, - TCG_REG_S8, - - /* Call clobbered registers. */ - TCG_REG_T0, - TCG_REG_T1, - TCG_REG_T2, - TCG_REG_T3, - TCG_REG_T4, - TCG_REG_T5, - TCG_REG_T6, - TCG_REG_T7, - TCG_REG_T8, - TCG_REG_T9, - TCG_REG_V1, - TCG_REG_V0, - - /* Argument registers, opposite order of allocation. */ - TCG_REG_A3, - TCG_REG_A2, - TCG_REG_A1, - TCG_REG_A0, -}; - -static const TCGReg tcg_target_call_iarg_regs[4] = { - TCG_REG_A0, - TCG_REG_A1, - TCG_REG_A2, - TCG_REG_A3 -}; - -static const TCGReg tcg_target_call_oarg_regs[2] = { - TCG_REG_V0, - TCG_REG_V1 -}; - -static tcg_insn_unit *tb_ret_addr; - -static inline uint32_t reloc_pc16_val(tcg_insn_unit *pc, tcg_insn_unit *target) -{ - /* Let the compiler perform the right-shift as part of the arithmetic. */ - ptrdiff_t disp = target - (pc + 1); - assert(disp == (int16_t)disp); - return disp & 0xffff; -} - -static inline void reloc_pc16(tcg_insn_unit *pc, tcg_insn_unit *target) -{ - *pc = deposit32(*pc, 0, 16, reloc_pc16_val(pc, target)); -} - -static inline uint32_t reloc_26_val(tcg_insn_unit *pc, tcg_insn_unit *target) -{ - assert((((uintptr_t)pc ^ (uintptr_t)target) & 0xf0000000) == 0); - return ((uintptr_t)target >> 2) & 0x3ffffff; -} - -static inline void reloc_26(tcg_insn_unit *pc, tcg_insn_unit *target) -{ - *pc = deposit32(*pc, 0, 26, reloc_26_val(pc, target)); -} - -static void patch_reloc(tcg_insn_unit *code_ptr, int type, - intptr_t value, intptr_t addend) -{ - assert(type == R_MIPS_PC16); - assert(addend == 0); - reloc_pc16(code_ptr, (tcg_insn_unit *)value); -} - -#define TCG_CT_CONST_ZERO 0x100 -#define TCG_CT_CONST_U16 0x200 /* Unsigned 16-bit: 0 - 0xffff. */ -#define TCG_CT_CONST_S16 0x400 /* Signed 16-bit: -32768 - 32767 */ -#define TCG_CT_CONST_P2M1 0x800 /* Power of 2 minus 1. */ -#define TCG_CT_CONST_N16 0x1000 /* "Negatable" 16-bit: -32767 - 32767 */ - -static inline bool is_p2m1(tcg_target_long val) -{ - return val && ((val + 1) & val) == 0; -} - -/* parse target specific constraints */ -static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) -{ - const char *ct_str; - - ct_str = *pct_str; - switch(ct_str[0]) { - case 'r': - ct->ct |= TCG_CT_REG; - tcg_regset_set(ct->u.regs, 0xffffffff); - break; - case 'L': /* qemu_ld output arg constraint */ - ct->ct |= TCG_CT_REG; - tcg_regset_set(ct->u.regs, 0xffffffff); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_V0); - break; - case 'l': /* qemu_ld input arg constraint */ - ct->ct |= TCG_CT_REG; - tcg_regset_set(ct->u.regs, 0xffffffff); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_A0); -#if defined(CONFIG_SOFTMMU) - if (TARGET_LONG_BITS == 64) { - tcg_regset_reset_reg(ct->u.regs, TCG_REG_A2); - } -#endif - break; - case 'S': /* qemu_st constraint */ - ct->ct |= TCG_CT_REG; - tcg_regset_set(ct->u.regs, 0xffffffff); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_A0); -#if defined(CONFIG_SOFTMMU) - if (TARGET_LONG_BITS == 32) { - tcg_regset_reset_reg(ct->u.regs, TCG_REG_A1); - } else { - tcg_regset_reset_reg(ct->u.regs, TCG_REG_A2); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_A3); - } -#endif - break; - case 'I': - ct->ct |= TCG_CT_CONST_U16; - break; - case 'J': - ct->ct |= TCG_CT_CONST_S16; - break; - case 'K': - ct->ct |= TCG_CT_CONST_P2M1; - break; - case 'N': - ct->ct |= TCG_CT_CONST_N16; - break; - case 'Z': - /* We are cheating a bit here, using the fact that the register - ZERO is also the register number 0. Hence there is no need - to check for const_args in each instruction. */ - ct->ct |= TCG_CT_CONST_ZERO; - break; - default: - return -1; - } - ct_str++; - *pct_str = ct_str; - return 0; -} - -/* test if a constant matches the constraint */ -static inline int tcg_target_const_match(tcg_target_long val, TCGType type, - const TCGArgConstraint *arg_ct) -{ - int ct; - ct = arg_ct->ct; - if (ct & TCG_CT_CONST) { - return 1; - } else if ((ct & TCG_CT_CONST_ZERO) && val == 0) { - return 1; - } else if ((ct & TCG_CT_CONST_U16) && val == (uint16_t)val) { - return 1; - } else if ((ct & TCG_CT_CONST_S16) && val == (int16_t)val) { - return 1; - } else if ((ct & TCG_CT_CONST_N16) && val >= -32767 && val <= 32767) { - return 1; - } else if ((ct & TCG_CT_CONST_P2M1) - && use_mips32r2_instructions && is_p2m1(val)) { - return 1; - } - return 0; -} - -/* instruction opcodes */ -typedef enum { - OPC_J = 0x02 << 26, - OPC_JAL = 0x03 << 26, - OPC_BEQ = 0x04 << 26, - OPC_BNE = 0x05 << 26, - OPC_BLEZ = 0x06 << 26, - OPC_BGTZ = 0x07 << 26, - OPC_ADDIU = 0x09 << 26, - OPC_SLTI = 0x0A << 26, - OPC_SLTIU = 0x0B << 26, - OPC_ANDI = 0x0C << 26, - OPC_ORI = 0x0D << 26, - OPC_XORI = 0x0E << 26, - OPC_LUI = 0x0F << 26, - OPC_LB = 0x20 << 26, - OPC_LH = 0x21 << 26, - OPC_LW = 0x23 << 26, - OPC_LBU = 0x24 << 26, - OPC_LHU = 0x25 << 26, - OPC_LWU = 0x27 << 26, - OPC_SB = 0x28 << 26, - OPC_SH = 0x29 << 26, - OPC_SW = 0x2B << 26, - - OPC_SPECIAL = 0x00 << 26, - OPC_SLL = OPC_SPECIAL | 0x00, - OPC_SRL = OPC_SPECIAL | 0x02, - OPC_ROTR = OPC_SPECIAL | (0x01 << 21) | 0x02, - OPC_SRA = OPC_SPECIAL | 0x03, - OPC_SLLV = OPC_SPECIAL | 0x04, - OPC_SRLV = OPC_SPECIAL | 0x06, - OPC_ROTRV = OPC_SPECIAL | (0x01 << 6) | 0x06, - OPC_SRAV = OPC_SPECIAL | 0x07, - OPC_JR = OPC_SPECIAL | 0x08, - OPC_JALR = OPC_SPECIAL | 0x09, - OPC_MOVZ = OPC_SPECIAL | 0x0A, - OPC_MOVN = OPC_SPECIAL | 0x0B, - OPC_MFHI = OPC_SPECIAL | 0x10, - OPC_MFLO = OPC_SPECIAL | 0x12, - OPC_MULT = OPC_SPECIAL | 0x18, - OPC_MULTU = OPC_SPECIAL | 0x19, - OPC_DIV = OPC_SPECIAL | 0x1A, - OPC_DIVU = OPC_SPECIAL | 0x1B, - OPC_ADDU = OPC_SPECIAL | 0x21, - OPC_SUBU = OPC_SPECIAL | 0x23, - OPC_AND = OPC_SPECIAL | 0x24, - OPC_OR = OPC_SPECIAL | 0x25, - OPC_XOR = OPC_SPECIAL | 0x26, - OPC_NOR = OPC_SPECIAL | 0x27, - OPC_SLT = OPC_SPECIAL | 0x2A, - OPC_SLTU = OPC_SPECIAL | 0x2B, - - OPC_REGIMM = 0x01 << 26, - OPC_BLTZ = OPC_REGIMM | (0x00 << 16), - OPC_BGEZ = OPC_REGIMM | (0x01 << 16), - - OPC_SPECIAL2 = 0x1c << 26, - OPC_MUL = OPC_SPECIAL2 | 0x002, - - OPC_SPECIAL3 = 0x1f << 26, - OPC_EXT = OPC_SPECIAL3 | 0x000, - OPC_INS = OPC_SPECIAL3 | 0x004, - OPC_WSBH = OPC_SPECIAL3 | 0x0a0, - OPC_SEB = OPC_SPECIAL3 | 0x420, - OPC_SEH = OPC_SPECIAL3 | 0x620, -} MIPSInsn; - -/* - * Type reg - */ -static inline void tcg_out_opc_reg(TCGContext *s, MIPSInsn opc, - TCGReg rd, TCGReg rs, TCGReg rt) -{ - int32_t inst; - - inst = opc; - inst |= (rs & 0x1F) << 21; - inst |= (rt & 0x1F) << 16; - inst |= (rd & 0x1F) << 11; - tcg_out32(s, inst); -} - -/* - * Type immediate - */ -static inline void tcg_out_opc_imm(TCGContext *s, MIPSInsn opc, - TCGReg rt, TCGReg rs, TCGArg imm) -{ - int32_t inst; - - inst = opc; - inst |= (rs & 0x1F) << 21; - inst |= (rt & 0x1F) << 16; - inst |= (imm & 0xffff); - tcg_out32(s, inst); -} - -/* - * Type bitfield - */ -static inline void tcg_out_opc_bf(TCGContext *s, MIPSInsn opc, TCGReg rt, - TCGReg rs, int msb, int lsb) -{ - int32_t inst; - - inst = opc; - inst |= (rs & 0x1F) << 21; - inst |= (rt & 0x1F) << 16; - inst |= (msb & 0x1F) << 11; - inst |= (lsb & 0x1F) << 6; - tcg_out32(s, inst); -} - -/* - * Type branch - */ -static inline void tcg_out_opc_br(TCGContext *s, MIPSInsn opc, - TCGReg rt, TCGReg rs) -{ - /* We pay attention here to not modify the branch target by reading - the existing value and using it again. This ensure that caches and - memory are kept coherent during retranslation. */ - uint16_t offset = (uint16_t)*s->code_ptr; - - tcg_out_opc_imm(s, opc, rt, rs, offset); -} - -/* - * Type sa - */ -static inline void tcg_out_opc_sa(TCGContext *s, MIPSInsn opc, - TCGReg rd, TCGReg rt, TCGArg sa) -{ - int32_t inst; - - inst = opc; - inst |= (rt & 0x1F) << 16; - inst |= (rd & 0x1F) << 11; - inst |= (sa & 0x1F) << 6; - tcg_out32(s, inst); - -} - -/* - * Type jump. - * Returns true if the branch was in range and the insn was emitted. - */ -static bool tcg_out_opc_jmp(TCGContext *s, MIPSInsn opc, void *target) -{ - uintptr_t dest = (uintptr_t)target; - uintptr_t from = (uintptr_t)s->code_ptr + 4; - int32_t inst; - - /* The pc-region branch happens within the 256MB region of - the delay slot (thus the +4). */ - if ((from ^ dest) & -(1 << 28)) { - return false; - } - assert((dest & 3) == 0); - - inst = opc; - inst |= (dest >> 2) & 0x3ffffff; - tcg_out32(s, inst); - return true; -} - -static inline void tcg_out_nop(TCGContext *s) -{ - tcg_out32(s, 0); -} - -static inline void tcg_out_mov(TCGContext *s, TCGType type, - TCGReg ret, TCGReg arg) -{ - /* Simple reg-reg move, optimising out the 'do nothing' case */ - if (ret != arg) { - tcg_out_opc_reg(s, OPC_ADDU, ret, arg, TCG_REG_ZERO); - } -} - -static inline void tcg_out_movi(TCGContext *s, TCGType type, - TCGReg reg, tcg_target_long arg) -{ - if (arg == (int16_t)arg) { - tcg_out_opc_imm(s, OPC_ADDIU, reg, TCG_REG_ZERO, arg); - } else if (arg == (uint16_t)arg) { - tcg_out_opc_imm(s, OPC_ORI, reg, TCG_REG_ZERO, arg); - } else { - tcg_out_opc_imm(s, OPC_LUI, reg, TCG_REG_ZERO, arg >> 16); - if (arg & 0xffff) { - tcg_out_opc_imm(s, OPC_ORI, reg, reg, arg & 0xffff); - } - } -} - -static inline void tcg_out_bswap16(TCGContext *s, TCGReg ret, TCGReg arg) -{ - if (use_mips32r2_instructions) { - tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); - } else { - /* ret and arg can't be register at */ - if (ret == TCG_TMP0 || arg == TCG_TMP0) { - tcg_abort(); - } - - tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, arg, 8); - tcg_out_opc_sa(s, OPC_SLL, ret, arg, 8); - tcg_out_opc_imm(s, OPC_ANDI, ret, ret, 0xff00); - tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP0); - } -} - -static inline void tcg_out_bswap16s(TCGContext *s, TCGReg ret, TCGReg arg) -{ - if (use_mips32r2_instructions) { - tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); - tcg_out_opc_reg(s, OPC_SEH, ret, 0, ret); - } else { - /* ret and arg can't be register at */ - if (ret == TCG_TMP0 || arg == TCG_TMP0) { - tcg_abort(); - } - - tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, arg, 8); - tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); - tcg_out_opc_sa(s, OPC_SRA, ret, ret, 16); - tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP0); - } -} - -static inline void tcg_out_bswap32(TCGContext *s, TCGReg ret, TCGReg arg) -{ - if (use_mips32r2_instructions) { - tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); - tcg_out_opc_sa(s, OPC_ROTR, ret, ret, 16); - } else { - /* ret and arg must be different and can't be register at */ - if (ret == arg || ret == TCG_TMP0 || arg == TCG_TMP0) { - tcg_abort(); - } - - tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); - - tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, arg, 24); - tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP0); - - tcg_out_opc_imm(s, OPC_ANDI, TCG_TMP0, arg, 0xff00); - tcg_out_opc_sa(s, OPC_SLL, TCG_TMP0, TCG_TMP0, 8); - tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP0); - - tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, arg, 8); - tcg_out_opc_imm(s, OPC_ANDI, TCG_TMP0, TCG_TMP0, 0xff00); - tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP0); - } -} - -static inline void tcg_out_ext8s(TCGContext *s, TCGReg ret, TCGReg arg) -{ - if (use_mips32r2_instructions) { - tcg_out_opc_reg(s, OPC_SEB, ret, 0, arg); - } else { - tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); - tcg_out_opc_sa(s, OPC_SRA, ret, ret, 24); - } -} - -static inline void tcg_out_ext16s(TCGContext *s, TCGReg ret, TCGReg arg) -{ - if (use_mips32r2_instructions) { - tcg_out_opc_reg(s, OPC_SEH, ret, 0, arg); - } else { - tcg_out_opc_sa(s, OPC_SLL, ret, arg, 16); - tcg_out_opc_sa(s, OPC_SRA, ret, ret, 16); - } -} - -static void tcg_out_ldst(TCGContext *s, MIPSInsn opc, TCGReg data, - TCGReg addr, intptr_t ofs) -{ - int16_t lo = ofs; - if (ofs != lo) { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, ofs - lo); - if (addr != TCG_REG_ZERO) { - tcg_out_opc_reg(s, OPC_ADDU, TCG_TMP0, TCG_TMP0, addr); - } - addr = TCG_TMP0; - } - tcg_out_opc_imm(s, opc, data, addr, lo); -} - -static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg arg, - TCGReg arg1, intptr_t arg2) -{ - tcg_out_ldst(s, OPC_LW, arg, arg1, arg2); -} - -static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, - TCGReg arg1, intptr_t arg2) -{ - tcg_out_ldst(s, OPC_SW, arg, arg1, arg2); -} - -static inline void tcg_out_addi(TCGContext *s, TCGReg reg, TCGArg val) -{ - if (val == (int16_t)val) { - tcg_out_opc_imm(s, OPC_ADDIU, reg, reg, val); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, val); - tcg_out_opc_reg(s, OPC_ADDU, reg, reg, TCG_TMP0); - } -} - -/* Bit 0 set if inversion required; bit 1 set if swapping required. */ -#define MIPS_CMP_INV 1 -#define MIPS_CMP_SWAP 2 - -static const uint8_t mips_cmp_map[16] = { - [TCG_COND_LT] = 0, - [TCG_COND_LTU] = 0, - [TCG_COND_GE] = MIPS_CMP_INV, - [TCG_COND_GEU] = MIPS_CMP_INV, - [TCG_COND_LE] = MIPS_CMP_INV | MIPS_CMP_SWAP, - [TCG_COND_LEU] = MIPS_CMP_INV | MIPS_CMP_SWAP, - [TCG_COND_GT] = MIPS_CMP_SWAP, - [TCG_COND_GTU] = MIPS_CMP_SWAP, -}; - -static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg arg1, TCGReg arg2) -{ - MIPSInsn s_opc = OPC_SLTU; - int cmp_map; - - switch (cond) { - case TCG_COND_EQ: - if (arg2 != 0) { - tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2); - arg1 = ret; - } - tcg_out_opc_imm(s, OPC_SLTIU, ret, arg1, 1); - break; - - case TCG_COND_NE: - if (arg2 != 0) { - tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2); - arg1 = ret; - } - tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, arg1); - break; - - case TCG_COND_LT: - case TCG_COND_GE: - case TCG_COND_LE: - case TCG_COND_GT: - s_opc = OPC_SLT; - /* FALLTHRU */ - - case TCG_COND_LTU: - case TCG_COND_GEU: - case TCG_COND_LEU: - case TCG_COND_GTU: - cmp_map = mips_cmp_map[cond]; - if (cmp_map & MIPS_CMP_SWAP) { - TCGReg t = arg1; - arg1 = arg2; - arg2 = t; - } - tcg_out_opc_reg(s, s_opc, ret, arg1, arg2); - if (cmp_map & MIPS_CMP_INV) { - tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); - } - break; - - default: - tcg_abort(); - break; - } -} - -static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, - TCGReg arg2, TCGLabel *l) -{ - static const MIPSInsn b_zero[16] = { - [TCG_COND_LT] = OPC_BLTZ, - [TCG_COND_GT] = OPC_BGTZ, - [TCG_COND_LE] = OPC_BLEZ, - [TCG_COND_GE] = OPC_BGEZ, - }; - - MIPSInsn s_opc = OPC_SLTU; - MIPSInsn b_opc; - int cmp_map; - - switch (cond) { - case TCG_COND_EQ: - b_opc = OPC_BEQ; - break; - case TCG_COND_NE: - b_opc = OPC_BNE; - break; - - case TCG_COND_LT: - case TCG_COND_GT: - case TCG_COND_LE: - case TCG_COND_GE: - if (arg2 == 0) { - b_opc = b_zero[cond]; - arg2 = arg1; - arg1 = 0; - break; - } - s_opc = OPC_SLT; - /* FALLTHRU */ - - case TCG_COND_LTU: - case TCG_COND_GTU: - case TCG_COND_LEU: - case TCG_COND_GEU: - cmp_map = mips_cmp_map[cond]; - if (cmp_map & MIPS_CMP_SWAP) { - TCGReg t = arg1; - arg1 = arg2; - arg2 = t; - } - tcg_out_opc_reg(s, s_opc, TCG_TMP0, arg1, arg2); - b_opc = (cmp_map & MIPS_CMP_INV ? OPC_BEQ : OPC_BNE); - arg1 = TCG_TMP0; - arg2 = TCG_REG_ZERO; - break; - - default: - tcg_abort(); - break; - } - - tcg_out_opc_br(s, b_opc, arg1, arg2); - if (l->has_value) { - reloc_pc16(s->code_ptr - 1, l->u.value_ptr); - } else { - tcg_out_reloc(s, s->code_ptr - 1, R_MIPS_PC16, l, 0); - } - tcg_out_nop(s); -} - -static TCGReg tcg_out_reduce_eq2(TCGContext *s, TCGReg tmp0, TCGReg tmp1, - TCGReg al, TCGReg ah, - TCGReg bl, TCGReg bh) -{ - /* Merge highpart comparison into AH. */ - if (bh != 0) { - if (ah != 0) { - tcg_out_opc_reg(s, OPC_XOR, tmp0, ah, bh); - ah = tmp0; - } else { - ah = bh; - } - } - /* Merge lowpart comparison into AL. */ - if (bl != 0) { - if (al != 0) { - tcg_out_opc_reg(s, OPC_XOR, tmp1, al, bl); - al = tmp1; - } else { - al = bl; - } - } - /* Merge high and low part comparisons into AL. */ - if (ah != 0) { - if (al != 0) { - tcg_out_opc_reg(s, OPC_OR, tmp0, ah, al); - al = tmp0; - } else { - al = ah; - } - } - return al; -} - -static void tcg_out_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh) -{ - TCGReg tmp0 = TCG_TMP0; - TCGReg tmp1 = ret; - - assert(ret != TCG_TMP0); - if (ret == ah || ret == bh) { - assert(ret != TCG_TMP1); - tmp1 = TCG_TMP1; - } - - switch (cond) { - case TCG_COND_EQ: - case TCG_COND_NE: - tmp1 = tcg_out_reduce_eq2(s, tmp0, tmp1, al, ah, bl, bh); - tcg_out_setcond(s, cond, ret, tmp1, TCG_REG_ZERO); - break; - - default: - tcg_out_setcond(s, TCG_COND_EQ, tmp0, ah, bh); - tcg_out_setcond(s, tcg_unsigned_cond(cond), tmp1, al, bl); - tcg_out_opc_reg(s, OPC_AND, tmp1, tmp1, tmp0); - tcg_out_setcond(s, tcg_high_cond(cond), tmp0, ah, bh); - tcg_out_opc_reg(s, OPC_OR, ret, tmp1, tmp0); - break; - } -} - -static void tcg_out_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, - TCGReg bl, TCGReg bh, TCGLabel *l) -{ - TCGCond b_cond = TCG_COND_NE; - TCGReg tmp = TCG_TMP1; - - /* With branches, we emit between 4 and 9 insns with 2 or 3 branches. - With setcond, we emit between 3 and 10 insns and only 1 branch, - which ought to get better branch prediction. */ - switch (cond) { - case TCG_COND_EQ: - case TCG_COND_NE: - b_cond = cond; - tmp = tcg_out_reduce_eq2(s, TCG_TMP0, TCG_TMP1, al, ah, bl, bh); - break; - - default: - /* Minimize code size by preferring a compare not requiring INV. */ - if (mips_cmp_map[cond] & MIPS_CMP_INV) { - cond = tcg_invert_cond(cond); - b_cond = TCG_COND_EQ; - } - tcg_out_setcond2(s, cond, tmp, al, ah, bl, bh); - break; - } - - tcg_out_brcond(s, b_cond, tmp, TCG_REG_ZERO, l); -} - -static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg c1, TCGReg c2, TCGReg v) -{ - MIPSInsn m_opc = OPC_MOVN; - - switch (cond) { - case TCG_COND_EQ: - m_opc = OPC_MOVZ; - /* FALLTHRU */ - case TCG_COND_NE: - if (c2 != 0) { - tcg_out_opc_reg(s, OPC_XOR, TCG_TMP0, c1, c2); - c1 = TCG_TMP0; - } - break; - - default: - /* Minimize code size by preferring a compare not requiring INV. */ - if (mips_cmp_map[cond] & MIPS_CMP_INV) { - cond = tcg_invert_cond(cond); - m_opc = OPC_MOVZ; - } - tcg_out_setcond(s, cond, TCG_TMP0, c1, c2); - c1 = TCG_TMP0; - break; - } - - tcg_out_opc_reg(s, m_opc, ret, v, c1); -} - -static void tcg_out_call_int(TCGContext *s, tcg_insn_unit *arg, bool tail) -{ - /* Note that the ABI requires the called function's address to be - loaded into T9, even if a direct branch is in range. */ - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_T9, (uintptr_t)arg); - - /* But do try a direct branch, allowing the cpu better insn prefetch. */ - if (tail) { - if (!tcg_out_opc_jmp(s, OPC_J, arg)) { - tcg_out_opc_reg(s, OPC_JR, 0, TCG_REG_T9, 0); - } - } else { - if (!tcg_out_opc_jmp(s, OPC_JAL, arg)) { - tcg_out_opc_reg(s, OPC_JALR, TCG_REG_RA, TCG_REG_T9, 0); - } - } -} - -static void tcg_out_call(TCGContext *s, tcg_insn_unit *arg) -{ - tcg_out_call_int(s, arg, false); - tcg_out_nop(s); -} - -#if defined(CONFIG_SOFTMMU) -static void * const qemu_ld_helpers[16] = { - [MO_UB] = helper_ret_ldub_mmu, - [MO_SB] = helper_ret_ldsb_mmu, - [MO_LEUW] = helper_le_lduw_mmu, - [MO_LESW] = helper_le_ldsw_mmu, - [MO_LEUL] = helper_le_ldul_mmu, - [MO_LEQ] = helper_le_ldq_mmu, - [MO_BEUW] = helper_be_lduw_mmu, - [MO_BESW] = helper_be_ldsw_mmu, - [MO_BEUL] = helper_be_ldul_mmu, - [MO_BEQ] = helper_be_ldq_mmu, -}; - -static void * const qemu_st_helpers[16] = { - [MO_UB] = helper_ret_stb_mmu, - [MO_LEUW] = helper_le_stw_mmu, - [MO_LEUL] = helper_le_stl_mmu, - [MO_LEQ] = helper_le_stq_mmu, - [MO_BEUW] = helper_be_stw_mmu, - [MO_BEUL] = helper_be_stl_mmu, - [MO_BEQ] = helper_be_stq_mmu, -}; - -/* Helper routines for marshalling helper function arguments into - * the correct registers and stack. - * I is where we want to put this argument, and is updated and returned - * for the next call. ARG is the argument itself. - * - * We provide routines for arguments which are: immediate, 32 bit - * value in register, 16 and 8 bit values in register (which must be zero - * extended before use) and 64 bit value in a lo:hi register pair. - */ - -static int tcg_out_call_iarg_reg(TCGContext *s, int i, TCGReg arg) -{ - if (i < ARRAY_SIZE(tcg_target_call_iarg_regs)) { - tcg_out_mov(s, TCG_TYPE_REG, tcg_target_call_iarg_regs[i], arg); - } else { - tcg_out_st(s, TCG_TYPE_REG, arg, TCG_REG_SP, 4 * i); - } - return i + 1; -} - -static int tcg_out_call_iarg_reg8(TCGContext *s, int i, TCGReg arg) -{ - TCGReg tmp = TCG_TMP0; - if (i < ARRAY_SIZE(tcg_target_call_iarg_regs)) { - tmp = tcg_target_call_iarg_regs[i]; - } - tcg_out_opc_imm(s, OPC_ANDI, tmp, arg, 0xff); - return tcg_out_call_iarg_reg(s, i, tmp); -} - -static int tcg_out_call_iarg_reg16(TCGContext *s, int i, TCGReg arg) -{ - TCGReg tmp = TCG_TMP0; - if (i < ARRAY_SIZE(tcg_target_call_iarg_regs)) { - tmp = tcg_target_call_iarg_regs[i]; - } - tcg_out_opc_imm(s, OPC_ANDI, tmp, arg, 0xffff); - return tcg_out_call_iarg_reg(s, i, tmp); -} - -static int tcg_out_call_iarg_imm(TCGContext *s, int i, TCGArg arg) -{ - TCGReg tmp = TCG_TMP0; - if (arg == 0) { - tmp = TCG_REG_ZERO; - } else { - if (i < ARRAY_SIZE(tcg_target_call_iarg_regs)) { - tmp = tcg_target_call_iarg_regs[i]; - } - tcg_out_movi(s, TCG_TYPE_REG, tmp, arg); - } - return tcg_out_call_iarg_reg(s, i, tmp); -} - -static int tcg_out_call_iarg_reg2(TCGContext *s, int i, TCGReg al, TCGReg ah) -{ - i = (i + 1) & ~1; - i = tcg_out_call_iarg_reg(s, i, (MIPS_BE ? ah : al)); - i = tcg_out_call_iarg_reg(s, i, (MIPS_BE ? al : ah)); - return i; -} - -/* Perform the tlb comparison operation. The complete host address is - placed in BASE. Clobbers AT, T0, A0. */ -static void tcg_out_tlb_load(TCGContext *s, TCGReg base, TCGReg addrl, - TCGReg addrh, int mem_index, TCGMemOp s_bits, - tcg_insn_unit *label_ptr[2], bool is_load) -{ - int cmp_off - = (is_load - ? offsetof(CPUArchState, tlb_table[mem_index][0].addr_read) - : offsetof(CPUArchState, tlb_table[mem_index][0].addr_write)); - int add_off = offsetof(CPUArchState, tlb_table[mem_index][0].addend); - - tcg_out_opc_sa(s, OPC_SRL, TCG_REG_A0, addrl, - TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_A0, TCG_REG_A0, - (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS); - tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_A0, TCG_REG_A0, TCG_AREG0); - - /* Compensate for very large offsets. */ - if (add_off >= 0x8000) { - /* Most target env are smaller than 32k; none are larger than 64k. - Simplify the logic here merely to offset by 0x7ff0, giving us a - range just shy of 64k. Check this assumption. */ - QEMU_BUILD_BUG_ON(offsetof(CPUArchState, - tlb_table[NB_MMU_MODES - 1][1]) - > 0x7ff0 + 0x7fff); - tcg_out_opc_imm(s, OPC_ADDIU, TCG_REG_A0, TCG_REG_A0, 0x7ff0); - cmp_off -= 0x7ff0; - add_off -= 0x7ff0; - } - - /* Load the tlb comparator. */ - if (TARGET_LONG_BITS == 64) { - tcg_out_opc_imm(s, OPC_LW, TCG_TMP0, TCG_REG_A0, cmp_off + LO_OFF); - tcg_out_opc_imm(s, OPC_LW, base, TCG_REG_A0, cmp_off + HI_OFF); - } else { - tcg_out_opc_imm(s, OPC_LW, TCG_TMP0, TCG_REG_A0, cmp_off); - } - - /* Mask the page bits, keeping the alignment bits to compare against. - In between, load the tlb addend for the fast path. */ - tcg_out_movi(s, TCG_TYPE_I32, TCG_TMP1, - TARGET_PAGE_MASK | ((1 << s_bits) - 1)); - tcg_out_opc_imm(s, OPC_LW, TCG_REG_A0, TCG_REG_A0, add_off); - tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, addrl); - - label_ptr[0] = s->code_ptr; - tcg_out_opc_br(s, OPC_BNE, TCG_TMP1, TCG_TMP0); - - if (TARGET_LONG_BITS == 64) { - /* delay slot */ - tcg_out_nop(s); - - label_ptr[1] = s->code_ptr; - tcg_out_opc_br(s, OPC_BNE, addrh, base); - } - - /* delay slot */ - tcg_out_opc_reg(s, OPC_ADDU, base, TCG_REG_A0, addrl); -} - -static void add_qemu_ldst_label(TCGContext *s, int is_ld, TCGMemOpIdx oi, - TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - void *raddr, tcg_insn_unit *label_ptr[2]) -{ - TCGLabelQemuLdst *label = new_ldst_label(s); - - label->is_ld = is_ld; - label->oi = oi; - label->datalo_reg = datalo; - label->datahi_reg = datahi; - label->addrlo_reg = addrlo; - label->addrhi_reg = addrhi; - label->raddr = raddr; - label->label_ptr[0] = label_ptr[0]; - if (TARGET_LONG_BITS == 64) { - label->label_ptr[1] = label_ptr[1]; - } -} - -static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) -{ - TCGMemOpIdx oi = l->oi; - TCGMemOp opc = get_memop(oi); - TCGReg v0; - int i; - - /* resolve label address */ - reloc_pc16(l->label_ptr[0], s->code_ptr); - if (TARGET_LONG_BITS == 64) { - reloc_pc16(l->label_ptr[1], s->code_ptr); - } - - i = 1; - if (TARGET_LONG_BITS == 64) { - i = tcg_out_call_iarg_reg2(s, i, l->addrlo_reg, l->addrhi_reg); - } else { - i = tcg_out_call_iarg_reg(s, i, l->addrlo_reg); - } - i = tcg_out_call_iarg_imm(s, i, oi); - i = tcg_out_call_iarg_imm(s, i, (intptr_t)l->raddr); - tcg_out_call_int(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SSIZE)], false); - /* delay slot */ - tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0); - - v0 = l->datalo_reg; - if ((opc & MO_SIZE) == MO_64) { - /* We eliminated V0 from the possible output registers, so it - cannot be clobbered here. So we must move V1 first. */ - if (MIPS_BE) { - tcg_out_mov(s, TCG_TYPE_I32, v0, TCG_REG_V1); - v0 = l->datahi_reg; - } else { - tcg_out_mov(s, TCG_TYPE_I32, l->datahi_reg, TCG_REG_V1); - } - } - - reloc_pc16(s->code_ptr, l->raddr); - tcg_out_opc_br(s, OPC_BEQ, TCG_REG_ZERO, TCG_REG_ZERO); - /* delay slot */ - tcg_out_mov(s, TCG_TYPE_REG, v0, TCG_REG_V0); -} - -static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) -{ - TCGMemOpIdx oi = l->oi; - TCGMemOp opc = get_memop(oi); - TCGMemOp s_bits = opc & MO_SIZE; - int i; - - /* resolve label address */ - reloc_pc16(l->label_ptr[0], s->code_ptr); - if (TARGET_LONG_BITS == 64) { - reloc_pc16(l->label_ptr[1], s->code_ptr); - } - - i = 1; - if (TARGET_LONG_BITS == 64) { - i = tcg_out_call_iarg_reg2(s, i, l->addrlo_reg, l->addrhi_reg); - } else { - i = tcg_out_call_iarg_reg(s, i, l->addrlo_reg); - } - switch (s_bits) { - case MO_8: - i = tcg_out_call_iarg_reg8(s, i, l->datalo_reg); - break; - case MO_16: - i = tcg_out_call_iarg_reg16(s, i, l->datalo_reg); - break; - case MO_32: - i = tcg_out_call_iarg_reg(s, i, l->datalo_reg); - break; - case MO_64: - i = tcg_out_call_iarg_reg2(s, i, l->datalo_reg, l->datahi_reg); - break; - default: - tcg_abort(); - } - i = tcg_out_call_iarg_imm(s, i, oi); - - /* Tail call to the store helper. Thus force the return address - computation to take place in the return address register. */ - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RA, (intptr_t)l->raddr); - i = tcg_out_call_iarg_reg(s, i, TCG_REG_RA); - tcg_out_call_int(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)], true); - /* delay slot */ - tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0); -} -#endif - -static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg base, TCGMemOp opc) -{ - switch (opc & (MO_SSIZE | MO_BSWAP)) { - case MO_UB: - tcg_out_opc_imm(s, OPC_LBU, datalo, base, 0); - break; - case MO_SB: - tcg_out_opc_imm(s, OPC_LB, datalo, base, 0); - break; - case MO_UW | MO_BSWAP: - tcg_out_opc_imm(s, OPC_LHU, TCG_TMP1, base, 0); - tcg_out_bswap16(s, datalo, TCG_TMP1); - break; - case MO_UW: - tcg_out_opc_imm(s, OPC_LHU, datalo, base, 0); - break; - case MO_SW | MO_BSWAP: - tcg_out_opc_imm(s, OPC_LHU, TCG_TMP1, base, 0); - tcg_out_bswap16s(s, datalo, TCG_TMP1); - break; - case MO_SW: - tcg_out_opc_imm(s, OPC_LH, datalo, base, 0); - break; - case MO_UL | MO_BSWAP: - tcg_out_opc_imm(s, OPC_LW, TCG_TMP1, base, 0); - tcg_out_bswap32(s, datalo, TCG_TMP1); - break; - case MO_UL: - tcg_out_opc_imm(s, OPC_LW, datalo, base, 0); - break; - case MO_Q | MO_BSWAP: - tcg_out_opc_imm(s, OPC_LW, TCG_TMP1, base, HI_OFF); - tcg_out_bswap32(s, datalo, TCG_TMP1); - tcg_out_opc_imm(s, OPC_LW, TCG_TMP1, base, LO_OFF); - tcg_out_bswap32(s, datahi, TCG_TMP1); - break; - case MO_Q: - tcg_out_opc_imm(s, OPC_LW, datalo, base, LO_OFF); - tcg_out_opc_imm(s, OPC_LW, datahi, base, HI_OFF); - break; - default: - tcg_abort(); - } -} - -static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) -{ - TCGReg addr_regl, addr_regh __attribute__((unused)); - TCGReg data_regl, data_regh; - TCGMemOpIdx oi; - TCGMemOp opc; -#if defined(CONFIG_SOFTMMU) - tcg_insn_unit *label_ptr[2]; - int mem_index; - TCGMemOp s_bits; -#endif - /* Note that we've eliminated V0 from the output registers, - so we won't overwrite the base register during loading. */ - TCGReg base = TCG_REG_V0; - - data_regl = *args++; - data_regh = (is_64 ? *args++ : 0); - addr_regl = *args++; - addr_regh = (TARGET_LONG_BITS == 64 ? *args++ : 0); - oi = *args++; - opc = get_memop(oi); - -#if defined(CONFIG_SOFTMMU) - mem_index = get_mmuidx(oi); - s_bits = opc & MO_SIZE; - - tcg_out_tlb_load(s, base, addr_regl, addr_regh, mem_index, - s_bits, label_ptr, 1); - tcg_out_qemu_ld_direct(s, data_regl, data_regh, base, opc); - add_qemu_ldst_label(s, 1, oi, data_regl, data_regh, addr_regl, addr_regh, - s->code_ptr, label_ptr); -#else - if (GUEST_BASE == 0 && data_regl != addr_regl) { - base = addr_regl; - } else if (GUEST_BASE == (int16_t)GUEST_BASE) { - tcg_out_opc_imm(s, OPC_ADDIU, base, addr_regl, GUEST_BASE); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, base, GUEST_BASE); - tcg_out_opc_reg(s, OPC_ADDU, base, base, addr_regl); - } - tcg_out_qemu_ld_direct(s, data_regl, data_regh, base, opc); -#endif -} - -static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg base, TCGMemOp opc) -{ - switch (opc & (MO_SIZE | MO_BSWAP)) { - case MO_8: - tcg_out_opc_imm(s, OPC_SB, datalo, base, 0); - break; - - case MO_16 | MO_BSWAP: - tcg_out_opc_imm(s, OPC_ANDI, TCG_TMP1, datalo, 0xffff); - tcg_out_bswap16(s, TCG_TMP1, TCG_TMP1); - datalo = TCG_TMP1; - /* FALLTHRU */ - case MO_16: - tcg_out_opc_imm(s, OPC_SH, datalo, base, 0); - break; - - case MO_32 | MO_BSWAP: - tcg_out_bswap32(s, TCG_TMP1, datalo); - datalo = TCG_TMP1; - /* FALLTHRU */ - case MO_32: - tcg_out_opc_imm(s, OPC_SW, datalo, base, 0); - break; - - case MO_64 | MO_BSWAP: - tcg_out_bswap32(s, TCG_TMP1, datalo); - tcg_out_opc_imm(s, OPC_SW, TCG_TMP1, base, HI_OFF); - tcg_out_bswap32(s, TCG_TMP1, datahi); - tcg_out_opc_imm(s, OPC_SW, TCG_TMP1, base, LO_OFF); - break; - case MO_64: - tcg_out_opc_imm(s, OPC_SW, datalo, base, LO_OFF); - tcg_out_opc_imm(s, OPC_SW, datahi, base, HI_OFF); - break; - - default: - tcg_abort(); - } -} - -static void tcg_out_addsub2(TCGContext *s, TCGReg rl, TCGReg rh, TCGReg al, - TCGReg ah, TCGArg bl, TCGArg bh, bool cbl, - bool cbh, bool is_sub) -{ - TCGReg th = TCG_TMP1; - - /* If we have a negative constant such that negating it would - make the high part zero, we can (usually) eliminate one insn. */ - if (cbl && cbh && bh == -1 && bl != 0) { - bl = -bl; - bh = 0; - is_sub = !is_sub; - } - - /* By operating on the high part first, we get to use the final - carry operation to move back from the temporary. */ - if (!cbh) { - tcg_out_opc_reg(s, (is_sub ? OPC_SUBU : OPC_ADDU), th, ah, bh); - } else if (bh != 0 || ah == rl) { - tcg_out_opc_imm(s, OPC_ADDIU, th, ah, (is_sub ? -bh : bh)); - } else { - th = ah; - } - - /* Note that tcg optimization should eliminate the bl == 0 case. */ - if (is_sub) { - if (cbl) { - tcg_out_opc_imm(s, OPC_SLTIU, TCG_TMP0, al, bl); - tcg_out_opc_imm(s, OPC_ADDIU, rl, al, -bl); - } else { - tcg_out_opc_reg(s, OPC_SLTU, TCG_TMP0, al, bl); - tcg_out_opc_reg(s, OPC_SUBU, rl, al, bl); - } - tcg_out_opc_reg(s, OPC_SUBU, rh, th, TCG_TMP0); - } else { - if (cbl) { - tcg_out_opc_imm(s, OPC_ADDIU, rl, al, bl); - tcg_out_opc_imm(s, OPC_SLTIU, TCG_TMP0, rl, bl); - } else if (rl == al && rl == bl) { - tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, al, 31); - tcg_out_opc_reg(s, OPC_ADDU, rl, al, bl); - } else { - tcg_out_opc_reg(s, OPC_ADDU, rl, al, bl); - tcg_out_opc_reg(s, OPC_SLTU, TCG_TMP0, rl, (rl == bl ? al : bl)); - } - tcg_out_opc_reg(s, OPC_ADDU, rh, th, TCG_TMP0); - } -} - -static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) -{ - TCGReg addr_regl, addr_regh __attribute__((unused)); - TCGReg data_regl, data_regh, base; - TCGMemOpIdx oi; - TCGMemOp opc; -#if defined(CONFIG_SOFTMMU) - tcg_insn_unit *label_ptr[2]; - int mem_index; - TCGMemOp s_bits; -#endif - - data_regl = *args++; - data_regh = (is_64 ? *args++ : 0); - addr_regl = *args++; - addr_regh = (TARGET_LONG_BITS == 64 ? *args++ : 0); - oi = *args++; - opc = get_memop(oi); - -#if defined(CONFIG_SOFTMMU) - mem_index = get_mmuidx(oi); - s_bits = opc & 3; - - /* Note that we eliminated the helper's address argument, - so we can reuse that for the base. */ - base = (TARGET_LONG_BITS == 32 ? TCG_REG_A1 : TCG_REG_A2); - tcg_out_tlb_load(s, base, addr_regl, addr_regh, mem_index, - s_bits, label_ptr, 0); - tcg_out_qemu_st_direct(s, data_regl, data_regh, base, opc); - add_qemu_ldst_label(s, 0, oi, data_regl, data_regh, addr_regl, addr_regh, - s->code_ptr, label_ptr); -#else - if (GUEST_BASE == 0) { - base = addr_regl; - } else { - base = TCG_REG_A0; - if (GUEST_BASE == (int16_t)GUEST_BASE) { - tcg_out_opc_imm(s, OPC_ADDIU, base, addr_regl, GUEST_BASE); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, base, GUEST_BASE); - tcg_out_opc_reg(s, OPC_ADDU, base, base, addr_regl); - } - } - tcg_out_qemu_st_direct(s, data_regl, data_regh, base, opc); -#endif -} - -static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, - const TCGArg *args, const int *const_args) -{ - MIPSInsn i1, i2; - TCGArg a0, a1, a2; - int c2; - - a0 = args[0]; - a1 = args[1]; - a2 = args[2]; - c2 = const_args[2]; - - switch (opc) { - case INDEX_op_exit_tb: - { - TCGReg b0 = TCG_REG_ZERO; - - if (a0 & ~0xffff) { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_V0, a0 & ~0xffff); - b0 = TCG_REG_V0; - } - if (!tcg_out_opc_jmp(s, OPC_J, tb_ret_addr)) { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, - (uintptr_t)tb_ret_addr); - tcg_out_opc_reg(s, OPC_JR, 0, TCG_TMP0, 0); - } - tcg_out_opc_imm(s, OPC_ORI, TCG_REG_V0, b0, a0 & 0xffff); - } - break; - case INDEX_op_goto_tb: - if (s->tb_jmp_offset) { - /* direct jump method */ - s->tb_jmp_offset[a0] = tcg_current_code_size(s); - /* Avoid clobbering the address during retranslation. */ - tcg_out32(s, OPC_J | (*(uint32_t *)s->code_ptr & 0x3ffffff)); - } else { - /* indirect jump method */ - tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP0, TCG_REG_ZERO, - (uintptr_t)(s->tb_next + a0)); - tcg_out_opc_reg(s, OPC_JR, 0, TCG_TMP0, 0); - } - tcg_out_nop(s); - s->tb_next_offset[a0] = tcg_current_code_size(s); - break; - case INDEX_op_br: - tcg_out_brcond(s, TCG_COND_EQ, TCG_REG_ZERO, TCG_REG_ZERO, - arg_label(a0)); - break; - - case INDEX_op_ld8u_i32: - i1 = OPC_LBU; - goto do_ldst; - case INDEX_op_ld8s_i32: - i1 = OPC_LB; - goto do_ldst; - case INDEX_op_ld16u_i32: - i1 = OPC_LHU; - goto do_ldst; - case INDEX_op_ld16s_i32: - i1 = OPC_LH; - goto do_ldst; - case INDEX_op_ld_i32: - i1 = OPC_LW; - goto do_ldst; - case INDEX_op_st8_i32: - i1 = OPC_SB; - goto do_ldst; - case INDEX_op_st16_i32: - i1 = OPC_SH; - goto do_ldst; - case INDEX_op_st_i32: - i1 = OPC_SW; - do_ldst: - tcg_out_ldst(s, i1, a0, a1, a2); - break; - - case INDEX_op_add_i32: - i1 = OPC_ADDU, i2 = OPC_ADDIU; - goto do_binary; - case INDEX_op_or_i32: - i1 = OPC_OR, i2 = OPC_ORI; - goto do_binary; - case INDEX_op_xor_i32: - i1 = OPC_XOR, i2 = OPC_XORI; - do_binary: - if (c2) { - tcg_out_opc_imm(s, i2, a0, a1, a2); - break; - } - do_binaryv: - tcg_out_opc_reg(s, i1, a0, a1, a2); - break; - - case INDEX_op_sub_i32: - if (c2) { - tcg_out_opc_imm(s, OPC_ADDIU, a0, a1, -a2); - break; - } - i1 = OPC_SUBU; - goto do_binary; - case INDEX_op_and_i32: - if (c2 && a2 != (uint16_t)a2) { - int msb = ctz32(~a2) - 1; - assert(use_mips32r2_instructions); - assert(is_p2m1(a2)); - tcg_out_opc_bf(s, OPC_EXT, a0, a1, msb, 0); - break; - } - i1 = OPC_AND, i2 = OPC_ANDI; - goto do_binary; - case INDEX_op_nor_i32: - i1 = OPC_NOR; - goto do_binaryv; - - case INDEX_op_mul_i32: - if (use_mips32_instructions) { - tcg_out_opc_reg(s, OPC_MUL, a0, a1, a2); - break; - } - i1 = OPC_MULT, i2 = OPC_MFLO; - goto do_hilo1; - case INDEX_op_mulsh_i32: - i1 = OPC_MULT, i2 = OPC_MFHI; - goto do_hilo1; - case INDEX_op_muluh_i32: - i1 = OPC_MULTU, i2 = OPC_MFHI; - goto do_hilo1; - case INDEX_op_div_i32: - i1 = OPC_DIV, i2 = OPC_MFLO; - goto do_hilo1; - case INDEX_op_divu_i32: - i1 = OPC_DIVU, i2 = OPC_MFLO; - goto do_hilo1; - case INDEX_op_rem_i32: - i1 = OPC_DIV, i2 = OPC_MFHI; - goto do_hilo1; - case INDEX_op_remu_i32: - i1 = OPC_DIVU, i2 = OPC_MFHI; - do_hilo1: - tcg_out_opc_reg(s, i1, 0, a1, a2); - tcg_out_opc_reg(s, i2, a0, 0, 0); - break; - - case INDEX_op_muls2_i32: - i1 = OPC_MULT; - goto do_hilo2; - case INDEX_op_mulu2_i32: - i1 = OPC_MULTU; - do_hilo2: - tcg_out_opc_reg(s, i1, 0, a2, args[3]); - tcg_out_opc_reg(s, OPC_MFLO, a0, 0, 0); - tcg_out_opc_reg(s, OPC_MFHI, a1, 0, 0); - break; - - case INDEX_op_not_i32: - i1 = OPC_NOR; - goto do_unary; - case INDEX_op_bswap16_i32: - i1 = OPC_WSBH; - goto do_unary; - case INDEX_op_ext8s_i32: - i1 = OPC_SEB; - goto do_unary; - case INDEX_op_ext16s_i32: - i1 = OPC_SEH; - do_unary: - tcg_out_opc_reg(s, i1, a0, TCG_REG_ZERO, a1); - break; - - case INDEX_op_sar_i32: - i1 = OPC_SRAV, i2 = OPC_SRA; - goto do_shift; - case INDEX_op_shl_i32: - i1 = OPC_SLLV, i2 = OPC_SLL; - goto do_shift; - case INDEX_op_shr_i32: - i1 = OPC_SRLV, i2 = OPC_SRL; - goto do_shift; - case INDEX_op_rotr_i32: - i1 = OPC_ROTRV, i2 = OPC_ROTR; - do_shift: - if (c2) { - tcg_out_opc_sa(s, i2, a0, a1, a2); - } else { - tcg_out_opc_reg(s, i1, a0, a2, a1); - } - break; - case INDEX_op_rotl_i32: - if (c2) { - tcg_out_opc_sa(s, OPC_ROTR, a0, a1, 32 - a2); - } else { - tcg_out_opc_reg(s, OPC_SUBU, TCG_TMP0, TCG_REG_ZERO, a2); - tcg_out_opc_reg(s, OPC_ROTRV, a0, TCG_TMP0, a1); - } - break; - - case INDEX_op_bswap32_i32: - tcg_out_opc_reg(s, OPC_WSBH, a0, 0, a1); - tcg_out_opc_sa(s, OPC_ROTR, a0, a0, 16); - break; - - case INDEX_op_deposit_i32: - tcg_out_opc_bf(s, OPC_INS, a0, a2, args[3] + args[4] - 1, args[3]); - break; - - case INDEX_op_brcond_i32: - tcg_out_brcond(s, a2, a0, a1, arg_label(args[3])); - break; - case INDEX_op_brcond2_i32: - tcg_out_brcond2(s, args[4], a0, a1, a2, args[3], arg_label(args[5])); - break; - - case INDEX_op_movcond_i32: - tcg_out_movcond(s, args[5], a0, a1, a2, args[3]); - break; - - case INDEX_op_setcond_i32: - tcg_out_setcond(s, args[3], a0, a1, a2); - break; - case INDEX_op_setcond2_i32: - tcg_out_setcond2(s, args[5], a0, a1, a2, args[3], args[4]); - break; - - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, args, false); - break; - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, args, true); - break; - case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, args, false); - break; - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, args, true); - break; - - case INDEX_op_add2_i32: - tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], - const_args[4], const_args[5], false); - break; - case INDEX_op_sub2_i32: - tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], - const_args[4], const_args[5], true); - break; - - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - default: - tcg_abort(); - } -} - -static const TCGTargetOpDef mips_op_defs[] = { - { INDEX_op_exit_tb, { } }, - { INDEX_op_goto_tb, { } }, - { INDEX_op_br, { } }, - - { INDEX_op_ld8u_i32, { "r", "r" } }, - { INDEX_op_ld8s_i32, { "r", "r" } }, - { INDEX_op_ld16u_i32, { "r", "r" } }, - { INDEX_op_ld16s_i32, { "r", "r" } }, - { INDEX_op_ld_i32, { "r", "r" } }, - { INDEX_op_st8_i32, { "rZ", "r" } }, - { INDEX_op_st16_i32, { "rZ", "r" } }, - { INDEX_op_st_i32, { "rZ", "r" } }, - - { INDEX_op_add_i32, { "r", "rZ", "rJ" } }, - { INDEX_op_mul_i32, { "r", "rZ", "rZ" } }, - { INDEX_op_muls2_i32, { "r", "r", "rZ", "rZ" } }, - { INDEX_op_mulu2_i32, { "r", "r", "rZ", "rZ" } }, - { INDEX_op_mulsh_i32, { "r", "rZ", "rZ" } }, - { INDEX_op_muluh_i32, { "r", "rZ", "rZ" } }, - { INDEX_op_div_i32, { "r", "rZ", "rZ" } }, - { INDEX_op_divu_i32, { "r", "rZ", "rZ" } }, - { INDEX_op_rem_i32, { "r", "rZ", "rZ" } }, - { INDEX_op_remu_i32, { "r", "rZ", "rZ" } }, - { INDEX_op_sub_i32, { "r", "rZ", "rN" } }, - - { INDEX_op_and_i32, { "r", "rZ", "rIK" } }, - { INDEX_op_nor_i32, { "r", "rZ", "rZ" } }, - { INDEX_op_not_i32, { "r", "rZ" } }, - { INDEX_op_or_i32, { "r", "rZ", "rIZ" } }, - { INDEX_op_xor_i32, { "r", "rZ", "rIZ" } }, - - { INDEX_op_shl_i32, { "r", "rZ", "ri" } }, - { INDEX_op_shr_i32, { "r", "rZ", "ri" } }, - { INDEX_op_sar_i32, { "r", "rZ", "ri" } }, - { INDEX_op_rotr_i32, { "r", "rZ", "ri" } }, - { INDEX_op_rotl_i32, { "r", "rZ", "ri" } }, - - { INDEX_op_bswap16_i32, { "r", "r" } }, - { INDEX_op_bswap32_i32, { "r", "r" } }, - - { INDEX_op_ext8s_i32, { "r", "rZ" } }, - { INDEX_op_ext16s_i32, { "r", "rZ" } }, - - { INDEX_op_deposit_i32, { "r", "0", "rZ" } }, - - { INDEX_op_brcond_i32, { "rZ", "rZ" } }, - { INDEX_op_movcond_i32, { "r", "rZ", "rZ", "rZ", "0" } }, - { INDEX_op_setcond_i32, { "r", "rZ", "rZ" } }, - { INDEX_op_setcond2_i32, { "r", "rZ", "rZ", "rZ", "rZ" } }, - - { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rN", "rN" } }, - { INDEX_op_sub2_i32, { "r", "r", "rZ", "rZ", "rN", "rN" } }, - { INDEX_op_brcond2_i32, { "rZ", "rZ", "rZ", "rZ" } }, - -#if TARGET_LONG_BITS == 32 - { INDEX_op_qemu_ld_i32, { "L", "lZ" } }, - { INDEX_op_qemu_st_i32, { "SZ", "SZ" } }, - { INDEX_op_qemu_ld_i64, { "L", "L", "lZ" } }, - { INDEX_op_qemu_st_i64, { "SZ", "SZ", "SZ" } }, -#else - { INDEX_op_qemu_ld_i32, { "L", "lZ", "lZ" } }, - { INDEX_op_qemu_st_i32, { "SZ", "SZ", "SZ" } }, - { INDEX_op_qemu_ld_i64, { "L", "L", "lZ", "lZ" } }, - { INDEX_op_qemu_st_i64, { "SZ", "SZ", "SZ", "SZ" } }, -#endif - { -1 }, -}; - -static int tcg_target_callee_save_regs[] = { - TCG_REG_S0, /* used for the global env (TCG_AREG0) */ - TCG_REG_S1, - TCG_REG_S2, - TCG_REG_S3, - TCG_REG_S4, - TCG_REG_S5, - TCG_REG_S6, - TCG_REG_S7, - TCG_REG_S8, - TCG_REG_RA, /* should be last for ABI compliance */ -}; - -/* The Linux kernel doesn't provide any information about the available - instruction set. Probe it using a signal handler. */ - -#include - -#ifndef use_movnz_instructions -bool use_movnz_instructions = false; -#endif - -#ifndef use_mips32_instructions -bool use_mips32_instructions = false; -#endif - -#ifndef use_mips32r2_instructions -bool use_mips32r2_instructions = false; -#endif - -static volatile sig_atomic_t got_sigill; - -static void sigill_handler(int signo, siginfo_t *si, void *data) -{ - /* Skip the faulty instruction */ - ucontext_t *uc = (ucontext_t *)data; - uc->uc_mcontext.pc += 4; - - got_sigill = 1; -} - -static void tcg_target_detect_isa(void) -{ - struct sigaction sa_old, sa_new; - - memset(&sa_new, 0, sizeof(sa_new)); - sa_new.sa_flags = SA_SIGINFO; - sa_new.sa_sigaction = sigill_handler; - sigaction(SIGILL, &sa_new, &sa_old); - - /* Probe for movn/movz, necessary to implement movcond. */ -#ifndef use_movnz_instructions - got_sigill = 0; - asm volatile(".set push\n" - ".set mips32\n" - "movn $zero, $zero, $zero\n" - "movz $zero, $zero, $zero\n" - ".set pop\n" - : : : ); - use_movnz_instructions = !got_sigill; -#endif - - /* Probe for MIPS32 instructions. As no subsetting is allowed - by the specification, it is only necessary to probe for one - of the instructions. */ -#ifndef use_mips32_instructions - got_sigill = 0; - asm volatile(".set push\n" - ".set mips32\n" - "mul $zero, $zero\n" - ".set pop\n" - : : : ); - use_mips32_instructions = !got_sigill; -#endif - - /* Probe for MIPS32r2 instructions if MIPS32 instructions are - available. As no subsetting is allowed by the specification, - it is only necessary to probe for one of the instructions. */ -#ifndef use_mips32r2_instructions - if (use_mips32_instructions) { - got_sigill = 0; - asm volatile(".set push\n" - ".set mips32r2\n" - "seb $zero, $zero\n" - ".set pop\n" - : : : ); - use_mips32r2_instructions = !got_sigill; - } -#endif - - sigaction(SIGILL, &sa_old, NULL); -} - -/* Generate global QEMU prologue and epilogue code */ -static void tcg_target_qemu_prologue(TCGContext *s) -{ - int i, frame_size; - - /* reserve some stack space, also for TCG temps. */ - frame_size = ARRAY_SIZE(tcg_target_callee_save_regs) * 4 - + TCG_STATIC_CALL_ARGS_SIZE - + CPU_TEMP_BUF_NLONGS * sizeof(long); - frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) & - ~(TCG_TARGET_STACK_ALIGN - 1); - tcg_set_frame(s, TCG_REG_SP, ARRAY_SIZE(tcg_target_callee_save_regs) * 4 - + TCG_STATIC_CALL_ARGS_SIZE, - CPU_TEMP_BUF_NLONGS * sizeof(long)); - - /* TB prologue */ - tcg_out_addi(s, TCG_REG_SP, -frame_size); - for(i = 0 ; i < ARRAY_SIZE(tcg_target_callee_save_regs) ; i++) { - tcg_out_st(s, TCG_TYPE_I32, tcg_target_callee_save_regs[i], - TCG_REG_SP, TCG_STATIC_CALL_ARGS_SIZE + i * 4); - } - - /* Call generated code */ - tcg_out_opc_reg(s, OPC_JR, 0, tcg_target_call_iarg_regs[1], 0); - tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); - tb_ret_addr = s->code_ptr; - - /* TB epilogue */ - for(i = 0 ; i < ARRAY_SIZE(tcg_target_callee_save_regs) ; i++) { - tcg_out_ld(s, TCG_TYPE_I32, tcg_target_callee_save_regs[i], - TCG_REG_SP, TCG_STATIC_CALL_ARGS_SIZE + i * 4); - } - - tcg_out_opc_reg(s, OPC_JR, 0, TCG_REG_RA, 0); - tcg_out_addi(s, TCG_REG_SP, frame_size); -} - -static void tcg_target_init(TCGContext *s) -{ - tcg_target_detect_isa(); - tcg_regset_set(tcg_target_available_regs[TCG_TYPE_I32], 0xffffffff); - tcg_regset_set(tcg_target_call_clobber_regs, - (1 << TCG_REG_V0) | - (1 << TCG_REG_V1) | - (1 << TCG_REG_A0) | - (1 << TCG_REG_A1) | - (1 << TCG_REG_A2) | - (1 << TCG_REG_A3) | - (1 << TCG_REG_T0) | - (1 << TCG_REG_T1) | - (1 << TCG_REG_T2) | - (1 << TCG_REG_T3) | - (1 << TCG_REG_T4) | - (1 << TCG_REG_T5) | - (1 << TCG_REG_T6) | - (1 << TCG_REG_T7) | - (1 << TCG_REG_T8) | - (1 << TCG_REG_T9)); - - tcg_regset_clear(s->reserved_regs); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_ZERO); /* zero register */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_K0); /* kernel use only */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_K1); /* kernel use only */ - tcg_regset_set_reg(s->reserved_regs, TCG_TMP0); /* internal use */ - tcg_regset_set_reg(s->reserved_regs, TCG_TMP1); /* internal use */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_RA); /* return address */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP); /* stack pointer */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_GP); /* global pointer */ - - tcg_add_target_add_op_defs(mips_op_defs); -} - -void tb_set_jmp_target1(uintptr_t jmp_addr, uintptr_t addr) -{ - uint32_t *ptr = (uint32_t *)jmp_addr; - *ptr = deposit32(*ptr, 0, 26, addr >> 2); - flush_icache_range(jmp_addr, jmp_addr + 4); -} diff --git a/qemu/tcg/mips/tcg-target.h b/qemu/tcg/mips/tcg-target.h index f5ba52cac..b1cda37b6 100644 --- a/qemu/tcg/mips/tcg-target.h +++ b/qemu/tcg/mips/tcg-target.h @@ -96,6 +96,13 @@ extern bool use_mips32_instructions; extern bool use_mips32r2_instructions; #endif +/* MIPS32R6 instruction set detection */ +#if defined(__mips_isa_rev) && (__mips_isa_rev >= 6) +#define use_mips32r6_instructions 1 +#else +#define use_mips32r6_instructions 0 +#endif + /* optional instructions */ #define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 @@ -105,8 +112,8 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_orc_i32 0 #define TCG_TARGET_HAS_eqv_i32 0 #define TCG_TARGET_HAS_nand_i32 0 -#define TCG_TARGET_HAS_mulu2_i32 1 -#define TCG_TARGET_HAS_muls2_i32 1 +#define TCG_TARGET_HAS_mulu2_i32 (!use_mips32r6_instructions) +#define TCG_TARGET_HAS_muls2_i32 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_muluh_i32 1 #define TCG_TARGET_HAS_mulsh_i32 1 diff --git a/qemu/tcg/mips/tcg-target.inc.c b/qemu/tcg/mips/tcg-target.inc.c new file mode 100644 index 000000000..aaf881cfd --- /dev/null +++ b/qemu/tcg/mips/tcg-target.inc.c @@ -0,0 +1,1891 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008-2009 Arnaud Patard + * Copyright (c) 2009 Aurelien Jarno + * Based on i386/tcg-target.c - Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "tcg-be-ldst.h" + +#ifdef HOST_WORDS_BIGENDIAN +# define MIPS_BE 1 +#else +# define MIPS_BE 0 +#endif + +#define LO_OFF (MIPS_BE * 4) +#define HI_OFF (4 - LO_OFF) + +#ifdef CONFIG_DEBUG_TCG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "zero", + "at", + "v0", + "v1", + "a0", + "a1", + "a2", + "a3", + "t0", + "t1", + "t2", + "t3", + "t4", + "t5", + "t6", + "t7", + "s0", + "s1", + "s2", + "s3", + "s4", + "s5", + "s6", + "s7", + "t8", + "t9", + "k0", + "k1", + "gp", + "sp", + "s8", + "ra", +}; +#endif + +#define TCG_TMP0 TCG_REG_AT +#define TCG_TMP1 TCG_REG_T9 + +/* check if we really need so many registers :P */ +static const int tcg_target_reg_alloc_order[] = { + /* Call saved registers. */ + TCG_REG_S0, + TCG_REG_S1, + TCG_REG_S2, + TCG_REG_S3, + TCG_REG_S4, + TCG_REG_S5, + TCG_REG_S6, + TCG_REG_S7, + TCG_REG_S8, + + /* Call clobbered registers. */ + TCG_REG_T0, + TCG_REG_T1, + TCG_REG_T2, + TCG_REG_T3, + TCG_REG_T4, + TCG_REG_T5, + TCG_REG_T6, + TCG_REG_T7, + TCG_REG_T8, + TCG_REG_T9, + TCG_REG_V1, + TCG_REG_V0, + + /* Argument registers, opposite order of allocation. */ + TCG_REG_A3, + TCG_REG_A2, + TCG_REG_A1, + TCG_REG_A0, +}; + +static const TCGReg tcg_target_call_iarg_regs[4] = { + TCG_REG_A0, + TCG_REG_A1, + TCG_REG_A2, + TCG_REG_A3 +}; + +static const TCGReg tcg_target_call_oarg_regs[2] = { + TCG_REG_V0, + TCG_REG_V1 +}; + +static tcg_insn_unit *tb_ret_addr; + +static inline uint32_t reloc_pc16_val(tcg_insn_unit *pc, tcg_insn_unit *target) +{ + /* Let the compiler perform the right-shift as part of the arithmetic. */ + ptrdiff_t disp = target - (pc + 1); + tcg_debug_assert(disp == (int16_t)disp); + return disp & 0xffff; +} + +static inline void reloc_pc16(tcg_insn_unit *pc, tcg_insn_unit *target) +{ + *pc = deposit32(*pc, 0, 16, reloc_pc16_val(pc, target)); +} + +static inline uint32_t reloc_26_val(tcg_insn_unit *pc, tcg_insn_unit *target) +{ + tcg_debug_assert((((uintptr_t)pc ^ (uintptr_t)target) & 0xf0000000) == 0); + return ((uintptr_t)target >> 2) & 0x3ffffff; +} + +static inline void reloc_26(tcg_insn_unit *pc, tcg_insn_unit *target) +{ + *pc = deposit32(*pc, 0, 26, reloc_26_val(pc, target)); +} + +static void patch_reloc(tcg_insn_unit *code_ptr, int type, + intptr_t value, intptr_t addend) +{ + tcg_debug_assert(type == R_MIPS_PC16); + tcg_debug_assert(addend == 0); + reloc_pc16(code_ptr, (tcg_insn_unit *)value); +} + +#define TCG_CT_CONST_ZERO 0x100 +#define TCG_CT_CONST_U16 0x200 /* Unsigned 16-bit: 0 - 0xffff. */ +#define TCG_CT_CONST_S16 0x400 /* Signed 16-bit: -32768 - 32767 */ +#define TCG_CT_CONST_P2M1 0x800 /* Power of 2 minus 1. */ +#define TCG_CT_CONST_N16 0x1000 /* "Negatable" 16-bit: -32767 - 32767 */ + +static inline bool is_p2m1(tcg_target_long val) +{ + return val && ((val + 1) & val) == 0; +} + +/* parse target specific constraints */ +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +{ + const char *ct_str; + + ct_str = *pct_str; + switch(ct_str[0]) { + case 'r': + ct->ct |= TCG_CT_REG; + tcg_regset_set(ct->u.regs, 0xffffffff); + break; + case 'L': /* qemu_ld output arg constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set(ct->u.regs, 0xffffffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_V0); + break; + case 'l': /* qemu_ld input arg constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set(ct->u.regs, 0xffffffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_A0); +#if defined(CONFIG_SOFTMMU) + if (TARGET_LONG_BITS == 64) { + tcg_regset_reset_reg(ct->u.regs, TCG_REG_A2); + } +#endif + break; + case 'S': /* qemu_st constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set(ct->u.regs, 0xffffffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_A0); +#if defined(CONFIG_SOFTMMU) + if (TARGET_LONG_BITS == 32) { + tcg_regset_reset_reg(ct->u.regs, TCG_REG_A1); + } else { + tcg_regset_reset_reg(ct->u.regs, TCG_REG_A2); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_A3); + } +#endif + break; + case 'I': + ct->ct |= TCG_CT_CONST_U16; + break; + case 'J': + ct->ct |= TCG_CT_CONST_S16; + break; + case 'K': + ct->ct |= TCG_CT_CONST_P2M1; + break; + case 'N': + ct->ct |= TCG_CT_CONST_N16; + break; + case 'Z': + /* We are cheating a bit here, using the fact that the register + ZERO is also the register number 0. Hence there is no need + to check for const_args in each instruction. */ + ct->ct |= TCG_CT_CONST_ZERO; + break; + default: + return -1; + } + ct_str++; + *pct_str = ct_str; + return 0; +} + +/* test if a constant matches the constraint */ +static inline int tcg_target_const_match(tcg_target_long val, TCGType type, + const TCGArgConstraint *arg_ct) +{ + int ct; + ct = arg_ct->ct; + if (ct & TCG_CT_CONST) { + return 1; + } else if ((ct & TCG_CT_CONST_ZERO) && val == 0) { + return 1; + } else if ((ct & TCG_CT_CONST_U16) && val == (uint16_t)val) { + return 1; + } else if ((ct & TCG_CT_CONST_S16) && val == (int16_t)val) { + return 1; + } else if ((ct & TCG_CT_CONST_N16) && val >= -32767 && val <= 32767) { + return 1; + } else if ((ct & TCG_CT_CONST_P2M1) + && use_mips32r2_instructions && is_p2m1(val)) { + return 1; + } + return 0; +} + +/* instruction opcodes */ +typedef enum { + OPC_J = 0x02 << 26, + OPC_JAL = 0x03 << 26, + OPC_BEQ = 0x04 << 26, + OPC_BNE = 0x05 << 26, + OPC_BLEZ = 0x06 << 26, + OPC_BGTZ = 0x07 << 26, + OPC_ADDIU = 0x09 << 26, + OPC_SLTI = 0x0A << 26, + OPC_SLTIU = 0x0B << 26, + OPC_ANDI = 0x0C << 26, + OPC_ORI = 0x0D << 26, + OPC_XORI = 0x0E << 26, + OPC_LUI = 0x0F << 26, + OPC_LB = 0x20 << 26, + OPC_LH = 0x21 << 26, + OPC_LW = 0x23 << 26, + OPC_LBU = 0x24 << 26, + OPC_LHU = 0x25 << 26, + OPC_LWU = 0x27 << 26, + OPC_SB = 0x28 << 26, + OPC_SH = 0x29 << 26, + OPC_SW = 0x2B << 26, + + OPC_SPECIAL = 0x00 << 26, + OPC_SLL = OPC_SPECIAL | 0x00, + OPC_SRL = OPC_SPECIAL | 0x02, + OPC_ROTR = OPC_SPECIAL | (0x01 << 21) | 0x02, + OPC_SRA = OPC_SPECIAL | 0x03, + OPC_SLLV = OPC_SPECIAL | 0x04, + OPC_SRLV = OPC_SPECIAL | 0x06, + OPC_ROTRV = OPC_SPECIAL | (0x01 << 6) | 0x06, + OPC_SRAV = OPC_SPECIAL | 0x07, + OPC_JR_R5 = OPC_SPECIAL | 0x08, + OPC_JALR = OPC_SPECIAL | 0x09, + OPC_MOVZ = OPC_SPECIAL | 0x0A, + OPC_MOVN = OPC_SPECIAL | 0x0B, + OPC_MFHI = OPC_SPECIAL | 0x10, + OPC_MFLO = OPC_SPECIAL | 0x12, + OPC_MULT = OPC_SPECIAL | 0x18, + OPC_MUL_R6 = OPC_SPECIAL | (0x02 << 6) | 0x18, + OPC_MUH = OPC_SPECIAL | (0x03 << 6) | 0x18, + OPC_MULTU = OPC_SPECIAL | 0x19, + OPC_MULU = OPC_SPECIAL | (0x02 << 6) | 0x19, + OPC_MUHU = OPC_SPECIAL | (0x03 << 6) | 0x19, + OPC_DIV = OPC_SPECIAL | 0x1A, + OPC_DIV_R6 = OPC_SPECIAL | (0x02 << 6) | 0x1A, + OPC_MOD = OPC_SPECIAL | (0x03 << 6) | 0x1A, + OPC_DIVU = OPC_SPECIAL | 0x1B, + OPC_DIVU_R6 = OPC_SPECIAL | (0x02 << 6) | 0x1B, + OPC_MODU = OPC_SPECIAL | (0x03 << 6) | 0x1B, + OPC_ADDU = OPC_SPECIAL | 0x21, + OPC_SUBU = OPC_SPECIAL | 0x23, + OPC_AND = OPC_SPECIAL | 0x24, + OPC_OR = OPC_SPECIAL | 0x25, + OPC_XOR = OPC_SPECIAL | 0x26, + OPC_NOR = OPC_SPECIAL | 0x27, + OPC_SLT = OPC_SPECIAL | 0x2A, + OPC_SLTU = OPC_SPECIAL | 0x2B, + OPC_SELEQZ = OPC_SPECIAL | 0x35, + OPC_SELNEZ = OPC_SPECIAL | 0x37, + + OPC_REGIMM = 0x01 << 26, + OPC_BLTZ = OPC_REGIMM | (0x00 << 16), + OPC_BGEZ = OPC_REGIMM | (0x01 << 16), + + OPC_SPECIAL2 = 0x1c << 26, + OPC_MUL_R5 = OPC_SPECIAL2 | 0x002, + + OPC_SPECIAL3 = 0x1f << 26, + OPC_EXT = OPC_SPECIAL3 | 0x000, + OPC_INS = OPC_SPECIAL3 | 0x004, + OPC_WSBH = OPC_SPECIAL3 | 0x0a0, + OPC_SEB = OPC_SPECIAL3 | 0x420, + OPC_SEH = OPC_SPECIAL3 | 0x620, + + /* MIPS r6 doesn't have JR, JALR should be used instead */ + OPC_JR = use_mips32r6_instructions ? OPC_JALR : OPC_JR_R5, + + /* + * MIPS r6 replaces MUL with an alternative encoding which is + * backwards-compatible at the assembly level. + */ + OPC_MUL = use_mips32r6_instructions ? OPC_MUL_R6 : OPC_MUL_R5, +} MIPSInsn; + +/* + * Type reg + */ +static inline void tcg_out_opc_reg(TCGContext *s, MIPSInsn opc, + TCGReg rd, TCGReg rs, TCGReg rt) +{ + int32_t inst; + + inst = opc; + inst |= (rs & 0x1F) << 21; + inst |= (rt & 0x1F) << 16; + inst |= (rd & 0x1F) << 11; + tcg_out32(s, inst); +} + +/* + * Type immediate + */ +static inline void tcg_out_opc_imm(TCGContext *s, MIPSInsn opc, + TCGReg rt, TCGReg rs, TCGArg imm) +{ + int32_t inst; + + inst = opc; + inst |= (rs & 0x1F) << 21; + inst |= (rt & 0x1F) << 16; + inst |= (imm & 0xffff); + tcg_out32(s, inst); +} + +/* + * Type bitfield + */ +static inline void tcg_out_opc_bf(TCGContext *s, MIPSInsn opc, TCGReg rt, + TCGReg rs, int msb, int lsb) +{ + int32_t inst; + + inst = opc; + inst |= (rs & 0x1F) << 21; + inst |= (rt & 0x1F) << 16; + inst |= (msb & 0x1F) << 11; + inst |= (lsb & 0x1F) << 6; + tcg_out32(s, inst); +} + +/* + * Type branch + */ +static inline void tcg_out_opc_br(TCGContext *s, MIPSInsn opc, + TCGReg rt, TCGReg rs) +{ + /* We pay attention here to not modify the branch target by reading + the existing value and using it again. This ensure that caches and + memory are kept coherent during retranslation. */ + uint16_t offset = (uint16_t)*s->code_ptr; + + tcg_out_opc_imm(s, opc, rt, rs, offset); +} + +/* + * Type sa + */ +static inline void tcg_out_opc_sa(TCGContext *s, MIPSInsn opc, + TCGReg rd, TCGReg rt, TCGArg sa) +{ + int32_t inst; + + inst = opc; + inst |= (rt & 0x1F) << 16; + inst |= (rd & 0x1F) << 11; + inst |= (sa & 0x1F) << 6; + tcg_out32(s, inst); + +} + +/* + * Type jump. + * Returns true if the branch was in range and the insn was emitted. + */ +static bool tcg_out_opc_jmp(TCGContext *s, MIPSInsn opc, void *target) +{ + uintptr_t dest = (uintptr_t)target; + uintptr_t from = (uintptr_t)s->code_ptr + 4; + int32_t inst; + + /* The pc-region branch happens within the 256MB region of + the delay slot (thus the +4). */ + if ((from ^ dest) & -(1 << 28)) { + return false; + } + tcg_debug_assert((dest & 3) == 0); + + inst = opc; + inst |= (dest >> 2) & 0x3ffffff; + tcg_out32(s, inst); + return true; +} + +static inline void tcg_out_nop(TCGContext *s) +{ + tcg_out32(s, 0); +} + +static inline void tcg_out_mov(TCGContext *s, TCGType type, + TCGReg ret, TCGReg arg) +{ + /* Simple reg-reg move, optimising out the 'do nothing' case */ + if (ret != arg) { + tcg_out_opc_reg(s, OPC_ADDU, ret, arg, TCG_REG_ZERO); + } +} + +static inline void tcg_out_movi(TCGContext *s, TCGType type, + TCGReg reg, tcg_target_long arg) +{ + if (arg == (int16_t)arg) { + tcg_out_opc_imm(s, OPC_ADDIU, reg, TCG_REG_ZERO, arg); + } else if (arg == (uint16_t)arg) { + tcg_out_opc_imm(s, OPC_ORI, reg, TCG_REG_ZERO, arg); + } else { + tcg_out_opc_imm(s, OPC_LUI, reg, TCG_REG_ZERO, arg >> 16); + if (arg & 0xffff) { + tcg_out_opc_imm(s, OPC_ORI, reg, reg, arg & 0xffff); + } + } +} + +static inline void tcg_out_bswap16(TCGContext *s, TCGReg ret, TCGReg arg) +{ + if (use_mips32r2_instructions) { + tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); + } else { + /* ret and arg can't be register at */ + if (ret == TCG_TMP0 || arg == TCG_TMP0) { + tcg_abort(); + } + + tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, arg, 8); + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 8); + tcg_out_opc_imm(s, OPC_ANDI, ret, ret, 0xff00); + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP0); + } +} + +static inline void tcg_out_bswap16s(TCGContext *s, TCGReg ret, TCGReg arg) +{ + if (use_mips32r2_instructions) { + tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); + tcg_out_opc_reg(s, OPC_SEH, ret, 0, ret); + } else { + /* ret and arg can't be register at */ + if (ret == TCG_TMP0 || arg == TCG_TMP0) { + tcg_abort(); + } + + tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, arg, 8); + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); + tcg_out_opc_sa(s, OPC_SRA, ret, ret, 16); + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP0); + } +} + +static inline void tcg_out_bswap32(TCGContext *s, TCGReg ret, TCGReg arg) +{ + if (use_mips32r2_instructions) { + tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); + tcg_out_opc_sa(s, OPC_ROTR, ret, ret, 16); + } else { + /* ret and arg must be different and can't be register at */ + if (ret == arg || ret == TCG_TMP0 || arg == TCG_TMP0) { + tcg_abort(); + } + + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); + + tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, arg, 24); + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP0); + + tcg_out_opc_imm(s, OPC_ANDI, TCG_TMP0, arg, 0xff00); + tcg_out_opc_sa(s, OPC_SLL, TCG_TMP0, TCG_TMP0, 8); + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP0); + + tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, arg, 8); + tcg_out_opc_imm(s, OPC_ANDI, TCG_TMP0, TCG_TMP0, 0xff00); + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP0); + } +} + +static inline void tcg_out_ext8s(TCGContext *s, TCGReg ret, TCGReg arg) +{ + if (use_mips32r2_instructions) { + tcg_out_opc_reg(s, OPC_SEB, ret, 0, arg); + } else { + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); + tcg_out_opc_sa(s, OPC_SRA, ret, ret, 24); + } +} + +static inline void tcg_out_ext16s(TCGContext *s, TCGReg ret, TCGReg arg) +{ + if (use_mips32r2_instructions) { + tcg_out_opc_reg(s, OPC_SEH, ret, 0, arg); + } else { + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 16); + tcg_out_opc_sa(s, OPC_SRA, ret, ret, 16); + } +} + +static void tcg_out_ldst(TCGContext *s, MIPSInsn opc, TCGReg data, + TCGReg addr, intptr_t ofs) +{ + int16_t lo = ofs; + if (ofs != lo) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, ofs - lo); + if (addr != TCG_REG_ZERO) { + tcg_out_opc_reg(s, OPC_ADDU, TCG_TMP0, TCG_TMP0, addr); + } + addr = TCG_TMP0; + } + tcg_out_opc_imm(s, opc, data, addr, lo); +} + +static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg arg, + TCGReg arg1, intptr_t arg2) +{ + tcg_out_ldst(s, OPC_LW, arg, arg1, arg2); +} + +static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, + TCGReg arg1, intptr_t arg2) +{ + tcg_out_ldst(s, OPC_SW, arg, arg1, arg2); +} + +static inline void tcg_out_addi(TCGContext *s, TCGReg reg, TCGArg val) +{ + if (val == (int16_t)val) { + tcg_out_opc_imm(s, OPC_ADDIU, reg, reg, val); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, val); + tcg_out_opc_reg(s, OPC_ADDU, reg, reg, TCG_TMP0); + } +} + +static void tcg_out_addsub2(TCGContext *s, TCGReg rl, TCGReg rh, TCGReg al, + TCGReg ah, TCGArg bl, TCGArg bh, bool cbl, + bool cbh, bool is_sub) +{ + TCGReg th = TCG_TMP1; + + /* If we have a negative constant such that negating it would + make the high part zero, we can (usually) eliminate one insn. */ + if (cbl && cbh && bh == -1 && bl != 0) { + bl = -bl; + bh = 0; + is_sub = !is_sub; + } + + /* By operating on the high part first, we get to use the final + carry operation to move back from the temporary. */ + if (!cbh) { + tcg_out_opc_reg(s, (is_sub ? OPC_SUBU : OPC_ADDU), th, ah, bh); + } else if (bh != 0 || ah == rl) { + tcg_out_opc_imm(s, OPC_ADDIU, th, ah, (is_sub ? -bh : bh)); + } else { + th = ah; + } + + /* Note that tcg optimization should eliminate the bl == 0 case. */ + if (is_sub) { + if (cbl) { + tcg_out_opc_imm(s, OPC_SLTIU, TCG_TMP0, al, bl); + tcg_out_opc_imm(s, OPC_ADDIU, rl, al, -bl); + } else { + tcg_out_opc_reg(s, OPC_SLTU, TCG_TMP0, al, bl); + tcg_out_opc_reg(s, OPC_SUBU, rl, al, bl); + } + tcg_out_opc_reg(s, OPC_SUBU, rh, th, TCG_TMP0); + } else { + if (cbl) { + tcg_out_opc_imm(s, OPC_ADDIU, rl, al, bl); + tcg_out_opc_imm(s, OPC_SLTIU, TCG_TMP0, rl, bl); + } else if (rl == al && rl == bl) { + tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, al, 31); + tcg_out_opc_reg(s, OPC_ADDU, rl, al, bl); + } else { + tcg_out_opc_reg(s, OPC_ADDU, rl, al, bl); + tcg_out_opc_reg(s, OPC_SLTU, TCG_TMP0, rl, (rl == bl ? al : bl)); + } + tcg_out_opc_reg(s, OPC_ADDU, rh, th, TCG_TMP0); + } +} + +/* Bit 0 set if inversion required; bit 1 set if swapping required. */ +#define MIPS_CMP_INV 1 +#define MIPS_CMP_SWAP 2 + +static const uint8_t mips_cmp_map[16] = { + [TCG_COND_LT] = 0, + [TCG_COND_LTU] = 0, + [TCG_COND_GE] = MIPS_CMP_INV, + [TCG_COND_GEU] = MIPS_CMP_INV, + [TCG_COND_LE] = MIPS_CMP_INV | MIPS_CMP_SWAP, + [TCG_COND_LEU] = MIPS_CMP_INV | MIPS_CMP_SWAP, + [TCG_COND_GT] = MIPS_CMP_SWAP, + [TCG_COND_GTU] = MIPS_CMP_SWAP, +}; + +static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg arg1, TCGReg arg2) +{ + MIPSInsn s_opc = OPC_SLTU; + int cmp_map; + + switch (cond) { + case TCG_COND_EQ: + if (arg2 != 0) { + tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2); + arg1 = ret; + } + tcg_out_opc_imm(s, OPC_SLTIU, ret, arg1, 1); + break; + + case TCG_COND_NE: + if (arg2 != 0) { + tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2); + arg1 = ret; + } + tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, arg1); + break; + + case TCG_COND_LT: + case TCG_COND_GE: + case TCG_COND_LE: + case TCG_COND_GT: + s_opc = OPC_SLT; + /* FALLTHRU */ + + case TCG_COND_LTU: + case TCG_COND_GEU: + case TCG_COND_LEU: + case TCG_COND_GTU: + cmp_map = mips_cmp_map[cond]; + if (cmp_map & MIPS_CMP_SWAP) { + TCGReg t = arg1; + arg1 = arg2; + arg2 = t; + } + tcg_out_opc_reg(s, s_opc, ret, arg1, arg2); + if (cmp_map & MIPS_CMP_INV) { + tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); + } + break; + + default: + tcg_abort(); + break; + } +} + +static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, + TCGReg arg2, TCGLabel *l) +{ + static const MIPSInsn b_zero[16] = { + [TCG_COND_LT] = OPC_BLTZ, + [TCG_COND_GT] = OPC_BGTZ, + [TCG_COND_LE] = OPC_BLEZ, + [TCG_COND_GE] = OPC_BGEZ, + }; + + MIPSInsn s_opc = OPC_SLTU; + MIPSInsn b_opc; + int cmp_map; + + switch (cond) { + case TCG_COND_EQ: + b_opc = OPC_BEQ; + break; + case TCG_COND_NE: + b_opc = OPC_BNE; + break; + + case TCG_COND_LT: + case TCG_COND_GT: + case TCG_COND_LE: + case TCG_COND_GE: + if (arg2 == 0) { + b_opc = b_zero[cond]; + arg2 = arg1; + arg1 = 0; + break; + } + s_opc = OPC_SLT; + /* FALLTHRU */ + + case TCG_COND_LTU: + case TCG_COND_GTU: + case TCG_COND_LEU: + case TCG_COND_GEU: + cmp_map = mips_cmp_map[cond]; + if (cmp_map & MIPS_CMP_SWAP) { + TCGReg t = arg1; + arg1 = arg2; + arg2 = t; + } + tcg_out_opc_reg(s, s_opc, TCG_TMP0, arg1, arg2); + b_opc = (cmp_map & MIPS_CMP_INV ? OPC_BEQ : OPC_BNE); + arg1 = TCG_TMP0; + arg2 = TCG_REG_ZERO; + break; + + default: + tcg_abort(); + break; + } + + tcg_out_opc_br(s, b_opc, arg1, arg2); + if (l->has_value) { + reloc_pc16(s->code_ptr - 1, l->u.value_ptr); + } else { + tcg_out_reloc(s, s->code_ptr - 1, R_MIPS_PC16, l, 0); + } + tcg_out_nop(s); +} + +static TCGReg tcg_out_reduce_eq2(TCGContext *s, TCGReg tmp0, TCGReg tmp1, + TCGReg al, TCGReg ah, + TCGReg bl, TCGReg bh) +{ + /* Merge highpart comparison into AH. */ + if (bh != 0) { + if (ah != 0) { + tcg_out_opc_reg(s, OPC_XOR, tmp0, ah, bh); + ah = tmp0; + } else { + ah = bh; + } + } + /* Merge lowpart comparison into AL. */ + if (bl != 0) { + if (al != 0) { + tcg_out_opc_reg(s, OPC_XOR, tmp1, al, bl); + al = tmp1; + } else { + al = bl; + } + } + /* Merge high and low part comparisons into AL. */ + if (ah != 0) { + if (al != 0) { + tcg_out_opc_reg(s, OPC_OR, tmp0, ah, al); + al = tmp0; + } else { + al = ah; + } + } + return al; +} + +static void tcg_out_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh) +{ + TCGReg tmp0 = TCG_TMP0; + TCGReg tmp1 = ret; + + tcg_debug_assert(ret != TCG_TMP0); + if (ret == ah || ret == bh) { + tcg_debug_assert(ret != TCG_TMP1); + tmp1 = TCG_TMP1; + } + + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_NE: + tmp1 = tcg_out_reduce_eq2(s, tmp0, tmp1, al, ah, bl, bh); + tcg_out_setcond(s, cond, ret, tmp1, TCG_REG_ZERO); + break; + + default: + tcg_out_setcond(s, TCG_COND_EQ, tmp0, ah, bh); + tcg_out_setcond(s, tcg_unsigned_cond(cond), tmp1, al, bl); + tcg_out_opc_reg(s, OPC_AND, tmp1, tmp1, tmp0); + tcg_out_setcond(s, tcg_high_cond(cond), tmp0, ah, bh); + tcg_out_opc_reg(s, OPC_OR, ret, tmp1, tmp0); + break; + } +} + +static void tcg_out_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, + TCGReg bl, TCGReg bh, TCGLabel *l) +{ + TCGCond b_cond = TCG_COND_NE; + TCGReg tmp = TCG_TMP1; + + /* With branches, we emit between 4 and 9 insns with 2 or 3 branches. + With setcond, we emit between 3 and 10 insns and only 1 branch, + which ought to get better branch prediction. */ + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_NE: + b_cond = cond; + tmp = tcg_out_reduce_eq2(s, TCG_TMP0, TCG_TMP1, al, ah, bl, bh); + break; + + default: + /* Minimize code size by preferring a compare not requiring INV. */ + if (mips_cmp_map[cond] & MIPS_CMP_INV) { + cond = tcg_invert_cond(cond); + b_cond = TCG_COND_EQ; + } + tcg_out_setcond2(s, cond, tmp, al, ah, bl, bh); + break; + } + + tcg_out_brcond(s, b_cond, tmp, TCG_REG_ZERO, l); +} + +static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg c1, TCGReg c2, TCGReg v1, TCGReg v2) +{ + bool eqz = false; + + /* If one of the values is zero, put it last to match SEL*Z instructions */ + if (use_mips32r6_instructions && v1 == 0) { + v1 = v2; + v2 = 0; + cond = tcg_invert_cond(cond); + } + + switch (cond) { + case TCG_COND_EQ: + eqz = true; + /* FALLTHRU */ + case TCG_COND_NE: + if (c2 != 0) { + tcg_out_opc_reg(s, OPC_XOR, TCG_TMP0, c1, c2); + c1 = TCG_TMP0; + } + break; + + default: + /* Minimize code size by preferring a compare not requiring INV. */ + if (mips_cmp_map[cond] & MIPS_CMP_INV) { + cond = tcg_invert_cond(cond); + eqz = true; + } + tcg_out_setcond(s, cond, TCG_TMP0, c1, c2); + c1 = TCG_TMP0; + break; + } + + if (use_mips32r6_instructions) { + MIPSInsn m_opc_t = eqz ? OPC_SELEQZ : OPC_SELNEZ; + MIPSInsn m_opc_f = eqz ? OPC_SELNEZ : OPC_SELEQZ; + + if (v2 != 0) { + tcg_out_opc_reg(s, m_opc_f, TCG_TMP1, v2, c1); + } + tcg_out_opc_reg(s, m_opc_t, ret, v1, c1); + if (v2 != 0) { + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP1); + } + } else { + MIPSInsn m_opc = eqz ? OPC_MOVZ : OPC_MOVN; + + tcg_out_opc_reg(s, m_opc, ret, v1, c1); + + /* This should be guaranteed via constraints */ + tcg_debug_assert(v2 == ret); + } +} + +static void tcg_out_call_int(TCGContext *s, tcg_insn_unit *arg, bool tail) +{ + /* Note that the ABI requires the called function's address to be + loaded into T9, even if a direct branch is in range. */ + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_T9, (uintptr_t)arg); + + /* But do try a direct branch, allowing the cpu better insn prefetch. */ + if (tail) { + if (!tcg_out_opc_jmp(s, OPC_J, arg)) { + tcg_out_opc_reg(s, OPC_JR, 0, TCG_REG_T9, 0); + } + } else { + if (!tcg_out_opc_jmp(s, OPC_JAL, arg)) { + tcg_out_opc_reg(s, OPC_JALR, TCG_REG_RA, TCG_REG_T9, 0); + } + } +} + +static void tcg_out_call(TCGContext *s, tcg_insn_unit *arg) +{ + tcg_out_call_int(s, arg, false); + tcg_out_nop(s); +} + +#if defined(CONFIG_SOFTMMU) +static void * const qemu_ld_helpers[16] = { + [MO_UB] = helper_ret_ldub_mmu, + [MO_SB] = helper_ret_ldsb_mmu, + [MO_LEUW] = helper_le_lduw_mmu, + [MO_LESW] = helper_le_ldsw_mmu, + [MO_LEUL] = helper_le_ldul_mmu, + [MO_LEQ] = helper_le_ldq_mmu, + [MO_BEUW] = helper_be_lduw_mmu, + [MO_BESW] = helper_be_ldsw_mmu, + [MO_BEUL] = helper_be_ldul_mmu, + [MO_BEQ] = helper_be_ldq_mmu, +}; + +static void * const qemu_st_helpers[16] = { + [MO_UB] = helper_ret_stb_mmu, + [MO_LEUW] = helper_le_stw_mmu, + [MO_LEUL] = helper_le_stl_mmu, + [MO_LEQ] = helper_le_stq_mmu, + [MO_BEUW] = helper_be_stw_mmu, + [MO_BEUL] = helper_be_stl_mmu, + [MO_BEQ] = helper_be_stq_mmu, +}; + +/* Helper routines for marshalling helper function arguments into + * the correct registers and stack. + * I is where we want to put this argument, and is updated and returned + * for the next call. ARG is the argument itself. + * + * We provide routines for arguments which are: immediate, 32 bit + * value in register, 16 and 8 bit values in register (which must be zero + * extended before use) and 64 bit value in a lo:hi register pair. + */ + +static int tcg_out_call_iarg_reg(TCGContext *s, int i, TCGReg arg) +{ + if (i < ARRAY_SIZE(tcg_target_call_iarg_regs)) { + tcg_out_mov(s, TCG_TYPE_REG, tcg_target_call_iarg_regs[i], arg); + } else { + tcg_out_st(s, TCG_TYPE_REG, arg, TCG_REG_SP, 4 * i); + } + return i + 1; +} + +static int tcg_out_call_iarg_reg8(TCGContext *s, int i, TCGReg arg) +{ + TCGReg tmp = TCG_TMP0; + if (i < ARRAY_SIZE(tcg_target_call_iarg_regs)) { + tmp = tcg_target_call_iarg_regs[i]; + } + tcg_out_opc_imm(s, OPC_ANDI, tmp, arg, 0xff); + return tcg_out_call_iarg_reg(s, i, tmp); +} + +static int tcg_out_call_iarg_reg16(TCGContext *s, int i, TCGReg arg) +{ + TCGReg tmp = TCG_TMP0; + if (i < ARRAY_SIZE(tcg_target_call_iarg_regs)) { + tmp = tcg_target_call_iarg_regs[i]; + } + tcg_out_opc_imm(s, OPC_ANDI, tmp, arg, 0xffff); + return tcg_out_call_iarg_reg(s, i, tmp); +} + +static int tcg_out_call_iarg_imm(TCGContext *s, int i, TCGArg arg) +{ + TCGReg tmp = TCG_TMP0; + if (arg == 0) { + tmp = TCG_REG_ZERO; + } else { + if (i < ARRAY_SIZE(tcg_target_call_iarg_regs)) { + tmp = tcg_target_call_iarg_regs[i]; + } + tcg_out_movi(s, TCG_TYPE_REG, tmp, arg); + } + return tcg_out_call_iarg_reg(s, i, tmp); +} + +static int tcg_out_call_iarg_reg2(TCGContext *s, int i, TCGReg al, TCGReg ah) +{ + i = (i + 1) & ~1; + i = tcg_out_call_iarg_reg(s, i, (MIPS_BE ? ah : al)); + i = tcg_out_call_iarg_reg(s, i, (MIPS_BE ? al : ah)); + return i; +} + +/* Perform the tlb comparison operation. The complete host address is + placed in BASE. Clobbers AT, T0, A0. */ +static void tcg_out_tlb_load(TCGContext *s, TCGReg base, TCGReg addrl, + TCGReg addrh, TCGMemOpIdx oi, + tcg_insn_unit *label_ptr[2], bool is_load) +{ + TCGMemOp s_bits = get_memop(oi) & MO_SIZE; + int mem_index = get_mmuidx(oi); + int cmp_off + = (is_load + ? offsetof(CPUArchState, tlb_table[mem_index][0].addr_read) + : offsetof(CPUArchState, tlb_table[mem_index][0].addr_write)); + int add_off = offsetof(CPUArchState, tlb_table[mem_index][0].addend); + + tcg_out_opc_sa(s, OPC_SRL, TCG_REG_A0, addrl, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); + tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_A0, TCG_REG_A0, + (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS); + tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_A0, TCG_REG_A0, TCG_AREG0); + + /* Compensate for very large offsets. */ + if (add_off >= 0x8000) { + /* Most target env are smaller than 32k; none are larger than 64k. + Simplify the logic here merely to offset by 0x7ff0, giving us a + range just shy of 64k. Check this assumption. */ + QEMU_BUILD_BUG_ON(offsetof(CPUArchState, + tlb_table[NB_MMU_MODES - 1][1]) + > 0x7ff0 + 0x7fff); + tcg_out_opc_imm(s, OPC_ADDIU, TCG_REG_A0, TCG_REG_A0, 0x7ff0); + cmp_off -= 0x7ff0; + add_off -= 0x7ff0; + } + + /* Load the (low half) tlb comparator. */ + tcg_out_opc_imm(s, OPC_LW, TCG_TMP0, TCG_REG_A0, + cmp_off + (TARGET_LONG_BITS == 64 ? LO_OFF : 0)); + + /* Mask the page bits, keeping the alignment bits to compare against. + In between on 32-bit targets, load the tlb addend for the fast path. */ + tcg_out_movi(s, TCG_TYPE_I32, TCG_TMP1, + TARGET_PAGE_MASK | ((1 << s_bits) - 1)); + if (TARGET_LONG_BITS == 32) { + tcg_out_opc_imm(s, OPC_LW, TCG_REG_A0, TCG_REG_A0, add_off); + } + tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, addrl); + + label_ptr[0] = s->code_ptr; + tcg_out_opc_br(s, OPC_BNE, TCG_TMP1, TCG_TMP0); + + /* Load and test the high half tlb comparator. */ + if (TARGET_LONG_BITS == 64) { + /* delay slot */ + tcg_out_opc_imm(s, OPC_LW, TCG_TMP0, TCG_REG_A0, cmp_off + HI_OFF); + + /* Load the tlb addend for the fast path. We can't do it earlier with + 64-bit targets or we'll clobber a0 before reading the high half tlb + comparator. */ + tcg_out_opc_imm(s, OPC_LW, TCG_REG_A0, TCG_REG_A0, add_off); + + label_ptr[1] = s->code_ptr; + tcg_out_opc_br(s, OPC_BNE, addrh, TCG_TMP0); + } + + /* delay slot */ + tcg_out_opc_reg(s, OPC_ADDU, base, TCG_REG_A0, addrl); +} + +static void add_qemu_ldst_label(TCGContext *s, int is_ld, TCGMemOpIdx oi, + TCGReg datalo, TCGReg datahi, + TCGReg addrlo, TCGReg addrhi, + void *raddr, tcg_insn_unit *label_ptr[2]) +{ + TCGLabelQemuLdst *label = new_ldst_label(s); + + label->is_ld = is_ld; + label->oi = oi; + label->datalo_reg = datalo; + label->datahi_reg = datahi; + label->addrlo_reg = addrlo; + label->addrhi_reg = addrhi; + label->raddr = raddr; + label->label_ptr[0] = label_ptr[0]; + if (TARGET_LONG_BITS == 64) { + label->label_ptr[1] = label_ptr[1]; + } +} + +static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + TCGMemOpIdx oi = l->oi; + TCGMemOp opc = get_memop(oi); + TCGReg v0; + int i; + + /* resolve label address */ + reloc_pc16(l->label_ptr[0], s->code_ptr); + if (TARGET_LONG_BITS == 64) { + reloc_pc16(l->label_ptr[1], s->code_ptr); + } + + i = 1; + if (TARGET_LONG_BITS == 64) { + i = tcg_out_call_iarg_reg2(s, i, l->addrlo_reg, l->addrhi_reg); + } else { + i = tcg_out_call_iarg_reg(s, i, l->addrlo_reg); + } + i = tcg_out_call_iarg_imm(s, i, oi); + i = tcg_out_call_iarg_imm(s, i, (intptr_t)l->raddr); + tcg_out_call_int(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SSIZE)], false); + /* delay slot */ + tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0); + + v0 = l->datalo_reg; + if ((opc & MO_SIZE) == MO_64) { + /* We eliminated V0 from the possible output registers, so it + cannot be clobbered here. So we must move V1 first. */ + if (MIPS_BE) { + tcg_out_mov(s, TCG_TYPE_I32, v0, TCG_REG_V1); + v0 = l->datahi_reg; + } else { + tcg_out_mov(s, TCG_TYPE_I32, l->datahi_reg, TCG_REG_V1); + } + } + + reloc_pc16(s->code_ptr, l->raddr); + tcg_out_opc_br(s, OPC_BEQ, TCG_REG_ZERO, TCG_REG_ZERO); + /* delay slot */ + tcg_out_mov(s, TCG_TYPE_REG, v0, TCG_REG_V0); +} + +static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + TCGMemOpIdx oi = l->oi; + TCGMemOp opc = get_memop(oi); + TCGMemOp s_bits = opc & MO_SIZE; + int i; + + /* resolve label address */ + reloc_pc16(l->label_ptr[0], s->code_ptr); + if (TARGET_LONG_BITS == 64) { + reloc_pc16(l->label_ptr[1], s->code_ptr); + } + + i = 1; + if (TARGET_LONG_BITS == 64) { + i = tcg_out_call_iarg_reg2(s, i, l->addrlo_reg, l->addrhi_reg); + } else { + i = tcg_out_call_iarg_reg(s, i, l->addrlo_reg); + } + switch (s_bits) { + case MO_8: + i = tcg_out_call_iarg_reg8(s, i, l->datalo_reg); + break; + case MO_16: + i = tcg_out_call_iarg_reg16(s, i, l->datalo_reg); + break; + case MO_32: + i = tcg_out_call_iarg_reg(s, i, l->datalo_reg); + break; + case MO_64: + i = tcg_out_call_iarg_reg2(s, i, l->datalo_reg, l->datahi_reg); + break; + default: + tcg_abort(); + } + i = tcg_out_call_iarg_imm(s, i, oi); + + /* Tail call to the store helper. Thus force the return address + computation to take place in the return address register. */ + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RA, (intptr_t)l->raddr); + i = tcg_out_call_iarg_reg(s, i, TCG_REG_RA); + tcg_out_call_int(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)], true); + /* delay slot */ + tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0); +} +#endif + +static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, + TCGReg base, TCGMemOp opc) +{ + switch (opc & (MO_SSIZE | MO_BSWAP)) { + case MO_UB: + tcg_out_opc_imm(s, OPC_LBU, datalo, base, 0); + break; + case MO_SB: + tcg_out_opc_imm(s, OPC_LB, datalo, base, 0); + break; + case MO_UW | MO_BSWAP: + tcg_out_opc_imm(s, OPC_LHU, TCG_TMP1, base, 0); + tcg_out_bswap16(s, datalo, TCG_TMP1); + break; + case MO_UW: + tcg_out_opc_imm(s, OPC_LHU, datalo, base, 0); + break; + case MO_SW | MO_BSWAP: + tcg_out_opc_imm(s, OPC_LHU, TCG_TMP1, base, 0); + tcg_out_bswap16s(s, datalo, TCG_TMP1); + break; + case MO_SW: + tcg_out_opc_imm(s, OPC_LH, datalo, base, 0); + break; + case MO_UL | MO_BSWAP: + tcg_out_opc_imm(s, OPC_LW, TCG_TMP1, base, 0); + tcg_out_bswap32(s, datalo, TCG_TMP1); + break; + case MO_UL: + tcg_out_opc_imm(s, OPC_LW, datalo, base, 0); + break; + case MO_Q | MO_BSWAP: + tcg_out_opc_imm(s, OPC_LW, TCG_TMP1, base, HI_OFF); + tcg_out_bswap32(s, datalo, TCG_TMP1); + tcg_out_opc_imm(s, OPC_LW, TCG_TMP1, base, LO_OFF); + tcg_out_bswap32(s, datahi, TCG_TMP1); + break; + case MO_Q: + tcg_out_opc_imm(s, OPC_LW, datalo, base, LO_OFF); + tcg_out_opc_imm(s, OPC_LW, datahi, base, HI_OFF); + break; + default: + tcg_abort(); + } +} + +static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) +{ + TCGReg addr_regl, addr_regh __attribute__((unused)); + TCGReg data_regl, data_regh; + TCGMemOpIdx oi; + TCGMemOp opc; +#if defined(CONFIG_SOFTMMU) + tcg_insn_unit *label_ptr[2]; +#endif + /* Note that we've eliminated V0 from the output registers, + so we won't overwrite the base register during loading. */ + TCGReg base = TCG_REG_V0; + + data_regl = *args++; + data_regh = (is_64 ? *args++ : 0); + addr_regl = *args++; + addr_regh = (TARGET_LONG_BITS == 64 ? *args++ : 0); + oi = *args++; + opc = get_memop(oi); + +#if defined(CONFIG_SOFTMMU) + tcg_out_tlb_load(s, base, addr_regl, addr_regh, oi, label_ptr, 1); + tcg_out_qemu_ld_direct(s, data_regl, data_regh, base, opc); + add_qemu_ldst_label(s, 1, oi, data_regl, data_regh, addr_regl, addr_regh, + s->code_ptr, label_ptr); +#else + if (guest_base == 0 && data_regl != addr_regl) { + base = addr_regl; + } else if (guest_base == (int16_t)guest_base) { + tcg_out_opc_imm(s, OPC_ADDIU, base, addr_regl, guest_base); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, base, guest_base); + tcg_out_opc_reg(s, OPC_ADDU, base, base, addr_regl); + } + tcg_out_qemu_ld_direct(s, data_regl, data_regh, base, opc); +#endif +} + +static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, + TCGReg base, TCGMemOp opc) +{ + switch (opc & (MO_SIZE | MO_BSWAP)) { + case MO_8: + tcg_out_opc_imm(s, OPC_SB, datalo, base, 0); + break; + + case MO_16 | MO_BSWAP: + tcg_out_opc_imm(s, OPC_ANDI, TCG_TMP1, datalo, 0xffff); + tcg_out_bswap16(s, TCG_TMP1, TCG_TMP1); + datalo = TCG_TMP1; + /* FALLTHRU */ + case MO_16: + tcg_out_opc_imm(s, OPC_SH, datalo, base, 0); + break; + + case MO_32 | MO_BSWAP: + tcg_out_bswap32(s, TCG_TMP1, datalo); + datalo = TCG_TMP1; + /* FALLTHRU */ + case MO_32: + tcg_out_opc_imm(s, OPC_SW, datalo, base, 0); + break; + + case MO_64 | MO_BSWAP: + tcg_out_bswap32(s, TCG_TMP1, datalo); + tcg_out_opc_imm(s, OPC_SW, TCG_TMP1, base, HI_OFF); + tcg_out_bswap32(s, TCG_TMP1, datahi); + tcg_out_opc_imm(s, OPC_SW, TCG_TMP1, base, LO_OFF); + break; + case MO_64: + tcg_out_opc_imm(s, OPC_SW, datalo, base, LO_OFF); + tcg_out_opc_imm(s, OPC_SW, datahi, base, HI_OFF); + break; + + default: + tcg_abort(); + } +} + +static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) +{ + TCGReg addr_regl, addr_regh __attribute__((unused)); + TCGReg data_regl, data_regh, base; + TCGMemOpIdx oi; + TCGMemOp opc; +#if defined(CONFIG_SOFTMMU) + tcg_insn_unit *label_ptr[2]; +#endif + + data_regl = *args++; + data_regh = (is_64 ? *args++ : 0); + addr_regl = *args++; + addr_regh = (TARGET_LONG_BITS == 64 ? *args++ : 0); + oi = *args++; + opc = get_memop(oi); + +#if defined(CONFIG_SOFTMMU) + /* Note that we eliminated the helper's address argument, + so we can reuse that for the base. */ + base = (TARGET_LONG_BITS == 32 ? TCG_REG_A1 : TCG_REG_A2); + tcg_out_tlb_load(s, base, addr_regl, addr_regh, oi, label_ptr, 0); + tcg_out_qemu_st_direct(s, data_regl, data_regh, base, opc); + add_qemu_ldst_label(s, 0, oi, data_regl, data_regh, addr_regl, addr_regh, + s->code_ptr, label_ptr); +#else + if (guest_base == 0) { + base = addr_regl; + } else { + base = TCG_REG_A0; + if (guest_base == (int16_t)guest_base) { + tcg_out_opc_imm(s, OPC_ADDIU, base, addr_regl, guest_base); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, base, guest_base); + tcg_out_opc_reg(s, OPC_ADDU, base, base, addr_regl); + } + } + tcg_out_qemu_st_direct(s, data_regl, data_regh, base, opc); +#endif +} + +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, + const TCGArg *args, const int *const_args) +{ + MIPSInsn i1, i2; + TCGArg a0, a1, a2; + int c2; + + a0 = args[0]; + a1 = args[1]; + a2 = args[2]; + c2 = const_args[2]; + + switch (opc) { + case INDEX_op_exit_tb: + { + TCGReg b0 = TCG_REG_ZERO; + + if (a0 & ~0xffff) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_V0, a0 & ~0xffff); + b0 = TCG_REG_V0; + } + if (!tcg_out_opc_jmp(s, OPC_J, tb_ret_addr)) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, + (uintptr_t)tb_ret_addr); + tcg_out_opc_reg(s, OPC_JR, 0, TCG_TMP0, 0); + } + tcg_out_opc_imm(s, OPC_ORI, TCG_REG_V0, b0, a0 & 0xffff); + } + break; + case INDEX_op_goto_tb: + if (s->tb_jmp_offset) { + /* direct jump method */ + s->tb_jmp_offset[a0] = tcg_current_code_size(s); + /* Avoid clobbering the address during retranslation. */ + tcg_out32(s, OPC_J | (*(uint32_t *)s->code_ptr & 0x3ffffff)); + } else { + /* indirect jump method */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP0, TCG_REG_ZERO, + (uintptr_t)(s->tb_next + a0)); + tcg_out_opc_reg(s, OPC_JR, 0, TCG_TMP0, 0); + } + tcg_out_nop(s); + s->tb_next_offset[a0] = tcg_current_code_size(s); + break; + case INDEX_op_br: + tcg_out_brcond(s, TCG_COND_EQ, TCG_REG_ZERO, TCG_REG_ZERO, + arg_label(a0)); + break; + + case INDEX_op_ld8u_i32: + i1 = OPC_LBU; + goto do_ldst; + case INDEX_op_ld8s_i32: + i1 = OPC_LB; + goto do_ldst; + case INDEX_op_ld16u_i32: + i1 = OPC_LHU; + goto do_ldst; + case INDEX_op_ld16s_i32: + i1 = OPC_LH; + goto do_ldst; + case INDEX_op_ld_i32: + i1 = OPC_LW; + goto do_ldst; + case INDEX_op_st8_i32: + i1 = OPC_SB; + goto do_ldst; + case INDEX_op_st16_i32: + i1 = OPC_SH; + goto do_ldst; + case INDEX_op_st_i32: + i1 = OPC_SW; + do_ldst: + tcg_out_ldst(s, i1, a0, a1, a2); + break; + + case INDEX_op_add_i32: + i1 = OPC_ADDU, i2 = OPC_ADDIU; + goto do_binary; + case INDEX_op_or_i32: + i1 = OPC_OR, i2 = OPC_ORI; + goto do_binary; + case INDEX_op_xor_i32: + i1 = OPC_XOR, i2 = OPC_XORI; + do_binary: + if (c2) { + tcg_out_opc_imm(s, i2, a0, a1, a2); + break; + } + do_binaryv: + tcg_out_opc_reg(s, i1, a0, a1, a2); + break; + + case INDEX_op_sub_i32: + if (c2) { + tcg_out_opc_imm(s, OPC_ADDIU, a0, a1, -a2); + break; + } + i1 = OPC_SUBU; + goto do_binary; + case INDEX_op_and_i32: + if (c2 && a2 != (uint16_t)a2) { + int msb = ctz32(~a2) - 1; + tcg_debug_assert(use_mips32r2_instructions); + tcg_debug_assert(is_p2m1(a2)); + tcg_out_opc_bf(s, OPC_EXT, a0, a1, msb, 0); + break; + } + i1 = OPC_AND, i2 = OPC_ANDI; + goto do_binary; + case INDEX_op_nor_i32: + i1 = OPC_NOR; + goto do_binaryv; + + case INDEX_op_mul_i32: + if (use_mips32_instructions) { + tcg_out_opc_reg(s, OPC_MUL, a0, a1, a2); + break; + } + i1 = OPC_MULT, i2 = OPC_MFLO; + goto do_hilo1; + case INDEX_op_mulsh_i32: + if (use_mips32r6_instructions) { + tcg_out_opc_reg(s, OPC_MUH, a0, a1, a2); + break; + } + i1 = OPC_MULT, i2 = OPC_MFHI; + goto do_hilo1; + case INDEX_op_muluh_i32: + if (use_mips32r6_instructions) { + tcg_out_opc_reg(s, OPC_MUHU, a0, a1, a2); + break; + } + i1 = OPC_MULTU, i2 = OPC_MFHI; + goto do_hilo1; + case INDEX_op_div_i32: + if (use_mips32r6_instructions) { + tcg_out_opc_reg(s, OPC_DIV_R6, a0, a1, a2); + break; + } + i1 = OPC_DIV, i2 = OPC_MFLO; + goto do_hilo1; + case INDEX_op_divu_i32: + if (use_mips32r6_instructions) { + tcg_out_opc_reg(s, OPC_DIVU_R6, a0, a1, a2); + break; + } + i1 = OPC_DIVU, i2 = OPC_MFLO; + goto do_hilo1; + case INDEX_op_rem_i32: + if (use_mips32r6_instructions) { + tcg_out_opc_reg(s, OPC_MOD, a0, a1, a2); + break; + } + i1 = OPC_DIV, i2 = OPC_MFHI; + goto do_hilo1; + case INDEX_op_remu_i32: + if (use_mips32r6_instructions) { + tcg_out_opc_reg(s, OPC_MODU, a0, a1, a2); + break; + } + i1 = OPC_DIVU, i2 = OPC_MFHI; + do_hilo1: + tcg_out_opc_reg(s, i1, 0, a1, a2); + tcg_out_opc_reg(s, i2, a0, 0, 0); + break; + + case INDEX_op_muls2_i32: + i1 = OPC_MULT; + goto do_hilo2; + case INDEX_op_mulu2_i32: + i1 = OPC_MULTU; + do_hilo2: + tcg_out_opc_reg(s, i1, 0, a2, args[3]); + tcg_out_opc_reg(s, OPC_MFLO, a0, 0, 0); + tcg_out_opc_reg(s, OPC_MFHI, a1, 0, 0); + break; + + case INDEX_op_not_i32: + i1 = OPC_NOR; + goto do_unary; + case INDEX_op_bswap16_i32: + i1 = OPC_WSBH; + goto do_unary; + case INDEX_op_ext8s_i32: + i1 = OPC_SEB; + goto do_unary; + case INDEX_op_ext16s_i32: + i1 = OPC_SEH; + do_unary: + tcg_out_opc_reg(s, i1, a0, TCG_REG_ZERO, a1); + break; + + case INDEX_op_sar_i32: + i1 = OPC_SRAV, i2 = OPC_SRA; + goto do_shift; + case INDEX_op_shl_i32: + i1 = OPC_SLLV, i2 = OPC_SLL; + goto do_shift; + case INDEX_op_shr_i32: + i1 = OPC_SRLV, i2 = OPC_SRL; + goto do_shift; + case INDEX_op_rotr_i32: + i1 = OPC_ROTRV, i2 = OPC_ROTR; + do_shift: + if (c2) { + tcg_out_opc_sa(s, i2, a0, a1, a2); + } else { + tcg_out_opc_reg(s, i1, a0, a2, a1); + } + break; + case INDEX_op_rotl_i32: + if (c2) { + tcg_out_opc_sa(s, OPC_ROTR, a0, a1, 32 - a2); + } else { + tcg_out_opc_reg(s, OPC_SUBU, TCG_TMP0, TCG_REG_ZERO, a2); + tcg_out_opc_reg(s, OPC_ROTRV, a0, TCG_TMP0, a1); + } + break; + + case INDEX_op_bswap32_i32: + tcg_out_opc_reg(s, OPC_WSBH, a0, 0, a1); + tcg_out_opc_sa(s, OPC_ROTR, a0, a0, 16); + break; + + case INDEX_op_deposit_i32: + tcg_out_opc_bf(s, OPC_INS, a0, a2, args[3] + args[4] - 1, args[3]); + break; + + case INDEX_op_brcond_i32: + tcg_out_brcond(s, a2, a0, a1, arg_label(args[3])); + break; + case INDEX_op_brcond2_i32: + tcg_out_brcond2(s, args[4], a0, a1, a2, args[3], arg_label(args[5])); + break; + + case INDEX_op_movcond_i32: + tcg_out_movcond(s, args[5], a0, a1, a2, args[3], args[4]); + break; + + case INDEX_op_setcond_i32: + tcg_out_setcond(s, args[3], a0, a1, a2); + break; + case INDEX_op_setcond2_i32: + tcg_out_setcond2(s, args[5], a0, a1, a2, args[3], args[4]); + break; + + case INDEX_op_qemu_ld_i32: + tcg_out_qemu_ld(s, args, false); + break; + case INDEX_op_qemu_ld_i64: + tcg_out_qemu_ld(s, args, true); + break; + case INDEX_op_qemu_st_i32: + tcg_out_qemu_st(s, args, false); + break; + case INDEX_op_qemu_st_i64: + tcg_out_qemu_st(s, args, true); + break; + + case INDEX_op_add2_i32: + tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], + const_args[4], const_args[5], false); + break; + case INDEX_op_sub2_i32: + tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], + const_args[4], const_args[5], true); + break; + + case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ + case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ + case INDEX_op_call: /* Always emitted via tcg_out_call. */ + default: + tcg_abort(); + } +} + +static const TCGTargetOpDef mips_op_defs[] = { + { INDEX_op_exit_tb, { } }, + { INDEX_op_goto_tb, { } }, + { INDEX_op_br, { } }, + + { INDEX_op_ld8u_i32, { "r", "r" } }, + { INDEX_op_ld8s_i32, { "r", "r" } }, + { INDEX_op_ld16u_i32, { "r", "r" } }, + { INDEX_op_ld16s_i32, { "r", "r" } }, + { INDEX_op_ld_i32, { "r", "r" } }, + { INDEX_op_st8_i32, { "rZ", "r" } }, + { INDEX_op_st16_i32, { "rZ", "r" } }, + { INDEX_op_st_i32, { "rZ", "r" } }, + + { INDEX_op_add_i32, { "r", "rZ", "rJ" } }, + { INDEX_op_mul_i32, { "r", "rZ", "rZ" } }, +#if !use_mips32r6_instructions + { INDEX_op_muls2_i32, { "r", "r", "rZ", "rZ" } }, + { INDEX_op_mulu2_i32, { "r", "r", "rZ", "rZ" } }, +#endif + { INDEX_op_mulsh_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_muluh_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_div_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_divu_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_rem_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_remu_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_sub_i32, { "r", "rZ", "rN" } }, + + { INDEX_op_and_i32, { "r", "rZ", "rIK" } }, + { INDEX_op_nor_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_not_i32, { "r", "rZ" } }, + { INDEX_op_or_i32, { "r", "rZ", "rIZ" } }, + { INDEX_op_xor_i32, { "r", "rZ", "rIZ" } }, + + { INDEX_op_shl_i32, { "r", "rZ", "ri" } }, + { INDEX_op_shr_i32, { "r", "rZ", "ri" } }, + { INDEX_op_sar_i32, { "r", "rZ", "ri" } }, + { INDEX_op_rotr_i32, { "r", "rZ", "ri" } }, + { INDEX_op_rotl_i32, { "r", "rZ", "ri" } }, + + { INDEX_op_bswap16_i32, { "r", "r" } }, + { INDEX_op_bswap32_i32, { "r", "r" } }, + + { INDEX_op_ext8s_i32, { "r", "rZ" } }, + { INDEX_op_ext16s_i32, { "r", "rZ" } }, + + { INDEX_op_deposit_i32, { "r", "0", "rZ" } }, + + { INDEX_op_brcond_i32, { "rZ", "rZ" } }, +#if use_mips32r6_instructions + { INDEX_op_movcond_i32, { "r", "rZ", "rZ", "rZ", "rZ" } }, +#else + { INDEX_op_movcond_i32, { "r", "rZ", "rZ", "rZ", "0" } }, +#endif + { INDEX_op_setcond_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_setcond2_i32, { "r", "rZ", "rZ", "rZ", "rZ" } }, + + { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rN", "rN" } }, + { INDEX_op_sub2_i32, { "r", "r", "rZ", "rZ", "rN", "rN" } }, + { INDEX_op_brcond2_i32, { "rZ", "rZ", "rZ", "rZ" } }, + +#if TARGET_LONG_BITS == 32 + { INDEX_op_qemu_ld_i32, { "L", "lZ" } }, + { INDEX_op_qemu_st_i32, { "SZ", "SZ" } }, + { INDEX_op_qemu_ld_i64, { "L", "L", "lZ" } }, + { INDEX_op_qemu_st_i64, { "SZ", "SZ", "SZ" } }, +#else + { INDEX_op_qemu_ld_i32, { "L", "lZ", "lZ" } }, + { INDEX_op_qemu_st_i32, { "SZ", "SZ", "SZ" } }, + { INDEX_op_qemu_ld_i64, { "L", "L", "lZ", "lZ" } }, + { INDEX_op_qemu_st_i64, { "SZ", "SZ", "SZ", "SZ" } }, +#endif + { -1 }, +}; + +static int tcg_target_callee_save_regs[] = { + TCG_REG_S0, /* used for the global env (TCG_AREG0) */ + TCG_REG_S1, + TCG_REG_S2, + TCG_REG_S3, + TCG_REG_S4, + TCG_REG_S5, + TCG_REG_S6, + TCG_REG_S7, + TCG_REG_S8, + TCG_REG_RA, /* should be last for ABI compliance */ +}; + +/* The Linux kernel doesn't provide any information about the available + instruction set. Probe it using a signal handler. */ + + +#ifndef use_movnz_instructions +bool use_movnz_instructions = false; +#endif + +#ifndef use_mips32_instructions +bool use_mips32_instructions = false; +#endif + +#ifndef use_mips32r2_instructions +bool use_mips32r2_instructions = false; +#endif + +static volatile sig_atomic_t got_sigill; + +static void sigill_handler(int signo, siginfo_t *si, void *data) +{ + /* Skip the faulty instruction */ + ucontext_t *uc = (ucontext_t *)data; + uc->uc_mcontext.pc += 4; + + got_sigill = 1; +} + +static void tcg_target_detect_isa(void) +{ + struct sigaction sa_old, sa_new; + + memset(&sa_new, 0, sizeof(sa_new)); + sa_new.sa_flags = SA_SIGINFO; + sa_new.sa_sigaction = sigill_handler; + sigaction(SIGILL, &sa_new, &sa_old); + + /* Probe for movn/movz, necessary to implement movcond. */ +#ifndef use_movnz_instructions + got_sigill = 0; + asm volatile(".set push\n" + ".set mips32\n" + "movn $zero, $zero, $zero\n" + "movz $zero, $zero, $zero\n" + ".set pop\n" + : : : ); + use_movnz_instructions = !got_sigill; +#endif + + /* Probe for MIPS32 instructions. As no subsetting is allowed + by the specification, it is only necessary to probe for one + of the instructions. */ +#ifndef use_mips32_instructions + got_sigill = 0; + asm volatile(".set push\n" + ".set mips32\n" + "mul $zero, $zero\n" + ".set pop\n" + : : : ); + use_mips32_instructions = !got_sigill; +#endif + + /* Probe for MIPS32r2 instructions if MIPS32 instructions are + available. As no subsetting is allowed by the specification, + it is only necessary to probe for one of the instructions. */ +#ifndef use_mips32r2_instructions + if (use_mips32_instructions) { + got_sigill = 0; + asm volatile(".set push\n" + ".set mips32r2\n" + "seb $zero, $zero\n" + ".set pop\n" + : : : ); + use_mips32r2_instructions = !got_sigill; + } +#endif + + sigaction(SIGILL, &sa_old, NULL); +} + +/* Generate global QEMU prologue and epilogue code */ +static void tcg_target_qemu_prologue(TCGContext *s) +{ + int i, frame_size; + + /* reserve some stack space, also for TCG temps. */ + frame_size = ARRAY_SIZE(tcg_target_callee_save_regs) * 4 + + TCG_STATIC_CALL_ARGS_SIZE + + CPU_TEMP_BUF_NLONGS * sizeof(long); + frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) & + ~(TCG_TARGET_STACK_ALIGN - 1); + tcg_set_frame(s, TCG_REG_SP, ARRAY_SIZE(tcg_target_callee_save_regs) * 4 + + TCG_STATIC_CALL_ARGS_SIZE, + CPU_TEMP_BUF_NLONGS * sizeof(long)); + + /* TB prologue */ + tcg_out_addi(s, TCG_REG_SP, -frame_size); + for(i = 0 ; i < ARRAY_SIZE(tcg_target_callee_save_regs) ; i++) { + tcg_out_st(s, TCG_TYPE_I32, tcg_target_callee_save_regs[i], + TCG_REG_SP, TCG_STATIC_CALL_ARGS_SIZE + i * 4); + } + + /* Call generated code */ + tcg_out_opc_reg(s, OPC_JR, 0, tcg_target_call_iarg_regs[1], 0); + tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); + tb_ret_addr = s->code_ptr; + + /* TB epilogue */ + for(i = 0 ; i < ARRAY_SIZE(tcg_target_callee_save_regs) ; i++) { + tcg_out_ld(s, TCG_TYPE_I32, tcg_target_callee_save_regs[i], + TCG_REG_SP, TCG_STATIC_CALL_ARGS_SIZE + i * 4); + } + + tcg_out_opc_reg(s, OPC_JR, 0, TCG_REG_RA, 0); + tcg_out_addi(s, TCG_REG_SP, frame_size); +} + +static void tcg_target_init(TCGContext *s) +{ + tcg_target_detect_isa(); + tcg_regset_set(tcg_target_available_regs[TCG_TYPE_I32], 0xffffffff); + tcg_regset_set(tcg_target_call_clobber_regs, + (1 << TCG_REG_V0) | + (1 << TCG_REG_V1) | + (1 << TCG_REG_A0) | + (1 << TCG_REG_A1) | + (1 << TCG_REG_A2) | + (1 << TCG_REG_A3) | + (1 << TCG_REG_T0) | + (1 << TCG_REG_T1) | + (1 << TCG_REG_T2) | + (1 << TCG_REG_T3) | + (1 << TCG_REG_T4) | + (1 << TCG_REG_T5) | + (1 << TCG_REG_T6) | + (1 << TCG_REG_T7) | + (1 << TCG_REG_T8) | + (1 << TCG_REG_T9)); + + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_ZERO); /* zero register */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_K0); /* kernel use only */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_K1); /* kernel use only */ + tcg_regset_set_reg(s->reserved_regs, TCG_TMP0); /* internal use */ + tcg_regset_set_reg(s->reserved_regs, TCG_TMP1); /* internal use */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_RA); /* return address */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP); /* stack pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_GP); /* global pointer */ + + tcg_add_target_add_op_defs(mips_op_defs); +} + +void tb_set_jmp_target1(uintptr_t jmp_addr, uintptr_t addr) +{ + uint32_t *ptr = (uint32_t *)jmp_addr; + *ptr = deposit32(*ptr, 0, 26, addr >> 2); + flush_icache_range(jmp_addr, jmp_addr + 4); +} diff --git a/qemu/tcg/optimize.c b/qemu/tcg/optimize.c index 18283cfd7..f01160815 100644 --- a/qemu/tcg/optimize.c +++ b/qemu/tcg/optimize.c @@ -23,10 +23,8 @@ * THE SOFTWARE. */ -#include "config.h" +#include "qemu/osdep.h" -#include -#include #include "qemu-common.h" #include "tcg-op.h" @@ -35,14 +33,8 @@ glue(glue(case INDEX_op_, x), _i32): \ glue(glue(case INDEX_op_, x), _i64) -typedef enum { - TCG_TEMP_UNDEF = 0, - TCG_TEMP_CONST, - TCG_TEMP_COPY, -} tcg_temp_state; - struct tcg_temp_info { - tcg_temp_state state; + bool is_const; uint16_t prev_copy; uint16_t next_copy; tcg_target_ulong val; @@ -50,23 +42,47 @@ struct tcg_temp_info { }; static struct tcg_temp_info temps[TCG_MAX_TEMPS]; +static TCGTempSet temps_used; + +static inline bool temp_is_const(TCGArg arg) +{ + return temps[arg].is_const; +} + +static inline bool temp_is_copy(TCGArg arg) +{ + return temps[arg].next_copy != arg; +} -/* Reset TEMP's state to TCG_TEMP_UNDEF. If TEMP only had one copy, remove - the copy flag from the left temp. */ +/* Reset TEMP's state, possibly removing the temp for the list of copies. */ static void reset_temp(TCGArg temp) { - if (temps[temp].state == TCG_TEMP_COPY) { - if (temps[temp].prev_copy == temps[temp].next_copy) { - temps[temps[temp].next_copy].state = TCG_TEMP_UNDEF; - } else { - temps[temps[temp].next_copy].prev_copy = temps[temp].prev_copy; - temps[temps[temp].prev_copy].next_copy = temps[temp].next_copy; - } - } - temps[temp].state = TCG_TEMP_UNDEF; + temps[temps[temp].next_copy].prev_copy = temps[temp].prev_copy; + temps[temps[temp].prev_copy].next_copy = temps[temp].next_copy; + temps[temp].next_copy = temp; + temps[temp].prev_copy = temp; + temps[temp].is_const = false; temps[temp].mask = -1; } +/* Reset all temporaries, given that there are NB_TEMPS of them. */ +static void reset_all_temps(int nb_temps) +{ + bitmap_zero(temps_used.l, nb_temps); +} + +/* Initialize and activate a temporary. */ +static void init_temp_info(TCGArg temp) +{ + if (!test_bit(temp, temps_used.l)) { + temps[temp].next_copy = temp; + temps[temp].prev_copy = temp; + temps[temp].is_const = false; + temps[temp].mask = -1; + set_bit(temp, temps_used.l); + } +} + static TCGOp *insert_op_before(TCGContext *s, TCGOp *old_op, TCGOpcode opc, int nargs) { @@ -98,16 +114,6 @@ static TCGOp *insert_op_before(TCGContext *s, TCGOp *old_op, return new_op; } -/* Reset all temporaries, given that there are NB_TEMPS of them. */ -static void reset_all_temps(int nb_temps) -{ - int i; - for (i = 0; i < nb_temps; i++) { - temps[i].state = TCG_TEMP_UNDEF; - temps[i].mask = -1; - } -} - static int op_bits(TCGOpcode op) { const TCGOpDef *def = &tcg_op_defs[op]; @@ -179,8 +185,7 @@ static bool temps_are_copies(TCGArg arg1, TCGArg arg2) return true; } - if (temps[arg1].state != TCG_TEMP_COPY - || temps[arg2].state != TCG_TEMP_COPY) { + if (!temp_is_copy(arg1) || !temp_is_copy(arg2)) { return false; } @@ -202,7 +207,7 @@ static void tcg_opt_gen_movi(TCGContext *s, TCGOp *op, TCGArg *args, op->opc = new_op; reset_temp(dst); - temps[dst].state = TCG_TEMP_CONST; + temps[dst].is_const = true; temps[dst].val = val; mask = val; if (TCG_TARGET_REG_BITS > 32 && new_op == INDEX_op_movi_i32) { @@ -223,11 +228,6 @@ static void tcg_opt_gen_mov(TCGContext *s, TCGOp *op, TCGArg *args, return; } - if (temps[src].state == TCG_TEMP_CONST) { - tcg_opt_gen_movi(s, op, args, dst, temps[src].val); - return; - } - TCGOpcode new_op = op_to_mov(op->opc); tcg_target_ulong mask; @@ -241,19 +241,13 @@ static void tcg_opt_gen_mov(TCGContext *s, TCGOp *op, TCGArg *args, } temps[dst].mask = mask; - assert(temps[src].state != TCG_TEMP_CONST); - if (s->temps[src].type == s->temps[dst].type) { - if (temps[src].state != TCG_TEMP_COPY) { - temps[src].state = TCG_TEMP_COPY; - temps[src].next_copy = src; - temps[src].prev_copy = src; - } - temps[dst].state = TCG_TEMP_COPY; temps[dst].next_copy = temps[src].next_copy; temps[dst].prev_copy = src; temps[temps[dst].next_copy].prev_copy = dst; temps[src].next_copy = dst; + temps[dst].is_const = temps[src].is_const; + temps[dst].val = temps[src].val; } args[0] = dst; @@ -292,7 +286,6 @@ static TCGArg do_constant_folding_2(TCGOpcode op, TCGArg x, TCGArg y) case INDEX_op_shr_i32: return (uint32_t)x >> (y & 31); - case INDEX_op_trunc_shr_i32: case INDEX_op_shr_i64: return (uint64_t)x >> (y & 63); @@ -347,12 +340,18 @@ static TCGArg do_constant_folding_2(TCGOpcode op, TCGArg x, TCGArg y) CASE_OP_32_64(ext16u): return (uint16_t)x; + case INDEX_op_ext_i32_i64: case INDEX_op_ext32s_i64: return (int32_t)x; + case INDEX_op_extu_i32_i64: + case INDEX_op_extrl_i64_i32: case INDEX_op_ext32u_i64: return (uint32_t)x; + case INDEX_op_extrh_i64_i32: + return (uint64_t)x >> 32; + case INDEX_op_muluh_i32: return ((uint64_t)(uint32_t)x * (uint32_t)y) >> 32; case INDEX_op_mulsh_i32: @@ -395,7 +394,7 @@ static TCGArg do_constant_folding(TCGOpcode op, TCGArg x, TCGArg y) { TCGArg res = do_constant_folding_2(op, x, y); if (op_bits(op) == 32) { - res &= 0xffffffff; + res = (int32_t)res; } return res; } @@ -481,7 +480,7 @@ static bool do_constant_folding_cond_eq(TCGCond c) static TCGArg do_constant_folding_cond(TCGOpcode op, TCGArg x, TCGArg y, TCGCond c) { - if (temps[x].state == TCG_TEMP_CONST && temps[y].state == TCG_TEMP_CONST) { + if (temp_is_const(x) && temp_is_const(y)) { switch (op_bits(op)) { case 32: return do_constant_folding_cond_32(temps[x].val, temps[y].val, c); @@ -492,7 +491,7 @@ static TCGArg do_constant_folding_cond(TCGOpcode op, TCGArg x, } } else if (temps_are_copies(x, y)) { return do_constant_folding_cond_eq(c); - } else if (temps[y].state == TCG_TEMP_CONST && temps[y].val == 0) { + } else if (temp_is_const(y) && temps[y].val == 0) { switch (c) { case TCG_COND_LTU: return 0; @@ -513,12 +512,10 @@ static TCGArg do_constant_folding_cond2(TCGArg *p1, TCGArg *p2, TCGCond c) TCGArg al = p1[0], ah = p1[1]; TCGArg bl = p2[0], bh = p2[1]; - if (temps[bl].state == TCG_TEMP_CONST - && temps[bh].state == TCG_TEMP_CONST) { + if (temp_is_const(bl) && temp_is_const(bh)) { uint64_t b = ((uint64_t)temps[bh].val << 32) | (uint32_t)temps[bl].val; - if (temps[al].state == TCG_TEMP_CONST - && temps[ah].state == TCG_TEMP_CONST) { + if (temp_is_const(al) && temp_is_const(ah)) { uint64_t a; a = ((uint64_t)temps[ah].val << 32) | (uint32_t)temps[al].val; return do_constant_folding_cond_64(a, b, c); @@ -544,8 +541,8 @@ static bool swap_commutative(TCGArg dest, TCGArg *p1, TCGArg *p2) { TCGArg a1 = *p1, a2 = *p2; int sum = 0; - sum += temps[a1].state == TCG_TEMP_CONST; - sum -= temps[a2].state == TCG_TEMP_CONST; + sum += temp_is_const(a1); + sum -= temp_is_const(a2); /* Prefer the constant in second argument, and then the form op a, a, b, which is better handled on non-RISC hosts. */ @@ -560,10 +557,10 @@ static bool swap_commutative(TCGArg dest, TCGArg *p1, TCGArg *p2) static bool swap_commutative2(TCGArg *p1, TCGArg *p2) { int sum = 0; - sum += temps[p1[0]].state == TCG_TEMP_CONST; - sum += temps[p1[1]].state == TCG_TEMP_CONST; - sum -= temps[p2[0]].state == TCG_TEMP_CONST; - sum -= temps[p2[1]].state == TCG_TEMP_CONST; + sum += temp_is_const(p1[0]); + sum += temp_is_const(p1[1]); + sum -= temp_is_const(p2[0]); + sum -= temp_is_const(p2[1]); if (sum > 0) { TCGArg t; t = p1[0], p1[0] = p2[0], p2[0] = t; @@ -598,17 +595,29 @@ void tcg_optimize(TCGContext *s) const TCGOpDef *def = &tcg_op_defs[opc]; oi_next = op->next; + + /* Count the arguments, and initialize the temps that are + going to be used */ if (opc == INDEX_op_call) { nb_oargs = op->callo; nb_iargs = op->calli; + for (i = 0; i < nb_oargs + nb_iargs; i++) { + tmp = args[i]; + if (tmp != TCG_CALL_DUMMY_ARG) { + init_temp_info(tmp); + } + } } else { nb_oargs = def->nb_oargs; nb_iargs = def->nb_iargs; + for (i = 0; i < nb_oargs + nb_iargs; i++) { + init_temp_info(args[i]); + } } /* Do copy propagation */ for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { - if (temps[args[i]].state == TCG_TEMP_COPY) { + if (temp_is_copy(args[i])) { args[i] = find_better_copy(s, args[i]); } } @@ -678,8 +687,7 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(sar): CASE_OP_32_64(rotl): CASE_OP_32_64(rotr): - if (temps[args[1]].state == TCG_TEMP_CONST - && temps[args[1]].val == 0) { + if (temp_is_const(args[1]) && temps[args[1]].val == 0) { tcg_opt_gen_movi(s, op, args, args[0], 0); continue; } @@ -689,7 +697,7 @@ void tcg_optimize(TCGContext *s) TCGOpcode neg_op; bool have_neg; - if (temps[args[2]].state == TCG_TEMP_CONST) { + if (temp_is_const(args[2])) { /* Proceed with possible constant folding. */ break; } @@ -703,8 +711,7 @@ void tcg_optimize(TCGContext *s) if (!have_neg) { break; } - if (temps[args[1]].state == TCG_TEMP_CONST - && temps[args[1]].val == 0) { + if (temp_is_const(args[1]) && temps[args[1]].val == 0) { op->opc = neg_op; reset_temp(args[0]); args[1] = args[2]; @@ -714,34 +721,30 @@ void tcg_optimize(TCGContext *s) break; CASE_OP_32_64(xor): CASE_OP_32_64(nand): - if (temps[args[1]].state != TCG_TEMP_CONST - && temps[args[2]].state == TCG_TEMP_CONST - && temps[args[2]].val == -1) { + if (!temp_is_const(args[1]) + && temp_is_const(args[2]) && temps[args[2]].val == -1) { i = 1; goto try_not; } break; CASE_OP_32_64(nor): - if (temps[args[1]].state != TCG_TEMP_CONST - && temps[args[2]].state == TCG_TEMP_CONST - && temps[args[2]].val == 0) { + if (!temp_is_const(args[1]) + && temp_is_const(args[2]) && temps[args[2]].val == 0) { i = 1; goto try_not; } break; CASE_OP_32_64(andc): - if (temps[args[2]].state != TCG_TEMP_CONST - && temps[args[1]].state == TCG_TEMP_CONST - && temps[args[1]].val == -1) { + if (!temp_is_const(args[2]) + && temp_is_const(args[1]) && temps[args[1]].val == -1) { i = 2; goto try_not; } break; CASE_OP_32_64(orc): CASE_OP_32_64(eqv): - if (temps[args[2]].state != TCG_TEMP_CONST - && temps[args[1]].state == TCG_TEMP_CONST - && temps[args[1]].val == 0) { + if (!temp_is_const(args[2]) + && temp_is_const(args[1]) && temps[args[1]].val == 0) { i = 2; goto try_not; } @@ -782,9 +785,8 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(or): CASE_OP_32_64(xor): CASE_OP_32_64(andc): - if (temps[args[1]].state != TCG_TEMP_CONST - && temps[args[2]].state == TCG_TEMP_CONST - && temps[args[2]].val == 0) { + if (!temp_is_const(args[1]) + && temp_is_const(args[2]) && temps[args[2]].val == 0) { tcg_opt_gen_mov(s, op, args, args[0], args[1]); continue; } @@ -792,9 +794,8 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(and): CASE_OP_32_64(orc): CASE_OP_32_64(eqv): - if (temps[args[1]].state != TCG_TEMP_CONST - && temps[args[2]].state == TCG_TEMP_CONST - && temps[args[2]].val == -1) { + if (!temp_is_const(args[1]) + && temp_is_const(args[2]) && temps[args[2]].val == -1) { tcg_opt_gen_mov(s, op, args, args[0], args[1]); continue; } @@ -832,17 +833,26 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(and): mask = temps[args[2]].mask; - if (temps[args[2]].state == TCG_TEMP_CONST) { + if (temp_is_const(args[2])) { and_const: affected = temps[args[1]].mask & ~mask; } mask = temps[args[1]].mask & mask; break; + case INDEX_op_ext_i32_i64: + if ((temps[args[1]].mask & 0x80000000) != 0) { + break; + } + case INDEX_op_extu_i32_i64: + /* We do not compute affected as it is a size changing op. */ + mask = (uint32_t)temps[args[1]].mask; + break; + CASE_OP_32_64(andc): /* Known-zeros does not imply known-ones. Therefore unless args[2] is constant, we can't infer anything from it. */ - if (temps[args[2]].state == TCG_TEMP_CONST) { + if (temp_is_const(args[2])) { mask = ~temps[args[2]].mask; goto and_const; } @@ -851,37 +861,40 @@ void tcg_optimize(TCGContext *s) break; case INDEX_op_sar_i32: - if (temps[args[2]].state == TCG_TEMP_CONST) { + if (temp_is_const(args[2])) { tmp = temps[args[2]].val & 31; mask = (int32_t)temps[args[1]].mask >> tmp; } break; case INDEX_op_sar_i64: - if (temps[args[2]].state == TCG_TEMP_CONST) { + if (temp_is_const(args[2])) { tmp = temps[args[2]].val & 63; mask = (int64_t)temps[args[1]].mask >> tmp; } break; case INDEX_op_shr_i32: - if (temps[args[2]].state == TCG_TEMP_CONST) { + if (temp_is_const(args[2])) { tmp = temps[args[2]].val & 31; mask = (uint32_t)temps[args[1]].mask >> tmp; } break; case INDEX_op_shr_i64: - if (temps[args[2]].state == TCG_TEMP_CONST) { + if (temp_is_const(args[2])) { tmp = temps[args[2]].val & 63; mask = (uint64_t)temps[args[1]].mask >> tmp; } break; - case INDEX_op_trunc_shr_i32: - mask = (uint64_t)temps[args[1]].mask >> args[2]; + case INDEX_op_extrl_i64_i32: + mask = (uint32_t)temps[args[1]].mask; + break; + case INDEX_op_extrh_i64_i32: + mask = (uint64_t)temps[args[1]].mask >> 32; break; CASE_OP_32_64(shl): - if (temps[args[2]].state == TCG_TEMP_CONST) { + if (temp_is_const(args[2])) { tmp = temps[args[2]].val & (TCG_TARGET_REG_BITS - 1); mask = temps[args[1]].mask << tmp; } @@ -946,12 +959,12 @@ void tcg_optimize(TCGContext *s) } if (partmask == 0) { - assert(nb_oargs == 1); + tcg_debug_assert(nb_oargs == 1); tcg_opt_gen_movi(s, op, args, args[0], 0); continue; } if (affected == 0) { - assert(nb_oargs == 1); + tcg_debug_assert(nb_oargs == 1); tcg_opt_gen_mov(s, op, args, args[0], args[1]); continue; } @@ -962,8 +975,7 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(mul): CASE_OP_32_64(muluh): CASE_OP_32_64(mulsh): - if ((temps[args[2]].state == TCG_TEMP_CONST - && temps[args[2]].val == 0)) { + if ((temp_is_const(args[2]) && temps[args[2]].val == 0)) { tcg_opt_gen_movi(s, op, args, args[0], 0); continue; } @@ -1018,21 +1030,17 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(ext16u): case INDEX_op_ext32s_i64: case INDEX_op_ext32u_i64: - if (temps[args[1]].state == TCG_TEMP_CONST) { + case INDEX_op_ext_i32_i64: + case INDEX_op_extu_i32_i64: + case INDEX_op_extrl_i64_i32: + case INDEX_op_extrh_i64_i32: + if (temp_is_const(args[1])) { tmp = do_constant_folding(opc, temps[args[1]].val, 0); tcg_opt_gen_movi(s, op, args, args[0], tmp); break; } goto do_default; - case INDEX_op_trunc_shr_i32: - if (temps[args[1]].state == TCG_TEMP_CONST) { - tmp = do_constant_folding(opc, temps[args[1]].val, args[2]); - tcg_opt_gen_movi(s, op, args, args[0], tmp); - break; - } - goto do_default; - CASE_OP_32_64(add): CASE_OP_32_64(sub): CASE_OP_32_64(mul): @@ -1055,8 +1063,7 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(divu): CASE_OP_32_64(rem): CASE_OP_32_64(remu): - if (temps[args[1]].state == TCG_TEMP_CONST - && temps[args[2]].state == TCG_TEMP_CONST) { + if (temp_is_const(args[1]) && temp_is_const(args[2])) { tmp = do_constant_folding(opc, temps[args[1]].val, temps[args[2]].val); tcg_opt_gen_movi(s, op, args, args[0], tmp); @@ -1065,8 +1072,7 @@ void tcg_optimize(TCGContext *s) goto do_default; CASE_OP_32_64(deposit): - if (temps[args[1]].state == TCG_TEMP_CONST - && temps[args[2]].state == TCG_TEMP_CONST) { + if (temp_is_const(args[1]) && temp_is_const(args[2])) { tmp = deposit64(temps[args[1]].val, args[3], args[4], temps[args[2]].val); tcg_opt_gen_movi(s, op, args, args[0], tmp); @@ -1106,10 +1112,8 @@ void tcg_optimize(TCGContext *s) case INDEX_op_add2_i32: case INDEX_op_sub2_i32: - if (temps[args[2]].state == TCG_TEMP_CONST - && temps[args[3]].state == TCG_TEMP_CONST - && temps[args[4]].state == TCG_TEMP_CONST - && temps[args[5]].state == TCG_TEMP_CONST) { + if (temp_is_const(args[2]) && temp_is_const(args[3]) + && temp_is_const(args[4]) && temp_is_const(args[5])) { uint32_t al = temps[args[2]].val; uint32_t ah = temps[args[3]].val; uint32_t bl = temps[args[4]].val; @@ -1128,8 +1132,8 @@ void tcg_optimize(TCGContext *s) rl = args[0]; rh = args[1]; - tcg_opt_gen_movi(s, op, args, rl, (uint32_t)a); - tcg_opt_gen_movi(s, op2, args2, rh, (uint32_t)(a >> 32)); + tcg_opt_gen_movi(s, op, args, rl, (int32_t)a); + tcg_opt_gen_movi(s, op2, args2, rh, (int32_t)(a >> 32)); /* We've done all we need to do with the movi. Skip it. */ oi_next = op2->next; @@ -1138,8 +1142,7 @@ void tcg_optimize(TCGContext *s) goto do_default; case INDEX_op_mulu2_i32: - if (temps[args[2]].state == TCG_TEMP_CONST - && temps[args[3]].state == TCG_TEMP_CONST) { + if (temp_is_const(args[2]) && temp_is_const(args[3])) { uint32_t a = temps[args[2]].val; uint32_t b = temps[args[3]].val; uint64_t r = (uint64_t)a * b; @@ -1149,8 +1152,8 @@ void tcg_optimize(TCGContext *s) rl = args[0]; rh = args[1]; - tcg_opt_gen_movi(s, op, args, rl, (uint32_t)r); - tcg_opt_gen_movi(s, op2, args2, rh, (uint32_t)(r >> 32)); + tcg_opt_gen_movi(s, op, args, rl, (int32_t)r); + tcg_opt_gen_movi(s, op2, args2, rh, (int32_t)(r >> 32)); /* We've done all we need to do with the movi. Skip it. */ oi_next = op2->next; @@ -1171,10 +1174,8 @@ void tcg_optimize(TCGContext *s) tcg_op_remove(s, op); } } else if ((args[4] == TCG_COND_LT || args[4] == TCG_COND_GE) - && temps[args[2]].state == TCG_TEMP_CONST - && temps[args[3]].state == TCG_TEMP_CONST - && temps[args[2]].val == 0 - && temps[args[3]].val == 0) { + && temp_is_const(args[2]) && temps[args[2]].val == 0 + && temp_is_const(args[3]) && temps[args[3]].val == 0) { /* Simplify LT/GE comparisons vs zero to a single compare vs the high word of the input. */ do_brcond_high: @@ -1236,10 +1237,8 @@ void tcg_optimize(TCGContext *s) do_setcond_const: tcg_opt_gen_movi(s, op, args, args[0], tmp); } else if ((args[5] == TCG_COND_LT || args[5] == TCG_COND_GE) - && temps[args[3]].state == TCG_TEMP_CONST - && temps[args[4]].state == TCG_TEMP_CONST - && temps[args[3]].val == 0 - && temps[args[4]].val == 0) { + && temp_is_const(args[3]) && temps[args[3]].val == 0 + && temp_is_const(args[4]) && temps[args[4]].val == 0) { /* Simplify LT/GE comparisons vs zero to a single compare vs the high word of the input. */ do_setcond_high: @@ -1299,7 +1298,9 @@ void tcg_optimize(TCGContext *s) if (!(args[nb_oargs + nb_iargs + 1] & (TCG_CALL_NO_READ_GLOBALS | TCG_CALL_NO_WRITE_GLOBALS))) { for (i = 0; i < nb_globals; i++) { - reset_temp(i); + if (test_bit(i, temps_used.l)) { + reset_temp(i); + } } } goto do_reset_output; diff --git a/qemu/tcg/ppc/tcg-target.c b/qemu/tcg/ppc/tcg-target.c deleted file mode 100644 index 2b6eafa03..000000000 --- a/qemu/tcg/ppc/tcg-target.c +++ /dev/null @@ -1,2722 +0,0 @@ -/* - * Tiny Code Generator for QEMU - * - * Copyright (c) 2008 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "tcg-be-ldst.h" - -#if defined _CALL_DARWIN || defined __APPLE__ -#define TCG_TARGET_CALL_DARWIN -#endif -#ifdef _CALL_SYSV -# define TCG_TARGET_CALL_ALIGN_ARGS 1 -#endif - -/* For some memory operations, we need a scratch that isn't R0. For the AIX - calling convention, we can re-use the TOC register since we'll be reloading - it at every call. Otherwise R12 will do nicely as neither a call-saved - register nor a parameter register. */ -#ifdef _CALL_AIX -# define TCG_REG_TMP1 TCG_REG_R2 -#else -# define TCG_REG_TMP1 TCG_REG_R12 -#endif - -/* For the 64-bit target, we don't like the 5 insn sequence needed to build - full 64-bit addresses. Better to have a base register to which we can - apply a 32-bit displacement. - - There are generally three items of interest: - (1) helper functions in the main executable, - (2) TranslationBlock data structures, - (3) the return address in the epilogue. - - For user-only, we USE_STATIC_CODE_GEN_BUFFER, so the code_gen_buffer - will be inside the main executable, and thus near enough to make a - pointer to the epilogue be within 2GB of all helper functions. - - For softmmu, we'll let the kernel choose the address of code_gen_buffer, - and odds are it'll be somewhere close to the main malloc arena, and so - a pointer to the epilogue will be within 2GB of the TranslationBlocks. - - For --enable-pie, everything will be kinda near everything else, - somewhere in high memory. - - Thus we choose to keep the return address in a call-saved register. */ -#define TCG_REG_RA TCG_REG_R31 -#define USE_REG_RA (TCG_TARGET_REG_BITS == 64) - -/* Shorthand for size of a pointer. Avoid promotion to unsigned. */ -#define SZP ((int)sizeof(void *)) - -/* Shorthand for size of a register. */ -#define SZR (TCG_TARGET_REG_BITS / 8) - -#define TCG_CT_CONST_S16 0x100 -#define TCG_CT_CONST_U16 0x200 -#define TCG_CT_CONST_S32 0x400 -#define TCG_CT_CONST_U32 0x800 -#define TCG_CT_CONST_ZERO 0x1000 -#define TCG_CT_CONST_MONE 0x2000 - -static tcg_insn_unit *tb_ret_addr; - -#ifndef GUEST_BASE -#define GUEST_BASE 0 -#endif - -#include "elf.h" -static bool have_isa_2_06; -#define HAVE_ISA_2_06 have_isa_2_06 -#define HAVE_ISEL have_isa_2_06 - -#ifdef CONFIG_USE_GUEST_BASE -#define TCG_GUEST_BASE_REG 30 -#else -#define TCG_GUEST_BASE_REG 0 -#endif - -#ifndef NDEBUG -static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { - "r0", - "r1", - "r2", - "r3", - "r4", - "r5", - "r6", - "r7", - "r8", - "r9", - "r10", - "r11", - "r12", - "r13", - "r14", - "r15", - "r16", - "r17", - "r18", - "r19", - "r20", - "r21", - "r22", - "r23", - "r24", - "r25", - "r26", - "r27", - "r28", - "r29", - "r30", - "r31" -}; -#endif - -static const int tcg_target_reg_alloc_order[] = { - TCG_REG_R14, /* call saved registers */ - TCG_REG_R15, - TCG_REG_R16, - TCG_REG_R17, - TCG_REG_R18, - TCG_REG_R19, - TCG_REG_R20, - TCG_REG_R21, - TCG_REG_R22, - TCG_REG_R23, - TCG_REG_R24, - TCG_REG_R25, - TCG_REG_R26, - TCG_REG_R27, - TCG_REG_R28, - TCG_REG_R29, - TCG_REG_R30, - TCG_REG_R31, - TCG_REG_R12, /* call clobbered, non-arguments */ - TCG_REG_R11, - TCG_REG_R2, - TCG_REG_R13, - TCG_REG_R10, /* call clobbered, arguments */ - TCG_REG_R9, - TCG_REG_R8, - TCG_REG_R7, - TCG_REG_R6, - TCG_REG_R5, - TCG_REG_R4, - TCG_REG_R3, -}; - -static const int tcg_target_call_iarg_regs[] = { - TCG_REG_R3, - TCG_REG_R4, - TCG_REG_R5, - TCG_REG_R6, - TCG_REG_R7, - TCG_REG_R8, - TCG_REG_R9, - TCG_REG_R10 -}; - -static const int tcg_target_call_oarg_regs[] = { - TCG_REG_R3, - TCG_REG_R4 -}; - -static const int tcg_target_callee_save_regs[] = { -#ifdef TCG_TARGET_CALL_DARWIN - TCG_REG_R11, -#endif - TCG_REG_R14, - TCG_REG_R15, - TCG_REG_R16, - TCG_REG_R17, - TCG_REG_R18, - TCG_REG_R19, - TCG_REG_R20, - TCG_REG_R21, - TCG_REG_R22, - TCG_REG_R23, - TCG_REG_R24, - TCG_REG_R25, - TCG_REG_R26, - TCG_REG_R27, /* currently used for the global env */ - TCG_REG_R28, - TCG_REG_R29, - TCG_REG_R30, - TCG_REG_R31 -}; - -static inline bool in_range_b(tcg_target_long target) -{ - return target == sextract64(target, 0, 26); -} - -static uint32_t reloc_pc24_val(tcg_insn_unit *pc, tcg_insn_unit *target) -{ - ptrdiff_t disp = tcg_ptr_byte_diff(target, pc); - assert(in_range_b(disp)); - return disp & 0x3fffffc; -} - -static void reloc_pc24(tcg_insn_unit *pc, tcg_insn_unit *target) -{ - *pc = (*pc & ~0x3fffffc) | reloc_pc24_val(pc, target); -} - -static uint16_t reloc_pc14_val(tcg_insn_unit *pc, tcg_insn_unit *target) -{ - ptrdiff_t disp = tcg_ptr_byte_diff(target, pc); - assert(disp == (int16_t) disp); - return disp & 0xfffc; -} - -static void reloc_pc14(tcg_insn_unit *pc, tcg_insn_unit *target) -{ - *pc = (*pc & ~0xfffc) | reloc_pc14_val(pc, target); -} - -static inline void tcg_out_b_noaddr(TCGContext *s, int insn) -{ - unsigned retrans = *s->code_ptr & 0x3fffffc; - tcg_out32(s, insn | retrans); -} - -static inline void tcg_out_bc_noaddr(TCGContext *s, int insn) -{ - unsigned retrans = *s->code_ptr & 0xfffc; - tcg_out32(s, insn | retrans); -} - -static void patch_reloc(tcg_insn_unit *code_ptr, int type, - intptr_t value, intptr_t addend) -{ - tcg_insn_unit *target = (tcg_insn_unit *)value; - - assert(addend == 0); - switch (type) { - case R_PPC_REL14: - reloc_pc14(code_ptr, target); - break; - case R_PPC_REL24: - reloc_pc24(code_ptr, target); - break; - default: - tcg_abort(); - } -} - -/* parse target specific constraints */ -static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) -{ - const char *ct_str; - - ct_str = *pct_str; - switch (ct_str[0]) { - case 'A': case 'B': case 'C': case 'D': - ct->ct |= TCG_CT_REG; - tcg_regset_set_reg(ct->u.regs, 3 + ct_str[0] - 'A'); - break; - case 'r': - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xffffffff); - break; - case 'L': /* qemu_ld constraint */ - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xffffffff); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3); -#ifdef CONFIG_SOFTMMU - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R5); -#endif - break; - case 'S': /* qemu_st constraint */ - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xffffffff); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3); -#ifdef CONFIG_SOFTMMU - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R5); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R6); -#endif - break; - case 'I': - ct->ct |= TCG_CT_CONST_S16; - break; - case 'J': - ct->ct |= TCG_CT_CONST_U16; - break; - case 'M': - ct->ct |= TCG_CT_CONST_MONE; - break; - case 'T': - ct->ct |= TCG_CT_CONST_S32; - break; - case 'U': - ct->ct |= TCG_CT_CONST_U32; - break; - case 'Z': - ct->ct |= TCG_CT_CONST_ZERO; - break; - default: - return -1; - } - ct_str++; - *pct_str = ct_str; - return 0; -} - -/* test if a constant matches the constraint */ -static int tcg_target_const_match(tcg_target_long val, TCGType type, - const TCGArgConstraint *arg_ct) -{ - int ct = arg_ct->ct; - if (ct & TCG_CT_CONST) { - return 1; - } - - /* The only 32-bit constraint we use aside from - TCG_CT_CONST is TCG_CT_CONST_S16. */ - if (type == TCG_TYPE_I32) { - val = (int32_t)val; - } - - if ((ct & TCG_CT_CONST_S16) && val == (int16_t)val) { - return 1; - } else if ((ct & TCG_CT_CONST_U16) && val == (uint16_t)val) { - return 1; - } else if ((ct & TCG_CT_CONST_S32) && val == (int32_t)val) { - return 1; - } else if ((ct & TCG_CT_CONST_U32) && val == (uint32_t)val) { - return 1; - } else if ((ct & TCG_CT_CONST_ZERO) && val == 0) { - return 1; - } else if ((ct & TCG_CT_CONST_MONE) && val == -1) { - return 1; - } - return 0; -} - -#define OPCD(opc) ((opc)<<26) -#define XO19(opc) (OPCD(19)|((opc)<<1)) -#define MD30(opc) (OPCD(30)|((opc)<<2)) -#define MDS30(opc) (OPCD(30)|((opc)<<1)) -#define XO31(opc) (OPCD(31)|((opc)<<1)) -#define XO58(opc) (OPCD(58)|(opc)) -#define XO62(opc) (OPCD(62)|(opc)) - -#define B OPCD( 18) -#define BC OPCD( 16) -#define LBZ OPCD( 34) -#define LHZ OPCD( 40) -#define LHA OPCD( 42) -#define LWZ OPCD( 32) -#define STB OPCD( 38) -#define STH OPCD( 44) -#define STW OPCD( 36) - -#define STD XO62( 0) -#define STDU XO62( 1) -#define STDX XO31(149) - -#define LD XO58( 0) -#define LDX XO31( 21) -#define LDU XO58( 1) -#define LWA XO58( 2) -#define LWAX XO31(341) - -#define ADDIC OPCD( 12) -#define ADDI OPCD( 14) -#define ADDIS OPCD( 15) -#define ORI OPCD( 24) -#define ORIS OPCD( 25) -#define XORI OPCD( 26) -#define XORIS OPCD( 27) -#define ANDI OPCD( 28) -#define ANDIS OPCD( 29) -#define MULLI OPCD( 7) -#define CMPLI OPCD( 10) -#define CMPI OPCD( 11) -#define SUBFIC OPCD( 8) - -#define LWZU OPCD( 33) -#define STWU OPCD( 37) - -#define RLWIMI OPCD( 20) -#define RLWINM OPCD( 21) -#define RLWNM OPCD( 23) - -#define RLDICL MD30( 0) -#define RLDICR MD30( 1) -#define RLDIMI MD30( 3) -#define RLDCL MDS30( 8) - -#define BCLR XO19( 16) -#define BCCTR XO19(528) -#define CRAND XO19(257) -#define CRANDC XO19(129) -#define CRNAND XO19(225) -#define CROR XO19(449) -#define CRNOR XO19( 33) - -#define EXTSB XO31(954) -#define EXTSH XO31(922) -#define EXTSW XO31(986) -#define ADD XO31(266) -#define ADDE XO31(138) -#define ADDME XO31(234) -#define ADDZE XO31(202) -#define ADDC XO31( 10) -#define AND XO31( 28) -#define SUBF XO31( 40) -#define SUBFC XO31( 8) -#define SUBFE XO31(136) -#define SUBFME XO31(232) -#define SUBFZE XO31(200) -#define OR XO31(444) -#define XOR XO31(316) -#define MULLW XO31(235) -#define MULHW XO31( 75) -#define MULHWU XO31( 11) -#define DIVW XO31(491) -#define DIVWU XO31(459) -#define CMP XO31( 0) -#define CMPL XO31( 32) -#define LHBRX XO31(790) -#define LWBRX XO31(534) -#define LDBRX XO31(532) -#define STHBRX XO31(918) -#define STWBRX XO31(662) -#define STDBRX XO31(660) -#define MFSPR XO31(339) -#define MTSPR XO31(467) -#define SRAWI XO31(824) -#define NEG XO31(104) -#define MFCR XO31( 19) -#define MFOCRF (MFCR | (1u << 20)) -#define NOR XO31(124) -#define CNTLZW XO31( 26) -#define CNTLZD XO31( 58) -#define ANDC XO31( 60) -#define ORC XO31(412) -#define EQV XO31(284) -#define NAND XO31(476) -#define ISEL XO31( 15) - -#define MULLD XO31(233) -#define MULHD XO31( 73) -#define MULHDU XO31( 9) -#define DIVD XO31(489) -#define DIVDU XO31(457) - -#define LBZX XO31( 87) -#define LHZX XO31(279) -#define LHAX XO31(343) -#define LWZX XO31( 23) -#define STBX XO31(215) -#define STHX XO31(407) -#define STWX XO31(151) - -#define SPR(a, b) ((((a)<<5)|(b))<<11) -#define LR SPR(8, 0) -#define CTR SPR(9, 0) - -#define SLW XO31( 24) -#define SRW XO31(536) -#define SRAW XO31(792) - -#define SLD XO31( 27) -#define SRD XO31(539) -#define SRAD XO31(794) -#define SRADI XO31(413<<1) - -#define TW XO31( 4) -#define TRAP (TW | TO(31)) - -#define NOP ORI /* ori 0,0,0 */ - -#define RT(r) ((r)<<21) -#define RS(r) ((r)<<21) -#define RA(r) ((r)<<16) -#define RB(r) ((r)<<11) -#define TO(t) ((t)<<21) -#define SH(s) ((s)<<11) -#define MB(b) ((b)<<6) -#define ME(e) ((e)<<1) -#define BO(o) ((o)<<21) -#define MB64(b) ((b)<<5) -#define FXM(b) (1 << (19 - (b))) - -#define LK 1 - -#define TAB(t, a, b) (RT(t) | RA(a) | RB(b)) -#define SAB(s, a, b) (RS(s) | RA(a) | RB(b)) -#define TAI(s, a, i) (RT(s) | RA(a) | ((i) & 0xffff)) -#define SAI(s, a, i) (RS(s) | RA(a) | ((i) & 0xffff)) - -#define BF(n) ((n)<<23) -#define BI(n, c) (((c)+((n)*4))<<16) -#define BT(n, c) (((c)+((n)*4))<<21) -#define BA(n, c) (((c)+((n)*4))<<16) -#define BB(n, c) (((c)+((n)*4))<<11) -#define BC_(n, c) (((c)+((n)*4))<<6) - -#define BO_COND_TRUE BO(12) -#define BO_COND_FALSE BO( 4) -#define BO_ALWAYS BO(20) - -enum { - CR_LT, - CR_GT, - CR_EQ, - CR_SO -}; - -static const uint32_t tcg_to_bc[] = { - [TCG_COND_EQ] = BC | BI(7, CR_EQ) | BO_COND_TRUE, - [TCG_COND_NE] = BC | BI(7, CR_EQ) | BO_COND_FALSE, - [TCG_COND_LT] = BC | BI(7, CR_LT) | BO_COND_TRUE, - [TCG_COND_GE] = BC | BI(7, CR_LT) | BO_COND_FALSE, - [TCG_COND_LE] = BC | BI(7, CR_GT) | BO_COND_FALSE, - [TCG_COND_GT] = BC | BI(7, CR_GT) | BO_COND_TRUE, - [TCG_COND_LTU] = BC | BI(7, CR_LT) | BO_COND_TRUE, - [TCG_COND_GEU] = BC | BI(7, CR_LT) | BO_COND_FALSE, - [TCG_COND_LEU] = BC | BI(7, CR_GT) | BO_COND_FALSE, - [TCG_COND_GTU] = BC | BI(7, CR_GT) | BO_COND_TRUE, -}; - -/* The low bit here is set if the RA and RB fields must be inverted. */ -static const uint32_t tcg_to_isel[] = { - [TCG_COND_EQ] = ISEL | BC_(7, CR_EQ), - [TCG_COND_NE] = ISEL | BC_(7, CR_EQ) | 1, - [TCG_COND_LT] = ISEL | BC_(7, CR_LT), - [TCG_COND_GE] = ISEL | BC_(7, CR_LT) | 1, - [TCG_COND_LE] = ISEL | BC_(7, CR_GT) | 1, - [TCG_COND_GT] = ISEL | BC_(7, CR_GT), - [TCG_COND_LTU] = ISEL | BC_(7, CR_LT), - [TCG_COND_GEU] = ISEL | BC_(7, CR_LT) | 1, - [TCG_COND_LEU] = ISEL | BC_(7, CR_GT) | 1, - [TCG_COND_GTU] = ISEL | BC_(7, CR_GT), -}; - -static void tcg_out_mem_long(TCGContext *s, int opi, int opx, TCGReg rt, - TCGReg base, tcg_target_long offset); - -static void tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) -{ - tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); - if (ret != arg) { - tcg_out32(s, OR | SAB(arg, ret, arg)); - } -} - -static inline void tcg_out_rld(TCGContext *s, int op, TCGReg ra, TCGReg rs, - int sh, int mb) -{ - assert(TCG_TARGET_REG_BITS == 64); - sh = SH(sh & 0x1f) | (((sh >> 5) & 1) << 1); - mb = MB64((mb >> 5) | ((mb << 1) & 0x3f)); - tcg_out32(s, op | RA(ra) | RS(rs) | sh | mb); -} - -static inline void tcg_out_rlw(TCGContext *s, int op, TCGReg ra, TCGReg rs, - int sh, int mb, int me) -{ - tcg_out32(s, op | RA(ra) | RS(rs) | SH(sh) | MB(mb) | ME(me)); -} - -static inline void tcg_out_ext32u(TCGContext *s, TCGReg dst, TCGReg src) -{ - tcg_out_rld(s, RLDICL, dst, src, 0, 32); -} - -static inline void tcg_out_shli32(TCGContext *s, TCGReg dst, TCGReg src, int c) -{ - tcg_out_rlw(s, RLWINM, dst, src, c, 0, 31 - c); -} - -static inline void tcg_out_shli64(TCGContext *s, TCGReg dst, TCGReg src, int c) -{ - tcg_out_rld(s, RLDICR, dst, src, c, 63 - c); -} - -static inline void tcg_out_shri32(TCGContext *s, TCGReg dst, TCGReg src, int c) -{ - tcg_out_rlw(s, RLWINM, dst, src, 32 - c, c, 31); -} - -static inline void tcg_out_shri64(TCGContext *s, TCGReg dst, TCGReg src, int c) -{ - tcg_out_rld(s, RLDICL, dst, src, 64 - c, c); -} - -static void tcg_out_movi32(TCGContext *s, TCGReg ret, int32_t arg) -{ - if (arg == (int16_t) arg) { - tcg_out32(s, ADDI | TAI(ret, 0, arg)); - } else { - tcg_out32(s, ADDIS | TAI(ret, 0, arg >> 16)); - if (arg & 0xffff) { - tcg_out32(s, ORI | SAI(ret, ret, arg)); - } - } -} - -static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg ret, - tcg_target_long arg) -{ - tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); - if (type == TCG_TYPE_I32 || arg == (int32_t)arg) { - tcg_out_movi32(s, ret, arg); - } else if (arg == (uint32_t)arg && !(arg & 0x8000)) { - tcg_out32(s, ADDI | TAI(ret, 0, arg)); - tcg_out32(s, ORIS | SAI(ret, ret, arg >> 16)); - } else { - int32_t high; - - if (USE_REG_RA) { - intptr_t diff = arg - (intptr_t)tb_ret_addr; - if (diff == (int32_t)diff) { - tcg_out_mem_long(s, ADDI, ADD, ret, TCG_REG_RA, diff); - return; - } - } - - high = arg >> 31 >> 1; - tcg_out_movi32(s, ret, high); - if (high) { - tcg_out_shli64(s, ret, ret, 32); - } - if (arg & 0xffff0000) { - tcg_out32(s, ORIS | SAI(ret, ret, arg >> 16)); - } - if (arg & 0xffff) { - tcg_out32(s, ORI | SAI(ret, ret, arg)); - } - } -} - -static bool mask_operand(uint32_t c, int *mb, int *me) -{ - uint32_t lsb, test; - - /* Accept a bit pattern like: - 0....01....1 - 1....10....0 - 0..01..10..0 - Keep track of the transitions. */ - if (c == 0 || c == -1) { - return false; - } - test = c; - lsb = test & -test; - test += lsb; - if (test & (test - 1)) { - return false; - } - - *me = clz32(lsb); - *mb = test ? clz32(test & -test) + 1 : 0; - return true; -} - -static bool mask64_operand(uint64_t c, int *mb, int *me) -{ - uint64_t lsb; - - if (c == 0) { - return false; - } - - lsb = c & -c; - /* Accept 1..10..0. */ - if (c == -lsb) { - *mb = 0; - *me = clz64(lsb); - return true; - } - /* Accept 0..01..1. */ - if (lsb == 1 && (c & (c + 1)) == 0) { - *mb = clz64(c + 1) + 1; - *me = 63; - return true; - } - return false; -} - -static void tcg_out_andi32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c) -{ - int mb, me; - - if ((c & 0xffff) == c) { - tcg_out32(s, ANDI | SAI(src, dst, c)); - return; - } else if ((c & 0xffff0000) == c) { - tcg_out32(s, ANDIS | SAI(src, dst, c >> 16)); - return; - } else if (mask_operand(c, &mb, &me)) { - tcg_out_rlw(s, RLWINM, dst, src, 0, mb, me); - } else { - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R0, c); - tcg_out32(s, AND | SAB(src, dst, TCG_REG_R0)); - } -} - -static void tcg_out_andi64(TCGContext *s, TCGReg dst, TCGReg src, uint64_t c) -{ - int mb, me; - - assert(TCG_TARGET_REG_BITS == 64); - if ((c & 0xffff) == c) { - tcg_out32(s, ANDI | SAI(src, dst, c)); - return; - } else if ((c & 0xffff0000) == c) { - tcg_out32(s, ANDIS | SAI(src, dst, c >> 16)); - return; - } else if (mask64_operand(c, &mb, &me)) { - if (mb == 0) { - tcg_out_rld(s, RLDICR, dst, src, 0, me); - } else { - tcg_out_rld(s, RLDICL, dst, src, 0, mb); - } - } else { - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R0, c); - tcg_out32(s, AND | SAB(src, dst, TCG_REG_R0)); - } -} - -static void tcg_out_zori32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c, - int op_lo, int op_hi) -{ - if (c >> 16) { - tcg_out32(s, op_hi | SAI(src, dst, c >> 16)); - src = dst; - } - if (c & 0xffff) { - tcg_out32(s, op_lo | SAI(src, dst, c)); - src = dst; - } -} - -static void tcg_out_ori32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c) -{ - tcg_out_zori32(s, dst, src, c, ORI, ORIS); -} - -static void tcg_out_xori32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c) -{ - tcg_out_zori32(s, dst, src, c, XORI, XORIS); -} - -static void tcg_out_b(TCGContext *s, int mask, tcg_insn_unit *target) -{ - ptrdiff_t disp = tcg_pcrel_diff(s, target); - if (in_range_b(disp)) { - tcg_out32(s, B | (disp & 0x3fffffc) | mask); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R0, (uintptr_t)target); - tcg_out32(s, MTSPR | RS(TCG_REG_R0) | CTR); - tcg_out32(s, BCCTR | BO_ALWAYS | mask); - } -} - -static void tcg_out_mem_long(TCGContext *s, int opi, int opx, TCGReg rt, - TCGReg base, tcg_target_long offset) -{ - tcg_target_long orig = offset, l0, l1, extra = 0, align = 0; - bool is_store = false; - TCGReg rs = TCG_REG_TMP1; - - switch (opi) { - case LD: case LWA: - align = 3; - /* FALLTHRU */ - default: - if (rt != TCG_REG_R0) { - rs = rt; - break; - } - break; - case STD: - align = 3; - /* FALLTHRU */ - case STB: case STH: case STW: - is_store = true; - break; - } - - /* For unaligned, or very large offsets, use the indexed form. */ - if (offset & align || offset != (int32_t)offset) { - if (rs == base) { - rs = TCG_REG_R0; - } - tcg_debug_assert(!is_store || rs != rt); - tcg_out_movi(s, TCG_TYPE_PTR, rs, orig); - tcg_out32(s, opx | TAB(rt, base, rs)); - return; - } - - l0 = (int16_t)offset; - offset = (offset - l0) >> 16; - l1 = (int16_t)offset; - - if (l1 < 0 && orig >= 0) { - extra = 0x4000; - l1 = (int16_t)(offset - 0x4000); - } - if (l1) { - tcg_out32(s, ADDIS | TAI(rs, base, l1)); - base = rs; - } - if (extra) { - tcg_out32(s, ADDIS | TAI(rs, base, extra)); - base = rs; - } - if (opi != ADDI || base != rt || l0 != 0) { - tcg_out32(s, opi | TAI(rt, base, l0)); - } -} - -static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, - TCGReg arg1, intptr_t arg2) -{ - int opi, opx; - - assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); - if (type == TCG_TYPE_I32) { - opi = LWZ, opx = LWZX; - } else { - opi = LD, opx = LDX; - } - tcg_out_mem_long(s, opi, opx, ret, arg1, arg2); -} - -static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, - TCGReg arg1, intptr_t arg2) -{ - int opi, opx; - - assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); - if (type == TCG_TYPE_I32) { - opi = STW, opx = STWX; - } else { - opi = STD, opx = STDX; - } - tcg_out_mem_long(s, opi, opx, arg, arg1, arg2); -} - -static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, - int const_arg2, int cr, TCGType type) -{ - int imm; - uint32_t op; - - tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); - - /* Simplify the comparisons below wrt CMPI. */ - if (type == TCG_TYPE_I32) { - arg2 = (int32_t)arg2; - } - - switch (cond) { - case TCG_COND_EQ: - case TCG_COND_NE: - if (const_arg2) { - if ((int16_t) arg2 == arg2) { - op = CMPI; - imm = 1; - break; - } else if ((uint16_t) arg2 == arg2) { - op = CMPLI; - imm = 1; - break; - } - } - op = CMPL; - imm = 0; - break; - - case TCG_COND_LT: - case TCG_COND_GE: - case TCG_COND_LE: - case TCG_COND_GT: - if (const_arg2) { - if ((int16_t) arg2 == arg2) { - op = CMPI; - imm = 1; - break; - } - } - op = CMP; - imm = 0; - break; - - case TCG_COND_LTU: - case TCG_COND_GEU: - case TCG_COND_LEU: - case TCG_COND_GTU: - if (const_arg2) { - if ((uint16_t) arg2 == arg2) { - op = CMPLI; - imm = 1; - break; - } - } - op = CMPL; - imm = 0; - break; - - default: - tcg_abort(); - } - op |= BF(cr) | ((type == TCG_TYPE_I64) << 21); - - if (imm) { - tcg_out32(s, op | RA(arg1) | (arg2 & 0xffff)); - } else { - if (const_arg2) { - tcg_out_movi(s, type, TCG_REG_R0, arg2); - arg2 = TCG_REG_R0; - } - tcg_out32(s, op | RA(arg1) | RB(arg2)); - } -} - -static void tcg_out_setcond_eq0(TCGContext *s, TCGType type, - TCGReg dst, TCGReg src) -{ - if (type == TCG_TYPE_I32) { - tcg_out32(s, CNTLZW | RS(src) | RA(dst)); - tcg_out_shri32(s, dst, dst, 5); - } else { - tcg_out32(s, CNTLZD | RS(src) | RA(dst)); - tcg_out_shri64(s, dst, dst, 6); - } -} - -static void tcg_out_setcond_ne0(TCGContext *s, TCGReg dst, TCGReg src) -{ - /* X != 0 implies X + -1 generates a carry. Extra addition - trickery means: R = X-1 + ~X + C = X-1 + (-X+1) + C = C. */ - if (dst != src) { - tcg_out32(s, ADDIC | TAI(dst, src, -1)); - tcg_out32(s, SUBFE | TAB(dst, dst, src)); - } else { - tcg_out32(s, ADDIC | TAI(TCG_REG_R0, src, -1)); - tcg_out32(s, SUBFE | TAB(dst, TCG_REG_R0, src)); - } -} - -static TCGReg tcg_gen_setcond_xor(TCGContext *s, TCGReg arg1, TCGArg arg2, - bool const_arg2) -{ - if (const_arg2) { - if ((uint32_t)arg2 == arg2) { - tcg_out_xori32(s, TCG_REG_R0, arg1, arg2); - } else { - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R0, arg2); - tcg_out32(s, XOR | SAB(arg1, TCG_REG_R0, TCG_REG_R0)); - } - } else { - tcg_out32(s, XOR | SAB(arg1, TCG_REG_R0, arg2)); - } - return TCG_REG_R0; -} - -static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, - TCGArg arg0, TCGArg arg1, TCGArg arg2, - int const_arg2) -{ - int crop, sh; - - assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); - - /* Ignore high bits of a potential constant arg2. */ - if (type == TCG_TYPE_I32) { - arg2 = (uint32_t)arg2; - } - - /* Handle common and trivial cases before handling anything else. */ - if (arg2 == 0) { - switch (cond) { - case TCG_COND_EQ: - tcg_out_setcond_eq0(s, type, arg0, arg1); - return; - case TCG_COND_NE: - if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) { - tcg_out_ext32u(s, TCG_REG_R0, arg1); - arg1 = TCG_REG_R0; - } - tcg_out_setcond_ne0(s, arg0, arg1); - return; - case TCG_COND_GE: - tcg_out32(s, NOR | SAB(arg1, arg0, arg1)); - arg1 = arg0; - /* FALLTHRU */ - case TCG_COND_LT: - /* Extract the sign bit. */ - if (type == TCG_TYPE_I32) { - tcg_out_shri32(s, arg0, arg1, 31); - } else { - tcg_out_shri64(s, arg0, arg1, 63); - } - return; - default: - break; - } - } - - /* If we have ISEL, we can implement everything with 3 or 4 insns. - All other cases below are also at least 3 insns, so speed up the - code generator by not considering them and always using ISEL. */ - if (HAVE_ISEL) { - int isel, tab; - - tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); - - isel = tcg_to_isel[cond]; - - tcg_out_movi(s, type, arg0, 1); - if (isel & 1) { - /* arg0 = (bc ? 0 : 1) */ - tab = TAB(arg0, 0, arg0); - isel &= ~1; - } else { - /* arg0 = (bc ? 1 : 0) */ - tcg_out_movi(s, type, TCG_REG_R0, 0); - tab = TAB(arg0, arg0, TCG_REG_R0); - } - tcg_out32(s, isel | tab); - return; - } - - switch (cond) { - case TCG_COND_EQ: - arg1 = tcg_gen_setcond_xor(s, arg1, arg2, const_arg2); - tcg_out_setcond_eq0(s, type, arg0, arg1); - return; - - case TCG_COND_NE: - arg1 = tcg_gen_setcond_xor(s, arg1, arg2, const_arg2); - /* Discard the high bits only once, rather than both inputs. */ - if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) { - tcg_out_ext32u(s, TCG_REG_R0, arg1); - arg1 = TCG_REG_R0; - } - tcg_out_setcond_ne0(s, arg0, arg1); - return; - - case TCG_COND_GT: - case TCG_COND_GTU: - sh = 30; - crop = 0; - goto crtest; - - case TCG_COND_LT: - case TCG_COND_LTU: - sh = 29; - crop = 0; - goto crtest; - - case TCG_COND_GE: - case TCG_COND_GEU: - sh = 31; - crop = CRNOR | BT(7, CR_EQ) | BA(7, CR_LT) | BB(7, CR_LT); - goto crtest; - - case TCG_COND_LE: - case TCG_COND_LEU: - sh = 31; - crop = CRNOR | BT(7, CR_EQ) | BA(7, CR_GT) | BB(7, CR_GT); - crtest: - tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); - if (crop) { - tcg_out32(s, crop); - } - tcg_out32(s, MFOCRF | RT(TCG_REG_R0) | FXM(7)); - tcg_out_rlw(s, RLWINM, arg0, TCG_REG_R0, sh, 31, 31); - break; - - default: - tcg_abort(); - } -} - -static void tcg_out_bc(TCGContext *s, int bc, TCGLabel *l) -{ - if (l->has_value) { - tcg_out32(s, bc | reloc_pc14_val(s->code_ptr, l->u.value_ptr)); - } else { - tcg_out_reloc(s, s->code_ptr, R_PPC_REL14, l, 0); - tcg_out_bc_noaddr(s, bc); - } -} - -static void tcg_out_brcond(TCGContext *s, TCGCond cond, - TCGArg arg1, TCGArg arg2, int const_arg2, - TCGLabel *l, TCGType type) -{ - tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); - tcg_out_bc(s, tcg_to_bc[cond], l); -} - -static void tcg_out_movcond(TCGContext *s, TCGType type, TCGCond cond, - TCGArg dest, TCGArg c1, TCGArg c2, TCGArg v1, - TCGArg v2, bool const_c2) -{ - /* If for some reason both inputs are zero, don't produce bad code. */ - if (v1 == 0 && v2 == 0) { - tcg_out_movi(s, type, dest, 0); - return; - } - - tcg_out_cmp(s, cond, c1, c2, const_c2, 7, type); - - if (HAVE_ISEL) { - int isel = tcg_to_isel[cond]; - - /* Swap the V operands if the operation indicates inversion. */ - if (isel & 1) { - int t = v1; - v1 = v2; - v2 = t; - isel &= ~1; - } - /* V1 == 0 is handled by isel; V2 == 0 must be handled by hand. */ - if (v2 == 0) { - tcg_out_movi(s, type, TCG_REG_R0, 0); - } - tcg_out32(s, isel | TAB(dest, v1, v2)); - } else { - if (dest == v2) { - cond = tcg_invert_cond(cond); - v2 = v1; - } else if (dest != v1) { - if (v1 == 0) { - tcg_out_movi(s, type, dest, 0); - } else { - tcg_out_mov(s, type, dest, v1); - } - } - /* Branch forward over one insn */ - tcg_out32(s, tcg_to_bc[cond] | 8); - if (v2 == 0) { - tcg_out_movi(s, type, dest, 0); - } else { - tcg_out_mov(s, type, dest, v2); - } - } -} - -static void tcg_out_cmp2(TCGContext *s, const TCGArg *args, - const int *const_args) -{ - static const struct { uint8_t bit1, bit2; } bits[] = { - [TCG_COND_LT ] = { CR_LT, CR_LT }, - [TCG_COND_LE ] = { CR_LT, CR_GT }, - [TCG_COND_GT ] = { CR_GT, CR_GT }, - [TCG_COND_GE ] = { CR_GT, CR_LT }, - [TCG_COND_LTU] = { CR_LT, CR_LT }, - [TCG_COND_LEU] = { CR_LT, CR_GT }, - [TCG_COND_GTU] = { CR_GT, CR_GT }, - [TCG_COND_GEU] = { CR_GT, CR_LT }, - }; - - TCGCond cond = args[4], cond2; - TCGArg al, ah, bl, bh; - int blconst, bhconst; - int op, bit1, bit2; - - al = args[0]; - ah = args[1]; - bl = args[2]; - bh = args[3]; - blconst = const_args[2]; - bhconst = const_args[3]; - - switch (cond) { - case TCG_COND_EQ: - op = CRAND; - goto do_equality; - case TCG_COND_NE: - op = CRNAND; - do_equality: - tcg_out_cmp(s, cond, al, bl, blconst, 6, TCG_TYPE_I32); - tcg_out_cmp(s, cond, ah, bh, bhconst, 7, TCG_TYPE_I32); - tcg_out32(s, op | BT(7, CR_EQ) | BA(6, CR_EQ) | BB(7, CR_EQ)); - break; - - case TCG_COND_LT: - case TCG_COND_LE: - case TCG_COND_GT: - case TCG_COND_GE: - case TCG_COND_LTU: - case TCG_COND_LEU: - case TCG_COND_GTU: - case TCG_COND_GEU: - bit1 = bits[cond].bit1; - bit2 = bits[cond].bit2; - op = (bit1 != bit2 ? CRANDC : CRAND); - cond2 = tcg_unsigned_cond(cond); - - tcg_out_cmp(s, cond, ah, bh, bhconst, 6, TCG_TYPE_I32); - tcg_out_cmp(s, cond2, al, bl, blconst, 7, TCG_TYPE_I32); - tcg_out32(s, op | BT(7, CR_EQ) | BA(6, CR_EQ) | BB(7, bit2)); - tcg_out32(s, CROR | BT(7, CR_EQ) | BA(6, bit1) | BB(7, CR_EQ)); - break; - - default: - tcg_abort(); - } -} - -static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, - const int *const_args) -{ - tcg_out_cmp2(s, args + 1, const_args + 1); - tcg_out32(s, MFOCRF | RT(TCG_REG_R0) | FXM(7)); - tcg_out_rlw(s, RLWINM, args[0], TCG_REG_R0, 31, 31, 31); -} - -static void tcg_out_brcond2 (TCGContext *s, const TCGArg *args, - const int *const_args) -{ - tcg_out_cmp2(s, args, const_args); - tcg_out_bc(s, BC | BI(7, CR_EQ) | BO_COND_TRUE, arg_label(args[5])); -} - -void ppc_tb_set_jmp_target(uintptr_t jmp_addr, uintptr_t addr) -{ - TCGContext s; - - s.code_buf = s.code_ptr = (tcg_insn_unit *)jmp_addr; - tcg_out_b(&s, 0, (tcg_insn_unit *)addr); - flush_icache_range(jmp_addr, jmp_addr + tcg_current_code_size(&s)); -} - -static void tcg_out_call(TCGContext *s, tcg_insn_unit *target) -{ -#ifdef _CALL_AIX - /* Look through the descriptor. If the branch is in range, and we - don't have to spend too much effort on building the toc. */ - void *tgt = ((void **)target)[0]; - uintptr_t toc = ((uintptr_t *)target)[1]; - intptr_t diff = tcg_pcrel_diff(s, tgt); - - if (in_range_b(diff) && toc == (uint32_t)toc) { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP1, toc); - tcg_out_b(s, LK, tgt); - } else { - /* Fold the low bits of the constant into the addresses below. */ - intptr_t arg = (intptr_t)target; - int ofs = (int16_t)arg; - - if (ofs + 8 < 0x8000) { - arg -= ofs; - } else { - ofs = 0; - } - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP1, arg); - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R0, TCG_REG_TMP1, ofs); - tcg_out32(s, MTSPR | RA(TCG_REG_R0) | CTR); - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R2, TCG_REG_TMP1, ofs + SZP); - tcg_out32(s, BCCTR | BO_ALWAYS | LK); - } -#elif defined(_CALL_ELF) && _CALL_ELF == 2 - intptr_t diff; - - /* In the ELFv2 ABI, we have to set up r12 to contain the destination - address, which the callee uses to compute its TOC address. */ - /* FIXME: when the branch is in range, we could avoid r12 load if we - knew that the destination uses the same TOC, and what its local - entry point offset is. */ - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R12, (intptr_t)target); - - diff = tcg_pcrel_diff(s, target); - if (in_range_b(diff)) { - tcg_out_b(s, LK, target); - } else { - tcg_out32(s, MTSPR | RS(TCG_REG_R12) | CTR); - tcg_out32(s, BCCTR | BO_ALWAYS | LK); - } -#else - tcg_out_b(s, LK, target); -#endif -} - -static const uint32_t qemu_ldx_opc[16] = { - [MO_UB] = LBZX, - [MO_UW] = LHZX, - [MO_UL] = LWZX, - [MO_Q] = LDX, - [MO_SW] = LHAX, - [MO_SL] = LWAX, - [MO_BSWAP | MO_UB] = LBZX, - [MO_BSWAP | MO_UW] = LHBRX, - [MO_BSWAP | MO_UL] = LWBRX, - [MO_BSWAP | MO_Q] = LDBRX, -}; - -static const uint32_t qemu_stx_opc[16] = { - [MO_UB] = STBX, - [MO_UW] = STHX, - [MO_UL] = STWX, - [MO_Q] = STDX, - [MO_BSWAP | MO_UB] = STBX, - [MO_BSWAP | MO_UW] = STHBRX, - [MO_BSWAP | MO_UL] = STWBRX, - [MO_BSWAP | MO_Q] = STDBRX, -}; - -static const uint32_t qemu_exts_opc[4] = { - EXTSB, EXTSH, EXTSW, 0 -}; - -#if defined (CONFIG_SOFTMMU) -/* helper signature: helper_ld_mmu(CPUState *env, target_ulong addr, - * int mmu_idx, uintptr_t ra) - */ -static void * const qemu_ld_helpers[16] = { - [MO_UB] = helper_ret_ldub_mmu, - [MO_LEUW] = helper_le_lduw_mmu, - [MO_LEUL] = helper_le_ldul_mmu, - [MO_LEQ] = helper_le_ldq_mmu, - [MO_BEUW] = helper_be_lduw_mmu, - [MO_BEUL] = helper_be_ldul_mmu, - [MO_BEQ] = helper_be_ldq_mmu, -}; - -/* helper signature: helper_st_mmu(CPUState *env, target_ulong addr, - * uintxx_t val, int mmu_idx, uintptr_t ra) - */ -static void * const qemu_st_helpers[16] = { - [MO_UB] = helper_ret_stb_mmu, - [MO_LEUW] = helper_le_stw_mmu, - [MO_LEUL] = helper_le_stl_mmu, - [MO_LEQ] = helper_le_stq_mmu, - [MO_BEUW] = helper_be_stw_mmu, - [MO_BEUL] = helper_be_stl_mmu, - [MO_BEQ] = helper_be_stq_mmu, -}; - -/* Perform the TLB load and compare. Places the result of the comparison - in CR7, loads the addend of the TLB into R3, and returns the register - containing the guest address (zero-extended into R4). Clobbers R0 and R2. */ - -static TCGReg tcg_out_tlb_read(TCGContext *s, TCGMemOp s_bits, - TCGReg addrlo, TCGReg addrhi, - int mem_index, bool is_read) -{ - int cmp_off - = (is_read - ? offsetof(CPUArchState, tlb_table[mem_index][0].addr_read) - : offsetof(CPUArchState, tlb_table[mem_index][0].addr_write)); - int add_off = offsetof(CPUArchState, tlb_table[mem_index][0].addend); - TCGReg base = TCG_AREG0; - - /* Extract the page index, shifted into place for tlb index. */ - if (TCG_TARGET_REG_BITS == 64) { - if (TARGET_LONG_BITS == 32) { - /* Zero-extend the address into a place helpful for further use. */ - tcg_out_ext32u(s, TCG_REG_R4, addrlo); - addrlo = TCG_REG_R4; - } else { - tcg_out_rld(s, RLDICL, TCG_REG_R3, addrlo, - 64 - TARGET_PAGE_BITS, 64 - CPU_TLB_BITS); - } - } - - /* Compensate for very large offsets. */ - if (add_off >= 0x8000) { - /* Most target env are smaller than 32k; none are larger than 64k. - Simplify the logic here merely to offset by 0x7ff0, giving us a - range just shy of 64k. Check this assumption. */ - QEMU_BUILD_BUG_ON(offsetof(CPUArchState, - tlb_table[NB_MMU_MODES - 1][1]) - > 0x7ff0 + 0x7fff); - tcg_out32(s, ADDI | TAI(TCG_REG_TMP1, base, 0x7ff0)); - base = TCG_REG_TMP1; - cmp_off -= 0x7ff0; - add_off -= 0x7ff0; - } - - /* Extraction and shifting, part 2. */ - if (TCG_TARGET_REG_BITS == 32 || TARGET_LONG_BITS == 32) { - tcg_out_rlw(s, RLWINM, TCG_REG_R3, addrlo, - 32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS), - 32 - (CPU_TLB_BITS + CPU_TLB_ENTRY_BITS), - 31 - CPU_TLB_ENTRY_BITS); - } else { - tcg_out_shli64(s, TCG_REG_R3, TCG_REG_R3, CPU_TLB_ENTRY_BITS); - } - - tcg_out32(s, ADD | TAB(TCG_REG_R3, TCG_REG_R3, base)); - - /* Load the tlb comparator. */ - if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { - tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_R4, TCG_REG_R3, cmp_off); - tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_TMP1, TCG_REG_R3, cmp_off + 4); - } else { - tcg_out_ld(s, TCG_TYPE_TL, TCG_REG_TMP1, TCG_REG_R3, cmp_off); - } - - /* Load the TLB addend for use on the fast path. Do this asap - to minimize any load use delay. */ - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R3, TCG_REG_R3, add_off); - - /* Clear the non-page, non-alignment bits from the address. */ - if (TCG_TARGET_REG_BITS == 32 || TARGET_LONG_BITS == 32) { - tcg_out_rlw(s, RLWINM, TCG_REG_R0, addrlo, 0, - (32 - s_bits) & 31, 31 - TARGET_PAGE_BITS); - } else if (!s_bits) { - tcg_out_rld(s, RLDICR, TCG_REG_R0, addrlo, - 0, 63 - TARGET_PAGE_BITS); - } else { - tcg_out_rld(s, RLDICL, TCG_REG_R0, addrlo, - 64 - TARGET_PAGE_BITS, TARGET_PAGE_BITS - s_bits); - tcg_out_rld(s, RLDICL, TCG_REG_R0, TCG_REG_R0, TARGET_PAGE_BITS, 0); - } - - if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { - tcg_out_cmp(s, TCG_COND_EQ, TCG_REG_R0, TCG_REG_TMP1, - 0, 7, TCG_TYPE_I32); - tcg_out_cmp(s, TCG_COND_EQ, addrhi, TCG_REG_R4, 0, 6, TCG_TYPE_I32); - tcg_out32(s, CRAND | BT(7, CR_EQ) | BA(6, CR_EQ) | BB(7, CR_EQ)); - } else { - tcg_out_cmp(s, TCG_COND_EQ, TCG_REG_R0, TCG_REG_TMP1, - 0, 7, TCG_TYPE_TL); - } - - return addrlo; -} - -/* Record the context of a call to the out of line helper code for the slow - path for a load or store, so that we can later generate the correct - helper code. */ -static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOpIdx oi, - TCGReg datalo_reg, TCGReg datahi_reg, - TCGReg addrlo_reg, TCGReg addrhi_reg, - tcg_insn_unit *raddr, tcg_insn_unit *lptr) -{ - TCGLabelQemuLdst *label = new_ldst_label(s); - - label->is_ld = is_ld; - label->oi = oi; - label->datalo_reg = datalo_reg; - label->datahi_reg = datahi_reg; - label->addrlo_reg = addrlo_reg; - label->addrhi_reg = addrhi_reg; - label->raddr = raddr; - label->label_ptr[0] = lptr; -} - -static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) -{ - TCGMemOpIdx oi = lb->oi; - TCGMemOp opc = get_memop(oi); - TCGReg hi, lo, arg = TCG_REG_R3; - - reloc_pc14(lb->label_ptr[0], s->code_ptr); - - tcg_out_mov(s, TCG_TYPE_PTR, arg++, TCG_AREG0); - - lo = lb->addrlo_reg; - hi = lb->addrhi_reg; - if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { -#ifdef TCG_TARGET_CALL_ALIGN_ARGS - arg |= 1; -#endif - tcg_out_mov(s, TCG_TYPE_I32, arg++, hi); - tcg_out_mov(s, TCG_TYPE_I32, arg++, lo); - } else { - /* If the address needed to be zero-extended, we'll have already - placed it in R4. The only remaining case is 64-bit guest. */ - tcg_out_mov(s, TCG_TYPE_TL, arg++, lo); - } - - tcg_out_movi(s, TCG_TYPE_I32, arg++, oi); - tcg_out32(s, MFSPR | RT(arg) | LR); - - tcg_out_call(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SIZE)]); - - lo = lb->datalo_reg; - hi = lb->datahi_reg; - if (TCG_TARGET_REG_BITS == 32 && (opc & MO_SIZE) == MO_64) { - tcg_out_mov(s, TCG_TYPE_I32, lo, TCG_REG_R4); - tcg_out_mov(s, TCG_TYPE_I32, hi, TCG_REG_R3); - } else if (opc & MO_SIGN) { - uint32_t insn = qemu_exts_opc[opc & MO_SIZE]; - tcg_out32(s, insn | RA(lo) | RS(TCG_REG_R3)); - } else { - tcg_out_mov(s, TCG_TYPE_REG, lo, TCG_REG_R3); - } - - tcg_out_b(s, 0, lb->raddr); -} - -static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) -{ - TCGMemOpIdx oi = lb->oi; - TCGMemOp opc = get_memop(oi); - TCGMemOp s_bits = opc & MO_SIZE; - TCGReg hi, lo, arg = TCG_REG_R3; - - reloc_pc14(lb->label_ptr[0], s->code_ptr); - - tcg_out_mov(s, TCG_TYPE_PTR, arg++, TCG_AREG0); - - lo = lb->addrlo_reg; - hi = lb->addrhi_reg; - if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { -#ifdef TCG_TARGET_CALL_ALIGN_ARGS - arg |= 1; -#endif - tcg_out_mov(s, TCG_TYPE_I32, arg++, hi); - tcg_out_mov(s, TCG_TYPE_I32, arg++, lo); - } else { - /* If the address needed to be zero-extended, we'll have already - placed it in R4. The only remaining case is 64-bit guest. */ - tcg_out_mov(s, TCG_TYPE_TL, arg++, lo); - } - - lo = lb->datalo_reg; - hi = lb->datahi_reg; - if (TCG_TARGET_REG_BITS == 32) { - switch (s_bits) { - case MO_64: -#ifdef TCG_TARGET_CALL_ALIGN_ARGS - arg |= 1; -#endif - tcg_out_mov(s, TCG_TYPE_I32, arg++, hi); - /* FALLTHRU */ - case MO_32: - tcg_out_mov(s, TCG_TYPE_I32, arg++, lo); - break; - default: - tcg_out_rlw(s, RLWINM, arg++, lo, 0, 32 - (8 << s_bits), 31); - break; - } - } else { - if (s_bits == MO_64) { - tcg_out_mov(s, TCG_TYPE_I64, arg++, lo); - } else { - tcg_out_rld(s, RLDICL, arg++, lo, 0, 64 - (8 << s_bits)); - } - } - - tcg_out_movi(s, TCG_TYPE_I32, arg++, oi); - tcg_out32(s, MFSPR | RT(arg) | LR); - - tcg_out_call(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)]); - - tcg_out_b(s, 0, lb->raddr); -} -#endif /* SOFTMMU */ - -static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) -{ - TCGReg datalo, datahi, addrlo, rbase; - TCGReg addrhi __attribute__((unused)); - TCGMemOpIdx oi; - TCGMemOp opc, s_bits; -#ifdef CONFIG_SOFTMMU - int mem_index; - tcg_insn_unit *label_ptr; -#endif - - datalo = *args++; - datahi = (TCG_TARGET_REG_BITS == 32 && is_64 ? *args++ : 0); - addrlo = *args++; - addrhi = (TCG_TARGET_REG_BITS < TARGET_LONG_BITS ? *args++ : 0); - oi = *args++; - opc = get_memop(oi); - s_bits = opc & MO_SIZE; - -#ifdef CONFIG_SOFTMMU - mem_index = get_mmuidx(oi); - addrlo = tcg_out_tlb_read(s, s_bits, addrlo, addrhi, mem_index, true); - - /* Load a pointer into the current opcode w/conditional branch-link. */ - label_ptr = s->code_ptr; - tcg_out_bc_noaddr(s, BC | BI(7, CR_EQ) | BO_COND_FALSE | LK); - - rbase = TCG_REG_R3; -#else /* !CONFIG_SOFTMMU */ - rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0; - if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { - tcg_out_ext32u(s, TCG_REG_TMP1, addrlo); - addrlo = TCG_REG_TMP1; - } -#endif - - if (TCG_TARGET_REG_BITS == 32 && s_bits == MO_64) { - if (opc & MO_BSWAP) { - tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); - tcg_out32(s, LWBRX | TAB(datalo, rbase, addrlo)); - tcg_out32(s, LWBRX | TAB(datahi, rbase, TCG_REG_R0)); - } else if (rbase != 0) { - tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); - tcg_out32(s, LWZX | TAB(datahi, rbase, addrlo)); - tcg_out32(s, LWZX | TAB(datalo, rbase, TCG_REG_R0)); - } else if (addrlo == datahi) { - tcg_out32(s, LWZ | TAI(datalo, addrlo, 4)); - tcg_out32(s, LWZ | TAI(datahi, addrlo, 0)); - } else { - tcg_out32(s, LWZ | TAI(datahi, addrlo, 0)); - tcg_out32(s, LWZ | TAI(datalo, addrlo, 4)); - } - } else { - uint32_t insn = qemu_ldx_opc[opc & (MO_BSWAP | MO_SSIZE)]; - if (!HAVE_ISA_2_06 && insn == LDBRX) { - tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); - tcg_out32(s, LWBRX | TAB(datalo, rbase, addrlo)); - tcg_out32(s, LWBRX | TAB(TCG_REG_R0, rbase, TCG_REG_R0)); - tcg_out_rld(s, RLDIMI, datalo, TCG_REG_R0, 32, 0); - } else if (insn) { - tcg_out32(s, insn | TAB(datalo, rbase, addrlo)); - } else { - insn = qemu_ldx_opc[opc & (MO_SIZE | MO_BSWAP)]; - tcg_out32(s, insn | TAB(datalo, rbase, addrlo)); - insn = qemu_exts_opc[s_bits]; - tcg_out32(s, insn | RA(datalo) | RS(datalo)); - } - } - -#ifdef CONFIG_SOFTMMU - add_qemu_ldst_label(s, true, oi, datalo, datahi, addrlo, addrhi, - s->code_ptr, label_ptr); -#endif -} - -static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) -{ - TCGReg datalo, datahi, addrlo, rbase; - TCGReg addrhi __attribute__((unused)); - TCGMemOpIdx oi; - TCGMemOp opc, s_bits; -#ifdef CONFIG_SOFTMMU - int mem_index; - tcg_insn_unit *label_ptr; -#endif - - datalo = *args++; - datahi = (TCG_TARGET_REG_BITS == 32 && is_64 ? *args++ : 0); - addrlo = *args++; - addrhi = (TCG_TARGET_REG_BITS < TARGET_LONG_BITS ? *args++ : 0); - oi = *args++; - opc = get_memop(oi); - s_bits = opc & MO_SIZE; - -#ifdef CONFIG_SOFTMMU - mem_index = get_mmuidx(oi); - addrlo = tcg_out_tlb_read(s, s_bits, addrlo, addrhi, mem_index, false); - - /* Load a pointer into the current opcode w/conditional branch-link. */ - label_ptr = s->code_ptr; - tcg_out_bc_noaddr(s, BC | BI(7, CR_EQ) | BO_COND_FALSE | LK); - - rbase = TCG_REG_R3; -#else /* !CONFIG_SOFTMMU */ - rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0; - if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { - tcg_out_ext32u(s, TCG_REG_TMP1, addrlo); - addrlo = TCG_REG_TMP1; - } -#endif - - if (TCG_TARGET_REG_BITS == 32 && s_bits == MO_64) { - if (opc & MO_BSWAP) { - tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); - tcg_out32(s, STWBRX | SAB(datalo, rbase, addrlo)); - tcg_out32(s, STWBRX | SAB(datahi, rbase, TCG_REG_R0)); - } else if (rbase != 0) { - tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); - tcg_out32(s, STWX | SAB(datahi, rbase, addrlo)); - tcg_out32(s, STWX | SAB(datalo, rbase, TCG_REG_R0)); - } else { - tcg_out32(s, STW | TAI(datahi, addrlo, 0)); - tcg_out32(s, STW | TAI(datalo, addrlo, 4)); - } - } else { - uint32_t insn = qemu_stx_opc[opc & (MO_BSWAP | MO_SIZE)]; - if (!HAVE_ISA_2_06 && insn == STDBRX) { - tcg_out32(s, STWBRX | SAB(datalo, rbase, addrlo)); - tcg_out32(s, ADDI | TAI(TCG_REG_TMP1, addrlo, 4)); - tcg_out_shri64(s, TCG_REG_R0, datalo, 32); - tcg_out32(s, STWBRX | SAB(TCG_REG_R0, rbase, TCG_REG_TMP1)); - } else { - tcg_out32(s, insn | SAB(datalo, rbase, addrlo)); - } - } - -#ifdef CONFIG_SOFTMMU - add_qemu_ldst_label(s, false, oi, datalo, datahi, addrlo, addrhi, - s->code_ptr, label_ptr); -#endif -} - -/* Parameters for function call generation, used in tcg.c. */ -#define TCG_TARGET_STACK_ALIGN 16 -#define TCG_TARGET_EXTEND_ARGS 1 - -#ifdef _CALL_AIX -# define LINK_AREA_SIZE (6 * SZR) -# define LR_OFFSET (1 * SZR) -# define TCG_TARGET_CALL_STACK_OFFSET (LINK_AREA_SIZE + 8 * SZR) -#elif defined(TCG_TARGET_CALL_DARWIN) -# define LINK_AREA_SIZE (6 * SZR) -# define LR_OFFSET (2 * SZR) -#elif TCG_TARGET_REG_BITS == 64 -# if defined(_CALL_ELF) && _CALL_ELF == 2 -# define LINK_AREA_SIZE (4 * SZR) -# define LR_OFFSET (1 * SZR) -# endif -#else /* TCG_TARGET_REG_BITS == 32 */ -# if defined(_CALL_SYSV) -# define LINK_AREA_SIZE (2 * SZR) -# define LR_OFFSET (1 * SZR) -# endif -#endif -#ifndef LR_OFFSET -# error "Unhandled abi" -#endif -#ifndef TCG_TARGET_CALL_STACK_OFFSET -# define TCG_TARGET_CALL_STACK_OFFSET LINK_AREA_SIZE -#endif - -#define CPU_TEMP_BUF_SIZE (CPU_TEMP_BUF_NLONGS * (int)sizeof(long)) -#define REG_SAVE_SIZE ((int)ARRAY_SIZE(tcg_target_callee_save_regs) * SZR) - -#define FRAME_SIZE ((TCG_TARGET_CALL_STACK_OFFSET \ - + TCG_STATIC_CALL_ARGS_SIZE \ - + CPU_TEMP_BUF_SIZE \ - + REG_SAVE_SIZE \ - + TCG_TARGET_STACK_ALIGN - 1) \ - & -TCG_TARGET_STACK_ALIGN) - -#define REG_SAVE_BOT (FRAME_SIZE - REG_SAVE_SIZE) - -static void tcg_target_qemu_prologue(TCGContext *s) -{ - int i; - -#ifdef _CALL_AIX - void **desc = (void **)s->code_ptr; - desc[0] = desc + 2; /* entry point */ - desc[1] = 0; /* environment pointer */ - s->code_ptr = (void *)(desc + 2); /* skip over descriptor */ -#endif - - tcg_set_frame(s, TCG_REG_CALL_STACK, REG_SAVE_BOT - CPU_TEMP_BUF_SIZE, - CPU_TEMP_BUF_SIZE); - - /* Prologue */ - tcg_out32(s, MFSPR | RT(TCG_REG_R0) | LR); - tcg_out32(s, (SZR == 8 ? STDU : STWU) - | SAI(TCG_REG_R1, TCG_REG_R1, -FRAME_SIZE)); - - for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); ++i) { - tcg_out_st(s, TCG_TYPE_REG, tcg_target_callee_save_regs[i], - TCG_REG_R1, REG_SAVE_BOT + i * SZR); - } - tcg_out_st(s, TCG_TYPE_PTR, TCG_REG_R0, TCG_REG_R1, FRAME_SIZE+LR_OFFSET); - -#ifdef CONFIG_USE_GUEST_BASE - if (GUEST_BASE) { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, GUEST_BASE); - tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); - } -#endif - - tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); - tcg_out32(s, MTSPR | RS(tcg_target_call_iarg_regs[1]) | CTR); - - if (USE_REG_RA) { -#ifdef _CALL_AIX - /* Make the caller load the value as the TOC into R2. */ - tb_ret_addr = s->code_ptr + 2; - desc[1] = tb_ret_addr; - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_RA, TCG_REG_R2); - tcg_out32(s, BCCTR | BO_ALWAYS); -#elif defined(_CALL_ELF) && _CALL_ELF == 2 - /* Compute from the incoming R12 value. */ - tb_ret_addr = s->code_ptr + 2; - tcg_out32(s, ADDI | TAI(TCG_REG_RA, TCG_REG_R12, - tcg_ptr_byte_diff(tb_ret_addr, s->code_buf))); - tcg_out32(s, BCCTR | BO_ALWAYS); -#else - /* Reserve max 5 insns for the constant load. */ - tb_ret_addr = s->code_ptr + 6; - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RA, (intptr_t)tb_ret_addr); - tcg_out32(s, BCCTR | BO_ALWAYS); - while (s->code_ptr < tb_ret_addr) { - tcg_out32(s, NOP); - } -#endif - } else { - tcg_out32(s, BCCTR | BO_ALWAYS); - tb_ret_addr = s->code_ptr; - } - - /* Epilogue */ - assert(tb_ret_addr == s->code_ptr); - - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R0, TCG_REG_R1, FRAME_SIZE+LR_OFFSET); - for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); ++i) { - tcg_out_ld(s, TCG_TYPE_REG, tcg_target_callee_save_regs[i], - TCG_REG_R1, REG_SAVE_BOT + i * SZR); - } - tcg_out32(s, MTSPR | RS(TCG_REG_R0) | LR); - tcg_out32(s, ADDI | TAI(TCG_REG_R1, TCG_REG_R1, FRAME_SIZE)); - tcg_out32(s, BCLR | BO_ALWAYS); -} - -static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, - const int *const_args) -{ - TCGArg a0, a1, a2; - int c; - - switch (opc) { - case INDEX_op_exit_tb: - if (USE_REG_RA) { - ptrdiff_t disp = tcg_pcrel_diff(s, tb_ret_addr); - - /* If we can use a direct branch, otherwise use the value in RA. - Note that the direct branch is always forward. If it's in - range now, it'll still be in range after the movi. Don't - bother about the 20 bytes where the test here fails but it - would succeed below. */ - if (!in_range_b(disp)) { - tcg_out32(s, MTSPR | RS(TCG_REG_RA) | CTR); - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R3, args[0]); - tcg_out32(s, BCCTR | BO_ALWAYS); - break; - } - } - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R3, args[0]); - tcg_out_b(s, 0, tb_ret_addr); - break; - case INDEX_op_goto_tb: - if (s->tb_jmp_offset) { - /* Direct jump method. */ - s->tb_jmp_offset[args[0]] = tcg_current_code_size(s); - s->code_ptr += 7; - } else { - /* Indirect jump method. */ - tcg_abort(); - } - s->tb_next_offset[args[0]] = tcg_current_code_size(s); - break; - case INDEX_op_br: - { - TCGLabel *l = arg_label(args[0]); - - if (l->has_value) { - tcg_out_b(s, 0, l->u.value_ptr); - } else { - tcg_out_reloc(s, s->code_ptr, R_PPC_REL24, l, 0); - tcg_out_b_noaddr(s, B); - } - } - break; - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - tcg_out_mem_long(s, LBZ, LBZX, args[0], args[1], args[2]); - break; - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - tcg_out_mem_long(s, LBZ, LBZX, args[0], args[1], args[2]); - tcg_out32(s, EXTSB | RS(args[0]) | RA(args[0])); - break; - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - tcg_out_mem_long(s, LHZ, LHZX, args[0], args[1], args[2]); - break; - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - tcg_out_mem_long(s, LHA, LHAX, args[0], args[1], args[2]); - break; - case INDEX_op_ld_i32: - case INDEX_op_ld32u_i64: - tcg_out_mem_long(s, LWZ, LWZX, args[0], args[1], args[2]); - break; - case INDEX_op_ld32s_i64: - tcg_out_mem_long(s, LWA, LWAX, args[0], args[1], args[2]); - break; - case INDEX_op_ld_i64: - tcg_out_mem_long(s, LD, LDX, args[0], args[1], args[2]); - break; - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - tcg_out_mem_long(s, STB, STBX, args[0], args[1], args[2]); - break; - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - tcg_out_mem_long(s, STH, STHX, args[0], args[1], args[2]); - break; - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - tcg_out_mem_long(s, STW, STWX, args[0], args[1], args[2]); - break; - case INDEX_op_st_i64: - tcg_out_mem_long(s, STD, STDX, args[0], args[1], args[2]); - break; - - case INDEX_op_add_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - do_addi_32: - tcg_out_mem_long(s, ADDI, ADD, a0, a1, (int32_t)a2); - } else { - tcg_out32(s, ADD | TAB(a0, a1, a2)); - } - break; - case INDEX_op_sub_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[1]) { - if (const_args[2]) { - tcg_out_movi(s, TCG_TYPE_I32, a0, a1 - a2); - } else { - tcg_out32(s, SUBFIC | TAI(a0, a2, a1)); - } - } else if (const_args[2]) { - a2 = -a2; - goto do_addi_32; - } else { - tcg_out32(s, SUBF | TAB(a0, a2, a1)); - } - break; - - case INDEX_op_and_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_andi32(s, a0, a1, a2); - } else { - tcg_out32(s, AND | SAB(a1, a0, a2)); - } - break; - case INDEX_op_and_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_andi64(s, a0, a1, a2); - } else { - tcg_out32(s, AND | SAB(a1, a0, a2)); - } - break; - case INDEX_op_or_i64: - case INDEX_op_or_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_ori32(s, a0, a1, a2); - } else { - tcg_out32(s, OR | SAB(a1, a0, a2)); - } - break; - case INDEX_op_xor_i64: - case INDEX_op_xor_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_xori32(s, a0, a1, a2); - } else { - tcg_out32(s, XOR | SAB(a1, a0, a2)); - } - break; - case INDEX_op_andc_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_andi32(s, a0, a1, ~a2); - } else { - tcg_out32(s, ANDC | SAB(a1, a0, a2)); - } - break; - case INDEX_op_andc_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_andi64(s, a0, a1, ~a2); - } else { - tcg_out32(s, ANDC | SAB(a1, a0, a2)); - } - break; - case INDEX_op_orc_i32: - if (const_args[2]) { - tcg_out_ori32(s, args[0], args[1], ~args[2]); - break; - } - /* FALLTHRU */ - case INDEX_op_orc_i64: - tcg_out32(s, ORC | SAB(args[1], args[0], args[2])); - break; - case INDEX_op_eqv_i32: - if (const_args[2]) { - tcg_out_xori32(s, args[0], args[1], ~args[2]); - break; - } - /* FALLTHRU */ - case INDEX_op_eqv_i64: - tcg_out32(s, EQV | SAB(args[1], args[0], args[2])); - break; - case INDEX_op_nand_i32: - case INDEX_op_nand_i64: - tcg_out32(s, NAND | SAB(args[1], args[0], args[2])); - break; - case INDEX_op_nor_i32: - case INDEX_op_nor_i64: - tcg_out32(s, NOR | SAB(args[1], args[0], args[2])); - break; - - case INDEX_op_mul_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out32(s, MULLI | TAI(a0, a1, a2)); - } else { - tcg_out32(s, MULLW | TAB(a0, a1, a2)); - } - break; - - case INDEX_op_div_i32: - tcg_out32(s, DIVW | TAB(args[0], args[1], args[2])); - break; - - case INDEX_op_divu_i32: - tcg_out32(s, DIVWU | TAB(args[0], args[1], args[2])); - break; - - case INDEX_op_shl_i32: - if (const_args[2]) { - tcg_out_shli32(s, args[0], args[1], args[2]); - } else { - tcg_out32(s, SLW | SAB(args[1], args[0], args[2])); - } - break; - case INDEX_op_shr_i32: - if (const_args[2]) { - tcg_out_shri32(s, args[0], args[1], args[2]); - } else { - tcg_out32(s, SRW | SAB(args[1], args[0], args[2])); - } - break; - case INDEX_op_sar_i32: - if (const_args[2]) { - tcg_out32(s, SRAWI | RS(args[1]) | RA(args[0]) | SH(args[2])); - } else { - tcg_out32(s, SRAW | SAB(args[1], args[0], args[2])); - } - break; - case INDEX_op_rotl_i32: - if (const_args[2]) { - tcg_out_rlw(s, RLWINM, args[0], args[1], args[2], 0, 31); - } else { - tcg_out32(s, RLWNM | SAB(args[1], args[0], args[2]) - | MB(0) | ME(31)); - } - break; - case INDEX_op_rotr_i32: - if (const_args[2]) { - tcg_out_rlw(s, RLWINM, args[0], args[1], 32 - args[2], 0, 31); - } else { - tcg_out32(s, SUBFIC | TAI(TCG_REG_R0, args[2], 32)); - tcg_out32(s, RLWNM | SAB(args[1], args[0], TCG_REG_R0) - | MB(0) | ME(31)); - } - break; - - case INDEX_op_brcond_i32: - tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], - arg_label(args[3]), TCG_TYPE_I32); - break; - case INDEX_op_brcond_i64: - tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], - arg_label(args[3]), TCG_TYPE_I64); - break; - case INDEX_op_brcond2_i32: - tcg_out_brcond2(s, args, const_args); - break; - - case INDEX_op_neg_i32: - case INDEX_op_neg_i64: - tcg_out32(s, NEG | RT(args[0]) | RA(args[1])); - break; - - case INDEX_op_not_i32: - case INDEX_op_not_i64: - tcg_out32(s, NOR | SAB(args[1], args[0], args[1])); - break; - - case INDEX_op_add_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - do_addi_64: - tcg_out_mem_long(s, ADDI, ADD, a0, a1, a2); - } else { - tcg_out32(s, ADD | TAB(a0, a1, a2)); - } - break; - case INDEX_op_sub_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[1]) { - if (const_args[2]) { - tcg_out_movi(s, TCG_TYPE_I64, a0, a1 - a2); - } else { - tcg_out32(s, SUBFIC | TAI(a0, a2, a1)); - } - } else if (const_args[2]) { - a2 = -a2; - goto do_addi_64; - } else { - tcg_out32(s, SUBF | TAB(a0, a2, a1)); - } - break; - - case INDEX_op_shl_i64: - if (const_args[2]) { - tcg_out_shli64(s, args[0], args[1], args[2]); - } else { - tcg_out32(s, SLD | SAB(args[1], args[0], args[2])); - } - break; - case INDEX_op_shr_i64: - if (const_args[2]) { - tcg_out_shri64(s, args[0], args[1], args[2]); - } else { - tcg_out32(s, SRD | SAB(args[1], args[0], args[2])); - } - break; - case INDEX_op_sar_i64: - if (const_args[2]) { - int sh = SH(args[2] & 0x1f) | (((args[2] >> 5) & 1) << 1); - tcg_out32(s, SRADI | RA(args[0]) | RS(args[1]) | sh); - } else { - tcg_out32(s, SRAD | SAB(args[1], args[0], args[2])); - } - break; - case INDEX_op_rotl_i64: - if (const_args[2]) { - tcg_out_rld(s, RLDICL, args[0], args[1], args[2], 0); - } else { - tcg_out32(s, RLDCL | SAB(args[1], args[0], args[2]) | MB64(0)); - } - break; - case INDEX_op_rotr_i64: - if (const_args[2]) { - tcg_out_rld(s, RLDICL, args[0], args[1], 64 - args[2], 0); - } else { - tcg_out32(s, SUBFIC | TAI(TCG_REG_R0, args[2], 64)); - tcg_out32(s, RLDCL | SAB(args[1], args[0], TCG_REG_R0) | MB64(0)); - } - break; - - case INDEX_op_mul_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out32(s, MULLI | TAI(a0, a1, a2)); - } else { - tcg_out32(s, MULLD | TAB(a0, a1, a2)); - } - break; - case INDEX_op_div_i64: - tcg_out32(s, DIVD | TAB(args[0], args[1], args[2])); - break; - case INDEX_op_divu_i64: - tcg_out32(s, DIVDU | TAB(args[0], args[1], args[2])); - break; - - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, args, false); - break; - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, args, true); - break; - case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, args, false); - break; - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, args, true); - break; - - case INDEX_op_ext8s_i32: - case INDEX_op_ext8s_i64: - c = EXTSB; - goto gen_ext; - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - c = EXTSH; - goto gen_ext; - case INDEX_op_ext32s_i64: - c = EXTSW; - goto gen_ext; - gen_ext: - tcg_out32(s, c | RS(args[1]) | RA(args[0])); - break; - - case INDEX_op_setcond_i32: - tcg_out_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], args[2], - const_args[2]); - break; - case INDEX_op_setcond_i64: - tcg_out_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1], args[2], - const_args[2]); - break; - case INDEX_op_setcond2_i32: - tcg_out_setcond2(s, args, const_args); - break; - - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: - a0 = args[0], a1 = args[1]; - /* a1 = abcd */ - if (a0 != a1) { - /* a0 = (a1 r<< 24) & 0xff # 000c */ - tcg_out_rlw(s, RLWINM, a0, a1, 24, 24, 31); - /* a0 = (a0 & ~0xff00) | (a1 r<< 8) & 0xff00 # 00dc */ - tcg_out_rlw(s, RLWIMI, a0, a1, 8, 16, 23); - } else { - /* r0 = (a1 r<< 8) & 0xff00 # 00d0 */ - tcg_out_rlw(s, RLWINM, TCG_REG_R0, a1, 8, 16, 23); - /* a0 = (a1 r<< 24) & 0xff # 000c */ - tcg_out_rlw(s, RLWINM, a0, a1, 24, 24, 31); - /* a0 = a0 | r0 # 00dc */ - tcg_out32(s, OR | SAB(TCG_REG_R0, a0, a0)); - } - break; - - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: - /* Stolen from gcc's builtin_bswap32 */ - a1 = args[1]; - a0 = args[0] == a1 ? TCG_REG_R0 : args[0]; - - /* a1 = args[1] # abcd */ - /* a0 = rotate_left (a1, 8) # bcda */ - tcg_out_rlw(s, RLWINM, a0, a1, 8, 0, 31); - /* a0 = (a0 & ~0xff000000) | ((a1 r<< 24) & 0xff000000) # dcda */ - tcg_out_rlw(s, RLWIMI, a0, a1, 24, 0, 7); - /* a0 = (a0 & ~0x0000ff00) | ((a1 r<< 24) & 0x0000ff00) # dcba */ - tcg_out_rlw(s, RLWIMI, a0, a1, 24, 16, 23); - - if (a0 == TCG_REG_R0) { - tcg_out_mov(s, TCG_TYPE_REG, args[0], a0); - } - break; - - case INDEX_op_bswap64_i64: - a0 = args[0], a1 = args[1], a2 = TCG_REG_R0; - if (a0 == a1) { - a0 = TCG_REG_R0; - a2 = a1; - } - - /* a1 = # abcd efgh */ - /* a0 = rl32(a1, 8) # 0000 fghe */ - tcg_out_rlw(s, RLWINM, a0, a1, 8, 0, 31); - /* a0 = dep(a0, rl32(a1, 24), 0xff000000) # 0000 hghe */ - tcg_out_rlw(s, RLWIMI, a0, a1, 24, 0, 7); - /* a0 = dep(a0, rl32(a1, 24), 0x0000ff00) # 0000 hgfe */ - tcg_out_rlw(s, RLWIMI, a0, a1, 24, 16, 23); - - /* a0 = rl64(a0, 32) # hgfe 0000 */ - /* a2 = rl64(a1, 32) # efgh abcd */ - tcg_out_rld(s, RLDICL, a0, a0, 32, 0); - tcg_out_rld(s, RLDICL, a2, a1, 32, 0); - - /* a0 = dep(a0, rl32(a2, 8), 0xffffffff) # hgfe bcda */ - tcg_out_rlw(s, RLWIMI, a0, a2, 8, 0, 31); - /* a0 = dep(a0, rl32(a2, 24), 0xff000000) # hgfe dcda */ - tcg_out_rlw(s, RLWIMI, a0, a2, 24, 0, 7); - /* a0 = dep(a0, rl32(a2, 24), 0x0000ff00) # hgfe dcba */ - tcg_out_rlw(s, RLWIMI, a0, a2, 24, 16, 23); - - if (a0 == 0) { - tcg_out_mov(s, TCG_TYPE_REG, args[0], a0); - } - break; - - case INDEX_op_deposit_i32: - if (const_args[2]) { - uint32_t mask = ((2u << (args[4] - 1)) - 1) << args[3]; - tcg_out_andi32(s, args[0], args[0], ~mask); - } else { - tcg_out_rlw(s, RLWIMI, args[0], args[2], args[3], - 32 - args[3] - args[4], 31 - args[3]); - } - break; - case INDEX_op_deposit_i64: - if (const_args[2]) { - uint64_t mask = ((2ull << (args[4] - 1)) - 1) << args[3]; - tcg_out_andi64(s, args[0], args[0], ~mask); - } else { - tcg_out_rld(s, RLDIMI, args[0], args[2], args[3], - 64 - args[3] - args[4]); - } - break; - - case INDEX_op_movcond_i32: - tcg_out_movcond(s, TCG_TYPE_I32, args[5], args[0], args[1], args[2], - args[3], args[4], const_args[2]); - break; - case INDEX_op_movcond_i64: - tcg_out_movcond(s, TCG_TYPE_I64, args[5], args[0], args[1], args[2], - args[3], args[4], const_args[2]); - break; - -#if TCG_TARGET_REG_BITS == 64 - case INDEX_op_add2_i64: -#else - case INDEX_op_add2_i32: -#endif - /* Note that the CA bit is defined based on the word size of the - environment. So in 64-bit mode it's always carry-out of bit 63. - The fallback code using deposit works just as well for 32-bit. */ - a0 = args[0], a1 = args[1]; - if (a0 == args[3] || (!const_args[5] && a0 == args[5])) { - a0 = TCG_REG_R0; - } - if (const_args[4]) { - tcg_out32(s, ADDIC | TAI(a0, args[2], args[4])); - } else { - tcg_out32(s, ADDC | TAB(a0, args[2], args[4])); - } - if (const_args[5]) { - tcg_out32(s, (args[5] ? ADDME : ADDZE) | RT(a1) | RA(args[3])); - } else { - tcg_out32(s, ADDE | TAB(a1, args[3], args[5])); - } - if (a0 != args[0]) { - tcg_out_mov(s, TCG_TYPE_REG, args[0], a0); - } - break; - -#if TCG_TARGET_REG_BITS == 64 - case INDEX_op_sub2_i64: -#else - case INDEX_op_sub2_i32: -#endif - a0 = args[0], a1 = args[1]; - if (a0 == args[5] || (!const_args[3] && a0 == args[3])) { - a0 = TCG_REG_R0; - } - if (const_args[2]) { - tcg_out32(s, SUBFIC | TAI(a0, args[4], args[2])); - } else { - tcg_out32(s, SUBFC | TAB(a0, args[4], args[2])); - } - if (const_args[3]) { - tcg_out32(s, (args[3] ? SUBFME : SUBFZE) | RT(a1) | RA(args[5])); - } else { - tcg_out32(s, SUBFE | TAB(a1, args[5], args[3])); - } - if (a0 != args[0]) { - tcg_out_mov(s, TCG_TYPE_REG, args[0], a0); - } - break; - - case INDEX_op_muluh_i32: - tcg_out32(s, MULHWU | TAB(args[0], args[1], args[2])); - break; - case INDEX_op_mulsh_i32: - tcg_out32(s, MULHW | TAB(args[0], args[1], args[2])); - break; - case INDEX_op_muluh_i64: - tcg_out32(s, MULHDU | TAB(args[0], args[1], args[2])); - break; - case INDEX_op_mulsh_i64: - tcg_out32(s, MULHD | TAB(args[0], args[1], args[2])); - break; - - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_mov_i64: - case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ - case INDEX_op_movi_i64: - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - default: - tcg_abort(); - } -} - -static const TCGTargetOpDef ppc_op_defs[] = { - { INDEX_op_exit_tb, { } }, - { INDEX_op_goto_tb, { } }, - { INDEX_op_br, { } }, - - { INDEX_op_ld8u_i32, { "r", "r" } }, - { INDEX_op_ld8s_i32, { "r", "r" } }, - { INDEX_op_ld16u_i32, { "r", "r" } }, - { INDEX_op_ld16s_i32, { "r", "r" } }, - { INDEX_op_ld_i32, { "r", "r" } }, - - { INDEX_op_st8_i32, { "r", "r" } }, - { INDEX_op_st16_i32, { "r", "r" } }, - { INDEX_op_st_i32, { "r", "r" } }, - - { INDEX_op_add_i32, { "r", "r", "ri" } }, - { INDEX_op_mul_i32, { "r", "r", "rI" } }, - { INDEX_op_div_i32, { "r", "r", "r" } }, - { INDEX_op_divu_i32, { "r", "r", "r" } }, - { INDEX_op_sub_i32, { "r", "rI", "ri" } }, - { INDEX_op_and_i32, { "r", "r", "ri" } }, - { INDEX_op_or_i32, { "r", "r", "ri" } }, - { INDEX_op_xor_i32, { "r", "r", "ri" } }, - { INDEX_op_andc_i32, { "r", "r", "ri" } }, - { INDEX_op_orc_i32, { "r", "r", "ri" } }, - { INDEX_op_eqv_i32, { "r", "r", "ri" } }, - { INDEX_op_nand_i32, { "r", "r", "r" } }, - { INDEX_op_nor_i32, { "r", "r", "r" } }, - - { INDEX_op_shl_i32, { "r", "r", "ri" } }, - { INDEX_op_shr_i32, { "r", "r", "ri" } }, - { INDEX_op_sar_i32, { "r", "r", "ri" } }, - { INDEX_op_rotl_i32, { "r", "r", "ri" } }, - { INDEX_op_rotr_i32, { "r", "r", "ri" } }, - - { INDEX_op_neg_i32, { "r", "r" } }, - { INDEX_op_not_i32, { "r", "r" } }, - { INDEX_op_ext8s_i32, { "r", "r" } }, - { INDEX_op_ext16s_i32, { "r", "r" } }, - { INDEX_op_bswap16_i32, { "r", "r" } }, - { INDEX_op_bswap32_i32, { "r", "r" } }, - - { INDEX_op_brcond_i32, { "r", "ri" } }, - { INDEX_op_setcond_i32, { "r", "r", "ri" } }, - { INDEX_op_movcond_i32, { "r", "r", "ri", "rZ", "rZ" } }, - - { INDEX_op_deposit_i32, { "r", "0", "rZ" } }, - - { INDEX_op_muluh_i32, { "r", "r", "r" } }, - { INDEX_op_mulsh_i32, { "r", "r", "r" } }, - -#if TCG_TARGET_REG_BITS == 64 - { INDEX_op_ld8u_i64, { "r", "r" } }, - { INDEX_op_ld8s_i64, { "r", "r" } }, - { INDEX_op_ld16u_i64, { "r", "r" } }, - { INDEX_op_ld16s_i64, { "r", "r" } }, - { INDEX_op_ld32u_i64, { "r", "r" } }, - { INDEX_op_ld32s_i64, { "r", "r" } }, - { INDEX_op_ld_i64, { "r", "r" } }, - - { INDEX_op_st8_i64, { "r", "r" } }, - { INDEX_op_st16_i64, { "r", "r" } }, - { INDEX_op_st32_i64, { "r", "r" } }, - { INDEX_op_st_i64, { "r", "r" } }, - - { INDEX_op_add_i64, { "r", "r", "rT" } }, - { INDEX_op_sub_i64, { "r", "rI", "rT" } }, - { INDEX_op_and_i64, { "r", "r", "ri" } }, - { INDEX_op_or_i64, { "r", "r", "rU" } }, - { INDEX_op_xor_i64, { "r", "r", "rU" } }, - { INDEX_op_andc_i64, { "r", "r", "ri" } }, - { INDEX_op_orc_i64, { "r", "r", "r" } }, - { INDEX_op_eqv_i64, { "r", "r", "r" } }, - { INDEX_op_nand_i64, { "r", "r", "r" } }, - { INDEX_op_nor_i64, { "r", "r", "r" } }, - - { INDEX_op_shl_i64, { "r", "r", "ri" } }, - { INDEX_op_shr_i64, { "r", "r", "ri" } }, - { INDEX_op_sar_i64, { "r", "r", "ri" } }, - { INDEX_op_rotl_i64, { "r", "r", "ri" } }, - { INDEX_op_rotr_i64, { "r", "r", "ri" } }, - - { INDEX_op_mul_i64, { "r", "r", "rI" } }, - { INDEX_op_div_i64, { "r", "r", "r" } }, - { INDEX_op_divu_i64, { "r", "r", "r" } }, - - { INDEX_op_neg_i64, { "r", "r" } }, - { INDEX_op_not_i64, { "r", "r" } }, - { INDEX_op_ext8s_i64, { "r", "r" } }, - { INDEX_op_ext16s_i64, { "r", "r" } }, - { INDEX_op_ext32s_i64, { "r", "r" } }, - { INDEX_op_bswap16_i64, { "r", "r" } }, - { INDEX_op_bswap32_i64, { "r", "r" } }, - { INDEX_op_bswap64_i64, { "r", "r" } }, - - { INDEX_op_brcond_i64, { "r", "ri" } }, - { INDEX_op_setcond_i64, { "r", "r", "ri" } }, - { INDEX_op_movcond_i64, { "r", "r", "ri", "rZ", "rZ" } }, - - { INDEX_op_deposit_i64, { "r", "0", "rZ" } }, - - { INDEX_op_mulsh_i64, { "r", "r", "r" } }, - { INDEX_op_muluh_i64, { "r", "r", "r" } }, -#endif - -#if TCG_TARGET_REG_BITS == 32 - { INDEX_op_brcond2_i32, { "r", "r", "ri", "ri" } }, - { INDEX_op_setcond2_i32, { "r", "r", "r", "ri", "ri" } }, -#endif - -#if TCG_TARGET_REG_BITS == 64 - { INDEX_op_add2_i64, { "r", "r", "r", "r", "rI", "rZM" } }, - { INDEX_op_sub2_i64, { "r", "r", "rI", "rZM", "r", "r" } }, -#else - { INDEX_op_add2_i32, { "r", "r", "r", "r", "rI", "rZM" } }, - { INDEX_op_sub2_i32, { "r", "r", "rI", "rZM", "r", "r" } }, -#endif - -#if TCG_TARGET_REG_BITS == 64 - { INDEX_op_qemu_ld_i32, { "r", "L" } }, - { INDEX_op_qemu_st_i32, { "S", "S" } }, - { INDEX_op_qemu_ld_i64, { "r", "L" } }, - { INDEX_op_qemu_st_i64, { "S", "S" } }, -#elif TARGET_LONG_BITS == 32 - { INDEX_op_qemu_ld_i32, { "r", "L" } }, - { INDEX_op_qemu_st_i32, { "S", "S" } }, - { INDEX_op_qemu_ld_i64, { "L", "L", "L" } }, - { INDEX_op_qemu_st_i64, { "S", "S", "S" } }, -#else - { INDEX_op_qemu_ld_i32, { "r", "L", "L" } }, - { INDEX_op_qemu_st_i32, { "S", "S", "S" } }, - { INDEX_op_qemu_ld_i64, { "L", "L", "L", "L" } }, - { INDEX_op_qemu_st_i64, { "S", "S", "S", "S" } }, -#endif - - { -1 }, -}; - -static void tcg_target_init(TCGContext *s) -{ - unsigned long hwcap = qemu_getauxval(AT_HWCAP); - if (hwcap & PPC_FEATURE_ARCH_2_06) { - have_isa_2_06 = true; - } - - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffffffff); - tcg_regset_set32(tcg_target_call_clobber_regs, 0, - (1 << TCG_REG_R0) | - (1 << TCG_REG_R2) | - (1 << TCG_REG_R3) | - (1 << TCG_REG_R4) | - (1 << TCG_REG_R5) | - (1 << TCG_REG_R6) | - (1 << TCG_REG_R7) | - (1 << TCG_REG_R8) | - (1 << TCG_REG_R9) | - (1 << TCG_REG_R10) | - (1 << TCG_REG_R11) | - (1 << TCG_REG_R12)); - - tcg_regset_clear(s->reserved_regs); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); /* tcg temp */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R1); /* stack pointer */ -#if defined(_CALL_SYSV) - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R2); /* toc pointer */ -#endif -#if defined(_CALL_SYSV) || TCG_TARGET_REG_BITS == 64 - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R13); /* thread pointer */ -#endif - tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP1); /* mem temp */ - if (USE_REG_RA) { - tcg_regset_set_reg(s->reserved_regs, TCG_REG_RA); /* return addr */ - } - - tcg_add_target_add_op_defs(ppc_op_defs); -} - -#ifdef __ELF__ -typedef struct { - DebugFrameCIE cie; - DebugFrameFDEHeader fde; - uint8_t fde_def_cfa[4]; - uint8_t fde_reg_ofs[ARRAY_SIZE(tcg_target_callee_save_regs) * 2 + 3]; -} DebugFrame; - -/* We're expecting a 2 byte uleb128 encoded value. */ -QEMU_BUILD_BUG_ON(FRAME_SIZE >= (1 << 14)); - -#if TCG_TARGET_REG_BITS == 64 -# define ELF_HOST_MACHINE EM_PPC64 -#else -# define ELF_HOST_MACHINE EM_PPC -#endif - -static DebugFrame debug_frame = { - .cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ - .cie.id = -1, - .cie.version = 1, - .cie.code_align = 1, - .cie.data_align = (-SZR & 0x7f), /* sleb128 -SZR */ - .cie.return_column = 65, - - /* Total FDE size does not include the "len" member. */ - .fde.len = sizeof(DebugFrame) - offsetof(DebugFrame, fde.cie_offset), - - .fde_def_cfa = { - 12, TCG_REG_R1, /* DW_CFA_def_cfa r1, ... */ - (FRAME_SIZE & 0x7f) | 0x80, /* ... uleb128 FRAME_SIZE */ - (FRAME_SIZE >> 7) - }, - .fde_reg_ofs = { - /* DW_CFA_offset_extended_sf, lr, LR_OFFSET */ - 0x11, 65, (LR_OFFSET / -SZR) & 0x7f, - } -}; - -void tcg_register_jit(void *buf, size_t buf_size) -{ - uint8_t *p = &debug_frame.fde_reg_ofs[3]; - int i; - - for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); ++i, p += 2) { - p[0] = 0x80 + tcg_target_callee_save_regs[i]; - p[1] = (FRAME_SIZE - (REG_SAVE_BOT + i * SZR)) / SZR; - } - - debug_frame.fde.func_start = (uintptr_t)buf; - debug_frame.fde.func_len = buf_size; - - tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); -} -#endif /* __ELF__ */ - -static size_t dcache_bsize = 16; -static size_t icache_bsize = 16; - -void flush_icache_range(uintptr_t start, uintptr_t stop) -{ - uintptr_t p, start1, stop1; - size_t dsize = dcache_bsize; - size_t isize = icache_bsize; - - start1 = start & ~(dsize - 1); - stop1 = (stop + dsize - 1) & ~(dsize - 1); - for (p = start1; p < stop1; p += dsize) { - asm volatile ("dcbst 0,%0" : : "r"(p) : "memory"); - } - asm volatile ("sync" : : : "memory"); - - start &= start & ~(isize - 1); - stop1 = (stop + isize - 1) & ~(isize - 1); - for (p = start1; p < stop1; p += isize) { - asm volatile ("icbi 0,%0" : : "r"(p) : "memory"); - } - asm volatile ("sync" : : : "memory"); - asm volatile ("isync" : : : "memory"); -} - -#if defined _AIX -#include - -static void __attribute__((constructor)) tcg_cache_init(void) -{ - icache_bsize = _system_configuration.icache_line; - dcache_bsize = _system_configuration.dcache_line; -} - -#elif defined __linux__ -static void __attribute__((constructor)) tcg_cache_init(void) -{ - unsigned long dsize = qemu_getauxval(AT_DCACHEBSIZE); - unsigned long isize = qemu_getauxval(AT_ICACHEBSIZE); - - if (dsize == 0 || isize == 0) { - if (dsize == 0) { - fprintf(stderr, "getauxval AT_DCACHEBSIZE failed\n"); - } - if (isize == 0) { - fprintf(stderr, "getauxval AT_ICACHEBSIZE failed\n"); - } - exit(1); - } - dcache_bsize = dsize; - icache_bsize = isize; -} - -#elif defined __APPLE__ -#include -#include -#include - -static void __attribute__((constructor)) tcg_cache_init(void) -{ - size_t len; - unsigned cacheline; - int name[2] = { CTL_HW, HW_CACHELINE }; - - len = sizeof(cacheline); - if (sysctl(name, 2, &cacheline, &len, NULL, 0)) { - perror("sysctl CTL_HW HW_CACHELINE failed"); - exit(1); - } - dcache_bsize = cacheline; - icache_bsize = cacheline; -} - -#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) -#include -#include -#include -#include -#include -#include - -static void __attribute__((constructor)) tcg_cache_init(void) -{ - size_t len = 4; - unsigned cacheline; - - if (sysctlbyname ("machdep.cacheline_size", &cacheline, &len, NULL, 0)) { - fprintf(stderr, "sysctlbyname machdep.cacheline_size failed: %s\n", - strerror(errno)); - exit(1); - } - dcache_bsize = cacheline; - icache_bsize = cacheline; -} -#endif diff --git a/qemu/tcg/ppc/tcg-target.h b/qemu/tcg/ppc/tcg-target.h index 7ce704882..b4f081876 100644 --- a/qemu/tcg/ppc/tcg-target.h +++ b/qemu/tcg/ppc/tcg-target.h @@ -77,7 +77,8 @@ typedef enum { #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 -#define TCG_TARGET_HAS_trunc_shr_i32 0 +#define TCG_TARGET_HAS_extrl_i64_i32 0 +#define TCG_TARGET_HAS_extrh_i64_i32 0 #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 0 #define TCG_TARGET_HAS_rot_i64 1 diff --git a/qemu/tcg/ppc/tcg-target.inc.c b/qemu/tcg/ppc/tcg-target.inc.c new file mode 100644 index 000000000..00bb90fc2 --- /dev/null +++ b/qemu/tcg/ppc/tcg-target.inc.c @@ -0,0 +1,2761 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "tcg-be-ldst.h" + +#if defined _CALL_DARWIN || defined __APPLE__ +#define TCG_TARGET_CALL_DARWIN +#endif +#ifdef _CALL_SYSV +# define TCG_TARGET_CALL_ALIGN_ARGS 1 +#endif + +/* For some memory operations, we need a scratch that isn't R0. For the AIX + calling convention, we can re-use the TOC register since we'll be reloading + it at every call. Otherwise R12 will do nicely as neither a call-saved + register nor a parameter register. */ +#ifdef _CALL_AIX +# define TCG_REG_TMP1 TCG_REG_R2 +#else +# define TCG_REG_TMP1 TCG_REG_R12 +#endif + +/* For the 64-bit target, we don't like the 5 insn sequence needed to build + full 64-bit addresses. Better to have a base register to which we can + apply a 32-bit displacement. + + There are generally three items of interest: + (1) helper functions in the main executable, + (2) TranslationBlock data structures, + (3) the return address in the epilogue. + + For user-only, we USE_STATIC_CODE_GEN_BUFFER, so the code_gen_buffer + will be inside the main executable, and thus near enough to make a + pointer to the epilogue be within 2GB of all helper functions. + + For softmmu, we'll let the kernel choose the address of code_gen_buffer, + and odds are it'll be somewhere close to the main malloc arena, and so + a pointer to the epilogue will be within 2GB of the TranslationBlocks. + + For --enable-pie, everything will be kinda near everything else, + somewhere in high memory. + + Thus we choose to keep the return address in a call-saved register. */ +#define TCG_REG_RA TCG_REG_R31 +#define USE_REG_RA (TCG_TARGET_REG_BITS == 64) + +/* Shorthand for size of a pointer. Avoid promotion to unsigned. */ +#define SZP ((int)sizeof(void *)) + +/* Shorthand for size of a register. */ +#define SZR (TCG_TARGET_REG_BITS / 8) + +#define TCG_CT_CONST_S16 0x100 +#define TCG_CT_CONST_U16 0x200 +#define TCG_CT_CONST_S32 0x400 +#define TCG_CT_CONST_U32 0x800 +#define TCG_CT_CONST_ZERO 0x1000 +#define TCG_CT_CONST_MONE 0x2000 + +static tcg_insn_unit *tb_ret_addr; + +#include "elf.h" +static bool have_isa_2_06; +#define HAVE_ISA_2_06 have_isa_2_06 +#define HAVE_ISEL have_isa_2_06 + +#ifndef CONFIG_SOFTMMU +#define TCG_GUEST_BASE_REG 30 +#endif + +#ifdef CONFIG_DEBUG_TCG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "r0", + "r1", + "r2", + "r3", + "r4", + "r5", + "r6", + "r7", + "r8", + "r9", + "r10", + "r11", + "r12", + "r13", + "r14", + "r15", + "r16", + "r17", + "r18", + "r19", + "r20", + "r21", + "r22", + "r23", + "r24", + "r25", + "r26", + "r27", + "r28", + "r29", + "r30", + "r31" +}; +#endif + +static const int tcg_target_reg_alloc_order[] = { + TCG_REG_R14, /* call saved registers */ + TCG_REG_R15, + TCG_REG_R16, + TCG_REG_R17, + TCG_REG_R18, + TCG_REG_R19, + TCG_REG_R20, + TCG_REG_R21, + TCG_REG_R22, + TCG_REG_R23, + TCG_REG_R24, + TCG_REG_R25, + TCG_REG_R26, + TCG_REG_R27, + TCG_REG_R28, + TCG_REG_R29, + TCG_REG_R30, + TCG_REG_R31, + TCG_REG_R12, /* call clobbered, non-arguments */ + TCG_REG_R11, + TCG_REG_R2, + TCG_REG_R13, + TCG_REG_R10, /* call clobbered, arguments */ + TCG_REG_R9, + TCG_REG_R8, + TCG_REG_R7, + TCG_REG_R6, + TCG_REG_R5, + TCG_REG_R4, + TCG_REG_R3, +}; + +static const int tcg_target_call_iarg_regs[] = { + TCG_REG_R3, + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10 +}; + +static const int tcg_target_call_oarg_regs[] = { + TCG_REG_R3, + TCG_REG_R4 +}; + +static const int tcg_target_callee_save_regs[] = { +#ifdef TCG_TARGET_CALL_DARWIN + TCG_REG_R11, +#endif + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R16, + TCG_REG_R17, + TCG_REG_R18, + TCG_REG_R19, + TCG_REG_R20, + TCG_REG_R21, + TCG_REG_R22, + TCG_REG_R23, + TCG_REG_R24, + TCG_REG_R25, + TCG_REG_R26, + TCG_REG_R27, /* currently used for the global env */ + TCG_REG_R28, + TCG_REG_R29, + TCG_REG_R30, + TCG_REG_R31 +}; + +static inline bool in_range_b(tcg_target_long target) +{ + return target == sextract64(target, 0, 26); +} + +static uint32_t reloc_pc24_val(tcg_insn_unit *pc, tcg_insn_unit *target) +{ + ptrdiff_t disp = tcg_ptr_byte_diff(target, pc); + tcg_debug_assert(in_range_b(disp)); + return disp & 0x3fffffc; +} + +static void reloc_pc24(tcg_insn_unit *pc, tcg_insn_unit *target) +{ + *pc = (*pc & ~0x3fffffc) | reloc_pc24_val(pc, target); +} + +static uint16_t reloc_pc14_val(tcg_insn_unit *pc, tcg_insn_unit *target) +{ + ptrdiff_t disp = tcg_ptr_byte_diff(target, pc); + tcg_debug_assert(disp == (int16_t) disp); + return disp & 0xfffc; +} + +static void reloc_pc14(tcg_insn_unit *pc, tcg_insn_unit *target) +{ + *pc = (*pc & ~0xfffc) | reloc_pc14_val(pc, target); +} + +static inline void tcg_out_b_noaddr(TCGContext *s, int insn) +{ + unsigned retrans = *s->code_ptr & 0x3fffffc; + tcg_out32(s, insn | retrans); +} + +static inline void tcg_out_bc_noaddr(TCGContext *s, int insn) +{ + unsigned retrans = *s->code_ptr & 0xfffc; + tcg_out32(s, insn | retrans); +} + +static void patch_reloc(tcg_insn_unit *code_ptr, int type, + intptr_t value, intptr_t addend) +{ + tcg_insn_unit *target = (tcg_insn_unit *)value; + + tcg_debug_assert(addend == 0); + switch (type) { + case R_PPC_REL14: + reloc_pc14(code_ptr, target); + break; + case R_PPC_REL24: + reloc_pc24(code_ptr, target); + break; + default: + tcg_abort(); + } +} + +/* parse target specific constraints */ +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +{ + const char *ct_str; + + ct_str = *pct_str; + switch (ct_str[0]) { + case 'A': case 'B': case 'C': case 'D': + ct->ct |= TCG_CT_REG; + tcg_regset_set_reg(ct->u.regs, 3 + ct_str[0] - 'A'); + break; + case 'r': + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffffffff); + break; + case 'L': /* qemu_ld constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffffffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3); +#ifdef CONFIG_SOFTMMU + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R5); +#endif + break; + case 'S': /* qemu_st constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffffffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3); +#ifdef CONFIG_SOFTMMU + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R5); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R6); +#endif + break; + case 'I': + ct->ct |= TCG_CT_CONST_S16; + break; + case 'J': + ct->ct |= TCG_CT_CONST_U16; + break; + case 'M': + ct->ct |= TCG_CT_CONST_MONE; + break; + case 'T': + ct->ct |= TCG_CT_CONST_S32; + break; + case 'U': + ct->ct |= TCG_CT_CONST_U32; + break; + case 'Z': + ct->ct |= TCG_CT_CONST_ZERO; + break; + default: + return -1; + } + ct_str++; + *pct_str = ct_str; + return 0; +} + +/* test if a constant matches the constraint */ +static int tcg_target_const_match(tcg_target_long val, TCGType type, + const TCGArgConstraint *arg_ct) +{ + int ct = arg_ct->ct; + if (ct & TCG_CT_CONST) { + return 1; + } + + /* The only 32-bit constraint we use aside from + TCG_CT_CONST is TCG_CT_CONST_S16. */ + if (type == TCG_TYPE_I32) { + val = (int32_t)val; + } + + if ((ct & TCG_CT_CONST_S16) && val == (int16_t)val) { + return 1; + } else if ((ct & TCG_CT_CONST_U16) && val == (uint16_t)val) { + return 1; + } else if ((ct & TCG_CT_CONST_S32) && val == (int32_t)val) { + return 1; + } else if ((ct & TCG_CT_CONST_U32) && val == (uint32_t)val) { + return 1; + } else if ((ct & TCG_CT_CONST_ZERO) && val == 0) { + return 1; + } else if ((ct & TCG_CT_CONST_MONE) && val == -1) { + return 1; + } + return 0; +} + +#define OPCD(opc) ((opc)<<26) +#define XO19(opc) (OPCD(19)|((opc)<<1)) +#define MD30(opc) (OPCD(30)|((opc)<<2)) +#define MDS30(opc) (OPCD(30)|((opc)<<1)) +#define XO31(opc) (OPCD(31)|((opc)<<1)) +#define XO58(opc) (OPCD(58)|(opc)) +#define XO62(opc) (OPCD(62)|(opc)) + +#define B OPCD( 18) +#define BC OPCD( 16) +#define LBZ OPCD( 34) +#define LHZ OPCD( 40) +#define LHA OPCD( 42) +#define LWZ OPCD( 32) +#define STB OPCD( 38) +#define STH OPCD( 44) +#define STW OPCD( 36) + +#define STD XO62( 0) +#define STDU XO62( 1) +#define STDX XO31(149) + +#define LD XO58( 0) +#define LDX XO31( 21) +#define LDU XO58( 1) +#define LWA XO58( 2) +#define LWAX XO31(341) + +#define ADDIC OPCD( 12) +#define ADDI OPCD( 14) +#define ADDIS OPCD( 15) +#define ORI OPCD( 24) +#define ORIS OPCD( 25) +#define XORI OPCD( 26) +#define XORIS OPCD( 27) +#define ANDI OPCD( 28) +#define ANDIS OPCD( 29) +#define MULLI OPCD( 7) +#define CMPLI OPCD( 10) +#define CMPI OPCD( 11) +#define SUBFIC OPCD( 8) + +#define LWZU OPCD( 33) +#define STWU OPCD( 37) + +#define RLWIMI OPCD( 20) +#define RLWINM OPCD( 21) +#define RLWNM OPCD( 23) + +#define RLDICL MD30( 0) +#define RLDICR MD30( 1) +#define RLDIMI MD30( 3) +#define RLDCL MDS30( 8) + +#define BCLR XO19( 16) +#define BCCTR XO19(528) +#define CRAND XO19(257) +#define CRANDC XO19(129) +#define CRNAND XO19(225) +#define CROR XO19(449) +#define CRNOR XO19( 33) + +#define EXTSB XO31(954) +#define EXTSH XO31(922) +#define EXTSW XO31(986) +#define ADD XO31(266) +#define ADDE XO31(138) +#define ADDME XO31(234) +#define ADDZE XO31(202) +#define ADDC XO31( 10) +#define AND XO31( 28) +#define SUBF XO31( 40) +#define SUBFC XO31( 8) +#define SUBFE XO31(136) +#define SUBFME XO31(232) +#define SUBFZE XO31(200) +#define OR XO31(444) +#define XOR XO31(316) +#define MULLW XO31(235) +#define MULHW XO31( 75) +#define MULHWU XO31( 11) +#define DIVW XO31(491) +#define DIVWU XO31(459) +#define CMP XO31( 0) +#define CMPL XO31( 32) +#define LHBRX XO31(790) +#define LWBRX XO31(534) +#define LDBRX XO31(532) +#define STHBRX XO31(918) +#define STWBRX XO31(662) +#define STDBRX XO31(660) +#define MFSPR XO31(339) +#define MTSPR XO31(467) +#define SRAWI XO31(824) +#define NEG XO31(104) +#define MFCR XO31( 19) +#define MFOCRF (MFCR | (1u << 20)) +#define NOR XO31(124) +#define CNTLZW XO31( 26) +#define CNTLZD XO31( 58) +#define ANDC XO31( 60) +#define ORC XO31(412) +#define EQV XO31(284) +#define NAND XO31(476) +#define ISEL XO31( 15) + +#define MULLD XO31(233) +#define MULHD XO31( 73) +#define MULHDU XO31( 9) +#define DIVD XO31(489) +#define DIVDU XO31(457) + +#define LBZX XO31( 87) +#define LHZX XO31(279) +#define LHAX XO31(343) +#define LWZX XO31( 23) +#define STBX XO31(215) +#define STHX XO31(407) +#define STWX XO31(151) + +#define SPR(a, b) ((((a)<<5)|(b))<<11) +#define LR SPR(8, 0) +#define CTR SPR(9, 0) + +#define SLW XO31( 24) +#define SRW XO31(536) +#define SRAW XO31(792) + +#define SLD XO31( 27) +#define SRD XO31(539) +#define SRAD XO31(794) +#define SRADI XO31(413<<1) + +#define TW XO31( 4) +#define TRAP (TW | TO(31)) + +#define NOP ORI /* ori 0,0,0 */ + +#define RT(r) ((r)<<21) +#define RS(r) ((r)<<21) +#define RA(r) ((r)<<16) +#define RB(r) ((r)<<11) +#define TO(t) ((t)<<21) +#define SH(s) ((s)<<11) +#define MB(b) ((b)<<6) +#define ME(e) ((e)<<1) +#define BO(o) ((o)<<21) +#define MB64(b) ((b)<<5) +#define FXM(b) (1 << (19 - (b))) + +#define LK 1 + +#define TAB(t, a, b) (RT(t) | RA(a) | RB(b)) +#define SAB(s, a, b) (RS(s) | RA(a) | RB(b)) +#define TAI(s, a, i) (RT(s) | RA(a) | ((i) & 0xffff)) +#define SAI(s, a, i) (RS(s) | RA(a) | ((i) & 0xffff)) + +#define BF(n) ((n)<<23) +#define BI(n, c) (((c)+((n)*4))<<16) +#define BT(n, c) (((c)+((n)*4))<<21) +#define BA(n, c) (((c)+((n)*4))<<16) +#define BB(n, c) (((c)+((n)*4))<<11) +#define BC_(n, c) (((c)+((n)*4))<<6) + +#define BO_COND_TRUE BO(12) +#define BO_COND_FALSE BO( 4) +#define BO_ALWAYS BO(20) + +enum { + CR_LT, + CR_GT, + CR_EQ, + CR_SO +}; + +static const uint32_t tcg_to_bc[] = { + [TCG_COND_EQ] = BC | BI(7, CR_EQ) | BO_COND_TRUE, + [TCG_COND_NE] = BC | BI(7, CR_EQ) | BO_COND_FALSE, + [TCG_COND_LT] = BC | BI(7, CR_LT) | BO_COND_TRUE, + [TCG_COND_GE] = BC | BI(7, CR_LT) | BO_COND_FALSE, + [TCG_COND_LE] = BC | BI(7, CR_GT) | BO_COND_FALSE, + [TCG_COND_GT] = BC | BI(7, CR_GT) | BO_COND_TRUE, + [TCG_COND_LTU] = BC | BI(7, CR_LT) | BO_COND_TRUE, + [TCG_COND_GEU] = BC | BI(7, CR_LT) | BO_COND_FALSE, + [TCG_COND_LEU] = BC | BI(7, CR_GT) | BO_COND_FALSE, + [TCG_COND_GTU] = BC | BI(7, CR_GT) | BO_COND_TRUE, +}; + +/* The low bit here is set if the RA and RB fields must be inverted. */ +static const uint32_t tcg_to_isel[] = { + [TCG_COND_EQ] = ISEL | BC_(7, CR_EQ), + [TCG_COND_NE] = ISEL | BC_(7, CR_EQ) | 1, + [TCG_COND_LT] = ISEL | BC_(7, CR_LT), + [TCG_COND_GE] = ISEL | BC_(7, CR_LT) | 1, + [TCG_COND_LE] = ISEL | BC_(7, CR_GT) | 1, + [TCG_COND_GT] = ISEL | BC_(7, CR_GT), + [TCG_COND_LTU] = ISEL | BC_(7, CR_LT), + [TCG_COND_GEU] = ISEL | BC_(7, CR_LT) | 1, + [TCG_COND_LEU] = ISEL | BC_(7, CR_GT) | 1, + [TCG_COND_GTU] = ISEL | BC_(7, CR_GT), +}; + +static void tcg_out_mem_long(TCGContext *s, int opi, int opx, TCGReg rt, + TCGReg base, tcg_target_long offset); + +static void tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) +{ + tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); + if (ret != arg) { + tcg_out32(s, OR | SAB(arg, ret, arg)); + } +} + +static inline void tcg_out_rld(TCGContext *s, int op, TCGReg ra, TCGReg rs, + int sh, int mb) +{ + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + sh = SH(sh & 0x1f) | (((sh >> 5) & 1) << 1); + mb = MB64((mb >> 5) | ((mb << 1) & 0x3f)); + tcg_out32(s, op | RA(ra) | RS(rs) | sh | mb); +} + +static inline void tcg_out_rlw(TCGContext *s, int op, TCGReg ra, TCGReg rs, + int sh, int mb, int me) +{ + tcg_out32(s, op | RA(ra) | RS(rs) | SH(sh) | MB(mb) | ME(me)); +} + +static inline void tcg_out_ext32u(TCGContext *s, TCGReg dst, TCGReg src) +{ + tcg_out_rld(s, RLDICL, dst, src, 0, 32); +} + +static inline void tcg_out_shli32(TCGContext *s, TCGReg dst, TCGReg src, int c) +{ + tcg_out_rlw(s, RLWINM, dst, src, c, 0, 31 - c); +} + +static inline void tcg_out_shli64(TCGContext *s, TCGReg dst, TCGReg src, int c) +{ + tcg_out_rld(s, RLDICR, dst, src, c, 63 - c); +} + +static inline void tcg_out_shri32(TCGContext *s, TCGReg dst, TCGReg src, int c) +{ + tcg_out_rlw(s, RLWINM, dst, src, 32 - c, c, 31); +} + +static inline void tcg_out_shri64(TCGContext *s, TCGReg dst, TCGReg src, int c) +{ + tcg_out_rld(s, RLDICL, dst, src, 64 - c, c); +} + +static void tcg_out_movi32(TCGContext *s, TCGReg ret, int32_t arg) +{ + if (arg == (int16_t) arg) { + tcg_out32(s, ADDI | TAI(ret, 0, arg)); + } else { + tcg_out32(s, ADDIS | TAI(ret, 0, arg >> 16)); + if (arg & 0xffff) { + tcg_out32(s, ORI | SAI(ret, ret, arg)); + } + } +} + +static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg ret, + tcg_target_long arg) +{ + tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); + if (type == TCG_TYPE_I32 || arg == (int32_t)arg) { + tcg_out_movi32(s, ret, arg); + } else if (arg == (uint32_t)arg && !(arg & 0x8000)) { + tcg_out32(s, ADDI | TAI(ret, 0, arg)); + tcg_out32(s, ORIS | SAI(ret, ret, arg >> 16)); + } else { + int32_t high; + + if (USE_REG_RA) { + intptr_t diff = arg - (intptr_t)tb_ret_addr; + if (diff == (int32_t)diff) { + tcg_out_mem_long(s, ADDI, ADD, ret, TCG_REG_RA, diff); + return; + } + } + + high = arg >> 31 >> 1; + tcg_out_movi32(s, ret, high); + if (high) { + tcg_out_shli64(s, ret, ret, 32); + } + if (arg & 0xffff0000) { + tcg_out32(s, ORIS | SAI(ret, ret, arg >> 16)); + } + if (arg & 0xffff) { + tcg_out32(s, ORI | SAI(ret, ret, arg)); + } + } +} + +static bool mask_operand(uint32_t c, int *mb, int *me) +{ + uint32_t lsb, test; + + /* Accept a bit pattern like: + 0....01....1 + 1....10....0 + 0..01..10..0 + Keep track of the transitions. */ + if (c == 0 || c == -1) { + return false; + } + test = c; + lsb = test & -test; + test += lsb; + if (test & (test - 1)) { + return false; + } + + *me = clz32(lsb); + *mb = test ? clz32(test & -test) + 1 : 0; + return true; +} + +static bool mask64_operand(uint64_t c, int *mb, int *me) +{ + uint64_t lsb; + + if (c == 0) { + return false; + } + + lsb = c & -c; + /* Accept 1..10..0. */ + if (c == -lsb) { + *mb = 0; + *me = clz64(lsb); + return true; + } + /* Accept 0..01..1. */ + if (lsb == 1 && (c & (c + 1)) == 0) { + *mb = clz64(c + 1) + 1; + *me = 63; + return true; + } + return false; +} + +static void tcg_out_andi32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c) +{ + int mb, me; + + if (mask_operand(c, &mb, &me)) { + tcg_out_rlw(s, RLWINM, dst, src, 0, mb, me); + } else if ((c & 0xffff) == c) { + tcg_out32(s, ANDI | SAI(src, dst, c)); + return; + } else if ((c & 0xffff0000) == c) { + tcg_out32(s, ANDIS | SAI(src, dst, c >> 16)); + return; + } else { + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R0, c); + tcg_out32(s, AND | SAB(src, dst, TCG_REG_R0)); + } +} + +static void tcg_out_andi64(TCGContext *s, TCGReg dst, TCGReg src, uint64_t c) +{ + int mb, me; + + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + if (mask64_operand(c, &mb, &me)) { + if (mb == 0) { + tcg_out_rld(s, RLDICR, dst, src, 0, me); + } else { + tcg_out_rld(s, RLDICL, dst, src, 0, mb); + } + } else if ((c & 0xffff) == c) { + tcg_out32(s, ANDI | SAI(src, dst, c)); + return; + } else if ((c & 0xffff0000) == c) { + tcg_out32(s, ANDIS | SAI(src, dst, c >> 16)); + return; + } else { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R0, c); + tcg_out32(s, AND | SAB(src, dst, TCG_REG_R0)); + } +} + +static void tcg_out_zori32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c, + int op_lo, int op_hi) +{ + if (c >> 16) { + tcg_out32(s, op_hi | SAI(src, dst, c >> 16)); + src = dst; + } + if (c & 0xffff) { + tcg_out32(s, op_lo | SAI(src, dst, c)); + src = dst; + } +} + +static void tcg_out_ori32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c) +{ + tcg_out_zori32(s, dst, src, c, ORI, ORIS); +} + +static void tcg_out_xori32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c) +{ + tcg_out_zori32(s, dst, src, c, XORI, XORIS); +} + +static void tcg_out_b(TCGContext *s, int mask, tcg_insn_unit *target) +{ + ptrdiff_t disp = tcg_pcrel_diff(s, target); + if (in_range_b(disp)) { + tcg_out32(s, B | (disp & 0x3fffffc) | mask); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R0, (uintptr_t)target); + tcg_out32(s, MTSPR | RS(TCG_REG_R0) | CTR); + tcg_out32(s, BCCTR | BO_ALWAYS | mask); + } +} + +static void tcg_out_mem_long(TCGContext *s, int opi, int opx, TCGReg rt, + TCGReg base, tcg_target_long offset) +{ + tcg_target_long orig = offset, l0, l1, extra = 0, align = 0; + bool is_store = false; + TCGReg rs = TCG_REG_TMP1; + + switch (opi) { + case LD: case LWA: + align = 3; + /* FALLTHRU */ + default: + if (rt != TCG_REG_R0) { + rs = rt; + break; + } + break; + case STD: + align = 3; + /* FALLTHRU */ + case STB: case STH: case STW: + is_store = true; + break; + } + + /* For unaligned, or very large offsets, use the indexed form. */ + if (offset & align || offset != (int32_t)offset) { + if (rs == base) { + rs = TCG_REG_R0; + } + tcg_debug_assert(!is_store || rs != rt); + tcg_out_movi(s, TCG_TYPE_PTR, rs, orig); + tcg_out32(s, opx | TAB(rt, base, rs)); + return; + } + + l0 = (int16_t)offset; + offset = (offset - l0) >> 16; + l1 = (int16_t)offset; + + if (l1 < 0 && orig >= 0) { + extra = 0x4000; + l1 = (int16_t)(offset - 0x4000); + } + if (l1) { + tcg_out32(s, ADDIS | TAI(rs, base, l1)); + base = rs; + } + if (extra) { + tcg_out32(s, ADDIS | TAI(rs, base, extra)); + base = rs; + } + if (opi != ADDI || base != rt || l0 != 0) { + tcg_out32(s, opi | TAI(rt, base, l0)); + } +} + +static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, + TCGReg arg1, intptr_t arg2) +{ + int opi, opx; + + tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); + if (type == TCG_TYPE_I32) { + opi = LWZ, opx = LWZX; + } else { + opi = LD, opx = LDX; + } + tcg_out_mem_long(s, opi, opx, ret, arg1, arg2); +} + +static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, + TCGReg arg1, intptr_t arg2) +{ + int opi, opx; + + tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); + if (type == TCG_TYPE_I32) { + opi = STW, opx = STWX; + } else { + opi = STD, opx = STDX; + } + tcg_out_mem_long(s, opi, opx, arg, arg1, arg2); +} + +static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, + int const_arg2, int cr, TCGType type) +{ + int imm; + uint32_t op; + + tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); + + /* Simplify the comparisons below wrt CMPI. */ + if (type == TCG_TYPE_I32) { + arg2 = (int32_t)arg2; + } + + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_NE: + if (const_arg2) { + if ((int16_t) arg2 == arg2) { + op = CMPI; + imm = 1; + break; + } else if ((uint16_t) arg2 == arg2) { + op = CMPLI; + imm = 1; + break; + } + } + op = CMPL; + imm = 0; + break; + + case TCG_COND_LT: + case TCG_COND_GE: + case TCG_COND_LE: + case TCG_COND_GT: + if (const_arg2) { + if ((int16_t) arg2 == arg2) { + op = CMPI; + imm = 1; + break; + } + } + op = CMP; + imm = 0; + break; + + case TCG_COND_LTU: + case TCG_COND_GEU: + case TCG_COND_LEU: + case TCG_COND_GTU: + if (const_arg2) { + if ((uint16_t) arg2 == arg2) { + op = CMPLI; + imm = 1; + break; + } + } + op = CMPL; + imm = 0; + break; + + default: + tcg_abort(); + } + op |= BF(cr) | ((type == TCG_TYPE_I64) << 21); + + if (imm) { + tcg_out32(s, op | RA(arg1) | (arg2 & 0xffff)); + } else { + if (const_arg2) { + tcg_out_movi(s, type, TCG_REG_R0, arg2); + arg2 = TCG_REG_R0; + } + tcg_out32(s, op | RA(arg1) | RB(arg2)); + } +} + +static void tcg_out_setcond_eq0(TCGContext *s, TCGType type, + TCGReg dst, TCGReg src) +{ + if (type == TCG_TYPE_I32) { + tcg_out32(s, CNTLZW | RS(src) | RA(dst)); + tcg_out_shri32(s, dst, dst, 5); + } else { + tcg_out32(s, CNTLZD | RS(src) | RA(dst)); + tcg_out_shri64(s, dst, dst, 6); + } +} + +static void tcg_out_setcond_ne0(TCGContext *s, TCGReg dst, TCGReg src) +{ + /* X != 0 implies X + -1 generates a carry. Extra addition + trickery means: R = X-1 + ~X + C = X-1 + (-X+1) + C = C. */ + if (dst != src) { + tcg_out32(s, ADDIC | TAI(dst, src, -1)); + tcg_out32(s, SUBFE | TAB(dst, dst, src)); + } else { + tcg_out32(s, ADDIC | TAI(TCG_REG_R0, src, -1)); + tcg_out32(s, SUBFE | TAB(dst, TCG_REG_R0, src)); + } +} + +static TCGReg tcg_gen_setcond_xor(TCGContext *s, TCGReg arg1, TCGArg arg2, + bool const_arg2) +{ + if (const_arg2) { + if ((uint32_t)arg2 == arg2) { + tcg_out_xori32(s, TCG_REG_R0, arg1, arg2); + } else { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R0, arg2); + tcg_out32(s, XOR | SAB(arg1, TCG_REG_R0, TCG_REG_R0)); + } + } else { + tcg_out32(s, XOR | SAB(arg1, TCG_REG_R0, arg2)); + } + return TCG_REG_R0; +} + +static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGArg arg0, TCGArg arg1, TCGArg arg2, + int const_arg2) +{ + int crop, sh; + + tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); + + /* Ignore high bits of a potential constant arg2. */ + if (type == TCG_TYPE_I32) { + arg2 = (uint32_t)arg2; + } + + /* Handle common and trivial cases before handling anything else. */ + if (arg2 == 0) { + switch (cond) { + case TCG_COND_EQ: + tcg_out_setcond_eq0(s, type, arg0, arg1); + return; + case TCG_COND_NE: + if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) { + tcg_out_ext32u(s, TCG_REG_R0, arg1); + arg1 = TCG_REG_R0; + } + tcg_out_setcond_ne0(s, arg0, arg1); + return; + case TCG_COND_GE: + tcg_out32(s, NOR | SAB(arg1, arg0, arg1)); + arg1 = arg0; + /* FALLTHRU */ + case TCG_COND_LT: + /* Extract the sign bit. */ + if (type == TCG_TYPE_I32) { + tcg_out_shri32(s, arg0, arg1, 31); + } else { + tcg_out_shri64(s, arg0, arg1, 63); + } + return; + default: + break; + } + } + + /* If we have ISEL, we can implement everything with 3 or 4 insns. + All other cases below are also at least 3 insns, so speed up the + code generator by not considering them and always using ISEL. */ + if (HAVE_ISEL) { + int isel, tab; + + tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); + + isel = tcg_to_isel[cond]; + + tcg_out_movi(s, type, arg0, 1); + if (isel & 1) { + /* arg0 = (bc ? 0 : 1) */ + tab = TAB(arg0, 0, arg0); + isel &= ~1; + } else { + /* arg0 = (bc ? 1 : 0) */ + tcg_out_movi(s, type, TCG_REG_R0, 0); + tab = TAB(arg0, arg0, TCG_REG_R0); + } + tcg_out32(s, isel | tab); + return; + } + + switch (cond) { + case TCG_COND_EQ: + arg1 = tcg_gen_setcond_xor(s, arg1, arg2, const_arg2); + tcg_out_setcond_eq0(s, type, arg0, arg1); + return; + + case TCG_COND_NE: + arg1 = tcg_gen_setcond_xor(s, arg1, arg2, const_arg2); + /* Discard the high bits only once, rather than both inputs. */ + if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) { + tcg_out_ext32u(s, TCG_REG_R0, arg1); + arg1 = TCG_REG_R0; + } + tcg_out_setcond_ne0(s, arg0, arg1); + return; + + case TCG_COND_GT: + case TCG_COND_GTU: + sh = 30; + crop = 0; + goto crtest; + + case TCG_COND_LT: + case TCG_COND_LTU: + sh = 29; + crop = 0; + goto crtest; + + case TCG_COND_GE: + case TCG_COND_GEU: + sh = 31; + crop = CRNOR | BT(7, CR_EQ) | BA(7, CR_LT) | BB(7, CR_LT); + goto crtest; + + case TCG_COND_LE: + case TCG_COND_LEU: + sh = 31; + crop = CRNOR | BT(7, CR_EQ) | BA(7, CR_GT) | BB(7, CR_GT); + crtest: + tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); + if (crop) { + tcg_out32(s, crop); + } + tcg_out32(s, MFOCRF | RT(TCG_REG_R0) | FXM(7)); + tcg_out_rlw(s, RLWINM, arg0, TCG_REG_R0, sh, 31, 31); + break; + + default: + tcg_abort(); + } +} + +static void tcg_out_bc(TCGContext *s, int bc, TCGLabel *l) +{ + if (l->has_value) { + tcg_out32(s, bc | reloc_pc14_val(s->code_ptr, l->u.value_ptr)); + } else { + tcg_out_reloc(s, s->code_ptr, R_PPC_REL14, l, 0); + tcg_out_bc_noaddr(s, bc); + } +} + +static void tcg_out_brcond(TCGContext *s, TCGCond cond, + TCGArg arg1, TCGArg arg2, int const_arg2, + TCGLabel *l, TCGType type) +{ + tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); + tcg_out_bc(s, tcg_to_bc[cond], l); +} + +static void tcg_out_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGArg dest, TCGArg c1, TCGArg c2, TCGArg v1, + TCGArg v2, bool const_c2) +{ + /* If for some reason both inputs are zero, don't produce bad code. */ + if (v1 == 0 && v2 == 0) { + tcg_out_movi(s, type, dest, 0); + return; + } + + tcg_out_cmp(s, cond, c1, c2, const_c2, 7, type); + + if (HAVE_ISEL) { + int isel = tcg_to_isel[cond]; + + /* Swap the V operands if the operation indicates inversion. */ + if (isel & 1) { + int t = v1; + v1 = v2; + v2 = t; + isel &= ~1; + } + /* V1 == 0 is handled by isel; V2 == 0 must be handled by hand. */ + if (v2 == 0) { + tcg_out_movi(s, type, TCG_REG_R0, 0); + } + tcg_out32(s, isel | TAB(dest, v1, v2)); + } else { + if (dest == v2) { + cond = tcg_invert_cond(cond); + v2 = v1; + } else if (dest != v1) { + if (v1 == 0) { + tcg_out_movi(s, type, dest, 0); + } else { + tcg_out_mov(s, type, dest, v1); + } + } + /* Branch forward over one insn */ + tcg_out32(s, tcg_to_bc[cond] | 8); + if (v2 == 0) { + tcg_out_movi(s, type, dest, 0); + } else { + tcg_out_mov(s, type, dest, v2); + } + } +} + +static void tcg_out_cmp2(TCGContext *s, const TCGArg *args, + const int *const_args) +{ + static const struct { uint8_t bit1, bit2; } bits[] = { + [TCG_COND_LT ] = { CR_LT, CR_LT }, + [TCG_COND_LE ] = { CR_LT, CR_GT }, + [TCG_COND_GT ] = { CR_GT, CR_GT }, + [TCG_COND_GE ] = { CR_GT, CR_LT }, + [TCG_COND_LTU] = { CR_LT, CR_LT }, + [TCG_COND_LEU] = { CR_LT, CR_GT }, + [TCG_COND_GTU] = { CR_GT, CR_GT }, + [TCG_COND_GEU] = { CR_GT, CR_LT }, + }; + + TCGCond cond = args[4], cond2; + TCGArg al, ah, bl, bh; + int blconst, bhconst; + int op, bit1, bit2; + + al = args[0]; + ah = args[1]; + bl = args[2]; + bh = args[3]; + blconst = const_args[2]; + bhconst = const_args[3]; + + switch (cond) { + case TCG_COND_EQ: + op = CRAND; + goto do_equality; + case TCG_COND_NE: + op = CRNAND; + do_equality: + tcg_out_cmp(s, cond, al, bl, blconst, 6, TCG_TYPE_I32); + tcg_out_cmp(s, cond, ah, bh, bhconst, 7, TCG_TYPE_I32); + tcg_out32(s, op | BT(7, CR_EQ) | BA(6, CR_EQ) | BB(7, CR_EQ)); + break; + + case TCG_COND_LT: + case TCG_COND_LE: + case TCG_COND_GT: + case TCG_COND_GE: + case TCG_COND_LTU: + case TCG_COND_LEU: + case TCG_COND_GTU: + case TCG_COND_GEU: + bit1 = bits[cond].bit1; + bit2 = bits[cond].bit2; + op = (bit1 != bit2 ? CRANDC : CRAND); + cond2 = tcg_unsigned_cond(cond); + + tcg_out_cmp(s, cond, ah, bh, bhconst, 6, TCG_TYPE_I32); + tcg_out_cmp(s, cond2, al, bl, blconst, 7, TCG_TYPE_I32); + tcg_out32(s, op | BT(7, CR_EQ) | BA(6, CR_EQ) | BB(7, bit2)); + tcg_out32(s, CROR | BT(7, CR_EQ) | BA(6, bit1) | BB(7, CR_EQ)); + break; + + default: + tcg_abort(); + } +} + +static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, + const int *const_args) +{ + tcg_out_cmp2(s, args + 1, const_args + 1); + tcg_out32(s, MFOCRF | RT(TCG_REG_R0) | FXM(7)); + tcg_out_rlw(s, RLWINM, args[0], TCG_REG_R0, 31, 31, 31); +} + +static void tcg_out_brcond2 (TCGContext *s, const TCGArg *args, + const int *const_args) +{ + tcg_out_cmp2(s, args, const_args); + tcg_out_bc(s, BC | BI(7, CR_EQ) | BO_COND_TRUE, arg_label(args[5])); +} + +void ppc_tb_set_jmp_target(uintptr_t jmp_addr, uintptr_t addr) +{ + tcg_insn_unit i1, i2; + uint64_t pair; + intptr_t diff = addr - jmp_addr; + + if (in_range_b(diff)) { + i1 = B | (diff & 0x3fffffc); + i2 = NOP; + } else if (USE_REG_RA) { + intptr_t lo, hi; + diff = addr - (uintptr_t)tb_ret_addr; + lo = (int16_t)diff; + hi = (int32_t)(diff - lo); + tcg_debug_assert(diff == hi + lo); + i1 = ADDIS | TAI(TCG_REG_TMP1, TCG_REG_RA, hi >> 16); + i2 = ADDI | TAI(TCG_REG_TMP1, TCG_REG_TMP1, lo); + } else { + tcg_debug_assert(TCG_TARGET_REG_BITS == 32 || addr == (int32_t)addr); + i1 = ADDIS | TAI(TCG_REG_TMP1, 0, addr >> 16); + i2 = ORI | SAI(TCG_REG_TMP1, TCG_REG_TMP1, addr); + } +#ifdef HOST_WORDS_BIGENDIAN + pair = (uint64_t)i1 << 32 | i2; +#else + pair = (uint64_t)i2 << 32 | i1; +#endif + + /* ??? __atomic_store_8, presuming there's some way to do that + for 32-bit, otherwise this is good enough for 64-bit. */ + *(uint64_t *)jmp_addr = pair; + flush_icache_range(jmp_addr, jmp_addr + 8); +} + +static void tcg_out_call(TCGContext *s, tcg_insn_unit *target) +{ +#ifdef _CALL_AIX + /* Look through the descriptor. If the branch is in range, and we + don't have to spend too much effort on building the toc. */ + void *tgt = ((void **)target)[0]; + uintptr_t toc = ((uintptr_t *)target)[1]; + intptr_t diff = tcg_pcrel_diff(s, tgt); + + if (in_range_b(diff) && toc == (uint32_t)toc) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP1, toc); + tcg_out_b(s, LK, tgt); + } else { + /* Fold the low bits of the constant into the addresses below. */ + intptr_t arg = (intptr_t)target; + int ofs = (int16_t)arg; + + if (ofs + 8 < 0x8000) { + arg -= ofs; + } else { + ofs = 0; + } + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP1, arg); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R0, TCG_REG_TMP1, ofs); + tcg_out32(s, MTSPR | RA(TCG_REG_R0) | CTR); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R2, TCG_REG_TMP1, ofs + SZP); + tcg_out32(s, BCCTR | BO_ALWAYS | LK); + } +#elif defined(_CALL_ELF) && _CALL_ELF == 2 + intptr_t diff; + + /* In the ELFv2 ABI, we have to set up r12 to contain the destination + address, which the callee uses to compute its TOC address. */ + /* FIXME: when the branch is in range, we could avoid r12 load if we + knew that the destination uses the same TOC, and what its local + entry point offset is. */ + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R12, (intptr_t)target); + + diff = tcg_pcrel_diff(s, target); + if (in_range_b(diff)) { + tcg_out_b(s, LK, target); + } else { + tcg_out32(s, MTSPR | RS(TCG_REG_R12) | CTR); + tcg_out32(s, BCCTR | BO_ALWAYS | LK); + } +#else + tcg_out_b(s, LK, target); +#endif +} + +static const uint32_t qemu_ldx_opc[16] = { + [MO_UB] = LBZX, + [MO_UW] = LHZX, + [MO_UL] = LWZX, + [MO_Q] = LDX, + [MO_SW] = LHAX, + [MO_SL] = LWAX, + [MO_BSWAP | MO_UB] = LBZX, + [MO_BSWAP | MO_UW] = LHBRX, + [MO_BSWAP | MO_UL] = LWBRX, + [MO_BSWAP | MO_Q] = LDBRX, +}; + +static const uint32_t qemu_stx_opc[16] = { + [MO_UB] = STBX, + [MO_UW] = STHX, + [MO_UL] = STWX, + [MO_Q] = STDX, + [MO_BSWAP | MO_UB] = STBX, + [MO_BSWAP | MO_UW] = STHBRX, + [MO_BSWAP | MO_UL] = STWBRX, + [MO_BSWAP | MO_Q] = STDBRX, +}; + +static const uint32_t qemu_exts_opc[4] = { + EXTSB, EXTSH, EXTSW, 0 +}; + +#if defined (CONFIG_SOFTMMU) +/* helper signature: helper_ld_mmu(CPUState *env, target_ulong addr, + * int mmu_idx, uintptr_t ra) + */ +static void * const qemu_ld_helpers[16] = { + [MO_UB] = helper_ret_ldub_mmu, + [MO_LEUW] = helper_le_lduw_mmu, + [MO_LEUL] = helper_le_ldul_mmu, + [MO_LEQ] = helper_le_ldq_mmu, + [MO_BEUW] = helper_be_lduw_mmu, + [MO_BEUL] = helper_be_ldul_mmu, + [MO_BEQ] = helper_be_ldq_mmu, +}; + +/* helper signature: helper_st_mmu(CPUState *env, target_ulong addr, + * uintxx_t val, int mmu_idx, uintptr_t ra) + */ +static void * const qemu_st_helpers[16] = { + [MO_UB] = helper_ret_stb_mmu, + [MO_LEUW] = helper_le_stw_mmu, + [MO_LEUL] = helper_le_stl_mmu, + [MO_LEQ] = helper_le_stq_mmu, + [MO_BEUW] = helper_be_stw_mmu, + [MO_BEUL] = helper_be_stl_mmu, + [MO_BEQ] = helper_be_stq_mmu, +}; + +/* Perform the TLB load and compare. Places the result of the comparison + in CR7, loads the addend of the TLB into R3, and returns the register + containing the guest address (zero-extended into R4). Clobbers R0 and R2. */ + +static TCGReg tcg_out_tlb_read(TCGContext *s, TCGMemOp opc, + TCGReg addrlo, TCGReg addrhi, + int mem_index, bool is_read) +{ + int cmp_off + = (is_read + ? offsetof(CPUArchState, tlb_table[mem_index][0].addr_read) + : offsetof(CPUArchState, tlb_table[mem_index][0].addr_write)); + int add_off = offsetof(CPUArchState, tlb_table[mem_index][0].addend); + TCGReg base = TCG_AREG0; + TCGMemOp s_bits = opc & MO_SIZE; + + /* Extract the page index, shifted into place for tlb index. */ + if (TCG_TARGET_REG_BITS == 64) { + if (TARGET_LONG_BITS == 32) { + /* Zero-extend the address into a place helpful for further use. */ + tcg_out_ext32u(s, TCG_REG_R4, addrlo); + addrlo = TCG_REG_R4; + } else { + tcg_out_rld(s, RLDICL, TCG_REG_R3, addrlo, + 64 - TARGET_PAGE_BITS, 64 - CPU_TLB_BITS); + } + } + + /* Compensate for very large offsets. */ + if (add_off >= 0x8000) { + /* Most target env are smaller than 32k; none are larger than 64k. + Simplify the logic here merely to offset by 0x7ff0, giving us a + range just shy of 64k. Check this assumption. */ + QEMU_BUILD_BUG_ON(offsetof(CPUArchState, + tlb_table[NB_MMU_MODES - 1][1]) + > 0x7ff0 + 0x7fff); + tcg_out32(s, ADDI | TAI(TCG_REG_TMP1, base, 0x7ff0)); + base = TCG_REG_TMP1; + cmp_off -= 0x7ff0; + add_off -= 0x7ff0; + } + + /* Extraction and shifting, part 2. */ + if (TCG_TARGET_REG_BITS == 32 || TARGET_LONG_BITS == 32) { + tcg_out_rlw(s, RLWINM, TCG_REG_R3, addrlo, + 32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS), + 32 - (CPU_TLB_BITS + CPU_TLB_ENTRY_BITS), + 31 - CPU_TLB_ENTRY_BITS); + } else { + tcg_out_shli64(s, TCG_REG_R3, TCG_REG_R3, CPU_TLB_ENTRY_BITS); + } + + tcg_out32(s, ADD | TAB(TCG_REG_R3, TCG_REG_R3, base)); + + /* Load the tlb comparator. */ + if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { + tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_R4, TCG_REG_R3, cmp_off); + tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_TMP1, TCG_REG_R3, cmp_off + 4); + } else { + tcg_out_ld(s, TCG_TYPE_TL, TCG_REG_TMP1, TCG_REG_R3, cmp_off); + } + + /* Load the TLB addend for use on the fast path. Do this asap + to minimize any load use delay. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R3, TCG_REG_R3, add_off); + + /* Clear the non-page, non-alignment bits from the address */ + if (TCG_TARGET_REG_BITS == 32 || TARGET_LONG_BITS == 32) { + /* We don't support unaligned accesses on 32-bits, preserve + * the bottom bits and thus trigger a comparison failure on + * unaligned accesses + */ + tcg_out_rlw(s, RLWINM, TCG_REG_R0, addrlo, 0, + (32 - s_bits) & 31, 31 - TARGET_PAGE_BITS); + } else if (s_bits) { + /* > byte access, we need to handle alignment */ + if ((opc & MO_AMASK) == MO_ALIGN) { + /* Alignment required by the front-end, same as 32-bits */ + tcg_out_rld(s, RLDICL, TCG_REG_R0, addrlo, + 64 - TARGET_PAGE_BITS, TARGET_PAGE_BITS - s_bits); + tcg_out_rld(s, RLDICL, TCG_REG_R0, TCG_REG_R0, TARGET_PAGE_BITS, 0); + } else { + /* We support unaligned accesses, we need to make sure we fail + * if we cross a page boundary. The trick is to add the + * access_size-1 to the address before masking the low bits. + * That will make the address overflow to the next page if we + * cross a page boundary which will then force a mismatch of + * the TLB compare since the next page cannot possibly be in + * the same TLB index. + */ + tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, (1 << s_bits) - 1)); + tcg_out_rld(s, RLDICR, TCG_REG_R0, TCG_REG_R0, + 0, 63 - TARGET_PAGE_BITS); + } + } else { + /* Byte access, just chop off the bits below the page index */ + tcg_out_rld(s, RLDICR, TCG_REG_R0, addrlo, 0, 63 - TARGET_PAGE_BITS); + } + + if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { + tcg_out_cmp(s, TCG_COND_EQ, TCG_REG_R0, TCG_REG_TMP1, + 0, 7, TCG_TYPE_I32); + tcg_out_cmp(s, TCG_COND_EQ, addrhi, TCG_REG_R4, 0, 6, TCG_TYPE_I32); + tcg_out32(s, CRAND | BT(7, CR_EQ) | BA(6, CR_EQ) | BB(7, CR_EQ)); + } else { + tcg_out_cmp(s, TCG_COND_EQ, TCG_REG_R0, TCG_REG_TMP1, + 0, 7, TCG_TYPE_TL); + } + + return addrlo; +} + +/* Record the context of a call to the out of line helper code for the slow + path for a load or store, so that we can later generate the correct + helper code. */ +static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOpIdx oi, + TCGReg datalo_reg, TCGReg datahi_reg, + TCGReg addrlo_reg, TCGReg addrhi_reg, + tcg_insn_unit *raddr, tcg_insn_unit *lptr) +{ + TCGLabelQemuLdst *label = new_ldst_label(s); + + label->is_ld = is_ld; + label->oi = oi; + label->datalo_reg = datalo_reg; + label->datahi_reg = datahi_reg; + label->addrlo_reg = addrlo_reg; + label->addrhi_reg = addrhi_reg; + label->raddr = raddr; + label->label_ptr[0] = lptr; +} + +static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) +{ + TCGMemOpIdx oi = lb->oi; + TCGMemOp opc = get_memop(oi); + TCGReg hi, lo, arg = TCG_REG_R3; + + reloc_pc14(lb->label_ptr[0], s->code_ptr); + + tcg_out_mov(s, TCG_TYPE_PTR, arg++, TCG_AREG0); + + lo = lb->addrlo_reg; + hi = lb->addrhi_reg; + if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { +#ifdef TCG_TARGET_CALL_ALIGN_ARGS + arg |= 1; +#endif + tcg_out_mov(s, TCG_TYPE_I32, arg++, hi); + tcg_out_mov(s, TCG_TYPE_I32, arg++, lo); + } else { + /* If the address needed to be zero-extended, we'll have already + placed it in R4. The only remaining case is 64-bit guest. */ + tcg_out_mov(s, TCG_TYPE_TL, arg++, lo); + } + + tcg_out_movi(s, TCG_TYPE_I32, arg++, oi); + tcg_out32(s, MFSPR | RT(arg) | LR); + + tcg_out_call(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SIZE)]); + + lo = lb->datalo_reg; + hi = lb->datahi_reg; + if (TCG_TARGET_REG_BITS == 32 && (opc & MO_SIZE) == MO_64) { + tcg_out_mov(s, TCG_TYPE_I32, lo, TCG_REG_R4); + tcg_out_mov(s, TCG_TYPE_I32, hi, TCG_REG_R3); + } else if (opc & MO_SIGN) { + uint32_t insn = qemu_exts_opc[opc & MO_SIZE]; + tcg_out32(s, insn | RA(lo) | RS(TCG_REG_R3)); + } else { + tcg_out_mov(s, TCG_TYPE_REG, lo, TCG_REG_R3); + } + + tcg_out_b(s, 0, lb->raddr); +} + +static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) +{ + TCGMemOpIdx oi = lb->oi; + TCGMemOp opc = get_memop(oi); + TCGMemOp s_bits = opc & MO_SIZE; + TCGReg hi, lo, arg = TCG_REG_R3; + + reloc_pc14(lb->label_ptr[0], s->code_ptr); + + tcg_out_mov(s, TCG_TYPE_PTR, arg++, TCG_AREG0); + + lo = lb->addrlo_reg; + hi = lb->addrhi_reg; + if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { +#ifdef TCG_TARGET_CALL_ALIGN_ARGS + arg |= 1; +#endif + tcg_out_mov(s, TCG_TYPE_I32, arg++, hi); + tcg_out_mov(s, TCG_TYPE_I32, arg++, lo); + } else { + /* If the address needed to be zero-extended, we'll have already + placed it in R4. The only remaining case is 64-bit guest. */ + tcg_out_mov(s, TCG_TYPE_TL, arg++, lo); + } + + lo = lb->datalo_reg; + hi = lb->datahi_reg; + if (TCG_TARGET_REG_BITS == 32) { + switch (s_bits) { + case MO_64: +#ifdef TCG_TARGET_CALL_ALIGN_ARGS + arg |= 1; +#endif + tcg_out_mov(s, TCG_TYPE_I32, arg++, hi); + /* FALLTHRU */ + case MO_32: + tcg_out_mov(s, TCG_TYPE_I32, arg++, lo); + break; + default: + tcg_out_rlw(s, RLWINM, arg++, lo, 0, 32 - (8 << s_bits), 31); + break; + } + } else { + if (s_bits == MO_64) { + tcg_out_mov(s, TCG_TYPE_I64, arg++, lo); + } else { + tcg_out_rld(s, RLDICL, arg++, lo, 0, 64 - (8 << s_bits)); + } + } + + tcg_out_movi(s, TCG_TYPE_I32, arg++, oi); + tcg_out32(s, MFSPR | RT(arg) | LR); + + tcg_out_call(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)]); + + tcg_out_b(s, 0, lb->raddr); +} +#endif /* SOFTMMU */ + +static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) +{ + TCGReg datalo, datahi, addrlo, rbase; + TCGReg addrhi __attribute__((unused)); + TCGMemOpIdx oi; + TCGMemOp opc, s_bits; +#ifdef CONFIG_SOFTMMU + int mem_index; + tcg_insn_unit *label_ptr; +#endif + + datalo = *args++; + datahi = (TCG_TARGET_REG_BITS == 32 && is_64 ? *args++ : 0); + addrlo = *args++; + addrhi = (TCG_TARGET_REG_BITS < TARGET_LONG_BITS ? *args++ : 0); + oi = *args++; + opc = get_memop(oi); + s_bits = opc & MO_SIZE; + +#ifdef CONFIG_SOFTMMU + mem_index = get_mmuidx(oi); + addrlo = tcg_out_tlb_read(s, opc, addrlo, addrhi, mem_index, true); + + /* Load a pointer into the current opcode w/conditional branch-link. */ + label_ptr = s->code_ptr; + tcg_out_bc_noaddr(s, BC | BI(7, CR_EQ) | BO_COND_FALSE | LK); + + rbase = TCG_REG_R3; +#else /* !CONFIG_SOFTMMU */ + rbase = guest_base ? TCG_GUEST_BASE_REG : 0; + if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { + tcg_out_ext32u(s, TCG_REG_TMP1, addrlo); + addrlo = TCG_REG_TMP1; + } +#endif + + if (TCG_TARGET_REG_BITS == 32 && s_bits == MO_64) { + if (opc & MO_BSWAP) { + tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); + tcg_out32(s, LWBRX | TAB(datalo, rbase, addrlo)); + tcg_out32(s, LWBRX | TAB(datahi, rbase, TCG_REG_R0)); + } else if (rbase != 0) { + tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); + tcg_out32(s, LWZX | TAB(datahi, rbase, addrlo)); + tcg_out32(s, LWZX | TAB(datalo, rbase, TCG_REG_R0)); + } else if (addrlo == datahi) { + tcg_out32(s, LWZ | TAI(datalo, addrlo, 4)); + tcg_out32(s, LWZ | TAI(datahi, addrlo, 0)); + } else { + tcg_out32(s, LWZ | TAI(datahi, addrlo, 0)); + tcg_out32(s, LWZ | TAI(datalo, addrlo, 4)); + } + } else { + uint32_t insn = qemu_ldx_opc[opc & (MO_BSWAP | MO_SSIZE)]; + if (!HAVE_ISA_2_06 && insn == LDBRX) { + tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); + tcg_out32(s, LWBRX | TAB(datalo, rbase, addrlo)); + tcg_out32(s, LWBRX | TAB(TCG_REG_R0, rbase, TCG_REG_R0)); + tcg_out_rld(s, RLDIMI, datalo, TCG_REG_R0, 32, 0); + } else if (insn) { + tcg_out32(s, insn | TAB(datalo, rbase, addrlo)); + } else { + insn = qemu_ldx_opc[opc & (MO_SIZE | MO_BSWAP)]; + tcg_out32(s, insn | TAB(datalo, rbase, addrlo)); + insn = qemu_exts_opc[s_bits]; + tcg_out32(s, insn | RA(datalo) | RS(datalo)); + } + } + +#ifdef CONFIG_SOFTMMU + add_qemu_ldst_label(s, true, oi, datalo, datahi, addrlo, addrhi, + s->code_ptr, label_ptr); +#endif +} + +static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) +{ + TCGReg datalo, datahi, addrlo, rbase; + TCGReg addrhi __attribute__((unused)); + TCGMemOpIdx oi; + TCGMemOp opc, s_bits; +#ifdef CONFIG_SOFTMMU + int mem_index; + tcg_insn_unit *label_ptr; +#endif + + datalo = *args++; + datahi = (TCG_TARGET_REG_BITS == 32 && is_64 ? *args++ : 0); + addrlo = *args++; + addrhi = (TCG_TARGET_REG_BITS < TARGET_LONG_BITS ? *args++ : 0); + oi = *args++; + opc = get_memop(oi); + s_bits = opc & MO_SIZE; + +#ifdef CONFIG_SOFTMMU + mem_index = get_mmuidx(oi); + addrlo = tcg_out_tlb_read(s, opc, addrlo, addrhi, mem_index, false); + + /* Load a pointer into the current opcode w/conditional branch-link. */ + label_ptr = s->code_ptr; + tcg_out_bc_noaddr(s, BC | BI(7, CR_EQ) | BO_COND_FALSE | LK); + + rbase = TCG_REG_R3; +#else /* !CONFIG_SOFTMMU */ + rbase = guest_base ? TCG_GUEST_BASE_REG : 0; + if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { + tcg_out_ext32u(s, TCG_REG_TMP1, addrlo); + addrlo = TCG_REG_TMP1; + } +#endif + + if (TCG_TARGET_REG_BITS == 32 && s_bits == MO_64) { + if (opc & MO_BSWAP) { + tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); + tcg_out32(s, STWBRX | SAB(datalo, rbase, addrlo)); + tcg_out32(s, STWBRX | SAB(datahi, rbase, TCG_REG_R0)); + } else if (rbase != 0) { + tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); + tcg_out32(s, STWX | SAB(datahi, rbase, addrlo)); + tcg_out32(s, STWX | SAB(datalo, rbase, TCG_REG_R0)); + } else { + tcg_out32(s, STW | TAI(datahi, addrlo, 0)); + tcg_out32(s, STW | TAI(datalo, addrlo, 4)); + } + } else { + uint32_t insn = qemu_stx_opc[opc & (MO_BSWAP | MO_SIZE)]; + if (!HAVE_ISA_2_06 && insn == STDBRX) { + tcg_out32(s, STWBRX | SAB(datalo, rbase, addrlo)); + tcg_out32(s, ADDI | TAI(TCG_REG_TMP1, addrlo, 4)); + tcg_out_shri64(s, TCG_REG_R0, datalo, 32); + tcg_out32(s, STWBRX | SAB(TCG_REG_R0, rbase, TCG_REG_TMP1)); + } else { + tcg_out32(s, insn | SAB(datalo, rbase, addrlo)); + } + } + +#ifdef CONFIG_SOFTMMU + add_qemu_ldst_label(s, false, oi, datalo, datahi, addrlo, addrhi, + s->code_ptr, label_ptr); +#endif +} + +/* Parameters for function call generation, used in tcg.c. */ +#define TCG_TARGET_STACK_ALIGN 16 +#define TCG_TARGET_EXTEND_ARGS 1 + +#ifdef _CALL_AIX +# define LINK_AREA_SIZE (6 * SZR) +# define LR_OFFSET (1 * SZR) +# define TCG_TARGET_CALL_STACK_OFFSET (LINK_AREA_SIZE + 8 * SZR) +#elif defined(TCG_TARGET_CALL_DARWIN) +# define LINK_AREA_SIZE (6 * SZR) +# define LR_OFFSET (2 * SZR) +#elif TCG_TARGET_REG_BITS == 64 +# if defined(_CALL_ELF) && _CALL_ELF == 2 +# define LINK_AREA_SIZE (4 * SZR) +# define LR_OFFSET (1 * SZR) +# endif +#else /* TCG_TARGET_REG_BITS == 32 */ +# if defined(_CALL_SYSV) +# define LINK_AREA_SIZE (2 * SZR) +# define LR_OFFSET (1 * SZR) +# endif +#endif +#ifndef LR_OFFSET +# error "Unhandled abi" +#endif +#ifndef TCG_TARGET_CALL_STACK_OFFSET +# define TCG_TARGET_CALL_STACK_OFFSET LINK_AREA_SIZE +#endif + +#define CPU_TEMP_BUF_SIZE (CPU_TEMP_BUF_NLONGS * (int)sizeof(long)) +#define REG_SAVE_SIZE ((int)ARRAY_SIZE(tcg_target_callee_save_regs) * SZR) + +#define FRAME_SIZE ((TCG_TARGET_CALL_STACK_OFFSET \ + + TCG_STATIC_CALL_ARGS_SIZE \ + + CPU_TEMP_BUF_SIZE \ + + REG_SAVE_SIZE \ + + TCG_TARGET_STACK_ALIGN - 1) \ + & -TCG_TARGET_STACK_ALIGN) + +#define REG_SAVE_BOT (FRAME_SIZE - REG_SAVE_SIZE) + +static void tcg_target_qemu_prologue(TCGContext *s) +{ + int i; + +#ifdef _CALL_AIX + void **desc = (void **)s->code_ptr; + desc[0] = desc + 2; /* entry point */ + desc[1] = 0; /* environment pointer */ + s->code_ptr = (void *)(desc + 2); /* skip over descriptor */ +#endif + + tcg_set_frame(s, TCG_REG_CALL_STACK, REG_SAVE_BOT - CPU_TEMP_BUF_SIZE, + CPU_TEMP_BUF_SIZE); + + /* Prologue */ + tcg_out32(s, MFSPR | RT(TCG_REG_R0) | LR); + tcg_out32(s, (SZR == 8 ? STDU : STWU) + | SAI(TCG_REG_R1, TCG_REG_R1, -FRAME_SIZE)); + + for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); ++i) { + tcg_out_st(s, TCG_TYPE_REG, tcg_target_callee_save_regs[i], + TCG_REG_R1, REG_SAVE_BOT + i * SZR); + } + tcg_out_st(s, TCG_TYPE_PTR, TCG_REG_R0, TCG_REG_R1, FRAME_SIZE+LR_OFFSET); + +#ifndef CONFIG_SOFTMMU + if (guest_base) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, guest_base); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + } +#endif + + tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); + tcg_out32(s, MTSPR | RS(tcg_target_call_iarg_regs[1]) | CTR); + + if (USE_REG_RA) { +#ifdef _CALL_AIX + /* Make the caller load the value as the TOC into R2. */ + tb_ret_addr = s->code_ptr + 2; + desc[1] = tb_ret_addr; + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_RA, TCG_REG_R2); + tcg_out32(s, BCCTR | BO_ALWAYS); +#elif defined(_CALL_ELF) && _CALL_ELF == 2 + /* Compute from the incoming R12 value. */ + tb_ret_addr = s->code_ptr + 2; + tcg_out32(s, ADDI | TAI(TCG_REG_RA, TCG_REG_R12, + tcg_ptr_byte_diff(tb_ret_addr, s->code_buf))); + tcg_out32(s, BCCTR | BO_ALWAYS); +#else + /* Reserve max 5 insns for the constant load. */ + tb_ret_addr = s->code_ptr + 6; + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RA, (intptr_t)tb_ret_addr); + tcg_out32(s, BCCTR | BO_ALWAYS); + while (s->code_ptr < tb_ret_addr) { + tcg_out32(s, NOP); + } +#endif + } else { + tcg_out32(s, BCCTR | BO_ALWAYS); + tb_ret_addr = s->code_ptr; + } + + /* Epilogue */ + tcg_debug_assert(tb_ret_addr == s->code_ptr); + + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R0, TCG_REG_R1, FRAME_SIZE+LR_OFFSET); + for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); ++i) { + tcg_out_ld(s, TCG_TYPE_REG, tcg_target_callee_save_regs[i], + TCG_REG_R1, REG_SAVE_BOT + i * SZR); + } + tcg_out32(s, MTSPR | RS(TCG_REG_R0) | LR); + tcg_out32(s, ADDI | TAI(TCG_REG_R1, TCG_REG_R1, FRAME_SIZE)); + tcg_out32(s, BCLR | BO_ALWAYS); +} + +static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, + const int *const_args) +{ + TCGArg a0, a1, a2; + int c; + + switch (opc) { + case INDEX_op_exit_tb: + if (USE_REG_RA) { + ptrdiff_t disp = tcg_pcrel_diff(s, tb_ret_addr); + + /* Use a direct branch if we can, otherwise use the value in RA. + Note that the direct branch is always backward, thus we need + to account for the possibility of 5 insns from the movi. */ + if (!in_range_b(disp - 20)) { + tcg_out32(s, MTSPR | RS(TCG_REG_RA) | CTR); + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R3, args[0]); + tcg_out32(s, BCCTR | BO_ALWAYS); + break; + } + } + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R3, args[0]); + tcg_out_b(s, 0, tb_ret_addr); + break; + case INDEX_op_goto_tb: + tcg_debug_assert(s->tb_jmp_offset); + /* Direct jump. Ensure the next insns are 8-byte aligned. */ + if ((uintptr_t)s->code_ptr & 7) { + tcg_out32(s, NOP); + } + s->tb_jmp_offset[args[0]] = tcg_current_code_size(s); + /* To be replaced by either a branch+nop or a load into TMP1. */ + s->code_ptr += 2; + tcg_out32(s, MTSPR | RS(TCG_REG_TMP1) | CTR); + tcg_out32(s, BCCTR | BO_ALWAYS); + s->tb_next_offset[args[0]] = tcg_current_code_size(s); + break; + case INDEX_op_br: + { + TCGLabel *l = arg_label(args[0]); + + if (l->has_value) { + tcg_out_b(s, 0, l->u.value_ptr); + } else { + tcg_out_reloc(s, s->code_ptr, R_PPC_REL24, l, 0); + tcg_out_b_noaddr(s, B); + } + } + break; + case INDEX_op_ld8u_i32: + case INDEX_op_ld8u_i64: + tcg_out_mem_long(s, LBZ, LBZX, args[0], args[1], args[2]); + break; + case INDEX_op_ld8s_i32: + case INDEX_op_ld8s_i64: + tcg_out_mem_long(s, LBZ, LBZX, args[0], args[1], args[2]); + tcg_out32(s, EXTSB | RS(args[0]) | RA(args[0])); + break; + case INDEX_op_ld16u_i32: + case INDEX_op_ld16u_i64: + tcg_out_mem_long(s, LHZ, LHZX, args[0], args[1], args[2]); + break; + case INDEX_op_ld16s_i32: + case INDEX_op_ld16s_i64: + tcg_out_mem_long(s, LHA, LHAX, args[0], args[1], args[2]); + break; + case INDEX_op_ld_i32: + case INDEX_op_ld32u_i64: + tcg_out_mem_long(s, LWZ, LWZX, args[0], args[1], args[2]); + break; + case INDEX_op_ld32s_i64: + tcg_out_mem_long(s, LWA, LWAX, args[0], args[1], args[2]); + break; + case INDEX_op_ld_i64: + tcg_out_mem_long(s, LD, LDX, args[0], args[1], args[2]); + break; + case INDEX_op_st8_i32: + case INDEX_op_st8_i64: + tcg_out_mem_long(s, STB, STBX, args[0], args[1], args[2]); + break; + case INDEX_op_st16_i32: + case INDEX_op_st16_i64: + tcg_out_mem_long(s, STH, STHX, args[0], args[1], args[2]); + break; + case INDEX_op_st_i32: + case INDEX_op_st32_i64: + tcg_out_mem_long(s, STW, STWX, args[0], args[1], args[2]); + break; + case INDEX_op_st_i64: + tcg_out_mem_long(s, STD, STDX, args[0], args[1], args[2]); + break; + + case INDEX_op_add_i32: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + do_addi_32: + tcg_out_mem_long(s, ADDI, ADD, a0, a1, (int32_t)a2); + } else { + tcg_out32(s, ADD | TAB(a0, a1, a2)); + } + break; + case INDEX_op_sub_i32: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[1]) { + if (const_args[2]) { + tcg_out_movi(s, TCG_TYPE_I32, a0, a1 - a2); + } else { + tcg_out32(s, SUBFIC | TAI(a0, a2, a1)); + } + } else if (const_args[2]) { + a2 = -a2; + goto do_addi_32; + } else { + tcg_out32(s, SUBF | TAB(a0, a2, a1)); + } + break; + + case INDEX_op_and_i32: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + tcg_out_andi32(s, a0, a1, a2); + } else { + tcg_out32(s, AND | SAB(a1, a0, a2)); + } + break; + case INDEX_op_and_i64: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + tcg_out_andi64(s, a0, a1, a2); + } else { + tcg_out32(s, AND | SAB(a1, a0, a2)); + } + break; + case INDEX_op_or_i64: + case INDEX_op_or_i32: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + tcg_out_ori32(s, a0, a1, a2); + } else { + tcg_out32(s, OR | SAB(a1, a0, a2)); + } + break; + case INDEX_op_xor_i64: + case INDEX_op_xor_i32: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + tcg_out_xori32(s, a0, a1, a2); + } else { + tcg_out32(s, XOR | SAB(a1, a0, a2)); + } + break; + case INDEX_op_andc_i32: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + tcg_out_andi32(s, a0, a1, ~a2); + } else { + tcg_out32(s, ANDC | SAB(a1, a0, a2)); + } + break; + case INDEX_op_andc_i64: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + tcg_out_andi64(s, a0, a1, ~a2); + } else { + tcg_out32(s, ANDC | SAB(a1, a0, a2)); + } + break; + case INDEX_op_orc_i32: + if (const_args[2]) { + tcg_out_ori32(s, args[0], args[1], ~args[2]); + break; + } + /* FALLTHRU */ + case INDEX_op_orc_i64: + tcg_out32(s, ORC | SAB(args[1], args[0], args[2])); + break; + case INDEX_op_eqv_i32: + if (const_args[2]) { + tcg_out_xori32(s, args[0], args[1], ~args[2]); + break; + } + /* FALLTHRU */ + case INDEX_op_eqv_i64: + tcg_out32(s, EQV | SAB(args[1], args[0], args[2])); + break; + case INDEX_op_nand_i32: + case INDEX_op_nand_i64: + tcg_out32(s, NAND | SAB(args[1], args[0], args[2])); + break; + case INDEX_op_nor_i32: + case INDEX_op_nor_i64: + tcg_out32(s, NOR | SAB(args[1], args[0], args[2])); + break; + + case INDEX_op_mul_i32: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + tcg_out32(s, MULLI | TAI(a0, a1, a2)); + } else { + tcg_out32(s, MULLW | TAB(a0, a1, a2)); + } + break; + + case INDEX_op_div_i32: + tcg_out32(s, DIVW | TAB(args[0], args[1], args[2])); + break; + + case INDEX_op_divu_i32: + tcg_out32(s, DIVWU | TAB(args[0], args[1], args[2])); + break; + + case INDEX_op_shl_i32: + if (const_args[2]) { + tcg_out_shli32(s, args[0], args[1], args[2]); + } else { + tcg_out32(s, SLW | SAB(args[1], args[0], args[2])); + } + break; + case INDEX_op_shr_i32: + if (const_args[2]) { + tcg_out_shri32(s, args[0], args[1], args[2]); + } else { + tcg_out32(s, SRW | SAB(args[1], args[0], args[2])); + } + break; + case INDEX_op_sar_i32: + if (const_args[2]) { + tcg_out32(s, SRAWI | RS(args[1]) | RA(args[0]) | SH(args[2])); + } else { + tcg_out32(s, SRAW | SAB(args[1], args[0], args[2])); + } + break; + case INDEX_op_rotl_i32: + if (const_args[2]) { + tcg_out_rlw(s, RLWINM, args[0], args[1], args[2], 0, 31); + } else { + tcg_out32(s, RLWNM | SAB(args[1], args[0], args[2]) + | MB(0) | ME(31)); + } + break; + case INDEX_op_rotr_i32: + if (const_args[2]) { + tcg_out_rlw(s, RLWINM, args[0], args[1], 32 - args[2], 0, 31); + } else { + tcg_out32(s, SUBFIC | TAI(TCG_REG_R0, args[2], 32)); + tcg_out32(s, RLWNM | SAB(args[1], args[0], TCG_REG_R0) + | MB(0) | ME(31)); + } + break; + + case INDEX_op_brcond_i32: + tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], + arg_label(args[3]), TCG_TYPE_I32); + break; + case INDEX_op_brcond_i64: + tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], + arg_label(args[3]), TCG_TYPE_I64); + break; + case INDEX_op_brcond2_i32: + tcg_out_brcond2(s, args, const_args); + break; + + case INDEX_op_neg_i32: + case INDEX_op_neg_i64: + tcg_out32(s, NEG | RT(args[0]) | RA(args[1])); + break; + + case INDEX_op_not_i32: + case INDEX_op_not_i64: + tcg_out32(s, NOR | SAB(args[1], args[0], args[1])); + break; + + case INDEX_op_add_i64: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + do_addi_64: + tcg_out_mem_long(s, ADDI, ADD, a0, a1, a2); + } else { + tcg_out32(s, ADD | TAB(a0, a1, a2)); + } + break; + case INDEX_op_sub_i64: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[1]) { + if (const_args[2]) { + tcg_out_movi(s, TCG_TYPE_I64, a0, a1 - a2); + } else { + tcg_out32(s, SUBFIC | TAI(a0, a2, a1)); + } + } else if (const_args[2]) { + a2 = -a2; + goto do_addi_64; + } else { + tcg_out32(s, SUBF | TAB(a0, a2, a1)); + } + break; + + case INDEX_op_shl_i64: + if (const_args[2]) { + tcg_out_shli64(s, args[0], args[1], args[2]); + } else { + tcg_out32(s, SLD | SAB(args[1], args[0], args[2])); + } + break; + case INDEX_op_shr_i64: + if (const_args[2]) { + tcg_out_shri64(s, args[0], args[1], args[2]); + } else { + tcg_out32(s, SRD | SAB(args[1], args[0], args[2])); + } + break; + case INDEX_op_sar_i64: + if (const_args[2]) { + int sh = SH(args[2] & 0x1f) | (((args[2] >> 5) & 1) << 1); + tcg_out32(s, SRADI | RA(args[0]) | RS(args[1]) | sh); + } else { + tcg_out32(s, SRAD | SAB(args[1], args[0], args[2])); + } + break; + case INDEX_op_rotl_i64: + if (const_args[2]) { + tcg_out_rld(s, RLDICL, args[0], args[1], args[2], 0); + } else { + tcg_out32(s, RLDCL | SAB(args[1], args[0], args[2]) | MB64(0)); + } + break; + case INDEX_op_rotr_i64: + if (const_args[2]) { + tcg_out_rld(s, RLDICL, args[0], args[1], 64 - args[2], 0); + } else { + tcg_out32(s, SUBFIC | TAI(TCG_REG_R0, args[2], 64)); + tcg_out32(s, RLDCL | SAB(args[1], args[0], TCG_REG_R0) | MB64(0)); + } + break; + + case INDEX_op_mul_i64: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + tcg_out32(s, MULLI | TAI(a0, a1, a2)); + } else { + tcg_out32(s, MULLD | TAB(a0, a1, a2)); + } + break; + case INDEX_op_div_i64: + tcg_out32(s, DIVD | TAB(args[0], args[1], args[2])); + break; + case INDEX_op_divu_i64: + tcg_out32(s, DIVDU | TAB(args[0], args[1], args[2])); + break; + + case INDEX_op_qemu_ld_i32: + tcg_out_qemu_ld(s, args, false); + break; + case INDEX_op_qemu_ld_i64: + tcg_out_qemu_ld(s, args, true); + break; + case INDEX_op_qemu_st_i32: + tcg_out_qemu_st(s, args, false); + break; + case INDEX_op_qemu_st_i64: + tcg_out_qemu_st(s, args, true); + break; + + case INDEX_op_ext8s_i32: + case INDEX_op_ext8s_i64: + c = EXTSB; + goto gen_ext; + case INDEX_op_ext16s_i32: + case INDEX_op_ext16s_i64: + c = EXTSH; + goto gen_ext; + case INDEX_op_ext_i32_i64: + case INDEX_op_ext32s_i64: + c = EXTSW; + goto gen_ext; + gen_ext: + tcg_out32(s, c | RS(args[1]) | RA(args[0])); + break; + case INDEX_op_extu_i32_i64: + tcg_out_ext32u(s, args[0], args[1]); + break; + + case INDEX_op_setcond_i32: + tcg_out_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], args[2], + const_args[2]); + break; + case INDEX_op_setcond_i64: + tcg_out_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1], args[2], + const_args[2]); + break; + case INDEX_op_setcond2_i32: + tcg_out_setcond2(s, args, const_args); + break; + + case INDEX_op_bswap16_i32: + case INDEX_op_bswap16_i64: + a0 = args[0], a1 = args[1]; + /* a1 = abcd */ + if (a0 != a1) { + /* a0 = (a1 r<< 24) & 0xff # 000c */ + tcg_out_rlw(s, RLWINM, a0, a1, 24, 24, 31); + /* a0 = (a0 & ~0xff00) | (a1 r<< 8) & 0xff00 # 00dc */ + tcg_out_rlw(s, RLWIMI, a0, a1, 8, 16, 23); + } else { + /* r0 = (a1 r<< 8) & 0xff00 # 00d0 */ + tcg_out_rlw(s, RLWINM, TCG_REG_R0, a1, 8, 16, 23); + /* a0 = (a1 r<< 24) & 0xff # 000c */ + tcg_out_rlw(s, RLWINM, a0, a1, 24, 24, 31); + /* a0 = a0 | r0 # 00dc */ + tcg_out32(s, OR | SAB(TCG_REG_R0, a0, a0)); + } + break; + + case INDEX_op_bswap32_i32: + case INDEX_op_bswap32_i64: + /* Stolen from gcc's builtin_bswap32 */ + a1 = args[1]; + a0 = args[0] == a1 ? TCG_REG_R0 : args[0]; + + /* a1 = args[1] # abcd */ + /* a0 = rotate_left (a1, 8) # bcda */ + tcg_out_rlw(s, RLWINM, a0, a1, 8, 0, 31); + /* a0 = (a0 & ~0xff000000) | ((a1 r<< 24) & 0xff000000) # dcda */ + tcg_out_rlw(s, RLWIMI, a0, a1, 24, 0, 7); + /* a0 = (a0 & ~0x0000ff00) | ((a1 r<< 24) & 0x0000ff00) # dcba */ + tcg_out_rlw(s, RLWIMI, a0, a1, 24, 16, 23); + + if (a0 == TCG_REG_R0) { + tcg_out_mov(s, TCG_TYPE_REG, args[0], a0); + } + break; + + case INDEX_op_bswap64_i64: + a0 = args[0], a1 = args[1], a2 = TCG_REG_R0; + if (a0 == a1) { + a0 = TCG_REG_R0; + a2 = a1; + } + + /* a1 = # abcd efgh */ + /* a0 = rl32(a1, 8) # 0000 fghe */ + tcg_out_rlw(s, RLWINM, a0, a1, 8, 0, 31); + /* a0 = dep(a0, rl32(a1, 24), 0xff000000) # 0000 hghe */ + tcg_out_rlw(s, RLWIMI, a0, a1, 24, 0, 7); + /* a0 = dep(a0, rl32(a1, 24), 0x0000ff00) # 0000 hgfe */ + tcg_out_rlw(s, RLWIMI, a0, a1, 24, 16, 23); + + /* a0 = rl64(a0, 32) # hgfe 0000 */ + /* a2 = rl64(a1, 32) # efgh abcd */ + tcg_out_rld(s, RLDICL, a0, a0, 32, 0); + tcg_out_rld(s, RLDICL, a2, a1, 32, 0); + + /* a0 = dep(a0, rl32(a2, 8), 0xffffffff) # hgfe bcda */ + tcg_out_rlw(s, RLWIMI, a0, a2, 8, 0, 31); + /* a0 = dep(a0, rl32(a2, 24), 0xff000000) # hgfe dcda */ + tcg_out_rlw(s, RLWIMI, a0, a2, 24, 0, 7); + /* a0 = dep(a0, rl32(a2, 24), 0x0000ff00) # hgfe dcba */ + tcg_out_rlw(s, RLWIMI, a0, a2, 24, 16, 23); + + if (a0 == 0) { + tcg_out_mov(s, TCG_TYPE_REG, args[0], a0); + } + break; + + case INDEX_op_deposit_i32: + if (const_args[2]) { + uint32_t mask = ((2u << (args[4] - 1)) - 1) << args[3]; + tcg_out_andi32(s, args[0], args[0], ~mask); + } else { + tcg_out_rlw(s, RLWIMI, args[0], args[2], args[3], + 32 - args[3] - args[4], 31 - args[3]); + } + break; + case INDEX_op_deposit_i64: + if (const_args[2]) { + uint64_t mask = ((2ull << (args[4] - 1)) - 1) << args[3]; + tcg_out_andi64(s, args[0], args[0], ~mask); + } else { + tcg_out_rld(s, RLDIMI, args[0], args[2], args[3], + 64 - args[3] - args[4]); + } + break; + + case INDEX_op_movcond_i32: + tcg_out_movcond(s, TCG_TYPE_I32, args[5], args[0], args[1], args[2], + args[3], args[4], const_args[2]); + break; + case INDEX_op_movcond_i64: + tcg_out_movcond(s, TCG_TYPE_I64, args[5], args[0], args[1], args[2], + args[3], args[4], const_args[2]); + break; + +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_add2_i64: +#else + case INDEX_op_add2_i32: +#endif + /* Note that the CA bit is defined based on the word size of the + environment. So in 64-bit mode it's always carry-out of bit 63. + The fallback code using deposit works just as well for 32-bit. */ + a0 = args[0], a1 = args[1]; + if (a0 == args[3] || (!const_args[5] && a0 == args[5])) { + a0 = TCG_REG_R0; + } + if (const_args[4]) { + tcg_out32(s, ADDIC | TAI(a0, args[2], args[4])); + } else { + tcg_out32(s, ADDC | TAB(a0, args[2], args[4])); + } + if (const_args[5]) { + tcg_out32(s, (args[5] ? ADDME : ADDZE) | RT(a1) | RA(args[3])); + } else { + tcg_out32(s, ADDE | TAB(a1, args[3], args[5])); + } + if (a0 != args[0]) { + tcg_out_mov(s, TCG_TYPE_REG, args[0], a0); + } + break; + +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_sub2_i64: +#else + case INDEX_op_sub2_i32: +#endif + a0 = args[0], a1 = args[1]; + if (a0 == args[5] || (!const_args[3] && a0 == args[3])) { + a0 = TCG_REG_R0; + } + if (const_args[2]) { + tcg_out32(s, SUBFIC | TAI(a0, args[4], args[2])); + } else { + tcg_out32(s, SUBFC | TAB(a0, args[4], args[2])); + } + if (const_args[3]) { + tcg_out32(s, (args[3] ? SUBFME : SUBFZE) | RT(a1) | RA(args[5])); + } else { + tcg_out32(s, SUBFE | TAB(a1, args[5], args[3])); + } + if (a0 != args[0]) { + tcg_out_mov(s, TCG_TYPE_REG, args[0], a0); + } + break; + + case INDEX_op_muluh_i32: + tcg_out32(s, MULHWU | TAB(args[0], args[1], args[2])); + break; + case INDEX_op_mulsh_i32: + tcg_out32(s, MULHW | TAB(args[0], args[1], args[2])); + break; + case INDEX_op_muluh_i64: + tcg_out32(s, MULHDU | TAB(args[0], args[1], args[2])); + break; + case INDEX_op_mulsh_i64: + tcg_out32(s, MULHD | TAB(args[0], args[1], args[2])); + break; + + case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ + case INDEX_op_mov_i64: + case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ + case INDEX_op_movi_i64: + case INDEX_op_call: /* Always emitted via tcg_out_call. */ + default: + tcg_abort(); + } +} + +static const TCGTargetOpDef ppc_op_defs[] = { + { INDEX_op_exit_tb, { } }, + { INDEX_op_goto_tb, { } }, + { INDEX_op_br, { } }, + + { INDEX_op_ld8u_i32, { "r", "r" } }, + { INDEX_op_ld8s_i32, { "r", "r" } }, + { INDEX_op_ld16u_i32, { "r", "r" } }, + { INDEX_op_ld16s_i32, { "r", "r" } }, + { INDEX_op_ld_i32, { "r", "r" } }, + + { INDEX_op_st8_i32, { "r", "r" } }, + { INDEX_op_st16_i32, { "r", "r" } }, + { INDEX_op_st_i32, { "r", "r" } }, + + { INDEX_op_add_i32, { "r", "r", "ri" } }, + { INDEX_op_mul_i32, { "r", "r", "rI" } }, + { INDEX_op_div_i32, { "r", "r", "r" } }, + { INDEX_op_divu_i32, { "r", "r", "r" } }, + { INDEX_op_sub_i32, { "r", "rI", "ri" } }, + { INDEX_op_and_i32, { "r", "r", "ri" } }, + { INDEX_op_or_i32, { "r", "r", "ri" } }, + { INDEX_op_xor_i32, { "r", "r", "ri" } }, + { INDEX_op_andc_i32, { "r", "r", "ri" } }, + { INDEX_op_orc_i32, { "r", "r", "ri" } }, + { INDEX_op_eqv_i32, { "r", "r", "ri" } }, + { INDEX_op_nand_i32, { "r", "r", "r" } }, + { INDEX_op_nor_i32, { "r", "r", "r" } }, + + { INDEX_op_shl_i32, { "r", "r", "ri" } }, + { INDEX_op_shr_i32, { "r", "r", "ri" } }, + { INDEX_op_sar_i32, { "r", "r", "ri" } }, + { INDEX_op_rotl_i32, { "r", "r", "ri" } }, + { INDEX_op_rotr_i32, { "r", "r", "ri" } }, + + { INDEX_op_neg_i32, { "r", "r" } }, + { INDEX_op_not_i32, { "r", "r" } }, + { INDEX_op_ext8s_i32, { "r", "r" } }, + { INDEX_op_ext16s_i32, { "r", "r" } }, + { INDEX_op_bswap16_i32, { "r", "r" } }, + { INDEX_op_bswap32_i32, { "r", "r" } }, + + { INDEX_op_brcond_i32, { "r", "ri" } }, + { INDEX_op_setcond_i32, { "r", "r", "ri" } }, + { INDEX_op_movcond_i32, { "r", "r", "ri", "rZ", "rZ" } }, + + { INDEX_op_deposit_i32, { "r", "0", "rZ" } }, + + { INDEX_op_muluh_i32, { "r", "r", "r" } }, + { INDEX_op_mulsh_i32, { "r", "r", "r" } }, + +#if TCG_TARGET_REG_BITS == 64 + { INDEX_op_ld8u_i64, { "r", "r" } }, + { INDEX_op_ld8s_i64, { "r", "r" } }, + { INDEX_op_ld16u_i64, { "r", "r" } }, + { INDEX_op_ld16s_i64, { "r", "r" } }, + { INDEX_op_ld32u_i64, { "r", "r" } }, + { INDEX_op_ld32s_i64, { "r", "r" } }, + { INDEX_op_ld_i64, { "r", "r" } }, + + { INDEX_op_st8_i64, { "r", "r" } }, + { INDEX_op_st16_i64, { "r", "r" } }, + { INDEX_op_st32_i64, { "r", "r" } }, + { INDEX_op_st_i64, { "r", "r" } }, + + { INDEX_op_add_i64, { "r", "r", "rT" } }, + { INDEX_op_sub_i64, { "r", "rI", "rT" } }, + { INDEX_op_and_i64, { "r", "r", "ri" } }, + { INDEX_op_or_i64, { "r", "r", "rU" } }, + { INDEX_op_xor_i64, { "r", "r", "rU" } }, + { INDEX_op_andc_i64, { "r", "r", "ri" } }, + { INDEX_op_orc_i64, { "r", "r", "r" } }, + { INDEX_op_eqv_i64, { "r", "r", "r" } }, + { INDEX_op_nand_i64, { "r", "r", "r" } }, + { INDEX_op_nor_i64, { "r", "r", "r" } }, + + { INDEX_op_shl_i64, { "r", "r", "ri" } }, + { INDEX_op_shr_i64, { "r", "r", "ri" } }, + { INDEX_op_sar_i64, { "r", "r", "ri" } }, + { INDEX_op_rotl_i64, { "r", "r", "ri" } }, + { INDEX_op_rotr_i64, { "r", "r", "ri" } }, + + { INDEX_op_mul_i64, { "r", "r", "rI" } }, + { INDEX_op_div_i64, { "r", "r", "r" } }, + { INDEX_op_divu_i64, { "r", "r", "r" } }, + + { INDEX_op_neg_i64, { "r", "r" } }, + { INDEX_op_not_i64, { "r", "r" } }, + { INDEX_op_ext8s_i64, { "r", "r" } }, + { INDEX_op_ext16s_i64, { "r", "r" } }, + { INDEX_op_ext32s_i64, { "r", "r" } }, + { INDEX_op_ext_i32_i64, { "r", "r" } }, + { INDEX_op_extu_i32_i64, { "r", "r" } }, + { INDEX_op_bswap16_i64, { "r", "r" } }, + { INDEX_op_bswap32_i64, { "r", "r" } }, + { INDEX_op_bswap64_i64, { "r", "r" } }, + + { INDEX_op_brcond_i64, { "r", "ri" } }, + { INDEX_op_setcond_i64, { "r", "r", "ri" } }, + { INDEX_op_movcond_i64, { "r", "r", "ri", "rZ", "rZ" } }, + + { INDEX_op_deposit_i64, { "r", "0", "rZ" } }, + + { INDEX_op_mulsh_i64, { "r", "r", "r" } }, + { INDEX_op_muluh_i64, { "r", "r", "r" } }, +#endif + +#if TCG_TARGET_REG_BITS == 32 + { INDEX_op_brcond2_i32, { "r", "r", "ri", "ri" } }, + { INDEX_op_setcond2_i32, { "r", "r", "r", "ri", "ri" } }, +#endif + +#if TCG_TARGET_REG_BITS == 64 + { INDEX_op_add2_i64, { "r", "r", "r", "r", "rI", "rZM" } }, + { INDEX_op_sub2_i64, { "r", "r", "rI", "rZM", "r", "r" } }, +#else + { INDEX_op_add2_i32, { "r", "r", "r", "r", "rI", "rZM" } }, + { INDEX_op_sub2_i32, { "r", "r", "rI", "rZM", "r", "r" } }, +#endif + +#if TCG_TARGET_REG_BITS == 64 + { INDEX_op_qemu_ld_i32, { "r", "L" } }, + { INDEX_op_qemu_st_i32, { "S", "S" } }, + { INDEX_op_qemu_ld_i64, { "r", "L" } }, + { INDEX_op_qemu_st_i64, { "S", "S" } }, +#elif TARGET_LONG_BITS == 32 + { INDEX_op_qemu_ld_i32, { "r", "L" } }, + { INDEX_op_qemu_st_i32, { "S", "S" } }, + { INDEX_op_qemu_ld_i64, { "L", "L", "L" } }, + { INDEX_op_qemu_st_i64, { "S", "S", "S" } }, +#else + { INDEX_op_qemu_ld_i32, { "r", "L", "L" } }, + { INDEX_op_qemu_st_i32, { "S", "S", "S" } }, + { INDEX_op_qemu_ld_i64, { "L", "L", "L", "L" } }, + { INDEX_op_qemu_st_i64, { "S", "S", "S", "S" } }, +#endif + + { -1 }, +}; + +static void tcg_target_init(TCGContext *s) +{ + unsigned long hwcap = qemu_getauxval(AT_HWCAP); + if (hwcap & PPC_FEATURE_ARCH_2_06) { + have_isa_2_06 = true; + } + + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffffffff); + tcg_regset_set32(tcg_target_call_clobber_regs, 0, + (1 << TCG_REG_R0) | + (1 << TCG_REG_R2) | + (1 << TCG_REG_R3) | + (1 << TCG_REG_R4) | + (1 << TCG_REG_R5) | + (1 << TCG_REG_R6) | + (1 << TCG_REG_R7) | + (1 << TCG_REG_R8) | + (1 << TCG_REG_R9) | + (1 << TCG_REG_R10) | + (1 << TCG_REG_R11) | + (1 << TCG_REG_R12)); + + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); /* tcg temp */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R1); /* stack pointer */ +#if defined(_CALL_SYSV) + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R2); /* toc pointer */ +#endif +#if defined(_CALL_SYSV) || TCG_TARGET_REG_BITS == 64 + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R13); /* thread pointer */ +#endif + tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP1); /* mem temp */ + if (USE_REG_RA) { + tcg_regset_set_reg(s->reserved_regs, TCG_REG_RA); /* return addr */ + } + + tcg_add_target_add_op_defs(ppc_op_defs); +} + +#ifdef __ELF__ +typedef struct { + DebugFrameCIE cie; + DebugFrameFDEHeader fde; + uint8_t fde_def_cfa[4]; + uint8_t fde_reg_ofs[ARRAY_SIZE(tcg_target_callee_save_regs) * 2 + 3]; +} DebugFrame; + +/* We're expecting a 2 byte uleb128 encoded value. */ +QEMU_BUILD_BUG_ON(FRAME_SIZE >= (1 << 14)); + +#if TCG_TARGET_REG_BITS == 64 +# define ELF_HOST_MACHINE EM_PPC64 +#else +# define ELF_HOST_MACHINE EM_PPC +#endif + +static DebugFrame debug_frame = { + .cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ + .cie.id = -1, + .cie.version = 1, + .cie.code_align = 1, + .cie.data_align = (-SZR & 0x7f), /* sleb128 -SZR */ + .cie.return_column = 65, + + /* Total FDE size does not include the "len" member. */ + .fde.len = sizeof(DebugFrame) - offsetof(DebugFrame, fde.cie_offset), + + .fde_def_cfa = { + 12, TCG_REG_R1, /* DW_CFA_def_cfa r1, ... */ + (FRAME_SIZE & 0x7f) | 0x80, /* ... uleb128 FRAME_SIZE */ + (FRAME_SIZE >> 7) + }, + .fde_reg_ofs = { + /* DW_CFA_offset_extended_sf, lr, LR_OFFSET */ + 0x11, 65, (LR_OFFSET / -SZR) & 0x7f, + } +}; + +void tcg_register_jit(void *buf, size_t buf_size) +{ + uint8_t *p = &debug_frame.fde_reg_ofs[3]; + int i; + + for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); ++i, p += 2) { + p[0] = 0x80 + tcg_target_callee_save_regs[i]; + p[1] = (FRAME_SIZE - (REG_SAVE_BOT + i * SZR)) / SZR; + } + + debug_frame.fde.func_start = (uintptr_t)buf; + debug_frame.fde.func_len = buf_size; + + tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); +} +#endif /* __ELF__ */ + +static size_t dcache_bsize = 16; +static size_t icache_bsize = 16; + +void flush_icache_range(uintptr_t start, uintptr_t stop) +{ + uintptr_t p, start1, stop1; + size_t dsize = dcache_bsize; + size_t isize = icache_bsize; + + start1 = start & ~(dsize - 1); + stop1 = (stop + dsize - 1) & ~(dsize - 1); + for (p = start1; p < stop1; p += dsize) { + asm volatile ("dcbst 0,%0" : : "r"(p) : "memory"); + } + asm volatile ("sync" : : : "memory"); + + start &= start & ~(isize - 1); + stop1 = (stop + isize - 1) & ~(isize - 1); + for (p = start1; p < stop1; p += isize) { + asm volatile ("icbi 0,%0" : : "r"(p) : "memory"); + } + asm volatile ("sync" : : : "memory"); + asm volatile ("isync" : : : "memory"); +} + +#if defined _AIX +#include + +static void __attribute__((constructor)) tcg_cache_init(void) +{ + icache_bsize = _system_configuration.icache_line; + dcache_bsize = _system_configuration.dcache_line; +} + +#elif defined __linux__ +static void __attribute__((constructor)) tcg_cache_init(void) +{ + unsigned long dsize = qemu_getauxval(AT_DCACHEBSIZE); + unsigned long isize = qemu_getauxval(AT_ICACHEBSIZE); + + if (dsize == 0 || isize == 0) { + if (dsize == 0) { + fprintf(stderr, "getauxval AT_DCACHEBSIZE failed\n"); + } + if (isize == 0) { + fprintf(stderr, "getauxval AT_ICACHEBSIZE failed\n"); + } + exit(1); + } + dcache_bsize = dsize; + icache_bsize = isize; +} + +#elif defined __APPLE__ +#include + +static void __attribute__((constructor)) tcg_cache_init(void) +{ + size_t len; + unsigned cacheline; + int name[2] = { CTL_HW, HW_CACHELINE }; + + len = sizeof(cacheline); + if (sysctl(name, 2, &cacheline, &len, NULL, 0)) { + perror("sysctl CTL_HW HW_CACHELINE failed"); + exit(1); + } + dcache_bsize = cacheline; + icache_bsize = cacheline; +} + +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#include + +static void __attribute__((constructor)) tcg_cache_init(void) +{ + size_t len = 4; + unsigned cacheline; + + if (sysctlbyname ("machdep.cacheline_size", &cacheline, &len, NULL, 0)) { + fprintf(stderr, "sysctlbyname machdep.cacheline_size failed: %s\n", + strerror(errno)); + exit(1); + } + dcache_bsize = cacheline; + icache_bsize = cacheline; +} +#endif diff --git a/qemu/tcg/s390/tcg-target.c b/qemu/tcg/s390/tcg-target.c deleted file mode 100644 index aa718eca0..000000000 --- a/qemu/tcg/s390/tcg-target.c +++ /dev/null @@ -1,2393 +0,0 @@ -/* - * Tiny Code Generator for QEMU - * - * Copyright (c) 2009 Ulrich Hecht - * Copyright (c) 2009 Alexander Graf - * Copyright (c) 2010 Richard Henderson - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "tcg-be-ldst.h" - -/* We only support generating code for 64-bit mode. */ -#if TCG_TARGET_REG_BITS != 64 -#error "unsupported code generation mode" -#endif - -#include "elf.h" - -/* ??? The translation blocks produced by TCG are generally small enough to - be entirely reachable with a 16-bit displacement. Leaving the option for - a 32-bit displacement here Just In Case. */ -#define USE_LONG_BRANCHES 0 - -#define TCG_CT_CONST_MULI 0x100 -#define TCG_CT_CONST_ORI 0x200 -#define TCG_CT_CONST_XORI 0x400 -#define TCG_CT_CONST_CMPI 0x800 -#define TCG_CT_CONST_ADLI 0x1000 - -/* Several places within the instruction set 0 means "no register" - rather than TCG_REG_R0. */ -#define TCG_REG_NONE 0 - -/* A scratch register that may be be used throughout the backend. */ -#define TCG_TMP0 TCG_REG_R14 - -#ifdef CONFIG_USE_GUEST_BASE -#define TCG_GUEST_BASE_REG TCG_REG_R13 -#else -#define TCG_GUEST_BASE_REG TCG_REG_R0 -#endif - -#ifndef GUEST_BASE -#define GUEST_BASE 0 -#endif - - -/* All of the following instructions are prefixed with their instruction - format, and are defined as 8- or 16-bit quantities, even when the two - halves of the 16-bit quantity may appear 32 bits apart in the insn. - This makes it easy to copy the values from the tables in Appendix B. */ -typedef enum S390Opcode { - RIL_AFI = 0xc209, - RIL_AGFI = 0xc208, - RIL_ALFI = 0xc20b, - RIL_ALGFI = 0xc20a, - RIL_BRASL = 0xc005, - RIL_BRCL = 0xc004, - RIL_CFI = 0xc20d, - RIL_CGFI = 0xc20c, - RIL_CLFI = 0xc20f, - RIL_CLGFI = 0xc20e, - RIL_IIHF = 0xc008, - RIL_IILF = 0xc009, - RIL_LARL = 0xc000, - RIL_LGFI = 0xc001, - RIL_LGRL = 0xc408, - RIL_LLIHF = 0xc00e, - RIL_LLILF = 0xc00f, - RIL_LRL = 0xc40d, - RIL_MSFI = 0xc201, - RIL_MSGFI = 0xc200, - RIL_NIHF = 0xc00a, - RIL_NILF = 0xc00b, - RIL_OIHF = 0xc00c, - RIL_OILF = 0xc00d, - RIL_SLFI = 0xc205, - RIL_SLGFI = 0xc204, - RIL_XIHF = 0xc006, - RIL_XILF = 0xc007, - - RI_AGHI = 0xa70b, - RI_AHI = 0xa70a, - RI_BRC = 0xa704, - RI_IIHH = 0xa500, - RI_IIHL = 0xa501, - RI_IILH = 0xa502, - RI_IILL = 0xa503, - RI_LGHI = 0xa709, - RI_LLIHH = 0xa50c, - RI_LLIHL = 0xa50d, - RI_LLILH = 0xa50e, - RI_LLILL = 0xa50f, - RI_MGHI = 0xa70d, - RI_MHI = 0xa70c, - RI_NIHH = 0xa504, - RI_NIHL = 0xa505, - RI_NILH = 0xa506, - RI_NILL = 0xa507, - RI_OIHH = 0xa508, - RI_OIHL = 0xa509, - RI_OILH = 0xa50a, - RI_OILL = 0xa50b, - - RIE_CGIJ = 0xec7c, - RIE_CGRJ = 0xec64, - RIE_CIJ = 0xec7e, - RIE_CLGRJ = 0xec65, - RIE_CLIJ = 0xec7f, - RIE_CLGIJ = 0xec7d, - RIE_CLRJ = 0xec77, - RIE_CRJ = 0xec76, - RIE_RISBG = 0xec55, - - RRE_AGR = 0xb908, - RRE_ALGR = 0xb90a, - RRE_ALCR = 0xb998, - RRE_ALCGR = 0xb988, - RRE_CGR = 0xb920, - RRE_CLGR = 0xb921, - RRE_DLGR = 0xb987, - RRE_DLR = 0xb997, - RRE_DSGFR = 0xb91d, - RRE_DSGR = 0xb90d, - RRE_LGBR = 0xb906, - RRE_LCGR = 0xb903, - RRE_LGFR = 0xb914, - RRE_LGHR = 0xb907, - RRE_LGR = 0xb904, - RRE_LLGCR = 0xb984, - RRE_LLGFR = 0xb916, - RRE_LLGHR = 0xb985, - RRE_LRVR = 0xb91f, - RRE_LRVGR = 0xb90f, - RRE_LTGR = 0xb902, - RRE_MLGR = 0xb986, - RRE_MSGR = 0xb90c, - RRE_MSR = 0xb252, - RRE_NGR = 0xb980, - RRE_OGR = 0xb981, - RRE_SGR = 0xb909, - RRE_SLGR = 0xb90b, - RRE_SLBR = 0xb999, - RRE_SLBGR = 0xb989, - RRE_XGR = 0xb982, - - RRF_LOCR = 0xb9f2, - RRF_LOCGR = 0xb9e2, - - RR_AR = 0x1a, - RR_ALR = 0x1e, - RR_BASR = 0x0d, - RR_BCR = 0x07, - RR_CLR = 0x15, - RR_CR = 0x19, - RR_DR = 0x1d, - RR_LCR = 0x13, - RR_LR = 0x18, - RR_LTR = 0x12, - RR_NR = 0x14, - RR_OR = 0x16, - RR_SR = 0x1b, - RR_SLR = 0x1f, - RR_XR = 0x17, - - RSY_RLL = 0xeb1d, - RSY_RLLG = 0xeb1c, - RSY_SLLG = 0xeb0d, - RSY_SRAG = 0xeb0a, - RSY_SRLG = 0xeb0c, - - RS_SLL = 0x89, - RS_SRA = 0x8a, - RS_SRL = 0x88, - - RXY_AG = 0xe308, - RXY_AY = 0xe35a, - RXY_CG = 0xe320, - RXY_CY = 0xe359, - RXY_LAY = 0xe371, - RXY_LB = 0xe376, - RXY_LG = 0xe304, - RXY_LGB = 0xe377, - RXY_LGF = 0xe314, - RXY_LGH = 0xe315, - RXY_LHY = 0xe378, - RXY_LLGC = 0xe390, - RXY_LLGF = 0xe316, - RXY_LLGH = 0xe391, - RXY_LMG = 0xeb04, - RXY_LRV = 0xe31e, - RXY_LRVG = 0xe30f, - RXY_LRVH = 0xe31f, - RXY_LY = 0xe358, - RXY_STCY = 0xe372, - RXY_STG = 0xe324, - RXY_STHY = 0xe370, - RXY_STMG = 0xeb24, - RXY_STRV = 0xe33e, - RXY_STRVG = 0xe32f, - RXY_STRVH = 0xe33f, - RXY_STY = 0xe350, - - RX_A = 0x5a, - RX_C = 0x59, - RX_L = 0x58, - RX_LA = 0x41, - RX_LH = 0x48, - RX_ST = 0x50, - RX_STC = 0x42, - RX_STH = 0x40, -} S390Opcode; - -#ifndef NDEBUG -static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { - "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", - "%r8", "%r9", "%r10" "%r11" "%r12" "%r13" "%r14" "%r15" -}; -#endif - -/* Since R6 is a potential argument register, choose it last of the - call-saved registers. Likewise prefer the call-clobbered registers - in reverse order to maximize the chance of avoiding the arguments. */ -static const int tcg_target_reg_alloc_order[] = { - /* Call saved registers. */ - TCG_REG_R13, - TCG_REG_R12, - TCG_REG_R11, - TCG_REG_R10, - TCG_REG_R9, - TCG_REG_R8, - TCG_REG_R7, - TCG_REG_R6, - /* Call clobbered registers. */ - TCG_REG_R14, - TCG_REG_R0, - TCG_REG_R1, - /* Argument registers, in reverse order of allocation. */ - TCG_REG_R5, - TCG_REG_R4, - TCG_REG_R3, - TCG_REG_R2, -}; - -static const int tcg_target_call_iarg_regs[] = { - TCG_REG_R2, - TCG_REG_R3, - TCG_REG_R4, - TCG_REG_R5, - TCG_REG_R6, -}; - -static const int tcg_target_call_oarg_regs[] = { - TCG_REG_R2, -}; - -#define S390_CC_EQ 8 -#define S390_CC_LT 4 -#define S390_CC_GT 2 -#define S390_CC_OV 1 -#define S390_CC_NE (S390_CC_LT | S390_CC_GT) -#define S390_CC_LE (S390_CC_LT | S390_CC_EQ) -#define S390_CC_GE (S390_CC_GT | S390_CC_EQ) -#define S390_CC_NEVER 0 -#define S390_CC_ALWAYS 15 - -/* Condition codes that result from a COMPARE and COMPARE LOGICAL. */ -static const uint8_t tcg_cond_to_s390_cond[] = { - [TCG_COND_EQ] = S390_CC_EQ, - [TCG_COND_NE] = S390_CC_NE, - [TCG_COND_LT] = S390_CC_LT, - [TCG_COND_LE] = S390_CC_LE, - [TCG_COND_GT] = S390_CC_GT, - [TCG_COND_GE] = S390_CC_GE, - [TCG_COND_LTU] = S390_CC_LT, - [TCG_COND_LEU] = S390_CC_LE, - [TCG_COND_GTU] = S390_CC_GT, - [TCG_COND_GEU] = S390_CC_GE, -}; - -/* Condition codes that result from a LOAD AND TEST. Here, we have no - unsigned instruction variation, however since the test is vs zero we - can re-map the outcomes appropriately. */ -static const uint8_t tcg_cond_to_ltr_cond[] = { - [TCG_COND_EQ] = S390_CC_EQ, - [TCG_COND_NE] = S390_CC_NE, - [TCG_COND_LT] = S390_CC_LT, - [TCG_COND_LE] = S390_CC_LE, - [TCG_COND_GT] = S390_CC_GT, - [TCG_COND_GE] = S390_CC_GE, - [TCG_COND_LTU] = S390_CC_NEVER, - [TCG_COND_LEU] = S390_CC_EQ, - [TCG_COND_GTU] = S390_CC_NE, - [TCG_COND_GEU] = S390_CC_ALWAYS, -}; - -#ifdef CONFIG_SOFTMMU -static void * const qemu_ld_helpers[16] = { - [MO_UB] = helper_ret_ldub_mmu, - [MO_SB] = helper_ret_ldsb_mmu, - [MO_LEUW] = helper_le_lduw_mmu, - [MO_LESW] = helper_le_ldsw_mmu, - [MO_LEUL] = helper_le_ldul_mmu, - [MO_LESL] = helper_le_ldsl_mmu, - [MO_LEQ] = helper_le_ldq_mmu, - [MO_BEUW] = helper_be_lduw_mmu, - [MO_BESW] = helper_be_ldsw_mmu, - [MO_BEUL] = helper_be_ldul_mmu, - [MO_BESL] = helper_be_ldsl_mmu, - [MO_BEQ] = helper_be_ldq_mmu, -}; - -static void * const qemu_st_helpers[16] = { - [MO_UB] = helper_ret_stb_mmu, - [MO_LEUW] = helper_le_stw_mmu, - [MO_LEUL] = helper_le_stl_mmu, - [MO_LEQ] = helper_le_stq_mmu, - [MO_BEUW] = helper_be_stw_mmu, - [MO_BEUL] = helper_be_stl_mmu, - [MO_BEQ] = helper_be_stq_mmu, -}; -#endif - -static tcg_insn_unit *tb_ret_addr; - -/* A list of relevant facilities used by this translator. Some of these - are required for proper operation, and these are checked at startup. */ - -#define FACILITY_ZARCH_ACTIVE (1ULL << (63 - 2)) -#define FACILITY_LONG_DISP (1ULL << (63 - 18)) -#define FACILITY_EXT_IMM (1ULL << (63 - 21)) -#define FACILITY_GEN_INST_EXT (1ULL << (63 - 34)) -#define FACILITY_LOAD_ON_COND (1ULL << (63 - 45)) - -static uint64_t facilities; - -static void patch_reloc(tcg_insn_unit *code_ptr, int type, - intptr_t value, intptr_t addend) -{ - intptr_t pcrel2 = (tcg_insn_unit *)value - (code_ptr - 1); - assert(addend == -2); - - switch (type) { - case R_390_PC16DBL: - assert(pcrel2 == (int16_t)pcrel2); - tcg_patch16(code_ptr, pcrel2); - break; - case R_390_PC32DBL: - assert(pcrel2 == (int32_t)pcrel2); - tcg_patch32(code_ptr, pcrel2); - break; - default: - tcg_abort(); - break; - } -} - -/* parse target specific constraints */ -static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) -{ - const char *ct_str = *pct_str; - - switch (ct_str[0]) { - case 'r': /* all registers */ - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xffff); - break; - case 'R': /* not R0 */ - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xffff); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); - break; - case 'L': /* qemu_ld/st constraint */ - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xffff); - tcg_regset_reset_reg (ct->u.regs, TCG_REG_R2); - tcg_regset_reset_reg (ct->u.regs, TCG_REG_R3); - tcg_regset_reset_reg (ct->u.regs, TCG_REG_R4); - break; - case 'a': /* force R2 for division */ - ct->ct |= TCG_CT_REG; - tcg_regset_clear(ct->u.regs); - tcg_regset_set_reg(ct->u.regs, TCG_REG_R2); - break; - case 'b': /* force R3 for division */ - ct->ct |= TCG_CT_REG; - tcg_regset_clear(ct->u.regs); - tcg_regset_set_reg(ct->u.regs, TCG_REG_R3); - break; - case 'A': - ct->ct |= TCG_CT_CONST_ADLI; - break; - case 'K': - ct->ct |= TCG_CT_CONST_MULI; - break; - case 'O': - ct->ct |= TCG_CT_CONST_ORI; - break; - case 'X': - ct->ct |= TCG_CT_CONST_XORI; - break; - case 'C': - ct->ct |= TCG_CT_CONST_CMPI; - break; - default: - return -1; - } - ct_str++; - *pct_str = ct_str; - - return 0; -} - -/* Immediates to be used with logical OR. This is an optimization only, - since a full 64-bit immediate OR can always be performed with 4 sequential - OI[LH][LH] instructions. What we're looking for is immediates that we - can load efficiently, and the immediate load plus the reg-reg OR is - smaller than the sequential OI's. */ - -static int tcg_match_ori(TCGType type, tcg_target_long val) -{ - if (facilities & FACILITY_EXT_IMM) { - if (type == TCG_TYPE_I32) { - /* All 32-bit ORs can be performed with 1 48-bit insn. */ - return 1; - } - } - - /* Look for negative values. These are best to load with LGHI. */ - if (val < 0) { - if (val == (int16_t)val) { - return 0; - } - if (facilities & FACILITY_EXT_IMM) { - if (val == (int32_t)val) { - return 0; - } - } - } - - return 1; -} - -/* Immediates to be used with logical XOR. This is almost, but not quite, - only an optimization. XOR with immediate is only supported with the - extended-immediate facility. That said, there are a few patterns for - which it is better to load the value into a register first. */ - -static int tcg_match_xori(TCGType type, tcg_target_long val) -{ - if ((facilities & FACILITY_EXT_IMM) == 0) { - return 0; - } - - if (type == TCG_TYPE_I32) { - /* All 32-bit XORs can be performed with 1 48-bit insn. */ - return 1; - } - - /* Look for negative values. These are best to load with LGHI. */ - if (val < 0 && val == (int32_t)val) { - return 0; - } - - return 1; -} - -/* Imediates to be used with comparisons. */ - -static int tcg_match_cmpi(TCGType type, tcg_target_long val) -{ - if (facilities & FACILITY_EXT_IMM) { - /* The COMPARE IMMEDIATE instruction is available. */ - if (type == TCG_TYPE_I32) { - /* We have a 32-bit immediate and can compare against anything. */ - return 1; - } else { - /* ??? We have no insight here into whether the comparison is - signed or unsigned. The COMPARE IMMEDIATE insn uses a 32-bit - signed immediate, and the COMPARE LOGICAL IMMEDIATE insn uses - a 32-bit unsigned immediate. If we were to use the (semi) - obvious "val == (int32_t)val" we would be enabling unsigned - comparisons vs very large numbers. The only solution is to - take the intersection of the ranges. */ - /* ??? Another possible solution is to simply lie and allow all - constants here and force the out-of-range values into a temp - register in tgen_cmp when we have knowledge of the actual - comparison code in use. */ - return val >= 0 && val <= 0x7fffffff; - } - } else { - /* Only the LOAD AND TEST instruction is available. */ - return val == 0; - } -} - -/* Immediates to be used with add2/sub2. */ - -static int tcg_match_add2i(TCGType type, tcg_target_long val) -{ - if (facilities & FACILITY_EXT_IMM) { - if (type == TCG_TYPE_I32) { - return 1; - } else if (val >= -0xffffffffll && val <= 0xffffffffll) { - return 1; - } - } - return 0; -} - -/* Test if a constant matches the constraint. */ -static int tcg_target_const_match(tcg_target_long val, TCGType type, - const TCGArgConstraint *arg_ct) -{ - int ct = arg_ct->ct; - - if (ct & TCG_CT_CONST) { - return 1; - } - - if (type == TCG_TYPE_I32) { - val = (int32_t)val; - } - - /* The following are mutually exclusive. */ - if (ct & TCG_CT_CONST_MULI) { - /* Immediates that may be used with multiply. If we have the - general-instruction-extensions, then we have MULTIPLY SINGLE - IMMEDIATE with a signed 32-bit, otherwise we have only - MULTIPLY HALFWORD IMMEDIATE, with a signed 16-bit. */ - if (facilities & FACILITY_GEN_INST_EXT) { - return val == (int32_t)val; - } else { - return val == (int16_t)val; - } - } else if (ct & TCG_CT_CONST_ADLI) { - return tcg_match_add2i(type, val); - } else if (ct & TCG_CT_CONST_ORI) { - return tcg_match_ori(type, val); - } else if (ct & TCG_CT_CONST_XORI) { - return tcg_match_xori(type, val); - } else if (ct & TCG_CT_CONST_CMPI) { - return tcg_match_cmpi(type, val); - } - - return 0; -} - -/* Emit instructions according to the given instruction format. */ - -static void tcg_out_insn_RR(TCGContext *s, S390Opcode op, TCGReg r1, TCGReg r2) -{ - tcg_out16(s, (op << 8) | (r1 << 4) | r2); -} - -static void tcg_out_insn_RRE(TCGContext *s, S390Opcode op, - TCGReg r1, TCGReg r2) -{ - tcg_out32(s, (op << 16) | (r1 << 4) | r2); -} - -static void tcg_out_insn_RRF(TCGContext *s, S390Opcode op, - TCGReg r1, TCGReg r2, int m3) -{ - tcg_out32(s, (op << 16) | (m3 << 12) | (r1 << 4) | r2); -} - -static void tcg_out_insn_RI(TCGContext *s, S390Opcode op, TCGReg r1, int i2) -{ - tcg_out32(s, (op << 16) | (r1 << 20) | (i2 & 0xffff)); -} - -static void tcg_out_insn_RIL(TCGContext *s, S390Opcode op, TCGReg r1, int i2) -{ - tcg_out16(s, op | (r1 << 4)); - tcg_out32(s, i2); -} - -static void tcg_out_insn_RS(TCGContext *s, S390Opcode op, TCGReg r1, - TCGReg b2, TCGReg r3, int disp) -{ - tcg_out32(s, (op << 24) | (r1 << 20) | (r3 << 16) | (b2 << 12) - | (disp & 0xfff)); -} - -static void tcg_out_insn_RSY(TCGContext *s, S390Opcode op, TCGReg r1, - TCGReg b2, TCGReg r3, int disp) -{ - tcg_out16(s, (op & 0xff00) | (r1 << 4) | r3); - tcg_out32(s, (op & 0xff) | (b2 << 28) - | ((disp & 0xfff) << 16) | ((disp & 0xff000) >> 4)); -} - -#define tcg_out_insn_RX tcg_out_insn_RS -#define tcg_out_insn_RXY tcg_out_insn_RSY - -/* Emit an opcode with "type-checking" of the format. */ -#define tcg_out_insn(S, FMT, OP, ...) \ - glue(tcg_out_insn_,FMT)(S, glue(glue(FMT,_),OP), ## __VA_ARGS__) - - -/* emit 64-bit shifts */ -static void tcg_out_sh64(TCGContext* s, S390Opcode op, TCGReg dest, - TCGReg src, TCGReg sh_reg, int sh_imm) -{ - tcg_out_insn_RSY(s, op, dest, sh_reg, src, sh_imm); -} - -/* emit 32-bit shifts */ -static void tcg_out_sh32(TCGContext* s, S390Opcode op, TCGReg dest, - TCGReg sh_reg, int sh_imm) -{ - tcg_out_insn_RS(s, op, dest, sh_reg, 0, sh_imm); -} - -static void tcg_out_mov(TCGContext *s, TCGType type, TCGReg dst, TCGReg src) -{ - if (src != dst) { - if (type == TCG_TYPE_I32) { - tcg_out_insn(s, RR, LR, dst, src); - } else { - tcg_out_insn(s, RRE, LGR, dst, src); - } - } -} - -/* load a register with an immediate value */ -static void tcg_out_movi(TCGContext *s, TCGType type, - TCGReg ret, tcg_target_long sval) -{ - static const S390Opcode lli_insns[4] = { - RI_LLILL, RI_LLILH, RI_LLIHL, RI_LLIHH - }; - - tcg_target_ulong uval = sval; - int i; - - if (type == TCG_TYPE_I32) { - uval = (uint32_t)sval; - sval = (int32_t)sval; - } - - /* Try all 32-bit insns that can load it in one go. */ - if (sval >= -0x8000 && sval < 0x8000) { - tcg_out_insn(s, RI, LGHI, ret, sval); - return; - } - - for (i = 0; i < 4; i++) { - tcg_target_long mask = 0xffffull << i*16; - if ((uval & mask) == uval) { - tcg_out_insn_RI(s, lli_insns[i], ret, uval >> i*16); - return; - } - } - - /* Try all 48-bit insns that can load it in one go. */ - if (facilities & FACILITY_EXT_IMM) { - if (sval == (int32_t)sval) { - tcg_out_insn(s, RIL, LGFI, ret, sval); - return; - } - if (uval <= 0xffffffff) { - tcg_out_insn(s, RIL, LLILF, ret, uval); - return; - } - if ((uval & 0xffffffff) == 0) { - tcg_out_insn(s, RIL, LLIHF, ret, uval >> 31 >> 1); - return; - } - } - - /* Try for PC-relative address load. */ - if ((sval & 1) == 0) { - ptrdiff_t off = tcg_pcrel_diff(s, (void *)sval) >> 1; - if (off == (int32_t)off) { - tcg_out_insn(s, RIL, LARL, ret, off); - return; - } - } - - /* If extended immediates are not present, then we may have to issue - several instructions to load the low 32 bits. */ - if (!(facilities & FACILITY_EXT_IMM)) { - /* A 32-bit unsigned value can be loaded in 2 insns. And given - that the lli_insns loop above did not succeed, we know that - both insns are required. */ - if (uval <= 0xffffffff) { - tcg_out_insn(s, RI, LLILL, ret, uval); - tcg_out_insn(s, RI, IILH, ret, uval >> 16); - return; - } - - /* If all high bits are set, the value can be loaded in 2 or 3 insns. - We first want to make sure that all the high bits get set. With - luck the low 16-bits can be considered negative to perform that for - free, otherwise we load an explicit -1. */ - if (sval >> 31 >> 1 == -1) { - if (uval & 0x8000) { - tcg_out_insn(s, RI, LGHI, ret, uval); - } else { - tcg_out_insn(s, RI, LGHI, ret, -1); - tcg_out_insn(s, RI, IILL, ret, uval); - } - tcg_out_insn(s, RI, IILH, ret, uval >> 16); - return; - } - } - - /* If we get here, both the high and low parts have non-zero bits. */ - - /* Recurse to load the lower 32-bits. */ - tcg_out_movi(s, TCG_TYPE_I64, ret, uval & 0xffffffff); - - /* Insert data into the high 32-bits. */ - uval = uval >> 31 >> 1; - if (facilities & FACILITY_EXT_IMM) { - if (uval < 0x10000) { - tcg_out_insn(s, RI, IIHL, ret, uval); - } else if ((uval & 0xffff) == 0) { - tcg_out_insn(s, RI, IIHH, ret, uval >> 16); - } else { - tcg_out_insn(s, RIL, IIHF, ret, uval); - } - } else { - if (uval & 0xffff) { - tcg_out_insn(s, RI, IIHL, ret, uval); - } - if (uval & 0xffff0000) { - tcg_out_insn(s, RI, IIHH, ret, uval >> 16); - } - } -} - - -/* Emit a load/store type instruction. Inputs are: - DATA: The register to be loaded or stored. - BASE+OFS: The effective address. - OPC_RX: If the operation has an RX format opcode (e.g. STC), otherwise 0. - OPC_RXY: The RXY format opcode for the operation (e.g. STCY). */ - -static void tcg_out_mem(TCGContext *s, S390Opcode opc_rx, S390Opcode opc_rxy, - TCGReg data, TCGReg base, TCGReg index, - tcg_target_long ofs) -{ - if (ofs < -0x80000 || ofs >= 0x80000) { - /* Combine the low 20 bits of the offset with the actual load insn; - the high 44 bits must come from an immediate load. */ - tcg_target_long low = ((ofs & 0xfffff) ^ 0x80000) - 0x80000; - tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, ofs - low); - ofs = low; - - /* If we were already given an index register, add it in. */ - if (index != TCG_REG_NONE) { - tcg_out_insn(s, RRE, AGR, TCG_TMP0, index); - } - index = TCG_TMP0; - } - - if (opc_rx && ofs >= 0 && ofs < 0x1000) { - tcg_out_insn_RX(s, opc_rx, data, base, index, ofs); - } else { - tcg_out_insn_RXY(s, opc_rxy, data, base, index, ofs); - } -} - - -/* load data without address translation or endianness conversion */ -static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg data, - TCGReg base, intptr_t ofs) -{ - if (type == TCG_TYPE_I32) { - tcg_out_mem(s, RX_L, RXY_LY, data, base, TCG_REG_NONE, ofs); - } else { - tcg_out_mem(s, 0, RXY_LG, data, base, TCG_REG_NONE, ofs); - } -} - -static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg data, - TCGReg base, intptr_t ofs) -{ - if (type == TCG_TYPE_I32) { - tcg_out_mem(s, RX_ST, RXY_STY, data, base, TCG_REG_NONE, ofs); - } else { - tcg_out_mem(s, 0, RXY_STG, data, base, TCG_REG_NONE, ofs); - } -} - -/* load data from an absolute host address */ -static void tcg_out_ld_abs(TCGContext *s, TCGType type, TCGReg dest, void *abs) -{ - intptr_t addr = (intptr_t)abs; - - if ((facilities & FACILITY_GEN_INST_EXT) && !(addr & 1)) { - ptrdiff_t disp = tcg_pcrel_diff(s, abs) >> 1; - if (disp == (int32_t)disp) { - if (type == TCG_TYPE_I32) { - tcg_out_insn(s, RIL, LRL, dest, disp); - } else { - tcg_out_insn(s, RIL, LGRL, dest, disp); - } - return; - } - } - - tcg_out_movi(s, TCG_TYPE_PTR, dest, addr & ~0xffff); - tcg_out_ld(s, type, dest, dest, addr & 0xffff); -} - -static inline void tcg_out_risbg(TCGContext *s, TCGReg dest, TCGReg src, - int msb, int lsb, int ofs, int z) -{ - /* Format RIE-f */ - tcg_out16(s, (RIE_RISBG & 0xff00) | (dest << 4) | src); - tcg_out16(s, (msb << 8) | (z << 7) | lsb); - tcg_out16(s, (ofs << 8) | (RIE_RISBG & 0xff)); -} - -static void tgen_ext8s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) -{ - if (facilities & FACILITY_EXT_IMM) { - tcg_out_insn(s, RRE, LGBR, dest, src); - return; - } - - if (type == TCG_TYPE_I32) { - if (dest == src) { - tcg_out_sh32(s, RS_SLL, dest, TCG_REG_NONE, 24); - } else { - tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 24); - } - tcg_out_sh32(s, RS_SRA, dest, TCG_REG_NONE, 24); - } else { - tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 56); - tcg_out_sh64(s, RSY_SRAG, dest, dest, TCG_REG_NONE, 56); - } -} - -static void tgen_ext8u(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) -{ - if (facilities & FACILITY_EXT_IMM) { - tcg_out_insn(s, RRE, LLGCR, dest, src); - return; - } - - if (dest == src) { - tcg_out_movi(s, type, TCG_TMP0, 0xff); - src = TCG_TMP0; - } else { - tcg_out_movi(s, type, dest, 0xff); - } - if (type == TCG_TYPE_I32) { - tcg_out_insn(s, RR, NR, dest, src); - } else { - tcg_out_insn(s, RRE, NGR, dest, src); - } -} - -static void tgen_ext16s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) -{ - if (facilities & FACILITY_EXT_IMM) { - tcg_out_insn(s, RRE, LGHR, dest, src); - return; - } - - if (type == TCG_TYPE_I32) { - if (dest == src) { - tcg_out_sh32(s, RS_SLL, dest, TCG_REG_NONE, 16); - } else { - tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 16); - } - tcg_out_sh32(s, RS_SRA, dest, TCG_REG_NONE, 16); - } else { - tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 48); - tcg_out_sh64(s, RSY_SRAG, dest, dest, TCG_REG_NONE, 48); - } -} - -static void tgen_ext16u(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) -{ - if (facilities & FACILITY_EXT_IMM) { - tcg_out_insn(s, RRE, LLGHR, dest, src); - return; - } - - if (dest == src) { - tcg_out_movi(s, type, TCG_TMP0, 0xffff); - src = TCG_TMP0; - } else { - tcg_out_movi(s, type, dest, 0xffff); - } - if (type == TCG_TYPE_I32) { - tcg_out_insn(s, RR, NR, dest, src); - } else { - tcg_out_insn(s, RRE, NGR, dest, src); - } -} - -static inline void tgen_ext32s(TCGContext *s, TCGReg dest, TCGReg src) -{ - tcg_out_insn(s, RRE, LGFR, dest, src); -} - -static inline void tgen_ext32u(TCGContext *s, TCGReg dest, TCGReg src) -{ - tcg_out_insn(s, RRE, LLGFR, dest, src); -} - -/* Accept bit patterns like these: - 0....01....1 - 1....10....0 - 1..10..01..1 - 0..01..10..0 - Copied from gcc sources. */ -static inline bool risbg_mask(uint64_t c) -{ - uint64_t lsb; - /* We don't change the number of transitions by inverting, - so make sure we start with the LSB zero. */ - if (c & 1) { - c = ~c; - } - /* Reject all zeros or all ones. */ - if (c == 0) { - return false; - } - /* Find the first transition. */ - lsb = c & -c; - /* Invert to look for a second transition. */ - c = ~c; - /* Erase the first transition. */ - c &= -lsb; - /* Find the second transition, if any. */ - lsb = c & -c; - /* Match if all the bits are 1's, or if c is zero. */ - return c == -lsb; -} - -static void tgen_andi_risbg(TCGContext *s, TCGReg out, TCGReg in, uint64_t val) -{ - int msb, lsb; - if ((val & 0x8000000000000001ull) == 0x8000000000000001ull) { - /* Achieve wraparound by swapping msb and lsb. */ - msb = 64 - ctz64(~val); - lsb = clz64(~val) - 1; - } else { - msb = clz64(val); - lsb = 63 - ctz64(val); - } - tcg_out_risbg(s, out, in, msb, lsb, 0, 1); -} - -static void tgen_andi(TCGContext *s, TCGType type, TCGReg dest, uint64_t val) -{ - static const S390Opcode ni_insns[4] = { - RI_NILL, RI_NILH, RI_NIHL, RI_NIHH - }; - static const S390Opcode nif_insns[2] = { - RIL_NILF, RIL_NIHF - }; - uint64_t valid = (type == TCG_TYPE_I32 ? 0xffffffffull : -1ull); - int i; - - /* Look for the zero-extensions. */ - if ((val & valid) == 0xffffffff) { - tgen_ext32u(s, dest, dest); - return; - } - if (facilities & FACILITY_EXT_IMM) { - if ((val & valid) == 0xff) { - tgen_ext8u(s, TCG_TYPE_I64, dest, dest); - return; - } - if ((val & valid) == 0xffff) { - tgen_ext16u(s, TCG_TYPE_I64, dest, dest); - return; - } - } - - /* Try all 32-bit insns that can perform it in one go. */ - for (i = 0; i < 4; i++) { - tcg_target_ulong mask = ~(0xffffull << i*16); - if (((val | ~valid) & mask) == mask) { - tcg_out_insn_RI(s, ni_insns[i], dest, val >> i*16); - return; - } - } - - /* Try all 48-bit insns that can perform it in one go. */ - if (facilities & FACILITY_EXT_IMM) { - for (i = 0; i < 2; i++) { - tcg_target_ulong mask = ~(0xffffffffull << i*32); - if (((val | ~valid) & mask) == mask) { - tcg_out_insn_RIL(s, nif_insns[i], dest, val >> i*32); - return; - } - } - } - if ((facilities & FACILITY_GEN_INST_EXT) && risbg_mask(val)) { - tgen_andi_risbg(s, dest, dest, val); - return; - } - - /* Fall back to loading the constant. */ - tcg_out_movi(s, type, TCG_TMP0, val); - if (type == TCG_TYPE_I32) { - tcg_out_insn(s, RR, NR, dest, TCG_TMP0); - } else { - tcg_out_insn(s, RRE, NGR, dest, TCG_TMP0); - } -} - -static void tgen64_ori(TCGContext *s, TCGReg dest, tcg_target_ulong val) -{ - static const S390Opcode oi_insns[4] = { - RI_OILL, RI_OILH, RI_OIHL, RI_OIHH - }; - static const S390Opcode nif_insns[2] = { - RIL_OILF, RIL_OIHF - }; - - int i; - - /* Look for no-op. */ - if (val == 0) { - return; - } - - if (facilities & FACILITY_EXT_IMM) { - /* Try all 32-bit insns that can perform it in one go. */ - for (i = 0; i < 4; i++) { - tcg_target_ulong mask = (0xffffull << i*16); - if ((val & mask) != 0 && (val & ~mask) == 0) { - tcg_out_insn_RI(s, oi_insns[i], dest, val >> i*16); - return; - } - } - - /* Try all 48-bit insns that can perform it in one go. */ - for (i = 0; i < 2; i++) { - tcg_target_ulong mask = (0xffffffffull << i*32); - if ((val & mask) != 0 && (val & ~mask) == 0) { - tcg_out_insn_RIL(s, nif_insns[i], dest, val >> i*32); - return; - } - } - - /* Perform the OR via sequential modifications to the high and - low parts. Do this via recursion to handle 16-bit vs 32-bit - masks in each half. */ - tgen64_ori(s, dest, val & 0x00000000ffffffffull); - tgen64_ori(s, dest, val & 0xffffffff00000000ull); - } else { - /* With no extended-immediate facility, we don't need to be so - clever. Just iterate over the insns and mask in the constant. */ - for (i = 0; i < 4; i++) { - tcg_target_ulong mask = (0xffffull << i*16); - if ((val & mask) != 0) { - tcg_out_insn_RI(s, oi_insns[i], dest, val >> i*16); - } - } - } -} - -static void tgen64_xori(TCGContext *s, TCGReg dest, tcg_target_ulong val) -{ - /* Perform the xor by parts. */ - if (val & 0xffffffff) { - tcg_out_insn(s, RIL, XILF, dest, val); - } - if (val > 0xffffffff) { - tcg_out_insn(s, RIL, XIHF, dest, val >> 31 >> 1); - } -} - -static int tgen_cmp(TCGContext *s, TCGType type, TCGCond c, TCGReg r1, - TCGArg c2, int c2const) -{ - bool is_unsigned = is_unsigned_cond(c); - if (c2const) { - if (c2 == 0) { - if (type == TCG_TYPE_I32) { - tcg_out_insn(s, RR, LTR, r1, r1); - } else { - tcg_out_insn(s, RRE, LTGR, r1, r1); - } - return tcg_cond_to_ltr_cond[c]; - } else { - if (is_unsigned) { - if (type == TCG_TYPE_I32) { - tcg_out_insn(s, RIL, CLFI, r1, c2); - } else { - tcg_out_insn(s, RIL, CLGFI, r1, c2); - } - } else { - if (type == TCG_TYPE_I32) { - tcg_out_insn(s, RIL, CFI, r1, c2); - } else { - tcg_out_insn(s, RIL, CGFI, r1, c2); - } - } - } - } else { - if (is_unsigned) { - if (type == TCG_TYPE_I32) { - tcg_out_insn(s, RR, CLR, r1, c2); - } else { - tcg_out_insn(s, RRE, CLGR, r1, c2); - } - } else { - if (type == TCG_TYPE_I32) { - tcg_out_insn(s, RR, CR, r1, c2); - } else { - tcg_out_insn(s, RRE, CGR, r1, c2); - } - } - } - return tcg_cond_to_s390_cond[c]; -} - -static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, - TCGReg dest, TCGReg c1, TCGArg c2, int c2const) -{ - int cc; - - switch (cond) { - case TCG_COND_GTU: - case TCG_COND_GT: - do_greater: - /* The result of a compare has CC=2 for GT and CC=3 unused. - ADD LOGICAL WITH CARRY considers (CC & 2) the carry bit. */ - tgen_cmp(s, type, cond, c1, c2, c2const); - tcg_out_movi(s, type, dest, 0); - tcg_out_insn(s, RRE, ALCGR, dest, dest); - return; - - case TCG_COND_GEU: - do_geu: - /* We need "real" carry semantics, so use SUBTRACT LOGICAL - instead of COMPARE LOGICAL. This needs an extra move. */ - tcg_out_mov(s, type, TCG_TMP0, c1); - if (c2const) { - tcg_out_movi(s, TCG_TYPE_I64, dest, 0); - if (type == TCG_TYPE_I32) { - tcg_out_insn(s, RIL, SLFI, TCG_TMP0, c2); - } else { - tcg_out_insn(s, RIL, SLGFI, TCG_TMP0, c2); - } - } else { - if (type == TCG_TYPE_I32) { - tcg_out_insn(s, RR, SLR, TCG_TMP0, c2); - } else { - tcg_out_insn(s, RRE, SLGR, TCG_TMP0, c2); - } - tcg_out_movi(s, TCG_TYPE_I64, dest, 0); - } - tcg_out_insn(s, RRE, ALCGR, dest, dest); - return; - - case TCG_COND_LEU: - case TCG_COND_LTU: - case TCG_COND_LT: - /* Swap operands so that we can use GEU/GTU/GT. */ - if (c2const) { - tcg_out_movi(s, type, TCG_TMP0, c2); - c2 = c1; - c2const = 0; - c1 = TCG_TMP0; - } else { - TCGReg t = c1; - c1 = c2; - c2 = t; - } - if (cond == TCG_COND_LEU) { - goto do_geu; - } - cond = tcg_swap_cond(cond); - goto do_greater; - - case TCG_COND_NE: - /* X != 0 is X > 0. */ - if (c2const && c2 == 0) { - cond = TCG_COND_GTU; - goto do_greater; - } - break; - - case TCG_COND_EQ: - /* X == 0 is X <= 0 is 0 >= X. */ - if (c2const && c2 == 0) { - tcg_out_movi(s, TCG_TYPE_I64, TCG_TMP0, 0); - c2 = c1; - c2const = 0; - c1 = TCG_TMP0; - goto do_geu; - } - break; - - default: - break; - } - - cc = tgen_cmp(s, type, cond, c1, c2, c2const); - if (facilities & FACILITY_LOAD_ON_COND) { - /* Emit: d = 0, t = 1, d = (cc ? t : d). */ - tcg_out_movi(s, TCG_TYPE_I64, dest, 0); - tcg_out_movi(s, TCG_TYPE_I64, TCG_TMP0, 1); - tcg_out_insn(s, RRF, LOCGR, dest, TCG_TMP0, cc); - } else { - /* Emit: d = 1; if (cc) goto over; d = 0; over: */ - tcg_out_movi(s, type, dest, 1); - tcg_out_insn(s, RI, BRC, cc, (4 + 4) >> 1); - tcg_out_movi(s, type, dest, 0); - } -} - -static void tgen_movcond(TCGContext *s, TCGType type, TCGCond c, TCGReg dest, - TCGReg c1, TCGArg c2, int c2const, TCGReg r3) -{ - int cc; - if (facilities & FACILITY_LOAD_ON_COND) { - cc = tgen_cmp(s, type, c, c1, c2, c2const); - tcg_out_insn(s, RRF, LOCGR, dest, r3, cc); - } else { - c = tcg_invert_cond(c); - cc = tgen_cmp(s, type, c, c1, c2, c2const); - - /* Emit: if (cc) goto over; dest = r3; over: */ - tcg_out_insn(s, RI, BRC, cc, (4 + 4) >> 1); - tcg_out_insn(s, RRE, LGR, dest, r3); - } -} - -bool tcg_target_deposit_valid(int ofs, int len) -{ - return (facilities & FACILITY_GEN_INST_EXT) != 0; -} - -static void tgen_deposit(TCGContext *s, TCGReg dest, TCGReg src, - int ofs, int len) -{ - int lsb = (63 - ofs); - int msb = lsb - (len - 1); - tcg_out_risbg(s, dest, src, msb, lsb, ofs, 0); -} - -static void tgen_gotoi(TCGContext *s, int cc, tcg_insn_unit *dest) -{ - ptrdiff_t off = dest - s->code_ptr; - if (off == (int16_t)off) { - tcg_out_insn(s, RI, BRC, cc, off); - } else if (off == (int32_t)off) { - tcg_out_insn(s, RIL, BRCL, cc, off); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, (uintptr_t)dest); - tcg_out_insn(s, RR, BCR, cc, TCG_TMP0); - } -} - -static void tgen_branch(TCGContext *s, int cc, TCGLabel *l) -{ - if (l->has_value) { - tgen_gotoi(s, cc, l->u.value_ptr); - } else if (USE_LONG_BRANCHES) { - tcg_out16(s, RIL_BRCL | (cc << 4)); - tcg_out_reloc(s, s->code_ptr, R_390_PC32DBL, l, -2); - s->code_ptr += 2; - } else { - tcg_out16(s, RI_BRC | (cc << 4)); - tcg_out_reloc(s, s->code_ptr, R_390_PC16DBL, l, -2); - s->code_ptr += 1; - } -} - -static void tgen_compare_branch(TCGContext *s, S390Opcode opc, int cc, - TCGReg r1, TCGReg r2, TCGLabel *l) -{ - intptr_t off; - - if (l->has_value) { - off = l->u.value_ptr - s->code_ptr; - } else { - /* We need to keep the offset unchanged for retranslation. */ - off = s->code_ptr[1]; - tcg_out_reloc(s, s->code_ptr + 1, R_390_PC16DBL, l, -2); - } - - tcg_out16(s, (opc & 0xff00) | (r1 << 4) | r2); - tcg_out16(s, off); - tcg_out16(s, cc << 12 | (opc & 0xff)); -} - -static void tgen_compare_imm_branch(TCGContext *s, S390Opcode opc, int cc, - TCGReg r1, int i2, TCGLabel *l) -{ - tcg_target_long off; - - if (l->has_value) { - off = l->u.value_ptr - s->code_ptr; - } else { - /* We need to keep the offset unchanged for retranslation. */ - off = s->code_ptr[1]; - tcg_out_reloc(s, s->code_ptr + 1, R_390_PC16DBL, l, -2); - } - - tcg_out16(s, (opc & 0xff00) | (r1 << 4) | cc); - tcg_out16(s, off); - tcg_out16(s, (i2 << 8) | (opc & 0xff)); -} - -static void tgen_brcond(TCGContext *s, TCGType type, TCGCond c, - TCGReg r1, TCGArg c2, int c2const, TCGLabel *l) -{ - int cc; - - if (facilities & FACILITY_GEN_INST_EXT) { - bool is_unsigned = is_unsigned_cond(c); - bool in_range; - S390Opcode opc; - - cc = tcg_cond_to_s390_cond[c]; - - if (!c2const) { - opc = (type == TCG_TYPE_I32 - ? (is_unsigned ? RIE_CLRJ : RIE_CRJ) - : (is_unsigned ? RIE_CLGRJ : RIE_CGRJ)); - tgen_compare_branch(s, opc, cc, r1, c2, l); - return; - } - - /* COMPARE IMMEDIATE AND BRANCH RELATIVE has an 8-bit immediate field. - If the immediate we've been given does not fit that range, we'll - fall back to separate compare and branch instructions using the - larger comparison range afforded by COMPARE IMMEDIATE. */ - if (type == TCG_TYPE_I32) { - if (is_unsigned) { - opc = RIE_CLIJ; - in_range = (uint32_t)c2 == (uint8_t)c2; - } else { - opc = RIE_CIJ; - in_range = (int32_t)c2 == (int8_t)c2; - } - } else { - if (is_unsigned) { - opc = RIE_CLGIJ; - in_range = (uint64_t)c2 == (uint8_t)c2; - } else { - opc = RIE_CGIJ; - in_range = (int64_t)c2 == (int8_t)c2; - } - } - if (in_range) { - tgen_compare_imm_branch(s, opc, cc, r1, c2, l); - return; - } - } - - cc = tgen_cmp(s, type, c, r1, c2, c2const); - tgen_branch(s, cc, l); -} - -static void tcg_out_call(TCGContext *s, tcg_insn_unit *dest) -{ - ptrdiff_t off = dest - s->code_ptr; - if (off == (int32_t)off) { - tcg_out_insn(s, RIL, BRASL, TCG_REG_R14, off); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, (uintptr_t)dest); - tcg_out_insn(s, RR, BASR, TCG_REG_R14, TCG_TMP0); - } -} - -static void tcg_out_qemu_ld_direct(TCGContext *s, TCGMemOp opc, TCGReg data, - TCGReg base, TCGReg index, int disp) -{ - switch (opc & (MO_SSIZE | MO_BSWAP)) { - case MO_UB: - tcg_out_insn(s, RXY, LLGC, data, base, index, disp); - break; - case MO_SB: - tcg_out_insn(s, RXY, LGB, data, base, index, disp); - break; - - case MO_UW | MO_BSWAP: - /* swapped unsigned halfword load with upper bits zeroed */ - tcg_out_insn(s, RXY, LRVH, data, base, index, disp); - tgen_ext16u(s, TCG_TYPE_I64, data, data); - break; - case MO_UW: - tcg_out_insn(s, RXY, LLGH, data, base, index, disp); - break; - - case MO_SW | MO_BSWAP: - /* swapped sign-extended halfword load */ - tcg_out_insn(s, RXY, LRVH, data, base, index, disp); - tgen_ext16s(s, TCG_TYPE_I64, data, data); - break; - case MO_SW: - tcg_out_insn(s, RXY, LGH, data, base, index, disp); - break; - - case MO_UL | MO_BSWAP: - /* swapped unsigned int load with upper bits zeroed */ - tcg_out_insn(s, RXY, LRV, data, base, index, disp); - tgen_ext32u(s, data, data); - break; - case MO_UL: - tcg_out_insn(s, RXY, LLGF, data, base, index, disp); - break; - - case MO_SL | MO_BSWAP: - /* swapped sign-extended int load */ - tcg_out_insn(s, RXY, LRV, data, base, index, disp); - tgen_ext32s(s, data, data); - break; - case MO_SL: - tcg_out_insn(s, RXY, LGF, data, base, index, disp); - break; - - case MO_Q | MO_BSWAP: - tcg_out_insn(s, RXY, LRVG, data, base, index, disp); - break; - case MO_Q: - tcg_out_insn(s, RXY, LG, data, base, index, disp); - break; - - default: - tcg_abort(); - } -} - -static void tcg_out_qemu_st_direct(TCGContext *s, TCGMemOp opc, TCGReg data, - TCGReg base, TCGReg index, int disp) -{ - switch (opc & (MO_SIZE | MO_BSWAP)) { - case MO_UB: - if (disp >= 0 && disp < 0x1000) { - tcg_out_insn(s, RX, STC, data, base, index, disp); - } else { - tcg_out_insn(s, RXY, STCY, data, base, index, disp); - } - break; - - case MO_UW | MO_BSWAP: - tcg_out_insn(s, RXY, STRVH, data, base, index, disp); - break; - case MO_UW: - if (disp >= 0 && disp < 0x1000) { - tcg_out_insn(s, RX, STH, data, base, index, disp); - } else { - tcg_out_insn(s, RXY, STHY, data, base, index, disp); - } - break; - - case MO_UL | MO_BSWAP: - tcg_out_insn(s, RXY, STRV, data, base, index, disp); - break; - case MO_UL: - if (disp >= 0 && disp < 0x1000) { - tcg_out_insn(s, RX, ST, data, base, index, disp); - } else { - tcg_out_insn(s, RXY, STY, data, base, index, disp); - } - break; - - case MO_Q | MO_BSWAP: - tcg_out_insn(s, RXY, STRVG, data, base, index, disp); - break; - case MO_Q: - tcg_out_insn(s, RXY, STG, data, base, index, disp); - break; - - default: - tcg_abort(); - } -} - -#if defined(CONFIG_SOFTMMU) -/* We're expecting to use a 20-bit signed offset on the tlb memory ops. - Using the offset of the second entry in the last tlb table ensures - that we can index all of the elements of the first entry. */ -QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table[NB_MMU_MODES - 1][1]) - > 0x7ffff); - -/* Load and compare a TLB entry, leaving the flags set. Loads the TLB - addend into R2. Returns a register with the santitized guest address. */ -static TCGReg tcg_out_tlb_read(TCGContext* s, TCGReg addr_reg, TCGMemOp opc, - int mem_index, bool is_ld) -{ - TCGMemOp s_bits = opc & MO_SIZE; - uint64_t tlb_mask = TARGET_PAGE_MASK | ((1 << s_bits) - 1); - int ofs; - - if (facilities & FACILITY_GEN_INST_EXT) { - tcg_out_risbg(s, TCG_REG_R2, addr_reg, - 64 - CPU_TLB_BITS - CPU_TLB_ENTRY_BITS, - 63 - CPU_TLB_ENTRY_BITS, - 64 + CPU_TLB_ENTRY_BITS - TARGET_PAGE_BITS, 1); - tgen_andi_risbg(s, TCG_REG_R3, addr_reg, tlb_mask); - } else { - tcg_out_sh64(s, RSY_SRLG, TCG_REG_R2, addr_reg, TCG_REG_NONE, - TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - tcg_out_movi(s, TCG_TYPE_TL, TCG_REG_R3, addr_reg); - tgen_andi(s, TCG_TYPE_I64, TCG_REG_R2, - (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS); - tgen_andi(s, TCG_TYPE_TL, TCG_REG_R3, tlb_mask); - } - - if (is_ld) { - ofs = offsetof(CPUArchState, tlb_table[mem_index][0].addr_read); - } else { - ofs = offsetof(CPUArchState, tlb_table[mem_index][0].addr_write); - } - if (TARGET_LONG_BITS == 32) { - tcg_out_mem(s, RX_C, RXY_CY, TCG_REG_R3, TCG_REG_R2, TCG_AREG0, ofs); - } else { - tcg_out_mem(s, 0, RXY_CG, TCG_REG_R3, TCG_REG_R2, TCG_AREG0, ofs); - } - - ofs = offsetof(CPUArchState, tlb_table[mem_index][0].addend); - tcg_out_mem(s, 0, RXY_LG, TCG_REG_R2, TCG_REG_R2, TCG_AREG0, ofs); - - if (TARGET_LONG_BITS == 32) { - tgen_ext32u(s, TCG_REG_R3, addr_reg); - return TCG_REG_R3; - } - return addr_reg; -} - -static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOpIdx oi, - TCGReg data, TCGReg addr, - tcg_insn_unit *raddr, tcg_insn_unit *label_ptr) -{ - TCGLabelQemuLdst *label = new_ldst_label(s); - - label->is_ld = is_ld; - label->oi = oi; - label->datalo_reg = data; - label->addrlo_reg = addr; - label->raddr = raddr; - label->label_ptr[0] = label_ptr; -} - -static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) -{ - TCGReg addr_reg = lb->addrlo_reg; - TCGReg data_reg = lb->datalo_reg; - TCGMemOpIdx oi = lb->oi; - TCGMemOp opc = get_memop(oi); - - patch_reloc(lb->label_ptr[0], R_390_PC16DBL, (intptr_t)s->code_ptr, -2); - - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_R2, TCG_AREG0); - if (TARGET_LONG_BITS == 64) { - tcg_out_mov(s, TCG_TYPE_I64, TCG_REG_R3, addr_reg); - } - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R4, oi); - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R5, (uintptr_t)lb->raddr); - tcg_out_call(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SSIZE)]); - tcg_out_mov(s, TCG_TYPE_I64, data_reg, TCG_REG_R2); - - tgen_gotoi(s, S390_CC_ALWAYS, lb->raddr); -} - -static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) -{ - TCGReg addr_reg = lb->addrlo_reg; - TCGReg data_reg = lb->datalo_reg; - TCGMemOpIdx oi = lb->oi; - TCGMemOp opc = get_memop(oi); - - patch_reloc(lb->label_ptr[0], R_390_PC16DBL, (intptr_t)s->code_ptr, -2); - - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_R2, TCG_AREG0); - if (TARGET_LONG_BITS == 64) { - tcg_out_mov(s, TCG_TYPE_I64, TCG_REG_R3, addr_reg); - } - switch (opc & MO_SIZE) { - case MO_UB: - tgen_ext8u(s, TCG_TYPE_I64, TCG_REG_R4, data_reg); - break; - case MO_UW: - tgen_ext16u(s, TCG_TYPE_I64, TCG_REG_R4, data_reg); - break; - case MO_UL: - tgen_ext32u(s, TCG_REG_R4, data_reg); - break; - case MO_Q: - tcg_out_mov(s, TCG_TYPE_I64, TCG_REG_R4, data_reg); - break; - default: - tcg_abort(); - } - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R5, oi); - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R6, (uintptr_t)lb->raddr); - tcg_out_call(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)]); - - tgen_gotoi(s, S390_CC_ALWAYS, lb->raddr); -} -#else -static void tcg_prepare_user_ldst(TCGContext *s, TCGReg *addr_reg, - TCGReg *index_reg, tcg_target_long *disp) -{ - if (TARGET_LONG_BITS == 32) { - tgen_ext32u(s, TCG_TMP0, *addr_reg); - *addr_reg = TCG_TMP0; - } - if (GUEST_BASE < 0x80000) { - *index_reg = TCG_REG_NONE; - *disp = GUEST_BASE; - } else { - *index_reg = TCG_GUEST_BASE_REG; - *disp = 0; - } -} -#endif /* CONFIG_SOFTMMU */ - -static void tcg_out_qemu_ld(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, - TCGMemOpIdx oi) -{ - TCGMemOp opc = get_memop(oi); -#ifdef CONFIG_SOFTMMU - unsigned mem_index = get_mmuidx(oi); - tcg_insn_unit *label_ptr; - TCGReg base_reg; - - base_reg = tcg_out_tlb_read(s, addr_reg, opc, mem_index, 1); - - /* We need to keep the offset unchanged for retranslation. */ - tcg_out16(s, RI_BRC | (S390_CC_NE << 4)); - label_ptr = s->code_ptr; - s->code_ptr += 1; - - tcg_out_qemu_ld_direct(s, opc, data_reg, base_reg, TCG_REG_R2, 0); - - add_qemu_ldst_label(s, 1, oi, data_reg, addr_reg, s->code_ptr, label_ptr); -#else - TCGReg index_reg; - tcg_target_long disp; - - tcg_prepare_user_ldst(s, &addr_reg, &index_reg, &disp); - tcg_out_qemu_ld_direct(s, opc, data_reg, addr_reg, index_reg, disp); -#endif -} - -static void tcg_out_qemu_st(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, - TCGMemOpIdx oi) -{ - TCGMemOp opc = get_memop(oi); -#ifdef CONFIG_SOFTMMU - unsigned mem_index = get_mmuidx(oi); - tcg_insn_unit *label_ptr; - TCGReg base_reg; - - base_reg = tcg_out_tlb_read(s, addr_reg, opc, mem_index, 0); - - /* We need to keep the offset unchanged for retranslation. */ - tcg_out16(s, RI_BRC | (S390_CC_NE << 4)); - label_ptr = s->code_ptr; - s->code_ptr += 1; - - tcg_out_qemu_st_direct(s, opc, data_reg, base_reg, TCG_REG_R2, 0); - - add_qemu_ldst_label(s, 0, oi, data_reg, addr_reg, s->code_ptr, label_ptr); -#else - TCGReg index_reg; - tcg_target_long disp; - - tcg_prepare_user_ldst(s, &addr_reg, &index_reg, &disp); - tcg_out_qemu_st_direct(s, opc, data_reg, addr_reg, index_reg, disp); -#endif -} - -# define OP_32_64(x) \ - case glue(glue(INDEX_op_,x),_i32): \ - case glue(glue(INDEX_op_,x),_i64) - -static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, - const TCGArg *args, const int *const_args) -{ - S390Opcode op; - TCGArg a0, a1, a2; - - switch (opc) { - case INDEX_op_exit_tb: - /* return value */ - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, args[0]); - tgen_gotoi(s, S390_CC_ALWAYS, tb_ret_addr); - break; - - case INDEX_op_goto_tb: - if (s->tb_jmp_offset) { - tcg_out16(s, RIL_BRCL | (S390_CC_ALWAYS << 4)); - s->tb_jmp_offset[args[0]] = tcg_current_code_size(s); - s->code_ptr += 2; - } else { - /* load address stored at s->tb_next + args[0] */ - tcg_out_ld_abs(s, TCG_TYPE_PTR, TCG_TMP0, s->tb_next + args[0]); - /* and go there */ - tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_TMP0); - } - s->tb_next_offset[args[0]] = tcg_current_code_size(s); - break; - - OP_32_64(ld8u): - /* ??? LLC (RXY format) is only present with the extended-immediate - facility, whereas LLGC is always present. */ - tcg_out_mem(s, 0, RXY_LLGC, args[0], args[1], TCG_REG_NONE, args[2]); - break; - - OP_32_64(ld8s): - /* ??? LB is no smaller than LGB, so no point to using it. */ - tcg_out_mem(s, 0, RXY_LGB, args[0], args[1], TCG_REG_NONE, args[2]); - break; - - OP_32_64(ld16u): - /* ??? LLH (RXY format) is only present with the extended-immediate - facility, whereas LLGH is always present. */ - tcg_out_mem(s, 0, RXY_LLGH, args[0], args[1], TCG_REG_NONE, args[2]); - break; - - case INDEX_op_ld16s_i32: - tcg_out_mem(s, RX_LH, RXY_LHY, args[0], args[1], TCG_REG_NONE, args[2]); - break; - - case INDEX_op_ld_i32: - tcg_out_ld(s, TCG_TYPE_I32, args[0], args[1], args[2]); - break; - - OP_32_64(st8): - tcg_out_mem(s, RX_STC, RXY_STCY, args[0], args[1], - TCG_REG_NONE, args[2]); - break; - - OP_32_64(st16): - tcg_out_mem(s, RX_STH, RXY_STHY, args[0], args[1], - TCG_REG_NONE, args[2]); - break; - - case INDEX_op_st_i32: - tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); - break; - - case INDEX_op_add_i32: - a0 = args[0], a1 = args[1], a2 = (int32_t)args[2]; - if (const_args[2]) { - do_addi_32: - if (a0 == a1) { - if (a2 == (int16_t)a2) { - tcg_out_insn(s, RI, AHI, a0, a2); - break; - } - if (facilities & FACILITY_EXT_IMM) { - tcg_out_insn(s, RIL, AFI, a0, a2); - break; - } - } - tcg_out_mem(s, RX_LA, RXY_LAY, a0, a1, TCG_REG_NONE, a2); - } else if (a0 == a1) { - tcg_out_insn(s, RR, AR, a0, a2); - } else { - tcg_out_insn(s, RX, LA, a0, a1, a2, 0); - } - break; - case INDEX_op_sub_i32: - a0 = args[0], a1 = args[1], a2 = (int32_t)args[2]; - if (const_args[2]) { - a2 = -a2; - goto do_addi_32; - } - tcg_out_insn(s, RR, SR, args[0], args[2]); - break; - - case INDEX_op_and_i32: - if (const_args[2]) { - tgen_andi(s, TCG_TYPE_I32, args[0], args[2]); - } else { - tcg_out_insn(s, RR, NR, args[0], args[2]); - } - break; - case INDEX_op_or_i32: - if (const_args[2]) { - tgen64_ori(s, args[0], args[2] & 0xffffffff); - } else { - tcg_out_insn(s, RR, OR, args[0], args[2]); - } - break; - case INDEX_op_xor_i32: - if (const_args[2]) { - tgen64_xori(s, args[0], args[2] & 0xffffffff); - } else { - tcg_out_insn(s, RR, XR, args[0], args[2]); - } - break; - - case INDEX_op_neg_i32: - tcg_out_insn(s, RR, LCR, args[0], args[1]); - break; - - case INDEX_op_mul_i32: - if (const_args[2]) { - if ((int32_t)args[2] == (int16_t)args[2]) { - tcg_out_insn(s, RI, MHI, args[0], args[2]); - } else { - tcg_out_insn(s, RIL, MSFI, args[0], args[2]); - } - } else { - tcg_out_insn(s, RRE, MSR, args[0], args[2]); - } - break; - - case INDEX_op_div2_i32: - tcg_out_insn(s, RR, DR, TCG_REG_R2, args[4]); - break; - case INDEX_op_divu2_i32: - tcg_out_insn(s, RRE, DLR, TCG_REG_R2, args[4]); - break; - - case INDEX_op_shl_i32: - op = RS_SLL; - do_shift32: - if (const_args[2]) { - tcg_out_sh32(s, op, args[0], TCG_REG_NONE, args[2]); - } else { - tcg_out_sh32(s, op, args[0], args[2], 0); - } - break; - case INDEX_op_shr_i32: - op = RS_SRL; - goto do_shift32; - case INDEX_op_sar_i32: - op = RS_SRA; - goto do_shift32; - - case INDEX_op_rotl_i32: - /* ??? Using tcg_out_sh64 here for the format; it is a 32-bit rol. */ - if (const_args[2]) { - tcg_out_sh64(s, RSY_RLL, args[0], args[1], TCG_REG_NONE, args[2]); - } else { - tcg_out_sh64(s, RSY_RLL, args[0], args[1], args[2], 0); - } - break; - case INDEX_op_rotr_i32: - if (const_args[2]) { - tcg_out_sh64(s, RSY_RLL, args[0], args[1], - TCG_REG_NONE, (32 - args[2]) & 31); - } else { - tcg_out_insn(s, RR, LCR, TCG_TMP0, args[2]); - tcg_out_sh64(s, RSY_RLL, args[0], args[1], TCG_TMP0, 0); - } - break; - - case INDEX_op_ext8s_i32: - tgen_ext8s(s, TCG_TYPE_I32, args[0], args[1]); - break; - case INDEX_op_ext16s_i32: - tgen_ext16s(s, TCG_TYPE_I32, args[0], args[1]); - break; - case INDEX_op_ext8u_i32: - tgen_ext8u(s, TCG_TYPE_I32, args[0], args[1]); - break; - case INDEX_op_ext16u_i32: - tgen_ext16u(s, TCG_TYPE_I32, args[0], args[1]); - break; - - OP_32_64(bswap16): - /* The TCG bswap definition requires bits 0-47 already be zero. - Thus we don't need the G-type insns to implement bswap16_i64. */ - tcg_out_insn(s, RRE, LRVR, args[0], args[1]); - tcg_out_sh32(s, RS_SRL, args[0], TCG_REG_NONE, 16); - break; - OP_32_64(bswap32): - tcg_out_insn(s, RRE, LRVR, args[0], args[1]); - break; - - case INDEX_op_add2_i32: - if (const_args[4]) { - tcg_out_insn(s, RIL, ALFI, args[0], args[4]); - } else { - tcg_out_insn(s, RR, ALR, args[0], args[4]); - } - tcg_out_insn(s, RRE, ALCR, args[1], args[5]); - break; - case INDEX_op_sub2_i32: - if (const_args[4]) { - tcg_out_insn(s, RIL, SLFI, args[0], args[4]); - } else { - tcg_out_insn(s, RR, SLR, args[0], args[4]); - } - tcg_out_insn(s, RRE, SLBR, args[1], args[5]); - break; - - case INDEX_op_br: - tgen_branch(s, S390_CC_ALWAYS, arg_label(args[0])); - break; - - case INDEX_op_brcond_i32: - tgen_brcond(s, TCG_TYPE_I32, args[2], args[0], - args[1], const_args[1], arg_label(args[3])); - break; - case INDEX_op_setcond_i32: - tgen_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], - args[2], const_args[2]); - break; - case INDEX_op_movcond_i32: - tgen_movcond(s, TCG_TYPE_I32, args[5], args[0], args[1], - args[2], const_args[2], args[3]); - break; - - case INDEX_op_qemu_ld_i32: - /* ??? Technically we can use a non-extending instruction. */ - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, args[0], args[1], args[2]); - break; - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, args[0], args[1], args[2]); - break; - - case INDEX_op_ld16s_i64: - tcg_out_mem(s, 0, RXY_LGH, args[0], args[1], TCG_REG_NONE, args[2]); - break; - case INDEX_op_ld32u_i64: - tcg_out_mem(s, 0, RXY_LLGF, args[0], args[1], TCG_REG_NONE, args[2]); - break; - case INDEX_op_ld32s_i64: - tcg_out_mem(s, 0, RXY_LGF, args[0], args[1], TCG_REG_NONE, args[2]); - break; - case INDEX_op_ld_i64: - tcg_out_ld(s, TCG_TYPE_I64, args[0], args[1], args[2]); - break; - - case INDEX_op_st32_i64: - tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); - break; - case INDEX_op_st_i64: - tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]); - break; - - case INDEX_op_add_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - do_addi_64: - if (a0 == a1) { - if (a2 == (int16_t)a2) { - tcg_out_insn(s, RI, AGHI, a0, a2); - break; - } - if (facilities & FACILITY_EXT_IMM) { - if (a2 == (int32_t)a2) { - tcg_out_insn(s, RIL, AGFI, a0, a2); - break; - } else if (a2 == (uint32_t)a2) { - tcg_out_insn(s, RIL, ALGFI, a0, a2); - break; - } else if (-a2 == (uint32_t)-a2) { - tcg_out_insn(s, RIL, SLGFI, a0, -a2); - break; - } - } - } - tcg_out_mem(s, RX_LA, RXY_LAY, a0, a1, TCG_REG_NONE, a2); - } else if (a0 == a1) { - tcg_out_insn(s, RRE, AGR, a0, a2); - } else { - tcg_out_insn(s, RX, LA, a0, a1, a2, 0); - } - break; - case INDEX_op_sub_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - a2 = -a2; - goto do_addi_64; - } else { - tcg_out_insn(s, RRE, SGR, args[0], args[2]); - } - break; - - case INDEX_op_and_i64: - if (const_args[2]) { - tgen_andi(s, TCG_TYPE_I64, args[0], args[2]); - } else { - tcg_out_insn(s, RRE, NGR, args[0], args[2]); - } - break; - case INDEX_op_or_i64: - if (const_args[2]) { - tgen64_ori(s, args[0], args[2]); - } else { - tcg_out_insn(s, RRE, OGR, args[0], args[2]); - } - break; - case INDEX_op_xor_i64: - if (const_args[2]) { - tgen64_xori(s, args[0], args[2]); - } else { - tcg_out_insn(s, RRE, XGR, args[0], args[2]); - } - break; - - case INDEX_op_neg_i64: - tcg_out_insn(s, RRE, LCGR, args[0], args[1]); - break; - case INDEX_op_bswap64_i64: - tcg_out_insn(s, RRE, LRVGR, args[0], args[1]); - break; - - case INDEX_op_mul_i64: - if (const_args[2]) { - if (args[2] == (int16_t)args[2]) { - tcg_out_insn(s, RI, MGHI, args[0], args[2]); - } else { - tcg_out_insn(s, RIL, MSGFI, args[0], args[2]); - } - } else { - tcg_out_insn(s, RRE, MSGR, args[0], args[2]); - } - break; - - case INDEX_op_div2_i64: - /* ??? We get an unnecessary sign-extension of the dividend - into R3 with this definition, but as we do in fact always - produce both quotient and remainder using INDEX_op_div_i64 - instead requires jumping through even more hoops. */ - tcg_out_insn(s, RRE, DSGR, TCG_REG_R2, args[4]); - break; - case INDEX_op_divu2_i64: - tcg_out_insn(s, RRE, DLGR, TCG_REG_R2, args[4]); - break; - case INDEX_op_mulu2_i64: - tcg_out_insn(s, RRE, MLGR, TCG_REG_R2, args[3]); - break; - - case INDEX_op_shl_i64: - op = RSY_SLLG; - do_shift64: - if (const_args[2]) { - tcg_out_sh64(s, op, args[0], args[1], TCG_REG_NONE, args[2]); - } else { - tcg_out_sh64(s, op, args[0], args[1], args[2], 0); - } - break; - case INDEX_op_shr_i64: - op = RSY_SRLG; - goto do_shift64; - case INDEX_op_sar_i64: - op = RSY_SRAG; - goto do_shift64; - - case INDEX_op_rotl_i64: - if (const_args[2]) { - tcg_out_sh64(s, RSY_RLLG, args[0], args[1], - TCG_REG_NONE, args[2]); - } else { - tcg_out_sh64(s, RSY_RLLG, args[0], args[1], args[2], 0); - } - break; - case INDEX_op_rotr_i64: - if (const_args[2]) { - tcg_out_sh64(s, RSY_RLLG, args[0], args[1], - TCG_REG_NONE, (64 - args[2]) & 63); - } else { - /* We can use the smaller 32-bit negate because only the - low 6 bits are examined for the rotate. */ - tcg_out_insn(s, RR, LCR, TCG_TMP0, args[2]); - tcg_out_sh64(s, RSY_RLLG, args[0], args[1], TCG_TMP0, 0); - } - break; - - case INDEX_op_ext8s_i64: - tgen_ext8s(s, TCG_TYPE_I64, args[0], args[1]); - break; - case INDEX_op_ext16s_i64: - tgen_ext16s(s, TCG_TYPE_I64, args[0], args[1]); - break; - case INDEX_op_ext32s_i64: - tgen_ext32s(s, args[0], args[1]); - break; - case INDEX_op_ext8u_i64: - tgen_ext8u(s, TCG_TYPE_I64, args[0], args[1]); - break; - case INDEX_op_ext16u_i64: - tgen_ext16u(s, TCG_TYPE_I64, args[0], args[1]); - break; - case INDEX_op_ext32u_i64: - tgen_ext32u(s, args[0], args[1]); - break; - - case INDEX_op_add2_i64: - if (const_args[4]) { - if ((int64_t)args[4] >= 0) { - tcg_out_insn(s, RIL, ALGFI, args[0], args[4]); - } else { - tcg_out_insn(s, RIL, SLGFI, args[0], -args[4]); - } - } else { - tcg_out_insn(s, RRE, ALGR, args[0], args[4]); - } - tcg_out_insn(s, RRE, ALCGR, args[1], args[5]); - break; - case INDEX_op_sub2_i64: - if (const_args[4]) { - if ((int64_t)args[4] >= 0) { - tcg_out_insn(s, RIL, SLGFI, args[0], args[4]); - } else { - tcg_out_insn(s, RIL, ALGFI, args[0], -args[4]); - } - } else { - tcg_out_insn(s, RRE, SLGR, args[0], args[4]); - } - tcg_out_insn(s, RRE, SLBGR, args[1], args[5]); - break; - - case INDEX_op_brcond_i64: - tgen_brcond(s, TCG_TYPE_I64, args[2], args[0], - args[1], const_args[1], arg_label(args[3])); - break; - case INDEX_op_setcond_i64: - tgen_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1], - args[2], const_args[2]); - break; - case INDEX_op_movcond_i64: - tgen_movcond(s, TCG_TYPE_I64, args[5], args[0], args[1], - args[2], const_args[2], args[3]); - break; - - OP_32_64(deposit): - tgen_deposit(s, args[0], args[2], args[3], args[4]); - break; - - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_mov_i64: - case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ - case INDEX_op_movi_i64: - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - default: - tcg_abort(); - } -} - -static const TCGTargetOpDef s390_op_defs[] = { - { INDEX_op_exit_tb, { } }, - { INDEX_op_goto_tb, { } }, - { INDEX_op_br, { } }, - - { INDEX_op_ld8u_i32, { "r", "r" } }, - { INDEX_op_ld8s_i32, { "r", "r" } }, - { INDEX_op_ld16u_i32, { "r", "r" } }, - { INDEX_op_ld16s_i32, { "r", "r" } }, - { INDEX_op_ld_i32, { "r", "r" } }, - { INDEX_op_st8_i32, { "r", "r" } }, - { INDEX_op_st16_i32, { "r", "r" } }, - { INDEX_op_st_i32, { "r", "r" } }, - - { INDEX_op_add_i32, { "r", "r", "ri" } }, - { INDEX_op_sub_i32, { "r", "0", "ri" } }, - { INDEX_op_mul_i32, { "r", "0", "rK" } }, - - { INDEX_op_div2_i32, { "b", "a", "0", "1", "r" } }, - { INDEX_op_divu2_i32, { "b", "a", "0", "1", "r" } }, - - { INDEX_op_and_i32, { "r", "0", "ri" } }, - { INDEX_op_or_i32, { "r", "0", "rO" } }, - { INDEX_op_xor_i32, { "r", "0", "rX" } }, - - { INDEX_op_neg_i32, { "r", "r" } }, - - { INDEX_op_shl_i32, { "r", "0", "Ri" } }, - { INDEX_op_shr_i32, { "r", "0", "Ri" } }, - { INDEX_op_sar_i32, { "r", "0", "Ri" } }, - - { INDEX_op_rotl_i32, { "r", "r", "Ri" } }, - { INDEX_op_rotr_i32, { "r", "r", "Ri" } }, - - { INDEX_op_ext8s_i32, { "r", "r" } }, - { INDEX_op_ext8u_i32, { "r", "r" } }, - { INDEX_op_ext16s_i32, { "r", "r" } }, - { INDEX_op_ext16u_i32, { "r", "r" } }, - - { INDEX_op_bswap16_i32, { "r", "r" } }, - { INDEX_op_bswap32_i32, { "r", "r" } }, - - { INDEX_op_add2_i32, { "r", "r", "0", "1", "rA", "r" } }, - { INDEX_op_sub2_i32, { "r", "r", "0", "1", "rA", "r" } }, - - { INDEX_op_brcond_i32, { "r", "rC" } }, - { INDEX_op_setcond_i32, { "r", "r", "rC" } }, - { INDEX_op_movcond_i32, { "r", "r", "rC", "r", "0" } }, - { INDEX_op_deposit_i32, { "r", "0", "r" } }, - - { INDEX_op_qemu_ld_i32, { "r", "L" } }, - { INDEX_op_qemu_ld_i64, { "r", "L" } }, - { INDEX_op_qemu_st_i32, { "L", "L" } }, - { INDEX_op_qemu_st_i64, { "L", "L" } }, - - { INDEX_op_ld8u_i64, { "r", "r" } }, - { INDEX_op_ld8s_i64, { "r", "r" } }, - { INDEX_op_ld16u_i64, { "r", "r" } }, - { INDEX_op_ld16s_i64, { "r", "r" } }, - { INDEX_op_ld32u_i64, { "r", "r" } }, - { INDEX_op_ld32s_i64, { "r", "r" } }, - { INDEX_op_ld_i64, { "r", "r" } }, - - { INDEX_op_st8_i64, { "r", "r" } }, - { INDEX_op_st16_i64, { "r", "r" } }, - { INDEX_op_st32_i64, { "r", "r" } }, - { INDEX_op_st_i64, { "r", "r" } }, - - { INDEX_op_add_i64, { "r", "r", "ri" } }, - { INDEX_op_sub_i64, { "r", "0", "ri" } }, - { INDEX_op_mul_i64, { "r", "0", "rK" } }, - - { INDEX_op_div2_i64, { "b", "a", "0", "1", "r" } }, - { INDEX_op_divu2_i64, { "b", "a", "0", "1", "r" } }, - { INDEX_op_mulu2_i64, { "b", "a", "0", "r" } }, - - { INDEX_op_and_i64, { "r", "0", "ri" } }, - { INDEX_op_or_i64, { "r", "0", "rO" } }, - { INDEX_op_xor_i64, { "r", "0", "rX" } }, - - { INDEX_op_neg_i64, { "r", "r" } }, - - { INDEX_op_shl_i64, { "r", "r", "Ri" } }, - { INDEX_op_shr_i64, { "r", "r", "Ri" } }, - { INDEX_op_sar_i64, { "r", "r", "Ri" } }, - - { INDEX_op_rotl_i64, { "r", "r", "Ri" } }, - { INDEX_op_rotr_i64, { "r", "r", "Ri" } }, - - { INDEX_op_ext8s_i64, { "r", "r" } }, - { INDEX_op_ext8u_i64, { "r", "r" } }, - { INDEX_op_ext16s_i64, { "r", "r" } }, - { INDEX_op_ext16u_i64, { "r", "r" } }, - { INDEX_op_ext32s_i64, { "r", "r" } }, - { INDEX_op_ext32u_i64, { "r", "r" } }, - - { INDEX_op_bswap16_i64, { "r", "r" } }, - { INDEX_op_bswap32_i64, { "r", "r" } }, - { INDEX_op_bswap64_i64, { "r", "r" } }, - - { INDEX_op_add2_i64, { "r", "r", "0", "1", "rA", "r" } }, - { INDEX_op_sub2_i64, { "r", "r", "0", "1", "rA", "r" } }, - - { INDEX_op_brcond_i64, { "r", "rC" } }, - { INDEX_op_setcond_i64, { "r", "r", "rC" } }, - { INDEX_op_movcond_i64, { "r", "r", "rC", "r", "0" } }, - { INDEX_op_deposit_i64, { "r", "0", "r" } }, - - { -1 }, -}; - -static void query_facilities(void) -{ - unsigned long hwcap = qemu_getauxval(AT_HWCAP); - - /* Is STORE FACILITY LIST EXTENDED available? Honestly, I believe this - is present on all 64-bit systems, but let's check for it anyway. */ - if (hwcap & HWCAP_S390_STFLE) { - register int r0 __asm__("0"); - register void *r1 __asm__("1"); - - /* stfle 0(%r1) */ - r1 = &facilities; - asm volatile(".word 0xb2b0,0x1000" - : "=r"(r0) : "0"(0), "r"(r1) : "memory", "cc"); - } -} - -static void tcg_target_init(TCGContext *s) -{ - query_facilities(); - - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff); - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffff); - - tcg_regset_clear(tcg_target_call_clobber_regs); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R0); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R1); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R2); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R3); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R4); - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R5); - /* The r6 register is technically call-saved, but it's also a parameter - register, so it can get killed by setup for the qemu_st helper. */ - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R6); - /* The return register can be considered call-clobbered. */ - tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R14); - - tcg_regset_clear(s->reserved_regs); - tcg_regset_set_reg(s->reserved_regs, TCG_TMP0); - /* XXX many insns can't be used with R0, so we better avoid it for now */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); - - tcg_add_target_add_op_defs(s390_op_defs); -} - -#define FRAME_SIZE ((int)(TCG_TARGET_CALL_STACK_OFFSET \ - + TCG_STATIC_CALL_ARGS_SIZE \ - + CPU_TEMP_BUF_NLONGS * sizeof(long))) - -static void tcg_target_qemu_prologue(TCGContext *s) -{ - /* stmg %r6,%r15,48(%r15) (save registers) */ - tcg_out_insn(s, RXY, STMG, TCG_REG_R6, TCG_REG_R15, TCG_REG_R15, 48); - - /* aghi %r15,-frame_size */ - tcg_out_insn(s, RI, AGHI, TCG_REG_R15, -FRAME_SIZE); - - tcg_set_frame(s, TCG_REG_CALL_STACK, - TCG_STATIC_CALL_ARGS_SIZE + TCG_TARGET_CALL_STACK_OFFSET, - CPU_TEMP_BUF_NLONGS * sizeof(long)); - - if (GUEST_BASE >= 0x80000) { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, GUEST_BASE); - tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); - } - - tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); - /* br %r3 (go to TB) */ - tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, tcg_target_call_iarg_regs[1]); - - tb_ret_addr = s->code_ptr; - - /* lmg %r6,%r15,fs+48(%r15) (restore registers) */ - tcg_out_insn(s, RXY, LMG, TCG_REG_R6, TCG_REG_R15, TCG_REG_R15, - FRAME_SIZE + 48); - - /* br %r14 (return) */ - tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_REG_R14); -} - -typedef struct { - DebugFrameHeader h; - uint8_t fde_def_cfa[4]; - uint8_t fde_reg_ofs[18]; -} DebugFrame; - -/* We're expecting a 2 byte uleb128 encoded value. */ -QEMU_BUILD_BUG_ON(FRAME_SIZE >= (1 << 14)); - -#define ELF_HOST_MACHINE EM_S390 - -static const DebugFrame debug_frame = { - .h.cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ - .h.cie.id = -1, - .h.cie.version = 1, - .h.cie.code_align = 1, - .h.cie.data_align = 8, /* sleb128 8 */ - .h.cie.return_column = TCG_REG_R14, - - /* Total FDE size does not include the "len" member. */ - .h.fde.len = sizeof(DebugFrame) - offsetof(DebugFrame, h.fde.cie_offset), - - .fde_def_cfa = { - 12, TCG_REG_CALL_STACK, /* DW_CFA_def_cfa %r15, ... */ - (FRAME_SIZE & 0x7f) | 0x80, /* ... uleb128 FRAME_SIZE */ - (FRAME_SIZE >> 7) - }, - .fde_reg_ofs = { - 0x86, 6, /* DW_CFA_offset, %r6, 48 */ - 0x87, 7, /* DW_CFA_offset, %r7, 56 */ - 0x88, 8, /* DW_CFA_offset, %r8, 64 */ - 0x89, 9, /* DW_CFA_offset, %r92, 72 */ - 0x8a, 10, /* DW_CFA_offset, %r10, 80 */ - 0x8b, 11, /* DW_CFA_offset, %r11, 88 */ - 0x8c, 12, /* DW_CFA_offset, %r12, 96 */ - 0x8d, 13, /* DW_CFA_offset, %r13, 104 */ - 0x8e, 14, /* DW_CFA_offset, %r14, 112 */ - } -}; - -void tcg_register_jit(void *buf, size_t buf_size) -{ - tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); -} diff --git a/qemu/tcg/s390/tcg-target.h b/qemu/tcg/s390/tcg-target.h index 91576d594..d9dc03873 100644 --- a/qemu/tcg/s390/tcg-target.h +++ b/qemu/tcg/s390/tcg-target.h @@ -72,7 +72,8 @@ typedef enum TCGReg { #define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_muluh_i32 0 #define TCG_TARGET_HAS_mulsh_i32 0 -#define TCG_TARGET_HAS_trunc_shr_i32 0 +#define TCG_TARGET_HAS_extrl_i64_i32 0 +#define TCG_TARGET_HAS_extrh_i64_i32 0 #define TCG_TARGET_HAS_div2_i64 1 #define TCG_TARGET_HAS_rot_i64 1 diff --git a/qemu/tcg/s390/tcg-target.inc.c b/qemu/tcg/s390/tcg-target.inc.c new file mode 100644 index 000000000..580553239 --- /dev/null +++ b/qemu/tcg/s390/tcg-target.inc.c @@ -0,0 +1,2409 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2009 Ulrich Hecht + * Copyright (c) 2009 Alexander Graf + * Copyright (c) 2010 Richard Henderson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "tcg-be-ldst.h" + +/* We only support generating code for 64-bit mode. */ +#if TCG_TARGET_REG_BITS != 64 +#error "unsupported code generation mode" +#endif + +#include "elf.h" + +/* ??? The translation blocks produced by TCG are generally small enough to + be entirely reachable with a 16-bit displacement. Leaving the option for + a 32-bit displacement here Just In Case. */ +#define USE_LONG_BRANCHES 0 + +#define TCG_CT_CONST_MULI 0x100 +#define TCG_CT_CONST_ORI 0x200 +#define TCG_CT_CONST_XORI 0x400 +#define TCG_CT_CONST_CMPI 0x800 +#define TCG_CT_CONST_ADLI 0x1000 + +/* Several places within the instruction set 0 means "no register" + rather than TCG_REG_R0. */ +#define TCG_REG_NONE 0 + +/* A scratch register that may be be used throughout the backend. */ +#define TCG_TMP0 TCG_REG_R14 + +#ifndef CONFIG_SOFTMMU +#define TCG_GUEST_BASE_REG TCG_REG_R13 +#endif + +/* All of the following instructions are prefixed with their instruction + format, and are defined as 8- or 16-bit quantities, even when the two + halves of the 16-bit quantity may appear 32 bits apart in the insn. + This makes it easy to copy the values from the tables in Appendix B. */ +typedef enum S390Opcode { + RIL_AFI = 0xc209, + RIL_AGFI = 0xc208, + RIL_ALFI = 0xc20b, + RIL_ALGFI = 0xc20a, + RIL_BRASL = 0xc005, + RIL_BRCL = 0xc004, + RIL_CFI = 0xc20d, + RIL_CGFI = 0xc20c, + RIL_CLFI = 0xc20f, + RIL_CLGFI = 0xc20e, + RIL_IIHF = 0xc008, + RIL_IILF = 0xc009, + RIL_LARL = 0xc000, + RIL_LGFI = 0xc001, + RIL_LGRL = 0xc408, + RIL_LLIHF = 0xc00e, + RIL_LLILF = 0xc00f, + RIL_LRL = 0xc40d, + RIL_MSFI = 0xc201, + RIL_MSGFI = 0xc200, + RIL_NIHF = 0xc00a, + RIL_NILF = 0xc00b, + RIL_OIHF = 0xc00c, + RIL_OILF = 0xc00d, + RIL_SLFI = 0xc205, + RIL_SLGFI = 0xc204, + RIL_XIHF = 0xc006, + RIL_XILF = 0xc007, + + RI_AGHI = 0xa70b, + RI_AHI = 0xa70a, + RI_BRC = 0xa704, + RI_IIHH = 0xa500, + RI_IIHL = 0xa501, + RI_IILH = 0xa502, + RI_IILL = 0xa503, + RI_LGHI = 0xa709, + RI_LLIHH = 0xa50c, + RI_LLIHL = 0xa50d, + RI_LLILH = 0xa50e, + RI_LLILL = 0xa50f, + RI_MGHI = 0xa70d, + RI_MHI = 0xa70c, + RI_NIHH = 0xa504, + RI_NIHL = 0xa505, + RI_NILH = 0xa506, + RI_NILL = 0xa507, + RI_OIHH = 0xa508, + RI_OIHL = 0xa509, + RI_OILH = 0xa50a, + RI_OILL = 0xa50b, + + RIE_CGIJ = 0xec7c, + RIE_CGRJ = 0xec64, + RIE_CIJ = 0xec7e, + RIE_CLGRJ = 0xec65, + RIE_CLIJ = 0xec7f, + RIE_CLGIJ = 0xec7d, + RIE_CLRJ = 0xec77, + RIE_CRJ = 0xec76, + RIE_RISBG = 0xec55, + + RRE_AGR = 0xb908, + RRE_ALGR = 0xb90a, + RRE_ALCR = 0xb998, + RRE_ALCGR = 0xb988, + RRE_CGR = 0xb920, + RRE_CLGR = 0xb921, + RRE_DLGR = 0xb987, + RRE_DLR = 0xb997, + RRE_DSGFR = 0xb91d, + RRE_DSGR = 0xb90d, + RRE_LGBR = 0xb906, + RRE_LCGR = 0xb903, + RRE_LGFR = 0xb914, + RRE_LGHR = 0xb907, + RRE_LGR = 0xb904, + RRE_LLGCR = 0xb984, + RRE_LLGFR = 0xb916, + RRE_LLGHR = 0xb985, + RRE_LRVR = 0xb91f, + RRE_LRVGR = 0xb90f, + RRE_LTGR = 0xb902, + RRE_MLGR = 0xb986, + RRE_MSGR = 0xb90c, + RRE_MSR = 0xb252, + RRE_NGR = 0xb980, + RRE_OGR = 0xb981, + RRE_SGR = 0xb909, + RRE_SLGR = 0xb90b, + RRE_SLBR = 0xb999, + RRE_SLBGR = 0xb989, + RRE_XGR = 0xb982, + + RRF_LOCR = 0xb9f2, + RRF_LOCGR = 0xb9e2, + + RR_AR = 0x1a, + RR_ALR = 0x1e, + RR_BASR = 0x0d, + RR_BCR = 0x07, + RR_CLR = 0x15, + RR_CR = 0x19, + RR_DR = 0x1d, + RR_LCR = 0x13, + RR_LR = 0x18, + RR_LTR = 0x12, + RR_NR = 0x14, + RR_OR = 0x16, + RR_SR = 0x1b, + RR_SLR = 0x1f, + RR_XR = 0x17, + + RSY_RLL = 0xeb1d, + RSY_RLLG = 0xeb1c, + RSY_SLLG = 0xeb0d, + RSY_SRAG = 0xeb0a, + RSY_SRLG = 0xeb0c, + + RS_SLL = 0x89, + RS_SRA = 0x8a, + RS_SRL = 0x88, + + RXY_AG = 0xe308, + RXY_AY = 0xe35a, + RXY_CG = 0xe320, + RXY_CY = 0xe359, + RXY_LAY = 0xe371, + RXY_LB = 0xe376, + RXY_LG = 0xe304, + RXY_LGB = 0xe377, + RXY_LGF = 0xe314, + RXY_LGH = 0xe315, + RXY_LHY = 0xe378, + RXY_LLGC = 0xe390, + RXY_LLGF = 0xe316, + RXY_LLGH = 0xe391, + RXY_LMG = 0xeb04, + RXY_LRV = 0xe31e, + RXY_LRVG = 0xe30f, + RXY_LRVH = 0xe31f, + RXY_LY = 0xe358, + RXY_STCY = 0xe372, + RXY_STG = 0xe324, + RXY_STHY = 0xe370, + RXY_STMG = 0xeb24, + RXY_STRV = 0xe33e, + RXY_STRVG = 0xe32f, + RXY_STRVH = 0xe33f, + RXY_STY = 0xe350, + + RX_A = 0x5a, + RX_C = 0x59, + RX_L = 0x58, + RX_LA = 0x41, + RX_LH = 0x48, + RX_ST = 0x50, + RX_STC = 0x42, + RX_STH = 0x40, +} S390Opcode; + +#ifdef CONFIG_DEBUG_TCG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", + "%r8", "%r9", "%r10" "%r11" "%r12" "%r13" "%r14" "%r15" +}; +#endif + +/* Since R6 is a potential argument register, choose it last of the + call-saved registers. Likewise prefer the call-clobbered registers + in reverse order to maximize the chance of avoiding the arguments. */ +static const int tcg_target_reg_alloc_order[] = { + /* Call saved registers. */ + TCG_REG_R13, + TCG_REG_R12, + TCG_REG_R11, + TCG_REG_R10, + TCG_REG_R9, + TCG_REG_R8, + TCG_REG_R7, + TCG_REG_R6, + /* Call clobbered registers. */ + TCG_REG_R14, + TCG_REG_R0, + TCG_REG_R1, + /* Argument registers, in reverse order of allocation. */ + TCG_REG_R5, + TCG_REG_R4, + TCG_REG_R3, + TCG_REG_R2, +}; + +static const int tcg_target_call_iarg_regs[] = { + TCG_REG_R2, + TCG_REG_R3, + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, +}; + +static const int tcg_target_call_oarg_regs[] = { + TCG_REG_R2, +}; + +#define S390_CC_EQ 8 +#define S390_CC_LT 4 +#define S390_CC_GT 2 +#define S390_CC_OV 1 +#define S390_CC_NE (S390_CC_LT | S390_CC_GT) +#define S390_CC_LE (S390_CC_LT | S390_CC_EQ) +#define S390_CC_GE (S390_CC_GT | S390_CC_EQ) +#define S390_CC_NEVER 0 +#define S390_CC_ALWAYS 15 + +/* Condition codes that result from a COMPARE and COMPARE LOGICAL. */ +static const uint8_t tcg_cond_to_s390_cond[] = { + [TCG_COND_EQ] = S390_CC_EQ, + [TCG_COND_NE] = S390_CC_NE, + [TCG_COND_LT] = S390_CC_LT, + [TCG_COND_LE] = S390_CC_LE, + [TCG_COND_GT] = S390_CC_GT, + [TCG_COND_GE] = S390_CC_GE, + [TCG_COND_LTU] = S390_CC_LT, + [TCG_COND_LEU] = S390_CC_LE, + [TCG_COND_GTU] = S390_CC_GT, + [TCG_COND_GEU] = S390_CC_GE, +}; + +/* Condition codes that result from a LOAD AND TEST. Here, we have no + unsigned instruction variation, however since the test is vs zero we + can re-map the outcomes appropriately. */ +static const uint8_t tcg_cond_to_ltr_cond[] = { + [TCG_COND_EQ] = S390_CC_EQ, + [TCG_COND_NE] = S390_CC_NE, + [TCG_COND_LT] = S390_CC_LT, + [TCG_COND_LE] = S390_CC_LE, + [TCG_COND_GT] = S390_CC_GT, + [TCG_COND_GE] = S390_CC_GE, + [TCG_COND_LTU] = S390_CC_NEVER, + [TCG_COND_LEU] = S390_CC_EQ, + [TCG_COND_GTU] = S390_CC_NE, + [TCG_COND_GEU] = S390_CC_ALWAYS, +}; + +#ifdef CONFIG_SOFTMMU +static void * const qemu_ld_helpers[16] = { + [MO_UB] = helper_ret_ldub_mmu, + [MO_SB] = helper_ret_ldsb_mmu, + [MO_LEUW] = helper_le_lduw_mmu, + [MO_LESW] = helper_le_ldsw_mmu, + [MO_LEUL] = helper_le_ldul_mmu, + [MO_LESL] = helper_le_ldsl_mmu, + [MO_LEQ] = helper_le_ldq_mmu, + [MO_BEUW] = helper_be_lduw_mmu, + [MO_BESW] = helper_be_ldsw_mmu, + [MO_BEUL] = helper_be_ldul_mmu, + [MO_BESL] = helper_be_ldsl_mmu, + [MO_BEQ] = helper_be_ldq_mmu, +}; + +static void * const qemu_st_helpers[16] = { + [MO_UB] = helper_ret_stb_mmu, + [MO_LEUW] = helper_le_stw_mmu, + [MO_LEUL] = helper_le_stl_mmu, + [MO_LEQ] = helper_le_stq_mmu, + [MO_BEUW] = helper_be_stw_mmu, + [MO_BEUL] = helper_be_stl_mmu, + [MO_BEQ] = helper_be_stq_mmu, +}; +#endif + +static tcg_insn_unit *tb_ret_addr; + +/* A list of relevant facilities used by this translator. Some of these + are required for proper operation, and these are checked at startup. */ + +#define FACILITY_ZARCH_ACTIVE (1ULL << (63 - 2)) +#define FACILITY_LONG_DISP (1ULL << (63 - 18)) +#define FACILITY_EXT_IMM (1ULL << (63 - 21)) +#define FACILITY_GEN_INST_EXT (1ULL << (63 - 34)) +#define FACILITY_LOAD_ON_COND (1ULL << (63 - 45)) + +static uint64_t facilities; + +static void patch_reloc(tcg_insn_unit *code_ptr, int type, + intptr_t value, intptr_t addend) +{ + intptr_t pcrel2 = (tcg_insn_unit *)value - (code_ptr - 1); + tcg_debug_assert(addend == -2); + + switch (type) { + case R_390_PC16DBL: + tcg_debug_assert(pcrel2 == (int16_t)pcrel2); + tcg_patch16(code_ptr, pcrel2); + break; + case R_390_PC32DBL: + tcg_debug_assert(pcrel2 == (int32_t)pcrel2); + tcg_patch32(code_ptr, pcrel2); + break; + default: + tcg_abort(); + break; + } +} + +/* parse target specific constraints */ +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +{ + const char *ct_str = *pct_str; + + switch (ct_str[0]) { + case 'r': /* all registers */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffff); + break; + case 'R': /* not R0 */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); + break; + case 'L': /* qemu_ld/st constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffff); + tcg_regset_reset_reg (ct->u.regs, TCG_REG_R2); + tcg_regset_reset_reg (ct->u.regs, TCG_REG_R3); + tcg_regset_reset_reg (ct->u.regs, TCG_REG_R4); + break; + case 'a': /* force R2 for division */ + ct->ct |= TCG_CT_REG; + tcg_regset_clear(ct->u.regs); + tcg_regset_set_reg(ct->u.regs, TCG_REG_R2); + break; + case 'b': /* force R3 for division */ + ct->ct |= TCG_CT_REG; + tcg_regset_clear(ct->u.regs); + tcg_regset_set_reg(ct->u.regs, TCG_REG_R3); + break; + case 'A': + ct->ct |= TCG_CT_CONST_ADLI; + break; + case 'K': + ct->ct |= TCG_CT_CONST_MULI; + break; + case 'O': + ct->ct |= TCG_CT_CONST_ORI; + break; + case 'X': + ct->ct |= TCG_CT_CONST_XORI; + break; + case 'C': + ct->ct |= TCG_CT_CONST_CMPI; + break; + default: + return -1; + } + ct_str++; + *pct_str = ct_str; + + return 0; +} + +/* Immediates to be used with logical OR. This is an optimization only, + since a full 64-bit immediate OR can always be performed with 4 sequential + OI[LH][LH] instructions. What we're looking for is immediates that we + can load efficiently, and the immediate load plus the reg-reg OR is + smaller than the sequential OI's. */ + +static int tcg_match_ori(TCGType type, tcg_target_long val) +{ + if (facilities & FACILITY_EXT_IMM) { + if (type == TCG_TYPE_I32) { + /* All 32-bit ORs can be performed with 1 48-bit insn. */ + return 1; + } + } + + /* Look for negative values. These are best to load with LGHI. */ + if (val < 0) { + if (val == (int16_t)val) { + return 0; + } + if (facilities & FACILITY_EXT_IMM) { + if (val == (int32_t)val) { + return 0; + } + } + } + + return 1; +} + +/* Immediates to be used with logical XOR. This is almost, but not quite, + only an optimization. XOR with immediate is only supported with the + extended-immediate facility. That said, there are a few patterns for + which it is better to load the value into a register first. */ + +static int tcg_match_xori(TCGType type, tcg_target_long val) +{ + if ((facilities & FACILITY_EXT_IMM) == 0) { + return 0; + } + + if (type == TCG_TYPE_I32) { + /* All 32-bit XORs can be performed with 1 48-bit insn. */ + return 1; + } + + /* Look for negative values. These are best to load with LGHI. */ + if (val < 0 && val == (int32_t)val) { + return 0; + } + + return 1; +} + +/* Imediates to be used with comparisons. */ + +static int tcg_match_cmpi(TCGType type, tcg_target_long val) +{ + if (facilities & FACILITY_EXT_IMM) { + /* The COMPARE IMMEDIATE instruction is available. */ + if (type == TCG_TYPE_I32) { + /* We have a 32-bit immediate and can compare against anything. */ + return 1; + } else { + /* ??? We have no insight here into whether the comparison is + signed or unsigned. The COMPARE IMMEDIATE insn uses a 32-bit + signed immediate, and the COMPARE LOGICAL IMMEDIATE insn uses + a 32-bit unsigned immediate. If we were to use the (semi) + obvious "val == (int32_t)val" we would be enabling unsigned + comparisons vs very large numbers. The only solution is to + take the intersection of the ranges. */ + /* ??? Another possible solution is to simply lie and allow all + constants here and force the out-of-range values into a temp + register in tgen_cmp when we have knowledge of the actual + comparison code in use. */ + return val >= 0 && val <= 0x7fffffff; + } + } else { + /* Only the LOAD AND TEST instruction is available. */ + return val == 0; + } +} + +/* Immediates to be used with add2/sub2. */ + +static int tcg_match_add2i(TCGType type, tcg_target_long val) +{ + if (facilities & FACILITY_EXT_IMM) { + if (type == TCG_TYPE_I32) { + return 1; + } else if (val >= -0xffffffffll && val <= 0xffffffffll) { + return 1; + } + } + return 0; +} + +/* Test if a constant matches the constraint. */ +static int tcg_target_const_match(tcg_target_long val, TCGType type, + const TCGArgConstraint *arg_ct) +{ + int ct = arg_ct->ct; + + if (ct & TCG_CT_CONST) { + return 1; + } + + if (type == TCG_TYPE_I32) { + val = (int32_t)val; + } + + /* The following are mutually exclusive. */ + if (ct & TCG_CT_CONST_MULI) { + /* Immediates that may be used with multiply. If we have the + general-instruction-extensions, then we have MULTIPLY SINGLE + IMMEDIATE with a signed 32-bit, otherwise we have only + MULTIPLY HALFWORD IMMEDIATE, with a signed 16-bit. */ + if (facilities & FACILITY_GEN_INST_EXT) { + return val == (int32_t)val; + } else { + return val == (int16_t)val; + } + } else if (ct & TCG_CT_CONST_ADLI) { + return tcg_match_add2i(type, val); + } else if (ct & TCG_CT_CONST_ORI) { + return tcg_match_ori(type, val); + } else if (ct & TCG_CT_CONST_XORI) { + return tcg_match_xori(type, val); + } else if (ct & TCG_CT_CONST_CMPI) { + return tcg_match_cmpi(type, val); + } + + return 0; +} + +/* Emit instructions according to the given instruction format. */ + +static void tcg_out_insn_RR(TCGContext *s, S390Opcode op, TCGReg r1, TCGReg r2) +{ + tcg_out16(s, (op << 8) | (r1 << 4) | r2); +} + +static void tcg_out_insn_RRE(TCGContext *s, S390Opcode op, + TCGReg r1, TCGReg r2) +{ + tcg_out32(s, (op << 16) | (r1 << 4) | r2); +} + +static void tcg_out_insn_RRF(TCGContext *s, S390Opcode op, + TCGReg r1, TCGReg r2, int m3) +{ + tcg_out32(s, (op << 16) | (m3 << 12) | (r1 << 4) | r2); +} + +static void tcg_out_insn_RI(TCGContext *s, S390Opcode op, TCGReg r1, int i2) +{ + tcg_out32(s, (op << 16) | (r1 << 20) | (i2 & 0xffff)); +} + +static void tcg_out_insn_RIL(TCGContext *s, S390Opcode op, TCGReg r1, int i2) +{ + tcg_out16(s, op | (r1 << 4)); + tcg_out32(s, i2); +} + +static void tcg_out_insn_RS(TCGContext *s, S390Opcode op, TCGReg r1, + TCGReg b2, TCGReg r3, int disp) +{ + tcg_out32(s, (op << 24) | (r1 << 20) | (r3 << 16) | (b2 << 12) + | (disp & 0xfff)); +} + +static void tcg_out_insn_RSY(TCGContext *s, S390Opcode op, TCGReg r1, + TCGReg b2, TCGReg r3, int disp) +{ + tcg_out16(s, (op & 0xff00) | (r1 << 4) | r3); + tcg_out32(s, (op & 0xff) | (b2 << 28) + | ((disp & 0xfff) << 16) | ((disp & 0xff000) >> 4)); +} + +#define tcg_out_insn_RX tcg_out_insn_RS +#define tcg_out_insn_RXY tcg_out_insn_RSY + +/* Emit an opcode with "type-checking" of the format. */ +#define tcg_out_insn(S, FMT, OP, ...) \ + glue(tcg_out_insn_,FMT)(S, glue(glue(FMT,_),OP), ## __VA_ARGS__) + + +/* emit 64-bit shifts */ +static void tcg_out_sh64(TCGContext* s, S390Opcode op, TCGReg dest, + TCGReg src, TCGReg sh_reg, int sh_imm) +{ + tcg_out_insn_RSY(s, op, dest, sh_reg, src, sh_imm); +} + +/* emit 32-bit shifts */ +static void tcg_out_sh32(TCGContext* s, S390Opcode op, TCGReg dest, + TCGReg sh_reg, int sh_imm) +{ + tcg_out_insn_RS(s, op, dest, sh_reg, 0, sh_imm); +} + +static void tcg_out_mov(TCGContext *s, TCGType type, TCGReg dst, TCGReg src) +{ + if (src != dst) { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, LR, dst, src); + } else { + tcg_out_insn(s, RRE, LGR, dst, src); + } + } +} + +/* load a register with an immediate value */ +static void tcg_out_movi(TCGContext *s, TCGType type, + TCGReg ret, tcg_target_long sval) +{ + static const S390Opcode lli_insns[4] = { + RI_LLILL, RI_LLILH, RI_LLIHL, RI_LLIHH + }; + + tcg_target_ulong uval = sval; + int i; + + if (type == TCG_TYPE_I32) { + uval = (uint32_t)sval; + sval = (int32_t)sval; + } + + /* Try all 32-bit insns that can load it in one go. */ + if (sval >= -0x8000 && sval < 0x8000) { + tcg_out_insn(s, RI, LGHI, ret, sval); + return; + } + + for (i = 0; i < 4; i++) { + tcg_target_long mask = 0xffffull << i*16; + if ((uval & mask) == uval) { + tcg_out_insn_RI(s, lli_insns[i], ret, uval >> i*16); + return; + } + } + + /* Try all 48-bit insns that can load it in one go. */ + if (facilities & FACILITY_EXT_IMM) { + if (sval == (int32_t)sval) { + tcg_out_insn(s, RIL, LGFI, ret, sval); + return; + } + if (uval <= 0xffffffff) { + tcg_out_insn(s, RIL, LLILF, ret, uval); + return; + } + if ((uval & 0xffffffff) == 0) { + tcg_out_insn(s, RIL, LLIHF, ret, uval >> 31 >> 1); + return; + } + } + + /* Try for PC-relative address load. */ + if ((sval & 1) == 0) { + ptrdiff_t off = tcg_pcrel_diff(s, (void *)sval) >> 1; + if (off == (int32_t)off) { + tcg_out_insn(s, RIL, LARL, ret, off); + return; + } + } + + /* If extended immediates are not present, then we may have to issue + several instructions to load the low 32 bits. */ + if (!(facilities & FACILITY_EXT_IMM)) { + /* A 32-bit unsigned value can be loaded in 2 insns. And given + that the lli_insns loop above did not succeed, we know that + both insns are required. */ + if (uval <= 0xffffffff) { + tcg_out_insn(s, RI, LLILL, ret, uval); + tcg_out_insn(s, RI, IILH, ret, uval >> 16); + return; + } + + /* If all high bits are set, the value can be loaded in 2 or 3 insns. + We first want to make sure that all the high bits get set. With + luck the low 16-bits can be considered negative to perform that for + free, otherwise we load an explicit -1. */ + if (sval >> 31 >> 1 == -1) { + if (uval & 0x8000) { + tcg_out_insn(s, RI, LGHI, ret, uval); + } else { + tcg_out_insn(s, RI, LGHI, ret, -1); + tcg_out_insn(s, RI, IILL, ret, uval); + } + tcg_out_insn(s, RI, IILH, ret, uval >> 16); + return; + } + } + + /* If we get here, both the high and low parts have non-zero bits. */ + + /* Recurse to load the lower 32-bits. */ + tcg_out_movi(s, TCG_TYPE_I64, ret, uval & 0xffffffff); + + /* Insert data into the high 32-bits. */ + uval = uval >> 31 >> 1; + if (facilities & FACILITY_EXT_IMM) { + if (uval < 0x10000) { + tcg_out_insn(s, RI, IIHL, ret, uval); + } else if ((uval & 0xffff) == 0) { + tcg_out_insn(s, RI, IIHH, ret, uval >> 16); + } else { + tcg_out_insn(s, RIL, IIHF, ret, uval); + } + } else { + if (uval & 0xffff) { + tcg_out_insn(s, RI, IIHL, ret, uval); + } + if (uval & 0xffff0000) { + tcg_out_insn(s, RI, IIHH, ret, uval >> 16); + } + } +} + + +/* Emit a load/store type instruction. Inputs are: + DATA: The register to be loaded or stored. + BASE+OFS: The effective address. + OPC_RX: If the operation has an RX format opcode (e.g. STC), otherwise 0. + OPC_RXY: The RXY format opcode for the operation (e.g. STCY). */ + +static void tcg_out_mem(TCGContext *s, S390Opcode opc_rx, S390Opcode opc_rxy, + TCGReg data, TCGReg base, TCGReg index, + tcg_target_long ofs) +{ + if (ofs < -0x80000 || ofs >= 0x80000) { + /* Combine the low 20 bits of the offset with the actual load insn; + the high 44 bits must come from an immediate load. */ + tcg_target_long low = ((ofs & 0xfffff) ^ 0x80000) - 0x80000; + tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, ofs - low); + ofs = low; + + /* If we were already given an index register, add it in. */ + if (index != TCG_REG_NONE) { + tcg_out_insn(s, RRE, AGR, TCG_TMP0, index); + } + index = TCG_TMP0; + } + + if (opc_rx && ofs >= 0 && ofs < 0x1000) { + tcg_out_insn_RX(s, opc_rx, data, base, index, ofs); + } else { + tcg_out_insn_RXY(s, opc_rxy, data, base, index, ofs); + } +} + + +/* load data without address translation or endianness conversion */ +static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, intptr_t ofs) +{ + if (type == TCG_TYPE_I32) { + tcg_out_mem(s, RX_L, RXY_LY, data, base, TCG_REG_NONE, ofs); + } else { + tcg_out_mem(s, 0, RXY_LG, data, base, TCG_REG_NONE, ofs); + } +} + +static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, intptr_t ofs) +{ + if (type == TCG_TYPE_I32) { + tcg_out_mem(s, RX_ST, RXY_STY, data, base, TCG_REG_NONE, ofs); + } else { + tcg_out_mem(s, 0, RXY_STG, data, base, TCG_REG_NONE, ofs); + } +} + +/* load data from an absolute host address */ +static void tcg_out_ld_abs(TCGContext *s, TCGType type, TCGReg dest, void *abs) +{ + intptr_t addr = (intptr_t)abs; + + if ((facilities & FACILITY_GEN_INST_EXT) && !(addr & 1)) { + ptrdiff_t disp = tcg_pcrel_diff(s, abs) >> 1; + if (disp == (int32_t)disp) { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RIL, LRL, dest, disp); + } else { + tcg_out_insn(s, RIL, LGRL, dest, disp); + } + return; + } + } + + tcg_out_movi(s, TCG_TYPE_PTR, dest, addr & ~0xffff); + tcg_out_ld(s, type, dest, dest, addr & 0xffff); +} + +static inline void tcg_out_risbg(TCGContext *s, TCGReg dest, TCGReg src, + int msb, int lsb, int ofs, int z) +{ + /* Format RIE-f */ + tcg_out16(s, (RIE_RISBG & 0xff00) | (dest << 4) | src); + tcg_out16(s, (msb << 8) | (z << 7) | lsb); + tcg_out16(s, (ofs << 8) | (RIE_RISBG & 0xff)); +} + +static void tgen_ext8s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) +{ + if (facilities & FACILITY_EXT_IMM) { + tcg_out_insn(s, RRE, LGBR, dest, src); + return; + } + + if (type == TCG_TYPE_I32) { + if (dest == src) { + tcg_out_sh32(s, RS_SLL, dest, TCG_REG_NONE, 24); + } else { + tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 24); + } + tcg_out_sh32(s, RS_SRA, dest, TCG_REG_NONE, 24); + } else { + tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 56); + tcg_out_sh64(s, RSY_SRAG, dest, dest, TCG_REG_NONE, 56); + } +} + +static void tgen_ext8u(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) +{ + if (facilities & FACILITY_EXT_IMM) { + tcg_out_insn(s, RRE, LLGCR, dest, src); + return; + } + + if (dest == src) { + tcg_out_movi(s, type, TCG_TMP0, 0xff); + src = TCG_TMP0; + } else { + tcg_out_movi(s, type, dest, 0xff); + } + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, NR, dest, src); + } else { + tcg_out_insn(s, RRE, NGR, dest, src); + } +} + +static void tgen_ext16s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) +{ + if (facilities & FACILITY_EXT_IMM) { + tcg_out_insn(s, RRE, LGHR, dest, src); + return; + } + + if (type == TCG_TYPE_I32) { + if (dest == src) { + tcg_out_sh32(s, RS_SLL, dest, TCG_REG_NONE, 16); + } else { + tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 16); + } + tcg_out_sh32(s, RS_SRA, dest, TCG_REG_NONE, 16); + } else { + tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 48); + tcg_out_sh64(s, RSY_SRAG, dest, dest, TCG_REG_NONE, 48); + } +} + +static void tgen_ext16u(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) +{ + if (facilities & FACILITY_EXT_IMM) { + tcg_out_insn(s, RRE, LLGHR, dest, src); + return; + } + + if (dest == src) { + tcg_out_movi(s, type, TCG_TMP0, 0xffff); + src = TCG_TMP0; + } else { + tcg_out_movi(s, type, dest, 0xffff); + } + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, NR, dest, src); + } else { + tcg_out_insn(s, RRE, NGR, dest, src); + } +} + +static inline void tgen_ext32s(TCGContext *s, TCGReg dest, TCGReg src) +{ + tcg_out_insn(s, RRE, LGFR, dest, src); +} + +static inline void tgen_ext32u(TCGContext *s, TCGReg dest, TCGReg src) +{ + tcg_out_insn(s, RRE, LLGFR, dest, src); +} + +/* Accept bit patterns like these: + 0....01....1 + 1....10....0 + 1..10..01..1 + 0..01..10..0 + Copied from gcc sources. */ +static inline bool risbg_mask(uint64_t c) +{ + uint64_t lsb; + /* We don't change the number of transitions by inverting, + so make sure we start with the LSB zero. */ + if (c & 1) { + c = ~c; + } + /* Reject all zeros or all ones. */ + if (c == 0) { + return false; + } + /* Find the first transition. */ + lsb = c & -c; + /* Invert to look for a second transition. */ + c = ~c; + /* Erase the first transition. */ + c &= -lsb; + /* Find the second transition, if any. */ + lsb = c & -c; + /* Match if all the bits are 1's, or if c is zero. */ + return c == -lsb; +} + +static void tgen_andi_risbg(TCGContext *s, TCGReg out, TCGReg in, uint64_t val) +{ + int msb, lsb; + if ((val & 0x8000000000000001ull) == 0x8000000000000001ull) { + /* Achieve wraparound by swapping msb and lsb. */ + msb = 64 - ctz64(~val); + lsb = clz64(~val) - 1; + } else { + msb = clz64(val); + lsb = 63 - ctz64(val); + } + tcg_out_risbg(s, out, in, msb, lsb, 0, 1); +} + +static void tgen_andi(TCGContext *s, TCGType type, TCGReg dest, uint64_t val) +{ + static const S390Opcode ni_insns[4] = { + RI_NILL, RI_NILH, RI_NIHL, RI_NIHH + }; + static const S390Opcode nif_insns[2] = { + RIL_NILF, RIL_NIHF + }; + uint64_t valid = (type == TCG_TYPE_I32 ? 0xffffffffull : -1ull); + int i; + + /* Look for the zero-extensions. */ + if ((val & valid) == 0xffffffff) { + tgen_ext32u(s, dest, dest); + return; + } + if (facilities & FACILITY_EXT_IMM) { + if ((val & valid) == 0xff) { + tgen_ext8u(s, TCG_TYPE_I64, dest, dest); + return; + } + if ((val & valid) == 0xffff) { + tgen_ext16u(s, TCG_TYPE_I64, dest, dest); + return; + } + } + + /* Try all 32-bit insns that can perform it in one go. */ + for (i = 0; i < 4; i++) { + tcg_target_ulong mask = ~(0xffffull << i*16); + if (((val | ~valid) & mask) == mask) { + tcg_out_insn_RI(s, ni_insns[i], dest, val >> i*16); + return; + } + } + + /* Try all 48-bit insns that can perform it in one go. */ + if (facilities & FACILITY_EXT_IMM) { + for (i = 0; i < 2; i++) { + tcg_target_ulong mask = ~(0xffffffffull << i*32); + if (((val | ~valid) & mask) == mask) { + tcg_out_insn_RIL(s, nif_insns[i], dest, val >> i*32); + return; + } + } + } + if ((facilities & FACILITY_GEN_INST_EXT) && risbg_mask(val)) { + tgen_andi_risbg(s, dest, dest, val); + return; + } + + /* Fall back to loading the constant. */ + tcg_out_movi(s, type, TCG_TMP0, val); + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, NR, dest, TCG_TMP0); + } else { + tcg_out_insn(s, RRE, NGR, dest, TCG_TMP0); + } +} + +static void tgen64_ori(TCGContext *s, TCGReg dest, tcg_target_ulong val) +{ + static const S390Opcode oi_insns[4] = { + RI_OILL, RI_OILH, RI_OIHL, RI_OIHH + }; + static const S390Opcode nif_insns[2] = { + RIL_OILF, RIL_OIHF + }; + + int i; + + /* Look for no-op. */ + if (val == 0) { + return; + } + + if (facilities & FACILITY_EXT_IMM) { + /* Try all 32-bit insns that can perform it in one go. */ + for (i = 0; i < 4; i++) { + tcg_target_ulong mask = (0xffffull << i*16); + if ((val & mask) != 0 && (val & ~mask) == 0) { + tcg_out_insn_RI(s, oi_insns[i], dest, val >> i*16); + return; + } + } + + /* Try all 48-bit insns that can perform it in one go. */ + for (i = 0; i < 2; i++) { + tcg_target_ulong mask = (0xffffffffull << i*32); + if ((val & mask) != 0 && (val & ~mask) == 0) { + tcg_out_insn_RIL(s, nif_insns[i], dest, val >> i*32); + return; + } + } + + /* Perform the OR via sequential modifications to the high and + low parts. Do this via recursion to handle 16-bit vs 32-bit + masks in each half. */ + tgen64_ori(s, dest, val & 0x00000000ffffffffull); + tgen64_ori(s, dest, val & 0xffffffff00000000ull); + } else { + /* With no extended-immediate facility, we don't need to be so + clever. Just iterate over the insns and mask in the constant. */ + for (i = 0; i < 4; i++) { + tcg_target_ulong mask = (0xffffull << i*16); + if ((val & mask) != 0) { + tcg_out_insn_RI(s, oi_insns[i], dest, val >> i*16); + } + } + } +} + +static void tgen64_xori(TCGContext *s, TCGReg dest, tcg_target_ulong val) +{ + /* Perform the xor by parts. */ + if (val & 0xffffffff) { + tcg_out_insn(s, RIL, XILF, dest, val); + } + if (val > 0xffffffff) { + tcg_out_insn(s, RIL, XIHF, dest, val >> 31 >> 1); + } +} + +static int tgen_cmp(TCGContext *s, TCGType type, TCGCond c, TCGReg r1, + TCGArg c2, int c2const) +{ + bool is_unsigned = is_unsigned_cond(c); + if (c2const) { + if (c2 == 0) { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, LTR, r1, r1); + } else { + tcg_out_insn(s, RRE, LTGR, r1, r1); + } + return tcg_cond_to_ltr_cond[c]; + } else { + if (is_unsigned) { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RIL, CLFI, r1, c2); + } else { + tcg_out_insn(s, RIL, CLGFI, r1, c2); + } + } else { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RIL, CFI, r1, c2); + } else { + tcg_out_insn(s, RIL, CGFI, r1, c2); + } + } + } + } else { + if (is_unsigned) { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, CLR, r1, c2); + } else { + tcg_out_insn(s, RRE, CLGR, r1, c2); + } + } else { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, CR, r1, c2); + } else { + tcg_out_insn(s, RRE, CGR, r1, c2); + } + } + } + return tcg_cond_to_s390_cond[c]; +} + +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg c1, TCGArg c2, int c2const) +{ + int cc; + + switch (cond) { + case TCG_COND_GTU: + case TCG_COND_GT: + do_greater: + /* The result of a compare has CC=2 for GT and CC=3 unused. + ADD LOGICAL WITH CARRY considers (CC & 2) the carry bit. */ + tgen_cmp(s, type, cond, c1, c2, c2const); + tcg_out_movi(s, type, dest, 0); + tcg_out_insn(s, RRE, ALCGR, dest, dest); + return; + + case TCG_COND_GEU: + do_geu: + /* We need "real" carry semantics, so use SUBTRACT LOGICAL + instead of COMPARE LOGICAL. This needs an extra move. */ + tcg_out_mov(s, type, TCG_TMP0, c1); + if (c2const) { + tcg_out_movi(s, TCG_TYPE_I64, dest, 0); + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RIL, SLFI, TCG_TMP0, c2); + } else { + tcg_out_insn(s, RIL, SLGFI, TCG_TMP0, c2); + } + } else { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, SLR, TCG_TMP0, c2); + } else { + tcg_out_insn(s, RRE, SLGR, TCG_TMP0, c2); + } + tcg_out_movi(s, TCG_TYPE_I64, dest, 0); + } + tcg_out_insn(s, RRE, ALCGR, dest, dest); + return; + + case TCG_COND_LEU: + case TCG_COND_LTU: + case TCG_COND_LT: + /* Swap operands so that we can use GEU/GTU/GT. */ + if (c2const) { + tcg_out_movi(s, type, TCG_TMP0, c2); + c2 = c1; + c2const = 0; + c1 = TCG_TMP0; + } else { + TCGReg t = c1; + c1 = c2; + c2 = t; + } + if (cond == TCG_COND_LEU) { + goto do_geu; + } + cond = tcg_swap_cond(cond); + goto do_greater; + + case TCG_COND_NE: + /* X != 0 is X > 0. */ + if (c2const && c2 == 0) { + cond = TCG_COND_GTU; + goto do_greater; + } + break; + + case TCG_COND_EQ: + /* X == 0 is X <= 0 is 0 >= X. */ + if (c2const && c2 == 0) { + tcg_out_movi(s, TCG_TYPE_I64, TCG_TMP0, 0); + c2 = c1; + c2const = 0; + c1 = TCG_TMP0; + goto do_geu; + } + break; + + default: + break; + } + + cc = tgen_cmp(s, type, cond, c1, c2, c2const); + if (facilities & FACILITY_LOAD_ON_COND) { + /* Emit: d = 0, t = 1, d = (cc ? t : d). */ + tcg_out_movi(s, TCG_TYPE_I64, dest, 0); + tcg_out_movi(s, TCG_TYPE_I64, TCG_TMP0, 1); + tcg_out_insn(s, RRF, LOCGR, dest, TCG_TMP0, cc); + } else { + /* Emit: d = 1; if (cc) goto over; d = 0; over: */ + tcg_out_movi(s, type, dest, 1); + tcg_out_insn(s, RI, BRC, cc, (4 + 4) >> 1); + tcg_out_movi(s, type, dest, 0); + } +} + +static void tgen_movcond(TCGContext *s, TCGType type, TCGCond c, TCGReg dest, + TCGReg c1, TCGArg c2, int c2const, TCGReg r3) +{ + int cc; + if (facilities & FACILITY_LOAD_ON_COND) { + cc = tgen_cmp(s, type, c, c1, c2, c2const); + tcg_out_insn(s, RRF, LOCGR, dest, r3, cc); + } else { + c = tcg_invert_cond(c); + cc = tgen_cmp(s, type, c, c1, c2, c2const); + + /* Emit: if (cc) goto over; dest = r3; over: */ + tcg_out_insn(s, RI, BRC, cc, (4 + 4) >> 1); + tcg_out_insn(s, RRE, LGR, dest, r3); + } +} + +bool tcg_target_deposit_valid(int ofs, int len) +{ + return (facilities & FACILITY_GEN_INST_EXT) != 0; +} + +static void tgen_deposit(TCGContext *s, TCGReg dest, TCGReg src, + int ofs, int len) +{ + int lsb = (63 - ofs); + int msb = lsb - (len - 1); + tcg_out_risbg(s, dest, src, msb, lsb, ofs, 0); +} + +static void tgen_gotoi(TCGContext *s, int cc, tcg_insn_unit *dest) +{ + ptrdiff_t off = dest - s->code_ptr; + if (off == (int16_t)off) { + tcg_out_insn(s, RI, BRC, cc, off); + } else if (off == (int32_t)off) { + tcg_out_insn(s, RIL, BRCL, cc, off); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, (uintptr_t)dest); + tcg_out_insn(s, RR, BCR, cc, TCG_TMP0); + } +} + +static void tgen_branch(TCGContext *s, int cc, TCGLabel *l) +{ + if (l->has_value) { + tgen_gotoi(s, cc, l->u.value_ptr); + } else if (USE_LONG_BRANCHES) { + tcg_out16(s, RIL_BRCL | (cc << 4)); + tcg_out_reloc(s, s->code_ptr, R_390_PC32DBL, l, -2); + s->code_ptr += 2; + } else { + tcg_out16(s, RI_BRC | (cc << 4)); + tcg_out_reloc(s, s->code_ptr, R_390_PC16DBL, l, -2); + s->code_ptr += 1; + } +} + +static void tgen_compare_branch(TCGContext *s, S390Opcode opc, int cc, + TCGReg r1, TCGReg r2, TCGLabel *l) +{ + intptr_t off; + + if (l->has_value) { + off = l->u.value_ptr - s->code_ptr; + } else { + /* We need to keep the offset unchanged for retranslation. */ + off = s->code_ptr[1]; + tcg_out_reloc(s, s->code_ptr + 1, R_390_PC16DBL, l, -2); + } + + tcg_out16(s, (opc & 0xff00) | (r1 << 4) | r2); + tcg_out16(s, off); + tcg_out16(s, cc << 12 | (opc & 0xff)); +} + +static void tgen_compare_imm_branch(TCGContext *s, S390Opcode opc, int cc, + TCGReg r1, int i2, TCGLabel *l) +{ + tcg_target_long off; + + if (l->has_value) { + off = l->u.value_ptr - s->code_ptr; + } else { + /* We need to keep the offset unchanged for retranslation. */ + off = s->code_ptr[1]; + tcg_out_reloc(s, s->code_ptr + 1, R_390_PC16DBL, l, -2); + } + + tcg_out16(s, (opc & 0xff00) | (r1 << 4) | cc); + tcg_out16(s, off); + tcg_out16(s, (i2 << 8) | (opc & 0xff)); +} + +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond c, + TCGReg r1, TCGArg c2, int c2const, TCGLabel *l) +{ + int cc; + + if (facilities & FACILITY_GEN_INST_EXT) { + bool is_unsigned = is_unsigned_cond(c); + bool in_range; + S390Opcode opc; + + cc = tcg_cond_to_s390_cond[c]; + + if (!c2const) { + opc = (type == TCG_TYPE_I32 + ? (is_unsigned ? RIE_CLRJ : RIE_CRJ) + : (is_unsigned ? RIE_CLGRJ : RIE_CGRJ)); + tgen_compare_branch(s, opc, cc, r1, c2, l); + return; + } + + /* COMPARE IMMEDIATE AND BRANCH RELATIVE has an 8-bit immediate field. + If the immediate we've been given does not fit that range, we'll + fall back to separate compare and branch instructions using the + larger comparison range afforded by COMPARE IMMEDIATE. */ + if (type == TCG_TYPE_I32) { + if (is_unsigned) { + opc = RIE_CLIJ; + in_range = (uint32_t)c2 == (uint8_t)c2; + } else { + opc = RIE_CIJ; + in_range = (int32_t)c2 == (int8_t)c2; + } + } else { + if (is_unsigned) { + opc = RIE_CLGIJ; + in_range = (uint64_t)c2 == (uint8_t)c2; + } else { + opc = RIE_CGIJ; + in_range = (int64_t)c2 == (int8_t)c2; + } + } + if (in_range) { + tgen_compare_imm_branch(s, opc, cc, r1, c2, l); + return; + } + } + + cc = tgen_cmp(s, type, c, r1, c2, c2const); + tgen_branch(s, cc, l); +} + +static void tcg_out_call(TCGContext *s, tcg_insn_unit *dest) +{ + ptrdiff_t off = dest - s->code_ptr; + if (off == (int32_t)off) { + tcg_out_insn(s, RIL, BRASL, TCG_REG_R14, off); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, (uintptr_t)dest); + tcg_out_insn(s, RR, BASR, TCG_REG_R14, TCG_TMP0); + } +} + +static void tcg_out_qemu_ld_direct(TCGContext *s, TCGMemOp opc, TCGReg data, + TCGReg base, TCGReg index, int disp) +{ + switch (opc & (MO_SSIZE | MO_BSWAP)) { + case MO_UB: + tcg_out_insn(s, RXY, LLGC, data, base, index, disp); + break; + case MO_SB: + tcg_out_insn(s, RXY, LGB, data, base, index, disp); + break; + + case MO_UW | MO_BSWAP: + /* swapped unsigned halfword load with upper bits zeroed */ + tcg_out_insn(s, RXY, LRVH, data, base, index, disp); + tgen_ext16u(s, TCG_TYPE_I64, data, data); + break; + case MO_UW: + tcg_out_insn(s, RXY, LLGH, data, base, index, disp); + break; + + case MO_SW | MO_BSWAP: + /* swapped sign-extended halfword load */ + tcg_out_insn(s, RXY, LRVH, data, base, index, disp); + tgen_ext16s(s, TCG_TYPE_I64, data, data); + break; + case MO_SW: + tcg_out_insn(s, RXY, LGH, data, base, index, disp); + break; + + case MO_UL | MO_BSWAP: + /* swapped unsigned int load with upper bits zeroed */ + tcg_out_insn(s, RXY, LRV, data, base, index, disp); + tgen_ext32u(s, data, data); + break; + case MO_UL: + tcg_out_insn(s, RXY, LLGF, data, base, index, disp); + break; + + case MO_SL | MO_BSWAP: + /* swapped sign-extended int load */ + tcg_out_insn(s, RXY, LRV, data, base, index, disp); + tgen_ext32s(s, data, data); + break; + case MO_SL: + tcg_out_insn(s, RXY, LGF, data, base, index, disp); + break; + + case MO_Q | MO_BSWAP: + tcg_out_insn(s, RXY, LRVG, data, base, index, disp); + break; + case MO_Q: + tcg_out_insn(s, RXY, LG, data, base, index, disp); + break; + + default: + tcg_abort(); + } +} + +static void tcg_out_qemu_st_direct(TCGContext *s, TCGMemOp opc, TCGReg data, + TCGReg base, TCGReg index, int disp) +{ + switch (opc & (MO_SIZE | MO_BSWAP)) { + case MO_UB: + if (disp >= 0 && disp < 0x1000) { + tcg_out_insn(s, RX, STC, data, base, index, disp); + } else { + tcg_out_insn(s, RXY, STCY, data, base, index, disp); + } + break; + + case MO_UW | MO_BSWAP: + tcg_out_insn(s, RXY, STRVH, data, base, index, disp); + break; + case MO_UW: + if (disp >= 0 && disp < 0x1000) { + tcg_out_insn(s, RX, STH, data, base, index, disp); + } else { + tcg_out_insn(s, RXY, STHY, data, base, index, disp); + } + break; + + case MO_UL | MO_BSWAP: + tcg_out_insn(s, RXY, STRV, data, base, index, disp); + break; + case MO_UL: + if (disp >= 0 && disp < 0x1000) { + tcg_out_insn(s, RX, ST, data, base, index, disp); + } else { + tcg_out_insn(s, RXY, STY, data, base, index, disp); + } + break; + + case MO_Q | MO_BSWAP: + tcg_out_insn(s, RXY, STRVG, data, base, index, disp); + break; + case MO_Q: + tcg_out_insn(s, RXY, STG, data, base, index, disp); + break; + + default: + tcg_abort(); + } +} + +#if defined(CONFIG_SOFTMMU) +/* We're expecting to use a 20-bit signed offset on the tlb memory ops. + Using the offset of the second entry in the last tlb table ensures + that we can index all of the elements of the first entry. */ +QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table[NB_MMU_MODES - 1][1]) + > 0x7ffff); + +/* Load and compare a TLB entry, leaving the flags set. Loads the TLB + addend into R2. Returns a register with the santitized guest address. */ +static TCGReg tcg_out_tlb_read(TCGContext* s, TCGReg addr_reg, TCGMemOp opc, + int mem_index, bool is_ld) +{ + int s_mask = (1 << (opc & MO_SIZE)) - 1; + int ofs, a_off; + uint64_t tlb_mask; + + /* For aligned accesses, we check the first byte and include the alignment + bits within the address. For unaligned access, we check that we don't + cross pages using the address of the last byte of the access. */ + if ((opc & MO_AMASK) == MO_ALIGN || s_mask == 0) { + a_off = 0; + tlb_mask = TARGET_PAGE_MASK | s_mask; + } else { + a_off = s_mask; + tlb_mask = TARGET_PAGE_MASK; + } + + if (facilities & FACILITY_GEN_INST_EXT) { + tcg_out_risbg(s, TCG_REG_R2, addr_reg, + 64 - CPU_TLB_BITS - CPU_TLB_ENTRY_BITS, + 63 - CPU_TLB_ENTRY_BITS, + 64 + CPU_TLB_ENTRY_BITS - TARGET_PAGE_BITS, 1); + if (a_off) { + tcg_out_insn(s, RX, LA, TCG_REG_R3, addr_reg, TCG_REG_NONE, a_off); + tgen_andi(s, TCG_TYPE_TL, TCG_REG_R3, tlb_mask); + } else { + tgen_andi_risbg(s, TCG_REG_R3, addr_reg, tlb_mask); + } + } else { + tcg_out_sh64(s, RSY_SRLG, TCG_REG_R2, addr_reg, TCG_REG_NONE, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); + tcg_out_insn(s, RX, LA, TCG_REG_R3, addr_reg, TCG_REG_NONE, a_off); + tgen_andi(s, TCG_TYPE_I64, TCG_REG_R2, + (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS); + tgen_andi(s, TCG_TYPE_TL, TCG_REG_R3, tlb_mask); + } + + if (is_ld) { + ofs = offsetof(CPUArchState, tlb_table[mem_index][0].addr_read); + } else { + ofs = offsetof(CPUArchState, tlb_table[mem_index][0].addr_write); + } + if (TARGET_LONG_BITS == 32) { + tcg_out_mem(s, RX_C, RXY_CY, TCG_REG_R3, TCG_REG_R2, TCG_AREG0, ofs); + } else { + tcg_out_mem(s, 0, RXY_CG, TCG_REG_R3, TCG_REG_R2, TCG_AREG0, ofs); + } + + ofs = offsetof(CPUArchState, tlb_table[mem_index][0].addend); + tcg_out_mem(s, 0, RXY_LG, TCG_REG_R2, TCG_REG_R2, TCG_AREG0, ofs); + + if (TARGET_LONG_BITS == 32) { + tgen_ext32u(s, TCG_REG_R3, addr_reg); + return TCG_REG_R3; + } + return addr_reg; +} + +static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOpIdx oi, + TCGReg data, TCGReg addr, + tcg_insn_unit *raddr, tcg_insn_unit *label_ptr) +{ + TCGLabelQemuLdst *label = new_ldst_label(s); + + label->is_ld = is_ld; + label->oi = oi; + label->datalo_reg = data; + label->addrlo_reg = addr; + label->raddr = raddr; + label->label_ptr[0] = label_ptr; +} + +static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) +{ + TCGReg addr_reg = lb->addrlo_reg; + TCGReg data_reg = lb->datalo_reg; + TCGMemOpIdx oi = lb->oi; + TCGMemOp opc = get_memop(oi); + + patch_reloc(lb->label_ptr[0], R_390_PC16DBL, (intptr_t)s->code_ptr, -2); + + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_R2, TCG_AREG0); + if (TARGET_LONG_BITS == 64) { + tcg_out_mov(s, TCG_TYPE_I64, TCG_REG_R3, addr_reg); + } + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R4, oi); + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R5, (uintptr_t)lb->raddr); + tcg_out_call(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SSIZE)]); + tcg_out_mov(s, TCG_TYPE_I64, data_reg, TCG_REG_R2); + + tgen_gotoi(s, S390_CC_ALWAYS, lb->raddr); +} + +static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) +{ + TCGReg addr_reg = lb->addrlo_reg; + TCGReg data_reg = lb->datalo_reg; + TCGMemOpIdx oi = lb->oi; + TCGMemOp opc = get_memop(oi); + + patch_reloc(lb->label_ptr[0], R_390_PC16DBL, (intptr_t)s->code_ptr, -2); + + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_R2, TCG_AREG0); + if (TARGET_LONG_BITS == 64) { + tcg_out_mov(s, TCG_TYPE_I64, TCG_REG_R3, addr_reg); + } + switch (opc & MO_SIZE) { + case MO_UB: + tgen_ext8u(s, TCG_TYPE_I64, TCG_REG_R4, data_reg); + break; + case MO_UW: + tgen_ext16u(s, TCG_TYPE_I64, TCG_REG_R4, data_reg); + break; + case MO_UL: + tgen_ext32u(s, TCG_REG_R4, data_reg); + break; + case MO_Q: + tcg_out_mov(s, TCG_TYPE_I64, TCG_REG_R4, data_reg); + break; + default: + tcg_abort(); + } + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R5, oi); + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R6, (uintptr_t)lb->raddr); + tcg_out_call(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)]); + + tgen_gotoi(s, S390_CC_ALWAYS, lb->raddr); +} +#else +static void tcg_prepare_user_ldst(TCGContext *s, TCGReg *addr_reg, + TCGReg *index_reg, tcg_target_long *disp) +{ + if (TARGET_LONG_BITS == 32) { + tgen_ext32u(s, TCG_TMP0, *addr_reg); + *addr_reg = TCG_TMP0; + } + if (guest_base < 0x80000) { + *index_reg = TCG_REG_NONE; + *disp = guest_base; + } else { + *index_reg = TCG_GUEST_BASE_REG; + *disp = 0; + } +} +#endif /* CONFIG_SOFTMMU */ + +static void tcg_out_qemu_ld(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, + TCGMemOpIdx oi) +{ + TCGMemOp opc = get_memop(oi); +#ifdef CONFIG_SOFTMMU + unsigned mem_index = get_mmuidx(oi); + tcg_insn_unit *label_ptr; + TCGReg base_reg; + + base_reg = tcg_out_tlb_read(s, addr_reg, opc, mem_index, 1); + + /* We need to keep the offset unchanged for retranslation. */ + tcg_out16(s, RI_BRC | (S390_CC_NE << 4)); + label_ptr = s->code_ptr; + s->code_ptr += 1; + + tcg_out_qemu_ld_direct(s, opc, data_reg, base_reg, TCG_REG_R2, 0); + + add_qemu_ldst_label(s, 1, oi, data_reg, addr_reg, s->code_ptr, label_ptr); +#else + TCGReg index_reg; + tcg_target_long disp; + + tcg_prepare_user_ldst(s, &addr_reg, &index_reg, &disp); + tcg_out_qemu_ld_direct(s, opc, data_reg, addr_reg, index_reg, disp); +#endif +} + +static void tcg_out_qemu_st(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, + TCGMemOpIdx oi) +{ + TCGMemOp opc = get_memop(oi); +#ifdef CONFIG_SOFTMMU + unsigned mem_index = get_mmuidx(oi); + tcg_insn_unit *label_ptr; + TCGReg base_reg; + + base_reg = tcg_out_tlb_read(s, addr_reg, opc, mem_index, 0); + + /* We need to keep the offset unchanged for retranslation. */ + tcg_out16(s, RI_BRC | (S390_CC_NE << 4)); + label_ptr = s->code_ptr; + s->code_ptr += 1; + + tcg_out_qemu_st_direct(s, opc, data_reg, base_reg, TCG_REG_R2, 0); + + add_qemu_ldst_label(s, 0, oi, data_reg, addr_reg, s->code_ptr, label_ptr); +#else + TCGReg index_reg; + tcg_target_long disp; + + tcg_prepare_user_ldst(s, &addr_reg, &index_reg, &disp); + tcg_out_qemu_st_direct(s, opc, data_reg, addr_reg, index_reg, disp); +#endif +} + +# define OP_32_64(x) \ + case glue(glue(INDEX_op_,x),_i32): \ + case glue(glue(INDEX_op_,x),_i64) + +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, + const TCGArg *args, const int *const_args) +{ + S390Opcode op; + TCGArg a0, a1, a2; + + switch (opc) { + case INDEX_op_exit_tb: + /* return value */ + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, args[0]); + tgen_gotoi(s, S390_CC_ALWAYS, tb_ret_addr); + break; + + case INDEX_op_goto_tb: + if (s->tb_jmp_offset) { + tcg_out16(s, RIL_BRCL | (S390_CC_ALWAYS << 4)); + s->tb_jmp_offset[args[0]] = tcg_current_code_size(s); + s->code_ptr += 2; + } else { + /* load address stored at s->tb_next + args[0] */ + tcg_out_ld_abs(s, TCG_TYPE_PTR, TCG_TMP0, s->tb_next + args[0]); + /* and go there */ + tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_TMP0); + } + s->tb_next_offset[args[0]] = tcg_current_code_size(s); + break; + + OP_32_64(ld8u): + /* ??? LLC (RXY format) is only present with the extended-immediate + facility, whereas LLGC is always present. */ + tcg_out_mem(s, 0, RXY_LLGC, args[0], args[1], TCG_REG_NONE, args[2]); + break; + + OP_32_64(ld8s): + /* ??? LB is no smaller than LGB, so no point to using it. */ + tcg_out_mem(s, 0, RXY_LGB, args[0], args[1], TCG_REG_NONE, args[2]); + break; + + OP_32_64(ld16u): + /* ??? LLH (RXY format) is only present with the extended-immediate + facility, whereas LLGH is always present. */ + tcg_out_mem(s, 0, RXY_LLGH, args[0], args[1], TCG_REG_NONE, args[2]); + break; + + case INDEX_op_ld16s_i32: + tcg_out_mem(s, RX_LH, RXY_LHY, args[0], args[1], TCG_REG_NONE, args[2]); + break; + + case INDEX_op_ld_i32: + tcg_out_ld(s, TCG_TYPE_I32, args[0], args[1], args[2]); + break; + + OP_32_64(st8): + tcg_out_mem(s, RX_STC, RXY_STCY, args[0], args[1], + TCG_REG_NONE, args[2]); + break; + + OP_32_64(st16): + tcg_out_mem(s, RX_STH, RXY_STHY, args[0], args[1], + TCG_REG_NONE, args[2]); + break; + + case INDEX_op_st_i32: + tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); + break; + + case INDEX_op_add_i32: + a0 = args[0], a1 = args[1], a2 = (int32_t)args[2]; + if (const_args[2]) { + do_addi_32: + if (a0 == a1) { + if (a2 == (int16_t)a2) { + tcg_out_insn(s, RI, AHI, a0, a2); + break; + } + if (facilities & FACILITY_EXT_IMM) { + tcg_out_insn(s, RIL, AFI, a0, a2); + break; + } + } + tcg_out_mem(s, RX_LA, RXY_LAY, a0, a1, TCG_REG_NONE, a2); + } else if (a0 == a1) { + tcg_out_insn(s, RR, AR, a0, a2); + } else { + tcg_out_insn(s, RX, LA, a0, a1, a2, 0); + } + break; + case INDEX_op_sub_i32: + a0 = args[0], a1 = args[1], a2 = (int32_t)args[2]; + if (const_args[2]) { + a2 = -a2; + goto do_addi_32; + } + tcg_out_insn(s, RR, SR, args[0], args[2]); + break; + + case INDEX_op_and_i32: + if (const_args[2]) { + tgen_andi(s, TCG_TYPE_I32, args[0], args[2]); + } else { + tcg_out_insn(s, RR, NR, args[0], args[2]); + } + break; + case INDEX_op_or_i32: + if (const_args[2]) { + tgen64_ori(s, args[0], args[2] & 0xffffffff); + } else { + tcg_out_insn(s, RR, OR, args[0], args[2]); + } + break; + case INDEX_op_xor_i32: + if (const_args[2]) { + tgen64_xori(s, args[0], args[2] & 0xffffffff); + } else { + tcg_out_insn(s, RR, XR, args[0], args[2]); + } + break; + + case INDEX_op_neg_i32: + tcg_out_insn(s, RR, LCR, args[0], args[1]); + break; + + case INDEX_op_mul_i32: + if (const_args[2]) { + if ((int32_t)args[2] == (int16_t)args[2]) { + tcg_out_insn(s, RI, MHI, args[0], args[2]); + } else { + tcg_out_insn(s, RIL, MSFI, args[0], args[2]); + } + } else { + tcg_out_insn(s, RRE, MSR, args[0], args[2]); + } + break; + + case INDEX_op_div2_i32: + tcg_out_insn(s, RR, DR, TCG_REG_R2, args[4]); + break; + case INDEX_op_divu2_i32: + tcg_out_insn(s, RRE, DLR, TCG_REG_R2, args[4]); + break; + + case INDEX_op_shl_i32: + op = RS_SLL; + do_shift32: + if (const_args[2]) { + tcg_out_sh32(s, op, args[0], TCG_REG_NONE, args[2]); + } else { + tcg_out_sh32(s, op, args[0], args[2], 0); + } + break; + case INDEX_op_shr_i32: + op = RS_SRL; + goto do_shift32; + case INDEX_op_sar_i32: + op = RS_SRA; + goto do_shift32; + + case INDEX_op_rotl_i32: + /* ??? Using tcg_out_sh64 here for the format; it is a 32-bit rol. */ + if (const_args[2]) { + tcg_out_sh64(s, RSY_RLL, args[0], args[1], TCG_REG_NONE, args[2]); + } else { + tcg_out_sh64(s, RSY_RLL, args[0], args[1], args[2], 0); + } + break; + case INDEX_op_rotr_i32: + if (const_args[2]) { + tcg_out_sh64(s, RSY_RLL, args[0], args[1], + TCG_REG_NONE, (32 - args[2]) & 31); + } else { + tcg_out_insn(s, RR, LCR, TCG_TMP0, args[2]); + tcg_out_sh64(s, RSY_RLL, args[0], args[1], TCG_TMP0, 0); + } + break; + + case INDEX_op_ext8s_i32: + tgen_ext8s(s, TCG_TYPE_I32, args[0], args[1]); + break; + case INDEX_op_ext16s_i32: + tgen_ext16s(s, TCG_TYPE_I32, args[0], args[1]); + break; + case INDEX_op_ext8u_i32: + tgen_ext8u(s, TCG_TYPE_I32, args[0], args[1]); + break; + case INDEX_op_ext16u_i32: + tgen_ext16u(s, TCG_TYPE_I32, args[0], args[1]); + break; + + OP_32_64(bswap16): + /* The TCG bswap definition requires bits 0-47 already be zero. + Thus we don't need the G-type insns to implement bswap16_i64. */ + tcg_out_insn(s, RRE, LRVR, args[0], args[1]); + tcg_out_sh32(s, RS_SRL, args[0], TCG_REG_NONE, 16); + break; + OP_32_64(bswap32): + tcg_out_insn(s, RRE, LRVR, args[0], args[1]); + break; + + case INDEX_op_add2_i32: + if (const_args[4]) { + tcg_out_insn(s, RIL, ALFI, args[0], args[4]); + } else { + tcg_out_insn(s, RR, ALR, args[0], args[4]); + } + tcg_out_insn(s, RRE, ALCR, args[1], args[5]); + break; + case INDEX_op_sub2_i32: + if (const_args[4]) { + tcg_out_insn(s, RIL, SLFI, args[0], args[4]); + } else { + tcg_out_insn(s, RR, SLR, args[0], args[4]); + } + tcg_out_insn(s, RRE, SLBR, args[1], args[5]); + break; + + case INDEX_op_br: + tgen_branch(s, S390_CC_ALWAYS, arg_label(args[0])); + break; + + case INDEX_op_brcond_i32: + tgen_brcond(s, TCG_TYPE_I32, args[2], args[0], + args[1], const_args[1], arg_label(args[3])); + break; + case INDEX_op_setcond_i32: + tgen_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], + args[2], const_args[2]); + break; + case INDEX_op_movcond_i32: + tgen_movcond(s, TCG_TYPE_I32, args[5], args[0], args[1], + args[2], const_args[2], args[3]); + break; + + case INDEX_op_qemu_ld_i32: + /* ??? Technically we can use a non-extending instruction. */ + case INDEX_op_qemu_ld_i64: + tcg_out_qemu_ld(s, args[0], args[1], args[2]); + break; + case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st_i64: + tcg_out_qemu_st(s, args[0], args[1], args[2]); + break; + + case INDEX_op_ld16s_i64: + tcg_out_mem(s, 0, RXY_LGH, args[0], args[1], TCG_REG_NONE, args[2]); + break; + case INDEX_op_ld32u_i64: + tcg_out_mem(s, 0, RXY_LLGF, args[0], args[1], TCG_REG_NONE, args[2]); + break; + case INDEX_op_ld32s_i64: + tcg_out_mem(s, 0, RXY_LGF, args[0], args[1], TCG_REG_NONE, args[2]); + break; + case INDEX_op_ld_i64: + tcg_out_ld(s, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + + case INDEX_op_st32_i64: + tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); + break; + case INDEX_op_st_i64: + tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + + case INDEX_op_add_i64: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + do_addi_64: + if (a0 == a1) { + if (a2 == (int16_t)a2) { + tcg_out_insn(s, RI, AGHI, a0, a2); + break; + } + if (facilities & FACILITY_EXT_IMM) { + if (a2 == (int32_t)a2) { + tcg_out_insn(s, RIL, AGFI, a0, a2); + break; + } else if (a2 == (uint32_t)a2) { + tcg_out_insn(s, RIL, ALGFI, a0, a2); + break; + } else if (-a2 == (uint32_t)-a2) { + tcg_out_insn(s, RIL, SLGFI, a0, -a2); + break; + } + } + } + tcg_out_mem(s, RX_LA, RXY_LAY, a0, a1, TCG_REG_NONE, a2); + } else if (a0 == a1) { + tcg_out_insn(s, RRE, AGR, a0, a2); + } else { + tcg_out_insn(s, RX, LA, a0, a1, a2, 0); + } + break; + case INDEX_op_sub_i64: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + a2 = -a2; + goto do_addi_64; + } else { + tcg_out_insn(s, RRE, SGR, args[0], args[2]); + } + break; + + case INDEX_op_and_i64: + if (const_args[2]) { + tgen_andi(s, TCG_TYPE_I64, args[0], args[2]); + } else { + tcg_out_insn(s, RRE, NGR, args[0], args[2]); + } + break; + case INDEX_op_or_i64: + if (const_args[2]) { + tgen64_ori(s, args[0], args[2]); + } else { + tcg_out_insn(s, RRE, OGR, args[0], args[2]); + } + break; + case INDEX_op_xor_i64: + if (const_args[2]) { + tgen64_xori(s, args[0], args[2]); + } else { + tcg_out_insn(s, RRE, XGR, args[0], args[2]); + } + break; + + case INDEX_op_neg_i64: + tcg_out_insn(s, RRE, LCGR, args[0], args[1]); + break; + case INDEX_op_bswap64_i64: + tcg_out_insn(s, RRE, LRVGR, args[0], args[1]); + break; + + case INDEX_op_mul_i64: + if (const_args[2]) { + if (args[2] == (int16_t)args[2]) { + tcg_out_insn(s, RI, MGHI, args[0], args[2]); + } else { + tcg_out_insn(s, RIL, MSGFI, args[0], args[2]); + } + } else { + tcg_out_insn(s, RRE, MSGR, args[0], args[2]); + } + break; + + case INDEX_op_div2_i64: + /* ??? We get an unnecessary sign-extension of the dividend + into R3 with this definition, but as we do in fact always + produce both quotient and remainder using INDEX_op_div_i64 + instead requires jumping through even more hoops. */ + tcg_out_insn(s, RRE, DSGR, TCG_REG_R2, args[4]); + break; + case INDEX_op_divu2_i64: + tcg_out_insn(s, RRE, DLGR, TCG_REG_R2, args[4]); + break; + case INDEX_op_mulu2_i64: + tcg_out_insn(s, RRE, MLGR, TCG_REG_R2, args[3]); + break; + + case INDEX_op_shl_i64: + op = RSY_SLLG; + do_shift64: + if (const_args[2]) { + tcg_out_sh64(s, op, args[0], args[1], TCG_REG_NONE, args[2]); + } else { + tcg_out_sh64(s, op, args[0], args[1], args[2], 0); + } + break; + case INDEX_op_shr_i64: + op = RSY_SRLG; + goto do_shift64; + case INDEX_op_sar_i64: + op = RSY_SRAG; + goto do_shift64; + + case INDEX_op_rotl_i64: + if (const_args[2]) { + tcg_out_sh64(s, RSY_RLLG, args[0], args[1], + TCG_REG_NONE, args[2]); + } else { + tcg_out_sh64(s, RSY_RLLG, args[0], args[1], args[2], 0); + } + break; + case INDEX_op_rotr_i64: + if (const_args[2]) { + tcg_out_sh64(s, RSY_RLLG, args[0], args[1], + TCG_REG_NONE, (64 - args[2]) & 63); + } else { + /* We can use the smaller 32-bit negate because only the + low 6 bits are examined for the rotate. */ + tcg_out_insn(s, RR, LCR, TCG_TMP0, args[2]); + tcg_out_sh64(s, RSY_RLLG, args[0], args[1], TCG_TMP0, 0); + } + break; + + case INDEX_op_ext8s_i64: + tgen_ext8s(s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_ext16s_i64: + tgen_ext16s(s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_ext_i32_i64: + case INDEX_op_ext32s_i64: + tgen_ext32s(s, args[0], args[1]); + break; + case INDEX_op_ext8u_i64: + tgen_ext8u(s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_ext16u_i64: + tgen_ext16u(s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_extu_i32_i64: + case INDEX_op_ext32u_i64: + tgen_ext32u(s, args[0], args[1]); + break; + + case INDEX_op_add2_i64: + if (const_args[4]) { + if ((int64_t)args[4] >= 0) { + tcg_out_insn(s, RIL, ALGFI, args[0], args[4]); + } else { + tcg_out_insn(s, RIL, SLGFI, args[0], -args[4]); + } + } else { + tcg_out_insn(s, RRE, ALGR, args[0], args[4]); + } + tcg_out_insn(s, RRE, ALCGR, args[1], args[5]); + break; + case INDEX_op_sub2_i64: + if (const_args[4]) { + if ((int64_t)args[4] >= 0) { + tcg_out_insn(s, RIL, SLGFI, args[0], args[4]); + } else { + tcg_out_insn(s, RIL, ALGFI, args[0], -args[4]); + } + } else { + tcg_out_insn(s, RRE, SLGR, args[0], args[4]); + } + tcg_out_insn(s, RRE, SLBGR, args[1], args[5]); + break; + + case INDEX_op_brcond_i64: + tgen_brcond(s, TCG_TYPE_I64, args[2], args[0], + args[1], const_args[1], arg_label(args[3])); + break; + case INDEX_op_setcond_i64: + tgen_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1], + args[2], const_args[2]); + break; + case INDEX_op_movcond_i64: + tgen_movcond(s, TCG_TYPE_I64, args[5], args[0], args[1], + args[2], const_args[2], args[3]); + break; + + OP_32_64(deposit): + tgen_deposit(s, args[0], args[2], args[3], args[4]); + break; + + case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ + case INDEX_op_mov_i64: + case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ + case INDEX_op_movi_i64: + case INDEX_op_call: /* Always emitted via tcg_out_call. */ + default: + tcg_abort(); + } +} + +static const TCGTargetOpDef s390_op_defs[] = { + { INDEX_op_exit_tb, { } }, + { INDEX_op_goto_tb, { } }, + { INDEX_op_br, { } }, + + { INDEX_op_ld8u_i32, { "r", "r" } }, + { INDEX_op_ld8s_i32, { "r", "r" } }, + { INDEX_op_ld16u_i32, { "r", "r" } }, + { INDEX_op_ld16s_i32, { "r", "r" } }, + { INDEX_op_ld_i32, { "r", "r" } }, + { INDEX_op_st8_i32, { "r", "r" } }, + { INDEX_op_st16_i32, { "r", "r" } }, + { INDEX_op_st_i32, { "r", "r" } }, + + { INDEX_op_add_i32, { "r", "r", "ri" } }, + { INDEX_op_sub_i32, { "r", "0", "ri" } }, + { INDEX_op_mul_i32, { "r", "0", "rK" } }, + + { INDEX_op_div2_i32, { "b", "a", "0", "1", "r" } }, + { INDEX_op_divu2_i32, { "b", "a", "0", "1", "r" } }, + + { INDEX_op_and_i32, { "r", "0", "ri" } }, + { INDEX_op_or_i32, { "r", "0", "rO" } }, + { INDEX_op_xor_i32, { "r", "0", "rX" } }, + + { INDEX_op_neg_i32, { "r", "r" } }, + + { INDEX_op_shl_i32, { "r", "0", "Ri" } }, + { INDEX_op_shr_i32, { "r", "0", "Ri" } }, + { INDEX_op_sar_i32, { "r", "0", "Ri" } }, + + { INDEX_op_rotl_i32, { "r", "r", "Ri" } }, + { INDEX_op_rotr_i32, { "r", "r", "Ri" } }, + + { INDEX_op_ext8s_i32, { "r", "r" } }, + { INDEX_op_ext8u_i32, { "r", "r" } }, + { INDEX_op_ext16s_i32, { "r", "r" } }, + { INDEX_op_ext16u_i32, { "r", "r" } }, + + { INDEX_op_bswap16_i32, { "r", "r" } }, + { INDEX_op_bswap32_i32, { "r", "r" } }, + + { INDEX_op_add2_i32, { "r", "r", "0", "1", "rA", "r" } }, + { INDEX_op_sub2_i32, { "r", "r", "0", "1", "rA", "r" } }, + + { INDEX_op_brcond_i32, { "r", "rC" } }, + { INDEX_op_setcond_i32, { "r", "r", "rC" } }, + { INDEX_op_movcond_i32, { "r", "r", "rC", "r", "0" } }, + { INDEX_op_deposit_i32, { "r", "0", "r" } }, + + { INDEX_op_qemu_ld_i32, { "r", "L" } }, + { INDEX_op_qemu_ld_i64, { "r", "L" } }, + { INDEX_op_qemu_st_i32, { "L", "L" } }, + { INDEX_op_qemu_st_i64, { "L", "L" } }, + + { INDEX_op_ld8u_i64, { "r", "r" } }, + { INDEX_op_ld8s_i64, { "r", "r" } }, + { INDEX_op_ld16u_i64, { "r", "r" } }, + { INDEX_op_ld16s_i64, { "r", "r" } }, + { INDEX_op_ld32u_i64, { "r", "r" } }, + { INDEX_op_ld32s_i64, { "r", "r" } }, + { INDEX_op_ld_i64, { "r", "r" } }, + + { INDEX_op_st8_i64, { "r", "r" } }, + { INDEX_op_st16_i64, { "r", "r" } }, + { INDEX_op_st32_i64, { "r", "r" } }, + { INDEX_op_st_i64, { "r", "r" } }, + + { INDEX_op_add_i64, { "r", "r", "ri" } }, + { INDEX_op_sub_i64, { "r", "0", "ri" } }, + { INDEX_op_mul_i64, { "r", "0", "rK" } }, + + { INDEX_op_div2_i64, { "b", "a", "0", "1", "r" } }, + { INDEX_op_divu2_i64, { "b", "a", "0", "1", "r" } }, + { INDEX_op_mulu2_i64, { "b", "a", "0", "r" } }, + + { INDEX_op_and_i64, { "r", "0", "ri" } }, + { INDEX_op_or_i64, { "r", "0", "rO" } }, + { INDEX_op_xor_i64, { "r", "0", "rX" } }, + + { INDEX_op_neg_i64, { "r", "r" } }, + + { INDEX_op_shl_i64, { "r", "r", "Ri" } }, + { INDEX_op_shr_i64, { "r", "r", "Ri" } }, + { INDEX_op_sar_i64, { "r", "r", "Ri" } }, + + { INDEX_op_rotl_i64, { "r", "r", "Ri" } }, + { INDEX_op_rotr_i64, { "r", "r", "Ri" } }, + + { INDEX_op_ext8s_i64, { "r", "r" } }, + { INDEX_op_ext8u_i64, { "r", "r" } }, + { INDEX_op_ext16s_i64, { "r", "r" } }, + { INDEX_op_ext16u_i64, { "r", "r" } }, + { INDEX_op_ext32s_i64, { "r", "r" } }, + { INDEX_op_ext32u_i64, { "r", "r" } }, + + { INDEX_op_ext_i32_i64, { "r", "r" } }, + { INDEX_op_extu_i32_i64, { "r", "r" } }, + + { INDEX_op_bswap16_i64, { "r", "r" } }, + { INDEX_op_bswap32_i64, { "r", "r" } }, + { INDEX_op_bswap64_i64, { "r", "r" } }, + + { INDEX_op_add2_i64, { "r", "r", "0", "1", "rA", "r" } }, + { INDEX_op_sub2_i64, { "r", "r", "0", "1", "rA", "r" } }, + + { INDEX_op_brcond_i64, { "r", "rC" } }, + { INDEX_op_setcond_i64, { "r", "r", "rC" } }, + { INDEX_op_movcond_i64, { "r", "r", "rC", "r", "0" } }, + { INDEX_op_deposit_i64, { "r", "0", "r" } }, + + { -1 }, +}; + +static void query_facilities(void) +{ + unsigned long hwcap = qemu_getauxval(AT_HWCAP); + + /* Is STORE FACILITY LIST EXTENDED available? Honestly, I believe this + is present on all 64-bit systems, but let's check for it anyway. */ + if (hwcap & HWCAP_S390_STFLE) { + register int r0 __asm__("0"); + register void *r1 __asm__("1"); + + /* stfle 0(%r1) */ + r1 = &facilities; + asm volatile(".word 0xb2b0,0x1000" + : "=r"(r0) : "0"(0), "r"(r1) : "memory", "cc"); + } +} + +static void tcg_target_init(TCGContext *s) +{ + query_facilities(); + + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff); + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffff); + + tcg_regset_clear(tcg_target_call_clobber_regs); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R0); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R1); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R2); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R3); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R4); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R5); + /* The r6 register is technically call-saved, but it's also a parameter + register, so it can get killed by setup for the qemu_st helper. */ + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R6); + /* The return register can be considered call-clobbered. */ + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R14); + + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_TMP0); + /* XXX many insns can't be used with R0, so we better avoid it for now */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); + + tcg_add_target_add_op_defs(s390_op_defs); +} + +#define FRAME_SIZE ((int)(TCG_TARGET_CALL_STACK_OFFSET \ + + TCG_STATIC_CALL_ARGS_SIZE \ + + CPU_TEMP_BUF_NLONGS * sizeof(long))) + +static void tcg_target_qemu_prologue(TCGContext *s) +{ + /* stmg %r6,%r15,48(%r15) (save registers) */ + tcg_out_insn(s, RXY, STMG, TCG_REG_R6, TCG_REG_R15, TCG_REG_R15, 48); + + /* aghi %r15,-frame_size */ + tcg_out_insn(s, RI, AGHI, TCG_REG_R15, -FRAME_SIZE); + + tcg_set_frame(s, TCG_REG_CALL_STACK, + TCG_STATIC_CALL_ARGS_SIZE + TCG_TARGET_CALL_STACK_OFFSET, + CPU_TEMP_BUF_NLONGS * sizeof(long)); + +#ifndef CONFIG_SOFTMMU + if (guest_base >= 0x80000) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, guest_base); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + } +#endif + + tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); + /* br %r3 (go to TB) */ + tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, tcg_target_call_iarg_regs[1]); + + tb_ret_addr = s->code_ptr; + + /* lmg %r6,%r15,fs+48(%r15) (restore registers) */ + tcg_out_insn(s, RXY, LMG, TCG_REG_R6, TCG_REG_R15, TCG_REG_R15, + FRAME_SIZE + 48); + + /* br %r14 (return) */ + tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_REG_R14); +} + +typedef struct { + DebugFrameHeader h; + uint8_t fde_def_cfa[4]; + uint8_t fde_reg_ofs[18]; +} DebugFrame; + +/* We're expecting a 2 byte uleb128 encoded value. */ +QEMU_BUILD_BUG_ON(FRAME_SIZE >= (1 << 14)); + +#define ELF_HOST_MACHINE EM_S390 + +static const DebugFrame debug_frame = { + .h.cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ + .h.cie.id = -1, + .h.cie.version = 1, + .h.cie.code_align = 1, + .h.cie.data_align = 8, /* sleb128 8 */ + .h.cie.return_column = TCG_REG_R14, + + /* Total FDE size does not include the "len" member. */ + .h.fde.len = sizeof(DebugFrame) - offsetof(DebugFrame, h.fde.cie_offset), + + .fde_def_cfa = { + 12, TCG_REG_CALL_STACK, /* DW_CFA_def_cfa %r15, ... */ + (FRAME_SIZE & 0x7f) | 0x80, /* ... uleb128 FRAME_SIZE */ + (FRAME_SIZE >> 7) + }, + .fde_reg_ofs = { + 0x86, 6, /* DW_CFA_offset, %r6, 48 */ + 0x87, 7, /* DW_CFA_offset, %r7, 56 */ + 0x88, 8, /* DW_CFA_offset, %r8, 64 */ + 0x89, 9, /* DW_CFA_offset, %r92, 72 */ + 0x8a, 10, /* DW_CFA_offset, %r10, 80 */ + 0x8b, 11, /* DW_CFA_offset, %r11, 88 */ + 0x8c, 12, /* DW_CFA_offset, %r12, 96 */ + 0x8d, 13, /* DW_CFA_offset, %r13, 104 */ + 0x8e, 14, /* DW_CFA_offset, %r14, 112 */ + } +}; + +void tcg_register_jit(void *buf, size_t buf_size) +{ + tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); +} diff --git a/qemu/tcg/sparc/tcg-target.c b/qemu/tcg/sparc/tcg-target.c deleted file mode 100644 index 1a870a81d..000000000 --- a/qemu/tcg/sparc/tcg-target.c +++ /dev/null @@ -1,1650 +0,0 @@ -/* - * Tiny Code Generator for QEMU - * - * Copyright (c) 2008 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "tcg-be-null.h" - -#ifndef NDEBUG -static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { - "%g0", - "%g1", - "%g2", - "%g3", - "%g4", - "%g5", - "%g6", - "%g7", - "%o0", - "%o1", - "%o2", - "%o3", - "%o4", - "%o5", - "%o6", - "%o7", - "%l0", - "%l1", - "%l2", - "%l3", - "%l4", - "%l5", - "%l6", - "%l7", - "%i0", - "%i1", - "%i2", - "%i3", - "%i4", - "%i5", - "%i6", - "%i7", -}; -#endif - -#ifdef __arch64__ -# define SPARC64 1 -#else -# define SPARC64 0 -#endif - -/* Note that sparcv8plus can only hold 64 bit quantities in %g and %o - registers. These are saved manually by the kernel in full 64-bit - slots. The %i and %l registers are saved by the register window - mechanism, which only allocates space for 32 bits. Given that this - window spill/fill can happen on any signal, we must consider the - high bits of the %i and %l registers garbage at all times. */ -#if SPARC64 -# define ALL_64 0xffffffffu -#else -# define ALL_64 0xffffu -#endif - -/* Define some temporary registers. T2 is used for constant generation. */ -#define TCG_REG_T1 TCG_REG_G1 -#define TCG_REG_T2 TCG_REG_O7 - -#ifdef CONFIG_USE_GUEST_BASE -# define TCG_GUEST_BASE_REG TCG_REG_I5 -#else -# define TCG_GUEST_BASE_REG TCG_REG_G0 -#endif - -static const int tcg_target_reg_alloc_order[] = { - TCG_REG_L0, - TCG_REG_L1, - TCG_REG_L2, - TCG_REG_L3, - TCG_REG_L4, - TCG_REG_L5, - TCG_REG_L6, - TCG_REG_L7, - - TCG_REG_I0, - TCG_REG_I1, - TCG_REG_I2, - TCG_REG_I3, - TCG_REG_I4, - TCG_REG_I5, - - TCG_REG_G2, - TCG_REG_G3, - TCG_REG_G4, - TCG_REG_G5, - - TCG_REG_O0, - TCG_REG_O1, - TCG_REG_O2, - TCG_REG_O3, - TCG_REG_O4, - TCG_REG_O5, -}; - -static const int tcg_target_call_iarg_regs[6] = { - TCG_REG_O0, - TCG_REG_O1, - TCG_REG_O2, - TCG_REG_O3, - TCG_REG_O4, - TCG_REG_O5, -}; - -static const int tcg_target_call_oarg_regs[] = { - TCG_REG_O0, - TCG_REG_O1, - TCG_REG_O2, - TCG_REG_O3, -}; - -#define INSN_OP(x) ((x) << 30) -#define INSN_OP2(x) ((x) << 22) -#define INSN_OP3(x) ((x) << 19) -#define INSN_OPF(x) ((x) << 5) -#define INSN_RD(x) ((x) << 25) -#define INSN_RS1(x) ((x) << 14) -#define INSN_RS2(x) (x) -#define INSN_ASI(x) ((x) << 5) - -#define INSN_IMM10(x) ((1 << 13) | ((x) & 0x3ff)) -#define INSN_IMM11(x) ((1 << 13) | ((x) & 0x7ff)) -#define INSN_IMM13(x) ((1 << 13) | ((x) & 0x1fff)) -#define INSN_OFF16(x) ((((x) >> 2) & 0x3fff) | ((((x) >> 16) & 3) << 20)) -#define INSN_OFF19(x) (((x) >> 2) & 0x07ffff) -#define INSN_COND(x) ((x) << 25) - -#define COND_N 0x0 -#define COND_E 0x1 -#define COND_LE 0x2 -#define COND_L 0x3 -#define COND_LEU 0x4 -#define COND_CS 0x5 -#define COND_NEG 0x6 -#define COND_VS 0x7 -#define COND_A 0x8 -#define COND_NE 0x9 -#define COND_G 0xa -#define COND_GE 0xb -#define COND_GU 0xc -#define COND_CC 0xd -#define COND_POS 0xe -#define COND_VC 0xf -#define BA (INSN_OP(0) | INSN_COND(COND_A) | INSN_OP2(0x2)) - -#define RCOND_Z 1 -#define RCOND_LEZ 2 -#define RCOND_LZ 3 -#define RCOND_NZ 5 -#define RCOND_GZ 6 -#define RCOND_GEZ 7 - -#define MOVCC_ICC (1 << 18) -#define MOVCC_XCC (1 << 18 | 1 << 12) - -#define BPCC_ICC 0 -#define BPCC_XCC (2 << 20) -#define BPCC_PT (1 << 19) -#define BPCC_PN 0 -#define BPCC_A (1 << 29) - -#define BPR_PT BPCC_PT - -#define ARITH_ADD (INSN_OP(2) | INSN_OP3(0x00)) -#define ARITH_ADDCC (INSN_OP(2) | INSN_OP3(0x10)) -#define ARITH_AND (INSN_OP(2) | INSN_OP3(0x01)) -#define ARITH_ANDN (INSN_OP(2) | INSN_OP3(0x05)) -#define ARITH_OR (INSN_OP(2) | INSN_OP3(0x02)) -#define ARITH_ORCC (INSN_OP(2) | INSN_OP3(0x12)) -#define ARITH_ORN (INSN_OP(2) | INSN_OP3(0x06)) -#define ARITH_XOR (INSN_OP(2) | INSN_OP3(0x03)) -#define ARITH_SUB (INSN_OP(2) | INSN_OP3(0x04)) -#define ARITH_SUBCC (INSN_OP(2) | INSN_OP3(0x14)) -#define ARITH_ADDC (INSN_OP(2) | INSN_OP3(0x08)) -#define ARITH_SUBC (INSN_OP(2) | INSN_OP3(0x0c)) -#define ARITH_UMUL (INSN_OP(2) | INSN_OP3(0x0a)) -#define ARITH_SMUL (INSN_OP(2) | INSN_OP3(0x0b)) -#define ARITH_UDIV (INSN_OP(2) | INSN_OP3(0x0e)) -#define ARITH_SDIV (INSN_OP(2) | INSN_OP3(0x0f)) -#define ARITH_MULX (INSN_OP(2) | INSN_OP3(0x09)) -#define ARITH_UDIVX (INSN_OP(2) | INSN_OP3(0x0d)) -#define ARITH_SDIVX (INSN_OP(2) | INSN_OP3(0x2d)) -#define ARITH_MOVCC (INSN_OP(2) | INSN_OP3(0x2c)) -#define ARITH_MOVR (INSN_OP(2) | INSN_OP3(0x2f)) - -#define ARITH_ADDXC (INSN_OP(2) | INSN_OP3(0x36) | INSN_OPF(0x11)) -#define ARITH_UMULXHI (INSN_OP(2) | INSN_OP3(0x36) | INSN_OPF(0x16)) - -#define SHIFT_SLL (INSN_OP(2) | INSN_OP3(0x25)) -#define SHIFT_SRL (INSN_OP(2) | INSN_OP3(0x26)) -#define SHIFT_SRA (INSN_OP(2) | INSN_OP3(0x27)) - -#define SHIFT_SLLX (INSN_OP(2) | INSN_OP3(0x25) | (1 << 12)) -#define SHIFT_SRLX (INSN_OP(2) | INSN_OP3(0x26) | (1 << 12)) -#define SHIFT_SRAX (INSN_OP(2) | INSN_OP3(0x27) | (1 << 12)) - -#define RDY (INSN_OP(2) | INSN_OP3(0x28) | INSN_RS1(0)) -#define WRY (INSN_OP(2) | INSN_OP3(0x30) | INSN_RD(0)) -#define JMPL (INSN_OP(2) | INSN_OP3(0x38)) -#define RETURN (INSN_OP(2) | INSN_OP3(0x39)) -#define SAVE (INSN_OP(2) | INSN_OP3(0x3c)) -#define RESTORE (INSN_OP(2) | INSN_OP3(0x3d)) -#define SETHI (INSN_OP(0) | INSN_OP2(0x4)) -#define CALL INSN_OP(1) -#define LDUB (INSN_OP(3) | INSN_OP3(0x01)) -#define LDSB (INSN_OP(3) | INSN_OP3(0x09)) -#define LDUH (INSN_OP(3) | INSN_OP3(0x02)) -#define LDSH (INSN_OP(3) | INSN_OP3(0x0a)) -#define LDUW (INSN_OP(3) | INSN_OP3(0x00)) -#define LDSW (INSN_OP(3) | INSN_OP3(0x08)) -#define LDX (INSN_OP(3) | INSN_OP3(0x0b)) -#define STB (INSN_OP(3) | INSN_OP3(0x05)) -#define STH (INSN_OP(3) | INSN_OP3(0x06)) -#define STW (INSN_OP(3) | INSN_OP3(0x04)) -#define STX (INSN_OP(3) | INSN_OP3(0x0e)) -#define LDUBA (INSN_OP(3) | INSN_OP3(0x11)) -#define LDSBA (INSN_OP(3) | INSN_OP3(0x19)) -#define LDUHA (INSN_OP(3) | INSN_OP3(0x12)) -#define LDSHA (INSN_OP(3) | INSN_OP3(0x1a)) -#define LDUWA (INSN_OP(3) | INSN_OP3(0x10)) -#define LDSWA (INSN_OP(3) | INSN_OP3(0x18)) -#define LDXA (INSN_OP(3) | INSN_OP3(0x1b)) -#define STBA (INSN_OP(3) | INSN_OP3(0x15)) -#define STHA (INSN_OP(3) | INSN_OP3(0x16)) -#define STWA (INSN_OP(3) | INSN_OP3(0x14)) -#define STXA (INSN_OP(3) | INSN_OP3(0x1e)) - -#ifndef ASI_PRIMARY_LITTLE -#define ASI_PRIMARY_LITTLE 0x88 -#endif - -#define LDUH_LE (LDUHA | INSN_ASI(ASI_PRIMARY_LITTLE)) -#define LDSH_LE (LDSHA | INSN_ASI(ASI_PRIMARY_LITTLE)) -#define LDUW_LE (LDUWA | INSN_ASI(ASI_PRIMARY_LITTLE)) -#define LDSW_LE (LDSWA | INSN_ASI(ASI_PRIMARY_LITTLE)) -#define LDX_LE (LDXA | INSN_ASI(ASI_PRIMARY_LITTLE)) - -#define STH_LE (STHA | INSN_ASI(ASI_PRIMARY_LITTLE)) -#define STW_LE (STWA | INSN_ASI(ASI_PRIMARY_LITTLE)) -#define STX_LE (STXA | INSN_ASI(ASI_PRIMARY_LITTLE)) - -#ifndef use_vis3_instructions -bool use_vis3_instructions; -#endif - -static inline int check_fit_i64(int64_t val, unsigned int bits) -{ - return val == sextract64(val, 0, bits); -} - -static inline int check_fit_i32(int32_t val, unsigned int bits) -{ - return val == sextract32(val, 0, bits); -} - -#define check_fit_tl check_fit_i64 -#if SPARC64 -# define check_fit_ptr check_fit_i64 -#else -# define check_fit_ptr check_fit_i32 -#endif - -static void patch_reloc(tcg_insn_unit *code_ptr, int type, - intptr_t value, intptr_t addend) -{ - uint32_t insn; - - assert(addend == 0); - value = tcg_ptr_byte_diff((tcg_insn_unit *)value, code_ptr); - - switch (type) { - case R_SPARC_WDISP16: - if (!check_fit_ptr(value >> 2, 16)) { - tcg_abort(); - } - insn = *code_ptr; - insn &= ~INSN_OFF16(-1); - insn |= INSN_OFF16(value); - *code_ptr = insn; - break; - case R_SPARC_WDISP19: - if (!check_fit_ptr(value >> 2, 19)) { - tcg_abort(); - } - insn = *code_ptr; - insn &= ~INSN_OFF19(-1); - insn |= INSN_OFF19(value); - *code_ptr = insn; - break; - default: - tcg_abort(); - } -} - -/* parse target specific constraints */ -static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) -{ - const char *ct_str; - - ct_str = *pct_str; - switch (ct_str[0]) { - case 'r': - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xffffffff); - break; - case 'R': - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, ALL_64); - break; - case 'A': /* qemu_ld/st address constraint */ - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, - TARGET_LONG_BITS == 64 ? ALL_64 : 0xffffffff); - reserve_helpers: - tcg_regset_reset_reg(ct->u.regs, TCG_REG_O0); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_O1); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_O2); - break; - case 's': /* qemu_st data 32-bit constraint */ - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xffffffff); - goto reserve_helpers; - case 'S': /* qemu_st data 64-bit constraint */ - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, ALL_64); - goto reserve_helpers; - case 'I': - ct->ct |= TCG_CT_CONST_S11; - break; - case 'J': - ct->ct |= TCG_CT_CONST_S13; - break; - case 'Z': - ct->ct |= TCG_CT_CONST_ZERO; - break; - default: - return -1; - } - ct_str++; - *pct_str = ct_str; - return 0; -} - -/* test if a constant matches the constraint */ -static inline int tcg_target_const_match(tcg_target_long val, TCGType type, - const TCGArgConstraint *arg_ct) -{ - int ct = arg_ct->ct; - - if (ct & TCG_CT_CONST) { - return 1; - } - - if (type == TCG_TYPE_I32) { - val = (int32_t)val; - } - - if ((ct & TCG_CT_CONST_ZERO) && val == 0) { - return 1; - } else if ((ct & TCG_CT_CONST_S11) && check_fit_tl(val, 11)) { - return 1; - } else if ((ct & TCG_CT_CONST_S13) && check_fit_tl(val, 13)) { - return 1; - } else { - return 0; - } -} - -static inline void tcg_out_arith(TCGContext *s, TCGReg rd, TCGReg rs1, - TCGReg rs2, int op) -{ - tcg_out32(s, op | INSN_RD(rd) | INSN_RS1(rs1) | INSN_RS2(rs2)); -} - -static inline void tcg_out_arithi(TCGContext *s, TCGReg rd, TCGReg rs1, - int32_t offset, int op) -{ - tcg_out32(s, op | INSN_RD(rd) | INSN_RS1(rs1) | INSN_IMM13(offset)); -} - -static void tcg_out_arithc(TCGContext *s, TCGReg rd, TCGReg rs1, - int32_t val2, int val2const, int op) -{ - tcg_out32(s, op | INSN_RD(rd) | INSN_RS1(rs1) - | (val2const ? INSN_IMM13(val2) : INSN_RS2(val2))); -} - -static inline void tcg_out_mov(TCGContext *s, TCGType type, - TCGReg ret, TCGReg arg) -{ - if (ret != arg) { - tcg_out_arith(s, ret, arg, TCG_REG_G0, ARITH_OR); - } -} - -static inline void tcg_out_sethi(TCGContext *s, TCGReg ret, uint32_t arg) -{ - tcg_out32(s, SETHI | INSN_RD(ret) | ((arg & 0xfffffc00) >> 10)); -} - -static inline void tcg_out_movi_imm13(TCGContext *s, TCGReg ret, int32_t arg) -{ - tcg_out_arithi(s, ret, TCG_REG_G0, arg, ARITH_OR); -} - -static void tcg_out_movi(TCGContext *s, TCGType type, - TCGReg ret, tcg_target_long arg) -{ - tcg_target_long hi, lo = (int32_t)arg; - - /* Make sure we test 32-bit constants for imm13 properly. */ - if (type == TCG_TYPE_I32) { - arg = lo; - } - - /* A 13-bit constant sign-extended to 64-bits. */ - if (check_fit_tl(arg, 13)) { - tcg_out_movi_imm13(s, ret, arg); - return; - } - - /* A 32-bit constant, or 32-bit zero-extended to 64-bits. */ - if (type == TCG_TYPE_I32 || arg == (uint32_t)arg) { - tcg_out_sethi(s, ret, arg); - if (arg & 0x3ff) { - tcg_out_arithi(s, ret, ret, arg & 0x3ff, ARITH_OR); - } - return; - } - - /* A 32-bit constant sign-extended to 64-bits. */ - if (arg == lo) { - tcg_out_sethi(s, ret, ~arg); - tcg_out_arithi(s, ret, ret, (arg & 0x3ff) | -0x400, ARITH_XOR); - return; - } - - /* A 64-bit constant decomposed into 2 32-bit pieces. */ - if (check_fit_i32(lo, 13)) { - hi = (arg - lo) >> 32; - tcg_out_movi(s, TCG_TYPE_I32, ret, hi); - tcg_out_arithi(s, ret, ret, 32, SHIFT_SLLX); - tcg_out_arithi(s, ret, ret, lo, ARITH_ADD); - } else { - hi = arg >> 32; - tcg_out_movi(s, TCG_TYPE_I32, ret, hi); - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_T2, lo); - tcg_out_arithi(s, ret, ret, 32, SHIFT_SLLX); - tcg_out_arith(s, ret, ret, TCG_REG_T2, ARITH_OR); - } -} - -static inline void tcg_out_ldst_rr(TCGContext *s, TCGReg data, TCGReg a1, - TCGReg a2, int op) -{ - tcg_out32(s, op | INSN_RD(data) | INSN_RS1(a1) | INSN_RS2(a2)); -} - -static void tcg_out_ldst(TCGContext *s, TCGReg ret, TCGReg addr, - intptr_t offset, int op) -{ - if (check_fit_ptr(offset, 13)) { - tcg_out32(s, op | INSN_RD(ret) | INSN_RS1(addr) | - INSN_IMM13(offset)); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_T1, offset); - tcg_out_ldst_rr(s, ret, addr, TCG_REG_T1, op); - } -} - -static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, - TCGReg arg1, intptr_t arg2) -{ - tcg_out_ldst(s, ret, arg1, arg2, (type == TCG_TYPE_I32 ? LDUW : LDX)); -} - -static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, - TCGReg arg1, intptr_t arg2) -{ - tcg_out_ldst(s, arg, arg1, arg2, (type == TCG_TYPE_I32 ? STW : STX)); -} - -static void tcg_out_ld_ptr(TCGContext *s, TCGReg ret, uintptr_t arg) -{ - tcg_out_movi(s, TCG_TYPE_PTR, ret, arg & ~0x3ff); - tcg_out_ld(s, TCG_TYPE_PTR, ret, ret, arg & 0x3ff); -} - -static inline void tcg_out_sety(TCGContext *s, TCGReg rs) -{ - tcg_out32(s, WRY | INSN_RS1(TCG_REG_G0) | INSN_RS2(rs)); -} - -static inline void tcg_out_rdy(TCGContext *s, TCGReg rd) -{ - tcg_out32(s, RDY | INSN_RD(rd)); -} - -static void tcg_out_div32(TCGContext *s, TCGReg rd, TCGReg rs1, - int32_t val2, int val2const, int uns) -{ - /* Load Y with the sign/zero extension of RS1 to 64-bits. */ - if (uns) { - tcg_out_sety(s, TCG_REG_G0); - } else { - tcg_out_arithi(s, TCG_REG_T1, rs1, 31, SHIFT_SRA); - tcg_out_sety(s, TCG_REG_T1); - } - - tcg_out_arithc(s, rd, rs1, val2, val2const, - uns ? ARITH_UDIV : ARITH_SDIV); -} - -static inline void tcg_out_nop(TCGContext *s) -{ - tcg_out_sethi(s, TCG_REG_G0, 0); -} - -static const uint8_t tcg_cond_to_bcond[] = { - [TCG_COND_EQ] = COND_E, - [TCG_COND_NE] = COND_NE, - [TCG_COND_LT] = COND_L, - [TCG_COND_GE] = COND_GE, - [TCG_COND_LE] = COND_LE, - [TCG_COND_GT] = COND_G, - [TCG_COND_LTU] = COND_CS, - [TCG_COND_GEU] = COND_CC, - [TCG_COND_LEU] = COND_LEU, - [TCG_COND_GTU] = COND_GU, -}; - -static const uint8_t tcg_cond_to_rcond[] = { - [TCG_COND_EQ] = RCOND_Z, - [TCG_COND_NE] = RCOND_NZ, - [TCG_COND_LT] = RCOND_LZ, - [TCG_COND_GT] = RCOND_GZ, - [TCG_COND_LE] = RCOND_LEZ, - [TCG_COND_GE] = RCOND_GEZ -}; - -static void tcg_out_bpcc0(TCGContext *s, int scond, int flags, int off19) -{ - tcg_out32(s, INSN_OP(0) | INSN_OP2(1) | INSN_COND(scond) | flags | off19); -} - -static void tcg_out_bpcc(TCGContext *s, int scond, int flags, TCGLabel *l) -{ - int off19; - - if (l->has_value) { - off19 = INSN_OFF19(tcg_pcrel_diff(s, l->u.value_ptr)); - } else { - /* Make sure to preserve destinations during retranslation. */ - off19 = *s->code_ptr & INSN_OFF19(-1); - tcg_out_reloc(s, s->code_ptr, R_SPARC_WDISP19, l, 0); - } - tcg_out_bpcc0(s, scond, flags, off19); -} - -static void tcg_out_cmp(TCGContext *s, TCGReg c1, int32_t c2, int c2const) -{ - tcg_out_arithc(s, TCG_REG_G0, c1, c2, c2const, ARITH_SUBCC); -} - -static void tcg_out_brcond_i32(TCGContext *s, TCGCond cond, TCGReg arg1, - int32_t arg2, int const_arg2, TCGLabel *l) -{ - tcg_out_cmp(s, arg1, arg2, const_arg2); - tcg_out_bpcc(s, tcg_cond_to_bcond[cond], BPCC_ICC | BPCC_PT, l); - tcg_out_nop(s); -} - -static void tcg_out_movcc(TCGContext *s, TCGCond cond, int cc, TCGReg ret, - int32_t v1, int v1const) -{ - tcg_out32(s, ARITH_MOVCC | cc | INSN_RD(ret) - | INSN_RS1(tcg_cond_to_bcond[cond]) - | (v1const ? INSN_IMM11(v1) : INSN_RS2(v1))); -} - -static void tcg_out_movcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg c1, int32_t c2, int c2const, - int32_t v1, int v1const) -{ - tcg_out_cmp(s, c1, c2, c2const); - tcg_out_movcc(s, cond, MOVCC_ICC, ret, v1, v1const); -} - -static void tcg_out_brcond_i64(TCGContext *s, TCGCond cond, TCGReg arg1, - int32_t arg2, int const_arg2, TCGLabel *l) -{ - /* For 64-bit signed comparisons vs zero, we can avoid the compare. */ - if (arg2 == 0 && !is_unsigned_cond(cond)) { - int off16; - - if (l->has_value) { - off16 = INSN_OFF16(tcg_pcrel_diff(s, l->u.value_ptr)); - } else { - /* Make sure to preserve destinations during retranslation. */ - off16 = *s->code_ptr & INSN_OFF16(-1); - tcg_out_reloc(s, s->code_ptr, R_SPARC_WDISP16, l, 0); - } - tcg_out32(s, INSN_OP(0) | INSN_OP2(3) | BPR_PT | INSN_RS1(arg1) - | INSN_COND(tcg_cond_to_rcond[cond]) | off16); - } else { - tcg_out_cmp(s, arg1, arg2, const_arg2); - tcg_out_bpcc(s, tcg_cond_to_bcond[cond], BPCC_XCC | BPCC_PT, l); - } - tcg_out_nop(s); -} - -static void tcg_out_movr(TCGContext *s, TCGCond cond, TCGReg ret, TCGReg c1, - int32_t v1, int v1const) -{ - tcg_out32(s, ARITH_MOVR | INSN_RD(ret) | INSN_RS1(c1) - | (tcg_cond_to_rcond[cond] << 10) - | (v1const ? INSN_IMM10(v1) : INSN_RS2(v1))); -} - -static void tcg_out_movcond_i64(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg c1, int32_t c2, int c2const, - int32_t v1, int v1const) -{ - /* For 64-bit signed comparisons vs zero, we can avoid the compare. - Note that the immediate range is one bit smaller, so we must check - for that as well. */ - if (c2 == 0 && !is_unsigned_cond(cond) - && (!v1const || check_fit_i32(v1, 10))) { - tcg_out_movr(s, cond, ret, c1, v1, v1const); - } else { - tcg_out_cmp(s, c1, c2, c2const); - tcg_out_movcc(s, cond, MOVCC_XCC, ret, v1, v1const); - } -} - -static void tcg_out_setcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg c1, int32_t c2, int c2const) -{ - /* For 32-bit comparisons, we can play games with ADDC/SUBC. */ - switch (cond) { - case TCG_COND_LTU: - case TCG_COND_GEU: - /* The result of the comparison is in the carry bit. */ - break; - - case TCG_COND_EQ: - case TCG_COND_NE: - /* For equality, we can transform to inequality vs zero. */ - if (c2 != 0) { - tcg_out_arithc(s, TCG_REG_T1, c1, c2, c2const, ARITH_XOR); - c2 = TCG_REG_T1; - } else { - c2 = c1; - } - c1 = TCG_REG_G0, c2const = 0; - cond = (cond == TCG_COND_EQ ? TCG_COND_GEU : TCG_COND_LTU); - break; - - case TCG_COND_GTU: - case TCG_COND_LEU: - /* If we don't need to load a constant into a register, we can - swap the operands on GTU/LEU. There's no benefit to loading - the constant into a temporary register. */ - if (!c2const || c2 == 0) { - TCGReg t = c1; - c1 = c2; - c2 = t; - c2const = 0; - cond = tcg_swap_cond(cond); - break; - } - /* FALLTHRU */ - - default: - tcg_out_cmp(s, c1, c2, c2const); - tcg_out_movi_imm13(s, ret, 0); - tcg_out_movcc(s, cond, MOVCC_ICC, ret, 1, 1); - return; - } - - tcg_out_cmp(s, c1, c2, c2const); - if (cond == TCG_COND_LTU) { - tcg_out_arithi(s, ret, TCG_REG_G0, 0, ARITH_ADDC); - } else { - tcg_out_arithi(s, ret, TCG_REG_G0, -1, ARITH_SUBC); - } -} - -static void tcg_out_setcond_i64(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg c1, int32_t c2, int c2const) -{ - if (use_vis3_instructions) { - switch (cond) { - case TCG_COND_NE: - if (c2 != 0) { - break; - } - c2 = c1, c2const = 0, c1 = TCG_REG_G0; - /* FALLTHRU */ - case TCG_COND_LTU: - tcg_out_cmp(s, c1, c2, c2const); - tcg_out_arith(s, ret, TCG_REG_G0, TCG_REG_G0, ARITH_ADDXC); - return; - default: - break; - } - } - - /* For 64-bit signed comparisons vs zero, we can avoid the compare - if the input does not overlap the output. */ - if (c2 == 0 && !is_unsigned_cond(cond) && c1 != ret) { - tcg_out_movi_imm13(s, ret, 0); - tcg_out_movr(s, cond, ret, c1, 1, 1); - } else { - tcg_out_cmp(s, c1, c2, c2const); - tcg_out_movi_imm13(s, ret, 0); - tcg_out_movcc(s, cond, MOVCC_XCC, ret, 1, 1); - } -} - -static void tcg_out_addsub2_i32(TCGContext *s, TCGReg rl, TCGReg rh, - TCGReg al, TCGReg ah, int32_t bl, int blconst, - int32_t bh, int bhconst, int opl, int oph) -{ - TCGReg tmp = TCG_REG_T1; - - /* Note that the low parts are fully consumed before tmp is set. */ - if (rl != ah && (bhconst || rl != bh)) { - tmp = rl; - } - - tcg_out_arithc(s, tmp, al, bl, blconst, opl); - tcg_out_arithc(s, rh, ah, bh, bhconst, oph); - tcg_out_mov(s, TCG_TYPE_I32, rl, tmp); -} - -static void tcg_out_addsub2_i64(TCGContext *s, TCGReg rl, TCGReg rh, - TCGReg al, TCGReg ah, int32_t bl, int blconst, - int32_t bh, int bhconst, bool is_sub) -{ - TCGReg tmp = TCG_REG_T1; - - /* Note that the low parts are fully consumed before tmp is set. */ - if (rl != ah && (bhconst || rl != bh)) { - tmp = rl; - } - - tcg_out_arithc(s, tmp, al, bl, blconst, is_sub ? ARITH_SUBCC : ARITH_ADDCC); - - if (use_vis3_instructions && !is_sub) { - /* Note that ADDXC doesn't accept immediates. */ - if (bhconst && bh != 0) { - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_T2, bh); - bh = TCG_REG_T2; - } - tcg_out_arith(s, rh, ah, bh, ARITH_ADDXC); - } else if (bh == TCG_REG_G0) { - /* If we have a zero, we can perform the operation in two insns, - with the arithmetic first, and a conditional move into place. */ - if (rh == ah) { - tcg_out_arithi(s, TCG_REG_T2, ah, 1, - is_sub ? ARITH_SUB : ARITH_ADD); - tcg_out_movcc(s, TCG_COND_LTU, MOVCC_XCC, rh, TCG_REG_T2, 0); - } else { - tcg_out_arithi(s, rh, ah, 1, is_sub ? ARITH_SUB : ARITH_ADD); - tcg_out_movcc(s, TCG_COND_GEU, MOVCC_XCC, rh, ah, 0); - } - } else { - /* Otherwise adjust BH as if there is carry into T2 ... */ - if (bhconst) { - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_T2, bh + (is_sub ? -1 : 1)); - } else { - tcg_out_arithi(s, TCG_REG_T2, bh, 1, - is_sub ? ARITH_SUB : ARITH_ADD); - } - /* ... smoosh T2 back to original BH if carry is clear ... */ - tcg_out_movcc(s, TCG_COND_GEU, MOVCC_XCC, TCG_REG_T2, bh, bhconst); - /* ... and finally perform the arithmetic with the new operand. */ - tcg_out_arith(s, rh, ah, TCG_REG_T2, is_sub ? ARITH_SUB : ARITH_ADD); - } - - tcg_out_mov(s, TCG_TYPE_I64, rl, tmp); -} - -static void tcg_out_call_nodelay(TCGContext *s, tcg_insn_unit *dest) -{ - ptrdiff_t disp = tcg_pcrel_diff(s, dest); - - if (disp == (int32_t)disp) { - tcg_out32(s, CALL | (uint32_t)disp >> 2); - } else { - uintptr_t desti = (uintptr_t)dest; - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_T1, desti & ~0xfff); - tcg_out_arithi(s, TCG_REG_O7, TCG_REG_T1, desti & 0xfff, JMPL); - } -} - -static void tcg_out_call(TCGContext *s, tcg_insn_unit *dest) -{ - tcg_out_call_nodelay(s, dest); - tcg_out_nop(s); -} - -#ifdef CONFIG_SOFTMMU -static tcg_insn_unit *qemu_ld_trampoline[16]; -static tcg_insn_unit *qemu_st_trampoline[16]; - -static void build_trampolines(TCGContext *s) -{ - static void * const qemu_ld_helpers[16] = { - [MO_UB] = helper_ret_ldub_mmu, - [MO_SB] = helper_ret_ldsb_mmu, - [MO_LEUW] = helper_le_lduw_mmu, - [MO_LESW] = helper_le_ldsw_mmu, - [MO_LEUL] = helper_le_ldul_mmu, - [MO_LEQ] = helper_le_ldq_mmu, - [MO_BEUW] = helper_be_lduw_mmu, - [MO_BESW] = helper_be_ldsw_mmu, - [MO_BEUL] = helper_be_ldul_mmu, - [MO_BEQ] = helper_be_ldq_mmu, - }; - static void * const qemu_st_helpers[16] = { - [MO_UB] = helper_ret_stb_mmu, - [MO_LEUW] = helper_le_stw_mmu, - [MO_LEUL] = helper_le_stl_mmu, - [MO_LEQ] = helper_le_stq_mmu, - [MO_BEUW] = helper_be_stw_mmu, - [MO_BEUL] = helper_be_stl_mmu, - [MO_BEQ] = helper_be_stq_mmu, - }; - - int i; - TCGReg ra; - - for (i = 0; i < 16; ++i) { - if (qemu_ld_helpers[i] == NULL) { - continue; - } - - /* May as well align the trampoline. */ - while ((uintptr_t)s->code_ptr & 15) { - tcg_out_nop(s); - } - qemu_ld_trampoline[i] = s->code_ptr; - - if (SPARC64 || TARGET_LONG_BITS == 32) { - ra = TCG_REG_O3; - } else { - /* Install the high part of the address. */ - tcg_out_arithi(s, TCG_REG_O1, TCG_REG_O2, 32, SHIFT_SRLX); - ra = TCG_REG_O4; - } - - /* Set the retaddr operand. */ - tcg_out_mov(s, TCG_TYPE_PTR, ra, TCG_REG_O7); - /* Set the env operand. */ - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_O0, TCG_AREG0); - /* Tail call. */ - tcg_out_call_nodelay(s, qemu_ld_helpers[i]); - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_O7, ra); - } - - for (i = 0; i < 16; ++i) { - if (qemu_st_helpers[i] == NULL) { - continue; - } - - /* May as well align the trampoline. */ - while ((uintptr_t)s->code_ptr & 15) { - tcg_out_nop(s); - } - qemu_st_trampoline[i] = s->code_ptr; - - if (SPARC64) { - ra = TCG_REG_O4; - } else { - ra = TCG_REG_O1; - if (TARGET_LONG_BITS == 64) { - /* Install the high part of the address. */ - tcg_out_arithi(s, ra, ra + 1, 32, SHIFT_SRLX); - ra += 2; - } else { - ra += 1; - } - if ((i & MO_SIZE) == MO_64) { - /* Install the high part of the data. */ - tcg_out_arithi(s, ra, ra + 1, 32, SHIFT_SRLX); - ra += 2; - } else { - ra += 1; - } - /* Skip the oi argument. */ - ra += 1; - } - - /* Set the retaddr operand. */ - if (ra >= TCG_REG_O6) { - tcg_out_st(s, TCG_TYPE_PTR, TCG_REG_O7, TCG_REG_CALL_STACK, - TCG_TARGET_CALL_STACK_OFFSET); - ra = TCG_REG_G1; - } - tcg_out_mov(s, TCG_TYPE_PTR, ra, TCG_REG_O7); - /* Set the env operand. */ - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_O0, TCG_AREG0); - /* Tail call. */ - tcg_out_call_nodelay(s, qemu_st_helpers[i]); - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_O7, ra); - } -} -#endif - -/* Generate global QEMU prologue and epilogue code */ -static void tcg_target_qemu_prologue(TCGContext *s) -{ - int tmp_buf_size, frame_size; - - /* The TCG temp buffer is at the top of the frame, immediately - below the frame pointer. */ - tmp_buf_size = CPU_TEMP_BUF_NLONGS * (int)sizeof(long); - tcg_set_frame(s, TCG_REG_I6, TCG_TARGET_STACK_BIAS - tmp_buf_size, - tmp_buf_size); - - /* TCG_TARGET_CALL_STACK_OFFSET includes the stack bias, but is - otherwise the minimal frame usable by callees. */ - frame_size = TCG_TARGET_CALL_STACK_OFFSET - TCG_TARGET_STACK_BIAS; - frame_size += TCG_STATIC_CALL_ARGS_SIZE + tmp_buf_size; - frame_size += TCG_TARGET_STACK_ALIGN - 1; - frame_size &= -TCG_TARGET_STACK_ALIGN; - tcg_out32(s, SAVE | INSN_RD(TCG_REG_O6) | INSN_RS1(TCG_REG_O6) | - INSN_IMM13(-frame_size)); - -#ifdef CONFIG_USE_GUEST_BASE - if (GUEST_BASE != 0) { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, GUEST_BASE); - tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); - } -#endif - - tcg_out_arithi(s, TCG_REG_G0, TCG_REG_I1, 0, JMPL); - /* delay slot */ - tcg_out_nop(s); - - /* No epilogue required. We issue ret + restore directly in the TB. */ - -#ifdef CONFIG_SOFTMMU - build_trampolines(s); -#endif -} - -#if defined(CONFIG_SOFTMMU) -/* Perform the TLB load and compare. - - Inputs: - ADDRLO and ADDRHI contain the possible two parts of the address. - - MEM_INDEX and S_BITS are the memory context and log2 size of the load. - - WHICH is the offset into the CPUTLBEntry structure of the slot to read. - This should be offsetof addr_read or addr_write. - - The result of the TLB comparison is in %[ix]cc. The sanitized address - is in the returned register, maybe %o0. The TLB addend is in %o1. */ - -static TCGReg tcg_out_tlb_load(TCGContext *s, TCGReg addr, int mem_index, - TCGMemOp s_bits, int which) -{ - const TCGReg r0 = TCG_REG_O0; - const TCGReg r1 = TCG_REG_O1; - const TCGReg r2 = TCG_REG_O2; - int tlb_ofs; - - /* Shift the page number down. */ - tcg_out_arithi(s, r1, addr, TARGET_PAGE_BITS, SHIFT_SRL); - - /* Mask out the page offset, except for the required alignment. */ - tcg_out_movi(s, TCG_TYPE_TL, TCG_REG_T1, - TARGET_PAGE_MASK | ((1 << s_bits) - 1)); - - /* Mask the tlb index. */ - tcg_out_arithi(s, r1, r1, CPU_TLB_SIZE - 1, ARITH_AND); - - /* Mask page, part 2. */ - tcg_out_arith(s, r0, addr, TCG_REG_T1, ARITH_AND); - - /* Shift the tlb index into place. */ - tcg_out_arithi(s, r1, r1, CPU_TLB_ENTRY_BITS, SHIFT_SLL); - - /* Relative to the current ENV. */ - tcg_out_arith(s, r1, TCG_AREG0, r1, ARITH_ADD); - - /* Find a base address that can load both tlb comparator and addend. */ - tlb_ofs = offsetof(CPUArchState, tlb_table[mem_index][0]); - if (!check_fit_ptr(tlb_ofs + sizeof(CPUTLBEntry), 13)) { - if (tlb_ofs & ~0x3ff) { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_T1, tlb_ofs & ~0x3ff); - tcg_out_arith(s, r1, r1, TCG_REG_T1, ARITH_ADD); - } - tlb_ofs &= 0x3ff; - } - - /* Load the tlb comparator and the addend. */ - tcg_out_ld(s, TCG_TYPE_TL, r2, r1, tlb_ofs + which); - tcg_out_ld(s, TCG_TYPE_PTR, r1, r1, tlb_ofs+offsetof(CPUTLBEntry, addend)); - - /* subcc arg0, arg2, %g0 */ - tcg_out_cmp(s, r0, r2, 0); - - /* If the guest address must be zero-extended, do so now. */ - if (SPARC64 && TARGET_LONG_BITS == 32) { - tcg_out_arithi(s, r0, addr, 0, SHIFT_SRL); - return r0; - } - return addr; -} -#endif /* CONFIG_SOFTMMU */ - -static const int qemu_ld_opc[16] = { - [MO_UB] = LDUB, - [MO_SB] = LDSB, - - [MO_BEUW] = LDUH, - [MO_BESW] = LDSH, - [MO_BEUL] = LDUW, - [MO_BESL] = LDSW, - [MO_BEQ] = LDX, - - [MO_LEUW] = LDUH_LE, - [MO_LESW] = LDSH_LE, - [MO_LEUL] = LDUW_LE, - [MO_LESL] = LDSW_LE, - [MO_LEQ] = LDX_LE, -}; - -static const int qemu_st_opc[16] = { - [MO_UB] = STB, - - [MO_BEUW] = STH, - [MO_BEUL] = STW, - [MO_BEQ] = STX, - - [MO_LEUW] = STH_LE, - [MO_LEUL] = STW_LE, - [MO_LEQ] = STX_LE, -}; - -static void tcg_out_qemu_ld(TCGContext *s, TCGReg data, TCGReg addr, - TCGMemOpIdx oi, bool is_64) -{ - TCGMemOp memop = get_memop(oi); -#ifdef CONFIG_SOFTMMU - unsigned memi = get_mmuidx(oi); - TCGReg addrz, param; - tcg_insn_unit *func; - tcg_insn_unit *label_ptr; - - addrz = tcg_out_tlb_load(s, addr, memi, memop & MO_SIZE, - offsetof(CPUTLBEntry, addr_read)); - - /* The fast path is exactly one insn. Thus we can perform the - entire TLB Hit in the (annulled) delay slot of the branch - over the TLB Miss case. */ - - /* beq,a,pt %[xi]cc, label0 */ - label_ptr = s->code_ptr; - tcg_out_bpcc0(s, COND_E, BPCC_A | BPCC_PT - | (TARGET_LONG_BITS == 64 ? BPCC_XCC : BPCC_ICC), 0); - /* delay slot */ - tcg_out_ldst_rr(s, data, addrz, TCG_REG_O1, - qemu_ld_opc[memop & (MO_BSWAP | MO_SSIZE)]); - - /* TLB Miss. */ - - param = TCG_REG_O1; - if (!SPARC64 && TARGET_LONG_BITS == 64) { - /* Skip the high-part; we'll perform the extract in the trampoline. */ - param++; - } - tcg_out_mov(s, TCG_TYPE_REG, param++, addr); - - /* We use the helpers to extend SB and SW data, leaving the case - of SL needing explicit extending below. */ - if ((memop & MO_SSIZE) == MO_SL) { - func = qemu_ld_trampoline[memop & (MO_BSWAP | MO_SIZE)]; - } else { - func = qemu_ld_trampoline[memop & (MO_BSWAP | MO_SSIZE)]; - } - assert(func != NULL); - tcg_out_call_nodelay(s, func); - /* delay slot */ - tcg_out_movi(s, TCG_TYPE_I32, param, oi); - - /* Recall that all of the helpers return 64-bit results. - Which complicates things for sparcv8plus. */ - if (SPARC64) { - /* We let the helper sign-extend SB and SW, but leave SL for here. */ - if (is_64 && (memop & MO_SSIZE) == MO_SL) { - tcg_out_arithi(s, data, TCG_REG_O0, 0, SHIFT_SRA); - } else { - tcg_out_mov(s, TCG_TYPE_REG, data, TCG_REG_O0); - } - } else { - if ((memop & MO_SIZE) == MO_64) { - tcg_out_arithi(s, TCG_REG_O0, TCG_REG_O0, 32, SHIFT_SLLX); - tcg_out_arithi(s, TCG_REG_O1, TCG_REG_O1, 0, SHIFT_SRL); - tcg_out_arith(s, data, TCG_REG_O0, TCG_REG_O1, ARITH_OR); - } else if (is_64) { - /* Re-extend from 32-bit rather than reassembling when we - know the high register must be an extension. */ - tcg_out_arithi(s, data, TCG_REG_O1, 0, - memop & MO_SIGN ? SHIFT_SRA : SHIFT_SRL); - } else { - tcg_out_mov(s, TCG_TYPE_I32, data, TCG_REG_O1); - } - } - - *label_ptr |= INSN_OFF19(tcg_ptr_byte_diff(s->code_ptr, label_ptr)); -#else - if (SPARC64 && TARGET_LONG_BITS == 32) { - tcg_out_arithi(s, TCG_REG_T1, addr, 0, SHIFT_SRL); - addr = TCG_REG_T1; - } - tcg_out_ldst_rr(s, data, addr, - (GUEST_BASE ? TCG_GUEST_BASE_REG : TCG_REG_G0), - qemu_ld_opc[memop & (MO_BSWAP | MO_SSIZE)]); -#endif /* CONFIG_SOFTMMU */ -} - -static void tcg_out_qemu_st(TCGContext *s, TCGReg data, TCGReg addr, - TCGMemOpIdx oi) -{ - TCGMemOp memop = get_memop(oi); -#ifdef CONFIG_SOFTMMU - unsigned memi = get_mmuidx(oi); - TCGReg addrz, param; - tcg_insn_unit *func; - tcg_insn_unit *label_ptr; - - addrz = tcg_out_tlb_load(s, addr, memi, memop & MO_SIZE, - offsetof(CPUTLBEntry, addr_write)); - - /* The fast path is exactly one insn. Thus we can perform the entire - TLB Hit in the (annulled) delay slot of the branch over TLB Miss. */ - /* beq,a,pt %[xi]cc, label0 */ - label_ptr = s->code_ptr; - tcg_out_bpcc0(s, COND_E, BPCC_A | BPCC_PT - | (TARGET_LONG_BITS == 64 ? BPCC_XCC : BPCC_ICC), 0); - /* delay slot */ - tcg_out_ldst_rr(s, data, addrz, TCG_REG_O1, - qemu_st_opc[memop & (MO_BSWAP | MO_SIZE)]); - - /* TLB Miss. */ - - param = TCG_REG_O1; - if (!SPARC64 && TARGET_LONG_BITS == 64) { - /* Skip the high-part; we'll perform the extract in the trampoline. */ - param++; - } - tcg_out_mov(s, TCG_TYPE_REG, param++, addr); - if (!SPARC64 && (memop & MO_SIZE) == MO_64) { - /* Skip the high-part; we'll perform the extract in the trampoline. */ - param++; - } - tcg_out_mov(s, TCG_TYPE_REG, param++, data); - - func = qemu_st_trampoline[memop & (MO_BSWAP | MO_SIZE)]; - assert(func != NULL); - tcg_out_call_nodelay(s, func); - /* delay slot */ - tcg_out_movi(s, TCG_TYPE_I32, param, oi); - - *label_ptr |= INSN_OFF19(tcg_ptr_byte_diff(s->code_ptr, label_ptr)); -#else - if (SPARC64 && TARGET_LONG_BITS == 32) { - tcg_out_arithi(s, TCG_REG_T1, addr, 0, SHIFT_SRL); - addr = TCG_REG_T1; - } - tcg_out_ldst_rr(s, data, addr, - (GUEST_BASE ? TCG_GUEST_BASE_REG : TCG_REG_G0), - qemu_st_opc[memop & (MO_BSWAP | MO_SIZE)]); -#endif /* CONFIG_SOFTMMU */ -} - -static void tcg_out_op(TCGContext *s, TCGOpcode opc, - const TCGArg args[TCG_MAX_OP_ARGS], - const int const_args[TCG_MAX_OP_ARGS]) -{ - TCGArg a0, a1, a2; - int c, c2; - - /* Hoist the loads of the most common arguments. */ - a0 = args[0]; - a1 = args[1]; - a2 = args[2]; - c2 = const_args[2]; - - switch (opc) { - case INDEX_op_exit_tb: - if (check_fit_ptr(a0, 13)) { - tcg_out_arithi(s, TCG_REG_G0, TCG_REG_I7, 8, RETURN); - tcg_out_movi_imm13(s, TCG_REG_O0, a0); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_I0, a0 & ~0x3ff); - tcg_out_arithi(s, TCG_REG_G0, TCG_REG_I7, 8, RETURN); - tcg_out_arithi(s, TCG_REG_O0, TCG_REG_O0, a0 & 0x3ff, ARITH_OR); - } - break; - case INDEX_op_goto_tb: - if (s->tb_jmp_offset) { - /* direct jump method */ - s->tb_jmp_offset[a0] = tcg_current_code_size(s); - /* Make sure to preserve links during retranslation. */ - tcg_out32(s, CALL | (*s->code_ptr & ~INSN_OP(-1))); - } else { - /* indirect jump method */ - tcg_out_ld_ptr(s, TCG_REG_T1, (uintptr_t)(s->tb_next + a0)); - tcg_out_arithi(s, TCG_REG_G0, TCG_REG_T1, 0, JMPL); - } - tcg_out_nop(s); - s->tb_next_offset[a0] = tcg_current_code_size(s); - break; - case INDEX_op_br: - tcg_out_bpcc(s, COND_A, BPCC_PT, arg_label(a0)); - tcg_out_nop(s); - break; - -#define OP_32_64(x) \ - glue(glue(case INDEX_op_, x), _i32): \ - glue(glue(case INDEX_op_, x), _i64) - - OP_32_64(ld8u): - tcg_out_ldst(s, a0, a1, a2, LDUB); - break; - OP_32_64(ld8s): - tcg_out_ldst(s, a0, a1, a2, LDSB); - break; - OP_32_64(ld16u): - tcg_out_ldst(s, a0, a1, a2, LDUH); - break; - OP_32_64(ld16s): - tcg_out_ldst(s, a0, a1, a2, LDSH); - break; - case INDEX_op_ld_i32: - case INDEX_op_ld32u_i64: - tcg_out_ldst(s, a0, a1, a2, LDUW); - break; - OP_32_64(st8): - tcg_out_ldst(s, a0, a1, a2, STB); - break; - OP_32_64(st16): - tcg_out_ldst(s, a0, a1, a2, STH); - break; - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - tcg_out_ldst(s, a0, a1, a2, STW); - break; - OP_32_64(add): - c = ARITH_ADD; - goto gen_arith; - OP_32_64(sub): - c = ARITH_SUB; - goto gen_arith; - OP_32_64(and): - c = ARITH_AND; - goto gen_arith; - OP_32_64(andc): - c = ARITH_ANDN; - goto gen_arith; - OP_32_64(or): - c = ARITH_OR; - goto gen_arith; - OP_32_64(orc): - c = ARITH_ORN; - goto gen_arith; - OP_32_64(xor): - c = ARITH_XOR; - goto gen_arith; - case INDEX_op_shl_i32: - c = SHIFT_SLL; - do_shift32: - /* Limit immediate shift count lest we create an illegal insn. */ - tcg_out_arithc(s, a0, a1, a2 & 31, c2, c); - break; - case INDEX_op_shr_i32: - c = SHIFT_SRL; - goto do_shift32; - case INDEX_op_sar_i32: - c = SHIFT_SRA; - goto do_shift32; - case INDEX_op_mul_i32: - c = ARITH_UMUL; - goto gen_arith; - - OP_32_64(neg): - c = ARITH_SUB; - goto gen_arith1; - OP_32_64(not): - c = ARITH_ORN; - goto gen_arith1; - - case INDEX_op_div_i32: - tcg_out_div32(s, a0, a1, a2, c2, 0); - break; - case INDEX_op_divu_i32: - tcg_out_div32(s, a0, a1, a2, c2, 1); - break; - - case INDEX_op_brcond_i32: - tcg_out_brcond_i32(s, a2, a0, a1, const_args[1], arg_label(args[3])); - break; - case INDEX_op_setcond_i32: - tcg_out_setcond_i32(s, args[3], a0, a1, a2, c2); - break; - case INDEX_op_movcond_i32: - tcg_out_movcond_i32(s, args[5], a0, a1, a2, c2, args[3], const_args[3]); - break; - - case INDEX_op_add2_i32: - tcg_out_addsub2_i32(s, args[0], args[1], args[2], args[3], - args[4], const_args[4], args[5], const_args[5], - ARITH_ADDCC, ARITH_ADDC); - break; - case INDEX_op_sub2_i32: - tcg_out_addsub2_i32(s, args[0], args[1], args[2], args[3], - args[4], const_args[4], args[5], const_args[5], - ARITH_SUBCC, ARITH_SUBC); - break; - case INDEX_op_mulu2_i32: - c = ARITH_UMUL; - goto do_mul2; - case INDEX_op_muls2_i32: - c = ARITH_SMUL; - do_mul2: - /* The 32-bit multiply insns produce a full 64-bit result. If the - destination register can hold it, we can avoid the slower RDY. */ - tcg_out_arithc(s, a0, a2, args[3], const_args[3], c); - if (SPARC64 || a0 <= TCG_REG_O7) { - tcg_out_arithi(s, a1, a0, 32, SHIFT_SRLX); - } else { - tcg_out_rdy(s, a1); - } - break; - - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, a0, a1, a2, false); - break; - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, a0, a1, a2, true); - break; - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, a0, a1, a2); - break; - - case INDEX_op_ld32s_i64: - tcg_out_ldst(s, a0, a1, a2, LDSW); - break; - case INDEX_op_ld_i64: - tcg_out_ldst(s, a0, a1, a2, LDX); - break; - case INDEX_op_st_i64: - tcg_out_ldst(s, a0, a1, a2, STX); - break; - case INDEX_op_shl_i64: - c = SHIFT_SLLX; - do_shift64: - /* Limit immediate shift count lest we create an illegal insn. */ - tcg_out_arithc(s, a0, a1, a2 & 63, c2, c); - break; - case INDEX_op_shr_i64: - c = SHIFT_SRLX; - goto do_shift64; - case INDEX_op_sar_i64: - c = SHIFT_SRAX; - goto do_shift64; - case INDEX_op_mul_i64: - c = ARITH_MULX; - goto gen_arith; - case INDEX_op_div_i64: - c = ARITH_SDIVX; - goto gen_arith; - case INDEX_op_divu_i64: - c = ARITH_UDIVX; - goto gen_arith; - case INDEX_op_ext32s_i64: - tcg_out_arithi(s, a0, a1, 0, SHIFT_SRA); - break; - case INDEX_op_ext32u_i64: - tcg_out_arithi(s, a0, a1, 0, SHIFT_SRL); - break; - case INDEX_op_trunc_shr_i32: - if (a2 == 0) { - tcg_out_mov(s, TCG_TYPE_I32, a0, a1); - } else { - tcg_out_arithi(s, a0, a1, a2, SHIFT_SRLX); - } - break; - - case INDEX_op_brcond_i64: - tcg_out_brcond_i64(s, a2, a0, a1, const_args[1], arg_label(args[3])); - break; - case INDEX_op_setcond_i64: - tcg_out_setcond_i64(s, args[3], a0, a1, a2, c2); - break; - case INDEX_op_movcond_i64: - tcg_out_movcond_i64(s, args[5], a0, a1, a2, c2, args[3], const_args[3]); - break; - case INDEX_op_add2_i64: - tcg_out_addsub2_i64(s, args[0], args[1], args[2], args[3], args[4], - const_args[4], args[5], const_args[5], false); - break; - case INDEX_op_sub2_i64: - tcg_out_addsub2_i64(s, args[0], args[1], args[2], args[3], args[4], - const_args[4], args[5], const_args[5], true); - break; - case INDEX_op_muluh_i64: - tcg_out_arith(s, args[0], args[1], args[2], ARITH_UMULXHI); - break; - - gen_arith: - tcg_out_arithc(s, a0, a1, a2, c2, c); - break; - - gen_arith1: - tcg_out_arithc(s, a0, TCG_REG_G0, a1, const_args[1], c); - break; - - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_mov_i64: - case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ - case INDEX_op_movi_i64: - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - default: - tcg_abort(); - } -} - -static const TCGTargetOpDef sparc_op_defs[] = { - { INDEX_op_exit_tb, { } }, - { INDEX_op_goto_tb, { } }, - { INDEX_op_br, { } }, - - { INDEX_op_ld8u_i32, { "r", "r" } }, - { INDEX_op_ld8s_i32, { "r", "r" } }, - { INDEX_op_ld16u_i32, { "r", "r" } }, - { INDEX_op_ld16s_i32, { "r", "r" } }, - { INDEX_op_ld_i32, { "r", "r" } }, - { INDEX_op_st8_i32, { "rZ", "r" } }, - { INDEX_op_st16_i32, { "rZ", "r" } }, - { INDEX_op_st_i32, { "rZ", "r" } }, - - { INDEX_op_add_i32, { "r", "rZ", "rJ" } }, - { INDEX_op_mul_i32, { "r", "rZ", "rJ" } }, - { INDEX_op_div_i32, { "r", "rZ", "rJ" } }, - { INDEX_op_divu_i32, { "r", "rZ", "rJ" } }, - { INDEX_op_sub_i32, { "r", "rZ", "rJ" } }, - { INDEX_op_and_i32, { "r", "rZ", "rJ" } }, - { INDEX_op_andc_i32, { "r", "rZ", "rJ" } }, - { INDEX_op_or_i32, { "r", "rZ", "rJ" } }, - { INDEX_op_orc_i32, { "r", "rZ", "rJ" } }, - { INDEX_op_xor_i32, { "r", "rZ", "rJ" } }, - - { INDEX_op_shl_i32, { "r", "rZ", "rJ" } }, - { INDEX_op_shr_i32, { "r", "rZ", "rJ" } }, - { INDEX_op_sar_i32, { "r", "rZ", "rJ" } }, - - { INDEX_op_neg_i32, { "r", "rJ" } }, - { INDEX_op_not_i32, { "r", "rJ" } }, - - { INDEX_op_brcond_i32, { "rZ", "rJ" } }, - { INDEX_op_setcond_i32, { "r", "rZ", "rJ" } }, - { INDEX_op_movcond_i32, { "r", "rZ", "rJ", "rI", "0" } }, - - { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rJ", "rJ" } }, - { INDEX_op_sub2_i32, { "r", "r", "rZ", "rZ", "rJ", "rJ" } }, - { INDEX_op_mulu2_i32, { "r", "r", "rZ", "rJ" } }, - { INDEX_op_muls2_i32, { "r", "r", "rZ", "rJ" } }, - - { INDEX_op_ld8u_i64, { "R", "r" } }, - { INDEX_op_ld8s_i64, { "R", "r" } }, - { INDEX_op_ld16u_i64, { "R", "r" } }, - { INDEX_op_ld16s_i64, { "R", "r" } }, - { INDEX_op_ld32u_i64, { "R", "r" } }, - { INDEX_op_ld32s_i64, { "R", "r" } }, - { INDEX_op_ld_i64, { "R", "r" } }, - { INDEX_op_st8_i64, { "RZ", "r" } }, - { INDEX_op_st16_i64, { "RZ", "r" } }, - { INDEX_op_st32_i64, { "RZ", "r" } }, - { INDEX_op_st_i64, { "RZ", "r" } }, - - { INDEX_op_add_i64, { "R", "RZ", "RJ" } }, - { INDEX_op_mul_i64, { "R", "RZ", "RJ" } }, - { INDEX_op_div_i64, { "R", "RZ", "RJ" } }, - { INDEX_op_divu_i64, { "R", "RZ", "RJ" } }, - { INDEX_op_sub_i64, { "R", "RZ", "RJ" } }, - { INDEX_op_and_i64, { "R", "RZ", "RJ" } }, - { INDEX_op_andc_i64, { "R", "RZ", "RJ" } }, - { INDEX_op_or_i64, { "R", "RZ", "RJ" } }, - { INDEX_op_orc_i64, { "R", "RZ", "RJ" } }, - { INDEX_op_xor_i64, { "R", "RZ", "RJ" } }, - - { INDEX_op_shl_i64, { "R", "RZ", "RJ" } }, - { INDEX_op_shr_i64, { "R", "RZ", "RJ" } }, - { INDEX_op_sar_i64, { "R", "RZ", "RJ" } }, - - { INDEX_op_neg_i64, { "R", "RJ" } }, - { INDEX_op_not_i64, { "R", "RJ" } }, - - { INDEX_op_ext32s_i64, { "R", "r" } }, - { INDEX_op_ext32u_i64, { "R", "r" } }, - { INDEX_op_trunc_shr_i32, { "r", "R" } }, - - { INDEX_op_brcond_i64, { "RZ", "RJ" } }, - { INDEX_op_setcond_i64, { "R", "RZ", "RJ" } }, - { INDEX_op_movcond_i64, { "R", "RZ", "RJ", "RI", "0" } }, - - { INDEX_op_add2_i64, { "R", "R", "RZ", "RZ", "RJ", "RI" } }, - { INDEX_op_sub2_i64, { "R", "R", "RZ", "RZ", "RJ", "RI" } }, - { INDEX_op_muluh_i64, { "R", "RZ", "RZ" } }, - - { INDEX_op_qemu_ld_i32, { "r", "A" } }, - { INDEX_op_qemu_ld_i64, { "R", "A" } }, - { INDEX_op_qemu_st_i32, { "sZ", "A" } }, - { INDEX_op_qemu_st_i64, { "SZ", "A" } }, - - { -1 }, -}; - -static void tcg_target_init(TCGContext *s) -{ - /* Only probe for the platform and capabilities if we havn't already - determined maximum values at compile time. */ -#ifndef use_vis3_instructions - { - unsigned long hwcap = qemu_getauxval(AT_HWCAP); - use_vis3_instructions = (hwcap & HWCAP_SPARC_VIS3) != 0; - } -#endif - - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, ALL_64); - - tcg_regset_set32(tcg_target_call_clobber_regs, 0, - (1 << TCG_REG_G1) | - (1 << TCG_REG_G2) | - (1 << TCG_REG_G3) | - (1 << TCG_REG_G4) | - (1 << TCG_REG_G5) | - (1 << TCG_REG_G6) | - (1 << TCG_REG_G7) | - (1 << TCG_REG_O0) | - (1 << TCG_REG_O1) | - (1 << TCG_REG_O2) | - (1 << TCG_REG_O3) | - (1 << TCG_REG_O4) | - (1 << TCG_REG_O5) | - (1 << TCG_REG_O7)); - - tcg_regset_clear(s->reserved_regs); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_G0); /* zero */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_G6); /* reserved for os */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_G7); /* thread pointer */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_I6); /* frame pointer */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_I7); /* return address */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_O6); /* stack pointer */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_T1); /* for internal use */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_T2); /* for internal use */ - - tcg_add_target_add_op_defs(sparc_op_defs); -} - -#if SPARC64 -# define ELF_HOST_MACHINE EM_SPARCV9 -#else -# define ELF_HOST_MACHINE EM_SPARC32PLUS -# define ELF_HOST_FLAGS EF_SPARC_32PLUS -#endif - -typedef struct { - DebugFrameHeader h; - uint8_t fde_def_cfa[SPARC64 ? 4 : 2]; - uint8_t fde_win_save; - uint8_t fde_ret_save[3]; -} DebugFrame; - -static const DebugFrame debug_frame = { - .h.cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ - .h.cie.id = -1, - .h.cie.version = 1, - .h.cie.code_align = 1, - .h.cie.data_align = -sizeof(void *) & 0x7f, - .h.cie.return_column = 15, /* o7 */ - - /* Total FDE size does not include the "len" member. */ - .h.fde.len = sizeof(DebugFrame) - offsetof(DebugFrame, h.fde.cie_offset), - - .fde_def_cfa = { -#if SPARC64 - 12, 30, /* DW_CFA_def_cfa i6, 2047 */ - (2047 & 0x7f) | 0x80, (2047 >> 7) -#else - 13, 30 /* DW_CFA_def_cfa_register i6 */ -#endif - }, - .fde_win_save = 0x2d, /* DW_CFA_GNU_window_save */ - .fde_ret_save = { 9, 15, 31 }, /* DW_CFA_register o7, i7 */ -}; - -void tcg_register_jit(void *buf, size_t buf_size) -{ - tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); -} - -void tb_set_jmp_target1(uintptr_t jmp_addr, uintptr_t addr) -{ - uint32_t *ptr = (uint32_t *)jmp_addr; - uintptr_t disp = addr - jmp_addr; - - /* We can reach the entire address space for 32-bit. For 64-bit - the code_gen_buffer can't be larger than 2GB. */ - assert(disp == (int32_t)disp); - - *ptr = CALL | (uint32_t)disp >> 2; - flush_icache_range(jmp_addr, jmp_addr + 4); -} diff --git a/qemu/tcg/sparc/tcg-target.h b/qemu/tcg/sparc/tcg-target.h index f584de476..2cd72d2d4 100644 --- a/qemu/tcg/sparc/tcg-target.h +++ b/qemu/tcg/sparc/tcg-target.h @@ -118,7 +118,8 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_muluh_i32 0 #define TCG_TARGET_HAS_mulsh_i32 0 -#define TCG_TARGET_HAS_trunc_shr_i32 1 +#define TCG_TARGET_HAS_extrl_i64_i32 1 +#define TCG_TARGET_HAS_extrh_i64_i32 1 #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 0 #define TCG_TARGET_HAS_rot_i64 0 diff --git a/qemu/tcg/sparc/tcg-target.inc.c b/qemu/tcg/sparc/tcg-target.inc.c new file mode 100644 index 000000000..d641cfd8c --- /dev/null +++ b/qemu/tcg/sparc/tcg-target.inc.c @@ -0,0 +1,1652 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "tcg-be-null.h" + +#ifdef CONFIG_DEBUG_TCG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "%g0", + "%g1", + "%g2", + "%g3", + "%g4", + "%g5", + "%g6", + "%g7", + "%o0", + "%o1", + "%o2", + "%o3", + "%o4", + "%o5", + "%o6", + "%o7", + "%l0", + "%l1", + "%l2", + "%l3", + "%l4", + "%l5", + "%l6", + "%l7", + "%i0", + "%i1", + "%i2", + "%i3", + "%i4", + "%i5", + "%i6", + "%i7", +}; +#endif + +#ifdef __arch64__ +# define SPARC64 1 +#else +# define SPARC64 0 +#endif + +/* Note that sparcv8plus can only hold 64 bit quantities in %g and %o + registers. These are saved manually by the kernel in full 64-bit + slots. The %i and %l registers are saved by the register window + mechanism, which only allocates space for 32 bits. Given that this + window spill/fill can happen on any signal, we must consider the + high bits of the %i and %l registers garbage at all times. */ +#if SPARC64 +# define ALL_64 0xffffffffu +#else +# define ALL_64 0xffffu +#endif + +/* Define some temporary registers. T2 is used for constant generation. */ +#define TCG_REG_T1 TCG_REG_G1 +#define TCG_REG_T2 TCG_REG_O7 + +#ifndef CONFIG_SOFTMMU +# define TCG_GUEST_BASE_REG TCG_REG_I5 +#endif + +static const int tcg_target_reg_alloc_order[] = { + TCG_REG_L0, + TCG_REG_L1, + TCG_REG_L2, + TCG_REG_L3, + TCG_REG_L4, + TCG_REG_L5, + TCG_REG_L6, + TCG_REG_L7, + + TCG_REG_I0, + TCG_REG_I1, + TCG_REG_I2, + TCG_REG_I3, + TCG_REG_I4, + TCG_REG_I5, + + TCG_REG_G2, + TCG_REG_G3, + TCG_REG_G4, + TCG_REG_G5, + + TCG_REG_O0, + TCG_REG_O1, + TCG_REG_O2, + TCG_REG_O3, + TCG_REG_O4, + TCG_REG_O5, +}; + +static const int tcg_target_call_iarg_regs[6] = { + TCG_REG_O0, + TCG_REG_O1, + TCG_REG_O2, + TCG_REG_O3, + TCG_REG_O4, + TCG_REG_O5, +}; + +static const int tcg_target_call_oarg_regs[] = { + TCG_REG_O0, + TCG_REG_O1, + TCG_REG_O2, + TCG_REG_O3, +}; + +#define INSN_OP(x) ((x) << 30) +#define INSN_OP2(x) ((x) << 22) +#define INSN_OP3(x) ((x) << 19) +#define INSN_OPF(x) ((x) << 5) +#define INSN_RD(x) ((x) << 25) +#define INSN_RS1(x) ((x) << 14) +#define INSN_RS2(x) (x) +#define INSN_ASI(x) ((x) << 5) + +#define INSN_IMM10(x) ((1 << 13) | ((x) & 0x3ff)) +#define INSN_IMM11(x) ((1 << 13) | ((x) & 0x7ff)) +#define INSN_IMM13(x) ((1 << 13) | ((x) & 0x1fff)) +#define INSN_OFF16(x) ((((x) >> 2) & 0x3fff) | ((((x) >> 16) & 3) << 20)) +#define INSN_OFF19(x) (((x) >> 2) & 0x07ffff) +#define INSN_COND(x) ((x) << 25) + +#define COND_N 0x0 +#define COND_E 0x1 +#define COND_LE 0x2 +#define COND_L 0x3 +#define COND_LEU 0x4 +#define COND_CS 0x5 +#define COND_NEG 0x6 +#define COND_VS 0x7 +#define COND_A 0x8 +#define COND_NE 0x9 +#define COND_G 0xa +#define COND_GE 0xb +#define COND_GU 0xc +#define COND_CC 0xd +#define COND_POS 0xe +#define COND_VC 0xf +#define BA (INSN_OP(0) | INSN_COND(COND_A) | INSN_OP2(0x2)) + +#define RCOND_Z 1 +#define RCOND_LEZ 2 +#define RCOND_LZ 3 +#define RCOND_NZ 5 +#define RCOND_GZ 6 +#define RCOND_GEZ 7 + +#define MOVCC_ICC (1 << 18) +#define MOVCC_XCC (1 << 18 | 1 << 12) + +#define BPCC_ICC 0 +#define BPCC_XCC (2 << 20) +#define BPCC_PT (1 << 19) +#define BPCC_PN 0 +#define BPCC_A (1 << 29) + +#define BPR_PT BPCC_PT + +#define ARITH_ADD (INSN_OP(2) | INSN_OP3(0x00)) +#define ARITH_ADDCC (INSN_OP(2) | INSN_OP3(0x10)) +#define ARITH_AND (INSN_OP(2) | INSN_OP3(0x01)) +#define ARITH_ANDN (INSN_OP(2) | INSN_OP3(0x05)) +#define ARITH_OR (INSN_OP(2) | INSN_OP3(0x02)) +#define ARITH_ORCC (INSN_OP(2) | INSN_OP3(0x12)) +#define ARITH_ORN (INSN_OP(2) | INSN_OP3(0x06)) +#define ARITH_XOR (INSN_OP(2) | INSN_OP3(0x03)) +#define ARITH_SUB (INSN_OP(2) | INSN_OP3(0x04)) +#define ARITH_SUBCC (INSN_OP(2) | INSN_OP3(0x14)) +#define ARITH_ADDC (INSN_OP(2) | INSN_OP3(0x08)) +#define ARITH_SUBC (INSN_OP(2) | INSN_OP3(0x0c)) +#define ARITH_UMUL (INSN_OP(2) | INSN_OP3(0x0a)) +#define ARITH_SMUL (INSN_OP(2) | INSN_OP3(0x0b)) +#define ARITH_UDIV (INSN_OP(2) | INSN_OP3(0x0e)) +#define ARITH_SDIV (INSN_OP(2) | INSN_OP3(0x0f)) +#define ARITH_MULX (INSN_OP(2) | INSN_OP3(0x09)) +#define ARITH_UDIVX (INSN_OP(2) | INSN_OP3(0x0d)) +#define ARITH_SDIVX (INSN_OP(2) | INSN_OP3(0x2d)) +#define ARITH_MOVCC (INSN_OP(2) | INSN_OP3(0x2c)) +#define ARITH_MOVR (INSN_OP(2) | INSN_OP3(0x2f)) + +#define ARITH_ADDXC (INSN_OP(2) | INSN_OP3(0x36) | INSN_OPF(0x11)) +#define ARITH_UMULXHI (INSN_OP(2) | INSN_OP3(0x36) | INSN_OPF(0x16)) + +#define SHIFT_SLL (INSN_OP(2) | INSN_OP3(0x25)) +#define SHIFT_SRL (INSN_OP(2) | INSN_OP3(0x26)) +#define SHIFT_SRA (INSN_OP(2) | INSN_OP3(0x27)) + +#define SHIFT_SLLX (INSN_OP(2) | INSN_OP3(0x25) | (1 << 12)) +#define SHIFT_SRLX (INSN_OP(2) | INSN_OP3(0x26) | (1 << 12)) +#define SHIFT_SRAX (INSN_OP(2) | INSN_OP3(0x27) | (1 << 12)) + +#define RDY (INSN_OP(2) | INSN_OP3(0x28) | INSN_RS1(0)) +#define WRY (INSN_OP(2) | INSN_OP3(0x30) | INSN_RD(0)) +#define JMPL (INSN_OP(2) | INSN_OP3(0x38)) +#define RETURN (INSN_OP(2) | INSN_OP3(0x39)) +#define SAVE (INSN_OP(2) | INSN_OP3(0x3c)) +#define RESTORE (INSN_OP(2) | INSN_OP3(0x3d)) +#define SETHI (INSN_OP(0) | INSN_OP2(0x4)) +#define CALL INSN_OP(1) +#define LDUB (INSN_OP(3) | INSN_OP3(0x01)) +#define LDSB (INSN_OP(3) | INSN_OP3(0x09)) +#define LDUH (INSN_OP(3) | INSN_OP3(0x02)) +#define LDSH (INSN_OP(3) | INSN_OP3(0x0a)) +#define LDUW (INSN_OP(3) | INSN_OP3(0x00)) +#define LDSW (INSN_OP(3) | INSN_OP3(0x08)) +#define LDX (INSN_OP(3) | INSN_OP3(0x0b)) +#define STB (INSN_OP(3) | INSN_OP3(0x05)) +#define STH (INSN_OP(3) | INSN_OP3(0x06)) +#define STW (INSN_OP(3) | INSN_OP3(0x04)) +#define STX (INSN_OP(3) | INSN_OP3(0x0e)) +#define LDUBA (INSN_OP(3) | INSN_OP3(0x11)) +#define LDSBA (INSN_OP(3) | INSN_OP3(0x19)) +#define LDUHA (INSN_OP(3) | INSN_OP3(0x12)) +#define LDSHA (INSN_OP(3) | INSN_OP3(0x1a)) +#define LDUWA (INSN_OP(3) | INSN_OP3(0x10)) +#define LDSWA (INSN_OP(3) | INSN_OP3(0x18)) +#define LDXA (INSN_OP(3) | INSN_OP3(0x1b)) +#define STBA (INSN_OP(3) | INSN_OP3(0x15)) +#define STHA (INSN_OP(3) | INSN_OP3(0x16)) +#define STWA (INSN_OP(3) | INSN_OP3(0x14)) +#define STXA (INSN_OP(3) | INSN_OP3(0x1e)) + +#ifndef ASI_PRIMARY_LITTLE +#define ASI_PRIMARY_LITTLE 0x88 +#endif + +#define LDUH_LE (LDUHA | INSN_ASI(ASI_PRIMARY_LITTLE)) +#define LDSH_LE (LDSHA | INSN_ASI(ASI_PRIMARY_LITTLE)) +#define LDUW_LE (LDUWA | INSN_ASI(ASI_PRIMARY_LITTLE)) +#define LDSW_LE (LDSWA | INSN_ASI(ASI_PRIMARY_LITTLE)) +#define LDX_LE (LDXA | INSN_ASI(ASI_PRIMARY_LITTLE)) + +#define STH_LE (STHA | INSN_ASI(ASI_PRIMARY_LITTLE)) +#define STW_LE (STWA | INSN_ASI(ASI_PRIMARY_LITTLE)) +#define STX_LE (STXA | INSN_ASI(ASI_PRIMARY_LITTLE)) + +#ifndef use_vis3_instructions +bool use_vis3_instructions; +#endif + +static inline int check_fit_i64(int64_t val, unsigned int bits) +{ + return val == sextract64(val, 0, bits); +} + +static inline int check_fit_i32(int32_t val, unsigned int bits) +{ + return val == sextract32(val, 0, bits); +} + +#define check_fit_tl check_fit_i64 +#if SPARC64 +# define check_fit_ptr check_fit_i64 +#else +# define check_fit_ptr check_fit_i32 +#endif + +static void patch_reloc(tcg_insn_unit *code_ptr, int type, + intptr_t value, intptr_t addend) +{ + uint32_t insn; + + tcg_debug_assert(addend == 0); + value = tcg_ptr_byte_diff((tcg_insn_unit *)value, code_ptr); + + switch (type) { + case R_SPARC_WDISP16: + if (!check_fit_ptr(value >> 2, 16)) { + tcg_abort(); + } + insn = *code_ptr; + insn &= ~INSN_OFF16(-1); + insn |= INSN_OFF16(value); + *code_ptr = insn; + break; + case R_SPARC_WDISP19: + if (!check_fit_ptr(value >> 2, 19)) { + tcg_abort(); + } + insn = *code_ptr; + insn &= ~INSN_OFF19(-1); + insn |= INSN_OFF19(value); + *code_ptr = insn; + break; + default: + tcg_abort(); + } +} + +/* parse target specific constraints */ +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +{ + const char *ct_str; + + ct_str = *pct_str; + switch (ct_str[0]) { + case 'r': + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffffffff); + break; + case 'R': + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, ALL_64); + break; + case 'A': /* qemu_ld/st address constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, + TARGET_LONG_BITS == 64 ? ALL_64 : 0xffffffff); + reserve_helpers: + tcg_regset_reset_reg(ct->u.regs, TCG_REG_O0); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_O1); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_O2); + break; + case 's': /* qemu_st data 32-bit constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffffffff); + goto reserve_helpers; + case 'S': /* qemu_st data 64-bit constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, ALL_64); + goto reserve_helpers; + case 'I': + ct->ct |= TCG_CT_CONST_S11; + break; + case 'J': + ct->ct |= TCG_CT_CONST_S13; + break; + case 'Z': + ct->ct |= TCG_CT_CONST_ZERO; + break; + default: + return -1; + } + ct_str++; + *pct_str = ct_str; + return 0; +} + +/* test if a constant matches the constraint */ +static inline int tcg_target_const_match(tcg_target_long val, TCGType type, + const TCGArgConstraint *arg_ct) +{ + int ct = arg_ct->ct; + + if (ct & TCG_CT_CONST) { + return 1; + } + + if (type == TCG_TYPE_I32) { + val = (int32_t)val; + } + + if ((ct & TCG_CT_CONST_ZERO) && val == 0) { + return 1; + } else if ((ct & TCG_CT_CONST_S11) && check_fit_tl(val, 11)) { + return 1; + } else if ((ct & TCG_CT_CONST_S13) && check_fit_tl(val, 13)) { + return 1; + } else { + return 0; + } +} + +static inline void tcg_out_arith(TCGContext *s, TCGReg rd, TCGReg rs1, + TCGReg rs2, int op) +{ + tcg_out32(s, op | INSN_RD(rd) | INSN_RS1(rs1) | INSN_RS2(rs2)); +} + +static inline void tcg_out_arithi(TCGContext *s, TCGReg rd, TCGReg rs1, + int32_t offset, int op) +{ + tcg_out32(s, op | INSN_RD(rd) | INSN_RS1(rs1) | INSN_IMM13(offset)); +} + +static void tcg_out_arithc(TCGContext *s, TCGReg rd, TCGReg rs1, + int32_t val2, int val2const, int op) +{ + tcg_out32(s, op | INSN_RD(rd) | INSN_RS1(rs1) + | (val2const ? INSN_IMM13(val2) : INSN_RS2(val2))); +} + +static inline void tcg_out_mov(TCGContext *s, TCGType type, + TCGReg ret, TCGReg arg) +{ + if (ret != arg) { + tcg_out_arith(s, ret, arg, TCG_REG_G0, ARITH_OR); + } +} + +static inline void tcg_out_sethi(TCGContext *s, TCGReg ret, uint32_t arg) +{ + tcg_out32(s, SETHI | INSN_RD(ret) | ((arg & 0xfffffc00) >> 10)); +} + +static inline void tcg_out_movi_imm13(TCGContext *s, TCGReg ret, int32_t arg) +{ + tcg_out_arithi(s, ret, TCG_REG_G0, arg, ARITH_OR); +} + +static void tcg_out_movi(TCGContext *s, TCGType type, + TCGReg ret, tcg_target_long arg) +{ + tcg_target_long hi, lo = (int32_t)arg; + + /* Make sure we test 32-bit constants for imm13 properly. */ + if (type == TCG_TYPE_I32) { + arg = lo; + } + + /* A 13-bit constant sign-extended to 64-bits. */ + if (check_fit_tl(arg, 13)) { + tcg_out_movi_imm13(s, ret, arg); + return; + } + + /* A 32-bit constant, or 32-bit zero-extended to 64-bits. */ + if (type == TCG_TYPE_I32 || arg == (uint32_t)arg) { + tcg_out_sethi(s, ret, arg); + if (arg & 0x3ff) { + tcg_out_arithi(s, ret, ret, arg & 0x3ff, ARITH_OR); + } + return; + } + + /* A 32-bit constant sign-extended to 64-bits. */ + if (arg == lo) { + tcg_out_sethi(s, ret, ~arg); + tcg_out_arithi(s, ret, ret, (arg & 0x3ff) | -0x400, ARITH_XOR); + return; + } + + /* A 64-bit constant decomposed into 2 32-bit pieces. */ + if (check_fit_i32(lo, 13)) { + hi = (arg - lo) >> 32; + tcg_out_movi(s, TCG_TYPE_I32, ret, hi); + tcg_out_arithi(s, ret, ret, 32, SHIFT_SLLX); + tcg_out_arithi(s, ret, ret, lo, ARITH_ADD); + } else { + hi = arg >> 32; + tcg_out_movi(s, TCG_TYPE_I32, ret, hi); + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_T2, lo); + tcg_out_arithi(s, ret, ret, 32, SHIFT_SLLX); + tcg_out_arith(s, ret, ret, TCG_REG_T2, ARITH_OR); + } +} + +static inline void tcg_out_ldst_rr(TCGContext *s, TCGReg data, TCGReg a1, + TCGReg a2, int op) +{ + tcg_out32(s, op | INSN_RD(data) | INSN_RS1(a1) | INSN_RS2(a2)); +} + +static void tcg_out_ldst(TCGContext *s, TCGReg ret, TCGReg addr, + intptr_t offset, int op) +{ + if (check_fit_ptr(offset, 13)) { + tcg_out32(s, op | INSN_RD(ret) | INSN_RS1(addr) | + INSN_IMM13(offset)); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_T1, offset); + tcg_out_ldst_rr(s, ret, addr, TCG_REG_T1, op); + } +} + +static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, + TCGReg arg1, intptr_t arg2) +{ + tcg_out_ldst(s, ret, arg1, arg2, (type == TCG_TYPE_I32 ? LDUW : LDX)); +} + +static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, + TCGReg arg1, intptr_t arg2) +{ + tcg_out_ldst(s, arg, arg1, arg2, (type == TCG_TYPE_I32 ? STW : STX)); +} + +static void tcg_out_ld_ptr(TCGContext *s, TCGReg ret, uintptr_t arg) +{ + tcg_out_movi(s, TCG_TYPE_PTR, ret, arg & ~0x3ff); + tcg_out_ld(s, TCG_TYPE_PTR, ret, ret, arg & 0x3ff); +} + +static inline void tcg_out_sety(TCGContext *s, TCGReg rs) +{ + tcg_out32(s, WRY | INSN_RS1(TCG_REG_G0) | INSN_RS2(rs)); +} + +static inline void tcg_out_rdy(TCGContext *s, TCGReg rd) +{ + tcg_out32(s, RDY | INSN_RD(rd)); +} + +static void tcg_out_div32(TCGContext *s, TCGReg rd, TCGReg rs1, + int32_t val2, int val2const, int uns) +{ + /* Load Y with the sign/zero extension of RS1 to 64-bits. */ + if (uns) { + tcg_out_sety(s, TCG_REG_G0); + } else { + tcg_out_arithi(s, TCG_REG_T1, rs1, 31, SHIFT_SRA); + tcg_out_sety(s, TCG_REG_T1); + } + + tcg_out_arithc(s, rd, rs1, val2, val2const, + uns ? ARITH_UDIV : ARITH_SDIV); +} + +static inline void tcg_out_nop(TCGContext *s) +{ + tcg_out_sethi(s, TCG_REG_G0, 0); +} + +static const uint8_t tcg_cond_to_bcond[] = { + [TCG_COND_EQ] = COND_E, + [TCG_COND_NE] = COND_NE, + [TCG_COND_LT] = COND_L, + [TCG_COND_GE] = COND_GE, + [TCG_COND_LE] = COND_LE, + [TCG_COND_GT] = COND_G, + [TCG_COND_LTU] = COND_CS, + [TCG_COND_GEU] = COND_CC, + [TCG_COND_LEU] = COND_LEU, + [TCG_COND_GTU] = COND_GU, +}; + +static const uint8_t tcg_cond_to_rcond[] = { + [TCG_COND_EQ] = RCOND_Z, + [TCG_COND_NE] = RCOND_NZ, + [TCG_COND_LT] = RCOND_LZ, + [TCG_COND_GT] = RCOND_GZ, + [TCG_COND_LE] = RCOND_LEZ, + [TCG_COND_GE] = RCOND_GEZ +}; + +static void tcg_out_bpcc0(TCGContext *s, int scond, int flags, int off19) +{ + tcg_out32(s, INSN_OP(0) | INSN_OP2(1) | INSN_COND(scond) | flags | off19); +} + +static void tcg_out_bpcc(TCGContext *s, int scond, int flags, TCGLabel *l) +{ + int off19; + + if (l->has_value) { + off19 = INSN_OFF19(tcg_pcrel_diff(s, l->u.value_ptr)); + } else { + /* Make sure to preserve destinations during retranslation. */ + off19 = *s->code_ptr & INSN_OFF19(-1); + tcg_out_reloc(s, s->code_ptr, R_SPARC_WDISP19, l, 0); + } + tcg_out_bpcc0(s, scond, flags, off19); +} + +static void tcg_out_cmp(TCGContext *s, TCGReg c1, int32_t c2, int c2const) +{ + tcg_out_arithc(s, TCG_REG_G0, c1, c2, c2const, ARITH_SUBCC); +} + +static void tcg_out_brcond_i32(TCGContext *s, TCGCond cond, TCGReg arg1, + int32_t arg2, int const_arg2, TCGLabel *l) +{ + tcg_out_cmp(s, arg1, arg2, const_arg2); + tcg_out_bpcc(s, tcg_cond_to_bcond[cond], BPCC_ICC | BPCC_PT, l); + tcg_out_nop(s); +} + +static void tcg_out_movcc(TCGContext *s, TCGCond cond, int cc, TCGReg ret, + int32_t v1, int v1const) +{ + tcg_out32(s, ARITH_MOVCC | cc | INSN_RD(ret) + | INSN_RS1(tcg_cond_to_bcond[cond]) + | (v1const ? INSN_IMM11(v1) : INSN_RS2(v1))); +} + +static void tcg_out_movcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg c1, int32_t c2, int c2const, + int32_t v1, int v1const) +{ + tcg_out_cmp(s, c1, c2, c2const); + tcg_out_movcc(s, cond, MOVCC_ICC, ret, v1, v1const); +} + +static void tcg_out_brcond_i64(TCGContext *s, TCGCond cond, TCGReg arg1, + int32_t arg2, int const_arg2, TCGLabel *l) +{ + /* For 64-bit signed comparisons vs zero, we can avoid the compare. */ + if (arg2 == 0 && !is_unsigned_cond(cond)) { + int off16; + + if (l->has_value) { + off16 = INSN_OFF16(tcg_pcrel_diff(s, l->u.value_ptr)); + } else { + /* Make sure to preserve destinations during retranslation. */ + off16 = *s->code_ptr & INSN_OFF16(-1); + tcg_out_reloc(s, s->code_ptr, R_SPARC_WDISP16, l, 0); + } + tcg_out32(s, INSN_OP(0) | INSN_OP2(3) | BPR_PT | INSN_RS1(arg1) + | INSN_COND(tcg_cond_to_rcond[cond]) | off16); + } else { + tcg_out_cmp(s, arg1, arg2, const_arg2); + tcg_out_bpcc(s, tcg_cond_to_bcond[cond], BPCC_XCC | BPCC_PT, l); + } + tcg_out_nop(s); +} + +static void tcg_out_movr(TCGContext *s, TCGCond cond, TCGReg ret, TCGReg c1, + int32_t v1, int v1const) +{ + tcg_out32(s, ARITH_MOVR | INSN_RD(ret) | INSN_RS1(c1) + | (tcg_cond_to_rcond[cond] << 10) + | (v1const ? INSN_IMM10(v1) : INSN_RS2(v1))); +} + +static void tcg_out_movcond_i64(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg c1, int32_t c2, int c2const, + int32_t v1, int v1const) +{ + /* For 64-bit signed comparisons vs zero, we can avoid the compare. + Note that the immediate range is one bit smaller, so we must check + for that as well. */ + if (c2 == 0 && !is_unsigned_cond(cond) + && (!v1const || check_fit_i32(v1, 10))) { + tcg_out_movr(s, cond, ret, c1, v1, v1const); + } else { + tcg_out_cmp(s, c1, c2, c2const); + tcg_out_movcc(s, cond, MOVCC_XCC, ret, v1, v1const); + } +} + +static void tcg_out_setcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg c1, int32_t c2, int c2const) +{ + /* For 32-bit comparisons, we can play games with ADDC/SUBC. */ + switch (cond) { + case TCG_COND_LTU: + case TCG_COND_GEU: + /* The result of the comparison is in the carry bit. */ + break; + + case TCG_COND_EQ: + case TCG_COND_NE: + /* For equality, we can transform to inequality vs zero. */ + if (c2 != 0) { + tcg_out_arithc(s, TCG_REG_T1, c1, c2, c2const, ARITH_XOR); + c2 = TCG_REG_T1; + } else { + c2 = c1; + } + c1 = TCG_REG_G0, c2const = 0; + cond = (cond == TCG_COND_EQ ? TCG_COND_GEU : TCG_COND_LTU); + break; + + case TCG_COND_GTU: + case TCG_COND_LEU: + /* If we don't need to load a constant into a register, we can + swap the operands on GTU/LEU. There's no benefit to loading + the constant into a temporary register. */ + if (!c2const || c2 == 0) { + TCGReg t = c1; + c1 = c2; + c2 = t; + c2const = 0; + cond = tcg_swap_cond(cond); + break; + } + /* FALLTHRU */ + + default: + tcg_out_cmp(s, c1, c2, c2const); + tcg_out_movi_imm13(s, ret, 0); + tcg_out_movcc(s, cond, MOVCC_ICC, ret, 1, 1); + return; + } + + tcg_out_cmp(s, c1, c2, c2const); + if (cond == TCG_COND_LTU) { + tcg_out_arithi(s, ret, TCG_REG_G0, 0, ARITH_ADDC); + } else { + tcg_out_arithi(s, ret, TCG_REG_G0, -1, ARITH_SUBC); + } +} + +static void tcg_out_setcond_i64(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg c1, int32_t c2, int c2const) +{ + if (use_vis3_instructions) { + switch (cond) { + case TCG_COND_NE: + if (c2 != 0) { + break; + } + c2 = c1, c2const = 0, c1 = TCG_REG_G0; + /* FALLTHRU */ + case TCG_COND_LTU: + tcg_out_cmp(s, c1, c2, c2const); + tcg_out_arith(s, ret, TCG_REG_G0, TCG_REG_G0, ARITH_ADDXC); + return; + default: + break; + } + } + + /* For 64-bit signed comparisons vs zero, we can avoid the compare + if the input does not overlap the output. */ + if (c2 == 0 && !is_unsigned_cond(cond) && c1 != ret) { + tcg_out_movi_imm13(s, ret, 0); + tcg_out_movr(s, cond, ret, c1, 1, 1); + } else { + tcg_out_cmp(s, c1, c2, c2const); + tcg_out_movi_imm13(s, ret, 0); + tcg_out_movcc(s, cond, MOVCC_XCC, ret, 1, 1); + } +} + +static void tcg_out_addsub2_i32(TCGContext *s, TCGReg rl, TCGReg rh, + TCGReg al, TCGReg ah, int32_t bl, int blconst, + int32_t bh, int bhconst, int opl, int oph) +{ + TCGReg tmp = TCG_REG_T1; + + /* Note that the low parts are fully consumed before tmp is set. */ + if (rl != ah && (bhconst || rl != bh)) { + tmp = rl; + } + + tcg_out_arithc(s, tmp, al, bl, blconst, opl); + tcg_out_arithc(s, rh, ah, bh, bhconst, oph); + tcg_out_mov(s, TCG_TYPE_I32, rl, tmp); +} + +static void tcg_out_addsub2_i64(TCGContext *s, TCGReg rl, TCGReg rh, + TCGReg al, TCGReg ah, int32_t bl, int blconst, + int32_t bh, int bhconst, bool is_sub) +{ + TCGReg tmp = TCG_REG_T1; + + /* Note that the low parts are fully consumed before tmp is set. */ + if (rl != ah && (bhconst || rl != bh)) { + tmp = rl; + } + + tcg_out_arithc(s, tmp, al, bl, blconst, is_sub ? ARITH_SUBCC : ARITH_ADDCC); + + if (use_vis3_instructions && !is_sub) { + /* Note that ADDXC doesn't accept immediates. */ + if (bhconst && bh != 0) { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_T2, bh); + bh = TCG_REG_T2; + } + tcg_out_arith(s, rh, ah, bh, ARITH_ADDXC); + } else if (bh == TCG_REG_G0) { + /* If we have a zero, we can perform the operation in two insns, + with the arithmetic first, and a conditional move into place. */ + if (rh == ah) { + tcg_out_arithi(s, TCG_REG_T2, ah, 1, + is_sub ? ARITH_SUB : ARITH_ADD); + tcg_out_movcc(s, TCG_COND_LTU, MOVCC_XCC, rh, TCG_REG_T2, 0); + } else { + tcg_out_arithi(s, rh, ah, 1, is_sub ? ARITH_SUB : ARITH_ADD); + tcg_out_movcc(s, TCG_COND_GEU, MOVCC_XCC, rh, ah, 0); + } + } else { + /* Otherwise adjust BH as if there is carry into T2 ... */ + if (bhconst) { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_T2, bh + (is_sub ? -1 : 1)); + } else { + tcg_out_arithi(s, TCG_REG_T2, bh, 1, + is_sub ? ARITH_SUB : ARITH_ADD); + } + /* ... smoosh T2 back to original BH if carry is clear ... */ + tcg_out_movcc(s, TCG_COND_GEU, MOVCC_XCC, TCG_REG_T2, bh, bhconst); + /* ... and finally perform the arithmetic with the new operand. */ + tcg_out_arith(s, rh, ah, TCG_REG_T2, is_sub ? ARITH_SUB : ARITH_ADD); + } + + tcg_out_mov(s, TCG_TYPE_I64, rl, tmp); +} + +static void tcg_out_call_nodelay(TCGContext *s, tcg_insn_unit *dest) +{ + ptrdiff_t disp = tcg_pcrel_diff(s, dest); + + if (disp == (int32_t)disp) { + tcg_out32(s, CALL | (uint32_t)disp >> 2); + } else { + uintptr_t desti = (uintptr_t)dest; + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_T1, desti & ~0xfff); + tcg_out_arithi(s, TCG_REG_O7, TCG_REG_T1, desti & 0xfff, JMPL); + } +} + +static void tcg_out_call(TCGContext *s, tcg_insn_unit *dest) +{ + tcg_out_call_nodelay(s, dest); + tcg_out_nop(s); +} + +#ifdef CONFIG_SOFTMMU +static tcg_insn_unit *qemu_ld_trampoline[16]; +static tcg_insn_unit *qemu_st_trampoline[16]; + +static void build_trampolines(TCGContext *s) +{ + static void * const qemu_ld_helpers[16] = { + [MO_UB] = helper_ret_ldub_mmu, + [MO_SB] = helper_ret_ldsb_mmu, + [MO_LEUW] = helper_le_lduw_mmu, + [MO_LESW] = helper_le_ldsw_mmu, + [MO_LEUL] = helper_le_ldul_mmu, + [MO_LEQ] = helper_le_ldq_mmu, + [MO_BEUW] = helper_be_lduw_mmu, + [MO_BESW] = helper_be_ldsw_mmu, + [MO_BEUL] = helper_be_ldul_mmu, + [MO_BEQ] = helper_be_ldq_mmu, + }; + static void * const qemu_st_helpers[16] = { + [MO_UB] = helper_ret_stb_mmu, + [MO_LEUW] = helper_le_stw_mmu, + [MO_LEUL] = helper_le_stl_mmu, + [MO_LEQ] = helper_le_stq_mmu, + [MO_BEUW] = helper_be_stw_mmu, + [MO_BEUL] = helper_be_stl_mmu, + [MO_BEQ] = helper_be_stq_mmu, + }; + + int i; + TCGReg ra; + + for (i = 0; i < 16; ++i) { + if (qemu_ld_helpers[i] == NULL) { + continue; + } + + /* May as well align the trampoline. */ + while ((uintptr_t)s->code_ptr & 15) { + tcg_out_nop(s); + } + qemu_ld_trampoline[i] = s->code_ptr; + + if (SPARC64 || TARGET_LONG_BITS == 32) { + ra = TCG_REG_O3; + } else { + /* Install the high part of the address. */ + tcg_out_arithi(s, TCG_REG_O1, TCG_REG_O2, 32, SHIFT_SRLX); + ra = TCG_REG_O4; + } + + /* Set the retaddr operand. */ + tcg_out_mov(s, TCG_TYPE_PTR, ra, TCG_REG_O7); + /* Set the env operand. */ + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_O0, TCG_AREG0); + /* Tail call. */ + tcg_out_call_nodelay(s, qemu_ld_helpers[i]); + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_O7, ra); + } + + for (i = 0; i < 16; ++i) { + if (qemu_st_helpers[i] == NULL) { + continue; + } + + /* May as well align the trampoline. */ + while ((uintptr_t)s->code_ptr & 15) { + tcg_out_nop(s); + } + qemu_st_trampoline[i] = s->code_ptr; + + if (SPARC64) { + ra = TCG_REG_O4; + } else { + ra = TCG_REG_O1; + if (TARGET_LONG_BITS == 64) { + /* Install the high part of the address. */ + tcg_out_arithi(s, ra, ra + 1, 32, SHIFT_SRLX); + ra += 2; + } else { + ra += 1; + } + if ((i & MO_SIZE) == MO_64) { + /* Install the high part of the data. */ + tcg_out_arithi(s, ra, ra + 1, 32, SHIFT_SRLX); + ra += 2; + } else { + ra += 1; + } + /* Skip the oi argument. */ + ra += 1; + } + + /* Set the retaddr operand. */ + if (ra >= TCG_REG_O6) { + tcg_out_st(s, TCG_TYPE_PTR, TCG_REG_O7, TCG_REG_CALL_STACK, + TCG_TARGET_CALL_STACK_OFFSET); + ra = TCG_REG_G1; + } + tcg_out_mov(s, TCG_TYPE_PTR, ra, TCG_REG_O7); + /* Set the env operand. */ + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_O0, TCG_AREG0); + /* Tail call. */ + tcg_out_call_nodelay(s, qemu_st_helpers[i]); + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_O7, ra); + } +} +#endif + +/* Generate global QEMU prologue and epilogue code */ +static void tcg_target_qemu_prologue(TCGContext *s) +{ + int tmp_buf_size, frame_size; + + /* The TCG temp buffer is at the top of the frame, immediately + below the frame pointer. */ + tmp_buf_size = CPU_TEMP_BUF_NLONGS * (int)sizeof(long); + tcg_set_frame(s, TCG_REG_I6, TCG_TARGET_STACK_BIAS - tmp_buf_size, + tmp_buf_size); + + /* TCG_TARGET_CALL_STACK_OFFSET includes the stack bias, but is + otherwise the minimal frame usable by callees. */ + frame_size = TCG_TARGET_CALL_STACK_OFFSET - TCG_TARGET_STACK_BIAS; + frame_size += TCG_STATIC_CALL_ARGS_SIZE + tmp_buf_size; + frame_size += TCG_TARGET_STACK_ALIGN - 1; + frame_size &= -TCG_TARGET_STACK_ALIGN; + tcg_out32(s, SAVE | INSN_RD(TCG_REG_O6) | INSN_RS1(TCG_REG_O6) | + INSN_IMM13(-frame_size)); + +#ifndef CONFIG_SOFTMMU + if (guest_base != 0) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, guest_base); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + } +#endif + + tcg_out_arithi(s, TCG_REG_G0, TCG_REG_I1, 0, JMPL); + /* delay slot */ + tcg_out_nop(s); + + /* No epilogue required. We issue ret + restore directly in the TB. */ + +#ifdef CONFIG_SOFTMMU + build_trampolines(s); +#endif +} + +#if defined(CONFIG_SOFTMMU) +/* Perform the TLB load and compare. + + Inputs: + ADDRLO and ADDRHI contain the possible two parts of the address. + + MEM_INDEX and S_BITS are the memory context and log2 size of the load. + + WHICH is the offset into the CPUTLBEntry structure of the slot to read. + This should be offsetof addr_read or addr_write. + + The result of the TLB comparison is in %[ix]cc. The sanitized address + is in the returned register, maybe %o0. The TLB addend is in %o1. */ + +static TCGReg tcg_out_tlb_load(TCGContext *s, TCGReg addr, int mem_index, + TCGMemOp s_bits, int which) +{ + const TCGReg r0 = TCG_REG_O0; + const TCGReg r1 = TCG_REG_O1; + const TCGReg r2 = TCG_REG_O2; + int tlb_ofs; + + /* Shift the page number down. */ + tcg_out_arithi(s, r1, addr, TARGET_PAGE_BITS, SHIFT_SRL); + + /* Mask out the page offset, except for the required alignment. */ + tcg_out_movi(s, TCG_TYPE_TL, TCG_REG_T1, + TARGET_PAGE_MASK | ((1 << s_bits) - 1)); + + /* Mask the tlb index. */ + tcg_out_arithi(s, r1, r1, CPU_TLB_SIZE - 1, ARITH_AND); + + /* Mask page, part 2. */ + tcg_out_arith(s, r0, addr, TCG_REG_T1, ARITH_AND); + + /* Shift the tlb index into place. */ + tcg_out_arithi(s, r1, r1, CPU_TLB_ENTRY_BITS, SHIFT_SLL); + + /* Relative to the current ENV. */ + tcg_out_arith(s, r1, TCG_AREG0, r1, ARITH_ADD); + + /* Find a base address that can load both tlb comparator and addend. */ + tlb_ofs = offsetof(CPUArchState, tlb_table[mem_index][0]); + if (!check_fit_ptr(tlb_ofs + sizeof(CPUTLBEntry), 13)) { + if (tlb_ofs & ~0x3ff) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_T1, tlb_ofs & ~0x3ff); + tcg_out_arith(s, r1, r1, TCG_REG_T1, ARITH_ADD); + } + tlb_ofs &= 0x3ff; + } + + /* Load the tlb comparator and the addend. */ + tcg_out_ld(s, TCG_TYPE_TL, r2, r1, tlb_ofs + which); + tcg_out_ld(s, TCG_TYPE_PTR, r1, r1, tlb_ofs+offsetof(CPUTLBEntry, addend)); + + /* subcc arg0, arg2, %g0 */ + tcg_out_cmp(s, r0, r2, 0); + + /* If the guest address must be zero-extended, do so now. */ + if (SPARC64 && TARGET_LONG_BITS == 32) { + tcg_out_arithi(s, r0, addr, 0, SHIFT_SRL); + return r0; + } + return addr; +} +#endif /* CONFIG_SOFTMMU */ + +static const int qemu_ld_opc[16] = { + [MO_UB] = LDUB, + [MO_SB] = LDSB, + + [MO_BEUW] = LDUH, + [MO_BESW] = LDSH, + [MO_BEUL] = LDUW, + [MO_BESL] = LDSW, + [MO_BEQ] = LDX, + + [MO_LEUW] = LDUH_LE, + [MO_LESW] = LDSH_LE, + [MO_LEUL] = LDUW_LE, + [MO_LESL] = LDSW_LE, + [MO_LEQ] = LDX_LE, +}; + +static const int qemu_st_opc[16] = { + [MO_UB] = STB, + + [MO_BEUW] = STH, + [MO_BEUL] = STW, + [MO_BEQ] = STX, + + [MO_LEUW] = STH_LE, + [MO_LEUL] = STW_LE, + [MO_LEQ] = STX_LE, +}; + +static void tcg_out_qemu_ld(TCGContext *s, TCGReg data, TCGReg addr, + TCGMemOpIdx oi, bool is_64) +{ + TCGMemOp memop = get_memop(oi); +#ifdef CONFIG_SOFTMMU + unsigned memi = get_mmuidx(oi); + TCGReg addrz, param; + tcg_insn_unit *func; + tcg_insn_unit *label_ptr; + + addrz = tcg_out_tlb_load(s, addr, memi, memop & MO_SIZE, + offsetof(CPUTLBEntry, addr_read)); + + /* The fast path is exactly one insn. Thus we can perform the + entire TLB Hit in the (annulled) delay slot of the branch + over the TLB Miss case. */ + + /* beq,a,pt %[xi]cc, label0 */ + label_ptr = s->code_ptr; + tcg_out_bpcc0(s, COND_E, BPCC_A | BPCC_PT + | (TARGET_LONG_BITS == 64 ? BPCC_XCC : BPCC_ICC), 0); + /* delay slot */ + tcg_out_ldst_rr(s, data, addrz, TCG_REG_O1, + qemu_ld_opc[memop & (MO_BSWAP | MO_SSIZE)]); + + /* TLB Miss. */ + + param = TCG_REG_O1; + if (!SPARC64 && TARGET_LONG_BITS == 64) { + /* Skip the high-part; we'll perform the extract in the trampoline. */ + param++; + } + tcg_out_mov(s, TCG_TYPE_REG, param++, addr); + + /* We use the helpers to extend SB and SW data, leaving the case + of SL needing explicit extending below. */ + if ((memop & MO_SSIZE) == MO_SL) { + func = qemu_ld_trampoline[memop & (MO_BSWAP | MO_SIZE)]; + } else { + func = qemu_ld_trampoline[memop & (MO_BSWAP | MO_SSIZE)]; + } + tcg_debug_assert(func != NULL); + tcg_out_call_nodelay(s, func); + /* delay slot */ + tcg_out_movi(s, TCG_TYPE_I32, param, oi); + + /* Recall that all of the helpers return 64-bit results. + Which complicates things for sparcv8plus. */ + if (SPARC64) { + /* We let the helper sign-extend SB and SW, but leave SL for here. */ + if (is_64 && (memop & MO_SSIZE) == MO_SL) { + tcg_out_arithi(s, data, TCG_REG_O0, 0, SHIFT_SRA); + } else { + tcg_out_mov(s, TCG_TYPE_REG, data, TCG_REG_O0); + } + } else { + if ((memop & MO_SIZE) == MO_64) { + tcg_out_arithi(s, TCG_REG_O0, TCG_REG_O0, 32, SHIFT_SLLX); + tcg_out_arithi(s, TCG_REG_O1, TCG_REG_O1, 0, SHIFT_SRL); + tcg_out_arith(s, data, TCG_REG_O0, TCG_REG_O1, ARITH_OR); + } else if (is_64) { + /* Re-extend from 32-bit rather than reassembling when we + know the high register must be an extension. */ + tcg_out_arithi(s, data, TCG_REG_O1, 0, + memop & MO_SIGN ? SHIFT_SRA : SHIFT_SRL); + } else { + tcg_out_mov(s, TCG_TYPE_I32, data, TCG_REG_O1); + } + } + + *label_ptr |= INSN_OFF19(tcg_ptr_byte_diff(s->code_ptr, label_ptr)); +#else + if (SPARC64 && TARGET_LONG_BITS == 32) { + tcg_out_arithi(s, TCG_REG_T1, addr, 0, SHIFT_SRL); + addr = TCG_REG_T1; + } + tcg_out_ldst_rr(s, data, addr, + (guest_base ? TCG_GUEST_BASE_REG : TCG_REG_G0), + qemu_ld_opc[memop & (MO_BSWAP | MO_SSIZE)]); +#endif /* CONFIG_SOFTMMU */ +} + +static void tcg_out_qemu_st(TCGContext *s, TCGReg data, TCGReg addr, + TCGMemOpIdx oi) +{ + TCGMemOp memop = get_memop(oi); +#ifdef CONFIG_SOFTMMU + unsigned memi = get_mmuidx(oi); + TCGReg addrz, param; + tcg_insn_unit *func; + tcg_insn_unit *label_ptr; + + addrz = tcg_out_tlb_load(s, addr, memi, memop & MO_SIZE, + offsetof(CPUTLBEntry, addr_write)); + + /* The fast path is exactly one insn. Thus we can perform the entire + TLB Hit in the (annulled) delay slot of the branch over TLB Miss. */ + /* beq,a,pt %[xi]cc, label0 */ + label_ptr = s->code_ptr; + tcg_out_bpcc0(s, COND_E, BPCC_A | BPCC_PT + | (TARGET_LONG_BITS == 64 ? BPCC_XCC : BPCC_ICC), 0); + /* delay slot */ + tcg_out_ldst_rr(s, data, addrz, TCG_REG_O1, + qemu_st_opc[memop & (MO_BSWAP | MO_SIZE)]); + + /* TLB Miss. */ + + param = TCG_REG_O1; + if (!SPARC64 && TARGET_LONG_BITS == 64) { + /* Skip the high-part; we'll perform the extract in the trampoline. */ + param++; + } + tcg_out_mov(s, TCG_TYPE_REG, param++, addr); + if (!SPARC64 && (memop & MO_SIZE) == MO_64) { + /* Skip the high-part; we'll perform the extract in the trampoline. */ + param++; + } + tcg_out_mov(s, TCG_TYPE_REG, param++, data); + + func = qemu_st_trampoline[memop & (MO_BSWAP | MO_SIZE)]; + tcg_debug_assert(func != NULL); + tcg_out_call_nodelay(s, func); + /* delay slot */ + tcg_out_movi(s, TCG_TYPE_I32, param, oi); + + *label_ptr |= INSN_OFF19(tcg_ptr_byte_diff(s->code_ptr, label_ptr)); +#else + if (SPARC64 && TARGET_LONG_BITS == 32) { + tcg_out_arithi(s, TCG_REG_T1, addr, 0, SHIFT_SRL); + addr = TCG_REG_T1; + } + tcg_out_ldst_rr(s, data, addr, + (guest_base ? TCG_GUEST_BASE_REG : TCG_REG_G0), + qemu_st_opc[memop & (MO_BSWAP | MO_SIZE)]); +#endif /* CONFIG_SOFTMMU */ +} + +static void tcg_out_op(TCGContext *s, TCGOpcode opc, + const TCGArg args[TCG_MAX_OP_ARGS], + const int const_args[TCG_MAX_OP_ARGS]) +{ + TCGArg a0, a1, a2; + int c, c2; + + /* Hoist the loads of the most common arguments. */ + a0 = args[0]; + a1 = args[1]; + a2 = args[2]; + c2 = const_args[2]; + + switch (opc) { + case INDEX_op_exit_tb: + if (check_fit_ptr(a0, 13)) { + tcg_out_arithi(s, TCG_REG_G0, TCG_REG_I7, 8, RETURN); + tcg_out_movi_imm13(s, TCG_REG_O0, a0); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_I0, a0 & ~0x3ff); + tcg_out_arithi(s, TCG_REG_G0, TCG_REG_I7, 8, RETURN); + tcg_out_arithi(s, TCG_REG_O0, TCG_REG_O0, a0 & 0x3ff, ARITH_OR); + } + break; + case INDEX_op_goto_tb: + if (s->tb_jmp_offset) { + /* direct jump method */ + s->tb_jmp_offset[a0] = tcg_current_code_size(s); + /* Make sure to preserve links during retranslation. */ + tcg_out32(s, CALL | (*s->code_ptr & ~INSN_OP(-1))); + } else { + /* indirect jump method */ + tcg_out_ld_ptr(s, TCG_REG_T1, (uintptr_t)(s->tb_next + a0)); + tcg_out_arithi(s, TCG_REG_G0, TCG_REG_T1, 0, JMPL); + } + tcg_out_nop(s); + s->tb_next_offset[a0] = tcg_current_code_size(s); + break; + case INDEX_op_br: + tcg_out_bpcc(s, COND_A, BPCC_PT, arg_label(a0)); + tcg_out_nop(s); + break; + +#define OP_32_64(x) \ + glue(glue(case INDEX_op_, x), _i32): \ + glue(glue(case INDEX_op_, x), _i64) + + OP_32_64(ld8u): + tcg_out_ldst(s, a0, a1, a2, LDUB); + break; + OP_32_64(ld8s): + tcg_out_ldst(s, a0, a1, a2, LDSB); + break; + OP_32_64(ld16u): + tcg_out_ldst(s, a0, a1, a2, LDUH); + break; + OP_32_64(ld16s): + tcg_out_ldst(s, a0, a1, a2, LDSH); + break; + case INDEX_op_ld_i32: + case INDEX_op_ld32u_i64: + tcg_out_ldst(s, a0, a1, a2, LDUW); + break; + OP_32_64(st8): + tcg_out_ldst(s, a0, a1, a2, STB); + break; + OP_32_64(st16): + tcg_out_ldst(s, a0, a1, a2, STH); + break; + case INDEX_op_st_i32: + case INDEX_op_st32_i64: + tcg_out_ldst(s, a0, a1, a2, STW); + break; + OP_32_64(add): + c = ARITH_ADD; + goto gen_arith; + OP_32_64(sub): + c = ARITH_SUB; + goto gen_arith; + OP_32_64(and): + c = ARITH_AND; + goto gen_arith; + OP_32_64(andc): + c = ARITH_ANDN; + goto gen_arith; + OP_32_64(or): + c = ARITH_OR; + goto gen_arith; + OP_32_64(orc): + c = ARITH_ORN; + goto gen_arith; + OP_32_64(xor): + c = ARITH_XOR; + goto gen_arith; + case INDEX_op_shl_i32: + c = SHIFT_SLL; + do_shift32: + /* Limit immediate shift count lest we create an illegal insn. */ + tcg_out_arithc(s, a0, a1, a2 & 31, c2, c); + break; + case INDEX_op_shr_i32: + c = SHIFT_SRL; + goto do_shift32; + case INDEX_op_sar_i32: + c = SHIFT_SRA; + goto do_shift32; + case INDEX_op_mul_i32: + c = ARITH_UMUL; + goto gen_arith; + + OP_32_64(neg): + c = ARITH_SUB; + goto gen_arith1; + OP_32_64(not): + c = ARITH_ORN; + goto gen_arith1; + + case INDEX_op_div_i32: + tcg_out_div32(s, a0, a1, a2, c2, 0); + break; + case INDEX_op_divu_i32: + tcg_out_div32(s, a0, a1, a2, c2, 1); + break; + + case INDEX_op_brcond_i32: + tcg_out_brcond_i32(s, a2, a0, a1, const_args[1], arg_label(args[3])); + break; + case INDEX_op_setcond_i32: + tcg_out_setcond_i32(s, args[3], a0, a1, a2, c2); + break; + case INDEX_op_movcond_i32: + tcg_out_movcond_i32(s, args[5], a0, a1, a2, c2, args[3], const_args[3]); + break; + + case INDEX_op_add2_i32: + tcg_out_addsub2_i32(s, args[0], args[1], args[2], args[3], + args[4], const_args[4], args[5], const_args[5], + ARITH_ADDCC, ARITH_ADDC); + break; + case INDEX_op_sub2_i32: + tcg_out_addsub2_i32(s, args[0], args[1], args[2], args[3], + args[4], const_args[4], args[5], const_args[5], + ARITH_SUBCC, ARITH_SUBC); + break; + case INDEX_op_mulu2_i32: + c = ARITH_UMUL; + goto do_mul2; + case INDEX_op_muls2_i32: + c = ARITH_SMUL; + do_mul2: + /* The 32-bit multiply insns produce a full 64-bit result. If the + destination register can hold it, we can avoid the slower RDY. */ + tcg_out_arithc(s, a0, a2, args[3], const_args[3], c); + if (SPARC64 || a0 <= TCG_REG_O7) { + tcg_out_arithi(s, a1, a0, 32, SHIFT_SRLX); + } else { + tcg_out_rdy(s, a1); + } + break; + + case INDEX_op_qemu_ld_i32: + tcg_out_qemu_ld(s, a0, a1, a2, false); + break; + case INDEX_op_qemu_ld_i64: + tcg_out_qemu_ld(s, a0, a1, a2, true); + break; + case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st_i64: + tcg_out_qemu_st(s, a0, a1, a2); + break; + + case INDEX_op_ld32s_i64: + tcg_out_ldst(s, a0, a1, a2, LDSW); + break; + case INDEX_op_ld_i64: + tcg_out_ldst(s, a0, a1, a2, LDX); + break; + case INDEX_op_st_i64: + tcg_out_ldst(s, a0, a1, a2, STX); + break; + case INDEX_op_shl_i64: + c = SHIFT_SLLX; + do_shift64: + /* Limit immediate shift count lest we create an illegal insn. */ + tcg_out_arithc(s, a0, a1, a2 & 63, c2, c); + break; + case INDEX_op_shr_i64: + c = SHIFT_SRLX; + goto do_shift64; + case INDEX_op_sar_i64: + c = SHIFT_SRAX; + goto do_shift64; + case INDEX_op_mul_i64: + c = ARITH_MULX; + goto gen_arith; + case INDEX_op_div_i64: + c = ARITH_SDIVX; + goto gen_arith; + case INDEX_op_divu_i64: + c = ARITH_UDIVX; + goto gen_arith; + case INDEX_op_ext_i32_i64: + case INDEX_op_ext32s_i64: + tcg_out_arithi(s, a0, a1, 0, SHIFT_SRA); + break; + case INDEX_op_extu_i32_i64: + case INDEX_op_ext32u_i64: + tcg_out_arithi(s, a0, a1, 0, SHIFT_SRL); + break; + case INDEX_op_extrl_i64_i32: + tcg_out_mov(s, TCG_TYPE_I32, a0, a1); + break; + case INDEX_op_extrh_i64_i32: + tcg_out_arithi(s, a0, a1, 32, SHIFT_SRLX); + break; + + case INDEX_op_brcond_i64: + tcg_out_brcond_i64(s, a2, a0, a1, const_args[1], arg_label(args[3])); + break; + case INDEX_op_setcond_i64: + tcg_out_setcond_i64(s, args[3], a0, a1, a2, c2); + break; + case INDEX_op_movcond_i64: + tcg_out_movcond_i64(s, args[5], a0, a1, a2, c2, args[3], const_args[3]); + break; + case INDEX_op_add2_i64: + tcg_out_addsub2_i64(s, args[0], args[1], args[2], args[3], args[4], + const_args[4], args[5], const_args[5], false); + break; + case INDEX_op_sub2_i64: + tcg_out_addsub2_i64(s, args[0], args[1], args[2], args[3], args[4], + const_args[4], args[5], const_args[5], true); + break; + case INDEX_op_muluh_i64: + tcg_out_arith(s, args[0], args[1], args[2], ARITH_UMULXHI); + break; + + gen_arith: + tcg_out_arithc(s, a0, a1, a2, c2, c); + break; + + gen_arith1: + tcg_out_arithc(s, a0, TCG_REG_G0, a1, const_args[1], c); + break; + + case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ + case INDEX_op_mov_i64: + case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ + case INDEX_op_movi_i64: + case INDEX_op_call: /* Always emitted via tcg_out_call. */ + default: + tcg_abort(); + } +} + +static const TCGTargetOpDef sparc_op_defs[] = { + { INDEX_op_exit_tb, { } }, + { INDEX_op_goto_tb, { } }, + { INDEX_op_br, { } }, + + { INDEX_op_ld8u_i32, { "r", "r" } }, + { INDEX_op_ld8s_i32, { "r", "r" } }, + { INDEX_op_ld16u_i32, { "r", "r" } }, + { INDEX_op_ld16s_i32, { "r", "r" } }, + { INDEX_op_ld_i32, { "r", "r" } }, + { INDEX_op_st8_i32, { "rZ", "r" } }, + { INDEX_op_st16_i32, { "rZ", "r" } }, + { INDEX_op_st_i32, { "rZ", "r" } }, + + { INDEX_op_add_i32, { "r", "rZ", "rJ" } }, + { INDEX_op_mul_i32, { "r", "rZ", "rJ" } }, + { INDEX_op_div_i32, { "r", "rZ", "rJ" } }, + { INDEX_op_divu_i32, { "r", "rZ", "rJ" } }, + { INDEX_op_sub_i32, { "r", "rZ", "rJ" } }, + { INDEX_op_and_i32, { "r", "rZ", "rJ" } }, + { INDEX_op_andc_i32, { "r", "rZ", "rJ" } }, + { INDEX_op_or_i32, { "r", "rZ", "rJ" } }, + { INDEX_op_orc_i32, { "r", "rZ", "rJ" } }, + { INDEX_op_xor_i32, { "r", "rZ", "rJ" } }, + + { INDEX_op_shl_i32, { "r", "rZ", "rJ" } }, + { INDEX_op_shr_i32, { "r", "rZ", "rJ" } }, + { INDEX_op_sar_i32, { "r", "rZ", "rJ" } }, + + { INDEX_op_neg_i32, { "r", "rJ" } }, + { INDEX_op_not_i32, { "r", "rJ" } }, + + { INDEX_op_brcond_i32, { "rZ", "rJ" } }, + { INDEX_op_setcond_i32, { "r", "rZ", "rJ" } }, + { INDEX_op_movcond_i32, { "r", "rZ", "rJ", "rI", "0" } }, + + { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rJ", "rJ" } }, + { INDEX_op_sub2_i32, { "r", "r", "rZ", "rZ", "rJ", "rJ" } }, + { INDEX_op_mulu2_i32, { "r", "r", "rZ", "rJ" } }, + { INDEX_op_muls2_i32, { "r", "r", "rZ", "rJ" } }, + + { INDEX_op_ld8u_i64, { "R", "r" } }, + { INDEX_op_ld8s_i64, { "R", "r" } }, + { INDEX_op_ld16u_i64, { "R", "r" } }, + { INDEX_op_ld16s_i64, { "R", "r" } }, + { INDEX_op_ld32u_i64, { "R", "r" } }, + { INDEX_op_ld32s_i64, { "R", "r" } }, + { INDEX_op_ld_i64, { "R", "r" } }, + { INDEX_op_st8_i64, { "RZ", "r" } }, + { INDEX_op_st16_i64, { "RZ", "r" } }, + { INDEX_op_st32_i64, { "RZ", "r" } }, + { INDEX_op_st_i64, { "RZ", "r" } }, + + { INDEX_op_add_i64, { "R", "RZ", "RJ" } }, + { INDEX_op_mul_i64, { "R", "RZ", "RJ" } }, + { INDEX_op_div_i64, { "R", "RZ", "RJ" } }, + { INDEX_op_divu_i64, { "R", "RZ", "RJ" } }, + { INDEX_op_sub_i64, { "R", "RZ", "RJ" } }, + { INDEX_op_and_i64, { "R", "RZ", "RJ" } }, + { INDEX_op_andc_i64, { "R", "RZ", "RJ" } }, + { INDEX_op_or_i64, { "R", "RZ", "RJ" } }, + { INDEX_op_orc_i64, { "R", "RZ", "RJ" } }, + { INDEX_op_xor_i64, { "R", "RZ", "RJ" } }, + + { INDEX_op_shl_i64, { "R", "RZ", "RJ" } }, + { INDEX_op_shr_i64, { "R", "RZ", "RJ" } }, + { INDEX_op_sar_i64, { "R", "RZ", "RJ" } }, + + { INDEX_op_neg_i64, { "R", "RJ" } }, + { INDEX_op_not_i64, { "R", "RJ" } }, + + { INDEX_op_ext32s_i64, { "R", "R" } }, + { INDEX_op_ext32u_i64, { "R", "R" } }, + { INDEX_op_ext_i32_i64, { "R", "r" } }, + { INDEX_op_extu_i32_i64, { "R", "r" } }, + { INDEX_op_extrl_i64_i32, { "r", "R" } }, + { INDEX_op_extrh_i64_i32, { "r", "R" } }, + + { INDEX_op_brcond_i64, { "RZ", "RJ" } }, + { INDEX_op_setcond_i64, { "R", "RZ", "RJ" } }, + { INDEX_op_movcond_i64, { "R", "RZ", "RJ", "RI", "0" } }, + + { INDEX_op_add2_i64, { "R", "R", "RZ", "RZ", "RJ", "RI" } }, + { INDEX_op_sub2_i64, { "R", "R", "RZ", "RZ", "RJ", "RI" } }, + { INDEX_op_muluh_i64, { "R", "RZ", "RZ" } }, + + { INDEX_op_qemu_ld_i32, { "r", "A" } }, + { INDEX_op_qemu_ld_i64, { "R", "A" } }, + { INDEX_op_qemu_st_i32, { "sZ", "A" } }, + { INDEX_op_qemu_st_i64, { "SZ", "A" } }, + + { -1 }, +}; + +static void tcg_target_init(TCGContext *s) +{ + /* Only probe for the platform and capabilities if we havn't already + determined maximum values at compile time. */ +#ifndef use_vis3_instructions + { + unsigned long hwcap = qemu_getauxval(AT_HWCAP); + use_vis3_instructions = (hwcap & HWCAP_SPARC_VIS3) != 0; + } +#endif + + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, ALL_64); + + tcg_regset_set32(tcg_target_call_clobber_regs, 0, + (1 << TCG_REG_G1) | + (1 << TCG_REG_G2) | + (1 << TCG_REG_G3) | + (1 << TCG_REG_G4) | + (1 << TCG_REG_G5) | + (1 << TCG_REG_G6) | + (1 << TCG_REG_G7) | + (1 << TCG_REG_O0) | + (1 << TCG_REG_O1) | + (1 << TCG_REG_O2) | + (1 << TCG_REG_O3) | + (1 << TCG_REG_O4) | + (1 << TCG_REG_O5) | + (1 << TCG_REG_O7)); + + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_G0); /* zero */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_G6); /* reserved for os */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_G7); /* thread pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_I6); /* frame pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_I7); /* return address */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_O6); /* stack pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_T1); /* for internal use */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_T2); /* for internal use */ + + tcg_add_target_add_op_defs(sparc_op_defs); +} + +#if SPARC64 +# define ELF_HOST_MACHINE EM_SPARCV9 +#else +# define ELF_HOST_MACHINE EM_SPARC32PLUS +# define ELF_HOST_FLAGS EF_SPARC_32PLUS +#endif + +typedef struct { + DebugFrameHeader h; + uint8_t fde_def_cfa[SPARC64 ? 4 : 2]; + uint8_t fde_win_save; + uint8_t fde_ret_save[3]; +} DebugFrame; + +static const DebugFrame debug_frame = { + .h.cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ + .h.cie.id = -1, + .h.cie.version = 1, + .h.cie.code_align = 1, + .h.cie.data_align = -sizeof(void *) & 0x7f, + .h.cie.return_column = 15, /* o7 */ + + /* Total FDE size does not include the "len" member. */ + .h.fde.len = sizeof(DebugFrame) - offsetof(DebugFrame, h.fde.cie_offset), + + .fde_def_cfa = { +#if SPARC64 + 12, 30, /* DW_CFA_def_cfa i6, 2047 */ + (2047 & 0x7f) | 0x80, (2047 >> 7) +#else + 13, 30 /* DW_CFA_def_cfa_register i6 */ +#endif + }, + .fde_win_save = 0x2d, /* DW_CFA_GNU_window_save */ + .fde_ret_save = { 9, 15, 31 }, /* DW_CFA_register o7, i7 */ +}; + +void tcg_register_jit(void *buf, size_t buf_size) +{ + tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); +} + +void tb_set_jmp_target1(uintptr_t jmp_addr, uintptr_t addr) +{ + uint32_t *ptr = (uint32_t *)jmp_addr; + uintptr_t disp = addr - jmp_addr; + + /* We can reach the entire address space for 32-bit. For 64-bit + the code_gen_buffer can't be larger than 2GB. */ + tcg_debug_assert(disp == (int32_t)disp); + + *ptr = CALL | (uint32_t)disp >> 2; + flush_icache_range(jmp_addr, jmp_addr + 4); +} diff --git a/qemu/tcg/tcg-be-ldst.h b/qemu/tcg/tcg-be-ldst.h index 40a2369b7..17777aec5 100644 --- a/qemu/tcg/tcg-be-ldst.h +++ b/qemu/tcg/tcg-be-ldst.h @@ -56,7 +56,7 @@ static inline void tcg_out_tb_init(TCGContext *s) static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l); static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l); -static void tcg_out_tb_finalize(TCGContext *s) +static bool tcg_out_tb_finalize(TCGContext *s) { TCGLabelQemuLdst *lb; @@ -67,7 +67,16 @@ static void tcg_out_tb_finalize(TCGContext *s) } else { tcg_out_qemu_st_slow_path(s, lb); } + + /* Test for (pending) buffer overflow. The assumption is that any + one operation beginning below the high water mark cannot overrun + the buffer completely. Thus we can test for overflow after + generating code without having to check during generation. */ + if (unlikely((void *)s->code_ptr > s->code_gen_highwater)) { + return false; + } } + return true; } /* diff --git a/qemu/tcg/tcg-be-null.h b/qemu/tcg/tcg-be-null.h index 74c57d5a6..5222fe29e 100644 --- a/qemu/tcg/tcg-be-null.h +++ b/qemu/tcg/tcg-be-null.h @@ -38,6 +38,7 @@ static inline void tcg_out_tb_init(TCGContext *s) * Generate TB finalization at the end of block */ -static inline void tcg_out_tb_finalize(TCGContext *s) +static inline bool tcg_out_tb_finalize(TCGContext *s) { + return true; } diff --git a/qemu/tcg/tcg-common.c b/qemu/tcg/tcg-common.c new file mode 100644 index 000000000..97305a3ef --- /dev/null +++ b/qemu/tcg/tcg-common.c @@ -0,0 +1,38 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "tcg/tcg.h" + +#if defined(CONFIG_TCG_INTERPRETER) +uintptr_t tci_tb_ptr; +#endif + +TCGOpDef tcg_op_defs[] = { +#define DEF(s, oargs, iargs, cargs, flags) \ + { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags }, +#include "tcg-opc.h" +#undef DEF +}; +const size_t tcg_op_defs_max = ARRAY_SIZE(tcg_op_defs); diff --git a/qemu/tcg/tcg-op.c b/qemu/tcg/tcg-op.c index 45098c310..f554b86d4 100644 --- a/qemu/tcg/tcg-op.c +++ b/qemu/tcg/tcg-op.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "tcg.h" #include "tcg-op.h" @@ -1737,28 +1738,28 @@ void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) /* Size changing operations. */ -void tcg_gen_trunc_shr_i64_i32(TCGv_i32 ret, TCGv_i64 arg, unsigned count) +void tcg_gen_extrl_i64_i32(TCGv_i32 ret, TCGv_i64 arg) { - tcg_debug_assert(count < 64); if (TCG_TARGET_REG_BITS == 32) { - if (count >= 32) { - tcg_gen_shri_i32(ret, TCGV_HIGH(arg), count - 32); - } else if (count == 0) { - tcg_gen_mov_i32(ret, TCGV_LOW(arg)); - } else { - TCGv_i64 t = tcg_temp_new_i64(); - tcg_gen_shri_i64(t, arg, count); - tcg_gen_mov_i32(ret, TCGV_LOW(t)); - tcg_temp_free_i64(t); - } - } else if (TCG_TARGET_HAS_trunc_shr_i32) { - tcg_gen_op3i_i32(INDEX_op_trunc_shr_i32, ret, - MAKE_TCGV_I32(GET_TCGV_I64(arg)), count); - } else if (count == 0) { + tcg_gen_mov_i32(ret, TCGV_LOW(arg)); + } else if (TCG_TARGET_HAS_extrl_i64_i32) { + tcg_gen_op2(&tcg_ctx, INDEX_op_extrl_i64_i32, + GET_TCGV_I32(ret), GET_TCGV_I64(arg)); + } else { tcg_gen_mov_i32(ret, MAKE_TCGV_I32(GET_TCGV_I64(arg))); + } +} + +void tcg_gen_extrh_i64_i32(TCGv_i32 ret, TCGv_i64 arg) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_mov_i32(ret, TCGV_HIGH(arg)); + } else if (TCG_TARGET_HAS_extrh_i64_i32) { + tcg_gen_op2(&tcg_ctx, INDEX_op_extrh_i64_i32, + GET_TCGV_I32(ret), GET_TCGV_I64(arg)); } else { TCGv_i64 t = tcg_temp_new_i64(); - tcg_gen_shri_i64(t, arg, count); + tcg_gen_shri_i64(t, arg, 32); tcg_gen_mov_i32(ret, MAKE_TCGV_I32(GET_TCGV_I64(t))); tcg_temp_free_i64(t); } @@ -1770,9 +1771,8 @@ void tcg_gen_extu_i32_i64(TCGv_i64 ret, TCGv_i32 arg) tcg_gen_mov_i32(TCGV_LOW(ret), arg); tcg_gen_movi_i32(TCGV_HIGH(ret), 0); } else { - /* Note: we assume the target supports move between - 32 and 64 bit registers. */ - tcg_gen_ext32u_i64(ret, MAKE_TCGV_I64(GET_TCGV_I32(arg))); + tcg_gen_op2(&tcg_ctx, INDEX_op_extu_i32_i64, + GET_TCGV_I64(ret), GET_TCGV_I32(arg)); } } @@ -1782,9 +1782,8 @@ void tcg_gen_ext_i32_i64(TCGv_i64 ret, TCGv_i32 arg) tcg_gen_mov_i32(TCGV_LOW(ret), arg); tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); } else { - /* Note: we assume the target supports move between - 32 and 64 bit registers. */ - tcg_gen_ext32s_i64(ret, MAKE_TCGV_I64(GET_TCGV_I32(arg))); + tcg_gen_op2(&tcg_ctx, INDEX_op_ext_i32_i64, + GET_TCGV_I64(ret), GET_TCGV_I32(arg)); } } @@ -1820,8 +1819,8 @@ void tcg_gen_extr_i64_i32(TCGv_i32 lo, TCGv_i32 hi, TCGv_i64 arg) tcg_gen_mov_i32(lo, TCGV_LOW(arg)); tcg_gen_mov_i32(hi, TCGV_HIGH(arg)); } else { - tcg_gen_trunc_shr_i64_i32(lo, arg, 0); - tcg_gen_trunc_shr_i64_i32(hi, arg, 32); + tcg_gen_extrl_i64_i32(lo, arg); + tcg_gen_extrh_i64_i32(hi, arg); } } diff --git a/qemu/tcg/tcg-op.h b/qemu/tcg/tcg-op.h index d1d763f6f..c446d3dc7 100644 --- a/qemu/tcg/tcg-op.h +++ b/qemu/tcg/tcg-op.h @@ -684,7 +684,8 @@ static inline void tcg_gen_neg_i64(TCGv_i64 ret, TCGv_i64 arg) void tcg_gen_extu_i32_i64(TCGv_i64 ret, TCGv_i32 arg); void tcg_gen_ext_i32_i64(TCGv_i64 ret, TCGv_i32 arg); void tcg_gen_concat_i32_i64(TCGv_i64 dest, TCGv_i32 low, TCGv_i32 high); -void tcg_gen_trunc_shr_i64_i32(TCGv_i32 ret, TCGv_i64 arg, unsigned int c); +void tcg_gen_extrl_i64_i32(TCGv_i32 ret, TCGv_i64 arg); +void tcg_gen_extrh_i64_i32(TCGv_i32 ret, TCGv_i64 arg); void tcg_gen_extr_i64_i32(TCGv_i32 lo, TCGv_i32 hi, TCGv_i64 arg); void tcg_gen_extr32_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i64 arg); @@ -693,28 +694,59 @@ static inline void tcg_gen_concat32_i64(TCGv_i64 ret, TCGv_i64 lo, TCGv_i64 hi) tcg_gen_deposit_i64(ret, lo, hi, 32, 32); } -static inline void tcg_gen_trunc_i64_i32(TCGv_i32 ret, TCGv_i64 arg) -{ - tcg_gen_trunc_shr_i64_i32(ret, arg, 0); -} - /* QEMU specific operations. */ #ifndef TARGET_LONG_BITS #error must include QEMU headers #endif -/* debug info: write the PC of the corresponding QEMU CPU instruction */ -static inline void tcg_gen_debug_insn_start(uint64_t pc) +#if TARGET_INSN_START_WORDS == 1 +# if TARGET_LONG_BITS <= TCG_TARGET_REG_BITS +static inline void tcg_gen_insn_start(target_ulong pc) +{ + tcg_gen_op1(&tcg_ctx, INDEX_op_insn_start, pc); +} +# else +static inline void tcg_gen_insn_start(target_ulong pc) +{ + tcg_gen_op2(&tcg_ctx, INDEX_op_insn_start, + (uint32_t)pc, (uint32_t)(pc >> 32)); +} +# endif +#elif TARGET_INSN_START_WORDS == 2 +# if TARGET_LONG_BITS <= TCG_TARGET_REG_BITS +static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1) { - /* XXX: must really use a 32 bit size for TCGArg in all cases */ -#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS - tcg_gen_op2ii(INDEX_op_debug_insn_start, - (uint32_t)(pc), (uint32_t)(pc >> 32)); + tcg_gen_op2(&tcg_ctx, INDEX_op_insn_start, pc, a1); +} +# else +static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1) +{ + tcg_gen_op4(&tcg_ctx, INDEX_op_insn_start, + (uint32_t)pc, (uint32_t)(pc >> 32), + (uint32_t)a1, (uint32_t)(a1 >> 32)); +} +# endif +#elif TARGET_INSN_START_WORDS == 3 +# if TARGET_LONG_BITS <= TCG_TARGET_REG_BITS +static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1, + target_ulong a2) +{ + tcg_gen_op3(&tcg_ctx, INDEX_op_insn_start, pc, a1, a2); +} +# else +static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1, + target_ulong a2) +{ + tcg_gen_op6(&tcg_ctx, INDEX_op_insn_start, + (uint32_t)pc, (uint32_t)(pc >> 32), + (uint32_t)a1, (uint32_t)(a1 >> 32), + (uint32_t)a2, (uint32_t)(a2 >> 32)); +} +# endif #else - tcg_gen_op1i(INDEX_op_debug_insn_start, pc); +# error "Unhandled number of operands to insn_start" #endif -} static inline void tcg_gen_exit_tb(uintptr_t val) { @@ -724,7 +756,6 @@ static inline void tcg_gen_exit_tb(uintptr_t val) void tcg_gen_goto_tb(unsigned idx); #if TARGET_LONG_BITS == 32 -#define TCGv TCGv_i32 #define tcg_temp_new() tcg_temp_new_i32() #define tcg_global_reg_new tcg_global_reg_new_i32 #define tcg_global_mem_new tcg_global_mem_new_i32 @@ -736,7 +767,6 @@ void tcg_gen_goto_tb(unsigned idx); #define tcg_gen_qemu_ld_tl tcg_gen_qemu_ld_i32 #define tcg_gen_qemu_st_tl tcg_gen_qemu_st_i32 #else -#define TCGv TCGv_i64 #define tcg_temp_new() tcg_temp_new_i64() #define tcg_global_reg_new tcg_global_reg_new_i64 #define tcg_global_mem_new tcg_global_mem_new_i64 @@ -853,7 +883,7 @@ static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index) #define tcg_gen_divu_tl tcg_gen_divu_i64 #define tcg_gen_remu_tl tcg_gen_remu_i64 #define tcg_gen_discard_tl tcg_gen_discard_i64 -#define tcg_gen_trunc_tl_i32 tcg_gen_trunc_i64_i32 +#define tcg_gen_trunc_tl_i32 tcg_gen_extrl_i64_i32 #define tcg_gen_trunc_i64_tl tcg_gen_mov_i64 #define tcg_gen_extu_i32_tl tcg_gen_extu_i32_i64 #define tcg_gen_ext_i32_tl tcg_gen_ext_i32_i64 @@ -932,7 +962,7 @@ static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index) #define tcg_gen_remu_tl tcg_gen_remu_i32 #define tcg_gen_discard_tl tcg_gen_discard_i32 #define tcg_gen_trunc_tl_i32 tcg_gen_mov_i32 -#define tcg_gen_trunc_i64_tl tcg_gen_trunc_i64_i32 +#define tcg_gen_trunc_i64_tl tcg_gen_extrl_i64_i32 #define tcg_gen_extu_i32_tl tcg_gen_mov_i32 #define tcg_gen_ext_i32_tl tcg_gen_mov_i32 #define tcg_gen_extu_tl_i64 tcg_gen_extu_i32_i64 diff --git a/qemu/tcg/tcg-opc.h b/qemu/tcg/tcg-opc.h index 13ccb60a5..6d0410c4b 100644 --- a/qemu/tcg/tcg-opc.h +++ b/qemu/tcg/tcg-opc.h @@ -138,8 +138,14 @@ DEF(rotl_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_rot_i64)) DEF(rotr_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_rot_i64)) DEF(deposit_i64, 1, 2, 2, IMPL64 | IMPL(TCG_TARGET_HAS_deposit_i64)) -DEF(trunc_shr_i32, 1, 1, 1, - IMPL(TCG_TARGET_HAS_trunc_shr_i32) +/* size changing ops */ +DEF(ext_i32_i64, 1, 1, 0, IMPL64) +DEF(extu_i32_i64, 1, 1, 0, IMPL64) +DEF(extrl_i64_i32, 1, 1, 0, + IMPL(TCG_TARGET_HAS_extrl_i64_i32) + | (TCG_TARGET_REG_BITS == 32 ? TCG_OPF_NOT_PRESENT : 0)) +DEF(extrh_i64_i32, 1, 1, 0, + IMPL(TCG_TARGET_HAS_extrh_i64_i32) | (TCG_TARGET_REG_BITS == 32 ? TCG_OPF_NOT_PRESENT : 0)) DEF(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | IMPL64) @@ -167,18 +173,15 @@ DEF(muls2_i64, 2, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_muls2_i64)) DEF(muluh_i64, 1, 2, 0, IMPL(TCG_TARGET_HAS_muluh_i64)) DEF(mulsh_i64, 1, 2, 0, IMPL(TCG_TARGET_HAS_mulsh_i64)) +#define TLADDR_ARGS (TARGET_LONG_BITS <= TCG_TARGET_REG_BITS ? 1 : 2) +#define DATA64_ARGS (TCG_TARGET_REG_BITS == 64 ? 1 : 2) + /* QEMU specific */ -#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS -DEF(debug_insn_start, 0, 0, 2, TCG_OPF_NOT_PRESENT) -#else -DEF(debug_insn_start, 0, 0, 1, TCG_OPF_NOT_PRESENT) -#endif +DEF(insn_start, 0, 0, TLADDR_ARGS * TARGET_INSN_START_WORDS, + TCG_OPF_NOT_PRESENT) DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_END) DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_END) -#define TLADDR_ARGS (TARGET_LONG_BITS <= TCG_TARGET_REG_BITS ? 1 : 2) -#define DATA64_ARGS (TCG_TARGET_REG_BITS == 64 ? 1 : 2) - DEF(qemu_ld_i32, 1, TLADDR_ARGS, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) DEF(qemu_st_i32, 0, TLADDR_ARGS + 1, 1, diff --git a/qemu/tcg/tcg.c b/qemu/tcg/tcg.c index 0892a9bbf..796addd1f 100644 --- a/qemu/tcg/tcg.c +++ b/qemu/tcg/tcg.c @@ -26,17 +26,12 @@ #define USE_LIVENESS_ANALYSIS #define USE_TCG_OPTIMIZATIONS -#include "config.h" +#include "qemu/osdep.h" /* Define to jump the ELF file used to communicate with GDB. */ #undef DEBUG_JIT -#if !defined(CONFIG_DEBUG_TCG) && !defined(NDEBUG) -/* define it to suppress various consistency checks (faster) */ -#define NDEBUG -#endif - -#include "qemu-common.h" +#include "qemu/cutils.h" #include "qemu/host-utils.h" #include "qemu/timer.h" @@ -60,8 +55,10 @@ #endif #include "elf.h" +#include "exec/log.h" -/* Forward declarations for functions declared in tcg-target.c and used here. */ +/* Forward declarations for functions declared in tcg-target.inc.c and + used here. */ static void tcg_target_init(TCGContext *s); static void tcg_target_qemu_prologue(TCGContext *s); static void patch_reloc(tcg_insn_unit *code_ptr, int type, @@ -95,7 +92,7 @@ static void tcg_register_jit_int(void *buf, size_t size, size_t debug_frame_size) __attribute__((unused)); -/* Forward declarations for functions declared and used in tcg-target.c. */ +/* Forward declarations for functions declared and used in tcg-target.inc.c. */ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str); static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, intptr_t arg2); @@ -110,15 +107,9 @@ static void tcg_out_call(TCGContext *s, tcg_insn_unit *target); static int tcg_target_const_match(tcg_target_long val, TCGType type, const TCGArgConstraint *arg_ct); static void tcg_out_tb_init(TCGContext *s); -static void tcg_out_tb_finalize(TCGContext *s); +static bool tcg_out_tb_finalize(TCGContext *s); -TCGOpDef tcg_op_defs[] = { -#define DEF(s, oargs, iargs, cargs, flags) { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags }, -#include "tcg-opc.h" -#undef DEF -}; -const size_t tcg_op_defs_max = ARRAY_SIZE(tcg_op_defs); static TCGRegSet tcg_target_available_regs[2]; static TCGRegSet tcg_target_call_clobber_regs; @@ -233,7 +224,7 @@ static void tcg_out_label(TCGContext *s, TCGLabel *l, tcg_insn_unit *ptr) intptr_t value = (intptr_t)ptr; TCGRelocation *r; - assert(!l->has_value); + tcg_debug_assert(!l->has_value); for (r = l->u.first_reloc; r != NULL; r = r->next) { patch_reloc(r->ptr, r->type, value, r->addend); @@ -255,7 +246,7 @@ TCGLabel *gen_new_label(void) return l; } -#include "tcg-target.c" +#include "tcg-target.inc.c" /* pool based memory allocation */ void *tcg_malloc_internal(TCGContext *s, int size) @@ -323,6 +314,8 @@ static const TCGHelperInfo all_helpers[] = { #include "exec/helper-tcg.h" }; +static int indirect_reg_alloc_order[ARRAY_SIZE(tcg_target_reg_alloc_order)]; + void tcg_context_init(TCGContext *s) { int op, total_args, n, i; @@ -365,34 +358,64 @@ void tcg_context_init(TCGContext *s) } tcg_target_init(s); + + /* Reverse the order of the saved registers, assuming they're all at + the start of tcg_target_reg_alloc_order. */ + for (n = 0; n < ARRAY_SIZE(tcg_target_reg_alloc_order); ++n) { + int r = tcg_target_reg_alloc_order[n]; + if (tcg_regset_test_reg(tcg_target_call_clobber_regs, r)) { + break; + } + } + for (i = 0; i < n; ++i) { + indirect_reg_alloc_order[i] = tcg_target_reg_alloc_order[n - 1 - i]; + } + for (; i < ARRAY_SIZE(tcg_target_reg_alloc_order); ++i) { + indirect_reg_alloc_order[i] = tcg_target_reg_alloc_order[i]; + } } void tcg_prologue_init(TCGContext *s) { - /* init global prologue and epilogue */ - s->code_buf = s->code_gen_prologue; - s->code_ptr = s->code_buf; + size_t prologue_size, total_size; + void *buf0, *buf1; + + /* Put the prologue at the beginning of code_gen_buffer. */ + buf0 = s->code_gen_buffer; + s->code_ptr = buf0; + s->code_buf = buf0; + s->code_gen_prologue = buf0; + + /* Generate the prologue. */ tcg_target_qemu_prologue(s); - flush_icache_range((uintptr_t)s->code_buf, (uintptr_t)s->code_ptr); + buf1 = s->code_ptr; + flush_icache_range((uintptr_t)buf0, (uintptr_t)buf1); + + /* Deduct the prologue from the buffer. */ + prologue_size = tcg_current_code_size(s); + s->code_gen_ptr = buf1; + s->code_gen_buffer = buf1; + s->code_buf = buf1; + total_size = s->code_gen_buffer_size - prologue_size; + s->code_gen_buffer_size = total_size; + + /* Compute a high-water mark, at which we voluntarily flush the buffer + and start over. The size here is arbitrary, significantly larger + than we expect the code generation for any one opcode to require. */ + s->code_gen_highwater = s->code_gen_buffer + (total_size - 1024); + + tcg_register_jit(s->code_gen_buffer, total_size); #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM)) { - size_t size = tcg_current_code_size(s); - qemu_log("PROLOGUE: [size=%zu]\n", size); - log_disas(s->code_buf, size); + qemu_log("PROLOGUE: [size=%zu]\n", prologue_size); + log_disas(buf0, prologue_size); qemu_log("\n"); qemu_log_flush(); } #endif } -void tcg_set_frame(TCGContext *s, int reg, intptr_t start, intptr_t size) -{ - s->frame_start = start; - s->frame_end = start + size; - s->frame_reg = reg; -} - void tcg_func_start(TCGContext *s) { tcg_pool_reset(s); @@ -416,128 +439,133 @@ void tcg_func_start(TCGContext *s) s->be = tcg_malloc(sizeof(TCGBackendData)); } -static inline void tcg_temp_alloc(TCGContext *s, int n) +static inline int temp_idx(TCGContext *s, TCGTemp *ts) { - if (n > TCG_MAX_TEMPS) - tcg_abort(); + ptrdiff_t n = ts - s->temps; + tcg_debug_assert(n >= 0 && n < s->nb_temps); + return n; } -static inline int tcg_global_reg_new_internal(TCGType type, int reg, - const char *name) +static inline TCGTemp *tcg_temp_alloc(TCGContext *s) +{ + int n = s->nb_temps++; + tcg_debug_assert(n < TCG_MAX_TEMPS); + return memset(&s->temps[n], 0, sizeof(TCGTemp)); +} + +static inline TCGTemp *tcg_global_alloc(TCGContext *s) +{ + tcg_debug_assert(s->nb_globals == s->nb_temps); + s->nb_globals++; + return tcg_temp_alloc(s); +} + +static int tcg_global_reg_new_internal(TCGContext *s, TCGType type, + TCGReg reg, const char *name) { - TCGContext *s = &tcg_ctx; TCGTemp *ts; - int idx; -#if TCG_TARGET_REG_BITS == 32 - if (type != TCG_TYPE_I32) - tcg_abort(); -#endif - if (tcg_regset_test_reg(s->reserved_regs, reg)) + if (TCG_TARGET_REG_BITS == 32 && type != TCG_TYPE_I32) { tcg_abort(); - idx = s->nb_globals; - tcg_temp_alloc(s, s->nb_globals + 1); - ts = &s->temps[s->nb_globals]; + } + + ts = tcg_global_alloc(s); ts->base_type = type; ts->type = type; ts->fixed_reg = 1; ts->reg = reg; ts->name = name; - s->nb_globals++; tcg_regset_set_reg(s->reserved_regs, reg); - return idx; + + return temp_idx(s, ts); } -TCGv_i32 tcg_global_reg_new_i32(int reg, const char *name) +void tcg_set_frame(TCGContext *s, TCGReg reg, intptr_t start, intptr_t size) { int idx; + s->frame_start = start; + s->frame_end = start + size; + idx = tcg_global_reg_new_internal(s, TCG_TYPE_PTR, reg, "_frame"); + s->frame_temp = &s->temps[idx]; +} - idx = tcg_global_reg_new_internal(TCG_TYPE_I32, reg, name); +TCGv_i32 tcg_global_reg_new_i32(TCGReg reg, const char *name) +{ + TCGContext *s = &tcg_ctx; + int idx; + + if (tcg_regset_test_reg(s->reserved_regs, reg)) { + tcg_abort(); + } + idx = tcg_global_reg_new_internal(s, TCG_TYPE_I32, reg, name); return MAKE_TCGV_I32(idx); } -TCGv_i64 tcg_global_reg_new_i64(int reg, const char *name) +TCGv_i64 tcg_global_reg_new_i64(TCGReg reg, const char *name) { + TCGContext *s = &tcg_ctx; int idx; - idx = tcg_global_reg_new_internal(TCG_TYPE_I64, reg, name); + if (tcg_regset_test_reg(s->reserved_regs, reg)) { + tcg_abort(); + } + idx = tcg_global_reg_new_internal(s, TCG_TYPE_I64, reg, name); return MAKE_TCGV_I64(idx); } -static inline int tcg_global_mem_new_internal(TCGType type, int reg, - intptr_t offset, - const char *name) +int tcg_global_mem_new_internal(TCGType type, TCGv_ptr base, + intptr_t offset, const char *name) { TCGContext *s = &tcg_ctx; - TCGTemp *ts; - int idx; + TCGTemp *base_ts = &s->temps[GET_TCGV_PTR(base)]; + TCGTemp *ts = tcg_global_alloc(s); + int indirect_reg = 0, bigendian = 0; +#ifdef HOST_WORDS_BIGENDIAN + bigendian = 1; +#endif - idx = s->nb_globals; -#if TCG_TARGET_REG_BITS == 32 - if (type == TCG_TYPE_I64) { + if (!base_ts->fixed_reg) { + indirect_reg = 1; + base_ts->indirect_base = 1; + } + + if (TCG_TARGET_REG_BITS == 32 && type == TCG_TYPE_I64) { + TCGTemp *ts2 = tcg_global_alloc(s); char buf[64]; - tcg_temp_alloc(s, s->nb_globals + 2); - ts = &s->temps[s->nb_globals]; - ts->base_type = type; + + ts->base_type = TCG_TYPE_I64; ts->type = TCG_TYPE_I32; - ts->fixed_reg = 0; + ts->indirect_reg = indirect_reg; ts->mem_allocated = 1; - ts->mem_reg = reg; -#ifdef HOST_WORDS_BIGENDIAN - ts->mem_offset = offset + 4; -#else - ts->mem_offset = offset; -#endif + ts->mem_base = base_ts; + ts->mem_offset = offset + bigendian * 4; pstrcpy(buf, sizeof(buf), name); pstrcat(buf, sizeof(buf), "_0"); ts->name = strdup(buf); - ts++; - ts->base_type = type; - ts->type = TCG_TYPE_I32; - ts->fixed_reg = 0; - ts->mem_allocated = 1; - ts->mem_reg = reg; -#ifdef HOST_WORDS_BIGENDIAN - ts->mem_offset = offset; -#else - ts->mem_offset = offset + 4; -#endif + tcg_debug_assert(ts2 == ts + 1); + ts2->base_type = TCG_TYPE_I64; + ts2->type = TCG_TYPE_I32; + ts2->indirect_reg = indirect_reg; + ts2->mem_allocated = 1; + ts2->mem_base = base_ts; + ts2->mem_offset = offset + (1 - bigendian) * 4; pstrcpy(buf, sizeof(buf), name); pstrcat(buf, sizeof(buf), "_1"); ts->name = strdup(buf); - - s->nb_globals += 2; - } else -#endif - { - tcg_temp_alloc(s, s->nb_globals + 1); - ts = &s->temps[s->nb_globals]; + } else { ts->base_type = type; ts->type = type; - ts->fixed_reg = 0; + ts->indirect_reg = indirect_reg; ts->mem_allocated = 1; - ts->mem_reg = reg; + ts->mem_base = base_ts; ts->mem_offset = offset; ts->name = name; - s->nb_globals++; } - return idx; -} - -TCGv_i32 tcg_global_mem_new_i32(int reg, intptr_t offset, const char *name) -{ - int idx = tcg_global_mem_new_internal(TCG_TYPE_I32, reg, offset, name); - return MAKE_TCGV_I32(idx); + return temp_idx(s, ts); } -TCGv_i64 tcg_global_mem_new_i64(int reg, intptr_t offset, const char *name) -{ - int idx = tcg_global_mem_new_internal(TCG_TYPE_I64, reg, offset, name); - return MAKE_TCGV_I64(idx); -} - -static inline int tcg_temp_new_internal(TCGType type, int temp_local) +static int tcg_temp_new_internal(TCGType type, int temp_local) { TCGContext *s = &tcg_ctx; TCGTemp *ts; @@ -551,38 +579,30 @@ static inline int tcg_temp_new_internal(TCGType type, int temp_local) ts = &s->temps[idx]; ts->temp_allocated = 1; - assert(ts->base_type == type); - assert(ts->temp_local == temp_local); + tcg_debug_assert(ts->base_type == type); + tcg_debug_assert(ts->temp_local == temp_local); } else { - idx = s->nb_temps; -#if TCG_TARGET_REG_BITS == 32 - if (type == TCG_TYPE_I64) { - tcg_temp_alloc(s, s->nb_temps + 2); - ts = &s->temps[s->nb_temps]; - ts->base_type = type; - ts->type = TCG_TYPE_I32; - ts->temp_allocated = 1; - ts->temp_local = temp_local; - ts->name = NULL; - ts++; + ts = tcg_temp_alloc(s); + if (TCG_TARGET_REG_BITS == 32 && type == TCG_TYPE_I64) { + TCGTemp *ts2 = tcg_temp_alloc(s); + ts->base_type = type; ts->type = TCG_TYPE_I32; ts->temp_allocated = 1; ts->temp_local = temp_local; - ts->name = NULL; - s->nb_temps += 2; - } else -#endif - { - tcg_temp_alloc(s, s->nb_temps + 1); - ts = &s->temps[s->nb_temps]; + + tcg_debug_assert(ts2 == ts + 1); + ts2->base_type = TCG_TYPE_I64; + ts2->type = TCG_TYPE_I32; + ts2->temp_allocated = 1; + ts2->temp_local = temp_local; + } else { ts->base_type = type; ts->type = type; ts->temp_allocated = 1; ts->temp_local = temp_local; - ts->name = NULL; - s->nb_temps++; } + idx = temp_idx(s, ts); } #if defined(CONFIG_DEBUG_TCG) @@ -620,9 +640,9 @@ static void tcg_temp_free_internal(int idx) } #endif - assert(idx >= s->nb_globals && idx < s->nb_temps); + tcg_debug_assert(idx >= s->nb_globals && idx < s->nb_temps); ts = &s->temps[idx]; - assert(ts->temp_allocated != 0); + tcg_debug_assert(ts->temp_allocated != 0); ts->temp_allocated = 0; k = ts->base_type + (ts->temp_local ? TCG_TYPE_COUNT : 0); @@ -901,37 +921,30 @@ static void tcg_reg_alloc_start(TCGContext *s) ts->mem_allocated = 0; ts->fixed_reg = 0; } - for(i = 0; i < TCG_TARGET_NB_REGS; i++) { - s->reg_to_temp[i] = -1; - } + + memset(s->reg_to_temp, 0, sizeof(s->reg_to_temp)); } -static char *tcg_get_arg_str_idx(TCGContext *s, char *buf, int buf_size, - int idx) +static char *tcg_get_arg_str_ptr(TCGContext *s, char *buf, int buf_size, + TCGTemp *ts) { - TCGTemp *ts; + int idx = temp_idx(s, ts); - assert(idx >= 0 && idx < s->nb_temps); - ts = &s->temps[idx]; if (idx < s->nb_globals) { pstrcpy(buf, buf_size, ts->name); + } else if (ts->temp_local) { + snprintf(buf, buf_size, "loc%d", idx - s->nb_globals); } else { - if (ts->temp_local) - snprintf(buf, buf_size, "loc%d", idx - s->nb_globals); - else - snprintf(buf, buf_size, "tmp%d", idx - s->nb_globals); + snprintf(buf, buf_size, "tmp%d", idx - s->nb_globals); } return buf; } -char *tcg_get_arg_str_i32(TCGContext *s, char *buf, int buf_size, TCGv_i32 arg) -{ - return tcg_get_arg_str_idx(s, buf, buf_size, GET_TCGV_I32(arg)); -} - -char *tcg_get_arg_str_i64(TCGContext *s, char *buf, int buf_size, TCGv_i64 arg) +static char *tcg_get_arg_str_idx(TCGContext *s, char *buf, + int buf_size, int idx) { - return tcg_get_arg_str_idx(s, buf, buf_size, GET_TCGV_I64(arg)); + tcg_debug_assert(idx >= 0 && idx < s->nb_temps); + return tcg_get_arg_str_ptr(s, buf, buf_size, &s->temps[idx]); } /* Find helper name. */ @@ -996,17 +1009,18 @@ void tcg_dump_ops(TCGContext *s) def = &tcg_op_defs[c]; args = &s->gen_opparam_buf[op->args]; - if (c == INDEX_op_debug_insn_start) { - uint64_t pc; + if (c == INDEX_op_insn_start) { + qemu_log("%s ----", oi != s->gen_first_op_idx ? "\n" : ""); + + for (i = 0; i < TARGET_INSN_START_WORDS; ++i) { + target_ulong a; #if TARGET_LONG_BITS > TCG_TARGET_REG_BITS - pc = ((uint64_t)args[1] << 32) | args[0]; + a = ((target_ulong)args[i * 2 + 1] << 32) | args[i * 2]; #else - pc = args[0]; + a = args[i]; #endif - if (oi != s->gen_first_op_idx) { - qemu_log("\n"); + qemu_log(" " TARGET_FMT_lx, a); } - qemu_log(" ---- 0x%" PRIx64, pc); } else if (c == INDEX_op_call) { /* variable number of arguments */ nb_oargs = op->callo; @@ -1172,25 +1186,25 @@ void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs) if (tdefs->op == (TCGOpcode)-1) break; op = tdefs->op; - assert((unsigned)op < NB_OPS); + tcg_debug_assert((unsigned)op < NB_OPS); def = &tcg_op_defs[op]; #if defined(CONFIG_DEBUG_TCG) /* Duplicate entry in op definitions? */ - assert(!def->used); + tcg_debug_assert(!def->used); def->used = 1; #endif nb_args = def->nb_iargs + def->nb_oargs; for(i = 0; i < nb_args; i++) { ct_str = tdefs->args_ct_str[i]; /* Incomplete TCGTargetOpDef entry? */ - assert(ct_str != NULL); + tcg_debug_assert(ct_str != NULL); tcg_regset_clear(def->args_ct[i].u.regs); def->args_ct[i].ct = 0; if (ct_str[0] >= '0' && ct_str[0] <= '9') { int oarg; oarg = ct_str[0] - '0'; - assert(oarg < def->nb_oargs); - assert(def->args_ct[oarg].ct & TCG_CT_REG); + tcg_debug_assert(oarg < def->nb_oargs); + tcg_debug_assert(def->args_ct[oarg].ct & TCG_CT_REG); /* TCG_CT_ALIAS is for the output arguments. The input argument is tagged with TCG_CT_IALIAS. */ def->args_ct[i] = def->args_ct[oarg]; @@ -1219,7 +1233,7 @@ void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs) } /* TCGTargetOpDef entry with too much information? */ - assert(i == TCG_MAX_OP_ARGS || tdefs->args_ct_str[i] == NULL); + tcg_debug_assert(i == TCG_MAX_OP_ARGS || tdefs->args_ct_str[i] == NULL); /* sort the constraints (XXX: this is just an heuristic) */ sort_constraints(def, 0, def->nb_oargs); @@ -1240,7 +1254,7 @@ void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs) #if defined(CONFIG_DEBUG_TCG) i = 0; - for (op = 0; op < ARRAY_SIZE(tcg_op_defs); op++) { + for (op = 0; op < tcg_op_defs_max; op++) { const TCGOpDef *def = &tcg_op_defs[op]; if (def->flags & TCG_OPF_NOT_PRESENT) { /* Wrong entry in op definitions? */ @@ -1396,7 +1410,7 @@ static void tcg_liveness_analysis(TCGContext *s) } } } - /* input arguments are live for preceeding opcodes */ + /* input arguments are live for preceding opcodes */ for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { arg = args[i]; dead_temps[arg] = 0; @@ -1406,7 +1420,7 @@ static void tcg_liveness_analysis(TCGContext *s) } } break; - case INDEX_op_debug_insn_start: + case INDEX_op_insn_start: break; case INDEX_op_discard: /* mark the temporary as dead */ @@ -1542,7 +1556,7 @@ static void tcg_liveness_analysis(TCGContext *s) dead_args |= (1 << i); } } - /* input arguments are live for preceeding opcodes */ + /* input arguments are live for preceding opcodes */ for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { arg = args[i]; dead_temps[arg] = 0; @@ -1558,8 +1572,7 @@ static void tcg_liveness_analysis(TCGContext *s) /* dummy liveness analysis */ static void tcg_liveness_analysis(TCGContext *s) { - int nb_ops; - nb_ops = s->gen_opc_ptr - s->gen_opc_buf; + int nb_ops = s->gen_next_op_idx; s->op_dead_args = tcg_malloc(nb_ops * sizeof(uint16_t)); memset(s->op_dead_args, 0, nb_ops * sizeof(uint16_t)); @@ -1568,7 +1581,7 @@ static void tcg_liveness_analysis(TCGContext *s) } #endif -#ifndef NDEBUG +#ifdef CONFIG_DEBUG_TCG static void dump_regs(TCGContext *s) { TCGTemp *ts; @@ -1583,7 +1596,8 @@ static void dump_regs(TCGContext *s) printf("%s", tcg_target_reg_names[ts->reg]); break; case TEMP_VAL_MEM: - printf("%d(%s)", (int)ts->mem_offset, tcg_target_reg_names[ts->mem_reg]); + printf("%d(%s)", (int)ts->mem_offset, + tcg_target_reg_names[ts->mem_base->reg]); break; case TEMP_VAL_CONST: printf("$0x%" TCG_PRIlx, ts->val); @@ -1599,43 +1613,41 @@ static void dump_regs(TCGContext *s) } for(i = 0; i < TCG_TARGET_NB_REGS; i++) { - if (s->reg_to_temp[i] >= 0) { + if (s->reg_to_temp[i] != NULL) { printf("%s: %s\n", tcg_target_reg_names[i], - tcg_get_arg_str_idx(s, buf, sizeof(buf), s->reg_to_temp[i])); + tcg_get_arg_str_ptr(s, buf, sizeof(buf), s->reg_to_temp[i])); } } } static void check_regs(TCGContext *s) { - int reg, k; + int reg; + int k; TCGTemp *ts; char buf[64]; - for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) { - k = s->reg_to_temp[reg]; - if (k >= 0) { - ts = &s->temps[k]; - if (ts->val_type != TEMP_VAL_REG || - ts->reg != reg) { + for (reg = 0; reg < TCG_TARGET_NB_REGS; reg++) { + ts = s->reg_to_temp[reg]; + if (ts != NULL) { + if (ts->val_type != TEMP_VAL_REG || ts->reg != reg) { printf("Inconsistency for register %s:\n", tcg_target_reg_names[reg]); goto fail; } } } - for(k = 0; k < s->nb_temps; k++) { + for (k = 0; k < s->nb_temps; k++) { ts = &s->temps[k]; - if (ts->val_type == TEMP_VAL_REG && - !ts->fixed_reg && - s->reg_to_temp[ts->reg] != k) { - printf("Inconsistency for temp %s:\n", - tcg_get_arg_str_idx(s, buf, sizeof(buf), k)); + if (ts->val_type == TEMP_VAL_REG && !ts->fixed_reg + && s->reg_to_temp[ts->reg] != ts) { + printf("Inconsistency for temp %s:\n", + tcg_get_arg_str_ptr(s, buf, sizeof(buf), ts)); fail: - printf("reg state:\n"); - dump_regs(s); - tcg_abort(); + printf("reg state:\n"); + dump_regs(s); + tcg_abort(); } } } @@ -1656,62 +1668,69 @@ static void temp_allocate_frame(TCGContext *s, int temp) tcg_abort(); } ts->mem_offset = s->current_frame_offset; - ts->mem_reg = s->frame_reg; + ts->mem_base = s->frame_temp; ts->mem_allocated = 1; s->current_frame_offset += sizeof(tcg_target_long); } +static void temp_load(TCGContext *, TCGTemp *, TCGRegSet, TCGRegSet); + /* sync register 'reg' by saving it to the corresponding temporary */ -static inline void tcg_reg_sync(TCGContext *s, int reg) +static void tcg_reg_sync(TCGContext *s, TCGReg reg, TCGRegSet allocated_regs) { - TCGTemp *ts; - int temp; + TCGTemp *ts = s->reg_to_temp[reg]; - temp = s->reg_to_temp[reg]; - ts = &s->temps[temp]; - assert(ts->val_type == TEMP_VAL_REG); + tcg_debug_assert(ts->val_type == TEMP_VAL_REG); if (!ts->mem_coherent && !ts->fixed_reg) { if (!ts->mem_allocated) { - temp_allocate_frame(s, temp); + temp_allocate_frame(s, temp_idx(s, ts)); + } else if (ts->indirect_reg) { + tcg_regset_set_reg(allocated_regs, ts->reg); + temp_load(s, ts->mem_base, + tcg_target_available_regs[TCG_TYPE_PTR], + allocated_regs); } - tcg_out_st(s, ts->type, reg, ts->mem_reg, ts->mem_offset); + tcg_out_st(s, ts->type, reg, ts->mem_base->reg, ts->mem_offset); } ts->mem_coherent = 1; } /* free register 'reg' by spilling the corresponding temporary if necessary */ -static void tcg_reg_free(TCGContext *s, int reg) +static void tcg_reg_free(TCGContext *s, TCGReg reg, TCGRegSet allocated_regs) { - int temp; + TCGTemp *ts = s->reg_to_temp[reg]; - temp = s->reg_to_temp[reg]; - if (temp != -1) { - tcg_reg_sync(s, reg); - s->temps[temp].val_type = TEMP_VAL_MEM; - s->reg_to_temp[reg] = -1; + if (ts != NULL) { + tcg_reg_sync(s, reg, allocated_regs); + ts->val_type = TEMP_VAL_MEM; + s->reg_to_temp[reg] = NULL; } } /* Allocate a register belonging to reg1 & ~reg2 */ -static int tcg_reg_alloc(TCGContext *s, TCGRegSet reg1, TCGRegSet reg2) +static TCGReg tcg_reg_alloc(TCGContext *s, TCGRegSet desired_regs, + TCGRegSet allocated_regs, bool rev) { - int i, reg; + int i, n = ARRAY_SIZE(tcg_target_reg_alloc_order); + const int *order; + TCGReg reg; TCGRegSet reg_ct; - tcg_regset_andnot(reg_ct, reg1, reg2); + tcg_regset_andnot(reg_ct, desired_regs, allocated_regs); + order = rev ? indirect_reg_alloc_order : tcg_target_reg_alloc_order; /* first try free registers */ - for(i = 0; i < ARRAY_SIZE(tcg_target_reg_alloc_order); i++) { - reg = tcg_target_reg_alloc_order[i]; - if (tcg_regset_test_reg(reg_ct, reg) && s->reg_to_temp[reg] == -1) + for(i = 0; i < n; i++) { + reg = order[i]; + if (tcg_regset_test_reg(reg_ct, reg) && s->reg_to_temp[reg] == NULL) return reg; } /* XXX: do better spill choice */ - for(i = 0; i < ARRAY_SIZE(tcg_target_reg_alloc_order); i++) { - reg = tcg_target_reg_alloc_order[i]; + for(i = 0; i < n; i++) { + reg = order[i]; if (tcg_regset_test_reg(reg_ct, reg)) { - tcg_reg_free(s, reg); + tcg_reg_free(s, reg, allocated_regs); return reg; } } @@ -1719,65 +1738,92 @@ static int tcg_reg_alloc(TCGContext *s, TCGRegSet reg1, TCGRegSet reg2) tcg_abort(); } -/* mark a temporary as dead. */ -static inline void temp_dead(TCGContext *s, int temp) +/* Make sure the temporary is in a register. If needed, allocate the register + from DESIRED while avoiding ALLOCATED. */ +static void temp_load(TCGContext *s, TCGTemp *ts, TCGRegSet desired_regs, + TCGRegSet allocated_regs) { - TCGTemp *ts; + TCGReg reg; - ts = &s->temps[temp]; - if (!ts->fixed_reg) { - if (ts->val_type == TEMP_VAL_REG) { - s->reg_to_temp[ts->reg] = -1; - } - if (temp < s->nb_globals || ts->temp_local) { - ts->val_type = TEMP_VAL_MEM; - } else { - ts->val_type = TEMP_VAL_DEAD; + switch (ts->val_type) { + case TEMP_VAL_REG: + return; + case TEMP_VAL_CONST: + reg = tcg_reg_alloc(s, desired_regs, allocated_regs, ts->indirect_base); + tcg_out_movi(s, ts->type, reg, ts->val); + ts->mem_coherent = 0; + break; + case TEMP_VAL_MEM: + reg = tcg_reg_alloc(s, desired_regs, allocated_regs, ts->indirect_base); + if (ts->indirect_reg) { + tcg_regset_set_reg(allocated_regs, reg); + temp_load(s, ts->mem_base, + tcg_target_available_regs[TCG_TYPE_PTR], + allocated_regs); } + tcg_out_ld(s, ts->type, reg, ts->mem_base->reg, ts->mem_offset); + ts->mem_coherent = 1; + break; + case TEMP_VAL_DEAD: + default: + tcg_abort(); } + ts->reg = reg; + ts->val_type = TEMP_VAL_REG; + s->reg_to_temp[reg] = ts; +} + +/* mark a temporary as dead. */ +static inline void temp_dead(TCGContext *s, TCGTemp *ts) +{ + if (ts->fixed_reg) { + return; + } + if (ts->val_type == TEMP_VAL_REG) { + s->reg_to_temp[ts->reg] = NULL; + } + ts->val_type = (temp_idx(s, ts) < s->nb_globals || ts->temp_local + ? TEMP_VAL_MEM : TEMP_VAL_DEAD); } /* sync a temporary to memory. 'allocated_regs' is used in case a temporary registers needs to be allocated to store a constant. */ -static inline void temp_sync(TCGContext *s, int temp, TCGRegSet allocated_regs) +static void temp_sync(TCGContext *s, TCGTemp *ts, TCGRegSet allocated_regs) { - TCGTemp *ts; - - ts = &s->temps[temp]; - if (!ts->fixed_reg) { - switch(ts->val_type) { - case TEMP_VAL_CONST: - ts->reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type], - allocated_regs); - ts->val_type = TEMP_VAL_REG; - s->reg_to_temp[ts->reg] = temp; - ts->mem_coherent = 0; - tcg_out_movi(s, ts->type, ts->reg, ts->val); - /* fallthrough*/ - case TEMP_VAL_REG: - tcg_reg_sync(s, ts->reg); - break; - case TEMP_VAL_DEAD: - case TEMP_VAL_MEM: - break; - default: - tcg_abort(); - } + if (ts->fixed_reg) { + return; + } + switch (ts->val_type) { + case TEMP_VAL_CONST: + temp_load(s, ts, tcg_target_available_regs[ts->type], allocated_regs); + /* fallthrough */ + case TEMP_VAL_REG: + tcg_reg_sync(s, ts->reg, allocated_regs); + break; + case TEMP_VAL_DEAD: + case TEMP_VAL_MEM: + break; + default: + tcg_abort(); } } /* save a temporary to memory. 'allocated_regs' is used in case a temporary registers needs to be allocated to store a constant. */ -static inline void temp_save(TCGContext *s, int temp, TCGRegSet allocated_regs) +static inline void temp_save(TCGContext *s, TCGTemp *ts, + TCGRegSet allocated_regs) { #ifdef USE_LIVENESS_ANALYSIS - /* The liveness analysis already ensures that globals are back - in memory. Keep an assert for safety. */ - assert(s->temps[temp].val_type == TEMP_VAL_MEM || s->temps[temp].fixed_reg); -#else - temp_sync(s, temp, allocated_regs); - temp_dead(s, temp); + /* ??? Liveness does not yet incorporate indirect bases. */ + if (!ts->indirect_base) { + /* The liveness analysis already ensures that globals are back + in memory. Keep an tcg_debug_assert for safety. */ + tcg_debug_assert(ts->val_type == TEMP_VAL_MEM || ts->fixed_reg); + return; + } #endif + temp_sync(s, ts, allocated_regs); + temp_dead(s, ts); } /* save globals to their canonical location and assume they can be @@ -1787,8 +1833,8 @@ static void save_globals(TCGContext *s, TCGRegSet allocated_regs) { int i; - for(i = 0; i < s->nb_globals; i++) { - temp_save(s, i, allocated_regs); + for (i = 0; i < s->nb_globals; i++) { + temp_save(s, &s->temps[i], allocated_regs); } } @@ -1800,12 +1846,17 @@ static void sync_globals(TCGContext *s, TCGRegSet allocated_regs) int i; for (i = 0; i < s->nb_globals; i++) { + TCGTemp *ts = &s->temps[i]; #ifdef USE_LIVENESS_ANALYSIS - assert(s->temps[i].val_type != TEMP_VAL_REG || s->temps[i].fixed_reg || - s->temps[i].mem_coherent); -#else - temp_sync(s, i, allocated_regs); + /* ??? Liveness does not yet incorporate indirect bases. */ + if (!ts->indirect_base) { + tcg_debug_assert(ts->val_type != TEMP_VAL_REG + || ts->fixed_reg + || ts->mem_coherent); + continue; + } #endif + temp_sync(s, ts, allocated_regs); } } @@ -1813,21 +1864,23 @@ static void sync_globals(TCGContext *s, TCGRegSet allocated_regs) all globals are stored at their canonical location. */ static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs) { - TCGTemp *ts; int i; - for(i = s->nb_globals; i < s->nb_temps; i++) { - ts = &s->temps[i]; + for (i = s->nb_globals; i < s->nb_temps; i++) { + TCGTemp *ts = &s->temps[i]; if (ts->temp_local) { - temp_save(s, i, allocated_regs); + temp_save(s, ts, allocated_regs); } else { #ifdef USE_LIVENESS_ANALYSIS - /* The liveness analysis already ensures that temps are dead. - Keep an assert for safety. */ - assert(ts->val_type == TEMP_VAL_DEAD); -#else - temp_dead(s, i); + /* ??? Liveness does not yet incorporate indirect bases. */ + if (!ts->indirect_base) { + /* The liveness analysis already ensures that temps are dead. + Keep an tcg_debug_assert for safety. */ + tcg_debug_assert(ts->val_type == TEMP_VAL_DEAD); + continue; + } #endif + temp_dead(s, ts); } } @@ -1852,16 +1905,17 @@ static void tcg_reg_alloc_movi(TCGContext *s, const TCGArg *args, tcg_out_movi(s, ots->type, ots->reg, val); } else { /* The movi is not explicitly generated here */ - if (ots->val_type == TEMP_VAL_REG) - s->reg_to_temp[ots->reg] = -1; + if (ots->val_type == TEMP_VAL_REG) { + s->reg_to_temp[ots->reg] = NULL; + } ots->val_type = TEMP_VAL_CONST; ots->val = val; } if (NEED_SYNC_ARG(0)) { - temp_sync(s, args[0], s->reserved_regs); + temp_sync(s, ots, s->reserved_regs); } if (IS_DEAD_ARG(0)) { - temp_dead(s, args[0]); + temp_dead(s, ots); } } @@ -1887,69 +1941,65 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOpDef *def, we don't have to reload SOURCE the next time it is used. */ if (((NEED_SYNC_ARG(0) || ots->fixed_reg) && ts->val_type != TEMP_VAL_REG) || ts->val_type == TEMP_VAL_MEM) { - ts->reg = tcg_reg_alloc(s, tcg_target_available_regs[itype], - allocated_regs); - if (ts->val_type == TEMP_VAL_MEM) { - tcg_out_ld(s, itype, ts->reg, ts->mem_reg, ts->mem_offset); - ts->mem_coherent = 1; - } else if (ts->val_type == TEMP_VAL_CONST) { - tcg_out_movi(s, itype, ts->reg, ts->val); - ts->mem_coherent = 0; - } - s->reg_to_temp[ts->reg] = args[1]; - ts->val_type = TEMP_VAL_REG; + temp_load(s, ts, tcg_target_available_regs[itype], allocated_regs); } if (IS_DEAD_ARG(0) && !ots->fixed_reg) { /* mov to a non-saved dead register makes no sense (even with liveness analysis disabled). */ - assert(NEED_SYNC_ARG(0)); + tcg_debug_assert(NEED_SYNC_ARG(0)); /* The code above should have moved the temp to a register. */ - assert(ts->val_type == TEMP_VAL_REG); + tcg_debug_assert(ts->val_type == TEMP_VAL_REG); if (!ots->mem_allocated) { temp_allocate_frame(s, args[0]); } - tcg_out_st(s, otype, ts->reg, ots->mem_reg, ots->mem_offset); + if (ots->indirect_reg) { + tcg_regset_set_reg(allocated_regs, ts->reg); + temp_load(s, ots->mem_base, + tcg_target_available_regs[TCG_TYPE_PTR], + allocated_regs); + } + tcg_out_st(s, otype, ts->reg, ots->mem_base->reg, ots->mem_offset); if (IS_DEAD_ARG(1)) { - temp_dead(s, args[1]); + temp_dead(s, ts); } - temp_dead(s, args[0]); + temp_dead(s, ots); } else if (ts->val_type == TEMP_VAL_CONST) { /* propagate constant */ if (ots->val_type == TEMP_VAL_REG) { - s->reg_to_temp[ots->reg] = -1; + s->reg_to_temp[ots->reg] = NULL; } ots->val_type = TEMP_VAL_CONST; ots->val = ts->val; if (IS_DEAD_ARG(1)) { - temp_dead(s, args[1]); + temp_dead(s, ts); } } else { /* The code in the first if block should have moved the temp to a register. */ - assert(ts->val_type == TEMP_VAL_REG); + tcg_debug_assert(ts->val_type == TEMP_VAL_REG); if (IS_DEAD_ARG(1) && !ts->fixed_reg && !ots->fixed_reg) { /* the mov can be suppressed */ if (ots->val_type == TEMP_VAL_REG) { - s->reg_to_temp[ots->reg] = -1; + s->reg_to_temp[ots->reg] = NULL; } ots->reg = ts->reg; - temp_dead(s, args[1]); + temp_dead(s, ts); } else { if (ots->val_type != TEMP_VAL_REG) { /* When allocating a new register, make sure to not spill the input one. */ tcg_regset_set_reg(allocated_regs, ts->reg); ots->reg = tcg_reg_alloc(s, tcg_target_available_regs[otype], - allocated_regs); + allocated_regs, ots->indirect_base); } tcg_out_mov(s, otype, ots->reg, ts->reg); } ots->val_type = TEMP_VAL_REG; ots->mem_coherent = 0; - s->reg_to_temp[ots->reg] = args[0]; + s->reg_to_temp[ots->reg] = ots; if (NEED_SYNC_ARG(0)) { - tcg_reg_sync(s, ots->reg); + tcg_reg_sync(s, ots->reg, allocated_regs); } } } @@ -1960,7 +2010,8 @@ static void tcg_reg_alloc_op(TCGContext *s, uint8_t sync_args) { TCGRegSet allocated_regs; - int i, k, nb_iargs, nb_oargs, reg; + int i, k, nb_iargs, nb_oargs; + TCGReg reg; TCGArg arg; const TCGArgConstraint *arg_ct; TCGTemp *ts; @@ -1982,30 +2033,17 @@ static void tcg_reg_alloc_op(TCGContext *s, arg = args[i]; arg_ct = &def->args_ct[i]; ts = &s->temps[arg]; - if (ts->val_type == TEMP_VAL_MEM) { - reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); - tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset); - ts->val_type = TEMP_VAL_REG; - ts->reg = reg; - ts->mem_coherent = 1; - s->reg_to_temp[reg] = arg; - } else if (ts->val_type == TEMP_VAL_CONST) { - if (tcg_target_const_match(ts->val, ts->type, arg_ct)) { - /* constant is OK for instruction */ - const_args[i] = 1; - new_args[i] = ts->val; - goto iarg_end; - } else { - /* need to move to a register */ - reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); - tcg_out_movi(s, ts->type, reg, ts->val); - ts->val_type = TEMP_VAL_REG; - ts->reg = reg; - ts->mem_coherent = 0; - s->reg_to_temp[reg] = arg; - } + + if (ts->val_type == TEMP_VAL_CONST + && tcg_target_const_match(ts->val, ts->type, arg_ct)) { + /* constant is OK for instruction */ + const_args[i] = 1; + new_args[i] = ts->val; + goto iarg_end; } - assert(ts->val_type == TEMP_VAL_REG); + + temp_load(s, ts, arg_ct->u.regs, allocated_regs); + if (arg_ct->ct & TCG_CT_IALIAS) { if (ts->fixed_reg) { /* if fixed register, we must allocate a new register @@ -2038,7 +2076,8 @@ static void tcg_reg_alloc_op(TCGContext *s, allocate_in_reg: /* allocate a new register matching the constraint and move the temporary register into it */ - reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); + reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs, + ts->indirect_base); tcg_out_mov(s, ts->type, reg, ts->reg); } new_args[i] = reg; @@ -2050,7 +2089,7 @@ static void tcg_reg_alloc_op(TCGContext *s, /* mark dead temporaries and free the associated registers */ for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { if (IS_DEAD_ARG(i)) { - temp_dead(s, args[i]); + temp_dead(s, &s->temps[args[i]]); } } @@ -2059,9 +2098,9 @@ static void tcg_reg_alloc_op(TCGContext *s, } else { if (def->flags & TCG_OPF_CALL_CLOBBER) { /* XXX: permit generic clobber register list ? */ - for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) { - if (tcg_regset_test_reg(tcg_target_call_clobber_regs, reg)) { - tcg_reg_free(s, reg); + for (i = 0; i < TCG_TARGET_NB_REGS; i++) { + if (tcg_regset_test_reg(tcg_target_call_clobber_regs, i)) { + tcg_reg_free(s, i, allocated_regs); } } } @@ -2087,20 +2126,21 @@ static void tcg_reg_alloc_op(TCGContext *s, tcg_regset_test_reg(arg_ct->u.regs, reg)) { goto oarg_end; } - reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); + reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs, + ts->indirect_base); } tcg_regset_set_reg(allocated_regs, reg); /* if a fixed register is used, then a move will be done afterwards */ if (!ts->fixed_reg) { if (ts->val_type == TEMP_VAL_REG) { - s->reg_to_temp[ts->reg] = -1; + s->reg_to_temp[ts->reg] = NULL; } ts->val_type = TEMP_VAL_REG; ts->reg = reg; /* temp value is modified, so the value kept in memory is potentially not the same */ ts->mem_coherent = 0; - s->reg_to_temp[reg] = arg; + s->reg_to_temp[reg] = ts; } oarg_end: new_args[i] = reg; @@ -2118,10 +2158,10 @@ static void tcg_reg_alloc_op(TCGContext *s, tcg_out_mov(s, ts->type, ts->reg, reg); } if (NEED_SYNC_ARG(i)) { - tcg_reg_sync(s, reg); + tcg_reg_sync(s, reg, allocated_regs); } if (IS_DEAD_ARG(i)) { - temp_dead(s, args[i]); + temp_dead(s, ts); } } } @@ -2136,7 +2176,8 @@ static void tcg_reg_alloc_call(TCGContext *s, int nb_oargs, int nb_iargs, const TCGArg * const args, uint16_t dead_args, uint8_t sync_args) { - int flags, nb_regs, i, reg; + int flags, nb_regs, i; + TCGReg reg; TCGArg arg; TCGTemp *ts; intptr_t stack_offset; @@ -2172,23 +2213,9 @@ static void tcg_reg_alloc_call(TCGContext *s, int nb_oargs, int nb_iargs, #endif if (arg != TCG_CALL_DUMMY_ARG) { ts = &s->temps[arg]; - if (ts->val_type == TEMP_VAL_REG) { - tcg_out_st(s, ts->type, ts->reg, TCG_REG_CALL_STACK, stack_offset); - } else if (ts->val_type == TEMP_VAL_MEM) { - reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type], - s->reserved_regs); - /* XXX: not correct if reading values from the stack */ - tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset); - tcg_out_st(s, ts->type, reg, TCG_REG_CALL_STACK, stack_offset); - } else if (ts->val_type == TEMP_VAL_CONST) { - reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type], - s->reserved_regs); - /* XXX: sign extend may be needed on some targets */ - tcg_out_movi(s, ts->type, reg, ts->val); - tcg_out_st(s, ts->type, reg, TCG_REG_CALL_STACK, stack_offset); - } else { - tcg_abort(); - } + temp_load(s, ts, tcg_target_available_regs[ts->type], + s->reserved_regs); + tcg_out_st(s, ts->type, ts->reg, TCG_REG_CALL_STACK, stack_offset); } #ifndef TCG_TARGET_STACK_GROWSUP stack_offset += sizeof(tcg_target_long); @@ -2202,19 +2229,20 @@ static void tcg_reg_alloc_call(TCGContext *s, int nb_oargs, int nb_iargs, if (arg != TCG_CALL_DUMMY_ARG) { ts = &s->temps[arg]; reg = tcg_target_call_iarg_regs[i]; - tcg_reg_free(s, reg); + tcg_reg_free(s, reg, allocated_regs); + if (ts->val_type == TEMP_VAL_REG) { if (ts->reg != reg) { tcg_out_mov(s, ts->type, reg, ts->reg); } - } else if (ts->val_type == TEMP_VAL_MEM) { - tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset); - } else if (ts->val_type == TEMP_VAL_CONST) { - /* XXX: sign extend ? */ - tcg_out_movi(s, ts->type, reg, ts->val); } else { - tcg_abort(); + TCGRegSet arg_set; + + tcg_regset_clear(arg_set); + tcg_regset_set_reg(arg_set, reg); + temp_load(s, ts, arg_set, allocated_regs); } + tcg_regset_set_reg(allocated_regs, reg); } } @@ -2222,14 +2250,14 @@ static void tcg_reg_alloc_call(TCGContext *s, int nb_oargs, int nb_iargs, /* mark dead temporaries and free the associated registers */ for(i = nb_oargs; i < nb_iargs + nb_oargs; i++) { if (IS_DEAD_ARG(i)) { - temp_dead(s, args[i]); + temp_dead(s, &s->temps[args[i]]); } } /* clobber call registers */ - for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) { - if (tcg_regset_test_reg(tcg_target_call_clobber_regs, reg)) { - tcg_reg_free(s, reg); + for (i = 0; i < TCG_TARGET_NB_REGS; i++) { + if (tcg_regset_test_reg(tcg_target_call_clobber_regs, i)) { + tcg_reg_free(s, i, allocated_regs); } } @@ -2250,7 +2278,7 @@ static void tcg_reg_alloc_call(TCGContext *s, int nb_oargs, int nb_iargs, arg = args[i]; ts = &s->temps[arg]; reg = tcg_target_call_oarg_regs[i]; - assert(s->reg_to_temp[reg] == -1); + tcg_debug_assert(s->reg_to_temp[reg] == NULL); if (ts->fixed_reg) { if (ts->reg != reg) { @@ -2258,17 +2286,17 @@ static void tcg_reg_alloc_call(TCGContext *s, int nb_oargs, int nb_iargs, } } else { if (ts->val_type == TEMP_VAL_REG) { - s->reg_to_temp[ts->reg] = -1; + s->reg_to_temp[ts->reg] = NULL; } ts->val_type = TEMP_VAL_REG; ts->reg = reg; ts->mem_coherent = 0; - s->reg_to_temp[reg] = arg; + s->reg_to_temp[reg] = ts; if (NEED_SYNC_ARG(i)) { - tcg_reg_sync(s, reg); + tcg_reg_sync(s, reg, allocated_regs); } if (IS_DEAD_ARG(i)) { - temp_dead(s, args[i]); + temp_dead(s, ts); } } } @@ -2295,14 +2323,31 @@ void tcg_dump_op_count(FILE *f, fprintf_function cpu_fprintf) #endif -static inline int tcg_gen_code_common(TCGContext *s, - tcg_insn_unit *gen_code_buf, - long search_pc) +int tcg_gen_code(TCGContext *s, TranslationBlock *tb) { - int oi, oi_next; + int i, oi, oi_next, num_insns; + +#ifdef CONFIG_PROFILER + { + int n; + + n = s->gen_last_op_idx + 1; + s->op_count += n; + if (n > s->op_count_max) { + s->op_count_max = n; + } + + n = s->nb_temps; + s->temp_count += n; + if (n > s->temp_count_max) { + s->temp_count_max = n; + } + } +#endif #ifdef DEBUG_DISAS - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP) + && qemu_log_in_addr_range(tb->pc))) { qemu_log("OP:\n"); tcg_dump_ops(s); qemu_log("\n"); @@ -2329,7 +2374,8 @@ static inline int tcg_gen_code_common(TCGContext *s, #endif #ifdef DEBUG_DISAS - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP_OPT))) { + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP_OPT) + && qemu_log_in_addr_range(tb->pc))) { qemu_log("OP after optimization and liveness analysis:\n"); tcg_dump_ops(s); qemu_log("\n"); @@ -2338,11 +2384,12 @@ static inline int tcg_gen_code_common(TCGContext *s, tcg_reg_alloc_start(s); - s->code_buf = gen_code_buf; - s->code_ptr = gen_code_buf; + s->code_buf = tb->tc_ptr; + s->code_ptr = tb->tc_ptr; tcg_out_tb_init(s); + num_insns = -1; for (oi = s->gen_first_op_idx; oi >= 0; oi = oi_next) { TCGOp * const op = &s->gen_op_buf[oi]; TCGArg * const args = &s->gen_opparam_buf[op->args]; @@ -2365,10 +2412,23 @@ static inline int tcg_gen_code_common(TCGContext *s, case INDEX_op_movi_i64: tcg_reg_alloc_movi(s, args, dead_args, sync_args); break; - case INDEX_op_debug_insn_start: + case INDEX_op_insn_start: + if (num_insns >= 0) { + s->gen_insn_end_off[num_insns] = tcg_current_code_size(s); + } + num_insns++; + for (i = 0; i < TARGET_INSN_START_WORDS; ++i) { + target_ulong a; +#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS + a = ((target_ulong)args[i * 2 + 1] << 32) | args[i * 2]; +#else + a = args[i]; +#endif + s->gen_insn_data[num_insns][i] = a; + } break; case INDEX_op_discard: - temp_dead(s, args[0]); + temp_dead(s, &s->temps[args[0]]); break; case INDEX_op_set_label: tcg_reg_alloc_bb_end(s, s->reserved_regs); @@ -2389,40 +2449,24 @@ static inline int tcg_gen_code_common(TCGContext *s, tcg_reg_alloc_op(s, def, opc, args, dead_args, sync_args); break; } - if (search_pc >= 0 && search_pc < tcg_current_code_size(s)) { - return oi; - } -#ifndef NDEBUG +#ifdef CONFIG_DEBUG_TCG check_regs(s); #endif + /* Test for (pending) buffer overflow. The assumption is that any + one operation beginning below the high water mark cannot overrun + the buffer completely. Thus we can test for overflow after + generating code without having to check during generation. */ + if (unlikely((void *)s->code_ptr > s->code_gen_highwater)) { + return -1; + } } + tcg_debug_assert(num_insns >= 0); + s->gen_insn_end_off[num_insns] = tcg_current_code_size(s); /* Generate TB finalization at the end of block */ - tcg_out_tb_finalize(s); - return -1; -} - -int tcg_gen_code(TCGContext *s, tcg_insn_unit *gen_code_buf) -{ -#ifdef CONFIG_PROFILER - { - int n; - - n = s->gen_last_op_idx + 1; - s->op_count += n; - if (n > s->op_count_max) { - s->op_count_max = n; - } - - n = s->nb_temps; - s->temp_count += n; - if (n > s->temp_count_max) { - s->temp_count_max = n; - } + if (!tcg_out_tb_finalize(s)) { + return -1; } -#endif - - tcg_gen_code_common(s, gen_code_buf, -1); /* flush instruction cache */ flush_icache_range((uintptr_t)s->code_buf, (uintptr_t)s->code_ptr); @@ -2430,38 +2474,30 @@ int tcg_gen_code(TCGContext *s, tcg_insn_unit *gen_code_buf) return tcg_current_code_size(s); } -/* Return the index of the micro operation such as the pc after is < - offset bytes from the start of the TB. The contents of gen_code_buf must - not be changed, though writing the same values is ok. - Return -1 if not found. */ -int tcg_gen_code_search_pc(TCGContext *s, tcg_insn_unit *gen_code_buf, - long offset) -{ - return tcg_gen_code_common(s, gen_code_buf, offset); -} - #ifdef CONFIG_PROFILER void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf) { TCGContext *s = &tcg_ctx; - int64_t tot; + int64_t tb_count = s->tb_count; + int64_t tb_div_count = tb_count ? tb_count : 1; + int64_t tot = s->interm_time + s->code_time; - tot = s->interm_time + s->code_time; cpu_fprintf(f, "JIT cycles %" PRId64 " (%0.3f s at 2.4 GHz)\n", tot, tot / 2.4e9); cpu_fprintf(f, "translated TBs %" PRId64 " (aborted=%" PRId64 " %0.1f%%)\n", - s->tb_count, - s->tb_count1 - s->tb_count, - s->tb_count1 ? (double)(s->tb_count1 - s->tb_count) / s->tb_count1 * 100.0 : 0); + tb_count, s->tb_count1 - tb_count, + (double)(s->tb_count1 - s->tb_count) + / (s->tb_count1 ? s->tb_count1 : 1) * 100.0); cpu_fprintf(f, "avg ops/TB %0.1f max=%d\n", - s->tb_count ? (double)s->op_count / s->tb_count : 0, s->op_count_max); + (double)s->op_count / tb_div_count, s->op_count_max); cpu_fprintf(f, "deleted ops/TB %0.2f\n", - s->tb_count ? - (double)s->del_op_count / s->tb_count : 0); + (double)s->del_op_count / tb_div_count); cpu_fprintf(f, "avg temps/TB %0.2f max=%d\n", - s->tb_count ? - (double)s->temp_count / s->tb_count : 0, - s->temp_count_max); + (double)s->temp_count / tb_div_count, s->temp_count_max); + cpu_fprintf(f, "avg host code/TB %0.1f\n", + (double)s->code_out_len / tb_div_count); + cpu_fprintf(f, "avg search data/TB %0.1f\n", + (double)s->search_out_len / tb_div_count); cpu_fprintf(f, "cycles/op %0.1f\n", s->op_count ? (double)tot / s->op_count : 0); @@ -2469,8 +2505,11 @@ void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf) s->code_in_len ? (double)tot / s->code_in_len : 0); cpu_fprintf(f, "cycles/out byte %0.1f\n", s->code_out_len ? (double)tot / s->code_out_len : 0); - if (tot == 0) + cpu_fprintf(f, "cycles/search byte %0.1f\n", + s->search_out_len ? (double)tot / s->search_out_len : 0); + if (tot == 0) { tot = 1; + } cpu_fprintf(f, " gen_interm time %0.1f%%\n", (double)s->interm_time / tot * 100.0); cpu_fprintf(f, " gen_code time %0.1f%%\n", diff --git a/qemu/tcg/tcg.h b/qemu/tcg/tcg.h index 231a78152..40c8fbe2a 100644 --- a/qemu/tcg/tcg.h +++ b/qemu/tcg/tcg.h @@ -66,7 +66,8 @@ typedef uint64_t TCGRegSet; #if TCG_TARGET_REG_BITS == 32 /* Turn some undef macros into false macros. */ -#define TCG_TARGET_HAS_trunc_shr_i32 0 +#define TCG_TARGET_HAS_extrl_i64_i32 0 +#define TCG_TARGET_HAS_extrh_i64_i32 0 #define TCG_TARGET_HAS_div_i64 0 #define TCG_TARGET_HAS_rem_i64 0 #define TCG_TARGET_HAS_div2_i64 0 @@ -128,6 +129,12 @@ typedef uint64_t TCGRegSet; # error "Missing unsigned widening multiply" #endif +#ifndef TARGET_INSN_START_EXTRA_WORDS +# define TARGET_INSN_START_WORDS 1 +#else +# define TARGET_INSN_START_WORDS (1 + TARGET_INSN_START_EXTRA_WORDS) +#endif + typedef enum TCGOpcode { #define DEF(name, oargs, iargs, cargs, flags) INDEX_op_ ## name, #include "tcg-opc.h" @@ -187,6 +194,7 @@ typedef struct TCGPool { #define TCG_POOL_CHUNK_SIZE 32768 #define TCG_MAX_TEMPS 512 +#define TCG_MAX_INSNS 512 /* when the size of the arguments of a called function is smaller than this value, they are statically allocated in the TB stack frame */ @@ -300,6 +308,14 @@ typedef tcg_target_ulong TCGArg; typedef struct TCGv_i32_d *TCGv_i32; typedef struct TCGv_i64_d *TCGv_i64; typedef struct TCGv_ptr_d *TCGv_ptr; +typedef TCGv_ptr TCGv_env; +#if TARGET_LONG_BITS == 32 +#define TCGv TCGv_i32 +#elif TARGET_LONG_BITS == 64 +#define TCGv TCGv_i64 +#else +#error Unhandled TARGET_LONG_BITS value +#endif static inline TCGv_i32 QEMU_ARTIFICIAL MAKE_TCGV_I32(intptr_t i) { @@ -440,12 +456,13 @@ typedef enum TCGTempVal { } TCGTempVal; typedef struct TCGTemp { - unsigned int reg:8; - unsigned int mem_reg:8; + TCGReg reg:8; TCGTempVal val_type:8; TCGType base_type:8; TCGType type:8; unsigned int fixed_reg:1; + unsigned int indirect_reg:1; + unsigned int indirect_base:1; unsigned int mem_coherent:1; unsigned int mem_allocated:1; unsigned int temp_local:1; /* If true, the temp is saved across @@ -454,6 +471,7 @@ typedef struct TCGTemp { unsigned int temp_allocated:1; /* never used for code gen */ tcg_target_long val; + struct TCGTemp *mem_base; intptr_t mem_offset; const char *name; } TCGTemp; @@ -507,7 +525,7 @@ struct TCGContext { intptr_t current_frame_offset; intptr_t frame_start; intptr_t frame_end; - int frame_reg; + TCGTemp *frame_temp; tcg_insn_unit *code_ptr; @@ -524,6 +542,7 @@ struct TCGContext { int64_t del_op_count; int64_t code_in_len; int64_t code_out_len; + int64_t search_out_len; int64_t interm_time; int64_t code_time; int64_t la_time; @@ -550,28 +569,28 @@ struct TCGContext { void *code_gen_prologue; void *code_gen_buffer; size_t code_gen_buffer_size; - /* threshold to flush the translated code buffer */ - size_t code_gen_buffer_max_size; void *code_gen_ptr; + /* Threshold to flush the translated code buffer. */ + void *code_gen_highwater; + TBContext tb_ctx; - /* The TCGBackendData structure is private to tcg-target.c. */ + /* The TCGBackendData structure is private to tcg-target.inc.c. */ struct TCGBackendData *be; TCGTempSet free_temps[TCG_TYPE_COUNT * 2]; TCGTemp temps[TCG_MAX_TEMPS]; /* globals first, temps after */ - /* tells in which temporary a given register is. It does not take - into account fixed registers */ - int reg_to_temp[TCG_TARGET_NB_REGS]; + /* Tells which temporary holds a given register. + It does not take into account fixed registers */ + TCGTemp *reg_to_temp[TCG_TARGET_NB_REGS]; TCGOp gen_op_buf[OPC_BUF_SIZE]; TCGArg gen_opparam_buf[OPPARAM_BUF_SIZE]; - target_ulong gen_opc_pc[OPC_BUF_SIZE]; - uint16_t gen_opc_icount[OPC_BUF_SIZE]; - uint8_t gen_opc_instr_start[OPC_BUF_SIZE]; + uint16_t gen_insn_end_off[TCG_MAX_INSNS]; + target_ulong gen_insn_data[TCG_MAX_INSNS][TARGET_INSN_START_WORDS]; }; extern TCGContext tcg_ctx; @@ -594,6 +613,10 @@ void *tcg_malloc_internal(TCGContext *s, int size); void tcg_pool_reset(TCGContext *s); void tcg_pool_delete(TCGContext *s); +void tb_lock(void); +void tb_unlock(void); +void tb_lock_reset(void); + static inline void *tcg_malloc(int size) { TCGContext *s = &tcg_ctx; @@ -613,39 +636,54 @@ void tcg_context_init(TCGContext *s); void tcg_prologue_init(TCGContext *s); void tcg_func_start(TCGContext *s); -int tcg_gen_code(TCGContext *s, tcg_insn_unit *gen_code_buf); -int tcg_gen_code_search_pc(TCGContext *s, tcg_insn_unit *gen_code_buf, - long offset); +int tcg_gen_code(TCGContext *s, TranslationBlock *tb); -void tcg_set_frame(TCGContext *s, int reg, intptr_t start, intptr_t size); +void tcg_set_frame(TCGContext *s, TCGReg reg, intptr_t start, intptr_t size); + +int tcg_global_mem_new_internal(TCGType, TCGv_ptr, intptr_t, const char *); + +TCGv_i32 tcg_global_reg_new_i32(TCGReg reg, const char *name); +TCGv_i64 tcg_global_reg_new_i64(TCGReg reg, const char *name); -TCGv_i32 tcg_global_reg_new_i32(int reg, const char *name); -TCGv_i32 tcg_global_mem_new_i32(int reg, intptr_t offset, const char *name); TCGv_i32 tcg_temp_new_internal_i32(int temp_local); +TCGv_i64 tcg_temp_new_internal_i64(int temp_local); + +void tcg_temp_free_i32(TCGv_i32 arg); +void tcg_temp_free_i64(TCGv_i64 arg); + +static inline TCGv_i32 tcg_global_mem_new_i32(TCGv_ptr reg, intptr_t offset, + const char *name) +{ + int idx = tcg_global_mem_new_internal(TCG_TYPE_I32, reg, offset, name); + return MAKE_TCGV_I32(idx); +} + static inline TCGv_i32 tcg_temp_new_i32(void) { return tcg_temp_new_internal_i32(0); } + static inline TCGv_i32 tcg_temp_local_new_i32(void) { return tcg_temp_new_internal_i32(1); } -void tcg_temp_free_i32(TCGv_i32 arg); -char *tcg_get_arg_str_i32(TCGContext *s, char *buf, int buf_size, TCGv_i32 arg); -TCGv_i64 tcg_global_reg_new_i64(int reg, const char *name); -TCGv_i64 tcg_global_mem_new_i64(int reg, intptr_t offset, const char *name); -TCGv_i64 tcg_temp_new_internal_i64(int temp_local); +static inline TCGv_i64 tcg_global_mem_new_i64(TCGv_ptr reg, intptr_t offset, + const char *name) +{ + int idx = tcg_global_mem_new_internal(TCG_TYPE_I64, reg, offset, name); + return MAKE_TCGV_I64(idx); +} + static inline TCGv_i64 tcg_temp_new_i64(void) { return tcg_temp_new_internal_i64(0); } + static inline TCGv_i64 tcg_temp_local_new_i64(void) { return tcg_temp_new_internal_i64(1); } -void tcg_temp_free_i64(TCGv_i64 arg); -char *tcg_get_arg_str_i64(TCGContext *s, char *buf, int buf_size, TCGv_i64 arg); #if defined(CONFIG_DEBUG_TCG) /* If you call tcg_clear_temp_count() at the start of a section of @@ -985,25 +1023,48 @@ void helper_be_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val, void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, TCGMemOpIdx oi, uintptr_t retaddr); +uint8_t helper_ret_ldb_cmmu(CPUArchState *env, target_ulong addr, + TCGMemOpIdx oi, uintptr_t retaddr); +uint16_t helper_le_ldw_cmmu(CPUArchState *env, target_ulong addr, + TCGMemOpIdx oi, uintptr_t retaddr); +uint32_t helper_le_ldl_cmmu(CPUArchState *env, target_ulong addr, + TCGMemOpIdx oi, uintptr_t retaddr); +uint64_t helper_le_ldq_cmmu(CPUArchState *env, target_ulong addr, + TCGMemOpIdx oi, uintptr_t retaddr); +uint16_t helper_be_ldw_cmmu(CPUArchState *env, target_ulong addr, + TCGMemOpIdx oi, uintptr_t retaddr); +uint32_t helper_be_ldl_cmmu(CPUArchState *env, target_ulong addr, + TCGMemOpIdx oi, uintptr_t retaddr); +uint64_t helper_be_ldq_cmmu(CPUArchState *env, target_ulong addr, + TCGMemOpIdx oi, uintptr_t retaddr); + /* Temporary aliases until backends are converted. */ #ifdef TARGET_WORDS_BIGENDIAN # define helper_ret_ldsw_mmu helper_be_ldsw_mmu # define helper_ret_lduw_mmu helper_be_lduw_mmu # define helper_ret_ldsl_mmu helper_be_ldsl_mmu # define helper_ret_ldul_mmu helper_be_ldul_mmu +# define helper_ret_ldl_mmu helper_be_ldul_mmu # define helper_ret_ldq_mmu helper_be_ldq_mmu # define helper_ret_stw_mmu helper_be_stw_mmu # define helper_ret_stl_mmu helper_be_stl_mmu # define helper_ret_stq_mmu helper_be_stq_mmu +# define helper_ret_ldw_cmmu helper_be_ldw_cmmu +# define helper_ret_ldl_cmmu helper_be_ldl_cmmu +# define helper_ret_ldq_cmmu helper_be_ldq_cmmu #else # define helper_ret_ldsw_mmu helper_le_ldsw_mmu # define helper_ret_lduw_mmu helper_le_lduw_mmu # define helper_ret_ldsl_mmu helper_le_ldsl_mmu # define helper_ret_ldul_mmu helper_le_ldul_mmu +# define helper_ret_ldl_mmu helper_le_ldul_mmu # define helper_ret_ldq_mmu helper_le_ldq_mmu # define helper_ret_stw_mmu helper_le_stw_mmu # define helper_ret_stl_mmu helper_le_stl_mmu # define helper_ret_stq_mmu helper_le_stq_mmu +# define helper_ret_ldw_cmmu helper_le_ldw_cmmu +# define helper_ret_ldl_cmmu helper_le_ldl_cmmu +# define helper_ret_ldq_cmmu helper_le_ldq_cmmu #endif #endif /* CONFIG_SOFTMMU */ diff --git a/qemu/tcg/tci/README b/qemu/tcg/tci/README index dc57f076b..3786b0915 100644 --- a/qemu/tcg/tci/README +++ b/qemu/tcg/tci/README @@ -21,7 +21,7 @@ This is what TCI (Tiny Code Interpreter) does. 2) Implementation Like each TCG host frontend, TCI implements the code generator in -tcg-target.c, tcg-target.h. Both files are in directory tcg/tci. +tcg-target.inc.c, tcg-target.h. Both files are in directory tcg/tci. The additional file tcg/tci.c adds the interpreter. @@ -123,7 +123,7 @@ u1 = linux-user-test works would also improve speed for hosts which support byte alignment). * A better disassembler for the pseudo code would be nice (a very primitive - disassembler is included in tcg-target.c). + disassembler is included in tcg-target.inc.c). * It might be useful to have a runtime option which selects the native TCG or TCI, so QEMU would have to include two TCGs. Today, selecting TCI diff --git a/qemu/tcg/tci/tcg-target.c b/qemu/tcg/tci/tcg-target.c deleted file mode 100644 index 83472dbcd..000000000 --- a/qemu/tcg/tci/tcg-target.c +++ /dev/null @@ -1,875 +0,0 @@ -/* - * Tiny Code Generator for QEMU - * - * Copyright (c) 2009, 2011 Stefan Weil - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "tcg-be-null.h" - -/* TODO list: - * - See TODO comments in code. - */ - -/* Marker for missing code. */ -#define TODO() \ - do { \ - fprintf(stderr, "TODO %s:%u: %s()\n", \ - __FILE__, __LINE__, __func__); \ - tcg_abort(); \ - } while (0) - -/* Bitfield n...m (in 32 bit value). */ -#define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m) - -/* Macros used in tcg_target_op_defs. */ -#define R "r" -#define RI "ri" -#if TCG_TARGET_REG_BITS == 32 -# define R64 "r", "r" -#else -# define R64 "r" -#endif -#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS -# define L "L", "L" -# define S "S", "S" -#else -# define L "L" -# define S "S" -#endif - -/* TODO: documentation. */ -static const TCGTargetOpDef tcg_target_op_defs[] = { - { INDEX_op_exit_tb, { NULL } }, - { INDEX_op_goto_tb, { NULL } }, - { INDEX_op_br, { NULL } }, - - { INDEX_op_ld8u_i32, { R, R } }, - { INDEX_op_ld8s_i32, { R, R } }, - { INDEX_op_ld16u_i32, { R, R } }, - { INDEX_op_ld16s_i32, { R, R } }, - { INDEX_op_ld_i32, { R, R } }, - { INDEX_op_st8_i32, { R, R } }, - { INDEX_op_st16_i32, { R, R } }, - { INDEX_op_st_i32, { R, R } }, - - { INDEX_op_add_i32, { R, RI, RI } }, - { INDEX_op_sub_i32, { R, RI, RI } }, - { INDEX_op_mul_i32, { R, RI, RI } }, -#if TCG_TARGET_HAS_div_i32 - { INDEX_op_div_i32, { R, R, R } }, - { INDEX_op_divu_i32, { R, R, R } }, - { INDEX_op_rem_i32, { R, R, R } }, - { INDEX_op_remu_i32, { R, R, R } }, -#elif TCG_TARGET_HAS_div2_i32 - { INDEX_op_div2_i32, { R, R, "0", "1", R } }, - { INDEX_op_divu2_i32, { R, R, "0", "1", R } }, -#endif - /* TODO: Does R, RI, RI result in faster code than R, R, RI? - If both operands are constants, we can optimize. */ - { INDEX_op_and_i32, { R, RI, RI } }, -#if TCG_TARGET_HAS_andc_i32 - { INDEX_op_andc_i32, { R, RI, RI } }, -#endif -#if TCG_TARGET_HAS_eqv_i32 - { INDEX_op_eqv_i32, { R, RI, RI } }, -#endif -#if TCG_TARGET_HAS_nand_i32 - { INDEX_op_nand_i32, { R, RI, RI } }, -#endif -#if TCG_TARGET_HAS_nor_i32 - { INDEX_op_nor_i32, { R, RI, RI } }, -#endif - { INDEX_op_or_i32, { R, RI, RI } }, -#if TCG_TARGET_HAS_orc_i32 - { INDEX_op_orc_i32, { R, RI, RI } }, -#endif - { INDEX_op_xor_i32, { R, RI, RI } }, - { INDEX_op_shl_i32, { R, RI, RI } }, - { INDEX_op_shr_i32, { R, RI, RI } }, - { INDEX_op_sar_i32, { R, RI, RI } }, -#if TCG_TARGET_HAS_rot_i32 - { INDEX_op_rotl_i32, { R, RI, RI } }, - { INDEX_op_rotr_i32, { R, RI, RI } }, -#endif -#if TCG_TARGET_HAS_deposit_i32 - { INDEX_op_deposit_i32, { R, "0", R } }, -#endif - - { INDEX_op_brcond_i32, { R, RI } }, - - { INDEX_op_setcond_i32, { R, R, RI } }, -#if TCG_TARGET_REG_BITS == 64 - { INDEX_op_setcond_i64, { R, R, RI } }, -#endif /* TCG_TARGET_REG_BITS == 64 */ - -#if TCG_TARGET_REG_BITS == 32 - /* TODO: Support R, R, R, R, RI, RI? Will it be faster? */ - { INDEX_op_add2_i32, { R, R, R, R, R, R } }, - { INDEX_op_sub2_i32, { R, R, R, R, R, R } }, - { INDEX_op_brcond2_i32, { R, R, RI, RI } }, - { INDEX_op_mulu2_i32, { R, R, R, R } }, - { INDEX_op_setcond2_i32, { R, R, R, RI, RI } }, -#endif - -#if TCG_TARGET_HAS_not_i32 - { INDEX_op_not_i32, { R, R } }, -#endif -#if TCG_TARGET_HAS_neg_i32 - { INDEX_op_neg_i32, { R, R } }, -#endif - -#if TCG_TARGET_REG_BITS == 64 - { INDEX_op_ld8u_i64, { R, R } }, - { INDEX_op_ld8s_i64, { R, R } }, - { INDEX_op_ld16u_i64, { R, R } }, - { INDEX_op_ld16s_i64, { R, R } }, - { INDEX_op_ld32u_i64, { R, R } }, - { INDEX_op_ld32s_i64, { R, R } }, - { INDEX_op_ld_i64, { R, R } }, - - { INDEX_op_st8_i64, { R, R } }, - { INDEX_op_st16_i64, { R, R } }, - { INDEX_op_st32_i64, { R, R } }, - { INDEX_op_st_i64, { R, R } }, - - { INDEX_op_add_i64, { R, RI, RI } }, - { INDEX_op_sub_i64, { R, RI, RI } }, - { INDEX_op_mul_i64, { R, RI, RI } }, -#if TCG_TARGET_HAS_div_i64 - { INDEX_op_div_i64, { R, R, R } }, - { INDEX_op_divu_i64, { R, R, R } }, - { INDEX_op_rem_i64, { R, R, R } }, - { INDEX_op_remu_i64, { R, R, R } }, -#elif TCG_TARGET_HAS_div2_i64 - { INDEX_op_div2_i64, { R, R, "0", "1", R } }, - { INDEX_op_divu2_i64, { R, R, "0", "1", R } }, -#endif - { INDEX_op_and_i64, { R, RI, RI } }, -#if TCG_TARGET_HAS_andc_i64 - { INDEX_op_andc_i64, { R, RI, RI } }, -#endif -#if TCG_TARGET_HAS_eqv_i64 - { INDEX_op_eqv_i64, { R, RI, RI } }, -#endif -#if TCG_TARGET_HAS_nand_i64 - { INDEX_op_nand_i64, { R, RI, RI } }, -#endif -#if TCG_TARGET_HAS_nor_i64 - { INDEX_op_nor_i64, { R, RI, RI } }, -#endif - { INDEX_op_or_i64, { R, RI, RI } }, -#if TCG_TARGET_HAS_orc_i64 - { INDEX_op_orc_i64, { R, RI, RI } }, -#endif - { INDEX_op_xor_i64, { R, RI, RI } }, - { INDEX_op_shl_i64, { R, RI, RI } }, - { INDEX_op_shr_i64, { R, RI, RI } }, - { INDEX_op_sar_i64, { R, RI, RI } }, -#if TCG_TARGET_HAS_rot_i64 - { INDEX_op_rotl_i64, { R, RI, RI } }, - { INDEX_op_rotr_i64, { R, RI, RI } }, -#endif -#if TCG_TARGET_HAS_deposit_i64 - { INDEX_op_deposit_i64, { R, "0", R } }, -#endif - { INDEX_op_brcond_i64, { R, RI } }, - -#if TCG_TARGET_HAS_ext8s_i64 - { INDEX_op_ext8s_i64, { R, R } }, -#endif -#if TCG_TARGET_HAS_ext16s_i64 - { INDEX_op_ext16s_i64, { R, R } }, -#endif -#if TCG_TARGET_HAS_ext32s_i64 - { INDEX_op_ext32s_i64, { R, R } }, -#endif -#if TCG_TARGET_HAS_ext8u_i64 - { INDEX_op_ext8u_i64, { R, R } }, -#endif -#if TCG_TARGET_HAS_ext16u_i64 - { INDEX_op_ext16u_i64, { R, R } }, -#endif -#if TCG_TARGET_HAS_ext32u_i64 - { INDEX_op_ext32u_i64, { R, R } }, -#endif -#if TCG_TARGET_HAS_bswap16_i64 - { INDEX_op_bswap16_i64, { R, R } }, -#endif -#if TCG_TARGET_HAS_bswap32_i64 - { INDEX_op_bswap32_i64, { R, R } }, -#endif -#if TCG_TARGET_HAS_bswap64_i64 - { INDEX_op_bswap64_i64, { R, R } }, -#endif -#if TCG_TARGET_HAS_not_i64 - { INDEX_op_not_i64, { R, R } }, -#endif -#if TCG_TARGET_HAS_neg_i64 - { INDEX_op_neg_i64, { R, R } }, -#endif -#endif /* TCG_TARGET_REG_BITS == 64 */ - - { INDEX_op_qemu_ld_i32, { R, L } }, - { INDEX_op_qemu_ld_i64, { R64, L } }, - - { INDEX_op_qemu_st_i32, { R, S } }, - { INDEX_op_qemu_st_i64, { R64, S } }, - -#if TCG_TARGET_HAS_ext8s_i32 - { INDEX_op_ext8s_i32, { R, R } }, -#endif -#if TCG_TARGET_HAS_ext16s_i32 - { INDEX_op_ext16s_i32, { R, R } }, -#endif -#if TCG_TARGET_HAS_ext8u_i32 - { INDEX_op_ext8u_i32, { R, R } }, -#endif -#if TCG_TARGET_HAS_ext16u_i32 - { INDEX_op_ext16u_i32, { R, R } }, -#endif - -#if TCG_TARGET_HAS_bswap16_i32 - { INDEX_op_bswap16_i32, { R, R } }, -#endif -#if TCG_TARGET_HAS_bswap32_i32 - { INDEX_op_bswap32_i32, { R, R } }, -#endif - - { -1 }, -}; - -static const int tcg_target_reg_alloc_order[] = { - TCG_REG_R0, - TCG_REG_R1, - TCG_REG_R2, - TCG_REG_R3, -#if 0 /* used for TCG_REG_CALL_STACK */ - TCG_REG_R4, -#endif - TCG_REG_R5, - TCG_REG_R6, - TCG_REG_R7, -#if TCG_TARGET_NB_REGS >= 16 - TCG_REG_R8, - TCG_REG_R9, - TCG_REG_R10, - TCG_REG_R11, - TCG_REG_R12, - TCG_REG_R13, - TCG_REG_R14, - TCG_REG_R15, -#endif -}; - -#if MAX_OPC_PARAM_IARGS != 5 -# error Fix needed, number of supported input arguments changed! -#endif - -static const int tcg_target_call_iarg_regs[] = { - TCG_REG_R0, - TCG_REG_R1, - TCG_REG_R2, - TCG_REG_R3, -#if 0 /* used for TCG_REG_CALL_STACK */ - TCG_REG_R4, -#endif - TCG_REG_R5, -#if TCG_TARGET_REG_BITS == 32 - /* 32 bit hosts need 2 * MAX_OPC_PARAM_IARGS registers. */ - TCG_REG_R6, - TCG_REG_R7, -#if TCG_TARGET_NB_REGS >= 16 - TCG_REG_R8, - TCG_REG_R9, - TCG_REG_R10, -#else -# error Too few input registers available -#endif -#endif -}; - -static const int tcg_target_call_oarg_regs[] = { - TCG_REG_R0, -#if TCG_TARGET_REG_BITS == 32 - TCG_REG_R1 -#endif -}; - -#ifndef NDEBUG -static const char *const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { - "r00", - "r01", - "r02", - "r03", - "r04", - "r05", - "r06", - "r07", -#if TCG_TARGET_NB_REGS >= 16 - "r08", - "r09", - "r10", - "r11", - "r12", - "r13", - "r14", - "r15", -#if TCG_TARGET_NB_REGS >= 32 - "r16", - "r17", - "r18", - "r19", - "r20", - "r21", - "r22", - "r23", - "r24", - "r25", - "r26", - "r27", - "r28", - "r29", - "r30", - "r31" -#endif -#endif -}; -#endif - -static void patch_reloc(tcg_insn_unit *code_ptr, int type, - intptr_t value, intptr_t addend) -{ - /* tcg_out_reloc always uses the same type, addend. */ - assert(type == sizeof(tcg_target_long)); - assert(addend == 0); - assert(value != 0); - if (TCG_TARGET_REG_BITS == 32) { - tcg_patch32(code_ptr, value); - } else { - tcg_patch64(code_ptr, value); - } -} - -/* Parse target specific constraints. */ -static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) -{ - const char *ct_str = *pct_str; - switch (ct_str[0]) { - case 'r': - case 'L': /* qemu_ld constraint */ - case 'S': /* qemu_st constraint */ - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, BIT(TCG_TARGET_NB_REGS) - 1); - break; - default: - return -1; - } - ct_str++; - *pct_str = ct_str; - return 0; -} - -#if defined(CONFIG_DEBUG_TCG_INTERPRETER) -/* Show current bytecode. Used by tcg interpreter. */ -void tci_disas(uint8_t opc) -{ - const TCGOpDef *def = &tcg_op_defs[opc]; - fprintf(stderr, "TCG %s %u, %u, %u\n", - def->name, def->nb_oargs, def->nb_iargs, def->nb_cargs); -} -#endif - -/* Write value (native size). */ -static void tcg_out_i(TCGContext *s, tcg_target_ulong v) -{ - if (TCG_TARGET_REG_BITS == 32) { - tcg_out32(s, v); - } else { - tcg_out64(s, v); - } -} - -/* Write opcode. */ -static void tcg_out_op_t(TCGContext *s, TCGOpcode op) -{ - tcg_out8(s, op); - tcg_out8(s, 0); -} - -/* Write register. */ -static void tcg_out_r(TCGContext *s, TCGArg t0) -{ - assert(t0 < TCG_TARGET_NB_REGS); - tcg_out8(s, t0); -} - -/* Write register or constant (native size). */ -static void tcg_out_ri(TCGContext *s, int const_arg, TCGArg arg) -{ - if (const_arg) { - assert(const_arg == 1); - tcg_out8(s, TCG_CONST); - tcg_out_i(s, arg); - } else { - tcg_out_r(s, arg); - } -} - -/* Write register or constant (32 bit). */ -static void tcg_out_ri32(TCGContext *s, int const_arg, TCGArg arg) -{ - if (const_arg) { - assert(const_arg == 1); - tcg_out8(s, TCG_CONST); - tcg_out32(s, arg); - } else { - tcg_out_r(s, arg); - } -} - -#if TCG_TARGET_REG_BITS == 64 -/* Write register or constant (64 bit). */ -static void tcg_out_ri64(TCGContext *s, int const_arg, TCGArg arg) -{ - if (const_arg) { - assert(const_arg == 1); - tcg_out8(s, TCG_CONST); - tcg_out64(s, arg); - } else { - tcg_out_r(s, arg); - } -} -#endif - -/* Write label. */ -static void tci_out_label(TCGContext *s, TCGLabel *label) -{ - if (label->has_value) { - tcg_out_i(s, label->u.value); - assert(label->u.value); - } else { - tcg_out_reloc(s, s->code_ptr, sizeof(tcg_target_ulong), label, 0); - s->code_ptr += sizeof(tcg_target_ulong); - } -} - -static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, - intptr_t arg2) -{ - uint8_t *old_code_ptr = s->code_ptr; - if (type == TCG_TYPE_I32) { - tcg_out_op_t(s, INDEX_op_ld_i32); - tcg_out_r(s, ret); - tcg_out_r(s, arg1); - tcg_out32(s, arg2); - } else { - assert(type == TCG_TYPE_I64); -#if TCG_TARGET_REG_BITS == 64 - tcg_out_op_t(s, INDEX_op_ld_i64); - tcg_out_r(s, ret); - tcg_out_r(s, arg1); - assert(arg2 == (int32_t)arg2); - tcg_out32(s, arg2); -#else - TODO(); -#endif - } - old_code_ptr[1] = s->code_ptr - old_code_ptr; -} - -static void tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) -{ - uint8_t *old_code_ptr = s->code_ptr; - assert(ret != arg); -#if TCG_TARGET_REG_BITS == 32 - tcg_out_op_t(s, INDEX_op_mov_i32); -#else - tcg_out_op_t(s, INDEX_op_mov_i64); -#endif - tcg_out_r(s, ret); - tcg_out_r(s, arg); - old_code_ptr[1] = s->code_ptr - old_code_ptr; -} - -static void tcg_out_movi(TCGContext *s, TCGType type, - TCGReg t0, tcg_target_long arg) -{ - uint8_t *old_code_ptr = s->code_ptr; - uint32_t arg32 = arg; - if (type == TCG_TYPE_I32 || arg == arg32) { - tcg_out_op_t(s, INDEX_op_movi_i32); - tcg_out_r(s, t0); - tcg_out32(s, arg32); - } else { - assert(type == TCG_TYPE_I64); -#if TCG_TARGET_REG_BITS == 64 - tcg_out_op_t(s, INDEX_op_movi_i64); - tcg_out_r(s, t0); - tcg_out64(s, arg); -#else - TODO(); -#endif - } - old_code_ptr[1] = s->code_ptr - old_code_ptr; -} - -static inline void tcg_out_call(TCGContext *s, tcg_insn_unit *arg) -{ - uint8_t *old_code_ptr = s->code_ptr; - tcg_out_op_t(s, INDEX_op_call); - tcg_out_ri(s, 1, (uintptr_t)arg); - old_code_ptr[1] = s->code_ptr - old_code_ptr; -} - -static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, - const int *const_args) -{ - uint8_t *old_code_ptr = s->code_ptr; - - tcg_out_op_t(s, opc); - - switch (opc) { - case INDEX_op_exit_tb: - tcg_out64(s, args[0]); - break; - case INDEX_op_goto_tb: - if (s->tb_jmp_offset) { - /* Direct jump method. */ - assert(args[0] < ARRAY_SIZE(s->tb_jmp_offset)); - s->tb_jmp_offset[args[0]] = tcg_current_code_size(s); - tcg_out32(s, 0); - } else { - /* Indirect jump method. */ - TODO(); - } - assert(args[0] < ARRAY_SIZE(s->tb_next_offset)); - s->tb_next_offset[args[0]] = tcg_current_code_size(s); - break; - case INDEX_op_br: - tci_out_label(s, arg_label(args[0])); - break; - case INDEX_op_setcond_i32: - tcg_out_r(s, args[0]); - tcg_out_r(s, args[1]); - tcg_out_ri32(s, const_args[2], args[2]); - tcg_out8(s, args[3]); /* condition */ - break; -#if TCG_TARGET_REG_BITS == 32 - case INDEX_op_setcond2_i32: - /* setcond2_i32 cond, t0, t1_low, t1_high, t2_low, t2_high */ - tcg_out_r(s, args[0]); - tcg_out_r(s, args[1]); - tcg_out_r(s, args[2]); - tcg_out_ri32(s, const_args[3], args[3]); - tcg_out_ri32(s, const_args[4], args[4]); - tcg_out8(s, args[5]); /* condition */ - break; -#elif TCG_TARGET_REG_BITS == 64 - case INDEX_op_setcond_i64: - tcg_out_r(s, args[0]); - tcg_out_r(s, args[1]); - tcg_out_ri64(s, const_args[2], args[2]); - tcg_out8(s, args[3]); /* condition */ - break; -#endif - case INDEX_op_ld8u_i32: - case INDEX_op_ld8s_i32: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16s_i32: - case INDEX_op_ld_i32: - case INDEX_op_st8_i32: - case INDEX_op_st16_i32: - case INDEX_op_st_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i64: - case INDEX_op_ld32u_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld_i64: - case INDEX_op_st8_i64: - case INDEX_op_st16_i64: - case INDEX_op_st32_i64: - case INDEX_op_st_i64: - tcg_out_r(s, args[0]); - tcg_out_r(s, args[1]); - assert(args[2] == (int32_t)args[2]); - tcg_out32(s, args[2]); - break; - case INDEX_op_add_i32: - case INDEX_op_sub_i32: - case INDEX_op_mul_i32: - case INDEX_op_and_i32: - case INDEX_op_andc_i32: /* Optional (TCG_TARGET_HAS_andc_i32). */ - case INDEX_op_eqv_i32: /* Optional (TCG_TARGET_HAS_eqv_i32). */ - case INDEX_op_nand_i32: /* Optional (TCG_TARGET_HAS_nand_i32). */ - case INDEX_op_nor_i32: /* Optional (TCG_TARGET_HAS_nor_i32). */ - case INDEX_op_or_i32: - case INDEX_op_orc_i32: /* Optional (TCG_TARGET_HAS_orc_i32). */ - case INDEX_op_xor_i32: - case INDEX_op_shl_i32: - case INDEX_op_shr_i32: - case INDEX_op_sar_i32: - case INDEX_op_rotl_i32: /* Optional (TCG_TARGET_HAS_rot_i32). */ - case INDEX_op_rotr_i32: /* Optional (TCG_TARGET_HAS_rot_i32). */ - tcg_out_r(s, args[0]); - tcg_out_ri32(s, const_args[1], args[1]); - tcg_out_ri32(s, const_args[2], args[2]); - break; - case INDEX_op_deposit_i32: /* Optional (TCG_TARGET_HAS_deposit_i32). */ - tcg_out_r(s, args[0]); - tcg_out_r(s, args[1]); - tcg_out_r(s, args[2]); - assert(args[3] <= UINT8_MAX); - tcg_out8(s, args[3]); - assert(args[4] <= UINT8_MAX); - tcg_out8(s, args[4]); - break; - -#if TCG_TARGET_REG_BITS == 64 - case INDEX_op_add_i64: - case INDEX_op_sub_i64: - case INDEX_op_mul_i64: - case INDEX_op_and_i64: - case INDEX_op_andc_i64: /* Optional (TCG_TARGET_HAS_andc_i64). */ - case INDEX_op_eqv_i64: /* Optional (TCG_TARGET_HAS_eqv_i64). */ - case INDEX_op_nand_i64: /* Optional (TCG_TARGET_HAS_nand_i64). */ - case INDEX_op_nor_i64: /* Optional (TCG_TARGET_HAS_nor_i64). */ - case INDEX_op_or_i64: - case INDEX_op_orc_i64: /* Optional (TCG_TARGET_HAS_orc_i64). */ - case INDEX_op_xor_i64: - case INDEX_op_shl_i64: - case INDEX_op_shr_i64: - case INDEX_op_sar_i64: - case INDEX_op_rotl_i64: /* Optional (TCG_TARGET_HAS_rot_i64). */ - case INDEX_op_rotr_i64: /* Optional (TCG_TARGET_HAS_rot_i64). */ - tcg_out_r(s, args[0]); - tcg_out_ri64(s, const_args[1], args[1]); - tcg_out_ri64(s, const_args[2], args[2]); - break; - case INDEX_op_deposit_i64: /* Optional (TCG_TARGET_HAS_deposit_i64). */ - tcg_out_r(s, args[0]); - tcg_out_r(s, args[1]); - tcg_out_r(s, args[2]); - assert(args[3] <= UINT8_MAX); - tcg_out8(s, args[3]); - assert(args[4] <= UINT8_MAX); - tcg_out8(s, args[4]); - break; - case INDEX_op_div_i64: /* Optional (TCG_TARGET_HAS_div_i64). */ - case INDEX_op_divu_i64: /* Optional (TCG_TARGET_HAS_div_i64). */ - case INDEX_op_rem_i64: /* Optional (TCG_TARGET_HAS_div_i64). */ - case INDEX_op_remu_i64: /* Optional (TCG_TARGET_HAS_div_i64). */ - TODO(); - break; - case INDEX_op_div2_i64: /* Optional (TCG_TARGET_HAS_div2_i64). */ - case INDEX_op_divu2_i64: /* Optional (TCG_TARGET_HAS_div2_i64). */ - TODO(); - break; - case INDEX_op_brcond_i64: - tcg_out_r(s, args[0]); - tcg_out_ri64(s, const_args[1], args[1]); - tcg_out8(s, args[2]); /* condition */ - tci_out_label(s, arg_label(args[3])); - break; - case INDEX_op_bswap16_i64: /* Optional (TCG_TARGET_HAS_bswap16_i64). */ - case INDEX_op_bswap32_i64: /* Optional (TCG_TARGET_HAS_bswap32_i64). */ - case INDEX_op_bswap64_i64: /* Optional (TCG_TARGET_HAS_bswap64_i64). */ - case INDEX_op_not_i64: /* Optional (TCG_TARGET_HAS_not_i64). */ - case INDEX_op_neg_i64: /* Optional (TCG_TARGET_HAS_neg_i64). */ - case INDEX_op_ext8s_i64: /* Optional (TCG_TARGET_HAS_ext8s_i64). */ - case INDEX_op_ext8u_i64: /* Optional (TCG_TARGET_HAS_ext8u_i64). */ - case INDEX_op_ext16s_i64: /* Optional (TCG_TARGET_HAS_ext16s_i64). */ - case INDEX_op_ext16u_i64: /* Optional (TCG_TARGET_HAS_ext16u_i64). */ - case INDEX_op_ext32s_i64: /* Optional (TCG_TARGET_HAS_ext32s_i64). */ - case INDEX_op_ext32u_i64: /* Optional (TCG_TARGET_HAS_ext32u_i64). */ -#endif /* TCG_TARGET_REG_BITS == 64 */ - case INDEX_op_neg_i32: /* Optional (TCG_TARGET_HAS_neg_i32). */ - case INDEX_op_not_i32: /* Optional (TCG_TARGET_HAS_not_i32). */ - case INDEX_op_ext8s_i32: /* Optional (TCG_TARGET_HAS_ext8s_i32). */ - case INDEX_op_ext16s_i32: /* Optional (TCG_TARGET_HAS_ext16s_i32). */ - case INDEX_op_ext8u_i32: /* Optional (TCG_TARGET_HAS_ext8u_i32). */ - case INDEX_op_ext16u_i32: /* Optional (TCG_TARGET_HAS_ext16u_i32). */ - case INDEX_op_bswap16_i32: /* Optional (TCG_TARGET_HAS_bswap16_i32). */ - case INDEX_op_bswap32_i32: /* Optional (TCG_TARGET_HAS_bswap32_i32). */ - tcg_out_r(s, args[0]); - tcg_out_r(s, args[1]); - break; - case INDEX_op_div_i32: /* Optional (TCG_TARGET_HAS_div_i32). */ - case INDEX_op_divu_i32: /* Optional (TCG_TARGET_HAS_div_i32). */ - case INDEX_op_rem_i32: /* Optional (TCG_TARGET_HAS_div_i32). */ - case INDEX_op_remu_i32: /* Optional (TCG_TARGET_HAS_div_i32). */ - tcg_out_r(s, args[0]); - tcg_out_ri32(s, const_args[1], args[1]); - tcg_out_ri32(s, const_args[2], args[2]); - break; - case INDEX_op_div2_i32: /* Optional (TCG_TARGET_HAS_div2_i32). */ - case INDEX_op_divu2_i32: /* Optional (TCG_TARGET_HAS_div2_i32). */ - TODO(); - break; -#if TCG_TARGET_REG_BITS == 32 - case INDEX_op_add2_i32: - case INDEX_op_sub2_i32: - tcg_out_r(s, args[0]); - tcg_out_r(s, args[1]); - tcg_out_r(s, args[2]); - tcg_out_r(s, args[3]); - tcg_out_r(s, args[4]); - tcg_out_r(s, args[5]); - break; - case INDEX_op_brcond2_i32: - tcg_out_r(s, args[0]); - tcg_out_r(s, args[1]); - tcg_out_ri32(s, const_args[2], args[2]); - tcg_out_ri32(s, const_args[3], args[3]); - tcg_out8(s, args[4]); /* condition */ - tci_out_label(s, arg_label(args[5])); - break; - case INDEX_op_mulu2_i32: - tcg_out_r(s, args[0]); - tcg_out_r(s, args[1]); - tcg_out_r(s, args[2]); - tcg_out_r(s, args[3]); - break; -#endif - case INDEX_op_brcond_i32: - tcg_out_r(s, args[0]); - tcg_out_ri32(s, const_args[1], args[1]); - tcg_out8(s, args[2]); /* condition */ - tci_out_label(s, arg_label(args[3])); - break; - case INDEX_op_qemu_ld_i32: - tcg_out_r(s, *args++); - tcg_out_r(s, *args++); - if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { - tcg_out_r(s, *args++); - } - tcg_out_i(s, *args++); - break; - case INDEX_op_qemu_ld_i64: - tcg_out_r(s, *args++); - if (TCG_TARGET_REG_BITS == 32) { - tcg_out_r(s, *args++); - } - tcg_out_r(s, *args++); - if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { - tcg_out_r(s, *args++); - } - tcg_out_i(s, *args++); - break; - case INDEX_op_qemu_st_i32: - tcg_out_r(s, *args++); - tcg_out_r(s, *args++); - if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { - tcg_out_r(s, *args++); - } - tcg_out_i(s, *args++); - break; - case INDEX_op_qemu_st_i64: - tcg_out_r(s, *args++); - if (TCG_TARGET_REG_BITS == 32) { - tcg_out_r(s, *args++); - } - tcg_out_r(s, *args++); - if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { - tcg_out_r(s, *args++); - } - tcg_out_i(s, *args++); - break; - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_mov_i64: - case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ - case INDEX_op_movi_i64: - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - default: - tcg_abort(); - } - old_code_ptr[1] = s->code_ptr - old_code_ptr; -} - -static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, TCGReg arg1, - intptr_t arg2) -{ - uint8_t *old_code_ptr = s->code_ptr; - if (type == TCG_TYPE_I32) { - tcg_out_op_t(s, INDEX_op_st_i32); - tcg_out_r(s, arg); - tcg_out_r(s, arg1); - tcg_out32(s, arg2); - } else { - assert(type == TCG_TYPE_I64); -#if TCG_TARGET_REG_BITS == 64 - tcg_out_op_t(s, INDEX_op_st_i64); - tcg_out_r(s, arg); - tcg_out_r(s, arg1); - tcg_out32(s, arg2); -#else - TODO(); -#endif - } - old_code_ptr[1] = s->code_ptr - old_code_ptr; -} - -/* Test if a constant matches the constraint. */ -static int tcg_target_const_match(tcg_target_long val, TCGType type, - const TCGArgConstraint *arg_ct) -{ - /* No need to return 0 or 1, 0 or != 0 is good enough. */ - return arg_ct->ct & TCG_CT_CONST; -} - -static void tcg_target_init(TCGContext *s) -{ -#if defined(CONFIG_DEBUG_TCG_INTERPRETER) - const char *envval = getenv("DEBUG_TCG"); - if (envval) { - qemu_set_log(strtol(envval, NULL, 0)); - } -#endif - - /* The current code uses uint8_t for tcg operations. */ - assert(ARRAY_SIZE(tcg_op_defs) <= UINT8_MAX); - - /* Registers available for 32 bit operations. */ - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, - BIT(TCG_TARGET_NB_REGS) - 1); - /* Registers available for 64 bit operations. */ - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, - BIT(TCG_TARGET_NB_REGS) - 1); - /* TODO: Which registers should be set here? */ - tcg_regset_set32(tcg_target_call_clobber_regs, 0, - BIT(TCG_TARGET_NB_REGS) - 1); - - tcg_regset_clear(s->reserved_regs); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); - tcg_add_target_add_op_defs(tcg_target_op_defs); - - /* We use negative offsets from "sp" so that we can distinguish - stores that might pretend to be call arguments. */ - tcg_set_frame(s, TCG_REG_CALL_STACK, - -CPU_TEMP_BUF_NLONGS * sizeof(long), - CPU_TEMP_BUF_NLONGS * sizeof(long)); -} - -/* Generate global QEMU prologue and epilogue code. */ -static inline void tcg_target_qemu_prologue(TCGContext *s) -{ -} diff --git a/qemu/tcg/tci/tcg-target.h b/qemu/tcg/tci/tcg-target.h index cbf3f9b5a..3942f9ccc 100644 --- a/qemu/tcg/tci/tcg-target.h +++ b/qemu/tcg/tci/tcg-target.h @@ -40,7 +40,6 @@ #if !defined(TCG_TARGET_H) #define TCG_TARGET_H -#include "config-host.h" #define TCG_TARGET_INTERPRETER 1 #define TCG_TARGET_INSN_UNIT_SIZE 1 @@ -84,7 +83,8 @@ #define TCG_TARGET_HAS_mulsh_i32 0 #if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_trunc_shr_i32 0 +#define TCG_TARGET_HAS_extrl_i64_i32 0 +#define TCG_TARGET_HAS_extrh_i64_i32 0 #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 diff --git a/qemu/tcg/tci/tcg-target.inc.c b/qemu/tcg/tci/tcg-target.inc.c new file mode 100644 index 000000000..e2fc52a16 --- /dev/null +++ b/qemu/tcg/tci/tcg-target.inc.c @@ -0,0 +1,879 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2009, 2011 Stefan Weil + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "tcg-be-null.h" + +/* TODO list: + * - See TODO comments in code. + */ + +/* Marker for missing code. */ +#define TODO() \ + do { \ + fprintf(stderr, "TODO %s:%u: %s()\n", \ + __FILE__, __LINE__, __func__); \ + tcg_abort(); \ + } while (0) + +/* Bitfield n...m (in 32 bit value). */ +#define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m) + +/* Macros used in tcg_target_op_defs. */ +#define R "r" +#define RI "ri" +#if TCG_TARGET_REG_BITS == 32 +# define R64 "r", "r" +#else +# define R64 "r" +#endif +#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS +# define L "L", "L" +# define S "S", "S" +#else +# define L "L" +# define S "S" +#endif + +/* TODO: documentation. */ +static const TCGTargetOpDef tcg_target_op_defs[] = { + { INDEX_op_exit_tb, { NULL } }, + { INDEX_op_goto_tb, { NULL } }, + { INDEX_op_br, { NULL } }, + + { INDEX_op_ld8u_i32, { R, R } }, + { INDEX_op_ld8s_i32, { R, R } }, + { INDEX_op_ld16u_i32, { R, R } }, + { INDEX_op_ld16s_i32, { R, R } }, + { INDEX_op_ld_i32, { R, R } }, + { INDEX_op_st8_i32, { R, R } }, + { INDEX_op_st16_i32, { R, R } }, + { INDEX_op_st_i32, { R, R } }, + + { INDEX_op_add_i32, { R, RI, RI } }, + { INDEX_op_sub_i32, { R, RI, RI } }, + { INDEX_op_mul_i32, { R, RI, RI } }, +#if TCG_TARGET_HAS_div_i32 + { INDEX_op_div_i32, { R, R, R } }, + { INDEX_op_divu_i32, { R, R, R } }, + { INDEX_op_rem_i32, { R, R, R } }, + { INDEX_op_remu_i32, { R, R, R } }, +#elif TCG_TARGET_HAS_div2_i32 + { INDEX_op_div2_i32, { R, R, "0", "1", R } }, + { INDEX_op_divu2_i32, { R, R, "0", "1", R } }, +#endif + /* TODO: Does R, RI, RI result in faster code than R, R, RI? + If both operands are constants, we can optimize. */ + { INDEX_op_and_i32, { R, RI, RI } }, +#if TCG_TARGET_HAS_andc_i32 + { INDEX_op_andc_i32, { R, RI, RI } }, +#endif +#if TCG_TARGET_HAS_eqv_i32 + { INDEX_op_eqv_i32, { R, RI, RI } }, +#endif +#if TCG_TARGET_HAS_nand_i32 + { INDEX_op_nand_i32, { R, RI, RI } }, +#endif +#if TCG_TARGET_HAS_nor_i32 + { INDEX_op_nor_i32, { R, RI, RI } }, +#endif + { INDEX_op_or_i32, { R, RI, RI } }, +#if TCG_TARGET_HAS_orc_i32 + { INDEX_op_orc_i32, { R, RI, RI } }, +#endif + { INDEX_op_xor_i32, { R, RI, RI } }, + { INDEX_op_shl_i32, { R, RI, RI } }, + { INDEX_op_shr_i32, { R, RI, RI } }, + { INDEX_op_sar_i32, { R, RI, RI } }, +#if TCG_TARGET_HAS_rot_i32 + { INDEX_op_rotl_i32, { R, RI, RI } }, + { INDEX_op_rotr_i32, { R, RI, RI } }, +#endif +#if TCG_TARGET_HAS_deposit_i32 + { INDEX_op_deposit_i32, { R, "0", R } }, +#endif + + { INDEX_op_brcond_i32, { R, RI } }, + + { INDEX_op_setcond_i32, { R, R, RI } }, +#if TCG_TARGET_REG_BITS == 64 + { INDEX_op_setcond_i64, { R, R, RI } }, +#endif /* TCG_TARGET_REG_BITS == 64 */ + +#if TCG_TARGET_REG_BITS == 32 + /* TODO: Support R, R, R, R, RI, RI? Will it be faster? */ + { INDEX_op_add2_i32, { R, R, R, R, R, R } }, + { INDEX_op_sub2_i32, { R, R, R, R, R, R } }, + { INDEX_op_brcond2_i32, { R, R, RI, RI } }, + { INDEX_op_mulu2_i32, { R, R, R, R } }, + { INDEX_op_setcond2_i32, { R, R, R, RI, RI } }, +#endif + +#if TCG_TARGET_HAS_not_i32 + { INDEX_op_not_i32, { R, R } }, +#endif +#if TCG_TARGET_HAS_neg_i32 + { INDEX_op_neg_i32, { R, R } }, +#endif + +#if TCG_TARGET_REG_BITS == 64 + { INDEX_op_ld8u_i64, { R, R } }, + { INDEX_op_ld8s_i64, { R, R } }, + { INDEX_op_ld16u_i64, { R, R } }, + { INDEX_op_ld16s_i64, { R, R } }, + { INDEX_op_ld32u_i64, { R, R } }, + { INDEX_op_ld32s_i64, { R, R } }, + { INDEX_op_ld_i64, { R, R } }, + + { INDEX_op_st8_i64, { R, R } }, + { INDEX_op_st16_i64, { R, R } }, + { INDEX_op_st32_i64, { R, R } }, + { INDEX_op_st_i64, { R, R } }, + + { INDEX_op_add_i64, { R, RI, RI } }, + { INDEX_op_sub_i64, { R, RI, RI } }, + { INDEX_op_mul_i64, { R, RI, RI } }, +#if TCG_TARGET_HAS_div_i64 + { INDEX_op_div_i64, { R, R, R } }, + { INDEX_op_divu_i64, { R, R, R } }, + { INDEX_op_rem_i64, { R, R, R } }, + { INDEX_op_remu_i64, { R, R, R } }, +#elif TCG_TARGET_HAS_div2_i64 + { INDEX_op_div2_i64, { R, R, "0", "1", R } }, + { INDEX_op_divu2_i64, { R, R, "0", "1", R } }, +#endif + { INDEX_op_and_i64, { R, RI, RI } }, +#if TCG_TARGET_HAS_andc_i64 + { INDEX_op_andc_i64, { R, RI, RI } }, +#endif +#if TCG_TARGET_HAS_eqv_i64 + { INDEX_op_eqv_i64, { R, RI, RI } }, +#endif +#if TCG_TARGET_HAS_nand_i64 + { INDEX_op_nand_i64, { R, RI, RI } }, +#endif +#if TCG_TARGET_HAS_nor_i64 + { INDEX_op_nor_i64, { R, RI, RI } }, +#endif + { INDEX_op_or_i64, { R, RI, RI } }, +#if TCG_TARGET_HAS_orc_i64 + { INDEX_op_orc_i64, { R, RI, RI } }, +#endif + { INDEX_op_xor_i64, { R, RI, RI } }, + { INDEX_op_shl_i64, { R, RI, RI } }, + { INDEX_op_shr_i64, { R, RI, RI } }, + { INDEX_op_sar_i64, { R, RI, RI } }, +#if TCG_TARGET_HAS_rot_i64 + { INDEX_op_rotl_i64, { R, RI, RI } }, + { INDEX_op_rotr_i64, { R, RI, RI } }, +#endif +#if TCG_TARGET_HAS_deposit_i64 + { INDEX_op_deposit_i64, { R, "0", R } }, +#endif + { INDEX_op_brcond_i64, { R, RI } }, + +#if TCG_TARGET_HAS_ext8s_i64 + { INDEX_op_ext8s_i64, { R, R } }, +#endif +#if TCG_TARGET_HAS_ext16s_i64 + { INDEX_op_ext16s_i64, { R, R } }, +#endif +#if TCG_TARGET_HAS_ext32s_i64 + { INDEX_op_ext32s_i64, { R, R } }, +#endif +#if TCG_TARGET_HAS_ext8u_i64 + { INDEX_op_ext8u_i64, { R, R } }, +#endif +#if TCG_TARGET_HAS_ext16u_i64 + { INDEX_op_ext16u_i64, { R, R } }, +#endif +#if TCG_TARGET_HAS_ext32u_i64 + { INDEX_op_ext32u_i64, { R, R } }, +#endif + { INDEX_op_ext_i32_i64, { R, R } }, + { INDEX_op_extu_i32_i64, { R, R } }, +#if TCG_TARGET_HAS_bswap16_i64 + { INDEX_op_bswap16_i64, { R, R } }, +#endif +#if TCG_TARGET_HAS_bswap32_i64 + { INDEX_op_bswap32_i64, { R, R } }, +#endif +#if TCG_TARGET_HAS_bswap64_i64 + { INDEX_op_bswap64_i64, { R, R } }, +#endif +#if TCG_TARGET_HAS_not_i64 + { INDEX_op_not_i64, { R, R } }, +#endif +#if TCG_TARGET_HAS_neg_i64 + { INDEX_op_neg_i64, { R, R } }, +#endif +#endif /* TCG_TARGET_REG_BITS == 64 */ + + { INDEX_op_qemu_ld_i32, { R, L } }, + { INDEX_op_qemu_ld_i64, { R64, L } }, + + { INDEX_op_qemu_st_i32, { R, S } }, + { INDEX_op_qemu_st_i64, { R64, S } }, + +#if TCG_TARGET_HAS_ext8s_i32 + { INDEX_op_ext8s_i32, { R, R } }, +#endif +#if TCG_TARGET_HAS_ext16s_i32 + { INDEX_op_ext16s_i32, { R, R } }, +#endif +#if TCG_TARGET_HAS_ext8u_i32 + { INDEX_op_ext8u_i32, { R, R } }, +#endif +#if TCG_TARGET_HAS_ext16u_i32 + { INDEX_op_ext16u_i32, { R, R } }, +#endif + +#if TCG_TARGET_HAS_bswap16_i32 + { INDEX_op_bswap16_i32, { R, R } }, +#endif +#if TCG_TARGET_HAS_bswap32_i32 + { INDEX_op_bswap32_i32, { R, R } }, +#endif + + { -1 }, +}; + +static const int tcg_target_reg_alloc_order[] = { + TCG_REG_R0, + TCG_REG_R1, + TCG_REG_R2, + TCG_REG_R3, +#if 0 /* used for TCG_REG_CALL_STACK */ + TCG_REG_R4, +#endif + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, +#if TCG_TARGET_NB_REGS >= 16 + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_R15, +#endif +}; + +#if MAX_OPC_PARAM_IARGS != 5 +# error Fix needed, number of supported input arguments changed! +#endif + +static const int tcg_target_call_iarg_regs[] = { + TCG_REG_R0, + TCG_REG_R1, + TCG_REG_R2, + TCG_REG_R3, +#if 0 /* used for TCG_REG_CALL_STACK */ + TCG_REG_R4, +#endif + TCG_REG_R5, +#if TCG_TARGET_REG_BITS == 32 + /* 32 bit hosts need 2 * MAX_OPC_PARAM_IARGS registers. */ + TCG_REG_R6, + TCG_REG_R7, +#if TCG_TARGET_NB_REGS >= 16 + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, +#else +# error Too few input registers available +#endif +#endif +}; + +static const int tcg_target_call_oarg_regs[] = { + TCG_REG_R0, +#if TCG_TARGET_REG_BITS == 32 + TCG_REG_R1 +#endif +}; + +#ifdef CONFIG_DEBUG_TCG +static const char *const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "r00", + "r01", + "r02", + "r03", + "r04", + "r05", + "r06", + "r07", +#if TCG_TARGET_NB_REGS >= 16 + "r08", + "r09", + "r10", + "r11", + "r12", + "r13", + "r14", + "r15", +#if TCG_TARGET_NB_REGS >= 32 + "r16", + "r17", + "r18", + "r19", + "r20", + "r21", + "r22", + "r23", + "r24", + "r25", + "r26", + "r27", + "r28", + "r29", + "r30", + "r31" +#endif +#endif +}; +#endif + +static void patch_reloc(tcg_insn_unit *code_ptr, int type, + intptr_t value, intptr_t addend) +{ + /* tcg_out_reloc always uses the same type, addend. */ + tcg_debug_assert(type == sizeof(tcg_target_long)); + tcg_debug_assert(addend == 0); + tcg_debug_assert(value != 0); + if (TCG_TARGET_REG_BITS == 32) { + tcg_patch32(code_ptr, value); + } else { + tcg_patch64(code_ptr, value); + } +} + +/* Parse target specific constraints. */ +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +{ + const char *ct_str = *pct_str; + switch (ct_str[0]) { + case 'r': + case 'L': /* qemu_ld constraint */ + case 'S': /* qemu_st constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, BIT(TCG_TARGET_NB_REGS) - 1); + break; + default: + return -1; + } + ct_str++; + *pct_str = ct_str; + return 0; +} + +#if defined(CONFIG_DEBUG_TCG_INTERPRETER) +/* Show current bytecode. Used by tcg interpreter. */ +void tci_disas(uint8_t opc) +{ + const TCGOpDef *def = &tcg_op_defs[opc]; + fprintf(stderr, "TCG %s %u, %u, %u\n", + def->name, def->nb_oargs, def->nb_iargs, def->nb_cargs); +} +#endif + +/* Write value (native size). */ +static void tcg_out_i(TCGContext *s, tcg_target_ulong v) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_out32(s, v); + } else { + tcg_out64(s, v); + } +} + +/* Write opcode. */ +static void tcg_out_op_t(TCGContext *s, TCGOpcode op) +{ + tcg_out8(s, op); + tcg_out8(s, 0); +} + +/* Write register. */ +static void tcg_out_r(TCGContext *s, TCGArg t0) +{ + tcg_debug_assert(t0 < TCG_TARGET_NB_REGS); + tcg_out8(s, t0); +} + +/* Write register or constant (native size). */ +static void tcg_out_ri(TCGContext *s, int const_arg, TCGArg arg) +{ + if (const_arg) { + tcg_debug_assert(const_arg == 1); + tcg_out8(s, TCG_CONST); + tcg_out_i(s, arg); + } else { + tcg_out_r(s, arg); + } +} + +/* Write register or constant (32 bit). */ +static void tcg_out_ri32(TCGContext *s, int const_arg, TCGArg arg) +{ + if (const_arg) { + tcg_debug_assert(const_arg == 1); + tcg_out8(s, TCG_CONST); + tcg_out32(s, arg); + } else { + tcg_out_r(s, arg); + } +} + +#if TCG_TARGET_REG_BITS == 64 +/* Write register or constant (64 bit). */ +static void tcg_out_ri64(TCGContext *s, int const_arg, TCGArg arg) +{ + if (const_arg) { + tcg_debug_assert(const_arg == 1); + tcg_out8(s, TCG_CONST); + tcg_out64(s, arg); + } else { + tcg_out_r(s, arg); + } +} +#endif + +/* Write label. */ +static void tci_out_label(TCGContext *s, TCGLabel *label) +{ + if (label->has_value) { + tcg_out_i(s, label->u.value); + tcg_debug_assert(label->u.value); + } else { + tcg_out_reloc(s, s->code_ptr, sizeof(tcg_target_ulong), label, 0); + s->code_ptr += sizeof(tcg_target_ulong); + } +} + +static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, + intptr_t arg2) +{ + uint8_t *old_code_ptr = s->code_ptr; + if (type == TCG_TYPE_I32) { + tcg_out_op_t(s, INDEX_op_ld_i32); + tcg_out_r(s, ret); + tcg_out_r(s, arg1); + tcg_out32(s, arg2); + } else { + tcg_debug_assert(type == TCG_TYPE_I64); +#if TCG_TARGET_REG_BITS == 64 + tcg_out_op_t(s, INDEX_op_ld_i64); + tcg_out_r(s, ret); + tcg_out_r(s, arg1); + tcg_debug_assert(arg2 == (int32_t)arg2); + tcg_out32(s, arg2); +#else + TODO(); +#endif + } + old_code_ptr[1] = s->code_ptr - old_code_ptr; +} + +static void tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) +{ + uint8_t *old_code_ptr = s->code_ptr; + tcg_debug_assert(ret != arg); +#if TCG_TARGET_REG_BITS == 32 + tcg_out_op_t(s, INDEX_op_mov_i32); +#else + tcg_out_op_t(s, INDEX_op_mov_i64); +#endif + tcg_out_r(s, ret); + tcg_out_r(s, arg); + old_code_ptr[1] = s->code_ptr - old_code_ptr; +} + +static void tcg_out_movi(TCGContext *s, TCGType type, + TCGReg t0, tcg_target_long arg) +{ + uint8_t *old_code_ptr = s->code_ptr; + uint32_t arg32 = arg; + if (type == TCG_TYPE_I32 || arg == arg32) { + tcg_out_op_t(s, INDEX_op_movi_i32); + tcg_out_r(s, t0); + tcg_out32(s, arg32); + } else { + tcg_debug_assert(type == TCG_TYPE_I64); +#if TCG_TARGET_REG_BITS == 64 + tcg_out_op_t(s, INDEX_op_movi_i64); + tcg_out_r(s, t0); + tcg_out64(s, arg); +#else + TODO(); +#endif + } + old_code_ptr[1] = s->code_ptr - old_code_ptr; +} + +static inline void tcg_out_call(TCGContext *s, tcg_insn_unit *arg) +{ + uint8_t *old_code_ptr = s->code_ptr; + tcg_out_op_t(s, INDEX_op_call); + tcg_out_ri(s, 1, (uintptr_t)arg); + old_code_ptr[1] = s->code_ptr - old_code_ptr; +} + +static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, + const int *const_args) +{ + uint8_t *old_code_ptr = s->code_ptr; + + tcg_out_op_t(s, opc); + + switch (opc) { + case INDEX_op_exit_tb: + tcg_out64(s, args[0]); + break; + case INDEX_op_goto_tb: + if (s->tb_jmp_offset) { + /* Direct jump method. */ + tcg_debug_assert(args[0] < ARRAY_SIZE(s->tb_jmp_offset)); + s->tb_jmp_offset[args[0]] = tcg_current_code_size(s); + tcg_out32(s, 0); + } else { + /* Indirect jump method. */ + TODO(); + } + tcg_debug_assert(args[0] < ARRAY_SIZE(s->tb_next_offset)); + s->tb_next_offset[args[0]] = tcg_current_code_size(s); + break; + case INDEX_op_br: + tci_out_label(s, arg_label(args[0])); + break; + case INDEX_op_setcond_i32: + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + tcg_out_ri32(s, const_args[2], args[2]); + tcg_out8(s, args[3]); /* condition */ + break; +#if TCG_TARGET_REG_BITS == 32 + case INDEX_op_setcond2_i32: + /* setcond2_i32 cond, t0, t1_low, t1_high, t2_low, t2_high */ + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + tcg_out_r(s, args[2]); + tcg_out_ri32(s, const_args[3], args[3]); + tcg_out_ri32(s, const_args[4], args[4]); + tcg_out8(s, args[5]); /* condition */ + break; +#elif TCG_TARGET_REG_BITS == 64 + case INDEX_op_setcond_i64: + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + tcg_out_ri64(s, const_args[2], args[2]); + tcg_out8(s, args[3]); /* condition */ + break; +#endif + case INDEX_op_ld8u_i32: + case INDEX_op_ld8s_i32: + case INDEX_op_ld16u_i32: + case INDEX_op_ld16s_i32: + case INDEX_op_ld_i32: + case INDEX_op_st8_i32: + case INDEX_op_st16_i32: + case INDEX_op_st_i32: + case INDEX_op_ld8u_i64: + case INDEX_op_ld8s_i64: + case INDEX_op_ld16u_i64: + case INDEX_op_ld16s_i64: + case INDEX_op_ld32u_i64: + case INDEX_op_ld32s_i64: + case INDEX_op_ld_i64: + case INDEX_op_st8_i64: + case INDEX_op_st16_i64: + case INDEX_op_st32_i64: + case INDEX_op_st_i64: + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + tcg_debug_assert(args[2] == (int32_t)args[2]); + tcg_out32(s, args[2]); + break; + case INDEX_op_add_i32: + case INDEX_op_sub_i32: + case INDEX_op_mul_i32: + case INDEX_op_and_i32: + case INDEX_op_andc_i32: /* Optional (TCG_TARGET_HAS_andc_i32). */ + case INDEX_op_eqv_i32: /* Optional (TCG_TARGET_HAS_eqv_i32). */ + case INDEX_op_nand_i32: /* Optional (TCG_TARGET_HAS_nand_i32). */ + case INDEX_op_nor_i32: /* Optional (TCG_TARGET_HAS_nor_i32). */ + case INDEX_op_or_i32: + case INDEX_op_orc_i32: /* Optional (TCG_TARGET_HAS_orc_i32). */ + case INDEX_op_xor_i32: + case INDEX_op_shl_i32: + case INDEX_op_shr_i32: + case INDEX_op_sar_i32: + case INDEX_op_rotl_i32: /* Optional (TCG_TARGET_HAS_rot_i32). */ + case INDEX_op_rotr_i32: /* Optional (TCG_TARGET_HAS_rot_i32). */ + tcg_out_r(s, args[0]); + tcg_out_ri32(s, const_args[1], args[1]); + tcg_out_ri32(s, const_args[2], args[2]); + break; + case INDEX_op_deposit_i32: /* Optional (TCG_TARGET_HAS_deposit_i32). */ + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + tcg_out_r(s, args[2]); + tcg_debug_assert(args[3] <= UINT8_MAX); + tcg_out8(s, args[3]); + tcg_debug_assert(args[4] <= UINT8_MAX); + tcg_out8(s, args[4]); + break; + +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_add_i64: + case INDEX_op_sub_i64: + case INDEX_op_mul_i64: + case INDEX_op_and_i64: + case INDEX_op_andc_i64: /* Optional (TCG_TARGET_HAS_andc_i64). */ + case INDEX_op_eqv_i64: /* Optional (TCG_TARGET_HAS_eqv_i64). */ + case INDEX_op_nand_i64: /* Optional (TCG_TARGET_HAS_nand_i64). */ + case INDEX_op_nor_i64: /* Optional (TCG_TARGET_HAS_nor_i64). */ + case INDEX_op_or_i64: + case INDEX_op_orc_i64: /* Optional (TCG_TARGET_HAS_orc_i64). */ + case INDEX_op_xor_i64: + case INDEX_op_shl_i64: + case INDEX_op_shr_i64: + case INDEX_op_sar_i64: + case INDEX_op_rotl_i64: /* Optional (TCG_TARGET_HAS_rot_i64). */ + case INDEX_op_rotr_i64: /* Optional (TCG_TARGET_HAS_rot_i64). */ + tcg_out_r(s, args[0]); + tcg_out_ri64(s, const_args[1], args[1]); + tcg_out_ri64(s, const_args[2], args[2]); + break; + case INDEX_op_deposit_i64: /* Optional (TCG_TARGET_HAS_deposit_i64). */ + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + tcg_out_r(s, args[2]); + tcg_debug_assert(args[3] <= UINT8_MAX); + tcg_out8(s, args[3]); + tcg_debug_assert(args[4] <= UINT8_MAX); + tcg_out8(s, args[4]); + break; + case INDEX_op_div_i64: /* Optional (TCG_TARGET_HAS_div_i64). */ + case INDEX_op_divu_i64: /* Optional (TCG_TARGET_HAS_div_i64). */ + case INDEX_op_rem_i64: /* Optional (TCG_TARGET_HAS_div_i64). */ + case INDEX_op_remu_i64: /* Optional (TCG_TARGET_HAS_div_i64). */ + TODO(); + break; + case INDEX_op_div2_i64: /* Optional (TCG_TARGET_HAS_div2_i64). */ + case INDEX_op_divu2_i64: /* Optional (TCG_TARGET_HAS_div2_i64). */ + TODO(); + break; + case INDEX_op_brcond_i64: + tcg_out_r(s, args[0]); + tcg_out_ri64(s, const_args[1], args[1]); + tcg_out8(s, args[2]); /* condition */ + tci_out_label(s, arg_label(args[3])); + break; + case INDEX_op_bswap16_i64: /* Optional (TCG_TARGET_HAS_bswap16_i64). */ + case INDEX_op_bswap32_i64: /* Optional (TCG_TARGET_HAS_bswap32_i64). */ + case INDEX_op_bswap64_i64: /* Optional (TCG_TARGET_HAS_bswap64_i64). */ + case INDEX_op_not_i64: /* Optional (TCG_TARGET_HAS_not_i64). */ + case INDEX_op_neg_i64: /* Optional (TCG_TARGET_HAS_neg_i64). */ + case INDEX_op_ext8s_i64: /* Optional (TCG_TARGET_HAS_ext8s_i64). */ + case INDEX_op_ext8u_i64: /* Optional (TCG_TARGET_HAS_ext8u_i64). */ + case INDEX_op_ext16s_i64: /* Optional (TCG_TARGET_HAS_ext16s_i64). */ + case INDEX_op_ext16u_i64: /* Optional (TCG_TARGET_HAS_ext16u_i64). */ + case INDEX_op_ext32s_i64: /* Optional (TCG_TARGET_HAS_ext32s_i64). */ + case INDEX_op_ext32u_i64: /* Optional (TCG_TARGET_HAS_ext32u_i64). */ + case INDEX_op_ext_i32_i64: + case INDEX_op_extu_i32_i64: +#endif /* TCG_TARGET_REG_BITS == 64 */ + case INDEX_op_neg_i32: /* Optional (TCG_TARGET_HAS_neg_i32). */ + case INDEX_op_not_i32: /* Optional (TCG_TARGET_HAS_not_i32). */ + case INDEX_op_ext8s_i32: /* Optional (TCG_TARGET_HAS_ext8s_i32). */ + case INDEX_op_ext16s_i32: /* Optional (TCG_TARGET_HAS_ext16s_i32). */ + case INDEX_op_ext8u_i32: /* Optional (TCG_TARGET_HAS_ext8u_i32). */ + case INDEX_op_ext16u_i32: /* Optional (TCG_TARGET_HAS_ext16u_i32). */ + case INDEX_op_bswap16_i32: /* Optional (TCG_TARGET_HAS_bswap16_i32). */ + case INDEX_op_bswap32_i32: /* Optional (TCG_TARGET_HAS_bswap32_i32). */ + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + break; + case INDEX_op_div_i32: /* Optional (TCG_TARGET_HAS_div_i32). */ + case INDEX_op_divu_i32: /* Optional (TCG_TARGET_HAS_div_i32). */ + case INDEX_op_rem_i32: /* Optional (TCG_TARGET_HAS_div_i32). */ + case INDEX_op_remu_i32: /* Optional (TCG_TARGET_HAS_div_i32). */ + tcg_out_r(s, args[0]); + tcg_out_ri32(s, const_args[1], args[1]); + tcg_out_ri32(s, const_args[2], args[2]); + break; + case INDEX_op_div2_i32: /* Optional (TCG_TARGET_HAS_div2_i32). */ + case INDEX_op_divu2_i32: /* Optional (TCG_TARGET_HAS_div2_i32). */ + TODO(); + break; +#if TCG_TARGET_REG_BITS == 32 + case INDEX_op_add2_i32: + case INDEX_op_sub2_i32: + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + tcg_out_r(s, args[2]); + tcg_out_r(s, args[3]); + tcg_out_r(s, args[4]); + tcg_out_r(s, args[5]); + break; + case INDEX_op_brcond2_i32: + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + tcg_out_ri32(s, const_args[2], args[2]); + tcg_out_ri32(s, const_args[3], args[3]); + tcg_out8(s, args[4]); /* condition */ + tci_out_label(s, arg_label(args[5])); + break; + case INDEX_op_mulu2_i32: + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + tcg_out_r(s, args[2]); + tcg_out_r(s, args[3]); + break; +#endif + case INDEX_op_brcond_i32: + tcg_out_r(s, args[0]); + tcg_out_ri32(s, const_args[1], args[1]); + tcg_out8(s, args[2]); /* condition */ + tci_out_label(s, arg_label(args[3])); + break; + case INDEX_op_qemu_ld_i32: + tcg_out_r(s, *args++); + tcg_out_r(s, *args++); + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + tcg_out_r(s, *args++); + } + tcg_out_i(s, *args++); + break; + case INDEX_op_qemu_ld_i64: + tcg_out_r(s, *args++); + if (TCG_TARGET_REG_BITS == 32) { + tcg_out_r(s, *args++); + } + tcg_out_r(s, *args++); + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + tcg_out_r(s, *args++); + } + tcg_out_i(s, *args++); + break; + case INDEX_op_qemu_st_i32: + tcg_out_r(s, *args++); + tcg_out_r(s, *args++); + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + tcg_out_r(s, *args++); + } + tcg_out_i(s, *args++); + break; + case INDEX_op_qemu_st_i64: + tcg_out_r(s, *args++); + if (TCG_TARGET_REG_BITS == 32) { + tcg_out_r(s, *args++); + } + tcg_out_r(s, *args++); + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + tcg_out_r(s, *args++); + } + tcg_out_i(s, *args++); + break; + case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ + case INDEX_op_mov_i64: + case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ + case INDEX_op_movi_i64: + case INDEX_op_call: /* Always emitted via tcg_out_call. */ + default: + tcg_abort(); + } + old_code_ptr[1] = s->code_ptr - old_code_ptr; +} + +static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, TCGReg arg1, + intptr_t arg2) +{ + uint8_t *old_code_ptr = s->code_ptr; + if (type == TCG_TYPE_I32) { + tcg_out_op_t(s, INDEX_op_st_i32); + tcg_out_r(s, arg); + tcg_out_r(s, arg1); + tcg_out32(s, arg2); + } else { + tcg_debug_assert(type == TCG_TYPE_I64); +#if TCG_TARGET_REG_BITS == 64 + tcg_out_op_t(s, INDEX_op_st_i64); + tcg_out_r(s, arg); + tcg_out_r(s, arg1); + tcg_out32(s, arg2); +#else + TODO(); +#endif + } + old_code_ptr[1] = s->code_ptr - old_code_ptr; +} + +/* Test if a constant matches the constraint. */ +static int tcg_target_const_match(tcg_target_long val, TCGType type, + const TCGArgConstraint *arg_ct) +{ + /* No need to return 0 or 1, 0 or != 0 is good enough. */ + return arg_ct->ct & TCG_CT_CONST; +} + +static void tcg_target_init(TCGContext *s) +{ +#if defined(CONFIG_DEBUG_TCG_INTERPRETER) + const char *envval = getenv("DEBUG_TCG"); + if (envval) { + qemu_set_log(strtol(envval, NULL, 0)); + } +#endif + + /* The current code uses uint8_t for tcg operations. */ + tcg_debug_assert(tcg_op_defs_max <= UINT8_MAX); + + /* Registers available for 32 bit operations. */ + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, + BIT(TCG_TARGET_NB_REGS) - 1); + /* Registers available for 64 bit operations. */ + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, + BIT(TCG_TARGET_NB_REGS) - 1); + /* TODO: Which registers should be set here? */ + tcg_regset_set32(tcg_target_call_clobber_regs, 0, + BIT(TCG_TARGET_NB_REGS) - 1); + + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); + tcg_add_target_add_op_defs(tcg_target_op_defs); + + /* We use negative offsets from "sp" so that we can distinguish + stores that might pretend to be call arguments. */ + tcg_set_frame(s, TCG_REG_CALL_STACK, + -CPU_TEMP_BUF_NLONGS * sizeof(long), + CPU_TEMP_BUF_NLONGS * sizeof(long)); +} + +/* Generate global QEMU prologue and epilogue code. */ +static inline void tcg_target_qemu_prologue(TCGContext *s) +{ +} diff --git a/qemu/tci.c b/qemu/tci.c index 84449489d..82705fe77 100644 --- a/qemu/tci.c +++ b/qemu/tci.c @@ -1,7 +1,7 @@ /* * Tiny Code Interpreter for QEMU * - * Copyright (c) 2009, 2011 Stefan Weil + * Copyright (c) 2009, 2011, 2016 Stefan Weil * * 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 @@ -17,11 +17,14 @@ * along with this program. If not, see . */ -#include "config.h" +#include "qemu/osdep.h" -/* Defining NDEBUG disables assertions (which makes the code faster). */ -#if !defined(CONFIG_DEBUG_TCG) && !defined(NDEBUG) -# define NDEBUG +/* Enable TCI assertions only when debugging TCG (and without NDEBUG defined). + * Without assertions, the interpreter runs much faster. */ +#if defined(CONFIG_DEBUG_TCG) +# define tci_assert(cond) assert(cond) +#else +# define tci_assert(cond) ((void)0) #endif #include "qemu-common.h" @@ -52,17 +55,11 @@ typedef uint64_t (*helper_function)(tcg_target_ulong, tcg_target_ulong, tcg_target_ulong); #endif -/* Targets which don't use GETPC also don't need tci_tb_ptr - which makes them a little faster. */ -#if defined(GETPC) -uintptr_t tci_tb_ptr; -#endif - static tcg_target_ulong tci_reg[TCG_TARGET_NB_REGS]; static tcg_target_ulong tci_read_reg(TCGReg index) { - assert(index < ARRAY_SIZE(tci_reg)); + tci_assert(index < ARRAY_SIZE(tci_reg)); return tci_reg[index]; } @@ -111,9 +108,9 @@ static uint64_t tci_read_reg64(TCGReg index) static void tci_write_reg(TCGReg index, tcg_target_ulong value) { - assert(index < ARRAY_SIZE(tci_reg)); - assert(index != TCG_AREG0); - assert(index != TCG_REG_CALL_STACK); + tci_assert(index < ARRAY_SIZE(tci_reg)); + tci_assert(index != TCG_AREG0); + tci_assert(index != TCG_REG_CALL_STACK); tci_reg[index] = value; } @@ -331,7 +328,7 @@ static uint64_t tci_read_ri64(uint8_t **tb_ptr) static tcg_target_ulong tci_read_label(uint8_t **tb_ptr) { tcg_target_ulong label = tci_read_i(tb_ptr); - assert(label != 0); + tci_assert(label != 0); return label; } @@ -474,11 +471,11 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) tci_reg[TCG_AREG0] = (tcg_target_ulong)env; tci_reg[TCG_REG_CALL_STACK] = sp_value; - assert(tb_ptr); + tci_assert(tb_ptr); for (;;) { TCGOpcode opc = tb_ptr[0]; -#if !defined(NDEBUG) +#if defined(CONFIG_DEBUG_TCG) && !defined(NDEBUG) uint8_t op_size = tb_ptr[1]; uint8_t *old_code_ptr = tb_ptr; #endif @@ -531,7 +528,7 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) break; case INDEX_op_br: label = tci_read_label(&tb_ptr); - assert(tb_ptr == old_code_ptr + op_size); + tci_assert(tb_ptr == old_code_ptr + op_size); tb_ptr = (uint8_t *)label; continue; case INDEX_op_setcond_i32: @@ -606,7 +603,7 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) t0 = tci_read_r32(&tb_ptr); t1 = tci_read_r(&tb_ptr); t2 = tci_read_s32(&tb_ptr); - assert(t1 != sp_value || (int32_t)t2 < 0); + tci_assert(t1 != sp_value || (int32_t)t2 < 0); *(uint32_t *)(t1 + t2) = t0; break; @@ -731,7 +728,7 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) condition = *tb_ptr++; label = tci_read_label(&tb_ptr); if (tci_compare32(t0, t1, condition)) { - assert(tb_ptr == old_code_ptr + op_size); + tci_assert(tb_ptr == old_code_ptr + op_size); tb_ptr = (uint8_t *)label; continue; } @@ -757,7 +754,7 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) condition = *tb_ptr++; label = tci_read_label(&tb_ptr); if (tci_compare64(tmp64, v64, condition)) { - assert(tb_ptr == old_code_ptr + op_size); + tci_assert(tb_ptr == old_code_ptr + op_size); tb_ptr = (uint8_t *)label; continue; } @@ -891,7 +888,7 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) t0 = tci_read_r64(&tb_ptr); t1 = tci_read_r(&tb_ptr); t2 = tci_read_s32(&tb_ptr); - assert(t1 != sp_value || (int32_t)t2 < 0); + tci_assert(t1 != sp_value || (int32_t)t2 < 0); *(uint64_t *)(t1 + t2) = t0; break; @@ -998,7 +995,7 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) condition = *tb_ptr++; label = tci_read_label(&tb_ptr); if (tci_compare64(t0, t1, condition)) { - assert(tb_ptr == old_code_ptr + op_size); + tci_assert(tb_ptr == old_code_ptr + op_size); tb_ptr = (uint8_t *)label; continue; } @@ -1033,18 +1030,20 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) #endif #if TCG_TARGET_HAS_ext32s_i64 case INDEX_op_ext32s_i64: +#endif + case INDEX_op_ext_i32_i64: t0 = *tb_ptr++; t1 = tci_read_r32s(&tb_ptr); tci_write_reg64(t0, t1); break; -#endif #if TCG_TARGET_HAS_ext32u_i64 case INDEX_op_ext32u_i64: +#endif + case INDEX_op_extu_i32_i64: t0 = *tb_ptr++; t1 = tci_read_r32(&tb_ptr); tci_write_reg64(t0, t1); break; -#endif #if TCG_TARGET_HAS_bswap16_i64 case INDEX_op_bswap16_i64: TODO(); @@ -1085,22 +1084,13 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) /* QEMU specific operations. */ -#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS - case INDEX_op_debug_insn_start: - TODO(); - break; -#else - case INDEX_op_debug_insn_start: - TODO(); - break; -#endif case INDEX_op_exit_tb: next_tb = *(uint64_t *)tb_ptr; goto exit; break; case INDEX_op_goto_tb: t0 = tci_read_i32(&tb_ptr); - assert(tb_ptr == old_code_ptr + op_size); + tci_assert(tb_ptr == old_code_ptr + op_size); tb_ptr += (int32_t)t0; continue; case INDEX_op_qemu_ld_i32: @@ -1247,7 +1237,7 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) TODO(); break; } - assert(tb_ptr == old_code_ptr + op_size); + tci_assert(tb_ptr == old_code_ptr + op_size); } exit: return next_tb; diff --git a/qemu/tests/.gitignore b/qemu/tests/.gitignore index ccc92e476..9eed22988 100644 --- a/qemu/tests/.gitignore +++ b/qemu/tests/.gitignore @@ -8,14 +8,38 @@ check-qom-interface check-qom-proplist rcutorture test-aio +test-base64 test-bitops +test-blockjob-txn test-coroutine +test-crypto-afsplit +test-crypto-block test-crypto-cipher test-crypto-hash +test-crypto-ivgen +test-crypto-pbkdf +test-crypto-secret +test-crypto-tlscredsx509 +test-crypto-tlscredsx509-work/ +test-crypto-tlscredsx509-certs/ +test-crypto-tlssession +test-crypto-tlssession-work/ +test-crypto-tlssession-client/ +test-crypto-tlssession-server/ +test-crypto-xts test-cutils test-hbitmap test-int128 test-iov +test-io-channel-buffer +test-io-channel-command +test-io-channel-command.fifo +test-io-channel-file +test-io-channel-file.txt +test-io-channel-socket +test-io-channel-tls +test-io-task +test-logging test-mul64 test-opts-visitor test-qapi-event.[ch] @@ -23,11 +47,13 @@ test-qapi-types.[ch] test-qapi-visit.[ch] test-qdev-global-props test-qemu-opts +test-qga test-qmp-commands test-qmp-commands.h test-qmp-event test-qmp-input-strict test-qmp-input-visitor +test-qmp-introspect.[ch] test-qmp-marshal.c test-qmp-output-visitor test-rcu-list @@ -36,10 +62,14 @@ test-string-input-visitor test-string-output-visitor test-thread-pool test-throttle +test-timed-average test-visitor-serialization test-vmstate test-write-threshold test-x86-cpuid test-xbzrle +test-netfilter +test-filter-mirror +test-filter-redirector *-test qapi-schema/*.test.* diff --git a/qemu/tests/Makefile b/qemu/tests/Makefile index 749458224..9194f1850 100644 --- a/qemu/tests/Makefile +++ b/qemu/tests/Makefile @@ -1,5 +1,7 @@ export SRC_PATH +qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py + # Get the list of all supported sysemu targets SYSEMU_TARGET_LIST := $(subst -softmmu.mak,,$(notdir \ $(wildcard $(SRC_PATH)/default-configs/*-softmmu.mak))) @@ -45,6 +47,8 @@ check-unit-y += tests/test-thread-pool$(EXESUF) gcov-files-test-thread-pool-y = thread-pool.c gcov-files-test-hbitmap-y = util/hbitmap.c check-unit-y += tests/test-hbitmap$(EXESUF) +gcov-files-test-hbitmap-y = blockjob.c +check-unit-y += tests/test-blockjob-txn$(EXESUF) check-unit-y += tests/test-x86-cpuid$(EXESUF) # all code tested by test-x86-cpuid is inside topology.h gcov-files-test-x86-cpuid-y = @@ -76,12 +80,36 @@ check-unit-y += tests/test-write-threshold$(EXESUF) gcov-files-test-write-threshold-y = block/write-threshold.c check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF) check-unit-y += tests/test-crypto-cipher$(EXESUF) +check-unit-y += tests/test-crypto-secret$(EXESUF) +check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF) +check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(EXESUF) +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-io-task$(EXESUF) +check-unit-y += tests/test-io-channel-socket$(EXESUF) +check-unit-y += tests/test-io-channel-file$(EXESUF) +check-unit-$(CONFIG_GNUTLS) += tests/test-io-channel-tls$(EXESUF) +check-unit-y += tests/test-io-channel-command$(EXESUF) +check-unit-y += tests/test-io-channel-buffer$(EXESUF) +check-unit-y += tests/test-base64$(EXESUF) +check-unit-$(if $(CONFIG_NETTLE_KDF),y,$(CONFIG_GCRYPT_KDF)) += tests/test-crypto-pbkdf$(EXESUF) +check-unit-y += tests/test-crypto-ivgen$(EXESUF) +check-unit-y += tests/test-crypto-afsplit$(EXESUF) +check-unit-y += tests/test-crypto-xts$(EXESUF) +check-unit-y += tests/test-crypto-block$(EXESUF) +gcov-files-test-logging-y = tests/test-logging.c +check-unit-y += tests/test-logging$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh # All QTests for now are POSIX-only, but the dependencies are # really in libqtest, not in the testcases themselves. +check-qtest-generic-y = tests/device-introspect-test$(EXESUF) +gcov-files-generic-y = qdev-monitor.c qmp.c + gcov-files-ipack-y += hw/ipack/ipack.c check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF) gcov-files-ipack-y += hw/char/ipoctal232.c @@ -142,6 +170,8 @@ gcov-files-pci-y += hw/display/virtio-gpu-pci.c gcov-files-pci-$(CONFIG_VIRTIO_VGA) += hw/display/virtio-vga.c check-qtest-pci-y += tests/intel-hda-test$(EXESUF) gcov-files-pci-y += hw/audio/intel-hda.c hw/audio/hda-codec.c +check-qtest-pci-$(CONFIG_EVENTFD) += tests/ivshmem-test$(EXESUF) +gcov-files-pci-y += hw/misc/ivshmem.c check-qtest-i386-y = tests/endianness-test$(EXESUF) check-qtest-i386-y += tests/fdc-test$(EXESUF) @@ -152,7 +182,10 @@ check-qtest-i386-y += tests/hd-geo-test$(EXESUF) gcov-files-i386-y += hw/block/hd-geometry.c check-qtest-i386-y += tests/boot-order-test$(EXESUF) check-qtest-i386-y += tests/bios-tables-test$(EXESUF) +check-qtest-i386-y += tests/pxe-test$(EXESUF) check-qtest-i386-y += tests/rtc-test$(EXESUF) +check-qtest-i386-y += tests/ipmi-kcs-test$(EXESUF) +check-qtest-i386-y += tests/ipmi-bt-test$(EXESUF) check-qtest-i386-y += tests/i440fx-test$(EXESUF) check-qtest-i386-y += tests/fw_cfg-test$(EXESUF) check-qtest-i386-y += tests/drive_del-test$(EXESUF) @@ -184,7 +217,13 @@ gcov-files-i386-y += hw/usb/hcd-xhci.c check-qtest-i386-y += tests/pc-cpu-test$(EXESUF) check-qtest-i386-y += tests/q35-test$(EXESUF) gcov-files-i386-y += hw/pci-host/q35.c -check-qtest-i386-$(CONFIG_LINUX) += tests/vhost-user-test$(EXESUF) +check-qtest-i386-$(CONFIG_VHOST_NET_TEST_i386) += tests/vhost-user-test$(EXESUF) +ifeq ($(CONFIG_VHOST_NET_TEST_i386),) +check-qtest-x86_64-$(CONFIG_VHOST_NET_TEST_x86_64) += tests/vhost-user-test$(EXESUF) +endif +check-qtest-i386-y += tests/test-netfilter$(EXESUF) +check-qtest-i386-y += tests/test-filter-mirror$(EXESUF) +check-qtest-i386-y += tests/test-filter-redirector$(EXESUF) check-qtest-x86_64-y = $(check-qtest-i386-y) gcov-files-i386-y += i386-softmmu/hw/timer/mc146818rtc.c gcov-files-x86_64-y = $(subst i386-softmmu/,x86_64-softmmu/,$(gcov-files-i386-y)) @@ -201,6 +240,7 @@ check-qtest-sparc64-y = tests/endianness-test$(EXESUF) gcov-files-sparc-y += hw/timer/m48t59.c gcov-files-sparc64-y += hw/timer/m48t59.c check-qtest-arm-y = tests/tmp105-test$(EXESUF) +check-qtest-arm-y = tests/ds1338-test$(EXESUF) gcov-files-arm-y += hw/misc/tmp105.c check-qtest-arm-y += tests/virtio-blk-test$(EXESUF) gcov-files-arm-y += arm-softmmu/hw/block/virtio-blk.c @@ -211,54 +251,135 @@ gcov-files-ppc64-y += ppc64-softmmu/hw/ppc/spapr_pci.c check-qtest-microblazeel-y = $(check-qtest-microblaze-y) check-qtest-xtensaeb-y = $(check-qtest-xtensa-y) -# qom-test works for all sysemu architectures: -$(foreach target,$(SYSEMU_TARGET_LIST), \ - $(if $(findstring tests/qom-test$(EXESUF), $(check-qtest-$(target)-y)),, \ - $(eval check-qtest-$(target)-y += tests/qom-test$(EXESUF)))) - -check-qapi-schema-y := $(addprefix tests/qapi-schema/, \ - comments.json empty.json enum-empty.json enum-missing-data.json \ - enum-wrong-data.json enum-int-member.json enum-dict-member.json \ - enum-clash-member.json enum-max-member.json enum-union-clash.json \ - enum-bad-name.json funny-char.json indented-expr.json \ - missing-type.json bad-ident.json ident-with-escape.json \ - escape-outside-string.json unknown-escape.json \ - escape-too-short.json escape-too-big.json unicode-str.json \ - double-type.json bad-base.json bad-type-bool.json bad-type-int.json \ - bad-type-dict.json double-data.json unknown-expr-key.json \ - redefined-type.json redefined-command.json redefined-builtin.json \ - redefined-event.json command-int.json bad-data.json event-max.json \ - type-bypass.json type-bypass-no-gen.json type-bypass-bad-gen.json \ - data-array-empty.json data-array-unknown.json data-int.json \ - data-unknown.json data-member-unknown.json data-member-array.json \ - data-member-array-bad.json returns-array-bad.json returns-int.json \ - returns-unknown.json returns-alternate.json returns-whitelist.json \ - missing-colon.json missing-comma-list.json missing-comma-object.json \ - nested-struct-data.json nested-struct-returns.json non-objects.json \ - qapi-schema-test.json quoted-structural-chars.json \ - trailing-comma-list.json trailing-comma-object.json \ - unclosed-list.json unclosed-object.json unclosed-string.json \ - duplicate-key.json union-invalid-base.json union-bad-branch.json \ - union-optional-branch.json union-unknown.json union-max.json \ - flat-union-optional-discriminator.json flat-union-no-base.json \ - flat-union-invalid-discriminator.json flat-union-inline.json \ - flat-union-invalid-branch-key.json flat-union-reverse-define.json \ - flat-union-string-discriminator.json union-base-no-discriminator.json \ - flat-union-bad-discriminator.json flat-union-bad-base.json \ - flat-union-base-star.json \ - flat-union-array-branch.json flat-union-int-branch.json \ - flat-union-base-union.json flat-union-branch-clash.json \ - alternate-nested.json alternate-unknown.json alternate-clash.json \ - alternate-good.json alternate-base.json alternate-array.json \ - alternate-conflict-string.json alternate-conflict-dict.json \ - include-simple.json include-relpath.json include-format-err.json \ - include-non-file.json include-no-file.json include-before-err.json \ - include-nested-err.json include-self-cycle.json include-cycle.json \ - include-repetition.json event-nest-struct.json event-case.json \ - struct-base-clash.json struct-base-clash-deep.json ) +check-qtest-generic-y += tests/qom-test$(EXESUF) + +qapi-schema += alternate-any.json +qapi-schema += alternate-array.json +qapi-schema += alternate-base.json +qapi-schema += alternate-clash.json +qapi-schema += alternate-conflict-dict.json +qapi-schema += alternate-conflict-string.json +qapi-schema += alternate-empty.json +qapi-schema += alternate-nested.json +qapi-schema += alternate-unknown.json +qapi-schema += args-alternate.json +qapi-schema += args-any.json +qapi-schema += args-array-empty.json +qapi-schema += args-array-unknown.json +qapi-schema += args-int.json +qapi-schema += args-invalid.json +qapi-schema += args-member-array-bad.json +qapi-schema += args-member-case.json +qapi-schema += args-member-unknown.json +qapi-schema += args-name-clash.json +qapi-schema += args-union.json +qapi-schema += args-unknown.json +qapi-schema += bad-base.json +qapi-schema += bad-data.json +qapi-schema += bad-ident.json +qapi-schema += bad-type-bool.json +qapi-schema += bad-type-dict.json +qapi-schema += bad-type-int.json +qapi-schema += base-cycle-direct.json +qapi-schema += base-cycle-indirect.json +qapi-schema += command-int.json +qapi-schema += comments.json +qapi-schema += double-data.json +qapi-schema += double-type.json +qapi-schema += duplicate-key.json +qapi-schema += empty.json +qapi-schema += enum-bad-name.json +qapi-schema += enum-bad-prefix.json +qapi-schema += enum-clash-member.json +qapi-schema += enum-dict-member.json +qapi-schema += enum-int-member.json +qapi-schema += enum-member-case.json +qapi-schema += enum-missing-data.json +qapi-schema += enum-wrong-data.json +qapi-schema += escape-outside-string.json +qapi-schema += escape-too-big.json +qapi-schema += escape-too-short.json +qapi-schema += event-case.json +qapi-schema += event-nest-struct.json +qapi-schema += flat-union-array-branch.json +qapi-schema += flat-union-bad-base.json +qapi-schema += flat-union-bad-discriminator.json +qapi-schema += flat-union-base-any.json +qapi-schema += flat-union-base-union.json +qapi-schema += flat-union-clash-member.json +qapi-schema += flat-union-empty.json +qapi-schema += flat-union-inline.json +qapi-schema += flat-union-int-branch.json +qapi-schema += flat-union-invalid-branch-key.json +qapi-schema += flat-union-invalid-discriminator.json +qapi-schema += flat-union-no-base.json +qapi-schema += flat-union-optional-discriminator.json +qapi-schema += flat-union-string-discriminator.json +qapi-schema += funny-char.json +qapi-schema += ident-with-escape.json +qapi-schema += include-before-err.json +qapi-schema += include-cycle.json +qapi-schema += include-format-err.json +qapi-schema += include-nested-err.json +qapi-schema += include-no-file.json +qapi-schema += include-non-file.json +qapi-schema += include-relpath.json +qapi-schema += include-repetition.json +qapi-schema += include-self-cycle.json +qapi-schema += include-simple.json +qapi-schema += indented-expr.json +qapi-schema += leading-comma-list.json +qapi-schema += leading-comma-object.json +qapi-schema += missing-colon.json +qapi-schema += missing-comma-list.json +qapi-schema += missing-comma-object.json +qapi-schema += missing-type.json +qapi-schema += nested-struct-data.json +qapi-schema += non-objects.json +qapi-schema += qapi-schema-test.json +qapi-schema += quoted-structural-chars.json +qapi-schema += redefined-builtin.json +qapi-schema += redefined-command.json +qapi-schema += redefined-event.json +qapi-schema += redefined-type.json +qapi-schema += reserved-command-q.json +qapi-schema += reserved-enum-q.json +qapi-schema += reserved-member-has.json +qapi-schema += reserved-member-q.json +qapi-schema += reserved-member-u.json +qapi-schema += reserved-member-underscore.json +qapi-schema += reserved-type-kind.json +qapi-schema += reserved-type-list.json +qapi-schema += returns-alternate.json +qapi-schema += returns-array-bad.json +qapi-schema += returns-dict.json +qapi-schema += returns-unknown.json +qapi-schema += returns-whitelist.json +qapi-schema += struct-base-clash-deep.json +qapi-schema += struct-base-clash.json +qapi-schema += struct-data-invalid.json +qapi-schema += struct-member-invalid.json +qapi-schema += trailing-comma-list.json +qapi-schema += trailing-comma-object.json +qapi-schema += type-bypass-bad-gen.json +qapi-schema += unclosed-list.json +qapi-schema += unclosed-object.json +qapi-schema += unclosed-string.json +qapi-schema += unicode-str.json +qapi-schema += union-base-no-discriminator.json +qapi-schema += union-branch-case.json +qapi-schema += union-clash-branches.json +qapi-schema += union-empty.json +qapi-schema += union-invalid-base.json +qapi-schema += union-optional-branch.json +qapi-schema += union-unknown.json +qapi-schema += unknown-escape.json +qapi-schema += unknown-expr-key.json +check-qapi-schema-y := $(addprefix tests/qapi-schema/, $(qapi-schema)) GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \ - tests/test-qmp-commands.h tests/test-qapi-event.h + tests/test-qmp-commands.h tests/test-qapi-event.h \ + tests/test-qmp-introspect.h test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \ tests/check-qlist.o tests/check-qfloat.o tests/check-qjson.o \ @@ -270,48 +391,59 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \ tests/test-opts-visitor.o tests/test-qmp-event.o \ tests/rcutorture.o tests/test-rcu-list.o -test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \ - tests/test-qapi-event.o - $(test-obj-y): QEMU_INCLUDES += -Itests QEMU_CFLAGS += -I$(SRC_PATH)/tests -qom-core-obj = qom/object.o qom/qom-qobject.o qom/container.o qom/object_interfaces.o - -tests/check-qint$(EXESUF): tests/check-qint.o libqemuutil.a -tests/check-qstring$(EXESUF): tests/check-qstring.o libqemuutil.a -tests/check-qdict$(EXESUF): tests/check-qdict.o libqemuutil.a -tests/check-qlist$(EXESUF): tests/check-qlist.o libqemuutil.a -tests/check-qfloat$(EXESUF): tests/check-qfloat.o libqemuutil.a -tests/check-qjson$(EXESUF): tests/check-qjson.o libqemuutil.a libqemustub.a -tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(qom-core-obj) libqemuutil.a libqemustub.a -tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(qom-core-obj) libqemuutil.a libqemustub.a -tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(block-obj-y) libqemuutil.a libqemustub.a -tests/test-aio$(EXESUF): tests/test-aio.o $(block-obj-y) libqemuutil.a libqemustub.a -tests/test-rfifolock$(EXESUF): tests/test-rfifolock.o libqemuutil.a libqemustub.a -tests/test-throttle$(EXESUF): tests/test-throttle.o $(block-obj-y) libqemuutil.a libqemustub.a -tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(block-obj-y) libqemuutil.a libqemustub.a -tests/test-iov$(EXESUF): tests/test-iov.o libqemuutil.a -tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o libqemuutil.a libqemustub.a + + +# Deps that are common to various different sets of tests below +test-util-obj-y = libqemuutil.a libqemustub.a +test-qom-obj-y = $(qom-obj-y) $(test-util-obj-y) +test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \ + tests/test-qapi-event.o tests/test-qmp-introspect.o \ + $(test-qom-obj-y) +test-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y) +test-io-obj-y = $(io-obj-y) $(test-crypto-obj-y) +test-block-obj-y = $(block-obj-y) $(test-io-obj-y) + +tests/check-qint$(EXESUF): tests/check-qint.o $(test-util-obj-y) +tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y) +tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y) +tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y) +tests/check-qfloat$(EXESUF): tests/check-qfloat.o $(test-util-obj-y) +tests/check-qjson$(EXESUF): tests/check-qjson.o $(test-util-obj-y) +tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom-obj-y) +tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y) +tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y) +tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y) +tests/test-rfifolock$(EXESUF): tests/test-rfifolock.o $(test-util-obj-y) +tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y) +tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y) +tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y) +tests/test-iov$(EXESUF): tests/test-iov.o $(test-util-obj-y) +tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o $(test-util-obj-y) tests/test-x86-cpuid$(EXESUF): tests/test-x86-cpuid.o -tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o migration/xbzrle.o page_cache.o libqemuutil.a +tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o migration/xbzrle.o page_cache.o $(test-util-obj-y) tests/test-cutils$(EXESUF): tests/test-cutils.o util/cutils.o tests/test-int128$(EXESUF): tests/test-int128.o -tests/rcutorture$(EXESUF): tests/rcutorture.o libqemuutil.a libqemustub.a -tests/test-rcu-list$(EXESUF): tests/test-rcu-list.o libqemuutil.a libqemustub.a +tests/rcutorture$(EXESUF): tests/rcutorture.o $(test-util-obj-y) +tests/test-rcu-list$(EXESUF): tests/test-rcu-list.o $(test-util-obj-y) tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \ hw/core/qdev.o hw/core/qdev-properties.o hw/core/hotplug.o\ hw/core/irq.o \ hw/core/fw-path-provider.o \ - $(qom-core-obj) \ - $(test-qapi-obj-y) \ - libqemuutil.a libqemustub.a + $(test-qapi-obj-y) tests/test-vmstate$(EXESUF): tests/test-vmstate.o \ migration/vmstate.o migration/qemu-file.o migration/qemu-file-buf.o \ migration/qemu-file-unix.o qjson.o \ - $(qom-core-obj) \ + $(test-qom-obj-y) +tests/test-timed-average$(EXESUF): tests/test-timed-average.o qemu-timer.o \ + $(test-util-obj-y) +tests/test-base64$(EXESUF): tests/test-base64.o \ libqemuutil.a libqemustub.a +tests/test-logging$(EXESUF): tests/test-logging.o $(test-util-obj-y) + tests/test-qapi-types.c tests/test-qapi-types.h :\ $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py) $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py \ @@ -332,21 +464,56 @@ $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-eve $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py \ $(gen-out-type) -o tests -p "test-" $<, \ " GEN $@") +tests/test-qmp-introspect.c tests/test-qmp-introspect.h :\ +$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py) + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-introspect.py \ + $(gen-out-type) -o tests -p "test-" $<, \ + " GEN $@") -tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a -tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a -tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a -tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a -tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a -tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a -tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a -tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a -tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a - -tests/test-mul64$(EXESUF): tests/test-mul64.o libqemuutil.a -tests/test-bitops$(EXESUF): tests/test-bitops.o libqemuutil.a -tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o libqemuutil.a libqemustub.a -tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o libqemuutil.a libqemustub.a +tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y) +tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) +tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y) +tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y) +tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y) +tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y) +tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y) +tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y) +tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y) + +tests/test-mul64$(EXESUF): tests/test-mul64.o $(test-util-obj-y) +tests/test-bitops$(EXESUF): tests/test-bitops.o $(test-util-obj-y) +tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o $(test-crypto-obj-y) +tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o $(test-crypto-obj-y) +tests/test-crypto-secret$(EXESUF): tests/test-crypto-secret.o $(test-crypto-obj-y) +tests/test-crypto-xts$(EXESUF): tests/test-crypto-xts.o $(test-crypto-obj-y) + +tests/crypto-tls-x509-helpers.o-cflags := $(TASN1_CFLAGS) +tests/crypto-tls-x509-helpers.o-libs := $(TASN1_LIBS) +tests/pkix_asn1_tab.o-cflags := $(TASN1_CFLAGS) + +tests/test-crypto-tlscredsx509.o-cflags := $(TASN1_CFLAGS) +tests/test-crypto-tlscredsx509$(EXESUF): tests/test-crypto-tlscredsx509.o \ + tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o $(test-crypto-obj-y) + +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-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/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \ + tests/io-channel-helpers.o $(test-io-obj-y) +tests/test-io-channel-tls$(EXESUF): tests/test-io-channel-tls.o \ + tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o \ + tests/io-channel-helpers.o $(test-io-obj-y) +tests/test-io-channel-command$(EXESUF): tests/test-io-channel-command.o \ + tests/io-channel-helpers.o $(test-io-obj-y) +tests/test-io-channel-buffer$(EXESUF): tests/test-io-channel-buffer.o \ + tests/io-channel-helpers.o $(test-io-obj-y) +tests/test-crypto-pbkdf$(EXESUF): tests/test-crypto-pbkdf.o $(test-crypto-obj-y) +tests/test-crypto-ivgen$(EXESUF): tests/test-crypto-ivgen.o $(test-crypto-obj-y) +tests/test-crypto-afsplit$(EXESUF): tests/test-crypto-afsplit.o $(test-crypto-obj-y) +tests/test-crypto-block$(EXESUF): tests/test-crypto-block.o $(test-crypto-obj-y) libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o @@ -354,9 +521,11 @@ libqos-pc-obj-y = $(libqos-obj-y) tests/libqos/pci-pc.o libqos-pc-obj-y += tests/libqos/malloc-pc.o tests/libqos/libqos-pc.o libqos-pc-obj-y += tests/libqos/ahci.o libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o +libqos-imx-obj-y = $(libqos-obj-y) tests/libqos/i2c-imx.o libqos-usb-obj-y = $(libqos-pc-obj-y) tests/libqos/usb.o libqos-virtio-obj-y = $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o +tests/device-introspect-test$(EXESUF): tests/device-introspect-test.o tests/rtc-test$(EXESUF): tests/rtc-test.o tests/m48t59-test$(EXESUF): tests/m48t59-test.o tests/endianness-test$(EXESUF): tests/endianness-test.o @@ -364,10 +533,15 @@ tests/spapr-phb-test$(EXESUF): tests/spapr-phb-test.o $(libqos-obj-y) tests/fdc-test$(EXESUF): tests/fdc-test.o tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y) tests/ahci-test$(EXESUF): tests/ahci-test.o $(libqos-pc-obj-y) +tests/ipmi-kcs-test$(EXESUF): tests/ipmi-kcs-test.o +tests/ipmi-bt-test$(EXESUF): tests/ipmi-bt-test.o tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y) -tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o $(libqos-obj-y) +tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o \ + tests/boot-sector.o $(libqos-obj-y) +tests/pxe-test$(EXESUF): tests/pxe-test.o tests/boot-sector.o $(libqos-obj-y) tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y) +tests/ds1338-test$(EXESUF): tests/ds1338-test.o $(libqos-imx-obj-y) tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y) tests/q35-test$(EXESUF): tests/q35-test.o $(libqos-pc-obj-y) tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y) @@ -381,7 +555,7 @@ tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y) tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y) -tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y) +tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y) $(libqos-virtio-obj-y) tests/virtio-rng-test$(EXESUF): tests/virtio-rng-test.o $(libqos-pc-obj-y) tests/virtio-scsi-test$(EXESUF): tests/virtio-scsi-test.o $(libqos-virtio-obj-y) tests/virtio-9p-test$(EXESUF): tests/virtio-9p-test.o @@ -405,10 +579,15 @@ tests/usb-hcd-uhci-test$(EXESUF): tests/usb-hcd-uhci-test.o $(libqos-usb-obj-y) tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-usb-obj-y) tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y) tests/pc-cpu-test$(EXESUF): tests/pc-cpu-test.o -tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o $(qtest-obj-y) +tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o $(qtest-obj-y) $(test-io-obj-y) tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o -tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o libqemuutil.a libqemustub.a -tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(block-obj-y) libqemuutil.a libqemustub.a +tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-y) +tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(test-block-obj-y) +tests/test-netfilter$(EXESUF): tests/test-netfilter.o $(qtest-obj-y) +tests/test-filter-mirror$(EXESUF): tests/test-filter-mirror.o $(qtest-obj-y) +tests/test-filter-redirector$(EXESUF): tests/test-filter-redirector.o $(qtest-obj-y) +tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y) +tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o ifeq ($(CONFIG_POSIX),y) LIBS += -lutil @@ -418,13 +597,18 @@ endif TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS))) ifeq ($(CONFIG_POSIX),y) -QTEST_TARGETS=$(foreach TARGET,$(TARGETS), $(if $(check-qtest-$(TARGET)-y), $(TARGET),)) +QTEST_TARGETS = $(TARGETS) check-qtest-y=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y)) +check-qtest-y += $(check-qtest-generic-y) +else +QTEST_TARGETS = endif -qtest-obj-y = tests/libqtest.o libqemuutil.a libqemustub.a +qtest-obj-y = tests/libqtest.o $(test-util-obj-y) $(check-qtest-y): $(qtest-obj-y) +tests/test-qga: tests/test-qga.o $(qtest-obj-y) + .PHONY: check-help check-help: @echo "Regression testing targets:" @@ -457,8 +641,8 @@ $(patsubst %, check-qtest-%, $(QTEST_TARGETS)): check-qtest-%: $(check-qtest-y) $(call quiet-command,QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \ QTEST_QEMU_IMG=qemu-img$(EXESUF) \ MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$((RANDOM % 255 + 1))} \ - gtester $(GTESTER_OPTIONS) -m=$(SPEED) $(check-qtest-$*-y),"GTESTER $@") - $(if $(CONFIG_GCOV),@for f in $(gcov-files-$*-y); do \ + gtester $(GTESTER_OPTIONS) -m=$(SPEED) $(check-qtest-$*-y) $(check-qtest-generic-y),"GTESTER $@") + $(if $(CONFIG_GCOV),@for f in $(gcov-files-$*-y) $(gcov-files-generic-y); do \ echo Gcov report for $$f:;\ $(GCOV) $(GCOV_OPTIONS) $$f -o `dirname $$f`; \ done,) @@ -469,7 +653,7 @@ $(patsubst %, check-%, $(check-unit-y)): check-%: % $(call quiet-command, \ MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$((RANDOM % 255 + 1))} \ gtester $(GTESTER_OPTIONS) -m=$(SPEED) $*,"GTESTER $*") - $(if $(CONFIG_GCOV),@for f in $(gcov-files-$(subst tests/,,$*)-y); do \ + $(if $(CONFIG_GCOV),@for f in $(gcov-files-$(subst tests/,,$*)-y) $(gcov-files-generic-y); do \ echo Gcov report for $$f:;\ $(GCOV) $(GCOV_OPTIONS) $$f -o `dirname $$f`; \ done,) @@ -479,7 +663,7 @@ $(patsubst %, check-%, $(check-unit-y)): check-%: % $(patsubst %, check-report-qtest-%.xml, $(QTEST_TARGETS)): check-report-qtest-%.xml: $(check-qtest-y) $(call quiet-command,QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \ QTEST_QEMU_IMG=qemu-img$(EXESUF) \ - gtester -q $(GTESTER_OPTIONS) -o $@ -m=$(SPEED) $(check-qtest-$*-y),"GTESTER $@") + gtester -q $(GTESTER_OPTIONS) -o $@ -m=$(SPEED) $(check-qtest-$*-y) $(check-qtest-generic-y),"GTESTER $@") check-report-unit.xml: $(check-unit-y) $(call quiet-command,gtester -q $(GTESTER_OPTIONS) -o $@ -m=$(SPEED) $^, "GTESTER $@") @@ -527,7 +711,7 @@ check: check-qapi-schema check-unit check-qtest check-clean: $(MAKE) -C tests/tcg clean rm -rf $(check-unit-y) tests/*.o $(QEMU_IOTESTS_HELPERS-y) - rm -rf $(sort $(foreach target,$(SYSEMU_TARGET_LIST), $(check-qtest-$(target)-y))) + rm -rf $(sort $(foreach target,$(SYSEMU_TARGET_LIST), $(check-qtest-$(target)-y)) $(check-qtest-generic-y)) clean: check-clean diff --git a/qemu/tests/ac97-test.c b/qemu/tests/ac97-test.c index af30ea1dd..75cab8f98 100644 --- a/qemu/tests/ac97-test.c +++ b/qemu/tests/ac97-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void nop(void) diff --git a/qemu/tests/acpi-test-data/pc/DSDT b/qemu/tests/acpi-test-data/pc/DSDT index c658203db94a7e7db7c36fde99a7075a8d75498d..9d1274d3c2e2b7a316d5133d013b0550024ee413 100644 GIT binary patch literal 5587 zcmb7I|8Eo75ufL^ll8Nmtdk{#Z-*7&=+OfH5(21I6?@l-9lXxsbtu)M?m7vosjg*} z1L;C^Qt+vPCJy00bo~4t6Ig%lx~}vjywhcG^V#wjotx!S1&C)ED?z^`*Fe_eZ>zwWxwbrbvhedtYm-EF})tt9;GKe0aa`K0V<4J)1I6MJ!7F)D{B zz&0r7E|E9nuH89i-p&<#Uy~9rbj8dSvDurR#|Ivr;Iqr@d+B=#qRZ@!^aeL-ZV@g-ecidGnpqTwy`)#CG79h* zU*%cGU-Jk*hksN21mEYM@*nslmt|*3FQ<=kmBMiJ7eT`A{IX^?hrUKZoehqjw$$y7 z;c@rjMRd4kg{PL;=hEkrEYI!w{*5O3N>^fVQSt=ZzYiD6kp8}smUKw z!y`jIc%q?tB5xz8M@BtLMLr0kT~o7~ECH{$j3G{hhB3I7!H1r)cdhaYrlS0cA}T?I zR?3!FP*AEH4VF*>0TEiMzGh@>R;p=Z@KEf;Sb}bvNj-Q z?49jyuwJ^`;?W`7g3TAHAyq_By1T(6Lr!VHk%k-%HoY&l*SP|ixbSonz}sn|y{DTD zyv?U>3m&%muKoDzWr@6{T%l}ZX5(g7#z&4Gd3qFNb-H>JceNMa;k#{2`gorw)P?Fz ztr15+Bb%P!iLb^W?g!g8JR=^D;j`LHJa6Ng+cfSI+wRoHahVv*K|&_6fKYjoaj&fO z44?jL0^(8(qm<)c0{^x_oeXl2oFMnqw4+xo_=3iK*XK-9Rzsg$^68_F)v(yJcHjk8 zvqqKp1M8(j)-irR*m`cndd|0=L;pst=hPp!p0hg!tzA20ok#u;^Z60;dEb2gkokOw z`TW4Ve%L%-5Ffny!if2TZ@zHId?Cbq!S1X^arM?=^U=V(2qC&f@W@5kO{io3VXKK? zkI7YJ(djBilG07RqM1gK;iajWtMxXxUOK^LkWml*T3-7pC7l0~z;ls`LcgUR*GvrQ zgIT?piv{n6)CZMoCMEkE|A)FVhW0eT0J5CBct3mKzFdkucb|~0!DJU&J-*}ead@=O z)?Xx%eHU*|2`J0g8D{w0pSrCLZ^L)cg&xQJt~Zx{?XnlL|K`#|SE*k-sA8SsMWbQ{ zCzUemX117J2(I~*argK4x3)9w`wzTNAO1G8b@#LT54Se%eg^8apC!F`*U;+?tl*fW zHZ%P4RU9)ctqun=T!(#zWc&LsfAQR<8J=rcWDkZM8r4Wv>*?cNVgoL@ zkWZiHC!ZW0T{10|mPt$WmsiMhj_Ms;6AmYE?qE2B4mloKCb37#N$8QEBpRF$O&bmu z=GC)?dD~!z55PmNb8t*QF;=Z+^gYl$W3_zC`yC3kK&3iolvlENJq8gajuXQKZ`c5$ zlvB>KS@x4ZRn*LCHj5#RnOw_eLA~yjvpKNO+&O68f}3`PVQTfJeSU;N2vxzEdAJj# z4yLMl3>gKub2%!yKuRQve&5!7VDd`v8aQw@psoe4>%prSyh_1K3toEgDhID6P|E3n zX}>L}&ayPl|L`;g5p1;S{OEz5;@jlgB$JWPg0Oyr;;E zKiEFIb7|>U+EU=a&9bqE5t1kLA&-@q5@SiUQvBl`I%B(YBYKHi7_(6xUM_shZ!1!o zx%OeMzlPT=gWG`Zgx^2D<{A5O(kH0)FDmwz1ufD6c?UO;df3V8w649!gQ_^8pSh9gW~^!gM5im=H2x{Fl-tGQpisrR)X9|`7TtKGOL zh+^EQos>%`#gIr?*(`o0&^M;txnSi61jcRzR6LoSj@Jwg@?ub+f!+DU%3Z^W)>?h= zjfztwP^bL8p;npT3urtHdPQjE0?I?7LMtY5;6Ne_x*)VB1vD876iveZzh z(0W8bkAy)>LhFox&V)jR)}sP?Gz_Wtgd(??^&2|g2p=;=>s|5%zGB5j*#P*6w(KBg=kCyA$H_waP#alF9JAR-YT9OB4O08*5&fNgRD!xDytEK9ifCwnm|Z{Ew~ G!3hAL1slNt diff --git a/qemu/tests/acpi-test-data/pc/DSDT.bridge b/qemu/tests/acpi-test-data/pc/DSDT.bridge new file mode 100644 index 0000000000000000000000000000000000000000..cf48c62aa71a7dd7d816fd4ef8ad626e39d4965c GIT binary patch literal 7446 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 diff --git a/qemu/tests/acpi-test-data/pc/SSDT.bridge b/qemu/tests/acpi-test-data/pc/SSDT.bridge deleted file mode 100644 index 6e6660b1fbe0bdb7fe0b257cb53c31fa9bb21a14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4345 zcmeH}KX2Pc7{>1;#%4}bOeu8iIC0FOL!H5u<3DjXNhD)gG3f(OG7``zha#u~6itvc z1p+U&3aH)t1w3SqK0^Bu`Vlhp8x&}rrRw{VdSws3qGJYWpzh)G<8yc9@m?zBcRt#T zakqKkwUpa@B0UA?IAaXk_-_Z5qqS5uQ|ay1%+!ljr8z6U`?<^t7TAo9@W6NZxpi)F z6V7w1h^PJr``nu7@(95{e_CKH_ZbsxG%6tqzXHn~>=A$FX!PqpdGoiw-m;|c8M`uo z{#$!~Sw^zfsBJSd?SjJd?yG07U%Yws^|RM-Qgs@ll#jG5#4i6Z{4nGOhO`aYlJQj` zFgdsN;`^81zWL@qv)Fs~o;z=uy?TT_ZKTy1gTtVqy_r@`Lli_IQ(NfZ1Z@A~5#GL} z|JYc=dnn^TWkawUzl)iC4xa4(zwU=@$X%GqiHuc;m)KCa$*GkIIHYwu`EkROwOW4b z94ieQ9rn(XVS|ByJA*=Xm{U%Jc8!Aw+N|ePqRykIg){L>j}7qh=cXqQqH2+IJ2a2J zDAMx}Thcc}KZ;?(xDvAa3(rxoMI2O1hA|9Eh{fb|1w}E8oBJ*tT5#CM);_f4lN3h1 zj6j3cMHr}*hkKFYVH_UvFleVdIi1h1R{>mg5FElmd9E`P;y~^CAK7Ybf(wteq-wR) zOdEa-;=WoR3s`>z=D?F#_u0Fw@qii|*hu;ZKdAMc{7Qg}%)~&R^gqi3RX2ipVysim zZkP0eHXChcYwF$MgD5X{it?1A%DYZ&yRf|&UO+s(# z&<3T#TbVxsM~hPGJfZVCv`MMA2)(66DRqI+1s&>9>LQ_wT9i_66M9>R`jomv=#mzt z)H{UU(V+pQ-X-*|7Nyi>LYH;uBTBtT=shh;sVju8T!lK(vVRzL3TLI`9%lpGWuKo} z$33W-Aa=&W00Db=?wBaZu|dF|mpd*Ba_S&p-^!g66zG(XdnMSD2MRWyvBI7?LnT-4Gu%6zMJ>H+)e{`m6Z(rX?%C1JjZ-KVPlkrvx(p0-RC(E&u=k diff --git a/qemu/tests/acpi-test-data/q35/DSDT b/qemu/tests/acpi-test-data/q35/DSDT index 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..1c089c34b06c9f2ea9fe67abb45498021319303c 100644 GIT binary patch delta 1127 zcmaJ>OK;Oa5T141)Y>@3OEcaaa-=;}6-7c6wG^qR zv_S&yzHvYxwLbv$2hc0G9(zXO$OR7cMA)@yC8CHujOLs9cIWfXtbfexJJ@4hYu^Gu z_9y%ujg*EbIV*GO*6pzCMnlR~RE%yah0EbigSx(gyI2W#$E6Rh<2R987N$XkF-rO9 zrSdUaNqt4j2Nm_oSe~d=R(CXBt9!b0q@UCHeBHxF)Xh&^^6CuCG-m-rVK*k0`-!8` zmw`mLpO}k`!k`nqe&SfPIFRV~6V2#-VPfQ8jnPrTRPcE8ZQv2r{v*yuH;RKq)%uC~ zXtOwx615KA6_?bGQPDNc*S9uSRyReEwH%yDq-HU$A}ejBk+BWNwYJPo^ITd*qLT3M@SLV%1uHSSD>JgH zF?0IO9XWZEl|{Fblz!xsdE2(929z;|j3K0pGPA|DoZ7JaP@R;cO9P*s-ZD-nqFp*~ z)z)7;f4cSfzuG&;`mv5IaRWOvU@eq6*0B}t>M;<8myGb$v`cMU>IN`%PMj9(xs*5( zh`W%0wZP1Bf7V6<__MHPjy7ABW#t2o*5I|={zP->Tr-zK$RJw#R_gD&>)515J&kzH zKzy?7ete~(cNhv%RgIDWWdP$sxO-886bZ@=OPHE44e2$6N0+#eeL)f&C}H9=j!3=9 z5DCZvBK|B#;vpydnJ;$D`3u4@>RM)+NU26eP5uKJX4_ojju(irxX$eR2@vCkz!W{b T?ZIB`Or1K@v6H55vju+u_7MZf delta 415 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

      u^pue6@4>!nN*9I%S~{+LyaC zc8Vu36H@d%9igJmuTCU)_Mff3;z{qA+^qOFaEVq4JygXRbH=PJs$5^ZN!O*be?9dq z{q+c1l~BC|aJ>8A)qm0pZshADbJb|lZ?oTKtvvMOm7kWnzs>S|=YCcMH2Ge2Iv=cg z0d5xY!#bT`Y_mg}`f1UQmUtBDq#9*p`T0gCJ@ z7ZV>IOH8L?_QcbGMf*9)$7GwQ2ML?d%e!fsaT88$OfE5J!a@OStF&O>*?%T1yeI?Q jX%@rFLAX-J3{Qb=Q?EzI33AL!DhaQkf zkbpZcz@cxz1HdD2%zJPkC(MjZIkJcG_sx&K`LQu^@wElO-bOQ-8b3PF(x8t40Ac!i zV=>>dCbC+N1J}yjY(&@i@uwcN1cm`BGHk@wVl9}9ekR)F`i0@O_{Vz{CI0+)1E7fm zaHy&-rQ&Nr9U(<}->d4|@4xWL1(ZwK8R-P|({bF>nGV8W8(h1GjQsxT;n~Ug>GQ)g zbebaM-KNtX=OgAjzQeo=@b1hhI<$SK!%ztEL)&*dyv1<`$R1IIM7_z)IgMN{pLVur zZk;El))wIp?lihm@A>J;o7b<-U;I~oC02s@IKw-gn}T&$g8St{)C7&)@yho@se_;y zcOa|^3%Hb8g@6R7Az}&`^lUN-7?NZHM}v*5*nQUe!apCu2P%K*N9!-5TDViyvDI$GfvFlru5W)^u ziLp$LGs9G__L)j1mX(X^O}$C5YL@JyG14D6iW>EHJG9Ai*)>66T^q(Zp7pK?9ybL4 X76t>n!u=R7Yw+L-3~S(MnHBH{@bQ-c diff --git a/qemu/tests/acpi-test-data/q35/SSDT.bridge b/qemu/tests/acpi-test-data/q35/SSDT.bridge deleted file mode 100644 index a77868861754ce0b3333b2b7bc8091c03a53754d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 708 zcmaJ<&2G~`5T12{u{ImwMn+Uf6~4luxC9U8N?M<3n z7pYlki})vZ8(pdQ!tD6n+t+6=|0}-`3&BF1;k_m^+ zsZ1xHO7a3nr0cDAg1hIiPEBQ66|4iCuUE4>L|UpCOhPmnnhBu^B7I=7>kUeXVVkSe zSf)m~VX9F3Or>MXs@ct^-lSNyo9?4AG7MZrjrzlDw8?7TF;Qea8z%*x^`42IGz9+^ g1_54SD}jp@cyI;!1giNBvvpaBxef%bmU#((0Zb>F0RR91 diff --git a/qemu/tests/ahci-test.c b/qemu/tests/ahci-test.c index 87d769186..6869f7f46 100644 --- a/qemu/tests/ahci-test.c +++ b/qemu/tests/ahci-test.c @@ -22,9 +22,7 @@ * THE SOFTWARE. */ -#include -#include -#include +#include "qemu/osdep.h" #include #include @@ -39,15 +37,17 @@ #include "hw/pci/pci_ids.h" #include "hw/pci/pci_regs.h" -/* Test-specific defines -- in MiB */ -#define TEST_IMAGE_SIZE_MB (200 * 1024) -#define TEST_IMAGE_SECTORS ((TEST_IMAGE_SIZE_MB / AHCI_SECTOR_SIZE) \ - * 1024 * 1024) +/* Test images sizes in MB */ +#define TEST_IMAGE_SIZE_MB_LARGE (200 * 1024) +#define TEST_IMAGE_SIZE_MB_SMALL 64 /*** Globals ***/ static char tmp_path[] = "/tmp/qtest.XXXXXX"; static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX"; +static char mig_socket[] = "/tmp/qtest-migration.XXXXXX"; static bool ahci_pedantic; +static const char *imgfmt; +static unsigned test_image_size_mb; /*** Function Declarations ***/ static void ahci_test_port_spec(AHCIQState *ahci, uint8_t port); @@ -60,6 +60,11 @@ static void ahci_test_pmcap(AHCIQState *ahci, uint8_t offset); /*** Utilities ***/ +static uint64_t mb_to_sectors(uint64_t image_size_mb) +{ + return (image_size_mb * 1024 * 1024) / AHCI_SECTOR_SIZE; +} + static void string_bswap16(uint16_t *s, size_t bytes) { g_assert_cmphex((bytes & 1), ==, 0); @@ -71,32 +76,6 @@ static void string_bswap16(uint16_t *s, size_t bytes) } } -static void generate_pattern(void *buffer, size_t len, size_t cycle_len) -{ - int i, j; - unsigned char *tx = (unsigned char *)buffer; - unsigned char p; - size_t *sx; - - /* Write an indicative pattern that varies and is unique per-cycle */ - p = rand() % 256; - for (i = j = 0; i < len; i++, j++) { - tx[i] = p; - if (j % cycle_len == 0) { - p = rand() % 256; - } - } - - /* force uniqueness by writing an id per-cycle */ - for (i = 0; i < len / cycle_len; i++) { - j = i * cycle_len; - if (j + sizeof(*sx) <= len) { - sx = (size_t *)&tx[j]; - *sx = i; - } - } -} - /** * Verify that the transfer did not corrupt our state at all. */ @@ -140,8 +119,11 @@ static void ahci_migrate(AHCIQState *from, AHCIQState *to, const char *uri) { QOSState *tmp = to->parent; QPCIDevice *dev = to->dev; + char *uri_local = NULL; + if (uri == NULL) { - uri = "tcp:127.0.0.1:1234"; + uri_local = g_strdup_printf("%s%s", "unix:", mig_socket); + uri = uri_local; } /* context will be 'to' after completion. */ @@ -161,6 +143,7 @@ static void ahci_migrate(AHCIQState *from, AHCIQState *to, const char *uri) from->dev = dev; verify_state(to); + g_free(uri_local); } /*** Test Setup & Teardown ***/ @@ -196,11 +179,11 @@ static AHCIQState *ahci_boot(const char *cli, ...) va_end(ap); } else { cli = "-drive if=none,id=drive0,file=%s,cache=writeback,serial=%s" - ",format=qcow2" + ",format=%s" " -M q35 " "-device ide-hd,drive=drive0 " "-global ide-hd.ver=%s"; - s = ahci_boot(cli, tmp_path, "testdisk", "version"); + s = ahci_boot(cli, tmp_path, "testdisk", imgfmt, "version"); } return s; @@ -230,6 +213,7 @@ static AHCIQState *ahci_boot_and_enable(const char *cli, ...) va_list ap; uint16_t buff[256]; uint8_t port; + uint8_t hello; if (cli) { va_start(ap, cli); @@ -244,7 +228,12 @@ static AHCIQState *ahci_boot_and_enable(const char *cli, ...) /* Initialize test device */ port = ahci_port_select(ahci); ahci_port_clear(ahci, port); - ahci_io(ahci, port, CMD_IDENTIFY, &buff, sizeof(buff), 0); + if (is_atapi(ahci, port)) { + hello = CMD_PACKET_ID; + } else { + hello = CMD_IDENTIFY; + } + ahci_io(ahci, port, hello, &buff, sizeof(buff), 0); return ahci; } @@ -899,18 +888,12 @@ static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize, static uint8_t ahci_test_nondata(AHCIQState *ahci, uint8_t ide_cmd) { uint8_t port; - AHCICommand *cmd; /* Sanitize */ port = ahci_port_select(ahci); ahci_port_clear(ahci, port); - /* Issue Command */ - cmd = ahci_command_create(ide_cmd); - ahci_command_commit(ahci, cmd, port); - ahci_command_issue(ahci, cmd); - ahci_command_verify(ahci, cmd); - ahci_command_free(cmd); + ahci_io(ahci, port, ide_cmd, NULL, 0, 0); return port; } @@ -926,7 +909,7 @@ static void ahci_test_max(AHCIQState *ahci) uint64_t nsect; uint8_t port; uint8_t cmd; - uint64_t config_sect = TEST_IMAGE_SECTORS - 1; + uint64_t config_sect = mb_to_sectors(test_image_size_mb) - 1; if (config_sect > 0xFFFFFF) { cmd = CMD_READ_MAX_EXT; @@ -1060,14 +1043,14 @@ static void test_dma_fragmented(void) ahci_command_commit(ahci, cmd, px); ahci_command_issue(ahci, cmd); ahci_command_verify(ahci, cmd); - g_free(cmd); + ahci_command_free(cmd); cmd = ahci_command_create(CMD_READ_DMA); ahci_command_adjust(cmd, 0, ptr, bufsize, 32); ahci_command_commit(ahci, cmd, px); ahci_command_issue(ahci, cmd); ahci_command_verify(ahci, cmd); - g_free(cmd); + ahci_command_free(cmd); /* Read back the guest's receive buffer into local memory */ bufread(ptr, rx, bufsize); @@ -1095,33 +1078,23 @@ static void test_flush_retry(void) AHCIQState *ahci; AHCICommand *cmd; uint8_t port; - const char *s; prepare_blkdebug_script(debug_path, "flush_to_disk"); ahci = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0," - "format=qcow2,cache=writeback," + "format=%s,cache=writeback," "rerror=stop,werror=stop " "-M q35 " "-device ide-hd,drive=drive0 ", debug_path, - tmp_path); + tmp_path, imgfmt); /* Issue Flush Command and wait for error */ port = ahci_port_select(ahci); ahci_port_clear(ahci, port); - cmd = ahci_command_create(CMD_FLUSH_CACHE); - ahci_command_commit(ahci, cmd, port); - ahci_command_issue_async(ahci, cmd); - qmp_eventwait("STOP"); - /* Complete the command */ - s = "{'execute':'cont' }"; - qmp_async(s); - qmp_eventwait("RESUME"); - ahci_command_wait(ahci, cmd); - ahci_command_verify(ahci, cmd); + cmd = ahci_guest_io_halt(ahci, port, CMD_FLUSH_CACHE, 0, 0, 0); + ahci_guest_io_resume(ahci, cmd); - ahci_command_free(cmd); ahci_shutdown(ahci); } @@ -1131,18 +1104,19 @@ static void test_flush_retry(void) static void test_migrate_sanity(void) { AHCIQState *src, *dst; - const char *uri = "tcp:127.0.0.1:1234"; + char *uri = g_strdup_printf("unix:%s", mig_socket); src = ahci_boot("-m 1024 -M q35 " - "-hda %s ", tmp_path); + "-drive if=ide,file=%s,format=%s ", tmp_path, imgfmt); dst = ahci_boot("-m 1024 -M q35 " - "-hda %s " - "-incoming %s", tmp_path, uri); + "-drive if=ide,file=%s,format=%s " + "-incoming %s", tmp_path, imgfmt, uri); ahci_migrate(src, dst, uri); ahci_shutdown(src); ahci_shutdown(dst); + g_free(uri); } /** @@ -1155,14 +1129,14 @@ static void ahci_migrate_simple(uint8_t cmd_read, uint8_t cmd_write) size_t bufsize = 4096; unsigned char *tx = g_malloc(bufsize); unsigned char *rx = g_malloc0(bufsize); - unsigned i; - const char *uri = "tcp:127.0.0.1:1234"; + char *uri = g_strdup_printf("unix:%s", mig_socket); src = ahci_boot_and_enable("-m 1024 -M q35 " - "-hda %s ", tmp_path); + "-drive if=ide,format=%s,file=%s ", + imgfmt, tmp_path); dst = ahci_boot("-m 1024 -M q35 " - "-hda %s " - "-incoming %s", tmp_path, uri); + "-drive if=ide,format=%s,file=%s " + "-incoming %s", imgfmt, tmp_path, uri); set_context(src->parent); @@ -1171,9 +1145,7 @@ static void ahci_migrate_simple(uint8_t cmd_read, uint8_t cmd_write) ahci_port_clear(src, px); /* create pattern */ - for (i = 0; i < bufsize; i++) { - tx[i] = (bufsize - i); - } + generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); /* Write, migrate, then read. */ ahci_io(src, px, cmd_write, tx, bufsize, 0); @@ -1187,6 +1159,7 @@ static void ahci_migrate_simple(uint8_t cmd_read, uint8_t cmd_write) ahci_shutdown(dst); g_free(rx); g_free(tx); + g_free(uri); } static void test_migrate_dma(void) @@ -1213,29 +1186,25 @@ static void ahci_halted_io_test(uint8_t cmd_read, uint8_t cmd_write) size_t bufsize = 4096; unsigned char *tx = g_malloc(bufsize); unsigned char *rx = g_malloc0(bufsize); - unsigned i; uint64_t ptr; AHCICommand *cmd; prepare_blkdebug_script(debug_path, "write_aio"); ahci = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0," - "format=qcow2,cache=writeback," + "format=%s,cache=writeback," "rerror=stop,werror=stop " "-M q35 " "-device ide-hd,drive=drive0 ", debug_path, - tmp_path); + tmp_path, imgfmt); /* Initialize and prepare */ port = ahci_port_select(ahci); ahci_port_clear(ahci, port); - for (i = 0; i < bufsize; i++) { - tx[i] = (bufsize - i); - } - /* create DMA source buffer and write pattern */ + generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); ptr = ahci_alloc(ahci, bufsize); g_assert(ptr); memwrite(ptr, tx, bufsize); @@ -1282,38 +1251,34 @@ static void ahci_migrate_halted_io(uint8_t cmd_read, uint8_t cmd_write) size_t bufsize = 4096; unsigned char *tx = g_malloc(bufsize); unsigned char *rx = g_malloc0(bufsize); - unsigned i; uint64_t ptr; AHCICommand *cmd; - const char *uri = "tcp:127.0.0.1:1234"; + char *uri = g_strdup_printf("unix:%s", mig_socket); prepare_blkdebug_script(debug_path, "write_aio"); src = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0," - "format=qcow2,cache=writeback," + "format=%s,cache=writeback," "rerror=stop,werror=stop " "-M q35 " "-device ide-hd,drive=drive0 ", debug_path, - tmp_path); + tmp_path, imgfmt); dst = ahci_boot("-drive file=%s,if=none,id=drive0," - "format=qcow2,cache=writeback," + "format=%s,cache=writeback," "rerror=stop,werror=stop " "-M q35 " "-device ide-hd,drive=drive0 " "-incoming %s", - tmp_path, uri); + tmp_path, imgfmt, uri); set_context(src->parent); /* Initialize and prepare */ port = ahci_port_select(src); ahci_port_clear(src, port); - - for (i = 0; i < bufsize; i++) { - tx[i] = (bufsize - i); - } + generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); /* create DMA source buffer and write pattern */ ptr = ahci_alloc(src, bufsize); @@ -1338,6 +1303,7 @@ static void ahci_migrate_halted_io(uint8_t cmd_read, uint8_t cmd_write) ahci_shutdown(dst); g_free(rx); g_free(tx); + g_free(uri); } static void test_migrate_halted_dma(void) @@ -1359,20 +1325,22 @@ static void test_flush_migrate(void) AHCICommand *cmd; uint8_t px; const char *s; - const char *uri = "tcp:127.0.0.1:1234"; + char *uri = g_strdup_printf("unix:%s", mig_socket); prepare_blkdebug_script(debug_path, "flush_to_disk"); src = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0," - "cache=writeback,rerror=stop,werror=stop " + "cache=writeback,rerror=stop,werror=stop," + "format=%s " "-M q35 " "-device ide-hd,drive=drive0 ", - debug_path, tmp_path); + debug_path, tmp_path, imgfmt); dst = ahci_boot("-drive file=%s,if=none,id=drive0," - "cache=writeback,rerror=stop,werror=stop " + "cache=writeback,rerror=stop,werror=stop," + "format=%s " "-M q35 " "-device ide-hd,drive=drive0 " - "-incoming %s", tmp_path, uri); + "-incoming %s", tmp_path, imgfmt, uri); set_context(src->parent); @@ -1397,6 +1365,7 @@ static void test_flush_migrate(void) ahci_command_free(cmd); ahci_shutdown(src); ahci_shutdown(dst); + g_free(uri); } static void test_max(void) @@ -1442,6 +1411,98 @@ static void test_ncq_simple(void) ahci_shutdown(ahci); } +static int prepare_iso(size_t size, unsigned char **buf, char **name) +{ + char cdrom_path[] = "/tmp/qtest.iso.XXXXXX"; + unsigned char *patt; + ssize_t ret; + int fd = mkstemp(cdrom_path); + + g_assert(buf); + g_assert(name); + patt = g_malloc(size); + + /* Generate a pattern and build a CDROM image to read from */ + generate_pattern(patt, size, ATAPI_SECTOR_SIZE); + ret = write(fd, patt, size); + g_assert(ret == size); + + *name = g_strdup(cdrom_path); + *buf = patt; + return fd; +} + +static void remove_iso(int fd, char *name) +{ + unlink(name); + g_free(name); + close(fd); +} + +static int ahci_cb_cmp_buff(AHCIQState *ahci, AHCICommand *cmd, + const AHCIOpts *opts) +{ + unsigned char *tx = opts->opaque; + unsigned char *rx = g_malloc0(opts->size); + + bufread(opts->buffer, rx, opts->size); + g_assert_cmphex(memcmp(tx, rx, opts->size), ==, 0); + g_free(rx); + + return 0; +} + +static void ahci_test_cdrom(int nsectors, bool dma) +{ + AHCIQState *ahci; + unsigned char *tx; + char *iso; + int fd; + AHCIOpts opts = { + .size = (ATAPI_SECTOR_SIZE * nsectors), + .atapi = true, + .atapi_dma = dma, + .post_cb = ahci_cb_cmp_buff, + }; + + /* Prepare ISO and fill 'tx' buffer */ + fd = prepare_iso(1024 * 1024, &tx, &iso); + opts.opaque = tx; + + /* Standard startup wonkery, but use ide-cd and our special iso file */ + ahci = ahci_boot_and_enable("-drive if=none,id=drive0,file=%s,format=raw " + "-M q35 " + "-device ide-cd,drive=drive0 ", iso); + + /* Build & Send AHCI command */ + ahci_exec(ahci, ahci_port_select(ahci), CMD_ATAPI_READ_10, &opts); + + /* Cleanup */ + g_free(tx); + ahci_shutdown(ahci); + remove_iso(fd, iso); +} + +static void test_cdrom_dma(void) +{ + ahci_test_cdrom(1, true); +} + +static void test_cdrom_dma_multi(void) +{ + ahci_test_cdrom(3, true); +} + +static void test_cdrom_pio(void) +{ + ahci_test_cdrom(1, false); +} + +static void test_cdrom_pio_multi(void) +{ + ahci_test_cdrom(3, false); +} + /******************************************************************************/ /* AHCI I/O Test Matrix Definitions */ @@ -1513,7 +1574,7 @@ static uint64_t offset_sector(enum OffsetType ofst, return 1; case OFFSET_HIGH: ceil = (addr_type == ADDR_MODE_LBA28) ? 0xfffffff : 0xffffffffffff; - ceil = MIN(ceil, TEST_IMAGE_SECTORS - 1); + ceil = MIN(ceil, mb_to_sectors(test_image_size_mb) - 1); nsectors = buffsize / AHCI_SECTOR_SIZE; return ceil - nsectors + 1; default: @@ -1595,8 +1656,9 @@ static void create_ahci_io_test(enum IOMode type, enum AddrMode addr, enum BuffLen len, enum OffsetType offset) { char *name; - AHCIIOTestOptions *opts = g_malloc(sizeof(AHCIIOTestOptions)); + AHCIIOTestOptions *opts; + opts = g_malloc(sizeof(AHCIIOTestOptions)); opts->length = len; opts->address_type = addr; opts->io_type = type; @@ -1608,6 +1670,13 @@ static void create_ahci_io_test(enum IOMode type, enum AddrMode addr, buff_len_str[len], offset_str[offset]); + if ((addr == ADDR_MODE_LBA48) && (offset == OFFSET_HIGH) && + (mb_to_sectors(test_image_size_mb) <= 0xFFFFFFF)) { + g_test_message("%s: skipped; test image too small", name); + g_free(name); + return; + } + qtest_add_data_func(name, opts, test_io_interface); g_free(name); } @@ -1654,15 +1723,33 @@ int main(int argc, char **argv) return 0; } - /* Create a temporary qcow2 image */ - close(mkstemp(tmp_path)); - mkqcow2(tmp_path, TEST_IMAGE_SIZE_MB); + /* Create a temporary image */ + fd = mkstemp(tmp_path); + g_assert(fd >= 0); + if (have_qemu_img()) { + imgfmt = "qcow2"; + test_image_size_mb = TEST_IMAGE_SIZE_MB_LARGE; + mkqcow2(tmp_path, TEST_IMAGE_SIZE_MB_LARGE); + } else { + g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; " + "skipping LBA48 high-sector tests"); + imgfmt = "raw"; + test_image_size_mb = TEST_IMAGE_SIZE_MB_SMALL; + ret = ftruncate(fd, test_image_size_mb * 1024 * 1024); + g_assert(ret == 0); + } + close(fd); /* Create temporary blkdebug instructions */ fd = mkstemp(debug_path); g_assert(fd >= 0); close(fd); + /* Reserve a hollow file to use as a socket for migration tests */ + fd = mkstemp(mig_socket); + g_assert(fd >= 0); + close(fd); + /* Run the tests */ qtest_add_func("/ahci/sanity", test_sanity); qtest_add_func("/ahci/pci_spec", test_pci_spec); @@ -1700,11 +1787,17 @@ int main(int argc, char **argv) qtest_add_func("/ahci/io/ncq/retry", test_halted_ncq); qtest_add_func("/ahci/migrate/ncq/halted", test_migrate_halted_ncq); + qtest_add_func("/ahci/cdrom/dma/single", test_cdrom_dma); + qtest_add_func("/ahci/cdrom/dma/multi", test_cdrom_dma_multi); + qtest_add_func("/ahci/cdrom/pio/single", test_cdrom_pio); + qtest_add_func("/ahci/cdrom/pio/multi", test_cdrom_pio_multi); + ret = g_test_run(); /* Cleanup */ unlink(tmp_path); unlink(debug_path); + unlink(mig_socket); return ret; } diff --git a/qemu/tests/bios-tables-test.c b/qemu/tests/bios-tables-test.c index 0de1742d7..03528140c 100644 --- a/qemu/tests/bios-tables-test.c +++ b/qemu/tests/bios-tables-test.c @@ -10,16 +10,15 @@ * See the COPYING file in the top-level directory. */ -#include -#include +#include "qemu/osdep.h" #include #include #include "qemu-common.h" #include "libqtest.h" -#include "qemu/compiler.h" #include "hw/acpi/acpi-defs.h" -#include "hw/i386/smbios.h" +#include "hw/smbios/smbios.h" #include "qemu/bitmap.h" +#include "boot-sector.h" #define MACHINE_PC "pc" #define MACHINE_Q35 "q35" @@ -50,16 +49,9 @@ typedef struct { int rsdt_tables_nr; GArray *tables; uint32_t smbios_ep_addr; - struct smbios_entry_point smbios_ep_table; + struct smbios_21_entry_point smbios_ep_table; } test_data; -#define LOW(x) ((x) & 0xff) -#define HIGH(x) ((x) >> 8) - -#define SIGNATURE 0xdead -#define SIGNATURE_OFFSET 0x10 -#define BOOT_SECTOR_ADDRESS 0x7c00 - #define ACPI_READ_FIELD(field, addr) \ do { \ switch (sizeof(field)) { \ @@ -119,35 +111,6 @@ typedef struct { g_assert_cmpstr(ACPI_ASSERT_CMP_str, ==, expected); \ } while (0) -/* Boot sector code: write SIGNATURE into memory, - * then halt. - * Q35 machine requires a minimum 0x7e000 bytes disk. - * (bug or feature?) - */ -static uint8_t boot_sector[0x7e000] = { - /* 7c00: mov $0xdead,%ax */ - [0x00] = 0xb8, - [0x01] = LOW(SIGNATURE), - [0x02] = HIGH(SIGNATURE), - /* 7c03: mov %ax,0x7c10 */ - [0x03] = 0xa3, - [0x04] = LOW(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET), - [0x05] = HIGH(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET), - /* 7c06: cli */ - [0x06] = 0xfa, - /* 7c07: hlt */ - [0x07] = 0xf4, - /* 7c08: jmp 0x7c07=0x7c0a-3 */ - [0x08] = 0xeb, - [0x09] = LOW(-3), - /* We mov 0xdead here: set value to make debugging easier */ - [SIGNATURE_OFFSET] = LOW(0xface), - [SIGNATURE_OFFSET + 1] = HIGH(0xface), - /* End of boot sector marker */ - [0x1FE] = 0x55, - [0x1FF] = 0xAA, -}; - static const char *disk = "tests/acpi-test-disk.raw"; static const char *data_dir = "tests/acpi-test-data"; #ifdef CONFIG_IASL @@ -161,31 +124,23 @@ static void free_test_data(test_data *data) AcpiSdtTable *temp; int i; - if (data->rsdt_tables_addr) { - g_free(data->rsdt_tables_addr); - } + g_free(data->rsdt_tables_addr); for (i = 0; i < data->tables->len; ++i) { temp = &g_array_index(data->tables, AcpiSdtTable, i); - if (temp->aml) { - g_free(temp->aml); + g_free(temp->aml); + if (temp->aml_file && + !temp->tmp_files_retain && + g_strstr_len(temp->aml_file, -1, "aml-")) { + unlink(temp->aml_file); } - if (temp->aml_file) { - if (!temp->tmp_files_retain && - g_strstr_len(temp->aml_file, -1, "aml-")) { - unlink(temp->aml_file); - } - g_free(temp->aml_file); - } - if (temp->asl) { - g_free(temp->asl); - } - if (temp->asl_file) { - if (!temp->tmp_files_retain) { - unlink(temp->asl_file); - } - g_free(temp->asl_file); + g_free(temp->aml_file); + g_free(temp->asl); + if (temp->asl_file && + !temp->tmp_files_retain) { + unlink(temp->asl_file); } + g_free(temp->asl_file); } g_array_free(data->tables, false); @@ -420,9 +375,7 @@ static void dump_aml_files(test_data *data, bool rebuild) close(fd); - if (aml_file) { - g_free(aml_file); - } + g_free(aml_file); } } @@ -479,7 +432,7 @@ static bool load_asl(GArray *sdts, AcpiSdtTable *sdt) #define COMMENT_END "*/" #define DEF_BLOCK "DefinitionBlock (" -#define BLOCK_NAME_END ".aml" +#define BLOCK_NAME_END "," static GString *normalize_asl(gchar *asl_code) { @@ -590,6 +543,22 @@ static void test_acpi_asl(test_data *data) (gchar *)&signature, sdt->asl_file, sdt->aml_file, exp_sdt->asl_file, exp_sdt->aml_file); + if (getenv("V")) { + const char *diff_cmd = getenv("DIFF"); + if (diff_cmd) { + int ret G_GNUC_UNUSED; + char *diff = g_strdup_printf("%s %s %s", diff_cmd, + exp_sdt->asl_file, sdt->asl_file); + ret = system(diff) ; + g_free(diff); + } else { + fprintf(stderr, "acpi-test: Warning. not showing " + "difference since no diff utility is specified. " + "Set 'DIFF' environment variable to a preferred " + "diff utility and run 'make V=1 check' again to " + "see ASL difference."); + } + } } } g_string_free(asl, true); @@ -601,7 +570,7 @@ static void test_acpi_asl(test_data *data) static bool smbios_ep_table_ok(test_data *data) { - struct smbios_entry_point *ep_table = &data->smbios_ep_table; + struct smbios_21_entry_point *ep_table = &data->smbios_ep_table; uint32_t addr = data->smbios_ep_addr; ACPI_READ_ARRAY(ep_table->anchor_string, addr); @@ -681,7 +650,7 @@ static inline bool smbios_single_instance(uint8_t type) static void test_smbios_structs(test_data *data) { DECLARE_BITMAP(struct_bitmap, SMBIOS_MAX_TYPE+1) = { 0 }; - struct smbios_entry_point *ep_table = &data->smbios_ep_table; + struct smbios_21_entry_point *ep_table = &data->smbios_ep_table; uint32_t addr = ep_table->structure_table_address; int i, len, max_len = 0; uint8_t type, prv, crt; @@ -733,10 +702,6 @@ static void test_smbios_structs(test_data *data) static void test_acpi_one(const char *params, test_data *data) { char *args; - uint8_t signature_low; - uint8_t signature_high; - uint16_t signature; - int i; args = g_strdup_printf("-net none -display none %s " "-drive id=hd0,if=none,file=%s,format=raw " @@ -745,24 +710,7 @@ static void test_acpi_one(const char *params, test_data *data) qtest_start(args); - /* Wait at most 1 minute */ -#define TEST_DELAY (1 * G_USEC_PER_SEC / 10) -#define TEST_CYCLES MAX((60 * G_USEC_PER_SEC / TEST_DELAY), 1) - - /* Poll until code has run and modified memory. Once it has we know BIOS - * initialization is done. TODO: check that IP reached the halt - * instruction. - */ - for (i = 0; i < TEST_CYCLES; ++i) { - signature_low = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET); - signature_high = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET + 1); - signature = (signature_high << 8) | signature_low; - if (signature == SIGNATURE) { - break; - } - g_usleep(TEST_DELAY); - } - g_assert_cmphex(signature, ==, SIGNATURE); + boot_sector_test(); test_acpi_rsdp_address(data); test_acpi_rsdp_table(data); @@ -836,15 +784,11 @@ static void test_acpi_q35_tcg_bridge(void) int main(int argc, char *argv[]) { const char *arch = qtest_get_arch(); - FILE *f = fopen(disk, "w"); int ret; - if (!f) { - fprintf(stderr, "Couldn't open \"%s\": %s", disk, strerror(errno)); - return 1; - } - fwrite(boot_sector, 1, sizeof boot_sector, f); - fclose(f); + ret = boot_sector_init(disk); + if(ret) + return ret; g_test_init(&argc, &argv, NULL); @@ -855,6 +799,6 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/q35/tcg/bridge", test_acpi_q35_tcg_bridge); } ret = g_test_run(); - unlink(disk); + boot_sector_cleanup(disk); return ret; } diff --git a/qemu/tests/boot-order-test.c b/qemu/tests/boot-order-test.c index 360a6911e..a6d8bd5cb 100644 --- a/qemu/tests/boot-order-test.c +++ b/qemu/tests/boot-order-test.c @@ -10,14 +10,12 @@ * See the COPYING file in the top-level directory. */ -#include +#include "qemu/osdep.h" #include #include "libqos/fw_cfg.h" #include "libqtest.h" -#define NO_QEMU_PROTOS -#include "hw/nvram/fw_cfg.h" -#undef NO_QEMU_PROTOS +#include "hw/nvram/fw_cfg_keys.h" typedef struct { const char *args; diff --git a/qemu/tests/boot-sector.c b/qemu/tests/boot-sector.c new file mode 100644 index 000000000..3ffe2987f --- /dev/null +++ b/qemu/tests/boot-sector.c @@ -0,0 +1,118 @@ +/* + * QEMU boot sector testing helpers. + * + * Copyright (c) 2016 Red Hat Inc. + * + * Authors: + * Michael S. Tsirkin + * Victor Kaplansky + * + * 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 "boot-sector.h" +#include "qemu-common.h" +#include "libqtest.h" + +#define LOW(x) ((x) & 0xff) +#define HIGH(x) ((x) >> 8) + +#define SIGNATURE 0xdead +#define SIGNATURE_OFFSET 0x10 +#define BOOT_SECTOR_ADDRESS 0x7c00 + +/* Boot sector code: write SIGNATURE into memory, + * then halt. + * Q35 machine requires a minimum 0x7e000 bytes disk. + * (bug or feature?) + */ +static uint8_t boot_sector[0x7e000] = { + /* The first sector will be placed at RAM address 00007C00, and + * the BIOS transfers control to 00007C00 + */ + + /* Data Segment register should be initialized, since pxe + * boot loader can leave it dirty. + */ + + /* 7c00: move $0000,%ax */ + [0x00] = 0xb8, + [0x01] = 0x00, + [0x02] = 0x00, + /* 7c03: move %ax,%ds */ + [0x03] = 0x8e, + [0x04] = 0xd8, + + /* 7c05: mov $0xdead,%ax */ + [0x05] = 0xb8, + [0x06] = LOW(SIGNATURE), + [0x07] = HIGH(SIGNATURE), + /* 7c08: mov %ax,0x7c10 */ + [0x08] = 0xa3, + [0x09] = LOW(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET), + [0x0a] = HIGH(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET), + + /* 7c0b cli */ + [0x0b] = 0xfa, + /* 7c0c: hlt */ + [0x0c] = 0xf4, + /* 7c0e: jmp 0x7c07=0x7c0f-3 */ + [0x0d] = 0xeb, + [0x0e] = LOW(-3), + /* We mov 0xdead here: set value to make debugging easier */ + [SIGNATURE_OFFSET] = LOW(0xface), + [SIGNATURE_OFFSET + 1] = HIGH(0xface), + /* End of boot sector marker */ + [0x1FE] = 0x55, + [0x1FF] = 0xAA, +}; + +/* Create boot disk file. */ +int boot_sector_init(const char *fname) +{ + FILE *f = fopen(fname, "w"); + + if (!f) { + fprintf(stderr, "Couldn't open \"%s\": %s", fname, strerror(errno)); + return 1; + } + fwrite(boot_sector, 1, sizeof boot_sector, f); + fclose(f); + return 0; +} + +/* Loop until signature in memory is OK. */ +void boot_sector_test(void) +{ + uint8_t signature_low; + uint8_t signature_high; + uint16_t signature; + int i; + + /* Wait at most 1 minute */ +#define TEST_DELAY (1 * G_USEC_PER_SEC / 10) +#define TEST_CYCLES MAX((60 * G_USEC_PER_SEC / TEST_DELAY), 1) + + /* Poll until code has run and modified memory. Once it has we know BIOS + * initialization is done. TODO: check that IP reached the halt + * instruction. + */ + for (i = 0; i < TEST_CYCLES; ++i) { + signature_low = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET); + signature_high = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET + 1); + signature = (signature_high << 8) | signature_low; + if (signature == SIGNATURE) { + break; + } + g_usleep(TEST_DELAY); + } + + g_assert_cmphex(signature, ==, SIGNATURE); +} + +/* unlink boot disk file. */ +void boot_sector_cleanup(const char *fname) +{ + unlink(fname); +} diff --git a/qemu/tests/boot-sector.h b/qemu/tests/boot-sector.h new file mode 100644 index 000000000..38be0290e --- /dev/null +++ b/qemu/tests/boot-sector.h @@ -0,0 +1,26 @@ +/* + * QEMU boot sector testing helpers. + * + * Copyright (c) 2016 Red Hat Inc. + * + * Authors: + * Michael S. Tsirkin + * Victor Kaplansky + * + * 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 TEST_BOOT_SECTOR +#define TEST_BOOT_SECTOR + +/* Create boot disk file. */ +int boot_sector_init(const char *fname); + +/* Loop until signature in memory is OK. */ +void boot_sector_test(void); + +/* unlink boot disk file. */ +void boot_sector_cleanup(const char *fname); + +#endif /* TEST_BOOT_SECTOR */ diff --git a/qemu/tests/check-block.sh b/qemu/tests/check-block.sh index b9d9c6a9f..a37797a49 100755 --- a/qemu/tests/check-block.sh +++ b/qemu/tests/check-block.sh @@ -9,7 +9,7 @@ if [ ! -x $QEMU_PROG ]; then exit 1 fi -cd $SRC_PATH/tests/qemu-iotests +cd tests/qemu-iotests ret=0 ./check -T -nocache -raw || ret=1 diff --git a/qemu/tests/check-qdict.c b/qemu/tests/check-qdict.c index a136f2add..a43056c5d 100644 --- a/qemu/tests/check-qdict.c +++ b/qemu/tests/check-qdict.c @@ -9,6 +9,7 @@ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include #include "qapi/qmp/qint.h" diff --git a/qemu/tests/check-qfloat.c b/qemu/tests/check-qfloat.c index 6404ac8df..3102608f5 100644 --- a/qemu/tests/check-qfloat.c +++ b/qemu/tests/check-qfloat.c @@ -10,6 +10,7 @@ * See the COPYING.LIB file in the top-level directory. * */ +#include "qemu/osdep.h" #include #include "qapi/qmp/qfloat.h" diff --git a/qemu/tests/check-qint.c b/qemu/tests/check-qint.c index 86868844a..c86f7dfa3 100644 --- a/qemu/tests/check-qint.c +++ b/qemu/tests/check-qint.c @@ -9,6 +9,7 @@ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include #include "qapi/qmp/qint.h" diff --git a/qemu/tests/check-qjson.c b/qemu/tests/check-qjson.c index 1cfffa593..99de6f525 100644 --- a/qemu/tests/check-qjson.c +++ b/qemu/tests/check-qjson.c @@ -10,6 +10,7 @@ * See the COPYING.LIB file in the top-level directory. * */ +#include "qemu/osdep.h" #include #include "qapi/qmp/qstring.h" @@ -1484,6 +1485,30 @@ static void unterminated_literal(void) g_assert(obj == NULL); } +static char *make_nest(char *buf, size_t cnt) +{ + memset(buf, '[', cnt - 1); + buf[cnt - 1] = '{'; + buf[cnt] = '}'; + memset(buf + cnt + 1, ']', cnt - 1); + buf[2 * cnt] = 0; + return buf; +} + +static void limits_nesting(void) +{ + enum { max_nesting = 1024 }; /* see qobject/json-streamer.c */ + char buf[2 * (max_nesting + 1) + 1]; + QObject *obj; + + obj = qobject_from_json(make_nest(buf, max_nesting)); + g_assert(obj != NULL); + qobject_decref(obj); + + obj = qobject_from_json(make_nest(buf, max_nesting + 1)); + g_assert(obj == NULL); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -1519,6 +1544,7 @@ int main(int argc, char **argv) g_test_add_func("/errors/invalid_array_comma", invalid_array_comma); g_test_add_func("/errors/invalid_dict_comma", invalid_dict_comma); g_test_add_func("/errors/unterminated/literal", unterminated_literal); + g_test_add_func("/errors/limits/nesting", limits_nesting); return g_test_run(); } diff --git a/qemu/tests/check-qlist.c b/qemu/tests/check-qlist.c index b9c05d43f..f231d5fa9 100644 --- a/qemu/tests/check-qlist.c +++ b/qemu/tests/check-qlist.c @@ -9,6 +9,7 @@ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include #include "qapi/qmp/qint.h" diff --git a/qemu/tests/check-qom-interface.c b/qemu/tests/check-qom-interface.c index f06380ef1..09354deb7 100644 --- a/qemu/tests/check-qom-interface.c +++ b/qemu/tests/check-qom-interface.c @@ -9,6 +9,7 @@ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include #include "qom/object.h" diff --git a/qemu/tests/check-qom-proplist.c b/qemu/tests/check-qom-proplist.c index 7400b1fce..ffffd872f 100644 --- a/qemu/tests/check-qom-proplist.c +++ b/qemu/tests/check-qom-proplist.c @@ -18,8 +18,10 @@ * Author: Daniel P. Berrange */ +#include "qemu/osdep.h" #include +#include "qapi/error.h" #include "qom/object.h" #include "qemu/module.h" @@ -123,18 +125,28 @@ static void dummy_init(Object *obj) dummy_get_bv, dummy_set_bv, NULL); - object_property_add_str(obj, "sv", - dummy_get_sv, - dummy_set_sv, - NULL); - object_property_add_enum(obj, "av", - "DummyAnimal", - dummy_animal_map, - dummy_get_av, - dummy_set_av, - NULL); } + +static void dummy_class_init(ObjectClass *cls, void *data) +{ + object_class_property_add_bool(cls, "bv", + dummy_get_bv, + dummy_set_bv, + NULL); + object_class_property_add_str(cls, "sv", + dummy_get_sv, + dummy_set_sv, + NULL); + object_class_property_add_enum(cls, "av", + "DummyAnimal", + dummy_animal_map, + dummy_get_av, + dummy_set_av, + NULL); +} + + static void dummy_finalize(Object *obj) { DummyObject *dobj = DUMMY_OBJECT(obj); @@ -150,8 +162,151 @@ static const TypeInfo dummy_info = { .instance_init = dummy_init, .instance_finalize = dummy_finalize, .class_size = sizeof(DummyObjectClass), + .class_init = dummy_class_init, +}; + + +/* + * The following 3 object classes are used to + * simulate the kind of relationships seen in + * qdev, which result in complex object + * property destruction ordering. + * + * DummyDev has a 'bus' child to a DummyBus + * DummyBus has a 'backend' child to a DummyBackend + * DummyDev has a 'backend' link to DummyBackend + * + * When DummyDev is finalized, it unparents the + * DummyBackend, which unparents the DummyDev + * which deletes the 'backend' link from DummyDev + * to DummyBackend. This illustrates that the + * object_property_del_all() method needs to + * cope with the list of properties being changed + * while it iterates over them. + */ +typedef struct DummyDev DummyDev; +typedef struct DummyDevClass DummyDevClass; +typedef struct DummyBus DummyBus; +typedef struct DummyBusClass DummyBusClass; +typedef struct DummyBackend DummyBackend; +typedef struct DummyBackendClass DummyBackendClass; + +#define TYPE_DUMMY_DEV "qemu-dummy-dev" +#define TYPE_DUMMY_BUS "qemu-dummy-bus" +#define TYPE_DUMMY_BACKEND "qemu-dummy-backend" + +#define DUMMY_DEV(obj) \ + OBJECT_CHECK(DummyDev, (obj), TYPE_DUMMY_DEV) +#define DUMMY_BUS(obj) \ + OBJECT_CHECK(DummyBus, (obj), TYPE_DUMMY_BUS) +#define DUMMY_BACKEND(obj) \ + OBJECT_CHECK(DummyBackend, (obj), TYPE_DUMMY_BACKEND) + +struct DummyDev { + Object parent_obj; + + DummyBus *bus; +}; + +struct DummyDevClass { + ObjectClass parent_class; +}; + +struct DummyBus { + Object parent_obj; + + DummyBackend *backend; +}; + +struct DummyBusClass { + ObjectClass parent_class; +}; + +struct DummyBackend { + Object parent_obj; +}; + +struct DummyBackendClass { + ObjectClass parent_class; +}; + + +static void dummy_dev_init(Object *obj) +{ + DummyDev *dev = DUMMY_DEV(obj); + DummyBus *bus = DUMMY_BUS(object_new(TYPE_DUMMY_BUS)); + DummyBackend *backend = DUMMY_BACKEND(object_new(TYPE_DUMMY_BACKEND)); + + object_property_add_child(obj, "bus", OBJECT(bus), NULL); + dev->bus = bus; + object_property_add_child(OBJECT(bus), "backend", OBJECT(backend), NULL); + bus->backend = backend; + + object_property_add_link(obj, "backend", TYPE_DUMMY_BACKEND, + (Object **)&bus->backend, NULL, 0, NULL); +} + +static void dummy_dev_unparent(Object *obj) +{ + DummyDev *dev = DUMMY_DEV(obj); + object_unparent(OBJECT(dev->bus)); +} + +static void dummy_dev_class_init(ObjectClass *klass, void *opaque) +{ + klass->unparent = dummy_dev_unparent; +} + + +static void dummy_bus_init(Object *obj) +{ +} + +static void dummy_bus_unparent(Object *obj) +{ + DummyBus *bus = DUMMY_BUS(obj); + object_property_del(obj->parent, "backend", NULL); + object_unparent(OBJECT(bus->backend)); +} + +static void dummy_bus_class_init(ObjectClass *klass, void *opaque) +{ + klass->unparent = dummy_bus_unparent; +} + +static void dummy_backend_init(Object *obj) +{ +} + + +static const TypeInfo dummy_dev_info = { + .name = TYPE_DUMMY_DEV, + .parent = TYPE_OBJECT, + .instance_size = sizeof(DummyDev), + .instance_init = dummy_dev_init, + .class_size = sizeof(DummyDevClass), + .class_init = dummy_dev_class_init, +}; + +static const TypeInfo dummy_bus_info = { + .name = TYPE_DUMMY_BUS, + .parent = TYPE_OBJECT, + .instance_size = sizeof(DummyBus), + .instance_init = dummy_bus_init, + .class_size = sizeof(DummyBusClass), + .class_init = dummy_bus_class_init, +}; + +static const TypeInfo dummy_backend_info = { + .name = TYPE_DUMMY_BACKEND, + .parent = TYPE_OBJECT, + .instance_size = sizeof(DummyBackend), + .instance_init = dummy_backend_init, + .class_size = sizeof(DummyBackendClass), }; + + static void test_dummy_createv(void) { Error *err = NULL; @@ -283,20 +438,82 @@ static void test_dummy_getenum(void) &err); g_assert(err != NULL); error_free(err); + + object_unparent(OBJECT(dobj)); } +static void test_dummy_iterator(void) +{ + Object *parent = object_get_objects_root(); + DummyObject *dobj = DUMMY_OBJECT( + object_new_with_props(TYPE_DUMMY, + parent, + "dummy0", + &error_abort, + "bv", "yes", + "sv", "Hiss hiss hiss", + "av", "platypus", + NULL)); + + ObjectProperty *prop; + ObjectPropertyIterator iter; + bool seenbv = false, seensv = false, seenav = false, seentype; + + object_property_iter_init(&iter, OBJECT(dobj)); + while ((prop = object_property_iter_next(&iter))) { + if (g_str_equal(prop->name, "bv")) { + seenbv = true; + } else if (g_str_equal(prop->name, "sv")) { + seensv = true; + } else if (g_str_equal(prop->name, "av")) { + seenav = true; + } else if (g_str_equal(prop->name, "type")) { + /* This prop comes from the base Object class */ + seentype = true; + } else { + g_printerr("Found prop '%s'\n", prop->name); + g_assert_not_reached(); + } + } + g_assert(seenbv); + g_assert(seenav); + g_assert(seensv); + g_assert(seentype); + + object_unparent(OBJECT(dobj)); +} + + +static void test_dummy_delchild(void) +{ + Object *parent = object_get_objects_root(); + DummyDev *dev = DUMMY_DEV( + object_new_with_props(TYPE_DUMMY_DEV, + parent, + "dev0", + &error_abort, + NULL)); + + object_unparent(OBJECT(dev)); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); module_call_init(MODULE_INIT_QOM); type_register_static(&dummy_info); + type_register_static(&dummy_dev_info); + type_register_static(&dummy_bus_info); + type_register_static(&dummy_backend_info); g_test_add_func("/qom/proplist/createlist", test_dummy_createlist); g_test_add_func("/qom/proplist/createv", test_dummy_createv); g_test_add_func("/qom/proplist/badenum", test_dummy_badenum); g_test_add_func("/qom/proplist/getenum", test_dummy_getenum); + g_test_add_func("/qom/proplist/iterator", test_dummy_iterator); + g_test_add_func("/qom/proplist/delchild", test_dummy_delchild); return g_test_run(); } diff --git a/qemu/tests/check-qstring.c b/qemu/tests/check-qstring.c index 95dc9e3e7..9877b42c8 100644 --- a/qemu/tests/check-qstring.c +++ b/qemu/tests/check-qstring.c @@ -9,6 +9,7 @@ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include #include "qapi/qmp/qstring.h" diff --git a/qemu/tests/crypto-tls-x509-helpers.c b/qemu/tests/crypto-tls-x509-helpers.c new file mode 100644 index 000000000..64073d3bd --- /dev/null +++ b/qemu/tests/crypto-tls-x509-helpers.c @@ -0,0 +1,485 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Daniel P. Berrange + */ + +#include "qemu/osdep.h" + +#include "crypto-tls-x509-helpers.h" +#include "qemu/sockets.h" + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT + +/* + * This stores some static data that is needed when + * encoding extensions in the x509 certs + */ +ASN1_TYPE pkix_asn1; + +/* + * To avoid consuming random entropy to generate keys, + * here's one we prepared earlier :-) + */ +gnutls_x509_privkey_t privkey; +# define PRIVATE_KEY \ + "-----BEGIN PRIVATE KEY-----\n" \ + "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALVcr\n" \ + "BL40Tm6yq88FBhJNw1aaoCjmtg0l4dWQZ/e9Fimx4ARxFpT+ji4FE\n" \ + "Cgl9s/SGqC+1nvlkm9ViSo0j7MKDbnDB+VRHDvMAzQhA2X7e8M0n9\n" \ + "rPolUY2lIVC83q0BBaOBkCj2RSmT2xTEbbC2xLukSrg2WP/ihVOxc\n" \ + "kXRuyFtzAgMBAAECgYB7slBexDwXrtItAMIH6m/U+LUpNe0Xx48OL\n" \ + "IOn4a4whNgO/o84uIwygUK27ZGFZT0kAGAk8CdF9hA6ArcbQ62s1H\n" \ + "myxrUbF9/mrLsQw1NEqpuUk9Ay2Tx5U/wPx35S3W/X2AvR/ZpTnCn\n" \ + "2q/7ym9fyiSoj86drD7BTvmKXlOnOwQJBAPOFMp4mMa9NGpGuEssO\n" \ + "m3Uwbp6lhcP0cA9MK+iOmeANpoKWfBdk5O34VbmeXnGYWEkrnX+9J\n" \ + "bM4wVhnnBWtgBMCQQC+qAEmvwcfhauERKYznMVUVksyeuhxhCe7EK\n" \ + "mPh+U2+g0WwdKvGDgO0PPt1gq0ILEjspMDeMHVdTwkaVBo/uMhAkA\n" \ + "Z5SsZyCP2aTOPFDypXRdI4eqRcjaEPOUBq27r3uYb/jeboVb2weLa\n" \ + "L1MmVuHiIHoa5clswPdWVI2y0em2IGoDAkBPSp/v9VKJEZabk9Frd\n" \ + "a+7u4fanrM9QrEjY3KhduslSilXZZSxrWjjAJPyPiqFb3M8XXA26W\n" \ + "nz1KYGnqYKhLcBAkB7dt57n9xfrhDpuyVEv+Uv1D3VVAhZlsaZ5Pp\n" \ + "dcrhrkJn2sa/+O8OKvdrPSeeu/N5WwYhJf61+CPoenMp7IFci\n" \ + "-----END PRIVATE KEY-----\n" + +/* + * This loads the private key we defined earlier + */ +static gnutls_x509_privkey_t test_tls_load_key(void) +{ + gnutls_x509_privkey_t key; + const gnutls_datum_t data = { (unsigned char *)PRIVATE_KEY, + strlen(PRIVATE_KEY) }; + int err; + + err = gnutls_x509_privkey_init(&key); + if (err < 0) { + g_critical("Failed to init key %s", gnutls_strerror(err)); + abort(); + } + + err = gnutls_x509_privkey_import(key, &data, + GNUTLS_X509_FMT_PEM); + if (err < 0) { + if (err != GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR && + err != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + g_critical("Failed to import key %s", gnutls_strerror(err)); + abort(); + } + + err = gnutls_x509_privkey_import_pkcs8( + key, &data, GNUTLS_X509_FMT_PEM, NULL, 0); + if (err < 0) { + g_critical("Failed to import PKCS8 key %s", gnutls_strerror(err)); + abort(); + } + } + + return key; +} + + +void test_tls_init(const char *keyfile) +{ + gnutls_global_init(); + + if (asn1_array2tree(pkix_asn1_tab, &pkix_asn1, NULL) != ASN1_SUCCESS) { + abort(); + } + + privkey = test_tls_load_key(); + if (!g_file_set_contents(keyfile, PRIVATE_KEY, -1, NULL)) { + abort(); + } +} + + +void test_tls_cleanup(const char *keyfile) +{ + asn1_delete_structure(&pkix_asn1); + unlink(keyfile); +} + +/* + * Turns an ASN1 object into a DER encoded byte array + */ +static void test_tls_der_encode(ASN1_TYPE src, + const char *src_name, + gnutls_datum_t *res) +{ + int size; + char *data = NULL; + + size = 0; + asn1_der_coding(src, src_name, NULL, &size, NULL); + + data = g_new0(char, size); + + asn1_der_coding(src, src_name, data, &size, NULL); + + res->data = (unsigned char *)data; + res->size = size; +} + + +static void +test_tls_get_ipaddr(const char *addrstr, + char **data, + int *datalen) +{ + struct addrinfo *res; + struct addrinfo hints; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + g_assert(getaddrinfo(addrstr, NULL, &hints, &res) == 0); + + *datalen = res->ai_addrlen; + *data = g_new(char, *datalen); + memcpy(*data, res->ai_addr, *datalen); + freeaddrinfo(res); +} + +/* + * This is a fairly lame x509 certificate generator. + * + * Do not copy/use this code for generating real certificates + * since it leaves out many things that you would want in + * certificates for real world usage. + * + * This is good enough only for doing tests of the QEMU + * TLS certificate code + */ +void +test_tls_generate_cert(QCryptoTLSTestCertReq *req, + gnutls_x509_crt_t ca) +{ + gnutls_x509_crt_t crt; + int err; + static char buffer[1024 * 1024]; + size_t size = sizeof(buffer); + char serial[5] = { 1, 2, 3, 4, 0 }; + gnutls_datum_t der; + time_t start = time(NULL) + (60 * 60 * req->start_offset); + time_t expire = time(NULL) + (60 * 60 * (req->expire_offset + ? req->expire_offset : 24)); + + /* + * Prepare our new certificate object + */ + err = gnutls_x509_crt_init(&crt); + if (err < 0) { + g_critical("Failed to initialize certificate %s", gnutls_strerror(err)); + abort(); + } + err = gnutls_x509_crt_set_key(crt, privkey); + if (err < 0) { + g_critical("Failed to set certificate key %s", gnutls_strerror(err)); + abort(); + } + + /* + * A v3 certificate is required in order to be able + * set any of the basic constraints, key purpose and + * key usage data + */ + gnutls_x509_crt_set_version(crt, 3); + + if (req->country) { + err = gnutls_x509_crt_set_dn_by_oid( + crt, GNUTLS_OID_X520_COUNTRY_NAME, 0, + req->country, strlen(req->country)); + if (err < 0) { + g_critical("Failed to set certificate country name %s", + gnutls_strerror(err)); + abort(); + } + } + if (req->cn) { + err = gnutls_x509_crt_set_dn_by_oid( + crt, GNUTLS_OID_X520_COMMON_NAME, 0, + req->cn, strlen(req->cn)); + if (err < 0) { + g_critical("Failed to set certificate common name %s", + gnutls_strerror(err)); + abort(); + } + } + + /* + * Setup the subject altnames, which are used + * for hostname checks in live sessions + */ + if (req->altname1) { + err = gnutls_x509_crt_set_subject_alt_name( + crt, GNUTLS_SAN_DNSNAME, + req->altname1, + strlen(req->altname1), + GNUTLS_FSAN_APPEND); + if (err < 0) { + g_critical("Failed to set certificate alt name %s", + gnutls_strerror(err)); + abort(); + } + } + if (req->altname2) { + err = gnutls_x509_crt_set_subject_alt_name( + crt, GNUTLS_SAN_DNSNAME, + req->altname2, + strlen(req->altname2), + GNUTLS_FSAN_APPEND); + if (err < 0) { + g_critical("Failed to set certificate %s alt name", + gnutls_strerror(err)); + abort(); + } + } + + /* + * IP address need to be put into the cert in their + * raw byte form, not strings, hence this is a little + * more complicated + */ + if (req->ipaddr1) { + char *data; + int len; + + test_tls_get_ipaddr(req->ipaddr1, &data, &len); + + err = gnutls_x509_crt_set_subject_alt_name( + crt, GNUTLS_SAN_IPADDRESS, + data, len, GNUTLS_FSAN_APPEND); + if (err < 0) { + g_critical("Failed to set certificate alt name %s", + gnutls_strerror(err)); + abort(); + } + g_free(data); + } + if (req->ipaddr2) { + char *data; + int len; + + test_tls_get_ipaddr(req->ipaddr2, &data, &len); + + err = gnutls_x509_crt_set_subject_alt_name( + crt, GNUTLS_SAN_IPADDRESS, + data, len, GNUTLS_FSAN_APPEND); + if (err < 0) { + g_critical("Failed to set certificate alt name %s", + gnutls_strerror(err)); + abort(); + } + g_free(data); + } + + + /* + * Basic constraints are used to decide if the cert + * is for a CA or not. We can't use the convenient + * gnutls API for setting this, since it hardcodes + * the 'critical' field which we want control over + */ + if (req->basicConstraintsEnable) { + ASN1_TYPE ext = ASN1_TYPE_EMPTY; + + asn1_create_element(pkix_asn1, "PKIX1.BasicConstraints", &ext); + asn1_write_value(ext, "cA", + req->basicConstraintsIsCA ? "TRUE" : "FALSE", 1); + asn1_write_value(ext, "pathLenConstraint", NULL, 0); + test_tls_der_encode(ext, "", &der); + err = gnutls_x509_crt_set_extension_by_oid( + crt, "2.5.29.19", + der.data, der.size, + req->basicConstraintsCritical); + if (err < 0) { + g_critical("Failed to set certificate basic constraints %s", + gnutls_strerror(err)); + g_free(der.data); + abort(); + } + asn1_delete_structure(&ext); + g_free(der.data); + } + + /* + * Next up the key usage extension. Again we can't + * use the gnutls API since it hardcodes the extension + * to be 'critical' + */ + if (req->keyUsageEnable) { + ASN1_TYPE ext = ASN1_TYPE_EMPTY; + char str[2]; + + str[0] = req->keyUsageValue & 0xff; + str[1] = (req->keyUsageValue >> 8) & 0xff; + + asn1_create_element(pkix_asn1, "PKIX1.KeyUsage", &ext); + asn1_write_value(ext, "", str, 9); + test_tls_der_encode(ext, "", &der); + err = gnutls_x509_crt_set_extension_by_oid( + crt, "2.5.29.15", + der.data, der.size, + req->keyUsageCritical); + if (err < 0) { + g_critical("Failed to set certificate key usage %s", + gnutls_strerror(err)); + g_free(der.data); + abort(); + } + asn1_delete_structure(&ext); + g_free(der.data); + } + + /* + * Finally the key purpose extension. This time + * gnutls has the opposite problem, always hardcoding + * it to be non-critical. So once again we have to + * set this the hard way building up ASN1 data ourselves + */ + if (req->keyPurposeEnable) { + ASN1_TYPE ext = ASN1_TYPE_EMPTY; + + asn1_create_element(pkix_asn1, "PKIX1.ExtKeyUsageSyntax", &ext); + if (req->keyPurposeOID1) { + asn1_write_value(ext, "", "NEW", 1); + asn1_write_value(ext, "?LAST", req->keyPurposeOID1, 1); + } + if (req->keyPurposeOID2) { + asn1_write_value(ext, "", "NEW", 1); + asn1_write_value(ext, "?LAST", req->keyPurposeOID2, 1); + } + test_tls_der_encode(ext, "", &der); + err = gnutls_x509_crt_set_extension_by_oid( + crt, "2.5.29.37", + der.data, der.size, + req->keyPurposeCritical); + if (err < 0) { + g_critical("Failed to set certificate key purpose %s", + gnutls_strerror(err)); + g_free(der.data); + abort(); + } + asn1_delete_structure(&ext); + g_free(der.data); + } + + /* + * Any old serial number will do, so lets pick 5 + */ + err = gnutls_x509_crt_set_serial(crt, serial, 5); + if (err < 0) { + g_critical("Failed to set certificate serial %s", + gnutls_strerror(err)); + abort(); + } + + err = gnutls_x509_crt_set_activation_time(crt, start); + if (err < 0) { + g_critical("Failed to set certificate activation %s", + gnutls_strerror(err)); + abort(); + } + err = gnutls_x509_crt_set_expiration_time(crt, expire); + if (err < 0) { + g_critical("Failed to set certificate expiration %s", + gnutls_strerror(err)); + abort(); + } + + + /* + * If no 'ca' is set then we are self signing + * the cert. This is done for the root CA certs + */ + err = gnutls_x509_crt_sign(crt, ca ? ca : crt, privkey); + if (err < 0) { + g_critical("Failed to sign certificate %s", + gnutls_strerror(err)); + abort(); + } + + /* + * Finally write the new cert out to disk + */ + err = gnutls_x509_crt_export( + crt, GNUTLS_X509_FMT_PEM, buffer, &size); + if (err < 0) { + g_critical("Failed to export certificate %s: %d", + gnutls_strerror(err), err); + abort(); + } + + if (!g_file_set_contents(req->filename, buffer, -1, NULL)) { + g_critical("Failed to write certificate %s", + req->filename); + abort(); + } + + req->crt = crt; +} + + +void test_tls_write_cert_chain(const char *filename, + gnutls_x509_crt_t *certs, + size_t ncerts) +{ + size_t i; + size_t capacity = 1024, offset = 0; + char *buffer = g_new0(char, capacity); + int err; + + for (i = 0; i < ncerts; i++) { + size_t len = capacity - offset; + retry: + err = gnutls_x509_crt_export(certs[i], GNUTLS_X509_FMT_PEM, + buffer + offset, &len); + if (err < 0) { + if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) { + buffer = g_renew(char, buffer, offset + len); + capacity = offset + len; + goto retry; + } + g_critical("Failed to export certificate chain %s: %d", + gnutls_strerror(err), err); + abort(); + } + offset += len; + } + + if (!g_file_set_contents(filename, buffer, offset, NULL)) { + abort(); + } + g_free(buffer); +} + + +void test_tls_discard_cert(QCryptoTLSTestCertReq *req) +{ + if (!req->crt) { + return; + } + + gnutls_x509_crt_deinit(req->crt); + req->crt = NULL; + + if (getenv("QEMU_TEST_DEBUG_CERTS") == NULL) { + unlink(req->filename); + } +} + +#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/qemu/tests/crypto-tls-x509-helpers.h b/qemu/tests/crypto-tls-x509-helpers.h new file mode 100644 index 000000000..356b49cd5 --- /dev/null +++ b/qemu/tests/crypto-tls-x509-helpers.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Daniel P. Berrange + */ + +#include +#include + +#include +#include + +#if !(defined WIN32) && \ + defined(CONFIG_TASN1) && \ + defined(LIBGNUTLS_VERSION_NUMBER) && \ + (LIBGNUTLS_VERSION_NUMBER >= 0x020600) +# define QCRYPTO_HAVE_TLS_TEST_SUPPORT +#endif + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT +# include + +# include "qemu-common.h" + +/* + * This contains parameter about how to generate + * certificates. + */ +typedef struct QCryptoTLSTestCertReq QCryptoTLSTestCertReq; +struct QCryptoTLSTestCertReq { + gnutls_x509_crt_t crt; + + const char *filename; + + /* Identifying information */ + const char *country; + const char *cn; + const char *altname1; + const char *altname2; + const char *ipaddr1; + const char *ipaddr2; + + /* Basic constraints */ + bool basicConstraintsEnable; + bool basicConstraintsCritical; + bool basicConstraintsIsCA; + + /* Key usage */ + bool keyUsageEnable; + bool keyUsageCritical; + int keyUsageValue; + + /* Key purpose (aka Extended key usage) */ + bool keyPurposeEnable; + bool keyPurposeCritical; + const char *keyPurposeOID1; + const char *keyPurposeOID2; + + /* zero for current time, or non-zero for hours from now */ + int start_offset; + /* zero for 24 hours from now, or non-zero for hours from now */ + int expire_offset; +}; + +void test_tls_generate_cert(QCryptoTLSTestCertReq *req, + gnutls_x509_crt_t ca); +void test_tls_write_cert_chain(const char *filename, + gnutls_x509_crt_t *certs, + size_t ncerts); +void test_tls_discard_cert(QCryptoTLSTestCertReq *req); + +void test_tls_init(const char *keyfile); +void test_tls_cleanup(const char *keyfile); + +# define TLS_CERT_REQ(varname, cavarname, \ + country, commonname, \ + altname1, altname2, \ + ipaddr1, ipaddr2, \ + basicconsenable, basicconscritical, basicconsca, \ + keyusageenable, keyusagecritical, keyusagevalue, \ + keypurposeenable, keypurposecritical, \ + keypurposeoid1, keypurposeoid2, \ + startoffset, endoffset) \ + static QCryptoTLSTestCertReq varname = { \ + NULL, WORKDIR #varname "-ctx.pem", \ + country, commonname, altname1, altname2, \ + ipaddr1, ipaddr2, \ + basicconsenable, basicconscritical, basicconsca, \ + keyusageenable, keyusagecritical, keyusagevalue, \ + keypurposeenable, keypurposecritical, \ + keypurposeoid1, keypurposeoid2, \ + startoffset, endoffset \ + }; \ + test_tls_generate_cert(&varname, cavarname.crt) + +# define TLS_ROOT_REQ(varname, \ + country, commonname, \ + altname1, altname2, \ + ipaddr1, ipaddr2, \ + basicconsenable, basicconscritical, basicconsca, \ + keyusageenable, keyusagecritical, keyusagevalue, \ + keypurposeenable, keypurposecritical, \ + keypurposeoid1, keypurposeoid2, \ + startoffset, endoffset) \ + static QCryptoTLSTestCertReq varname = { \ + NULL, WORKDIR #varname "-ctx.pem", \ + country, commonname, altname1, altname2, \ + ipaddr1, ipaddr2, \ + basicconsenable, basicconscritical, basicconsca, \ + keyusageenable, keyusagecritical, keyusagevalue, \ + keypurposeenable, keypurposecritical, \ + keypurposeoid1, keypurposeoid2, \ + startoffset, endoffset \ + }; \ + test_tls_generate_cert(&varname, NULL) + +extern const ASN1_ARRAY_TYPE pkix_asn1_tab[]; + +#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/qemu/tests/device-introspect-test.c b/qemu/tests/device-introspect-test.c new file mode 100644 index 000000000..447792601 --- /dev/null +++ b/qemu/tests/device-introspect-test.c @@ -0,0 +1,124 @@ +/* + * Device introspection test cases + * + * Copyright (c) 2015 Red Hat Inc. + * + * Authors: + * Markus Armbruster , + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* + * Covers QMP device-list-properties and HMP device_add help. We + * currently don't check that their output makes sense, only that QEMU + * survives. Useful since we've had an astounding number of crash + * bugs around here. + */ + +#include "qemu/osdep.h" +#include +#include "qemu-common.h" +#include "qapi/qmp/qstring.h" +#include "libqtest.h" + +const char common_args[] = "-nodefaults -machine none"; + +static QList *device_type_list(bool abstract) +{ + QDict *resp; + QList *ret; + + resp = qmp("{'execute': 'qom-list-types'," + " 'arguments': {'implements': 'device', 'abstract': %i}}", + abstract); + g_assert(qdict_haskey(resp, "return")); + ret = qdict_get_qlist(resp, "return"); + QINCREF(ret); + QDECREF(resp); + return ret; +} + +static void test_one_device(const char *type) +{ + QDict *resp; + char *help, *qom_tree; + + resp = qmp("{'execute': 'device-list-properties'," + " 'arguments': {'typename': %s}}", + type); + QDECREF(resp); + + help = hmp("device_add \"%s,help\"", type); + g_free(help); + + /* + * Some devices leave dangling pointers in QOM behind. + * "info qom-tree" has a good chance at crashing then + */ + qom_tree = hmp("info qom-tree"); + g_free(qom_tree); +} + +static void test_device_intro_list(void) +{ + QList *types; + char *help; + + qtest_start(common_args); + + types = device_type_list(true); + QDECREF(types); + + help = hmp("device_add help"); + g_free(help); + + qtest_end(); +} + +static void test_device_intro_none(void) +{ + qtest_start(common_args); + test_one_device("nonexistent"); + qtest_end(); +} + +static void test_device_intro_abstract(void) +{ + qtest_start(common_args); + test_one_device("device"); + qtest_end(); +} + +static void test_device_intro_concrete(void) +{ + QList *types; + QListEntry *entry; + const char *type; + + qtest_start(common_args); + types = device_type_list(false); + + QLIST_FOREACH_ENTRY(types, entry) { + type = qdict_get_try_str(qobject_to_qdict(qlist_entry_obj(entry)), + "name"); + g_assert(type); + test_one_device(type); + } + + QDECREF(types); + qtest_end(); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("device/introspect/list", test_device_intro_list); + qtest_add_func("device/introspect/none", test_device_intro_none); + qtest_add_func("device/introspect/abstract", test_device_intro_abstract); + qtest_add_func("device/introspect/concrete", test_device_intro_concrete); + + return g_test_run(); +} diff --git a/qemu/tests/display-vga-test.c b/qemu/tests/display-vga-test.c index 7694344ea..5706d338a 100644 --- a/qemu/tests/display-vga-test.c +++ b/qemu/tests/display-vga-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" static void pci_cirrus(void) { diff --git a/qemu/tests/drive_del-test.c b/qemu/tests/drive_del-test.c index 8951f6f61..fe03236f3 100644 --- a/qemu/tests/drive_del-test.c +++ b/qemu/tests/drive_del-test.c @@ -10,34 +10,24 @@ * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" static void drive_add(void) { - QDict *response; + char *resp = hmp("drive_add 0 if=none,id=drive0"); - response = qmp("{'execute': 'human-monitor-command'," - " 'arguments': {" - " 'command-line': 'drive_add 0 if=none,id=drive0'" - "}}"); - g_assert(response); - g_assert_cmpstr(qdict_get_try_str(response, "return"), ==, "OK\r\n"); - QDECREF(response); + g_assert_cmpstr(resp, ==, "OK\r\n"); + g_free(resp); } static void drive_del(void) { - QDict *response; + char *resp = hmp("drive_del drive0"); - response = qmp("{'execute': 'human-monitor-command'," - " 'arguments': {" - " 'command-line': 'drive_del drive0'" - "}}"); - g_assert(response); - g_assert_cmpstr(qdict_get_try_str(response, "return"), ==, ""); - QDECREF(response); + g_assert_cmpstr(resp, ==, ""); + g_free(resp); } static void device_del(void) diff --git a/qemu/tests/ds1338-test.c b/qemu/tests/ds1338-test.c new file mode 100644 index 000000000..279241584 --- /dev/null +++ b/qemu/tests/ds1338-test.c @@ -0,0 +1,79 @@ +/* + * QTest testcase for the DS1338 RTC + * + * Copyright (c) 2013 Jean-Christophe Dubois + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/i2c.h" + +#include + +#define IMX25_I2C_0_BASE 0x43F80000 + +#define DS1338_ADDR 0x68 + +static I2CAdapter *i2c; +static uint8_t addr; + +static inline uint8_t bcd2bin(uint8_t x) +{ + return ((x) & 0x0f) + ((x) >> 4) * 10; +} + +static void send_and_receive(void) +{ + uint8_t cmd[1]; + uint8_t resp[7]; + time_t now = time(NULL); + struct tm *tm_ptr = gmtime(&now); + + /* reset the index in the RTC memory */ + cmd[0] = 0; + i2c_send(i2c, addr, cmd, 1); + + /* retrieve the date */ + i2c_recv(i2c, addr, resp, 7); + + /* check retrieved time againt local time */ + g_assert_cmpuint(bcd2bin(resp[4]), == , tm_ptr->tm_mday); + g_assert_cmpuint(bcd2bin(resp[5]), == , 1 + tm_ptr->tm_mon); + g_assert_cmpuint(2000 + bcd2bin(resp[6]), == , 1900 + tm_ptr->tm_year); +} + +int main(int argc, char **argv) +{ + QTestState *s = NULL; + int ret; + + g_test_init(&argc, &argv, NULL); + + s = qtest_start("-display none -machine imx25-pdk"); + i2c = imx_i2c_create(IMX25_I2C_0_BASE); + addr = DS1338_ADDR; + + qtest_add_func("/ds1338/tx-rx", send_and_receive); + + ret = g_test_run(); + + if (s) { + qtest_quit(s); + } + g_free(i2c); + + return ret; +} diff --git a/qemu/tests/e1000-test.c b/qemu/tests/e1000-test.c index 7ca6d7e72..a42b3810c 100644 --- a/qemu/tests/e1000-test.c +++ b/qemu/tests/e1000-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void test_device(gconstpointer data) diff --git a/qemu/tests/eepro100-test.c b/qemu/tests/eepro100-test.c index 8bfaccdcb..e17eed0b7 100644 --- a/qemu/tests/eepro100-test.c +++ b/qemu/tests/eepro100-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" static void test_device(gconstpointer data) { diff --git a/qemu/tests/endianness-test.c b/qemu/tests/endianness-test.c index 2054338e1..cc5bccd88 100644 --- a/qemu/tests/endianness-test.c +++ b/qemu/tests/endianness-test.c @@ -11,11 +11,8 @@ * */ +#include "qemu/osdep.h" #include -#include -#include -#include -#include #include "libqtest.h" #include "qemu/bswap.h" diff --git a/qemu/tests/es1370-test.c b/qemu/tests/es1370-test.c index cc23fb5c6..824dc31c6 100644 --- a/qemu/tests/es1370-test.c +++ b/qemu/tests/es1370-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void nop(void) diff --git a/qemu/tests/fdc-test.c b/qemu/tests/fdc-test.c index 416394fc7..53df1d0d8 100644 --- a/qemu/tests/fdc-test.c +++ b/qemu/tests/fdc-test.c @@ -22,9 +22,7 @@ * THE SOFTWARE. */ -#include -#include -#include +#include "qemu/osdep.h" #include @@ -267,7 +265,7 @@ static void test_cmos(void) uint8_t cmos; cmos = cmos_read(CMOS_FLOPPY); - g_assert(cmos == 0x40); + g_assert(cmos == 0x40 || cmos == 0x50); } static void test_no_media_on_start(void) @@ -304,9 +302,6 @@ static void test_media_insert(void) qmp_discard_response("{'execute':'change', 'arguments':{" " 'device':'floppy0', 'target': %s, 'arg': 'raw' }}", test_image); - qmp_discard_response(""); /* ignore event - (FIXME open -> open transition?!) */ - qmp_discard_response(""); /* ignore event */ dir = inb(FLOPPY_BASE + reg_dir); assert_bit_set(dir, DSKCHG); @@ -337,7 +332,6 @@ static void test_media_change(void) * reset the bit. */ qmp_discard_response("{'execute':'eject', 'arguments':{" " 'device':'floppy0' }}"); - qmp_discard_response(""); /* ignore event */ dir = inb(FLOPPY_BASE + reg_dir); assert_bit_set(dir, DSKCHG); diff --git a/qemu/tests/fw_cfg-test.c b/qemu/tests/fw_cfg-test.c index 9be78e956..b4392c2d3 100644 --- a/qemu/tests/fw_cfg-test.c +++ b/qemu/tests/fw_cfg-test.c @@ -10,12 +10,11 @@ * See the COPYING file in the top-level directory. */ -#include +#include "qemu/osdep.h" #include #include "libqtest.h" -#define NO_QEMU_PROTOS -#include "hw/nvram/fw_cfg.h" +#include "hw/nvram/fw_cfg_keys.h" #include "libqos/fw_cfg.h" static uint64_t ram_size = 128 << 20; @@ -37,7 +36,9 @@ static void test_fw_cfg_signature(void) static void test_fw_cfg_id(void) { - g_assert_cmpint(qfw_cfg_get_u32(fw_cfg, FW_CFG_ID), ==, 1); + uint32_t id = qfw_cfg_get_u32(fw_cfg, FW_CFG_ID); + g_assert((id == 1) || + (id == 3)); } static void test_fw_cfg_uuid(void) diff --git a/qemu/tests/guest-debug/test-gdbstub.py b/qemu/tests/guest-debug/test-gdbstub.py new file mode 100644 index 000000000..31ba6c943 --- /dev/null +++ b/qemu/tests/guest-debug/test-gdbstub.py @@ -0,0 +1,176 @@ +# +# This script needs to be run on startup +# qemu -kernel ${KERNEL} -s -S +# and then: +# gdb ${KERNEL}.vmlinux -x ${QEMU_SRC}/tests/guest-debug/test-gdbstub.py + +import gdb + +failcount = 0 + + +def report(cond, msg): + "Report success/fail of test" + if cond: + print ("PASS: %s" % (msg)) + else: + print ("FAIL: %s" % (msg)) + failcount += 1 + + +def check_step(): + "Step an instruction, check it moved." + start_pc = gdb.parse_and_eval('$pc') + gdb.execute("si") + end_pc = gdb.parse_and_eval('$pc') + + return not (start_pc == end_pc) + + +def check_break(sym_name): + "Setup breakpoint, continue and check we stopped." + sym, ok = gdb.lookup_symbol(sym_name) + bp = gdb.Breakpoint(sym_name) + + gdb.execute("c") + + # hopefully we came back + end_pc = gdb.parse_and_eval('$pc') + print ("%s == %s %d" % (end_pc, sym.value(), bp.hit_count)) + bp.delete() + + # can we test we hit bp? + return end_pc == sym.value() + + +# We need to do hbreak manually as the python interface doesn't export it +def check_hbreak(sym_name): + "Setup hardware breakpoint, continue and check we stopped." + sym, ok = gdb.lookup_symbol(sym_name) + gdb.execute("hbreak %s" % (sym_name)) + gdb.execute("c") + + # hopefully we came back + end_pc = gdb.parse_and_eval('$pc') + print ("%s == %s" % (end_pc, sym.value())) + + if end_pc == sym.value(): + gdb.execute("d 1") + return True + else: + return False + + +class WatchPoint(gdb.Breakpoint): + + def get_wpstr(self, sym_name): + "Setup sym and wp_str for given symbol." + self.sym, ok = gdb.lookup_symbol(sym_name) + wp_addr = gdb.parse_and_eval(sym_name).address + self.wp_str = '*(%(type)s)(&%(address)s)' % dict( + type = wp_addr.type, address = sym_name) + + return(self.wp_str) + + def __init__(self, sym_name, type): + wp_str = self.get_wpstr(sym_name) + super(WatchPoint, self).__init__(wp_str, gdb.BP_WATCHPOINT, type) + + def stop(self): + end_pc = gdb.parse_and_eval('$pc') + print ("HIT WP @ %s" % (end_pc)) + return True + + +def do_one_watch(sym, wtype, text): + + wp = WatchPoint(sym, wtype) + gdb.execute("c") + report_str = "%s for %s (%s)" % (text, sym, wp.sym.value()) + + if wp.hit_count > 0: + report(True, report_str) + wp.delete() + else: + report(False, report_str) + + +def check_watches(sym_name): + "Watch a symbol for any access." + + # Should hit for any read + do_one_watch(sym_name, gdb.WP_ACCESS, "awatch") + + # Again should hit for reads + do_one_watch(sym_name, gdb.WP_READ, "rwatch") + + # Finally when it is written + do_one_watch(sym_name, gdb.WP_WRITE, "watch") + + +class CatchBreakpoint(gdb.Breakpoint): + def __init__(self, sym_name): + super(CatchBreakpoint, self).__init__(sym_name) + self.sym, ok = gdb.lookup_symbol(sym_name) + + def stop(self): + end_pc = gdb.parse_and_eval('$pc') + print ("CB: %s == %s" % (end_pc, self.sym.value())) + if end_pc == self.sym.value(): + report(False, "Hit final catchpoint") + + +def run_test(): + "Run throught the tests one by one" + + print ("Checking we can step the first few instructions") + step_ok = 0 + for i in range(3): + if check_step(): + step_ok += 1 + + report(step_ok == 3, "single step in boot code") + + print ("Checking HW breakpoint works") + break_ok = check_hbreak("kernel_init") + report(break_ok, "hbreak @ kernel_init") + + # Can't set this up until we are in the kernel proper + # if we make it to run_init_process we've over-run and + # one of the tests failed + print ("Setup catch-all for run_init_process") + cbp = CatchBreakpoint("run_init_process") + cpb2 = CatchBreakpoint("try_to_run_init_process") + + print ("Checking Normal breakpoint works") + break_ok = check_break("wait_for_completion") + report(break_ok, "break @ wait_for_completion") + + print ("Checking watchpoint works") + check_watches("system_state") + +# +# This runs as the script it sourced (via -x) +# + +try: + print ("Connecting to remote") + gdb.execute("target remote localhost:1234") + + # These are not very useful in scripts + gdb.execute("set pagination off") + gdb.execute("set confirm off") + + # Run the actual tests + run_test() + +except: + print ("GDB Exception: %s" % (sys.exc_info()[0])) + failcount += 1 + import code + code.InteractiveConsole(locals=globals()).interact() + raise + +# Finally kill the inferior and exit gdb with a count of failures +gdb.execute("kill") +exit(failcount) diff --git a/qemu/tests/hd-geo-test.c b/qemu/tests/hd-geo-test.c index 00afc209e..c8e669ac2 100644 --- a/qemu/tests/hd-geo-test.c +++ b/qemu/tests/hd-geo-test.c @@ -15,10 +15,8 @@ * Improvements welcome. */ +#include "qemu/osdep.h" #include -#include -#include -#include #include "qemu-common.h" #include "libqtest.h" @@ -206,13 +204,13 @@ static int setup_ide(int argc, char *argv[], int argv_sz, { char *s1, *s2, *s3; - s1 = g_strdup_printf("-drive id=drive%d,if=%s,format=raw", + s1 = g_strdup_printf("-drive id=drive%d,if=%s", ide_idx, dev ? "none" : "ide"); s2 = dev ? g_strdup("") : g_strdup_printf(",index=%d", ide_idx); if (img_secs[img_idx] >= 0) { setup_mbr(img_idx, mbr); - s3 = g_strdup_printf(",file=%s", img_file_name[img_idx]); + s3 = g_strdup_printf(",format=raw,file=%s", img_file_name[img_idx]); } else { s3 = g_strdup(",media=cdrom"); } diff --git a/qemu/tests/i440fx-test.c b/qemu/tests/i440fx-test.c index d0bc8de25..05029e90b 100644 --- a/qemu/tests/i440fx-test.c +++ b/qemu/tests/i440fx-test.c @@ -12,13 +12,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include -#include -#include -#include #include -#include #include "libqtest.h" #include "libqos/pci.h" @@ -27,8 +23,6 @@ #define BROKEN 1 -#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) - typedef struct TestData { int num_cpus; @@ -191,7 +185,7 @@ static void write_area(uint32_t start, uint32_t end, uint8_t value) uint32_t size = end - start + 1; uint8_t *data; - data = g_malloc0(size); + data = g_malloc(size); memset(data, value, size); memwrite(start, data, size); diff --git a/qemu/tests/i82801b11-test.c b/qemu/tests/i82801b11-test.c index 78d9ce0e6..c3b5ebbca 100644 --- a/qemu/tests/i82801b11-test.c +++ b/qemu/tests/i82801b11-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void nop(void) diff --git a/qemu/tests/ide-test.c b/qemu/tests/ide-test.c index 4a07e3a9d..0d9ab4df9 100644 --- a/qemu/tests/ide-test.c +++ b/qemu/tests/ide-test.c @@ -22,9 +22,7 @@ * THE SOFTWARE. */ -#include -#include -#include +#include "qemu/osdep.h" #include @@ -45,8 +43,15 @@ #define IDE_BASE 0x1f0 #define IDE_PRIMARY_IRQ 14 +#define ATAPI_BLOCK_SIZE 2048 + +/* How many bytes to receive via ATAPI PIO at one time. + * Must be less than 0xFFFF. */ +#define BYTE_COUNT_LIMIT 5120 + enum { reg_data = 0x0, + reg_feature = 0x1, reg_nsectors = 0x2, reg_lba_low = 0x3, reg_lba_middle = 0x4, @@ -80,6 +85,7 @@ enum { CMD_WRITE_DMA = 0xca, CMD_FLUSH_CACHE = 0xe7, CMD_IDENTIFY = 0xec, + CMD_PACKET = 0xa0, CMDF_ABORT = 0x100, CMDF_NO_BM = 0x200, @@ -172,7 +178,8 @@ typedef struct PrdtEntry { #define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0) static int send_dma_request(int cmd, uint64_t sector, int nb_sectors, - PrdtEntry *prdt, int prdt_entries) + PrdtEntry *prdt, int prdt_entries, + void(*post_exec)(uint64_t sector, int nb_sectors)) { QPCIDevice *dev; uint16_t bmdma_base; @@ -189,6 +196,9 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors, switch (cmd) { case CMD_READ_DMA: + case CMD_PACKET: + /* Assuming we only test data reads w/ ATAPI, otherwise we need to know + * the SCSI command being sent in the packet, too. */ from_dev = true; break; case CMD_WRITE_DMA: @@ -217,14 +227,22 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors, outl(bmdma_base + bmreg_prdt, guest_prdt); /* ATA DMA command */ - outb(IDE_BASE + reg_nsectors, nb_sectors); - - outb(IDE_BASE + reg_lba_low, sector & 0xff); - outb(IDE_BASE + reg_lba_middle, (sector >> 8) & 0xff); - outb(IDE_BASE + reg_lba_high, (sector >> 16) & 0xff); + if (cmd == CMD_PACKET) { + /* Enables ATAPI DMA; otherwise PIO is attempted */ + outb(IDE_BASE + reg_feature, 0x01); + } else { + outb(IDE_BASE + reg_nsectors, nb_sectors); + outb(IDE_BASE + reg_lba_low, sector & 0xff); + outb(IDE_BASE + reg_lba_middle, (sector >> 8) & 0xff); + outb(IDE_BASE + reg_lba_high, (sector >> 16) & 0xff); + } outb(IDE_BASE + reg_command, cmd); + if (post_exec) { + post_exec(sector, nb_sectors); + } + /* Start DMA transfer */ outb(bmdma_base + bmreg_cmd, BM_CMD_START | (from_dev ? BM_CMD_WRITE : 0)); @@ -278,7 +296,8 @@ static void test_bmdma_simple_rw(void) memset(buf, 0x55, len); memwrite(guest_buf, buf, len); - status = send_dma_request(CMD_WRITE_DMA, 0, 1, prdt, ARRAY_SIZE(prdt)); + status = send_dma_request(CMD_WRITE_DMA, 0, 1, prdt, + ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); @@ -286,14 +305,15 @@ static void test_bmdma_simple_rw(void) memset(buf, 0xaa, len); memwrite(guest_buf, buf, len); - status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt, ARRAY_SIZE(prdt)); + status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt, + ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); /* Read and verify 0x55 pattern in sector 0 */ memset(cmpbuf, 0x55, len); - status = send_dma_request(CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt)); + status = send_dma_request(CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); @@ -303,7 +323,7 @@ static void test_bmdma_simple_rw(void) /* Read and verify 0xaa pattern in sector 1 */ memset(cmpbuf, 0xaa, len); - status = send_dma_request(CMD_READ_DMA, 1, 1, prdt, ARRAY_SIZE(prdt)); + status = send_dma_request(CMD_READ_DMA, 1, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); @@ -328,13 +348,13 @@ static void test_bmdma_short_prdt(void) /* Normal request */ status = send_dma_request(CMD_READ_DMA, 0, 1, - prdt, ARRAY_SIZE(prdt)); + prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, 0); assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); /* Abort the request before it completes */ status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1, - prdt, ARRAY_SIZE(prdt)); + prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, 0); assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); } @@ -353,13 +373,13 @@ static void test_bmdma_one_sector_short_prdt(void) /* Normal request */ status = send_dma_request(CMD_READ_DMA, 0, 2, - prdt, ARRAY_SIZE(prdt)); + prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, 0); assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); /* Abort the request before it completes */ status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 2, - prdt, ARRAY_SIZE(prdt)); + prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, 0); assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); } @@ -377,13 +397,13 @@ static void test_bmdma_long_prdt(void) /* Normal request */ status = send_dma_request(CMD_READ_DMA, 0, 1, - prdt, ARRAY_SIZE(prdt)); + prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR); assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); /* Abort the request before it completes */ status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1, - prdt, ARRAY_SIZE(prdt)); + prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); } @@ -399,7 +419,7 @@ static void test_bmdma_no_busmaster(void) PrdtEntry prdt[4096] = { }; status = send_dma_request(CMD_READ_DMA | CMDF_NO_BM, 0, 512, - prdt, ARRAY_SIZE(prdt)); + prdt, ARRAY_SIZE(prdt), NULL); /* Not entirely clear what the expected result is, but this is what we get * in practice. At least we want to be aware of any changes. */ @@ -488,9 +508,7 @@ static void test_flush(void) tmp_path); /* Delay the completion of the flush request until we explicitly do it */ - qmp_discard_response("{'execute':'human-monitor-command', 'arguments': {" - " 'command-line':" - " 'qemu-io ide0-hd0 \"break flush_to_os A\"'} }"); + g_free(hmp("qemu-io ide0-hd0 \"break flush_to_os A\"")); /* FLUSH CACHE command on device 0*/ outb(IDE_BASE + reg_device, 0); @@ -502,9 +520,7 @@ static void test_flush(void) assert_bit_clear(data, DF | ERR | DRQ); /* Complete the command */ - qmp_discard_response("{'execute':'human-monitor-command', 'arguments': {" - " 'command-line':" - " 'qemu-io ide0-hd0 \"resume A\"'} }"); + g_free(hmp("qemu-io ide0-hd0 \"resume A\"")); /* Check registers */ data = inb(IDE_BASE + reg_device); @@ -575,16 +591,215 @@ static void test_flush_nodev(void) ide_test_quit(); } -static void test_pci_retry_flush(const char *machine) +static void test_pci_retry_flush(void) { test_retry_flush("pc"); } -static void test_isa_retry_flush(const char *machine) +static void test_isa_retry_flush(void) { test_retry_flush("isapc"); } +typedef struct Read10CDB { + uint8_t opcode; + uint8_t flags; + uint32_t lba; + uint8_t reserved; + uint16_t nblocks; + uint8_t control; + uint16_t padding; +} __attribute__((__packed__)) Read10CDB; + +static void send_scsi_cdb_read10(uint64_t lba, int nblocks) +{ + Read10CDB pkt = { .padding = 0 }; + int i; + + g_assert_cmpint(lba, <=, UINT32_MAX); + g_assert_cmpint(nblocks, <=, UINT16_MAX); + g_assert_cmpint(nblocks, >=, 0); + + /* Construct SCSI CDB packet */ + pkt.opcode = 0x28; + pkt.lba = cpu_to_be32(lba); + pkt.nblocks = cpu_to_be16(nblocks); + + /* Send Packet */ + for (i = 0; i < sizeof(Read10CDB)/2; i++) { + outw(IDE_BASE + reg_data, cpu_to_le16(((uint16_t *)&pkt)[i])); + } +} + +static void nsleep(int64_t nsecs) +{ + const struct timespec val = { .tv_nsec = nsecs }; + nanosleep(&val, NULL); + clock_set(nsecs); +} + +static uint8_t ide_wait_clear(uint8_t flag) +{ + uint8_t data; + time_t st; + + /* Wait with a 5 second timeout */ + time(&st); + while (true) { + data = inb(IDE_BASE + reg_status); + if (!(data & flag)) { + return data; + } + if (difftime(time(NULL), st) > 5.0) { + break; + } + nsleep(400); + } + g_assert_not_reached(); +} + +static void ide_wait_intr(int irq) +{ + time_t st; + bool intr; + + time(&st); + while (true) { + intr = get_irq(irq); + if (intr) { + return; + } + if (difftime(time(NULL), st) > 5.0) { + break; + } + nsleep(400); + } + + g_assert_not_reached(); +} + +static void cdrom_pio_impl(int nblocks) +{ + FILE *fh; + int patt_blocks = MAX(16, nblocks); + size_t patt_len = ATAPI_BLOCK_SIZE * patt_blocks; + char *pattern = g_malloc(patt_len); + size_t rxsize = ATAPI_BLOCK_SIZE * nblocks; + uint16_t *rx = g_malloc0(rxsize); + int i, j; + uint8_t data; + uint16_t limit; + + /* Prepopulate the CDROM with an interesting pattern */ + generate_pattern(pattern, patt_len, ATAPI_BLOCK_SIZE); + fh = fopen(tmp_path, "w+"); + fwrite(pattern, ATAPI_BLOCK_SIZE, patt_blocks, fh); + fclose(fh); + + ide_test_start("-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 " + "-device ide-cd,drive=sr0,bus=ide.0", tmp_path); + qtest_irq_intercept_in(global_qtest, "ioapic"); + + /* PACKET command on device 0 */ + outb(IDE_BASE + reg_device, 0); + outb(IDE_BASE + reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF); + outb(IDE_BASE + reg_lba_high, (BYTE_COUNT_LIMIT >> 8 & 0xFF)); + outb(IDE_BASE + reg_command, CMD_PACKET); + /* HP0: Check_Status_A State */ + nsleep(400); + data = ide_wait_clear(BSY); + /* HP1: Send_Packet State */ + assert_bit_set(data, DRQ | DRDY); + assert_bit_clear(data, ERR | DF | BSY); + + /* SCSI CDB (READ10) -- read n*2048 bytes from block 0 */ + send_scsi_cdb_read10(0, nblocks); + + /* Read data back: occurs in bursts of 'BYTE_COUNT_LIMIT' bytes. + * If BYTE_COUNT_LIMIT is odd, we transfer BYTE_COUNT_LIMIT - 1 bytes. + * We allow an odd limit only when the remaining transfer size is + * less than BYTE_COUNT_LIMIT. However, SCSI's read10 command can only + * request n blocks, so our request size is always even. + * For this reason, we assume there is never a hanging byte to fetch. */ + g_assert(!(rxsize & 1)); + limit = BYTE_COUNT_LIMIT & ~1; + for (i = 0; i < DIV_ROUND_UP(rxsize, limit); i++) { + size_t offset = i * (limit / 2); + size_t rem = (rxsize / 2) - offset; + + /* HP3: INTRQ_Wait */ + ide_wait_intr(IDE_PRIMARY_IRQ); + + /* HP2: Check_Status_B (and clear IRQ) */ + data = ide_wait_clear(BSY); + assert_bit_set(data, DRQ | DRDY); + assert_bit_clear(data, ERR | DF | BSY); + + /* HP4: Transfer_Data */ + for (j = 0; j < MIN((limit / 2), rem); j++) { + rx[offset + j] = le16_to_cpu(inw(IDE_BASE + reg_data)); + } + } + + /* Check for final completion IRQ */ + ide_wait_intr(IDE_PRIMARY_IRQ); + + /* Sanity check final state */ + data = ide_wait_clear(DRQ); + assert_bit_set(data, DRDY); + assert_bit_clear(data, DRQ | ERR | DF | BSY); + + g_assert_cmpint(memcmp(pattern, rx, rxsize), ==, 0); + g_free(pattern); + g_free(rx); + test_bmdma_teardown(); +} + +static void test_cdrom_pio(void) +{ + cdrom_pio_impl(1); +} + +static void test_cdrom_pio_large(void) +{ + /* Test a few loops of the PIO DRQ mechanism. */ + cdrom_pio_impl(BYTE_COUNT_LIMIT * 4 / ATAPI_BLOCK_SIZE); +} + + +static void test_cdrom_dma(void) +{ + static const size_t len = ATAPI_BLOCK_SIZE; + char *pattern = g_malloc(ATAPI_BLOCK_SIZE * 16); + char *rx = g_malloc0(len); + uintptr_t guest_buf; + PrdtEntry prdt[1]; + FILE *fh; + + ide_test_start("-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 " + "-device ide-cd,drive=sr0,bus=ide.0", tmp_path); + qtest_irq_intercept_in(global_qtest, "ioapic"); + + guest_buf = guest_alloc(guest_malloc, len); + prdt[0].addr = cpu_to_le32(guest_buf); + prdt[0].size = cpu_to_le32(len | PRDT_EOT); + + generate_pattern(pattern, ATAPI_BLOCK_SIZE * 16, ATAPI_BLOCK_SIZE); + fh = fopen(tmp_path, "w+"); + fwrite(pattern, ATAPI_BLOCK_SIZE, 16, fh); + fclose(fh); + + send_dma_request(CMD_PACKET, 0, 1, prdt, 1, send_scsi_cdb_read10); + + /* Read back data from guest memory into local qtest memory */ + memread(guest_buf, rx, len); + g_assert_cmpint(memcmp(pattern, rx, len), ==, 0); + + g_free(pattern); + g_free(rx); + test_bmdma_teardown(); +} + int main(int argc, char **argv) { const char *arch = qtest_get_arch(); @@ -628,6 +843,10 @@ int main(int argc, char **argv) qtest_add_func("/ide/flush/retry_pci", test_pci_retry_flush); qtest_add_func("/ide/flush/retry_isa", test_isa_retry_flush); + qtest_add_func("/ide/cdrom/pio", test_cdrom_pio); + qtest_add_func("/ide/cdrom/pio_large", test_cdrom_pio_large); + qtest_add_func("/ide/cdrom/dma", test_cdrom_dma); + ret = g_test_run(); /* Cleanup */ diff --git a/qemu/tests/image-fuzzer/runner.py b/qemu/tests/image-fuzzer/runner.py index 0a8743ef4..96a1c11b2 100755 --- a/qemu/tests/image-fuzzer/runner.py +++ b/qemu/tests/image-fuzzer/runner.py @@ -157,7 +157,7 @@ class TestEnv(object): try: os.makedirs(self.current_dir) - except OSError, e: + except OSError as e: print >>sys.stderr, \ "Error: The working directory '%s' cannot be used. Reason: %s"\ % (self.work_dir, e[1]) @@ -244,7 +244,7 @@ class TestEnv(object): temp_log = StringIO.StringIO() try: retcode = run_app(temp_log, current_cmd) - except OSError, e: + except OSError as e: multilog("%sError: Start of '%s' failed. Reason: %s\n\n" % (test_summary, os.path.basename(current_cmd[0]), e[1]), @@ -301,7 +301,7 @@ if __name__ == '__main__': JSON: '--command' accepts a JSON array of commands. Each command presents - an application under test with all its paramaters as a list of strings, + an application under test with all its parameters as a list of strings, e.g. ["qemu-io", "$test_img", "-c", "write $off $len"]. Supported application aliases: 'qemu-img' and 'qemu-io'. @@ -356,7 +356,7 @@ if __name__ == '__main__': opts, args = getopt.gnu_getopt(sys.argv[1:], 'c:hs:kvd:', ['command=', 'help', 'seed=', 'config=', 'keep_passed', 'verbose', 'duration=']) - except getopt.error, e: + except getopt.error as e: print >>sys.stderr, \ "Error: %s\n\nTry 'runner.py --help' for more information" % e sys.exit(1) @@ -374,7 +374,7 @@ if __name__ == '__main__': elif opt in ('-c', '--command'): try: command = json.loads(arg) - except (TypeError, ValueError, NameError), e: + except (TypeError, ValueError, NameError) as e: print >>sys.stderr, \ "Error: JSON array of test commands cannot be loaded.\n" \ "Reason: %s" % e @@ -390,7 +390,7 @@ if __name__ == '__main__': elif opt == '--config': try: config = json.loads(arg) - except (TypeError, ValueError, NameError), e: + except (TypeError, ValueError, NameError) as e: print >>sys.stderr, \ "Error: JSON array with the fuzzer configuration cannot" \ " be loaded\nReason: %s" % e @@ -414,7 +414,7 @@ if __name__ == '__main__': try: image_generator = __import__(generator_name) - except ImportError, e: + except ImportError as e: print >>sys.stderr, \ "Error: The image generator '%s' cannot be imported.\n" \ "Reason: %s" % (generator_name, e) diff --git a/qemu/tests/intel-hda-test.c b/qemu/tests/intel-hda-test.c index d89b407dc..1be6add9b 100644 --- a/qemu/tests/intel-hda-test.c +++ b/qemu/tests/intel-hda-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" #define HDA_ID "hda0" #define CODEC_DEVICES " -device hda-output,bus=" HDA_ID ".0" \ diff --git a/qemu/tests/io-channel-helpers.c b/qemu/tests/io-channel-helpers.c new file mode 100644 index 000000000..05e5579cf --- /dev/null +++ b/qemu/tests/io-channel-helpers.c @@ -0,0 +1,248 @@ +/* + * QEMU I/O channel test helpers + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "io-channel-helpers.h" +#include "qapi/error.h" + +struct QIOChannelTest { + QIOChannel *src; + QIOChannel *dst; + bool blocking; + size_t len; + size_t niov; + char *input; + struct iovec *inputv; + char *output; + struct iovec *outputv; + Error *writeerr; + Error *readerr; +}; + + +static void test_skip_iovec(struct iovec **iov, + size_t *niov, + size_t skip, + struct iovec *old) +{ + size_t offset = 0; + size_t i; + + for (i = 0; i < *niov; i++) { + if (skip < (*iov)[i].iov_len) { + old->iov_len = (*iov)[i].iov_len; + old->iov_base = (*iov)[i].iov_base; + + (*iov)[i].iov_len -= skip; + (*iov)[i].iov_base += skip; + break; + } else { + skip -= (*iov)[i].iov_len; + + if (i == 0 && old->iov_base) { + (*iov)[i].iov_len = old->iov_len; + (*iov)[i].iov_base = old->iov_base; + old->iov_len = 0; + old->iov_base = NULL; + } + + offset++; + } + } + + *iov = *iov + offset; + *niov -= offset; +} + + +/* This thread sends all data using iovecs */ +static gpointer test_io_thread_writer(gpointer opaque) +{ + QIOChannelTest *data = opaque; + struct iovec *iov = data->inputv; + size_t niov = data->niov; + struct iovec old = { 0 }; + + qio_channel_set_blocking(data->src, data->blocking, NULL); + + while (niov) { + ssize_t ret; + ret = qio_channel_writev(data->src, + iov, + niov, + &data->writeerr); + if (ret == QIO_CHANNEL_ERR_BLOCK) { + if (data->blocking) { + error_setg(&data->writeerr, + "Unexpected I/O blocking"); + break; + } else { + qio_channel_wait(data->src, + G_IO_OUT); + continue; + } + } else if (ret < 0) { + break; + } else if (ret == 0) { + error_setg(&data->writeerr, + "Unexpected zero length write"); + break; + } + + test_skip_iovec(&iov, &niov, ret, &old); + } + + return NULL; +} + + +/* This thread receives all data using iovecs */ +static gpointer test_io_thread_reader(gpointer opaque) +{ + QIOChannelTest *data = opaque; + struct iovec *iov = data->outputv; + size_t niov = data->niov; + struct iovec old = { 0 }; + + qio_channel_set_blocking(data->dst, data->blocking, NULL); + + while (niov) { + ssize_t ret; + + ret = qio_channel_readv(data->dst, + iov, + niov, + &data->readerr); + + if (ret == QIO_CHANNEL_ERR_BLOCK) { + if (data->blocking) { + error_setg(&data->readerr, + "Unexpected I/O blocking"); + break; + } else { + qio_channel_wait(data->dst, + G_IO_IN); + continue; + } + } else if (ret < 0) { + break; + } else if (ret == 0) { + break; + } + + test_skip_iovec(&iov, &niov, ret, &old); + } + + return NULL; +} + + +QIOChannelTest *qio_channel_test_new(void) +{ + QIOChannelTest *data = g_new0(QIOChannelTest, 1); + size_t i; + size_t offset; + + + /* We'll send 1 MB of data */ +#define CHUNK_COUNT 250 +#define CHUNK_LEN 4194 + + data->len = CHUNK_COUNT * CHUNK_LEN; + data->input = g_new0(char, data->len); + data->output = g_new0(gchar, data->len); + + /* Fill input with a pattern */ + for (i = 0; i < data->len; i += CHUNK_LEN) { + memset(data->input + i, (i / CHUNK_LEN), CHUNK_LEN); + } + + /* We'll split the data across a bunch of IO vecs */ + data->niov = CHUNK_COUNT; + data->inputv = g_new0(struct iovec, data->niov); + data->outputv = g_new0(struct iovec, data->niov); + + for (i = 0, offset = 0; i < data->niov; i++, offset += CHUNK_LEN) { + data->inputv[i].iov_base = data->input + offset; + data->outputv[i].iov_base = data->output + offset; + data->inputv[i].iov_len = CHUNK_LEN; + data->outputv[i].iov_len = CHUNK_LEN; + } + + return data; +} + +void qio_channel_test_run_threads(QIOChannelTest *test, + bool blocking, + QIOChannel *src, + QIOChannel *dst) +{ + GThread *reader, *writer; + + test->src = src; + test->dst = dst; + test->blocking = blocking; + + reader = g_thread_new("reader", + test_io_thread_reader, + test); + writer = g_thread_new("writer", + test_io_thread_writer, + test); + + g_thread_join(reader); + g_thread_join(writer); + + test->dst = test->src = NULL; +} + + +void qio_channel_test_run_writer(QIOChannelTest *test, + QIOChannel *src) +{ + test->src = src; + test_io_thread_writer(test); + test->src = NULL; +} + + +void qio_channel_test_run_reader(QIOChannelTest *test, + QIOChannel *dst) +{ + test->dst = dst; + test_io_thread_reader(test); + test->dst = NULL; +} + + +void qio_channel_test_validate(QIOChannelTest *test) +{ + g_assert(test->readerr == NULL); + g_assert(test->writeerr == NULL); + g_assert_cmpint(memcmp(test->input, + test->output, + test->len), ==, 0); + + g_free(test->inputv); + g_free(test->outputv); + g_free(test->input); + g_free(test->output); + g_free(test); +} diff --git a/qemu/tests/io-channel-helpers.h b/qemu/tests/io-channel-helpers.h new file mode 100644 index 000000000..fedc64fd5 --- /dev/null +++ b/qemu/tests/io-channel-helpers.h @@ -0,0 +1,42 @@ +/* + * QEMU I/O channel test helpers + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "io/channel.h" + +#ifndef TEST_IO_CHANNEL_HELPERS +#define TEST_IO_CHANNEL_HELPERS + +typedef struct QIOChannelTest QIOChannelTest; + +QIOChannelTest *qio_channel_test_new(void); + +void qio_channel_test_run_threads(QIOChannelTest *test, + bool blocking, + QIOChannel *src, + QIOChannel *dst); + +void qio_channel_test_run_writer(QIOChannelTest *test, + QIOChannel *src); +void qio_channel_test_run_reader(QIOChannelTest *test, + QIOChannel *dst); + +void qio_channel_test_validate(QIOChannelTest *test); + +#endif /* TEST_IO_CHANNEL_HELPERS */ diff --git a/qemu/tests/ioh3420-test.c b/qemu/tests/ioh3420-test.c index c991a5f87..93eb2f750 100644 --- a/qemu/tests/ioh3420-test.c +++ b/qemu/tests/ioh3420-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void nop(void) diff --git a/qemu/tests/ipmi-bt-test.c b/qemu/tests/ipmi-bt-test.c new file mode 100644 index 000000000..812907fb7 --- /dev/null +++ b/qemu/tests/ipmi-bt-test.c @@ -0,0 +1,433 @@ +/* + * IPMI BT test cases, using the external interface for checking + * + * Copyright (c) 2012 Corey Minyard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" + +#include +#include +#include +#include + +#include + +#include "libqtest.h" +#include "qemu-common.h" + +#define IPMI_IRQ 5 + +#define IPMI_BT_BASE 0xe4 + +#define IPMI_BT_CTLREG_CLR_WR_PTR 0 +#define IPMI_BT_CTLREG_CLR_RD_PTR 1 +#define IPMI_BT_CTLREG_H2B_ATN 2 +#define IPMI_BT_CTLREG_B2H_ATN 3 +#define IPMI_BT_CTLREG_SMS_ATN 4 +#define IPMI_BT_CTLREG_H_BUSY 6 +#define IPMI_BT_CTLREG_B_BUSY 7 + +#define IPMI_BT_CTLREG_GET(b) ((bt_get_ctrlreg() >> (b)) & 1) +#define IPMI_BT_CTLREG_GET_H2B_ATN() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_H2B_ATN) +#define IPMI_BT_CTLREG_GET_B2H_ATN() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_B2H_ATN) +#define IPMI_BT_CTLREG_GET_SMS_ATN() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_SMS_ATN) +#define IPMI_BT_CTLREG_GET_H_BUSY() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_H_BUSY) +#define IPMI_BT_CTLREG_GET_B_BUSY() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_B_BUSY) + +#define IPMI_BT_CTLREG_SET(b) bt_write_ctrlreg(1 << (b)) +#define IPMI_BT_CTLREG_SET_CLR_WR_PTR() IPMI_BT_CTLREG_SET( \ + IPMI_BT_CTLREG_CLR_WR_PTR) +#define IPMI_BT_CTLREG_SET_CLR_RD_PTR() IPMI_BT_CTLREG_SET( \ + IPMI_BT_CTLREG_CLR_RD_PTR) +#define IPMI_BT_CTLREG_SET_H2B_ATN() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_H2B_ATN) +#define IPMI_BT_CTLREG_SET_B2H_ATN() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_B2H_ATN) +#define IPMI_BT_CTLREG_SET_SMS_ATN() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_SMS_ATN) +#define IPMI_BT_CTLREG_SET_H_BUSY() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_H_BUSY) + +static int bt_ints_enabled; + +static uint8_t bt_get_ctrlreg(void) +{ + return inb(IPMI_BT_BASE); +} + +static void bt_write_ctrlreg(uint8_t val) +{ + outb(IPMI_BT_BASE, val); +} + +static uint8_t bt_get_buf(void) +{ + return inb(IPMI_BT_BASE + 1); +} + +static void bt_write_buf(uint8_t val) +{ + outb(IPMI_BT_BASE + 1, val); +} + +static uint8_t bt_get_irqreg(void) +{ + return inb(IPMI_BT_BASE + 2); +} + +static void bt_write_irqreg(uint8_t val) +{ + outb(IPMI_BT_BASE + 2, val); +} + +static void bt_wait_b_busy(void) +{ + unsigned int count = 1000; + while (IPMI_BT_CTLREG_GET_B_BUSY() != 0) { + g_assert(--count != 0); + } +} + +static void bt_wait_b2h_atn(void) +{ + unsigned int count = 1000; + while (IPMI_BT_CTLREG_GET_B2H_ATN() == 0) { + g_assert(--count != 0); + } +} + + +static int emu_lfd; +static int emu_fd; +static in_port_t emu_port; +static uint8_t inbuf[100]; +static unsigned int inbuf_len; +static unsigned int inbuf_pos; +static int last_was_aa; + +static void read_emu_data(void) +{ + fd_set readfds; + int rv; + struct timeval tv; + + FD_ZERO(&readfds); + FD_SET(emu_fd, &readfds); + tv.tv_sec = 10; + tv.tv_usec = 0; + rv = select(emu_fd + 1, &readfds, NULL, NULL, &tv); + if (rv == -1) { + perror("select"); + } + g_assert(rv == 1); + rv = read(emu_fd, inbuf, sizeof(inbuf)); + if (rv == -1) { + perror("read"); + } + g_assert(rv > 0); + inbuf_len = rv; + inbuf_pos = 0; +} + +static void write_emu_msg(uint8_t *msg, unsigned int len) +{ + int rv; + +#ifdef DEBUG_TEST + { + unsigned int i; + printf("sending:"); + for (i = 0; i < len; i++) { + printf(" %2.2x", msg[i]); + } + printf("\n"); + } +#endif + rv = write(emu_fd, msg, len); + g_assert(rv == len); +} + +static void get_emu_msg(uint8_t *msg, unsigned int *len) +{ + unsigned int outpos = 0; + + for (;;) { + while (inbuf_pos < inbuf_len) { + uint8_t ch = inbuf[inbuf_pos++]; + + g_assert(outpos < *len); + if (last_was_aa) { + assert(ch & 0x10); + msg[outpos++] = ch & ~0x10; + last_was_aa = 0; + } else if (ch == 0xaa) { + last_was_aa = 1; + } else { + msg[outpos++] = ch; + if ((ch == 0xa0) || (ch == 0xa1)) { + /* Message complete */ + *len = outpos; + goto done; + } + } + } + read_emu_data(); + } + done: +#ifdef DEBUG_TEST + { + unsigned int i; + printf("Msg:"); + for (i = 0; i < outpos; i++) { + printf(" %2.2x", msg[i]); + } + printf("\n"); + } +#endif + return; +} + +static uint8_t +ipmb_checksum(const unsigned char *data, int size, unsigned char start) +{ + unsigned char csum = start; + + for (; size > 0; size--, data++) { + csum += *data; + } + return csum; +} + +static uint8_t get_dev_id_cmd[] = { 0x18, 0x01 }; +static uint8_t get_dev_id_rsp[] = { 0x1c, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x02, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static uint8_t set_bmc_globals_cmd[] = { 0x18, 0x2e, 0x0f }; +static uint8_t set_bmc_globals_rsp[] = { 0x1c, 0x2e, 0x00 }; +static uint8_t enable_irq_cmd[] = { 0x05, 0xa1 }; + +static void emu_msg_handler(void) +{ + uint8_t msg[100]; + unsigned int msg_len = sizeof(msg); + + get_emu_msg(msg, &msg_len); + g_assert(msg_len >= 5); + g_assert(msg[msg_len - 1] == 0xa0); + msg_len--; + g_assert(ipmb_checksum(msg, msg_len, 0) == 0); + msg_len--; + if ((msg[1] == get_dev_id_cmd[0]) && (msg[2] == get_dev_id_cmd[1])) { + memcpy(msg + 1, get_dev_id_rsp, sizeof(get_dev_id_rsp)); + msg_len = sizeof(get_dev_id_rsp) + 1; + msg[msg_len] = -ipmb_checksum(msg, msg_len, 0); + msg_len++; + msg[msg_len++] = 0xa0; + write_emu_msg(msg, msg_len); + } else if ((msg[1] == set_bmc_globals_cmd[0]) && + (msg[2] == set_bmc_globals_cmd[1])) { + memcpy(msg + 1, set_bmc_globals_rsp, sizeof(set_bmc_globals_rsp)); + msg_len = sizeof(set_bmc_globals_rsp) + 1; + msg[msg_len] = -ipmb_checksum(msg, msg_len, 0); + msg_len++; + msg[msg_len++] = 0xa0; + write_emu_msg(msg, msg_len); + write_emu_msg(enable_irq_cmd, sizeof(enable_irq_cmd)); + } else { + g_assert(0); + } +} + +static void bt_cmd(uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len) +{ + unsigned int i, len, j = 0; + uint8_t seq = 5; + + /* Should be idle */ + g_assert(bt_get_ctrlreg() == 0); + + bt_wait_b_busy(); + IPMI_BT_CTLREG_SET_CLR_WR_PTR(); + bt_write_buf(cmd_len + 1); + bt_write_buf(cmd[0]); + bt_write_buf(seq); + for (i = 1; i < cmd_len; i++) { + bt_write_buf(cmd[i]); + } + IPMI_BT_CTLREG_SET_H2B_ATN(); + + emu_msg_handler(); /* We should get a message on the socket here. */ + + bt_wait_b2h_atn(); + if (bt_ints_enabled) { + g_assert((bt_get_irqreg() & 0x02) == 0x02); + g_assert(get_irq(IPMI_IRQ)); + bt_write_irqreg(0x03); + } else { + g_assert(!get_irq(IPMI_IRQ)); + } + IPMI_BT_CTLREG_SET_H_BUSY(); + IPMI_BT_CTLREG_SET_B2H_ATN(); + IPMI_BT_CTLREG_SET_CLR_RD_PTR(); + len = bt_get_buf(); + g_assert(len >= 4); + rsp[0] = bt_get_buf(); + assert(bt_get_buf() == seq); + len--; + for (j = 1; j < len; j++) { + rsp[j] = bt_get_buf(); + } + IPMI_BT_CTLREG_SET_H_BUSY(); + *rsp_len = j; +} + + +/* + * We should get a connect request and a short message with capabilities. + */ +static void test_connect(void) +{ + fd_set readfds; + int rv; + int val; + struct timeval tv; + uint8_t msg[100]; + unsigned int msglen; + static uint8_t exp1[] = { 0xff, 0x01, 0xa1 }; /* A protocol version */ + static uint8_t exp2[] = { 0x08, 0x1f, 0xa1 }; /* A capabilities cmd */ + + FD_ZERO(&readfds); + FD_SET(emu_lfd, &readfds); + tv.tv_sec = 10; + tv.tv_usec = 0; + rv = select(emu_lfd + 1, &readfds, NULL, NULL, &tv); + g_assert(rv == 1); + emu_fd = accept(emu_lfd, NULL, 0); + if (emu_fd < 0) { + perror("accept"); + } + g_assert(emu_fd >= 0); + + val = 1; + rv = setsockopt(emu_fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); + g_assert(rv != -1); + + /* Report our version */ + write_emu_msg(exp1, sizeof(exp1)); + + /* Validate that we get the info we expect. */ + msglen = sizeof(msg); + get_emu_msg(msg, &msglen); + g_assert(msglen == sizeof(exp1)); + g_assert(memcmp(msg, exp1, msglen) == 0); + msglen = sizeof(msg); + get_emu_msg(msg, &msglen); + g_assert(msglen == sizeof(exp2)); + g_assert(memcmp(msg, exp2, msglen) == 0); +} + +/* + * Send a get_device_id to do a basic test. + */ +static void test_bt_base(void) +{ + uint8_t rsp[20]; + unsigned int rsplen = sizeof(rsp); + + bt_cmd(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen); + g_assert(rsplen == sizeof(get_dev_id_rsp)); + g_assert(memcmp(get_dev_id_rsp, rsp, rsplen) == 0); +} + +/* + * Enable IRQs for the interface. + */ +static void test_enable_irq(void) +{ + uint8_t rsp[20]; + unsigned int rsplen = sizeof(rsp); + + bt_cmd(set_bmc_globals_cmd, sizeof(set_bmc_globals_cmd), rsp, &rsplen); + g_assert(rsplen == sizeof(set_bmc_globals_rsp)); + g_assert(memcmp(set_bmc_globals_rsp, rsp, rsplen) == 0); + bt_write_irqreg(0x01); + bt_ints_enabled = 1; +} + +/* + * Create a local TCP socket with any port, then save off the port we got. + */ +static void open_socket(void) +{ + struct sockaddr_in myaddr; + socklen_t addrlen; + + myaddr.sin_family = AF_INET; + myaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + myaddr.sin_port = 0; + emu_lfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (emu_lfd == -1) { + perror("socket"); + exit(1); + } + if (bind(emu_lfd, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) { + perror("bind"); + exit(1); + } + addrlen = sizeof(myaddr); + if (getsockname(emu_lfd, (struct sockaddr *) &myaddr , &addrlen) == -1) { + perror("getsockname"); + exit(1); + } + emu_port = ntohs(myaddr.sin_port); + assert(listen(emu_lfd, 1) != -1); +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + char *cmdline; + int ret; + + /* Check architecture */ + if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) { + g_test_message("Skipping test for non-x86\n"); + return 0; + } + + open_socket(); + + /* Run the tests */ + g_test_init(&argc, &argv, NULL); + + cmdline = g_strdup_printf("-vnc none" + " -chardev socket,id=ipmi0,host=localhost,port=%d,reconnect=10" + " -device ipmi-bmc-extern,chardev=ipmi0,id=bmc0" + " -device isa-ipmi-bt,bmc=bmc0", emu_port); + qtest_start(cmdline); + qtest_irq_intercept_in(global_qtest, "ioapic"); + qtest_add_func("/ipmi/extern/connect", test_connect); + qtest_add_func("/ipmi/extern/bt_base", test_bt_base); + qtest_add_func("/ipmi/extern/bt_enable_irq", test_enable_irq); + qtest_add_func("/ipmi/extern/bt_base_irq", test_bt_base); + ret = g_test_run(); + qtest_quit(global_qtest); + + return ret; +} diff --git a/qemu/tests/ipmi-kcs-test.c b/qemu/tests/ipmi-kcs-test.c new file mode 100644 index 000000000..42c4b974c --- /dev/null +++ b/qemu/tests/ipmi-kcs-test.c @@ -0,0 +1,293 @@ +/* + * IPMI KCS test cases, using the local interface. + * + * Copyright (c) 2012 Corey Minyard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" + +#include + +#include "libqtest.h" + +#define IPMI_IRQ 5 + +#define IPMI_KCS_BASE 0xca2 + +#define IPMI_KCS_STATUS_ABORT 0x60 +#define IPMI_KCS_CMD_WRITE_START 0x61 +#define IPMI_KCS_CMD_WRITE_END 0x62 +#define IPMI_KCS_CMD_READ 0x68 + +#define IPMI_KCS_ABORTED_BY_CMD 0x01 + +#define IPMI_KCS_CMDREG_GET_STATE() ((kcs_get_cmdreg() >> 6) & 3) +#define IPMI_KCS_STATE_IDLE 0 +#define IPMI_KCS_STATE_READ 1 +#define IPMI_KCS_STATE_WRITE 2 +#define IPMI_KCS_STATE_ERROR 3 +#define IPMI_KCS_CMDREG_GET_CD() ((kcs_get_cmdreg() >> 3) & 1) +#define IPMI_KCS_CMDREG_GET_ATN() ((kcs_get_cmdreg() >> 2) & 1) +#define IPMI_KCS_CMDREG_GET_IBF() ((kcs_get_cmdreg() >> 1) & 1) +#define IPMI_KCS_CMDREG_GET_OBF() ((kcs_get_cmdreg() >> 0) & 1) + +static int kcs_ints_enabled; + +static uint8_t kcs_get_cmdreg(void) +{ + return inb(IPMI_KCS_BASE + 1); +} + +static void kcs_write_cmdreg(uint8_t val) +{ + outb(IPMI_KCS_BASE + 1, val); +} + +static uint8_t kcs_get_datareg(void) +{ + return inb(IPMI_KCS_BASE); +} + +static void kcs_write_datareg(uint8_t val) +{ + outb(IPMI_KCS_BASE, val); +} + +static void kcs_wait_ibf(void) +{ + unsigned int count = 1000; + while (IPMI_KCS_CMDREG_GET_IBF() != 0) { + g_assert(--count != 0); + } +} + +static void kcs_wait_obf(void) +{ + unsigned int count = 1000; + while (IPMI_KCS_CMDREG_GET_OBF() == 0) { + g_assert(--count != 0); + } +} + +static void kcs_clear_obf(void) +{ + if (kcs_ints_enabled) { + g_assert(get_irq(IPMI_IRQ)); + } else { + g_assert(!get_irq(IPMI_IRQ)); + } + g_assert(IPMI_KCS_CMDREG_GET_OBF() == 1); + kcs_get_datareg(); + g_assert(IPMI_KCS_CMDREG_GET_OBF() == 0); + g_assert(!get_irq(IPMI_IRQ)); +} + +static void kcs_check_state(uint8_t state) +{ + g_assert(IPMI_KCS_CMDREG_GET_STATE() == state); +} + +static void kcs_cmd(uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len) +{ + unsigned int i, j = 0; + + /* Should be idle */ + g_assert(kcs_get_cmdreg() == 0); + + kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_START); + kcs_wait_ibf(); + kcs_check_state(IPMI_KCS_STATE_WRITE); + kcs_clear_obf(); + for (i = 0; i < cmd_len; i++) { + kcs_write_datareg(cmd[i]); + kcs_wait_ibf(); + kcs_check_state(IPMI_KCS_STATE_WRITE); + kcs_clear_obf(); + } + kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_END); + kcs_wait_ibf(); + kcs_check_state(IPMI_KCS_STATE_WRITE); + kcs_clear_obf(); + kcs_write_datareg(0); + next_read_byte: + kcs_wait_ibf(); + switch (IPMI_KCS_CMDREG_GET_STATE()) { + case IPMI_KCS_STATE_READ: + kcs_wait_obf(); + g_assert(j < *rsp_len); + rsp[j++] = kcs_get_datareg(); + kcs_write_datareg(IPMI_KCS_CMD_READ); + goto next_read_byte; + break; + + case IPMI_KCS_STATE_IDLE: + kcs_wait_obf(); + kcs_get_datareg(); + break; + + default: + g_assert(0); + } + *rsp_len = j; +} + +static void kcs_abort(uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len) +{ + unsigned int i, j = 0; + unsigned int retries = 4; + + /* Should be idle */ + g_assert(kcs_get_cmdreg() == 0); + + kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_START); + kcs_wait_ibf(); + kcs_check_state(IPMI_KCS_STATE_WRITE); + kcs_clear_obf(); + for (i = 0; i < cmd_len; i++) { + kcs_write_datareg(cmd[i]); + kcs_wait_ibf(); + kcs_check_state(IPMI_KCS_STATE_WRITE); + kcs_clear_obf(); + } + kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_END); + kcs_wait_ibf(); + kcs_check_state(IPMI_KCS_STATE_WRITE); + kcs_clear_obf(); + kcs_write_datareg(0); + kcs_wait_ibf(); + switch (IPMI_KCS_CMDREG_GET_STATE()) { + case IPMI_KCS_STATE_READ: + kcs_wait_obf(); + g_assert(j < *rsp_len); + rsp[j++] = kcs_get_datareg(); + kcs_write_datareg(IPMI_KCS_CMD_READ); + break; + + default: + g_assert(0); + } + + /* Start the abort here */ + retry_abort: + g_assert(retries > 0); + + kcs_wait_ibf(); + kcs_write_cmdreg(IPMI_KCS_STATUS_ABORT); + kcs_wait_ibf(); + kcs_clear_obf(); + kcs_write_datareg(0); + kcs_wait_ibf(); + if (IPMI_KCS_CMDREG_GET_STATE() != IPMI_KCS_STATE_READ) { + retries--; + goto retry_abort; + } + kcs_wait_obf(); + rsp[0] = kcs_get_datareg(); + kcs_write_datareg(IPMI_KCS_CMD_READ); + kcs_wait_ibf(); + if (IPMI_KCS_CMDREG_GET_STATE() != IPMI_KCS_STATE_IDLE) { + retries--; + goto retry_abort; + } + kcs_wait_obf(); + kcs_clear_obf(); + + *rsp_len = j; +} + + +static uint8_t get_dev_id_cmd[] = { 0x18, 0x01 }; +static uint8_t get_dev_id_rsp[] = { 0x1c, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x02, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/* + * Send a get_device_id to do a basic test. + */ +static void test_kcs_base(void) +{ + uint8_t rsp[20]; + unsigned int rsplen = sizeof(rsp); + + kcs_cmd(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen); + g_assert(rsplen == sizeof(get_dev_id_rsp)); + g_assert(memcmp(get_dev_id_rsp, rsp, rsplen) == 0); +} + +/* + * Abort a kcs operation while reading + */ +static void test_kcs_abort(void) +{ + uint8_t rsp[20]; + unsigned int rsplen = sizeof(rsp); + + kcs_abort(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen); + g_assert(rsp[0] == IPMI_KCS_ABORTED_BY_CMD); +} + +static uint8_t set_bmc_globals_cmd[] = { 0x18, 0x2e, 0x0f }; +static uint8_t set_bmc_globals_rsp[] = { 0x1c, 0x2e, 0x00 }; + +/* + * Enable interrupts + */ +static void test_enable_irq(void) +{ + uint8_t rsp[20]; + unsigned int rsplen = sizeof(rsp); + + kcs_cmd(set_bmc_globals_cmd, sizeof(set_bmc_globals_cmd), rsp, &rsplen); + g_assert(rsplen == sizeof(set_bmc_globals_rsp)); + g_assert(memcmp(set_bmc_globals_rsp, rsp, rsplen) == 0); + kcs_ints_enabled = 1; +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + char *cmdline; + int ret; + + /* Check architecture */ + if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) { + g_test_message("Skipping test for non-x86\n"); + return 0; + } + + /* Run the tests */ + g_test_init(&argc, &argv, NULL); + + cmdline = g_strdup_printf("-vnc none -device ipmi-bmc-sim,id=bmc0" + " -device isa-ipmi-kcs,bmc=bmc0"); + qtest_start(cmdline); + qtest_irq_intercept_in(global_qtest, "ioapic"); + qtest_add_func("/ipmi/local/kcs_base", test_kcs_base); + qtest_add_func("/ipmi/local/kcs_abort", test_kcs_abort); + qtest_add_func("/ipmi/local/kcs_enable_irq", test_enable_irq); + qtest_add_func("/ipmi/local/kcs_base_irq", test_kcs_base); + qtest_add_func("/ipmi/local/kcs_abort_irq", test_kcs_abort); + ret = g_test_run(); + qtest_quit(global_qtest); + + return ret; +} diff --git a/qemu/tests/ipoctal232-test.c b/qemu/tests/ipoctal232-test.c index 3ac1714b4..846aaf571 100644 --- a/qemu/tests/ipoctal232-test.c +++ b/qemu/tests/ipoctal232-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void nop(void) diff --git a/qemu/tests/ivshmem-test.c b/qemu/tests/ivshmem-test.c new file mode 100644 index 000000000..c027ff1e0 --- /dev/null +++ b/qemu/tests/ivshmem-test.c @@ -0,0 +1,518 @@ +/* + * QTest testcase for ivshmem + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * Copyright (c) 2015 Red Hat, Inc. + * + * 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 +#include +#include +#include "contrib/ivshmem-server/ivshmem-server.h" +#include "libqos/pci-pc.h" +#include "libqtest.h" +#include "qemu-common.h" + +#define TMPSHMSIZE (1 << 20) +static char *tmpshm; +static void *tmpshmem; +static char *tmpdir; +static char *tmpserver; + +static void save_fn(QPCIDevice *dev, int devfn, void *data) +{ + QPCIDevice **pdev = (QPCIDevice **) data; + + *pdev = dev; +} + +static QPCIDevice *get_device(QPCIBus *pcibus) +{ + QPCIDevice *dev; + + dev = NULL; + qpci_device_foreach(pcibus, 0x1af4, 0x1110, save_fn, &dev); + g_assert(dev != NULL); + + return dev; +} + +typedef struct _IVState { + QTestState *qtest; + void *reg_base, *mem_base; + QPCIBus *pcibus; + QPCIDevice *dev; +} IVState; + +enum Reg { + INTRMASK = 0, + INTRSTATUS = 4, + IVPOSITION = 8, + DOORBELL = 12, +}; + +static const char* reg2str(enum Reg reg) { + switch (reg) { + case INTRMASK: + return "IntrMask"; + case INTRSTATUS: + return "IntrStatus"; + case IVPOSITION: + return "IVPosition"; + case DOORBELL: + return "DoorBell"; + default: + return NULL; + } +} + +static inline unsigned in_reg(IVState *s, enum Reg reg) +{ + const char *name = reg2str(reg); + QTestState *qtest = global_qtest; + unsigned res; + + global_qtest = s->qtest; + res = qpci_io_readl(s->dev, s->reg_base + reg); + g_test_message("*%s -> %x\n", name, res); + global_qtest = qtest; + + return res; +} + +static inline void out_reg(IVState *s, enum Reg reg, unsigned v) +{ + const char *name = reg2str(reg); + QTestState *qtest = global_qtest; + + global_qtest = s->qtest; + g_test_message("%x -> *%s\n", v, name); + qpci_io_writel(s->dev, s->reg_base + reg, v); + global_qtest = qtest; +} + +static void cleanup_vm(IVState *s) +{ + g_free(s->dev); + qpci_free_pc(s->pcibus); + qtest_quit(s->qtest); +} + +static void setup_vm_cmd(IVState *s, const char *cmd, bool msix) +{ + uint64_t barsize; + + s->qtest = qtest_start(cmd); + s->pcibus = qpci_init_pc(); + s->dev = get_device(s->pcibus); + + s->reg_base = qpci_iomap(s->dev, 0, &barsize); + g_assert_nonnull(s->reg_base); + g_assert_cmpuint(barsize, ==, 256); + + if (msix) { + qpci_msix_enable(s->dev); + } + + s->mem_base = qpci_iomap(s->dev, 2, &barsize); + g_assert_nonnull(s->mem_base); + g_assert_cmpuint(barsize, ==, TMPSHMSIZE); + + qpci_device_enable(s->dev); +} + +static void setup_vm(IVState *s) +{ + char *cmd = g_strdup_printf("-object memory-backend-file" + ",id=mb1,size=1M,share,mem-path=/dev/shm%s" + " -device ivshmem-plain,memdev=mb1", tmpshm); + + setup_vm_cmd(s, cmd, false); + + g_free(cmd); +} + +static void test_ivshmem_single(void) +{ + IVState state, *s; + uint32_t data[1024]; + int i; + + setup_vm(&state); + s = &state; + + /* initial state of readable registers */ + g_assert_cmpuint(in_reg(s, INTRMASK), ==, 0); + g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 0); + g_assert_cmpuint(in_reg(s, IVPOSITION), ==, 0); + + /* trigger interrupt via registers */ + out_reg(s, INTRMASK, 0xffffffff); + g_assert_cmpuint(in_reg(s, INTRMASK), ==, 0xffffffff); + out_reg(s, INTRSTATUS, 1); + /* check interrupt status */ + g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 1); + /* reading clears */ + g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 0); + /* TODO intercept actual interrupt (needs qtest work) */ + + /* invalid register access */ + out_reg(s, IVPOSITION, 1); + in_reg(s, DOORBELL); + + /* ring the (non-functional) doorbell */ + out_reg(s, DOORBELL, 8 << 16); + + /* write shared memory */ + for (i = 0; i < G_N_ELEMENTS(data); i++) { + data[i] = i; + } + qtest_memwrite(s->qtest, (uintptr_t)s->mem_base, data, sizeof(data)); + + /* verify write */ + for (i = 0; i < G_N_ELEMENTS(data); i++) { + g_assert_cmpuint(((uint32_t *)tmpshmem)[i], ==, i); + } + + /* read it back and verify read */ + memset(data, 0, sizeof(data)); + qtest_memread(s->qtest, (uintptr_t)s->mem_base, data, sizeof(data)); + for (i = 0; i < G_N_ELEMENTS(data); i++) { + g_assert_cmpuint(data[i], ==, i); + } + + cleanup_vm(s); +} + +static void test_ivshmem_pair(void) +{ + IVState state1, state2, *s1, *s2; + char *data; + int i; + + setup_vm(&state1); + s1 = &state1; + setup_vm(&state2); + s2 = &state2; + + data = g_malloc0(TMPSHMSIZE); + + /* host write, guest 1 & 2 read */ + memset(tmpshmem, 0x42, TMPSHMSIZE); + qtest_memread(s1->qtest, (uintptr_t)s1->mem_base, data, TMPSHMSIZE); + for (i = 0; i < TMPSHMSIZE; i++) { + g_assert_cmpuint(data[i], ==, 0x42); + } + qtest_memread(s2->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE); + for (i = 0; i < TMPSHMSIZE; i++) { + g_assert_cmpuint(data[i], ==, 0x42); + } + + /* guest 1 write, guest 2 read */ + memset(data, 0x43, TMPSHMSIZE); + qtest_memwrite(s1->qtest, (uintptr_t)s1->mem_base, data, TMPSHMSIZE); + memset(data, 0, TMPSHMSIZE); + qtest_memread(s2->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE); + for (i = 0; i < TMPSHMSIZE; i++) { + g_assert_cmpuint(data[i], ==, 0x43); + } + + /* guest 2 write, guest 1 read */ + memset(data, 0x44, TMPSHMSIZE); + qtest_memwrite(s2->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE); + memset(data, 0, TMPSHMSIZE); + qtest_memread(s1->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE); + for (i = 0; i < TMPSHMSIZE; i++) { + g_assert_cmpuint(data[i], ==, 0x44); + } + + cleanup_vm(s1); + cleanup_vm(s2); + g_free(data); +} + +typedef struct ServerThread { + GThread *thread; + IvshmemServer *server; + int pipe[2]; /* to handle quit */ +} ServerThread; + +static void *server_thread(void *data) +{ + ServerThread *t = data; + IvshmemServer *server = t->server; + + while (true) { + fd_set fds; + int maxfd, ret; + + FD_ZERO(&fds); + FD_SET(t->pipe[0], &fds); + maxfd = t->pipe[0] + 1; + + ivshmem_server_get_fds(server, &fds, &maxfd); + + ret = select(maxfd, &fds, NULL, NULL, NULL); + + if (ret < 0) { + if (errno == EINTR) { + continue; + } + + g_critical("select error: %s\n", strerror(errno)); + break; + } + if (ret == 0) { + continue; + } + + if (FD_ISSET(t->pipe[0], &fds)) { + break; + } + + if (ivshmem_server_handle_fds(server, &fds, maxfd) < 0) { + g_critical("ivshmem_server_handle_fds() failed\n"); + break; + } + } + + return NULL; +} + +static void setup_vm_with_server(IVState *s, int nvectors, bool msi) +{ + char *cmd = g_strdup_printf("-chardev socket,id=chr0,path=%s,nowait " + "-device ivshmem%s,chardev=chr0,vectors=%d", + tmpserver, + msi ? "-doorbell" : ",size=1M,msi=off", + nvectors); + + setup_vm_cmd(s, cmd, msi); + + g_free(cmd); +} + +static void test_ivshmem_server(bool msi) +{ + IVState state1, state2, *s1, *s2; + ServerThread thread; + IvshmemServer server; + int ret, vm1, vm2; + int nvectors = 2; + guint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; + + ret = ivshmem_server_init(&server, tmpserver, tmpshm, true, + TMPSHMSIZE, nvectors, + g_test_verbose()); + g_assert_cmpint(ret, ==, 0); + + ret = ivshmem_server_start(&server); + g_assert_cmpint(ret, ==, 0); + + thread.server = &server; + ret = pipe(thread.pipe); + g_assert_cmpint(ret, ==, 0); + thread.thread = g_thread_new("ivshmem-server", server_thread, &thread); + g_assert(thread.thread != NULL); + + setup_vm_with_server(&state1, nvectors, msi); + s1 = &state1; + setup_vm_with_server(&state2, nvectors, msi); + s2 = &state2; + + /* check got different VM ids */ + vm1 = in_reg(s1, IVPOSITION); + vm2 = in_reg(s2, IVPOSITION); + g_assert_cmpint(vm1, >=, 0); + g_assert_cmpint(vm2, >=, 0); + g_assert_cmpint(vm1, !=, vm2); + + /* check number of MSI-X vectors */ + global_qtest = s1->qtest; + if (msi) { + ret = qpci_msix_table_size(s1->dev); + g_assert_cmpuint(ret, ==, nvectors); + } + + /* TODO test behavior before MSI-X is enabled */ + + /* ping vm2 -> vm1 on vector 0 */ + if (msi) { + ret = qpci_msix_pending(s1->dev, 0); + g_assert_cmpuint(ret, ==, 0); + } else { + g_assert_cmpuint(in_reg(s1, INTRSTATUS), ==, 0); + } + out_reg(s2, DOORBELL, vm1 << 16); + do { + g_usleep(10000); + ret = msi ? qpci_msix_pending(s1->dev, 0) : in_reg(s1, INTRSTATUS); + } while (ret == 0 && g_get_monotonic_time() < end_time); + g_assert_cmpuint(ret, !=, 0); + + /* ping vm1 -> vm2 on vector 1 */ + global_qtest = s2->qtest; + if (msi) { + ret = qpci_msix_pending(s2->dev, 1); + g_assert_cmpuint(ret, ==, 0); + } else { + g_assert_cmpuint(in_reg(s2, INTRSTATUS), ==, 0); + } + out_reg(s1, DOORBELL, vm2 << 16 | 1); + do { + g_usleep(10000); + ret = msi ? qpci_msix_pending(s2->dev, 1) : in_reg(s2, INTRSTATUS); + } while (ret == 0 && g_get_monotonic_time() < end_time); + g_assert_cmpuint(ret, !=, 0); + + cleanup_vm(s2); + cleanup_vm(s1); + + if (qemu_write_full(thread.pipe[1], "q", 1) != 1) { + g_error("qemu_write_full: %s", g_strerror(errno)); + } + + g_thread_join(thread.thread); + + ivshmem_server_close(&server); + close(thread.pipe[1]); + close(thread.pipe[0]); +} + +static void test_ivshmem_server_msi(void) +{ + test_ivshmem_server(true); +} + +static void test_ivshmem_server_irq(void) +{ + test_ivshmem_server(false); +} + +#define PCI_SLOT_HP 0x06 + +static void test_ivshmem_hotplug(void) +{ + gchar *opts; + + qtest_start(""); + + opts = g_strdup_printf("'shm': '%s', 'size': '1M'", tmpshm); + + qpci_plug_device_test("ivshmem", "iv1", PCI_SLOT_HP, opts); + qpci_unplug_acpi_device_test("iv1", PCI_SLOT_HP); + + qtest_end(); + g_free(opts); +} + +static void test_ivshmem_memdev(void) +{ + IVState state; + + /* just for the sake of checking memory-backend property */ + setup_vm_cmd(&state, "-object memory-backend-ram,size=1M,id=mb1" + " -device ivshmem-plain,memdev=mb1", false); + + cleanup_vm(&state); +} + +static void cleanup(void) +{ + if (tmpshmem) { + munmap(tmpshmem, TMPSHMSIZE); + tmpshmem = NULL; + } + + if (tmpshm) { + shm_unlink(tmpshm); + g_free(tmpshm); + tmpshm = NULL; + } + + if (tmpserver) { + g_unlink(tmpserver); + g_free(tmpserver); + tmpserver = NULL; + } + + if (tmpdir) { + g_rmdir(tmpdir); + tmpdir = NULL; + } +} + +static void abrt_handler(void *data) +{ + cleanup(); +} + +static gchar *mktempshm(int size, int *fd) +{ + while (true) { + gchar *name; + + name = g_strdup_printf("/qtest-%u-%u", getpid(), g_random_int()); + *fd = shm_open(name, O_CREAT|O_RDWR|O_EXCL, + S_IRWXU|S_IRWXG|S_IRWXO); + if (*fd > 0) { + g_assert(ftruncate(*fd, size) == 0); + return name; + } + + g_free(name); + + if (errno != EEXIST) { + perror("shm_open"); + return NULL; + } + } +} + +int main(int argc, char **argv) +{ + int ret, fd; + gchar dir[] = "/tmp/ivshmem-test.XXXXXX"; + +#if !GLIB_CHECK_VERSION(2, 31, 0) + if (!g_thread_supported()) { + g_thread_init(NULL); + } +#endif + + g_test_init(&argc, &argv, NULL); + + qtest_add_abrt_handler(abrt_handler, NULL); + /* shm */ + tmpshm = mktempshm(TMPSHMSIZE, &fd); + if (!tmpshm) { + return 0; + } + tmpshmem = mmap(0, TMPSHMSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + g_assert(tmpshmem != MAP_FAILED); + /* server */ + if (mkdtemp(dir) == NULL) { + g_error("mkdtemp: %s", g_strerror(errno)); + } + tmpdir = dir; + tmpserver = g_strconcat(tmpdir, "/server", NULL); + + qtest_add_func("/ivshmem/single", test_ivshmem_single); + qtest_add_func("/ivshmem/hotplug", test_ivshmem_hotplug); + qtest_add_func("/ivshmem/memdev", test_ivshmem_memdev); + if (g_test_slow()) { + qtest_add_func("/ivshmem/pair", test_ivshmem_pair); + qtest_add_func("/ivshmem/server-msi", test_ivshmem_server_msi); + qtest_add_func("/ivshmem/server-irq", test_ivshmem_server_irq); + } + + ret = g_test_run(); + + cleanup(); + + return ret; +} diff --git a/qemu/tests/libqos/ahci.c b/qemu/tests/libqos/ahci.c index cf66b3e32..ac6c155c8 100644 --- a/qemu/tests/libqos/ahci.c +++ b/qemu/tests/libqos/ahci.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include "libqtest.h" @@ -74,7 +75,11 @@ AHCICommandProp ahci_command_properties[] = { .lba48 = true, .write = true, .ncq = true }, { .cmd = CMD_READ_MAX, .lba28 = true }, { .cmd = CMD_READ_MAX_EXT, .lba48 = true }, - { .cmd = CMD_FLUSH_CACHE, .data = false } + { .cmd = CMD_FLUSH_CACHE, .data = false }, + { .cmd = CMD_PACKET, .data = true, .size = 16, + .atapi = true, .pio = true }, + { .cmd = CMD_PACKET_ID, .data = true, .pio = true, + .size = 512, .read = true } }; struct AHCICommand { @@ -90,7 +95,7 @@ struct AHCICommand { /* Data to be transferred to the guest */ AHCICommandHeader header; RegH2DFIS fis; - void *atapi_cmd; + unsigned char *atapi_cmd; }; /** @@ -110,6 +115,11 @@ void ahci_free(AHCIQState *ahci, uint64_t addr) qfree(ahci->parent, addr); } +bool is_atapi(AHCIQState *ahci, uint8_t port) +{ + return ahci_px_rreg(ahci, port, AHCI_PX_SIG) == AHCI_SIGNATURE_CDROM; +} + /** * Locate, verify, and return a handle to the AHCI device. */ @@ -592,6 +602,82 @@ inline unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd) return (bytes + bytes_per_prd - 1) / bytes_per_prd; } +const AHCIOpts default_opts = { .size = 0 }; + +/** + * ahci_exec: execute a given command on a specific + * AHCI port. + * + * @ahci: The device to send the command to + * @port: The port number of the SATA device we wish + * to have execute this command + * @op: The S/ATA command to execute, or if opts.atapi + * is true, the SCSI command code. + * @opts: Optional arguments to modify execution behavior. + */ +void ahci_exec(AHCIQState *ahci, uint8_t port, + uint8_t op, const AHCIOpts *opts_in) +{ + AHCICommand *cmd; + int rc; + AHCIOpts *opts; + + opts = g_memdup((opts_in == NULL ? &default_opts : opts_in), + sizeof(AHCIOpts)); + + /* No guest buffer provided, create one. */ + if (opts->size && !opts->buffer) { + opts->buffer = ahci_alloc(ahci, opts->size); + g_assert(opts->buffer); + qmemset(opts->buffer, 0x00, opts->size); + } + + /* Command creation */ + if (opts->atapi) { + cmd = ahci_atapi_command_create(op); + if (opts->atapi_dma) { + ahci_command_enable_atapi_dma(cmd); + } + } else { + cmd = ahci_command_create(op); + } + ahci_command_adjust(cmd, opts->lba, opts->buffer, + opts->size, opts->prd_size); + + if (opts->pre_cb) { + rc = opts->pre_cb(ahci, cmd, opts); + g_assert_cmpint(rc, ==, 0); + } + + /* Write command to memory and issue it */ + ahci_command_commit(ahci, cmd, port); + ahci_command_issue_async(ahci, cmd); + if (opts->error) { + qmp_eventwait("STOP"); + } + if (opts->mid_cb) { + rc = opts->mid_cb(ahci, cmd, opts); + g_assert_cmpint(rc, ==, 0); + } + if (opts->error) { + qmp_async("{'execute':'cont' }"); + qmp_eventwait("RESUME"); + } + + /* Wait for command to complete and verify sanity */ + ahci_command_wait(ahci, cmd); + ahci_command_verify(ahci, cmd); + if (opts->post_cb) { + rc = opts->post_cb(ahci, cmd, opts); + g_assert_cmpint(rc, ==, 0); + } + ahci_command_free(cmd); + if (opts->buffer != opts_in->buffer) { + ahci_free(ahci, opts->buffer); + } + g_free(opts); +} + /* Issue a command, expecting it to fail and STOP the VM */ AHCICommand *ahci_guest_io_halt(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, uint64_t buffer, @@ -659,16 +745,16 @@ void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, props = ahci_command_find(ide_cmd); g_assert(props); ptr = ahci_alloc(ahci, bufsize); - g_assert(ptr); + g_assert(!bufsize || ptr); qmemset(ptr, 0x00, bufsize); - if (props->write) { + if (bufsize && props->write) { bufwrite(ptr, buffer, bufsize); } ahci_guest_io(ahci, port, ide_cmd, ptr, bufsize, sector); - if (props->read) { + if (bufsize && props->read) { bufread(ptr, buffer, bufsize); } @@ -731,6 +817,18 @@ static void command_table_init(AHCICommand *cmd) memset(fis->aux, 0x00, ARRAY_SIZE(fis->aux)); } +void ahci_command_enable_atapi_dma(AHCICommand *cmd) +{ + RegH2DFIS *fis = &(cmd->fis); + g_assert(cmd->props->atapi); + fis->feature_low |= 0x01; + cmd->interrupts &= ~AHCI_PX_IS_PSS; + cmd->props->dma = true; + cmd->props->pio = false; + /* BUG: We expect the DMA Setup interrupt for DMA commands */ + /* cmd->interrupts |= AHCI_PX_IS_DSS; */ +} + AHCICommand *ahci_command_create(uint8_t command_name) { AHCICommandProp *props = ahci_command_find(command_name); @@ -742,10 +840,10 @@ AHCICommand *ahci_command_create(uint8_t command_name) g_assert(!(props->lba28 && props->lba48)); g_assert(!(props->read && props->write)); g_assert(!props->size || props->data); - g_assert(!props->ncq || (props->ncq && props->lba48)); + g_assert(!props->ncq || props->lba48); /* Defaults and book-keeping */ - cmd->props = props; + cmd->props = g_memdup(props, sizeof(AHCICommandProp)); cmd->name = command_name; cmd->xbytes = props->size; cmd->prd_size = 4096; @@ -767,8 +865,23 @@ AHCICommand *ahci_command_create(uint8_t command_name) return cmd; } +AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd) +{ + AHCICommand *cmd = ahci_command_create(CMD_PACKET); + cmd->atapi_cmd = g_malloc0(16); + cmd->atapi_cmd[0] = scsi_cmd; + /* ATAPI needs a PIO transfer chunk size set inside of the LBA registers. + * The block/sector size is a natural default. */ + cmd->fis.lba_lo[1] = ATAPI_SECTOR_SIZE >> 8 & 0xFF; + cmd->fis.lba_lo[2] = ATAPI_SECTOR_SIZE & 0xFF; + + return cmd; +} + void ahci_command_free(AHCICommand *cmd) { + g_free(cmd->atapi_cmd); + g_free(cmd->props); g_free(cmd); } @@ -782,10 +895,34 @@ void ahci_command_clr_flags(AHCICommand *cmd, uint16_t cmdh_flags) cmd->header.flags &= ~cmdh_flags; } +static void ahci_atapi_command_set_offset(AHCICommand *cmd, uint64_t lba) +{ + unsigned char *cbd = cmd->atapi_cmd; + g_assert(cbd); + + switch (cbd[0]) { + case CMD_ATAPI_READ_10: + g_assert_cmpuint(lba, <=, UINT32_MAX); + stl_be_p(&cbd[2], lba); + break; + default: + /* SCSI doesn't have uniform packet formats, + * so you have to add support for it manually. Sorry! */ + g_assert_not_reached(); + } +} + void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect) { RegH2DFIS *fis = &(cmd->fis); - if (cmd->props->lba28) { + + if (cmd->props->atapi) { + ahci_atapi_command_set_offset(cmd, lba_sect); + return; + } else if (!cmd->props->data && !lba_sect) { + /* Not meaningful, ignore. */ + return; + } else if (cmd->props->lba28) { g_assert_cmphex(lba_sect, <=, 0xFFFFFFF); } else if (cmd->props->lba48 || cmd->props->ncq) { g_assert_cmphex(lba_sect, <=, 0xFFFFFFFFFFFF); @@ -811,6 +948,24 @@ void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer) cmd->buffer = buffer; } +static void ahci_atapi_set_size(AHCICommand *cmd, uint64_t xbytes) +{ + unsigned char *cbd = cmd->atapi_cmd; + uint64_t nsectors = xbytes / 2048; + g_assert(cbd); + + switch (cbd[0]) { + case CMD_ATAPI_READ_10: + g_assert_cmpuint(nsectors, <=, UINT16_MAX); + stw_be_p(&cbd[7], nsectors); + break; + default: + /* SCSI doesn't have uniform packet formats, + * so you have to add support for it manually. Sorry! */ + g_assert_not_reached(); + } +} + void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes, unsigned prd_size) { @@ -829,6 +984,8 @@ void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes, NCQFIS *nfis = (NCQFIS *)&(cmd->fis); nfis->sector_low = sect_count & 0xFF; nfis->sector_hi = (sect_count >> 8) & 0xFF; + } else if (cmd->props->atapi) { + ahci_atapi_set_size(cmd, xbytes); } else { cmd->fis.count = sect_count; } @@ -877,9 +1034,14 @@ void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port) g_assert((table_ptr & 0x7F) == 0x00); cmd->header.ctba = table_ptr; - /* Commit the command header and command FIS */ + /* Commit the command header (part of the Command List Buffer) */ ahci_set_command_header(ahci, port, cmd->slot, &(cmd->header)); + /* Now, write the command table (FIS, ACMD, and PRDT) -- FIS first, */ ahci_write_fis(ahci, cmd); + /* Then ATAPI CMD, if needed */ + if (cmd->props->atapi) { + memwrite(table_ptr + 0x40, cmd->atapi_cmd, 16); + } /* Construct and write the PRDs to the command table */ g_assert_cmphex(prdtl, ==, cmd->header.prdtl); diff --git a/qemu/tests/libqos/ahci.h b/qemu/tests/libqos/ahci.h index cffc2c351..71dd7a6e5 100644 --- a/qemu/tests/libqos/ahci.h +++ b/qemu/tests/libqos/ahci.h @@ -25,9 +25,6 @@ * THE SOFTWARE. */ -#include -#include -#include #include "libqos/libqos.h" #include "libqos/pci.h" #include "libqos/malloc-pc.h" @@ -244,6 +241,10 @@ #define AHCI_VERSION_1_3 (0x00010300) #define AHCI_SECTOR_SIZE (512) +#define ATAPI_SECTOR_SIZE (2048) + +#define AHCI_SIGNATURE_CDROM (0xeb140101) +#define AHCI_SIGNATURE_DISK (0x00000101) /* FIS types */ enum { @@ -277,11 +278,18 @@ enum { CMD_READ_MAX_EXT = 0x27, CMD_FLUSH_CACHE = 0xE7, CMD_IDENTIFY = 0xEC, + CMD_PACKET = 0xA0, + CMD_PACKET_ID = 0xA1, /* NCQ */ READ_FPDMA_QUEUED = 0x60, WRITE_FPDMA_QUEUED = 0x61, }; +/* ATAPI Commands */ +enum { + CMD_ATAPI_READ_10 = 0x28, +}; + /* AHCI Command Header Flags & Masks*/ #define CMDH_CFL (0x1F) #define CMDH_ATAPI (0x20) @@ -451,6 +459,21 @@ typedef struct PRD { /* Opaque, defined within ahci.c */ typedef struct AHCICommand AHCICommand; +/* Options to ahci_exec */ +typedef struct AHCIOpts { + size_t size; + unsigned prd_size; + uint64_t lba; + uint64_t buffer; + bool atapi; + bool atapi_dma; + bool error; + int (*pre_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *); + int (*mid_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *); + int (*post_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *); + void *opaque; +} AHCIOpts; + /*** Macro Utilities ***/ #define BITANY(data, mask) (((data) & (mask)) != 0) #define BITSET(data, mask) (((data) & (mask)) == (mask)) @@ -527,14 +550,28 @@ static inline void ahci_px_clr(AHCIQState *ahci, uint8_t port, /*** Prototypes ***/ uint64_t ahci_alloc(AHCIQState *ahci, size_t bytes); void ahci_free(AHCIQState *ahci, uint64_t addr); +void ahci_clean_mem(AHCIQState *ahci); + +/* Device management */ QPCIDevice *get_ahci_device(uint32_t *fingerprint); void free_ahci_device(QPCIDevice *dev); -void ahci_clean_mem(AHCIQState *ahci); void ahci_pci_enable(AHCIQState *ahci); void start_ahci_device(AHCIQState *ahci); void ahci_hba_enable(AHCIQState *ahci); + +/* Port Management */ unsigned ahci_port_select(AHCIQState *ahci); void ahci_port_clear(AHCIQState *ahci, uint8_t port); + +/* Command header / table management */ +unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port); +void ahci_get_command_header(AHCIQState *ahci, uint8_t port, + uint8_t slot, AHCICommandHeader *cmd); +void ahci_set_command_header(AHCIQState *ahci, uint8_t port, + uint8_t slot, AHCICommandHeader *cmd); +void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot); + +/* AHCI sanity check routines */ void ahci_port_check_error(AHCIQState *ahci, uint8_t port); void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port, uint32_t intr_mask); @@ -543,14 +580,12 @@ void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot); void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot, size_t buffsize); void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd); -void ahci_get_command_header(AHCIQState *ahci, uint8_t port, - uint8_t slot, AHCICommandHeader *cmd); -void ahci_set_command_header(AHCIQState *ahci, uint8_t port, - uint8_t slot, AHCICommandHeader *cmd); -void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot); -void ahci_write_fis(AHCIQState *ahci, AHCICommand *cmd); -unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port); + +/* Misc */ +bool is_atapi(AHCIQState *ahci, uint8_t port); unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd); + +/* Command: Macro level execution */ void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, uint64_t gbuffer, size_t size, uint64_t sector); AHCICommand *ahci_guest_io_halt(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, @@ -558,9 +593,12 @@ AHCICommand *ahci_guest_io_halt(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, void ahci_guest_io_resume(AHCIQState *ahci, AHCICommand *cmd); void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, void *buffer, size_t bufsize, uint64_t sector); +void ahci_exec(AHCIQState *ahci, uint8_t port, + uint8_t op, const AHCIOpts *opts); -/* Command Lifecycle */ +/* Command: Fine-grained lifecycle */ AHCICommand *ahci_command_create(uint8_t command_name); +AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd); void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port); void ahci_command_issue(AHCIQState *ahci, AHCICommand *cmd); void ahci_command_issue_async(AHCIQState *ahci, AHCICommand *cmd); @@ -568,7 +606,7 @@ void ahci_command_wait(AHCIQState *ahci, AHCICommand *cmd); void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd); void ahci_command_free(AHCICommand *cmd); -/* Command adjustments */ +/* Command: adjustments */ void ahci_command_set_flags(AHCICommand *cmd, uint16_t cmdh_flags); void ahci_command_clr_flags(AHCICommand *cmd, uint16_t cmdh_flags); void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect); @@ -577,10 +615,13 @@ void ahci_command_set_size(AHCICommand *cmd, uint64_t xbytes); void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size); void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes, unsigned prd_size); +void ahci_command_set_acmd(AHCICommand *cmd, void *acmd); +void ahci_command_enable_atapi_dma(AHCICommand *cmd); void ahci_command_adjust(AHCICommand *cmd, uint64_t lba_sect, uint64_t gbuffer, uint64_t xbytes, unsigned prd_size); -/* Command Misc */ +/* Command: Misc */ uint8_t ahci_command_slot(AHCICommand *cmd); +void ahci_write_fis(AHCIQState *ahci, AHCICommand *cmd); #endif diff --git a/qemu/tests/libqos/fw_cfg.c b/qemu/tests/libqos/fw_cfg.c index ef00fedf1..76894d575 100644 --- a/qemu/tests/libqos/fw_cfg.c +++ b/qemu/tests/libqos/fw_cfg.c @@ -12,6 +12,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include #include "libqos/fw_cfg.h" #include "libqtest.h" diff --git a/qemu/tests/libqos/fw_cfg.h b/qemu/tests/libqos/fw_cfg.h index 61b1548b4..e8371b231 100644 --- a/qemu/tests/libqos/fw_cfg.h +++ b/qemu/tests/libqos/fw_cfg.h @@ -13,8 +13,6 @@ #ifndef LIBQOS_FW_CFG_H #define LIBQOS_FW_CFG_H -#include -#include typedef struct QFWCFG QFWCFG; diff --git a/qemu/tests/libqos/i2c-imx.c b/qemu/tests/libqos/i2c-imx.c new file mode 100644 index 000000000..51c3468f9 --- /dev/null +++ b/qemu/tests/libqos/i2c-imx.c @@ -0,0 +1,208 @@ +/* + * QTest i.MX I2C driver + * + * Copyright (c) 2013 Jean-Christophe Dubois + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "libqos/i2c.h" + +#include + +#include "libqtest.h" + +#include "hw/i2c/imx_i2c.h" + +enum IMXI2CDirection { + IMX_I2C_READ, + IMX_I2C_WRITE, +}; + +typedef struct IMXI2C { + I2CAdapter parent; + + uint64_t addr; +} IMXI2C; + + +static void imx_i2c_set_slave_addr(IMXI2C *s, uint8_t addr, + enum IMXI2CDirection direction) +{ + writeb(s->addr + I2DR_ADDR, (addr << 1) | + (direction == IMX_I2C_READ ? 1 : 0)); +} + +static void imx_i2c_send(I2CAdapter *i2c, uint8_t addr, + const uint8_t *buf, uint16_t len) +{ + IMXI2C *s = (IMXI2C *)i2c; + uint8_t data; + uint8_t status; + uint16_t size = 0; + + if (!len) { + return; + } + + /* set the bus for write */ + data = I2CR_IEN | + I2CR_IIEN | + I2CR_MSTA | + I2CR_MTX | + I2CR_TXAK; + + writeb(s->addr + I2CR_ADDR, data); + status = readb(s->addr + I2SR_ADDR); + g_assert((status & I2SR_IBB) != 0); + + /* set the slave address */ + imx_i2c_set_slave_addr(s, addr, IMX_I2C_WRITE); + status = readb(s->addr + I2SR_ADDR); + g_assert((status & I2SR_IIF) != 0); + g_assert((status & I2SR_RXAK) == 0); + + /* ack the interrupt */ + writeb(s->addr + I2SR_ADDR, 0); + status = readb(s->addr + I2SR_ADDR); + g_assert((status & I2SR_IIF) == 0); + + while (size < len) { + /* check we are still busy */ + status = readb(s->addr + I2SR_ADDR); + g_assert((status & I2SR_IBB) != 0); + + /* write the data */ + writeb(s->addr + I2DR_ADDR, buf[size]); + status = readb(s->addr + I2SR_ADDR); + g_assert((status & I2SR_IIF) != 0); + g_assert((status & I2SR_RXAK) == 0); + + /* ack the interrupt */ + writeb(s->addr + I2SR_ADDR, 0); + status = readb(s->addr + I2SR_ADDR); + g_assert((status & I2SR_IIF) == 0); + + size++; + } + + /* release the bus */ + data &= ~(I2CR_MSTA | I2CR_MTX); + writeb(s->addr + I2CR_ADDR, data); + status = readb(s->addr + I2SR_ADDR); + g_assert((status & I2SR_IBB) == 0); +} + +static void imx_i2c_recv(I2CAdapter *i2c, uint8_t addr, + uint8_t *buf, uint16_t len) +{ + IMXI2C *s = (IMXI2C *)i2c; + uint8_t data; + uint8_t status; + uint16_t size = 0; + + if (!len) { + return; + } + + /* set the bus for write */ + data = I2CR_IEN | + I2CR_IIEN | + I2CR_MSTA | + I2CR_MTX | + I2CR_TXAK; + + writeb(s->addr + I2CR_ADDR, data); + status = readb(s->addr + I2SR_ADDR); + g_assert((status & I2SR_IBB) != 0); + + /* set the slave address */ + imx_i2c_set_slave_addr(s, addr, IMX_I2C_READ); + status = readb(s->addr + I2SR_ADDR); + g_assert((status & I2SR_IIF) != 0); + g_assert((status & I2SR_RXAK) == 0); + + /* ack the interrupt */ + writeb(s->addr + I2SR_ADDR, 0); + status = readb(s->addr + I2SR_ADDR); + g_assert((status & I2SR_IIF) == 0); + + /* set the bus for read */ + data &= ~I2CR_MTX; + /* if only one byte don't ack */ + if (len != 1) { + data &= ~I2CR_TXAK; + } + writeb(s->addr + I2CR_ADDR, data); + status = readb(s->addr + I2SR_ADDR); + g_assert((status & I2SR_IBB) != 0); + + /* dummy read */ + readb(s->addr + I2DR_ADDR); + status = readb(s->addr + I2SR_ADDR); + g_assert((status & I2SR_IIF) != 0); + + /* ack the interrupt */ + writeb(s->addr + I2SR_ADDR, 0); + status = readb(s->addr + I2SR_ADDR); + g_assert((status & I2SR_IIF) == 0); + + while (size < len) { + /* check we are still busy */ + status = readb(s->addr + I2SR_ADDR); + g_assert((status & I2SR_IBB) != 0); + + if (size == (len - 1)) { + /* stop the read transaction */ + data &= ~(I2CR_MSTA | I2CR_MTX); + } else { + /* ack the data read */ + data |= I2CR_TXAK; + } + writeb(s->addr + I2CR_ADDR, data); + + /* read the data */ + buf[size] = readb(s->addr + I2DR_ADDR); + + if (size != (len - 1)) { + status = readb(s->addr + I2SR_ADDR); + g_assert((status & I2SR_IIF) != 0); + + /* ack the interrupt */ + writeb(s->addr + I2SR_ADDR, 0); + } + + status = readb(s->addr + I2SR_ADDR); + g_assert((status & I2SR_IIF) == 0); + + size++; + } + + status = readb(s->addr + I2SR_ADDR); + g_assert((status & I2SR_IBB) == 0); +} + +I2CAdapter *imx_i2c_create(uint64_t addr) +{ + IMXI2C *s = g_malloc0(sizeof(*s)); + I2CAdapter *i2c = (I2CAdapter *)s; + + s->addr = addr; + + i2c->send = imx_i2c_send; + i2c->recv = imx_i2c_recv; + + return i2c; +} diff --git a/qemu/tests/libqos/i2c-omap.c b/qemu/tests/libqos/i2c-omap.c index 3d4d45d84..2028f2f14 100644 --- a/qemu/tests/libqos/i2c-omap.c +++ b/qemu/tests/libqos/i2c-omap.c @@ -6,12 +6,11 @@ * 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 "libqos/i2c.h" #include -#include -#include "qemu/osdep.h" #include "qemu/bswap.h" #include "libqtest.h" diff --git a/qemu/tests/libqos/i2c.c b/qemu/tests/libqos/i2c.c index da7592f71..23bc2a3eb 100644 --- a/qemu/tests/libqos/i2c.c +++ b/qemu/tests/libqos/i2c.c @@ -6,6 +6,7 @@ * 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 "libqos/i2c.h" #include "libqtest.h" diff --git a/qemu/tests/libqos/i2c.h b/qemu/tests/libqos/i2c.h index 1ce9af405..6e648f922 100644 --- a/qemu/tests/libqos/i2c.h +++ b/qemu/tests/libqos/i2c.h @@ -9,7 +9,6 @@ #ifndef LIBQOS_I2C_H #define LIBQOS_I2C_H -#include typedef struct I2CAdapter I2CAdapter; struct I2CAdapter { @@ -27,4 +26,7 @@ void i2c_recv(I2CAdapter *i2c, uint8_t addr, /* libi2c-omap.c */ I2CAdapter *omap_i2c_create(uint64_t addr); +/* libi2c-imx.c */ +I2CAdapter *imx_i2c_create(uint64_t addr); + #endif diff --git a/qemu/tests/libqos/libqos-pc.c b/qemu/tests/libqos/libqos-pc.c index 140369937..72b5e3ba0 100644 --- a/qemu/tests/libqos/libqos-pc.c +++ b/qemu/tests/libqos/libqos-pc.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "libqos/libqos-pc.h" #include "libqos/malloc-pc.h" diff --git a/qemu/tests/libqos/libqos.c b/qemu/tests/libqos/libqos.c index fce625b18..79b0b29b4 100644 --- a/qemu/tests/libqos/libqos.c +++ b/qemu/tests/libqos/libqos.c @@ -1,9 +1,5 @@ -#include -#include -#include +#include "qemu/osdep.h" #include -#include -#include #include #include "libqtest.h" @@ -147,6 +143,23 @@ void migrate(QOSState *from, QOSState *to, const char *uri) set_context(to); } +bool have_qemu_img(void) +{ + char *rpath; + const char *path = getenv("QTEST_QEMU_IMG"); + if (!path) { + return false; + } + + rpath = realpath(path, NULL); + if (!rpath) { + return false; + } else { + free(rpath); + return true; + } +} + void mkimg(const char *file, const char *fmt, unsigned size_mb) { gchar *cli; @@ -155,13 +168,14 @@ void mkimg(const char *file, const char *fmt, unsigned size_mb) GError *err = NULL; char *qemu_img_path; gchar *out, *out2; - char *abs_path; + char *qemu_img_abs_path; qemu_img_path = getenv("QTEST_QEMU_IMG"); - abs_path = realpath(qemu_img_path, NULL); - assert(qemu_img_path); + g_assert(qemu_img_path); + qemu_img_abs_path = realpath(qemu_img_path, NULL); + g_assert(qemu_img_abs_path); - cli = g_strdup_printf("%s create -f %s %s %uM", abs_path, + cli = g_strdup_printf("%s create -f %s %s %uM", qemu_img_abs_path, fmt, file, size_mb); ret = g_spawn_command_line_sync(cli, &out, &out2, &rc, &err); if (err) { @@ -183,7 +197,7 @@ void mkimg(const char *file, const char *fmt, unsigned size_mb) g_free(out); g_free(out2); g_free(cli); - free(abs_path); + free(qemu_img_abs_path); } void mkqcow2(const char *file, unsigned size_mb) @@ -212,3 +226,29 @@ void prepare_blkdebug_script(const char *debug_fn, const char *event) ret = fclose(debug_file); g_assert(ret == 0); } + +void generate_pattern(void *buffer, size_t len, size_t cycle_len) +{ + int i, j; + unsigned char *tx = (unsigned char *)buffer; + unsigned char p; + size_t *sx; + + /* Write an indicative pattern that varies and is unique per-cycle */ + p = rand() % 256; + for (i = 0; i < len; i++) { + tx[i] = p++ % 256; + if (i % cycle_len == 0) { + p = rand() % 256; + } + } + + /* force uniqueness by writing an id per-cycle */ + for (i = 0; i < len / cycle_len; i++) { + j = i * cycle_len; + if (j + sizeof(*sx) <= len) { + sx = (size_t *)&tx[j]; + *sx = i; + } + } +} diff --git a/qemu/tests/libqos/libqos.h b/qemu/tests/libqos/libqos.h index e1f14ea6f..ca14d2e9f 100644 --- a/qemu/tests/libqos/libqos.h +++ b/qemu/tests/libqos/libqos.h @@ -19,11 +19,13 @@ typedef struct QOSState { QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap); QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...); void qtest_shutdown(QOSState *qs); +bool have_qemu_img(void); void mkimg(const char *file, const char *fmt, unsigned size_mb); void mkqcow2(const char *file, unsigned size_mb); void set_context(QOSState *s); void migrate(QOSState *from, QOSState *to, const char *uri); void prepare_blkdebug_script(const char *debug_fn, const char *event); +void generate_pattern(void *buffer, size_t len, size_t cycle_len); static inline uint64_t qmalloc(QOSState *q, size_t bytes) { diff --git a/qemu/tests/libqos/malloc-generic.c b/qemu/tests/libqos/malloc-generic.c index d30a2f424..6000df2b8 100644 --- a/qemu/tests/libqos/malloc-generic.c +++ b/qemu/tests/libqos/malloc-generic.c @@ -7,6 +7,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include #include "libqos/malloc-generic.h" #include "libqos/malloc.h" diff --git a/qemu/tests/libqos/malloc-pc.c b/qemu/tests/libqos/malloc-pc.c index 6e253b687..eee706bd6 100644 --- a/qemu/tests/libqos/malloc-pc.c +++ b/qemu/tests/libqos/malloc-pc.c @@ -10,11 +10,11 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "libqos/malloc-pc.h" #include "libqos/fw_cfg.h" -#define NO_QEMU_PROTOS -#include "hw/nvram/fw_cfg.h" +#include "hw/nvram/fw_cfg_keys.h" #include "qemu-common.h" #include diff --git a/qemu/tests/libqos/malloc.c b/qemu/tests/libqos/malloc.c index 82b9df537..c0df52f33 100644 --- a/qemu/tests/libqos/malloc.c +++ b/qemu/tests/libqos/malloc.c @@ -10,10 +10,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "libqos/malloc.h" #include "qemu-common.h" -#include -#include #include typedef QTAILQ_HEAD(MemList, MemBlock) MemList; @@ -270,6 +269,10 @@ uint64_t guest_alloc(QGuestAllocator *allocator, size_t size) uint64_t rsize = size; uint64_t naddr; + if (!size) { + return 0; + } + rsize += (allocator->page_size - 1); rsize &= -allocator->page_size; g_assert_cmpint((allocator->start + rsize), <=, allocator->end); diff --git a/qemu/tests/libqos/malloc.h b/qemu/tests/libqos/malloc.h index 0c6c9b7f3..ae9dac8f6 100644 --- a/qemu/tests/libqos/malloc.h +++ b/qemu/tests/libqos/malloc.h @@ -13,8 +13,6 @@ #ifndef LIBQOS_MALLOC_H #define LIBQOS_MALLOC_H -#include -#include #include "qemu/queue.h" typedef enum { diff --git a/qemu/tests/libqos/pci-pc.c b/qemu/tests/libqos/pci-pc.c index 6dba0db00..77f15e5a0 100644 --- a/qemu/tests/libqos/pci-pc.c +++ b/qemu/tests/libqos/pci-pc.c @@ -10,6 +10,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "libqtest.h" #include "libqos/pci-pc.h" @@ -183,7 +184,9 @@ static void *qpci_pc_iomap(QPCIBus *bus, QPCIDevice *dev, int barno, uint64_t *s if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { uint16_t loc; - g_assert((s->pci_iohole_alloc + size) <= s->pci_iohole_size); + g_assert(QEMU_ALIGN_UP(s->pci_iohole_alloc, size) + size + <= s->pci_iohole_size); + s->pci_iohole_alloc = QEMU_ALIGN_UP(s->pci_iohole_alloc, size); loc = s->pci_iohole_start + s->pci_iohole_alloc; s->pci_iohole_alloc += size; @@ -193,7 +196,9 @@ static void *qpci_pc_iomap(QPCIBus *bus, QPCIDevice *dev, int barno, uint64_t *s } else { uint64_t loc; - g_assert((s->pci_hole_alloc + size) <= s->pci_hole_size); + g_assert(QEMU_ALIGN_UP(s->pci_hole_alloc, size) + size + <= s->pci_hole_size); + s->pci_hole_alloc = QEMU_ALIGN_UP(s->pci_hole_alloc, size); loc = s->pci_hole_start + s->pci_hole_alloc; s->pci_hole_alloc += size; diff --git a/qemu/tests/libqos/pci.c b/qemu/tests/libqos/pci.c index 4e630c250..0e104e14e 100644 --- a/qemu/tests/libqos/pci.c +++ b/qemu/tests/libqos/pci.c @@ -10,6 +10,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "libqos/pci.h" #include "hw/pci/pci_regs.h" @@ -34,11 +35,13 @@ void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, if (vendor_id != -1 && qpci_config_readw(dev, PCI_VENDOR_ID) != vendor_id) { + g_free(dev); continue; } if (device_id != -1 && qpci_config_readw(dev, PCI_DEVICE_ID) != device_id) { + g_free(dev); continue; } diff --git a/qemu/tests/libqos/pci.h b/qemu/tests/libqos/pci.h index dfaee9ec3..c06add8db 100644 --- a/qemu/tests/libqos/pci.h +++ b/qemu/tests/libqos/pci.h @@ -13,7 +13,6 @@ #ifndef LIBQOS_PCI_H #define LIBQOS_PCI_H -#include #include "libqtest.h" #define QPCI_DEVFN(dev, fn) (((dev) << 3) | (fn)) diff --git a/qemu/tests/libqos/usb.c b/qemu/tests/libqos/usb.c index 41d89b848..87efb9078 100644 --- a/qemu/tests/libqos/usb.c +++ b/qemu/tests/libqos/usb.c @@ -11,10 +11,9 @@ * 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 -#include #include "libqtest.h" -#include "qemu/osdep.h" #include "hw/usb/uhci-regs.h" #include "libqos/usb.h" diff --git a/qemu/tests/libqos/virtio-mmio.c b/qemu/tests/libqos/virtio-mmio.c index b3e62e77d..a4382f366 100644 --- a/qemu/tests/libqos/virtio-mmio.c +++ b/qemu/tests/libqos/virtio-mmio.c @@ -7,8 +7,8 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" #include "libqos/virtio.h" #include "libqos/virtio-mmio.h" diff --git a/qemu/tests/libqos/virtio-pci.c b/qemu/tests/libqos/virtio-pci.c index f9fb924b8..fde2ff0bc 100644 --- a/qemu/tests/libqos/virtio-pci.c +++ b/qemu/tests/libqos/virtio-pci.c @@ -7,8 +7,8 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" #include "libqos/virtio.h" #include "libqos/virtio-pci.h" diff --git a/qemu/tests/libqos/virtio.c b/qemu/tests/libqos/virtio.c index 3205b88d9..613decea5 100644 --- a/qemu/tests/libqos/virtio.c +++ b/qemu/tests/libqos/virtio.c @@ -7,6 +7,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include #include "libqtest.h" #include "libqos/virtio.h" diff --git a/qemu/tests/libqtest.c b/qemu/tests/libqtest.c index e5188e032..b12a9e4ca 100644 --- a/qemu/tests/libqtest.c +++ b/qemu/tests/libqtest.c @@ -14,22 +14,14 @@ * See the COPYING file in the top-level directory. * */ +#include "qemu/osdep.h" #include "libqtest.h" #include -#include #include #include #include -#include -#include -#include -#include -#include -#include - -#include "qemu/compiler.h" -#include "qemu/osdep.h" + #include "qapi/qmp/json-parser.h" #include "qapi/qmp/json-streamer.h" #include "qapi/qmp/qjson.h" @@ -46,9 +38,9 @@ struct QTestState bool irq_level[MAX_IRQ]; GString *rx; pid_t qemu_pid; /* our child QEMU process */ - struct sigaction sigact_old; /* restored on exit */ }; +static GHookList abrt_hooks; static GList *qtest_instances; static struct sigaction sigact_old; @@ -110,12 +102,14 @@ static void kill_qemu(QTestState *s) } } +static void kill_qemu_hook_func(void *s) +{ + kill_qemu(s); +} + static void sigabrt_handler(int signo) { - GList *elem; - for (elem = qtest_instances; elem; elem = elem->next) { - kill_qemu(elem->data); - } + g_hook_list_invoke(&abrt_hooks, FALSE); } static void setup_sigabrt_handler(void) @@ -136,6 +130,23 @@ static void cleanup_sigabrt_handler(void) sigaction(SIGABRT, &sigact_old, NULL); } +void qtest_add_abrt_handler(GHookFunc fn, const void *data) +{ + GHook *hook; + + /* Only install SIGABRT handler once */ + if (!abrt_hooks.is_setup) { + g_hook_list_init(&abrt_hooks, sizeof(GHook)); + setup_sigabrt_handler(); + } + + hook = g_hook_alloc(&abrt_hooks); + hook->func = fn; + hook->data = (void *)data; + + g_hook_prepend(&abrt_hooks, hook); +} + QTestState *qtest_init(const char *extra_args) { QTestState *s; @@ -156,12 +167,7 @@ QTestState *qtest_init(const char *extra_args) sock = init_socket(socket_path); qmpsock = init_socket(qmp_socket_path); - /* Only install SIGABRT handler once */ - if (!qtest_instances) { - setup_sigabrt_handler(); - } - - qtest_instances = g_list_prepend(qtest_instances, s); + qtest_add_abrt_handler(kill_qemu_hook_func, s); s->qemu_pid = fork(); if (s->qemu_pid == 0) { @@ -209,13 +215,14 @@ QTestState *qtest_init(const char *extra_args) void qtest_quit(QTestState *s) { + qtest_instances = g_list_remove(qtest_instances, s); + g_hook_destroy_link(&abrt_hooks, g_hook_find_data(&abrt_hooks, TRUE, s)); + /* Uninstall SIGABRT handler on last instance */ - if (qtest_instances && !qtest_instances->next) { + if (!qtest_instances) { cleanup_sigabrt_handler(); } - qtest_instances = g_list_remove(qtest_instances, s); - kill_qemu(s); close(s->fd); close(s->qmp_fd); @@ -341,7 +348,7 @@ typedef struct { QDict *response; } QMPResponseParser; -static void qmp_response(JSONMessageParser *parser, QList *tokens) +static void qmp_response(JSONMessageParser *parser, GQueue *tokens) { QMPResponseParser *qmp = container_of(parser, QMPResponseParser, parser); QObject *obj; @@ -357,7 +364,7 @@ static void qmp_response(JSONMessageParser *parser, QList *tokens) qmp->response = (QDict *)obj; } -QDict *qtest_qmp_receive(QTestState *s) +QDict *qmp_fd_receive(int fd) { QMPResponseParser qmp; bool log = getenv("QTEST_LOG") != NULL; @@ -368,7 +375,7 @@ QDict *qtest_qmp_receive(QTestState *s) ssize_t len; char c; - len = read(s->qmp_fd, &c, 1); + len = read(fd, &c, 1); if (len == -1 && errno == EINTR) { continue; } @@ -388,12 +395,17 @@ QDict *qtest_qmp_receive(QTestState *s) return qmp.response; } +QDict *qtest_qmp_receive(QTestState *s) +{ + return qmp_fd_receive(s->qmp_fd); +} + /** * Allow users to send a message without waiting for the reply, * in the case that they choose to discard all replies up until * a particular EVENT is received. */ -void qtest_async_qmpv(QTestState *s, const char *fmt, va_list ap) +void qmp_fd_sendv(int fd, const char *fmt, va_list ap) { va_list ap_copy; QObject *qobj; @@ -417,13 +429,25 @@ void qtest_async_qmpv(QTestState *s, const char *fmt, va_list ap) fprintf(stderr, "%s", str); } /* Send QMP request */ - socket_send(s->qmp_fd, str, size); + socket_send(fd, str, size); QDECREF(qstr); qobject_decref(qobj); } } +void qtest_async_qmpv(QTestState *s, const char *fmt, va_list ap) +{ + qmp_fd_sendv(s->qmp_fd, fmt, ap); +} + +QDict *qmp_fdv(int fd, const char *fmt, va_list ap) +{ + qmp_fd_sendv(fd, fmt, ap); + + return qmp_fd_receive(fd); +} + QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap) { qtest_async_qmpv(s, fmt, ap); @@ -432,6 +456,26 @@ QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap) return qtest_qmp_receive(s); } +QDict *qmp_fd(int fd, const char *fmt, ...) +{ + va_list ap; + QDict *response; + + va_start(ap, fmt); + response = qmp_fdv(fd, fmt, ap); + va_end(ap); + return response; +} + +void qmp_fd_send(int fd, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + qmp_fd_sendv(fd, fmt, ap); + va_end(ap); +} + QDict *qtest_qmp(QTestState *s, const char *fmt, ...) { va_list ap; @@ -484,6 +528,33 @@ void qtest_qmp_eventwait(QTestState *s, const char *event) } } +char *qtest_hmpv(QTestState *s, const char *fmt, va_list ap) +{ + char *cmd; + QDict *resp; + char *ret; + + cmd = g_strdup_vprintf(fmt, ap); + resp = qtest_qmp(s, "{'execute': 'human-monitor-command'," + " 'arguments': {'command-line': %s}}", + cmd); + ret = g_strdup(qdict_get_try_str(resp, "return")); + g_assert(ret); + QDECREF(resp); + g_free(cmd); + return ret; +} + +char *qtest_hmp(QTestState *s, const char *fmt, ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = qtest_hmpv(s, fmt, ap); + va_end(ap); + return ret; +} const char *qtest_get_arch(void) { @@ -681,14 +752,15 @@ void qtest_memread(QTestState *s, uint64_t addr, void *data, size_t size) g_strfreev(args); } -void qtest_add_func(const char *str, void (*fn)) +void qtest_add_func(const char *str, void (*fn)(void)) { gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str); g_test_add_func(path, fn); g_free(path); } -void qtest_add_data_func(const char *str, const void *data, void (*fn)) +void qtest_add_data_func(const char *str, const void *data, + void (*fn)(const void *)) { gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str); g_test_add_data_func(path, data, fn); @@ -775,6 +847,16 @@ void qmp_discard_response(const char *fmt, ...) qtest_qmpv_discard_response(global_qtest, fmt, ap); va_end(ap); } +char *hmp(const char *fmt, ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = qtest_hmpv(global_qtest, fmt, ap); + va_end(ap); + return ret; +} bool qtest_big_endian(void) { diff --git a/qemu/tests/libqtest.h b/qemu/tests/libqtest.h index ec4203152..37f37adbf 100644 --- a/qemu/tests/libqtest.h +++ b/qemu/tests/libqtest.h @@ -17,13 +17,7 @@ #ifndef LIBQTEST_H #define LIBQTEST_H -#include -#include -#include -#include -#include #include "qapi/qmp/qdict.h" -#include "glib-compat.h" typedef struct QTestState QTestState; @@ -119,6 +113,29 @@ QDict *qtest_qmp_receive(QTestState *s); */ void qtest_qmp_eventwait(QTestState *s, const char *event); +/** + * qtest_hmpv: + * @s: #QTestState instance to operate on. + * @fmt...: HMP command to send to QEMU + * + * Send HMP command to QEMU via QMP's human-monitor-command. + * + * Returns: the command's output. The caller should g_free() it. + */ +char *qtest_hmp(QTestState *s, const char *fmt, ...); + +/** + * qtest_hmpv: + * @s: #QTestState instance to operate on. + * @fmt: HMP command to send to QEMU + * @ap: HMP command arguments + * + * Send HMP command to QEMU via QMP's human-monitor-command. + * + * Returns: the command's output. The caller should g_free() it. + */ +char *qtest_hmpv(QTestState *s, const char *fmt, va_list ap); + /** * qtest_get_irq: * @s: #QTestState instance to operate on. @@ -393,7 +410,7 @@ const char *qtest_get_arch(void); * The path is prefixed with the architecture under test, as * returned by qtest_get_arch(). */ -void qtest_add_func(const char *str, void (*fn)); +void qtest_add_func(const char *str, void (*fn)(void)); /** * qtest_add_data_func: @@ -405,7 +422,8 @@ void qtest_add_func(const char *str, void (*fn)); * The path is prefixed with the architecture under test, as * returned by qtest_get_arch(). */ -void qtest_add_data_func(const char *str, const void *data, void (*fn)); +void qtest_add_data_func(const char *str, const void *data, + void (*fn)(const void *)); /** * qtest_add: @@ -427,6 +445,8 @@ void qtest_add_data_func(const char *str, const void *data, void (*fn)); g_free(path); \ } while (0) +void qtest_add_abrt_handler(GHookFunc fn, const void *data); + /** * qtest_start: * @args: other arguments to pass to QEMU @@ -498,6 +518,16 @@ static inline void qmp_eventwait(const char *event) return qtest_qmp_eventwait(global_qtest, event); } +/** + * hmp: + * @fmt...: HMP command to send to QEMU + * + * Send HMP command to QEMU via QMP's human-monitor-command. + * + * Returns: the command's output. The caller should g_free() it. + */ +char *hmp(const char *fmt, ...); + /** * get_irq: * @num: Interrupt to observe. @@ -818,4 +848,11 @@ static inline int64_t clock_set(int64_t val) */ bool qtest_big_endian(void); + +QDict *qmp_fd_receive(int fd); +void qmp_fd_sendv(int fd, const char *fmt, va_list ap); +void qmp_fd_send(int fd, const char *fmt, ...); +QDict *qmp_fdv(int fd, const char *fmt, va_list ap); +QDict *qmp_fd(int fd, const char *fmt, ...); + #endif diff --git a/qemu/tests/m48t59-test.c b/qemu/tests/m48t59-test.c index 71b4f2805..a751fd350 100644 --- a/qemu/tests/m48t59-test.c +++ b/qemu/tests/m48t59-test.c @@ -12,11 +12,8 @@ * */ +#include "qemu/osdep.h" #include -#include -#include -#include -#include #include "libqtest.h" diff --git a/qemu/tests/ne2000-test.c b/qemu/tests/ne2000-test.c index 61a678ad3..3727875f2 100644 --- a/qemu/tests/ne2000-test.c +++ b/qemu/tests/ne2000-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void pci_nop(void) diff --git a/qemu/tests/nvme-test.c b/qemu/tests/nvme-test.c index ff38b5e48..ec06893ee 100644 --- a/qemu/tests/nvme-test.c +++ b/qemu/tests/nvme-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void nop(void) diff --git a/qemu/tests/pc-cpu-test.c b/qemu/tests/pc-cpu-test.c index 3505c7c43..6b34ca588 100644 --- a/qemu/tests/pc-cpu-test.c +++ b/qemu/tests/pc-cpu-test.c @@ -7,12 +7,11 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "qemu-common.h" #include "libqtest.h" -#include "qemu/osdep.h" #include "qapi/qmp/types.h" struct PCTestData { diff --git a/qemu/tests/pcnet-test.c b/qemu/tests/pcnet-test.c index 84af4f327..2ddf4965c 100644 --- a/qemu/tests/pcnet-test.c +++ b/qemu/tests/pcnet-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void pci_nop(void) diff --git a/qemu/tests/pkix_asn1_tab.c b/qemu/tests/pkix_asn1_tab.c new file mode 100644 index 000000000..903bc0251 --- /dev/null +++ b/qemu/tests/pkix_asn1_tab.c @@ -0,0 +1,1105 @@ +/* + * This file is taken from gnutls 1.6.3 under the GPLv2+ + * and is under copyright of various GNUTLS contributors. + */ + +#include "qemu/osdep.h" +#include + +const ASN1_ARRAY_TYPE pkix_asn1_tab[] = { + {"PKIX1", 536875024, 0}, + {0, 1073741836, 0}, + {"id-ce", 1879048204, 0}, + {"joint-iso-ccitt", 1073741825, "2"}, + {"ds", 1073741825, "5"}, + {0, 1, "29"}, + {"id-ce-authorityKeyIdentifier", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "35"}, + {"AuthorityKeyIdentifier", 1610612741, 0}, + {"keyIdentifier", 1610637314, "KeyIdentifier"}, + {0, 4104, "0"}, + {"authorityCertIssuer", 1610637314, "GeneralNames"}, + {0, 4104, "1"}, + {"authorityCertSerialNumber", 536895490, "CertificateSerialNumber"}, + {0, 4104, "2"}, + {"KeyIdentifier", 1073741831, 0}, + {"id-ce-subjectKeyIdentifier", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "14"}, + {"SubjectKeyIdentifier", 1073741826, "KeyIdentifier"}, + {"id-ce-keyUsage", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "15"}, + {"KeyUsage", 1610874886, 0}, + {"digitalSignature", 1073741825, "0"}, + {"nonRepudiation", 1073741825, "1"}, + {"keyEncipherment", 1073741825, "2"}, + {"dataEncipherment", 1073741825, "3"}, + {"keyAgreement", 1073741825, "4"}, + {"keyCertSign", 1073741825, "5"}, + {"cRLSign", 1073741825, "6"}, + {"encipherOnly", 1073741825, "7"}, + {"decipherOnly", 1, "8"}, + {"id-ce-privateKeyUsagePeriod", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "16"}, + {"PrivateKeyUsagePeriod", 1610612741, 0}, + {"notBefore", 1619025937, 0}, + {0, 4104, "0"}, + {"notAfter", 545284113, 0}, + {0, 4104, "1"}, + {"id-ce-certificatePolicies", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "32"}, + {"CertificatePolicies", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "PolicyInformation"}, + {"PolicyInformation", 1610612741, 0}, + {"policyIdentifier", 1073741826, "CertPolicyId"}, + {"policyQualifiers", 538984459, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "PolicyQualifierInfo"}, + {"CertPolicyId", 1073741836, 0}, + {"PolicyQualifierInfo", 1610612741, 0}, + {"policyQualifierId", 1073741826, "PolicyQualifierId"}, + {"qualifier", 541065229, 0}, + {"policyQualifierId", 1, 0}, + {"PolicyQualifierId", 1073741836, 0}, + {"CPSuri", 1073741826, "IA5String"}, + {"UserNotice", 1610612741, 0}, + {"noticeRef", 1073758210, "NoticeReference"}, + {"explicitText", 16386, "DisplayText"}, + {"NoticeReference", 1610612741, 0}, + {"organization", 1073741826, "DisplayText"}, + {"noticeNumbers", 536870923, 0}, + {0, 3, 0}, + {"DisplayText", 1610612754, 0}, + {"visibleString", 1612709890, "VisibleString"}, + {"200", 524298, "1"}, + {"bmpString", 1612709890, "BMPString"}, + {"200", 524298, "1"}, + {"utf8String", 538968066, "UTF8String"}, + {"200", 524298, "1"}, + {"id-ce-policyMappings", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "33"}, + {"PolicyMappings", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 536870917, 0}, + {"issuerDomainPolicy", 1073741826, "CertPolicyId"}, + {"subjectDomainPolicy", 2, "CertPolicyId"}, + {"DirectoryString", 1610612754, 0}, + {"teletexString", 1612709890, "TeletexString"}, + {"MAX", 524298, "1"}, + {"printableString", 1612709890, "PrintableString"}, + {"MAX", 524298, "1"}, + {"universalString", 1612709890, "UniversalString"}, + {"MAX", 524298, "1"}, + {"utf8String", 1612709890, "UTF8String"}, + {"MAX", 524298, "1"}, + {"bmpString", 1612709890, "BMPString"}, + {"MAX", 524298, "1"}, + {"ia5String", 538968066, "IA5String"}, + {"MAX", 524298, "1"}, + {"id-ce-subjectAltName", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "17"}, + {"SubjectAltName", 1073741826, "GeneralNames"}, + {"GeneralNames", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "GeneralName"}, + {"GeneralName", 1610612754, 0}, + {"otherName", 1610620930, "AnotherName"}, + {0, 4104, "0"}, + {"rfc822Name", 1610620930, "IA5String"}, + {0, 4104, "1"}, + {"dNSName", 1610620930, "IA5String"}, + {0, 4104, "2"}, + {"x400Address", 1610620930, "ORAddress"}, + {0, 4104, "3"}, + {"directoryName", 1610620930, "RDNSequence"}, + {0, 2056, "4"}, + {"ediPartyName", 1610620930, "EDIPartyName"}, + {0, 4104, "5"}, + {"uniformResourceIdentifier", 1610620930, "IA5String"}, + {0, 4104, "6"}, + {"iPAddress", 1610620935, 0}, + {0, 4104, "7"}, + {"registeredID", 536879116, 0}, + {0, 4104, "8"}, + {"AnotherName", 1610612741, 0}, + {"type-id", 1073741836, 0}, + {"value", 541073421, 0}, + {0, 1073743880, "0"}, + {"type-id", 1, 0}, + {"EDIPartyName", 1610612741, 0}, + {"nameAssigner", 1610637314, "DirectoryString"}, + {0, 4104, "0"}, + {"partyName", 536879106, "DirectoryString"}, + {0, 4104, "1"}, + {"id-ce-issuerAltName", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "18"}, + {"IssuerAltName", 1073741826, "GeneralNames"}, + {"id-ce-subjectDirectoryAttributes", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "9"}, + {"SubjectDirectoryAttributes", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "Attribute"}, + {"id-ce-basicConstraints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "19"}, + {"BasicConstraints", 1610612741, 0}, + {"cA", 1610645508, 0}, + {0, 131081, 0}, + {"pathLenConstraint", 537411587, 0}, + {"0", 10, "MAX"}, + {"id-ce-nameConstraints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "30"}, + {"NameConstraints", 1610612741, 0}, + {"permittedSubtrees", 1610637314, "GeneralSubtrees"}, + {0, 4104, "0"}, + {"excludedSubtrees", 536895490, "GeneralSubtrees"}, + {0, 4104, "1"}, + {"GeneralSubtrees", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "GeneralSubtree"}, + {"GeneralSubtree", 1610612741, 0}, + {"base", 1073741826, "GeneralName"}, + {"minimum", 1610653698, "BaseDistance"}, + {0, 1073741833, "0"}, + {0, 4104, "0"}, + {"maximum", 536895490, "BaseDistance"}, + {0, 4104, "1"}, + {"BaseDistance", 1611137027, 0}, + {"0", 10, "MAX"}, + {"id-ce-policyConstraints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "36"}, + {"PolicyConstraints", 1610612741, 0}, + {"requireExplicitPolicy", 1610637314, "SkipCerts"}, + {0, 4104, "0"}, + {"inhibitPolicyMapping", 536895490, "SkipCerts"}, + {0, 4104, "1"}, + {"SkipCerts", 1611137027, 0}, + {"0", 10, "MAX"}, + {"id-ce-cRLDistributionPoints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "31"}, + {"CRLDistributionPoints", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "DistributionPoint"}, + {"DistributionPoint", 1610612741, 0}, + {"distributionPoint", 1610637314, "DistributionPointName"}, + {0, 2056, "0"}, + {"reasons", 1610637314, "ReasonFlags"}, + {0, 4104, "1"}, + {"cRLIssuer", 536895490, "GeneralNames"}, + {0, 4104, "2"}, + {"DistributionPointName", 1610612754, 0}, + {"fullName", 1610620930, "GeneralNames"}, + {0, 4104, "0"}, + {"nameRelativeToCRLIssuer", 536879106, "RelativeDistinguishedName"}, + {0, 4104, "1"}, + {"ReasonFlags", 1610874886, 0}, + {"unused", 1073741825, "0"}, + {"keyCompromise", 1073741825, "1"}, + {"cACompromise", 1073741825, "2"}, + {"affiliationChanged", 1073741825, "3"}, + {"superseded", 1073741825, "4"}, + {"cessationOfOperation", 1073741825, "5"}, + {"certificateHold", 1073741825, "6"}, + {"privilegeWithdrawn", 1073741825, "7"}, + {"aACompromise", 1, "8"}, + {"id-ce-extKeyUsage", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "37"}, + {"ExtKeyUsageSyntax", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "KeyPurposeId"}, + {"KeyPurposeId", 1073741836, 0}, + {"id-kp-serverAuth", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "1"}, + {"id-kp-clientAuth", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "2"}, + {"id-kp-codeSigning", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "3"}, + {"id-kp-emailProtection", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "4"}, + {"id-kp-ipsecEndSystem", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "5"}, + {"id-kp-ipsecTunnel", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "6"}, + {"id-kp-ipsecUser", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "7"}, + {"id-kp-timeStamping", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "8"}, + {"id-pe-authorityInfoAccess", 1879048204, 0}, + {0, 1073741825, "id-pe"}, + {0, 1, "1"}, + {"AuthorityInfoAccessSyntax", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "AccessDescription"}, + {"AccessDescription", 1610612741, 0}, + {"accessMethod", 1073741836, 0}, + {"accessLocation", 2, "GeneralName"}, + {"id-ce-cRLNumber", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "20"}, + {"CRLNumber", 1611137027, 0}, + {"0", 10, "MAX"}, + {"id-ce-issuingDistributionPoint", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "28"}, + {"IssuingDistributionPoint", 1610612741, 0}, + {"distributionPoint", 1610637314, "DistributionPointName"}, + {0, 4104, "0"}, + {"onlyContainsUserCerts", 1610653700, 0}, + {0, 1073872905, 0}, + {0, 4104, "1"}, + {"onlyContainsCACerts", 1610653700, 0}, + {0, 1073872905, 0}, + {0, 4104, "2"}, + {"onlySomeReasons", 1610637314, "ReasonFlags"}, + {0, 4104, "3"}, + {"indirectCRL", 536911876, 0}, + {0, 1073872905, 0}, + {0, 4104, "4"}, + {"id-ce-deltaCRLIndicator", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "27"}, + {"BaseCRLNumber", 1073741826, "CRLNumber"}, + {"id-ce-cRLReasons", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "21"}, + {"CRLReason", 1610874901, 0}, + {"unspecified", 1073741825, "0"}, + {"keyCompromise", 1073741825, "1"}, + {"cACompromise", 1073741825, "2"}, + {"affiliationChanged", 1073741825, "3"}, + {"superseded", 1073741825, "4"}, + {"cessationOfOperation", 1073741825, "5"}, + {"certificateHold", 1073741825, "6"}, + {"removeFromCRL", 1, "8"}, + {"id-ce-certificateIssuer", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "29"}, + {"CertificateIssuer", 1073741826, "GeneralNames"}, + {"id-ce-holdInstructionCode", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "23"}, + {"HoldInstructionCode", 1073741836, 0}, + {"holdInstruction", 1879048204, 0}, + {"joint-iso-itu-t", 1073741825, "2"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"x9cm", 1073741825, "10040"}, + {0, 1, "2"}, + {"id-holdinstruction-none", 1879048204, 0}, + {0, 1073741825, "holdInstruction"}, + {0, 1, "1"}, + {"id-holdinstruction-callissuer", 1879048204, 0}, + {0, 1073741825, "holdInstruction"}, + {0, 1, "2"}, + {"id-holdinstruction-reject", 1879048204, 0}, + {0, 1073741825, "holdInstruction"}, + {0, 1, "3"}, + {"id-ce-invalidityDate", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "24"}, + {"InvalidityDate", 1082130449, 0}, + {"VisibleString", 1610620935, 0}, + {0, 4360, "26"}, + {"NumericString", 1610620935, 0}, + {0, 4360, "18"}, + {"IA5String", 1610620935, 0}, + {0, 4360, "22"}, + {"TeletexString", 1610620935, 0}, + {0, 4360, "20"}, + {"PrintableString", 1610620935, 0}, + {0, 4360, "19"}, + {"UniversalString", 1610620935, 0}, + {0, 4360, "28"}, + {"BMPString", 1610620935, 0}, + {0, 4360, "30"}, + {"UTF8String", 1610620935, 0}, + {0, 4360, "12"}, + {"id-pkix", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"identified-organization", 1073741825, "3"}, + {"dod", 1073741825, "6"}, + {"internet", 1073741825, "1"}, + {"security", 1073741825, "5"}, + {"mechanisms", 1073741825, "5"}, + {"pkix", 1, "7"}, + {"id-pe", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "1"}, + {"id-qt", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "2"}, + {"id-kp", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "3"}, + {"id-ad", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "48"}, + {"id-qt-cps", 1879048204, 0}, + {0, 1073741825, "id-qt"}, + {0, 1, "1"}, + {"id-qt-unotice", 1879048204, 0}, + {0, 1073741825, "id-qt"}, + {0, 1, "2"}, + {"id-ad-ocsp", 1879048204, 0}, + {0, 1073741825, "id-ad"}, + {0, 1, "1"}, + {"id-ad-caIssuers", 1879048204, 0}, + {0, 1073741825, "id-ad"}, + {0, 1, "2"}, + {"Attribute", 1610612741, 0}, + {"type", 1073741826, "AttributeType"}, + {"values", 536870927, 0}, + {0, 2, "AttributeValue"}, + {"AttributeType", 1073741836, 0}, + {"AttributeValue", 1614807053, 0}, + {"type", 1, 0}, + {"AttributeTypeAndValue", 1610612741, 0}, + {"type", 1073741826, "AttributeType"}, + {"value", 2, "AttributeValue"}, + {"id-at", 1879048204, 0}, + {"joint-iso-ccitt", 1073741825, "2"}, + {"ds", 1073741825, "5"}, + {0, 1, "4"}, + {"id-at-initials", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "43"}, + {"X520initials", 1073741826, "DirectoryString"}, + {"id-at-generationQualifier", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "44"}, + {"X520generationQualifier", 1073741826, "DirectoryString"}, + {"id-at-surname", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "4"}, + {"X520surName", 1073741826, "DirectoryString"}, + {"id-at-givenName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "42"}, + {"X520givenName", 1073741826, "DirectoryString"}, + {"id-at-name", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "41"}, + {"X520name", 1073741826, "DirectoryString"}, + {"id-at-commonName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "3"}, + {"X520CommonName", 1073741826, "DirectoryString"}, + {"id-at-localityName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "7"}, + {"X520LocalityName", 1073741826, "DirectoryString"}, + {"id-at-stateOrProvinceName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "8"}, + {"X520StateOrProvinceName", 1073741826, "DirectoryString"}, + {"id-at-organizationName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "10"}, + {"X520OrganizationName", 1073741826, "DirectoryString"}, + {"id-at-organizationalUnitName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "11"}, + {"X520OrganizationalUnitName", 1073741826, "DirectoryString"}, + {"id-at-title", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "12"}, + {"X520Title", 1073741826, "DirectoryString"}, + {"id-at-description", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "13"}, + {"X520Description", 1073741826, "DirectoryString"}, + {"id-at-dnQualifier", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "46"}, + {"X520dnQualifier", 1073741826, "PrintableString"}, + {"id-at-countryName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "6"}, + {"X520countryName", 1612709890, "PrintableString"}, + {0, 1048586, "2"}, + {"id-at-serialNumber", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "5"}, + {"X520serialNumber", 1073741826, "PrintableString"}, + {"id-at-telephoneNumber", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "20"}, + {"X520telephoneNumber", 1073741826, "PrintableString"}, + {"id-at-facsimileTelephoneNumber", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "23"}, + {"X520facsimileTelephoneNumber", 1073741826, "PrintableString"}, + {"id-at-pseudonym", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "65"}, + {"X520pseudonym", 1073741826, "DirectoryString"}, + {"id-at-name", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "41"}, + {"X520name", 1073741826, "DirectoryString"}, + {"id-at-streetAddress", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "9"}, + {"X520streetAddress", 1073741826, "DirectoryString"}, + {"id-at-postalAddress", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "16"}, + {"X520postalAddress", 1073741826, "PostalAddress"}, + {"PostalAddress", 1610612747, 0}, + {0, 2, "DirectoryString"}, + {"pkcs", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {"pkcs", 1, "1"}, + {"pkcs-9", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "9"}, + {"emailAddress", 1880096780, "AttributeType"}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "1"}, + {"Pkcs9email", 1612709890, "IA5String"}, + {"ub-emailaddress-length", 524298, "1"}, + {"Name", 1610612754, 0}, + {"rdnSequence", 2, "RDNSequence"}, + {"RDNSequence", 1610612747, 0}, + {0, 2, "RelativeDistinguishedName"}, + {"DistinguishedName", 1073741826, "RDNSequence"}, + {"RelativeDistinguishedName", 1612709903, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "AttributeTypeAndValue"}, + {"Certificate", 1610612741, 0}, + {"tbsCertificate", 1073741826, "TBSCertificate"}, + {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"signature", 6, 0}, + {"TBSCertificate", 1610612741, 0}, + {"version", 1610653698, "Version"}, + {0, 1073741833, "v1"}, + {0, 2056, "0"}, + {"serialNumber", 1073741826, "CertificateSerialNumber"}, + {"signature", 1073741826, "AlgorithmIdentifier"}, + {"issuer", 1073741826, "Name"}, + {"validity", 1073741826, "Validity"}, + {"subject", 1073741826, "Name"}, + {"subjectPublicKeyInfo", 1073741826, "SubjectPublicKeyInfo"}, + {"issuerUniqueID", 1610637314, "UniqueIdentifier"}, + {0, 4104, "1"}, + {"subjectUniqueID", 1610637314, "UniqueIdentifier"}, + {0, 4104, "2"}, + {"extensions", 536895490, "Extensions"}, + {0, 2056, "3"}, + {"Version", 1610874883, 0}, + {"v1", 1073741825, "0"}, + {"v2", 1073741825, "1"}, + {"v3", 1, "2"}, + {"CertificateSerialNumber", 1073741827, 0}, + {"Validity", 1610612741, 0}, + {"notBefore", 1073741826, "Time"}, + {"notAfter", 2, "Time"}, + {"Time", 1610612754, 0}, + {"utcTime", 1090519057, 0}, + {"generalTime", 8388625, 0}, + {"UniqueIdentifier", 1073741830, 0}, + {"SubjectPublicKeyInfo", 1610612741, 0}, + {"algorithm", 1073741826, "AlgorithmIdentifier"}, + {"subjectPublicKey", 6, 0}, + {"Extensions", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "Extension"}, + {"Extension", 1610612741, 0}, + {"extnID", 1073741836, 0}, + {"critical", 1610645508, 0}, + {0, 131081, 0}, + {"extnValue", 7, 0}, + {"CertificateList", 1610612741, 0}, + {"tbsCertList", 1073741826, "TBSCertList"}, + {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"signature", 6, 0}, + {"TBSCertList", 1610612741, 0}, + {"version", 1073758210, "Version"}, + {"signature", 1073741826, "AlgorithmIdentifier"}, + {"issuer", 1073741826, "Name"}, + {"thisUpdate", 1073741826, "Time"}, + {"nextUpdate", 1073758210, "Time"}, + {"revokedCertificates", 1610629131, 0}, + {0, 536870917, 0}, + {"userCertificate", 1073741826, "CertificateSerialNumber"}, + {"revocationDate", 1073741826, "Time"}, + {"crlEntryExtensions", 16386, "Extensions"}, + {"crlExtensions", 536895490, "Extensions"}, + {0, 2056, "0"}, + {"AlgorithmIdentifier", 1610612741, 0}, + {"algorithm", 1073741836, 0}, + {"parameters", 541081613, 0}, + {"algorithm", 1, 0}, + {"pkcs-1", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "1"}, + {"rsaEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "1"}, + {"md2WithRSAEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "2"}, + {"md5WithRSAEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "4"}, + {"sha1WithRSAEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "5"}, + {"id-dsa-with-sha1", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"x9-57", 1073741825, "10040"}, + {"x9algorithm", 1073741825, "4"}, + {0, 1, "3"}, + {"Dss-Sig-Value", 1610612741, 0}, + {"r", 1073741827, 0}, + {"s", 3, 0}, + {"dhpublicnumber", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"ansi-x942", 1073741825, "10046"}, + {"number-type", 1073741825, "2"}, + {0, 1, "1"}, + {"DomainParameters", 1610612741, 0}, + {"p", 1073741827, 0}, + {"g", 1073741827, 0}, + {"q", 1073741827, 0}, + {"j", 1073758211, 0}, + {"validationParms", 16386, "ValidationParms"}, + {"ValidationParms", 1610612741, 0}, + {"seed", 1073741830, 0}, + {"pgenCounter", 3, 0}, + {"id-dsa", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"x9-57", 1073741825, "10040"}, + {"x9algorithm", 1073741825, "4"}, + {0, 1, "1"}, + {"Dss-Parms", 1610612741, 0}, + {"p", 1073741827, 0}, + {"q", 1073741827, 0}, + {"g", 3, 0}, + {"ORAddress", 1610612741, 0}, + {"built-in-standard-attributes", 1073741826, "BuiltInStandardAttributes"}, + {"built-in-domain-defined-attributes", 1073758210, + "BuiltInDomainDefinedAttributes"}, + {"extension-attributes", 16386, "ExtensionAttributes"}, + {"BuiltInStandardAttributes", 1610612741, 0}, + {"country-name", 1073758210, "CountryName"}, + {"administration-domain-name", 1073758210, "AdministrationDomainName"}, + {"network-address", 1610637314, "NetworkAddress"}, + {0, 2056, "0"}, + {"terminal-identifier", 1610637314, "TerminalIdentifier"}, + {0, 2056, "1"}, + {"private-domain-name", 1610637314, "PrivateDomainName"}, + {0, 2056, "2"}, + {"organization-name", 1610637314, "OrganizationName"}, + {0, 2056, "3"}, + {"numeric-user-identifier", 1610637314, "NumericUserIdentifier"}, + {0, 2056, "4"}, + {"personal-name", 1610637314, "PersonalName"}, + {0, 2056, "5"}, + {"organizational-unit-names", 536895490, "OrganizationalUnitNames"}, + {0, 2056, "6"}, + {"CountryName", 1610620946, 0}, + {0, 1073746952, "1"}, + {"x121-dcc-code", 1612709890, "NumericString"}, + {0, 1048586, "ub-country-name-numeric-length"}, + {"iso-3166-alpha2-code", 538968066, "PrintableString"}, + {0, 1048586, "ub-country-name-alpha-length"}, + {"AdministrationDomainName", 1610620946, 0}, + {0, 1073744904, "2"}, + {"numeric", 1612709890, "NumericString"}, + {"ub-domain-name-length", 524298, "0"}, + {"printable", 538968066, "PrintableString"}, + {"ub-domain-name-length", 524298, "0"}, + {"NetworkAddress", 1073741826, "X121Address"}, + {"X121Address", 1612709890, "NumericString"}, + {"ub-x121-address-length", 524298, "1"}, + {"TerminalIdentifier", 1612709890, "PrintableString"}, + {"ub-terminal-id-length", 524298, "1"}, + {"PrivateDomainName", 1610612754, 0}, + {"numeric", 1612709890, "NumericString"}, + {"ub-domain-name-length", 524298, "1"}, + {"printable", 538968066, "PrintableString"}, + {"ub-domain-name-length", 524298, "1"}, + {"OrganizationName", 1612709890, "PrintableString"}, + {"ub-organization-name-length", 524298, "1"}, + {"NumericUserIdentifier", 1612709890, "NumericString"}, + {"ub-numeric-user-id-length", 524298, "1"}, + {"PersonalName", 1610612750, 0}, + {"surname", 1814044674, "PrintableString"}, + {0, 1073745928, "0"}, + {"ub-surname-length", 524298, "1"}, + {"given-name", 1814061058, "PrintableString"}, + {0, 1073745928, "1"}, + {"ub-given-name-length", 524298, "1"}, + {"initials", 1814061058, "PrintableString"}, + {0, 1073745928, "2"}, + {"ub-initials-length", 524298, "1"}, + {"generation-qualifier", 740319234, "PrintableString"}, + {0, 1073745928, "3"}, + {"ub-generation-qualifier-length", 524298, "1"}, + {"OrganizationalUnitNames", 1612709899, 0}, + {"ub-organizational-units", 1074266122, "1"}, + {0, 2, "OrganizationalUnitName"}, + {"OrganizationalUnitName", 1612709890, "PrintableString"}, + {"ub-organizational-unit-name-length", 524298, "1"}, + {"BuiltInDomainDefinedAttributes", 1612709899, 0}, + {"ub-domain-defined-attributes", 1074266122, "1"}, + {0, 2, "BuiltInDomainDefinedAttribute"}, + {"BuiltInDomainDefinedAttribute", 1610612741, 0}, + {"type", 1612709890, "PrintableString"}, + {"ub-domain-defined-attribute-type-length", 524298, "1"}, + {"value", 538968066, "PrintableString"}, + {"ub-domain-defined-attribute-value-length", 524298, "1"}, + {"ExtensionAttributes", 1612709903, 0}, + {"ub-extension-attributes", 1074266122, "1"}, + {0, 2, "ExtensionAttribute"}, + {"ExtensionAttribute", 1610612741, 0}, + {"extension-attribute-type", 1611145219, 0}, + {0, 1073743880, "0"}, + {"0", 10, "ub-extension-attributes"}, + {"extension-attribute-value", 541073421, 0}, + {0, 1073743880, "1"}, + {"extension-attribute-type", 1, 0}, + {"common-name", 1342177283, "1"}, + {"CommonName", 1612709890, "PrintableString"}, + {"ub-common-name-length", 524298, "1"}, + {"teletex-common-name", 1342177283, "2"}, + {"TeletexCommonName", 1612709890, "TeletexString"}, + {"ub-common-name-length", 524298, "1"}, + {"teletex-organization-name", 1342177283, "3"}, + {"TeletexOrganizationName", 1612709890, "TeletexString"}, + {"ub-organization-name-length", 524298, "1"}, + {"teletex-personal-name", 1342177283, "4"}, + {"TeletexPersonalName", 1610612750, 0}, + {"surname", 1814044674, "TeletexString"}, + {0, 1073743880, "0"}, + {"ub-surname-length", 524298, "1"}, + {"given-name", 1814061058, "TeletexString"}, + {0, 1073743880, "1"}, + {"ub-given-name-length", 524298, "1"}, + {"initials", 1814061058, "TeletexString"}, + {0, 1073743880, "2"}, + {"ub-initials-length", 524298, "1"}, + {"generation-qualifier", 740319234, "TeletexString"}, + {0, 1073743880, "3"}, + {"ub-generation-qualifier-length", 524298, "1"}, + {"teletex-organizational-unit-names", 1342177283, "5"}, + {"TeletexOrganizationalUnitNames", 1612709899, 0}, + {"ub-organizational-units", 1074266122, "1"}, + {0, 2, "TeletexOrganizationalUnitName"}, + {"TeletexOrganizationalUnitName", 1612709890, "TeletexString"}, + {"ub-organizational-unit-name-length", 524298, "1"}, + {"pds-name", 1342177283, "7"}, + {"PDSName", 1612709890, "PrintableString"}, + {"ub-pds-name-length", 524298, "1"}, + {"physical-delivery-country-name", 1342177283, "8"}, + {"PhysicalDeliveryCountryName", 1610612754, 0}, + {"x121-dcc-code", 1612709890, "NumericString"}, + {0, 1048586, "ub-country-name-numeric-length"}, + {"iso-3166-alpha2-code", 538968066, "PrintableString"}, + {0, 1048586, "ub-country-name-alpha-length"}, + {"postal-code", 1342177283, "9"}, + {"PostalCode", 1610612754, 0}, + {"numeric-code", 1612709890, "NumericString"}, + {"ub-postal-code-length", 524298, "1"}, + {"printable-code", 538968066, "PrintableString"}, + {"ub-postal-code-length", 524298, "1"}, + {"physical-delivery-office-name", 1342177283, "10"}, + {"PhysicalDeliveryOfficeName", 1073741826, "PDSParameter"}, + {"physical-delivery-office-number", 1342177283, "11"}, + {"PhysicalDeliveryOfficeNumber", 1073741826, "PDSParameter"}, + {"extension-OR-address-components", 1342177283, "12"}, + {"ExtensionORAddressComponents", 1073741826, "PDSParameter"}, + {"physical-delivery-personal-name", 1342177283, "13"}, + {"PhysicalDeliveryPersonalName", 1073741826, "PDSParameter"}, + {"physical-delivery-organization-name", 1342177283, "14"}, + {"PhysicalDeliveryOrganizationName", 1073741826, "PDSParameter"}, + {"extension-physical-delivery-address-components", 1342177283, "15"}, + {"ExtensionPhysicalDeliveryAddressComponents", 1073741826, "PDSParameter"}, + {"unformatted-postal-address", 1342177283, "16"}, + {"UnformattedPostalAddress", 1610612750, 0}, + {"printable-address", 1814052875, 0}, + {"ub-pds-physical-address-lines", 1074266122, "1"}, + {0, 538968066, "PrintableString"}, + {"ub-pds-parameter-length", 524298, "1"}, + {"teletex-string", 740311042, "TeletexString"}, + {"ub-unformatted-address-length", 524298, "1"}, + {"street-address", 1342177283, "17"}, + {"StreetAddress", 1073741826, "PDSParameter"}, + {"post-office-box-address", 1342177283, "18"}, + {"PostOfficeBoxAddress", 1073741826, "PDSParameter"}, + {"poste-restante-address", 1342177283, "19"}, + {"PosteRestanteAddress", 1073741826, "PDSParameter"}, + {"unique-postal-name", 1342177283, "20"}, + {"UniquePostalName", 1073741826, "PDSParameter"}, + {"local-postal-attributes", 1342177283, "21"}, + {"LocalPostalAttributes", 1073741826, "PDSParameter"}, + {"PDSParameter", 1610612750, 0}, + {"printable-string", 1814052866, "PrintableString"}, + {"ub-pds-parameter-length", 524298, "1"}, + {"teletex-string", 740311042, "TeletexString"}, + {"ub-pds-parameter-length", 524298, "1"}, + {"extended-network-address", 1342177283, "22"}, + {"ExtendedNetworkAddress", 1610612754, 0}, + {"e163-4-address", 1610612741, 0}, + {"number", 1612718082, "NumericString"}, + {0, 1073743880, "0"}, + {"ub-e163-4-number-length", 524298, "1"}, + {"sub-address", 538992642, "NumericString"}, + {0, 1073743880, "1"}, + {"ub-e163-4-sub-address-length", 524298, "1"}, + {"psap-address", 536879106, "PresentationAddress"}, + {0, 2056, "0"}, + {"PresentationAddress", 1610612741, 0}, + {"pSelector", 1610637319, 0}, + {0, 2056, "0"}, + {"sSelector", 1610637319, 0}, + {0, 2056, "1"}, + {"tSelector", 1610637319, 0}, + {0, 2056, "2"}, + {"nAddresses", 538976271, 0}, + {0, 1073743880, "3"}, + {"MAX", 1074266122, "1"}, + {0, 7, 0}, + {"terminal-type", 1342177283, "23"}, + {"TerminalType", 1610874883, 0}, + {"telex", 1073741825, "3"}, + {"teletex", 1073741825, "4"}, + {"g3-facsimile", 1073741825, "5"}, + {"g4-facsimile", 1073741825, "6"}, + {"ia5-terminal", 1073741825, "7"}, + {"videotex", 1, "8"}, + {"teletex-domain-defined-attributes", 1342177283, "6"}, + {"TeletexDomainDefinedAttributes", 1612709899, 0}, + {"ub-domain-defined-attributes", 1074266122, "1"}, + {0, 2, "TeletexDomainDefinedAttribute"}, + {"TeletexDomainDefinedAttribute", 1610612741, 0}, + {"type", 1612709890, "TeletexString"}, + {"ub-domain-defined-attribute-type-length", 524298, "1"}, + {"value", 538968066, "TeletexString"}, + {"ub-domain-defined-attribute-value-length", 524298, "1"}, + {"ub-name", 1342177283, "32768"}, + {"ub-common-name", 1342177283, "64"}, + {"ub-locality-name", 1342177283, "128"}, + {"ub-state-name", 1342177283, "128"}, + {"ub-organization-name", 1342177283, "64"}, + {"ub-organizational-unit-name", 1342177283, "64"}, + {"ub-title", 1342177283, "64"}, + {"ub-match", 1342177283, "128"}, + {"ub-emailaddress-length", 1342177283, "128"}, + {"ub-common-name-length", 1342177283, "64"}, + {"ub-country-name-alpha-length", 1342177283, "2"}, + {"ub-country-name-numeric-length", 1342177283, "3"}, + {"ub-domain-defined-attributes", 1342177283, "4"}, + {"ub-domain-defined-attribute-type-length", 1342177283, "8"}, + {"ub-domain-defined-attribute-value-length", 1342177283, "128"}, + {"ub-domain-name-length", 1342177283, "16"}, + {"ub-extension-attributes", 1342177283, "256"}, + {"ub-e163-4-number-length", 1342177283, "15"}, + {"ub-e163-4-sub-address-length", 1342177283, "40"}, + {"ub-generation-qualifier-length", 1342177283, "3"}, + {"ub-given-name-length", 1342177283, "16"}, + {"ub-initials-length", 1342177283, "5"}, + {"ub-integer-options", 1342177283, "256"}, + {"ub-numeric-user-id-length", 1342177283, "32"}, + {"ub-organization-name-length", 1342177283, "64"}, + {"ub-organizational-unit-name-length", 1342177283, "32"}, + {"ub-organizational-units", 1342177283, "4"}, + {"ub-pds-name-length", 1342177283, "16"}, + {"ub-pds-parameter-length", 1342177283, "30"}, + {"ub-pds-physical-address-lines", 1342177283, "6"}, + {"ub-postal-code-length", 1342177283, "16"}, + {"ub-surname-length", 1342177283, "40"}, + {"ub-terminal-id-length", 1342177283, "24"}, + {"ub-unformatted-address-length", 1342177283, "180"}, + {"ub-x121-address-length", 1342177283, "16"}, + {"pkcs-7-ContentInfo", 1610612741, 0}, + {"contentType", 1073741826, "pkcs-7-ContentType"}, + {"content", 541073421, 0}, + {0, 1073743880, "0"}, + {"contentType", 1, 0}, + {"pkcs-7-DigestInfo", 1610612741, 0}, + {"digestAlgorithm", 1073741826, "pkcs-7-DigestAlgorithmIdentifier"}, + {"digest", 2, "pkcs-7-Digest"}, + {"pkcs-7-Digest", 1073741831, 0}, + {"pkcs-7-ContentType", 1073741836, 0}, + {"pkcs-7-SignedData", 1610612741, 0}, + {"version", 1073741826, "pkcs-7-CMSVersion"}, + {"digestAlgorithms", 1073741826, "pkcs-7-DigestAlgorithmIdentifiers"}, + {"encapContentInfo", 1073741826, "pkcs-7-EncapsulatedContentInfo"}, + {"certificates", 1610637314, "pkcs-7-CertificateSet"}, + {0, 4104, "0"}, + {"crls", 1610637314, "pkcs-7-CertificateRevocationLists"}, + {0, 4104, "1"}, + {"signerInfos", 2, "pkcs-7-SignerInfos"}, + {"pkcs-7-CMSVersion", 1610874883, 0}, + {"v0", 1073741825, "0"}, + {"v1", 1073741825, "1"}, + {"v2", 1073741825, "2"}, + {"v3", 1073741825, "3"}, + {"v4", 1, "4"}, + {"pkcs-7-DigestAlgorithmIdentifiers", 1610612751, 0}, + {0, 2, "pkcs-7-DigestAlgorithmIdentifier"}, + {"pkcs-7-DigestAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"}, + {"pkcs-7-EncapsulatedContentInfo", 1610612741, 0}, + {"eContentType", 1073741826, "pkcs-7-ContentType"}, + {"eContent", 536895495, 0}, + {0, 2056, "0"}, + {"pkcs-7-CertificateRevocationLists", 1610612751, 0}, + {0, 13, 0}, + {"pkcs-7-CertificateChoices", 1610612754, 0}, + {"certificate", 13, 0}, + {"pkcs-7-CertificateSet", 1610612751, 0}, + {0, 2, "pkcs-7-CertificateChoices"}, + {"pkcs-7-SignerInfos", 1610612751, 0}, + {0, 13, 0}, + {"pkcs-10-CertificationRequestInfo", 1610612741, 0}, + {"version", 1610874883, 0}, + {"v1", 1, "0"}, + {"subject", 1073741826, "Name"}, + {"subjectPKInfo", 1073741826, "SubjectPublicKeyInfo"}, + {"attributes", 536879106, "Attributes"}, + {0, 4104, "0"}, + {"Attributes", 1610612751, 0}, + {0, 2, "Attribute"}, + {"pkcs-10-CertificationRequest", 1610612741, 0}, + {"certificationRequestInfo", 1073741826, "pkcs-10-CertificationRequestInfo"}, + {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"signature", 6, 0}, + {"pkcs-9-ub-challengePassword", 1342177283, "255"}, + {"pkcs-9-certTypes", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "22"}, + {"pkcs-9-crlTypes", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "23"}, + {"pkcs-9-at-challengePassword", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "7"}, + {"pkcs-9-challengePassword", 1610612754, 0}, + {"printableString", 1612709890, "PrintableString"}, + {"pkcs-9-ub-challengePassword", 524298, "1"}, + {"utf8String", 538968066, "UTF8String"}, + {"pkcs-9-ub-challengePassword", 524298, "1"}, + {"pkcs-9-at-localKeyId", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "21"}, + {"pkcs-9-localKeyId", 1073741831, 0}, + {"pkcs-9-at-friendlyName", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "20"}, + {"pkcs-9-friendlyName", 1612709890, "BMPString"}, + {"255", 524298, "1"}, + {"pkcs-8-PrivateKeyInfo", 1610612741, 0}, + {"version", 1073741826, "pkcs-8-Version"}, + {"privateKeyAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"privateKey", 1073741826, "pkcs-8-PrivateKey"}, + {"attributes", 536895490, "Attributes"}, + {0, 4104, "0"}, + {"pkcs-8-Version", 1610874883, 0}, + {"v1", 1, "0"}, + {"pkcs-8-PrivateKey", 1073741831, 0}, + {"pkcs-8-Attributes", 1610612751, 0}, + {0, 2, "Attribute"}, + {"pkcs-8-EncryptedPrivateKeyInfo", 1610612741, 0}, + {"encryptionAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"encryptedData", 2, "pkcs-8-EncryptedData"}, + {"pkcs-8-EncryptedData", 1073741831, 0}, + {"pkcs-5", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "5"}, + {"pkcs-5-encryptionAlgorithm", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {0, 1, "3"}, + {"pkcs-5-des-EDE3-CBC", 1879048204, 0}, + {0, 1073741825, "pkcs-5-encryptionAlgorithm"}, + {0, 1, "7"}, + {"pkcs-5-des-EDE3-CBC-params", 1612709895, 0}, + {0, 1048586, "8"}, + {"pkcs-5-id-PBES2", 1879048204, 0}, + {0, 1073741825, "pkcs-5"}, + {0, 1, "13"}, + {"pkcs-5-PBES2-params", 1610612741, 0}, + {"keyDerivationFunc", 1073741826, "AlgorithmIdentifier"}, + {"encryptionScheme", 2, "AlgorithmIdentifier"}, + {"pkcs-5-id-PBKDF2", 1879048204, 0}, + {0, 1073741825, "pkcs-5"}, + {0, 1, "12"}, + {"pkcs-5-PBKDF2-params", 1610612741, 0}, + {"salt", 1610612754, 0}, + {"specified", 1073741831, 0}, + {"otherSource", 2, "AlgorithmIdentifier"}, + {"iterationCount", 1611137027, 0}, + {"1", 10, "MAX"}, + {"keyLength", 1611153411, 0}, + {"1", 10, "MAX"}, + {"prf", 16386, "AlgorithmIdentifier"}, + {"pkcs-12", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "12"}, + {"pkcs-12-PFX", 1610612741, 0}, + {"version", 1610874883, 0}, + {"v3", 1, "3"}, + {"authSafe", 1073741826, "pkcs-7-ContentInfo"}, + {"macData", 16386, "pkcs-12-MacData"}, + {"pkcs-12-PbeParams", 1610612741, 0}, + {"salt", 1073741831, 0}, + {"iterations", 3, 0}, + {"pkcs-12-MacData", 1610612741, 0}, + {"mac", 1073741826, "pkcs-7-DigestInfo"}, + {"macSalt", 1073741831, 0}, + {"iterations", 536903683, 0}, + {0, 9, "1"}, + {"pkcs-12-AuthenticatedSafe", 1610612747, 0}, + {0, 2, "pkcs-7-ContentInfo"}, + {"pkcs-12-SafeContents", 1610612747, 0}, + {0, 2, "pkcs-12-SafeBag"}, + {"pkcs-12-SafeBag", 1610612741, 0}, + {"bagId", 1073741836, 0}, + {"bagValue", 1614815245, 0}, + {0, 1073743880, "0"}, + {"badId", 1, 0}, + {"bagAttributes", 536887311, 0}, + {0, 2, "pkcs-12-PKCS12Attribute"}, + {"pkcs-12-bagtypes", 1879048204, 0}, + {0, 1073741825, "pkcs-12"}, + {0, 1073741825, "10"}, + {0, 1, "1"}, + {"pkcs-12-keyBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "1"}, + {"pkcs-12-pkcs8ShroudedKeyBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "2"}, + {"pkcs-12-certBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "3"}, + {"pkcs-12-crlBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "4"}, + {"pkcs-12-KeyBag", 1073741826, "pkcs-8-PrivateKeyInfo"}, + {"pkcs-12-PKCS8ShroudedKeyBag", 1073741826, "pkcs-8-EncryptedPrivateKeyInfo"}, + {"pkcs-12-CertBag", 1610612741, 0}, + {"certId", 1073741836, 0}, + {"certValue", 541073421, 0}, + {0, 1073743880, "0"}, + {"certId", 1, 0}, + {"pkcs-12-CRLBag", 1610612741, 0}, + {"crlId", 1073741836, 0}, + {"crlValue", 541073421, 0}, + {0, 1073743880, "0"}, + {"crlId", 1, 0}, + {"pkcs-12-PKCS12Attribute", 1073741826, "Attribute"}, + {"pkcs-7-data", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {"pkcs", 1073741825, "1"}, + {"pkcs7", 1073741825, "7"}, + {0, 1, "1"}, + {"pkcs-7-encryptedData", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {"pkcs", 1073741825, "1"}, + {"pkcs7", 1073741825, "7"}, + {0, 1, "6"}, + {"pkcs-7-Data", 1073741831, 0}, + {"pkcs-7-EncryptedData", 1610612741, 0}, + {"version", 1073741826, "pkcs-7-CMSVersion"}, + {"encryptedContentInfo", 1073741826, "pkcs-7-EncryptedContentInfo"}, + {"unprotectedAttrs", 536895490, "pkcs-7-UnprotectedAttributes"}, + {0, 4104, "1"}, + {"pkcs-7-EncryptedContentInfo", 1610612741, 0}, + {"contentType", 1073741826, "pkcs-7-ContentType"}, + {"contentEncryptionAlgorithm", 1073741826, + "pkcs-7-ContentEncryptionAlgorithmIdentifier"}, + {"encryptedContent", 536895490, "pkcs-7-EncryptedContent"}, + {0, 4104, "0"}, + {"pkcs-7-ContentEncryptionAlgorithmIdentifier", 1073741826, + "AlgorithmIdentifier"}, + {"pkcs-7-EncryptedContent", 1073741831, 0}, + {"pkcs-7-UnprotectedAttributes", 1612709903, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "Attribute"}, + {"id-at-ldap-DC", 1880096780, "AttributeType"}, + {0, 1073741825, "0"}, + {0, 1073741825, "9"}, + {0, 1073741825, "2342"}, + {0, 1073741825, "19200300"}, + {0, 1073741825, "100"}, + {0, 1073741825, "1"}, + {0, 1, "25"}, + {"ldap-DC", 1073741826, "IA5String"}, + {"id-at-ldap-UID", 1880096780, "AttributeType"}, + {0, 1073741825, "0"}, + {0, 1073741825, "9"}, + {0, 1073741825, "2342"}, + {0, 1073741825, "19200300"}, + {0, 1073741825, "100"}, + {0, 1073741825, "1"}, + {0, 1, "1"}, + {"ldap-UID", 1073741826, "DirectoryString"}, + {"id-pda", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "9"}, + {"id-pda-dateOfBirth", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "1"}, + {"DateOfBirth", 1082130449, 0}, + {"id-pda-placeOfBirth", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "2"}, + {"PlaceOfBirth", 1073741826, "DirectoryString"}, + {"id-pda-gender", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "3"}, + {"Gender", 1612709890, "PrintableString"}, + {0, 1048586, "1"}, + {"id-pda-countryOfCitizenship", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "4"}, + {"CountryOfCitizenship", 1612709890, "PrintableString"}, + {0, 1048586, "2"}, + {"id-pda-countryOfResidence", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "5"}, + {"CountryOfResidence", 538968066, "PrintableString"}, + {0, 1048586, "2"}, + {0, 0, 0} +}; diff --git a/qemu/tests/pvpanic-test.c b/qemu/tests/pvpanic-test.c index a7ad6b306..d435833f7 100644 --- a/qemu/tests/pvpanic-test.c +++ b/qemu/tests/pvpanic-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" static void test_panic(void) { diff --git a/qemu/tests/pxe-test.c b/qemu/tests/pxe-test.c new file mode 100644 index 000000000..875e4c4a2 --- /dev/null +++ b/qemu/tests/pxe-test.c @@ -0,0 +1,68 @@ +/* + * PXE test cases. + * + * Copyright (c) 2016 Red Hat Inc. + * + * Authors: + * Michael S. Tsirkin , + * Victor Kaplansky + * + * 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 +#include +#include "qemu-common.h" +#include "libqtest.h" +#include "boot-sector.h" + +#define NETNAME "net0" + +static const char *disk = "tests/pxe-test-disk.raw"; + +static void test_pxe_one(const char *params) +{ + char *args; + + args = g_strdup_printf("-machine accel=tcg " + "-netdev user,id=" NETNAME ",tftp=./,bootfile=%s " + "%s ", + disk, params); + + qtest_start(args); + boot_sector_test(); + qtest_quit(global_qtest); + g_free(args); +} + +static void test_pxe_e1000(void) +{ + test_pxe_one("-device e1000,netdev=" NETNAME); +} + +static void test_pxe_virtio_pci(void) +{ + test_pxe_one("-device virtio-net-pci,netdev=" NETNAME); +} + +int main(int argc, char *argv[]) +{ + int ret; + const char *arch = qtest_get_arch(); + + ret = boot_sector_init(disk); + if(ret) + return ret; + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qtest_add_func("pxe/e1000", test_pxe_e1000); + qtest_add_func("pxe/virtio", test_pxe_virtio_pci); + } + ret = g_test_run(); + boot_sector_cleanup(disk); + return ret; +} diff --git a/qemu/tests/q35-test.c b/qemu/tests/q35-test.c index 812abe548..a105f1078 100644 --- a/qemu/tests/q35-test.c +++ b/qemu/tests/q35-test.c @@ -9,12 +9,11 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" #include "libqos/pci.h" #include "libqos/pci-pc.h" -#include "qemu/osdep.h" #include "hw/pci-host/q35.h" static void smram_set_bit(QPCIDevice *pcidev, uint8_t mask, bool enabled) diff --git a/qemu/tests/qapi-schema/alternate-any.err b/qemu/tests/qapi-schema/alternate-any.err new file mode 100644 index 000000000..aaa015473 --- /dev/null +++ b/qemu/tests/qapi-schema/alternate-any.err @@ -0,0 +1 @@ +tests/qapi-schema/alternate-any.json:2: Alternate 'Alt' member 'one' cannot use type 'any' diff --git a/qemu/tests/qapi-schema/data-array-empty.exit b/qemu/tests/qapi-schema/alternate-any.exit similarity index 100% rename from qemu/tests/qapi-schema/data-array-empty.exit rename to qemu/tests/qapi-schema/alternate-any.exit diff --git a/qemu/tests/qapi-schema/alternate-any.json b/qemu/tests/qapi-schema/alternate-any.json new file mode 100644 index 000000000..e47a73a11 --- /dev/null +++ b/qemu/tests/qapi-schema/alternate-any.json @@ -0,0 +1,4 @@ +# we do not allow the 'any' type as an alternate branch +{ 'alternate': 'Alt', + 'data': { 'one': 'any', + 'two': 'int' } } diff --git a/qemu/tests/qapi-schema/enum-empty.err b/qemu/tests/qapi-schema/alternate-any.out similarity index 100% rename from qemu/tests/qapi-schema/enum-empty.err rename to qemu/tests/qapi-schema/alternate-any.out diff --git a/qemu/tests/qapi-schema/alternate-clash.err b/qemu/tests/qapi-schema/alternate-clash.err index 51bea3e27..604d8495e 100644 --- a/qemu/tests/qapi-schema/alternate-clash.err +++ b/qemu/tests/qapi-schema/alternate-clash.err @@ -1 +1 @@ -tests/qapi-schema/alternate-clash.json:2: Alternate 'Alt1' member 'ONE' clashes with 'one' +tests/qapi-schema/alternate-clash.json:7: 'a_b' (branch of Alt1) collides with 'a-b' (branch of Alt1) diff --git a/qemu/tests/qapi-schema/alternate-clash.json b/qemu/tests/qapi-schema/alternate-clash.json index 39479353b..6d73bc527 100644 --- a/qemu/tests/qapi-schema/alternate-clash.json +++ b/qemu/tests/qapi-schema/alternate-clash.json @@ -1,3 +1,8 @@ -# we detect C enum collisions in an alternate +# Alternate branch name collision +# Reject an alternate that would result in a collision in generated C +# names (this would try to generate two enum values 'ALT1_KIND_A_B'). +# TODO: In the future, if alternates are simplified to not generate +# the implicit Alt1Kind enum, we would still have a collision with the +# resulting C union trying to have two members named 'a_b'. { 'alternate': 'Alt1', - 'data': { 'one': 'str', 'ONE': 'int' } } + 'data': { 'a-b': 'str', 'a_b': 'int' } } diff --git a/qemu/tests/qapi-schema/alternate-empty.err b/qemu/tests/qapi-schema/alternate-empty.err new file mode 100644 index 000000000..bb06c5bfe --- /dev/null +++ b/qemu/tests/qapi-schema/alternate-empty.err @@ -0,0 +1 @@ +tests/qapi-schema/alternate-empty.json:2: Alternate 'Alt' should have at least two branches in 'data' diff --git a/qemu/tests/qapi-schema/data-array-unknown.exit b/qemu/tests/qapi-schema/alternate-empty.exit similarity index 100% rename from qemu/tests/qapi-schema/data-array-unknown.exit rename to qemu/tests/qapi-schema/alternate-empty.exit diff --git a/qemu/tests/qapi-schema/alternate-empty.json b/qemu/tests/qapi-schema/alternate-empty.json new file mode 100644 index 000000000..fff15baf1 --- /dev/null +++ b/qemu/tests/qapi-schema/alternate-empty.json @@ -0,0 +1,2 @@ +# alternates must list at least two types to be useful +{ 'alternate': 'Alt', 'data': { 'i': 'int' } } diff --git a/qemu/tests/qapi-schema/enum-max-member.out b/qemu/tests/qapi-schema/alternate-empty.out similarity index 100% rename from qemu/tests/qapi-schema/enum-max-member.out rename to qemu/tests/qapi-schema/alternate-empty.out diff --git a/qemu/tests/qapi-schema/alternate-good.exit b/qemu/tests/qapi-schema/alternate-good.exit deleted file mode 100644 index 573541ac9..000000000 --- a/qemu/tests/qapi-schema/alternate-good.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/qemu/tests/qapi-schema/alternate-good.json b/qemu/tests/qapi-schema/alternate-good.json deleted file mode 100644 index 33717704c..000000000 --- a/qemu/tests/qapi-schema/alternate-good.json +++ /dev/null @@ -1,9 +0,0 @@ -# Working example of alternate -{ 'struct': 'Data', - 'data': { '*number': 'int', '*name': 'str' } } -{ 'enum': 'Enum', - 'data': [ 'hello', 'world' ] } -{ 'alternate': 'Alt', - 'data': { 'value': 'int', - 'string': 'Enum', - 'struct': 'Data' } } diff --git a/qemu/tests/qapi-schema/alternate-good.out b/qemu/tests/qapi-schema/alternate-good.out deleted file mode 100644 index 99848eefb..000000000 --- a/qemu/tests/qapi-schema/alternate-good.out +++ /dev/null @@ -1,6 +0,0 @@ -[OrderedDict([('struct', 'Data'), ('data', OrderedDict([('*number', 'int'), ('*name', 'str')]))]), - OrderedDict([('enum', 'Enum'), ('data', ['hello', 'world'])]), - OrderedDict([('alternate', 'Alt'), ('data', OrderedDict([('value', 'int'), ('string', 'Enum'), ('struct', 'Data')]))])] -[{'enum_name': 'Enum', 'enum_values': ['hello', 'world']}, - {'enum_name': 'AltKind', 'enum_values': None}] -[OrderedDict([('struct', 'Data'), ('data', OrderedDict([('*number', 'int'), ('*name', 'str')]))])] diff --git a/qemu/tests/qapi-schema/alternate-nested.json b/qemu/tests/qapi-schema/alternate-nested.json index c4233b9f3..8e2218649 100644 --- a/qemu/tests/qapi-schema/alternate-nested.json +++ b/qemu/tests/qapi-schema/alternate-nested.json @@ -2,4 +2,4 @@ { 'alternate': 'Alt1', 'data': { 'name': 'str', 'value': 'int' } } { 'alternate': 'Alt2', - 'data': { 'nested': 'Alt1' } } + 'data': { 'nested': 'Alt1', 'b': 'bool' } } diff --git a/qemu/tests/qapi-schema/alternate-unknown.json b/qemu/tests/qapi-schema/alternate-unknown.json index ad5c10302..08c80dced 100644 --- a/qemu/tests/qapi-schema/alternate-unknown.json +++ b/qemu/tests/qapi-schema/alternate-unknown.json @@ -1,3 +1,3 @@ # we reject an alternate with unknown type in branch { 'alternate': 'Alt', - 'data': { 'unknown': 'MissingType' } } + 'data': { 'unknown': 'MissingType', 'i': 'int' } } diff --git a/qemu/tests/qapi-schema/args-alternate.err b/qemu/tests/qapi-schema/args-alternate.err new file mode 100644 index 000000000..3086eae56 --- /dev/null +++ b/qemu/tests/qapi-schema/args-alternate.err @@ -0,0 +1 @@ +tests/qapi-schema/args-alternate.json:3: 'data' for command 'oops' cannot use alternate type 'Alt' diff --git a/qemu/tests/qapi-schema/data-int.exit b/qemu/tests/qapi-schema/args-alternate.exit similarity index 100% rename from qemu/tests/qapi-schema/data-int.exit rename to qemu/tests/qapi-schema/args-alternate.exit diff --git a/qemu/tests/qapi-schema/args-alternate.json b/qemu/tests/qapi-schema/args-alternate.json new file mode 100644 index 000000000..69e94d481 --- /dev/null +++ b/qemu/tests/qapi-schema/args-alternate.json @@ -0,0 +1,3 @@ +# we do not allow alternate arguments +{ 'alternate': 'Alt', 'data': { 'case1': 'int', 'case2': 'str' } } +{ 'command': 'oops', 'data': 'Alt' } diff --git a/qemu/tests/qapi-schema/enum-union-clash.out b/qemu/tests/qapi-schema/args-alternate.out similarity index 100% rename from qemu/tests/qapi-schema/enum-union-clash.out rename to qemu/tests/qapi-schema/args-alternate.out diff --git a/qemu/tests/qapi-schema/args-any.err b/qemu/tests/qapi-schema/args-any.err new file mode 100644 index 000000000..bf9b5e073 --- /dev/null +++ b/qemu/tests/qapi-schema/args-any.err @@ -0,0 +1 @@ +tests/qapi-schema/args-any.json:2: 'data' for command 'oops' cannot use built-in type 'any' diff --git a/qemu/tests/qapi-schema/data-member-array-bad.exit b/qemu/tests/qapi-schema/args-any.exit similarity index 100% rename from qemu/tests/qapi-schema/data-member-array-bad.exit rename to qemu/tests/qapi-schema/args-any.exit diff --git a/qemu/tests/qapi-schema/args-any.json b/qemu/tests/qapi-schema/args-any.json new file mode 100644 index 000000000..58fe5e470 --- /dev/null +++ b/qemu/tests/qapi-schema/args-any.json @@ -0,0 +1,2 @@ +# we do not allow an 'any' argument +{ 'command': 'oops', 'data': 'any' } diff --git a/qemu/tests/qapi-schema/event-max.out b/qemu/tests/qapi-schema/args-any.out similarity index 100% rename from qemu/tests/qapi-schema/event-max.out rename to qemu/tests/qapi-schema/args-any.out diff --git a/qemu/tests/qapi-schema/args-array-empty.err b/qemu/tests/qapi-schema/args-array-empty.err new file mode 100644 index 000000000..cb7ed33b3 --- /dev/null +++ b/qemu/tests/qapi-schema/args-array-empty.err @@ -0,0 +1 @@ +tests/qapi-schema/args-array-empty.json:2: Member 'empty' of 'data' for command 'oops': array type must contain single type name diff --git a/qemu/tests/qapi-schema/data-member-unknown.exit b/qemu/tests/qapi-schema/args-array-empty.exit similarity index 100% rename from qemu/tests/qapi-schema/data-member-unknown.exit rename to qemu/tests/qapi-schema/args-array-empty.exit diff --git a/qemu/tests/qapi-schema/data-array-empty.json b/qemu/tests/qapi-schema/args-array-empty.json similarity index 100% rename from qemu/tests/qapi-schema/data-array-empty.json rename to qemu/tests/qapi-schema/args-array-empty.json diff --git a/qemu/tests/qapi-schema/flat-union-base-star.out b/qemu/tests/qapi-schema/args-array-empty.out similarity index 100% rename from qemu/tests/qapi-schema/flat-union-base-star.out rename to qemu/tests/qapi-schema/args-array-empty.out diff --git a/qemu/tests/qapi-schema/args-array-unknown.err b/qemu/tests/qapi-schema/args-array-unknown.err new file mode 100644 index 000000000..cd7a0f98d --- /dev/null +++ b/qemu/tests/qapi-schema/args-array-unknown.err @@ -0,0 +1 @@ +tests/qapi-schema/args-array-unknown.json:2: Member 'array' of 'data' for command 'oops' uses unknown type 'NoSuchType' diff --git a/qemu/tests/qapi-schema/data-unknown.exit b/qemu/tests/qapi-schema/args-array-unknown.exit similarity index 100% rename from qemu/tests/qapi-schema/data-unknown.exit rename to qemu/tests/qapi-schema/args-array-unknown.exit diff --git a/qemu/tests/qapi-schema/data-array-unknown.json b/qemu/tests/qapi-schema/args-array-unknown.json similarity index 100% rename from qemu/tests/qapi-schema/data-array-unknown.json rename to qemu/tests/qapi-schema/args-array-unknown.json diff --git a/qemu/tests/qapi-schema/flat-union-branch-clash.out b/qemu/tests/qapi-schema/args-array-unknown.out similarity index 100% rename from qemu/tests/qapi-schema/flat-union-branch-clash.out rename to qemu/tests/qapi-schema/args-array-unknown.out diff --git a/qemu/tests/qapi-schema/args-int.err b/qemu/tests/qapi-schema/args-int.err new file mode 100644 index 000000000..dc1d2504f --- /dev/null +++ b/qemu/tests/qapi-schema/args-int.err @@ -0,0 +1 @@ +tests/qapi-schema/args-int.json:2: 'data' for command 'oops' cannot use built-in type 'int' diff --git a/qemu/tests/qapi-schema/enum-max-member.exit b/qemu/tests/qapi-schema/args-int.exit similarity index 100% rename from qemu/tests/qapi-schema/enum-max-member.exit rename to qemu/tests/qapi-schema/args-int.exit diff --git a/qemu/tests/qapi-schema/data-int.json b/qemu/tests/qapi-schema/args-int.json similarity index 100% rename from qemu/tests/qapi-schema/data-int.json rename to qemu/tests/qapi-schema/args-int.json diff --git a/qemu/tests/qapi-schema/flat-union-reverse-define.err b/qemu/tests/qapi-schema/args-int.out similarity index 100% rename from qemu/tests/qapi-schema/flat-union-reverse-define.err rename to qemu/tests/qapi-schema/args-int.out diff --git a/qemu/tests/qapi-schema/args-invalid.err b/qemu/tests/qapi-schema/args-invalid.err new file mode 100644 index 000000000..fe1e94975 --- /dev/null +++ b/qemu/tests/qapi-schema/args-invalid.err @@ -0,0 +1 @@ +tests/qapi-schema/args-invalid.json:1: 'data' for command 'foo' should be a dictionary or type name diff --git a/qemu/tests/qapi-schema/enum-union-clash.exit b/qemu/tests/qapi-schema/args-invalid.exit similarity index 100% rename from qemu/tests/qapi-schema/enum-union-clash.exit rename to qemu/tests/qapi-schema/args-invalid.exit diff --git a/qemu/tests/qapi-schema/args-invalid.json b/qemu/tests/qapi-schema/args-invalid.json new file mode 100644 index 000000000..db0981341 --- /dev/null +++ b/qemu/tests/qapi-schema/args-invalid.json @@ -0,0 +1,2 @@ +{ 'command': 'foo', + 'data': false } diff --git a/qemu/tests/qapi-schema/nested-struct-returns.out b/qemu/tests/qapi-schema/args-invalid.out similarity index 100% rename from qemu/tests/qapi-schema/nested-struct-returns.out rename to qemu/tests/qapi-schema/args-invalid.out diff --git a/qemu/tests/qapi-schema/args-member-array-bad.err b/qemu/tests/qapi-schema/args-member-array-bad.err new file mode 100644 index 000000000..881b4d954 --- /dev/null +++ b/qemu/tests/qapi-schema/args-member-array-bad.err @@ -0,0 +1 @@ +tests/qapi-schema/args-member-array-bad.json:2: Member 'member' of 'data' for command 'oops': array type must contain single type name diff --git a/qemu/tests/qapi-schema/event-max.exit b/qemu/tests/qapi-schema/args-member-array-bad.exit similarity index 100% rename from qemu/tests/qapi-schema/event-max.exit rename to qemu/tests/qapi-schema/args-member-array-bad.exit diff --git a/qemu/tests/qapi-schema/data-member-array-bad.json b/qemu/tests/qapi-schema/args-member-array-bad.json similarity index 100% rename from qemu/tests/qapi-schema/data-member-array-bad.json rename to qemu/tests/qapi-schema/args-member-array-bad.json diff --git a/qemu/tests/qapi-schema/returns-int.err b/qemu/tests/qapi-schema/args-member-array-bad.out similarity index 100% rename from qemu/tests/qapi-schema/returns-int.err rename to qemu/tests/qapi-schema/args-member-array-bad.out diff --git a/qemu/tests/qapi-schema/args-member-case.err b/qemu/tests/qapi-schema/args-member-case.err new file mode 100644 index 000000000..19c442660 --- /dev/null +++ b/qemu/tests/qapi-schema/args-member-case.err @@ -0,0 +1 @@ +tests/qapi-schema/args-member-case.json:2: 'Arg' (parameter of no-way-this-will-get-whitelisted) should not use uppercase diff --git a/qemu/tests/qapi-schema/flat-union-base-star.exit b/qemu/tests/qapi-schema/args-member-case.exit similarity index 100% rename from qemu/tests/qapi-schema/flat-union-base-star.exit rename to qemu/tests/qapi-schema/args-member-case.exit diff --git a/qemu/tests/qapi-schema/args-member-case.json b/qemu/tests/qapi-schema/args-member-case.json new file mode 100644 index 000000000..93439bee8 --- /dev/null +++ b/qemu/tests/qapi-schema/args-member-case.json @@ -0,0 +1,2 @@ +# Member names should be 'lower-case' unless the struct/command is whitelisted +{ 'command': 'no-way-this-will-get-whitelisted', 'data': { 'Arg': 'int' } } diff --git a/qemu/tests/qapi-schema/type-bypass-no-gen.out b/qemu/tests/qapi-schema/args-member-case.out similarity index 100% rename from qemu/tests/qapi-schema/type-bypass-no-gen.out rename to qemu/tests/qapi-schema/args-member-case.out diff --git a/qemu/tests/qapi-schema/args-member-unknown.err b/qemu/tests/qapi-schema/args-member-unknown.err new file mode 100644 index 000000000..f6f82828c --- /dev/null +++ b/qemu/tests/qapi-schema/args-member-unknown.err @@ -0,0 +1 @@ +tests/qapi-schema/args-member-unknown.json:2: Member 'member' of 'data' for command 'oops' uses unknown type 'NoSuchType' diff --git a/qemu/tests/qapi-schema/flat-union-branch-clash.exit b/qemu/tests/qapi-schema/args-member-unknown.exit similarity index 100% rename from qemu/tests/qapi-schema/flat-union-branch-clash.exit rename to qemu/tests/qapi-schema/args-member-unknown.exit diff --git a/qemu/tests/qapi-schema/data-member-unknown.json b/qemu/tests/qapi-schema/args-member-unknown.json similarity index 100% rename from qemu/tests/qapi-schema/data-member-unknown.json rename to qemu/tests/qapi-schema/args-member-unknown.json diff --git a/qemu/tests/qapi-schema/type-bypass.err b/qemu/tests/qapi-schema/args-member-unknown.out similarity index 100% rename from qemu/tests/qapi-schema/type-bypass.err rename to qemu/tests/qapi-schema/args-member-unknown.out diff --git a/qemu/tests/qapi-schema/args-name-clash.err b/qemu/tests/qapi-schema/args-name-clash.err new file mode 100644 index 000000000..d953e8d24 --- /dev/null +++ b/qemu/tests/qapi-schema/args-name-clash.err @@ -0,0 +1 @@ +tests/qapi-schema/args-name-clash.json:4: 'a_b' (parameter of oops) collides with 'a-b' (parameter of oops) diff --git a/qemu/tests/qapi-schema/nested-struct-returns.exit b/qemu/tests/qapi-schema/args-name-clash.exit similarity index 100% rename from qemu/tests/qapi-schema/nested-struct-returns.exit rename to qemu/tests/qapi-schema/args-name-clash.exit diff --git a/qemu/tests/qapi-schema/args-name-clash.json b/qemu/tests/qapi-schema/args-name-clash.json new file mode 100644 index 000000000..61423cb89 --- /dev/null +++ b/qemu/tests/qapi-schema/args-name-clash.json @@ -0,0 +1,4 @@ +# C member name collision +# Reject members that clash when mapped to C names (we would have two 'a_b' +# members). +{ 'command': 'oops', 'data': { 'a-b': 'str', 'a_b': 'str' } } diff --git a/qemu/tests/qapi-schema/union-bad-branch.out b/qemu/tests/qapi-schema/args-name-clash.out similarity index 100% rename from qemu/tests/qapi-schema/union-bad-branch.out rename to qemu/tests/qapi-schema/args-name-clash.out diff --git a/qemu/tests/qapi-schema/args-union.err b/qemu/tests/qapi-schema/args-union.err new file mode 100644 index 000000000..1d693d74d --- /dev/null +++ b/qemu/tests/qapi-schema/args-union.err @@ -0,0 +1 @@ +tests/qapi-schema/args-union.json:4: 'data' for command 'oops' cannot use union type 'Uni' diff --git a/qemu/tests/qapi-schema/type-bypass-no-gen.exit b/qemu/tests/qapi-schema/args-union.exit similarity index 100% rename from qemu/tests/qapi-schema/type-bypass-no-gen.exit rename to qemu/tests/qapi-schema/args-union.exit diff --git a/qemu/tests/qapi-schema/args-union.json b/qemu/tests/qapi-schema/args-union.json new file mode 100644 index 000000000..7bdcbb7f0 --- /dev/null +++ b/qemu/tests/qapi-schema/args-union.json @@ -0,0 +1,4 @@ +# we do not allow union arguments +# TODO should we support this? +{ 'union': 'Uni', 'data': { 'case1': 'int', 'case2': 'str' } } +{ 'command': 'oops', 'data': 'Uni' } diff --git a/qemu/tests/qapi-schema/union-max.out b/qemu/tests/qapi-schema/args-union.out similarity index 100% rename from qemu/tests/qapi-schema/union-max.out rename to qemu/tests/qapi-schema/args-union.out diff --git a/qemu/tests/qapi-schema/args-unknown.err b/qemu/tests/qapi-schema/args-unknown.err new file mode 100644 index 000000000..4d91ec869 --- /dev/null +++ b/qemu/tests/qapi-schema/args-unknown.err @@ -0,0 +1 @@ +tests/qapi-schema/args-unknown.json:2: 'data' for command 'oops' uses unknown type 'NoSuchType' diff --git a/qemu/tests/qapi-schema/union-bad-branch.exit b/qemu/tests/qapi-schema/args-unknown.exit similarity index 100% rename from qemu/tests/qapi-schema/union-bad-branch.exit rename to qemu/tests/qapi-schema/args-unknown.exit diff --git a/qemu/tests/qapi-schema/data-unknown.json b/qemu/tests/qapi-schema/args-unknown.json similarity index 100% rename from qemu/tests/qapi-schema/data-unknown.json rename to qemu/tests/qapi-schema/args-unknown.json diff --git a/qemu/tests/qapi-schema/args-unknown.out b/qemu/tests/qapi-schema/args-unknown.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/base-cycle-direct.err b/qemu/tests/qapi-schema/base-cycle-direct.err new file mode 100644 index 000000000..9c68f6543 --- /dev/null +++ b/qemu/tests/qapi-schema/base-cycle-direct.err @@ -0,0 +1 @@ +tests/qapi-schema/base-cycle-direct.json:2: Object Loopy contains itself diff --git a/qemu/tests/qapi-schema/union-max.exit b/qemu/tests/qapi-schema/base-cycle-direct.exit similarity index 100% rename from qemu/tests/qapi-schema/union-max.exit rename to qemu/tests/qapi-schema/base-cycle-direct.exit diff --git a/qemu/tests/qapi-schema/base-cycle-direct.json b/qemu/tests/qapi-schema/base-cycle-direct.json new file mode 100644 index 000000000..4fc66d051 --- /dev/null +++ b/qemu/tests/qapi-schema/base-cycle-direct.json @@ -0,0 +1,2 @@ +# we reject a loop in base classes +{ 'struct': 'Loopy', 'base': 'Loopy', 'data': {} } diff --git a/qemu/tests/qapi-schema/base-cycle-direct.out b/qemu/tests/qapi-schema/base-cycle-direct.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/base-cycle-indirect.err b/qemu/tests/qapi-schema/base-cycle-indirect.err new file mode 100644 index 000000000..fc92fe47f --- /dev/null +++ b/qemu/tests/qapi-schema/base-cycle-indirect.err @@ -0,0 +1 @@ +tests/qapi-schema/base-cycle-indirect.json:2: Object Base1 contains itself diff --git a/qemu/tests/qapi-schema/base-cycle-indirect.exit b/qemu/tests/qapi-schema/base-cycle-indirect.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/base-cycle-indirect.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/base-cycle-indirect.json b/qemu/tests/qapi-schema/base-cycle-indirect.json new file mode 100644 index 000000000..28667721a --- /dev/null +++ b/qemu/tests/qapi-schema/base-cycle-indirect.json @@ -0,0 +1,3 @@ +# we reject a loop in base classes +{ 'struct': 'Base1', 'base': 'Base2', 'data': {} } +{ 'struct': 'Base2', 'base': 'Base1', 'data': {} } diff --git a/qemu/tests/qapi-schema/base-cycle-indirect.out b/qemu/tests/qapi-schema/base-cycle-indirect.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/command-int.json b/qemu/tests/qapi-schema/command-int.json index c90d408ab..9a62554fc 100644 --- a/qemu/tests/qapi-schema/command-int.json +++ b/qemu/tests/qapi-schema/command-int.json @@ -1,3 +1,2 @@ # we reject collisions between commands and types -{ 'command': 'int', 'data': { 'character': 'str' }, - 'returns': { 'value': 'int' } } +{ 'command': 'int', 'data': { 'character': 'str' } } diff --git a/qemu/tests/qapi-schema/comments.out b/qemu/tests/qapi-schema/comments.out index 4ce3dcf12..5d7c13cad 100644 --- a/qemu/tests/qapi-schema/comments.out +++ b/qemu/tests/qapi-schema/comments.out @@ -1,3 +1,4 @@ -[OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])])] -[{'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']}] -[] +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE +enum Status ['good', 'bad', 'ugly'] +object q_empty diff --git a/qemu/tests/qapi-schema/data-array-empty.err b/qemu/tests/qapi-schema/data-array-empty.err deleted file mode 100644 index f713f1489..000000000 --- a/qemu/tests/qapi-schema/data-array-empty.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/data-array-empty.json:2: Member 'empty' of 'data' for command 'oops': array type must contain single type name diff --git a/qemu/tests/qapi-schema/data-array-unknown.err b/qemu/tests/qapi-schema/data-array-unknown.err deleted file mode 100644 index 8b731bbcc..000000000 --- a/qemu/tests/qapi-schema/data-array-unknown.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/data-array-unknown.json:2: Member 'array' of 'data' for command 'oops' uses unknown type 'array of NoSuchType' diff --git a/qemu/tests/qapi-schema/data-int.err b/qemu/tests/qapi-schema/data-int.err deleted file mode 100644 index 1a9b077c0..000000000 --- a/qemu/tests/qapi-schema/data-int.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/data-int.json:2: 'data' for command 'oops' cannot use built-in type 'int' diff --git a/qemu/tests/qapi-schema/data-member-array-bad.err b/qemu/tests/qapi-schema/data-member-array-bad.err deleted file mode 100644 index 2c072d598..000000000 --- a/qemu/tests/qapi-schema/data-member-array-bad.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/data-member-array-bad.json:2: Member 'member' of 'data' for command 'oops': array type must contain single type name diff --git a/qemu/tests/qapi-schema/data-member-array.exit b/qemu/tests/qapi-schema/data-member-array.exit deleted file mode 100644 index 573541ac9..000000000 --- a/qemu/tests/qapi-schema/data-member-array.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/qemu/tests/qapi-schema/data-member-array.json b/qemu/tests/qapi-schema/data-member-array.json deleted file mode 100644 index e6f7f5da1..000000000 --- a/qemu/tests/qapi-schema/data-member-array.json +++ /dev/null @@ -1,4 +0,0 @@ -# valid array members -{ 'enum': 'abc', 'data': [ 'a', 'b', 'c' ] } -{ 'struct': 'def', 'data': { 'array': [ 'abc' ] } } -{ 'command': 'okay', 'data': { 'member1': [ 'int' ], 'member2': [ 'def' ] } } diff --git a/qemu/tests/qapi-schema/data-member-array.out b/qemu/tests/qapi-schema/data-member-array.out deleted file mode 100644 index c39fa2548..000000000 --- a/qemu/tests/qapi-schema/data-member-array.out +++ /dev/null @@ -1,5 +0,0 @@ -[OrderedDict([('enum', 'abc'), ('data', ['a', 'b', 'c'])]), - OrderedDict([('struct', 'def'), ('data', OrderedDict([('array', ['abc'])]))]), - OrderedDict([('command', 'okay'), ('data', OrderedDict([('member1', ['int']), ('member2', ['def'])]))])] -[{'enum_name': 'abc', 'enum_values': ['a', 'b', 'c']}] -[OrderedDict([('struct', 'def'), ('data', OrderedDict([('array', ['abc'])]))])] diff --git a/qemu/tests/qapi-schema/data-member-unknown.err b/qemu/tests/qapi-schema/data-member-unknown.err deleted file mode 100644 index ab905db80..000000000 --- a/qemu/tests/qapi-schema/data-member-unknown.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/data-member-unknown.json:2: Member 'member' of 'data' for command 'oops' uses unknown type 'NoSuchType' diff --git a/qemu/tests/qapi-schema/data-unknown.err b/qemu/tests/qapi-schema/data-unknown.err deleted file mode 100644 index 5b07277a9..000000000 --- a/qemu/tests/qapi-schema/data-unknown.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/data-unknown.json:2: 'data' for command 'oops' uses unknown type 'NoSuchType' diff --git a/qemu/tests/qapi-schema/duplicate-key.err b/qemu/tests/qapi-schema/duplicate-key.err index 768b276f8..6d02f8353 100644 --- a/qemu/tests/qapi-schema/duplicate-key.err +++ b/qemu/tests/qapi-schema/duplicate-key.err @@ -1 +1 @@ -tests/qapi-schema/duplicate-key.json:2:10: Duplicate key "key" +tests/qapi-schema/duplicate-key.json:3:10: Duplicate key "key" diff --git a/qemu/tests/qapi-schema/duplicate-key.json b/qemu/tests/qapi-schema/duplicate-key.json index 1b55d8810..14ac0e8a4 100644 --- a/qemu/tests/qapi-schema/duplicate-key.json +++ b/qemu/tests/qapi-schema/duplicate-key.json @@ -1,2 +1,3 @@ +# QAPI cannot include the same key more than once in any {} { 'key': 'value', 'key': 'value' } diff --git a/qemu/tests/qapi-schema/empty.out b/qemu/tests/qapi-schema/empty.out index b7f89a45c..8a5b03442 100644 --- a/qemu/tests/qapi-schema/empty.out +++ b/qemu/tests/qapi-schema/empty.out @@ -1,3 +1,3 @@ -[] -[] -[] +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE +object q_empty diff --git a/qemu/tests/qapi-schema/enum-bad-prefix.err b/qemu/tests/qapi-schema/enum-bad-prefix.err new file mode 100644 index 000000000..399f5f7af --- /dev/null +++ b/qemu/tests/qapi-schema/enum-bad-prefix.err @@ -0,0 +1 @@ +tests/qapi-schema/enum-bad-prefix.json:2: Enum 'MyEnum' requires a string for 'prefix' diff --git a/qemu/tests/qapi-schema/enum-bad-prefix.exit b/qemu/tests/qapi-schema/enum-bad-prefix.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/enum-bad-prefix.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/enum-bad-prefix.json b/qemu/tests/qapi-schema/enum-bad-prefix.json new file mode 100644 index 000000000..996f628f6 --- /dev/null +++ b/qemu/tests/qapi-schema/enum-bad-prefix.json @@ -0,0 +1,2 @@ +# The prefix must be a string type +{ 'enum': 'MyEnum', 'data': [ 'one' ], 'prefix': [ 'fish' ] } diff --git a/qemu/tests/qapi-schema/enum-bad-prefix.out b/qemu/tests/qapi-schema/enum-bad-prefix.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/enum-clash-member.err b/qemu/tests/qapi-schema/enum-clash-member.err index 48bd1360e..5403c7850 100644 --- a/qemu/tests/qapi-schema/enum-clash-member.err +++ b/qemu/tests/qapi-schema/enum-clash-member.err @@ -1 +1 @@ -tests/qapi-schema/enum-clash-member.json:2: Enum 'MyEnum' member 'ONE' clashes with 'one' +tests/qapi-schema/enum-clash-member.json:2: 'one_two' (member of MyEnum) collides with 'one-two' (member of MyEnum) diff --git a/qemu/tests/qapi-schema/enum-clash-member.json b/qemu/tests/qapi-schema/enum-clash-member.json index b7dc02a28..b6928b8bf 100644 --- a/qemu/tests/qapi-schema/enum-clash-member.json +++ b/qemu/tests/qapi-schema/enum-clash-member.json @@ -1,2 +1,2 @@ # we reject enums where members will clash when mapped to C enum -{ 'enum': 'MyEnum', 'data': [ 'one', 'ONE' ] } +{ 'enum': 'MyEnum', 'data': [ 'one-two', 'one_two' ] } diff --git a/qemu/tests/qapi-schema/enum-empty.exit b/qemu/tests/qapi-schema/enum-empty.exit deleted file mode 100644 index 573541ac9..000000000 --- a/qemu/tests/qapi-schema/enum-empty.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/qemu/tests/qapi-schema/enum-empty.json b/qemu/tests/qapi-schema/enum-empty.json deleted file mode 100644 index 40d4e85a2..000000000 --- a/qemu/tests/qapi-schema/enum-empty.json +++ /dev/null @@ -1,2 +0,0 @@ -# An empty enum, although unusual, is currently acceptable -{ 'enum': 'MyEnum', 'data': [ ] } diff --git a/qemu/tests/qapi-schema/enum-empty.out b/qemu/tests/qapi-schema/enum-empty.out deleted file mode 100644 index 3b75c1613..000000000 --- a/qemu/tests/qapi-schema/enum-empty.out +++ /dev/null @@ -1,3 +0,0 @@ -[OrderedDict([('enum', 'MyEnum'), ('data', [])])] -[{'enum_name': 'MyEnum', 'enum_values': []}] -[] diff --git a/qemu/tests/qapi-schema/enum-max-member.err b/qemu/tests/qapi-schema/enum-max-member.err deleted file mode 100644 index f77837fb4..000000000 --- a/qemu/tests/qapi-schema/enum-max-member.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/enum-max-member.json:3: Enum 'MyEnum' member 'max' clashes with '(automatic)' diff --git a/qemu/tests/qapi-schema/enum-max-member.json b/qemu/tests/qapi-schema/enum-max-member.json deleted file mode 100644 index 4bcda0bf0..000000000 --- a/qemu/tests/qapi-schema/enum-max-member.json +++ /dev/null @@ -1,3 +0,0 @@ -# we reject user-supplied 'max' for clashing with implicit enum end -# TODO: should we instead munge the implicit value to avoid the clash? -{ 'enum': 'MyEnum', 'data': [ 'max' ] } diff --git a/qemu/tests/qapi-schema/enum-member-case.err b/qemu/tests/qapi-schema/enum-member-case.err new file mode 100644 index 000000000..b652e9aac --- /dev/null +++ b/qemu/tests/qapi-schema/enum-member-case.err @@ -0,0 +1 @@ +tests/qapi-schema/enum-member-case.json:3: 'Value' (member of NoWayThisWillGetWhitelisted) should not use uppercase diff --git a/qemu/tests/qapi-schema/enum-member-case.exit b/qemu/tests/qapi-schema/enum-member-case.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/enum-member-case.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/enum-member-case.json b/qemu/tests/qapi-schema/enum-member-case.json new file mode 100644 index 000000000..2096b350c --- /dev/null +++ b/qemu/tests/qapi-schema/enum-member-case.json @@ -0,0 +1,3 @@ +# Member names should be 'lower-case' unless the enum is whitelisted +{ 'enum': 'UuidInfo', 'data': [ 'Value' ] } # UuidInfo is whitelisted +{ 'enum': 'NoWayThisWillGetWhitelisted', 'data': [ 'Value' ] } diff --git a/qemu/tests/qapi-schema/enum-member-case.out b/qemu/tests/qapi-schema/enum-member-case.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/enum-union-clash.err b/qemu/tests/qapi-schema/enum-union-clash.err deleted file mode 100644 index c04e1a806..000000000 --- a/qemu/tests/qapi-schema/enum-union-clash.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/enum-union-clash.json:2: enum 'UnionKind' should not end in 'Kind' diff --git a/qemu/tests/qapi-schema/enum-union-clash.json b/qemu/tests/qapi-schema/enum-union-clash.json deleted file mode 100644 index 593282b6c..000000000 --- a/qemu/tests/qapi-schema/enum-union-clash.json +++ /dev/null @@ -1,4 +0,0 @@ -# we reject types that would conflict with implicit union enum -{ 'enum': 'UnionKind', 'data': [ 'oops' ] } -{ 'union': 'Union', - 'data': { 'a': 'int' } } diff --git a/qemu/tests/qapi-schema/event-case.out b/qemu/tests/qapi-schema/event-case.out index 3764bc781..b6b4134a8 100644 --- a/qemu/tests/qapi-schema/event-case.out +++ b/qemu/tests/qapi-schema/event-case.out @@ -1,3 +1,4 @@ -[OrderedDict([('event', 'oops')])] -[] -[] +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE +event oops None +object q_empty diff --git a/qemu/tests/qapi-schema/event-max.err b/qemu/tests/qapi-schema/event-max.err deleted file mode 100644 index c85653437..000000000 --- a/qemu/tests/qapi-schema/event-max.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/event-max.json:2: Event name 'MAX' cannot be created diff --git a/qemu/tests/qapi-schema/event-max.json b/qemu/tests/qapi-schema/event-max.json deleted file mode 100644 index f3d7de2a3..000000000 --- a/qemu/tests/qapi-schema/event-max.json +++ /dev/null @@ -1,2 +0,0 @@ -# an event named 'MAX' would conflict with implicit C enum -{ 'event': 'MAX' } diff --git a/qemu/tests/qapi-schema/flat-union-bad-base.err b/qemu/tests/qapi-schema/flat-union-bad-base.err index f9c31b2bf..bee24a217 100644 --- a/qemu/tests/qapi-schema/flat-union-bad-base.err +++ b/qemu/tests/qapi-schema/flat-union-bad-base.err @@ -1 +1 @@ -tests/qapi-schema/flat-union-bad-base.json:9: Flat union 'TestUnion' must have a string base field +tests/qapi-schema/flat-union-bad-base.json:8: 'string' (member of TestTypeA) collides with 'string' (base of TestUnion) diff --git a/qemu/tests/qapi-schema/flat-union-bad-base.json b/qemu/tests/qapi-schema/flat-union-bad-base.json index e2e622bb6..74dd42170 100644 --- a/qemu/tests/qapi-schema/flat-union-bad-base.json +++ b/qemu/tests/qapi-schema/flat-union-bad-base.json @@ -1,5 +1,4 @@ -# we require the base to be an existing struct -# TODO: should we allow an anonymous inline base type? +# we allow anonymous base, but enforce no duplicate keys { 'enum': 'TestEnum', 'data': [ 'value1', 'value2' ] } { 'struct': 'TestTypeA', @@ -7,7 +6,7 @@ { 'struct': 'TestTypeB', 'data': { 'integer': 'int' } } { 'union': 'TestUnion', - 'base': { 'enum1': 'TestEnum', 'kind': 'str' }, + 'base': { 'enum1': 'TestEnum', 'string': 'str' }, 'discriminator': 'enum1', 'data': { 'value1': 'TestTypeA', 'value2': 'TestTypeB' } } diff --git a/qemu/tests/qapi-schema/flat-union-base-any.err b/qemu/tests/qapi-schema/flat-union-base-any.err new file mode 100644 index 000000000..646f1c9cd --- /dev/null +++ b/qemu/tests/qapi-schema/flat-union-base-any.err @@ -0,0 +1 @@ +tests/qapi-schema/flat-union-base-any.json:8: 'base' for union 'TestUnion' cannot use built-in type 'any' diff --git a/qemu/tests/qapi-schema/flat-union-base-any.exit b/qemu/tests/qapi-schema/flat-union-base-any.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/flat-union-base-any.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/flat-union-base-any.json b/qemu/tests/qapi-schema/flat-union-base-any.json new file mode 100644 index 000000000..fe66b713e --- /dev/null +++ b/qemu/tests/qapi-schema/flat-union-base-any.json @@ -0,0 +1,12 @@ +# we require the base to be an existing struct +{ 'enum': 'TestEnum', + 'data': [ 'value1', 'value2' ] } +{ 'struct': 'TestTypeA', + 'data': { 'string': 'str' } } +{ 'struct': 'TestTypeB', + 'data': { 'integer': 'int' } } +{ 'union': 'TestUnion', + 'base': 'any', + 'discriminator': 'enum1', + 'data': { 'value1': 'TestTypeA', + 'value2': 'TestTypeB' } } diff --git a/qemu/tests/qapi-schema/flat-union-base-any.out b/qemu/tests/qapi-schema/flat-union-base-any.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/flat-union-base-star.err b/qemu/tests/qapi-schema/flat-union-base-star.err deleted file mode 100644 index b7748f08b..000000000 --- a/qemu/tests/qapi-schema/flat-union-base-star.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/flat-union-base-star.json:8: Base '**' is not a valid struct diff --git a/qemu/tests/qapi-schema/flat-union-base-star.json b/qemu/tests/qapi-schema/flat-union-base-star.json deleted file mode 100644 index 5099439a9..000000000 --- a/qemu/tests/qapi-schema/flat-union-base-star.json +++ /dev/null @@ -1,12 +0,0 @@ -# we require the base to be an existing struct -{ 'enum': 'TestEnum', - 'data': [ 'value1', 'value2' ] } -{ 'struct': 'TestTypeA', - 'data': { 'string': 'str' } } -{ 'struct': 'TestTypeB', - 'data': { 'integer': 'int' } } -{ 'union': 'TestUnion', - 'base': '**', - 'discriminator': 'enum1', - 'data': { 'value1': 'TestTypeA', - 'value2': 'TestTypeB' } } diff --git a/qemu/tests/qapi-schema/flat-union-base-union.err b/qemu/tests/qapi-schema/flat-union-base-union.err index ede9859a3..f138395e4 100644 --- a/qemu/tests/qapi-schema/flat-union-base-union.err +++ b/qemu/tests/qapi-schema/flat-union-base-union.err @@ -1 +1 @@ -tests/qapi-schema/flat-union-base-union.json:11: Base 'UnionBase' is not a valid struct +tests/qapi-schema/flat-union-base-union.json:14: 'base' for union 'TestUnion' cannot use union type 'UnionBase' diff --git a/qemu/tests/qapi-schema/flat-union-base-union.json b/qemu/tests/qapi-schema/flat-union-base-union.json index 6a8ea687a..98b4eba18 100644 --- a/qemu/tests/qapi-schema/flat-union-base-union.json +++ b/qemu/tests/qapi-schema/flat-union-base-union.json @@ -1,4 +1,7 @@ -# we require the base to be a struct +# For now, we require the base to be a struct without variants +# TODO: It would be possible to allow a union as a base, as long as all +# permutations of QMP names exposed by base do not clash with any QMP +# member names added by local variants. { 'enum': 'TestEnum', 'data': [ 'value1', 'value2' ] } { 'struct': 'TestTypeA', diff --git a/qemu/tests/qapi-schema/flat-union-branch-clash.err b/qemu/tests/qapi-schema/flat-union-branch-clash.err deleted file mode 100644 index f11276688..000000000 --- a/qemu/tests/qapi-schema/flat-union-branch-clash.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/flat-union-branch-clash.json:10: Member name 'name' of branch 'value1' clashes with base 'Base' diff --git a/qemu/tests/qapi-schema/flat-union-branch-clash.json b/qemu/tests/qapi-schema/flat-union-branch-clash.json deleted file mode 100644 index 8fb054f00..000000000 --- a/qemu/tests/qapi-schema/flat-union-branch-clash.json +++ /dev/null @@ -1,14 +0,0 @@ -# we check for no duplicate keys between branches and base -{ 'enum': 'TestEnum', - 'data': [ 'value1', 'value2' ] } -{ 'struct': 'Base', - 'data': { 'enum1': 'TestEnum', '*name': 'str' } } -{ 'struct': 'Branch1', - 'data': { 'name': 'str' } } -{ 'struct': 'Branch2', - 'data': { 'value': 'int' } } -{ 'union': 'TestUnion', - 'base': 'Base', - 'discriminator': 'enum1', - 'data': { 'value1': 'Branch1', - 'value2': 'Branch2' } } diff --git a/qemu/tests/qapi-schema/flat-union-clash-member.err b/qemu/tests/qapi-schema/flat-union-clash-member.err new file mode 100644 index 000000000..2adf69755 --- /dev/null +++ b/qemu/tests/qapi-schema/flat-union-clash-member.err @@ -0,0 +1 @@ +tests/qapi-schema/flat-union-clash-member.json:11: 'name' (member of Branch1) collides with 'name' (member of Base) diff --git a/qemu/tests/qapi-schema/flat-union-clash-member.exit b/qemu/tests/qapi-schema/flat-union-clash-member.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/flat-union-clash-member.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/flat-union-clash-member.json b/qemu/tests/qapi-schema/flat-union-clash-member.json new file mode 100644 index 000000000..9efc7719b --- /dev/null +++ b/qemu/tests/qapi-schema/flat-union-clash-member.json @@ -0,0 +1,15 @@ +# We check for no duplicate keys between branch members and base +# base's member 'name' clashes with Branch1's +{ 'enum': 'TestEnum', + 'data': [ 'value1', 'value2' ] } +{ 'struct': 'Base', + 'data': { 'enum1': 'TestEnum', '*name': 'str' } } +{ 'struct': 'Branch1', + 'data': { 'name': 'str' } } +{ 'struct': 'Branch2', + 'data': { 'value': 'int' } } +{ 'union': 'TestUnion', + 'base': 'Base', + 'discriminator': 'enum1', + 'data': { 'value1': 'Branch1', + 'value2': 'Branch2' } } diff --git a/qemu/tests/qapi-schema/flat-union-clash-member.out b/qemu/tests/qapi-schema/flat-union-clash-member.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/flat-union-empty.err b/qemu/tests/qapi-schema/flat-union-empty.err new file mode 100644 index 000000000..15754f54e --- /dev/null +++ b/qemu/tests/qapi-schema/flat-union-empty.err @@ -0,0 +1 @@ +tests/qapi-schema/flat-union-empty.json:4: Union 'Union' cannot have empty 'data' diff --git a/qemu/tests/qapi-schema/flat-union-empty.exit b/qemu/tests/qapi-schema/flat-union-empty.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/flat-union-empty.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/flat-union-empty.json b/qemu/tests/qapi-schema/flat-union-empty.json new file mode 100644 index 000000000..77f1d9abf --- /dev/null +++ b/qemu/tests/qapi-schema/flat-union-empty.json @@ -0,0 +1,4 @@ +# flat unions cannot be empty +{ 'enum': 'Empty', 'data': [ ] } +{ 'struct': 'Base', 'data': { 'type': 'Empty' } } +{ 'union': 'Union', 'base': 'Base', 'discriminator': 'type', 'data': { } } diff --git a/qemu/tests/qapi-schema/flat-union-empty.out b/qemu/tests/qapi-schema/flat-union-empty.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/flat-union-inline.err b/qemu/tests/qapi-schema/flat-union-inline.err index ec586277b..2333358d2 100644 --- a/qemu/tests/qapi-schema/flat-union-inline.err +++ b/qemu/tests/qapi-schema/flat-union-inline.err @@ -1 +1 @@ -tests/qapi-schema/flat-union-inline.json:7: Flat union 'TestUnion' must have a string base field +tests/qapi-schema/flat-union-inline.json:7: Member 'value1' of union 'TestUnion' should be a type name diff --git a/qemu/tests/qapi-schema/flat-union-inline.json b/qemu/tests/qapi-schema/flat-union-inline.json index 6bfdd6581..62c7cda61 100644 --- a/qemu/tests/qapi-schema/flat-union-inline.json +++ b/qemu/tests/qapi-schema/flat-union-inline.json @@ -1,11 +1,11 @@ # we require branches to be a struct name -# TODO: should we allow anonymous inline types? +# TODO: should we allow anonymous inline branch types? { 'enum': 'TestEnum', 'data': [ 'value1', 'value2' ] } { 'struct': 'Base', 'data': { 'enum1': 'TestEnum', 'kind': 'str' } } { 'union': 'TestUnion', - 'base': { 'enum1': 'TestEnum', 'kind': 'str' }, + 'base': 'Base', 'discriminator': 'enum1', 'data': { 'value1': { 'string': 'str' }, 'value2': { 'integer': 'int' } } } diff --git a/qemu/tests/qapi-schema/flat-union-no-base.err b/qemu/tests/qapi-schema/flat-union-no-base.err index bb3f70874..841c93b55 100644 --- a/qemu/tests/qapi-schema/flat-union-no-base.err +++ b/qemu/tests/qapi-schema/flat-union-no-base.err @@ -1 +1 @@ -tests/qapi-schema/flat-union-no-base.json:9: Flat union 'TestUnion' must have a string base field +tests/qapi-schema/flat-union-no-base.json:9: Flat union 'TestUnion' must have a base diff --git a/qemu/tests/qapi-schema/flat-union-reverse-define.exit b/qemu/tests/qapi-schema/flat-union-reverse-define.exit deleted file mode 100644 index 573541ac9..000000000 --- a/qemu/tests/qapi-schema/flat-union-reverse-define.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/qemu/tests/qapi-schema/flat-union-reverse-define.json b/qemu/tests/qapi-schema/flat-union-reverse-define.json deleted file mode 100644 index 648bbfe2b..000000000 --- a/qemu/tests/qapi-schema/flat-union-reverse-define.json +++ /dev/null @@ -1,17 +0,0 @@ -{ 'union': 'TestUnion', - 'base': 'TestBase', - 'discriminator': 'enum1', - 'data': { 'value1': 'TestTypeA', - 'value2': 'TestTypeB' } } - -{ 'struct': 'TestBase', - 'data': { 'enum1': 'TestEnum' } } - -{ 'enum': 'TestEnum', - 'data': [ 'value1', 'value2' ] } - -{ 'struct': 'TestTypeA', - 'data': { 'string': 'str' } } - -{ 'struct': 'TestTypeB', - 'data': { 'integer': 'int' } } diff --git a/qemu/tests/qapi-schema/flat-union-reverse-define.out b/qemu/tests/qapi-schema/flat-union-reverse-define.out deleted file mode 100644 index 1ed7b8a51..000000000 --- a/qemu/tests/qapi-schema/flat-union-reverse-define.out +++ /dev/null @@ -1,9 +0,0 @@ -[OrderedDict([('union', 'TestUnion'), ('base', 'TestBase'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'TestTypeA'), ('value2', 'TestTypeB')]))]), - OrderedDict([('struct', 'TestBase'), ('data', OrderedDict([('enum1', 'TestEnum')]))]), - OrderedDict([('enum', 'TestEnum'), ('data', ['value1', 'value2'])]), - OrderedDict([('struct', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]), - OrderedDict([('struct', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))])] -[{'enum_name': 'TestEnum', 'enum_values': ['value1', 'value2']}] -[OrderedDict([('struct', 'TestBase'), ('data', OrderedDict([('enum1', 'TestEnum')]))]), - OrderedDict([('struct', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]), - OrderedDict([('struct', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))])] diff --git a/qemu/tests/qapi-schema/ident-with-escape.out b/qemu/tests/qapi-schema/ident-with-escape.out index 402843081..382ce2fa2 100644 --- a/qemu/tests/qapi-schema/ident-with-escape.out +++ b/qemu/tests/qapi-schema/ident-with-escape.out @@ -1,3 +1,7 @@ -[OrderedDict([('command', 'fooA'), ('data', OrderedDict([('bar1', 'str')]))])] -[] -[] +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE +command fooA q_obj_fooA-arg -> None + gen=True success_response=True +object q_empty +object q_obj_fooA-arg + member bar1: str optional=False diff --git a/qemu/tests/qapi-schema/include-non-file.err b/qemu/tests/qapi-schema/include-non-file.err index 9658c7880..faae1eacf 100644 --- a/qemu/tests/qapi-schema/include-non-file.err +++ b/qemu/tests/qapi-schema/include-non-file.err @@ -1 +1 @@ -tests/qapi-schema/include-non-file.json:1: Expected a file name (string), got: ['foo', 'bar'] +tests/qapi-schema/include-non-file.json:1: Value of 'include' must be a string diff --git a/qemu/tests/qapi-schema/include-non-file.json b/qemu/tests/qapi-schema/include-non-file.json index cd43c3f9d..4711aa42e 100644 --- a/qemu/tests/qapi-schema/include-non-file.json +++ b/qemu/tests/qapi-schema/include-non-file.json @@ -1 +1 @@ -{ 'include': [ 'foo', 'bar' ] } +{ 'include': {} } diff --git a/qemu/tests/qapi-schema/include-relpath.out b/qemu/tests/qapi-schema/include-relpath.out index 4ce3dcf12..5d7c13cad 100644 --- a/qemu/tests/qapi-schema/include-relpath.out +++ b/qemu/tests/qapi-schema/include-relpath.out @@ -1,3 +1,4 @@ -[OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])])] -[{'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']}] -[] +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE +enum Status ['good', 'bad', 'ugly'] +object q_empty diff --git a/qemu/tests/qapi-schema/include-repetition.out b/qemu/tests/qapi-schema/include-repetition.out index 4ce3dcf12..5d7c13cad 100644 --- a/qemu/tests/qapi-schema/include-repetition.out +++ b/qemu/tests/qapi-schema/include-repetition.out @@ -1,3 +1,4 @@ -[OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])])] -[{'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']}] -[] +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE +enum Status ['good', 'bad', 'ugly'] +object q_empty diff --git a/qemu/tests/qapi-schema/include-simple.out b/qemu/tests/qapi-schema/include-simple.out index 4ce3dcf12..5d7c13cad 100644 --- a/qemu/tests/qapi-schema/include-simple.out +++ b/qemu/tests/qapi-schema/include-simple.out @@ -1,3 +1,4 @@ -[OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])])] -[{'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']}] -[] +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE +enum Status ['good', 'bad', 'ugly'] +object q_empty diff --git a/qemu/tests/qapi-schema/indented-expr.out b/qemu/tests/qapi-schema/indented-expr.out index b5ce9151b..ae3293a3a 100644 --- a/qemu/tests/qapi-schema/indented-expr.out +++ b/qemu/tests/qapi-schema/indented-expr.out @@ -1,3 +1,7 @@ -[OrderedDict([('command', 'eins')]), OrderedDict([('command', 'zwei')])] -[] -[] +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE +command eins None -> None + gen=True success_response=True +object q_empty +command zwei None -> None + gen=True success_response=True diff --git a/qemu/tests/qapi-schema/leading-comma-list.err b/qemu/tests/qapi-schema/leading-comma-list.err new file mode 100644 index 000000000..f5c870bb9 --- /dev/null +++ b/qemu/tests/qapi-schema/leading-comma-list.err @@ -0,0 +1 @@ +tests/qapi-schema/leading-comma-list.json:2:13: Expected "{", "[", "]", string, boolean or "null" diff --git a/qemu/tests/qapi-schema/leading-comma-list.exit b/qemu/tests/qapi-schema/leading-comma-list.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/leading-comma-list.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/leading-comma-list.json b/qemu/tests/qapi-schema/leading-comma-list.json new file mode 100644 index 000000000..c5ba50159 --- /dev/null +++ b/qemu/tests/qapi-schema/leading-comma-list.json @@ -0,0 +1,2 @@ +{ 'enum': 'Status', + 'data': [ , 'good', 'bad', 'ugly' ] } diff --git a/qemu/tests/qapi-schema/leading-comma-list.out b/qemu/tests/qapi-schema/leading-comma-list.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/leading-comma-object.err b/qemu/tests/qapi-schema/leading-comma-object.err new file mode 100644 index 000000000..f767b9554 --- /dev/null +++ b/qemu/tests/qapi-schema/leading-comma-object.err @@ -0,0 +1 @@ +tests/qapi-schema/leading-comma-object.json:1:3: Expected string or "}" diff --git a/qemu/tests/qapi-schema/leading-comma-object.exit b/qemu/tests/qapi-schema/leading-comma-object.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/leading-comma-object.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/leading-comma-object.json b/qemu/tests/qapi-schema/leading-comma-object.json new file mode 100644 index 000000000..c89023ff3 --- /dev/null +++ b/qemu/tests/qapi-schema/leading-comma-object.json @@ -0,0 +1,2 @@ +{ , 'enum': 'Status', + 'data': [ 'good', 'bad', 'ugly' ] } diff --git a/qemu/tests/qapi-schema/leading-comma-object.out b/qemu/tests/qapi-schema/leading-comma-object.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/nested-struct-data.json b/qemu/tests/qapi-schema/nested-struct-data.json index 3d52d2b39..efbe773de 100644 --- a/qemu/tests/qapi-schema/nested-struct-data.json +++ b/qemu/tests/qapi-schema/nested-struct-data.json @@ -1,4 +1,3 @@ # inline subtypes collide with our desired future use of defaults { 'command': 'foo', - 'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' }, - 'returns': {} } + 'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } } diff --git a/qemu/tests/qapi-schema/nested-struct-returns.err b/qemu/tests/qapi-schema/nested-struct-returns.err deleted file mode 100644 index 5238d075b..000000000 --- a/qemu/tests/qapi-schema/nested-struct-returns.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/nested-struct-returns.json:2: Member 'a' of 'returns' for command 'foo' should be a type name diff --git a/qemu/tests/qapi-schema/nested-struct-returns.json b/qemu/tests/qapi-schema/nested-struct-returns.json deleted file mode 100644 index d2cd047f0..000000000 --- a/qemu/tests/qapi-schema/nested-struct-returns.json +++ /dev/null @@ -1,3 +0,0 @@ -# inline subtypes collide with our desired future use of defaults -{ 'command': 'foo', - 'returns': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } } diff --git a/qemu/tests/qapi-schema/qapi-schema-test.json b/qemu/tests/qapi-schema/qapi-schema-test.json index c7eaa865d..f571e1bb3 100644 --- a/qemu/tests/qapi-schema/qapi-schema-test.json +++ b/qemu/tests/qapi-schema/qapi-schema-test.json @@ -1,19 +1,42 @@ # *-*- Mode: Python -*-* +# This file is a stress test of supported qapi constructs that must +# parse and compile correctly. + +{ 'struct': 'TestStruct', + 'data': { 'integer': 'int', 'boolean': 'bool', 'string': 'str' } } + # for testing enums -{ 'enum': 'EnumOne', - 'data': [ 'value1', 'value2', 'value3' ] } { 'struct': 'NestedEnumsOne', - 'data': { 'enum1': 'EnumOne', '*enum2': 'EnumOne', 'enum3': 'EnumOne', '*enum4': 'EnumOne' } } + 'data': { 'enum1': 'EnumOne', # Intentional forward reference + '*enum2': 'EnumOne', 'enum3': 'EnumOne', '*enum4': 'EnumOne' } } + +# An empty enum, although unusual, is currently acceptable +{ 'enum': 'MyEnum', 'data': [ ] } + +# Likewise for an empty struct, including an empty base +{ 'struct': 'Empty1', 'data': { } } +{ 'struct': 'Empty2', 'base': 'Empty1', 'data': { } } + +{ 'command': 'user_def_cmd0', 'data': 'Empty2', 'returns': 'Empty2' } + +# for testing override of default naming heuristic +{ 'enum': 'QEnumTwo', + 'prefix': 'QENUM_TWO', + 'data': [ 'value1', 'value2' ] } # for testing nested structs +{ 'struct': 'UserDefOne', + 'base': 'UserDefZero', # intentional forward reference + 'data': { 'string': 'str', + '*enum1': 'EnumOne' } } # intentional forward reference + +{ 'enum': 'EnumOne', + 'data': [ 'value1', 'value2', 'value3' ] } + { 'struct': 'UserDefZero', 'data': { 'integer': 'int' } } -{ 'struct': 'UserDefOne', - 'base': 'UserDefZero', - 'data': { 'string': 'str', '*enum1': 'EnumOne' } } - { 'struct': 'UserDefTwoDictDict', 'data': { 'userdef': 'UserDefOne', 'string': 'str' } } @@ -26,35 +49,55 @@ 'data': { 'string0': 'str', 'dict1': 'UserDefTwoDict' } } +# dummy struct to force generation of array types not otherwise mentioned +{ 'struct': 'ForceArrays', + 'data': { 'unused1':['UserDefOne'], 'unused2':['UserDefTwo'], + 'unused3':['TestStruct'] } } + # for testing unions +# Among other things, test that a name collision between branches does +# not cause any problems (since only one branch can be in use at a time), +# by intentionally using two branches that both have a C member 'a_b' { 'struct': 'UserDefA', - 'data': { 'boolean': 'bool' } } + 'data': { 'boolean': 'bool', '*a_b': 'int' } } { 'struct': 'UserDefB', - 'data': { 'integer': 'int' } } + 'data': { 'intb': 'int', '*a-b': 'bool' } } -{ 'struct': 'UserDefC', - 'data': { 'string1': 'str', 'string2': 'str' } } +{ 'union': 'UserDefFlatUnion', + 'base': 'UserDefUnionBase', # intentional forward reference + 'discriminator': 'enum1', + 'data': { 'value1' : 'UserDefA', + 'value2' : 'UserDefB', + 'value3' : 'UserDefB' } } { 'struct': 'UserDefUnionBase', + 'base': 'UserDefZero', 'data': { 'string': 'str', 'enum1': 'EnumOne' } } -{ 'union': 'UserDefFlatUnion', - 'base': 'UserDefUnionBase', - 'discriminator': 'enum1', - 'data': { 'value1' : 'UserDefA', 'value2' : 'UserDefB', 'value3' : 'UserDefB' } } -# FIXME generated struct UserDefFlatUnion has members for direct base -# UserDefOne, but lacks members for indirect base UserDefZero - -# this variant of UserDefFlatUnion defaults to a union that uses fields with +# this variant of UserDefFlatUnion defaults to a union that uses members with # allocated types to test corner cases in the cleanup/dealloc visitor { 'union': 'UserDefFlatUnion2', - 'base': 'UserDefUnionBase', + 'base': { '*integer': 'int', 'string': 'str', 'enum1': 'QEnumTwo' }, 'discriminator': 'enum1', - 'data': { 'value1' : 'UserDefC', 'value2' : 'UserDefB', 'value3' : 'UserDefA' } } + 'data': { 'value1' : 'UserDefC', # intentional forward reference + 'value2' : 'UserDefB' } } +{ 'struct': 'WrapAlternate', + 'data': { 'alt': 'UserDefAlternate' } } { 'alternate': 'UserDefAlternate', - 'data': { 'uda': 'UserDefA', 's': 'str', 'i': 'int' } } + 'data': { 'udfu': 'UserDefFlatUnion', 's': 'str', 'i': 'int' } } + +{ 'struct': 'UserDefC', + 'data': { 'string1': 'str', 'string2': 'str' } } + +# for testing use of 'number' within alternates +{ 'alternate': 'AltStrBool', 'data': { 's': 'str', 'b': 'bool' } } +{ 'alternate': 'AltStrNum', 'data': { 's': 'str', 'n': 'number' } } +{ 'alternate': 'AltNumStr', 'data': { 'n': 'number', 's': 'str' } } +{ 'alternate': 'AltStrInt', 'data': { 's': 'str', 'i': 'int' } } +{ 'alternate': 'AltIntNum', 'data': { 'i': 'int', 'n': 'number' } } +{ 'alternate': 'AltNumInt', 'data': { 'n': 'number', 'i': 'int' } } # for testing native lists { 'union': 'UserDefNativeListUnion', @@ -70,7 +113,8 @@ 'number': ['number'], 'boolean': ['bool'], 'string': ['str'], - 'sizes': ['size'] } } + 'sizes': ['size'], + 'any': ['any'] } } # testing commands { 'command': 'user_def_cmd', 'data': {} } @@ -78,8 +122,11 @@ { 'command': 'user_def_cmd2', 'data': {'ud1a': 'UserDefOne', '*ud1b': 'UserDefOne'}, 'returns': 'UserDefTwo' } -{ 'command': 'user_def_cmd3', 'data': {'a': 'int', '*b': 'int' }, + +# Returning a non-dictionary requires a name from the whitelist +{ 'command': 'guest-get-time', 'data': {'a': 'int', '*b': 'int' }, 'returns': 'int' } +{ 'command': 'guest-sync', 'data': { 'arg': 'any' }, 'returns': 'any' } # For testing integer range flattening in opts-visitor. The following schema # corresponds to the option format: @@ -108,12 +155,13 @@ { 'event': 'EVENT_D', 'data': { 'a' : 'EventStructOne', 'b' : 'str', '*c': 'str', '*enum3': 'EnumOne' } } -# test that we correctly compile downstream extensions +# test that we correctly compile downstream extensions, as well as munge +# ticklish names { 'enum': '__org.qemu_x-Enum', 'data': [ '__org.qemu_x-value' ] } { 'struct': '__org.qemu_x-Base', 'data': { '__org.qemu_x-member1': '__org.qemu_x-Enum' } } { 'struct': '__org.qemu_x-Struct', 'base': '__org.qemu_x-Base', - 'data': { '__org.qemu_x-member2': 'str' } } + 'data': { '__org.qemu_x-member2': 'str', '*wchar-t': 'int' } } { 'union': '__org.qemu_x-Union1', 'data': { '__org.qemu_x-branch': 'str' } } { 'struct': '__org.qemu_x-Struct2', 'data': { 'array': ['__org.qemu_x-Union1'] } } diff --git a/qemu/tests/qapi-schema/qapi-schema-test.out b/qemu/tests/qapi-schema/qapi-schema-test.out index cf0ccc402..19cd214f6 100644 --- a/qemu/tests/qapi-schema/qapi-schema-test.out +++ b/qemu/tests/qapi-schema/qapi-schema-test.out @@ -1,55 +1,211 @@ -[OrderedDict([('enum', 'EnumOne'), ('data', ['value1', 'value2', 'value3'])]), - OrderedDict([('struct', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]), - OrderedDict([('struct', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]), - OrderedDict([('struct', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]), - OrderedDict([('struct', 'UserDefTwoDictDict'), ('data', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]), - OrderedDict([('struct', 'UserDefTwoDict'), ('data', OrderedDict([('string1', 'str'), ('dict2', 'UserDefTwoDictDict'), ('*dict3', 'UserDefTwoDictDict')]))]), - OrderedDict([('struct', 'UserDefTwo'), ('data', OrderedDict([('string0', 'str'), ('dict1', 'UserDefTwoDict')]))]), - OrderedDict([('struct', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]), - OrderedDict([('struct', 'UserDefB'), ('data', OrderedDict([('integer', 'int')]))]), - OrderedDict([('struct', 'UserDefC'), ('data', OrderedDict([('string1', 'str'), ('string2', 'str')]))]), - OrderedDict([('struct', 'UserDefUnionBase'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]), - OrderedDict([('union', 'UserDefFlatUnion'), ('base', 'UserDefUnionBase'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'UserDefA'), ('value2', 'UserDefB'), ('value3', 'UserDefB')]))]), - OrderedDict([('union', 'UserDefFlatUnion2'), ('base', 'UserDefUnionBase'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'UserDefC'), ('value2', 'UserDefB'), ('value3', 'UserDefA')]))]), - OrderedDict([('alternate', 'UserDefAlternate'), ('data', OrderedDict([('uda', 'UserDefA'), ('s', 'str'), ('i', 'int')]))]), - OrderedDict([('union', 'UserDefNativeListUnion'), ('data', OrderedDict([('integer', ['int']), ('s8', ['int8']), ('s16', ['int16']), ('s32', ['int32']), ('s64', ['int64']), ('u8', ['uint8']), ('u16', ['uint16']), ('u32', ['uint32']), ('u64', ['uint64']), ('number', ['number']), ('boolean', ['bool']), ('string', ['str']), ('sizes', ['size'])]))]), - OrderedDict([('command', 'user_def_cmd'), ('data', OrderedDict())]), - OrderedDict([('command', 'user_def_cmd1'), ('data', OrderedDict([('ud1a', 'UserDefOne')]))]), - OrderedDict([('command', 'user_def_cmd2'), ('data', OrderedDict([('ud1a', 'UserDefOne'), ('*ud1b', 'UserDefOne')])), ('returns', 'UserDefTwo')]), - OrderedDict([('command', 'user_def_cmd3'), ('data', OrderedDict([('a', 'int'), ('*b', 'int')])), ('returns', 'int')]), - OrderedDict([('struct', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))]), - OrderedDict([('struct', 'EventStructOne'), ('data', OrderedDict([('struct1', 'UserDefOne'), ('string', 'str'), ('*enum2', 'EnumOne')]))]), - OrderedDict([('event', 'EVENT_A')]), - OrderedDict([('event', 'EVENT_B'), ('data', OrderedDict())]), - OrderedDict([('event', 'EVENT_C'), ('data', OrderedDict([('*a', 'int'), ('*b', 'UserDefOne'), ('c', 'str')]))]), - OrderedDict([('event', 'EVENT_D'), ('data', OrderedDict([('a', 'EventStructOne'), ('b', 'str'), ('*c', 'str'), ('*enum3', 'EnumOne')]))]), - OrderedDict([('enum', '__org.qemu_x-Enum'), ('data', ['__org.qemu_x-value'])]), - OrderedDict([('struct', '__org.qemu_x-Base'), ('data', OrderedDict([('__org.qemu_x-member1', '__org.qemu_x-Enum')]))]), - OrderedDict([('struct', '__org.qemu_x-Struct'), ('base', '__org.qemu_x-Base'), ('data', OrderedDict([('__org.qemu_x-member2', 'str')]))]), - OrderedDict([('union', '__org.qemu_x-Union1'), ('data', OrderedDict([('__org.qemu_x-branch', 'str')]))]), - OrderedDict([('struct', '__org.qemu_x-Struct2'), ('data', OrderedDict([('array', ['__org.qemu_x-Union1'])]))]), - OrderedDict([('union', '__org.qemu_x-Union2'), ('base', '__org.qemu_x-Base'), ('discriminator', '__org.qemu_x-member1'), ('data', OrderedDict([('__org.qemu_x-value', '__org.qemu_x-Struct2')]))]), - OrderedDict([('alternate', '__org.qemu_x-Alt'), ('data', OrderedDict([('__org.qemu_x-branch', 'str'), ('b', '__org.qemu_x-Base')]))]), - OrderedDict([('event', '__ORG.QEMU_X-EVENT'), ('data', '__org.qemu_x-Struct')]), - OrderedDict([('command', '__org.qemu_x-command'), ('data', OrderedDict([('a', ['__org.qemu_x-Enum']), ('b', ['__org.qemu_x-Struct']), ('c', '__org.qemu_x-Union2'), ('d', '__org.qemu_x-Alt')])), ('returns', '__org.qemu_x-Union1')])] -[{'enum_name': 'EnumOne', 'enum_values': ['value1', 'value2', 'value3']}, - {'enum_name': '__org.qemu_x-Enum', 'enum_values': ['__org.qemu_x-value']}, - {'enum_name': 'UserDefAlternateKind', 'enum_values': None}, - {'enum_name': 'UserDefNativeListUnionKind', 'enum_values': None}, - {'enum_name': '__org.qemu_x-Union1Kind', 'enum_values': None}, - {'enum_name': '__org.qemu_x-AltKind', 'enum_values': None}] -[OrderedDict([('struct', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]), - OrderedDict([('struct', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]), - OrderedDict([('struct', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]), - OrderedDict([('struct', 'UserDefTwoDictDict'), ('data', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]), - OrderedDict([('struct', 'UserDefTwoDict'), ('data', OrderedDict([('string1', 'str'), ('dict2', 'UserDefTwoDictDict'), ('*dict3', 'UserDefTwoDictDict')]))]), - OrderedDict([('struct', 'UserDefTwo'), ('data', OrderedDict([('string0', 'str'), ('dict1', 'UserDefTwoDict')]))]), - OrderedDict([('struct', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]), - OrderedDict([('struct', 'UserDefB'), ('data', OrderedDict([('integer', 'int')]))]), - OrderedDict([('struct', 'UserDefC'), ('data', OrderedDict([('string1', 'str'), ('string2', 'str')]))]), - OrderedDict([('struct', 'UserDefUnionBase'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]), - OrderedDict([('struct', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))]), - OrderedDict([('struct', 'EventStructOne'), ('data', OrderedDict([('struct1', 'UserDefOne'), ('string', 'str'), ('*enum2', 'EnumOne')]))]), - OrderedDict([('struct', '__org.qemu_x-Base'), ('data', OrderedDict([('__org.qemu_x-member1', '__org.qemu_x-Enum')]))]), - OrderedDict([('struct', '__org.qemu_x-Struct'), ('base', '__org.qemu_x-Base'), ('data', OrderedDict([('__org.qemu_x-member2', 'str')]))]), - OrderedDict([('struct', '__org.qemu_x-Struct2'), ('data', OrderedDict([('array', ['__org.qemu_x-Union1'])]))])] +alternate AltIntNum + case i: int + case n: number +alternate AltNumInt + case n: number + case i: int +alternate AltNumStr + case n: number + case s: str +alternate AltStrBool + case s: str + case b: bool +alternate AltStrInt + case s: str + case i: int +alternate AltStrNum + case s: str + case n: number +event EVENT_A None +event EVENT_B None +event EVENT_C q_obj_EVENT_C-arg +event EVENT_D q_obj_EVENT_D-arg +object Empty1 +object Empty2 + base Empty1 +enum EnumOne ['value1', 'value2', 'value3'] +object EventStructOne + member struct1: UserDefOne optional=False + member string: str optional=False + member enum2: EnumOne optional=True +object ForceArrays + member unused1: UserDefOneList optional=False + member unused2: UserDefTwoList optional=False + member unused3: TestStructList optional=False +enum MyEnum [] +object NestedEnumsOne + member enum1: EnumOne optional=False + member enum2: EnumOne optional=True + member enum3: EnumOne optional=False + member enum4: EnumOne optional=True +enum QEnumTwo ['value1', 'value2'] + prefix QENUM_TWO +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE +object TestStruct + member integer: int optional=False + member boolean: bool optional=False + member string: str optional=False +object UserDefA + member boolean: bool optional=False + member a_b: int optional=True +alternate UserDefAlternate + case udfu: UserDefFlatUnion + case s: str + case i: int +object UserDefB + member intb: int optional=False + member a-b: bool optional=True +object UserDefC + member string1: str optional=False + member string2: str optional=False +object UserDefFlatUnion + base UserDefUnionBase + tag enum1 + case value1: UserDefA + case value2: UserDefB + case value3: UserDefB +object UserDefFlatUnion2 + base q_obj_UserDefFlatUnion2-base + tag enum1 + case value1: UserDefC + case value2: UserDefB +object UserDefNativeListUnion + member type: UserDefNativeListUnionKind optional=False + case integer: q_obj_intList-wrapper + case s8: q_obj_int8List-wrapper + case s16: q_obj_int16List-wrapper + case s32: q_obj_int32List-wrapper + case s64: q_obj_int64List-wrapper + case u8: q_obj_uint8List-wrapper + case u16: q_obj_uint16List-wrapper + case u32: q_obj_uint32List-wrapper + case u64: q_obj_uint64List-wrapper + case number: q_obj_numberList-wrapper + case boolean: q_obj_boolList-wrapper + case string: q_obj_strList-wrapper + case sizes: q_obj_sizeList-wrapper + case any: q_obj_anyList-wrapper +enum UserDefNativeListUnionKind ['integer', 's8', 's16', 's32', 's64', 'u8', 'u16', 'u32', 'u64', 'number', 'boolean', 'string', 'sizes', 'any'] +object UserDefOne + base UserDefZero + member string: str optional=False + member enum1: EnumOne optional=True +object UserDefOptions + member i64: intList optional=True + member u64: uint64List optional=True + member u16: uint16List optional=True + member i64x: int optional=True + member u64x: uint64 optional=True +object UserDefTwo + member string0: str optional=False + member dict1: UserDefTwoDict optional=False +object UserDefTwoDict + member string1: str optional=False + member dict2: UserDefTwoDictDict optional=False + member dict3: UserDefTwoDictDict optional=True +object UserDefTwoDictDict + member userdef: UserDefOne optional=False + member string: str optional=False +object UserDefUnionBase + base UserDefZero + member string: str optional=False + member enum1: EnumOne optional=False +object UserDefZero + member integer: int optional=False +object WrapAlternate + member alt: UserDefAlternate optional=False +event __ORG.QEMU_X-EVENT __org.qemu_x-Struct +alternate __org.qemu_x-Alt + case __org.qemu_x-branch: str + case b: __org.qemu_x-Base +object __org.qemu_x-Base + member __org.qemu_x-member1: __org.qemu_x-Enum optional=False +enum __org.qemu_x-Enum ['__org.qemu_x-value'] +object __org.qemu_x-Struct + base __org.qemu_x-Base + member __org.qemu_x-member2: str optional=False + member wchar-t: int optional=True +object __org.qemu_x-Struct2 + member array: __org.qemu_x-Union1List optional=False +object __org.qemu_x-Union1 + member type: __org.qemu_x-Union1Kind optional=False + case __org.qemu_x-branch: q_obj_str-wrapper +enum __org.qemu_x-Union1Kind ['__org.qemu_x-branch'] +object __org.qemu_x-Union2 + base __org.qemu_x-Base + tag __org.qemu_x-member1 + case __org.qemu_x-value: __org.qemu_x-Struct2 +command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1 + gen=True success_response=True +command guest-get-time q_obj_guest-get-time-arg -> int + gen=True success_response=True +command guest-sync q_obj_guest-sync-arg -> any + gen=True success_response=True +object q_empty +object q_obj_EVENT_C-arg + member a: int optional=True + member b: UserDefOne optional=True + member c: str optional=False +object q_obj_EVENT_D-arg + member a: EventStructOne optional=False + member b: str optional=False + member c: str optional=True + member enum3: EnumOne optional=True +object q_obj_UserDefFlatUnion2-base + member integer: int optional=True + member string: str optional=False + member enum1: QEnumTwo optional=False +object q_obj___org.qemu_x-command-arg + member a: __org.qemu_x-EnumList optional=False + member b: __org.qemu_x-StructList optional=False + member c: __org.qemu_x-Union2 optional=False + member d: __org.qemu_x-Alt optional=False +object q_obj_anyList-wrapper + member data: anyList optional=False +object q_obj_boolList-wrapper + member data: boolList optional=False +object q_obj_guest-get-time-arg + member a: int optional=False + member b: int optional=True +object q_obj_guest-sync-arg + member arg: any optional=False +object q_obj_int16List-wrapper + member data: int16List optional=False +object q_obj_int32List-wrapper + member data: int32List optional=False +object q_obj_int64List-wrapper + member data: int64List optional=False +object q_obj_int8List-wrapper + member data: int8List optional=False +object q_obj_intList-wrapper + member data: intList optional=False +object q_obj_numberList-wrapper + member data: numberList optional=False +object q_obj_sizeList-wrapper + member data: sizeList optional=False +object q_obj_str-wrapper + member data: str optional=False +object q_obj_strList-wrapper + member data: strList optional=False +object q_obj_uint16List-wrapper + member data: uint16List optional=False +object q_obj_uint32List-wrapper + member data: uint32List optional=False +object q_obj_uint64List-wrapper + member data: uint64List optional=False +object q_obj_uint8List-wrapper + member data: uint8List optional=False +object q_obj_user_def_cmd1-arg + member ud1a: UserDefOne optional=False +object q_obj_user_def_cmd2-arg + member ud1a: UserDefOne optional=False + member ud1b: UserDefOne optional=True +command user_def_cmd None -> None + gen=True success_response=True +command user_def_cmd0 Empty2 -> Empty2 + gen=True success_response=True +command user_def_cmd1 q_obj_user_def_cmd1-arg -> None + gen=True success_response=True +command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo + gen=True success_response=True diff --git a/qemu/tests/qapi-schema/reserved-command-q.err b/qemu/tests/qapi-schema/reserved-command-q.err new file mode 100644 index 000000000..f939e044e --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-command-q.err @@ -0,0 +1 @@ +tests/qapi-schema/reserved-command-q.json:5: 'command' uses invalid name 'q-unix' diff --git a/qemu/tests/qapi-schema/reserved-command-q.exit b/qemu/tests/qapi-schema/reserved-command-q.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-command-q.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/reserved-command-q.json b/qemu/tests/qapi-schema/reserved-command-q.json new file mode 100644 index 000000000..99f8aae31 --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-command-q.json @@ -0,0 +1,5 @@ +# C entity name collision +# We reject names like 'q-unix', because they can collide with the mangled +# name for 'unix' in generated C. +{ 'command': 'unix' } +{ 'command': 'q-unix' } diff --git a/qemu/tests/qapi-schema/reserved-command-q.out b/qemu/tests/qapi-schema/reserved-command-q.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/reserved-enum-q.err b/qemu/tests/qapi-schema/reserved-enum-q.err new file mode 100644 index 000000000..e1c3480ee --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-enum-q.err @@ -0,0 +1 @@ +tests/qapi-schema/reserved-enum-q.json:4: Member of enum 'Foo' uses invalid name 'q-Unix' diff --git a/qemu/tests/qapi-schema/reserved-enum-q.exit b/qemu/tests/qapi-schema/reserved-enum-q.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-enum-q.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/reserved-enum-q.json b/qemu/tests/qapi-schema/reserved-enum-q.json new file mode 100644 index 000000000..3593a765e --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-enum-q.json @@ -0,0 +1,4 @@ +# C entity name collision +# We reject names like 'q-unix', because they can collide with the mangled +# name for 'unix' in generated C. +{ 'enum': 'Foo', 'data': [ 'unix', 'q-Unix' ] } diff --git a/qemu/tests/qapi-schema/reserved-enum-q.out b/qemu/tests/qapi-schema/reserved-enum-q.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/reserved-member-has.err b/qemu/tests/qapi-schema/reserved-member-has.err new file mode 100644 index 000000000..e75577144 --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-member-has.err @@ -0,0 +1 @@ +tests/qapi-schema/reserved-member-has.json:5: Member of 'data' for command 'oops' uses reserved name 'has-a' diff --git a/qemu/tests/qapi-schema/reserved-member-has.exit b/qemu/tests/qapi-schema/reserved-member-has.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-member-has.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/reserved-member-has.json b/qemu/tests/qapi-schema/reserved-member-has.json new file mode 100644 index 000000000..45b9109bd --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-member-has.json @@ -0,0 +1,5 @@ +# C member name collision +# We reject names like 'has-a', because they can collide with the flag +# for an optional 'a' in generated C. +# TODO we could munge the optional flag name to avoid the collision. +{ 'command': 'oops', 'data': { '*a': 'str', 'has-a': 'str' } } diff --git a/qemu/tests/qapi-schema/reserved-member-has.out b/qemu/tests/qapi-schema/reserved-member-has.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/reserved-member-q.err b/qemu/tests/qapi-schema/reserved-member-q.err new file mode 100644 index 000000000..f3d5dd781 --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-member-q.err @@ -0,0 +1 @@ +tests/qapi-schema/reserved-member-q.json:4: Member of 'data' for struct 'Foo' uses invalid name 'q-unix' diff --git a/qemu/tests/qapi-schema/reserved-member-q.exit b/qemu/tests/qapi-schema/reserved-member-q.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-member-q.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/reserved-member-q.json b/qemu/tests/qapi-schema/reserved-member-q.json new file mode 100644 index 000000000..62fed8fdd --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-member-q.json @@ -0,0 +1,4 @@ +# C member name collision +# We reject names like 'q-unix', because they can collide with the mangled +# name for 'unix' in generated C. +{ 'struct': 'Foo', 'data': { 'unix':'int', 'q-unix':'bool' } } diff --git a/qemu/tests/qapi-schema/reserved-member-q.out b/qemu/tests/qapi-schema/reserved-member-q.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/reserved-member-u.err b/qemu/tests/qapi-schema/reserved-member-u.err new file mode 100644 index 000000000..87d42296c --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-member-u.err @@ -0,0 +1 @@ +tests/qapi-schema/reserved-member-u.json:7: Member of 'data' for struct 'Oops' uses reserved name 'u' diff --git a/qemu/tests/qapi-schema/reserved-member-u.exit b/qemu/tests/qapi-schema/reserved-member-u.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-member-u.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/reserved-member-u.json b/qemu/tests/qapi-schema/reserved-member-u.json new file mode 100644 index 000000000..1eaf0f301 --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-member-u.json @@ -0,0 +1,7 @@ +# Potential C member name collision +# We reject use of 'u' as a member name, to allow it for internal use in +# putting union branch members in a separate namespace from QMP members. +# This is true even for non-unions, because it is possible to convert a +# struct to flat union while remaining backwards compatible in QMP. +# TODO - we could munge the member name to 'q_u' to avoid the collision +{ 'struct': 'Oops', 'data': { 'u': 'str' } } diff --git a/qemu/tests/qapi-schema/reserved-member-u.out b/qemu/tests/qapi-schema/reserved-member-u.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/reserved-member-underscore.err b/qemu/tests/qapi-schema/reserved-member-underscore.err new file mode 100644 index 000000000..65ff0da8c --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-member-underscore.err @@ -0,0 +1 @@ +tests/qapi-schema/reserved-member-underscore.json:4: Member of 'data' for struct 'Oops' uses invalid name '_oops' diff --git a/qemu/tests/qapi-schema/reserved-member-underscore.exit b/qemu/tests/qapi-schema/reserved-member-underscore.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-member-underscore.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/reserved-member-underscore.json b/qemu/tests/qapi-schema/reserved-member-underscore.json new file mode 100644 index 000000000..4a3a01763 --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-member-underscore.json @@ -0,0 +1,4 @@ +# C member name collision +# We reject use of a single leading underscore in all names (names must +# begin with a letter or a downstream extension double-underscore prefix). +{ 'struct': 'Oops', 'data': { '_oops': 'str' } } diff --git a/qemu/tests/qapi-schema/reserved-member-underscore.out b/qemu/tests/qapi-schema/reserved-member-underscore.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/reserved-type-kind.err b/qemu/tests/qapi-schema/reserved-type-kind.err new file mode 100644 index 000000000..0a38efaad --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-type-kind.err @@ -0,0 +1 @@ +tests/qapi-schema/reserved-type-kind.json:2: enum 'UnionKind' should not end in 'Kind' diff --git a/qemu/tests/qapi-schema/reserved-type-kind.exit b/qemu/tests/qapi-schema/reserved-type-kind.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-type-kind.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/reserved-type-kind.json b/qemu/tests/qapi-schema/reserved-type-kind.json new file mode 100644 index 000000000..9ecaba12b --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-type-kind.json @@ -0,0 +1,2 @@ +# we reject types that would conflict with implicit union enum +{ 'enum': 'UnionKind', 'data': [ 'oops' ] } diff --git a/qemu/tests/qapi-schema/reserved-type-kind.out b/qemu/tests/qapi-schema/reserved-type-kind.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/reserved-type-list.err b/qemu/tests/qapi-schema/reserved-type-list.err new file mode 100644 index 000000000..4510fa6d9 --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-type-list.err @@ -0,0 +1 @@ +tests/qapi-schema/reserved-type-list.json:5: struct 'FooList' should not end in 'List' diff --git a/qemu/tests/qapi-schema/reserved-type-list.exit b/qemu/tests/qapi-schema/reserved-type-list.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-type-list.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/reserved-type-list.json b/qemu/tests/qapi-schema/reserved-type-list.json new file mode 100644 index 000000000..98d53bf80 --- /dev/null +++ b/qemu/tests/qapi-schema/reserved-type-list.json @@ -0,0 +1,5 @@ +# Potential C name collision +# We reserve names ending in 'List' for use by array types. +# TODO - we could choose array names to avoid collision with user types, +# in order to let this compile +{ 'struct': 'FooList', 'data': { 's': 'str' } } diff --git a/qemu/tests/qapi-schema/reserved-type-list.out b/qemu/tests/qapi-schema/reserved-type-list.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/returns-dict.err b/qemu/tests/qapi-schema/returns-dict.err new file mode 100644 index 000000000..eb2d0c466 --- /dev/null +++ b/qemu/tests/qapi-schema/returns-dict.err @@ -0,0 +1 @@ +tests/qapi-schema/returns-dict.json:2: 'returns' for command 'oops' should be a type name diff --git a/qemu/tests/qapi-schema/returns-dict.exit b/qemu/tests/qapi-schema/returns-dict.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/returns-dict.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/returns-dict.json b/qemu/tests/qapi-schema/returns-dict.json new file mode 100644 index 000000000..1cfef3ede --- /dev/null +++ b/qemu/tests/qapi-schema/returns-dict.json @@ -0,0 +1,2 @@ +# we reject inline struct return type +{ 'command': 'oops', 'returns': { 'a': 'str' } } diff --git a/qemu/tests/qapi-schema/returns-dict.out b/qemu/tests/qapi-schema/returns-dict.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/returns-int.exit b/qemu/tests/qapi-schema/returns-int.exit deleted file mode 100644 index 573541ac9..000000000 --- a/qemu/tests/qapi-schema/returns-int.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/qemu/tests/qapi-schema/returns-int.json b/qemu/tests/qapi-schema/returns-int.json deleted file mode 100644 index 870ec6366..000000000 --- a/qemu/tests/qapi-schema/returns-int.json +++ /dev/null @@ -1,3 +0,0 @@ -# It is okay (although not extensible) to return a non-dictionary -# But to make it work, the name must be in a whitelist -{ 'command': 'guest-get-time', 'returns': 'int' } diff --git a/qemu/tests/qapi-schema/returns-int.out b/qemu/tests/qapi-schema/returns-int.out deleted file mode 100644 index 70b3ac5e6..000000000 --- a/qemu/tests/qapi-schema/returns-int.out +++ /dev/null @@ -1,3 +0,0 @@ -[OrderedDict([('command', 'guest-get-time'), ('returns', 'int')])] -[] -[] diff --git a/qemu/tests/qapi-schema/returns-whitelist.err b/qemu/tests/qapi-schema/returns-whitelist.err index a41f019a5..f47c1ee7c 100644 --- a/qemu/tests/qapi-schema/returns-whitelist.err +++ b/qemu/tests/qapi-schema/returns-whitelist.err @@ -1 +1 @@ -tests/qapi-schema/returns-whitelist.json:10: 'returns' for command 'no-way-this-will-get-whitelisted' cannot use built-in type 'array of int' +tests/qapi-schema/returns-whitelist.json:10: 'returns' for command 'no-way-this-will-get-whitelisted' cannot use built-in type 'int' diff --git a/qemu/tests/qapi-schema/struct-base-clash-deep.err b/qemu/tests/qapi-schema/struct-base-clash-deep.err index e3e9f8d28..e2d7943f2 100644 --- a/qemu/tests/qapi-schema/struct-base-clash-deep.err +++ b/qemu/tests/qapi-schema/struct-base-clash-deep.err @@ -1 +1 @@ -tests/qapi-schema/struct-base-clash-deep.json:7: Member name 'name' clashes with base 'Base' +tests/qapi-schema/struct-base-clash-deep.json:10: 'name' (member of Sub) collides with 'name' (member of Base) diff --git a/qemu/tests/qapi-schema/struct-base-clash-deep.json b/qemu/tests/qapi-schema/struct-base-clash-deep.json index 552fe9431..fa873ab5d 100644 --- a/qemu/tests/qapi-schema/struct-base-clash-deep.json +++ b/qemu/tests/qapi-schema/struct-base-clash-deep.json @@ -1,4 +1,7 @@ -# we check for no duplicate keys with indirect base +# Reject attempts to duplicate QMP members +# Here, 'name' would have to appear twice on the wire, locally and +# indirectly for the grandparent base; the collision doesn't care that +# one instance is optional. { 'struct': 'Base', 'data': { 'name': 'str' } } { 'struct': 'Mid', diff --git a/qemu/tests/qapi-schema/struct-base-clash.err b/qemu/tests/qapi-schema/struct-base-clash.err index 3ac37fb26..c52f33d27 100644 --- a/qemu/tests/qapi-schema/struct-base-clash.err +++ b/qemu/tests/qapi-schema/struct-base-clash.err @@ -1 +1 @@ -tests/qapi-schema/struct-base-clash.json:4: Member name 'name' clashes with base 'Base' +tests/qapi-schema/struct-base-clash.json:5: 'name' (member of Sub) collides with 'name' (member of Base) diff --git a/qemu/tests/qapi-schema/struct-base-clash.json b/qemu/tests/qapi-schema/struct-base-clash.json index f2afc9b6f..11aec80fe 100644 --- a/qemu/tests/qapi-schema/struct-base-clash.json +++ b/qemu/tests/qapi-schema/struct-base-clash.json @@ -1,4 +1,5 @@ -# we check for no duplicate keys with base +# Reject attempts to duplicate QMP members +# Here, 'name' would have to appear twice on the wire, locally and for base. { 'struct': 'Base', 'data': { 'name': 'str' } } { 'struct': 'Sub', diff --git a/qemu/tests/qapi-schema/struct-data-invalid.err b/qemu/tests/qapi-schema/struct-data-invalid.err new file mode 100644 index 000000000..6644f4c2a --- /dev/null +++ b/qemu/tests/qapi-schema/struct-data-invalid.err @@ -0,0 +1 @@ +tests/qapi-schema/struct-data-invalid.json:1: 'data' for struct 'foo' should be a dictionary or type name diff --git a/qemu/tests/qapi-schema/struct-data-invalid.exit b/qemu/tests/qapi-schema/struct-data-invalid.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/struct-data-invalid.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/struct-data-invalid.json b/qemu/tests/qapi-schema/struct-data-invalid.json new file mode 100644 index 000000000..9adbc3bb6 --- /dev/null +++ b/qemu/tests/qapi-schema/struct-data-invalid.json @@ -0,0 +1,2 @@ +{ 'struct': 'foo', + 'data': false } diff --git a/qemu/tests/qapi-schema/struct-data-invalid.out b/qemu/tests/qapi-schema/struct-data-invalid.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/struct-member-invalid.err b/qemu/tests/qapi-schema/struct-member-invalid.err new file mode 100644 index 000000000..69a326d45 --- /dev/null +++ b/qemu/tests/qapi-schema/struct-member-invalid.err @@ -0,0 +1 @@ +tests/qapi-schema/struct-member-invalid.json:1: Member 'a' of 'data' for struct 'foo' should be a type name diff --git a/qemu/tests/qapi-schema/struct-member-invalid.exit b/qemu/tests/qapi-schema/struct-member-invalid.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/struct-member-invalid.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/struct-member-invalid.json b/qemu/tests/qapi-schema/struct-member-invalid.json new file mode 100644 index 000000000..8f172f7a8 --- /dev/null +++ b/qemu/tests/qapi-schema/struct-member-invalid.json @@ -0,0 +1,2 @@ +{ 'struct': 'foo', + 'data': { 'a': false } } diff --git a/qemu/tests/qapi-schema/struct-member-invalid.out b/qemu/tests/qapi-schema/struct-member-invalid.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/test-qapi.py b/qemu/tests/qapi-schema/test-qapi.py index 634ef2d00..649677e01 100644 --- a/qemu/tests/qapi-schema/test-qapi.py +++ b/qemu/tests/qapi-schema/test-qapi.py @@ -15,11 +15,42 @@ from pprint import pprint import os import sys -try: - exprs = parse_schema(sys.argv[1]) -except SystemExit: - raise - -pprint(exprs) -pprint(enum_types) -pprint(struct_types) + +class QAPISchemaTestVisitor(QAPISchemaVisitor): + def visit_enum_type(self, name, info, values, prefix): + print 'enum %s %s' % (name, values) + if prefix: + print ' prefix %s' % prefix + + def visit_object_type(self, name, info, base, members, variants): + print 'object %s' % name + if base: + print ' base %s' % base.name + for m in members: + print ' member %s: %s optional=%s' % \ + (m.name, m.type.name, m.optional) + self._print_variants(variants) + + def visit_alternate_type(self, name, info, variants): + print 'alternate %s' % name + self._print_variants(variants) + + def visit_command(self, name, info, arg_type, ret_type, + gen, success_response): + print 'command %s %s -> %s' % \ + (name, arg_type and arg_type.name, ret_type and ret_type.name) + print ' gen=%s success_response=%s' % (gen, success_response) + + def visit_event(self, name, info, arg_type): + print 'event %s %s' % (name, arg_type and arg_type.name) + + @staticmethod + def _print_variants(variants): + if variants: + if variants.tag_name: + print ' tag %s' % variants.tag_name + for v in variants.variants: + print ' case %s: %s' % (v.name, v.type.name) + +schema = QAPISchema(sys.argv[1]) +schema.visit(QAPISchemaTestVisitor()) diff --git a/qemu/tests/qapi-schema/type-bypass-no-gen.err b/qemu/tests/qapi-schema/type-bypass-no-gen.err deleted file mode 100644 index 20cef0a8a..000000000 --- a/qemu/tests/qapi-schema/type-bypass-no-gen.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/type-bypass-no-gen.json:2: Member 'arg' of 'data' for command 'unsafe' uses '**' but did not request 'gen':false diff --git a/qemu/tests/qapi-schema/type-bypass-no-gen.json b/qemu/tests/qapi-schema/type-bypass-no-gen.json deleted file mode 100644 index 4feae3719..000000000 --- a/qemu/tests/qapi-schema/type-bypass-no-gen.json +++ /dev/null @@ -1,2 +0,0 @@ -# type bypass only works with 'gen':false -{ 'command': 'unsafe', 'data': { 'arg': '**' }, 'returns': '**' } diff --git a/qemu/tests/qapi-schema/type-bypass.exit b/qemu/tests/qapi-schema/type-bypass.exit deleted file mode 100644 index 573541ac9..000000000 --- a/qemu/tests/qapi-schema/type-bypass.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/qemu/tests/qapi-schema/type-bypass.json b/qemu/tests/qapi-schema/type-bypass.json deleted file mode 100644 index 48b213783..000000000 --- a/qemu/tests/qapi-schema/type-bypass.json +++ /dev/null @@ -1,2 +0,0 @@ -# Use of 'gen':false allows bypassing type system -{ 'command': 'unsafe', 'data': { 'arg': '**' }, 'returns': '**', 'gen': false } diff --git a/qemu/tests/qapi-schema/type-bypass.out b/qemu/tests/qapi-schema/type-bypass.out deleted file mode 100644 index eaf20f834..000000000 --- a/qemu/tests/qapi-schema/type-bypass.out +++ /dev/null @@ -1,3 +0,0 @@ -[OrderedDict([('command', 'unsafe'), ('data', OrderedDict([('arg', '**')])), ('returns', '**'), ('gen', False)])] -[] -[] diff --git a/qemu/tests/qapi-schema/union-bad-branch.err b/qemu/tests/qapi-schema/union-bad-branch.err deleted file mode 100644 index 882273556..000000000 --- a/qemu/tests/qapi-schema/union-bad-branch.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/union-bad-branch.json:6: Union 'MyUnion' member 'ONE' clashes with 'one' diff --git a/qemu/tests/qapi-schema/union-bad-branch.json b/qemu/tests/qapi-schema/union-bad-branch.json deleted file mode 100644 index 913aa38bc..000000000 --- a/qemu/tests/qapi-schema/union-bad-branch.json +++ /dev/null @@ -1,8 +0,0 @@ -# we reject normal unions where branches would collide in C -{ 'struct': 'One', - 'data': { 'string': 'str' } } -{ 'struct': 'Two', - 'data': { 'number': 'int' } } -{ 'union': 'MyUnion', - 'data': { 'one': 'One', - 'ONE': 'Two' } } diff --git a/qemu/tests/qapi-schema/union-base-no-discriminator.err b/qemu/tests/qapi-schema/union-base-no-discriminator.err index fc8b79c45..8b7a24260 100644 --- a/qemu/tests/qapi-schema/union-base-no-discriminator.err +++ b/qemu/tests/qapi-schema/union-base-no-discriminator.err @@ -1 +1 @@ -tests/qapi-schema/union-base-no-discriminator.json:11: Union 'TestUnion' requires a discriminator to go along with base +tests/qapi-schema/union-base-no-discriminator.json:11: Simple union 'TestUnion' must not have a base diff --git a/qemu/tests/qapi-schema/union-branch-case.err b/qemu/tests/qapi-schema/union-branch-case.err new file mode 100644 index 000000000..11521901d --- /dev/null +++ b/qemu/tests/qapi-schema/union-branch-case.err @@ -0,0 +1 @@ +tests/qapi-schema/union-branch-case.json:2: 'Branch' (branch of NoWayThisWillGetWhitelisted) should not use uppercase diff --git a/qemu/tests/qapi-schema/union-branch-case.exit b/qemu/tests/qapi-schema/union-branch-case.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/union-branch-case.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/union-branch-case.json b/qemu/tests/qapi-schema/union-branch-case.json new file mode 100644 index 000000000..e6565dc3b --- /dev/null +++ b/qemu/tests/qapi-schema/union-branch-case.json @@ -0,0 +1,2 @@ +# Branch names should be 'lower-case' unless the union is whitelisted +{ 'union': 'NoWayThisWillGetWhitelisted', 'data': { 'Branch': 'int' } } diff --git a/qemu/tests/qapi-schema/union-branch-case.out b/qemu/tests/qapi-schema/union-branch-case.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/union-clash-branches.err b/qemu/tests/qapi-schema/union-clash-branches.err new file mode 100644 index 000000000..e5b21135b --- /dev/null +++ b/qemu/tests/qapi-schema/union-clash-branches.err @@ -0,0 +1 @@ +tests/qapi-schema/union-clash-branches.json:4: 'a_b' (branch of TestUnion) collides with 'a-b' (branch of TestUnion) diff --git a/qemu/tests/qapi-schema/union-clash-branches.exit b/qemu/tests/qapi-schema/union-clash-branches.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/union-clash-branches.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/union-clash-branches.json b/qemu/tests/qapi-schema/union-clash-branches.json new file mode 100644 index 000000000..3bece8c94 --- /dev/null +++ b/qemu/tests/qapi-schema/union-clash-branches.json @@ -0,0 +1,5 @@ +# Union branch name collision +# Reject a union that would result in a collision in generated C names (this +# would try to generate two members 'a_b'). +{ 'union': 'TestUnion', + 'data': { 'a-b': 'int', 'a_b': 'str' } } diff --git a/qemu/tests/qapi-schema/union-clash-branches.out b/qemu/tests/qapi-schema/union-clash-branches.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/union-empty.err b/qemu/tests/qapi-schema/union-empty.err new file mode 100644 index 000000000..12c20221b --- /dev/null +++ b/qemu/tests/qapi-schema/union-empty.err @@ -0,0 +1 @@ +tests/qapi-schema/union-empty.json:2: Union 'Union' cannot have empty 'data' diff --git a/qemu/tests/qapi-schema/union-empty.exit b/qemu/tests/qapi-schema/union-empty.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/qemu/tests/qapi-schema/union-empty.exit @@ -0,0 +1 @@ +1 diff --git a/qemu/tests/qapi-schema/union-empty.json b/qemu/tests/qapi-schema/union-empty.json new file mode 100644 index 000000000..1f0b13ca2 --- /dev/null +++ b/qemu/tests/qapi-schema/union-empty.json @@ -0,0 +1,2 @@ +# unions cannot be empty +{ 'union': 'Union', 'data': { } } diff --git a/qemu/tests/qapi-schema/union-empty.out b/qemu/tests/qapi-schema/union-empty.out new file mode 100644 index 000000000..e69de29bb diff --git a/qemu/tests/qapi-schema/union-invalid-base.err b/qemu/tests/qapi-schema/union-invalid-base.err index 9f637963e..03d7b97a9 100644 --- a/qemu/tests/qapi-schema/union-invalid-base.err +++ b/qemu/tests/qapi-schema/union-invalid-base.err @@ -1 +1 @@ -tests/qapi-schema/union-invalid-base.json:8: Base 'int' is not a valid struct +tests/qapi-schema/union-invalid-base.json:8: 'base' for union 'TestUnion' cannot use built-in type 'int' diff --git a/qemu/tests/qapi-schema/union-max.err b/qemu/tests/qapi-schema/union-max.err deleted file mode 100644 index 55ce4399d..000000000 --- a/qemu/tests/qapi-schema/union-max.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/union-max.json:2: Union 'Union' member 'max' clashes with '(automatic)' diff --git a/qemu/tests/qapi-schema/union-max.json b/qemu/tests/qapi-schema/union-max.json deleted file mode 100644 index d6ad98699..000000000 --- a/qemu/tests/qapi-schema/union-max.json +++ /dev/null @@ -1,3 +0,0 @@ -# we reject 'max' branch in a union, for collision with C enum -{ 'union': 'Union', - 'data': { 'max': 'int' } } diff --git a/qemu/tests/qemu-iotests/001 b/qemu/tests/qemu-iotests/001 index 4e1646941..ffd14e2ce 100755 --- a/qemu/tests/qemu-iotests/001 +++ b/qemu/tests/qemu-iotests/001 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/002 b/qemu/tests/qemu-iotests/002 index 6a865aac7..d4f8e91b9 100755 --- a/qemu/tests/qemu-iotests/002 +++ b/qemu/tests/qemu-iotests/002 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/003 b/qemu/tests/qemu-iotests/003 index 98638d4ce..19889b9fc 100755 --- a/qemu/tests/qemu-iotests/003 +++ b/qemu/tests/qemu-iotests/003 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/004 b/qemu/tests/qemu-iotests/004 index 2ad77ed51..67e1beb20 100755 --- a/qemu/tests/qemu-iotests/004 +++ b/qemu/tests/qemu-iotests/004 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/005 b/qemu/tests/qemu-iotests/005 index ba1236dfb..444737751 100755 --- a/qemu/tests/qemu-iotests/005 +++ b/qemu/tests/qemu-iotests/005 @@ -28,7 +28,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/007 b/qemu/tests/qemu-iotests/007 index 7b5aff59b..fa543eeb7 100755 --- a/qemu/tests/qemu-iotests/007 +++ b/qemu/tests/qemu-iotests/007 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/008 b/qemu/tests/qemu-iotests/008 index 2d28efd42..8e89d74fe 100755 --- a/qemu/tests/qemu-iotests/008 +++ b/qemu/tests/qemu-iotests/008 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/009 b/qemu/tests/qemu-iotests/009 index 57a43f5a1..16e4475ca 100755 --- a/qemu/tests/qemu-iotests/009 +++ b/qemu/tests/qemu-iotests/009 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/010 b/qemu/tests/qemu-iotests/010 index 896a0058f..151dac238 100755 --- a/qemu/tests/qemu-iotests/010 +++ b/qemu/tests/qemu-iotests/010 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/011 b/qemu/tests/qemu-iotests/011 index 1c5158af4..f8d044ec8 100755 --- a/qemu/tests/qemu-iotests/011 +++ b/qemu/tests/qemu-iotests/011 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/012 b/qemu/tests/qemu-iotests/012 index 7c5b6892d..d1d3f2209 100755 --- a/qemu/tests/qemu-iotests/012 +++ b/qemu/tests/qemu-iotests/012 @@ -27,7 +27,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/013 b/qemu/tests/qemu-iotests/013 index ea3cab91d..d013f87da 100755 --- a/qemu/tests/qemu-iotests/013 +++ b/qemu/tests/qemu-iotests/013 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/014 b/qemu/tests/qemu-iotests/014 index b23c2db9b..2ea79e8c8 100755 --- a/qemu/tests/qemu-iotests/014 +++ b/qemu/tests/qemu-iotests/014 @@ -27,7 +27,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/015 b/qemu/tests/qemu-iotests/015 index 6f2609524..aaf9c3f41 100755 --- a/qemu/tests/qemu-iotests/015 +++ b/qemu/tests/qemu-iotests/015 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/017 b/qemu/tests/qemu-iotests/017 index 3af3cdfbc..e3f9e7596 100755 --- a/qemu/tests/qemu-iotests/017 +++ b/qemu/tests/qemu-iotests/017 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/017.out b/qemu/tests/qemu-iotests/017.out index 7c409fc5b..8fc924194 100644 --- a/qemu/tests/qemu-iotests/017.out +++ b/qemu/tests/qemu-iotests/017.out @@ -269,7 +269,7 @@ wrote 65536/65536 bytes at offset 4295032832 No errors were found on the image. Creating test image with backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base Filling test image === IO: pattern 1 diff --git a/qemu/tests/qemu-iotests/018 b/qemu/tests/qemu-iotests/018 index d8a7d435a..1d39d35c4 100755 --- a/qemu/tests/qemu-iotests/018 +++ b/qemu/tests/qemu-iotests/018 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -66,8 +65,8 @@ _check_test_img echo "Creating test image with backing file" echo -TEST_IMG=$TEST_IMG_SAVE -_make_test_img -b "$TEST_IMG.base" 6G +TEST_IMG="$TEST_IMG_SAVE.orig" +_make_test_img -b "$TEST_IMG_SAVE.base" 6G echo "Filling test image" echo @@ -81,8 +80,8 @@ for offset in $TEST_OFFSETS; do done _check_test_img -mv "$TEST_IMG" "$TEST_IMG.orig" -$QEMU_IMG convert -O $IMGFMT "$TEST_IMG.orig" "$TEST_IMG" +TEST_IMG="$TEST_IMG_SAVE" +$QEMU_IMG convert -f $IMGFMT -O $IMGFMT "$TEST_IMG.orig" "$TEST_IMG" echo "Reading" echo diff --git a/qemu/tests/qemu-iotests/018.out b/qemu/tests/qemu-iotests/018.out index 39a6011d2..5df966727 100644 --- a/qemu/tests/qemu-iotests/018.out +++ b/qemu/tests/qemu-iotests/018.out @@ -269,7 +269,7 @@ wrote 65536/65536 bytes at offset 4295032832 No errors were found on the image. Creating test image with backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base Filling test image === IO: pattern 1 diff --git a/qemu/tests/qemu-iotests/019 b/qemu/tests/qemu-iotests/019 index f5ecbf545..24a789a25 100755 --- a/qemu/tests/qemu-iotests/019 +++ b/qemu/tests/qemu-iotests/019 @@ -27,7 +27,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -54,6 +53,9 @@ _unsupported_imgopts "subformat=monolithicFlat" \ TEST_OFFSETS="0 4294967296" CLUSTER_SIZE=65536 +TEST_IMG_SAVE="$TEST_IMG" +TEST_IMG="$TEST_IMG.base" + _make_test_img 6G echo "Filling base image" @@ -71,8 +73,8 @@ _check_test_img echo "Creating test image with backing file" echo -mv "$TEST_IMG" "$TEST_IMG.base" -_make_test_img -b "$TEST_IMG.base" 6G +TEST_IMG="$TEST_IMG_SAVE.orig" +_make_test_img -b "$TEST_IMG_SAVE.base" 6G echo "Filling test image" echo @@ -86,9 +88,7 @@ for offset in $TEST_OFFSETS; do done _check_test_img -mv "$TEST_IMG" "$TEST_IMG.orig" - - +TEST_IMG="$TEST_IMG_SAVE" # Test the conversion twice: One test with the old-style -B option and another # one with -o backing_file @@ -98,7 +98,7 @@ for backing_option in "-B " "-o backing_file="; do echo echo Testing conversion with $backing_option"$TEST_IMG.base" | _filter_testdir | _filter_imgfmt echo - $QEMU_IMG convert -O $IMGFMT $backing_option"$TEST_IMG.base" "$TEST_IMG.orig" "$TEST_IMG" + $QEMU_IMG convert -f $IMGFMT -O $IMGFMT $backing_option"$TEST_IMG.base" "$TEST_IMG.orig" "$TEST_IMG" echo "Checking if backing clusters are allocated when they shouldn't" echo diff --git a/qemu/tests/qemu-iotests/019.out b/qemu/tests/qemu-iotests/019.out index 4695b972d..012426497 100644 --- a/qemu/tests/qemu-iotests/019.out +++ b/qemu/tests/qemu-iotests/019.out @@ -1,5 +1,5 @@ QA output created by 019 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=6442450944 Filling base image === IO: pattern 42 @@ -269,7 +269,7 @@ wrote 65536/65536 bytes at offset 4296015872 No errors were found on the image. Creating test image with backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base Filling test image === IO: pattern 43 diff --git a/qemu/tests/qemu-iotests/020 b/qemu/tests/qemu-iotests/020 index 2f258dc6e..9c4a68c97 100755 --- a/qemu/tests/qemu-iotests/020 +++ b/qemu/tests/qemu-iotests/020 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -56,6 +55,9 @@ fi TEST_OFFSETS="0 4294967296" +TEST_IMG_SAVE="$TEST_IMG" +TEST_IMG="$TEST_IMG.base" + _make_test_img 6G echo "Filling base image" @@ -73,7 +75,7 @@ _check_test_img echo "Creating test image with backing file" echo -mv "$TEST_IMG" "$TEST_IMG.base" +TEST_IMG="$TEST_IMG_SAVE" _make_test_img -b "$TEST_IMG.base" 6G echo "Filling test image" @@ -89,7 +91,7 @@ done _check_test_img $QEMU_IMG commit "$TEST_IMG" -mv "$TEST_IMG.base" "$TEST_IMG" +TEST_IMG="$TEST_IMG.base" echo "Reading from the backing file" echo diff --git a/qemu/tests/qemu-iotests/020.out b/qemu/tests/qemu-iotests/020.out index 71aab1c74..42f6c1b15 100644 --- a/qemu/tests/qemu-iotests/020.out +++ b/qemu/tests/qemu-iotests/020.out @@ -1,5 +1,5 @@ QA output created by 020 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=6442450944 Filling base image === IO: pattern 0 @@ -269,7 +269,7 @@ wrote 65536/65536 bytes at offset 4295032832 No errors were found on the image. Creating test image with backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base Filling test image === IO: pattern 1 diff --git a/qemu/tests/qemu-iotests/021 b/qemu/tests/qemu-iotests/021 index 1c69024cc..11e8ed718 100755 --- a/qemu/tests/qemu-iotests/021 +++ b/qemu/tests/qemu-iotests/021 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/022 b/qemu/tests/qemu-iotests/022 index d35b8162b..2452a9f86 100755 --- a/qemu/tests/qemu-iotests/022 +++ b/qemu/tests/qemu-iotests/022 @@ -27,7 +27,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/023 b/qemu/tests/qemu-iotests/023 index 9ad06b990..497ae1ed1 100755 --- a/qemu/tests/qemu-iotests/023 +++ b/qemu/tests/qemu-iotests/023 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/024 b/qemu/tests/qemu-iotests/024 index 9bf99e198..e0d77ce2f 100755 --- a/qemu/tests/qemu-iotests/024 +++ b/qemu/tests/qemu-iotests/024 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -60,16 +59,22 @@ CLUSTER_SIZE=65536 echo "Creating backing file" echo +TEST_IMG_SAVE="$TEST_IMG" +TEST_IMG="$TEST_IMG.base_old" + _make_test_img 1G io_pattern writev 0 $CLUSTER_SIZE $((2 * CLUSTER_SIZE)) 8 0x11 -mv "$TEST_IMG" "$TEST_IMG.base_old" + +TEST_IMG="$TEST_IMG_SAVE.base_new" echo "Creating new backing file" echo _make_test_img 1G io_pattern writev 0 $((2 * CLUSTER_SIZE)) $((4 * CLUSTER_SIZE)) 4 0x22 -mv "$TEST_IMG" "$TEST_IMG.base_new" + + +TEST_IMG="$TEST_IMG_SAVE" echo "Creating COW image" echo diff --git a/qemu/tests/qemu-iotests/024.out b/qemu/tests/qemu-iotests/024.out index 521d46942..33cfaf5cf 100644 --- a/qemu/tests/qemu-iotests/024.out +++ b/qemu/tests/qemu-iotests/024.out @@ -1,7 +1,7 @@ QA output created by 024 Creating backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT.base_old', fmt=IMGFMT size=1073741824 === IO: pattern 0x11 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -21,7 +21,7 @@ wrote 65536/65536 bytes at offset 917504 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Creating new backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT.base_new', fmt=IMGFMT size=1073741824 === IO: pattern 0x22 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -33,7 +33,7 @@ wrote 131072/131072 bytes at offset 786432 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Creating COW image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 backing_file='TEST_DIR/t.IMGFMT.base_old' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 backing_file=TEST_DIR/t.IMGFMT.base_old === IO: pattern 0x33 wrote 262144/262144 bytes at offset 0 256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/qemu/tests/qemu-iotests/025 b/qemu/tests/qemu-iotests/025 index 467a4b709..c41370f3b 100755 --- a/qemu/tests/qemu-iotests/025 +++ b/qemu/tests/qemu-iotests/025 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/026 b/qemu/tests/qemu-iotests/026 index 0fc3244c7..f5a7f02b2 100755 --- a/qemu/tests/qemu-iotests/026 +++ b/qemu/tests/qemu-iotests/026 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -66,7 +65,7 @@ for event in \ \ l2_load \ l2_update \ - l2_alloc.write \ + l2_alloc_write \ \ write_aio \ \ @@ -126,11 +125,11 @@ CLUSTER_SIZE=512 for event in \ - refblock_alloc.hookup \ - refblock_alloc.write \ - refblock_alloc.write_blocks \ - refblock_alloc.write_table \ - refblock_alloc.switch_table \ + refblock_alloc_hookup \ + refblock_alloc_write \ + refblock_alloc_write_blocks \ + refblock_alloc_write_table \ + refblock_alloc_switch_table \ do @@ -170,9 +169,9 @@ CLUSTER_SIZE=1024 for event in \ - l1_grow.alloc_table \ - l1_grow.write_table \ - l1_grow.activate_table \ + l1_grow_alloc_table \ + l1_grow_write_table \ + l1_grow_activate_table \ do diff --git a/qemu/tests/qemu-iotests/026.out b/qemu/tests/qemu-iotests/026.out index 5e964fb5a..d84d82c11 100644 --- a/qemu/tests/qemu-iotests/026.out +++ b/qemu/tests/qemu-iotests/026.out @@ -195,24 +195,24 @@ write failed: No space left on device This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 5; imm: off; once: on; write +Event: l2_alloc_write; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 5; imm: off; once: on; write -b +Event: l2_alloc_write; errno: 5; imm: off; once: on; write -b write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 5; imm: off; once: off; write +Event: l2_alloc_write; errno: 5; imm: off; once: off; write Failed to flush the L2 table cache: Input/output error Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 5; imm: off; once: off; write -b +Event: l2_alloc_write; errno: 5; imm: off; once: off; write -b Failed to flush the L2 table cache: Input/output error Failed to flush the refcount block cache: Input/output error write failed: Input/output error @@ -221,24 +221,24 @@ write failed: Input/output error This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 28; imm: off; once: on; write +Event: l2_alloc_write; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 28; imm: off; once: on; write -b +Event: l2_alloc_write; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 28; imm: off; once: off; write +Event: l2_alloc_write; errno: 28; imm: off; once: off; write Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 28; imm: off; once: off; write -b +Event: l2_alloc_write; errno: 28; imm: off; once: off; write -b Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device @@ -490,17 +490,17 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.hookup; errno: 28; imm: off; once: on; write +Event: refblock_alloc_hookup; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.hookup; errno: 28; imm: off; once: on; write -b +Event: refblock_alloc_hookup; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.hookup; errno: 28; imm: off; once: off; write +Event: refblock_alloc_hookup; errno: 28; imm: off; once: off; write Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device @@ -509,7 +509,7 @@ write failed: No space left on device This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.hookup; errno: 28; imm: off; once: off; write -b +Event: refblock_alloc_hookup; errno: 28; imm: off; once: off; write -b Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device @@ -518,41 +518,41 @@ write failed: No space left on device This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write; errno: 28; imm: off; once: on; write +Event: refblock_alloc_write; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write; errno: 28; imm: off; once: on; write -b +Event: refblock_alloc_write; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write; errno: 28; imm: off; once: off; write +Event: refblock_alloc_write; errno: 28; imm: off; once: off; write Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write; errno: 28; imm: off; once: off; write -b +Event: refblock_alloc_write; errno: 28; imm: off; once: off; write -b Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: on; write +Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: on; write -b +Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: off; write +Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: off; write Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device @@ -561,7 +561,7 @@ write failed: No space left on device This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: off; write -b +Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: off; write -b Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device @@ -570,17 +570,17 @@ write failed: No space left on device This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_table; errno: 28; imm: off; once: on; write +Event: refblock_alloc_write_table; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_table; errno: 28; imm: off; once: on; write -b +Event: refblock_alloc_write_table; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_table; errno: 28; imm: off; once: off; write +Event: refblock_alloc_write_table; errno: 28; imm: off; once: off; write Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device @@ -589,7 +589,7 @@ write failed: No space left on device This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_table; errno: 28; imm: off; once: off; write -b +Event: refblock_alloc_write_table; errno: 28; imm: off; once: off; write -b Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device @@ -598,17 +598,17 @@ write failed: No space left on device This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.switch_table; errno: 28; imm: off; once: on; write +Event: refblock_alloc_switch_table; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.switch_table; errno: 28; imm: off; once: on; write -b +Event: refblock_alloc_switch_table; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.switch_table; errno: 28; imm: off; once: off; write +Event: refblock_alloc_switch_table; errno: 28; imm: off; once: off; write Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device @@ -617,7 +617,7 @@ write failed: No space left on device This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.switch_table; errno: 28; imm: off; once: off; write -b +Event: refblock_alloc_switch_table; errno: 28; imm: off; once: off; write -b Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device @@ -629,60 +629,60 @@ This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.alloc_table; errno: 5; imm: off; once: on +Event: l1_grow_alloc_table; errno: 5; imm: off; once: on write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.alloc_table; errno: 5; imm: off; once: off +Event: l1_grow_alloc_table; errno: 5; imm: off; once: off Failed to flush the L2 table cache: Input/output error Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.alloc_table; errno: 28; imm: off; once: on +Event: l1_grow_alloc_table; errno: 28; imm: off; once: on write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.alloc_table; errno: 28; imm: off; once: off +Event: l1_grow_alloc_table; errno: 28; imm: off; once: off Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.write_table; errno: 5; imm: off; once: on +Event: l1_grow_write_table; errno: 5; imm: off; once: on write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.write_table; errno: 5; imm: off; once: off +Event: l1_grow_write_table; errno: 5; imm: off; once: off Failed to flush the L2 table cache: Input/output error Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.write_table; errno: 28; imm: off; once: on +Event: l1_grow_write_table; errno: 28; imm: off; once: on write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.write_table; errno: 28; imm: off; once: off +Event: l1_grow_write_table; errno: 28; imm: off; once: off Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.activate_table; errno: 5; imm: off; once: on +Event: l1_grow_activate_table; errno: 5; imm: off; once: on write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.activate_table; errno: 5; imm: off; once: off +Event: l1_grow_activate_table; errno: 5; imm: off; once: off Failed to flush the L2 table cache: Input/output error Failed to flush the refcount block cache: Input/output error write failed: Input/output error @@ -691,12 +691,12 @@ write failed: Input/output error This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.activate_table; errno: 28; imm: off; once: on +Event: l1_grow_activate_table; errno: 28; imm: off; once: on write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.activate_table; errno: 28; imm: off; once: off +Event: l1_grow_activate_table; errno: 28; imm: off; once: off Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device diff --git a/qemu/tests/qemu-iotests/026.out.nocache b/qemu/tests/qemu-iotests/026.out.nocache index c9d242e9e..9c2c8a948 100644 --- a/qemu/tests/qemu-iotests/026.out.nocache +++ b/qemu/tests/qemu-iotests/026.out.nocache @@ -14,6 +14,8 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 5; imm: off; once: off; write +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error 1 leaked clusters were found on the image. @@ -21,6 +23,8 @@ This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 5; imm: off; once: off; write -b +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error 1 leaked clusters were found on the image. @@ -38,6 +42,8 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 1 leaked clusters were found on the image. @@ -45,6 +51,8 @@ This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 1 leaked clusters were found on the image. @@ -70,7 +78,11 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 5; imm: off; once: off; write wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error read failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -78,7 +90,11 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 5; imm: off; once: off; write -b wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error read failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -102,7 +118,11 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 28; imm: off; once: off; write wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device read failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -110,7 +130,11 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 28; imm: off; once: off; write -b wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device read failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -118,20 +142,18 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: on; write wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: on; write -b wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: off; write +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -140,6 +162,8 @@ This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: off; write -b +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -150,20 +174,18 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: on; write wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: on; write -b wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -172,6 +194,8 @@ This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -179,44 +203,52 @@ wrote 131072/131072 bytes at offset 0 This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 5; imm: off; once: on; write +Event: l2_alloc_write; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 5; imm: off; once: on; write -b +Event: l2_alloc_write; errno: 5; imm: off; once: on; write -b write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 5; imm: off; once: off; write +Event: l2_alloc_write; errno: 5; imm: off; once: off; write +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 5; imm: off; once: off; write -b +Event: l2_alloc_write; errno: 5; imm: off; once: off; write -b +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 28; imm: off; once: on; write +Event: l2_alloc_write; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 28; imm: off; once: on; write -b +Event: l2_alloc_write; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 28; imm: off; once: off; write +Event: l2_alloc_write; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 28; imm: off; once: off; write -b +Event: l2_alloc_write; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 1 leaked clusters were found on the image. @@ -234,11 +266,15 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 5; imm: off; once: off; write +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 5; imm: off; once: off; write -b +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -254,11 +290,15 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -274,11 +314,15 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 5; imm: off; once: off; write +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 5; imm: off; once: off; write -b +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -294,11 +338,15 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -314,11 +362,15 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 5; imm: off; once: off; write +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 5; imm: off; once: off; write -b +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -334,11 +386,15 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -354,11 +410,15 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 5; imm: off; once: off; write +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 5; imm: off; once: off; write -b +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -374,11 +434,15 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -394,11 +458,15 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 5; imm: off; once: off; write +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 5; imm: off; once: off; write -b +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -414,11 +482,15 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. @@ -426,116 +498,136 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.hookup; errno: 28; imm: off; once: on; write +Event: refblock_alloc_hookup; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.hookup; errno: 28; imm: off; once: on; write -b +Event: refblock_alloc_hookup; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.hookup; errno: 28; imm: off; once: off; write +Event: refblock_alloc_hookup; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 55 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.hookup; errno: 28; imm: off; once: off; write -b +Event: refblock_alloc_hookup; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 251 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write; errno: 28; imm: off; once: on; write +Event: refblock_alloc_write; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write; errno: 28; imm: off; once: on; write -b +Event: refblock_alloc_write; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write; errno: 28; imm: off; once: off; write +Event: refblock_alloc_write; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write; errno: 28; imm: off; once: off; write -b +Event: refblock_alloc_write; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: on; write +Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: on; write -b +Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: off; write +Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device -10 leaked clusters were found on the image. +11 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: off; write -b +Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 23 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_table; errno: 28; imm: off; once: on; write +Event: refblock_alloc_write_table; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_table; errno: 28; imm: off; once: on; write -b +Event: refblock_alloc_write_table; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_table; errno: 28; imm: off; once: off; write +Event: refblock_alloc_write_table; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device -10 leaked clusters were found on the image. +11 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_table; errno: 28; imm: off; once: off; write -b +Event: refblock_alloc_write_table; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 23 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.switch_table; errno: 28; imm: off; once: on; write +Event: refblock_alloc_switch_table; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.switch_table; errno: 28; imm: off; once: on; write -b +Event: refblock_alloc_switch_table; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.switch_table; errno: 28; imm: off; once: off; write +Event: refblock_alloc_switch_table; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device -10 leaked clusters were found on the image. +11 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.switch_table; errno: 28; imm: off; once: off; write -b +Event: refblock_alloc_switch_table; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 23 leaked clusters were found on the image. @@ -545,64 +637,76 @@ This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.alloc_table; errno: 5; imm: off; once: on +Event: l1_grow_alloc_table; errno: 5; imm: off; once: on write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.alloc_table; errno: 5; imm: off; once: off +Event: l1_grow_alloc_table; errno: 5; imm: off; once: off +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.alloc_table; errno: 28; imm: off; once: on +Event: l1_grow_alloc_table; errno: 28; imm: off; once: on write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.alloc_table; errno: 28; imm: off; once: off +Event: l1_grow_alloc_table; errno: 28; imm: off; once: off +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.write_table; errno: 5; imm: off; once: on +Event: l1_grow_write_table; errno: 5; imm: off; once: on write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.write_table; errno: 5; imm: off; once: off +Event: l1_grow_write_table; errno: 5; imm: off; once: off +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.write_table; errno: 28; imm: off; once: on +Event: l1_grow_write_table; errno: 28; imm: off; once: on write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.write_table; errno: 28; imm: off; once: off +Event: l1_grow_write_table; errno: 28; imm: off; once: off +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.activate_table; errno: 5; imm: off; once: on +Event: l1_grow_activate_table; errno: 5; imm: off; once: on write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.activate_table; errno: 5; imm: off; once: off +Event: l1_grow_activate_table; errno: 5; imm: off; once: off +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error 96 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.activate_table; errno: 28; imm: off; once: on +Event: l1_grow_activate_table; errno: 28; imm: off; once: on write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_grow.activate_table; errno: 28; imm: off; once: off +Event: l1_grow_activate_table; errno: 28; imm: off; once: off +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 96 leaked clusters were found on the image. diff --git a/qemu/tests/qemu-iotests/027 b/qemu/tests/qemu-iotests/027 index 3fa81b83b..08593da77 100755 --- a/qemu/tests/qemu-iotests/027 +++ b/qemu/tests/qemu-iotests/027 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/028 b/qemu/tests/qemu-iotests/028 index a1f4423d4..7783e57c7 100755 --- a/qemu/tests/qemu-iotests/028 +++ b/qemu/tests/qemu-iotests/028 @@ -28,7 +28,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -60,6 +59,9 @@ base_size=$(( image_size - 1024 * 1024 * 1024 )) offset=$(( base_size - 32 * 1024 )) +TEST_IMG_SAVE="$TEST_IMG" +TEST_IMG="$TEST_IMG.base" + _make_test_img $base_size echo "Filling base image" @@ -73,7 +75,7 @@ _check_test_img echo "Creating test image with backing file" echo -mv "$TEST_IMG" "$TEST_IMG.base" +TEST_IMG="$TEST_IMG_SAVE" _make_test_img -b "$TEST_IMG.base" $image_size echo "Filling test image" @@ -111,10 +113,12 @@ h=$QEMU_HANDLE QEMU_COMM_TIMEOUT=1 # Silence output since it contains the disk image path and QEMU's readline -# character echoing makes it very hard to filter the output +# character echoing makes it very hard to filter the output. Plus, there +# is no telling how many times the command will repeat before succeeding. _send_qemu_cmd $h "drive_backup disk ${TEST_IMG}.copy" "(qemu)" >/dev/null _send_qemu_cmd $h "" "Formatting" | _filter_img_create -qemu_cmd_repeat=20 _send_qemu_cmd $h "info block-jobs" "No active jobs" +qemu_cmd_repeat=20 _send_qemu_cmd $h "info block-jobs" "No active jobs" >/dev/null +_send_qemu_cmd $h "info block-jobs" "No active jobs" _send_qemu_cmd $h 'quit' "" # Base image sectors diff --git a/qemu/tests/qemu-iotests/028.out b/qemu/tests/qemu-iotests/028.out index 5db167ce7..acd2870ba 100644 --- a/qemu/tests/qemu-iotests/028.out +++ b/qemu/tests/qemu-iotests/028.out @@ -1,5 +1,5 @@ QA output created by 028 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=3221227008 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=3221227008 Filling base image === IO: pattern 195 @@ -70,7 +70,7 @@ wrote 512/512 bytes at offset 3221225984 No errors were found on the image. Creating test image with backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294968832 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294968832 backing_file=TEST_DIR/t.IMGFMT.base Filling test image === IO: pattern 196 @@ -468,11 +468,8 @@ No errors were found on the image. block-backup -Formatting 'TEST_DIR/t.IMGFMT.copy', fmt=IMGFMT size=4294968832 backing_file='TEST_DIR/t.IMGFMT.base' backing_fmt='IMGFMT' -(qemu) +Formatting 'TEST_DIR/t.IMGFMT.copy', fmt=IMGFMT size=4294968832 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT (qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block-info block-jinfo block-joinfo block-jobinfo block-jobs -Type backup, device disk: Completed 0 of 4294968832 bytes, speed limit 0 bytes/s -iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block-info block-jinfo block-joinfo block-jobinfo block-jobs No active jobs === IO: pattern 195 read 512/512 bytes at offset 3221194240 diff --git a/qemu/tests/qemu-iotests/029 b/qemu/tests/qemu-iotests/029 index b9cd826c7..e639ac0dd 100755 --- a/qemu/tests/qemu-iotests/029 +++ b/qemu/tests/qemu-iotests/029 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/030 b/qemu/tests/qemu-iotests/030 index 952a524ec..3ac2443e5 100755 --- a/qemu/tests/qemu-iotests/030 +++ b/qemu/tests/qemu-iotests/030 @@ -35,6 +35,7 @@ class TestSingleDrive(iotests.QMPTestCase): qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img) qemu_io('-f', 'raw', '-c', 'write -P 0x1 0 512', backing_img) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 524288 512', mid_img) self.vm = iotests.VM().add_drive("blkdebug::" + test_img) self.vm.launch() @@ -90,9 +91,13 @@ class TestSingleDrive(iotests.QMPTestCase): qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img), 'image file map does not match backing file after streaming') - def test_stream_partial(self): + def test_stream_no_op(self): self.assert_no_active_block_jobs() + # The image map is empty before the operation + empty_map = qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img) + + # This is a no-op: no data should ever be copied from the base image result = self.vm.qmp('block-stream', device='drive0', base=mid_img) self.assert_qmp(result, 'return', {}) @@ -101,6 +106,20 @@ class TestSingleDrive(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.shutdown() + self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img), + empty_map, 'image file map changed after a no-op') + + def test_stream_partial(self): + self.assert_no_active_block_jobs() + + result = self.vm.qmp('block-stream', device='drive0', base=backing_img) + self.assert_qmp(result, 'return', {}) + + self.wait_until_completed() + + self.assert_no_active_block_jobs() + self.vm.shutdown() + self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', mid_img), qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img), 'image file map does not match backing file after streaming') @@ -245,6 +264,7 @@ class TestEIO(TestErrors): while not completed: for event in self.vm.get_qmp_events(wait=True): if event['event'] == 'BLOCK_JOB_ERROR': + error = True self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'read') @@ -257,9 +277,11 @@ class TestEIO(TestErrors): self.assert_qmp(result, 'return', {}) result = self.vm.qmp('query-block-jobs') + if result == {'return': []}: + # Race; likely already finished. Check. + continue self.assert_qmp(result, 'return[0]/paused', False) self.assert_qmp(result, 'return[0]/io-status', 'ok') - error = True elif event['event'] == 'BLOCK_JOB_COMPLETED': self.assertTrue(error, 'job completed unexpectedly') self.assert_qmp(event, 'data/type', 'stream') diff --git a/qemu/tests/qemu-iotests/030.out b/qemu/tests/qemu-iotests/030.out index fa16b5cce..6323079e0 100644 --- a/qemu/tests/qemu-iotests/030.out +++ b/qemu/tests/qemu-iotests/030.out @@ -1,5 +1,5 @@ -............. +.............. ---------------------------------------------------------------------- -Ran 13 tests +Ran 14 tests OK diff --git a/qemu/tests/qemu-iotests/031 b/qemu/tests/qemu-iotests/031 index 2a77ba8cb..1e08abc5e 100755 --- a/qemu/tests/qemu-iotests/031 +++ b/qemu/tests/qemu-iotests/031 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/031.out b/qemu/tests/qemu-iotests/031.out index fce3ce098..7f5050b81 100644 --- a/qemu/tests/qemu-iotests/031.out +++ b/qemu/tests/qemu-iotests/031.out @@ -52,11 +52,6 @@ autoclear_features 0x0 refcount_order 4 header_length 72 -Header extension: -magic 0x6803f857 -length 144 -data - Header extension: magic 0x12345678 length 31 @@ -68,7 +63,7 @@ No errors were found on the image. magic 0x514649fb version 2 -backing_file_offset 0x128 +backing_file_offset 0x90 backing_file_size 0x17 cluster_bits 16 size 67108864 @@ -90,11 +85,6 @@ magic 0xe2792aca length 11 data 'host_device' -Header extension: -magic 0x6803f857 -length 144 -data - Header extension: magic 0x12345678 length 31 @@ -125,6 +115,11 @@ autoclear_features 0x0 refcount_order 4 header_length 104 +Header extension: +magic 0x6803f857 +length 144 +data + Header extension: magic 0x12345678 length 31 diff --git a/qemu/tests/qemu-iotests/032 b/qemu/tests/qemu-iotests/032 index b1ba5c321..24bcb52fc 100755 --- a/qemu/tests/qemu-iotests/032 +++ b/qemu/tests/qemu-iotests/032 @@ -27,7 +27,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/033 b/qemu/tests/qemu-iotests/033 index a61d8ced1..16edcf2f0 100755 --- a/qemu/tests/qemu-iotests/033 +++ b/qemu/tests/qemu-iotests/033 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -57,12 +56,13 @@ do_test() } | $QEMU_IO } +for write_zero_cmd in "write -z" "aio_write -z"; do for align in 512 4k; do echo echo "== preparing image ==" do_test $align "write -P 0xa 0x200 0x400" "$TEST_IMG" | _filter_qemu_io do_test $align "write -P 0xa 0x20000 0x600" "$TEST_IMG" | _filter_qemu_io - do_test $align "write -z 0x400 0x20000" "$TEST_IMG" | _filter_qemu_io + do_test $align "$write_zero_cmd 0x400 0x20000" "$TEST_IMG" | _filter_qemu_io echo echo "== verifying patterns (1) ==" @@ -73,7 +73,7 @@ for align in 512 4k; do echo echo "== rewriting zeroes ==" do_test $align "write -P 0xb 0x10000 0x10000" "$TEST_IMG" | _filter_qemu_io - do_test $align "write -z 0x10000 0x10000" "$TEST_IMG" | _filter_qemu_io + do_test $align "$write_zero_cmd 0x10000 0x10000" "$TEST_IMG" | _filter_qemu_io echo echo "== verifying patterns (2) ==" @@ -82,7 +82,7 @@ for align in 512 4k; do echo echo "== rewriting unaligned zeroes ==" do_test $align "write -P 0xb 0x0 0x1000" "$TEST_IMG" | _filter_qemu_io - do_test $align "write -z 0x200 0x200" "$TEST_IMG" | _filter_qemu_io + do_test $align "$write_zero_cmd 0x200 0x200" "$TEST_IMG" | _filter_qemu_io echo echo "== verifying patterns (3) ==" @@ -92,6 +92,7 @@ for align in 512 4k; do echo done +done # success, all done echo "*** done" diff --git a/qemu/tests/qemu-iotests/033.out b/qemu/tests/qemu-iotests/033.out index c3d18aa45..95929eff7 100644 --- a/qemu/tests/qemu-iotests/033.out +++ b/qemu/tests/qemu-iotests/033.out @@ -42,6 +42,88 @@ read 3072/3072 bytes at offset 1024 3 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== preparing image == +wrote 1024/1024 bytes at offset 512 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1536/1536 bytes at offset 131072 +1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 1024 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== verifying patterns (1) == +read 512/512 bytes at offset 512 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 1024 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 132096 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rewriting zeroes == +wrote 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== verifying patterns (2) == +read 131072/131072 bytes at offset 1024 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rewriting unaligned zeroes == +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 512/512 bytes at offset 512 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== verifying patterns (3) == +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 512 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 3072/3072 bytes at offset 1024 +3 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + + +== preparing image == +wrote 1024/1024 bytes at offset 512 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1536/1536 bytes at offset 131072 +1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 1024 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== verifying patterns (1) == +read 512/512 bytes at offset 512 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 1024 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 132096 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rewriting zeroes == +wrote 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== verifying patterns (2) == +read 131072/131072 bytes at offset 1024 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rewriting unaligned zeroes == +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 512/512 bytes at offset 512 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== verifying patterns (3) == +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 512 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 3072/3072 bytes at offset 1024 +3 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + + == preparing image == wrote 1024/1024 bytes at offset 512 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/qemu/tests/qemu-iotests/034 b/qemu/tests/qemu-iotests/034 index 69c785858..c711cfce9 100755 --- a/qemu/tests/qemu-iotests/034 +++ b/qemu/tests/qemu-iotests/034 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -51,9 +50,13 @@ size=128M echo echo "== creating backing file for COW tests ==" +TEST_IMG_SAVE="$TEST_IMG" +TEST_IMG="$TEST_IMG.base" + _make_test_img $size $QEMU_IO -c "write -P 0x55 0 1M" "$TEST_IMG" | _filter_qemu_io -mv "$TEST_IMG" "$TEST_IMG.base" + +TEST_IMG="$TEST_IMG_SAVE" _make_test_img -b "$TEST_IMG.base" 6G diff --git a/qemu/tests/qemu-iotests/034.out b/qemu/tests/qemu-iotests/034.out index d12daf206..0764ead8b 100644 --- a/qemu/tests/qemu-iotests/034.out +++ b/qemu/tests/qemu-iotests/034.out @@ -1,10 +1,10 @@ QA output created by 034 == creating backing file for COW tests == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 wrote 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base == zero write with backing file == wrote 196608/196608 bytes at offset 65536 diff --git a/qemu/tests/qemu-iotests/035 b/qemu/tests/qemu-iotests/035 index ebe9b8c92..efc38e4d4 100755 --- a/qemu/tests/qemu-iotests/035 +++ b/qemu/tests/qemu-iotests/035 @@ -26,7 +26,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/036 b/qemu/tests/qemu-iotests/036 index 392f1ef3e..ce638d607 100755 --- a/qemu/tests/qemu-iotests/036 +++ b/qemu/tests/qemu-iotests/036 @@ -28,7 +28,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -57,6 +56,7 @@ _make_test_img 64M $PYTHON qcow2.py "$TEST_IMG" set-feature-bit incompatible 63 # Without feature table +$PYTHON qcow2.py "$TEST_IMG" del-header-ext 0x6803f857 $PYTHON qcow2.py "$TEST_IMG" dump-header _img_info @@ -73,6 +73,7 @@ $PYTHON qcow2.py "$TEST_IMG" set-feature-bit incompatible 62 $PYTHON qcow2.py "$TEST_IMG" set-feature-bit incompatible 63 # Without feature table +$PYTHON qcow2.py "$TEST_IMG" del-header-ext 0x6803f857 _img_info # With feature table containing bit 63 diff --git a/qemu/tests/qemu-iotests/036.out b/qemu/tests/qemu-iotests/036.out index 5616e37b3..9b009b8c1 100644 --- a/qemu/tests/qemu-iotests/036.out +++ b/qemu/tests/qemu-iotests/036.out @@ -22,18 +22,18 @@ autoclear_features 0x0 refcount_order 4 header_length 104 -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: Unknown incompatible feature: 8000000000000000 -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: Test feature +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported IMGFMT feature(s): Unknown incompatible feature: 8000000000000000 +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported IMGFMT feature(s): Test feature === Image with multiple incompatible feature bits === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: Unknown incompatible feature: e000000000000000 -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: Test feature, Unknown incompatible feature: 6000000000000000 -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: Test feature, Unknown incompatible feature: c000000000000000 -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: test1, test2, Unknown incompatible feature: 8000000000000000 -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: test1, test2, test3 -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: test2, Unknown incompatible feature: a000000000000000 +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported IMGFMT feature(s): Unknown incompatible feature: e000000000000000 +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported IMGFMT feature(s): Test feature, Unknown incompatible feature: 6000000000000000 +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported IMGFMT feature(s): Test feature, Unknown incompatible feature: c000000000000000 +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported IMGFMT feature(s): test1, test2, Unknown incompatible feature: 8000000000000000 +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported IMGFMT feature(s): test1, test2, test3 +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported IMGFMT feature(s): test2, Unknown incompatible feature: a000000000000000 === Create image with unknown autoclear feature bit === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 @@ -56,6 +56,11 @@ autoclear_features 0x8000000000000000 refcount_order 4 header_length 104 +Header extension: +magic 0x6803f857 +length 144 +data + === Repair image === diff --git a/qemu/tests/qemu-iotests/037 b/qemu/tests/qemu-iotests/037 index 9171d8c8a..c476b823d 100755 --- a/qemu/tests/qemu-iotests/037 +++ b/qemu/tests/qemu-iotests/037 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -51,6 +50,9 @@ size=128M echo echo "== creating backing file for COW tests ==" +TEST_IMG_SAVE="$TEST_IMG" +TEST_IMG="$TEST_IMG.base" + _make_test_img $size function backing_io() @@ -71,7 +73,7 @@ function backing_io() backing_io 0 256 write | $QEMU_IO "$TEST_IMG" | _filter_qemu_io -mv "$TEST_IMG" "$TEST_IMG.base" +TEST_IMG="$TEST_IMG_SAVE" _make_test_img -b "$TEST_IMG.base" 6G diff --git a/qemu/tests/qemu-iotests/037.out b/qemu/tests/qemu-iotests/037.out index dc40a021a..cd6710c90 100644 --- a/qemu/tests/qemu-iotests/037.out +++ b/qemu/tests/qemu-iotests/037.out @@ -1,7 +1,7 @@ QA output created by 037 == creating backing file for COW tests == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 512/512 bytes at offset 512 @@ -514,7 +514,7 @@ wrote 512/512 bytes at offset 130048 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 512/512 bytes at offset 130560 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base == COW in a single cluster == wrote 2048/2048 bytes at offset 0 diff --git a/qemu/tests/qemu-iotests/038 b/qemu/tests/qemu-iotests/038 index cfaf00a78..d99a1501d 100755 --- a/qemu/tests/qemu-iotests/038 +++ b/qemu/tests/qemu-iotests/038 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -48,6 +47,9 @@ size=128M echo echo "== creating backing file for COW tests ==" +TEST_IMG_SAVE="$TEST_IMG" +TEST_IMG="$TEST_IMG.base" + _make_test_img $size function backing_io() @@ -68,7 +70,7 @@ function backing_io() backing_io 0 256 write | $QEMU_IO "$TEST_IMG" | _filter_qemu_io -mv "$TEST_IMG" "$TEST_IMG.base" +TEST_IMG="$TEST_IMG_SAVE" _make_test_img -b "$TEST_IMG.base" 6G diff --git a/qemu/tests/qemu-iotests/038.out b/qemu/tests/qemu-iotests/038.out index e1a7e9441..0bdfb19fa 100644 --- a/qemu/tests/qemu-iotests/038.out +++ b/qemu/tests/qemu-iotests/038.out @@ -1,7 +1,7 @@ QA output created by 038 == creating backing file for COW tests == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 65536 @@ -514,7 +514,7 @@ wrote 65536/65536 bytes at offset 16646144 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 16711680 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base == Some concurrent requests touching the same cluster == wrote 65536/65536 bytes at offset XXX diff --git a/qemu/tests/qemu-iotests/039 b/qemu/tests/qemu-iotests/039 index 859705f84..1f4833969 100755 --- a/qemu/tests/qemu-iotests/039 +++ b/qemu/tests/qemu-iotests/039 @@ -28,7 +28,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -47,13 +46,6 @@ _supported_os Linux _default_cache_mode "writethrough" _supported_cache_modes "writethrough" -_subshell_exec() -{ - # Executing crashing commands in a subshell prevents information like the - # "Killed" line from being lost - (exec "$@") -} - size=128M echo @@ -74,8 +66,8 @@ echo "== Creating a dirty image file ==" IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img $size -_subshell_exec $QEMU_IO -c "write -P 0x5a 0 512" \ - -c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 \ +$QEMU_IO -c "write -P 0x5a 0 512" \ + -c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 \ | _filter_qemu_io # The dirty bit must be set @@ -109,8 +101,8 @@ echo "== Opening a dirty image read/write should repair it ==" IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img $size -_subshell_exec $QEMU_IO -c "write -P 0x5a 0 512" \ - -c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 \ +$QEMU_IO -c "write -P 0x5a 0 512" \ + -c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 \ | _filter_qemu_io # The dirty bit must be set @@ -127,8 +119,8 @@ echo "== Creating an image file with lazy_refcounts=off ==" IMGOPTS="compat=1.1,lazy_refcounts=off" _make_test_img $size -_subshell_exec $QEMU_IO -c "write -P 0x5a 0 512" \ - -c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 \ +$QEMU_IO -c "write -P 0x5a 0 512" \ + -c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 \ | _filter_qemu_io # The dirty bit must not be set since lazy_refcounts=off @@ -154,6 +146,33 @@ $PYTHON qcow2.py "$TEST_IMG".base dump-header | grep incompatible_features _check_test_img TEST_IMG="$TEST_IMG".base _check_test_img +echo +echo "== Changing lazy_refcounts setting at runtime ==" + +IMGOPTS="compat=1.1,lazy_refcounts=off" +_make_test_img $size + +$QEMU_IO -c "reopen -o lazy-refcounts=on" \ + -c "write -P 0x5a 0 512" \ + -c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 \ + | _filter_qemu_io + +# The dirty bit must be set +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_check_test_img + +IMGOPTS="compat=1.1,lazy_refcounts=on" +_make_test_img $size + +$QEMU_IO -c "reopen -o lazy-refcounts=off" \ + -c "write -P 0x5a 0 512" \ + -c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 \ + | _filter_qemu_io + +# The dirty bit must not be set +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_check_test_img + # success, all done echo "*** done" diff --git a/qemu/tests/qemu-iotests/039.out b/qemu/tests/qemu-iotests/039.out index d09751f9c..32c884694 100644 --- a/qemu/tests/qemu-iotests/039.out +++ b/qemu/tests/qemu-iotests/039.out @@ -11,7 +11,11 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./039: Killed ( exec "$@" ) +./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then + exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"; +else + exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"; +fi ) incompatible_features 0x1 ERROR cluster 5 refcount=0 reference=1 ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0 @@ -46,7 +50,11 @@ read 512/512 bytes at offset 0 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./039: Killed ( exec "$@" ) +./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then + exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"; +else + exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"; +fi ) incompatible_features 0x1 ERROR cluster 5 refcount=0 reference=1 Rebuilding refcount structure @@ -60,13 +68,17 @@ incompatible_features 0x0 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./039: Killed ( exec "$@" ) +./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then + exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"; +else + exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"; +fi ) incompatible_features 0x0 No errors were found on the image. == Committing to a backing file with lazy_refcounts=on == Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Image committed. @@ -74,4 +86,30 @@ incompatible_features 0x0 incompatible_features 0x0 No errors were found on the image. No errors were found on the image. + +== Changing lazy_refcounts setting at runtime == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then + exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"; +else + exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"; +fi ) +incompatible_features 0x1 +ERROR cluster 5 refcount=0 reference=1 +ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0 + +2 errors were found on the image. +Data may be corrupted, or further writes to the image may corrupt it. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then + exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"; +else + exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"; +fi ) +incompatible_features 0x0 +No errors were found on the image. *** done diff --git a/qemu/tests/qemu-iotests/040 b/qemu/tests/qemu-iotests/040 index ea2f98e51..5bdaf3d48 100755 --- a/qemu/tests/qemu-iotests/040 +++ b/qemu/tests/qemu-iotests/040 @@ -41,6 +41,7 @@ class ImageCommitTestCase(iotests.QMPTestCase): while not completed: for event in self.vm.get_qmp_events(wait=True): if event['event'] == 'BLOCK_JOB_COMPLETED': + self.assert_qmp_absent(event, 'data/error') self.assert_qmp(event, 'data/type', 'commit') self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/offset', event['data']['len']) @@ -251,5 +252,34 @@ class TestSetSpeed(ImageCommitTestCase): class TestActiveZeroLengthImage(TestSingleDrive): image_len = 0 +class TestReopenOverlay(ImageCommitTestCase): + image_len = 1024 * 1024 + img0 = os.path.join(iotests.test_dir, '0.img') + img1 = os.path.join(iotests.test_dir, '1.img') + img2 = os.path.join(iotests.test_dir, '2.img') + img3 = os.path.join(iotests.test_dir, '3.img') + + def setUp(self): + iotests.create_image(self.img0, self.image_len) + qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.img0, self.img1) + qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.img1, self.img2) + qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.img2, self.img3) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xab 0 128K', self.img1) + self.vm = iotests.VM().add_drive(self.img3) + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + os.remove(self.img0) + os.remove(self.img1) + os.remove(self.img2) + os.remove(self.img3) + + # This tests what happens when the overlay image of the 'top' node + # needs to be reopened in read-write mode in order to update the + # backing image string. + def test_reopen_overlay(self): + self.run_commit_test(self.img1, self.img0) + if __name__ == '__main__': iotests.main(supported_fmts=['qcow2', 'qed']) diff --git a/qemu/tests/qemu-iotests/040.out b/qemu/tests/qemu-iotests/040.out index 42314e9c0..4fd1c2dcd 100644 --- a/qemu/tests/qemu-iotests/040.out +++ b/qemu/tests/qemu-iotests/040.out @@ -1,5 +1,5 @@ -........................ +......................... ---------------------------------------------------------------------- -Ran 24 tests +Ran 25 tests OK diff --git a/qemu/tests/qemu-iotests/041 b/qemu/tests/qemu-iotests/041 index 3d46ed705..b1c542f99 100755 --- a/qemu/tests/qemu-iotests/041 +++ b/qemu/tests/qemu-iotests/041 @@ -34,14 +34,18 @@ quorum_img3 = os.path.join(iotests.test_dir, 'quorum3.img') quorum_repair_img = os.path.join(iotests.test_dir, 'quorum_repair.img') quorum_snapshot_file = os.path.join(iotests.test_dir, 'quorum_snapshot.img') - class TestSingleDrive(iotests.QMPTestCase): image_len = 1 * 1024 * 1024 # MB + qmp_cmd = 'drive-mirror' + qmp_target = target_img + not_found_error = 'DeviceNotFound' def setUp(self): iotests.create_image(backing_img, self.image_len) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) self.vm = iotests.VM().add_drive(test_img) + if iotests.qemu_default_machine == 'pc': + self.vm.add_drive(None, 'media=cdrom', 'ide') self.vm.launch() def tearDown(self): @@ -56,8 +60,8 @@ class TestSingleDrive(iotests.QMPTestCase): def test_complete(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - target=target_img) + result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', + target=self.qmp_target) self.assert_qmp(result, 'return', {}) self.complete_and_wait() @@ -70,8 +74,8 @@ class TestSingleDrive(iotests.QMPTestCase): def test_cancel(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - target=target_img) + result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', + target=self.qmp_target) self.assert_qmp(result, 'return', {}) self.cancel_and_wait(force=True) @@ -82,8 +86,8 @@ class TestSingleDrive(iotests.QMPTestCase): def test_cancel_after_ready(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - target=target_img) + result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', + target=self.qmp_target) self.assert_qmp(result, 'return', {}) self.wait_ready_and_cancel() @@ -96,8 +100,8 @@ class TestSingleDrive(iotests.QMPTestCase): def test_pause(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - target=target_img) + result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', + target=self.qmp_target) self.assert_qmp(result, 'return', {}) result = self.vm.qmp('block-job-pause', device='drive0') @@ -123,8 +127,8 @@ class TestSingleDrive(iotests.QMPTestCase): self.assert_no_active_block_jobs() # A small buffer is rounded up automatically - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - buf_size=4096, target=target_img) + result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', + buf_size=4096, target=self.qmp_target) self.assert_qmp(result, 'return', {}) self.complete_and_wait() @@ -139,8 +143,8 @@ class TestSingleDrive(iotests.QMPTestCase): qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d' % (self.image_len, self.image_len), target_img) - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - buf_size=65536, mode='existing', target=target_img) + result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', + buf_size=65536, mode='existing', target=self.qmp_target) self.assert_qmp(result, 'return', {}) self.complete_and_wait() @@ -155,8 +159,8 @@ class TestSingleDrive(iotests.QMPTestCase): qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s' % (self.image_len, backing_img), target_img) - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - mode='existing', target=target_img) + result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', + mode='existing', target=self.qmp_target) self.assert_qmp(result, 'return', {}) self.complete_and_wait() @@ -167,30 +171,85 @@ class TestSingleDrive(iotests.QMPTestCase): 'target image does not match source after mirroring') def test_medium_not_found(self): - result = self.vm.qmp('drive-mirror', device='ide1-cd0', sync='full', - target=target_img) - self.assert_qmp(result, 'error/class', 'GenericError') + if iotests.qemu_default_machine != 'pc': + return + + result = self.vm.qmp(self.qmp_cmd, device='ide1-cd0', sync='full', + target=self.qmp_target) + self.assert_qmp(result, 'error/class', self.not_found_error) def test_image_not_found(self): - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - mode='existing', target=target_img) + result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', + mode='existing', target=self.qmp_target) self.assert_qmp(result, 'error/class', 'GenericError') def test_device_not_found(self): - result = self.vm.qmp('drive-mirror', device='nonexistent', sync='full', - target=target_img) - self.assert_qmp(result, 'error/class', 'DeviceNotFound') + result = self.vm.qmp(self.qmp_cmd, device='nonexistent', sync='full', + target=self.qmp_target) + self.assert_qmp(result, 'error/class', self.not_found_error) + +class TestSingleBlockdev(TestSingleDrive): + qmp_cmd = 'blockdev-mirror' + qmp_target = 'node1' + not_found_error = 'GenericError' + + def setUp(self): + TestSingleDrive.setUp(self) + qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img) + args = {'options': + {'driver': iotests.imgfmt, + 'node-name': self.qmp_target, + 'file': { 'filename': target_img, 'driver': 'file' } } } + result = self.vm.qmp("blockdev-add", **args) + self.assert_qmp(result, 'return', {}) + + test_large_cluster = None + test_image_not_found = None + test_small_buffer2 = None + +class TestBlockdevAttached(iotests.QMPTestCase): + image_len = 1 * 1024 * 1024 # MB + + def setUp(self): + iotests.create_image(backing_img, self.image_len) + qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) + qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img) + self.vm = iotests.VM().add_drive(test_img) + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + os.remove(test_img) + os.remove(target_img) + + def test_blockdev_attached(self): + self.assert_no_active_block_jobs() + args = {'options': + {'driver': iotests.imgfmt, + 'id': 'drive1', + 'file': { 'filename': target_img, 'driver': 'file' } } } + result = self.vm.qmp("blockdev-add", **args) + self.assert_qmp(result, 'return', {}) + result = self.vm.qmp('blockdev-mirror', device='drive0', sync='full', + target='drive1') + self.assert_qmp(result, 'error/class', 'GenericError') class TestSingleDriveZeroLength(TestSingleDrive): image_len = 0 test_small_buffer2 = None test_large_cluster = None +class TestSingleBlockdevZeroLength(TestSingleBlockdev): + image_len = 0 + class TestSingleDriveUnalignedLength(TestSingleDrive): image_len = 1025 * 1024 test_small_buffer2 = None test_large_cluster = None +class TestSingleBlockdevUnalignedLength(TestSingleBlockdev): + image_len = 1025 * 1024 + class TestMirrorNoBacking(iotests.QMPTestCase): image_len = 2 * 1024 * 1024 # MB @@ -707,6 +766,9 @@ class TestRepairQuorum(iotests.QMPTestCase): def setUp(self): self.vm = iotests.VM() + if iotests.qemu_default_machine == 'pc': + self.vm.add_drive(None, 'media=cdrom', 'ide') + # Add each individual quorum images for i in self.IMAGES: qemu_img('create', '-f', iotests.imgfmt, i, @@ -748,8 +810,7 @@ class TestRepairQuorum(iotests.QMPTestCase): self.assert_qmp(result, 'return', {}) self.complete_and_wait(drive="quorum0") - result = self.vm.qmp('query-named-block-nodes') - self.assert_qmp(result, 'return[0]/file', quorum_repair_img) + self.assert_has_block_node("repair0", quorum_repair_img) # TODO: a better test requiring some QEMU infrastructure will be added # to check that this file is really driven by quorum self.vm.shutdown() @@ -771,8 +832,7 @@ class TestRepairQuorum(iotests.QMPTestCase): self.cancel_and_wait(drive="quorum0", force=True) # here we check that the last registered quorum file has not been # swapped out and unref - result = self.vm.qmp('query-named-block-nodes') - self.assert_qmp(result, 'return[0]/file', quorum_img3) + self.assert_has_block_node(None, quorum_img3) self.vm.shutdown() def test_cancel_after_ready(self): @@ -788,10 +848,9 @@ class TestRepairQuorum(iotests.QMPTestCase): self.assert_qmp(result, 'return', {}) self.wait_ready_and_cancel(drive="quorum0") - result = self.vm.qmp('query-named-block-nodes') # here we check that the last registered quorum file has not been # swapped out and unref - self.assert_qmp(result, 'return[0]/file', quorum_img3) + self.assert_has_block_node(None, quorum_img3) self.vm.shutdown() self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), 'target image does not match source after mirroring') @@ -831,7 +890,11 @@ class TestRepairQuorum(iotests.QMPTestCase): if not self.has_quorum(): return - result = self.vm.qmp('drive-mirror', device='ide1-cd0', sync='full', + if iotests.qemu_default_machine != 'pc': + return + + result = self.vm.qmp('drive-mirror', device='drive0', # CD-ROM + sync='full', node_name='repair0', replaces='img1', target=quorum_repair_img, format=iotests.imgfmt) @@ -877,7 +940,7 @@ class TestRepairQuorum(iotests.QMPTestCase): target=quorum_repair_img, format=iotests.imgfmt) self.assert_qmp(result, 'error/class', 'GenericError') - def test_unexistant_replaces(self): + def test_nonexistent_replaces(self): if not self.has_quorum(): return @@ -908,8 +971,7 @@ class TestRepairQuorum(iotests.QMPTestCase): self.assert_qmp(result, 'return', {}) self.complete_and_wait(drive="quorum0") - result = self.vm.qmp('query-named-block-nodes') - self.assert_qmp(result, 'return[0]/file', quorum_repair_img) + self.assert_has_block_node("repair0", quorum_repair_img) # TODO: a better test requiring some QEMU infrastructure will be added # to check that this file is really driven by quorum self.vm.shutdown() diff --git a/qemu/tests/qemu-iotests/041.out b/qemu/tests/qemu-iotests/041.out index 24093bc63..b67d0504a 100644 --- a/qemu/tests/qemu-iotests/041.out +++ b/qemu/tests/qemu-iotests/041.out @@ -1,5 +1,5 @@ -...................................................... +............................................................................ ---------------------------------------------------------------------- -Ran 54 tests +Ran 76 tests OK diff --git a/qemu/tests/qemu-iotests/042 b/qemu/tests/qemu-iotests/042 index 94ce3a9cc..351b2830a 100755 --- a/qemu/tests/qemu-iotests/042 +++ b/qemu/tests/qemu-iotests/042 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/043 b/qemu/tests/qemu-iotests/043 index b316b97c0..1c6c22d92 100755 --- a/qemu/tests/qemu-iotests/043 +++ b/qemu/tests/qemu-iotests/043 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/043.out b/qemu/tests/qemu-iotests/043.out index 012cc008e..b37d2a380 100644 --- a/qemu/tests/qemu-iotests/043.out +++ b/qemu/tests/qemu-iotests/043.out @@ -4,20 +4,20 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 == backing file references self == qemu-img: Backing file 'TEST_DIR/t.IMGFMT' creates an infinite loop. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base == parent references self == qemu-img: Backing file 'TEST_DIR/t.IMGFMT' creates an infinite loop. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.1.base' -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.2.base' -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.3.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.1.base +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.2.base +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.3.base == ancestor references another ancestor == qemu-img: Backing file 'TEST_DIR/t.IMGFMT.2.base' creates an infinite loop. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.1.base' -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.2.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.1.base +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.2.base == finite chain of length 3 (human) == image: TEST_DIR/t.IMGFMT @@ -44,6 +44,7 @@ cluster_size: 65536 "filename": "TEST_DIR/t.IMGFMT", "cluster-size": 65536, "format": "IMGFMT", + "full-backing-filename": "TEST_DIR/t.IMGFMT.2.base", "backing-filename": "TEST_DIR/t.IMGFMT.2.base", "dirty-flag": false }, @@ -52,6 +53,7 @@ cluster_size: 65536 "filename": "TEST_DIR/t.IMGFMT.2.base", "cluster-size": 65536, "format": "IMGFMT", + "full-backing-filename": "TEST_DIR/t.IMGFMT.1.base", "backing-filename": "TEST_DIR/t.IMGFMT.1.base", "dirty-flag": false }, diff --git a/qemu/tests/qemu-iotests/046 b/qemu/tests/qemu-iotests/046 index e0be46cf2..e528b67cc 100755 --- a/qemu/tests/qemu-iotests/046 +++ b/qemu/tests/qemu-iotests/046 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/046.out b/qemu/tests/qemu-iotests/046.out index 9d18af53a..ca2c7404a 100644 --- a/qemu/tests/qemu-iotests/046.out +++ b/qemu/tests/qemu-iotests/046.out @@ -66,7 +66,7 @@ wrote 65536/65536 bytes at offset 1966080 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 2031616 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base == Some concurrent requests touching the same cluster == blkdebug: Suspended request 'A' diff --git a/qemu/tests/qemu-iotests/047 b/qemu/tests/qemu-iotests/047 index c35cd096b..1b8f3d4a6 100755 --- a/qemu/tests/qemu-iotests/047 +++ b/qemu/tests/qemu-iotests/047 @@ -26,7 +26,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/049 b/qemu/tests/qemu-iotests/049 index 93aa0ea55..fff07604f 100755 --- a/qemu/tests/qemu-iotests/049 +++ b/qemu/tests/qemu-iotests/049 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/049.out b/qemu/tests/qemu-iotests/049.out index 9f93666c5..4673b67f3 100644 --- a/qemu/tests/qemu-iotests/049.out +++ b/qemu/tests/qemu-iotests/049.out @@ -95,17 +95,15 @@ qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1024 qemu-img: Image size must be less than 8 EiB! qemu-img create -f qcow2 -o size=-1024 TEST_DIR/t.qcow2 -qemu-img: qcow2 doesn't support shrinking images yet -qemu-img: TEST_DIR/t.qcow2: Could not resize image: Operation not supported -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +qemu-img: Parameter 'size' expects a non-negative number below 2^64 +qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2' qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k qemu-img: Image size must be less than 8 EiB! qemu-img create -f qcow2 -o size=-1k TEST_DIR/t.qcow2 -qemu-img: qcow2 doesn't support shrinking images yet -qemu-img: TEST_DIR/t.qcow2: Could not resize image: Operation not supported -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +qemu-img: Parameter 'size' expects a non-negative number below 2^64 +qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2' qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for @@ -120,6 +118,7 @@ qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. qemu-img create -f qcow2 -o size=foobar TEST_DIR/t.qcow2 qemu-img: Parameter 'size' expects a size +You may use k, M, G or T suffixes for kilobytes, megabytes, gigabytes and terabytes. qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2' == Check correct interpretation of suffixes for cluster size == @@ -157,30 +156,30 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_si == Check compat level option == qemu-img create -f qcow2 -o compat=0.10 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.10 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=1.1 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=1.1 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: '0.42' -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.42' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.42 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: 'foobar' -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='foobar' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=foobar encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 == Check preallocation option == qemu-img create -f qcow2 -o preallocation=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='off' lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation=off lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o preallocation=metadata TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation=metadata lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: invalid parameter value: 1234 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='1234' lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation=1234 lazy_refcounts=off refcount_bits=16 == Check encryption option == @@ -188,27 +187,21 @@ qemu-img create -f qcow2 -o encryption=off TEST_DIR/t.qcow2 64M Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o encryption=on TEST_DIR/t.qcow2 64M -qemu-img: Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. -qemu-img: Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=on cluster_size=65536 lazy_refcounts=off refcount_bits=16 == Check lazy_refcounts option (only with v3) == qemu-img create -f qcow2 -o compat=1.1,lazy_refcounts=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=1.1 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=1.1,lazy_refcounts=on TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=on refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=1.1 encryption=off cluster_size=65536 lazy_refcounts=on refcount_bits=16 qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.10 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=on TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=on refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.10 encryption=off cluster_size=65536 lazy_refcounts=on refcount_bits=16 *** done diff --git a/qemu/tests/qemu-iotests/050 b/qemu/tests/qemu-iotests/050 index 07802bc49..03b4a5d62 100755 --- a/qemu/tests/qemu-iotests/050 +++ b/qemu/tests/qemu-iotests/050 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -51,14 +50,19 @@ fi echo echo "== Creating images ==" +TEST_IMG_SAVE="$TEST_IMG" +TEST_IMG="$TEST_IMG.old" + size=10M _make_test_img $size $QEMU_IO -c "write -P 0x40 0 1048576" "$TEST_IMG" | _filter_qemu_io -mv "$TEST_IMG" "$TEST_IMG.old" + +TEST_IMG="$TEST_IMG_SAVE.new" _make_test_img $size $QEMU_IO -c "write -P 0x5a 0 1048576" "$TEST_IMG" | _filter_qemu_io -mv "$TEST_IMG" "$TEST_IMG.new" + +TEST_IMG="$TEST_IMG_SAVE" _make_test_img -b "$TEST_IMG.old" $size $QEMU_IO -c "write -z 0 1048576" "$TEST_IMG" | _filter_qemu_io diff --git a/qemu/tests/qemu-iotests/050.out b/qemu/tests/qemu-iotests/050.out index a6cb2e686..3602d580d 100644 --- a/qemu/tests/qemu-iotests/050.out +++ b/qemu/tests/qemu-iotests/050.out @@ -1,13 +1,13 @@ QA output created by 050 == Creating images == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=10485760 +Formatting 'TEST_DIR/t.IMGFMT.old', fmt=IMGFMT size=10485760 wrote 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=10485760 +Formatting 'TEST_DIR/t.IMGFMT.new', fmt=IMGFMT size=10485760 wrote 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=10485760 backing_file='TEST_DIR/t.IMGFMT.old' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=10485760 backing_file=TEST_DIR/t.IMGFMT.old wrote 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/qemu/tests/qemu-iotests/051 b/qemu/tests/qemu-iotests/051 index 4a8055b67..630cb7a11 100755 --- a/qemu/tests/qemu-iotests/051 +++ b/qemu/tests/qemu-iotests/051 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -61,10 +60,11 @@ function do_run_qemu() function run_qemu() { - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_generated_node_ids } size=128M +device_id="drive0" _make_test_img $size cp "$TEST_IMG" "$TEST_IMG.orig" @@ -75,10 +75,10 @@ echo echo === Unknown option === echo -run_qemu -drive file="$TEST_IMG",format=qcow2,unknown_opt= -run_qemu -drive file="$TEST_IMG",format=qcow2,unknown_opt=on -run_qemu -drive file="$TEST_IMG",format=qcow2,unknown_opt=1234 -run_qemu -drive file="$TEST_IMG",format=qcow2,unknown_opt=foo +run_qemu -drive file="$TEST_IMG",format=qcow2,unknown_opt=,if=none,id=$device_id +run_qemu -drive file="$TEST_IMG",format=qcow2,unknown_opt=on,if=none,id=$device_id +run_qemu -drive file="$TEST_IMG",format=qcow2,unknown_opt=1234,if=none,id=$device_id +run_qemu -drive file="$TEST_IMG",format=qcow2,unknown_opt=foo,if=none,id=$device_id echo echo === Unknown protocol option === @@ -108,7 +108,8 @@ echo echo === Overriding backing file === echo -echo "info block" | run_qemu -drive file="$TEST_IMG",driver=qcow2,backing.file.filename="$TEST_IMG.orig" -nodefaults +echo "info block" | run_qemu -drive file="$TEST_IMG",driver=qcow2,backing.file.filename="$TEST_IMG.orig",if=none,id=$device_id -nodefaults\ + | _filter_generated_node_ids # Drivers that don't support backing files run_qemu -drive file="$TEST_IMG",driver=raw,backing.file.filename="$TEST_IMG.orig" @@ -139,41 +140,63 @@ echo echo === No medium === echo -run_qemu -drive if=floppy -run_qemu -drive if=ide,media=cdrom -run_qemu -drive if=scsi,media=cdrom +case "$QEMU_DEFAULT_MACHINE" in + pc) + run_qemu -drive if=floppy + run_qemu -drive if=ide,media=cdrom + run_qemu -drive if=scsi,media=cdrom + run_qemu -drive if=ide + run_qemu -drive if=scsi + ;; + *) + ;; +esac -run_qemu -drive if=ide run_qemu -drive if=virtio -run_qemu -drive if=scsi -run_qemu -drive if=none,id=disk -device ide-cd,drive=disk -run_qemu -drive if=none,id=disk -device lsi53c895a -device scsi-cd,drive=disk - -run_qemu -drive if=none,id=disk -device ide-drive,drive=disk -run_qemu -drive if=none,id=disk -device ide-hd,drive=disk -run_qemu -drive if=none,id=disk -device lsi53c895a -device scsi-disk,drive=disk -run_qemu -drive if=none,id=disk -device lsi53c895a -device scsi-hd,drive=disk +case "$QEMU_DEFAULT_MACHINE" in + pc) + run_qemu -drive if=none,id=disk -device ide-cd,drive=disk + run_qemu -drive if=none,id=disk -device lsi53c895a -device scsi-cd,drive=disk + run_qemu -drive if=none,id=disk -device ide-drive,drive=disk + run_qemu -drive if=none,id=disk -device ide-hd,drive=disk + run_qemu -drive if=none,id=disk -device lsi53c895a -device scsi-disk,drive=disk + run_qemu -drive if=none,id=disk -device lsi53c895a -device scsi-hd,drive=disk + ;; + *) + ;; +esac echo echo === Read-only === echo -run_qemu -drive file="$TEST_IMG",if=floppy,readonly=on -run_qemu -drive file="$TEST_IMG",if=ide,media=cdrom,readonly=on -run_qemu -drive file="$TEST_IMG",if=scsi,media=cdrom,readonly=on +case "$QEMU_DEFAULT_MACHINE" in + pc) + run_qemu -drive file="$TEST_IMG",if=floppy,readonly=on + run_qemu -drive file="$TEST_IMG",if=ide,media=cdrom,readonly=on + run_qemu -drive file="$TEST_IMG",if=scsi,media=cdrom,readonly=on + run_qemu -drive file="$TEST_IMG",if=ide,readonly=on + run_qemu -drive file="$TEST_IMG",if=scsi,readonly=on + ;; + *) + ;; +esac -run_qemu -drive file="$TEST_IMG",if=ide,readonly=on run_qemu -drive file="$TEST_IMG",if=virtio,readonly=on -run_qemu -drive file="$TEST_IMG",if=scsi,readonly=on - -run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device ide-cd,drive=disk -run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device lsi53c895a -device scsi-cd,drive=disk -run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device ide-drive,drive=disk -run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device ide-hd,drive=disk -run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device lsi53c895a -device scsi-disk,drive=disk -run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device lsi53c895a -device scsi-hd,drive=disk +case "$QEMU_DEFAULT_MACHINE" in + pc) + run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device ide-cd,drive=disk + run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device lsi53c895a -device scsi-cd,drive=disk + run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device ide-drive,drive=disk + run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device ide-hd,drive=disk + run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device lsi53c895a -device scsi-disk,drive=disk + run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device lsi53c895a -device scsi-hd,drive=disk + ;; + *) + ;; +esac echo echo === Cache modes === @@ -182,12 +205,20 @@ echo # Cannot use the test image because cache=none might not work on the host FS # Use cdrom so that we won't get errors about missing media -run_qemu -drive media=cdrom,cache=none -run_qemu -drive media=cdrom,cache=directsync -run_qemu -drive media=cdrom,cache=writeback -run_qemu -drive media=cdrom,cache=writethrough -run_qemu -drive media=cdrom,cache=unsafe -run_qemu -drive media=cdrom,cache=invalid_value +run_qemu -drive driver=null-co,cache=none +run_qemu -drive driver=null-co,cache=directsync +run_qemu -drive driver=null-co,cache=writeback +run_qemu -drive driver=null-co,cache=writethrough +run_qemu -drive driver=null-co,cache=unsafe +run_qemu -drive driver=null-co,cache=invalid_value + +# Can't test direct=on here because O_DIRECT might not be supported on this FS +# Test 142 checks the direct=on cases + +for cache in writeback writethrough unsafe invalid_value; do + echo -e "info block\ninfo block file\ninfo block backing\ninfo block backing-file" | \ + run_qemu -drive file="$TEST_IMG",cache=$cache,backing.file.filename="$TEST_IMG.base",backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=$device_id -nodefaults +done echo echo === Specifying the protocol layer === @@ -230,6 +261,24 @@ run_qemu -drive file="$TEST_IMG",bps_wr_max=1234,throttling.bps-write-max=5678 run_qemu -drive file="$TEST_IMG",iops_size=1234,throttling.iops-size=5678 run_qemu -drive file="$TEST_IMG",readonly=on,read-only=off +echo +echo === Catching negative/large throttling values === +echo + +run_qemu -drive file="$TEST_IMG",iops=-1 +run_qemu -drive file="$TEST_IMG",bps=-2 +run_qemu -drive file="$TEST_IMG",bps_rd=-3 +run_qemu -drive file="$TEST_IMG",bps_rd_max=-3 +run_qemu -drive file="$TEST_IMG",throttling.iops-total=-4 +run_qemu -drive file="$TEST_IMG",throttling.bps-total=-5 +# These are accepted +run_qemu -drive file="$TEST_IMG",bps=0 +run_qemu -drive file="$TEST_IMG",bps=1 +run_qemu -drive file="$TEST_IMG",bps=1000000000000000 +# While these are not +run_qemu -drive file="$TEST_IMG",bps=1000000000000001 +run_qemu -drive file="$TEST_IMG",bps=9999999999999999 + echo echo === Parsing protocol from file name === echo @@ -252,26 +301,30 @@ echo $QEMU_IO -c "write -P 0x11 0 4k" "$TEST_IMG" | _filter_qemu_io -echo 'qemu-io ide0-hd0 "write -P 0x22 0 4k"' | run_qemu -drive file="$TEST_IMG" -snapshot | _filter_qemu_io -echo 'qemu-io ide0-hd0 "write -P 0x22 0 4k"' | run_qemu -drive file="$TEST_IMG",snapshot=on | _filter_qemu_io -echo 'qemu-io ide0-hd0 "write -P 0x22 0 4k"' | run_qemu -drive file.filename="$TEST_IMG",driver=qcow2,snapshot=on | _filter_qemu_io -echo 'qemu-io ide0-hd0 "write -P 0x22 0 4k"' | run_qemu -drive file.filename="$TEST_IMG",driver=qcow2 -snapshot | _filter_qemu_io -echo 'qemu-io ide0-hd0 "write -P 0x22 0 4k"' | run_qemu -drive file="file:$TEST_IMG" -snapshot | _filter_qemu_io -echo 'qemu-io ide0-hd0 "write -P 0x22 0 4k"' | run_qemu -drive file="file:$TEST_IMG",snapshot=on | _filter_qemu_io + +echo "qemu-io $device_id \"write -P 0x22 0 4k\"" | run_qemu -drive file="$TEST_IMG",if=none,id=$device_id -snapshot | _filter_qemu_io +echo "qemu-io $device_id \"write -P 0x22 0 4k\"" | run_qemu -drive file="$TEST_IMG",snapshot=on,if=none,id=$device_id | _filter_qemu_io +echo "qemu-io $device_id \"write -P 0x22 0 4k\"" | run_qemu -drive file.filename="$TEST_IMG",driver=qcow2,snapshot=on,if=none,id=$device_id\ + | _filter_qemu_io +echo "qemu-io $device_id \"write -P 0x22 0 4k\"" | run_qemu -drive file.filename="$TEST_IMG",driver=qcow2,if=none,id=$device_id -snapshot\ + | _filter_qemu_io +echo "qemu-io $device_id \"write -P 0x22 0 4k\"" | run_qemu -drive file="file:$TEST_IMG",if=none,id=$device_id -snapshot | _filter_qemu_io +echo "qemu-io $device_id \"write -P 0x22 0 4k\"" | run_qemu -drive file="file:$TEST_IMG",snapshot=on,if=none,id=$device_id | _filter_qemu_io # Opening a read-only file r/w with snapshot=on chmod u-w "$TEST_IMG" -echo 'qemu-io ide0-hd0 "write -P 0x22 0 4k"' | run_qemu -drive file="$TEST_IMG" -snapshot | _filter_qemu_io -echo 'qemu-io ide0-hd0 "write -P 0x22 0 4k"' | run_qemu -drive file="$TEST_IMG",snapshot=on | _filter_qemu_io +echo "qemu-io $device_id \"write -P 0x22 0 4k\"" | run_qemu -drive file="$TEST_IMG",if=none,id=$device_id -snapshot | _filter_qemu_io +echo "qemu-io $device_id \"write -P 0x22 0 4k\"" | run_qemu -drive file="$TEST_IMG",snapshot=on,if=none,id=$device_id | _filter_qemu_io chmod u+w "$TEST_IMG" $QEMU_IO -c "read -P 0x11 0 4k" "$TEST_IMG" | _filter_qemu_io -echo 'qemu-io ide0-hd0 "write -P 0x22 0 4k"' | run_qemu -drive file="$TEST_IMG",snapshot=off | _filter_qemu_io +echo "qemu-io $device_id \"write -P 0x22 0 4k\"" | run_qemu -drive file="$TEST_IMG",snapshot=off,if=none,id=$device_id | _filter_qemu_io $QEMU_IO -c "read -P 0x22 0 4k" "$TEST_IMG" | _filter_qemu_io -echo -e 'qemu-io ide0-hd0 "write -P 0x33 0 4k"\ncommit ide0-hd0' | run_qemu -drive file="$TEST_IMG",snapshot=on | _filter_qemu_io +echo -e "qemu-io $device_id \"write -P 0x33 0 4k\"\ncommit $device_id" | run_qemu -drive file="$TEST_IMG",snapshot=on,if=none,id=$device_id\ + | _filter_qemu_io $QEMU_IO -c "read -P 0x33 0 4k" "$TEST_IMG" | _filter_qemu_io diff --git a/qemu/tests/qemu-iotests/051.out b/qemu/tests/qemu-iotests/051.out index 23c282357..408d613bc 100644 --- a/qemu/tests/qemu-iotests/051.out +++ b/qemu/tests/qemu-iotests/051.out @@ -1,20 +1,20 @@ QA output created by 051 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base === Unknown option === -Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt= -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=,if=none,id=drive0 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=,if=none,id=drive0: Block format 'qcow2' does not support the option 'unknown_opt' -Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on,if=none,id=drive0 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on,if=none,id=drive0: Block format 'qcow2' does not support the option 'unknown_opt' -Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234 -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234,if=none,id=drive0 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234,if=none,id=drive0: Block format 'qcow2' does not support the option 'unknown_opt' -Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo,if=none,id=drive0 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo,if=none,id=drive0: Block format 'qcow2' does not support the option 'unknown_opt' === Unknown protocol option === @@ -56,10 +56,11 @@ QEMU X.Y.Z monitor - type 'help' for more information === Overriding backing file === -Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,backing.file.filename=TEST_DIR/t.qcow2.orig -nodefaults +Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,backing.file.filename=TEST_DIR/t.qcow2.orig,if=none,id=drive0 -nodefaults QEMU X.Y.Z monitor - type 'help' for more information (qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo block -ide0-hd0: TEST_DIR/t.qcow2 (qcow2) +drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) + Removable device: not locked, tray closed Cache mode: writeback Backing file: TEST_DIR/t.qcow2.orig (chain depth: 1) (qemu) qququiquit @@ -107,136 +108,105 @@ QEMU X.Y.Z monitor - type 'help' for more information === No medium === -Testing: -drive if=floppy -QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qququiquit - -Testing: -drive if=ide,media=cdrom -QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qququiquit - -Testing: -drive if=scsi,media=cdrom -QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qququiquit - -Testing: -drive if=ide -QEMU X.Y.Z monitor - type 'help' for more information -(qemu) QEMU_PROG: Device needs media, but drive is empty -QEMU_PROG: Initialization of device ide-hd failed: Device initialization failed. - Testing: -drive if=virtio QEMU X.Y.Z monitor - type 'help' for more information (qemu) QEMU_PROG: -drive if=virtio: Device needs media, but drive is empty -Testing: -drive if=scsi -QEMU X.Y.Z monitor - type 'help' for more information -(qemu) QEMU_PROG: Initialization of device lsi53c895a failed: Device needs media, but drive is empty -Testing: -drive if=none,id=disk -device ide-cd,drive=disk -QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qququiquit +=== Read-only === -Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-cd,drive=disk +Testing: -drive file=TEST_DIR/t.qcow2,if=virtio,readonly=on QEMU X.Y.Z monitor - type 'help' for more information (qemu) qququiquit -Testing: -drive if=none,id=disk -device ide-drive,drive=disk -QEMU X.Y.Z monitor - type 'help' for more information -(qemu) QEMU_PROG: -device ide-drive,drive=disk: Device needs media, but drive is empty -QEMU_PROG: -device ide-drive,drive=disk: Device initialization failed. - -Testing: -drive if=none,id=disk -device ide-hd,drive=disk -QEMU X.Y.Z monitor - type 'help' for more information -(qemu) QEMU_PROG: -device ide-hd,drive=disk: Device needs media, but drive is empty -QEMU_PROG: -device ide-hd,drive=disk: Device initialization failed. - -Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-disk,drive=disk -QEMU X.Y.Z monitor - type 'help' for more information -(qemu) QEMU_PROG: -device scsi-disk,drive=disk: Device needs media, but drive is empty - -Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-hd,drive=disk -QEMU X.Y.Z monitor - type 'help' for more information -(qemu) QEMU_PROG: -device scsi-hd,drive=disk: Device needs media, but drive is empty +=== Cache modes === -=== Read-only === - -Testing: -drive file=TEST_DIR/t.qcow2,if=floppy,readonly=on -QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qququiquit - -Testing: -drive file=TEST_DIR/t.qcow2,if=ide,media=cdrom,readonly=on +Testing: -drive driver=null-co,cache=none QEMU X.Y.Z monitor - type 'help' for more information (qemu) qququiquit -Testing: -drive file=TEST_DIR/t.qcow2,if=scsi,media=cdrom,readonly=on +Testing: -drive driver=null-co,cache=directsync QEMU X.Y.Z monitor - type 'help' for more information (qemu) qququiquit -Testing: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on -QEMU X.Y.Z monitor - type 'help' for more information -(qemu) QEMU_PROG: Can't use a read-only drive -QEMU_PROG: Initialization of device ide-hd failed: Device initialization failed. - -Testing: -drive file=TEST_DIR/t.qcow2,if=virtio,readonly=on +Testing: -drive driver=null-co,cache=writeback QEMU X.Y.Z monitor - type 'help' for more information (qemu) qququiquit -Testing: -drive file=TEST_DIR/t.qcow2,if=scsi,readonly=on +Testing: -drive driver=null-co,cache=writethrough QEMU X.Y.Z monitor - type 'help' for more information (qemu) qququiquit -Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-cd,drive=disk +Testing: -drive driver=null-co,cache=unsafe QEMU X.Y.Z monitor - type 'help' for more information (qemu) qququiquit -Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-cd,drive=disk -QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qququiquit +Testing: -drive driver=null-co,cache=invalid_value +QEMU_PROG: -drive driver=null-co,cache=invalid_value: invalid cache option -Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-drive,drive=disk +Testing: -drive file=TEST_DIR/t.qcow2,cache=writeback,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults QEMU X.Y.Z monitor - type 'help' for more information -(qemu) QEMU_PROG: -device ide-drive,drive=disk: Can't use a read-only drive -QEMU_PROG: -device ide-drive,drive=disk: Device initialization failed. +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo block +drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) + Removable device: not locked, tray closed + Cache mode: writeback + Backing file: TEST_DIR/t.qcow2.base (chain depth: 1) +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block info block finfo block fiinfo block filinfo block file -Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-hd,drive=disk -QEMU X.Y.Z monitor - type 'help' for more information -(qemu) QEMU_PROG: -device ide-hd,drive=disk: Can't use a read-only drive -QEMU_PROG: -device ide-hd,drive=disk: Device initialization failed. +file: TEST_DIR/t.qcow2 (file) + Cache mode: writeback +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block info block binfo block bainfo block bacinfo block backinfo block backiinfo block backininfo block backing +backing: TEST_DIR/t.qcow2.base (qcow2, read-only) + Cache mode: writeback, ignore flushes +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block info block binfo block bainfo block bacinfo block backinfo block backiinfo block backininfo block backinginfo block backing-info block backing-finfo block backing-fiinfo block backing-filinfo block backing-file -Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-disk,drive=disk -QEMU X.Y.Z monitor - type 'help' for more information +backing-file: TEST_DIR/t.qcow2.base (file, read-only) + Cache mode: writeback, ignore flushes (qemu) qququiquit -Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-hd,drive=disk +Testing: -drive file=TEST_DIR/t.qcow2,cache=writethrough,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qququiquit - - -=== Cache modes === +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo block +drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) + Removable device: not locked, tray closed + Cache mode: writethrough + Backing file: TEST_DIR/t.qcow2.base (chain depth: 1) +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block info block finfo block fiinfo block filinfo block file -Testing: -drive media=cdrom,cache=none -QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qququiquit +file: TEST_DIR/t.qcow2 (file) + Cache mode: writeback +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block info block binfo block bainfo block bacinfo block backinfo block backiinfo block backininfo block backing +backing: TEST_DIR/t.qcow2.base (qcow2, read-only) + Cache mode: writeback, ignore flushes +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block info block binfo block bainfo block bacinfo block backinfo block backiinfo block backininfo block backinginfo block backing-info block backing-finfo block backing-fiinfo block backing-filinfo block backing-file -Testing: -drive media=cdrom,cache=directsync -QEMU X.Y.Z monitor - type 'help' for more information +backing-file: TEST_DIR/t.qcow2.base (file, read-only) + Cache mode: writeback, ignore flushes (qemu) qququiquit -Testing: -drive media=cdrom,cache=writeback +Testing: -drive file=TEST_DIR/t.qcow2,cache=unsafe,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qququiquit +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo block +drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) + Removable device: not locked, tray closed + Cache mode: writeback, ignore flushes + Backing file: TEST_DIR/t.qcow2.base (chain depth: 1) +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block info block finfo block fiinfo block filinfo block file -Testing: -drive media=cdrom,cache=writethrough -QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qququiquit +file: TEST_DIR/t.qcow2 (file) + Cache mode: writeback, ignore flushes +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block info block binfo block bainfo block bacinfo block backinfo block backiinfo block backininfo block backing +backing: TEST_DIR/t.qcow2.base (qcow2, read-only) + Cache mode: writeback, ignore flushes +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block info block binfo block bainfo block bacinfo block backinfo block backiinfo block backininfo block backinginfo block backing-info block backing-finfo block backing-fiinfo block backing-filinfo block backing-file -Testing: -drive media=cdrom,cache=unsafe -QEMU X.Y.Z monitor - type 'help' for more information +backing-file: TEST_DIR/t.qcow2.base (file, read-only) + Cache mode: writeback, ignore flushes (qemu) qququiquit -Testing: -drive media=cdrom,cache=invalid_value -QEMU_PROG: -drive media=cdrom,cache=invalid_value: invalid cache option +Testing: -drive file=TEST_DIR/t.qcow2,cache=invalid_value,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,cache=invalid_value,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0: invalid cache option === Specifying the protocol layer === @@ -315,6 +285,45 @@ Testing: -drive file=TEST_DIR/t.qcow2,readonly=on,read-only=off QEMU_PROG: -drive file=TEST_DIR/t.qcow2,readonly=on,read-only=off: 'read-only' and its alias 'readonly' can't be used at the same time +=== Catching negative/large throttling values === + +Testing: -drive file=TEST_DIR/t.qcow2,iops=-1 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops=-1: bps/iops/max values must be within [0, 1000000000000000] + +Testing: -drive file=TEST_DIR/t.qcow2,bps=-2 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps=-2: bps/iops/max values must be within [0, 1000000000000000] + +Testing: -drive file=TEST_DIR/t.qcow2,bps_rd=-3 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps_rd=-3: bps/iops/max values must be within [0, 1000000000000000] + +Testing: -drive file=TEST_DIR/t.qcow2,bps_rd_max=-3 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps_rd_max=-3: bps/iops/max values must be within [0, 1000000000000000] + +Testing: -drive file=TEST_DIR/t.qcow2,throttling.iops-total=-4 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,throttling.iops-total=-4: bps/iops/max values must be within [0, 1000000000000000] + +Testing: -drive file=TEST_DIR/t.qcow2,throttling.bps-total=-5 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,throttling.bps-total=-5: bps/iops/max values must be within [0, 1000000000000000] + +Testing: -drive file=TEST_DIR/t.qcow2,bps=0 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,bps=1 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,bps=1000000000000000 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,bps=1000000000000001 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps=1000000000000001: bps/iops/max values must be within [0, 1000000000000000] + +Testing: -drive file=TEST_DIR/t.qcow2,bps=9999999999999999 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps=9999999999999999: bps/iops/max values must be within [0, 1000000000000000] + + === Parsing protocol from file name === Testing: -hda foo:bar @@ -342,79 +351,79 @@ QEMU_PROG: -drive file.filename=file:TEST_DIR/t.qcow2: Could not open 'file:TEST wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Testing: -drive file=TEST_DIR/t.qcow2 -snapshot +Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=drive0 -snapshot QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io iqemu-io idqemu-io ideqemu-io ide0qemu-io ide0-qemu-io ide0-hqemu-io ide0-hdqemu-io ide0-hd0qemu-io ide0-hd0 qemu-io ide0-hd0 "qemu-io ide0-hd0 "wqemu-io ide0-hd0 "wrqemu-io ide0-hd0 "wriqemu-io ide0-hd0 "writqemu-io ide0-hd0 "writeqemu-io ide0-hd0 "write qemu-io ide0-hd0 "write -qemu-io ide0-hd0 "write -Pqemu-io ide0-hd0 "write -P qemu-io ide0-hd0 "write -P 0qemu-io ide0-hd0 "write -P 0xqemu-io ide0-hd0 "write -P 0x2qemu-io ide0-hd0 "write -P 0x22qemu-io ide0-hd0 "write -P 0x22 qemu-io ide0-hd0 "write -P 0x22 0qemu-io ide0-hd0 "write -P 0x22 0 qemu-io ide0-hd0 "write -P 0x22 0 4qemu-io ide0-hd0 "write -P 0x22 0 4kqemu-io ide0-hd0 "write -P 0x22 0 4k" +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x2qemu-io drive0 "write -P 0x22qemu-io drive0 "write -P 0x22 qemu-io drive0 "write -P 0x22 0qemu-io drive0 "write -P 0x22 0 qemu-io drive0 "write -P 0x22 0 4qemu-io drive0 "write -P 0x22 0 4kqemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) (qemu) qququiquit -Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on +Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,if=none,id=drive0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io iqemu-io idqemu-io ideqemu-io ide0qemu-io ide0-qemu-io ide0-hqemu-io ide0-hdqemu-io ide0-hd0qemu-io ide0-hd0 qemu-io ide0-hd0 "qemu-io ide0-hd0 "wqemu-io ide0-hd0 "wrqemu-io ide0-hd0 "wriqemu-io ide0-hd0 "writqemu-io ide0-hd0 "writeqemu-io ide0-hd0 "write qemu-io ide0-hd0 "write -qemu-io ide0-hd0 "write -Pqemu-io ide0-hd0 "write -P qemu-io ide0-hd0 "write -P 0qemu-io ide0-hd0 "write -P 0xqemu-io ide0-hd0 "write -P 0x2qemu-io ide0-hd0 "write -P 0x22qemu-io ide0-hd0 "write -P 0x22 qemu-io ide0-hd0 "write -P 0x22 0qemu-io ide0-hd0 "write -P 0x22 0 qemu-io ide0-hd0 "write -P 0x22 0 4qemu-io ide0-hd0 "write -P 0x22 0 4kqemu-io ide0-hd0 "write -P 0x22 0 4k" +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x2qemu-io drive0 "write -P 0x22qemu-io drive0 "write -P 0x22 qemu-io drive0 "write -P 0x22 0qemu-io drive0 "write -P 0x22 0 qemu-io drive0 "write -P 0x22 0 4qemu-io drive0 "write -P 0x22 0 4kqemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) (qemu) qququiquit -Testing: -drive file.filename=TEST_DIR/t.qcow2,driver=qcow2,snapshot=on +Testing: -drive file.filename=TEST_DIR/t.qcow2,driver=qcow2,snapshot=on,if=none,id=drive0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io iqemu-io idqemu-io ideqemu-io ide0qemu-io ide0-qemu-io ide0-hqemu-io ide0-hdqemu-io ide0-hd0qemu-io ide0-hd0 qemu-io ide0-hd0 "qemu-io ide0-hd0 "wqemu-io ide0-hd0 "wrqemu-io ide0-hd0 "wriqemu-io ide0-hd0 "writqemu-io ide0-hd0 "writeqemu-io ide0-hd0 "write qemu-io ide0-hd0 "write -qemu-io ide0-hd0 "write -Pqemu-io ide0-hd0 "write -P qemu-io ide0-hd0 "write -P 0qemu-io ide0-hd0 "write -P 0xqemu-io ide0-hd0 "write -P 0x2qemu-io ide0-hd0 "write -P 0x22qemu-io ide0-hd0 "write -P 0x22 qemu-io ide0-hd0 "write -P 0x22 0qemu-io ide0-hd0 "write -P 0x22 0 qemu-io ide0-hd0 "write -P 0x22 0 4qemu-io ide0-hd0 "write -P 0x22 0 4kqemu-io ide0-hd0 "write -P 0x22 0 4k" +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x2qemu-io drive0 "write -P 0x22qemu-io drive0 "write -P 0x22 qemu-io drive0 "write -P 0x22 0qemu-io drive0 "write -P 0x22 0 qemu-io drive0 "write -P 0x22 0 4qemu-io drive0 "write -P 0x22 0 4kqemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) (qemu) qququiquit -Testing: -drive file.filename=TEST_DIR/t.qcow2,driver=qcow2 -snapshot +Testing: -drive file.filename=TEST_DIR/t.qcow2,driver=qcow2,if=none,id=drive0 -snapshot QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io iqemu-io idqemu-io ideqemu-io ide0qemu-io ide0-qemu-io ide0-hqemu-io ide0-hdqemu-io ide0-hd0qemu-io ide0-hd0 qemu-io ide0-hd0 "qemu-io ide0-hd0 "wqemu-io ide0-hd0 "wrqemu-io ide0-hd0 "wriqemu-io ide0-hd0 "writqemu-io ide0-hd0 "writeqemu-io ide0-hd0 "write qemu-io ide0-hd0 "write -qemu-io ide0-hd0 "write -Pqemu-io ide0-hd0 "write -P qemu-io ide0-hd0 "write -P 0qemu-io ide0-hd0 "write -P 0xqemu-io ide0-hd0 "write -P 0x2qemu-io ide0-hd0 "write -P 0x22qemu-io ide0-hd0 "write -P 0x22 qemu-io ide0-hd0 "write -P 0x22 0qemu-io ide0-hd0 "write -P 0x22 0 qemu-io ide0-hd0 "write -P 0x22 0 4qemu-io ide0-hd0 "write -P 0x22 0 4kqemu-io ide0-hd0 "write -P 0x22 0 4k" +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x2qemu-io drive0 "write -P 0x22qemu-io drive0 "write -P 0x22 qemu-io drive0 "write -P 0x22 0qemu-io drive0 "write -P 0x22 0 qemu-io drive0 "write -P 0x22 0 4qemu-io drive0 "write -P 0x22 0 4kqemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) (qemu) qququiquit -Testing: -drive file=file:TEST_DIR/t.qcow2 -snapshot +Testing: -drive file=file:TEST_DIR/t.qcow2,if=none,id=drive0 -snapshot QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io iqemu-io idqemu-io ideqemu-io ide0qemu-io ide0-qemu-io ide0-hqemu-io ide0-hdqemu-io ide0-hd0qemu-io ide0-hd0 qemu-io ide0-hd0 "qemu-io ide0-hd0 "wqemu-io ide0-hd0 "wrqemu-io ide0-hd0 "wriqemu-io ide0-hd0 "writqemu-io ide0-hd0 "writeqemu-io ide0-hd0 "write qemu-io ide0-hd0 "write -qemu-io ide0-hd0 "write -Pqemu-io ide0-hd0 "write -P qemu-io ide0-hd0 "write -P 0qemu-io ide0-hd0 "write -P 0xqemu-io ide0-hd0 "write -P 0x2qemu-io ide0-hd0 "write -P 0x22qemu-io ide0-hd0 "write -P 0x22 qemu-io ide0-hd0 "write -P 0x22 0qemu-io ide0-hd0 "write -P 0x22 0 qemu-io ide0-hd0 "write -P 0x22 0 4qemu-io ide0-hd0 "write -P 0x22 0 4kqemu-io ide0-hd0 "write -P 0x22 0 4k" +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x2qemu-io drive0 "write -P 0x22qemu-io drive0 "write -P 0x22 qemu-io drive0 "write -P 0x22 0qemu-io drive0 "write -P 0x22 0 qemu-io drive0 "write -P 0x22 0 4qemu-io drive0 "write -P 0x22 0 4kqemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) (qemu) qququiquit -Testing: -drive file=file:TEST_DIR/t.qcow2,snapshot=on +Testing: -drive file=file:TEST_DIR/t.qcow2,snapshot=on,if=none,id=drive0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io iqemu-io idqemu-io ideqemu-io ide0qemu-io ide0-qemu-io ide0-hqemu-io ide0-hdqemu-io ide0-hd0qemu-io ide0-hd0 qemu-io ide0-hd0 "qemu-io ide0-hd0 "wqemu-io ide0-hd0 "wrqemu-io ide0-hd0 "wriqemu-io ide0-hd0 "writqemu-io ide0-hd0 "writeqemu-io ide0-hd0 "write qemu-io ide0-hd0 "write -qemu-io ide0-hd0 "write -Pqemu-io ide0-hd0 "write -P qemu-io ide0-hd0 "write -P 0qemu-io ide0-hd0 "write -P 0xqemu-io ide0-hd0 "write -P 0x2qemu-io ide0-hd0 "write -P 0x22qemu-io ide0-hd0 "write -P 0x22 qemu-io ide0-hd0 "write -P 0x22 0qemu-io ide0-hd0 "write -P 0x22 0 qemu-io ide0-hd0 "write -P 0x22 0 4qemu-io ide0-hd0 "write -P 0x22 0 4kqemu-io ide0-hd0 "write -P 0x22 0 4k" +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x2qemu-io drive0 "write -P 0x22qemu-io drive0 "write -P 0x22 qemu-io drive0 "write -P 0x22 0qemu-io drive0 "write -P 0x22 0 qemu-io drive0 "write -P 0x22 0 4qemu-io drive0 "write -P 0x22 0 4kqemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) (qemu) qququiquit -Testing: -drive file=TEST_DIR/t.qcow2 -snapshot +Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=drive0 -snapshot QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io iqemu-io idqemu-io ideqemu-io ide0qemu-io ide0-qemu-io ide0-hqemu-io ide0-hdqemu-io ide0-hd0qemu-io ide0-hd0 qemu-io ide0-hd0 "qemu-io ide0-hd0 "wqemu-io ide0-hd0 "wrqemu-io ide0-hd0 "wriqemu-io ide0-hd0 "writqemu-io ide0-hd0 "writeqemu-io ide0-hd0 "write qemu-io ide0-hd0 "write -qemu-io ide0-hd0 "write -Pqemu-io ide0-hd0 "write -P qemu-io ide0-hd0 "write -P 0qemu-io ide0-hd0 "write -P 0xqemu-io ide0-hd0 "write -P 0x2qemu-io ide0-hd0 "write -P 0x22qemu-io ide0-hd0 "write -P 0x22 qemu-io ide0-hd0 "write -P 0x22 0qemu-io ide0-hd0 "write -P 0x22 0 qemu-io ide0-hd0 "write -P 0x22 0 4qemu-io ide0-hd0 "write -P 0x22 0 4kqemu-io ide0-hd0 "write -P 0x22 0 4k" +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x2qemu-io drive0 "write -P 0x22qemu-io drive0 "write -P 0x22 qemu-io drive0 "write -P 0x22 0qemu-io drive0 "write -P 0x22 0 qemu-io drive0 "write -P 0x22 0 4qemu-io drive0 "write -P 0x22 0 4kqemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) (qemu) qququiquit -Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on +Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,if=none,id=drive0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io iqemu-io idqemu-io ideqemu-io ide0qemu-io ide0-qemu-io ide0-hqemu-io ide0-hdqemu-io ide0-hd0qemu-io ide0-hd0 qemu-io ide0-hd0 "qemu-io ide0-hd0 "wqemu-io ide0-hd0 "wrqemu-io ide0-hd0 "wriqemu-io ide0-hd0 "writqemu-io ide0-hd0 "writeqemu-io ide0-hd0 "write qemu-io ide0-hd0 "write -qemu-io ide0-hd0 "write -Pqemu-io ide0-hd0 "write -P qemu-io ide0-hd0 "write -P 0qemu-io ide0-hd0 "write -P 0xqemu-io ide0-hd0 "write -P 0x2qemu-io ide0-hd0 "write -P 0x22qemu-io ide0-hd0 "write -P 0x22 qemu-io ide0-hd0 "write -P 0x22 0qemu-io ide0-hd0 "write -P 0x22 0 qemu-io ide0-hd0 "write -P 0x22 0 4qemu-io ide0-hd0 "write -P 0x22 0 4kqemu-io ide0-hd0 "write -P 0x22 0 4k" +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x2qemu-io drive0 "write -P 0x22qemu-io drive0 "write -P 0x22 qemu-io drive0 "write -P 0x22 0qemu-io drive0 "write -P 0x22 0 qemu-io drive0 "write -P 0x22 0 4qemu-io drive0 "write -P 0x22 0 4kqemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) (qemu) qququiquit read 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Testing: -drive file=TEST_DIR/t.qcow2,snapshot=off +Testing: -drive file=TEST_DIR/t.qcow2,snapshot=off,if=none,id=drive0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io iqemu-io idqemu-io ideqemu-io ide0qemu-io ide0-qemu-io ide0-hqemu-io ide0-hdqemu-io ide0-hd0qemu-io ide0-hd0 qemu-io ide0-hd0 "qemu-io ide0-hd0 "wqemu-io ide0-hd0 "wrqemu-io ide0-hd0 "wriqemu-io ide0-hd0 "writqemu-io ide0-hd0 "writeqemu-io ide0-hd0 "write qemu-io ide0-hd0 "write -qemu-io ide0-hd0 "write -Pqemu-io ide0-hd0 "write -P qemu-io ide0-hd0 "write -P 0qemu-io ide0-hd0 "write -P 0xqemu-io ide0-hd0 "write -P 0x2qemu-io ide0-hd0 "write -P 0x22qemu-io ide0-hd0 "write -P 0x22 qemu-io ide0-hd0 "write -P 0x22 0qemu-io ide0-hd0 "write -P 0x22 0 qemu-io ide0-hd0 "write -P 0x22 0 4qemu-io ide0-hd0 "write -P 0x22 0 4kqemu-io ide0-hd0 "write -P 0x22 0 4k" +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x2qemu-io drive0 "write -P 0x22qemu-io drive0 "write -P 0x22 qemu-io drive0 "write -P 0x22 0qemu-io drive0 "write -P 0x22 0 qemu-io drive0 "write -P 0x22 0 4qemu-io drive0 "write -P 0x22 0 4kqemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) (qemu) qququiquit read 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on +Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,if=none,id=drive0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io iqemu-io idqemu-io ideqemu-io ide0qemu-io ide0-qemu-io ide0-hqemu-io ide0-hdqemu-io ide0-hd0qemu-io ide0-hd0 qemu-io ide0-hd0 "qemu-io ide0-hd0 "wqemu-io ide0-hd0 "wrqemu-io ide0-hd0 "wriqemu-io ide0-hd0 "writqemu-io ide0-hd0 "writeqemu-io ide0-hd0 "write qemu-io ide0-hd0 "write -qemu-io ide0-hd0 "write -Pqemu-io ide0-hd0 "write -P qemu-io ide0-hd0 "write -P 0qemu-io ide0-hd0 "write -P 0xqemu-io ide0-hd0 "write -P 0x3qemu-io ide0-hd0 "write -P 0x33qemu-io ide0-hd0 "write -P 0x33 qemu-io ide0-hd0 "write -P 0x33 0qemu-io ide0-hd0 "write -P 0x33 0 qemu-io ide0-hd0 "write -P 0x33 0 4qemu-io ide0-hd0 "write -P 0x33 0 4kqemu-io ide0-hd0 "write -P 0x33 0 4k" +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x3qemu-io drive0 "write -P 0x33qemu-io drive0 "write -P 0x33 qemu-io drive0 "write -P 0x33 0qemu-io drive0 "write -P 0x33 0 qemu-io drive0 "write -P 0x33 0 4qemu-io drive0 "write -P 0x33 0 4kqemu-io drive0 "write -P 0x33 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) ccocomcommcommicommitcommit commit icommit idcommit idecommit ide0commit ide0-commit ide0-hcommit ide0-hdcommit ide0-hd0 +(qemu) ccocomcommcommicommitcommit commit dcommit drcommit dricommit drivcommit drivecommit drive0 (qemu) qququiquit read 4096/4096 bytes at offset 0 diff --git a/qemu/tests/qemu-iotests/051.pc.out b/qemu/tests/qemu-iotests/051.pc.out new file mode 100644 index 000000000..ec6d22229 --- /dev/null +++ b/qemu/tests/qemu-iotests/051.pc.out @@ -0,0 +1,525 @@ +QA output created by 051 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base + +=== Unknown option === + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=,if=none,id=drive0 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=,if=none,id=drive0: Block format 'qcow2' does not support the option 'unknown_opt' + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on,if=none,id=drive0 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on,if=none,id=drive0: Block format 'qcow2' does not support the option 'unknown_opt' + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234,if=none,id=drive0 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234,if=none,id=drive0: Block format 'qcow2' does not support the option 'unknown_opt' + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo,if=none,id=drive0 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo,if=none,id=drive0: Block format 'qcow2' does not support the option 'unknown_opt' + + +=== Unknown protocol option === + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt= +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=: Block protocol 'file' doesn't support the option 'unknown_opt' + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=on +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=on: Block protocol 'file' doesn't support the option 'unknown_opt' + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=1234 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=1234: Block protocol 'file' doesn't support the option 'unknown_opt' + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=foo +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=foo: Block protocol 'file' doesn't support the option 'unknown_opt' + + +=== Invalid format === + +Testing: -drive file=TEST_DIR/t.qcow2,format=foo +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=foo: Unknown driver 'foo' + +Testing: -drive file=TEST_DIR/t.qcow2,driver=foo +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=foo: Unknown driver 'foo' + +Testing: -drive file=TEST_DIR/t.qcow2,driver=raw,format=qcow2 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=raw,format=qcow2: Cannot specify both 'driver' and 'format' + +Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2: Cannot specify both 'driver' and 'format' + + +=== Device without drive === + +Testing: -device virtio-scsi-pci -device scsi-hd +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) QEMU_PROG: -device scsi-hd: drive property not set + + +=== Overriding backing file === + +Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,backing.file.filename=TEST_DIR/t.qcow2.orig,if=none,id=drive0 -nodefaults +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo block +drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) + Removable device: not locked, tray closed + Cache mode: writeback + Backing file: TEST_DIR/t.qcow2.orig (chain depth: 1) +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,driver=raw,backing.file.filename=TEST_DIR/t.qcow2.orig +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=raw,backing.file.filename=TEST_DIR/t.qcow2.orig: Driver doesn't support backing files + +Testing: -drive file=TEST_DIR/t.qcow2,file.backing.driver=file,file.backing.filename=TEST_DIR/t.qcow2.orig +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.backing.driver=file,file.backing.filename=TEST_DIR/t.qcow2.orig: Driver doesn't support backing files + +Testing: -drive file=TEST_DIR/t.qcow2,file.backing.driver=qcow2,file.backing.file.filename=TEST_DIR/t.qcow2.orig +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.backing.driver=qcow2,file.backing.file.filename=TEST_DIR/t.qcow2.orig: Driver doesn't support backing files + + +=== Enable and disable lazy refcounting on the command line, plus some invalid values === + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=off +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts= +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=: Parameter 'lazy-refcounts' expects 'on' or 'off' + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42: Parameter 'lazy-refcounts' expects 'on' or 'off' + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo: Parameter 'lazy-refcounts' expects 'on' or 'off' + + +=== With version 2 images enabling lazy refcounts must fail === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on: Lazy refcounts require a qcow2 image with at least qemu 1.1 compatibility level + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=off +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + + +=== No medium === + +Testing: -drive if=floppy +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive if=ide,media=cdrom +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive if=scsi,media=cdrom +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive if=ide +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) QEMU_PROG: Device needs media, but drive is empty +QEMU_PROG: Initialization of device ide-hd failed: Device initialization failed. + +Testing: -drive if=scsi +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) QEMU_PROG: Initialization of device lsi53c895a failed: Device needs media, but drive is empty + +Testing: -drive if=virtio +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) QEMU_PROG: -drive if=virtio: Device needs media, but drive is empty + +Testing: -drive if=none,id=disk -device ide-cd,drive=disk +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-cd,drive=disk +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive if=none,id=disk -device ide-drive,drive=disk +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) QEMU_PROG: -device ide-drive,drive=disk: Device needs media, but drive is empty +QEMU_PROG: -device ide-drive,drive=disk: Device initialization failed. + +Testing: -drive if=none,id=disk -device ide-hd,drive=disk +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) QEMU_PROG: -device ide-hd,drive=disk: Device needs media, but drive is empty +QEMU_PROG: -device ide-hd,drive=disk: Device initialization failed. + +Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-disk,drive=disk +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) QEMU_PROG: -device scsi-disk,drive=disk: Device needs media, but drive is empty + +Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-hd,drive=disk +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) QEMU_PROG: -device scsi-hd,drive=disk: Device needs media, but drive is empty + + +=== Read-only === + +Testing: -drive file=TEST_DIR/t.qcow2,if=floppy,readonly=on +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,if=ide,media=cdrom,readonly=on +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,if=scsi,media=cdrom,readonly=on +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) QEMU_PROG: Can't use a read-only drive +QEMU_PROG: Initialization of device ide-hd failed: Device initialization failed. + +Testing: -drive file=TEST_DIR/t.qcow2,if=scsi,readonly=on +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,if=virtio,readonly=on +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-cd,drive=disk +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-cd,drive=disk +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-drive,drive=disk +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) QEMU_PROG: -device ide-drive,drive=disk: Can't use a read-only drive +QEMU_PROG: -device ide-drive,drive=disk: Device initialization failed. + +Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-hd,drive=disk +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) QEMU_PROG: -device ide-hd,drive=disk: Can't use a read-only drive +QEMU_PROG: -device ide-hd,drive=disk: Device initialization failed. + +Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-disk,drive=disk +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-hd,drive=disk +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + + +=== Cache modes === + +Testing: -drive driver=null-co,cache=none +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive driver=null-co,cache=directsync +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive driver=null-co,cache=writeback +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive driver=null-co,cache=writethrough +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive driver=null-co,cache=unsafe +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive driver=null-co,cache=invalid_value +QEMU_PROG: -drive driver=null-co,cache=invalid_value: invalid cache option + +Testing: -drive file=TEST_DIR/t.qcow2,cache=writeback,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo block +drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) + Removable device: not locked, tray closed + Cache mode: writeback + Backing file: TEST_DIR/t.qcow2.base (chain depth: 1) +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block info block finfo block fiinfo block filinfo block file + +file: TEST_DIR/t.qcow2 (file) + Cache mode: writeback +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block info block binfo block bainfo block bacinfo block backinfo block backiinfo block backininfo block backing +backing: TEST_DIR/t.qcow2.base (qcow2, read-only) + Cache mode: writeback, ignore flushes +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block info block binfo block bainfo block bacinfo block backinfo block backiinfo block backininfo block backinginfo block backing-info block backing-finfo block backing-fiinfo block backing-filinfo block backing-file + +backing-file: TEST_DIR/t.qcow2.base (file, read-only) + Cache mode: writeback, ignore flushes +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,cache=writethrough,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo block +drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) + Removable device: not locked, tray closed + Cache mode: writethrough + Backing file: TEST_DIR/t.qcow2.base (chain depth: 1) +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block info block finfo block fiinfo block filinfo block file + +file: TEST_DIR/t.qcow2 (file) + Cache mode: writeback +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block info block binfo block bainfo block bacinfo block backinfo block backiinfo block backininfo block backing +backing: TEST_DIR/t.qcow2.base (qcow2, read-only) + Cache mode: writeback, ignore flushes +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block info block binfo block bainfo block bacinfo block backinfo block backiinfo block backininfo block backinginfo block backing-info block backing-finfo block backing-fiinfo block backing-filinfo block backing-file + +backing-file: TEST_DIR/t.qcow2.base (file, read-only) + Cache mode: writeback, ignore flushes +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,cache=unsafe,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo block +drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) + Removable device: not locked, tray closed + Cache mode: writeback, ignore flushes + Backing file: TEST_DIR/t.qcow2.base (chain depth: 1) +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block info block finfo block fiinfo block filinfo block file + +file: TEST_DIR/t.qcow2 (file) + Cache mode: writeback, ignore flushes +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block info block binfo block bainfo block bacinfo block backinfo block backiinfo block backininfo block backing +backing: TEST_DIR/t.qcow2.base (qcow2, read-only) + Cache mode: writeback, ignore flushes +(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block info block binfo block bainfo block bacinfo block backinfo block backiinfo block backininfo block backinginfo block backing-info block backing-finfo block backing-fiinfo block backing-filinfo block backing-file + +backing-file: TEST_DIR/t.qcow2.base (file, read-only) + Cache mode: writeback, ignore flushes +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,cache=invalid_value,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,cache=invalid_value,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0: invalid cache option + + +=== Specifying the protocol layer === + +Testing: -drive file=TEST_DIR/t.qcow2,file.driver=file +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + + +=== Leaving out required options === + +Testing: -drive driver=file +QEMU_PROG: -drive driver=file: The 'file' block driver requires a file name + +Testing: -drive driver=nbd +QEMU_PROG: -drive driver=nbd: one of path and host must be specified. + +Testing: -drive driver=raw +QEMU_PROG: -drive driver=raw: Can't use 'raw' as a block driver for the protocol level + +Testing: -drive file.driver=file +QEMU_PROG: -drive file.driver=file: The 'file' block driver requires a file name + +Testing: -drive file.driver=nbd +QEMU_PROG: -drive file.driver=nbd: one of path and host must be specified. + +Testing: -drive file.driver=raw +QEMU_PROG: -drive file.driver=raw: Can't use 'raw' as a block driver for the protocol level + +Testing: -drive foo=bar +QEMU_PROG: -drive foo=bar: Must specify either driver or file + + +=== Specifying both an option and its legacy alias === + +Testing: -drive file=TEST_DIR/t.qcow2,iops=1234,throttling.iops-total=5678 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops=1234,throttling.iops-total=5678: 'throttling.iops-total' and its alias 'iops' can't be used at the same time + +Testing: -drive file=TEST_DIR/t.qcow2,iops_rd=1234,throttling.iops-read=5678 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops_rd=1234,throttling.iops-read=5678: 'throttling.iops-read' and its alias 'iops_rd' can't be used at the same time + +Testing: -drive file=TEST_DIR/t.qcow2,iops_wr=1234,throttling.iops-write=5678 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops_wr=1234,throttling.iops-write=5678: 'throttling.iops-write' and its alias 'iops_wr' can't be used at the same time + +Testing: -drive file=TEST_DIR/t.qcow2,bps=1234,throttling.bps-total=5678 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps=1234,throttling.bps-total=5678: 'throttling.bps-total' and its alias 'bps' can't be used at the same time + +Testing: -drive file=TEST_DIR/t.qcow2,bps_rd=1234,throttling.bps-read=5678 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps_rd=1234,throttling.bps-read=5678: 'throttling.bps-read' and its alias 'bps_rd' can't be used at the same time + +Testing: -drive file=TEST_DIR/t.qcow2,bps_wr=1234,throttling.bps-write=5678 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps_wr=1234,throttling.bps-write=5678: 'throttling.bps-write' and its alias 'bps_wr' can't be used at the same time + +Testing: -drive file=TEST_DIR/t.qcow2,iops_max=1234,throttling.iops-total-max=5678 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops_max=1234,throttling.iops-total-max=5678: 'throttling.iops-total-max' and its alias 'iops_max' can't be used at the same time + +Testing: -drive file=TEST_DIR/t.qcow2,iops_rd_max=1234,throttling.iops-read-max=5678 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops_rd_max=1234,throttling.iops-read-max=5678: 'throttling.iops-read-max' and its alias 'iops_rd_max' can't be used at the same time + +Testing: -drive file=TEST_DIR/t.qcow2,iops_wr_max=1234,throttling.iops-write-max=5678 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops_wr_max=1234,throttling.iops-write-max=5678: 'throttling.iops-write-max' and its alias 'iops_wr_max' can't be used at the same time + +Testing: -drive file=TEST_DIR/t.qcow2,bps_max=1234,throttling.bps-total-max=5678 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps_max=1234,throttling.bps-total-max=5678: 'throttling.bps-total-max' and its alias 'bps_max' can't be used at the same time + +Testing: -drive file=TEST_DIR/t.qcow2,bps_rd_max=1234,throttling.bps-read-max=5678 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps_rd_max=1234,throttling.bps-read-max=5678: 'throttling.bps-read-max' and its alias 'bps_rd_max' can't be used at the same time + +Testing: -drive file=TEST_DIR/t.qcow2,bps_wr_max=1234,throttling.bps-write-max=5678 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps_wr_max=1234,throttling.bps-write-max=5678: 'throttling.bps-write-max' and its alias 'bps_wr_max' can't be used at the same time + +Testing: -drive file=TEST_DIR/t.qcow2,iops_size=1234,throttling.iops-size=5678 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops_size=1234,throttling.iops-size=5678: 'throttling.iops-size' and its alias 'iops_size' can't be used at the same time + +Testing: -drive file=TEST_DIR/t.qcow2,readonly=on,read-only=off +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,readonly=on,read-only=off: 'read-only' and its alias 'readonly' can't be used at the same time + + +=== Catching negative/large throttling values === + +Testing: -drive file=TEST_DIR/t.qcow2,iops=-1 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops=-1: bps/iops/max values must be within [0, 1000000000000000] + +Testing: -drive file=TEST_DIR/t.qcow2,bps=-2 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps=-2: bps/iops/max values must be within [0, 1000000000000000] + +Testing: -drive file=TEST_DIR/t.qcow2,bps_rd=-3 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps_rd=-3: bps/iops/max values must be within [0, 1000000000000000] + +Testing: -drive file=TEST_DIR/t.qcow2,bps_rd_max=-3 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps_rd_max=-3: bps/iops/max values must be within [0, 1000000000000000] + +Testing: -drive file=TEST_DIR/t.qcow2,throttling.iops-total=-4 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,throttling.iops-total=-4: bps/iops/max values must be within [0, 1000000000000000] + +Testing: -drive file=TEST_DIR/t.qcow2,throttling.bps-total=-5 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,throttling.bps-total=-5: bps/iops/max values must be within [0, 1000000000000000] + +Testing: -drive file=TEST_DIR/t.qcow2,bps=0 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,bps=1 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,bps=1000000000000000 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,bps=1000000000000001 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps=1000000000000001: bps/iops/max values must be within [0, 1000000000000000] + +Testing: -drive file=TEST_DIR/t.qcow2,bps=9999999999999999 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps=9999999999999999: bps/iops/max values must be within [0, 1000000000000000] + + +=== Parsing protocol from file name === + +Testing: -hda foo:bar +QEMU_PROG: -hda foo:bar: Unknown protocol 'foo' + +Testing: -drive file=foo:bar +QEMU_PROG: -drive file=foo:bar: Unknown protocol 'foo' + +Testing: -drive file.filename=foo:bar +QEMU_PROG: -drive file.filename=foo:bar: Could not open 'foo:bar': No such file or directory + +Testing: -hda file:TEST_DIR/t.qcow2 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=file:TEST_DIR/t.qcow2 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file.filename=file:TEST_DIR/t.qcow2 +QEMU_PROG: -drive file.filename=file:TEST_DIR/t.qcow2: Could not open 'file:TEST_DIR/t.qcow2': No such file or directory + + +=== Snapshot mode === + +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=drive0 -snapshot +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x2qemu-io drive0 "write -P 0x22qemu-io drive0 "write -P 0x22 qemu-io drive0 "write -P 0x22 0qemu-io drive0 "write -P 0x22 0 qemu-io drive0 "write -P 0x22 0 4qemu-io drive0 "write -P 0x22 0 4kqemu-io drive0 "write -P 0x22 0 4k" +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,if=none,id=drive0 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x2qemu-io drive0 "write -P 0x22qemu-io drive0 "write -P 0x22 qemu-io drive0 "write -P 0x22 0qemu-io drive0 "write -P 0x22 0 qemu-io drive0 "write -P 0x22 0 4qemu-io drive0 "write -P 0x22 0 4kqemu-io drive0 "write -P 0x22 0 4k" +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) qququiquit + +Testing: -drive file.filename=TEST_DIR/t.qcow2,driver=qcow2,snapshot=on,if=none,id=drive0 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x2qemu-io drive0 "write -P 0x22qemu-io drive0 "write -P 0x22 qemu-io drive0 "write -P 0x22 0qemu-io drive0 "write -P 0x22 0 qemu-io drive0 "write -P 0x22 0 4qemu-io drive0 "write -P 0x22 0 4kqemu-io drive0 "write -P 0x22 0 4k" +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) qququiquit + +Testing: -drive file.filename=TEST_DIR/t.qcow2,driver=qcow2,if=none,id=drive0 -snapshot +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x2qemu-io drive0 "write -P 0x22qemu-io drive0 "write -P 0x22 qemu-io drive0 "write -P 0x22 0qemu-io drive0 "write -P 0x22 0 qemu-io drive0 "write -P 0x22 0 4qemu-io drive0 "write -P 0x22 0 4kqemu-io drive0 "write -P 0x22 0 4k" +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) qququiquit + +Testing: -drive file=file:TEST_DIR/t.qcow2,if=none,id=drive0 -snapshot +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x2qemu-io drive0 "write -P 0x22qemu-io drive0 "write -P 0x22 qemu-io drive0 "write -P 0x22 0qemu-io drive0 "write -P 0x22 0 qemu-io drive0 "write -P 0x22 0 4qemu-io drive0 "write -P 0x22 0 4kqemu-io drive0 "write -P 0x22 0 4k" +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) qququiquit + +Testing: -drive file=file:TEST_DIR/t.qcow2,snapshot=on,if=none,id=drive0 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x2qemu-io drive0 "write -P 0x22qemu-io drive0 "write -P 0x22 qemu-io drive0 "write -P 0x22 0qemu-io drive0 "write -P 0x22 0 qemu-io drive0 "write -P 0x22 0 4qemu-io drive0 "write -P 0x22 0 4kqemu-io drive0 "write -P 0x22 0 4k" +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=drive0 -snapshot +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x2qemu-io drive0 "write -P 0x22qemu-io drive0 "write -P 0x22 qemu-io drive0 "write -P 0x22 0qemu-io drive0 "write -P 0x22 0 qemu-io drive0 "write -P 0x22 0 4qemu-io drive0 "write -P 0x22 0 4kqemu-io drive0 "write -P 0x22 0 4k" +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,if=none,id=drive0 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x2qemu-io drive0 "write -P 0x22qemu-io drive0 "write -P 0x22 qemu-io drive0 "write -P 0x22 0qemu-io drive0 "write -P 0x22 0 qemu-io drive0 "write -P 0x22 0 4qemu-io drive0 "write -P 0x22 0 4kqemu-io drive0 "write -P 0x22 0 4k" +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) qququiquit + +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Testing: -drive file=TEST_DIR/t.qcow2,snapshot=off,if=none,id=drive0 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x2qemu-io drive0 "write -P 0x22qemu-io drive0 "write -P 0x22 qemu-io drive0 "write -P 0x22 0qemu-io drive0 "write -P 0x22 0 qemu-io drive0 "write -P 0x22 0 4qemu-io drive0 "write -P 0x22 0 4kqemu-io drive0 "write -P 0x22 0 4k" +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) qququiquit + +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,if=none,id=drive0 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io dqemu-io drqemu-io driqemu-io drivqemu-io driveqemu-io drive0qemu-io drive0 qemu-io drive0 "qemu-io drive0 "wqemu-io drive0 "wrqemu-io drive0 "wriqemu-io drive0 "writqemu-io drive0 "writeqemu-io drive0 "write qemu-io drive0 "write -qemu-io drive0 "write -Pqemu-io drive0 "write -P qemu-io drive0 "write -P 0qemu-io drive0 "write -P 0xqemu-io drive0 "write -P 0x3qemu-io drive0 "write -P 0x33qemu-io drive0 "write -P 0x33 qemu-io drive0 "write -P 0x33 0qemu-io drive0 "write -P 0x33 0 qemu-io drive0 "write -P 0x33 0 4qemu-io drive0 "write -P 0x33 0 4kqemu-io drive0 "write -P 0x33 0 4k" +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) ccocomcommcommicommitcommit commit dcommit drcommit dricommit drivcommit drivecommit drive0 +(qemu) qququiquit + +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +*** done diff --git a/qemu/tests/qemu-iotests/052 b/qemu/tests/qemu-iotests/052 index 61959e286..4b647242d 100755 --- a/qemu/tests/qemu-iotests/052 +++ b/qemu/tests/qemu-iotests/052 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/053 b/qemu/tests/qemu-iotests/053 index e589e5f12..2a04f5f55 100755 --- a/qemu/tests/qemu-iotests/053 +++ b/qemu/tests/qemu-iotests/053 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/054 b/qemu/tests/qemu-iotests/054 index bd94153d6..bf47ef9fa 100755 --- a/qemu/tests/qemu-iotests/054 +++ b/qemu/tests/qemu-iotests/054 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/055 b/qemu/tests/qemu-iotests/055 index 017a609f3..c8e357870 100755 --- a/qemu/tests/qemu-iotests/055 +++ b/qemu/tests/qemu-iotests/055 @@ -42,6 +42,8 @@ class TestSingleDrive(iotests.QMPTestCase): qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len)) self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img) + if iotests.qemu_default_machine == 'pc': + self.vm.add_drive(None, 'media=cdrom', 'ide') self.vm.launch() def tearDown(self): @@ -104,12 +106,18 @@ class TestSingleDrive(iotests.QMPTestCase): self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img) def test_medium_not_found(self): - result = self.vm.qmp('drive-backup', device='ide1-cd0', + if iotests.qemu_default_machine != 'pc': + return + + result = self.vm.qmp('drive-backup', device='drive2', # CD-ROM target=target_img, sync='full') self.assert_qmp(result, 'error/class', 'GenericError') def test_medium_not_found_blockdev_backup(self): - result = self.vm.qmp('blockdev-backup', device='ide1-cd0', + if iotests.qemu_default_machine != 'pc': + return + + result = self.vm.qmp('blockdev-backup', device='drive2', # CD-ROM target='drive1', sync='full') self.assert_qmp(result, 'error/class', 'GenericError') @@ -249,6 +257,8 @@ class TestSingleTransaction(iotests.QMPTestCase): qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len)) self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img) + if iotests.qemu_default_machine == 'pc': + self.vm.add_drive(None, 'media=cdrom', 'ide') self.vm.launch() def tearDown(self): @@ -323,9 +333,12 @@ class TestSingleTransaction(iotests.QMPTestCase): self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img) def do_test_medium_not_found(self, cmd, target): + if iotests.qemu_default_machine != 'pc': + return + result = self.vm.qmp('transaction', actions=[{ 'type': cmd, - 'data': { 'device': 'ide1-cd0', + 'data': { 'device': 'drive2', # CD-ROM 'target': target, 'sync': 'full' }, } diff --git a/qemu/tests/qemu-iotests/056 b/qemu/tests/qemu-iotests/056 index 54e4bd069..04f2c3c84 100755 --- a/qemu/tests/qemu-iotests/056 +++ b/qemu/tests/qemu-iotests/056 @@ -82,6 +82,31 @@ class TestSyncModesNoneAndTop(iotests.QMPTestCase): time.sleep(1) self.assertEqual(-1, qemu_io('-c', 'read -P0x41 0 512', target_img).find("verification failed")) +class TestBeforeWriteNotifier(iotests.QMPTestCase): + def setUp(self): + self.vm = iotests.VM().add_drive_raw("file=blkdebug::null-co://,id=drive0,align=65536,driver=blkdebug") + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + os.remove(target_img) + + def test_before_write_notifier(self): + self.vm.pause_drive("drive0") + result = self.vm.qmp('drive-backup', device='drive0', + sync='full', target=target_img, + format="file", speed=1) + self.assert_qmp(result, 'return', {}) + result = self.vm.qmp('block-job-pause', device="drive0") + self.assert_qmp(result, 'return', {}) + # Speed is low enough that this must be an uncopied range, which will + # trigger the before write notifier + self.vm.hmp_qemu_io('drive0', 'aio_write -P 1 512512 512') + self.vm.resume_drive("drive0") + result = self.vm.qmp('block-job-resume', device="drive0") + self.assert_qmp(result, 'return', {}) + event = self.cancel_and_wait() + self.assert_qmp(event, 'data/type', 'backup') if __name__ == '__main__': iotests.main(supported_fmts=['qcow2', 'qed']) diff --git a/qemu/tests/qemu-iotests/056.out b/qemu/tests/qemu-iotests/056.out index fbc63e62f..8d7e99670 100644 --- a/qemu/tests/qemu-iotests/056.out +++ b/qemu/tests/qemu-iotests/056.out @@ -1,5 +1,5 @@ -.. +... ---------------------------------------------------------------------- -Ran 2 tests +Ran 3 tests OK diff --git a/qemu/tests/qemu-iotests/058 b/qemu/tests/qemu-iotests/058 index f2bdd0bff..2253c6a6d 100755 --- a/qemu/tests/qemu-iotests/058 +++ b/qemu/tests/qemu-iotests/058 @@ -27,16 +27,21 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! nbd_unix_socket=$TEST_DIR/test_qemu_nbd_socket nbd_snapshot_img="nbd:unix:$nbd_unix_socket" +rm -f "${TEST_DIR}/qemu-nbd.pid" _cleanup_nbd() { - if [ -n "$NBD_SNAPSHOT_PID" ]; then - kill "$NBD_SNAPSHOT_PID" + local NBD_SNAPSHOT_PID + if [ -f "${TEST_DIR}/qemu-nbd.pid" ]; then + read NBD_SNAPSHOT_PID < "${TEST_DIR}/qemu-nbd.pid" + rm -f "${TEST_DIR}/qemu-nbd.pid" + if [ -n "$NBD_SNAPSHOT_PID" ]; then + kill "$NBD_SNAPSHOT_PID" + fi fi rm -f "$nbd_unix_socket" } @@ -60,7 +65,6 @@ _export_nbd_snapshot() { _cleanup_nbd $QEMU_NBD -v -t -k "$nbd_unix_socket" "$TEST_IMG" -l $1 & - NBD_SNAPSHOT_PID=$! _wait_for_nbd } @@ -68,7 +72,6 @@ _export_nbd_snapshot1() { _cleanup_nbd $QEMU_NBD -v -t -k "$nbd_unix_socket" "$TEST_IMG" -l snapshot.name=$1 & - NBD_SNAPSHOT_PID=$! _wait_for_nbd } diff --git a/qemu/tests/qemu-iotests/059 b/qemu/tests/qemu-iotests/059 index 0ded0c3da..6655aaf38 100755 --- a/qemu/tests/qemu-iotests/059 +++ b/qemu/tests/qemu-iotests/059 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -132,6 +131,16 @@ _img_info $QEMU_IO -c "write -P 0xa 900G 512" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "read -v 900G 1024" "$TEST_IMG" | _filter_qemu_io +echo +echo "=== Testing qemu-img map on extents ===" +for fmt in monolithicSparse twoGbMaxExtentSparse; do + IMGOPTS="subformat=$fmt" _make_test_img 31G + $QEMU_IO -c "write 65024 1k" "$TEST_IMG" | _filter_qemu_io + $QEMU_IO -c "write 2147483136 1k" "$TEST_IMG" | _filter_qemu_io + $QEMU_IO -c "write 5G 1k" "$TEST_IMG" | _filter_qemu_io + $QEMU_IMG map "$TEST_IMG" | _filter_testdir +done + echo echo "=== Testing afl image with a very large capacity ===" _use_sample_img afl9.vmdk.bz2 diff --git a/qemu/tests/qemu-iotests/059.out b/qemu/tests/qemu-iotests/059.out index 67e3cf57e..678adb437 100644 --- a/qemu/tests/qemu-iotests/059.out +++ b/qemu/tests/qemu-iotests/059.out @@ -2,31 +2,31 @@ QA output created by 059 === Testing invalid granularity === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -qemu-io: can't open device TEST_DIR/t.vmdk: Invalid granularity, image may be corrupt +can't open device TEST_DIR/t.vmdk: Invalid granularity, image may be corrupt no file open, try 'help open' === Testing too big L2 table size === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -qemu-io: can't open device TEST_DIR/t.vmdk: L2 table size too big +can't open device TEST_DIR/t.vmdk: L2 table size too big no file open, try 'help open' === Testing too big L1 table size === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -qemu-io: can't open device TEST_DIR/t.vmdk: L1 size too big +can't open device TEST_DIR/t.vmdk: L1 size too big no file open, try 'help open' === Testing monolithicFlat creation and opening === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2147483648 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2147483648 subformat=monolithicFlat image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 2.0G (2147483648 bytes) === Testing monolithicFlat with zeroed_grain === qemu-img: TEST_DIR/t.IMGFMT: Flat image can't enable zeroed grain -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2147483648 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2147483648 subformat=monolithicFlat === Testing big twoGbMaxExtentFlat === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824000 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824000 subformat=twoGbMaxExtentFlat image: TEST_DIR/t.vmdk file format: vmdk virtual size: 1.0T (1073741824000 bytes) @@ -2038,12 +2038,10 @@ Format specific information: format: FLAT === Testing malformed VMFS extent description line === -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Invalid extent lines: -RW 12582912 VMFS "dummy.IMGFMT" 1 - +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Invalid extent line: RW 12582912 VMFS "dummy.IMGFMT" 1 === Testing truncated sparse === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=107374182400 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=107374182400 subformat=monolithicSparse qemu-img: Could not open 'TEST_DIR/t.IMGFMT': File truncated, expecting at least 13172736 bytes === Converting to streamOptimized from image with small cluster size=== @@ -2054,8 +2052,8 @@ wrote 512/512 bytes at offset 10240 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) === Testing monolithicFlat with internally generated JSON file name === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -qemu-io: can't open: Cannot use relative extent paths with VMDK descriptor file 'json:{"image": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "driver": "blkdebug", "inject-error.0.event": "read_aio"}' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 subformat=monolithicFlat +can't open: Cannot use relative extent paths with VMDK descriptor file 'json:{"image": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "driver": "blkdebug", "inject-error.0.event": "read_aio"}' === Testing version 3 === image: TEST_DIR/iotest-version3.IMGFMT @@ -2264,7 +2262,7 @@ read 512/512 bytes at offset 64931328 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) === Testing 4TB monolithicFlat creation and IO === -Formatting 'TEST_DIR/iotest-version3.IMGFMT', fmt=IMGFMT size=4398046511104 +Formatting 'TEST_DIR/iotest-version3.IMGFMT', fmt=IMGFMT size=4398046511104 subformat=monolithicFlat image: TEST_DIR/iotest-version3.IMGFMT file format: IMGFMT virtual size: 4.0T (4398046511104 bytes) @@ -2337,6 +2335,31 @@ e1000003f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ read 1024/1024 bytes at offset 966367641600 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +=== Testing qemu-img map on extents === +Formatting 'TEST_DIR/iotest-version3.IMGFMT', fmt=IMGFMT size=33285996544 subformat=monolithicSparse +wrote 1024/1024 bytes at offset 65024 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1024/1024 bytes at offset 2147483136 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1024/1024 bytes at offset 5368709120 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Offset Length Mapped to File +0 0x20000 0x3f0000 TEST_DIR/iotest-version3.vmdk +0x7fff0000 0x20000 0x410000 TEST_DIR/iotest-version3.vmdk +0x140000000 0x10000 0x430000 TEST_DIR/iotest-version3.vmdk +Formatting 'TEST_DIR/iotest-version3.IMGFMT', fmt=IMGFMT size=33285996544 subformat=twoGbMaxExtentSparse +wrote 1024/1024 bytes at offset 65024 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1024/1024 bytes at offset 2147483136 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1024/1024 bytes at offset 5368709120 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Offset Length Mapped to File +0 0x20000 0x50000 TEST_DIR/iotest-version3-s001.vmdk +0x7fff0000 0x10000 0x70000 TEST_DIR/iotest-version3-s001.vmdk +0x80000000 0x10000 0x50000 TEST_DIR/iotest-version3-s002.vmdk +0x140000000 0x10000 0x50000 TEST_DIR/iotest-version3-s003.vmdk + === Testing afl image with a very large capacity === qemu-img: Can't get size of device 'image': File too large *** done diff --git a/qemu/tests/qemu-iotests/060 b/qemu/tests/qemu-iotests/060 index c81319c16..8e95c450e 100755 --- a/qemu/tests/qemu-iotests/060 +++ b/qemu/tests/qemu-iotests/060 @@ -25,7 +25,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/060.out b/qemu/tests/qemu-iotests/060.out index 751118951..5d40206ef 100644 --- a/qemu/tests/qemu-iotests/060.out +++ b/qemu/tests/qemu-iotests/060.out @@ -20,7 +20,7 @@ Format specific information: lazy refcounts: false refcount bits: 16 corrupt: true -qemu-io: can't open device TEST_DIR/t.IMGFMT: IMGFMT: Image is corrupt; cannot be opened read/write +can't open device TEST_DIR/t.IMGFMT: IMGFMT: Image is corrupt; cannot be opened read/write read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/qemu/tests/qemu-iotests/061 b/qemu/tests/qemu-iotests/061 index 8d37f8a65..f5678b10c 100755 --- a/qemu/tests/qemu-iotests/061 +++ b/qemu/tests/qemu-iotests/061 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -58,7 +57,8 @@ echo echo "=== Testing dirty version downgrade ===" echo IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M -$QEMU_IO -c "write -P 0x2a 0 128k" -c flush -c abort "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 0x2a 0 128k" -c flush \ + -c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 | _filter_qemu_io $PYTHON qcow2.py "$TEST_IMG" dump-header $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" $PYTHON qcow2.py "$TEST_IMG" dump-header @@ -91,7 +91,8 @@ echo echo "=== Testing dirty lazy_refcounts=off ===" echo IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M -$QEMU_IO -c "write -P 0x2a 0 128k" -c flush -c abort "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 0x2a 0 128k" -c flush \ + -c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 | _filter_qemu_io $PYTHON qcow2.py "$TEST_IMG" dump-header $QEMU_IMG amend -o "lazy_refcounts=off" "$TEST_IMG" $PYTHON qcow2.py "$TEST_IMG" dump-header diff --git a/qemu/tests/qemu-iotests/061.out b/qemu/tests/qemu-iotests/061.out index 5ec248f79..a03732e19 100644 --- a/qemu/tests/qemu-iotests/061.out +++ b/qemu/tests/qemu-iotests/061.out @@ -24,6 +24,11 @@ autoclear_features 0x0 refcount_order 4 header_length 104 +Header extension: +magic 0x6803f857 +length 144 +data + magic 0x514649fb version 2 backing_file_offset 0x0 @@ -43,11 +48,6 @@ autoclear_features 0x0 refcount_order 4 header_length 72 -Header extension: -magic 0x6803f857 -length 144 -data - read 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. @@ -57,6 +57,11 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then + exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"; +else + exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"; +fi ) magic 0x514649fb version 3 backing_file_offset 0x0 @@ -76,6 +81,11 @@ autoclear_features 0x0 refcount_order 4 header_length 104 +Header extension: +magic 0x6803f857 +length 144 +data + ERROR cluster 5 refcount=0 reference=1 ERROR cluster 6 refcount=0 reference=1 Rebuilding refcount structure @@ -100,11 +110,6 @@ autoclear_features 0x0 refcount_order 4 header_length 72 -Header extension: -magic 0x6803f857 -length 144 -data - read 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. @@ -131,6 +136,11 @@ autoclear_features 0x40000000000 refcount_order 4 header_length 104 +Header extension: +magic 0x6803f857 +length 144 +data + magic 0x514649fb version 2 backing_file_offset 0x0 @@ -150,11 +160,6 @@ autoclear_features 0x0 refcount_order 4 header_length 72 -Header extension: -magic 0x6803f857 -length 144 -data - No errors were found on the image. === Testing version upgrade and resize === @@ -214,6 +219,11 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then + exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"; +else + exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"; +fi ) magic 0x514649fb version 3 backing_file_offset 0x0 @@ -233,6 +243,11 @@ autoclear_features 0x0 refcount_order 4 header_length 104 +Header extension: +magic 0x6803f857 +length 144 +data + ERROR cluster 5 refcount=0 reference=1 ERROR cluster 6 refcount=0 reference=1 Rebuilding refcount structure @@ -281,18 +296,18 @@ No errors were found on the image. === Testing invalid configurations === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) +qemu-img: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) qemu-img: Error while amending options: Invalid argument -Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) +qemu-img: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) qemu-img: Error while amending options: Invalid argument -Unknown compatibility level 0.42. +qemu-img: Unknown compatibility level 0.42 qemu-img: Error while amending options: Invalid argument qemu-img: Invalid parameter 'foo' -Changing the cluster size is not supported. +qemu-img: Changing the cluster size is not supported qemu-img: Error while amending options: Operation not supported -Changing the encryption flag is not supported. +qemu-img: Changing the encryption flag is not supported qemu-img: Error while amending options: Operation not supported -Cannot change preallocation mode. +qemu-img: Cannot change preallocation mode qemu-img: Error while amending options: Operation not supported === Testing correct handling of unset value === @@ -300,7 +315,7 @@ qemu-img: Error while amending options: Operation not supported Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Should work: Should not work: -Changing the cluster size is not supported. +qemu-img: Changing the cluster size is not supported qemu-img: Error while amending options: Operation not supported === Testing zero expansion on inactive clusters === @@ -334,7 +349,7 @@ read 131072/131072 bytes at offset 0 Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base read 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 0 @@ -350,7 +365,7 @@ read 65536/65536 bytes at offset 65536 Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 0 @@ -369,7 +384,7 @@ read 65536/65536 bytes at offset 65536 Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. @@ -393,7 +408,7 @@ read 67108864/67108864 bytes at offset 0 === Testing progress report without snapshot === Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=4294967296 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file=TEST_DIR/t.IMGFMT.base wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 1073741824 @@ -408,7 +423,7 @@ No errors were found on the image. === Testing progress report with snapshot === Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=4294967296 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file=TEST_DIR/t.IMGFMT.base wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 1073741824 diff --git a/qemu/tests/qemu-iotests/062 b/qemu/tests/qemu-iotests/062 index 0511246de..051fb9f41 100755 --- a/qemu/tests/qemu-iotests/062 +++ b/qemu/tests/qemu-iotests/062 @@ -26,7 +26,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/063 b/qemu/tests/qemu-iotests/063 index a47493a07..352e78c77 100755 --- a/qemu/tests/qemu-iotests/063 +++ b/qemu/tests/qemu-iotests/063 @@ -26,7 +26,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/064 b/qemu/tests/qemu-iotests/064 index 7564563ab..5792fbbc9 100755 --- a/qemu/tests/qemu-iotests/064 +++ b/qemu/tests/qemu-iotests/064 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/066 b/qemu/tests/qemu-iotests/066 index 1c2452b0c..364166d3b 100755 --- a/qemu/tests/qemu-iotests/066 +++ b/qemu/tests/qemu-iotests/066 @@ -25,7 +25,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/067 b/qemu/tests/qemu-iotests/067 index 83eefa394..c1df48ede 100755 --- a/qemu/tests/qemu-iotests/067 +++ b/qemu/tests/qemu-iotests/067 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! # get standard environment, filters and checks @@ -45,10 +44,20 @@ function do_run_qemu() echo } +# Remove QMP events from (pretty-printed) output. Doesn't handle +# nested dicts correctly, but we don't get any of those in this test. +_filter_qmp_events() +{ + tr '\n' '\t' | sed -e \ + 's/{\s*"timestamp":\s*{[^}]*},\s*"event":[^,}]*\(,\s*"data":\s*{[^}]*}\)\?\s*}\s*//g' \ + | tr '\t' '\n' +} + function run_qemu() { do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | _filter_qemu \ - | sed -e 's/\("actual-size":\s*\)[0-9]\+/\1SIZE/g' + | sed -e 's/\("actual-size":\s*\)[0-9]\+/\1SIZE/g' \ + | _filter_generated_node_ids | _filter_qmp_events } size=128M @@ -59,7 +68,7 @@ echo echo === -drive/-device and device_del === echo -run_qemu -drive file=$TEST_IMG,format=$IMGFMT,if=none,id=disk -device virtio-blk-pci,drive=disk,id=virtio0 <&1 | _filter_testdir | _filter_qemu | _filter_qmp | _filter_qemu_io + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp\ + | _filter_qemu_io | _filter_generated_node_ids } test_quorum=$($QEMU_IMG --help|grep quorum) @@ -101,8 +101,20 @@ $QEMU_IO -c "open -o $quorum" -c "read -P 0x32 0 $size" | _filter_qemu_io echo echo "== checking mixed reference/option specification ==" -run_qemu -drive "file=$TEST_DIR/2.raw,format=$IMGFMT,if=none,id=drive2" <&1 >/dev/null & wait_for_tcp_port "127\\.0\\.0\\.1:$port" - $QEMU_IO -c "read 0 512" "$nbd_url" 2>&1 | _filter_qemu_io | filter_nbd + $QEMU_IO -c "read 0 512" "$nbd_url" 2>&1 | _filter_qemu_io | _filter_nbd echo } diff --git a/qemu/tests/qemu-iotests/083.out b/qemu/tests/qemu-iotests/083.out index 8c1441bf4..ef3d1e32a 100644 --- a/qemu/tests/qemu-iotests/083.out +++ b/qemu/tests/qemu-iotests/083.out @@ -1,138 +1,128 @@ QA output created by 083 === Check disconnect before neg1 === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo +can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect after neg1 === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo +can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect 8 neg1 === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo +can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect 16 neg1 === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo +can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect before export === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo +can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect after export === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo +can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect 4 export === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo +can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect 12 export === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo +can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect 16 export === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo +can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect before neg2 === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo +can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect after neg2 === - read failed: Input/output error === Check disconnect 8 neg2 === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo +can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect 10 neg2 === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo +can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect before request === - read failed: Input/output error === Check disconnect after request === - read failed: Input/output error === Check disconnect before reply === - read failed: Input/output error === Check disconnect after reply === - read failed: Input/output error === Check disconnect 4 reply === - read failed: Input/output error === Check disconnect 8 reply === - read failed: Input/output error === Check disconnect before data === - read failed: Input/output error === Check disconnect after data === - read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) === Check disconnect before neg-classic === -qemu-io: can't open device nbd:127.0.0.1:PORT +can't open device nbd:127.0.0.1:PORT no file open, try 'help open' === Check disconnect 8 neg-classic === -qemu-io: can't open device nbd:127.0.0.1:PORT +can't open device nbd:127.0.0.1:PORT no file open, try 'help open' === Check disconnect 16 neg-classic === -qemu-io: can't open device nbd:127.0.0.1:PORT +can't open device nbd:127.0.0.1:PORT no file open, try 'help open' === Check disconnect 24 neg-classic === -qemu-io: can't open device nbd:127.0.0.1:PORT +can't open device nbd:127.0.0.1:PORT no file open, try 'help open' === Check disconnect 28 neg-classic === -qemu-io: can't open device nbd:127.0.0.1:PORT +can't open device nbd:127.0.0.1:PORT no file open, try 'help open' === Check disconnect after neg-classic === - read failed: Input/output error *** done diff --git a/qemu/tests/qemu-iotests/084 b/qemu/tests/qemu-iotests/084 index 733018d4a..04f2aa9d7 100755 --- a/qemu/tests/qemu-iotests/084 +++ b/qemu/tests/qemu-iotests/084 @@ -26,7 +26,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/085 b/qemu/tests/qemu-iotests/085 index 56cd6f89b..aa77eca77 100755 --- a/qemu/tests/qemu-iotests/085 +++ b/qemu/tests/qemu-iotests/085 @@ -7,6 +7,7 @@ # snapshots are performed. # # Copyright (C) 2014 Red Hat, Inc. +# Copyright (C) 2015 Igalia, S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -34,17 +35,17 @@ status=1 # failure is the default! snapshot_virt0="snapshot-v0.qcow2" snapshot_virt1="snapshot-v1.qcow2" -MAX_SNAPSHOTS=10 +SNAPSHOTS=10 _cleanup() { _cleanup_qemu - for i in $(seq 1 ${MAX_SNAPSHOTS}) + for i in $(seq 1 ${SNAPSHOTS}) do rm -f "${TEST_DIR}/${i}-${snapshot_virt0}" rm -f "${TEST_DIR}/${i}-${snapshot_virt1}" done - _cleanup_test_img + rm -f "${TEST_IMG}.1" "${TEST_IMG}.2" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -64,7 +65,7 @@ function create_single_snapshot() { cmd="{ 'execute': 'blockdev-snapshot-sync', 'arguments': { 'device': 'virtio0', - 'snapshot-file':'"${TEST_DIR}/${1}-${snapshot_virt0}"', + 'snapshot-file':'${TEST_DIR}/${1}-${snapshot_virt0}', 'format': 'qcow2' } }" _send_qemu_cmd $h "${cmd}" "return" } @@ -76,27 +77,60 @@ function create_group_snapshot() {'actions': [ { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio0', - 'snapshot-file': '"${TEST_DIR}/${1}-${snapshot_virt0}"' } }, + 'snapshot-file': '${TEST_DIR}/${1}-${snapshot_virt0}' } }, { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio1', - 'snapshot-file': '"${TEST_DIR}/${1}-${snapshot_virt1}"' } } ] + 'snapshot-file': '${TEST_DIR}/${1}-${snapshot_virt1}' } } ] } }" _send_qemu_cmd $h "${cmd}" "return" } +# ${1}: unique identifier for the snapshot filename +# ${2}: true: open backing images; false: don't open them (default) +function add_snapshot_image() +{ + if [ "${2}" = "true" ]; then + extra_params="" + else + extra_params="'backing': '', " + fi + base_image="${TEST_DIR}/$((${1}-1))-${snapshot_virt0}" + snapshot_file="${TEST_DIR}/${1}-${snapshot_virt0}" + _make_test_img -b "${base_image}" "$size" + mv "${TEST_IMG}" "${snapshot_file}" + cmd="{ 'execute': 'blockdev-add', 'arguments': + { 'options': + { 'driver': 'qcow2', 'node-name': 'snap_${1}', ${extra_params} + 'file': + { 'driver': 'file', 'filename': '${snapshot_file}', + 'node-name': 'file_${1}' } } } }" + _send_qemu_cmd $h "${cmd}" "return" +} + +# ${1}: unique identifier for the snapshot filename +# ${2}: expected response, defaults to 'return' +function blockdev_snapshot() +{ + cmd="{ 'execute': 'blockdev-snapshot', + 'arguments': { 'node': 'virtio0', + 'overlay':'snap_${1}' } }" + _send_qemu_cmd $h "${cmd}" "${2:-return}" +} + size=128M _make_test_img $size -mv "${TEST_IMG}" "${TEST_IMG}.orig" +mv "${TEST_IMG}" "${TEST_IMG}.1" _make_test_img $size +mv "${TEST_IMG}" "${TEST_IMG}.2" echo echo === Running QEMU === echo qemu_comm_method="qmp" -_launch_qemu -drive file="${TEST_IMG}.orig",if=virtio -drive file="${TEST_IMG}",if=virtio +_launch_qemu -drive file="${TEST_IMG}.1",if=virtio -drive file="${TEST_IMG}.2",if=virtio h=$QEMU_HANDLE echo @@ -105,6 +139,8 @@ echo _send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" "return" +# Tests for the blockdev-snapshot-sync command + echo echo === Create a single snapshot on virtio0 === echo @@ -117,7 +153,7 @@ echo === Invalid command - missing device and nodename === echo _send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot-sync', - 'arguments': { 'snapshot-file':'"${TEST_DIR}/1-${snapshot_virt0}"', + 'arguments': { 'snapshot-file':'${TEST_DIR}/1-${snapshot_virt0}', 'format': 'qcow2' } }" "error" echo @@ -132,11 +168,75 @@ echo echo === Create several transactional group snapshots === echo -for i in $(seq 2 ${MAX_SNAPSHOTS}) +for i in $(seq 2 ${SNAPSHOTS}) do create_group_snapshot ${i} done +# Tests for the blockdev-snapshot command + +echo +echo === Create a couple of snapshots using blockdev-snapshot === +echo + +SNAPSHOTS=$((${SNAPSHOTS}+1)) +add_snapshot_image ${SNAPSHOTS} +blockdev_snapshot ${SNAPSHOTS} + +SNAPSHOTS=$((${SNAPSHOTS}+1)) +add_snapshot_image ${SNAPSHOTS} +blockdev_snapshot ${SNAPSHOTS} + +echo +echo === Invalid command - cannot create a snapshot using a file BDS === +echo + +_send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot', + 'arguments': { 'node':'virtio0', + 'overlay':'file_${SNAPSHOTS}' } + }" "error" + +echo +echo === Invalid command - snapshot node used as active layer === +echo + +blockdev_snapshot ${SNAPSHOTS} error + +_send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot', + 'arguments': { 'node':'virtio0', + 'overlay':'virtio0' } + }" "error" + +_send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot', + 'arguments': { 'node':'virtio0', + 'overlay':'virtio1' } + }" "error" + +echo +echo === Invalid command - snapshot node used as backing hd === +echo + +blockdev_snapshot $((${SNAPSHOTS}-1)) error + +echo +echo === Invalid command - snapshot node has a backing image === +echo + +SNAPSHOTS=$((${SNAPSHOTS}+1)) +add_snapshot_image ${SNAPSHOTS} true +blockdev_snapshot ${SNAPSHOTS} error + +echo +echo === Invalid command - The node does not exist === +echo + +blockdev_snapshot $((${SNAPSHOTS}+1)) error + +_send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot', + 'arguments': { 'node':'nodevice', + 'overlay':'snap_${SNAPSHOTS}' } + }" "error" + # success, all done echo "*** done" rm -f $seq.full diff --git a/qemu/tests/qemu-iotests/085.out b/qemu/tests/qemu-iotests/085.out index 5eb8b947c..01c78d689 100644 --- a/qemu/tests/qemu-iotests/085.out +++ b/qemu/tests/qemu-iotests/085.out @@ -11,7 +11,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 === Create a single snapshot on virtio0 === -Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2.orig' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/t.qcow2.1 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} === Invalid command - missing device and nodename === @@ -25,31 +25,65 @@ Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file === Create several transactional group snapshots === -Formatting 'TEST_DIR/2-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/1-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/2-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/2-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/1-snapshot-v0.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/2-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/t.qcow2.2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/3-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/3-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/3-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/2-snapshot-v0.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/3-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/2-snapshot-v1.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/4-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/4-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/4-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/3-snapshot-v0.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/4-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/3-snapshot-v1.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/5-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/5-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/5-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/4-snapshot-v0.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/5-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/4-snapshot-v1.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/6-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/6-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/6-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/5-snapshot-v0.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/6-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/5-snapshot-v1.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/7-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/7-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/7-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/6-snapshot-v0.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/7-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/6-snapshot-v1.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/8-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/8-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/8-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/7-snapshot-v0.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/8-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/7-snapshot-v1.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/9-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/9-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/9-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/8-snapshot-v0.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/9-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/8-snapshot-v1.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/10-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/10-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/10-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/9-snapshot-v0.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/10-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/9-snapshot-v1.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} + +=== Create a couple of snapshots using blockdev-snapshot === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/10-snapshot-v0.IMGFMT +{"return": {}} +{"return": {}} +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/11-snapshot-v0.IMGFMT +{"return": {}} +{"return": {}} + +=== Invalid command - cannot create a snapshot using a file BDS === + +{"error": {"class": "GenericError", "desc": "The snapshot does not support backing images"}} + +=== Invalid command - snapshot node used as active layer === + +{"error": {"class": "GenericError", "desc": "The snapshot is already in use by virtio0"}} +{"error": {"class": "GenericError", "desc": "The snapshot is already in use by virtio0"}} +{"error": {"class": "GenericError", "desc": "The snapshot is already in use by virtio1"}} + +=== Invalid command - snapshot node used as backing hd === + +{"error": {"class": "GenericError", "desc": "Node 'snap_11' is busy: node is used as backing hd of 'virtio0'"}} + +=== Invalid command - snapshot node has a backing image === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/12-snapshot-v0.IMGFMT +{"return": {}} +{"error": {"class": "GenericError", "desc": "The snapshot already has a backing image"}} + +=== Invalid command - The node does not exist === + +{"error": {"class": "GenericError", "desc": "Cannot find device=snap_14 nor node_name=snap_14"}} +{"error": {"class": "GenericError", "desc": "Cannot find device=nodevice nor node_name=nodevice"}} *** done diff --git a/qemu/tests/qemu-iotests/086 b/qemu/tests/qemu-iotests/086 index 234eb9a91..cd4494a66 100755 --- a/qemu/tests/qemu-iotests/086 +++ b/qemu/tests/qemu-iotests/086 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -38,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -_supported_fmt qcow2 +_supported_fmt qcow2 raw _supported_proto file nfs _supported_os Linux diff --git a/qemu/tests/qemu-iotests/087 b/qemu/tests/qemu-iotests/087 index 869474994..e7bca37ef 100755 --- a/qemu/tests/qemu-iotests/087 +++ b/qemu/tests/qemu-iotests/087 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! # get standard environment, filters and checks @@ -45,7 +44,8 @@ function do_run_qemu() function run_qemu() { - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | _filter_qemu \ + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ + | _filter_qemu | _filter_imgfmt \ | sed -e 's/\("actual-size":\s*\)[0-9]\+/\1SIZE/g' } @@ -54,7 +54,7 @@ size=128M _make_test_img $size echo -echo === Missing ID === +echo === Missing ID and node-name === echo run_qemu < seconds * limit * 0.9 / ndrives) - - nsec_per_sec = 1000000000 - + def configure_throttle(self, ndrives, params): params['group'] = 'test' # Set the I/O throttling parameters to all drives @@ -60,13 +53,21 @@ class ThrottleTestCase(iotests.QMPTestCase): result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params) self.assert_qmp(result, 'return', {}) + def do_test_throttle(self, ndrives, seconds, params): + def check_limit(limit, num): + # IO throttling algorithm is discrete, allow 10% error so the test + # is more robust + return limit == 0 or \ + (num < seconds * limit * 1.1 / ndrives + and num > seconds * limit * 0.9 / ndrives) + # Set vm clock to a known value ns = seconds * nsec_per_sec self.vm.qtest("clock_step %d" % ns) - # Submit enough requests. They will drain bps_max and iops_max, but the - # rest requests won't get executed until we advance the virtual clock - # with qtest interface + # Submit enough requests so the throttling mechanism kicks + # in. The throttled requests won't be executed until we + # advance the virtual clock. rq_size = 512 rd_nr = max(params['bps'] / rq_size / 2, params['bps_rd'] / rq_size, @@ -142,8 +143,44 @@ class ThrottleTestCase(iotests.QMPTestCase): for tk in params: limits = dict([(k, 0) for k in params]) limits[tk] = params[tk] * ndrives + self.configure_throttle(ndrives, limits) self.do_test_throttle(ndrives, 5, limits) + def test_burst(self): + params = {"bps": 4096, + "bps_rd": 4096, + "bps_wr": 4096, + "iops": 10, + "iops_rd": 10, + "iops_wr": 10, + } + ndrives = 1 + # Pick each out of all possible params and test + for tk in params: + rate = params[tk] * ndrives + burst_rate = rate * 7 + burst_length = 4 + + # Configure the throttling settings + settings = dict([(k, 0) for k in params]) + settings[tk] = rate + settings['%s_max' % tk] = burst_rate + settings['%s_max_length' % tk] = burst_length + self.configure_throttle(ndrives, settings) + + # Wait for the bucket to empty so we can do bursts + wait_ns = nsec_per_sec * burst_length * burst_rate / rate + self.vm.qtest("clock_step %d" % wait_ns) + + # Test I/O at the max burst rate + limits = dict([(k, 0) for k in params]) + limits[tk] = burst_rate + self.do_test_throttle(ndrives, burst_length, limits) + + # Now test I/O at the normal rate + limits[tk] = rate + self.do_test_throttle(ndrives, 5, limits) + class ThrottleTestCoroutine(ThrottleTestCase): test_img = "null-co://" diff --git a/qemu/tests/qemu-iotests/093.out b/qemu/tests/qemu-iotests/093.out index fbc63e62f..89968f35d 100644 --- a/qemu/tests/qemu-iotests/093.out +++ b/qemu/tests/qemu-iotests/093.out @@ -1,5 +1,5 @@ -.. +.... ---------------------------------------------------------------------- -Ran 2 tests +Ran 4 tests OK diff --git a/qemu/tests/qemu-iotests/094 b/qemu/tests/qemu-iotests/094 index 27a2be256..0ba0b0c36 100755 --- a/qemu/tests/qemu-iotests/094 +++ b/qemu/tests/qemu-iotests/094 @@ -1,6 +1,6 @@ #!/bin/bash # -# Test case for drive-mirror to NBD (especially bdrv_swap() on NBD BDS) +# Test case for drive-mirror to NBD # # Copyright (C) 2015 Red Hat, Inc. # @@ -25,7 +25,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! trap "exit \$status" 0 1 2 3 15 @@ -50,8 +49,10 @@ _send_qemu_cmd $QEMU_HANDLE \ "{'execute': 'qmp_capabilities'}" \ 'return' -# 'format': 'nbd' is not actually "correct", but this is probably the only way -# to test bdrv_swap() on an NBD BDS +# 'format': 'nbd' is not actually "correct", but this was the only way to +# test the bug fixed in commit f53a829. Though the bug's related code +# bdrv_swap() was replaced later, let's make sure we don't fall in the same +# pit again. _send_qemu_cmd $QEMU_HANDLE \ "{'execute': 'drive-mirror', 'arguments': {'device': 'src', diff --git a/qemu/tests/qemu-iotests/095 b/qemu/tests/qemu-iotests/095 index 6630181a7..dad04b9ac 100755 --- a/qemu/tests/qemu-iotests/095 +++ b/qemu/tests/qemu-iotests/095 @@ -50,17 +50,15 @@ _supported_os Linux size_smaller=5M size_larger=100M -_make_test_img $size_smaller -mv "${TEST_IMG}" "${TEST_IMG}.base" +TEST_IMG="$TEST_IMG.base" _make_test_img $size_smaller -_make_test_img -b "${TEST_IMG}.base" $size_larger -mv "${TEST_IMG}" "${TEST_IMG}.snp1" +TEST_IMG="$TEST_IMG.snp1" _make_test_img -b "$TEST_IMG.base" $size_larger _make_test_img -b "${TEST_IMG}.snp1" $size_larger echo echo "=== Base image info before commit and resize ===" -TEST_IMG="${TEST_IMG}.base" _img_info +TEST_IMG="${TEST_IMG}.base" _img_info | _filter_img_info echo echo === Running QEMU Live Commit Test === @@ -78,7 +76,7 @@ _send_qemu_cmd $h "{ 'execute': 'block-commit', echo echo "=== Base image info after commit and resize ===" -TEST_IMG="${TEST_IMG}.base" _img_info +TEST_IMG="${TEST_IMG}.base" _img_info | _filter_img_info # success, all done echo "*** done" diff --git a/qemu/tests/qemu-iotests/095.out b/qemu/tests/qemu-iotests/095.out index 267c4836e..73875cab4 100644 --- a/qemu/tests/qemu-iotests/095.out +++ b/qemu/tests/qemu-iotests/095.out @@ -1,13 +1,12 @@ QA output created by 095 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=5242880 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=104857600 backing_file='TEST_DIR/t.IMGFMT.base' -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=104857600 backing_file='TEST_DIR/t.IMGFMT.snp1' +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=5242880 +Formatting 'TEST_DIR/t.IMGFMT.snp1', fmt=IMGFMT size=104857600 backing_file=TEST_DIR/t.IMGFMT.base +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=104857600 backing_file=TEST_DIR/t.IMGFMT.snp1 === Base image info before commit and resize === image: TEST_DIR/t.IMGFMT.base file format: IMGFMT virtual size: 5.0M (5242880 bytes) -cluster_size: 65536 === Running QEMU Live Commit Test === @@ -19,5 +18,4 @@ cluster_size: 65536 image: TEST_DIR/t.IMGFMT.base file format: IMGFMT virtual size: 100M (104857600 bytes) -cluster_size: 65536 *** done diff --git a/qemu/tests/qemu-iotests/096 b/qemu/tests/qemu-iotests/096 new file mode 100644 index 000000000..e34204b8f --- /dev/null +++ b/qemu/tests/qemu-iotests/096 @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# +# Test that snapshots move the throttling configuration to the active +# layer +# +# Copyright (C) 2015 Igalia, S.L. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# 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 . +# + +import iotests +import os + +class TestLiveSnapshot(iotests.QMPTestCase): + base_img = os.path.join(iotests.test_dir, 'base.img') + target_img = os.path.join(iotests.test_dir, 'target.img') + group = 'mygroup' + iops = 6000 + iops_size = 1024 + + def setUp(self): + opts = [] + opts.append('node-name=base') + opts.append('throttling.group=%s' % self.group) + opts.append('throttling.iops-total=%d' % self.iops) + opts.append('throttling.iops-size=%d' % self.iops_size) + iotests.qemu_img('create', '-f', iotests.imgfmt, self.base_img, '100M') + self.vm = iotests.VM().add_drive(self.base_img, ','.join(opts)) + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + os.remove(self.base_img) + os.remove(self.target_img) + + def checkConfig(self, active_layer): + result = self.vm.qmp('query-named-block-nodes') + for r in result['return']: + if r['node-name'] == active_layer: + self.assertEqual(r['group'], self.group) + self.assertEqual(r['iops'], self.iops) + self.assertEqual(r['iops_size'], self.iops_size) + else: + self.assertFalse(r.has_key('group')) + self.assertEqual(r['iops'], 0) + self.assertFalse(r.has_key('iops_size')) + + def testSnapshot(self): + self.checkConfig('base') + self.vm.qmp('blockdev-snapshot-sync', + node_name = 'base', + snapshot_node_name = 'target', + snapshot_file = self.target_img, + format = iotests.imgfmt) + self.checkConfig('target') + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2']) diff --git a/qemu/tests/qemu-iotests/096.out b/qemu/tests/qemu-iotests/096.out new file mode 100644 index 000000000..ae1213e6f --- /dev/null +++ b/qemu/tests/qemu-iotests/096.out @@ -0,0 +1,5 @@ +. +---------------------------------------------------------------------- +Ran 1 tests + +OK diff --git a/qemu/tests/qemu-iotests/097 b/qemu/tests/qemu-iotests/097 index c7a613b7e..01d8dd033 100755 --- a/qemu/tests/qemu-iotests/097 +++ b/qemu/tests/qemu-iotests/097 @@ -26,7 +26,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/097.out b/qemu/tests/qemu-iotests/097.out index 81651f496..48abd2e64 100644 --- a/qemu/tests/qemu-iotests/097.out +++ b/qemu/tests/qemu-iotests/097.out @@ -3,8 +3,8 @@ QA output created by 097 === Test pass 0 === Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd' +Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.itmd wrote 196608/196608 bytes at offset 0 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 65536 @@ -32,8 +32,8 @@ Offset Length File === Test pass 1 === Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd' +Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.itmd wrote 196608/196608 bytes at offset 0 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 65536 @@ -62,8 +62,8 @@ Offset Length File === Test pass 2 === Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd' +Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.itmd wrote 196608/196608 bytes at offset 0 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 65536 @@ -92,8 +92,8 @@ Offset Length File === Test pass 3 === Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd' +Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.itmd wrote 196608/196608 bytes at offset 0 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 65536 diff --git a/qemu/tests/qemu-iotests/098 b/qemu/tests/qemu-iotests/098 index e2230ad60..b002e969b 100755 --- a/qemu/tests/qemu-iotests/098 +++ b/qemu/tests/qemu-iotests/098 @@ -25,7 +25,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/098.out b/qemu/tests/qemu-iotests/098.out index e08a189b4..7634d0e8b 100644 --- a/qemu/tests/qemu-iotests/098.out +++ b/qemu/tests/qemu-iotests/098.out @@ -3,7 +3,7 @@ QA output created by 098 === l1_update === Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error @@ -12,7 +12,7 @@ No errors were found on the image. === empty_image_prepare === Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error @@ -25,7 +25,7 @@ No errors were found on the image. === reftable_update === Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error @@ -39,7 +39,7 @@ No errors were found on the image. === refblock_alloc === Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error diff --git a/qemu/tests/qemu-iotests/099 b/qemu/tests/qemu-iotests/099 index 80f3d9aaf..caaf58eee 100755 --- a/qemu/tests/qemu-iotests/099 +++ b/qemu/tests/qemu-iotests/099 @@ -26,7 +26,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/100 b/qemu/tests/qemu-iotests/100 index 7c1b235b5..5b2fb3333 100755 --- a/qemu/tests/qemu-iotests/100 +++ b/qemu/tests/qemu-iotests/100 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/101 b/qemu/tests/qemu-iotests/101 index 70fbf25f6..ea53f8b8d 100755 --- a/qemu/tests/qemu-iotests/101 +++ b/qemu/tests/qemu-iotests/101 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/102 b/qemu/tests/qemu-iotests/102 index 161b1974c..64b4af944 100755 --- a/qemu/tests/qemu-iotests/102 +++ b/qemu/tests/qemu-iotests/102 @@ -25,7 +25,6 @@ seq=$(basename $0) echo "QA output created by $seq" here=$PWD -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/103 b/qemu/tests/qemu-iotests/103 index fa9a3c1fc..ecbd8ebd7 100755 --- a/qemu/tests/qemu-iotests/103 +++ b/qemu/tests/qemu-iotests/103 @@ -25,7 +25,6 @@ seq=$(basename $0) echo "QA output created by $seq" here=$PWD -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/103.out b/qemu/tests/qemu-iotests/103.out index d05f49fdb..b7aaadf89 100644 --- a/qemu/tests/qemu-iotests/103.out +++ b/qemu/tests/qemu-iotests/103.out @@ -5,10 +5,10 @@ wrote 65536/65536 bytes at offset 0 === Testing invalid option combinations === -qemu-io: can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cache-size may not be set the same time -qemu-io: can't open device TEST_DIR/t.IMGFMT: l2-cache-size may not exceed cache-size -qemu-io: can't open device TEST_DIR/t.IMGFMT: refcount-cache-size may not exceed cache-size -qemu-io: can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cache-size may not be set the same time +can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cache-size may not be set the same time +can't open device TEST_DIR/t.IMGFMT: l2-cache-size may not exceed cache-size +can't open device TEST_DIR/t.IMGFMT: refcount-cache-size may not exceed cache-size +can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cache-size may not be set the same time === Testing valid option combinations === diff --git a/qemu/tests/qemu-iotests/104 b/qemu/tests/qemu-iotests/104 index 2e35ea80d..726d46705 100755 --- a/qemu/tests/qemu-iotests/104 +++ b/qemu/tests/qemu-iotests/104 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! trap "exit \$status" 0 1 2 3 15 diff --git a/qemu/tests/qemu-iotests/105 b/qemu/tests/qemu-iotests/105 index 9bae49e32..3db4ce3cf 100755 --- a/qemu/tests/qemu-iotests/105 +++ b/qemu/tests/qemu-iotests/105 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/107 b/qemu/tests/qemu-iotests/107 index 986203046..d7222dc1c 100755 --- a/qemu/tests/qemu-iotests/107 +++ b/qemu/tests/qemu-iotests/107 @@ -25,7 +25,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/108 b/qemu/tests/qemu-iotests/108 index ce447498e..2355d98c1 100755 --- a/qemu/tests/qemu-iotests/108 +++ b/qemu/tests/qemu-iotests/108 @@ -26,7 +26,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/109 b/qemu/tests/qemu-iotests/109 index 0b668da85..f980b0c9e 100755 --- a/qemu/tests/qemu-iotests/109 +++ b/qemu/tests/qemu-iotests/109 @@ -25,7 +25,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/109.out b/qemu/tests/qemu-iotests/109.out index 7db92c9ce..38bc073a3 100644 --- a/qemu/tests/qemu-iotests/109.out +++ b/qemu/tests/qemu-iotests/109.out @@ -2,8 +2,8 @@ QA output created by 109 === Writing a qcow header into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -23,15 +23,15 @@ Images are identical. === Writing a qcow2 header into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. Specify the 'raw' format explicitly to remove the restrictions. {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 197120, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 197120, "offset": 512, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} {"return": []} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -44,15 +44,15 @@ Images are identical. === Writing a qed header into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. Specify the 'raw' format explicitly to remove the restrictions. {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 262144, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} {"return": []} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -65,8 +65,8 @@ Images are identical. === Writing a vdi header into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -86,8 +86,8 @@ Images are identical. === Writing a vmdk header into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -107,8 +107,8 @@ Images are identical. === Writing a vpc header into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -128,7 +128,7 @@ Images are identical. === Copying sample image empty.bochs into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -149,7 +149,7 @@ Images are identical. === Copying sample image iotest-dirtylog-10G-4M.vhdx into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -170,7 +170,7 @@ Images are identical. === Copying sample image parallels-v1 into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -191,7 +191,7 @@ Images are identical. === Copying sample image simple-pattern.cloop into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -212,7 +212,7 @@ Images are identical. === Write legitimate MBR into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. diff --git a/qemu/tests/qemu-iotests/110 b/qemu/tests/qemu-iotests/110 index a687f9567..9de7369f3 100755 --- a/qemu/tests/qemu-iotests/110 +++ b/qemu/tests/qemu-iotests/110 @@ -25,7 +25,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/110.out b/qemu/tests/qemu-iotests/110.out index 152bacf41..b3584ff87 100644 --- a/qemu/tests/qemu-iotests/110.out +++ b/qemu/tests/qemu-iotests/110.out @@ -3,7 +3,7 @@ QA output created by 110 === Reconstructable filename === Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=t.IMGFMT.base image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 64M (67108864 bytes) @@ -11,9 +11,12 @@ backing file: t.IMGFMT.base (actual path: TEST_DIR/t.IMGFMT.base) === Non-reconstructable filename === -qemu-img: Cannot use relative backing file names for 'json:{"driver": "IMGFMT", "file": {"set-state.0.event": "read_aio", "image": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "driver": "blkdebug", "set-state.0.new_state": 42}}' +image: json:{"driver": "IMGFMT", "file": {"set-state.0.event": "read_aio", "image": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "driver": "blkdebug", "set-state.0.new_state": 42}} +file format: IMGFMT +virtual size: 64M (67108864 bytes) +backing file: t.IMGFMT.base (cannot determine actual path) === Backing name is always relative to the backed image === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=t.IMGFMT.base *** done diff --git a/qemu/tests/qemu-iotests/111 b/qemu/tests/qemu-iotests/111 index 6011c94b7..a1c152d0c 100755 --- a/qemu/tests/qemu-iotests/111 +++ b/qemu/tests/qemu-iotests/111 @@ -26,7 +26,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/112 b/qemu/tests/qemu-iotests/112 index 3f054a3fc..28eb9aae9 100755 --- a/qemu/tests/qemu-iotests/112 +++ b/qemu/tests/qemu-iotests/112 @@ -25,7 +25,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -180,6 +179,115 @@ $QEMU_IMG snapshot -c foo "$TEST_IMG" # leaked (refcount=UINT64_MAX reference=1) _check_test_img +echo +echo '=== Amend from refcount_bits=16 to refcount_bits=1 ===' +echo + +_make_test_img 64M +print_refcount_bits + +$QEMU_IO -c 'write 16M 32M' "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG amend -o refcount_bits=1 "$TEST_IMG" +_check_test_img +print_refcount_bits + +echo +echo '=== Amend from refcount_bits=1 to refcount_bits=64 ===' +echo + +$QEMU_IMG amend -o refcount_bits=64 "$TEST_IMG" +_check_test_img +print_refcount_bits + +echo +echo '=== Amend to compat=0.10 ===' +echo + +# Should not work because refcount_bits needs to be 16 for compat=0.10 +$QEMU_IMG amend -o compat=0.10 "$TEST_IMG" +print_refcount_bits +# Should work +$QEMU_IMG amend -o compat=0.10,refcount_bits=16 "$TEST_IMG" +_check_test_img +print_refcount_bits + +# Get back to compat=1.1 and refcount_bits=16 +$QEMU_IMG amend -o compat=1.1 "$TEST_IMG" +print_refcount_bits +# Should not work +$QEMU_IMG amend -o refcount_bits=32,compat=0.10 "$TEST_IMG" +print_refcount_bits + +echo +echo '=== Amend with snapshot ===' +echo + +$QEMU_IMG snapshot -c foo "$TEST_IMG" +# Just to have different refcounts across the image +$QEMU_IO -c 'write 0 16M' "$TEST_IMG" | _filter_qemu_io + +# Should not work (may work in the future by first decreasing all refcounts so +# they fit into the target range by copying them) +$QEMU_IMG amend -o refcount_bits=1 "$TEST_IMG" +_check_test_img +print_refcount_bits + +# Should work +$QEMU_IMG amend -o refcount_bits=2 "$TEST_IMG" +_check_test_img +print_refcount_bits + +echo +echo '=== Testing too many references for check ===' +echo + +IMGOPTS="$IMGOPTS,refcount_bits=1" _make_test_img 64M +print_refcount_bits + +# This cluster should be created at 0x50000 +$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io +# Now make the second L2 entry (the L2 table should be at 0x40000) point to that +# cluster, so we have two references +poke_file "$TEST_IMG" $((0x40008)) "\x80\x00\x00\x00\x00\x05\x00\x00" + +# This should say "please use amend" +_check_test_img -r all + +# So we do that +$QEMU_IMG amend -o refcount_bits=2 "$TEST_IMG" +print_refcount_bits + +# And try again +_check_test_img -r all + +echo +echo '=== Multiple walks necessary during amend ===' +echo + +IMGOPTS="$IMGOPTS,refcount_bits=1,cluster_size=512" _make_test_img 64k + +# Cluster 0 is the image header, clusters 1 to 4 are used by the L1 table, a +# single L2 table, the reftable and a single refblock. This creates 58 data +# clusters (actually, the L2 table is created here, too), so in total there are +# then 63 used clusters in the image. With a refcount width of 64, one refblock +# describes 64 clusters (512 bytes / 64 bits/entry = 64 entries), so this will +# make the first refblock in the amended image have exactly one free entry. +$QEMU_IO -c "write 0 $((58 * 512))" "$TEST_IMG" | _filter_qemu_io + +# Now change the refcount width; since the first new refblock will have exactly +# one free entry, that entry will be used to store its own reference. No other +# refblocks are needed, so then the new reftable will be allocated; since the +# first new refblock is completely filled up, this will require a new refblock +# which is why the refcount width changing function will need to run through +# everything one more time until the allocations are stable. +# Having more walks than usual should be visible as regressing progress (from +# 66.67 % (2/3 walks) to 50.00 % (2/4 walks)). +$QEMU_IMG amend -o refcount_bits=64 -p "$TEST_IMG" | tr '\r' '\n' \ + | grep -A 1 '66.67' +print_refcount_bits + +_check_test_img + # success, all done echo '*** done' diff --git a/qemu/tests/qemu-iotests/112.out b/qemu/tests/qemu-iotests/112.out index 9a98633f6..81b04d145 100644 --- a/qemu/tests/qemu-iotests/112.out +++ b/qemu/tests/qemu-iotests/112.out @@ -21,9 +21,9 @@ refcount bits: 16 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 refcount bits: 16 -qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater) +qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use or greater) Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater) +qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use or greater) Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 === Snapshot limit on refcount_bits=1 === @@ -81,4 +81,75 @@ Leaked cluster 6 refcount=1 reference=0 2 leaked clusters were found on the image. This means waste of disk space, but no harm to data. + +=== Amend from refcount_bits=16 to refcount_bits=1 === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 16 +wrote 33554432/33554432 bytes at offset 16777216 +32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +refcount bits: 1 + +=== Amend from refcount_bits=1 to refcount_bits=64 === + +No errors were found on the image. +refcount bits: 64 + +=== Amend to compat=0.10 === + +qemu-img: compat=0.10 requires refcount_bits=16 +qemu-img: Error while amending options: Operation not supported +refcount bits: 64 +No errors were found on the image. +refcount bits: 16 +refcount bits: 16 +qemu-img: Different refcount widths than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater) +qemu-img: Error while amending options: Invalid argument +refcount bits: 16 + +=== Amend with snapshot === + +wrote 16777216/16777216 bytes at offset 0 +16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Cannot decrease refcount entry width to 1 bits: Cluster at offset 0x50000 has a refcount of 2 +qemu-img: Error while amending options: Invalid argument +No errors were found on the image. +refcount bits: 16 +No errors were found on the image. +refcount bits: 2 + +=== Testing too many references for check === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 1 +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +ERROR: overflow cluster offset=0x50000 +Use qemu-img amend to increase the refcount entry width or qemu-img convert to create a clean copy if the image cannot be opened for writing + +1 errors were found on the image. +Data may be corrupted, or further writes to the image may corrupt it. +refcount bits: 2 +ERROR cluster 5 refcount=1 reference=2 +Repairing cluster 5 refcount=1 reference=2 +Repairing OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=2 +Repairing OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=2 +The following inconsistencies were found and repaired: + + 0 leaked clusters + 3 corruptions + +Double checking the fixed image now... +No errors were found on the image. + +=== Multiple walks necessary during amend === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536 +wrote 29696/29696 bytes at offset 0 +29 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + (66.67/100%) + (50.00/100%) +refcount bits: 64 +No errors were found on the image. *** done diff --git a/qemu/tests/qemu-iotests/113 b/qemu/tests/qemu-iotests/113 index a2cd96b17..19b68b272 100755 --- a/qemu/tests/qemu-iotests/113 +++ b/qemu/tests/qemu-iotests/113 @@ -26,7 +26,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/114 b/qemu/tests/qemu-iotests/114 index d02e7ffbe..f110d4f65 100755 --- a/qemu/tests/qemu-iotests/114 +++ b/qemu/tests/qemu-iotests/114 @@ -25,7 +25,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/114.out b/qemu/tests/qemu-iotests/114.out index 6c6b21085..b6d10e480 100644 --- a/qemu/tests/qemu-iotests/114.out +++ b/qemu/tests/qemu-iotests/114.out @@ -1,13 +1,13 @@ QA output created by 114 Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 64M (67108864 bytes) cluster_size: 65536 backing file: TEST_DIR/t.IMGFMT.base backing file format: foo -qemu-io: can't open device TEST_DIR/t.qcow2: Could not open backing file: Unknown driver 'foo' +can't open device TEST_DIR/t.qcow2: Could not open backing file: Unknown driver 'foo' read 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) *** done diff --git a/qemu/tests/qemu-iotests/115 b/qemu/tests/qemu-iotests/115 index a6be1876a..665c2ead4 100755 --- a/qemu/tests/qemu-iotests/115 +++ b/qemu/tests/qemu-iotests/115 @@ -25,7 +25,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/115.out b/qemu/tests/qemu-iotests/115.out index 7b2c5e02f..dbdad17b2 100644 --- a/qemu/tests/qemu-iotests/115.out +++ b/qemu/tests/qemu-iotests/115.out @@ -2,7 +2,7 @@ QA output created by 115 === Testing large refcount and L1 table === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=268435456 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=268435456 preallocation=metadata No errors were found on the image. 100.00% allocated clusters *** done diff --git a/qemu/tests/qemu-iotests/116 b/qemu/tests/qemu-iotests/116 index 713ed484b..df0172fed 100755 --- a/qemu/tests/qemu-iotests/116 +++ b/qemu/tests/qemu-iotests/116 @@ -28,7 +28,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/116.out b/qemu/tests/qemu-iotests/116.out index b679ceea6..1f11d4446 100644 --- a/qemu/tests/qemu-iotests/116.out +++ b/qemu/tests/qemu-iotests/116.out @@ -2,36 +2,36 @@ QA output created by 116 == truncated header cluster == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -qemu-io: can't open device TEST_DIR/t.qed: Could not open 'TEST_DIR/t.qed': Invalid argument +can't open device TEST_DIR/t.qed: Could not open 'TEST_DIR/t.qed': Invalid argument no file open, try 'help open' == invalid header magic == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -qemu-io: can't open device TEST_DIR/t.qed: Image not in QED format +can't open device TEST_DIR/t.qed: Image not in QED format no file open, try 'help open' == invalid cluster size == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -qemu-io: can't open device TEST_DIR/t.qed: Could not open 'TEST_DIR/t.qed': Invalid argument +can't open device TEST_DIR/t.qed: Could not open 'TEST_DIR/t.qed': Invalid argument no file open, try 'help open' == invalid table size == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -qemu-io: can't open device TEST_DIR/t.qed: Could not open 'TEST_DIR/t.qed': Invalid argument +can't open device TEST_DIR/t.qed: Could not open 'TEST_DIR/t.qed': Invalid argument no file open, try 'help open' == invalid header size == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -qemu-io: can't open device TEST_DIR/t.qed: Could not open 'TEST_DIR/t.qed': Invalid argument +can't open device TEST_DIR/t.qed: Could not open 'TEST_DIR/t.qed': Invalid argument no file open, try 'help open' == invalid L1 table offset == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -qemu-io: can't open device TEST_DIR/t.qed: Could not open 'TEST_DIR/t.qed': Invalid argument +can't open device TEST_DIR/t.qed: Could not open 'TEST_DIR/t.qed': Invalid argument no file open, try 'help open' == invalid image size == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -qemu-io: can't open device TEST_DIR/t.qed: Could not open 'TEST_DIR/t.qed': Invalid argument +can't open device TEST_DIR/t.qed: Could not open 'TEST_DIR/t.qed': Invalid argument no file open, try 'help open' *** done diff --git a/qemu/tests/qemu-iotests/117 b/qemu/tests/qemu-iotests/117 new file mode 100755 index 000000000..9385b3f8d --- /dev/null +++ b/qemu/tests/qemu-iotests/117 @@ -0,0 +1,85 @@ +#!/bin/bash +# +# Test case for shared BDS between backend trees +# +# Copyright (C) 2016 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 . +# + +# creator +owner=mreitz@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +_make_test_img 64k + +_launch_qemu + +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'qmp_capabilities' }" \ + 'return' + +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'blockdev-add', + 'arguments': { 'options': { 'id': 'protocol', + 'driver': 'file', + 'filename': '$TEST_IMG' } } }" \ + 'return' + +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'blockdev-add', + 'arguments': { 'options': { 'id': 'format', + 'driver': '$IMGFMT', + 'file': 'protocol' } } }" \ + 'return' + +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'human-monitor-command', + 'arguments': { 'command-line': 'qemu-io format \"write -P 42 0 64k\"' } }" \ + 'return' + +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'quit' }" \ + 'return' + +wait=1 _cleanup_qemu + +_check_test_img + +$QEMU_IO -c 'read -P 42 0 64k' "$TEST_IMG" | _filter_qemu_io + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/qemu/tests/qemu-iotests/117.out b/qemu/tests/qemu-iotests/117.out new file mode 100644 index 000000000..f52dc1a35 --- /dev/null +++ b/qemu/tests/qemu-iotests/117.out @@ -0,0 +1,14 @@ +QA output created by 117 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536 +{"return": {}} +{"return": {}} +{"return": {}} +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": ""} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} +No errors were found on the image. +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +*** done diff --git a/qemu/tests/qemu-iotests/118 b/qemu/tests/qemu-iotests/118 new file mode 100755 index 000000000..9e5951f64 --- /dev/null +++ b/qemu/tests/qemu-iotests/118 @@ -0,0 +1,677 @@ +#!/usr/bin/env python +# +# Test case for the QMP 'change' command and all other associated +# commands +# +# Copyright (C) 2015 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import os +import stat +import time +import iotests +from iotests import qemu_img + +old_img = os.path.join(iotests.test_dir, 'test0.img') +new_img = os.path.join(iotests.test_dir, 'test1.img') + +class ChangeBaseClass(iotests.QMPTestCase): + has_opened = False + has_closed = False + + def process_events(self): + for event in self.vm.get_qmp_events(wait=False): + if (event['event'] == 'DEVICE_TRAY_MOVED' and + event['data']['device'] == 'drive0'): + if event['data']['tray-open'] == False: + self.has_closed = True + else: + self.has_opened = True + + def wait_for_open(self): + if not self.has_real_tray: + return + + timeout = time.clock() + 3 + while not self.has_opened and time.clock() < timeout: + self.process_events() + if not self.has_opened: + self.fail('Timeout while waiting for the tray to open') + + def wait_for_close(self): + if not self.has_real_tray: + return + + timeout = time.clock() + 3 + while not self.has_closed and time.clock() < timeout: + self.process_events() + if not self.has_opened: + self.fail('Timeout while waiting for the tray to close') + +class GeneralChangeTestsBaseClass(ChangeBaseClass): + def test_change(self): + result = self.vm.qmp('change', device='drive0', target=new_img, + arg=iotests.imgfmt) + self.assert_qmp(result, 'return', {}) + + self.wait_for_open() + self.wait_for_close() + + result = self.vm.qmp('query-block') + if self.has_real_tray: + self.assert_qmp(result, 'return[0]/tray_open', False) + self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + + def test_blockdev_change_medium(self): + result = self.vm.qmp('blockdev-change-medium', device='drive0', + filename=new_img, + format=iotests.imgfmt) + self.assert_qmp(result, 'return', {}) + + self.wait_for_open() + self.wait_for_close() + + result = self.vm.qmp('query-block') + if self.has_real_tray: + self.assert_qmp(result, 'return[0]/tray_open', False) + self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + + def test_eject(self): + result = self.vm.qmp('eject', device='drive0', force=True) + self.assert_qmp(result, 'return', {}) + + self.wait_for_open() + + result = self.vm.qmp('query-block') + if self.has_real_tray: + self.assert_qmp(result, 'return[0]/tray_open', True) + self.assert_qmp_absent(result, 'return[0]/inserted') + + def test_tray_eject_change(self): + result = self.vm.qmp('eject', device='drive0', force=True) + self.assert_qmp(result, 'return', {}) + + self.wait_for_open() + + result = self.vm.qmp('query-block') + if self.has_real_tray: + self.assert_qmp(result, 'return[0]/tray_open', True) + self.assert_qmp_absent(result, 'return[0]/inserted') + + result = self.vm.qmp('blockdev-change-medium', device='drive0', + filename=new_img, + format=iotests.imgfmt) + self.assert_qmp(result, 'return', {}) + + self.wait_for_close() + + result = self.vm.qmp('query-block') + if self.has_real_tray: + self.assert_qmp(result, 'return[0]/tray_open', False) + self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + + def test_tray_open_close(self): + result = self.vm.qmp('blockdev-open-tray', device='drive0', force=True) + self.assert_qmp(result, 'return', {}) + + self.wait_for_open() + + result = self.vm.qmp('query-block') + if self.has_real_tray: + self.assert_qmp(result, 'return[0]/tray_open', True) + if self.was_empty == True: + self.assert_qmp_absent(result, 'return[0]/inserted') + else: + self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) + + result = self.vm.qmp('blockdev-close-tray', device='drive0') + self.assert_qmp(result, 'return', {}) + + if self.has_real_tray or not self.was_empty: + self.wait_for_close() + + result = self.vm.qmp('query-block') + if self.has_real_tray: + self.assert_qmp(result, 'return[0]/tray_open', False) + if self.was_empty == True: + self.assert_qmp_absent(result, 'return[0]/inserted') + else: + self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) + + def test_tray_eject_close(self): + result = self.vm.qmp('eject', device='drive0', force=True) + self.assert_qmp(result, 'return', {}) + + self.wait_for_open() + + result = self.vm.qmp('query-block') + if self.has_real_tray: + self.assert_qmp(result, 'return[0]/tray_open', True) + self.assert_qmp_absent(result, 'return[0]/inserted') + + result = self.vm.qmp('blockdev-close-tray', device='drive0') + self.assert_qmp(result, 'return', {}) + + self.wait_for_close() + + result = self.vm.qmp('query-block') + if self.has_real_tray: + self.assert_qmp(result, 'return[0]/tray_open', False) + self.assert_qmp_absent(result, 'return[0]/inserted') + + def test_tray_open_change(self): + result = self.vm.qmp('blockdev-open-tray', device='drive0', force=True) + self.assert_qmp(result, 'return', {}) + + self.wait_for_open() + + result = self.vm.qmp('query-block') + if self.has_real_tray: + self.assert_qmp(result, 'return[0]/tray_open', True) + if self.was_empty == True: + self.assert_qmp_absent(result, 'return[0]/inserted') + else: + self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) + + result = self.vm.qmp('blockdev-change-medium', device='drive0', + filename=new_img, + format=iotests.imgfmt) + self.assert_qmp(result, 'return', {}) + + self.wait_for_close() + + result = self.vm.qmp('query-block') + if self.has_real_tray: + self.assert_qmp(result, 'return[0]/tray_open', False) + self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + + def test_cycle(self): + result = self.vm.qmp('blockdev-add', + options={'node-name': 'new', + 'driver': iotests.imgfmt, + 'file': {'filename': new_img, + 'driver': 'file'}}) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('blockdev-open-tray', device='drive0', force=True) + self.assert_qmp(result, 'return', {}) + + self.wait_for_open() + + result = self.vm.qmp('query-block') + if self.has_real_tray: + self.assert_qmp(result, 'return[0]/tray_open', True) + if self.was_empty == True: + self.assert_qmp_absent(result, 'return[0]/inserted') + else: + self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) + + result = self.vm.qmp('x-blockdev-remove-medium', device='drive0') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('query-block') + if self.has_real_tray: + self.assert_qmp(result, 'return[0]/tray_open', True) + self.assert_qmp_absent(result, 'return[0]/inserted') + + result = self.vm.qmp('x-blockdev-insert-medium', device='drive0', + node_name='new') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('query-block') + if self.has_real_tray: + self.assert_qmp(result, 'return[0]/tray_open', True) + self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + + result = self.vm.qmp('blockdev-close-tray', device='drive0') + self.assert_qmp(result, 'return', {}) + + self.wait_for_close() + + result = self.vm.qmp('query-block') + if self.has_real_tray: + self.assert_qmp(result, 'return[0]/tray_open', False) + self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + + def test_close_on_closed(self): + result = self.vm.qmp('blockdev-close-tray', device='drive0') + # Should be a no-op + self.assert_qmp(result, 'return', {}) + self.assertEquals(self.vm.get_qmp_events(wait=False), []) + + def test_remove_on_closed(self): + if not self.has_real_tray: + return + + result = self.vm.qmp('x-blockdev-remove-medium', device='drive0') + self.assert_qmp(result, 'error/class', 'GenericError') + + def test_insert_on_closed(self): + if not self.has_real_tray: + return + + result = self.vm.qmp('blockdev-add', + options={'node-name': 'new', + 'driver': iotests.imgfmt, + 'file': {'filename': new_img, + 'driver': 'file'}}) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('x-blockdev-insert-medium', device='drive0', + node_name='new') + self.assert_qmp(result, 'error/class', 'GenericError') + +class TestInitiallyFilled(GeneralChangeTestsBaseClass): + was_empty = False + + def setUp(self, media, interface): + qemu_img('create', '-f', iotests.imgfmt, old_img, '1440k') + qemu_img('create', '-f', iotests.imgfmt, new_img, '1440k') + self.vm = iotests.VM().add_drive(old_img, 'media=%s' % media, interface) + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + os.remove(old_img) + os.remove(new_img) + + def test_insert_on_filled(self): + result = self.vm.qmp('blockdev-add', + options={'node-name': 'new', + 'driver': iotests.imgfmt, + 'file': {'filename': new_img, + 'driver': 'file'}}) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('blockdev-open-tray', device='drive0') + self.assert_qmp(result, 'return', {}) + + self.wait_for_open() + + result = self.vm.qmp('x-blockdev-insert-medium', device='drive0', + node_name='new') + self.assert_qmp(result, 'error/class', 'GenericError') + +class TestInitiallyEmpty(GeneralChangeTestsBaseClass): + was_empty = True + + def setUp(self, media, interface): + qemu_img('create', '-f', iotests.imgfmt, new_img, '1440k') + self.vm = iotests.VM().add_drive(None, 'media=%s' % media, interface) + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + os.remove(new_img) + + def test_remove_on_empty(self): + result = self.vm.qmp('blockdev-open-tray', device='drive0') + self.assert_qmp(result, 'return', {}) + + self.wait_for_open() + + result = self.vm.qmp('x-blockdev-remove-medium', device='drive0') + # Should be a no-op + self.assert_qmp(result, 'return', {}) + +class TestCDInitiallyFilled(TestInitiallyFilled): + TestInitiallyFilled = TestInitiallyFilled + has_real_tray = True + + def setUp(self): + self.TestInitiallyFilled.setUp(self, 'cdrom', 'ide') + +class TestCDInitiallyEmpty(TestInitiallyEmpty): + TestInitiallyEmpty = TestInitiallyEmpty + has_real_tray = True + + def setUp(self): + self.TestInitiallyEmpty.setUp(self, 'cdrom', 'ide') + +class TestFloppyInitiallyFilled(TestInitiallyFilled): + TestInitiallyFilled = TestInitiallyFilled + has_real_tray = False + + def setUp(self): + self.TestInitiallyFilled.setUp(self, 'disk', 'floppy') + +class TestFloppyInitiallyEmpty(TestInitiallyEmpty): + TestInitiallyEmpty = TestInitiallyEmpty + has_real_tray = False + + def setUp(self): + self.TestInitiallyEmpty.setUp(self, 'disk', 'floppy') + # FDDs not having a real tray and there not being a medium inside the + # tray at startup means the tray will be considered open + self.has_opened = True + +class TestChangeReadOnly(ChangeBaseClass): + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, old_img, '1440k') + qemu_img('create', '-f', iotests.imgfmt, new_img, '1440k') + self.vm = iotests.VM() + + def tearDown(self): + self.vm.shutdown() + os.chmod(old_img, 0666) + os.chmod(new_img, 0666) + os.remove(old_img) + os.remove(new_img) + + def test_ro_ro_retain(self): + os.chmod(old_img, 0444) + os.chmod(new_img, 0444) + self.vm.add_drive(old_img, 'media=disk,read-only=on', 'floppy') + self.vm.launch() + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', True) + self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) + + result = self.vm.qmp('blockdev-change-medium', device='drive0', + filename=new_img, + format=iotests.imgfmt, + read_only_mode='retain') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', True) + self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + + def test_ro_rw_retain(self): + os.chmod(old_img, 0444) + self.vm.add_drive(old_img, 'media=disk,read-only=on', 'floppy') + self.vm.launch() + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', True) + self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) + + result = self.vm.qmp('blockdev-change-medium', device='drive0', + filename=new_img, + format=iotests.imgfmt, + read_only_mode='retain') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', True) + self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + + def test_rw_ro_retain(self): + os.chmod(new_img, 0444) + self.vm.add_drive(old_img, 'media=disk', 'floppy') + self.vm.launch() + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', False) + self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) + + result = self.vm.qmp('blockdev-change-medium', device='drive0', + filename=new_img, + format=iotests.imgfmt, + read_only_mode='retain') + self.assert_qmp(result, 'error/class', 'GenericError') + + self.assertEquals(self.vm.get_qmp_events(wait=False), []) + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', False) + self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) + + def test_ro_rw(self): + os.chmod(old_img, 0444) + self.vm.add_drive(old_img, 'media=disk,read-only=on', 'floppy') + self.vm.launch() + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', True) + self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) + + result = self.vm.qmp('blockdev-change-medium', + device='drive0', + filename=new_img, + format=iotests.imgfmt, + read_only_mode='read-write') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', False) + self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + + def test_rw_ro(self): + os.chmod(new_img, 0444) + self.vm.add_drive(old_img, 'media=disk', 'floppy') + self.vm.launch() + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', False) + self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) + + result = self.vm.qmp('blockdev-change-medium', + device='drive0', + filename=new_img, + format=iotests.imgfmt, + read_only_mode='read-only') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', True) + self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + + def test_make_rw_ro(self): + self.vm.add_drive(old_img, 'media=disk', 'floppy') + self.vm.launch() + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', False) + self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) + + result = self.vm.qmp('blockdev-change-medium', + device='drive0', + filename=new_img, + format=iotests.imgfmt, + read_only_mode='read-only') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', True) + self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + + def test_make_ro_rw(self): + os.chmod(new_img, 0444) + self.vm.add_drive(old_img, 'media=disk', 'floppy') + self.vm.launch() + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', False) + self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) + + result = self.vm.qmp('blockdev-change-medium', + device='drive0', + filename=new_img, + format=iotests.imgfmt, + read_only_mode='read-write') + self.assert_qmp(result, 'error/class', 'GenericError') + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', False) + self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) + + def test_make_rw_ro_by_retain(self): + os.chmod(old_img, 0444) + self.vm.add_drive(old_img, 'media=disk,read-only=on', 'floppy') + self.vm.launch() + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', True) + self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) + + result = self.vm.qmp('blockdev-change-medium', device='drive0', + filename=new_img, + format=iotests.imgfmt, + read_only_mode='retain') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', True) + self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + + def test_make_ro_rw_by_retain(self): + os.chmod(new_img, 0444) + self.vm.add_drive(old_img, 'media=disk', 'floppy') + self.vm.launch() + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', False) + self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) + + result = self.vm.qmp('blockdev-change-medium', device='drive0', + filename=new_img, + format=iotests.imgfmt, + read_only_mode='retain') + self.assert_qmp(result, 'error/class', 'GenericError') + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', False) + self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) + + def test_rw_ro_cycle(self): + os.chmod(new_img, 0444) + self.vm.add_drive(old_img, 'media=disk', 'floppy') + self.vm.launch() + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', False) + self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) + + result = self.vm.qmp('blockdev-add', + options={'node-name': 'new', + 'driver': iotests.imgfmt, + 'read-only': True, + 'file': {'filename': new_img, + 'driver': 'file'}}) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', False) + self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) + + result = self.vm.qmp('x-blockdev-remove-medium', device='drive0') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('query-block') + self.assert_qmp_absent(result, 'return[0]/inserted') + + result = self.vm.qmp('x-blockdev-insert-medium', device='drive0', + node_name='new') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', True) + self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/ro', True) + self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + +GeneralChangeTestsBaseClass = None +TestInitiallyFilled = None +TestInitiallyEmpty = None + + +class TestBlockJobsAfterCycle(ChangeBaseClass): + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, old_img, '1M') + + self.vm = iotests.VM() + self.vm.launch() + + result = self.vm.qmp('blockdev-add', + options={'id': 'drive0', + 'driver': 'null-co'}) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/image/format', 'null-co') + + # For device-less BBs, calling blockdev-open-tray or blockdev-close-tray + # is not necessary + result = self.vm.qmp('x-blockdev-remove-medium', device='drive0') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('query-block') + self.assert_qmp_absent(result, 'return[0]/inserted') + + result = self.vm.qmp('blockdev-add', + options={'node-name': 'node0', + 'driver': iotests.imgfmt, + 'file': {'filename': old_img, + 'driver': 'file'}}) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('x-blockdev-insert-medium', device='drive0', + node_name='node0') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) + + def tearDown(self): + self.vm.shutdown() + os.remove(old_img) + try: + os.remove(new_img) + except OSError: + pass + + def test_snapshot_and_commit(self): + # We need backing file support + if iotests.imgfmt != 'qcow2' and iotests.imgfmt != 'qed': + return + + result = self.vm.qmp('blockdev-snapshot-sync', device='drive0', + snapshot_file=new_img, + format=iotests.imgfmt) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + self.assert_qmp(result, + 'return[0]/inserted/image/backing-image/filename', + old_img) + + result = self.vm.qmp('block-commit', device='drive0') + self.assert_qmp(result, 'return', {}) + + self.vm.event_wait(name='BLOCK_JOB_READY') + + result = self.vm.qmp('query-block-jobs') + self.assert_qmp(result, 'return[0]/device', 'drive0') + + result = self.vm.qmp('block-job-complete', device='drive0') + self.assert_qmp(result, 'return', {}) + + self.vm.event_wait(name='BLOCK_JOB_COMPLETED') + + +if __name__ == '__main__': + if iotests.qemu_default_machine != 'pc': + # We need floppy and IDE CD-ROM + iotests.notrun('not suitable for this machine type: %s' % + iotests.qemu_default_machine) + # Need to support image creation + iotests.main(supported_fmts=['vpc', 'parallels', 'qcow', 'vdi', 'qcow2', + 'vmdk', 'raw', 'vhdx', 'qed']) diff --git a/qemu/tests/qemu-iotests/118.out b/qemu/tests/qemu-iotests/118.out new file mode 100644 index 000000000..6a917130b --- /dev/null +++ b/qemu/tests/qemu-iotests/118.out @@ -0,0 +1,5 @@ +........................................................... +---------------------------------------------------------------------- +Ran 59 tests + +OK diff --git a/qemu/tests/qemu-iotests/119 b/qemu/tests/qemu-iotests/119 index 9a11f1b92..4f34fb434 100755 --- a/qemu/tests/qemu-iotests/119 +++ b/qemu/tests/qemu-iotests/119 @@ -26,7 +26,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -49,7 +48,7 @@ echo "{'execute': 'qmp_capabilities'} {'execute': 'human-monitor-command', 'arguments': {'command-line': 'qemu-io drv \"read -P 0 0 64k\"'}} {'execute': 'quit'}" \ - | $QEMU -drive id=drv,if=none,file="$TEST_IMG",driver=nbd \ + | $QEMU -nographic -drive id=drv,if=none,file="$TEST_IMG",driver=nbd \ -qmp stdio -nodefaults \ | _filter_qmp | _filter_qemu_io diff --git a/qemu/tests/qemu-iotests/120 b/qemu/tests/qemu-iotests/120 index 9f1307876..4f88a67fe 100755 --- a/qemu/tests/qemu-iotests/120 +++ b/qemu/tests/qemu-iotests/120 @@ -26,7 +26,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -49,7 +48,7 @@ echo "{'execute': 'qmp_capabilities'} {'execute': 'human-monitor-command', 'arguments': {'command-line': 'qemu-io drv \"write -P 42 0 64k\"'}} {'execute': 'quit'}" \ - | $QEMU -qmp stdio -nodefaults \ + | $QEMU -qmp stdio -nographic -nodefaults \ -drive id=drv,if=none,file="$TEST_IMG",driver=raw,file.driver=$IMGFMT \ | _filter_qmp | _filter_qemu_io $QEMU_IO -c 'read -P 42 0 64k' "$TEST_IMG" | _filter_qemu_io diff --git a/qemu/tests/qemu-iotests/121 b/qemu/tests/qemu-iotests/121 index 0912c3f0c..1307b4e32 100755 --- a/qemu/tests/qemu-iotests/121 +++ b/qemu/tests/qemu-iotests/121 @@ -25,7 +25,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/121.out b/qemu/tests/qemu-iotests/121.out index ff18e2c61..5961a44cd 100644 --- a/qemu/tests/qemu-iotests/121.out +++ b/qemu/tests/qemu-iotests/121.out @@ -4,7 +4,7 @@ QA output created by 121 --- Test 1 --- -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=66060288 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=66060288 preallocation=metadata Image resized. wrote 1049600/1049600 bytes at offset 65011712 1.001 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -14,7 +14,7 @@ No errors were found on the image. --- Test 2 --- -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=66061312 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=66061312 preallocation=metadata Image resized. wrote 133120/133120 bytes at offset 66060288 130 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/qemu/tests/qemu-iotests/122 b/qemu/tests/qemu-iotests/122 index 350ca9c46..45b359c2b 100755 --- a/qemu/tests/qemu-iotests/122 +++ b/qemu/tests/qemu-iotests/122 @@ -25,7 +25,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/122.out b/qemu/tests/qemu-iotests/122.out index 1f853b9e9..98814de5d 100644 --- a/qemu/tests/qemu-iotests/122.out +++ b/qemu/tests/qemu-iotests/122.out @@ -5,7 +5,7 @@ wrote 67108864/67108864 bytes at offset 0 === Check allocation status regression with -B === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base wrote 3145728/3145728 bytes at offset 0 3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Offset Length File @@ -14,7 +14,7 @@ Offset Length File === Check that zero clusters are kept in overlay === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base wrote 3145728/3145728 bytes at offset 0 3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 3145728/3145728 bytes at offset 0 @@ -112,20 +112,18 @@ read 3145728/3145728 bytes at offset 0 3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 63963136/63963136 bytes at offset 3145728 61 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 6291456, "depth": 0, "zero": false, "data": true, "offset": 327680}, -{ "start": 6291456, "length": 60817408, "depth": 0, "zero": true, "data": false}] +[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": 327680}] convert -c -S 0: read 3145728/3145728 bytes at offset 0 3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 63963136/63963136 bytes at offset 3145728 61 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 6291456, "depth": 0, "zero": false, "data": true}, -{ "start": 6291456, "length": 60817408, "depth": 0, "zero": true, "data": false}] +[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true}] Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 wrote 33554432/33554432 bytes at offset 0 32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base wrote 3145728/3145728 bytes at offset 0 3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/qemu/tests/qemu-iotests/123 b/qemu/tests/qemu-iotests/123 index ad608035d..b18e3fca9 100755 --- a/qemu/tests/qemu-iotests/123 +++ b/qemu/tests/qemu-iotests/123 @@ -25,7 +25,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/124 b/qemu/tests/qemu-iotests/124 index 9ccd11809..de7cdbe00 100644 --- a/qemu/tests/qemu-iotests/124 +++ b/qemu/tests/qemu-iotests/124 @@ -36,6 +36,23 @@ def try_remove(img): pass +def transaction_action(action, **kwargs): + return { + 'type': action, + 'data': dict((k.replace('_', '-'), v) for k, v in kwargs.iteritems()) + } + + +def transaction_bitmap_clear(node, name, **kwargs): + return transaction_action('block-dirty-bitmap-clear', + node=node, name=name, **kwargs) + + +def transaction_drive_backup(device, target, **kwargs): + return transaction_action('drive-backup', device=device, target=target, + **kwargs) + + class Bitmap: def __init__(self, name, drive): self.name = name @@ -74,24 +91,31 @@ class Bitmap: try_remove(image) -class TestIncrementalBackup(iotests.QMPTestCase): - def setUp(self): +class TestIncrementalBackupBase(iotests.QMPTestCase): + def __init__(self, *args): + super(TestIncrementalBackupBase, self).__init__(*args) self.bitmaps = list() self.files = list() self.drives = list() self.vm = iotests.VM() self.err_img = os.path.join(iotests.test_dir, 'err.%s' % iotests.imgfmt) + + def setUp(self): # Create a base image with a distinctive patterning drive0 = self.add_node('drive0') self.img_create(drive0['file'], drive0['fmt']) self.vm.add_drive(drive0['file']) - io_write_patterns(drive0['file'], (('0x41', 0, 512), - ('0xd5', '1M', '32k'), - ('0xdc', '32M', '124k'))) + self.write_default_pattern(drive0['file']) self.vm.launch() + def write_default_pattern(self, target): + io_write_patterns(target, (('0x41', 0, 512), + ('0xd5', '1M', '32k'), + ('0xdc', '32M', '124k'))) + + def add_node(self, node_id, fmt=iotests.imgfmt, path=None, backup=None): if path is None: path = os.path.join(iotests.test_dir, '%s.%s' % (node_id, fmt)) @@ -108,23 +132,28 @@ class TestIncrementalBackup(iotests.QMPTestCase): def img_create(self, img, fmt=iotests.imgfmt, size='64M', - parent=None, parentFormat=None): + parent=None, parentFormat=None, **kwargs): + optargs = [] + for k,v in kwargs.iteritems(): + optargs = optargs + ['-o', '%s=%s' % (k,v)] + args = ['create', '-f', fmt] + optargs + [img, size] if parent: if parentFormat is None: parentFormat = fmt - iotests.qemu_img('create', '-f', fmt, img, size, - '-b', parent, '-F', parentFormat) - else: - iotests.qemu_img('create', '-f', fmt, img, size) + args = args + ['-b', parent, '-F', parentFormat] + iotests.qemu_img(*args) self.files.append(img) def do_qmp_backup(self, error='Input/output error', **kwargs): res = self.vm.qmp('drive-backup', **kwargs) self.assert_qmp(res, 'return', {}) + return self.wait_qmp_backup(kwargs['device'], error) + + def wait_qmp_backup(self, device, error='Input/output error'): event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED", - match={'data': {'device': kwargs['device']}}) + match={'data': {'device': device}}) self.assertNotEqual(event, None) try: @@ -139,6 +168,12 @@ class TestIncrementalBackup(iotests.QMPTestCase): return False + def wait_qmp_backup_cancelled(self, device): + event = self.vm.event_wait(name='BLOCK_JOB_CANCELLED', + match={'data': {'device': device}}) + self.assertNotEqual(event, None) + + def create_anchor_backup(self, drive=None): if drive is None: drive = self.drives[-1] @@ -233,6 +268,16 @@ class TestIncrementalBackup(iotests.QMPTestCase): self.check_backups() + def tearDown(self): + self.vm.shutdown() + for bitmap in self.bitmaps: + bitmap.cleanup() + for filename in self.files: + try_remove(filename) + + + +class TestIncrementalBackup(TestIncrementalBackupBase): def test_incremental_simple(self): ''' Test: Create and verify three incremental backups. @@ -264,19 +309,110 @@ class TestIncrementalBackup(iotests.QMPTestCase): return self.do_incremental_simple(granularity=131072) - def test_incremental_failure(self): - '''Test: Verify backups made after a failure are correct. + def test_larger_cluster_target(self): + ''' + Test: Create and verify backups made to a larger cluster size target. - Simulate a failure during an incremental backup block job, - emulate additional writes, then create another incremental backup - afterwards and verify that the backup created is correct. + With a default granularity of 64KiB, verify that backups made to a + larger cluster size target of 128KiB without a backing file works. ''' + drive0 = self.drives[0] + + # Create a cluster_size=128k full backup / "anchor" backup + self.img_create(drive0['backup'], cluster_size='128k') + self.assertTrue(self.do_qmp_backup(device=drive0['id'], sync='full', + format=drive0['fmt'], + target=drive0['backup'], + mode='existing')) + + # Create bitmap and dirty it with some new writes. + # overwrite [32736, 32799] which will dirty bitmap clusters at + # 32M-64K and 32M. 32M+64K will be left undirtied. + bitmap0 = self.add_bitmap('bitmap0', drive0) + self.hmp_io_writes(drive0['id'], + (('0xab', 0, 512), + ('0xfe', '16M', '256k'), + ('0x64', '32736k', '64k'))) + + + # Prepare a cluster_size=128k backup target without a backing file. + (target, _) = bitmap0.new_target() + self.img_create(target, bitmap0.drive['fmt'], cluster_size='128k') + + # Perform Incremental Backup + self.assertTrue(self.do_qmp_backup(device=bitmap0.drive['id'], + sync='incremental', + bitmap=bitmap0.name, + format=bitmap0.drive['fmt'], + target=target, + mode='existing')) + self.make_reference_backup(bitmap0) + + # Add the backing file, then compare and exit. + iotests.qemu_img('rebase', '-f', drive0['fmt'], '-u', '-b', + drive0['backup'], '-F', drive0['fmt'], target) + self.vm.shutdown() + self.check_backups() + + + def test_incremental_transaction(self): + '''Test: Verify backups made from transactionally created bitmaps. + + Create a bitmap "before" VM execution begins, then create a second + bitmap AFTER writes have already occurred. Use transactions to create + a full backup and synchronize both bitmaps to this backup. + Create an incremental backup through both bitmaps and verify that + both backups match the current drive0 image. + ''' + + drive0 = self.drives[0] + bitmap0 = self.add_bitmap('bitmap0', drive0) + self.hmp_io_writes(drive0['id'], (('0xab', 0, 512), + ('0xfe', '16M', '256k'), + ('0x64', '32736k', '64k'))) + bitmap1 = self.add_bitmap('bitmap1', drive0) + + result = self.vm.qmp('transaction', actions=[ + transaction_bitmap_clear(bitmap0.drive['id'], bitmap0.name), + transaction_bitmap_clear(bitmap1.drive['id'], bitmap1.name), + transaction_drive_backup(drive0['id'], drive0['backup'], + sync='full', format=drive0['fmt']) + ]) + self.assert_qmp(result, 'return', {}) + self.wait_until_completed(drive0['id']) + self.files.append(drive0['backup']) + + self.hmp_io_writes(drive0['id'], (('0x9a', 0, 512), + ('0x55', '8M', '352k'), + ('0x78', '15872k', '1M'))) + # Both bitmaps should be correctly in sync. + self.create_incremental(bitmap0) + self.create_incremental(bitmap1) + self.vm.shutdown() + self.check_backups() - # Create a blkdebug interface to this img as 'drive1', - # but don't actually create a new image. - drive1 = self.add_node('drive1', self.drives[0]['fmt'], - path=self.drives[0]['file'], - backup=self.drives[0]['backup']) + + def test_transaction_failure(self): + '''Test: Verify backups made from a transaction that partially fails. + + Add a second drive with its own unique pattern, and add a bitmap to each + drive. Use blkdebug to interfere with the backup on just one drive and + attempt to create a coherent incremental backup across both drives. + + verify a failure in one but not both, then delete the failed stubs and + re-run the same transaction. + + verify that both incrementals are created successfully. + ''' + + # Create a second drive, with pattern: + drive1 = self.add_node('drive1') + self.img_create(drive1['file'], drive1['fmt']) + io_write_patterns(drive1['file'], (('0x14', 0, 512), + ('0x5d', '1M', '32k'), + ('0xcd', '32M', '124k'))) + + # Create a blkdebug interface to this img as 'drive1' result = self.vm.qmp('blockdev-add', options={ 'id': drive1['id'], 'driver': drive1['fmt'], @@ -302,21 +438,73 @@ class TestIncrementalBackup(iotests.QMPTestCase): }) self.assert_qmp(result, 'return', {}) - self.create_anchor_backup(self.drives[0]) - self.add_bitmap('bitmap0', drive1) - # Note: at this point, during a normal execution, - # Assume that the VM resumes and begins issuing IO requests here. + # Create bitmaps and full backups for both drives + drive0 = self.drives[0] + dr0bm0 = self.add_bitmap('bitmap0', drive0) + dr1bm0 = self.add_bitmap('bitmap0', drive1) + self.create_anchor_backup(drive0) + self.create_anchor_backup(drive1) + self.assert_no_active_block_jobs() + self.assertFalse(self.vm.get_qmp_events(wait=False)) - self.hmp_io_writes(drive1['id'], (('0xab', 0, 512), + # Emulate some writes + self.hmp_io_writes(drive0['id'], (('0xab', 0, 512), ('0xfe', '16M', '256k'), ('0x64', '32736k', '64k'))) + self.hmp_io_writes(drive1['id'], (('0xba', 0, 512), + ('0xef', '16M', '256k'), + ('0x46', '32736k', '64k'))) + + # Create incremental backup targets + target0 = self.prepare_backup(dr0bm0) + target1 = self.prepare_backup(dr1bm0) + + # Ask for a new incremental backup per-each drive, + # expecting drive1's backup to fail: + transaction = [ + transaction_drive_backup(drive0['id'], target0, sync='incremental', + format=drive0['fmt'], mode='existing', + bitmap=dr0bm0.name), + transaction_drive_backup(drive1['id'], target1, sync='incremental', + format=drive1['fmt'], mode='existing', + bitmap=dr1bm0.name) + ] + result = self.vm.qmp('transaction', actions=transaction, + properties={'completion-mode': 'grouped'} ) + self.assert_qmp(result, 'return', {}) - result = self.create_incremental(validate=False) - self.assertFalse(result) - self.hmp_io_writes(drive1['id'], (('0x9a', 0, 512), - ('0x55', '8M', '352k'), - ('0x78', '15872k', '1M'))) - self.create_incremental() + # Observe that drive0's backup is cancelled and drive1 completes with + # an error. + self.wait_qmp_backup_cancelled(drive0['id']) + self.assertFalse(self.wait_qmp_backup(drive1['id'])) + error = self.vm.event_wait('BLOCK_JOB_ERROR') + self.assert_qmp(error, 'data', {'device': drive1['id'], + 'action': 'report', + 'operation': 'read'}) + self.assertFalse(self.vm.get_qmp_events(wait=False)) + self.assert_no_active_block_jobs() + + # Delete drive0's successful target and eliminate our record of the + # unsuccessful drive1 target. Then re-run the same transaction. + dr0bm0.del_target() + dr1bm0.del_target() + target0 = self.prepare_backup(dr0bm0) + target1 = self.prepare_backup(dr1bm0) + + # Re-run the exact same transaction. + result = self.vm.qmp('transaction', actions=transaction, + properties={'completion-mode':'grouped'}) + self.assert_qmp(result, 'return', {}) + + # Both should complete successfully this time. + self.assertTrue(self.wait_qmp_backup(drive0['id'])) + self.assertTrue(self.wait_qmp_backup(drive1['id'])) + self.make_reference_backup(dr0bm0) + self.make_reference_backup(dr1bm0) + self.assertFalse(self.vm.get_qmp_events(wait=False)) + self.assert_no_active_block_jobs() + + # And the images should of course validate. self.vm.shutdown() self.check_backups() @@ -351,12 +539,66 @@ class TestIncrementalBackup(iotests.QMPTestCase): granularity=64000) - def tearDown(self): +class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase): + '''Incremental backup tests that utilize a BlkDebug filter on drive0.''' + + def setUp(self): + drive0 = self.add_node('drive0') + self.img_create(drive0['file'], drive0['fmt']) + self.write_default_pattern(drive0['file']) + self.vm.launch() + + def test_incremental_failure(self): + '''Test: Verify backups made after a failure are correct. + + Simulate a failure during an incremental backup block job, + emulate additional writes, then create another incremental backup + afterwards and verify that the backup created is correct. + ''' + + drive0 = self.drives[0] + result = self.vm.qmp('blockdev-add', options={ + 'id': drive0['id'], + 'driver': drive0['fmt'], + 'file': { + 'driver': 'blkdebug', + 'image': { + 'driver': 'file', + 'filename': drive0['file'] + }, + 'set-state': [{ + 'event': 'flush_to_disk', + 'state': 1, + 'new_state': 2 + }], + 'inject-error': [{ + 'event': 'read_aio', + 'errno': 5, + 'state': 2, + 'immediately': False, + 'once': True + }], + } + }) + self.assert_qmp(result, 'return', {}) + + self.create_anchor_backup(drive0) + self.add_bitmap('bitmap0', drive0) + # Note: at this point, during a normal execution, + # Assume that the VM resumes and begins issuing IO requests here. + + self.hmp_io_writes(drive0['id'], (('0xab', 0, 512), + ('0xfe', '16M', '256k'), + ('0x64', '32736k', '64k'))) + + result = self.create_incremental(validate=False) + self.assertFalse(result) + self.hmp_io_writes(drive0['id'], (('0x9a', 0, 512), + ('0x55', '8M', '352k'), + ('0x78', '15872k', '1M'))) + self.create_incremental() self.vm.shutdown() - for bitmap in self.bitmaps: - bitmap.cleanup() - for filename in self.files: - try_remove(filename) + self.check_backups() if __name__ == '__main__': diff --git a/qemu/tests/qemu-iotests/124.out b/qemu/tests/qemu-iotests/124.out index 2f7d3902f..36376bed8 100644 --- a/qemu/tests/qemu-iotests/124.out +++ b/qemu/tests/qemu-iotests/124.out @@ -1,5 +1,5 @@ -....... +.......... ---------------------------------------------------------------------- -Ran 7 tests +Ran 10 tests OK diff --git a/qemu/tests/qemu-iotests/128 b/qemu/tests/qemu-iotests/128 index e2a0f2f89..0976a1813 100755 --- a/qemu/tests/qemu-iotests/128 +++ b/qemu/tests/qemu-iotests/128 @@ -25,12 +25,16 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! devname="eiodev$$" sudo="" +_sudo_qemu_io_wrapper() +{ + (exec $sudo "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@") +} + _setup_eiodev() { # This test should either be run as root or with passwordless sudo @@ -76,7 +80,9 @@ TEST_IMG="/dev/mapper/$devname" echo echo "== reading from error device ==" # Opening image should succeed but the read operation should fail -$sudo $QEMU_IO --format "$IMGFMT" --nocache -c "read 0 65536" "$TEST_IMG" | _filter_qemu_io +_sudo_qemu_io_wrapper --format "$IMGFMT" --nocache \ + -c "read 0 65536" "$TEST_IMG" \ + | _filter_qemu_io # success, all done echo "*** done" diff --git a/qemu/tests/qemu-iotests/130 b/qemu/tests/qemu-iotests/130 index bc26247e3..ecc8a5ba1 100755 --- a/qemu/tests/qemu-iotests/130 +++ b/qemu/tests/qemu-iotests/130 @@ -27,7 +27,6 @@ seq="$(basename $0)" echo "QA output created by $seq" here="$PWD" -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() @@ -59,8 +58,8 @@ echo # bdrv_make_empty() involves a header update for qcow2 # Test that a backing file isn't written -_launch_qemu -drive file="$TEST_IMG",backing.file.filename="$TEST_IMG.base" -_send_qemu_cmd $QEMU_HANDLE "commit ide0-hd0" "(qemu)" +_launch_qemu -drive id=testdisk,file="$TEST_IMG",backing.file.filename="$TEST_IMG.base" +_send_qemu_cmd $QEMU_HANDLE "commit testdisk" "(qemu)" _send_qemu_cmd $QEMU_HANDLE '' '(qemu)' _cleanup_qemu _img_info | _filter_img_info @@ -68,8 +67,8 @@ _img_info | _filter_img_info # Make sure that if there was a backing file that was just overridden on the # command line, that backing file is retained, with the right format _make_test_img -F raw -b "$TEST_IMG.orig" 64M -_launch_qemu -drive file="$TEST_IMG",backing.file.filename="$TEST_IMG.base",backing.driver=$IMGFMT -_send_qemu_cmd $QEMU_HANDLE "commit ide0-hd0" "(qemu)" +_launch_qemu -drive id=testdisk,file="$TEST_IMG",backing.file.filename="$TEST_IMG.base",backing.driver=$IMGFMT +_send_qemu_cmd $QEMU_HANDLE "commit testdisk" "(qemu)" _send_qemu_cmd $QEMU_HANDLE '' '(qemu)' _cleanup_qemu _img_info | _filter_img_info diff --git a/qemu/tests/qemu-iotests/130.out b/qemu/tests/qemu-iotests/130.out index ea68b5d28..ae95b5027 100644 --- a/qemu/tests/qemu-iotests/130.out +++ b/qemu/tests/qemu-iotests/130.out @@ -9,14 +9,14 @@ virtual size: 64M (67108864 bytes) === HMP commit === QEMU X.Y.Z monitor - type 'help' for more information -(qemu) ccocomcommcommicommitcommit commit icommit idcommit idecommit ide0commit ide0-commit ide0-hcommit ide0-hdcommit ide0-hd0 +(qemu) ccocomcommcommicommitcommit commit tcommit tecommit tescommit testcommit testdcommit testdicommit testdiscommit testdisk (qemu) image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 64M (67108864 bytes) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.orig' backing_fmt='raw' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.orig backing_fmt=raw QEMU X.Y.Z monitor - type 'help' for more information -(qemu) ccocomcommcommicommitcommit commit icommit idcommit idecommit ide0commit ide0-commit ide0-hcommit ide0-hdcommit ide0-hd0 +(qemu) ccocomcommcommicommitcommit commit tcommit tecommit tescommit testcommit testdcommit testdicommit testdiscommit testdisk (qemu) image: TEST_DIR/t.IMGFMT file format: IMGFMT @@ -32,7 +32,7 @@ wrote 4096/4096 bytes at offset 0 image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 64M (67108864 bytes) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.orig' backing_fmt='raw' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.orig backing_fmt=raw wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) image: TEST_DIR/t.IMGFMT diff --git a/qemu/tests/qemu-iotests/131 b/qemu/tests/qemu-iotests/131 index 4873f40e9..94a9ae76a 100755 --- a/qemu/tests/qemu-iotests/131 +++ b/qemu/tests/qemu-iotests/131 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/131.out b/qemu/tests/qemu-iotests/131.out index 021a04c81..ae2412ebf 100644 --- a/qemu/tests/qemu-iotests/131.out +++ b/qemu/tests/qemu-iotests/131.out @@ -22,7 +22,7 @@ read 32768/32768 bytes at offset 163840 read 32768/32768 bytes at offset 0 32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == Corrupt image == -qemu-io: can't open device TEST_DIR/t.parallels: parallels: Image was not closed correctly; cannot be opened read/write +can't open device TEST_DIR/t.parallels: parallels: Image was not closed correctly; cannot be opened read/write no file open, try 'help open' ERROR image was not closed correctly diff --git a/qemu/tests/qemu-iotests/133 b/qemu/tests/qemu-iotests/133 new file mode 100755 index 000000000..9d35a6a1c --- /dev/null +++ b/qemu/tests/qemu-iotests/133 @@ -0,0 +1,89 @@ +#!/bin/bash +# +# Test for reopen +# +# Copyright (C) 2015 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +TEST_IMG="$TEST_IMG.base" _make_test_img 64M +_make_test_img -b "$TEST_IMG.base" + +echo +echo "=== Check that node-name can't be changed ===" +echo + +$QEMU_IO -c 'reopen -o node-name=foo' $TEST_IMG +$QEMU_IO -c 'reopen -o file.node-name=foo' $TEST_IMG +$QEMU_IO -c 'reopen -o backing.node-name=foo' $TEST_IMG + +echo +echo "=== Check that unchanged node-name is okay ===" +echo + +# Explicitly repeated +$QEMU_IO -c "open -o node-name=foo $TEST_IMG" -c 'reopen -o node-name=foo' +$QEMU_IO -c "open -o file.node-name=foo $TEST_IMG" -c 'reopen -o file.node-name=foo' +$QEMU_IO -c "open -o backing.node-name=foo $TEST_IMG" -c 'reopen -o backing.node-name=foo' + +# Implicitly retained +$QEMU_IO -c "open -o node-name=foo $TEST_IMG" -c 'reopen' +$QEMU_IO -c "open -o file.node-name=foo $TEST_IMG" -c 'reopen' +$QEMU_IO -c "open -o backing.node-name=foo $TEST_IMG" -c 'reopen' + +echo +echo "=== Check that driver can't be changed ===" +echo + +$QEMU_IO -c 'reopen -o driver=raw' $TEST_IMG +$QEMU_IO -c 'reopen -o file.driver=qcow2' $TEST_IMG +$QEMU_IO -c 'reopen -o backing.driver=file' $TEST_IMG + +echo +echo "=== Check that unchanged driver is okay ===" +echo + +# Explicitly repeated (implicit case is covered in node-name test) +$QEMU_IO -c 'reopen -o driver=qcow2' $TEST_IMG +$QEMU_IO -c 'reopen -o file.driver=file' $TEST_IMG +$QEMU_IO -c 'reopen -o backing.driver=qcow2' $TEST_IMG + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/qemu/tests/qemu-iotests/133.out b/qemu/tests/qemu-iotests/133.out new file mode 100644 index 000000000..cc86b9488 --- /dev/null +++ b/qemu/tests/qemu-iotests/133.out @@ -0,0 +1,22 @@ +QA output created by 133 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base + +=== Check that node-name can't be changed === + +Cannot change the option 'node-name' +Cannot change the option 'node-name' +Cannot change the option 'node-name' + +=== Check that unchanged node-name is okay === + + +=== Check that driver can't be changed === + +Cannot change the option 'driver' +Cannot change the option 'driver' +Cannot change the option 'driver' + +=== Check that unchanged driver is okay === + +*** done diff --git a/qemu/tests/qemu-iotests/134 b/qemu/tests/qemu-iotests/134 index 1c3820b17..af618b881 100755 --- a/qemu/tests/qemu-iotests/134 +++ b/qemu/tests/qemu-iotests/134 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/134.out b/qemu/tests/qemu-iotests/134.out index a16acb81c..6493704ec 100644 --- a/qemu/tests/qemu-iotests/134.out +++ b/qemu/tests/qemu-iotests/134.out @@ -1,43 +1,25 @@ QA output created by 134 -qemu-img: Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. -qemu-img: Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on == reading whole image == -Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. Disk image 'TEST_DIR/t.qcow2' is encrypted. password: read 134217728/134217728 bytes at offset 0 128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == rewriting whole image == -Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. Disk image 'TEST_DIR/t.qcow2' is encrypted. password: wrote 134217728/134217728 bytes at offset 0 128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == verify pattern == -Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. Disk image 'TEST_DIR/t.qcow2' is encrypted. password: read 134217728/134217728 bytes at offset 0 128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == verify pattern failure with wrong password == -Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. Disk image 'TEST_DIR/t.qcow2' is encrypted. password: Pattern verification failed at offset 0, 134217728 bytes diff --git a/qemu/tests/qemu-iotests/135 b/qemu/tests/qemu-iotests/135 index 16bf73656..ce608312f 100755 --- a/qemu/tests/qemu-iotests/135 +++ b/qemu/tests/qemu-iotests/135 @@ -25,7 +25,6 @@ seq=`basename $0` echo "QA output created by $seq" here=`pwd` -tmp=/tmp/$$ status=1 # failure is the default! _cleanup() diff --git a/qemu/tests/qemu-iotests/136 b/qemu/tests/qemu-iotests/136 new file mode 100644 index 000000000..e8c6937fc --- /dev/null +++ b/qemu/tests/qemu-iotests/136 @@ -0,0 +1,349 @@ +#!/usr/bin/env python +# +# Tests for block device statistics +# +# Copyright (C) 2015 Igalia, S.L. +# Author: Alberto Garcia +# +# 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 . +# + +import iotests +import os + +interval_length = 10 +nsec_per_sec = 1000000000 +op_latency = nsec_per_sec / 1000 # See qtest_latency_ns in accounting.c +bad_sector = 8192 +bad_offset = bad_sector * 512 +blkdebug_file = os.path.join(iotests.test_dir, 'blkdebug.conf') + +class BlockDeviceStatsTestCase(iotests.QMPTestCase): + test_img = "null-aio://" + total_rd_bytes = 0 + total_rd_ops = 0 + total_wr_bytes = 0 + total_wr_ops = 0 + total_wr_merged = 0 + total_flush_ops = 0 + failed_rd_ops = 0 + failed_wr_ops = 0 + invalid_rd_ops = 0 + invalid_wr_ops = 0 + wr_highest_offset = 0 + account_invalid = False + account_failed = False + + def blockstats(self, device): + result = self.vm.qmp("query-blockstats") + for r in result['return']: + if r['device'] == device: + return r['stats'] + raise Exception("Device not found for blockstats: %s" % device) + + def create_blkdebug_file(self): + file = open(blkdebug_file, 'w') + file.write(''' +[inject-error] +event = "read_aio" +errno = "5" +sector = "%d" + +[inject-error] +event = "write_aio" +errno = "5" +sector = "%d" +''' % (bad_sector, bad_sector)) + file.close() + + def setUp(self): + drive_args = [] + drive_args.append("stats-intervals.0=%d" % interval_length) + drive_args.append("stats-account-invalid=%s" % + (self.account_invalid and "on" or "off")) + drive_args.append("stats-account-failed=%s" % + (self.account_failed and "on" or "off")) + self.create_blkdebug_file() + self.vm = iotests.VM().add_drive('blkdebug:%s:%s ' % + (blkdebug_file, self.test_img), + ','.join(drive_args)) + self.vm.launch() + # Set an initial value for the clock + self.vm.qtest("clock_step %d" % nsec_per_sec) + + def tearDown(self): + self.vm.shutdown() + os.remove(blkdebug_file) + + def accounted_ops(self, read = False, write = False, flush = False): + ops = 0 + if write: + ops += self.total_wr_ops + if self.account_failed: + ops += self.failed_wr_ops + if self.account_invalid: + ops += self.invalid_wr_ops + if read: + ops += self.total_rd_ops + if self.account_failed: + ops += self.failed_rd_ops + if self.account_invalid: + ops += self.invalid_rd_ops + if flush: + ops += self.total_flush_ops + return ops + + def accounted_latency(self, read = False, write = False, flush = False): + latency = 0 + if write: + latency += self.total_wr_ops * op_latency + if self.account_failed: + latency += self.failed_wr_ops * op_latency + if read: + latency += self.total_rd_ops * op_latency + if self.account_failed: + latency += self.failed_rd_ops * op_latency + if flush: + latency += self.total_flush_ops * op_latency + return latency + + def check_values(self): + stats = self.blockstats('drive0') + + # Check that the totals match with what we have calculated + self.assertEqual(self.total_rd_bytes, stats['rd_bytes']) + self.assertEqual(self.total_wr_bytes, stats['wr_bytes']) + self.assertEqual(self.total_rd_ops, stats['rd_operations']) + self.assertEqual(self.total_wr_ops, stats['wr_operations']) + self.assertEqual(self.total_flush_ops, stats['flush_operations']) + self.assertEqual(self.wr_highest_offset, stats['wr_highest_offset']) + self.assertEqual(self.failed_rd_ops, stats['failed_rd_operations']) + self.assertEqual(self.failed_wr_ops, stats['failed_wr_operations']) + self.assertEqual(self.invalid_rd_ops, stats['invalid_rd_operations']) + self.assertEqual(self.invalid_wr_ops, stats['invalid_wr_operations']) + self.assertEqual(self.account_invalid, stats['account_invalid']) + self.assertEqual(self.account_failed, stats['account_failed']) + self.assertEqual(self.total_wr_merged, stats['wr_merged']) + + # Check that there's exactly one interval with the length we defined + self.assertEqual(1, len(stats['timed_stats'])) + timed_stats = stats['timed_stats'][0] + self.assertEqual(interval_length, timed_stats['interval_length']) + + total_rd_latency = self.accounted_latency(read = True) + if (total_rd_latency != 0): + self.assertEqual(total_rd_latency, stats['rd_total_time_ns']) + self.assertEqual(op_latency, timed_stats['min_rd_latency_ns']) + self.assertEqual(op_latency, timed_stats['max_rd_latency_ns']) + self.assertEqual(op_latency, timed_stats['avg_rd_latency_ns']) + self.assertLess(0, timed_stats['avg_rd_queue_depth']) + else: + self.assertEqual(0, stats['rd_total_time_ns']) + self.assertEqual(0, timed_stats['min_rd_latency_ns']) + self.assertEqual(0, timed_stats['max_rd_latency_ns']) + self.assertEqual(0, timed_stats['avg_rd_latency_ns']) + self.assertEqual(0, timed_stats['avg_rd_queue_depth']) + + # min read latency <= avg read latency <= max read latency + self.assertLessEqual(timed_stats['min_rd_latency_ns'], + timed_stats['avg_rd_latency_ns']) + self.assertLessEqual(timed_stats['avg_rd_latency_ns'], + timed_stats['max_rd_latency_ns']) + + total_wr_latency = self.accounted_latency(write = True) + if (total_wr_latency != 0): + self.assertEqual(total_wr_latency, stats['wr_total_time_ns']) + self.assertEqual(op_latency, timed_stats['min_wr_latency_ns']) + self.assertEqual(op_latency, timed_stats['max_wr_latency_ns']) + self.assertEqual(op_latency, timed_stats['avg_wr_latency_ns']) + self.assertLess(0, timed_stats['avg_wr_queue_depth']) + else: + self.assertEqual(0, stats['wr_total_time_ns']) + self.assertEqual(0, timed_stats['min_wr_latency_ns']) + self.assertEqual(0, timed_stats['max_wr_latency_ns']) + self.assertEqual(0, timed_stats['avg_wr_latency_ns']) + self.assertEqual(0, timed_stats['avg_wr_queue_depth']) + + # min write latency <= avg write latency <= max write latency + self.assertLessEqual(timed_stats['min_wr_latency_ns'], + timed_stats['avg_wr_latency_ns']) + self.assertLessEqual(timed_stats['avg_wr_latency_ns'], + timed_stats['max_wr_latency_ns']) + + total_flush_latency = self.accounted_latency(flush = True) + if (total_flush_latency != 0): + self.assertEqual(total_flush_latency, stats['flush_total_time_ns']) + self.assertEqual(op_latency, timed_stats['min_flush_latency_ns']) + self.assertEqual(op_latency, timed_stats['max_flush_latency_ns']) + self.assertEqual(op_latency, timed_stats['avg_flush_latency_ns']) + else: + self.assertEqual(0, stats['flush_total_time_ns']) + self.assertEqual(0, timed_stats['min_flush_latency_ns']) + self.assertEqual(0, timed_stats['max_flush_latency_ns']) + self.assertEqual(0, timed_stats['avg_flush_latency_ns']) + + # min flush latency <= avg flush latency <= max flush latency + self.assertLessEqual(timed_stats['min_flush_latency_ns'], + timed_stats['avg_flush_latency_ns']) + self.assertLessEqual(timed_stats['avg_flush_latency_ns'], + timed_stats['max_flush_latency_ns']) + + # idle_time_ns must be > 0 if we have performed any operation + if (self.accounted_ops(read = True, write = True, flush = True) != 0): + self.assertLess(0, stats['idle_time_ns']) + else: + self.assertFalse(stats.has_key('idle_time_ns')) + + # This test does not alter these, so they must be all 0 + self.assertEqual(0, stats['rd_merged']) + self.assertEqual(0, stats['failed_flush_operations']) + self.assertEqual(0, stats['invalid_flush_operations']) + + def do_test_stats(self, rd_size = 0, rd_ops = 0, wr_size = 0, wr_ops = 0, + flush_ops = 0, invalid_rd_ops = 0, invalid_wr_ops = 0, + failed_rd_ops = 0, failed_wr_ops = 0, wr_merged = 0): + # The 'ops' list will contain all the requested I/O operations + ops = [] + for i in range(rd_ops): + ops.append("aio_read %d %d" % (i * rd_size, rd_size)) + + for i in range(wr_ops): + ops.append("aio_write %d %d" % (i * wr_size, wr_size)) + + for i in range(flush_ops): + ops.append("aio_flush") + + highest_offset = wr_ops * wr_size + + # Two types of invalid operations: unaligned length and unaligned offset + for i in range(invalid_rd_ops / 2): + ops.append("aio_read 0 511") + + for i in range(invalid_rd_ops / 2, invalid_rd_ops): + ops.append("aio_read 13 512") + + for i in range(invalid_wr_ops / 2): + ops.append("aio_write 0 511") + + for i in range(invalid_wr_ops / 2, invalid_wr_ops): + ops.append("aio_write 13 512") + + for i in range(failed_rd_ops): + ops.append("aio_read %d 512" % bad_offset) + + for i in range(failed_wr_ops): + ops.append("aio_write %d 512" % bad_offset) + + if failed_wr_ops > 0: + highest_offset = max(highest_offset, bad_offset + 512) + + for i in range(wr_merged): + first = i * wr_size * 2 + second = first + wr_size + ops.append("multiwrite %d %d ; %d %d" % + (first, wr_size, second, wr_size)) + + highest_offset = max(highest_offset, wr_merged * wr_size * 2) + + # Now perform all operations + for op in ops: + self.vm.hmp_qemu_io("drive0", op) + + # Update the expected totals + self.total_rd_bytes += rd_ops * rd_size + self.total_rd_ops += rd_ops + self.total_wr_bytes += wr_ops * wr_size + self.total_wr_ops += wr_ops + self.total_wr_merged += wr_merged + self.total_flush_ops += flush_ops + self.invalid_rd_ops += invalid_rd_ops + self.invalid_wr_ops += invalid_wr_ops + self.failed_rd_ops += failed_rd_ops + self.failed_wr_ops += failed_wr_ops + + self.wr_highest_offset = max(self.wr_highest_offset, highest_offset) + + # Advance the clock so idle_time_ns has a meaningful value + self.vm.qtest("clock_step %d" % nsec_per_sec) + + # And check that the actual statistics match the expected ones + self.check_values() + + def test_read_only(self): + test_values = [[512, 1], + [65536, 1], + [512, 12], + [65536, 12]] + for i in test_values: + self.do_test_stats(rd_size = i[0], rd_ops = i[1]) + + def test_write_only(self): + test_values = [[512, 1], + [65536, 1], + [512, 12], + [65536, 12]] + for i in test_values: + self.do_test_stats(wr_size = i[0], wr_ops = i[1]) + + def test_invalid(self): + self.do_test_stats(invalid_rd_ops = 7) + self.do_test_stats(invalid_wr_ops = 3) + self.do_test_stats(invalid_rd_ops = 4, invalid_wr_ops = 5) + + def test_failed(self): + self.do_test_stats(failed_rd_ops = 8) + self.do_test_stats(failed_wr_ops = 6) + self.do_test_stats(failed_rd_ops = 5, failed_wr_ops = 12) + + def test_flush(self): + self.do_test_stats(flush_ops = 8) + + def test_merged(self): + for i in range(5): + self.do_test_stats(wr_merged = i * 3) + + def test_all(self): + # rd_size, rd_ops, wr_size, wr_ops, flush_ops + # invalid_rd_ops, invalid_wr_ops, + # failed_rd_ops, failed_wr_ops + # wr_merged + test_values = [[512, 1, 512, 1, 1, 4, 7, 5, 2, 1], + [65536, 1, 2048, 12, 7, 7, 5, 2, 5, 5], + [32768, 9, 8192, 1, 4, 3, 2, 4, 6, 4], + [16384, 11, 3584, 16, 9, 8, 6, 7, 3, 4]] + for i in test_values: + self.do_test_stats(*i) + + def test_no_op(self): + # All values must be sane before doing any I/O + self.check_values() + + +class BlockDeviceStatsTestAccountInvalid(BlockDeviceStatsTestCase): + account_invalid = True + account_failed = False + +class BlockDeviceStatsTestAccountFailed(BlockDeviceStatsTestCase): + account_invalid = False + account_failed = True + +class BlockDeviceStatsTestAccountBoth(BlockDeviceStatsTestCase): + account_invalid = True + account_failed = True + +class BlockDeviceStatsTestCoroutine(BlockDeviceStatsTestCase): + test_img = "null-co://" + +if __name__ == '__main__': + iotests.main(supported_fmts=["raw"]) diff --git a/qemu/tests/qemu-iotests/136.out b/qemu/tests/qemu-iotests/136.out new file mode 100644 index 000000000..0a5e9583a --- /dev/null +++ b/qemu/tests/qemu-iotests/136.out @@ -0,0 +1,5 @@ +........................................ +---------------------------------------------------------------------- +Ran 40 tests + +OK diff --git a/qemu/tests/qemu-iotests/137 b/qemu/tests/qemu-iotests/137 new file mode 100755 index 000000000..e5e30de2f --- /dev/null +++ b/qemu/tests/qemu-iotests/137 @@ -0,0 +1,144 @@ +#!/bin/bash +# +# Test qcow2 reopen +# +# Copyright (C) 2015 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=kwolf@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt qcow2 +_supported_proto generic +_supported_os Linux + + +_make_test_img 64M + +echo === Try setting valid values for all options === +echo + +# Try all options and then check that all of the basic I/O operations still +# work on this image. +$QEMU_IO \ + -c "reopen -o lazy-refcounts=on,pass-discard-request=on" \ + -c "reopen -o lazy-refcounts=off,pass-discard-request=off" \ + -c "reopen -o pass-discard-snapshot=on,pass-discard-other=on" \ + -c "reopen -o pass-discard-snapshot=off,pass-discard-other=off" \ + -c "reopen -o overlap-check=all" \ + -c "reopen -o overlap-check=none" \ + -c "reopen -o overlap-check=cached" \ + -c "reopen -o overlap-check=constant" \ + -c "reopen -o overlap-check.template=all" \ + -c "reopen -o overlap-check.template=none" \ + -c "reopen -o overlap-check.template=cached" \ + -c "reopen -o overlap-check.template=constant" \ + -c "reopen -o overlap-check.main-header=on" \ + -c "reopen -o overlap-check.main-header=off" \ + -c "reopen -o overlap-check.active-l1=on" \ + -c "reopen -o overlap-check.active-l1=off" \ + -c "reopen -o overlap-check.active-l2=on" \ + -c "reopen -o overlap-check.active-l2=off" \ + -c "reopen -o overlap-check.refcount-table=on" \ + -c "reopen -o overlap-check.refcount-table=off" \ + -c "reopen -o overlap-check.refcount-block=on" \ + -c "reopen -o overlap-check.refcount-block=off" \ + -c "reopen -o overlap-check.snapshot-table=on" \ + -c "reopen -o overlap-check.snapshot-table=off" \ + -c "reopen -o overlap-check.inactive-l1=on" \ + -c "reopen -o overlap-check.inactive-l1=off" \ + -c "reopen -o overlap-check.inactive-l2=on" \ + -c "reopen -o overlap-check.inactive-l2=off" \ + -c "reopen -o cache-size=1M" \ + -c "reopen -o l2-cache-size=512k" \ + -c "reopen -o refcount-cache-size=128k" \ + -c "reopen -o cache-clean-interval=5" \ + -c "reopen -o cache-clean-interval=0" \ + -c "reopen -o cache-clean-interval=10" \ + \ + -c "write -P 55 0 32M" \ + -c "read -P 55 0 32M" \ + -c "discard 0 32M" \ + -c "write -z 0 32M" \ + -c "read -P 0 0 32M" \ + \ + "$TEST_IMG" | _filter_qemu_io + + +echo +echo === Try setting some invalid values === +echo + +$QEMU_IO \ + -c "reopen -o lazy-refcounts=42" \ + -c "reopen -o cache-size=1M,l2-cache-size=64k,refcount-cache-size=64k" \ + -c "reopen -o cache-size=1M,l2-cache-size=2M" \ + -c "reopen -o cache-size=1M,refcount-cache-size=2M" \ + -c "reopen -o l2-cache-size=256T" \ + -c "reopen -o refcount-cache-size=256T" \ + -c "reopen -o overlap-check=constant,overlap-check.template=all" \ + -c "reopen -o overlap-check=blubb" \ + -c "reopen -o overlap-check.template=blubb" \ + -c "reopen -o cache-clean-interval=-1" \ + "$TEST_IMG" | _filter_qemu_io + +echo +echo === Test transaction semantics === +echo + +# Whether lazy-refcounts was actually enabled can easily be tested: Check if +# the dirty bit is set after a crash +$QEMU_IO \ + -c "reopen -o lazy-refcounts=on,overlap-check=blubb" \ + -c "write -P 0x5a 0 512" \ + -c "sigraise $(kill -l KILL)" \ + "$TEST_IMG" 2>&1 | _filter_qemu_io + +# The dirty bit must not be set +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features + +# Similarly we can test whether corruption detection has been enabled: +# Create L1/L2, overwrite first entry in refcount block, allocate something. +# Disabling the checks should fail, so the corruption must be detected. +_make_test_img 64M +$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io +poke_file "$TEST_IMG" "$((0x20000))" "\x00\x00" +$QEMU_IO \ + -c "reopen -o overlap-check=none,lazy-refcounts=42" \ + -c "write 64k 64k" \ + "$TEST_IMG" 2>&1 | _filter_qemu_io + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/qemu/tests/qemu-iotests/137.out b/qemu/tests/qemu-iotests/137.out new file mode 100644 index 000000000..88c702cf7 --- /dev/null +++ b/qemu/tests/qemu-iotests/137.out @@ -0,0 +1,46 @@ +QA output created by 137 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +=== Try setting valid values for all options === + +wrote 33554432/33554432 bytes at offset 0 +32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 33554432/33554432 bytes at offset 0 +32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +discard 33554432/33554432 bytes at offset 0 +32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 33554432/33554432 bytes at offset 0 +32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 33554432/33554432 bytes at offset 0 +32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Try setting some invalid values === + +Parameter 'lazy-refcounts' expects 'on' or 'off' +cache-size, l2-cache-size and refcount-cache-size may not be set the same time +l2-cache-size may not exceed cache-size +refcount-cache-size may not exceed cache-size +L2 cache size too big +L2 cache size too big +Conflicting values for qcow2 options 'overlap-check' ('constant') and 'overlap-check.template' ('all') +Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all +Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all +Cache clean interval too big + +=== Test transaction semantics === + +Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then + exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"; +else + exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"; +fi ) +incompatible_features 0x0 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Parameter 'lazy-refcounts' expects 'on' or 'off' +qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with qcow2_header); further corruption events will be suppressed +write failed: Input/output error +*** done diff --git a/qemu/tests/qemu-iotests/138 b/qemu/tests/qemu-iotests/138 new file mode 100755 index 000000000..21650d819 --- /dev/null +++ b/qemu/tests/qemu-iotests/138 @@ -0,0 +1,72 @@ +#!/bin/bash +# +# General test case for qcow2's image check +# +# Copyright (C) 2015 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=mreitz@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +# This tests qocw2-specific low-level functionality +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +echo +echo '=== Check on an image with a multiple of 2^32 clusters ===' +echo + +IMGOPTS=$(_optstr_add "$IMGOPTS" "cluster_size=512") \ + _make_test_img 512 + +# Allocate L2 table +$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io + +# Put the data cluster at a multiple of 2 TB, resulting in the image apparently +# having a multiple of 2^32 clusters +# (To be more specific: It is at 32 PB) +poke_file "$TEST_IMG" 2048 "\x80\x80\x00\x00\x00\x00\x00\x00" + +# An offset of 32 PB results in qemu-img check having to allocate an in-memory +# refcount table of 128 TB (16 bit refcounts, 512 byte clusters). +# This should be generally too much for any system and thus fail. +# What this test is checking is that the qcow2 driver actually tries to allocate +# such a large amount of memory (and is consequently aborting) instead of having +# truncated the cluster count somewhere (which would result in much less memory +# being allocated and then a segfault occurring). +_check_test_img + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/qemu/tests/qemu-iotests/138.out b/qemu/tests/qemu-iotests/138.out new file mode 100644 index 000000000..3fe911f85 --- /dev/null +++ b/qemu/tests/qemu-iotests/138.out @@ -0,0 +1,9 @@ +QA output created by 138 + +=== Check on an image with a multiple of 2^32 clusters === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=512 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Check failed: Cannot allocate memory +*** done diff --git a/qemu/tests/qemu-iotests/139 b/qemu/tests/qemu-iotests/139 new file mode 100644 index 000000000..a4b969499 --- /dev/null +++ b/qemu/tests/qemu-iotests/139 @@ -0,0 +1,416 @@ +#!/usr/bin/env python +# +# Test cases for the QMP 'x-blockdev-del' command +# +# Copyright (C) 2015 Igalia, S.L. +# Author: Alberto Garcia +# +# 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 . +# + +import os +import iotests +import time + +base_img = os.path.join(iotests.test_dir, 'base.img') +new_img = os.path.join(iotests.test_dir, 'new.img') + +class TestBlockdevDel(iotests.QMPTestCase): + + def setUp(self): + iotests.qemu_img('create', '-f', iotests.imgfmt, base_img, '1M') + self.vm = iotests.VM() + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + os.remove(base_img) + if os.path.isfile(new_img): + os.remove(new_img) + + # Check whether a BlockBackend exists + def checkBlockBackend(self, backend, node, must_exist = True): + result = self.vm.qmp('query-block') + backends = filter(lambda x: x['device'] == backend, result['return']) + self.assertLessEqual(len(backends), 1) + self.assertEqual(must_exist, len(backends) == 1) + if must_exist: + if node: + self.assertEqual(backends[0]['inserted']['node-name'], node) + else: + self.assertFalse(backends[0].has_key('inserted')) + + # Check whether a BlockDriverState exists + def checkBlockDriverState(self, node, must_exist = True): + result = self.vm.qmp('query-named-block-nodes') + nodes = filter(lambda x: x['node-name'] == node, result['return']) + self.assertLessEqual(len(nodes), 1) + self.assertEqual(must_exist, len(nodes) == 1) + + # Add a new BlockBackend (with its attached BlockDriverState) + def addBlockBackend(self, backend, node): + file_node = '%s_file' % node + self.checkBlockBackend(backend, node, False) + self.checkBlockDriverState(node, False) + self.checkBlockDriverState(file_node, False) + opts = {'driver': iotests.imgfmt, + 'id': backend, + 'node-name': node, + 'file': {'driver': 'file', + 'node-name': file_node, + 'filename': base_img}} + result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts) + self.assert_qmp(result, 'return', {}) + self.checkBlockBackend(backend, node) + self.checkBlockDriverState(node) + self.checkBlockDriverState(file_node) + + # Add a BlockDriverState without a BlockBackend + def addBlockDriverState(self, node): + file_node = '%s_file' % node + self.checkBlockDriverState(node, False) + self.checkBlockDriverState(file_node, False) + opts = {'driver': iotests.imgfmt, + 'node-name': node, + 'file': {'driver': 'file', + 'node-name': file_node, + 'filename': base_img}} + result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts) + self.assert_qmp(result, 'return', {}) + self.checkBlockDriverState(node) + self.checkBlockDriverState(file_node) + + # Add a BlockDriverState that will be used as overlay for the base_img BDS + def addBlockDriverStateOverlay(self, node): + self.checkBlockDriverState(node, False) + iotests.qemu_img('create', '-f', iotests.imgfmt, + '-b', base_img, new_img, '1M') + opts = {'driver': iotests.imgfmt, + 'node-name': node, + 'backing': '', + 'file': {'driver': 'file', + 'filename': new_img}} + result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts) + self.assert_qmp(result, 'return', {}) + self.checkBlockDriverState(node) + + # Delete a BlockBackend + def delBlockBackend(self, backend, node, expect_error = False, + destroys_media = True): + self.checkBlockBackend(backend, node) + if node: + self.checkBlockDriverState(node) + result = self.vm.qmp('x-blockdev-del', id = backend) + if expect_error: + self.assert_qmp(result, 'error/class', 'GenericError') + if node: + self.checkBlockDriverState(node) + else: + self.assert_qmp(result, 'return', {}) + if node: + self.checkBlockDriverState(node, not destroys_media) + self.checkBlockBackend(backend, node, must_exist = expect_error) + + # Delete a BlockDriverState + def delBlockDriverState(self, node, expect_error = False): + self.checkBlockDriverState(node) + result = self.vm.qmp('x-blockdev-del', node_name = node) + if expect_error: + self.assert_qmp(result, 'error/class', 'GenericError') + else: + self.assert_qmp(result, 'return', {}) + self.checkBlockDriverState(node, expect_error) + + # Add a device model + def addDeviceModel(self, device, backend): + result = self.vm.qmp('device_add', id = device, + driver = 'virtio-blk-pci', drive = backend) + self.assert_qmp(result, 'return', {}) + + # Delete a device model + def delDeviceModel(self, device): + result = self.vm.qmp('device_del', id = device) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('system_reset') + self.assert_qmp(result, 'return', {}) + + device_path = '/machine/peripheral/%s/virtio-backend' % device + event = self.vm.event_wait(name="DEVICE_DELETED", + match={'data': {'path': device_path}}) + self.assertNotEqual(event, None) + + event = self.vm.event_wait(name="DEVICE_DELETED", + match={'data': {'device': device}}) + self.assertNotEqual(event, None) + + # Remove a BlockDriverState + def ejectDrive(self, backend, node, expect_error = False, + destroys_media = True): + self.checkBlockBackend(backend, node) + self.checkBlockDriverState(node) + result = self.vm.qmp('eject', device = backend) + if expect_error: + self.assert_qmp(result, 'error/class', 'GenericError') + self.checkBlockDriverState(node) + self.checkBlockBackend(backend, node) + else: + self.assert_qmp(result, 'return', {}) + self.checkBlockDriverState(node, not destroys_media) + self.checkBlockBackend(backend, None) + + # Insert a BlockDriverState + def insertDrive(self, backend, node): + self.checkBlockBackend(backend, None) + self.checkBlockDriverState(node) + result = self.vm.qmp('x-blockdev-insert-medium', + device = backend, node_name = node) + self.assert_qmp(result, 'return', {}) + self.checkBlockBackend(backend, node) + self.checkBlockDriverState(node) + + # Create a snapshot using 'blockdev-snapshot-sync' + def createSnapshotSync(self, node, overlay): + self.checkBlockDriverState(node) + self.checkBlockDriverState(overlay, False) + opts = {'node-name': node, + 'snapshot-file': new_img, + 'snapshot-node-name': overlay, + 'format': iotests.imgfmt} + result = self.vm.qmp('blockdev-snapshot-sync', conv_keys=False, **opts) + self.assert_qmp(result, 'return', {}) + self.checkBlockDriverState(node) + self.checkBlockDriverState(overlay) + + # Create a snapshot using 'blockdev-snapshot' + def createSnapshot(self, node, overlay): + self.checkBlockDriverState(node) + self.checkBlockDriverState(overlay) + result = self.vm.qmp('blockdev-snapshot', + node = node, overlay = overlay) + self.assert_qmp(result, 'return', {}) + self.checkBlockDriverState(node) + self.checkBlockDriverState(overlay) + + # Create a mirror + def createMirror(self, backend, node, new_node): + self.checkBlockBackend(backend, node) + self.checkBlockDriverState(new_node, False) + opts = {'device': backend, + 'target': new_img, + 'node-name': new_node, + 'sync': 'top', + 'format': iotests.imgfmt} + result = self.vm.qmp('drive-mirror', conv_keys=False, **opts) + self.assert_qmp(result, 'return', {}) + self.checkBlockBackend(backend, node) + self.checkBlockDriverState(new_node) + + # Complete an existing block job + def completeBlockJob(self, backend, node_before, node_after): + self.checkBlockBackend(backend, node_before) + result = self.vm.qmp('block-job-complete', device=backend) + self.assert_qmp(result, 'return', {}) + self.wait_until_completed(backend) + self.checkBlockBackend(backend, node_after) + + # Add a BlkDebug node + # Note that the purpose of this is to test the x-blockdev-del + # sanity checks, not to create a usable blkdebug drive + def addBlkDebug(self, debug, node): + self.checkBlockDriverState(node, False) + self.checkBlockDriverState(debug, False) + image = {'driver': iotests.imgfmt, + 'node-name': node, + 'file': {'driver': 'file', + 'filename': base_img}} + opts = {'driver': 'blkdebug', + 'node-name': debug, + 'image': image} + result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts) + self.assert_qmp(result, 'return', {}) + self.checkBlockDriverState(node) + self.checkBlockDriverState(debug) + + # Add a BlkVerify node + # Note that the purpose of this is to test the x-blockdev-del + # sanity checks, not to create a usable blkverify drive + def addBlkVerify(self, blkverify, test, raw): + self.checkBlockDriverState(test, False) + self.checkBlockDriverState(raw, False) + self.checkBlockDriverState(blkverify, False) + iotests.qemu_img('create', '-f', iotests.imgfmt, new_img, '1M') + node_0 = {'driver': iotests.imgfmt, + 'node-name': test, + 'file': {'driver': 'file', + 'filename': base_img}} + node_1 = {'driver': iotests.imgfmt, + 'node-name': raw, + 'file': {'driver': 'file', + 'filename': new_img}} + opts = {'driver': 'blkverify', + 'node-name': blkverify, + 'test': node_0, + 'raw': node_1} + result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts) + self.assert_qmp(result, 'return', {}) + self.checkBlockDriverState(test) + self.checkBlockDriverState(raw) + self.checkBlockDriverState(blkverify) + + # Add a Quorum node + def addQuorum(self, quorum, child0, child1): + self.checkBlockDriverState(child0, False) + self.checkBlockDriverState(child1, False) + self.checkBlockDriverState(quorum, False) + iotests.qemu_img('create', '-f', iotests.imgfmt, new_img, '1M') + child_0 = {'driver': iotests.imgfmt, + 'node-name': child0, + 'file': {'driver': 'file', + 'filename': base_img}} + child_1 = {'driver': iotests.imgfmt, + 'node-name': child1, + 'file': {'driver': 'file', + 'filename': new_img}} + opts = {'driver': 'quorum', + 'node-name': quorum, + 'vote-threshold': 1, + 'children': [ child_0, child_1 ]} + result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts) + self.assert_qmp(result, 'return', {}) + self.checkBlockDriverState(child0) + self.checkBlockDriverState(child1) + self.checkBlockDriverState(quorum) + + ######################## + # The tests start here # + ######################## + + def testWrongParameters(self): + self.addBlockBackend('drive0', 'node0') + result = self.vm.qmp('x-blockdev-del') + self.assert_qmp(result, 'error/class', 'GenericError') + result = self.vm.qmp('x-blockdev-del', id='drive0', node_name='node0') + self.assert_qmp(result, 'error/class', 'GenericError') + self.delBlockBackend('drive0', 'node0') + + def testBlockBackend(self): + self.addBlockBackend('drive0', 'node0') + # You cannot delete a BDS that is attached to a backend + self.delBlockDriverState('node0', expect_error = True) + self.delBlockBackend('drive0', 'node0') + + def testBlockDriverState(self): + self.addBlockDriverState('node0') + # You cannot delete a file BDS directly + self.delBlockDriverState('node0_file', expect_error = True) + self.delBlockDriverState('node0') + + def testEject(self): + self.addBlockBackend('drive0', 'node0') + self.ejectDrive('drive0', 'node0') + self.delBlockBackend('drive0', None) + + def testDeviceModel(self): + self.addBlockBackend('drive0', 'node0') + self.addDeviceModel('device0', 'drive0') + self.ejectDrive('drive0', 'node0', expect_error = True) + self.delBlockBackend('drive0', 'node0', expect_error = True) + self.delDeviceModel('device0') + self.delBlockBackend('drive0', 'node0') + + def testAttachMedia(self): + # This creates a BlockBackend and removes its media + self.addBlockBackend('drive0', 'node0') + self.ejectDrive('drive0', 'node0') + # This creates a new BlockDriverState and inserts it into the backend + self.addBlockDriverState('node1') + self.insertDrive('drive0', 'node1') + # The backend can't be removed: the new BDS has an extra reference + self.delBlockBackend('drive0', 'node1', expect_error = True) + self.delBlockDriverState('node1', expect_error = True) + # The BDS still exists after being ejected, but now it can be removed + self.ejectDrive('drive0', 'node1', destroys_media = False) + self.delBlockDriverState('node1') + self.delBlockBackend('drive0', None) + + def testSnapshotSync(self): + self.addBlockBackend('drive0', 'node0') + self.createSnapshotSync('node0', 'overlay0') + # This fails because node0 is now being used as a backing image + self.delBlockDriverState('node0', expect_error = True) + # This succeeds because overlay0 only has the backend reference + self.delBlockBackend('drive0', 'overlay0') + self.checkBlockDriverState('node0', False) + + def testSnapshot(self): + self.addBlockBackend('drive0', 'node0') + self.addBlockDriverStateOverlay('overlay0') + self.createSnapshot('node0', 'overlay0') + self.delBlockBackend('drive0', 'overlay0', expect_error = True) + self.delBlockDriverState('node0', expect_error = True) + self.delBlockDriverState('overlay0', expect_error = True) + self.ejectDrive('drive0', 'overlay0', destroys_media = False) + self.delBlockBackend('drive0', None) + self.delBlockDriverState('node0', expect_error = True) + self.delBlockDriverState('overlay0') + self.checkBlockDriverState('node0', False) + + def testMirror(self): + self.addBlockBackend('drive0', 'node0') + self.createMirror('drive0', 'node0', 'mirror0') + # The block job prevents removing the device + self.delBlockBackend('drive0', 'node0', expect_error = True) + self.delBlockDriverState('node0', expect_error = True) + self.delBlockDriverState('mirror0', expect_error = True) + self.wait_ready('drive0') + self.completeBlockJob('drive0', 'node0', 'mirror0') + self.assert_no_active_block_jobs() + self.checkBlockDriverState('node0', False) + # This succeeds because the backend now points to mirror0 + self.delBlockBackend('drive0', 'mirror0') + + def testBlkDebug(self): + self.addBlkDebug('debug0', 'node0') + # 'node0' is used by the blkdebug node + self.delBlockDriverState('node0', expect_error = True) + # But we can remove the blkdebug node directly + self.delBlockDriverState('debug0') + self.checkBlockDriverState('node0', False) + + def testBlkVerify(self): + self.addBlkVerify('verify0', 'node0', 'node1') + # We cannot remove the children of a blkverify device + self.delBlockDriverState('node0', expect_error = True) + self.delBlockDriverState('node1', expect_error = True) + # But we can remove the blkverify node directly + self.delBlockDriverState('verify0') + self.checkBlockDriverState('node0', False) + self.checkBlockDriverState('node1', False) + + def testQuorum(self): + if not 'quorum' in iotests.qemu_img_pipe('--help'): + return + self.addQuorum('quorum0', 'node0', 'node1') + # We cannot remove the children of a Quorum device + self.delBlockDriverState('node0', expect_error = True) + self.delBlockDriverState('node1', expect_error = True) + # But we can remove the Quorum node directly + self.delBlockDriverState('quorum0') + self.checkBlockDriverState('node0', False) + self.checkBlockDriverState('node1', False) + + +if __name__ == '__main__': + iotests.main(supported_fmts=["qcow2"]) diff --git a/qemu/tests/qemu-iotests/139.out b/qemu/tests/qemu-iotests/139.out new file mode 100644 index 000000000..281b69efe --- /dev/null +++ b/qemu/tests/qemu-iotests/139.out @@ -0,0 +1,5 @@ +............ +---------------------------------------------------------------------- +Ran 12 tests + +OK diff --git a/qemu/tests/qemu-iotests/140 b/qemu/tests/qemu-iotests/140 new file mode 100755 index 000000000..49f9df4eb --- /dev/null +++ b/qemu/tests/qemu-iotests/140 @@ -0,0 +1,95 @@ +#!/bin/bash +# +# Test case for ejecting a BlockBackend with an NBD server attached to it +# +# Verify that the NBD server stops offering the drive when ejecting a +# BlockDriverState tree from a BlockBackend (that is, a medium from a +# drive) exposed via an NBD server. +# +# Copyright (C) 2016 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 . +# + +# creator +owner=mreitz@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + rm -f "$TEST_DIR/nbd" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt generic +_supported_proto file +_supported_os Linux + +_make_test_img 64k + +$QEMU_IO -c 'write -P 42 0 64k' "$TEST_IMG" | _filter_qemu_io + +keep_stderr=y \ +_launch_qemu -drive if=none,media=cdrom,id=drv,file="$TEST_IMG",format=$IMGFMT \ + 2> >(_filter_nbd) + +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'qmp_capabilities' }" \ + 'return' + +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'nbd-server-start', + 'arguments': { 'addr': { 'type': 'unix', + 'data': { 'path': '$TEST_DIR/nbd' }}}}" \ + 'return' + +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'nbd-server-add', + 'arguments': { 'device': 'drv' }}" \ + 'return' + +$QEMU_IO_PROG -f raw -c 'read -P 42 0 64k' \ + "nbd+unix:///drv?socket=$TEST_DIR/nbd" 2>&1 \ + | _filter_qemu_io | _filter_nbd + +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'eject', + 'arguments': { 'device': 'drv' }}" \ + 'return' + +$QEMU_IO_PROG -f raw -c close \ + "nbd+unix:///drv?socket=$TEST_DIR/nbd" 2>&1 \ + | _filter_qemu_io | _filter_nbd + +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'quit' }" \ + 'return' + +wait=1 _cleanup_qemu + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/qemu/tests/qemu-iotests/140.out b/qemu/tests/qemu-iotests/140.out new file mode 100644 index 000000000..0409cd017 --- /dev/null +++ b/qemu/tests/qemu-iotests/140.out @@ -0,0 +1,15 @@ +QA output created by 140 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536 +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": {}} +{"return": {}} +{"return": {}} +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": {}} +can't open device nbd+unix:///drv?socket=TEST_DIR/nbd: No export with name 'drv' available +no file open, try 'help open' +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} +*** done diff --git a/qemu/tests/qemu-iotests/141 b/qemu/tests/qemu-iotests/141 new file mode 100755 index 000000000..b2617e5e2 --- /dev/null +++ b/qemu/tests/qemu-iotests/141 @@ -0,0 +1,185 @@ +#!/bin/bash +# +# Test case for ejecting BDSs with block jobs still running on them +# +# Copyright (C) 2016 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 . +# + +# creator +owner=mreitz@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + rm -f "$TEST_DIR/{b,m,o}.$IMGFMT" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +# Needs backing file and backing format support +_supported_fmt qcow2 qed +_supported_proto file +_supported_os Linux + + +test_blockjob() +{ + _send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'blockdev-add', + 'arguments': { + 'options': { + 'id': 'drv0', + 'driver': '$IMGFMT', + 'file': { + 'driver': 'file', + 'filename': '$TEST_IMG' + }}}}" \ + 'return' + + _send_qemu_cmd $QEMU_HANDLE \ + "$1" \ + "$2" \ + | _filter_img_create + + # We want this to return an error because the block job is still running + _send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'x-blockdev-remove-medium', + 'arguments': {'device': 'drv0'}}" \ + 'error' + + _send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'block-job-cancel', + 'arguments': {'device': 'drv0'}}" \ + "$3" + + _send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'x-blockdev-del', + 'arguments': {'id': 'drv0'}}" \ + 'return' +} + + +TEST_IMG="$TEST_DIR/b.$IMGFMT" _make_test_img 1M +TEST_IMG="$TEST_DIR/m.$IMGFMT" _make_test_img -b "$TEST_DIR/b.$IMGFMT" 1M +_make_test_img -b "$TEST_DIR/m.$IMGFMT" 1M + +_launch_qemu -nodefaults + +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'qmp_capabilities'}" \ + 'return' + +echo +echo '=== Testing drive-backup ===' +echo + +# drive-backup will not send BLOCK_JOB_READY by itself, and cancelling the job +# will consequently result in BLOCK_JOB_CANCELLED being emitted. + +test_blockjob \ + "{'execute': 'drive-backup', + 'arguments': {'device': 'drv0', + 'target': '$TEST_DIR/o.$IMGFMT', + 'format': '$IMGFMT', + 'sync': 'none'}}" \ + 'return' \ + 'BLOCK_JOB_CANCELLED' + +echo +echo '=== Testing drive-mirror ===' +echo + +# drive-mirror will send BLOCK_JOB_READY basically immediately, and cancelling +# the job will consequently result in BLOCK_JOB_COMPLETED being emitted. + +test_blockjob \ + "{'execute': 'drive-mirror', + 'arguments': {'device': 'drv0', + 'target': '$TEST_DIR/o.$IMGFMT', + 'format': '$IMGFMT', + 'sync': 'none'}}" \ + 'BLOCK_JOB_READY' \ + 'BLOCK_JOB_COMPLETED' + +echo +echo '=== Testing active block-commit ===' +echo + +# An active block-commit will send BLOCK_JOB_READY basically immediately, and +# cancelling the job will consequently result in BLOCK_JOB_COMPLETED being +# emitted. + +test_blockjob \ + "{'execute': 'block-commit', + 'arguments': {'device': 'drv0'}}" \ + 'BLOCK_JOB_READY' \ + 'BLOCK_JOB_COMPLETED' + +echo +echo '=== Testing non-active block-commit ===' +echo + +# Give block-commit something to work on, otherwise it would be done +# immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would work just +# fine without the block job still running. + +$QEMU_IO -c 'write 0 1M' "$TEST_DIR/m.$IMGFMT" | _filter_qemu_io + +test_blockjob \ + "{'execute': 'block-commit', + 'arguments': {'device': 'drv0', + 'top': '$TEST_DIR/m.$IMGFMT', + 'speed': 1}}" \ + 'return' \ + 'BLOCK_JOB_CANCELLED' + +echo +echo '=== Testing block-stream ===' +echo + +# Give block-stream something to work on, otherwise it would be done +# immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would work just +# fine without the block job still running. + +$QEMU_IO -c 'write 0 1M' "$TEST_DIR/b.$IMGFMT" | _filter_qemu_io + +# With some data to stream (and @speed set to 1), block-stream will not complete +# until we send the block-job-cancel command. Therefore, no event other than +# BLOCK_JOB_CANCELLED will be emitted. + +test_blockjob \ + "{'execute': 'block-stream', + 'arguments': {'device': 'drv0', + 'speed': 1}}" \ + 'return' \ + 'BLOCK_JOB_CANCELLED' + +_cleanup_qemu + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/qemu/tests/qemu-iotests/141.out b/qemu/tests/qemu-iotests/141.out new file mode 100644 index 000000000..adceac181 --- /dev/null +++ b/qemu/tests/qemu-iotests/141.out @@ -0,0 +1,59 @@ +QA output created by 141 +Formatting 'TEST_DIR/b.IMGFMT', fmt=IMGFMT size=1048576 +Formatting 'TEST_DIR/m.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/b.IMGFMT +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/m.IMGFMT +{"return": {}} + +=== Testing drive-backup === + +{"return": {}} +Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT +{"return": {}} +{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: backup"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "drv0", "len": 1048576, "offset": 0, "speed": 0, "type": "backup"}} +{"return": {}} + +=== Testing drive-mirror === + +{"return": {}} +Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "drv0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}} +{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: mirror"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "drv0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}} +{"return": {}} + +=== Testing active block-commit === + +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "drv0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} +{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: commit"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "drv0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} +{"return": {}} + +=== Testing non-active block-commit === + +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": {}} +{"return": {}} +{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: commit"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "drv0", "len": 1048576, "offset": 524288, "speed": 1, "type": "commit"}} +{"return": {}} + +=== Testing block-stream === + +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": {}} +{"return": {}} +{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: stream"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "drv0", "len": 1048576, "offset": 524288, "speed": 1, "type": "stream"}} +{"return": {}} +*** done diff --git a/qemu/tests/qemu-iotests/142 b/qemu/tests/qemu-iotests/142 new file mode 100755 index 000000000..29c0606bd --- /dev/null +++ b/qemu/tests/qemu-iotests/142 @@ -0,0 +1,358 @@ +#!/bin/bash +# +# Test for configuring cache modes of arbitrary nodes (requires O_DIRECT) +# +# Copyright (C) 2015 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + rm -f $TEST_IMG.snap +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +# We test all cache modes anyway, but O_DIRECT needs to be supported +_default_cache_mode none +_supported_cache_modes none directsync + +function do_run_qemu() +{ + echo Testing: "$@" + ( + if ! test -t 0; then + while read cmd; do + echo $cmd + done + fi + echo quit + ) | $QEMU -nographic -monitor stdio -nodefaults "$@" + echo +} + +function run_qemu() +{ + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu +} + +size=128M + +TEST_IMG="$TEST_IMG.base" _make_test_img $size +TEST_IMG="$TEST_IMG.snap" _make_test_img $size +_make_test_img -b "$TEST_IMG.base" $size + +echo +echo === Simple test for all cache modes === +echo + +run_qemu -drive file="$TEST_IMG",cache=none +run_qemu -drive file="$TEST_IMG",cache=directsync +run_qemu -drive file="$TEST_IMG",cache=writeback +run_qemu -drive file="$TEST_IMG",cache=writethrough +run_qemu -drive file="$TEST_IMG",cache=unsafe +run_qemu -drive file="$TEST_IMG",cache=invalid_value + +echo +echo === Check inheritance of cache modes === +echo + +files="if=none,file=$TEST_IMG,backing.file.filename=$TEST_IMG.base" +ids="node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file" + +function check_cache_all() +{ + # cache.direct is supposed to be inherited by both bs->file and + # bs->backing + + echo -e "cache.direct=on on none0" + echo "$hmp_cmds" | run_qemu -drive "$files","$ids",cache.direct=on | grep -e "Cache" -e "[Cc]annot|[Cc]ould not|[Cc]an't" + echo -e "\ncache.direct=on on file" + echo "$hmp_cmds" | run_qemu -drive "$files","$ids",file.cache.direct=on | grep -e "Cache" -e "[Cc]annot|[Cc]ould not|[Cc]an't" + echo -e "\ncache.direct=on on backing" + echo "$hmp_cmds" | run_qemu -drive "$files","$ids",backing.cache.direct=on | grep -e "Cache" -e "[Cc]annot|[Cc]ould not|[Cc]an't" + echo -e "\ncache.direct=on on backing-file" + echo "$hmp_cmds" | run_qemu -drive "$files","$ids",backing.file.cache.direct=on | grep -e "Cache" -e "[Cc]annot|[Cc]ould not|[Cc]an't" + + # cache.writeback is supposed to be inherited by bs->backing; bs->file + # always gets cache.writeback=on + + echo -e "\n\ncache.writeback=off on none0" + echo "$hmp_cmds" | run_qemu -drive "$files","$ids",cache.writeback=off | grep -e "Cache" -e "[Cc]annot\|[Cc]ould not\|[Cc]an't" + echo -e "\ncache.writeback=off on file" + echo "$hmp_cmds" | run_qemu -drive "$files","$ids",file.cache.writeback=off | grep -e "doesn't" -e "does not" + echo -e "\ncache.writeback=off on backing" + echo "$hmp_cmds" | run_qemu -drive "$files","$ids",backing.cache.writeback=off | grep -e "doesn't" -e "does not" + echo -e "\ncache.writeback=off on backing-file" + echo "$hmp_cmds" | run_qemu -drive "$files","$ids",backing.file.cache.writeback=off | grep -e "doesn't" -e "does not" + + # cache.no-flush is supposed to be inherited by both bs->file and bs->backing + + echo -e "\n\ncache.no-flush=on on none0" + echo "$hmp_cmds" | run_qemu -drive "$files","$ids",cache.no-flush=on | grep -e "Cache" -e "[Cc]annot\|[Cc]ould not\|[Cc]an't" + echo -e "\ncache.no-flush=on on file" + echo "$hmp_cmds" | run_qemu -drive "$files","$ids",file.cache.no-flush=on | grep -e "Cache" -e "[Cc]annot\|[Cc]ould not\|[Cc]an't" + echo -e "\ncache.no-flush=on on backing" + echo "$hmp_cmds" | run_qemu -drive "$files","$ids",backing.cache.no-flush=on | grep -e "Cache" -e "[Cc]annot\|[Cc]ould not\|[Cc]an't" + echo -e "\ncache.no-flush=on on backing-file" + echo "$hmp_cmds" | run_qemu -drive "$files","$ids",backing.file.cache.no-flush=on | grep -e "Cache" -e "[Cc]annot\|[Cc]ould not\|[Cc]an't" +} + +echo +echo "--- Configure cache modes on the command line ---" +echo + +# First check the inherited cache mode after opening the image. + +hmp_cmds="info block none0 +info block image +info block file +info block backing +info block backing-file" + +check_cache_all + +echo +echo "--- Cache modes after reopen (live snapshot) ---" +echo + +# Then trigger a reopen and check that the cache modes are still the same. + +hmp_cmds="snapshot_blkdev -n none0 $TEST_IMG.snap $IMGFMT +info block +info block image +info block file +info block backing +info block backing-file" + +check_cache_all + +echo +echo "--- Change cache modes with reopen (qemu-io command, flags) ---" +echo + +# This one actually changes the cache mode with the reopen. For this test, the +# new cache mode is specified in the flags, not as an option. + +hmp_cmds='qemu-io none0 "reopen -c none" +info block none0 +info block image +info block file +info block backing +info block backing-file' + +check_cache_all + +echo +echo "--- Change cache modes with reopen (qemu-io command, options) ---" +echo + +# This one actually changes the cache mode with the reopen. For this test, the +# new cache mode is specified as an option, not in the flags. + +hmp_cmds='qemu-io none0 "reopen -o cache.direct=on" +info block none0 +info block image +info block file +info block backing +info block backing-file' + +check_cache_all + +echo +echo "--- Change cache modes after snapshot ---" +echo + +# This checks that the original image doesn't inherit from the snapshot + +hmp_cmds="snapshot_blkdev -n none0 $TEST_IMG.snap $IMGFMT +qemu-io none0 \"reopen -c none\" +info block none0 +info block image +info block file +info block backing +info block backing-file" + +check_cache_all + +echo +echo "--- Change cache mode in parent, child has explicit option in JSON ---" +echo + +# This checks that children with options explicitly set by the json: +# pseudo-protocol don't inherit these options from their parents. +# +# Yes, blkdebug::json:... is criminal, but I can't see another way to have a +# BDS initialised with the json: pseudo-protocol, but still have it inherit +# options from its parent node. + +hmp_cmds="qemu-io none0 \"reopen -o cache.direct=on,cache.no-flush=on\" +info block none0 +info block image +info block blkdebug +info block file" + +echo "$hmp_cmds" | run_qemu -drive if=none,file="blkdebug::json:{\"filename\":\"$TEST_IMG\",,\"cache\":{\"direct\":false}}",node-name=image,file.node-name=blkdebug,file.image.node-name=file | grep "Cache" + +echo +echo "=== Check that referenced BDSes don't inherit ===" +echo + +drv_bkfile="if=none,driver=file,filename=$TEST_IMG.base,node-name=backing-file" +drv_bk="if=none,file=json:{'driver':'$IMGFMT',,'file':'backing-file',,'node-name':'backing'}" +drv_file="if=none,driver=file,filename=$TEST_IMG,node-name=file" +drv_img="if=none,id=blk,file=json:{'driver':'$IMGFMT',,'file':'file',,'backing':'backing',,'node-name':'image'}" + +function check_cache_all_separate() +{ + # Check cache.direct + + echo -e "cache.direct=on on blk" + echo "$hmp_cmds" | run_qemu -drive "$drv_bkfile" -drive "$drv_bk" -drive "$drv_file" -drive "$drv_img",cache.direct=on | grep -e "Cache" -e "[Cc]annot\|[Cc]ould not\|[Cc]an't" + echo -e "\ncache.direct=on on file" + echo "$hmp_cmds" | run_qemu -drive "$drv_bkfile" -drive "$drv_bk" -drive "$drv_file",cache.direct=on -drive "$drv_img" | grep -e "Cache" -e "[Cc]annot\|[Cc]ould not\|[Cc]an't" + echo -e "\ncache.direct=on on backing" + echo "$hmp_cmds" | run_qemu -drive "$drv_bkfile" -drive "$drv_bk",cache.direct=on -drive "$drv_file" -drive "$drv_img" | grep -e "Cache" -e "[Cc]annot\|[Cc]ould not\|[Cc]an't" + echo -e "\ncache.direct=on on backing-file" + echo "$hmp_cmds" | run_qemu -drive "$drv_bkfile",cache.direct=on -drive "$drv_bk" -drive "$drv_file" -drive "$drv_img" | grep -e "Cache" -e "[Cc]annot\|[Cc]ould not\|[Cc]an't" + + # Check cache.writeback + + echo -e "\n\ncache.writeback=off on blk" + echo "$hmp_cmds" | run_qemu -drive "$drv_bkfile" -drive "$drv_bk" -drive "$drv_file" -drive "$drv_img",cache.writeback=off | grep -e "Cache" -e "[Cc]annot\|[Cc]ould not\|[Cc]an't" + echo -e "\ncache.writeback=off on file" + echo "$hmp_cmds" | run_qemu -drive "$drv_bkfile" -drive "$drv_bk" -drive "$drv_file",cache.writeback=off -drive "$drv_img" | grep -e "Cache" -e "[Cc]annot\|[Cc]ould not\|[Cc]an't" + echo -e "\ncache.writeback=off on backing" + echo "$hmp_cmds" | run_qemu -drive "$drv_bkfile" -drive "$drv_bk",cache.writeback=off -drive "$drv_file" -drive "$drv_img" | grep -e "Cache" -e "[Cc]annot\|[Cc]ould not\|[Cc]an't" + echo -e "\ncache.writeback=off on backing-file" + echo "$hmp_cmds" | run_qemu -drive "$drv_bkfile",cache.writeback=off -drive "$drv_bk" -drive "$drv_file" -drive "$drv_img" | grep -e "Cache" -e "[Cc]annot\|[Cc]ould not\|[Cc]an't" + + # Check cache.no-flush + + echo -e "\n\ncache.no-flush=on on blk" + echo "$hmp_cmds" | run_qemu -drive "$drv_bkfile" -drive "$drv_bk" -drive "$drv_file" -drive "$drv_img",cache.no-flush=on | grep -e "Cache" -e "[Cc]annot\|[Cc]ould not\|[Cc]an't" + echo -e "\ncache.no-flush=on on file" + echo "$hmp_cmds" | run_qemu -drive "$drv_bkfile" -drive "$drv_bk" -drive "$drv_file",cache.no-flush=on -drive "$drv_img" | grep -e "Cache" -e "[Cc]annot\|[Cc]ould not\|[Cc]an't" + echo -e "\ncache.no-flush=on on backing" + echo "$hmp_cmds" | run_qemu -drive "$drv_bkfile" -drive "$drv_bk",cache.no-flush=on -drive "$drv_file" -drive "$drv_img" | grep -e "Cache" -e "[Cc]annot\|[Cc]ould not\|[Cc]an't" + echo -e "\ncache.no-flush=on on backing-file" + echo "$hmp_cmds" | run_qemu -drive "$drv_bkfile",cache.no-flush=on -drive "$drv_bk" -drive "$drv_file" -drive "$drv_img" | grep -e "Cache" -e "[Cc]annot\|[Cc]ould not\|[Cc]an't" +} + +echo +echo "--- Configure cache modes on the command line ---" +echo + +# First check the inherited cache mode after opening the image. + +hmp_cmds="info block image +info block file +info block backing +info block backing-file" + +check_cache_all_separate + +echo +echo "--- Cache modes after reopen (live snapshot) ---" +echo + +# Then trigger a reopen and check that the cache modes are still the same. + +hmp_cmds="snapshot_blkdev -n blk $TEST_IMG.snap $IMGFMT +info block blk +info block image +info block file +info block backing +info block backing-file" + +check_cache_all_separate + +echo +echo "--- Change cache modes with reopen (qemu-io command, flags) ---" +echo + +# This one actually changes the cache mode with the reopen. For this test, the +# new cache mode is specified as flags, not as option. + +hmp_cmds='qemu-io blk "reopen -c none" +info block image +info block file +info block backing +info block backing-file' + +check_cache_all_separate + + +echo +echo "=== Reopening children instead of the root ===" +echo + +files="if=none,file=$TEST_IMG,backing.file.filename=$TEST_IMG.base" +ids="node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file" + +echo +echo "--- Basic reopen ---" +echo + +hmp_cmds='qemu-io none0 "reopen -o backing.cache.direct=on" +info block none0 +info block image +info block file +info block backing +info block backing-file' + +check_cache_all + +echo +echo "--- Change cache mode after reopening child ---" +echo + +# This checks that children with options explicitly set with reopen don't +# inherit these options from their parents any more + +# TODO Implement node-name support for 'qemu-io' HMP command for -c +# Can use only -o to access child node options for now + +hmp_cmds="qemu-io none0 \"reopen -o file.cache.direct=off,file.cache.no-flush=off\" +qemu-io none0 \"reopen -o backing.file.cache.direct=off,backing.file.cache.no-flush=on\" +qemu-io none0 \"reopen -c none\" +info block image +info block file +info block backing +info block backing-file" + +echo "$hmp_cmds" | run_qemu -drive "$files","$ids" | grep "Cache" + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/qemu/tests/qemu-iotests/142.out b/qemu/tests/qemu-iotests/142.out new file mode 100644 index 000000000..600beca8f --- /dev/null +++ b/qemu/tests/qemu-iotests/142.out @@ -0,0 +1,750 @@ +QA output created by 142 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT.snap', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base + +=== Simple test for all cache modes === + +Testing: -drive file=TEST_DIR/t.qcow2,cache=none +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,cache=directsync +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,cache=writeback +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,cache=writethrough +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,cache=unsafe +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit + +Testing: -drive file=TEST_DIR/t.qcow2,cache=invalid_value +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,cache=invalid_value: invalid cache option + + +=== Check inheritance of cache modes === + + +--- Configure cache modes on the command line --- + +cache.direct=on on none0 + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.direct=on on file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + +cache.direct=on on backing + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.direct=on on backing-file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct + + +cache.writeback=off on none0 + Cache mode: writethrough + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + +cache.writeback=off on file +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,backing.file.filename=TEST_DIR/t.qcow2.base,node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,file.cache.writeback=off: Block protocol 'file' doesn't support the option 'cache.writeback' + +cache.writeback=off on backing +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,backing.file.filename=TEST_DIR/t.qcow2.base,node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,backing.cache.writeback=off: Could not open backing file: Block format 'qcow2' does not support the option 'cache.writeback' + +cache.writeback=off on backing-file +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,backing.file.filename=TEST_DIR/t.qcow2.base,node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,backing.file.cache.writeback=off: Could not open backing file: Block protocol 'file' doesn't support the option 'cache.writeback' + + +cache.no-flush=on on none0 + Cache mode: writeback, ignore flushes + Cache mode: writeback, ignore flushes + Cache mode: writeback, ignore flushes + Cache mode: writeback, ignore flushes + Cache mode: writeback, ignore flushes + +cache.no-flush=on on file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, ignore flushes + Cache mode: writeback + Cache mode: writeback + +cache.no-flush=on on backing + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, ignore flushes + Cache mode: writeback, ignore flushes + +cache.no-flush=on on backing-file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, ignore flushes + +--- Cache modes after reopen (live snapshot) --- + +cache.direct=on on none0 + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.direct=on on file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + +cache.direct=on on backing + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.direct=on on backing-file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct + + +cache.writeback=off on none0 + Cache mode: writethrough + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + +cache.writeback=off on file +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,backing.file.filename=TEST_DIR/t.qcow2.base,node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,file.cache.writeback=off: Block protocol 'file' doesn't support the option 'cache.writeback' + +cache.writeback=off on backing +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,backing.file.filename=TEST_DIR/t.qcow2.base,node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,backing.cache.writeback=off: Could not open backing file: Block format 'qcow2' does not support the option 'cache.writeback' + +cache.writeback=off on backing-file +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,backing.file.filename=TEST_DIR/t.qcow2.base,node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,backing.file.cache.writeback=off: Could not open backing file: Block protocol 'file' doesn't support the option 'cache.writeback' + + +cache.no-flush=on on none0 + Cache mode: writeback, ignore flushes + Cache mode: writeback, ignore flushes + Cache mode: writeback, ignore flushes + Cache mode: writeback, ignore flushes + Cache mode: writeback, ignore flushes + +cache.no-flush=on on file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, ignore flushes + Cache mode: writeback + Cache mode: writeback + +cache.no-flush=on on backing + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, ignore flushes + Cache mode: writeback, ignore flushes + +cache.no-flush=on on backing-file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, ignore flushes + +--- Change cache modes with reopen (qemu-io command, flags) --- + +cache.direct=on on none0 + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.direct=on on file + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.direct=on on backing + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.direct=on on backing-file + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + + +cache.writeback=off on none0 + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.writeback=off on file +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,backing.file.filename=TEST_DIR/t.qcow2.base,node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,file.cache.writeback=off: Block protocol 'file' doesn't support the option 'cache.writeback' + +cache.writeback=off on backing +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,backing.file.filename=TEST_DIR/t.qcow2.base,node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,backing.cache.writeback=off: Could not open backing file: Block format 'qcow2' does not support the option 'cache.writeback' + +cache.writeback=off on backing-file +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,backing.file.filename=TEST_DIR/t.qcow2.base,node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,backing.file.cache.writeback=off: Could not open backing file: Block protocol 'file' doesn't support the option 'cache.writeback' + + +cache.no-flush=on on none0 + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.no-flush=on on file + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct, ignore flushes + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.no-flush=on on backing + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct, ignore flushes + Cache mode: writeback, direct, ignore flushes + +cache.no-flush=on on backing-file + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct, ignore flushes + +--- Change cache modes with reopen (qemu-io command, options) --- + +cache.direct=on on none0 + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.direct=on on file + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.direct=on on backing + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.direct=on on backing-file + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + + +cache.writeback=off on none0 + Cache mode: writethrough, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.writeback=off on file +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,backing.file.filename=TEST_DIR/t.qcow2.base,node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,file.cache.writeback=off: Block protocol 'file' doesn't support the option 'cache.writeback' + +cache.writeback=off on backing +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,backing.file.filename=TEST_DIR/t.qcow2.base,node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,backing.cache.writeback=off: Could not open backing file: Block format 'qcow2' does not support the option 'cache.writeback' + +cache.writeback=off on backing-file +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,backing.file.filename=TEST_DIR/t.qcow2.base,node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,backing.file.cache.writeback=off: Could not open backing file: Block protocol 'file' doesn't support the option 'cache.writeback' + + +cache.no-flush=on on none0 + Cache mode: writeback, direct, ignore flushes + Cache mode: writeback, direct, ignore flushes + Cache mode: writeback, direct, ignore flushes + Cache mode: writeback, direct, ignore flushes + Cache mode: writeback, direct, ignore flushes + +cache.no-flush=on on file + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct, ignore flushes + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.no-flush=on on backing + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct, ignore flushes + Cache mode: writeback, direct, ignore flushes + +cache.no-flush=on on backing-file + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct, ignore flushes + +--- Change cache modes after snapshot --- + +cache.direct=on on none0 + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.direct=on on file + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + +cache.direct=on on backing + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.direct=on on backing-file + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct + + +cache.writeback=off on none0 + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + +cache.writeback=off on file +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,backing.file.filename=TEST_DIR/t.qcow2.base,node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,file.cache.writeback=off: Block protocol 'file' doesn't support the option 'cache.writeback' + +cache.writeback=off on backing +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,backing.file.filename=TEST_DIR/t.qcow2.base,node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,backing.cache.writeback=off: Could not open backing file: Block format 'qcow2' does not support the option 'cache.writeback' + +cache.writeback=off on backing-file +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,backing.file.filename=TEST_DIR/t.qcow2.base,node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,backing.file.cache.writeback=off: Could not open backing file: Block protocol 'file' doesn't support the option 'cache.writeback' + + +cache.no-flush=on on none0 + Cache mode: writeback, direct + Cache mode: writeback, ignore flushes + Cache mode: writeback, ignore flushes + Cache mode: writeback, ignore flushes + Cache mode: writeback, ignore flushes + +cache.no-flush=on on file + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback, ignore flushes + Cache mode: writeback + Cache mode: writeback + +cache.no-flush=on on backing + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, ignore flushes + Cache mode: writeback, ignore flushes + +cache.no-flush=on on backing-file + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, ignore flushes + +--- Change cache mode in parent, child has explicit option in JSON --- + + Cache mode: writeback, direct, ignore flushes + Cache mode: writeback, direct, ignore flushes + Cache mode: writeback, direct, ignore flushes + Cache mode: writeback, ignore flushes + +=== Check that referenced BDSes don't inherit === + + +--- Configure cache modes on the command line --- + +cache.direct=on on blk + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + +cache.direct=on on file + Cache mode: writeback + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + +cache.direct=on on backing + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct + Cache mode: writeback + +cache.direct=on on backing-file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct + + +cache.writeback=off on blk + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + +cache.writeback=off on file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + +cache.writeback=off on backing + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + +cache.writeback=off on backing-file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + + +cache.no-flush=on on blk + Cache mode: writeback, ignore flushes + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + +cache.no-flush=on on file + Cache mode: writeback + Cache mode: writeback, ignore flushes + Cache mode: writeback + Cache mode: writeback + +cache.no-flush=on on backing + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, ignore flushes + Cache mode: writeback + +cache.no-flush=on on backing-file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, ignore flushes + +--- Cache modes after reopen (live snapshot) --- + +cache.direct=on on blk + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + +cache.direct=on on file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + +cache.direct=on on backing + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct + Cache mode: writeback + +cache.direct=on on backing-file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct + + +cache.writeback=off on blk + Cache mode: writethrough + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + +cache.writeback=off on file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + +cache.writeback=off on backing + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + +cache.writeback=off on backing-file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + + +cache.no-flush=on on blk + Cache mode: writeback, ignore flushes + Cache mode: writeback, ignore flushes + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + +cache.no-flush=on on file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, ignore flushes + Cache mode: writeback + Cache mode: writeback + +cache.no-flush=on on backing + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, ignore flushes + Cache mode: writeback + +cache.no-flush=on on backing-file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, ignore flushes + +--- Change cache modes with reopen (qemu-io command, flags) --- + +cache.direct=on on blk + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + +cache.direct=on on file + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + +cache.direct=on on backing + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback, direct + Cache mode: writeback + +cache.direct=on on backing-file + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct + + +cache.writeback=off on blk + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + +cache.writeback=off on file + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + +cache.writeback=off on backing + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + +cache.writeback=off on backing-file + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + + +cache.no-flush=on on blk + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + +cache.no-flush=on on file + Cache mode: writeback, direct + Cache mode: writeback, ignore flushes + Cache mode: writeback + Cache mode: writeback + +cache.no-flush=on on backing + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback, ignore flushes + Cache mode: writeback + +cache.no-flush=on on backing-file + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, ignore flushes + +=== Reopening children instead of the root === + + +--- Basic reopen --- + +cache.direct=on on none0 + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.direct=on on file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.direct=on on backing + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.direct=on on backing-file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct + Cache mode: writeback, direct + + +cache.writeback=off on none0 + Cache mode: writethrough + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.writeback=off on file +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,backing.file.filename=TEST_DIR/t.qcow2.base,node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,file.cache.writeback=off: Block protocol 'file' doesn't support the option 'cache.writeback' + +cache.writeback=off on backing +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,backing.file.filename=TEST_DIR/t.qcow2.base,node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,backing.cache.writeback=off: Could not open backing file: Block format 'qcow2' does not support the option 'cache.writeback' + +cache.writeback=off on backing-file +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,backing.file.filename=TEST_DIR/t.qcow2.base,node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,backing.file.cache.writeback=off: Could not open backing file: Block protocol 'file' doesn't support the option 'cache.writeback' + + +cache.no-flush=on on none0 + Cache mode: writeback, ignore flushes + Cache mode: writeback, ignore flushes + Cache mode: writeback, ignore flushes + Cache mode: writeback, direct, ignore flushes + Cache mode: writeback, direct, ignore flushes + +cache.no-flush=on on file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, ignore flushes + Cache mode: writeback, direct + Cache mode: writeback, direct + +cache.no-flush=on on backing + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct, ignore flushes + Cache mode: writeback, direct, ignore flushes + +cache.no-flush=on on backing-file + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback + Cache mode: writeback, direct + Cache mode: writeback, direct, ignore flushes + +--- Change cache mode after reopening child --- + + Cache mode: writeback, direct + Cache mode: writeback + Cache mode: writeback, direct + Cache mode: writeback, ignore flushes +*** done diff --git a/qemu/tests/qemu-iotests/143 b/qemu/tests/qemu-iotests/143 new file mode 100755 index 000000000..ec4ef2221 --- /dev/null +++ b/qemu/tests/qemu-iotests/143 @@ -0,0 +1,72 @@ +#!/bin/bash +# +# Test case for connecting to a non-existing NBD export name +# +# Copyright (C) 2016 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 . +# + +# creator +owner=mreitz@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +status=1 # failure is the default! + +_cleanup() +{ + rm -f "$TEST_DIR/nbd" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt generic +_supported_proto generic +_supported_os Linux + +keep_stderr=y \ +_launch_qemu 2> >(_filter_nbd) + +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'qmp_capabilities' }" \ + 'return' + +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'nbd-server-start', + 'arguments': { 'addr': { 'type': 'unix', + 'data': { 'path': '$TEST_DIR/nbd' }}}}" \ + 'return' + +# This should just result in a client error, not in the server crashing +$QEMU_IO_PROG -f raw -c quit \ + "nbd+unix:///no_such_export?socket=$TEST_DIR/nbd" 2>&1 \ + | _filter_qemu_io | _filter_nbd + +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'quit' }" \ + 'return' + +wait=1 _cleanup_qemu + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/qemu/tests/qemu-iotests/143.out b/qemu/tests/qemu-iotests/143.out new file mode 100644 index 000000000..d24ad20db --- /dev/null +++ b/qemu/tests/qemu-iotests/143.out @@ -0,0 +1,7 @@ +QA output created by 143 +{"return": {}} +{"return": {}} +can't open device nbd+unix:///no_such_export?socket=TEST_DIR/nbd: No export with name 'no_such_export' available +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} +*** done diff --git a/qemu/tests/qemu-iotests/144 b/qemu/tests/qemu-iotests/144 new file mode 100755 index 000000000..00de3c33c --- /dev/null +++ b/qemu/tests/qemu-iotests/144 @@ -0,0 +1,114 @@ +#!/bin/bash +# Check live snapshot, followed by active commit, and another snapshot. +# +# This test is to catch the error case of BZ #1300209: +# https://bugzilla.redhat.com/show_bug.cgi?id=1300209 +# +# Copyright (C) 2016 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 . +# + +# creator +owner=jcody@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +TMP_SNAP1=${TEST_DIR}/tmp.qcow2 +TMP_SNAP2=${TEST_DIR}/tmp2.qcow2 + +_cleanup() +{ + _cleanup_qemu + rm -f "${TEST_IMG}" "${TMP_SNAP1}" "${TMP_SNAP2}" +} + +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +size=512M + +_make_test_img $size + +echo +echo === Launching QEMU === +echo + +qemu_comm_method="qmp" +_launch_qemu -drive file="${TEST_IMG}",if=virtio +h=$QEMU_HANDLE + + +echo +echo === Performing Live Snapshot 1 === +echo + +_send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" "return" + + +# First live snapshot, new overlay as active layer +_send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot-sync', + 'arguments': { + 'device': 'virtio0', + 'snapshot-file':'${TMP_SNAP1}', + 'format': 'qcow2' + } + }" "return" + +echo +echo === Performing block-commit on active layer === +echo + +# Block commit on active layer, push the new overlay into base +_send_qemu_cmd $h "{ 'execute': 'block-commit', + 'arguments': { + 'device': 'virtio0' + } + }" "READY" + +_send_qemu_cmd $h "{ 'execute': 'block-job-complete', + 'arguments': { + 'device': 'virtio0' + } + }" "COMPLETED" + +echo +echo === Performing Live Snapshot 2 === +echo + +# New live snapshot, new overlays as active layer +_send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot-sync', + 'arguments': { + 'device': 'virtio0', + 'snapshot-file':'${TMP_SNAP2}', + 'format': 'qcow2' + } + }" "return" + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/qemu/tests/qemu-iotests/144.out b/qemu/tests/qemu-iotests/144.out new file mode 100644 index 000000000..410d74180 --- /dev/null +++ b/qemu/tests/qemu-iotests/144.out @@ -0,0 +1,24 @@ +QA output created by 144 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=536870912 + +=== Launching QEMU === + + +=== Performing Live Snapshot 1 === + +{"return": {}} +Formatting 'TEST_DIR/tmp.qcow2', fmt=qcow2 size=536870912 backing_file=TEST_DIR/t.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +{"return": {}} + +=== Performing block-commit on active layer === + +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} + +=== Performing Live Snapshot 2 === + +Formatting 'TEST_DIR/tmp2.qcow2', fmt=qcow2 size=536870912 backing_file=TEST_DIR/t.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +{"return": {}} +*** done diff --git a/qemu/tests/qemu-iotests/145 b/qemu/tests/qemu-iotests/145 new file mode 100755 index 000000000..1eca0e899 --- /dev/null +++ b/qemu/tests/qemu-iotests/145 @@ -0,0 +1,51 @@ +#!/bin/bash +# +# Test the combination of -incoming and snapshot=on +# +# Copyright (C) 2016 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 . +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + true +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt generic +_supported_proto generic +_supported_os Linux + +_make_test_img 1M +echo quit | $QEMU -nographic -hda "$TEST_IMG" -incoming 'exec:true' -snapshot -serial none -monitor stdio | _filter_qemu + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/qemu/tests/qemu-iotests/145.out b/qemu/tests/qemu-iotests/145.out new file mode 100644 index 000000000..75b5c8ac3 --- /dev/null +++ b/qemu/tests/qemu-iotests/145.out @@ -0,0 +1,5 @@ +QA output created by 145 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qququiquit +*** done diff --git a/qemu/tests/qemu-iotests/146 b/qemu/tests/qemu-iotests/146 new file mode 100755 index 000000000..043711be6 --- /dev/null +++ b/qemu/tests/qemu-iotests/146 @@ -0,0 +1,165 @@ +#!/bin/bash +# +# Test VHD image format creator detection and override +# +# Copyright (C) 2016 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 . +# + +# creator +owner=jcody@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_qemu + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt vpc +_supported_proto file +_supported_os Linux + + +qemu_comm_method="monitor" +silent= + +echo +echo === Testing VPC Autodetect === +echo +_use_sample_img virtualpc-dynamic.vhd.bz2 + +${QEMU_IO} -c "open -o driver=vpc ${TEST_IMG}" -c 'map' + +echo +echo === Testing VPC with current_size force === +echo + +${QEMU_IO} -c "open -o driver=vpc,force_size_calc=current_size ${TEST_IMG}" -c 'map' + +echo +echo === Testing VPC with chs force === +echo + +${QEMU_IO} -c "open -o driver=vpc,force_size_calc=chs ${TEST_IMG}" -c 'map' + +_cleanup_test_img + +echo +echo === Testing Hyper-V Autodetect === +echo +_use_sample_img hyperv2012r2-dynamic.vhd.bz2 + +${QEMU_IO} -c "open -o driver=vpc ${TEST_IMG}" -c 'map' + +echo +echo === Testing Hyper-V with current_size force === +echo + +${QEMU_IO} -c "open -o driver=vpc,force_size_calc=current_size ${TEST_IMG}" -c 'map' + +echo +echo === Testing Hyper-V with chs force === +echo + +${QEMU_IO} -c "open -o driver=vpc,force_size_calc=chs ${TEST_IMG}" -c 'map' + +_cleanup_test_img + +echo +echo === Testing d2v Autodetect === +echo +_use_sample_img d2v-zerofilled.vhd.bz2 + +${QEMU_IO} -c "open -o driver=vpc ${TEST_IMG}" -c 'map' + +echo +echo === Testing d2v with current_size force === +echo + +${QEMU_IO} -c "open -o driver=vpc,force_size_calc=current_size ${TEST_IMG}" -c 'map' + +echo +echo === Testing d2v with chs force === +echo + +${QEMU_IO} -c "open -o driver=vpc,force_size_calc=chs ${TEST_IMG}" -c 'map' + +_cleanup_test_img + +echo +echo === Testing Image create, default === +echo + +TEST_IMG="${TEST_DIR}/vpc-create-test.vpc" + +_make_test_img 4G + +echo +echo === Read created image, default opts ==== +echo + +${QEMU_IO} -c "open -o driver=vpc ${TEST_IMG}" -c 'map' + +echo +echo === Read created image, force_size_calc=chs ==== +echo + +${QEMU_IO} -c "open -o driver=vpc,force_size_calc=chs ${TEST_IMG}" -c 'map' + +echo +echo === Read created image, force_size_calc=current_size ==== +echo + +${QEMU_IO} -c "open -o driver=vpc,force_size_calc=current_size ${TEST_IMG}" -c 'map' + +echo +echo === Testing Image create, force_size === +echo + +_make_test_img -o force_size 4G + +echo +echo === Read created image, default opts ==== +echo + +${QEMU_IO} -c "open -o driver=vpc ${TEST_IMG}" -c 'map' + +echo +echo === Read created image, force_size_calc=chs ==== +echo + +${QEMU_IO} -c "open -o driver=vpc,force_size_calc=chs ${TEST_IMG}" -c 'map' + +echo +echo === Read created image, force_size_calc=current_size ==== +echo + +${QEMU_IO} -c "open -o driver=vpc,force_size_calc=current_size ${TEST_IMG}" -c 'map' + +echo "*** done" +rm -f $seq.full +status=0 diff --git a/qemu/tests/qemu-iotests/146.out b/qemu/tests/qemu-iotests/146.out new file mode 100644 index 000000000..4f334d86b --- /dev/null +++ b/qemu/tests/qemu-iotests/146.out @@ -0,0 +1,70 @@ +QA output created by 146 + +=== Testing VPC Autodetect === + +[ 0] 266334240/ 266334240 sectors not allocated at offset 0 bytes (0) + +=== Testing VPC with current_size force === + +[ 0] 266338304/ 266338304 sectors not allocated at offset 0 bytes (0) + +=== Testing VPC with chs force === + +[ 0] 266334240/ 266334240 sectors not allocated at offset 0 bytes (0) + +=== Testing Hyper-V Autodetect === + +[ 0] 266338304/ 266338304 sectors not allocated at offset 0 bytes (0) + +=== Testing Hyper-V with current_size force === + +[ 0] 266338304/ 266338304 sectors not allocated at offset 0 bytes (0) + +=== Testing Hyper-V with chs force === + +[ 0] 266334240/ 266334240 sectors not allocated at offset 0 bytes (0) + +=== Testing d2v Autodetect === + +[ 0] 514560/ 514560 sectors allocated at offset 0 bytes (1) + +=== Testing d2v with current_size force === + +[ 0] 514560/ 514560 sectors allocated at offset 0 bytes (1) + +=== Testing d2v with chs force === + +[ 0] 514560/ 514560 sectors allocated at offset 0 bytes (1) + +=== Testing Image create, default === + +Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296 + +=== Read created image, default opts ==== + +[ 0] 8389584/ 8389584 sectors not allocated at offset 0 bytes (0) + +=== Read created image, force_size_calc=chs ==== + +[ 0] 8389584/ 8389584 sectors not allocated at offset 0 bytes (0) + +=== Read created image, force_size_calc=current_size ==== + +[ 0] 8389584/ 8389584 sectors not allocated at offset 0 bytes (0) + +=== Testing Image create, force_size === + +Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296 force_size=on + +=== Read created image, default opts ==== + +[ 0] 8388608/ 8388608 sectors not allocated at offset 0 bytes (0) + +=== Read created image, force_size_calc=chs ==== + +[ 0] 8388608/ 8388608 sectors not allocated at offset 0 bytes (0) + +=== Read created image, force_size_calc=current_size ==== + +[ 0] 8388608/ 8388608 sectors not allocated at offset 0 bytes (0) +*** done diff --git a/qemu/tests/qemu-iotests/148 b/qemu/tests/qemu-iotests/148 new file mode 100644 index 000000000..e01b061fe --- /dev/null +++ b/qemu/tests/qemu-iotests/148 @@ -0,0 +1,140 @@ +#!/usr/bin/env python +# +# Test the rate limit of QMP events +# +# Copyright (C) 2016 Igalia, S.L. +# Author: Alberto Garcia +# +# 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 . +# + +import os +import iotests + +imgs = (os.path.join(iotests.test_dir, 'quorum0.img'), + os.path.join(iotests.test_dir, 'quorum1.img'), + os.path.join(iotests.test_dir, 'quorum2.img')) + +img_conf = (os.path.join(iotests.test_dir, 'quorum0.conf'), + os.path.join(iotests.test_dir, 'quorum1.conf'), + os.path.join(iotests.test_dir, 'quorum2.conf')) + +event_rate = 1000000000 +sector_size = 512 +offset = 10 + +class TestQuorumEvents(iotests.QMPTestCase): + read_pattern = 'quorum' + + def create_blkdebug_file(self, blkdebug_file, bad_sector): + file = open(blkdebug_file, 'w') + file.write(''' +[inject-error] +event = "read_aio" +errno = "5" +sector = "%d" +''' % bad_sector) + file.close() + + def setUp(self): + driveopts = ['driver=quorum', 'vote-threshold=2'] + driveopts.append('read-pattern=%s' % self.read_pattern) + for i in range(len(imgs)): + iotests.qemu_img('create', '-f', iotests.imgfmt, imgs[i], '1M') + self.create_blkdebug_file(img_conf[i], i + offset) + driveopts.append('children.%d.driver=%s' % (i, iotests.imgfmt)) + driveopts.append('children.%d.file.driver=blkdebug' % i) + driveopts.append('children.%d.file.config=%s' % (i, img_conf[i])) + driveopts.append('children.%d.file.image.filename=%s' % (i, imgs[i])) + driveopts.append('children.%d.node-name=img%d' % (i, i)) + self.vm = iotests.VM() + self.vm.add_drive(None, opts = ','.join(driveopts)) + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + for i in range(len(imgs)): + os.remove(imgs[i]) + os.remove(img_conf[i]) + + def do_check_event(self, node, sector = 0): + if node == None: + self.assertEqual(self.vm.get_qmp_event(), None) + return + + for event in self.vm.get_qmp_events(wait=True): + if event['event'] == 'QUORUM_REPORT_BAD': + self.assert_qmp(event, 'data/node-name', node) + self.assert_qmp(event, 'data/sector-num', sector) + + def testQuorum(self): + # Generate an error and get an event + self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % + (offset * sector_size, sector_size)) + self.vm.qtest("clock_step 10") + self.do_check_event('img0', offset) + + # I/O errors in the same child: only one event is emitted + delay = 10 + for i in range(3): + self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % + (offset * sector_size, sector_size)) + self.vm.qtest("clock_step %d" % delay) + self.do_check_event(None) + + # Wait enough so the event is finally emitted + self.vm.qtest("clock_step %d" % (2 * event_rate)) + self.do_check_event('img0', offset) + + # I/O errors in the same child: all events are emitted + delay = 2 * event_rate + for i in range(3): + self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % + (offset * sector_size, sector_size)) + self.vm.qtest("clock_step %d" % delay) + self.do_check_event('img0', offset) + + # I/O errors in different children: all events are emitted + delay = 10 + for i in range(len(imgs)): + self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % + ((offset + i) * sector_size, sector_size)) + self.vm.qtest("clock_step %d" % delay) + # In fifo mode only errors in the first child are detected + if i > 0 and self.read_pattern == 'fifo': + self.do_check_event(None) + else: + self.do_check_event('img%d' % i, offset + i) + + # I/O errors in different children: all events are emitted + delay = 2 * event_rate + for i in range(len(imgs)): + self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % + ((offset + i) * sector_size, sector_size)) + self.vm.qtest("clock_step %d" % delay) + # In fifo mode only errors in the first child are detected + if i > 0 and self.read_pattern == 'fifo': + self.do_check_event(None) + else: + self.do_check_event('img%d' % i, offset + i) + + # No more pending events + self.do_check_event(None) + +class TestFifoQuorumEvents(TestQuorumEvents): + read_pattern = 'fifo' + +if __name__ == '__main__': + iotests.verify_quorum() + iotests.main(supported_fmts=["raw"]) diff --git a/qemu/tests/qemu-iotests/148.out b/qemu/tests/qemu-iotests/148.out new file mode 100644 index 000000000..fbc63e62f --- /dev/null +++ b/qemu/tests/qemu-iotests/148.out @@ -0,0 +1,5 @@ +.. +---------------------------------------------------------------------- +Ran 2 tests + +OK diff --git a/qemu/tests/qemu-iotests/149 b/qemu/tests/qemu-iotests/149 new file mode 100755 index 000000000..52e23d294 --- /dev/null +++ b/qemu/tests/qemu-iotests/149 @@ -0,0 +1,519 @@ +#!/usr/bin/env python +# +# Copyright (C) 2016 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 . +# +# Creator/Owner: Daniel P. Berrange +# +# Exercise the QEMU 'luks' block driver to validate interoperability +# with the Linux dm-crypt + cryptsetup implementation + +import subprocess +import os +import os.path + +import base64 + +import iotests + + +class LUKSConfig(object): + """Represent configuration parameters for a single LUKS + setup to be tested""" + + def __init__(self, name, cipher, keylen, mode, ivgen, + ivgen_hash, hash, password=None, passwords=None): + + self.name = name + self.cipher = cipher + self.keylen = keylen + self.mode = mode + self.ivgen = ivgen + self.ivgen_hash = ivgen_hash + self.hash = hash + + if passwords is not None: + self.passwords = passwords + else: + self.passwords = {} + + if password is None: + self.passwords["0"] = "123456" + else: + self.passwords["0"] = password + + def __repr__(self): + return self.name + + def image_name(self): + return "luks-%s.img" % self.name + + def image_path(self): + return os.path.join(iotests.test_dir, self.image_name()) + + def device_name(self): + return "qiotest-145-%s" % self.name + + def device_path(self): + return "/dev/mapper/" + self.device_name() + + def first_password(self): + for i in range(8): + slot = str(i) + if slot in self.passwords: + return (self.passwords[slot], slot) + raise Exception("No password found") + + def first_password_base64(self): + (pw, slot) = self.first_password() + return base64.b64encode(pw) + + def active_slots(self): + slots = [] + for i in range(8): + slot = str(i) + if slot in self.passwords: + slots.append(slot) + return slots + +def verify_passwordless_sudo(): + """Check whether sudo is configured to allow + password-less access to commands""" + + args = ["sudo", "-n", "/bin/true"] + + proc = subprocess.Popen(args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + + msg = proc.communicate()[0] + + if proc.returncode != 0: + iotests.notrun('requires password-less sudo access: %s' % msg) + + +def cryptsetup(args, password=None): + """Run the cryptsetup command in batch mode""" + + fullargs = ["sudo", "cryptsetup", "-q", "-v"] + fullargs.extend(args) + + iotests.log(" ".join(fullargs), filters=[iotests.filter_test_dir]) + proc = subprocess.Popen(fullargs, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + + msg = proc.communicate(password)[0] + + if proc.returncode != 0: + raise Exception(msg) + + +def cryptsetup_add_password(config, slot): + """Add another password to a LUKS key slot""" + + (password, mainslot) = config.first_password() + + pwfile = os.path.join(iotests.test_dir, "passwd.txt") + with open(pwfile, "w") as fh: + fh.write(config.passwords[slot]) + + try: + args = ["luksAddKey", config.image_path(), + "--key-slot", slot, + "--key-file", "-", + pwfile] + + cryptsetup(args, password) + finally: + os.unlink(pwfile) + + +def cryptsetup_format(config): + """Format a new LUKS volume with cryptsetup, adding the + first key slot only""" + + (password, slot) = config.first_password() + + args = ["luksFormat"] + cipher = config.cipher + "-" + config.mode + "-" + config.ivgen + if config.ivgen_hash is not None: + cipher = cipher + ":" + config.ivgen_hash + args.extend(["--cipher", cipher]) + if config.mode == "xts": + args.extend(["--key-size", str(config.keylen * 2)]) + else: + args.extend(["--key-size", str(config.keylen)]) + if config.hash is not None: + args.extend(["--hash", config.hash]) + args.extend(["--key-slot", slot]) + args.extend(["--key-file", "-"]) + args.append(config.image_path()) + + cryptsetup(args, password) + + +def chown(config): + """Set the ownership of a open LUKS device to this user""" + + path = config.device_path() + + args = ["sudo", "chown", "%d:%d" % (os.getuid(), os.getgid()), path] + iotests.log(" ".join(args), filters=[iotests.filter_chown]) + proc = subprocess.Popen(args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + + msg = proc.communicate()[0] + + if proc.returncode != 0: + raise Exception("Cannot change owner on %s" % path) + + +def cryptsetup_open(config): + """Open an image as a LUKS device""" + + (password, slot) = config.first_password() + + args = ["luksOpen", config.image_path(), config.device_name()] + + cryptsetup(args, password) + + +def cryptsetup_close(config): + """Close an active LUKS device """ + + args = ["luksClose", config.device_name()] + cryptsetup(args) + + +def delete_image(config): + """Delete a disk image""" + + try: + os.unlink(config.image_path()) + iotests.log("unlink %s" % config.image_path(), + filters=[iotests.filter_test_dir]) + except Exception as e: + pass + + +def create_image(config, size_mb): + """Create a bare disk image with requested size""" + + delete_image(config) + iotests.log("truncate %s --size %dMB" % (config.image_path(), size_mb), + filters=[iotests.filter_test_dir]) + with open(config.image_path(), "w") as fn: + fn.truncate(size_mb * 1024 * 1024) + + +def qemu_img_create(config, size_mb): + """Create and format a disk image with LUKS using qemu-img""" + + opts = [ + "key-secret=sec0", + "cipher-alg=%s-%d" % (config.cipher, config.keylen), + "cipher-mode=%s" % config.mode, + "ivgen-alg=%s" % config.ivgen, + "hash-alg=%s" % config.hash, + ] + if config.ivgen_hash is not None: + opts.append("ivgen-hash-alg=%s" % config.ivgen_hash) + + args = ["create", "-f", "luks", + "--object", + ("secret,id=sec0,data=%s,format=base64" % + config.first_password_base64()), + "-o", ",".join(opts), + config.image_path(), + "%dM" % size_mb] + + iotests.log("qemu-img " + " ".join(args), filters=[iotests.filter_test_dir]) + iotests.log(iotests.qemu_img_pipe(*args), filters=[iotests.filter_test_dir]) + +def qemu_io_image_args(config, dev=False): + """Get the args for access an image or device with qemu-io""" + + if dev: + return [ + "--image-opts", + "driver=file,filename=%s" % config.device_path()] + else: + return [ + "--object", + ("secret,id=sec0,data=%s,format=base64" % + config.first_password_base64()), + "--image-opts", + ("driver=luks,key-secret=sec0,file.filename=%s" % + config.image_path())] + +def qemu_io_write_pattern(config, pattern, offset_mb, size_mb, dev=False): + """Write a pattern of data to a LUKS image or device""" + + args = ["-c", "write -P 0x%x %dM %dM" % (pattern, offset_mb, size_mb)] + args.extend(qemu_io_image_args(config, dev)) + iotests.log("qemu-io " + " ".join(args), filters=[iotests.filter_test_dir]) + iotests.log(iotests.qemu_io(*args), filters=[iotests.filter_test_dir, + iotests.filter_qemu_io]) + + +def qemu_io_read_pattern(config, pattern, offset_mb, size_mb, dev=False): + """Read a pattern of data to a LUKS image or device""" + + args = ["-c", "read -P 0x%x %dM %dM" % (pattern, offset_mb, size_mb)] + args.extend(qemu_io_image_args(config, dev)) + iotests.log("qemu-io " + " ".join(args), filters=[iotests.filter_test_dir]) + iotests.log(iotests.qemu_io(*args), filters=[iotests.filter_test_dir, + iotests.filter_qemu_io]) + + +def test_once(config, qemu_img=False): + """Run the test with a desired LUKS configuration. Can either + use qemu-img for creating the initial volume, or cryptsetup, + in order to test interoperability in both directions""" + + iotests.log("# ================= %s %s =================" % ( + "qemu-img" if qemu_img else "dm-crypt", config)) + + oneKB = 1024 + oneMB = oneKB * 1024 + oneGB = oneMB * 1024 + oneTB = oneGB * 1024 + + # 4 TB, so that we pass the 32-bit sector number boundary. + # Important for testing correctness of some IV generators + # The files are sparse, so not actually using this much space + image_size = 4 * oneTB + if qemu_img: + iotests.log("# Create image") + qemu_img_create(config, image_size / oneMB) + else: + iotests.log("# Create image") + create_image(config, image_size / oneMB) + + lowOffsetMB = 100 + highOffsetMB = 3 * oneTB / oneMB + + try: + if not qemu_img: + iotests.log("# Format image") + cryptsetup_format(config) + + for slot in config.active_slots()[1:]: + iotests.log("# Add password slot %s" % slot) + cryptsetup_add_password(config, slot) + + # First we'll open the image using cryptsetup and write a + # known pattern of data that we'll then verify with QEMU + + iotests.log("# Open dev") + cryptsetup_open(config) + + try: + iotests.log("# Set dev owner") + chown(config) + + iotests.log("# Write test pattern 0xa7") + qemu_io_write_pattern(config, 0xa7, lowOffsetMB, 10, dev=True) + iotests.log("# Write test pattern 0x13") + qemu_io_write_pattern(config, 0x13, highOffsetMB, 10, dev=True) + finally: + iotests.log("# Close dev") + cryptsetup_close(config) + + # Ok, now we're using QEMU to verify the pattern just + # written via dm-crypt + + iotests.log("# Read test pattern 0xa7") + qemu_io_read_pattern(config, 0xa7, lowOffsetMB, 10, dev=False) + iotests.log("# Read test pattern 0x13") + qemu_io_read_pattern(config, 0x13, highOffsetMB, 10, dev=False) + + + # Write a new pattern to the image, which we'll later + # verify with dm-crypt + iotests.log("# Write test pattern 0x91") + qemu_io_write_pattern(config, 0x91, lowOffsetMB, 10, dev=False) + iotests.log("# Write test pattern 0x5e") + qemu_io_write_pattern(config, 0x5e, highOffsetMB, 10, dev=False) + + + # Now we're opening the image with dm-crypt once more + # and verifying what QEMU wrote, completing the circle + iotests.log("# Open dev") + cryptsetup_open(config) + + try: + iotests.log("# Set dev owner") + chown(config) + + iotests.log("# Read test pattern 0x91") + qemu_io_read_pattern(config, 0x91, lowOffsetMB, 10, dev=True) + iotests.log("# Read test pattern 0x5e") + qemu_io_read_pattern(config, 0x5e, highOffsetMB, 10, dev=True) + finally: + iotests.log("# Close dev") + cryptsetup_close(config) + finally: + iotests.log("# Delete image") + delete_image(config) + print + + +# Obviously we only work with the luks image format +iotests.verify_image_format(supported_fmts=['luks']) +iotests.verify_platform() + +# We need sudo in order to run cryptsetup to create +# dm-crypt devices. This is safe to use on any +# machine, since all dm-crypt devices are backed +# by newly created plain files, and have a dm-crypt +# name prefix of 'qiotest' to avoid clashing with +# user LUKS volumes +verify_passwordless_sudo() + + +# If we look at all permutations of cipher, key size, +# mode, ivgen, hash, there are ~1000 possible configs. +# +# We certainly don't want/need to test every permutation +# to get good validation of interoperability between QEMU +# and dm-crypt/cryptsetup. +# +# The configs below are a representative set that aim to +# exercise each axis of configurability. +# +configs = [ + # A common LUKS default + LUKSConfig("aes-256-xts-plain64-sha1", + "aes", 256, "xts", "plain64", None, "sha1"), + + + # LUKS default but diff ciphers + LUKSConfig("twofish-256-xts-plain64-sha1", + "twofish", 256, "xts", "plain64", None, "sha1"), + LUKSConfig("serpent-256-xts-plain64-sha1", + "serpent", 256, "xts", "plain64", None, "sha1"), + # Should really be xts, but kernel doesn't support xts+cast5 + # nor does it do essiv+cast5 + LUKSConfig("cast5-128-cbc-plain64-sha1", + "cast5", 128, "cbc", "plain64", None, "sha1"), + LUKSConfig("cast6-256-xts-plain64-sha1", + "cast6", 256, "xts", "plain64", None, "sha1"), + + + # LUKS default but diff modes / ivgens + LUKSConfig("aes-256-cbc-plain-sha1", + "aes", 256, "cbc", "plain", None, "sha1"), + LUKSConfig("aes-256-cbc-plain64-sha1", + "aes", 256, "cbc", "plain64", None, "sha1"), + LUKSConfig("aes-256-cbc-essiv-sha256-sha1", + "aes", 256, "cbc", "essiv", "sha256", "sha1"), + LUKSConfig("aes-256-xts-essiv-sha256-sha1", + "aes", 256, "xts", "essiv", "sha256", "sha1"), + + + # LUKS default but smaller key sizes + LUKSConfig("aes-128-xts-plain64-sha256-sha1", + "aes", 128, "xts", "plain64", None, "sha1"), + LUKSConfig("aes-192-xts-plain64-sha256-sha1", + "aes", 192, "xts", "plain64", None, "sha1"), + + LUKSConfig("twofish-128-xts-plain64-sha1", + "twofish", 128, "xts", "plain64", None, "sha1"), + LUKSConfig("twofish-192-xts-plain64-sha1", + "twofish", 192, "xts", "plain64", None, "sha1"), + + LUKSConfig("serpent-128-xts-plain64-sha1", + "serpent", 128, "xts", "plain64", None, "sha1"), + LUKSConfig("serpent-192-xts-plain64-sha1", + "serpent", 192, "xts", "plain64", None, "sha1"), + + LUKSConfig("cast6-128-xts-plain64-sha1", + "cast6", 128, "xts", "plain", None, "sha1"), + LUKSConfig("cast6-192-xts-plain64-sha1", + "cast6", 192, "xts", "plain64", None, "sha1"), + + + # LUKS default but diff hash + LUKSConfig("aes-256-xts-plain64-sha256", + "aes", 256, "xts", "plain64", None, "sha256"), + LUKSConfig("aes-256-xts-plain64-sha512", + "aes", 256, "xts", "plain64", None, "sha512"), + LUKSConfig("aes-256-xts-plain64-ripemd160", + "aes", 256, "xts", "plain64", None, "ripemd160"), + + # Password in slot 3 + LUKSConfig("aes-256-xts-plain-sha1-pwslot3", + "aes", 256, "xts", "plain", None, "sha1", + passwords={ + "3": "slot3", + }), + + # Passwords in every slot + LUKSConfig("aes-256-xts-plain-sha1-pwallslots", + "aes", 256, "xts", "plain", None, "sha1", + passwords={ + "0": "slot1", + "1": "slot1", + "2": "slot2", + "3": "slot3", + "4": "slot4", + "5": "slot5", + "6": "slot6", + "7": "slot7", + }), +] + +blacklist = [ + # We don't have a cast-6 cipher impl for QEMU yet + "cast6-256-xts-plain64-sha1", + "cast6-128-xts-plain64-sha1", + "cast6-192-xts-plain64-sha1", + + # GCrypt doesn't support Twofish with 192 bit key + "twofish-192-xts-plain64-sha1", + + # We don't have sha512 hash wired up yet + "aes-256-xts-plain64-sha512", + + # We don't have ripemd160 hash wired up yet + "aes-256-xts-plain64-ripemd160", +] + +whitelist = [] +if "LUKS_CONFIG" in os.environ: + whitelist = os.environ["LUKS_CONFIG"].split(",") + +for config in configs: + if config.name in blacklist: + iotests.log("Skipping %s in blacklist" % config.name) + continue + + if len(whitelist) > 0 and config.name not in whitelist: + iotests.log("Skipping %s not in whitelist" % config.name) + continue + + test_once(config, qemu_img=False) + + # XXX we should support setting passwords in a non-0 + # key slot with 'qemu-img create' in future + (pw, slot) = config.first_password() + if slot == "0": + test_once(config, qemu_img=True) diff --git a/qemu/tests/qemu-iotests/149.out b/qemu/tests/qemu-iotests/149.out new file mode 100644 index 000000000..287f01301 --- /dev/null +++ b/qemu/tests/qemu-iotests/149.out @@ -0,0 +1,1880 @@ +# ================= dm-crypt aes-256-xts-plain64-sha1 ================= +# Create image +truncate TEST_DIR/luks-aes-256-xts-plain64-sha1.img --size 4194304MB +# Format image +sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - TEST_DIR/luks-aes-256-xts-plain64-sha1.img +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha1.img qiotest-145-aes-256-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-xts-plain64-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha1.img qiotest-145-aes-256-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-xts-plain64-sha1 +# Delete image +unlink TEST_DIR/luks-aes-256-xts-plain64-sha1.img + +# ================= qemu-img aes-256-xts-plain64-sha1 ================= +# Create image +qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-aes-256-xts-plain64-sha1.img 4194304M +Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha1.img qiotest-145-aes-256-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-xts-plain64-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha1.img qiotest-145-aes-256-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-xts-plain64-sha1 +# Delete image +unlink TEST_DIR/luks-aes-256-xts-plain64-sha1.img + +# ================= dm-crypt twofish-256-xts-plain64-sha1 ================= +# Create image +truncate TEST_DIR/luks-twofish-256-xts-plain64-sha1.img --size 4194304MB +# Format image +sudo cryptsetup -q -v luksFormat --cipher twofish-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - TEST_DIR/luks-twofish-256-xts-plain64-sha1.img +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-256-xts-plain64-sha1.img qiotest-145-twofish-256-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-twofish-256-xts-plain64-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-twofish-256-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-twofish-256-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-twofish-256-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-twofish-256-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-256-xts-plain64-sha1.img qiotest-145-twofish-256-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-twofish-256-xts-plain64-sha1 +# Delete image +unlink TEST_DIR/luks-twofish-256-xts-plain64-sha1.img + +# ================= qemu-img twofish-256-xts-plain64-sha1 ================= +# Create image +qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,cipher-alg=twofish-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-twofish-256-xts-plain64-sha1.img 4194304M +Formatting 'TEST_DIR/luks-twofish-256-xts-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=twofish-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-256-xts-plain64-sha1.img qiotest-145-twofish-256-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-twofish-256-xts-plain64-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-twofish-256-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-twofish-256-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-twofish-256-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-twofish-256-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-256-xts-plain64-sha1.img qiotest-145-twofish-256-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-twofish-256-xts-plain64-sha1 +# Delete image +unlink TEST_DIR/luks-twofish-256-xts-plain64-sha1.img + +# ================= dm-crypt serpent-256-xts-plain64-sha1 ================= +# Create image +truncate TEST_DIR/luks-serpent-256-xts-plain64-sha1.img --size 4194304MB +# Format image +sudo cryptsetup -q -v luksFormat --cipher serpent-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - TEST_DIR/luks-serpent-256-xts-plain64-sha1.img +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-256-xts-plain64-sha1.img qiotest-145-serpent-256-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-serpent-256-xts-plain64-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-256-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-256-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-256-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-256-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-256-xts-plain64-sha1.img qiotest-145-serpent-256-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-serpent-256-xts-plain64-sha1 +# Delete image +unlink TEST_DIR/luks-serpent-256-xts-plain64-sha1.img + +# ================= qemu-img serpent-256-xts-plain64-sha1 ================= +# Create image +qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,cipher-alg=serpent-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-serpent-256-xts-plain64-sha1.img 4194304M +Formatting 'TEST_DIR/luks-serpent-256-xts-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=serpent-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-256-xts-plain64-sha1.img qiotest-145-serpent-256-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-serpent-256-xts-plain64-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-256-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-256-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-256-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-256-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-256-xts-plain64-sha1.img qiotest-145-serpent-256-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-serpent-256-xts-plain64-sha1 +# Delete image +unlink TEST_DIR/luks-serpent-256-xts-plain64-sha1.img + +# ================= dm-crypt cast5-128-cbc-plain64-sha1 ================= +# Create image +truncate TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img --size 4194304MB +# Format image +sudo cryptsetup -q -v luksFormat --cipher cast5-cbc-plain64 --key-size 128 --hash sha1 --key-slot 0 --key-file - TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img qiotest-145-cast5-128-cbc-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-cast5-128-cbc-plain64-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img qiotest-145-cast5-128-cbc-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-cast5-128-cbc-plain64-sha1 +# Delete image +unlink TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img + +# ================= qemu-img cast5-128-cbc-plain64-sha1 ================= +# Create image +qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,cipher-alg=cast5-128,cipher-mode=cbc,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img 4194304M +Formatting 'TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=cast5-128 cipher-mode=cbc ivgen-alg=plain64 hash-alg=sha1 + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img qiotest-145-cast5-128-cbc-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-cast5-128-cbc-plain64-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img qiotest-145-cast5-128-cbc-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-cast5-128-cbc-plain64-sha1 +# Delete image +unlink TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img + +Skipping cast6-256-xts-plain64-sha1 in blacklist +# ================= dm-crypt aes-256-cbc-plain-sha1 ================= +# Create image +truncate TEST_DIR/luks-aes-256-cbc-plain-sha1.img --size 4194304MB +# Format image +sudo cryptsetup -q -v luksFormat --cipher aes-cbc-plain --key-size 256 --hash sha1 --key-slot 0 --key-file - TEST_DIR/luks-aes-256-cbc-plain-sha1.img +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain-sha1.img qiotest-145-aes-256-cbc-plain-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-plain-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain-sha1.img qiotest-145-aes-256-cbc-plain-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-plain-sha1 +# Delete image +unlink TEST_DIR/luks-aes-256-cbc-plain-sha1.img + +# ================= qemu-img aes-256-cbc-plain-sha1 ================= +# Create image +qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=plain,hash-alg=sha1 TEST_DIR/luks-aes-256-cbc-plain-sha1.img 4194304M +Formatting 'TEST_DIR/luks-aes-256-cbc-plain-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=cbc ivgen-alg=plain hash-alg=sha1 + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain-sha1.img qiotest-145-aes-256-cbc-plain-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-plain-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain-sha1.img qiotest-145-aes-256-cbc-plain-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-plain-sha1 +# Delete image +unlink TEST_DIR/luks-aes-256-cbc-plain-sha1.img + +# ================= dm-crypt aes-256-cbc-plain64-sha1 ================= +# Create image +truncate TEST_DIR/luks-aes-256-cbc-plain64-sha1.img --size 4194304MB +# Format image +sudo cryptsetup -q -v luksFormat --cipher aes-cbc-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - TEST_DIR/luks-aes-256-cbc-plain64-sha1.img +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha1.img qiotest-145-aes-256-cbc-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-plain64-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain64-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain64-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha1.img qiotest-145-aes-256-cbc-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-plain64-sha1 +# Delete image +unlink TEST_DIR/luks-aes-256-cbc-plain64-sha1.img + +# ================= qemu-img aes-256-cbc-plain64-sha1 ================= +# Create image +qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-aes-256-cbc-plain64-sha1.img 4194304M +Formatting 'TEST_DIR/luks-aes-256-cbc-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=cbc ivgen-alg=plain64 hash-alg=sha1 + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha1.img qiotest-145-aes-256-cbc-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-plain64-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain64-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain64-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha1.img qiotest-145-aes-256-cbc-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-plain64-sha1 +# Delete image +unlink TEST_DIR/luks-aes-256-cbc-plain64-sha1.img + +# ================= dm-crypt aes-256-cbc-essiv-sha256-sha1 ================= +# Create image +truncate TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img --size 4194304MB +# Format image +sudo cryptsetup -q -v luksFormat --cipher aes-cbc-essiv:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img qiotest-145-aes-256-cbc-essiv-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-essiv-sha256-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img qiotest-145-aes-256-cbc-essiv-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-essiv-sha256-sha1 +# Delete image +unlink TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img + +# ================= qemu-img aes-256-cbc-essiv-sha256-sha1 ================= +# Create image +qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=essiv,hash-alg=sha1,ivgen-hash-alg=sha256 TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img 4194304M +Formatting 'TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=cbc ivgen-alg=essiv ivgen-hash-alg=sha256 hash-alg=sha1 + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img qiotest-145-aes-256-cbc-essiv-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-essiv-sha256-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img qiotest-145-aes-256-cbc-essiv-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-essiv-sha256-sha1 +# Delete image +unlink TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img + +# ================= dm-crypt aes-256-xts-essiv-sha256-sha1 ================= +# Create image +truncate TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img --size 4194304MB +# Format image +sudo cryptsetup -q -v luksFormat --cipher aes-xts-essiv:sha256 --key-size 512 --hash sha1 --key-slot 0 --key-file - TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img qiotest-145-aes-256-xts-essiv-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-xts-essiv-sha256-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img qiotest-145-aes-256-xts-essiv-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-xts-essiv-sha256-sha1 +# Delete image +unlink TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img + +# ================= qemu-img aes-256-xts-essiv-sha256-sha1 ================= +# Create image +qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=essiv,hash-alg=sha1,ivgen-hash-alg=sha256 TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img 4194304M +Formatting 'TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=essiv ivgen-hash-alg=sha256 hash-alg=sha1 + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img qiotest-145-aes-256-xts-essiv-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-xts-essiv-sha256-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img qiotest-145-aes-256-xts-essiv-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-xts-essiv-sha256-sha1 +# Delete image +unlink TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img + +# ================= dm-crypt aes-128-xts-plain64-sha256-sha1 ================= +# Create image +truncate TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img --size 4194304MB +# Format image +sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img qiotest-145-aes-128-xts-plain64-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-128-xts-plain64-sha256-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img qiotest-145-aes-128-xts-plain64-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-128-xts-plain64-sha256-sha1 +# Delete image +unlink TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img + +# ================= qemu-img aes-128-xts-plain64-sha256-sha1 ================= +# Create image +qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,cipher-alg=aes-128,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img 4194304M +Formatting 'TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-128 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img qiotest-145-aes-128-xts-plain64-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-128-xts-plain64-sha256-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img qiotest-145-aes-128-xts-plain64-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-128-xts-plain64-sha256-sha1 +# Delete image +unlink TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img + +# ================= dm-crypt aes-192-xts-plain64-sha256-sha1 ================= +# Create image +truncate TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img --size 4194304MB +# Format image +sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 384 --hash sha1 --key-slot 0 --key-file - TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img qiotest-145-aes-192-xts-plain64-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-192-xts-plain64-sha256-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img qiotest-145-aes-192-xts-plain64-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-192-xts-plain64-sha256-sha1 +# Delete image +unlink TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img + +# ================= qemu-img aes-192-xts-plain64-sha256-sha1 ================= +# Create image +qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,cipher-alg=aes-192,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img 4194304M +Formatting 'TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-192 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img qiotest-145-aes-192-xts-plain64-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-192-xts-plain64-sha256-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img qiotest-145-aes-192-xts-plain64-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-192-xts-plain64-sha256-sha1 +# Delete image +unlink TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img + +# ================= dm-crypt twofish-128-xts-plain64-sha1 ================= +# Create image +truncate TEST_DIR/luks-twofish-128-xts-plain64-sha1.img --size 4194304MB +# Format image +sudo cryptsetup -q -v luksFormat --cipher twofish-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - TEST_DIR/luks-twofish-128-xts-plain64-sha1.img +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-128-xts-plain64-sha1.img qiotest-145-twofish-128-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-twofish-128-xts-plain64-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-twofish-128-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-twofish-128-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-twofish-128-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-twofish-128-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-128-xts-plain64-sha1.img qiotest-145-twofish-128-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-twofish-128-xts-plain64-sha1 +# Delete image +unlink TEST_DIR/luks-twofish-128-xts-plain64-sha1.img + +# ================= qemu-img twofish-128-xts-plain64-sha1 ================= +# Create image +qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,cipher-alg=twofish-128,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-twofish-128-xts-plain64-sha1.img 4194304M +Formatting 'TEST_DIR/luks-twofish-128-xts-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=twofish-128 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-128-xts-plain64-sha1.img qiotest-145-twofish-128-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-twofish-128-xts-plain64-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-twofish-128-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-twofish-128-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-twofish-128-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-twofish-128-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-128-xts-plain64-sha1.img qiotest-145-twofish-128-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-twofish-128-xts-plain64-sha1 +# Delete image +unlink TEST_DIR/luks-twofish-128-xts-plain64-sha1.img + +Skipping twofish-192-xts-plain64-sha1 in blacklist +# ================= dm-crypt serpent-128-xts-plain64-sha1 ================= +# Create image +truncate TEST_DIR/luks-serpent-128-xts-plain64-sha1.img --size 4194304MB +# Format image +sudo cryptsetup -q -v luksFormat --cipher serpent-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - TEST_DIR/luks-serpent-128-xts-plain64-sha1.img +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-128-xts-plain64-sha1.img qiotest-145-serpent-128-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-serpent-128-xts-plain64-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-128-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-128-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-128-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-128-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-128-xts-plain64-sha1.img qiotest-145-serpent-128-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-serpent-128-xts-plain64-sha1 +# Delete image +unlink TEST_DIR/luks-serpent-128-xts-plain64-sha1.img + +# ================= qemu-img serpent-128-xts-plain64-sha1 ================= +# Create image +qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,cipher-alg=serpent-128,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-serpent-128-xts-plain64-sha1.img 4194304M +Formatting 'TEST_DIR/luks-serpent-128-xts-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=serpent-128 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-128-xts-plain64-sha1.img qiotest-145-serpent-128-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-serpent-128-xts-plain64-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-128-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-128-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-128-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-128-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-128-xts-plain64-sha1.img qiotest-145-serpent-128-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-serpent-128-xts-plain64-sha1 +# Delete image +unlink TEST_DIR/luks-serpent-128-xts-plain64-sha1.img + +# ================= dm-crypt serpent-192-xts-plain64-sha1 ================= +# Create image +truncate TEST_DIR/luks-serpent-192-xts-plain64-sha1.img --size 4194304MB +# Format image +sudo cryptsetup -q -v luksFormat --cipher serpent-xts-plain64 --key-size 384 --hash sha1 --key-slot 0 --key-file - TEST_DIR/luks-serpent-192-xts-plain64-sha1.img +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-192-xts-plain64-sha1.img qiotest-145-serpent-192-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-serpent-192-xts-plain64-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-192-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-192-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-192-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-192-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-192-xts-plain64-sha1.img qiotest-145-serpent-192-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-serpent-192-xts-plain64-sha1 +# Delete image +unlink TEST_DIR/luks-serpent-192-xts-plain64-sha1.img + +# ================= qemu-img serpent-192-xts-plain64-sha1 ================= +# Create image +qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,cipher-alg=serpent-192,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-serpent-192-xts-plain64-sha1.img 4194304M +Formatting 'TEST_DIR/luks-serpent-192-xts-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=serpent-192 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-192-xts-plain64-sha1.img qiotest-145-serpent-192-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-serpent-192-xts-plain64-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-192-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-192-xts-plain64-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-192-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-serpent-192-xts-plain64-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-192-xts-plain64-sha1.img qiotest-145-serpent-192-xts-plain64-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-serpent-192-xts-plain64-sha1 +# Delete image +unlink TEST_DIR/luks-serpent-192-xts-plain64-sha1.img + +Skipping cast6-128-xts-plain64-sha1 in blacklist +Skipping cast6-192-xts-plain64-sha1 in blacklist +# ================= dm-crypt aes-256-xts-plain64-sha256 ================= +# Create image +truncate TEST_DIR/luks-aes-256-xts-plain64-sha256.img --size 4194304MB +# Format image +sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sha256 --key-slot 0 --key-file - TEST_DIR/luks-aes-256-xts-plain64-sha256.img +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha256.img qiotest-145-aes-256-xts-plain64-sha256 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-xts-plain64-sha256 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain64-sha256.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain64-sha256.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain64-sha256.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain64-sha256.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha256.img qiotest-145-aes-256-xts-plain64-sha256 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-xts-plain64-sha256 +# Delete image +unlink TEST_DIR/luks-aes-256-xts-plain64-sha256.img + +# ================= qemu-img aes-256-xts-plain64-sha256 ================= +# Create image +qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha256 TEST_DIR/luks-aes-256-xts-plain64-sha256.img 4194304M +Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha256.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha256 + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha256.img qiotest-145-aes-256-xts-plain64-sha256 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-xts-plain64-sha256 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain64-sha256.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain64-sha256.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain64-sha256.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain64-sha256.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha256.img qiotest-145-aes-256-xts-plain64-sha256 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-xts-plain64-sha256 +# Delete image +unlink TEST_DIR/luks-aes-256-xts-plain64-sha256.img + +Skipping aes-256-xts-plain64-sha512 in blacklist +Skipping aes-256-xts-plain64-ripemd160 in blacklist +# ================= dm-crypt aes-256-xts-plain-sha1-pwslot3 ================= +# Create image +truncate TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img --size 4194304MB +# Format image +sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain --key-size 512 --hash sha1 --key-slot 3 --key-file - TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img qiotest-145-aes-256-xts-plain-sha1-pwslot3 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-xts-plain-sha1-pwslot3 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=c2xvdDM=,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=c2xvdDM=,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=c2xvdDM=,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=c2xvdDM=,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img qiotest-145-aes-256-xts-plain-sha1-pwslot3 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-xts-plain-sha1-pwslot3 +# Delete image +unlink TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img + +# ================= dm-crypt aes-256-xts-plain-sha1-pwallslots ================= +# Create image +truncate TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img --size 4194304MB +# Format image +sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain --key-size 512 --hash sha1 --key-slot 0 --key-file - TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img +# Add password slot 1 +sudo cryptsetup -q -v luksAddKey TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img --key-slot 1 --key-file - TEST_DIR/passwd.txt +# Add password slot 2 +sudo cryptsetup -q -v luksAddKey TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img --key-slot 2 --key-file - TEST_DIR/passwd.txt +# Add password slot 3 +sudo cryptsetup -q -v luksAddKey TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img --key-slot 3 --key-file - TEST_DIR/passwd.txt +# Add password slot 4 +sudo cryptsetup -q -v luksAddKey TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img --key-slot 4 --key-file - TEST_DIR/passwd.txt +# Add password slot 5 +sudo cryptsetup -q -v luksAddKey TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img --key-slot 5 --key-file - TEST_DIR/passwd.txt +# Add password slot 6 +sudo cryptsetup -q -v luksAddKey TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img --key-slot 6 --key-file - TEST_DIR/passwd.txt +# Add password slot 7 +sudo cryptsetup -q -v luksAddKey TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img --key-slot 7 --key-file - TEST_DIR/passwd.txt +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img qiotest-145-aes-256-xts-plain-sha1-pwallslots +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-xts-plain-sha1-pwallslots +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=c2xvdDE=,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=c2xvdDE=,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=c2xvdDE=,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=c2xvdDE=,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img qiotest-145-aes-256-xts-plain-sha1-pwallslots +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-xts-plain-sha1-pwallslots +# Delete image +unlink TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img + +# ================= qemu-img aes-256-xts-plain-sha1-pwallslots ================= +# Create image +qemu-img create -f luks --object secret,id=sec0,data=c2xvdDE=,format=base64 -o key-secret=sec0,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain,hash-alg=sha1 TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img 4194304M +Formatting 'TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=plain hash-alg=sha1 + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img qiotest-145-aes-256-xts-plain-sha1-pwallslots +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-xts-plain-sha1-pwallslots +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=c2xvdDE=,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=c2xvdDE=,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=c2xvdDE=,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=c2xvdDE=,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img qiotest-145-aes-256-xts-plain-sha1-pwallslots +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-xts-plain-sha1-pwallslots +# Delete image +unlink TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img + diff --git a/qemu/tests/qemu-iotests/150 b/qemu/tests/qemu-iotests/150 new file mode 100755 index 000000000..ee8f6375f --- /dev/null +++ b/qemu/tests/qemu-iotests/150 @@ -0,0 +1,73 @@ +#!/bin/bash +# +# Test that qemu-img convert -S 0 fully allocates the target image +# +# Copyright (C) 2016 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 . +# + +# creator +owner=mreitz@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt raw qcow2 +_supported_proto file +_supported_os Linux + + +img_size=1048576 + + +echo +echo '=== Mapping sparse conversion ===' +echo + +$QEMU_IMG_PROG convert -O "$IMGFMT" -S 512 \ + "json:{ 'driver': 'null-co', 'size': $img_size, 'read-zeroes': true }" \ + "$TEST_IMG" + +$QEMU_IMG map "$TEST_IMG" | _filter_qemu_img_map + + +echo +echo '=== Mapping non-sparse conversion ===' +echo + +$QEMU_IMG convert -O "$IMGFMT" -S 0 \ + "json:{ 'driver': 'null-co', 'size': $img_size, 'read-zeroes': true }" \ + "$TEST_IMG" + +$QEMU_IMG map "$TEST_IMG" | _filter_qemu_img_map + + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/qemu/tests/qemu-iotests/150.out b/qemu/tests/qemu-iotests/150.out new file mode 100644 index 000000000..2a54e8dcf --- /dev/null +++ b/qemu/tests/qemu-iotests/150.out @@ -0,0 +1,11 @@ +QA output created by 150 + +=== Mapping sparse conversion === + +Offset Length File + +=== Mapping non-sparse conversion === + +Offset Length File +0 0x100000 TEST_DIR/t.IMGFMT +*** done diff --git a/qemu/tests/qemu-iotests/152 b/qemu/tests/qemu-iotests/152 new file mode 100644 index 000000000..fec546d03 --- /dev/null +++ b/qemu/tests/qemu-iotests/152 @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# +# Tests for drive-mirror with source size unaligned to granularity +# +# Copyright (C) 2016 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 . +# + +import os +import iotests +from iotests import qemu_img + +test_img = os.path.join(iotests.test_dir, 'test.img') +target_img = os.path.join(iotests.test_dir, 'target.img') + +class TestUnaligned(iotests.QMPTestCase): + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, test_img, '512') + self.vm = iotests.VM().add_drive(test_img) + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + os.remove(test_img) + try: + os.remove(target_img) + except OSError: + pass + + def test_unaligned(self): + result = self.vm.qmp('drive-mirror', device='drive0', sync='full', + granularity=65536, target=target_img) + self.complete_and_wait() + self.vm.shutdown() + self.assertEqual(iotests.image_size(test_img), iotests.image_size(target_img), + "Target size doesn't match source when granularity when unaligend") + + def test_unaligned_with_update(self): + result = self.vm.qmp('drive-mirror', device='drive0', sync='full', + granularity=65536, target=target_img) + self.wait_ready() + self.vm.hmp_qemu_io('drive0', 'write 0 512') + self.complete_and_wait(wait_ready=False) + self.vm.shutdown() + self.assertEqual(iotests.image_size(test_img), iotests.image_size(target_img), + "Target size doesn't match source when granularity when unaligend") + + +if __name__ == '__main__': + iotests.main(supported_fmts=['raw', 'qcow2']) diff --git a/qemu/tests/qemu-iotests/152.out b/qemu/tests/qemu-iotests/152.out new file mode 100644 index 000000000..fbc63e62f --- /dev/null +++ b/qemu/tests/qemu-iotests/152.out @@ -0,0 +1,5 @@ +.. +---------------------------------------------------------------------- +Ran 2 tests + +OK diff --git a/qemu/tests/qemu-iotests/check b/qemu/tests/qemu-iotests/check index 1fa63193b..4cba2151e 100755 --- a/qemu/tests/qemu-iotests/check +++ b/qemu/tests/qemu-iotests/check @@ -19,7 +19,6 @@ # Control script for QA # -tmp=/tmp/$$ status=0 needwrap=true try=0 @@ -130,6 +129,8 @@ fi # exit 1 #fi +tmp="${TEST_DIR}"/$$ + _wallclock() { date "+%H %M %S" | $AWK_PROG '{ print $1*3600 + $2*60 + $3 }' @@ -146,8 +147,8 @@ _wrapup() # for hangcheck ... # remove files that were used by hangcheck # - [ -f /tmp/check.pid ] && rm -rf /tmp/check.pid - [ -f /tmp/check.sts ] && rm -rf /tmp/check.sts + [ -f "${TEST_DIR}"/check.pid ] && rm -rf "${TEST_DIR}"/check.pid + [ -f "${TEST_DIR}"/check.sts ] && rm -rf "${TEST_DIR}"/check.sts if $showme then @@ -197,8 +198,8 @@ END { if (NR > 0) { needwrap=false fi - rm -f /tmp/*.out /tmp/*.err /tmp/*.time - rm -f /tmp/check.pid /tmp/check.sts + rm -f "${TEST_DIR}"/*.out "${TEST_DIR}"/*.err "${TEST_DIR}"/*.time + rm -f "${TEST_DIR}"/check.pid "${TEST_DIR}"/check.sts rm -f $tmp.* } @@ -208,16 +209,16 @@ trap "_wrapup; exit \$status" 0 1 2 3 15 # Save pid of check in a well known place, so that hangcheck can be sure it # has the right pid (getting the pid from ps output is not reliable enough). # -rm -rf /tmp/check.pid -echo $$ >/tmp/check.pid +rm -rf "${TEST_DIR}"/check.pid +echo $$ > "${TEST_DIR}"/check.pid # for hangcheck ... # Save the status of check in a well known place, so that hangcheck can be # sure to know where check is up to (getting test number from ps output is # not reliable enough since the trace stuff has been introduced). # -rm -rf /tmp/check.sts -echo "preamble" >/tmp/check.sts +rm -rf "${TEST_DIR}"/check.sts +echo "preamble" > "${TEST_DIR}"/check.sts # don't leave old full output behind on a clean run rm -f check.full @@ -231,10 +232,10 @@ FULL_HOST_DETAILS=`_full_platform_details` #FULL_MOUNT_OPTIONS=`_scratch_mount_options` cat </tmp/check.sts + echo "$seq" > "${TEST_DIR}"/check.sts start=`_wallclock` $timestamp && echo -n " ["`date "+%T"`"]" @@ -330,6 +331,11 @@ do fi reference="$source_iotests/$seq.out" + reference_machine="$source_iotests/$seq.$QEMU_DEFAULT_MACHINE.out" + if [ -f "$reference_machine" ]; then + reference="$reference_machine" + fi + if [ "$CACHEMODE" = "none" ]; then [ -f "$source_iotests/$seq.out.nocache" ] && reference="$source_iotests/$seq.out.nocache" fi diff --git a/qemu/tests/qemu-iotests/common b/qemu/tests/qemu-iotests/common index 1030aaf25..49e193112 100644 --- a/qemu/tests/qemu-iotests/common +++ b/qemu/tests/qemu-iotests/common @@ -41,7 +41,6 @@ sortme=false expunge=true have_test_arg=false randomize=false -valgrind=false cachemode=false rm -f $tmp.list $tmp.tmp $tmp.sed @@ -52,6 +51,8 @@ export IMGOPTS="" export CACHEMODE="writeback" export QEMU_IO_OPTIONS="" export CACHEMODE_IS_DEFAULT=true +export QEMU_OPTIONS="-nodefaults" +export VALGRIND_QEMU= for r do @@ -154,6 +155,7 @@ check options -ssh test ssh -nfs test nfs -archipelago test archipelago + -luks test luks -xdiff graphical mode diff -nocache use O_DIRECT on backing file -misalign misalign memory allocations @@ -277,7 +279,7 @@ testlist options ;; -valgrind) - valgrind=true + VALGRIND_QEMU='y' xpand=false ;; @@ -435,8 +437,3 @@ fi if [ "$IMGPROTO" = "nbd" ] ; then [ "$QEMU_NBD" = "" ] && _fatal "qemu-nbd not found" fi - -if $valgrind; then - export REAL_QEMU_IO="$QEMU_IO_PROG" - export QEMU_IO_PROG=valgrind_qemu_io -fi diff --git a/qemu/tests/qemu-iotests/common.config b/qemu/tests/qemu-iotests/common.config index a1973ad9d..f824651ba 100644 --- a/qemu/tests/qemu-iotests/common.config +++ b/qemu/tests/qemu-iotests/common.config @@ -44,6 +44,8 @@ export HOST_OPTIONS=${HOST_OPTIONS:=local.config} export CHECK_OPTIONS=${CHECK_OPTIONS:="-g auto"} export PWD=`pwd` +export _QEMU_HANDLE=0 + # $1 = prog to look for, $2* = default pathnames if not found in $PATH set_prog_path() { @@ -103,10 +105,63 @@ if [ -z "$QEMU_NBD_PROG" ]; then export QEMU_NBD_PROG="`set_prog_path qemu-nbd`" fi -export QEMU=$QEMU_PROG -export QEMU_IMG=$QEMU_IMG_PROG -export QEMU_IO="$QEMU_IO_PROG $QEMU_IO_OPTIONS" -export QEMU_NBD=$QEMU_NBD_PROG +_qemu_wrapper() +{ + ( + if [ -n "${QEMU_NEED_PID}" ]; then + echo $BASHPID > "${TEST_DIR}/qemu-${_QEMU_HANDLE}.pid" + fi + exec "$QEMU_PROG" $QEMU_OPTIONS "$@" + ) +} + +_qemu_img_wrapper() +{ + (exec "$QEMU_IMG_PROG" $QEMU_IMG_OPTIONS "$@") +} + +_qemu_io_wrapper() +{ + local VALGRIND_LOGFILE="${TEST_DIR}"/$$.valgrind + local RETVAL + ( + if [ "${VALGRIND_QEMU}" == "y" ]; then + exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" + else + exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" + fi + ) + RETVAL=$? + if [ "${VALGRIND_QEMU}" == "y" ]; then + if [ $RETVAL == 99 ]; then + cat "${VALGRIND_LOGFILE}" + fi + rm -f "${VALGRIND_LOGFILE}" + fi + (exit $RETVAL) +} + +_qemu_nbd_wrapper() +{ + ( + echo $BASHPID > "${TEST_DIR}/qemu-nbd.pid" + exec "$QEMU_NBD_PROG" $QEMU_NBD_OPTIONS "$@" + ) +} + +export QEMU=_qemu_wrapper +export QEMU_IMG=_qemu_img_wrapper +export QEMU_IO=_qemu_io_wrapper +export QEMU_NBD=_qemu_nbd_wrapper + +default_machine=$($QEMU -machine help | sed -n '/(default)/ s/ .*//p') +default_alias_machine=$($QEMU -machine help | \ + sed -n "/(alias of $default_machine)/ { s/ .*//p; q; }") +if [[ "$default_alias_machine" ]]; then + default_machine="$default_alias_machine" +fi + +export QEMU_DEFAULT_MACHINE="$default_machine" [ -f /etc/qemu-iotest.config ] && . /etc/qemu-iotest.config diff --git a/qemu/tests/qemu-iotests/common.filter b/qemu/tests/qemu-iotests/common.filter index 012a8122d..8a6e1b57c 100644 --- a/qemu/tests/qemu-iotests/common.filter +++ b/qemu/tests/qemu-iotests/common.filter @@ -19,107 +19,6 @@ # standard filters # -# Checks that given_value is in range of correct_value +/- tolerance. -# Tolerance can be an absolute value or a percentage of the correct value -# (see examples with tolerances below). -# Outputs suitable message to stdout if it's not in range. -# -# A verbose option, -v, may be used as the LAST argument -# -# e.g. -# foo: 0.0298 = 0.03 +/- 5% -# _within_tolerance "foo" 0.0298 0.03 5% -# -# foo: 0.0298 = 0.03 +/- 0.01 -# _within_tolerance "foo" 0.0298 0.03 0.01 -# -# foo: 0.0298 = 0.03 -0.01 +0.002 -# _within_tolerance "foo" 0.0298 0.03 0.01 0.002 -# -# foo: verbose output of 0.0298 = 0.03 +/- 5% -# _within_tolerance "foo" 0.0298 0.03 5% -v -_within_tolerance() -{ - _name=$1 - _given_val=$2 - _correct_val=$3 - _mintol=$4 - _maxtol=$_mintol - _verbose=0 - _debug=false - - # maxtol arg is optional - # verbose arg is optional - if [ $# -ge 5 ] - then - if [ "$5" = "-v" ] - then - _verbose=1 - else - _maxtol=$5 - fi - fi - if [ $# -ge 6 ] - then - [ "$6" = "-v" ] && _verbose=1 - fi - - # find min with or without % - _mintolerance=`echo $_mintol | sed -e 's/%//'` - if [ $_mintol = $_mintolerance ] - then - _min=`echo "scale=5; $_correct_val-$_mintolerance" | bc` - else - _min=`echo "scale=5; $_correct_val-$_mintolerance*0.01*$_correct_val" | bc` - fi - - # find max with or without % - _maxtolerance=`echo $_maxtol | sed -e 's/%//'` - if [ $_maxtol = $_maxtolerance ] - then - _max=`echo "scale=5; $_correct_val+$_maxtolerance" | bc` - else - _max=`echo "scale=5; $_correct_val+$_maxtolerance*0.01*$_correct_val" | bc` - fi - - $_debug && echo "min = $_min" - $_debug && echo "max = $_max" - - cat <$tmp.bc.1 -scale=5; -if ($_min <= $_given_val) 1; -if ($_min > $_given_val) 0; -EOF - - cat <$tmp.bc.2 -scale=5; -if ($_given_val <= $_max) 1; -if ($_given_val > $_max) 0; -EOF - - _above_min=`bc <$tmp.bc.1` - _below_max=`bc <$tmp.bc.2` - - rm -f $tmp.bc.[12] - - _in_range=`expr $_above_min \& $_below_max` - - # fix up min, max precision for output - # can vary for 5.3, 6.2 - _min=`echo $_min | sed -e 's/0*$//'` # get rid of trailling zeroes - _max=`echo $_max | sed -e 's/0*$//'` # get rid of trailling zeroes - - if [ $_in_range -eq 1 ] - then - [ $_verbose -eq 1 ] && echo $_name is in range - return 0 - else - [ $_verbose -eq 1 ] && echo $_name has value of $_given_val - [ $_verbose -eq 1 ] && echo $_name is NOT in range $_min .. $_max - return 1 - fi -} - # ctime(3) dates # _filter_date() @@ -128,6 +27,11 @@ _filter_date() -e 's/[A-Z][a-z][a-z] [A-z][a-z][a-z] *[0-9][0-9]* [0-9][0-9]:[0-9][0-9]:[0-9][0-9] [0-9][0-9][0-9][0-9]$/DATE/' } +_filter_generated_node_ids() +{ + sed -re 's/\#block[0-9]{3,}/NODE_NAME/' +} + # replace occurrences of the actual TEST_DIR value with TEST_DIR _filter_testdir() { @@ -182,7 +86,7 @@ _filter_img_create() -e "s# encryption=off##g" \ -e "s# cluster_size=[0-9]\\+##g" \ -e "s# table_size=[0-9]\\+##g" \ - -e "s# compat='[^']*'##g" \ + -e "s# compat=[^ ]*##g" \ -e "s# compat6=\\(on\\|off\\)##g" \ -e "s# static=\\(on\\|off\\)##g" \ -e "s# zeroed_grain=\\(on\\|off\\)##g" \ @@ -225,5 +129,18 @@ _filter_qemu_img_map() -e 's/Mapped to *//' | _filter_testdir | _filter_imgfmt } +_filter_nbd() +{ + # nbd.c error messages contain function names and line numbers that are + # prone to change. Message ordering depends on timing between send and + # receive callbacks sometimes, making them unreliable. + # + # Filter out the TCP port number since this changes between runs. + sed -e '/nbd\/.*\.c:/d' \ + -e 's#nbd:\(//\)\?127\.0\.0\.1:[0-9]*#nbd:\1127.0.0.1:PORT#g' \ + -e "s#?socket=$TEST_DIR#?socket=TEST_DIR#g" \ + -e 's#\(exportname=foo\|PORT\): Failed to .*$#\1#' +} + # make sure this script returns success true diff --git a/qemu/tests/qemu-iotests/common.qemu b/qemu/tests/qemu-iotests/common.qemu index 4e1996c3e..2548a8700 100644 --- a/qemu/tests/qemu-iotests/common.qemu +++ b/qemu/tests/qemu-iotests/common.qemu @@ -30,8 +30,6 @@ QEMU_COMM_TIMEOUT=10 QEMU_FIFO_IN="${TEST_DIR}/qmp-in-$$" QEMU_FIFO_OUT="${TEST_DIR}/qmp-out-$$" -QEMU_PID= -_QEMU_HANDLE=0 QEMU_HANDLE=0 # If bash version is >= 4.1, these will be overwritten and dynamic @@ -131,6 +129,8 @@ function _send_qemu_cmd() # $qemu_comm_method: set this variable to 'monitor' (case insensitive) # to use the QEMU HMP monitor for communication. # Otherwise, the default of QMP is used. +# $keep_stderr: Set this variable to 'y' to keep QEMU's stderr output on stderr. +# If this variable is empty, stderr will be redirected to stdout. # Returns: # $QEMU_HANDLE: set to a handle value to communicate with this QEMU instance. # @@ -153,11 +153,20 @@ function _launch_qemu() mkfifo "${fifo_out}" mkfifo "${fifo_in}" - "${QEMU}" -nographic -serial none ${comm} -machine accel=qtest "${@}" \ + if [ -z "$keep_stderr" ]; then + QEMU_NEED_PID='y'\ + ${QEMU} -nographic -serial none ${comm} -machine accel=qtest "${@}" \ >"${fifo_out}" \ 2>&1 \ <"${fifo_in}" & - QEMU_PID[${_QEMU_HANDLE}]=$! + elif [ "$keep_stderr" = "y" ]; then + QEMU_NEED_PID='y'\ + ${QEMU} -nographic -serial none ${comm} -machine accel=qtest "${@}" \ + >"${fifo_out}" \ + <"${fifo_in}" & + else + exit 1 + fi if [[ "${BASH_VERSINFO[0]}" -ge "5" || ("${BASH_VERSINFO[0]}" -ge "4" && "${BASH_VERSINFO[1]}" -ge "1") ]] @@ -196,10 +205,18 @@ function _cleanup_qemu() # QEMU_PID[], QEMU_IN[], QEMU_OUT[] all use same indices for i in "${!QEMU_OUT[@]}" do - if [ -z "${wait}" ]; then - kill -KILL ${QEMU_PID[$i]} 2>/dev/null + local QEMU_PID + if [ -f "${TEST_DIR}/qemu-${i}.pid" ]; then + read QEMU_PID < "${TEST_DIR}/qemu-${i}.pid" + rm -f "${TEST_DIR}/qemu-${i}.pid" + if [ -z "${wait}" ] && [ -n "${QEMU_PID}" ]; then + kill -KILL ${QEMU_PID} 2>/dev/null + fi + if [ -n "${QEMU_PID}" ]; then + wait ${QEMU_PID} 2>/dev/null # silent kill + fi fi - wait ${QEMU_PID[$i]} 2>/dev/null # silent kill + if [ -n "${wait}" ]; then cat <&${QEMU_OUT[$i]} | _filter_testdir | _filter_qemu \ | _filter_qemu_io | _filter_qmp diff --git a/qemu/tests/qemu-iotests/common.rc b/qemu/tests/qemu-iotests/common.rc index 22d351404..5249ec592 100644 --- a/qemu/tests/qemu-iotests/common.rc +++ b/qemu/tests/qemu-iotests/common.rc @@ -70,16 +70,6 @@ else TEST_IMG=$IMGPROTO:$TEST_DIR/t.$IMGFMT fi -function valgrind_qemu_io() -{ - valgrind --log-file=/tmp/$$.valgrind --error-exitcode=99 $REAL_QEMU_IO "$@" - if [ $? != 0 ]; then - cat /tmp/$$.valgrind - fi - rm -f /tmp/$$.valgrind -} - - _optstr_add() { if [ -n "$1" ]; then @@ -154,7 +144,6 @@ _make_test_img() # Start an NBD server on the image file, which is what we'll be talking to if [ $IMGPROTO = "nbd" ]; then eval "$QEMU_NBD -v -t -b 127.0.0.1 -p 10810 -f $IMGFMT $TEST_IMG_FILE &" - QEMU_NBD_PID=$! sleep 1 # FIXME: qemu-nbd needs to be listening before we continue fi } @@ -175,8 +164,11 @@ _cleanup_test_img() case "$IMGPROTO" in nbd) - if [ -n "$QEMU_NBD_PID" ]; then - kill $QEMU_NBD_PID + if [ -f "${TEST_DIR}/qemu-nbd.pid" ]; then + local QEMU_NBD_PID + read QEMU_NBD_PID < "${TEST_DIR}/qemu-nbd.pid" + kill ${QEMU_NBD_PID} + rm -f "${TEST_DIR}/qemu-nbd.pid" fi rm -f "$TEST_IMG_FILE" ;; @@ -295,52 +287,6 @@ _need_to_be_root() fi } - -# Do a command, log it to $seq.full, optionally test return status -# and die if command fails. If called with one argument _do executes the -# command, logs it, and returns its exit status. With two arguments _do -# first prints the message passed in the first argument, and then "done" -# or "fail" depending on the return status of the command passed in the -# second argument. If the command fails and the variable _do_die_on_error -# is set to "always" or the two argument form is used and _do_die_on_error -# is set to "message_only" _do will print an error message to -# $seq.out and exit. - -_do() -{ - if [ $# -eq 1 ]; then - _cmd=$1 - elif [ $# -eq 2 ]; then - _note=$1 - _cmd=$2 - echo -n "$_note... " - else - echo "Usage: _do [note] cmd" 1>&2 - status=1; exit - fi - - (eval "echo '---' \"$_cmd\"") >>"$OUTPUT_DIR/$seq.full" - (eval "$_cmd") >$tmp._out 2>&1; ret=$? - cat $tmp._out >>"$OUTPUT_DIR/$seq.full" - if [ $# -eq 2 ]; then - if [ $ret -eq 0 ]; then - echo "done" - else - echo "fail" - fi - fi - if [ $ret -ne 0 ] \ - && [ "$_do_die_on_error" = "always" \ - -o \( $# -eq 2 -a "$_do_die_on_error" = "message_only" \) ] - then - [ $# -ne 2 ] && echo - eval "echo \"$_cmd\" failed \(returned $ret\): see $seq.full" - status=1; exit - fi - - return $ret -} - # bail out, setting up .notrun file # _notrun() @@ -439,7 +385,17 @@ _unsupported_imgopts() # _require_command() { - eval c=\$$1 + if [ "$1" = "QEMU" ]; then + c=$QEMU_PROG + elif [ "$1" = "QEMU_IMG" ]; then + c=$QEMU_IMG_PROG + elif [ "$1" = "QEMU_IO" ]; then + c=$QEMU_IO_PROG + elif [ "$1" = "QEMU_NBD" ]; then + c=$QEMU_NBD_PROG + else + eval c=\$$1 + fi [ -x "$c" ] || _notrun "$1 utility required, skipped this test" } diff --git a/qemu/tests/qemu-iotests/group b/qemu/tests/qemu-iotests/group index c430b6c23..822953b6f 100644 --- a/qemu/tests/qemu-iotests/group +++ b/qemu/tests/qemu-iotests/group @@ -102,6 +102,7 @@ 093 auto 094 rw auto quick 095 rw auto quick +096 rw auto quick 097 rw auto backing 098 rw auto backing quick 099 rw auto quick @@ -121,6 +122,8 @@ 114 rw auto quick 115 rw auto 116 rw auto quick +117 rw auto +118 rw auto 119 rw auto quick 120 rw auto quick 121 rw auto @@ -132,5 +135,21 @@ 130 rw auto quick 131 rw auto quick 132 rw auto quick +133 auto quick 134 rw auto quick 135 rw auto +136 rw auto +137 rw auto +138 rw auto quick +139 rw auto quick +140 rw auto quick +141 rw auto quick +142 auto +143 auto quick +144 rw auto quick +145 auto quick +146 auto quick +148 rw auto quick +149 rw auto sudo +150 rw auto quick +152 rw auto quick diff --git a/qemu/tests/qemu-iotests/iotests.py b/qemu/tests/qemu-iotests/iotests.py index 8615b1075..56f988ab3 100644 --- a/qemu/tests/qemu-iotests/iotests.py +++ b/qemu/tests/qemu-iotests/iotests.py @@ -16,6 +16,7 @@ # along with this program. If not, see . # +import errno import os import re import subprocess @@ -27,41 +28,66 @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts', ' import qmp import qtest import struct +import json -__all__ = ['imgfmt', 'imgproto', 'test_dir' 'qemu_img', 'qemu_io', - 'VM', 'QMPTestCase', 'notrun', 'main'] -# This will not work if arguments or path contain spaces but is necessary if we +# This will not work if arguments contain spaces but is necessary if we # want to support the override options that ./check supports. -qemu_img_args = os.environ.get('QEMU_IMG', 'qemu-img').strip().split(' ') -qemu_io_args = os.environ.get('QEMU_IO', 'qemu-io').strip().split(' ') -qemu_args = os.environ.get('QEMU', 'qemu').strip().split(' ') +qemu_img_args = [os.environ.get('QEMU_IMG_PROG', 'qemu-img')] +if os.environ.get('QEMU_IMG_OPTIONS'): + qemu_img_args += os.environ['QEMU_IMG_OPTIONS'].strip().split(' ') + +qemu_io_args = [os.environ.get('QEMU_IO_PROG', 'qemu-io')] +if os.environ.get('QEMU_IO_OPTIONS'): + qemu_io_args += os.environ['QEMU_IO_OPTIONS'].strip().split(' ') + +qemu_args = [os.environ.get('QEMU_PROG', 'qemu')] +if os.environ.get('QEMU_OPTIONS'): + qemu_args += os.environ['QEMU_OPTIONS'].strip().split(' ') imgfmt = os.environ.get('IMGFMT', 'raw') imgproto = os.environ.get('IMGPROTO', 'file') test_dir = os.environ.get('TEST_DIR', '/var/tmp') output_dir = os.environ.get('OUTPUT_DIR', '.') cachemode = os.environ.get('CACHEMODE') +qemu_default_machine = os.environ.get('QEMU_DEFAULT_MACHINE') socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper') def qemu_img(*args): '''Run qemu-img and return the exit code''' devnull = open('/dev/null', 'r+') - return subprocess.call(qemu_img_args + list(args), stdin=devnull, stdout=devnull) + exitcode = subprocess.call(qemu_img_args + list(args), stdin=devnull, stdout=devnull) + if exitcode < 0: + sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args)))) + return exitcode def qemu_img_verbose(*args): '''Run qemu-img without suppressing its output and return the exit code''' - return subprocess.call(qemu_img_args + list(args)) + exitcode = subprocess.call(qemu_img_args + list(args)) + if exitcode < 0: + sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args)))) + return exitcode def qemu_img_pipe(*args): '''Run qemu-img and return its output''' - return subprocess.Popen(qemu_img_args + list(args), stdout=subprocess.PIPE).communicate()[0] + subp = subprocess.Popen(qemu_img_args + list(args), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + exitcode = subp.wait() + if exitcode < 0: + sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args)))) + return subp.communicate()[0] def qemu_io(*args): '''Run qemu-io and return the stdout data''' args = qemu_io_args + list(args) - return subprocess.Popen(args, stdout=subprocess.PIPE).communicate()[0] + subp = subprocess.Popen(args, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + exitcode = subp.wait() + if exitcode < 0: + sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args))) + return subp.communicate()[0] def compare_images(img1, img2): '''Return True if two image files are identical''' @@ -78,6 +104,33 @@ def create_image(name, size): i = i + 512 file.close() +def image_size(img): + '''Return image's virtual size''' + r = qemu_img_pipe('info', '--output=json', '-f', imgfmt, img) + return json.loads(r)['virtual-size'] + +test_dir_re = re.compile(r"%s" % test_dir) +def filter_test_dir(msg): + return test_dir_re.sub("TEST_DIR", msg) + +win32_re = re.compile(r"\r") +def filter_win32(msg): + return win32_re.sub("", msg) + +qemu_io_re = re.compile(r"[0-9]* ops; [0-9\/:. sec]* \([0-9\/.inf]* [EPTGMKiBbytes]*\/sec and [0-9\/.inf]* ops\/sec\)") +def filter_qemu_io(msg): + msg = filter_win32(msg) + return qemu_io_re.sub("X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)", msg) + +chown_re = re.compile(r"chown [0-9]+:[0-9]+") +def filter_chown(msg): + return chown_re.sub("chown UID:GID", msg) + +def log(msg, filters=[]): + for flt in filters: + msg = flt(msg) + print msg + # Test if 'match' is a recursive subset of 'event' def event_match(event, match=None): if match is None: @@ -117,13 +170,21 @@ class VM(object): self._args.append('-monitor') self._args.append(args) - def add_drive(self, path, opts=''): + def add_drive_raw(self, opts): + self._args.append('-drive') + self._args.append(opts) + return self + + def add_drive(self, path, opts='', interface='virtio'): '''Add a virtio-blk drive to the VM''' - options = ['if=virtio', - 'format=%s' % imgfmt, - 'cache=%s' % cachemode, - 'file=%s' % path, + options = ['if=%s' % interface, 'id=drive%d' % self._num_drives] + + if path is not None: + options.append('file=%s' % path) + options.append('format=%s' % imgfmt) + options.append('cache=%s' % cachemode) + if opts: options.append(opts) @@ -189,14 +250,17 @@ class VM(object): self._qmp.accept() self._qtest.accept() except: - os.remove(self._monitor_path) + _remove_if_exists(self._monitor_path) + _remove_if_exists(self._qtest_path) raise def shutdown(self): '''Terminate the VM and clean up''' if not self._popen is None: self._qmp.cmd('quit') - self._popen.wait() + exitcode = self._popen.wait() + if exitcode < 0: + sys.stderr.write('qemu received signal %i: %s\n' % (-exitcode, ' '.join(self._args))) os.remove(self._monitor_path) os.remove(self._qtest_path) os.remove(self._qemu_log_path) @@ -290,6 +354,20 @@ class QMPTestCase(unittest.TestCase): result = self.vm.qmp('query-block-jobs') self.assert_qmp(result, 'return', []) + def assert_has_block_node(self, node_name=None, file_name=None): + """Issue a query-named-block-nodes and assert node_name and/or + file_name is present in the result""" + def check_equal_or_none(a, b): + return a == None or b == None or a == b + assert node_name or file_name + result = self.vm.qmp('query-named-block-nodes') + for x in result["return"]: + if check_equal_or_none(x.get("node-name"), node_name) and \ + check_equal_or_none(x.get("file"), file_name): + return + self.assertTrue(False, "Cannot find %s %s in result:\n%s" % \ + (node_name, file_name, result)) + def cancel_and_wait(self, drive='drive0', force=False, resume=False): '''Cancel a block job and wait for it to finish, returning the event''' result = self.vm.qmp('block-job-cancel', device=drive, force=force) @@ -349,6 +427,15 @@ class QMPTestCase(unittest.TestCase): event = self.wait_until_completed(drive=drive) self.assert_qmp(event, 'data/type', 'mirror') +def _remove_if_exists(path): + '''Remove file object at path if it exists''' + try: + os.remove(path) + except OSError as exception: + if exception.errno == errno.ENOENT: + return + raise + def notrun(reason): '''Skip this test suite''' # Each test in qemu-iotests has a number ("seq") @@ -358,17 +445,27 @@ def notrun(reason): print '%s not run: %s' % (seq, reason) sys.exit(0) -def main(supported_fmts=[], supported_oses=['linux']): - '''Run tests''' - - debug = '-d' in sys.argv - verbosity = 1 +def verify_image_format(supported_fmts=[]): if supported_fmts and (imgfmt not in supported_fmts): notrun('not suitable for this image format: %s' % imgfmt) +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_quorum(): + '''Skip test suite if quorum support is not available''' + if 'quorum' not in qemu_img_pipe('--help'): + notrun('quorum support missing') + +def main(supported_fmts=[], supported_oses=['linux']): + '''Run tests''' + + debug = '-d' in sys.argv + verbosity = 1 + verify_image_format(supported_fmts) + verify_platform(supported_oses) + # We need to filter out the time taken from the output so that qemu-iotest # can reliably diff the results against master output. import StringIO diff --git a/qemu/tests/qemu-iotests/qed.py b/qemu/tests/qemu-iotests/qed.py index 52ff84559..748068d7f 100755 --- a/qemu/tests/qemu-iotests/qed.py +++ b/qemu/tests/qemu-iotests/qed.py @@ -227,7 +227,7 @@ def main(): qed = QED(open(filename, 'r+b')) try: globals()[cmd](qed, *sys.argv[3:]) - except TypeError, e: + except TypeError as e: sys.stderr.write(globals()[cmd].__doc__ + '\n') sys.exit(1) diff --git a/qemu/tests/qemu-iotests/sample_images/d2v-zerofilled.vhd.bz2 b/qemu/tests/qemu-iotests/sample_images/d2v-zerofilled.vhd.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..f12cb9203aa3271af1d610d5313a3aa3ffad1a6e GIT binary patch literal 1021 zcmVR0Kl057?_45N2K&kFhwSy{iam`qxDZv z00000Gynhq0000000000HlQMuspUUPey9PViI4zfG|8rhKmY&%qednmXblEH&>Ca_ zWYB5qc4VLNl7#$VQwjl0Xaxii5h#j6sEUm1_%YBbC@P|3{PF>V2$@`#TQ8H$W@$Lg zunSNdj{OKq5`>`%N>Y?123z>=l}RDjq{>_xT55WUad`6}nE`}o-m$~F`x_^ZphAfn zO|8An)TxuGQmIYdlwK7|@o=XTJk-QQNXhmVo7%?O?8@<|vm>5o(%$o0Y^psI;F6c2CaKv)BP zpa2GWE&u_5&=y+8ic%G?Z+DQFvhR)xYNaV(A1s>;FuEJ-BVQ(@hddCK)7>No9svWtM-S007|t0002M+$MmK002S&004>ZC;2vf zS?+VWYEqSP%#rBgy6^ew%^6lpWoug2R<+8rT;7C8(p!1yl4xYQ>#oR-rAC0IDNZ=! zDN0h5r6@uWgdq*Zz53)tL`CKd(?^+^nVIm2h=`2|NoAH&B(Yi-HR$=LqW7aHq> r>VAzO3jW3zM|uPx5)cG{1Xu(02t5UF)d5g$5901frwS4U1zi6?E&-p( literal 0 HcmV?d00001 diff --git a/qemu/tests/qemu-iotests/sample_images/hyperv2012r2-dynamic.vhd.bz2 b/qemu/tests/qemu-iotests/sample_images/hyperv2012r2-dynamic.vhd.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..bfeccf7b9f2596f119bb35d7e650a29f8e6f17ef GIT binary patch literal 214 zcmV;{04e`MT4*^jL0KkKS<7>@aR2~j|Ns3EPyxUK5D)+a0HCTX+~fcS06+jB01!Yx z0Eqx02&u3Dw$>7=8feLprXhl8!7=I+01-+c00006fB?`)si-s!00E((0ieW+9LW!< z$bjJjeWx)DFnA{B7q@4*a;xX z24}+|;{k09^toeVF+M+y8lDaaN{P?GVI@TYSp=OSB$8;1RzUy&6`?NT5C8<(0ssS^ Oh1`)&6eJCs&V_&uMoJF= literal 0 HcmV?d00001 diff --git a/qemu/tests/qemu-iotests/socket_scm_helper.c b/qemu/tests/qemu-iotests/socket_scm_helper.c index 81959835e..80cadf43b 100644 --- a/qemu/tests/qemu-iotests/socket_scm_helper.c +++ b/qemu/tests/qemu-iotests/socket_scm_helper.c @@ -10,15 +10,9 @@ * See the COPYING.LIB file in the top-level directory. */ -#include -#include +#include "qemu/osdep.h" #include #include -#include -#include -#include -#include -#include /* #define SOCKET_SCM_DEBUG */ diff --git a/qemu/tests/qom-test.c b/qemu/tests/qom-test.c index fde04e7a1..bd5cdde26 100644 --- a/qemu/tests/qom-test.c +++ b/qemu/tests/qom-test.c @@ -7,12 +7,12 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "qemu-common.h" +#include "qemu/cutils.h" #include "libqtest.h" -#include "qemu/osdep.h" #include "qapi/qmp/types.h" static const char *blacklist_x86[] = { @@ -47,7 +47,7 @@ static bool is_blacklisted(const char *arch, const char *mach) static void test_properties(const char *path, bool recurse) { char *child_path; - QDict *response, *tuple; + QDict *response, *tuple, *tmp; QList *list; QListEntry *entry; @@ -57,6 +57,7 @@ static void test_properties(const char *path, bool recurse) g_assert(response); if (!recurse) { + QDECREF(response); return; } @@ -75,14 +76,16 @@ static void test_properties(const char *path, bool recurse) } else { const char *prop = qdict_get_str(tuple, "name"); g_test_message("Testing property %s.%s", path, prop); - response = qmp("{ 'execute': 'qom-get'," - " 'arguments': { 'path': %s," - " 'property': %s } }", - path, prop); + tmp = qmp("{ 'execute': 'qom-get'," + " 'arguments': { 'path': %s," + " 'property': %s } }", + path, prop); /* qom-get may fail but should not, e.g., segfault. */ - g_assert(response); + g_assert(tmp); + QDECREF(tmp); } } + QDECREF(response); } static void test_machine(gconstpointer data) @@ -98,9 +101,11 @@ static void test_machine(gconstpointer data) response = qmp("{ 'execute': 'quit' }"); g_assert(qdict_haskey(response, "return")); + QDECREF(response); qtest_end(); g_free(args); + g_free((void *)machine); } static void add_machine_test_cases(void) @@ -129,10 +134,12 @@ static void add_machine_test_cases(void) mname = qstring_get_str(qstr); if (!is_blacklisted(arch, mname)) { path = g_strdup_printf("qom/%s", mname); - qtest_add_data_func(path, mname, test_machine); + qtest_add_data_func(path, g_strdup(mname), test_machine); } } + qtest_end(); + QDECREF(response); } int main(int argc, char **argv) diff --git a/qemu/tests/rcutorture.c b/qemu/tests/rcutorture.c index d6b304d00..244f0f28b 100644 --- a/qemu/tests/rcutorture.c +++ b/qemu/tests/rcutorture.c @@ -60,13 +60,10 @@ * Test variables. */ +#include "qemu/osdep.h" #include -#include -#include -#include #include "qemu/atomic.h" #include "qemu/rcu.h" -#include "qemu/compiler.h" #include "qemu/thread.h" long long n_reads = 0LL; diff --git a/qemu/tests/rtc-test.c b/qemu/tests/rtc-test.c index 4243624de..fa7029aa8 100644 --- a/qemu/tests/rtc-test.c +++ b/qemu/tests/rtc-test.c @@ -11,11 +11,8 @@ * */ +#include "qemu/osdep.h" #include -#include -#include -#include -#include #include "libqtest.h" #include "hw/timer/mc146818rtc_regs.h" diff --git a/qemu/tests/rtl8139-test.c b/qemu/tests/rtl8139-test.c index e749be38e..54e5aa7d0 100644 --- a/qemu/tests/rtl8139-test.c +++ b/qemu/tests/rtl8139-test.c @@ -7,11 +7,10 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" #include "libqos/pci-pc.h" -#include "qemu/osdep.h" #include "qemu/timer.h" #include "qemu-common.h" @@ -20,7 +19,7 @@ static void nop(void) { } -#define CLK 33000000 +#define CLK 33333333 static QPCIBus *pcibus; static QPCIDevice *dev; diff --git a/qemu/tests/spapr-phb-test.c b/qemu/tests/spapr-phb-test.c index b629de475..f53911d9f 100644 --- a/qemu/tests/spapr-phb-test.c +++ b/qemu/tests/spapr-phb-test.c @@ -7,6 +7,7 @@ * 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 #include "libqtest.h" diff --git a/qemu/tests/tcg/linux-test.c b/qemu/tests/tcg/linux-test.c index 1c6c01318..5070d3144 100644 --- a/qemu/tests/tcg/linux-test.c +++ b/qemu/tests/tcg/linux-test.c @@ -39,6 +39,7 @@ #include #include #include +#include "qemu/cutils.h" #define TESTPATH "/tmp/linux-test.tmp" #define TESTPORT 7654 diff --git a/qemu/tests/tcg/test-i386-fprem.c b/qemu/tests/tcg/test-i386-fprem.c index e91fb1ae9..1a7162320 100644 --- a/qemu/tests/tcg/test-i386-fprem.c +++ b/qemu/tests/tcg/test-i386-fprem.c @@ -22,10 +22,8 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ -#include "qemu/compiler.h" + #include "qemu/osdep.h" -#include -#include /* * Inspired by 's union ieee854_long_double, but with single diff --git a/qemu/tests/tcg/testthread.c b/qemu/tests/tcg/testthread.c index 2679af119..810ba5de6 100644 --- a/qemu/tests/tcg/testthread.c +++ b/qemu/tests/tcg/testthread.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include diff --git a/qemu/tests/tco-test.c b/qemu/tests/tco-test.c index 419f7cf46..ac11175e9 100644 --- a/qemu/tests/tco-test.c +++ b/qemu/tests/tco-test.c @@ -6,10 +6,8 @@ * 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 -#include -#include -#include #include "libqtest.h" #include "libqos/pci.h" diff --git a/qemu/tests/test-aio.c b/qemu/tests/test-aio.c index 217e33772..687dfa062 100644 --- a/qemu/tests/test-aio.c +++ b/qemu/tests/test-aio.c @@ -10,8 +10,10 @@ * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include #include "block/aio.h" +#include "qapi/error.h" #include "qemu/timer.h" #include "qemu/sockets.h" #include "qemu/error-report.h" @@ -118,6 +120,12 @@ static void *test_acquire_thread(void *opaque) return NULL; } +static void set_event_notifier(AioContext *ctx, EventNotifier *notifier, + EventNotifierHandler *handler) +{ + aio_set_event_notifier(ctx, notifier, false, handler); +} + static void dummy_notifier_read(EventNotifier *unused) { g_assert(false); /* should never be invoked */ @@ -131,7 +139,7 @@ static void test_acquire(void) /* Dummy event notifier ensures aio_poll() will block */ event_notifier_init(¬ifier, false); - aio_set_event_notifier(ctx, ¬ifier, dummy_notifier_read); + set_event_notifier(ctx, ¬ifier, dummy_notifier_read); g_assert(!aio_poll(ctx, false)); /* consume aio_notify() */ qemu_mutex_init(&data.start_lock); @@ -149,7 +157,7 @@ static void test_acquire(void) aio_context_release(ctx); qemu_thread_join(&thread); - aio_set_event_notifier(ctx, ¬ifier, NULL); + set_event_notifier(ctx, ¬ifier, NULL); event_notifier_cleanup(¬ifier); g_assert(data.thread_acquired); @@ -308,11 +316,11 @@ static void test_set_event_notifier(void) { EventNotifierTestData data = { .n = 0, .active = 0 }; event_notifier_init(&data.e, false); - aio_set_event_notifier(ctx, &data.e, event_ready_cb); + set_event_notifier(ctx, &data.e, event_ready_cb); g_assert(!aio_poll(ctx, false)); g_assert_cmpint(data.n, ==, 0); - aio_set_event_notifier(ctx, &data.e, NULL); + set_event_notifier(ctx, &data.e, NULL); g_assert(!aio_poll(ctx, false)); g_assert_cmpint(data.n, ==, 0); event_notifier_cleanup(&data.e); @@ -322,7 +330,7 @@ static void test_wait_event_notifier(void) { EventNotifierTestData data = { .n = 0, .active = 1 }; event_notifier_init(&data.e, false); - aio_set_event_notifier(ctx, &data.e, event_ready_cb); + set_event_notifier(ctx, &data.e, event_ready_cb); while (aio_poll(ctx, false)); g_assert_cmpint(data.n, ==, 0); g_assert_cmpint(data.active, ==, 1); @@ -336,7 +344,7 @@ static void test_wait_event_notifier(void) g_assert_cmpint(data.n, ==, 1); g_assert_cmpint(data.active, ==, 0); - aio_set_event_notifier(ctx, &data.e, NULL); + set_event_notifier(ctx, &data.e, NULL); g_assert(!aio_poll(ctx, false)); g_assert_cmpint(data.n, ==, 1); @@ -347,7 +355,7 @@ static void test_flush_event_notifier(void) { EventNotifierTestData data = { .n = 0, .active = 10, .auto_set = true }; event_notifier_init(&data.e, false); - aio_set_event_notifier(ctx, &data.e, event_ready_cb); + set_event_notifier(ctx, &data.e, event_ready_cb); while (aio_poll(ctx, false)); g_assert_cmpint(data.n, ==, 0); g_assert_cmpint(data.active, ==, 10); @@ -363,18 +371,42 @@ static void test_flush_event_notifier(void) g_assert_cmpint(data.active, ==, 0); g_assert(!aio_poll(ctx, false)); - aio_set_event_notifier(ctx, &data.e, NULL); + set_event_notifier(ctx, &data.e, NULL); g_assert(!aio_poll(ctx, false)); event_notifier_cleanup(&data.e); } +static void test_aio_external_client(void) +{ + int i, j; + + for (i = 1; i < 3; i++) { + EventNotifierTestData data = { .n = 0, .active = 10, .auto_set = true }; + event_notifier_init(&data.e, false); + aio_set_event_notifier(ctx, &data.e, true, event_ready_cb); + event_notifier_set(&data.e); + for (j = 0; j < i; j++) { + aio_disable_external(ctx); + } + for (j = 0; j < i; j++) { + assert(!aio_poll(ctx, false)); + assert(event_notifier_test_and_clear(&data.e)); + event_notifier_set(&data.e); + aio_enable_external(ctx); + } + assert(aio_poll(ctx, false)); + set_event_notifier(ctx, &data.e, NULL); + event_notifier_cleanup(&data.e); + } +} + static void test_wait_event_notifier_noflush(void) { EventNotifierTestData data = { .n = 0 }; EventNotifierTestData dummy = { .n = 0, .active = 1 }; event_notifier_init(&data.e, false); - aio_set_event_notifier(ctx, &data.e, event_ready_cb); + set_event_notifier(ctx, &data.e, event_ready_cb); g_assert(!aio_poll(ctx, false)); g_assert_cmpint(data.n, ==, 0); @@ -387,7 +419,7 @@ static void test_wait_event_notifier_noflush(void) /* An active event notifier forces aio_poll to look at EventNotifiers. */ event_notifier_init(&dummy.e, false); - aio_set_event_notifier(ctx, &dummy.e, event_ready_cb); + set_event_notifier(ctx, &dummy.e, event_ready_cb); event_notifier_set(&data.e); g_assert(aio_poll(ctx, false)); @@ -407,10 +439,10 @@ static void test_wait_event_notifier_noflush(void) g_assert_cmpint(dummy.n, ==, 1); g_assert_cmpint(dummy.active, ==, 0); - aio_set_event_notifier(ctx, &dummy.e, NULL); + set_event_notifier(ctx, &dummy.e, NULL); event_notifier_cleanup(&dummy.e); - aio_set_event_notifier(ctx, &data.e, NULL); + set_event_notifier(ctx, &data.e, NULL); g_assert(!aio_poll(ctx, false)); g_assert_cmpint(data.n, ==, 2); @@ -428,7 +460,7 @@ static void test_timer_schedule(void) * an fd to wait on. Fixing this breaks other tests. So create a dummy one. */ event_notifier_init(&e, false); - aio_set_event_notifier(ctx, &e, dummy_io_handler_read); + set_event_notifier(ctx, &e, dummy_io_handler_read); aio_poll(ctx, false); aio_timer_init(ctx, &data.timer, data.clock_type, @@ -467,7 +499,7 @@ static void test_timer_schedule(void) g_assert(!aio_poll(ctx, false)); g_assert_cmpint(data.n, ==, 2); - aio_set_event_notifier(ctx, &e, NULL); + set_event_notifier(ctx, &e, NULL); event_notifier_cleanup(&e); timer_del(&data.timer); @@ -638,11 +670,11 @@ static void test_source_set_event_notifier(void) { EventNotifierTestData data = { .n = 0, .active = 0 }; event_notifier_init(&data.e, false); - aio_set_event_notifier(ctx, &data.e, event_ready_cb); + set_event_notifier(ctx, &data.e, event_ready_cb); while (g_main_context_iteration(NULL, false)); g_assert_cmpint(data.n, ==, 0); - aio_set_event_notifier(ctx, &data.e, NULL); + set_event_notifier(ctx, &data.e, NULL); while (g_main_context_iteration(NULL, false)); g_assert_cmpint(data.n, ==, 0); event_notifier_cleanup(&data.e); @@ -652,7 +684,7 @@ static void test_source_wait_event_notifier(void) { EventNotifierTestData data = { .n = 0, .active = 1 }; event_notifier_init(&data.e, false); - aio_set_event_notifier(ctx, &data.e, event_ready_cb); + set_event_notifier(ctx, &data.e, event_ready_cb); while (g_main_context_iteration(NULL, false)); g_assert_cmpint(data.n, ==, 0); g_assert_cmpint(data.active, ==, 1); @@ -666,7 +698,7 @@ static void test_source_wait_event_notifier(void) g_assert_cmpint(data.n, ==, 1); g_assert_cmpint(data.active, ==, 0); - aio_set_event_notifier(ctx, &data.e, NULL); + set_event_notifier(ctx, &data.e, NULL); while (g_main_context_iteration(NULL, false)); g_assert_cmpint(data.n, ==, 1); @@ -677,7 +709,7 @@ static void test_source_flush_event_notifier(void) { EventNotifierTestData data = { .n = 0, .active = 10, .auto_set = true }; event_notifier_init(&data.e, false); - aio_set_event_notifier(ctx, &data.e, event_ready_cb); + set_event_notifier(ctx, &data.e, event_ready_cb); while (g_main_context_iteration(NULL, false)); g_assert_cmpint(data.n, ==, 0); g_assert_cmpint(data.active, ==, 10); @@ -693,7 +725,7 @@ static void test_source_flush_event_notifier(void) g_assert_cmpint(data.active, ==, 0); g_assert(!g_main_context_iteration(NULL, false)); - aio_set_event_notifier(ctx, &data.e, NULL); + set_event_notifier(ctx, &data.e, NULL); while (g_main_context_iteration(NULL, false)); event_notifier_cleanup(&data.e); } @@ -704,7 +736,7 @@ static void test_source_wait_event_notifier_noflush(void) EventNotifierTestData dummy = { .n = 0, .active = 1 }; event_notifier_init(&data.e, false); - aio_set_event_notifier(ctx, &data.e, event_ready_cb); + set_event_notifier(ctx, &data.e, event_ready_cb); while (g_main_context_iteration(NULL, false)); g_assert_cmpint(data.n, ==, 0); @@ -717,7 +749,7 @@ static void test_source_wait_event_notifier_noflush(void) /* An active event notifier forces aio_poll to look at EventNotifiers. */ event_notifier_init(&dummy.e, false); - aio_set_event_notifier(ctx, &dummy.e, event_ready_cb); + set_event_notifier(ctx, &dummy.e, event_ready_cb); event_notifier_set(&data.e); g_assert(g_main_context_iteration(NULL, false)); @@ -737,10 +769,10 @@ static void test_source_wait_event_notifier_noflush(void) g_assert_cmpint(dummy.n, ==, 1); g_assert_cmpint(dummy.active, ==, 0); - aio_set_event_notifier(ctx, &dummy.e, NULL); + set_event_notifier(ctx, &dummy.e, NULL); event_notifier_cleanup(&dummy.e); - aio_set_event_notifier(ctx, &data.e, NULL); + set_event_notifier(ctx, &data.e, NULL); while (g_main_context_iteration(NULL, false)); g_assert_cmpint(data.n, ==, 2); @@ -759,7 +791,7 @@ static void test_source_timer_schedule(void) * an fd to wait on. Fixing this breaks other tests. So create a dummy one. */ event_notifier_init(&e, false); - aio_set_event_notifier(ctx, &e, dummy_io_handler_read); + set_event_notifier(ctx, &e, dummy_io_handler_read); do {} while (g_main_context_iteration(NULL, false)); aio_timer_init(ctx, &data.timer, data.clock_type, @@ -784,7 +816,7 @@ static void test_source_timer_schedule(void) g_assert_cmpint(data.n, ==, 2); g_assert(qemu_clock_get_ns(data.clock_type) > expiry); - aio_set_event_notifier(ctx, &e, NULL); + set_event_notifier(ctx, &e, NULL); event_notifier_cleanup(&e); timer_del(&data.timer); @@ -802,9 +834,7 @@ int main(int argc, char **argv) ctx = aio_context_new(&local_error); if (!ctx) { - error_report("Failed to create AIO Context: '%s'", - error_get_pretty(local_error)); - error_free(local_error); + error_reportf_err(local_error, "Failed to create AIO Context: "); exit(1); } src = aio_get_g_source(ctx); @@ -826,6 +856,7 @@ int main(int argc, char **argv) g_test_add_func("/aio/event/wait", test_wait_event_notifier); g_test_add_func("/aio/event/wait/no-flush-cb", test_wait_event_notifier_noflush); g_test_add_func("/aio/event/flush", test_flush_event_notifier); + g_test_add_func("/aio/external-client", test_aio_external_client); g_test_add_func("/aio/timer/schedule", test_timer_schedule); g_test_add_func("/aio-gsource/flush", test_source_flush); diff --git a/qemu/tests/test-base64.c b/qemu/tests/test-base64.c new file mode 100644 index 000000000..922e839dd --- /dev/null +++ b/qemu/tests/test-base64.c @@ -0,0 +1,110 @@ +/* + * QEMU base64 helper test + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include + +#include "qapi/error.h" +#include "qemu/base64.h" + +static void test_base64_good(void) +{ + const char input[] = + "QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW\n" + "lzc2VkIHRoZSBzY29ycGlvbi4="; + const char expect[] = "Because we focused on the snake, " + "we missed the scorpion."; + + size_t len; + uint8_t *actual = qbase64_decode(input, + -1, + &len, + &error_abort); + + g_assert(actual != NULL); + g_assert_cmpint(len, ==, strlen(expect)); + g_assert_cmpstr((char *)actual, ==, expect); + g_free(actual); +} + + +static void test_base64_bad(const char *input, + size_t input_len) +{ + size_t len; + Error *err = NULL; + uint8_t *actual = qbase64_decode(input, + input_len, + &len, + &err); + + g_assert(err != NULL); + g_assert(actual == NULL); + g_assert_cmpint(len, ==, 0); + error_free(err); +} + + +static void test_base64_embedded_nul(void) +{ + /* We put a NUL character in the middle of the base64 + * text which is invalid data, given the expected length */ + const char input[] = + "QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW\0" + "lzc2VkIHRoZSBzY29ycGlvbi4="; + + test_base64_bad(input, G_N_ELEMENTS(input) - 1); +} + + +static void test_base64_not_nul_terminated(void) +{ + const char input[] = + "QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW\n" + "lzc2VkIHRoZSBzY29ycGlvbi4="; + + /* Using '-2' to make us drop the trailing NUL, thus + * creating an invalid base64 sequence for decoding */ + test_base64_bad(input, G_N_ELEMENTS(input) - 2); +} + + +static void test_base64_invalid_chars(void) +{ + /* We put a single quote character in the middle + * of the base64 text which is invalid data */ + const char input[] = + "QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW'" + "lzc2VkIHRoZSBzY29ycGlvbi4="; + + test_base64_bad(input, strlen(input)); +} + + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/util/base64/good", test_base64_good); + g_test_add_func("/util/base64/embedded-nul", test_base64_embedded_nul); + g_test_add_func("/util/base64/not-nul-terminated", + test_base64_not_nul_terminated); + g_test_add_func("/util/base64/invalid-chars", test_base64_invalid_chars); + return g_test_run(); +} diff --git a/qemu/tests/test-bitops.c b/qemu/tests/test-bitops.c index 47b5d3ed9..505095060 100644 --- a/qemu/tests/test-bitops.c +++ b/qemu/tests/test-bitops.c @@ -6,9 +6,8 @@ * */ -#include -#include #include "qemu/osdep.h" +#include #include "qemu/bitops.h" typedef struct { diff --git a/qemu/tests/test-blockjob-txn.c b/qemu/tests/test-blockjob-txn.c new file mode 100644 index 000000000..55fad9507 --- /dev/null +++ b/qemu/tests/test-blockjob-txn.c @@ -0,0 +1,251 @@ +/* + * Blockjob transactions tests + * + * Copyright Red Hat, Inc. 2015 + * + * Authors: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include +#include "qapi/error.h" +#include "qemu/main-loop.h" +#include "block/blockjob.h" + +typedef struct { + BlockJob common; + unsigned int iterations; + bool use_timer; + int rc; + int *result; +} TestBlockJob; + +static const BlockJobDriver test_block_job_driver = { + .instance_size = sizeof(TestBlockJob), +}; + +static void test_block_job_complete(BlockJob *job, void *opaque) +{ + BlockDriverState *bs = job->bs; + int rc = (intptr_t)opaque; + + if (block_job_is_cancelled(job)) { + rc = -ECANCELED; + } + + block_job_completed(job, rc); + bdrv_unref(bs); +} + +static void coroutine_fn test_block_job_run(void *opaque) +{ + TestBlockJob *s = opaque; + BlockJob *job = &s->common; + + while (s->iterations--) { + if (s->use_timer) { + block_job_sleep_ns(job, QEMU_CLOCK_REALTIME, 0); + } else { + block_job_yield(job); + } + + if (block_job_is_cancelled(job)) { + break; + } + } + + block_job_defer_to_main_loop(job, test_block_job_complete, + (void *)(intptr_t)s->rc); +} + +typedef struct { + TestBlockJob *job; + int *result; +} TestBlockJobCBData; + +static void test_block_job_cb(void *opaque, int ret) +{ + TestBlockJobCBData *data = opaque; + if (!ret && block_job_is_cancelled(&data->job->common)) { + ret = -ECANCELED; + } + *data->result = ret; + g_free(data); +} + +/* Create a block job that completes with a given return code after a given + * number of event loop iterations. The return code is stored in the given + * result pointer. + * + * The event loop iterations can either be handled automatically with a 0 delay + * timer, or they can be stepped manually by entering the coroutine. + */ +static BlockJob *test_block_job_start(unsigned int iterations, + bool use_timer, + int rc, int *result) +{ + BlockDriverState *bs; + TestBlockJob *s; + TestBlockJobCBData *data; + + data = g_new0(TestBlockJobCBData, 1); + bs = bdrv_new(); + s = block_job_create(&test_block_job_driver, bs, 0, test_block_job_cb, + data, &error_abort); + s->iterations = iterations; + s->use_timer = use_timer; + s->rc = rc; + s->result = result; + s->common.co = qemu_coroutine_create(test_block_job_run); + data->job = s; + data->result = result; + qemu_coroutine_enter(s->common.co, s); + return &s->common; +} + +static void test_single_job(int expected) +{ + BlockJob *job; + BlockJobTxn *txn; + int result = -EINPROGRESS; + + txn = block_job_txn_new(); + job = test_block_job_start(1, true, expected, &result); + block_job_txn_add_job(txn, job); + + if (expected == -ECANCELED) { + block_job_cancel(job); + } + + while (result == -EINPROGRESS) { + aio_poll(qemu_get_aio_context(), true); + } + g_assert_cmpint(result, ==, expected); + + block_job_txn_unref(txn); +} + +static void test_single_job_success(void) +{ + test_single_job(0); +} + +static void test_single_job_failure(void) +{ + test_single_job(-EIO); +} + +static void test_single_job_cancel(void) +{ + test_single_job(-ECANCELED); +} + +static void test_pair_jobs(int expected1, int expected2) +{ + BlockJob *job1; + BlockJob *job2; + BlockJobTxn *txn; + int result1 = -EINPROGRESS; + 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); + + if (expected1 == -ECANCELED) { + block_job_cancel(job1); + } + if (expected2 == -ECANCELED) { + block_job_cancel(job2); + } + + while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { + aio_poll(qemu_get_aio_context(), true); + } + + /* Failure or cancellation of one job cancels the other job */ + if (expected1 != 0) { + expected2 = -ECANCELED; + } else if (expected2 != 0) { + expected1 = -ECANCELED; + } + + g_assert_cmpint(result1, ==, expected1); + g_assert_cmpint(result2, ==, expected2); + + block_job_txn_unref(txn); +} + +static void test_pair_jobs_success(void) +{ + test_pair_jobs(0, 0); +} + +static void test_pair_jobs_failure(void) +{ + /* Test both orderings. The two jobs run for a different number of + * iterations so the code path is different depending on which job fails + * first. + */ + test_pair_jobs(-EIO, 0); + test_pair_jobs(0, -EIO); +} + +static void test_pair_jobs_cancel(void) +{ + test_pair_jobs(-ECANCELED, 0); + test_pair_jobs(0, -ECANCELED); +} + +static void test_pair_jobs_fail_cancel_race(void) +{ + BlockJob *job1; + BlockJob *job2; + BlockJobTxn *txn; + int result1 = -EINPROGRESS; + 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); + + block_job_cancel(job1); + + /* Now make job2 finish before the main loop kicks jobs. This simulates + * the race between a pending kick and another job completing. + */ + block_job_enter(job2); + block_job_enter(job2); + + while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { + aio_poll(qemu_get_aio_context(), true); + } + + g_assert_cmpint(result1, ==, -ECANCELED); + g_assert_cmpint(result2, ==, -ECANCELED); + + block_job_txn_unref(txn); +} + +int main(int argc, char **argv) +{ + qemu_init_main_loop(&error_abort); + + g_test_init(&argc, &argv, NULL); + g_test_add_func("/single/success", test_single_job_success); + g_test_add_func("/single/failure", test_single_job_failure); + g_test_add_func("/single/cancel", test_single_job_cancel); + g_test_add_func("/pair/success", test_pair_jobs_success); + g_test_add_func("/pair/failure", test_pair_jobs_failure); + g_test_add_func("/pair/cancel", test_pair_jobs_cancel); + g_test_add_func("/pair/fail-cancel-race", test_pair_jobs_fail_cancel_race); + return g_test_run(); +} diff --git a/qemu/tests/test-coroutine.c b/qemu/tests/test-coroutine.c index b552d9f5e..dd4ced946 100644 --- a/qemu/tests/test-coroutine.c +++ b/qemu/tests/test-coroutine.c @@ -11,9 +11,10 @@ * */ +#include "qemu/osdep.h" #include -#include "block/coroutine.h" -#include "block/coroutine_int.h" +#include "qemu/coroutine.h" +#include "qemu/coroutine_int.h" /* * Check that qemu_in_coroutine() works diff --git a/qemu/tests/test-crypto-afsplit.c b/qemu/tests/test-crypto-afsplit.c new file mode 100644 index 000000000..f9f2fcd41 --- /dev/null +++ b/qemu/tests/test-crypto-afsplit.c @@ -0,0 +1,194 @@ +/* + * QEMU Crypto anti-forensic splitter + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "crypto/init.h" +#include "crypto/afsplit.h" + +typedef struct QCryptoAFSplitTestData QCryptoAFSplitTestData; +struct QCryptoAFSplitTestData { + const char *path; + QCryptoHashAlgorithm hash; + uint32_t stripes; + size_t blocklen; + const uint8_t *key; + const uint8_t *splitkey; +}; + +static QCryptoAFSplitTestData test_data[] = { + { + .path = "/crypto/afsplit/sha256/5", + .hash = QCRYPTO_HASH_ALG_SHA256, + .stripes = 5, + .blocklen = 32, + .key = (const uint8_t *) + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf", + .splitkey = (const uint8_t *) + "\xfd\xd2\x73\xb1\x7d\x99\x93\x34" + "\x70\xde\xfa\x07\xc5\xac\x58\xd2" + "\x30\x67\x2f\x1a\x35\x43\x60\x7d" + "\x77\x02\xdb\x62\x3c\xcb\x2c\x33" + "\x48\x08\xb6\xf1\x7c\xa3\x20\xa0" + "\xad\x2d\x4c\xf3\xcd\x18\x6f\x53" + "\xf9\xe8\xe7\x59\x27\x3c\xa9\x54" + "\x61\x87\xb3\xaf\xf6\xf7\x7e\x64" + "\x86\xaa\x89\x7f\x1f\x9f\xdb\x86" + "\xf4\xa2\x16\xff\xa3\x4f\x8c\xa1" + "\x59\xc4\x23\x34\x28\xc4\x77\x71" + "\x83\xd4\xcd\x8e\x89\x1b\xc7\xc5" + "\xae\x4d\xa9\xcd\xc9\x72\x85\x70" + "\x13\x68\x52\x83\xfc\xb8\x11\x72" + "\xba\x3d\xc6\x4a\x28\xfa\xe2\x86" + "\x7b\x27\xab\x58\xe1\xa4\xca\xf6" + "\x9e\xbc\xfe\x0c\x92\x79\xb3\xec" + "\x1c\x5f\x79\x3b\x0d\x1e\xaa\x1a" + "\x77\x0f\x70\x19\x4b\xc8\x80\xee" + "\x27\x7c\x6e\x4a\x91\x96\x5c\xf4" + }, + { + .path = "/crypto/afsplit/sha256/5000", + .hash = QCRYPTO_HASH_ALG_SHA256, + .stripes = 5000, + .blocklen = 16, + .key = (const uint8_t *) + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + }, + { + .path = "/crypto/afsplit/sha1/1000", + .hash = QCRYPTO_HASH_ALG_SHA1, + .stripes = 1000, + .blocklen = 32, + .key = (const uint8_t *) + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf", + }, + { + .path = "/crypto/afsplit/sha256/big", + .hash = QCRYPTO_HASH_ALG_SHA256, + .stripes = 1000, + .blocklen = 64, + .key = (const uint8_t *) + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + }, +}; + + +static inline char hex(int i) +{ + if (i < 10) { + return '0' + i; + } + return 'a' + (i - 10); +} + +static char *hex_string(const uint8_t *bytes, + size_t len) +{ + char *hexstr = g_new0(char, len * 2 + 1); + size_t i; + + for (i = 0; i < len; i++) { + hexstr[i * 2] = hex((bytes[i] >> 4) & 0xf); + hexstr[i * 2 + 1] = hex(bytes[i] & 0xf); + } + hexstr[len * 2] = '\0'; + + return hexstr; +} + +static void test_afsplit(const void *opaque) +{ + const QCryptoAFSplitTestData *data = opaque; + size_t splitlen = data->blocklen * data->stripes; + uint8_t *splitkey = g_new0(uint8_t, splitlen); + uint8_t *key = g_new0(uint8_t, data->blocklen); + gchar *expect, *actual; + + /* First time we round-trip the key */ + qcrypto_afsplit_encode(data->hash, + data->blocklen, data->stripes, + data->key, splitkey, + &error_abort); + + qcrypto_afsplit_decode(data->hash, + data->blocklen, data->stripes, + splitkey, key, + &error_abort); + + expect = hex_string(data->key, data->blocklen); + actual = hex_string(key, data->blocklen); + + g_assert_cmpstr(actual, ==, expect); + + g_free(actual); + g_free(expect); + + /* Second time we merely try decoding a previous split */ + if (data->splitkey) { + memset(key, 0, data->blocklen); + + qcrypto_afsplit_decode(data->hash, + data->blocklen, data->stripes, + data->splitkey, key, + &error_abort); + + expect = hex_string(data->key, data->blocklen); + actual = hex_string(key, data->blocklen); + + g_assert_cmpstr(actual, ==, expect); + + g_free(actual); + g_free(expect); + } + + g_free(key); + g_free(splitkey); +} + +int main(int argc, char **argv) +{ + size_t i; + + g_test_init(&argc, &argv, NULL); + + g_assert(qcrypto_init(NULL) == 0); + + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + if (!qcrypto_hash_supports(test_data[i].hash)) { + continue; + } + g_test_add_data_func(test_data[i].path, &test_data[i], test_afsplit); + } + return g_test_run(); +} diff --git a/qemu/tests/test-crypto-block.c b/qemu/tests/test-crypto-block.c new file mode 100644 index 000000000..a38110d3f --- /dev/null +++ b/qemu/tests/test-crypto-block.c @@ -0,0 +1,363 @@ +/* + * QEMU Crypto block encryption + * + * Copyright (c) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "crypto/init.h" +#include "crypto/block.h" +#include "qemu/buffer.h" +#include "crypto/secret.h" +#ifndef _WIN32 +#include +#endif + +#if defined(CONFIG_UUID) && (defined(_WIN32) || defined RUSAGE_THREAD) +#define TEST_LUKS +#else +#undef TEST_LUKS +#endif + +static QCryptoBlockCreateOptions qcow_create_opts = { + .format = Q_CRYPTO_BLOCK_FORMAT_QCOW, + .u.qcow = { + .has_key_secret = true, + .key_secret = (char *)"sec0", + }, +}; + +static QCryptoBlockOpenOptions qcow_open_opts = { + .format = Q_CRYPTO_BLOCK_FORMAT_QCOW, + .u.qcow = { + .has_key_secret = true, + .key_secret = (char *)"sec0", + }, +}; + + +#ifdef TEST_LUKS +static QCryptoBlockOpenOptions luks_open_opts = { + .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .u.luks = { + .has_key_secret = true, + .key_secret = (char *)"sec0", + }, +}; + + +/* Creation with all default values */ +static QCryptoBlockCreateOptions luks_create_opts_default = { + .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .u.luks = { + .has_key_secret = true, + .key_secret = (char *)"sec0", + }, +}; + + +/* ...and with explicit values */ +static QCryptoBlockCreateOptions luks_create_opts_aes256_cbc_plain64 = { + .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .u.luks = { + .has_key_secret = true, + .key_secret = (char *)"sec0", + .has_cipher_alg = true, + .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .has_cipher_mode = true, + .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, + .has_ivgen_alg = true, + .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + }, +}; + + +static QCryptoBlockCreateOptions luks_create_opts_aes256_cbc_essiv = { + .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .u.luks = { + .has_key_secret = true, + .key_secret = (char *)"sec0", + .has_cipher_alg = true, + .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .has_cipher_mode = true, + .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, + .has_ivgen_alg = true, + .ivgen_alg = QCRYPTO_IVGEN_ALG_ESSIV, + .has_ivgen_hash_alg = true, + .ivgen_hash_alg = QCRYPTO_HASH_ALG_SHA256, + .has_hash_alg = true, + .hash_alg = QCRYPTO_HASH_ALG_SHA1, + }, +}; +#endif /* TEST_LUKS */ + + +static struct QCryptoBlockTestData { + const char *path; + QCryptoBlockCreateOptions *create_opts; + QCryptoBlockOpenOptions *open_opts; + + bool expect_header; + + QCryptoCipherAlgorithm cipher_alg; + QCryptoCipherMode cipher_mode; + QCryptoHashAlgorithm hash_alg; + + QCryptoIVGenAlgorithm ivgen_alg; + QCryptoHashAlgorithm ivgen_hash; + + bool slow; +} test_data[] = { + { + .path = "/crypto/block/qcow", + .create_opts = &qcow_create_opts, + .open_opts = &qcow_open_opts, + + .expect_header = false, + + .cipher_alg = QCRYPTO_CIPHER_ALG_AES_128, + .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, + + .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + }, +#ifdef TEST_LUKS + { + .path = "/crypto/block/luks/default", + .create_opts = &luks_create_opts_default, + .open_opts = &luks_open_opts, + + .expect_header = true, + + .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .cipher_mode = QCRYPTO_CIPHER_MODE_XTS, + .hash_alg = QCRYPTO_HASH_ALG_SHA256, + + .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + + .slow = true, + }, + { + .path = "/crypto/block/luks/aes-256-cbc-plain64", + .create_opts = &luks_create_opts_aes256_cbc_plain64, + .open_opts = &luks_open_opts, + + .expect_header = true, + + .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, + .hash_alg = QCRYPTO_HASH_ALG_SHA256, + + .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + + .slow = true, + }, + { + .path = "/crypto/block/luks/aes-256-cbc-essiv", + .create_opts = &luks_create_opts_aes256_cbc_essiv, + .open_opts = &luks_open_opts, + + .expect_header = true, + + .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, + .hash_alg = QCRYPTO_HASH_ALG_SHA1, + + .ivgen_alg = QCRYPTO_IVGEN_ALG_ESSIV, + .ivgen_hash = QCRYPTO_HASH_ALG_SHA256, + + .slow = true, + }, +#endif +}; + + +static ssize_t test_block_read_func(QCryptoBlock *block, + size_t offset, + uint8_t *buf, + size_t buflen, + Error **errp, + void *opaque) +{ + Buffer *header = opaque; + + g_assert_cmpint(offset + buflen, <=, header->capacity); + + memcpy(buf, header->buffer + offset, buflen); + + return buflen; +} + + +static ssize_t test_block_init_func(QCryptoBlock *block, + size_t headerlen, + Error **errp, + void *opaque) +{ + Buffer *header = opaque; + + g_assert_cmpint(header->capacity, ==, 0); + + buffer_reserve(header, headerlen); + + return headerlen; +} + + +static ssize_t test_block_write_func(QCryptoBlock *block, + size_t offset, + const uint8_t *buf, + size_t buflen, + Error **errp, + void *opaque) +{ + Buffer *header = opaque; + + g_assert_cmpint(buflen + offset, <=, header->capacity); + + memcpy(header->buffer + offset, buf, buflen); + header->offset = offset + buflen; + + return buflen; +} + + +static Object *test_block_secret(void) +{ + return object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "data", "123456", + NULL); +} + +static void test_block_assert_setup(const struct QCryptoBlockTestData *data, + QCryptoBlock *blk) +{ + QCryptoIVGen *ivgen; + QCryptoCipher *cipher; + + ivgen = qcrypto_block_get_ivgen(blk); + cipher = qcrypto_block_get_cipher(blk); + + g_assert(ivgen); + g_assert(cipher); + + g_assert_cmpint(data->cipher_alg, ==, cipher->alg); + g_assert_cmpint(data->cipher_mode, ==, cipher->mode); + g_assert_cmpint(data->hash_alg, ==, + qcrypto_block_get_kdf_hash(blk)); + + g_assert_cmpint(data->ivgen_alg, ==, + qcrypto_ivgen_get_algorithm(ivgen)); + g_assert_cmpint(data->ivgen_hash, ==, + qcrypto_ivgen_get_hash(ivgen)); +} + + +static void test_block(gconstpointer opaque) +{ + const struct QCryptoBlockTestData *data = opaque; + QCryptoBlock *blk; + Buffer header; + Object *sec = test_block_secret(); + + memset(&header, 0, sizeof(header)); + buffer_init(&header, "header"); + + blk = qcrypto_block_create(data->create_opts, + test_block_init_func, + test_block_write_func, + &header, + &error_abort); + g_assert(blk); + + if (data->expect_header) { + g_assert_cmpint(header.capacity, >, 0); + } else { + g_assert_cmpint(header.capacity, ==, 0); + } + + test_block_assert_setup(data, blk); + + qcrypto_block_free(blk); + object_unparent(sec); + + /* Ensure we can't open without the secret */ + blk = qcrypto_block_open(data->open_opts, + test_block_read_func, + &header, + 0, + NULL); + g_assert(blk == NULL); + + /* Ensure we can't open without the secret, unless NO_IO */ + blk = qcrypto_block_open(data->open_opts, + test_block_read_func, + &header, + QCRYPTO_BLOCK_OPEN_NO_IO, + &error_abort); + + g_assert(qcrypto_block_get_cipher(blk) == NULL); + g_assert(qcrypto_block_get_ivgen(blk) == NULL); + + qcrypto_block_free(blk); + + + /* Now open for real with secret */ + sec = test_block_secret(); + blk = qcrypto_block_open(data->open_opts, + test_block_read_func, + &header, + 0, + &error_abort); + g_assert(blk); + + test_block_assert_setup(data, blk); + + qcrypto_block_free(blk); + + object_unparent(sec); + + buffer_free(&header); +} + + +int main(int argc, char **argv) +{ + gsize i; + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + + g_assert(qcrypto_init(NULL) == 0); + + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + if (test_data[i].open_opts->format == Q_CRYPTO_BLOCK_FORMAT_LUKS && + !qcrypto_hash_supports(test_data[i].hash_alg)) { + continue; + } + if (!test_data[i].slow || + g_test_slow()) { + g_test_add_data_func(test_data[i].path, &test_data[i], test_block); + } + } + + return g_test_run(); +} diff --git a/qemu/tests/test-crypto-cipher.c b/qemu/tests/test-crypto-cipher.c index 9d38d2640..66d1c63fd 100644 --- a/qemu/tests/test-crypto-cipher.c +++ b/qemu/tests/test-crypto-cipher.c @@ -18,10 +18,12 @@ * */ +#include "qemu/osdep.h" #include #include "crypto/init.h" #include "crypto/cipher.h" +#include "qapi/error.h" typedef struct QCryptoCipherTestData QCryptoCipherTestData; struct QCryptoCipherTestData { @@ -164,6 +166,211 @@ static QCryptoCipherTestData test_data[] = { "ffd29f1bb5596ad94ea2d8e6196b7f09" "30d8ed0bf2773af36dd82a6280c20926", }, + { + /* RFC 2144, Appendix B.1 */ + .path = "/crypto/cipher/cast5-128", + .alg = QCRYPTO_CIPHER_ALG_CAST5_128, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "0123456712345678234567893456789A", + .plaintext = "0123456789abcdef", + .ciphertext = "238b4fe5847e44b2", + }, + { + /* libgcrypt serpent.c */ + .path = "/crypto/cipher/serpent-128", + .alg = QCRYPTO_CIPHER_ALG_SERPENT_128, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "00000000000000000000000000000000", + .plaintext = "d29d576fcea3a3a7ed9099f29273d78e", + .ciphertext = "b2288b968ae8b08648d1ce9606fd992d", + }, + { + /* libgcrypt serpent.c */ + .path = "/crypto/cipher/serpent-192", + .alg = QCRYPTO_CIPHER_ALG_SERPENT_192, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "00000000000000000000000000000000" + "0000000000000000", + .plaintext = "d29d576fceaba3a7ed9899f2927bd78e", + .ciphertext = "130e353e1037c22405e8faefb2c3c3e9", + }, + { + /* libgcrypt serpent.c */ + .path = "/crypto/cipher/serpent-256a", + .alg = QCRYPTO_CIPHER_ALG_SERPENT_256, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "00000000000000000000000000000000" + "00000000000000000000000000000000", + .plaintext = "d095576fcea3e3a7ed98d9f29073d78e", + .ciphertext = "b90ee5862de69168f2bdd5125b45472b", + }, + { + /* libgcrypt serpent.c */ + .path = "/crypto/cipher/serpent-256b", + .alg = QCRYPTO_CIPHER_ALG_SERPENT_256, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "00000000000000000000000000000000" + "00000000000000000000000000000000", + .plaintext = "00000000010000000200000003000000", + .ciphertext = "2061a42782bd52ec691ec383b03ba77c", + }, + { + /* Twofish paper "Known Answer Test" */ + .path = "/crypto/cipher/twofish-128", + .alg = QCRYPTO_CIPHER_ALG_TWOFISH_128, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "d491db16e7b1c39e86cb086b789f5419", + .plaintext = "019f9809de1711858faac3a3ba20fbc3", + .ciphertext = "6363977de839486297e661c6c9d668eb", + }, + { + /* Twofish paper "Known Answer Test", I=3 */ + .path = "/crypto/cipher/twofish-192", + .alg = QCRYPTO_CIPHER_ALG_TWOFISH_192, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "88b2b2706b105e36b446bb6d731a1e88" + "efa71f788965bd44", + .plaintext = "39da69d6ba4997d585b6dc073ca341b2", + .ciphertext = "182b02d81497ea45f9daacdc29193a65", + }, + { + /* Twofish paper "Known Answer Test", I=4 */ + .path = "/crypto/cipher/twofish-256", + .alg = QCRYPTO_CIPHER_ALG_TWOFISH_256, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "d43bb7556ea32e46f2a282b7d45b4e0d" + "57ff739d4dc92c1bd7fc01700cc8216f", + .plaintext = "90afe91bb288544f2c32dc239b2635e6", + .ciphertext = "6cb4561c40bf0a9705931cb6d408e7fa", + }, + { + /* #1 32 byte key, 32 byte PTX */ + .path = "/crypto/cipher/aes-xts-128-1", + .alg = QCRYPTO_CIPHER_ALG_AES_128, + .mode = QCRYPTO_CIPHER_MODE_XTS, + .key = + "00000000000000000000000000000000" + "00000000000000000000000000000000", + .iv = + "00000000000000000000000000000000", + .plaintext = + "00000000000000000000000000000000" + "00000000000000000000000000000000", + .ciphertext = + "917cf69ebd68b2ec9b9fe9a3eadda692" + "cd43d2f59598ed858c02c2652fbf922e", + }, + { + /* #2, 32 byte key, 32 byte PTX */ + .path = "/crypto/cipher/aes-xts-128-2", + .alg = QCRYPTO_CIPHER_ALG_AES_128, + .mode = QCRYPTO_CIPHER_MODE_XTS, + .key = + "11111111111111111111111111111111" + "22222222222222222222222222222222", + .iv = + "33333333330000000000000000000000", + .plaintext = + "44444444444444444444444444444444" + "44444444444444444444444444444444", + .ciphertext = + "c454185e6a16936e39334038acef838b" + "fb186fff7480adc4289382ecd6d394f0", + }, + { + /* #5 from xts.7, 32 byte key, 32 byte PTX */ + .path = "/crypto/cipher/aes-xts-128-3", + .alg = QCRYPTO_CIPHER_ALG_AES_128, + .mode = QCRYPTO_CIPHER_MODE_XTS, + .key = + "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0" + "bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0", + .iv = + "9a785634120000000000000000000000", + .plaintext = + "44444444444444444444444444444444" + "44444444444444444444444444444444", + .ciphertext = + "b01f86f8edc1863706fa8a4253e34f28" + "af319de38334870f4dd1f94cbe9832f1", + }, + { + /* #4, 32 byte key, 512 byte PTX */ + .path = "/crypto/cipher/aes-xts-128-4", + .alg = QCRYPTO_CIPHER_ALG_AES_128, + .mode = QCRYPTO_CIPHER_MODE_XTS, + .key = + "27182818284590452353602874713526" + "31415926535897932384626433832795", + .iv = + "00000000000000000000000000000000", + .plaintext = + "000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f" + "505152535455565758595a5b5c5d5e5f" + "606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" + "000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f" + "505152535455565758595a5b5c5d5e5f" + "606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + .ciphertext = + "27a7479befa1d476489f308cd4cfa6e2" + "a96e4bbe3208ff25287dd3819616e89c" + "c78cf7f5e543445f8333d8fa7f560000" + "05279fa5d8b5e4ad40e736ddb4d35412" + "328063fd2aab53e5ea1e0a9f332500a5" + "df9487d07a5c92cc512c8866c7e860ce" + "93fdf166a24912b422976146ae20ce84" + "6bb7dc9ba94a767aaef20c0d61ad0265" + "5ea92dc4c4e41a8952c651d33174be51" + "a10c421110e6d81588ede82103a252d8" + "a750e8768defffed9122810aaeb99f91" + "72af82b604dc4b8e51bcb08235a6f434" + "1332e4ca60482a4ba1a03b3e65008fc5" + "da76b70bf1690db4eae29c5f1badd03c" + "5ccf2a55d705ddcd86d449511ceb7ec3" + "0bf12b1fa35b913f9f747a8afd1b130e" + "94bff94effd01a91735ca1726acd0b19" + "7c4e5b03393697e126826fb6bbde8ecc" + "1e08298516e2c9ed03ff3c1b7860f6de" + "76d4cecd94c8119855ef5297ca67e9f3" + "e7ff72b1e99785ca0a7e7720c5b36dc6" + "d72cac9574c8cbbc2f801e23e56fd344" + "b07f22154beba0f08ce8891e643ed995" + "c94d9a69c9f1b5f499027a78572aeebd" + "74d20cc39881c213ee770b1010e4bea7" + "18846977ae119f7a023ab58cca0ad752" + "afe656bb3c17256a9f6e9bf19fdd5a38" + "fc82bbe872c5539edb609ef4f79c203e" + "bb140f2e583cb2ad15b4aa5b655016a8" + "449277dbd477ef2c8d6c017db738b18d" + "eb4a427d1923ce3ff262735779a418f2" + "0a282df920147beabe421ee5319d0568", + }, }; @@ -229,6 +436,7 @@ static void test_cipher(const void *opaque) uint8_t *key, *iv, *ciphertext, *plaintext, *outtext; size_t nkey, niv, nciphertext, nplaintext; char *outtexthex; + size_t ivsize, keysize, blocksize; nkey = unhex_string(data->key, &key); niv = unhex_string(data->iv, &iv); @@ -245,6 +453,19 @@ static void test_cipher(const void *opaque) &error_abort); g_assert(cipher != NULL); + keysize = qcrypto_cipher_get_key_len(data->alg); + blocksize = qcrypto_cipher_get_block_len(data->alg); + ivsize = qcrypto_cipher_get_iv_len(data->alg, data->mode); + + if (data->mode == QCRYPTO_CIPHER_MODE_XTS) { + g_assert_cmpint(keysize * 2, ==, nkey); + } else { + g_assert_cmpint(keysize, ==, nkey); + } + g_assert_cmpint(ivsize, ==, niv); + if (niv) { + g_assert_cmpint(blocksize, ==, niv); + } if (iv) { g_assert(qcrypto_cipher_setiv(cipher, @@ -287,6 +508,79 @@ static void test_cipher(const void *opaque) qcrypto_cipher_free(cipher); } + +static void test_cipher_null_iv(void) +{ + QCryptoCipher *cipher; + uint8_t key[32] = { 0 }; + uint8_t plaintext[32] = { 0 }; + uint8_t ciphertext[32] = { 0 }; + + cipher = qcrypto_cipher_new( + QCRYPTO_CIPHER_ALG_AES_256, + QCRYPTO_CIPHER_MODE_CBC, + key, sizeof(key), + &error_abort); + g_assert(cipher != NULL); + + /* Don't call qcrypto_cipher_setiv */ + + qcrypto_cipher_encrypt(cipher, + plaintext, + ciphertext, + sizeof(plaintext), + &error_abort); + + qcrypto_cipher_free(cipher); +} + +static void test_cipher_short_plaintext(void) +{ + Error *err = NULL; + QCryptoCipher *cipher; + uint8_t key[32] = { 0 }; + uint8_t plaintext1[20] = { 0 }; + uint8_t ciphertext1[20] = { 0 }; + uint8_t plaintext2[40] = { 0 }; + uint8_t ciphertext2[40] = { 0 }; + int ret; + + cipher = qcrypto_cipher_new( + QCRYPTO_CIPHER_ALG_AES_256, + QCRYPTO_CIPHER_MODE_CBC, + key, sizeof(key), + &error_abort); + g_assert(cipher != NULL); + + /* Should report an error as plaintext is shorter + * than block size + */ + ret = qcrypto_cipher_encrypt(cipher, + plaintext1, + ciphertext1, + sizeof(plaintext1), + &err); + g_assert(ret == -1); + g_assert(err != NULL); + + error_free(err); + err = NULL; + + /* Should report an error as plaintext is larger than + * block size, but not a multiple of block size + */ + ret = qcrypto_cipher_encrypt(cipher, + plaintext2, + ciphertext2, + sizeof(plaintext2), + &err); + g_assert(ret == -1); + g_assert(err != NULL); + + error_free(err); + qcrypto_cipher_free(cipher); +} + int main(int argc, char **argv) { size_t i; @@ -296,7 +590,16 @@ int main(int argc, char **argv) g_assert(qcrypto_init(NULL) == 0); for (i = 0; i < G_N_ELEMENTS(test_data); i++) { - g_test_add_data_func(test_data[i].path, &test_data[i], test_cipher); + if (qcrypto_cipher_supports(test_data[i].alg)) { + g_test_add_data_func(test_data[i].path, &test_data[i], test_cipher); + } } + + g_test_add_func("/crypto/cipher/null-iv", + test_cipher_null_iv); + + g_test_add_func("/crypto/cipher/short-plaintext", + test_cipher_short_plaintext); + return g_test_run(); } diff --git a/qemu/tests/test-crypto-hash.c b/qemu/tests/test-crypto-hash.c index 911437e60..735d6d7e0 100644 --- a/qemu/tests/test-crypto-hash.c +++ b/qemu/tests/test-crypto-hash.c @@ -18,6 +18,7 @@ * */ +#include "qemu/osdep.h" #include #include "crypto/init.h" @@ -163,6 +164,11 @@ static void test_hash_digest(void) for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { int ret; char *digest; + size_t digestsize; + + digestsize = qcrypto_hash_digest_len(i); + + g_assert_cmpint(digestsize * 2, ==, strlen(expected_outputs[i])); ret = qcrypto_hash_digest(i, INPUT_TEXT, diff --git a/qemu/tests/test-crypto-ivgen.c b/qemu/tests/test-crypto-ivgen.c new file mode 100644 index 000000000..a5ff5d3da --- /dev/null +++ b/qemu/tests/test-crypto-ivgen.c @@ -0,0 +1,174 @@ +/* + * QEMU Crypto IV generator algorithms + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "crypto/ivgen.h" + + +struct QCryptoIVGenTestData { + const char *path; + uint64_t sector; + QCryptoIVGenAlgorithm ivalg; + QCryptoHashAlgorithm hashalg; + QCryptoCipherAlgorithm cipheralg; + const uint8_t *key; + size_t nkey; + const uint8_t *iv; + size_t niv; +} test_data[] = { + /* Small */ + { + "/crypto/ivgen/plain/1", + .sector = 0x1, + .ivalg = QCRYPTO_IVGEN_ALG_PLAIN, + .iv = (const uint8_t *)"\x01\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .niv = 16, + }, + /* Big ! */ + { + "/crypto/ivgen/plain/1f2e3d4c", + .sector = 0x1f2e3d4cULL, + .ivalg = QCRYPTO_IVGEN_ALG_PLAIN, + .iv = (const uint8_t *)"\x4c\x3d\x2e\x1f\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .niv = 16, + }, + /* Truncation */ + { + "/crypto/ivgen/plain/1f2e3d4c5b6a7988", + .sector = 0x1f2e3d4c5b6a7988ULL, + .ivalg = QCRYPTO_IVGEN_ALG_PLAIN, + .iv = (const uint8_t *)"\x88\x79\x6a\x5b\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .niv = 16, + }, + /* Small */ + { + "/crypto/ivgen/plain64/1", + .sector = 0x1, + .ivalg = QCRYPTO_IVGEN_ALG_PLAIN64, + .iv = (const uint8_t *)"\x01\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .niv = 16, + }, + /* Big ! */ + { + "/crypto/ivgen/plain64/1f2e3d4c", + .sector = 0x1f2e3d4cULL, + .ivalg = QCRYPTO_IVGEN_ALG_PLAIN64, + .iv = (const uint8_t *)"\x4c\x3d\x2e\x1f\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .niv = 16, + }, + /* No Truncation */ + { + "/crypto/ivgen/plain64/1f2e3d4c5b6a7988", + .sector = 0x1f2e3d4c5b6a7988ULL, + .ivalg = QCRYPTO_IVGEN_ALG_PLAIN64, + .iv = (const uint8_t *)"\x88\x79\x6a\x5b\x4c\x3d\x2e\x1f" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .niv = 16, + }, + /* Small */ + { + "/crypto/ivgen/essiv/1", + .sector = 0x1, + .ivalg = QCRYPTO_IVGEN_ALG_ESSIV, + .cipheralg = QCRYPTO_CIPHER_ALG_AES_128, + .hashalg = QCRYPTO_HASH_ALG_SHA256, + .key = (const uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .nkey = 16, + .iv = (const uint8_t *)"\xd4\x83\x71\xb2\xa1\x94\x53\x88" + "\x1c\x7a\x2d\06\x2d\x0b\x65\x46", + .niv = 16, + }, + /* Big ! */ + { + "/crypto/ivgen/essiv/1f2e3d4c", + .sector = 0x1f2e3d4cULL, + .ivalg = QCRYPTO_IVGEN_ALG_ESSIV, + .cipheralg = QCRYPTO_CIPHER_ALG_AES_128, + .hashalg = QCRYPTO_HASH_ALG_SHA256, + .key = (const uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .nkey = 16, + .iv = (const uint8_t *)"\x5d\x36\x09\x5d\xc6\x9e\x5e\xe9" + "\xe3\x02\x8d\xd8\x7a\x3d\xe7\x8f", + .niv = 16, + }, + /* No Truncation */ + { + "/crypto/ivgen/essiv/1f2e3d4c5b6a7988", + .sector = 0x1f2e3d4c5b6a7988ULL, + .ivalg = QCRYPTO_IVGEN_ALG_ESSIV, + .cipheralg = QCRYPTO_CIPHER_ALG_AES_128, + .hashalg = QCRYPTO_HASH_ALG_SHA256, + .key = (const uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .nkey = 16, + .iv = (const uint8_t *)"\x58\xbb\x81\x94\x51\x83\x23\x23" + "\x7a\x08\x93\xa9\xdc\xd2\xd9\xab", + .niv = 16, + }, +}; + + +static void test_ivgen(const void *opaque) +{ + const struct QCryptoIVGenTestData *data = opaque; + uint8_t *iv = g_new0(uint8_t, data->niv); + QCryptoIVGen *ivgen = qcrypto_ivgen_new( + data->ivalg, + data->cipheralg, + data->hashalg, + data->key, + data->nkey, + &error_abort); + + qcrypto_ivgen_calculate(ivgen, + data->sector, + iv, + data->niv, + &error_abort); + + g_assert(memcmp(iv, data->iv, data->niv) == 0); + + qcrypto_ivgen_free(ivgen); + g_free(iv); +} + +int main(int argc, char **argv) +{ + size_t i; + g_test_init(&argc, &argv, NULL); + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + if (test_data[i].ivalg == QCRYPTO_IVGEN_ALG_ESSIV && + !qcrypto_hash_supports(test_data[i].hashalg)) { + continue; + } + g_test_add_data_func(test_data[i].path, + &(test_data[i]), + test_ivgen); + } + return g_test_run(); +} diff --git a/qemu/tests/test-crypto-pbkdf.c b/qemu/tests/test-crypto-pbkdf.c new file mode 100644 index 000000000..8ceceb182 --- /dev/null +++ b/qemu/tests/test-crypto-pbkdf.c @@ -0,0 +1,393 @@ +/* + * QEMU Crypto cipher algorithms + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "crypto/init.h" +#ifndef _WIN32 +#include +#endif + +#if ((defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT)) && \ + (defined(_WIN32) || defined(RUSAGE_THREAD))) +#include "crypto/pbkdf.h" + +typedef struct QCryptoPbkdfTestData QCryptoPbkdfTestData; +struct QCryptoPbkdfTestData { + const char *path; + QCryptoHashAlgorithm hash; + unsigned int iterations; + const char *key; + size_t nkey; + const char *salt; + size_t nsalt; + const char *out; + size_t nout; + bool slow; +}; + +/* This test data comes from cryptsetup package + * + * $SRC/lib/crypto_backend/pbkdf2_generic.c + * + * under LGPLv2.1+ license + */ +static QCryptoPbkdfTestData test_data[] = { + /* RFC 3962 test data */ + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter1", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 1, + .key = "password", + .nkey = 8, + .salt = "ATHENA.MIT.EDUraeburn", + .nsalt = 21, + .out = "\xcd\xed\xb5\x28\x1b\xb2\xf8\x01" + "\x56\x5a\x11\x22\xb2\x56\x35\x15" + "\x0a\xd1\xf7\xa0\x4b\xb9\xf3\xa3" + "\x33\xec\xc0\xe2\xe1\xf7\x08\x37", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter2", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 2, + .key = "password", + .nkey = 8, + .salt = "ATHENA.MIT.EDUraeburn", + .nsalt = 21, + .out = "\x01\xdb\xee\x7f\x4a\x9e\x24\x3e" + "\x98\x8b\x62\xc7\x3c\xda\x93\x5d" + "\xa0\x53\x78\xb9\x32\x44\xec\x8f" + "\x48\xa9\x9e\x61\xad\x79\x9d\x86", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter1200a", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 1200, + .key = "password", + .nkey = 8, + .salt = "ATHENA.MIT.EDUraeburn", + .nsalt = 21, + .out = "\x5c\x08\xeb\x61\xfd\xf7\x1e\x4e" + "\x4e\xc3\xcf\x6b\xa1\xf5\x51\x2b" + "\xa7\xe5\x2d\xdb\xc5\xe5\x14\x2f" + "\x70\x8a\x31\xe2\xe6\x2b\x1e\x13", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter5", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 5, + .key = "password", + .nkey = 8, + .salt = "\0224VxxV4\022", /* "\x1234567878563412 */ + .nsalt = 8, + .out = "\xd1\xda\xa7\x86\x15\xf2\x87\xe6" + "\xa1\xc8\xb1\x20\xd7\x06\x2a\x49" + "\x3f\x98\xd2\x03\xe6\xbe\x49\xa6" + "\xad\xf4\xfa\x57\x4b\x6e\x64\xee", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter1200b", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 1200, + .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + .nkey = 64, + .salt = "pass phrase equals block size", + .nsalt = 29, + .out = "\x13\x9c\x30\xc0\x96\x6b\xc3\x2b" + "\xa5\x5f\xdb\xf2\x12\x53\x0a\xc9" + "\xc5\xec\x59\xf1\xa4\x52\xf5\xcc" + "\x9a\xd9\x40\xfe\xa0\x59\x8e\xd1", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter1200c", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 1200, + .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + .nkey = 65, + .salt = "pass phrase exceeds block size", + .nsalt = 30, + .out = "\x9c\xca\xd6\xd4\x68\x77\x0c\xd5" + "\x1b\x10\xe6\xa6\x87\x21\xbe\x61" + "\x1a\x8b\x4d\x28\x26\x01\xdb\x3b" + "\x36\xbe\x92\x46\x91\x5e\xc8\x2a", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter50", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 50, + .key = "\360\235\204\236", /* g-clef ("\xf09d849e) */ + .nkey = 4, + .salt = "EXAMPLE.COMpianist", + .nsalt = 18, + .out = "\x6b\x9c\xf2\x6d\x45\x45\x5a\x43" + "\xa5\xb8\xbb\x27\x6a\x40\x3b\x39" + "\xe7\xfe\x37\xa0\xc4\x1e\x02\xc2" + "\x81\xff\x30\x69\xe1\xe9\x4f\x52", + .nout = 32 + }, + + /* RFC-6070 test data */ + { + .path = "/crypto/pbkdf/rfc6070/sha1/iter1", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 1, + .key = "password", + .nkey = 8, + .salt = "salt", + .nsalt = 4, + .out = "\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9" + "\xb5\x24\xaf\x60\x12\x06\x2f\xe0\x37\xa6", + .nout = 20 + }, + { + .path = "/crypto/pbkdf/rfc6070/sha1/iter2", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 2, + .key = "password", + .nkey = 8, + .salt = "salt", + .nsalt = 4, + .out = "\xea\x6c\x01\x4d\xc7\x2d\x6f\x8c\xcd\x1e" + "\xd9\x2a\xce\x1d\x41\xf0\xd8\xde\x89\x57", + .nout = 20 + }, + { + .path = "/crypto/pbkdf/rfc6070/sha1/iter4096", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 4096, + .key = "password", + .nkey = 8, + .salt = "salt", + .nsalt = 4, + .out = "\x4b\x00\x79\x01\xb7\x65\x48\x9a\xbe\xad" + "\x49\xd9\x26\xf7\x21\xd0\x65\xa4\x29\xc1", + .nout = 20 + }, + { + .path = "/crypto/pbkdf/rfc6070/sha1/iter16777216", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 16777216, + .key = "password", + .nkey = 8, + .salt = "salt", + .nsalt = 4, + .out = "\xee\xfe\x3d\x61\xcd\x4d\xa4\xe4\xe9\x94" + "\x5b\x3d\x6b\xa2\x15\x8c\x26\x34\xe9\x84", + .nout = 20, + .slow = true, + }, + { + .path = "/crypto/pbkdf/rfc6070/sha1/iter4096a", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 4096, + .key = "passwordPASSWORDpassword", + .nkey = 24, + .salt = "saltSALTsaltSALTsaltSALTsaltSALTsalt", + .nsalt = 36, + .out = "\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8" + "\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96" + "\x4c\xf2\xf0\x70\x38", + .nout = 25 + }, + { + .path = "/crypto/pbkdf/rfc6070/sha1/iter4096b", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 4096, + .key = "pass\0word", + .nkey = 9, + .salt = "sa\0lt", + .nsalt = 5, + .out = "\x56\xfa\x6a\xa7\x55\x48\x09\x9d\xcc\x37" + "\xd7\xf0\x34\x25\xe0\xc3", + .nout = 16 + }, + + /* non-RFC misc test data */ +#ifdef CONFIG_NETTLE + { + /* empty password test. + * Broken with libgcrypt <= 1.5.0, hence CONFIG_NETTLE */ + .path = "/crypto/pbkdf/nonrfc/sha1/iter2", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 2, + .key = "", + .nkey = 0, + .salt = "salt", + .nsalt = 4, + .out = "\x13\x3a\x4c\xe8\x37\xb4\xd2\x52\x1e\xe2" + "\xbf\x03\xe1\x1c\x71\xca\x79\x4e\x07\x97", + .nout = 20 + }, +#endif + { + /* Password exceeds block size test */ + .path = "/crypto/pbkdf/nonrfc/sha256/iter1200", + .hash = QCRYPTO_HASH_ALG_SHA256, + .iterations = 1200, + .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + .nkey = 65, + .salt = "pass phrase exceeds block size", + .nsalt = 30, + .out = "\x22\x34\x4b\xc4\xb6\xe3\x26\x75" + "\xa8\x09\x0f\x3e\xa8\x0b\xe0\x1d" + "\x5f\x95\x12\x6a\x2c\xdd\xc3\xfa" + "\xcc\x4a\x5e\x6d\xca\x04\xec\x58", + .nout = 32 + }, +#if 0 + { + .path = "/crypto/pbkdf/nonrfc/sha512/iter1200", + .hash = QCRYPTO_HASH_ALG_SHA512, + .iterations = 1200, + .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + .nkey = 129, + .salt = "pass phrase exceeds block size", + .nsalt = 30, + .out = "\x0f\xb2\xed\x2c\x0e\x6e\xfb\x7d" + "\x7d\x8e\xdd\x58\x01\xb4\x59\x72" + "\x99\x92\x16\x30\x5e\xa4\x36\x8d" + "\x76\x14\x80\xf3\xe3\x7a\x22\xb9", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/nonrfc/whirlpool/iter1200", + .hash = QCRYPTO_HASH_ALG_WHIRLPOOL, + .iterations = 1200, + .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + .nkey = 65, + .salt = "pass phrase exceeds block size", + .nsalt = 30, + .out = "\x9c\x1c\x74\xf5\x88\x26\xe7\x6a" + "\x53\x58\xf4\x0c\x39\xe7\x80\x89" + "\x07\xc0\x31\x19\x9a\x50\xa2\x48" + "\xf1\xd9\xfe\x78\x64\xe5\x84\x50", + .nout = 32 + } +#endif +}; + + +static inline char hex(int i) +{ + if (i < 10) { + return '0' + i; + } + return 'a' + (i - 10); +} + +static char *hex_string(const uint8_t *bytes, + size_t len) +{ + char *hexstr = g_new0(char, len * 2 + 1); + size_t i; + + for (i = 0; i < len; i++) { + hexstr[i * 2] = hex((bytes[i] >> 4) & 0xf); + hexstr[i * 2 + 1] = hex(bytes[i] & 0xf); + } + hexstr[len * 2] = '\0'; + + return hexstr; +} + +static void test_pbkdf(const void *opaque) +{ + const QCryptoPbkdfTestData *data = opaque; + size_t nout = data->nout; + uint8_t *out = g_new0(uint8_t, nout); + gchar *expect, *actual; + + qcrypto_pbkdf2(data->hash, + (uint8_t *)data->key, data->nkey, + (uint8_t *)data->salt, data->nsalt, + data->iterations, + (uint8_t *)out, nout, + &error_abort); + + expect = hex_string((const uint8_t *)data->out, data->nout); + actual = hex_string(out, nout); + + g_assert_cmpstr(actual, ==, expect); + + g_free(actual); + g_free(expect); + g_free(out); +} + + +static void test_pbkdf_timing(void) +{ + uint8_t key[32]; + uint8_t salt[32]; + int iters; + + memset(key, 0x5d, sizeof(key)); + memset(salt, 0x7c, sizeof(salt)); + + iters = qcrypto_pbkdf2_count_iters(QCRYPTO_HASH_ALG_SHA256, + key, sizeof(key), + salt, sizeof(salt), + &error_abort); + + g_assert(iters >= (1 << 15)); +} + + +int main(int argc, char **argv) +{ + size_t i; + + g_test_init(&argc, &argv, NULL); + + g_assert(qcrypto_init(NULL) == 0); + + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + if (!test_data[i].slow || + g_test_slow()) { + g_test_add_data_func(test_data[i].path, &test_data[i], test_pbkdf); + } + } + + if (g_test_slow()) { + g_test_add_func("/crypt0/pbkdf/timing", test_pbkdf_timing); + } + + return g_test_run(); +} +#else +int main(int argc, char **argv) +{ + return 0; +} +#endif diff --git a/qemu/tests/test-crypto-secret.c b/qemu/tests/test-crypto-secret.c new file mode 100644 index 000000000..aa26c2049 --- /dev/null +++ b/qemu/tests/test-crypto-secret.c @@ -0,0 +1,455 @@ +/* + * QEMU Crypto secret handling + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include + +#include "crypto/init.h" +#include "crypto/secret.h" +#include "qapi/error.h" +#include "qemu/module.h" + +static void test_secret_direct(void) +{ + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "data", "123456", + NULL); + + char *pw = qcrypto_secret_lookup_as_utf8("sec0", + &error_abort); + + g_assert_cmpstr(pw, ==, "123456"); + + object_unparent(sec); + g_free(pw); +} + + +static void test_secret_indirect_good(void) +{ + Object *sec; + char *fname = NULL; + int fd = g_file_open_tmp("secretXXXXXX", + &fname, + NULL); + + g_assert(fd >= 0); + g_assert_nonnull(fname); + + g_assert(write(fd, "123456", 6) == 6); + + sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "file", fname, + NULL); + + char *pw = qcrypto_secret_lookup_as_utf8("sec0", + &error_abort); + + g_assert_cmpstr(pw, ==, "123456"); + + object_unparent(sec); + g_free(pw); + close(fd); + g_free(fname); +} + + +static void test_secret_indirect_badfile(void) +{ + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + NULL, + "file", "does-not-exist", + NULL); + + g_assert(sec == NULL); +} + + +static void test_secret_indirect_emptyfile(void) +{ + Object *sec; + char *fname = NULL; + int fd = g_file_open_tmp("secretXXXXXX", + &fname, + NULL); + + g_assert(fd >= 0); + g_assert_nonnull(fname); + + sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "file", fname, + NULL); + + char *pw = qcrypto_secret_lookup_as_utf8("sec0", + &error_abort); + + g_assert_cmpstr(pw, ==, ""); + + object_unparent(sec); + g_free(pw); + close(fd); + g_free(fname); +} + + +static void test_secret_noconv_base64_good(void) +{ + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "data", "MTIzNDU2", + "format", "base64", + NULL); + + char *pw = qcrypto_secret_lookup_as_base64("sec0", + &error_abort); + + g_assert_cmpstr(pw, ==, "MTIzNDU2"); + + object_unparent(sec); + g_free(pw); +} + + +static void test_secret_noconv_base64_bad(void) +{ + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + NULL, + "data", "MTI$NDU2", + "format", "base64", + NULL); + + g_assert(sec == NULL); +} + + +static void test_secret_noconv_utf8(void) +{ + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "data", "123456", + "format", "raw", + NULL); + + char *pw = qcrypto_secret_lookup_as_utf8("sec0", + &error_abort); + + g_assert_cmpstr(pw, ==, "123456"); + + object_unparent(sec); + g_free(pw); +} + + +static void test_secret_conv_base64_utf8valid(void) +{ + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "data", "MTIzNDU2", + "format", "base64", + NULL); + + char *pw = qcrypto_secret_lookup_as_utf8("sec0", + &error_abort); + + g_assert_cmpstr(pw, ==, "123456"); + + object_unparent(sec); + g_free(pw); +} + + +static void test_secret_conv_base64_utf8invalid(void) +{ + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "data", "f0VMRgIBAQAAAA==", + "format", "base64", + NULL); + + char *pw = qcrypto_secret_lookup_as_utf8("sec0", + NULL); + g_assert(pw == NULL); + + object_unparent(sec); +} + + +static void test_secret_conv_utf8_base64(void) +{ + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "data", "123456", + NULL); + + char *pw = qcrypto_secret_lookup_as_base64("sec0", + &error_abort); + + g_assert_cmpstr(pw, ==, "MTIzNDU2"); + + object_unparent(sec); + g_free(pw); +} + + +static void test_secret_crypt_raw(void) +{ + Object *master = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "master", + &error_abort, + "data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", + "format", "base64", + NULL); + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "data", + "\xCC\xBF\xF7\x09\x46\x19\x0B\x52\x2A\x3A\xB4\x6B\xCD\x7A\xB0\xB0", + "format", "raw", + "keyid", "master", + "iv", "0I7Gw/TKuA+Old2W2apQ3g==", + NULL); + + char *pw = qcrypto_secret_lookup_as_utf8("sec0", + &error_abort); + + g_assert_cmpstr(pw, ==, "123456"); + + object_unparent(sec); + object_unparent(master); + g_free(pw); +} + + +static void test_secret_crypt_base64(void) +{ + Object *master = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "master", + &error_abort, + "data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", + "format", "base64", + NULL); + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "data", "zL/3CUYZC1IqOrRrzXqwsA==", + "format", "base64", + "keyid", "master", + "iv", "0I7Gw/TKuA+Old2W2apQ3g==", + NULL); + + char *pw = qcrypto_secret_lookup_as_utf8("sec0", + &error_abort); + + g_assert_cmpstr(pw, ==, "123456"); + + object_unparent(sec); + object_unparent(master); + g_free(pw); +} + + +static void test_secret_crypt_short_key(void) +{ + Object *master = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "master", + &error_abort, + "data", "9miloPQCzGy+TL6aonfzVc", + "format", "base64", + NULL); + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + NULL, + "data", "zL/3CUYZC1IqOrRrzXqwsA==", + "format", "raw", + "keyid", "master", + "iv", "0I7Gw/TKuA+Old2W2apQ3g==", + NULL); + + g_assert(sec == NULL); + object_unparent(master); +} + + +static void test_secret_crypt_short_iv(void) +{ + Object *master = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "master", + &error_abort, + "data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", + "format", "base64", + NULL); + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + NULL, + "data", "zL/3CUYZC1IqOrRrzXqwsA==", + "format", "raw", + "keyid", "master", + "iv", "0I7Gw/TKuA+Old2W2a", + NULL); + + g_assert(sec == NULL); + object_unparent(master); +} + + +static void test_secret_crypt_missing_iv(void) +{ + Object *master = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "master", + &error_abort, + "data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", + "format", "base64", + NULL); + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + NULL, + "data", "zL/3CUYZC1IqOrRrzXqwsA==", + "format", "raw", + "keyid", "master", + NULL); + + g_assert(sec == NULL); + object_unparent(master); +} + + +static void test_secret_crypt_bad_iv(void) +{ + Object *master = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "master", + &error_abort, + "data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", + "format", "base64", + NULL); + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + NULL, + "data", "zL/3CUYZC1IqOrRrzXqwsA==", + "format", "raw", + "keyid", "master", + "iv", "0I7Gw/TK$$uA+Old2W2a", + NULL); + + g_assert(sec == NULL); + object_unparent(master); +} + + +int main(int argc, char **argv) +{ + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + + g_assert(qcrypto_init(NULL) == 0); + + g_test_add_func("/crypto/secret/direct", + test_secret_direct); + g_test_add_func("/crypto/secret/indirect/good", + test_secret_indirect_good); + g_test_add_func("/crypto/secret/indirect/badfile", + test_secret_indirect_badfile); + g_test_add_func("/crypto/secret/indirect/emptyfile", + test_secret_indirect_emptyfile); + + g_test_add_func("/crypto/secret/noconv/base64/good", + test_secret_noconv_base64_good); + g_test_add_func("/crypto/secret/noconv/base64/bad", + test_secret_noconv_base64_bad); + g_test_add_func("/crypto/secret/noconv/utf8", + test_secret_noconv_utf8); + g_test_add_func("/crypto/secret/conv/base64/utf8valid", + test_secret_conv_base64_utf8valid); + g_test_add_func("/crypto/secret/conv/base64/utf8invalid", + test_secret_conv_base64_utf8invalid); + g_test_add_func("/crypto/secret/conv/utf8/base64", + test_secret_conv_utf8_base64); + + g_test_add_func("/crypto/secret/crypt/raw", + test_secret_crypt_raw); + g_test_add_func("/crypto/secret/crypt/base64", + test_secret_crypt_base64); + g_test_add_func("/crypto/secret/crypt/shortkey", + test_secret_crypt_short_key); + g_test_add_func("/crypto/secret/crypt/shortiv", + test_secret_crypt_short_iv); + g_test_add_func("/crypto/secret/crypt/missingiv", + test_secret_crypt_missing_iv); + g_test_add_func("/crypto/secret/crypt/badiv", + test_secret_crypt_bad_iv); + + return g_test_run(); +} diff --git a/qemu/tests/test-crypto-tlscredsx509.c b/qemu/tests/test-crypto-tlscredsx509.c new file mode 100644 index 000000000..af2f80e89 --- /dev/null +++ b/qemu/tests/test-crypto-tlscredsx509.c @@ -0,0 +1,730 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Daniel P. Berrange + */ + +#include "qemu/osdep.h" + +#include "crypto-tls-x509-helpers.h" +#include "crypto/tlscredsx509.h" +#include "qapi/error.h" + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT + +#define WORKDIR "tests/test-crypto-tlscredsx509-work/" +#define KEYFILE WORKDIR "key-ctx.pem" + +struct QCryptoTLSCredsTestData { + bool isServer; + const char *cacrt; + const char *crt; + bool expectFail; +}; + + +static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, + const char *certdir, + Error **errp) +{ + Object *parent = object_get_objects_root(); + Object *creds = object_new_with_props( + TYPE_QCRYPTO_TLS_CREDS_X509, + parent, + "testtlscreds", + errp, + "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "server" : "client"), + "dir", certdir, + "verify-peer", "yes", + "sanity-check", "yes", + NULL); + + if (*errp) { + return NULL; + } + return QCRYPTO_TLS_CREDS(creds); +} + +/* + * This tests sanity checking of our own certificates + * + * The code being tested is used when TLS creds are created, + * and aim to ensure QMEU has been configured with sane + * certificates. This allows us to give much much much + * clearer error messages to the admin when they misconfigure + * things. + */ +static void test_tls_creds(const void *opaque) +{ + struct QCryptoTLSCredsTestData *data = + (struct QCryptoTLSCredsTestData *)opaque; + QCryptoTLSCreds *creds; + Error *err = NULL; + +#define CERT_DIR "tests/test-crypto-tlscredsx509-certs/" + mkdir(CERT_DIR, 0700); + + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + if (data->isServer) { + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); + } else { + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); + } + + if (access(data->cacrt, R_OK) == 0) { + g_assert(link(data->cacrt, + CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); + } + if (data->isServer) { + if (access(data->crt, R_OK) == 0) { + g_assert(link(data->crt, + CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0); + } + g_assert(link(KEYFILE, + CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0); + } else { + if (access(data->crt, R_OK) == 0) { + g_assert(link(data->crt, + CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0); + } + g_assert(link(KEYFILE, + CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0); + } + + creds = test_tls_creds_create( + (data->isServer ? + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER : + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT), + CERT_DIR, + &err); + + if (data->expectFail) { + error_free(err); + g_assert(creds == NULL); + } else { + if (err) { + g_printerr("Failed to generate creds: %s\n", + error_get_pretty(err)); + error_free(err); + } + g_assert(creds != NULL); + } + + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + if (data->isServer) { + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); + } else { + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); + } + rmdir(CERT_DIR); + if (creds) { + object_unparent(OBJECT(creds)); + } +} + +int main(int argc, char **argv) +{ + int ret; + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); + + mkdir(WORKDIR, 0700); + + test_tls_init(KEYFILE); + +# define TLS_TEST_REG(name, isServer, caCrt, crt, expectFail) \ + struct QCryptoTLSCredsTestData name = { \ + isServer, caCrt, crt, expectFail \ + }; \ + g_test_add_data_func("/qcrypto/tlscredsx509/" # name, \ + &name, test_tls_creds); \ + + /* A perfect CA, perfect client & perfect server */ + + /* Basic:CA:critical */ + TLS_ROOT_REQ(cacertreq, + "UK", "qemu CA", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + + TLS_CERT_REQ(servercertreq, cacertreq, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(clientcertreq, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + + TLS_TEST_REG(perfectserver, true, + cacertreq.filename, servercertreq.filename, false); + TLS_TEST_REG(perfectclient, false, + cacertreq.filename, clientcertreq.filename, false); + + + /* Some other CAs which are good */ + + /* Basic:CA:critical */ + TLS_ROOT_REQ(cacert1req, + "UK", "qemu CA 1", NULL, NULL, NULL, NULL, + true, true, true, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert1req, cacert1req, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + + /* Basic:CA:not-critical */ + TLS_ROOT_REQ(cacert2req, + "UK", "qemu CA 2", NULL, NULL, NULL, NULL, + true, false, true, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert2req, cacert2req, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + + /* Key usage:cert-sign:critical */ + TLS_ROOT_REQ(cacert3req, + "UK", "qemu CA 3", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert3req, cacert3req, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + + TLS_TEST_REG(goodca1, true, + cacert1req.filename, servercert1req.filename, false); + TLS_TEST_REG(goodca2, true, + cacert2req.filename, servercert2req.filename, false); + TLS_TEST_REG(goodca3, true, + cacert3req.filename, servercert3req.filename, false); + + /* Now some bad certs */ + + /* Key usage:dig-sig:not-critical */ + TLS_ROOT_REQ(cacert4req, + "UK", "qemu CA 4", NULL, NULL, NULL, NULL, + true, true, true, + true, false, GNUTLS_KEY_DIGITAL_SIGNATURE, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert4req, cacert4req, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + /* no-basic */ + TLS_ROOT_REQ(cacert5req, + "UK", "qemu CA 5", NULL, NULL, NULL, NULL, + false, false, false, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert5req, cacert5req, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + /* Key usage:dig-sig:critical */ + TLS_ROOT_REQ(cacert6req, + "UK", "qemu CA 6", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_DIGITAL_SIGNATURE, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert6req, cacert6req, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + + /* Technically a CA cert with basic constraints + * key purpose == key signing + non-critical should + * be rejected. GNUTLS < 3.1 does not reject it and + * we don't anticipate them changing this behaviour + */ + TLS_TEST_REG(badca1, true, cacert4req.filename, servercert4req.filename, + (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR >= 1) || + GNUTLS_VERSION_MAJOR > 3); + TLS_TEST_REG(badca2, true, + cacert5req.filename, servercert5req.filename, true); + TLS_TEST_REG(badca3, true, + cacert6req.filename, servercert6req.filename, true); + + + /* Various good servers */ + /* no usage or purpose */ + TLS_CERT_REQ(servercert7req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + /* usage:cert-sign+dig-sig+encipher:critical */ + TLS_CERT_REQ(servercert8req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT | + GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + /* usage:cert-sign:not-critical */ + TLS_CERT_REQ(servercert9req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, false, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + /* purpose:server:critical */ + TLS_CERT_REQ(servercert10req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + /* purpose:server:not-critical */ + TLS_CERT_REQ(servercert11req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, false, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + /* purpose:client+server:critical */ + TLS_CERT_REQ(servercert12req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, + GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, + 0, 0); + /* purpose:client+server:not-critical */ + TLS_CERT_REQ(servercert13req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, false, + GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, + 0, 0); + + TLS_TEST_REG(goodserver1, true, + cacertreq.filename, servercert7req.filename, false); + TLS_TEST_REG(goodserver2, true, + cacertreq.filename, servercert8req.filename, false); + TLS_TEST_REG(goodserver3, true, + cacertreq.filename, servercert9req.filename, false); + TLS_TEST_REG(goodserver4, true, + cacertreq.filename, servercert10req.filename, false); + TLS_TEST_REG(goodserver5, true, + cacertreq.filename, servercert11req.filename, false); + TLS_TEST_REG(goodserver6, true, + cacertreq.filename, servercert12req.filename, false); + TLS_TEST_REG(goodserver7, true, + cacertreq.filename, servercert13req.filename, false); + + /* Bad servers */ + + /* usage:cert-sign:critical */ + TLS_CERT_REQ(servercert14req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + /* purpose:client:critical */ + TLS_CERT_REQ(servercert15req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + /* usage: none:critical */ + TLS_CERT_REQ(servercert16req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, 0, + false, false, NULL, NULL, + 0, 0); + + TLS_TEST_REG(badserver1, true, + cacertreq.filename, servercert14req.filename, true); + TLS_TEST_REG(badserver2, true, + cacertreq.filename, servercert15req.filename, true); + TLS_TEST_REG(badserver3, true, + cacertreq.filename, servercert16req.filename, true); + + + + /* Various good clients */ + /* no usage or purpose */ + TLS_CERT_REQ(clientcert1req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + /* usage:cert-sign+dig-sig+encipher:critical */ + TLS_CERT_REQ(clientcert2req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT | + GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + /* usage:cert-sign:not-critical */ + TLS_CERT_REQ(clientcert3req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, false, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + /* purpose:client:critical */ + TLS_CERT_REQ(clientcert4req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + /* purpose:client:not-critical */ + TLS_CERT_REQ(clientcert5req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, false, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + /* purpose:client+client:critical */ + TLS_CERT_REQ(clientcert6req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, + GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, + 0, 0); + /* purpose:client+client:not-critical */ + TLS_CERT_REQ(clientcert7req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, false, + GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, + 0, 0); + + TLS_TEST_REG(goodclient1, false, + cacertreq.filename, clientcert1req.filename, false); + TLS_TEST_REG(goodclient2, false, + cacertreq.filename, clientcert2req.filename, false); + TLS_TEST_REG(goodclient3, false, + cacertreq.filename, clientcert3req.filename, false); + TLS_TEST_REG(goodclient4, false, + cacertreq.filename, clientcert4req.filename, false); + TLS_TEST_REG(goodclient5, false, + cacertreq.filename, clientcert5req.filename, false); + TLS_TEST_REG(goodclient6, false, + cacertreq.filename, clientcert6req.filename, false); + TLS_TEST_REG(goodclient7, false, + cacertreq.filename, clientcert7req.filename, false); + + /* Bad clients */ + + /* usage:cert-sign:critical */ + TLS_CERT_REQ(clientcert8req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + /* purpose:client:critical */ + TLS_CERT_REQ(clientcert9req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + /* usage: none:critical */ + TLS_CERT_REQ(clientcert10req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, 0, + false, false, NULL, NULL, + 0, 0); + + TLS_TEST_REG(badclient1, false, + cacertreq.filename, clientcert8req.filename, true); + TLS_TEST_REG(badclient2, false, + cacertreq.filename, clientcert9req.filename, true); + TLS_TEST_REG(badclient3, false, + cacertreq.filename, clientcert10req.filename, true); + + + + /* Expired stuff */ + + TLS_ROOT_REQ(cacertexpreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, -1); + TLS_CERT_REQ(servercertexpreq, cacertexpreq, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(servercertexp1req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, -1); + TLS_CERT_REQ(clientcertexp1req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, -1); + + TLS_TEST_REG(expired1, true, + cacertexpreq.filename, servercertexpreq.filename, true); + TLS_TEST_REG(expired2, true, + cacertreq.filename, servercertexp1req.filename, true); + TLS_TEST_REG(expired3, false, + cacertreq.filename, clientcertexp1req.filename, true); + + + /* Not activated stuff */ + + TLS_ROOT_REQ(cacertnewreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 1, 2); + TLS_CERT_REQ(servercertnewreq, cacertnewreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(servercertnew1req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 1, 2); + TLS_CERT_REQ(clientcertnew1req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 1, 2); + + TLS_TEST_REG(inactive1, true, + cacertnewreq.filename, servercertnewreq.filename, true); + TLS_TEST_REG(inactive2, true, + cacertreq.filename, servercertnew1req.filename, true); + TLS_TEST_REG(inactive3, false, + cacertreq.filename, clientcertnew1req.filename, true); + + TLS_ROOT_REQ(cacertrootreq, + "UK", "qemu root", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1areq, cacertrootreq, + "UK", "qemu level 1a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1breq, cacertrootreq, + "UK", "qemu level 1b", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel2areq, cacertlevel1areq, + "UK", "qemu level 2a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercertlevel3areq, cacertlevel2areq, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(clientcertlevel2breq, cacertlevel1breq, + "UK", "qemu client level 2b", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + + gnutls_x509_crt_t certchain[] = { + cacertrootreq.crt, + cacertlevel1areq.crt, + cacertlevel1breq.crt, + cacertlevel2areq.crt, + }; + + test_tls_write_cert_chain(WORKDIR "cacertchain-ctx.pem", + certchain, + G_N_ELEMENTS(certchain)); + + TLS_TEST_REG(chain1, true, + WORKDIR "cacertchain-ctx.pem", + servercertlevel3areq.filename, false); + TLS_TEST_REG(chain2, false, + WORKDIR "cacertchain-ctx.pem", + clientcertlevel2breq.filename, false); + + /* Some missing certs - first two are fatal, the last + * is ok + */ + TLS_TEST_REG(missingca, true, + "cacertdoesnotexist.pem", + servercert1req.filename, true); + TLS_TEST_REG(missingserver, true, + cacert1req.filename, + "servercertdoesnotexist.pem", true); + TLS_TEST_REG(missingclient, false, + cacert1req.filename, + "clientcertdoesnotexist.pem", false); + + ret = g_test_run(); + + test_tls_discard_cert(&cacertreq); + test_tls_discard_cert(&cacert1req); + test_tls_discard_cert(&cacert2req); + test_tls_discard_cert(&cacert3req); + test_tls_discard_cert(&cacert4req); + test_tls_discard_cert(&cacert5req); + test_tls_discard_cert(&cacert6req); + + test_tls_discard_cert(&servercertreq); + test_tls_discard_cert(&servercert1req); + test_tls_discard_cert(&servercert2req); + test_tls_discard_cert(&servercert3req); + test_tls_discard_cert(&servercert4req); + test_tls_discard_cert(&servercert5req); + test_tls_discard_cert(&servercert6req); + test_tls_discard_cert(&servercert7req); + test_tls_discard_cert(&servercert8req); + test_tls_discard_cert(&servercert9req); + test_tls_discard_cert(&servercert10req); + test_tls_discard_cert(&servercert11req); + test_tls_discard_cert(&servercert12req); + test_tls_discard_cert(&servercert13req); + test_tls_discard_cert(&servercert14req); + test_tls_discard_cert(&servercert15req); + test_tls_discard_cert(&servercert16req); + + test_tls_discard_cert(&clientcertreq); + test_tls_discard_cert(&clientcert1req); + test_tls_discard_cert(&clientcert2req); + test_tls_discard_cert(&clientcert3req); + test_tls_discard_cert(&clientcert4req); + test_tls_discard_cert(&clientcert5req); + test_tls_discard_cert(&clientcert6req); + test_tls_discard_cert(&clientcert7req); + test_tls_discard_cert(&clientcert8req); + test_tls_discard_cert(&clientcert9req); + test_tls_discard_cert(&clientcert10req); + + test_tls_discard_cert(&cacertexpreq); + test_tls_discard_cert(&servercertexpreq); + test_tls_discard_cert(&servercertexp1req); + test_tls_discard_cert(&clientcertexp1req); + + test_tls_discard_cert(&cacertnewreq); + test_tls_discard_cert(&servercertnewreq); + test_tls_discard_cert(&servercertnew1req); + test_tls_discard_cert(&clientcertnew1req); + + test_tls_discard_cert(&cacertrootreq); + test_tls_discard_cert(&cacertlevel1areq); + test_tls_discard_cert(&cacertlevel1breq); + test_tls_discard_cert(&cacertlevel2areq); + test_tls_discard_cert(&servercertlevel3areq); + test_tls_discard_cert(&clientcertlevel2breq); + unlink(WORKDIR "cacertchain-ctx.pem"); + + test_tls_cleanup(KEYFILE); + rmdir(WORKDIR); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +#else /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ + +int +main(void) +{ + return EXIT_SUCCESS; +} + +#endif /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/qemu/tests/test-crypto-tlssession.c b/qemu/tests/test-crypto-tlssession.c new file mode 100644 index 000000000..1a4a066d7 --- /dev/null +++ b/qemu/tests/test-crypto-tlssession.c @@ -0,0 +1,534 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Daniel P. Berrange + */ + +#include "qemu/osdep.h" + +#include "crypto-tls-x509-helpers.h" +#include "crypto/tlscredsx509.h" +#include "crypto/tlssession.h" +#include "qom/object_interfaces.h" +#include "qapi/error.h" +#include "qemu/sockets.h" +#include "qemu/acl.h" + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT + +#define WORKDIR "tests/test-crypto-tlssession-work/" +#define KEYFILE WORKDIR "key-ctx.pem" + +struct QCryptoTLSSessionTestData { + const char *servercacrt; + const char *clientcacrt; + const char *servercrt; + const char *clientcrt; + bool expectServerFail; + bool expectClientFail; + const char *hostname; + const char *const *wildcards; +}; + + +static ssize_t testWrite(const char *buf, size_t len, void *opaque) +{ + int *fd = opaque; + + return write(*fd, buf, len); +} + +static ssize_t testRead(char *buf, size_t len, void *opaque) +{ + int *fd = opaque; + + return read(*fd, buf, len); +} + +static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, + const char *certdir, + Error **errp) +{ + Error *err = NULL; + Object *parent = object_get_objects_root(); + Object *creds = object_new_with_props( + TYPE_QCRYPTO_TLS_CREDS_X509, + parent, + (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "testtlscredsserver" : "testtlscredsclient"), + &err, + "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "server" : "client"), + "dir", certdir, + "verify-peer", "yes", + /* We skip initial sanity checks here because we + * want to make sure that problems are being + * detected at the TLS session validation stage, + * and the test-crypto-tlscreds test already + * validate the sanity check code. + */ + "sanity-check", "no", + NULL + ); + + if (err) { + error_propagate(errp, err); + return NULL; + } + return QCRYPTO_TLS_CREDS(creds); +} + + +/* + * This tests validation checking of peer certificates + * + * This is replicating the checks that are done for an + * active TLS session after handshake completes. To + * simulate that we create our TLS contexts, skipping + * sanity checks. We then get a socketpair, and + * initiate a TLS session across them. Finally do + * do actual cert validation tests + */ +static void test_crypto_tls_session(const void *opaque) +{ + struct QCryptoTLSSessionTestData *data = + (struct QCryptoTLSSessionTestData *)opaque; + QCryptoTLSCreds *clientCreds; + QCryptoTLSCreds *serverCreds; + QCryptoTLSSession *clientSess = NULL; + QCryptoTLSSession *serverSess = NULL; + qemu_acl *acl; + const char * const *wildcards; + int channel[2]; + bool clientShake = false; + bool serverShake = false; + Error *err = NULL; + int ret; + + /* We'll use this for our fake client-server connection */ + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, channel); + g_assert(ret == 0); + + /* + * We have an evil loop to do the handshake in a single + * thread, so we need these non-blocking to avoid deadlock + * of ourselves + */ + qemu_set_nonblock(channel[0]); + qemu_set_nonblock(channel[1]); + +#define CLIENT_CERT_DIR "tests/test-crypto-tlssession-client/" +#define SERVER_CERT_DIR "tests/test-crypto-tlssession-server/" + mkdir(CLIENT_CERT_DIR, 0700); + mkdir(SERVER_CERT_DIR, 0700); + + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); + + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); + + g_assert(link(data->servercacrt, + SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); + g_assert(link(data->servercrt, + SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0); + g_assert(link(KEYFILE, + SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0); + + g_assert(link(data->clientcacrt, + CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); + g_assert(link(data->clientcrt, + CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0); + g_assert(link(KEYFILE, + CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0); + + clientCreds = test_tls_creds_create( + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, + CLIENT_CERT_DIR, + &err); + g_assert(clientCreds != NULL); + + serverCreds = test_tls_creds_create( + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, + SERVER_CERT_DIR, + &err); + g_assert(serverCreds != NULL); + + acl = qemu_acl_init("tlssessionacl"); + qemu_acl_reset(acl); + wildcards = data->wildcards; + while (wildcards && *wildcards) { + qemu_acl_append(acl, 0, *wildcards); + wildcards++; + } + + /* Now the real part of the test, setup the sessions */ + clientSess = qcrypto_tls_session_new( + clientCreds, data->hostname, NULL, + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &err); + serverSess = qcrypto_tls_session_new( + serverCreds, NULL, + data->wildcards ? "tlssessionacl" : NULL, + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &err); + + g_assert(clientSess != NULL); + g_assert(serverSess != NULL); + + /* For handshake to work, we need to set the I/O callbacks + * to read/write over the socketpair + */ + qcrypto_tls_session_set_callbacks(serverSess, + testWrite, testRead, + &channel[0]); + qcrypto_tls_session_set_callbacks(clientSess, + testWrite, testRead, + &channel[1]); + + /* + * Finally we loop around & around doing handshake on each + * session until we get an error, or the handshake completes. + * This relies on the socketpair being nonblocking to avoid + * deadlocking ourselves upon handshake + */ + do { + int rv; + if (!serverShake) { + rv = qcrypto_tls_session_handshake(serverSess, + &err); + g_assert(rv >= 0); + if (qcrypto_tls_session_get_handshake_status(serverSess) == + QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + serverShake = true; + } + } + if (!clientShake) { + rv = qcrypto_tls_session_handshake(clientSess, + &err); + g_assert(rv >= 0); + if (qcrypto_tls_session_get_handshake_status(clientSess) == + QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + clientShake = true; + } + } + } while (!clientShake && !serverShake); + + + /* Finally make sure the server validation does what + * we were expecting + */ + if (qcrypto_tls_session_check_credentials(serverSess, &err) < 0) { + g_assert(data->expectServerFail); + error_free(err); + err = NULL; + } else { + g_assert(!data->expectServerFail); + } + + /* + * And the same for the client validation check + */ + if (qcrypto_tls_session_check_credentials(clientSess, &err) < 0) { + g_assert(data->expectClientFail); + error_free(err); + err = NULL; + } else { + g_assert(!data->expectClientFail); + } + + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); + + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); + + rmdir(CLIENT_CERT_DIR); + rmdir(SERVER_CERT_DIR); + + object_unparent(OBJECT(serverCreds)); + object_unparent(OBJECT(clientCreds)); + + qcrypto_tls_session_free(serverSess); + qcrypto_tls_session_free(clientSess); + + close(channel[0]); + close(channel[1]); +} + + +int main(int argc, char **argv) +{ + int ret; + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); + + mkdir(WORKDIR, 0700); + + test_tls_init(KEYFILE); + +# define TEST_SESS_REG(name, caCrt, \ + serverCrt, clientCrt, \ + expectServerFail, expectClientFail, \ + hostname, wildcards) \ + struct QCryptoTLSSessionTestData name = { \ + caCrt, caCrt, serverCrt, clientCrt, \ + expectServerFail, expectClientFail, \ + hostname, wildcards \ + }; \ + g_test_add_data_func("/qcrypto/tlssession/" # name, \ + &name, test_crypto_tls_session); \ + + +# define TEST_SESS_REG_EXT(name, serverCaCrt, clientCaCrt, \ + serverCrt, clientCrt, \ + expectServerFail, expectClientFail, \ + hostname, wildcards) \ + struct QCryptoTLSSessionTestData name = { \ + serverCaCrt, clientCaCrt, serverCrt, clientCrt, \ + expectServerFail, expectClientFail, \ + hostname, wildcards \ + }; \ + g_test_add_data_func("/qcrypto/tlssession/" # name, \ + &name, test_crypto_tls_session); \ + + /* A perfect CA, perfect client & perfect server */ + + /* Basic:CA:critical */ + TLS_ROOT_REQ(cacertreq, + "UK", "qemu CA", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + + TLS_ROOT_REQ(altcacertreq, + "UK", "qemu CA 1", NULL, NULL, NULL, NULL, + true, true, true, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + + TLS_CERT_REQ(servercertreq, cacertreq, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(clientcertreq, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + + TLS_CERT_REQ(clientcertaltreq, altcacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + + TEST_SESS_REG(basicca, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + false, false, "qemu.org", NULL); + TEST_SESS_REG_EXT(differentca, cacertreq.filename, + altcacertreq.filename, servercertreq.filename, + clientcertaltreq.filename, true, true, "qemu.org", NULL); + + + /* When an altname is set, the CN is ignored, so it must be duplicated + * as an altname for it to match */ + TLS_CERT_REQ(servercertalt1req, cacertreq, + "UK", "qemu.org", "www.qemu.org", "qemu.org", + "192.168.122.1", "fec0::dead:beaf", + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + /* This intentionally doesn't replicate */ + TLS_CERT_REQ(servercertalt2req, cacertreq, + "UK", "qemu.org", "www.qemu.org", "wiki.qemu.org", + "192.168.122.1", "fec0::dead:beaf", + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + + TEST_SESS_REG(altname1, cacertreq.filename, + servercertalt1req.filename, clientcertreq.filename, + false, false, "qemu.org", NULL); + TEST_SESS_REG(altname2, cacertreq.filename, + servercertalt1req.filename, clientcertreq.filename, + false, false, "www.qemu.org", NULL); + TEST_SESS_REG(altname3, cacertreq.filename, + servercertalt1req.filename, clientcertreq.filename, + false, true, "wiki.qemu.org", NULL); + + TEST_SESS_REG(altname4, cacertreq.filename, + servercertalt2req.filename, clientcertreq.filename, + false, true, "qemu.org", NULL); + TEST_SESS_REG(altname5, cacertreq.filename, + servercertalt2req.filename, clientcertreq.filename, + false, false, "www.qemu.org", NULL); + TEST_SESS_REG(altname6, cacertreq.filename, + servercertalt2req.filename, clientcertreq.filename, + false, false, "wiki.qemu.org", NULL); + + const char *const wildcards1[] = { + "C=UK,CN=dogfood", + NULL, + }; + const char *const wildcards2[] = { + "C=UK,CN=qemu", + NULL, + }; + const char *const wildcards3[] = { + "C=UK,CN=dogfood", + "C=UK,CN=qemu", + NULL, + }; + const char *const wildcards4[] = { + "C=UK,CN=qemustuff", + NULL, + }; + const char *const wildcards5[] = { + "C=UK,CN=qemu*", + NULL, + }; + const char *const wildcards6[] = { + "C=UK,CN=*emu*", + NULL, + }; + + TEST_SESS_REG(wildcard1, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + true, false, "qemu.org", wildcards1); + TEST_SESS_REG(wildcard2, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + false, false, "qemu.org", wildcards2); + TEST_SESS_REG(wildcard3, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + false, false, "qemu.org", wildcards3); + TEST_SESS_REG(wildcard4, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + true, false, "qemu.org", wildcards4); + TEST_SESS_REG(wildcard5, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + false, false, "qemu.org", wildcards5); + TEST_SESS_REG(wildcard6, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + false, false, "qemu.org", wildcards6); + + TLS_ROOT_REQ(cacertrootreq, + "UK", "qemu root", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1areq, cacertrootreq, + "UK", "qemu level 1a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1breq, cacertrootreq, + "UK", "qemu level 1b", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel2areq, cacertlevel1areq, + "UK", "qemu level 2a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercertlevel3areq, cacertlevel2areq, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(clientcertlevel2breq, cacertlevel1breq, + "UK", "qemu client level 2b", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + + gnutls_x509_crt_t certchain[] = { + cacertrootreq.crt, + cacertlevel1areq.crt, + cacertlevel1breq.crt, + cacertlevel2areq.crt, + }; + + test_tls_write_cert_chain(WORKDIR "cacertchain-sess.pem", + certchain, + G_N_ELEMENTS(certchain)); + + TEST_SESS_REG(cachain, WORKDIR "cacertchain-sess.pem", + servercertlevel3areq.filename, clientcertlevel2breq.filename, + false, false, "qemu.org", NULL); + + ret = g_test_run(); + + test_tls_discard_cert(&clientcertreq); + test_tls_discard_cert(&clientcertaltreq); + + test_tls_discard_cert(&servercertreq); + test_tls_discard_cert(&servercertalt1req); + test_tls_discard_cert(&servercertalt2req); + + test_tls_discard_cert(&cacertreq); + test_tls_discard_cert(&altcacertreq); + + test_tls_discard_cert(&cacertrootreq); + test_tls_discard_cert(&cacertlevel1areq); + test_tls_discard_cert(&cacertlevel1breq); + test_tls_discard_cert(&cacertlevel2areq); + test_tls_discard_cert(&servercertlevel3areq); + test_tls_discard_cert(&clientcertlevel2breq); + unlink(WORKDIR "cacertchain-sess.pem"); + + test_tls_cleanup(KEYFILE); + rmdir(WORKDIR); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +#else /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ + +int +main(void) +{ + return EXIT_SUCCESS; +} + +#endif /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/qemu/tests/test-crypto-xts.c b/qemu/tests/test-crypto-xts.c new file mode 100644 index 000000000..7f68b063c --- /dev/null +++ b/qemu/tests/test-crypto-xts.c @@ -0,0 +1,423 @@ +/* + * QEMU Crypto XTS cipher mode + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + * This code is originally derived from public domain / WTFPL code in + * LibTomCrypt crytographic library http://libtom.org. The XTS code + * was donated by Elliptic Semiconductor Inc (www.ellipticsemi.com) + * to the LibTom Projects + * + */ + +#include "qemu/osdep.h" +#include "crypto/init.h" +#include "crypto/xts.h" +#include "crypto/aes.h" + +typedef struct { + const char *path; + int keylen; + unsigned char key1[32]; + unsigned char key2[32]; + uint64_t seqnum; + unsigned long PTLEN; + unsigned char PTX[512], CTX[512]; +} QCryptoXTSTestData; + +static const QCryptoXTSTestData test_data[] = { + /* #1 32 byte key, 32 byte PTX */ + { + "/crypto/xts/t-1-key-32-ptx-32", + 32, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + 0, + 32, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x91, 0x7c, 0xf6, 0x9e, 0xbd, 0x68, 0xb2, 0xec, + 0x9b, 0x9f, 0xe9, 0xa3, 0xea, 0xdd, 0xa6, 0x92, + 0xcd, 0x43, 0xd2, 0xf5, 0x95, 0x98, 0xed, 0x85, + 0x8c, 0x02, 0xc2, 0x65, 0x2f, 0xbf, 0x92, 0x2e }, + }, + + /* #2, 32 byte key, 32 byte PTX */ + { + "/crypto/xts/t-2-key-32-ptx-32", + 32, + { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 }, + { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 }, + 0x3333333333LL, + 32, + { 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44 }, + { 0xc4, 0x54, 0x18, 0x5e, 0x6a, 0x16, 0x93, 0x6e, + 0x39, 0x33, 0x40, 0x38, 0xac, 0xef, 0x83, 0x8b, + 0xfb, 0x18, 0x6f, 0xff, 0x74, 0x80, 0xad, 0xc4, + 0x28, 0x93, 0x82, 0xec, 0xd6, 0xd3, 0x94, 0xf0 }, + }, + + /* #5 from xts.7, 32 byte key, 32 byte PTX */ + { + "/crypto/xts/t-5-key-32-ptx-32", + 32, + { 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 }, + { 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 }, + 0x123456789aLL, + 32, + { 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44 }, + { 0xb0, 0x1f, 0x86, 0xf8, 0xed, 0xc1, 0x86, 0x37, + 0x06, 0xfa, 0x8a, 0x42, 0x53, 0xe3, 0x4f, 0x28, + 0xaf, 0x31, 0x9d, 0xe3, 0x83, 0x34, 0x87, 0x0f, + 0x4d, 0xd1, 0xf9, 0x4c, 0xbe, 0x98, 0x32, 0xf1 }, + }, + + /* #4, 32 byte key, 512 byte PTX */ + { + "/crypto/xts/t-4-key-32-ptx-512", + 32, + { 0x27, 0x18, 0x28, 0x18, 0x28, 0x45, 0x90, 0x45, + 0x23, 0x53, 0x60, 0x28, 0x74, 0x71, 0x35, 0x26 }, + { 0x31, 0x41, 0x59, 0x26, 0x53, 0x58, 0x97, 0x93, + 0x23, 0x84, 0x62, 0x64, 0x33, 0x83, 0x27, 0x95 }, + 0, + 512, + { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, + }, + { + 0x27, 0xa7, 0x47, 0x9b, 0xef, 0xa1, 0xd4, 0x76, + 0x48, 0x9f, 0x30, 0x8c, 0xd4, 0xcf, 0xa6, 0xe2, + 0xa9, 0x6e, 0x4b, 0xbe, 0x32, 0x08, 0xff, 0x25, + 0x28, 0x7d, 0xd3, 0x81, 0x96, 0x16, 0xe8, 0x9c, + 0xc7, 0x8c, 0xf7, 0xf5, 0xe5, 0x43, 0x44, 0x5f, + 0x83, 0x33, 0xd8, 0xfa, 0x7f, 0x56, 0x00, 0x00, + 0x05, 0x27, 0x9f, 0xa5, 0xd8, 0xb5, 0xe4, 0xad, + 0x40, 0xe7, 0x36, 0xdd, 0xb4, 0xd3, 0x54, 0x12, + 0x32, 0x80, 0x63, 0xfd, 0x2a, 0xab, 0x53, 0xe5, + 0xea, 0x1e, 0x0a, 0x9f, 0x33, 0x25, 0x00, 0xa5, + 0xdf, 0x94, 0x87, 0xd0, 0x7a, 0x5c, 0x92, 0xcc, + 0x51, 0x2c, 0x88, 0x66, 0xc7, 0xe8, 0x60, 0xce, + 0x93, 0xfd, 0xf1, 0x66, 0xa2, 0x49, 0x12, 0xb4, + 0x22, 0x97, 0x61, 0x46, 0xae, 0x20, 0xce, 0x84, + 0x6b, 0xb7, 0xdc, 0x9b, 0xa9, 0x4a, 0x76, 0x7a, + 0xae, 0xf2, 0x0c, 0x0d, 0x61, 0xad, 0x02, 0x65, + 0x5e, 0xa9, 0x2d, 0xc4, 0xc4, 0xe4, 0x1a, 0x89, + 0x52, 0xc6, 0x51, 0xd3, 0x31, 0x74, 0xbe, 0x51, + 0xa1, 0x0c, 0x42, 0x11, 0x10, 0xe6, 0xd8, 0x15, + 0x88, 0xed, 0xe8, 0x21, 0x03, 0xa2, 0x52, 0xd8, + 0xa7, 0x50, 0xe8, 0x76, 0x8d, 0xef, 0xff, 0xed, + 0x91, 0x22, 0x81, 0x0a, 0xae, 0xb9, 0x9f, 0x91, + 0x72, 0xaf, 0x82, 0xb6, 0x04, 0xdc, 0x4b, 0x8e, + 0x51, 0xbc, 0xb0, 0x82, 0x35, 0xa6, 0xf4, 0x34, + 0x13, 0x32, 0xe4, 0xca, 0x60, 0x48, 0x2a, 0x4b, + 0xa1, 0xa0, 0x3b, 0x3e, 0x65, 0x00, 0x8f, 0xc5, + 0xda, 0x76, 0xb7, 0x0b, 0xf1, 0x69, 0x0d, 0xb4, + 0xea, 0xe2, 0x9c, 0x5f, 0x1b, 0xad, 0xd0, 0x3c, + 0x5c, 0xcf, 0x2a, 0x55, 0xd7, 0x05, 0xdd, 0xcd, + 0x86, 0xd4, 0x49, 0x51, 0x1c, 0xeb, 0x7e, 0xc3, + 0x0b, 0xf1, 0x2b, 0x1f, 0xa3, 0x5b, 0x91, 0x3f, + 0x9f, 0x74, 0x7a, 0x8a, 0xfd, 0x1b, 0x13, 0x0e, + 0x94, 0xbf, 0xf9, 0x4e, 0xff, 0xd0, 0x1a, 0x91, + 0x73, 0x5c, 0xa1, 0x72, 0x6a, 0xcd, 0x0b, 0x19, + 0x7c, 0x4e, 0x5b, 0x03, 0x39, 0x36, 0x97, 0xe1, + 0x26, 0x82, 0x6f, 0xb6, 0xbb, 0xde, 0x8e, 0xcc, + 0x1e, 0x08, 0x29, 0x85, 0x16, 0xe2, 0xc9, 0xed, + 0x03, 0xff, 0x3c, 0x1b, 0x78, 0x60, 0xf6, 0xde, + 0x76, 0xd4, 0xce, 0xcd, 0x94, 0xc8, 0x11, 0x98, + 0x55, 0xef, 0x52, 0x97, 0xca, 0x67, 0xe9, 0xf3, + 0xe7, 0xff, 0x72, 0xb1, 0xe9, 0x97, 0x85, 0xca, + 0x0a, 0x7e, 0x77, 0x20, 0xc5, 0xb3, 0x6d, 0xc6, + 0xd7, 0x2c, 0xac, 0x95, 0x74, 0xc8, 0xcb, 0xbc, + 0x2f, 0x80, 0x1e, 0x23, 0xe5, 0x6f, 0xd3, 0x44, + 0xb0, 0x7f, 0x22, 0x15, 0x4b, 0xeb, 0xa0, 0xf0, + 0x8c, 0xe8, 0x89, 0x1e, 0x64, 0x3e, 0xd9, 0x95, + 0xc9, 0x4d, 0x9a, 0x69, 0xc9, 0xf1, 0xb5, 0xf4, + 0x99, 0x02, 0x7a, 0x78, 0x57, 0x2a, 0xee, 0xbd, + 0x74, 0xd2, 0x0c, 0xc3, 0x98, 0x81, 0xc2, 0x13, + 0xee, 0x77, 0x0b, 0x10, 0x10, 0xe4, 0xbe, 0xa7, + 0x18, 0x84, 0x69, 0x77, 0xae, 0x11, 0x9f, 0x7a, + 0x02, 0x3a, 0xb5, 0x8c, 0xca, 0x0a, 0xd7, 0x52, + 0xaf, 0xe6, 0x56, 0xbb, 0x3c, 0x17, 0x25, 0x6a, + 0x9f, 0x6e, 0x9b, 0xf1, 0x9f, 0xdd, 0x5a, 0x38, + 0xfc, 0x82, 0xbb, 0xe8, 0x72, 0xc5, 0x53, 0x9e, + 0xdb, 0x60, 0x9e, 0xf4, 0xf7, 0x9c, 0x20, 0x3e, + 0xbb, 0x14, 0x0f, 0x2e, 0x58, 0x3c, 0xb2, 0xad, + 0x15, 0xb4, 0xaa, 0x5b, 0x65, 0x50, 0x16, 0xa8, + 0x44, 0x92, 0x77, 0xdb, 0xd4, 0x77, 0xef, 0x2c, + 0x8d, 0x6c, 0x01, 0x7d, 0xb7, 0x38, 0xb1, 0x8d, + 0xeb, 0x4a, 0x42, 0x7d, 0x19, 0x23, 0xce, 0x3f, + 0xf2, 0x62, 0x73, 0x57, 0x79, 0xa4, 0x18, 0xf2, + 0x0a, 0x28, 0x2d, 0xf9, 0x20, 0x14, 0x7b, 0xea, + 0xbe, 0x42, 0x1e, 0xe5, 0x31, 0x9d, 0x05, 0x68, + } + }, + + /* #7, 32 byte key, 17 byte PTX */ + { + "/crypto/xts/t-7-key-32-ptx-17", + 32, + { 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 }, + { 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 }, + 0x123456789aLL, + 17, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }, + { 0x6c, 0x16, 0x25, 0xdb, 0x46, 0x71, 0x52, 0x2d, + 0x3d, 0x75, 0x99, 0x60, 0x1d, 0xe7, 0xca, 0x09, 0xed }, + }, + + /* #15, 32 byte key, 25 byte PTX */ + { + "/crypto/xts/t-15-key-32-ptx-25", + 32, + { 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 }, + { 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 }, + 0x123456789aLL, + 25, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 }, + { 0x8f, 0x4d, 0xcb, 0xad, 0x55, 0x55, 0x8d, 0x7b, + 0x4e, 0x01, 0xd9, 0x37, 0x9c, 0xd4, 0xea, 0x22, + 0xed, 0xbf, 0x9d, 0xac, 0xe4, 0x5d, 0x6f, 0x6a, 0x73 }, + }, + + /* #21, 32 byte key, 31 byte PTX */ + { + "/crypto/xts/t-21-key-32-ptx-31", + 32, + { 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 }, + { 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 }, + 0x123456789aLL, + 31, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e }, + { 0xd0, 0x5b, 0xc0, 0x90, 0xa8, 0xe0, 0x4f, 0x1b, + 0x3d, 0x3e, 0xcd, 0xd5, 0xba, 0xec, 0x0f, 0xd4, + 0xed, 0xbf, 0x9d, 0xac, 0xe4, 0x5d, 0x6f, 0x6a, + 0x73, 0x06, 0xe6, 0x4b, 0xe5, 0xdd, 0x82 }, + }, +}; + +#define STORE64L(x, y) \ + do { \ + (y)[7] = (unsigned char)(((x) >> 56) & 255); \ + (y)[6] = (unsigned char)(((x) >> 48) & 255); \ + (y)[5] = (unsigned char)(((x) >> 40) & 255); \ + (y)[4] = (unsigned char)(((x) >> 32) & 255); \ + (y)[3] = (unsigned char)(((x) >> 24) & 255); \ + (y)[2] = (unsigned char)(((x) >> 16) & 255); \ + (y)[1] = (unsigned char)(((x) >> 8) & 255); \ + (y)[0] = (unsigned char)((x) & 255); \ + } while (0) + +struct TestAES { + AES_KEY enc; + AES_KEY dec; +}; + +static void test_xts_aes_encrypt(const void *ctx, + size_t length, + uint8_t *dst, + const uint8_t *src) +{ + const struct TestAES *aesctx = ctx; + + AES_encrypt(src, dst, &aesctx->enc); +} + + +static void test_xts_aes_decrypt(const void *ctx, + size_t length, + uint8_t *dst, + const uint8_t *src) +{ + const struct TestAES *aesctx = ctx; + + AES_decrypt(src, dst, &aesctx->dec); +} + + +static void test_xts(const void *opaque) +{ + const QCryptoXTSTestData *data = opaque; + unsigned char OUT[512], Torg[16], T[16]; + uint64_t seq; + int j; + unsigned long len; + struct TestAES aesdata; + struct TestAES aestweak; + + for (j = 0; j < 2; j++) { + /* skip the cases where + * the length is smaller than 2*blocklen + * or the length is not a multiple of 32 + */ + if ((j == 1) && ((data->PTLEN < 32) || (data->PTLEN % 32))) { + continue; + } + len = data->PTLEN / 2; + + AES_set_encrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.enc); + AES_set_decrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.dec); + AES_set_encrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.enc); + AES_set_decrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.dec); + + seq = data->seqnum; + STORE64L(seq, Torg); + memset(Torg + 8, 0, 8); + + memcpy(T, Torg, sizeof(T)); + if (j == 0) { + xts_encrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, data->PTLEN, OUT, data->PTX); + } else { + xts_encrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, len, OUT, data->PTX); + xts_encrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, len, &OUT[len], &data->PTX[len]); + } + + g_assert(memcmp(OUT, data->CTX, data->PTLEN) == 0); + + memcpy(T, Torg, sizeof(T)); + if (j == 0) { + xts_decrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, data->PTLEN, OUT, data->CTX); + } else { + xts_decrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, len, OUT, data->CTX); + xts_decrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, len, &OUT[len], &data->CTX[len]); + } + + g_assert(memcmp(OUT, data->PTX, data->PTLEN) == 0); + } +} + + +int main(int argc, char **argv) +{ + size_t i; + + g_test_init(&argc, &argv, NULL); + + g_assert(qcrypto_init(NULL) == 0); + + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + g_test_add_data_func(test_data[i].path, &test_data[i], test_xts); + } + + return g_test_run(); +} diff --git a/qemu/tests/test-cutils.c b/qemu/tests/test-cutils.c index 2a4556d3a..fb8f5b532 100644 --- a/qemu/tests/test-cutils.c +++ b/qemu/tests/test-cutils.c @@ -25,12 +25,10 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include -#include -#include - -#include "qemu-common.h" +#include "qemu/cutils.h" static void test_parse_uint_null(void) { @@ -226,26 +224,1372 @@ static void test_parse_uint_full_correct(void) g_assert_cmpint(i, ==, 123); } -int main(int argc, char **argv) +static void test_qemu_strtol_correct(void) { - g_test_init(&argc, &argv, NULL); + const char *str = "12345 foo"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; - g_test_add_func("/cutils/parse_uint/null", test_parse_uint_null); - g_test_add_func("/cutils/parse_uint/empty", test_parse_uint_empty); - g_test_add_func("/cutils/parse_uint/whitespace", - test_parse_uint_whitespace); - g_test_add_func("/cutils/parse_uint/invalid", test_parse_uint_invalid); - g_test_add_func("/cutils/parse_uint/trailing", test_parse_uint_trailing); - g_test_add_func("/cutils/parse_uint/correct", test_parse_uint_correct); - g_test_add_func("/cutils/parse_uint/octal", test_parse_uint_octal); - g_test_add_func("/cutils/parse_uint/decimal", test_parse_uint_decimal); - g_test_add_func("/cutils/parse_uint/llong_max", test_parse_uint_llong_max); - g_test_add_func("/cutils/parse_uint/overflow", test_parse_uint_overflow); - g_test_add_func("/cutils/parse_uint/negative", test_parse_uint_negative); - g_test_add_func("/cutils/parse_uint_full/trailing", - test_parse_uint_full_trailing); - g_test_add_func("/cutils/parse_uint_full/correct", - test_parse_uint_full_correct); + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 12345); + g_assert(endptr == str + 5); +} + +static void test_qemu_strtol_null(void) +{ + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(NULL, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == NULL); +} + +static void test_qemu_strtol_empty(void) +{ + const char *str = ""; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtol_whitespace(void) +{ + const char *str = " \t "; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtol_invalid(void) +{ + const char *str = " xxxx \t abc"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtol_trailing(void) +{ + const char *str = "123xxx"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + 3); +} + +static void test_qemu_strtol_octal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(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_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtol_decimal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(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_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtol_hex(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(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_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtol_max(void) +{ + const char *str = g_strdup_printf("%ld", LONG_MAX); + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, LONG_MAX); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtol_overflow(void) +{ + const char *str = "99999999999999999999999999999999999999999999"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MAX); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtol_underflow(void) +{ + const char *str = "-99999999999999999999999999999999999999999999"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MIN); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtol_negative(void) +{ + const char *str = " \t -321"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, -321); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtol_full_correct(void) +{ + const char *str = "123"; + long res = 999; + int err; + + err = qemu_strtol(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); +} + +static void test_qemu_strtol_full_null(void) +{ + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(NULL, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == NULL); +} + +static void test_qemu_strtol_full_empty(void) +{ + const char *str = ""; + long res = 999L; + int err; + + err = qemu_strtol(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtol_full_negative(void) +{ + const char *str = " \t -321"; + long res = 999; + int err; + + err = qemu_strtol(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, -321); +} + +static void test_qemu_strtol_full_trailing(void) +{ + const char *str = "123xxx"; + long res; + int err; + + err = qemu_strtol(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtol_full_max(void) +{ + const char *str = g_strdup_printf("%ld", LONG_MAX); + long res; + int err; + + err = qemu_strtol(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, LONG_MAX); +} + +static void test_qemu_strtoul_correct(void) +{ + const char *str = "12345 foo"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 12345); + g_assert(endptr == str + 5); +} + +static void test_qemu_strtoul_null(void) +{ + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(NULL, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == NULL); +} + +static void test_qemu_strtoul_empty(void) +{ + const char *str = ""; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoul_whitespace(void) +{ + const char *str = " \t "; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoul_invalid(void) +{ + const char *str = " xxxx \t abc"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoul_trailing(void) +{ + const char *str = "123xxx"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + 3); +} + +static void test_qemu_strtoul_octal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(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_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoul_decimal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(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_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoul_hex(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(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_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoul_max(void) +{ + const char *str = g_strdup_printf("%lu", ULONG_MAX); + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, ULONG_MAX); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoul_overflow(void) +{ + const char *str = "99999999999999999999999999999999999999999999"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, ULONG_MAX); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoul_underflow(void) +{ + const char *str = "-99999999999999999999999999999999999999999999"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, -1ul); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoul_negative(void) +{ + const char *str = " \t -321"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, -321ul); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoul_full_correct(void) +{ + const char *str = "123"; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); +} + +static void test_qemu_strtoul_full_null(void) +{ + unsigned long res = 999; + int err; + + err = qemu_strtoul(NULL, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoul_full_empty(void) +{ + const char *str = ""; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} +static void test_qemu_strtoul_full_negative(void) +{ + const char *str = " \t -321"; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, NULL, 0, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, -321ul); +} + +static void test_qemu_strtoul_full_trailing(void) +{ + const char *str = "123xxx"; + unsigned long res; + int err; + + err = qemu_strtoul(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoul_full_max(void) +{ + const char *str = g_strdup_printf("%lu", ULONG_MAX); + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, ULONG_MAX); +} + +static void test_qemu_strtoll_correct(void) +{ + const char *str = "12345 foo"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoll(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 12345); + g_assert(endptr == str + 5); +} + +static void test_qemu_strtoll_null(void) +{ + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoll(NULL, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == NULL); +} + +static void test_qemu_strtoll_empty(void) +{ + const char *str = ""; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoll(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoll_whitespace(void) +{ + const char *str = " \t "; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoll(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoll_invalid(void) +{ + const char *str = " xxxx \t abc"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoll(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoll_trailing(void) +{ + const char *str = "123xxx"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoll(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + 3); +} + +static void test_qemu_strtoll_octal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoll(str, &endptr, 8, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); + + endptr = &f; + res = 999; + err = qemu_strtoll(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoll_decimal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoll(str, &endptr, 10, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + strlen(str)); + + str = "123"; + endptr = &f; + res = 999; + err = qemu_strtoll(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoll_hex(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoll(str, &endptr, 16, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); + + str = "0x123"; + endptr = &f; + res = 999; + err = qemu_strtoll(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoll_max(void) +{ + const char *str = g_strdup_printf("%lld", LLONG_MAX); + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoll(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, LLONG_MAX); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoll_overflow(void) +{ + const char *str = "99999999999999999999999999999999999999999999"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoll(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LLONG_MAX); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoll_underflow(void) +{ + const char *str = "-99999999999999999999999999999999999999999999"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoll(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LLONG_MIN); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoll_negative(void) +{ + const char *str = " \t -321"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoll(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, -321); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoll_full_correct(void) +{ + const char *str = "123"; + int64_t res = 999; + int err; + + err = qemu_strtoll(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); +} + +static void test_qemu_strtoll_full_null(void) +{ + int64_t res = 999; + int err; + + err = qemu_strtoll(NULL, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoll_full_empty(void) +{ + const char *str = ""; + int64_t res = 999; + int err; + + err = qemu_strtoll(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoll_full_negative(void) +{ + const char *str = " \t -321"; + int64_t res = 999; + int err; + + err = qemu_strtoll(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, -321); +} + +static void test_qemu_strtoll_full_trailing(void) +{ + const char *str = "123xxx"; + int64_t res = 999; + int err; + + err = qemu_strtoll(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoll_full_max(void) +{ + + const char *str = g_strdup_printf("%lld", LLONG_MAX); + int64_t res; + int err; + + err = qemu_strtoll(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, LLONG_MAX); +} + +static void test_qemu_strtoull_correct(void) +{ + const char *str = "12345 foo"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtoull(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 12345); + g_assert(endptr == str + 5); +} + +static void test_qemu_strtoull_null(void) +{ + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtoull(NULL, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == NULL); +} + +static void test_qemu_strtoull_empty(void) +{ + const char *str = ""; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtoull(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoull_whitespace(void) +{ + const char *str = " \t "; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtoull(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoull_invalid(void) +{ + const char *str = " xxxx \t abc"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtoull(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoull_trailing(void) +{ + const char *str = "123xxx"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtoull(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + 3); +} + +static void test_qemu_strtoull_octal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtoull(str, &endptr, 8, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); + + endptr = &f; + res = 999; + err = qemu_strtoull(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoull_decimal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtoull(str, &endptr, 10, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + strlen(str)); + + str = "123"; + endptr = &f; + res = 999; + err = qemu_strtoull(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoull_hex(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtoull(str, &endptr, 16, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); + + str = "0x123"; + endptr = &f; + res = 999; + err = qemu_strtoull(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoull_max(void) +{ + const char *str = g_strdup_printf("%llu", ULLONG_MAX); + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtoull(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, ULLONG_MAX); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoull_overflow(void) +{ + const char *str = "99999999999999999999999999999999999999999999"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtoull(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, ULLONG_MAX); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoull_underflow(void) +{ + const char *str = "-99999999999999999999999999999999999999999999"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtoull(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, -1); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoull_negative(void) +{ + const char *str = " \t -321"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtoull(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, -321); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoull_full_correct(void) +{ + const char *str = "18446744073709551614"; + uint64_t res = 999; + int err; + + err = qemu_strtoull(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 18446744073709551614LLU); +} + +static void test_qemu_strtoull_full_null(void) +{ + uint64_t res = 999; + int err; + + err = qemu_strtoull(NULL, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoull_full_empty(void) +{ + const char *str = ""; + uint64_t res = 999; + int err; + + err = qemu_strtoull(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoull_full_negative(void) +{ + const char *str = " \t -321"; + uint64_t res = 999; + int err; + + err = qemu_strtoull(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 18446744073709551295LLU); +} + +static void test_qemu_strtoull_full_trailing(void) +{ + const char *str = "18446744073709551614xxxxxx"; + uint64_t res = 999; + int err; + + err = qemu_strtoull(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoull_full_max(void) +{ + const char *str = g_strdup_printf("%lld", ULLONG_MAX); + uint64_t res = 999; + int err; + + err = qemu_strtoull(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, ULLONG_MAX); +} + +static void test_qemu_strtosz_simple(void) +{ + const char *str = "12345M"; + char *endptr = NULL; + int64_t res; + + res = qemu_strtosz(str, &endptr); + g_assert_cmpint(res, ==, 12345 * M_BYTE); + g_assert(endptr == str + 6); + + res = qemu_strtosz(str, NULL); + g_assert_cmpint(res, ==, 12345 * M_BYTE); +} + +static void test_qemu_strtosz_units(void) +{ + const char *none = "1"; + const char *b = "1B"; + const char *k = "1K"; + const char *m = "1M"; + const char *g = "1G"; + const char *t = "1T"; + const char *p = "1P"; + const char *e = "1E"; + int64_t res; + + /* default is M */ + res = qemu_strtosz(none, NULL); + g_assert_cmpint(res, ==, M_BYTE); + + res = qemu_strtosz(b, NULL); + g_assert_cmpint(res, ==, 1); + + res = qemu_strtosz(k, NULL); + g_assert_cmpint(res, ==, K_BYTE); + + res = qemu_strtosz(m, NULL); + g_assert_cmpint(res, ==, M_BYTE); + + res = qemu_strtosz(g, NULL); + g_assert_cmpint(res, ==, G_BYTE); + + res = qemu_strtosz(t, NULL); + g_assert_cmpint(res, ==, T_BYTE); + + res = qemu_strtosz(p, NULL); + g_assert_cmpint(res, ==, P_BYTE); + + res = qemu_strtosz(e, NULL); + g_assert_cmpint(res, ==, E_BYTE); +} + +static void test_qemu_strtosz_float(void) +{ + const char *str = "12.345M"; + int64_t res; + + res = qemu_strtosz(str, NULL); + g_assert_cmpint(res, ==, 12.345 * M_BYTE); +} + +static void test_qemu_strtosz_erange(void) +{ + const char *str = "10E"; + int64_t res; + + res = qemu_strtosz(str, NULL); + g_assert_cmpint(res, ==, -ERANGE); +} + +static void test_qemu_strtosz_suffix_unit(void) +{ + const char *str = "12345"; + int64_t res; + + res = qemu_strtosz_suffix_unit(str, NULL, + QEMU_STRTOSZ_DEFSUFFIX_KB, 1000); + g_assert_cmpint(res, ==, 12345000); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/cutils/parse_uint/null", test_parse_uint_null); + g_test_add_func("/cutils/parse_uint/empty", test_parse_uint_empty); + g_test_add_func("/cutils/parse_uint/whitespace", + test_parse_uint_whitespace); + g_test_add_func("/cutils/parse_uint/invalid", test_parse_uint_invalid); + g_test_add_func("/cutils/parse_uint/trailing", test_parse_uint_trailing); + g_test_add_func("/cutils/parse_uint/correct", test_parse_uint_correct); + g_test_add_func("/cutils/parse_uint/octal", test_parse_uint_octal); + g_test_add_func("/cutils/parse_uint/decimal", test_parse_uint_decimal); + g_test_add_func("/cutils/parse_uint/llong_max", test_parse_uint_llong_max); + g_test_add_func("/cutils/parse_uint/overflow", test_parse_uint_overflow); + g_test_add_func("/cutils/parse_uint/negative", test_parse_uint_negative); + g_test_add_func("/cutils/parse_uint_full/trailing", + test_parse_uint_full_trailing); + g_test_add_func("/cutils/parse_uint_full/correct", + test_parse_uint_full_correct); + + /* qemu_strtol() tests */ + g_test_add_func("/cutils/qemu_strtol/correct", test_qemu_strtol_correct); + g_test_add_func("/cutils/qemu_strtol/null", test_qemu_strtol_null); + g_test_add_func("/cutils/qemu_strtol/empty", test_qemu_strtol_empty); + g_test_add_func("/cutils/qemu_strtol/whitespace", + test_qemu_strtol_whitespace); + g_test_add_func("/cutils/qemu_strtol/invalid", test_qemu_strtol_invalid); + g_test_add_func("/cutils/qemu_strtol/trailing", test_qemu_strtol_trailing); + g_test_add_func("/cutils/qemu_strtol/octal", test_qemu_strtol_octal); + g_test_add_func("/cutils/qemu_strtol/decimal", test_qemu_strtol_decimal); + g_test_add_func("/cutils/qemu_strtol/hex", test_qemu_strtol_hex); + g_test_add_func("/cutils/qemu_strtol/max", test_qemu_strtol_max); + g_test_add_func("/cutils/qemu_strtol/overflow", test_qemu_strtol_overflow); + g_test_add_func("/cutils/qemu_strtol/underflow", + test_qemu_strtol_underflow); + g_test_add_func("/cutils/qemu_strtol/negative", test_qemu_strtol_negative); + g_test_add_func("/cutils/qemu_strtol_full/correct", + test_qemu_strtol_full_correct); + g_test_add_func("/cutils/qemu_strtol_full/null", + test_qemu_strtol_full_null); + g_test_add_func("/cutils/qemu_strtol_full/empty", + test_qemu_strtol_full_empty); + g_test_add_func("/cutils/qemu_strtol_full/negative", + test_qemu_strtol_full_negative); + g_test_add_func("/cutils/qemu_strtol_full/trailing", + test_qemu_strtol_full_trailing); + g_test_add_func("/cutils/qemu_strtol_full/max", + test_qemu_strtol_full_max); + + /* qemu_strtoul() tests */ + g_test_add_func("/cutils/qemu_strtoul/correct", test_qemu_strtoul_correct); + g_test_add_func("/cutils/qemu_strtoul/null", test_qemu_strtoul_null); + g_test_add_func("/cutils/qemu_strtoul/empty", test_qemu_strtoul_empty); + g_test_add_func("/cutils/qemu_strtoul/whitespace", + test_qemu_strtoul_whitespace); + g_test_add_func("/cutils/qemu_strtoul/invalid", test_qemu_strtoul_invalid); + g_test_add_func("/cutils/qemu_strtoul/trailing", + test_qemu_strtoul_trailing); + g_test_add_func("/cutils/qemu_strtoul/octal", test_qemu_strtoul_octal); + g_test_add_func("/cutils/qemu_strtoul/decimal", test_qemu_strtoul_decimal); + g_test_add_func("/cutils/qemu_strtoul/hex", test_qemu_strtoul_hex); + g_test_add_func("/cutils/qemu_strtoul/max", test_qemu_strtoul_max); + g_test_add_func("/cutils/qemu_strtoul/overflow", + test_qemu_strtoul_overflow); + g_test_add_func("/cutils/qemu_strtoul/underflow", + test_qemu_strtoul_underflow); + g_test_add_func("/cutils/qemu_strtoul/negative", + test_qemu_strtoul_negative); + g_test_add_func("/cutils/qemu_strtoul_full/correct", + test_qemu_strtoul_full_correct); + g_test_add_func("/cutils/qemu_strtoul_full/null", + test_qemu_strtoul_full_null); + g_test_add_func("/cutils/qemu_strtoul_full/empty", + test_qemu_strtoul_full_empty); + g_test_add_func("/cutils/qemu_strtoul_full/negative", + test_qemu_strtoul_full_negative); + g_test_add_func("/cutils/qemu_strtoul_full/trailing", + test_qemu_strtoul_full_trailing); + g_test_add_func("/cutils/qemu_strtoul_full/max", + test_qemu_strtoul_full_max); + + /* qemu_strtoll() tests */ + g_test_add_func("/cutils/qemu_strtoll/correct", test_qemu_strtoll_correct); + g_test_add_func("/cutils/qemu_strtoll/null", test_qemu_strtoll_null); + g_test_add_func("/cutils/qemu_strtoll/empty", test_qemu_strtoll_empty); + g_test_add_func("/cutils/qemu_strtoll/whitespace", + test_qemu_strtoll_whitespace); + g_test_add_func("/cutils/qemu_strtoll/invalid", test_qemu_strtoll_invalid); + g_test_add_func("/cutils/qemu_strtoll/trailing", + test_qemu_strtoll_trailing); + g_test_add_func("/cutils/qemu_strtoll/octal", test_qemu_strtoll_octal); + g_test_add_func("/cutils/qemu_strtoll/decimal", test_qemu_strtoll_decimal); + g_test_add_func("/cutils/qemu_strtoll/hex", test_qemu_strtoll_hex); + g_test_add_func("/cutils/qemu_strtoll/max", test_qemu_strtoll_max); + g_test_add_func("/cutils/qemu_strtoll/overflow", + test_qemu_strtoll_overflow); + g_test_add_func("/cutils/qemu_strtoll/underflow", + test_qemu_strtoll_underflow); + g_test_add_func("/cutils/qemu_strtoll/negative", + test_qemu_strtoll_negative); + g_test_add_func("/cutils/qemu_strtoll_full/correct", + test_qemu_strtoll_full_correct); + g_test_add_func("/cutils/qemu_strtoll_full/null", + test_qemu_strtoll_full_null); + g_test_add_func("/cutils/qemu_strtoll_full/empty", + test_qemu_strtoll_full_empty); + g_test_add_func("/cutils/qemu_strtoll_full/negative", + test_qemu_strtoll_full_negative); + g_test_add_func("/cutils/qemu_strtoll_full/trailing", + test_qemu_strtoll_full_trailing); + g_test_add_func("/cutils/qemu_strtoll_full/max", + test_qemu_strtoll_full_max); + + /* qemu_strtoull() tests */ + g_test_add_func("/cutils/qemu_strtoull/correct", + test_qemu_strtoull_correct); + g_test_add_func("/cutils/qemu_strtoull/null", + test_qemu_strtoull_null); + g_test_add_func("/cutils/qemu_strtoull/empty", test_qemu_strtoull_empty); + g_test_add_func("/cutils/qemu_strtoull/whitespace", + test_qemu_strtoull_whitespace); + g_test_add_func("/cutils/qemu_strtoull/invalid", + test_qemu_strtoull_invalid); + g_test_add_func("/cutils/qemu_strtoull/trailing", + test_qemu_strtoull_trailing); + g_test_add_func("/cutils/qemu_strtoull/octal", test_qemu_strtoull_octal); + g_test_add_func("/cutils/qemu_strtoull/decimal", + test_qemu_strtoull_decimal); + g_test_add_func("/cutils/qemu_strtoull/hex", test_qemu_strtoull_hex); + g_test_add_func("/cutils/qemu_strtoull/max", test_qemu_strtoull_max); + g_test_add_func("/cutils/qemu_strtoull/overflow", + test_qemu_strtoull_overflow); + g_test_add_func("/cutils/qemu_strtoull/underflow", + test_qemu_strtoull_underflow); + g_test_add_func("/cutils/qemu_strtoull/negative", + test_qemu_strtoull_negative); + g_test_add_func("/cutils/qemu_strtoull_full/correct", + test_qemu_strtoull_full_correct); + g_test_add_func("/cutils/qemu_strtoull_full/null", + test_qemu_strtoull_full_null); + g_test_add_func("/cutils/qemu_strtoull_full/empty", + test_qemu_strtoull_full_empty); + g_test_add_func("/cutils/qemu_strtoull_full/negative", + test_qemu_strtoull_full_negative); + g_test_add_func("/cutils/qemu_strtoull_full/trailing", + test_qemu_strtoull_full_trailing); + g_test_add_func("/cutils/qemu_strtoull_full/max", + test_qemu_strtoull_full_max); + + g_test_add_func("/cutils/strtosz/simple", + test_qemu_strtosz_simple); + g_test_add_func("/cutils/strtosz/units", + test_qemu_strtosz_units); + g_test_add_func("/cutils/strtosz/float", + test_qemu_strtosz_float); + g_test_add_func("/cutils/strtosz/erange", + test_qemu_strtosz_erange); + g_test_add_func("/cutils/strtosz/suffix-unit", + test_qemu_strtosz_suffix_unit); return g_test_run(); } diff --git a/qemu/tests/test-filter-mirror.c b/qemu/tests/test-filter-mirror.c new file mode 100644 index 000000000..f60bf2adb --- /dev/null +++ b/qemu/tests/test-filter-mirror.c @@ -0,0 +1,93 @@ +/* + * QTest testcase for filter-mirror + * + * Copyright (c) 2016 FUJITSU LIMITED + * Author: Zhang Chen + * + * 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 +#include "libqtest.h" +#include "qemu/iov.h" +#include "qemu/sockets.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" + +static void test_mirror(void) +{ +#ifndef _WIN32 +/* socketpair(PF_UNIX) which does not exist on windows */ + + int send_sock[2], recv_sock; + char *cmdline; + uint32_t ret = 0, len = 0; + char send_buf[] = "Hello! filter-mirror~"; + char sock_path[] = "filter-mirror.XXXXXX"; + char *recv_buf; + uint32_t size = sizeof(send_buf); + size = htonl(size); + + ret = socketpair(PF_UNIX, SOCK_STREAM, 0, send_sock); + g_assert_cmpint(ret, !=, -1); + + ret = mkstemp(sock_path); + g_assert_cmpint(ret, !=, -1); + + cmdline = g_strdup_printf("-netdev socket,id=qtest-bn0,fd=%d " + "-device e1000,netdev=qtest-bn0,id=qtest-e0 " + "-chardev socket,id=mirror0,path=%s,server,nowait " + "-object filter-mirror,id=qtest-f0,netdev=qtest-bn0,queue=tx,outdev=mirror0 " + , send_sock[1], sock_path); + qtest_start(cmdline); + g_free(cmdline); + + recv_sock = unix_connect(sock_path, NULL); + g_assert_cmpint(recv_sock, !=, -1); + + struct iovec iov[] = { + { + .iov_base = &size, + .iov_len = sizeof(size), + }, { + .iov_base = send_buf, + .iov_len = sizeof(send_buf), + }, + }; + + /* send a qmp command to guarantee that 'connected' is setting to true. */ + qmp("{ 'execute' : 'query-status'}"); + ret = iov_send(send_sock[0], iov, 2, 0, sizeof(size) + sizeof(send_buf)); + g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size)); + close(send_sock[0]); + + ret = qemu_recv(recv_sock, &len, sizeof(len), 0); + g_assert_cmpint(ret, ==, sizeof(len)); + len = ntohl(len); + + g_assert_cmpint(len, ==, sizeof(send_buf)); + recv_buf = g_malloc(len); + ret = qemu_recv(recv_sock, recv_buf, len, 0); + g_assert_cmpstr(recv_buf, ==, send_buf); + + g_free(recv_buf); + close(recv_sock); + unlink(sock_path); + +#endif +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/netfilter/mirror", test_mirror); + ret = g_test_run(); + qtest_end(); + + return ret; +} diff --git a/qemu/tests/test-filter-redirector.c b/qemu/tests/test-filter-redirector.c new file mode 100644 index 000000000..b93012cea --- /dev/null +++ b/qemu/tests/test-filter-redirector.c @@ -0,0 +1,221 @@ +/* + * QTest testcase for filter-redirector + * + * Copyright (c) 2016 FUJITSU LIMITED + * Author: Zhang Chen + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + * + * Case 1, tx traffic flow: + * + * qemu side | test side + * | + * +---------+ | +-------+ + * | backend <---------------+ sock0 | + * +----+----+ | +-------+ + * | | + * +----v----+ +-------+ | + * | rd0 +->+chardev| | + * +---------+ +---+---+ | + * | | + * +---------+ | | + * | rd1 <------+ | + * +----+----+ | + * | | + * +----v----+ | +-------+ + * | rd2 +--------------->sock1 | + * +---------+ | +-------+ + * + + * + * -------------------------------------- + * Case 2, rx traffic flow + * qemu side | test side + * | + * +---------+ | +-------+ + * | backend +---------------> sock1 | + * +----^----+ | +-------+ + * | | + * +----+----+ +-------+ | + * | rd0 +<-+chardev| | + * +---------+ +---+---+ | + * ^ | + * +---------+ | | + * | rd1 +------+ | + * +----^----+ | + * | | + * +----+----+ | +-------+ + * | rd2 <---------------+sock0 | + * +---------+ | +-------+ + * + + */ + +#include "qemu/osdep.h" +#include +#include "libqtest.h" +#include "qemu/iov.h" +#include "qemu/sockets.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" + +static void test_redirector_tx(void) +{ +#ifndef _WIN32 +/* socketpair(PF_UNIX) which does not exist on windows */ + + int backend_sock[2], recv_sock; + char *cmdline; + uint32_t ret = 0, len = 0; + char send_buf[] = "Hello!!"; + char sock_path0[] = "filter-redirector0.XXXXXX"; + char sock_path1[] = "filter-redirector1.XXXXXX"; + char *recv_buf; + uint32_t size = sizeof(send_buf); + size = htonl(size); + + ret = socketpair(PF_UNIX, SOCK_STREAM, 0, backend_sock); + g_assert_cmpint(ret, !=, -1); + + ret = mkstemp(sock_path0); + g_assert_cmpint(ret, !=, -1); + ret = mkstemp(sock_path1); + g_assert_cmpint(ret, !=, -1); + + cmdline = g_strdup_printf("-netdev socket,id=qtest-bn0,fd=%d " + "-device rtl8139,netdev=qtest-bn0,id=qtest-e0 " + "-chardev socket,id=redirector0,path=%s,server,nowait " + "-chardev socket,id=redirector1,path=%s,server,nowait " + "-chardev socket,id=redirector2,path=%s,nowait " + "-object filter-redirector,id=qtest-f0,netdev=qtest-bn0," + "queue=tx,outdev=redirector0 " + "-object filter-redirector,id=qtest-f1,netdev=qtest-bn0," + "queue=tx,indev=redirector2 " + "-object filter-redirector,id=qtest-f2,netdev=qtest-bn0," + "queue=tx,outdev=redirector1 " + , backend_sock[1], sock_path0, sock_path1, sock_path0); + qtest_start(cmdline); + g_free(cmdline); + + recv_sock = unix_connect(sock_path1, NULL); + g_assert_cmpint(recv_sock, !=, -1); + + /* send a qmp command to guarantee that 'connected' is setting to true. */ + qmp("{ 'execute' : 'query-status'}"); + + struct iovec iov[] = { + { + .iov_base = &size, + .iov_len = sizeof(size), + }, { + .iov_base = send_buf, + .iov_len = sizeof(send_buf), + }, + }; + + ret = iov_send(backend_sock[0], iov, 2, 0, sizeof(size) + sizeof(send_buf)); + g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size)); + close(backend_sock[0]); + + ret = qemu_recv(recv_sock, &len, sizeof(len), 0); + g_assert_cmpint(ret, ==, sizeof(len)); + len = ntohl(len); + + g_assert_cmpint(len, ==, sizeof(send_buf)); + recv_buf = g_malloc(len); + ret = qemu_recv(recv_sock, recv_buf, len, 0); + g_assert_cmpstr(recv_buf, ==, send_buf); + + g_free(recv_buf); + close(recv_sock); + unlink(sock_path0); + unlink(sock_path1); + qtest_end(); + +#endif +} + +static void test_redirector_rx(void) +{ +#ifndef _WIN32 +/* socketpair(PF_UNIX) which does not exist on windows */ + + int backend_sock[2], send_sock; + char *cmdline; + uint32_t ret = 0, len = 0; + char send_buf[] = "Hello!!"; + char sock_path0[] = "filter-redirector0.XXXXXX"; + char sock_path1[] = "filter-redirector1.XXXXXX"; + char *recv_buf; + uint32_t size = sizeof(send_buf); + size = htonl(size); + + ret = socketpair(PF_UNIX, SOCK_STREAM, 0, backend_sock); + g_assert_cmpint(ret, !=, -1); + + ret = mkstemp(sock_path0); + g_assert_cmpint(ret, !=, -1); + ret = mkstemp(sock_path1); + g_assert_cmpint(ret, !=, -1); + + cmdline = g_strdup_printf("-netdev socket,id=qtest-bn0,fd=%d " + "-device rtl8139,netdev=qtest-bn0,id=qtest-e0 " + "-chardev socket,id=redirector0,path=%s,server,nowait " + "-chardev socket,id=redirector1,path=%s,server,nowait " + "-chardev socket,id=redirector2,path=%s,nowait " + "-object filter-redirector,id=qtest-f0,netdev=qtest-bn0," + "queue=rx,indev=redirector0 " + "-object filter-redirector,id=qtest-f1,netdev=qtest-bn0," + "queue=rx,outdev=redirector2 " + "-object filter-redirector,id=qtest-f2,netdev=qtest-bn0," + "queue=rx,indev=redirector1 " + , backend_sock[1], sock_path0, sock_path1, sock_path0); + qtest_start(cmdline); + g_free(cmdline); + + struct iovec iov[] = { + { + .iov_base = &size, + .iov_len = sizeof(size), + }, { + .iov_base = send_buf, + .iov_len = sizeof(send_buf), + }, + }; + + send_sock = unix_connect(sock_path1, NULL); + g_assert_cmpint(send_sock, !=, -1); + /* send a qmp command to guarantee that 'connected' is setting to true. */ + qmp("{ 'execute' : 'query-status'}"); + + ret = iov_send(send_sock, iov, 2, 0, sizeof(size) + sizeof(send_buf)); + g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size)); + close(send_sock); + + ret = qemu_recv(backend_sock[0], &len, sizeof(len), 0); + g_assert_cmpint(ret, ==, sizeof(len)); + len = ntohl(len); + + g_assert_cmpint(len, ==, sizeof(send_buf)); + recv_buf = g_malloc(len); + ret = qemu_recv(backend_sock[0], recv_buf, len, 0); + g_assert_cmpstr(recv_buf, ==, send_buf); + + g_free(recv_buf); + unlink(sock_path0); + unlink(sock_path1); + qtest_end(); + +#endif +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + qtest_add_func("/netfilter/redirector_tx", test_redirector_tx); + qtest_add_func("/netfilter/redirector_rx", test_redirector_rx); + ret = g_test_run(); + + return ret; +} diff --git a/qemu/tests/test-hbitmap.c b/qemu/tests/test-hbitmap.c index 161eeb496..abe142791 100644 --- a/qemu/tests/test-hbitmap.c +++ b/qemu/tests/test-hbitmap.c @@ -9,10 +9,8 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include -#include -#include #include "qemu/hbitmap.h" #define LOG_BITS_PER_LONG (BITS_PER_LONG == 32 ? 5 : 6) @@ -139,10 +137,8 @@ static void hbitmap_test_teardown(TestHBitmapData *data, hbitmap_free(data->hb); data->hb = NULL; } - if (data->bits) { - g_free(data->bits); - data->bits = NULL; - } + g_free(data->bits); + data->bits = NULL; } /* Set a range in the HBitmap and in the shadow "simple" bitmap. diff --git a/qemu/tests/test-int128.c b/qemu/tests/test-int128.c index 0772ef753..cacf6beac 100644 --- a/qemu/tests/test-int128.c +++ b/qemu/tests/test-int128.c @@ -6,10 +6,9 @@ * */ +#include "qemu/osdep.h" #include -#include #include "qemu/int128.h" -#include "qemu/osdep.h" /* clang doesn't support __noclone__ but it does have a mechanism for * telling us this. We assume that if we don't have __has_attribute() diff --git a/qemu/tests/test-io-channel-buffer.c b/qemu/tests/test-io-channel-buffer.c new file mode 100644 index 000000000..64722a214 --- /dev/null +++ b/qemu/tests/test-io-channel-buffer.c @@ -0,0 +1,51 @@ +/* + * QEMU I/O channel buffer test + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "io/channel-buffer.h" +#include "io-channel-helpers.h" + + +static void test_io_channel_buf(void) +{ + QIOChannelBuffer *buf; + QIOChannelTest *test; + + buf = qio_channel_buffer_new(0); + + test = qio_channel_test_new(); + qio_channel_test_run_writer(test, QIO_CHANNEL(buf)); + buf->offset = 0; + qio_channel_test_run_reader(test, QIO_CHANNEL(buf)); + qio_channel_test_validate(test); + + object_unref(OBJECT(buf)); +} + + +int main(int argc, char **argv) +{ + module_call_init(MODULE_INIT_QOM); + + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/io/channel/buf", test_io_channel_buf); + return g_test_run(); +} diff --git a/qemu/tests/test-io-channel-command.c b/qemu/tests/test-io-channel-command.c new file mode 100644 index 000000000..1d1f461be --- /dev/null +++ b/qemu/tests/test-io-channel-command.c @@ -0,0 +1,131 @@ +/* + * QEMU I/O channel command test + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "io/channel-command.h" +#include "io-channel-helpers.h" +#include "qapi/error.h" + +#ifndef WIN32 +static void test_io_channel_command_fifo(bool async) +{ +#define TEST_FIFO "tests/test-io-channel-command.fifo" + QIOChannel *src, *dst; + QIOChannelTest *test; + char *srcfifo = g_strdup_printf("PIPE:%s,wronly", TEST_FIFO); + char *dstfifo = g_strdup_printf("PIPE:%s,rdonly", TEST_FIFO); + const char *srcargv[] = { + "/bin/socat", "-", srcfifo, NULL, + }; + const char *dstargv[] = { + "/bin/socat", dstfifo, "-", NULL, + }; + + unlink(TEST_FIFO); + if (access("/bin/socat", X_OK) < 0) { + return; /* Pretend success if socat is not present */ + } + if (mkfifo(TEST_FIFO, 0600) < 0) { + abort(); + } + src = QIO_CHANNEL(qio_channel_command_new_spawn(srcargv, + O_WRONLY, + &error_abort)); + dst = QIO_CHANNEL(qio_channel_command_new_spawn(dstargv, + O_RDONLY, + &error_abort)); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, async, src, dst); + qio_channel_test_validate(test); + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); + + g_free(srcfifo); + g_free(dstfifo); + unlink(TEST_FIFO); +} + + +static void test_io_channel_command_fifo_async(void) +{ + test_io_channel_command_fifo(true); +} + +static void test_io_channel_command_fifo_sync(void) +{ + test_io_channel_command_fifo(false); +} + + +static void test_io_channel_command_echo(bool async) +{ + QIOChannel *ioc; + QIOChannelTest *test; + const char *socatargv[] = { + "/bin/socat", "-", "-", NULL, + }; + + if (access("/bin/socat", X_OK) < 0) { + return; /* Pretend success if socat is not present */ + } + + ioc = QIO_CHANNEL(qio_channel_command_new_spawn(socatargv, + O_RDWR, + &error_abort)); + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, async, ioc, ioc); + qio_channel_test_validate(test); + + object_unref(OBJECT(ioc)); +} + + +static void test_io_channel_command_echo_async(void) +{ + test_io_channel_command_echo(true); +} + +static void test_io_channel_command_echo_sync(void) +{ + test_io_channel_command_echo(false); +} +#endif + +int main(int argc, char **argv) +{ + module_call_init(MODULE_INIT_QOM); + + g_test_init(&argc, &argv, NULL); + +#ifndef WIN32 + g_test_add_func("/io/channel/command/fifo/sync", + test_io_channel_command_fifo_sync); + g_test_add_func("/io/channel/command/fifo/async", + test_io_channel_command_fifo_async); + g_test_add_func("/io/channel/command/echo/sync", + test_io_channel_command_echo_sync); + g_test_add_func("/io/channel/command/echo/async", + test_io_channel_command_echo_async); +#endif + + return g_test_run(); +} diff --git a/qemu/tests/test-io-channel-file.c b/qemu/tests/test-io-channel-file.c new file mode 100644 index 000000000..6bfede6bb --- /dev/null +++ b/qemu/tests/test-io-channel-file.c @@ -0,0 +1,123 @@ +/* + * QEMU I/O channel file test + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "io/channel-file.h" +#include "io/channel-util.h" +#include "io-channel-helpers.h" +#include "qapi/error.h" + +static void test_io_channel_file(void) +{ + QIOChannel *src, *dst; + QIOChannelTest *test; + +#define TEST_FILE "tests/test-io-channel-file.txt" + unlink(TEST_FILE); + src = QIO_CHANNEL(qio_channel_file_new_path( + TEST_FILE, + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600, + &error_abort)); + dst = QIO_CHANNEL(qio_channel_file_new_path( + TEST_FILE, + O_RDONLY | O_BINARY, 0, + &error_abort)); + + test = qio_channel_test_new(); + qio_channel_test_run_writer(test, src); + qio_channel_test_run_reader(test, dst); + qio_channel_test_validate(test); + + unlink(TEST_FILE); + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); +} + + +static void test_io_channel_fd(void) +{ + QIOChannel *ioc; + int fd = -1; + +#define TEST_FILE "tests/test-io-channel-file.txt" + fd = open(TEST_FILE, O_CREAT | O_TRUNC | O_WRONLY, 0600); + g_assert_cmpint(fd, >, -1); + + ioc = qio_channel_new_fd(fd, &error_abort); + + g_assert_cmpstr(object_get_typename(OBJECT(ioc)), + ==, + TYPE_QIO_CHANNEL_FILE); + + unlink(TEST_FILE); + object_unref(OBJECT(ioc)); +} + + +#ifndef _WIN32 +static void test_io_channel_pipe(bool async) +{ + QIOChannel *src, *dst; + QIOChannelTest *test; + int fd[2]; + + if (pipe(fd) < 0) { + perror("pipe"); + abort(); + } + + src = QIO_CHANNEL(qio_channel_file_new_fd(fd[1])); + dst = QIO_CHANNEL(qio_channel_file_new_fd(fd[0])); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, async, src, dst); + qio_channel_test_validate(test); + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); +} + + +static void test_io_channel_pipe_async(void) +{ + test_io_channel_pipe(true); +} + +static void test_io_channel_pipe_sync(void) +{ + test_io_channel_pipe(false); +} +#endif /* ! _WIN32 */ + + +int main(int argc, char **argv) +{ + module_call_init(MODULE_INIT_QOM); + + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/io/channel/file", test_io_channel_file); + g_test_add_func("/io/channel/file/fd", test_io_channel_fd); +#ifndef _WIN32 + g_test_add_func("/io/channel/pipe/sync", test_io_channel_pipe_sync); + g_test_add_func("/io/channel/pipe/async", test_io_channel_pipe_async); +#endif + return g_test_run(); +} diff --git a/qemu/tests/test-io-channel-socket.c b/qemu/tests/test-io-channel-socket.c new file mode 100644 index 000000000..855306b8d --- /dev/null +++ b/qemu/tests/test-io-channel-socket.c @@ -0,0 +1,568 @@ +/* + * QEMU I/O channel sockets test + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "io/channel-socket.h" +#include "io/channel-util.h" +#include "io-channel-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) +{ + int buflen = 64 * 1024; + + /* + * Make the socket buffers small so that we see + * the effects of partial reads/writes + */ + setsockopt(((QIOChannelSocket *)src)->fd, + SOL_SOCKET, SO_SNDBUF, + (char *)&buflen, + sizeof(buflen)); + + setsockopt(((QIOChannelSocket *)dst)->fd, + SOL_SOCKET, SO_SNDBUF, + (char *)&buflen, + sizeof(buflen)); +} + + +static void test_io_channel_setup_sync(SocketAddress *listen_addr, + SocketAddress *connect_addr, + QIOChannel **src, + QIOChannel **dst) +{ + QIOChannelSocket *lioc; + + lioc = qio_channel_socket_new(); + qio_channel_socket_listen_sync(lioc, listen_addr, &error_abort); + + if (listen_addr->type == SOCKET_ADDRESS_KIND_INET) { + SocketAddress *laddr = qio_channel_socket_get_local_address( + lioc, &error_abort); + + g_free(connect_addr->u.inet.data->port); + connect_addr->u.inet.data->port = g_strdup(laddr->u.inet.data->port); + + qapi_free_SocketAddress(laddr); + } + + *src = QIO_CHANNEL(qio_channel_socket_new()); + qio_channel_socket_connect_sync( + QIO_CHANNEL_SOCKET(*src), connect_addr, &error_abort); + qio_channel_set_delay(*src, false); + + qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN); + *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort)); + g_assert(*dst); + + test_io_channel_set_socket_bufs(*src, *dst); + + object_unref(OBJECT(lioc)); +} + + +struct TestIOChannelData { + bool err; + GMainLoop *loop; +}; + + +static void test_io_channel_complete(Object *src, + Error *err, + gpointer opaque) +{ + struct TestIOChannelData *data = opaque; + data->err = err != NULL; + g_main_loop_quit(data->loop); +} + + +static void test_io_channel_setup_async(SocketAddress *listen_addr, + SocketAddress *connect_addr, + QIOChannel **src, + QIOChannel **dst) +{ + QIOChannelSocket *lioc; + struct TestIOChannelData data; + + data.loop = g_main_loop_new(g_main_context_default(), + TRUE); + + lioc = qio_channel_socket_new(); + qio_channel_socket_listen_async( + lioc, listen_addr, + test_io_channel_complete, &data, NULL); + + g_main_loop_run(data.loop); + g_main_context_iteration(g_main_context_default(), FALSE); + + g_assert(!data.err); + + if (listen_addr->type == SOCKET_ADDRESS_KIND_INET) { + SocketAddress *laddr = qio_channel_socket_get_local_address( + lioc, &error_abort); + + g_free(connect_addr->u.inet.data->port); + connect_addr->u.inet.data->port = g_strdup(laddr->u.inet.data->port); + + qapi_free_SocketAddress(laddr); + } + + *src = QIO_CHANNEL(qio_channel_socket_new()); + + qio_channel_socket_connect_async( + QIO_CHANNEL_SOCKET(*src), connect_addr, + test_io_channel_complete, &data, NULL); + + g_main_loop_run(data.loop); + g_main_context_iteration(g_main_context_default(), FALSE); + + g_assert(!data.err); + + qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN); + *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort)); + g_assert(*dst); + + qio_channel_set_delay(*src, false); + test_io_channel_set_socket_bufs(*src, *dst); + + object_unref(OBJECT(lioc)); + + g_main_loop_unref(data.loop); +} + + +static void test_io_channel(bool async, + SocketAddress *listen_addr, + SocketAddress *connect_addr, + bool passFD) +{ + QIOChannel *src, *dst; + QIOChannelTest *test; + if (async) { + test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst); + + g_assert(!passFD || + qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); + g_assert(!passFD || + qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, true, src, dst); + qio_channel_test_validate(test); + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); + + test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst); + + g_assert(!passFD || + qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); + g_assert(!passFD || + qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, false, src, dst); + qio_channel_test_validate(test); + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); + } else { + test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst); + + g_assert(!passFD || + qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); + g_assert(!passFD || + qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, true, src, dst); + qio_channel_test_validate(test); + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); + + test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst); + + g_assert(!passFD || + qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); + g_assert(!passFD || + qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, false, src, dst); + qio_channel_test_validate(test); + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); + } +} + + +static void test_io_channel_ipv4(bool async) +{ + SocketAddress *listen_addr = g_new0(SocketAddress, 1); + SocketAddress *connect_addr = g_new0(SocketAddress, 1); + + listen_addr->type = SOCKET_ADDRESS_KIND_INET; + listen_addr->u.inet.data = g_new(InetSocketAddress, 1); + *listen_addr->u.inet.data = (InetSocketAddress) { + .host = g_strdup("127.0.0.1"), + .port = NULL, /* Auto-select */ + }; + + connect_addr->type = SOCKET_ADDRESS_KIND_INET; + connect_addr->u.inet.data = g_new(InetSocketAddress, 1); + *connect_addr->u.inet.data = (InetSocketAddress) { + .host = g_strdup("127.0.0.1"), + .port = NULL, /* Filled in later */ + }; + + test_io_channel(async, listen_addr, connect_addr, false); + + qapi_free_SocketAddress(listen_addr); + qapi_free_SocketAddress(connect_addr); +} + + +static void test_io_channel_ipv4_sync(void) +{ + return test_io_channel_ipv4(false); +} + + +static void test_io_channel_ipv4_async(void) +{ + return test_io_channel_ipv4(true); +} + + +static void test_io_channel_ipv6(bool async) +{ + SocketAddress *listen_addr = g_new0(SocketAddress, 1); + SocketAddress *connect_addr = g_new0(SocketAddress, 1); + + listen_addr->type = SOCKET_ADDRESS_KIND_INET; + listen_addr->u.inet.data = g_new(InetSocketAddress, 1); + *listen_addr->u.inet.data = (InetSocketAddress) { + .host = g_strdup("::1"), + .port = NULL, /* Auto-select */ + }; + + connect_addr->type = SOCKET_ADDRESS_KIND_INET; + connect_addr->u.inet.data = g_new(InetSocketAddress, 1); + *connect_addr->u.inet.data = (InetSocketAddress) { + .host = g_strdup("::1"), + .port = NULL, /* Filled in later */ + }; + + test_io_channel(async, listen_addr, connect_addr, false); + + qapi_free_SocketAddress(listen_addr); + qapi_free_SocketAddress(connect_addr); +} + + +static void test_io_channel_ipv6_sync(void) +{ + return test_io_channel_ipv6(false); +} + + +static void test_io_channel_ipv6_async(void) +{ + return test_io_channel_ipv6(true); +} + + +#ifndef _WIN32 +static void test_io_channel_unix(bool async) +{ + SocketAddress *listen_addr = g_new0(SocketAddress, 1); + SocketAddress *connect_addr = g_new0(SocketAddress, 1); + +#define TEST_SOCKET "test-io-channel-socket.sock" + listen_addr->type = SOCKET_ADDRESS_KIND_UNIX; + listen_addr->u.q_unix.data = g_new0(UnixSocketAddress, 1); + listen_addr->u.q_unix.data->path = g_strdup(TEST_SOCKET); + + connect_addr->type = SOCKET_ADDRESS_KIND_UNIX; + connect_addr->u.q_unix.data = g_new0(UnixSocketAddress, 1); + connect_addr->u.q_unix.data->path = g_strdup(TEST_SOCKET); + + test_io_channel(async, listen_addr, connect_addr, true); + + qapi_free_SocketAddress(listen_addr); + qapi_free_SocketAddress(connect_addr); + unlink(TEST_SOCKET); +} + + +static void test_io_channel_unix_sync(void) +{ + return test_io_channel_unix(false); +} + + +static void test_io_channel_unix_async(void) +{ + return test_io_channel_unix(true); +} + +static void test_io_channel_unix_fd_pass(void) +{ + SocketAddress *listen_addr = g_new0(SocketAddress, 1); + SocketAddress *connect_addr = g_new0(SocketAddress, 1); + QIOChannel *src, *dst; + int testfd; + int fdsend[3]; + int *fdrecv = NULL; + size_t nfdrecv = 0; + size_t i; + char bufsend[12], bufrecv[12]; + struct iovec iosend[1], iorecv[1]; + +#define TEST_SOCKET "test-io-channel-socket.sock" +#define TEST_FILE "test-io-channel-socket.txt" + + testfd = open(TEST_FILE, O_RDWR|O_TRUNC|O_CREAT, 0700); + g_assert(testfd != -1); + fdsend[0] = testfd; + fdsend[1] = testfd; + fdsend[2] = testfd; + + listen_addr->type = SOCKET_ADDRESS_KIND_UNIX; + listen_addr->u.q_unix.data = g_new0(UnixSocketAddress, 1); + listen_addr->u.q_unix.data->path = g_strdup(TEST_SOCKET); + + connect_addr->type = SOCKET_ADDRESS_KIND_UNIX; + connect_addr->u.q_unix.data = g_new0(UnixSocketAddress, 1); + connect_addr->u.q_unix.data->path = g_strdup(TEST_SOCKET); + + test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst); + + memcpy(bufsend, "Hello World", G_N_ELEMENTS(bufsend)); + + iosend[0].iov_base = bufsend; + iosend[0].iov_len = G_N_ELEMENTS(bufsend); + + iorecv[0].iov_base = bufrecv; + iorecv[0].iov_len = G_N_ELEMENTS(bufrecv); + + g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); + g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); + + qio_channel_writev_full(src, + iosend, + G_N_ELEMENTS(iosend), + fdsend, + G_N_ELEMENTS(fdsend), + &error_abort); + + qio_channel_readv_full(dst, + iorecv, + G_N_ELEMENTS(iorecv), + &fdrecv, + &nfdrecv, + &error_abort); + + g_assert(nfdrecv == G_N_ELEMENTS(fdsend)); + /* Each recvd FD should be different from sent FD */ + for (i = 0; i < nfdrecv; i++) { + g_assert_cmpint(fdrecv[i], !=, testfd); + } + /* Each recvd FD should be different from each other */ + g_assert_cmpint(fdrecv[0], !=, fdrecv[1]); + g_assert_cmpint(fdrecv[0], !=, fdrecv[2]); + g_assert_cmpint(fdrecv[1], !=, fdrecv[2]); + + /* Check the I/O buf we sent at the same time matches */ + g_assert(memcmp(bufsend, bufrecv, G_N_ELEMENTS(bufsend)) == 0); + + /* Write some data into the FD we received */ + g_assert(write(fdrecv[0], bufsend, G_N_ELEMENTS(bufsend)) == + G_N_ELEMENTS(bufsend)); + + /* Read data from the original FD and make sure it matches */ + memset(bufrecv, 0, G_N_ELEMENTS(bufrecv)); + g_assert(lseek(testfd, 0, SEEK_SET) == 0); + g_assert(read(testfd, bufrecv, G_N_ELEMENTS(bufrecv)) == + G_N_ELEMENTS(bufrecv)); + g_assert(memcmp(bufsend, bufrecv, G_N_ELEMENTS(bufsend)) == 0); + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); + qapi_free_SocketAddress(listen_addr); + qapi_free_SocketAddress(connect_addr); + unlink(TEST_SOCKET); + unlink(TEST_FILE); + close(testfd); + for (i = 0; i < nfdrecv; i++) { + close(fdrecv[i]); + } + g_free(fdrecv); +} +#endif /* _WIN32 */ + + +static void test_io_channel_ipv4_fd(void) +{ + QIOChannel *ioc; + int fd = -1; + struct sockaddr_in sa = { + .sin_family = AF_INET, + .sin_addr = { + .s_addr = htonl(INADDR_LOOPBACK), + } + /* Leave port unset for auto-assign */ + }; + socklen_t salen = sizeof(sa); + + fd = socket(AF_INET, SOCK_STREAM, 0); + g_assert_cmpint(fd, >, -1); + + g_assert_cmpint(bind(fd, (struct sockaddr *)&sa, salen), ==, 0); + + ioc = qio_channel_new_fd(fd, &error_abort); + + g_assert_cmpstr(object_get_typename(OBJECT(ioc)), + ==, + TYPE_QIO_CHANNEL_SOCKET); + + object_unref(OBJECT(ioc)); +} + + +int main(int argc, char **argv) +{ + bool has_ipv4, has_ipv6; + + module_call_init(MODULE_INIT_QOM); + 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 (check_protocol_support(&has_ipv4, &has_ipv6) < 0) { + return 1; + } + + if (has_ipv4) { + g_test_add_func("/io/channel/socket/ipv4-sync", + test_io_channel_ipv4_sync); + g_test_add_func("/io/channel/socket/ipv4-async", + test_io_channel_ipv4_async); + g_test_add_func("/io/channel/socket/ipv4-fd", + test_io_channel_ipv4_fd); + } + if (has_ipv6) { + g_test_add_func("/io/channel/socket/ipv6-sync", + test_io_channel_ipv6_sync); + g_test_add_func("/io/channel/socket/ipv6-async", + test_io_channel_ipv6_async); + } + +#ifndef _WIN32 + g_test_add_func("/io/channel/socket/unix-sync", + test_io_channel_unix_sync); + g_test_add_func("/io/channel/socket/unix-async", + test_io_channel_unix_async); + g_test_add_func("/io/channel/socket/unix-fd-pass", + test_io_channel_unix_fd_pass); +#endif /* _WIN32 */ + + return g_test_run(); +} diff --git a/qemu/tests/test-io-channel-tls.c b/qemu/tests/test-io-channel-tls.c new file mode 100644 index 000000000..3c361a7be --- /dev/null +++ b/qemu/tests/test-io-channel-tls.c @@ -0,0 +1,340 @@ +/* + * QEMU I/O channel TLS test + * + * Copyright (C) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Daniel P. Berrange + */ + + +#include "qemu/osdep.h" + +#include "crypto-tls-x509-helpers.h" +#include "io/channel-tls.h" +#include "io/channel-socket.h" +#include "io-channel-helpers.h" +#include "crypto/tlscredsx509.h" +#include "qemu/acl.h" +#include "qom/object_interfaces.h" + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT + +#define WORKDIR "tests/test-io-channel-tls-work/" +#define KEYFILE WORKDIR "key-ctx.pem" + +struct QIOChannelTLSTestData { + const char *servercacrt; + const char *clientcacrt; + const char *servercrt; + const char *clientcrt; + bool expectServerFail; + bool expectClientFail; + const char *hostname; + const char *const *wildcards; +}; + +struct QIOChannelTLSHandshakeData { + bool finished; + bool failed; +}; + +static void test_tls_handshake_done(Object *source, + Error *err, + gpointer opaque) +{ + struct QIOChannelTLSHandshakeData *data = opaque; + + data->finished = true; + data->failed = err != NULL; +} + + +static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, + const char *certdir, + Error **errp) +{ + Object *parent = object_get_objects_root(); + Object *creds = object_new_with_props( + TYPE_QCRYPTO_TLS_CREDS_X509, + parent, + (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "testtlscredsserver" : "testtlscredsclient"), + errp, + "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "server" : "client"), + "dir", certdir, + "verify-peer", "yes", + /* We skip initial sanity checks here because we + * want to make sure that problems are being + * detected at the TLS session validation stage, + * and the test-crypto-tlscreds test already + * validate the sanity check code. + */ + "sanity-check", "no", + NULL + ); + + if (*errp) { + return NULL; + } + return QCRYPTO_TLS_CREDS(creds); +} + + +/* + * This tests validation checking of peer certificates + * + * This is replicating the checks that are done for an + * active TLS session after handshake completes. To + * simulate that we create our TLS contexts, skipping + * sanity checks. When then get a socketpair, and + * initiate a TLS session across them. Finally do + * do actual cert validation tests + */ +static void test_io_channel_tls(const void *opaque) +{ + struct QIOChannelTLSTestData *data = + (struct QIOChannelTLSTestData *)opaque; + QCryptoTLSCreds *clientCreds; + QCryptoTLSCreds *serverCreds; + QIOChannelTLS *clientChanTLS; + QIOChannelTLS *serverChanTLS; + QIOChannelSocket *clientChanSock; + QIOChannelSocket *serverChanSock; + qemu_acl *acl; + const char * const *wildcards; + int channel[2]; + struct QIOChannelTLSHandshakeData clientHandshake = { false, false }; + struct QIOChannelTLSHandshakeData serverHandshake = { false, false }; + Error *err = NULL; + QIOChannelTest *test; + GMainContext *mainloop; + + /* We'll use this for our fake client-server connection */ + g_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == 0); + +#define CLIENT_CERT_DIR "tests/test-crypto-tlssession-client/" +#define SERVER_CERT_DIR "tests/test-crypto-tlssession-server/" + mkdir(CLIENT_CERT_DIR, 0700); + mkdir(SERVER_CERT_DIR, 0700); + + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); + + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); + + g_assert(link(data->servercacrt, + SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); + g_assert(link(data->servercrt, + SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0); + g_assert(link(KEYFILE, + SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0); + + g_assert(link(data->clientcacrt, + CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); + g_assert(link(data->clientcrt, + CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0); + g_assert(link(KEYFILE, + CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0); + + clientCreds = test_tls_creds_create( + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, + CLIENT_CERT_DIR, + &err); + g_assert(clientCreds != NULL); + + serverCreds = test_tls_creds_create( + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, + SERVER_CERT_DIR, + &err); + g_assert(serverCreds != NULL); + + acl = qemu_acl_init("channeltlsacl"); + qemu_acl_reset(acl); + wildcards = data->wildcards; + while (wildcards && *wildcards) { + qemu_acl_append(acl, 0, *wildcards); + wildcards++; + } + + clientChanSock = qio_channel_socket_new_fd( + channel[0], &err); + g_assert(clientChanSock != NULL); + serverChanSock = qio_channel_socket_new_fd( + channel[1], &err); + g_assert(serverChanSock != NULL); + + /* + * We have an evil loop to do the handshake in a single + * thread, so we need these non-blocking to avoid deadlock + * of ourselves + */ + qio_channel_set_blocking(QIO_CHANNEL(clientChanSock), false, NULL); + qio_channel_set_blocking(QIO_CHANNEL(serverChanSock), false, NULL); + + /* Now the real part of the test, setup the sessions */ + clientChanTLS = qio_channel_tls_new_client( + QIO_CHANNEL(clientChanSock), clientCreds, + data->hostname, &err); + g_assert(clientChanTLS != NULL); + + serverChanTLS = qio_channel_tls_new_server( + QIO_CHANNEL(serverChanSock), serverCreds, + "channeltlsacl", &err); + g_assert(serverChanTLS != NULL); + + qio_channel_tls_handshake(clientChanTLS, + test_tls_handshake_done, + &clientHandshake, + NULL); + qio_channel_tls_handshake(serverChanTLS, + test_tls_handshake_done, + &serverHandshake, + NULL); + + /* + * Finally we loop around & around doing handshake on each + * session until we get an error, or the handshake completes. + * This relies on the socketpair being nonblocking to avoid + * deadlocking ourselves upon handshake + */ + mainloop = g_main_context_default(); + do { + g_main_context_iteration(mainloop, TRUE); + } while (!clientHandshake.finished && + !serverHandshake.finished); + + g_assert(clientHandshake.failed == data->expectClientFail); + g_assert(serverHandshake.failed == data->expectServerFail); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, false, + QIO_CHANNEL(clientChanTLS), + QIO_CHANNEL(serverChanTLS)); + qio_channel_test_validate(test); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, true, + QIO_CHANNEL(clientChanTLS), + QIO_CHANNEL(serverChanTLS)); + qio_channel_test_validate(test); + + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); + + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); + + rmdir(CLIENT_CERT_DIR); + rmdir(SERVER_CERT_DIR); + + object_unparent(OBJECT(serverCreds)); + object_unparent(OBJECT(clientCreds)); + + object_unref(OBJECT(serverChanTLS)); + object_unref(OBJECT(clientChanTLS)); + + object_unref(OBJECT(serverChanSock)); + object_unref(OBJECT(clientChanSock)); + + close(channel[0]); + close(channel[1]); +} + + +int main(int argc, char **argv) +{ + int ret; + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); + + mkdir(WORKDIR, 0700); + + test_tls_init(KEYFILE); + +# define TEST_CHANNEL(name, caCrt, \ + serverCrt, clientCrt, \ + expectServerFail, expectClientFail, \ + hostname, wildcards) \ + struct QIOChannelTLSTestData name = { \ + caCrt, caCrt, serverCrt, clientCrt, \ + expectServerFail, expectClientFail, \ + hostname, wildcards \ + }; \ + g_test_add_data_func("/qio/channel/tls/" # name, \ + &name, test_io_channel_tls); + + /* A perfect CA, perfect client & perfect server */ + + /* Basic:CA:critical */ + TLS_ROOT_REQ(cacertreq, + "UK", "qemu CA", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercertreq, cacertreq, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(clientcertreq, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + + const char *const wildcards[] = { + "C=UK,CN=qemu*", + NULL, + }; + TEST_CHANNEL(basic, cacertreq.filename, servercertreq.filename, + clientcertreq.filename, false, false, + "qemu.org", wildcards); + + ret = g_test_run(); + + test_tls_discard_cert(&clientcertreq); + test_tls_discard_cert(&servercertreq); + test_tls_discard_cert(&cacertreq); + + test_tls_cleanup(KEYFILE); + rmdir(WORKDIR); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +#else /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ + +int +main(void) +{ + return EXIT_SUCCESS; +} + +#endif /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/qemu/tests/test-io-task.c b/qemu/tests/test-io-task.c new file mode 100644 index 000000000..5a9775086 --- /dev/null +++ b/qemu/tests/test-io-task.c @@ -0,0 +1,270 @@ +/* + * QEMU I/O task tests + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include + +#include "io/task.h" +#include "qapi/error.h" + +#define TYPE_DUMMY "qemu:dummy" + +typedef struct DummyObject DummyObject; +typedef struct DummyObjectClass DummyObjectClass; + +struct DummyObject { + Object parent; +}; + +struct DummyObjectClass { + ObjectClass parent; +}; + +static const TypeInfo dummy_info = { + .parent = TYPE_OBJECT, + .name = TYPE_DUMMY, + .instance_size = sizeof(DummyObject), + .class_size = sizeof(DummyObjectClass), +}; + +struct TestTaskData { + Object *source; + Error *err; + bool freed; +}; + + +static void task_callback(Object *source, + Error *err, + gpointer opaque) +{ + struct TestTaskData *data = opaque; + + data->source = source; + data->err = err; +} + + +static void test_task_complete(void) +{ + QIOTask *task; + Object *obj = object_new(TYPE_DUMMY); + Object *src; + struct TestTaskData data = { NULL, NULL, false }; + + task = qio_task_new(obj, task_callback, &data, NULL); + src = qio_task_get_source(task); + + qio_task_complete(task); + + g_assert(obj == src); + + object_unref(obj); + object_unref(src); + + g_assert(data.source == obj); + g_assert(data.err == NULL); + g_assert(data.freed == false); +} + + +static void task_data_free(gpointer opaque) +{ + struct TestTaskData *data = opaque; + + data->freed = true; +} + + +static void test_task_data_free(void) +{ + QIOTask *task; + Object *obj = object_new(TYPE_DUMMY); + struct TestTaskData data = { NULL, NULL, false }; + + task = qio_task_new(obj, task_callback, &data, task_data_free); + + qio_task_complete(task); + + object_unref(obj); + + g_assert(data.source == obj); + g_assert(data.err == NULL); + g_assert(data.freed == true); +} + + +static void test_task_error(void) +{ + QIOTask *task; + Object *obj = object_new(TYPE_DUMMY); + struct TestTaskData data = { NULL, NULL, false }; + Error *err = NULL; + + task = qio_task_new(obj, task_callback, &data, NULL); + + error_setg(&err, "Some error"); + + qio_task_abort(task, err); + + error_free(err); + object_unref(obj); + + g_assert(data.source == obj); + g_assert(data.err == err); + g_assert(data.freed == false); + +} + + +struct TestThreadWorkerData { + Object *source; + Error *err; + bool fail; + GThread *worker; + GThread *complete; + GMainLoop *loop; +}; + +static int test_task_thread_worker(QIOTask *task, + Error **errp, + gpointer opaque) +{ + struct TestThreadWorkerData *data = opaque; + + data->worker = g_thread_self(); + + if (data->fail) { + error_setg(errp, "Testing fail"); + return -1; + } + + return 0; +} + + +static void test_task_thread_callback(Object *source, + Error *err, + gpointer opaque) +{ + struct TestThreadWorkerData *data = opaque; + + data->source = source; + data->err = err; + + data->complete = g_thread_self(); + + g_main_loop_quit(data->loop); +} + + +static void test_task_thread_complete(void) +{ + QIOTask *task; + Object *obj = object_new(TYPE_DUMMY); + struct TestThreadWorkerData data = { 0 }; + GThread *self; + + data.loop = g_main_loop_new(g_main_context_default(), + TRUE); + + task = qio_task_new(obj, + test_task_thread_callback, + &data, + NULL); + + qio_task_run_in_thread(task, + test_task_thread_worker, + &data, + NULL); + + g_main_loop_run(data.loop); + + g_main_loop_unref(data.loop); + object_unref(obj); + + g_assert(data.source == obj); + g_assert(data.err == NULL); + + self = g_thread_self(); + + /* Make sure the test_task_thread_worker actually got + * run in a different thread */ + g_assert(data.worker != self); + + /* And that the test_task_thread_callback got rnu in + * the main loop thread (ie this one) */ + g_assert(data.complete == self); +} + + +static void test_task_thread_error(void) +{ + QIOTask *task; + Object *obj = object_new(TYPE_DUMMY); + struct TestThreadWorkerData data = { 0 }; + GThread *self; + + data.loop = g_main_loop_new(g_main_context_default(), + TRUE); + data.fail = true; + + task = qio_task_new(obj, + test_task_thread_callback, + &data, + NULL); + + qio_task_run_in_thread(task, + test_task_thread_worker, + &data, + NULL); + + g_main_loop_run(data.loop); + + g_main_loop_unref(data.loop); + object_unref(obj); + + g_assert(data.source == obj); + g_assert(data.err != NULL); + + self = g_thread_self(); + + /* Make sure the test_task_thread_worker actually got + * run in a different thread */ + g_assert(data.worker != self); + + /* And that the test_task_thread_callback got rnu in + * the main loop thread (ie this one) */ + g_assert(data.complete == self); +} + + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + module_call_init(MODULE_INIT_QOM); + type_register_static(&dummy_info); + g_test_add_func("/crypto/task/complete", test_task_complete); + g_test_add_func("/crypto/task/datafree", test_task_data_free); + g_test_add_func("/crypto/task/error", test_task_error); + g_test_add_func("/crypto/task/thread_complete", test_task_thread_complete); + g_test_add_func("/crypto/task/thread_error", test_task_thread_error); + return g_test_run(); +} diff --git a/qemu/tests/test-iov.c b/qemu/tests/test-iov.c index 46e4dddc5..3f25268dd 100644 --- a/qemu/tests/test-iov.c +++ b/qemu/tests/test-iov.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include #include "qemu-common.h" #include "qemu/iov.h" diff --git a/qemu/tests/test-logging.c b/qemu/tests/test-logging.c new file mode 100644 index 000000000..ac8deedc9 --- /dev/null +++ b/qemu/tests/test-logging.c @@ -0,0 +1,141 @@ +/* + * logging unit-tests + * + * Copyright (C) 2016 Linaro Ltd. + * + * Author: Alex Bennée + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include + +#include "qemu-common.h" +#include "include/qemu/log.h" + +static void test_parse_range(void) +{ + qemu_set_dfilter_ranges("0x1000+0x100"); + + g_assert_false(qemu_log_in_addr_range(0xfff)); + g_assert(qemu_log_in_addr_range(0x1000)); + g_assert(qemu_log_in_addr_range(0x1001)); + g_assert(qemu_log_in_addr_range(0x10ff)); + g_assert_false(qemu_log_in_addr_range(0x1100)); + + qemu_set_dfilter_ranges("0x1000-0x100"); + + g_assert_false(qemu_log_in_addr_range(0x1001)); + g_assert(qemu_log_in_addr_range(0x1000)); + g_assert(qemu_log_in_addr_range(0x0f01)); + g_assert_false(qemu_log_in_addr_range(0x0f00)); + + qemu_set_dfilter_ranges("0x1000..0x1100"); + + g_assert_false(qemu_log_in_addr_range(0xfff)); + g_assert(qemu_log_in_addr_range(0x1000)); + g_assert(qemu_log_in_addr_range(0x1100)); + g_assert_false(qemu_log_in_addr_range(0x1101)); + + qemu_set_dfilter_ranges("0x1000..0x1000"); + + g_assert_false(qemu_log_in_addr_range(0xfff)); + g_assert(qemu_log_in_addr_range(0x1000)); + g_assert_false(qemu_log_in_addr_range(0x1001)); + + qemu_set_dfilter_ranges("0x1000+0x100,0x2100-0x100,0x3000..0x3100"); + g_assert(qemu_log_in_addr_range(0x1050)); + g_assert(qemu_log_in_addr_range(0x2050)); + g_assert(qemu_log_in_addr_range(0x3050)); +} + +#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS +static void test_parse_invalid_range_subprocess(void) +{ + qemu_set_dfilter_ranges("0x1000+onehundred"); +} +static void test_parse_invalid_range(void) +{ + g_test_trap_subprocess("/logging/parse_invalid_range/subprocess", 0, 0); + g_test_trap_assert_failed(); + g_test_trap_assert_stdout(""); + g_test_trap_assert_stderr("*Failed to parse range in: 0x1000+onehundred\n"); +} +static void test_parse_zero_range_subprocess(void) +{ + qemu_set_dfilter_ranges("0x1000+0"); +} +static void test_parse_zero_range(void) +{ + g_test_trap_subprocess("/logging/parse_zero_range/subprocess", 0, 0); + g_test_trap_assert_failed(); + g_test_trap_assert_stdout(""); + g_test_trap_assert_stderr("*Failed to parse range in: 0x1000+0\n"); +} + +/* As the only real failure from a bad log filename path spec is + * reporting to the user we have to use the g_test_trap_subprocess + * mechanism and check no errors reported on stderr. + */ +static void test_parse_path_subprocess(void) +{ + /* All these should work without issue */ + qemu_set_log_filename("/tmp/qemu.log"); + qemu_set_log_filename("/tmp/qemu-%d.log"); + qemu_set_log_filename("/tmp/qemu.log.%d"); +} +static void test_parse_path(void) +{ + g_test_trap_subprocess ("/logging/parse_path/subprocess", 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stdout(""); + g_test_trap_assert_stderr(""); +} +static void test_parse_invalid_path_subprocess(void) +{ + qemu_set_log_filename("/tmp/qemu-%d%d.log"); +} +static void test_parse_invalid_path(void) +{ + g_test_trap_subprocess ("/logging/parse_invalid_path/subprocess", 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stdout(""); + g_test_trap_assert_stderr("Bad logfile format: /tmp/qemu-%d%d.log\n"); +} +#endif /* CONFIG_HAS_GLIB_SUBPROCESS_TESTS */ + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/logging/parse_range", test_parse_range); +#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS + g_test_add_func("/logging/parse_invalid_range/subprocess", test_parse_invalid_range_subprocess); + g_test_add_func("/logging/parse_invalid_range", test_parse_invalid_range); + g_test_add_func("/logging/parse_zero_range/subprocess", test_parse_zero_range_subprocess); + g_test_add_func("/logging/parse_zero_range", test_parse_zero_range); + g_test_add_func("/logging/parse_path", test_parse_path); + g_test_add_func("/logging/parse_path/subprocess", test_parse_path_subprocess); + g_test_add_func("/logging/parse_invalid_path", test_parse_invalid_path); + g_test_add_func("/logging/parse_invalid_path/subprocess", test_parse_invalid_path_subprocess); +#endif + + return g_test_run(); +} diff --git a/qemu/tests/test-mul64.c b/qemu/tests/test-mul64.c index a0a17f777..1282ec5a2 100644 --- a/qemu/tests/test-mul64.c +++ b/qemu/tests/test-mul64.c @@ -6,10 +6,9 @@ * */ +#include "qemu/osdep.h" #include -#include #include "qemu/host-utils.h" -#include "qemu/osdep.h" typedef struct { diff --git a/qemu/tests/test-netfilter.c b/qemu/tests/test-netfilter.c new file mode 100644 index 000000000..7d105c323 --- /dev/null +++ b/qemu/tests/test-netfilter.c @@ -0,0 +1,201 @@ +/* + * QTest testcase for netfilter + * + * Copyright (c) 2015 FUJITSU LIMITED + * Author: Yang Hongyang + * + * 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 +#include "libqtest.h" + +/* add a netfilter to a netdev and then remove it */ +static void add_one_netfilter(void) +{ + QDict *response; + + response = qmp("{'execute': 'object-add'," + " 'arguments': {" + " 'qom-type': 'filter-buffer'," + " 'id': 'qtest-f0'," + " 'props': {" + " 'netdev': 'qtest-bn0'," + " 'queue': 'rx'," + " 'interval': 1000" + "}}}"); + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + response = qmp("{'execute': 'object-del'," + " 'arguments': {" + " 'id': 'qtest-f0'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); +} + +/* add a netfilter to a netdev and then remove the netdev */ +static void remove_netdev_with_one_netfilter(void) +{ + QDict *response; + + response = qmp("{'execute': 'object-add'," + " 'arguments': {" + " 'qom-type': 'filter-buffer'," + " 'id': 'qtest-f0'," + " 'props': {" + " 'netdev': 'qtest-bn0'," + " 'queue': 'rx'," + " 'interval': 1000" + "}}}"); + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + response = qmp("{'execute': 'netdev_del'," + " 'arguments': {" + " 'id': 'qtest-bn0'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + /* add back the netdev */ + response = qmp("{'execute': 'netdev_add'," + " 'arguments': {" + " 'type': 'user'," + " 'id': 'qtest-bn0'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); +} + +/* add multi(2) netfilters to a netdev and then remove them */ +static void add_multi_netfilter(void) +{ + QDict *response; + + response = qmp("{'execute': 'object-add'," + " 'arguments': {" + " 'qom-type': 'filter-buffer'," + " 'id': 'qtest-f0'," + " 'props': {" + " 'netdev': 'qtest-bn0'," + " 'queue': 'rx'," + " 'interval': 1000" + "}}}"); + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + response = qmp("{'execute': 'object-add'," + " 'arguments': {" + " 'qom-type': 'filter-buffer'," + " 'id': 'qtest-f1'," + " 'props': {" + " 'netdev': 'qtest-bn0'," + " 'queue': 'rx'," + " 'interval': 1000" + "}}}"); + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + response = qmp("{'execute': 'object-del'," + " 'arguments': {" + " 'id': 'qtest-f0'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + response = qmp("{'execute': 'object-del'," + " 'arguments': {" + " 'id': 'qtest-f1'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); +} + +/* add multi(2) netfilters to a netdev and then remove the netdev */ +static void remove_netdev_with_multi_netfilter(void) +{ + QDict *response; + + response = qmp("{'execute': 'object-add'," + " 'arguments': {" + " 'qom-type': 'filter-buffer'," + " 'id': 'qtest-f0'," + " 'props': {" + " 'netdev': 'qtest-bn0'," + " 'queue': 'rx'," + " 'interval': 1000" + "}}}"); + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + response = qmp("{'execute': 'object-add'," + " 'arguments': {" + " 'qom-type': 'filter-buffer'," + " 'id': 'qtest-f1'," + " 'props': {" + " 'netdev': 'qtest-bn0'," + " 'queue': 'rx'," + " 'interval': 1000" + "}}}"); + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + response = qmp("{'execute': 'netdev_del'," + " 'arguments': {" + " 'id': 'qtest-bn0'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + /* add back the netdev */ + response = qmp("{'execute': 'netdev_add'," + " 'arguments': {" + " 'type': 'user'," + " 'id': 'qtest-bn0'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + qtest_add_func("/netfilter/addremove_one", add_one_netfilter); + qtest_add_func("/netfilter/remove_netdev_one", + remove_netdev_with_one_netfilter); + qtest_add_func("/netfilter/addremove_multi", add_multi_netfilter); + qtest_add_func("/netfilter/remove_netdev_multi", + remove_netdev_with_multi_netfilter); + + qtest_start("-netdev user,id=qtest-bn0 -device e1000,netdev=qtest-bn0"); + ret = g_test_run(); + + qtest_end(); + + return ret; +} diff --git a/qemu/tests/test-opts-visitor.c b/qemu/tests/test-opts-visitor.c index 1c753d982..008e67738 100644 --- a/qemu/tests/test-opts-visitor.c +++ b/qemu/tests/test-opts-visitor.c @@ -10,13 +10,14 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include #include "qemu/config-file.h" /* qemu_add_opts() */ #include "qemu/option.h" /* qemu_opts_parse() */ +#include "qapi/error.h" #include "qapi/opts-visitor.h" /* opts_visitor_new() */ #include "test-qapi-visit.h" /* visit_type_UserDefOptions() */ -#include "qapi/dealloc-visitor.h" /* qapi_dealloc_visitor_new() */ static QemuOptsList userdef_opts = { .name = "userdef", @@ -44,7 +45,7 @@ setup_fixture(OptsVisitorFixture *f, gconstpointer test_data) g_assert(opts != NULL); ov = opts_visitor_new(opts); - visit_type_UserDefOptions(opts_get_visitor(ov), &f->userdef, NULL, + visit_type_UserDefOptions(opts_get_visitor(ov), NULL, &f->userdef, &f->err); opts_visitor_cleanup(ov); qemu_opts_del(opts); @@ -54,14 +55,7 @@ setup_fixture(OptsVisitorFixture *f, gconstpointer test_data) static void teardown_fixture(OptsVisitorFixture *f, gconstpointer test_data) { - if (f->userdef != NULL) { - QapiDeallocVisitor *dv; - - dv = qapi_dealloc_visitor_new(); - visit_type_UserDefOptions(qapi_dealloc_get_visitor(dv), &f->userdef, - NULL, NULL); - qapi_dealloc_visitor_cleanup(dv); - } + qapi_free_UserDefOptions(f->userdef); error_free(f->err); } diff --git a/qemu/tests/test-qdev-global-props.c b/qemu/tests/test-qdev-global-props.c index 0be98355c..f0cc31e11 100644 --- a/qemu/tests/test-qdev-global-props.c +++ b/qemu/tests/test-qdev-global-props.c @@ -22,8 +22,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include -#include #include "hw/qdev.h" #include "qom/object.h" @@ -116,26 +116,20 @@ static void test_static_globalprop(void) #define TYPE_UNUSED_HOTPLUG "hotplug-type" #define TYPE_UNUSED_NOHOTPLUG "nohotplug-type" -static void prop1_accessor(Object *obj, - Visitor *v, - void *opaque, - const char *name, - Error **errp) +static void prop1_accessor(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { MyType *mt = DYNAMIC_TYPE(obj); - visit_type_uint32(v, &mt->prop1, name, errp); + visit_type_uint32(v, name, &mt->prop1, errp); } -static void prop2_accessor(Object *obj, - Visitor *v, - void *opaque, - const char *name, - Error **errp) +static void prop2_accessor(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { MyType *mt = DYNAMIC_TYPE(obj); - visit_type_uint32(v, &mt->prop2, name, errp); + visit_type_uint32(v, name, &mt->prop2, errp); } static void dynamic_instance_init(Object *obj) diff --git a/qemu/tests/test-qemu-opts.c b/qemu/tests/test-qemu-opts.c index 0c1136d1b..32abed5ea 100644 --- a/qemu/tests/test-qemu-opts.c +++ b/qemu/tests/test-qemu-opts.c @@ -7,12 +7,12 @@ * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include "qapi/error.h" #include "qapi/qmp/qstring.h" #include "qemu/config-file.h" #include -#include static QemuOptsList opts_list_01 = { .name = "opts_list_01", diff --git a/qemu/tests/test-qga.c b/qemu/tests/test-qga.c new file mode 100644 index 000000000..72a89dec2 --- /dev/null +++ b/qemu/tests/test-qga.c @@ -0,0 +1,869 @@ +#include "qemu/osdep.h" +#include +#include +#include +#include +#include + +#include "libqtest.h" + +typedef struct { + char *test_dir; + GMainLoop *loop; + int fd; + GPid pid; +} TestFixture; + +static int connect_qga(char *path) +{ + int s, ret, len, i = 0; + struct sockaddr_un remote; + + s = socket(AF_UNIX, SOCK_STREAM, 0); + g_assert(s != -1); + + remote.sun_family = AF_UNIX; + do { + strcpy(remote.sun_path, path); + len = strlen(remote.sun_path) + sizeof(remote.sun_family); + ret = connect(s, (struct sockaddr *)&remote, len); + if (ret == -1) { + g_usleep(G_USEC_PER_SEC); + } + if (i++ == 10) { + return -1; + } + } while (ret == -1); + + return s; +} + +static void qga_watch(GPid pid, gint status, gpointer user_data) +{ + TestFixture *fixture = user_data; + + g_assert_cmpint(status, ==, 0); + g_main_loop_quit(fixture->loop); +} + +static void +fixture_setup(TestFixture *fixture, gconstpointer data) +{ + const gchar *extra_arg = data; + GError *error = NULL; + gchar *cwd, *path, *cmd, **argv = NULL; + + fixture->loop = g_main_loop_new(NULL, FALSE); + + fixture->test_dir = g_strdup("/tmp/qgatest.XXXXXX"); + g_assert_nonnull(mkdtemp(fixture->test_dir)); + + path = g_build_filename(fixture->test_dir, "sock", NULL); + cwd = g_get_current_dir(); + cmd = g_strdup_printf("%s%cqemu-ga -m unix-listen -t %s -p %s %s %s", + cwd, G_DIR_SEPARATOR, + fixture->test_dir, path, + getenv("QTEST_LOG") ? "-v" : "", + extra_arg ?: ""); + g_shell_parse_argv(cmd, NULL, &argv, &error); + g_assert_no_error(error); + + g_spawn_async(fixture->test_dir, argv, NULL, + G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, + NULL, NULL, &fixture->pid, &error); + g_assert_no_error(error); + + g_child_watch_add(fixture->pid, qga_watch, fixture); + + fixture->fd = connect_qga(path); + g_assert_cmpint(fixture->fd, !=, -1); + + g_strfreev(argv); + g_free(cmd); + g_free(cwd); + g_free(path); +} + +static void +fixture_tear_down(TestFixture *fixture, gconstpointer data) +{ + gchar *tmp; + + kill(fixture->pid, SIGTERM); + + g_main_loop_run(fixture->loop); + g_main_loop_unref(fixture->loop); + + g_spawn_close_pid(fixture->pid); + + tmp = g_build_filename(fixture->test_dir, "foo", NULL); + g_unlink(tmp); + g_free(tmp); + + tmp = g_build_filename(fixture->test_dir, "qga.state", NULL); + g_unlink(tmp); + g_free(tmp); + + tmp = g_build_filename(fixture->test_dir, "sock", NULL); + g_unlink(tmp); + g_free(tmp); + + g_rmdir(fixture->test_dir); + g_free(fixture->test_dir); +} + +static void qmp_assertion_message_error(const char *domain, + const char *file, + int line, + const char *func, + const char *expr, + QDict *dict) +{ + const char *class, *desc; + char *s; + QDict *error; + + error = qdict_get_qdict(dict, "error"); + class = qdict_get_try_str(error, "class"); + desc = qdict_get_try_str(error, "desc"); + + s = g_strdup_printf("assertion failed %s: %s %s", expr, class, desc); + g_assertion_message(domain, file, line, func, s); + g_free(s); +} + +#define qmp_assert_no_error(err) do { \ + if (qdict_haskey(err, "error")) { \ + qmp_assertion_message_error(G_LOG_DOMAIN, __FILE__, __LINE__, \ + G_STRFUNC, #err, err); \ + } \ +} while (0) + +static void test_qga_sync_delimited(gconstpointer fix) +{ + const TestFixture *fixture = fix; + guint32 v, r = g_random_int(); + unsigned char c; + QDict *ret; + gchar *cmd; + + cmd = g_strdup_printf("%c{'execute': 'guest-sync-delimited'," + " 'arguments': {'id': %u } }", 0xff, r); + qmp_fd_send(fixture->fd, cmd); + g_free(cmd); + + v = read(fixture->fd, &c, 1); + g_assert_cmpint(v, ==, 1); + g_assert_cmpint(c, ==, 0xff); + + ret = qmp_fd_receive(fixture->fd); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + v = qdict_get_int(ret, "return"); + g_assert_cmpint(r, ==, v); + + QDECREF(ret); +} + +static void test_qga_sync(gconstpointer fix) +{ + const TestFixture *fixture = fix; + guint32 v, r = g_random_int(); + QDict *ret; + gchar *cmd; + + cmd = g_strdup_printf("%c{'execute': 'guest-sync'," + " 'arguments': {'id': %u } }", 0xff, r); + ret = qmp_fd(fixture->fd, cmd); + g_free(cmd); + + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + v = qdict_get_int(ret, "return"); + g_assert_cmpint(r, ==, v); + + QDECREF(ret); +} + +static void test_qga_ping(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + QDECREF(ret); +} + +static void test_qga_invalid_cmd(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *error; + const gchar *class, *desc; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-invalid-cmd'}"); + g_assert_nonnull(ret); + + error = qdict_get_qdict(ret, "error"); + class = qdict_get_try_str(error, "class"); + desc = qdict_get_try_str(error, "desc"); + + g_assert_cmpstr(class, ==, "CommandNotFound"); + g_assert_cmpint(strlen(desc), >, 0); + + QDECREF(ret); +} + +static void test_qga_info(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *val; + const gchar *version; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-info'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + val = qdict_get_qdict(ret, "return"); + version = qdict_get_try_str(val, "version"); + g_assert_cmpstr(version, ==, QEMU_VERSION); + + QDECREF(ret); +} + +static void test_qga_get_vcpus(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + QList *list; + const QListEntry *entry; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-vcpus'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + /* 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")); + + QDECREF(ret); +} + +static void test_qga_get_fsinfo(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + QList *list; + const QListEntry *entry; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-fsinfo'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + /* sanity-check the response if there are any filesystems */ + 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")); + } + + QDECREF(ret); +} + +static void test_qga_get_memory_block_info(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *val; + int64_t size; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-block-info'}"); + g_assert_nonnull(ret); + + /* some systems might not expose memory block info in sysfs */ + if (!qdict_haskey(ret, "error")) { + /* check there is at least some memory */ + val = qdict_get_qdict(ret, "return"); + size = qdict_get_int(val, "size"); + g_assert_cmpint(size, >, 0); + } + + QDECREF(ret); +} + +static void test_qga_get_memory_blocks(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + QList *list; + const QListEntry *entry; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-blocks'}"); + g_assert_nonnull(ret); + + /* some systems might not expose memory block info in sysfs */ + if (!qdict_haskey(ret, "error")) { + list = qdict_get_qlist(ret, "return"); + 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")); + } + } + + QDECREF(ret); +} + +static void test_qga_network_get_interfaces(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + QList *list; + const QListEntry *entry; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-network-get-interfaces'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + /* 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")); + + QDECREF(ret); +} + +static void test_qga_file_ops(gconstpointer fix) +{ + const TestFixture *fixture = fix; + const unsigned char helloworld[] = "Hello World!\n"; + const char *b64; + gchar *cmd, *path, *enc; + unsigned char *dec; + QDict *ret, *val; + int64_t id, eof; + gsize count; + FILE *f; + char tmp[100]; + + /* open */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," + " 'arguments': { 'path': 'foo', 'mode': 'w+' } }"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + id = qdict_get_int(ret, "return"); + QDECREF(ret); + + enc = g_base64_encode(helloworld, sizeof(helloworld)); + /* write */ + cmd = g_strdup_printf("{'execute': 'guest-file-write'," + " 'arguments': { 'handle': %" PRId64 "," + " 'buf-b64': '%s' } }", id, enc); + ret = qmp_fd(fixture->fd, cmd); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "count"); + eof = qdict_get_bool(val, "eof"); + g_assert_cmpint(count, ==, sizeof(helloworld)); + g_assert_cmpint(eof, ==, 0); + QDECREF(ret); + g_free(cmd); + + /* flush */ + cmd = g_strdup_printf("{'execute': 'guest-file-flush'," + " 'arguments': {'handle': %" PRId64 "} }", + id); + ret = qmp_fd(fixture->fd, cmd); + QDECREF(ret); + g_free(cmd); + + /* close */ + cmd = g_strdup_printf("{'execute': 'guest-file-close'," + " 'arguments': {'handle': %" PRId64 "} }", + id); + ret = qmp_fd(fixture->fd, cmd); + QDECREF(ret); + g_free(cmd); + + /* check content */ + path = g_build_filename(fixture->test_dir, "foo", NULL); + f = fopen(path, "r"); + g_assert_nonnull(f); + count = fread(tmp, 1, sizeof(tmp), f); + g_assert_cmpint(count, ==, sizeof(helloworld)); + tmp[count] = 0; + g_assert_cmpstr(tmp, ==, (char *)helloworld); + fclose(f); + + /* open */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," + " 'arguments': { 'path': 'foo', 'mode': 'r' } }"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + id = qdict_get_int(ret, "return"); + QDECREF(ret); + + /* read */ + cmd = g_strdup_printf("{'execute': 'guest-file-read'," + " 'arguments': { 'handle': %" PRId64 "} }", + id); + ret = qmp_fd(fixture->fd, cmd); + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "count"); + eof = qdict_get_bool(val, "eof"); + b64 = qdict_get_str(val, "buf-b64"); + g_assert_cmpint(count, ==, sizeof(helloworld)); + g_assert(eof); + g_assert_cmpstr(b64, ==, enc); + + QDECREF(ret); + g_free(cmd); + g_free(enc); + + /* read eof */ + cmd = g_strdup_printf("{'execute': 'guest-file-read'," + " 'arguments': { 'handle': %" PRId64 "} }", + id); + ret = qmp_fd(fixture->fd, cmd); + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "count"); + eof = qdict_get_bool(val, "eof"); + b64 = qdict_get_str(val, "buf-b64"); + g_assert_cmpint(count, ==, 0); + g_assert(eof); + g_assert_cmpstr(b64, ==, ""); + QDECREF(ret); + g_free(cmd); + + /* seek */ + cmd = g_strdup_printf("{'execute': 'guest-file-seek'," + " 'arguments': { 'handle': %" PRId64 ", " + " 'offset': %d, 'whence': '%s' } }", + id, 6, "set"); + ret = qmp_fd(fixture->fd, cmd); + qmp_assert_no_error(ret); + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "position"); + eof = qdict_get_bool(val, "eof"); + g_assert_cmpint(count, ==, 6); + g_assert(!eof); + QDECREF(ret); + g_free(cmd); + + /* partial read */ + cmd = g_strdup_printf("{'execute': 'guest-file-read'," + " 'arguments': { 'handle': %" PRId64 "} }", + id); + ret = qmp_fd(fixture->fd, cmd); + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "count"); + eof = qdict_get_bool(val, "eof"); + b64 = qdict_get_str(val, "buf-b64"); + g_assert_cmpint(count, ==, sizeof(helloworld) - 6); + g_assert(eof); + dec = g_base64_decode(b64, &count); + g_assert_cmpint(count, ==, sizeof(helloworld) - 6); + g_assert_cmpmem(dec, count, helloworld + 6, sizeof(helloworld) - 6); + g_free(dec); + + QDECREF(ret); + g_free(cmd); + + /* close */ + cmd = g_strdup_printf("{'execute': 'guest-file-close'," + " 'arguments': {'handle': %" PRId64 "} }", + id); + ret = qmp_fd(fixture->fd, cmd); + QDECREF(ret); + g_free(cmd); +} + +static void test_qga_file_write_read(gconstpointer fix) +{ + const TestFixture *fixture = fix; + const unsigned char helloworld[] = "Hello World!\n"; + const char *b64; + gchar *cmd, *enc; + QDict *ret, *val; + int64_t id, eof; + gsize count; + + /* open */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," + " 'arguments': { 'path': 'foo', 'mode': 'w+' } }"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + id = qdict_get_int(ret, "return"); + QDECREF(ret); + + enc = g_base64_encode(helloworld, sizeof(helloworld)); + /* write */ + cmd = g_strdup_printf("{'execute': 'guest-file-write'," + " 'arguments': { 'handle': %" PRId64 "," + " 'buf-b64': '%s' } }", id, enc); + ret = qmp_fd(fixture->fd, cmd); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "count"); + eof = qdict_get_bool(val, "eof"); + g_assert_cmpint(count, ==, sizeof(helloworld)); + g_assert_cmpint(eof, ==, 0); + QDECREF(ret); + g_free(cmd); + + /* read (check implicit flush) */ + cmd = g_strdup_printf("{'execute': 'guest-file-read'," + " 'arguments': { 'handle': %" PRId64 "} }", + id); + ret = qmp_fd(fixture->fd, cmd); + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "count"); + eof = qdict_get_bool(val, "eof"); + b64 = qdict_get_str(val, "buf-b64"); + g_assert_cmpint(count, ==, 0); + g_assert(eof); + g_assert_cmpstr(b64, ==, ""); + QDECREF(ret); + g_free(cmd); + + /* seek to 0 */ + cmd = g_strdup_printf("{'execute': 'guest-file-seek'," + " 'arguments': { 'handle': %" PRId64 ", " + " 'offset': %d, 'whence': '%s' } }", + id, 0, "set"); + ret = qmp_fd(fixture->fd, cmd); + qmp_assert_no_error(ret); + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "position"); + eof = qdict_get_bool(val, "eof"); + g_assert_cmpint(count, ==, 0); + g_assert(!eof); + QDECREF(ret); + g_free(cmd); + + /* read */ + cmd = g_strdup_printf("{'execute': 'guest-file-read'," + " 'arguments': { 'handle': %" PRId64 "} }", + id); + ret = qmp_fd(fixture->fd, cmd); + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "count"); + eof = qdict_get_bool(val, "eof"); + b64 = qdict_get_str(val, "buf-b64"); + g_assert_cmpint(count, ==, sizeof(helloworld)); + g_assert(eof); + g_assert_cmpstr(b64, ==, enc); + QDECREF(ret); + g_free(cmd); + g_free(enc); + + /* close */ + cmd = g_strdup_printf("{'execute': 'guest-file-close'," + " 'arguments': {'handle': %" PRId64 "} }", + id); + ret = qmp_fd(fixture->fd, cmd); + QDECREF(ret); + g_free(cmd); +} + +static void test_qga_get_time(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + int64_t time; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + time = qdict_get_int(ret, "return"); + g_assert_cmpint(time, >, 0); + + QDECREF(ret); +} + +static void test_qga_set_time(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + int64_t current, time; + gchar *cmd; + + /* get current time */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + current = qdict_get_int(ret, "return"); + g_assert_cmpint(current, >, 0); + QDECREF(ret); + + /* set some old time */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-set-time'," + " 'arguments': { 'time': 1000 } }"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + QDECREF(ret); + + /* check old time */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + time = qdict_get_int(ret, "return"); + g_assert_cmpint(time / 1000, <, G_USEC_PER_SEC * 10); + QDECREF(ret); + + /* set back current time */ + cmd = g_strdup_printf("{'execute': 'guest-set-time'," + " 'arguments': { 'time': %" PRId64 " } }", + current + time * 1000); + ret = qmp_fd(fixture->fd, cmd); + g_free(cmd); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + QDECREF(ret); +} + +static void test_qga_fstrim(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + QList *list; + const QListEntry *entry; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fstrim'," + " arguments: { minimum: 4194304 } }"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + list = qdict_get_qlist(ret, "return"); + entry = qlist_first(list); + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "paths")); + + QDECREF(ret); +} + +static void test_qga_blacklist(gconstpointer data) +{ + TestFixture fix; + QDict *ret, *error; + const gchar *class, *desc; + + fixture_setup(&fix, "-b guest-ping,guest-get-time"); + + /* check blacklist */ + ret = qmp_fd(fix.fd, "{'execute': 'guest-ping'}"); + g_assert_nonnull(ret); + error = qdict_get_qdict(ret, "error"); + class = qdict_get_try_str(error, "class"); + desc = qdict_get_try_str(error, "desc"); + g_assert_cmpstr(class, ==, "GenericError"); + g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); + QDECREF(ret); + + ret = qmp_fd(fix.fd, "{'execute': 'guest-get-time'}"); + g_assert_nonnull(ret); + error = qdict_get_qdict(ret, "error"); + class = qdict_get_try_str(error, "class"); + desc = qdict_get_try_str(error, "desc"); + g_assert_cmpstr(class, ==, "GenericError"); + g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); + QDECREF(ret); + + /* check something work */ + ret = qmp_fd(fix.fd, "{'execute': 'guest-get-fsinfo'}"); + qmp_assert_no_error(ret); + QDECREF(ret); + + fixture_tear_down(&fix, NULL); +} + +static void test_qga_config(gconstpointer data) +{ + GError *error = NULL; + char *cwd, *cmd, *out, *err, *str, **strv, *conf, **argv = NULL; + char *env[2]; + int status, tmp; + gsize n; + GKeyFile *kf; + const char *qga_config = + "[general]\n" + "daemon=false\n" + "method=virtio-serial\n" + "path=/path/to/org.qemu.guest_agent.0\n" + "pidfile=/var/foo/qemu-ga.pid\n" + "statedir=/var/state\n" + "verbose=true\n" + "blacklist=guest-ping;guest-get-time\n"; + + tmp = g_file_open_tmp(NULL, &conf, &error); + g_assert_no_error(error); + g_assert_cmpint(tmp, >=, 0); + g_assert_cmpstr(conf, !=, ""); + + g_file_set_contents(conf, qga_config, -1, &error); + g_assert_no_error(error); + + cwd = g_get_current_dir(); + cmd = g_strdup_printf("%s%cqemu-ga -D", + cwd, G_DIR_SEPARATOR); + g_shell_parse_argv(cmd, NULL, &argv, &error); + g_assert_no_error(error); + + env[0] = g_strdup_printf("QGA_CONF=%s", conf); + env[1] = NULL; + g_spawn_sync(NULL, argv, env, 0, + NULL, NULL, &out, &err, &status, &error); + g_assert_no_error(error); + g_assert_cmpstr(err, ==, ""); + g_assert_cmpint(status, ==, 0); + + kf = g_key_file_new(); + g_key_file_load_from_data(kf, out, -1, G_KEY_FILE_NONE, &error); + g_assert_no_error(error); + + str = g_key_file_get_start_group(kf); + g_assert_cmpstr(str, ==, "general"); + g_free(str); + + g_assert_false(g_key_file_get_boolean(kf, "general", "daemon", &error)); + g_assert_no_error(error); + + str = g_key_file_get_string(kf, "general", "method", &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "virtio-serial"); + g_free(str); + + str = g_key_file_get_string(kf, "general", "path", &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "/path/to/org.qemu.guest_agent.0"); + g_free(str); + + str = g_key_file_get_string(kf, "general", "pidfile", &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "/var/foo/qemu-ga.pid"); + g_free(str); + + str = g_key_file_get_string(kf, "general", "statedir", &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "/var/state"); + g_free(str); + + g_assert_true(g_key_file_get_boolean(kf, "general", "verbose", &error)); + g_assert_no_error(error); + + strv = g_key_file_get_string_list(kf, "general", "blacklist", &n, &error); + g_assert_cmpint(n, ==, 2); +#if GLIB_CHECK_VERSION(2, 44, 0) + g_assert_true(g_strv_contains((const char * const *)strv, + "guest-ping")); + g_assert_true(g_strv_contains((const char * const *)strv, + "guest-get-time")); +#endif + g_assert_no_error(error); + g_strfreev(strv); + + g_free(out); + g_free(err); + g_free(conf); + g_free(env[0]); + g_key_file_free(kf); + + close(tmp); +} + +static void test_qga_fsfreeze_status(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + const gchar *status; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-status'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + status = qdict_get_try_str(ret, "return"); + g_assert_cmpstr(status, ==, "thawed"); + + QDECREF(ret); +} + +static void test_qga_fsfreeze_and_thaw(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + const gchar *status; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-freeze'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + QDECREF(ret); + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-status'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + status = qdict_get_try_str(ret, "return"); + g_assert_cmpstr(status, ==, "frozen"); + QDECREF(ret); + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-thaw'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + QDECREF(ret); +} + +int main(int argc, char **argv) +{ + TestFixture fix; + int ret; + + setlocale (LC_ALL, ""); + g_test_init(&argc, &argv, NULL); + fixture_setup(&fix, NULL); + + g_test_add_data_func("/qga/sync-delimited", &fix, test_qga_sync_delimited); + g_test_add_data_func("/qga/sync", &fix, test_qga_sync); + g_test_add_data_func("/qga/ping", &fix, test_qga_ping); + g_test_add_data_func("/qga/info", &fix, test_qga_info); + g_test_add_data_func("/qga/network-get-interfaces", &fix, + test_qga_network_get_interfaces); + g_test_add_data_func("/qga/get-vcpus", &fix, test_qga_get_vcpus); + g_test_add_data_func("/qga/get-fsinfo", &fix, test_qga_get_fsinfo); + g_test_add_data_func("/qga/get-memory-block-info", &fix, + test_qga_get_memory_block_info); + g_test_add_data_func("/qga/get-memory-blocks", &fix, + test_qga_get_memory_blocks); + g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops); + g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read); + g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time); + g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd); + g_test_add_data_func("/qga/fsfreeze-status", &fix, + test_qga_fsfreeze_status); + + g_test_add_data_func("/qga/blacklist", NULL, test_qga_blacklist); + g_test_add_data_func("/qga/config", NULL, test_qga_config); + + if (g_getenv("QGA_TEST_SIDE_EFFECTING")) { + g_test_add_data_func("/qga/fsfreeze-and-thaw", &fix, + test_qga_fsfreeze_and_thaw); + g_test_add_data_func("/qga/set-time", &fix, test_qga_set_time); + g_test_add_data_func("/qga/fstrim", &fix, test_qga_fstrim); + } + + ret = g_test_run(); + + fixture_tear_down(&fix, NULL); + + return ret; +} diff --git a/qemu/tests/test-qmp-commands.c b/qemu/tests/test-qmp-commands.c index 9918f2306..14a9ebbd5 100644 --- a/qemu/tests/test-qmp-commands.c +++ b/qemu/tests/test-qmp-commands.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include #include "qemu-common.h" #include "qapi/qmp/types.h" @@ -12,6 +13,11 @@ void qmp_user_def_cmd(Error **errp) { } +Empty2 *qmp_user_def_cmd0(Error **errp) +{ + return g_new0(Empty2, 1); +} + void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp) { } @@ -25,11 +31,9 @@ UserDefTwo *qmp_user_def_cmd2(UserDefOne *ud1a, UserDefOne *ud1d = g_malloc0(sizeof(UserDefOne)); ud1c->string = strdup(ud1a->string); - ud1c->base = g_new0(UserDefZero, 1); - ud1c->base->integer = ud1a->base->integer; + ud1c->integer = ud1a->integer; ud1d->string = strdup(has_udb1 ? ud1b->string : "blah0"); - ud1d->base = g_new0(UserDefZero, 1); - ud1d->base->integer = has_udb1 ? ud1b->base->integer : 0; + ud1d->integer = has_udb1 ? ud1b->integer : 0; ret = g_new0(UserDefTwo, 1); ret->string0 = strdup("blah1"); @@ -46,11 +50,16 @@ UserDefTwo *qmp_user_def_cmd2(UserDefOne *ud1a, return ret; } -int64_t qmp_user_def_cmd3(int64_t a, bool has_b, int64_t b, Error **errp) +int64_t qmp_guest_get_time(int64_t a, bool has_b, int64_t b, Error **errp) { return a + (has_b ? b : 0); } +QObject *qmp_guest_sync(QObject *arg, Error **errp) +{ + return arg; +} + __org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a, __org_qemu_x_StructList *b, __org_qemu_x_Union2 *c, @@ -59,9 +68,13 @@ __org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a, { __org_qemu_x_Union1 *ret = g_new0(__org_qemu_x_Union1, 1); - ret->kind = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH; - ret->__org_qemu_x_branch = strdup("blah1"); + ret->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH; + ret->u.__org_qemu_x_branch.data = strdup("blah1"); + /* Also test that 'wchar-t' was munged to 'q_wchar_t' */ + if (b && b->value && !b->value->has_q_wchar_t) { + b->value->q_wchar_t = 1; + } return ret; } @@ -155,7 +168,7 @@ static void test_dispatch_cmd_io(void) qdict_put(args3, "a", qint_from_int(66)); qdict_put(req, "arguments", args3); - qdict_put(req, "execute", qstring_from_str("user_def_cmd3")); + qdict_put(req, "execute", qstring_from_str("guest-get-time")); ret3 = qobject_to_qint(test_qmp_dispatch(req)); assert(qint_get_int(ret3) == 66); @@ -171,20 +184,17 @@ static void test_dealloc_types(void) UserDefOneList *ud1list; ud1test = g_malloc0(sizeof(UserDefOne)); - ud1test->base = g_new0(UserDefZero, 1); - ud1test->base->integer = 42; + ud1test->integer = 42; ud1test->string = g_strdup("hi there 42"); qapi_free_UserDefOne(ud1test); ud1a = g_malloc0(sizeof(UserDefOne)); - ud1a->base = g_new0(UserDefZero, 1); - ud1a->base->integer = 43; + ud1a->integer = 43; ud1a->string = g_strdup("hi there 43"); ud1b = g_malloc0(sizeof(UserDefOne)); - ud1b->base = g_new0(UserDefZero, 1); - ud1b->base->integer = 44; + ud1b->integer = 44; ud1b->string = g_strdup("hi there 44"); ud1list = g_malloc0(sizeof(UserDefOneList)); @@ -213,7 +223,7 @@ static void test_dealloc_partial(void) qdict_put_obj(ud2_dict, "string0", QOBJECT(qstring_from_str(text))); qiv = qmp_input_visitor_new(QOBJECT(ud2_dict)); - visit_type_UserDefTwo(qmp_input_get_visitor(qiv), &ud2, NULL, &err); + visit_type_UserDefTwo(qmp_input_get_visitor(qiv), NULL, &ud2, &err); qmp_input_visitor_cleanup(qiv); QDECREF(ud2_dict); } @@ -225,8 +235,7 @@ static void test_dealloc_partial(void) assert(ud2->dict1 == NULL); /* confirm & release construction error */ - assert(err != NULL); - error_free(err); + error_free_or_abort(&err); /* tear down partial object */ qapi_free_UserDefTwo(ud2); diff --git a/qemu/tests/test-qmp-event.c b/qemu/tests/test-qmp-event.c index 1ee40e148..a296fdbac 100644 --- a/qemu/tests/test-qmp-event.c +++ b/qemu/tests/test-qmp-event.c @@ -11,8 +11,8 @@ * */ +#include "qemu/osdep.h" #include -#include #include "qemu-common.h" #include "test-qapi-types.h" @@ -94,7 +94,7 @@ static bool qdict_cmp_simple(QDict *a, QDict *b) /* This function is hooked as final emit function, which can verify the correctness. */ -static void event_test_emit(TEST_QAPIEvent event, QDict *d, Error **errp) +static void event_test_emit(test_QAPIEvent event, QDict *d, Error **errp) { QObject *obj; QDict *t; @@ -179,9 +179,7 @@ static void test_event_c(TestEventData *data, QDict *d, *d_data, *d_b; UserDefOne b; - UserDefZero z; - z.integer = 2; - b.base = &z; + b.integer = 2; b.string = g_strdup("test1"); b.has_enum1 = false; @@ -209,11 +207,9 @@ static void test_event_d(TestEventData *data, { UserDefOne struct1; EventStructOne a; - UserDefZero z; QDict *d, *d_data, *d_a, *d_struct1; - z.integer = 2; - struct1.base = &z; + struct1.integer = 2; struct1.string = g_strdup("test1"); struct1.has_enum1 = true; struct1.enum1 = ENUM_ONE_VALUE1; diff --git a/qemu/tests/test-qmp-input-strict.c b/qemu/tests/test-qmp-input-strict.c index 68f855bdf..d71727e27 100644 --- a/qemu/tests/test-qmp-input-strict.c +++ b/qemu/tests/test-qmp-input-strict.c @@ -11,14 +11,18 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "qemu-common.h" +#include "qapi/error.h" #include "qapi/qmp-input-visitor.h" #include "test-qapi-types.h" #include "test-qapi-visit.h" #include "qapi/qmp/types.h" +#include "test-qmp-introspect.h" +#include "qmp-introspect.h" +#include "qapi-visit.h" typedef struct TestInputVisitorData { QObject *obj; @@ -37,78 +41,65 @@ static void validate_teardown(TestInputVisitorData *data, } } -/* This is provided instead of a test setup function so that the JSON - string used by the tests are kept in the test functions (and not - int main()) */ -static GCC_FMT_ATTR(2, 3) -Visitor *validate_test_init(TestInputVisitorData *data, - const char *json_string, ...) +/* The various test_init functions are provided instead of a test setup + function so that the JSON string used by the tests are kept in the test + functions (and not in main()). */ +static Visitor *validate_test_init_internal(TestInputVisitorData *data, + const char *json_string, + va_list *ap) { Visitor *v; - va_list ap; - va_start(ap, json_string); - data->obj = qobject_from_jsonv(json_string, &ap); - va_end(ap); + validate_teardown(data, NULL); - g_assert(data->obj != NULL); + data->obj = qobject_from_jsonv(json_string, ap); + g_assert(data->obj); data->qiv = qmp_input_visitor_new_strict(data->obj); - g_assert(data->qiv != NULL); + g_assert(data->qiv); v = qmp_input_get_visitor(data->qiv); - g_assert(v != NULL); + g_assert(v); return v; } -typedef struct TestStruct -{ - int64_t integer; - bool boolean; - char *string; -} TestStruct; - -static void visit_type_TestStruct(Visitor *v, TestStruct **obj, - const char *name, Error **errp) +static GCC_FMT_ATTR(2, 3) +Visitor *validate_test_init(TestInputVisitorData *data, + const char *json_string, ...) { - Error *err = NULL; + Visitor *v; + va_list ap; - visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), - &err); - if (err) { - goto out; - } + va_start(ap, json_string); + v = validate_test_init_internal(data, json_string, &ap); + va_end(ap); + return v; +} - visit_type_int(v, &(*obj)->integer, "integer", &err); - if (err) { - goto out_end; - } - visit_type_bool(v, &(*obj)->boolean, "boolean", &err); - if (err) { - goto out_end; - } - visit_type_str(v, &(*obj)->string, "string", &err); - -out_end: - error_propagate(errp, err); - err = NULL; - visit_end_struct(v, &err); -out: - error_propagate(errp, err); +/* similar to validate_test_init(), but does not expect a string + * literal/format json_string argument and so can be used for + * programatically generated strings (and we can't pass in programatically + * generated strings via %s format parameters since qobject_from_jsonv() + * will wrap those in double-quotes and treat the entire object as a + * string) + */ +static Visitor *validate_test_init_raw(TestInputVisitorData *data, + const char *json_string) +{ + return validate_test_init_internal(data, json_string, NULL); } + static void test_validate_struct(TestInputVisitorData *data, const void *unused) { TestStruct *p = NULL; - Error *err = NULL; Visitor *v; v = validate_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }"); - visit_type_TestStruct(v, &p, NULL, &err); - g_assert(!err); + visit_type_TestStruct(v, NULL, &p, &error_abort); g_free(p->string); g_free(p); } @@ -117,7 +108,6 @@ static void test_validate_struct_nested(TestInputVisitorData *data, const void *unused) { UserDefTwo *udp = NULL; - Error *err = NULL; Visitor *v; v = validate_test_init(data, "{ 'string0': 'string0', " @@ -125,8 +115,7 @@ static void test_validate_struct_nested(TestInputVisitorData *data, "'dict2': { 'userdef': { 'integer': 42, " "'string': 'string' }, 'string': 'string2'}}}"); - visit_type_UserDefTwo(v, &udp, NULL, &err); - g_assert(!err); + visit_type_UserDefTwo(v, NULL, &udp, &error_abort); qapi_free_UserDefTwo(udp); } @@ -134,13 +123,11 @@ static void test_validate_list(TestInputVisitorData *data, const void *unused) { UserDefOneList *head = NULL; - Error *err = NULL; Visitor *v; v = validate_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44 } ]"); - visit_type_UserDefOneList(v, &head, NULL, &err); - g_assert(!err); + visit_type_UserDefOneList(v, NULL, &head, &error_abort); qapi_free_UserDefOneList(head); } @@ -149,12 +136,10 @@ static void test_validate_union_native_list(TestInputVisitorData *data, { UserDefNativeListUnion *tmp = NULL; Visitor *v; - Error *err = NULL; v = validate_test_init(data, "{ 'type': 'integer', 'data' : [ 1, 2 ] }"); - visit_type_UserDefNativeListUnion(v, &tmp, NULL, &err); - g_assert(!err); + visit_type_UserDefNativeListUnion(v, NULL, &tmp, &error_abort); qapi_free_UserDefNativeListUnion(tmp); } @@ -163,16 +148,14 @@ static void test_validate_union_flat(TestInputVisitorData *data, { UserDefFlatUnion *tmp = NULL; Visitor *v; - Error *err = NULL; v = validate_test_init(data, "{ 'enum1': 'value1', " + "'integer': 41, " "'string': 'str', " "'boolean': true }"); - /* TODO when generator bug is fixed, add 'integer': 41 */ - visit_type_UserDefFlatUnion(v, &tmp, NULL, &err); - g_assert(!err); + visit_type_UserDefFlatUnion(v, NULL, &tmp, &error_abort); qapi_free_UserDefFlatUnion(tmp); } @@ -181,12 +164,10 @@ static void test_validate_alternate(TestInputVisitorData *data, { UserDefAlternate *tmp = NULL; Visitor *v; - Error *err = NULL; v = validate_test_init(data, "42"); - visit_type_UserDefAlternate(v, &tmp, NULL, &err); - g_assert(!err); + visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort); qapi_free_UserDefAlternate(tmp); } @@ -199,8 +180,8 @@ static void test_validate_fail_struct(TestInputVisitorData *data, v = validate_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo', 'extra': 42 }"); - visit_type_TestStruct(v, &p, NULL, &err); - g_assert(err); + visit_type_TestStruct(v, NULL, &p, &err); + error_free_or_abort(&err); if (p) { g_free(p->string); } @@ -216,8 +197,8 @@ static void test_validate_fail_struct_nested(TestInputVisitorData *data, v = validate_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string', 'extra': [42, 23, {'foo':'bar'}] }, 'string2': 'string2'}}}"); - visit_type_UserDefTwo(v, &udp, NULL, &err); - g_assert(err); + visit_type_UserDefTwo(v, NULL, &udp, &err); + error_free_or_abort(&err); qapi_free_UserDefTwo(udp); } @@ -230,8 +211,8 @@ static void test_validate_fail_list(TestInputVisitorData *data, v = validate_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44, 'extra': 'ggg' } ]"); - visit_type_UserDefOneList(v, &head, NULL, &err); - g_assert(err); + visit_type_UserDefOneList(v, NULL, &head, &err); + error_free_or_abort(&err); qapi_free_UserDefOneList(head); } @@ -245,8 +226,8 @@ static void test_validate_fail_union_native_list(TestInputVisitorData *data, v = validate_test_init(data, "{ 'type': 'integer', 'data' : [ 'string' ] }"); - visit_type_UserDefNativeListUnion(v, &tmp, NULL, &err); - g_assert(err); + visit_type_UserDefNativeListUnion(v, NULL, &tmp, &err); + error_free_or_abort(&err); qapi_free_UserDefNativeListUnion(tmp); } @@ -259,8 +240,8 @@ static void test_validate_fail_union_flat(TestInputVisitorData *data, v = validate_test_init(data, "{ 'string': 'c', 'integer': 41, 'boolean': true }"); - visit_type_UserDefFlatUnion(v, &tmp, NULL, &err); - g_assert(err); + visit_type_UserDefFlatUnion(v, NULL, &tmp, &err); + error_free_or_abort(&err); qapi_free_UserDefFlatUnion(tmp); } @@ -272,10 +253,10 @@ static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data, Visitor *v; /* test situation where discriminator field ('enum1' here) is missing */ - v = validate_test_init(data, "{ 'string': 'c', 'string1': 'd', 'string2': 'e' }"); + v = validate_test_init(data, "{ 'integer': 42, 'string': 'c', 'string1': 'd', 'string2': 'e' }"); - visit_type_UserDefFlatUnion2(v, &tmp, NULL, &err); - g_assert(err); + visit_type_UserDefFlatUnion2(v, NULL, &tmp, &err); + error_free_or_abort(&err); qapi_free_UserDefFlatUnion2(tmp); } @@ -288,11 +269,32 @@ static void test_validate_fail_alternate(TestInputVisitorData *data, v = validate_test_init(data, "3.14"); - visit_type_UserDefAlternate(v, &tmp, NULL, &err); - g_assert(err); + visit_type_UserDefAlternate(v, NULL, &tmp, &err); + error_free_or_abort(&err); qapi_free_UserDefAlternate(tmp); } +static void do_test_validate_qmp_introspect(TestInputVisitorData *data, + const char *schema_json) +{ + SchemaInfoList *schema = NULL; + Visitor *v; + + v = validate_test_init_raw(data, schema_json); + + visit_type_SchemaInfoList(v, NULL, &schema, &error_abort); + g_assert(schema); + + qapi_free_SchemaInfoList(schema); +} + +static void test_validate_qmp_introspect(TestInputVisitorData *data, + const void *unused) +{ + do_test_validate_qmp_introspect(data, test_qmp_schema_json); + do_test_validate_qmp_introspect(data, qmp_schema_json); +} + static void validate_test_add(const char *testpath, TestInputVisitorData *data, void (*test_func)(TestInputVisitorData *data, const void *user_data)) @@ -333,6 +335,8 @@ int main(int argc, char **argv) &testdata, test_validate_fail_alternate); validate_test_add("/visitor/input-strict/fail/union-native-list", &testdata, test_validate_fail_union_native_list); + validate_test_add("/visitor/input-strict/pass/qmp-introspect", + &testdata, test_validate_qmp_introspect); g_test_run(); diff --git a/qemu/tests/test-qmp-input-visitor.c b/qemu/tests/test-qmp-input-visitor.c index b96195309..80527eb85 100644 --- a/qemu/tests/test-qmp-input-visitor.c +++ b/qemu/tests/test-qmp-input-visitor.c @@ -1,7 +1,7 @@ /* * QMP Input Visitor unit-tests. * - * Copyright (C) 2011, 2015 Red Hat Inc. + * Copyright (C) 2011-2016 Red Hat Inc. * * Authors: * Luiz Capitulino @@ -10,10 +10,11 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "qemu-common.h" +#include "qapi/error.h" #include "qapi/qmp-input-visitor.h" #include "test-qapi-types.h" #include "test-qapi-visit.h" @@ -36,31 +37,42 @@ static void visitor_input_teardown(TestInputVisitorData *data, } } -/* This is provided instead of a test setup function so that the JSON - string used by the tests are kept in the test functions (and not - int main()) */ -static GCC_FMT_ATTR(2, 3) -Visitor *visitor_input_test_init(TestInputVisitorData *data, - const char *json_string, ...) +/* The various test_init functions are provided instead of a test setup + function so that the JSON string used by the tests are kept in the test + functions (and not in main()). */ +static Visitor *visitor_input_test_init_internal(TestInputVisitorData *data, + const char *json_string, + va_list *ap) { Visitor *v; - va_list ap; - va_start(ap, json_string); - data->obj = qobject_from_jsonv(json_string, &ap); - va_end(ap); + visitor_input_teardown(data, NULL); - g_assert(data->obj != NULL); + data->obj = qobject_from_jsonv(json_string, ap); + g_assert(data->obj); data->qiv = qmp_input_visitor_new(data->obj); - g_assert(data->qiv != NULL); + g_assert(data->qiv); v = qmp_input_get_visitor(data->qiv); - g_assert(v != NULL); + g_assert(v); return v; } +static GCC_FMT_ATTR(2, 3) +Visitor *visitor_input_test_init(TestInputVisitorData *data, + const char *json_string, ...) +{ + Visitor *v; + va_list ap; + + va_start(ap, json_string); + v = visitor_input_test_init_internal(data, json_string, &ap); + va_end(ap); + return v; +} + /* similar to visitor_input_test_init(), but does not expect a string * literal/format json_string argument and so can be used for * programatically generated strings (and we can't pass in programatically @@ -71,32 +83,18 @@ Visitor *visitor_input_test_init(TestInputVisitorData *data, static Visitor *visitor_input_test_init_raw(TestInputVisitorData *data, const char *json_string) { - Visitor *v; - - data->obj = qobject_from_json(json_string); - - g_assert(data->obj != NULL); - - data->qiv = qmp_input_visitor_new(data->obj); - g_assert(data->qiv != NULL); - - v = qmp_input_get_visitor(data->qiv); - g_assert(v != NULL); - - return v; + return visitor_input_test_init_internal(data, json_string, NULL); } static void test_visitor_in_int(TestInputVisitorData *data, const void *unused) { int64_t res = 0, value = -42; - Error *err = NULL; Visitor *v; v = visitor_input_test_init(data, "%" PRId64, value); - visit_type_int(v, &res, NULL, &err); - g_assert(!err); + visit_type_int(v, NULL, &res, &error_abort); g_assert_cmpint(res, ==, value); } @@ -113,22 +111,19 @@ static void test_visitor_in_int_overflow(TestInputVisitorData *data, */ v = visitor_input_test_init(data, "%f", DBL_MAX); - visit_type_int(v, &res, NULL, &err); - g_assert(err); - error_free(err); + visit_type_int(v, NULL, &res, &err); + error_free_or_abort(&err); } static void test_visitor_in_bool(TestInputVisitorData *data, const void *unused) { - Error *err = NULL; bool res = false; Visitor *v; v = visitor_input_test_init(data, "true"); - visit_type_bool(v, &res, NULL, &err); - g_assert(!err); + visit_type_bool(v, NULL, &res, &error_abort); g_assert_cmpint(res, ==, true); } @@ -136,13 +131,11 @@ static void test_visitor_in_number(TestInputVisitorData *data, const void *unused) { double res = 0, value = 3.14; - Error *err = NULL; Visitor *v; v = visitor_input_test_init(data, "%f", value); - visit_type_number(v, &res, NULL, &err); - g_assert(!err); + visit_type_number(v, NULL, &res, &error_abort); g_assert_cmpfloat(res, ==, value); } @@ -150,13 +143,11 @@ static void test_visitor_in_string(TestInputVisitorData *data, const void *unused) { char *res = NULL, *value = (char *) "Q E M U"; - Error *err = NULL; Visitor *v; v = visitor_input_test_init(data, "%s", value); - visit_type_str(v, &res, NULL, &err); - g_assert(!err); + visit_type_str(v, NULL, &res, &error_abort); g_assert_cmpstr(res, ==, value); g_free(res); @@ -165,7 +156,6 @@ static void test_visitor_in_string(TestInputVisitorData *data, static void test_visitor_in_enum(TestInputVisitorData *data, const void *unused) { - Error *err = NULL; Visitor *v; EnumOne i; @@ -174,63 +164,21 @@ static void test_visitor_in_enum(TestInputVisitorData *data, v = visitor_input_test_init(data, "%s", EnumOne_lookup[i]); - visit_type_EnumOne(v, &res, NULL, &err); - g_assert(!err); + visit_type_EnumOne(v, NULL, &res, &error_abort); g_assert_cmpint(i, ==, res); - - visitor_input_teardown(data, NULL); } - - data->obj = NULL; - data->qiv = NULL; } -typedef struct TestStruct -{ - int64_t integer; - bool boolean; - char *string; -} TestStruct; - -static void visit_type_TestStruct(Visitor *v, TestStruct **obj, - const char *name, Error **errp) -{ - Error *err = NULL; - - visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), - &err); - if (err) { - goto out; - } - visit_type_int(v, &(*obj)->integer, "integer", &err); - if (err) { - goto out_end; - } - visit_type_bool(v, &(*obj)->boolean, "boolean", &err); - if (err) { - goto out_end; - } - visit_type_str(v, &(*obj)->string, "string", &err); - -out_end: - error_propagate(errp, err); - err = NULL; - visit_end_struct(v, &err); -out: - error_propagate(errp, err); -} static void test_visitor_in_struct(TestInputVisitorData *data, const void *unused) { TestStruct *p = NULL; - Error *err = NULL; Visitor *v; v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }"); - visit_type_TestStruct(v, &p, NULL, &err); - g_assert(!err); + visit_type_TestStruct(v, NULL, &p, &error_abort); g_assert_cmpint(p->integer, ==, -42); g_assert(p->boolean == true); g_assert_cmpstr(p->string, ==, "foo"); @@ -239,17 +187,10 @@ static void test_visitor_in_struct(TestInputVisitorData *data, g_free(p); } -static void check_and_free_str(char *str, const char *cmp) -{ - g_assert_cmpstr(str, ==, cmp); - g_free(str); -} - static void test_visitor_in_struct_nested(TestInputVisitorData *data, const void *unused) { UserDefTwo *udp = NULL; - Error *err = NULL; Visitor *v; v = visitor_input_test_init(data, "{ 'string0': 'string0', " @@ -257,34 +198,28 @@ static void test_visitor_in_struct_nested(TestInputVisitorData *data, "'dict2': { 'userdef': { 'integer': 42, " "'string': 'string' }, 'string': 'string2'}}}"); - visit_type_UserDefTwo(v, &udp, NULL, &err); - g_assert(!err); + visit_type_UserDefTwo(v, NULL, &udp, &error_abort); - check_and_free_str(udp->string0, "string0"); - check_and_free_str(udp->dict1->string1, "string1"); - g_assert_cmpint(udp->dict1->dict2->userdef->base->integer, ==, 42); - check_and_free_str(udp->dict1->dict2->userdef->string, "string"); - check_and_free_str(udp->dict1->dict2->string, "string2"); + g_assert_cmpstr(udp->string0, ==, "string0"); + g_assert_cmpstr(udp->dict1->string1, ==, "string1"); + g_assert_cmpint(udp->dict1->dict2->userdef->integer, ==, 42); + g_assert_cmpstr(udp->dict1->dict2->userdef->string, ==, "string"); + g_assert_cmpstr(udp->dict1->dict2->string, ==, "string2"); g_assert(udp->dict1->has_dict3 == false); - g_free(udp->dict1->dict2->userdef); - g_free(udp->dict1->dict2); - g_free(udp->dict1); - g_free(udp); + qapi_free_UserDefTwo(udp); } static void test_visitor_in_list(TestInputVisitorData *data, const void *unused) { UserDefOneList *item, *head = NULL; - Error *err = NULL; Visitor *v; int i; v = visitor_input_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44 } ]"); - visit_type_UserDefOneList(v, &head, NULL, &err); - g_assert(!err); + visit_type_UserDefOneList(v, NULL, &head, &error_abort); g_assert(head != NULL); for (i = 0, item = head; item; item = item->next, i++) { @@ -292,31 +227,80 @@ static void test_visitor_in_list(TestInputVisitorData *data, snprintf(string, sizeof(string), "string%d", i); g_assert_cmpstr(item->value->string, ==, string); - g_assert_cmpint(item->value->base->integer, ==, 42 + i); + g_assert_cmpint(item->value->integer, ==, 42 + i); } qapi_free_UserDefOneList(head); + head = NULL; + + /* An empty list is valid */ + v = visitor_input_test_init(data, "[]"); + visit_type_UserDefOneList(v, NULL, &head, &error_abort); + g_assert(!head); +} + +static void test_visitor_in_any(TestInputVisitorData *data, + const void *unused) +{ + QObject *res = NULL; + Visitor *v; + QInt *qint; + QBool *qbool; + QString *qstring; + QDict *qdict; + QObject *qobj; + + v = visitor_input_test_init(data, "-42"); + visit_type_any(v, NULL, &res, &error_abort); + qint = qobject_to_qint(res); + g_assert(qint); + g_assert_cmpint(qint_get_int(qint), ==, -42); + qobject_decref(res); + + 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); + g_assert(qdict && qdict_size(qdict) == 3); + qobj = qdict_get(qdict, "integer"); + g_assert(qobj); + qint = qobject_to_qint(qobj); + g_assert(qint); + g_assert_cmpint(qint_get_int(qint), ==, -42); + qobj = qdict_get(qdict, "boolean"); + g_assert(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); + g_assert(qstring); + g_assert_cmpstr(qstring_get_str(qstring), ==, "foo"); + qobject_decref(res); } static void test_visitor_in_union_flat(TestInputVisitorData *data, const void *unused) { Visitor *v; - Error *err = NULL; UserDefFlatUnion *tmp; + UserDefUnionBase *base; v = visitor_input_test_init(data, "{ 'enum1': 'value1', " + "'integer': 41, " "'string': 'str', " "'boolean': true }"); - /* TODO when generator bug is fixed, add 'integer': 41 */ - visit_type_UserDefFlatUnion(v, &tmp, NULL, &err); - g_assert(err == NULL); - g_assert_cmpint(tmp->kind, ==, ENUM_ONE_VALUE1); + visit_type_UserDefFlatUnion(v, NULL, &tmp, &error_abort); + g_assert_cmpint(tmp->enum1, ==, ENUM_ONE_VALUE1); g_assert_cmpstr(tmp->string, ==, "str"); - /* TODO g_assert_cmpint(tmp->integer, ==, 41); */ - g_assert_cmpint(tmp->value1->boolean, ==, true); + g_assert_cmpint(tmp->integer, ==, 41); + g_assert_cmpint(tmp->u.value1.boolean, ==, true); + + base = qapi_UserDefFlatUnion_base(tmp); + g_assert(&base->enum1 == &tmp->enum1); + qapi_free_UserDefFlatUnion(tmp); } @@ -326,14 +310,144 @@ static void test_visitor_in_alternate(TestInputVisitorData *data, Visitor *v; Error *err = NULL; UserDefAlternate *tmp; + WrapAlternate *wrap; v = visitor_input_test_init(data, "42"); + visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort); + g_assert_cmpint(tmp->type, ==, QTYPE_QINT); + g_assert_cmpint(tmp->u.i, ==, 42); + qapi_free_UserDefAlternate(tmp); + + v = visitor_input_test_init(data, "'string'"); + visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort); + g_assert_cmpint(tmp->type, ==, QTYPE_QSTRING); + g_assert_cmpstr(tmp->u.s, ==, "string"); + qapi_free_UserDefAlternate(tmp); + + v = visitor_input_test_init(data, "{'integer':1, 'string':'str', " + "'enum1':'value1', 'boolean':true}"); + visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort); + g_assert_cmpint(tmp->type, ==, QTYPE_QDICT); + g_assert_cmpint(tmp->u.udfu.integer, ==, 1); + g_assert_cmpstr(tmp->u.udfu.string, ==, "str"); + g_assert_cmpint(tmp->u.udfu.enum1, ==, ENUM_ONE_VALUE1); + g_assert_cmpint(tmp->u.udfu.u.value1.boolean, ==, true); + g_assert_cmpint(tmp->u.udfu.u.value1.has_a_b, ==, false); + qapi_free_UserDefAlternate(tmp); - visit_type_UserDefAlternate(v, &tmp, NULL, &err); - g_assert(err == NULL); - g_assert_cmpint(tmp->kind, ==, USER_DEF_ALTERNATE_KIND_I); - g_assert_cmpint(tmp->i, ==, 42); + v = visitor_input_test_init(data, "false"); + visit_type_UserDefAlternate(v, NULL, &tmp, &err); + error_free_or_abort(&err); qapi_free_UserDefAlternate(tmp); + + v = visitor_input_test_init(data, "{ 'alt': 42 }"); + visit_type_WrapAlternate(v, NULL, &wrap, &error_abort); + g_assert_cmpint(wrap->alt->type, ==, QTYPE_QINT); + g_assert_cmpint(wrap->alt->u.i, ==, 42); + qapi_free_WrapAlternate(wrap); + + v = visitor_input_test_init(data, "{ 'alt': 'string' }"); + visit_type_WrapAlternate(v, NULL, &wrap, &error_abort); + g_assert_cmpint(wrap->alt->type, ==, QTYPE_QSTRING); + g_assert_cmpstr(wrap->alt->u.s, ==, "string"); + qapi_free_WrapAlternate(wrap); + + v = visitor_input_test_init(data, "{ 'alt': {'integer':1, 'string':'str', " + "'enum1':'value1', 'boolean':true} }"); + visit_type_WrapAlternate(v, NULL, &wrap, &error_abort); + g_assert_cmpint(wrap->alt->type, ==, QTYPE_QDICT); + g_assert_cmpint(wrap->alt->u.udfu.integer, ==, 1); + g_assert_cmpstr(wrap->alt->u.udfu.string, ==, "str"); + g_assert_cmpint(wrap->alt->u.udfu.enum1, ==, ENUM_ONE_VALUE1); + g_assert_cmpint(wrap->alt->u.udfu.u.value1.boolean, ==, true); + g_assert_cmpint(wrap->alt->u.udfu.u.value1.has_a_b, ==, false); + qapi_free_WrapAlternate(wrap); +} + +static void test_visitor_in_alternate_number(TestInputVisitorData *data, + const void *unused) +{ + Visitor *v; + Error *err = NULL; + AltStrBool *asb; + AltStrNum *asn; + AltNumStr *ans; + AltStrInt *asi; + AltIntNum *ain; + AltNumInt *ani; + + /* Parsing an int */ + + v = visitor_input_test_init(data, "42"); + visit_type_AltStrBool(v, NULL, &asb, &err); + error_free_or_abort(&err); + qapi_free_AltStrBool(asb); + + v = visitor_input_test_init(data, "42"); + visit_type_AltStrNum(v, NULL, &asn, &error_abort); + g_assert_cmpint(asn->type, ==, QTYPE_QFLOAT); + g_assert_cmpfloat(asn->u.n, ==, 42); + qapi_free_AltStrNum(asn); + + v = visitor_input_test_init(data, "42"); + visit_type_AltNumStr(v, NULL, &ans, &error_abort); + g_assert_cmpint(ans->type, ==, QTYPE_QFLOAT); + g_assert_cmpfloat(ans->u.n, ==, 42); + qapi_free_AltNumStr(ans); + + v = visitor_input_test_init(data, "42"); + visit_type_AltStrInt(v, NULL, &asi, &error_abort); + g_assert_cmpint(asi->type, ==, QTYPE_QINT); + g_assert_cmpint(asi->u.i, ==, 42); + qapi_free_AltStrInt(asi); + + v = visitor_input_test_init(data, "42"); + visit_type_AltIntNum(v, NULL, &ain, &error_abort); + g_assert_cmpint(ain->type, ==, QTYPE_QINT); + g_assert_cmpint(ain->u.i, ==, 42); + qapi_free_AltIntNum(ain); + + v = visitor_input_test_init(data, "42"); + visit_type_AltNumInt(v, NULL, &ani, &error_abort); + g_assert_cmpint(ani->type, ==, QTYPE_QINT); + g_assert_cmpint(ani->u.i, ==, 42); + qapi_free_AltNumInt(ani); + + /* Parsing a double */ + + v = visitor_input_test_init(data, "42.5"); + visit_type_AltStrBool(v, NULL, &asb, &err); + error_free_or_abort(&err); + qapi_free_AltStrBool(asb); + + v = visitor_input_test_init(data, "42.5"); + visit_type_AltStrNum(v, NULL, &asn, &error_abort); + g_assert_cmpint(asn->type, ==, QTYPE_QFLOAT); + g_assert_cmpfloat(asn->u.n, ==, 42.5); + qapi_free_AltStrNum(asn); + + v = visitor_input_test_init(data, "42.5"); + visit_type_AltNumStr(v, NULL, &ans, &error_abort); + g_assert_cmpint(ans->type, ==, QTYPE_QFLOAT); + g_assert_cmpfloat(ans->u.n, ==, 42.5); + qapi_free_AltNumStr(ans); + + v = visitor_input_test_init(data, "42.5"); + visit_type_AltStrInt(v, NULL, &asi, &err); + error_free_or_abort(&err); + qapi_free_AltStrInt(asi); + + v = visitor_input_test_init(data, "42.5"); + visit_type_AltIntNum(v, NULL, &ain, &error_abort); + g_assert_cmpint(ain->type, ==, QTYPE_QFLOAT); + g_assert_cmpfloat(ain->u.n, ==, 42.5); + qapi_free_AltIntNum(ain); + + v = visitor_input_test_init(data, "42.5"); + visit_type_AltNumInt(v, NULL, &ani, &error_abort); + g_assert_cmpint(ani->type, ==, QTYPE_QFLOAT); + g_assert_cmpfloat(ani->u.n, ==, 42.5); + qapi_free_AltNumInt(ani); } static void test_native_list_integer_helper(TestInputVisitorData *data, @@ -341,7 +455,6 @@ static void test_native_list_integer_helper(TestInputVisitorData *data, UserDefNativeListUnionKind kind) { UserDefNativeListUnion *cvalue = NULL; - Error *err = NULL; Visitor *v; GString *gstr_list = g_string_new(""); GString *gstr_union = g_string_new(""); @@ -358,71 +471,71 @@ static void test_native_list_integer_helper(TestInputVisitorData *data, gstr_list->str); v = visitor_input_test_init_raw(data, gstr_union->str); - visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &err); - g_assert(err == NULL); + visit_type_UserDefNativeListUnion(v, NULL, &cvalue, &error_abort); g_assert(cvalue != NULL); - g_assert_cmpint(cvalue->kind, ==, kind); + g_assert_cmpint(cvalue->type, ==, kind); switch (kind) { case USER_DEF_NATIVE_LIST_UNION_KIND_INTEGER: { intList *elem = NULL; - for (i = 0, elem = cvalue->integer; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.integer.data; + elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, i); } break; } case USER_DEF_NATIVE_LIST_UNION_KIND_S8: { int8List *elem = NULL; - for (i = 0, elem = cvalue->s8; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.s8.data; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, i); } break; } case USER_DEF_NATIVE_LIST_UNION_KIND_S16: { int16List *elem = NULL; - for (i = 0, elem = cvalue->s16; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.s16.data; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, i); } break; } case USER_DEF_NATIVE_LIST_UNION_KIND_S32: { int32List *elem = NULL; - for (i = 0, elem = cvalue->s32; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.s32.data; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, i); } break; } case USER_DEF_NATIVE_LIST_UNION_KIND_S64: { int64List *elem = NULL; - for (i = 0, elem = cvalue->s64; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.s64.data; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, i); } break; } case USER_DEF_NATIVE_LIST_UNION_KIND_U8: { uint8List *elem = NULL; - for (i = 0, elem = cvalue->u8; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.u8.data; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, i); } break; } case USER_DEF_NATIVE_LIST_UNION_KIND_U16: { uint16List *elem = NULL; - for (i = 0, elem = cvalue->u16; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.u16.data; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, i); } break; } case USER_DEF_NATIVE_LIST_UNION_KIND_U32: { uint32List *elem = NULL; - for (i = 0, elem = cvalue->u32; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.u32.data; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, i); } break; } case USER_DEF_NATIVE_LIST_UNION_KIND_U64: { uint64List *elem = NULL; - for (i = 0, elem = cvalue->u64; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.u64.data; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, i); } break; @@ -504,7 +617,6 @@ static void test_visitor_in_native_list_bool(TestInputVisitorData *data, { UserDefNativeListUnion *cvalue = NULL; boolList *elem = NULL; - Error *err = NULL; Visitor *v; GString *gstr_list = g_string_new(""); GString *gstr_union = g_string_new(""); @@ -521,12 +633,11 @@ static void test_visitor_in_native_list_bool(TestInputVisitorData *data, gstr_list->str); v = visitor_input_test_init_raw(data, gstr_union->str); - visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &err); - g_assert(err == NULL); + visit_type_UserDefNativeListUnion(v, NULL, &cvalue, &error_abort); g_assert(cvalue != NULL); - g_assert_cmpint(cvalue->kind, ==, USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN); + g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN); - for (i = 0, elem = cvalue->boolean; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.boolean.data; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, (i % 3 == 0) ? 1 : 0); } @@ -540,7 +651,6 @@ static void test_visitor_in_native_list_string(TestInputVisitorData *data, { UserDefNativeListUnion *cvalue = NULL; strList *elem = NULL; - Error *err = NULL; Visitor *v; GString *gstr_list = g_string_new(""); GString *gstr_union = g_string_new(""); @@ -556,12 +666,11 @@ static void test_visitor_in_native_list_string(TestInputVisitorData *data, gstr_list->str); v = visitor_input_test_init_raw(data, gstr_union->str); - visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &err); - g_assert(err == NULL); + visit_type_UserDefNativeListUnion(v, NULL, &cvalue, &error_abort); g_assert(cvalue != NULL); - g_assert_cmpint(cvalue->kind, ==, USER_DEF_NATIVE_LIST_UNION_KIND_STRING); + g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_STRING); - for (i = 0, elem = cvalue->string; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.string.data; elem; elem = elem->next, i++) { gchar str[8]; sprintf(str, "%d", i); g_assert_cmpstr(elem->value, ==, str); @@ -579,7 +688,6 @@ static void test_visitor_in_native_list_number(TestInputVisitorData *data, { UserDefNativeListUnion *cvalue = NULL; numberList *elem = NULL; - Error *err = NULL; Visitor *v; GString *gstr_list = g_string_new(""); GString *gstr_union = g_string_new(""); @@ -595,12 +703,11 @@ static void test_visitor_in_native_list_number(TestInputVisitorData *data, gstr_list->str); v = visitor_input_test_init_raw(data, gstr_union->str); - visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &err); - g_assert(err == NULL); + visit_type_UserDefNativeListUnion(v, NULL, &cvalue, &error_abort); g_assert(cvalue != NULL); - g_assert_cmpint(cvalue->kind, ==, USER_DEF_NATIVE_LIST_UNION_KIND_NUMBER); + g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_NUMBER); - for (i = 0, elem = cvalue->number; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.number.data; elem; elem = elem->next, i++) { GString *double_expected = g_string_new(""); GString *double_actual = g_string_new(""); @@ -631,16 +738,69 @@ static void test_visitor_in_errors(TestInputVisitorData *data, TestStruct *p = NULL; Error *err = NULL; Visitor *v; + strList *q = NULL; - v = visitor_input_test_init(data, "{ 'integer': false, 'boolean': 'foo', 'string': -42 }"); + v = visitor_input_test_init(data, "{ 'integer': false, 'boolean': 'foo', " + "'string': -42 }"); - visit_type_TestStruct(v, &p, NULL, &err); - g_assert(err); + visit_type_TestStruct(v, NULL, &p, &err); + error_free_or_abort(&err); + /* FIXME - a failed parse should not leave a partially-allocated p + * for us to clean up; this could cause callers to leak memory. */ g_assert(p->string == NULL); - error_free(err); g_free(p->string); g_free(p); + + v = visitor_input_test_init(data, "[ '1', '2', false, '3' ]"); + visit_type_strList(v, NULL, &q, &err); + error_free_or_abort(&err); + assert(q); + qapi_free_strList(q); +} + +static void test_visitor_in_wrong_type(TestInputVisitorData *data, + const void *unused) +{ + TestStruct *p = NULL; + Visitor *v; + strList *q = NULL; + int64_t i; + Error *err = NULL; + + /* Make sure arrays and structs cannot be confused */ + + v = visitor_input_test_init(data, "[]"); + visit_type_TestStruct(v, NULL, &p, &err); + error_free_or_abort(&err); + g_assert(!p); + + v = visitor_input_test_init(data, "{}"); + visit_type_strList(v, NULL, &q, &err); + error_free_or_abort(&err); + assert(!q); + + /* Make sure primitives and struct cannot be confused */ + + v = visitor_input_test_init(data, "1"); + visit_type_TestStruct(v, NULL, &p, &err); + error_free_or_abort(&err); + g_assert(!p); + + v = visitor_input_test_init(data, "{}"); + visit_type_int(v, NULL, &i, &err); + error_free_or_abort(&err); + + /* Make sure primitives and arrays cannot be confused */ + + v = visitor_input_test_init(data, "1"); + visit_type_strList(v, NULL, &q, &err); + error_free_or_abort(&err); + assert(!q); + + v = visitor_input_test_init(data, "[]"); + visit_type_int(v, NULL, &i, &err); + error_free_or_abort(&err); } int main(int argc, char **argv) @@ -667,12 +827,18 @@ int main(int argc, char **argv) &in_visitor_data, test_visitor_in_struct_nested); input_visitor_test_add("/visitor/input/list", &in_visitor_data, test_visitor_in_list); + input_visitor_test_add("/visitor/input/any", + &in_visitor_data, test_visitor_in_any); input_visitor_test_add("/visitor/input/union-flat", &in_visitor_data, test_visitor_in_union_flat); input_visitor_test_add("/visitor/input/alternate", &in_visitor_data, test_visitor_in_alternate); input_visitor_test_add("/visitor/input/errors", &in_visitor_data, test_visitor_in_errors); + input_visitor_test_add("/visitor/input/wrong-type", + &in_visitor_data, test_visitor_in_wrong_type); + input_visitor_test_add("/visitor/input/alternate-number", + &in_visitor_data, test_visitor_in_alternate_number); input_visitor_test_add("/visitor/input/native_list/int", &in_visitor_data, test_visitor_in_native_list_int); diff --git a/qemu/tests/test-qmp-output-visitor.c b/qemu/tests/test-qmp-output-visitor.c index 87ba350b4..c70926793 100644 --- a/qemu/tests/test-qmp-output-visitor.c +++ b/qemu/tests/test-qmp-output-visitor.c @@ -1,7 +1,7 @@ /* * QMP Output Visitor unit-tests. * - * Copyright (C) 2011, 2015 Red Hat Inc. + * Copyright (C) 2011-2016 Red Hat Inc. * * Authors: * Luiz Capitulino @@ -10,9 +10,11 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include #include "qemu-common.h" +#include "qapi/error.h" #include "qapi/qmp-output-visitor.h" #include "test-qapi-types.h" #include "test-qapi-visit.h" @@ -45,11 +47,9 @@ static void test_visitor_out_int(TestOutputVisitorData *data, const void *unused) { int64_t value = -42; - Error *err = NULL; QObject *obj; - visit_type_int(data->ov, &value, NULL, &err); - g_assert(!err); + visit_type_int(data->ov, NULL, &value, &error_abort); obj = qmp_output_get_qobject(data->qov); g_assert(obj != NULL); @@ -62,12 +62,10 @@ static void test_visitor_out_int(TestOutputVisitorData *data, static void test_visitor_out_bool(TestOutputVisitorData *data, const void *unused) { - Error *err = NULL; bool value = true; QObject *obj; - visit_type_bool(data->ov, &value, NULL, &err); - g_assert(!err); + visit_type_bool(data->ov, NULL, &value, &error_abort); obj = qmp_output_get_qobject(data->qov); g_assert(obj != NULL); @@ -81,11 +79,9 @@ static void test_visitor_out_number(TestOutputVisitorData *data, const void *unused) { double value = 3.14; - Error *err = NULL; QObject *obj; - visit_type_number(data->ov, &value, NULL, &err); - g_assert(!err); + visit_type_number(data->ov, NULL, &value, &error_abort); obj = qmp_output_get_qobject(data->qov); g_assert(obj != NULL); @@ -99,11 +95,9 @@ static void test_visitor_out_string(TestOutputVisitorData *data, const void *unused) { char *string = (char *) "Q E M U"; - Error *err = NULL; QObject *obj; - visit_type_str(data->ov, &string, NULL, &err); - g_assert(!err); + visit_type_str(data->ov, NULL, &string, &error_abort); obj = qmp_output_get_qobject(data->qov); g_assert(obj != NULL); @@ -117,12 +111,10 @@ static void test_visitor_out_no_string(TestOutputVisitorData *data, const void *unused) { char *string = NULL; - Error *err = NULL; QObject *obj; /* A null string should return "" */ - visit_type_str(data->ov, &string, NULL, &err); - g_assert(!err); + visit_type_str(data->ov, NULL, &string, &error_abort); obj = qmp_output_get_qobject(data->qov); g_assert(obj != NULL); @@ -135,13 +127,11 @@ static void test_visitor_out_no_string(TestOutputVisitorData *data, static void test_visitor_out_enum(TestOutputVisitorData *data, const void *unused) { - Error *err = NULL; QObject *obj; EnumOne i; - for (i = 0; i < ENUM_ONE_MAX; i++) { - visit_type_EnumOne(data->ov, &i, "unused", &err); - g_assert(!err); + for (i = 0; i < ENUM_ONE__MAX; i++) { + visit_type_EnumOne(data->ov, "unused", &i, &error_abort); obj = qmp_output_get_qobject(data->qov); g_assert(obj != NULL); @@ -155,52 +145,17 @@ static void test_visitor_out_enum(TestOutputVisitorData *data, static void test_visitor_out_enum_errors(TestOutputVisitorData *data, const void *unused) { - EnumOne i, bad_values[] = { ENUM_ONE_MAX, -1 }; + EnumOne i, bad_values[] = { ENUM_ONE__MAX, -1 }; Error *err; for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) { err = NULL; - visit_type_EnumOne(data->ov, &bad_values[i], "unused", &err); + visit_type_EnumOne(data->ov, "unused", &bad_values[i], &err); g_assert(err); error_free(err); } } -typedef struct TestStruct -{ - int64_t integer; - bool boolean; - char *string; -} TestStruct; - -static void visit_type_TestStruct(Visitor *v, TestStruct **obj, - const char *name, Error **errp) -{ - Error *err = NULL; - - visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), - &err); - if (err) { - goto out; - } - - visit_type_int(v, &(*obj)->integer, "integer", &err); - if (err) { - goto out_end; - } - visit_type_bool(v, &(*obj)->boolean, "boolean", &err); - if (err) { - goto out_end; - } - visit_type_str(v, &(*obj)->string, "string", &err); - -out_end: - error_propagate(errp, err); - err = NULL; - visit_end_struct(v, &err); -out: - error_propagate(errp, err); -} static void test_visitor_out_struct(TestOutputVisitorData *data, const void *unused) @@ -209,12 +164,10 @@ static void test_visitor_out_struct(TestOutputVisitorData *data, .boolean = false, .string = (char *) "foo"}; TestStruct *p = &test_struct; - Error *err = NULL; QObject *obj; QDict *qdict; - visit_type_TestStruct(data->ov, &p, NULL, &err); - g_assert(!err); + visit_type_TestStruct(data->ov, NULL, &p, &error_abort); obj = qmp_output_get_qobject(data->qov); g_assert(obj != NULL); @@ -233,7 +186,6 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data, const void *unused) { int64_t value = 42; - Error *err = NULL; UserDefTwo *ud2; QObject *obj; QDict *qdict, *dict1, *dict2, *dict3, *userdef; @@ -250,20 +202,17 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data, ud2->dict1->dict2 = g_malloc0(sizeof(*ud2->dict1->dict2)); ud2->dict1->dict2->userdef = g_new0(UserDefOne, 1); ud2->dict1->dict2->userdef->string = g_strdup(string); - ud2->dict1->dict2->userdef->base = g_new0(UserDefZero, 1); - ud2->dict1->dict2->userdef->base->integer = value; + ud2->dict1->dict2->userdef->integer = value; ud2->dict1->dict2->string = g_strdup(strings[2]); ud2->dict1->dict3 = g_malloc0(sizeof(*ud2->dict1->dict3)); ud2->dict1->has_dict3 = true; ud2->dict1->dict3->userdef = g_new0(UserDefOne, 1); ud2->dict1->dict3->userdef->string = g_strdup(string); - ud2->dict1->dict3->userdef->base = g_new0(UserDefZero, 1); - ud2->dict1->dict3->userdef->base->integer = value; + ud2->dict1->dict3->userdef->integer = value; ud2->dict1->dict3->string = g_strdup(strings[3]); - visit_type_UserDefTwo(data->ov, &ud2, "unused", &err); - g_assert(!err); + visit_type_UserDefTwo(data->ov, "unused", &ud2, &error_abort); obj = qmp_output_get_qobject(data->qov); g_assert(obj != NULL); @@ -300,9 +249,9 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data, static void test_visitor_out_struct_errors(TestOutputVisitorData *data, const void *unused) { - EnumOne bad_values[] = { ENUM_ONE_MAX, -1 }; - UserDefZero b; - UserDefOne u = { .base = &b }, *pu = &u; + EnumOne bad_values[] = { ENUM_ONE__MAX, -1 }; + UserDefOne u = {0}; + UserDefOne *pu = &u; Error *err; int i; @@ -310,63 +259,39 @@ static void test_visitor_out_struct_errors(TestOutputVisitorData *data, err = NULL; u.has_enum1 = true; u.enum1 = bad_values[i]; - visit_type_UserDefOne(data->ov, &pu, "unused", &err); + visit_type_UserDefOne(data->ov, "unused", &pu, &err); g_assert(err); error_free(err); } } -typedef struct TestStructList -{ - union { - TestStruct *value; - uint64_t padding; - }; - struct TestStructList *next; -} TestStructList; - -static void visit_type_TestStructList(Visitor *v, TestStructList **obj, - const char *name, Error **errp) -{ - GenericList *i, **head = (GenericList **)obj; - - visit_start_list(v, name, errp); - - for (*head = i = visit_next_list(v, head, errp); i; i = visit_next_list(v, &i, errp)) { - TestStructList *native_i = (TestStructList *)i; - visit_type_TestStruct(v, &native_i->value, NULL, errp); - } - - visit_end_list(v, errp); -} static void test_visitor_out_list(TestOutputVisitorData *data, const void *unused) { - char *value_str = (char *) "list value"; + const char *value_str = "list value"; TestStructList *p, *head = NULL; const int max_items = 10; bool value_bool = true; int value_int = 10; - Error *err = NULL; QListEntry *entry; QObject *obj; QList *qlist; int i; + /* Build the list in reverse order... */ for (i = 0; i < max_items; i++) { p = g_malloc0(sizeof(*p)); p->value = g_malloc0(sizeof(*p->value)); - p->value->integer = value_int; + p->value->integer = value_int + (max_items - i - 1); p->value->boolean = value_bool; - p->value->string = value_str; + p->value->string = g_strdup(value_str); p->next = head; head = p; } - visit_type_TestStructList(data->ov, &head, NULL, &err); - g_assert(!err); + visit_type_TestStructList(data->ov, NULL, &head, &error_abort); obj = qmp_output_get_qobject(data->qov); g_assert(obj != NULL); @@ -375,6 +300,7 @@ static void test_visitor_out_list(TestOutputVisitorData *data, qlist = qobject_to_qlist(obj); g_assert(!qlist_empty(qlist)); + /* ...and ensure that the visitor sees it in order */ i = 0; QLIST_FOREACH_ENTRY(qlist, entry) { QDict *qdict; @@ -382,7 +308,7 @@ static void test_visitor_out_list(TestOutputVisitorData *data, g_assert(qobject_type(entry->value) == QTYPE_QDICT); qdict = qobject_to_qdict(entry->value); g_assert_cmpint(qdict_size(qdict), ==, 3); - g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, value_int); + g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, value_int + i); g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, value_bool); g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, value_str); i++; @@ -390,13 +316,7 @@ static void test_visitor_out_list(TestOutputVisitorData *data, g_assert_cmpint(i, ==, max_items); QDECREF(qlist); - - for (p = head; p;) { - TestStructList *tmp = p->next; - g_free(p->value); - g_free(p); - p = tmp; - } + qapi_free_TestStructList(head); } static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data, @@ -416,8 +336,7 @@ static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data, p->value->dict1->dict2 = g_new0(UserDefTwoDictDict, 1); p->value->dict1->dict2->userdef = g_new0(UserDefOne, 1); p->value->dict1->dict2->userdef->string = g_strdup(string); - p->value->dict1->dict2->userdef->base = g_new0(UserDefZero, 1); - p->value->dict1->dict2->userdef->base->integer = 42; + p->value->dict1->dict2->userdef->integer = 42; p->value->dict1->dict2->string = g_strdup(string); p->value->dict1->has_dict3 = false; @@ -428,23 +347,67 @@ static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data, qapi_free_UserDefTwoList(head); } +static void test_visitor_out_any(TestOutputVisitorData *data, + const void *unused) +{ + QObject *qobj; + QInt *qint; + QBool *qbool; + QString *qstring; + QDict *qdict; + QObject *obj; + + qobj = QOBJECT(qint_from_int(-42)); + visit_type_any(data->ov, NULL, &qobj, &error_abort); + obj = qmp_output_get_qobject(data->qov); + g_assert(obj != NULL); + g_assert(qobject_type(obj) == QTYPE_QINT); + g_assert_cmpint(qint_get_int(qobject_to_qint(obj)), ==, -42); + qobject_decref(obj); + qobject_decref(qobj); + + qdict = qdict_new(); + qdict_put(qdict, "integer", qint_from_int(-42)); + qdict_put(qdict, "boolean", qbool_from_bool(true)); + qdict_put(qdict, "string", qstring_from_str("foo")); + qobj = QOBJECT(qdict); + visit_type_any(data->ov, NULL, &qobj, &error_abort); + qobject_decref(qobj); + obj = qmp_output_get_qobject(data->qov); + g_assert(obj != NULL); + qdict = qobject_to_qdict(obj); + g_assert(qdict); + qobj = qdict_get(qdict, "integer"); + g_assert(qobj); + qint = qobject_to_qint(qobj); + g_assert(qint); + g_assert_cmpint(qint_get_int(qint), ==, -42); + qobj = qdict_get(qdict, "boolean"); + g_assert(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); + g_assert(qstring); + g_assert_cmpstr(qstring_get_str(qstring), ==, "foo"); + qobject_decref(obj); +} + static void test_visitor_out_union_flat(TestOutputVisitorData *data, const void *unused) { QObject *arg; QDict *qdict; - Error *err = NULL; - UserDefFlatUnion *tmp = g_malloc0(sizeof(UserDefFlatUnion)); - tmp->kind = ENUM_ONE_VALUE1; + tmp->enum1 = ENUM_ONE_VALUE1; tmp->string = g_strdup("str"); - tmp->value1 = g_malloc0(sizeof(UserDefA)); - /* TODO when generator bug is fixed: tmp->integer = 41; */ - tmp->value1->boolean = true; + tmp->integer = 41; + tmp->u.value1.boolean = true; - visit_type_UserDefFlatUnion(data->ov, &tmp, NULL, &err); - g_assert(err == NULL); + visit_type_UserDefFlatUnion(data->ov, NULL, &tmp, &error_abort); arg = qmp_output_get_qobject(data->qov); g_assert(qobject_type(arg) == QTYPE_QDICT); @@ -452,7 +415,7 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data, g_assert_cmpstr(qdict_get_str(qdict, "enum1"), ==, "value1"); g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "str"); - /* TODO g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 41); */ + g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 41); g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true); qapi_free_UserDefFlatUnion(tmp); @@ -463,20 +426,55 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data, const void *unused) { QObject *arg; - Error *err = NULL; + UserDefAlternate *tmp; + QDict *qdict; - UserDefAlternate *tmp = g_malloc0(sizeof(UserDefAlternate)); - tmp->kind = USER_DEF_ALTERNATE_KIND_I; - tmp->i = 42; + tmp = g_new0(UserDefAlternate, 1); + tmp->type = QTYPE_QINT; + tmp->u.i = 42; - visit_type_UserDefAlternate(data->ov, &tmp, NULL, &err); - g_assert(err == NULL); + visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort); arg = qmp_output_get_qobject(data->qov); g_assert(qobject_type(arg) == QTYPE_QINT); g_assert_cmpint(qint_get_int(qobject_to_qint(arg)), ==, 42); qapi_free_UserDefAlternate(tmp); + qobject_decref(arg); + + tmp = g_new0(UserDefAlternate, 1); + tmp->type = QTYPE_QSTRING; + tmp->u.s = g_strdup("hello"); + + visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort); + arg = qmp_output_get_qobject(data->qov); + + g_assert(qobject_type(arg) == QTYPE_QSTRING); + g_assert_cmpstr(qstring_get_str(qobject_to_qstring(arg)), ==, "hello"); + + qapi_free_UserDefAlternate(tmp); + qobject_decref(arg); + + tmp = g_new0(UserDefAlternate, 1); + tmp->type = QTYPE_QDICT; + tmp->u.udfu.integer = 1; + tmp->u.udfu.string = g_strdup("str"); + tmp->u.udfu.enum1 = ENUM_ONE_VALUE1; + tmp->u.udfu.u.value1.boolean = true; + + visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort); + arg = qmp_output_get_qobject(data->qov); + + g_assert_cmpint(qobject_type(arg), ==, QTYPE_QDICT); + qdict = qobject_to_qdict(arg); + g_assert_cmpint(qdict_size(qdict), ==, 4); + g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 1); + g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "str"); + g_assert_cmpstr(qdict_get_str(qdict, "enum1"), ==, "value1"); + g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true); + + qapi_free_UserDefAlternate(tmp); + qobject_decref(arg); } static void test_visitor_out_empty(TestOutputVisitorData *data, @@ -485,15 +483,18 @@ static void test_visitor_out_empty(TestOutputVisitorData *data, QObject *arg; arg = qmp_output_get_qobject(data->qov); - g_assert(!arg); + g_assert(qobject_type(arg) == QTYPE_QNULL); + /* Check that qnull reference counting is sane */ + g_assert(arg->refcnt == 2); + qobject_decref(arg); } static void init_native_list(UserDefNativeListUnion *cvalue) { int i; - switch (cvalue->kind) { + switch (cvalue->type) { case USER_DEF_NATIVE_LIST_UNION_KIND_INTEGER: { - intList **list = &cvalue->integer; + intList **list = &cvalue->u.integer.data; for (i = 0; i < 32; i++) { *list = g_new0(intList, 1); (*list)->value = i; @@ -503,7 +504,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_S8: { - int8List **list = &cvalue->s8; + int8List **list = &cvalue->u.s8.data; for (i = 0; i < 32; i++) { *list = g_new0(int8List, 1); (*list)->value = i; @@ -513,7 +514,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_S16: { - int16List **list = &cvalue->s16; + int16List **list = &cvalue->u.s16.data; for (i = 0; i < 32; i++) { *list = g_new0(int16List, 1); (*list)->value = i; @@ -523,7 +524,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_S32: { - int32List **list = &cvalue->s32; + int32List **list = &cvalue->u.s32.data; for (i = 0; i < 32; i++) { *list = g_new0(int32List, 1); (*list)->value = i; @@ -533,7 +534,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_S64: { - int64List **list = &cvalue->s64; + int64List **list = &cvalue->u.s64.data; for (i = 0; i < 32; i++) { *list = g_new0(int64List, 1); (*list)->value = i; @@ -543,7 +544,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_U8: { - uint8List **list = &cvalue->u8; + uint8List **list = &cvalue->u.u8.data; for (i = 0; i < 32; i++) { *list = g_new0(uint8List, 1); (*list)->value = i; @@ -553,7 +554,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_U16: { - uint16List **list = &cvalue->u16; + uint16List **list = &cvalue->u.u16.data; for (i = 0; i < 32; i++) { *list = g_new0(uint16List, 1); (*list)->value = i; @@ -563,7 +564,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_U32: { - uint32List **list = &cvalue->u32; + uint32List **list = &cvalue->u.u32.data; for (i = 0; i < 32; i++) { *list = g_new0(uint32List, 1); (*list)->value = i; @@ -573,7 +574,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_U64: { - uint64List **list = &cvalue->u64; + uint64List **list = &cvalue->u.u64.data; for (i = 0; i < 32; i++) { *list = g_new0(uint64List, 1); (*list)->value = i; @@ -583,7 +584,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN: { - boolList **list = &cvalue->boolean; + boolList **list = &cvalue->u.boolean.data; for (i = 0; i < 32; i++) { *list = g_new0(boolList, 1); (*list)->value = (i % 3 == 0); @@ -593,7 +594,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_STRING: { - strList **list = &cvalue->string; + strList **list = &cvalue->u.string.data; for (i = 0; i < 32; i++) { *list = g_new0(strList, 1); (*list)->value = g_strdup_printf("%d", i); @@ -603,7 +604,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_NUMBER: { - numberList **list = &cvalue->number; + numberList **list = &cvalue->u.number.data; for (i = 0; i < 32; i++) { *list = g_new0(numberList, 1); (*list)->value = (double)i / 3; @@ -709,17 +710,15 @@ static void test_native_list(TestOutputVisitorData *data, UserDefNativeListUnionKind kind) { UserDefNativeListUnion *cvalue = g_new0(UserDefNativeListUnion, 1); - Error *err = NULL; QObject *obj; - cvalue->kind = kind; + cvalue->type = kind; init_native_list(cvalue); - visit_type_UserDefNativeListUnion(data->ov, &cvalue, NULL, &err); - g_assert(err == NULL); + visit_type_UserDefNativeListUnion(data->ov, NULL, &cvalue, &error_abort); obj = qmp_output_get_qobject(data->qov); - check_native_list(obj, cvalue->kind); + check_native_list(obj, cvalue->type); qapi_free_UserDefNativeListUnion(cvalue); qobject_decref(obj); } @@ -832,6 +831,8 @@ int main(int argc, char **argv) &out_visitor_data, test_visitor_out_struct_errors); output_visitor_test_add("/visitor/output/list", &out_visitor_data, test_visitor_out_list); + output_visitor_test_add("/visitor/output/any", + &out_visitor_data, test_visitor_out_any); output_visitor_test_add("/visitor/output/list-qapi-free", &out_visitor_data, test_visitor_out_list_qapi_free); output_visitor_test_add("/visitor/output/union-flat", diff --git a/qemu/tests/test-rcu-list.c b/qemu/tests/test-rcu-list.c index daa8bf41d..79d375014 100644 --- a/qemu/tests/test-rcu-list.c +++ b/qemu/tests/test-rcu-list.c @@ -20,14 +20,10 @@ * Copyright (c) 2013 Mike D. Day, IBM Corporation. */ +#include "qemu/osdep.h" #include -#include -#include -#include #include "qemu/atomic.h" #include "qemu/rcu.h" -#include "qemu/compiler.h" -#include "qemu/osdep.h" #include "qemu/thread.h" #include "qemu/rcu_queue.h" diff --git a/qemu/tests/test-rfifolock.c b/qemu/tests/test-rfifolock.c index 0572ebb42..9a3cb243b 100644 --- a/qemu/tests/test-rfifolock.c +++ b/qemu/tests/test-rfifolock.c @@ -10,6 +10,7 @@ * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include #include "qemu-common.h" #include "qemu/rfifolock.h" diff --git a/qemu/tests/test-string-input-visitor.c b/qemu/tests/test-string-input-visitor.c index 8e3433e0c..9e6906a56 100644 --- a/qemu/tests/test-string-input-visitor.c +++ b/qemu/tests/test-string-input-visitor.c @@ -10,10 +10,11 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "qemu-common.h" +#include "qapi/error.h" #include "qapi/string-input-visitor.h" #include "test-qapi-types.h" #include "test-qapi-visit.h" @@ -59,7 +60,7 @@ static void test_visitor_in_int(TestInputVisitorData *data, v = visitor_input_test_init(data, "-42"); - visit_type_int(v, &res, NULL, &err); + visit_type_int(v, NULL, &res, &err); g_assert(!err); g_assert_cmpint(res, ==, value); } @@ -74,7 +75,7 @@ static void test_visitor_in_intList(TestInputVisitorData *data, v = visitor_input_test_init(data, "1,2,0,2-4,20,5-9,1-8"); - visit_type_int16List(v, &res, NULL, &error_abort); + visit_type_int16List(v, NULL, &res, &error_abort); tmp = res; while (i < sizeof(value) / sizeof(value[0])) { g_assert(tmp); @@ -100,42 +101,42 @@ static void test_visitor_in_bool(TestInputVisitorData *data, v = visitor_input_test_init(data, "true"); - visit_type_bool(v, &res, NULL, &err); + visit_type_bool(v, NULL, &res, &err); g_assert(!err); g_assert_cmpint(res, ==, true); visitor_input_teardown(data, unused); v = visitor_input_test_init(data, "yes"); - visit_type_bool(v, &res, NULL, &err); + visit_type_bool(v, NULL, &res, &err); g_assert(!err); g_assert_cmpint(res, ==, true); visitor_input_teardown(data, unused); v = visitor_input_test_init(data, "on"); - visit_type_bool(v, &res, NULL, &err); + visit_type_bool(v, NULL, &res, &err); g_assert(!err); g_assert_cmpint(res, ==, true); visitor_input_teardown(data, unused); v = visitor_input_test_init(data, "false"); - visit_type_bool(v, &res, NULL, &err); + visit_type_bool(v, NULL, &res, &err); g_assert(!err); g_assert_cmpint(res, ==, false); visitor_input_teardown(data, unused); v = visitor_input_test_init(data, "no"); - visit_type_bool(v, &res, NULL, &err); + visit_type_bool(v, NULL, &res, &err); g_assert(!err); g_assert_cmpint(res, ==, false); visitor_input_teardown(data, unused); v = visitor_input_test_init(data, "off"); - visit_type_bool(v, &res, NULL, &err); + visit_type_bool(v, NULL, &res, &err); g_assert(!err); g_assert_cmpint(res, ==, false); } @@ -149,7 +150,7 @@ static void test_visitor_in_number(TestInputVisitorData *data, v = visitor_input_test_init(data, "3.14"); - visit_type_number(v, &res, NULL, &err); + visit_type_number(v, NULL, &res, &err); g_assert(!err); g_assert_cmpfloat(res, ==, value); } @@ -163,7 +164,7 @@ static void test_visitor_in_string(TestInputVisitorData *data, v = visitor_input_test_init(data, value); - visit_type_str(v, &res, NULL, &err); + visit_type_str(v, NULL, &res, &err); g_assert(!err); g_assert_cmpstr(res, ==, value); @@ -182,7 +183,7 @@ static void test_visitor_in_enum(TestInputVisitorData *data, v = visitor_input_test_init(data, EnumOne_lookup[i]); - visit_type_EnumOne(v, &res, NULL, &err); + visit_type_EnumOne(v, NULL, &res, &err); g_assert(!err); g_assert_cmpint(i, ==, res); @@ -220,29 +221,29 @@ static void test_visitor_in_fuzz(TestInputVisitorData *data, } v = visitor_input_test_init(data, buf); - visit_type_int(v, &ires, NULL, NULL); + visit_type_int(v, NULL, &ires, NULL); visitor_input_teardown(data, NULL); v = visitor_input_test_init(data, buf); - visit_type_intList(v, &ilres, NULL, NULL); + visit_type_intList(v, NULL, &ilres, NULL); visitor_input_teardown(data, NULL); v = visitor_input_test_init(data, buf); - visit_type_bool(v, &bres, NULL, NULL); + visit_type_bool(v, NULL, &bres, NULL); visitor_input_teardown(data, NULL); v = visitor_input_test_init(data, buf); - visit_type_number(v, &nres, NULL, NULL); + visit_type_number(v, NULL, &nres, NULL); visitor_input_teardown(data, NULL); v = visitor_input_test_init(data, buf); sres = NULL; - visit_type_str(v, &sres, NULL, NULL); + visit_type_str(v, NULL, &sres, NULL); g_free(sres); visitor_input_teardown(data, NULL); v = visitor_input_test_init(data, buf); - visit_type_EnumOne(v, &eres, NULL, NULL); + visit_type_EnumOne(v, NULL, &eres, NULL); visitor_input_teardown(data, NULL); } } diff --git a/qemu/tests/test-string-output-visitor.c b/qemu/tests/test-string-output-visitor.c index 101fb27dd..1ecd75b85 100644 --- a/qemu/tests/test-string-output-visitor.c +++ b/qemu/tests/test-string-output-visitor.c @@ -10,9 +10,11 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include #include "qemu-common.h" +#include "qapi/error.h" #include "qapi/string-output-visitor.h" #include "test-qapi-types.h" #include "test-qapi-visit.h" @@ -61,7 +63,7 @@ static void test_visitor_out_int(TestOutputVisitorData *data, Error *err = NULL; char *str; - visit_type_int(data->ov, &value, NULL, &err); + visit_type_int(data->ov, NULL, &value, &err); g_assert(!err); str = string_output_get_string(data->sov); @@ -81,7 +83,7 @@ static void test_visitor_out_intList(TestOutputVisitorData *data, 3, 4, 5, 6, 11, 12, 13, 21, 22, INT64_MAX - 1, INT64_MAX}; intList *list = NULL, **tmp = &list; int i; - Error *errp = NULL; + Error *err = NULL; char *str; for (i = 0; i < sizeof(value) / sizeof(value[0]); i++) { @@ -90,8 +92,8 @@ static void test_visitor_out_intList(TestOutputVisitorData *data, tmp = &(*tmp)->next; } - visit_type_intList(data->ov, &list, NULL, &errp); - g_assert(errp == NULL); + visit_type_intList(data->ov, NULL, &list, &err); + g_assert(err == NULL); str = string_output_get_string(data->sov); g_assert(str != NULL); @@ -120,7 +122,7 @@ static void test_visitor_out_bool(TestOutputVisitorData *data, bool value = true; char *str; - visit_type_bool(data->ov, &value, NULL, &err); + visit_type_bool(data->ov, NULL, &value, &err); g_assert(!err); str = string_output_get_string(data->sov); @@ -136,7 +138,7 @@ static void test_visitor_out_number(TestOutputVisitorData *data, Error *err = NULL; char *str; - visit_type_number(data->ov, &value, NULL, &err); + visit_type_number(data->ov, NULL, &value, &err); g_assert(!err); str = string_output_get_string(data->sov); @@ -153,7 +155,7 @@ static void test_visitor_out_string(TestOutputVisitorData *data, Error *err = NULL; char *str; - visit_type_str(data->ov, &string, NULL, &err); + visit_type_str(data->ov, NULL, &string, &err); g_assert(!err); str = string_output_get_string(data->sov); @@ -174,7 +176,7 @@ static void test_visitor_out_no_string(TestOutputVisitorData *data, char *str; /* A null string should return "" */ - visit_type_str(data->ov, &string, NULL, &err); + visit_type_str(data->ov, NULL, &string, &err); g_assert(!err); str = string_output_get_string(data->sov); @@ -194,10 +196,10 @@ static void test_visitor_out_enum(TestOutputVisitorData *data, char *str; EnumOne i; - for (i = 0; i < ENUM_ONE_MAX; i++) { + for (i = 0; i < ENUM_ONE__MAX; i++) { char *str_human; - visit_type_EnumOne(data->ov, &i, "unused", &err); + visit_type_EnumOne(data->ov, "unused", &i, &err); g_assert(!err); str_human = g_strdup_printf("\"%s\"", EnumOne_lookup[i]); @@ -217,12 +219,12 @@ static void test_visitor_out_enum(TestOutputVisitorData *data, static void test_visitor_out_enum_errors(TestOutputVisitorData *data, const void *unused) { - EnumOne i, bad_values[] = { ENUM_ONE_MAX, -1 }; + EnumOne i, bad_values[] = { ENUM_ONE__MAX, -1 }; Error *err; for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) { err = NULL; - visit_type_EnumOne(data->ov, &bad_values[i], "unused", &err); + visit_type_EnumOne(data->ov, "unused", &bad_values[i], &err); g_assert(err); error_free(err); } @@ -248,39 +250,39 @@ int main(int argc, char **argv) output_visitor_test_add("/string-visitor/output/int", &out_visitor_data, test_visitor_out_int, false); - output_visitor_test_add("/string-visitor/output/int", + output_visitor_test_add("/string-visitor/output/int-human", &out_visitor_data, test_visitor_out_int, true); output_visitor_test_add("/string-visitor/output/bool", &out_visitor_data, test_visitor_out_bool, false); - output_visitor_test_add("/string-visitor/output/bool", + output_visitor_test_add("/string-visitor/output/bool-human", &out_visitor_data, test_visitor_out_bool, true); output_visitor_test_add("/string-visitor/output/number", &out_visitor_data, test_visitor_out_number, false); - output_visitor_test_add("/string-visitor/output/number", + output_visitor_test_add("/string-visitor/output/number-human", &out_visitor_data, test_visitor_out_number, true); output_visitor_test_add("/string-visitor/output/string", &out_visitor_data, test_visitor_out_string, false); - output_visitor_test_add("/string-visitor/output/string", + output_visitor_test_add("/string-visitor/output/string-human", &out_visitor_data, test_visitor_out_string, true); output_visitor_test_add("/string-visitor/output/no-string", &out_visitor_data, test_visitor_out_no_string, false); - output_visitor_test_add("/string-visitor/output/no-string", + output_visitor_test_add("/string-visitor/output/no-string-human", &out_visitor_data, test_visitor_out_no_string, true); output_visitor_test_add("/string-visitor/output/enum", &out_visitor_data, test_visitor_out_enum, false); - output_visitor_test_add("/string-visitor/output/enum", + output_visitor_test_add("/string-visitor/output/enum-human", &out_visitor_data, test_visitor_out_enum, true); output_visitor_test_add("/string-visitor/output/enum-errors", &out_visitor_data, test_visitor_out_enum_errors, false); - output_visitor_test_add("/string-visitor/output/enum-errors", + output_visitor_test_add("/string-visitor/output/enum-errors-human", &out_visitor_data, test_visitor_out_enum_errors, true); output_visitor_test_add("/string-visitor/output/intList", &out_visitor_data, test_visitor_out_intList, false); - output_visitor_test_add("/string-visitor/output/intList", + output_visitor_test_add("/string-visitor/output/intList-human", &out_visitor_data, test_visitor_out_intList, true); g_test_run(); diff --git a/qemu/tests/test-thread-pool.c b/qemu/tests/test-thread-pool.c index 6a0b9813f..88dc7316b 100644 --- a/qemu/tests/test-thread-pool.c +++ b/qemu/tests/test-thread-pool.c @@ -1,8 +1,10 @@ +#include "qemu/osdep.h" #include #include "qemu-common.h" #include "block/aio.h" #include "block/thread-pool.h" #include "block/block.h" +#include "qapi/error.h" #include "qemu/timer.h" #include "qemu/error-report.h" @@ -229,9 +231,7 @@ int main(int argc, char **argv) ctx = aio_context_new(&local_error); if (!ctx) { - error_report("Failed to create AIO Context: '%s'", - error_get_pretty(local_error)); - error_free(local_error); + error_reportf_err(local_error, "Failed to create AIO Context: "); exit(1); } pool = aio_get_thread_pool(ctx); diff --git a/qemu/tests/test-throttle.c b/qemu/tests/test-throttle.c index 016844546..744a52436 100644 --- a/qemu/tests/test-throttle.c +++ b/qemu/tests/test-throttle.c @@ -12,9 +12,11 @@ * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include #include #include "block/aio.h" +#include "qapi/error.h" #include "qemu/throttle.h" #include "qemu/error-report.h" #include "block/throttle-groups.h" @@ -34,6 +36,9 @@ static bool double_cmp(double x, double y) /* tests for single bucket operations */ static void test_leak_bucket(void) { + throttle_config_init(&cfg); + bkt = cfg.buckets[THROTTLE_BPS_TOTAL]; + /* set initial value */ bkt.avg = 150; bkt.max = 15; @@ -56,13 +61,33 @@ static void test_leak_bucket(void) g_assert(bkt.avg == 150); g_assert(bkt.max == 15); g_assert(double_cmp(bkt.level, 0)); + + /* check that burst_level leaks correctly */ + bkt.burst_level = 6; + bkt.max = 250; + bkt.burst_length = 2; /* otherwise burst_level will not leak */ + throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100); + g_assert(double_cmp(bkt.burst_level, 3.5)); + + throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100); + g_assert(double_cmp(bkt.burst_level, 1)); + + throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100); + g_assert(double_cmp(bkt.burst_level, 0)); + + throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100); + g_assert(double_cmp(bkt.burst_level, 0)); } static void test_compute_wait(void) { + unsigned i; int64_t wait; int64_t result; + throttle_config_init(&cfg); + bkt = cfg.buckets[THROTTLE_BPS_TOTAL]; + /* no operation limit set */ bkt.avg = 0; bkt.max = 15; @@ -92,6 +117,27 @@ static void test_compute_wait(void) /* time required to do half an operation */ result = (int64_t) NANOSECONDS_PER_SECOND / 150 / 2; g_assert(wait == result); + + /* Perform I/O for 2.2 seconds at a rate of bkt.max */ + bkt.burst_length = 2; + bkt.level = 0; + bkt.avg = 10; + bkt.max = 200; + for (i = 0; i < 22; i++) { + double units = bkt.max / 10; + bkt.level += units; + bkt.burst_level += units; + throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 10); + wait = throttle_compute_wait(&bkt); + g_assert(double_cmp(bkt.burst_level, 0)); + g_assert(double_cmp(bkt.level, (i + 1) * (bkt.max - bkt.avg) / 10)); + /* We can do bursts for the 2 seconds we have configured in + * burst_length. We have 100 extra miliseconds of burst + * because bkt.level has been leaking during this time. + * After that, we have to wait. */ + result = i < 21 ? 0 : 1.8 * NANOSECONDS_PER_SECOND; + g_assert(wait == result); + } } /* functions to test ThrottleState initialization/destroy methods */ @@ -221,6 +267,8 @@ static void set_cfg_value(bool is_max, int index, int value) { if (is_max) { cfg.buckets[index].max = value; + /* If max is set, avg should never be 0 */ + cfg.buckets[index].avg = MAX(cfg.buckets[index].avg, 1); } else { cfg.buckets[index].avg = value; } @@ -230,17 +278,17 @@ static void test_enabled(void) { int i; - memset(&cfg, 0, sizeof(cfg)); + throttle_config_init(&cfg); g_assert(!throttle_enabled(&cfg)); for (i = 0; i < BUCKETS_COUNT; i++) { - memset(&cfg, 0, sizeof(cfg)); + throttle_config_init(&cfg); set_cfg_value(false, i, 150); g_assert(throttle_enabled(&cfg)); } for (i = 0; i < BUCKETS_COUNT; i++) { - memset(&cfg, 0, sizeof(cfg)); + throttle_config_init(&cfg); set_cfg_value(false, i, -150); g_assert(!throttle_enabled(&cfg)); } @@ -253,32 +301,32 @@ static void test_conflicts_for_one_set(bool is_max, int read, int write) { - memset(&cfg, 0, sizeof(cfg)); - g_assert(!throttle_conflicting(&cfg)); + throttle_config_init(&cfg); + g_assert(throttle_is_valid(&cfg, NULL)); set_cfg_value(is_max, total, 1); set_cfg_value(is_max, read, 1); - g_assert(throttle_conflicting(&cfg)); + g_assert(!throttle_is_valid(&cfg, NULL)); - memset(&cfg, 0, sizeof(cfg)); + throttle_config_init(&cfg); set_cfg_value(is_max, total, 1); set_cfg_value(is_max, write, 1); - g_assert(throttle_conflicting(&cfg)); + g_assert(!throttle_is_valid(&cfg, NULL)); - memset(&cfg, 0, sizeof(cfg)); + throttle_config_init(&cfg); set_cfg_value(is_max, total, 1); set_cfg_value(is_max, read, 1); set_cfg_value(is_max, write, 1); - g_assert(throttle_conflicting(&cfg)); + g_assert(!throttle_is_valid(&cfg, NULL)); - memset(&cfg, 0, sizeof(cfg)); + throttle_config_init(&cfg); set_cfg_value(is_max, total, 1); - g_assert(!throttle_conflicting(&cfg)); + g_assert(throttle_is_valid(&cfg, NULL)); - memset(&cfg, 0, sizeof(cfg)); + throttle_config_init(&cfg); set_cfg_value(is_max, read, 1); set_cfg_value(is_max, write, 1); - g_assert(!throttle_conflicting(&cfg)); + g_assert(throttle_is_valid(&cfg, NULL)); } static void test_conflicting_config(void) @@ -312,9 +360,9 @@ static void test_is_valid_for_value(int value, bool should_be_valid) int is_max, index; for (is_max = 0; is_max < 2; is_max++) { for (index = 0; index < BUCKETS_COUNT; index++) { - memset(&cfg, 0, sizeof(cfg)); + throttle_config_init(&cfg); set_cfg_value(is_max, index, value); - g_assert(throttle_is_valid(&cfg) == should_be_valid); + g_assert(throttle_is_valid(&cfg, NULL) == should_be_valid); } } } @@ -329,6 +377,26 @@ static void test_is_valid(void) test_is_valid_for_value(1, true); } +static void test_max_is_missing_limit(void) +{ + int i; + + for (i = 0; i < BUCKETS_COUNT; i++) { + throttle_config_init(&cfg); + cfg.buckets[i].max = 100; + cfg.buckets[i].avg = 0; + g_assert(!throttle_is_valid(&cfg, NULL)); + + cfg.buckets[i].max = 0; + cfg.buckets[i].avg = 0; + g_assert(throttle_is_valid(&cfg, NULL)); + + cfg.buckets[i].max = 0; + cfg.buckets[i].avg = 100; + g_assert(throttle_is_valid(&cfg, NULL)); + } +} + static void test_have_timer(void) { /* zero structures */ @@ -529,7 +597,7 @@ static void test_groups(void) g_assert(bdrv1->throttle_state == bdrv3->throttle_state); /* Setting the config of a group member affects the whole group */ - memset(&cfg1, 0, sizeof(cfg1)); + throttle_config_init(&cfg1); cfg1.buckets[THROTTLE_BPS_READ].avg = 500000; cfg1.buckets[THROTTLE_BPS_WRITE].avg = 285000; cfg1.buckets[THROTTLE_OPS_READ].avg = 20000; @@ -561,21 +629,8 @@ static void test_groups(void) int main(int argc, char **argv) { - Error *local_error = NULL; - - qemu_init_main_loop(&local_error); + qemu_init_main_loop(&error_fatal); ctx = qemu_get_aio_context(); - - if (!ctx) { - error_report("Failed to create AIO Context: '%s'", - local_error ? error_get_pretty(local_error) : - "Failed to initialize the QEMU main loop"); - if (local_error) { - error_free(local_error); - } - exit(1); - } - bdrv_init(); do {} while (g_main_context_iteration(NULL, false)); @@ -591,6 +646,7 @@ int main(int argc, char **argv) g_test_add_func("/throttle/config/enabled", test_enabled); g_test_add_func("/throttle/config/conflicting", test_conflicting_config); g_test_add_func("/throttle/config/is_valid", test_is_valid); + g_test_add_func("/throttle/config/max", test_max_is_missing_limit); g_test_add_func("/throttle/config_functions", test_config_functions); g_test_add_func("/throttle/accounting", test_accounting); g_test_add_func("/throttle/groups", test_groups); diff --git a/qemu/tests/test-timed-average.c b/qemu/tests/test-timed-average.c new file mode 100644 index 000000000..1cc4ab302 --- /dev/null +++ b/qemu/tests/test-timed-average.c @@ -0,0 +1,90 @@ +/* + * Timed average computation tests + * + * Copyright Nodalink, EURL. 2014 + * + * Authors: + * Benoît Canet + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include + +#include "qemu/timed-average.h" + +/* This is the clock for QEMU_CLOCK_VIRTUAL */ +static int64_t my_clock_value; + +int64_t cpu_get_clock(void) +{ + return my_clock_value; +} + +static void account(TimedAverage *ta) +{ + timed_average_account(ta, 1); + timed_average_account(ta, 5); + timed_average_account(ta, 2); + timed_average_account(ta, 4); + timed_average_account(ta, 3); +} + +static void test_average(void) +{ + TimedAverage ta; + uint64_t result; + int i; + + /* we will compute some average on a period of 1 second */ + timed_average_init(&ta, QEMU_CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND); + + result = timed_average_min(&ta); + g_assert(result == 0); + result = timed_average_avg(&ta); + g_assert(result == 0); + result = timed_average_max(&ta); + g_assert(result == 0); + + for (i = 0; i < 100; i++) { + account(&ta); + result = timed_average_min(&ta); + g_assert(result == 1); + result = timed_average_avg(&ta); + g_assert(result == 3); + result = timed_average_max(&ta); + g_assert(result == 5); + my_clock_value += NANOSECONDS_PER_SECOND / 10; + } + + my_clock_value += NANOSECONDS_PER_SECOND * 100; + + result = timed_average_min(&ta); + g_assert(result == 0); + result = timed_average_avg(&ta); + g_assert(result == 0); + result = timed_average_max(&ta); + g_assert(result == 0); + + for (i = 0; i < 100; i++) { + account(&ta); + result = timed_average_min(&ta); + g_assert(result == 1); + result = timed_average_avg(&ta); + g_assert(result == 3); + result = timed_average_max(&ta); + g_assert(result == 5); + my_clock_value += NANOSECONDS_PER_SECOND / 10; + } +} + +int main(int argc, char **argv) +{ + /* tests in the same order as the header function declarations */ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/timed-average/average", test_average); + return g_test_run(); +} + diff --git a/qemu/tests/test-visitor-serialization.c b/qemu/tests/test-visitor-serialization.c index fa86cae88..9adbc30a4 100644 --- a/qemu/tests/test-visitor-serialization.c +++ b/qemu/tests/test-visitor-serialization.c @@ -11,14 +11,14 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include -#include #include #include "qemu-common.h" #include "test-qapi-types.h" #include "test-qapi-visit.h" +#include "qapi/error.h" #include "qapi/qmp/types.h" #include "qapi/qmp-input-visitor.h" #include "qapi/qmp-output-visitor.h" @@ -101,40 +101,40 @@ static void visit_primitive_type(Visitor *v, void **native, Error **errp) PrimitiveType *pt = *native; switch(pt->type) { case PTYPE_STRING: - visit_type_str(v, (char **)&pt->value.string, NULL, errp); + visit_type_str(v, NULL, (char **)&pt->value.string, errp); break; case PTYPE_BOOLEAN: - visit_type_bool(v, &pt->value.boolean, NULL, errp); + visit_type_bool(v, NULL, &pt->value.boolean, errp); break; case PTYPE_NUMBER: - visit_type_number(v, &pt->value.number, NULL, errp); + visit_type_number(v, NULL, &pt->value.number, errp); break; case PTYPE_INTEGER: - visit_type_int(v, &pt->value.integer, NULL, errp); + visit_type_int(v, NULL, &pt->value.integer, errp); break; case PTYPE_U8: - visit_type_uint8(v, &pt->value.u8, NULL, errp); + visit_type_uint8(v, NULL, &pt->value.u8, errp); break; case PTYPE_U16: - visit_type_uint16(v, &pt->value.u16, NULL, errp); + visit_type_uint16(v, NULL, &pt->value.u16, errp); break; case PTYPE_U32: - visit_type_uint32(v, &pt->value.u32, NULL, errp); + visit_type_uint32(v, NULL, &pt->value.u32, errp); break; case PTYPE_U64: - visit_type_uint64(v, &pt->value.u64, NULL, errp); + visit_type_uint64(v, NULL, &pt->value.u64, errp); break; case PTYPE_S8: - visit_type_int8(v, &pt->value.s8, NULL, errp); + visit_type_int8(v, NULL, &pt->value.s8, errp); break; case PTYPE_S16: - visit_type_int16(v, &pt->value.s16, NULL, errp); + visit_type_int16(v, NULL, &pt->value.s16, errp); break; case PTYPE_S32: - visit_type_int32(v, &pt->value.s32, NULL, errp); + visit_type_int32(v, NULL, &pt->value.s32, errp); break; case PTYPE_S64: - visit_type_int64(v, &pt->value.s64, NULL, errp); + visit_type_int64(v, NULL, &pt->value.s64, errp); break; case PTYPE_EOL: g_assert_not_reached(); @@ -146,80 +146,46 @@ static void visit_primitive_list(Visitor *v, void **native, Error **errp) PrimitiveList *pl = *native; switch (pl->type) { case PTYPE_STRING: - visit_type_strList(v, &pl->value.strings, NULL, errp); + visit_type_strList(v, NULL, &pl->value.strings, errp); break; case PTYPE_BOOLEAN: - visit_type_boolList(v, &pl->value.booleans, NULL, errp); + visit_type_boolList(v, NULL, &pl->value.booleans, errp); break; case PTYPE_NUMBER: - visit_type_numberList(v, &pl->value.numbers, NULL, errp); + visit_type_numberList(v, NULL, &pl->value.numbers, errp); break; case PTYPE_INTEGER: - visit_type_intList(v, &pl->value.integers, NULL, errp); + visit_type_intList(v, NULL, &pl->value.integers, errp); break; case PTYPE_S8: - visit_type_int8List(v, &pl->value.s8_integers, NULL, errp); + visit_type_int8List(v, NULL, &pl->value.s8_integers, errp); break; case PTYPE_S16: - visit_type_int16List(v, &pl->value.s16_integers, NULL, errp); + visit_type_int16List(v, NULL, &pl->value.s16_integers, errp); break; case PTYPE_S32: - visit_type_int32List(v, &pl->value.s32_integers, NULL, errp); + visit_type_int32List(v, NULL, &pl->value.s32_integers, errp); break; case PTYPE_S64: - visit_type_int64List(v, &pl->value.s64_integers, NULL, errp); + visit_type_int64List(v, NULL, &pl->value.s64_integers, errp); break; case PTYPE_U8: - visit_type_uint8List(v, &pl->value.u8_integers, NULL, errp); + visit_type_uint8List(v, NULL, &pl->value.u8_integers, errp); break; case PTYPE_U16: - visit_type_uint16List(v, &pl->value.u16_integers, NULL, errp); + visit_type_uint16List(v, NULL, &pl->value.u16_integers, errp); break; case PTYPE_U32: - visit_type_uint32List(v, &pl->value.u32_integers, NULL, errp); + visit_type_uint32List(v, NULL, &pl->value.u32_integers, errp); break; case PTYPE_U64: - visit_type_uint64List(v, &pl->value.u64_integers, NULL, errp); + visit_type_uint64List(v, NULL, &pl->value.u64_integers, errp); break; default: g_assert_not_reached(); } } -typedef struct TestStruct -{ - int64_t integer; - bool boolean; - char *string; -} TestStruct; - -static void visit_type_TestStruct(Visitor *v, TestStruct **obj, - const char *name, Error **errp) -{ - Error *err = NULL; - - visit_start_struct(v, (void **)obj, NULL, name, sizeof(TestStruct), &err); - if (err) { - goto out; - } - - visit_type_int(v, &(*obj)->integer, "integer", &err); - if (err) { - goto out_end; - } - visit_type_bool(v, &(*obj)->boolean, "boolean", &err); - if (err) { - goto out_end; - } - visit_type_str(v, &(*obj)->string, "string", &err); - -out_end: - error_propagate(errp, err); - err = NULL; - visit_end_struct(v, &err); -out: - error_propagate(errp, err); -} static TestStruct *struct_create(void) { @@ -247,7 +213,7 @@ static void struct_cleanup(TestStruct *ts) static void visit_struct(Visitor *v, void **native, Error **errp) { - visit_type_TestStruct(v, (TestStruct **)native, NULL, errp); + visit_type_TestStruct(v, NULL, (TestStruct **)native, errp); } static UserDefTwo *nested_struct_create(void) @@ -258,15 +224,13 @@ static UserDefTwo *nested_struct_create(void) udnp->dict1->string1 = strdup("test_string1"); udnp->dict1->dict2 = g_malloc0(sizeof(*udnp->dict1->dict2)); udnp->dict1->dict2->userdef = g_new0(UserDefOne, 1); - udnp->dict1->dict2->userdef->base = g_new0(UserDefZero, 1); - udnp->dict1->dict2->userdef->base->integer = 42; + udnp->dict1->dict2->userdef->integer = 42; udnp->dict1->dict2->userdef->string = strdup("test_string"); udnp->dict1->dict2->string = strdup("test_string2"); udnp->dict1->dict3 = g_malloc0(sizeof(*udnp->dict1->dict3)); udnp->dict1->has_dict3 = true; udnp->dict1->dict3->userdef = g_new0(UserDefOne, 1); - udnp->dict1->dict3->userdef->base = g_new0(UserDefZero, 1); - udnp->dict1->dict3->userdef->base->integer = 43; + udnp->dict1->dict3->userdef->integer = 43; udnp->dict1->dict3->userdef->string = strdup("test_string"); udnp->dict1->dict3->string = strdup("test_string3"); return udnp; @@ -278,15 +242,15 @@ static void nested_struct_compare(UserDefTwo *udnp1, UserDefTwo *udnp2) g_assert(udnp2); g_assert_cmpstr(udnp1->string0, ==, udnp2->string0); g_assert_cmpstr(udnp1->dict1->string1, ==, udnp2->dict1->string1); - g_assert_cmpint(udnp1->dict1->dict2->userdef->base->integer, ==, - udnp2->dict1->dict2->userdef->base->integer); + g_assert_cmpint(udnp1->dict1->dict2->userdef->integer, ==, + udnp2->dict1->dict2->userdef->integer); g_assert_cmpstr(udnp1->dict1->dict2->userdef->string, ==, udnp2->dict1->dict2->userdef->string); g_assert_cmpstr(udnp1->dict1->dict2->string, ==, udnp2->dict1->dict2->string); g_assert(udnp1->dict1->has_dict3 == udnp2->dict1->has_dict3); - g_assert_cmpint(udnp1->dict1->dict3->userdef->base->integer, ==, - udnp2->dict1->dict3->userdef->base->integer); + g_assert_cmpint(udnp1->dict1->dict3->userdef->integer, ==, + udnp2->dict1->dict3->userdef->integer); g_assert_cmpstr(udnp1->dict1->dict3->userdef->string, ==, udnp2->dict1->dict3->userdef->string); g_assert_cmpstr(udnp1->dict1->dict3->string, ==, @@ -300,12 +264,12 @@ static void nested_struct_cleanup(UserDefTwo *udnp) static void visit_nested_struct(Visitor *v, void **native, Error **errp) { - visit_type_UserDefTwo(v, (UserDefTwo **)native, NULL, errp); + visit_type_UserDefTwo(v, NULL, (UserDefTwo **)native, errp); } static void visit_nested_struct_list(Visitor *v, void **native, Error **errp) { - visit_type_UserDefTwoList(v, (UserDefTwoList **)native, NULL, errp); + visit_type_UserDefTwoList(v, NULL, (UserDefTwoList **)native, errp); } /* test cases */ @@ -338,14 +302,13 @@ static void test_primitives(gconstpointer opaque) const SerializeOps *ops = args->ops; PrimitiveType *pt = args->test_data; PrimitiveType *pt_copy = g_malloc0(sizeof(*pt_copy)); - Error *err = NULL; void *serialize_data; pt_copy->type = pt->type; - ops->serialize(pt, &serialize_data, visit_primitive_type, &err); - ops->deserialize((void **)&pt_copy, serialize_data, visit_primitive_type, &err); + ops->serialize(pt, &serialize_data, visit_primitive_type, &error_abort); + ops->deserialize((void **)&pt_copy, serialize_data, visit_primitive_type, + &error_abort); - g_assert(err == NULL); g_assert(pt_copy != NULL); if (pt->type == PTYPE_STRING) { g_assert_cmpstr(pt->value.string, ==, pt_copy->value.string); @@ -381,7 +344,6 @@ static void test_primitive_lists(gconstpointer opaque) PrimitiveList pl = { .value = { NULL } }; PrimitiveList pl_copy = { .value = { NULL } }; PrimitiveList *pl_copy_ptr = &pl_copy; - Error *err = NULL; void *serialize_data; void *cur_head = NULL; int i; @@ -528,10 +490,11 @@ static void test_primitive_lists(gconstpointer opaque) } } - ops->serialize((void **)&pl, &serialize_data, visit_primitive_list, &err); - ops->deserialize((void **)&pl_copy_ptr, serialize_data, visit_primitive_list, &err); + ops->serialize((void **)&pl, &serialize_data, visit_primitive_list, + &error_abort); + ops->deserialize((void **)&pl_copy_ptr, serialize_data, + visit_primitive_list, &error_abort); - g_assert(err == NULL); i = 0; /* compare our deserialized list of primitives to the original */ @@ -688,10 +651,8 @@ static void test_primitive_lists(gconstpointer opaque) g_assert_cmpint(i, ==, 33); ops->cleanup(serialize_data); - dealloc_helper(&pl, visit_primitive_list, &err); - g_assert(!err); - dealloc_helper(&pl_copy, visit_primitive_list, &err); - g_assert(!err); + dealloc_helper(&pl, visit_primitive_list, &error_abort); + dealloc_helper(&pl_copy, visit_primitive_list, &error_abort); g_free(args); } @@ -701,13 +662,12 @@ static void test_struct(gconstpointer opaque) const SerializeOps *ops = args->ops; TestStruct *ts = struct_create(); TestStruct *ts_copy = NULL; - Error *err = NULL; void *serialize_data; - ops->serialize(ts, &serialize_data, visit_struct, &err); - ops->deserialize((void **)&ts_copy, serialize_data, visit_struct, &err); + ops->serialize(ts, &serialize_data, visit_struct, &error_abort); + ops->deserialize((void **)&ts_copy, serialize_data, visit_struct, + &error_abort); - g_assert(err == NULL); struct_compare(ts, ts_copy); struct_cleanup(ts); @@ -723,14 +683,12 @@ static void test_nested_struct(gconstpointer opaque) const SerializeOps *ops = args->ops; UserDefTwo *udnp = nested_struct_create(); UserDefTwo *udnp_copy = NULL; - Error *err = NULL; void *serialize_data; - ops->serialize(udnp, &serialize_data, visit_nested_struct, &err); + ops->serialize(udnp, &serialize_data, visit_nested_struct, &error_abort); ops->deserialize((void **)&udnp_copy, serialize_data, visit_nested_struct, - &err); + &error_abort); - g_assert(err == NULL); nested_struct_compare(udnp, udnp_copy); nested_struct_cleanup(udnp); @@ -745,7 +703,6 @@ static void test_nested_struct_list(gconstpointer opaque) TestArgs *args = (TestArgs *) opaque; const SerializeOps *ops = args->ops; UserDefTwoList *listp = NULL, *tmp, *tmp_copy, *listp_copy = NULL; - Error *err = NULL; void *serialize_data; int i = 0; @@ -756,11 +713,10 @@ static void test_nested_struct_list(gconstpointer opaque) listp = tmp; } - ops->serialize(listp, &serialize_data, visit_nested_struct_list, &err); + ops->serialize(listp, &serialize_data, visit_nested_struct_list, + &error_abort); ops->deserialize((void **)&listp_copy, serialize_data, - visit_nested_struct_list, &err); - - g_assert(err == NULL); + visit_nested_struct_list, &error_abort); tmp = listp; tmp_copy = listp_copy; diff --git a/qemu/tests/test-vmstate.c b/qemu/tests/test-vmstate.c index 1d620e04f..713d4443b 100644 --- a/qemu/tests/test-vmstate.c +++ b/qemu/tests/test-vmstate.c @@ -22,12 +22,13 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include "qemu-common.h" #include "migration/migration.h" #include "migration/vmstate.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" static char temp_file[] = "/tmp/vmst.test.XXXXXX"; static int temp_fd; diff --git a/qemu/tests/test-write-threshold.c b/qemu/tests/test-write-threshold.c index faffa7b85..fdbc8020f 100644 --- a/qemu/tests/test-write-threshold.c +++ b/qemu/tests/test-write-threshold.c @@ -6,8 +6,8 @@ * */ +#include "qemu/osdep.h" #include -#include #include "block/block_int.h" #include "block/write-threshold.h" diff --git a/qemu/tests/test-x86-cpuid.c b/qemu/tests/test-x86-cpuid.c index 6cd20d4a2..8eb0bc6ad 100644 --- a/qemu/tests/test-x86-cpuid.c +++ b/qemu/tests/test-x86-cpuid.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include "hw/i386/topology.h" diff --git a/qemu/tests/test-xbzrle.c b/qemu/tests/test-xbzrle.c index db93b0a3d..49f64195a 100644 --- a/qemu/tests/test-xbzrle.c +++ b/qemu/tests/test-xbzrle.c @@ -10,14 +10,9 @@ * See the COPYING file in the top-level directory. * */ -#include -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include "qemu-common.h" +#include "qemu/cutils.h" #include "include/migration/migration.h" #define PAGE_SIZE 4096 diff --git a/qemu/tests/tmp105-test.c b/qemu/tests/tmp105-test.c index 99db53819..235cae013 100644 --- a/qemu/tests/tmp105-test.c +++ b/qemu/tests/tmp105-test.c @@ -7,6 +7,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include #include "libqtest.h" diff --git a/qemu/tests/tpci200-test.c b/qemu/tests/tpci200-test.c index 9ae01277e..cb2b00ca8 100644 --- a/qemu/tests/tpci200-test.c +++ b/qemu/tests/tpci200-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void nop(void) diff --git a/qemu/tests/usb-hcd-ehci-test.c b/qemu/tests/usb-hcd-ehci-test.c index 75073bf24..a0f13ef40 100644 --- a/qemu/tests/usb-hcd-ehci-test.c +++ b/qemu/tests/usb-hcd-ehci-test.c @@ -7,12 +7,10 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include -#include #include "libqtest.h" #include "libqos/pci-pc.h" -#include "qemu/osdep.h" #include "hw/usb/uhci-regs.h" #include "hw/usb/ehci-regs.h" #include "libqos/usb.h" diff --git a/qemu/tests/usb-hcd-ohci-test.c b/qemu/tests/usb-hcd-ohci-test.c index fa592d487..efd6669c7 100644 --- a/qemu/tests/usb-hcd-ohci-test.c +++ b/qemu/tests/usb-hcd-ohci-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" #include "libqos/usb.h" diff --git a/qemu/tests/usb-hcd-uhci-test.c b/qemu/tests/usb-hcd-uhci-test.c index a96b71683..71ff2ea18 100644 --- a/qemu/tests/usb-hcd-uhci-test.c +++ b/qemu/tests/usb-hcd-uhci-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" #include "libqos/usb.h" #include "hw/usb/uhci-regs.h" diff --git a/qemu/tests/usb-hcd-xhci-test.c b/qemu/tests/usb-hcd-xhci-test.c index 56ab36731..7e2e212df 100644 --- a/qemu/tests/usb-hcd-xhci-test.c +++ b/qemu/tests/usb-hcd-xhci-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" #include "libqos/usb.h" diff --git a/qemu/tests/vhost-user-bridge.c b/qemu/tests/vhost-user-bridge.c new file mode 100644 index 000000000..0779ba260 --- /dev/null +++ b/qemu/tests/vhost-user-bridge.c @@ -0,0 +1,1418 @@ +/* + * Vhost User Bridge + * + * Copyright (c) 2015 Red Hat, Inc. + * + * Authors: + * Victor Kaplansky + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +/* + * TODO: + * - main should get parameters from the command line. + * - implement all request handlers. Still not implemented: + * vubr_get_queue_num_exec() + * vubr_send_rarp_exec() + * - test for broken requests and virtqueue. + * - implement features defined by Virtio 1.0 spec. + * - support mergeable buffers and indirect descriptors. + * - implement clean shutdown. + * - implement non-blocking writes to UDP backend. + * - implement polling strategy. + * - implement clean starting/stopping of vq processing + * - implement clean starting/stopping of used and buffers + * dirty page logging. + */ + +#define _FILE_OFFSET_BITS 64 + +#include "qemu/osdep.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "qemu/atomic.h" +#include "standard-headers/linux/virtio_net.h" +#include "standard-headers/linux/virtio_ring.h" + +#define VHOST_USER_BRIDGE_DEBUG 1 + +#define DPRINT(...) \ + do { \ + if (VHOST_USER_BRIDGE_DEBUG) { \ + printf(__VA_ARGS__); \ + } \ + } while (0) + +typedef void (*CallbackFunc)(int sock, void *ctx); + +typedef struct Event { + void *ctx; + CallbackFunc callback; +} Event; + +typedef struct Dispatcher { + int max_sock; + fd_set fdset; + Event events[FD_SETSIZE]; +} Dispatcher; + +static void +vubr_die(const char *s) +{ + perror(s); + exit(1); +} + +static int +dispatcher_init(Dispatcher *dispr) +{ + FD_ZERO(&dispr->fdset); + dispr->max_sock = -1; + return 0; +} + +static int +dispatcher_add(Dispatcher *dispr, int sock, void *ctx, CallbackFunc cb) +{ + if (sock >= FD_SETSIZE) { + fprintf(stderr, + "Error: Failed to add new event. sock %d should be less than %d\n", + sock, FD_SETSIZE); + return -1; + } + + dispr->events[sock].ctx = ctx; + dispr->events[sock].callback = cb; + + FD_SET(sock, &dispr->fdset); + if (sock > dispr->max_sock) { + dispr->max_sock = sock; + } + DPRINT("Added sock %d for watching. max_sock: %d\n", + sock, dispr->max_sock); + return 0; +} + +/* dispatcher_remove() is not currently in use but may be useful + * in the future. */ +static int +dispatcher_remove(Dispatcher *dispr, int sock) +{ + if (sock >= FD_SETSIZE) { + fprintf(stderr, + "Error: Failed to remove event. sock %d should be less than %d\n", + sock, FD_SETSIZE); + return -1; + } + + FD_CLR(sock, &dispr->fdset); + DPRINT("Sock %d removed from dispatcher watch.\n", sock); + return 0; +} + +/* timeout in us */ +static int +dispatcher_wait(Dispatcher *dispr, uint32_t timeout) +{ + struct timeval tv; + tv.tv_sec = timeout / 1000000; + tv.tv_usec = timeout % 1000000; + + fd_set fdset = dispr->fdset; + + /* wait until some of sockets become readable. */ + int rc = select(dispr->max_sock + 1, &fdset, 0, 0, &tv); + + if (rc == -1) { + vubr_die("select"); + } + + /* Timeout */ + if (rc == 0) { + return 0; + } + + /* Now call callback for every ready socket. */ + + int sock; + for (sock = 0; sock < dispr->max_sock + 1; sock++) { + /* The callback on a socket can remove other sockets from the + * dispatcher, thus we have to check that the socket is + * still not removed from dispatcher's list + */ + if (FD_ISSET(sock, &fdset) && FD_ISSET(sock, &dispr->fdset)) { + Event *e = &dispr->events[sock]; + e->callback(sock, e->ctx); + } + } + + return 0; +} + +typedef struct VubrVirtq { + int call_fd; + int kick_fd; + uint32_t size; + uint16_t last_avail_index; + uint16_t last_used_index; + struct vring_desc *desc; + struct vring_avail *avail; + struct vring_used *used; + uint64_t log_guest_addr; + int enable; +} VubrVirtq; + +/* Based on qemu/hw/virtio/vhost-user.c */ + +#define VHOST_MEMORY_MAX_NREGIONS 8 +#define VHOST_USER_F_PROTOCOL_FEATURES 30 +/* v1.0 compliant. */ +#define VIRTIO_F_VERSION_1 32 + +#define VHOST_LOG_PAGE 4096 + +enum VhostUserProtocolFeature { + VHOST_USER_PROTOCOL_F_MQ = 0, + VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1, + VHOST_USER_PROTOCOL_F_RARP = 2, + + VHOST_USER_PROTOCOL_F_MAX +}; + +#define VHOST_USER_PROTOCOL_FEATURE_MASK ((1 << VHOST_USER_PROTOCOL_F_MAX) - 1) + +typedef enum VhostUserRequest { + VHOST_USER_NONE = 0, + VHOST_USER_GET_FEATURES = 1, + VHOST_USER_SET_FEATURES = 2, + VHOST_USER_SET_OWNER = 3, + VHOST_USER_RESET_OWNER = 4, + VHOST_USER_SET_MEM_TABLE = 5, + VHOST_USER_SET_LOG_BASE = 6, + VHOST_USER_SET_LOG_FD = 7, + VHOST_USER_SET_VRING_NUM = 8, + VHOST_USER_SET_VRING_ADDR = 9, + VHOST_USER_SET_VRING_BASE = 10, + VHOST_USER_GET_VRING_BASE = 11, + VHOST_USER_SET_VRING_KICK = 12, + VHOST_USER_SET_VRING_CALL = 13, + VHOST_USER_SET_VRING_ERR = 14, + VHOST_USER_GET_PROTOCOL_FEATURES = 15, + VHOST_USER_SET_PROTOCOL_FEATURES = 16, + VHOST_USER_GET_QUEUE_NUM = 17, + VHOST_USER_SET_VRING_ENABLE = 18, + VHOST_USER_SEND_RARP = 19, + VHOST_USER_MAX +} VhostUserRequest; + +typedef struct VhostUserMemoryRegion { + uint64_t guest_phys_addr; + uint64_t memory_size; + uint64_t userspace_addr; + uint64_t mmap_offset; +} VhostUserMemoryRegion; + +typedef struct VhostUserMemory { + uint32_t nregions; + uint32_t padding; + VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS]; +} VhostUserMemory; + +typedef struct VhostUserLog { + uint64_t mmap_size; + uint64_t mmap_offset; +} VhostUserLog; + +typedef struct VhostUserMsg { + VhostUserRequest request; + +#define VHOST_USER_VERSION_MASK (0x3) +#define VHOST_USER_REPLY_MASK (0x1<<2) + uint32_t flags; + uint32_t size; /* the following payload size */ + union { +#define VHOST_USER_VRING_IDX_MASK (0xff) +#define VHOST_USER_VRING_NOFD_MASK (0x1<<8) + uint64_t u64; + struct vhost_vring_state state; + struct vhost_vring_addr addr; + VhostUserMemory memory; + VhostUserLog log; + } payload; + int fds[VHOST_MEMORY_MAX_NREGIONS]; + int fd_num; +} QEMU_PACKED VhostUserMsg; + +#define VHOST_USER_HDR_SIZE offsetof(VhostUserMsg, payload.u64) + +/* The version of the protocol we support */ +#define VHOST_USER_VERSION (0x1) + +#define MAX_NR_VIRTQUEUE (8) + +typedef struct VubrDevRegion { + /* Guest Physical address. */ + uint64_t gpa; + /* Memory region size. */ + uint64_t size; + /* QEMU virtual address (userspace). */ + uint64_t qva; + /* Starting offset in our mmaped space. */ + uint64_t mmap_offset; + /* Start address of mmaped space. */ + uint64_t mmap_addr; +} VubrDevRegion; + +typedef struct VubrDev { + int sock; + Dispatcher dispatcher; + uint32_t nregions; + VubrDevRegion regions[VHOST_MEMORY_MAX_NREGIONS]; + VubrVirtq vq[MAX_NR_VIRTQUEUE]; + int log_call_fd; + uint64_t log_size; + uint8_t *log_table; + int backend_udp_sock; + struct sockaddr_in backend_udp_dest; + int ready; + uint64_t features; + int hdrlen; +} VubrDev; + +static const char *vubr_request_str[] = { + [VHOST_USER_NONE] = "VHOST_USER_NONE", + [VHOST_USER_GET_FEATURES] = "VHOST_USER_GET_FEATURES", + [VHOST_USER_SET_FEATURES] = "VHOST_USER_SET_FEATURES", + [VHOST_USER_SET_OWNER] = "VHOST_USER_SET_OWNER", + [VHOST_USER_RESET_OWNER] = "VHOST_USER_RESET_OWNER", + [VHOST_USER_SET_MEM_TABLE] = "VHOST_USER_SET_MEM_TABLE", + [VHOST_USER_SET_LOG_BASE] = "VHOST_USER_SET_LOG_BASE", + [VHOST_USER_SET_LOG_FD] = "VHOST_USER_SET_LOG_FD", + [VHOST_USER_SET_VRING_NUM] = "VHOST_USER_SET_VRING_NUM", + [VHOST_USER_SET_VRING_ADDR] = "VHOST_USER_SET_VRING_ADDR", + [VHOST_USER_SET_VRING_BASE] = "VHOST_USER_SET_VRING_BASE", + [VHOST_USER_GET_VRING_BASE] = "VHOST_USER_GET_VRING_BASE", + [VHOST_USER_SET_VRING_KICK] = "VHOST_USER_SET_VRING_KICK", + [VHOST_USER_SET_VRING_CALL] = "VHOST_USER_SET_VRING_CALL", + [VHOST_USER_SET_VRING_ERR] = "VHOST_USER_SET_VRING_ERR", + [VHOST_USER_GET_PROTOCOL_FEATURES] = "VHOST_USER_GET_PROTOCOL_FEATURES", + [VHOST_USER_SET_PROTOCOL_FEATURES] = "VHOST_USER_SET_PROTOCOL_FEATURES", + [VHOST_USER_GET_QUEUE_NUM] = "VHOST_USER_GET_QUEUE_NUM", + [VHOST_USER_SET_VRING_ENABLE] = "VHOST_USER_SET_VRING_ENABLE", + [VHOST_USER_SEND_RARP] = "VHOST_USER_SEND_RARP", + [VHOST_USER_MAX] = "VHOST_USER_MAX", +}; + +static void +print_buffer(uint8_t *buf, size_t len) +{ + int i; + printf("Raw buffer:\n"); + for (i = 0; i < len; i++) { + if (i % 16 == 0) { + printf("\n"); + } + if (i % 4 == 0) { + printf(" "); + } + printf("%02x ", buf[i]); + } + printf("\n............................................................\n"); +} + +/* Translate guest physical address to our virtual address. */ +static uint64_t +gpa_to_va(VubrDev *dev, uint64_t guest_addr) +{ + int i; + + /* Find matching memory region. */ + for (i = 0; i < dev->nregions; i++) { + VubrDevRegion *r = &dev->regions[i]; + + if ((guest_addr >= r->gpa) && (guest_addr < (r->gpa + r->size))) { + return guest_addr - r->gpa + r->mmap_addr + r->mmap_offset; + } + } + + assert(!"address not found in regions"); + return 0; +} + +/* Translate qemu virtual address to our virtual address. */ +static uint64_t +qva_to_va(VubrDev *dev, uint64_t qemu_addr) +{ + int i; + + /* Find matching memory region. */ + for (i = 0; i < dev->nregions; i++) { + VubrDevRegion *r = &dev->regions[i]; + + if ((qemu_addr >= r->qva) && (qemu_addr < (r->qva + r->size))) { + return qemu_addr - r->qva + r->mmap_addr + r->mmap_offset; + } + } + + assert(!"address not found in regions"); + return 0; +} + +static void +vubr_message_read(int conn_fd, VhostUserMsg *vmsg) +{ + char control[CMSG_SPACE(VHOST_MEMORY_MAX_NREGIONS * sizeof(int))] = { }; + struct iovec iov = { + .iov_base = (char *)vmsg, + .iov_len = VHOST_USER_HDR_SIZE, + }; + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = control, + .msg_controllen = sizeof(control), + }; + size_t fd_size; + struct cmsghdr *cmsg; + int rc; + + rc = recvmsg(conn_fd, &msg, 0); + + if (rc == 0) { + vubr_die("recvmsg"); + fprintf(stderr, "Peer disconnected.\n"); + exit(1); + } + if (rc < 0) { + vubr_die("recvmsg"); + } + + vmsg->fd_num = 0; + for (cmsg = CMSG_FIRSTHDR(&msg); + cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + fd_size = cmsg->cmsg_len - CMSG_LEN(0); + vmsg->fd_num = fd_size / sizeof(int); + memcpy(vmsg->fds, CMSG_DATA(cmsg), fd_size); + break; + } + } + + if (vmsg->size > sizeof(vmsg->payload)) { + fprintf(stderr, + "Error: too big message request: %d, size: vmsg->size: %u, " + "while sizeof(vmsg->payload) = %zu\n", + vmsg->request, vmsg->size, sizeof(vmsg->payload)); + exit(1); + } + + if (vmsg->size) { + rc = read(conn_fd, &vmsg->payload, vmsg->size); + if (rc == 0) { + vubr_die("recvmsg"); + fprintf(stderr, "Peer disconnected.\n"); + exit(1); + } + if (rc < 0) { + vubr_die("recvmsg"); + } + + assert(rc == vmsg->size); + } +} + +static void +vubr_message_write(int conn_fd, VhostUserMsg *vmsg) +{ + int rc; + + do { + rc = write(conn_fd, vmsg, VHOST_USER_HDR_SIZE + vmsg->size); + } while (rc < 0 && errno == EINTR); + + if (rc < 0) { + vubr_die("write"); + } +} + +static void +vubr_backend_udp_sendbuf(VubrDev *dev, uint8_t *buf, size_t len) +{ + int slen = sizeof(struct sockaddr_in); + + if (sendto(dev->backend_udp_sock, buf, len, 0, + (struct sockaddr *) &dev->backend_udp_dest, slen) == -1) { + vubr_die("sendto()"); + } +} + +static int +vubr_backend_udp_recvbuf(VubrDev *dev, uint8_t *buf, size_t buflen) +{ + int slen = sizeof(struct sockaddr_in); + int rc; + + rc = recvfrom(dev->backend_udp_sock, buf, buflen, 0, + (struct sockaddr *) &dev->backend_udp_dest, + (socklen_t *)&slen); + if (rc == -1) { + vubr_die("recvfrom()"); + } + + return rc; +} + +static void +vubr_consume_raw_packet(VubrDev *dev, uint8_t *buf, uint32_t len) +{ + int hdrlen = dev->hdrlen; + DPRINT(" hdrlen = %d\n", dev->hdrlen); + + if (VHOST_USER_BRIDGE_DEBUG) { + print_buffer(buf, len); + } + vubr_backend_udp_sendbuf(dev, buf + hdrlen, len - hdrlen); +} + +/* Kick the log_call_fd if required. */ +static void +vubr_log_kick(VubrDev *dev) +{ + if (dev->log_call_fd != -1) { + DPRINT("Kicking the QEMU's log...\n"); + eventfd_write(dev->log_call_fd, 1); + } +} + +/* Kick the guest if necessary. */ +static void +vubr_virtqueue_kick(VubrVirtq *vq) +{ + if (!(vq->avail->flags & VRING_AVAIL_F_NO_INTERRUPT)) { + DPRINT("Kicking the guest...\n"); + eventfd_write(vq->call_fd, 1); + } +} + +static void +vubr_log_page(uint8_t *log_table, uint64_t page) +{ + DPRINT("Logged dirty guest page: %"PRId64"\n", page); + atomic_or(&log_table[page / 8], 1 << (page % 8)); +} + +static void +vubr_log_write(VubrDev *dev, uint64_t address, uint64_t length) +{ + uint64_t page; + + if (!(dev->features & (1ULL << VHOST_F_LOG_ALL)) || + !dev->log_table || !length) { + return; + } + + assert(dev->log_size > ((address + length - 1) / VHOST_LOG_PAGE / 8)); + + page = address / VHOST_LOG_PAGE; + while (page * VHOST_LOG_PAGE < address + length) { + vubr_log_page(dev->log_table, page); + page += VHOST_LOG_PAGE; + } + vubr_log_kick(dev); +} + +static void +vubr_post_buffer(VubrDev *dev, VubrVirtq *vq, uint8_t *buf, int32_t len) +{ + struct vring_desc *desc = vq->desc; + struct vring_avail *avail = vq->avail; + struct vring_used *used = vq->used; + uint64_t log_guest_addr = vq->log_guest_addr; + int32_t remaining_len = len; + + unsigned int size = vq->size; + + uint16_t avail_index = atomic_mb_read(&avail->idx); + + /* We check the available descriptors before posting the + * buffer, so here we assume that enough available + * descriptors. */ + assert(vq->last_avail_index != avail_index); + uint16_t a_index = vq->last_avail_index % size; + uint16_t u_index = vq->last_used_index % size; + uint16_t d_index = avail->ring[a_index]; + + int i = d_index; + uint32_t written_len = 0; + + do { + DPRINT("Post packet to guest on vq:\n"); + DPRINT(" size = %d\n", vq->size); + DPRINT(" last_avail_index = %d\n", vq->last_avail_index); + DPRINT(" last_used_index = %d\n", vq->last_used_index); + DPRINT(" a_index = %d\n", a_index); + DPRINT(" u_index = %d\n", u_index); + DPRINT(" d_index = %d\n", d_index); + DPRINT(" desc[%d].addr = 0x%016"PRIx64"\n", i, desc[i].addr); + DPRINT(" desc[%d].len = %d\n", i, desc[i].len); + DPRINT(" desc[%d].flags = %d\n", i, desc[i].flags); + DPRINT(" avail->idx = %d\n", avail_index); + DPRINT(" used->idx = %d\n", used->idx); + + if (!(desc[i].flags & VRING_DESC_F_WRITE)) { + /* FIXME: we should find writable descriptor. */ + fprintf(stderr, "Error: descriptor is not writable. Exiting.\n"); + exit(1); + } + + void *chunk_start = (void *)(uintptr_t)gpa_to_va(dev, desc[i].addr); + uint32_t chunk_len = desc[i].len; + uint32_t chunk_write_len = MIN(remaining_len, chunk_len); + + memcpy(chunk_start, buf + written_len, chunk_write_len); + vubr_log_write(dev, desc[i].addr, chunk_write_len); + remaining_len -= chunk_write_len; + written_len += chunk_write_len; + + if ((remaining_len == 0) || !(desc[i].flags & VRING_DESC_F_NEXT)) { + break; + } + + i = desc[i].next; + } while (1); + + if (remaining_len > 0) { + fprintf(stderr, + "Too long packet for RX, remaining_len = %d, Dropping...\n", + remaining_len); + return; + } + + /* Add descriptor to the used ring. */ + used->ring[u_index].id = d_index; + used->ring[u_index].len = len; + vubr_log_write(dev, + log_guest_addr + offsetof(struct vring_used, ring[u_index]), + sizeof(used->ring[u_index])); + + vq->last_avail_index++; + vq->last_used_index++; + + atomic_mb_set(&used->idx, vq->last_used_index); + vubr_log_write(dev, + log_guest_addr + offsetof(struct vring_used, idx), + sizeof(used->idx)); + + /* Kick the guest if necessary. */ + vubr_virtqueue_kick(vq); +} + +static int +vubr_process_desc(VubrDev *dev, VubrVirtq *vq) +{ + struct vring_desc *desc = vq->desc; + struct vring_avail *avail = vq->avail; + struct vring_used *used = vq->used; + uint64_t log_guest_addr = vq->log_guest_addr; + + unsigned int size = vq->size; + + uint16_t a_index = vq->last_avail_index % size; + uint16_t u_index = vq->last_used_index % size; + uint16_t d_index = avail->ring[a_index]; + + uint32_t i, len = 0; + size_t buf_size = 4096; + uint8_t buf[4096]; + + DPRINT("Chunks: "); + i = d_index; + do { + void *chunk_start = (void *)(uintptr_t)gpa_to_va(dev, desc[i].addr); + uint32_t chunk_len = desc[i].len; + + assert(!(desc[i].flags & VRING_DESC_F_WRITE)); + + if (len + chunk_len < buf_size) { + memcpy(buf + len, chunk_start, chunk_len); + DPRINT("%d ", chunk_len); + } else { + fprintf(stderr, "Error: too long packet. Dropping...\n"); + break; + } + + len += chunk_len; + + if (!(desc[i].flags & VRING_DESC_F_NEXT)) { + break; + } + + i = desc[i].next; + } while (1); + DPRINT("\n"); + + if (!len) { + return -1; + } + + /* Add descriptor to the used ring. */ + used->ring[u_index].id = d_index; + used->ring[u_index].len = len; + vubr_log_write(dev, + log_guest_addr + offsetof(struct vring_used, ring[u_index]), + sizeof(used->ring[u_index])); + + vubr_consume_raw_packet(dev, buf, len); + + return 0; +} + +static void +vubr_process_avail(VubrDev *dev, VubrVirtq *vq) +{ + struct vring_avail *avail = vq->avail; + struct vring_used *used = vq->used; + uint64_t log_guest_addr = vq->log_guest_addr; + + while (vq->last_avail_index != atomic_mb_read(&avail->idx)) { + vubr_process_desc(dev, vq); + vq->last_avail_index++; + vq->last_used_index++; + } + + atomic_mb_set(&used->idx, vq->last_used_index); + vubr_log_write(dev, + log_guest_addr + offsetof(struct vring_used, idx), + sizeof(used->idx)); +} + +static void +vubr_backend_recv_cb(int sock, void *ctx) +{ + VubrDev *dev = (VubrDev *) ctx; + VubrVirtq *rx_vq = &dev->vq[0]; + uint8_t buf[4096]; + struct virtio_net_hdr_v1 *hdr = (struct virtio_net_hdr_v1 *)buf; + int hdrlen = dev->hdrlen; + int buflen = sizeof(buf); + int len; + + if (!dev->ready) { + return; + } + + DPRINT("\n\n *** IN UDP RECEIVE CALLBACK ***\n\n"); + DPRINT(" hdrlen = %d\n", hdrlen); + + uint16_t avail_index = atomic_mb_read(&rx_vq->avail->idx); + + /* If there is no available descriptors, just do nothing. + * The buffer will be handled by next arrived UDP packet, + * or next kick on receive virtq. */ + if (rx_vq->last_avail_index == avail_index) { + DPRINT("Got UDP packet, but no available descriptors on RX virtq.\n"); + return; + } + + memset(buf, 0, hdrlen); + /* TODO: support mergeable buffers. */ + if (hdrlen == 12) + hdr->num_buffers = 1; + len = vubr_backend_udp_recvbuf(dev, buf + hdrlen, buflen - hdrlen); + + vubr_post_buffer(dev, rx_vq, buf, len + hdrlen); +} + +static void +vubr_kick_cb(int sock, void *ctx) +{ + VubrDev *dev = (VubrDev *) ctx; + eventfd_t kick_data; + ssize_t rc; + + rc = eventfd_read(sock, &kick_data); + if (rc == -1) { + vubr_die("eventfd_read()"); + } else { + DPRINT("Got kick_data: %016"PRIx64"\n", kick_data); + vubr_process_avail(dev, &dev->vq[1]); + } +} + +static int +vubr_none_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + DPRINT("Function %s() not implemented yet.\n", __func__); + return 0; +} + +static int +vubr_get_features_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + vmsg->payload.u64 = + ((1ULL << VIRTIO_NET_F_MRG_RXBUF) | + (1ULL << VHOST_F_LOG_ALL) | + (1ULL << VIRTIO_NET_F_GUEST_ANNOUNCE) | + (1ULL << VHOST_USER_F_PROTOCOL_FEATURES)); + + vmsg->size = sizeof(vmsg->payload.u64); + + DPRINT("Sending back to guest u64: 0x%016"PRIx64"\n", vmsg->payload.u64); + + /* Reply */ + return 1; +} + +static int +vubr_set_features_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + DPRINT("u64: 0x%016"PRIx64"\n", vmsg->payload.u64); + + dev->features = vmsg->payload.u64; + if ((dev->features & (1ULL << VIRTIO_F_VERSION_1)) || + (dev->features & (1ULL << VIRTIO_NET_F_MRG_RXBUF))) { + dev->hdrlen = 12; + } else { + dev->hdrlen = 10; + } + + return 0; +} + +static int +vubr_set_owner_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + return 0; +} + +static void +vubr_close_log(VubrDev *dev) +{ + if (dev->log_table) { + if (munmap(dev->log_table, dev->log_size) != 0) { + vubr_die("munmap()"); + } + + dev->log_table = 0; + } + if (dev->log_call_fd != -1) { + close(dev->log_call_fd); + dev->log_call_fd = -1; + } +} + +static int +vubr_reset_device_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + vubr_close_log(dev); + dev->ready = 0; + dev->features = 0; + return 0; +} + +static int +vubr_set_mem_table_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + int i; + VhostUserMemory *memory = &vmsg->payload.memory; + dev->nregions = memory->nregions; + + DPRINT("Nregions: %d\n", memory->nregions); + for (i = 0; i < dev->nregions; i++) { + void *mmap_addr; + VhostUserMemoryRegion *msg_region = &memory->regions[i]; + VubrDevRegion *dev_region = &dev->regions[i]; + + DPRINT("Region %d\n", i); + DPRINT(" guest_phys_addr: 0x%016"PRIx64"\n", + msg_region->guest_phys_addr); + DPRINT(" memory_size: 0x%016"PRIx64"\n", + msg_region->memory_size); + DPRINT(" userspace_addr 0x%016"PRIx64"\n", + msg_region->userspace_addr); + DPRINT(" mmap_offset 0x%016"PRIx64"\n", + msg_region->mmap_offset); + + dev_region->gpa = msg_region->guest_phys_addr; + dev_region->size = msg_region->memory_size; + dev_region->qva = msg_region->userspace_addr; + dev_region->mmap_offset = msg_region->mmap_offset; + + /* We don't use offset argument of mmap() since the + * mapped address has to be page aligned, and we use huge + * pages. */ + mmap_addr = mmap(0, dev_region->size + dev_region->mmap_offset, + PROT_READ | PROT_WRITE, MAP_SHARED, + vmsg->fds[i], 0); + + if (mmap_addr == MAP_FAILED) { + vubr_die("mmap"); + } + dev_region->mmap_addr = (uint64_t)(uintptr_t)mmap_addr; + DPRINT(" mmap_addr: 0x%016"PRIx64"\n", dev_region->mmap_addr); + + close(vmsg->fds[i]); + } + + return 0; +} + +static int +vubr_set_log_base_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + int fd; + uint64_t log_mmap_size, log_mmap_offset; + void *rc; + + assert(vmsg->fd_num == 1); + fd = vmsg->fds[0]; + + assert(vmsg->size == sizeof(vmsg->payload.log)); + log_mmap_offset = vmsg->payload.log.mmap_offset; + log_mmap_size = vmsg->payload.log.mmap_size; + DPRINT("Log mmap_offset: %"PRId64"\n", log_mmap_offset); + DPRINT("Log mmap_size: %"PRId64"\n", log_mmap_size); + + rc = mmap(0, log_mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, + log_mmap_offset); + if (rc == MAP_FAILED) { + vubr_die("mmap"); + } + dev->log_table = rc; + dev->log_size = log_mmap_size; + + vmsg->size = sizeof(vmsg->payload.u64); + /* Reply */ + return 1; +} + +static int +vubr_set_log_fd_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + assert(vmsg->fd_num == 1); + dev->log_call_fd = vmsg->fds[0]; + DPRINT("Got log_call_fd: %d\n", vmsg->fds[0]); + return 0; +} + +static int +vubr_set_vring_num_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + unsigned int index = vmsg->payload.state.index; + unsigned int num = vmsg->payload.state.num; + + DPRINT("State.index: %d\n", index); + DPRINT("State.num: %d\n", num); + dev->vq[index].size = num; + return 0; +} + +static int +vubr_set_vring_addr_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + struct vhost_vring_addr *vra = &vmsg->payload.addr; + unsigned int index = vra->index; + VubrVirtq *vq = &dev->vq[index]; + + DPRINT("vhost_vring_addr:\n"); + DPRINT(" index: %d\n", vra->index); + DPRINT(" flags: %d\n", vra->flags); + DPRINT(" desc_user_addr: 0x%016llx\n", vra->desc_user_addr); + DPRINT(" used_user_addr: 0x%016llx\n", vra->used_user_addr); + DPRINT(" avail_user_addr: 0x%016llx\n", vra->avail_user_addr); + DPRINT(" log_guest_addr: 0x%016llx\n", vra->log_guest_addr); + + vq->desc = (struct vring_desc *)(uintptr_t)qva_to_va(dev, vra->desc_user_addr); + vq->used = (struct vring_used *)(uintptr_t)qva_to_va(dev, vra->used_user_addr); + vq->avail = (struct vring_avail *)(uintptr_t)qva_to_va(dev, vra->avail_user_addr); + vq->log_guest_addr = vra->log_guest_addr; + + DPRINT("Setting virtq addresses:\n"); + DPRINT(" vring_desc at %p\n", vq->desc); + DPRINT(" vring_used at %p\n", vq->used); + DPRINT(" vring_avail at %p\n", vq->avail); + + vq->last_used_index = vq->used->idx; + return 0; +} + +static int +vubr_set_vring_base_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + unsigned int index = vmsg->payload.state.index; + unsigned int num = vmsg->payload.state.num; + + DPRINT("State.index: %d\n", index); + DPRINT("State.num: %d\n", num); + dev->vq[index].last_avail_index = num; + + return 0; +} + +static int +vubr_get_vring_base_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + unsigned int index = vmsg->payload.state.index; + + DPRINT("State.index: %d\n", index); + vmsg->payload.state.num = dev->vq[index].last_avail_index; + vmsg->size = sizeof(vmsg->payload.state); + /* FIXME: this is a work-around for a bug in QEMU enabling + * too early vrings. When protocol features are enabled, + * we have to respect * VHOST_USER_SET_VRING_ENABLE request. */ + dev->ready = 0; + + if (dev->vq[index].call_fd != -1) { + close(dev->vq[index].call_fd); + dispatcher_remove(&dev->dispatcher, dev->vq[index].call_fd); + dev->vq[index].call_fd = -1; + } + if (dev->vq[index].kick_fd != -1) { + close(dev->vq[index].kick_fd); + dispatcher_remove(&dev->dispatcher, dev->vq[index].kick_fd); + dev->vq[index].kick_fd = -1; + } + + /* Reply */ + return 1; +} + +static int +vubr_set_vring_kick_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + uint64_t u64_arg = vmsg->payload.u64; + int index = u64_arg & VHOST_USER_VRING_IDX_MASK; + + DPRINT("u64: 0x%016"PRIx64"\n", vmsg->payload.u64); + + assert((u64_arg & VHOST_USER_VRING_NOFD_MASK) == 0); + assert(vmsg->fd_num == 1); + + if (dev->vq[index].kick_fd != -1) { + close(dev->vq[index].kick_fd); + dispatcher_remove(&dev->dispatcher, dev->vq[index].kick_fd); + } + dev->vq[index].kick_fd = vmsg->fds[0]; + DPRINT("Got kick_fd: %d for vq: %d\n", vmsg->fds[0], index); + + if (index % 2 == 1) { + /* TX queue. */ + dispatcher_add(&dev->dispatcher, dev->vq[index].kick_fd, + dev, vubr_kick_cb); + + DPRINT("Waiting for kicks on fd: %d for vq: %d\n", + dev->vq[index].kick_fd, index); + } + /* We temporarily use this hack to determine that both TX and RX + * queues are set up and ready for processing. + * FIXME: we need to rely in VHOST_USER_SET_VRING_ENABLE and + * actual kicks. */ + if (dev->vq[0].kick_fd != -1 && + dev->vq[1].kick_fd != -1) { + dev->ready = 1; + DPRINT("vhost-user-bridge is ready for processing queues.\n"); + } + return 0; + +} + +static int +vubr_set_vring_call_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + uint64_t u64_arg = vmsg->payload.u64; + int index = u64_arg & VHOST_USER_VRING_IDX_MASK; + + DPRINT("u64: 0x%016"PRIx64"\n", vmsg->payload.u64); + assert((u64_arg & VHOST_USER_VRING_NOFD_MASK) == 0); + assert(vmsg->fd_num == 1); + + if (dev->vq[index].call_fd != -1) { + close(dev->vq[index].call_fd); + dispatcher_remove(&dev->dispatcher, dev->vq[index].call_fd); + } + dev->vq[index].call_fd = vmsg->fds[0]; + DPRINT("Got call_fd: %d for vq: %d\n", vmsg->fds[0], index); + + return 0; +} + +static int +vubr_set_vring_err_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + DPRINT("u64: 0x%016"PRIx64"\n", vmsg->payload.u64); + return 0; +} + +static int +vubr_get_protocol_features_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + vmsg->payload.u64 = 1ULL << VHOST_USER_PROTOCOL_F_LOG_SHMFD; + DPRINT("u64: 0x%016"PRIx64"\n", vmsg->payload.u64); + vmsg->size = sizeof(vmsg->payload.u64); + + /* Reply */ + return 1; +} + +static int +vubr_set_protocol_features_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + /* FIXME: unimplented */ + DPRINT("u64: 0x%016"PRIx64"\n", vmsg->payload.u64); + return 0; +} + +static int +vubr_get_queue_num_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + DPRINT("Function %s() not implemented yet.\n", __func__); + return 0; +} + +static int +vubr_set_vring_enable_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + unsigned int index = vmsg->payload.state.index; + unsigned int enable = vmsg->payload.state.num; + + DPRINT("State.index: %d\n", index); + DPRINT("State.enable: %d\n", enable); + dev->vq[index].enable = enable; + return 0; +} + +static int +vubr_send_rarp_exec(VubrDev *dev, VhostUserMsg *vmsg) +{ + DPRINT("Function %s() not implemented yet.\n", __func__); + return 0; +} + +static int +vubr_execute_request(VubrDev *dev, VhostUserMsg *vmsg) +{ + /* Print out generic part of the request. */ + DPRINT( + "================== Vhost user message from QEMU ==================\n"); + DPRINT("Request: %s (%d)\n", vubr_request_str[vmsg->request], + vmsg->request); + DPRINT("Flags: 0x%x\n", vmsg->flags); + DPRINT("Size: %d\n", vmsg->size); + + if (vmsg->fd_num) { + int i; + DPRINT("Fds:"); + for (i = 0; i < vmsg->fd_num; i++) { + DPRINT(" %d", vmsg->fds[i]); + } + DPRINT("\n"); + } + + switch (vmsg->request) { + case VHOST_USER_NONE: + return vubr_none_exec(dev, vmsg); + case VHOST_USER_GET_FEATURES: + return vubr_get_features_exec(dev, vmsg); + case VHOST_USER_SET_FEATURES: + return vubr_set_features_exec(dev, vmsg); + case VHOST_USER_SET_OWNER: + return vubr_set_owner_exec(dev, vmsg); + case VHOST_USER_RESET_OWNER: + return vubr_reset_device_exec(dev, vmsg); + case VHOST_USER_SET_MEM_TABLE: + return vubr_set_mem_table_exec(dev, vmsg); + case VHOST_USER_SET_LOG_BASE: + return vubr_set_log_base_exec(dev, vmsg); + case VHOST_USER_SET_LOG_FD: + return vubr_set_log_fd_exec(dev, vmsg); + case VHOST_USER_SET_VRING_NUM: + return vubr_set_vring_num_exec(dev, vmsg); + case VHOST_USER_SET_VRING_ADDR: + return vubr_set_vring_addr_exec(dev, vmsg); + case VHOST_USER_SET_VRING_BASE: + return vubr_set_vring_base_exec(dev, vmsg); + case VHOST_USER_GET_VRING_BASE: + return vubr_get_vring_base_exec(dev, vmsg); + case VHOST_USER_SET_VRING_KICK: + return vubr_set_vring_kick_exec(dev, vmsg); + case VHOST_USER_SET_VRING_CALL: + return vubr_set_vring_call_exec(dev, vmsg); + case VHOST_USER_SET_VRING_ERR: + return vubr_set_vring_err_exec(dev, vmsg); + case VHOST_USER_GET_PROTOCOL_FEATURES: + return vubr_get_protocol_features_exec(dev, vmsg); + case VHOST_USER_SET_PROTOCOL_FEATURES: + return vubr_set_protocol_features_exec(dev, vmsg); + case VHOST_USER_GET_QUEUE_NUM: + return vubr_get_queue_num_exec(dev, vmsg); + case VHOST_USER_SET_VRING_ENABLE: + return vubr_set_vring_enable_exec(dev, vmsg); + case VHOST_USER_SEND_RARP: + return vubr_send_rarp_exec(dev, vmsg); + + case VHOST_USER_MAX: + assert(vmsg->request != VHOST_USER_MAX); + } + return 0; +} + +static void +vubr_receive_cb(int sock, void *ctx) +{ + VubrDev *dev = (VubrDev *) ctx; + VhostUserMsg vmsg; + int reply_requested; + + vubr_message_read(sock, &vmsg); + reply_requested = vubr_execute_request(dev, &vmsg); + if (reply_requested) { + /* Set the version in the flags when sending the reply */ + vmsg.flags &= ~VHOST_USER_VERSION_MASK; + vmsg.flags |= VHOST_USER_VERSION; + vmsg.flags |= VHOST_USER_REPLY_MASK; + vubr_message_write(sock, &vmsg); + } +} + +static void +vubr_accept_cb(int sock, void *ctx) +{ + VubrDev *dev = (VubrDev *)ctx; + int conn_fd; + struct sockaddr_un un; + socklen_t len = sizeof(un); + + conn_fd = accept(sock, (struct sockaddr *) &un, &len); + if (conn_fd == -1) { + vubr_die("accept()"); + } + DPRINT("Got connection from remote peer on sock %d\n", conn_fd); + dispatcher_add(&dev->dispatcher, conn_fd, ctx, vubr_receive_cb); +} + +static VubrDev * +vubr_new(const char *path) +{ + VubrDev *dev = (VubrDev *) calloc(1, sizeof(VubrDev)); + dev->nregions = 0; + int i; + struct sockaddr_un un; + size_t len; + + for (i = 0; i < MAX_NR_VIRTQUEUE; i++) { + dev->vq[i] = (VubrVirtq) { + .call_fd = -1, .kick_fd = -1, + .size = 0, + .last_avail_index = 0, .last_used_index = 0, + .desc = 0, .avail = 0, .used = 0, + .enable = 0, + }; + } + + /* Init log */ + dev->log_call_fd = -1; + dev->log_size = 0; + dev->log_table = 0; + dev->ready = 0; + dev->features = 0; + + /* Get a UNIX socket. */ + dev->sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (dev->sock == -1) { + vubr_die("socket"); + } + + un.sun_family = AF_UNIX; + strcpy(un.sun_path, path); + len = sizeof(un.sun_family) + strlen(path); + unlink(path); + + if (bind(dev->sock, (struct sockaddr *) &un, len) == -1) { + vubr_die("bind"); + } + + if (listen(dev->sock, 1) == -1) { + vubr_die("listen"); + } + + dispatcher_init(&dev->dispatcher); + dispatcher_add(&dev->dispatcher, dev->sock, (void *)dev, + vubr_accept_cb); + + DPRINT("Waiting for connections on UNIX socket %s ...\n", path); + return dev; +} + +static void +vubr_set_host(struct sockaddr_in *saddr, const char *host) +{ + if (isdigit(host[0])) { + if (!inet_aton(host, &saddr->sin_addr)) { + fprintf(stderr, "inet_aton() failed.\n"); + exit(1); + } + } else { + struct hostent *he = gethostbyname(host); + + if (!he) { + fprintf(stderr, "gethostbyname() failed.\n"); + exit(1); + } + saddr->sin_addr = *(struct in_addr *)he->h_addr; + } +} + +static void +vubr_backend_udp_setup(VubrDev *dev, + const char *local_host, + const char *local_port, + const char *remote_host, + const char *remote_port) +{ + int sock; + const char *r; + + int lport, rport; + + lport = strtol(local_port, (char **)&r, 0); + if (r == local_port) { + fprintf(stderr, "lport parsing failed.\n"); + exit(1); + } + + rport = strtol(remote_port, (char **)&r, 0); + if (r == remote_port) { + fprintf(stderr, "rport parsing failed.\n"); + exit(1); + } + + struct sockaddr_in si_local = { + .sin_family = AF_INET, + .sin_port = htons(lport), + }; + + vubr_set_host(&si_local, local_host); + + /* setup destination for sends */ + dev->backend_udp_dest = (struct sockaddr_in) { + .sin_family = AF_INET, + .sin_port = htons(rport), + }; + vubr_set_host(&dev->backend_udp_dest, remote_host); + + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == -1) { + vubr_die("socket"); + } + + if (bind(sock, (struct sockaddr *)&si_local, sizeof(si_local)) == -1) { + vubr_die("bind"); + } + + dev->backend_udp_sock = sock; + dispatcher_add(&dev->dispatcher, sock, dev, vubr_backend_recv_cb); + DPRINT("Waiting for data from udp backend on %s:%d...\n", + local_host, lport); +} + +static void +vubr_run(VubrDev *dev) +{ + while (1) { + /* timeout 200ms */ + dispatcher_wait(&dev->dispatcher, 200000); + /* Here one can try polling strategy. */ + } +} + +static int +vubr_parse_host_port(const char **host, const char **port, const char *buf) +{ + char *p = strchr(buf, ':'); + + if (!p) { + return -1; + } + *p = '\0'; + *host = strdup(buf); + *port = strdup(p + 1); + return 0; +} + +#define DEFAULT_UD_SOCKET "/tmp/vubr.sock" +#define DEFAULT_LHOST "127.0.0.1" +#define DEFAULT_LPORT "4444" +#define DEFAULT_RHOST "127.0.0.1" +#define DEFAULT_RPORT "5555" + +static const char *ud_socket_path = DEFAULT_UD_SOCKET; +static const char *lhost = DEFAULT_LHOST; +static const char *lport = DEFAULT_LPORT; +static const char *rhost = DEFAULT_RHOST; +static const char *rport = DEFAULT_RPORT; + +int +main(int argc, char *argv[]) +{ + VubrDev *dev; + int opt; + + while ((opt = getopt(argc, argv, "l:r:u:")) != -1) { + + switch (opt) { + case 'l': + if (vubr_parse_host_port(&lhost, &lport, optarg) < 0) { + goto out; + } + break; + case 'r': + if (vubr_parse_host_port(&rhost, &rport, optarg) < 0) { + goto out; + } + break; + case 'u': + ud_socket_path = strdup(optarg); + break; + default: + goto out; + } + } + + DPRINT("ud socket: %s\n", ud_socket_path); + DPRINT("local: %s:%s\n", lhost, lport); + DPRINT("remote: %s:%s\n", rhost, rport); + + dev = vubr_new(ud_socket_path); + if (!dev) { + return 1; + } + + vubr_backend_udp_setup(dev, lhost, lport, rhost, rport); + vubr_run(dev); + return 0; + +out: + fprintf(stderr, "Usage: %s ", argv[0]); + fprintf(stderr, "[-u ud_socket_path] [-l lhost:lport] [-r rhost:rport]\n"); + fprintf(stderr, "\t-u path to unix doman socket. default: %s\n", + DEFAULT_UD_SOCKET); + fprintf(stderr, "\t-l local host and port. default: %s:%s\n", + DEFAULT_LHOST, DEFAULT_LPORT); + fprintf(stderr, "\t-r remote host and port. default: %s:%s\n", + DEFAULT_RHOST, DEFAULT_RPORT); + + return 1; +} diff --git a/qemu/tests/vhost-user-test.c b/qemu/tests/vhost-user-test.c index 75fedf097..69615968c 100644 --- a/qemu/tests/vhost-user-test.c +++ b/qemu/tests/vhost-user-test.c @@ -8,11 +8,12 @@ * */ -#define QEMU_GLIB_COMPAT_H +#include "qemu/osdep.h" #include #include "libqtest.h" #include "qemu/option.h" +#include "qemu/range.h" #include "sysemu/char.h" #include "sysemu/sysemu.h" @@ -30,22 +31,15 @@ #define HAVE_MONOTONIC_TIME #endif -#if GLIB_CHECK_VERSION(2, 32, 0) -#define HAVE_MUTEX_INIT -#define HAVE_COND_INIT -#define HAVE_THREAD_NEW -#endif - #define QEMU_CMD_ACCEL " -machine accel=tcg" -#define QEMU_CMD_MEM " -m 512 -object memory-backend-file,id=mem,size=512M,"\ +#define QEMU_CMD_MEM " -m %d -object memory-backend-file,id=mem,size=%dM,"\ "mem-path=%s,share=on -numa node,memdev=mem" -#define QEMU_CMD_CHR " -chardev socket,id=chr0,path=%s" -#define QEMU_CMD_NETDEV " -netdev vhost-user,id=net0,chardev=chr0,vhostforce" -#define QEMU_CMD_NET " -device virtio-net-pci,netdev=net0 " -#define QEMU_CMD_ROM " -option-rom ../pc-bios/pxe-virtio.rom" +#define QEMU_CMD_CHR " -chardev socket,id=%s,path=%s" +#define QEMU_CMD_NETDEV " -netdev vhost-user,id=net0,chardev=%s,vhostforce" +#define QEMU_CMD_NET " -device virtio-net-pci,netdev=net0,romfile=./pc-bios/pxe-virtio.rom" #define QEMU_CMD QEMU_CMD_ACCEL QEMU_CMD_MEM QEMU_CMD_CHR \ - QEMU_CMD_NETDEV QEMU_CMD_NET QEMU_CMD_ROM + QEMU_CMD_NETDEV QEMU_CMD_NET #define HUGETLBFS_MAGIC 0x958458f6 @@ -53,6 +47,11 @@ #define VHOST_MEMORY_MAX_NREGIONS 8 +#define VHOST_USER_F_PROTOCOL_FEATURES 30 +#define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1 + +#define VHOST_LOG_PAGE 0x1000 + typedef enum VhostUserRequest { VHOST_USER_NONE = 0, VHOST_USER_GET_FEATURES = 1, @@ -69,6 +68,9 @@ typedef enum VhostUserRequest { VHOST_USER_SET_VRING_KICK = 12, VHOST_USER_SET_VRING_CALL = 13, VHOST_USER_SET_VRING_ERR = 14, + VHOST_USER_GET_PROTOCOL_FEATURES = 15, + VHOST_USER_SET_PROTOCOL_FEATURES = 16, + VHOST_USER_SET_VRING_ENABLE = 18, VHOST_USER_MAX } VhostUserRequest; @@ -85,6 +87,11 @@ typedef struct VhostUserMemory { VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS]; } VhostUserMemory; +typedef struct VhostUserLog { + uint64_t mmap_size; + uint64_t mmap_offset; +} VhostUserLog; + typedef struct VhostUserMsg { VhostUserRequest request; @@ -93,11 +100,14 @@ typedef struct VhostUserMsg { uint32_t flags; uint32_t size; /* the following payload size */ union { +#define VHOST_USER_VRING_IDX_MASK (0xff) +#define VHOST_USER_VRING_NOFD_MASK (0x1<<8) uint64_t u64; struct vhost_vring_state state; struct vhost_vring_addr addr; VhostUserMemory memory; - }; + VhostUserLog log; + } payload; } QEMU_PACKED VhostUserMsg; static VhostUserMsg m __attribute__ ((unused)); @@ -111,154 +121,104 @@ static VhostUserMsg m __attribute__ ((unused)); #define VHOST_USER_VERSION (0x1) /*****************************************************************************/ -int fds_num = 0, fds[VHOST_MEMORY_MAX_NREGIONS]; -static VhostUserMemory memory; -static GMutex *data_mutex; -static GCond *data_cond; - -static gint64 _get_time(void) -{ -#ifdef HAVE_MONOTONIC_TIME - return g_get_monotonic_time(); -#else - GTimeVal time; - g_get_current_time(&time); - - return time.tv_sec * G_TIME_SPAN_SECOND + time.tv_usec; -#endif -} - -static GMutex *_mutex_new(void) -{ - GMutex *mutex; - -#ifdef HAVE_MUTEX_INIT - mutex = g_new(GMutex, 1); - g_mutex_init(mutex); -#else - mutex = g_mutex_new(); -#endif - - return mutex; -} - -static void _mutex_free(GMutex *mutex) -{ -#ifdef HAVE_MUTEX_INIT - g_mutex_clear(mutex); - g_free(mutex); -#else - g_mutex_free(mutex); -#endif -} - -static GCond *_cond_new(void) -{ - GCond *cond; - -#ifdef HAVE_COND_INIT - cond = g_new(GCond, 1); - g_cond_init(cond); -#else - cond = g_cond_new(); -#endif - - return cond; -} - -static gboolean _cond_wait_until(GCond *cond, GMutex *mutex, gint64 end_time) +typedef struct TestServer { + gchar *socket_path; + gchar *mig_path; + gchar *chr_name; + CharDriverState *chr; + int fds_num; + int fds[VHOST_MEMORY_MAX_NREGIONS]; + VhostUserMemory memory; + GMutex data_mutex; + GCond data_cond; + int log_fd; + uint64_t rings; +} TestServer; + +#if !GLIB_CHECK_VERSION(2, 32, 0) +static gboolean g_cond_wait_until(CompatGCond cond, CompatGMutex mutex, + gint64 end_time) { gboolean ret = FALSE; -#ifdef HAVE_COND_INIT - ret = g_cond_wait_until(cond, mutex, end_time); -#else + end_time -= g_get_monotonic_time(); GTimeVal time = { end_time / G_TIME_SPAN_SECOND, end_time % G_TIME_SPAN_SECOND }; ret = g_cond_timed_wait(cond, mutex, &time); -#endif return ret; } - -static void _cond_free(GCond *cond) -{ -#ifdef HAVE_COND_INIT - g_cond_clear(cond); - g_free(cond); -#else - g_cond_free(cond); #endif -} -static GThread *_thread_new(const gchar *name, GThreadFunc func, gpointer data) -{ - GThread *thread = NULL; - GError *error = NULL; -#ifdef HAVE_THREAD_NEW - thread = g_thread_try_new(name, func, data, &error); -#else - thread = g_thread_create(func, data, TRUE, &error); -#endif - return thread; -} +static const char *tmpfs; +static const char *root; -static void read_guest_mem(void) +static void wait_for_fds(TestServer *s) { - uint32_t *guest_mem; gint64 end_time; - int i, j; - size_t size; - g_mutex_lock(data_mutex); + g_mutex_lock(&s->data_mutex); - end_time = _get_time() + 5 * G_TIME_SPAN_SECOND; - while (!fds_num) { - if (!_cond_wait_until(data_cond, data_mutex, end_time)) { + end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; + while (!s->fds_num) { + if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { /* timeout has passed */ - g_assert(fds_num); + g_assert(s->fds_num); break; } } /* check for sanity */ - g_assert_cmpint(fds_num, >, 0); - g_assert_cmpint(fds_num, ==, memory.nregions); + g_assert_cmpint(s->fds_num, >, 0); + g_assert_cmpint(s->fds_num, ==, s->memory.nregions); + + g_mutex_unlock(&s->data_mutex); +} + +static void read_guest_mem(const void *data) +{ + TestServer *s = (void *)data; + uint32_t *guest_mem; + int i, j; + size_t size; + + wait_for_fds(s); + + g_mutex_lock(&s->data_mutex); /* iterate all regions */ - for (i = 0; i < fds_num; i++) { + for (i = 0; i < s->fds_num; i++) { /* We'll check only the region statring at 0x0*/ - if (memory.regions[i].guest_phys_addr != 0x0) { + if (s->memory.regions[i].guest_phys_addr != 0x0) { continue; } - g_assert_cmpint(memory.regions[i].memory_size, >, 1024); + g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024); - size = memory.regions[i].memory_size + memory.regions[i].mmap_offset; + size = s->memory.regions[i].memory_size + + s->memory.regions[i].mmap_offset; guest_mem = mmap(0, size, PROT_READ | PROT_WRITE, - MAP_SHARED, fds[i], 0); + MAP_SHARED, s->fds[i], 0); g_assert(guest_mem != MAP_FAILED); - guest_mem += (memory.regions[i].mmap_offset / sizeof(*guest_mem)); + guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem)); for (j = 0; j < 256; j++) { - uint32_t a = readl(memory.regions[i].guest_phys_addr + j*4); + uint32_t a = readl(s->memory.regions[i].guest_phys_addr + j*4); uint32_t b = guest_mem[j]; g_assert_cmpint(a, ==, b); } - munmap(guest_mem, memory.regions[i].memory_size); + munmap(guest_mem, s->memory.regions[i].memory_size); } - g_assert_cmpint(1, ==, 1); - g_mutex_unlock(data_mutex); + g_mutex_unlock(&s->data_mutex); } static void *thread_function(void *data) { - GMainLoop *loop; - loop = g_main_loop_new(NULL, FALSE); + GMainLoop *loop = data; g_main_loop_run(loop); return NULL; } @@ -270,7 +230,8 @@ static int chr_can_read(void *opaque) static void chr_read(void *opaque, const uint8_t *buf, int size) { - CharDriverState *chr = opaque; + TestServer *s = opaque; + CharDriverState *chr = s->chr; VhostUserMsg msg; uint8_t *p = (uint8_t *) &msg; int fd; @@ -280,20 +241,35 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) return; } - g_mutex_lock(data_mutex); + g_mutex_lock(&s->data_mutex); memcpy(p, buf, VHOST_USER_HDR_SIZE); if (msg.size) { p += VHOST_USER_HDR_SIZE; - qemu_chr_fe_read_all(chr, p, msg.size); + g_assert_cmpint(qemu_chr_fe_read_all(chr, p, msg.size), ==, msg.size); } switch (msg.request) { case VHOST_USER_GET_FEATURES: /* send back features to qemu */ msg.flags |= VHOST_USER_REPLY_MASK; - msg.size = sizeof(m.u64); - msg.u64 = 0; + msg.size = sizeof(m.payload.u64); + msg.payload.u64 = 0x1ULL << VHOST_F_LOG_ALL | + 0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES; + p = (uint8_t *) &msg; + qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); + break; + + case VHOST_USER_SET_FEATURES: + g_assert_cmpint(msg.payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES), + !=, 0ULL); + break; + + case VHOST_USER_GET_PROTOCOL_FEATURES: + /* send back features to qemu */ + msg.flags |= VHOST_USER_REPLY_MASK; + msg.size = sizeof(m.payload.u64); + msg.payload.u64 = 1 << VHOST_USER_PROTOCOL_F_LOG_SHMFD; p = (uint8_t *) &msg; qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); break; @@ -301,19 +277,22 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) case VHOST_USER_GET_VRING_BASE: /* send back vring base to qemu */ msg.flags |= VHOST_USER_REPLY_MASK; - msg.size = sizeof(m.state); - msg.state.num = 0; + msg.size = sizeof(m.payload.state); + msg.payload.state.num = 0; p = (uint8_t *) &msg; qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); + + assert(msg.payload.state.index < 2); + s->rings &= ~(0x1ULL << msg.payload.state.index); break; case VHOST_USER_SET_MEM_TABLE: /* received the mem table */ - memcpy(&memory, &msg.memory, sizeof(msg.memory)); - fds_num = qemu_chr_fe_get_msgfds(chr, fds, sizeof(fds) / sizeof(int)); + memcpy(&s->memory, &msg.payload.memory, sizeof(msg.payload.memory)); + s->fds_num = qemu_chr_fe_get_msgfds(chr, s->fds, G_N_ELEMENTS(s->fds)); /* signal the test that it can continue */ - g_cond_signal(data_cond); + g_cond_signal(&s->data_cond); break; case VHOST_USER_SET_VRING_KICK: @@ -327,23 +306,38 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) */ qemu_set_nonblock(fd); break; + + case VHOST_USER_SET_LOG_BASE: + if (s->log_fd != -1) { + close(s->log_fd); + s->log_fd = -1; + } + qemu_chr_fe_get_msgfds(chr, &s->log_fd, 1); + msg.flags |= VHOST_USER_REPLY_MASK; + msg.size = 0; + p = (uint8_t *) &msg; + qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE); + + g_cond_signal(&s->data_cond); + break; + + case VHOST_USER_SET_VRING_BASE: + assert(msg.payload.state.index < 2); + s->rings |= 0x1ULL << msg.payload.state.index; + break; + default: break; } - g_mutex_unlock(data_mutex); + + g_mutex_unlock(&s->data_mutex); } -static const char *init_hugepagefs(void) +static const char *init_hugepagefs(const char *path) { - const char *path; struct statfs fs; int ret; - path = getenv("QTEST_HUGETLBFS_PATH"); - if (!path) { - path = "/hugetlbfs"; - } - if (access(path, R_OK | W_OK | X_OK)) { g_test_message("access on path (%s): %s\n", path, strerror(errno)); return NULL; @@ -366,44 +360,294 @@ static const char *init_hugepagefs(void) return path; } +static TestServer *test_server_new(const gchar *name) +{ + TestServer *server = g_new0(TestServer, 1); + gchar *chr_path; + + server->socket_path = g_strdup_printf("%s/%s.sock", tmpfs, name); + server->mig_path = g_strdup_printf("%s/%s.mig", tmpfs, name); + + chr_path = g_strdup_printf("unix:%s,server,nowait", server->socket_path); + server->chr_name = g_strdup_printf("chr-%s", name); + server->chr = qemu_chr_new(server->chr_name, chr_path, NULL); + g_free(chr_path); + + qemu_chr_add_handlers(server->chr, chr_can_read, chr_read, NULL, server); + + g_mutex_init(&server->data_mutex); + g_cond_init(&server->data_cond); + + server->log_fd = -1; + + return server; +} + +#define GET_QEMU_CMD(s) \ + g_strdup_printf(QEMU_CMD, 512, 512, (root), (s)->chr_name, \ + (s)->socket_path, (s)->chr_name) + +#define GET_QEMU_CMDE(s, mem, extra, ...) \ + g_strdup_printf(QEMU_CMD extra, (mem), (mem), (root), (s)->chr_name, \ + (s)->socket_path, (s)->chr_name, ##__VA_ARGS__) + +static gboolean _test_server_free(TestServer *server) +{ + int i; + + qemu_chr_delete(server->chr); + + for (i = 0; i < server->fds_num; i++) { + close(server->fds[i]); + } + + if (server->log_fd != -1) { + close(server->log_fd); + } + + unlink(server->socket_path); + g_free(server->socket_path); + + unlink(server->mig_path); + g_free(server->mig_path); + + g_free(server->chr_name); + g_free(server); + + return FALSE; +} + +static void test_server_free(TestServer *server) +{ + g_idle_add((GSourceFunc)_test_server_free, server); +} + +static void wait_for_log_fd(TestServer *s) +{ + gint64 end_time; + + g_mutex_lock(&s->data_mutex); + end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; + while (s->log_fd == -1) { + if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { + /* timeout has passed */ + g_assert(s->log_fd != -1); + break; + } + } + + g_mutex_unlock(&s->data_mutex); +} + +static void write_guest_mem(TestServer *s, uint32_t seed) +{ + uint32_t *guest_mem; + int i, j; + size_t size; + + wait_for_fds(s); + + /* iterate all regions */ + for (i = 0; i < s->fds_num; i++) { + + /* We'll write only the region statring at 0x0 */ + if (s->memory.regions[i].guest_phys_addr != 0x0) { + continue; + } + + g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024); + + size = s->memory.regions[i].memory_size + + s->memory.regions[i].mmap_offset; + + guest_mem = mmap(0, size, PROT_READ | PROT_WRITE, + MAP_SHARED, s->fds[i], 0); + + g_assert(guest_mem != MAP_FAILED); + guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem)); + + for (j = 0; j < 256; j++) { + guest_mem[j] = seed + j; + } + + munmap(guest_mem, s->memory.regions[i].memory_size); + break; + } +} + +static guint64 get_log_size(TestServer *s) +{ + guint64 log_size = 0; + int i; + + for (i = 0; i < s->memory.nregions; ++i) { + VhostUserMemoryRegion *reg = &s->memory.regions[i]; + guint64 last = range_get_last(reg->guest_phys_addr, + reg->memory_size); + log_size = MAX(log_size, last / (8 * VHOST_LOG_PAGE) + 1); + } + + return log_size; +} + +typedef struct TestMigrateSource { + GSource source; + TestServer *src; + TestServer *dest; +} TestMigrateSource; + +static gboolean +test_migrate_source_check(GSource *source) +{ + TestMigrateSource *t = (TestMigrateSource *)source; + gboolean overlap = t->src->rings && t->dest->rings; + + g_assert(!overlap); + + return FALSE; +} + +#if !GLIB_CHECK_VERSION(2,36,0) +/* this callback is unnecessary with glib >2.36, the default + * prepare for the source does the same */ +static gboolean +test_migrate_source_prepare(GSource *source, gint *timeout) +{ + *timeout = -1; + return FALSE; +} +#endif + +GSourceFuncs test_migrate_source_funcs = { +#if !GLIB_CHECK_VERSION(2,36,0) + .prepare = test_migrate_source_prepare, +#endif + .check = test_migrate_source_check, +}; + +static void test_migrate(void) +{ + TestServer *s = test_server_new("src"); + TestServer *dest = test_server_new("dest"); + char *uri = g_strdup_printf("%s%s", "unix:", dest->mig_path); + QTestState *global = global_qtest, *from, *to; + GSource *source; + gchar *cmd; + QDict *rsp; + guint8 *log; + guint64 size; + + cmd = GET_QEMU_CMDE(s, 2, ""); + from = qtest_start(cmd); + g_free(cmd); + + wait_for_fds(s); + size = get_log_size(s); + g_assert_cmpint(size, ==, (2 * 1024 * 1024) / (VHOST_LOG_PAGE * 8)); + + cmd = GET_QEMU_CMDE(dest, 2, " -incoming %s", uri); + to = qtest_init(cmd); + g_free(cmd); + + source = g_source_new(&test_migrate_source_funcs, + sizeof(TestMigrateSource)); + ((TestMigrateSource *)source)->src = s; + ((TestMigrateSource *)source)->dest = dest; + g_source_attach(source, NULL); + + /* slow down migration to have time to fiddle with log */ + /* TODO: qtest could learn to break on some places */ + rsp = qmp("{ 'execute': 'migrate_set_speed'," + "'arguments': { 'value': 10 } }"); + g_assert(qdict_haskey(rsp, "return")); + QDECREF(rsp); + + cmd = g_strdup_printf("{ 'execute': 'migrate'," + "'arguments': { 'uri': '%s' } }", + uri); + rsp = qmp(cmd); + g_free(cmd); + g_assert(qdict_haskey(rsp, "return")); + QDECREF(rsp); + + wait_for_log_fd(s); + + log = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, s->log_fd, 0); + g_assert(log != MAP_FAILED); + + /* modify first page */ + write_guest_mem(s, 0x42); + log[0] = 1; + munmap(log, size); + + /* speed things up */ + rsp = qmp("{ 'execute': 'migrate_set_speed'," + "'arguments': { 'value': 0 } }"); + g_assert(qdict_haskey(rsp, "return")); + QDECREF(rsp); + + qmp_eventwait("STOP"); + + global_qtest = to; + qmp_eventwait("RESUME"); + + read_guest_mem(dest); + + g_source_destroy(source); + g_source_unref(source); + + qtest_quit(to); + test_server_free(dest); + qtest_quit(from); + test_server_free(s); + g_free(uri); + + global_qtest = global; +} + int main(int argc, char **argv) { QTestState *s = NULL; - CharDriverState *chr = NULL; - const char *hugefs = 0; - char *socket_path = 0; - char *qemu_cmd = 0; - char *chr_path = 0; + TestServer *server = NULL; + const char *hugefs; + char *qemu_cmd = NULL; int ret; + char template[] = "/tmp/vhost-test-XXXXXX"; + GMainLoop *loop; + GThread *thread; g_test_init(&argc, &argv, NULL); module_call_init(MODULE_INIT_QOM); + qemu_add_opts(&qemu_chardev_opts); - hugefs = init_hugepagefs(); - if (!hugefs) { - return 0; + tmpfs = mkdtemp(template); + if (!tmpfs) { + g_test_message("mkdtemp on path (%s): %s\n", template, strerror(errno)); + } + g_assert(tmpfs); + + hugefs = getenv("QTEST_HUGETLBFS_PATH"); + if (hugefs) { + root = init_hugepagefs(hugefs); + g_assert(root); + } else { + root = tmpfs; } - socket_path = g_strdup_printf("/tmp/vhost-%d.sock", getpid()); - - /* create char dev and add read handlers */ - qemu_add_opts(&qemu_chardev_opts); - chr_path = g_strdup_printf("unix:%s,server,nowait", socket_path); - chr = qemu_chr_new("chr0", chr_path, NULL); - g_free(chr_path); - qemu_chr_add_handlers(chr, chr_can_read, chr_read, NULL, chr); + server = test_server_new("test"); + loop = g_main_loop_new(NULL, FALSE); /* run the main loop thread so the chardev may operate */ - data_mutex = _mutex_new(); - data_cond = _cond_new(); - _thread_new(NULL, thread_function, NULL); + thread = g_thread_new(NULL, thread_function, loop); + + qemu_cmd = GET_QEMU_CMD(server); - qemu_cmd = g_strdup_printf(QEMU_CMD, hugefs, socket_path); s = qtest_start(qemu_cmd); g_free(qemu_cmd); - qtest_add_func("/vhost-user/read-guest-mem", read_guest_mem); + qtest_add_data_func("/vhost-user/read-guest-mem", server, read_guest_mem); + qtest_add_func("/vhost-user/migrate", test_migrate); ret = g_test_run(); @@ -412,10 +656,22 @@ int main(int argc, char **argv) } /* cleanup */ - unlink(socket_path); - g_free(socket_path); - _cond_free(data_cond); - _mutex_free(data_mutex); + test_server_free(server); + + /* finish the helper thread and dispatch pending sources */ + g_main_loop_quit(loop); + g_thread_join(thread); + while (g_main_context_pending(NULL)) { + g_main_context_iteration (NULL, TRUE); + } + g_main_loop_unref(loop); + + ret = rmdir(tmpfs); + if (ret != 0) { + g_test_message("unable to rmdir: path (%s): %s\n", + tmpfs, strerror(errno)); + } + g_assert_cmpint(ret, ==, 0); return ret; } diff --git a/qemu/tests/virtio-9p-test.c b/qemu/tests/virtio-9p-test.c index 1fae47797..59d0f1fa9 100644 --- a/qemu/tests/virtio-9p-test.c +++ b/qemu/tests/virtio-9p-test.c @@ -7,12 +7,10 @@ * See the COPYING file in the top-level directory. */ -#include -#include +#include "qemu/osdep.h" #include #include "libqtest.h" #include "qemu-common.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void pci_nop(void) diff --git a/qemu/tests/virtio-balloon-test.c b/qemu/tests/virtio-balloon-test.c index becebb51a..b010ce98e 100644 --- a/qemu/tests/virtio-balloon-test.c +++ b/qemu/tests/virtio-balloon-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void pci_nop(void) diff --git a/qemu/tests/virtio-blk-test.c b/qemu/tests/virtio-blk-test.c index 4078321a2..3a66630d7 100644 --- a/qemu/tests/virtio-blk-test.c +++ b/qemu/tests/virtio-blk-test.c @@ -8,11 +8,8 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include -#include -#include -#include #include "libqtest.h" #include "libqos/virtio.h" #include "libqos/virtio-pci.h" diff --git a/qemu/tests/virtio-console-test.c b/qemu/tests/virtio-console-test.c index 6be96e8c6..0b9c2a55e 100644 --- a/qemu/tests/virtio-console-test.c +++ b/qemu/tests/virtio-console-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void console_pci_nop(void) diff --git a/qemu/tests/virtio-net-test.c b/qemu/tests/virtio-net-test.c index ea7478c27..04cfcd594 100644 --- a/qemu/tests/virtio-net-test.c +++ b/qemu/tests/virtio-net-test.c @@ -7,23 +7,244 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" -#include "libqos/pci.h" +#include "qemu-common.h" +#include "qemu/sockets.h" +#include "qemu/iov.h" +#include "libqos/pci-pc.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" +#include "libqos/malloc.h" +#include "libqos/malloc-pc.h" +#include "libqos/malloc-generic.h" +#include "qemu/bswap.h" +#include "hw/virtio/virtio-net.h" #define PCI_SLOT_HP 0x06 +#define PCI_SLOT 0x04 +#define PCI_FN 0x00 + +#define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000) +#define VNET_HDR_SIZE sizeof(struct virtio_net_hdr_mrg_rxbuf) + +static void test_end(void) +{ + qtest_end(); +} + +#ifndef _WIN32 + +static QVirtioPCIDevice *virtio_net_pci_init(QPCIBus *bus, int slot) +{ + QVirtioPCIDevice *dev; + + dev = qvirtio_pci_device_find(bus, QVIRTIO_NET_DEVICE_ID); + g_assert(dev != NULL); + g_assert_cmphex(dev->vdev.device_type, ==, QVIRTIO_NET_DEVICE_ID); + + qvirtio_pci_device_enable(dev); + qvirtio_reset(&qvirtio_pci, &dev->vdev); + qvirtio_set_acknowledge(&qvirtio_pci, &dev->vdev); + qvirtio_set_driver(&qvirtio_pci, &dev->vdev); + + return dev; +} + +static QPCIBus *pci_test_start(int socket) +{ + char *cmdline; + + cmdline = g_strdup_printf("-netdev socket,fd=%d,id=hs0 -device " + "virtio-net-pci,netdev=hs0", socket); + qtest_start(cmdline); + g_free(cmdline); + + return qpci_init_pc(); +} + +static void driver_init(const QVirtioBus *bus, QVirtioDevice *dev) +{ + uint32_t features; + + features = qvirtio_get_features(bus, dev); + features = features & ~(QVIRTIO_F_BAD_FEATURE | + QVIRTIO_F_RING_INDIRECT_DESC | + QVIRTIO_F_RING_EVENT_IDX); + qvirtio_set_features(bus, dev, features); + + qvirtio_set_driver_ok(bus, dev); +} + +static void rx_test(const QVirtioBus *bus, QVirtioDevice *dev, + QGuestAllocator *alloc, QVirtQueue *vq, + int socket) +{ + uint64_t req_addr; + uint32_t free_head; + char test[] = "TEST"; + char buffer[64]; + int len = htonl(sizeof(test)); + struct iovec iov[] = { + { + .iov_base = &len, + .iov_len = sizeof(len), + }, { + .iov_base = test, + .iov_len = sizeof(test), + }, + }; + int ret; + + req_addr = guest_alloc(alloc, 64); + + free_head = qvirtqueue_add(vq, req_addr, 64, true, false); + qvirtqueue_kick(bus, dev, vq, free_head); + + ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test)); + g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len)); + + qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_NET_TIMEOUT_US); + memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test)); + g_assert_cmpstr(buffer, ==, "TEST"); + + guest_free(alloc, req_addr); +} + +static void tx_test(const QVirtioBus *bus, QVirtioDevice *dev, + QGuestAllocator *alloc, QVirtQueue *vq, + int socket) +{ + uint64_t req_addr; + uint32_t free_head; + uint32_t len; + char buffer[64]; + int ret; + + req_addr = guest_alloc(alloc, 64); + memwrite(req_addr + VNET_HDR_SIZE, "TEST", 4); + + free_head = qvirtqueue_add(vq, req_addr, 64, false, false); + qvirtqueue_kick(bus, dev, vq, free_head); + + qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_NET_TIMEOUT_US); + guest_free(alloc, req_addr); + + ret = qemu_recv(socket, &len, sizeof(len), 0); + g_assert_cmpint(ret, ==, sizeof(len)); + len = ntohl(len); + + ret = qemu_recv(socket, buffer, len, 0); + g_assert_cmpstr(buffer, ==, "TEST"); +} + +static void rx_stop_cont_test(const QVirtioBus *bus, QVirtioDevice *dev, + QGuestAllocator *alloc, QVirtQueue *vq, + int socket) +{ + uint64_t req_addr; + uint32_t free_head; + char test[] = "TEST"; + char buffer[64]; + int len = htonl(sizeof(test)); + struct iovec iov[] = { + { + .iov_base = &len, + .iov_len = sizeof(len), + }, { + .iov_base = test, + .iov_len = sizeof(test), + }, + }; + int ret; + + req_addr = guest_alloc(alloc, 64); + + free_head = qvirtqueue_add(vq, req_addr, 64, true, false); + qvirtqueue_kick(bus, dev, vq, free_head); + + qmp("{ 'execute' : 'stop'}"); + + ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test)); + g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len)); + + /* We could check the status, but this command is more importantly to + * ensure the packet data gets queued in QEMU, before we do 'cont'. + */ + qmp("{ 'execute' : 'query-status'}"); + qmp("{ 'execute' : 'cont'}"); + + qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_NET_TIMEOUT_US); + memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test)); + g_assert_cmpstr(buffer, ==, "TEST"); + + guest_free(alloc, req_addr); +} -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void pci_nop(void) +static void send_recv_test(const QVirtioBus *bus, QVirtioDevice *dev, + QGuestAllocator *alloc, QVirtQueue *rvq, + QVirtQueue *tvq, int socket) { + rx_test(bus, dev, alloc, rvq, socket); + tx_test(bus, dev, alloc, tvq, socket); } +static void stop_cont_test(const QVirtioBus *bus, QVirtioDevice *dev, + QGuestAllocator *alloc, QVirtQueue *rvq, + QVirtQueue *tvq, int socket) +{ + rx_stop_cont_test(bus, dev, alloc, rvq, socket); +} + +static void pci_basic(gconstpointer data) +{ + QVirtioPCIDevice *dev; + QPCIBus *bus; + QVirtQueuePCI *tx, *rx; + QGuestAllocator *alloc; + void (*func) (const QVirtioBus *bus, + QVirtioDevice *dev, + QGuestAllocator *alloc, + QVirtQueue *rvq, + QVirtQueue *tvq, + int socket) = data; + int sv[2], ret; + + ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv); + g_assert_cmpint(ret, !=, -1); + + bus = pci_test_start(sv[1]); + dev = virtio_net_pci_init(bus, PCI_SLOT); + + alloc = pc_alloc_init(); + rx = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, + alloc, 0); + tx = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, + alloc, 1); + + driver_init(&qvirtio_pci, &dev->vdev); + func(&qvirtio_pci, &dev->vdev, alloc, &rx->vq, &tx->vq, sv[0]); + + /* End test */ + close(sv[0]); + guest_free(alloc, tx->vq.desc); + pc_alloc_uninit(alloc); + qvirtio_pci_device_disable(dev); + g_free(dev); + qpci_free_pc(bus); + test_end(); +} +#endif + static void hotplug(void) { + qtest_start("-device virtio-net-pci"); + qpci_plug_device_test("virtio-net-pci", "net1", PCI_SLOT_HP, NULL); qpci_unplug_acpi_device_test("net1", PCI_SLOT_HP); + + test_end(); } int main(int argc, char **argv) @@ -31,13 +252,14 @@ int main(int argc, char **argv) int ret; g_test_init(&argc, &argv, NULL); - qtest_add_func("/virtio/net/pci/nop", pci_nop); +#ifndef _WIN32 + qtest_add_data_func("/virtio/net/pci/basic", send_recv_test, pci_basic); + qtest_add_data_func("/virtio/net/pci/rx_stop_cont", + stop_cont_test, pci_basic); +#endif qtest_add_func("/virtio/net/pci/hotplug", hotplug); - qtest_start("-device virtio-net-pci"); ret = g_test_run(); - qtest_end(); - return ret; } diff --git a/qemu/tests/virtio-rng-test.c b/qemu/tests/virtio-rng-test.c index 41c1cdb1a..771dbd73a 100644 --- a/qemu/tests/virtio-rng-test.c +++ b/qemu/tests/virtio-rng-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" #include "libqos/pci.h" #define PCI_SLOT_HP 0x06 diff --git a/qemu/tests/virtio-scsi-test.c b/qemu/tests/virtio-scsi-test.c index 11ccdd632..d78747a46 100644 --- a/qemu/tests/virtio-scsi-test.c +++ b/qemu/tests/virtio-scsi-test.c @@ -8,11 +8,10 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" -#include +#include "block/scsi.h" #include "libqos/virtio.h" #include "libqos/virtio-pci.h" #include "libqos/pci-pc.h" @@ -71,40 +70,6 @@ static void qvirtio_scsi_stop(void) qtest_end(); } -static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot) -{ - QVirtIOSCSI *vs; - QVirtioPCIDevice *dev; - void *addr; - int i; - - vs = g_new0(QVirtIOSCSI, 1); - vs->alloc = pc_alloc_init(); - vs->bus = qpci_init_pc(); - - dev = qvirtio_pci_device_find(vs->bus, QVIRTIO_SCSI_DEVICE_ID); - vs->dev = (QVirtioDevice *)dev; - g_assert(dev != NULL); - g_assert_cmphex(vs->dev->device_type, ==, QVIRTIO_SCSI_DEVICE_ID); - - qvirtio_pci_device_enable(dev); - qvirtio_reset(&qvirtio_pci, vs->dev); - qvirtio_set_acknowledge(&qvirtio_pci, vs->dev); - qvirtio_set_driver(&qvirtio_pci, vs->dev); - - addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX; - vs->num_queues = qvirtio_config_readl(&qvirtio_pci, vs->dev, - (uint64_t)(uintptr_t)addr); - - g_assert_cmpint(vs->num_queues, <, MAX_NUM_QUEUES); - - for (i = 0; i < vs->num_queues + 2; i++) { - vs->vq[i] = qvirtqueue_setup(&qvirtio_pci, vs->dev, vs->alloc, i); - } - - return vs; -} - static void qvirtio_scsi_pci_free(QVirtIOSCSI *vs) { int i; @@ -134,7 +99,8 @@ static uint64_t qvirtio_scsi_alloc(QVirtIOSCSI *vs, size_t alloc_size, static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb, const uint8_t *data_in, size_t data_in_len, - uint8_t *data_out, size_t data_out_len) + uint8_t *data_out, size_t data_out_len, + QVirtIOSCSICmdResp *resp_out) { QVirtQueue *vq; QVirtIOSCSICmdReq req = { { 0 } }; @@ -174,6 +140,10 @@ static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb, response = readb(resp_addr + offsetof(QVirtIOSCSICmdResp, response)); + if (resp_out) { + memread(resp_addr, resp_out, sizeof(*resp_out)); + } + guest_free(vs->alloc, req_addr); guest_free(vs->alloc, resp_addr); guest_free(vs->alloc, data_in_addr); @@ -181,6 +151,52 @@ static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb, return response; } +static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot) +{ + const uint8_t test_unit_ready_cdb[CDB_SIZE] = {}; + QVirtIOSCSI *vs; + QVirtioPCIDevice *dev; + QVirtIOSCSICmdResp resp; + void *addr; + int i; + + vs = g_new0(QVirtIOSCSI, 1); + vs->alloc = pc_alloc_init(); + vs->bus = qpci_init_pc(); + + dev = qvirtio_pci_device_find(vs->bus, QVIRTIO_SCSI_DEVICE_ID); + vs->dev = (QVirtioDevice *)dev; + g_assert(dev != NULL); + g_assert_cmphex(vs->dev->device_type, ==, QVIRTIO_SCSI_DEVICE_ID); + + qvirtio_pci_device_enable(dev); + qvirtio_reset(&qvirtio_pci, vs->dev); + qvirtio_set_acknowledge(&qvirtio_pci, vs->dev); + qvirtio_set_driver(&qvirtio_pci, vs->dev); + + addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX; + vs->num_queues = qvirtio_config_readl(&qvirtio_pci, vs->dev, + (uint64_t)(uintptr_t)addr); + + g_assert_cmpint(vs->num_queues, <, MAX_NUM_QUEUES); + + for (i = 0; i < vs->num_queues + 2; i++) { + vs->vq[i] = qvirtqueue_setup(&qvirtio_pci, vs->dev, vs->alloc, i); + } + + /* Clear the POWER ON OCCURRED unit attention */ + g_assert_cmpint(virtio_scsi_do_command(vs, test_unit_ready_cdb, + NULL, 0, NULL, 0, &resp), + ==, 0); + g_assert_cmpint(resp.status, ==, CHECK_CONDITION); + g_assert_cmpint(resp.sense[0], ==, 0x70); /* Fixed format sense buffer */ + g_assert_cmpint(resp.sense[2], ==, UNIT_ATTENTION); + g_assert_cmpint(resp.sense[12], ==, 0x29); /* POWER ON */ + g_assert_cmpint(resp.sense[13], ==, 0x00); + + return vs; +} + /* Tests only initialization so far. TODO: Replace with functional tests */ static void pci_nop(void) { @@ -221,9 +237,12 @@ static void hotplug(void) static void test_unaligned_write_same(void) { QVirtIOSCSI *vs; - uint8_t buf[512] = { 0 }; - const uint8_t write_same_cdb[CDB_SIZE] = { 0x41, 0x00, 0x00, 0x00, 0x00, + uint8_t buf1[512] = { 0 }; + uint8_t buf2[512] = { 1 }; + const uint8_t write_same_cdb_1[CDB_SIZE] = { 0x41, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00 }; + const uint8_t write_same_cdb_2[CDB_SIZE] = { 0x41, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x33, 0x00, 0x00 }; qvirtio_scsi_start("-drive file=blkdebug::null-co://,if=none,id=dr1" ",format=raw,file.align=4k " @@ -231,7 +250,10 @@ static void test_unaligned_write_same(void) vs = qvirtio_scsi_pci_init(PCI_SLOT); g_assert_cmphex(0, ==, - virtio_scsi_do_command(vs, write_same_cdb, NULL, 0, buf, 512)); + virtio_scsi_do_command(vs, write_same_cdb_1, NULL, 0, buf1, 512, NULL)); + + g_assert_cmphex(0, ==, + virtio_scsi_do_command(vs, write_same_cdb_2, NULL, 0, buf2, 512, NULL)); qvirtio_scsi_pci_free(vs); qvirtio_scsi_stop(); diff --git a/qemu/tests/virtio-serial-test.c b/qemu/tests/virtio-serial-test.c index bf030a616..480d4abb2 100644 --- a/qemu/tests/virtio-serial-test.c +++ b/qemu/tests/virtio-serial-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void pci_nop(void) diff --git a/qemu/tests/vmxnet3-test.c b/qemu/tests/vmxnet3-test.c index a2ebed39c..6ef0e2f04 100644 --- a/qemu/tests/vmxnet3-test.c +++ b/qemu/tests/vmxnet3-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void nop(void) diff --git a/qemu/tests/wdt_ib700-test.c b/qemu/tests/wdt_ib700-test.c index 82ca59725..efe337045 100644 --- a/qemu/tests/wdt_ib700-test.c +++ b/qemu/tests/wdt_ib700-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include #include "libqtest.h" -#include "qemu/osdep.h" #include "qemu/timer.h" static void qmp_check_no_event(void) diff --git a/qemu/thread-pool.c b/qemu/thread-pool.c index ac909f498..03ba0b02a 100644 --- a/qemu/thread-pool.c +++ b/qemu/thread-pool.c @@ -14,11 +14,11 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/queue.h" #include "qemu/thread.h" -#include "qemu/osdep.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "trace.h" #include "block/thread-pool.h" #include "qemu/main-loop.h" diff --git a/qemu/thunk.c b/qemu/thunk.c index f501fd72f..f057d86d9 100644 --- a/qemu/thunk.c +++ b/qemu/thunk.c @@ -16,9 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include -#include -#include +#include "qemu/osdep.h" #include "qemu.h" #include "exec/user/thunk.h" diff --git a/qemu/tpm.c b/qemu/tpm.c index 4e9b109fb..9a7c7114d 100644 --- a/qemu/tpm.c +++ b/qemu/tpm.c @@ -11,7 +11,7 @@ * * Based on net.c */ -#include "config-host.h" +#include "qemu/osdep.h" #include "qapi/qmp/qerror.h" #include "sysemu/tpm_backend.h" @@ -32,7 +32,7 @@ static TPMDriverOps const *be_drivers[TPM_MAX_DRIVERS] = { }; static enum TpmModel tpm_models[TPM_MAX_MODELS] = { - TPM_MODEL_MAX, + TPM_MODEL__MAX, }; int tpm_register_model(enum TpmModel model) @@ -40,7 +40,7 @@ int tpm_register_model(enum TpmModel model) int i; for (i = 0; i < TPM_MAX_MODELS; i++) { - if (tpm_models[i] == TPM_MODEL_MAX) { + if (tpm_models[i] == TPM_MODEL__MAX) { tpm_models[i] = model; return 0; } @@ -260,9 +260,9 @@ static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv) switch (drv->ops->type) { case TPM_TYPE_PASSTHROUGH: - res->options->kind = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH; + res->options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH; tpo = g_new0(TPMPassthroughOptions, 1); - res->options->passthrough = tpo; + res->options->u.passthrough.data = tpo; if (drv->path) { tpo->path = g_strdup(drv->path); tpo->has_path = true; @@ -272,7 +272,7 @@ static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv) tpo->has_cancel_path = true; } break; - case TPM_TYPE_MAX: + case TPM_TYPE__MAX: break; } @@ -311,7 +311,7 @@ TpmTypeList *qmp_query_tpm_types(Error **errp) unsigned int i = 0; TpmTypeList *head = NULL, *prev = NULL, *cur_item; - for (i = 0; i < TPM_TYPE_MAX; i++) { + for (i = 0; i < TPM_TYPE__MAX; i++) { if (!tpm_driver_find_by_type(i)) { continue; } @@ -335,7 +335,7 @@ TpmModelList *qmp_query_tpm_models(Error **errp) unsigned int i = 0; TpmModelList *head = NULL, *prev = NULL, *cur_item; - for (i = 0; i < TPM_MODEL_MAX; i++) { + for (i = 0; i < TPM_MODEL__MAX; i++) { if (!tpm_model_is_registered(i)) { continue; } diff --git a/qemu/trace-events b/qemu/trace-events index 94bf3bb18..835074387 100644 --- a/qemu/trace-events +++ b/qemu/trace-events @@ -59,6 +59,9 @@ virtio_console_chr_event(unsigned int port, int event) "port %u, event %d" # block.c bdrv_open_common(void *bs, const char *filename, int flags, const char *format_name) "bs %p filename \"%s\" flags %#x format_name \"%s\"" +bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d" + +# block/io.c multiwrite_cb(void *mcb, int ret) "mcb %p ret %d" bdrv_aio_multiwrite(void *mcb, int num_callbacks, int num_reqs) "mcb %p num_callbacks %d num_reqs %d" bdrv_aio_discard(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p" @@ -66,9 +69,9 @@ bdrv_aio_flush(void *bs, void *opaque) "bs %p opaque %p" bdrv_aio_readv(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p" bdrv_aio_writev(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p" bdrv_aio_write_zeroes(void *bs, int64_t sector_num, int nb_sectors, int flags, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d flags %#x opaque %p" -bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d" bdrv_co_readv(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d" bdrv_co_copy_on_readv(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d" +bdrv_co_readv_no_serialising(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d" bdrv_co_writev(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d" bdrv_co_write_zeroes(void *bs, int64_t sector_num, int nb_sector, int flags) "bs %p sector_num %"PRId64" nb_sectors %d flags %#x" bdrv_co_io_em(void *bs, int64_t sector_num, int nb_sectors, int is_write, void *acb) "bs %p sector_num %"PRId64" nb_sectors %d is_write %d acb %p" @@ -123,9 +126,6 @@ virtio_blk_data_plane_start(void *s) "dataplane %p" virtio_blk_data_plane_stop(void *s) "dataplane %p" virtio_blk_data_plane_process_request(void *s, unsigned int out_num, unsigned int in_num, unsigned int head) "dataplane %p out_num %u in_num %u head %u" -# hw/virtio/dataplane/vring.c -vring_setup(uint64_t physical, void *desc, void *avail, void *used) "vring physical %#"PRIx64" desc %p avail %p used %p" - # thread-pool.c thread_pool_submit(void *pool, void *req, void *opaque) "pool %p req %p opaque %p" thread_pool_complete(void *pool, void *req, void *opaque, int ret) "pool %p req %p opaque %p ret %d" @@ -137,15 +137,15 @@ paio_submit_co(int64_t sector_num, int nb_sectors, int type) "sector_num %"PRId6 paio_submit(void *acb, void *opaque, int64_t sector_num, int nb_sectors, int type) "acb %p opaque %p sector_num %"PRId64" nb_sectors %d type %d" # ioport.c -cpu_in(unsigned int addr, unsigned int val) "addr %#x value %u" -cpu_out(unsigned int addr, unsigned int val) "addr %#x value %u" +cpu_in(unsigned int addr, char size, unsigned int val) "addr %#x(%c) value %u" +cpu_out(unsigned int addr, char size, unsigned int val) "addr %#x(%c) value %u" # balloon.c # Since requests are raised via monitor, not many tracepoints are needed. balloon_event(void *opaque, unsigned long addr) "opaque %p addr %lu" -virtio_balloon_handle_output(const char *name, uint64_t gpa) "setion name: %s gpa: %"PRIx64"" -virtio_balloon_get_config(uint32_t num_pages, uint32_t acutal) "num_pages: %d acutal: %d" -virtio_balloon_set_config(uint32_t acutal, uint32_t oldacutal) "acutal: %d oldacutal: %d" +virtio_balloon_handle_output(const char *name, uint64_t gpa) "section name: %s gpa: %"PRIx64 +virtio_balloon_get_config(uint32_t num_pages, uint32_t actual) "num_pages: %d actual: %d" +virtio_balloon_set_config(uint32_t actual, uint32_t oldactual) "actual: %d oldactual: %d" virtio_balloon_to_target(uint64_t target, uint32_t num_pages) "balloon target: %"PRIx64" num_pages: %d" # hw/intc/apic_common.c @@ -194,7 +194,7 @@ ecc_diag_mem_readb(uint64_t addr, uint32_t ret) "Read diagnostic %"PRId64"= %02x # hw/nvram/fw_cfg.c fw_cfg_select(void *s, uint16_t key, int ret) "%p key %d = %d" -fw_cfg_read(void *s, uint8_t ret) "%p = %d" +fw_cfg_read(void *s, uint64_t ret) "%p = %"PRIx64 fw_cfg_add_file(void *s, int index, char *name, size_t len) "%p #%d: %s (%zd bytes)" # hw/block/hd-geometry.c @@ -550,6 +550,8 @@ usb_mtp_op_get_partial_object(int dev, uint32_t handle, const char *path, uint32 usb_mtp_op_unknown(int dev, uint32_t code) "dev %d, command code 0x%x" usb_mtp_object_alloc(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s" usb_mtp_object_free(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s" +usb_mtp_add_child(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s" +usb_mtp_inotify_event(int dev, const char *path, uint32_t mask, const char *s) "dev %d, path %s mask 0x%x event %s" # hw/usb/host-libusb.c usb_host_open_started(int bus, int addr) "dev %d:%d" @@ -601,9 +603,6 @@ scsi_request_sense(int target, int lun, int tag) "target %d lun %d tag %d" vm_state_notify(int running, int reason) "running %d reason %d" load_file(const char *name, const char *path) "name %s location %s" runstate_set(int new_state) "new state %d" -g_malloc(size_t size, void *ptr) "size %zu ptr %p" -g_realloc(void *ptr, size_t size, void *newptr) "ptr %p size %zu newptr %p" -g_free(void *ptr) "ptr %p" system_wakeup_request(int reason) "reason=%d" qemu_system_shutdown_request(void) "" qemu_system_powerdown_request(void) "" @@ -684,7 +683,7 @@ grlib_irqmp_writel_unknown(uint64_t addr, uint32_t value) "addr 0x%"PRIx64" valu # hw/char/grlib_apbuart.c grlib_apbuart_event(int event) "event:%d" grlib_apbuart_writel_unknown(uint64_t addr, uint32_t value) "addr 0x%"PRIx64" value 0x%x" -grlib_apbuart_readl_unknown(uint64_t addr) "addr 0x%"PRIx64"" +grlib_apbuart_readl_unknown(uint64_t addr) "addr 0x%"PRIx64 # hw/sparc/leon3.c leon3_set_irq(int intno) "Set CPU IRQ %d" @@ -723,14 +722,36 @@ lm32_uart_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" lm32_uart_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" lm32_uart_irq_state(int level) "irq state %d" +# hw/scsi/mptsas.c +mptsas_command_complete(void *dev, uint32_t ctx, uint32_t status, uint32_t resid) "dev %p context 0x%08x status %x resid %d" +mptsas_diag_read(void *dev, uint32_t addr, uint32_t val) "dev %p addr 0x%08x value 0x%08x" +mptsas_diag_write(void *dev, uint32_t addr, uint32_t val) "dev %p addr 0x%08x value 0x%08x" +mptsas_irq_intx(void *dev, int level) "dev %p level %d" +mptsas_irq_msi(void *dev) "dev %p " +mptsas_mmio_read(void *dev, uint32_t addr, uint32_t val) "dev %p addr 0x%08x value 0x%x" +mptsas_mmio_unhandled_read(void *dev, uint32_t addr) "dev %p addr 0x%08x" +mptsas_mmio_unhandled_write(void *dev, uint32_t addr, uint32_t val) "dev %p addr 0x%08x value 0x%x" +mptsas_mmio_write(void *dev, uint32_t addr, uint32_t val) "dev %p addr 0x%08x value 0x%x" +mptsas_process_message(void *dev, int msg, uint32_t ctx) "dev %p cmd %d context 0x%08x\n" +mptsas_process_scsi_io_request(void *dev, int bus, int target, int lun, uint64_t len) "dev %p dev %d:%d:%d length %"PRIu64"" +mptsas_reset(void *dev) "dev %p " +mptsas_scsi_overflow(void *dev, uint32_t ctx, uint64_t req, uint64_t found) "dev %p context 0x%08x: %"PRIu64"/%"PRIu64"" +mptsas_sgl_overflow(void *dev, uint32_t ctx, uint64_t req, uint64_t found) "dev %p context 0x%08x: %"PRIu64"/%"PRIu64"" +mptsas_unhandled_cmd(void *dev, uint32_t ctx, uint8_t msg_cmd) "dev %p context 0x%08x: Unhandled cmd %x" +mptsas_unhandled_doorbell_cmd(void *dev, int cmd) "dev %p value 0x%08x" + +# hw/scsi/mptconfig.c +mptsas_config_sas_device(void *dev, int address, int port, int phy_handle, int dev_handle, int page) "dev %p address %d (port %d, handles: phy %d dev %d) page %d" +mptsas_config_sas_phy(void *dev, int address, int port, int phy_handle, int dev_handle, int page) "dev %p address %d (port %d, handles: phy %d dev %d) page %d" + # hw/scsi/megasas.c megasas_init_firmware(uint64_t pa) "pa %" PRIx64 " " megasas_init_queue(uint64_t queue_pa, int queue_len, uint64_t head, uint64_t tail, uint32_t flags) "queue at %" PRIx64 " len %d head %" PRIx64 " tail %" PRIx64 " flags %x" megasas_initq_map_failed(int frame) "scmd %d: failed to map queue" -megasas_initq_mapped(uint64_t pa) "queue already mapped at %" PRIx64 "" +megasas_initq_mapped(uint64_t pa) "queue already mapped at %" PRIx64 megasas_initq_mismatch(int queue_len, int fw_cmds) "queue size %d max fw cmds %d" megasas_qf_mapped(unsigned int index) "skip mapped frame %x" -megasas_qf_new(unsigned int index, uint64_t frame) "frame %x addr %" PRIx64 "" +megasas_qf_new(unsigned int index, uint64_t frame) "frame %x addr %" PRIx64 megasas_qf_busy(unsigned long pa) "all frames busy for frame %lx" megasas_qf_enqueue(unsigned int index, unsigned int count, uint64_t context, unsigned int head, unsigned int tail, int busy) "frame %x count %d context %" PRIx64 " head %x tail %x busy %d" megasas_qf_update(unsigned int head, unsigned int tail, unsigned int busy) "head %x tail %x busy %d" @@ -784,7 +805,7 @@ megasas_dcmd_pd_list_query(int cmd, int flags) "scmd %d: query flags %x" megasas_dcmd_reset_ld(int cmd, int target_id) "scmd %d: dev %d" megasas_dcmd_unsupported(int cmd, unsigned long size) "scmd %d: set properties len %ld" megasas_abort_frame(int cmd, int abort_cmd) "scmd %d: frame %x" -megasas_abort_no_cmd(int cmd, uint64_t context) "scmd %d: no active command for frame context %" PRIx64 "" +megasas_abort_no_cmd(int cmd, uint64_t context) "scmd %d: no active command for frame context %" PRIx64 megasas_abort_invalid_context(int cmd, uint64_t context, int abort_cmd) "scmd %d: invalid frame context %" PRIx64 " for abort frame %x" megasas_reset(int fw_state) "firmware state %x" megasas_init(int sges, int cmds, const char *mode) "Using %d sges, %d cmds, %s mode" @@ -877,7 +898,7 @@ milkymist_vgafb_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08 mipsnet_send(uint32_t size) "sending len=%u" mipsnet_receive(uint32_t size) "receiving len=%u" mipsnet_read(uint64_t addr, uint32_t val) "read addr=0x%" PRIx64 " val=0x%x" -mipsnet_write(uint64_t addr, uint64_t val) "write addr=0x%" PRIx64 " val=0x%" PRIx64 "" +mipsnet_write(uint64_t addr, uint64_t val) "write addr=0x%" PRIx64 " val=0x%" PRIx64 mipsnet_irq(uint32_t isr, uint32_t intctl) "set irq to %d (%02x)" # hw/isa/pc87312.c @@ -891,8 +912,8 @@ pc87312_info_serial(int n, uint32_t base, uint32_t irq) "id=%d, base 0x%x, irq % # hw/scsi/vmw_pvscsi.c pvscsi_ring_init_data(uint32_t txr_len_log2, uint32_t rxr_len_log2) "TX/RX rings logarithms set to %d/%d" pvscsi_ring_init_msg(uint32_t len_log2) "MSG ring logarithm set to %d" -pvscsi_ring_flush_cmp(uint64_t filled_cmp_ptr) "new production counter of completion ring is 0x%"PRIx64"" -pvscsi_ring_flush_msg(uint64_t filled_cmp_ptr) "new production counter of message ring is 0x%"PRIx64"" +pvscsi_ring_flush_cmp(uint64_t filled_cmp_ptr) "new production counter of completion ring is 0x%"PRIx64 +pvscsi_ring_flush_msg(uint64_t filled_cmp_ptr) "new production counter of message ring is 0x%"PRIx64 pvscsi_update_irq_level(bool raise, uint64_t mask, uint64_t status) "interrupt level set to %d (MASK: 0x%"PRIx64", STATUS: 0x%"PRIx64")" pvscsi_update_irq_msi(void) "sending MSI notification" pvscsi_cmp_ring_put(unsigned long addr) "got completion descriptor 0x%lx" @@ -904,7 +925,7 @@ pvscsi_command_complete_not_found(uint32_t tag) "can't find request for tag 0x%x pvscsi_command_complete_data_run(void) "not all data required for command transferred" pvscsi_command_complete_sense_len(int len) "sense information length is %d bytes" pvscsi_convert_sglist(uint64_t context, unsigned long addr, uint32_t resid) "element: ctx: 0x%"PRIx64" addr: 0x%lx, len: %ul" -pvscsi_process_req_descr(uint8_t cmd, uint64_t ctx) "SCSI cmd 0x%x, ctx: 0x%"PRIx64"" +pvscsi_process_req_descr(uint8_t cmd, uint64_t ctx) "SCSI cmd 0x%x, ctx: 0x%"PRIx64 pvscsi_process_req_descr_unknown_device(void) "command directed to unknown device rejected" pvscsi_process_req_descr_invalid_dir(void) "command with invalid transfer direction rejected" pvscsi_process_io(unsigned long addr) "got descriptor 0x%lx" @@ -912,15 +933,15 @@ pvscsi_on_cmd_noimpl(const char* cmd) "unimplemented command %s ignored" pvscsi_on_cmd_reset_dev(uint32_t tgt, int lun, void* dev) "PVSCSI_CMD_RESET_DEVICE[target %u lun %d (dev 0x%p)]" pvscsi_on_cmd_arrived(const char* cmd) "command %s arrived" pvscsi_on_cmd_abort(uint64_t ctx, uint32_t tgt) "command PVSCSI_CMD_ABORT_CMD for ctx 0x%"PRIx64", target %u" -pvscsi_on_cmd_unknown(uint64_t cmd_id) "unknown command %"PRIx64"" +pvscsi_on_cmd_unknown(uint64_t cmd_id) "unknown command %"PRIx64 pvscsi_on_cmd_unknown_data(uint32_t data) "data for unknown command 0x:%x" -pvscsi_io_write(const char* cmd, uint64_t val) "%s write: %"PRIx64"" -pvscsi_io_write_unknown(unsigned long addr, unsigned sz, uint64_t val) "unknown write address: 0x%lx size: %u bytes value: 0x%"PRIx64"" -pvscsi_io_read(const char* cmd, uint64_t status) "%s read: 0x%"PRIx64"" +pvscsi_io_write(const char* cmd, uint64_t val) "%s write: %"PRIx64 +pvscsi_io_write_unknown(unsigned long addr, unsigned sz, uint64_t val) "unknown write address: 0x%lx size: %u bytes value: 0x%"PRIx64 +pvscsi_io_read(const char* cmd, uint64_t status) "%s read: 0x%"PRIx64 pvscsi_io_read_unknown(unsigned long addr, unsigned sz) "unknown read address: 0x%lx size: %u bytes" pvscsi_init_msi_fail(int res) "failed to initialize MSI, error %d" pvscsi_state(const char* state) "starting %s ..." -pvscsi_tx_rings_ppn(const char* label, uint64_t ppn) "%s page: %"PRIx64"" +pvscsi_tx_rings_ppn(const char* label, uint64_t ppn) "%s page: %"PRIx64 pvscsi_tx_rings_num_pages(const char* label, uint32_t num) "Number of %s pages: %u" # xen-hvm.c @@ -935,6 +956,13 @@ xen_map_portio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: % xen_unmap_portio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: %u start: %#"PRIx64" end: %#"PRIx64 xen_map_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x" xen_unmap_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x" +handle_ioreq(void *req, uint32_t type, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p type=%d dir=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d" +handle_ioreq_read(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p read type=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d" +handle_ioreq_write(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p write type=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d" +cpu_ioreq_pio(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p pio dir=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d" +cpu_ioreq_pio_read_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio read reg data=%#"PRIx64" port=%#"PRIx64" size=%d" +cpu_ioreq_pio_write_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio write reg data=%#"PRIx64" port=%#"PRIx64" size=%d" +cpu_ioreq_move(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p copy dir=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d" # xen-mapcache.c xen_map_cache(uint64_t phys_addr) "want %#"PRIx64 @@ -1025,9 +1053,9 @@ esp_pci_sbac_write(uint32_t reg, uint32_t val) "sbac: 0x%8.8x -> 0x%8.8x" # monitor.c handle_qmp_command(void *mon, const char *cmd_name) "mon %p cmd_name \"%s\"" monitor_protocol_emitter(void *mon) "mon %p" -monitor_protocol_event_handler(uint32_t event, void *data, uint64_t last, uint64_t now) "event=%d data=%p last=%" PRId64 " now=%" PRId64 +monitor_protocol_event_handler(uint32_t event, void *qdict) "event=%d data=%p" monitor_protocol_event_emit(uint32_t event, void *data) "event=%d data=%p" -monitor_protocol_event_queue(uint32_t event, void *data, uint64_t rate, uint64_t last, uint64_t now) "event=%d data=%p rate=%" PRId64 " last=%" PRId64 " now=%" PRId64 +monitor_protocol_event_queue(uint32_t event, void *qdict, uint64_t rate) "event=%d data=%p rate=%" PRId64 monitor_protocol_event_throttle(uint32_t event, uint64_t rate) "event=%d rate=%" PRId64 # hw/net/opencores_eth.c @@ -1049,10 +1077,10 @@ v9fs_rerror(uint16_t tag, uint8_t id, int err) "tag %d id %d err %d" v9fs_version(uint16_t tag, uint8_t id, int32_t msize, char* version) "tag %d id %d msize %d version %s" v9fs_version_return(uint16_t tag, uint8_t id, int32_t msize, char* version) "tag %d id %d msize %d version %s" v9fs_attach(uint16_t tag, uint8_t id, int32_t fid, int32_t afid, char* uname, char* aname) "tag %u id %u fid %d afid %d uname %s aname %s" -v9fs_attach_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path) "tag %d id %d type %d version %d path %"PRId64"" +v9fs_attach_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path) "tag %d id %d type %d version %d path %"PRId64 v9fs_stat(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d" v9fs_stat_return(uint16_t tag, uint8_t id, int32_t mode, int32_t atime, int32_t mtime, int64_t length) "tag %d id %d stat={mode %d atime %d mtime %d length %"PRId64"}" -v9fs_getattr(uint16_t tag, uint8_t id, int32_t fid, uint64_t request_mask) "tag %d id %d fid %d request_mask %"PRIu64"" +v9fs_getattr(uint16_t tag, uint8_t id, int32_t fid, uint64_t request_mask) "tag %d id %d fid %d request_mask %"PRIu64 v9fs_getattr_return(uint16_t tag, uint8_t id, uint64_t result_mask, uint32_t mode, uint32_t uid, uint32_t gid) "tag %d id %d getattr={result_mask %"PRId64" mode %u uid %u gid %u}" v9fs_walk(uint16_t tag, uint8_t id, int32_t fid, int32_t newfid, uint16_t nwnames) "tag %d id %d fid %d newfid %d nwnames %d" v9fs_walk_return(uint16_t tag, uint8_t id, uint16_t nwnames, void* qids) "tag %d id %d nwnames %d qids %p" @@ -1078,14 +1106,14 @@ v9fs_remove(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d" v9fs_wstat(uint16_t tag, uint8_t id, int32_t fid, int32_t mode, int32_t atime, int32_t mtime) "tag %u id %u fid %d stat={mode %d atime %d mtime %d}" v9fs_mknod(uint16_t tag, uint8_t id, int32_t fid, int mode, int major, int minor) "tag %d id %d fid %d mode %d major %d minor %d" v9fs_mknod_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path) "tag %d id %d qid={type %d version %d path %"PRId64"}" -v9fs_lock(uint16_t tag, uint8_t id, int32_t fid, uint8_t type, uint64_t start, uint64_t length) "tag %d id %d fid %d type %d start %"PRIu64" length %"PRIu64"" +v9fs_lock(uint16_t tag, uint8_t id, int32_t fid, uint8_t type, uint64_t start, uint64_t length) "tag %d id %d fid %d type %d start %"PRIu64" length %"PRIu64 v9fs_lock_return(uint16_t tag, uint8_t id, int8_t status) "tag %d id %d status %d" -v9fs_getlock(uint16_t tag, uint8_t id, int32_t fid, uint8_t type, uint64_t start, uint64_t length)"tag %d id %d fid %d type %d start %"PRIu64" length %"PRIu64"" +v9fs_getlock(uint16_t tag, uint8_t id, int32_t fid, uint8_t type, uint64_t start, uint64_t length)"tag %d id %d fid %d type %d start %"PRIu64" length %"PRIu64 v9fs_getlock_return(uint16_t tag, uint8_t id, uint8_t type, uint64_t start, uint64_t length, uint32_t proc_id) "tag %d id %d type %d start %"PRIu64" length %"PRIu64" proc_id %u" v9fs_mkdir(uint16_t tag, uint8_t id, int32_t fid, char* name, int mode, uint32_t gid) "tag %u id %u fid %d name %s mode %d gid %u" v9fs_mkdir_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path, int err) "tag %u id %u qid={type %d version %d path %"PRId64"} err %d" v9fs_xattrwalk(uint16_t tag, uint8_t id, int32_t fid, int32_t newfid, char* name) "tag %d id %d fid %d newfid %d name %s" -v9fs_xattrwalk_return(uint16_t tag, uint8_t id, int64_t size) "tag %d id %d size %"PRId64"" +v9fs_xattrwalk_return(uint16_t tag, uint8_t id, int64_t size) "tag %d id %d size %"PRId64 v9fs_xattrcreate(uint16_t tag, uint8_t id, int32_t fid, char* name, int64_t size, int flags) "tag %d id %d fid %d name %s size %"PRId64" flags %d" v9fs_readlink(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d" v9fs_readlink_return(uint16_t tag, uint8_t id, char* target) "tag %d id %d name %s" @@ -1093,12 +1121,12 @@ v9fs_readlink_return(uint16_t tag, uint8_t id, char* target) "tag %d id %d name # target-sparc/mmu_helper.c mmu_helper_dfault(uint64_t address, uint64_t context, int mmu_idx, uint32_t tl) "DFAULT at %"PRIx64" context %"PRIx64" mmu_idx=%d tl=%d" mmu_helper_dprot(uint64_t address, uint64_t context, int mmu_idx, uint32_t tl) "DPROT at %"PRIx64" context %"PRIx64" mmu_idx=%d tl=%d" -mmu_helper_dmiss(uint64_t address, uint64_t context) "DMISS at %"PRIx64" context %"PRIx64"" -mmu_helper_tfault(uint64_t address, uint64_t context) "TFAULT at %"PRIx64" context %"PRIx64"" -mmu_helper_tmiss(uint64_t address, uint64_t context) "TMISS at %"PRIx64" context %"PRIx64"" -mmu_helper_get_phys_addr_code(uint32_t tl, int mmu_idx, uint64_t prim_context, uint64_t sec_context, uint64_t address) "tl=%d mmu_idx=%d primary context=%"PRIx64" secondary context=%"PRIx64" address=%"PRIx64"" -mmu_helper_get_phys_addr_data(uint32_t tl, int mmu_idx, uint64_t prim_context, uint64_t sec_context, uint64_t address) "tl=%d mmu_idx=%d primary context=%"PRIx64" secondary context=%"PRIx64" address=%"PRIx64"" -mmu_helper_mmu_fault(uint64_t address, uint64_t paddr, int mmu_idx, uint32_t tl, uint64_t prim_context, uint64_t sec_context) "Translate at %"PRIx64" -> %"PRIx64", mmu_idx=%d tl=%d primary context=%"PRIx64" secondary context=%"PRIx64"" +mmu_helper_dmiss(uint64_t address, uint64_t context) "DMISS at %"PRIx64" context %"PRIx64 +mmu_helper_tfault(uint64_t address, uint64_t context) "TFAULT at %"PRIx64" context %"PRIx64 +mmu_helper_tmiss(uint64_t address, uint64_t context) "TMISS at %"PRIx64" context %"PRIx64 +mmu_helper_get_phys_addr_code(uint32_t tl, int mmu_idx, uint64_t prim_context, uint64_t sec_context, uint64_t address) "tl=%d mmu_idx=%d primary context=%"PRIx64" secondary context=%"PRIx64" address=%"PRIx64 +mmu_helper_get_phys_addr_data(uint32_t tl, int mmu_idx, uint64_t prim_context, uint64_t sec_context, uint64_t address) "tl=%d mmu_idx=%d primary context=%"PRIx64" secondary context=%"PRIx64" address=%"PRIx64 +mmu_helper_mmu_fault(uint64_t address, uint64_t paddr, int mmu_idx, uint32_t tl, uint64_t prim_context, uint64_t sec_context) "Translate at %"PRIx64" -> %"PRIx64", mmu_idx=%d tl=%d primary context=%"PRIx64" secondary context=%"PRIx64 # target-sparc/int64_helper.c int_helper_set_softint(uint32_t softint) "new %08x" @@ -1133,6 +1161,7 @@ console_select(int nr) "%d" console_refresh(int interval) "interval %d ms" displaysurface_create(void *display_surface, int w, int h) "surface=%p, %dx%d" displaysurface_create_from(void *display_surface, int w, int h, uint32_t format) "surface=%p, %dx%d, format 0x%x" +displaysurface_create_pixman(void *display_surface) "surface=%p" displaysurface_free(void *display_surface) "surface=%p" displaychangelistener_register(void *dcl, const char *name) "%p [ %s ]" displaychangelistener_unregister(void *dcl, const char *name) "%p [ %s ]" @@ -1142,7 +1171,8 @@ ppm_save(const char *filename, void *display_surface) "%s surface=%p" gd_switch(const char *tab, int width, int height) "tab=%s, width=%d, height=%d" gd_update(const char *tab, int x, int y, int w, int h) "tab=%s, x=%d, y=%d, w=%d, h=%d" gd_key_event(const char *tab, int gdk_keycode, int qemu_keycode, const char *action) "tab=%s, translated GDK keycode %d to QEMU keycode %d (%s)" -gd_grab(const char *tab, const char *device, bool on) "tab=%s, %s %d" +gd_grab(const char *tab, const char *device, const char *reason) "tab=%s, dev=%s, reason=%s" +gd_ungrab(const char *tab, const char *device) "tab=%s, dev=%s" # ui/vnc.c vnc_key_guest_leds(bool caps, bool num, bool scroll) "caps %d, num %d, scroll %d" @@ -1171,6 +1201,7 @@ vmware_scratch_write(uint32_t index, uint32_t value) "index %d, value 0x%x" vmware_setmode(uint32_t w, uint32_t h, uint32_t bpp) "%dx%d @ %d bpp" # hw/display/virtio-gpu.c +virtio_gpu_features(bool virgl) "virgl %d" virtio_gpu_cmd_get_display_info(void) "" virtio_gpu_cmd_get_caps(void) "" virtio_gpu_cmd_set_scanout(uint32_t id, uint32_t res, uint32_t w, uint32_t h, uint32_t x, uint32_t y) "id %d, res 0x%x, w %d, h %d, x %d, y %d" @@ -1180,22 +1211,57 @@ virtio_gpu_cmd_res_unref(uint32_t res) "res 0x%x" virtio_gpu_cmd_res_back_attach(uint32_t res) "res 0x%x" virtio_gpu_cmd_res_back_detach(uint32_t res) "res 0x%x" virtio_gpu_cmd_res_xfer_toh_2d(uint32_t res) "res 0x%x" +virtio_gpu_cmd_res_xfer_toh_3d(uint32_t res) "res 0x%x" +virtio_gpu_cmd_res_xfer_fromh_3d(uint32_t res) "res 0x%x" virtio_gpu_cmd_res_flush(uint32_t res, uint32_t w, uint32_t h, uint32_t x, uint32_t y) "res 0x%x, w %d, h %d, x %d, y %d" +virtio_gpu_cmd_ctx_create(uint32_t ctx, const char *name) "ctx 0x%x, name %s" +virtio_gpu_cmd_ctx_destroy(uint32_t ctx) "ctx 0x%x" +virtio_gpu_cmd_ctx_res_attach(uint32_t ctx, uint32_t res) "ctx 0x%x, res 0x%x" +virtio_gpu_cmd_ctx_res_detach(uint32_t ctx, uint32_t res) "ctx 0x%x, res 0x%x" +virtio_gpu_cmd_ctx_submit(uint32_t ctx, uint32_t size) "ctx 0x%x, size %d" +virtio_gpu_update_cursor(uint32_t scanout, uint32_t x, uint32_t y, const char *type, uint32_t res) "scanout %d, x %d, y %d, %s, res 0x%x" virtio_gpu_fence_ctrl(uint64_t fence, uint32_t type) "fence 0x%" PRIx64 ", type 0x%x" virtio_gpu_fence_resp(uint64_t fence) "fence 0x%" PRIx64 # migration/savevm.c qemu_loadvm_state_section(unsigned int section_type) "%d" +qemu_loadvm_state_section_command(int ret) "%d" qemu_loadvm_state_section_partend(uint32_t section_id) "%u" +qemu_loadvm_state_main(void) "" +qemu_loadvm_state_main_quit_parent(void) "" +qemu_loadvm_state_post_main(int ret) "%d" qemu_loadvm_state_section_startfull(uint32_t section_id, const char *idstr, uint32_t instance_id, uint32_t version_id) "%u(%s) %u %u" +qemu_savevm_send_packaged(void) "" +loadvm_handle_cmd_packaged(unsigned int length) "%u" +loadvm_handle_cmd_packaged_main(int ret) "%d" +loadvm_handle_cmd_packaged_received(int ret) "%d" +loadvm_postcopy_handle_advise(void) "" +loadvm_postcopy_handle_listen(void) "" +loadvm_postcopy_handle_run(void) "" +loadvm_postcopy_handle_run_cpu_sync(void) "" +loadvm_postcopy_handle_run_vmstart(void) "" +loadvm_postcopy_ram_handle_discard(void) "" +loadvm_postcopy_ram_handle_discard_end(void) "" +loadvm_postcopy_ram_handle_discard_header(const char *ramid, uint16_t len) "%s: %ud" +loadvm_process_command(uint16_t com, uint16_t len) "com=0x%x len=%d" +loadvm_process_command_ping(uint32_t val) "%x" +postcopy_ram_listen_thread_exit(void) "" +postcopy_ram_listen_thread_start(void) "" +qemu_savevm_send_postcopy_advise(void) "" +qemu_savevm_send_postcopy_ram_discard(const char *id, uint16_t len) "%s: %ud" +savevm_command_send(uint16_t command, uint16_t len) "com=0x%x len=%d" savevm_section_start(const char *id, unsigned int section_id) "%s, section_id %u" savevm_section_end(const char *id, unsigned int section_id, int ret) "%s, section_id %u -> %d" savevm_section_skip(const char *id, unsigned int section_id) "%s, section_id %u" +savevm_send_open_return_path(void) "" +savevm_send_ping(uint32_t val) "%x" +savevm_send_postcopy_listen(void) "" +savevm_send_postcopy_run(void) "" savevm_state_begin(void) "" savevm_state_header(void) "" savevm_state_iterate(void) "" -savevm_state_complete(void) "" -savevm_state_cancel(void) "" +savevm_state_cleanup(void) "" +savevm_state_complete_precopy(void) "" vmstate_save(const char *idstr, const char *vmsd_name) "%s, %s" vmstate_load(const char *idstr, const char *vmsd_name) "%s, %s" qemu_announce_self_iter(const char *mac) "%s" @@ -1213,9 +1279,14 @@ vmstate_subsection_load_good(const char *parent) "%s" qemu_file_fclose(void) "" # migration/ram.c +get_queued_page(const char *block_name, uint64_t tmp_offset, uint64_t ram_addr) "%s/%" PRIx64 " ram_addr=%" PRIx64 +get_queued_page_not_dirty(const char *block_name, uint64_t tmp_offset, uint64_t ram_addr, int sent) "%s/%" PRIx64 " ram_addr=%" PRIx64 " (sent=%d)" migration_bitmap_sync_start(void) "" -migration_bitmap_sync_end(uint64_t dirty_pages) "dirty_pages %" PRIu64"" +migration_bitmap_sync_end(uint64_t dirty_pages) "dirty_pages %" PRIu64 migration_throttle(void) "" +ram_load_postcopy_loop(uint64_t addr, int flags) "@%" PRIx64 " %x" +ram_postcopy_send_discard_bitmap(void) "" +ram_save_queue_pages(const char *rbname, size_t start, size_t len) "%s: start: %zx len: %zx" # hw/display/qxl.c disable qxl_interface_set_mm_time(int qid, uint32_t mm_time) "%d %d" @@ -1225,7 +1296,7 @@ qxl_create_guest_primary_rest(int qid, int32_t stride, uint32_t type, uint32_t f qxl_destroy_primary(int qid) "%d" qxl_enter_vga_mode(int qid) "%d" qxl_exit_vga_mode(int qid) "%d" -qxl_hard_reset(int qid, int64_t loadvm) "%d loadvm=%"PRId64"" +qxl_hard_reset(int qid, int64_t loadvm) "%d loadvm=%"PRId64 qxl_interface_async_complete_io(int qid, uint32_t current_async, void *cookie) "%d current=%d cookie=%p" qxl_interface_attach_worker(int qid) "%d" qxl_interface_get_init_info(int qid) "%d" @@ -1302,8 +1373,8 @@ spapr_pci_lsi_set(const char *busname, int pin, uint32_t irq) "%s PIN%d IRQ %u" spapr_pci_msi_retry(unsigned config_addr, unsigned req_num, unsigned max_irqs) "Guest device at %x asked %u, have only %u" # hw/pci/pci.c -pci_update_mappings_del(void *d, uint32_t bus, uint32_t func, uint32_t slot, int bar, uint64_t addr, uint64_t size) "d=%p %02x:%02x.%x %d,%#"PRIx64"+%#"PRIx64 -pci_update_mappings_add(void *d, uint32_t bus, uint32_t func, uint32_t slot, int bar, uint64_t addr, uint64_t size) "d=%p %02x:%02x.%x %d,%#"PRIx64"+%#"PRIx64 +pci_update_mappings_del(void *d, uint32_t bus, uint32_t slot, uint32_t func, int bar, uint64_t addr, uint64_t size) "d=%p %02x:%02x.%x %d,%#"PRIx64"+%#"PRIx64 +pci_update_mappings_add(void *d, uint32_t bus, uint32_t slot, uint32_t func, int bar, uint64_t addr, uint64_t size) "d=%p %02x:%02x.%x %d,%#"PRIx64"+%#"PRIx64 # hw/net/pcnet.c pcnet_s_reset(void *s) "s=%p" @@ -1338,8 +1409,6 @@ xics_ics_write_xive(int nr, int srcno, int server, uint8_t priority) "ics_write_ xics_ics_reject(int nr, int srcno) "reject irq %#x [src %d]" xics_ics_eoi(int nr) "ics_eoi: irq %#x" xics_alloc(int src, int irq) "source#%d, irq %d" -xics_alloc_failed_hint(int src, int irq) "source#%d, irq %d is already in use" -xics_alloc_failed_no_left(int src) "source#%d, no irq left" xics_alloc_block(int src, int first, int num, bool lsi, int align) "source#%d, first irq %d, %d irqs, lsi=%d, alignnum %d" xics_ics_free(int src, int irq, int num) "Source#%d, first irq %d, %d irqs" xics_ics_free_warn(int src, int irq) "Source#%d, irq %d is already free" @@ -1367,11 +1436,25 @@ spapr_iommu_new_table(uint64_t liobn, void *tcet, void *table, int fd) "liobn=%" # hw/ppc/ppc.c ppc_tb_adjust(uint64_t offs1, uint64_t offs2, int64_t diff, int64_t seconds) "adjusted from 0x%"PRIx64" to 0x%"PRIx64", diff %"PRId64" (%"PRId64"s)" +# hw/ppc/prep.c +prep_io_800_writeb(uint32_t addr, uint32_t val) "0x%08" PRIx32 " => 0x%02" PRIx32 +prep_io_800_readb(uint32_t addr, uint32_t retval) "0x%08" PRIx32 " <= 0x%02" PRIx32 + +# io/buffer.c +buffer_resize(const char *buf, size_t olen, size_t len) "%s: old %zd, new %zd" +buffer_move_empty(const char *buf, size_t len, const char *from) "%s: %zd bytes from %s" +buffer_move(const char *buf, size_t len, const char *from) "%s: %zd bytes from %s" +buffer_free(const char *buf, size_t len) "%s: capacity %zd" + # util/hbitmap.c hbitmap_iter_skip_words(const void *hb, void *hbi, uint64_t pos, unsigned long cur) "hb %p hbi %p pos %"PRId64" cur 0x%lx" hbitmap_reset(void *hb, uint64_t start, uint64_t count, uint64_t sbit, uint64_t ebit) "hb %p items %"PRIu64",%"PRIu64" bits %"PRIu64"..%"PRIu64 hbitmap_set(void *hb, uint64_t start, uint64_t count, uint64_t sbit, uint64_t ebit) "hb %p items %"PRIu64",%"PRIu64" bits %"PRIu64"..%"PRIu64 +# target-s390x/mmu_helper.c +get_skeys_nonzero(int rc) "SKEY: Call to get_skeys unexpectedly returned %d" +set_skeys_nonzero(int rc) "SKEY: Call to set_skeys unexpectedly returned %d" + # target-s390x/ioinst.c ioinst(const char *insn) "IOINST: %s" ioinst_sch_id(const char *insn, int cssid, int ssid, int schid) "IOINST: %s (%x.%x.%04x)" @@ -1397,15 +1480,40 @@ flic_no_device_api(int err) "flic: no Device Contral API support %d" flic_reset_failed(int err) "flic: reset failed %d" # migration.c +await_return_path_close_on_source_close(void) "" +await_return_path_close_on_source_joining(void) "" migrate_set_state(int new_state) "new state %d" migrate_fd_cleanup(void) "" migrate_fd_error(void) "" migrate_fd_cancel(void) "" -migrate_pending(uint64_t size, uint64_t max) "pending size %" PRIu64 " max %" PRIu64 -migrate_transferred(uint64_t tranferred, uint64_t time_spent, double bandwidth, uint64_t size) "transferred %" PRIu64 " time_spent %" PRIu64 " bandwidth %g max_size %" PRId64 -migrate_state_too_big(void) "" +migrate_handle_rp_req_pages(const char *rbname, size_t start, size_t len) "in %s at %zx len %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_send_rp_message(int msg_type, uint16_t len) "%d: len %d" +migration_completion_file_err(void) "" +migration_completion_postcopy_end(void) "" +migration_completion_postcopy_end_after_complete(void) "" +migration_completion_postcopy_end_before_rp(void) "" +migration_completion_postcopy_end_after_rp(int rp_error) "%d" +migration_thread_after_loop(void) "" +migration_thread_file_err(void) "" +migration_thread_setup_complete(void) "" +open_return_path_on_source(void) "" +open_return_path_on_source_continue(void) "" +postcopy_start(void) "" +postcopy_start_set_run(void) "" +source_return_path_thread_bad_end(void) "" +source_return_path_thread_end(void) "" +source_return_path_thread_entry(void) "" +source_return_path_thread_loop_top(void) "" +source_return_path_thread_pong(uint32_t val) "%x" +source_return_path_thread_shut(uint32_t val) "%x" migrate_global_state_post_load(const char *state) "loaded state: %s" migrate_global_state_pre_save(const char *state) "saved state: %s" +migration_thread_low_pending(uint64_t pending) "%" PRIu64 +migrate_state_too_big(void) "" +migrate_transferred(uint64_t tranferred, uint64_t time_spent, double bandwidth, uint64_t size) "transferred %" PRIu64 " time_spent %" PRIu64 " bandwidth %g max_size %" PRId64 +process_incoming_migration_co_end(int ret, int ps) "ret=%d postcopy-state=%d" +process_incoming_migration_co_postcopy_end_main(void) "" # migration/rdma.c qemu_rdma_accept_incoming_migration(void) "" @@ -1426,7 +1534,7 @@ qemu_rdma_exchange_get_response_none(const char *desc, int type) "Surprise: got qemu_rdma_exchange_send_issue_callback(void) "" qemu_rdma_exchange_send_waiting(const char *desc) "Waiting for response %s" qemu_rdma_exchange_send_received(const char *desc) "Response %s received." -qemu_rdma_fill(int64_t control_len, int size) "RDMA %" PRId64 " of %d bytes already in buffer" +qemu_rdma_fill(size_t control_len, size_t size) "RDMA %zd of %zd bytes already in buffer" qemu_rdma_init_ram_blocks(int blocks) "Allocated %d local ram block structures" qemu_rdma_poll_recv(const char *compstr, int64_t comp, int64_t id, int sent) "completion %s #%" PRId64 " received (%" PRId64 ") left %d" qemu_rdma_poll_write(const char *compstr, int64_t comp, int left, uint64_t block, uint64_t chunk, void *local, void *remote) "completions %s (%" PRId64 ") left %d, block %" PRIu64 ", chunk: %" PRIu64 " %p %p" @@ -1471,6 +1579,25 @@ rdma_start_incoming_migration_after_rdma_listen(void) "" rdma_start_outgoing_migration_after_rdma_connect(void) "" rdma_start_outgoing_migration_after_rdma_source_init(void) "" +# migration/postcopy-ram.c +postcopy_discard_send_finish(const char *ramblock, int nwords, int ncmds) "%s mask words sent=%d in %d commands" +postcopy_discard_send_range(const char *ramblock, unsigned long start, unsigned long length) "%s:%lx/%lx" +postcopy_ram_discard_range(void *start, size_t length) "%p,+%zx" +postcopy_cleanup_range(const char *ramblock, void *host_addr, size_t offset, size_t length) "%s: %p offset=%zx length=%zx" +postcopy_init_range(const char *ramblock, void *host_addr, size_t offset, size_t length) "%s: %p offset=%zx length=%zx" +postcopy_nhp_range(const char *ramblock, void *host_addr, size_t offset, size_t length) "%s: %p offset=%zx length=%zx" +postcopy_place_page(void *host_addr) "host=%p" +postcopy_place_page_zero(void *host_addr) "host=%p" +postcopy_ram_enable_notify(void) "" +postcopy_ram_fault_thread_entry(void) "" +postcopy_ram_fault_thread_exit(void) "" +postcopy_ram_fault_thread_quit(void) "" +postcopy_ram_fault_thread_request(uint64_t hostaddr, const char *ramblock, size_t offset) "Request for HVA=%" PRIx64 " rb=%s offset=%zx" +postcopy_ram_incoming_cleanup_closeuf(void) "" +postcopy_ram_incoming_cleanup_entry(void) "" +postcopy_ram_incoming_cleanup_exit(void) "" +postcopy_ram_incoming_cleanup_join(void) "" + # kvm-all.c kvm_ioctl(int type, void *arg) "type 0x%x, arg %p" kvm_vm_ioctl(int type, void *arg) "type 0x%x, arg %p" @@ -1494,8 +1621,12 @@ disable exec_tb_exit(void *next_tb, unsigned int flags) "tb:%p flags=%x" translate_block(void *tb, uintptr_t pc, uint8_t *tb_code) "tb:%p, pc:0x%"PRIxPTR", tb_code:%p" # memory.c -memory_region_ops_read(void *mr, uint64_t addr, uint64_t value, unsigned size) "mr %p addr %#"PRIx64" value %#"PRIx64" size %u" -memory_region_ops_write(void *mr, uint64_t addr, uint64_t value, unsigned size) "mr %p addr %#"PRIx64" value %#"PRIx64" size %u" +memory_region_ops_read(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr %#"PRIx64" value %#"PRIx64" size %u" +memory_region_ops_write(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr %#"PRIx64" value %#"PRIx64" size %u" +memory_region_subpage_read(int cpu_index, void *mr, uint64_t offset, uint64_t value, unsigned size) "cpu %d mr %p offset %#"PRIx64" value %#"PRIx64" size %u" +memory_region_subpage_write(int cpu_index, void *mr, uint64_t offset, uint64_t value, unsigned size) "cpu %d mr %p offset %#"PRIx64" value %#"PRIx64" size %u" +memory_region_tb_read(int cpu_index, uint64_t addr, uint64_t value, unsigned size) "cpu %d addr %#"PRIx64" value %#"PRIx64" size %u" +memory_region_tb_write(int cpu_index, uint64_t addr, uint64_t value, unsigned size) "cpu %d addr %#"PRIx64" value %#"PRIx64" size %u" # qom/object.c object_dynamic_cast_assert(const char *type, const char *target, const char *file, int line, const char *func) "%s->%s (%s:%d:%s)" @@ -1509,56 +1640,33 @@ xen_pv_mmio_write(uint64_t addr) "WARNING: write to Xen PV Device MMIO space (ad pci_cfg_read(const char *dev, unsigned devid, unsigned fnid, unsigned offs, unsigned val) "%s %02u:%u @0x%x -> 0x%x" pci_cfg_write(const char *dev, unsigned devid, unsigned fnid, unsigned offs, unsigned val) "%s %02u:%u @0x%x <- 0x%x" -# hw/vfio/vfio-pci.c +# hw/vfio/pci.c vfio_intx_interrupt(const char *name, char line) " (%s) Pin %c" -vfio_eoi(const char *name) " (%s) EOI" -vfio_enable_intx_kvm(const char *name) " (%s) KVM INTx accel enabled" -vfio_disable_intx_kvm(const char *name) " (%s) KVM INTx accel disabled" -vfio_update_irq(const char *name, int new_irq, int target_irq) " (%s) IRQ moved %d -> %d" -vfio_enable_intx(const char *name) " (%s)" -vfio_disable_intx(const char *name) " (%s)" +vfio_intx_eoi(const char *name) " (%s) EOI" +vfio_intx_enable_kvm(const char *name) " (%s) KVM INTx accel enabled" +vfio_intx_disable_kvm(const char *name) " (%s) KVM INTx accel disabled" +vfio_intx_update(const char *name, int new_irq, int target_irq) " (%s) IRQ moved %d -> %d" +vfio_intx_enable(const char *name) " (%s)" +vfio_intx_disable(const char *name) " (%s)" vfio_msi_interrupt(const char *name, int index, uint64_t addr, int data) " (%s) vector %d 0x%"PRIx64"/0x%x" vfio_msix_vector_do_use(const char *name, int index) " (%s) vector %d used" vfio_msix_vector_release(const char *name, int index) " (%s) vector %d released" -vfio_enable_msix(const char *name) " (%s)" -vfio_enable_msi(const char *name, int nr_vectors) " (%s) Enabled %d MSI vectors" -vfio_disable_msix(const char *name) " (%s)" -vfio_disable_msi(const char *name) " (%s)" +vfio_msix_enable(const char *name) " (%s)" +vfio_msix_pba_disable(const char *name) " (%s)" +vfio_msix_pba_enable(const char *name) " (%s)" +vfio_msix_disable(const char *name) " (%s)" +vfio_msix_fixup(const char *name, int bar, uint64_t start, uint64_t end) " (%s) MSI-X region %d mmap fixup [0x%"PRIx64" - 0x%"PRIx64"]" +vfio_msi_enable(const char *name, int nr_vectors) " (%s) Enabled %d MSI vectors" +vfio_msi_disable(const char *name) " (%s)" vfio_pci_load_rom(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device %s ROM:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx" vfio_rom_read(const char *name, uint64_t addr, int size, uint64_t data) " (%s, 0x%"PRIx64", 0x%x) = 0x%"PRIx64 vfio_pci_size_rom(const char *name, int size) "%s ROM size 0x%x" vfio_vga_write(uint64_t addr, uint64_t data, int size) " (0x%"PRIx64", 0x%"PRIx64", %d)" vfio_vga_read(uint64_t addr, int size, uint64_t data) " (0x%"PRIx64", %d) = 0x%"PRIx64 -# remove ) = -vfio_generic_window_quirk_read(const char * region_name, const char *name, int index, uint64_t addr, int size, uint64_t data) "%s read(%s:BAR%d+0x%"PRIx64", %d = 0x%"PRIx64 -## remove ) -vfio_generic_window_quirk_write(const char * region_name, const char *name, int index, uint64_t addr, uint64_t data, int size) "%s write(%s:BAR%d+0x%"PRIx64", 0x%"PRIx64", %d" -# remove ) = -vfio_generic_quirk_read(const char * region_name, const char *name, int index, uint64_t addr, int size, uint64_t data) "%s read(%s:BAR%d+0x%"PRIx64", %d = 0x%"PRIx64 -# remove ) -vfio_generic_quirk_write(const char * region_name, const char *name, int index, uint64_t addr, uint64_t data, int size) "%s write(%s:BAR%d+0x%"PRIx64", 0x%"PRIx64", %d" -vfio_ati_3c3_quirk_read(uint64_t data) " (0x3c3, 1) = 0x%"PRIx64 -vfio_vga_probe_ati_3c3_quirk(const char *name) "Enabled ATI/AMD quirk 0x3c3 BAR4for device %s" -vfio_probe_ati_bar4_window_quirk(const char *name) "Enabled ATI/AMD BAR4 window quirk for device %s" -#issue with ) -vfio_rtl8168_window_quirk_read_fake(const char *region_name, const char *name) "%s fake read(%s" -vfio_rtl8168_window_quirk_read_table(const char *region_name, const char *name) "%s MSI-X table read(%s" -vfio_rtl8168_window_quirk_read_direct(const char *region_name, const char *name) "%s direct read(%s" -vfio_rtl8168_window_quirk_write_table(const char *region_name, const char *name) "%s MSI-X table write(%s" -vfio_rtl8168_window_quirk_write_direct(const char *region_name, const char *name) "%s direct write(%s" -vfio_probe_rtl8168_bar2_window_quirk(const char *name) "Enabled RTL8168 BAR2 window quirk for device %s" -vfio_probe_ati_bar2_4000_quirk(const char *name) "Enabled ATI/AMD BAR2 0x4000 quirk for device %s" -vfio_nvidia_3d0_quirk_read(int size, uint64_t data) " (0x3d0, %d) = 0x%"PRIx64 -vfio_nvidia_3d0_quirk_write(uint64_t data, int size) " (0x3d0, 0x%"PRIx64", %d)" -vfio_vga_probe_nvidia_3d0_quirk(const char *name) "Enabled NVIDIA VGA 0x3d0 quirk for device %s" -vfio_probe_nvidia_bar5_window_quirk(const char *name) "Enabled NVIDIA BAR5 window quirk for device %s" -vfio_probe_nvidia_bar0_88000_quirk(const char *name) "Enabled NVIDIA BAR0 0x88000 quirk for device %s" -vfio_probe_nvidia_bar0_1800_quirk_id(int id) "Nvidia NV%02x" -vfio_probe_nvidia_bar0_1800_quirk(const char *name) "Enabled NVIDIA BAR0 0x1800 quirk for device %s" vfio_pci_read_config(const char *name, int addr, int len, int val) " (%s, @0x%x, len=0x%x) %x" vfio_pci_write_config(const char *name, int addr, int val, int len) " (%s, @0x%x, 0x%x, len=0x%x)" -vfio_setup_msi(const char *name, int pos) "%s PCI MSI CAP @0x%x" -vfio_early_setup_msix(const char *name, int pos, int table_bar, int offset, int entries) "%s PCI MSI-X CAP @0x%x, BAR %d, offset 0x%x, entries %d" +vfio_msi_setup(const char *name, int pos) "%s PCI MSI CAP @0x%x" +vfio_msix_early_setup(const char *name, int pos, int table_bar, int offset, int entries) "%s PCI MSI-X CAP @0x%x, BAR %d, offset 0x%x, entries %d" vfio_check_pcie_flr(const char *name) "%s Supports FLR via PCIe cap" vfio_check_pm_reset(const char *name) "%s Supports PM reset" vfio_check_af_flr(const char *name) "%s Supports FLR via AF cap" @@ -1566,13 +1674,47 @@ vfio_pci_hot_reset(const char *name, const char *type) " (%s) %s" vfio_pci_hot_reset_has_dep_devices(const char *name) "%s: hot reset dependent devices:" vfio_pci_hot_reset_dep_devices(int domain, int bus, int slot, int function, int group_id) "\t%04x:%02x:%02x.%x group %d" vfio_pci_hot_reset_result(const char *name, const char *result) "%s hot reset: %s" -vfio_populate_device_region(const char *region_name, int index, unsigned long size, unsigned long offset, unsigned long flags) "Device %s region %d:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx" vfio_populate_device_config(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device %s config:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx" vfio_populate_device_get_irq_info_failure(void) "VFIO_DEVICE_GET_IRQ_INFO failure: %m" vfio_initfn(const char *name, int group_id) " (%s) group %d" vfio_pci_reset(const char *name) " (%s)" vfio_pci_reset_flr(const char *name) "%s FLR/VFIO_DEVICE_RESET" vfio_pci_reset_pm(const char *name) "%s PCI PM Reset" +vfio_pci_emulated_vendor_id(const char *name, uint16_t val) "%s %04x" +vfio_pci_emulated_device_id(const char *name, uint16_t val) "%s %04x" +vfio_pci_emulated_sub_vendor_id(const char *name, uint16_t val) "%s %04x" +vfio_pci_emulated_sub_device_id(const char *name, uint16_t val) "%s %04x" + +# hw/vfio/pci-quirks. +vfio_quirk_rom_blacklisted(const char *name, uint16_t vid, uint16_t did) "%s %04x:%04x" +vfio_quirk_generic_window_address_write(const char *name, const char * region_name, uint64_t data) "%s %s 0x%"PRIx64 +vfio_quirk_generic_window_data_read(const char *name, const char * region_name, uint64_t data) "%s %s 0x%"PRIx64 +vfio_quirk_generic_window_data_write(const char *name, const char * region_name, uint64_t data) "%s %s 0x%"PRIx64 +vfio_quirk_generic_mirror_read(const char *name, const char * region_name, uint64_t addr, uint64_t data) "%s %s 0x%"PRIx64": 0x%"PRIx64 +vfio_quirk_generic_mirror_write(const char *name, const char * region_name, uint64_t addr, uint64_t data) "%s %s 0x%"PRIx64": 0x%"PRIx64 +vfio_quirk_ati_3c3_read(const char *name, uint64_t data) "%s 0x%"PRIx64 +vfio_quirk_ati_3c3_probe(const char *name) "%s" +vfio_quirk_ati_bar4_probe(const char *name) "%s" +vfio_quirk_ati_bar2_probe(const char *name) "%s" +vfio_quirk_nvidia_3d0_state(const char *name, const char *state) "%s %s" +vfio_quirk_nvidia_3d0_read(const char *name, uint8_t offset, unsigned size, uint64_t val) " (%s, @0x%x, len=0x%x) %"PRIx64 +vfio_quirk_nvidia_3d0_write(const char *name, uint8_t offset, uint64_t data, unsigned size) "(%s, @0x%x, 0x%"PRIx64", len=0x%x)" +vfio_quirk_nvidia_3d0_probe(const char *name) "%s" +vfio_quirk_nvidia_bar5_state(const char *name, const char *state) "%s %s" +vfio_quirk_nvidia_bar5_probe(const char *name) "%s" +vfio_quirk_nvidia_bar0_msi_ack(const char *name) "%s" +vfio_quirk_nvidia_bar0_probe(const char *name) "%s" +vfio_quirk_rtl8168_fake_latch(const char *name, uint64_t val) "%s 0x%"PRIx64 +vfio_quirk_rtl8168_msix_write(const char *name, uint16_t offset, uint64_t val) "%s MSI-X table write[0x%x]: 0x%"PRIx64 +vfio_quirk_rtl8168_msix_read(const char *name, uint16_t offset, uint64_t val) "%s MSI-X table read[0x%x]: 0x%"PRIx64 +vfio_quirk_rtl8168_probe(const char *name) "%s" + +vfio_quirk_ati_bonaire_reset_skipped(const char *name) "%s" +vfio_quirk_ati_bonaire_reset_no_smc(const char *name) "%s" +vfio_quirk_ati_bonaire_reset_timeout(const char *name) "%s" +vfio_quirk_ati_bonaire_reset_done(const char *name) "%s" +vfio_quirk_ati_bonaire_reset(const char *name) "%s" + # hw/vfio/vfio-common.c vfio_region_write(const char *name, int index, uint64_t addr, uint64_t data, unsigned size) " (%s:region%d+0x%"PRIx64", 0x%"PRIx64 ", %d)" @@ -1587,19 +1729,25 @@ vfio_disconnect_container(int fd) "close container->fd=%d" vfio_put_group(int fd) "close group->fd=%d" vfio_get_device(const char * name, unsigned int flags, unsigned int num_regions, unsigned int num_irqs) "Device %s flags: %u, regions: %u, irqs: %u" vfio_put_base_device(int fd) "close vdev->fd=%d" +vfio_region_setup(const char *dev, int index, const char *name, unsigned long flags, unsigned long offset, unsigned long size) "Device %s, region %d \"%s\", flags: %lx, offset: %lx, size: %lx" +vfio_region_mmap_fault(const char *name, int index, unsigned long offset, unsigned long size, int fault) "Region %s mmaps[%d], [%lx - %lx], fault: %d" +vfio_region_mmap(const char *name, unsigned long offset, unsigned long end) "Region %s [%lx - %lx]" +vfio_region_exit(const char *name, int index) "Device %s, region %d" +vfio_region_finalize(const char *name, int index) "Device %s, region %d" +vfio_region_mmaps_set_enabled(const char *name, bool enabled) "Region %s mmaps enabled: %d" # hw/vfio/platform.c -vfio_platform_populate_regions(int region_index, unsigned long flag, unsigned long size, int fd, unsigned long offset) "- region %d flags = 0x%lx, size = 0x%lx, fd= %d, offset = 0x%lx" vfio_platform_base_device_init(char *name, int groupid) "%s belongs to group #%d" vfio_platform_realize(char *name, char *compat) "vfio device %s, compat = %s" vfio_platform_eoi(int pin, int fd) "EOI IRQ pin %d (fd=%d)" -vfio_platform_mmap_set_enabled(bool enabled) "fast path = %d" vfio_platform_intp_mmap_enable(int pin) "IRQ #%d still active, stay in slow path" vfio_platform_intp_interrupt(int pin, int fd) "Inject IRQ #%d (fd = %d)" vfio_platform_intp_inject_pending_lockheld(int pin, int fd) "Inject pending IRQ #%d (fd = %d)" vfio_platform_populate_interrupts(int pin, int count, int flags) "- IRQ index %d: count %d, flags=0x%x" vfio_intp_interrupt_set_pending(int index) "irq %d is set PENDING" -vfio_platform_start_irqfd_injection(int index, int fd, int resamplefd) "IRQ index=%d, fd = %d, resamplefd = %d" +vfio_platform_start_level_irqfd_injection(int index, int fd, int resamplefd) "IRQ index=%d, fd = %d, resamplefd = %d" +vfio_platform_start_edge_irqfd_injection(int index, int fd) "IRQ index=%d, fd = %d" + #hw/acpi/memory_hotplug.c mhp_acpi_invalid_slot_selected(uint32_t slot) "0x%"PRIx32 @@ -1639,6 +1787,9 @@ cpu_unhalt(int cpu_index) "unhalting cpu %d" # hw/arm/virt-acpi-build.c virt_acpi_setup(void) "No fw cfg or ACPI disabled. Bailing out." +# hw/alpha/pci.c +alpha_pci_iack_write(void) "" + # audio/alsaaudio.c alsa_revents(int revents) "revents = %d" alsa_pollout(int i, int fd) "i = %d fd = %d" @@ -1654,3 +1805,107 @@ alsa_no_frames(int state) "No frames available and ALSA state is %d" # audio/ossaudio.c oss_version(int version) "OSS version = %#x" oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d" + +# crypto/tlscreds.c +qcrypto_tls_creds_load_dh(void *creds, const char *filename) "TLS creds load DH creds=%p filename=%s" +qcrypto_tls_creds_get_path(void *creds, const char *filename, const char *path) "TLS creds path creds=%p filename=%s path=%s" + +# crypto/tlscredsanon.c +qcrypto_tls_creds_anon_load(void *creds, const char *dir) "TLS creds anon load creds=%p dir=%s" + +# crypto/tlscredsx509.c +qcrypto_tls_creds_x509_load(void *creds, const char *dir) "TLS creds x509 load creds=%p dir=%s" +qcrypto_tls_creds_x509_check_basic_constraints(void *creds, const char *file, int status) "TLS creds x509 check basic constraints creds=%p file=%s status=%d" +qcrypto_tls_creds_x509_check_key_usage(void *creds, const char *file, int status, int usage, int critical) "TLS creds x509 check key usage creds=%p file=%s status=%d usage=%d critical=%d" +qcrypto_tls_creds_x509_check_key_purpose(void *creds, const char *file, int status, const char *usage, int critical) "TLS creds x509 check key usage creds=%p file=%s status=%d usage=%s critical=%d" +qcrypto_tls_creds_x509_load_cert(void *creds, int isServer, const char *file) "TLS creds x509 load cert creds=%p isServer=%d file=%s" +qcrypto_tls_creds_x509_load_cert_list(void *creds, const char *file) "TLS creds x509 load cert list creds=%p file=%s" + +# crypto/tlssession.c +qcrypto_tls_session_new(void *session, void *creds, const char *hostname, const char *aclname, int endpoint) "TLS session new session=%p creds=%p hostname=%s aclname=%s endpoint=%d" + +# net/vhost-user.c +vhost_user_event(const char *chr, int event) "chr: %s got event: %d" + +# linux-user/signal.c +user_setup_frame(void *env, uint64_t frame_addr) "env=%p frame_addr=%"PRIx64 +user_setup_rt_frame(void *env, uint64_t frame_addr) "env=%p frame_addr=%"PRIx64 +user_do_rt_sigreturn(void *env, uint64_t frame_addr) "env=%p frame_addr=%"PRIx64 +user_do_sigreturn(void *env, uint64_t frame_addr) "env=%p frame_addr=%"PRIx64 +user_force_sig(void *env, int target_sig, int host_sig) "env=%p signal %d (host %d)" +user_handle_signal(void *env, int target_sig) "env=%p signal %d" +user_host_signal(void *env, int host_sig, int target_sig) "env=%p signal %d (target %d(" +user_queue_signal(void *env, int target_sig) "env=%p signal %d" +user_s390x_restore_sigregs(void *env, uint64_t sc_psw_addr, uint64_t env_psw_addr) "env=%p frame psw.addr %"PRIx64 " current psw.addr %"PRIx64 + +# io/task.c +qio_task_new(void *task, void *source, void *func, void *opaque) "Task new task=%p source=%p func=%p opaque=%p" +qio_task_complete(void *task) "Task complete task=%p" +qio_task_abort(void *task) "Task abort task=%p" +qio_task_thread_start(void *task, void *worker, void *opaque) "Task thread start task=%p worker=%p opaque=%p" +qio_task_thread_run(void *task) "Task thread run task=%p" +qio_task_thread_exit(void *task) "Task thread exit task=%p" +qio_task_thread_result(void *task) "Task thread result task=%p" + +# io/channel-socket.c +qio_channel_socket_new(void *ioc) "Socket new ioc=%p" +qio_channel_socket_new_fd(void *ioc, int fd) "Socket new ioc=%p fd=%d" +qio_channel_socket_connect_sync(void *ioc, void *addr) "Socket connect sync ioc=%p addr=%p" +qio_channel_socket_connect_async(void *ioc, void *addr) "Socket connect async ioc=%p addr=%p" +qio_channel_socket_connect_fail(void *ioc) "Socket connect fail ioc=%p" +qio_channel_socket_connect_complete(void *ioc, int fd) "Socket connect complete ioc=%p fd=%d" +qio_channel_socket_listen_sync(void *ioc, void *addr) "Socket listen sync ioc=%p addr=%p" +qio_channel_socket_listen_async(void *ioc, void *addr) "Socket listen async ioc=%p addr=%p" +qio_channel_socket_listen_fail(void *ioc) "Socket listen fail ioc=%p" +qio_channel_socket_listen_complete(void *ioc, int fd) "Socket listen complete ioc=%p fd=%d" +qio_channel_socket_dgram_sync(void *ioc, void *localAddr, void *remoteAddr) "Socket dgram sync ioc=%p localAddr=%p remoteAddr=%p" +qio_channel_socket_dgram_async(void *ioc, void *localAddr, void *remoteAddr) "Socket dgram async ioc=%p localAddr=%p remoteAddr=%p" +qio_channel_socket_dgram_fail(void *ioc) "Socket dgram fail ioc=%p" +qio_channel_socket_dgram_complete(void *ioc, int fd) "Socket dgram complete ioc=%p fd=%d" +qio_channel_socket_accept(void *ioc) "Socket accept start ioc=%p" +qio_channel_socket_accept_fail(void *ioc) "Socket accept fail ioc=%p" +qio_channel_socket_accept_complete(void *ioc, void *cioc, int fd) "Socket accept complete ioc=%p cioc=%p fd=%d" + +# io/channel-file.c +qio_channel_file_new_fd(void *ioc, int fd) "File new fd ioc=%p fd=%d" +qio_channel_file_new_path(void *ioc, const char *path, int flags, int mode, int fd) "File new fd ioc=%p path=%s flags=%d mode=%d fd=%d" + +# io/channel-tls.c +qio_channel_tls_new_client(void *ioc, void *master, void *creds, const char *hostname) "TLS new client ioc=%p master=%p creds=%p hostname=%s" +qio_channel_tls_new_server(void *ioc, void *master, void *creds, const char *aclname) "TLS new client ioc=%p master=%p creds=%p acltname=%s" +qio_channel_tls_handshake_start(void *ioc) "TLS handshake start ioc=%p" +qio_channel_tls_handshake_pending(void *ioc, int status) "TLS handshake pending ioc=%p status=%d" +qio_channel_tls_handshake_fail(void *ioc) "TLS handshake fail ioc=%p" +qio_channel_tls_handshake_complete(void *ioc) "TLS handshake complete ioc=%p" +qio_channel_tls_credentials_allow(void *ioc) "TLS credentials allow ioc=%p" +qio_channel_tls_credentials_deny(void *ioc) "TLS credentials deny ioc=%p" + +# io/channel-websock.c +qio_channel_websock_new_server(void *ioc, void *master) "Websock new client ioc=%p master=%p" +qio_channel_websock_handshake_start(void *ioc) "Websock handshake start ioc=%p" +qio_channel_websock_handshake_pending(void *ioc, int status) "Websock handshake pending ioc=%p status=%d" +qio_channel_websock_handshake_reply(void *ioc) "Websock handshake reply ioc=%p" +qio_channel_websock_handshake_fail(void *ioc) "Websock handshake fail ioc=%p" +qio_channel_websock_handshake_complete(void *ioc) "Websock handshake complete ioc=%p" + +# io/channel-command.c +qio_channel_command_new_pid(void *ioc, int writefd, int readfd, int pid) "Command new pid ioc=%p writefd=%d readfd=%d pid=%d" +qio_channel_command_new_spawn(void *ioc, const char *binary, int flags) "Command new spawn ioc=%p binary=%s flags=%d" +qio_channel_command_abort(void *ioc, int pid) "Command abort ioc=%p pid=%d" +qio_channel_command_wait(void *ioc, int pid, int ret, int status) "Command abort ioc=%p pid=%d ret=%d status=%d" + +# hw/timer/aspeed_timer.c +aspeed_timer_ctrl_enable(uint8_t i, bool enable) "Timer %" PRIu8 ": %d" +aspeed_timer_ctrl_external_clock(uint8_t i, bool enable) "Timer %" PRIu8 ": %d" +aspeed_timer_ctrl_overflow_interrupt(uint8_t i, bool enable) "Timer %" PRIu8 ": %d" +aspeed_timer_ctrl_pulse_enable(uint8_t i, bool enable) "Timer %" PRIu8 ": %d" +aspeed_timer_set_ctrl2(uint32_t value) "Value: 0x%" PRIx32 +aspeed_timer_set_value(int timer, int reg, uint32_t value) "Timer %d register %d: 0x%" PRIx32 +aspeed_timer_read(uint64_t offset, unsigned size, uint64_t value) "From 0x%" PRIx64 ": of size %u: 0x%" PRIx64 + +# hw/intc/aspeed_vic.c +aspeed_vic_set_irq(int irq, int level) "Enabling IRQ %d: %d" +aspeed_vic_update_fiq(int flags) "Raising FIQ: %d" +aspeed_vic_update_irq(int flags) "Raising IRQ: %d" +aspeed_vic_read(uint64_t offset, unsigned size, uint32_t value) "From 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_vic_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 diff --git a/qemu/trace/Makefile.objs b/qemu/trace/Makefile.objs index 32f7a32ce..5145b34d1 100644 --- a/qemu/trace/Makefile.objs +++ b/qemu/trace/Makefile.objs @@ -1,24 +1,32 @@ # -*- mode: makefile -*- +###################################################################### +# tracetool source files +# Every rule that invokes tracetool must depend on this so code is regenerated +# if tracetool itself changes. + +tracetool-y = $(SRC_PATH)/scripts/tracetool.py +tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") + ###################################################################### # Auto-generated event descriptions for LTTng ust code ifeq ($(findstring ust,$(TRACE_BACKENDS)),ust) $(obj)/generated-ust-provider.h: $(obj)/generated-ust-provider.h-timestamp -$(obj)/generated-ust-provider.h-timestamp: $(SRC_PATH)/trace-events + @cmp $< $@ >/dev/null 2>&1 || cp $< $@ +$(obj)/generated-ust-provider.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(call quiet-command,$(TRACETOOL) \ --format=ust-events-h \ --backends=$(TRACE_BACKENDS) \ < $< > $@," GEN $(patsubst %-timestamp,%,$@)") - @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@) $(obj)/generated-ust.c: $(obj)/generated-ust.c-timestamp $(BUILD_DIR)/config-host.mak -$(obj)/generated-ust.c-timestamp: $(SRC_PATH)/trace-events + @cmp $< $@ >/dev/null 2>&1 || cp $< $@ +$(obj)/generated-ust.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(call quiet-command,$(TRACETOOL) \ --format=ust-events-c \ --backends=$(TRACE_BACKENDS) \ < $< > $@," GEN $(patsubst %-timestamp,%,$@)") - @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@) $(obj)/generated-events.h: $(obj)/generated-ust-provider.h $(obj)/generated-events.c: $(obj)/generated-ust.c @@ -28,20 +36,20 @@ endif # Auto-generated event descriptions $(obj)/generated-events.h: $(obj)/generated-events.h-timestamp -$(obj)/generated-events.h-timestamp: $(SRC_PATH)/trace-events + @cmp $< $@ >/dev/null 2>&1 || cp $< $@ +$(obj)/generated-events.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(call quiet-command,$(TRACETOOL) \ --format=events-h \ --backends=$(TRACE_BACKENDS) \ < $< > $@," GEN $(patsubst %-timestamp,%,$@)") - @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@) $(obj)/generated-events.c: $(obj)/generated-events.c-timestamp $(BUILD_DIR)/config-host.mak -$(obj)/generated-events.c-timestamp: $(SRC_PATH)/trace-events + @cmp $< $@ >/dev/null 2>&1 || cp $< $@ +$(obj)/generated-events.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(call quiet-command,$(TRACETOOL) \ --format=events-c \ --backends=$(TRACE_BACKENDS) \ < $< > $@," GEN $(patsubst %-timestamp,%,$@)") - @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@) util-obj-y += generated-events.o @@ -54,7 +62,7 @@ util-obj-y += generated-events.o $(obj)/generated-tracers.h: $(obj)/generated-tracers.h-timestamp @cmp -s $< $@ || cp $< $@ -$(obj)/generated-tracers.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak +$(obj)/generated-tracers.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak $(tracetool-y) $(call quiet-command,$(TRACETOOL) \ --format=h \ --backends=$(TRACE_BACKENDS) \ @@ -65,7 +73,7 @@ $(obj)/generated-tracers.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/conf $(obj)/generated-tracers.c: $(obj)/generated-tracers.c-timestamp @cmp -s $< $@ || cp $< $@ -$(obj)/generated-tracers.c-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak +$(obj)/generated-tracers.c-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak $(tracetool-y) $(call quiet-command,$(TRACETOOL) \ --format=c \ --backends=$(TRACE_BACKENDS) \ @@ -81,12 +89,12 @@ $(obj)/generated-tracers.o: $(obj)/generated-tracers.c $(obj)/generated-tracers. # rule file. So we use '.dtrace' instead ifeq ($(findstring dtrace,$(TRACE_BACKENDS)),dtrace) $(obj)/generated-tracers-dtrace.dtrace: $(obj)/generated-tracers-dtrace.dtrace-timestamp -$(obj)/generated-tracers-dtrace.dtrace-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak + @cmp $< $@ >/dev/null 2>&1 || cp $< $@ +$(obj)/generated-tracers-dtrace.dtrace-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak $(tracetool-y) $(call quiet-command,$(TRACETOOL) \ --format=d \ --backends=$(TRACE_BACKENDS) \ < $< > $@," GEN $(patsubst %-timestamp,%,$@)") - @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@) $(obj)/generated-tracers-dtrace.h: $(obj)/generated-tracers-dtrace.dtrace $(call quiet-command,dtrace -o $@ -h -s $<, " GEN $@") @@ -100,28 +108,28 @@ endif # Translation level $(obj)/generated-helpers-wrappers.h: $(obj)/generated-helpers-wrappers.h-timestamp -$(obj)/generated-helpers-wrappers.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak + @cmp $< $@ >/dev/null 2>&1 || cp $< $@ +$(obj)/generated-helpers-wrappers.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak $(tracetool-y) $(call quiet-command,$(TRACETOOL) \ --format=tcg-helper-wrapper-h \ --backend=$(TRACE_BACKENDS) \ < $< > $@," GEN $(patsubst %-timestamp,%,$@)") - @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@) $(obj)/generated-helpers.h: $(obj)/generated-helpers.h-timestamp -$(obj)/generated-helpers.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak + @cmp $< $@ >/dev/null 2>&1 || cp $< $@ +$(obj)/generated-helpers.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak $(tracetool-y) $(call quiet-command,$(TRACETOOL) \ --format=tcg-helper-h \ --backend=$(TRACE_BACKENDS) \ < $< > $@," GEN $(patsubst %-timestamp,%,$@)") - @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@) $(obj)/generated-helpers.c: $(obj)/generated-helpers.c-timestamp -$(obj)/generated-helpers.c-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak + @cmp $< $@ >/dev/null 2>&1 || cp $< $@ +$(obj)/generated-helpers.c-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak $(tracetool-y) $(call quiet-command,$(TRACETOOL) \ --format=tcg-helper-c \ --backend=$(TRACE_BACKENDS) \ < $< > $@," GEN $(patsubst %-timestamp,%,$@)") - @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@) $(obj)/generated-helpers.o: $(obj)/generated-helpers.c @@ -129,12 +137,12 @@ target-obj-y += generated-helpers.o $(obj)/generated-tcg-tracers.h: $(obj)/generated-tcg-tracers.h-timestamp -$(obj)/generated-tcg-tracers.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak + @cmp $< $@ >/dev/null 2>&1 || cp $< $@ +$(obj)/generated-tcg-tracers.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak $(tracetool-y) $(call quiet-command,$(TRACETOOL) \ --format=tcg-h \ --backend=$(TRACE_BACKENDS) \ < $< > $@," GEN $(patsubst %-timestamp,%,$@)") - @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@) ###################################################################### diff --git a/qemu/trace/control-internal.h b/qemu/trace/control-internal.h index 5a8df28c5..dcf67f505 100644 --- a/qemu/trace/control-internal.h +++ b/qemu/trace/control-internal.h @@ -10,10 +10,11 @@ #ifndef TRACE__CONTROL_INTERNAL_H #define TRACE__CONTROL_INTERNAL_H -#include extern TraceEvent trace_events[]; +extern bool trace_events_dstate[]; +extern int trace_events_enabled_count; static inline TraceEventID trace_event_count(void) @@ -51,17 +52,24 @@ static inline bool trace_event_get_state_static(TraceEvent *ev) return ev->sstate; } +static inline bool trace_event_get_state_dynamic_by_id(int id) +{ + return unlikely(trace_events_enabled_count) && trace_events_dstate[id]; +} + static inline bool trace_event_get_state_dynamic(TraceEvent *ev) { - assert(ev != NULL); - return ev->dstate; + int id = trace_event_get_id(ev); + return trace_event_get_state_dynamic_by_id(id); } static inline void trace_event_set_state_dynamic(TraceEvent *ev, bool state) { + int id = trace_event_get_id(ev); assert(ev != NULL); assert(trace_event_get_state_static(ev)); - ev->dstate = state; + trace_events_enabled_count += state - trace_events_dstate[id]; + trace_events_dstate[id] = state; } #endif /* TRACE__CONTROL_INTERNAL_H */ diff --git a/qemu/trace/control.c b/qemu/trace/control.c index 995beb384..d099f735d 100644 --- a/qemu/trace/control.c +++ b/qemu/trace/control.c @@ -7,14 +7,23 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "trace/control.h" +#include "qemu/help_option.h" #ifdef CONFIG_TRACE_SIMPLE #include "trace/simple.h" #endif #ifdef CONFIG_TRACE_FTRACE #include "trace/ftrace.h" #endif +#ifdef CONFIG_TRACE_LOG +#include "qemu/log.h" +#endif #include "qemu/error-report.h" +#include "monitor/monitor.h" + +int trace_events_enabled_count; +bool trace_events_dstate[TRACE_EVENT_COUNT]; TraceEvent *trace_event_name(const char *name) { @@ -85,7 +94,54 @@ TraceEvent *trace_event_pattern(const char *pat, TraceEvent *ev) return NULL; } -static void trace_init_events(const char *fname) +void trace_list_events(void) +{ + int i; + for (i = 0; i < trace_event_count(); i++) { + TraceEvent *res = trace_event_id(i); + fprintf(stderr, "%s\n", trace_event_get_name(res)); + } +} + +static void do_trace_enable_events(const char *line_buf) +{ + const bool enable = ('-' != line_buf[0]); + const char *line_ptr = enable ? line_buf : line_buf + 1; + + if (trace_event_is_pattern(line_ptr)) { + TraceEvent *ev = NULL; + while ((ev = trace_event_pattern(line_ptr, ev)) != NULL) { + if (trace_event_get_state_static(ev)) { + trace_event_set_state_dynamic(ev, enable); + } + } + } else { + TraceEvent *ev = trace_event_name(line_ptr); + if (ev == NULL) { + error_report("WARNING: trace event '%s' does not exist", + line_ptr); + } else if (!trace_event_get_state_static(ev)) { + error_report("WARNING: trace event '%s' is not traceable", + line_ptr); + } else { + trace_event_set_state_dynamic(ev, enable); + } + } +} + +void trace_enable_events(const char *line_buf) +{ + if (is_help_option(line_buf)) { + trace_list_events(); + if (cur_mon == NULL) { + exit(0); + } + } else { + do_trace_enable_events(line_buf); + } +} + +void trace_init_events(const char *fname) { Location loc; FILE *fp; @@ -111,27 +167,7 @@ static void trace_init_events(const char *fname) if ('#' == line_buf[0]) { /* skip commented lines */ continue; } - const bool enable = ('-' != line_buf[0]); - char *line_ptr = enable ? line_buf : line_buf + 1; - if (trace_event_is_pattern(line_ptr)) { - TraceEvent *ev = NULL; - while ((ev = trace_event_pattern(line_ptr, ev)) != NULL) { - if (trace_event_get_state_static(ev)) { - trace_event_set_state_dynamic(ev, enable); - } - } - } else { - TraceEvent *ev = trace_event_name(line_ptr); - if (ev == NULL) { - error_report("WARNING: trace event '%s' does not exist", - line_ptr); - } else if (!trace_event_get_state_static(ev)) { - error_report("WARNING: trace event '%s' is not traceable", - line_ptr); - } else { - trace_event_set_state_dynamic(ev, enable); - } - } + trace_enable_events(line_buf); } } if (fclose(fp) != 0) { @@ -142,17 +178,31 @@ static void trace_init_events(const char *fname) loc_pop(&loc); } -bool trace_init_backends(const char *events, const char *file) +void trace_init_file(const char *file) { #ifdef CONFIG_TRACE_SIMPLE - if (!st_init(file)) { - fprintf(stderr, "failed to initialize simple tracing backend.\n"); - return false; + st_set_trace_file(file); +#elif defined CONFIG_TRACE_LOG + /* If both the simple and the log backends are enabled, "-trace file" + * only applies to the simple backend; use "-D" for the log backend. + */ + if (file) { + qemu_set_log_filename(file); } #else if (file) { fprintf(stderr, "error: -trace file=...: " "option not supported by the selected tracing backends\n"); + exit(1); + } +#endif +} + +bool trace_init_backends(void) +{ +#ifdef CONFIG_TRACE_SIMPLE + if (!st_init()) { + fprintf(stderr, "failed to initialize simple tracing backend.\n"); return false; } #endif @@ -164,6 +214,5 @@ bool trace_init_backends(const char *events, const char *file) } #endif - trace_init_events(events); return true; } diff --git a/qemu/trace/control.h b/qemu/trace/control.h index da9bb6b77..e2ba6d4de 100644 --- a/qemu/trace/control.h +++ b/qemu/trace/control.h @@ -1,7 +1,7 @@ /* * Interface for configuring and controlling the state of tracing events. * - * Copyright (C) 2011-2014 Lluís Vilanova + * Copyright (C) 2011-2016 Lluís Vilanova * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -104,7 +104,7 @@ static const char * trace_event_get_name(TraceEvent *ev); * As a down side, you must always use an immediate #TraceEventID value. */ #define trace_event_get_state(id) \ - ((id ##_ENABLED) && trace_event_get_state_dynamic(trace_event_id(id))) + ((id ##_ENABLED) && trace_event_get_state_dynamic_by_id(id)) /** * trace_event_get_state_static: @@ -150,8 +150,6 @@ static void trace_event_set_state_dynamic(TraceEvent *ev, bool state); /** * trace_init_backends: - * @events: Name of file with events to be enabled at startup; may be NULL. - * Corresponds to commandline option "-trace events=...". * @file: Name of trace output file; may be NULL. * Corresponds to commandline option "-trace file=...". * @@ -159,7 +157,45 @@ static void trace_event_set_state_dynamic(TraceEvent *ev, bool state); * * Returns: Whether the backends could be successfully initialized. */ -bool trace_init_backends(const char *events, const char *file); +bool trace_init_backends(void); + +/** + * trace_init_events: + * @events: Name of file with events to be enabled at startup; may be NULL. + * Corresponds to commandline option "-trace events=...". + * + * Read the list of enabled tracing events. + * + * Returns: Whether the backends could be successfully initialized. + */ +void trace_init_events(const char *file); + +/** + * trace_init_file: + * @file: Name of trace output file; may be NULL. + * Corresponds to commandline option "-trace file=...". + * + * Record the name of the output file for the tracing backend. + * Exits if no selected backend does not support specifying the + * output file, and a non-NULL file was passed. + */ +void trace_init_file(const char *file); + +/** + * trace_list_events: + * + * List all available events. + */ +void trace_list_events(void); + +/** + * trace_enable_events: + * @line_buf: A string with a glob pattern of events to be enabled or, + * if the string starts with '-', disabled. + * + * Enable or disable matching events. + */ +void trace_enable_events(const char *line_buf); #include "trace/control-internal.h" diff --git a/qemu/trace/event-internal.h b/qemu/trace/event-internal.h index b2310d9be..86f6a511b 100644 --- a/qemu/trace/event-internal.h +++ b/qemu/trace/event-internal.h @@ -18,7 +18,6 @@ * @id: Unique event identifier. * @name: Event name. * @sstate: Static tracing state. - * @dstate: Dynamic tracing state. * * Opaque generic description of a tracing event. */ @@ -26,7 +25,6 @@ typedef struct TraceEvent { TraceEventID id; const char * name; const bool sstate; - bool dstate; } TraceEvent; diff --git a/qemu/trace/ftrace.c b/qemu/trace/ftrace.c index a7ae371e6..e953922f5 100644 --- a/qemu/trace/ftrace.c +++ b/qemu/trace/ftrace.c @@ -9,10 +9,7 @@ * */ -#include -#include -#include -#include +#include "qemu/osdep.h" #include "trace.h" #include "trace/control.h" diff --git a/qemu/trace/ftrace.h b/qemu/trace/ftrace.h index 863e052e9..92372e3ca 100644 --- a/qemu/trace/ftrace.h +++ b/qemu/trace/ftrace.h @@ -1,7 +1,6 @@ #ifndef TRACE_FTRACE_H #define TRACE_FTRACE_H -#include #define MAX_TRACE_STRLEN 512 diff --git a/qemu/trace/qmp.c b/qemu/trace/qmp.c index 0b1948952..8aa2660aa 100644 --- a/qemu/trace/qmp.c +++ b/qemu/trace/qmp.c @@ -7,7 +7,7 @@ * See the COPYING file in the top-level directory. */ -#include "qemu/typedefs.h" +#include "qemu/osdep.h" #include "qmp-commands.h" #include "trace/control.h" diff --git a/qemu/trace/simple.c b/qemu/trace/simple.c index 11ad03093..3fdcc8226 100644 --- a/qemu/trace/simple.c +++ b/qemu/trace/simple.c @@ -8,12 +8,8 @@ * */ -#include -#include -#include -#include +#include "qemu/osdep.h" #ifndef _WIN32 -#include #include #endif #include "qemu/timer.h" @@ -322,20 +318,20 @@ void st_set_trace_file_enabled(bool enable) * @file The trace file name or NULL for the default name- set at * config time */ -bool st_set_trace_file(const char *file) +void st_set_trace_file(const char *file) { st_set_trace_file_enabled(false); g_free(trace_file_name); if (!file) { - trace_file_name = g_strdup_printf(CONFIG_TRACE_FILE, getpid()); + /* Type cast needed for Windows where getpid() returns an int. */ + trace_file_name = g_strdup_printf(CONFIG_TRACE_FILE, (pid_t)getpid()); } else { trace_file_name = g_strdup_printf("%s", file); } st_set_trace_file_enabled(true); - return true; } void st_print_trace_file_status(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...)) @@ -373,7 +369,7 @@ static GThread *trace_thread_create(GThreadFunc fn) return thread; } -bool st_init(const char *file) +bool st_init(void) { GThread *thread; @@ -386,6 +382,5 @@ bool st_init(const char *file) } atexit(st_flush_trace_buffer); - st_set_trace_file(file); return true; } diff --git a/qemu/trace/simple.h b/qemu/trace/simple.h index 699799685..1e7de4557 100644 --- a/qemu/trace/simple.h +++ b/qemu/trace/simple.h @@ -11,17 +11,14 @@ #ifndef TRACE_SIMPLE_H #define TRACE_SIMPLE_H -#include -#include -#include #include "trace/generated-events.h" void st_print_trace_file_status(FILE *stream, fprintf_function stream_printf); void st_set_trace_file_enabled(bool enable); -bool st_set_trace_file(const char *file); -bool st_init(const char *file); +void st_set_trace_file(const char *file); +bool st_init(void); void st_flush_trace_buffer(void); typedef struct { diff --git a/qemu/translate-all.c b/qemu/translate-all.c index 60a3d8b2b..8329ea60e 100644 --- a/qemu/translate-all.c +++ b/qemu/translate-all.c @@ -19,16 +19,10 @@ #ifdef _WIN32 #include #else -#include #include #endif -#include -#include -#include -#include -#include +#include "qemu/osdep.h" -#include "config.h" #include "qemu-common.h" #define NO_CPU_IO_DEFS @@ -43,7 +37,6 @@ #if __FreeBSD_version >= 700104 #define HAVE_KINFO_GETVMMAP #define sigqueue sigqueue_freebsd /* avoid redefinition */ -#include #include #include #define _KERNEL @@ -62,6 +55,7 @@ #include "translate-all.h" #include "qemu/bitmap.h" #include "qemu/timer.h" +#include "exec/log.h" //#define DEBUG_TB_INVALIDATE //#define DEBUG_FLUSH @@ -117,18 +111,48 @@ typedef struct PageDesc { #define V_L1_SHIFT (L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS - V_L1_BITS) -uintptr_t qemu_real_host_page_size; -uintptr_t qemu_real_host_page_mask; uintptr_t qemu_host_page_size; -uintptr_t qemu_host_page_mask; +intptr_t qemu_host_page_mask; -/* This is a multi-level map on the virtual address space. - The bottom level has pointers to PageDesc. */ +/* The bottom level has pointers to PageDesc */ static void *l1_map[V_L1_SIZE]; /* code generation context */ TCGContext tcg_ctx; +/* translation block context */ +#ifdef CONFIG_USER_ONLY +__thread int have_tb_lock; +#endif + +void tb_lock(void) +{ +#ifdef CONFIG_USER_ONLY + assert(!have_tb_lock); + qemu_mutex_lock(&tcg_ctx.tb_ctx.tb_lock); + have_tb_lock++; +#endif +} + +void tb_unlock(void) +{ +#ifdef CONFIG_USER_ONLY + assert(have_tb_lock); + have_tb_lock--; + qemu_mutex_unlock(&tcg_ctx.tb_ctx.tb_lock); +#endif +} + +void tb_lock_reset(void) +{ +#ifdef CONFIG_USER_ONLY + if (have_tb_lock) { + qemu_mutex_unlock(&tcg_ctx.tb_ctx.tb_lock); + have_tb_lock = 0; + } +#endif +} + static void tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, tb_page_addr_t phys_page2); static TranslationBlock *tb_find_pc(uintptr_t tc_ptr); @@ -138,124 +162,137 @@ void cpu_gen_init(void) tcg_context_init(&tcg_ctx); } -/* return non zero if the very first instruction is invalid so that - the virtual CPU can trigger an exception. +/* Encode VAL as a signed leb128 sequence at P. + Return P incremented past the encoded value. */ +static uint8_t *encode_sleb128(uint8_t *p, target_long val) +{ + int more, byte; + + do { + byte = val & 0x7f; + val >>= 7; + more = !((val == 0 && (byte & 0x40) == 0) + || (val == -1 && (byte & 0x40) != 0)); + if (more) { + byte |= 0x80; + } + *p++ = byte; + } while (more); - '*gen_code_size_ptr' contains the size of the generated code (host - code). -*/ -int cpu_gen_code(CPUArchState *env, TranslationBlock *tb, int *gen_code_size_ptr) + return p; +} + +/* Decode a signed leb128 sequence at *PP; increment *PP past the + decoded value. Return the decoded value. */ +static target_long decode_sleb128(uint8_t **pp) { - TCGContext *s = &tcg_ctx; - tcg_insn_unit *gen_code_buf; - int gen_code_size; -#ifdef CONFIG_PROFILER - int64_t ti; -#endif + uint8_t *p = *pp; + target_long val = 0; + int byte, shift = 0; -#ifdef CONFIG_PROFILER - s->tb_count1++; /* includes aborted translations because of - exceptions */ - ti = profile_getclock(); -#endif - tcg_func_start(s); + do { + byte = *p++; + val |= (target_ulong)(byte & 0x7f) << shift; + shift += 7; + } while (byte & 0x80); + if (shift < TARGET_LONG_BITS && (byte & 0x40)) { + val |= -(target_ulong)1 << shift; + } - gen_intermediate_code(env, tb); + *pp = p; + return val; +} - trace_translate_block(tb, tb->pc, tb->tc_ptr); +/* Encode the data collected about the instructions while compiling TB. + Place the data at BLOCK, and return the number of bytes consumed. - /* generate machine code */ - gen_code_buf = tb->tc_ptr; - tb->tb_next_offset[0] = 0xffff; - tb->tb_next_offset[1] = 0xffff; - s->tb_next_offset = tb->tb_next_offset; -#ifdef USE_DIRECT_JUMP - s->tb_jmp_offset = tb->tb_jmp_offset; - s->tb_next = NULL; -#else - s->tb_jmp_offset = NULL; - s->tb_next = tb->tb_next; -#endif + The logical table consisits of TARGET_INSN_START_WORDS target_ulong's, + which come from the target's insn_start data, followed by a uintptr_t + which comes from the host pc of the end of the code implementing the insn. -#ifdef CONFIG_PROFILER - s->tb_count++; - s->interm_time += profile_getclock() - ti; - s->code_time -= profile_getclock(); -#endif - gen_code_size = tcg_gen_code(s, gen_code_buf); - *gen_code_size_ptr = gen_code_size; -#ifdef CONFIG_PROFILER - s->code_time += profile_getclock(); - s->code_in_len += tb->size; - s->code_out_len += gen_code_size; -#endif + Each line of the table is encoded as sleb128 deltas from the previous + line. The seed for the first line is { tb->pc, 0..., tb->tc_ptr }. + That is, the first column is seeded with the guest pc, the last column + with the host pc, and the middle columns with zeros. */ -#ifdef DEBUG_DISAS - if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM)) { - qemu_log("OUT: [size=%d]\n", gen_code_size); - log_disas(tb->tc_ptr, gen_code_size); - qemu_log("\n"); - qemu_log_flush(); +static int encode_search(TranslationBlock *tb, uint8_t *block) +{ + uint8_t *highwater = tcg_ctx.code_gen_highwater; + uint8_t *p = block; + int i, j, n; + + tb->tc_search = block; + + for (i = 0, n = tb->icount; i < n; ++i) { + target_ulong prev; + + for (j = 0; j < TARGET_INSN_START_WORDS; ++j) { + if (i == 0) { + prev = (j == 0 ? tb->pc : 0); + } else { + prev = tcg_ctx.gen_insn_data[i - 1][j]; + } + p = encode_sleb128(p, tcg_ctx.gen_insn_data[i][j] - prev); + } + prev = (i == 0 ? 0 : tcg_ctx.gen_insn_end_off[i - 1]); + p = encode_sleb128(p, tcg_ctx.gen_insn_end_off[i] - prev); + + /* Test for (pending) buffer overflow. The assumption is that any + one row beginning below the high water mark cannot overrun + the buffer completely. Thus we can test for overflow after + encoding a row without having to check during encoding. */ + if (unlikely(p > highwater)) { + return -1; + } } -#endif - return 0; + + return p - block; } -/* The cpu state corresponding to 'searched_pc' is restored. - */ +/* The cpu state corresponding to 'searched_pc' is restored. */ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, uintptr_t searched_pc) { + target_ulong data[TARGET_INSN_START_WORDS] = { tb->pc }; + uintptr_t host_pc = (uintptr_t)tb->tc_ptr; CPUArchState *env = cpu->env_ptr; - TCGContext *s = &tcg_ctx; - int j; - uintptr_t tc_ptr; + uint8_t *p = tb->tc_search; + int i, j, num_insns = tb->icount; #ifdef CONFIG_PROFILER - int64_t ti; + int64_t ti = profile_getclock(); #endif -#ifdef CONFIG_PROFILER - ti = profile_getclock(); -#endif - tcg_func_start(s); + if (searched_pc < host_pc) { + return -1; + } - gen_intermediate_code_pc(env, tb); + /* Reconstruct the stored insn data while looking for the point at + which the end of the insn exceeds the searched_pc. */ + for (i = 0; i < num_insns; ++i) { + for (j = 0; j < TARGET_INSN_START_WORDS; ++j) { + data[j] += decode_sleb128(&p); + } + host_pc += decode_sleb128(&p); + if (host_pc > searched_pc) { + goto found; + } + } + return -1; + found: if (tb->cflags & CF_USE_ICOUNT) { + assert(use_icount); /* Reset the cycle counter to the start of the block. */ - cpu->icount_decr.u16.low += tb->icount; + cpu->icount_decr.u16.low += num_insns; /* Clear the IO flag. */ cpu->can_do_io = 0; } - - /* find opc index corresponding to search_pc */ - tc_ptr = (uintptr_t)tb->tc_ptr; - if (searched_pc < tc_ptr) - return -1; - - s->tb_next_offset = tb->tb_next_offset; -#ifdef USE_DIRECT_JUMP - s->tb_jmp_offset = tb->tb_jmp_offset; - s->tb_next = NULL; -#else - s->tb_jmp_offset = NULL; - s->tb_next = tb->tb_next; -#endif - j = tcg_gen_code_search_pc(s, (tcg_insn_unit *)tc_ptr, - searched_pc - tc_ptr); - if (j < 0) - return -1; - /* now find start of instruction before */ - while (s->gen_opc_instr_start[j] == 0) { - j--; - } - cpu->icount_decr.u16.low -= s->gen_opc_icount[j]; - - restore_state_to_opc(env, tb, j); + cpu->icount_decr.u16.low -= i; + restore_state_to_opc(env, tb, data); #ifdef CONFIG_PROFILER - s->restore_time += profile_getclock() - ti; - s->restore_count++; + tcg_ctx.restore_time += profile_getclock() - ti; + tcg_ctx.restore_count++; #endif return 0; } @@ -278,44 +315,19 @@ bool cpu_restore_state(CPUState *cpu, uintptr_t retaddr) return false; } -#ifdef _WIN32 -static __attribute__((unused)) void map_exec(void *addr, long size) -{ - DWORD old_protect; - VirtualProtect(addr, size, - PAGE_EXECUTE_READWRITE, &old_protect); -} -#else -static __attribute__((unused)) void map_exec(void *addr, long size) -{ - unsigned long start, end, page_size; - - page_size = getpagesize(); - start = (unsigned long)addr; - start &= ~(page_size - 1); - - end = (unsigned long)addr + size; - end += page_size - 1; - end &= ~(page_size - 1); - - mprotect((void *)start, end - start, - PROT_READ | PROT_WRITE | PROT_EXEC); -} -#endif - void page_size_init(void) { /* NOTE: we can always suppose that qemu_host_page_size >= TARGET_PAGE_SIZE */ qemu_real_host_page_size = getpagesize(); - qemu_real_host_page_mask = ~(qemu_real_host_page_size - 1); + qemu_real_host_page_mask = -(intptr_t)qemu_real_host_page_size; if (qemu_host_page_size == 0) { qemu_host_page_size = qemu_real_host_page_size; } if (qemu_host_page_size < TARGET_PAGE_SIZE) { qemu_host_page_size = TARGET_PAGE_SIZE; } - qemu_host_page_mask = ~(qemu_host_page_size - 1); + qemu_host_page_mask = -(intptr_t)qemu_host_page_size; } static void page_init(void) @@ -387,6 +399,9 @@ static void page_init(void) #endif } +/* If alloc=1: + * Called with mmap_lock held for user-mode emulation. + */ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) { PageDesc *pd; @@ -398,26 +413,26 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) /* Level 2..N-1. */ for (i = V_L1_SHIFT / V_L2_BITS - 1; i > 0; i--) { - void **p = *lp; + void **p = atomic_rcu_read(lp); if (p == NULL) { if (!alloc) { return NULL; } p = g_new0(void *, V_L2_SIZE); - *lp = p; + atomic_rcu_set(lp, p); } lp = p + ((index >> (i * V_L2_BITS)) & (V_L2_SIZE - 1)); } - pd = *lp; + pd = atomic_rcu_read(lp); if (pd == NULL) { if (!alloc) { return NULL; } pd = g_new0(PageDesc, V_L2_SIZE); - *lp = pd; + atomic_rcu_set(lp, pd); } return pd + (index & (V_L2_SIZE - 1)); @@ -428,11 +443,6 @@ static inline PageDesc *page_find(tb_page_addr_t index) return page_find_alloc(index, 0); } -#if !defined(CONFIG_USER_ONLY) -#define mmap_lock() do { } while (0) -#define mmap_unlock() do { } while (0) -#endif - #if defined(CONFIG_USER_ONLY) /* Currently it is not recommended to allocate big chunks of data in user mode. It will change when a dedicated libc will be used. */ @@ -441,14 +451,6 @@ static inline PageDesc *page_find(tb_page_addr_t index) #define USE_STATIC_CODE_GEN_BUFFER #endif -/* ??? Should configure for this, not list operating systems here. */ -#if (defined(__linux__) \ - || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) \ - || defined(__DragonFly__) || defined(__OpenBSD__) \ - || defined(__NetBSD__)) -# define USE_MMAP -#endif - /* Minimum size of the code gen buffer. This number is randomly chosen, but not so small that we can't have a fair number of TB's live. */ #define MIN_CODE_GEN_BUFFER_SIZE (1024u * 1024) @@ -460,6 +462,8 @@ static inline PageDesc *page_find(tb_page_addr_t index) # define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024) #elif defined(__sparc__) # define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024) +#elif defined(__powerpc64__) +# define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024) #elif defined(__aarch64__) # define MAX_CODE_GEN_BUFFER_SIZE (128ul * 1024 * 1024) #elif defined(__arm__) @@ -536,22 +540,102 @@ static inline void *split_cross_256mb(void *buf1, size_t size1) static uint8_t static_code_gen_buffer[DEFAULT_CODE_GEN_BUFFER_SIZE] __attribute__((aligned(CODE_GEN_ALIGN))); +# ifdef _WIN32 +static inline void do_protect(void *addr, long size, int prot) +{ + DWORD old_protect; + VirtualProtect(addr, size, prot, &old_protect); +} + +static inline void map_exec(void *addr, long size) +{ + do_protect(addr, size, PAGE_EXECUTE_READWRITE); +} + +static inline void map_none(void *addr, long size) +{ + do_protect(addr, size, PAGE_NOACCESS); +} +# else +static inline void do_protect(void *addr, long size, int prot) +{ + uintptr_t start, end; + + start = (uintptr_t)addr; + start &= qemu_real_host_page_mask; + + end = (uintptr_t)addr + size; + end = ROUND_UP(end, qemu_real_host_page_size); + + mprotect((void *)start, end - start, prot); +} + +static inline void map_exec(void *addr, long size) +{ + do_protect(addr, size, PROT_READ | PROT_WRITE | PROT_EXEC); +} + +static inline void map_none(void *addr, long size) +{ + do_protect(addr, size, PROT_NONE); +} +# endif /* WIN32 */ + static inline void *alloc_code_gen_buffer(void) { void *buf = static_code_gen_buffer; + size_t full_size, size; + + /* The size of the buffer, rounded down to end on a page boundary. */ + full_size = (((uintptr_t)buf + sizeof(static_code_gen_buffer)) + & qemu_real_host_page_mask) - (uintptr_t)buf; + + /* Reserve a guard page. */ + size = full_size - qemu_real_host_page_size; + + /* Honor a command-line option limiting the size of the buffer. */ + if (size > tcg_ctx.code_gen_buffer_size) { + size = (((uintptr_t)buf + tcg_ctx.code_gen_buffer_size) + & qemu_real_host_page_mask) - (uintptr_t)buf; + } + tcg_ctx.code_gen_buffer_size = size; + #ifdef __mips__ - if (cross_256mb(buf, tcg_ctx.code_gen_buffer_size)) { - buf = split_cross_256mb(buf, tcg_ctx.code_gen_buffer_size); + if (cross_256mb(buf, size)) { + buf = split_cross_256mb(buf, size); + size = tcg_ctx.code_gen_buffer_size; } #endif - map_exec(buf, tcg_ctx.code_gen_buffer_size); + + map_exec(buf, size); + map_none(buf + size, qemu_real_host_page_size); + qemu_madvise(buf, size, QEMU_MADV_HUGEPAGE); + return buf; } -#elif defined(USE_MMAP) +#elif defined(_WIN32) +static inline void *alloc_code_gen_buffer(void) +{ + size_t size = tcg_ctx.code_gen_buffer_size; + void *buf1, *buf2; + + /* Perform the allocation in two steps, so that the guard page + is reserved but uncommitted. */ + buf1 = VirtualAlloc(NULL, size + qemu_real_host_page_size, + MEM_RESERVE, PAGE_NOACCESS); + if (buf1 != NULL) { + buf2 = VirtualAlloc(buf1, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + assert(buf1 == buf2); + } + + return buf1; +} +#else static inline void *alloc_code_gen_buffer(void) { int flags = MAP_PRIVATE | MAP_ANONYMOUS; uintptr_t start = 0; + size_t size = tcg_ctx.code_gen_buffer_size; void *buf; /* Constrain the position of the buffer based on the host cpu. @@ -567,86 +651,70 @@ static inline void *alloc_code_gen_buffer(void) Leave the choice of exact location with the kernel. */ flags |= MAP_32BIT; /* Cannot expect to map more than 800MB in low memory. */ - if (tcg_ctx.code_gen_buffer_size > 800u * 1024 * 1024) { - tcg_ctx.code_gen_buffer_size = 800u * 1024 * 1024; + if (size > 800u * 1024 * 1024) { + tcg_ctx.code_gen_buffer_size = size = 800u * 1024 * 1024; } # elif defined(__sparc__) start = 0x40000000ul; # elif defined(__s390x__) start = 0x90000000ul; # elif defined(__mips__) - /* ??? We ought to more explicitly manage layout for softmmu too. */ -# ifdef CONFIG_USER_ONLY - start = 0x68000000ul; -# elif _MIPS_SIM == _ABI64 +# if _MIPS_SIM == _ABI64 start = 0x128000000ul; # else start = 0x08000000ul; # endif # endif - buf = mmap((void *)start, tcg_ctx.code_gen_buffer_size, - PROT_WRITE | PROT_READ | PROT_EXEC, flags, -1, 0); + buf = mmap((void *)start, size + qemu_real_host_page_size, + PROT_NONE, flags, -1, 0); if (buf == MAP_FAILED) { return NULL; } #ifdef __mips__ - if (cross_256mb(buf, tcg_ctx.code_gen_buffer_size)) { + if (cross_256mb(buf, size)) { /* Try again, with the original still mapped, to avoid re-acquiring that 256mb crossing. This time don't specify an address. */ - size_t size2, size1 = tcg_ctx.code_gen_buffer_size; - void *buf2 = mmap(NULL, size1, PROT_WRITE | PROT_READ | PROT_EXEC, - flags, -1, 0); - if (buf2 != MAP_FAILED) { - if (!cross_256mb(buf2, size1)) { + size_t size2; + void *buf2 = mmap(NULL, size + qemu_real_host_page_size, + PROT_NONE, flags, -1, 0); + switch (buf2 != MAP_FAILED) { + case 1: + if (!cross_256mb(buf2, size)) { /* Success! Use the new buffer. */ - munmap(buf, size1); - return buf2; + munmap(buf, size); + break; } /* Failure. Work with what we had. */ - munmap(buf2, size1); + munmap(buf2, size); + /* fallthru */ + default: + /* Split the original buffer. Free the smaller half. */ + buf2 = split_cross_256mb(buf, size); + size2 = tcg_ctx.code_gen_buffer_size; + if (buf == buf2) { + munmap(buf + size2 + qemu_real_host_page_size, size - size2); + } else { + munmap(buf, size - size2); + } + size = size2; + break; } - - /* Split the original buffer. Free the smaller half. */ - buf2 = split_cross_256mb(buf, size1); - size2 = tcg_ctx.code_gen_buffer_size; - munmap(buf + (buf == buf2 ? size2 : 0), size1 - size2); - return buf2; + buf = buf2; } #endif - return buf; -} -#else -static inline void *alloc_code_gen_buffer(void) -{ - void *buf = g_try_malloc(tcg_ctx.code_gen_buffer_size); - - if (buf == NULL) { - return NULL; - } + /* Make the final buffer accessible. The guard page at the end + will remain inaccessible with PROT_NONE. */ + mprotect(buf, size, PROT_WRITE | PROT_READ | PROT_EXEC); -#ifdef __mips__ - if (cross_256mb(buf, tcg_ctx.code_gen_buffer_size)) { - void *buf2 = g_malloc(tcg_ctx.code_gen_buffer_size); - if (buf2 != NULL && !cross_256mb(buf2, size1)) { - /* Success! Use the new buffer. */ - free(buf); - buf = buf2; - } else { - /* Failure. Work with what we had. Since this is malloc - and not mmap, we can't free the other half. */ - free(buf2); - buf = split_cross_256mb(buf, tcg_ctx.code_gen_buffer_size); - } - } -#endif + /* Request large pages for the buffer. */ + qemu_madvise(buf, size, QEMU_MADV_HUGEPAGE); - map_exec(buf, tcg_ctx.code_gen_buffer_size); return buf; } -#endif /* USE_STATIC_CODE_GEN_BUFFER, USE_MMAP */ +#endif /* USE_STATIC_CODE_GEN_BUFFER, WIN32, POSIX */ static inline void code_gen_alloc(size_t tb_size) { @@ -657,24 +725,14 @@ static inline void code_gen_alloc(size_t tb_size) exit(1); } - qemu_madvise(tcg_ctx.code_gen_buffer, tcg_ctx.code_gen_buffer_size, - QEMU_MADV_HUGEPAGE); + /* Estimate a good size for the number of TBs we can support. We + still haven't deducted the prologue from the buffer size here, + but that's minimal and won't affect the estimate much. */ + tcg_ctx.code_gen_max_blocks + = tcg_ctx.code_gen_buffer_size / CODE_GEN_AVG_BLOCK_SIZE; + tcg_ctx.tb_ctx.tbs = g_new(TranslationBlock, tcg_ctx.code_gen_max_blocks); - /* Steal room for the prologue at the end of the buffer. This ensures - (via the MAX_CODE_GEN_BUFFER_SIZE limits above) that direct branches - from TB's to the prologue are going to be in range. It also means - that we don't need to mark (additional) portions of the data segment - as executable. */ - tcg_ctx.code_gen_prologue = tcg_ctx.code_gen_buffer + - tcg_ctx.code_gen_buffer_size - 1024; - tcg_ctx.code_gen_buffer_size -= 1024; - - tcg_ctx.code_gen_buffer_max_size = tcg_ctx.code_gen_buffer_size - - (TCG_MAX_OP_SIZE * OPC_BUF_SIZE); - tcg_ctx.code_gen_max_blocks = tcg_ctx.code_gen_buffer_size / - CODE_GEN_AVG_BLOCK_SIZE; - tcg_ctx.tb_ctx.tbs = - g_malloc(tcg_ctx.code_gen_max_blocks * sizeof(TranslationBlock)); + qemu_mutex_init(&tcg_ctx.tb_ctx.tb_lock); } /* Must be called before using the QEMU cpus. 'tb_size' is the size @@ -683,11 +741,9 @@ static inline void code_gen_alloc(size_t tb_size) void tcg_exec_init(unsigned long tb_size) { cpu_gen_init(); - code_gen_alloc(tb_size); - tcg_ctx.code_gen_ptr = tcg_ctx.code_gen_buffer; - tcg_register_jit(tcg_ctx.code_gen_buffer, tcg_ctx.code_gen_buffer_size); page_init(); -#if !defined(CONFIG_USER_ONLY) || !defined(CONFIG_USE_GUEST_BASE) + code_gen_alloc(tb_size); +#if defined(CONFIG_SOFTMMU) /* There's no guest base to take into account, so go ahead and initialize the prologue now. */ tcg_prologue_init(&tcg_ctx); @@ -705,9 +761,7 @@ static TranslationBlock *tb_alloc(target_ulong pc) { TranslationBlock *tb; - if (tcg_ctx.tb_ctx.nb_tbs >= tcg_ctx.code_gen_max_blocks || - (tcg_ctx.code_gen_ptr - tcg_ctx.code_gen_buffer) >= - tcg_ctx.code_gen_buffer_max_size) { + if (tcg_ctx.tb_ctx.nb_tbs >= tcg_ctx.code_gen_max_blocks) { return NULL; } tb = &tcg_ctx.tb_ctx.tbs[tcg_ctx.tb_ctx.nb_tbs++]; @@ -730,10 +784,8 @@ void tb_free(TranslationBlock *tb) static inline void invalidate_page_bitmap(PageDesc *p) { - if (p->code_bitmap) { - g_free(p->code_bitmap); - p->code_bitmap = NULL; - } + g_free(p->code_bitmap); + p->code_bitmap = NULL; p->code_write_count = 0; } @@ -809,7 +861,8 @@ static void tb_invalidate_check(target_ulong address) address &= TARGET_PAGE_MASK; for (i = 0; i < CODE_GEN_PHYS_HASH_SIZE; i++) { - for (tb = tb_ctx.tb_phys_hash[i]; tb != NULL; tb = tb->phys_hash_next) { + for (tb = tcg_ctx.tb_ctx.tb_phys_hash[i]; tb != NULL; + tb = tb->phys_hash_next) { if (!(address + TARGET_PAGE_SIZE <= tb->pc || address >= tb->pc + tb->size)) { printf("ERROR invalidate: address=" TARGET_FMT_lx @@ -995,6 +1048,7 @@ static void build_page_bitmap(PageDesc *p) } } +/* Called with mmap_lock held for user mode emulation. */ TranslationBlock *tb_gen_code(CPUState *cpu, target_ulong pc, target_ulong cs_base, int flags, int cflags) @@ -1003,28 +1057,99 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TranslationBlock *tb; tb_page_addr_t phys_pc, phys_page2; target_ulong virt_page2; - int code_gen_size; + tcg_insn_unit *gen_code_buf; + int gen_code_size, search_size; +#ifdef CONFIG_PROFILER + int64_t ti; +#endif phys_pc = get_page_addr_code(env, pc); - if (use_icount) { + if (use_icount && !(cflags & CF_IGNORE_ICOUNT)) { cflags |= CF_USE_ICOUNT; } + tb = tb_alloc(pc); - if (!tb) { + if (unlikely(!tb)) { + buffer_overflow: /* flush must be done */ tb_flush(cpu); /* cannot fail at this point */ tb = tb_alloc(pc); + assert(tb != NULL); /* Don't forget to invalidate previous TB info. */ tcg_ctx.tb_ctx.tb_invalidated_flag = 1; } - tb->tc_ptr = tcg_ctx.code_gen_ptr; + + gen_code_buf = tcg_ctx.code_gen_ptr; + tb->tc_ptr = gen_code_buf; tb->cs_base = cs_base; tb->flags = flags; tb->cflags = cflags; - cpu_gen_code(env, tb, &code_gen_size); - tcg_ctx.code_gen_ptr = (void *)(((uintptr_t)tcg_ctx.code_gen_ptr + - code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1)); + +#ifdef CONFIG_PROFILER + tcg_ctx.tb_count1++; /* includes aborted translations because of + exceptions */ + ti = profile_getclock(); +#endif + + tcg_func_start(&tcg_ctx); + + gen_intermediate_code(env, tb); + + trace_translate_block(tb, tb->pc, tb->tc_ptr); + + /* generate machine code */ + tb->tb_next_offset[0] = 0xffff; + tb->tb_next_offset[1] = 0xffff; + tcg_ctx.tb_next_offset = tb->tb_next_offset; +#ifdef USE_DIRECT_JUMP + tcg_ctx.tb_jmp_offset = tb->tb_jmp_offset; + tcg_ctx.tb_next = NULL; +#else + tcg_ctx.tb_jmp_offset = NULL; + tcg_ctx.tb_next = tb->tb_next; +#endif + +#ifdef CONFIG_PROFILER + tcg_ctx.tb_count++; + tcg_ctx.interm_time += profile_getclock() - ti; + tcg_ctx.code_time -= profile_getclock(); +#endif + + /* ??? Overflow could be handled better here. In particular, we + don't need to re-do gen_intermediate_code, nor should we re-do + the tcg optimization currently hidden inside tcg_gen_code. All + that should be required is to flush the TBs, allocate a new TB, + re-initialize it per above, and re-do the actual code generation. */ + gen_code_size = tcg_gen_code(&tcg_ctx, tb); + if (unlikely(gen_code_size < 0)) { + goto buffer_overflow; + } + search_size = encode_search(tb, (void *)gen_code_buf + gen_code_size); + if (unlikely(search_size < 0)) { + goto buffer_overflow; + } + +#ifdef CONFIG_PROFILER + tcg_ctx.code_time += profile_getclock(); + tcg_ctx.code_in_len += tb->size; + tcg_ctx.code_out_len += gen_code_size; + tcg_ctx.search_out_len += search_size; +#endif + +#ifdef DEBUG_DISAS + if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM) && + qemu_log_in_addr_range(tb->pc)) { + qemu_log("OUT: [size=%d]\n", gen_code_size); + log_disas(tb->tc_ptr, gen_code_size); + qemu_log("\n"); + qemu_log_flush(); + } +#endif + + tcg_ctx.code_gen_ptr = (void *) + ROUND_UP((uintptr_t)gen_code_buf + gen_code_size + search_size, + CODE_GEN_ALIGN); /* check next page if needed */ virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK; @@ -1042,6 +1167,8 @@ TranslationBlock *tb_gen_code(CPUState *cpu, * 'is_cpu_write_access' should be true if called from a real cpu write * access: the virtual CPU will exit the current TB if code is modified inside * this TB. + * + * Called with mmap_lock held for user-mode emulation */ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end) { @@ -1058,6 +1185,8 @@ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end) * 'is_cpu_write_access' should be true if called from a real cpu write * access: the virtual CPU will exit the current TB if code is modified inside * this TB. + * + * Called with mmap_lock held for user-mode emulation */ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, int is_cpu_write_access) @@ -1206,6 +1335,7 @@ void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len) } #if !defined(CONFIG_SOFTMMU) +/* Called with mmap_lock held. */ static void tb_invalidate_phys_page(tb_page_addr_t addr, uintptr_t pc, void *puc, bool locked) @@ -1275,7 +1405,10 @@ static void tb_invalidate_phys_page(tb_page_addr_t addr, } #endif -/* add the tb in the target page and protect it if necessary */ +/* add the tb in the target page and protect it if necessary + * + * Called with mmap_lock held for user-mode emulation. + */ static inline void tb_alloc_page(TranslationBlock *tb, unsigned int n, tb_page_addr_t page_addr) { @@ -1331,16 +1464,16 @@ static inline void tb_alloc_page(TranslationBlock *tb, } /* add a new TB and link it to the physical page tables. phys_page2 is - (-1) to indicate that only one page contains the TB. */ + * (-1) to indicate that only one page contains the TB. + * + * Called with mmap_lock held for user-mode emulation. + */ static void tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, tb_page_addr_t phys_page2) { unsigned int h; TranslationBlock **ptb; - /* Grab the mmap lock to stop another thread invalidating this TB - before we are done. */ - mmap_lock(); /* add in the physical hash table */ h = tb_phys_hash_func(phys_pc); ptb = &tcg_ctx.tb_ctx.tb_phys_hash[h]; @@ -1370,7 +1503,6 @@ static void tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, #ifdef DEBUG_TB_CHECK tb_page_check(); #endif - mmap_unlock(); } /* find the TB 'tb' such that tb[0].tc_ptr <= tc_ptr < @@ -1451,36 +1583,6 @@ void tb_check_watchpoint(CPUState *cpu) } #ifndef CONFIG_USER_ONLY -/* mask must never be zero, except for A20 change call */ -static void tcg_handle_interrupt(CPUState *cpu, int mask) -{ - int old_mask; - - old_mask = cpu->interrupt_request; - cpu->interrupt_request |= mask; - - /* - * If called from iothread context, wake the target cpu in - * case its halted. - */ - if (!qemu_cpu_is_self(cpu)) { - qemu_cpu_kick(cpu); - return; - } - - if (use_icount) { - cpu->icount_decr.u16.high = 0xffff; - if (!cpu_can_do_io(cpu) - && (mask & ~old_mask) != 0) { - cpu_abort(cpu, "Raised interrupt while not in I/O function"); - } - } else { - cpu->tcg_exit_req = 1; - } -} - -CPUInterruptHandler cpu_interrupt_handler = tcg_handle_interrupt; - /* in deterministic execution mode, instructions doing device I/Os must be at the end of the TB */ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) @@ -1533,6 +1635,14 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) cs_base = tb->cs_base; flags = tb->flags; tb_phys_invalidate(tb, -1); + if (tb->cflags & CF_NOCACHE) { + if (tb->orig_tb) { + /* Invalidate original TB if this TB was generated in + * cpu_exec_nocache() */ + tb_phys_invalidate(tb->orig_tb, -1); + } + tb_free(tb); + } /* 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, cflags); @@ -1590,7 +1700,7 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf) cpu_fprintf(f, "Translation buffer state:\n"); cpu_fprintf(f, "gen code size %td/%zd\n", tcg_ctx.code_gen_ptr - tcg_ctx.code_gen_buffer, - tcg_ctx.code_gen_buffer_max_size); + tcg_ctx.code_gen_highwater - tcg_ctx.code_gen_buffer); cpu_fprintf(f, "TB count %d/%d\n", tcg_ctx.tb_ctx.nb_tbs, tcg_ctx.code_gen_max_blocks); cpu_fprintf(f, "TB avg target size %d max=%d bytes\n", diff --git a/qemu/translate-common.c b/qemu/translate-common.c new file mode 100644 index 000000000..ffbfe856c --- /dev/null +++ b/qemu/translate-common.c @@ -0,0 +1,57 @@ +/* + * Host code generation common components + * + * Copyright (c) 2015 Peter Crosthwaite + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qom/cpu.h" + +uintptr_t qemu_real_host_page_size; +intptr_t qemu_real_host_page_mask; + +#ifndef CONFIG_USER_ONLY +/* mask must never be zero, except for A20 change call */ +static void tcg_handle_interrupt(CPUState *cpu, int mask) +{ + int old_mask; + + old_mask = cpu->interrupt_request; + cpu->interrupt_request |= mask; + + /* + * If called from iothread context, wake the target cpu in + * case its halted. + */ + if (!qemu_cpu_is_self(cpu)) { + qemu_cpu_kick(cpu); + return; + } + + if (use_icount) { + cpu->icount_decr.u16.high = 0xffff; + if (!cpu->can_do_io + && (mask & ~old_mask) != 0) { + cpu_abort(cpu, "Raised interrupt while not in I/O function"); + } + } else { + cpu->tcg_exit_req = 1; + } +} + +CPUInterruptHandler cpu_interrupt_handler = tcg_handle_interrupt; +#endif diff --git a/qemu/ui/Makefile.objs b/qemu/ui/Makefile.objs index c62d4d972..dc936f150 100644 --- a/qemu/ui/Makefile.objs +++ b/qemu/ui/Makefile.objs @@ -2,13 +2,14 @@ vnc-obj-y += vnc.o vnc-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o vnc-obj-y += vnc-enc-tight.o vnc-palette.o vnc-obj-y += vnc-enc-zrle.o -vnc-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o +vnc-obj-y += vnc-auth-vencrypt.o vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o vnc-obj-y += vnc-ws.o vnc-obj-y += vnc-jobs.o common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o common-obj-y += input.o input-keymap.o input-legacy.o +common-obj-$(CONFIG_LINUX) += input-linux.o common-obj-$(CONFIG_SPICE) += spice-core.o spice-input.o spice-display.o common-obj-$(CONFIG_SDL) += sdl.mo x_keymap.o common-obj-$(CONFIG_COCOA) += cocoa.o @@ -31,11 +32,17 @@ ifeq ($(CONFIG_OPENGL),y) common-obj-y += shader.o common-obj-y += console-gl.o common-obj-y += egl-helpers.o +common-obj-y += egl-context.o +ifeq ($(CONFIG_GTK_GL),y) +common-obj-$(CONFIG_GTK) += gtk-gl-area.o +else common-obj-$(CONFIG_GTK) += gtk-egl.o endif +endif gtk.o-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS) gtk-egl.o-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS) $(OPENGL_CFLAGS) +gtk-gl-area.o-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS) $(OPENGL_CFLAGS) shader.o-cflags += $(OPENGL_CFLAGS) console-gl.o-cflags += $(OPENGL_CFLAGS) egl-helpers.o-cflags += $(OPENGL_CFLAGS) diff --git a/qemu/ui/cocoa.m b/qemu/ui/cocoa.m index 334e6f666..60a7c07ec 100644 --- a/qemu/ui/cocoa.m +++ b/qemu/ui/cocoa.m @@ -22,6 +22,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" + #import #include @@ -31,6 +33,7 @@ #include "sysemu/sysemu.h" #include "qmp-commands.h" #include "sysemu/blockdev.h" +#include #ifndef MAC_OS_X_VERSION_10_5 #define MAC_OS_X_VERSION_10_5 1050 @@ -70,178 +73,139 @@ bool stretch_video; NSTextField *pauseLabel; NSArray * supportedImageFileTypes; -// keymap conversion -int keymap[] = -{ -// SdlI macI macH SdlH 104xtH 104xtC sdl - 30, // 0 0x00 0x1e A QZ_a - 31, // 1 0x01 0x1f S QZ_s - 32, // 2 0x02 0x20 D QZ_d - 33, // 3 0x03 0x21 F QZ_f - 35, // 4 0x04 0x23 H QZ_h - 34, // 5 0x05 0x22 G QZ_g - 44, // 6 0x06 0x2c Z QZ_z - 45, // 7 0x07 0x2d X QZ_x - 46, // 8 0x08 0x2e C QZ_c - 47, // 9 0x09 0x2f V QZ_v - 0, // 10 0x0A Undefined - 48, // 11 0x0B 0x30 B QZ_b - 16, // 12 0x0C 0x10 Q QZ_q - 17, // 13 0x0D 0x11 W QZ_w - 18, // 14 0x0E 0x12 E QZ_e - 19, // 15 0x0F 0x13 R QZ_r - 21, // 16 0x10 0x15 Y QZ_y - 20, // 17 0x11 0x14 T QZ_t - 2, // 18 0x12 0x02 1 QZ_1 - 3, // 19 0x13 0x03 2 QZ_2 - 4, // 20 0x14 0x04 3 QZ_3 - 5, // 21 0x15 0x05 4 QZ_4 - 7, // 22 0x16 0x07 6 QZ_6 - 6, // 23 0x17 0x06 5 QZ_5 - 13, // 24 0x18 0x0d = QZ_EQUALS - 10, // 25 0x19 0x0a 9 QZ_9 - 8, // 26 0x1A 0x08 7 QZ_7 - 12, // 27 0x1B 0x0c - QZ_MINUS - 9, // 28 0x1C 0x09 8 QZ_8 - 11, // 29 0x1D 0x0b 0 QZ_0 - 27, // 30 0x1E 0x1b ] QZ_RIGHTBRACKET - 24, // 31 0x1F 0x18 O QZ_o - 22, // 32 0x20 0x16 U QZ_u - 26, // 33 0x21 0x1a [ QZ_LEFTBRACKET - 23, // 34 0x22 0x17 I QZ_i - 25, // 35 0x23 0x19 P QZ_p - 28, // 36 0x24 0x1c ENTER QZ_RETURN - 38, // 37 0x25 0x26 L QZ_l - 36, // 38 0x26 0x24 J QZ_j - 40, // 39 0x27 0x28 ' QZ_QUOTE - 37, // 40 0x28 0x25 K QZ_k - 39, // 41 0x29 0x27 ; QZ_SEMICOLON - 43, // 42 0x2A 0x2b \ QZ_BACKSLASH - 51, // 43 0x2B 0x33 , QZ_COMMA - 53, // 44 0x2C 0x35 / QZ_SLASH - 49, // 45 0x2D 0x31 N QZ_n - 50, // 46 0x2E 0x32 M QZ_m - 52, // 47 0x2F 0x34 . QZ_PERIOD - 15, // 48 0x30 0x0f TAB QZ_TAB - 57, // 49 0x31 0x39 SPACE QZ_SPACE - 41, // 50 0x32 0x29 ` QZ_BACKQUOTE - 14, // 51 0x33 0x0e BKSP QZ_BACKSPACE - 0, // 52 0x34 Undefined - 1, // 53 0x35 0x01 ESC QZ_ESCAPE - 220, // 54 0x36 0xdc E0,5C R GUI QZ_RMETA - 219, // 55 0x37 0xdb E0,5B L GUI QZ_LMETA - 42, // 56 0x38 0x2a L SHFT QZ_LSHIFT - 58, // 57 0x39 0x3a CAPS QZ_CAPSLOCK - 56, // 58 0x3A 0x38 L ALT QZ_LALT - 29, // 59 0x3B 0x1d L CTRL QZ_LCTRL - 54, // 60 0x3C 0x36 R SHFT QZ_RSHIFT - 184,// 61 0x3D 0xb8 E0,38 R ALT QZ_RALT - 157,// 62 0x3E 0x9d E0,1D R CTRL QZ_RCTRL - 0, // 63 0x3F Undefined - 0, // 64 0x40 Undefined - 0, // 65 0x41 Undefined - 0, // 66 0x42 Undefined - 55, // 67 0x43 0x37 KP * QZ_KP_MULTIPLY - 0, // 68 0x44 Undefined - 78, // 69 0x45 0x4e KP + QZ_KP_PLUS - 0, // 70 0x46 Undefined - 69, // 71 0x47 0x45 NUM QZ_NUMLOCK - 0, // 72 0x48 Undefined - 0, // 73 0x49 Undefined - 0, // 74 0x4A Undefined - 181,// 75 0x4B 0xb5 E0,35 KP / QZ_KP_DIVIDE - 152,// 76 0x4C 0x9c E0,1C KP EN QZ_KP_ENTER - 0, // 77 0x4D undefined - 74, // 78 0x4E 0x4a KP - QZ_KP_MINUS - 0, // 79 0x4F Undefined - 0, // 80 0x50 Undefined - 0, // 81 0x51 QZ_KP_EQUALS - 82, // 82 0x52 0x52 KP 0 QZ_KP0 - 79, // 83 0x53 0x4f KP 1 QZ_KP1 - 80, // 84 0x54 0x50 KP 2 QZ_KP2 - 81, // 85 0x55 0x51 KP 3 QZ_KP3 - 75, // 86 0x56 0x4b KP 4 QZ_KP4 - 76, // 87 0x57 0x4c KP 5 QZ_KP5 - 77, // 88 0x58 0x4d KP 6 QZ_KP6 - 71, // 89 0x59 0x47 KP 7 QZ_KP7 - 0, // 90 0x5A Undefined - 72, // 91 0x5B 0x48 KP 8 QZ_KP8 - 73, // 92 0x5C 0x49 KP 9 QZ_KP9 - 0, // 93 0x5D Undefined - 0, // 94 0x5E Undefined - 0, // 95 0x5F Undefined - 63, // 96 0x60 0x3f F5 QZ_F5 - 64, // 97 0x61 0x40 F6 QZ_F6 - 65, // 98 0x62 0x41 F7 QZ_F7 - 61, // 99 0x63 0x3d F3 QZ_F3 - 66, // 100 0x64 0x42 F8 QZ_F8 - 67, // 101 0x65 0x43 F9 QZ_F9 - 0, // 102 0x66 Undefined - 87, // 103 0x67 0x57 F11 QZ_F11 - 0, // 104 0x68 Undefined - 183,// 105 0x69 0xb7 QZ_PRINT - 0, // 106 0x6A Undefined - 70, // 107 0x6B 0x46 SCROLL QZ_SCROLLOCK - 0, // 108 0x6C Undefined - 68, // 109 0x6D 0x44 F10 QZ_F10 - 0, // 110 0x6E Undefined - 88, // 111 0x6F 0x58 F12 QZ_F12 - 0, // 112 0x70 Undefined - 110,// 113 0x71 0x0 QZ_PAUSE - 210,// 114 0x72 0xd2 E0,52 INSERT QZ_INSERT - 199,// 115 0x73 0xc7 E0,47 HOME QZ_HOME - 201,// 116 0x74 0xc9 E0,49 PG UP QZ_PAGEUP - 211,// 117 0x75 0xd3 E0,53 DELETE QZ_DELETE - 62, // 118 0x76 0x3e F4 QZ_F4 - 207,// 119 0x77 0xcf E0,4f END QZ_END - 60, // 120 0x78 0x3c F2 QZ_F2 - 209,// 121 0x79 0xd1 E0,51 PG DN QZ_PAGEDOWN - 59, // 122 0x7A 0x3b F1 QZ_F1 - 203,// 123 0x7B 0xcb e0,4B L ARROW QZ_LEFT - 205,// 124 0x7C 0xcd e0,4D R ARROW QZ_RIGHT - 208,// 125 0x7D 0xd0 E0,50 D ARROW QZ_DOWN - 200,// 126 0x7E 0xc8 E0,48 U ARROW QZ_UP -/* completed according to http://www.libsdl.org/cgi/cvsweb.cgi/SDL12/src/video/quartz/SDL_QuartzKeys.h?rev=1.6&content-type=text/x-cvsweb-markup */ - -/* Additional 104 Key XP-Keyboard Scancodes from http://www.computer-engineering.org/ps2keyboard/scancodes1.html */ -/* - 221 // 0xdd e0,5d APPS - // E0,2A,E0,37 PRNT SCRN - // E1,1D,45,E1,9D,C5 PAUSE - 83 // 0x53 0x53 KP . -// ACPI Scan Codes - 222 // 0xde E0, 5E Power - 223 // 0xdf E0, 5F Sleep - 227 // 0xe3 E0, 63 Wake -// Windows Multimedia Scan Codes - 153 // 0x99 E0, 19 Next Track - 144 // 0x90 E0, 10 Previous Track - 164 // 0xa4 E0, 24 Stop - 162 // 0xa2 E0, 22 Play/Pause - 160 // 0xa0 E0, 20 Mute - 176 // 0xb0 E0, 30 Volume Up - 174 // 0xae E0, 2E Volume Down - 237 // 0xed E0, 6D Media Select - 236 // 0xec E0, 6C E-Mail - 161 // 0xa1 E0, 21 Calculator - 235 // 0xeb E0, 6B My Computer - 229 // 0xe5 E0, 65 WWW Search - 178 // 0xb2 E0, 32 WWW Home - 234 // 0xea E0, 6A WWW Back - 233 // 0xe9 E0, 69 WWW Forward - 232 // 0xe8 E0, 68 WWW Stop - 231 // 0xe7 E0, 67 WWW Refresh - 230 // 0xe6 E0, 66 WWW Favorites -*/ +// Mac to QKeyCode conversion +const int mac_to_qkeycode_map[] = { + [kVK_ANSI_A] = Q_KEY_CODE_A, + [kVK_ANSI_B] = Q_KEY_CODE_B, + [kVK_ANSI_C] = Q_KEY_CODE_C, + [kVK_ANSI_D] = Q_KEY_CODE_D, + [kVK_ANSI_E] = Q_KEY_CODE_E, + [kVK_ANSI_F] = Q_KEY_CODE_F, + [kVK_ANSI_G] = Q_KEY_CODE_G, + [kVK_ANSI_H] = Q_KEY_CODE_H, + [kVK_ANSI_I] = Q_KEY_CODE_I, + [kVK_ANSI_J] = Q_KEY_CODE_J, + [kVK_ANSI_K] = Q_KEY_CODE_K, + [kVK_ANSI_L] = Q_KEY_CODE_L, + [kVK_ANSI_M] = Q_KEY_CODE_M, + [kVK_ANSI_N] = Q_KEY_CODE_N, + [kVK_ANSI_O] = Q_KEY_CODE_O, + [kVK_ANSI_P] = Q_KEY_CODE_P, + [kVK_ANSI_Q] = Q_KEY_CODE_Q, + [kVK_ANSI_R] = Q_KEY_CODE_R, + [kVK_ANSI_S] = Q_KEY_CODE_S, + [kVK_ANSI_T] = Q_KEY_CODE_T, + [kVK_ANSI_U] = Q_KEY_CODE_U, + [kVK_ANSI_V] = Q_KEY_CODE_V, + [kVK_ANSI_W] = Q_KEY_CODE_W, + [kVK_ANSI_X] = Q_KEY_CODE_X, + [kVK_ANSI_Y] = Q_KEY_CODE_Y, + [kVK_ANSI_Z] = Q_KEY_CODE_Z, + + [kVK_ANSI_0] = Q_KEY_CODE_0, + [kVK_ANSI_1] = Q_KEY_CODE_1, + [kVK_ANSI_2] = Q_KEY_CODE_2, + [kVK_ANSI_3] = Q_KEY_CODE_3, + [kVK_ANSI_4] = Q_KEY_CODE_4, + [kVK_ANSI_5] = Q_KEY_CODE_5, + [kVK_ANSI_6] = Q_KEY_CODE_6, + [kVK_ANSI_7] = Q_KEY_CODE_7, + [kVK_ANSI_8] = Q_KEY_CODE_8, + [kVK_ANSI_9] = Q_KEY_CODE_9, + + [kVK_ANSI_Grave] = Q_KEY_CODE_GRAVE_ACCENT, + [kVK_ANSI_Minus] = Q_KEY_CODE_MINUS, + [kVK_ANSI_Equal] = Q_KEY_CODE_EQUAL, + [kVK_Delete] = Q_KEY_CODE_BACKSPACE, + [kVK_CapsLock] = Q_KEY_CODE_CAPS_LOCK, + [kVK_Tab] = Q_KEY_CODE_TAB, + [kVK_Return] = Q_KEY_CODE_RET, + [kVK_ANSI_LeftBracket] = Q_KEY_CODE_BRACKET_LEFT, + [kVK_ANSI_RightBracket] = Q_KEY_CODE_BRACKET_RIGHT, + [kVK_ANSI_Backslash] = Q_KEY_CODE_BACKSLASH, + [kVK_ANSI_Semicolon] = Q_KEY_CODE_SEMICOLON, + [kVK_ANSI_Quote] = Q_KEY_CODE_APOSTROPHE, + [kVK_ANSI_Comma] = Q_KEY_CODE_COMMA, + [kVK_ANSI_Period] = Q_KEY_CODE_DOT, + [kVK_ANSI_Slash] = Q_KEY_CODE_SLASH, + [kVK_Shift] = Q_KEY_CODE_SHIFT, + [kVK_RightShift] = Q_KEY_CODE_SHIFT_R, + [kVK_Control] = Q_KEY_CODE_CTRL, + [kVK_RightControl] = Q_KEY_CODE_CTRL_R, + [kVK_Option] = Q_KEY_CODE_ALT, + [kVK_RightOption] = Q_KEY_CODE_ALT_R, + [kVK_Command] = Q_KEY_CODE_META_L, + [0x36] = Q_KEY_CODE_META_R, /* There is no kVK_RightCommand */ + [kVK_Space] = Q_KEY_CODE_SPC, + + [kVK_ANSI_Keypad0] = Q_KEY_CODE_KP_0, + [kVK_ANSI_Keypad1] = Q_KEY_CODE_KP_1, + [kVK_ANSI_Keypad2] = Q_KEY_CODE_KP_2, + [kVK_ANSI_Keypad3] = Q_KEY_CODE_KP_3, + [kVK_ANSI_Keypad4] = Q_KEY_CODE_KP_4, + [kVK_ANSI_Keypad5] = Q_KEY_CODE_KP_5, + [kVK_ANSI_Keypad6] = Q_KEY_CODE_KP_6, + [kVK_ANSI_Keypad7] = Q_KEY_CODE_KP_7, + [kVK_ANSI_Keypad8] = Q_KEY_CODE_KP_8, + [kVK_ANSI_Keypad9] = Q_KEY_CODE_KP_9, + [kVK_ANSI_KeypadDecimal] = Q_KEY_CODE_KP_DECIMAL, + [kVK_ANSI_KeypadEnter] = Q_KEY_CODE_KP_ENTER, + [kVK_ANSI_KeypadPlus] = Q_KEY_CODE_KP_ADD, + [kVK_ANSI_KeypadMinus] = Q_KEY_CODE_KP_SUBTRACT, + [kVK_ANSI_KeypadMultiply] = Q_KEY_CODE_KP_MULTIPLY, + [kVK_ANSI_KeypadDivide] = Q_KEY_CODE_KP_DIVIDE, + [kVK_ANSI_KeypadEquals] = Q_KEY_CODE_KP_EQUALS, + [kVK_ANSI_KeypadClear] = Q_KEY_CODE_NUM_LOCK, + + [kVK_UpArrow] = Q_KEY_CODE_UP, + [kVK_DownArrow] = Q_KEY_CODE_DOWN, + [kVK_LeftArrow] = Q_KEY_CODE_LEFT, + [kVK_RightArrow] = Q_KEY_CODE_RIGHT, + + [kVK_Help] = Q_KEY_CODE_INSERT, + [kVK_Home] = Q_KEY_CODE_HOME, + [kVK_PageUp] = Q_KEY_CODE_PGUP, + [kVK_PageDown] = Q_KEY_CODE_PGDN, + [kVK_End] = Q_KEY_CODE_END, + [kVK_ForwardDelete] = Q_KEY_CODE_DELETE, + + [kVK_Escape] = Q_KEY_CODE_ESC, + + /* The Power key can't be used directly because the operating system uses + * it. This key can be emulated by using it in place of another key such as + * F1. Don't forget to disable the real key binding. + */ + /* [kVK_F1] = Q_KEY_CODE_POWER, */ + + [kVK_F1] = Q_KEY_CODE_F1, + [kVK_F2] = Q_KEY_CODE_F2, + [kVK_F3] = Q_KEY_CODE_F3, + [kVK_F4] = Q_KEY_CODE_F4, + [kVK_F5] = Q_KEY_CODE_F5, + [kVK_F6] = Q_KEY_CODE_F6, + [kVK_F7] = Q_KEY_CODE_F7, + [kVK_F8] = Q_KEY_CODE_F8, + [kVK_F9] = Q_KEY_CODE_F9, + [kVK_F10] = Q_KEY_CODE_F10, + [kVK_F11] = Q_KEY_CODE_F11, + [kVK_F12] = Q_KEY_CODE_F12, + [kVK_F13] = Q_KEY_CODE_PRINT, + [kVK_F14] = Q_KEY_CODE_SCROLL_LOCK, + [kVK_F15] = Q_KEY_CODE_PAUSE, + + /* + * The eject and volume keys can't be used here because they are handled at + * a lower level than what an Application can see. + */ }; static int cocoa_keycode_to_qemu(int keycode) { - if (ARRAY_SIZE(keymap) <= keycode) { + if (ARRAY_SIZE(mac_to_qkeycode_map) <= keycode) { fprintf(stderr, "(cocoa) warning unknown keycode 0x%x\n", keycode); return 0; } - return keymap[keycode]; + return mac_to_qkeycode_map[keycode]; } /* Displays an alert dialog box with the specified message */ @@ -304,6 +268,7 @@ static void handleAnyDeviceErrors(Error * err) - (float) cdx; - (float) cdy; - (QEMUScreen) gscreen; +- (void) raiseAllKeys; @end QemuCocoaView *cocoaView; @@ -554,21 +519,24 @@ QemuCocoaView *cocoaView; case NSFlagsChanged: keycode = cocoa_keycode_to_qemu([event keyCode]); - if ((keycode == 219 || keycode == 220) && !isMouseGrabbed) { + if ((keycode == Q_KEY_CODE_META_L || keycode == Q_KEY_CODE_META_R) + && !isMouseGrabbed) { /* Don't pass command key changes to guest unless mouse is grabbed */ keycode = 0; } if (keycode) { - if (keycode == 58 || keycode == 69) { // emulate caps lock and num lock keydown and keyup - qemu_input_event_send_key_number(dcl->con, keycode, true); - qemu_input_event_send_key_number(dcl->con, keycode, false); + // emulate caps lock and num lock keydown and keyup + if (keycode == Q_KEY_CODE_CAPS_LOCK || + keycode == Q_KEY_CODE_NUM_LOCK) { + qemu_input_event_send_key_qcode(dcl->con, keycode, true); + qemu_input_event_send_key_qcode(dcl->con, keycode, false); } else if (qemu_console_is_graphic(NULL)) { if (modifiers_state[keycode] == 0) { // keydown - qemu_input_event_send_key_number(dcl->con, keycode, true); + qemu_input_event_send_key_qcode(dcl->con, keycode, true); modifiers_state[keycode] = 1; } else { // keyup - qemu_input_event_send_key_number(dcl->con, keycode, false); + qemu_input_event_send_key_qcode(dcl->con, keycode, false); modifiers_state[keycode] = 0; } } @@ -595,14 +563,14 @@ QemuCocoaView *cocoaView; switch (keycode) { // enable graphic console - case 0x02 ... 0x0a: // '1' to '9' keys - console_select(keycode - 0x02); + case Q_KEY_CODE_1 ... Q_KEY_CODE_9: // '1' to '9' keys + console_select(keycode - 11); break; } // handle keys for graphic console } else if (qemu_console_is_graphic(NULL)) { - qemu_input_event_send_key_number(dcl->con, keycode, true); + qemu_input_event_send_key_qcode(dcl->con, keycode, true); // handlekeys for Monitor } else { @@ -650,7 +618,7 @@ QemuCocoaView *cocoaView; } if (qemu_console_is_graphic(NULL)) { - qemu_input_event_send_key_number(dcl->con, keycode, false); + qemu_input_event_send_key_qcode(dcl->con, keycode, false); } break; case NSMouseMoved: @@ -723,8 +691,16 @@ QemuCocoaView *cocoaView; } if (mouse_event) { - if (last_buttons != buttons) { - static uint32_t bmap[INPUT_BUTTON_MAX] = { + /* Don't send button events to the guest unless we've got a + * mouse grab or window focus. If we have neither then this event + * is the user clicking on the background window to activate and + * bring us to the front, which will be done by the sendEvent + * call below. We definitely don't want to pass that click through + * to the guest. + */ + if ((isMouseGrabbed || [[self window] isKeyWindow]) && + (last_buttons != buttons)) { + static uint32_t bmap[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON, [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON, [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON, @@ -798,6 +774,24 @@ QemuCocoaView *cocoaView; - (float) cdx {return cdx;} - (float) cdy {return cdy;} - (QEMUScreen) gscreen {return screen;} + +/* + * Makes the target think all down keys are being released. + * This prevents a stuck key problem, since we will not see + * key up events for those keys after we have lost focus. + */ +- (void) raiseAllKeys +{ + int index; + const int max_index = ARRAY_SIZE(modifiers_state); + + for (index = 0; index < max_index; index++) { + if (modifiers_state[index]) { + modifiers_state[index] = 0; + qemu_input_event_send_key_qcode(dcl->con, index, false); + } + } +} @end @@ -809,12 +803,11 @@ QemuCocoaView *cocoaView; */ @interface QemuCocoaAppController : NSObject #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) - + #endif { } - (void)startEmulationWithArgc:(int)argc argv:(char**)argv; -- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo; - (void)doToggleFullScreen:(id)sender; - (void)toggleFullScreen:(id)sender; - (void)showQEMUDoc:(id)sender; @@ -829,6 +822,8 @@ QemuCocoaView *cocoaView; - (void)powerDownQEMU:(id)sender; - (void)ejectDeviceMedia:(id)sender; - (void)changeDeviceMedia:(id)sender; +- (BOOL)verifyQuit; +- (void)openDocumentation:(NSString *)filename; @end @implementation QemuCocoaAppController @@ -855,13 +850,14 @@ QemuCocoaView *cocoaView; exit(1); } [normalWindow setAcceptsMouseMovedEvents:YES]; - [normalWindow setTitle:[NSString stringWithFormat:@"QEMU"]]; + [normalWindow setTitle:@"QEMU"]; [normalWindow setContentView:cocoaView]; #if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10) [normalWindow useOptimizedDrawing:YES]; #endif [normalWindow makeKeyAndOrderFront:self]; [normalWindow center]; + [normalWindow setDelegate: self]; stretch_video = false; /* Used for displaying pause on the screen */ @@ -878,7 +874,8 @@ QemuCocoaView *cocoaView; // set the supported image file types that can be opened supportedImageFileTypes = [NSArray arrayWithObjects: @"img", @"iso", @"dmg", - @"qcow", @"qcow2", @"cloop", @"vmdk", nil]; + @"qcow", @"qcow2", @"cloop", @"vmdk", @"cdr", + nil]; } return self; } @@ -895,29 +892,8 @@ QemuCocoaView *cocoaView; - (void)applicationDidFinishLaunching: (NSNotification *) note { COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n"); - - // Display an open dialog box if no arguments were passed or - // if qemu was launched from the finder ( the Finder passes "-psn" ) - if( gArgc <= 1 || strncmp ((char *)gArgv[1], "-psn", 4) == 0) { - NSOpenPanel *op = [[NSOpenPanel alloc] init]; - [op setPrompt:@"Boot image"]; - [op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"]; -#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) - [op setAllowedFileTypes:supportedImageFileTypes]; - [op beginSheetModalForWindow:normalWindow - completionHandler:^(NSInteger returnCode) - { [self openPanelDidEnd:op - returnCode:returnCode contextInfo:NULL ]; } ]; -#else - // Compatibility code for pre-10.6, using deprecated method - [op beginSheetForDirectory:nil file:nil types:filetypes - modalForWindow:normalWindow modalDelegate:self - didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL]; -#endif - } else { - // or launch QEMU, with the global args - [self startEmulationWithArgc:gArgc argv:(char **)gArgv]; - } + // launch QEMU, with the global args + [self startEmulationWithArgc:gArgc argv:(char **)gArgv]; } - (void)applicationWillTerminate:(NSNotification *)aNotification @@ -933,43 +909,40 @@ QemuCocoaView *cocoaView; return YES; } -- (void)startEmulationWithArgc:(int)argc argv:(char**)argv +- (NSApplicationTerminateReply)applicationShouldTerminate: + (NSApplication *)sender { - COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n"); - - int status; - status = qemu_main(argc, argv, *_NSGetEnviron()); - exit(status); + COCOA_DEBUG("QemuCocoaAppController: applicationShouldTerminate\n"); + return [self verifyQuit]; } -- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo +/* Called when the user clicks on a window's close button */ +- (BOOL)windowShouldClose:(id)sender { - COCOA_DEBUG("QemuCocoaAppController: openPanelDidEnd\n"); - - /* The NSFileHandlingPanelOKButton/NSFileHandlingPanelCancelButton values for - * returnCode strictly only apply for the 10.6-and-up beginSheetModalForWindow - * API. For the legacy pre-10.6 beginSheetForDirectory API they are NSOKButton - * and NSCancelButton. However conveniently the values are the same. - * We use the non-legacy names because the others are deprecated in OSX 10.10. + COCOA_DEBUG("QemuCocoaAppController: windowShouldClose\n"); + [NSApp terminate: sender]; + /* If the user allows the application to quit then the call to + * NSApp terminate will never return. If we get here then the user + * cancelled the quit, so we should return NO to not permit the + * closing of this window. */ - if (returnCode == NSFileHandlingPanelCancelButton) { - exit(0); - } else if (returnCode == NSFileHandlingPanelOKButton) { - char *img = (char*)[ [ [ sheet URL ] path ] cStringUsingEncoding:NSASCIIStringEncoding]; - - char **argv = g_new(char *, 4); - - [sheet close]; + return NO; +} - argv[0] = g_strdup(gArgv[0]); - argv[1] = g_strdup("-hda"); - argv[2] = g_strdup(img); - argv[3] = NULL; +/* Called when QEMU goes into the background */ +- (void) applicationWillResignActive: (NSNotification *)aNotification +{ + COCOA_DEBUG("QemuCocoaAppController: applicationWillResignActive\n"); + [cocoaView raiseAllKeys]; +} - // printf("Using argc %d argv %s -hda %s\n", 3, gArgv[0], img); +- (void)startEmulationWithArgc:(int)argc argv:(char**)argv +{ + COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n"); - [self startEmulationWithArgc:3 argv:(char**)argv]; - } + int status; + status = qemu_main(argc, argv, *_NSGetEnviron()); + exit(status); } /* We abstract the method called by the Enter Fullscreen menu item @@ -988,20 +961,42 @@ QemuCocoaView *cocoaView; [cocoaView toggleFullScreen:sender]; } +/* Tries to find then open the specified filename */ +- (void) openDocumentation: (NSString *) filename +{ + /* Where to look for local files */ + NSString *path_array[] = {@"../share/doc/qemu/", @"../doc/qemu/", @"../"}; + NSString *full_file_path; + + /* iterate thru the possible paths until the file is found */ + int index; + for (index = 0; index < ARRAY_SIZE(path_array); index++) { + full_file_path = [[NSBundle mainBundle] executablePath]; + full_file_path = [full_file_path stringByDeletingLastPathComponent]; + full_file_path = [NSString stringWithFormat: @"%@/%@%@", full_file_path, + path_array[index], filename]; + if ([[NSWorkspace sharedWorkspace] openFile: full_file_path] == YES) { + return; + } + } + + /* If none of the paths opened a file */ + NSBeep(); + QEMU_Alert(@"Failed to open file"); +} + - (void)showQEMUDoc:(id)sender { COCOA_DEBUG("QemuCocoaAppController: showQEMUDoc\n"); - [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-doc.html", - [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"]; + [self openDocumentation: @"qemu-doc.html"]; } - (void)showQEMUTec:(id)sender { COCOA_DEBUG("QemuCocoaAppController: showQEMUTec\n"); - [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-tech.html", - [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"]; + [self openDocumentation: @"qemu-tech.html"]; } /* Stretches video to fit host monitor size */ @@ -1117,14 +1112,32 @@ QemuCocoaView *cocoaView; } Error *err = NULL; - qmp_change_blockdev([drive cStringUsingEncoding: NSASCIIStringEncoding], - [file cStringUsingEncoding: NSASCIIStringEncoding], - "raw", - &err); + qmp_blockdev_change_medium([drive cStringUsingEncoding: + NSASCIIStringEncoding], + [file cStringUsingEncoding: + NSASCIIStringEncoding], + true, "raw", + false, 0, + &err); handleAnyDeviceErrors(err); } } +/* Verifies if the user really wants to quit */ +- (BOOL)verifyQuit +{ + NSAlert *alert = [NSAlert new]; + [alert autorelease]; + [alert setMessageText: @"Are you sure you want to quit QEMU?"]; + [alert addButtonWithTitle: @"Cancel"]; + [alert addButtonWithTitle: @"Quit"]; + if([alert runModal] == NSAlertSecondButtonReturn) { + return YES; + } else { + return NO; + } +} + @end @@ -1148,6 +1161,7 @@ int main (int argc, const char * argv[]) { !strcmp(opt, "-nographic") || !strcmp(opt, "-version") || !strcmp(opt, "-curses") || + !strcmp(opt, "-display") || !strcmp(opt, "-qtest")) { return qemu_main(gArgc, gArgv, *_NSGetEnviron()); } @@ -1275,6 +1289,7 @@ static void cocoa_refresh(DisplayChangeListener *dcl) NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n"); + graphic_hw_update(NULL); if (qemu_input_is_absolute()) { if (![cocoaView isAbsoluteEnabled]) { @@ -1295,7 +1310,6 @@ static void cocoa_refresh(DisplayChangeListener *dcl) [cocoaView handleEvent:event]; } } while(event != nil); - graphic_hw_update(NULL); [pool release]; } @@ -1341,7 +1355,7 @@ static void add_console_menu_entries(void) /* Make menu items for all removable devices. * Each device is given an 'Eject' and 'Change' menu item. */ -static void addRemovableDevicesMenuItems() +static void addRemovableDevicesMenuItems(void) { NSMenu *menu; NSMenuItem *menuItem; diff --git a/qemu/ui/console-gl.c b/qemu/ui/console-gl.c index cb45cf8a2..74b1bed6e 100644 --- a/qemu/ui/console-gl.c +++ b/qemu/ui/console-gl.c @@ -24,6 +24,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "ui/console.h" #include "ui/shader.h" @@ -33,6 +34,7 @@ struct ConsoleGLState { GLint texture_blit_prog; + GLint texture_blit_vao; }; /* ---------------------------------------------------------------------- */ @@ -47,6 +49,9 @@ ConsoleGLState *console_gl_init_context(void) exit(1); } + gls->texture_blit_vao = + qemu_gl_init_texture_blit(gls->texture_blit_prog); + return gls; } @@ -131,7 +136,8 @@ void surface_gl_render_texture(ConsoleGLState *gls, glClearColor(0.1f, 0.1f, 0.1f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); - qemu_gl_run_texture_blit(gls->texture_blit_prog); + qemu_gl_run_texture_blit(gls->texture_blit_prog, + gls->texture_blit_vao); } void surface_gl_destroy_texture(ConsoleGLState *gls, diff --git a/qemu/ui/console.c b/qemu/ui/console.c index 75fc492f7..bf385790b 100644 --- a/qemu/ui/console.c +++ b/qemu/ui/console.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "ui/console.h" #include "hw/qdev-core.h" @@ -121,6 +122,7 @@ struct QemuConsole { DisplayState *ds; DisplaySurface *surface; int dcls; + DisplayChangeListener *gl; /* Graphic console state. */ Object *device; @@ -260,6 +262,16 @@ void graphic_hw_update(QemuConsole *con) } } +void graphic_hw_gl_block(QemuConsole *con, bool block) +{ + if (!con) { + con = active_console; + } + if (con && con->hw_ops->gl_block) { + con->hw_ops->gl_block(con->hw, block); + } +} + void graphic_hw_invalidate(QemuConsole *con) { if (!con) { @@ -375,42 +387,29 @@ static void vga_bitblt(QemuConsole *con, #include "vgafont.h" -#ifndef CONFIG_CURSES -enum color_names { - COLOR_BLACK = 0, - COLOR_RED = 1, - COLOR_GREEN = 2, - COLOR_YELLOW = 3, - COLOR_BLUE = 4, - COLOR_MAGENTA = 5, - COLOR_CYAN = 6, - COLOR_WHITE = 7 -}; -#endif - #define QEMU_RGB(r, g, b) \ { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff } static const pixman_color_t color_table_rgb[2][8] = { { /* dark */ - QEMU_RGB(0x00, 0x00, 0x00), /* black */ - QEMU_RGB(0xaa, 0x00, 0x00), /* red */ - QEMU_RGB(0x00, 0xaa, 0x00), /* green */ - QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */ - QEMU_RGB(0x00, 0x00, 0xaa), /* blue */ - QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */ - QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */ - QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */ + [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */ + [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xaa), /* blue */ + [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xaa, 0x00), /* green */ + [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */ + [QEMU_COLOR_RED] = QEMU_RGB(0xaa, 0x00, 0x00), /* red */ + [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */ + [QEMU_COLOR_YELLOW] = QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */ + [QEMU_COLOR_WHITE] = QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */ }, { /* bright */ - QEMU_RGB(0x00, 0x00, 0x00), /* black */ - QEMU_RGB(0xff, 0x00, 0x00), /* red */ - QEMU_RGB(0x00, 0xff, 0x00), /* green */ - QEMU_RGB(0xff, 0xff, 0x00), /* yellow */ - QEMU_RGB(0x00, 0x00, 0xff), /* blue */ - QEMU_RGB(0xff, 0x00, 0xff), /* magenta */ - QEMU_RGB(0x00, 0xff, 0xff), /* cyan */ - QEMU_RGB(0xff, 0xff, 0xff), /* white */ + [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */ + [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xff), /* blue */ + [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xff, 0x00), /* green */ + [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xff, 0xff), /* cyan */ + [QEMU_COLOR_RED] = QEMU_RGB(0xff, 0x00, 0x00), /* red */ + [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xff, 0x00, 0xff), /* magenta */ + [QEMU_COLOR_YELLOW] = QEMU_RGB(0xff, 0xff, 0x00), /* yellow */ + [QEMU_COLOR_WHITE] = QEMU_RGB(0xff, 0xff, 0xff), /* white */ } }; @@ -449,7 +448,7 @@ static void text_console_resize(QemuConsole *s) if (s->width < w1) w1 = s->width; - cells = g_malloc(s->width * s->total_height * sizeof(TextCell)); + cells = g_new(TextCell, s->width * s->total_height); for(y = 0; y < s->total_height; y++) { c = &cells[y * s->width]; if (w1 > 0) { @@ -559,7 +558,7 @@ static void console_refresh(QemuConsole *s) } vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface), - color_table_rgb[0][COLOR_BLACK]); + color_table_rgb[0][QEMU_COLOR_BLACK]); y1 = s->y_displayed; for (y = 0; y < s->height; y++) { c = s->cells + y1 * s->width; @@ -697,53 +696,53 @@ static void console_handle_escape(QemuConsole *s) break; /* set foreground color */ case 30: - s->t_attrib.fgcol=COLOR_BLACK; + s->t_attrib.fgcol = QEMU_COLOR_BLACK; break; case 31: - s->t_attrib.fgcol=COLOR_RED; + s->t_attrib.fgcol = QEMU_COLOR_RED; break; case 32: - s->t_attrib.fgcol=COLOR_GREEN; + s->t_attrib.fgcol = QEMU_COLOR_GREEN; break; case 33: - s->t_attrib.fgcol=COLOR_YELLOW; + s->t_attrib.fgcol = QEMU_COLOR_YELLOW; break; case 34: - s->t_attrib.fgcol=COLOR_BLUE; + s->t_attrib.fgcol = QEMU_COLOR_BLUE; break; case 35: - s->t_attrib.fgcol=COLOR_MAGENTA; + s->t_attrib.fgcol = QEMU_COLOR_MAGENTA; break; case 36: - s->t_attrib.fgcol=COLOR_CYAN; + s->t_attrib.fgcol = QEMU_COLOR_CYAN; break; case 37: - s->t_attrib.fgcol=COLOR_WHITE; + s->t_attrib.fgcol = QEMU_COLOR_WHITE; break; /* set background color */ case 40: - s->t_attrib.bgcol=COLOR_BLACK; + s->t_attrib.bgcol = QEMU_COLOR_BLACK; break; case 41: - s->t_attrib.bgcol=COLOR_RED; + s->t_attrib.bgcol = QEMU_COLOR_RED; break; case 42: - s->t_attrib.bgcol=COLOR_GREEN; + s->t_attrib.bgcol = QEMU_COLOR_GREEN; break; case 43: - s->t_attrib.bgcol=COLOR_YELLOW; + s->t_attrib.bgcol = QEMU_COLOR_YELLOW; break; case 44: - s->t_attrib.bgcol=COLOR_BLUE; + s->t_attrib.bgcol = QEMU_COLOR_BLUE; break; case 45: - s->t_attrib.bgcol=COLOR_MAGENTA; + s->t_attrib.bgcol = QEMU_COLOR_MAGENTA; break; case 46: - s->t_attrib.bgcol=COLOR_CYAN; + s->t_attrib.bgcol = QEMU_COLOR_CYAN; break; case 47: - s->t_attrib.bgcol=COLOR_WHITE; + s->t_attrib.bgcol = QEMU_COLOR_WHITE; break; } } @@ -758,6 +757,31 @@ static void console_clear_xy(QemuConsole *s, int x, int y) update_xy(s, x, y); } +static void console_put_one(QemuConsole *s, int ch) +{ + TextCell *c; + int y1; + if (s->x >= s->width) { + /* line wrap */ + s->x = 0; + console_put_lf(s); + } + y1 = (s->y_base + s->y) % s->total_height; + c = &s->cells[y1 * s->width + s->x]; + c->ch = ch; + c->t_attrib = s->t_attrib; + update_xy(s, s->x, s->y); + s->x++; +} + +static void console_respond_str(QemuConsole *s, const char *buf) +{ + while (*buf) { + console_put_one(s, *buf); + buf++; + } +} + /* set cursor, checking bounds */ static void set_cursor(QemuConsole *s, int x, int y) { @@ -780,9 +804,9 @@ static void set_cursor(QemuConsole *s, int x, int y) static void console_putchar(QemuConsole *s, int ch) { - TextCell *c; - int y1, i; + int i; int x, y; + char response[40]; switch(s->state) { case TTY_STATE_NORM: @@ -818,17 +842,7 @@ static void console_putchar(QemuConsole *s, int ch) s->state = TTY_STATE_ESC; break; default: - if (s->x >= s->width) { - /* line wrap */ - s->x = 0; - console_put_lf(s); - } - y1 = (s->y_base + s->y) % s->total_height; - c = &s->cells[y1 * s->width + s->x]; - c->ch = ch; - c->t_attrib = s->t_attrib; - update_xy(s, s->x, s->y); - s->x++; + console_put_one(s, ch); break; } break; @@ -957,8 +971,19 @@ static void console_putchar(QemuConsole *s, int ch) console_handle_escape(s); break; case 'n': - /* report cursor position */ - /* TODO: send ESC[row;colR */ + switch (s->esc_params[0]) { + case 5: + /* report console status (always succeed)*/ + console_respond_str(s, "\033[0n"); + break; + case 6: + /* report cursor position */ + sprintf(response, "\033[%d;%dR", + (s->y_base + s->y) % s->total_height + 1, + s->x + 1); + console_respond_str(s, response); + break; + } break; case 's': /* save cursor position */ @@ -1107,7 +1132,7 @@ void kbd_put_keysym_console(QemuConsole *s, int keysym) } } -static const int qcode_to_keysym[Q_KEY_CODE_MAX] = { +static const int qcode_to_keysym[Q_KEY_CODE__MAX] = { [Q_KEY_CODE_UP] = QEMU_KEY_UP, [Q_KEY_CODE_DOWN] = QEMU_KEY_DOWN, [Q_KEY_CODE_RIGHT] = QEMU_KEY_RIGHT, @@ -1164,11 +1189,13 @@ static void text_console_update(void *opaque, console_ch_t *chardata) src = (s->y_base + s->text_y[0]) * s->width; chardata += s->text_y[0] * s->width; for (i = s->text_y[0]; i <= s->text_y[1]; i ++) - for (j = 0; j < s->width; j ++, src ++) - console_write_ch(chardata ++, s->cells[src].ch | - (s->cells[src].t_attrib.fgcol << 12) | - (s->cells[src].t_attrib.bgcol << 8) | - (s->cells[src].t_attrib.bold << 21)); + for (j = 0; j < s->width; j++, src++) { + console_write_ch(chardata ++, + ATTR2CHTYPE(s->cells[src].ch, + s->cells[src].t_attrib.fgcol, + s->cells[src].t_attrib.bgcol, + s->cells[src].t_attrib.bold)); + } dpy_text_update(s, s->text_x[0], s->text_y[0], s->text_x[1] - s->text_x[0], i - s->text_y[0]); s->text_x[0] = s->width; @@ -1265,6 +1292,17 @@ DisplaySurface *qemu_create_displaysurface_from(int width, int height, return surface; } +DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image) +{ + DisplaySurface *surface = g_new0(DisplaySurface, 1); + + trace_displaysurface_create_pixman(surface); + surface->format = pixman_image_get_format(image); + surface->image = pixman_image_ref(image); + + return surface; +} + static void qemu_unmap_displaysurface_guestmem(pixman_image_t *image, void *unused) { @@ -1305,8 +1343,8 @@ static DisplaySurface *qemu_create_message_surface(int w, int h, const char *msg) { DisplaySurface *surface = qemu_create_displaysurface(w, h); - pixman_color_t bg = color_table_rgb[0][COLOR_BLACK]; - pixman_color_t fg = color_table_rgb[0][COLOR_WHITE]; + pixman_color_t bg = color_table_rgb[0][QEMU_COLOR_BLACK]; + pixman_color_t fg = color_table_rgb[0][QEMU_COLOR_WHITE]; pixman_image_t *glyph; int len, x, y, i; @@ -1332,6 +1370,11 @@ void qemu_free_displaysurface(DisplaySurface *surface) g_free(surface); } +bool console_has_gl(QemuConsole *con) +{ + return con->gl != NULL; +} + void register_displaychangelistener(DisplayChangeListener *dcl) { static const char nodev[] = @@ -1339,6 +1382,17 @@ void register_displaychangelistener(DisplayChangeListener *dcl) static DisplaySurface *dummy; QemuConsole *con; + if (dcl->ops->dpy_gl_ctx_create) { + /* display has opengl support */ + assert(dcl->con); + if (dcl->con->gl) { + fprintf(stderr, "can't register two opengl displays (%s, %s)\n", + dcl->ops->dpy_name, dcl->con->gl->ops->dpy_name); + exit(1); + } + dcl->con->gl = dcl; + } + trace_displaychangelistener_register(dcl, dcl->ops->dpy_name); dcl->ds = get_alloc_displaystate(); QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next); @@ -1417,9 +1471,13 @@ void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h) { DisplayState *s = con->ds; DisplayChangeListener *dcl; - int width = surface_width(con->surface); - int height = surface_height(con->surface); + int width = w; + int height = h; + if (con->surface) { + width = surface_width(con->surface); + height = surface_height(con->surface); + } x = MAX(x, 0); y = MAX(y, 0); x = MIN(x, width); @@ -1619,6 +1677,48 @@ bool dpy_cursor_define_supported(QemuConsole *con) return false; } +QEMUGLContext dpy_gl_ctx_create(QemuConsole *con, + struct QEMUGLParams *qparams) +{ + assert(con->gl); + return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams); +} + +void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx) +{ + assert(con->gl); + con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx); +} + +int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx) +{ + assert(con->gl); + return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx); +} + +QEMUGLContext dpy_gl_ctx_get_current(QemuConsole *con) +{ + assert(con->gl); + return con->gl->ops->dpy_gl_ctx_get_current(con->gl); +} + +void dpy_gl_scanout(QemuConsole *con, + uint32_t backing_id, bool backing_y_0_top, + uint32_t x, uint32_t y, uint32_t width, uint32_t height) +{ + assert(con->gl); + con->gl->ops->dpy_gl_scanout(con->gl, backing_id, + backing_y_0_top, + x, y, width, height); +} + +void dpy_gl_update(QemuConsole *con, + uint32_t x, uint32_t y, uint32_t w, uint32_t h) +{ + assert(con->gl); + con->gl->ops->dpy_gl_update(con->gl, x, y, w, h); +} + /***********************************************************/ /* register display */ @@ -1727,6 +1827,29 @@ QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head) return NULL; } +QemuConsole *qemu_console_lookup_by_device_name(const char *device_id, + uint32_t head, Error **errp) +{ + DeviceState *dev; + QemuConsole *con; + + dev = qdev_find_recursive(sysbus_get_default(), device_id); + if (dev == NULL) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", device_id); + return NULL; + } + + con = qemu_console_lookup_by_device(dev, head); + if (con == NULL) { + error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole", + device_id, head); + return NULL; + } + + return con; +} + bool qemu_console_is_visible(QemuConsole *con) { return (con == active_console) || (con->dcls > 0); @@ -1878,8 +2001,8 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds) s->t_attrib_default.blink = 0; s->t_attrib_default.invers = 0; s->t_attrib_default.unvisible = 0; - s->t_attrib_default.fgcol = COLOR_WHITE; - s->t_attrib_default.bgcol = COLOR_BLACK; + s->t_attrib_default.fgcol = QEMU_COLOR_WHITE; + s->t_attrib_default.bgcol = QEMU_COLOR_BLACK; /* set current text attributes to default */ s->t_attrib = s->t_attrib_default; text_console_resize(s); @@ -1888,7 +2011,7 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds) char msg[128]; int len; - s->t_attrib.bgcol = COLOR_BLUE; + s->t_attrib.bgcol = QEMU_COLOR_BLUE; len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label); console_puts(chr, (uint8_t*)msg, len); s->t_attrib = s->t_attrib_default; @@ -1899,14 +2022,18 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds) chr->init(chr); } -static CharDriverState *text_console_init(ChardevVC *vc) +static CharDriverState *text_console_init(ChardevVC *vc, Error **errp) { + ChardevCommon *common = qapi_ChardevVC_base(vc); CharDriverState *chr; QemuConsole *s; unsigned width = 0; unsigned height = 0; - chr = qemu_chr_alloc(); + chr = qemu_chr_alloc(common, errp); + if (!chr) { + return NULL; + } if (vc->has_width) { width = vc->width; @@ -1930,6 +2057,7 @@ static CharDriverState *text_console_init(ChardevVC *vc) if (!s) { g_free(chr); + error_setg(errp, "cannot create text console"); return NULL; } @@ -1949,9 +2077,10 @@ static CharDriverState *text_console_init(ChardevVC *vc) static VcHandler *vc_handler = text_console_init; -CharDriverState *vc_init(ChardevVC *vc) +static CharDriverState *vc_init(const char *id, ChardevBackend *backend, + ChardevReturn *ret, Error **errp) { - return vc_handler(vc); + return vc_handler(backend->u.vc.data, errp); } void register_vc_handler(VcHandler *handler) @@ -1991,31 +2120,33 @@ static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp) { int val; + ChardevVC *vc; - backend->vc = g_new0(ChardevVC, 1); + vc = backend->u.vc.data = g_new0(ChardevVC, 1); + qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc)); val = qemu_opt_get_number(opts, "width", 0); if (val != 0) { - backend->vc->has_width = true; - backend->vc->width = val; + vc->has_width = true; + vc->width = val; } val = qemu_opt_get_number(opts, "height", 0); if (val != 0) { - backend->vc->has_height = true; - backend->vc->height = val; + vc->has_height = true; + vc->height = val; } val = qemu_opt_get_number(opts, "cols", 0); if (val != 0) { - backend->vc->has_cols = true; - backend->vc->cols = val; + vc->has_cols = true; + vc->cols = val; } val = qemu_opt_get_number(opts, "rows", 0); if (val != 0) { - backend->vc->has_rows = true; - backend->vc->rows = val; + vc->has_rows = true; + vc->rows = val; } } @@ -2030,7 +2161,8 @@ static const TypeInfo qemu_console_info = { static void register_types(void) { type_register_static(&qemu_console_info); - register_char_driver("vc", CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc); + register_char_driver("vc", CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc, + vc_init); } type_init(register_types); diff --git a/qemu/ui/curses.c b/qemu/ui/curses.c index 8edb038bb..b47558956 100644 --- a/qemu/ui/curses.c +++ b/qemu/ui/curses.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #ifndef _WIN32 @@ -42,6 +43,8 @@ static WINDOW *screenpad = NULL; static int width, height, gwidth, gheight, invalidate; static int px, py, sminx, sminy, smaxx, smaxy; +chtype vga_to_curses[256]; + static void curses_update(DisplayChangeListener *dcl, int x, int y, int w, int h) { @@ -331,8 +334,14 @@ static void curses_atexit(void) static void curses_setup(void) { int i, colour_default[8] = { - COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN, - COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE, + [QEMU_COLOR_BLACK] = COLOR_BLACK, + [QEMU_COLOR_BLUE] = COLOR_BLUE, + [QEMU_COLOR_GREEN] = COLOR_GREEN, + [QEMU_COLOR_CYAN] = COLOR_CYAN, + [QEMU_COLOR_RED] = COLOR_RED, + [QEMU_COLOR_MAGENTA] = COLOR_MAGENTA, + [QEMU_COLOR_YELLOW] = COLOR_YELLOW, + [QEMU_COLOR_WHITE] = COLOR_WHITE, }; /* input as raw as possible, let everything be interpreted @@ -341,8 +350,56 @@ static void curses_setup(void) nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE); start_color(); raw(); scrollok(stdscr, FALSE); - for (i = 0; i < 64; i ++) + /* Make color pair to match color format (3bits bg:3bits fg) */ + for (i = 0; i < 64; i++) { init_pair(i, colour_default[i & 7], colour_default[i >> 3]); + } + /* Set default color for more than 64 for safety. */ + for (i = 64; i < COLOR_PAIRS; i++) { + init_pair(i, COLOR_WHITE, COLOR_BLACK); + } + + /* + * Setup mapping for vga to curses line graphics. + * FIXME: for better font, have to use ncursesw and setlocale() + */ +#if 0 + /* FIXME: map from where? */ + ACS_S1; + ACS_S3; + ACS_S7; + ACS_S9; +#endif + /* ACS_* is not constant. So, we can't initialize statically. */ + vga_to_curses['\0'] = ' '; + vga_to_curses[0x04] = ACS_DIAMOND; + vga_to_curses[0x0a] = ACS_RARROW; + vga_to_curses[0x0b] = ACS_LARROW; + vga_to_curses[0x18] = ACS_UARROW; + vga_to_curses[0x19] = ACS_DARROW; + vga_to_curses[0x9c] = ACS_STERLING; + vga_to_curses[0xb0] = ACS_BOARD; + vga_to_curses[0xb1] = ACS_CKBOARD; + vga_to_curses[0xb3] = ACS_VLINE; + vga_to_curses[0xb4] = ACS_RTEE; + vga_to_curses[0xbf] = ACS_URCORNER; + vga_to_curses[0xc0] = ACS_LLCORNER; + vga_to_curses[0xc1] = ACS_BTEE; + vga_to_curses[0xc2] = ACS_TTEE; + vga_to_curses[0xc3] = ACS_LTEE; + vga_to_curses[0xc4] = ACS_HLINE; + vga_to_curses[0xc5] = ACS_PLUS; + vga_to_curses[0xce] = ACS_LANTERN; + vga_to_curses[0xd8] = ACS_NEQUAL; + vga_to_curses[0xd9] = ACS_LRCORNER; + vga_to_curses[0xda] = ACS_ULCORNER; + vga_to_curses[0xdb] = ACS_BLOCK; + vga_to_curses[0xe3] = ACS_PI; + vga_to_curses[0xf1] = ACS_PLMINUS; + vga_to_curses[0xf2] = ACS_GEQUAL; + vga_to_curses[0xf3] = ACS_LEQUAL; + vga_to_curses[0xf8] = ACS_DEGREE; + vga_to_curses[0xfe] = ACS_BULLET; } static void curses_keyboard_setup(void) @@ -382,7 +439,7 @@ void curses_display_init(DisplayState *ds, int full_screen) curses_winch_init(); - dcl = (DisplayChangeListener *) g_malloc0(sizeof(DisplayChangeListener)); + dcl = g_new0(DisplayChangeListener, 1); dcl->ops = &dcl_ops; register_displaychangelistener(dcl); diff --git a/qemu/ui/curses_keys.h b/qemu/ui/curses_keys.h index 18ce6dcee..f7467449b 100644 --- a/qemu/ui/curses_keys.h +++ b/qemu/ui/curses_keys.h @@ -29,8 +29,7 @@ #include "keymaps.h" -#define KEY_RELEASE 0x80 -#define KEY_MASK 0x7f +#define KEY_MASK SCANCODE_KEYMASK #define GREY_CODE 0xe0 #define GREY SCANCODE_GREY #define SHIFT_CODE 0x2a @@ -60,6 +59,8 @@ static const int curses2keysym[CURSES_KEYS] = { ['\n'] = KEY_ENTER, [27] = 27, [KEY_BTAB] = '\t' | KEYSYM_SHIFT, + [KEY_SPREVIOUS] = KEY_PPAGE | KEYSYM_SHIFT, + [KEY_SNEXT] = KEY_NPAGE | KEYSYM_SHIFT, }; static const int curses2keycode[CURSES_KEYS] = { @@ -149,6 +150,9 @@ static const int curses2keycode[CURSES_KEYS] = { [KEY_IC] = 82 | GREY, /* Insert */ [KEY_DC] = 83 | GREY, /* Delete */ + [KEY_SPREVIOUS] = 73 | GREY | SHIFT, /* Shift + Page Up */ + [KEY_SNEXT] = 81 | GREY | SHIFT, /* Shift + Page Down */ + ['!'] = 2 | SHIFT, ['@'] = 3 | SHIFT, ['#'] = 4 | SHIFT, diff --git a/qemu/ui/cursor.c b/qemu/ui/cursor.c index 2b8dd3fa5..a276e01f1 100644 --- a/qemu/ui/cursor.c +++ b/qemu/ui/cursor.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "ui/console.h" diff --git a/qemu/ui/egl-context.c b/qemu/ui/egl-context.c new file mode 100644 index 000000000..3a02b68d1 --- /dev/null +++ b/qemu/ui/egl-context.c @@ -0,0 +1,35 @@ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "ui/egl-context.h" + +QEMUGLContext qemu_egl_create_context(DisplayChangeListener *dcl, + QEMUGLParams *params) +{ + EGLContext ctx; + EGLint ctx_att[] = { + EGL_CONTEXT_CLIENT_VERSION, params->major_ver, + EGL_CONTEXT_MINOR_VERSION_KHR, params->minor_ver, + EGL_NONE + }; + + ctx = eglCreateContext(qemu_egl_display, qemu_egl_config, + eglGetCurrentContext(), ctx_att); + return ctx; +} + +void qemu_egl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx) +{ + eglDestroyContext(qemu_egl_display, ctx); +} + +int qemu_egl_make_context_current(DisplayChangeListener *dcl, + QEMUGLContext ctx) +{ + return eglMakeCurrent(qemu_egl_display, + EGL_NO_SURFACE, EGL_NO_SURFACE, ctx); +} + +QEMUGLContext qemu_egl_get_current_context(DisplayChangeListener *dcl) +{ + return eglGetCurrentContext(); +} diff --git a/qemu/ui/egl-helpers.c b/qemu/ui/egl-helpers.c index 87d77afaa..558edfdeb 100644 --- a/qemu/ui/egl-helpers.c +++ b/qemu/ui/egl-helpers.c @@ -1,12 +1,6 @@ -#include -#include -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include +#include #include "ui/egl-helpers.h" @@ -27,6 +21,133 @@ static int egl_debug; /* ---------------------------------------------------------------------- */ +#ifdef CONFIG_OPENGL_DMABUF + +int qemu_egl_rn_fd; +struct gbm_device *qemu_egl_rn_gbm_dev; +EGLContext qemu_egl_rn_ctx; + +int qemu_egl_rendernode_open(void) +{ + DIR *dir; + struct dirent *e; + int r, fd; + char *p; + + dir = opendir("/dev/dri"); + if (!dir) { + return -1; + } + + fd = -1; + while ((e = readdir(dir))) { + if (e->d_type != DT_CHR) { + continue; + } + + if (strncmp(e->d_name, "renderD", 7)) { + continue; + } + + r = asprintf(&p, "/dev/dri/%s", e->d_name); + if (r < 0) { + return -1; + } + + r = open(p, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); + if (r < 0) { + free(p); + continue; + } + fd = r; + free(p); + break; + } + + closedir(dir); + if (fd < 0) { + return -1; + } + return fd; +} + +int egl_rendernode_init(void) +{ + qemu_egl_rn_fd = -1; + + qemu_egl_rn_fd = qemu_egl_rendernode_open(); + if (qemu_egl_rn_fd == -1) { + fprintf(stderr, "egl: no drm render node available\n"); + goto err; + } + + qemu_egl_rn_gbm_dev = gbm_create_device(qemu_egl_rn_fd); + if (!qemu_egl_rn_gbm_dev) { + fprintf(stderr, "egl: gbm_create_device failed\n"); + goto err; + } + + qemu_egl_init_dpy((EGLNativeDisplayType)qemu_egl_rn_gbm_dev, false, false); + + if (!epoxy_has_egl_extension(qemu_egl_display, + "EGL_KHR_surfaceless_context")) { + fprintf(stderr, "egl: EGL_KHR_surfaceless_context not supported\n"); + goto err; + } + if (!epoxy_has_egl_extension(qemu_egl_display, + "EGL_MESA_image_dma_buf_export")) { + fprintf(stderr, "egl: EGL_MESA_image_dma_buf_export not supported\n"); + goto err; + } + + qemu_egl_rn_ctx = qemu_egl_init_ctx(); + if (!qemu_egl_rn_ctx) { + fprintf(stderr, "egl: egl_init_ctx failed\n"); + goto err; + } + + return 0; + +err: + if (qemu_egl_rn_gbm_dev) { + gbm_device_destroy(qemu_egl_rn_gbm_dev); + } + if (qemu_egl_rn_fd != -1) { + close(qemu_egl_rn_fd); + } + + return -1; +} + +int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc) +{ + EGLImageKHR image; + EGLint num_planes, fd; + + image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(), + EGL_GL_TEXTURE_2D_KHR, + (EGLClientBuffer)(unsigned long)tex_id, + NULL); + if (!image) { + return -1; + } + + eglExportDMABUFImageQueryMESA(qemu_egl_display, image, fourcc, + &num_planes, NULL); + if (num_planes != 1) { + eglDestroyImageKHR(qemu_egl_display, image); + return -1; + } + eglExportDMABUFImageMESA(qemu_egl_display, image, &fd, stride, NULL); + eglDestroyImageKHR(qemu_egl_display, image); + + return fd; +} + +#endif /* CONFIG_OPENGL_DMABUF */ + +/* ---------------------------------------------------------------------- */ + EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win) { EGLSurface esurface; diff --git a/qemu/ui/gtk-egl.c b/qemu/ui/gtk-egl.c index 15b41f2ba..431457c74 100644 --- a/qemu/ui/gtk-egl.c +++ b/qemu/ui/gtk-egl.c @@ -11,6 +11,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "trace.h" @@ -21,6 +22,29 @@ #include "sysemu/sysemu.h" +static void gtk_egl_set_scanout_mode(VirtualConsole *vc, bool scanout) +{ + if (vc->gfx.scanout_mode == scanout) { + return; + } + + vc->gfx.scanout_mode = scanout; + if (!vc->gfx.scanout_mode) { + if (vc->gfx.fbo_id) { + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, 0, 0); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + glDeleteFramebuffers(1, &vc->gfx.fbo_id); + vc->gfx.fbo_id = 0; + } + if (vc->gfx.surface) { + surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); + surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds); + } + } +} + /** DisplayState Callbacks (opengl version) **/ void gd_egl_init(VirtualConsole *vc) @@ -50,19 +74,26 @@ void gd_egl_draw(VirtualConsole *vc) GdkWindow *window; int ww, wh; - if (!vc->gfx.gls || !vc->gfx.ds) { + if (!vc->gfx.gls) { return; } - eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, - vc->gfx.esurface, vc->gfx.ectx); + if (vc->gfx.scanout_mode) { + gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h); + } else { + if (!vc->gfx.ds) { + return; + } + eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, + vc->gfx.esurface, vc->gfx.ectx); - window = gtk_widget_get_window(vc->gfx.drawing_area); - gdk_drawable_get_size(window, &ww, &wh); - surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh); - surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds); + window = gtk_widget_get_window(vc->gfx.drawing_area); + gdk_drawable_get_size(window, &ww, &wh); + surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh); + surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds); - eglSwapBuffers(qemu_egl_display, vc->gfx.esurface); + eglSwapBuffers(qemu_egl_display, vc->gfx.esurface); + } } void gd_egl_update(DisplayChangeListener *dcl, @@ -99,6 +130,7 @@ void gd_egl_refresh(DisplayChangeListener *dcl) if (vc->gfx.glupdates) { vc->gfx.glupdates = 0; + gtk_egl_set_scanout_mode(vc, false); gd_egl_draw(vc); } } @@ -128,6 +160,81 @@ void gd_egl_switch(DisplayChangeListener *dcl, } } +QEMUGLContext gd_egl_create_context(DisplayChangeListener *dcl, + QEMUGLParams *params) +{ + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + + eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, + vc->gfx.esurface, vc->gfx.ectx); + return qemu_egl_create_context(dcl, params); +} + +void gd_egl_scanout(DisplayChangeListener *dcl, + uint32_t backing_id, bool backing_y_0_top, + uint32_t x, uint32_t y, + uint32_t w, uint32_t h) +{ + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + + vc->gfx.x = x; + vc->gfx.y = y; + vc->gfx.w = w; + vc->gfx.h = h; + vc->gfx.tex_id = backing_id; + vc->gfx.y0_top = backing_y_0_top; + + eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, + vc->gfx.esurface, vc->gfx.ectx); + + if (vc->gfx.tex_id == 0 || vc->gfx.w == 0 || vc->gfx.h == 0) { + gtk_egl_set_scanout_mode(vc, false); + return; + } + + gtk_egl_set_scanout_mode(vc, true); + if (!vc->gfx.fbo_id) { + glGenFramebuffers(1, &vc->gfx.fbo_id); + } + + glBindFramebuffer(GL_FRAMEBUFFER_EXT, vc->gfx.fbo_id); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, vc->gfx.tex_id, 0); +} + +void gd_egl_scanout_flush(DisplayChangeListener *dcl, + uint32_t x, uint32_t y, uint32_t w, uint32_t h) +{ + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + GdkWindow *window; + int ww, wh, y1, y2; + + if (!vc->gfx.scanout_mode) { + return; + } + if (!vc->gfx.fbo_id) { + return; + } + + eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, + vc->gfx.esurface, vc->gfx.ectx); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.fbo_id); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + + window = gtk_widget_get_window(vc->gfx.drawing_area); + gdk_drawable_get_size(window, &ww, &wh); + glViewport(0, 0, ww, wh); + y1 = vc->gfx.y0_top ? 0 : vc->gfx.h; + y2 = vc->gfx.y0_top ? vc->gfx.h : 0; + glBlitFramebuffer(0, y1, vc->gfx.w, y2, + 0, 0, ww, wh, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, vc->gfx.fbo_id); + + eglSwapBuffers(qemu_egl_display, vc->gfx.esurface); +} + void gtk_egl_init(void) { GdkDisplay *gdk_display = gdk_display_get_default(); @@ -139,3 +246,12 @@ void gtk_egl_init(void) display_opengl = 1; } + +int gd_egl_make_current(DisplayChangeListener *dcl, + QEMUGLContext ctx) +{ + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + + return eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, + vc->gfx.esurface, ctx); +} diff --git a/qemu/ui/gtk-gl-area.c b/qemu/ui/gtk-gl-area.c new file mode 100644 index 000000000..b86ff3cbe --- /dev/null +++ b/qemu/ui/gtk-gl-area.c @@ -0,0 +1,224 @@ +/* + * GTK UI -- glarea opengl code. + * + * Requires 3.16+ (GtkGLArea widget). + * + * 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 "trace.h" + +#include "ui/console.h" +#include "ui/gtk.h" +#include "ui/egl-helpers.h" + +#include "sysemu/sysemu.h" + +static void gtk_gl_area_set_scanout_mode(VirtualConsole *vc, bool scanout) +{ + if (vc->gfx.scanout_mode == scanout) { + return; + } + + vc->gfx.scanout_mode = scanout; + if (!vc->gfx.scanout_mode) { + if (vc->gfx.fbo_id) { + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, 0, 0); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + glDeleteFramebuffers(1, &vc->gfx.fbo_id); + vc->gfx.fbo_id = 0; + } + if (vc->gfx.surface) { + surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); + surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds); + } + } +} + +/** DisplayState Callbacks (opengl version) **/ + +void gd_gl_area_draw(VirtualConsole *vc) +{ + int ww, wh, y1, y2; + + if (!vc->gfx.gls) { + return; + } + + gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); + ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area); + wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area); + + if (vc->gfx.scanout_mode) { + if (!vc->gfx.fbo_id) { + return; + } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.fbo_id); + /* GtkGLArea sets GL_DRAW_FRAMEBUFFER for us */ + + glViewport(0, 0, ww, wh); + y1 = vc->gfx.y0_top ? 0 : vc->gfx.h; + y2 = vc->gfx.y0_top ? vc->gfx.h : 0; + glBlitFramebuffer(0, y1, vc->gfx.w, y2, + 0, 0, ww, wh, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + } else { + if (!vc->gfx.ds) { + return; + } + gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); + + surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh); + surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds); + } +} + +void gd_gl_area_update(DisplayChangeListener *dcl, + int x, int y, int w, int h) +{ + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + + if (!vc->gfx.gls || !vc->gfx.ds) { + return; + } + + gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); + surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h); + vc->gfx.glupdates++; +} + +void gd_gl_area_refresh(DisplayChangeListener *dcl) +{ + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + + if (!vc->gfx.gls) { + if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { + return; + } + gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); + vc->gfx.gls = console_gl_init_context(); + if (vc->gfx.ds) { + surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds); + } + } + + graphic_hw_update(dcl->con); + + if (vc->gfx.glupdates) { + vc->gfx.glupdates = 0; + gtk_gl_area_set_scanout_mode(vc, false); + gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); + } +} + +void gd_gl_area_switch(DisplayChangeListener *dcl, + DisplaySurface *surface) +{ + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + bool resized = true; + + trace_gd_switch(vc->label, surface_width(surface), surface_height(surface)); + + if (vc->gfx.ds && + surface_width(vc->gfx.ds) == surface_width(surface) && + surface_height(vc->gfx.ds) == surface_height(surface)) { + resized = false; + } + + if (vc->gfx.gls) { + gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); + surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); + surface_gl_create_texture(vc->gfx.gls, surface); + } + vc->gfx.ds = surface; + + if (resized) { + gd_update_windowsize(vc); + } +} + +QEMUGLContext gd_gl_area_create_context(DisplayChangeListener *dcl, + QEMUGLParams *params) +{ + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + GdkWindow *window; + GdkGLContext *ctx; + GError *err = NULL; + + gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); + window = gtk_widget_get_window(vc->gfx.drawing_area); + ctx = gdk_window_create_gl_context(window, &err); + gdk_gl_context_set_required_version(ctx, + params->major_ver, + params->minor_ver); + gdk_gl_context_realize(ctx, &err); + return ctx; +} + +void gd_gl_area_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx) +{ + /* FIXME */ +} + +void gd_gl_area_scanout(DisplayChangeListener *dcl, + uint32_t backing_id, bool backing_y_0_top, + uint32_t x, uint32_t y, + uint32_t w, uint32_t h) +{ + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + + vc->gfx.x = x; + vc->gfx.y = y; + vc->gfx.w = w; + vc->gfx.h = h; + vc->gfx.tex_id = backing_id; + vc->gfx.y0_top = backing_y_0_top; + + gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); + + if (vc->gfx.tex_id == 0 || vc->gfx.w == 0 || vc->gfx.h == 0) { + gtk_gl_area_set_scanout_mode(vc, false); + return; + } + + gtk_gl_area_set_scanout_mode(vc, true); + if (!vc->gfx.fbo_id) { + glGenFramebuffers(1, &vc->gfx.fbo_id); + } + + glBindFramebuffer(GL_FRAMEBUFFER_EXT, vc->gfx.fbo_id); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, vc->gfx.tex_id, 0); +} + +void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, + uint32_t x, uint32_t y, uint32_t w, uint32_t h) +{ + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + + gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); +} + +void gtk_gl_area_init(void) +{ + display_opengl = 1; +} + +QEMUGLContext gd_gl_area_get_current_context(DisplayChangeListener *dcl) +{ + return gdk_gl_context_get_current(); +} + +int gd_gl_area_make_current(DisplayChangeListener *dcl, + QEMUGLContext ctx) +{ + gdk_gl_context_make_current(ctx); + return 0; +} diff --git a/qemu/ui/gtk.c b/qemu/ui/gtk.c index df2a79e7a..f372a6d5a 100644 --- a/qemu/ui/gtk.c +++ b/qemu/ui/gtk.c @@ -34,7 +34,9 @@ #define GETTEXT_PACKAGE "qemu" #define LOCALEDIR "po" +#include "qemu/osdep.h" #include "qemu-common.h" +#include "qemu/cutils.h" #include "ui/console.h" #include "ui/gtk.h" @@ -104,6 +106,15 @@ #define GDK_KEY_Pause GDK_Pause #endif +/* Some older mingw versions lack this constant or have + * it conditionally defined */ +#ifdef _WIN32 +# ifndef MAPVK_VK_TO_VSC +# define MAPVK_VK_TO_VSC 0 +# endif +#endif + + #define HOTKEY_MODIFIERS (GDK_CONTROL_MASK | GDK_MOD1_MASK) static const int modifier_keycode[] = { @@ -165,8 +176,10 @@ struct GtkDisplayState { bool ignore_keys; }; -static void gd_grab_pointer(VirtualConsole *vc); +static void gd_grab_pointer(VirtualConsole *vc, const char *reason); static void gd_ungrab_pointer(GtkDisplayState *s); +static void gd_grab_keyboard(VirtualConsole *vc, const char *reason); +static void gd_ungrab_keyboard(GtkDisplayState *s); /** Utility Functions **/ @@ -356,6 +369,12 @@ static void gd_update_full_redraw(VirtualConsole *vc) GtkWidget *area = vc->gfx.drawing_area; int ww, wh; gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh); +#if defined(CONFIG_GTK_GL) + if (vc->gfx.gls) { + gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); + return; + } +#endif gtk_widget_queue_draw_area(area, 0, 0, ww, wh); } @@ -596,6 +615,27 @@ static const DisplayChangeListenerOps dcl_ops = { /** DisplayState Callbacks (opengl version) **/ +#if defined(CONFIG_GTK_GL) + +static const DisplayChangeListenerOps dcl_gl_area_ops = { + .dpy_name = "gtk-egl", + .dpy_gfx_update = gd_gl_area_update, + .dpy_gfx_switch = gd_gl_area_switch, + .dpy_gfx_check_format = console_gl_check_format, + .dpy_refresh = gd_gl_area_refresh, + .dpy_mouse_set = gd_mouse_set, + .dpy_cursor_define = gd_cursor_define, + + .dpy_gl_ctx_create = gd_gl_area_create_context, + .dpy_gl_ctx_destroy = gd_gl_area_destroy_context, + .dpy_gl_ctx_make_current = gd_gl_area_make_current, + .dpy_gl_ctx_get_current = gd_gl_area_get_current_context, + .dpy_gl_scanout = gd_gl_area_scanout, + .dpy_gl_update = gd_gl_area_scanout_flush, +}; + +#else + static const DisplayChangeListenerOps dcl_egl_ops = { .dpy_name = "gtk-egl", .dpy_gfx_update = gd_egl_update, @@ -604,9 +644,17 @@ static const DisplayChangeListenerOps dcl_egl_ops = { .dpy_refresh = gd_egl_refresh, .dpy_mouse_set = gd_mouse_set, .dpy_cursor_define = gd_cursor_define, + + .dpy_gl_ctx_create = gd_egl_create_context, + .dpy_gl_ctx_destroy = qemu_egl_destroy_context, + .dpy_gl_ctx_make_current = gd_egl_make_current, + .dpy_gl_ctx_get_current = qemu_egl_get_current_context, + .dpy_gl_scanout = gd_egl_scanout, + .dpy_gl_update = gd_egl_scanout_flush, }; -#endif +#endif /* CONFIG_GTK_GL */ +#endif /* CONFIG_OPENGL */ /** QEMU Events **/ @@ -656,6 +704,39 @@ static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event, return TRUE; } +static void gd_set_ui_info(VirtualConsole *vc, gint width, gint height) +{ + QemuUIInfo info; + + memset(&info, 0, sizeof(info)); + info.width = width; + info.height = height; + dpy_set_ui_info(vc->gfx.dcl.con, &info); +} + +#if defined(CONFIG_GTK_GL) + +static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context, + void *opaque) +{ + VirtualConsole *vc = opaque; + + if (vc->gfx.gls) { + gd_gl_area_draw(vc); + } + return TRUE; +} + +static void gd_resize_event(GtkGLArea *area, + gint width, gint height, gpointer *opaque) +{ + VirtualConsole *vc = (void *)opaque; + + gd_set_ui_info(vc, width, height); +} + +#endif + static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) { VirtualConsole *vc = opaque; @@ -666,8 +747,13 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) #if defined(CONFIG_OPENGL) if (vc->gfx.gls) { +#if defined(CONFIG_GTK_GL) + /* invoke render callback please */ + return FALSE; +#else gd_egl_draw(vc); return TRUE; +#endif } #endif @@ -849,13 +935,11 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, /* implicitly grab the input at the first click in the relative mode */ if (button->button == 1 && button->type == GDK_BUTTON_PRESS && !qemu_input_is_absolute() && s->ptr_owner != vc) { - gd_ungrab_pointer(s); if (!vc->window) { gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), TRUE); } else { - gd_grab_pointer(vc); - gd_update_caption(s); + gd_grab_pointer(vc, "relative-mode-click"); } return TRUE; } @@ -1092,9 +1176,8 @@ static gboolean gd_win_grab(void *opaque) if (vc->s->ptr_owner) { gd_ungrab_pointer(vc->s); } else { - gd_grab_pointer(vc); + gd_grab_pointer(vc, "user-request-detached-tab"); } - gd_update_caption(vc->s); return TRUE; } @@ -1141,10 +1224,6 @@ static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) gtk_widget_hide(s->menu_bar); if (vc->type == GD_VC_GFX) { gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1); - if (qemu_console_is_graphic(vc->gfx.dcl.con)) { - gtk_check_menu_item_set_active - (GTK_CHECK_MENU_ITEM(s->grab_item), TRUE); - } } gtk_window_fullscreen(GTK_WINDOW(s->window)); s->full_screen = TRUE; @@ -1157,8 +1236,6 @@ static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) vc->gfx.scale_x = 1.0; vc->gfx.scale_y = 1.0; gd_update_windowsize(vc); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), - FALSE); } } @@ -1257,8 +1334,16 @@ static void gd_grab_devices(VirtualConsole *vc, bool grab, } #endif -static void gd_grab_keyboard(VirtualConsole *vc) +static void gd_grab_keyboard(VirtualConsole *vc, const char *reason) { + if (vc->s->kbd_owner) { + if (vc->s->kbd_owner == vc) { + return; + } else { + gd_ungrab_keyboard(vc->s); + } + } + #if GTK_CHECK_VERSION(3, 0, 0) gd_grab_devices(vc, true, GDK_SOURCE_KEYBOARD, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK, @@ -1269,7 +1354,8 @@ static void gd_grab_keyboard(VirtualConsole *vc) GDK_CURRENT_TIME); #endif vc->s->kbd_owner = vc; - trace_gd_grab(vc->label, "kbd", true); + gd_update_caption(vc->s); + trace_gd_grab(vc->label, "kbd", reason); } static void gd_ungrab_keyboard(GtkDisplayState *s) @@ -1286,12 +1372,22 @@ static void gd_ungrab_keyboard(GtkDisplayState *s) #else gdk_keyboard_ungrab(GDK_CURRENT_TIME); #endif - trace_gd_grab(vc->label, "kbd", false); + gd_update_caption(s); + trace_gd_ungrab(vc->label, "kbd"); } -static void gd_grab_pointer(VirtualConsole *vc) +static void gd_grab_pointer(VirtualConsole *vc, const char *reason) { GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); + + if (vc->s->ptr_owner) { + if (vc->s->ptr_owner == vc) { + return; + } else { + gd_ungrab_pointer(vc->s); + } + } + #if GTK_CHECK_VERSION(3, 0, 0) GdkDeviceManager *mgr = gdk_display_get_device_manager(display); gd_grab_devices(vc, true, GDK_SOURCE_MOUSE, @@ -1318,7 +1414,8 @@ static void gd_grab_pointer(VirtualConsole *vc) &vc->s->grab_x_root, &vc->s->grab_y_root, NULL); #endif vc->s->ptr_owner = vc; - trace_gd_grab(vc->label, "ptr", true); + gd_update_caption(vc->s); + trace_gd_grab(vc->label, "ptr", reason); } static void gd_ungrab_pointer(GtkDisplayState *s) @@ -1343,7 +1440,8 @@ static void gd_ungrab_pointer(GtkDisplayState *s) gtk_widget_get_screen(vc->gfx.drawing_area), vc->s->grab_x_root, vc->s->grab_y_root); #endif - trace_gd_grab(vc->label, "ptr", false); + gd_update_caption(s); + trace_gd_ungrab(vc->label, "ptr"); } static void gd_menu_grab_input(GtkMenuItem *item, void *opaque) @@ -1352,16 +1450,13 @@ static void gd_menu_grab_input(GtkMenuItem *item, void *opaque) VirtualConsole *vc = gd_vc_find_current(s); if (gd_is_grab_active(s)) { - if (!gd_grab_on_hover(s)) { - gd_grab_keyboard(vc); - } - gd_grab_pointer(vc); + gd_grab_keyboard(vc, "user-request-main-window"); + gd_grab_pointer(vc, "user-request-main-window"); } else { gd_ungrab_keyboard(s); gd_ungrab_pointer(s); } - gd_update_caption(s); gd_update_cursor(vc); } @@ -1415,9 +1510,7 @@ static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, GtkDisplayState *s = vc->s; if (gd_grab_on_hover(s)) { - gd_ungrab_keyboard(s); - gd_grab_keyboard(vc); - gd_update_caption(s); + gd_grab_keyboard(vc, "grab-on-hover"); } return TRUE; } @@ -1430,7 +1523,6 @@ static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, if (gd_grab_on_hover(s)) { gd_ungrab_keyboard(s); - gd_update_caption(s); } return TRUE; } @@ -1449,12 +1541,8 @@ static gboolean gd_configure(GtkWidget *widget, GdkEventConfigure *cfg, gpointer opaque) { VirtualConsole *vc = opaque; - QemuUIInfo info; - memset(&info, 0, sizeof(info)); - info.width = cfg->width; - info.height = cfg->height; - dpy_set_ui_info(vc->gfx.dcl.con, &info); + gd_set_ui_info(vc, cfg->width, cfg->height); return FALSE; } @@ -1502,15 +1590,32 @@ static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len) return len; } +static void gd_vc_chr_set_echo(CharDriverState *chr, bool echo) +{ + VirtualConsole *vc = chr->opaque; + + vc->vte.echo = echo; +} + static int nb_vcs; static CharDriverState *vcs[MAX_VCS]; -static CharDriverState *gd_vc_handler(ChardevVC *unused) +static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp) { + ChardevCommon *common = qapi_ChardevVC_base(vc); CharDriverState *chr; - chr = g_malloc0(sizeof(*chr)); + chr = qemu_chr_alloc(common, errp); + if (!chr) { + return NULL; + } + chr->chr_write = gd_vc_chr_write; + chr->chr_set_echo = gd_vc_chr_set_echo; + + /* Temporary, until gd_vc_vte_init runs. */ + chr->opaque = g_new0(VirtualConsole, 1); + /* defer OPENED events until our vc is fully initialized */ chr->explicit_be_open = true; @@ -1524,6 +1629,24 @@ static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size, { VirtualConsole *vc = user_data; + if (vc->vte.echo) { + VteTerminal *term = VTE_TERMINAL(vc->vte.terminal); + int i; + for (i = 0; i < size; i++) { + uint8_t c = text[i]; + if (c >= 128 || isprint(c)) { + /* 8-bit characters are considered printable. */ + vte_terminal_feed(term, &text[i], 1); + } else if (c == '\r' || c == '\n') { + vte_terminal_feed(term, "\r\n", 2); + } else { + char ctrl[2] = { '^', 0}; + ctrl[1] = text[i] ^ 64; + vte_terminal_feed(term, ctrl, 2); + } + } + } + qemu_chr_be_write(vc->vte.chr, (uint8_t *)text, (unsigned int)size); return TRUE; } @@ -1536,9 +1659,14 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, GtkWidget *box; GtkWidget *scrollbar; GtkAdjustment *vadjustment; + VirtualConsole *tmp_vc = chr->opaque; vc->s = s; + vc->vte.echo = tmp_vc->vte.echo; + vc->vte.chr = chr; + chr->opaque = vc; + g_free(tmp_vc); snprintf(buffer, sizeof(buffer), "vc%d", idx); vc->label = g_strdup_printf("%s", vc->vte.chr->label @@ -1548,6 +1676,15 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, vc->vte.terminal = vte_terminal_new(); g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc); + /* The documentation says that the default is UTF-8, but actually it is + * 7-bit ASCII at least in VTE 0.38. + */ +#if VTE_CHECK_VERSION(0, 40, 0) + vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8", NULL); +#else + vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8"); +#endif + vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1); vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal), VC_TERM_X_MIN, VC_TERM_Y_MIN); @@ -1570,7 +1707,6 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, gtk_box_pack_start(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(box), scrollbar, FALSE, FALSE, 0); - vc->vte.chr->opaque = vc; vc->vte.box = box; vc->vte.scrollbar = scrollbar; @@ -1611,6 +1747,15 @@ static void gd_connect_vc_gfx_signals(VirtualConsole *vc) #if GTK_CHECK_VERSION(3, 0, 0) g_signal_connect(vc->gfx.drawing_area, "draw", G_CALLBACK(gd_draw_event), vc); +#if defined(CONFIG_GTK_GL) + if (display_opengl) { + /* wire up GtkGlArea events */ + g_signal_connect(vc->gfx.drawing_area, "render", + G_CALLBACK(gd_render_event), vc); + g_signal_connect(vc->gfx.drawing_area, "resize", + G_CALLBACK(gd_resize_event), vc); + } +#endif #else g_signal_connect(vc->gfx.drawing_area, "expose-event", G_CALLBACK(gd_expose_event), vc); @@ -1719,26 +1864,13 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, vc->gfx.scale_x = 1.0; vc->gfx.scale_y = 1.0; - vc->gfx.drawing_area = gtk_drawing_area_new(); - gtk_widget_add_events(vc->gfx.drawing_area, - GDK_POINTER_MOTION_MASK | - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_BUTTON_MOTION_MASK | - GDK_ENTER_NOTIFY_MASK | - GDK_LEAVE_NOTIFY_MASK | - GDK_SCROLL_MASK | - GDK_KEY_PRESS_MASK); - gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE); - - vc->type = GD_VC_GFX; - vc->tab_item = vc->gfx.drawing_area; - vc->focus = vc->gfx.drawing_area; - gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), - vc->tab_item, gtk_label_new(vc->label)); - #if defined(CONFIG_OPENGL) if (display_opengl) { +#if defined(CONFIG_GTK_GL) + vc->gfx.drawing_area = gtk_gl_area_new(); + vc->gfx.dcl.ops = &dcl_gl_area_ops; +#else + vc->gfx.drawing_area = gtk_drawing_area_new(); /* * gtk_widget_set_double_buffered() was deprecated in 3.14. * It is required for opengl rendering on X11 though. A @@ -1754,12 +1886,32 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, #pragma GCC diagnostic pop #endif vc->gfx.dcl.ops = &dcl_egl_ops; +#endif /* CONFIG_GTK_GL */ } else #endif { + vc->gfx.drawing_area = gtk_drawing_area_new(); vc->gfx.dcl.ops = &dcl_ops; } + + gtk_widget_add_events(vc->gfx.drawing_area, + GDK_POINTER_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON_MOTION_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_SCROLL_MASK | + GDK_KEY_PRESS_MASK); + gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE); + + vc->type = GD_VC_GFX; + vc->tab_item = vc->gfx.drawing_area; + vc->focus = vc->gfx.drawing_area; + gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), + vc->tab_item, gtk_label_new(vc->label)); + vc->gfx.dcl.con = con; register_displaychangelistener(&vc->gfx.dcl); @@ -1768,6 +1920,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, if (dpy_ui_info_supported(vc->gfx.dcl.con)) { gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_fit_item)); + s->free_scale = true; } return group; @@ -1941,7 +2094,8 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) s->free_scale = FALSE; - setlocale(LC_ALL, ""); + /* LC_MESSAGES only. See early_gtk_display_init() for details */ + setlocale(LC_MESSAGES, ""); bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR); textdomain("qemu"); @@ -2010,6 +2164,24 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) void early_gtk_display_init(int opengl) { + /* The QEMU code relies on the assumption that it's always run in + * the C locale. Therefore it is not prepared to deal with + * operations that produce different results depending on the + * locale, such as printf's formatting of decimal numbers, and + * possibly others. + * + * Since GTK+ calls setlocale() by default -importing the locale + * settings from the environment- we must prevent it from doing so + * using gtk_disable_setlocale(). + * + * QEMU's GTK+ UI, however, _does_ have translations for some of + * the menu items. As a trade-off between a functionally correct + * QEMU and a fully internationalized UI we support importing + * LC_MESSAGES from the environment (see the setlocale() call + * earlier in this file). This allows us to display translated + * messages leaving everything else untouched. + */ + gtk_disable_setlocale(); gtkinit = gtk_init_check(NULL, NULL); if (!gtkinit) { /* don't exit yet, that'll break -help */ @@ -2022,7 +2194,11 @@ void early_gtk_display_init(int opengl) break; case 1: /* on */ #if defined(CONFIG_OPENGL) +#if defined(CONFIG_GTK_GL) + gtk_gl_area_init(); +#else gtk_egl_init(); +#endif #endif break; default: diff --git a/qemu/ui/input-keymap.c b/qemu/ui/input-keymap.c index 7635cb0dc..f1e700d72 100644 --- a/qemu/ui/input-keymap.c +++ b/qemu/ui/input-keymap.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "sysemu/sysemu.h" #include "ui/keymaps.h" #include "ui/input.h" @@ -132,18 +133,18 @@ static const int qcode_to_number[] = { [Q_KEY_CODE_RO] = 0x73, [Q_KEY_CODE_KP_COMMA] = 0x7e, - [Q_KEY_CODE_MAX] = 0, + [Q_KEY_CODE__MAX] = 0, }; static int number_to_qcode[0x100]; int qemu_input_key_value_to_number(const KeyValue *value) { - if (value->kind == KEY_VALUE_KIND_QCODE) { - return qcode_to_number[value->qcode]; + if (value->type == KEY_VALUE_KIND_QCODE) { + return qcode_to_number[value->u.qcode.data]; } else { - assert(value->kind == KEY_VALUE_KIND_NUMBER); - return value->number; + assert(value->type == KEY_VALUE_KIND_NUMBER); + return value->u.number.data; } } @@ -154,7 +155,7 @@ int qemu_input_key_number_to_qcode(uint8_t nr) if (first) { int qcode, number; first = false; - for (qcode = 0; qcode < Q_KEY_CODE_MAX; qcode++) { + for (qcode = 0; qcode < Q_KEY_CODE__MAX; qcode++) { number = qcode_to_number[qcode]; assert(number < ARRAY_SIZE(number_to_qcode)); number_to_qcode[number] = qcode; @@ -166,11 +167,11 @@ int qemu_input_key_number_to_qcode(uint8_t nr) int qemu_input_key_value_to_qcode(const KeyValue *value) { - if (value->kind == KEY_VALUE_KIND_QCODE) { - return value->qcode; + if (value->type == KEY_VALUE_KIND_QCODE) { + return value->u.qcode.data; } else { - assert(value->kind == KEY_VALUE_KIND_NUMBER); - return qemu_input_key_number_to_qcode(value->number); + assert(value->type == KEY_VALUE_KIND_NUMBER); + return qemu_input_key_number_to_qcode(value->u.number.data); } } @@ -180,8 +181,8 @@ int qemu_input_key_value_to_scancode(const KeyValue *value, bool down, int keycode = qemu_input_key_value_to_number(value); int count = 0; - if (value->kind == KEY_VALUE_KIND_QCODE && - value->qcode == Q_KEY_CODE_PAUSE) { + if (value->type == KEY_VALUE_KIND_QCODE && + value->u.qcode.data == Q_KEY_CODE_PAUSE) { /* specific case */ int v = down ? 0 : 0x80; codes[count++] = 0xe1; diff --git a/qemu/ui/input-legacy.c b/qemu/ui/input-legacy.c index e50f2968e..715974740 100644 --- a/qemu/ui/input-legacy.c +++ b/qemu/ui/input-legacy.c @@ -22,9 +22,9 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "sysemu/sysemu.h" #include "ui/console.h" -#include "qapi/error.h" #include "qmp-commands.h" #include "qapi-types.h" #include "ui/keymaps.h" @@ -38,7 +38,7 @@ struct QEMUPutMouseEntry { /* new input core */ QemuInputHandler h; QemuInputHandlerState *s; - int axis[INPUT_AXIS_MAX]; + int axis[INPUT_AXIS__MAX]; int buttons; }; @@ -57,17 +57,18 @@ struct QEMUPutLEDEntry { static QTAILQ_HEAD(, QEMUPutLEDEntry) led_handlers = QTAILQ_HEAD_INITIALIZER(led_handlers); -int index_from_key(const char *key) +int index_from_key(const char *key, size_t key_length) { int i; for (i = 0; QKeyCode_lookup[i] != NULL; i++) { - if (!strcmp(key, QKeyCode_lookup[i])) { + if (!strncmp(key, QKeyCode_lookup[i], key_length) && + !QKeyCode_lookup[i][key_length]) { break; } } - /* Return Q_KEY_CODE_MAX if the key is invalid */ + /* Return Q_KEY_CODE__MAX if the key is invalid */ return i; } @@ -109,12 +110,13 @@ static void legacy_kbd_event(DeviceState *dev, QemuConsole *src, { QEMUPutKbdEntry *entry = (QEMUPutKbdEntry *)dev; int scancodes[3], i, count; + InputKeyEvent *key = evt->u.key.data; if (!entry || !entry->put_kbd) { return; } - count = qemu_input_key_value_to_scancode(evt->key->key, - evt->key->down, + count = qemu_input_key_value_to_scancode(key->key, + key->down, scancodes); for (i = 0; i < count; i++) { entry->put_kbd(entry->opaque, scancodes[i]); @@ -143,28 +145,31 @@ QEMUPutKbdEntry *qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) static void legacy_mouse_event(DeviceState *dev, QemuConsole *src, InputEvent *evt) { - static const int bmap[INPUT_BUTTON_MAX] = { + static const int bmap[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON, [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON, [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON, }; QEMUPutMouseEntry *s = (QEMUPutMouseEntry *)dev; + InputBtnEvent *btn; + InputMoveEvent *move; - switch (evt->kind) { + switch (evt->type) { case INPUT_EVENT_KIND_BTN: - if (evt->btn->down) { - s->buttons |= bmap[evt->btn->button]; + btn = evt->u.btn.data; + if (btn->down) { + s->buttons |= bmap[btn->button]; } else { - s->buttons &= ~bmap[evt->btn->button]; + s->buttons &= ~bmap[btn->button]; } - if (evt->btn->down && evt->btn->button == INPUT_BUTTON_WHEEL_UP) { + if (btn->down && btn->button == INPUT_BUTTON_WHEEL_UP) { s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque, s->axis[INPUT_AXIS_X], s->axis[INPUT_AXIS_Y], -1, s->buttons); } - if (evt->btn->down && evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) { + if (btn->down && btn->button == INPUT_BUTTON_WHEEL_DOWN) { s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque, s->axis[INPUT_AXIS_X], s->axis[INPUT_AXIS_Y], @@ -173,10 +178,12 @@ static void legacy_mouse_event(DeviceState *dev, QemuConsole *src, } break; case INPUT_EVENT_KIND_ABS: - s->axis[evt->abs->axis] = evt->abs->value; + move = evt->u.abs.data; + s->axis[move->axis] = move->value; break; case INPUT_EVENT_KIND_REL: - s->axis[evt->rel->axis] += evt->rel->value; + move = evt->u.rel.data; + s->axis[move->axis] += move->value; break; default: break; @@ -205,7 +212,7 @@ QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, { QEMUPutMouseEntry *s; - s = g_malloc0(sizeof(QEMUPutMouseEntry)); + s = g_new0(QEMUPutMouseEntry, 1); s->qemu_put_mouse_event = func; s->qemu_put_mouse_event_opaque = opaque; @@ -239,7 +246,7 @@ QEMUPutLEDEntry *qemu_add_led_event_handler(QEMUPutLEDEvent *func, { QEMUPutLEDEntry *s; - s = g_malloc0(sizeof(QEMUPutLEDEntry)); + s = g_new0(QEMUPutLEDEntry, 1); s->put_led = func; s->opaque = opaque; diff --git a/qemu/ui/input-linux.c b/qemu/ui/input-linux.c new file mode 100644 index 000000000..1d33b5c12 --- /dev/null +++ b/qemu/ui/input-linux.c @@ -0,0 +1,507 @@ +/* + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "qemu/config-file.h" +#include "qemu/sockets.h" +#include "sysemu/sysemu.h" +#include "ui/input.h" +#include "qom/object_interfaces.h" + +#include +#include "standard-headers/linux/input.h" + +static int linux_to_qcode[KEY_CNT] = { + [KEY_ESC] = Q_KEY_CODE_ESC, + [KEY_1] = Q_KEY_CODE_1, + [KEY_2] = Q_KEY_CODE_2, + [KEY_3] = Q_KEY_CODE_3, + [KEY_4] = Q_KEY_CODE_4, + [KEY_5] = Q_KEY_CODE_5, + [KEY_6] = Q_KEY_CODE_6, + [KEY_7] = Q_KEY_CODE_7, + [KEY_8] = Q_KEY_CODE_8, + [KEY_9] = Q_KEY_CODE_9, + [KEY_0] = Q_KEY_CODE_0, + [KEY_MINUS] = Q_KEY_CODE_MINUS, + [KEY_EQUAL] = Q_KEY_CODE_EQUAL, + [KEY_BACKSPACE] = Q_KEY_CODE_BACKSPACE, + [KEY_TAB] = Q_KEY_CODE_TAB, + [KEY_Q] = Q_KEY_CODE_Q, + [KEY_W] = Q_KEY_CODE_W, + [KEY_E] = Q_KEY_CODE_E, + [KEY_R] = Q_KEY_CODE_R, + [KEY_T] = Q_KEY_CODE_T, + [KEY_Y] = Q_KEY_CODE_Y, + [KEY_U] = Q_KEY_CODE_U, + [KEY_I] = Q_KEY_CODE_I, + [KEY_O] = Q_KEY_CODE_O, + [KEY_P] = Q_KEY_CODE_P, + [KEY_LEFTBRACE] = Q_KEY_CODE_BRACKET_LEFT, + [KEY_RIGHTBRACE] = Q_KEY_CODE_BRACKET_RIGHT, + [KEY_ENTER] = Q_KEY_CODE_RET, + [KEY_LEFTCTRL] = Q_KEY_CODE_CTRL, + [KEY_A] = Q_KEY_CODE_A, + [KEY_S] = Q_KEY_CODE_S, + [KEY_D] = Q_KEY_CODE_D, + [KEY_F] = Q_KEY_CODE_F, + [KEY_G] = Q_KEY_CODE_G, + [KEY_H] = Q_KEY_CODE_H, + [KEY_J] = Q_KEY_CODE_J, + [KEY_K] = Q_KEY_CODE_K, + [KEY_L] = Q_KEY_CODE_L, + [KEY_SEMICOLON] = Q_KEY_CODE_SEMICOLON, + [KEY_APOSTROPHE] = Q_KEY_CODE_APOSTROPHE, + [KEY_GRAVE] = Q_KEY_CODE_GRAVE_ACCENT, + [KEY_LEFTSHIFT] = Q_KEY_CODE_SHIFT, + [KEY_BACKSLASH] = Q_KEY_CODE_BACKSLASH, + [KEY_102ND] = Q_KEY_CODE_LESS, + [KEY_Z] = Q_KEY_CODE_Z, + [KEY_X] = Q_KEY_CODE_X, + [KEY_C] = Q_KEY_CODE_C, + [KEY_V] = Q_KEY_CODE_V, + [KEY_B] = Q_KEY_CODE_B, + [KEY_N] = Q_KEY_CODE_N, + [KEY_M] = Q_KEY_CODE_M, + [KEY_COMMA] = Q_KEY_CODE_COMMA, + [KEY_DOT] = Q_KEY_CODE_DOT, + [KEY_SLASH] = Q_KEY_CODE_SLASH, + [KEY_RIGHTSHIFT] = Q_KEY_CODE_SHIFT_R, + [KEY_LEFTALT] = Q_KEY_CODE_ALT, + [KEY_SPACE] = Q_KEY_CODE_SPC, + [KEY_CAPSLOCK] = Q_KEY_CODE_CAPS_LOCK, + [KEY_F1] = Q_KEY_CODE_F1, + [KEY_F2] = Q_KEY_CODE_F2, + [KEY_F3] = Q_KEY_CODE_F3, + [KEY_F4] = Q_KEY_CODE_F4, + [KEY_F5] = Q_KEY_CODE_F5, + [KEY_F6] = Q_KEY_CODE_F6, + [KEY_F7] = Q_KEY_CODE_F7, + [KEY_F8] = Q_KEY_CODE_F8, + [KEY_F9] = Q_KEY_CODE_F9, + [KEY_F10] = Q_KEY_CODE_F10, + [KEY_NUMLOCK] = Q_KEY_CODE_NUM_LOCK, + [KEY_SCROLLLOCK] = Q_KEY_CODE_SCROLL_LOCK, + [KEY_KP0] = Q_KEY_CODE_KP_0, + [KEY_KP1] = Q_KEY_CODE_KP_1, + [KEY_KP2] = Q_KEY_CODE_KP_2, + [KEY_KP3] = Q_KEY_CODE_KP_3, + [KEY_KP4] = Q_KEY_CODE_KP_4, + [KEY_KP5] = Q_KEY_CODE_KP_5, + [KEY_KP6] = Q_KEY_CODE_KP_6, + [KEY_KP7] = Q_KEY_CODE_KP_7, + [KEY_KP8] = Q_KEY_CODE_KP_8, + [KEY_KP9] = Q_KEY_CODE_KP_9, + [KEY_KPMINUS] = Q_KEY_CODE_KP_SUBTRACT, + [KEY_KPPLUS] = Q_KEY_CODE_KP_ADD, + [KEY_KPDOT] = Q_KEY_CODE_KP_DECIMAL, + [KEY_KPENTER] = Q_KEY_CODE_KP_ENTER, + [KEY_KPSLASH] = Q_KEY_CODE_KP_DIVIDE, + [KEY_KPASTERISK] = Q_KEY_CODE_KP_MULTIPLY, + [KEY_F11] = Q_KEY_CODE_F11, + [KEY_F12] = Q_KEY_CODE_F12, + [KEY_RIGHTCTRL] = Q_KEY_CODE_CTRL_R, + [KEY_SYSRQ] = Q_KEY_CODE_SYSRQ, + [KEY_RIGHTALT] = Q_KEY_CODE_ALT_R, + [KEY_HOME] = Q_KEY_CODE_HOME, + [KEY_UP] = Q_KEY_CODE_UP, + [KEY_PAGEUP] = Q_KEY_CODE_PGUP, + [KEY_LEFT] = Q_KEY_CODE_LEFT, + [KEY_RIGHT] = Q_KEY_CODE_RIGHT, + [KEY_END] = Q_KEY_CODE_END, + [KEY_DOWN] = Q_KEY_CODE_DOWN, + [KEY_PAGEDOWN] = Q_KEY_CODE_PGDN, + [KEY_INSERT] = Q_KEY_CODE_INSERT, + [KEY_DELETE] = Q_KEY_CODE_DELETE, + [KEY_LEFTMETA] = Q_KEY_CODE_META_L, + [KEY_RIGHTMETA] = Q_KEY_CODE_META_R, + [KEY_MENU] = Q_KEY_CODE_MENU, +}; + +static int qemu_input_linux_to_qcode(unsigned int lnx) +{ + assert(lnx < KEY_CNT); + return linux_to_qcode[lnx]; +} + +#define TYPE_INPUT_LINUX "input-linux" +#define INPUT_LINUX(obj) \ + OBJECT_CHECK(InputLinux, (obj), TYPE_INPUT_LINUX) +#define INPUT_LINUX_GET_CLASS(obj) \ + OBJECT_GET_CLASS(InputLinuxClass, (obj), TYPE_INPUT_LINUX) +#define INPUT_LINUX_CLASS(klass) \ + OBJECT_CLASS_CHECK(InputLinuxClass, (klass), TYPE_INPUT_LINUX) + +typedef struct InputLinux InputLinux; +typedef struct InputLinuxClass InputLinuxClass; + +struct InputLinux { + Object parent; + + char *evdev; + int fd; + bool repeat; + bool grab_request; + bool grab_active; + bool grab_all; + bool keydown[KEY_CNT]; + int keycount; + int wheel; + bool initialized; + QTAILQ_ENTRY(InputLinux) next; +}; + +struct InputLinuxClass { + ObjectClass parent_class; +}; + +static QTAILQ_HEAD(, InputLinux) inputs = QTAILQ_HEAD_INITIALIZER(inputs); + +static void input_linux_toggle_grab(InputLinux *il) +{ + intptr_t request = !il->grab_active; + InputLinux *item; + int rc; + + rc = ioctl(il->fd, EVIOCGRAB, request); + if (rc < 0) { + return; + } + il->grab_active = !il->grab_active; + + if (!il->grab_all) { + return; + } + QTAILQ_FOREACH(item, &inputs, next) { + if (item == il || item->grab_all) { + /* avoid endless loops */ + continue; + } + if (item->grab_active != il->grab_active) { + input_linux_toggle_grab(item); + } + } +} + +static void input_linux_event_keyboard(void *opaque) +{ + InputLinux *il = opaque; + struct input_event event; + int rc; + + for (;;) { + rc = read(il->fd, &event, sizeof(event)); + if (rc != sizeof(event)) { + if (rc < 0 && errno != EAGAIN) { + fprintf(stderr, "%s: read: %s\n", __func__, strerror(errno)); + qemu_set_fd_handler(il->fd, NULL, NULL, NULL); + close(il->fd); + } + break; + } + + switch (event.type) { + case EV_KEY: + if (event.value > 2 || (event.value > 1 && !il->repeat)) { + /* + * ignore autorepeat + unknown key events + * 0 == up, 1 == down, 2 == autorepeat, other == undefined + */ + continue; + } + if (event.code >= KEY_CNT) { + /* + * Should not happen. But better safe than sorry, + * and we make Coverity happy too. + */ + continue; + } + /* keep track of key state */ + if (!il->keydown[event.code] && event.value) { + il->keydown[event.code] = true; + il->keycount++; + } + if (il->keydown[event.code] && !event.value) { + il->keydown[event.code] = false; + il->keycount--; + } + + /* send event to guest when grab is active */ + if (il->grab_active) { + int qcode = qemu_input_linux_to_qcode(event.code); + qemu_input_event_send_key_qcode(NULL, qcode, event.value); + } + + /* hotkey -> record switch request ... */ + if (il->keydown[KEY_LEFTCTRL] && + il->keydown[KEY_RIGHTCTRL]) { + il->grab_request = true; + } + + /* + * ... and do the switch when all keys are lifted, so we + * confuse neither guest nor host with keys which seem to + * be stuck due to missing key-up events. + */ + if (il->grab_request && !il->keycount) { + il->grab_request = false; + input_linux_toggle_grab(il); + } + break; + } + } +} + +static void input_linux_event_mouse_button(int button) +{ + qemu_input_queue_btn(NULL, button, true); + qemu_input_event_sync(); + qemu_input_queue_btn(NULL, button, false); + qemu_input_event_sync(); +} + +static void input_linux_event_mouse(void *opaque) +{ + InputLinux *il = opaque; + struct input_event event; + int rc; + + for (;;) { + rc = read(il->fd, &event, sizeof(event)); + if (rc != sizeof(event)) { + if (rc < 0 && errno != EAGAIN) { + fprintf(stderr, "%s: read: %s\n", __func__, strerror(errno)); + qemu_set_fd_handler(il->fd, NULL, NULL, NULL); + close(il->fd); + } + break; + } + + /* only send event to guest when grab is active */ + if (!il->grab_active) { + continue; + } + + switch (event.type) { + case EV_KEY: + switch (event.code) { + case BTN_LEFT: + qemu_input_queue_btn(NULL, INPUT_BUTTON_LEFT, event.value); + break; + case BTN_RIGHT: + qemu_input_queue_btn(NULL, INPUT_BUTTON_RIGHT, event.value); + break; + case BTN_MIDDLE: + qemu_input_queue_btn(NULL, INPUT_BUTTON_MIDDLE, event.value); + break; + case BTN_GEAR_UP: + qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_UP, event.value); + break; + case BTN_GEAR_DOWN: + qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_DOWN, + event.value); + break; + }; + break; + case EV_REL: + switch (event.code) { + case REL_X: + qemu_input_queue_rel(NULL, INPUT_AXIS_X, event.value); + break; + case REL_Y: + qemu_input_queue_rel(NULL, INPUT_AXIS_Y, event.value); + break; + case REL_WHEEL: + il->wheel = event.value; + break; + } + break; + case EV_SYN: + qemu_input_event_sync(); + if (il->wheel != 0) { + input_linux_event_mouse_button((il->wheel > 0) + ? INPUT_BUTTON_WHEEL_UP + : INPUT_BUTTON_WHEEL_DOWN); + il->wheel = 0; + } + break; + } + } +} + +static void input_linux_complete(UserCreatable *uc, Error **errp) +{ + InputLinux *il = INPUT_LINUX(uc); + uint32_t evtmap, relmap, absmap; + int rc, ver; + + if (!il->evdev) { + error_setg(errp, "no input device specified"); + return; + } + + il->fd = open(il->evdev, O_RDWR); + if (il->fd < 0) { + error_setg_file_open(errp, errno, il->evdev); + return; + } + qemu_set_nonblock(il->fd); + + rc = ioctl(il->fd, EVIOCGVERSION, &ver); + if (rc < 0) { + error_setg(errp, "%s: is not an evdev device", il->evdev); + goto err_close; + } + + rc = ioctl(il->fd, EVIOCGBIT(0, sizeof(evtmap)), &evtmap); + if (rc < 0) { + error_setg(errp, "%s: failed to read event bits", il->evdev); + goto err_close; + } + + if (evtmap & (1 << EV_REL)) { + rc = ioctl(il->fd, EVIOCGBIT(EV_REL, sizeof(relmap)), &relmap); + if (rc < 0) { + relmap = 0; + } + } + + if (evtmap & (1 << EV_ABS)) { + ioctl(il->fd, EVIOCGBIT(EV_ABS, sizeof(absmap)), &absmap); + if (rc < 0) { + absmap = 0; + } + } + + if ((evtmap & (1 << EV_REL)) && + (relmap & (1 << REL_X))) { + /* has relative x axis -> assume mouse */ + qemu_set_fd_handler(il->fd, input_linux_event_mouse, NULL, il); + } else if ((evtmap & (1 << EV_ABS)) && + (absmap & (1 << ABS_X))) { + /* has absolute x axis -> not supported */ + error_setg(errp, "tablet/touchscreen not supported"); + goto err_close; + } else if (evtmap & (1 << EV_KEY)) { + /* has keys/buttons (and no x axis) -> assume keyboard */ + qemu_set_fd_handler(il->fd, input_linux_event_keyboard, NULL, il); + } else { + /* Huh? What is this? */ + error_setg(errp, "unknown kind of input device"); + goto err_close; + } + input_linux_toggle_grab(il); + QTAILQ_INSERT_TAIL(&inputs, il, next); + il->initialized = true; + return; + +err_close: + close(il->fd); + return; +} + +static void input_linux_instance_finalize(Object *obj) +{ + InputLinux *il = INPUT_LINUX(obj); + + if (il->initialized) { + QTAILQ_REMOVE(&inputs, il, next); + close(il->fd); + } + g_free(il->evdev); +} + +static char *input_linux_get_evdev(Object *obj, Error **errp) +{ + InputLinux *il = INPUT_LINUX(obj); + + return g_strdup(il->evdev); +} + +static void input_linux_set_evdev(Object *obj, const char *value, + Error **errp) +{ + InputLinux *il = INPUT_LINUX(obj); + + if (il->evdev) { + error_setg(errp, "evdev property already set"); + return; + } + il->evdev = g_strdup(value); +} + +static bool input_linux_get_grab_all(Object *obj, Error **errp) +{ + InputLinux *il = INPUT_LINUX(obj); + + return il->grab_all; +} + +static void input_linux_set_grab_all(Object *obj, bool value, + Error **errp) +{ + InputLinux *il = INPUT_LINUX(obj); + + il->grab_all = value; +} + +static bool input_linux_get_repeat(Object *obj, Error **errp) +{ + InputLinux *il = INPUT_LINUX(obj); + + return il->repeat; +} + +static void input_linux_set_repeat(Object *obj, bool value, + Error **errp) +{ + InputLinux *il = INPUT_LINUX(obj); + + il->repeat = value; +} + +static void input_linux_instance_init(Object *obj) +{ + object_property_add_str(obj, "evdev", + input_linux_get_evdev, + input_linux_set_evdev, NULL); + object_property_add_bool(obj, "grab_all", + input_linux_get_grab_all, + input_linux_set_grab_all, NULL); + object_property_add_bool(obj, "repeat", + input_linux_get_repeat, + input_linux_set_repeat, NULL); +} + +static void input_linux_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = input_linux_complete; +} + +static const TypeInfo input_linux_info = { + .name = TYPE_INPUT_LINUX, + .parent = TYPE_OBJECT, + .class_size = sizeof(InputLinuxClass), + .class_init = input_linux_class_init, + .instance_size = sizeof(InputLinux), + .instance_init = input_linux_instance_init, + .instance_finalize = input_linux_instance_finalize, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +static void register_types(void) +{ + type_register_static(&input_linux_info); +} + +type_init(register_types); diff --git a/qemu/ui/input.c b/qemu/ui/input.c index 1a552d1de..ed88cda6d 100644 --- a/qemu/ui/input.c +++ b/qemu/ui/input.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/qdev.h" #include "sysemu/sysemu.h" #include "qapi-types.h" @@ -6,6 +7,7 @@ #include "trace.h" #include "ui/input.h" #include "ui/console.h" +#include "sysemu/replay.h" struct QemuInputHandlerState { DeviceState *dev; @@ -80,19 +82,12 @@ void qemu_input_handler_bind(QemuInputHandlerState *s, const char *device_id, int head, Error **errp) { - DeviceState *dev; QemuConsole *con; + Error *err = NULL; - dev = qdev_find_recursive(sysbus_get_default(), device_id); - if (dev == NULL) { - error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, - "Device '%s' not found", device_id); - return; - } - - con = qemu_console_lookup_by_device(dev, head); - if (con == NULL) { - error_setg(errp, "Device %s is not bound to a QemuConsole", device_id); + con = qemu_console_lookup_by_device_name(device_id, head, &err); + if (err) { + error_propagate(errp, err); return; } @@ -124,17 +119,22 @@ qemu_input_find_handler(uint32_t mask, QemuConsole *con) return NULL; } -void qmp_x_input_send_event(bool has_console, int64_t console, - InputEventList *events, Error **errp) +void qmp_input_send_event(bool has_device, const char *device, + bool has_head, int64_t head, + InputEventList *events, Error **errp) { InputEventList *e; QemuConsole *con; + Error *err = NULL; con = NULL; - if (has_console) { - con = qemu_console_lookup_by_index(console); - if (!con) { - error_setg(errp, "console %" PRId64 " not found", console); + if (has_device) { + if (!has_head) { + head = 0; + } + con = qemu_console_lookup_by_device_name(device, head, &err); + if (err) { + error_propagate(errp, err); return; } } @@ -147,10 +147,10 @@ void qmp_x_input_send_event(bool has_console, int64_t console, for (e = events; e != NULL; e = e->next) { InputEvent *event = e->value; - if (!qemu_input_find_handler(1 << event->kind, con)) { + if (!qemu_input_find_handler(1 << event->type, con)) { error_setg(errp, "Input handler not found for " "event type %s", - InputEventKind_lookup[event->kind]); + InputEventKind_lookup[event->type]); return; } } @@ -166,24 +166,25 @@ void qmp_x_input_send_event(bool has_console, int64_t console, static void qemu_input_transform_abs_rotate(InputEvent *evt) { + InputMoveEvent *move = evt->u.abs.data; switch (graphic_rotate) { case 90: - if (evt->abs->axis == INPUT_AXIS_X) { - evt->abs->axis = INPUT_AXIS_Y; - } else if (evt->abs->axis == INPUT_AXIS_Y) { - evt->abs->axis = INPUT_AXIS_X; - evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value; + if (move->axis == INPUT_AXIS_X) { + move->axis = INPUT_AXIS_Y; + } else if (move->axis == INPUT_AXIS_Y) { + move->axis = INPUT_AXIS_X; + move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value; } break; case 180: - evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value; + move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value; break; case 270: - if (evt->abs->axis == INPUT_AXIS_X) { - evt->abs->axis = INPUT_AXIS_Y; - evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value; - } else if (evt->abs->axis == INPUT_AXIS_Y) { - evt->abs->axis = INPUT_AXIS_X; + if (move->axis == INPUT_AXIS_X) { + move->axis = INPUT_AXIS_Y; + move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value; + } else if (move->axis == INPUT_AXIS_Y) { + move->axis = INPUT_AXIS_X; } break; } @@ -193,41 +194,48 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt) { const char *name; int qcode, idx = -1; + InputKeyEvent *key; + InputBtnEvent *btn; + InputMoveEvent *move; if (src) { idx = qemu_console_get_index(src); } - switch (evt->kind) { + switch (evt->type) { case INPUT_EVENT_KIND_KEY: - switch (evt->key->key->kind) { + key = evt->u.key.data; + switch (key->key->type) { case KEY_VALUE_KIND_NUMBER: - qcode = qemu_input_key_number_to_qcode(evt->key->key->number); + qcode = qemu_input_key_number_to_qcode(key->key->u.number.data); name = QKeyCode_lookup[qcode]; - trace_input_event_key_number(idx, evt->key->key->number, - name, evt->key->down); + trace_input_event_key_number(idx, key->key->u.number.data, + name, key->down); break; case KEY_VALUE_KIND_QCODE: - name = QKeyCode_lookup[evt->key->key->qcode]; - trace_input_event_key_qcode(idx, name, evt->key->down); + name = QKeyCode_lookup[key->key->u.qcode.data]; + trace_input_event_key_qcode(idx, name, key->down); break; - case KEY_VALUE_KIND_MAX: + case KEY_VALUE_KIND__MAX: /* keep gcc happy */ break; } break; case INPUT_EVENT_KIND_BTN: - name = InputButton_lookup[evt->btn->button]; - trace_input_event_btn(idx, name, evt->btn->down); + btn = evt->u.btn.data; + name = InputButton_lookup[btn->button]; + trace_input_event_btn(idx, name, btn->down); break; case INPUT_EVENT_KIND_REL: - name = InputAxis_lookup[evt->rel->axis]; - trace_input_event_rel(idx, name, evt->rel->value); + move = evt->u.rel.data; + name = InputAxis_lookup[move->axis]; + trace_input_event_rel(idx, name, move->value); break; case INPUT_EVENT_KIND_ABS: - name = InputAxis_lookup[evt->abs->axis]; - trace_input_event_abs(idx, name, evt->abs->value); + move = evt->u.abs.data; + name = InputAxis_lookup[move->axis]; + trace_input_event_abs(idx, name, move->value); break; - case INPUT_EVENT_KIND_MAX: + case INPUT_EVENT_KIND__MAX: /* keep gcc happy */ break; } @@ -300,23 +308,19 @@ static void qemu_input_queue_sync(struct QemuInputEventQueueHead *queue) QTAILQ_INSERT_TAIL(queue, item, node); } -void qemu_input_event_send(QemuConsole *src, InputEvent *evt) +void qemu_input_event_send_impl(QemuConsole *src, InputEvent *evt) { QemuInputHandlerState *s; - if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { - return; - } - qemu_input_event_trace(src, evt); /* pre processing */ - if (graphic_rotate && (evt->kind == INPUT_EVENT_KIND_ABS)) { + if (graphic_rotate && (evt->type == INPUT_EVENT_KIND_ABS)) { qemu_input_transform_abs_rotate(evt); } /* send event */ - s = qemu_input_find_handler(1 << evt->kind, src); + s = qemu_input_find_handler(1 << evt->type, src); if (!s) { return; } @@ -324,14 +328,19 @@ void qemu_input_event_send(QemuConsole *src, InputEvent *evt) s->events++; } -void qemu_input_event_sync(void) +void qemu_input_event_send(QemuConsole *src, InputEvent *evt) { - QemuInputHandlerState *s; - if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { return; } + replay_input_event(src, evt); +} + +void qemu_input_event_sync_impl(void) +{ + QemuInputHandlerState *s; + trace_input_event_sync(); QTAILQ_FOREACH(s, &handlers, node) { @@ -345,13 +354,22 @@ void qemu_input_event_sync(void) } } +void qemu_input_event_sync(void) +{ + if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { + return; + } + + replay_input_sync_event(); +} + InputEvent *qemu_input_event_new_key(KeyValue *key, bool down) { InputEvent *evt = g_new0(InputEvent, 1); - evt->key = g_new0(InputKeyEvent, 1); - evt->kind = INPUT_EVENT_KIND_KEY; - evt->key->key = key; - evt->key->down = down; + evt->u.key.data = g_new0(InputKeyEvent, 1); + evt->type = INPUT_EVENT_KIND_KEY; + evt->u.key.data->key = key; + evt->u.key.data->down = down; return evt; } @@ -372,16 +390,16 @@ void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down) void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down) { KeyValue *key = g_new0(KeyValue, 1); - key->kind = KEY_VALUE_KIND_NUMBER; - key->number = num; + key->type = KEY_VALUE_KIND_NUMBER; + key->u.number.data = num; qemu_input_event_send_key(src, key, down); } void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down) { KeyValue *key = g_new0(KeyValue, 1); - key->kind = KEY_VALUE_KIND_QCODE; - key->qcode = q; + key->type = KEY_VALUE_KIND_QCODE; + key->u.qcode.data = q; qemu_input_event_send_key(src, key, down); } @@ -398,10 +416,10 @@ void qemu_input_event_send_key_delay(uint32_t delay_ms) InputEvent *qemu_input_event_new_btn(InputButton btn, bool down) { InputEvent *evt = g_new0(InputEvent, 1); - evt->btn = g_new0(InputBtnEvent, 1); - evt->kind = INPUT_EVENT_KIND_BTN; - evt->btn->button = btn; - evt->btn->down = down; + evt->u.btn.data = g_new0(InputBtnEvent, 1); + evt->type = INPUT_EVENT_KIND_BTN; + evt->u.btn.data->button = btn; + evt->u.btn.data->down = down; return evt; } @@ -419,7 +437,7 @@ void qemu_input_update_buttons(QemuConsole *src, uint32_t *button_map, InputButton btn; uint32_t mask; - for (btn = 0; btn < INPUT_BUTTON_MAX; btn++) { + for (btn = 0; btn < INPUT_BUTTON__MAX; btn++) { mask = button_map[btn]; if ((button_old & mask) == (button_new & mask)) { continue; @@ -451,8 +469,8 @@ InputEvent *qemu_input_event_new_move(InputEventKind kind, InputEvent *evt = g_new0(InputEvent, 1); InputMoveEvent *move = g_new0(InputMoveEvent, 1); - evt->kind = kind; - evt->data = move; + evt->type = kind; + evt->u.rel.data = move; /* evt->u.rel is the same as evt->u.abs */ move->axis = axis; move->value = value; return evt; diff --git a/qemu/ui/keymaps.c b/qemu/ui/keymaps.c index 49410ae9d..8899a0b31 100644 --- a/qemu/ui/keymaps.c +++ b/qemu/ui/keymaps.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "keymaps.h" #include "sysemu/sysemu.h" @@ -109,7 +110,7 @@ static kbd_layout_t *parse_keyboard_layout(const name2keysym_t *table, } if (!k) { - k = g_malloc0(sizeof(kbd_layout_t)); + k = g_new0(kbd_layout_t, 1); } for(;;) { diff --git a/qemu/ui/qemu-pixman.c b/qemu/ui/qemu-pixman.c index 4116e1507..c9f8dce7f 100644 --- a/qemu/ui/qemu-pixman.c +++ b/qemu/ui/qemu-pixman.c @@ -3,6 +3,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "ui/console.h" diff --git a/qemu/ui/sdl.c b/qemu/ui/sdl.c index 3be29101e..d8cf5bcf7 100644 --- a/qemu/ui/sdl.c +++ b/qemu/ui/sdl.c @@ -25,10 +25,12 @@ /* Avoid compiler warning because macro is redefined in SDL_syswm.h. */ #undef WIN32_LEAN_AND_MEAN +#include "qemu/osdep.h" #include #include #include "qemu-common.h" +#include "qemu/cutils.h" #include "ui/console.h" #include "ui/input.h" #include "sysemu/sysemu.h" @@ -60,6 +62,11 @@ static SDL_Cursor *guest_sprite = NULL; static SDL_PixelFormat host_format; static int scaling_active = 0; static Notifier mouse_mode_notifier; +static int idle_counter; + +#define SDL_REFRESH_INTERVAL_BUSY 10 +#define SDL_MAX_IDLE_COUNT (2 * GUI_REFRESH_INTERVAL_DEFAULT \ + / SDL_REFRESH_INTERVAL_BUSY + 1) #if 0 #define DEBUG_SDL @@ -465,7 +472,7 @@ static void sdl_mouse_mode_change(Notifier *notify, void *data) static void sdl_send_mouse_event(int dx, int dy, int x, int y, int state) { - static uint32_t bmap[INPUT_BUTTON_MAX] = { + static uint32_t bmap[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT), [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE), [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT), @@ -802,6 +809,7 @@ static void handle_activation(SDL_Event *ev) static void sdl_refresh(DisplayChangeListener *dcl) { SDL_Event ev1, *ev = &ev1; + int idle = 1; if (last_vm_running != runstate_is_running()) { last_vm_running = runstate_is_running(); @@ -817,9 +825,11 @@ static void sdl_refresh(DisplayChangeListener *dcl) sdl_update(dcl, 0, 0, real_screen->w, real_screen->h); break; case SDL_KEYDOWN: + idle = 0; handle_keydown(ev); break; case SDL_KEYUP: + idle = 0; handle_keyup(ev); break; case SDL_QUIT: @@ -829,10 +839,12 @@ static void sdl_refresh(DisplayChangeListener *dcl) } break; case SDL_MOUSEMOTION: + idle = 0; handle_mousemotion(ev); break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: + idle = 0; handle_mousebutton(ev); break; case SDL_ACTIVEEVENT: @@ -847,6 +859,18 @@ static void sdl_refresh(DisplayChangeListener *dcl) break; } } + + if (idle) { + if (idle_counter < SDL_MAX_IDLE_COUNT) { + idle_counter++; + if (idle_counter >= SDL_MAX_IDLE_COUNT) { + dcl->update_interval = GUI_REFRESH_INTERVAL_DEFAULT; + } + } + } else { + idle_counter = 0; + dcl->update_interval = SDL_REFRESH_INTERVAL_BUSY; + } } static void sdl_mouse_warp(DisplayChangeListener *dcl, @@ -985,7 +1009,7 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) sdl_grab_start(); } - dcl = g_malloc0(sizeof(DisplayChangeListener)); + dcl = g_new0(DisplayChangeListener, 1); dcl->ops = &dcl_ops; register_displaychangelistener(dcl); diff --git a/qemu/ui/sdl2-2d.c b/qemu/ui/sdl2-2d.c index d0b340f95..95930061e 100644 --- a/qemu/ui/sdl2-2d.c +++ b/qemu/ui/sdl2-2d.c @@ -23,6 +23,7 @@ */ /* Ported SDL 1.2 code to 2.0 by Dave Airlie. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "ui/console.h" #include "ui/input.h" @@ -45,10 +46,23 @@ void sdl2_2d_update(DisplayChangeListener *dcl, return; } + /* + * SDL2 seems to do some double-buffering, and trying to only + * update the changed areas results in only one of the two buffers + * being updated. Which flickers alot. So lets not try to be + * clever do a full update every time ... + */ +#if 0 rect.x = x; rect.y = y; rect.w = w; rect.h = h; +#else + rect.x = 0; + rect.y = 0; + rect.w = surface_width(surf); + rect.h = surface_height(surf); +#endif SDL_UpdateTexture(scon->texture, NULL, surface_data(surf), surface_stride(surf)); diff --git a/qemu/ui/sdl2-gl.c b/qemu/ui/sdl2-gl.c index b604c0671..a324ecaca 100644 --- a/qemu/ui/sdl2-gl.c +++ b/qemu/ui/sdl2-gl.c @@ -25,17 +25,44 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "ui/console.h" #include "ui/input.h" #include "ui/sdl2.h" #include "sysemu/sysemu.h" +#include + +static void sdl2_set_scanout_mode(struct sdl2_console *scon, bool scanout) +{ + if (scon->scanout_mode == scanout) { + return; + } + + scon->scanout_mode = scanout; + if (!scon->scanout_mode) { + if (scon->fbo_id) { + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, 0, 0); + glDeleteFramebuffers(1, &scon->fbo_id); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + scon->fbo_id = 0; + } + if (scon->surface) { + surface_gl_destroy_texture(scon->gls, scon->surface); + surface_gl_create_texture(scon->gls, scon->surface); + } + } +} + static void sdl2_gl_render_surface(struct sdl2_console *scon) { int ww, wh; SDL_GL_MakeCurrent(scon->real_window, scon->winctx); + sdl2_set_scanout_mode(scon, false); SDL_GetWindowSize(scon->real_window, &ww, &wh); surface_gl_setup_viewport(scon->gls, scon->surface, ww, wh); @@ -110,3 +137,112 @@ void sdl2_gl_redraw(struct sdl2_console *scon) sdl2_gl_render_surface(scon); } } + +QEMUGLContext sdl2_gl_create_context(DisplayChangeListener *dcl, + QEMUGLParams *params) +{ + struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); + SDL_GLContext ctx; + + assert(scon->opengl); + + SDL_GL_MakeCurrent(scon->real_window, scon->winctx); + + SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, + SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, params->major_ver); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, params->minor_ver); + + ctx = SDL_GL_CreateContext(scon->real_window); + return (QEMUGLContext)ctx; +} + +void sdl2_gl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx) +{ + SDL_GLContext sdlctx = (SDL_GLContext)ctx; + + SDL_GL_DeleteContext(sdlctx); +} + +int sdl2_gl_make_context_current(DisplayChangeListener *dcl, + QEMUGLContext ctx) +{ + struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); + SDL_GLContext sdlctx = (SDL_GLContext)ctx; + + assert(scon->opengl); + + return SDL_GL_MakeCurrent(scon->real_window, sdlctx); +} + +QEMUGLContext sdl2_gl_get_current_context(DisplayChangeListener *dcl) +{ + SDL_GLContext sdlctx; + + sdlctx = SDL_GL_GetCurrentContext(); + return (QEMUGLContext)sdlctx; +} + +void sdl2_gl_scanout(DisplayChangeListener *dcl, + uint32_t backing_id, bool backing_y_0_top, + uint32_t x, uint32_t y, + uint32_t w, uint32_t h) +{ + struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); + + assert(scon->opengl); + scon->x = x; + scon->y = y; + scon->w = w; + scon->h = h; + scon->tex_id = backing_id; + scon->y0_top = backing_y_0_top; + + SDL_GL_MakeCurrent(scon->real_window, scon->winctx); + + if (scon->tex_id == 0 || scon->w == 0 || scon->h == 0) { + sdl2_set_scanout_mode(scon, false); + return; + } + + sdl2_set_scanout_mode(scon, true); + if (!scon->fbo_id) { + glGenFramebuffers(1, &scon->fbo_id); + } + + glBindFramebuffer(GL_FRAMEBUFFER_EXT, scon->fbo_id); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, scon->tex_id, 0); +} + +void sdl2_gl_scanout_flush(DisplayChangeListener *dcl, + uint32_t x, uint32_t y, uint32_t w, uint32_t h) +{ + struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); + int ww, wh, y1, y2; + + assert(scon->opengl); + if (!scon->scanout_mode) { + return; + } + if (!scon->fbo_id) { + return; + } + + SDL_GL_MakeCurrent(scon->real_window, scon->winctx); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, scon->fbo_id); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + + SDL_GetWindowSize(scon->real_window, &ww, &wh); + glViewport(0, 0, ww, wh); + y1 = scon->y0_top ? 0 : scon->h; + y2 = scon->y0_top ? scon->h : 0; + glBlitFramebuffer(0, y1, scon->w, y2, + 0, 0, ww, wh, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, scon->fbo_id); + + SDL_GL_SwapWindow(scon->real_window); +} diff --git a/qemu/ui/sdl2-input.c b/qemu/ui/sdl2-input.c index ac5dc9476..6e315ae80 100644 --- a/qemu/ui/sdl2-input.c +++ b/qemu/ui/sdl2-input.c @@ -23,6 +23,7 @@ */ /* Ported SDL 1.2 code to 2.0 by Dave Airlie. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "ui/console.h" #include "ui/input.h" diff --git a/qemu/ui/sdl2.c b/qemu/ui/sdl2.c index 5cb75aa36..d0424421e 100644 --- a/qemu/ui/sdl2.c +++ b/qemu/ui/sdl2.c @@ -23,6 +23,7 @@ */ /* Ported SDL 1.2 code to 2.0 by Dave Airlie. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "ui/console.h" #include "ui/input.h" @@ -49,6 +50,10 @@ static int guest_x, guest_y; static SDL_Cursor *guest_sprite; static Notifier mouse_mode_notifier; +#define SDL2_REFRESH_INTERVAL_BUSY 10 +#define SDL2_MAX_IDLE_COUNT (2 * GUI_REFRESH_INTERVAL_DEFAULT \ + / SDL2_REFRESH_INTERVAL_BUSY + 1) + static void sdl_update_caption(struct sdl2_console *scon); static struct sdl2_console *get_scon_from_window(uint32_t window_id) @@ -256,7 +261,7 @@ static void sdl_mouse_mode_change(Notifier *notify, void *data) static void sdl_send_mouse_event(struct sdl2_console *scon, int dx, int dy, int x, int y, int state) { - static uint32_t bmap[INPUT_BUTTON_MAX] = { + static uint32_t bmap[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT), [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE), [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT), @@ -578,6 +583,7 @@ static void handle_windowevent(SDL_Event *ev) void sdl2_poll_events(struct sdl2_console *scon) { SDL_Event ev1, *ev = &ev1; + int idle = 1; if (scon->last_vm_running != runstate_is_running()) { scon->last_vm_running = runstate_is_running(); @@ -587,12 +593,15 @@ void sdl2_poll_events(struct sdl2_console *scon) while (SDL_PollEvent(ev)) { switch (ev->type) { case SDL_KEYDOWN: + idle = 0; handle_keydown(ev); break; case SDL_KEYUP: + idle = 0; handle_keyup(ev); break; case SDL_TEXTINPUT: + idle = 0; handle_textinput(ev); break; case SDL_QUIT: @@ -602,13 +611,16 @@ void sdl2_poll_events(struct sdl2_console *scon) } break; case SDL_MOUSEMOTION: + idle = 0; handle_mousemotion(ev); break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: + idle = 0; handle_mousebutton(ev); break; case SDL_MOUSEWHEEL: + idle = 0; handle_mousewheel(ev); break; case SDL_WINDOWEVENT: @@ -618,6 +630,18 @@ void sdl2_poll_events(struct sdl2_console *scon) break; } } + + if (idle) { + if (scon->idle_counter < SDL2_MAX_IDLE_COUNT) { + scon->idle_counter++; + if (scon->idle_counter >= SDL2_MAX_IDLE_COUNT) { + scon->dcl.update_interval = GUI_REFRESH_INTERVAL_DEFAULT; + } + } + } else { + scon->idle_counter = 0; + scon->dcl.update_interval = SDL2_REFRESH_INTERVAL_BUSY; + } } static void sdl_mouse_warp(DisplayChangeListener *dcl, @@ -700,6 +724,13 @@ static const DisplayChangeListenerOps dcl_gl_ops = { .dpy_refresh = sdl2_gl_refresh, .dpy_mouse_set = sdl_mouse_warp, .dpy_cursor_define = sdl_mouse_define, + + .dpy_gl_ctx_create = sdl2_gl_create_context, + .dpy_gl_ctx_destroy = sdl2_gl_destroy_context, + .dpy_gl_ctx_make_current = sdl2_gl_make_context_current, + .dpy_gl_ctx_get_current = sdl2_gl_get_current_context, + .dpy_gl_scanout = sdl2_gl_scanout, + .dpy_gl_update = sdl2_gl_scanout_flush, }; #endif diff --git a/qemu/ui/sdl_zoom.c b/qemu/ui/sdl_zoom.c index 2625c4557..72622c264 100644 --- a/qemu/ui/sdl_zoom.c +++ b/qemu/ui/sdl_zoom.c @@ -11,11 +11,9 @@ * */ -#include "sdl_zoom.h" #include "qemu/osdep.h" +#include "sdl_zoom.h" #include -#include -#include static void sdl_zoom_rgb16(SDL_Surface *src, SDL_Surface *dst, int smooth, SDL_Rect *dst_rect); diff --git a/qemu/ui/shader.c b/qemu/ui/shader.c index 52a463293..9264009b8 100644 --- a/qemu/ui/shader.c +++ b/qemu/ui/shader.c @@ -24,26 +24,48 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "ui/shader.h" /* ---------------------------------------------------------------------- */ -void qemu_gl_run_texture_blit(GLint texture_blit_prog) +GLuint qemu_gl_init_texture_blit(GLint texture_blit_prog) { - GLfloat in_position[] = { + static const GLfloat in_position[] = { -1, -1, 1, -1, -1, 1, 1, 1, }; GLint l_position; + GLuint vao, buffer; + + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + /* this is the VBO that holds the vertex data */ + glGenBuffers(1, &buffer); + glBindBuffer(GL_ARRAY_BUFFER, buffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(in_position), in_position, + GL_STATIC_DRAW); - glUseProgram(texture_blit_prog); l_position = glGetAttribLocation(texture_blit_prog, "in_position"); - glVertexAttribPointer(l_position, 2, GL_FLOAT, GL_FALSE, 0, in_position); + glVertexAttribPointer(l_position, 2, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(l_position); - glDrawArrays(GL_TRIANGLE_STRIP, l_position, 4); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + return vao; +} + +void qemu_gl_run_texture_blit(GLint texture_blit_prog, + GLint texture_blit_vao) +{ + glUseProgram(texture_blit_prog); + glBindVertexArray(texture_blit_vao); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } /* ---------------------------------------------------------------------- */ diff --git a/qemu/ui/spice-core.c b/qemu/ui/spice-core.c index bf4fd0749..61db3c18b 100644 --- a/qemu/ui/spice-core.c +++ b/qemu/ui/spice-core.c @@ -15,6 +15,7 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include #include @@ -200,8 +201,6 @@ static void channel_event(int event, SpiceChannelEventInfo *info) { SpiceServerInfo *server = g_malloc0(sizeof(*server)); SpiceChannel *client = g_malloc0(sizeof(*client)); - server->base = g_malloc0(sizeof(*server->base)); - client->base = g_malloc0(sizeof(*client->base)); /* * Spice server might have called us from spice worker thread @@ -218,9 +217,11 @@ static void channel_event(int event, SpiceChannelEventInfo *info) } if (info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) { - add_addr_info(client->base, (struct sockaddr *)&info->paddr_ext, + add_addr_info(qapi_SpiceChannel_base(client), + (struct sockaddr *)&info->paddr_ext, info->plen_ext); - add_addr_info(server->base, (struct sockaddr *)&info->laddr_ext, + add_addr_info(qapi_SpiceServerInfo_base(server), + (struct sockaddr *)&info->laddr_ext, info->llen_ext); } else { error_report("spice: %s, extended address is expected", @@ -229,7 +230,9 @@ static void channel_event(int event, SpiceChannelEventInfo *info) switch (event) { case SPICE_CHANNEL_EVENT_CONNECTED: - qapi_event_send_spice_connected(server->base, client->base, &error_abort); + qapi_event_send_spice_connected(qapi_SpiceServerInfo_base(server), + qapi_SpiceChannel_base(client), + &error_abort); break; case SPICE_CHANNEL_EVENT_INITIALIZED: if (auth) { @@ -242,7 +245,9 @@ static void channel_event(int event, SpiceChannelEventInfo *info) break; case SPICE_CHANNEL_EVENT_DISCONNECTED: channel_list_del(info); - qapi_event_send_spice_disconnected(server->base, client->base, &error_abort); + qapi_event_send_spice_disconnected(qapi_SpiceServerInfo_base(server), + qapi_SpiceChannel_base(client), + &error_abort); break; default: break; @@ -378,16 +383,15 @@ static SpiceChannelList *qmp_query_spice_channels(void) chan = g_malloc0(sizeof(*chan)); chan->value = g_malloc0(sizeof(*chan->value)); - chan->value->base = g_malloc0(sizeof(*chan->value->base)); paddr = (struct sockaddr *)&item->info->paddr_ext; plen = item->info->plen_ext; getnameinfo(paddr, plen, host, sizeof(host), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); - chan->value->base->host = g_strdup(host); - chan->value->base->port = g_strdup(port); - chan->value->base->family = inet_netfamily(paddr->sa_family); + chan->value->host = g_strdup(host); + chan->value->port = g_strdup(port); + chan->value->family = inet_netfamily(paddr->sa_family); chan->value->connection_id = item->info->connection_id; chan->value->channel_type = item->info->type; @@ -490,9 +494,14 @@ static QemuOptsList qemu_spice_opts = { },{ .name = "playback-compression", .type = QEMU_OPT_BOOL, - }, { + },{ .name = "seamless-migration", .type = QEMU_OPT_BOOL, +#ifdef HAVE_SPICE_GL + },{ + .name = "gl", + .type = QEMU_OPT_BOOL, +#endif }, { /* end of list */ } }, @@ -564,7 +573,8 @@ static void migration_state_notifier(Notifier *notifier, void *data) if (migration_in_setup(s)) { spice_server_migrate_start(spice_server); - } else if (migration_has_finished(s)) { + } else if (migration_has_finished(s) || + migration_in_postcopy_after_devices(s)) { spice_server_migrate_end(spice_server, true); spice_have_target_host = false; } else if (migration_has_failed(s)) { @@ -724,8 +734,7 @@ void qemu_spice_init(void) qemu_spice_set_passwd(password, false, false); } if (qemu_opt_get_bool(opts, "sasl", 0)) { - if (spice_server_set_sasl_appname(spice_server, "qemu") == -1 || - spice_server_set_sasl(spice_server, 1) == -1) { + if (spice_server_set_sasl(spice_server, 1) == -1) { error_report("spice: failed to enable sasl"); exit(1); } @@ -791,6 +800,7 @@ void qemu_spice_init(void) seamless_migration = qemu_opt_get_bool(opts, "seamless-migration", 0); spice_server_set_seamless_migration(spice_server, seamless_migration); + spice_server_set_sasl_appname(spice_server, "qemu"); if (spice_server_init(spice_server, &core_interface) != 0) { error_report("failed to initialize spice server"); exit(1); @@ -815,6 +825,19 @@ void qemu_spice_init(void) #if SPICE_SERVER_VERSION >= 0x000c02 qemu_spice_register_ports(); #endif + +#ifdef HAVE_SPICE_GL + if (qemu_opt_get_bool(opts, "gl", 0)) { + if ((port != 0) || (tls_port != 0)) { + error_report("SPICE GL support is local-only for now and " + "incompatible with -spice port/tls-port"); + exit(1); + } + if (egl_rendernode_init() == 0) { + display_opengl = 1; + } + } +#endif } int qemu_spice_add_interface(SpiceBaseInstance *sin) @@ -927,4 +950,4 @@ static void spice_register_config(void) { qemu_add_opts(&qemu_spice_opts); } -machine_init(spice_register_config); +opts_init(spice_register_config); diff --git a/qemu/ui/spice-display.c b/qemu/ui/spice-display.c index 0360abfd2..242ab5f46 100644 --- a/qemu/ui/spice-display.c +++ b/qemu/ui/spice-display.c @@ -15,6 +15,7 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "ui/qemu-spice.h" #include "qemu/timer.h" @@ -408,7 +409,8 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd, if (surface && ssd->surface && surface_width(surface) == pixman_image_get_width(ssd->surface) && - surface_height(surface) == pixman_image_get_height(ssd->surface)) { + surface_height(surface) == pixman_image_get_height(ssd->surface) && + surface_format(surface) == pixman_image_get_format(ssd->surface)) { /* no-resize fast path: just swap backing store */ dprint(1, "%s/%d: fast (%dx%d)\n", __func__, ssd->qxl.id, surface_width(surface), surface_height(surface)); @@ -458,6 +460,13 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd, memset(&ssd->dirty, 0, sizeof(ssd->dirty)); ssd->notify++; + + qemu_mutex_lock(&ssd->lock); + if (ssd->cursor) { + g_free(ssd->ptr_define); + ssd->ptr_define = qemu_spice_create_cursor_update(ssd, ssd->cursor, 0); + } + qemu_mutex_unlock(&ssd->lock); } static void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd) @@ -465,8 +474,6 @@ static void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd) if (ssd->cursor) { assert(ssd->dcl.con); dpy_cursor_define(ssd->dcl.con, ssd->cursor); - cursor_put(ssd->cursor); - ssd->cursor = NULL; } if (ssd->mouse_x != -1 && ssd->mouse_y != -1) { assert(ssd->dcl.con); @@ -561,7 +568,7 @@ static int interface_get_command(QXLInstance *sin, QXLCommandExt *ext) static int interface_req_cmd_notification(QXLInstance *sin) { - dprint(1, "%s/%d:\n", __func__, sin->id); + dprint(2, "%s/%d:\n", __func__, sin->id); return 1; } @@ -614,7 +621,7 @@ static int interface_get_cursor_command(QXLInstance *sin, QXLCommandExt *ext) static int interface_req_cursor_notification(QXLInstance *sin) { - dprint(1, "%s:\n", __FUNCTION__); + dprint(2, "%s:\n", __func__); return 1; } @@ -643,9 +650,23 @@ static void interface_update_area_complete(QXLInstance *sin, /* called from spice server thread context only */ static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token) { - /* should never be called, used in qxl native mode only */ - fprintf(stderr, "%s: abort()\n", __func__); - abort(); + QXLCookie *cookie = (QXLCookie *)(uintptr_t)cookie_token; + + switch (cookie->type) { +#ifdef HAVE_SPICE_GL + case QXL_COOKIE_TYPE_GL_DRAW_DONE: + { + SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); + qemu_bh_schedule(ssd->gl_unblock_bh); + break; + } +#endif + default: + /* should never be called, used in qxl native mode only */ + fprintf(stderr, "%s: abort()\n", __func__); + abort(); + } + g_free(cookie); } static void interface_set_client_capabilities(QXLInstance *sin, @@ -737,9 +758,7 @@ static void display_mouse_set(DisplayChangeListener *dcl, qemu_mutex_lock(&ssd->lock); ssd->ptr_x = x; ssd->ptr_y = y; - if (ssd->ptr_move) { - g_free(ssd->ptr_move); - } + g_free(ssd->ptr_move); ssd->ptr_move = qemu_spice_create_cursor_update(ssd, NULL, on); qemu_mutex_unlock(&ssd->lock); } @@ -750,15 +769,16 @@ static void display_mouse_define(DisplayChangeListener *dcl, SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); qemu_mutex_lock(&ssd->lock); + if (c) { + cursor_get(c); + } + cursor_put(ssd->cursor); + ssd->cursor = c; ssd->hot_x = c->hot_x; ssd->hot_y = c->hot_y; - if (ssd->ptr_move) { - g_free(ssd->ptr_move); - ssd->ptr_move = NULL; - } - if (ssd->ptr_define) { - g_free(ssd->ptr_define); - } + g_free(ssd->ptr_move); + ssd->ptr_move = NULL; + g_free(ssd->ptr_define); ssd->ptr_define = qemu_spice_create_cursor_update(ssd, c, 0); qemu_mutex_unlock(&ssd->lock); } @@ -773,20 +793,128 @@ static const DisplayChangeListenerOps display_listener_ops = { .dpy_cursor_define = display_mouse_define, }; +#ifdef HAVE_SPICE_GL + +static void qemu_spice_gl_block(SimpleSpiceDisplay *ssd, bool block) +{ + uint64_t timeout; + + if (block) { + timeout = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + timeout += 1000; /* one sec */ + timer_mod(ssd->gl_unblock_timer, timeout); + } else { + timer_del(ssd->gl_unblock_timer); + } + graphic_hw_gl_block(ssd->dcl.con, block); +} + +static void qemu_spice_gl_unblock_bh(void *opaque) +{ + SimpleSpiceDisplay *ssd = opaque; + + qemu_spice_gl_block(ssd, false); +} + +static void qemu_spice_gl_block_timer(void *opaque) +{ + fprintf(stderr, "WARNING: spice: no gl-draw-done within one second\n"); +} + +static QEMUGLContext qemu_spice_gl_create_context(DisplayChangeListener *dcl, + QEMUGLParams *params) +{ + eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, + qemu_egl_rn_ctx); + return qemu_egl_create_context(dcl, params); +} + +static void qemu_spice_gl_scanout(DisplayChangeListener *dcl, + uint32_t tex_id, + bool y_0_top, + uint32_t x, uint32_t y, + uint32_t w, uint32_t h) +{ + SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); + EGLint stride = 0, fourcc = 0; + int fd = -1; + + if (tex_id) { + fd = egl_get_fd_for_texture(tex_id, &stride, &fourcc); + if (fd < 0) { + fprintf(stderr, "%s: failed to get fd for texture\n", __func__); + return; + } + dprint(1, "%s: %dx%d (stride %d, fourcc 0x%x)\n", __func__, + w, h, stride, fourcc); + } else { + dprint(1, "%s: no texture (no framebuffer)\n", __func__); + } + + assert(!tex_id || fd >= 0); + + /* note: spice server will close the fd */ + spice_qxl_gl_scanout(&ssd->qxl, fd, + surface_width(ssd->ds), + surface_height(ssd->ds), + stride, fourcc, y_0_top); +} + +static void qemu_spice_gl_update(DisplayChangeListener *dcl, + uint32_t x, uint32_t y, uint32_t w, uint32_t h) +{ + SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); + uint64_t cookie; + + dprint(2, "%s: %dx%d+%d+%d\n", __func__, w, h, x, y); + qemu_spice_gl_block(ssd, true); + cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0); + spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie); +} + +static const DisplayChangeListenerOps display_listener_gl_ops = { + .dpy_name = "spice-egl", + .dpy_gfx_update = display_update, + .dpy_gfx_switch = display_switch, + .dpy_gfx_check_format = qemu_pixman_check_format, + .dpy_refresh = display_refresh, + .dpy_mouse_set = display_mouse_set, + .dpy_cursor_define = display_mouse_define, + + .dpy_gl_ctx_create = qemu_spice_gl_create_context, + .dpy_gl_ctx_destroy = qemu_egl_destroy_context, + .dpy_gl_ctx_make_current = qemu_egl_make_context_current, + .dpy_gl_ctx_get_current = qemu_egl_get_current_context, + + .dpy_gl_scanout = qemu_spice_gl_scanout, + .dpy_gl_update = qemu_spice_gl_update, +}; + +#endif /* HAVE_SPICE_GL */ + static void qemu_spice_display_init_one(QemuConsole *con) { SimpleSpiceDisplay *ssd = g_new0(SimpleSpiceDisplay, 1); qemu_spice_display_init_common(ssd); + ssd->dcl.ops = &display_listener_ops; +#ifdef HAVE_SPICE_GL + if (display_opengl) { + ssd->dcl.ops = &display_listener_gl_ops; + ssd->dmabuf_fd = -1; + ssd->gl_unblock_bh = qemu_bh_new(qemu_spice_gl_unblock_bh, ssd); + ssd->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME, + qemu_spice_gl_block_timer, ssd); + } +#endif + ssd->dcl.con = con; + ssd->qxl.base.sif = &dpy_interface.base; qemu_spice_add_display_interface(&ssd->qxl, con); assert(ssd->worker); - qemu_spice_create_host_memslot(ssd); - ssd->dcl.ops = &display_listener_ops; - ssd->dcl.con = con; register_displaychangelistener(&ssd->dcl); } diff --git a/qemu/ui/spice-input.c b/qemu/ui/spice-input.c index c342e0dcf..8eeebdbb2 100644 --- a/qemu/ui/spice-input.c +++ b/qemu/ui/spice-input.c @@ -15,10 +15,7 @@ * along with this program; if not, see . */ -#include -#include -#include -#include +#include "qemu/osdep.h" #include #include @@ -107,7 +104,7 @@ typedef struct QemuSpicePointer { static void spice_update_buttons(QemuSpicePointer *pointer, int wheel, uint32_t button_mask) { - static uint32_t bmap[INPUT_BUTTON_MAX] = { + static uint32_t bmap[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = 0x01, [INPUT_BUTTON_MIDDLE] = 0x04, [INPUT_BUTTON_RIGHT] = 0x02, diff --git a/qemu/ui/vnc-auth-sasl.c b/qemu/ui/vnc-auth-sasl.c index 62a5fc4bf..5ae29c14c 100644 --- a/qemu/ui/vnc-auth-sasl.c +++ b/qemu/ui/vnc-auth-sasl.c @@ -22,6 +22,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "vnc.h" /* Max amount of data we send/recv for SASL steps to prevent DOS */ @@ -62,7 +64,7 @@ long vnc_client_write_sasl(VncState *vs) (const char **)&vs->sasl.encoded, &vs->sasl.encodedLength); if (err != SASL_OK) - return vnc_client_io_error(vs, -1, EIO); + return vnc_client_io_error(vs, -1, NULL); vs->sasl.encodedOffset = 0; } @@ -86,7 +88,11 @@ long vnc_client_write_sasl(VncState *vs) * SASL encoded output */ if (vs->output.offset == 0) { - qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); + if (vs->ioc_tag) { + g_source_remove(vs->ioc_tag); + } + vs->ioc_tag = qio_channel_add_watch( + vs->ioc, G_IO_IN, vnc_client_io, vs, NULL); } return ret; @@ -110,7 +116,7 @@ long vnc_client_read_sasl(VncState *vs) &decoded, &decodedLen); if (err != SASL_OK) - return vnc_client_io_error(vs, -1, -EIO); + return vnc_client_io_error(vs, -1, NULL); VNC_DEBUG("Read SASL Encoded %p size %ld Decoded %p size %d\n", encoded, ret, decoded, decodedLen); buffer_reserve(&vs->input, decodedLen); @@ -255,17 +261,17 @@ static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t le vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4); } else { if (!vnc_auth_sasl_check_ssf(vs)) { - VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock); + VNC_DEBUG("Authentication rejected for weak SSF %p\n", vs->ioc); goto authreject; } /* Check username whitelist ACL */ if (vnc_auth_sasl_check_access(vs) < 0) { - VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock); + VNC_DEBUG("Authentication rejected for ACL %p\n", vs->ioc); goto authreject; } - VNC_DEBUG("Authentication successful %d\n", vs->csock); + VNC_DEBUG("Authentication successful %p\n", vs->ioc); vnc_write_u32(vs, 0); /* Accept auth */ /* * Delay writing in SSF encoded mode until pending output @@ -383,17 +389,17 @@ static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t l vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4); } else { if (!vnc_auth_sasl_check_ssf(vs)) { - VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock); + VNC_DEBUG("Authentication rejected for weak SSF %p\n", vs->ioc); goto authreject; } /* Check username whitelist ACL */ if (vnc_auth_sasl_check_access(vs) < 0) { - VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock); + VNC_DEBUG("Authentication rejected for ACL %p\n", vs->ioc); goto authreject; } - VNC_DEBUG("Authentication successful %d\n", vs->csock); + VNC_DEBUG("Authentication successful %p\n", vs->ioc); vnc_write_u32(vs, 0); /* Accept auth */ start_client_init(vs); } @@ -487,6 +493,33 @@ static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, s return 0; } +static char * +vnc_socket_ip_addr_string(QIOChannelSocket *ioc, + bool local, + Error **errp) +{ + SocketAddress *addr; + char *ret; + + if (local) { + addr = qio_channel_socket_get_local_address(ioc, errp); + } else { + addr = qio_channel_socket_get_remote_address(ioc, errp); + } + if (!addr) { + return NULL; + } + + if (addr->type != SOCKET_ADDRESS_KIND_INET) { + error_setg(errp, "Not an inet socket type"); + return NULL; + } + ret = g_strdup_printf("%s;%s", addr->u.inet.data->host, + addr->u.inet.data->port); + qapi_free_SocketAddress(addr); + return ret; +} + void start_auth_sasl(VncState *vs) { const char *mechlist = NULL; @@ -495,13 +528,16 @@ void start_auth_sasl(VncState *vs) char *localAddr, *remoteAddr; int mechlistlen; - VNC_DEBUG("Initialize SASL auth %d\n", vs->csock); + VNC_DEBUG("Initialize SASL auth %p\n", vs->ioc); /* Get local & remote client addresses in form IPADDR;PORT */ - if (!(localAddr = vnc_socket_local_addr("%s;%s", vs->csock))) + localAddr = vnc_socket_ip_addr_string(vs->sioc, true, NULL); + if (!localAddr) { goto authabort; + } - if (!(remoteAddr = vnc_socket_remote_addr("%s;%s", vs->csock))) { + remoteAddr = vnc_socket_ip_addr_string(vs->sioc, false, NULL); + if (!remoteAddr) { g_free(localAddr); goto authabort; } @@ -525,21 +561,24 @@ void start_auth_sasl(VncState *vs) goto authabort; } -#ifdef CONFIG_VNC_TLS /* Inform SASL that we've got an external SSF layer from TLS/x509 */ if (vs->auth == VNC_AUTH_VENCRYPT && vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) { - gnutls_cipher_algorithm_t cipher; + Error *local_err = NULL; + int keysize; sasl_ssf_t ssf; - cipher = gnutls_cipher_get(vs->tls.session); - if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) { - VNC_DEBUG("%s", "cannot TLS get cipher size\n"); + keysize = qcrypto_tls_session_get_key_size(vs->tls, + &local_err); + if (keysize < 0) { + VNC_DEBUG("cannot TLS get cipher size: %s\n", + error_get_pretty(local_err)); + error_free(local_err); sasl_dispose(&vs->sasl.conn); vs->sasl.conn = NULL; goto authabort; } - ssf *= 8; /* tls key size is bytes, sasl wants bits */ + ssf = keysize * CHAR_BIT; /* tls key size is bytes, sasl wants bits */ err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf); if (err != SASL_OK) { @@ -549,20 +588,19 @@ void start_auth_sasl(VncState *vs) vs->sasl.conn = NULL; goto authabort; } - } else -#endif /* CONFIG_VNC_TLS */ + } else { vs->sasl.wantSSF = 1; + } memset (&secprops, 0, sizeof secprops); - /* Inform SASL that we've got an external SSF layer from TLS */ - if (vs->vd->is_unix -#ifdef CONFIG_VNC_TLS - /* Disable SSF, if using TLS+x509+SASL only. TLS without x509 - is not sufficiently strong */ - || (vs->auth == VNC_AUTH_VENCRYPT && - vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) -#endif /* CONFIG_VNC_TLS */ - ) { + /* Inform SASL that we've got an external SSF layer from TLS. + * + * Disable SSF, if using TLS+x509+SASL only. TLS without x509 + * is not sufficiently strong + */ + if (vs->vd->is_unix || + (vs->auth == VNC_AUTH_VENCRYPT && + vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)) { /* If we've got TLS or UNIX domain sock, we don't care about SSF */ secprops.min_ssf = 0; secprops.max_ssf = 0; diff --git a/qemu/ui/vnc-auth-vencrypt.c b/qemu/ui/vnc-auth-vencrypt.c index 8fc965b4a..11c8c9a81 100644 --- a/qemu/ui/vnc-auth-vencrypt.c +++ b/qemu/ui/vnc-auth-vencrypt.c @@ -24,7 +24,9 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "vnc.h" +#include "qapi/error.h" #include "qemu/main-loop.h" static void start_auth_vencrypt_subauth(VncState *vs) @@ -63,60 +65,23 @@ static void start_auth_vencrypt_subauth(VncState *vs) } } -static void vnc_tls_handshake_io(void *opaque); - -static int vnc_start_vencrypt_handshake(VncState *vs) +static void vnc_tls_handshake_done(Object *source, + Error *err, + gpointer user_data) { - int ret; - - if ((ret = gnutls_handshake(vs->tls.session)) < 0) { - if (!gnutls_error_is_fatal(ret)) { - VNC_DEBUG("Handshake interrupted (blocking)\n"); - if (!gnutls_record_get_direction(vs->tls.session)) - qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs); - else - qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs); - return 0; - } - VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret)); - vnc_client_error(vs); - return -1; - } + VncState *vs = user_data; - if (vs->vd->tls.x509verify) { - if (vnc_tls_validate_certificate(vs) < 0) { - VNC_DEBUG("Client verification failed\n"); - vnc_client_error(vs); - return -1; - } else { - VNC_DEBUG("Client verification passed\n"); - } + if (err) { + VNC_DEBUG("Handshake failed %s\n", + error_get_pretty(err)); + vnc_client_error(vs); + } else { + vs->ioc_tag = qio_channel_add_watch( + vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL); + start_auth_vencrypt_subauth(vs); } - - VNC_DEBUG("Handshake done, switching to TLS data mode\n"); - qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs); - - start_auth_vencrypt_subauth(vs); - - return 0; } -static void vnc_tls_handshake_io(void *opaque) -{ - VncState *vs = (VncState *)opaque; - - VNC_DEBUG("Handshake IO continue\n"); - vnc_start_vencrypt_handshake(vs); -} - - - -#define NEED_X509_AUTH(vs) \ - ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \ - (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \ - (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \ - (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL) - static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len) { @@ -128,20 +93,38 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len vnc_flush(vs); vnc_client_error(vs); } else { + Error *err = NULL; + QIOChannelTLS *tls; VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth); vnc_write_u8(vs, 1); /* Accept auth */ vnc_flush(vs); - if (vnc_tls_client_setup(vs, NEED_X509_AUTH(vs)) < 0) { - VNC_DEBUG("Failed to setup TLS\n"); - return 0; + if (vs->ioc_tag) { + g_source_remove(vs->ioc_tag); + vs->ioc_tag = 0; } - VNC_DEBUG("Start TLS VeNCrypt handshake process\n"); - if (vnc_start_vencrypt_handshake(vs) < 0) { - VNC_DEBUG("Failed to start TLS handshake\n"); + tls = qio_channel_tls_new_server( + vs->ioc, + vs->vd->tlscreds, + vs->vd->tlsaclname, + &err); + if (!tls) { + VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err)); + error_free(err); + vnc_client_error(vs); return 0; } + + VNC_DEBUG("Start TLS VeNCrypt handshake process\n"); + object_unref(OBJECT(vs->ioc)); + vs->ioc = QIO_CHANNEL(tls); + vs->tls = qio_channel_tls_get_session(tls); + + qio_channel_tls_handshake(tls, + vnc_tls_handshake_done, + vs, + NULL); } return 0; } diff --git a/qemu/ui/vnc-enc-hextile.c b/qemu/ui/vnc-enc-hextile.c index 2e768fd89..4215bd7da 100644 --- a/qemu/ui/vnc-enc-hextile.c +++ b/qemu/ui/vnc-enc-hextile.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "vnc.h" static void hextile_enc_cord(uint8_t *ptr, int x, int y, int w, int h) diff --git a/qemu/ui/vnc-enc-tight.c b/qemu/ui/vnc-enc-tight.c index 9a9ddf2e3..e5cba0e5a 100644 --- a/qemu/ui/vnc-enc-tight.c +++ b/qemu/ui/vnc-enc-tight.c @@ -26,7 +26,7 @@ * THE SOFTWARE. */ -#include "config-host.h" +#include "qemu/osdep.h" /* This needs to be before jpeglib.h line because of conflict with INT32 definitions between jmorecfg.h (included by jpeglib.h) and @@ -40,7 +40,6 @@ #include #endif #ifdef CONFIG_VNC_JPEG -#include #include #endif diff --git a/qemu/ui/vnc-enc-zlib.c b/qemu/ui/vnc-enc-zlib.c index d1b97f251..33e9df2f6 100644 --- a/qemu/ui/vnc-enc-zlib.c +++ b/qemu/ui/vnc-enc-zlib.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "vnc.h" #define ZALLOC_ALIGNMENT 16 diff --git a/qemu/ui/vnc-enc-zrle-template.c b/qemu/ui/vnc-enc-zrle-template.c index 70ae624ee..abf6b86e4 100644 --- a/qemu/ui/vnc-enc-zrle-template.c +++ b/qemu/ui/vnc-enc-zrle-template.c @@ -22,7 +22,7 @@ */ -#include +#include "qemu/osdep.h" #undef ZRLE_ENDIAN_SUFFIX diff --git a/qemu/ui/vnc-enc-zrle.c b/qemu/ui/vnc-enc-zrle.c index ed3b48465..5489870e7 100644 --- a/qemu/ui/vnc-enc-zrle.c +++ b/qemu/ui/vnc-enc-zrle.c @@ -26,6 +26,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "vnc.h" #include "vnc-enc-zrle.h" diff --git a/qemu/ui/vnc-enc-zywrle-template.c b/qemu/ui/vnc-enc-zywrle-template.c index 561f7bfab..b446380a7 100644 --- a/qemu/ui/vnc-enc-zywrle-template.c +++ b/qemu/ui/vnc-enc-zywrle-template.c @@ -100,6 +100,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endif #define ZYWRLE_QUANTIZE +#include "qemu/osdep.h" #include "vnc-enc-zywrle.h" #ifndef ZRLE_COMPACT_PIXEL diff --git a/qemu/ui/vnc-jobs.c b/qemu/ui/vnc-jobs.c index 22c9abce5..98ca978b0 100644 --- a/qemu/ui/vnc-jobs.c +++ b/qemu/ui/vnc-jobs.c @@ -26,9 +26,11 @@ */ +#include "qemu/osdep.h" #include "vnc.h" #include "vnc-jobs.h" #include "qemu/sockets.h" +#include "qemu/main-loop.h" #include "block/aio.h" /* @@ -54,7 +56,6 @@ struct VncJobQueue { QemuCond cond; QemuMutex mutex; QemuThread thread; - Buffer buffer; bool exit; QTAILQ_HEAD(, VncJob) jobs; }; @@ -79,7 +80,7 @@ static void vnc_unlock_queue(VncJobQueue *queue) VncJob *vnc_job_new(VncState *vs) { - VncJob *job = g_malloc0(sizeof(VncJob)); + VncJob *job = g_new0(VncJob, 1); job->vs = vs; vnc_lock_queue(queue); @@ -90,7 +91,7 @@ VncJob *vnc_job_new(VncState *vs) int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h) { - VncRectEntry *entry = g_malloc0(sizeof(VncRectEntry)); + VncRectEntry *entry = g_new0(VncRectEntry, 1); entry->rect.x = x; entry->rect.y = y; @@ -166,10 +167,16 @@ void vnc_jobs_consume_buffer(VncState *vs) vnc_lock_output(vs); if (vs->jobs_buffer.offset) { - vnc_write(vs, vs->jobs_buffer.buffer, vs->jobs_buffer.offset); - buffer_reset(&vs->jobs_buffer); + if (vs->ioc != NULL && buffer_empty(&vs->output)) { + if (vs->ioc_tag) { + g_source_remove(vs->ioc_tag); + } + vs->ioc_tag = qio_channel_add_watch( + vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL); + } + buffer_move(&vs->output, &vs->jobs_buffer); } - flush = vs->csock != -1 && vs->abort != true; + flush = vs->ioc != NULL && vs->abort != true; vnc_unlock_output(vs); if (flush) { @@ -182,6 +189,10 @@ void vnc_jobs_consume_buffer(VncState *vs) */ static void vnc_async_encoding_start(VncState *orig, VncState *local) { + buffer_init(&local->output, "vnc-worker-output"); + local->sioc = NULL; /* Don't do any network work on this thread */ + local->ioc = NULL; /* Don't do any network work on this thread */ + local->vnc_encoding = orig->vnc_encoding; local->features = orig->features; local->vd = orig->vd; @@ -193,10 +204,6 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local) local->zlib = orig->zlib; local->hextile = orig->hextile; local->zrle = orig->zrle; - local->output = queue->buffer; - local->csock = -1; /* Don't do any network work on this thread */ - - buffer_reset(&local->output); } static void vnc_async_encoding_end(VncState *orig, VncState *local) @@ -206,15 +213,13 @@ static void vnc_async_encoding_end(VncState *orig, VncState *local) orig->hextile = local->hextile; orig->zrle = local->zrle; orig->lossy_rect = local->lossy_rect; - - queue->buffer = local->output; } static int vnc_worker_thread_loop(VncJobQueue *queue) { VncJob *job; VncRectEntry *entry, *tmp; - VncState vs; + VncState vs = {}; int n_rectangles; int saved_offset; @@ -231,10 +236,18 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) } vnc_lock_output(job->vs); - if (job->vs->csock == -1 || job->vs->abort == true) { + if (job->vs->ioc == NULL || job->vs->abort == true) { vnc_unlock_output(job->vs); goto disconnected; } + if (buffer_empty(&job->vs->output)) { + /* + * Looks like a NOP as it obviously moves no data. But it + * moves the empty buffer, so we don't have to malloc a new + * one for vs.output + */ + buffer_move_empty(&vs.output, &job->vs->output); + } vnc_unlock_output(job->vs); /* Make a local copy of vs and switch output buffers */ @@ -251,7 +264,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) QLIST_FOREACH_SAFE(entry, &job->rectangles, next, tmp) { int n; - if (job->vs->csock == -1) { + if (job->vs->ioc == NULL) { vnc_unlock_display(job->vs->vd); /* Copy persistent encoding data */ vnc_async_encoding_end(job->vs, &vs); @@ -273,15 +286,14 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) vs.output.buffer[saved_offset + 1] = n_rectangles & 0xFF; vnc_lock_output(job->vs); - if (job->vs->csock != -1) { - buffer_reserve(&job->vs->jobs_buffer, vs.output.offset); - buffer_append(&job->vs->jobs_buffer, vs.output.buffer, - vs.output.offset); + if (job->vs->ioc != NULL) { + buffer_move(&job->vs->jobs_buffer, &vs.output); /* Copy persistent encoding data */ vnc_async_encoding_end(job->vs, &vs); qemu_bh_schedule(job->vs->bh); } else { + buffer_reset(&vs.output); /* Copy persistent encoding data */ vnc_async_encoding_end(job->vs, &vs); } @@ -298,7 +310,7 @@ disconnected: static VncJobQueue *vnc_queue_init(void) { - VncJobQueue *queue = g_malloc0(sizeof(VncJobQueue)); + VncJobQueue *queue = g_new0(VncJobQueue, 1); qemu_cond_init(&queue->cond); qemu_mutex_init(&queue->mutex); @@ -310,7 +322,6 @@ static void vnc_queue_clear(VncJobQueue *q) { qemu_cond_destroy(&queue->cond); qemu_mutex_destroy(&queue->mutex); - buffer_free(&queue->buffer); g_free(q); queue = NULL; /* Unset global queue */ } diff --git a/qemu/ui/vnc-palette.c b/qemu/ui/vnc-palette.c index c130deee9..3b89d1af2 100644 --- a/qemu/ui/vnc-palette.c +++ b/qemu/ui/vnc-palette.c @@ -26,9 +26,9 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "vnc-palette.h" #include -#include static VncPaletteEntry *palette_find(const VncPalette *palette, uint32_t color, unsigned int hash) diff --git a/qemu/ui/vnc-palette.h b/qemu/ui/vnc-palette.h index d02f0236c..1bd4318f5 100644 --- a/qemu/ui/vnc-palette.h +++ b/qemu/ui/vnc-palette.h @@ -31,8 +31,6 @@ #include "qapi/qmp/qlist.h" #include "qemu/queue.h" -#include -#include #define VNC_PALETTE_HASH_SIZE 256 #define VNC_PALETTE_MAX_SIZE 256 diff --git a/qemu/ui/vnc-tls.c b/qemu/ui/vnc-tls.c deleted file mode 100644 index 028fc4db1..000000000 --- a/qemu/ui/vnc-tls.c +++ /dev/null @@ -1,474 +0,0 @@ -/* - * QEMU VNC display driver: TLS helpers - * - * Copyright (C) 2006 Anthony Liguori - * Copyright (C) 2006 Fabrice Bellard - * Copyright (C) 2009 Red Hat, Inc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu-x509.h" -#include "vnc.h" -#include "qemu/sockets.h" - -#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 -/* Very verbose, so only enabled for _VNC_DEBUG >= 2 */ -static void vnc_debug_gnutls_log(int level, const char* str) { - VNC_DEBUG("%d %s", level, str); -} -#endif /* defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 */ - - -#define DH_BITS 1024 -static gnutls_dh_params_t dh_params; - -static int vnc_tls_initialize(void) -{ - static int tlsinitialized = 0; - - if (tlsinitialized) - return 1; - - if (gnutls_global_init () < 0) - return 0; - - /* XXX ought to re-generate diffie-hellman params periodically */ - if (gnutls_dh_params_init (&dh_params) < 0) - return 0; - if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0) - return 0; - -#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 - gnutls_global_set_log_level(10); - gnutls_global_set_log_function(vnc_debug_gnutls_log); -#endif - - tlsinitialized = 1; - - return 1; -} - -static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport, - const void *data, - size_t len) { - VncState *vs = (VncState *)transport; - int ret; - - retry: - ret = send(vs->csock, data, len, 0); - if (ret < 0) { - if (errno == EINTR) - goto retry; - return -1; - } - return ret; -} - - -static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport, - void *data, - size_t len) { - VncState *vs = (VncState *)transport; - int ret; - - retry: - ret = qemu_recv(vs->csock, data, len, 0); - if (ret < 0) { - if (errno == EINTR) - goto retry; - return -1; - } - return ret; -} - - -static gnutls_anon_server_credentials_t vnc_tls_initialize_anon_cred(void) -{ - gnutls_anon_server_credentials_t anon_cred; - int ret; - - if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) { - VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret)); - return NULL; - } - - gnutls_anon_set_server_dh_params(anon_cred, dh_params); - - return anon_cred; -} - - -static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncDisplay *vd) -{ - gnutls_certificate_credentials_t x509_cred; - int ret; - - if (!vd->tls.x509cacert) { - VNC_DEBUG("No CA x509 certificate specified\n"); - return NULL; - } - if (!vd->tls.x509cert) { - VNC_DEBUG("No server x509 certificate specified\n"); - return NULL; - } - if (!vd->tls.x509key) { - VNC_DEBUG("No server private key specified\n"); - return NULL; - } - - if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) { - VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret)); - return NULL; - } - if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred, - vd->tls.x509cacert, - GNUTLS_X509_FMT_PEM)) < 0) { - VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret)); - gnutls_certificate_free_credentials(x509_cred); - return NULL; - } - - if ((ret = gnutls_certificate_set_x509_key_file (x509_cred, - vd->tls.x509cert, - vd->tls.x509key, - GNUTLS_X509_FMT_PEM)) < 0) { - VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret)); - gnutls_certificate_free_credentials(x509_cred); - return NULL; - } - - if (vd->tls.x509cacrl) { - if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred, - vd->tls.x509cacrl, - GNUTLS_X509_FMT_PEM)) < 0) { - VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret)); - gnutls_certificate_free_credentials(x509_cred); - return NULL; - } - } - - gnutls_certificate_set_dh_params (x509_cred, dh_params); - - return x509_cred; -} - - -int vnc_tls_validate_certificate(VncState *vs) -{ - int ret; - unsigned int status; - const gnutls_datum_t *certs; - unsigned int nCerts, i; - time_t now; - - VNC_DEBUG("Validating client certificate\n"); - if ((ret = gnutls_certificate_verify_peers2 (vs->tls.session, &status)) < 0) { - VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret)); - return -1; - } - - if ((now = time(NULL)) == ((time_t)-1)) { - return -1; - } - - if (status != 0) { - if (status & GNUTLS_CERT_INVALID) - VNC_DEBUG("The certificate is not trusted.\n"); - - if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) - VNC_DEBUG("The certificate hasn't got a known issuer.\n"); - - if (status & GNUTLS_CERT_REVOKED) - VNC_DEBUG("The certificate has been revoked.\n"); - - if (status & GNUTLS_CERT_INSECURE_ALGORITHM) - VNC_DEBUG("The certificate uses an insecure algorithm\n"); - - return -1; - } else { - VNC_DEBUG("Certificate is valid!\n"); - } - - /* Only support x509 for now */ - if (gnutls_certificate_type_get(vs->tls.session) != GNUTLS_CRT_X509) - return -1; - - if (!(certs = gnutls_certificate_get_peers(vs->tls.session, &nCerts))) - return -1; - - for (i = 0 ; i < nCerts ; i++) { - gnutls_x509_crt_t cert; - VNC_DEBUG ("Checking certificate chain %d\n", i); - if (gnutls_x509_crt_init (&cert) < 0) - return -1; - - if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) { - gnutls_x509_crt_deinit (cert); - return -1; - } - - if (gnutls_x509_crt_get_expiration_time (cert) < now) { - VNC_DEBUG("The certificate has expired\n"); - gnutls_x509_crt_deinit (cert); - return -1; - } - - if (gnutls_x509_crt_get_activation_time (cert) > now) { - VNC_DEBUG("The certificate is not yet activated\n"); - gnutls_x509_crt_deinit (cert); - return -1; - } - - if (gnutls_x509_crt_get_activation_time (cert) > now) { - VNC_DEBUG("The certificate is not yet activated\n"); - gnutls_x509_crt_deinit (cert); - return -1; - } - - if (i == 0) { - size_t dnameSize = 1024; - vs->tls.dname = g_malloc(dnameSize); - requery: - if ((ret = gnutls_x509_crt_get_dn (cert, vs->tls.dname, &dnameSize)) != 0) { - if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { - vs->tls.dname = g_realloc(vs->tls.dname, dnameSize); - goto requery; - } - gnutls_x509_crt_deinit (cert); - VNC_DEBUG("Cannot get client distinguished name: %s", - gnutls_strerror (ret)); - return -1; - } - - if (vs->vd->tls.x509verify) { - int allow; - if (!vs->vd->tls.acl) { - VNC_DEBUG("no ACL activated, allowing access"); - gnutls_x509_crt_deinit (cert); - continue; - } - - allow = qemu_acl_party_is_allowed(vs->vd->tls.acl, - vs->tls.dname); - - VNC_DEBUG("TLS x509 ACL check for %s is %s\n", - vs->tls.dname, allow ? "allowed" : "denied"); - if (!allow) { - gnutls_x509_crt_deinit (cert); - return -1; - } - } - } - - gnutls_x509_crt_deinit (cert); - } - - return 0; -} - -#if defined(GNUTLS_VERSION_NUMBER) && \ - GNUTLS_VERSION_NUMBER >= 0x020200 /* 2.2.0 */ - -static int vnc_set_gnutls_priority(gnutls_session_t s, int x509) -{ - const char *priority = x509 ? "NORMAL" : "NORMAL:+ANON-DH"; - int rc; - - rc = gnutls_priority_set_direct(s, priority, NULL); - if (rc != GNUTLS_E_SUCCESS) { - return -1; - } - return 0; -} - -#else - -static int vnc_set_gnutls_priority(gnutls_session_t s, int x509) -{ - static const int cert_types[] = { GNUTLS_CRT_X509, 0 }; - static const int protocols[] = { - GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 - }; - static const int kx_anon[] = { GNUTLS_KX_ANON_DH, 0 }; - static const int kx_x509[] = { - GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, - GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0 - }; - int rc; - - rc = gnutls_kx_set_priority(s, x509 ? kx_x509 : kx_anon); - if (rc != GNUTLS_E_SUCCESS) { - return -1; - } - - rc = gnutls_certificate_type_set_priority(s, cert_types); - if (rc != GNUTLS_E_SUCCESS) { - return -1; - } - - rc = gnutls_protocol_set_priority(s, protocols); - if (rc != GNUTLS_E_SUCCESS) { - return -1; - } - return 0; -} - -#endif - -int vnc_tls_client_setup(VncState *vs, - int needX509Creds) { - VNC_DEBUG("Do TLS setup\n"); - if (vnc_tls_initialize() < 0) { - VNC_DEBUG("Failed to init TLS\n"); - vnc_client_error(vs); - return -1; - } - if (vs->tls.session == NULL) { - if (gnutls_init(&vs->tls.session, GNUTLS_SERVER) < 0) { - vnc_client_error(vs); - return -1; - } - - if (gnutls_set_default_priority(vs->tls.session) < 0) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - vnc_client_error(vs); - return -1; - } - - if (vnc_set_gnutls_priority(vs->tls.session, needX509Creds) < 0) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - vnc_client_error(vs); - return -1; - } - - if (needX509Creds) { - gnutls_certificate_server_credentials x509_cred = - vnc_tls_initialize_x509_cred(vs->vd); - if (!x509_cred) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - vnc_client_error(vs); - return -1; - } - if (gnutls_credentials_set(vs->tls.session, - GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - gnutls_certificate_free_credentials(x509_cred); - vnc_client_error(vs); - return -1; - } - if (vs->vd->tls.x509verify) { - VNC_DEBUG("Requesting a client certificate\n"); - gnutls_certificate_server_set_request(vs->tls.session, - GNUTLS_CERT_REQUEST); - } - - } else { - gnutls_anon_server_credentials_t anon_cred = - vnc_tls_initialize_anon_cred(); - if (!anon_cred) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - vnc_client_error(vs); - return -1; - } - if (gnutls_credentials_set(vs->tls.session, - GNUTLS_CRD_ANON, anon_cred) < 0) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - gnutls_anon_free_server_credentials(anon_cred); - vnc_client_error(vs); - return -1; - } - } - - gnutls_transport_set_ptr(vs->tls.session, (gnutls_transport_ptr_t)vs); - gnutls_transport_set_push_function(vs->tls.session, vnc_tls_push); - gnutls_transport_set_pull_function(vs->tls.session, vnc_tls_pull); - } - return 0; -} - - -void vnc_tls_client_cleanup(VncState *vs) -{ - if (vs->tls.session) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - } - g_free(vs->tls.dname); -} - - - -static int vnc_set_x509_credential(VncDisplay *vd, - const char *certdir, - const char *filename, - char **cred, - int ignoreMissing) -{ - struct stat sb; - - g_free(*cred); - *cred = g_malloc(strlen(certdir) + strlen(filename) + 2); - - strcpy(*cred, certdir); - strcat(*cred, "/"); - strcat(*cred, filename); - - VNC_DEBUG("Check %s\n", *cred); - if (stat(*cred, &sb) < 0) { - g_free(*cred); - *cred = NULL; - if (ignoreMissing && errno == ENOENT) - return 0; - return -1; - } - - return 0; -} - - -int vnc_tls_set_x509_creds_dir(VncDisplay *vd, - const char *certdir) -{ - if (vnc_set_x509_credential(vd, certdir, X509_CA_CERT_FILE, &vd->tls.x509cacert, 0) < 0) - goto cleanup; - if (vnc_set_x509_credential(vd, certdir, X509_CA_CRL_FILE, &vd->tls.x509cacrl, 1) < 0) - goto cleanup; - if (vnc_set_x509_credential(vd, certdir, X509_SERVER_CERT_FILE, &vd->tls.x509cert, 0) < 0) - goto cleanup; - if (vnc_set_x509_credential(vd, certdir, X509_SERVER_KEY_FILE, &vd->tls.x509key, 0) < 0) - goto cleanup; - - return 0; - - cleanup: - g_free(vd->tls.x509cacert); - g_free(vd->tls.x509cacrl); - g_free(vd->tls.x509cert); - g_free(vd->tls.x509key); - vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL; - return -1; -} - diff --git a/qemu/ui/vnc-tls.h b/qemu/ui/vnc-tls.h deleted file mode 100644 index f9829c782..000000000 --- a/qemu/ui/vnc-tls.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * QEMU VNC display driver. TLS helpers - * - * Copyright (C) 2006 Anthony Liguori - * Copyright (C) 2006 Fabrice Bellard - * Copyright (C) 2009 Red Hat, Inc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - - -#ifndef __QEMU_VNC_TLS_H__ -#define __QEMU_VNC_TLS_H__ - -#include -#include - -#include "qemu/acl.h" - -typedef struct VncDisplayTLS VncDisplayTLS; -typedef struct VncStateTLS VncStateTLS; - -/* Server state */ -struct VncDisplayTLS { - int x509verify; /* Non-zero if server requests & validates client cert */ - qemu_acl *acl; - - /* Paths to x509 certs/keys */ - char *x509cacert; - char *x509cacrl; - char *x509cert; - char *x509key; -}; - -/* Per client state */ -struct VncStateTLS { - gnutls_session_t session; - - /* Client's Distinguished Name from the x509 cert */ - char *dname; -}; - -int vnc_tls_client_setup(VncState *vs, int x509Creds); -void vnc_tls_client_cleanup(VncState *vs); - -int vnc_tls_validate_certificate(VncState *vs); - -int vnc_tls_set_x509_creds_dir(VncDisplay *vd, - const char *path); - - -#endif /* __QEMU_VNC_TLS_H__ */ - diff --git a/qemu/ui/vnc-ws.c b/qemu/ui/vnc-ws.c index b4cb6bde7..7c79a4c37 100644 --- a/qemu/ui/vnc-ws.c +++ b/qemu/ui/vnc-ws.c @@ -18,364 +18,108 @@ * along with this software; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "vnc.h" -#include "qemu/main-loop.h" -#include "crypto/hash.h" +#include "io/channel-websock.h" -#ifdef CONFIG_VNC_TLS -#include "qemu/sockets.h" - -static int vncws_start_tls_handshake(VncState *vs) +static void vncws_tls_handshake_done(Object *source, + Error *err, + gpointer user_data) { - int ret = gnutls_handshake(vs->tls.session); + VncState *vs = user_data; - if (ret < 0) { - if (!gnutls_error_is_fatal(ret)) { - VNC_DEBUG("Handshake interrupted (blocking)\n"); - if (!gnutls_record_get_direction(vs->tls.session)) { - qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, - NULL, vs); - } else { - qemu_set_fd_handler(vs->csock, NULL, vncws_tls_handshake_io, - vs); - } - return 0; - } - VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret)); + if (err) { + VNC_DEBUG("Handshake failed %s\n", error_get_pretty(err)); vnc_client_error(vs); - return -1; - } - - if (vs->vd->tls.x509verify) { - if (vnc_tls_validate_certificate(vs) < 0) { - VNC_DEBUG("Client verification failed\n"); - vnc_client_error(vs); - return -1; - } else { - VNC_DEBUG("Client verification passed\n"); - } + } else { + VNC_DEBUG("TLS handshake complete, starting websocket handshake\n"); + vs->ioc_tag = qio_channel_add_watch( + QIO_CHANNEL(vs->ioc), G_IO_IN, vncws_handshake_io, vs, NULL); } - - VNC_DEBUG("Handshake done, switching to TLS data mode\n"); - qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs); - - return 0; } -void vncws_tls_handshake_io(void *opaque) -{ - VncState *vs = (VncState *)opaque; - if (!vs->tls.session) { - VNC_DEBUG("TLS Websocket setup\n"); - if (vnc_tls_client_setup(vs, vs->vd->tls.x509cert != NULL) < 0) { - return; - } - } - VNC_DEBUG("Handshake IO continue\n"); - vncws_start_tls_handshake(vs); -} -#endif /* CONFIG_VNC_TLS */ - -void vncws_handshake_read(void *opaque) +gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED, + GIOCondition condition G_GNUC_UNUSED, + void *opaque) { VncState *vs = opaque; - uint8_t *handshake_end; - long ret; - /* Typical HTTP headers from novnc are 512 bytes, so limiting - * total header size to 4096 is easily enough. */ - size_t want = 4096 - vs->ws_input.offset; - buffer_reserve(&vs->ws_input, want); - ret = vnc_client_read_buf(vs, buffer_end(&vs->ws_input), want); + QIOChannelTLS *tls; + Error *err = NULL; - if (!ret) { - if (vs->csock == -1) { - vnc_disconnect_finish(vs); - } - return; + VNC_DEBUG("TLS Websocket connection required\n"); + if (vs->ioc_tag) { + g_source_remove(vs->ioc_tag); + vs->ioc_tag = 0; } - vs->ws_input.offset += ret; - handshake_end = (uint8_t *)g_strstr_len((char *)vs->ws_input.buffer, - vs->ws_input.offset, WS_HANDSHAKE_END); - if (handshake_end) { - qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); - vncws_process_handshake(vs, vs->ws_input.buffer, vs->ws_input.offset); - buffer_advance(&vs->ws_input, handshake_end - vs->ws_input.buffer + - strlen(WS_HANDSHAKE_END)); - } else if (vs->ws_input.offset >= 4096) { - VNC_DEBUG("End of headers not found in first 4096 bytes\n"); + tls = qio_channel_tls_new_server( + vs->ioc, + vs->vd->tlscreds, + vs->vd->tlsaclname, + &err); + if (!tls) { + VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err)); + error_free(err); vnc_client_error(vs); + return TRUE; } -} - - -long vnc_client_read_ws(VncState *vs) -{ - int ret, err; - uint8_t *payload; - size_t payload_size, header_size; - VNC_DEBUG("Read websocket %p size %zd offset %zd\n", vs->ws_input.buffer, - vs->ws_input.capacity, vs->ws_input.offset); - buffer_reserve(&vs->ws_input, 4096); - ret = vnc_client_read_buf(vs, buffer_end(&vs->ws_input), 4096); - if (!ret) { - return 0; - } - vs->ws_input.offset += ret; - ret = 0; - /* consume as much of ws_input buffer as possible */ - do { - if (vs->ws_payload_remain == 0) { - err = vncws_decode_frame_header(&vs->ws_input, - &header_size, - &vs->ws_payload_remain, - &vs->ws_payload_mask); - if (err <= 0) { - return err; - } + VNC_DEBUG("Start TLS WS handshake process\n"); + object_unref(OBJECT(vs->ioc)); + vs->ioc = QIO_CHANNEL(tls); + vs->tls = qio_channel_tls_get_session(tls); - buffer_advance(&vs->ws_input, header_size); - } - if (vs->ws_payload_remain != 0) { - err = vncws_decode_frame_payload(&vs->ws_input, - &vs->ws_payload_remain, - &vs->ws_payload_mask, - &payload, - &payload_size); - if (err < 0) { - return err; - } - if (err == 0) { - return ret; - } - ret += err; + qio_channel_tls_handshake(tls, + vncws_tls_handshake_done, + vs, + NULL); - buffer_reserve(&vs->input, payload_size); - buffer_append(&vs->input, payload, payload_size); - - buffer_advance(&vs->ws_input, payload_size); - } - } while (vs->ws_input.offset > 0); - - return ret; + return TRUE; } -long vnc_client_write_ws(VncState *vs) -{ - long ret; - VNC_DEBUG("Write WS: Pending output %p size %zd offset %zd\n", - vs->output.buffer, vs->output.capacity, vs->output.offset); - vncws_encode_frame(&vs->ws_output, vs->output.buffer, vs->output.offset); - buffer_reset(&vs->output); - ret = vnc_client_write_buf(vs, vs->ws_output.buffer, vs->ws_output.offset); - if (!ret) { - return 0; - } - - buffer_advance(&vs->ws_output, ret); - - if (vs->ws_output.offset == 0) { - qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); - } - - return ret; -} -static char *vncws_extract_handshake_entry(const char *handshake, - size_t handshake_len, const char *name) +static void vncws_handshake_done(Object *source, + Error *err, + gpointer user_data) { - char *begin, *end, *ret = NULL; - char *line = g_strdup_printf("%s%s: ", WS_HANDSHAKE_DELIM, name); - begin = g_strstr_len(handshake, handshake_len, line); - if (begin != NULL) { - begin += strlen(line); - end = g_strstr_len(begin, handshake_len - (begin - handshake), - WS_HANDSHAKE_DELIM); - if (end != NULL) { - ret = g_strndup(begin, end - begin); - } - } - g_free(line); - return ret; -} - -static void vncws_send_handshake_response(VncState *vs, const char* key) -{ - char combined_key[WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1]; - char *accept = NULL, *response = NULL; - Error *err = NULL; - - g_strlcpy(combined_key, key, WS_CLIENT_KEY_LEN + 1); - g_strlcat(combined_key, WS_GUID, WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1); + VncState *vs = user_data; - /* hash and encode it */ - if (qcrypto_hash_base64(QCRYPTO_HASH_ALG_SHA1, - combined_key, - WS_CLIENT_KEY_LEN + WS_GUID_LEN, - &accept, - &err) < 0) { - VNC_DEBUG("Hashing Websocket combined key failed %s\n", - error_get_pretty(err)); - error_free(err); + if (err) { + VNC_DEBUG("Websock handshake failed %s\n", error_get_pretty(err)); vnc_client_error(vs); - return; - } - - response = g_strdup_printf(WS_HANDSHAKE, accept); - vnc_client_write_buf(vs, (const uint8_t *)response, strlen(response)); - - g_free(accept); - g_free(response); - - vs->encode_ws = 1; - vnc_init_state(vs); -} - -void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size) -{ - char *protocols = vncws_extract_handshake_entry((const char *)line, size, - "Sec-WebSocket-Protocol"); - char *version = vncws_extract_handshake_entry((const char *)line, size, - "Sec-WebSocket-Version"); - char *key = vncws_extract_handshake_entry((const char *)line, size, - "Sec-WebSocket-Key"); - - if (protocols && version && key - && g_strrstr(protocols, "binary") - && !strcmp(version, WS_SUPPORTED_VERSION) - && strlen(key) == WS_CLIENT_KEY_LEN) { - vncws_send_handshake_response(vs, key); } else { - VNC_DEBUG("Defective Websockets header or unsupported protocol\n"); - vnc_client_error(vs); + VNC_DEBUG("Websock handshake complete, starting VNC protocol\n"); + vnc_init_state(vs); + vs->ioc_tag = qio_channel_add_watch( + vs->ioc, G_IO_IN, vnc_client_io, vs, NULL); } - - g_free(protocols); - g_free(version); - g_free(key); } -void vncws_encode_frame(Buffer *output, const void *payload, - const size_t payload_size) -{ - size_t header_size = 0; - unsigned char opcode = WS_OPCODE_BINARY_FRAME; - union { - char buf[WS_HEAD_MAX_LEN]; - WsHeader ws; - } header; - - if (!payload_size) { - return; - } - - header.ws.b0 = 0x80 | (opcode & 0x0f); - if (payload_size <= 125) { - header.ws.b1 = (uint8_t)payload_size; - header_size = 2; - } else if (payload_size < 65536) { - header.ws.b1 = 0x7e; - header.ws.u.s16.l16 = cpu_to_be16((uint16_t)payload_size); - header_size = 4; - } else { - header.ws.b1 = 0x7f; - header.ws.u.s64.l64 = cpu_to_be64(payload_size); - header_size = 10; - } - - buffer_reserve(output, header_size + payload_size); - buffer_append(output, header.buf, header_size); - buffer_append(output, payload, payload_size); -} -int vncws_decode_frame_header(Buffer *input, - size_t *header_size, - size_t *payload_remain, - WsMask *payload_mask) +gboolean vncws_handshake_io(QIOChannel *ioc G_GNUC_UNUSED, + GIOCondition condition G_GNUC_UNUSED, + void *opaque) { - unsigned char opcode = 0, fin = 0, has_mask = 0; - size_t payload_len; - WsHeader *header = (WsHeader *)input->buffer; - - if (input->offset < WS_HEAD_MIN_LEN + 4) { - /* header not complete */ - return 0; - } - - fin = (header->b0 & 0x80) >> 7; - opcode = header->b0 & 0x0f; - has_mask = (header->b1 & 0x80) >> 7; - payload_len = header->b1 & 0x7f; - - if (opcode == WS_OPCODE_CLOSE) { - /* disconnect */ - return -1; - } + VncState *vs = opaque; + QIOChannelWebsock *wioc; - /* Websocket frame sanity check: - * * Websocket fragmentation is not supported. - * * All websockets frames sent by a client have to be masked. - * * Only binary encoding is supported. - */ - if (!fin || !has_mask || opcode != WS_OPCODE_BINARY_FRAME) { - VNC_DEBUG("Received faulty/unsupported Websocket frame\n"); - return -2; + VNC_DEBUG("Websocket negotiate starting\n"); + if (vs->ioc_tag) { + g_source_remove(vs->ioc_tag); + vs->ioc_tag = 0; } - if (payload_len < 126) { - *payload_remain = payload_len; - *header_size = 6; - *payload_mask = header->u.m; - } else if (payload_len == 126 && input->offset >= 8) { - *payload_remain = be16_to_cpu(header->u.s16.l16); - *header_size = 8; - *payload_mask = header->u.s16.m16; - } else if (payload_len == 127 && input->offset >= 14) { - *payload_remain = be64_to_cpu(header->u.s64.l64); - *header_size = 14; - *payload_mask = header->u.s64.m64; - } else { - /* header not complete */ - return 0; - } + wioc = qio_channel_websock_new_server(vs->ioc); - return 1; -} + object_unref(OBJECT(vs->ioc)); + vs->ioc = QIO_CHANNEL(wioc); -int vncws_decode_frame_payload(Buffer *input, - size_t *payload_remain, WsMask *payload_mask, - uint8_t **payload, size_t *payload_size) -{ - size_t i; - uint32_t *payload32; - - *payload = input->buffer; - /* If we aren't at the end of the payload, then drop - * off the last bytes, so we're always multiple of 4 - * for purpose of unmasking, except at end of payload - */ - if (input->offset < *payload_remain) { - *payload_size = input->offset - (input->offset % 4); - } else { - *payload_size = *payload_remain; - } - if (*payload_size == 0) { - return 0; - } - *payload_remain -= *payload_size; - - /* unmask frame */ - /* process 1 frame (32 bit op) */ - payload32 = (uint32_t *)(*payload); - for (i = 0; i < *payload_size / 4; i++) { - payload32[i] ^= payload_mask->u; - } - /* process the remaining bytes (if any) */ - for (i *= 4; i < *payload_size; i++) { - (*payload)[i] ^= payload_mask->c[i % 4]; - } + qio_channel_websock_handshake(wioc, + vncws_handshake_done, + vs, + NULL); - return 1; + return TRUE; } diff --git a/qemu/ui/vnc-ws.h b/qemu/ui/vnc-ws.h index 94942258e..652b6fc39 100644 --- a/qemu/ui/vnc-ws.h +++ b/qemu/ui/vnc-ws.h @@ -21,72 +21,11 @@ #ifndef __QEMU_UI_VNC_WS_H #define __QEMU_UI_VNC_WS_H -#define B64LEN(__x) (((__x + 2) / 3) * 12 / 3) -#define SHA1_DIGEST_LEN 20 - -#define WS_ACCEPT_LEN (B64LEN(SHA1_DIGEST_LEN) + 1) -#define WS_CLIENT_KEY_LEN 24 -#define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" -#define WS_GUID_LEN strlen(WS_GUID) - -#define WS_HANDSHAKE "HTTP/1.1 101 Switching Protocols\r\n\ -Upgrade: websocket\r\n\ -Connection: Upgrade\r\n\ -Sec-WebSocket-Accept: %s\r\n\ -Sec-WebSocket-Protocol: binary\r\n\ -\r\n" -#define WS_HANDSHAKE_DELIM "\r\n" -#define WS_HANDSHAKE_END "\r\n\r\n" -#define WS_SUPPORTED_VERSION "13" - -#define WS_HEAD_MIN_LEN sizeof(uint16_t) -#define WS_HEAD_MAX_LEN (WS_HEAD_MIN_LEN + sizeof(uint64_t) + sizeof(uint32_t)) - -typedef union WsMask { - char c[4]; - uint32_t u; -} WsMask; - -typedef struct QEMU_PACKED WsHeader { - unsigned char b0; - unsigned char b1; - union { - struct QEMU_PACKED { - uint16_t l16; - WsMask m16; - } s16; - struct QEMU_PACKED { - uint64_t l64; - WsMask m64; - } s64; - WsMask m; - } u; -} WsHeader; - -enum { - WS_OPCODE_CONTINUATION = 0x0, - WS_OPCODE_TEXT_FRAME = 0x1, - WS_OPCODE_BINARY_FRAME = 0x2, - WS_OPCODE_CLOSE = 0x8, - WS_OPCODE_PING = 0x9, - WS_OPCODE_PONG = 0xA -}; - -#ifdef CONFIG_VNC_TLS -void vncws_tls_handshake_io(void *opaque); -#endif /* CONFIG_VNC_TLS */ -void vncws_handshake_read(void *opaque); -long vnc_client_write_ws(VncState *vs); -long vnc_client_read_ws(VncState *vs); -void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size); -void vncws_encode_frame(Buffer *output, const void *payload, - const size_t payload_size); -int vncws_decode_frame_header(Buffer *input, - size_t *header_size, - size_t *payload_remain, - WsMask *payload_mask); -int vncws_decode_frame_payload(Buffer *input, - size_t *payload_remain, WsMask *payload_mask, - uint8_t **payload, size_t *payload_size); +gboolean vncws_tls_handshake_io(QIOChannel *ioc, + GIOCondition condition, + void *opaque); +gboolean vncws_handshake_io(QIOChannel *ioc, + GIOCondition condition, + void *opaque); #endif /* __QEMU_UI_VNC_WS_H */ diff --git a/qemu/ui/vnc.c b/qemu/ui/vnc.c index caf82f56f..d2ebf1fb7 100644 --- a/qemu/ui/vnc.c +++ b/qemu/ui/vnc.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "vnc.h" #include "vnc-jobs.h" #include "trace.h" @@ -37,10 +38,13 @@ #include "qapi/qmp/qerror.h" #include "qapi/qmp/types.h" #include "qmp-commands.h" -#include "qemu/osdep.h" #include "ui/input.h" #include "qapi-event.h" #include "crypto/hash.h" +#include "crypto/tlscredsanon.h" +#include "crypto/tlscredsx509.h" +#include "qom/object_interfaces.h" +#include "qemu/cutils.h" #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT #define VNC_REFRESH_INTERVAL_INC 50 @@ -67,8 +71,8 @@ static void vnc_set_share_mode(VncState *vs, VncShareMode mode) [VNC_SHARE_MODE_EXCLUSIVE] = "exclusive", [VNC_SHARE_MODE_DISCONNECTED] = "disconnected", }; - fprintf(stderr, "%s/%d: %s -> %s\n", __func__, - vs->csock, mn[vs->share_mode], mn[mode]); + fprintf(stderr, "%s/%p: %s -> %s\n", __func__, + vs->ioc, mn[vs->share_mode], mn[mode]); #endif switch (vs->share_mode) { @@ -102,105 +106,65 @@ static void vnc_set_share_mode(VncState *vs, VncShareMode mode) } } -static char *addr_to_string(const char *format, - struct sockaddr_storage *sa, - socklen_t salen) { - char *addr; - char host[NI_MAXHOST]; - char serv[NI_MAXSERV]; - int err; - size_t addrlen; - - if ((err = getnameinfo((struct sockaddr *)sa, salen, - host, sizeof(host), - serv, sizeof(serv), - NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { - VNC_DEBUG("Cannot resolve address %d: %s\n", - err, gai_strerror(err)); - return NULL; - } - - /* Enough for the existing format + the 2 vars we're - * substituting in. */ - addrlen = strlen(format) + strlen(host) + strlen(serv); - addr = g_malloc(addrlen + 1); - snprintf(addr, addrlen, format, host, serv); - addr[addrlen] = '\0'; - return addr; -} - - -char *vnc_socket_local_addr(const char *format, int fd) { - struct sockaddr_storage sa; - socklen_t salen; - - salen = sizeof(sa); - if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0) - return NULL; - - return addr_to_string(format, &sa, salen); -} - -char *vnc_socket_remote_addr(const char *format, int fd) { - struct sockaddr_storage sa; - socklen_t salen; - - salen = sizeof(sa); - if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0) - return NULL; +static void vnc_init_basic_info(SocketAddress *addr, + VncBasicInfo *info, + Error **errp) +{ + switch (addr->type) { + case SOCKET_ADDRESS_KIND_INET: + info->host = g_strdup(addr->u.inet.data->host); + info->service = g_strdup(addr->u.inet.data->port); + if (addr->u.inet.data->ipv6) { + info->family = NETWORK_ADDRESS_FAMILY_IPV6; + } else { + info->family = NETWORK_ADDRESS_FAMILY_IPV4; + } + break; - return addr_to_string(format, &sa, salen); -} + case SOCKET_ADDRESS_KIND_UNIX: + info->host = g_strdup(""); + info->service = g_strdup(addr->u.q_unix.data->path); + info->family = NETWORK_ADDRESS_FAMILY_UNIX; + break; -static VncBasicInfo *vnc_basic_info_get(struct sockaddr_storage *sa, - socklen_t salen) -{ - VncBasicInfo *info; - char host[NI_MAXHOST]; - char serv[NI_MAXSERV]; - int err; - - if ((err = getnameinfo((struct sockaddr *)sa, salen, - host, sizeof(host), - serv, sizeof(serv), - NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { - VNC_DEBUG("Cannot resolve address %d: %s\n", - err, gai_strerror(err)); - return NULL; + default: + error_setg(errp, "Unsupported socket kind %d", + addr->type); + break; } - info = g_malloc0(sizeof(VncBasicInfo)); - info->host = g_strdup(host); - info->service = g_strdup(serv); - info->family = inet_netfamily(sa->ss_family); - return info; + return; } -static VncBasicInfo *vnc_basic_info_get_from_server_addr(int fd) +static void vnc_init_basic_info_from_server_addr(QIOChannelSocket *ioc, + VncBasicInfo *info, + Error **errp) { - struct sockaddr_storage sa; - socklen_t salen; + SocketAddress *addr = NULL; - salen = sizeof(sa); - if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0) { - return NULL; + addr = qio_channel_socket_get_local_address(ioc, errp); + if (!addr) { + return; } - return vnc_basic_info_get(&sa, salen); + vnc_init_basic_info(addr, info, errp); + qapi_free_SocketAddress(addr); } -static VncBasicInfo *vnc_basic_info_get_from_remote_addr(int fd) +static void vnc_init_basic_info_from_remote_addr(QIOChannelSocket *ioc, + VncBasicInfo *info, + Error **errp) { - struct sockaddr_storage sa; - socklen_t salen; + SocketAddress *addr = NULL; - salen = sizeof(sa); - if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0) { - return NULL; + addr = qio_channel_socket_get_remote_address(ioc, errp); + if (!addr) { + return; } - return vnc_basic_info_get(&sa, salen); + vnc_init_basic_info(addr, info, errp); + qapi_free_SocketAddress(addr); } static const char *vnc_auth_name(VncDisplay *vd) { @@ -222,7 +186,6 @@ static const char *vnc_auth_name(VncDisplay *vd) { case VNC_AUTH_TLS: return "tls"; case VNC_AUTH_VENCRYPT: -#ifdef CONFIG_VNC_TLS switch (vd->subauth) { case VNC_AUTH_VENCRYPT_PLAIN: return "vencrypt+plain"; @@ -245,9 +208,6 @@ static const char *vnc_auth_name(VncDisplay *vd) { default: return "vencrypt"; } -#else - return "vencrypt"; -#endif case VNC_AUTH_SASL: return "sasl"; } @@ -257,15 +217,18 @@ static const char *vnc_auth_name(VncDisplay *vd) { static VncServerInfo *vnc_server_info_get(VncDisplay *vd) { VncServerInfo *info; - VncBasicInfo *bi = vnc_basic_info_get_from_server_addr(vd->lsock); - if (!bi) { - return NULL; - } + Error *err = NULL; info = g_malloc(sizeof(*info)); - info->base = bi; + vnc_init_basic_info_from_server_addr(vd->lsock, + qapi_VncServerInfo_base(info), &err); info->has_auth = true; info->auth = g_strdup(vnc_auth_name(vd)); + if (err) { + qapi_free_VncServerInfo(info); + info = NULL; + error_free(err); + } return info; } @@ -275,13 +238,12 @@ static void vnc_client_cache_auth(VncState *client) return; } -#ifdef CONFIG_VNC_TLS - if (client->tls.session && - client->tls.dname) { - client->info->has_x509_dname = true; - client->info->x509_dname = g_strdup(client->tls.dname); + if (client->tls) { + client->info->x509_dname = + qcrypto_tls_session_get_peer_name(client->tls); + client->info->has_x509_dname = + client->info->x509_dname != NULL; } -#endif #ifdef CONFIG_VNC_SASL if (client->sasl.conn && client->sasl.username) { @@ -293,11 +255,16 @@ static void vnc_client_cache_auth(VncState *client) static void vnc_client_cache_addr(VncState *client) { - VncBasicInfo *bi = vnc_basic_info_get_from_remote_addr(client->csock); + Error *err = NULL; - if (bi) { - client->info = g_malloc0(sizeof(*client->info)); - client->info->base = bi; + client->info = g_malloc0(sizeof(*client->info)); + vnc_init_basic_info_from_remote_addr(client->sioc, + qapi_VncClientInfo_base(client->info), + &err); + if (err) { + qapi_free_VncClientInfo(client->info); + client->info = NULL; + error_free(err); } } @@ -308,7 +275,6 @@ static void vnc_qmp_event(VncState *vs, QAPIEvent event) if (!vs->info) { return; } - g_assert(vs->info->base); si = vnc_server_info_get(vs->vd); if (!si) { @@ -317,7 +283,8 @@ static void vnc_qmp_event(VncState *vs, QAPIEvent event) switch (event) { case QAPI_EVENT_VNC_CONNECTED: - qapi_event_send_vnc_connected(si, vs->info->base, &error_abort); + qapi_event_send_vnc_connected(si, qapi_VncClientInfo_base(vs->info), + &error_abort); break; case QAPI_EVENT_VNC_INITIALIZED: qapi_event_send_vnc_initialized(si, vs->info, &error_abort); @@ -334,36 +301,26 @@ static void vnc_qmp_event(VncState *vs, QAPIEvent event) static VncClientInfo *qmp_query_vnc_client(const VncState *client) { - struct sockaddr_storage sa; - socklen_t salen = sizeof(sa); - char host[NI_MAXHOST]; - char serv[NI_MAXSERV]; VncClientInfo *info; + Error *err = NULL; - if (getpeername(client->csock, (struct sockaddr *)&sa, &salen) < 0) { - return NULL; - } + info = g_malloc0(sizeof(*info)); - if (getnameinfo((struct sockaddr *)&sa, salen, - host, sizeof(host), - serv, sizeof(serv), - NI_NUMERICHOST | NI_NUMERICSERV) < 0) { + vnc_init_basic_info_from_remote_addr(client->sioc, + qapi_VncClientInfo_base(info), + &err); + if (err) { + error_free(err); + qapi_free_VncClientInfo(info); return NULL; } - info = g_malloc0(sizeof(*info)); - info->base = g_malloc0(sizeof(*info->base)); - info->base->host = g_strdup(host); - info->base->service = g_strdup(serv); - info->base->family = inet_netfamily(sa.ss_family); - info->base->websocket = client->websocket; + info->websocket = client->websocket; -#ifdef CONFIG_VNC_TLS - if (client->tls.session && client->tls.dname) { - info->has_x509_dname = true; - info->x509_dname = g_strdup(client->tls.dname); + if (client->tls) { + info->x509_dname = qcrypto_tls_session_get_peer_name(client->tls); + info->has_x509_dname = info->x509_dname != NULL; } -#endif #ifdef CONFIG_VNC_SASL if (client->sasl.conn && client->sasl.username) { info->has_sasl_username = true; @@ -407,81 +364,89 @@ VncInfo *qmp_query_vnc(Error **errp) { VncInfo *info = g_malloc0(sizeof(*info)); VncDisplay *vd = vnc_display_find(NULL); + SocketAddress *addr = NULL; if (vd == NULL || !vd->enabled) { info->enabled = false; } else { - struct sockaddr_storage sa; - socklen_t salen = sizeof(sa); - char host[NI_MAXHOST]; - char serv[NI_MAXSERV]; - info->enabled = true; /* for compatibility with the original command */ info->has_clients = true; info->clients = qmp_query_client_list(vd); - if (vd->lsock == -1) { + if (vd->lsock == NULL) { return info; } - if (getsockname(vd->lsock, (struct sockaddr *)&sa, - &salen) == -1) { - error_setg(errp, QERR_UNDEFINED_ERROR); + addr = qio_channel_socket_get_local_address(vd->lsock, errp); + if (!addr) { goto out_error; } - if (getnameinfo((struct sockaddr *)&sa, salen, - host, sizeof(host), - serv, sizeof(serv), - NI_NUMERICHOST | NI_NUMERICSERV) < 0) { - error_setg(errp, QERR_UNDEFINED_ERROR); + switch (addr->type) { + case SOCKET_ADDRESS_KIND_INET: + info->host = g_strdup(addr->u.inet.data->host); + info->service = g_strdup(addr->u.inet.data->port); + if (addr->u.inet.data->ipv6) { + info->family = NETWORK_ADDRESS_FAMILY_IPV6; + } else { + info->family = NETWORK_ADDRESS_FAMILY_IPV4; + } + break; + + case SOCKET_ADDRESS_KIND_UNIX: + info->host = g_strdup(""); + info->service = g_strdup(addr->u.q_unix.data->path); + info->family = NETWORK_ADDRESS_FAMILY_UNIX; + break; + + default: + error_setg(errp, "Unsupported socket kind %d", + addr->type); goto out_error; } info->has_host = true; - info->host = g_strdup(host); - info->has_service = true; - info->service = g_strdup(serv); - info->has_family = true; - info->family = inet_netfamily(sa.ss_family); info->has_auth = true; info->auth = g_strdup(vnc_auth_name(vd)); } + qapi_free_SocketAddress(addr); return info; out_error: + qapi_free_SocketAddress(addr); qapi_free_VncInfo(info); return NULL; } -static VncBasicInfoList *qmp_query_server_entry(int socket, +static VncBasicInfoList *qmp_query_server_entry(QIOChannelSocket *ioc, bool websocket, VncBasicInfoList *prev) { VncBasicInfoList *list; VncBasicInfo *info; - struct sockaddr_storage sa; - socklen_t salen = sizeof(sa); - char host[NI_MAXHOST]; - char serv[NI_MAXSERV]; - - if (getsockname(socket, (struct sockaddr *)&sa, &salen) < 0 || - getnameinfo((struct sockaddr *)&sa, salen, - host, sizeof(host), serv, sizeof(serv), - NI_NUMERICHOST | NI_NUMERICSERV) < 0) { + Error *err = NULL; + SocketAddress *addr; + + addr = qio_channel_socket_get_local_address(ioc, &err); + if (!addr) { + error_free(err); return prev; } info = g_new0(VncBasicInfo, 1); - info->host = g_strdup(host); - info->service = g_strdup(serv); - info->family = inet_netfamily(sa.ss_family); + vnc_init_basic_info(addr, info, &err); + qapi_free_SocketAddress(addr); + if (err) { + qapi_free_VncBasicInfo(info); + error_free(err); + return prev; + } info->websocket = websocket; list = g_new0(VncBasicInfoList, 1); @@ -513,7 +478,6 @@ static void qmp_query_auth(VncDisplay *vd, VncInfo2 *info) break; case VNC_AUTH_VENCRYPT: info->auth = VNC_PRIMARY_AUTH_VENCRYPT; -#ifdef CONFIG_VNC_TLS info->has_vencrypt = true; switch (vd->subauth) { case VNC_AUTH_VENCRYPT_PLAIN: @@ -547,7 +511,6 @@ static void qmp_query_auth(VncDisplay *vd, VncInfo2 *info) info->has_vencrypt = false; break; } -#endif break; case VNC_AUTH_SASL: info->auth = VNC_PRIMARY_AUTH_SASL; @@ -577,13 +540,13 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp) info->has_display = true; info->display = g_strdup(dev->id); } - if (vd->lsock != -1) { - info->server = qmp_query_server_entry(vd->lsock, false, - info->server); + if (vd->lsock != NULL) { + info->server = qmp_query_server_entry( + vd->lsock, false, info->server); } - if (vd->lwebsock != -1) { - info->server = qmp_query_server_entry(vd->lwebsock, true, - info->server); + if (vd->lwebsock != NULL) { + info->server = qmp_query_server_entry( + vd->lwebsock, true, info->server); } item = g_new0(VncInfo2List, 1); @@ -611,10 +574,25 @@ static void framebuffer_update_request(VncState *vs, int incremental, static void vnc_refresh(DisplayChangeListener *dcl); static int vnc_refresh_server_surface(VncDisplay *vd); +static int vnc_width(VncDisplay *vd) +{ + return MIN(VNC_MAX_WIDTH, ROUND_UP(surface_width(vd->ds), + VNC_DIRTY_PIXELS_PER_BIT)); +} + +static int vnc_height(VncDisplay *vd) +{ + return MIN(VNC_MAX_HEIGHT, surface_height(vd->ds)); +} + static void vnc_set_area_dirty(DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT], VNC_MAX_WIDTH / VNC_DIRTY_PIXELS_PER_BIT), - int width, int height, - int x, int y, int w, int h) { + VncDisplay *vd, + int x, int y, int w, int h) +{ + int width = vnc_width(vd); + int height = vnc_height(vd); + /* this is needed this to ensure we updated all affected * blocks if x % VNC_DIRTY_PIXELS_PER_BIT != 0 */ w += (x % VNC_DIRTY_PIXELS_PER_BIT); @@ -636,10 +614,8 @@ static void vnc_dpy_update(DisplayChangeListener *dcl, { VncDisplay *vd = container_of(dcl, VncDisplay, dcl); struct VncSurface *s = &vd->guest; - int width = pixman_image_get_width(vd->server); - int height = pixman_image_get_height(vd->server); - vnc_set_area_dirty(s->dirty, width, height, x, y, w, h); + vnc_set_area_dirty(s->dirty, vd, x, y, w, h); } void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, @@ -653,53 +629,10 @@ void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, vnc_write_s32(vs, encoding); } -void buffer_reserve(Buffer *buffer, size_t len) -{ - if ((buffer->capacity - buffer->offset) < len) { - buffer->capacity += (len + 1024); - buffer->buffer = g_realloc(buffer->buffer, buffer->capacity); - } -} - -static int buffer_empty(Buffer *buffer) -{ - return buffer->offset == 0; -} - -uint8_t *buffer_end(Buffer *buffer) -{ - return buffer->buffer + buffer->offset; -} - -void buffer_reset(Buffer *buffer) -{ - buffer->offset = 0; -} - -void buffer_free(Buffer *buffer) -{ - g_free(buffer->buffer); - buffer->offset = 0; - buffer->capacity = 0; - buffer->buffer = NULL; -} - -void buffer_append(Buffer *buffer, const void *data, size_t len) -{ - memcpy(buffer->buffer + buffer->offset, data, len); - buffer->offset += len; -} - -void buffer_advance(Buffer *buf, size_t len) -{ - memmove(buf->buffer, buf->buffer + len, - (buf->offset - len)); - buf->offset -= len; -} static void vnc_desktop_resize(VncState *vs) { - if (vs->csock == -1 || !vnc_has_feature(vs, VNC_FEATURE_RESIZE)) { + if (vs->ioc == NULL || !vnc_has_feature(vs, VNC_FEATURE_RESIZE)) { return; } if (vs->client_width == pixman_image_get_width(vs->vd->server) && @@ -752,6 +685,21 @@ void *vnc_server_fb_ptr(VncDisplay *vd, int x, int y) return ptr; } +static void vnc_update_server_surface(VncDisplay *vd) +{ + qemu_pixman_image_unref(vd->server); + vd->server = NULL; + + if (QTAILQ_EMPTY(&vd->clients)) { + return; + } + + vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT, + vnc_width(vd), + vnc_height(vd), + NULL, 0); +} + static void vnc_dpy_switch(DisplayChangeListener *dcl, DisplaySurface *surface) { @@ -760,26 +708,19 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl, int width, height; vnc_abort_display_jobs(vd); + vd->ds = surface; /* server surface */ - qemu_pixman_image_unref(vd->server); - vd->ds = surface; - width = MIN(VNC_MAX_WIDTH, ROUND_UP(surface_width(vd->ds), - VNC_DIRTY_PIXELS_PER_BIT)); - height = MIN(VNC_MAX_HEIGHT, surface_height(vd->ds)); - vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT, - width, height, NULL, 0); + vnc_update_server_surface(vd); /* guest surface */ -#if 0 /* FIXME */ - if (ds_get_bytes_per_pixel(ds) != vd->guest.ds->pf.bytes_per_pixel) - console_color_init(ds); -#endif qemu_pixman_image_unref(vd->guest.fb); vd->guest.fb = pixman_image_ref(surface->image); vd->guest.format = surface->format; + width = vnc_width(vd); + height = vnc_height(vd); memset(vd->guest.dirty, 0x00, sizeof(vd->guest.dirty)); - vnc_set_area_dirty(vd->guest.dirty, width, height, 0, 0, + vnc_set_area_dirty(vd->guest.dirty, vd, 0, 0, width, height); QTAILQ_FOREACH(vs, &vd->clients, next) { @@ -789,7 +730,7 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl, vnc_cursor_define(vs); } memset(vs->dirty, 0x00, sizeof(vs->dirty)); - vnc_set_area_dirty(vs->dirty, width, height, 0, 0, + vnc_set_area_dirty(vs->dirty, vd, 0, 0, width, height); } } @@ -879,6 +820,8 @@ int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) { int n = 0; + bool encode_raw = false; + size_t saved_offs = vs->output.offset; switch(vs->vnc_encoding) { case VNC_ENCODING_ZLIB: @@ -901,10 +844,24 @@ int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) n = vnc_zywrle_send_framebuffer_update(vs, x, y, w, h); break; default: - vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW); - n = vnc_raw_send_framebuffer_update(vs, x, y, w, h); + encode_raw = true; break; } + + /* If the client has the same pixel format as our internal buffer and + * a RAW encoding would need less space fall back to RAW encoding to + * save bandwidth and processing power in the client. */ + if (!encode_raw && vs->write_pixels == vnc_write_pixels_copy && + 12 + h * w * VNC_SERVER_FB_BYTES <= (vs->output.offset - saved_offs)) { + vs->output.offset = saved_offs; + encode_raw = true; + } + + if (encode_raw) { + vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW); + n = vnc_raw_send_framebuffer_update(vs, x, y, w, h); + } + return n; } @@ -933,6 +890,11 @@ static void vnc_dpy_copy(DisplayChangeListener *dcl, int i, x, y, pitch, inc, w_lim, s; int cmp_bytes; + if (!vd->server) { + /* no client connected */ + return; + } + vnc_refresh_server_surface(vd); QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) { if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) { @@ -1063,7 +1025,7 @@ static int find_and_clear_dirty_height(VncState *vs, static int vnc_update_client(VncState *vs, int has_dirty, bool sync) { vs->has_dirty += has_dirty; - if (vs->need_update && vs->csock != -1) { + if (vs->need_update && vs->ioc != NULL) { VncDisplay *vd = vs->vd; VncJob *job; int y; @@ -1127,7 +1089,7 @@ static int vnc_update_client(VncState *vs, int has_dirty, bool sync) return n; } - if (vs->csock == -1) { + if (vs->disconnecting) { vnc_disconnect_finish(vs); } else if (sync) { vnc_jobs_join(vs); @@ -1209,12 +1171,15 @@ static void audio_del(VncState *vs) static void vnc_disconnect_start(VncState *vs) { - if (vs->csock == -1) + if (vs->disconnecting) { return; + } vnc_set_share_mode(vs, VNC_SHARE_MODE_DISCONNECTED); - qemu_set_fd_handler(vs->csock, NULL, NULL, NULL); - closesocket(vs->csock); - vs->csock = -1; + if (vs->ioc_tag) { + g_source_remove(vs->ioc_tag); + } + qio_channel_close(vs->ioc, NULL); + vs->disconnecting = TRUE; } void vnc_disconnect_finish(VncState *vs) @@ -1228,8 +1193,6 @@ void vnc_disconnect_finish(VncState *vs) buffer_free(&vs->input); buffer_free(&vs->output); - buffer_free(&vs->ws_input); - buffer_free(&vs->ws_output); qapi_free_VncClientInfo(vs->info); @@ -1237,9 +1200,6 @@ void vnc_disconnect_finish(VncState *vs) vnc_tight_clear(vs); vnc_zrle_clear(vs); -#ifdef CONFIG_VNC_TLS - vnc_tls_client_cleanup(vs); -#endif /* CONFIG_VNC_TLS */ #ifdef CONFIG_VNC_SASL vnc_sasl_client_cleanup(vs); #endif /* CONFIG_VNC_SASL */ @@ -1249,6 +1209,10 @@ void vnc_disconnect_finish(VncState *vs) if (vs->initialized) { QTAILQ_REMOVE(&vs->vd->clients, vs, next); qemu_remove_mouse_mode_change_notifier(&vs->mouse_mode_notifier); + if (QTAILQ_EMPTY(&vs->vd->clients)) { + /* last client gone */ + vnc_update_server_surface(vs->vd); + } } if (vs->vd->lock_key_sync) @@ -1265,29 +1229,29 @@ void vnc_disconnect_finish(VncState *vs) g_free(vs->lossy_rect[i]); } g_free(vs->lossy_rect); + + object_unref(OBJECT(vs->ioc)); + vs->ioc = NULL; + object_unref(OBJECT(vs->sioc)); + vs->sioc = NULL; g_free(vs); } -int vnc_client_io_error(VncState *vs, int ret, int last_errno) +ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp) { - if (ret == 0 || ret == -1) { - if (ret == -1) { - switch (last_errno) { - case EINTR: - case EAGAIN: -#ifdef _WIN32 - case WSAEWOULDBLOCK: -#endif - return 0; - default: - break; - } + if (ret <= 0) { + if (ret == 0) { + VNC_DEBUG("Closing down client sock: EOF\n"); + } else if (ret != QIO_CHANNEL_ERR_BLOCK) { + VNC_DEBUG("Closing down client sock: ret %d (%s)\n", + ret, errp ? error_get_pretty(*errp) : "Unknown"); } - VNC_DEBUG("Closing down client sock: ret %d, errno %d\n", - ret, ret < 0 ? last_errno : 0); vnc_disconnect_start(vs); - + if (errp) { + error_free(*errp); + *errp = NULL; + } return 0; } return ret; @@ -1300,23 +1264,6 @@ void vnc_client_error(VncState *vs) vnc_disconnect_start(vs); } -#ifdef CONFIG_VNC_TLS -static long vnc_client_write_tls(gnutls_session_t *session, - const uint8_t *data, - size_t datalen) -{ - long ret = gnutls_write(*session, data, datalen); - if (ret < 0) { - if (ret == GNUTLS_E_AGAIN) { - errno = EAGAIN; - } else { - errno = EIO; - } - ret = -1; - } - return ret; -} -#endif /* CONFIG_VNC_TLS */ /* * Called to write a chunk of data to the client socket. The data may @@ -1333,20 +1280,14 @@ static long vnc_client_write_tls(gnutls_session_t *session, * the requested 'datalen' if the socket would block. Returns * -1 on error, and disconnects the client socket. */ -long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen) +ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen) { - long ret; -#ifdef CONFIG_VNC_TLS - if (vs->tls.session) { - ret = vnc_client_write_tls(&vs->tls.session, data, datalen); - } else { -#endif /* CONFIG_VNC_TLS */ - ret = send(vs->csock, (const void *)data, datalen, 0); -#ifdef CONFIG_VNC_TLS - } -#endif /* CONFIG_VNC_TLS */ + Error *err = NULL; + ssize_t ret; + ret = qio_channel_write( + vs->ioc, (const char *)data, datalen, &err); VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret); - return vnc_client_io_error(vs, ret, socket_error()); + return vnc_client_io_error(vs, ret, &err); } @@ -1360,9 +1301,9 @@ long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen) * the buffered output data if the socket would block. Returns * -1 on error, and disconnects the client socket. */ -static long vnc_client_write_plain(VncState *vs) +static ssize_t vnc_client_write_plain(VncState *vs) { - long ret; + ssize_t ret; #ifdef CONFIG_VNC_SASL VNC_DEBUG("Write Plain: Pending output %p size %zd offset %zd. Wait SSF %d\n", @@ -1384,7 +1325,11 @@ static long vnc_client_write_plain(VncState *vs) buffer_advance(&vs->output, ret); if (vs->output.offset == 0) { - qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); + if (vs->ioc_tag) { + g_source_remove(vs->ioc_tag); + } + vs->ioc_tag = qio_channel_add_watch( + vs->ioc, G_IO_IN, vnc_client_io, vs, NULL); } return ret; @@ -1396,10 +1341,8 @@ static long vnc_client_write_plain(VncState *vs) * the client socket. Will delegate actual work according to whether * SASL SSF layers are enabled (thus requiring encryption calls) */ -static void vnc_client_write_locked(void *opaque) +static void vnc_client_write_locked(VncState *vs) { - VncState *vs = opaque; - #ifdef CONFIG_VNC_SASL if (vs->sasl.conn && vs->sasl.runSSF && @@ -1408,23 +1351,22 @@ static void vnc_client_write_locked(void *opaque) } else #endif /* CONFIG_VNC_SASL */ { - if (vs->encode_ws) { - vnc_client_write_ws(vs); - } else { - vnc_client_write_plain(vs); - } + vnc_client_write_plain(vs); } } -void vnc_client_write(void *opaque) +static void vnc_client_write(VncState *vs) { - VncState *vs = opaque; vnc_lock_output(vs); - if (vs->output.offset || vs->ws_output.offset) { - vnc_client_write_locked(opaque); - } else if (vs->csock != -1) { - qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); + if (vs->output.offset) { + vnc_client_write_locked(vs); + } else if (vs->ioc != NULL) { + if (vs->ioc_tag) { + g_source_remove(vs->ioc_tag); + } + vs->ioc_tag = qio_channel_add_watch( + vs->ioc, G_IO_IN, vnc_client_io, vs, NULL); } vnc_unlock_output(vs); } @@ -1435,22 +1377,6 @@ void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting) vs->read_handler_expect = expecting; } -#ifdef CONFIG_VNC_TLS -static long vnc_client_read_tls(gnutls_session_t *session, uint8_t *data, - size_t datalen) -{ - long ret = gnutls_read(*session, data, datalen); - if (ret < 0) { - if (ret == GNUTLS_E_AGAIN) { - errno = EAGAIN; - } else { - errno = EIO; - } - ret = -1; - } - return ret; -} -#endif /* CONFIG_VNC_TLS */ /* * Called to read a chunk of data from the client socket. The data may @@ -1467,20 +1393,14 @@ static long vnc_client_read_tls(gnutls_session_t *session, uint8_t *data, * the requested 'datalen' if the socket would block. Returns * -1 on error, and disconnects the client socket. */ -long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen) +ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen) { - long ret; -#ifdef CONFIG_VNC_TLS - if (vs->tls.session) { - ret = vnc_client_read_tls(&vs->tls.session, data, datalen); - } else { -#endif /* CONFIG_VNC_TLS */ - ret = qemu_recv(vs->csock, data, datalen, 0); -#ifdef CONFIG_VNC_TLS - } -#endif /* CONFIG_VNC_TLS */ + ssize_t ret; + Error *err = NULL; + ret = qio_channel_read( + vs->ioc, (char *)data, datalen, &err); VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret); - return vnc_client_io_error(vs, ret, socket_error()); + return vnc_client_io_error(vs, ret, &err); } @@ -1492,9 +1412,9 @@ long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen) * Returns the number of bytes read. Returns -1 on error, and * disconnects the client socket. */ -static long vnc_client_read_plain(VncState *vs) +static ssize_t vnc_client_read_plain(VncState *vs) { - int ret; + ssize_t ret; VNC_DEBUG("Read plain %p size %zd offset %zd\n", vs->input.buffer, vs->input.capacity, vs->input.offset); buffer_reserve(&vs->input, 4096); @@ -1517,31 +1437,20 @@ static void vnc_jobs_bh(void *opaque) * the client socket. Will delegate actual work according to whether * SASL SSF layers are enabled (thus requiring decryption calls) */ -void vnc_client_read(void *opaque) +static void vnc_client_read(VncState *vs) { - VncState *vs = opaque; - long ret; + ssize_t ret; #ifdef CONFIG_VNC_SASL if (vs->sasl.conn && vs->sasl.runSSF) ret = vnc_client_read_sasl(vs); else #endif /* CONFIG_VNC_SASL */ - if (vs->encode_ws) { - ret = vnc_client_read_ws(vs); - if (ret == -1) { - vnc_disconnect_start(vs); - return; - } else if (ret == -2) { - vnc_client_error(vs); - return; - } - } else { - ret = vnc_client_read_plain(vs); - } + ret = vnc_client_read_plain(vs); if (!ret) { - if (vs->csock == -1) + if (vs->disconnecting) { vnc_disconnect_finish(vs); + } return; } @@ -1550,7 +1459,7 @@ void vnc_client_read(void *opaque) int ret; ret = vs->read_handler(vs, vs->input.buffer, len); - if (vs->csock == -1) { + if (vs->disconnecting) { vnc_disconnect_finish(vs); return; } @@ -1563,12 +1472,30 @@ void vnc_client_read(void *opaque) } } +gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED, + GIOCondition condition, void *opaque) +{ + VncState *vs = opaque; + if (condition & G_IO_IN) { + vnc_client_read(vs); + } + if (condition & G_IO_OUT) { + vnc_client_write(vs); + } + return TRUE; +} + + void vnc_write(VncState *vs, const void *data, size_t len) { buffer_reserve(&vs->output, len); - if (vs->csock != -1 && buffer_empty(&vs->output)) { - qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs); + if (vs->ioc != NULL && buffer_empty(&vs->output)) { + if (vs->ioc_tag) { + g_source_remove(vs->ioc_tag); + } + vs->ioc_tag = qio_channel_add_watch( + vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL); } buffer_append(&vs->output, data, len); @@ -1609,8 +1536,7 @@ void vnc_write_u8(VncState *vs, uint8_t value) void vnc_flush(VncState *vs) { vnc_lock_output(vs); - if (vs->csock != -1 && (vs->output.offset || - vs->ws_output.offset)) { + if (vs->ioc != NULL && vs->output.offset) { vnc_client_write_locked(vs); } vnc_unlock_output(vs); @@ -1664,7 +1590,7 @@ static void check_pointer_type_change(Notifier *notifier, void *data) static void pointer_event(VncState *vs, int button_mask, int x, int y) { - static uint32_t bmap[INPUT_BUTTON_MAX] = { + static uint32_t bmap[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = 0x01, [INPUT_BUTTON_MIDDLE] = 0x02, [INPUT_BUTTON_RIGHT] = 0x04, @@ -2024,9 +1950,6 @@ static void ext_key_event(VncState *vs, int down, static void framebuffer_update_request(VncState *vs, int incremental, int x, int y, int w, int h) { - int width = pixman_image_get_width(vs->vd->server); - int height = pixman_image_get_height(vs->vd->server); - vs->need_update = 1; if (incremental) { @@ -2034,7 +1957,7 @@ static void framebuffer_update_request(VncState *vs, int incremental, } vs->force_update = 1; - vnc_set_area_dirty(vs->dirty, width, height, x, y, w, h); + vnc_set_area_dirty(vs->dirty, vs->vd, x, y, w, h); } static void send_ext_key_event_ack(VncState *vs) @@ -2124,6 +2047,9 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) break; case VNC_ENCODING_RICH_CURSOR: vs->features |= VNC_FEATURE_RICH_CURSOR_MASK; + if (vs->vd->cursor) { + vnc_cursor_define(vs); + } break; case VNC_ENCODING_EXT_KEY_EVENT: send_ext_key_event_ack(vs); @@ -2189,15 +2115,15 @@ static void set_pixel_format(VncState *vs, return; } - vs->client_pf.rmax = red_max; + vs->client_pf.rmax = red_max ? red_max : 0xFF; vs->client_pf.rbits = hweight_long(red_max); vs->client_pf.rshift = red_shift; vs->client_pf.rmask = red_max << red_shift; - vs->client_pf.gmax = green_max; + vs->client_pf.gmax = green_max ? green_max : 0xFF; vs->client_pf.gbits = hweight_long(green_max); vs->client_pf.gshift = green_shift; vs->client_pf.gmask = green_max << green_shift; - vs->client_pf.bmax = blue_max; + vs->client_pf.bmax = blue_max ? blue_max : 0xFF; vs->client_pf.bbits = hweight_long(blue_max); vs->client_pf.bshift = blue_shift; vs->client_pf.bmask = blue_max << blue_shift; @@ -2631,12 +2557,10 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len) start_auth_vnc(vs); break; -#ifdef CONFIG_VNC_TLS case VNC_AUTH_VENCRYPT: VNC_DEBUG("Accept VeNCrypt auth\n"); start_auth_vencrypt(vs); break; -#endif /* CONFIG_VNC_TLS */ #ifdef CONFIG_VNC_SASL case VNC_AUTH_SASL: @@ -2999,15 +2923,36 @@ static void vnc_refresh(DisplayChangeListener *dcl) } } -static void vnc_connect(VncDisplay *vd, int csock, +static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, bool skipauth, bool websocket) { - VncState *vs = g_malloc0(sizeof(VncState)); + VncState *vs = g_new0(VncState, 1); int i; - vs->csock = csock; + vs->sioc = sioc; + object_ref(OBJECT(vs->sioc)); + vs->ioc = QIO_CHANNEL(sioc); + object_ref(OBJECT(vs->ioc)); vs->vd = vd; + buffer_init(&vs->input, "vnc-input/%p", sioc); + buffer_init(&vs->output, "vnc-output/%p", sioc); + buffer_init(&vs->jobs_buffer, "vnc-jobs_buffer/%p", sioc); + + buffer_init(&vs->tight.tight, "vnc-tight/%p", sioc); + buffer_init(&vs->tight.zlib, "vnc-tight-zlib/%p", sioc); + buffer_init(&vs->tight.gradient, "vnc-tight-gradient/%p", sioc); +#ifdef CONFIG_VNC_JPEG + buffer_init(&vs->tight.jpeg, "vnc-tight-jpeg/%p", sioc); +#endif +#ifdef CONFIG_VNC_PNG + buffer_init(&vs->tight.png, "vnc-tight-png/%p", sioc); +#endif + buffer_init(&vs->zlib.zlib, "vnc-zlib/%p", sioc); + buffer_init(&vs->zrle.zrle, "vnc-zrle/%p", sioc); + buffer_init(&vs->zrle.fb, "vnc-zrle-fb/%p", sioc); + buffer_init(&vs->zrle.zlib, "vnc-zrle-zlib/%p", sioc); + if (skipauth) { vs->auth = VNC_AUTH_NONE; vs->subauth = VNC_AUTH_INVALID; @@ -3020,30 +2965,29 @@ static void vnc_connect(VncDisplay *vd, int csock, vs->subauth = vd->subauth; } } - VNC_DEBUG("Client sock=%d ws=%d auth=%d subauth=%d\n", - csock, websocket, vs->auth, vs->subauth); + VNC_DEBUG("Client sioc=%p ws=%d auth=%d subauth=%d\n", + sioc, websocket, vs->auth, vs->subauth); vs->lossy_rect = g_malloc0(VNC_STAT_ROWS * sizeof (*vs->lossy_rect)); for (i = 0; i < VNC_STAT_ROWS; ++i) { - vs->lossy_rect[i] = g_malloc0(VNC_STAT_COLS * sizeof (uint8_t)); + vs->lossy_rect[i] = g_new0(uint8_t, VNC_STAT_COLS); } - VNC_DEBUG("New client on socket %d\n", csock); + VNC_DEBUG("New client on socket %p\n", vs->sioc); update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE); - qemu_set_nonblock(vs->csock); + qio_channel_set_blocking(vs->ioc, false, NULL); if (websocket) { vs->websocket = 1; -#ifdef CONFIG_VNC_TLS if (vd->ws_tls) { - qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs); - } else -#endif /* CONFIG_VNC_TLS */ - { - qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs); + vs->ioc_tag = qio_channel_add_watch( + vs->ioc, G_IO_IN, vncws_tls_handshake_io, vs, NULL); + } else { + vs->ioc_tag = qio_channel_add_watch( + vs->ioc, G_IO_IN, vncws_handshake_io, vs, NULL); } - } else - { - qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); + } else { + vs->ioc_tag = qio_channel_add_watch( + vs->ioc, G_IO_IN, vnc_client_io, vs, NULL); } vnc_client_cache_addr(vs); @@ -3068,6 +3012,7 @@ void vnc_init_state(VncState *vs) { vs->initialized = true; VncDisplay *vd = vs->vd; + bool first_client = QTAILQ_EMPTY(&vd->clients); vs->last_x = -1; vs->last_y = -1; @@ -3081,6 +3026,9 @@ void vnc_init_state(VncState *vs) vs->bh = qemu_bh_new(vnc_jobs_bh, vs); QTAILQ_INSERT_TAIL(&vd->clients, vs, next); + if (first_client) { + vnc_update_server_surface(vd); + } graphic_hw_update(vd->dcl.con); @@ -3097,35 +3045,28 @@ void vnc_init_state(VncState *vs) /* vs might be free()ed here */ } -static void vnc_listen_read(void *opaque, bool websocket) +static gboolean vnc_listen_io(QIOChannel *ioc, + GIOCondition condition, + void *opaque) { VncDisplay *vs = opaque; - struct sockaddr_in addr; - socklen_t addrlen = sizeof(addr); - int csock; + QIOChannelSocket *sioc = NULL; + Error *err = NULL; /* Catch-up */ graphic_hw_update(vs->dcl.con); - if (websocket) { - csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen); + sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), &err); + if (sioc != NULL) { + qio_channel_set_delay(QIO_CHANNEL(sioc), false); + vnc_connect(vs, sioc, false, + ioc != QIO_CHANNEL(vs->lsock)); + object_unref(OBJECT(sioc)); } else { - csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); - } - - if (csock != -1) { - socket_set_nodelay(csock); - vnc_connect(vs, csock, false, websocket); + /* client probably closed connection before we got there */ + error_free(err); } -} -static void vnc_listen_regular_read(void *opaque) -{ - vnc_listen_read(opaque, false); -} - -static void vnc_listen_websocket_read(void *opaque) -{ - vnc_listen_read(opaque, true); + return TRUE; } static const DisplayChangeListenerOps dcl_ops = { @@ -3151,9 +3092,6 @@ void vnc_display_init(const char *id) vs->id = strdup(id); QTAILQ_INSERT_TAIL(&vnc_displays, vs, next); - vs->lsock = -1; - vs->lwebsock = -1; - QTAILQ_INIT(&vs->clients); vs->expires = TIME_MAX; @@ -3181,22 +3119,29 @@ static void vnc_display_close(VncDisplay *vs) return; vs->enabled = false; vs->is_unix = false; - if (vs->lsock != -1) { - qemu_set_fd_handler(vs->lsock, NULL, NULL, NULL); - close(vs->lsock); - vs->lsock = -1; + if (vs->lsock != NULL) { + if (vs->lsock_tag) { + g_source_remove(vs->lsock_tag); + } + object_unref(OBJECT(vs->lsock)); + vs->lsock = NULL; } vs->ws_enabled = false; - if (vs->lwebsock != -1) { - qemu_set_fd_handler(vs->lwebsock, NULL, NULL, NULL); - close(vs->lwebsock); - vs->lwebsock = -1; + if (vs->lwebsock != NULL) { + if (vs->lwebsock_tag) { + g_source_remove(vs->lwebsock_tag); + } + object_unref(OBJECT(vs->lwebsock)); + vs->lwebsock = NULL; } vs->auth = VNC_AUTH_INVALID; vs->subauth = VNC_AUTH_INVALID; -#ifdef CONFIG_VNC_TLS - vs->tls.x509verify = 0; -#endif + if (vs->tlscreds) { + object_unparent(OBJECT(vs->tlscreds)); + vs->tlscreds = NULL; + } + g_free(vs->tlsaclname); + vs->tlsaclname = NULL; } int vnc_display_password(const char *id, const char *password) @@ -3233,9 +3178,26 @@ int vnc_display_pw_expire(const char *id, time_t expires) char *vnc_display_local_addr(const char *id) { VncDisplay *vs = vnc_display_find(id); + SocketAddress *addr; + char *ret; + Error *err = NULL; assert(vs); - return vnc_socket_local_addr("%s:%s", vs->lsock); + + addr = qio_channel_socket_get_local_address(vs->lsock, &err); + if (!addr) { + return NULL; + } + + if (addr->type != SOCKET_ADDRESS_KIND_INET) { + qapi_free_SocketAddress(addr); + return NULL; + } + ret = g_strdup_printf("%s;%s", addr->u.inet.data->host, + addr->u.inet.data->port); + qapi_free_SocketAddress(addr); + + return ret; } static QemuOptsList qemu_vnc_opts = { @@ -3250,6 +3212,10 @@ static QemuOptsList qemu_vnc_opts = { .name = "websocket", .type = QEMU_OPT_STRING, },{ + .name = "tls-creds", + .type = QEMU_OPT_STRING, + },{ + /* Deprecated in favour of tls-creds */ .name = "x509", .type = QEMU_OPT_STRING, },{ @@ -3286,9 +3252,11 @@ static QemuOptsList qemu_vnc_opts = { .name = "sasl", .type = QEMU_OPT_BOOL, },{ + /* Deprecated in favour of tls-creds */ .name = "tls", .type = QEMU_OPT_BOOL, },{ + /* Deprecated in favour of tls-creds */ .name = "x509verify", .type = QEMU_OPT_STRING, },{ @@ -3306,13 +3274,12 @@ static QemuOptsList qemu_vnc_opts = { }; -static void +static int vnc_display_setup_auth(VncDisplay *vs, bool password, bool sasl, - bool tls, - bool x509, - bool websocket) + bool websocket, + Error **errp) { /* * We have a choice of 3 authentication options @@ -3362,17 +3329,24 @@ vnc_display_setup_auth(VncDisplay *vs, * result has the same security characteristics. */ if (password) { - if (tls) { + if (vs->tlscreds) { vs->auth = VNC_AUTH_VENCRYPT; if (websocket) { vs->ws_tls = true; } - if (x509) { + if (object_dynamic_cast(OBJECT(vs->tlscreds), + TYPE_QCRYPTO_TLS_CREDS_X509)) { VNC_DEBUG("Initializing VNC server with x509 password auth\n"); vs->subauth = VNC_AUTH_VENCRYPT_X509VNC; - } else { + } else if (object_dynamic_cast(OBJECT(vs->tlscreds), + TYPE_QCRYPTO_TLS_CREDS_ANON)) { VNC_DEBUG("Initializing VNC server with TLS password auth\n"); vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC; + } else { + error_setg(errp, + "Unsupported TLS cred type %s", + object_get_typename(OBJECT(vs->tlscreds))); + return -1; } } else { VNC_DEBUG("Initializing VNC server with password auth\n"); @@ -3385,17 +3359,24 @@ vnc_display_setup_auth(VncDisplay *vs, vs->ws_auth = VNC_AUTH_INVALID; } } else if (sasl) { - if (tls) { + if (vs->tlscreds) { vs->auth = VNC_AUTH_VENCRYPT; if (websocket) { vs->ws_tls = true; } - if (x509) { + if (object_dynamic_cast(OBJECT(vs->tlscreds), + TYPE_QCRYPTO_TLS_CREDS_X509)) { VNC_DEBUG("Initializing VNC server with x509 SASL auth\n"); vs->subauth = VNC_AUTH_VENCRYPT_X509SASL; - } else { + } else if (object_dynamic_cast(OBJECT(vs->tlscreds), + TYPE_QCRYPTO_TLS_CREDS_ANON)) { VNC_DEBUG("Initializing VNC server with TLS SASL auth\n"); vs->subauth = VNC_AUTH_VENCRYPT_TLSSASL; + } else { + error_setg(errp, + "Unsupported TLS cred type %s", + object_get_typename(OBJECT(vs->tlscreds))); + return -1; } } else { VNC_DEBUG("Initializing VNC server with SASL auth\n"); @@ -3408,17 +3389,24 @@ vnc_display_setup_auth(VncDisplay *vs, vs->ws_auth = VNC_AUTH_INVALID; } } else { - if (tls) { + if (vs->tlscreds) { vs->auth = VNC_AUTH_VENCRYPT; if (websocket) { vs->ws_tls = true; } - if (x509) { + if (object_dynamic_cast(OBJECT(vs->tlscreds), + TYPE_QCRYPTO_TLS_CREDS_X509)) { VNC_DEBUG("Initializing VNC server with x509 no auth\n"); vs->subauth = VNC_AUTH_VENCRYPT_X509NONE; - } else { + } else if (object_dynamic_cast(OBJECT(vs->tlscreds), + TYPE_QCRYPTO_TLS_CREDS_ANON)) { VNC_DEBUG("Initializing VNC server with TLS no auth\n"); vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE; + } else { + error_setg(errp, + "Unsupported TLS cred type %s", + object_get_typename(OBJECT(vs->tlscreds))); + return -1; } } else { VNC_DEBUG("Initializing VNC server with no auth\n"); @@ -3431,34 +3419,72 @@ vnc_display_setup_auth(VncDisplay *vs, vs->ws_auth = VNC_AUTH_INVALID; } } + return 0; } + +/* + * Handle back compat with old CLI syntax by creating some + * suitable QCryptoTLSCreds objects + */ +static QCryptoTLSCreds * +vnc_display_create_creds(bool x509, + bool x509verify, + const char *dir, + const char *id, + Error **errp) +{ + gchar *credsid = g_strdup_printf("tlsvnc%s", id); + Object *parent = object_get_objects_root(); + Object *creds; + Error *err = NULL; + + if (x509) { + creds = object_new_with_props(TYPE_QCRYPTO_TLS_CREDS_X509, + parent, + credsid, + &err, + "endpoint", "server", + "dir", dir, + "verify-peer", x509verify ? "yes" : "no", + NULL); + } else { + creds = object_new_with_props(TYPE_QCRYPTO_TLS_CREDS_ANON, + parent, + credsid, + &err, + "endpoint", "server", + NULL); + } + + g_free(credsid); + + if (err) { + error_propagate(errp, err); + return NULL; + } + + return QCRYPTO_TLS_CREDS(creds); +} + + void vnc_display_open(const char *id, Error **errp) { VncDisplay *vs = vnc_display_find(id); QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id); - QemuOpts *sopts, *wsopts; + SocketAddress *saddr = NULL, *wsaddr = NULL; const char *share, *device_id; QemuConsole *con; bool password = false; bool reverse = false; const char *vnc; - const char *has_to; char *h; - bool has_ipv4 = false; - bool has_ipv6 = false; - const char *websocket; - bool tls = false, x509 = false; -#ifdef CONFIG_VNC_TLS - const char *path; -#endif + const char *credid; bool sasl = false; #ifdef CONFIG_VNC_SASL int saslErr; #endif -#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) int acl = 0; -#endif int lock_key_sync = 1; if (!vs) { @@ -3475,44 +3501,90 @@ void vnc_display_open(const char *id, Error **errp) return; } - sopts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); - wsopts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); - h = strrchr(vnc, ':'); if (h) { - char *host; size_t hlen = h - vnc; - if (vnc[0] == '[' && vnc[hlen - 1] == ']') { - host = g_strndup(vnc + 1, hlen - 2); + const char *websocket = qemu_opt_get(opts, "websocket"); + int to = qemu_opt_get_number(opts, "to", 0); + bool has_ipv4 = qemu_opt_get(opts, "ipv4"); + bool has_ipv6 = qemu_opt_get(opts, "ipv6"); + bool ipv4 = qemu_opt_get_bool(opts, "ipv4", false); + bool ipv6 = qemu_opt_get_bool(opts, "ipv6", false); + + saddr = g_new0(SocketAddress, 1); + if (websocket) { + if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) { + error_setg(errp, + "SHA1 hash support is required for websockets"); + goto fail; + } + + wsaddr = g_new0(SocketAddress, 1); + vs->ws_enabled = true; + } + + if (strncmp(vnc, "unix:", 5) == 0) { + saddr->type = SOCKET_ADDRESS_KIND_UNIX; + saddr->u.q_unix.data = g_new0(UnixSocketAddress, 1); + saddr->u.q_unix.data->path = g_strdup(vnc + 5); + + if (vs->ws_enabled) { + error_setg(errp, "UNIX sockets not supported with websock"); + goto fail; + } } else { - host = g_strndup(vnc, hlen); + unsigned long long baseport; + InetSocketAddress *inet; + saddr->type = SOCKET_ADDRESS_KIND_INET; + inet = saddr->u.inet.data = g_new0(InetSocketAddress, 1); + if (vnc[0] == '[' && vnc[hlen - 1] == ']') { + inet->host = g_strndup(vnc + 1, hlen - 2); + } else { + inet->host = g_strndup(vnc, hlen); + } + if (parse_uint_full(h + 1, &baseport, 10) < 0) { + error_setg(errp, "can't convert to a number: %s", h + 1); + goto fail; + } + if (baseport > 65535 || + baseport + 5900 > 65535) { + error_setg(errp, "port %s out of range", h + 1); + goto fail; + } + inet->port = g_strdup_printf( + "%d", (int)baseport + 5900); + + if (to) { + inet->has_to = true; + inet->to = to + 5900; + } + inet->ipv4 = ipv4; + inet->has_ipv4 = has_ipv4; + inet->ipv6 = ipv6; + inet->has_ipv6 = has_ipv6; + + if (vs->ws_enabled) { + wsaddr->type = SOCKET_ADDRESS_KIND_INET; + inet = wsaddr->u.inet.data = g_new0(InetSocketAddress, 1); + inet->host = g_strdup(saddr->u.inet.data->host); + inet->port = g_strdup(websocket); + + if (to) { + inet->has_to = true; + inet->to = to; + } + inet->ipv4 = ipv4; + inet->has_ipv4 = has_ipv4; + inet->ipv6 = ipv6; + inet->has_ipv6 = has_ipv6; + } } - qemu_opt_set(sopts, "host", host, &error_abort); - qemu_opt_set(wsopts, "host", host, &error_abort); - qemu_opt_set(sopts, "port", h+1, &error_abort); - g_free(host); } else { error_setg(errp, "no vnc port specified"); goto fail; } - has_to = qemu_opt_get(opts, "to"); - has_ipv4 = qemu_opt_get_bool(opts, "ipv4", false); - has_ipv6 = qemu_opt_get_bool(opts, "ipv6", false); - if (has_to) { - qemu_opt_set(sopts, "to", has_to, &error_abort); - qemu_opt_set(wsopts, "to", has_to, &error_abort); - } - if (has_ipv4) { - qemu_opt_set(sopts, "ipv4", "on", &error_abort); - qemu_opt_set(wsopts, "ipv4", "on", &error_abort); - } - if (has_ipv6) { - qemu_opt_set(sopts, "ipv6", "on", &error_abort); - qemu_opt_set(wsopts, "ipv6", "on", &error_abort); - } - password = qemu_opt_get_bool(opts, "password", false); if (password) { if (fips_get_state()) { @@ -3539,32 +3611,67 @@ void vnc_display_open(const char *id, Error **errp) goto fail; } #endif /* CONFIG_VNC_SASL */ - tls = qemu_opt_get_bool(opts, "tls", false); -#ifdef CONFIG_VNC_TLS - path = qemu_opt_get(opts, "x509"); - if (!path) { - path = qemu_opt_get(opts, "x509verify"); - if (path) { - vs->tls.x509verify = true; - } - } - if (path) { - x509 = true; - if (vnc_tls_set_x509_creds_dir(vs, path) < 0) { - error_setg(errp, "Failed to find x509 certificates/keys in %s", - path); + credid = qemu_opt_get(opts, "tls-creds"); + if (credid) { + Object *creds; + if (qemu_opt_get(opts, "tls") || + qemu_opt_get(opts, "x509") || + qemu_opt_get(opts, "x509verify")) { + error_setg(errp, + "'tls-creds' parameter is mutually exclusive with " + "'tls', 'x509' and 'x509verify' parameters"); goto fail; } + + creds = object_resolve_path_component( + object_get_objects_root(), credid); + if (!creds) { + error_setg(errp, "No TLS credentials with id '%s'", + credid); + goto fail; + } + vs->tlscreds = (QCryptoTLSCreds *) + object_dynamic_cast(creds, + TYPE_QCRYPTO_TLS_CREDS); + if (!vs->tlscreds) { + error_setg(errp, "Object with id '%s' is not TLS credentials", + credid); + goto fail; + } + object_ref(OBJECT(vs->tlscreds)); + + if (vs->tlscreds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + error_setg(errp, + "Expecting TLS credentials with a server endpoint"); + goto fail; + } + } else { + const char *path; + bool tls = false, x509 = false, x509verify = false; + tls = qemu_opt_get_bool(opts, "tls", false); + if (tls) { + path = qemu_opt_get(opts, "x509"); + + if (path) { + x509 = true; + } else { + path = qemu_opt_get(opts, "x509verify"); + if (path) { + x509 = true; + x509verify = true; + } + } + vs->tlscreds = vnc_display_create_creds(x509, + x509verify, + path, + vs->id, + errp); + if (!vs->tlscreds) { + goto fail; + } + } } -#else /* ! CONFIG_VNC_TLS */ - if (tls) { - error_setg(errp, "VNC TLS auth requires gnutls support"); - goto fail; - } -#endif /* ! CONFIG_VNC_TLS */ -#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) acl = qemu_opt_get_bool(opts, "acl", false); -#endif share = qemu_opt_get(opts, "share"); if (share) { @@ -3583,16 +3690,6 @@ void vnc_display_open(const char *id, Error **errp) } vs->connections_limit = qemu_opt_get_number(opts, "connections", 32); - websocket = qemu_opt_get(opts, "websocket"); - if (websocket) { - vs->ws_enabled = true; - qemu_opt_set(wsopts, "port", websocket, &error_abort); - if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) { - error_setg(errp, "SHA1 hash support is required for websockets"); - goto fail; - } - } - #ifdef CONFIG_VNC_JPEG vs->lossy = qemu_opt_get_bool(opts, "lossy", false); #endif @@ -3604,19 +3701,14 @@ void vnc_display_open(const char *id, Error **errp) vs->non_adaptive = true; } -#ifdef CONFIG_VNC_TLS - if (acl && x509 && vs->tls.x509verify) { - char *aclname; - + if (acl) { if (strcmp(vs->id, "default") == 0) { - aclname = g_strdup("vnc.x509dname"); + vs->tlsaclname = g_strdup("vnc.x509dname"); } else { - aclname = g_strdup_printf("vnc.%s.x509dname", vs->id); + vs->tlsaclname = g_strdup_printf("vnc.%s.x509dname", vs->id); } - vs->tls.acl = qemu_acl_init(aclname); - g_free(aclname); + qemu_acl_init(vs->tlsaclname); } -#endif #ifdef CONFIG_VNC_SASL if (acl && sasl) { char *aclname; @@ -3631,7 +3723,9 @@ void vnc_display_open(const char *id, Error **errp) } #endif - vnc_display_setup_auth(vs, password, sasl, tls, x509, websocket); + if (vnc_display_setup_auth(vs, password, sasl, vs->ws_enabled, errp) < 0) { + goto fail; + } #ifdef CONFIG_VNC_SASL if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) { @@ -3644,19 +3738,12 @@ void vnc_display_open(const char *id, Error **errp) device_id = qemu_opt_get(opts, "display"); if (device_id) { - DeviceState *dev; int head = qemu_opt_get_number(opts, "head", 0); + Error *err = NULL; - dev = qdev_find_recursive(sysbus_get_default(), device_id); - if (dev == NULL) { - error_setg(errp, "Device '%s' not found", device_id); - goto fail; - } - - con = qemu_console_lookup_by_device(dev, head); - if (con == NULL) { - error_setg(errp, "Device %s is not bound to a QemuConsole", - device_id); + con = qemu_console_lookup_by_device_name(device_id, head, &err); + if (err) { + error_propagate(errp, err); goto fail; } } else { @@ -3671,56 +3758,55 @@ void vnc_display_open(const char *id, Error **errp) if (reverse) { /* connect to viewer */ - int csock; - vs->lsock = -1; - vs->lwebsock = -1; - if (strncmp(vnc, "unix:", 5) == 0) { - csock = unix_connect(vnc+5, errp); - } else { - csock = inet_connect(vnc, errp); + QIOChannelSocket *sioc = NULL; + vs->lsock = NULL; + vs->lwebsock = NULL; + if (vs->ws_enabled) { + error_setg(errp, "Cannot use websockets in reverse mode"); + goto fail; } - if (csock < 0) { + vs->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; + sioc = qio_channel_socket_new(); + if (qio_channel_socket_connect_sync(sioc, saddr, errp) < 0) { goto fail; } - vnc_connect(vs, csock, false, false); + vnc_connect(vs, sioc, false, false); + object_unref(OBJECT(sioc)); } else { - /* listen for connects */ - if (strncmp(vnc, "unix:", 5) == 0) { - vs->lsock = unix_listen(vnc+5, NULL, 0, errp); - if (vs->lsock < 0) { - goto fail; - } - vs->is_unix = true; - } else { - vs->lsock = inet_listen_opts(sopts, 5900, errp); - if (vs->lsock < 0) { + vs->lsock = qio_channel_socket_new(); + if (qio_channel_socket_listen_sync(vs->lsock, saddr, errp) < 0) { + goto fail; + } + vs->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; + vs->enabled = true; + + if (vs->ws_enabled) { + vs->lwebsock = qio_channel_socket_new(); + if (qio_channel_socket_listen_sync(vs->lwebsock, + wsaddr, errp) < 0) { + object_unref(OBJECT(vs->lsock)); + vs->lsock = NULL; goto fail; } - if (vs->ws_enabled) { - vs->lwebsock = inet_listen_opts(wsopts, 0, errp); - if (vs->lwebsock < 0) { - if (vs->lsock != -1) { - close(vs->lsock); - vs->lsock = -1; - } - goto fail; - } - } } - vs->enabled = true; - qemu_set_fd_handler(vs->lsock, vnc_listen_regular_read, NULL, vs); + + vs->lsock_tag = qio_channel_add_watch( + QIO_CHANNEL(vs->lsock), + G_IO_IN, vnc_listen_io, vs, NULL); if (vs->ws_enabled) { - qemu_set_fd_handler(vs->lwebsock, vnc_listen_websocket_read, - NULL, vs); + vs->lwebsock_tag = qio_channel_add_watch( + QIO_CHANNEL(vs->lwebsock), + G_IO_IN, vnc_listen_io, vs, NULL); } } - qemu_opts_del(sopts); - qemu_opts_del(wsopts); + + qapi_free_SocketAddress(saddr); + qapi_free_SocketAddress(wsaddr); return; fail: - qemu_opts_del(sopts); - qemu_opts_del(wsopts); + qapi_free_SocketAddress(saddr); + qapi_free_SocketAddress(wsaddr); vs->enabled = false; vs->ws_enabled = false; } @@ -3728,11 +3814,17 @@ fail: void vnc_display_add_client(const char *id, int csock, bool skipauth) { VncDisplay *vs = vnc_display_find(id); + QIOChannelSocket *sioc; if (!vs) { return; } - vnc_connect(vs, csock, skipauth, false); + + sioc = qio_channel_socket_new_fd(csock, NULL); + if (sioc) { + vnc_connect(vs, sioc, skipauth, false); + object_unref(OBJECT(sioc)); + } } static void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts) @@ -3775,9 +3867,7 @@ int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp) vnc_display_init(id); vnc_display_open(id, &local_err); if (local_err != NULL) { - error_report("Failed to start VNC server: %s", - error_get_pretty(local_err)); - error_free(local_err); + error_reportf_err(local_err, "Failed to start VNC server: "); exit(1); } return 0; @@ -3787,4 +3877,4 @@ static void vnc_register_config(void) { qemu_add_opts(&qemu_vnc_opts); } -machine_init(vnc_register_config); +opts_init(vnc_register_config); diff --git a/qemu/ui/vnc.h b/qemu/ui/vnc.h index 814d720df..81a326116 100644 --- a/qemu/ui/vnc.h +++ b/qemu/ui/vnc.h @@ -33,8 +33,11 @@ #include "ui/console.h" #include "audio/audio.h" #include "qemu/bitmap.h" +#include "crypto/tlssession.h" +#include "qemu/buffer.h" +#include "io/channel-socket.h" +#include "io/channel-tls.h" #include -#include #include "keymaps.h" #include "vnc-palette.h" @@ -55,13 +58,6 @@ * *****************************************************************************/ -typedef struct Buffer -{ - size_t capacity; - size_t offset; - uint8_t *buffer; -} Buffer; - typedef struct VncState VncState; typedef struct VncJob VncJob; typedef struct VncRect VncRect; @@ -101,10 +97,7 @@ typedef void VncSendHextileTile(VncState *vs, typedef struct VncDisplay VncDisplay; -#ifdef CONFIG_VNC_TLS -#include "vnc-tls.h" #include "vnc-auth-vencrypt.h" -#endif #ifdef CONFIG_VNC_SASL #include "vnc-auth-sasl.h" #endif @@ -153,8 +146,10 @@ struct VncDisplay int num_exclusive; int connections_limit; VncSharePolicy share_policy; - int lsock; - int lwebsock; + QIOChannelSocket *lsock; + guint lsock_tag; + QIOChannelSocket *lwebsock; + guint lwebsock_tag; bool ws_enabled; DisplaySurface *ds; DisplayChangeListener dcl; @@ -181,9 +176,8 @@ struct VncDisplay bool ws_tls; /* Used by websockets */ bool lossy; bool non_adaptive; -#ifdef CONFIG_VNC_TLS - VncDisplayTLS tls; -#endif + QCryptoTLSCreds *tlscreds; + char *tlsaclname; #ifdef CONFIG_VNC_SASL VncDisplaySASL sasl; #endif @@ -257,7 +251,10 @@ struct VncJob struct VncState { - int csock; + QIOChannelSocket *sioc; /* The underlying socket */ + QIOChannel *ioc; /* The channel currently used for I/O */ + guint ioc_tag; + gboolean disconnecting; DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT], VNC_DIRTY_BITS); uint8_t **lossy_rect; /* Not an Array to avoid costly memcpy in @@ -284,9 +281,7 @@ struct VncState int auth; int subauth; /* Used by VeNCrypt */ char challenge[VNC_AUTH_CHALLENGE_SIZE]; -#ifdef CONFIG_VNC_TLS - VncStateTLS tls; -#endif + QCryptoTLSSession *tls; /* Borrowed pointer from channel, don't free */ #ifdef CONFIG_VNC_SASL VncStateSASL sasl; #endif @@ -297,10 +292,6 @@ struct VncState Buffer output; Buffer input; - Buffer ws_input; - Buffer ws_output; - size_t ws_payload_remain; - WsMask ws_payload_mask; /* current output mode information */ VncWritePixels *write_pixels; PixelFormat client_pf; @@ -510,11 +501,12 @@ enum { *****************************************************************************/ /* Event loop functions */ -void vnc_client_read(void *opaque); -void vnc_client_write(void *opaque); +gboolean vnc_client_io(QIOChannel *ioc, + GIOCondition condition, + void *opaque); -long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen); -long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen); +ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen); +ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen); /* Protocol I/O functions */ void vnc_write(VncState *vs, const void *data, size_t len); @@ -533,25 +525,14 @@ uint32_t read_u32(uint8_t *data, size_t offset); /* Protocol stage functions */ void vnc_client_error(VncState *vs); -int vnc_client_io_error(VncState *vs, int ret, int last_errno); +ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp); void start_client_init(VncState *vs); void start_auth_vnc(VncState *vs); -/* Buffer management */ -void buffer_reserve(Buffer *buffer, size_t len); -void buffer_reset(Buffer *buffer); -void buffer_free(Buffer *buffer); -void buffer_append(Buffer *buffer, const void *data, size_t len); -void buffer_advance(Buffer *buf, size_t len); -uint8_t *buffer_end(Buffer *buffer); - /* Misc helpers */ -char *vnc_socket_local_addr(const char *format, int fd); -char *vnc_socket_remote_addr(const char *format, int fd); - static inline uint32_t vnc_has_feature(VncState *vs, int feature) { return (vs->features & (1 << feature)); } diff --git a/qemu/ui/x_keymap.c b/qemu/ui/x_keymap.c index 1a773174f..27884851d 100644 --- a/qemu/ui/x_keymap.c +++ b/qemu/ui/x_keymap.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "x_keymap.h" diff --git a/qemu/user-exec.c b/qemu/user-exec.c index ed9a07f15..d8d597baf 100644 --- a/qemu/user-exec.c +++ b/qemu/user-exec.c @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" +#include "qemu/osdep.h" #include "cpu.h" #include "disas/disas.h" #include "tcg.h" @@ -33,7 +33,6 @@ #undef ESI #undef EDI #undef EIP -#include #ifdef __linux__ #include #endif @@ -92,8 +91,8 @@ static inline int handle_cpu_signal(uintptr_t pc, unsigned long address, int ret; #if defined(DEBUG_SIGNAL) - qemu_printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", - pc, address, is_write, *(unsigned long *)old_set); + printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", + pc, address, is_write, *(unsigned long *)old_set); #endif /* XXX: locking issue */ if (is_write && h2g_valid(address) diff --git a/qemu/util/Makefile.objs b/qemu/util/Makefile.objs index 114d6578c..a8a777ec4 100644 --- a/qemu/util/Makefile.objs +++ b/qemu/util/Makefile.objs @@ -1,13 +1,20 @@ util-obj-y = osdep.o cutils.o unicode.o qemu-timer-common.o -util-obj-$(CONFIG_WIN32) += oslib-win32.o qemu-thread-win32.o event_notifier-win32.o -util-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o event_notifier-posix.o qemu-openpty.o +util-obj-$(CONFIG_POSIX) += compatfd.o +util-obj-$(CONFIG_POSIX) += event_notifier-posix.o +util-obj-$(CONFIG_POSIX) += mmap-alloc.o +util-obj-$(CONFIG_POSIX) += oslib-posix.o +util-obj-$(CONFIG_POSIX) += qemu-openpty.o +util-obj-$(CONFIG_POSIX) += qemu-thread-posix.o +util-obj-$(CONFIG_WIN32) += event_notifier-win32.o +util-obj-$(CONFIG_POSIX) += memfd.o +util-obj-$(CONFIG_WIN32) += oslib-win32.o +util-obj-$(CONFIG_WIN32) += qemu-thread-win32.o util-obj-y += envlist.o path.o module.o util-obj-$(call lnot,$(CONFIG_INT128)) += host-utils.o util-obj-y += bitmap.o bitops.o hbitmap.o util-obj-y += fifo8.o util-obj-y += acl.o util-obj-y += error.o qemu-error.o -util-obj-$(CONFIG_POSIX) += compatfd.o util-obj-y += id.o util-obj-y += iov.o qemu-config.o qemu-sockets.o uri.o notify.o util-obj-y += qemu-option.o qemu-progress.o @@ -18,3 +25,10 @@ util-obj-y += getauxval.o util-obj-y += readline.o util-obj-y += rfifolock.o util-obj-y += rcu.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 +util-obj-y += buffer.o +util-obj-y += timed-average.o +util-obj-y += base64.o +util-obj-y += log.o diff --git a/qemu/util/acl.c b/qemu/util/acl.c index 571d68615..723b6a89b 100644 --- a/qemu/util/acl.c +++ b/qemu/util/acl.c @@ -23,6 +23,7 @@ */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/acl.h" diff --git a/qemu/util/base64.c b/qemu/util/base64.c new file mode 100644 index 000000000..9d3c46cbc --- /dev/null +++ b/qemu/util/base64.c @@ -0,0 +1,60 @@ +/* + * QEMU base64 helpers + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/base64.h" + +static const char *base64_valid_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n"; + +uint8_t *qbase64_decode(const char *input, + size_t in_len, + size_t *out_len, + Error **errp) +{ + *out_len = 0; + + if (in_len != -1) { + /* Lack of NUL terminator is an error */ + if (input[in_len] != '\0') { + error_setg(errp, "Base64 data is not NUL terminated"); + return NULL; + } + /* Check there's no NULs embedded since we expect + * this to be valid base64 data */ + if (memchr(input, '\0', in_len) != NULL) { + error_setg(errp, "Base64 data contains embedded NUL characters"); + return NULL; + } + + /* Now we know its a valid nul terminated string + * strspn is safe to use... */ + } else { + in_len = strlen(input); + } + + if (strspn(input, base64_valid_chars) != in_len) { + error_setg(errp, "Base64 data contains invalid characters"); + return NULL; + } + + return g_base64_decode(input, out_len); +} diff --git a/qemu/util/bitmap.c b/qemu/util/bitmap.c index 300a68e38..40aadfb4f 100644 --- a/qemu/util/bitmap.c +++ b/qemu/util/bitmap.c @@ -9,12 +9,13 @@ * Version 2. */ +#include "qemu/osdep.h" #include "qemu/bitops.h" #include "qemu/bitmap.h" #include "qemu/atomic.h" /* - * bitmaps provide an array of bits, implemented using an an + * bitmaps provide an array of bits, implemented using an * array of unsigned longs. The number of valid bits in a * given bitmap does _not_ need to be an exact multiple of * BITS_PER_LONG. diff --git a/qemu/util/bitops.c b/qemu/util/bitops.c index 227c38b88..b0c35dd5f 100644 --- a/qemu/util/bitops.c +++ b/qemu/util/bitops.c @@ -11,6 +11,7 @@ * 2 of the License, or (at your option) any later version. */ +#include "qemu/osdep.h" #include "qemu/bitops.h" #define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) diff --git a/qemu/util/buffer.c b/qemu/util/buffer.c new file mode 100644 index 000000000..a6118bf5b --- /dev/null +++ b/qemu/util/buffer.c @@ -0,0 +1,172 @@ +/* + * QEMU generic buffers + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 . + * + */ + +#include "qemu/osdep.h" +#include "qemu/buffer.h" +#include "trace.h" + +#define BUFFER_MIN_INIT_SIZE 4096 +#define BUFFER_MIN_SHRINK_SIZE 65536 + +/* define the factor alpha for the expentional smoothing + * that is used in the average size calculation. a shift + * of 7 results in an alpha of 1/2^7. */ +#define BUFFER_AVG_SIZE_SHIFT 7 + +static size_t buffer_req_size(Buffer *buffer, size_t len) +{ + return MAX(BUFFER_MIN_INIT_SIZE, + pow2ceil(buffer->offset + len)); +} + +static void buffer_adj_size(Buffer *buffer, size_t len) +{ + size_t old = buffer->capacity; + buffer->capacity = buffer_req_size(buffer, len); + buffer->buffer = g_realloc(buffer->buffer, buffer->capacity); + trace_buffer_resize(buffer->name ?: "unnamed", + old, buffer->capacity); + + /* make it even harder for the buffer to shrink, reset average size + * to currenty capacity if it is larger than the average. */ + buffer->avg_size = MAX(buffer->avg_size, + buffer->capacity << BUFFER_AVG_SIZE_SHIFT); +} + +void buffer_init(Buffer *buffer, const char *name, ...) +{ + va_list ap; + + va_start(ap, name); + buffer->name = g_strdup_vprintf(name, ap); + va_end(ap); +} + +static uint64_t buffer_get_avg_size(Buffer *buffer) +{ + return buffer->avg_size >> BUFFER_AVG_SIZE_SHIFT; +} + +void buffer_shrink(Buffer *buffer) +{ + size_t new; + + /* Calculate the average size of the buffer as + * avg_size = avg_size * ( 1 - a ) + required_size * a + * where a is 1 / 2 ^ BUFFER_AVG_SIZE_SHIFT. */ + buffer->avg_size *= (1 << BUFFER_AVG_SIZE_SHIFT) - 1; + buffer->avg_size >>= BUFFER_AVG_SIZE_SHIFT; + buffer->avg_size += buffer_req_size(buffer, 0); + + /* And then only shrink if the average size of the buffer is much + * too big, to avoid bumping up & down the buffers all the time. + * realloc() isn't exactly cheap ... */ + new = buffer_req_size(buffer, buffer_get_avg_size(buffer)); + if (new < buffer->capacity >> 3 && + new >= BUFFER_MIN_SHRINK_SIZE) { + buffer_adj_size(buffer, buffer_get_avg_size(buffer)); + } + + buffer_adj_size(buffer, 0); +} + +void buffer_reserve(Buffer *buffer, size_t len) +{ + if ((buffer->capacity - buffer->offset) < len) { + buffer_adj_size(buffer, len); + } +} + +gboolean buffer_empty(Buffer *buffer) +{ + return buffer->offset == 0; +} + +uint8_t *buffer_end(Buffer *buffer) +{ + return buffer->buffer + buffer->offset; +} + +void buffer_reset(Buffer *buffer) +{ + buffer->offset = 0; + buffer_shrink(buffer); +} + +void buffer_free(Buffer *buffer) +{ + trace_buffer_free(buffer->name ?: "unnamed", buffer->capacity); + g_free(buffer->buffer); + g_free(buffer->name); + buffer->offset = 0; + buffer->capacity = 0; + buffer->buffer = NULL; + buffer->name = NULL; +} + +void buffer_append(Buffer *buffer, const void *data, size_t len) +{ + memcpy(buffer->buffer + buffer->offset, data, len); + buffer->offset += len; +} + +void buffer_advance(Buffer *buffer, size_t len) +{ + memmove(buffer->buffer, buffer->buffer + len, + (buffer->offset - len)); + buffer->offset -= len; + buffer_shrink(buffer); +} + +void buffer_move_empty(Buffer *to, Buffer *from) +{ + trace_buffer_move_empty(to->name ?: "unnamed", + from->offset, + from->name ?: "unnamed"); + assert(to->offset == 0); + + g_free(to->buffer); + to->offset = from->offset; + to->capacity = from->capacity; + to->buffer = from->buffer; + + from->offset = 0; + from->capacity = 0; + from->buffer = NULL; +} + +void buffer_move(Buffer *to, Buffer *from) +{ + if (to->offset == 0) { + buffer_move_empty(to, from); + return; + } + + trace_buffer_move(to->name ?: "unnamed", + from->offset, + from->name ?: "unnamed"); + buffer_reserve(to, from->offset); + buffer_append(to, from->buffer, from->offset); + + g_free(from->buffer); + from->offset = 0; + from->capacity = 0; + from->buffer = NULL; +} diff --git a/qemu/util/compatfd.c b/qemu/util/compatfd.c index e8571502b..9a43042ae 100644 --- a/qemu/util/compatfd.c +++ b/qemu/util/compatfd.c @@ -13,6 +13,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/compatfd.h" #include "qemu/thread.h" diff --git a/qemu/util/coroutine-gthread.c b/qemu/util/coroutine-gthread.c new file mode 100644 index 000000000..fb697eb0b --- /dev/null +++ b/qemu/util/coroutine-gthread.c @@ -0,0 +1,199 @@ +/* + * GThread coroutine initialization code + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2011 Aneesh Kumar K.V + * + * 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.0 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 . + */ + +#include "qemu/osdep.h" +#include +#include "qemu-common.h" +#include "qemu/coroutine_int.h" + +typedef struct { + Coroutine base; + GThread *thread; + bool runnable; + bool free_on_thread_exit; + CoroutineAction action; +} CoroutineGThread; + +static CompatGMutex coroutine_lock; +static CompatGCond coroutine_cond; + +/* GLib 2.31 and beyond deprecated various parts of the thread API, + * but the new interfaces are not available in older GLib versions + * so we have to cope with both. + */ +#if GLIB_CHECK_VERSION(2, 31, 0) +/* Awkwardly, the GPrivate API doesn't provide a way to update the + * GDestroyNotify handler for the coroutine key dynamically. So instead + * we track whether or not the CoroutineGThread should be freed on + * thread exit / coroutine key update using the free_on_thread_exit + * field. + */ +static void coroutine_destroy_notify(gpointer data) +{ + CoroutineGThread *co = data; + if (co && co->free_on_thread_exit) { + g_free(co); + } +} + +static GPrivate coroutine_key = G_PRIVATE_INIT(coroutine_destroy_notify); + +static inline CoroutineGThread *get_coroutine_key(void) +{ + return g_private_get(&coroutine_key); +} + +static inline void set_coroutine_key(CoroutineGThread *co, + bool free_on_thread_exit) +{ + /* Unlike g_static_private_set() this does not call the GDestroyNotify + * if the previous value of the key was NULL. Fortunately we only need + * the GDestroyNotify in the non-NULL key case. + */ + co->free_on_thread_exit = free_on_thread_exit; + g_private_replace(&coroutine_key, co); +} + +static inline GThread *create_thread(GThreadFunc func, gpointer data) +{ + return g_thread_new("coroutine", func, data); +} + +#else + +/* Handle older GLib versions */ + +static GStaticPrivate coroutine_key = G_STATIC_PRIVATE_INIT; + +static inline CoroutineGThread *get_coroutine_key(void) +{ + return g_static_private_get(&coroutine_key); +} + +static inline void set_coroutine_key(CoroutineGThread *co, + bool free_on_thread_exit) +{ + g_static_private_set(&coroutine_key, co, + free_on_thread_exit ? (GDestroyNotify)g_free : NULL); +} + +static inline GThread *create_thread(GThreadFunc func, gpointer data) +{ + return g_thread_create_full(func, data, 0, TRUE, TRUE, + G_THREAD_PRIORITY_NORMAL, NULL); +} + +#endif + + +static void __attribute__((constructor)) coroutine_init(void) +{ +#if !GLIB_CHECK_VERSION(2, 31, 0) + if (!g_thread_supported()) { + g_thread_init(NULL); + } +#endif +} + +static void coroutine_wait_runnable_locked(CoroutineGThread *co) +{ + while (!co->runnable) { + g_cond_wait(&coroutine_cond, &coroutine_lock); + } +} + +static void coroutine_wait_runnable(CoroutineGThread *co) +{ + g_mutex_lock(&coroutine_lock); + coroutine_wait_runnable_locked(co); + g_mutex_unlock(&coroutine_lock); +} + +static gpointer coroutine_thread(gpointer opaque) +{ + CoroutineGThread *co = opaque; + + set_coroutine_key(co, false); + coroutine_wait_runnable(co); + co->base.entry(co->base.entry_arg); + qemu_coroutine_switch(&co->base, co->base.caller, COROUTINE_TERMINATE); + return NULL; +} + +Coroutine *qemu_coroutine_new(void) +{ + CoroutineGThread *co; + + co = g_malloc0(sizeof(*co)); + co->thread = create_thread(coroutine_thread, co); + if (!co->thread) { + g_free(co); + return NULL; + } + return &co->base; +} + +void qemu_coroutine_delete(Coroutine *co_) +{ + CoroutineGThread *co = DO_UPCAST(CoroutineGThread, base, co_); + + g_thread_join(co->thread); + g_free(co); +} + +CoroutineAction qemu_coroutine_switch(Coroutine *from_, + Coroutine *to_, + CoroutineAction action) +{ + CoroutineGThread *from = DO_UPCAST(CoroutineGThread, base, from_); + CoroutineGThread *to = DO_UPCAST(CoroutineGThread, base, to_); + + g_mutex_lock(&coroutine_lock); + from->runnable = false; + from->action = action; + to->runnable = true; + to->action = action; + g_cond_broadcast(&coroutine_cond); + + if (action != COROUTINE_TERMINATE) { + coroutine_wait_runnable_locked(from); + } + g_mutex_unlock(&coroutine_lock); + return from->action; +} + +Coroutine *qemu_coroutine_self(void) +{ + CoroutineGThread *co = get_coroutine_key(); + if (!co) { + co = g_malloc0(sizeof(*co)); + co->runnable = true; + set_coroutine_key(co, true); + } + + return &co->base; +} + +bool qemu_in_coroutine(void) +{ + CoroutineGThread *co = get_coroutine_key(); + + return co && co->base.caller; +} diff --git a/qemu/util/coroutine-sigaltstack.c b/qemu/util/coroutine-sigaltstack.c new file mode 100644 index 000000000..a7c336655 --- /dev/null +++ b/qemu/util/coroutine-sigaltstack.c @@ -0,0 +1,290 @@ +/* + * sigaltstack coroutine initialization code + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2011 Kevin Wolf + * Copyright (C) 2012 Alex Barcelo +** This file is partly based on pth_mctx.c, from the GNU Portable Threads +** Copyright (c) 1999-2006 Ralf S. Engelschall + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +/* XXX Is there a nicer way to disable glibc's stack check for longjmp? */ +#ifdef _FORTIFY_SOURCE +#undef _FORTIFY_SOURCE +#endif +#include "qemu/osdep.h" +#include +#include "qemu-common.h" +#include "qemu/coroutine_int.h" + +typedef struct { + Coroutine base; + void *stack; + sigjmp_buf env; +} CoroutineUContext; + +/** + * Per-thread coroutine bookkeeping + */ +typedef struct { + /** Currently executing coroutine */ + Coroutine *current; + + /** The default coroutine */ + CoroutineUContext leader; + + /** Information for the signal handler (trampoline) */ + sigjmp_buf tr_reenter; + volatile sig_atomic_t tr_called; + void *tr_handler; +} CoroutineThreadState; + +static pthread_key_t thread_state_key; + +static CoroutineThreadState *coroutine_get_thread_state(void) +{ + CoroutineThreadState *s = pthread_getspecific(thread_state_key); + + if (!s) { + s = g_malloc0(sizeof(*s)); + s->current = &s->leader.base; + pthread_setspecific(thread_state_key, s); + } + return s; +} + +static void qemu_coroutine_thread_cleanup(void *opaque) +{ + CoroutineThreadState *s = opaque; + + g_free(s); +} + +static void __attribute__((constructor)) coroutine_init(void) +{ + int ret; + + ret = pthread_key_create(&thread_state_key, qemu_coroutine_thread_cleanup); + if (ret != 0) { + fprintf(stderr, "unable to create leader key: %s\n", strerror(errno)); + abort(); + } +} + +/* "boot" function + * This is what starts the coroutine, is called from the trampoline + * (from the signal handler when it is not signal handling, read ahead + * for more information). + */ +static void coroutine_bootstrap(CoroutineUContext *self, Coroutine *co) +{ + /* Initialize longjmp environment and switch back the caller */ + if (!sigsetjmp(self->env, 0)) { + siglongjmp(*(sigjmp_buf *)co->entry_arg, 1); + } + + while (true) { + co->entry(co->entry_arg); + qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); + } +} + +/* + * This is used as the signal handler. This is called with the brand new stack + * (thanks to sigaltstack). We have to return, given that this is a signal + * handler and the sigmask and some other things are changed. + */ +static void coroutine_trampoline(int signal) +{ + CoroutineUContext *self; + Coroutine *co; + CoroutineThreadState *coTS; + + /* Get the thread specific information */ + coTS = coroutine_get_thread_state(); + self = coTS->tr_handler; + coTS->tr_called = 1; + co = &self->base; + + /* + * Here we have to do a bit of a ping pong between the caller, given that + * this is a signal handler and we have to do a return "soon". Then the + * caller can reestablish everything and do a siglongjmp here again. + */ + if (!sigsetjmp(coTS->tr_reenter, 0)) { + return; + } + + /* + * Ok, the caller has siglongjmp'ed back to us, so now prepare + * us for the real machine state switching. We have to jump + * into another function here to get a new stack context for + * the auto variables (which have to be auto-variables + * because the start of the thread happens later). Else with + * PIC (i.e. Position Independent Code which is used when PTH + * is built as a shared library) most platforms would + * horrible core dump as experience showed. + */ + coroutine_bootstrap(self, co); +} + +Coroutine *qemu_coroutine_new(void) +{ + const size_t stack_size = 1 << 20; + CoroutineUContext *co; + CoroutineThreadState *coTS; + struct sigaction sa; + struct sigaction osa; + stack_t ss; + stack_t oss; + sigset_t sigs; + sigset_t osigs; + sigjmp_buf old_env; + + /* The way to manipulate stack is with the sigaltstack function. We + * prepare a stack, with it delivering a signal to ourselves and then + * put sigsetjmp/siglongjmp where needed. + * This has been done keeping coroutine-ucontext as a model and with the + * pth ideas (GNU Portable Threads). See coroutine-ucontext for the basics + * of the coroutines and see pth_mctx.c (from the pth project) for the + * sigaltstack way of manipulating stacks. + */ + + co = g_malloc0(sizeof(*co)); + co->stack = g_malloc(stack_size); + co->base.entry_arg = &old_env; /* stash away our jmp_buf */ + + coTS = coroutine_get_thread_state(); + coTS->tr_handler = co; + + /* + * Preserve the SIGUSR2 signal state, block SIGUSR2, + * and establish our signal handler. The signal will + * later transfer control onto the signal stack. + */ + sigemptyset(&sigs); + sigaddset(&sigs, SIGUSR2); + pthread_sigmask(SIG_BLOCK, &sigs, &osigs); + sa.sa_handler = coroutine_trampoline; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_ONSTACK; + if (sigaction(SIGUSR2, &sa, &osa) != 0) { + abort(); + } + + /* + * Set the new stack. + */ + ss.ss_sp = co->stack; + ss.ss_size = stack_size; + ss.ss_flags = 0; + if (sigaltstack(&ss, &oss) < 0) { + abort(); + } + + /* + * Now transfer control onto the signal stack and set it up. + * It will return immediately via "return" after the sigsetjmp() + * was performed. Be careful here with race conditions. The + * signal can be delivered the first time sigsuspend() is + * called. + */ + coTS->tr_called = 0; + pthread_kill(pthread_self(), SIGUSR2); + sigfillset(&sigs); + sigdelset(&sigs, SIGUSR2); + while (!coTS->tr_called) { + sigsuspend(&sigs); + } + + /* + * Inform the system that we are back off the signal stack by + * removing the alternative signal stack. Be careful here: It + * first has to be disabled, before it can be removed. + */ + sigaltstack(NULL, &ss); + ss.ss_flags = SS_DISABLE; + if (sigaltstack(&ss, NULL) < 0) { + abort(); + } + sigaltstack(NULL, &ss); + if (!(oss.ss_flags & SS_DISABLE)) { + sigaltstack(&oss, NULL); + } + + /* + * Restore the old SIGUSR2 signal handler and mask + */ + sigaction(SIGUSR2, &osa, NULL); + pthread_sigmask(SIG_SETMASK, &osigs, NULL); + + /* + * Now enter the trampoline again, but this time not as a signal + * handler. Instead we jump into it directly. The functionally + * redundant ping-pong pointer arithmetic is necessary to avoid + * type-conversion warnings related to the `volatile' qualifier and + * the fact that `jmp_buf' usually is an array type. + */ + if (!sigsetjmp(old_env, 0)) { + siglongjmp(coTS->tr_reenter, 1); + } + + /* + * Ok, we returned again, so now we're finished + */ + + return &co->base; +} + +void qemu_coroutine_delete(Coroutine *co_) +{ + CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_); + + g_free(co->stack); + g_free(co); +} + +CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, + CoroutineAction action) +{ + CoroutineUContext *from = DO_UPCAST(CoroutineUContext, base, from_); + CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, to_); + CoroutineThreadState *s = coroutine_get_thread_state(); + int ret; + + s->current = to_; + + ret = sigsetjmp(from->env, 0); + if (ret == 0) { + siglongjmp(to->env, action); + } + return ret; +} + +Coroutine *qemu_coroutine_self(void) +{ + CoroutineThreadState *s = coroutine_get_thread_state(); + + return s->current; +} + +bool qemu_in_coroutine(void) +{ + CoroutineThreadState *s = pthread_getspecific(thread_state_key); + + return s && s->current->caller; +} + diff --git a/qemu/util/coroutine-ucontext.c b/qemu/util/coroutine-ucontext.c new file mode 100644 index 000000000..2bb7e10d4 --- /dev/null +++ b/qemu/util/coroutine-ucontext.c @@ -0,0 +1,192 @@ +/* + * ucontext coroutine initialization code + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2011 Kevin Wolf + * + * 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.0 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 . + */ + +/* XXX Is there a nicer way to disable glibc's stack check for longjmp? */ +#ifdef _FORTIFY_SOURCE +#undef _FORTIFY_SOURCE +#endif +#include "qemu/osdep.h" +#include +#include "qemu-common.h" +#include "qemu/coroutine_int.h" + +#ifdef CONFIG_VALGRIND_H +#include +#endif + +typedef struct { + Coroutine base; + void *stack; + sigjmp_buf env; + +#ifdef CONFIG_VALGRIND_H + unsigned int valgrind_stack_id; +#endif + +} CoroutineUContext; + +/** + * Per-thread coroutine bookkeeping + */ +static __thread CoroutineUContext leader; +static __thread Coroutine *current; + +/* + * va_args to makecontext() must be type 'int', so passing + * the pointer we need may require several int args. This + * union is a quick hack to let us do that + */ +union cc_arg { + void *p; + int i[2]; +}; + +static void coroutine_trampoline(int i0, int i1) +{ + union cc_arg arg; + CoroutineUContext *self; + Coroutine *co; + + arg.i[0] = i0; + arg.i[1] = i1; + self = arg.p; + co = &self->base; + + /* Initialize longjmp environment and switch back the caller */ + if (!sigsetjmp(self->env, 0)) { + siglongjmp(*(sigjmp_buf *)co->entry_arg, 1); + } + + while (true) { + co->entry(co->entry_arg); + qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); + } +} + +Coroutine *qemu_coroutine_new(void) +{ + const size_t stack_size = 1 << 20; + CoroutineUContext *co; + ucontext_t old_uc, uc; + sigjmp_buf old_env; + union cc_arg arg = {0}; + + /* The ucontext functions preserve signal masks which incurs a + * system call overhead. sigsetjmp(buf, 0)/siglongjmp() does not + * preserve signal masks but only works on the current stack. + * Since we need a way to create and switch to a new stack, use + * the ucontext functions for that but sigsetjmp()/siglongjmp() for + * everything else. + */ + + if (getcontext(&uc) == -1) { + abort(); + } + + co = g_malloc0(sizeof(*co)); + co->stack = g_malloc(stack_size); + co->base.entry_arg = &old_env; /* stash away our jmp_buf */ + + uc.uc_link = &old_uc; + uc.uc_stack.ss_sp = co->stack; + uc.uc_stack.ss_size = stack_size; + uc.uc_stack.ss_flags = 0; + +#ifdef CONFIG_VALGRIND_H + co->valgrind_stack_id = + VALGRIND_STACK_REGISTER(co->stack, co->stack + stack_size); +#endif + + arg.p = co; + + makecontext(&uc, (void (*)(void))coroutine_trampoline, + 2, arg.i[0], arg.i[1]); + + /* swapcontext() in, siglongjmp() back out */ + if (!sigsetjmp(old_env, 0)) { + swapcontext(&old_uc, &uc); + } + return &co->base; +} + +#ifdef CONFIG_VALGRIND_H +#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE +/* Work around an unused variable in the valgrind.h macro... */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#endif +static inline void valgrind_stack_deregister(CoroutineUContext *co) +{ + VALGRIND_STACK_DEREGISTER(co->valgrind_stack_id); +} +#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE +#pragma GCC diagnostic pop +#endif +#endif + +void qemu_coroutine_delete(Coroutine *co_) +{ + CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_); + +#ifdef CONFIG_VALGRIND_H + valgrind_stack_deregister(co); +#endif + + g_free(co->stack); + g_free(co); +} + +/* This function is marked noinline to prevent GCC from inlining it + * into coroutine_trampoline(). If we allow it to do that then it + * hoists the code to get the address of the TLS variable "current" + * out of the while() loop. This is an invalid transformation because + * the sigsetjmp() call may be called when running thread A but + * return in thread B, and so we might be in a different thread + * context each time round the loop. + */ +CoroutineAction __attribute__((noinline)) +qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, + CoroutineAction action) +{ + CoroutineUContext *from = DO_UPCAST(CoroutineUContext, base, from_); + CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, to_); + int ret; + + current = to_; + + ret = sigsetjmp(from->env, 0); + if (ret == 0) { + siglongjmp(to->env, action); + } + return ret; +} + +Coroutine *qemu_coroutine_self(void) +{ + if (!current) { + current = &leader.base; + } + return current; +} + +bool qemu_in_coroutine(void) +{ + return current && current->caller; +} diff --git a/qemu/util/coroutine-win32.c b/qemu/util/coroutine-win32.c new file mode 100644 index 000000000..02e28e825 --- /dev/null +++ b/qemu/util/coroutine-win32.c @@ -0,0 +1,102 @@ +/* + * Win32 coroutine initialization code + * + * Copyright (c) 2011 Kevin Wolf + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/coroutine_int.h" + +typedef struct +{ + Coroutine base; + + LPVOID fiber; + CoroutineAction action; +} CoroutineWin32; + +static __thread CoroutineWin32 leader; +static __thread Coroutine *current; + +/* This function is marked noinline to prevent GCC from inlining it + * into coroutine_trampoline(). If we allow it to do that then it + * hoists the code to get the address of the TLS variable "current" + * out of the while() loop. This is an invalid transformation because + * the SwitchToFiber() call may be called when running thread A but + * return in thread B, and so we might be in a different thread + * context each time round the loop. + */ +CoroutineAction __attribute__((noinline)) +qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, + CoroutineAction action) +{ + CoroutineWin32 *from = DO_UPCAST(CoroutineWin32, base, from_); + CoroutineWin32 *to = DO_UPCAST(CoroutineWin32, base, to_); + + current = to_; + + to->action = action; + SwitchToFiber(to->fiber); + return from->action; +} + +static void CALLBACK coroutine_trampoline(void *co_) +{ + Coroutine *co = co_; + + while (true) { + co->entry(co->entry_arg); + qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); + } +} + +Coroutine *qemu_coroutine_new(void) +{ + const size_t stack_size = 1 << 20; + CoroutineWin32 *co; + + co = g_malloc0(sizeof(*co)); + co->fiber = CreateFiber(stack_size, coroutine_trampoline, &co->base); + return &co->base; +} + +void qemu_coroutine_delete(Coroutine *co_) +{ + CoroutineWin32 *co = DO_UPCAST(CoroutineWin32, base, co_); + + DeleteFiber(co->fiber); + g_free(co); +} + +Coroutine *qemu_coroutine_self(void) +{ + if (!current) { + current = &leader.base; + leader.fiber = ConvertThreadToFiber(NULL); + } + return current; +} + +bool qemu_in_coroutine(void) +{ + return current && current->caller; +} diff --git a/qemu/util/crc32c.c b/qemu/util/crc32c.c index 886632780..7e99555c1 100644 --- a/qemu/util/crc32c.c +++ b/qemu/util/crc32c.c @@ -25,6 +25,7 @@ * */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/crc32c.h" diff --git a/qemu/util/cutils.c b/qemu/util/cutils.c index 5d1c9ebe0..43d1afbbe 100644 --- a/qemu/util/cutils.c +++ b/qemu/util/cutils.c @@ -21,15 +21,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/host-utils.h" #include -#include -#include #include "qemu/sockets.h" #include "qemu/iov.h" #include "net/net.h" +#include "qemu/cutils.h" void strpadcpy(char *buf, int buf_size, const char *str, char pad) { @@ -145,11 +145,6 @@ time_t mktimegm(struct tm *tm) return t; } -int qemu_fls(int i) -{ - return 32 - clz32(i); -} - /* * Make sure data goes on disk, but if possible do not bother to * write out the inode just for timestamp updates. @@ -166,6 +161,46 @@ int qemu_fdatasync(int fd) #endif } +/* vector definitions */ +#ifdef __ALTIVEC__ +#include +/* The altivec.h header says we're allowed to undef these for + * C++ compatibility. Here we don't care about C++, but we + * undef them anyway to avoid namespace pollution. + */ +#undef vector +#undef pixel +#undef bool +#define VECTYPE __vector unsigned char +#define SPLAT(p) vec_splat(vec_ld(0, p), 0) +#define ALL_EQ(v1, v2) vec_all_eq(v1, v2) +#define VEC_OR(v1, v2) ((v1) | (v2)) +/* altivec.h may redefine the bool macro as vector type. + * Reset it to POSIX semantics. */ +#define bool _Bool +#elif defined __SSE2__ +#include +#define VECTYPE __m128i +#define SPLAT(p) _mm_set1_epi8(*(p)) +#define ALL_EQ(v1, v2) (_mm_movemask_epi8(_mm_cmpeq_epi8(v1, v2)) == 0xFFFF) +#define VEC_OR(v1, v2) (_mm_or_si128(v1, v2)) +#else +#define VECTYPE unsigned long +#define SPLAT(p) (*(p) * (~0UL / 255)) +#define ALL_EQ(v1, v2) ((v1) == (v2)) +#define VEC_OR(v1, v2) ((v1) | (v2)) +#endif + +#define BUFFER_FIND_NONZERO_OFFSET_UNROLL_FACTOR 8 + +static bool +can_use_buffer_find_nonzero_offset_inner(const void *buf, size_t len) +{ + return (len % (BUFFER_FIND_NONZERO_OFFSET_UNROLL_FACTOR + * sizeof(VECTYPE)) == 0 + && ((uintptr_t) buf) % sizeof(VECTYPE) == 0); +} + /* * Searches for an area with non-zero content in a buffer * @@ -174,8 +209,8 @@ int qemu_fdatasync(int fd) * and addr must be a multiple of sizeof(VECTYPE) due to * restriction of optimizations in this function. * - * can_use_buffer_find_nonzero_offset() can be used to check - * these requirements. + * can_use_buffer_find_nonzero_offset_inner() can be used to + * check these requirements. * * The return value is the offset of the non-zero area rounded * down to a multiple of sizeof(VECTYPE) for the first @@ -186,13 +221,13 @@ int qemu_fdatasync(int fd) * If the buffer is all zero the return value is equal to len. */ -size_t buffer_find_nonzero_offset(const void *buf, size_t len) +static size_t buffer_find_nonzero_offset_inner(const void *buf, size_t len) { const VECTYPE *p = buf; const VECTYPE zero = (VECTYPE){0}; size_t i; - assert(can_use_buffer_find_nonzero_offset(buf, len)); + assert(can_use_buffer_find_nonzero_offset_inner(buf, len)); if (!len) { return 0; @@ -221,6 +256,114 @@ size_t buffer_find_nonzero_offset(const void *buf, size_t len) return i * sizeof(VECTYPE); } +/* + * GCC before version 4.9 has a bug which will cause the target + * attribute work incorrectly and failed to compile in some case, + * restrict the gcc version to 4.9+ to prevent the failure. + */ + +#if defined CONFIG_AVX2_OPT && QEMU_GNUC_PREREQ(4, 9) +#pragma GCC push_options +#pragma GCC target("avx2") +#include +#include + +#define AVX2_VECTYPE __m256i +#define AVX2_SPLAT(p) _mm256_set1_epi8(*(p)) +#define AVX2_ALL_EQ(v1, v2) \ + (_mm256_movemask_epi8(_mm256_cmpeq_epi8(v1, v2)) == 0xFFFFFFFF) +#define AVX2_VEC_OR(v1, v2) (_mm256_or_si256(v1, v2)) + +static bool +can_use_buffer_find_nonzero_offset_avx2(const void *buf, size_t len) +{ + return (len % (BUFFER_FIND_NONZERO_OFFSET_UNROLL_FACTOR + * sizeof(AVX2_VECTYPE)) == 0 + && ((uintptr_t) buf) % sizeof(AVX2_VECTYPE) == 0); +} + +static size_t buffer_find_nonzero_offset_avx2(const void *buf, size_t len) +{ + const AVX2_VECTYPE *p = buf; + const AVX2_VECTYPE zero = (AVX2_VECTYPE){0}; + size_t i; + + assert(can_use_buffer_find_nonzero_offset_avx2(buf, len)); + + if (!len) { + return 0; + } + + for (i = 0; i < BUFFER_FIND_NONZERO_OFFSET_UNROLL_FACTOR; i++) { + if (!AVX2_ALL_EQ(p[i], zero)) { + return i * sizeof(AVX2_VECTYPE); + } + } + + for (i = BUFFER_FIND_NONZERO_OFFSET_UNROLL_FACTOR; + i < len / sizeof(AVX2_VECTYPE); + i += BUFFER_FIND_NONZERO_OFFSET_UNROLL_FACTOR) { + AVX2_VECTYPE tmp0 = AVX2_VEC_OR(p[i + 0], p[i + 1]); + AVX2_VECTYPE tmp1 = AVX2_VEC_OR(p[i + 2], p[i + 3]); + AVX2_VECTYPE tmp2 = AVX2_VEC_OR(p[i + 4], p[i + 5]); + AVX2_VECTYPE tmp3 = AVX2_VEC_OR(p[i + 6], p[i + 7]); + AVX2_VECTYPE tmp01 = AVX2_VEC_OR(tmp0, tmp1); + AVX2_VECTYPE tmp23 = AVX2_VEC_OR(tmp2, tmp3); + if (!AVX2_ALL_EQ(AVX2_VEC_OR(tmp01, tmp23), zero)) { + break; + } + } + + return i * sizeof(AVX2_VECTYPE); +} + +static bool avx2_support(void) +{ + int a, b, c, d; + + if (__get_cpuid_max(0, NULL) < 7) { + return false; + } + + __cpuid_count(7, 0, a, b, c, d); + + return b & bit_AVX2; +} + +bool can_use_buffer_find_nonzero_offset(const void *buf, size_t len) \ + __attribute__ ((ifunc("can_use_buffer_find_nonzero_offset_ifunc"))); +size_t buffer_find_nonzero_offset(const void *buf, size_t len) \ + __attribute__ ((ifunc("buffer_find_nonzero_offset_ifunc"))); + +static void *buffer_find_nonzero_offset_ifunc(void) +{ + typeof(buffer_find_nonzero_offset) *func = (avx2_support()) ? + buffer_find_nonzero_offset_avx2 : buffer_find_nonzero_offset_inner; + + return func; +} + +static void *can_use_buffer_find_nonzero_offset_ifunc(void) +{ + typeof(can_use_buffer_find_nonzero_offset) *func = (avx2_support()) ? + can_use_buffer_find_nonzero_offset_avx2 : + can_use_buffer_find_nonzero_offset_inner; + + return func; +} +#pragma GCC pop_options +#else +bool can_use_buffer_find_nonzero_offset(const void *buf, size_t len) +{ + return can_use_buffer_find_nonzero_offset_inner(buf, len); +} + +size_t buffer_find_nonzero_offset(const void *buf, size_t len) +{ + return buffer_find_nonzero_offset_inner(buf, len); +} +#endif + /* * Checks if a buffer is all zeroes * @@ -281,19 +424,19 @@ int fcntl_setfl(int fd, int flag) static int64_t suffix_mul(char suffix, int64_t unit) { switch (qemu_toupper(suffix)) { - case STRTOSZ_DEFSUFFIX_B: + case QEMU_STRTOSZ_DEFSUFFIX_B: return 1; - case STRTOSZ_DEFSUFFIX_KB: + case QEMU_STRTOSZ_DEFSUFFIX_KB: return unit; - case STRTOSZ_DEFSUFFIX_MB: + case QEMU_STRTOSZ_DEFSUFFIX_MB: return unit * unit; - case STRTOSZ_DEFSUFFIX_GB: + case QEMU_STRTOSZ_DEFSUFFIX_GB: return unit * unit * unit; - case STRTOSZ_DEFSUFFIX_TB: + case QEMU_STRTOSZ_DEFSUFFIX_TB: return unit * unit * unit * unit; - case STRTOSZ_DEFSUFFIX_PB: + case QEMU_STRTOSZ_DEFSUFFIX_PB: return unit * unit * unit * unit * unit; - case STRTOSZ_DEFSUFFIX_EB: + case QEMU_STRTOSZ_DEFSUFFIX_EB: return unit * unit * unit * unit * unit * unit; } return -1; @@ -305,7 +448,7 @@ static int64_t suffix_mul(char suffix, int64_t unit) * in *end, if not NULL. Return -ERANGE on overflow, Return -EINVAL on * other error. */ -int64_t strtosz_suffix_unit(const char *nptr, char **end, +int64_t qemu_strtosz_suffix_unit(const char *nptr, char **end, const char default_suffix, int64_t unit) { int64_t retval = -EINVAL; @@ -348,14 +491,165 @@ fail: return retval; } -int64_t strtosz_suffix(const char *nptr, char **end, const char default_suffix) +int64_t qemu_strtosz_suffix(const char *nptr, char **end, + const char default_suffix) { - return strtosz_suffix_unit(nptr, end, default_suffix, 1024); + return qemu_strtosz_suffix_unit(nptr, end, default_suffix, 1024); } -int64_t strtosz(const char *nptr, char **end) +int64_t qemu_strtosz(const char *nptr, char **end) { - return strtosz_suffix(nptr, end, STRTOSZ_DEFSUFFIX_MB); + return qemu_strtosz_suffix(nptr, end, QEMU_STRTOSZ_DEFSUFFIX_MB); +} + +/** + * Helper function for qemu_strto*l() functions. + */ +static int check_strtox_error(const char *p, char *endptr, const char **next, + int err) +{ + /* If no conversion was performed, prefer BSD behavior over glibc + * behavior. + */ + if (err == 0 && endptr == p) { + err = EINVAL; + } + if (!next && *endptr) { + return -EINVAL; + } + if (next) { + *next = endptr; + } + return -err; +} + +/** + * QEMU wrappers for strtol(), strtoll(), strtoul(), strotull() C functions. + * + * Convert ASCII string @nptr to a long integer value + * from the given @base. Parameters @nptr, @endptr, @base + * follows same semantics as strtol() C function. + * + * Unlike from strtol() function, if @endptr is not NULL, this + * function will return -EINVAL whenever it cannot fully convert + * the string in @nptr with given @base to a long. This function returns + * the result of the conversion only through the @result parameter. + * + * If NULL is passed in @endptr, then the whole string in @ntpr + * is a number otherwise it returns -EINVAL. + * + * RETURN VALUE + * Unlike from strtol() function, this wrapper returns either + * -EINVAL or the errno set by strtol() function (e.g -ERANGE). + * If the conversion overflows, -ERANGE is returned, and @result + * is set to the max value of the desired type + * (e.g. LONG_MAX, LLONG_MAX, ULONG_MAX, ULLONG_MAX). If the case + * of underflow, -ERANGE is returned, and @result is set to the min + * value of the desired type. For strtol(), strtoll(), @result is set to + * LONG_MIN, LLONG_MIN, respectively, and for strtoul(), strtoull() it + * is set to 0. + */ +int qemu_strtol(const char *nptr, const char **endptr, int base, + long *result) +{ + char *p; + int err = 0; + if (!nptr) { + if (endptr) { + *endptr = nptr; + } + err = -EINVAL; + } else { + errno = 0; + *result = strtol(nptr, &p, base); + err = check_strtox_error(nptr, p, endptr, errno); + } + return err; +} + +/** + * Converts ASCII string to an unsigned long integer. + * + * If string contains a negative number, value will be converted to + * the unsigned representation of the signed value, unless the original + * (nonnegated) value would overflow, in this case, it will set @result + * to ULONG_MAX, and return ERANGE. + * + * The same behavior holds, for qemu_strtoull() but sets @result to + * ULLONG_MAX instead of ULONG_MAX. + * + * See qemu_strtol() documentation for more info. + */ +int qemu_strtoul(const char *nptr, const char **endptr, int base, + unsigned long *result) +{ + char *p; + int err = 0; + if (!nptr) { + if (endptr) { + *endptr = nptr; + } + err = -EINVAL; + } else { + errno = 0; + *result = strtoul(nptr, &p, base); + /* Windows returns 1 for negative out-of-range values. */ + if (errno == ERANGE) { + *result = -1; + } + err = check_strtox_error(nptr, p, endptr, errno); + } + return err; +} + +/** + * Converts ASCII string to a long long integer. + * + * See qemu_strtol() documentation for more info. + */ +int qemu_strtoll(const char *nptr, const char **endptr, int base, + int64_t *result) +{ + char *p; + int err = 0; + if (!nptr) { + if (endptr) { + *endptr = nptr; + } + err = -EINVAL; + } else { + errno = 0; + *result = strtoll(nptr, &p, base); + err = check_strtox_error(nptr, p, endptr, errno); + } + return err; +} + +/** + * Converts ASCII string to an unsigned long long integer. + * + * See qemu_strtol() documentation for more info. + */ +int qemu_strtoull(const char *nptr, const char **endptr, int base, + uint64_t *result) +{ + char *p; + int err = 0; + if (!nptr) { + if (endptr) { + *endptr = nptr; + } + err = -EINVAL; + } else { + errno = 0; + *result = strtoull(nptr, &p, base); + /* Windows returns 1 for negative out-of-range values. */ + if (errno == ERANGE) { + *result = -1; + } + err = check_strtox_error(nptr, p, endptr, errno); + } + return err; } /** @@ -474,29 +768,6 @@ int qemu_parse_fd(const char *param) return fd; } -/* round down to the nearest power of 2*/ -int64_t pow2floor(int64_t value) -{ - if (!is_power_of_2(value)) { - value = 0x8000000000000000ULL >> clz64(value); - } - return value; -} - -/* round up to the nearest power of 2 (0 if overflow) */ -uint64_t pow2ceil(uint64_t value) -{ - uint8_t nlz = clz64(value); - - if (is_power_of_2(value)) { - return value; - } - if (!nlz) { - return 0; - } - return 1ULL << (64 - nlz); -} - /* * Implementation of ULEB128 (http://en.wikipedia.org/wiki/LEB128) * Input is limited to 14-bit numbers diff --git a/qemu/util/envlist.c b/qemu/util/envlist.c index 099a544a4..e86857e70 100644 --- a/qemu/util/envlist.c +++ b/qemu/util/envlist.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/queue.h" #include "qemu/envlist.h" diff --git a/qemu/util/error.c b/qemu/util/error.c index 14f435187..cae251173 100644 --- a/qemu/util/error.c +++ b/qemu/util/error.c @@ -2,30 +2,53 @@ * QEMU Error Objects * * Copyright IBM, Corp. 2011 + * Copyright (C) 2011-2015 Red Hat, Inc. * * Authors: * Anthony Liguori + * Markus Armbruster , * * This work is licensed under the terms of the GNU LGPL, version 2. See * the COPYING.LIB file in the top-level directory. */ -#include "qemu-common.h" +#include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu-common.h" #include "qemu/error-report.h" struct Error { char *msg; ErrorClass err_class; + const char *src, *func; + int line; + GString *hint; }; Error *error_abort; +Error *error_fatal; + +static void error_handle_fatal(Error **errp, Error *err) +{ + if (errp == &error_abort) { + fprintf(stderr, "Unexpected error in %s() at %s:%d:\n", + err->func, err->src, err->line); + error_report_err(err); + abort(); + } + if (errp == &error_fatal) { + error_report_err(err); + exit(1); + } +} -void error_set(Error **errp, ErrorClass err_class, const char *fmt, ...) +static void error_setv(Error **errp, + const char *src, int line, const char *func, + ErrorClass err_class, const char *fmt, va_list ap, + const char *suffix) { Error *err; - va_list ap; int saved_errno = errno; if (errp == NULL) { @@ -34,99 +57,140 @@ void error_set(Error **errp, ErrorClass err_class, const char *fmt, ...) assert(*errp == NULL); err = g_malloc0(sizeof(*err)); - - va_start(ap, fmt); err->msg = g_strdup_vprintf(fmt, ap); - va_end(ap); - err->err_class = err_class; - - if (errp == &error_abort) { - error_report_err(err); - abort(); + if (suffix) { + char *msg = err->msg; + err->msg = g_strdup_printf("%s: %s", msg, suffix); + g_free(msg); } + err->err_class = err_class; + err->src = src; + err->line = line; + err->func = func; + error_handle_fatal(errp, err); *errp = err; errno = saved_errno; } -void error_set_errno(Error **errp, int os_errno, ErrorClass err_class, - const char *fmt, ...) +void error_set_internal(Error **errp, + const char *src, int line, const char *func, + ErrorClass err_class, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + error_setv(errp, src, line, func, err_class, fmt, ap, NULL); + va_end(ap); +} + +void error_setg_internal(Error **errp, + const char *src, int line, const char *func, + const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + error_setv(errp, src, line, func, ERROR_CLASS_GENERIC_ERROR, fmt, ap, NULL); + va_end(ap); +} + +void error_setg_errno_internal(Error **errp, + const char *src, int line, const char *func, + int os_errno, const char *fmt, ...) { - Error *err; - char *msg1; va_list ap; int saved_errno = errno; if (errp == NULL) { return; } - assert(*errp == NULL); - - err = g_malloc0(sizeof(*err)); va_start(ap, fmt); - msg1 = g_strdup_vprintf(fmt, ap); - if (os_errno != 0) { - err->msg = g_strdup_printf("%s: %s", msg1, strerror(os_errno)); - g_free(msg1); - } else { - err->msg = msg1; - } + error_setv(errp, src, line, func, ERROR_CLASS_GENERIC_ERROR, fmt, ap, + os_errno != 0 ? strerror(os_errno) : NULL); va_end(ap); - err->err_class = err_class; - if (errp == &error_abort) { - error_report_err(err); - abort(); + errno = saved_errno; +} + +void error_setg_file_open_internal(Error **errp, + const char *src, int line, const char *func, + int os_errno, const char *filename) +{ + error_setg_errno_internal(errp, src, line, func, os_errno, + "Could not open '%s'", filename); +} + +void error_vprepend(Error **errp, const char *fmt, va_list ap) +{ + GString *newmsg; + + if (!errp) { + return; } - *errp = err; + newmsg = g_string_new(NULL); + g_string_vprintf(newmsg, fmt, ap); + g_string_append(newmsg, (*errp)->msg); + (*errp)->msg = g_string_free(newmsg, 0); +} - errno = saved_errno; +void error_prepend(Error **errp, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + error_vprepend(errp, fmt, ap); + va_end(ap); } -void error_setg_file_open(Error **errp, int os_errno, const char *filename) +void error_append_hint(Error **errp, const char *fmt, ...) { - error_setg_errno(errp, os_errno, "Could not open '%s'", filename); + va_list ap; + int saved_errno = errno; + Error *err; + + if (!errp) { + return; + } + err = *errp; + assert(err && errp != &error_abort && errp != &error_fatal); + + if (!err->hint) { + err->hint = g_string_new(NULL); + } + va_start(ap, fmt); + g_string_append_vprintf(err->hint, fmt, ap); + va_end(ap); + + errno = saved_errno; } #ifdef _WIN32 -void error_set_win32(Error **errp, int win32_err, ErrorClass err_class, - const char *fmt, ...) +void error_setg_win32_internal(Error **errp, + const char *src, int line, const char *func, + int win32_err, const char *fmt, ...) { - Error *err; - char *msg1; va_list ap; + char *suffix = NULL; if (errp == NULL) { return; } - assert(*errp == NULL); - err = g_malloc0(sizeof(*err)); - - va_start(ap, fmt); - msg1 = g_strdup_vprintf(fmt, ap); if (win32_err != 0) { - char *msg2 = g_win32_error_message(win32_err); - err->msg = g_strdup_printf("%s: %s (error: %x)", msg1, msg2, - (unsigned)win32_err); - g_free(msg2); - g_free(msg1); - } else { - err->msg = msg1; + suffix = g_win32_error_message(win32_err); } - va_end(ap); - err->err_class = err_class; - if (errp == &error_abort) { - error_report_err(err); - abort(); - } + va_start(ap, fmt); + error_setv(errp, src, line, func, ERROR_CLASS_GENERIC_ERROR, + fmt, ap, suffix); + va_end(ap); - *errp = err; + g_free(suffix); } #endif @@ -138,6 +202,12 @@ Error *error_copy(const Error *err) err_new = g_malloc0(sizeof(*err)); err_new->msg = g_strdup(err->msg); err_new->err_class = err->err_class; + err_new->src = err->src; + err_new->line = err->line; + err_new->func = err->func; + if (err->hint) { + err_new->hint = g_string_new(err->hint->str); + } return err_new; } @@ -155,25 +225,49 @@ const char *error_get_pretty(Error *err) void error_report_err(Error *err) { error_report("%s", error_get_pretty(err)); + if (err->hint) { + error_printf_unless_qmp("%s", err->hint->str); + } error_free(err); } +void error_reportf_err(Error *err, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + error_vprepend(&err, fmt, ap); + va_end(ap); + error_report_err(err); +} + void error_free(Error *err) { if (err) { g_free(err->msg); + if (err->hint) { + g_string_free(err->hint, true); + } g_free(err); } } +void error_free_or_abort(Error **errp) +{ + assert(errp && *errp); + error_free(*errp); + *errp = NULL; +} + void error_propagate(Error **dst_errp, Error *local_err) { - if (local_err && dst_errp == &error_abort) { - error_report_err(local_err); - abort(); - } else if (dst_errp && !*dst_errp) { + if (!local_err) { + return; + } + error_handle_fatal(dst_errp, local_err); + if (dst_errp && !*dst_errp) { *dst_errp = local_err; - } else if (local_err) { + } else { error_free(local_err); } } diff --git a/qemu/util/event_notifier-posix.c b/qemu/util/event_notifier-posix.c index ed4ca2b01..c1f0d79b3 100644 --- a/qemu/util/event_notifier-posix.c +++ b/qemu/util/event_notifier-posix.c @@ -10,7 +10,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "qemu-common.h" +#include "qemu/cutils.h" #include "qemu/event_notifier.h" #include "sysemu/char.h" #include "qemu/main-loop.h" @@ -19,11 +21,17 @@ #include #endif +#ifdef CONFIG_EVENTFD +/* + * Initialize @e with existing file descriptor @fd. + * @fd must be a genuine eventfd object, emulation with pipe won't do. + */ void event_notifier_init_fd(EventNotifier *e, int fd) { e->rfd = fd; e->wfd = fd; } +#endif int event_notifier_init(EventNotifier *e, int active) { @@ -77,15 +85,17 @@ void event_notifier_cleanup(EventNotifier *e) close(e->wfd); } -int event_notifier_get_fd(EventNotifier *e) +int event_notifier_get_fd(const EventNotifier *e) { return e->rfd; } int event_notifier_set_handler(EventNotifier *e, + bool is_external, EventNotifierHandler *handler) { - qemu_set_fd_handler(e->rfd, (IOHandler *)handler, NULL, e); + aio_set_fd_handler(iohandler_get_aio_context(), e->rfd, is_external, + (IOHandler *)handler, NULL, e); return 0; } diff --git a/qemu/util/event_notifier-win32.c b/qemu/util/event_notifier-win32.c index 6dbb530cf..de87df02d 100644 --- a/qemu/util/event_notifier-win32.c +++ b/qemu/util/event_notifier-win32.c @@ -10,6 +10,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/event_notifier.h" #include "qemu/main-loop.h" @@ -32,6 +33,7 @@ HANDLE event_notifier_get_handle(EventNotifier *e) } int event_notifier_set_handler(EventNotifier *e, + bool is_external, EventNotifierHandler *handler) { if (handler) { diff --git a/qemu/util/fifo8.c b/qemu/util/fifo8.c index 0ea5ad98e..5c64101b3 100644 --- a/qemu/util/fifo8.c +++ b/qemu/util/fifo8.c @@ -12,6 +12,7 @@ * with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/fifo8.h" diff --git a/qemu/util/getauxval.c b/qemu/util/getauxval.c index 1732ace2b..0b3bae2dc 100644 --- a/qemu/util/getauxval.c +++ b/qemu/util/getauxval.c @@ -22,8 +22,8 @@ * THE SOFTWARE. */ -#include "qemu-common.h" #include "qemu/osdep.h" +#include "qemu-common.h" #ifdef CONFIG_GETAUXVAL /* Don't inline this in qemu/osdep.h, because pulling in for diff --git a/qemu/util/hbitmap.c b/qemu/util/hbitmap.c index 50b888fd6..b22b87d0a 100644 --- a/qemu/util/hbitmap.c +++ b/qemu/util/hbitmap.c @@ -9,10 +9,8 @@ * later. See the COPYING file in the top-level directory. */ -#include -#include -#include #include "qemu/osdep.h" +#include #include "qemu/hbitmap.h" #include "qemu/host-utils.h" #include "trace.h" diff --git a/qemu/util/hexdump.c b/qemu/util/hexdump.c index 969b3406c..f879ff0ad 100644 --- a/qemu/util/hexdump.c +++ b/qemu/util/hexdump.c @@ -13,25 +13,37 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "qemu-common.h" void qemu_hexdump(const char *buf, FILE *fp, const char *prefix, size_t size) { - unsigned int b; + unsigned int b, len, i, c; - for (b = 0; b < size; b++) { - if ((b % 16) == 0) { - fprintf(fp, "%s: %04x:", prefix, b); + for (b = 0; b < size; b += 16) { + len = size - b; + if (len > 16) { + len = 16; } - if ((b % 4) == 0) { - fprintf(fp, " "); + fprintf(fp, "%s: %04x:", prefix, b); + for (i = 0; i < 16; i++) { + if ((i % 4) == 0) { + fprintf(fp, " "); + } + if (i < len) { + fprintf(fp, " %02x", (unsigned char)buf[b + i]); + } else { + fprintf(fp, " "); + } } - fprintf(fp, " %02x", (unsigned char)buf[b]); - if ((b % 16) == 15) { - fprintf(fp, "\n"); + fprintf(fp, " "); + for (i = 0; i < len; i++) { + c = buf[b + i]; + if (c < ' ' || c > '~') { + c = '.'; + } + fprintf(fp, "%c", c); } - } - if ((b % 16) != 0) { fprintf(fp, "\n"); } } diff --git a/qemu/util/host-utils.c b/qemu/util/host-utils.c index 102e5bf30..b166e5758 100644 --- a/qemu/util/host-utils.c +++ b/qemu/util/host-utils.c @@ -23,8 +23,7 @@ * THE SOFTWARE. */ -#include -#include +#include "qemu/osdep.h" #include "qemu/host-utils.h" /* Long integer helpers */ diff --git a/qemu/util/id.c b/qemu/util/id.c index 09b22fb8f..614135295 100644 --- a/qemu/util/id.c +++ b/qemu/util/id.c @@ -10,7 +10,9 @@ * or later. See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include "qemu-common.h" +#include "qemu/id.h" bool id_wellformed(const char *id) { @@ -26,3 +28,40 @@ bool id_wellformed(const char *id) } return true; } + +#define ID_SPECIAL_CHAR '#' + +static const char *const id_subsys_str[ID_MAX] = { + [ID_QDEV] = "qdev", + [ID_BLOCK] = "block", +}; + +/* + * Generates an ID of the form PREFIX SUBSYSTEM NUMBER + * where: + * + * - PREFIX is the reserved character '#' + * - SUBSYSTEM identifies the subsystem creating the ID + * - NUMBER is a decimal number unique within SUBSYSTEM. + * + * Example: "#block146" + * + * Note that these IDs do not satisfy id_wellformed(). + * + * The caller is responsible for freeing the returned string with g_free() + */ +char *id_generate(IdSubSystems id) +{ + static uint64_t id_counters[ID_MAX]; + uint32_t rnd; + + assert(id < ARRAY_SIZE(id_subsys_str)); + assert(id_subsys_str[id]); + + rnd = g_random_int_range(0, 100); + + return g_strdup_printf("%c%s%" PRIu64 "%02" PRId32, ID_SPECIAL_CHAR, + id_subsys_str[id], + id_counters[id]++, + rnd); +} diff --git a/qemu/util/iov.c b/qemu/util/iov.c index a0d5934e8..003fcce66 100644 --- a/qemu/util/iov.c +++ b/qemu/util/iov.c @@ -16,11 +16,14 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" +#include "qemu-common.h" #include "qemu/iov.h" #include "qemu/sockets.h" +#include "qemu/cutils.h" -size_t iov_from_buf(const struct iovec *iov, unsigned int iov_cnt, - size_t offset, const void *buf, size_t bytes) +size_t iov_from_buf_full(const struct iovec *iov, unsigned int iov_cnt, + size_t offset, const void *buf, size_t bytes) { size_t done; unsigned int i; @@ -38,8 +41,8 @@ size_t iov_from_buf(const struct iovec *iov, unsigned int iov_cnt, return done; } -size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, - size_t offset, void *buf, size_t bytes) +size_t iov_to_buf_full(const struct iovec *iov, const unsigned int iov_cnt, + size_t offset, void *buf, size_t bytes) { size_t done; unsigned int i; diff --git a/qemu/util/log.c b/qemu/util/log.c new file mode 100644 index 000000000..1857730dc --- /dev/null +++ b/qemu/util/log.c @@ -0,0 +1,313 @@ +/* + * Logging support + * + * Copyright (c) 2003 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 . + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/log.h" +#include "qemu/range.h" +#include "qemu/error-report.h" +#include "qemu/cutils.h" +#include "trace/control.h" + +static char *logfilename; +FILE *qemu_logfile; +int qemu_loglevel; +static int log_append = 0; +static GArray *debug_regions; + +void qemu_log(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (qemu_logfile) { + vfprintf(qemu_logfile, fmt, ap); + } + va_end(ap); +} + +/* enable or disable low levels log */ +void do_qemu_set_log(int log_flags, bool use_own_buffers) +{ + qemu_loglevel = log_flags; +#ifdef CONFIG_TRACE_LOG + qemu_loglevel |= LOG_TRACE; +#endif + if (!qemu_logfile && + (is_daemonized() ? logfilename != NULL : qemu_loglevel)) { + if (logfilename) { + qemu_logfile = fopen(logfilename, log_append ? "a" : "w"); + if (!qemu_logfile) { + perror(logfilename); + _exit(1); + } + /* In case we are a daemon redirect stderr to logfile */ + if (is_daemonized()) { + dup2(fileno(qemu_logfile), STDERR_FILENO); + fclose(qemu_logfile); + /* This will skip closing logfile in qemu_log_close() */ + qemu_logfile = stderr; + } + } else { + /* Default to stderr if no log file specified */ + assert(!is_daemonized()); + qemu_logfile = stderr; + } + /* must avoid mmap() usage of glibc by setting a buffer "by hand" */ + if (use_own_buffers) { + static char logfile_buf[4096]; + + setvbuf(qemu_logfile, logfile_buf, _IOLBF, sizeof(logfile_buf)); + } else { +#if defined(_WIN32) + /* Win32 doesn't support line-buffering, so use unbuffered output. */ + setvbuf(qemu_logfile, NULL, _IONBF, 0); +#else + setvbuf(qemu_logfile, NULL, _IOLBF, 0); +#endif + log_append = 1; + } + } + if (qemu_logfile && + (is_daemonized() ? logfilename == NULL : !qemu_loglevel)) { + qemu_log_close(); + } +} +/* + * Allow the user to include %d in their logfile which will be + * substituted with the current PID. This is useful for debugging many + * nested linux-user tasks but will result in lots of logs. + */ +void qemu_set_log_filename(const char *filename) +{ + char *pidstr; + g_free(logfilename); + + pidstr = strstr(filename, "%"); + if (pidstr) { + /* We only accept one %d, no other format strings */ + if (pidstr[1] != 'd' || strchr(pidstr + 2, '%')) { + error_report("Bad logfile format: %s", filename); + logfilename = NULL; + } else { + logfilename = g_strdup_printf(filename, getpid()); + } + } else { + logfilename = g_strdup(filename); + } + qemu_log_close(); + qemu_set_log(qemu_loglevel); +} + +/* Returns true if addr is in our debug filter or no filter defined + */ +bool qemu_log_in_addr_range(uint64_t addr) +{ + if (debug_regions) { + int i = 0; + for (i = 0; i < debug_regions->len; i++) { + struct Range *range = &g_array_index(debug_regions, Range, i); + if (addr >= range->begin && addr <= range->end) { + return true; + } + } + return false; + } else { + return true; + } +} + + +void qemu_set_dfilter_ranges(const char *filter_spec) +{ + gchar **ranges = g_strsplit(filter_spec, ",", 0); + if (ranges) { + gchar **next = ranges; + gchar *r = *next++; + debug_regions = g_array_sized_new(FALSE, FALSE, + sizeof(Range), g_strv_length(ranges)); + while (r) { + char *range_op = strstr(r, "-"); + char *r2 = range_op ? range_op + 1 : NULL; + if (!range_op) { + range_op = strstr(r, "+"); + r2 = range_op ? range_op + 1 : NULL; + } + if (!range_op) { + range_op = strstr(r, ".."); + r2 = range_op ? range_op + 2 : NULL; + } + if (range_op) { + const char *e = NULL; + uint64_t r1val, r2val; + + if ((qemu_strtoull(r, &e, 0, &r1val) == 0) && + (qemu_strtoull(r2, NULL, 0, &r2val) == 0) && + r2val > 0) { + struct Range range; + + g_assert(e == range_op); + + switch (*range_op) { + case '+': + { + range.begin = r1val; + range.end = r1val + (r2val - 1); + break; + } + case '-': + { + range.end = r1val; + range.begin = r1val - (r2val - 1); + break; + } + case '.': + range.begin = r1val; + range.end = r2val; + break; + default: + g_assert_not_reached(); + } + g_array_append_val(debug_regions, range); + + } else { + g_error("Failed to parse range in: %s", r); + } + } else { + g_error("Bad range specifier in: %s", r); + } + r = *next++; + } + g_strfreev(ranges); + } +} + +/* fflush() the log file */ +void qemu_log_flush(void) +{ + fflush(qemu_logfile); +} + +/* Close the log file */ +void qemu_log_close(void) +{ + if (qemu_logfile) { + if (qemu_logfile != stderr) { + fclose(qemu_logfile); + } + qemu_logfile = NULL; + } +} + +const QEMULogItem qemu_log_items[] = { + { CPU_LOG_TB_OUT_ASM, "out_asm", + "show generated host assembly code for each compiled TB" }, + { CPU_LOG_TB_IN_ASM, "in_asm", + "show target assembly code for each compiled TB" }, + { CPU_LOG_TB_OP, "op", + "show micro ops for each compiled TB" }, + { CPU_LOG_TB_OP_OPT, "op_opt", + "show micro ops (x86 only: before eflags optimization) and\n" + "after liveness analysis" }, + { CPU_LOG_INT, "int", + "show interrupts/exceptions in short format" }, + { CPU_LOG_EXEC, "exec", + "show trace before each executed TB (lots of logs)" }, + { CPU_LOG_TB_CPU, "cpu", + "show CPU registers before entering a TB (lots of logs)" }, + { CPU_LOG_MMU, "mmu", + "log MMU-related activities" }, + { CPU_LOG_PCALL, "pcall", + "x86 only: show protected mode far calls/returns/exceptions" }, + { CPU_LOG_RESET, "cpu_reset", + "show CPU state before CPU resets" }, + { LOG_UNIMP, "unimp", + "log unimplemented functionality" }, + { LOG_GUEST_ERROR, "guest_errors", + "log when the guest OS does something invalid (eg accessing a\n" + "non-existent register)" }, + { CPU_LOG_PAGE, "page", + "dump pages at beginning of user mode emulation" }, + { CPU_LOG_TB_NOCHAIN, "nochain", + "do not chain compiled TBs so that \"exec\" and \"cpu\" show\n" + "complete traces" }, + { 0, NULL, NULL }, +}; + +static int cmp1(const char *s1, int n, const char *s2) +{ + if (strlen(s2) != n) { + return 0; + } + return memcmp(s1, s2, n) == 0; +} + +/* takes a comma separated list of log masks. Return 0 if error. */ +int qemu_str_to_log_mask(const char *str) +{ + const QEMULogItem *item; + int mask; + const char *p, *p1; + + p = str; + mask = 0; + for (;;) { + p1 = strchr(p, ','); + if (!p1) { + p1 = p + strlen(p); + } + if (cmp1(p,p1-p,"all")) { + for (item = qemu_log_items; item->mask != 0; item++) { + mask |= item->mask; + } +#ifdef CONFIG_TRACE_LOG + } else if (strncmp(p, "trace:", 6) == 0 && p + 6 != p1) { + trace_enable_events(p + 6); + mask |= LOG_TRACE; +#endif + } else { + for (item = qemu_log_items; item->mask != 0; item++) { + if (cmp1(p, p1 - p, item->name)) { + goto found; + } + } + return 0; + found: + mask |= item->mask; + } + if (*p1 != ',') { + break; + } + p = p1 + 1; + } + return mask; +} + +void qemu_print_log_usage(FILE *f) +{ + const QEMULogItem *item; + fprintf(f, "Log items (comma separated):\n"); + for (item = qemu_log_items; item->mask != 0; item++) { + fprintf(f, "%-15s %s\n", item->name, item->help); + } +#ifdef CONFIG_TRACE_LOG + fprintf(f, "trace:PATTERN enable trace events\n"); + fprintf(f, "\nUse \"-d trace:help\" to get a list of trace events.\n\n"); +#endif +} diff --git a/qemu/util/memfd.c b/qemu/util/memfd.c new file mode 100644 index 000000000..7c406914c --- /dev/null +++ b/qemu/util/memfd.c @@ -0,0 +1,162 @@ +/* + * memfd.c + * + * Copyright (c) 2015 Red Hat, Inc. + * + * QEMU library functions on POSIX which are shared between QEMU and + * the QEMU tools. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" + +#include +#include + +#include + +#include "qemu/memfd.h" + +#ifdef CONFIG_MEMFD +#include +#elif defined CONFIG_LINUX +#include +#include + +static int memfd_create(const char *name, unsigned int flags) +{ +#ifdef __NR_memfd_create + return syscall(__NR_memfd_create, name, flags); +#else + return -1; +#endif +} +#endif + +#ifndef MFD_CLOEXEC +#define MFD_CLOEXEC 0x0001U +#endif + +#ifndef MFD_ALLOW_SEALING +#define MFD_ALLOW_SEALING 0x0002U +#endif + +/* + * This is a best-effort helper for shared memory allocation, with + * optional sealing. The helper will do his best to allocate using + * memfd with sealing, but may fallback on other methods without + * sealing. + */ +void *qemu_memfd_alloc(const char *name, size_t size, unsigned int seals, + int *fd) +{ + void *ptr; + int mfd = -1; + + *fd = -1; + +#ifdef CONFIG_LINUX + if (seals) { + mfd = memfd_create(name, MFD_ALLOW_SEALING | MFD_CLOEXEC); + } + + if (mfd == -1) { + /* some systems have memfd without sealing */ + mfd = memfd_create(name, MFD_CLOEXEC); + seals = 0; + } +#endif + + if (mfd != -1) { + if (ftruncate(mfd, size) == -1) { + perror("ftruncate"); + close(mfd); + return NULL; + } + + if (seals && fcntl(mfd, F_ADD_SEALS, seals) == -1) { + perror("fcntl"); + close(mfd); + return NULL; + } + } else { + const char *tmpdir = g_get_tmp_dir(); + gchar *fname; + + fname = g_strdup_printf("%s/memfd-XXXXXX", tmpdir); + mfd = mkstemp(fname); + unlink(fname); + g_free(fname); + + if (mfd == -1) { + perror("mkstemp"); + return NULL; + } + + if (ftruncate(mfd, size) == -1) { + perror("ftruncate"); + close(mfd); + return NULL; + } + } + + ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, mfd, 0); + if (ptr == MAP_FAILED) { + perror("mmap"); + close(mfd); + return NULL; + } + + *fd = mfd; + return ptr; +} + +void qemu_memfd_free(void *ptr, size_t size, int fd) +{ + if (ptr) { + munmap(ptr, size); + } + + if (fd != -1) { + close(fd); + } +} + +enum { + MEMFD_KO, + MEMFD_OK, + MEMFD_TODO +}; + +bool qemu_memfd_check(void) +{ + static int memfd_check = MEMFD_TODO; + + if (memfd_check == MEMFD_TODO) { + int fd; + void *ptr; + + ptr = qemu_memfd_alloc("test", 4096, 0, &fd); + memfd_check = ptr ? MEMFD_OK : MEMFD_KO; + qemu_memfd_free(ptr, 4096, fd); + } + + return memfd_check == MEMFD_OK; +} diff --git a/qemu/util/mmap-alloc.c b/qemu/util/mmap-alloc.c new file mode 100644 index 000000000..0b4cc7f7f --- /dev/null +++ b/qemu/util/mmap-alloc.c @@ -0,0 +1,110 @@ +/* + * Support for RAM backed by mmaped host memory. + * + * Copyright (c) 2015 Red Hat, Inc. + * + * Authors: + * Michael S. Tsirkin + * + * 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 +#include + +#define HUGETLBFS_MAGIC 0x958458f6 + +#ifdef CONFIG_LINUX +#include +#endif + +size_t qemu_fd_getpagesize(int fd) +{ +#ifdef CONFIG_LINUX + struct statfs fs; + int ret; + + if (fd != -1) { + do { + ret = fstatfs(fd, &fs); + } while (ret != 0 && errno == EINTR); + + if (ret == 0 && fs.f_type == HUGETLBFS_MAGIC) { + return fs.f_bsize; + } + } +#endif + + return getpagesize(); +} + +void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared) +{ + /* + * Note: this always allocates at least one extra page of virtual address + * space, even if size is already aligned. + */ + size_t total = size + align; +#if defined(__powerpc64__) && defined(__linux__) + /* On ppc64 mappings in the same segment (aka slice) must share the same + * page size. Since we will be re-allocating part of this segment + * from the supplied fd, we should make sure to use the same page size, to + * this end we mmap the supplied fd. In this case, set MAP_NORESERVE to + * avoid allocating backing store memory. + * We do this unless we are using the system page size, in which case + * anonymous memory is OK. + */ + int anonfd = fd == -1 || qemu_fd_getpagesize(fd) == getpagesize() ? -1 : fd; + int flags = anonfd == -1 ? MAP_ANONYMOUS : MAP_NORESERVE; + void *ptr = mmap(0, total, PROT_NONE, flags | MAP_PRIVATE, anonfd, 0); +#else + void *ptr = mmap(0, total, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); +#endif + size_t offset = QEMU_ALIGN_UP((uintptr_t)ptr, align) - (uintptr_t)ptr; + void *ptr1; + + if (ptr == MAP_FAILED) { + return MAP_FAILED; + } + + /* Make sure align is a power of 2 */ + assert(!(align & (align - 1))); + /* Always align to host page size */ + assert(align >= getpagesize()); + + ptr1 = mmap(ptr + offset, size, PROT_READ | PROT_WRITE, + MAP_FIXED | + (fd == -1 ? MAP_ANONYMOUS : 0) | + (shared ? MAP_SHARED : MAP_PRIVATE), + fd, 0); + if (ptr1 == MAP_FAILED) { + munmap(ptr, total); + return MAP_FAILED; + } + + ptr += offset; + total -= offset; + + if (offset > 0) { + munmap(ptr - offset, offset); + } + + /* + * Leave a single PROT_NONE page allocated after the RAM block, to serve as + * a guard page guarding against potential buffer overflows. + */ + if (total > size + getpagesize()) { + munmap(ptr + size + getpagesize(), total - size - getpagesize()); + } + + return ptr; +} + +void qemu_ram_munmap(void *ptr, size_t size) +{ + if (ptr) { + /* Unmap both the RAM block and the guard page */ + munmap(ptr, size + getpagesize()); + } +} diff --git a/qemu/util/module.c b/qemu/util/module.c index 4bd4a94d8..ce058aef6 100644 --- a/qemu/util/module.c +++ b/qemu/util/module.c @@ -13,7 +13,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include +#include "qemu/osdep.h" #include "qemu-common.h" #ifdef CONFIG_MODULES #include diff --git a/qemu/util/notify.c b/qemu/util/notify.c index f215dfc21..06de63a83 100644 --- a/qemu/util/notify.c +++ b/qemu/util/notify.c @@ -13,6 +13,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/notify.h" diff --git a/qemu/util/osdep.c b/qemu/util/osdep.c index 0092bb61b..d56d07111 100644 --- a/qemu/util/osdep.c +++ b/qemu/util/osdep.c @@ -21,24 +21,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include -#include -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" /* Needed early for CONFIG_BSD etc. */ -#include "config-host.h" #if defined(CONFIG_MADVISE) || defined(CONFIG_POSIX_MADVISE) #include #endif #ifdef CONFIG_SOLARIS -#include #include /* See MySQL bug #7156 (http://bugs.mysql.com/bug.php?id=7156) for discussion about Solaris header problems */ @@ -46,13 +37,21 @@ extern int madvise(caddr_t, size_t, int); #endif #include "qemu-common.h" +#include "qemu/cutils.h" #include "qemu/sockets.h" #include "qemu/error-report.h" #include "monitor/monitor.h" static bool fips_enabled = false; -static const char *qemu_version = QEMU_VERSION; +/* Starting on QEMU 2.5, qemu_hw_version() returns "2.5+" by default + * instead of QEMU_VERSION, so setting hw_version on MachineClass + * is no longer mandatory. + * + * Do NOT change this string, or it will break compatibility on all + * machine classes that don't set hw_version. + */ +static const char *hw_version = "2.5+"; int socket_set_cork(int fd, int v) { @@ -311,14 +310,14 @@ int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen) return ret; } -void qemu_set_version(const char *version) +void qemu_set_hw_version(const char *version) { - qemu_version = version; + hw_version = version; } -const char *qemu_get_version(void) +const char *qemu_hw_version(void) { - return qemu_version; + return hw_version; } void fips_set_state(bool requested) diff --git a/qemu/util/oslib-posix.c b/qemu/util/oslib-posix.c index 3ae4987b6..6cc4b8f00 100644 --- a/qemu/util/oslib-posix.c +++ b/qemu/util/oslib-posix.c @@ -26,16 +26,8 @@ * THE SOFTWARE. */ -/* The following block of code temporarily renames the daemon() function so the - compiler does not see the warning associated with it in stdlib.h on OSX */ -#ifdef __APPLE__ -#define daemon qemu_fake_daemon_function -#include -#undef daemon -extern int daemon(int, int); -#endif - -#if defined(__linux__) && (defined(__x86_64__) || defined(__arm__)) +#if defined(__linux__) && \ + (defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)) /* Use 2 MiB alignment so transparent hugepages can be used by KVM. Valgrind does not support alignments larger than 1 MiB, therefore we need special code which handles running on Valgrind. */ @@ -46,32 +38,32 @@ extern int daemon(int, int); #else # define QEMU_VMALLOC_ALIGN getpagesize() #endif -#define HUGETLBFS_MAGIC 0x958458f6 +#include "qemu/osdep.h" #include -#include #include #include -#include "config-host.h" #include "sysemu/sysemu.h" #include "trace.h" +#include "qapi/error.h" #include "qemu/sockets.h" #include #include -#include #include +#include "qemu/cutils.h" #ifdef CONFIG_LINUX #include -#include #endif #ifdef __FreeBSD__ #include #endif +#include + int qemu_get_thread_id(void) { #if defined(__linux__) @@ -128,10 +120,7 @@ void *qemu_memalign(size_t alignment, size_t size) void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment) { size_t align = QEMU_VMALLOC_ALIGN; - size_t total = size + align - getpagesize(); - void *ptr = mmap(0, total, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - size_t offset = QEMU_ALIGN_UP((uintptr_t)ptr, align) - (uintptr_t)ptr; + void *ptr = qemu_ram_mmap(-1, size, align, false); if (ptr == MAP_FAILED) { return NULL; @@ -140,15 +129,6 @@ void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment) if (alignment) { *alignment = align; } - ptr += offset; - total -= offset; - - if (offset > 0) { - munmap(ptr - offset, offset); - } - if (total > size) { - munmap(ptr + size, total - size); - } trace_qemu_anon_ram_alloc(size, ptr); return ptr; @@ -163,9 +143,7 @@ void qemu_vfree(void *ptr) void qemu_anon_ram_free(void *ptr, size_t size) { trace_qemu_anon_ram_free(ptr, size); - if (ptr) { - munmap(ptr, size); - } + qemu_ram_munmap(ptr, size); } void qemu_set_block(int fd) @@ -352,26 +330,6 @@ static void sigbus_handler(int signal) siglongjmp(sigjump, 1); } -static size_t fd_getpagesize(int fd) -{ -#ifdef CONFIG_LINUX - struct statfs fs; - int ret; - - if (fd != -1) { - do { - ret = fstatfs(fd, &fs); - } while (ret != 0 && errno == EINTR); - - if (ret == 0 && fs.f_type == HUGETLBFS_MAGIC) { - return fs.f_bsize; - } - } -#endif - - return getpagesize(); -} - void os_mem_prealloc(int fd, char *area, size_t memory) { int ret; @@ -399,7 +357,7 @@ void os_mem_prealloc(int fd, char *area, size_t memory) exit(1); } else { int i; - size_t hpagesize = fd_getpagesize(fd); + size_t hpagesize = qemu_fd_getpagesize(fd); size_t numpages = DIV_ROUND_UP(memory, hpagesize); /* MAP_POPULATE silently ignores failures */ @@ -482,3 +440,74 @@ int qemu_read_password(char *buf, int buf_size) printf("\n"); return ret; } + + +pid_t qemu_fork(Error **errp) +{ + sigset_t oldmask, newmask; + struct sigaction sig_action; + int saved_errno; + pid_t pid; + + /* + * Need to block signals now, so that child process can safely + * kill off caller's signal handlers without a race. + */ + sigfillset(&newmask); + if (pthread_sigmask(SIG_SETMASK, &newmask, &oldmask) != 0) { + error_setg_errno(errp, errno, + "cannot block signals"); + return -1; + } + + pid = fork(); + saved_errno = errno; + + if (pid < 0) { + /* attempt to restore signal mask, but ignore failure, to + * avoid obscuring the fork failure */ + (void)pthread_sigmask(SIG_SETMASK, &oldmask, NULL); + error_setg_errno(errp, saved_errno, + "cannot fork child process"); + errno = saved_errno; + return -1; + } else if (pid) { + /* parent process */ + + /* Restore our original signal mask now that the child is + * safely running. Only documented failures are EFAULT (not + * possible, since we are using just-grabbed mask) or EINVAL + * (not possible, since we are using correct arguments). */ + (void)pthread_sigmask(SIG_SETMASK, &oldmask, NULL); + } else { + /* child process */ + size_t i; + + /* Clear out all signal handlers from parent so nothing + * unexpected can happen in our child once we unblock + * signals */ + sig_action.sa_handler = SIG_DFL; + sig_action.sa_flags = 0; + sigemptyset(&sig_action.sa_mask); + + for (i = 1; i < NSIG; i++) { + /* Only possible errors are EFAULT or EINVAL The former + * won't happen, the latter we expect, so no need to check + * return value */ + (void)sigaction(i, &sig_action, NULL); + } + + /* Unmask all signals in child, since we've no idea what the + * caller's done with their signal mask and don't want to + * propagate that to children */ + sigemptyset(&newmask); + if (pthread_sigmask(SIG_SETMASK, &newmask, NULL) != 0) { + Error *local_err = NULL; + error_setg_errno(&local_err, errno, + "cannot unblock signals"); + error_report_err(local_err); + _exit(1); + } + } + return pid; +} diff --git a/qemu/util/oslib-win32.c b/qemu/util/oslib-win32.c index 730a6707a..c926db4a5 100644 --- a/qemu/util/oslib-win32.c +++ b/qemu/util/oslib-win32.c @@ -2,7 +2,7 @@ * os-win32.c * * Copyright (c) 2003-2008 Fabrice Bellard - * Copyright (c) 2010 Red Hat, Inc. + * Copyright (c) 2010-2016 Red Hat, Inc. * * QEMU library functions for win32 which are shared between QEMU and * the QEMU tools. @@ -29,14 +29,15 @@ * this file are based on code from GNOME glib-2 and use a different license, * see the license comment there. */ +#include "qemu/osdep.h" #include #include -#include -#include "config-host.h" +#include "qapi/error.h" #include "sysemu/sysemu.h" #include "qemu/main-loop.h" #include "trace.h" #include "qemu/sockets.h" +#include "qemu/cutils.h" /* this must come after including "trace.h" */ #include @@ -95,6 +96,7 @@ void qemu_anon_ram_free(void *ptr, size_t size) } } +#ifndef CONFIG_LOCALTIME_R /* FIXME: add proper locking */ struct tm *gmtime_r(const time_t *timep, struct tm *result) { @@ -118,6 +120,7 @@ struct tm *localtime_r(const time_t *timep, struct tm *result) } return p; } +#endif /* CONFIG_LOCALTIME_R */ void qemu_set_block(int fd) { @@ -143,6 +146,83 @@ int socket_set_fast_reuse(int fd) return 0; } + +static int socket_error(void) +{ + switch (WSAGetLastError()) { + case 0: + return 0; + case WSAEINTR: + return EINTR; + case WSAEINVAL: + return EINVAL; + case WSA_INVALID_HANDLE: + return EBADF; + case WSA_NOT_ENOUGH_MEMORY: + return ENOMEM; + case WSA_INVALID_PARAMETER: + return EINVAL; + case WSAENAMETOOLONG: + return ENAMETOOLONG; + case WSAENOTEMPTY: + return ENOTEMPTY; + case WSAEWOULDBLOCK: + /* not using EWOULDBLOCK as we don't want code to have + * to check both EWOULDBLOCK and EAGAIN */ + return EAGAIN; + case WSAEINPROGRESS: + return EINPROGRESS; + case WSAEALREADY: + return EALREADY; + case WSAENOTSOCK: + return ENOTSOCK; + case WSAEDESTADDRREQ: + return EDESTADDRREQ; + case WSAEMSGSIZE: + return EMSGSIZE; + case WSAEPROTOTYPE: + return EPROTOTYPE; + case WSAENOPROTOOPT: + return ENOPROTOOPT; + case WSAEPROTONOSUPPORT: + return EPROTONOSUPPORT; + case WSAEOPNOTSUPP: + return EOPNOTSUPP; + case WSAEAFNOSUPPORT: + return EAFNOSUPPORT; + case WSAEADDRINUSE: + return EADDRINUSE; + case WSAEADDRNOTAVAIL: + return EADDRNOTAVAIL; + case WSAENETDOWN: + return ENETDOWN; + case WSAENETUNREACH: + return ENETUNREACH; + case WSAENETRESET: + return ENETRESET; + case WSAECONNABORTED: + return ECONNABORTED; + case WSAECONNRESET: + return ECONNRESET; + case WSAENOBUFS: + return ENOBUFS; + case WSAEISCONN: + return EISCONN; + case WSAENOTCONN: + return ENOTCONN; + case WSAETIMEDOUT: + return ETIMEDOUT; + case WSAECONNREFUSED: + return ECONNREFUSED; + case WSAELOOP: + return ELOOP; + case WSAEHOSTUNREACH: + return EHOSTUNREACH; + default: + return EIO; + } +} + int inet_aton(const char *cp, struct in_addr *ia) { uint32_t addr = inet_addr(cp); @@ -452,7 +532,7 @@ gint g_poll(GPollFD *fds, guint nfds, gint timeout) return retval; } -size_t getpagesize(void) +int getpagesize(void) { SYSTEM_INFO system_info; @@ -494,3 +574,213 @@ int qemu_read_password(char *buf, int buf_size) buf[i] = '\0'; return 0; } + + +pid_t qemu_fork(Error **errp) +{ + errno = ENOSYS; + error_setg_errno(errp, errno, + "cannot fork child process"); + return -1; +} + + +#undef connect +int qemu_connect_wrap(int sockfd, const struct sockaddr *addr, + socklen_t addrlen) +{ + int ret; + ret = connect(sockfd, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + + +#undef listen +int qemu_listen_wrap(int sockfd, int backlog) +{ + int ret; + ret = listen(sockfd, backlog); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + + +#undef bind +int qemu_bind_wrap(int sockfd, const struct sockaddr *addr, + socklen_t addrlen) +{ + int ret; + ret = bind(sockfd, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + + +#undef socket +int qemu_socket_wrap(int domain, int type, int protocol) +{ + int ret; + ret = socket(domain, type, protocol); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + + +#undef accept +int qemu_accept_wrap(int sockfd, struct sockaddr *addr, + socklen_t *addrlen) +{ + int ret; + ret = accept(sockfd, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + + +#undef shutdown +int qemu_shutdown_wrap(int sockfd, int how) +{ + int ret; + ret = shutdown(sockfd, how); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + + +#undef ioctlsocket +int qemu_ioctlsocket_wrap(int fd, int req, void *val) +{ + int ret; + ret = ioctlsocket(fd, req, val); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + + +#undef closesocket +int qemu_closesocket_wrap(int fd) +{ + int ret; + ret = closesocket(fd); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + + +#undef getsockopt +int qemu_getsockopt_wrap(int sockfd, int level, int optname, + void *optval, socklen_t *optlen) +{ + int ret; + ret = getsockopt(sockfd, level, optname, optval, optlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + + +#undef setsockopt +int qemu_setsockopt_wrap(int sockfd, int level, int optname, + const void *optval, socklen_t optlen) +{ + int ret; + ret = setsockopt(sockfd, level, optname, optval, optlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + + +#undef getpeername +int qemu_getpeername_wrap(int sockfd, struct sockaddr *addr, + socklen_t *addrlen) +{ + int ret; + ret = getpeername(sockfd, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + + +#undef getsockname +int qemu_getsockname_wrap(int sockfd, struct sockaddr *addr, + socklen_t *addrlen) +{ + int ret; + ret = getsockname(sockfd, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + + +#undef send +ssize_t qemu_send_wrap(int sockfd, const void *buf, size_t len, int flags) +{ + int ret; + ret = send(sockfd, buf, len, flags); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + + +#undef sendto +ssize_t qemu_sendto_wrap(int sockfd, const void *buf, size_t len, int flags, + const struct sockaddr *addr, socklen_t addrlen) +{ + int ret; + ret = sendto(sockfd, buf, len, flags, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + + +#undef recv +ssize_t qemu_recv_wrap(int sockfd, void *buf, size_t len, int flags) +{ + int ret; + ret = recv(sockfd, buf, len, flags); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + + +#undef recvfrom +ssize_t qemu_recvfrom_wrap(int sockfd, void *buf, size_t len, int flags, + struct sockaddr *addr, socklen_t *addrlen) +{ + int ret; + ret = recvfrom(sockfd, buf, len, flags, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} diff --git a/qemu/util/path.c b/qemu/util/path.c index 4e4877e82..5479f76c6 100644 --- a/qemu/util/path.c +++ b/qemu/util/path.c @@ -3,15 +3,12 @@ The assumption is that this area does not change. */ -#include +#include "qemu/osdep.h" #include #include -#include -#include -#include -#include -#include #include "qemu-common.h" +#include "qemu/cutils.h" +#include "qemu/path.h" struct pathelem { diff --git a/qemu/util/qemu-config.c b/qemu/util/qemu-config.c index 5fcfd0e6a..fb973074d 100644 --- a/qemu/util/qemu-config.c +++ b/qemu/util/qemu-config.c @@ -1,8 +1,8 @@ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/error-report.h" #include "qemu/option.h" #include "qemu/config-file.h" -#include "qapi/error.h" #include "qmp-commands.h" static QemuOptsList *vm_config_groups[48]; @@ -219,6 +219,14 @@ static QemuOptsList machine_opts = { .name = "suppress-vmdesc", .type = QEMU_OPT_BOOL, .help = "Set on to disable self-describing migration", + },{ + .name = "aes-key-wrap", + .type = QEMU_OPT_BOOL, + .help = "enable/disable AES key wrapping using the CPACF wrapping key", + },{ + .name = "dea-key-wrap", + .type = QEMU_OPT_BOOL, + .help = "enable/disable DEA key wrapping using the CPACF wrapping key", }, { /* End of list */ } } diff --git a/qemu/util/qemu-coroutine-io.c b/qemu/util/qemu-coroutine-io.c new file mode 100644 index 000000000..91b9357d4 --- /dev/null +++ b/qemu/util/qemu-coroutine-io.c @@ -0,0 +1,90 @@ +/* + * Coroutine-aware I/O functions + * + * Copyright (C) 2009-2010 Nippon Telegraph and Telephone Corporation. + * Copyright (c) 2011, Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/sockets.h" +#include "qemu/coroutine.h" +#include "qemu/iov.h" +#include "qemu/main-loop.h" + +ssize_t coroutine_fn +qemu_co_sendv_recvv(int sockfd, struct iovec *iov, unsigned iov_cnt, + size_t offset, size_t bytes, bool do_send) +{ + size_t done = 0; + ssize_t ret; + while (done < bytes) { + ret = iov_send_recv(sockfd, iov, iov_cnt, + offset + done, bytes - done, do_send); + if (ret > 0) { + done += ret; + } else if (ret < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + qemu_coroutine_yield(); + } else if (done == 0) { + return -errno; + } else { + break; + } + } else if (ret == 0 && !do_send) { + /* write (send) should never return 0. + * read (recv) returns 0 for end-of-file (-data). + * In both cases there's little point retrying, + * but we do for write anyway, just in case */ + break; + } + } + return done; +} + +ssize_t coroutine_fn +qemu_co_send_recv(int sockfd, void *buf, size_t bytes, bool do_send) +{ + struct iovec iov = { .iov_base = buf, .iov_len = bytes }; + return qemu_co_sendv_recvv(sockfd, &iov, 1, 0, bytes, do_send); +} + +typedef struct { + Coroutine *co; + int fd; +} FDYieldUntilData; + +static void fd_coroutine_enter(void *opaque) +{ + FDYieldUntilData *data = opaque; + qemu_set_fd_handler(data->fd, NULL, NULL, NULL); + qemu_coroutine_enter(data->co, NULL); +} + +void coroutine_fn yield_until_fd_readable(int fd) +{ + FDYieldUntilData data; + + assert(qemu_in_coroutine()); + data.co = qemu_coroutine_self(); + data.fd = fd; + qemu_set_fd_handler(fd, fd_coroutine_enter, NULL, &data); + qemu_coroutine_yield(); +} diff --git a/qemu/util/qemu-coroutine-lock.c b/qemu/util/qemu-coroutine-lock.c new file mode 100644 index 000000000..da37ca7f9 --- /dev/null +++ b/qemu/util/qemu-coroutine-lock.c @@ -0,0 +1,187 @@ +/* + * coroutine queues and locks + * + * Copyright (c) 2011 Kevin Wolf + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/coroutine.h" +#include "qemu/coroutine_int.h" +#include "qemu/queue.h" +#include "trace.h" + +void qemu_co_queue_init(CoQueue *queue) +{ + QTAILQ_INIT(&queue->entries); +} + +void coroutine_fn qemu_co_queue_wait(CoQueue *queue) +{ + Coroutine *self = qemu_coroutine_self(); + QTAILQ_INSERT_TAIL(&queue->entries, self, co_queue_next); + qemu_coroutine_yield(); + assert(qemu_in_coroutine()); +} + +/** + * qemu_co_queue_run_restart: + * + * Enter each coroutine that was previously marked for restart by + * qemu_co_queue_next() or qemu_co_queue_restart_all(). This function is + * invoked by the core coroutine code when the current coroutine yields or + * terminates. + */ +void qemu_co_queue_run_restart(Coroutine *co) +{ + Coroutine *next; + + trace_qemu_co_queue_run_restart(co); + while ((next = QTAILQ_FIRST(&co->co_queue_wakeup))) { + QTAILQ_REMOVE(&co->co_queue_wakeup, next, co_queue_next); + qemu_coroutine_enter(next, NULL); + } +} + +static bool qemu_co_queue_do_restart(CoQueue *queue, bool single) +{ + Coroutine *self = qemu_coroutine_self(); + Coroutine *next; + + if (QTAILQ_EMPTY(&queue->entries)) { + return false; + } + + while ((next = QTAILQ_FIRST(&queue->entries)) != NULL) { + QTAILQ_REMOVE(&queue->entries, next, co_queue_next); + QTAILQ_INSERT_TAIL(&self->co_queue_wakeup, next, co_queue_next); + trace_qemu_co_queue_next(next); + if (single) { + break; + } + } + return true; +} + +bool coroutine_fn qemu_co_queue_next(CoQueue *queue) +{ + assert(qemu_in_coroutine()); + return qemu_co_queue_do_restart(queue, true); +} + +void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue) +{ + assert(qemu_in_coroutine()); + qemu_co_queue_do_restart(queue, false); +} + +bool qemu_co_enter_next(CoQueue *queue) +{ + Coroutine *next; + + next = QTAILQ_FIRST(&queue->entries); + if (!next) { + return false; + } + + QTAILQ_REMOVE(&queue->entries, next, co_queue_next); + qemu_coroutine_enter(next, NULL); + return true; +} + +bool qemu_co_queue_empty(CoQueue *queue) +{ + return QTAILQ_FIRST(&queue->entries) == NULL; +} + +void qemu_co_mutex_init(CoMutex *mutex) +{ + memset(mutex, 0, sizeof(*mutex)); + qemu_co_queue_init(&mutex->queue); +} + +void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex) +{ + Coroutine *self = qemu_coroutine_self(); + + trace_qemu_co_mutex_lock_entry(mutex, self); + + while (mutex->locked) { + qemu_co_queue_wait(&mutex->queue); + } + + mutex->locked = true; + + trace_qemu_co_mutex_lock_return(mutex, self); +} + +void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex) +{ + Coroutine *self = qemu_coroutine_self(); + + trace_qemu_co_mutex_unlock_entry(mutex, self); + + assert(mutex->locked == true); + assert(qemu_in_coroutine()); + + mutex->locked = false; + qemu_co_queue_next(&mutex->queue); + + trace_qemu_co_mutex_unlock_return(mutex, self); +} + +void qemu_co_rwlock_init(CoRwlock *lock) +{ + memset(lock, 0, sizeof(*lock)); + qemu_co_queue_init(&lock->queue); +} + +void qemu_co_rwlock_rdlock(CoRwlock *lock) +{ + while (lock->writer) { + qemu_co_queue_wait(&lock->queue); + } + lock->reader++; +} + +void qemu_co_rwlock_unlock(CoRwlock *lock) +{ + assert(qemu_in_coroutine()); + if (lock->writer) { + lock->writer = false; + qemu_co_queue_restart_all(&lock->queue); + } else { + lock->reader--; + assert(lock->reader >= 0); + /* Wakeup only one waiting writer */ + if (!lock->reader) { + qemu_co_queue_next(&lock->queue); + } + } +} + +void qemu_co_rwlock_wrlock(CoRwlock *lock) +{ + while (lock->writer || lock->reader) { + qemu_co_queue_wait(&lock->queue); + } + lock->writer = true; +} diff --git a/qemu/util/qemu-coroutine-sleep.c b/qemu/util/qemu-coroutine-sleep.c new file mode 100644 index 000000000..6966831d3 --- /dev/null +++ b/qemu/util/qemu-coroutine-sleep.c @@ -0,0 +1,42 @@ +/* + * QEMU coroutine sleep + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/coroutine.h" +#include "qemu/timer.h" +#include "block/aio.h" + +typedef struct CoSleepCB { + QEMUTimer *ts; + Coroutine *co; +} CoSleepCB; + +static void co_sleep_cb(void *opaque) +{ + CoSleepCB *sleep_cb = opaque; + + qemu_coroutine_enter(sleep_cb->co, NULL); +} + +void coroutine_fn co_aio_sleep_ns(AioContext *ctx, QEMUClockType type, + int64_t ns) +{ + CoSleepCB sleep_cb = { + .co = qemu_coroutine_self(), + }; + sleep_cb.ts = aio_timer_new(ctx, type, SCALE_NS, co_sleep_cb, &sleep_cb); + timer_mod(sleep_cb.ts, qemu_clock_get_ns(type) + ns); + qemu_coroutine_yield(); + timer_del(sleep_cb.ts); + timer_free(sleep_cb.ts); +} diff --git a/qemu/util/qemu-coroutine.c b/qemu/util/qemu-coroutine.c new file mode 100644 index 000000000..5816702cc --- /dev/null +++ b/qemu/util/qemu-coroutine.c @@ -0,0 +1,147 @@ +/* + * QEMU coroutines + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Stefan Hajnoczi + * Kevin Wolf + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "trace.h" +#include "qemu-common.h" +#include "qemu/thread.h" +#include "qemu/atomic.h" +#include "qemu/coroutine.h" +#include "qemu/coroutine_int.h" + +enum { + POOL_BATCH_SIZE = 64, +}; + +/** Free list to speed up creation */ +static QSLIST_HEAD(, Coroutine) release_pool = QSLIST_HEAD_INITIALIZER(pool); +static unsigned int release_pool_size; +static __thread QSLIST_HEAD(, Coroutine) alloc_pool = QSLIST_HEAD_INITIALIZER(pool); +static __thread unsigned int alloc_pool_size; +static __thread Notifier coroutine_pool_cleanup_notifier; + +static void coroutine_pool_cleanup(Notifier *n, void *value) +{ + Coroutine *co; + Coroutine *tmp; + + QSLIST_FOREACH_SAFE(co, &alloc_pool, pool_next, tmp) { + QSLIST_REMOVE_HEAD(&alloc_pool, pool_next); + qemu_coroutine_delete(co); + } +} + +Coroutine *qemu_coroutine_create(CoroutineEntry *entry) +{ + Coroutine *co = NULL; + + if (CONFIG_COROUTINE_POOL) { + co = QSLIST_FIRST(&alloc_pool); + if (!co) { + if (release_pool_size > POOL_BATCH_SIZE) { + /* Slow path; a good place to register the destructor, too. */ + if (!coroutine_pool_cleanup_notifier.notify) { + coroutine_pool_cleanup_notifier.notify = coroutine_pool_cleanup; + qemu_thread_atexit_add(&coroutine_pool_cleanup_notifier); + } + + /* This is not exact; there could be a little skew between + * release_pool_size and the actual size of release_pool. But + * it is just a heuristic, it does not need to be perfect. + */ + alloc_pool_size = atomic_xchg(&release_pool_size, 0); + QSLIST_MOVE_ATOMIC(&alloc_pool, &release_pool); + co = QSLIST_FIRST(&alloc_pool); + } + } + if (co) { + QSLIST_REMOVE_HEAD(&alloc_pool, pool_next); + alloc_pool_size--; + } + } + + if (!co) { + co = qemu_coroutine_new(); + } + + co->entry = entry; + QTAILQ_INIT(&co->co_queue_wakeup); + return co; +} + +static void coroutine_delete(Coroutine *co) +{ + co->caller = NULL; + + if (CONFIG_COROUTINE_POOL) { + if (release_pool_size < POOL_BATCH_SIZE * 2) { + QSLIST_INSERT_HEAD_ATOMIC(&release_pool, co, pool_next); + atomic_inc(&release_pool_size); + return; + } + if (alloc_pool_size < POOL_BATCH_SIZE) { + QSLIST_INSERT_HEAD(&alloc_pool, co, pool_next); + alloc_pool_size++; + return; + } + } + + qemu_coroutine_delete(co); +} + +void qemu_coroutine_enter(Coroutine *co, void *opaque) +{ + Coroutine *self = qemu_coroutine_self(); + CoroutineAction ret; + + trace_qemu_coroutine_enter(self, co, opaque); + + if (co->caller) { + fprintf(stderr, "Co-routine re-entered recursively\n"); + abort(); + } + + co->caller = self; + co->entry_arg = opaque; + ret = qemu_coroutine_switch(self, co, COROUTINE_ENTER); + + qemu_co_queue_run_restart(co); + + switch (ret) { + case COROUTINE_YIELD: + return; + case COROUTINE_TERMINATE: + trace_qemu_coroutine_terminate(co); + coroutine_delete(co); + return; + default: + abort(); + } +} + +void coroutine_fn qemu_coroutine_yield(void) +{ + Coroutine *self = qemu_coroutine_self(); + Coroutine *to = self->caller; + + trace_qemu_coroutine_yield(self, to); + + if (!to) { + fprintf(stderr, "Co-routine is yielding to no one\n"); + abort(); + } + + self->caller = NULL; + qemu_coroutine_switch(self, to, COROUTINE_YIELD); +} diff --git a/qemu/util/qemu-error.c b/qemu/util/qemu-error.c index 77ea6c614..1ef35664a 100644 --- a/qemu/util/qemu-error.c +++ b/qemu/util/qemu-error.c @@ -10,7 +10,7 @@ * See the COPYING file in the top-level directory. */ -#include +#include "qemu/osdep.h" #include "monitor/monitor.h" #include "qemu/error-report.h" @@ -200,8 +200,8 @@ static void error_print_loc(void) bool enable_timestamp_msg; /* * Print an error message to current monitor if we have one, else to stderr. - * Format arguments like vsprintf(). The result should not contain - * newlines. + * Format arguments like vsprintf(). The resulting message should be + * a single phrase, with no newline or trailing punctuation. * Prepend the current location and append a newline. * It's wrong to call this in a QMP monitor. Use error_setg() there. */ @@ -210,7 +210,7 @@ void error_vreport(const char *fmt, va_list ap) GTimeVal tv; gchar *timestr; - if (enable_timestamp_msg) { + if (enable_timestamp_msg && !cur_mon) { g_get_current_time(&tv); timestr = g_time_val_to_iso8601(&tv); error_printf("%s ", timestr); @@ -224,8 +224,8 @@ void error_vreport(const char *fmt, va_list ap) /* * Print an error message to current monitor if we have one, else to stderr. - * Format arguments like sprintf(). The result should not contain - * newlines. + * Format arguments like sprintf(). The resulting message should be a + * single phrase, with no newline or trailing punctuation. * Prepend the current location and append a newline. * It's wrong to call this in a QMP monitor. Use error_setg() there. */ diff --git a/qemu/util/qemu-openpty.c b/qemu/util/qemu-openpty.c index 4c5321116..2e8b43bdf 100644 --- a/qemu/util/qemu-openpty.c +++ b/qemu/util/qemu-openpty.c @@ -32,7 +32,7 @@ * linked with -lutil. */ -#include "config-host.h" +#include "qemu/osdep.h" #include "qemu-common.h" #if defined(__GLIBC__) diff --git a/qemu/util/qemu-option.c b/qemu/util/qemu-option.c index efe9d279c..3467dc239 100644 --- a/qemu/util/qemu-option.c +++ b/qemu/util/qemu-option.c @@ -23,15 +23,17 @@ * THE SOFTWARE. */ -#include -#include +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/error-report.h" #include "qapi/qmp/types.h" -#include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qemu/option_int.h" +#include "qemu/cutils.h" +#include "qemu/id.h" +#include "qemu/help_option.h" /* * Extracts the name of an option from the parameter string (p points at the @@ -180,6 +182,11 @@ void parse_option_size(const char *name, const char *value, if (value != NULL) { sizef = strtod(value, &postfix); + if (sizef < 0 || sizef > UINT64_MAX) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, + "a non-negative number below 2^64"); + return; + } switch (*postfix) { case 'T': sizef *= 1024; @@ -200,10 +207,8 @@ void parse_option_size(const char *name, const char *value, break; default: error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, "a size"); -#if 0 /* conversion from qerror_report() to error_set() broke this: */ - error_printf_unless_qmp("You may use k, M, G or T suffixes for " + error_append_hint(errp, "You may use k, M, G or T suffixes for " "kilobytes, megabytes, gigabytes and terabytes.\n"); -#endif return; } } else { @@ -643,9 +648,8 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, if (!id_wellformed(id)) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "id", "an identifier"); -#if 0 /* conversion from qerror_report() to error_set() broke this: */ - error_printf_unless_qmp("Identifiers consist of letters, digits, '-', '.', '_', starting with a letter.\n"); -#endif + error_append_hint(errp, "Identifiers consist of letters, digits, " + "'-', '.', '_', starting with a letter.\n"); return NULL; } opts = qemu_opts_find(list, id); @@ -730,14 +734,35 @@ void qemu_opts_del(QemuOpts *opts) g_free(opts); } -void qemu_opts_print(QemuOpts *opts, const char *sep) +/* print value, escaping any commas in value */ +static void escaped_print(const char *value) +{ + const char *ptr; + + for (ptr = value; *ptr; ++ptr) { + if (*ptr == ',') { + putchar(','); + } + putchar(*ptr); + } +} + +void qemu_opts_print(QemuOpts *opts, const char *separator) { QemuOpt *opt; QemuOptDesc *desc = opts->list->desc; + const char *sep = ""; + + if (opts->id) { + printf("id=%s", opts->id); /* passed id_wellformed -> no commas */ + sep = separator; + } if (desc[0].name == NULL) { QTAILQ_FOREACH(opt, &opts->head, next) { - printf("%s%s=\"%s\"", sep, opt->name, opt->str); + printf("%s%s=", sep, opt->name); + escaped_print(opt->str); + sep = separator; } return; } @@ -750,13 +775,15 @@ void qemu_opts_print(QemuOpts *opts, const char *sep) continue; } if (desc->type == QEMU_OPT_STRING) { - printf("%s%s='%s'", sep, desc->name, value); + printf("%s%s=", sep, desc->name); + escaped_print(value); } else if ((desc->type == QEMU_OPT_SIZE || desc->type == QEMU_OPT_NUMBER) && opt) { printf("%s%s=%" PRId64, sep, desc->name, opt->value.uint); } else { printf("%s%s=%s", sep, desc->name, value); } + sep = separator; } } @@ -1081,19 +1108,19 @@ int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, { Location loc; QemuOpts *opts; - int rc; + int rc = 0; loc_push_none(&loc); QTAILQ_FOREACH(opts, &list->head, next) { loc_restore(&opts->loc); rc = func(opaque, opts, errp); if (rc) { - return rc; + break; } assert(!errp || !*errp); } loc_pop(&loc); - return 0; + return rc; } static size_t count_opts_list(QemuOptsList *list) diff --git a/qemu/util/qemu-progress.c b/qemu/util/qemu-progress.c index 4ee5cd07f..f74523376 100644 --- a/qemu/util/qemu-progress.c +++ b/qemu/util/qemu-progress.c @@ -22,9 +22,8 @@ * THE SOFTWARE. */ -#include "qemu-common.h" #include "qemu/osdep.h" -#include +#include "qemu-common.h" struct progress_state { float current; @@ -152,7 +151,8 @@ void qemu_progress_print(float delta, int max) state.current = current; if (current > (state.last_print + state.min_skip) || - (current == 100) || (current == 0)) { + current < (state.last_print - state.min_skip) || + current == 100 || current == 0) { state.last_print = state.current; state.print(); } diff --git a/qemu/util/qemu-sockets.c b/qemu/util/qemu-sockets.c index 2add83a0f..0d536911c 100644 --- a/qemu/util/qemu-sockets.c +++ b/qemu/util/qemu-sockets.c @@ -15,57 +15,25 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #include "monitor/monitor.h" +#include "qapi/error.h" #include "qemu/sockets.h" #include "qemu/main-loop.h" +#include "qapi/qmp-input-visitor.h" +#include "qapi/qmp-output-visitor.h" +#include "qapi-visit.h" +#include "qemu/cutils.h" #ifndef AI_ADDRCONFIG # define AI_ADDRCONFIG 0 #endif + #ifndef AI_V4MAPPED # define AI_V4MAPPED 0 #endif -/* used temporarily until all users are converted to QemuOpts */ -QemuOptsList socket_optslist = { - .name = "socket", - .head = QTAILQ_HEAD_INITIALIZER(socket_optslist.head), - .desc = { - { - .name = "path", - .type = QEMU_OPT_STRING, - },{ - .name = "host", - .type = QEMU_OPT_STRING, - },{ - .name = "port", - .type = QEMU_OPT_STRING, - },{ - .name = "localaddr", - .type = QEMU_OPT_STRING, - },{ - .name = "localport", - .type = QEMU_OPT_STRING, - },{ - .name = "to", - .type = QEMU_OPT_NUMBER, - },{ - .name = "ipv4", - .type = QEMU_OPT_BOOL, - },{ - .name = "ipv6", - .type = QEMU_OPT_BOOL, - }, - { /* end if list */ } - }, -}; static int inet_getport(struct addrinfo *e) { @@ -111,37 +79,86 @@ NetworkAddressFamily inet_netfamily(int family) return NETWORK_ADDRESS_FAMILY_UNKNOWN; } -int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp) +/* + * Matrix we're trying to apply + * + * ipv4 ipv6 family + * - - PF_UNSPEC + * - f PF_INET + * - t PF_INET6 + * f - PF_INET6 + * f f + * f t PF_INET6 + * t - PF_INET + * t f PF_INET + * t t PF_INET6 + * + * NB, this matrix is only about getting the neccessary results + * from getaddrinfo(). Some of the cases require further work + * after reading results from getaddrinfo in order to fully + * apply the logic the end user wants. eg with the last case + * ipv4=t + ipv6=t + PF_INET6, getaddrinfo alone can only + * guarantee the ipv6=t part of the request - we need more + * checks to provide ipv4=t part of the guarantee. This is + * outside scope of this method and not currently handled by + * callers at all. + */ +static int inet_ai_family_from_address(InetSocketAddress *addr, + Error **errp) +{ + if (addr->has_ipv6 && addr->has_ipv4 && + !addr->ipv6 && !addr->ipv4) { + error_setg(errp, "Cannot disable IPv4 and IPv6 at same time"); + return PF_UNSPEC; + } + if ((addr->has_ipv6 && addr->ipv6) || (addr->has_ipv4 && !addr->ipv4)) { + return PF_INET6; + } + if ((addr->has_ipv4 && addr->ipv4) || (addr->has_ipv6 && !addr->ipv6)) { + return PF_INET; + } + return PF_UNSPEC; +} + +static int inet_listen_saddr(InetSocketAddress *saddr, + int port_offset, + bool update_addr, + Error **errp) { struct addrinfo ai,*res,*e; - const char *addr; char port[33]; char uaddr[INET6_ADDRSTRLEN+1]; char uport[33]; - int slisten, rc, to, port_min, port_max, p; + int slisten, rc, port_min, port_max, p; + Error *err = NULL; memset(&ai,0, sizeof(ai)); ai.ai_flags = AI_PASSIVE; - ai.ai_family = PF_UNSPEC; + ai.ai_family = inet_ai_family_from_address(saddr, &err); ai.ai_socktype = SOCK_STREAM; - if ((qemu_opt_get(opts, "host") == NULL) || - (qemu_opt_get(opts, "port") == NULL)) { - error_setg(errp, "host and/or port not specified"); + if (err) { + error_propagate(errp, err); return -1; } - pstrcpy(port, sizeof(port), qemu_opt_get(opts, "port")); - addr = qemu_opt_get(opts, "host"); - to = qemu_opt_get_number(opts, "to", 0); - if (qemu_opt_get_bool(opts, "ipv4", 0)) - ai.ai_family = PF_INET; - if (qemu_opt_get_bool(opts, "ipv6", 0)) - ai.ai_family = PF_INET6; + if (saddr->host == NULL) { + error_setg(errp, "host not specified"); + return -1; + } + if (saddr->port != NULL) { + pstrcpy(port, sizeof(port), saddr->port); + } else { + port[0] = '\0'; + } /* lookup */ if (port_offset) { unsigned long long baseport; + if (strlen(port) == 0) { + error_setg(errp, "port not specified"); + return -1; + } if (parse_uint_full(port, &baseport, 10) < 0) { error_setg(errp, "can't convert to a number: %s", port); return -1; @@ -153,10 +170,11 @@ int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp) } snprintf(port, sizeof(port), "%d", (int)baseport + port_offset); } - rc = getaddrinfo(strlen(addr) ? addr : NULL, port, &ai, &res); + rc = getaddrinfo(strlen(saddr->host) ? saddr->host : NULL, + strlen(port) ? port : NULL, &ai, &res); if (rc != 0) { - error_setg(errp, "address resolution failed for %s:%s: %s", addr, port, - gai_strerror(rc)); + error_setg(errp, "address resolution failed for %s:%s: %s", + saddr->host, port, gai_strerror(rc)); return -1; } @@ -184,7 +202,7 @@ int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp) #endif port_min = inet_getport(e); - port_max = to ? to + port_offset : port_min; + port_max = saddr->has_to ? saddr->to + port_offset : port_min; for (p = port_min; p <= port_max; p++) { inet_setport(e, p); if (bind(slisten, e->ai_addr, e->ai_addrlen) == 0) { @@ -208,13 +226,15 @@ listen: freeaddrinfo(res); return -1; } - qemu_opt_set(opts, "host", uaddr, &error_abort); - qemu_opt_set_number(opts, "port", inet_getport(e) - port_offset, - &error_abort); - qemu_opt_set_bool(opts, "ipv6", e->ai_family == PF_INET6, - &error_abort); - qemu_opt_set_bool(opts, "ipv4", e->ai_family != PF_INET6, - &error_abort); + if (update_addr) { + g_free(saddr->host); + saddr->host = g_strdup(uaddr); + g_free(saddr->port); + saddr->port = g_strdup_printf("%d", + inet_getport(e) - port_offset); + saddr->has_ipv6 = saddr->ipv6 = e->ai_family == PF_INET6; + saddr->has_ipv4 = saddr->ipv4 = e->ai_family != PF_INET6; + } freeaddrinfo(res); return slisten; } @@ -251,7 +271,7 @@ static void wait_for_connect(void *opaque) do { rc = qemu_getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &val, &valsize); - } while (rc == -1 && socket_error() == EINTR); + } while (rc == -1 && errno == EINTR); /* update rc to contain error */ if (!rc && val) { @@ -313,7 +333,7 @@ static int inet_connect_addr(struct addrinfo *addr, bool *in_progress, do { rc = 0; if (connect(sock, addr->ai_addr, addr->ai_addrlen) < 0) { - rc = -socket_error(); + rc = -errno; } } while (rc == -EINTR); @@ -329,38 +349,50 @@ static int inet_connect_addr(struct addrinfo *addr, bool *in_progress, return sock; } -static struct addrinfo *inet_parse_connect_opts(QemuOpts *opts, Error **errp) +static struct addrinfo *inet_parse_connect_saddr(InetSocketAddress *saddr, + Error **errp) { struct addrinfo ai, *res; int rc; - const char *addr; - const char *port; + Error *err = NULL; + static int useV4Mapped = 1; memset(&ai, 0, sizeof(ai)); - ai.ai_flags = AI_CANONNAME | AI_V4MAPPED | AI_ADDRCONFIG; - ai.ai_family = PF_UNSPEC; + ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; + if (atomic_read(&useV4Mapped)) { + ai.ai_flags |= AI_V4MAPPED; + } + ai.ai_family = inet_ai_family_from_address(saddr, &err); ai.ai_socktype = SOCK_STREAM; - addr = qemu_opt_get(opts, "host"); - port = qemu_opt_get(opts, "port"); - if (addr == NULL || port == NULL) { - error_setg(errp, "host and/or port not specified"); + if (err) { + error_propagate(errp, err); return NULL; } - if (qemu_opt_get_bool(opts, "ipv4", 0)) { - ai.ai_family = PF_INET; - } - if (qemu_opt_get_bool(opts, "ipv6", 0)) { - ai.ai_family = PF_INET6; + if (saddr->host == NULL || saddr->port == NULL) { + error_setg(errp, "host and/or port not specified"); + return NULL; } /* lookup */ - rc = getaddrinfo(addr, port, &ai, &res); + rc = getaddrinfo(saddr->host, saddr->port, &ai, &res); + + /* At least FreeBSD and OS-X 10.6 declare AI_V4MAPPED but + * then don't implement it in their getaddrinfo(). Detect + * this and retry without the flag since that's preferrable + * to a fatal error + */ + if (rc == EAI_BADFLAGS && + (ai.ai_flags & AI_V4MAPPED)) { + atomic_set(&useV4Mapped, 0); + ai.ai_flags &= ~AI_V4MAPPED; + rc = getaddrinfo(saddr->host, saddr->port, &ai, &res); + } if (rc != 0) { - error_setg(errp, "address resolution failed for %s:%s: %s", addr, port, - gai_strerror(rc)); + error_setg(errp, "address resolution failed for %s:%s: %s", + saddr->host, saddr->port, gai_strerror(rc)); return NULL; } return res; @@ -369,8 +401,7 @@ static struct addrinfo *inet_parse_connect_opts(QemuOpts *opts, Error **errp) /** * Create a socket and connect it to an address. * - * @opts: QEMU options, recognized parameters strings "host" and "port", - * bools "ipv4" and "ipv6". + * @saddr: Inet socket address specification * @errp: set on error * @callback: callback function for non-blocking connect * @opaque: opaque for callback function @@ -381,8 +412,8 @@ static struct addrinfo *inet_parse_connect_opts(QemuOpts *opts, Error **errp) * function succeeds, callback will be called when the connection * completes, with the file descriptor on success, or -1 on error. */ -int inet_connect_opts(QemuOpts *opts, Error **errp, - NonBlockingConnectHandler *callback, void *opaque) +static int inet_connect_saddr(InetSocketAddress *saddr, Error **errp, + NonBlockingConnectHandler *callback, void *opaque) { Error *local_err = NULL; struct addrinfo *res, *e; @@ -390,7 +421,7 @@ int inet_connect_opts(QemuOpts *opts, Error **errp, bool in_progress; ConnectState *connect_state = NULL; - res = inet_parse_connect_opts(opts, errp); + res = inet_parse_connect_saddr(saddr, errp); if (!res) { return -1; } @@ -429,38 +460,41 @@ int inet_connect_opts(QemuOpts *opts, Error **errp, return sock; } -int inet_dgram_opts(QemuOpts *opts, Error **errp) +static int inet_dgram_saddr(InetSocketAddress *sraddr, + InetSocketAddress *sladdr, + Error **errp) { struct addrinfo ai, *peer = NULL, *local = NULL; const char *addr; const char *port; int sock = -1, rc; + Error *err = NULL; /* lookup peer addr */ memset(&ai,0, sizeof(ai)); ai.ai_flags = AI_CANONNAME | AI_V4MAPPED | AI_ADDRCONFIG; - ai.ai_family = PF_UNSPEC; + ai.ai_family = inet_ai_family_from_address(sraddr, &err); ai.ai_socktype = SOCK_DGRAM; - addr = qemu_opt_get(opts, "host"); - port = qemu_opt_get(opts, "port"); + if (err) { + error_propagate(errp, err); + goto err; + } + + addr = sraddr->host; + port = sraddr->port; if (addr == NULL || strlen(addr) == 0) { addr = "localhost"; } if (port == NULL || strlen(port) == 0) { error_setg(errp, "remote port not specified"); - return -1; + goto err; } - if (qemu_opt_get_bool(opts, "ipv4", 0)) - ai.ai_family = PF_INET; - if (qemu_opt_get_bool(opts, "ipv6", 0)) - ai.ai_family = PF_INET6; - if (0 != (rc = getaddrinfo(addr, port, &ai, &peer))) { error_setg(errp, "address resolution failed for %s:%s: %s", addr, port, gai_strerror(rc)); - return -1; + goto err; } /* lookup local addr */ @@ -469,13 +503,19 @@ int inet_dgram_opts(QemuOpts *opts, Error **errp) ai.ai_family = peer->ai_family; ai.ai_socktype = SOCK_DGRAM; - addr = qemu_opt_get(opts, "localaddr"); - port = qemu_opt_get(opts, "localport"); - if (addr == NULL || strlen(addr) == 0) { + if (sladdr) { + addr = sladdr->host; + port = sladdr->port; + if (addr == NULL || strlen(addr) == 0) { + addr = NULL; + } + if (!port || strlen(port) == 0) { + port = "0"; + } + } else { addr = NULL; - } - if (!port || strlen(port) == 0) port = "0"; + } if (0 != (rc = getaddrinfo(addr, port, &ai, &local))) { error_setg(errp, "address resolution failed for %s:%s: %s", addr, port, @@ -584,51 +624,31 @@ fail: return NULL; } -static void inet_addr_to_opts(QemuOpts *opts, const InetSocketAddress *addr) -{ - bool ipv4 = addr->ipv4 || !addr->has_ipv4; - bool ipv6 = addr->ipv6 || !addr->has_ipv6; - - if (!ipv4 || !ipv6) { - qemu_opt_set_bool(opts, "ipv4", ipv4, &error_abort); - qemu_opt_set_bool(opts, "ipv6", ipv6, &error_abort); - } - if (addr->has_to) { - qemu_opt_set_number(opts, "to", addr->to, &error_abort); - } - qemu_opt_set(opts, "host", addr->host, &error_abort); - qemu_opt_set(opts, "port", addr->port, &error_abort); -} - int inet_listen(const char *str, char *ostr, int olen, int socktype, int port_offset, Error **errp) { - QemuOpts *opts; char *optstr; int sock = -1; InetSocketAddress *addr; addr = inet_parse(str, errp); if (addr != NULL) { - opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); - inet_addr_to_opts(opts, addr); - qapi_free_InetSocketAddress(addr); - sock = inet_listen_opts(opts, port_offset, errp); + sock = inet_listen_saddr(addr, port_offset, true, errp); if (sock != -1 && ostr) { optstr = strchr(str, ','); - if (qemu_opt_get_bool(opts, "ipv6", 0)) { + if (addr->ipv6) { snprintf(ostr, olen, "[%s]:%s%s", - qemu_opt_get(opts, "host"), - qemu_opt_get(opts, "port"), + addr->host, + addr->port, optstr ? optstr : ""); } else { snprintf(ostr, olen, "%s:%s%s", - qemu_opt_get(opts, "host"), - qemu_opt_get(opts, "port"), + addr->host, + addr->port, optstr ? optstr : ""); } } - qemu_opts_del(opts); + qapi_free_InetSocketAddress(addr); } return sock; } @@ -643,17 +663,13 @@ int inet_listen(const char *str, char *ostr, int olen, **/ int inet_connect(const char *str, Error **errp) { - QemuOpts *opts; int sock = -1; InetSocketAddress *addr; addr = inet_parse(str, errp); if (addr != NULL) { - opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); - inet_addr_to_opts(opts, addr); + sock = inet_connect_saddr(addr, errp, NULL, NULL); qapi_free_InetSocketAddress(addr); - sock = inet_connect_opts(opts, errp, NULL, NULL); - qemu_opts_del(opts); } return sock; } @@ -675,7 +691,6 @@ int inet_nonblocking_connect(const char *str, NonBlockingConnectHandler *callback, void *opaque, Error **errp) { - QemuOpts *opts; int sock = -1; InetSocketAddress *addr; @@ -683,21 +698,19 @@ int inet_nonblocking_connect(const char *str, addr = inet_parse(str, errp); if (addr != NULL) { - opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); - inet_addr_to_opts(opts, addr); + sock = inet_connect_saddr(addr, errp, callback, opaque); qapi_free_InetSocketAddress(addr); - sock = inet_connect_opts(opts, errp, callback, opaque); - qemu_opts_del(opts); } return sock; } #ifndef _WIN32 -int unix_listen_opts(QemuOpts *opts, Error **errp) +static int unix_listen_saddr(UnixSocketAddress *saddr, + bool update_addr, + Error **errp) { struct sockaddr_un un; - const char *path = qemu_opt_get(opts, "path"); int sock, fd; sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0); @@ -708,8 +721,8 @@ int unix_listen_opts(QemuOpts *opts, Error **errp) memset(&un, 0, sizeof(un)); un.sun_family = AF_UNIX; - if (path && strlen(path)) { - snprintf(un.sun_path, sizeof(un.sun_path), "%s", path); + if (saddr->path && strlen(saddr->path)) { + snprintf(un.sun_path, sizeof(un.sun_path), "%s", saddr->path); } else { const char *tmpdir = getenv("TMPDIR"); tmpdir = tmpdir ? tmpdir : "/tmp"; @@ -734,11 +747,13 @@ int unix_listen_opts(QemuOpts *opts, Error **errp) goto err; } close(fd); - qemu_opt_set(opts, "path", un.sun_path, &error_abort); + if (update_addr) { + g_free(saddr->path); + saddr->path = g_strdup(un.sun_path); + } } - if ((access(un.sun_path, F_OK) == 0) && - unlink(un.sun_path) < 0) { + if (unlink(un.sun_path) < 0 && errno != ENOENT) { error_setg_errno(errp, errno, "Failed to unlink socket %s", un.sun_path); goto err; @@ -759,15 +774,14 @@ err: return -1; } -int unix_connect_opts(QemuOpts *opts, Error **errp, - NonBlockingConnectHandler *callback, void *opaque) +static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp, + NonBlockingConnectHandler *callback, void *opaque) { struct sockaddr_un un; - const char *path = qemu_opt_get(opts, "path"); ConnectState *connect_state = NULL; int sock, rc; - if (path == NULL) { + if (saddr->path == NULL) { error_setg(errp, "unix connect: no path specified"); return -1; } @@ -786,13 +800,13 @@ int unix_connect_opts(QemuOpts *opts, Error **errp, memset(&un, 0, sizeof(un)); un.sun_family = AF_UNIX; - snprintf(un.sun_path, sizeof(un.sun_path), "%s", path); + snprintf(un.sun_path, sizeof(un.sun_path), "%s", saddr->path); /* connect to peer */ do { rc = 0; if (connect(sock, (struct sockaddr *) &un, sizeof(un)) < 0) { - rc = -socket_error(); + rc = -errno; } } while (rc == -EINTR); @@ -819,15 +833,17 @@ int unix_connect_opts(QemuOpts *opts, Error **errp, #else -int unix_listen_opts(QemuOpts *opts, Error **errp) +static int unix_listen_saddr(UnixSocketAddress *saddr, + bool update_addr, + Error **errp) { error_setg(errp, "unix sockets are not available on windows"); errno = ENOTSUP; return -1; } -int unix_connect_opts(QemuOpts *opts, Error **errp, - NonBlockingConnectHandler *callback, void *opaque) +static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp, + NonBlockingConnectHandler *callback, void *opaque) { error_setg(errp, "unix sockets are not available on windows"); errno = ENOTSUP; @@ -838,11 +854,11 @@ int unix_connect_opts(QemuOpts *opts, Error **errp, /* compatibility wrapper */ int unix_listen(const char *str, char *ostr, int olen, Error **errp) { - QemuOpts *opts; char *path, *optstr; int sock, len; + UnixSocketAddress *saddr; - opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); + saddr = g_new0(UnixSocketAddress, 1); optstr = strchr(str, ','); if (optstr) { @@ -850,30 +866,29 @@ int unix_listen(const char *str, char *ostr, int olen, Error **errp) if (len) { path = g_malloc(len+1); snprintf(path, len+1, "%.*s", len, str); - qemu_opt_set(opts, "path", path, &error_abort); - g_free(path); + saddr->path = path; } } else { - qemu_opt_set(opts, "path", str, &error_abort); + saddr->path = g_strdup(str); } - sock = unix_listen_opts(opts, errp); + sock = unix_listen_saddr(saddr, true, errp); if (sock != -1 && ostr) - snprintf(ostr, olen, "%s%s", qemu_opt_get(opts, "path"), optstr ? optstr : ""); - qemu_opts_del(opts); + snprintf(ostr, olen, "%s%s", saddr->path, optstr ? optstr : ""); + qapi_free_UnixSocketAddress(saddr); return sock; } int unix_connect(const char *path, Error **errp) { - QemuOpts *opts; + UnixSocketAddress *saddr; int sock; - opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); - qemu_opt_set(opts, "path", path, &error_abort); - sock = unix_connect_opts(opts, errp, NULL, NULL); - qemu_opts_del(opts); + saddr = g_new0(UnixSocketAddress, 1); + saddr->path = g_strdup(path); + sock = unix_connect_saddr(saddr, errp, NULL, NULL); + qapi_free_UnixSocketAddress(saddr); return sock; } @@ -882,15 +897,15 @@ int unix_nonblocking_connect(const char *path, NonBlockingConnectHandler *callback, void *opaque, Error **errp) { - QemuOpts *opts; + UnixSocketAddress *saddr; int sock = -1; g_assert(callback != NULL); - opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); - qemu_opt_set(opts, "path", path, &error_abort); - sock = unix_connect_opts(opts, errp, callback, opaque); - qemu_opts_del(opts); + saddr = g_new0(UnixSocketAddress, 1); + saddr->path = g_strdup(path); + sock = unix_connect_saddr(saddr, errp, callback, opaque); + qapi_free_UnixSocketAddress(saddr); return sock; } @@ -904,23 +919,23 @@ SocketAddress *socket_parse(const char *str, Error **errp) error_setg(errp, "invalid Unix socket address"); goto fail; } else { - addr->kind = SOCKET_ADDRESS_KIND_UNIX; - addr->q_unix = g_new(UnixSocketAddress, 1); - addr->q_unix->path = g_strdup(str + 5); + addr->type = SOCKET_ADDRESS_KIND_UNIX; + addr->u.q_unix.data = g_new(UnixSocketAddress, 1); + addr->u.q_unix.data->path = g_strdup(str + 5); } } else if (strstart(str, "fd:", NULL)) { if (str[3] == '\0') { error_setg(errp, "invalid file descriptor address"); goto fail; } else { - addr->kind = SOCKET_ADDRESS_KIND_FD; - addr->fd = g_new(String, 1); - addr->fd->str = g_strdup(str + 3); + addr->type = SOCKET_ADDRESS_KIND_FD; + addr->u.fd.data = g_new(String, 1); + addr->u.fd.data->str = g_strdup(str + 3); } } else { - addr->kind = SOCKET_ADDRESS_KIND_INET; - addr->inet = inet_parse(str, errp); - if (addr->inet == NULL) { + addr->type = SOCKET_ADDRESS_KIND_INET; + addr->u.inet.data = inet_parse(str, errp); + if (addr->u.inet.data == NULL) { goto fail; } } @@ -934,23 +949,19 @@ fail: int socket_connect(SocketAddress *addr, Error **errp, NonBlockingConnectHandler *callback, void *opaque) { - QemuOpts *opts; int fd; - opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); - switch (addr->kind) { + switch (addr->type) { case SOCKET_ADDRESS_KIND_INET: - inet_addr_to_opts(opts, addr->inet); - fd = inet_connect_opts(opts, errp, callback, opaque); + fd = inet_connect_saddr(addr->u.inet.data, errp, callback, opaque); break; case SOCKET_ADDRESS_KIND_UNIX: - qemu_opt_set(opts, "path", addr->q_unix->path, &error_abort); - fd = unix_connect_opts(opts, errp, callback, opaque); + fd = unix_connect_saddr(addr->u.q_unix.data, errp, callback, opaque); break; case SOCKET_ADDRESS_KIND_FD: - fd = monitor_get_fd(cur_mon, addr->fd->str, errp); + fd = monitor_get_fd(cur_mon, addr->u.fd.data->str, errp); if (fd >= 0 && callback) { qemu_set_nonblock(fd); callback(fd, NULL, opaque); @@ -960,58 +971,183 @@ int socket_connect(SocketAddress *addr, Error **errp, default: abort(); } - qemu_opts_del(opts); return fd; } int socket_listen(SocketAddress *addr, Error **errp) { - QemuOpts *opts; int fd; - opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); - switch (addr->kind) { + switch (addr->type) { case SOCKET_ADDRESS_KIND_INET: - inet_addr_to_opts(opts, addr->inet); - fd = inet_listen_opts(opts, 0, errp); + fd = inet_listen_saddr(addr->u.inet.data, 0, false, errp); break; case SOCKET_ADDRESS_KIND_UNIX: - qemu_opt_set(opts, "path", addr->q_unix->path, &error_abort); - fd = unix_listen_opts(opts, errp); + fd = unix_listen_saddr(addr->u.q_unix.data, false, errp); break; case SOCKET_ADDRESS_KIND_FD: - fd = monitor_get_fd(cur_mon, addr->fd->str, errp); + fd = monitor_get_fd(cur_mon, addr->u.fd.data->str, errp); break; default: abort(); } - qemu_opts_del(opts); return fd; } int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp) { - QemuOpts *opts; int fd; - opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); - switch (remote->kind) { + switch (remote->type) { case SOCKET_ADDRESS_KIND_INET: - inet_addr_to_opts(opts, remote->inet); - if (local) { - qemu_opt_set(opts, "localaddr", local->inet->host, &error_abort); - qemu_opt_set(opts, "localport", local->inet->port, &error_abort); - } - fd = inet_dgram_opts(opts, errp); + fd = inet_dgram_saddr(remote->u.inet.data, + local ? local->u.inet.data : NULL, errp); break; default: error_setg(errp, "socket type unsupported for datagram"); fd = -1; } - qemu_opts_del(opts); return fd; } + + +static SocketAddress * +socket_sockaddr_to_address_inet(struct sockaddr_storage *sa, + socklen_t salen, + Error **errp) +{ + char host[NI_MAXHOST]; + char serv[NI_MAXSERV]; + SocketAddress *addr; + InetSocketAddress *inet; + int ret; + + ret = getnameinfo((struct sockaddr *)sa, salen, + host, sizeof(host), + serv, sizeof(serv), + NI_NUMERICHOST | NI_NUMERICSERV); + if (ret != 0) { + error_setg(errp, "Cannot format numeric socket address: %s", + gai_strerror(ret)); + return NULL; + } + + addr = g_new0(SocketAddress, 1); + addr->type = SOCKET_ADDRESS_KIND_INET; + inet = addr->u.inet.data = g_new0(InetSocketAddress, 1); + inet->host = g_strdup(host); + inet->port = g_strdup(serv); + if (sa->ss_family == AF_INET) { + inet->has_ipv4 = inet->ipv4 = true; + } else { + inet->has_ipv6 = inet->ipv6 = true; + } + + return addr; +} + + +#ifndef WIN32 +static SocketAddress * +socket_sockaddr_to_address_unix(struct sockaddr_storage *sa, + socklen_t salen, + Error **errp) +{ + SocketAddress *addr; + struct sockaddr_un *su = (struct sockaddr_un *)sa; + + addr = g_new0(SocketAddress, 1); + addr->type = SOCKET_ADDRESS_KIND_UNIX; + addr->u.q_unix.data = g_new0(UnixSocketAddress, 1); + if (su->sun_path[0]) { + addr->u.q_unix.data->path = g_strndup(su->sun_path, + sizeof(su->sun_path)); + } + + return addr; +} +#endif /* WIN32 */ + +SocketAddress * +socket_sockaddr_to_address(struct sockaddr_storage *sa, + socklen_t salen, + Error **errp) +{ + switch (sa->ss_family) { + case AF_INET: + case AF_INET6: + return socket_sockaddr_to_address_inet(sa, salen, errp); + +#ifndef WIN32 + case AF_UNIX: + return socket_sockaddr_to_address_unix(sa, salen, errp); +#endif /* WIN32 */ + + default: + error_setg(errp, "socket family %d unsupported", + sa->ss_family); + return NULL; + } + return 0; +} + + +SocketAddress *socket_local_address(int fd, Error **errp) +{ + struct sockaddr_storage ss; + socklen_t sslen = sizeof(ss); + + if (getsockname(fd, (struct sockaddr *)&ss, &sslen) < 0) { + error_setg_errno(errp, errno, "%s", + "Unable to query local socket address"); + return NULL; + } + + return socket_sockaddr_to_address(&ss, sslen, errp); +} + + +SocketAddress *socket_remote_address(int fd, Error **errp) +{ + struct sockaddr_storage ss; + socklen_t sslen = sizeof(ss); + + if (getpeername(fd, (struct sockaddr *)&ss, &sslen) < 0) { + error_setg_errno(errp, errno, "%s", + "Unable to query remote socket address"); + return NULL; + } + + return socket_sockaddr_to_address(&ss, sslen, errp); +} + + +void qapi_copy_SocketAddress(SocketAddress **p_dest, + SocketAddress *src) +{ + QmpOutputVisitor *qov; + QmpInputVisitor *qiv; + Visitor *ov, *iv; + QObject *obj; + + *p_dest = NULL; + + qov = qmp_output_visitor_new(); + ov = qmp_output_get_visitor(qov); + visit_type_SocketAddress(ov, NULL, &src, &error_abort); + obj = qmp_output_get_qobject(qov); + qmp_output_visitor_cleanup(qov); + if (!obj) { + return; + } + + qiv = qmp_input_visitor_new(obj); + iv = qmp_input_get_visitor(qiv); + visit_type_SocketAddress(iv, NULL, p_dest, &error_abort); + qmp_input_visitor_cleanup(qiv); + qobject_decref(obj); +} diff --git a/qemu/util/qemu-thread-posix.c b/qemu/util/qemu-thread-posix.c index ba67cec62..74a3023f3 100644 --- a/qemu/util/qemu-thread-posix.c +++ b/qemu/util/qemu-thread-posix.c @@ -10,16 +10,7 @@ * See the COPYING file in the top-level directory. * */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" #ifdef __linux__ #include #include @@ -298,7 +289,16 @@ static inline void futex_wake(QemuEvent *ev, int n) static inline void futex_wait(QemuEvent *ev, unsigned val) { - futex(ev, FUTEX_WAIT, (int) val, NULL, NULL, 0); + while (futex(ev, FUTEX_WAIT, (int) val, NULL, NULL, 0)) { + switch (errno) { + case EWOULDBLOCK: + return; + case EINTR: + break; /* get out of switch and retry */ + default: + abort(); + } + } } #else static inline void futex_wake(QemuEvent *ev, int n) @@ -389,7 +389,7 @@ void qemu_event_wait(QemuEvent *ev) /* * Leave the event reset and tell qemu_event_set that there * are waiters. No need to retry, because there cannot be - * a concurent busy->free transition. After the CAS, the + * a concurrent busy->free transition. After the CAS, the * event will be either set or busy. */ if (atomic_cmpxchg(&ev->value, EV_FREE, EV_BUSY) == EV_SET) { diff --git a/qemu/util/qemu-thread-win32.c b/qemu/util/qemu-thread-win32.c index 406b52f91..98a5ddff8 100644 --- a/qemu/util/qemu-thread-win32.c +++ b/qemu/util/qemu-thread-win32.c @@ -10,12 +10,11 @@ * See the COPYING file in the top-level directory. * */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/thread.h" #include "qemu/notify.h" #include -#include -#include static bool name_threads; @@ -238,10 +237,34 @@ void qemu_sem_wait(QemuSemaphore *sem) } } +/* Wrap a Win32 manual-reset event with a fast userspace path. The idea + * is to reset the Win32 event lazily, as part of a test-reset-test-wait + * sequence. Such a sequence is, indeed, how QemuEvents are used by + * RCU and other subsystems! + * + * Valid transitions: + * - free->set, when setting the event + * - busy->set, when setting the event, followed by futex_wake + * - set->free, when resetting the event + * - free->busy, when waiting + * + * set->busy does not happen (it can be observed from the outside but + * it really is set->free->busy). + * + * busy->free provably cannot happen; to enforce it, the set->free transition + * is done with an OR, which becomes a no-op if the event has concurrently + * transitioned to free or busy (and is faster than cmpxchg). + */ + +#define EV_SET 0 +#define EV_FREE 1 +#define EV_BUSY -1 + void qemu_event_init(QemuEvent *ev, bool init) { /* Manual reset. */ - ev->event = CreateEvent(NULL, TRUE, init, NULL); + ev->event = CreateEvent(NULL, TRUE, TRUE, NULL); + ev->value = (init ? EV_SET : EV_FREE); } void qemu_event_destroy(QemuEvent *ev) @@ -251,17 +274,51 @@ void qemu_event_destroy(QemuEvent *ev) void qemu_event_set(QemuEvent *ev) { - SetEvent(ev->event); + if (atomic_mb_read(&ev->value) != EV_SET) { + if (atomic_xchg(&ev->value, EV_SET) == EV_BUSY) { + /* There were waiters, wake them up. */ + SetEvent(ev->event); + } + } } void qemu_event_reset(QemuEvent *ev) { - ResetEvent(ev->event); + if (atomic_mb_read(&ev->value) == EV_SET) { + /* If there was a concurrent reset (or even reset+wait), + * do nothing. Otherwise change EV_SET->EV_FREE. + */ + atomic_or(&ev->value, EV_FREE); + } } void qemu_event_wait(QemuEvent *ev) { - WaitForSingleObject(ev->event, INFINITE); + unsigned value; + + value = atomic_mb_read(&ev->value); + if (value != EV_SET) { + if (value == EV_FREE) { + /* qemu_event_set is not yet going to call SetEvent, but we are + * going to do another check for EV_SET below when setting EV_BUSY. + * At that point it is safe to call WaitForSingleObject. + */ + ResetEvent(ev->event); + + /* Tell qemu_event_set that there are waiters. No need to retry + * because there cannot be a concurent busy->free transition. + * After the CAS, the event will be either set or busy. + */ + if (atomic_cmpxchg(&ev->value, EV_FREE, EV_BUSY) == EV_SET) { + value = EV_SET; + } else { + value = EV_BUSY; + } + } + if (value == EV_BUSY) { + WaitForSingleObject(ev->event, INFINITE); + } + } } struct QemuThreadData { diff --git a/qemu/util/qemu-timer-common.c b/qemu/util/qemu-timer-common.c index 95e0847c7..06d084d36 100644 --- a/qemu/util/qemu-timer-common.c +++ b/qemu/util/qemu-timer-common.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu/timer.h" /***********************************************************/ diff --git a/qemu/util/rcu.c b/qemu/util/rcu.c index cdcad678b..bceb3e472 100644 --- a/qemu/util/rcu.c +++ b/qemu/util/rcu.c @@ -26,12 +26,8 @@ * IBM's contributions to this file may be relicensed under LGPLv2 or later. */ +#include "qemu/osdep.h" #include "qemu-common.h" -#include -#include -#include -#include -#include #include "qemu/rcu.h" #include "qemu/atomic.h" #include "qemu/thread.h" @@ -47,7 +43,8 @@ unsigned long rcu_gp_ctr = RCU_GP_LOCKED; QemuEvent rcu_gp_event; -static QemuMutex rcu_gp_lock; +static QemuMutex rcu_registry_lock; +static QemuMutex rcu_sync_lock; /* * Check whether a quiescent state was crossed between the beginning of @@ -66,7 +63,7 @@ static inline int rcu_gp_ongoing(unsigned long *ctr) */ __thread struct rcu_reader_data rcu_reader; -/* Protected by rcu_gp_lock. */ +/* Protected by rcu_registry_lock. */ typedef QLIST_HEAD(, rcu_reader_data) ThreadList; static ThreadList registry = QLIST_HEAD_INITIALIZER(registry); @@ -114,10 +111,26 @@ static void wait_for_readers(void) break; } - /* Wait for one thread to report a quiescent state and - * try again. + /* Wait for one thread to report a quiescent state and try again. + * Release rcu_registry_lock, so rcu_(un)register_thread() doesn't + * wait too much time. + * + * rcu_register_thread() may add nodes to ®istry; it will not + * wake up synchronize_rcu, but that is okay because at least another + * thread must exit its RCU read-side critical section before + * synchronize_rcu is done. The next iteration of the loop will + * move the new thread's rcu_reader from ®istry to &qsreaders, + * because rcu_gp_ongoing() will return false. + * + * rcu_unregister_thread() may remove nodes from &qsreaders instead + * of ®istry if it runs during qemu_event_wait. That's okay; + * the node then will not be added back to ®istry by QLIST_SWAP + * below. The invariant is that the node is part of one list when + * rcu_registry_lock is released. */ + qemu_mutex_unlock(&rcu_registry_lock); qemu_event_wait(&rcu_gp_event); + qemu_mutex_lock(&rcu_registry_lock); } /* put back the reader list in the registry */ @@ -126,7 +139,8 @@ static void wait_for_readers(void) void synchronize_rcu(void) { - qemu_mutex_lock(&rcu_gp_lock); + qemu_mutex_lock(&rcu_sync_lock); + qemu_mutex_lock(&rcu_registry_lock); if (!QLIST_EMPTY(®istry)) { /* In either case, the atomic_mb_set below blocks stores that free @@ -149,7 +163,8 @@ void synchronize_rcu(void) wait_for_readers(); } - qemu_mutex_unlock(&rcu_gp_lock); + qemu_mutex_unlock(&rcu_registry_lock); + qemu_mutex_unlock(&rcu_sync_lock); } @@ -273,23 +288,24 @@ void call_rcu1(struct rcu_head *node, void (*func)(struct rcu_head *node)) void rcu_register_thread(void) { assert(rcu_reader.ctr == 0); - qemu_mutex_lock(&rcu_gp_lock); + qemu_mutex_lock(&rcu_registry_lock); QLIST_INSERT_HEAD(®istry, &rcu_reader, node); - qemu_mutex_unlock(&rcu_gp_lock); + qemu_mutex_unlock(&rcu_registry_lock); } void rcu_unregister_thread(void) { - qemu_mutex_lock(&rcu_gp_lock); + qemu_mutex_lock(&rcu_registry_lock); QLIST_REMOVE(&rcu_reader, node); - qemu_mutex_unlock(&rcu_gp_lock); + qemu_mutex_unlock(&rcu_registry_lock); } static void rcu_init_complete(void) { QemuThread thread; - qemu_mutex_init(&rcu_gp_lock); + qemu_mutex_init(&rcu_registry_lock); + qemu_mutex_init(&rcu_sync_lock); qemu_event_init(&rcu_gp_event, true); qemu_event_init(&rcu_call_ready_event, false); @@ -306,12 +322,14 @@ static void rcu_init_complete(void) #ifdef CONFIG_POSIX static void rcu_init_lock(void) { - qemu_mutex_lock(&rcu_gp_lock); + qemu_mutex_lock(&rcu_sync_lock); + qemu_mutex_lock(&rcu_registry_lock); } static void rcu_init_unlock(void) { - qemu_mutex_unlock(&rcu_gp_lock); + qemu_mutex_unlock(&rcu_registry_lock); + qemu_mutex_unlock(&rcu_sync_lock); } #endif diff --git a/qemu/util/readline.c b/qemu/util/readline.c index cc1302ac0..bbdee790b 100644 --- a/qemu/util/readline.c +++ b/qemu/util/readline.c @@ -22,8 +22,10 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/readline.h" +#include "qemu/cutils.h" #define IS_NORM 0 #define IS_ESC 1 diff --git a/qemu/util/rfifolock.c b/qemu/util/rfifolock.c index afbf7488d..c22f5feee 100644 --- a/qemu/util/rfifolock.c +++ b/qemu/util/rfifolock.c @@ -11,7 +11,7 @@ * */ -#include +#include "qemu/osdep.h" #include "qemu/rfifolock.h" void rfifolock_init(RFifoLock *r, void (*cb)(void *), void *opaque) diff --git a/qemu/util/throttle.c b/qemu/util/throttle.c index 706c13111..71246b234 100644 --- a/qemu/util/throttle.c +++ b/qemu/util/throttle.c @@ -22,6 +22,8 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/throttle.h" #include "qemu/timer.h" #include "block/aio.h" @@ -40,6 +42,14 @@ void throttle_leak_bucket(LeakyBucket *bkt, int64_t delta_ns) /* make the bucket leak */ bkt->level = MAX(bkt->level - leak, 0); + + /* if we allow bursts for more than one second we also need to + * keep track of bkt->burst_level so the bkt->max goal per second + * is attained */ + if (bkt->burst_length > 1) { + leak = (bkt->max * (double) delta_ns) / NANOSECONDS_PER_SECOND; + bkt->burst_level = MAX(bkt->burst_level - leak, 0); + } } /* Calculate the time delta since last leak and make proportionals leaks @@ -90,13 +100,24 @@ int64_t throttle_compute_wait(LeakyBucket *bkt) return 0; } - extra = bkt->level - bkt->max; + /* If the bucket is full then we have to wait */ + extra = bkt->level - bkt->max * bkt->burst_length; + if (extra > 0) { + return throttle_do_compute_wait(bkt->avg, extra); + } - if (extra <= 0) { - return 0; + /* If the bucket is not full yet we have to make sure that we + * fulfill the goal of bkt->max units per second. */ + if (bkt->burst_length > 1) { + /* We use 1/10 of the max value to smooth the throttling. + * See throttle_fix_bucket() for more details. */ + extra = bkt->burst_level - bkt->max / 10; + if (extra > 0) { + return throttle_do_compute_wait(bkt->max, extra); + } } - return throttle_do_compute_wait(bkt->avg, extra); + return 0; } /* This function compute the time that must be waited while this IO @@ -136,10 +157,10 @@ static int64_t throttle_compute_wait_for(ThrottleState *ts, * @next_timestamp: the resulting timer * @ret: true if a timer must be set */ -bool throttle_compute_timer(ThrottleState *ts, - bool is_write, - int64_t now, - int64_t *next_timestamp) +static bool throttle_compute_timer(ThrottleState *ts, + bool is_write, + int64_t now, + int64_t *next_timestamp) { int64_t wait; @@ -170,10 +191,24 @@ void throttle_timers_attach_aio_context(ThrottleTimers *tt, tt->write_timer_cb, tt->timer_opaque); } +/* + * Initialize the ThrottleConfig structure to a valid state + * @cfg: the config to initialize + */ +void throttle_config_init(ThrottleConfig *cfg) +{ + unsigned i; + memset(cfg, 0, sizeof(*cfg)); + for (i = 0; i < BUCKETS_COUNT; i++) { + cfg->buckets[i].burst_length = 1; + } +} + /* To be called first on the ThrottleState */ void throttle_init(ThrottleState *ts) { memset(ts, 0, sizeof(ThrottleState)); + throttle_config_init(&ts->cfg); } /* To be called first on the ThrottleTimers */ @@ -247,13 +282,14 @@ bool throttle_enabled(ThrottleConfig *cfg) return false; } -/* return true if any two throttling parameters conflicts - * +/* check if a throttling configuration is valid * @cfg: the throttling configuration to inspect - * @ret: true if any conflict detected else false + * @ret: true if valid else false + * @errp: error object */ -bool throttle_conflicting(ThrottleConfig *cfg) +bool throttle_is_valid(ThrottleConfig *cfg, Error **errp) { + int i; bool bps_flag, ops_flag; bool bps_max_flag, ops_max_flag; @@ -273,31 +309,40 @@ bool throttle_conflicting(ThrottleConfig *cfg) (cfg->buckets[THROTTLE_OPS_READ].max || cfg->buckets[THROTTLE_OPS_WRITE].max); - return bps_flag || ops_flag || bps_max_flag || ops_max_flag; -} - -/* check if a throttling configuration is valid - * @cfg: the throttling configuration to inspect - * @ret: true if valid else false - */ -bool throttle_is_valid(ThrottleConfig *cfg) -{ - bool invalid = false; - int i; + if (bps_flag || ops_flag || bps_max_flag || ops_max_flag) { + error_setg(errp, "bps/iops/max total values and read/write values" + " cannot be used at the same time"); + return false; + } for (i = 0; i < BUCKETS_COUNT; i++) { - if (cfg->buckets[i].avg < 0) { - invalid = true; + if (cfg->buckets[i].avg < 0 || + cfg->buckets[i].max < 0 || + cfg->buckets[i].avg > THROTTLE_VALUE_MAX || + cfg->buckets[i].max > THROTTLE_VALUE_MAX) { + error_setg(errp, "bps/iops/max values must be within [0, %lld]", + THROTTLE_VALUE_MAX); + return false; } - } - for (i = 0; i < BUCKETS_COUNT; i++) { - if (cfg->buckets[i].max < 0) { - invalid = true; + if (!cfg->buckets[i].burst_length) { + error_setg(errp, "the burst length cannot be 0"); + return false; + } + + if (cfg->buckets[i].burst_length > 1 && !cfg->buckets[i].max) { + error_setg(errp, "burst length set without burst rate"); + return false; + } + + if (cfg->buckets[i].max && !cfg->buckets[i].avg) { + error_setg(errp, "bps_max/iops_max require corresponding" + " bps/iops values"); + return false; } } - return !invalid; + return true; } /* fix bucket parameters */ @@ -306,7 +351,7 @@ static void throttle_fix_bucket(LeakyBucket *bkt) double min; /* zero bucket level */ - bkt->level = 0; + bkt->level = bkt->burst_level = 0; /* The following is done to cope with the Linux CFQ block scheduler * which regroup reads and writes by block of 100ms in the guest. @@ -409,22 +454,36 @@ bool throttle_schedule_timer(ThrottleState *ts, */ void throttle_account(ThrottleState *ts, bool is_write, uint64_t size) { + const BucketType bucket_types_size[2][2] = { + { THROTTLE_BPS_TOTAL, THROTTLE_BPS_READ }, + { THROTTLE_BPS_TOTAL, THROTTLE_BPS_WRITE } + }; + const BucketType bucket_types_units[2][2] = { + { THROTTLE_OPS_TOTAL, THROTTLE_OPS_READ }, + { THROTTLE_OPS_TOTAL, THROTTLE_OPS_WRITE } + }; double units = 1.0; + unsigned i; /* if cfg.op_size is defined and smaller than size we compute unit count */ if (ts->cfg.op_size && size > ts->cfg.op_size) { units = (double) size / ts->cfg.op_size; } - ts->cfg.buckets[THROTTLE_BPS_TOTAL].level += size; - ts->cfg.buckets[THROTTLE_OPS_TOTAL].level += units; + for (i = 0; i < 2; i++) { + LeakyBucket *bkt; + + bkt = &ts->cfg.buckets[bucket_types_size[is_write][i]]; + bkt->level += size; + if (bkt->burst_length > 1) { + bkt->burst_level += size; + } - if (is_write) { - ts->cfg.buckets[THROTTLE_BPS_WRITE].level += size; - ts->cfg.buckets[THROTTLE_OPS_WRITE].level += units; - } else { - ts->cfg.buckets[THROTTLE_BPS_READ].level += size; - ts->cfg.buckets[THROTTLE_OPS_READ].level += units; + bkt = &ts->cfg.buckets[bucket_types_units[is_write][i]]; + bkt->level += units; + if (bkt->burst_length > 1) { + bkt->burst_level += units; + } } } diff --git a/qemu/util/timed-average.c b/qemu/util/timed-average.c new file mode 100644 index 000000000..2eef9cbb1 --- /dev/null +++ b/qemu/util/timed-average.c @@ -0,0 +1,231 @@ +/* + * QEMU timed average computation + * + * Copyright (C) Nodalink, EURL. 2014 + * Copyright (C) Igalia, S.L. 2015 + * + * Authors: + * Benoît Canet + * Alberto Garcia + * + * This program is free sofware: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Sofware Foundation, either version 2 of the License, or + * (at your option) version 3 or 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 . + */ + +#include "qemu/osdep.h" + +#include "qemu/timed-average.h" + +/* This module computes an average of a set of values within a time + * window. + * + * Algorithm: + * + * - Create two windows with a certain expiration period, and + * offsetted by period / 2. + * - Each time you want to account a new value, do it in both windows. + * - The minimum / maximum / average values are always returned from + * the oldest window. + * + * Example: + * + * t=0 |t=0.5 |t=1 |t=1.5 |t=2 + * wnd0: [0,0.5)|wnd0: [0.5,1.5) | |wnd0: [1.5,2.5) | + * wnd1: [0,1) | |wnd1: [1,2) | | + * + * Values are returned from: + * + * wnd0---------|wnd1------------|wnd0---------|wnd1-------------| + */ + +/* Update the expiration of a time window + * + * @w: the window used + * @now: the current time in nanoseconds + * @period: the expiration period in nanoseconds + */ +static void update_expiration(TimedAverageWindow *w, int64_t now, + int64_t period) +{ + /* time elapsed since the last theoretical expiration */ + int64_t elapsed = (now - w->expiration) % period; + /* time remaininging until the next expiration */ + int64_t remaining = period - elapsed; + /* compute expiration */ + w->expiration = now + remaining; +} + +/* Reset a window + * + * @w: the window to reset + */ +static void window_reset(TimedAverageWindow *w) +{ + w->min = UINT64_MAX; + w->max = 0; + w->sum = 0; + w->count = 0; +} + +/* Get the current window (that is, the one with the earliest + * expiration time). + * + * @ta: the TimedAverage structure + * @ret: a pointer to the current window + */ +static TimedAverageWindow *current_window(TimedAverage *ta) +{ + return &ta->windows[ta->current]; +} + +/* Initialize a TimedAverage structure + * + * @ta: the TimedAverage structure + * @clock_type: the type of clock to use + * @period: the time window period in nanoseconds + */ +void timed_average_init(TimedAverage *ta, QEMUClockType clock_type, + uint64_t period) +{ + int64_t now = qemu_clock_get_ns(clock_type); + + /* Returned values are from the oldest window, so they belong to + * the interval [ta->period/2,ta->period). By adjusting the + * requested period by 4/3, we guarantee that they're in the + * interval [2/3 period,4/3 period), closer to the requested + * period on average */ + ta->period = (uint64_t) period * 4 / 3; + ta->clock_type = clock_type; + ta->current = 0; + + window_reset(&ta->windows[0]); + window_reset(&ta->windows[1]); + + /* Both windows are offsetted by half a period */ + ta->windows[0].expiration = now + ta->period / 2; + ta->windows[1].expiration = now + ta->period; +} + +/* Check if the time windows have expired, updating their counters and + * expiration time if that's the case. + * + * @ta: the TimedAverage structure + * @elapsed: if non-NULL, the elapsed time (in ns) within the current + * window will be stored here + */ +static void check_expirations(TimedAverage *ta, uint64_t *elapsed) +{ + int64_t now = qemu_clock_get_ns(ta->clock_type); + int i; + + assert(ta->period != 0); + + /* Check if the windows have expired */ + for (i = 0; i < 2; i++) { + TimedAverageWindow *w = &ta->windows[i]; + if (w->expiration <= now) { + window_reset(w); + update_expiration(w, now, ta->period); + } + } + + /* Make ta->current point to the oldest window */ + if (ta->windows[0].expiration < ta->windows[1].expiration) { + ta->current = 0; + } else { + ta->current = 1; + } + + /* Calculate the elapsed time within the current window */ + if (elapsed) { + int64_t remaining = ta->windows[ta->current].expiration - now; + *elapsed = ta->period - remaining; + } +} + +/* Account a value + * + * @ta: the TimedAverage structure + * @value: the value to account + */ +void timed_average_account(TimedAverage *ta, uint64_t value) +{ + int i; + check_expirations(ta, NULL); + + /* Do the accounting in both windows at the same time */ + for (i = 0; i < 2; i++) { + TimedAverageWindow *w = &ta->windows[i]; + + w->sum += value; + w->count++; + + if (value < w->min) { + w->min = value; + } + + if (value > w->max) { + w->max = value; + } + } +} + +/* Get the minimum value + * + * @ta: the TimedAverage structure + * @ret: the minimum value + */ +uint64_t timed_average_min(TimedAverage *ta) +{ + TimedAverageWindow *w; + check_expirations(ta, NULL); + w = current_window(ta); + return w->min < UINT64_MAX ? w->min : 0; +} + +/* Get the average value + * + * @ta: the TimedAverage structure + * @ret: the average value + */ +uint64_t timed_average_avg(TimedAverage *ta) +{ + TimedAverageWindow *w; + check_expirations(ta, NULL); + w = current_window(ta); + return w->count > 0 ? w->sum / w->count : 0; +} + +/* Get the maximum value + * + * @ta: the TimedAverage structure + * @ret: the maximum value + */ +uint64_t timed_average_max(TimedAverage *ta) +{ + check_expirations(ta, NULL); + return current_window(ta)->max; +} + +/* Get the sum of all accounted values + * @ta: the TimedAverage structure + * @elapsed: if non-NULL, the elapsed time (in ns) will be stored here + * @ret: the sum of all accounted values + */ +uint64_t timed_average_sum(TimedAverage *ta, uint64_t *elapsed) +{ + TimedAverageWindow *w; + check_expirations(ta, elapsed); + w = current_window(ta); + return w->sum; +} diff --git a/qemu/util/unicode.c b/qemu/util/unicode.c index d1c865885..a812a3517 100644 --- a/qemu/util/unicode.c +++ b/qemu/util/unicode.c @@ -10,7 +10,8 @@ * later. See the COPYING file in the top-level directory. */ -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "qemu/unicode.h" /** * mod_utf8_codepoint: diff --git a/qemu/util/uri.c b/qemu/util/uri.c index 550b98458..d109d6c01 100644 --- a/qemu/util/uri.c +++ b/qemu/util/uri.c @@ -51,9 +51,8 @@ * */ +#include "qemu/osdep.h" #include -#include -#include #include "qemu/uri.h" diff --git a/qemu/vl.c b/qemu/vl.c index 0adbbd674..5fd22cb2e 100644 --- a/qemu/vl.c +++ b/qemu/vl.c @@ -21,14 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include -#include -#include -#include -#include -#include - -#include "config-host.h" +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "qemu/help_option.h" #ifdef CONFIG_SECCOMP #include "sysemu/seccomp.h" @@ -68,7 +63,7 @@ int main(int argc, char **argv) #include "hw/isa/isa.h" #include "hw/bt.h" #include "sysemu/watchdog.h" -#include "hw/i386/smbios.h" +#include "hw/smbios/smbios.h" #include "hw/xen/xen.h" #include "hw/qdev.h" #include "hw/loader.h" @@ -78,6 +73,7 @@ int main(int argc, char **argv) #include "net/slirp.h" #include "monitor/monitor.h" #include "ui/console.h" +#include "ui/input.h" #include "sysemu/sysemu.h" #include "sysemu/numa.h" #include "exec/gdbstub.h" @@ -113,7 +109,6 @@ int main(int argc, char **argv) #include "qemu/queue.h" #include "sysemu/cpus.h" #include "sysemu/arch_init.h" -#include "qemu/osdep.h" #include "ui/qemu-spice.h" #include "qapi/string-input-visitor.h" @@ -122,6 +117,8 @@ int main(int argc, char **argv) #include "qapi-event.h" #include "exec/semihost.h" #include "crypto/init.h" +#include "sysemu/replay.h" +#include "qapi/qmp/qerror.h" #define MAX_VIRTIO_CONSOLES 1 #define MAX_SCLP_CONSOLES 1 @@ -225,7 +222,6 @@ static struct { { .driver = "ide-drive", .flag = &default_cdrom }, { .driver = "scsi-cd", .flag = &default_cdrom }, { .driver = "virtio-serial-pci", .flag = &default_virtcon }, - { .driver = "virtio-serial-s390", .flag = &default_virtcon }, { .driver = "virtio-serial", .flag = &default_virtcon }, { .driver = "VGA", .flag = &default_vga }, { .driver = "isa-vga", .flag = &default_vga }, @@ -269,9 +265,13 @@ static QemuOptsList qemu_sandbox_opts = { static QemuOptsList qemu_trace_opts = { .name = "trace", - .implied_opt_name = "trace", + .implied_opt_name = "enable", .head = QTAILQ_HEAD_INITIALIZER(qemu_trace_opts.head), .desc = { + { + .name = "enable", + .type = QEMU_OPT_STRING, + }, { .name = "events", .type = QEMU_OPT_STRING, @@ -474,6 +474,12 @@ static QemuOptsList qemu_icount_opts = { }, { .name = "sleep", .type = QEMU_OPT_BOOL, + }, { + .name = "rr", + .type = QEMU_OPT_STRING, + }, { + .name = "rrfile", + .type = QEMU_OPT_STRING, }, { /* end of list */ } }, @@ -512,6 +518,10 @@ static QemuOptsList qemu_fw_cfg_opts = { .type = QEMU_OPT_STRING, .help = "Sets the name of the file from which\n" "the fw_cfg blob will be loaded", + }, { + .name = "string", + .type = QEMU_OPT_STRING, + .help = "Sets content of the blob to be inserted from a string", }, { /* end of list */ } }, @@ -534,10 +544,8 @@ const char *qemu_get_vm_name(void) static void res_free(void) { - if (boot_splash_filedata != NULL) { - g_free(boot_splash_filedata); - boot_splash_filedata = NULL; - } + g_free(boot_splash_filedata); + boot_splash_filedata = NULL; } static int default_driver_check(void *opaque, QemuOpts *opts, Error **errp) @@ -560,8 +568,8 @@ static int default_driver_check(void *opaque, QemuOpts *opts, Error **errp) static RunState current_run_state = RUN_STATE_PRELAUNCH; -/* We use RUN_STATE_MAX but any invalid value will do */ -static RunState vmstop_requested = RUN_STATE_MAX; +/* We use RUN_STATE__MAX but any invalid value will do */ +static RunState vmstop_requested = RUN_STATE__MAX; static QemuMutex vmstop_lock; typedef struct { @@ -573,6 +581,7 @@ static const RunStateTransition runstate_transitions_def[] = { /* from -> to */ { RUN_STATE_DEBUG, RUN_STATE_RUNNING }, { RUN_STATE_DEBUG, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_DEBUG, RUN_STATE_PRELAUNCH }, { RUN_STATE_INMIGRATE, RUN_STATE_INTERNAL_ERROR }, { RUN_STATE_INMIGRATE, RUN_STATE_IO_ERROR }, @@ -582,18 +591,25 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_INMIGRATE, RUN_STATE_SUSPENDED }, { RUN_STATE_INMIGRATE, RUN_STATE_WATCHDOG }, { RUN_STATE_INMIGRATE, RUN_STATE_GUEST_PANICKED }, + { RUN_STATE_INMIGRATE, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_INMIGRATE, RUN_STATE_PRELAUNCH }, + { RUN_STATE_INMIGRATE, RUN_STATE_POSTMIGRATE }, { RUN_STATE_INTERNAL_ERROR, RUN_STATE_PAUSED }, { RUN_STATE_INTERNAL_ERROR, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_INTERNAL_ERROR, RUN_STATE_PRELAUNCH }, { RUN_STATE_IO_ERROR, RUN_STATE_RUNNING }, { RUN_STATE_IO_ERROR, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_IO_ERROR, RUN_STATE_PRELAUNCH }, { RUN_STATE_PAUSED, RUN_STATE_RUNNING }, { RUN_STATE_PAUSED, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_PAUSED, RUN_STATE_PRELAUNCH }, { RUN_STATE_POSTMIGRATE, RUN_STATE_RUNNING }, { RUN_STATE_POSTMIGRATE, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_POSTMIGRATE, RUN_STATE_PRELAUNCH }, { RUN_STATE_PRELAUNCH, RUN_STATE_RUNNING }, { RUN_STATE_PRELAUNCH, RUN_STATE_FINISH_MIGRATE }, @@ -601,8 +617,10 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_FINISH_MIGRATE, RUN_STATE_RUNNING }, { RUN_STATE_FINISH_MIGRATE, RUN_STATE_POSTMIGRATE }, + { RUN_STATE_FINISH_MIGRATE, RUN_STATE_PRELAUNCH }, { RUN_STATE_RESTORE_VM, RUN_STATE_RUNNING }, + { RUN_STATE_RESTORE_VM, RUN_STATE_PRELAUNCH }, { RUN_STATE_RUNNING, RUN_STATE_DEBUG }, { RUN_STATE_RUNNING, RUN_STATE_INTERNAL_ERROR }, @@ -619,22 +637,26 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_SHUTDOWN, RUN_STATE_PAUSED }, { RUN_STATE_SHUTDOWN, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_SHUTDOWN, RUN_STATE_PRELAUNCH }, { RUN_STATE_DEBUG, RUN_STATE_SUSPENDED }, { RUN_STATE_RUNNING, RUN_STATE_SUSPENDED }, { RUN_STATE_SUSPENDED, RUN_STATE_RUNNING }, { RUN_STATE_SUSPENDED, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_SUSPENDED, RUN_STATE_PRELAUNCH }, { RUN_STATE_WATCHDOG, RUN_STATE_RUNNING }, { RUN_STATE_WATCHDOG, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_WATCHDOG, RUN_STATE_PRELAUNCH }, { RUN_STATE_GUEST_PANICKED, RUN_STATE_RUNNING }, { RUN_STATE_GUEST_PANICKED, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_GUEST_PANICKED, RUN_STATE_PRELAUNCH }, - { RUN_STATE_MAX, RUN_STATE_MAX }, + { RUN_STATE__MAX, RUN_STATE__MAX }, }; -static bool runstate_valid_transitions[RUN_STATE_MAX][RUN_STATE_MAX]; +static bool runstate_valid_transitions[RUN_STATE__MAX][RUN_STATE__MAX]; bool runstate_check(RunState state) { @@ -658,7 +680,7 @@ static void runstate_init(void) const RunStateTransition *p; memset(&runstate_valid_transitions, 0, sizeof(runstate_valid_transitions)); - for (p = &runstate_transitions_def[0]; p->from != RUN_STATE_MAX; p++) { + for (p = &runstate_transitions_def[0]; p->from != RUN_STATE__MAX; p++) { runstate_valid_transitions[p->from][p->to] = true; } @@ -668,12 +690,12 @@ static void runstate_init(void) /* This function will abort() on invalid state transitions */ void runstate_set(RunState new_state) { - assert(new_state < RUN_STATE_MAX); + assert(new_state < RUN_STATE__MAX); if (!runstate_valid_transitions[current_run_state][new_state]) { - fprintf(stderr, "ERROR: invalid runstate transition: '%s' -> '%s'\n", - RunState_lookup[current_run_state], - RunState_lookup[new_state]); + error_report("invalid runstate transition: '%s' -> '%s'", + RunState_lookup[current_run_state], + RunState_lookup[new_state]); abort(); } trace_runstate_set(new_state); @@ -706,9 +728,9 @@ static bool qemu_vmstop_requested(RunState *r) { qemu_mutex_lock(&vmstop_lock); *r = vmstop_requested; - vmstop_requested = RUN_STATE_MAX; + vmstop_requested = RUN_STATE__MAX; qemu_mutex_unlock(&vmstop_lock); - return *r < RUN_STATE_MAX; + return *r < RUN_STATE__MAX; } void qemu_system_vmstop_request_prepare(void) @@ -728,7 +750,7 @@ void vm_start(void) RunState requested; qemu_vmstop_requested(&requested); - if (runstate_is_running() && requested == RUN_STATE_MAX) { + if (runstate_is_running() && requested == RUN_STATE__MAX) { return; } @@ -825,8 +847,9 @@ static void configure_rtc_date_offset(const char *startdate, int legacy) rtc_start_date = mktimegm(&tm); if (rtc_start_date == -1) { date_fail: - fprintf(stderr, "Invalid date format. Valid formats are:\n" - "'2006-06-17T16:01:21' or '2006-06-17'\n"); + error_report("invalid date format"); + error_printf("valid formats: " + "'2006-06-17T16:01:21' or '2006-06-17'\n"); exit(1); } rtc_date_offset = qemu_time() - rtc_start_date; @@ -842,7 +865,11 @@ static void configure_rtc(QemuOpts *opts) if (!strcmp(value, "utc")) { rtc_utc = 1; } else if (!strcmp(value, "localtime")) { + Error *blocker = NULL; rtc_utc = 0; + error_setg(&blocker, QERR_REPLAY_NOT_SUPPORTED, + "-rtc base=localtime"); + replay_add_blocker(blocker); } else { configure_rtc_date_offset(value, 0); } @@ -856,7 +883,7 @@ static void configure_rtc(QemuOpts *opts) } else if (!strcmp(value, "vm")) { rtc_clock = QEMU_CLOCK_VIRTUAL; } else { - fprintf(stderr, "qemu: invalid option value '%s'\n", value); + error_report("invalid option value '%s'", value); exit(1); } } @@ -876,7 +903,7 @@ static void configure_rtc(QemuOpts *opts) } else if (!strcmp(value, "none")) { /* discard is default */ } else { - fprintf(stderr, "qemu: invalid option value '%s'\n", value); + error_report("invalid option value '%s'", value); exit(1); } } @@ -902,7 +929,7 @@ static int bt_hci_parse(const char *str) bdaddr_t bdaddr; if (nb_hcis >= MAX_NICS) { - fprintf(stderr, "qemu: Too many bluetooth HCIs (max %i).\n", MAX_NICS); + error_report("too many bluetooth HCIs (max %i)", MAX_NICS); return -1; } @@ -928,8 +955,8 @@ static void bt_vhci_add(int vlan_id) struct bt_scatternet_s *vlan = qemu_find_bt_vlan(vlan_id); if (!vlan->slave) - fprintf(stderr, "qemu: warning: adding a VHCI to " - "an empty scatternet %i\n", vlan_id); + error_report("warning: adding a VHCI to an empty scatternet %i", + vlan_id); bt_vhci_init(bt_new_hci(vlan)); } @@ -947,7 +974,7 @@ static struct bt_device_s *bt_device_add(const char *opt) if (endp) { vlan_id = strtol(endp + 6, &endp, 0); if (*endp) { - fprintf(stderr, "qemu: unrecognised bluetooth vlan Id\n"); + error_report("unrecognised bluetooth vlan Id"); return 0; } } @@ -955,13 +982,13 @@ static struct bt_device_s *bt_device_add(const char *opt) vlan = qemu_find_bt_vlan(vlan_id); if (!vlan->slave) - fprintf(stderr, "qemu: warning: adding a slave device to " - "an empty scatternet %i\n", vlan_id); + error_report("warning: adding a slave device to an empty scatternet %i", + vlan_id); if (!strcmp(devname, "keyboard")) return bt_keyboard_init(vlan); - fprintf(stderr, "qemu: unsupported bluetooth device `%s'\n", devname); + error_report("unsupported bluetooth device '%s'", devname); return 0; } @@ -984,11 +1011,11 @@ static int bt_parse(const char *opt) if (strstart(endp, ",vlan=", &p)) { vlan = strtol(p, (char **) &endp, 0); if (*endp) { - fprintf(stderr, "qemu: bad scatternet '%s'\n", p); + error_report("bad scatternet '%s'", p); return 1; } } else { - fprintf(stderr, "qemu: bad parameter '%s'\n", endp + 1); + error_report("bad parameter '%s'", endp + 1); return 1; } } else @@ -1000,7 +1027,7 @@ static int bt_parse(const char *opt) } else if (strstart(opt, "device:", &endp)) return !bt_device_add(endp); - fprintf(stderr, "qemu: bad bluetooth parameter '%s'\n", opt); + error_report("bad bluetooth parameter '%s'", opt); return 1; } @@ -1015,8 +1042,7 @@ static int parse_sandbox(void *opaque, QemuOpts *opts, Error **errp) return -1; } #else - error_report("sandboxing request but seccomp is not compiled " - "into this build"); + error_report("seccomp support is disabled"); return -1; #endif } @@ -1097,7 +1123,7 @@ static int parse_add_fd(void *opaque, QemuOpts *opts, Error **errp) } #endif if (dupfd == -1) { - error_report("Error duplicating fd: %s", strerror(errno)); + error_report("error duplicating fd: %s", strerror(errno)); return -1; } @@ -1217,14 +1243,21 @@ static void smp_parse(QemuOpts *opts) } else if (threads == 0) { threads = cpus / (cores * sockets); } else if (sockets * cores * threads < cpus) { - fprintf(stderr, "cpu topology: error: " - "sockets (%u) * cores (%u) * threads (%u) < " - "smp_cpus (%u)\n", - sockets, cores, threads, cpus); + error_report("cpu topology: " + "sockets (%u) * cores (%u) * threads (%u) < " + "smp_cpus (%u)", + sockets, cores, threads, cpus); exit(1); } - max_cpus = qemu_opt_get_number(opts, "maxcpus", 0); + max_cpus = qemu_opt_get_number(opts, "maxcpus", cpus); + if (sockets * cores * threads > max_cpus) { + error_report("cpu topology: " + "sockets (%u) * cores (%u) * threads (%u) > " + "maxcpus (%u)", + sockets, cores, threads, max_cpus); + exit(1); + } smp_cpus = cpus; smp_cores = cores > 0 ? cores : 1; @@ -1237,21 +1270,26 @@ static void smp_parse(QemuOpts *opts) } if (max_cpus > MAX_CPUMASK_BITS) { - fprintf(stderr, "Unsupported number of maxcpus\n"); + error_report("unsupported number of maxcpus"); exit(1); } if (max_cpus < smp_cpus) { - fprintf(stderr, "maxcpus must be equal to or greater than smp\n"); + error_report("maxcpus must be equal to or greater than smp"); exit(1); } + if (smp_cpus > 1 || smp_cores > 1 || smp_threads > 1) { + Error *blocker = NULL; + error_setg(&blocker, QERR_REPLAY_NOT_SUPPORTED, "smp"); + replay_add_blocker(blocker); + } } static void realtime_init(void) { if (enable_mlock) { if (os_mlock() < 0) { - fprintf(stderr, "qemu: locking memory failed\n"); + error_report("locking memory failed"); exit(1); } } @@ -1338,6 +1376,13 @@ static inline void semihosting_arg_fallback(const char *file, const char *cmd) } } +/* Now we still need this for compatibility with XEN. */ +bool has_igd_gfx_passthru; +static void igd_gfx_passthru(void) +{ + has_igd_gfx_passthru = current_machine->igd_gfx_passthru; +} + /***********************************************************/ /* USB devices */ @@ -1398,7 +1443,7 @@ static int usb_parse(const char *cmdline) int r; r = usb_device_add(cmdline); if (r < 0) { - fprintf(stderr, "qemu: could not add USB device '%s'\n", cmdline); + error_report("could not add USB device '%s'", cmdline); } return r; } @@ -1424,43 +1469,6 @@ void hmp_usb_del(Monitor *mon, const QDict *qdict) MachineState *current_machine; -/* - * Transitional class registration/init used for converting from - * legacy QEMUMachine to MachineClass. - */ -static void qemu_machine_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - QEMUMachine *qm = data; - mc->name = qm->name; - mc->desc = qm->desc; - mc->init = qm->init; - mc->kvm_type = qm->kvm_type; - mc->block_default_type = qm->block_default_type; - mc->max_cpus = qm->max_cpus; - mc->no_sdcard = qm->no_sdcard; - mc->has_dynamic_sysbus = qm->has_dynamic_sysbus; - mc->is_default = qm->is_default; - mc->default_machine_opts = qm->default_machine_opts; - mc->default_boot_order = qm->default_boot_order; -} - -int qemu_register_machine(QEMUMachine *m) -{ - char *name = g_strconcat(m->name, TYPE_MACHINE_SUFFIX, NULL); - TypeInfo ti = { - .name = name, - .parent = TYPE_MACHINE, - .class_init = qemu_machine_class_init, - .class_data = (void *)m, - }; - - type_register(&ti); - g_free(name); - - return 0; -} - static MachineClass *find_machine(const char *name) { GSList *el, *machines = object_class_get_list(TYPE_MACHINE, false); @@ -1539,12 +1547,14 @@ MachineInfoList *qmp_query_machines(Error **errp) static int machine_help_func(QemuOpts *opts, MachineState *machine) { ObjectProperty *prop; + ObjectPropertyIterator iter; if (!qemu_opt_has_help_opt(opts)) { return 0; } - QTAILQ_FOREACH(prop, &OBJECT(machine)->properties, node) { + object_property_iter_init(&iter, OBJECT(machine)); + while ((prop = object_property_iter_next(&iter))) { if (!prop->set) { continue; } @@ -1645,14 +1655,14 @@ static int qemu_shutdown_requested(void) static void qemu_kill_report(void) { if (!qtest_driver() && shutdown_signal != -1) { - fprintf(stderr, "qemu: terminating on signal %d", shutdown_signal); if (shutdown_pid == 0) { /* This happens for eg ^C at the terminal, so it's worth * avoiding printing an odd message in that case. */ - fputc('\n', stderr); + error_report("terminating on signal %d", shutdown_signal); } else { - fprintf(stderr, " from pid " FMT_pid "\n", shutdown_pid); + error_report("terminating on signal %d from pid " FMT_pid, + shutdown_signal, shutdown_pid); } shutdown_signal = -1; } @@ -1661,15 +1671,21 @@ static void qemu_kill_report(void) static int qemu_reset_requested(void) { int r = reset_requested; - reset_requested = 0; - return r; + if (r && replay_checkpoint(CHECKPOINT_RESET_REQUESTED)) { + reset_requested = 0; + return r; + } + return false; } static int qemu_suspend_requested(void) { int r = suspend_requested; - suspend_requested = 0; - return r; + if (r && replay_checkpoint(CHECKPOINT_SUSPEND_REQUESTED)) { + suspend_requested = 0; + return r; + } + return false; } static WakeupReason qemu_wakeup_requested(void) @@ -1729,6 +1745,8 @@ void qemu_system_reset(bool report) mc = current_machine ? MACHINE_GET_CLASS(current_machine) : NULL; + cpu_synchronize_all_states(); + if (mc && mc->reset) { mc->reset(); } else { @@ -1740,6 +1758,15 @@ void qemu_system_reset(bool report) cpu_synchronize_all_post_reset(); } +void qemu_system_guest_panicked(void) +{ + if (current_cpu) { + current_cpu->crash_occurred = true; + } + qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, &error_abort); + vm_stop(RUN_STATE_GUEST_PANICKED); +} + void qemu_system_reset_request(void) { if (no_reboot) { @@ -1808,12 +1835,18 @@ void qemu_system_killed(int signal, pid_t pid) shutdown_signal = signal; shutdown_pid = pid; no_shutdown = 0; - qemu_system_shutdown_request(); + + /* Cannot call qemu_system_shutdown_request directly because + * we are in a signal handler. + */ + shutdown_requested = 1; + qemu_notify_event(); } void qemu_system_shutdown_request(void) { trace_qemu_system_shutdown_request(); + replay_shutdown_request(); shutdown_requested = 1; qemu_notify_event(); } @@ -1862,16 +1895,15 @@ static bool main_loop_should_exit(void) } if (qemu_reset_requested()) { pause_all_vcpus(); - cpu_synchronize_all_states(); qemu_system_reset(VMRESET_REPORT); resume_all_vcpus(); - if (runstate_needs_reset()) { - runstate_set(RUN_STATE_PAUSED); + if (!runstate_check(RUN_STATE_RUNNING) && + !runstate_check(RUN_STATE_INMIGRATE)) { + runstate_set(RUN_STATE_PRELAUNCH); } } if (qemu_wakeup_requested()) { pause_all_vcpus(); - cpu_synchronize_all_states(); qemu_system_reset(VMRESET_SILENT); notifier_list_notify(&wakeup_notifiers, &wakeup_reason); wakeup_reason = QEMU_WAKEUP_REASON_NONE; @@ -1992,28 +2024,28 @@ static void select_vgahw (const char *p) if (vga_available()) { vga_interface_type = VGA_STD; } else { - fprintf(stderr, "Error: standard VGA not available\n"); + error_report("standard VGA not available"); exit(0); } } else if (strstart(p, "cirrus", &opts)) { if (cirrus_vga_available()) { vga_interface_type = VGA_CIRRUS; } else { - fprintf(stderr, "Error: Cirrus VGA not available\n"); + error_report("Cirrus VGA not available"); exit(0); } } else if (strstart(p, "vmware", &opts)) { if (vmware_vga_available()) { vga_interface_type = VGA_VMWARE; } else { - fprintf(stderr, "Error: VMWare SVGA not available\n"); + error_report("VMWare SVGA not available"); exit(0); } } else if (strstart(p, "virtio", &opts)) { if (virtio_vga_available()) { vga_interface_type = VGA_VIRTIO; } else { - fprintf(stderr, "Error: Virtio VGA not available\n"); + error_report("Virtio VGA not available"); exit(0); } } else if (strstart(p, "xenfb", &opts)) { @@ -2022,26 +2054,26 @@ static void select_vgahw (const char *p) if (qxl_vga_available()) { vga_interface_type = VGA_QXL; } else { - fprintf(stderr, "Error: QXL VGA not available\n"); + error_report("QXL VGA not available"); exit(0); } } else if (strstart(p, "tcx", &opts)) { if (tcx_vga_available()) { vga_interface_type = VGA_TCX; } else { - fprintf(stderr, "Error: TCX framebuffer not available\n"); + error_report("TCX framebuffer not available"); exit(0); } } else if (strstart(p, "cg3", &opts)) { if (cg3_vga_available()) { vga_interface_type = VGA_CG3; } else { - fprintf(stderr, "Error: CG3 framebuffer not available\n"); + error_report("CG3 framebuffer not available"); exit(0); } } else if (!strstart(p, "none", &opts)) { invalid_vga: - fprintf(stderr, "Unknown vga type: %s\n", p); + error_report("unknown vga type: %s", p); exit(1); } while (*opts) { @@ -2117,7 +2149,7 @@ static DisplayType select_display(const char *p) } } else { invalid_sdl_args: - error_report("Invalid SDL option string"); + error_report("invalid SDL option string"); exit(1); } opts = nextopt; @@ -2146,7 +2178,7 @@ static DisplayType select_display(const char *p) #ifdef CONFIG_CURSES display = DT_CURSES; #else - error_report("Curses support is disabled"); + error_report("curses support is disabled"); exit(1); #endif } else if (strstart(p, "gtk", &opts)) { @@ -2175,7 +2207,7 @@ static DisplayType select_display(const char *p) } } else { invalid_gtk_args: - error_report("Invalid GTK option string"); + error_report("invalid GTK option string"); exit(1); } opts = nextopt; @@ -2187,7 +2219,7 @@ static DisplayType select_display(const char *p) } else if (strstart(p, "none", &opts)) { display = DT_NONE; } else { - error_report("Unknown display type"); + error_report("unknown display type"); exit(1); } @@ -2255,20 +2287,33 @@ char *qemu_find_file(int type, const char *name) return NULL; } +static inline bool nonempty_str(const char *str) +{ + return str && *str; +} + static int parse_fw_cfg(void *opaque, QemuOpts *opts, Error **errp) { gchar *buf; size_t size; - const char *name, *file; + const char *name, *file, *str; + FWCfgState *fw_cfg = (FWCfgState *) opaque; - if (opaque == NULL) { + if (fw_cfg == NULL) { error_report("fw_cfg device not available"); return -1; } name = qemu_opt_get(opts, "name"); file = qemu_opt_get(opts, "file"); - if (name == NULL || *name == '\0' || file == NULL || *file == '\0') { - error_report("invalid argument value"); + str = qemu_opt_get(opts, "string"); + + /* we need name and either a file or the content string */ + if (!(nonempty_str(name) && (nonempty_str(file) || nonempty_str(str)))) { + error_report("invalid argument(s)"); + return -1; + } + if (nonempty_str(file) && nonempty_str(str)) { + error_report("file and string are mutually exclusive"); return -1; } if (strlen(name) > FW_CFG_MAX_FILE_PATH - 1) { @@ -2276,14 +2321,22 @@ static int parse_fw_cfg(void *opaque, QemuOpts *opts, Error **errp) return -1; } if (strncmp(name, "opt/", 4) != 0) { - error_report("WARNING: externally provided fw_cfg item names " - "should be prefixed with \"opt/\"!"); + error_report("warning: externally provided fw_cfg item names " + "should be prefixed with \"opt/\""); } - if (!g_file_get_contents(file, &buf, &size, NULL)) { - error_report("can't load %s", file); - return -1; + if (nonempty_str(str)) { + size = strlen(str); /* NUL terminator NOT included in fw_cfg blob */ + buf = g_memdup(str, size); + } else { + if (!g_file_get_contents(file, &buf, &size, NULL)) { + error_report("can't load %s", file); + return -1; + } } - fw_cfg_add_file((FWCfgState *)opaque, name, buf, size); + /* For legacy, keep user files in a specific global order. */ + fw_cfg_set_order_override(fw_cfg, FW_CFG_ORDER_OVERRIDE_USER); + fw_cfg_add_file(fw_cfg, name, buf, size); + fw_cfg_reset_order_override(fw_cfg); return 0; } @@ -2344,7 +2397,7 @@ static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp) } else if (strcmp(mode, "control") == 0) { flags = MONITOR_USE_CONTROL; } else { - fprintf(stderr, "unknown monitor mode \"%s\"\n", mode); + error_report("unknown monitor mode \"%s\"", mode); exit(1); } @@ -2357,7 +2410,7 @@ static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp) chardev = qemu_opt_get(opts, "chardev"); chr = qemu_chr_find(chardev); if (chr == NULL) { - fprintf(stderr, "chardev \"%s\" not found\n", chardev); + error_report("chardev \"%s\" not found", chardev); exit(1); } @@ -2385,7 +2438,7 @@ static void monitor_parse(const char *optarg, const char *mode, bool pretty) } opts = qemu_chr_parse_compat(label, optarg); if (!opts) { - fprintf(stderr, "parse error: %s\n", optarg); + error_report("parse error: %s", optarg); exit(1); } } @@ -2459,14 +2512,14 @@ static int serial_parse(const char *devname) if (strcmp(devname, "none") == 0) return 0; if (index == MAX_SERIAL_PORTS) { - fprintf(stderr, "qemu: too many serial ports\n"); + error_report("too many serial ports"); exit(1); } snprintf(label, sizeof(label), "serial%d", index); serial_hds[index] = qemu_chr_new(label, devname, NULL); if (!serial_hds[index]) { - fprintf(stderr, "qemu: could not connect serial device" - " to character backend '%s'\n", devname); + error_report("could not connect serial device" + " to character backend '%s'", devname); return -1; } index++; @@ -2481,14 +2534,14 @@ static int parallel_parse(const char *devname) if (strcmp(devname, "none") == 0) return 0; if (index == MAX_PARALLEL_PORTS) { - fprintf(stderr, "qemu: too many parallel ports\n"); + error_report("too many parallel ports"); exit(1); } snprintf(label, sizeof(label), "parallel%d", index); parallel_hds[index] = qemu_chr_new(label, devname, NULL); if (!parallel_hds[index]) { - fprintf(stderr, "qemu: could not connect parallel device" - " to character backend '%s'\n", devname); + error_report("could not connect parallel device" + " to character backend '%s'", devname); return -1; } index++; @@ -2505,16 +2558,12 @@ static int virtcon_parse(const char *devname) if (strcmp(devname, "none") == 0) return 0; if (index == MAX_VIRTIO_CONSOLES) { - fprintf(stderr, "qemu: too many virtio consoles\n"); + error_report("too many virtio consoles"); exit(1); } bus_opts = qemu_opts_create(device, NULL, 0, &error_abort); - if (arch_type == QEMU_ARCH_S390X) { - qemu_opt_set(bus_opts, "driver", "virtio-serial-s390", &error_abort); - } else { - qemu_opt_set(bus_opts, "driver", "virtio-serial-pci", &error_abort); - } + qemu_opt_set(bus_opts, "driver", "virtio-serial", &error_abort); dev_opts = qemu_opts_create(device, NULL, 0, &error_abort); qemu_opt_set(dev_opts, "driver", "virtconsole", &error_abort); @@ -2522,8 +2571,8 @@ static int virtcon_parse(const char *devname) snprintf(label, sizeof(label), "virtcon%d", index); virtcon_hds[index] = qemu_chr_new(label, devname, NULL); if (!virtcon_hds[index]) { - fprintf(stderr, "qemu: could not connect virtio console" - " to character backend '%s'\n", devname); + error_report("could not connect virtio console" + " to character backend '%s'", devname); return -1; } qemu_opt_set(dev_opts, "chardev", label, &error_abort); @@ -2543,7 +2592,7 @@ static int sclp_parse(const char *devname) return 0; } if (index == MAX_SCLP_CONSOLES) { - fprintf(stderr, "qemu: too many sclp consoles\n"); + error_report("too many sclp consoles"); exit(1); } @@ -2555,8 +2604,8 @@ static int sclp_parse(const char *devname) snprintf(label, sizeof(label), "sclpcon%d", index); sclp_hds[index] = qemu_chr_new(label, devname, NULL); if (!sclp_hds[index]) { - fprintf(stderr, "qemu: could not connect sclp console" - " to character backend '%s'\n", devname); + error_report("could not connect sclp console" + " to character backend '%s'", devname); return -1; } qemu_opt_set(dev_opts, "chardev", label, &error_abort); @@ -2574,7 +2623,7 @@ static int debugcon_parse(const char *devname) } opts = qemu_opts_create(qemu_find_opts("device"), "debugcon", 1, NULL); if (!opts) { - fprintf(stderr, "qemu: already have a debugcon device\n"); + error_report("already have a debugcon device"); exit(1); } qemu_opt_set(opts, "driver", "isa-debugcon", &error_abort); @@ -2629,8 +2678,8 @@ static gint machine_class_cmp(gconstpointer a, gconstpointer b) return mc; } if (name && !is_help_option(name)) { - error_report("Unsupported machine type"); - error_printf("Use -machine help to list supported machines!\n"); + error_report("unsupported machine type"); + error_printf("Use -machine help to list supported machines\n"); } else { printf("Supported machines are:\n"); machines = g_slist_sort(machines, machine_class_cmp); @@ -2719,24 +2768,31 @@ static const QEMUOption *lookup_opt(int argc, char **argv, return popt; } -static gpointer malloc_and_trace(gsize n_bytes) +static MachineClass *select_machine(void) { - void *ptr = malloc(n_bytes); - trace_g_malloc(n_bytes, ptr); - return ptr; -} + MachineClass *machine_class = find_default_machine(); + const char *optarg; + QemuOpts *opts; + Location loc; -static gpointer realloc_and_trace(gpointer mem, gsize n_bytes) -{ - void *ptr = realloc(mem, n_bytes); - trace_g_realloc(mem, n_bytes, ptr); - return ptr; -} + loc_push_none(&loc); -static void free_and_trace(gpointer mem) -{ - trace_g_free(mem); - free(mem); + opts = qemu_get_machine_opts(); + qemu_opts_loc_restore(opts); + + optarg = qemu_opt_get(opts, "type"); + if (optarg) { + machine_class = machine_parse(optarg); + } + + if (!machine_class) { + error_report("No machine specified, and there is no default"); + error_printf("Use -machine help to list supported machines\n"); + exit(1); + } + + loc_pop(&loc); + return machine_class; } static int machine_set_property(void *opaque, @@ -2783,13 +2839,25 @@ static bool object_create_initial(const char *type) if (g_str_equal(type, "rng-egd")) { return false; } + + /* + * return false for concrete netfilters since + * they depend on netdevs already existing + */ + if (g_str_equal(type, "filter-buffer") || + g_str_equal(type, "filter-dump") || + g_str_equal(type, "filter-mirror") || + g_str_equal(type, "filter-redirector")) { + return false; + } + return true; } /* * The remainder of object creation happens after the - * creation of chardev, fsdev and device data types. + * creation of chardev, fsdev, net clients and device data types. */ static bool object_create_delayed(const char *type) { @@ -2797,62 +2865,6 @@ static bool object_create_delayed(const char *type) } -static int object_create(void *opaque, QemuOpts *opts, Error **errp) -{ - Error *err = NULL; - char *type = NULL; - char *id = NULL; - void *dummy = NULL; - OptsVisitor *ov; - QDict *pdict; - bool (*type_predicate)(const char *) = opaque; - - ov = opts_visitor_new(opts); - pdict = qemu_opts_to_qdict(opts, NULL); - - visit_start_struct(opts_get_visitor(ov), &dummy, NULL, NULL, 0, &err); - if (err) { - goto out; - } - - qdict_del(pdict, "qom-type"); - visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err); - if (err) { - goto out; - } - if (!type_predicate(type)) { - goto out; - } - - qdict_del(pdict, "id"); - visit_type_str(opts_get_visitor(ov), &id, "id", &err); - if (err) { - goto out; - } - - object_add(type, id, pdict, opts_get_visitor(ov), &err); - if (err) { - goto out; - } - visit_end_struct(opts_get_visitor(ov), &err); - if (err) { - qmp_object_del(id, NULL); - } - -out: - opts_visitor_cleanup(ov); - - QDECREF(pdict); - g_free(id); - g_free(type); - g_free(dummy); - if (err) { - error_report_err(err); - return -1; - } - return 0; -} - static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, MachineClass *mc) { @@ -2861,6 +2873,10 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, const char *maxmem_str, *slots_str; const ram_addr_t default_ram_size = mc->default_ram_size; QemuOpts *opts = qemu_find_opts_singleton("memory"); + Location loc; + + loc_push_none(&loc); + qemu_opts_loc_restore(opts); sz = 0; mem_str = qemu_opt_get(opts, "size"); @@ -2935,6 +2951,8 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, "'%s' option", slots_str ? "maxmem" : "slots"); exit(EXIT_FAILURE); } + + loc_pop(&loc); } int main(int argc, char **argv, char **envp) @@ -2966,13 +2984,7 @@ int main(int argc, char **argv, char **envp) bool userconfig = true; const char *log_mask = NULL; const char *log_file = NULL; - GMemVTable mem_trace = { - .malloc = malloc_and_trace, - .realloc = realloc_and_trace, - .free = free_and_trace, - }; - const char *trace_events = NULL; - const char *trace_file = NULL; + char *trace_file = NULL; ram_addr_t maxram_size; uint64_t ram_slots = 0; FILE *vmstate_dump_file = NULL; @@ -2986,8 +2998,6 @@ int main(int argc, char **argv, char **envp) error_set_progname(argv[0]); qemu_init_exec_dir(argv[0]); - g_mem_set_vtable(&mem_trace); - module_call_init(MODULE_INIT_QOM); qemu_add_opts(&qemu_drive_opts); @@ -3018,12 +3028,12 @@ int main(int argc, char **argv, char **envp) qemu_add_opts(&qemu_icount_opts); qemu_add_opts(&qemu_semihosting_config_opts); qemu_add_opts(&qemu_fw_cfg_opts); + module_call_init(MODULE_INIT_OPTS); runstate_init(); if (qcrypto_init(&err) < 0) { - fprintf(stderr, "Cannot initialize crypto: %s\n", - error_get_pretty(err)); + error_reportf_err(err, "cannot initialize crypto: "); exit(1); } rtc_clock = QEMU_CLOCK_HOST; @@ -3031,8 +3041,6 @@ int main(int argc, char **argv, char **envp) QLIST_INIT (&vm_change_state_head); os_setup_early_signal_handling(); - module_call_init(MODULE_INIT_MACHINE); - machine_class = find_default_machine(); cpu_model = NULL; snapshot = 0; cyls = heads = secs = 0; @@ -3181,7 +3189,7 @@ int main(int argc, char **argv, char **envp) } } else if (*p != '\0') { chs_fail: - fprintf(stderr, "qemu: invalid physical CHS format\n"); + error_report("invalid physical CHS format"); exit(1); } if (hda_opts != NULL) { @@ -3224,7 +3232,7 @@ int main(int argc, char **argv, char **envp) #ifdef CONFIG_CURSES display_type = DT_CURSES; #else - fprintf(stderr, "Curses support is disabled\n"); + error_report("curses support is disabled"); exit(1); #endif break; @@ -3235,8 +3243,7 @@ int main(int argc, char **argv, char **envp) graphic_rotate = strtol(optarg, (char **) &optarg, 10); if (graphic_rotate != 0 && graphic_rotate != 90 && graphic_rotate != 180 && graphic_rotate != 270) { - fprintf(stderr, - "qemu: only 90, 180, 270 deg rotation is available\n"); + error_report("only 90, 180, 270 deg rotation is available"); exit(1); } break; @@ -3295,12 +3302,18 @@ int main(int argc, char **argv, char **envp) #endif #ifdef CONFIG_SLIRP case QEMU_OPTION_tftp: + error_report("The -tftp option is deprecated. " + "Please use '-netdev user,tftp=...' instead."); legacy_tftp_prefix = optarg; break; case QEMU_OPTION_bootp: + error_report("The -bootp option is deprecated. " + "Please use '-netdev user,bootfile=...' instead."); legacy_bootp_filename = optarg; break; case QEMU_OPTION_redir: + error_report("The -redir option is deprecated. " + "Please use '-netdev user,hostfwd=...' instead."); if (net_slirp_redir(optarg) < 0) exit(1); break; @@ -3348,6 +3361,9 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_D: log_file = optarg; break; + case QEMU_OPTION_DFILTER: + qemu_set_dfilter_ranges(optarg); + break; case QEMU_OPTION_s: add_device_config(DEV_GDB, "tcp::" DEFAULT_GDBSTUB_PORT); break; @@ -3387,7 +3403,7 @@ int main(int argc, char **argv, char **envp) w = strtol(p, (char **)&p, 10); if (w <= 0) { graphic_error: - fprintf(stderr, "qemu: invalid resolution or depth\n"); + error_report("invalid resolution or depth"); exit(1); } if (*p != 'x') @@ -3453,7 +3469,7 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_fsdev: olist = qemu_find_opts("fsdev"); if (!olist) { - fprintf(stderr, "fsdev is not supported by this qemu build.\n"); + error_report("fsdev support is disabled"); exit(1); } opts = qemu_opts_parse_noisily(olist, optarg, true); @@ -3468,7 +3484,7 @@ int main(int argc, char **argv, char **envp) olist = qemu_find_opts("virtfs"); if (!olist) { - fprintf(stderr, "virtfs is not supported by this qemu build.\n"); + error_report("virtfs support is disabled"); exit(1); } opts = qemu_opts_parse_noisily(olist, optarg, true); @@ -3478,15 +3494,15 @@ int main(int argc, char **argv, char **envp) if (qemu_opt_get(opts, "fsdriver") == NULL || qemu_opt_get(opts, "mount_tag") == NULL) { - fprintf(stderr, "Usage: -virtfs fsdriver,mount_tag=tag.\n"); + error_report("Usage: -virtfs fsdriver,mount_tag=tag"); exit(1); } fsdev = qemu_opts_create(qemu_find_opts("fsdev"), qemu_opt_get(opts, "mount_tag"), 1, NULL); if (!fsdev) { - fprintf(stderr, "duplicate fsdev id: %s\n", - qemu_opt_get(opts, "mount_tag")); + error_report("duplicate fsdev id: %s", + qemu_opt_get(opts, "mount_tag")); exit(1); } @@ -3495,8 +3511,8 @@ int main(int argc, char **argv, char **envp) #ifdef CONFIG_SYNC_FILE_RANGE qemu_opt_set(fsdev, "writeout", writeout, &error_abort); #else - fprintf(stderr, "writeout=immediate not supported on " - "this platform\n"); + error_report("writeout=immediate not supported " + "on this platform"); exit(1); #endif } @@ -3535,7 +3551,7 @@ int main(int argc, char **argv, char **envp) fsdev = qemu_opts_create(qemu_find_opts("fsdev"), "v_synth", 1, NULL); if (!fsdev) { - fprintf(stderr, "duplicate option: %s\n", "virtfs_synth"); + error_report("duplicate option: %s", "virtfs_synth"); exit(1); } qemu_opt_set(fsdev, "fsdriver", "synth", &error_abort); @@ -3556,15 +3572,14 @@ int main(int argc, char **argv, char **envp) break; case QEMU_OPTION_watchdog: if (watchdog) { - fprintf(stderr, - "qemu: only one watchdog option may be given\n"); + error_report("only one watchdog option may be given"); return 1; } watchdog = optarg; break; case QEMU_OPTION_watchdog_action: if (select_watchdog_action(optarg) == -1) { - fprintf(stderr, "Unknown -watchdog-action parameter\n"); + error_report("unknown -watchdog-action parameter"); exit(1); } break; @@ -3608,7 +3623,7 @@ int main(int argc, char **argv, char **envp) display_type = DT_SDL; break; #else - fprintf(stderr, "SDL support is disabled\n"); + error_report("SDL support is disabled"); exit(1); #endif case QEMU_OPTION_pidfile: @@ -3670,8 +3685,7 @@ int main(int argc, char **argv, char **envp) qemu_opts_parse_noisily(olist, "accel=tcg", false); break; case QEMU_OPTION_no_kvm_pit: { - fprintf(stderr, "Warning: KVM PIT can no longer be disabled " - "separately.\n"); + error_report("warning: ignoring deprecated option"); break; } case QEMU_OPTION_no_kvm_pit_reinjection: { @@ -3684,8 +3698,8 @@ int main(int argc, char **argv, char **envp) { /* end of list */ } }; - fprintf(stderr, "Warning: option deprecated, use " - "lost_tick_policy property of kvm-pit instead.\n"); + error_report("warning: deprecated, replaced by " + "-global kvm-pit.lost_tick_policy=discard"); qdev_prop_register_global_list(kvm_pit_lost_tick_policy); break; } @@ -3720,7 +3734,7 @@ int main(int argc, char **argv, char **envp) exit(1); } #else - fprintf(stderr, "VNC support is disabled\n"); + error_report("VNC support is disabled"); exit(1); #endif break; @@ -3733,7 +3747,7 @@ int main(int argc, char **argv, char **envp) break; case QEMU_OPTION_balloon: if (balloon_parse(optarg) < 0) { - fprintf(stderr, "Unknown -balloon argument %s\n", optarg); + error_report("unknown -balloon argument %s", optarg); exit(1); } break; @@ -3748,15 +3762,14 @@ int main(int argc, char **argv, char **envp) break; case QEMU_OPTION_uuid: if(qemu_uuid_parse(optarg, qemu_uuid) < 0) { - fprintf(stderr, "Fail to parse UUID string." - " Wrong format.\n"); + error_report("failed to parse UUID string: wrong format"); exit(1); } qemu_uuid_set = true; break; case QEMU_OPTION_option_rom: if (nb_option_roms >= MAX_OPTION_ROMS) { - fprintf(stderr, "Too many option ROMs\n"); + error_report("too many option ROMs"); exit(1); } opts = qemu_opts_parse_noisily(qemu_find_opts("option-rom"), @@ -3768,7 +3781,7 @@ int main(int argc, char **argv, char **envp) option_rom[nb_option_roms].bootindex = qemu_opt_get_number(opts, "bootindex", -1); if (!option_rom[nb_option_roms].name) { - fprintf(stderr, "Option ROM file is not specified\n"); + error_report("Option ROM file is not specified"); exit(1); } nb_option_roms++; @@ -3793,9 +3806,8 @@ int main(int argc, char **argv, char **envp) } else if (strcmp("auto", target) == 0) { semihosting.target = SEMIHOSTING_TARGET_AUTO; } else { - fprintf(stderr, "Unsupported semihosting-config" - " %s\n", - optarg); + error_report("unsupported semihosting-config %s", + optarg); exit(1); } } else { @@ -3805,14 +3817,12 @@ int main(int argc, char **argv, char **envp) qemu_opt_foreach(opts, add_semihosting_arg, &semihosting, NULL); } else { - fprintf(stderr, "Unsupported semihosting-config %s\n", - optarg); + error_report("unsupported semihosting-config %s", optarg); exit(1); } break; case QEMU_OPTION_tdf: - fprintf(stderr, "Warning: user space PIT time drift fix " - "is no longer supported.\n"); + error_report("warning: ignoring deprecated option"); break; case QEMU_OPTION_name: opts = qemu_opts_parse_noisily(qemu_find_opts("name"), @@ -3823,7 +3833,7 @@ int main(int argc, char **argv, char **envp) break; case QEMU_OPTION_prom_env: if (nb_prom_envs >= MAX_PROM_ENVS) { - fprintf(stderr, "Too many prom variables\n"); + error_report("too many prom variables"); exit(1); } prom_envs[nb_prom_envs] = optarg; @@ -3894,20 +3904,27 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_trace: { opts = qemu_opts_parse_noisily(qemu_find_opts("trace"), - optarg, false); + optarg, true); if (!opts) { exit(1); } - trace_events = qemu_opt_get(opts, "events"); - trace_file = qemu_opt_get(opts, "file"); + if (qemu_opt_get(opts, "enable")) { + trace_enable_events(qemu_opt_get(opts, "enable")); + } + trace_init_events(qemu_opt_get(opts, "events")); + if (trace_file) { + g_free(trace_file); + } + trace_file = g_strdup(qemu_opt_get(opts, "file")); + qemu_opts_del(opts); break; } case QEMU_OPTION_readconfig: { int ret = qemu_read_config_file(optarg); if (ret < 0) { - fprintf(stderr, "read config %s: %s\n", optarg, - strerror(-ret)); + error_report("read config %s: %s", optarg, + strerror(-ret)); exit(1); } break; @@ -3915,7 +3932,7 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_spice: olist = qemu_find_opts("spice"); if (!olist) { - fprintf(stderr, "spice is not supported by this qemu build.\n"); + error_report("spice support is disabled"); exit(1); } opts = qemu_opts_parse_noisily(olist, optarg, false); @@ -3932,7 +3949,8 @@ int main(int argc, char **argv, char **envp) } else { fp = fopen(optarg, "w"); if (fp == NULL) { - fprintf(stderr, "open %s: %s\n", optarg, strerror(errno)); + error_report("open %s: %s", optarg, + strerror(errno)); exit(1); } } @@ -3993,13 +4011,13 @@ int main(int argc, char **argv, char **envp) break; case QEMU_OPTION_dump_vmstate: if (vmstate_dump_file) { - fprintf(stderr, "qemu: only one '-dump-vmstate' " - "option may be given\n"); + error_report("only one '-dump-vmstate' " + "option may be given"); exit(1); } vmstate_dump_file = fopen(optarg, "w"); if (vmstate_dump_file == NULL) { - fprintf(stderr, "open %s: %s\n", optarg, strerror(errno)); + error_report("open %s: %s", optarg, strerror(errno)); exit(1); } break; @@ -4008,23 +4026,18 @@ int main(int argc, char **argv, char **envp) } } } + /* + * Clear error location left behind by the loop. + * Best done right after the loop. Do not insert code here! + */ + loc_set_none(); - opts = qemu_get_machine_opts(); - optarg = qemu_opt_get(opts, "type"); - if (optarg) { - machine_class = machine_parse(optarg); - } + replay_configure(icount_opts); - if (machine_class == NULL) { - fprintf(stderr, "No machine specified, and there is no default.\n" - "Use -machine help to list supported machines!\n"); - exit(1); - } + machine_class = select_machine(); set_memory_options(&ram_slots, &maxram_size, machine_class); - loc_set_none(); - os_daemonize(); if (qemu_init_main_loop(&main_loop_err)) { @@ -4064,7 +4077,7 @@ int main(int argc, char **argv, char **envp) cpu_exec_init_all(); if (machine_class->hw_version) { - qemu_set_version(machine_class->hw_version); + qemu_set_hw_version(machine_class->hw_version); } /* Init CPU def lists, based on config @@ -4079,6 +4092,11 @@ int main(int argc, char **argv, char **envp) exit(0); } + if (!trace_init_backends()) { + exit(1); + } + trace_init_file(trace_file); + /* Open the logfile at this point and set the log mask if necessary. */ if (log_file) { @@ -4093,12 +4111,8 @@ int main(int argc, char **argv, char **envp) exit(1); } qemu_set_log(mask); - } - - if (!is_daemonized()) { - if (!trace_init_backends(trace_events, trace_file)) { - exit(1); - } + } else { + qemu_set_log(0); } /* If no data_dir is specified then try to find it relative to the @@ -4118,9 +4132,9 @@ int main(int argc, char **argv, char **envp) machine_class->max_cpus = machine_class->max_cpus ?: 1; /* Default to UP */ if (max_cpus > machine_class->max_cpus) { - fprintf(stderr, "Number of SMP cpus requested (%d), exceeds max cpus " - "supported by machine `%s' (%d)\n", max_cpus, - machine_class->name, machine_class->max_cpus); + error_report("Number of SMP CPUs requested (%d) exceeds max CPUs " + "supported by machine '%s' (%d)", max_cpus, + machine_class->name, machine_class->max_cpus); exit(1); } @@ -4181,12 +4195,12 @@ int main(int argc, char **argv, char **envp) if (display_type == DT_NOGRAPHIC && (default_parallel || default_serial || default_monitor || default_virtcon)) { - fprintf(stderr, "-nographic can not be used with -daemonize\n"); + error_report("-nographic cannot be used with -daemonize"); exit(1); } #ifdef CONFIG_CURSES if (display_type == DT_CURSES) { - fprintf(stderr, "curses display can not be used with -daemonize\n"); + error_report("curses display cannot be used with -daemonize"); exit(1); } #endif @@ -4245,12 +4259,12 @@ int main(int argc, char **argv, char **envp) } if ((no_frame || alt_grab || ctrl_grab) && display_type != DT_SDL) { - fprintf(stderr, "-no-frame, -alt-grab and -ctrl-grab are only valid " - "for SDL, ignoring option\n"); + error_report("-no-frame, -alt-grab and -ctrl-grab are only valid " + "for SDL, ignoring option"); } if (no_quit && (display_type != DT_GTK && display_type != DT_SDL)) { - fprintf(stderr, "-no-quit is only valid for GTK and SDL, " - "ignoring option\n"); + error_report("-no-quit is only valid for GTK and SDL, " + "ignoring option"); } #if defined(CONFIG_GTK) @@ -4265,17 +4279,18 @@ int main(int argc, char **argv, char **envp) #endif if (request_opengl == 1 && display_opengl == 0) { #if defined(CONFIG_OPENGL) - fprintf(stderr, "OpenGL is not supported by the display.\n"); + error_report("OpenGL is not supported by the display"); #else - fprintf(stderr, "QEMU was built without opengl support.\n"); + error_report("OpenGL support is disabled"); #endif exit(1); } + page_size_init(); socket_init(); if (qemu_opts_foreach(qemu_find_opts("object"), - object_create, + user_creatable_add_opts_foreach, object_create_initial, NULL)) { exit(1); } @@ -4293,7 +4308,7 @@ int main(int argc, char **argv, char **envp) #endif if (pid_file && qemu_create_pidfile(pid_file) != 0) { - fprintf(stderr, "Could not acquire pid file: %s\n", strerror(errno)); + error_report("could not acquire pid file: %s", strerror(errno)); exit(1); } @@ -4302,12 +4317,6 @@ int main(int argc, char **argv, char **envp) exit(0); } - if (qemu_opts_foreach(qemu_find_opts("object"), - object_create, - object_create_delayed, NULL)) { - exit(1); - } - machine_opts = qemu_get_machine_opts(); if (qemu_opt_foreach(machine_opts, machine_set_property, current_machine, NULL)) { @@ -4318,12 +4327,7 @@ int main(int argc, char **argv, char **envp) configure_accelerator(current_machine); if (qtest_chrdev) { - Error *local_err = NULL; - qtest_init(qtest_chrdev, qtest_log, &local_err); - if (local_err) { - error_report_err(local_err); - exit(1); - } + qtest_init(qtest_chrdev, qtest_log, &error_fatal); } machine_opts = qemu_get_machine_opts(); @@ -4334,24 +4338,14 @@ int main(int argc, char **argv, char **envp) opts = qemu_opts_find(qemu_find_opts("boot-opts"), NULL); if (opts) { - Error *local_err = NULL; - boot_order = qemu_opt_get(opts, "order"); if (boot_order) { - validate_bootdevices(boot_order, &local_err); - if (local_err) { - error_report_err(local_err); - exit(1); - } + validate_bootdevices(boot_order, &error_fatal); } boot_once = qemu_opt_get(opts, "once"); if (boot_once) { - validate_bootdevices(boot_once, &local_err); - if (local_err) { - error_report_err(local_err); - exit(1); - } + validate_bootdevices(boot_once, &error_fatal); } boot_menu = qemu_opt_get_bool(opts, "menu", boot_menu); @@ -4370,17 +4364,17 @@ int main(int argc, char **argv, char **envp) linux_boot = (kernel_filename != NULL); if (!linux_boot && *kernel_cmdline != '\0') { - fprintf(stderr, "-append only allowed with -kernel option\n"); + error_report("-append only allowed with -kernel option"); exit(1); } if (!linux_boot && initrd_filename != NULL) { - fprintf(stderr, "-initrd only allowed with -kernel option\n"); + error_report("-initrd only allowed with -kernel option"); exit(1); } if (!linux_boot && qemu_opt_get(machine_opts, "dtb")) { - fprintf(stderr, "-dtb only allowed with -kernel option\n"); + error_report("-dtb only allowed with -kernel option"); exit(1); } @@ -4399,7 +4393,7 @@ int main(int argc, char **argv, char **envp) cpu_ticks_init(); if (icount_opts) { if (kvm_enabled() || xen_enabled()) { - fprintf(stderr, "-icount is not allowed with kvm or xen\n"); + error_report("-icount is not allowed with kvm or xen"); exit(1); } configure_icount(icount_opts, &error_abort); @@ -4413,6 +4407,12 @@ int main(int argc, char **argv, char **envp) exit(1); } + if (qemu_opts_foreach(qemu_find_opts("object"), + user_creatable_add_opts_foreach, + object_create_delayed, NULL)) { + exit(1); + } + #ifdef CONFIG_TPM if (tpm_init() < 0) { exit(1); @@ -4426,7 +4426,7 @@ int main(int argc, char **argv, char **envp) if (!xen_enabled()) { /* On 32-bit hosts, QEMU is limited by virtual address space */ if (ram_size > (2047 << 20) && HOST_LONG_BITS == 32) { - fprintf(stderr, "qemu: at most 2047 MB RAM can be simulated\n"); + error_report("at most 2047 MB RAM can be simulated"); exit(1); } } @@ -4442,9 +4442,10 @@ int main(int argc, char **argv, char **envp) } /* open the virtual block devices */ - if (snapshot) - qemu_opts_foreach(qemu_find_opts("drive"), - drive_enable_snapshot, NULL, NULL); + if (snapshot || replay_mode != REPLAY_MODE_NONE) { + qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot, + NULL, NULL); + } if (qemu_opts_foreach(qemu_find_opts("drive"), drive_init_func, &machine_class->block_default_type, NULL)) { exit(1); @@ -4499,6 +4500,10 @@ int main(int argc, char **argv, char **envp) } qemu_add_globals(); + /* This checkpoint is required by replay to separate prior clock + reading from the other reads, because timer polling functions query + clock values from the log. */ + replay_checkpoint(CHECKPOINT_INIT); qdev_machine_init(); current_machine->ram_size = ram_size; @@ -4528,11 +4533,16 @@ int main(int argc, char **argv, char **envp) exit(1); } + /* Check if IGD GFX passthrough. */ + igd_gfx_passthru(); + /* init generic devices */ + rom_set_order_override(FW_CFG_ORDER_OVERRIDE_DEVICE); if (qemu_opts_foreach(qemu_find_opts("device"), device_init_func, NULL, NULL)) { exit(1); } + rom_reset_order_override(); /* Did we create any drives that we failed to create a device for? */ drive_check_orphaned(); @@ -4540,12 +4550,7 @@ int main(int argc, char **argv, char **envp) net_check_clients(); if (boot_once) { - Error *local_err = NULL; - qemu_boot_set(boot_once, &local_err); - if (local_err) { - error_report("%s", error_get_pretty(local_err)); - exit(1); - } + qemu_boot_set(boot_once, &error_fatal); qemu_register_reset(restore_boot_order, g_strdup(boot_order)); } @@ -4588,7 +4593,7 @@ int main(int argc, char **argv, char **envp) vnc_init_func, NULL, NULL); if (show_vnc_port) { char *ret = vnc_display_local_addr("default"); - printf("VNC server running on `%s'\n", ret); + printf("VNC server running on '%s'\n", ret); g_free(ret); } #endif @@ -4610,10 +4615,16 @@ int main(int argc, char **argv, char **envp) qemu_run_machine_init_done_notifiers(); if (rom_check_and_register_reset() != 0) { - fprintf(stderr, "rom check and register reset failed\n"); + error_report("rom check and register reset failed"); exit(1); } + replay_start(); + + /* This checkpoint is required by replay to separate prior clock + reading from the other reads, because timer polling functions query + clock values from the log. */ + replay_checkpoint(CHECKPOINT_RESET); qemu_system_reset(VMRESET_SILENT); register_global_state(); if (loadvm) { @@ -4633,9 +4644,7 @@ int main(int argc, char **argv, char **envp) Error *local_err = NULL; qemu_start_incoming_migration(incoming, &local_err); if (local_err) { - error_report("-incoming %s: %s", incoming, - error_get_pretty(local_err)); - error_free(local_err); + error_reportf_err(local_err, "-incoming %s: ", incoming); exit(1); } } else if (autostart) { @@ -4644,13 +4653,9 @@ int main(int argc, char **argv, char **envp) os_setup_post(); - if (is_daemonized()) { - if (!trace_init_backends(trace_events, trace_file)) { - exit(1); - } - } - main_loop(); + replay_disable_events(); + bdrv_close_all(); pause_all_vcpus(); res_free(); diff --git a/qemu/xen-common-stub.c b/qemu/xen-common-stub.c index 906f991f1..699c3f1c6 100644 --- a/qemu/xen-common-stub.c +++ b/qemu/xen-common-stub.c @@ -5,6 +5,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/xen/xen.h" diff --git a/qemu/xen-common.c b/qemu/xen-common.c index 0dcdbc39f..e641ad1ae 100644 --- a/qemu/xen-common.c +++ b/qemu/xen-common.c @@ -8,6 +8,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/xen/xen_backend.h" #include "qmp-commands.h" #include "sysemu/char.h" @@ -113,11 +114,17 @@ static void xen_change_state_handler(void *opaque, int running, static int xen_init(MachineState *ms) { - xen_xc = xen_xc_interface_open(0, 0, 0); - if (xen_xc == XC_HANDLER_INITIAL_VALUE) { + xen_xc = xc_interface_open(0, 0, 0); + if (xen_xc == NULL) { xen_be_printf(NULL, 0, "can't open xen interface\n"); return -1; } + xen_fmem = xenforeignmemory_open(0, 0); + if (xen_fmem == NULL) { + xen_be_printf(NULL, 0, "can't open xen fmem interface\n"); + xc_interface_close(xen_xc); + return -1; + } qemu_add_vm_change_state_handler(xen_change_state_handler, NULL); global_state_set_optional(); diff --git a/qemu/xen-hvm-stub.c b/qemu/xen-hvm-stub.c index 46867d87d..c5003251c 100644 --- a/qemu/xen-hvm-stub.c +++ b/qemu/xen-hvm-stub.c @@ -8,6 +8,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/xen/xen.h" #include "exec/memory.h" @@ -30,7 +31,13 @@ void xen_hvm_inject_msi(uint64_t addr, uint32_t data) { } -void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr) +int xen_is_pirq_msi(uint32_t msi_data) +{ + return 0; +} + +void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr, + Error **errp) { } @@ -47,10 +54,8 @@ void xen_modified_memory(ram_addr_t start, ram_addr_t length) { } -int xen_hvm_init(ram_addr_t *below_4g_mem_size, ram_addr_t *above_4g_mem_size, - MemoryRegion **ram_memory) +void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) { - return 0; } void qmp_xen_set_global_dirty_log(bool enable, Error **errp) diff --git a/qemu/xen-hvm.c b/qemu/xen-hvm.c index 040846236..039680a6d 100644 --- a/qemu/xen-hvm.c +++ b/qemu/xen-hvm.c @@ -8,15 +8,18 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include #include "hw/pci/pci.h" #include "hw/i386/pc.h" +#include "hw/i386/apic-msidef.h" #include "hw/xen/xen_common.h" #include "hw/xen/xen_backend.h" #include "qmp-commands.h" #include "sysemu/char.h" +#include "qemu/error-report.h" #include "qemu/range.h" #include "sysemu/xen-mapcache.h" #include "trace.h" @@ -64,17 +67,6 @@ struct shared_vmport_iopage { typedef struct shared_vmport_iopage shared_vmport_iopage_t; #endif -#if __XEN_LATEST_INTERFACE_VERSION__ < 0x0003020a -static inline uint32_t xen_vcpu_eport(shared_iopage_t *shared_page, int i) -{ - return shared_page->vcpu_iodata[i].vp_eport; -} -static inline ioreq_t *xen_vcpu_ioreq(shared_iopage_t *shared_page, int vcpu) -{ - return &shared_page->vcpu_iodata[vcpu].vp_ioreq; -} -# define FMT_ioreq_size PRIx64 -#else static inline uint32_t xen_vcpu_eport(shared_iopage_t *shared_page, int i) { return shared_page->vcpu_ioreq[i].vp_eport; @@ -83,8 +75,6 @@ static inline ioreq_t *xen_vcpu_ioreq(shared_iopage_t *shared_page, int vcpu) { return &shared_page->vcpu_ioreq[vcpu]; } -# define FMT_ioreq_size "u" -#endif #define BUFFER_IO_MAX_DELAY 100 @@ -109,7 +99,7 @@ typedef struct XenIOState { /* evtchn local port for buffered io */ evtchn_port_t bufioreq_local_port; /* the evtchn fd for polling */ - XenEvtchn xce_handle; + xenevtchn_handle *xce_handle; /* which vcpu we are serving */ int send_vcpu; @@ -156,9 +146,17 @@ void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len) } } +int xen_is_pirq_msi(uint32_t msi_data) +{ + /* If vector is 0, the msi is remapped into a pirq, passed as + * dest_id. + */ + return ((msi_data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT) == 0; +} + void xen_hvm_inject_msi(uint64_t addr, uint32_t data) { - xen_xc_hvm_inject_msi(xen_xc, xen_domid, addr, data); + xc_hvm_inject_msi(xen_xc, xen_domid, addr, data); } static void xen_suspend_notifier(Notifier *notifier, void *data) @@ -180,8 +178,7 @@ qemu_irq *xen_interrupt_controller_init(void) /* Memory Ops */ -static void xen_ram_init(ram_addr_t *below_4g_mem_size, - ram_addr_t *above_4g_mem_size, +static void xen_ram_init(PCMachineState *pcms, ram_addr_t ram_size, MemoryRegion **ram_memory_p) { MemoryRegion *sysmem = get_system_memory(); @@ -198,23 +195,23 @@ static void xen_ram_init(ram_addr_t *below_4g_mem_size, } if (ram_size >= user_lowmem) { - *above_4g_mem_size = ram_size - user_lowmem; - *below_4g_mem_size = user_lowmem; + pcms->above_4g_mem_size = ram_size - user_lowmem; + pcms->below_4g_mem_size = user_lowmem; } else { - *above_4g_mem_size = 0; - *below_4g_mem_size = ram_size; + pcms->above_4g_mem_size = 0; + pcms->below_4g_mem_size = ram_size; } - if (!*above_4g_mem_size) { + if (!pcms->above_4g_mem_size) { block_len = ram_size; } else { /* * Xen does not allocate the memory continuously, it keeps a * hole of the size computed above or passed in. */ - block_len = (1ULL << 32) + *above_4g_mem_size; + block_len = (1ULL << 32) + pcms->above_4g_mem_size; } memory_region_init_ram(&ram_memory, NULL, "xen.ram", block_len, - &error_abort); + &error_fatal); *ram_memory_p = &ram_memory; vmstate_register_ram_global(&ram_memory); @@ -229,17 +226,18 @@ static void xen_ram_init(ram_addr_t *below_4g_mem_size, */ memory_region_init_alias(&ram_lo, NULL, "xen.ram.lo", &ram_memory, 0xc0000, - *below_4g_mem_size - 0xc0000); + pcms->below_4g_mem_size - 0xc0000); memory_region_add_subregion(sysmem, 0xc0000, &ram_lo); - if (*above_4g_mem_size > 0) { + if (pcms->above_4g_mem_size > 0) { memory_region_init_alias(&ram_hi, NULL, "xen.ram.hi", &ram_memory, 0x100000000ULL, - *above_4g_mem_size); + pcms->above_4g_mem_size); memory_region_add_subregion(sysmem, 0x100000000ULL, &ram_hi); } } -void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr) +void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr, + Error **errp) { unsigned long nr_pfn; xen_pfn_t *pfn_list; @@ -267,7 +265,8 @@ void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr) } if (xc_domain_populate_physmap_exact(xen_xc, xen_domid, nr_pfn, 0, 0, pfn_list)) { - hw_error("xen: failed to populate ram at " RAM_ADDR_FMT, ram_addr); + error_setg(errp, "xen: failed to populate ram at " RAM_ADDR_FMT, + ram_addr); } g_free(pfn_list); @@ -304,7 +303,6 @@ static hwaddr xen_phys_offset_to_gaddr(hwaddr start_addr, return start_addr; } -#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 340 static int xen_add_to_physmap(XenIOState *state, hwaddr start_addr, ram_addr_t size, @@ -345,10 +343,10 @@ go_physmap: unsigned long idx = pfn + i; xen_pfn_t gpfn = start_gpfn + i; - rc = xc_domain_add_to_physmap(xen_xc, xen_domid, XENMAPSPACE_gmfn, idx, gpfn); + rc = xen_xc_domain_add_to_physmap(xen_xc, xen_domid, XENMAPSPACE_gmfn, idx, gpfn); if (rc) { DPRINTF("add_to_physmap MFN %"PRI_xen_pfn" to PFN %" - PRI_xen_pfn" failed: %d\n", idx, gpfn, rc); + PRI_xen_pfn" failed: %d (errno: %d)\n", idx, gpfn, rc, errno); return -rc; } } @@ -422,10 +420,10 @@ static int xen_remove_from_physmap(XenIOState *state, xen_pfn_t idx = start_addr + i; xen_pfn_t gpfn = phys_offset + i; - rc = xc_domain_add_to_physmap(xen_xc, xen_domid, XENMAPSPACE_gmfn, idx, gpfn); + rc = xen_xc_domain_add_to_physmap(xen_xc, xen_domid, XENMAPSPACE_gmfn, idx, gpfn); if (rc) { fprintf(stderr, "add_to_physmap MFN %"PRI_xen_pfn" to PFN %" - PRI_xen_pfn" failed: %d\n", idx, gpfn, rc); + PRI_xen_pfn" failed: %d (errno: %d)\n", idx, gpfn, rc, errno); return -rc; } } @@ -439,24 +437,6 @@ static int xen_remove_from_physmap(XenIOState *state, return 0; } -#else -static int xen_add_to_physmap(XenIOState *state, - hwaddr start_addr, - ram_addr_t size, - MemoryRegion *mr, - hwaddr offset_within_region) -{ - return -ENOSYS; -} - -static int xen_remove_from_physmap(XenIOState *state, - hwaddr start_addr, - ram_addr_t size) -{ - return -ENOSYS; -} -#endif - static void xen_set_memory(struct MemoryListener *listener, MemoryRegionSection *section, bool add) @@ -695,7 +675,7 @@ static ioreq_t *cpu_get_ioreq_from_shared_memory(XenIOState *state, int vcpu) if (req->state != STATE_IOREQ_READY) { DPRINTF("I/O request not ready: " "%x, ptr: %x, port: %"PRIx64", " - "data: %"PRIx64", count: %" FMT_ioreq_size ", size: %" FMT_ioreq_size "\n", + "data: %"PRIx64", count: %u, size: %u\n", req->state, req->data_is_ptr, req->addr, req->data, req->count, req->size); return NULL; @@ -715,7 +695,7 @@ static ioreq_t *cpu_get_ioreq(XenIOState *state) int i; evtchn_port_t port; - port = xc_evtchn_pending(state->xce_handle); + port = xenevtchn_pending(state->xce_handle); if (port == state->bufioreq_local_port) { timer_mod(state->buffered_io_timer, BUFFER_IO_MAX_DELAY + qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); @@ -734,7 +714,7 @@ static ioreq_t *cpu_get_ioreq(XenIOState *state) } /* unmask the wanted port again */ - xc_evtchn_unmask(state->xce_handle, port); + xenevtchn_unmask(state->xce_handle, port); /* get the io packet from shared memory */ state->send_vcpu = i; @@ -814,9 +794,14 @@ static void cpu_ioreq_pio(ioreq_t *req) { uint32_t i; + trace_cpu_ioreq_pio(req, req->dir, req->df, req->data_is_ptr, req->addr, + req->data, req->count, req->size); + if (req->dir == IOREQ_READ) { if (!req->data_is_ptr) { req->data = do_inp(req->addr, req->size); + trace_cpu_ioreq_pio_read_reg(req, req->data, req->addr, + req->size); } else { uint32_t tmp; @@ -827,6 +812,8 @@ static void cpu_ioreq_pio(ioreq_t *req) } } else if (req->dir == IOREQ_WRITE) { if (!req->data_is_ptr) { + trace_cpu_ioreq_pio_write_reg(req, req->data, req->addr, + req->size); do_outp(req->addr, req->size, req->data); } else { for (i = 0; i < req->count; i++) { @@ -843,6 +830,9 @@ static void cpu_ioreq_move(ioreq_t *req) { uint32_t i; + trace_cpu_ioreq_move(req, req->dir, req->df, req->data_is_ptr, req->addr, + req->data, req->count, req->size); + if (!req->data_is_ptr) { if (req->dir == IOREQ_READ) { for (i = 0; i < req->count; i++) { @@ -915,11 +905,18 @@ static void handle_vmport_ioreq(XenIOState *state, ioreq_t *req) static void handle_ioreq(XenIOState *state, ioreq_t *req) { + trace_handle_ioreq(req, req->type, req->dir, req->df, req->data_is_ptr, + req->addr, req->data, req->count, req->size); + if (!req->data_is_ptr && (req->dir == IOREQ_WRITE) && (req->size < sizeof (target_ulong))) { req->data &= ((target_ulong) 1 << (8 * req->size)) - 1; } + if (req->dir == IOREQ_WRITE) + trace_handle_ioreq_write(req, req->type, req->df, req->data_is_ptr, + req->addr, req->data, req->count, req->size); + switch (req->type) { case IOREQ_TYPE_PIO: cpu_ioreq_pio(req); @@ -959,23 +956,38 @@ static void handle_ioreq(XenIOState *state, ioreq_t *req) default: hw_error("Invalid ioreq type 0x%x\n", req->type); } + if (req->dir == IOREQ_READ) { + trace_handle_ioreq_read(req, req->type, req->df, req->data_is_ptr, + req->addr, req->data, req->count, req->size); + } } static int handle_buffered_iopage(XenIOState *state) { + buffered_iopage_t *buf_page = state->buffered_io_page; buf_ioreq_t *buf_req = NULL; ioreq_t req; int qw; - if (!state->buffered_io_page) { + if (!buf_page) { return 0; } memset(&req, 0x00, sizeof(req)); - while (state->buffered_io_page->read_pointer != state->buffered_io_page->write_pointer) { - buf_req = &state->buffered_io_page->buf_ioreq[ - state->buffered_io_page->read_pointer % IOREQ_BUFFER_SLOT_NUM]; + for (;;) { + uint32_t rdptr = buf_page->read_pointer, wrptr; + + xen_rmb(); + wrptr = buf_page->write_pointer; + xen_rmb(); + if (rdptr != buf_page->read_pointer) { + continue; + } + if (rdptr == wrptr) { + break; + } + buf_req = &buf_page->buf_ioreq[rdptr % IOREQ_BUFFER_SLOT_NUM]; req.size = 1UL << buf_req->size; req.count = 1; req.addr = buf_req->addr; @@ -987,15 +999,14 @@ static int handle_buffered_iopage(XenIOState *state) req.data_is_ptr = 0; qw = (req.size == 8); if (qw) { - buf_req = &state->buffered_io_page->buf_ioreq[ - (state->buffered_io_page->read_pointer + 1) % IOREQ_BUFFER_SLOT_NUM]; + buf_req = &buf_page->buf_ioreq[(rdptr + 1) % + IOREQ_BUFFER_SLOT_NUM]; req.data |= ((uint64_t)buf_req->data) << 32; } handle_ioreq(state, &req); - xen_mb(); - state->buffered_io_page->read_pointer += qw ? 2 : 1; + atomic_add(&buf_page->read_pointer, qw + 1); } return req.count; @@ -1010,7 +1021,7 @@ static void handle_buffered_io(void *opaque) BUFFER_IO_MAX_DELAY + qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); } else { timer_del(state->buffered_io_timer); - xc_evtchn_unmask(state->xce_handle, state->bufioreq_local_port); + xenevtchn_unmask(state->xce_handle, state->bufioreq_local_port); } } @@ -1026,9 +1037,7 @@ static void cpu_handle_ioreq(void *opaque) if (req->state != STATE_IOREQ_INPROCESS) { fprintf(stderr, "Badness in I/O request ... not in service?!: " "%x, ptr: %x, port: %"PRIx64", " - "data: %"PRIx64", count: %" FMT_ioreq_size - ", size: %" FMT_ioreq_size - ", type: %"FMT_ioreq_size"\n", + "data: %"PRIx64", count: %u, size: %u, type: %u\n", req->state, req->data_is_ptr, req->addr, req->data, req->count, req->size, req->type); destroy_hvm_domain(false); @@ -1054,7 +1063,8 @@ static void cpu_handle_ioreq(void *opaque) } req->state = STATE_IORESP_READY; - xc_evtchn_notify(state->xce_handle, state->ioreq_local_port[state->send_vcpu]); + xenevtchn_notify(state->xce_handle, + state->ioreq_local_port[state->send_vcpu]); } } @@ -1062,8 +1072,8 @@ static void xen_main_loop_prepare(XenIOState *state) { int evtchn_fd = -1; - if (state->xce_handle != XC_HANDLER_INITIAL_VALUE) { - evtchn_fd = xc_evtchn_fd(state->xce_handle); + if (state->xce_handle != NULL) { + evtchn_fd = xenevtchn_fd(state->xce_handle); } state->buffered_io_timer = timer_new_ms(QEMU_CLOCK_REALTIME, handle_buffered_io, @@ -1101,7 +1111,7 @@ static void xen_exit_notifier(Notifier *n, void *data) { XenIOState *state = container_of(n, XenIOState, exit); - xc_evtchn_close(state->xce_handle); + xenevtchn_close(state->xce_handle); xs_daemon_close(state->xenstore); } @@ -1158,9 +1168,7 @@ static void xen_wakeup_notifier(Notifier *notifier, void *data) xc_set_hvm_param(xen_xc, xen_domid, HVM_PARAM_ACPI_S_STATE, 0); } -/* return 0 means OK, or -1 means critical issue -- will exit(1) */ -int xen_hvm_init(ram_addr_t *below_4g_mem_size, ram_addr_t *above_4g_mem_size, - MemoryRegion **ram_memory) +void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) { int i, rc; xen_pfn_t ioreq_pfn; @@ -1170,22 +1178,22 @@ int xen_hvm_init(ram_addr_t *below_4g_mem_size, ram_addr_t *above_4g_mem_size, state = g_malloc0(sizeof (XenIOState)); - state->xce_handle = xen_xc_evtchn_open(NULL, 0); - if (state->xce_handle == XC_HANDLER_INITIAL_VALUE) { + state->xce_handle = xenevtchn_open(NULL, 0); + if (state->xce_handle == NULL) { perror("xen: event channel open"); - return -1; + goto err; } state->xenstore = xs_daemon_open(); if (state->xenstore == NULL) { perror("xen: xenstore open"); - return -1; + goto err; } rc = xen_create_ioreq_server(xen_xc, xen_domid, &state->ioservid); if (rc < 0) { perror("xen: ioreq server create"); - return -1; + goto err; } state->exit.notify = xen_exit_notifier; @@ -1201,41 +1209,47 @@ int xen_hvm_init(ram_addr_t *below_4g_mem_size, ram_addr_t *above_4g_mem_size, &ioreq_pfn, &bufioreq_pfn, &bufioreq_evtchn); if (rc < 0) { - hw_error("failed to get ioreq server info: error %d handle=" XC_INTERFACE_FMT, - errno, xen_xc); + error_report("failed to get ioreq server info: error %d handle=%p", + errno, xen_xc); + goto err; } DPRINTF("shared page at pfn %lx\n", ioreq_pfn); DPRINTF("buffered io page at pfn %lx\n", bufioreq_pfn); DPRINTF("buffered io evtchn is %x\n", bufioreq_evtchn); - state->shared_page = xc_map_foreign_range(xen_xc, xen_domid, XC_PAGE_SIZE, - PROT_READ|PROT_WRITE, ioreq_pfn); + state->shared_page = xenforeignmemory_map(xen_fmem, xen_domid, + PROT_READ|PROT_WRITE, + 1, &ioreq_pfn, NULL); if (state->shared_page == NULL) { - hw_error("map shared IO page returned error %d handle=" XC_INTERFACE_FMT, - errno, xen_xc); + error_report("map shared IO page returned error %d handle=%p", + errno, xen_xc); + goto err; } rc = xen_get_vmport_regs_pfn(xen_xc, xen_domid, &ioreq_pfn); if (!rc) { DPRINTF("shared vmport page at pfn %lx\n", ioreq_pfn); state->shared_vmport_page = - xc_map_foreign_range(xen_xc, xen_domid, XC_PAGE_SIZE, - PROT_READ|PROT_WRITE, ioreq_pfn); + xenforeignmemory_map(xen_fmem, xen_domid, PROT_READ|PROT_WRITE, + 1, &ioreq_pfn, NULL); if (state->shared_vmport_page == NULL) { - hw_error("map shared vmport IO page returned error %d handle=" - XC_INTERFACE_FMT, errno, xen_xc); + error_report("map shared vmport IO page returned error %d handle=%p", + errno, xen_xc); + goto err; } } else if (rc != -ENOSYS) { - hw_error("get vmport regs pfn returned error %d, rc=%d", errno, rc); + error_report("get vmport regs pfn returned error %d, rc=%d", + errno, rc); + goto err; } - state->buffered_io_page = xc_map_foreign_range(xen_xc, xen_domid, - XC_PAGE_SIZE, + state->buffered_io_page = xenforeignmemory_map(xen_fmem, xen_domid, PROT_READ|PROT_WRITE, - bufioreq_pfn); + 1, &bufioreq_pfn, NULL); if (state->buffered_io_page == NULL) { - hw_error("map buffered IO page returned error %d", errno); + error_report("map buffered IO page returned error %d", errno); + goto err; } /* Note: cpus is empty at this point in init */ @@ -1243,34 +1257,35 @@ int xen_hvm_init(ram_addr_t *below_4g_mem_size, ram_addr_t *above_4g_mem_size, rc = xen_set_ioreq_server_state(xen_xc, xen_domid, state->ioservid, true); if (rc < 0) { - hw_error("failed to enable ioreq server info: error %d handle=" XC_INTERFACE_FMT, - errno, xen_xc); + error_report("failed to enable ioreq server info: error %d handle=%p", + errno, xen_xc); + goto err; } state->ioreq_local_port = g_malloc0(max_cpus * sizeof (evtchn_port_t)); /* FIXME: how about if we overflow the page here? */ for (i = 0; i < max_cpus; i++) { - rc = xc_evtchn_bind_interdomain(state->xce_handle, xen_domid, + rc = xenevtchn_bind_interdomain(state->xce_handle, xen_domid, xen_vcpu_eport(state->shared_page, i)); if (rc == -1) { - fprintf(stderr, "shared evtchn %d bind error %d\n", i, errno); - return -1; + error_report("shared evtchn %d bind error %d", i, errno); + goto err; } state->ioreq_local_port[i] = rc; } - rc = xc_evtchn_bind_interdomain(state->xce_handle, xen_domid, + rc = xenevtchn_bind_interdomain(state->xce_handle, xen_domid, bufioreq_evtchn); if (rc == -1) { - fprintf(stderr, "buffered evtchn bind error %d\n", errno); - return -1; + error_report("buffered evtchn bind error %d", errno); + goto err; } state->bufioreq_local_port = rc; /* Init RAM management */ xen_map_cache_init(xen_phys_offset_to_gaddr, state); - xen_ram_init(below_4g_mem_size, above_4g_mem_size, ram_size, ram_memory); + xen_ram_init(pcms, ram_size, ram_memory); qemu_add_vm_change_state_handler(xen_hvm_change_state_handler, state); @@ -1287,24 +1302,27 @@ int xen_hvm_init(ram_addr_t *below_4g_mem_size, ram_addr_t *above_4g_mem_size, /* Initialize backend core & drivers */ if (xen_be_init() != 0) { - fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); - return -1; + error_report("xen backend core setup failed"); + goto err; } xen_be_register("console", &xen_console_ops); xen_be_register("vkbd", &xen_kbdmouse_ops); xen_be_register("qdisk", &xen_blkdev_ops); xen_read_physmap(state); + return; - return 0; +err: + error_report("xen hardware virtual machine initialisation failed"); + exit(1); } void destroy_hvm_domain(bool reboot) { - XenXC xc_handle; + xc_interface *xc_handle; int sts; - xc_handle = xen_xc_interface_open(0, 0, 0); - if (xc_handle == XC_HANDLER_INITIAL_VALUE) { + xc_handle = xc_interface_open(0, 0, 0); + if (xc_handle == NULL) { fprintf(stderr, "Cannot acquire xenctrl handle\n"); } else { sts = xc_domain_shutdown(xc_handle, xen_domid, diff --git a/qemu/xen-mapcache.c b/qemu/xen-mapcache.c index 8cefd0c62..49f394a77 100644 --- a/qemu/xen-mapcache.c +++ b/qemu/xen-mapcache.c @@ -8,7 +8,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "config.h" +#include "qemu/osdep.h" #include @@ -169,19 +169,17 @@ static void xen_remap_bucket(MapCacheEntry *entry, exit(-1); } } - if (entry->valid_mapping != NULL) { - g_free(entry->valid_mapping); - entry->valid_mapping = NULL; - } + g_free(entry->valid_mapping); + entry->valid_mapping = NULL; for (i = 0; i < nb_pfn; i++) { pfns[i] = (address_index << (MCACHE_BUCKET_SHIFT-XC_PAGE_SHIFT)) + i; } - vaddr_base = xc_map_foreign_bulk(xen_xc, xen_domid, PROT_READ|PROT_WRITE, - pfns, err, nb_pfn); + vaddr_base = xenforeignmemory_map(xen_fmem, xen_domid, PROT_READ|PROT_WRITE, + nb_pfn, pfns, err); if (vaddr_base == NULL) { - perror("xc_map_foreign_bulk"); + perror("xenforeignmemory_map"); exit(-1); } -- 2.16.6

    Q~Aj3iiY}nuv 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 F0szD^^hN*x literal 200192 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 mZGd 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�P=YYqOL(mWqPdSC0Xwa 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